Merge tag 'please-pull-fix-ia64-warnings' of git://git.kernel.org/pub/scm/linux/kerne...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 10 Jul 2013 17:09:04 +0000 (10:09 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 10 Jul 2013 17:09:04 +0000 (10:09 -0700)
P{ill ia64 warning fix from Tony Luck:
 "Add some casts to avoid warnings from efi_runtime_services_t members"

* tag 'please-pull-fix-ia64-warnings' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux:
  [IA64] sim: Add casts to avoid assignment warnings

2149 files changed:
Documentation/DocBook/80211.tmpl
Documentation/DocBook/device-drivers.tmpl
Documentation/DocBook/drm.tmpl
Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt
Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/allwinner,sun4i-mdio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/arc_emac.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/can/fsl-flexcan.txt
Documentation/devicetree/bindings/net/cpsw.txt
Documentation/devicetree/bindings/net/davicom-dm9000.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/marvell-orion-net.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/micrel-ks8851.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/stmmac.txt
Documentation/devicetree/bindings/net/via-velocity.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/devicetree/bindings/video/display-timing.txt
Documentation/devicetree/bindings/video/exynos_hdmi.txt
Documentation/devicetree/bindings/video/exynos_hdmiddc.txt
Documentation/devicetree/bindings/video/exynos_hdmiphy.txt
Documentation/devicetree/bindings/video/exynos_mixer.txt
Documentation/devicetree/bindings/video/fsl,imx-fb.txt [new file with mode: 0644]
Documentation/devicetree/bindings/video/ssd1307fb.txt
Documentation/fb/uvesafb.txt
Documentation/kernel-parameters.txt
Documentation/networking/.gitignore
Documentation/networking/00-INDEX
Documentation/networking/Makefile
Documentation/networking/arcnet.txt
Documentation/networking/bonding.txt
Documentation/networking/ifenslave.c [deleted file]
Documentation/networking/ip-sysctl.txt
Documentation/networking/ipvs-sysctl.txt
Documentation/networking/netlink_mmap.txt
Documentation/networking/packet_mmap.txt
Documentation/networking/scaling.txt
Documentation/networking/vortex.txt
Documentation/printk-formats.txt
Documentation/sysctl/net.txt
Documentation/sysctl/vm.txt
Documentation/vm/transhuge.txt
Documentation/x86/boot.txt
MAINTAINERS
arch/alpha/include/uapi/asm/socket.h
arch/arc/mm/fault.c
arch/arm/Kconfig
arch/arm/boot/compressed/.gitignore
arch/arm/boot/compressed/Makefile
arch/arm/boot/compressed/decompress.c
arch/arm/boot/compressed/piggy.lz4.S [new file with mode: 0644]
arch/arm/boot/dts/am335x-bone.dts
arch/arm/boot/dts/am335x-evm.dts
arch/arm/boot/dts/am335x-evmsk.dts
arch/arm/boot/dts/cros5250-common.dtsi
arch/arm/boot/dts/exynos5250-smdk5250.dts
arch/arm/boot/dts/exynos5250.dtsi
arch/arm/boot/dts/imx28-evk.dts
arch/arm/boot/dts/sun4i-a10-cubieboard.dts
arch/arm/boot/dts/sun4i-a10-hackberry.dts
arch/arm/boot/dts/sun4i-a10.dtsi
arch/arm/configs/omap2plus_defconfig
arch/arm/include/asm/hardware/iop3xx.h
arch/arm/include/asm/mach/arch.h
arch/arm/include/asm/system_misc.h
arch/arm/kernel/process.c
arch/arm/kernel/ptrace.c
arch/arm/kernel/setup.c
arch/arm/mach-at91/at91rm9200.c
arch/arm/mach-at91/generic.h
arch/arm/mach-bcm2835/bcm2835.c
arch/arm/mach-clps711x/common.c
arch/arm/mach-clps711x/common.h
arch/arm/mach-cns3xxx/core.h
arch/arm/mach-cns3xxx/pm.c
arch/arm/mach-davinci/devices-da8xx.c
arch/arm/mach-davinci/devices.c
arch/arm/mach-davinci/include/mach/common.h
arch/arm/mach-davinci/include/mach/da8xx.h
arch/arm/mach-davinci/include/mach/tnetv107x.h
arch/arm/mach-davinci/tnetv107x.c
arch/arm/mach-dove/common.c
arch/arm/mach-dove/common.h
arch/arm/mach-ebsa110/core.c
arch/arm/mach-ep93xx/core.c
arch/arm/mach-ep93xx/include/mach/platform.h
arch/arm/mach-exynos/common.c
arch/arm/mach-exynos/common.h
arch/arm/mach-footbridge/cats-hw.c
arch/arm/mach-footbridge/common.c
arch/arm/mach-footbridge/common.h
arch/arm/mach-footbridge/netwinder-hw.c
arch/arm/mach-highbank/core.h
arch/arm/mach-highbank/system.c
arch/arm/mach-imx/Kconfig
arch/arm/mach-imx/common.h
arch/arm/mach-imx/devices-imx25.h
arch/arm/mach-imx/devices-imx35.h
arch/arm/mach-imx/devices/Kconfig
arch/arm/mach-imx/devices/devices-common.h
arch/arm/mach-imx/devices/platform-flexcan.c
arch/arm/mach-imx/eukrea_mbimxsd25-baseboard.c
arch/arm/mach-imx/eukrea_mbimxsd35-baseboard.c
arch/arm/mach-imx/mach-imx6q.c
arch/arm/mach-imx/mach-mx25_3ds.c
arch/arm/mach-imx/mach-pcm043.c
arch/arm/mach-imx/system.c
arch/arm/mach-integrator/common.h
arch/arm/mach-integrator/core.c
arch/arm/mach-iop13xx/include/mach/iop13xx.h
arch/arm/mach-iop13xx/setup.c
arch/arm/mach-iop32x/n2100.c
arch/arm/mach-ixp4xx/common.c
arch/arm/mach-ixp4xx/dsmg600-setup.c
arch/arm/mach-ixp4xx/include/mach/platform.h
arch/arm/mach-kirkwood/common.c
arch/arm/mach-kirkwood/common.h
arch/arm/mach-ks8695/generic.h
arch/arm/mach-ks8695/time.c
arch/arm/mach-lpc32xx/common.c
arch/arm/mach-lpc32xx/common.h
arch/arm/mach-mmp/common.c
arch/arm/mach-mmp/common.h
arch/arm/mach-mmp/include/mach/pxa168.h
arch/arm/mach-mmp/pxa168.c
arch/arm/mach-mv78xx0/common.c
arch/arm/mach-mv78xx0/common.h
arch/arm/mach-mvebu/common.h
arch/arm/mach-mvebu/system-controller.c
arch/arm/mach-mxs/Kconfig
arch/arm/mach-mxs/mach-mxs.c
arch/arm/mach-netx/generic.c
arch/arm/mach-netx/generic.h
arch/arm/mach-nomadik/cpu-8815.c
arch/arm/mach-omap1/board-voiceblue.c
arch/arm/mach-omap1/common.h
arch/arm/mach-omap1/reset.c
arch/arm/mach-omap2/am33xx-restart.c
arch/arm/mach-omap2/common.h
arch/arm/mach-omap2/omap2-restart.c
arch/arm/mach-omap2/omap3-restart.c
arch/arm/mach-omap2/omap4-common.c
arch/arm/mach-omap2/omap4-restart.c
arch/arm/mach-orion5x/common.c
arch/arm/mach-orion5x/common.h
arch/arm/mach-orion5x/ls-chl-setup.c
arch/arm/mach-orion5x/ls_hgl-setup.c
arch/arm/mach-orion5x/lsmini-setup.c
arch/arm/mach-picoxcell/common.c
arch/arm/mach-prima2/common.h
arch/arm/mach-prima2/rstc.c
arch/arm/mach-pxa/corgi.c
arch/arm/mach-pxa/generic.h
arch/arm/mach-pxa/mioa701.c
arch/arm/mach-pxa/poodle.c
arch/arm/mach-pxa/reset.c
arch/arm/mach-pxa/spitz.c
arch/arm/mach-pxa/tosa.c
arch/arm/mach-realview/realview_eb.c
arch/arm/mach-realview/realview_pb1176.c
arch/arm/mach-realview/realview_pb11mp.c
arch/arm/mach-realview/realview_pba8.c
arch/arm/mach-realview/realview_pbx.c
arch/arm/mach-rpc/riscpc.c
arch/arm/mach-s3c24xx/common.h
arch/arm/mach-s3c24xx/s3c2410.c
arch/arm/mach-s3c24xx/s3c2412.c
arch/arm/mach-s3c24xx/s3c2416.c
arch/arm/mach-s3c24xx/s3c2443.c
arch/arm/mach-s3c24xx/s3c244x.c
arch/arm/mach-s3c64xx/common.c
arch/arm/mach-s3c64xx/common.h
arch/arm/mach-s5p64x0/common.c
arch/arm/mach-s5p64x0/common.h
arch/arm/mach-s5pc100/common.c
arch/arm/mach-s5pc100/common.h
arch/arm/mach-s5pv210/common.c
arch/arm/mach-s5pv210/common.h
arch/arm/mach-sa1100/generic.c
arch/arm/mach-sa1100/generic.h
arch/arm/mach-shark/core.c
arch/arm/mach-shmobile/board-armadillo800eva.c
arch/arm/mach-shmobile/board-kzm9g.c
arch/arm/mach-shmobile/clock-r8a7740.c
arch/arm/mach-shmobile/clock-r8a7778.c
arch/arm/mach-shmobile/clock-r8a7779.c
arch/arm/mach-socfpga/socfpga.c
arch/arm/mach-spear/generic.h
arch/arm/mach-spear/restart.c
arch/arm/mach-sunxi/sunxi.c
arch/arm/mach-tegra/board.h
arch/arm/mach-tegra/common.c
arch/arm/mach-u300/core.c
arch/arm/mach-versatile/core.c
arch/arm/mach-versatile/core.h
arch/arm/mach-vt8500/vt8500.c
arch/arm/mach-w90x900/cpu.c
arch/arm/mach-w90x900/nuc9xx.h
arch/arm/net/bpf_jit_32.c
arch/arm/plat-iop/gpio.c
arch/arm/plat-iop/restart.c
arch/avr32/include/uapi/asm/socket.h
arch/cris/arch-v10/drivers/Kconfig
arch/cris/arch-v32/drivers/Kconfig
arch/cris/include/uapi/asm/socket.h
arch/frv/include/uapi/asm/socket.h
arch/h8300/include/uapi/asm/socket.h
arch/ia64/hp/sim/simeth.c
arch/ia64/include/uapi/asm/socket.h
arch/m32r/include/uapi/asm/socket.h
arch/metag/mm/fault.c
arch/mips/bcm63xx/boards/board_bcm963xx.c
arch/mips/bcm63xx/dev-enet.c
arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_enet.h
arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h
arch/mips/include/uapi/asm/socket.h
arch/mips/txx9/generic/setup_tx4939.c
arch/mn10300/include/uapi/asm/socket.h
arch/mn10300/mm/fault.c
arch/openrisc/mm/fault.c
arch/parisc/include/uapi/asm/socket.h
arch/powerpc/Kconfig
arch/powerpc/include/uapi/asm/socket.h
arch/powerpc/kernel/ptrace.c
arch/powerpc/net/bpf_jit_comp.c
arch/s390/include/uapi/asm/socket.h
arch/score/mm/fault.c
arch/sh/boards/board-espt.c
arch/sh/boards/board-sh7757lcr.c
arch/sh/boards/mach-ecovec24/setup.c
arch/sh/boards/mach-se/770x/setup.c
arch/sh/boards/mach-se/7724/setup.c
arch/sh/boards/mach-sh7763rdp/setup.c
arch/sh/kernel/cpu/sh2/setup-sh7619.c
arch/sh/kernel/cpu/sh4a/clock-sh7724.c
arch/sh/kernel/cpu/sh4a/clock-sh7734.c
arch/sh/kernel/ptrace_32.c
arch/sparc/include/uapi/asm/socket.h
arch/sparc/net/bpf_jit_comp.c
arch/tile/mm/fault.c
arch/unicore32/kernel/process.c
arch/unicore32/kernel/setup.h
arch/unicore32/mm/mmu.c
arch/x86/Kconfig
arch/x86/boot/compressed/Makefile
arch/x86/boot/compressed/misc.c
arch/x86/include/asm/emergency-restart.h
arch/x86/include/asm/io.h
arch/x86/include/asm/mtrr.h
arch/x86/kernel/apic/x2apic_uv_x.c
arch/x86/kernel/cpu/mtrr/main.c
arch/x86/kernel/hw_breakpoint.c
arch/x86/kernel/ptrace.c
arch/x86/kernel/reboot.c
arch/x86/mm/pgtable.c
arch/x86/net/bpf_jit_comp.c
arch/xtensa/include/uapi/asm/socket.h
block/partitions/Kconfig
block/partitions/Makefile
block/partitions/aix.c [new file with mode: 0644]
block/partitions/aix.h [new file with mode: 0644]
block/partitions/msdos.c
crypto/Kconfig
crypto/Makefile
crypto/lz4.c [new file with mode: 0644]
crypto/lz4hc.c [new file with mode: 0644]
drivers/atm/ambassador.c
drivers/base/Makefile
drivers/base/reservation.c [new file with mode: 0644]
drivers/bcma/Kconfig
drivers/bcma/bcma_private.h
drivers/bcma/core.c
drivers/bcma/driver_chipcommon.c
drivers/bcma/driver_chipcommon_pmu.c
drivers/bcma/driver_chipcommon_sflash.c
drivers/bcma/host_pci.c
drivers/bcma/main.c
drivers/bcma/sprom.c
drivers/block/rbd.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btusb.c
drivers/char/agp/ati-agp.c
drivers/char/agp/frontend.c
drivers/char/agp/nvidia-agp.c
drivers/char/mwave/tp3780i.c
drivers/crypto/talitos.c
drivers/dma/iop-adma.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/ast/ast_drv.h
drivers/gpu/drm/ast/ast_fb.c
drivers/gpu/drm/ast/ast_ttm.c
drivers/gpu/drm/cirrus/cirrus_drv.h
drivers/gpu/drm/cirrus/cirrus_fbdev.c
drivers/gpu/drm/cirrus/cirrus_ttm.c
drivers/gpu/drm/drm_bufs.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_crtc_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_fb_helper.c
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/drm_gem_cma_helper.c
drivers/gpu/drm/drm_ioctl.c
drivers/gpu/drm/drm_mm.c
drivers/gpu/drm/drm_modes.c
drivers/gpu/drm/drm_pci.c
drivers/gpu/drm/drm_prime.c
drivers/gpu/drm/drm_rect.c [new file with mode: 0644]
drivers/gpu/drm/drm_stub.c
drivers/gpu/drm/drm_sysfs.c
drivers/gpu/drm/drm_trace.h
drivers/gpu/drm/drm_vm.c
drivers/gpu/drm/exynos/exynos_ddc.c
drivers/gpu/drm/exynos/exynos_drm_buf.c
drivers/gpu/drm/exynos/exynos_drm_connector.c
drivers/gpu/drm/exynos/exynos_drm_core.c
drivers/gpu/drm/exynos/exynos_drm_crtc.c
drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
drivers/gpu/drm/exynos/exynos_drm_drv.c
drivers/gpu/drm/exynos/exynos_drm_drv.h
drivers/gpu/drm/exynos/exynos_drm_encoder.c
drivers/gpu/drm/exynos/exynos_drm_fb.c
drivers/gpu/drm/exynos/exynos_drm_fbdev.c
drivers/gpu/drm/exynos/exynos_drm_fimc.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_gsc.c
drivers/gpu/drm/exynos/exynos_drm_hdmi.c
drivers/gpu/drm/exynos/exynos_drm_hdmi.h
drivers/gpu/drm/exynos/exynos_drm_ipp.c
drivers/gpu/drm/exynos/exynos_drm_plane.c
drivers/gpu/drm/exynos/exynos_drm_rotator.c
drivers/gpu/drm/exynos/exynos_drm_vidi.c
drivers/gpu/drm/exynos/exynos_hdmi.c
drivers/gpu/drm/exynos/exynos_hdmiphy.c
drivers/gpu/drm/exynos/exynos_mixer.c
drivers/gpu/drm/exynos/regs-mixer.h
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/dvo_ch7xxx.c
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_context.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_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_ums.c
drivers/gpu/drm/i915/intel_bios.c
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_dvo.c
drivers/gpu/drm/i915/intel_fb.c
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_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 [new file with mode: 0644]
drivers/gpu/drm/i915/intel_sprite.c
drivers/gpu/drm/i915/intel_tv.c
drivers/gpu/drm/mgag200/Makefile
drivers/gpu/drm/mgag200/mgag200_cursor.c [new file with mode: 0644]
drivers/gpu/drm/mgag200/mgag200_drv.h
drivers/gpu/drm/mgag200/mgag200_fb.c
drivers/gpu/drm/mgag200/mgag200_main.c
drivers/gpu/drm/mgag200/mgag200_mode.c
drivers/gpu/drm/mgag200/mgag200_reg.h
drivers/gpu/drm/mgag200/mgag200_ttm.c
drivers/gpu/drm/nouveau/Kconfig
drivers/gpu/drm/nouveau/Makefile
drivers/gpu/drm/nouveau/core/core/falcon.c [deleted file]
drivers/gpu/drm/nouveau/core/core/mm.c
drivers/gpu/drm/nouveau/core/engine/bsp/nv84.c
drivers/gpu/drm/nouveau/core/engine/bsp/nv98.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/bsp/nvc0.c
drivers/gpu/drm/nouveau/core/engine/bsp/nve0.c
drivers/gpu/drm/nouveau/core/engine/copy/fuc/nva3.fuc.h
drivers/gpu/drm/nouveau/core/engine/copy/fuc/nvc0.fuc.h
drivers/gpu/drm/nouveau/core/engine/copy/nva3.c
drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c
drivers/gpu/drm/nouveau/core/engine/copy/nve0.c
drivers/gpu/drm/nouveau/core/engine/crypt/fuc/nv98.fuc.h
drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c
drivers/gpu/drm/nouveau/core/engine/crypt/nv98.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/disp/hdminva3.c
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c
drivers/gpu/drm/nouveau/core/engine/falcon.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv84.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 [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c [deleted file]
drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc
drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h
drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc
drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h
drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc
drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h
drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc
drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h
drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/nvc0.fuc [deleted file]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/nve0.fuc [deleted file]
drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h
drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/nve0.c [deleted file]
drivers/gpu/drm/nouveau/core/engine/graph/nve4.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c
drivers/gpu/drm/nouveau/core/engine/mpeg/nv84.c
drivers/gpu/drm/nouveau/core/engine/ppp/nvc0.c
drivers/gpu/drm/nouveau/core/engine/vp/nv84.c
drivers/gpu/drm/nouveau/core/engine/vp/nv98.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/vp/nvc0.c
drivers/gpu/drm/nouveau/core/engine/vp/nve0.c
drivers/gpu/drm/nouveau/core/engine/xtensa.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/core/device.h
drivers/gpu/drm/nouveau/core/include/core/falcon.h [deleted file]
drivers/gpu/drm/nouveau/core/include/core/mm.h
drivers/gpu/drm/nouveau/core/include/engine/bsp.h
drivers/gpu/drm/nouveau/core/include/engine/copy.h
drivers/gpu/drm/nouveau/core/include/engine/falcon.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/engine/graph.h
drivers/gpu/drm/nouveau/core/include/engine/mpeg.h
drivers/gpu/drm/nouveau/core/include/engine/vp.h
drivers/gpu/drm/nouveau/core/include/engine/xtensa.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/include/subdev/clock.h
drivers/gpu/drm/nouveau/core/include/subdev/devinit.h
drivers/gpu/drm/nouveau/core/include/subdev/fb.h
drivers/gpu/drm/nouveau/core/include/subdev/vm.h
drivers/gpu/drm/nouveau/core/subdev/bar/nv50.c
drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/bios/base.c
drivers/gpu/drm/nouveau/core/subdev/bios/init.c
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/nva3.c
drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/clock/pll.h
drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c
drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c
drivers/gpu/drm/nouveau/core/subdev/devinit/base.c
drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c
drivers/gpu/drm/nouveau/core/subdev/devinit/nv05.c
drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c
drivers/gpu/drm/nouveau/core/subdev/devinit/nv1a.c
drivers/gpu/drm/nouveau/core/subdev/devinit/nv20.c
drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c
drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/base.c
drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c
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/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/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/fb/priv.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv10.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv1a.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv20.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c
drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c
drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/vm/base.c
drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c
drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c
drivers/gpu/drm/nouveau/nouveau_abi16.c
drivers/gpu/drm/nouveau/nouveau_bios.c
drivers/gpu/drm/nouveau/nouveau_bo.c
drivers/gpu/drm/nouveau/nouveau_chan.c
drivers/gpu/drm/nouveau/nouveau_display.c
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_fence.h
drivers/gpu/drm/nouveau/nouveau_gem.c
drivers/gpu/drm/nouveau/nouveau_gem.h
drivers/gpu/drm/nouveau/nouveau_mem.c
drivers/gpu/drm/nouveau/nouveau_prime.c
drivers/gpu/drm/nouveau/nouveau_ttm.c
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_pm.c
drivers/gpu/drm/nouveau/nva3_pm.c
drivers/gpu/drm/nouveau/nvc0_pm.c
drivers/gpu/drm/omapdrm/Kconfig
drivers/gpu/drm/omapdrm/omap_crtc.c
drivers/gpu/drm/omapdrm/omap_drv.c
drivers/gpu/drm/omapdrm/omap_drv.h
drivers/gpu/drm/omapdrm/omap_fbdev.c
drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
drivers/gpu/drm/qxl/qxl_cmd.c
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_ioctl.c
drivers/gpu/drm/qxl/qxl_kms.c
drivers/gpu/drm/qxl/qxl_object.c
drivers/gpu/drm/qxl/qxl_object.h
drivers/gpu/drm/radeon/Makefile
drivers/gpu/drm/radeon/ObjectID.h
drivers/gpu/drm/radeon/atombios.h
drivers/gpu/drm/radeon/atombios_crtc.c
drivers/gpu/drm/radeon/atombios_encoders.c
drivers/gpu/drm/radeon/btc_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/btc_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/btcd.h [new file with mode: 0644]
drivers/gpu/drm/radeon/cik.c [new file with mode: 0644]
drivers/gpu/drm/radeon/cik_blit_shaders.c [new file with mode: 0644]
drivers/gpu/drm/radeon/cik_blit_shaders.h [new file with mode: 0644]
drivers/gpu/drm/radeon/cik_reg.h [new file with mode: 0644]
drivers/gpu/drm/radeon/cikd.h [new file with mode: 0644]
drivers/gpu/drm/radeon/clearstate_cayman.h [new file with mode: 0644]
drivers/gpu/drm/radeon/clearstate_defs.h [new file with mode: 0644]
drivers/gpu/drm/radeon/clearstate_evergreen.h [new file with mode: 0644]
drivers/gpu/drm/radeon/clearstate_si.h [new file with mode: 0644]
drivers/gpu/drm/radeon/cypress_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/cypress_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/evergreen.c
drivers/gpu/drm/radeon/evergreen_hdmi.c
drivers/gpu/drm/radeon/evergreen_reg.h
drivers/gpu/drm/radeon/evergreen_smc.h [new file with mode: 0644]
drivers/gpu/drm/radeon/evergreend.h
drivers/gpu/drm/radeon/ni.c
drivers/gpu/drm/radeon/ni_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/ni_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/nid.h
drivers/gpu/drm/radeon/nislands_smc.h [new file with mode: 0644]
drivers/gpu/drm/radeon/ppsmc.h [new file with mode: 0644]
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/r600_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/r600_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/r600_hdmi.c
drivers/gpu/drm/radeon/r600_reg.h
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_atombios.c
drivers/gpu/drm/radeon/radeon_cs.c
drivers/gpu/drm/radeon/radeon_cursor.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_family.h
drivers/gpu/drm/radeon/radeon_irq_kms.c
drivers/gpu/drm/radeon/radeon_kms.c
drivers/gpu/drm/radeon/radeon_mode.h
drivers/gpu/drm/radeon/radeon_object.c
drivers/gpu/drm/radeon/radeon_object.h
drivers/gpu/drm/radeon/radeon_pm.c
drivers/gpu/drm/radeon/radeon_prime.c
drivers/gpu/drm/radeon/radeon_reg.h
drivers/gpu/drm/radeon/radeon_ring.c
drivers/gpu/drm/radeon/radeon_test.c
drivers/gpu/drm/radeon/radeon_ucode.h [new file with mode: 0644]
drivers/gpu/drm/radeon/radeon_uvd.c
drivers/gpu/drm/radeon/rs690.c
drivers/gpu/drm/radeon/rs780_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/rs780_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/rs780d.h [new file with mode: 0644]
drivers/gpu/drm/radeon/rv515.c
drivers/gpu/drm/radeon/rv6xx_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/rv6xx_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/rv6xxd.h [new file with mode: 0644]
drivers/gpu/drm/radeon/rv730_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/rv730d.h [new file with mode: 0644]
drivers/gpu/drm/radeon/rv740_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/rv740d.h [new file with mode: 0644]
drivers/gpu/drm/radeon/rv770_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/rv770_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/rv770_smc.c [new file with mode: 0644]
drivers/gpu/drm/radeon/rv770_smc.h [new file with mode: 0644]
drivers/gpu/drm/radeon/rv770d.h
drivers/gpu/drm/radeon/si.c
drivers/gpu/drm/radeon/si_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/si_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/si_smc.c [new file with mode: 0644]
drivers/gpu/drm/radeon/sid.h
drivers/gpu/drm/radeon/sislands_smc.h [new file with mode: 0644]
drivers/gpu/drm/radeon/sumo_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/sumo_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/sumo_smc.c [new file with mode: 0644]
drivers/gpu/drm/radeon/sumod.h [new file with mode: 0644]
drivers/gpu/drm/radeon/trinity_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/trinity_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/trinity_smc.c [new file with mode: 0644]
drivers/gpu/drm/radeon/trinityd.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/Kconfig [new file with mode: 0644]
drivers/gpu/drm/rcar-du/Makefile [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_crtc.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_crtc.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_drv.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_drv.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_kms.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_kms.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_lvds.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_lvds.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_plane.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_plane.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_regs.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_vga.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_vga.h [new file with mode: 0644]
drivers/gpu/drm/savage/savage_bci.c
drivers/gpu/drm/savage/savage_drv.h
drivers/gpu/drm/shmobile/Kconfig
drivers/gpu/drm/shmobile/shmob_drm_drv.c
drivers/gpu/drm/shmobile/shmob_drm_kms.c
drivers/gpu/drm/shmobile/shmob_drm_plane.c
drivers/gpu/drm/tilcdc/tilcdc_crtc.c
drivers/gpu/drm/tilcdc/tilcdc_drv.c
drivers/gpu/drm/tilcdc/tilcdc_drv.h
drivers/gpu/drm/tilcdc/tilcdc_panel.c
drivers/gpu/drm/tilcdc/tilcdc_regs.h
drivers/gpu/drm/tilcdc/tilcdc_slave.c
drivers/gpu/drm/tilcdc/tilcdc_tfp410.c
drivers/gpu/drm/ttm/ttm_bo.c
drivers/gpu/drm/ttm/ttm_bo_manager.c
drivers/gpu/drm/ttm/ttm_bo_util.c
drivers/gpu/drm/ttm/ttm_execbuf_util.c
drivers/gpu/drm/udl/udl_fb.c
drivers/gpu/drm/udl/udl_modeset.c
drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
drivers/gpu/host1x/dev.h
drivers/gpu/host1x/drm/dc.c
drivers/gpu/host1x/drm/drm.c
drivers/gpu/host1x/drm/gr2d.c
drivers/gpu/host1x/hw/cdma_hw.c
drivers/gpu/host1x/hw/syncpt_hw.c
drivers/gpu/host1x/job.c
drivers/gpu/host1x/syncpt.c
drivers/gpu/host1x/syncpt.h
drivers/infiniband/core/cma.c
drivers/infiniband/hw/mlx4/main.c
drivers/iommu/msm_iommu_dev.c
drivers/isdn/i4l/isdn_net.c
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/bonding/bond_alb.c
drivers/net/bonding/bond_main.c
drivers/net/bonding/bond_sysfs.c
drivers/net/bonding/bonding.h
drivers/net/caif/caif_serial.c
drivers/net/can/Kconfig
drivers/net/can/at91_can.c
drivers/net/can/bfin_can.c
drivers/net/can/c_can/c_can_platform.c
drivers/net/can/cc770/cc770_isa.c
drivers/net/can/cc770/cc770_platform.c
drivers/net/can/flexcan.c
drivers/net/can/grcan.c
drivers/net/can/janz-ican3.c
drivers/net/can/led.c
drivers/net/can/mscan/mpc5xxx_can.c
drivers/net/can/sja1000/sja1000_isa.c
drivers/net/can/sja1000/sja1000_of_platform.c
drivers/net/can/sja1000/sja1000_platform.c
drivers/net/can/slcan.c
drivers/net/can/softing/softing_main.c
drivers/net/can/ti_hecc.c
drivers/net/ethernet/3com/3c509.c
drivers/net/ethernet/3com/3c59x.c
drivers/net/ethernet/3com/Kconfig
drivers/net/ethernet/8390/ne.c
drivers/net/ethernet/8390/ne2k-pci.c
drivers/net/ethernet/Kconfig
drivers/net/ethernet/Makefile
drivers/net/ethernet/adaptec/Kconfig
drivers/net/ethernet/adi/Kconfig
drivers/net/ethernet/adi/bfin_mac.c
drivers/net/ethernet/aeroflex/greth.c
drivers/net/ethernet/allwinner/Kconfig [new file with mode: 0644]
drivers/net/ethernet/allwinner/Makefile [new file with mode: 0644]
drivers/net/ethernet/allwinner/sun4i-emac.c [new file with mode: 0644]
drivers/net/ethernet/allwinner/sun4i-emac.h [new file with mode: 0644]
drivers/net/ethernet/alteon/acenic.c
drivers/net/ethernet/amd/Kconfig
drivers/net/ethernet/amd/amd8111e.c
drivers/net/ethernet/amd/au1000_eth.c
drivers/net/ethernet/amd/sunlance.c
drivers/net/ethernet/apple/bmac.c
drivers/net/ethernet/arc/Kconfig [new file with mode: 0644]
drivers/net/ethernet/arc/Makefile [new file with mode: 0644]
drivers/net/ethernet/arc/emac.h [new file with mode: 0644]
drivers/net/ethernet/arc/emac_main.c [new file with mode: 0644]
drivers/net/ethernet/arc/emac_mdio.c [new file with mode: 0644]
drivers/net/ethernet/atheros/Kconfig
drivers/net/ethernet/atheros/alx/alx.h
drivers/net/ethernet/atheros/alx/ethtool.c
drivers/net/ethernet/atheros/alx/hw.c
drivers/net/ethernet/atheros/alx/hw.h
drivers/net/ethernet/atheros/alx/main.c
drivers/net/ethernet/atheros/atl1c/atl1c_main.c
drivers/net/ethernet/atheros/atl1e/atl1e_main.c
drivers/net/ethernet/atheros/atlx/atl1.c
drivers/net/ethernet/broadcom/Kconfig
drivers/net/ethernet/broadcom/bcm63xx_enet.c
drivers/net/ethernet/broadcom/bcm63xx_enet.h
drivers/net/ethernet/broadcom/bnx2.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h
drivers/net/ethernet/broadcom/cnic.c
drivers/net/ethernet/broadcom/sb1250-mac.c
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/broadcom/tg3.h
drivers/net/ethernet/brocade/bna/bfa_defs.h
drivers/net/ethernet/brocade/bna/bfa_ioc.c
drivers/net/ethernet/brocade/bna/bfa_ioc.h
drivers/net/ethernet/brocade/bna/bna.h
drivers/net/ethernet/brocade/bna/bna_enet.c
drivers/net/ethernet/brocade/bna/bna_tx_rx.c
drivers/net/ethernet/brocade/bna/bnad.c
drivers/net/ethernet/brocade/bna/bnad.h
drivers/net/ethernet/brocade/bna/cna.h
drivers/net/ethernet/cadence/Kconfig
drivers/net/ethernet/cadence/at91_ether.c
drivers/net/ethernet/cadence/macb.c
drivers/net/ethernet/cadence/macb.h
drivers/net/ethernet/calxeda/xgmac.c
drivers/net/ethernet/chelsio/cxgb/cxgb2.c
drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c
drivers/net/ethernet/chelsio/cxgb3/sge.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/cirrus/Kconfig
drivers/net/ethernet/cirrus/ep93xx_eth.c
drivers/net/ethernet/cisco/enic/enic_main.c
drivers/net/ethernet/davicom/Kconfig
drivers/net/ethernet/davicom/dm9000.c
drivers/net/ethernet/dec/tulip/Kconfig
drivers/net/ethernet/dec/tulip/tulip_core.c
drivers/net/ethernet/dec/tulip/xircom_cb.c
drivers/net/ethernet/dlink/Kconfig
drivers/net/ethernet/emulex/benet/be.h
drivers/net/ethernet/emulex/benet/be_cmds.c
drivers/net/ethernet/emulex/benet/be_cmds.h
drivers/net/ethernet/emulex/benet/be_ethtool.c
drivers/net/ethernet/emulex/benet/be_hw.h
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/ethoc.c
drivers/net/ethernet/faraday/Kconfig
drivers/net/ethernet/faraday/ftgmac100.c
drivers/net/ethernet/faraday/ftmac100.c
drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/freescale/fec_mpc52xx.c
drivers/net/ethernet/freescale/fec_ptp.c
drivers/net/ethernet/freescale/fs_enet/Kconfig
drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
drivers/net/ethernet/freescale/fs_enet/mii-fec.c
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/freescale/gianfar_ptp.c
drivers/net/ethernet/freescale/ucc_geth.c
drivers/net/ethernet/freescale/xgmac_mdio.c
drivers/net/ethernet/ibm/Kconfig
drivers/net/ethernet/ibm/ehea/ehea_main.c
drivers/net/ethernet/ibm/emac/mal.c
drivers/net/ethernet/ibm/emac/rgmii.c
drivers/net/ethernet/ibm/emac/tah.c
drivers/net/ethernet/ibm/emac/zmii.c
drivers/net/ethernet/icplus/Kconfig
drivers/net/ethernet/icplus/ipg.c
drivers/net/ethernet/intel/Kconfig
drivers/net/ethernet/intel/e100.c
drivers/net/ethernet/intel/e1000e/80003es2lan.c
drivers/net/ethernet/intel/e1000e/82571.c
drivers/net/ethernet/intel/e1000e/ethtool.c
drivers/net/ethernet/intel/e1000e/hw.h
drivers/net/ethernet/intel/e1000e/ich8lan.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/e1000e/nvm.c
drivers/net/ethernet/intel/e1000e/phy.c
drivers/net/ethernet/intel/igb/e1000_82575.c
drivers/net/ethernet/intel/igb/e1000_defines.h
drivers/net/ethernet/intel/igb/e1000_hw.h
drivers/net/ethernet/intel/igb/e1000_i210.h
drivers/net/ethernet/intel/igb/e1000_mac.c
drivers/net/ethernet/intel/igb/e1000_phy.c
drivers/net/ethernet/intel/igb/e1000_phy.h
drivers/net/ethernet/intel/igb/igb.h
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/jme.c
drivers/net/ethernet/korina.c
drivers/net/ethernet/marvell/mv643xx_eth.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/pxa168_eth.c
drivers/net/ethernet/marvell/skge.c
drivers/net/ethernet/marvell/sky2.c
drivers/net/ethernet/mellanox/mlx4/cmd.c
drivers/net/ethernet/mellanox/mlx4/en_cq.c
drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
drivers/net/ethernet/mellanox/mlx4/en_main.c
drivers/net/ethernet/mellanox/mlx4/en_netdev.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/en_tx.c
drivers/net/ethernet/mellanox/mlx4/eq.c
drivers/net/ethernet/mellanox/mlx4/fw.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx4/mlx4.h
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
drivers/net/ethernet/micrel/Kconfig
drivers/net/ethernet/micrel/ks8695net.c
drivers/net/ethernet/micrel/ks8842.c
drivers/net/ethernet/micrel/ks8851_mll.c
drivers/net/ethernet/myricom/myri10ge/myri10ge.c
drivers/net/ethernet/netx-eth.c
drivers/net/ethernet/nuvoton/Kconfig
drivers/net/ethernet/nuvoton/w90p910_ether.c
drivers/net/ethernet/nvidia/forcedeth.c
drivers/net/ethernet/nxp/lpc_eth.c
drivers/net/ethernet/octeon/octeon_mgmt.c
drivers/net/ethernet/oki-semi/pch_gbe/Kconfig
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c
drivers/net/ethernet/packetengines/Kconfig
drivers/net/ethernet/qlogic/netxen/netxen_nic.h
drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h
drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
drivers/net/ethernet/qlogic/qlge/qlge_main.c
drivers/net/ethernet/rdc/Kconfig
drivers/net/ethernet/realtek/8139cp.c
drivers/net/ethernet/realtek/Kconfig
drivers/net/ethernet/renesas/Kconfig
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/renesas/sh_eth.h
drivers/net/ethernet/s6gmac.c
drivers/net/ethernet/seeq/sgiseeq.c
drivers/net/ethernet/sfc/efx.c
drivers/net/ethernet/sfc/efx.h
drivers/net/ethernet/sfc/ethtool.c
drivers/net/ethernet/sfc/filter.c
drivers/net/ethernet/sfc/net_driver.h
drivers/net/ethernet/sfc/nic.c
drivers/net/ethernet/sfc/nic.h
drivers/net/ethernet/sfc/ptp.c
drivers/net/ethernet/sfc/rx.c
drivers/net/ethernet/sfc/siena.c
drivers/net/ethernet/sgi/Kconfig
drivers/net/ethernet/sgi/ioc3-eth.c
drivers/net/ethernet/sgi/meth.c
drivers/net/ethernet/silan/sc92031.c
drivers/net/ethernet/sis/Kconfig
drivers/net/ethernet/sis/sis190.c
drivers/net/ethernet/smsc/Kconfig
drivers/net/ethernet/smsc/smc911x.c
drivers/net/ethernet/smsc/smc91x.c
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/ethernet/stmicro/stmmac/Kconfig
drivers/net/ethernet/stmicro/stmmac/common.h
drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
drivers/net/ethernet/stmicro/stmmac/enh_desc.c
drivers/net/ethernet/stmicro/stmmac/norm_desc.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
drivers/net/ethernet/sun/cassini.c
drivers/net/ethernet/sun/niu.c
drivers/net/ethernet/sun/sunbmac.c
drivers/net/ethernet/sun/sungem.c
drivers/net/ethernet/sun/sunhme.c
drivers/net/ethernet/sun/sunqe.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/davinci_cpdma.c
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/ethernet/ti/davinci_mdio.c
drivers/net/ethernet/ti/tlan.c
drivers/net/ethernet/ti/tlan.h
drivers/net/ethernet/toshiba/tc35815.c
drivers/net/ethernet/tundra/tsi108_eth.c
drivers/net/ethernet/via/Kconfig
drivers/net/ethernet/via/via-velocity.c
drivers/net/ethernet/via/via-velocity.h
drivers/net/ethernet/wiznet/w5100.c
drivers/net/ethernet/wiznet/w5300.c
drivers/net/ethernet/xilinx/Kconfig
drivers/net/ethernet/xilinx/ll_temac_main.c
drivers/net/ethernet/xilinx/xilinx_axienet_main.c
drivers/net/ethernet/xilinx/xilinx_emaclite.c
drivers/net/ethernet/xscale/ixp4xx_eth.c
drivers/net/fddi/skfp/skfddi.c
drivers/net/hamradio/bpqether.c
drivers/net/hippi/rrunner.c
drivers/net/irda/bfin_sir.c
drivers/net/irda/sh_irda.c
drivers/net/irda/sh_sir.c
drivers/net/macvlan.c
drivers/net/macvtap.c
drivers/net/netconsole.c
drivers/net/nlmon.c [new file with mode: 0644]
drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/at803x.c
drivers/net/phy/bcm63xx.c
drivers/net/phy/marvell.c
drivers/net/phy/mdio-sun4i.c [new file with mode: 0644]
drivers/net/phy/phy.c
drivers/net/phy/phy_device.c
drivers/net/phy/spi_ks8995.c
drivers/net/phy/vitesse.c
drivers/net/ppp/pppoe.c
drivers/net/team/team.c
drivers/net/team/team_mode_loadbalance.c
drivers/net/team/team_mode_roundrobin.c
drivers/net/tun.c
drivers/net/usb/Kconfig
drivers/net/usb/ax88179_178a.c
drivers/net/usb/cdc_ether.c
drivers/net/usb/ipheth.c
drivers/net/usb/kalmia.c
drivers/net/usb/qmi_wwan.c
drivers/net/usb/r8152.c
drivers/net/veth.c
drivers/net/virtio_net.c
drivers/net/vxlan.c
drivers/net/wan/dlci.c
drivers/net/wan/hdlc.c
drivers/net/wan/ixp4xx_hss.c
drivers/net/wan/lapbether.c
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/ath/Kconfig
drivers/net/wireless/ath/Makefile
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath10k/Kconfig [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/Makefile [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/bmi.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/bmi.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/ce.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/ce.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/core.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/core.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/debug.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/debug.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/hif.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htc.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htc.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt_rx.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt_tx.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/hw.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/mac.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/mac.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/pci.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/pci.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/rx_desc.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/targaddrs.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/trace.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/trace.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/txrx.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/txrx.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/wmi.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/wmi.h [new file with mode: 0644]
drivers/net/wireless/ath/ath5k/ahb.c
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath5k/base.h
drivers/net/wireless/ath/ath5k/mac80211-ops.c
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/debug.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/sdio.c
drivers/net/wireless/ath/ath6kl/usb.c
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/ahb.c
drivers/net/wireless/ath/ath9k/ani.c
drivers/net/wireless/ath/ath9k/ani.h
drivers/net/wireless/ath/ath9k/ar5008_phy.c
drivers/net/wireless/ath/ath9k/ar9002_hw.c
drivers/net/wireless/ath/ath9k/ar9002_initvals.h
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_hw.c
drivers/net/wireless/ath/ath9k/ar9003_mac.c
drivers/net/wireless/ath/ath9k/ar9003_paprd.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h
drivers/net/wireless/ath/ath9k/ar9462_2p1_initvals.h [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/calib.c
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/dfs_debug.c
drivers/net/wireless/ath/ath9k/hif_usb.c
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
drivers/net/wireless/ath/ath9k/htc_drv_debug.c
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/link.c
drivers/net/wireless/ath/ath9k/mac.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/reg.h
drivers/net/wireless/ath/ath9k/wow.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/carl9170/carl9170.h
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/ath/carl9170/tx.c
drivers/net/wireless/ath/regd.c
drivers/net/wireless/ath/wil6210/Kconfig
drivers/net/wireless/ath/wil6210/Makefile
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/debug.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/interrupt.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/netdev.c
drivers/net/wireless/ath/wil6210/trace.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/trace.h [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/txrx.h
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/b43/Kconfig
drivers/net/wireless/b43/main.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/fweh.h
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
drivers/net/wireless/cw1200/Kconfig [new file with mode: 0644]
drivers/net/wireless/cw1200/Makefile [new file with mode: 0644]
drivers/net/wireless/cw1200/bh.c [new file with mode: 0644]
drivers/net/wireless/cw1200/bh.h [new file with mode: 0644]
drivers/net/wireless/cw1200/cw1200.h [new file with mode: 0644]
drivers/net/wireless/cw1200/cw1200_sdio.c [new file with mode: 0644]
drivers/net/wireless/cw1200/cw1200_spi.c [new file with mode: 0644]
drivers/net/wireless/cw1200/debug.c [new file with mode: 0644]
drivers/net/wireless/cw1200/debug.h [new file with mode: 0644]
drivers/net/wireless/cw1200/fwio.c [new file with mode: 0644]
drivers/net/wireless/cw1200/fwio.h [new file with mode: 0644]
drivers/net/wireless/cw1200/hwbus.h [new file with mode: 0644]
drivers/net/wireless/cw1200/hwio.c [new file with mode: 0644]
drivers/net/wireless/cw1200/hwio.h [new file with mode: 0644]
drivers/net/wireless/cw1200/main.c [new file with mode: 0644]
drivers/net/wireless/cw1200/pm.c [new file with mode: 0644]
drivers/net/wireless/cw1200/pm.h [new file with mode: 0644]
drivers/net/wireless/cw1200/queue.c [new file with mode: 0644]
drivers/net/wireless/cw1200/queue.h [new file with mode: 0644]
drivers/net/wireless/cw1200/scan.c [new file with mode: 0644]
drivers/net/wireless/cw1200/scan.h [new file with mode: 0644]
drivers/net/wireless/cw1200/sta.c [new file with mode: 0644]
drivers/net/wireless/cw1200/sta.h [new file with mode: 0644]
drivers/net/wireless/cw1200/txrx.c [new file with mode: 0644]
drivers/net/wireless/cw1200/txrx.h [new file with mode: 0644]
drivers/net/wireless/cw1200/wsm.c [new file with mode: 0644]
drivers/net/wireless/cw1200/wsm.h [new file with mode: 0644]
drivers/net/wireless/ipw2x00/ipw2100.c
drivers/net/wireless/ipw2x00/ipw2200.c
drivers/net/wireless/ipw2x00/libipw_rx.c
drivers/net/wireless/iwlegacy/3945-mac.c
drivers/net/wireless/iwlegacy/3945.c
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/commands.h
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlegacy/common.h
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/Makefile
drivers/net/wireless/iwlwifi/dvm/Makefile
drivers/net/wireless/iwlwifi/dvm/agn.h
drivers/net/wireless/iwlwifi/dvm/calib.c
drivers/net/wireless/iwlwifi/dvm/commands.h
drivers/net/wireless/iwlwifi/dvm/dev.h
drivers/net/wireless/iwlwifi/dvm/devices.c
drivers/net/wireless/iwlwifi/dvm/lib.c
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/dvm/power.c
drivers/net/wireless/iwlwifi/dvm/rs.c
drivers/net/wireless/iwlwifi/dvm/rx.c
drivers/net/wireless/iwlwifi/dvm/scan.c
drivers/net/wireless/iwlwifi/dvm/testmode.c [deleted file]
drivers/net/wireless/iwlwifi/dvm/tt.c
drivers/net/wireless/iwlwifi/dvm/tx.c
drivers/net/wireless/iwlwifi/dvm/ucode.c
drivers/net/wireless/iwlwifi/iwl-1000.c
drivers/net/wireless/iwlwifi/iwl-2000.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-csr.h
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-drv.h
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
drivers/net/wireless/iwlwifi/iwl-phy-db.c
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-test.c [deleted file]
drivers/net/wireless/iwlwifi/iwl-test.h [deleted file]
drivers/net/wireless/iwlwifi/iwl-testmode.h [deleted file]
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/Makefile
drivers/net/wireless/iwlwifi/mvm/bt-coex.c
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/nvm.c
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/quota.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rs.h
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/tt.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/internal.h
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/libertas/mesh.c
drivers/net/wireless/mwifiex/11h.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/Kconfig
drivers/net/wireless/mwifiex/Makefile
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/scan.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sdio.h
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/uap_cmd.c
drivers/net/wireless/mwifiex/uap_event.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/orinoco/orinoco_pci.h
drivers/net/wireless/orinoco/orinoco_usb.c
drivers/net/wireless/p54/p54spi.c
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2800.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00pci.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rt2x00/rt2x00queue.h
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rtlwifi/base.c
drivers/net/wireless/rtlwifi/rtl8192cu/rf.c
drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
drivers/net/wireless/rtlwifi/rtl8192de/dm.c
drivers/net/wireless/rtlwifi/rtl8723ae/sw.c
drivers/net/wireless/ti/wl1251/spi.c
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/reg.h
drivers/net/wireless/ti/wlcore/Makefile
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/ps.c
drivers/net/wireless/ti/wlcore/spi.c
drivers/net/wireless/ti/wlcore/sysfs.c [new file with mode: 0644]
drivers/net/wireless/ti/wlcore/sysfs.h [new file with mode: 0644]
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/xen-netback/common.h
drivers/net/xen-netback/interface.c
drivers/net/xen-netback/netback.c
drivers/net/xen-netback/xenbus.c
drivers/net/xen-netfront.c
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/mei_phy.c
drivers/nfc/microread/microread.c
drivers/nfc/nfcsim.c [new file with mode: 0644]
drivers/nfc/nfcwilink.c
drivers/nfc/pn533.c
drivers/nfc/pn544/pn544.c
drivers/of/of_net.c
drivers/power/reset/restart-poweroff.c
drivers/power/reset/vexpress-poweroff.c
drivers/rtc/rtc-stmp3xxx.c
drivers/s390/net/netiucv.c
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
drivers/scsi/fcoe/fcoe.c
drivers/scsi/fcoe/fcoe_transport.c
drivers/scsi/libiscsi_tcp.c
drivers/scsi/scsi_debug.c
drivers/ssb/driver_chipcommon_sflash.c
drivers/ssb/main.c
drivers/ssb/pcihost_wrapper.c
drivers/ssb/sprom.c
drivers/ssb/ssb_private.h
drivers/staging/csr/netdev.c
drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c
drivers/staging/ft1000/ft1000-usb/ft1000_proc.c
drivers/staging/imx-drm/ipuv3-crtc.c
drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h
drivers/staging/lustre/lustre/include/linux/lvfs.h
drivers/staging/lustre/lustre/include/lprocfs_status.h
drivers/staging/lustre/lustre/llite/dcache.c
drivers/staging/lustre/lustre/llite/llite_internal.h
drivers/staging/lustre/lustre/llite/llite_lib.c
drivers/staging/lustre/lustre/llite/namei.c
drivers/staging/lustre/lustre/lvfs/lvfs_linux.c
drivers/staging/lustre/lustre/obdclass/lprocfs_status.c
drivers/staging/rtl8192u/r8192U_core.c
drivers/staging/silicom/Kconfig
drivers/staging/silicom/bpctl_mod.c
drivers/vhost/net.c
drivers/video/Kconfig
drivers/video/aty/aty128fb.c
drivers/video/aty/atyfb_base.c
drivers/video/aty/radeon_pm.c
drivers/video/au1100fb.c
drivers/video/bf54x-lq043fb.c
drivers/video/bfin-lq035q1-fb.c
drivers/video/bfin-t350mcqb-fb.c
drivers/video/console/vgacon.c
drivers/video/ep93xx-fb.c
drivers/video/fbmem.c
drivers/video/fsl-diu-fb.c
drivers/video/i740fb.c
drivers/video/imxfb.c
drivers/video/jz4740_fb.c
drivers/video/mmp/fb/mmpfb.c
drivers/video/mmp/hw/mmp_ctrl.c
drivers/video/mxsfb.c
drivers/video/nuc900fb.c
drivers/video/of_display_timing.c
drivers/video/omap2/Kconfig
drivers/video/omap2/Makefile
drivers/video/omap2/displays-new/Kconfig [new file with mode: 0644]
drivers/video/omap2/displays-new/Makefile [new file with mode: 0644]
drivers/video/omap2/displays-new/connector-analog-tv.c [new file with mode: 0644]
drivers/video/omap2/displays-new/connector-dvi.c [new file with mode: 0644]
drivers/video/omap2/displays-new/connector-hdmi.c [new file with mode: 0644]
drivers/video/omap2/displays-new/encoder-tfp410.c [new file with mode: 0644]
drivers/video/omap2/displays-new/encoder-tpd12s015.c [new file with mode: 0644]
drivers/video/omap2/displays-new/panel-dpi.c [new file with mode: 0644]
drivers/video/omap2/displays-new/panel-dsi-cm.c [new file with mode: 0644]
drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c [new file with mode: 0644]
drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c [new file with mode: 0644]
drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c [new file with mode: 0644]
drivers/video/omap2/displays-new/panel-sony-acx565akm.c [new file with mode: 0644]
drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c [new file with mode: 0644]
drivers/video/omap2/displays/Kconfig
drivers/video/omap2/displays/panel-acx565akm.c
drivers/video/omap2/displays/panel-generic-dpi.c
drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
drivers/video/omap2/displays/panel-n8x0.c
drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
drivers/video/omap2/displays/panel-picodlp.c
drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
drivers/video/omap2/displays/panel-taal.c
drivers/video/omap2/displays/panel-tfp410.c
drivers/video/omap2/displays/panel-tpo-td043mtea1.c
drivers/video/omap2/dss/Kconfig
drivers/video/omap2/dss/apply.c
drivers/video/omap2/dss/core.c
drivers/video/omap2/dss/dispc-compat.c
drivers/video/omap2/dss/dispc.c
drivers/video/omap2/dss/display-sysfs.c
drivers/video/omap2/dss/display.c
drivers/video/omap2/dss/dpi.c
drivers/video/omap2/dss/dsi.c
drivers/video/omap2/dss/dss.c
drivers/video/omap2/dss/dss.h
drivers/video/omap2/dss/dss_features.c
drivers/video/omap2/dss/hdmi.c
drivers/video/omap2/dss/manager-sysfs.c
drivers/video/omap2/dss/manager.c
drivers/video/omap2/dss/output.c
drivers/video/omap2/dss/rfbi.c
drivers/video/omap2/dss/sdi.c
drivers/video/omap2/dss/ti_hdmi.h
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
drivers/video/omap2/dss/venc.c
drivers/video/omap2/dss/venc_panel.c
drivers/video/omap2/omapfb/omapfb-ioctl.c
drivers/video/omap2/omapfb/omapfb-main.c
drivers/video/pxa3xx-gcu.c
drivers/video/pxafb.c
drivers/video/s3c2410fb.c
drivers/video/sa1100fb.c
drivers/video/sh7760fb.c
drivers/video/sh_mipi_dsi.c
drivers/video/smscufx.c
drivers/video/ssd1307fb.c
drivers/video/tmiofb.c
drivers/video/udlfb.c
drivers/video/uvesafb.c
drivers/video/vga16fb.c
drivers/video/vt8500lcdfb.c
drivers/video/wm8505fb.c
drivers/video/xilinxfb.c
drivers/virtio/virtio_ring.c
fs/autofs4/expire.c
fs/autofs4/root.c
fs/btrfs/backref.c
fs/btrfs/backref.h
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/delayed-inode.c
fs/btrfs/dev-replace.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/file-item.c
fs/btrfs/file.c
fs/btrfs/free-space-cache.c
fs/btrfs/free-space-cache.h
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/lzo.c
fs/btrfs/ordered-data.c
fs/btrfs/ordered-data.h
fs/btrfs/qgroup.c
fs/btrfs/relocation.c
fs/btrfs/root-tree.c
fs/btrfs/scrub.c
fs/btrfs/send.c
fs/btrfs/super.c
fs/btrfs/transaction.c
fs/btrfs/transaction.h
fs/btrfs/tree-log.c
fs/btrfs/ulist.c
fs/btrfs/version.h [deleted file]
fs/btrfs/volumes.c
fs/btrfs/volumes.h
fs/ceph/addr.c
fs/ceph/caps.c
fs/ceph/file.c
fs/ceph/inode.c
fs/ceph/locks.c
fs/ceph/mds_client.c
fs/ceph/mdsmap.c
fs/ceph/super.c
fs/ceph/super.h
fs/ceph/xattr.c
fs/coda/dir.c
fs/configfs/dir.c
fs/ecryptfs/inode.c
fs/ext3/fsync.c
fs/ext3/super.c
fs/f2fs/dir.c
fs/fat/fat.h
fs/fat/file.c
fs/fat/inode.c
fs/fs-writeback.c
fs/locks.c
fs/ncpfs/inode.c
fs/nfs/Kconfig
fs/nfs/Makefile
fs/nfs/blocklayout/blocklayout.c
fs/nfs/callback.c
fs/nfs/callback.h
fs/nfs/callback_proc.c
fs/nfs/callback_xdr.c
fs/nfs/client.c
fs/nfs/dir.c
fs/nfs/dns_resolve.c
fs/nfs/getroot.c
fs/nfs/idmap.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/mount_clnt.c
fs/nfs/namespace.c
fs/nfs/nfs3proc.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4client.c
fs/nfs/nfs4file.c
fs/nfs/nfs4filelayout.c
fs/nfs/nfs4filelayout.h
fs/nfs/nfs4filelayoutdev.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4session.c
fs/nfs/nfs4session.h
fs/nfs/nfs4state.c
fs/nfs/nfs4super.c
fs/nfs/nfs4xdr.c
fs/nfs/objlayout/objlayout.c
fs/nfs/pnfs.c
fs/nfs/pnfs.h
fs/nfs/proc.c
fs/nfs/super.c
fs/nfs/unlink.c
fs/nfsd/nfsd.h
fs/nilfs2/super.c
fs/notify/dnotify/dnotify.c
fs/notify/fanotify/fanotify_user.c
fs/notify/inotify/inotify_user.c
fs/notify/mark.c
fs/quota/dquot.c
fs/select.c
fs/seq_file.c
fs/xfs/Makefile
fs/xfs/xfs_alloc.c
fs/xfs/xfs_bmap_btree.h
fs/xfs/xfs_buf_item.c
fs/xfs/xfs_buf_item.h
fs/xfs/xfs_dfrag.c
fs/xfs/xfs_dir2_leaf.c
fs/xfs/xfs_dquot.c
fs/xfs/xfs_dquot.h
fs/xfs/xfs_fsops.c
fs/xfs/xfs_ialloc.c
fs/xfs/xfs_ialloc.h
fs/xfs/xfs_icache.c
fs/xfs/xfs_icache.h
fs/xfs/xfs_icreate_item.c [new file with mode: 0644]
fs/xfs/xfs_icreate_item.h [new file with mode: 0644]
fs/xfs/xfs_inode.c
fs/xfs/xfs_iomap.c
fs/xfs/xfs_iops.c
fs/xfs/xfs_itable.c
fs/xfs/xfs_log.c
fs/xfs/xfs_log.h
fs/xfs/xfs_log_cil.c
fs/xfs/xfs_log_recover.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_syscalls.c
fs/xfs/xfs_quota.h
fs/xfs/xfs_quotaops.c
fs/xfs/xfs_sb.h
fs/xfs/xfs_super.c
fs/xfs/xfs_symlink.c
fs/xfs/xfs_symlink.h
fs/xfs/xfs_sysctl.c
fs/xfs/xfs_trace.h
fs/xfs/xfs_trans.c
fs/xfs/xfs_trans.h
fs/xfs/xfs_trans_buf.c
fs/xfs/xfs_trans_dquot.c
fs/xfs/xfs_trans_inode.c
fs/xfs/xfs_vnodeops.c
include/drm/drmP.h
include/drm/drm_crtc.h
include/drm/drm_fixed.h
include/drm/drm_gem_cma_helper.h
include/drm/drm_mm.h
include/drm/drm_os_linux.h
include/drm/drm_pciids.h
include/drm/drm_rect.h [new file with mode: 0644]
include/drm/i915_powerwell.h [new file with mode: 0644]
include/drm/ttm/ttm_bo_api.h
include/drm/ttm/ttm_bo_driver.h
include/drm/ttm/ttm_execbuf_util.h
include/linux/audit.h
include/linux/bcma/bcma.h
include/linux/bcma/bcma_driver_chipcommon.h
include/linux/can/platform/flexcan.h [deleted file]
include/linux/ceph/decode.h
include/linux/ceph/osd_client.h
include/linux/dcache.h
include/linux/decompress/unlz4.h [new file with mode: 0644]
include/linux/fb.h
include/linux/filter.h
include/linux/fs.h
include/linux/gfp.h
include/linux/ieee80211.h
include/linux/if_link.h
include/linux/if_macvlan.h
include/linux/if_team.h
include/linux/if_vlan.h
include/linux/igmp.h
include/linux/inetdevice.h
include/linux/io.h
include/linux/jiffies.h
include/linux/ktime.h
include/linux/lz4.h [new file with mode: 0644]
include/linux/marvell_phy.h
include/linux/mlx4/cmd.h
include/linux/mlx4/device.h
include/linux/mlx4/qp.h
include/linux/mm.h
include/linux/mmzone.h
include/linux/mv643xx_eth.h
include/linux/net.h
include/linux/netdev_features.h
include/linux/netdevice.h
include/linux/netfilter.h
include/linux/netlink.h
include/linux/netpoll.h
include/linux/nfs4.h
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h
include/linux/nfs_xdr.h
include/linux/phy.h
include/linux/platform_data/brcmfmac-sdio.h
include/linux/platform_data/net-cw1200.h [new file with mode: 0644]
include/linux/platform_data/rcar-du.h [new file with mode: 0644]
include/linux/ptrace.h
include/linux/reboot.h
include/linux/reservation.h [new file with mode: 0644]
include/linux/scatterlist.h
include/linux/sched.h
include/linux/security.h
include/linux/sem.h
include/linux/seq_file.h
include/linux/skbuff.h
include/linux/ssb/ssb_driver_mips.h
include/linux/ssb/ssb_regs.h
include/linux/stmmac.h
include/linux/sunrpc/sched.h
include/linux/tcp.h
include/linux/vexpress.h
include/linux/virtio.h
include/linux/vmalloc.h
include/linux/writeback.h
include/net/act_api.h
include/net/addrconf.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/cfg80211.h
include/net/gen_stats.h
include/net/gre.h
include/net/ieee80211_radiotap.h
include/net/if_inet6.h
include/net/inet_ecn.h
include/net/inet_sock.h
include/net/ip_fib.h
include/net/ip_tunnels.h
include/net/ip_vs.h
include/net/ipv6.h
include/net/ll_poll.h [new file with mode: 0644]
include/net/mac80211.h
include/net/ndisc.h
include/net/net_namespace.h
include/net/netfilter/xt_rateest.h
include/net/netns/x_tables.h
include/net/nfc/hci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
include/net/ping.h
include/net/sch_generic.h
include/net/sctp/checksum.h
include/net/sctp/sctp.h
include/net/sctp/structs.h
include/net/sock.h
include/net/tcp.h
include/net/transp_v6.h
include/net/udp.h
include/trace/events/9p.h
include/trace/events/btrfs.h
include/uapi/asm-generic/poll.h
include/uapi/asm-generic/socket.h
include/uapi/drm/drm.h
include/uapi/drm/drm_mode.h
include/uapi/drm/i915_drm.h
include/uapi/drm/tegra_drm.h
include/uapi/linux/btrfs.h
include/uapi/linux/ethtool.h
include/uapi/linux/gen_stats.h
include/uapi/linux/if_arp.h
include/uapi/linux/if_link.h
include/uapi/linux/if_pppox.h
include/uapi/linux/if_tun.h
include/uapi/linux/ip_vs.h
include/uapi/linux/msdos_fs.h
include/uapi/linux/netfilter/nfnetlink_queue.h
include/uapi/linux/netfilter/xt_socket.h
include/uapi/linux/nfc.h
include/uapi/linux/nl80211.h
include/uapi/linux/openvswitch.h
include/uapi/linux/rtnetlink.h
include/uapi/linux/snmp.h
include/uapi/linux/tipc.h
include/uapi/linux/tipc_config.h
include/video/display_timing.h
include/video/of_display_timing.h
include/video/omap-panel-data.h
include/video/omapdss.h
include/video/uvesafb.h
include/xen/interface/io/netif.h
init/Kconfig
ipc/mqueue.c
ipc/msg.c
ipc/sem.c
ipc/shm.c
ipc/util.c
ipc/util.h
kernel/Makefile
kernel/audit.h
kernel/auditfilter.c
kernel/auditsc.c
kernel/exit.c
kernel/panic.c
kernel/ptrace.c
kernel/reboot.c [new file with mode: 0644]
kernel/sys.c
kernel/sysctl_binary.c
lib/Kconfig
lib/Makefile
lib/clz_ctz.c [new file with mode: 0644]
lib/decompress.c
lib/decompress_unlz4.c [new file with mode: 0644]
lib/lz4/Makefile [new file with mode: 0644]
lib/lz4/lz4_compress.c [new file with mode: 0644]
lib/lz4/lz4_decompress.c [new file with mode: 0644]
lib/lz4/lz4defs.h [new file with mode: 0644]
lib/lz4/lz4hc_compress.c [new file with mode: 0644]
lib/scatterlist.c
lib/vsprintf.c
mm/filemap.c
mm/internal.h
mm/memblock.c
mm/memcontrol.c
mm/memory.c
mm/memory_hotplug.c
mm/mmap.c
mm/mremap.c
mm/page_alloc.c
mm/rmap.c
mm/sparse.c
mm/vmalloc.c
mm/vmscan.c
net/8021q/vlan.c
net/9p/client.c
net/Kconfig
net/Makefile
net/appletalk/aarp.c
net/appletalk/ddp.c
net/atm/clip.c
net/atm/mpc.c
net/ax25/af_ax25.c
net/ax25/sysctl_net_ax25.c
net/batman-adv/Makefile
net/batman-adv/bat_iv_ogm.c
net/batman-adv/bridge_loop_avoidance.c
net/batman-adv/bridge_loop_avoidance.h
net/batman-adv/distributed-arp-table.c
net/batman-adv/hard-interface.c
net/batman-adv/icmp_socket.c
net/batman-adv/main.c
net/batman-adv/main.h
net/batman-adv/network-coding.c
net/batman-adv/network-coding.h
net/batman-adv/originator.c
net/batman-adv/originator.h
net/batman-adv/ring_buffer.c [deleted file]
net/batman-adv/ring_buffer.h [deleted file]
net/batman-adv/routing.c
net/batman-adv/routing.h
net/batman-adv/send.c
net/batman-adv/send.h
net/batman-adv/soft-interface.c
net/batman-adv/translation-table.c
net/batman-adv/translation-table.h
net/batman-adv/types.h
net/batman-adv/unicast.c
net/batman-adv/vis.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hidp/core.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bridge/br_device.c
net/bridge/br_fdb.c
net/bridge/br_forward.c
net/bridge/br_if.c
net/bridge/br_input.c
net/bridge/br_mdb.c
net/bridge/br_multicast.c
net/bridge/br_netfilter.c
net/bridge/br_netlink.c
net/bridge/br_notify.c
net/bridge/br_private.h
net/bridge/br_sysfs_br.c
net/bridge/br_sysfs_if.c
net/bridge/netfilter/ebt_ulog.c
net/bridge/netfilter/ebtables.c
net/caif/caif_dev.c
net/caif/caif_usb.c
net/can/af_can.c
net/can/bcm.c
net/can/gw.c
net/can/raw.c
net/ceph/auth_none.c
net/ceph/osd_client.c
net/core/datagram.c
net/core/dev.c
net/core/drop_monitor.c
net/core/dst.c
net/core/ethtool.c
net/core/fib_rules.c
net/core/gen_estimator.c
net/core/gen_stats.c
net/core/link_watch.c
net/core/neighbour.c
net/core/net-procfs.c
net/core/netpoll.c
net/core/netprio_cgroup.c
net/core/pktgen.c
net/core/rtnetlink.c
net/core/skbuff.c
net/core/sock.c
net/core/sysctl_net_core.c
net/decnet/af_decnet.c
net/decnet/dn_dev.c
net/decnet/sysctl_net_decnet.c
net/ieee802154/6lowpan.c
net/ipv4/Kconfig
net/ipv4/Makefile
net/ipv4/af_inet.c
net/ipv4/ah4.c
net/ipv4/arp.c
net/ipv4/devinet.c
net/ipv4/esp4.c
net/ipv4/fib_frontend.c
net/ipv4/fib_semantics.c
net/ipv4/gre.c [deleted file]
net/ipv4/gre_demux.c [new file with mode: 0644]
net/ipv4/gre_offload.c [new file with mode: 0644]
net/ipv4/icmp.c
net/ipv4/igmp.c
net/ipv4/inet_fragment.c
net/ipv4/ip_gre.c
net/ipv4/ip_tunnel.c
net/ipv4/ip_tunnel_core.c [new file with mode: 0644]
net/ipv4/ip_vti.c
net/ipv4/ipcomp.c
net/ipv4/ipip.c
net/ipv4/ipmr.c
net/ipv4/netfilter/Kconfig
net/ipv4/netfilter/ipt_MASQUERADE.c
net/ipv4/netfilter/ipt_ULOG.c
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
net/ipv4/ping.c
net/ipv4/proc.c
net/ipv4/route.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_minisocks.c
net/ipv4/tcp_offload.c [new file with mode: 0644]
net/ipv4/tcp_output.c
net/ipv4/udp.c
net/ipv4/udp_offload.c [new file with mode: 0644]
net/ipv4/xfrm4_tunnel.c
net/ipv6/Makefile
net/ipv6/addrconf.c
net/ipv6/addrconf_core.c
net/ipv6/af_inet6.c
net/ipv6/datagram.c
net/ipv6/exthdrs_core.c
net/ipv6/icmp.c
net/ipv6/ip6_offload.c
net/ipv6/ip6_output.c
net/ipv6/ip6mr.c
net/ipv6/mcast.c
net/ipv6/mip6.c
net/ipv6/ndisc.c
net/ipv6/netfilter/ip6t_MASQUERADE.c
net/ipv6/output_core.c
net/ipv6/ping.c [new file with mode: 0644]
net/ipv6/raw.c
net/ipv6/route.c
net/ipv6/sit.c
net/ipv6/sysctl_net_ipv6.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/ipv6/udp_offload.c
net/ipx/af_ipx.c
net/irda/irsysctl.c
net/iucv/af_iucv.c
net/l2tp/l2tp_core.c
net/l2tp/l2tp_core.h
net/l2tp/l2tp_ppp.c
net/mac80211/aes_ccm.c
net/mac80211/cfg.c
net/mac80211/debugfs_netdev.c
net/mac80211/driver-ops.h
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/key.h
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/rate.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/vht.c
net/mac80211/wep.c
net/mac80211/wpa.c
net/mpls/Kconfig [new file with mode: 0644]
net/mpls/Makefile [new file with mode: 0644]
net/mpls/mpls_gso.c [new file with mode: 0644]
net/netfilter/core.c
net/netfilter/ipvs/ip_vs_conn.c
net/netfilter/ipvs/ip_vs_core.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/ipvs/ip_vs_dh.c
net/netfilter/ipvs/ip_vs_lblc.c
net/netfilter/ipvs/ip_vs_lblcr.c
net/netfilter/ipvs/ip_vs_lc.c
net/netfilter/ipvs/ip_vs_nq.c
net/netfilter/ipvs/ip_vs_proto_sctp.c
net/netfilter/ipvs/ip_vs_proto_tcp.c
net/netfilter/ipvs/ip_vs_rr.c
net/netfilter/ipvs/ip_vs_sed.c
net/netfilter/ipvs/ip_vs_sh.c
net/netfilter/ipvs/ip_vs_sync.c
net/netfilter/ipvs/ip_vs_wlc.c
net/netfilter/ipvs/ip_vs_wrr.c
net/netfilter/nf_conntrack_ftp.c
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nf_conntrack_proto_tcp.c
net/netfilter/nf_conntrack_standalone.c
net/netfilter/nf_log.c
net/netfilter/nf_nat_helper.c
net/netfilter/nfnetlink_cthelper.c
net/netfilter/nfnetlink_cttimeout.c
net/netfilter/nfnetlink_queue_core.c
net/netfilter/xt_CT.c
net/netfilter/xt_TEE.c
net/netfilter/xt_rateest.c
net/netfilter/xt_socket.c
net/netlabel/netlabel_unlabeled.c
net/netlink/af_netlink.c
net/netlink/af_netlink.h
net/netrom/af_netrom.c
net/netrom/sysctl_net_netrom.c
net/nfc/core.c
net/nfc/hci/core.c
net/nfc/llcp.h
net/nfc/llcp_commands.c
net/nfc/llcp_core.c
net/nfc/llcp_sock.c
net/nfc/nci/Kconfig
net/nfc/nci/Makefile
net/nfc/nci/core.c
net/nfc/nci/data.c
net/nfc/nci/spi.c [new file with mode: 0644]
net/nfc/netlink.c
net/nfc/nfc.h
net/openvswitch/Kconfig
net/openvswitch/Makefile
net/openvswitch/actions.c
net/openvswitch/datapath.c
net/openvswitch/datapath.h
net/openvswitch/dp_notify.c
net/openvswitch/flow.c
net/openvswitch/flow.h
net/openvswitch/vport-gre.c [new file with mode: 0644]
net/openvswitch/vport-internal_dev.c
net/openvswitch/vport-netdev.c
net/openvswitch/vport-netdev.h
net/openvswitch/vport.c
net/openvswitch/vport.h
net/packet/af_packet.c
net/phonet/pn_dev.c
net/phonet/sysctl.c
net/rds/ib_sysctl.c
net/rds/iw_sysctl.c
net/rds/sysctl.c
net/rose/af_rose.c
net/rose/sysctl_net_rose.c
net/sched/act_mirred.c
net/sched/sch_cbq.c
net/sched/sch_drr.c
net/sched/sch_generic.c
net/sched/sch_hfsc.c
net/sched/sch_htb.c
net/sched/sch_netem.c
net/sched/sch_qfq.c
net/sched/sch_tbf.c
net/sctp/Kconfig
net/sctp/associola.c
net/sctp/bind_addr.c
net/sctp/chunk.c
net/sctp/debug.c
net/sctp/endpointola.c
net/sctp/input.c
net/sctp/inqueue.c
net/sctp/ipv6.c
net/sctp/output.c
net/sctp/outqueue.c
net/sctp/proc.c
net/sctp/protocol.c
net/sctp/sm_make_chunk.c
net/sctp/sm_sideeffect.c
net/sctp/sm_statefuns.c
net/sctp/socket.c
net/sctp/sysctl.c
net/sctp/transport.c
net/sctp/tsnmap.c
net/sctp/ulpevent.c
net/socket.c
net/sunrpc/clnt.c
net/sunrpc/rpc_pipe.c
net/sunrpc/sched.c
net/sunrpc/sysctl.c
net/sunrpc/xprtrdma/svc_rdma.c
net/sunrpc/xprtrdma/transport.c
net/sunrpc/xprtsock.c
net/tipc/Makefile
net/tipc/bcast.c
net/tipc/bcast.h
net/tipc/config.c
net/tipc/core.c
net/tipc/core.h
net/tipc/discover.c
net/tipc/eth_media.c
net/tipc/ib_media.c
net/tipc/link.c
net/tipc/msg.c
net/tipc/msg.h
net/tipc/name_table.c
net/tipc/name_table.h
net/tipc/node_subscr.c
net/tipc/port.c
net/tipc/port.h
net/tipc/server.c [new file with mode: 0644]
net/tipc/server.h [new file with mode: 0644]
net/tipc/socket.c
net/tipc/subscr.c
net/tipc/subscr.h
net/tipc/sysctl.c [new file with mode: 0644]
net/unix/sysctl_net_unix.c
net/vmw_vsock/af_vsock.c
net/vmw_vsock/vmci_transport.c
net/wireless/chan.c
net/wireless/core.c
net/wireless/core.h
net/wireless/debugfs.c
net/wireless/ibss.c
net/wireless/mesh.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/reg.c
net/wireless/scan.c
net/wireless/sme.c
net/wireless/sysfs.c
net/wireless/trace.h
net/wireless/util.c
net/wireless/wext-compat.c
net/wireless/wext-sme.c
net/x25/af_x25.c
net/xfrm/xfrm_input.c
net/xfrm/xfrm_output.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_proc.c
scripts/Makefile.lib
scripts/checkpatch.pl
security/capability.c
security/security.c
security/selinux/hooks.c
security/selinux/include/security.h
security/selinux/netif.c
security/selinux/ss/policydb.c
security/smack/smack_lsm.c
sound/pci/hda/Kconfig
sound/pci/hda/Makefile
sound/pci/hda/hda_i915.c [new file with mode: 0644]
sound/pci/hda/hda_i915.h [new file with mode: 0644]
sound/pci/hda/hda_intel.c
tools/testing/selftests/net/psock_tpacket.c
usr/Kconfig

index 0f6a3ed..49267ea 100644 (file)
 !Finclude/net/cfg80211.h cfg80211_ibss_params
 !Finclude/net/cfg80211.h cfg80211_connect_params
 !Finclude/net/cfg80211.h cfg80211_pmksa
-!Finclude/net/cfg80211.h cfg80211_send_rx_auth
-!Finclude/net/cfg80211.h cfg80211_send_auth_timeout
-!Finclude/net/cfg80211.h cfg80211_send_rx_assoc
-!Finclude/net/cfg80211.h cfg80211_send_assoc_timeout
-!Finclude/net/cfg80211.h cfg80211_send_deauth
-!Finclude/net/cfg80211.h __cfg80211_send_deauth
-!Finclude/net/cfg80211.h cfg80211_send_disassoc
-!Finclude/net/cfg80211.h __cfg80211_send_disassoc
+!Finclude/net/cfg80211.h cfg80211_rx_mlme_mgmt
+!Finclude/net/cfg80211.h cfg80211_auth_timeout
+!Finclude/net/cfg80211.h cfg80211_rx_assoc_resp
+!Finclude/net/cfg80211.h cfg80211_assoc_timeout
+!Finclude/net/cfg80211.h cfg80211_tx_mlme_mgmt
 !Finclude/net/cfg80211.h cfg80211_ibss_joined
 !Finclude/net/cfg80211.h cfg80211_connect_result
 !Finclude/net/cfg80211.h cfg80211_roamed
index fca3419..cbfdf54 100644 (file)
@@ -126,6 +126,8 @@ X!Edrivers/base/interface.c
      </sect1>
      <sect1><title>Device Drivers DMA Management</title>
 !Edrivers/base/dma-buf.c
+!Edrivers/base/reservation.c
+!Iinclude/linux/reservation.h
 !Edrivers/base/dma-coherent.c
 !Edrivers/base/dma-mapping.c
      </sect1>
index 6dd8d10..7d1278e 100644 (file)
           <varlistentry>
             <term>DRIVER_HAVE_IRQ</term><term>DRIVER_IRQ_SHARED</term>
             <listitem><para>
-              DRIVER_HAVE_IRQ indicates whether the driver has an IRQ handler. The
-              DRM core will automatically register an interrupt handler when the
-              flag is set. DRIVER_IRQ_SHARED indicates whether the device &amp;
-              handler support shared IRQs (note that this is required of PCI
-              drivers).
+              DRIVER_HAVE_IRQ indicates whether the driver has an IRQ handler
+              managed by the DRM Core. The core will support simple IRQ handler
+              installation when the flag is set. The installation process is
+              described in <xref linkend="drm-irq-registration"/>.</para>
+              <para>DRIVER_IRQ_SHARED indicates whether the device &amp; handler
+              support shared IRQs (note that this is required of PCI  drivers).
             </para></listitem>
           </varlistentry>
           <varlistentry>
@@ -344,50 +345,71 @@ char *date;</synopsis>
           The DRM core tries to facilitate IRQ handler registration and
           unregistration by providing <function>drm_irq_install</function> and
           <function>drm_irq_uninstall</function> functions. Those functions only
-          support a single interrupt per device.
-        </para>
-  <!--!Fdrivers/char/drm/drm_irq.c drm_irq_install-->
-        <para>
-          Both functions get the device IRQ by calling
-          <function>drm_dev_to_irq</function>. This inline function will call a
-          bus-specific operation to retrieve the IRQ number. For platform devices,
-          <function>platform_get_irq</function>(..., 0) is used to retrieve the
-          IRQ number.
-        </para>
-        <para>
-          <function>drm_irq_install</function> starts by calling the
-          <methodname>irq_preinstall</methodname> driver operation. The operation
-          is optional and must make sure that the interrupt will not get fired by
-          clearing all pending interrupt flags or disabling the interrupt.
-        </para>
-        <para>
-          The IRQ will then be requested by a call to
-          <function>request_irq</function>. If the DRIVER_IRQ_SHARED driver
-          feature flag is set, a shared (IRQF_SHARED) IRQ handler will be
-          requested.
-        </para>
-        <para>
-          The IRQ handler function must be provided as the mandatory irq_handler
-          driver operation. It will get passed directly to
-          <function>request_irq</function> and thus has the same prototype as all
-          IRQ handlers. It will get called with a pointer to the DRM device as the
-          second argument.
-        </para>
-        <para>
-          Finally the function calls the optional
-          <methodname>irq_postinstall</methodname> driver operation. The operation
-          usually enables interrupts (excluding the vblank interrupt, which is
-          enabled separately), but drivers may choose to enable/disable interrupts
-          at a different time.
-        </para>
-        <para>
-          <function>drm_irq_uninstall</function> is similarly used to uninstall an
-          IRQ handler. It starts by waking up all processes waiting on a vblank
-          interrupt to make sure they don't hang, and then calls the optional
-          <methodname>irq_uninstall</methodname> driver operation. The operation
-          must disable all hardware interrupts. Finally the function frees the IRQ
-          by calling <function>free_irq</function>.
+          support a single interrupt per device, devices that use more than one
+          IRQs need to be handled manually.
         </para>
+        <sect4>
+          <title>Managed IRQ Registration</title>
+          <para>
+            Both the <function>drm_irq_install</function> and
+           <function>drm_irq_uninstall</function> functions get the device IRQ by
+           calling <function>drm_dev_to_irq</function>. This inline function will
+           call a bus-specific operation to retrieve the IRQ number. For platform
+           devices, <function>platform_get_irq</function>(..., 0) is used to
+           retrieve the IRQ number.
+          </para>
+          <para>
+            <function>drm_irq_install</function> starts by calling the
+            <methodname>irq_preinstall</methodname> driver operation. The operation
+            is optional and must make sure that the interrupt will not get fired by
+            clearing all pending interrupt flags or disabling the interrupt.
+          </para>
+          <para>
+            The IRQ will then be requested by a call to
+            <function>request_irq</function>. If the DRIVER_IRQ_SHARED driver
+            feature flag is set, a shared (IRQF_SHARED) IRQ handler will be
+            requested.
+          </para>
+          <para>
+            The IRQ handler function must be provided as the mandatory irq_handler
+            driver operation. It will get passed directly to
+            <function>request_irq</function> and thus has the same prototype as all
+            IRQ handlers. It will get called with a pointer to the DRM device as the
+            second argument.
+          </para>
+          <para>
+            Finally the function calls the optional
+            <methodname>irq_postinstall</methodname> driver operation. The operation
+            usually enables interrupts (excluding the vblank interrupt, which is
+            enabled separately), but drivers may choose to enable/disable interrupts
+            at a different time.
+          </para>
+          <para>
+            <function>drm_irq_uninstall</function> is similarly used to uninstall an
+            IRQ handler. It starts by waking up all processes waiting on a vblank
+            interrupt to make sure they don't hang, and then calls the optional
+            <methodname>irq_uninstall</methodname> driver operation. The operation
+            must disable all hardware interrupts. Finally the function frees the IRQ
+            by calling <function>free_irq</function>.
+          </para>
+        </sect4>
+        <sect4>
+          <title>Manual IRQ Registration</title>
+          <para>
+            Drivers that require multiple interrupt handlers can't use the managed
+            IRQ registration functions. In that case IRQs must be registered and
+            unregistered manually (usually with the <function>request_irq</function>
+            and <function>free_irq</function> functions, or their devm_* equivalent).
+          </para>
+          <para>
+            When manually registering IRQs, drivers must not set the DRIVER_HAVE_IRQ
+            driver feature flag, and must not provide the
+           <methodname>irq_handler</methodname> driver operation. They must set the
+           <structname>drm_device</structname> <structfield>irq_enabled</structfield>
+           field to 1 upon registration of the IRQs, and clear it to 0 after
+           unregistering the IRQs.
+          </para>
+        </sect4>
       </sect3>
       <sect3>
         <title>Memory Manager Initialization</title>
@@ -1213,6 +1235,15 @@ int max_width, max_height;</synopsis>
         <sect4>
           <title>Miscellaneous</title>
           <itemizedlist>
+            <listitem>
+              <synopsis>void (*set_property)(struct drm_crtc *crtc,
+                     struct drm_property *property, uint64_t value);</synopsis>
+              <para>
+                Set the value of the given CRTC property to
+                <parameter>value</parameter>. See <xref linkend="drm-kms-properties"/>
+                for more information about properties.
+              </para>
+            </listitem>
             <listitem>
               <synopsis>void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
                         uint32_t start, uint32_t size);</synopsis>
@@ -1363,6 +1394,15 @@ int max_width, max_height;</synopsis>
               <xref linkend="drm-kms-init"/>.
             </para>
           </listitem>
+          <listitem>
+            <synopsis>void (*set_property)(struct drm_plane *plane,
+                     struct drm_property *property, uint64_t value);</synopsis>
+            <para>
+              Set the value of the given plane property to
+              <parameter>value</parameter>. See <xref linkend="drm-kms-properties"/>
+              for more information about properties.
+            </para>
+          </listitem>
         </itemizedlist>
       </sect3>
     </sect2>
@@ -1571,6 +1611,15 @@ int max_width, max_height;</synopsis>
         <sect4>
           <title>Miscellaneous</title>
           <itemizedlist>
+            <listitem>
+              <synopsis>void (*set_property)(struct drm_connector *connector,
+                     struct drm_property *property, uint64_t value);</synopsis>
+              <para>
+                Set the value of the given connector property to
+                <parameter>value</parameter>. See <xref linkend="drm-kms-properties"/>
+                for more information about properties.
+              </para>
+            </listitem>
             <listitem>
               <synopsis>void (*destroy)(struct drm_connector *connector);</synopsis>
               <para>
@@ -1846,10 +1895,6 @@ void intel_crt_init(struct drm_device *dev)
           <synopsis>bool (*mode_fixup)(struct drm_encoder *encoder,
                        const struct drm_display_mode *mode,
                        struct drm_display_mode *adjusted_mode);</synopsis>
-          <note><para>
-            FIXME: The mode argument be const, but the i915 driver modifies
-            mode-&gt;clock in <function>intel_dp_mode_fixup</function>.
-          </para></note>
           <para>
             Let encoders adjust the requested mode or reject it completely. This
             operation returns true if the mode is accepted (possibly after being
@@ -2161,6 +2206,128 @@ void intel_crt_init(struct drm_device *dev)
       <title>EDID Helper Functions Reference</title>
 !Edrivers/gpu/drm/drm_edid.c
     </sect2>
+    <sect2>
+      <title>Rectangle Utilities Reference</title>
+!Pinclude/drm/drm_rect.h rect utils
+!Iinclude/drm/drm_rect.h
+!Edrivers/gpu/drm/drm_rect.c
+    </sect2>
+  </sect1>
+
+  <!-- Internals: kms properties -->
+
+  <sect1 id="drm-kms-properties">
+    <title>KMS Properties</title>
+    <para>
+      Drivers may need to expose additional parameters to applications than
+      those described in the previous sections. KMS supports attaching
+      properties to CRTCs, connectors and planes and offers a userspace API to
+      list, get and set the property values.
+    </para>
+    <para>
+      Properties are identified by a name that uniquely defines the property
+      purpose, and store an associated value. For all property types except blob
+      properties the value is a 64-bit unsigned integer.
+    </para>
+    <para>
+      KMS differentiates between properties and property instances. Drivers
+      first create properties and then create and associate individual instances
+      of those properties to objects. A property can be instantiated multiple
+      times and associated with different objects. Values are stored in property
+      instances, and all other property information are stored in the propery
+      and shared between all instances of the property.
+    </para>
+    <para>
+      Every property is created with a type that influences how the KMS core
+      handles the property. Supported property types are
+      <variablelist>
+        <varlistentry>
+          <term>DRM_MODE_PROP_RANGE</term>
+          <listitem><para>Range properties report their minimum and maximum
+            admissible values. The KMS core verifies that values set by
+            application fit in that range.</para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>DRM_MODE_PROP_ENUM</term>
+          <listitem><para>Enumerated properties take a numerical value that
+            ranges from 0 to the number of enumerated values defined by the
+            property minus one, and associate a free-formed string name to each
+            value. Applications can retrieve the list of defined value-name pairs
+            and use the numerical value to get and set property instance values.
+            </para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>DRM_MODE_PROP_BITMASK</term>
+          <listitem><para>Bitmask properties are enumeration properties that
+            additionally restrict all enumerated values to the 0..63 range.
+            Bitmask property instance values combine one or more of the
+            enumerated bits defined by the property.</para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term>DRM_MODE_PROP_BLOB</term>
+          <listitem><para>Blob properties store a binary blob without any format
+            restriction. The binary blobs are created as KMS standalone objects,
+            and blob property instance values store the ID of their associated
+            blob object.</para>
+           <para>Blob properties are only used for the connector EDID property
+           and cannot be created by drivers.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </para>
+    <para>
+      To create a property drivers call one of the following functions depending
+      on the property type. All property creation functions take property flags
+      and name, as well as type-specific arguments.
+      <itemizedlist>
+        <listitem>
+          <synopsis>struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
+                                               const char *name,
+                                               uint64_t min, uint64_t max);</synopsis>
+          <para>Create a range property with the given minimum and maximum
+            values.</para>
+        </listitem>
+        <listitem>
+          <synopsis>struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
+                                              const char *name,
+                                              const struct drm_prop_enum_list *props,
+                                              int num_values);</synopsis>
+          <para>Create an enumerated property. The <parameter>props</parameter>
+            argument points to an array of <parameter>num_values</parameter>
+            value-name pairs.</para>
+        </listitem>
+        <listitem>
+          <synopsis>struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
+                                                 int flags, const char *name,
+                                                 const struct drm_prop_enum_list *props,
+                                                 int num_values);</synopsis>
+          <para>Create a bitmask property. The <parameter>props</parameter>
+            argument points to an array of <parameter>num_values</parameter>
+            value-name pairs.</para>
+        </listitem>
+      </itemizedlist>
+    </para>
+    <para>
+      Properties can additionally be created as immutable, in which case they
+      will be read-only for applications but can be modified by the driver. To
+      create an immutable property drivers must set the DRM_MODE_PROP_IMMUTABLE
+      flag at property creation time.
+    </para>
+    <para>
+      When no array of value-name pairs is readily available at property
+      creation time for enumerated or range properties, drivers can create
+      the property using the <function>drm_property_create</function> function
+      and manually add enumeration value-name pairs by calling the
+      <function>drm_property_add_enum</function> function. Care must be taken to
+      properly specify the property type through the <parameter>flags</parameter>
+      argument.
+    </para>
+    <para>
+      After creating properties drivers can attach property instances to CRTC,
+      connector and plane objects by calling the
+      <function>drm_object_attach_property</function>. The function takes a
+      pointer to the target object, a pointer to the previously created property
+      and an initial instance value.
+    </para>
   </sect1>
 
   <!-- Internals: vertical blanking -->
index e5f1301..fff10da 100644 (file)
@@ -10,6 +10,14 @@ Recommended properties:
    services interrupts for this device.
  - ti,hwmods: Name of the hwmod associated to the LCDC
 
+Optional properties:
+ - max-bandwidth: The maximum pixels per second that the memory
+   interface / lcd controller combination can sustain
+ - max-width: The maximum horizontal pixel width supported by
+   the lcd controller.
+ - max-pixelclock: The maximum pixel clock that can be supported
+   by the lcd controller in KHz.
+
 Example:
 
        fb: fb@4830e000 {
diff --git a/Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt b/Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt
new file mode 100644 (file)
index 0000000..b90bfcd
--- /dev/null
@@ -0,0 +1,22 @@
+* Allwinner EMAC ethernet controller
+
+Required properties:
+- compatible: should be "allwinner,sun4i-emac".
+- reg: address and length of the register set for the device.
+- interrupts: interrupt for the device
+- phy: A phandle to a phy node defining the PHY address (as the reg
+  property, a single integer).
+- clocks: A phandle to the reference clock for this device
+
+Optional properties:
+- (local-)mac-address: mac address to be used by this driver
+
+Example:
+
+emac: ethernet@01c0b000 {
+       compatible = "allwinner,sun4i-emac";
+       reg = <0x01c0b000 0x1000>;
+       interrupts = <55>;
+       clocks = <&ahb_gates 17>;
+       phy = <&phy0>;
+};
diff --git a/Documentation/devicetree/bindings/net/allwinner,sun4i-mdio.txt b/Documentation/devicetree/bindings/net/allwinner,sun4i-mdio.txt
new file mode 100644 (file)
index 0000000..00b9f9a
--- /dev/null
@@ -0,0 +1,26 @@
+* Allwinner A10 MDIO Ethernet Controller interface
+
+Required properties:
+- compatible: should be "allwinner,sun4i-mdio".
+- reg: address and length of the register set for the device.
+
+Optional properties:
+- phy-supply: phandle to a regulator if the PHY needs one
+
+Example at the SoC level:
+mdio@01c0b080 {
+       compatible = "allwinner,sun4i-mdio";
+       reg = <0x01c0b080 0x14>;
+       #address-cells = <1>;
+       #size-cells = <0>;
+};
+
+And at the board level:
+
+mdio@01c0b080 {
+       phy-supply = <&reg_emac_3v3>;
+
+       phy0: ethernet-phy@0 {
+               reg = <0>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/net/arc_emac.txt b/Documentation/devicetree/bindings/net/arc_emac.txt
new file mode 100644 (file)
index 0000000..bcbc3f0
--- /dev/null
@@ -0,0 +1,38 @@
+* Synopsys ARC EMAC 10/100 Ethernet driver (EMAC)
+
+Required properties:
+- compatible: Should be "snps,arc-emac"
+- reg: Address and length of the register set for the device
+- interrupts: Should contain the EMAC interrupts
+- clock-frequency: CPU frequency. It is needed to calculate and set polling
+period of EMAC.
+- max-speed: Maximum supported data-rate in Mbit/s. In some HW configurations
+bandwidth of external memory controller might be a limiting factor. That's why
+it's required to specify which data-rate is supported on current SoC or FPGA.
+For example if only 10 Mbit/s is supported (10BASE-T) set "10". If 100 Mbit/s is
+supported (100BASE-TX) set "100".
+- phy: PHY device attached to the EMAC via MDIO bus
+
+Child nodes of the driver are the individual PHY devices connected to the
+MDIO bus. They must have a "reg" property given the PHY address on the MDIO bus.
+
+Optional properties:
+- mac-address: 6 bytes, mac address
+
+Examples:
+
+       ethernet@c0fc2000 {
+               compatible = "snps,arc-emac";
+               reg = <0xc0fc2000 0x3c>;
+               interrupts = <6>;
+               mac-address = [ 00 11 22 33 44 55 ];
+               clock-frequency = <80000000>;
+               max-speed = <100>;
+               phy = <&phy0>;
+
+               #address-cells = <1>;
+               #size-cells = <0>;
+               phy0: ethernet-phy@0 {
+                       reg = <1>;
+               };
+       };
index 8ff324e..56d6cc3 100644 (file)
@@ -16,6 +16,8 @@ Optional properties:
 
 - clock-frequency : The oscillator frequency driving the flexcan device
 
+- xceiver-supply: Regulator that powers the CAN transceiver
+
 Example:
 
        can@1c000 {
index 4f2ca6b..05d660e 100644 (file)
@@ -28,6 +28,8 @@ Optional properties:
 Slave Properties:
 Required properties:
 - phy_id               : Specifies slave phy id
+- phy-mode             : The interface between the SoC and the PHY (a string
+                         that of_get_phy_mode() can understand)
 - mac-address          : Specifies slave MAC address
 
 Optional properties:
@@ -58,11 +60,13 @@ Examples:
                cpts_clock_shift = <29>;
                cpsw_emac0: slave@0 {
                        phy_id = <&davinci_mdio>, <0>;
+                       phy-mode = "rgmii-txid";
                        /* Filled in by U-Boot */
                        mac-address = [ 00 00 00 00 00 00 ];
                };
                cpsw_emac1: slave@1 {
                        phy_id = <&davinci_mdio>, <1>;
+                       phy-mode = "rgmii-txid";
                        /* Filled in by U-Boot */
                        mac-address = [ 00 00 00 00 00 00 ];
                };
@@ -84,11 +88,13 @@ Examples:
                cpts_clock_shift = <29>;
                cpsw_emac0: slave@0 {
                        phy_id = <&davinci_mdio>, <0>;
+                       phy-mode = "rgmii-txid";
                        /* Filled in by U-Boot */
                        mac-address = [ 00 00 00 00 00 00 ];
                };
                cpsw_emac1: slave@1 {
                        phy_id = <&davinci_mdio>, <1>;
+                       phy-mode = "rgmii-txid";
                        /* Filled in by U-Boot */
                        mac-address = [ 00 00 00 00 00 00 ];
                };
diff --git a/Documentation/devicetree/bindings/net/davicom-dm9000.txt b/Documentation/devicetree/bindings/net/davicom-dm9000.txt
new file mode 100644 (file)
index 0000000..2d39c99
--- /dev/null
@@ -0,0 +1,26 @@
+Davicom DM9000 Fast Ethernet controller
+
+Required properties:
+- compatible = "davicom,dm9000";
+- reg : physical addresses and sizes of registers, must contain 2 entries:
+    first entry : address register,
+    second entry : data register.
+- interrupt-parent : interrupt controller to which the device is connected
+- interrupts : interrupt specifier specific to interrupt controller
+
+Optional properties:
+- local-mac-address : A bytestring of 6 bytes specifying Ethernet MAC address
+    to use (from firmware or bootloader)
+- davicom,no-eeprom : Configuration EEPROM is not available
+- davicom,ext-phy : Use external PHY
+
+Example:
+
+       ethernet@18000000 {
+               compatible = "davicom,dm9000";
+               reg = <0x18000000 0x2 0x18000004 0x2>;
+               interrupt-parent = <&gpn>;
+               interrupts = <7 4>;
+               local-mac-address = [00 00 de ad be ef];
+               davicom,no-eeprom;
+       };
diff --git a/Documentation/devicetree/bindings/net/marvell-orion-net.txt b/Documentation/devicetree/bindings/net/marvell-orion-net.txt
new file mode 100644 (file)
index 0000000..a73b79f
--- /dev/null
@@ -0,0 +1,85 @@
+Marvell Orion/Discovery ethernet controller
+=============================================
+
+The Marvell Discovery ethernet controller can be found on Marvell Orion SoCs
+(Kirkwood, Dove, Orion5x, and Discovery Innovation) and as part of Marvell
+Discovery system controller chips (mv64[345]60).
+
+The Discovery ethernet controller is described with two levels of nodes. The
+first level describes the ethernet controller itself and the second level
+describes up to 3 ethernet port nodes within that controller. The reason for
+the multiple levels is that the port registers are interleaved within a single
+set of controller registers. Each port node describes port-specific properties.
+
+Note: The above separation is only true for Discovery system controllers.
+For Orion SoCs we stick to the separation, although there each controller has
+only one port associated. Multiple ports are implemented as multiple single-port
+controllers. As Kirkwood has some issues with proper initialization after reset,
+an extra compatible string is added for it.
+
+* Ethernet controller node
+
+Required controller properties:
+ - #address-cells: shall be 1.
+ - #size-cells: shall be 0.
+ - compatible: shall be one of "marvell,orion-eth", "marvell,kirkwood-eth".
+ - reg: address and length of the controller registers.
+
+Optional controller properties:
+ - clocks: phandle reference to the controller clock.
+ - marvell,tx-checksum-limit: max tx packet size for hardware checksum.
+
+* Ethernet port node
+
+Required port properties:
+ - device_type: shall be "network".
+ - compatible: shall be one of "marvell,orion-eth-port",
+      "marvell,kirkwood-eth-port".
+ - reg: port number relative to ethernet controller, shall be 0, 1, or 2.
+ - interrupts: port interrupt.
+ - local-mac-address: 6 bytes MAC address.
+
+Optional port properties:
+ - marvell,tx-queue-size: size of the transmit ring buffer.
+ - marvell,tx-sram-addr: address of transmit descriptor buffer located in SRAM.
+ - marvell,tx-sram-size: size of transmit descriptor buffer located in SRAM.
+ - marvell,rx-queue-size: size of the receive ring buffer.
+ - marvell,rx-sram-addr: address of receive descriptor buffer located in SRAM.
+ - marvell,rx-sram-size: size of receive descriptor buffer located in SRAM.
+
+and
+
+ - phy-handle: phandle reference to ethernet PHY.
+
+or
+
+ - speed: port speed if no PHY connected.
+ - duplex: port mode if no PHY connected.
+
+* Node example:
+
+mdio-bus {
+       ...
+       ethphy: ethernet-phy@8 {
+               device_type = "ethernet-phy";
+               ...
+       };
+};
+
+eth: ethernet-controller@72000 {
+       compatible = "marvell,orion-eth";
+       #address-cells = <1>;
+       #size-cells = <0>;
+       reg = <0x72000 0x2000>;
+       clocks = <&gate_clk 2>;
+       marvell,tx-checksum-limit = <1600>;
+
+       ethernet@0 {
+               device_type = "network";
+               compatible = "marvell,orion-eth-port";
+               reg = <0>;
+               interrupts = <29>;
+               phy-handle = <&ethphy>;
+               local-mac-address = [00 00 00 00 00 00];
+       };
+};
diff --git a/Documentation/devicetree/bindings/net/micrel-ks8851.txt b/Documentation/devicetree/bindings/net/micrel-ks8851.txt
new file mode 100644 (file)
index 0000000..11ace3c
--- /dev/null
@@ -0,0 +1,9 @@
+Micrel KS8851 Ethernet mac
+
+Required properties:
+- compatible = "micrel,ks8851-ml" of parallel interface
+- reg : 2 physical address and size of registers for data and command
+- interrupts : interrupt connection
+
+Optional properties:
+- local-mac-address : Ethernet mac address to use
index 060bbf0..261c563 100644 (file)
@@ -12,6 +12,16 @@ Required properties:
   property
 - phy-mode: String, operation mode of the PHY interface.
   Supported values are: "mii", "rmii", "gmii", "rgmii".
+- snps,phy-addr                phy address to connect to.
+- snps,reset-gpio      gpio number for phy reset.
+- snps,reset-active-low boolean flag to indicate if phy reset is active low.
+- snps,reset-delays-us  is triplet of delays
+       The 1st cell is reset pre-delay in micro seconds.
+       The 2nd cell is reset pulse in micro seconds.
+       The 3rd cell is reset post-delay in micro seconds.
+- snps,pbl             Programmable Burst Length
+- snps,fixed-burst     Program the DMA to use the fixed burst mode
+- snps,mixed-burst     Program the DMA to use the mixed burst mode
 
 Optional properties:
 - mac-address: 6 bytes, mac address
diff --git a/Documentation/devicetree/bindings/net/via-velocity.txt b/Documentation/devicetree/bindings/net/via-velocity.txt
new file mode 100644 (file)
index 0000000..b3db469
--- /dev/null
@@ -0,0 +1,20 @@
+* VIA Velocity 10/100/1000 Network Controller
+
+Required properties:
+- compatible : Should be "via,velocity-vt6110"
+- reg : Address and length of the io space
+- interrupts : Should contain the controller interrupt line
+
+Optional properties:
+- no-eeprom : PCI network cards use an external EEPROM to store data. Embedded
+       devices quite often set this data in uboot and do not provide an eeprom.
+       Specify this option if you have no external eeprom.
+
+Examples:
+
+eth0@d8004000 {
+       compatible = "via,velocity-vt6110";
+       reg = <0xd8004000 0x400>;
+       interrupts = <10>;
+       no-eeprom;
+};
index 948f615..d5a79ca 100644 (file)
@@ -18,6 +18,7 @@ chrp  Common Hardware Reference Platform
 cirrus Cirrus Logic, Inc.
 cortina        Cortina Systems, Inc.
 dallas Maxim Integrated Products (formerly Dallas Semiconductor)
+davicom        DAVICOM Semiconductor, Inc.
 denx   Denx Software Engineering
 emmicro        EM Microelectronic
 epson  Seiko Epson Corp.
index 1500385..e1d4a0b 100644 (file)
@@ -34,6 +34,7 @@ optional properties:
                        - ignored     = ignored
  - interlaced (bool): boolean to enable interlaced mode
  - doublescan (bool): boolean to enable doublescan mode
+ - doubleclk (bool): boolean to enable doubleclock mode
 
 All the optional properties that are not bool follow the following logic:
     <1>: high active
index 589edee..323983b 100644 (file)
@@ -1,22 +1,23 @@
 Device-Tree bindings for drm hdmi driver
 
 Required properties:
-- compatible: value should be "samsung,exynos5-hdmi".
+- compatible: value should be one among the following:
+       1) "samsung,exynos5-hdmi" <DEPRECATED>
+       2) "samsung,exynos4210-hdmi"
+       3) "samsung,exynos4212-hdmi"
 - reg: physical base address of the hdmi and length of memory mapped
        region.
 - interrupts: interrupt number to the cpu.
 - hpd-gpio: following information about the hotplug gpio pin.
        a) phandle of the gpio controller node.
        b) pin number within the gpio controller.
-       c) pin function mode.
-       d) optional flags and pull up/down.
-       e) drive strength.
+       c) optional flags and pull up/down.
 
 Example:
 
        hdmi {
-               compatible = "samsung,exynos5-hdmi";
+               compatible = "samsung,exynos4212-hdmi";
                reg = <0x14530000 0x100000>;
                interrupts = <0 95 0>;
-               hpd-gpio = <&gpx3 7 0xf 1 3>;
+               hpd-gpio = <&gpx3 7 1>;
        };
index fa166d9..41eee97 100644 (file)
@@ -1,12 +1,15 @@
 Device-Tree bindings for hdmiddc driver
 
 Required properties:
-- compatible: value should be "samsung,exynos5-hdmiddc".
+- compatible: value should be one of the following
+       1) "samsung,exynos5-hdmiddc" <DEPRECATED>
+       2) "samsung,exynos4210-hdmiddc"
+
 - reg: I2C address of the hdmiddc device.
 
 Example:
 
        hdmiddc {
-               compatible = "samsung,exynos5-hdmiddc";
+               compatible = "samsung,exynos4210-hdmiddc";
                reg = <0x50>;
        };
index 858f4f9..162f641 100644 (file)
@@ -1,12 +1,15 @@
 Device-Tree bindings for hdmiphy driver
 
 Required properties:
-- compatible: value should be "samsung,exynos5-hdmiphy".
+- compatible: value should be one of the following:
+       1) "samsung,exynos5-hdmiphy" <DEPRECATED>
+       2) "samsung,exynos4210-hdmiphy".
+       3) "samsung,exynos4212-hdmiphy".
 - reg: I2C address of the hdmiphy device.
 
 Example:
 
        hdmiphy {
-               compatible = "samsung,exynos5-hdmiphy";
+               compatible = "samsung,exynos4210-hdmiphy";
                reg = <0x38>;
        };
index 9b2ea03..3334b0a 100644 (file)
@@ -1,7 +1,12 @@
 Device-Tree bindings for mixer driver
 
 Required properties:
-- compatible: value should be "samsung,exynos5-mixer".
+- compatible: value should be one of the following:
+       1) "samsung,exynos5-mixer" <DEPRECATED>
+       2) "samsung,exynos4210-mixer"
+       3) "samsung,exynos5250-mixer"
+       4) "samsung,exynos5420-mixer"
+
 - reg: physical base address of the mixer and length of memory mapped
        region.
 - interrupts: interrupt number to the cpu.
@@ -9,7 +14,7 @@ Required properties:
 Example:
 
        mixer {
-               compatible = "samsung,exynos5-mixer";
+               compatible = "samsung,exynos5250-mixer";
                reg = <0x14450000 0x10000>;
                interrupts = <0 94 0>;
        };
diff --git a/Documentation/devicetree/bindings/video/fsl,imx-fb.txt b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
new file mode 100644 (file)
index 0000000..46da08d
--- /dev/null
@@ -0,0 +1,51 @@
+Freescale imx21 Framebuffer
+
+This framebuffer driver supports devices imx1, imx21, imx25, and imx27.
+
+Required properties:
+- compatible : "fsl,<chip>-fb", chip should be imx1 or imx21
+- reg : Should contain 1 register ranges(address and length)
+- interrupts : One interrupt of the fb dev
+
+Required nodes:
+- display: Phandle to a display node as described in
+       Documentation/devicetree/bindings/video/display-timing.txt
+       Additional, the display node has to define properties:
+       - bits-per-pixel: Bits per pixel
+       - fsl,pcr: LCDC PCR value
+
+Optional properties:
+- fsl,dmacr: DMA Control Register value. This is optional. By default, the
+       register is not modified as recommended by the datasheet.
+- fsl,lscr1: LCDC Sharp Configuration Register value.
+
+Example:
+
+       imxfb: fb@10021000 {
+               compatible = "fsl,imx21-fb";
+               interrupts = <61>;
+               reg = <0x10021000 0x1000>;
+               display = <&display0>;
+       };
+
+       ...
+
+       display0: display0 {
+               model = "Primeview-PD050VL1";
+               native-mode = <&timing_disp0>;
+               bits-per-pixel = <16>;
+               fsl,pcr = <0xf0c88080>; /* non-standard but required */
+               display-timings {
+                       timing_disp0: 640x480 {
+                               hactive = <640>;
+                               vactive = <480>;
+                               hback-porch = <112>;
+                               hfront-porch = <36>;
+                               hsync-len = <32>;
+                               vback-porch = <33>;
+                               vfront-porch = <33>;
+                               vsync-len = <2>;
+                               clock-frequency = <25000000>;
+                       };
+               };
+       };
index 3d0060c..7a12542 100644 (file)
@@ -1,13 +1,17 @@
 * Solomon SSD1307 Framebuffer Driver
 
 Required properties:
-  - compatible: Should be "solomon,ssd1307fb-<bus>". The only supported bus for
-    now is i2c.
+  - compatible: Should be "solomon,<chip>fb-<bus>". The only supported bus for
+    now is i2c, and the supported chips are ssd1306 and ssd1307.
   - reg: Should contain address of the controller on the I2C bus. Most likely
          0x3c or 0x3d
   - pwm: Should contain the pwm to use according to the OF device tree PWM
-         specification [0]
+         specification [0]. Only required for the ssd1307.
   - reset-gpios: Should contain the GPIO used to reset the OLED display
+  - solomon,height: Height in pixel of the screen driven by the controller
+  - solomon,width: Width in pixel of the screen driven by the controller
+  - solomon,page-offset: Offset of pages (band of 8 pixels) that the screen is
+    mapped to.
 
 Optional properties:
   - reset-active-low: Is the reset gpio is active on physical low?
index eefdd91..f6362d8 100644 (file)
@@ -81,17 +81,11 @@ pmipal  Use the protected mode interface for palette changes.
 
 mtrr:n  Setup memory type range registers for the framebuffer
         where n:
-              0 - disabled (equivalent to nomtrr) (default)
-              1 - uncachable
-              2 - write-back
-              3 - write-combining
-              4 - write-through
-
-        If you see the following in dmesg, choose the type that matches
-        the old one.  In this example, use "mtrr:2".
-...
-mtrr: type mismatch for e0000000,8000000 old: write-back new: write-combining
-...
+              0 - disabled (equivalent to nomtrr)
+              3 - write-combining (default)
+
+       Values other than 0 and 3 will result in a warning and will be
+       treated just like 3.
 
 nomtrr  Do not use memory type range registers.
 
index 25dc4a0..75236f1 100644 (file)
@@ -2681,9 +2681,17 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        Run specified binary instead of /init from the ramdisk,
                        used for early userspace startup. See initrd.
 
-       reboot=         [BUGS=X86-32,BUGS=ARM,BUGS=IA-64] Rebooting mode
-                       Format: <reboot_mode>[,<reboot_mode2>[,...]]
-                       See arch/*/kernel/reboot.c or arch/*/kernel/process.c
+       reboot=         [KNL]
+                       Format (x86 or x86_64):
+                               [w[arm] | c[old] | h[ard] | s[oft] | g[pio]] \
+                               [[,]s[mp]#### \
+                               [[,]b[ios] | a[cpi] | k[bd] | t[riple] | e[fi] | p[ci]] \
+                               [[,]f[orce]
+                       Where reboot_mode is one of warm (soft) or cold (hard) or gpio,
+                             reboot_type is one of bios, acpi, kbd, triple, efi, or pci,
+                             reboot_force is either force or not specified,
+                             reboot_cpu is s[mp]#### with #### being the processor
+                                       to be used for rebooting.
 
        relax_domain_level=
                        [KNL, SMP] Set scheduler's default relax_domain_level.
index 258d9b9..32dfbd9 100644 (file)
@@ -88,8 +88,6 @@ gianfar.txt
        - Gianfar Ethernet Driver.
 ieee802154.txt
        - Linux IEEE 802.15.4 implementation, API and drivers
-ifenslave.c
-       - Configure network interfaces for parallel routing (bonding).
 igb.txt
        - README for the Intel Gigabit Ethernet Driver (igb).
 igbvf.txt
index 24c308d..0aa1ac9 100644 (file)
@@ -1,11 +1,6 @@
 # kbuild trick to avoid linker error. Can be omitted if a module is built.
 obj- := dummy.o
 
-# List of programs to build
-hostprogs-y := ifenslave
-
-HOSTCFLAGS_ifenslave.o += -I$(objtree)/usr/include
-
 # Tell kbuild to always build the programs
 always := $(hostprogs-y)
 
index 9ff5795..aff97f4 100644 (file)
@@ -70,9 +70,10 @@ list, mail to linux-arcnet@tichy.ch.uj.edu.pl.
 There are archives of the mailing list at:
        http://epistolary.org/mailman/listinfo.cgi/arcnet
 
-The people on linux-net@vger.kernel.org have also been known to be very
-helpful, especially when we're talking about ALPHA Linux kernels that may or
-may not work right in the first place.
+The people on linux-net@vger.kernel.org (now defunct, replaced by
+netdev@vger.kernel.org) have also been known to be very helpful, especially
+when we're talking about ALPHA Linux kernels that may or may not work right
+in the first place.
 
 
 Other Drivers and Info
index 10a015c..87bbcfe 100644 (file)
@@ -104,8 +104,7 @@ Table of Contents
 ==============================
 
        Most popular distro kernels ship with the bonding driver
-already available as a module and the ifenslave user level control
-program installed and ready for use. If your distro does not, or you
+already available as a module. If your distro does not, or you
 have need to compile bonding from source (e.g., configuring and
 installing a mainline kernel from kernel.org), you'll need to perform
 the following steps:
@@ -124,46 +123,13 @@ device support" section.  It is recommended that you configure the
 driver as module since it is currently the only way to pass parameters
 to the driver or configure more than one bonding device.
 
-       Build and install the new kernel and modules, then continue
-below to install ifenslave.
+       Build and install the new kernel and modules.
 
-1.2 Install ifenslave Control Utility
+1.2 Bonding Control Utility
 -------------------------------------
 
-       The ifenslave user level control program is included in the
-kernel source tree, in the file Documentation/networking/ifenslave.c.
-It is generally recommended that you use the ifenslave that
-corresponds to the kernel that you are using (either from the same
-source tree or supplied with the distro), however, ifenslave
-executables from older kernels should function (but features newer
-than the ifenslave release are not supported).  Running an ifenslave
-that is newer than the kernel is not supported, and may or may not
-work.
-
-       To install ifenslave, do the following:
-
-# gcc -Wall -O -I/usr/src/linux/include ifenslave.c -o ifenslave
-# cp ifenslave /sbin/ifenslave
-
-       If your kernel source is not in "/usr/src/linux," then replace
-"/usr/src/linux/include" in the above with the location of your kernel
-source include directory.
-
-       You may wish to back up any existing /sbin/ifenslave, or, for
-testing or informal use, tag the ifenslave to the kernel version
-(e.g., name the ifenslave executable /sbin/ifenslave-2.6.10).
-
-IMPORTANT NOTE:
-
-       If you omit the "-I" or specify an incorrect directory, you
-may end up with an ifenslave that is incompatible with the kernel
-you're trying to build it for.  Some distros (e.g., Red Hat from 7.1
-onwards) do not have /usr/include/linux symbolically linked to the
-default kernel source include directory.
-
-SECOND IMPORTANT NOTE:
-       If you plan to configure bonding using sysfs or using the
-/etc/network/interfaces file, you do not need to use ifenslave.
+        It is recommended to configure bonding via iproute2 (netlink)
+or sysfs, the old ifenslave control utility is obsolete.
 
 2. Bonding Driver Options
 =========================
@@ -337,6 +303,12 @@ arp_validate
        such a situation, validation of backup slaves must be
        disabled.
 
+       The validation of ARP requests on backup slaves is mainly
+       helping bonding to decide which slaves are more likely to
+       work in case of the active slave failure, it doesn't really
+       guarantee that the backup slave will work if it's selected
+       as the next active slave.
+
        This option is useful in network configurations in which
        multiple bonding hosts are concurrently issuing ARPs to one or
        more targets beyond a common switch.  Should the link between
@@ -349,6 +321,25 @@ arp_validate
 
        This option was added in bonding version 3.1.0.
 
+arp_all_targets
+
+       Specifies the quantity of arp_ip_targets that must be reachable
+       in order for the ARP monitor to consider a slave as being up.
+       This option affects only active-backup mode for slaves with
+       arp_validation enabled.
+
+       Possible values are:
+
+       any or 0
+
+               consider the slave up only when any of the arp_ip_targets
+               is reachable
+
+       all or 1
+
+               consider the slave up only when all of the arp_ip_targets
+               are reachable
+
 downdelay
 
        Specifies the time, in milliseconds, to wait before disabling
@@ -851,7 +842,7 @@ resend_igmp
 ==============================
 
        You can configure bonding using either your distro's network
-initialization scripts, or manually using either ifenslave or the
+initialization scripts, or manually using either iproute2 or the
 sysfs interface.  Distros generally use one of three packages for the
 network initialization scripts: initscripts, sysconfig or interfaces.
 Recent versions of these packages have support for bonding, while older
@@ -1160,7 +1151,7 @@ not support this method for specifying multiple bonding interfaces; for
 those instances, see the "Configuring Multiple Bonds Manually" section,
 below.
 
-3.3 Configuring Bonding Manually with Ifenslave
+3.3 Configuring Bonding Manually with iproute2
 -----------------------------------------------
 
        This section applies to distros whose network initialization
@@ -1171,7 +1162,7 @@ version 8.
        The general method for these systems is to place the bonding
 module parameters into a config file in /etc/modprobe.d/ (as
 appropriate for the installed distro), then add modprobe and/or
-ifenslave commands to the system's global init script.  The name of
+`ip link` commands to the system's global init script.  The name of
 the global init script differs; for sysconfig, it is
 /etc/init.d/boot.local and for initscripts it is /etc/rc.d/rc.local.
 
@@ -1183,8 +1174,8 @@ reboots, edit the appropriate file (/etc/init.d/boot.local or
 modprobe bonding mode=balance-alb miimon=100
 modprobe e100
 ifconfig bond0 192.168.1.1 netmask 255.255.255.0 up
-ifenslave bond0 eth0
-ifenslave bond0 eth1
+ip link set eth0 master bond0
+ip link set eth1 master bond0
 
        Replace the example bonding module parameters and bond0
 network configuration (IP address, netmask, etc) with the appropriate
diff --git a/Documentation/networking/ifenslave.c b/Documentation/networking/ifenslave.c
deleted file mode 100644 (file)
index ac5debb..0000000
+++ /dev/null
@@ -1,1105 +0,0 @@
-/* Mode: C;
- * ifenslave.c: Configure network interfaces for parallel routing.
- *
- *     This program controls the Linux implementation of running multiple
- *     network interfaces in parallel.
- *
- * Author:     Donald Becker <becker@cesdis.gsfc.nasa.gov>
- *             Copyright 1994-1996 Donald Becker
- *
- *             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.
- *
- *     The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
- *     Center of Excellence in Space Data and Information Sciences
- *        Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
- *
- *  Changes :
- *    - 2000/10/02 Willy Tarreau <willy at meta-x.org> :
- *       - few fixes. Master's MAC address is now correctly taken from
- *         the first device when not previously set ;
- *       - detach support : call BOND_RELEASE to detach an enslaved interface.
- *       - give a mini-howto from command-line help : # ifenslave -h
- *
- *    - 2001/02/16 Chad N. Tindel <ctindel at ieee dot org> :
- *       - Master is now brought down before setting the MAC address.  In
- *         the 2.4 kernel you can't change the MAC address while the device is
- *         up because you get EBUSY.
- *
- *    - 2001/09/13 Takao Indoh <indou dot takao at jp dot fujitsu dot com>
- *       - Added the ability to change the active interface on a mode 1 bond
- *         at runtime.
- *
- *    - 2001/10/23 Chad N. Tindel <ctindel at ieee dot org> :
- *       - No longer set the MAC address of the master.  The bond device will
- *         take care of this itself
- *       - Try the SIOC*** versions of the bonding ioctls before using the
- *         old versions
- *    - 2002/02/18 Erik Habbinga <erik_habbinga @ hp dot com> :
- *       - ifr2.ifr_flags was not initialized in the hwaddr_notset case,
- *         SIOCGIFFLAGS now called before hwaddr_notset test
- *
- *    - 2002/10/31 Tony Cureington <tony.cureington * hp_com> :
- *       - If the master does not have a hardware address when the first slave
- *         is enslaved, the master is assigned the hardware address of that
- *         slave - there is a comment in bonding.c stating "ifenslave takes
- *         care of this now." This corrects the problem of slaves having
- *         different hardware addresses in active-backup mode when
- *         multiple interfaces are specified on a single ifenslave command
- *         (ifenslave bond0 eth0 eth1).
- *
- *    - 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
- *                   Shmulik Hen <shmulik.hen at intel dot com>
- *       - Moved setting the slave's mac address and openning it, from
- *         the application to the driver. This enables support of modes
- *         that need to use the unique mac address of each slave.
- *         The driver also takes care of closing the slave and restoring its
- *         original mac address upon release.
- *         In addition, block possibility of enslaving before the master is up.
- *         This prevents putting the system in an undefined state.
- *
- *    - 2003/05/01 - Amir Noam <amir.noam at intel dot com>
- *       - Added ABI version control to restore compatibility between
- *         new/old ifenslave and new/old bonding.
- *       - Prevent adding an adapter that is already a slave.
- *         Fixes the problem of stalling the transmission and leaving
- *         the slave in a down state.
- *
- *    - 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
- *       - Prevent enslaving if the bond device is down.
- *         Fixes the problem of leaving the system in unstable state and
- *         halting when trying to remove the module.
- *       - Close socket on all abnormal exists.
- *       - Add versioning scheme that follows that of the bonding driver.
- *         current version is 1.0.0 as a base line.
- *
- *    - 2003/05/22 - Jay Vosburgh <fubar at us dot ibm dot com>
- *      - ifenslave -c was broken; it's now fixed
- *      - Fixed problem with routes vanishing from master during enslave
- *        processing.
- *
- *    - 2003/05/27 - Amir Noam <amir.noam at intel dot com>
- *      - Fix backward compatibility issues:
- *        For drivers not using ABI versions, slave was set down while
- *        it should be left up before enslaving.
- *        Also, master was not set down and the default set_mac_address()
- *        would fail and generate an error message in the system log.
- *      - For opt_c: slave should not be set to the master's setting
- *        while it is running. It was already set during enslave. To
- *        simplify things, it is now handled separately.
- *
- *    - 2003/12/01 - Shmulik Hen <shmulik.hen at intel dot com>
- *      - Code cleanup and style changes
- *        set version to 1.1.0
- */
-
-#define APP_VERSION    "1.1.0"
-#define APP_RELDATE    "December 1, 2003"
-#define APP_NAME       "ifenslave"
-
-static char *version =
-APP_NAME ".c:v" APP_VERSION " (" APP_RELDATE ")\n"
-"o Donald Becker (becker@cesdis.gsfc.nasa.gov).\n"
-"o Detach support added on 2000/10/02 by Willy Tarreau (willy at meta-x.org).\n"
-"o 2.4 kernel support added on 2001/02/16 by Chad N. Tindel\n"
-"  (ctindel at ieee dot org).\n";
-
-static const char *usage_msg =
-"Usage: ifenslave [-f] <master-if> <slave-if> [<slave-if>...]\n"
-"       ifenslave -d   <master-if> <slave-if> [<slave-if>...]\n"
-"       ifenslave -c   <master-if> <slave-if>\n"
-"       ifenslave --help\n";
-
-static const char *help_msg =
-"\n"
-"       To create a bond device, simply follow these three steps :\n"
-"       - ensure that the required drivers are properly loaded :\n"
-"         # modprobe bonding ; modprobe <3c59x|eepro100|pcnet32|tulip|...>\n"
-"       - assign an IP address to the bond device :\n"
-"         # ifconfig bond0 <addr> netmask <mask> broadcast <bcast>\n"
-"       - attach all the interfaces you need to the bond device :\n"
-"         # ifenslave [{-f|--force}] bond0 eth0 [eth1 [eth2]...]\n"
-"         If bond0 didn't have a MAC address, it will take eth0's. Then, all\n"
-"         interfaces attached AFTER this assignment will get the same MAC addr.\n"
-"         (except for ALB/TLB modes)\n"
-"\n"
-"       To set the bond device down and automatically release all the slaves :\n"
-"         # ifconfig bond0 down\n"
-"\n"
-"       To detach a dead interface without setting the bond device down :\n"
-"         # ifenslave {-d|--detach} bond0 eth0 [eth1 [eth2]...]\n"
-"\n"
-"       To change active slave :\n"
-"         # ifenslave {-c|--change-active} bond0 eth0\n"
-"\n"
-"       To show master interface info\n"
-"         # ifenslave bond0\n"
-"\n"
-"       To show all interfaces info\n"
-"       # ifenslave {-a|--all-interfaces}\n"
-"\n"
-"       To be more verbose\n"
-"       # ifenslave {-v|--verbose} ...\n"
-"\n"
-"       # ifenslave {-u|--usage}   Show usage\n"
-"       # ifenslave {-V|--version} Show version\n"
-"       # ifenslave {-h|--help}    This message\n"
-"\n";
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <linux/if.h>
-#include <net/if_arp.h>
-#include <linux/if_ether.h>
-#include <linux/if_bonding.h>
-#include <linux/sockios.h>
-
-typedef unsigned long long u64;        /* hack, so we may include kernel's ethtool.h */
-typedef __uint32_t u32;                /* ditto */
-typedef __uint16_t u16;                /* ditto */
-typedef __uint8_t u8;          /* ditto */
-#include <linux/ethtool.h>
-
-struct option longopts[] = {
-       /* { name  has_arg  *flag  val } */
-       {"all-interfaces",      0, 0, 'a'},     /* Show all interfaces. */
-       {"change-active",       0, 0, 'c'},     /* Change the active slave.  */
-       {"detach",              0, 0, 'd'},     /* Detach a slave interface. */
-       {"force",               0, 0, 'f'},     /* Force the operation. */
-       {"help",                0, 0, 'h'},     /* Give help */
-       {"usage",               0, 0, 'u'},     /* Give usage */
-       {"verbose",             0, 0, 'v'},     /* Report each action taken. */
-       {"version",             0, 0, 'V'},     /* Emit version information. */
-       { 0, 0, 0, 0}
-};
-
-/* Command-line flags. */
-unsigned int
-opt_a = 0,     /* Show-all-interfaces flag. */
-opt_c = 0,     /* Change-active-slave flag. */
-opt_d = 0,     /* Detach a slave interface. */
-opt_f = 0,     /* Force the operation. */
-opt_h = 0,     /* Help */
-opt_u = 0,     /* Usage */
-opt_v = 0,     /* Verbose flag. */
-opt_V = 0;     /* Version */
-
-int skfd = -1;         /* AF_INET socket for ioctl() calls.*/
-int abi_ver = 0;       /* userland - kernel ABI version */
-int hwaddr_set = 0;    /* Master's hwaddr is set */
-int saved_errno;
-
-struct ifreq master_mtu, master_flags, master_hwaddr;
-struct ifreq slave_mtu, slave_flags, slave_hwaddr;
-
-struct dev_ifr {
-       struct ifreq *req_ifr;
-       char *req_name;
-       int req_type;
-};
-
-struct dev_ifr master_ifra[] = {
-       {&master_mtu,     "SIOCGIFMTU",     SIOCGIFMTU},
-       {&master_flags,   "SIOCGIFFLAGS",   SIOCGIFFLAGS},
-       {&master_hwaddr,  "SIOCGIFHWADDR",  SIOCGIFHWADDR},
-       {NULL, "", 0}
-};
-
-struct dev_ifr slave_ifra[] = {
-       {&slave_mtu,     "SIOCGIFMTU",     SIOCGIFMTU},
-       {&slave_flags,   "SIOCGIFFLAGS",   SIOCGIFFLAGS},
-       {&slave_hwaddr,  "SIOCGIFHWADDR",  SIOCGIFHWADDR},
-       {NULL, "", 0}
-};
-
-static void if_print(char *ifname);
-static int get_drv_info(char *master_ifname);
-static int get_if_settings(char *ifname, struct dev_ifr ifra[]);
-static int get_slave_flags(char *slave_ifname);
-static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr);
-static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr);
-static int set_slave_mtu(char *slave_ifname, int mtu);
-static int set_if_flags(char *ifname, short flags);
-static int set_if_up(char *ifname, short flags);
-static int set_if_down(char *ifname, short flags);
-static int clear_if_addr(char *ifname);
-static int set_if_addr(char *master_ifname, char *slave_ifname);
-static int change_active(char *master_ifname, char *slave_ifname);
-static int enslave(char *master_ifname, char *slave_ifname);
-static int release(char *master_ifname, char *slave_ifname);
-#define v_print(fmt, args...)  \
-       if (opt_v)              \
-               fprintf(stderr, fmt, ## args )
-
-int main(int argc, char *argv[])
-{
-       char **spp, *master_ifname, *slave_ifname;
-       int c, i, rv;
-       int res = 0;
-       int exclusive = 0;
-
-       while ((c = getopt_long(argc, argv, "acdfhuvV", longopts, 0)) != EOF) {
-               switch (c) {
-               case 'a': opt_a++; exclusive++; break;
-               case 'c': opt_c++; exclusive++; break;
-               case 'd': opt_d++; exclusive++; break;
-               case 'f': opt_f++; exclusive++; break;
-               case 'h': opt_h++; exclusive++; break;
-               case 'u': opt_u++; exclusive++; break;
-               case 'v': opt_v++; break;
-               case 'V': opt_V++; exclusive++; break;
-
-               case '?':
-                       fprintf(stderr, "%s", usage_msg);
-                       res = 2;
-                       goto out;
-               }
-       }
-
-       /* options check */
-       if (exclusive > 1) {
-               fprintf(stderr, "%s", usage_msg);
-               res = 2;
-               goto out;
-       }
-
-       if (opt_v || opt_V) {
-               printf("%s", version);
-               if (opt_V) {
-                       res = 0;
-                       goto out;
-               }
-       }
-
-       if (opt_u) {
-               printf("%s", usage_msg);
-               res = 0;
-               goto out;
-       }
-
-       if (opt_h) {
-               printf("%s", usage_msg);
-               printf("%s", help_msg);
-               res = 0;
-               goto out;
-       }
-
-       /* Open a basic socket */
-       if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-               perror("socket");
-               res = 1;
-               goto out;
-       }
-
-       if (opt_a) {
-               if (optind == argc) {
-                       /* No remaining args */
-                       /* show all interfaces */
-                       if_print((char *)NULL);
-                       goto out;
-               } else {
-                       /* Just show usage */
-                       fprintf(stderr, "%s", usage_msg);
-                       res = 2;
-                       goto out;
-               }
-       }
-
-       /* Copy the interface name */
-       spp = argv + optind;
-       master_ifname = *spp++;
-
-       if (master_ifname == NULL) {
-               fprintf(stderr, "%s", usage_msg);
-               res = 2;
-               goto out;
-       }
-
-       /* exchange abi version with bonding module */
-       res = get_drv_info(master_ifname);
-       if (res) {
-               fprintf(stderr,
-                       "Master '%s': Error: handshake with driver failed. "
-                       "Aborting\n",
-                       master_ifname);
-               goto out;
-       }
-
-       slave_ifname = *spp++;
-
-       if (slave_ifname == NULL) {
-               if (opt_d || opt_c) {
-                       fprintf(stderr, "%s", usage_msg);
-                       res = 2;
-                       goto out;
-               }
-
-               /* A single arg means show the
-                * configuration for this interface
-                */
-               if_print(master_ifname);
-               goto out;
-       }
-
-       res = get_if_settings(master_ifname, master_ifra);
-       if (res) {
-               /* Probably a good reason not to go on */
-               fprintf(stderr,
-                       "Master '%s': Error: get settings failed: %s. "
-                       "Aborting\n",
-                       master_ifname, strerror(res));
-               goto out;
-       }
-
-       /* check if master is indeed a master;
-        * if not then fail any operation
-        */
-       if (!(master_flags.ifr_flags & IFF_MASTER)) {
-               fprintf(stderr,
-                       "Illegal operation; the specified interface '%s' "
-                       "is not a master. Aborting\n",
-                       master_ifname);
-               res = 1;
-               goto out;
-       }
-
-       /* check if master is up; if not then fail any operation */
-       if (!(master_flags.ifr_flags & IFF_UP)) {
-               fprintf(stderr,
-                       "Illegal operation; the specified master interface "
-                       "'%s' is not up.\n",
-                       master_ifname);
-               res = 1;
-               goto out;
-       }
-
-       /* Only for enslaving */
-       if (!opt_c && !opt_d) {
-               sa_family_t master_family = master_hwaddr.ifr_hwaddr.sa_family;
-               unsigned char *hwaddr =
-                       (unsigned char *)master_hwaddr.ifr_hwaddr.sa_data;
-
-               /* The family '1' is ARPHRD_ETHER for ethernet. */
-               if (master_family != 1 && !opt_f) {
-                       fprintf(stderr,
-                               "Illegal operation: The specified master "
-                               "interface '%s' is not ethernet-like.\n "
-                               "This program is designed to work with "
-                               "ethernet-like network interfaces.\n "
-                               "Use the '-f' option to force the "
-                               "operation.\n",
-                               master_ifname);
-                       res = 1;
-                       goto out;
-               }
-
-               /* Check master's hw addr */
-               for (i = 0; i < 6; i++) {
-                       if (hwaddr[i] != 0) {
-                               hwaddr_set = 1;
-                               break;
-                       }
-               }
-
-               if (hwaddr_set) {
-                       v_print("current hardware address of master '%s' "
-                               "is %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
-                               "type %d\n",
-                               master_ifname,
-                               hwaddr[0], hwaddr[1],
-                               hwaddr[2], hwaddr[3],
-                               hwaddr[4], hwaddr[5],
-                               master_family);
-               }
-       }
-
-       /* Accepts only one slave */
-       if (opt_c) {
-               /* change active slave */
-               res = get_slave_flags(slave_ifname);
-               if (res) {
-                       fprintf(stderr,
-                               "Slave '%s': Error: get flags failed. "
-                               "Aborting\n",
-                               slave_ifname);
-                       goto out;
-               }
-               res = change_active(master_ifname, slave_ifname);
-               if (res) {
-                       fprintf(stderr,
-                               "Master '%s', Slave '%s': Error: "
-                               "Change active failed\n",
-                               master_ifname, slave_ifname);
-               }
-       } else {
-               /* Accept multiple slaves */
-               do {
-                       if (opt_d) {
-                               /* detach a slave interface from the master */
-                               rv = get_slave_flags(slave_ifname);
-                               if (rv) {
-                                       /* Can't work with this slave. */
-                                       /* remember the error and skip it*/
-                                       fprintf(stderr,
-                                               "Slave '%s': Error: get flags "
-                                               "failed. Skipping\n",
-                                               slave_ifname);
-                                       res = rv;
-                                       continue;
-                               }
-                               rv = release(master_ifname, slave_ifname);
-                               if (rv) {
-                                       fprintf(stderr,
-                                               "Master '%s', Slave '%s': Error: "
-                                               "Release failed\n",
-                                               master_ifname, slave_ifname);
-                                       res = rv;
-                               }
-                       } else {
-                               /* attach a slave interface to the master */
-                               rv = get_if_settings(slave_ifname, slave_ifra);
-                               if (rv) {
-                                       /* Can't work with this slave. */
-                                       /* remember the error and skip it*/
-                                       fprintf(stderr,
-                                               "Slave '%s': Error: get "
-                                               "settings failed: %s. "
-                                               "Skipping\n",
-                                               slave_ifname, strerror(rv));
-                                       res = rv;
-                                       continue;
-                               }
-                               rv = enslave(master_ifname, slave_ifname);
-                               if (rv) {
-                                       fprintf(stderr,
-                                               "Master '%s', Slave '%s': Error: "
-                                               "Enslave failed\n",
-                                               master_ifname, slave_ifname);
-                                       res = rv;
-                               }
-                       }
-               } while ((slave_ifname = *spp++) != NULL);
-       }
-
-out:
-       if (skfd >= 0) {
-               close(skfd);
-       }
-
-       return res;
-}
-
-static short mif_flags;
-
-/* Get the inteface configuration from the kernel. */
-static int if_getconfig(char *ifname)
-{
-       struct ifreq ifr;
-       int metric, mtu;        /* Parameters of the master interface. */
-       struct sockaddr dstaddr, broadaddr, netmask;
-       unsigned char *hwaddr;
-
-       strcpy(ifr.ifr_name, ifname);
-       if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
-               return -1;
-       mif_flags = ifr.ifr_flags;
-       printf("The result of SIOCGIFFLAGS on %s is %x.\n",
-              ifname, ifr.ifr_flags);
-
-       strcpy(ifr.ifr_name, ifname);
-       if (ioctl(skfd, SIOCGIFADDR, &ifr) < 0)
-               return -1;
-       printf("The result of SIOCGIFADDR is %2.2x.%2.2x.%2.2x.%2.2x.\n",
-              ifr.ifr_addr.sa_data[0], ifr.ifr_addr.sa_data[1],
-              ifr.ifr_addr.sa_data[2], ifr.ifr_addr.sa_data[3]);
-
-       strcpy(ifr.ifr_name, ifname);
-       if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0)
-               return -1;
-
-       /* Gotta convert from 'char' to unsigned for printf(). */
-       hwaddr = (unsigned char *)ifr.ifr_hwaddr.sa_data;
-       printf("The result of SIOCGIFHWADDR is type %d  "
-              "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n",
-              ifr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1],
-              hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
-
-       strcpy(ifr.ifr_name, ifname);
-       if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0) {
-               metric = 0;
-       } else
-               metric = ifr.ifr_metric;
-       printf("The result of SIOCGIFMETRIC is %d\n", metric);
-
-       strcpy(ifr.ifr_name, ifname);
-       if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0)
-               mtu = 0;
-       else
-               mtu = ifr.ifr_mtu;
-       printf("The result of SIOCGIFMTU is %d\n", mtu);
-
-       strcpy(ifr.ifr_name, ifname);
-       if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) < 0) {
-               memset(&dstaddr, 0, sizeof(struct sockaddr));
-       } else
-               dstaddr = ifr.ifr_dstaddr;
-
-       strcpy(ifr.ifr_name, ifname);
-       if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) < 0) {
-               memset(&broadaddr, 0, sizeof(struct sockaddr));
-       } else
-               broadaddr = ifr.ifr_broadaddr;
-
-       strcpy(ifr.ifr_name, ifname);
-       if (ioctl(skfd, SIOCGIFNETMASK, &ifr) < 0) {
-               memset(&netmask, 0, sizeof(struct sockaddr));
-       } else
-               netmask = ifr.ifr_netmask;
-
-       return 0;
-}
-
-static void if_print(char *ifname)
-{
-       char buff[1024];
-       struct ifconf ifc;
-       struct ifreq *ifr;
-       int i;
-
-       if (ifname == (char *)NULL) {
-               ifc.ifc_len = sizeof(buff);
-               ifc.ifc_buf = buff;
-               if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) {
-                       perror("SIOCGIFCONF failed");
-                       return;
-               }
-
-               ifr = ifc.ifc_req;
-               for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) {
-                       if (if_getconfig(ifr->ifr_name) < 0) {
-                               fprintf(stderr,
-                                       "%s: unknown interface.\n",
-                                       ifr->ifr_name);
-                               continue;
-                       }
-
-                       if (((mif_flags & IFF_UP) == 0) && !opt_a) continue;
-                       /*ife_print(&ife);*/
-               }
-       } else {
-               if (if_getconfig(ifname) < 0) {
-                       fprintf(stderr,
-                               "%s: unknown interface.\n", ifname);
-               }
-       }
-}
-
-static int get_drv_info(char *master_ifname)
-{
-       struct ifreq ifr;
-       struct ethtool_drvinfo info;
-       char *endptr;
-
-       memset(&ifr, 0, sizeof(ifr));
-       strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ);
-       ifr.ifr_data = (caddr_t)&info;
-
-       info.cmd = ETHTOOL_GDRVINFO;
-       strncpy(info.driver, "ifenslave", 32);
-       snprintf(info.fw_version, 32, "%d", BOND_ABI_VERSION);
-
-       if (ioctl(skfd, SIOCETHTOOL, &ifr) < 0) {
-               if (errno == EOPNOTSUPP) {
-                       goto out;
-               }
-
-               saved_errno = errno;
-               v_print("Master '%s': Error: get bonding info failed %s\n",
-                       master_ifname, strerror(saved_errno));
-               return 1;
-       }
-
-       abi_ver = strtoul(info.fw_version, &endptr, 0);
-       if (*endptr) {
-                v_print("Master '%s': Error: got invalid string as an ABI "
-                       "version from the bonding module\n",
-                       master_ifname);
-               return 1;
-       }
-
-out:
-       v_print("ABI ver is %d\n", abi_ver);
-
-       return 0;
-}
-
-static int change_active(char *master_ifname, char *slave_ifname)
-{
-       struct ifreq ifr;
-       int res = 0;
-
-       if (!(slave_flags.ifr_flags & IFF_SLAVE)) {
-               fprintf(stderr,
-                       "Illegal operation: The specified slave interface "
-                       "'%s' is not a slave\n",
-                       slave_ifname);
-               return 1;
-       }
-
-       strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ);
-       strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ);
-       if ((ioctl(skfd, SIOCBONDCHANGEACTIVE, &ifr) < 0) &&
-           (ioctl(skfd, BOND_CHANGE_ACTIVE_OLD, &ifr) < 0)) {
-               saved_errno = errno;
-               v_print("Master '%s': Error: SIOCBONDCHANGEACTIVE failed: "
-                       "%s\n",
-                       master_ifname, strerror(saved_errno));
-               res = 1;
-       }
-
-       return res;
-}
-
-static int enslave(char *master_ifname, char *slave_ifname)
-{
-       struct ifreq ifr;
-       int res = 0;
-
-       if (slave_flags.ifr_flags & IFF_SLAVE) {
-               fprintf(stderr,
-                       "Illegal operation: The specified slave interface "
-                       "'%s' is already a slave\n",
-                       slave_ifname);
-               return 1;
-       }
-
-       res = set_if_down(slave_ifname, slave_flags.ifr_flags);
-       if (res) {
-               fprintf(stderr,
-                       "Slave '%s': Error: bring interface down failed\n",
-                       slave_ifname);
-               return res;
-       }
-
-       if (abi_ver < 2) {
-               /* Older bonding versions would panic if the slave has no IP
-                * address, so get the IP setting from the master.
-                */
-               set_if_addr(master_ifname, slave_ifname);
-       } else {
-               res = clear_if_addr(slave_ifname);
-               if (res) {
-                       fprintf(stderr,
-                               "Slave '%s': Error: clear address failed\n",
-                               slave_ifname);
-                       return res;
-               }
-       }
-
-       if (master_mtu.ifr_mtu != slave_mtu.ifr_mtu) {
-               res = set_slave_mtu(slave_ifname, master_mtu.ifr_mtu);
-               if (res) {
-                       fprintf(stderr,
-                               "Slave '%s': Error: set MTU failed\n",
-                               slave_ifname);
-                       return res;
-               }
-       }
-
-       if (hwaddr_set) {
-               /* Master already has an hwaddr
-                * so set it's hwaddr to the slave
-                */
-               if (abi_ver < 1) {
-                       /* The driver is using an old ABI, so
-                        * the application sets the slave's
-                        * hwaddr
-                        */
-                       res = set_slave_hwaddr(slave_ifname,
-                                              &(master_hwaddr.ifr_hwaddr));
-                       if (res) {
-                               fprintf(stderr,
-                                       "Slave '%s': Error: set hw address "
-                                       "failed\n",
-                                       slave_ifname);
-                               goto undo_mtu;
-                       }
-
-                       /* For old ABI the application needs to bring the
-                        * slave back up
-                        */
-                       res = set_if_up(slave_ifname, slave_flags.ifr_flags);
-                       if (res) {
-                               fprintf(stderr,
-                                       "Slave '%s': Error: bring interface "
-                                       "down failed\n",
-                                       slave_ifname);
-                               goto undo_slave_mac;
-                       }
-               }
-               /* The driver is using a new ABI,
-                * so the driver takes care of setting
-                * the slave's hwaddr and bringing
-                * it up again
-                */
-       } else {
-               /* No hwaddr for master yet, so
-                * set the slave's hwaddr to it
-                */
-               if (abi_ver < 1) {
-                       /* For old ABI, the master needs to be
-                        * down before setting its hwaddr
-                        */
-                       res = set_if_down(master_ifname, master_flags.ifr_flags);
-                       if (res) {
-                               fprintf(stderr,
-                                       "Master '%s': Error: bring interface "
-                                       "down failed\n",
-                                       master_ifname);
-                               goto undo_mtu;
-                       }
-               }
-
-               res = set_master_hwaddr(master_ifname,
-                                       &(slave_hwaddr.ifr_hwaddr));
-               if (res) {
-                       fprintf(stderr,
-                               "Master '%s': Error: set hw address "
-                               "failed\n",
-                               master_ifname);
-                       goto undo_mtu;
-               }
-
-               if (abi_ver < 1) {
-                       /* For old ABI, bring the master
-                        * back up
-                        */
-                       res = set_if_up(master_ifname, master_flags.ifr_flags);
-                       if (res) {
-                               fprintf(stderr,
-                                       "Master '%s': Error: bring interface "
-                                       "up failed\n",
-                                       master_ifname);
-                               goto undo_master_mac;
-                       }
-               }
-
-               hwaddr_set = 1;
-       }
-
-       /* Do the real thing */
-       strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ);
-       strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ);
-       if ((ioctl(skfd, SIOCBONDENSLAVE, &ifr) < 0) &&
-           (ioctl(skfd, BOND_ENSLAVE_OLD, &ifr) < 0)) {
-               saved_errno = errno;
-               v_print("Master '%s': Error: SIOCBONDENSLAVE failed: %s\n",
-                       master_ifname, strerror(saved_errno));
-               res = 1;
-       }
-
-       if (res) {
-               goto undo_master_mac;
-       }
-
-       return 0;
-
-/* rollback (best effort) */
-undo_master_mac:
-       set_master_hwaddr(master_ifname, &(master_hwaddr.ifr_hwaddr));
-       hwaddr_set = 0;
-       goto undo_mtu;
-undo_slave_mac:
-       set_slave_hwaddr(slave_ifname, &(slave_hwaddr.ifr_hwaddr));
-undo_mtu:
-       set_slave_mtu(slave_ifname, slave_mtu.ifr_mtu);
-       return res;
-}
-
-static int release(char *master_ifname, char *slave_ifname)
-{
-       struct ifreq ifr;
-       int res = 0;
-
-       if (!(slave_flags.ifr_flags & IFF_SLAVE)) {
-               fprintf(stderr,
-                       "Illegal operation: The specified slave interface "
-                       "'%s' is not a slave\n",
-                       slave_ifname);
-               return 1;
-       }
-
-       strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ);
-       strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ);
-       if ((ioctl(skfd, SIOCBONDRELEASE, &ifr) < 0) &&
-           (ioctl(skfd, BOND_RELEASE_OLD, &ifr) < 0)) {
-               saved_errno = errno;
-               v_print("Master '%s': Error: SIOCBONDRELEASE failed: %s\n",
-                       master_ifname, strerror(saved_errno));
-               return 1;
-       } else if (abi_ver < 1) {
-               /* The driver is using an old ABI, so we'll set the interface
-                * down to avoid any conflicts due to same MAC/IP
-                */
-               res = set_if_down(slave_ifname, slave_flags.ifr_flags);
-               if (res) {
-                       fprintf(stderr,
-                               "Slave '%s': Error: bring interface "
-                               "down failed\n",
-                               slave_ifname);
-               }
-       }
-
-       /* set to default mtu */
-       set_slave_mtu(slave_ifname, 1500);
-
-       return res;
-}
-
-static int get_if_settings(char *ifname, struct dev_ifr ifra[])
-{
-       int i;
-       int res = 0;
-
-       for (i = 0; ifra[i].req_ifr; i++) {
-               strncpy(ifra[i].req_ifr->ifr_name, ifname, IFNAMSIZ);
-               res = ioctl(skfd, ifra[i].req_type, ifra[i].req_ifr);
-               if (res < 0) {
-                       saved_errno = errno;
-                       v_print("Interface '%s': Error: %s failed: %s\n",
-                               ifname, ifra[i].req_name,
-                               strerror(saved_errno));
-
-                       return saved_errno;
-               }
-       }
-
-       return 0;
-}
-
-static int get_slave_flags(char *slave_ifname)
-{
-       int res = 0;
-
-       strncpy(slave_flags.ifr_name, slave_ifname, IFNAMSIZ);
-       res = ioctl(skfd, SIOCGIFFLAGS, &slave_flags);
-       if (res < 0) {
-               saved_errno = errno;
-               v_print("Slave '%s': Error: SIOCGIFFLAGS failed: %s\n",
-                       slave_ifname, strerror(saved_errno));
-       } else {
-               v_print("Slave %s: flags %04X.\n",
-                       slave_ifname, slave_flags.ifr_flags);
-       }
-
-       return res;
-}
-
-static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr)
-{
-       unsigned char *addr = (unsigned char *)hwaddr->sa_data;
-       struct ifreq ifr;
-       int res = 0;
-
-       strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ);
-       memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr));
-       res = ioctl(skfd, SIOCSIFHWADDR, &ifr);
-       if (res < 0) {
-               saved_errno = errno;
-               v_print("Master '%s': Error: SIOCSIFHWADDR failed: %s\n",
-                       master_ifname, strerror(saved_errno));
-               return res;
-       } else {
-               v_print("Master '%s': hardware address set to "
-                       "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n",
-                       master_ifname, addr[0], addr[1], addr[2],
-                       addr[3], addr[4], addr[5]);
-       }
-
-       return res;
-}
-
-static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr)
-{
-       unsigned char *addr = (unsigned char *)hwaddr->sa_data;
-       struct ifreq ifr;
-       int res = 0;
-
-       strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ);
-       memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr));
-       res = ioctl(skfd, SIOCSIFHWADDR, &ifr);
-       if (res < 0) {
-               saved_errno = errno;
-
-               v_print("Slave '%s': Error: SIOCSIFHWADDR failed: %s\n",
-                       slave_ifname, strerror(saved_errno));
-
-               if (saved_errno == EBUSY) {
-                       v_print("  The device is busy: it must be idle "
-                               "before running this command.\n");
-               } else if (saved_errno == EOPNOTSUPP) {
-                       v_print("  The device does not support setting "
-                               "the MAC address.\n"
-                               "  Your kernel likely does not support slave "
-                               "devices.\n");
-               } else if (saved_errno == EINVAL) {
-                       v_print("  The device's address type does not match "
-                               "the master's address type.\n");
-               }
-               return res;
-       } else {
-               v_print("Slave '%s': hardware address set to "
-                       "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n",
-                       slave_ifname, addr[0], addr[1], addr[2],
-                       addr[3], addr[4], addr[5]);
-       }
-
-       return res;
-}
-
-static int set_slave_mtu(char *slave_ifname, int mtu)
-{
-       struct ifreq ifr;
-       int res = 0;
-
-       ifr.ifr_mtu = mtu;
-       strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ);
-
-       res = ioctl(skfd, SIOCSIFMTU, &ifr);
-       if (res < 0) {
-               saved_errno = errno;
-               v_print("Slave '%s': Error: SIOCSIFMTU failed: %s\n",
-                       slave_ifname, strerror(saved_errno));
-       } else {
-               v_print("Slave '%s': MTU set to %d.\n", slave_ifname, mtu);
-       }
-
-       return res;
-}
-
-static int set_if_flags(char *ifname, short flags)
-{
-       struct ifreq ifr;
-       int res = 0;
-
-       ifr.ifr_flags = flags;
-       strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
-
-       res = ioctl(skfd, SIOCSIFFLAGS, &ifr);
-       if (res < 0) {
-               saved_errno = errno;
-               v_print("Interface '%s': Error: SIOCSIFFLAGS failed: %s\n",
-                       ifname, strerror(saved_errno));
-       } else {
-               v_print("Interface '%s': flags set to %04X.\n", ifname, flags);
-       }
-
-       return res;
-}
-
-static int set_if_up(char *ifname, short flags)
-{
-       return set_if_flags(ifname, flags | IFF_UP);
-}
-
-static int set_if_down(char *ifname, short flags)
-{
-       return set_if_flags(ifname, flags & ~IFF_UP);
-}
-
-static int clear_if_addr(char *ifname)
-{
-       struct ifreq ifr;
-       int res = 0;
-
-       strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
-       ifr.ifr_addr.sa_family = AF_INET;
-       memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data));
-
-       res = ioctl(skfd, SIOCSIFADDR, &ifr);
-       if (res < 0) {
-               saved_errno = errno;
-               v_print("Interface '%s': Error: SIOCSIFADDR failed: %s\n",
-                       ifname, strerror(saved_errno));
-       } else {
-               v_print("Interface '%s': address cleared\n", ifname);
-       }
-
-       return res;
-}
-
-static int set_if_addr(char *master_ifname, char *slave_ifname)
-{
-       struct ifreq ifr;
-       int res;
-       unsigned char *ipaddr;
-       int i;
-       struct {
-               char *req_name;
-               char *desc;
-               int g_ioctl;
-               int s_ioctl;
-       } ifra[] = {
-               {"IFADDR", "addr", SIOCGIFADDR, SIOCSIFADDR},
-               {"DSTADDR", "destination addr", SIOCGIFDSTADDR, SIOCSIFDSTADDR},
-               {"BRDADDR", "broadcast addr", SIOCGIFBRDADDR, SIOCSIFBRDADDR},
-               {"NETMASK", "netmask", SIOCGIFNETMASK, SIOCSIFNETMASK},
-               {NULL, NULL, 0, 0},
-       };
-
-       for (i = 0; ifra[i].req_name; i++) {
-               strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ);
-               res = ioctl(skfd, ifra[i].g_ioctl, &ifr);
-               if (res < 0) {
-                       int saved_errno = errno;
-
-                       v_print("Interface '%s': Error: SIOCG%s failed: %s\n",
-                               master_ifname, ifra[i].req_name,
-                               strerror(saved_errno));
-
-                       ifr.ifr_addr.sa_family = AF_INET;
-                       memset(ifr.ifr_addr.sa_data, 0,
-                              sizeof(ifr.ifr_addr.sa_data));
-               }
-
-               strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ);
-               res = ioctl(skfd, ifra[i].s_ioctl, &ifr);
-               if (res < 0) {
-                       int saved_errno = errno;
-
-                       v_print("Interface '%s': Error: SIOCS%s failed: %s\n",
-                               slave_ifname, ifra[i].req_name,
-                               strerror(saved_errno));
-
-               }
-
-               ipaddr = (unsigned char *)ifr.ifr_addr.sa_data;
-               v_print("Interface '%s': set IP %s to %d.%d.%d.%d\n",
-                       slave_ifname, ifra[i].desc,
-                       ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
-       }
-
-       return 0;
-}
-
-/*
- * Local variables:
- *  version-control: t
- *  kept-new-versions: 5
- *  c-indent-level: 4
- *  c-basic-offset: 4
- *  tab-width: 4
- *  compile-command: "gcc -Wall -Wstrict-prototypes -O -I/usr/src/linux/include ifenslave.c -o ifenslave"
- * End:
- */
-
index aa68f3c..1074290 100644 (file)
@@ -685,6 +685,15 @@ ip_dynaddr - BOOLEAN
        occurs.
        Default: 0
 
+ip_early_demux - BOOLEAN
+       Optimize input packet processing down to one demux for
+       certain kinds of local sockets.  Currently we only do this
+       for established TCP sockets.
+
+       It may add an additional cost for pure routing workloads that
+       reduces overall throughput, in such case you should disable it.
+       Default: 1
+
 icmp_echo_ignore_all - BOOLEAN
        If set non-zero, then the kernel will ignore all ICMP ECHO
        requests sent to it.
@@ -729,7 +738,7 @@ icmp_ignore_bogus_error_responses - BOOLEAN
        frames.  Such violations are normally logged via a kernel warning.
        If this is set to TRUE, the kernel will not give such warnings, which
        will avoid log file clutter.
-       Default: FALSE
+       Default: 1
 
 icmp_errors_use_inbound_ifaddr - BOOLEAN
 
index 9573d0c..7a3c047 100644 (file)
@@ -181,6 +181,19 @@ snat_reroute - BOOLEAN
        always be the same as the original route so it is an optimisation
        to disable snat_reroute and avoid the recalculation.
 
+sync_persist_mode - INTEGER
+       default 0
+
+       Controls the synchronisation of connections when using persistence
+
+       0: All types of connections are synchronised
+       1: Attempt to reduce the synchronisation traffic depending on
+       the connection type. For persistent services avoid synchronisation
+       for normal connections, do it only for persistence templates.
+       In such case, for TCP and SCTP it may need enabling sloppy_tcp and
+       sloppy_sctp flags on backup servers. For non-persistent services
+       such optimization is not applied, mode 0 is assumed.
+
 sync_version - INTEGER
        default 1
 
index 9bd0f52..5333788 100644 (file)
@@ -114,7 +114,7 @@ Some parameters are constrained, specifically:
 - nm_frame_nr must equal the actual number of frames as specified above.
 
 When the kernel can't allocate physically continuous memory for a ring block,
-it will fall back to use physically discontinous memory. This might affect
+it will fall back to use physically discontinuous memory. This might affect
 performance negatively, in order to avoid this the nm_frame_size parameter
 should be chosen to be as small as possible for the required frame size and
 the number of blocks should be increased instead.
@@ -274,9 +274,9 @@ This example assumes some ring parameters of the ring setup are available.
                        /* Get next frame header */
                        hdr = rx_ring + frame_offset;
 
-                       if (hdr->nm_status == NL_MMAP_STATUS_VALID)
+                       if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
                                /* Regular memory mapped frame */
-                               nlh = (void *hdr) + NL_MMAP_HDRLEN;
+                               nlh = (void *)hdr + NL_MMAP_HDRLEN;
                                len = hdr->nm_len;
 
                                /* Release empty message immediately. May happen
index 23dd80e..8572796 100644 (file)
@@ -704,6 +704,12 @@ So it seems to be a good candidate to be used with packet fanout.
 Minimal example code by Daniel Borkmann based on Chetan Loke's lolpcap (compile
 it with gcc -Wall -O2 blob.c, and try things like "./a.out eth0", etc.):
 
+/* Written from scratch, but kernel-to-user space API usage
+ * dissected from lolpcap:
+ *  Copyright 2011, Chetan Loke <loke.chetan@gmail.com>
+ *  License: GPL, version 2.0
+ */
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
@@ -722,27 +728,6 @@ it with gcc -Wall -O2 blob.c, and try things like "./a.out eth0", etc.):
 #include <linux/if_ether.h>
 #include <linux/ip.h>
 
-#define BLOCK_SIZE             (1 << 22)
-#define FRAME_SIZE             2048
-
-#define NUM_BLOCKS             64
-#define NUM_FRAMES             ((BLOCK_SIZE * NUM_BLOCKS) / FRAME_SIZE)
-
-#define BLOCK_RETIRE_TOV_IN_MS 64
-#define BLOCK_PRIV_AREA_SZ     13
-
-#define ALIGN_8(x)             (((x) + 8 - 1) & ~(8 - 1))
-
-#define BLOCK_STATUS(x)                ((x)->h1.block_status)
-#define BLOCK_NUM_PKTS(x)      ((x)->h1.num_pkts)
-#define BLOCK_O2FP(x)          ((x)->h1.offset_to_first_pkt)
-#define BLOCK_LEN(x)           ((x)->h1.blk_len)
-#define BLOCK_SNUM(x)          ((x)->h1.seq_num)
-#define BLOCK_O2PRIV(x)                ((x)->offset_to_priv)
-#define BLOCK_PRIV(x)          ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x)))
-#define BLOCK_HDR_LEN          (ALIGN_8(sizeof(struct block_desc)))
-#define BLOCK_PLUS_PRIV(sz_pri)        (BLOCK_HDR_LEN + ALIGN_8((sz_pri)))
-
 #ifndef likely
 # define likely(x)             __builtin_expect(!!(x), 1)
 #endif
@@ -765,7 +750,7 @@ struct ring {
 static unsigned long packets_total = 0, bytes_total = 0;
 static sig_atomic_t sigint = 0;
 
-void sighandler(int num)
+static void sighandler(int num)
 {
        sigint = 1;
 }
@@ -774,6 +759,8 @@ static int setup_socket(struct ring *ring, char *netdev)
 {
        int err, i, fd, v = TPACKET_V3;
        struct sockaddr_ll ll;
+       unsigned int blocksiz = 1 << 22, framesiz = 1 << 11;
+       unsigned int blocknum = 64;
 
        fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
        if (fd < 0) {
@@ -788,13 +775,12 @@ static int setup_socket(struct ring *ring, char *netdev)
        }
 
        memset(&ring->req, 0, sizeof(ring->req));
-       ring->req.tp_block_size = BLOCK_SIZE;
-       ring->req.tp_frame_size = FRAME_SIZE;
-       ring->req.tp_block_nr = NUM_BLOCKS;
-       ring->req.tp_frame_nr = NUM_FRAMES;
-       ring->req.tp_retire_blk_tov = BLOCK_RETIRE_TOV_IN_MS;
-       ring->req.tp_sizeof_priv = BLOCK_PRIV_AREA_SZ;
-       ring->req.tp_feature_req_word |= TP_FT_REQ_FILL_RXHASH;
+       ring->req.tp_block_size = blocksiz;
+       ring->req.tp_frame_size = framesiz;
+       ring->req.tp_block_nr = blocknum;
+       ring->req.tp_frame_nr = (blocksiz * blocknum) / framesiz;
+       ring->req.tp_retire_blk_tov = 60;
+       ring->req.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH;
 
        err = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &ring->req,
                         sizeof(ring->req));
@@ -804,8 +790,7 @@ static int setup_socket(struct ring *ring, char *netdev)
        }
 
        ring->map = mmap(NULL, ring->req.tp_block_size * ring->req.tp_block_nr,
-                        PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED,
-                        fd, 0);
+                        PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, 0);
        if (ring->map == MAP_FAILED) {
                perror("mmap");
                exit(1);
@@ -835,58 +820,6 @@ static int setup_socket(struct ring *ring, char *netdev)
        return fd;
 }
 
-#ifdef __checked
-static uint64_t prev_block_seq_num = 0;
-
-void assert_block_seq_num(struct block_desc *pbd)
-{
-       if (unlikely(prev_block_seq_num + 1 != BLOCK_SNUM(pbd))) {
-               printf("prev_block_seq_num:%"PRIu64", expected seq:%"PRIu64" != "
-                      "actual seq:%"PRIu64"\n", prev_block_seq_num,
-                      prev_block_seq_num + 1, (uint64_t) BLOCK_SNUM(pbd));
-               exit(1);
-       }
-
-       prev_block_seq_num = BLOCK_SNUM(pbd);
-}
-
-static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num)
-{
-       if (BLOCK_NUM_PKTS(pbd)) {
-               if (unlikely(bytes != BLOCK_LEN(pbd))) {
-                       printf("block:%u with %upackets, expected len:%u != actual len:%u\n",
-                              block_num, BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd));
-                       exit(1);
-               }
-       } else {
-               if (unlikely(BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ))) {
-                       printf("block:%u, expected len:%lu != actual len:%u\n",
-                              block_num, BLOCK_HDR_LEN, BLOCK_LEN(pbd));
-                       exit(1);
-               }
-       }
-}
-
-static void assert_block_header(struct block_desc *pbd, const int block_num)
-{
-       uint32_t block_status = BLOCK_STATUS(pbd);
-
-       if (unlikely((block_status & TP_STATUS_USER) == 0)) {
-               printf("block:%u, not in TP_STATUS_USER\n", block_num);
-               exit(1);
-       }
-
-       assert_block_seq_num(pbd);
-}
-#else
-static inline void assert_block_header(struct block_desc *pbd, const int block_num)
-{
-}
-static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num)
-{
-}
-#endif
-
 static void display(struct tpacket3_hdr *ppd)
 {
        struct ethhdr *eth = (struct ethhdr *) ((uint8_t *) ppd + ppd->tp_mac);
@@ -916,37 +849,27 @@ static void display(struct tpacket3_hdr *ppd)
 
 static void walk_block(struct block_desc *pbd, const int block_num)
 {
-       int num_pkts = BLOCK_NUM_PKTS(pbd), i;
+       int num_pkts = pbd->h1.num_pkts, i;
        unsigned long bytes = 0;
-       unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ);
        struct tpacket3_hdr *ppd;
 
-       assert_block_header(pbd, block_num);
-
-       ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd));
+       ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd +
+                                      pbd->h1.offset_to_first_pkt);
        for (i = 0; i < num_pkts; ++i) {
                bytes += ppd->tp_snaplen;
-               if (ppd->tp_next_offset)
-                       bytes_with_padding += ppd->tp_next_offset;
-               else
-                       bytes_with_padding += ALIGN_8(ppd->tp_snaplen + ppd->tp_mac);
-
                display(ppd);
 
-               ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset);
-               __sync_synchronize();
+               ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd +
+                                              ppd->tp_next_offset);
        }
 
-       assert_block_len(pbd, bytes_with_padding, block_num);
-
        packets_total += num_pkts;
        bytes_total += bytes;
 }
 
-void flush_block(struct block_desc *pbd)
+static void flush_block(struct block_desc *pbd)
 {
-       BLOCK_STATUS(pbd) = TP_STATUS_KERNEL;
-       __sync_synchronize();
+       pbd->h1.block_status = TP_STATUS_KERNEL;
 }
 
 static void teardown_socket(struct ring *ring, int fd)
@@ -962,7 +885,7 @@ int main(int argc, char **argp)
        socklen_t len;
        struct ring ring;
        struct pollfd pfd;
-       unsigned int block_num = 0;
+       unsigned int block_num = 0, blocks = 64;
        struct block_desc *pbd;
        struct tpacket_stats_v3 stats;
 
@@ -984,15 +907,15 @@ int main(int argc, char **argp)
 
        while (likely(!sigint)) {
                pbd = (struct block_desc *) ring.rd[block_num].iov_base;
-retry_block:
-               if ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0) {
+
+               if ((pbd->h1.block_status & TP_STATUS_USER) == 0) {
                        poll(&pfd, 1, -1);
-                       goto retry_block;
+                       continue;
                }
 
                walk_block(pbd, block_num);
                flush_block(pbd);
-               block_num = (block_num + 1) % NUM_BLOCKS;
+               block_num = (block_num + 1) % blocks;
        }
 
        len = sizeof(stats);
index 579994a..ca6977f 100644 (file)
@@ -163,6 +163,64 @@ and unnecessary. If there are fewer hardware queues than CPUs, then
 RPS might be beneficial if the rps_cpus for each queue are the ones that
 share the same memory domain as the interrupting CPU for that queue.
 
+==== RPS Flow Limit
+
+RPS scales kernel receive processing across CPUs without introducing
+reordering. The trade-off to sending all packets from the same flow
+to the same CPU is CPU load imbalance if flows vary in packet rate.
+In the extreme case a single flow dominates traffic. Especially on
+common server workloads with many concurrent connections, such
+behavior indicates a problem such as a misconfiguration or spoofed
+source Denial of Service attack.
+
+Flow Limit is an optional RPS feature that prioritizes small flows
+during CPU contention by dropping packets from large flows slightly
+ahead of those from small flows. It is active only when an RPS or RFS
+destination CPU approaches saturation.  Once a CPU's input packet
+queue exceeds half the maximum queue length (as set by sysctl
+net.core.netdev_max_backlog), the kernel starts a per-flow packet
+count over the last 256 packets. If a flow exceeds a set ratio (by
+default, half) of these packets when a new packet arrives, then the
+new packet is dropped. Packets from other flows are still only
+dropped once the input packet queue reaches netdev_max_backlog.
+No packets are dropped when the input packet queue length is below
+the threshold, so flow limit does not sever connections outright:
+even large flows maintain connectivity.
+
+== Interface
+
+Flow limit is compiled in by default (CONFIG_NET_FLOW_LIMIT), but not
+turned on. It is implemented for each CPU independently (to avoid lock
+and cache contention) and toggled per CPU by setting the relevant bit
+in sysctl net.core.flow_limit_cpu_bitmap. It exposes the same CPU
+bitmap interface as rps_cpus (see above) when called from procfs:
+
+ /proc/sys/net/core/flow_limit_cpu_bitmap
+
+Per-flow rate is calculated by hashing each packet into a hashtable
+bucket and incrementing a per-bucket counter. The hash function is
+the same that selects a CPU in RPS, but as the number of buckets can
+be much larger than the number of CPUs, flow limit has finer-grained
+identification of large flows and fewer false positives. The default
+table has 4096 buckets. This value can be modified through sysctl
+
+ net.core.flow_limit_table_len
+
+The value is only consulted when a new table is allocated. Modifying
+it does not update active tables.
+
+== Suggested Configuration
+
+Flow limit is useful on systems with many concurrent connections,
+where a single connection taking up 50% of a CPU indicates a problem.
+In such environments, enable the feature on all CPUs that handle
+network rx interrupts (as set in /proc/irq/N/smp_affinity).
+
+The feature depends on the input packet queue length to exceed
+the flow limit threshold (50%) + the flow history length (256).
+Setting net.core.netdev_max_backlog to either 1000 or 10000
+performed well in experiments.
+
 
 RFS: Receive Flow Steering
 ==========================
index b4038ff..9a8041d 100644 (file)
@@ -359,7 +359,7 @@ steps you should take:
 - OK, it's a driver problem.
 
    You need to generate a report.  Typically this is an email to the
-   maintainer and/or linux-net@vger.kernel.org.  The maintainer's
+   maintainer and/or netdev@vger.kernel.org.  The maintainer's
    email address will be in the driver source or in the MAINTAINERS file.
 
 - The contents of your report will vary a lot depending upon the
index 3af5ae6..3e8cb73 100644 (file)
@@ -121,6 +121,38 @@ IPv6 addresses:
        print a compressed IPv6 address as described by
        http://tools.ietf.org/html/rfc5952
 
+IPv4/IPv6 addresses (generic, with port, flowinfo, scope):
+
+       %pIS    1.2.3.4         or 0001:0002:0003:0004:0005:0006:0007:0008
+       %piS    001.002.003.004 or 00010002000300040005000600070008
+       %pISc   1.2.3.4         or 1:2:3:4:5:6:7:8
+       %pISpc  1.2.3.4:12345   or [1:2:3:4:5:6:7:8]:12345
+       %p[Ii]S[pfschnbl]
+
+       For printing an IP address without the need to distinguish whether it's
+       of type AF_INET or AF_INET6, a pointer to a valid 'struct sockaddr',
+       specified through 'IS' or 'iS', can be passed to this format specifier.
+
+       The additional 'p', 'f', and 's' specifiers are used to specify port
+       (IPv4, IPv6), flowinfo (IPv6) and scope (IPv6). Ports have a ':' prefix,
+       flowinfo a '/' and scope a '%', each followed by the actual value.
+
+       In case of an IPv6 address the compressed IPv6 address as described by
+       http://tools.ietf.org/html/rfc5952 is being used if the additional
+       specifier 'c' is given. The IPv6 address is surrounded by '[', ']' in
+       case of additional specifiers 'p', 'f' or 's' as suggested by
+       https://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-07
+
+       In case of IPv4 addresses, the additional 'h', 'n', 'b', and 'l'
+       specifiers can be used as well and are ignored in case of an IPv6
+       address.
+
+       Further examples:
+
+       %pISfc          1.2.3.4         or [1:2:3:4:5:6:7:8]/123456789
+       %pISsc          1.2.3.4         or [1:2:3:4:5:6:7:8]%1234567890
+       %pISpfc         1.2.3.4:12345   or [1:2:3:4:5:6:7:8]:12345/123456789
+
 UUID/GUID addresses:
 
        %pUb    00010203-0405-0607-0809-0a0b0c0d0e0f
index 6f1c201..d69e14c 100644 (file)
@@ -26,7 +26,7 @@ Table : Subdirectories in /proc/sys/net
  ipv4      IP version 4        x25        X.25 protocol
  ipx       IPX                 token-ring IBM token ring
  bridge    Bridging            decnet     DEC net
- ipv6      IP version 6
+ ipv6      IP version 6        tipc       TIPC
 ..............................................................................
 
 1. /proc/sys/net/core - Network core options
@@ -50,6 +50,29 @@ The maximum number of packets that kernel can handle on a NAPI interrupt,
 it's a Per-CPU variable.
 Default: 64
 
+low_latency_read
+----------------
+Low latency busy poll timeout for socket reads. (needs CONFIG_NET_LL_RX_POLL)
+Approximate time in us to busy loop waiting for packets on the device queue.
+This sets the default value of the SO_LL socket option.
+Can be set or overridden per socket by setting socket option SO_LL, which is
+the preferred method of enabling.
+If you need to enable the feature globally via sysctl, a value of 50 is recommended.
+Will increase power usage.
+Default: 0 (off)
+
+low_latency_poll
+----------------
+Low latency busy poll timeout for poll and select. (needs CONFIG_NET_LL_RX_POLL)
+Approximate time in us to busy loop waiting for events.
+Recommended value depends on the number of sockets you poll on.
+For several sockets 50, for several hundreds 100.
+For more than that you probably want to use epoll.
+Note that only sockets with SO_LL set will be busy polled, so you want to either
+selectively set SO_LL on those sockets or set sysctl.net.low_latency_read globally.
+Will increase power usage.
+Default: 0 (off)
+
 rmem_default
 ------------
 
@@ -93,8 +116,7 @@ netdev_budget
 
 Maximum number of packets taken from all interfaces in one polling cycle (NAPI
 poll). In one polling cycle interfaces which are registered to polling are
-probed in a round-robin manner. The limit of packets in one such probe can be
-set per-device via sysfs class/net/<device>/weight .
+probed in a round-robin manner.
 
 netdev_max_backlog
 ------------------
@@ -201,3 +223,18 @@ IPX.
 The /proc/net/ipx_route  table  holds  a list of IPX routes. For each route it
 gives the  destination  network, the router node (or Directly) and the network
 address of the router (or Connected) for internal networks.
+
+6. TIPC
+-------------------------------------------------------
+
+The TIPC protocol now has a tunable for the receive memory, similar to the
+tcp_rmem - i.e. a vector of 3 INTEGERs: (min, default, max)
+
+    # cat /proc/sys/net/tipc/tipc_rmem
+    4252725 34021800        68043600
+    #
+
+The max value is set to CONN_OVERLOAD_LIMIT, and the default and min values
+are scaled (shifted) versions of that same value.  Note that the min value
+is not at this point in time used in any meaningful way, but the triplet is
+preserved in order to be consistent with things like tcp_rmem.
index dcc75a9..36ecc26 100644 (file)
@@ -510,7 +510,7 @@ Specify "[Dd]efault" to request automatic configuration.  Autoconfiguration
 will select "node" order in following case.
 (1) if the DMA zone does not exist or
 (2) if the DMA zone comprises greater than 50% of the available memory or
-(3) if any node's DMA zone comprises greater than 60% of its local memory and
+(3) if any node's DMA zone comprises greater than 70% of its local memory and
     the amount of local memory is big enough.
 
 Otherwise, "zone" order will be selected. Default order is recommended unless
index 8785fb8..4a63953 100644 (file)
@@ -120,8 +120,8 @@ By default kernel tries to use huge zero page on read page fault.
 It's possible to disable huge zero page by writing 0 or enable it
 back by writing 1:
 
-echo 0 >/sys/kernel/mm/transparent_hugepage/khugepaged/use_zero_page
-echo 1 >/sys/kernel/mm/transparent_hugepage/khugepaged/use_zero_page
+echo 0 >/sys/kernel/mm/transparent_hugepage/use_zero_page
+echo 1 >/sys/kernel/mm/transparent_hugepage/use_zero_page
 
 khugepaged will be automatically started when
 transparent_hugepage/enabled is set to "always" or "madvise, and it'll
index 3840b6f..fc66d42 100644 (file)
@@ -657,9 +657,10 @@ Protocol:  2.08+
   uncompressed data should be determined using the standard magic
   numbers.  The currently supported compression formats are gzip
   (magic numbers 1F 8B or 1F 9E), bzip2 (magic number 42 5A), LZMA
-  (magic number 5D 00), and XZ (magic number FD 37).  The uncompressed
-  payload is currently always ELF (magic number 7F 45 4C 46).
-  
+  (magic number 5D 00), XZ (magic number FD 37), and LZ4 (magic number
+  02 21).  The uncompressed payload is currently always ELF (magic
+  number 7F 45 4C 46).
+
 Field name:    payload_length
 Type:          read
 Offset/size:   0x24c/4
index 97762ad..d7e0cfb 100644 (file)
@@ -180,6 +180,11 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs.git
 S:     Maintained
 F:     Documentation/filesystems/9p.txt
 F:     fs/9p/
+F:     net/9p/
+F:     include/net/9p/
+F:     include/uapi/linux/virtio_9p.h
+F:     include/trace/events/9p.h
+
 
 A8293 MEDIA DRIVER
 M:     Antti Palosaari <crope@iki.fi>
@@ -2327,6 +2332,11 @@ M:       Jaya Kumar <jayakumar.alsa@gmail.com>
 S:     Maintained
 F:     sound/pci/cs5535audio/
 
+CW1200 WLAN driver
+M:     Solomon Peachy <pizza@shaftnet.org>
+S:     Maintained
+F:     drivers/net/wireless/cw1200/
+
 CX18 VIDEO4LINUX DRIVER
 M:     Andy Walls <awalls@md.metrocast.net>
 L:     ivtv-devel@ivtvdriver.org (moderated for non-subscribers)
@@ -2725,12 +2735,14 @@ F:      include/drm/exynos*
 F:     include/uapi/drm/exynos*
 
 DRM DRIVERS FOR NVIDIA TEGRA
-M:     Thierry Reding <thierry.reding@avionic-design.de>
+M:     Thierry Reding <thierry.reding@gmail.com>
+M:     Terje Bergström <tbergstrom@nvidia.com>
 L:     dri-devel@lists.freedesktop.org
 L:     linux-tegra@vger.kernel.org
-T:     git git://gitorious.org/thierryreding/linux.git
+T:     git git://anongit.freedesktop.org/tegra/linux.git
 S:     Maintained
-F:     drivers/gpu/drm/tegra/
+F:     drivers/gpu/host1x/
+F:     include/uapi/drm/tegra_drm.h
 F:     Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt
 
 DSBR100 USB FM RADIO DRIVER
@@ -9263,6 +9275,13 @@ F:       Documentation/networking/z8530drv.txt
 F:     drivers/net/hamradio/*scc.c
 F:     drivers/net/hamradio/z8530.h
 
+ZBUD COMPRESSED PAGE ALLOCATOR
+M:     Seth Jennings <sjenning@linux.vnet.ibm.com>
+L:     linux-mm@kvack.org
+S:     Maintained
+F:     mm/zbud.c
+F:     include/linux/zbud.h
+
 ZD1211RW WIRELESS DRIVER
 M:     Daniel Drake <dsd@gentoo.org>
 M:     Ulrich Kunitz <kune@deine-taler.de>
@@ -9285,6 +9304,12 @@ M:       "Maciej W. Rozycki" <macro@linux-mips.org>
 S:     Maintained
 F:     drivers/tty/serial/zs.*
 
+ZSWAP COMPRESSED SWAP CACHING
+M:     Seth Jennings <sjenning@linux.vnet.ibm.com>
+L:     linux-mm@kvack.org
+S:     Maintained
+F:     mm/zswap.c
+
 THE REST
 M:     Linus Torvalds <torvalds@linux-foundation.org>
 L:     linux-kernel@vger.kernel.org
index eee6ea7..4885825 100644 (file)
@@ -81,4 +81,6 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* _UAPI_ASM_SOCKET_H */
index 318164c..0fd1f0d 100644 (file)
@@ -207,8 +207,10 @@ out_of_memory:
        }
        up_read(&mm->mmap_sem);
 
-       if (user_mode(regs))
-               do_group_exit(SIGKILL); /* This will never return */
+       if (user_mode(regs)) {
+               pagefault_out_of_memory();
+               return;
+       }
 
        goto no_context;
 
index 5ef7af0..0ac9be6 100644 (file)
@@ -41,6 +41,7 @@ config ARM
        select HAVE_IDE if PCI || ISA || PCMCIA
        select HAVE_IRQ_TIME_ACCOUNTING
        select HAVE_KERNEL_GZIP
+       select HAVE_KERNEL_LZ4
        select HAVE_KERNEL_LZMA
        select HAVE_KERNEL_LZO
        select HAVE_KERNEL_XZ
index f79a08e..47279aa 100644 (file)
@@ -6,6 +6,7 @@ piggy.gzip
 piggy.lzo
 piggy.lzma
 piggy.xzkern
+piggy.lz4
 vmlinux
 vmlinux.lds
 
index 48d0a44..7ac1610 100644 (file)
@@ -91,6 +91,7 @@ suffix_$(CONFIG_KERNEL_GZIP) = gzip
 suffix_$(CONFIG_KERNEL_LZO)  = lzo
 suffix_$(CONFIG_KERNEL_LZMA) = lzma
 suffix_$(CONFIG_KERNEL_XZ)   = xzkern
+suffix_$(CONFIG_KERNEL_LZ4)  = lz4
 
 # Borrowed libfdt files for the ATAG compatibility mode
 
@@ -115,7 +116,7 @@ targets       := vmlinux vmlinux.lds \
                 font.o font.c head.o misc.o $(OBJS)
 
 # Make sure files are removed during clean
-extra-y       += piggy.gzip piggy.lzo piggy.lzma piggy.xzkern \
+extra-y       += piggy.gzip piggy.lzo piggy.lzma piggy.xzkern piggy.lz4 \
                 lib1funcs.S ashldi3.S $(libfdt) $(libfdt_hdrs) \
                 hyp-stub.S
 
index 24b0475..bd245d3 100644 (file)
@@ -51,6 +51,10 @@ extern char * strstr(const char * s1, const char *s2);
 #include "../../../../lib/decompress_unxz.c"
 #endif
 
+#ifdef CONFIG_KERNEL_LZ4
+#include "../../../../lib/decompress_unlz4.c"
+#endif
+
 int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x))
 {
        return decompress(input, len, NULL, NULL, output, NULL, error);
diff --git a/arch/arm/boot/compressed/piggy.lz4.S b/arch/arm/boot/compressed/piggy.lz4.S
new file mode 100644 (file)
index 0000000..3d9a575
--- /dev/null
@@ -0,0 +1,6 @@
+       .section .piggydata,#alloc
+       .globl  input_data
+input_data:
+       .incbin "arch/arm/boot/compressed/piggy.lz4"
+       .globl  input_data_end
+input_data_end:
index 04feaf8..444b4ed 100644 (file)
 
 &cpsw_emac0 {
        phy_id = <&davinci_mdio>, <0>;
+       phy-mode = "mii";
 };
 
 &cpsw_emac1 {
        phy_id = <&davinci_mdio>, <1>;
+       phy-mode = "mii";
 };
 
 &mac {
index a16bb96..904ba83 100644 (file)
 
 &cpsw_emac0 {
        phy_id = <&davinci_mdio>, <0>;
+       phy-mode = "rgmii-txid";
 };
 
 &cpsw_emac1 {
        phy_id = <&davinci_mdio>, <1>;
+       phy-mode = "rgmii-txid";
 };
index 9e00eef..0c8ad17 100644 (file)
        pinctrl-0 = <&davinci_mdio_default>;
        pinctrl-1 = <&davinci_mdio_sleep>;
 };
+
+&cpsw_emac0 {
+       phy_id = <&davinci_mdio>, <0>;
+       phy-mode = "rgmii-txid";
+};
+
+&cpsw_emac1 {
+       phy_id = <&davinci_mdio>, <1>;
+       phy-mode = "rgmii-txid";
+};
index 3f0239e..dc259e8 100644 (file)
                samsung,i2c-max-bus-freq = <66000>;
 
                hdmiddc@50 {
-                       compatible = "samsung,exynos5-hdmiddc";
+                       compatible = "samsung,exynos4210-hdmiddc";
                        reg = <0x50>;
                };
        };
                samsung,i2c-max-bus-freq = <378000>;
 
                hdmiphy@38 {
-                       compatible = "samsung,exynos5-hdmiphy";
+                       compatible = "samsung,exynos4212-hdmiphy";
                        reg = <0x38>;
                };
        };
index 35a66de..49f18c2 100644 (file)
                samsung,i2c-max-bus-freq = <66000>;
 
                hdmiddc@50 {
-                       compatible = "samsung,exynos5-hdmiddc";
+                       compatible = "samsung,exynos4210-hdmiddc";
                        reg = <0x50>;
                };
        };
                samsung,i2c-max-bus-freq = <66000>;
 
                hdmiphy@38 {
-                       compatible = "samsung,exynos5-hdmiphy";
+                       compatible = "samsung,exynos4212-hdmiphy";
                        reg = <0x38>;
                };
        };
index 41cd625..ef57277 100644 (file)
        };
 
        hdmi {
-               compatible = "samsung,exynos5-hdmi";
+               compatible = "samsung,exynos4212-hdmi";
                reg = <0x14530000 0x70000>;
                interrupts = <0 95 0>;
                clocks = <&clock 333>, <&clock 136>, <&clock 137>,
        };
 
        mixer {
-               compatible = "samsung,exynos5-mixer";
+               compatible = "samsung,exynos5250-mixer";
                reg = <0x14450000 0x10000>;
                interrupts = <0 94 0>;
        };
index 3637bf3..1f0d38d 100644 (file)
                        can0: can@80032000 {
                                pinctrl-names = "default";
                                pinctrl-0 = <&can0_pins_a>;
+                               xceiver-supply = <&reg_can_3v3>;
                                status = "okay";
                        };
 
                        can1: can@80034000 {
                                pinctrl-names = "default";
                                pinctrl-0 = <&can1_pins_a>;
+                               xceiver-supply = <&reg_can_3v3>;
                                status = "okay";
                        };
                };
                        gpio = <&gpio3 30 0>;
                        enable-active-high;
                };
+
+               reg_can_3v3: can-3v3 {
+                       compatible = "regulator-fixed";
+                       regulator-name = "can-3v3";
+                       regulator-min-microvolt = <3300000>;
+                       regulator-max-microvolt = <3300000>;
+                       gpio = <&gpio2 13 0>;
+                       enable-active-high;
+               };
+
        };
 
        sound {
index 0e22a28..757c4cd 100644 (file)
        };
 
        soc@01c20000 {
+               emac: ethernet@01c0b000 {
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&emac_pins_a>;
+                       phy = <&phy1>;
+                       status = "okay";
+               };
+
+               mdio@01c0b080 {
+                       status = "okay";
+
+                       phy1: ethernet-phy@1 {
+                               reg = <1>;
+                       };
+               };
+
                pinctrl@01c20800 {
                        led_pins_cubieboard: led_pins@0 {
                                allwinner,pins = "PH20", "PH21";
index b9efac1..3514b37 100644 (file)
        };
 
        soc@01c20000 {
+               emac: ethernet@01c0b000 {
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&emac_pins_a>;
+                       phy = <&phy0>;
+                       status = "okay";
+               };
+
+               mdio@01c0b080 {
+                       phy-supply = <&reg_emac_3v3>;
+                       status = "okay";
+
+                       phy0: ethernet-phy@0 {
+                               reg = <0>;
+                       };
+               };
+
+               pio: pinctrl@01c20800 {
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&hackberry_hogs>;
+
+                       hackberry_hogs: hogs@0 {
+                               allwinner,pins = "PH19";
+                               allwinner,function = "gpio_out";
+                               allwinner,drive = <0>;
+                               allwinner,pull = <0>;
+                       };
+               };
+
                uart0: serial@01c28000 {
                        pinctrl-names = "default";
                        pinctrl-0 = <&uart0_pins_a>;
                        status = "okay";
                };
        };
+
+       regulators {
+               compatible = "simple-bus";
+
+               reg_emac_3v3: emac-3v3 {
+                       compatible = "regulator-fixed";
+                       regulator-name = "emac-3v3";
+                       regulator-min-microvolt = <3300000>;
+                       regulator-max-microvolt = <3300000>;
+                       enable-active-high;
+                       gpio = <&pio 7 19 0>;
+               };
+       };
 };
index 82e03d2..b2bd6e1 100644 (file)
                reg = <0x01c20000 0x300000>;
                ranges;
 
+               emac: ethernet@01c0b000 {
+                       compatible = "allwinner,sun4i-emac";
+                       reg = <0x01c0b000 0x1000>;
+                       interrupts = <55>;
+                       clocks = <&ahb_gates 17>;
+                       status = "disabled";
+               };
+
+               mdio@01c0b080 {
+                       compatible = "allwinner,sun4i-mdio";
+                       reg = <0x01c0b080 0x14>;
+                       status = "disabled";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+               };
+
                intc: interrupt-controller@01c20400 {
                        compatible = "allwinner,sun4i-ic";
                        reg = <0x01c20400 0x400>;
                                allwinner,drive = <0>;
                                allwinner,pull = <0>;
                        };
+
+                       emac_pins_a: emac0@0 {
+                               allwinner,pins = "PA0", "PA1", "PA2",
+                                               "PA3", "PA4", "PA5", "PA6",
+                                               "PA7", "PA8", "PA9", "PA10",
+                                               "PA11", "PA12", "PA13", "PA14",
+                                               "PA15", "PA16";
+                               allwinner,function = "emac";
+                               allwinner,drive = <0>;
+                               allwinner,pull = <0>;
+                       };
                };
 
                timer@01c20c00 {
index 2ac0ffb..a24c024 100644 (file)
@@ -286,3 +286,4 @@ CONFIG_SOC_OMAP5=y
 CONFIG_TI_DAVINCI_MDIO=y
 CONFIG_TI_DAVINCI_CPDMA=y
 CONFIG_TI_CPSW=y
+CONFIG_AT803X_PHY=y
index ed94b1a..423744b 100644 (file)
@@ -223,11 +223,12 @@ extern int iop3xx_get_init_atu(void);
 #ifndef __ASSEMBLY__
 
 #include <linux/types.h>
+#include <linux/reboot.h>
 
 void iop3xx_map_io(void);
 void iop_init_cp6_handler(void);
 void iop_init_time(unsigned long tickrate);
-void iop3xx_restart(char, const char *);
+void iop3xx_restart(enum reboot_mode, const char *);
 
 static inline u32 read_tmr0(void)
 {
index 75bf079..441efc4 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/types.h>
 
 #ifndef __ASSEMBLY__
+#include <linux/reboot.h>
 
 struct tag;
 struct meminfo;
@@ -43,7 +44,7 @@ struct machine_desc {
        unsigned char           reserve_lp0 :1; /* never has lp0        */
        unsigned char           reserve_lp1 :1; /* never has lp1        */
        unsigned char           reserve_lp2 :1; /* never has lp2        */
-       char                    restart_mode;   /* default restart mode */
+       enum reboot_mode        reboot_mode;    /* default restart mode */
        struct smp_operations   *smp;           /* SMP operations       */
        bool                    (*smp_init)(void);
        void                    (*fixup)(struct tag *, char **,
@@ -58,7 +59,7 @@ struct machine_desc {
 #ifdef CONFIG_MULTI_IRQ_HANDLER
        void                    (*handle_irq)(struct pt_regs *);
 #endif
-       void                    (*restart)(char, const char *);
+       void                    (*restart)(enum reboot_mode, const char *);
 };
 
 /*
index 21a23e3..a3d61ad 100644 (file)
@@ -6,11 +6,12 @@
 #include <linux/compiler.h>
 #include <linux/linkage.h>
 #include <linux/irqflags.h>
+#include <linux/reboot.h>
 
 extern void cpu_init(void);
 
 void soft_restart(unsigned long);
-extern void (*arm_pm_restart)(char str, const char *cmd);
+extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
 extern void (*arm_pm_idle)(void);
 
 #define UDBG_UNDEFINED (1 << 0)
index 7f1efcd..d3ca4f6 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/hw_breakpoint.h>
 #include <linux/cpuidle.h>
 #include <linux/leds.h>
+#include <linux/reboot.h>
 
 #include <asm/cacheflush.h>
 #include <asm/idmap.h>
@@ -113,7 +114,7 @@ void soft_restart(unsigned long addr)
        BUG();
 }
 
-static void null_restart(char mode, const char *cmd)
+static void null_restart(enum reboot_mode reboot_mode, const char *cmd)
 {
 }
 
@@ -123,7 +124,7 @@ static void null_restart(char mode, const char *cmd)
 void (*pm_power_off)(void);
 EXPORT_SYMBOL(pm_power_off);
 
-void (*arm_pm_restart)(char str, const char *cmd) = null_restart;
+void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd) = null_restart;
 EXPORT_SYMBOL_GPL(arm_pm_restart);
 
 /*
@@ -175,16 +176,6 @@ void arch_cpu_idle(void)
                default_idle();
 }
 
-static char reboot_mode = 'h';
-
-int __init reboot_setup(char *str)
-{
-       reboot_mode = str[0];
-       return 1;
-}
-
-__setup("reboot=", reboot_setup);
-
 /*
  * Called by kexec, immediately prior to machine_kexec().
  *
index 2bc1514..0dd3b79 100644 (file)
@@ -886,20 +886,12 @@ long arch_ptrace(struct task_struct *child, long request,
 
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
                case PTRACE_GETHBPREGS:
-                       if (ptrace_get_breakpoints(child) < 0)
-                               return -ESRCH;
-
                        ret = ptrace_gethbpregs(child, addr,
                                                (unsigned long __user *)data);
-                       ptrace_put_breakpoints(child);
                        break;
                case PTRACE_SETHBPREGS:
-                       if (ptrace_get_breakpoints(child) < 0)
-                               return -ESRCH;
-
                        ret = ptrace_sethbpregs(child, addr,
                                                (unsigned long __user *)data);
-                       ptrace_put_breakpoints(child);
                        break;
 #endif
 
index 9b65327..63af9a7 100644 (file)
@@ -74,7 +74,7 @@ __setup("fpe=", fpe_setup);
 
 extern void paging_init(struct machine_desc *desc);
 extern void sanity_check_meminfo(void);
-extern void reboot_setup(char *str);
+extern enum reboot_mode reboot_mode;
 extern void setup_dma_zone(struct machine_desc *desc);
 
 unsigned int processor_id;
@@ -861,8 +861,8 @@ void __init setup_arch(char **cmdline_p)
 
        setup_dma_zone(mdesc);
 
-       if (mdesc->restart_mode)
-               reboot_setup(&mdesc->restart_mode);
+       if (mdesc->reboot_mode != REBOOT_HARD)
+               reboot_mode = mdesc->reboot_mode;
 
        init_mm.start_code = (unsigned long) _text;
        init_mm.end_code   = (unsigned long) _etext;
index 9eb5743..4aad93d 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/reboot.h>
 
 #include <asm/irq.h>
 #include <asm/mach/arch.h>
@@ -304,7 +305,7 @@ static void at91rm9200_idle(void)
        at91_pmc_write(AT91_PMC_SCDR, AT91_PMC_PCK);
 }
 
-static void at91rm9200_restart(char mode, const char *cmd)
+static void at91rm9200_restart(enum reboot_mode reboot_mode, const char *cmd)
 {
        /*
         * Perform a hardware reset with the use of the Watchdog timer.
index f6de36a..dc6e2f5 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <linux/clkdev.h>
 #include <linux/of.h>
+#include <linux/reboot.h>
 
  /* Map io */
 extern void __init at91_map_io(void);
@@ -60,8 +61,8 @@ extern void at91sam9_idle(void);
 
 /* reset */
 extern void at91_ioremap_rstc(u32 base_addr);
-extern void at91sam9_alt_restart(char, const char *);
-extern void at91sam9g45_restart(char, const char *);
+extern void at91sam9_alt_restart(enum reboot_mode, const char *);
+extern void at91sam9g45_restart(enum reboot_mode, const char *);
 
 /* shutdown */
 extern void at91_ioremap_shdwc(u32 base_addr);
index 740fa9e..40686d7 100644 (file)
@@ -53,7 +53,7 @@ static void bcm2835_setup_restart(void)
        WARN(!wdt_regs, "failed to remap watchdog regs");
 }
 
-static void bcm2835_restart(char mode, const char *cmd)
+static void bcm2835_restart(enum reboot_mode mode, const char *cmd)
 {
        u32 val;
 
@@ -91,7 +91,7 @@ static void bcm2835_power_off(void)
        writel_relaxed(val, wdt_regs + PM_RSTS);
 
        /* Continue with normal reset mechanism */
-       bcm2835_restart(0, "");
+       bcm2835_restart(REBOOT_HARD, "");
 }
 
 static struct map_desc io_map __initdata = {
index f6d1746..4ca2f3c 100644 (file)
@@ -384,7 +384,7 @@ void __init clps711x_timer_init(void)
        setup_irq(IRQ_TC2OI, &clps711x_timer_irq);
 }
 
-void clps711x_restart(char mode, const char *cmd)
+void clps711x_restart(enum reboot_mode mode, const char *cmd)
 {
        soft_restart(0);
 }
index 2a22f4c..9a6767b 100644 (file)
@@ -4,6 +4,8 @@
  * Common bits.
  */
 
+#include <linux/reboot.h>
+
 #define CLPS711X_NR_IRQS       (33)
 #define CLPS711X_NR_GPIO       (4 * 8 + 3)
 #define CLPS711X_GPIO(prt, bit)        ((prt) * 8 + (bit))
@@ -12,5 +14,5 @@ extern void clps711x_map_io(void);
 extern void clps711x_init_irq(void);
 extern void clps711x_timer_init(void);
 extern void clps711x_handle_irq(struct pt_regs *regs);
-extern void clps711x_restart(char mode, const char *cmd);
+extern void clps711x_restart(enum reboot_mode mode, const char *cmd);
 extern void clps711x_init_early(void);
index b23b17b..5218b61 100644 (file)
@@ -11,6 +11,8 @@
 #ifndef __CNS3XXX_CORE_H
 #define __CNS3XXX_CORE_H
 
+#include <linux/reboot.h>
+
 extern void cns3xxx_timer_init(void);
 
 #ifdef CONFIG_CACHE_L2X0
@@ -22,6 +24,6 @@ static inline void cns3xxx_l2x0_init(void) {}
 void __init cns3xxx_map_io(void);
 void __init cns3xxx_init_irq(void);
 void cns3xxx_power_off(void);
-void cns3xxx_restart(char, const char *);
+void cns3xxx_restart(enum reboot_mode, const char *);
 
 #endif /* __CNS3XXX_CORE_H */
index 79e3d47..fb38c72 100644 (file)
@@ -89,7 +89,7 @@ void cns3xxx_pwr_soft_rst(unsigned int block)
 }
 EXPORT_SYMBOL(cns3xxx_pwr_soft_rst);
 
-void cns3xxx_restart(char mode, const char *cmd)
+void cns3xxx_restart(enum reboot_mode mode, const char *cmd)
 {
        /*
         * To reset, we hit the on-board reset register
index eb254fe..71a46a3 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/serial_8250.h>
 #include <linux/ahci_platform.h>
 #include <linux/clk.h>
+#include <linux/reboot.h>
 
 #include <mach/cputype.h>
 #include <mach/common.h>
@@ -366,7 +367,7 @@ static struct platform_device da8xx_wdt_device = {
        .resource       = da8xx_watchdog_resources,
 };
 
-void da8xx_restart(char mode, const char *cmd)
+void da8xx_restart(enum reboot_mode mode, const char *cmd)
 {
        struct device *dev;
 
index 90b83d0..111573c 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
+#include <linux/reboot.h>
 
 #include <mach/hardware.h>
 #include <linux/platform_data/i2c-davinci.h>
@@ -307,7 +308,7 @@ struct platform_device davinci_wdt_device = {
        .resource       = wdt_resources,
 };
 
-void davinci_restart(char mode, const char *cmd)
+void davinci_restart(enum reboot_mode mode, const char *cmd)
 {
        davinci_watchdog_reset(&davinci_wdt_device);
 }
index b124b77..cce316b 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <linux/compiler.h>
 #include <linux/types.h>
+#include <linux/reboot.h>
 
 extern void davinci_timer_init(void);
 
@@ -81,7 +82,7 @@ extern struct davinci_soc_info davinci_soc_info;
 
 extern void davinci_common_init(struct davinci_soc_info *soc_info);
 extern void davinci_init_ide(void);
-void davinci_restart(char mode, const char *cmd);
+void davinci_restart(enum reboot_mode mode, const char *cmd);
 void davinci_init_late(void);
 
 #ifdef CONFIG_DAVINCI_RESET_CLOCKS
index 3c797e2..7b41a5e 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/davinci_emac.h>
 #include <linux/spi/spi.h>
 #include <linux/platform_data/davinci_asp.h>
+#include <linux/reboot.h>
 #include <linux/videodev2.h>
 
 #include <mach/serial.h>
@@ -106,7 +107,7 @@ int da850_register_vpif_display
                        (struct vpif_display_config *display_config);
 int da850_register_vpif_capture
                        (struct vpif_capture_config *capture_config);
-void da8xx_restart(char mode, const char *cmd);
+void da8xx_restart(enum reboot_mode mode, const char *cmd);
 void da8xx_rproc_reserve_cma(void);
 int da8xx_register_rproc(void);
 
index 366e975..16314c6 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/serial_8250.h>
 #include <linux/input/matrix_keypad.h>
 #include <linux/mfd/ti_ssp.h>
+#include <linux/reboot.h>
 
 #include <linux/platform_data/mmc-davinci.h>
 #include <linux/platform_data/mtd-davinci.h>
@@ -54,7 +55,7 @@ extern struct platform_device tnetv107x_serial_device;
 extern void tnetv107x_init(void);
 extern void tnetv107x_devices_init(struct tnetv107x_device_info *);
 extern void tnetv107x_irq_init(void);
-void tnetv107x_restart(char mode, const char *cmd);
+void tnetv107x_restart(enum reboot_mode mode, const char *cmd);
 
 #endif
 
index 3b2a70d..4545667 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/io.h>
 #include <linux/err.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 
 #include <asm/mach/map.h>
 
@@ -730,7 +731,7 @@ static void tnetv107x_watchdog_reset(struct platform_device *pdev)
        __raw_writel(1, &regs->kick);
 }
 
-void tnetv107x_restart(char mode, const char *cmd)
+void tnetv107x_restart(enum reboot_mode mode, const char *cmd)
 {
        tnetv107x_watchdog_reset(&tnetv107x_wdt_device);
 }
index 2a9443d..00247c7 100644 (file)
@@ -381,7 +381,7 @@ void __init dove_init(void)
        dove_xor1_init();
 }
 
-void dove_restart(char mode, const char *cmd)
+void dove_restart(enum reboot_mode mode, const char *cmd)
 {
        /*
         * Enable soft reset to assert RSTOUTn.
index e863479..1d72522 100644 (file)
@@ -11,6 +11,8 @@
 #ifndef __ARCH_DOVE_COMMON_H
 #define __ARCH_DOVE_COMMON_H
 
+#include <linux/reboot.h>
+
 struct mv643xx_eth_platform_data;
 struct mv_sata_platform_data;
 
@@ -42,6 +44,6 @@ void dove_spi1_init(void);
 void dove_i2c_init(void);
 void dove_sdio0_init(void);
 void dove_sdio1_init(void);
-void dove_restart(char, const char *);
+void dove_restart(enum reboot_mode, const char *);
 
 #endif
index 8a53f34..68ac934 100644 (file)
@@ -311,7 +311,7 @@ static int __init ebsa110_init(void)
 
 arch_initcall(ebsa110_init);
 
-static void ebsa110_restart(char mode, const char *cmd)
+static void ebsa110_restart(enum reboot_mode mode, const char *cmd)
 {
        soft_restart(0x80000000);
 }
@@ -321,7 +321,6 @@ MACHINE_START(EBSA110, "EBSA110")
        .atag_offset    = 0x400,
        .reserve_lp0    = 1,
        .reserve_lp2    = 1,
-       .restart_mode   = 's',
        .map_io         = ebsa110_map_io,
        .init_early     = ebsa110_init_early,
        .init_irq       = ebsa110_init_irq,
index c49ed3d..df8612f 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/spi/spi.h>
 #include <linux/export.h>
 #include <linux/irqchip/arm-vic.h>
+#include <linux/reboot.h>
 
 #include <mach/hardware.h>
 #include <linux/platform_data/video-ep93xx.h>
@@ -921,7 +922,7 @@ void __init ep93xx_init_devices(void)
        gpio_led_register_device(-1, &ep93xx_led_data);
 }
 
-void ep93xx_restart(char mode, const char *cmd)
+void ep93xx_restart(enum reboot_mode mode, const char *cmd)
 {
        /*
         * Set then clear the SWRST bit to initiate a software reset
index a14e1b3..e256e0b 100644 (file)
@@ -4,6 +4,8 @@
 
 #ifndef __ASSEMBLY__
 
+#include <linux/reboot.h>
+
 struct i2c_gpio_platform_data;
 struct i2c_board_info;
 struct spi_board_info;
@@ -55,7 +57,7 @@ void ep93xx_ide_release_gpio(struct platform_device *pdev);
 void ep93xx_init_devices(void);
 extern void ep93xx_timer_init(void);
 
-void ep93xx_restart(char, const char *);
+void ep93xx_restart(enum reboot_mode, const char *);
 void ep93xx_init_late(void);
 
 #ifdef CONFIG_CRUNCH
index 2c655db..164685b 100644 (file)
@@ -285,12 +285,12 @@ static struct map_desc exynos5440_iodesc0[] __initdata = {
        },
 };
 
-void exynos4_restart(char mode, const char *cmd)
+void exynos4_restart(enum reboot_mode mode, const char *cmd)
 {
        __raw_writel(0x1, S5P_SWRESET);
 }
 
-void exynos5_restart(char mode, const char *cmd)
+void exynos5_restart(enum reboot_mode mode, const char *cmd)
 {
        struct device_node *np;
        u32 val;
index 38d45fd..3e156bc 100644 (file)
@@ -12,6 +12,7 @@
 #ifndef __ARCH_ARM_MACH_EXYNOS_COMMON_H
 #define __ARCH_ARM_MACH_EXYNOS_COMMON_H
 
+#include <linux/reboot.h>
 #include <linux/of.h>
 
 void mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1);
@@ -20,8 +21,8 @@ extern unsigned long xxti_f, xusbxti_f;
 
 struct map_desc;
 void exynos_init_io(void);
-void exynos4_restart(char mode, const char *cmd);
-void exynos5_restart(char mode, const char *cmd);
+void exynos4_restart(enum reboot_mode mode, const char *cmd);
+void exynos5_restart(enum reboot_mode mode, const char *cmd);
 void exynos_init_late(void);
 
 /* ToDo: remove these after migrating legacy exynos4 platforms to dt */
index 6987a09..9669cc0 100644 (file)
@@ -86,7 +86,7 @@ fixup_cats(struct tag *tags, char **cmdline, struct meminfo *mi)
 MACHINE_START(CATS, "Chalice-CATS")
        /* Maintainer: Philip Blundell */
        .atag_offset    = 0x100,
-       .restart_mode   = 's',
+       .reboot_mode    = REBOOT_SOFT,
        .fixup          = fixup_cats,
        .map_io         = footbridge_map_io,
        .init_irq       = footbridge_init_irq,
index a42b369..2739ca2 100644 (file)
@@ -198,9 +198,9 @@ void __init footbridge_map_io(void)
        }
 }
 
-void footbridge_restart(char mode, const char *cmd)
+void footbridge_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode == 's') {
+       if (mode == REBOOT_SOFT) {
                /* Jump into the ROM */
                soft_restart(0x41000000);
        } else {
index a846e50..56607b3 100644 (file)
@@ -1,3 +1,4 @@
+#include <linux/reboot.h>
 
 extern void footbridge_timer_init(void);
 extern void isa_timer_init(void);
@@ -8,4 +9,4 @@ extern void footbridge_map_io(void);
 extern void footbridge_init_irq(void);
 
 extern void isa_init_irq(unsigned int irq);
-extern void footbridge_restart(char, const char *);
+extern void footbridge_restart(enum reboot_mode, const char *);
index 90ea23f..1fd2cf0 100644 (file)
@@ -634,9 +634,9 @@ fixup_netwinder(struct tag *tags, char **cmdline, struct meminfo *mi)
 #endif
 }
 
-static void netwinder_restart(char mode, const char *cmd)
+static void netwinder_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode == 's') {
+       if (mode == REBOOT_SOFT) {
                /* Jump into the ROM */
                soft_restart(0x41000000);
        } else {
index 3f65206..aea1ec5 100644 (file)
@@ -1,8 +1,10 @@
 #ifndef __HIGHBANK_CORE_H
 #define __HIGHBANK_CORE_H
 
+#include <linux/reboot.h>
+
 extern void highbank_set_cpu_jump(int cpu, void *jump_addr);
-extern void highbank_restart(char, const char *);
+extern void highbank_restart(enum reboot_mode, const char *);
 extern void __iomem *scu_base_addr;
 
 #ifdef CONFIG_PM_SLEEP
index 37d8384..2df5870 100644 (file)
  */
 #include <linux/io.h>
 #include <asm/proc-fns.h>
+#include <linux/reboot.h>
 
 #include "core.h"
 #include "sysregs.h"
 
-void highbank_restart(char mode, const char *cmd)
+void highbank_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode == 'h')
+       if (mode == REBOOT_HARD)
                highbank_set_pwr_hard_reset();
        else
                highbank_set_pwr_soft_reset();
index 60661a4..f546560 100644 (file)
@@ -108,7 +108,6 @@ config SOC_IMX25
        select ARCH_MXC_IOMUX_V3
        select COMMON_CLK
        select CPU_ARM926T
-       select HAVE_CAN_FLEXCAN if CAN
        select MXC_AVIC
 
 config SOC_IMX27
@@ -134,7 +133,6 @@ config SOC_IMX35
        select ARCH_MXC_IOMUX_V3
        select COMMON_CLK
        select CPU_V6K
-       select HAVE_CAN_FLEXCAN if CAN
        select HAVE_EPIT
        select MXC_AVIC
        select SMP_ON_UP if SMP
@@ -774,7 +772,6 @@ comment "Device tree only"
 
 config SOC_IMX53
        bool "i.MX53 support"
-       select HAVE_CAN_FLEXCAN if CAN
        select HAVE_IMX_SRC
        select IMX_HAVE_PLATFORM_IMX2_WDT
        select PINCTRL
@@ -797,7 +794,6 @@ config SOC_IMX6Q
        select CPU_V7
        select HAVE_ARM_SCU if SMP
        select HAVE_ARM_TWD if LOCAL_TIMERS
-       select HAVE_CAN_FLEXCAN if CAN
        select HAVE_IMX_ANATOP
        select HAVE_IMX_GPC
        select HAVE_IMX_MMDC
index ee78847..cb6c838 100644 (file)
@@ -11,6 +11,8 @@
 #ifndef __ASM_ARCH_MXC_COMMON_H__
 #define __ASM_ARCH_MXC_COMMON_H__
 
+#include <linux/reboot.h>
+
 struct platform_device;
 struct pt_regs;
 struct clk;
@@ -71,7 +73,7 @@ extern int mx53_clocks_init_dt(void);
 extern struct platform_device *mxc_register_gpio(char *name, int id,
        resource_size_t iobase, resource_size_t iosize, int irq, int irq_high);
 extern void mxc_set_cpu_type(unsigned int type);
-extern void mxc_restart(char, const char *);
+extern void mxc_restart(enum reboot_mode, const char *);
 extern void mxc_arch_reset_init(void __iomem *);
 extern void mxc_arch_reset_init_dt(void);
 extern int mx53_revision(void);
index 0d2922b..769563f 100644 (file)
@@ -13,10 +13,10 @@ extern const struct imx_fec_data imx25_fec_data;
        imx_add_fec(&imx25_fec_data, pdata)
 
 extern const struct imx_flexcan_data imx25_flexcan_data[];
-#define imx25_add_flexcan(id, pdata)   \
-       imx_add_flexcan(&imx25_flexcan_data[id], pdata)
-#define imx25_add_flexcan0(pdata)      imx25_add_flexcan(0, pdata)
-#define imx25_add_flexcan1(pdata)      imx25_add_flexcan(1, pdata)
+#define imx25_add_flexcan(id)  \
+       imx_add_flexcan(&imx25_flexcan_data[id])
+#define imx25_add_flexcan0()           imx25_add_flexcan(0)
+#define imx25_add_flexcan1()           imx25_add_flexcan(1)
 
 extern const struct imx_fsl_usb2_udc_data imx25_fsl_usb2_udc_data;
 #define imx25_add_fsl_usb2_udc(pdata)  \
index e2675f1..780d824 100644 (file)
@@ -17,10 +17,10 @@ extern const struct imx_fsl_usb2_udc_data imx35_fsl_usb2_udc_data;
        imx_add_fsl_usb2_udc(&imx35_fsl_usb2_udc_data, pdata)
 
 extern const struct imx_flexcan_data imx35_flexcan_data[];
-#define imx35_add_flexcan(id, pdata)   \
-       imx_add_flexcan(&imx35_flexcan_data[id], pdata)
-#define imx35_add_flexcan0(pdata)      imx35_add_flexcan(0, pdata)
-#define imx35_add_flexcan1(pdata)      imx35_add_flexcan(1, pdata)
+#define imx35_add_flexcan(id)  \
+       imx_add_flexcan(&imx35_flexcan_data[id])
+#define imx35_add_flexcan0()           imx35_add_flexcan(0)
+#define imx35_add_flexcan1()           imx35_add_flexcan(1)
 
 extern const struct imx_imx2_wdt_data imx35_imx2_wdt_data;
 #define imx35_add_imx2_wdt()       \
index 3dd2b1b..68c74fb 100644 (file)
@@ -4,7 +4,6 @@ config IMX_HAVE_PLATFORM_FEC
 
 config IMX_HAVE_PLATFORM_FLEXCAN
        bool
-       select HAVE_CAN_FLEXCAN if CAN
 
 config IMX_HAVE_PLATFORM_FSL_USB2_UDC
        bool
index 453e20b..c13b76b 100644 (file)
@@ -50,7 +50,6 @@ struct platform_device *__init imx_add_fec(
                const struct imx_fec_data *data,
                const struct fec_platform_data *pdata);
 
-#include <linux/can/platform/flexcan.h>
 struct imx_flexcan_data {
        int id;
        resource_size_t iobase;
@@ -58,8 +57,7 @@ struct imx_flexcan_data {
        resource_size_t irq;
 };
 struct platform_device *__init imx_add_flexcan(
-               const struct imx_flexcan_data *data,
-               const struct flexcan_platform_data *pdata);
+               const struct imx_flexcan_data *data);
 
 #include <linux/fsl_devices.h>
 struct imx_fsl_usb2_udc_data {
index 1078bf0..55d61ea 100644 (file)
@@ -38,8 +38,7 @@ const struct imx_flexcan_data imx35_flexcan_data[] __initconst = {
 #endif /* ifdef CONFIG_SOC_IMX35 */
 
 struct platform_device *__init imx_add_flexcan(
-               const struct imx_flexcan_data *data,
-               const struct flexcan_platform_data *pdata)
+               const struct imx_flexcan_data *data)
 {
        struct resource res[] = {
                {
@@ -54,5 +53,5 @@ struct platform_device *__init imx_add_flexcan(
        };
 
        return imx_add_platform_device("flexcan", data->id,
-                       res, ARRAY_SIZE(res), pdata, sizeof(*pdata));
+                       res, ARRAY_SIZE(res), NULL, 0);
 }
index e2b70f4..e77cc3a 100644 (file)
@@ -279,7 +279,7 @@ void __init eukrea_mbimxsd25_baseboard_init(void)
        imx25_add_imx_fb(&eukrea_mximxsd_fb_pdata);
        imx25_add_imx_ssi(0, &eukrea_mbimxsd_ssi_pdata);
 
-       imx25_add_flexcan1(NULL);
+       imx25_add_flexcan1();
        imx25_add_sdhci_esdhc_imx(0, &sd1_pdata);
 
        gpio_request(GPIO_LED1, "LED1");
index 5a2d5ef..14d6c82 100644 (file)
@@ -287,7 +287,7 @@ void __init eukrea_mbimxsd35_baseboard_init(void)
 
        imx35_add_imx_ssi(0, &eukrea_mbimxsd_ssi_pdata);
 
-       imx35_add_flexcan1(NULL);
+       imx35_add_flexcan1();
        imx35_add_sdhci_esdhc_imx(0, &sd1_pdata);
 
        gpio_request(GPIO_LED1, "LED1");
index f596522..7be13f8 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/of_platform.h>
 #include <linux/opp.h>
 #include <linux/phy.h>
+#include <linux/reboot.h>
 #include <linux/regmap.h>
 #include <linux/micrel_phy.h>
 #include <linux/mfd/syscon.h>
@@ -67,7 +68,7 @@ static void __init imx6q_init_revision(void)
        mxc_set_cpu_type(rev >> 16 & 0xff);
 }
 
-static void imx6q_restart(char mode, const char *cmd)
+static void imx6q_restart(enum reboot_mode mode, const char *cmd)
 {
        struct device_node *np;
        void __iomem *wdog_base;
index 8bcda68..13490c2 100644 (file)
@@ -249,7 +249,7 @@ static void __init mx25pdk_init(void)
        imx25_add_imx_i2c0(&mx25_3ds_i2c0_data);
 
        gpio_request_one(MX25PDK_CAN_PWDN, GPIOF_OUT_INIT_LOW, "can-pwdn");
-       imx25_add_flexcan0(NULL);
+       imx25_add_flexcan0();
 }
 
 static void __init mx25pdk_timer_init(void)
index 8ed533f..b726cb1 100644 (file)
@@ -385,7 +385,7 @@ static void __init pcm043_init(void)
        if (!otg_mode_host)
                imx35_add_fsl_usb2_udc(&otg_device_pdata);
 
-       imx35_add_flexcan1(NULL);
+       imx35_add_flexcan1();
        imx35_add_sdhci_esdhc_imx(0, &sd1_pdata);
 }
 
index 7cdc79a..6fe81bb 100644 (file)
@@ -37,7 +37,7 @@ static struct clk *wdog_clk;
 /*
  * Reset the system. It is called by machine_restart().
  */
-void mxc_restart(char mode, const char *cmd)
+void mxc_restart(enum reboot_mode mode, const char *cmd)
 {
        unsigned int wcr_enable;
 
index 7251665..ad0ac55 100644 (file)
@@ -1,7 +1,8 @@
+#include <linux/reboot.h>
 #include <linux/amba/serial.h>
 extern struct amba_pl010_data ap_uart_data;
 void integrator_init_early(void);
 int integrator_init(bool is_cp);
 void integrator_reserve(void);
-void integrator_restart(char, const char *);
+void integrator_restart(enum reboot_mode, const char *);
 void integrator_init_sysfs(struct device *parent, u32 id);
index 81461d2..4cdfd73 100644 (file)
@@ -124,7 +124,7 @@ void __init integrator_reserve(void)
 /*
  * To reset, we hit the on-board reset register in the system FPGA
  */
-void integrator_restart(char mode, const char *cmd)
+void integrator_restart(enum reboot_mode mode, const char *cmd)
 {
        cm_control(CM_CTRL_RESET, CM_CTRL_RESET);
 }
index 7480f58..17b4027 100644 (file)
@@ -2,6 +2,9 @@
 #define _IOP13XX_HW_H_
 
 #ifndef __ASSEMBLY__
+
+#include <linux/reboot.h>
+
 /* The ATU offsets can change based on the strapping */
 extern u32 iop13xx_atux_pmmr_offset;
 extern u32 iop13xx_atue_pmmr_offset;
@@ -11,7 +14,7 @@ void iop13xx_map_io(void);
 void iop13xx_platform_init(void);
 void iop13xx_add_tpmi_devices(void);
 void iop13xx_init_irq(void);
-void iop13xx_restart(char, const char *);
+void iop13xx_restart(enum reboot_mode, const char *);
 
 /* CPUID CP6 R0 Page 0 */
 static inline int iop13xx_cpu_id(void)
index 1c5bd76..96e6c7a 100644 (file)
@@ -594,7 +594,7 @@ __setup("iop13xx_init_adma", iop13xx_init_adma_setup);
 __setup("iop13xx_init_uart", iop13xx_init_uart_setup);
 __setup("iop13xx_init_i2c", iop13xx_init_i2c_setup);
 
-void iop13xx_restart(char mode, const char *cmd)
+void iop13xx_restart(enum reboot_mode mode, const char *cmd)
 {
        /*
         * Reset the internal bus (warning both cores are reset)
index ea0984a..0691443 100644 (file)
@@ -286,7 +286,7 @@ static void n2100_power_off(void)
                ;
 }
 
-static void n2100_restart(char mode, const char *cmd)
+static void n2100_restart(enum reboot_mode mode, const char *cmd)
 {
        gpio_line_set(N2100_HARDWARE_RESET, GPIO_LOW);
        gpio_line_config(N2100_HARDWARE_RESET, GPIO_OUT);
index 1f6c1fb..5327dec 100644 (file)
@@ -531,9 +531,9 @@ static void __init ixp4xx_clockevent_init(void)
                                        0xf, 0xfffffffe);
 }
 
-void ixp4xx_restart(char mode, const char *cmd)
+void ixp4xx_restart(enum reboot_mode mode, const char *cmd)
 {
-       if ( 1 && mode == 's') {
+       if ( 1 && mode == REBOOT_SOFT) {
                /* Jump into ROM at address 0 */
                soft_restart(0);
        } else {
index 5d413f8..686ef34 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/i2c.h>
 #include <linux/i2c-gpio.h>
 
+#include <mach/hardware.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/flash.h>
index db5afb6..4c4c6a6 100644 (file)
@@ -13,6 +13,8 @@
 
 #ifndef __ASSEMBLY__
 
+#include <linux/reboot.h>
+
 #include <asm/types.h>
 
 #ifndef        __ARMEB__
@@ -123,7 +125,7 @@ extern void ixp4xx_init_early(void);
 extern void ixp4xx_init_irq(void);
 extern void ixp4xx_sys_init(void);
 extern void ixp4xx_timer_init(void);
-extern void ixp4xx_restart(char, const char *);
+extern void ixp4xx_restart(enum reboot_mode, const char *);
 extern void ixp4xx_pci_preinit(void);
 struct pci_sys_data;
 extern int ixp4xx_setup(int nr, struct pci_sys_data *sys);
index 7c72c72..e9238b5 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/mv643xx_i2c.h>
 #include <linux/timex.h>
 #include <linux/kexec.h>
+#include <linux/reboot.h>
 #include <net/dsa.h>
 #include <asm/page.h>
 #include <asm/mach/map.h>
@@ -722,7 +723,7 @@ void __init kirkwood_init(void)
 #endif
 }
 
-void kirkwood_restart(char mode, const char *cmd)
+void kirkwood_restart(enum reboot_mode mode, const char *cmd)
 {
        /*
         * Enable soft reset to assert RSTOUTn.
index 1c09f3f..fcf3ba6 100644 (file)
@@ -11,6 +11,8 @@
 #ifndef __ARCH_KIRKWOOD_COMMON_H
 #define __ARCH_KIRKWOOD_COMMON_H
 
+#include <linux/reboot.h>
+
 struct dsa_platform_data;
 struct mv643xx_eth_platform_data;
 struct mv_sata_platform_data;
@@ -53,7 +55,7 @@ void kirkwood_audio_init(void);
 void kirkwood_cpuidle_init(void);
 void kirkwood_cpufreq_init(void);
 
-void kirkwood_restart(char, const char *);
+void kirkwood_restart(enum reboot_mode, const char *);
 void kirkwood_clk_init(void);
 
 /* board init functions for boards not fully converted to fdt */
index 6e97ce4..43253f8 100644 (file)
@@ -12,5 +12,5 @@
 
 extern __init void ks8695_map_io(void);
 extern __init void ks8695_init_irq(void);
-extern void ks8695_restart(char, const char *);
+extern void ks8695_restart(enum reboot_mode, const char *);
 extern void ks8695_timer_init(void);
index c272a38..426c976 100644 (file)
@@ -154,11 +154,11 @@ void __init ks8695_timer_init(void)
        setup_irq(KS8695_IRQ_TIMER1, &ks8695_timer_irq);
 }
 
-void ks8695_restart(char mode, const char *cmd)
+void ks8695_restart(enum reboot_mode reboot_mode, const char *cmd)
 {
        unsigned int reg;
 
-       if (mode == 's')
+       if (reboot_mode == REBOOT_SOFT)
                soft_restart(0);
 
        /* disable timer0 */
index 0d4db8c..d7aa54c 100644 (file)
@@ -207,11 +207,11 @@ void __init lpc32xx_map_io(void)
        iotable_init(lpc32xx_io_desc, ARRAY_SIZE(lpc32xx_io_desc));
 }
 
-void lpc23xx_restart(char mode, const char *cmd)
+void lpc23xx_restart(enum reboot_mode mode, const char *cmd)
 {
        switch (mode) {
-       case 's':
-       case 'h':
+       case REBOOT_SOFT:
+       case REBOOT_HARD:
                lpc32xx_watchdog_reset();
                break;
 
index e0b2606..1cd8853 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <mach/board.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 
 /*
  * Other arch specific structures and functions
@@ -29,7 +30,7 @@ extern void lpc32xx_timer_init(void);
 extern void __init lpc32xx_init_irq(void);
 extern void __init lpc32xx_map_io(void);
 extern void __init lpc32xx_serial_init(void);
-extern void lpc23xx_restart(char, const char *);
+extern void lpc23xx_restart(enum reboot_mode, const char *);
 
 
 /*
index 9292b79..c03b4ab 100644 (file)
@@ -47,7 +47,7 @@ void __init mmp_map_io(void)
        mmp_chip_id = __raw_readl(MMP_CHIPID);
 }
 
-void mmp_restart(char mode, const char *cmd)
+void mmp_restart(enum reboot_mode mode, const char *cmd)
 {
        soft_restart(0);
 }
index 0bdc50b..991d7e9 100644 (file)
@@ -1,10 +1,11 @@
+#include <linux/reboot.h>
 #define ARRAY_AND_SIZE(x)      (x), ARRAY_SIZE(x)
 
 extern void timer_init(int irq);
 
 extern void __init icu_init_irq(void);
 extern void __init mmp_map_io(void);
-extern void mmp_restart(char, const char *);
+extern void mmp_restart(enum reboot_mode, const char *);
 extern void __init pxa168_clk_init(void);
 extern void __init pxa910_clk_init(void);
 extern void __init mmp2_clk_init(void);
index 7ed1df2..459c2d0 100644 (file)
@@ -1,9 +1,11 @@
 #ifndef __ASM_MACH_PXA168_H
 #define __ASM_MACH_PXA168_H
 
+#include <linux/reboot.h>
+
 extern void pxa168_timer_init(void);
 extern void __init pxa168_init_irq(void);
-extern void pxa168_restart(char, const char *);
+extern void pxa168_restart(enum reboot_mode, const char *);
 extern void pxa168_clear_keypad_wakeup(void);
 
 #include <linux/i2c.h>
index a30dcf3..144e997 100644 (file)
@@ -172,7 +172,7 @@ int __init pxa168_add_usb_host(struct mv_usb_platform_data *pdata)
        return platform_device_register(&pxa168_device_usb_host);
 }
 
-void pxa168_restart(char mode, const char *cmd)
+void pxa168_restart(enum reboot_mode mode, const char *cmd)
 {
        soft_restart(0xffff0000);
 }
index 749a7f8..75062ef 100644 (file)
@@ -413,7 +413,7 @@ void __init mv78xx0_init(void)
        clk_init();
 }
 
-void mv78xx0_restart(char mode, const char *cmd)
+void mv78xx0_restart(enum reboot_mode mode, const char *cmd)
 {
        /*
         * Enable soft reset to assert RSTOUTn.
index 5e9485b..6889af2 100644 (file)
@@ -11,6 +11,8 @@
 #ifndef __ARCH_MV78XX0_COMMON_H
 #define __ARCH_MV78XX0_COMMON_H
 
+#include <linux/reboot.h>
+
 struct mv643xx_eth_platform_data;
 struct mv_sata_platform_data;
 
@@ -45,7 +47,7 @@ void mv78xx0_uart1_init(void);
 void mv78xx0_uart2_init(void);
 void mv78xx0_uart3_init(void);
 void mv78xx0_i2c_init(void);
-void mv78xx0_restart(char, const char *);
+void mv78xx0_restart(enum reboot_mode, const char *);
 
 extern void mv78xx0_timer_init(void);
 
index 98defd5..e366010 100644 (file)
@@ -17,7 +17,9 @@
 
 #define ARMADA_XP_MAX_CPUS 4
 
-void mvebu_restart(char mode, const char *cmd);
+#include <linux/reboot.h>
+
+void mvebu_restart(enum reboot_mode mode, const char *cmd);
 
 void armada_370_xp_init_irq(void);
 void armada_370_xp_handle_irq(struct pt_regs *regs);
index b8079df..f875124 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/init.h>
 #include <linux/of_address.h>
 #include <linux/io.h>
+#include <linux/reboot.h>
 
 static void __iomem *system_controller_base;
 
@@ -63,7 +64,7 @@ static struct of_device_id of_system_controller_table[] = {
        { /* end of list */ },
 };
 
-void mvebu_restart(char mode, const char *cmd)
+void mvebu_restart(enum reboot_mode mode, const char *cmd)
 {
        if (!system_controller_base) {
                pr_err("Cannot restart, system-controller not available: check the device tree\n");
index 616fe02..8cde9e0 100644 (file)
@@ -10,7 +10,6 @@ config SOC_IMX28
        select ARM_AMBA
        select ARM_CPU_SUSPEND if PM
        select CPU_ARM926T
-       select HAVE_CAN_FLEXCAN if CAN
        select PINCTRL_IMX28
 
 config ARCH_MXS
index 7fa611c..4ce27b5 100644 (file)
 #include <linux/clk/mxs.h>
 #include <linux/clkdev.h>
 #include <linux/clocksource.h>
-#include <linux/can/platform/flexcan.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/gpio.h>
 #include <linux/init.h>
 #include <linux/irqchip/mxs.h>
+#include <linux/reboot.h>
 #include <linux/micrel_phy.h>
 #include <linux/of_address.h>
 #include <linux/of_platform.h>
@@ -76,41 +76,6 @@ static inline void __mxs_togl(u32 mask, void __iomem *reg)
        __raw_writel(mask, reg + MXS_TOG_ADDR);
 }
 
-/*
- * MX28EVK_FLEXCAN_SWITCH is shared between both flexcan controllers
- */
-#define MX28EVK_FLEXCAN_SWITCH MXS_GPIO_NR(2, 13)
-
-static int flexcan0_en, flexcan1_en;
-
-static void mx28evk_flexcan_switch(void)
-{
-       if (flexcan0_en || flexcan1_en)
-               gpio_set_value(MX28EVK_FLEXCAN_SWITCH, 1);
-       else
-               gpio_set_value(MX28EVK_FLEXCAN_SWITCH, 0);
-}
-
-static void mx28evk_flexcan0_switch(int enable)
-{
-       flexcan0_en = enable;
-       mx28evk_flexcan_switch();
-}
-
-static void mx28evk_flexcan1_switch(int enable)
-{
-       flexcan1_en = enable;
-       mx28evk_flexcan_switch();
-}
-
-static struct flexcan_platform_data flexcan_pdata[2];
-
-static struct of_dev_auxdata mxs_auxdata_lookup[] __initdata = {
-       OF_DEV_AUXDATA("fsl,imx28-flexcan", 0x80032000, NULL, &flexcan_pdata[0]),
-       OF_DEV_AUXDATA("fsl,imx28-flexcan", 0x80034000, NULL, &flexcan_pdata[1]),
-       { /* sentinel */ }
-};
-
 #define OCOTP_WORD_OFFSET              0x20
 #define OCOTP_WORD_COUNT               0x20
 
@@ -270,15 +235,6 @@ static void __init imx28_evk_init(void)
        mxs_saif_clkmux_select(MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0);
 }
 
-static void __init imx28_evk_post_init(void)
-{
-       if (!gpio_request_one(MX28EVK_FLEXCAN_SWITCH, GPIOF_DIR_OUT,
-                             "flexcan-switch")) {
-               flexcan_pdata[0].transceiver_switch = mx28evk_flexcan0_switch;
-               flexcan_pdata[1].transceiver_switch = mx28evk_flexcan1_switch;
-       }
-}
-
 static int apx4devkit_phy_fixup(struct phy_device *phy)
 {
        phy->dev_flags |= MICREL_PHY_50MHZ_CLK;
@@ -484,13 +440,10 @@ static void __init mxs_machine_init(void)
                crystalfontz_init();
 
        of_platform_populate(NULL, of_default_bus_match_table,
-                            mxs_auxdata_lookup, parent);
+                            NULL, parent);
 
        if (of_machine_is_compatible("karo,tx28"))
                tx28_post_init();
-
-       if (of_machine_is_compatible("fsl,imx28-evk"))
-               imx28_evk_post_init();
 }
 
 #define MX23_CLKCTRL_RESET_OFFSET      0x120
@@ -500,7 +453,7 @@ static void __init mxs_machine_init(void)
 /*
  * Reset the system. It is called by machine_restart().
  */
-static void mxs_restart(char mode, const char *cmd)
+static void mxs_restart(enum reboot_mode mode, const char *cmd)
 {
        struct device_node *np;
        void __iomem *reset_addr;
index 1504b68..db25b0c 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/irqchip/arm-vic.h>
+#include <linux/reboot.h>
 #include <mach/hardware.h>
 #include <asm/mach/map.h>
 #include <mach/netx-regs.h>
@@ -187,7 +188,7 @@ static int __init netx_init(void)
 
 subsys_initcall(netx_init);
 
-void netx_restart(char mode, const char *cmd)
+void netx_restart(enum reboot_mode mode, const char *cmd)
 {
        writel(NETX_SYSTEM_RES_CR_FIRMW_RES_EN | NETX_SYSTEM_RES_CR_FIRMW_RES,
               NETX_SYSTEM_RES_CR);
index 768b26b..bb2ce47 100644 (file)
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include <linux/reboot.h>
+
 extern void __init netx_map_io(void);
 extern void __init netx_init_irq(void);
-extern void netx_restart(char, const char *);
+extern void netx_restart(enum reboot_mode, const char *);
 
 extern void netx_timer_init(void);
index 2df209e..13e0df9 100644 (file)
@@ -103,7 +103,7 @@ static void __init cpu8815_map_io(void)
        iotable_init(cpu8815_io_desc, ARRAY_SIZE(cpu8815_io_desc));
 }
 
-static void cpu8815_restart(char mode, const char *cmd)
+static void cpu8815_restart(enum reboot_mode mode, const char *cmd)
 {
        void __iomem *srcbase = ioremap(NOMADIK_SRC_BASE, SZ_4K);
 
index 6c116e1..4677a9c 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/serial_reg.h>
 #include <linux/smc91x.h>
 #include <linux/export.h>
+#include <linux/reboot.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -215,7 +216,7 @@ void voiceblue_wdt_ping(void)
        gpio_set_value(0, wdt_gpio_state);
 }
 
-static void voiceblue_restart(char mode, const char *cmd)
+static void voiceblue_restart(enum reboot_mode mode, const char *cmd)
 {
        /*
         * Workaround for 5912/1611b bug mentioned in sprz209d.pdf p. 28
index 14f7e99..abec019 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <linux/mtd/mtd.h>
 #include <linux/i2c-omap.h>
+#include <linux/reboot.h>
 
 #include <plat/i2c.h>
 
@@ -70,7 +71,7 @@ static inline int omap_serial_wakeup_init(void)
 void omap1_init_early(void);
 void omap1_init_irq(void);
 void omap1_init_late(void);
-void omap1_restart(char, const char *);
+void omap1_restart(enum reboot_mode, const char *);
 
 extern void __init omap_check_revision(void);
 
index 5eebd7e..72bf4bf 100644 (file)
@@ -3,6 +3,7 @@
  */
 #include <linux/kernel.h>
 #include <linux/io.h>
+#include <linux/reboot.h>
 
 #include <mach/hardware.h>
 
@@ -22,7 +23,7 @@
 #define OMAP_EXTWARM_RST_SRC_ID_SHIFT                  5
 
 
-void omap1_restart(char mode, const char *cmd)
+void omap1_restart(enum reboot_mode mode, const char *cmd)
 {
        /*
         * Workaround for 5912/1611b bug mentioned in sprz209d.pdf p. 28
index 88e4fa8..1eae962 100644 (file)
@@ -6,6 +6,7 @@
  * published by the Free Software Foundation.
  */
 #include <linux/kernel.h>
+#include <linux/reboot.h>
 
 #include "common.h"
 #include "prm-regbits-33xx.h"
@@ -19,7 +20,7 @@
  * Resets the SoC.  For @cmd, see the 'reboot' syscall in
  * kernel/sys.c.  No return value.
  */
-void am33xx_restart(char mode, const char *cmd)
+void am33xx_restart(enum reboot_mode mode, const char *cmd)
 {
        /* TODO: Handle mode and cmd if necessary */
 
index 72cab3f..dfcc182 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/i2c.h>
 #include <linux/i2c/twl.h>
 #include <linux/i2c-omap.h>
+#include <linux/reboot.h>
 
 #include <asm/proc-fns.h>
 
@@ -119,33 +120,33 @@ static inline void omap_soc_device_init(void)
 #endif
 
 #if defined(CONFIG_SOC_OMAP2420) || defined(CONFIG_SOC_OMAP2430)
-void omap2xxx_restart(char mode, const char *cmd);
+void omap2xxx_restart(enum reboot_mode mode, const char *cmd);
 #else
-static inline void omap2xxx_restart(char mode, const char *cmd)
+static inline void omap2xxx_restart(enum reboot_mode mode, const char *cmd)
 {
 }
 #endif
 
 #ifdef CONFIG_SOC_AM33XX
-void am33xx_restart(char mode, const char *cmd);
+void am33xx_restart(enum reboot_mode mode, const char *cmd);
 #else
-static inline void am33xx_restart(char mode, const char *cmd)
+static inline void am33xx_restart(enum reboot_mode mode, const char *cmd)
 {
 }
 #endif
 
 #ifdef CONFIG_ARCH_OMAP3
-void omap3xxx_restart(char mode, const char *cmd);
+void omap3xxx_restart(enum reboot_mode mode, const char *cmd);
 #else
-static inline void omap3xxx_restart(char mode, const char *cmd)
+static inline void omap3xxx_restart(enum reboot_mode mode, const char *cmd)
 {
 }
 #endif
 
 #if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5)
-void omap44xx_restart(char mode, const char *cmd);
+void omap44xx_restart(enum reboot_mode mode, const char *cmd);
 #else
-static inline void omap44xx_restart(char mode, const char *cmd)
+static inline void omap44xx_restart(enum reboot_mode mode, const char *cmd)
 {
 }
 #endif
index 719b716..68423e2 100644 (file)
@@ -31,7 +31,7 @@ static struct clk *reset_virt_prcm_set_ck, *reset_sys_ck;
  * Set the DPLL to bypass so that reboot completes successfully.  No
  * return value.
  */
-void omap2xxx_restart(char mode, const char *cmd)
+void omap2xxx_restart(enum reboot_mode mode, const char *cmd)
 {
        u32 rate;
 
index 923c582..5de2a0c 100644 (file)
@@ -12,6 +12,7 @@
  */
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/reboot.h>
 
 #include "iomap.h"
 #include "common.h"
@@ -28,7 +29,7 @@
  * Resets the SoC.  For @cmd, see the 'reboot' syscall in
  * kernel/sys.c.  No return value.
  */
-void omap3xxx_restart(char mode, const char *cmd)
+void omap3xxx_restart(enum reboot_mode mode, const char *cmd)
 {
        omap3_ctrl_write_boot_mode((cmd ? (u8)*cmd : 0));
        omap3xxx_prm_dpll3_reset(); /* never returns */
index 38cd3a6..5791143 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/export.h>
 #include <linux/irqchip/arm-gic.h>
 #include <linux/of_address.h>
+#include <linux/reboot.h>
 
 #include <asm/hardware/cache-l2x0.h>
 #include <asm/mach/map.h>
index f90e02e..41dfd7d 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <linux/types.h>
+#include <linux/reboot.h>
 #include "prminst44xx.h"
 
 /**
@@ -18,7 +19,7 @@
  * Resets the SoC.  For @cmd, see the 'reboot' syscall in
  * kernel/sys.c.  No return value.
  */
-void omap44xx_restart(char mode, const char *cmd)
+void omap44xx_restart(enum reboot_mode mode, const char *cmd)
 {
        /* XXX Should save 'cmd' into scratchpad for use after reboot */
        omap4_prminst_global_warm_sw_reset(); /* never returns */
index f8a6db9..b41599f 100644 (file)
@@ -347,7 +347,7 @@ void __init orion5x_init(void)
        orion5x_wdt_init();
 }
 
-void orion5x_restart(char mode, const char *cmd)
+void orion5x_restart(enum reboot_mode mode, const char *cmd)
 {
        /*
         * Enable and issue soft reset
index cdaa01f..a909afb 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef __ARCH_ORION5X_COMMON_H
 #define __ARCH_ORION5X_COMMON_H
 
+#include <linux/reboot.h>
+
 struct dsa_platform_data;
 struct mv643xx_eth_platform_data;
 struct mv_sata_platform_data;
@@ -29,7 +31,7 @@ void orion5x_spi_init(void);
 void orion5x_uart0_init(void);
 void orion5x_uart1_init(void);
 void orion5x_xor_init(void);
-void orion5x_restart(char, const char *);
+void orion5x_restart(enum reboot_mode, const char *);
 
 /*
  * PCIe/PCI functions.
index 24f4e14..6234977 100644 (file)
@@ -139,7 +139,7 @@ static struct mv_sata_platform_data lschl_sata_data = {
 
 static void lschl_power_off(void)
 {
-       orion5x_restart('h', NULL);
+       orion5x_restart(REBOOT_HARD, NULL);
 }
 
 /*****************************************************************************
index fc653bb..fe04c4b 100644 (file)
@@ -185,7 +185,7 @@ static struct mv_sata_platform_data ls_hgl_sata_data = {
 
 static void ls_hgl_power_off(void)
 {
-       orion5x_restart('h', NULL);
+       orion5x_restart(REBOOT_HARD, NULL);
 }
 
 
index 18e66e6..ca4dbe9 100644 (file)
@@ -185,7 +185,7 @@ static struct mv_sata_platform_data lsmini_sata_data = {
 
 static void lsmini_power_off(void)
 {
-       orion5x_restart('h', NULL);
+       orion5x_restart(REBOOT_HARD, NULL);
 }
 
 
index b13f51b..ec79fea 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_platform.h>
+#include <linux/reboot.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -63,7 +64,7 @@ static const char *picoxcell_dt_match[] = {
        NULL
 };
 
-static void picoxcell_wdt_restart(char mode, const char *cmd)
+static void picoxcell_wdt_restart(enum reboot_mode mode, const char *cmd)
 {
        /*
         * Configure the watchdog to reset with the shortest possible timeout
index 81135cd..a630485 100644 (file)
@@ -10,6 +10,8 @@
 #define __MACH_PRIMA2_COMMON_H__
 
 #include <linux/init.h>
+#include <linux/reboot.h>
+
 #include <asm/mach/time.h>
 #include <asm/exception.h>
 
@@ -22,7 +24,7 @@ extern void sirfsoc_cpu_die(unsigned int cpu);
 
 extern void __init sirfsoc_of_irq_init(void);
 extern void __init sirfsoc_of_clk_init(void);
-extern void sirfsoc_restart(char, const char *);
+extern void sirfsoc_restart(enum reboot_mode, const char *);
 extern asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs);
 
 #ifndef CONFIG_DEBUG_LL
index d5e0cbc..ccb5339 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/device.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/reboot.h>
 
 void __iomem *sirfsoc_rstc_base;
 static DEFINE_MUTEX(rstc_lock);
@@ -84,7 +85,7 @@ int sirfsoc_reset_device(struct device *dev)
 
 #define SIRFSOC_SYS_RST_BIT  BIT(31)
 
-void sirfsoc_restart(char mode, const char *cmd)
+void sirfsoc_restart(enum reboot_mode mode, const char *cmd)
 {
        writel(SIRFSOC_SYS_RST_BIT, sirfsoc_rstc_base);
 }
index a5b8fea..f162f1b 100644 (file)
@@ -663,16 +663,16 @@ static void corgi_poweroff(void)
                /* Green LED off tells the bootloader to halt */
                gpio_set_value(CORGI_GPIO_LED_GREEN, 0);
 
-       pxa_restart('h', NULL);
+       pxa_restart(REBOOT_HARD, NULL);
 }
 
-static void corgi_restart(char mode, const char *cmd)
+static void corgi_restart(enum reboot_mode mode, const char *cmd)
 {
        if (!machine_is_corgi())
                /* Green LED on tells the bootloader to reboot */
                gpio_set_value(CORGI_GPIO_LED_GREEN, 1);
 
-       pxa_restart('h', cmd);
+       pxa_restart(REBOOT_HARD, cmd);
 }
 
 static void __init corgi_init(void)
index fd7ea39..8963984 100644 (file)
@@ -9,6 +9,8 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/reboot.h>
+
 struct irq_data;
 
 extern void pxa_timer_init(void);
@@ -56,4 +58,4 @@ void __init pxa_set_btuart_info(void *info);
 void __init pxa_set_stuart_info(void *info);
 void __init pxa_set_hwuart_info(void *info);
 
-void pxa_restart(char, const char *);
+void pxa_restart(enum reboot_mode, const char *);
index 654b0ac..acc9d3c 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/wm97xx.h>
 #include <linux/mtd/physmap.h>
 #include <linux/usb/gpio_vbus.h>
+#include <linux/reboot.h>
 #include <linux/regulator/max1586.h>
 #include <linux/slab.h>
 #include <linux/i2c/pxa-i2c.h>
@@ -696,13 +697,13 @@ static void mioa701_machine_exit(void);
 static void mioa701_poweroff(void)
 {
        mioa701_machine_exit();
-       pxa_restart('s', NULL);
+       pxa_restart(REBOOT_SOFT, NULL);
 }
 
-static void mioa701_restart(char c, const char *cmd)
+static void mioa701_restart(enum reboot_mode c, const char *cmd)
 {
        mioa701_machine_exit();
-       pxa_restart('s', cmd);
+       pxa_restart(REBOOT_SOFT, cmd);
 }
 
 static struct gpio global_gpios[] = {
@@ -761,7 +762,6 @@ static void mioa701_machine_exit(void)
 
 MACHINE_START(MIOA701, "MIO A701")
        .atag_offset    = 0x100,
-       .restart_mode   = 's',
        .map_io         = &pxa27x_map_io,
        .nr_irqs        = PXA_NR_IRQS,
        .init_irq       = &pxa27x_init_irq,
index 50ccd5f..711d37e 100644 (file)
@@ -422,7 +422,7 @@ static struct i2c_board_info __initdata poodle_i2c_devices[] = {
 
 static void poodle_poweroff(void)
 {
-       pxa_restart('h', NULL);
+       pxa_restart(REBOOT_HARD, NULL);
 }
 
 static void __init poodle_init(void)
index 3fab583..0d5dd64 100644 (file)
@@ -83,7 +83,7 @@ static void do_hw_reset(void)
        writel_relaxed(readl_relaxed(OSCR) + 368640, OSMR3);
 }
 
-void pxa_restart(char mode, const char *cmd)
+void pxa_restart(enum reboot_mode mode, const char *cmd)
 {
        local_irq_disable();
        local_fiq_disable();
@@ -91,14 +91,14 @@ void pxa_restart(char mode, const char *cmd)
        clear_reset_status(RESET_STATUS_ALL);
 
        switch (mode) {
-       case 's':
+       case REBOOT_SOFT:
                /* Jump into ROM at address 0 */
                soft_restart(0);
                break;
-       case 'g':
+       case REBOOT_GPIO:
                do_gpio_reset();
                break;
-       case 'h':
+       case REBOOT_HARD:
        default:
                do_hw_reset();
                break;
index 362726c..2125df0 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/regulator/machine.h>
 #include <linux/io.h>
 #include <linux/module.h>
+#include <linux/reboot.h>
 
 #include <asm/setup.h>
 #include <asm/mach-types.h>
@@ -924,10 +925,10 @@ static inline void spitz_i2c_init(void) {}
  ******************************************************************************/
 static void spitz_poweroff(void)
 {
-       pxa_restart('g', NULL);
+       pxa_restart(REBOOT_GPIO, NULL);
 }
 
-static void spitz_restart(char mode, const char *cmd)
+static void spitz_restart(enum reboot_mode mode, const char *cmd)
 {
        uint32_t msc0 = __raw_readl(MSC0);
        /* Bootloader magic for a reboot */
@@ -979,7 +980,6 @@ static void __init spitz_fixup(struct tag *tags, char **cmdline,
 
 #ifdef CONFIG_MACH_SPITZ
 MACHINE_START(SPITZ, "SHARP Spitz")
-       .restart_mode   = 'g',
        .fixup          = spitz_fixup,
        .map_io         = pxa27x_map_io,
        .nr_irqs        = PXA_NR_IRQS,
@@ -993,7 +993,6 @@ MACHINE_END
 
 #ifdef CONFIG_MACH_BORZOI
 MACHINE_START(BORZOI, "SHARP Borzoi")
-       .restart_mode   = 'g',
        .fixup          = spitz_fixup,
        .map_io         = pxa27x_map_io,
        .nr_irqs        = PXA_NR_IRQS,
@@ -1007,7 +1006,6 @@ MACHINE_END
 
 #ifdef CONFIG_MACH_AKITA
 MACHINE_START(AKITA, "SHARP Akita")
-       .restart_mode   = 'g',
        .fixup          = spitz_fixup,
        .map_io         = pxa27x_map_io,
        .nr_irqs        = PXA_NR_IRQS,
index 3d91d2e..0206b91 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/input/matrix_keypad.h>
 #include <linux/i2c/pxa-i2c.h>
 #include <linux/usb/gpio_vbus.h>
+#include <linux/reboot.h>
 
 #include <asm/setup.h>
 #include <asm/mach-types.h>
@@ -911,10 +912,10 @@ static struct platform_device *devices[] __initdata = {
 
 static void tosa_poweroff(void)
 {
-       pxa_restart('g', NULL);
+       pxa_restart(REBOOT_GPIO, NULL);
 }
 
-static void tosa_restart(char mode, const char *cmd)
+static void tosa_restart(enum reboot_mode mode, const char *cmd)
 {
        uint32_t msc0 = __raw_readl(MSC0);
 
@@ -969,7 +970,6 @@ static void __init fixup_tosa(struct tag *tags, char **cmdline,
 }
 
 MACHINE_START(TOSA, "SHARP Tosa")
-       .restart_mode   = 'g',
        .fixup          = fixup_tosa,
        .map_io         = pxa25x_map_io,
        .nr_irqs        = TOSA_NR_IRQS,
index 5b1c8bf..c85ddb2 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/io.h>
 #include <linux/irqchip/arm-gic.h>
 #include <linux/platform_data/clk-realview.h>
+#include <linux/reboot.h>
 
 #include <mach/hardware.h>
 #include <asm/irq.h>
@@ -418,7 +419,7 @@ static void __init realview_eb_timer_init(void)
        realview_eb_twd_init();
 }
 
-static void realview_eb_restart(char mode, const char *cmd)
+static void realview_eb_restart(enum reboot_mode mode, const char *cmd)
 {
        void __iomem *reset_ctrl = __io_address(REALVIEW_SYS_RESETCTL);
        void __iomem *lock_ctrl = __io_address(REALVIEW_SYS_LOCK);
index d5e83a1..c5eade7 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/io.h>
 #include <linux/irqchip/arm-gic.h>
 #include <linux/platform_data/clk-realview.h>
+#include <linux/reboot.h>
 
 #include <mach/hardware.h>
 #include <asm/irq.h>
@@ -329,7 +330,7 @@ static void __init realview_pb1176_timer_init(void)
        realview_timer_init(IRQ_DC1176_TIMER0);
 }
 
-static void realview_pb1176_restart(char mode, const char *cmd)
+static void realview_pb1176_restart(enum reboot_mode mode, const char *cmd)
 {
        void __iomem *reset_ctrl = __io_address(REALVIEW_SYS_RESETCTL);
        void __iomem *lock_ctrl = __io_address(REALVIEW_SYS_LOCK);
index c3cfe21..f4b0962 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/io.h>
 #include <linux/irqchip/arm-gic.h>
 #include <linux/platform_data/clk-realview.h>
+#include <linux/reboot.h>
 
 #include <mach/hardware.h>
 #include <asm/irq.h>
@@ -316,7 +317,7 @@ static void __init realview_pb11mp_timer_init(void)
        realview_pb11mp_twd_init();
 }
 
-static void realview_pb11mp_restart(char mode, const char *cmd)
+static void realview_pb11mp_restart(enum reboot_mode mode, const char *cmd)
 {
        void __iomem *reset_ctrl = __io_address(REALVIEW_SYS_RESETCTL);
        void __iomem *lock_ctrl = __io_address(REALVIEW_SYS_LOCK);
index dde652a..10a3e1d 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/io.h>
 #include <linux/irqchip/arm-gic.h>
 #include <linux/platform_data/clk-realview.h>
+#include <linux/reboot.h>
 
 #include <asm/irq.h>
 #include <asm/mach-types.h>
@@ -264,7 +265,7 @@ static void __init realview_pba8_timer_init(void)
        realview_timer_init(IRQ_PBA8_TIMER0_1);
 }
 
-static void realview_pba8_restart(char mode, const char *cmd)
+static void realview_pba8_restart(enum reboot_mode mode, const char *cmd)
 {
        void __iomem *reset_ctrl = __io_address(REALVIEW_SYS_RESETCTL);
        void __iomem *lock_ctrl = __io_address(REALVIEW_SYS_LOCK);
index 54f0185..9d75493 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/io.h>
 #include <linux/irqchip/arm-gic.h>
 #include <linux/platform_data/clk-realview.h>
+#include <linux/reboot.h>
 
 #include <asm/irq.h>
 #include <asm/mach-types.h>
@@ -344,7 +345,7 @@ static void realview_pbx_fixup(struct tag *tags, char **from,
 #endif
 }
 
-static void realview_pbx_restart(char mode, const char *cmd)
+static void realview_pbx_restart(enum reboot_mode mode, const char *cmd)
 {
        void __iomem *reset_ctrl = __io_address(REALVIEW_SYS_RESETCTL);
        void __iomem *lock_ctrl = __io_address(REALVIEW_SYS_LOCK);
index a302cf5..09d602b 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/ata_platform.h>
 #include <linux/io.h>
 #include <linux/i2c.h>
+#include <linux/reboot.h>
 
 #include <asm/elf.h>
 #include <asm/mach-types.h>
@@ -201,7 +202,7 @@ static int __init rpc_init(void)
 
 arch_initcall(rpc_init);
 
-static void rpc_restart(char mode, const char *cmd)
+static void rpc_restart(enum reboot_mode mode, const char *cmd)
 {
        iomd_writeb(0, IOMD_ROMCR0);
 
index 307c371..84b2806 100644 (file)
@@ -12,6 +12,8 @@
 #ifndef __ARCH_ARM_MACH_S3C24XX_COMMON_H
 #define __ARCH_ARM_MACH_S3C24XX_COMMON_H __FILE__
 
+#include <linux/reboot.h>
+
 struct s3c2410_uartcfg;
 
 #ifdef CONFIG_CPU_S3C2410
@@ -20,7 +22,7 @@ extern  int s3c2410a_init(void);
 extern void s3c2410_map_io(void);
 extern void s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no);
 extern void s3c2410_init_clocks(int xtal);
-extern void s3c2410_restart(char mode, const char *cmd);
+extern void s3c2410_restart(enum reboot_mode mode, const char *cmd);
 extern void s3c2410_init_irq(void);
 #else
 #define s3c2410_init_clocks NULL
@@ -36,7 +38,7 @@ extern void s3c2412_map_io(void);
 extern void s3c2412_init_uarts(struct s3c2410_uartcfg *cfg, int no);
 extern void s3c2412_init_clocks(int xtal);
 extern  int s3c2412_baseclk_add(void);
-extern void s3c2412_restart(char mode, const char *cmd);
+extern void s3c2412_restart(enum reboot_mode mode, const char *cmd);
 extern void s3c2412_init_irq(void);
 #else
 #define s3c2412_init_clocks NULL
@@ -51,7 +53,7 @@ extern void s3c2416_map_io(void);
 extern void s3c2416_init_uarts(struct s3c2410_uartcfg *cfg, int no);
 extern void s3c2416_init_clocks(int xtal);
 extern  int s3c2416_baseclk_add(void);
-extern void s3c2416_restart(char mode, const char *cmd);
+extern void s3c2416_restart(enum reboot_mode mode, const char *cmd);
 extern void s3c2416_init_irq(void);
 
 extern struct syscore_ops s3c2416_irq_syscore_ops;
@@ -66,7 +68,7 @@ extern struct syscore_ops s3c2416_irq_syscore_ops;
 extern void s3c244x_map_io(void);
 extern void s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no);
 extern void s3c244x_init_clocks(int xtal);
-extern void s3c244x_restart(char mode, const char *cmd);
+extern void s3c244x_restart(enum reboot_mode mode, const char *cmd);
 #else
 #define s3c244x_init_clocks NULL
 #define s3c244x_init_uarts NULL
@@ -96,7 +98,7 @@ extern void s3c2443_map_io(void);
 extern void s3c2443_init_uarts(struct s3c2410_uartcfg *cfg, int no);
 extern void s3c2443_init_clocks(int xtal);
 extern  int s3c2443_baseclk_add(void);
-extern void s3c2443_restart(char mode, const char *cmd);
+extern void s3c2443_restart(enum reboot_mode mode, const char *cmd);
 extern void s3c2443_init_irq(void);
 #else
 #define s3c2443_init_clocks NULL
index ff384ac..34676d1 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/serial_core.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 #include <linux/io.h>
 
 #include <asm/mach/arch.h>
@@ -196,9 +197,9 @@ int __init s3c2410a_init(void)
        return s3c2410_init();
 }
 
-void s3c2410_restart(char mode, const char *cmd)
+void s3c2410_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode == 's') {
+       if (mode == REBOOT_SOFT) {
                soft_restart(0);
        }
 
index 0f864d4..0251650 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/serial_core.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
+#include <linux/reboot.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -129,9 +130,9 @@ static void s3c2412_idle(void)
        cpu_do_idle();
 }
 
-void s3c2412_restart(char mode, const char *cmd)
+void s3c2412_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode == 's')
+       if (mode == REBOOT_SOFT)
                soft_restart(0);
 
        /* errata "Watch-dog/Software Reset Problem" specifies that
index b9c5d38..9ef3ccf 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/reboot.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -79,9 +80,9 @@ static struct device s3c2416_dev = {
        .bus            = &s3c2416_subsys,
 };
 
-void s3c2416_restart(char mode, const char *cmd)
+void s3c2416_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode == 's')
+       if (mode == REBOOT_SOFT)
                soft_restart(0);
 
        __raw_writel(S3C2443_SWRST_RESET, S3C2443_SWRST);
index 8328cd6..b6c7191 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/device.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/reboot.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -59,9 +60,9 @@ static struct device s3c2443_dev = {
        .bus            = &s3c2443_subsys,
 };
 
-void s3c2443_restart(char mode, const char *cmd)
+void s3c2443_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode == 's')
+       if (mode == REBOOT_SOFT)
                soft_restart(0);
 
        __raw_writel(S3C2443_SWRST_RESET, S3C2443_SWRST);
index d0423e2..911b555 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/init.h>
 #include <linux/serial_core.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 #include <linux/device.h>
 #include <linux/syscore_ops.h>
 #include <linux/clk.h>
@@ -198,9 +199,9 @@ struct syscore_ops s3c244x_pm_syscore_ops = {
        .resume         = s3c244x_resume,
 };
 
-void s3c244x_restart(char mode, const char *cmd)
+void s3c244x_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode == 's')
+       if (mode == REBOOT_SOFT)
                soft_restart(0);
 
        samsung_wdt_reset();
index 1aed6f4..3f62e46 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/ioport.h>
 #include <linux/serial_core.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 #include <linux/io.h>
 #include <linux/dma-mapping.h>
 #include <linux/irq.h>
@@ -381,9 +382,9 @@ static int __init s3c64xx_init_irq_eint(void)
 }
 arch_initcall(s3c64xx_init_irq_eint);
 
-void s3c64xx_restart(char mode, const char *cmd)
+void s3c64xx_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode != 's')
+       if (mode != REBOOT_SOFT)
                samsung_wdt_reset();
 
        /* if all else fails, or mode was for soft, jump to 0 */
index 6cfc99b..e8f990b 100644 (file)
 #ifndef __ARCH_ARM_MACH_S3C64XX_COMMON_H
 #define __ARCH_ARM_MACH_S3C64XX_COMMON_H
 
+#include <linux/reboot.h>
+
 void s3c64xx_init_irq(u32 vic0, u32 vic1);
 void s3c64xx_init_io(struct map_desc *mach_desc, int size);
 
 void s3c64xx_register_clocks(unsigned long xtal, unsigned armclk_limit);
 void s3c64xx_setup_clocks(void);
 
-void s3c64xx_restart(char mode, const char *cmd);
+void s3c64xx_restart(enum reboot_mode mode, const char *cmd);
 void s3c64xx_init_late(void);
 
 #ifdef CONFIG_CPU_S3C6400
index 76d0053..dfdfdc3 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/gpio.h>
 #include <linux/irq.h>
+#include <linux/reboot.h>
 
 #include <asm/irq.h>
 #include <asm/proc-fns.h>
@@ -439,9 +440,9 @@ static int __init s5p64x0_init_irq_eint(void)
 }
 arch_initcall(s5p64x0_init_irq_eint);
 
-void s5p64x0_restart(char mode, const char *cmd)
+void s5p64x0_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode != 's')
+       if (mode != REBOOT_SOFT)
                samsung_wdt_reset();
 
        soft_restart(0);
index f8a60fd..f3a9b43 100644 (file)
@@ -12,6 +12,8 @@
 #ifndef __ARCH_ARM_MACH_S5P64X0_COMMON_H
 #define __ARCH_ARM_MACH_S5P64X0_COMMON_H
 
+#include <linux/reboot.h>
+
 void s5p6440_init_irq(void);
 void s5p6450_init_irq(void);
 void s5p64x0_init_io(struct map_desc *mach_desc, int size);
@@ -22,7 +24,7 @@ void s5p6440_setup_clocks(void);
 void s5p6450_register_clocks(void);
 void s5p6450_setup_clocks(void);
 
-void s5p64x0_restart(char mode, const char *cmd);
+void s5p64x0_restart(enum reboot_mode mode, const char *cmd);
 
 #ifdef CONFIG_CPU_S5P6440
 
index 5110315..4bdfecf 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/serial_core.h>
 #include <linux/platform_device.h>
 #include <linux/sched.h>
+#include <linux/reboot.h>
 
 #include <asm/irq.h>
 #include <asm/proc-fns.h>
@@ -217,9 +218,9 @@ void __init s5pc100_init_uarts(struct s3c2410_uartcfg *cfg, int no)
        s3c24xx_init_uartdevs("s3c6400-uart", s5p_uart_resources, cfg, no);
 }
 
-void s5pc100_restart(char mode, const char *cmd)
+void s5pc100_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode != 's')
+       if (mode != REBOOT_SOFT)
                samsung_wdt_reset();
 
        soft_restart(0);
index c41f912..08d782d 100644 (file)
 #ifndef __ARCH_ARM_MACH_S5PC100_COMMON_H
 #define __ARCH_ARM_MACH_S5PC100_COMMON_H
 
+#include <linux/reboot.h>
+
 void s5pc100_init_io(struct map_desc *mach_desc, int size);
 void s5pc100_init_irq(void);
 
 void s5pc100_register_clocks(void);
 void s5pc100_setup_clocks(void);
 
-void s5pc100_restart(char mode, const char *cmd);
+void s5pc100_restart(enum reboot_mode mode, const char *cmd);
 
 extern  int s5pc100_init(void);
 extern void s5pc100_map_io(void);
index 9dfe93e..023f1a7 100644 (file)
@@ -143,7 +143,7 @@ static struct map_desc s5pv210_iodesc[] __initdata = {
        }
 };
 
-void s5pv210_restart(char mode, const char *cmd)
+void s5pv210_restart(enum reboot_mode mode, const char *cmd)
 {
        __raw_writel(0x1, S5P_SWRESET);
 }
index 0a1cc0a..fe1beb5 100644 (file)
 #ifndef __ARCH_ARM_MACH_S5PV210_COMMON_H
 #define __ARCH_ARM_MACH_S5PV210_COMMON_H
 
+#include <linux/reboot.h>
+
 void s5pv210_init_io(struct map_desc *mach_desc, int size);
 void s5pv210_init_irq(void);
 
 void s5pv210_register_clocks(void);
 void s5pv210_setup_clocks(void);
 
-void s5pv210_restart(char mode, const char *cmd);
+void s5pv210_restart(enum reboot_mode mode, const char *cmd);
 
 extern  int s5pv210_init(void);
 extern void s5pv210_map_io(void);
index 9db3e98..f25b611 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/cpufreq.h>
 #include <linux/ioport.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 
 #include <video/sa1100fb.h>
 
@@ -131,9 +132,9 @@ static void sa1100_power_off(void)
        PMCR = PMCR_SF;
 }
 
-void sa11x0_restart(char mode, const char *cmd)
+void sa11x0_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode == 's') {
+       if (mode == REBOOT_SOFT) {
                /* Jump into ROM at address 0 */
                soft_restart(0);
        } else {
index 2abc6a1..9a33695 100644 (file)
@@ -3,12 +3,13 @@
  *
  * Author: Nicolas Pitre
  */
+#include <linux/reboot.h>
 
 extern void sa1100_timer_init(void);
 extern void __init sa1100_map_io(void);
 extern void __init sa1100_init_irq(void);
 extern void __init sa1100_init_gpio(void);
-extern void sa11x0_restart(char, const char *);
+extern void sa11x0_restart(enum reboot_mode, const char *);
 extern void sa11x0_init_late(void);
 
 #define SET_BANK(__nr,__start,__size) \
index 1535557..1d32c5e 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/serial_8250.h>
 #include <linux/io.h>
 #include <linux/cpu.h>
+#include <linux/reboot.h>
 
 #include <asm/setup.h>
 #include <asm/mach-types.h>
@@ -24,7 +25,7 @@
 #define ROMCARD_SIZE            0x08000000
 #define ROMCARD_START           0x10000000
 
-static void shark_restart(char mode, const char *cmd)
+static void shark_restart(enum reboot_mode mode, const char *cmd)
 {
         short temp;
         /* Reset the Machine via pc[3] of the sequoia chipset */
index 44a6215..e115f67 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/mmc/sh_mmcif.h>
 #include <linux/mmc/sh_mobile_sdhi.h>
 #include <linux/i2c-gpio.h>
+#include <linux/reboot.h>
 #include <mach/common.h>
 #include <mach/irqs.h>
 #include <mach/r8a7740.h>
@@ -377,7 +378,7 @@ static struct resource sh_eth_resources[] = {
 };
 
 static struct platform_device sh_eth_device = {
-       .name = "sh-eth",
+       .name = "r8a7740-gether",
        .id = -1,
        .dev = {
                .platform_data = &sh_eth_platdata,
@@ -1259,7 +1260,7 @@ static void __init eva_add_early_devices(void)
 }
 
 #define RESCNT2 IOMEM(0xe6188020)
-static void eva_restart(char mode, const char *cmd)
+static void eva_restart(enum reboot_mode mode, const char *cmd)
 {
        /* Do soft power on reset */
        writel((1 << 31), RESCNT2);
index 165483c..1068120 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/pinctrl/machine.h>
 #include <linux/pinctrl/pinconf-generic.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/machine.h>
 #include <linux/smsc911x.h>
@@ -890,7 +891,7 @@ static void __init kzm_init(void)
        sh73a0_pm_init();
 }
 
-static void kzm9g_restart(char mode, const char *cmd)
+static void kzm9g_restart(enum reboot_mode mode, const char *cmd)
 {
 #define RESCNT2 IOMEM(0xe6188020)
        /* Do soft power on reset */
index 7fd32d6..de10fd7 100644 (file)
@@ -594,7 +594,7 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("e6860000.sdhi",          &mstp_clks[MSTP313]),
        CLKDEV_DEV_ID("sh_mmcif",               &mstp_clks[MSTP312]),
        CLKDEV_DEV_ID("e6bd0000.mmcif",         &mstp_clks[MSTP312]),
-       CLKDEV_DEV_ID("sh-eth",                 &mstp_clks[MSTP309]),
+       CLKDEV_DEV_ID("r8a7740-gether",         &mstp_clks[MSTP309]),
        CLKDEV_DEV_ID("e9a00000.sh-eth",        &mstp_clks[MSTP309]),
        CLKDEV_DEV_ID("renesas_tpu_pwm",        &mstp_clks[MSTP304]),
 
index 53798e5..a0e9eb7 100644 (file)
@@ -145,7 +145,7 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP323]), /* SDHI0 */
        CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP322]), /* SDHI1 */
        CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP321]), /* SDHI2 */
-       CLKDEV_DEV_ID("sh-eth", &mstp_clks[MSTP114]), /* Ether */
+       CLKDEV_DEV_ID("r8a777x-ether", &mstp_clks[MSTP114]), /* Ether */
        CLKDEV_DEV_ID("ehci-platform", &mstp_clks[MSTP100]), /* USB EHCI port0/1 */
        CLKDEV_DEV_ID("ohci-platform", &mstp_clks[MSTP100]), /* USB OHCI port0/1 */
        CLKDEV_DEV_ID("i2c-rcar.0", &mstp_clks[MSTP030]), /* I2C0 */
index 9daeb8c..10340f5 100644 (file)
@@ -165,7 +165,7 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("rcar-pcie", &mstp_clks[MSTP116]), /* PCIe */
        CLKDEV_DEV_ID("sata_rcar", &mstp_clks[MSTP115]), /* SATA */
        CLKDEV_DEV_ID("fc600000.sata", &mstp_clks[MSTP115]), /* SATA w/DT */
-       CLKDEV_DEV_ID("sh-eth", &mstp_clks[MSTP114]), /* Ether */
+       CLKDEV_DEV_ID("r8a777x-ether", &mstp_clks[MSTP114]), /* Ether */
        CLKDEV_DEV_ID("ehci-platform.1", &mstp_clks[MSTP101]), /* USB EHCI port2 */
        CLKDEV_DEV_ID("ohci-platform.1", &mstp_clks[MSTP101]), /* USB OHCI port2 */
        CLKDEV_DEV_ID("ehci-platform.0", &mstp_clks[MSTP100]), /* USB EHCI port0/1 */
index 8ea11b4..bfce964 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
+#include <linux/reboot.h>
 
 #include <asm/hardware/cache-l2x0.h>
 #include <asm/mach/arch.h>
@@ -89,13 +90,13 @@ static void __init socfpga_init_irq(void)
        socfpga_sysmgr_init();
 }
 
-static void socfpga_cyclone5_restart(char mode, const char *cmd)
+static void socfpga_cyclone5_restart(enum reboot_mode mode, const char *cmd)
 {
        u32 temp;
 
        temp = readl(rst_manager_base_addr + SOCFPGA_RSTMGR_CTRL);
 
-       if (mode == 'h')
+       if (mode == REBOOT_HARD)
                temp |= RSTMGR_CTRL_SWCOLDRSTREQ;
        else
                temp |= RSTMGR_CTRL_SWWARMRSTREQ;
index a9fd453..904f2c9 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/dmaengine.h>
 #include <linux/amba/pl08x.h>
 #include <linux/init.h>
+#include <linux/reboot.h>
+
 #include <asm/mach/time.h>
 
 extern void spear13xx_timer_init(void);
@@ -32,7 +34,7 @@ void __init spear6xx_clk_init(void __iomem *misc_base);
 void __init spear13xx_map_io(void);
 void __init spear13xx_l2x0_init(void);
 
-void spear_restart(char, const char *);
+void spear_restart(enum reboot_mode, const char *);
 
 void spear13xx_secondary_startup(void);
 void __cpuinit spear13xx_cpu_die(unsigned int cpu);
index 2b44500..ce5e098 100644 (file)
  */
 #include <linux/io.h>
 #include <linux/amba/sp810.h>
+#include <linux/reboot.h>
 #include <asm/system_misc.h>
 #include <mach/spear.h>
 #include "generic.h"
 
 #define SPEAR13XX_SYS_SW_RES                   (VA_MISC_BASE + 0x204)
-void spear_restart(char mode, const char *cmd)
+void spear_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode == 's') {
+       if (mode == REBOOT_SOFT) {
                /* software reset, Jump into ROM at address 0 */
                soft_restart(0);
        } else {
index 84485a1..38a3c55 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/io.h>
+#include <linux/reboot.h>
 
 #include <linux/clk/sunxi.h>
 
@@ -33,7 +34,7 @@
 
 static void __iomem *wdt_base;
 
-static void sun4i_restart(char mode, const char *cmd)
+static void sun4i_restart(enum reboot_mode mode, const char *cmd)
 {
        if (!wdt_base)
                return;
index 1787327..9a6659f 100644 (file)
@@ -23,8 +23,9 @@
 #define __MACH_TEGRA_BOARD_H
 
 #include <linux/types.h>
+#include <linux/reboot.h>
 
-void tegra_assert_system_reset(char mode, const char *cmd);
+void tegra_assert_system_reset(enum reboot_mode mode, const char *cmd);
 
 void __init tegra_init_early(void);
 void __init tegra_map_common_io(void);
index b25153e..94a119a 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/io.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/reboot.h>
 #include <linux/irqchip.h>
 #include <linux/clk-provider.h>
 
@@ -68,7 +69,7 @@ void __init tegra_dt_init_irq(void)
 }
 #endif
 
-void tegra_assert_system_reset(char mode, const char *cmd)
+void tegra_assert_system_reset(enum reboot_mode mode, const char *cmd)
 {
        void __iomem *reset = IO_ADDRESS(TEGRA_PMC_BASE + 0);
        u32 reg;
index 4f7ac2a..35670b1 100644 (file)
@@ -300,11 +300,11 @@ static void __init u300_init_check_chip(void)
 /* Forward declare this function from the watchdog */
 void coh901327_watchdog_reset(void);
 
-static void u300_restart(char mode, const char *cmd)
+static void u300_restart(enum reboot_mode mode, const char *cmd)
 {
        switch (mode) {
-       case 's':
-       case 'h':
+       case REBOOT_SOFT:
+       case REBOOT_HARD:
 #ifdef CONFIG_COH901327_WATCHDOG
                coh901327_watchdog_reset();
 #endif
index 54bb80b..3b0572f 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/clkdev.h>
 #include <linux/mtd/physmap.h>
 #include <linux/bitops.h>
+#include <linux/reboot.h>
 
 #include <asm/irq.h>
 #include <asm/hardware/arm_timer.h>
@@ -733,7 +734,7 @@ static void versatile_leds_event(led_event_t ledevt)
 }
 #endif /* CONFIG_LEDS */
 
-void versatile_restart(char mode, const char *cmd)
+void versatile_restart(enum reboot_mode mode, const char *cmd)
 {
        void __iomem *sys = __io_address(VERSATILE_SYS_BASE);
        u32 val;
index 5c1b87d..f06d576 100644 (file)
 
 #include <linux/amba/bus.h>
 #include <linux/of_platform.h>
+#include <linux/reboot.h>
 
 extern void __init versatile_init(void);
 extern void __init versatile_init_early(void);
 extern void __init versatile_init_irq(void);
 extern void __init versatile_map_io(void);
 extern void versatile_timer_init(void);
-extern void versatile_restart(char, const char *);
+extern void versatile_restart(enum reboot_mode, const char *);
 extern unsigned int mmc_status(struct device *dev);
 #ifdef CONFIG_OF
 extern struct of_dev_auxdata versatile_auxdata_lookup[];
index f8f2f00..eefaa60 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/clocksource.h>
 #include <linux/io.h>
 #include <linux/pm.h>
+#include <linux/reboot.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -46,7 +47,7 @@
 
 static void __iomem *pmc_base;
 
-void vt8500_restart(char mode, const char *cmd)
+void vt8500_restart(enum reboot_mode mode, const char *cmd)
 {
        if (pmc_base)
                writel(1, pmc_base + VT8500_PMSR_REG);
index 9e4dd8b..b1eabaa 100644 (file)
@@ -230,9 +230,9 @@ void __init nuc900_init_clocks(void)
 #define        WTE     (1 << 7)
 #define        WTRE    (1 << 1)
 
-void nuc9xx_restart(char mode, const char *cmd)
+void nuc9xx_restart(enum reboot_mode mode, const char *cmd)
 {
-       if (mode == 's') {
+       if (mode == REBOOT_SOFT) {
                /* Jump into ROM at address 0 */
                soft_restart(0);
        } else {
index 88ef4b2..e3ab1e1 100644 (file)
  * published by the Free Software Foundation.
  *
  */
+
+#include <linux/reboot.h>
+
 struct map_desc;
 
 /* core initialisation functions */
 
 extern void nuc900_init_irq(void);
 extern void nuc900_timer_init(void);
-extern void nuc9xx_restart(char, const char *);
+extern void nuc9xx_restart(enum reboot_mode, const char *);
index 1a643ee..f50d223 100644 (file)
@@ -900,8 +900,7 @@ void bpf_jit_compile(struct sk_filter *fp)
 #endif
 
        alloc_size = 4 * ctx.idx;
-       ctx.target = module_alloc(max(sizeof(struct work_struct),
-                                     alloc_size));
+       ctx.target = module_alloc(alloc_size);
        if (unlikely(ctx.target == NULL))
                goto out;
 
@@ -927,19 +926,8 @@ out:
        return;
 }
 
-static void bpf_jit_free_worker(struct work_struct *work)
-{
-       module_free(NULL, work);
-}
-
 void bpf_jit_free(struct sk_filter *fp)
 {
-       struct work_struct *work;
-
-       if (fp->bpf_func != sk_run_filter) {
-               work = (struct work_struct *)fp->bpf_func;
-
-               INIT_WORK(work, bpf_jit_free_worker);
-               schedule_work(work);
-       }
+       if (fp->bpf_func != sk_run_filter)
+               module_free(NULL, fp->bpf_func);
 }
index e4de9be..697de6d 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/gpio.h>
 #include <linux/export.h>
 #include <asm/hardware/iop3xx.h>
+#include <mach/gpio.h>
 
 void gpio_line_config(int line, int direction)
 {
index 33fa699..3a4d5e5 100644 (file)
@@ -11,7 +11,7 @@
 #include <asm/system_misc.h>
 #include <mach/hardware.h>
 
-void iop3xx_restart(char mode, const char *cmd)
+void iop3xx_restart(enum reboot_mode mode, const char *cmd)
 {
        *IOP3XX_PCSR = 0x30;
 
index 37401f5..79b6179 100644 (file)
@@ -74,4 +74,6 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* __ASM_AVR32_SOCKET_H */
index 5f2cdb3..daf5f19 100644 (file)
@@ -2,9 +2,7 @@ if ETRAX_ARCH_V10
 
 config ETRAX_ETHERNET
        bool "Ethernet support"
-       depends on ETRAX_ARCH_V10
-       select ETHERNET
-       select NET_CORE
+       depends on ETRAX_ARCH_V10 && NETDEVICES
        select MII
        help
          This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet
index acff3df..1d866d3 100644 (file)
@@ -2,9 +2,7 @@ if ETRAX_ARCH_V32
 
 config ETRAX_ETHERNET
        bool "Ethernet support"
-       depends on ETRAX_ARCH_V32
-       select ETHERNET
-       select NET_CORE
+       depends on ETRAX_ARCH_V32 && NETDEVICES
        select MII
        help
          This option enables the ETRAX FS built-in 10/100Mbit Ethernet
index ba409c9..47b1ec5 100644 (file)
@@ -76,6 +76,8 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* _ASM_SOCKET_H */
 
 
index 31dbb5d..dbc0852 100644 (file)
@@ -74,5 +74,7 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* _ASM_SOCKET_H */
 
index 5d1c6d0..a38d38a 100644 (file)
@@ -74,4 +74,6 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* _ASM_SOCKET_H */
index c13064e..d1b04c4 100644 (file)
@@ -268,7 +268,7 @@ static __inline__ int dev_is_ethdev(struct net_device *dev)
 static int
 simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct simeth_local *local;
        struct in_device *in_dev;
        struct in_ifaddr **ifap = NULL;
index 6b4329f..d3358b7 100644 (file)
@@ -83,4 +83,6 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* _ASM_IA64_SOCKET_H */
index 2a3b59e..44aaf46 100644 (file)
@@ -74,4 +74,6 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* _ASM_M32R_SOCKET_H */
index 2c75bf7..8fddf46 100644 (file)
@@ -224,8 +224,10 @@ do_sigbus:
         */
 out_of_memory:
        up_read(&mm->mmap_sem);
-       if (user_mode(regs))
-               do_group_exit(SIGKILL);
+       if (user_mode(regs)) {
+               pagefault_out_of_memory();
+               return 1;
+       }
 
 no_context:
        /* Are we prepared to handle this kernel fault?  */
index a9505c4..9c0ddaf 100644 (file)
@@ -845,6 +845,10 @@ int __init board_register_devices(void)
            !bcm63xx_nvram_get_mac_address(board.enet1.mac_addr))
                bcm63xx_enet_register(1, &board.enet1);
 
+       if (board.has_enetsw &&
+           !bcm63xx_nvram_get_mac_address(board.enetsw.mac_addr))
+               bcm63xx_enetsw_register(&board.enetsw);
+
        if (board.has_usbd)
                bcm63xx_usbd_register(&board.usbd);
 
index 39c2336..52bc01d 100644 (file)
@@ -9,16 +9,60 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
+#include <linux/export.h>
 #include <bcm63xx_dev_enet.h>
 #include <bcm63xx_io.h>
 #include <bcm63xx_regs.h>
 
+#ifdef BCMCPU_RUNTIME_DETECT
+static const unsigned long bcm6348_regs_enetdmac[] = {
+       [ENETDMAC_CHANCFG]      = ENETDMAC_CHANCFG_REG,
+       [ENETDMAC_IR]           = ENETDMAC_IR_REG,
+       [ENETDMAC_IRMASK]       = ENETDMAC_IRMASK_REG,
+       [ENETDMAC_MAXBURST]     = ENETDMAC_MAXBURST_REG,
+};
+
+static const unsigned long bcm6345_regs_enetdmac[] = {
+       [ENETDMAC_CHANCFG]      = ENETDMA_6345_CHANCFG_REG,
+       [ENETDMAC_IR]           = ENETDMA_6345_IR_REG,
+       [ENETDMAC_IRMASK]       = ENETDMA_6345_IRMASK_REG,
+       [ENETDMAC_MAXBURST]     = ENETDMA_6345_MAXBURST_REG,
+       [ENETDMAC_BUFALLOC]     = ENETDMA_6345_BUFALLOC_REG,
+       [ENETDMAC_RSTART]       = ENETDMA_6345_RSTART_REG,
+       [ENETDMAC_FC]           = ENETDMA_6345_FC_REG,
+       [ENETDMAC_LEN]          = ENETDMA_6345_LEN_REG,
+};
+
+const unsigned long *bcm63xx_regs_enetdmac;
+EXPORT_SYMBOL(bcm63xx_regs_enetdmac);
+
+static __init void bcm63xx_enetdmac_regs_init(void)
+{
+       if (BCMCPU_IS_6345())
+               bcm63xx_regs_enetdmac = bcm6345_regs_enetdmac;
+       else
+               bcm63xx_regs_enetdmac = bcm6348_regs_enetdmac;
+}
+#else
+static __init void bcm63xx_enetdmac_regs_init(void) { }
+#endif
+
 static struct resource shared_res[] = {
        {
                .start          = -1, /* filled at runtime */
                .end            = -1, /* filled at runtime */
                .flags          = IORESOURCE_MEM,
        },
+       {
+               .start          = -1, /* filled at runtime */
+               .end            = -1, /* filled at runtime */
+               .flags          = IORESOURCE_MEM,
+       },
+       {
+               .start          = -1, /* filled at runtime */
+               .end            = -1, /* filled at runtime */
+               .flags          = IORESOURCE_MEM,
+       },
 };
 
 static struct platform_device bcm63xx_enet_shared_device = {
@@ -94,6 +138,71 @@ static struct platform_device bcm63xx_enet1_device = {
        },
 };
 
+static struct resource enetsw_res[] = {
+       {
+               /* start & end filled at runtime */
+               .flags          = IORESOURCE_MEM,
+       },
+       {
+               /* start filled at runtime */
+               .flags          = IORESOURCE_IRQ,
+       },
+       {
+               /* start filled at runtime */
+               .flags          = IORESOURCE_IRQ,
+       },
+};
+
+static struct bcm63xx_enetsw_platform_data enetsw_pd;
+
+static struct platform_device bcm63xx_enetsw_device = {
+       .name           = "bcm63xx_enetsw",
+       .num_resources  = ARRAY_SIZE(enetsw_res),
+       .resource       = enetsw_res,
+       .dev            = {
+               .platform_data = &enetsw_pd,
+       },
+};
+
+static int __init register_shared(void)
+{
+       int ret, chan_count;
+
+       if (shared_device_registered)
+               return 0;
+
+       bcm63xx_enetdmac_regs_init();
+
+       shared_res[0].start = bcm63xx_regset_address(RSET_ENETDMA);
+       shared_res[0].end = shared_res[0].start;
+       if (BCMCPU_IS_6345())
+               shared_res[0].end += (RSET_6345_ENETDMA_SIZE) - 1;
+       else
+               shared_res[0].end += (RSET_ENETDMA_SIZE)  - 1;
+
+       if (BCMCPU_IS_6328() || BCMCPU_IS_6362() || BCMCPU_IS_6368())
+               chan_count = 32;
+       else if (BCMCPU_IS_6345())
+               chan_count = 8;
+       else
+               chan_count = 16;
+
+       shared_res[1].start = bcm63xx_regset_address(RSET_ENETDMAC);
+       shared_res[1].end = shared_res[1].start;
+       shared_res[1].end += RSET_ENETDMAC_SIZE(chan_count)  - 1;
+
+       shared_res[2].start = bcm63xx_regset_address(RSET_ENETDMAS);
+       shared_res[2].end = shared_res[2].start;
+       shared_res[2].end += RSET_ENETDMAS_SIZE(chan_count)  - 1;
+
+       ret = platform_device_register(&bcm63xx_enet_shared_device);
+       if (ret)
+               return ret;
+       shared_device_registered = 1;
+
+       return 0;
+}
+
 int __init bcm63xx_enet_register(int unit,
                                 const struct bcm63xx_enet_platform_data *pd)
 {
@@ -104,22 +213,12 @@ int __init bcm63xx_enet_register(int unit,
        if (unit > 1)
                return -ENODEV;
 
-       if (unit == 1 && BCMCPU_IS_6338())
+       if (unit == 1 && (BCMCPU_IS_6338() || BCMCPU_IS_6345()))
                return -ENODEV;
 
-       if (!shared_device_registered) {
-               shared_res[0].start = bcm63xx_regset_address(RSET_ENETDMA);
-               shared_res[0].end = shared_res[0].start;
-               if (BCMCPU_IS_6338())
-                       shared_res[0].end += (RSET_ENETDMA_SIZE / 2)  - 1;
-               else
-                       shared_res[0].end += (RSET_ENETDMA_SIZE)  - 1;
-
-               ret = platform_device_register(&bcm63xx_enet_shared_device);
-               if (ret)
-                       return ret;
-               shared_device_registered = 1;
-       }
+       ret = register_shared();
+       if (ret)
+               return ret;
 
        if (unit == 0) {
                enet0_res[0].start = bcm63xx_regset_address(RSET_ENET0);
@@ -155,8 +254,62 @@ int __init bcm63xx_enet_register(int unit,
                dpd->phy_interrupt = bcm63xx_get_irq_number(IRQ_ENET_PHY);
        }
 
+       dpd->dma_chan_en_mask = ENETDMAC_CHANCFG_EN_MASK;
+       dpd->dma_chan_int_mask = ENETDMAC_IR_PKTDONE_MASK;
+       if (BCMCPU_IS_6345()) {
+               dpd->dma_chan_en_mask |= ENETDMAC_CHANCFG_CHAINING_MASK;
+               dpd->dma_chan_en_mask |= ENETDMAC_CHANCFG_WRAP_EN_MASK;
+               dpd->dma_chan_en_mask |= ENETDMAC_CHANCFG_FLOWC_EN_MASK;
+               dpd->dma_chan_int_mask |= ENETDMA_IR_BUFDONE_MASK;
+               dpd->dma_chan_int_mask |= ENETDMA_IR_NOTOWNER_MASK;
+               dpd->dma_chan_width = ENETDMA_6345_CHAN_WIDTH;
+               dpd->dma_desc_shift = ENETDMA_6345_DESC_SHIFT;
+       } else {
+               dpd->dma_has_sram = true;
+               dpd->dma_chan_width = ENETDMA_CHAN_WIDTH;
+       }
+
        ret = platform_device_register(pdev);
        if (ret)
                return ret;
        return 0;
 }
+
+int __init
+bcm63xx_enetsw_register(const struct bcm63xx_enetsw_platform_data *pd)
+{
+       int ret;
+
+       if (!BCMCPU_IS_6328() && !BCMCPU_IS_6362() && !BCMCPU_IS_6368())
+               return -ENODEV;
+
+       ret = register_shared();
+       if (ret)
+               return ret;
+
+       enetsw_res[0].start = bcm63xx_regset_address(RSET_ENETSW);
+       enetsw_res[0].end = enetsw_res[0].start;
+       enetsw_res[0].end += RSET_ENETSW_SIZE - 1;
+       enetsw_res[1].start = bcm63xx_get_irq_number(IRQ_ENETSW_RXDMA0);
+       enetsw_res[2].start = bcm63xx_get_irq_number(IRQ_ENETSW_TXDMA0);
+       if (!enetsw_res[2].start)
+               enetsw_res[2].start = -1;
+
+       memcpy(bcm63xx_enetsw_device.dev.platform_data, pd, sizeof(*pd));
+
+       if (BCMCPU_IS_6328())
+               enetsw_pd.num_ports = ENETSW_PORTS_6328;
+       else if (BCMCPU_IS_6362() || BCMCPU_IS_6368())
+               enetsw_pd.num_ports = ENETSW_PORTS_6368;
+
+       enetsw_pd.dma_has_sram = true;
+       enetsw_pd.dma_chan_width = ENETDMA_CHAN_WIDTH;
+       enetsw_pd.dma_chan_en_mask = ENETDMAC_CHANCFG_EN_MASK;
+       enetsw_pd.dma_chan_int_mask = ENETDMAC_IR_PKTDONE_MASK;
+
+       ret = platform_device_register(&bcm63xx_enetsw_device);
+       if (ret)
+               return ret;
+
+       return 0;
+}
index 3362289..e6e65dc 100644 (file)
@@ -173,7 +173,10 @@ enum bcm63xx_regs_set {
 #define BCM_6358_RSET_SPI_SIZE         1804
 #define BCM_6368_RSET_SPI_SIZE         1804
 #define RSET_ENET_SIZE                 2048
-#define RSET_ENETDMA_SIZE              2048
+#define RSET_ENETDMA_SIZE              256
+#define RSET_6345_ENETDMA_SIZE         64
+#define RSET_ENETDMAC_SIZE(chans)      (16 * (chans))
+#define RSET_ENETDMAS_SIZE(chans)      (16 * (chans))
 #define RSET_ENETSW_SIZE               65536
 #define RSET_UART_SIZE                 24
 #define RSET_UDC_SIZE                  256
@@ -298,7 +301,7 @@ enum bcm63xx_regs_set {
 #define BCM_6345_USBDMA_BASE           (0xfffe2800)
 #define BCM_6345_ENET0_BASE            (0xfffe1800)
 #define BCM_6345_ENETDMA_BASE          (0xfffe2800)
-#define BCM_6345_ENETDMAC_BASE         (0xfffe2900)
+#define BCM_6345_ENETDMAC_BASE         (0xfffe2840)
 #define BCM_6345_ENETDMAS_BASE         (0xfffe2a00)
 #define BCM_6345_ENETSW_BASE           (0xdeadbeef)
 #define BCM_6345_PCMCIA_BASE           (0xfffe2028)
index d53f611..753953e 100644 (file)
@@ -4,6 +4,8 @@
 #include <linux/if_ether.h>
 #include <linux/init.h>
 
+#include <bcm63xx_regs.h>
+
 /*
  * on board ethernet platform data
  */
@@ -37,9 +39,129 @@ struct bcm63xx_enet_platform_data {
                                          int phy_id, int reg),
                          void (*mii_write)(struct net_device *dev,
                                            int phy_id, int reg, int val));
+
+       /* DMA channel enable mask */
+       u32 dma_chan_en_mask;
+
+       /* DMA channel interrupt mask */
+       u32 dma_chan_int_mask;
+
+       /* DMA engine has internal SRAM */
+       bool dma_has_sram;
+
+       /* DMA channel register width */
+       unsigned int dma_chan_width;
+
+       /* DMA descriptor shift */
+       unsigned int dma_desc_shift;
+};
+
+/*
+ * on board ethernet switch platform data
+ */
+#define ENETSW_MAX_PORT        8
+#define ENETSW_PORTS_6328 5 /* 4 FE PHY + 1 RGMII */
+#define ENETSW_PORTS_6368 6 /* 4 FE PHY + 2 RGMII */
+
+#define ENETSW_RGMII_PORT0     4
+
+struct bcm63xx_enetsw_port {
+       int             used;
+       int             phy_id;
+
+       int             bypass_link;
+       int             force_speed;
+       int             force_duplex_full;
+
+       const char      *name;
+};
+
+struct bcm63xx_enetsw_platform_data {
+       char mac_addr[ETH_ALEN];
+       int num_ports;
+       struct bcm63xx_enetsw_port used_ports[ENETSW_MAX_PORT];
+
+       /* DMA channel enable mask */
+       u32 dma_chan_en_mask;
+
+       /* DMA channel interrupt mask */
+       u32 dma_chan_int_mask;
+
+       /* DMA channel register width */
+       unsigned int dma_chan_width;
+
+       /* DMA engine has internal SRAM */
+       bool dma_has_sram;
 };
 
 int __init bcm63xx_enet_register(int unit,
                                 const struct bcm63xx_enet_platform_data *pd);
 
+int bcm63xx_enetsw_register(const struct bcm63xx_enetsw_platform_data *pd);
+
+enum bcm63xx_regs_enetdmac {
+       ENETDMAC_CHANCFG,
+       ENETDMAC_IR,
+       ENETDMAC_IRMASK,
+       ENETDMAC_MAXBURST,
+       ENETDMAC_BUFALLOC,
+       ENETDMAC_RSTART,
+       ENETDMAC_FC,
+       ENETDMAC_LEN,
+};
+
+static inline unsigned long bcm63xx_enetdmacreg(enum bcm63xx_regs_enetdmac reg)
+{
+#ifdef BCMCPU_RUNTIME_DETECT
+       extern const unsigned long *bcm63xx_regs_enetdmac;
+
+       return bcm63xx_regs_enetdmac[reg];
+#else
+#ifdef CONFIG_BCM63XX_CPU_6345
+       switch (reg) {
+       case ENETDMAC_CHANCFG:
+               return ENETDMA_6345_CHANCFG_REG;
+       case ENETDMAC_IR:
+               return ENETDMA_6345_IR_REG;
+       case ENETDMAC_IRMASK:
+               return ENETDMA_6345_IRMASK_REG;
+       case ENETDMAC_MAXBURST:
+               return ENETDMA_6345_MAXBURST_REG;
+       case ENETDMAC_BUFALLOC:
+               return ENETDMA_6345_BUFALLOC_REG;
+       case ENETDMAC_RSTART:
+               return ENETDMA_6345_RSTART_REG;
+       case ENETDMAC_FC:
+               return ENETDMA_6345_FC_REG;
+       case ENETDMAC_LEN:
+               return ENETDMA_6345_LEN_REG;
+       }
+#endif
+#if defined(CONFIG_BCM63XX_CPU_6328) || \
+       defined(CONFIG_BCM63XX_CPU_6338) || \
+       defined(CONFIG_BCM63XX_CPU_6348) || \
+       defined(CONFIG_BCM63XX_CPU_6358) || \
+       defined(CONFIG_BCM63XX_CPU_6362) || \
+       defined(CONFIG_BCM63XX_CPU_6368)
+       switch (reg) {
+       case ENETDMAC_CHANCFG:
+               return ENETDMAC_CHANCFG_REG;
+       case ENETDMAC_IR:
+               return ENETDMAC_IR_REG;
+       case ENETDMAC_IRMASK:
+               return ENETDMAC_IRMASK_REG;
+       case ENETDMAC_MAXBURST:
+               return ENETDMAC_MAXBURST_REG;
+       case ENETDMAC_BUFALLOC:
+       case ENETDMAC_RSTART:
+       case ENETDMAC_FC:
+       case ENETDMAC_LEN:
+               return 0;
+       }
+#endif
+#endif
+       return 0;
+}
+
+
 #endif /* ! BCM63XX_DEV_ENET_H_ */
index 3203fe4..eff7ca7 100644 (file)
 /*************************************************************************
  * _REG relative to RSET_ENETDMA
  *************************************************************************/
+#define ENETDMA_CHAN_WIDTH             0x10
+#define ENETDMA_6345_CHAN_WIDTH                0x40
 
 /* Controller Configuration Register */
 #define ENETDMA_CFG_REG                        (0x0)
 /* State Ram Word 4 */
 #define ENETDMA_SRAM4_REG(x)           (0x20c + (x) * 0x10)
 
+/* Broadcom 6345 ENET DMA definitions */
+#define ENETDMA_6345_CHANCFG_REG       (0x00)
+
+#define ENETDMA_6345_MAXBURST_REG      (0x40)
+
+#define ENETDMA_6345_RSTART_REG                (0x08)
+
+#define ENETDMA_6345_LEN_REG           (0x0C)
+
+#define ENETDMA_6345_IR_REG            (0x14)
+
+#define ENETDMA_6345_IRMASK_REG                (0x18)
+
+#define ENETDMA_6345_FC_REG            (0x1C)
+
+#define ENETDMA_6345_BUFALLOC_REG      (0x20)
+
+/* Shift down for EOP, SOP and WRAP bits */
+#define ENETDMA_6345_DESC_SHIFT                (3)
 
 /*************************************************************************
  * _REG relative to RSET_ENETDMAC
  *************************************************************************/
 
 /* Channel Configuration register */
-#define ENETDMAC_CHANCFG_REG(x)                ((x) * 0x10)
+#define ENETDMAC_CHANCFG_REG           (0x0)
 #define ENETDMAC_CHANCFG_EN_SHIFT      0
 #define ENETDMAC_CHANCFG_EN_MASK       (1 << ENETDMAC_CHANCFG_EN_SHIFT)
 #define ENETDMAC_CHANCFG_PKTHALT_SHIFT 1
 #define ENETDMAC_CHANCFG_PKTHALT_MASK  (1 << ENETDMAC_CHANCFG_PKTHALT_SHIFT)
 #define ENETDMAC_CHANCFG_BUFHALT_SHIFT 2
 #define ENETDMAC_CHANCFG_BUFHALT_MASK  (1 << ENETDMAC_CHANCFG_BUFHALT_SHIFT)
+#define ENETDMAC_CHANCFG_CHAINING_SHIFT        2
+#define ENETDMAC_CHANCFG_CHAINING_MASK (1 << ENETDMAC_CHANCFG_CHAINING_SHIFT)
+#define ENETDMAC_CHANCFG_WRAP_EN_SHIFT 3
+#define ENETDMAC_CHANCFG_WRAP_EN_MASK  (1 << ENETDMAC_CHANCFG_WRAP_EN_SHIFT)
+#define ENETDMAC_CHANCFG_FLOWC_EN_SHIFT        4
+#define ENETDMAC_CHANCFG_FLOWC_EN_MASK (1 << ENETDMAC_CHANCFG_FLOWC_EN_SHIFT)
 
 /* Interrupt Control/Status register */
-#define ENETDMAC_IR_REG(x)             (0x4 + (x) * 0x10)
+#define ENETDMAC_IR_REG                        (0x4)
 #define ENETDMAC_IR_BUFDONE_MASK       (1 << 0)
 #define ENETDMAC_IR_PKTDONE_MASK       (1 << 1)
 #define ENETDMAC_IR_NOTOWNER_MASK      (1 << 2)
 
 /* Interrupt Mask register */
-#define ENETDMAC_IRMASK_REG(x)         (0x8 + (x) * 0x10)
+#define ENETDMAC_IRMASK_REG            (0x8)
 
 /* Maximum Burst Length */
-#define ENETDMAC_MAXBURST_REG(x)       (0xc + (x) * 0x10)
+#define ENETDMAC_MAXBURST_REG          (0xc)
 
 
 /*************************************************************************
  *************************************************************************/
 
 /* Ring Start Address register */
-#define ENETDMAS_RSTART_REG(x)         ((x) * 0x10)
+#define ENETDMAS_RSTART_REG            (0x0)
 
 /* State Ram Word 2 */
-#define ENETDMAS_SRAM2_REG(x)          (0x4 + (x) * 0x10)
+#define ENETDMAS_SRAM2_REG             (0x4)
 
 /* State Ram Word 3 */
-#define ENETDMAS_SRAM3_REG(x)          (0x8 + (x) * 0x10)
+#define ENETDMAS_SRAM3_REG             (0x8)
 
 /* State Ram Word 4 */
-#define ENETDMAS_SRAM4_REG(x)          (0xc + (x) * 0x10)
+#define ENETDMAS_SRAM4_REG             (0xc)
 
 
 /*************************************************************************
  * _REG relative to RSET_ENETSW
  *************************************************************************/
 
+/* Port traffic control */
+#define ENETSW_PTCTRL_REG(x)           (0x0 + (x))
+#define ENETSW_PTCTRL_RXDIS_MASK       (1 << 0)
+#define ENETSW_PTCTRL_TXDIS_MASK       (1 << 1)
+
+/* Switch mode register */
+#define ENETSW_SWMODE_REG              (0xb)
+#define ENETSW_SWMODE_FWD_EN_MASK      (1 << 1)
+
+/* IMP override Register */
+#define ENETSW_IMPOV_REG               (0xe)
+#define ENETSW_IMPOV_FORCE_MASK                (1 << 7)
+#define ENETSW_IMPOV_TXFLOW_MASK       (1 << 5)
+#define ENETSW_IMPOV_RXFLOW_MASK       (1 << 4)
+#define ENETSW_IMPOV_1000_MASK         (1 << 3)
+#define ENETSW_IMPOV_100_MASK          (1 << 2)
+#define ENETSW_IMPOV_FDX_MASK          (1 << 1)
+#define ENETSW_IMPOV_LINKUP_MASK       (1 << 0)
+
+/* Port override Register */
+#define ENETSW_PORTOV_REG(x)           (0x58 + (x))
+#define ENETSW_PORTOV_ENABLE_MASK      (1 << 6)
+#define ENETSW_PORTOV_TXFLOW_MASK      (1 << 5)
+#define ENETSW_PORTOV_RXFLOW_MASK      (1 << 4)
+#define ENETSW_PORTOV_1000_MASK                (1 << 3)
+#define ENETSW_PORTOV_100_MASK         (1 << 2)
+#define ENETSW_PORTOV_FDX_MASK         (1 << 1)
+#define ENETSW_PORTOV_LINKUP_MASK      (1 << 0)
+
+/* MDIO control register */
+#define ENETSW_MDIOC_REG               (0xb0)
+#define ENETSW_MDIOC_EXT_MASK          (1 << 16)
+#define ENETSW_MDIOC_REG_SHIFT         20
+#define ENETSW_MDIOC_PHYID_SHIFT       25
+#define ENETSW_MDIOC_RD_MASK           (1 << 30)
+#define ENETSW_MDIOC_WR_MASK           (1 << 31)
+
+/* MDIO data register */
+#define ENETSW_MDIOD_REG               (0xb4)
+
+/* Global Management Configuration Register */
+#define ENETSW_GMCR_REG                        (0x200)
+#define ENETSW_GMCR_RST_MIB_MASK       (1 << 0)
+
 /* MIB register */
 #define ENETSW_MIB_REG(x)              (0x2800 + (x) * 4)
 #define ENETSW_MIB_REG_COUNT           47
 
+/* Jumbo control register port mask register */
+#define ENETSW_JMBCTL_PORT_REG         (0x4004)
+
+/* Jumbo control mib good frame register */
+#define ENETSW_JMBCTL_MAXSIZE_REG      (0x4008)
+
 
 /*************************************************************************
  * _REG relative to RSET_OHCI_PRIV
index 682bcf3..d9aee1a 100644 (file)
@@ -24,6 +24,7 @@ struct board_info {
        /* enabled feature/device */
        unsigned int    has_enet0:1;
        unsigned int    has_enet1:1;
+       unsigned int    has_enetsw:1;
        unsigned int    has_pci:1;
        unsigned int    has_pccard:1;
        unsigned int    has_ohci0:1;
@@ -36,6 +37,7 @@ struct board_info {
        /* ethernet config */
        struct bcm63xx_enet_platform_data enet0;
        struct bcm63xx_enet_platform_data enet1;
+       struct bcm63xx_enetsw_platform_data enetsw;
 
        /* USB config */
        struct bcm63xx_usbd_platform_data usbd;
index 3b21150..6a07992 100644 (file)
@@ -92,4 +92,6 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* _UAPI_ASM_SOCKET_H */
index 729a509..b7eccbd 100644 (file)
@@ -331,7 +331,8 @@ static int tx4939_netdev_event(struct notifier_block *this,
                               unsigned long event,
                               void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+
        if (event == NETDEV_CHANGE && netif_carrier_ok(dev)) {
                __u64 bit = 0;
                if (dev->irq == TXX9_IRQ_BASE + TX4939_IR_ETH(0))
index b4ce844..db80fd3 100644 (file)
@@ -74,4 +74,6 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* _ASM_SOCKET_H */
index d48a84f..8a2e6de 100644 (file)
@@ -345,9 +345,10 @@ no_context:
  */
 out_of_memory:
        up_read(&mm->mmap_sem);
-       printk(KERN_ALERT "VM: killing process %s\n", tsk->comm);
-       if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR)
-               do_exit(SIGKILL);
+       if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) {
+               pagefault_out_of_memory();
+               return;
+       }
        goto no_context;
 
 do_sigbus:
index e2bfafc..4a41f84 100644 (file)
@@ -267,10 +267,10 @@ out_of_memory:
        __asm__ __volatile__("l.nop 1");
 
        up_read(&mm->mmap_sem);
-       printk("VM: killing process %s\n", tsk->comm);
-       if (user_mode(regs))
-               do_exit(SIGKILL);
-       goto no_context;
+       if (!user_mode(regs))
+               goto no_context;
+       pagefault_out_of_memory();
+       return;
 
 do_sigbus:
        up_read(&mm->mmap_sem);
index 70c512a..f866fff 100644 (file)
@@ -73,6 +73,8 @@
 
 #define SO_SELECT_ERR_QUEUE    0x4026
 
+#define SO_LL                  0x4027
+
 /* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
  */
index bc3a0eb..3bf72cd 100644 (file)
@@ -668,7 +668,6 @@ config SBUS
 
 config FSL_SOC
        bool
-       select HAVE_CAN_FLEXCAN if NET && CAN
 
 config FSL_PCI
        bool
index a36daf3..405fb09 100644 (file)
@@ -81,4 +81,6 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* _ASM_POWERPC_SOCKET_H */
index 64f7bd5..9a0d24c 100644 (file)
@@ -975,16 +975,12 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
        hw_brk.type = (data & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL;
        hw_brk.len = 8;
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
-       if (ptrace_get_breakpoints(task) < 0)
-               return -ESRCH;
-
        bp = thread->ptrace_bps[0];
        if ((!data) || !(hw_brk.type & HW_BRK_TYPE_RDWR)) {
                if (bp) {
                        unregister_hw_breakpoint(bp);
                        thread->ptrace_bps[0] = NULL;
                }
-               ptrace_put_breakpoints(task);
                return 0;
        }
        if (bp) {
@@ -997,11 +993,9 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
 
                ret =  modify_user_hw_breakpoint(bp, &attr);
                if (ret) {
-                       ptrace_put_breakpoints(task);
                        return ret;
                }
                thread->ptrace_bps[0] = bp;
-               ptrace_put_breakpoints(task);
                thread->hw_brk = hw_brk;
                return 0;
        }
@@ -1016,12 +1010,9 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
                                               ptrace_triggered, NULL, task);
        if (IS_ERR(bp)) {
                thread->ptrace_bps[0] = NULL;
-               ptrace_put_breakpoints(task);
                return PTR_ERR(bp);
        }
 
-       ptrace_put_breakpoints(task);
-
 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
        task->thread.hw_brk = hw_brk;
 #else /* CONFIG_PPC_ADV_DEBUG_REGS */
@@ -1440,26 +1431,19 @@ static long ppc_set_hwdebug(struct task_struct *child,
        if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
                brk.type |= HW_BRK_TYPE_WRITE;
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
-       if (ptrace_get_breakpoints(child) < 0)
-               return -ESRCH;
-
        /*
         * Check if the request is for 'range' breakpoints. We can
         * support it if range < 8 bytes.
         */
-       if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE) {
+       if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE)
                len = bp_info->addr2 - bp_info->addr;
-       else if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)
+       else if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)
                len = 1;
-       else {
-               ptrace_put_breakpoints(child);
+       else
                return -EINVAL;
-       }
        bp = thread->ptrace_bps[0];
-       if (bp) {
-               ptrace_put_breakpoints(child);
+       if (bp)
                return -ENOSPC;
-       }
 
        /* Create a new breakpoint request if one doesn't exist already */
        hw_breakpoint_init(&attr);
@@ -1471,11 +1455,9 @@ static long ppc_set_hwdebug(struct task_struct *child,
                                               ptrace_triggered, NULL, child);
        if (IS_ERR(bp)) {
                thread->ptrace_bps[0] = NULL;
-               ptrace_put_breakpoints(child);
                return PTR_ERR(bp);
        }
 
-       ptrace_put_breakpoints(child);
        return 1;
 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
 
@@ -1519,16 +1501,12 @@ static long ppc_del_hwdebug(struct task_struct *child, long data)
                return -EINVAL;
 
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
-       if (ptrace_get_breakpoints(child) < 0)
-               return -ESRCH;
-
        bp = thread->ptrace_bps[0];
        if (bp) {
                unregister_hw_breakpoint(bp);
                thread->ptrace_bps[0] = NULL;
        } else
                ret = -ENOENT;
-       ptrace_put_breakpoints(child);
        return ret;
 #else /* CONFIG_HAVE_HW_BREAKPOINT */
        if (child->thread.hw_brk.address == 0)
index c427ae3..bf56e33 100644 (file)
@@ -650,8 +650,7 @@ void bpf_jit_compile(struct sk_filter *fp)
 
        proglen = cgctx.idx * 4;
        alloclen = proglen + FUNCTION_DESCR_SIZE;
-       image = module_alloc(max_t(unsigned int, alloclen,
-                                  sizeof(struct work_struct)));
+       image = module_alloc(alloclen);
        if (!image)
                goto out;
 
@@ -688,20 +687,8 @@ out:
        return;
 }
 
-static void jit_free_defer(struct work_struct *arg)
-{
-       module_free(NULL, arg);
-}
-
-/* run from softirq, we must use a work_struct to call
- * module_free() from process context
- */
 void bpf_jit_free(struct sk_filter *fp)
 {
-       if (fp->bpf_func != sk_run_filter) {
-               struct work_struct *work = (struct work_struct *)fp->bpf_func;
-
-               INIT_WORK(work, jit_free_defer);
-               schedule_work(work);
-       }
+       if (fp->bpf_func != sk_run_filter)
+               module_free(NULL, fp->bpf_func);
 }
index 2dacb30..0c5105f 100644 (file)
@@ -80,4 +80,6 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* _ASM_SOCKET_H */
index 47b600e..6b18fb0 100644 (file)
@@ -172,10 +172,10 @@ out_of_memory:
                down_read(&mm->mmap_sem);
                goto survive;
        }
-       printk("VM: killing process %s\n", tsk->comm);
-       if (user_mode(regs))
-               do_group_exit(SIGKILL);
-       goto no_context;
+       if (!user_mode(regs))
+               goto no_context;
+       pagefault_out_of_memory();
+       return;
 
 do_sigbus:
        up_read(&mm->mmap_sem);
index d71a0bc..4d94dff 100644 (file)
@@ -85,7 +85,7 @@ static struct sh_eth_plat_data sh7763_eth_pdata = {
 };
 
 static struct platform_device espt_eth_device = {
-       .name       = "sh-eth",
+       .name       = "sh7763-gether",
        .resource   = sh_eth_resources,
        .num_resources  = ARRAY_SIZE(sh_eth_resources),
        .dev        = {
index 41f8670..4f114d1 100644 (file)
@@ -82,7 +82,7 @@ static struct sh_eth_plat_data sh7757_eth0_pdata = {
 };
 
 static struct platform_device sh7757_eth0_device = {
-       .name           = "sh-eth",
+       .name           = "sh7757-ether",
        .resource       = sh_eth0_resources,
        .id             = 0,
        .num_resources  = ARRAY_SIZE(sh_eth0_resources),
@@ -111,7 +111,7 @@ static struct sh_eth_plat_data sh7757_eth1_pdata = {
 };
 
 static struct platform_device sh7757_eth1_device = {
-       .name           = "sh-eth",
+       .name           = "sh7757-ether",
        .resource       = sh_eth1_resources,
        .id             = 1,
        .num_resources  = ARRAY_SIZE(sh_eth1_resources),
@@ -157,7 +157,7 @@ static struct sh_eth_plat_data sh7757_eth_giga0_pdata = {
 };
 
 static struct platform_device sh7757_eth_giga0_device = {
-       .name           = "sh-eth",
+       .name           = "sh7757-gether",
        .resource       = sh_eth_giga0_resources,
        .id             = 2,
        .num_resources  = ARRAY_SIZE(sh_eth_giga0_resources),
@@ -192,7 +192,7 @@ static struct sh_eth_plat_data sh7757_eth_giga1_pdata = {
 };
 
 static struct platform_device sh7757_eth_giga1_device = {
-       .name           = "sh-eth",
+       .name           = "sh7757-gether",
        .resource       = sh_eth_giga1_resources,
        .id             = 3,
        .num_resources  = ARRAY_SIZE(sh_eth_giga1_resources),
index 764530c..61fade0 100644 (file)
@@ -165,8 +165,8 @@ static struct sh_eth_plat_data sh_eth_plat = {
 };
 
 static struct platform_device sh_eth_device = {
-       .name = "sh-eth",
-       .id     = 0,
+       .name = "sh7724-ether",
+       .id = 0,
        .dev = {
                .platform_data = &sh_eth_plat,
        },
index 9759d6b..658326f 100644 (file)
@@ -128,8 +128,8 @@ static struct resource sh_eth0_resources[] = {
 };
 
 static struct platform_device sh_eth0_device = {
-       .name = "sh-eth",
-       .id     = 0,
+       .name = "sh771x-ether",
+       .id = 0,
        .dev = {
                .platform_data = PHY_ID,
        },
@@ -151,8 +151,8 @@ static struct resource sh_eth1_resources[] = {
 };
 
 static struct platform_device sh_eth1_device = {
-       .name = "sh-eth",
-       .id     = 1,
+       .name = "sh771x-ether",
+       .id = 1,
        .dev = {
                .platform_data = PHY_ID,
        },
index 4010e63..b70180e 100644 (file)
@@ -380,8 +380,8 @@ static struct sh_eth_plat_data sh_eth_plat = {
 };
 
 static struct platform_device sh_eth_device = {
-       .name = "sh-eth",
-       .id     = 0,
+       .name = "sh7724-ether",
+       .id = 0,
        .dev = {
                .platform_data = &sh_eth_plat,
        },
index b7c7529..50ba481 100644 (file)
@@ -93,7 +93,7 @@ static struct sh_eth_plat_data sh7763_eth_pdata = {
 };
 
 static struct platform_device sh7763rdp_eth_device = {
-       .name       = "sh-eth",
+       .name       = "sh7763-gether",
        .resource   = sh_eth_resources,
        .num_resources  = ARRAY_SIZE(sh_eth_resources),
        .dev        = {
index e0b740c..bb11e19 100644 (file)
@@ -124,8 +124,8 @@ static struct resource eth_resources[] = {
 };
 
 static struct platform_device eth_device = {
-       .name = "sh-eth",
-       .id     = -1,
+       .name = "sh7619-ether",
+       .id = -1,
        .dev = {
                .platform_data = (void *)1,
        },
index 5f30f80..0128af3 100644 (file)
@@ -329,7 +329,7 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[HWBLK_IIC0]),
        CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[HWBLK_IIC1]),
        CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[HWBLK_MMC]),
-       CLKDEV_DEV_ID("sh-eth.0", &mstp_clks[HWBLK_ETHER]),
+       CLKDEV_DEV_ID("sh7724-ether.0", &mstp_clks[HWBLK_ETHER]),
        CLKDEV_CON_ID("atapi0", &mstp_clks[HWBLK_ATAPI]),
        CLKDEV_CON_ID("tpu0", &mstp_clks[HWBLK_TPU]),
        CLKDEV_CON_ID("irda0", &mstp_clks[HWBLK_IRDA]),
index deb683a..ed95015 100644 (file)
@@ -238,7 +238,7 @@ static struct clk_lookup lookups[] = {
        CLKDEV_CON_ID("adc0", &mstp_clks[MSTP313]),
        CLKDEV_CON_ID("mtu0", &mstp_clks[MSTP312]),
        CLKDEV_CON_ID("iebus0", &mstp_clks[MSTP304]),
-       CLKDEV_DEV_ID("sh-eth.0", &mstp_clks[MSTP114]),
+       CLKDEV_DEV_ID("sh7734-gether.0", &mstp_clks[MSTP114]),
        CLKDEV_CON_ID("rtc0", &mstp_clks[MSTP303]),
        CLKDEV_CON_ID("hif0", &mstp_clks[MSTP302]),
        CLKDEV_CON_ID("stif0", &mstp_clks[MSTP301]),
index 81f999a..668c816 100644 (file)
@@ -117,11 +117,7 @@ void user_enable_single_step(struct task_struct *child)
 
        set_tsk_thread_flag(child, TIF_SINGLESTEP);
 
-       if (ptrace_get_breakpoints(child) < 0)
-               return;
-
        set_single_step(child, pc);
-       ptrace_put_breakpoints(child);
 }
 
 void user_disable_single_step(struct task_struct *child)
index 89f49b6..b46c3fa 100644 (file)
@@ -70,6 +70,8 @@
 
 #define SO_SELECT_ERR_QUEUE    0x0029
 
+#define SO_LL                  0x0030
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
index d36a85e..9c7be59 100644 (file)
@@ -785,9 +785,7 @@ cond_branch:                        f_offset = addrs[i + filter[i].jf];
                        break;
                }
                if (proglen == oldproglen) {
-                       image = module_alloc(max_t(unsigned int,
-                                                  proglen,
-                                                  sizeof(struct work_struct)));
+                       image = module_alloc(proglen);
                        if (!image)
                                goto out;
                }
@@ -806,20 +804,8 @@ out:
        return;
 }
 
-static void jit_free_defer(struct work_struct *arg)
-{
-       module_free(NULL, arg);
-}
-
-/* run from softirq, we must use a work_struct to call
- * module_free() from process context
- */
 void bpf_jit_free(struct sk_filter *fp)
 {
-       if (fp->bpf_func != sk_run_filter) {
-               struct work_struct *work = (struct work_struct *)fp->bpf_func;
-
-               INIT_WORK(work, jit_free_defer);
-               schedule_work(work);
-       }
+       if (fp->bpf_func != sk_run_filter)
+               module_free(NULL, fp->bpf_func);
 }
index 3d2b81c..f7f99f9 100644 (file)
@@ -573,10 +573,10 @@ out_of_memory:
                down_read(&mm->mmap_sem);
                goto survive;
        }
-       pr_alert("VM: killing process %s\n", tsk->comm);
-       if (!is_kernel_mode)
-               do_group_exit(SIGKILL);
-       goto no_context;
+       if (is_kernel_mode)
+               goto no_context;
+       pagefault_out_of_memory();
+       return 0;
 
 do_sigbus:
        up_read(&mm->mmap_sem);
index c944769..778ebba 100644 (file)
@@ -51,16 +51,6 @@ void arch_cpu_idle(void)
        local_irq_enable();
 }
 
-static char reboot_mode = 'h';
-
-int __init reboot_setup(char *str)
-{
-       reboot_mode = str[0];
-       return 1;
-}
-
-__setup("reboot=", reboot_setup);
-
 void machine_halt(void)
 {
        gpio_set_value(GPO_SOFT_OFF, 0);
@@ -88,7 +78,7 @@ void machine_restart(char *cmd)
         * we may need it to insert some 1:1 mappings so that
         * soft boot works.
         */
-       setup_mm_for_reboot(reboot_mode);
+       setup_mm_for_reboot();
 
        /* Clean and invalidate caches */
        flush_cache_all();
@@ -102,7 +92,7 @@ void machine_restart(char *cmd)
        /*
         * Now handle reboot code.
         */
-       if (reboot_mode == 's') {
+       if (reboot_mode == REBOOT_SOFT) {
                /* Jump into ROM at address 0xffff0000 */
                cpu_reset(VECTORS_BASE);
        } else {
index 30f749d..f5c51b8 100644 (file)
@@ -22,7 +22,7 @@ extern void puv3_ps2_init(void);
 extern void pci_puv3_preinit(void);
 extern void __init puv3_init_gpio(void);
 
-extern void setup_mm_for_reboot(char mode);
+extern void setup_mm_for_reboot(void);
 
 extern char __stubs_start[], __stubs_end[];
 extern char __vectors_start[], __vectors_end[];
index 43c20b4..4f5a532 100644 (file)
@@ -445,7 +445,7 @@ void __init paging_init(void)
  * the user-mode pages.  This will then ensure that we have predictable
  * results when turning the mmu off
  */
-void setup_mm_for_reboot(char mode)
+void setup_mm_for_reboot(void)
 {
        unsigned long base_pmdval;
        pgd_t *pgd;
index 265c672..b32ebf9 100644 (file)
@@ -65,6 +65,7 @@ config X86
        select HAVE_KERNEL_LZMA
        select HAVE_KERNEL_XZ
        select HAVE_KERNEL_LZO
+       select HAVE_KERNEL_LZ4
        select HAVE_HW_BREAKPOINT
        select HAVE_MIXED_BREAKPOINTS_REGS
        select PERF_EVENTS
index 5ef205c..dcd90df 100644 (file)
@@ -4,7 +4,8 @@
 # create a compressed vmlinux image from the original vmlinux
 #
 
-targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma vmlinux.bin.xz vmlinux.bin.lzo
+targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
+       vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4
 
 KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2
 KBUILD_CFLAGS += -fno-strict-aliasing -fPIC
@@ -63,12 +64,15 @@ $(obj)/vmlinux.bin.xz: $(vmlinux.bin.all-y) FORCE
        $(call if_changed,xzkern)
 $(obj)/vmlinux.bin.lzo: $(vmlinux.bin.all-y) FORCE
        $(call if_changed,lzo)
+$(obj)/vmlinux.bin.lz4: $(vmlinux.bin.all-y) FORCE
+       $(call if_changed,lz4)
 
 suffix-$(CONFIG_KERNEL_GZIP)   := gz
 suffix-$(CONFIG_KERNEL_BZIP2)  := bz2
 suffix-$(CONFIG_KERNEL_LZMA)   := lzma
 suffix-$(CONFIG_KERNEL_XZ)     := xz
 suffix-$(CONFIG_KERNEL_LZO)    := lzo
+suffix-$(CONFIG_KERNEL_LZ4)    := lz4
 
 quiet_cmd_mkpiggy = MKPIGGY $@
       cmd_mkpiggy = $(obj)/mkpiggy $< > $@ || ( rm -f $@ ; false )
index 7cb56c6..0319c88 100644 (file)
@@ -145,6 +145,10 @@ static int lines, cols;
 #include "../../../../lib/decompress_unlzo.c"
 #endif
 
+#ifdef CONFIG_KERNEL_LZ4
+#include "../../../../lib/decompress_unlz4.c"
+#endif
+
 static void scroll(void)
 {
        int i;
index 75ce3f4..77a99ac 100644 (file)
@@ -1,18 +1,6 @@
 #ifndef _ASM_X86_EMERGENCY_RESTART_H
 #define _ASM_X86_EMERGENCY_RESTART_H
 
-enum reboot_type {
-       BOOT_TRIPLE = 't',
-       BOOT_KBD = 'k',
-       BOOT_BIOS = 'b',
-       BOOT_ACPI = 'a',
-       BOOT_EFI = 'e',
-       BOOT_CF9 = 'p',
-       BOOT_CF9_COND = 'q',
-};
-
-extern enum reboot_type reboot_type;
-
 extern void machine_emergency_restart(void);
 
 #endif /* _ASM_X86_EMERGENCY_RESTART_H */
index d8e8eef..34f69cb 100644 (file)
@@ -345,4 +345,11 @@ extern bool xen_biovec_phys_mergeable(const struct bio_vec *vec1,
 
 #define IO_SPACE_LIMIT 0xffff
 
+#ifdef CONFIG_MTRR
+extern int __must_check arch_phys_wc_add(unsigned long base,
+                                        unsigned long size);
+extern void arch_phys_wc_del(int handle);
+#define arch_phys_wc_add arch_phys_wc_add
+#endif
+
 #endif /* _ASM_X86_IO_H */
index e235582..f768f62 100644 (file)
 #include <uapi/asm/mtrr.h>
 
 
-/*  The following functions are for use by other drivers  */
+/*
+ * The following functions are for use by other drivers that cannot use
+ * arch_phys_wc_add and arch_phys_wc_del.
+ */
 # ifdef CONFIG_MTRR
 extern u8 mtrr_type_lookup(u64 addr, u64 end);
 extern void mtrr_save_fixed_ranges(void *);
@@ -45,6 +48,7 @@ extern void mtrr_aps_init(void);
 extern void mtrr_bp_restore(void);
 extern int mtrr_trim_uncached_memory(unsigned long end_pfn);
 extern int amd_special_default_mtrr(void);
+extern int phys_wc_to_mtrr_index(int handle);
 #  else
 static inline u8 mtrr_type_lookup(u64 addr, u64 end)
 {
@@ -80,6 +84,10 @@ static inline int mtrr_trim_uncached_memory(unsigned long end_pfn)
 static inline void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi)
 {
 }
+static inline int phys_wc_to_mtrr_index(int handle)
+{
+       return -1;
+}
 
 #define mtrr_ap_init() do {} while (0)
 #define mtrr_bp_init() do {} while (0)
index 39cc7f7..63092af 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/kdebug.h>
 #include <linux/delay.h>
 #include <linux/crash_dump.h>
+#include <linux/reboot.h>
 
 #include <asm/uv/uv_mmrs.h>
 #include <asm/uv/uv_hub.h>
@@ -36,7 +37,6 @@
 #include <asm/ipi.h>
 #include <asm/smp.h>
 #include <asm/x86_init.h>
-#include <asm/emergency-restart.h>
 #include <asm/nmi.h>
 
 /* BMC sets a bit this MMR non-zero before sending an NMI */
index ca22b73..f961de9 100644 (file)
 #include <asm/e820.h>
 #include <asm/mtrr.h>
 #include <asm/msr.h>
+#include <asm/pat.h>
 
 #include "mtrr.h"
 
+/* arch_phys_wc_add returns an MTRR register index plus this offset. */
+#define MTRR_TO_PHYS_WC_OFFSET 1000
+
 u32 num_var_ranges;
 
 unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES];
@@ -525,6 +529,73 @@ int mtrr_del(int reg, unsigned long base, unsigned long size)
 }
 EXPORT_SYMBOL(mtrr_del);
 
+/**
+ * arch_phys_wc_add - add a WC MTRR and handle errors if PAT is unavailable
+ * @base: Physical base address
+ * @size: Size of region
+ *
+ * If PAT is available, this does nothing.  If PAT is unavailable, it
+ * attempts to add a WC MTRR covering size bytes starting at base and
+ * logs an error if this fails.
+ *
+ * Drivers must store the return value to pass to mtrr_del_wc_if_needed,
+ * but drivers should not try to interpret that return value.
+ */
+int arch_phys_wc_add(unsigned long base, unsigned long size)
+{
+       int ret;
+
+       if (pat_enabled)
+               return 0;  /* Success!  (We don't need to do anything.) */
+
+       ret = mtrr_add(base, size, MTRR_TYPE_WRCOMB, true);
+       if (ret < 0) {
+               pr_warn("Failed to add WC MTRR for [%p-%p]; performance may suffer.",
+                       (void *)base, (void *)(base + size - 1));
+               return ret;
+       }
+       return ret + MTRR_TO_PHYS_WC_OFFSET;
+}
+EXPORT_SYMBOL(arch_phys_wc_add);
+
+/*
+ * arch_phys_wc_del - undoes arch_phys_wc_add
+ * @handle: Return value from arch_phys_wc_add
+ *
+ * This cleans up after mtrr_add_wc_if_needed.
+ *
+ * The API guarantees that mtrr_del_wc_if_needed(error code) and
+ * mtrr_del_wc_if_needed(0) do nothing.
+ */
+void arch_phys_wc_del(int handle)
+{
+       if (handle >= 1) {
+               WARN_ON(handle < MTRR_TO_PHYS_WC_OFFSET);
+               mtrr_del(handle - MTRR_TO_PHYS_WC_OFFSET, 0, 0);
+       }
+}
+EXPORT_SYMBOL(arch_phys_wc_del);
+
+/*
+ * phys_wc_to_mtrr_index - translates arch_phys_wc_add's return value
+ * @handle: Return value from arch_phys_wc_add
+ *
+ * This will turn the return value from arch_phys_wc_add into an mtrr
+ * index suitable for debugging.
+ *
+ * Note: There is no legitimate use for this function, except possibly
+ * in printk line.  Alas there is an illegitimate use in some ancient
+ * drm ioctls.
+ */
+int phys_wc_to_mtrr_index(int handle)
+{
+       if (handle < MTRR_TO_PHYS_WC_OFFSET)
+               return -1;
+       else
+               return handle - MTRR_TO_PHYS_WC_OFFSET;
+}
+EXPORT_SYMBOL_GPL(phys_wc_to_mtrr_index);
+
 /*
  * HACK ALERT!
  * These should be called implicitly, but we can't yet until all the initcall
index 02f0763..f66ff16 100644 (file)
@@ -393,6 +393,9 @@ void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
                unregister_hw_breakpoint(t->ptrace_bps[i]);
                t->ptrace_bps[i] = NULL;
        }
+
+       t->debugreg6 = 0;
+       t->ptrace_dr7 = 0;
 }
 
 void hw_breakpoint_restore(void)
index 29a8120..7461f50 100644 (file)
@@ -601,30 +601,48 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[])
        return dr7;
 }
 
-static int
-ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
-                        struct task_struct *tsk, int disabled)
+static int ptrace_fill_bp_fields(struct perf_event_attr *attr,
+                                       int len, int type, bool disabled)
+{
+       int err, bp_len, bp_type;
+
+       err = arch_bp_generic_fields(len, type, &bp_len, &bp_type);
+       if (!err) {
+               attr->bp_len = bp_len;
+               attr->bp_type = bp_type;
+               attr->disabled = disabled;
+       }
+
+       return err;
+}
+
+static struct perf_event *
+ptrace_register_breakpoint(struct task_struct *tsk, int len, int type,
+                               unsigned long addr, bool disabled)
 {
-       int err;
-       int gen_len, gen_type;
        struct perf_event_attr attr;
+       int err;
 
-       /*
-        * We should have at least an inactive breakpoint at this
-        * slot. It means the user is writing dr7 without having
-        * written the address register first
-        */
-       if (!bp)
-               return -EINVAL;
+       ptrace_breakpoint_init(&attr);
+       attr.bp_addr = addr;
 
-       err = arch_bp_generic_fields(len, type, &gen_len, &gen_type);
+       err = ptrace_fill_bp_fields(&attr, len, type, disabled);
        if (err)
-               return err;
+               return ERR_PTR(err);
+
+       return register_user_hw_breakpoint(&attr, ptrace_triggered,
+                                                NULL, tsk);
+}
 
-       attr = bp->attr;
-       attr.bp_len = gen_len;
-       attr.bp_type = gen_type;
-       attr.disabled = disabled;
+static int ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
+                                       int disabled)
+{
+       struct perf_event_attr attr = bp->attr;
+       int err;
+
+       err = ptrace_fill_bp_fields(&attr, len, type, disabled);
+       if (err)
+               return err;
 
        return modify_user_hw_breakpoint(bp, &attr);
 }
@@ -634,67 +652,50 @@ ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
  */
 static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
 {
-       struct thread_struct *thread = &(tsk->thread);
+       struct thread_struct *thread = &tsk->thread;
        unsigned long old_dr7;
-       int i, orig_ret = 0, rc = 0;
-       int enabled, second_pass = 0;
-       unsigned len, type;
-       struct perf_event *bp;
-
-       if (ptrace_get_breakpoints(tsk) < 0)
-               return -ESRCH;
+       bool second_pass = false;
+       int i, rc, ret = 0;
 
        data &= ~DR_CONTROL_RESERVED;
        old_dr7 = ptrace_get_dr7(thread->ptrace_bps);
+
 restore:
-       /*
-        * Loop through all the hardware breakpoints, making the
-        * appropriate changes to each.
-        */
+       rc = 0;
        for (i = 0; i < HBP_NUM; i++) {
-               enabled = decode_dr7(data, i, &len, &type);
-               bp = thread->ptrace_bps[i];
-
-               if (!enabled) {
-                       if (bp) {
-                               /*
-                                * Don't unregister the breakpoints right-away,
-                                * unless all register_user_hw_breakpoint()
-                                * requests have succeeded. This prevents
-                                * any window of opportunity for debug
-                                * register grabbing by other users.
-                                */
-                               if (!second_pass)
-                                       continue;
-
-                               rc = ptrace_modify_breakpoint(bp, len, type,
-                                                             tsk, 1);
-                               if (rc)
-                                       break;
+               unsigned len, type;
+               bool disabled = !decode_dr7(data, i, &len, &type);
+               struct perf_event *bp = thread->ptrace_bps[i];
+
+               if (!bp) {
+                       if (disabled)
+                               continue;
+
+                       bp = ptrace_register_breakpoint(tsk,
+                                       len, type, 0, disabled);
+                       if (IS_ERR(bp)) {
+                               rc = PTR_ERR(bp);
+                               break;
                        }
+
+                       thread->ptrace_bps[i] = bp;
                        continue;
                }
 
-               rc = ptrace_modify_breakpoint(bp, len, type, tsk, 0);
+               rc = ptrace_modify_breakpoint(bp, len, type, disabled);
                if (rc)
                        break;
        }
-       /*
-        * Make a second pass to free the remaining unused breakpoints
-        * or to restore the original breakpoints if an error occurred.
-        */
-       if (!second_pass) {
-               second_pass = 1;
-               if (rc < 0) {
-                       orig_ret = rc;
-                       data = old_dr7;
-               }
+
+       /* Restore if the first pass failed, second_pass shouldn't fail. */
+       if (rc && !WARN_ON(second_pass)) {
+               ret = rc;
+               data = old_dr7;
+               second_pass = true;
                goto restore;
        }
 
-       ptrace_put_breakpoints(tsk);
-
-       return ((orig_ret < 0) ? orig_ret : rc);
+       return ret;
 }
 
 /*
@@ -702,25 +703,17 @@ restore:
  */
 static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n)
 {
-       struct thread_struct *thread = &(tsk->thread);
+       struct thread_struct *thread = &tsk->thread;
        unsigned long val = 0;
 
        if (n < HBP_NUM) {
-               struct perf_event *bp;
+               struct perf_event *bp = thread->ptrace_bps[n];
 
-               if (ptrace_get_breakpoints(tsk) < 0)
-                       return -ESRCH;
-
-               bp = thread->ptrace_bps[n];
-               if (!bp)
-                       val = 0;
-               else
+               if (bp)
                        val = bp->hw.info.address;
-
-               ptrace_put_breakpoints(tsk);
        } else if (n == 6) {
                val = thread->debugreg6;
-        } else if (n == 7) {
+       } else if (n == 7) {
                val = thread->ptrace_dr7;
        }
        return val;
@@ -729,29 +722,14 @@ static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n)
 static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
                                      unsigned long addr)
 {
-       struct perf_event *bp;
        struct thread_struct *t = &tsk->thread;
-       struct perf_event_attr attr;
+       struct perf_event *bp = t->ptrace_bps[nr];
        int err = 0;
 
-       if (ptrace_get_breakpoints(tsk) < 0)
-               return -ESRCH;
-
-       if (!t->ptrace_bps[nr]) {
-               ptrace_breakpoint_init(&attr);
-               /*
-                * Put stub len and type to register (reserve) an inactive but
-                * correct bp
-                */
-               attr.bp_addr = addr;
-               attr.bp_len = HW_BREAKPOINT_LEN_1;
-               attr.bp_type = HW_BREAKPOINT_W;
-               attr.disabled = 1;
-
-               bp = register_user_hw_breakpoint(&attr, ptrace_triggered,
-                                                NULL, tsk);
-
+       if (!bp) {
                /*
+                * Put stub len and type to create an inactive but correct bp.
+                *
                 * CHECKME: the previous code returned -EIO if the addr wasn't
                 * a valid task virtual addr. The new one will return -EINVAL in
                 *  this case.
@@ -760,22 +738,20 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
                 * writing for the user. And anyway this is the previous
                 * behaviour.
                 */
-               if (IS_ERR(bp)) {
+               bp = ptrace_register_breakpoint(tsk,
+                               X86_BREAKPOINT_LEN_1, X86_BREAKPOINT_WRITE,
+                               addr, true);
+               if (IS_ERR(bp))
                        err = PTR_ERR(bp);
-                       goto put;
-               }
-
-               t->ptrace_bps[nr] = bp;
+               else
+                       t->ptrace_bps[nr] = bp;
        } else {
-               bp = t->ptrace_bps[nr];
+               struct perf_event_attr attr = bp->attr;
 
-               attr = bp->attr;
                attr.bp_addr = addr;
                err = modify_user_hw_breakpoint(bp, &attr);
        }
 
-put:
-       ptrace_put_breakpoints(tsk);
        return err;
 }
 
@@ -785,30 +761,20 @@ put:
 static int ptrace_set_debugreg(struct task_struct *tsk, int n,
                               unsigned long val)
 {
-       struct thread_struct *thread = &(tsk->thread);
-       int rc = 0;
-
+       struct thread_struct *thread = &tsk->thread;
        /* There are no DR4 or DR5 registers */
-       if (n == 4 || n == 5)
-               return -EIO;
+       int rc = -EIO;
 
-       if (n == 6) {
-               thread->debugreg6 = val;
-               goto ret_path;
-       }
        if (n < HBP_NUM) {
                rc = ptrace_set_breakpoint_addr(tsk, n, val);
-               if (rc)
-                       return rc;
-       }
-       /* All that's left is DR7 */
-       if (n == 7) {
+       } else if (n == 6) {
+               thread->debugreg6 = val;
+               rc = 0;
+       } else if (n == 7) {
                rc = ptrace_write_dr7(tsk, val);
                if (!rc)
                        thread->ptrace_dr7 = val;
        }
-
-ret_path:
        return rc;
 }
 
index 76fa1e9..563ed91 100644 (file)
@@ -36,22 +36,6 @@ void (*pm_power_off)(void);
 EXPORT_SYMBOL(pm_power_off);
 
 static const struct desc_ptr no_idt = {};
-static int reboot_mode;
-enum reboot_type reboot_type = BOOT_ACPI;
-int reboot_force;
-
-/*
- * This variable is used privately to keep track of whether or not
- * reboot_type is still set to its default value (i.e., reboot= hasn't
- * been set on the command line).  This is needed so that we can
- * suppress DMI scanning for reboot quirks.  Without it, it's
- * impossible to override a faulty reboot quirk without recompiling.
- */
-static int reboot_default = 1;
-
-#ifdef CONFIG_SMP
-static int reboot_cpu = -1;
-#endif
 
 /*
  * This is set if we need to go through the 'emergency' path.
@@ -63,79 +47,6 @@ static int reboot_emergency;
 /* This is set by the PCI code if either type 1 or type 2 PCI is detected */
 bool port_cf9_safe = false;
 
-/*
- * reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci]
- * warm   Don't set the cold reboot flag
- * cold   Set the cold reboot flag
- * bios   Reboot by jumping through the BIOS
- * smp    Reboot by executing reset on BSP or other CPU
- * triple Force a triple fault (init)
- * kbd    Use the keyboard controller. cold reset (default)
- * acpi   Use the RESET_REG in the FADT
- * efi    Use efi reset_system runtime service
- * pci    Use the so-called "PCI reset register", CF9
- * force  Avoid anything that could hang.
- */
-static int __init reboot_setup(char *str)
-{
-       for (;;) {
-               /*
-                * Having anything passed on the command line via
-                * reboot= will cause us to disable DMI checking
-                * below.
-                */
-               reboot_default = 0;
-
-               switch (*str) {
-               case 'w':
-                       reboot_mode = 0x1234;
-                       break;
-
-               case 'c':
-                       reboot_mode = 0;
-                       break;
-
-#ifdef CONFIG_SMP
-               case 's':
-                       if (isdigit(*(str+1))) {
-                               reboot_cpu = (int) (*(str+1) - '0');
-                               if (isdigit(*(str+2)))
-                                       reboot_cpu = reboot_cpu*10 + (int)(*(str+2) - '0');
-                       }
-                       /*
-                        * We will leave sorting out the final value
-                        * when we are ready to reboot, since we might not
-                        * have detected BSP APIC ID or smp_num_cpu
-                        */
-                       break;
-#endif /* CONFIG_SMP */
-
-               case 'b':
-               case 'a':
-               case 'k':
-               case 't':
-               case 'e':
-               case 'p':
-                       reboot_type = *str;
-                       break;
-
-               case 'f':
-                       reboot_force = 1;
-                       break;
-               }
-
-               str = strchr(str, ',');
-               if (str)
-                       str++;
-               else
-                       break;
-       }
-       return 1;
-}
-
-__setup("reboot=", reboot_setup);
-
-
 /*
  * Reboot options and system auto-detection code provided by
  * Dell Inc. so their systems "just work". :-)
@@ -536,6 +447,7 @@ static void native_machine_emergency_restart(void)
        int i;
        int attempt = 0;
        int orig_reboot_type = reboot_type;
+       unsigned short mode;
 
        if (reboot_emergency)
                emergency_vmx_disable_all();
@@ -543,7 +455,8 @@ static void native_machine_emergency_restart(void)
        tboot_shutdown(TB_SHUTDOWN_REBOOT);
 
        /* Tell the BIOS if we want cold or warm reboot */
-       *((unsigned short *)__va(0x472)) = reboot_mode;
+       mode = reboot_mode == REBOOT_WARM ? 0x1234 : 0;
+       *((unsigned short *)__va(0x472)) = mode;
 
        for (;;) {
                /* Could also try the reset bit in the Hammer NB */
@@ -585,7 +498,7 @@ static void native_machine_emergency_restart(void)
 
                case BOOT_EFI:
                        if (efi_enabled(EFI_RUNTIME_SERVICES))
-                               efi.reset_system(reboot_mode ?
+                               efi.reset_system(reboot_mode == REBOOT_WARM ?
                                                 EFI_RESET_WARM :
                                                 EFI_RESET_COLD,
                                                 EFI_SUCCESS, 0, NULL);
@@ -614,26 +527,10 @@ void native_machine_shutdown(void)
 {
        /* Stop the cpus and apics */
 #ifdef CONFIG_SMP
-
-       /* The boot cpu is always logical cpu 0 */
-       int reboot_cpu_id = 0;
-
-       /* See if there has been given a command line override */
-       if ((reboot_cpu != -1) && (reboot_cpu < nr_cpu_ids) &&
-               cpu_online(reboot_cpu))
-               reboot_cpu_id = reboot_cpu;
-
-       /* Make certain the cpu I'm about to reboot on is online */
-       if (!cpu_online(reboot_cpu_id))
-               reboot_cpu_id = smp_processor_id();
-
-       /* Make certain I only run on the appropriate processor */
-       set_cpus_allowed_ptr(current, cpumask_of(reboot_cpu_id));
-
        /*
-        * O.K Now that I'm on the appropriate processor, stop all of the
-        * others. Also disable the local irq to not receive the per-cpu
-        * timer interrupt which may trigger scheduler's load balance.
+        * Stop all of the others. Also disable the local irq to
+        * not receive the per-cpu timer interrupt which may trigger
+        * scheduler's load balance.
         */
        local_irq_disable();
        stop_other_cpus();
index 17fda6a..dfa537a 100644 (file)
@@ -240,7 +240,6 @@ static void pgd_mop_up_pmds(struct mm_struct *mm, pgd_t *pgdp)
 static void pgd_prepopulate_pmd(struct mm_struct *mm, pgd_t *pgd, pmd_t *pmds[])
 {
        pud_t *pud;
-       unsigned long addr;
        int i;
 
        if (PREALLOCATED_PMDS == 0) /* Work around gcc-3.4.x bug */
@@ -248,8 +247,7 @@ static void pgd_prepopulate_pmd(struct mm_struct *mm, pgd_t *pgd, pmd_t *pmds[])
 
        pud = pud_offset(pgd, 0);
 
-       for (addr = i = 0; i < PREALLOCATED_PMDS;
-            i++, pud++, addr += PUD_SIZE) {
+       for (i = 0; i < PREALLOCATED_PMDS; i++, pud++) {
                pmd_t *pmd = pmds[i];
 
                if (i >= KERNEL_PGD_BOUNDARY)
index f66b540..79c216a 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/netdevice.h>
 #include <linux/filter.h>
 #include <linux/if_vlan.h>
+#include <linux/random.h>
 
 /*
  * Conventions :
@@ -144,6 +145,39 @@ static int pkt_type_offset(void)
        return -1;
 }
 
+struct bpf_binary_header {
+       unsigned int    pages;
+       /* Note : for security reasons, bpf code will follow a randomly
+        * sized amount of int3 instructions
+        */
+       u8              image[];
+};
+
+static struct bpf_binary_header *bpf_alloc_binary(unsigned int proglen,
+                                                 u8 **image_ptr)
+{
+       unsigned int sz, hole;
+       struct bpf_binary_header *header;
+
+       /* Most of BPF filters are really small,
+        * but if some of them fill a page, allow at least
+        * 128 extra bytes to insert a random section of int3
+        */
+       sz = round_up(proglen + sizeof(*header) + 128, PAGE_SIZE);
+       header = module_alloc(sz);
+       if (!header)
+               return NULL;
+
+       memset(header, 0xcc, sz); /* fill whole space with int3 instructions */
+
+       header->pages = sz / PAGE_SIZE;
+       hole = sz - (proglen + sizeof(*header));
+
+       /* insert a random number of int3 instructions before BPF code */
+       *image_ptr = &header->image[prandom_u32() % hole];
+       return header;
+}
+
 void bpf_jit_compile(struct sk_filter *fp)
 {
        u8 temp[64];
@@ -153,6 +187,7 @@ void bpf_jit_compile(struct sk_filter *fp)
        int t_offset, f_offset;
        u8 t_op, f_op, seen = 0, pass;
        u8 *image = NULL;
+       struct bpf_binary_header *header = NULL;
        u8 *func;
        int pc_ret0 = -1; /* bpf index of first RET #0 instruction (if any) */
        unsigned int cleanup_addr; /* epilogue code offset */
@@ -693,7 +728,7 @@ cond_branch:                        f_offset = addrs[i + filter[i].jf] - addrs[i];
                                if (unlikely(proglen + ilen > oldproglen)) {
                                        pr_err("bpb_jit_compile fatal error\n");
                                        kfree(addrs);
-                                       module_free(NULL, image);
+                                       module_free(NULL, header);
                                        return;
                                }
                                memcpy(image + proglen, temp, ilen);
@@ -717,10 +752,8 @@ cond_branch:                       f_offset = addrs[i + filter[i].jf] - addrs[i];
                        break;
                }
                if (proglen == oldproglen) {
-                       image = module_alloc(max_t(unsigned int,
-                                                  proglen,
-                                                  sizeof(struct work_struct)));
-                       if (!image)
+                       header = bpf_alloc_binary(proglen, &image);
+                       if (!header)
                                goto out;
                }
                oldproglen = proglen;
@@ -730,7 +763,8 @@ cond_branch:                        f_offset = addrs[i + filter[i].jf] - addrs[i];
                bpf_jit_dump(flen, proglen, pass, image);
 
        if (image) {
-               bpf_flush_icache(image, image + proglen);
+               bpf_flush_icache(header, image + proglen);
+               set_memory_ro((unsigned long)header, header->pages);
                fp->bpf_func = (void *)image;
        }
 out:
@@ -738,20 +772,13 @@ out:
        return;
 }
 
-static void jit_free_defer(struct work_struct *arg)
-{
-       module_free(NULL, arg);
-}
-
-/* run from softirq, we must use a work_struct to call
- * module_free() from process context
- */
 void bpf_jit_free(struct sk_filter *fp)
 {
        if (fp->bpf_func != sk_run_filter) {
-               struct work_struct *work = (struct work_struct *)fp->bpf_func;
+               unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
+               struct bpf_binary_header *header = (void *)addr;
 
-               INIT_WORK(work, jit_free_defer);
-               schedule_work(work);
+               set_memory_rw(addr, header->pages);
+               module_free(NULL, header);
        }
 }
index a8f44f5..b21ace4 100644 (file)
@@ -85,4 +85,6 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* _XTENSA_SOCKET_H */
index 75a54e1..4cebb2f 100644 (file)
@@ -68,6 +68,17 @@ config ACORN_PARTITION_RISCIX
          of machines called RISCiX.  If you say 'Y' here, Linux will be able
          to read disks partitioned under RISCiX.
 
+config AIX_PARTITION
+       bool "AIX basic partition table support" if PARTITION_ADVANCED
+       help
+         Say Y here if you would like to be able to read the hard disk
+         partition table format used by IBM or Motorola PowerPC machines
+         running AIX.  AIX actually uses a Logical Volume Manager, where
+         "logical volumes" can be spread across one or multiple disks,
+         but this driver works only for the simple case of partitions which
+         are contiguous.
+         Otherwise, say N.
+
 config OSF_PARTITION
        bool "Alpha OSF partition support" if PARTITION_ADVANCED
        default y if ALPHA
index 03af8ea..2be4d7b 100644 (file)
@@ -7,6 +7,7 @@ obj-$(CONFIG_BLOCK) := check.o
 obj-$(CONFIG_ACORN_PARTITION) += acorn.o
 obj-$(CONFIG_AMIGA_PARTITION) += amiga.o
 obj-$(CONFIG_ATARI_PARTITION) += atari.o
+obj-$(CONFIG_AIX_PARTITION) += aix.o
 obj-$(CONFIG_MAC_PARTITION) += mac.o
 obj-$(CONFIG_LDM_PARTITION) += ldm.o
 obj-$(CONFIG_MSDOS_PARTITION) += msdos.o
diff --git a/block/partitions/aix.c b/block/partitions/aix.c
new file mode 100644 (file)
index 0000000..43be471
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ *  fs/partitions/aix.c
+ *
+ *  Copyright (C) 2012-2013 Philippe De Muyter <phdm@macqel.be>
+ */
+
+#include "check.h"
+#include "aix.h"
+
+struct lvm_rec {
+       char lvm_id[4]; /* "_LVM" */
+       char reserved4[16];
+       __be32 lvmarea_len;
+       __be32 vgda_len;
+       __be32 vgda_psn[2];
+       char reserved36[10];
+       __be16 pp_size; /* log2(pp_size) */
+       char reserved46[12];
+       __be16 version;
+       };
+
+struct vgda {
+       __be32 secs;
+       __be32 usec;
+       char reserved8[16];
+       __be16 numlvs;
+       __be16 maxlvs;
+       __be16 pp_size;
+       __be16 numpvs;
+       __be16 total_vgdas;
+       __be16 vgda_size;
+       };
+
+struct lvd {
+       __be16 lv_ix;
+       __be16 res2;
+       __be16 res4;
+       __be16 maxsize;
+       __be16 lv_state;
+       __be16 mirror;
+       __be16 mirror_policy;
+       __be16 num_lps;
+       __be16 res10[8];
+       };
+
+struct lvname {
+       char name[64];
+       };
+
+struct ppe {
+       __be16 lv_ix;
+       unsigned short res2;
+       unsigned short res4;
+       __be16 lp_ix;
+       unsigned short res8[12];
+       };
+
+struct pvd {
+       char reserved0[16];
+       __be16 pp_count;
+       char reserved18[2];
+       __be32 psn_part1;
+       char reserved24[8];
+       struct ppe ppe[1016];
+       };
+
+#define LVM_MAXLVS 256
+
+/**
+ * last_lba(): return number of last logical block of device
+ * @bdev: block device
+ *
+ * Description: Returns last LBA value on success, 0 on error.
+ * This is stored (by sd and ide-geometry) in
+ *  the part[0] entry for this disk, and is the number of
+ *  physical sectors available on the disk.
+ */
+static u64 last_lba(struct block_device *bdev)
+{
+       if (!bdev || !bdev->bd_inode)
+               return 0;
+       return (bdev->bd_inode->i_size >> 9) - 1ULL;
+}
+
+/**
+ * read_lba(): Read bytes from disk, starting at given LBA
+ * @state
+ * @lba
+ * @buffer
+ * @count
+ *
+ * Description:  Reads @count bytes from @state->bdev into @buffer.
+ * Returns number of bytes read on success, 0 on error.
+ */
+static size_t read_lba(struct parsed_partitions *state, u64 lba, u8 *buffer,
+                       size_t count)
+{
+       size_t totalreadcount = 0;
+
+       if (!buffer || lba + count / 512 > last_lba(state->bdev))
+               return 0;
+
+       while (count) {
+               int copied = 512;
+               Sector sect;
+               unsigned char *data = read_part_sector(state, lba++, &sect);
+               if (!data)
+                       break;
+               if (copied > count)
+                       copied = count;
+               memcpy(buffer, data, copied);
+               put_dev_sector(sect);
+               buffer += copied;
+               totalreadcount += copied;
+               count -= copied;
+       }
+       return totalreadcount;
+}
+
+/**
+ * alloc_pvd(): reads physical volume descriptor
+ * @state
+ * @lba
+ *
+ * Description: Returns pvd on success,  NULL on error.
+ * Allocates space for pvd and fill it with disk blocks at @lba
+ * Notes: remember to free pvd when you're done!
+ */
+static struct pvd *alloc_pvd(struct parsed_partitions *state, u32 lba)
+{
+       size_t count = sizeof(struct pvd);
+       struct pvd *p;
+
+       p = kmalloc(count, GFP_KERNEL);
+       if (!p)
+               return NULL;
+
+       if (read_lba(state, lba, (u8 *) p, count) < count) {
+               kfree(p);
+               return NULL;
+       }
+       return p;
+}
+
+/**
+ * alloc_lvn(): reads logical volume names
+ * @state
+ * @lba
+ *
+ * Description: Returns lvn on success,  NULL on error.
+ * Allocates space for lvn and fill it with disk blocks at @lba
+ * Notes: remember to free lvn when you're done!
+ */
+static struct lvname *alloc_lvn(struct parsed_partitions *state, u32 lba)
+{
+       size_t count = sizeof(struct lvname) * LVM_MAXLVS;
+       struct lvname *p;
+
+       p = kmalloc(count, GFP_KERNEL);
+       if (!p)
+               return NULL;
+
+       if (read_lba(state, lba, (u8 *) p, count) < count) {
+               kfree(p);
+               return NULL;
+       }
+       return p;
+}
+
+int aix_partition(struct parsed_partitions *state)
+{
+       int ret = 0;
+       Sector sect;
+       unsigned char *d;
+       u32 pp_bytes_size;
+       u32 pp_blocks_size = 0;
+       u32 vgda_sector = 0;
+       u32 vgda_len = 0;
+       int numlvs = 0;
+       struct pvd *pvd;
+       struct lv_info {
+               unsigned short pps_per_lv;
+               unsigned short pps_found;
+               unsigned char lv_is_contiguous;
+       } *lvip;
+       struct lvname *n = NULL;
+
+       d = read_part_sector(state, 7, &sect);
+       if (d) {
+               struct lvm_rec *p = (struct lvm_rec *)d;
+               u16 lvm_version = be16_to_cpu(p->version);
+               char tmp[64];
+
+               if (lvm_version == 1) {
+                       int pp_size_log2 = be16_to_cpu(p->pp_size);
+
+                       pp_bytes_size = 1 << pp_size_log2;
+                       pp_blocks_size = pp_bytes_size / 512;
+                       snprintf(tmp, sizeof(tmp),
+                               " AIX LVM header version %u found\n",
+                               lvm_version);
+                       vgda_len = be32_to_cpu(p->vgda_len);
+                       vgda_sector = be32_to_cpu(p->vgda_psn[0]);
+               } else {
+                       snprintf(tmp, sizeof(tmp),
+                               " unsupported AIX LVM version %d found\n",
+                               lvm_version);
+               }
+               strlcat(state->pp_buf, tmp, PAGE_SIZE);
+               put_dev_sector(sect);
+       }
+       if (vgda_sector && (d = read_part_sector(state, vgda_sector, &sect))) {
+               struct vgda *p = (struct vgda *)d;
+
+               numlvs = be16_to_cpu(p->numlvs);
+               put_dev_sector(sect);
+       }
+       lvip = kzalloc(sizeof(struct lv_info) * state->limit, GFP_KERNEL);
+       if (!lvip)
+               return 0;
+       if (numlvs && (d = read_part_sector(state, vgda_sector + 1, &sect))) {
+               struct lvd *p = (struct lvd *)d;
+               int i;
+
+               n = alloc_lvn(state, vgda_sector + vgda_len - 33);
+               if (n) {
+                       int foundlvs = 0;
+
+                       for (i = 0; foundlvs < numlvs && i < state->limit; i += 1) {
+                               lvip[i].pps_per_lv = be16_to_cpu(p[i].num_lps);
+                               if (lvip[i].pps_per_lv)
+                                       foundlvs += 1;
+                       }
+               }
+               put_dev_sector(sect);
+       }
+       pvd = alloc_pvd(state, vgda_sector + 17);
+       if (pvd) {
+               int numpps = be16_to_cpu(pvd->pp_count);
+               int psn_part1 = be32_to_cpu(pvd->psn_part1);
+               int i;
+               int cur_lv_ix = -1;
+               int next_lp_ix = 1;
+               int lp_ix;
+
+               for (i = 0; i < numpps; i += 1) {
+                       struct ppe *p = pvd->ppe + i;
+                       unsigned int lv_ix;
+
+                       lp_ix = be16_to_cpu(p->lp_ix);
+                       if (!lp_ix) {
+                               next_lp_ix = 1;
+                               continue;
+                       }
+                       lv_ix = be16_to_cpu(p->lv_ix) - 1;
+                       if (lv_ix > state->limit) {
+                               cur_lv_ix = -1;
+                               continue;
+                       }
+                       lvip[lv_ix].pps_found += 1;
+                       if (lp_ix == 1) {
+                               cur_lv_ix = lv_ix;
+                               next_lp_ix = 1;
+                       } else if (lv_ix != cur_lv_ix || lp_ix != next_lp_ix) {
+                               next_lp_ix = 1;
+                               continue;
+                       }
+                       if (lp_ix == lvip[lv_ix].pps_per_lv) {
+                               char tmp[70];
+
+                               put_partition(state, lv_ix + 1,
+                                 (i + 1 - lp_ix) * pp_blocks_size + psn_part1,
+                                 lvip[lv_ix].pps_per_lv * pp_blocks_size);
+                               snprintf(tmp, sizeof(tmp), " <%s>\n",
+                                        n[lv_ix].name);
+                               strlcat(state->pp_buf, tmp, PAGE_SIZE);
+                               lvip[lv_ix].lv_is_contiguous = 1;
+                               ret = 1;
+                               next_lp_ix = 1;
+                       } else
+                               next_lp_ix += 1;
+               }
+               for (i = 0; i < state->limit; i += 1)
+                       if (lvip[i].pps_found && !lvip[i].lv_is_contiguous)
+                               pr_warn("partition %s (%u pp's found) is "
+                                       "not contiguous\n",
+                                       n[i].name, lvip[i].pps_found);
+               kfree(pvd);
+       }
+       kfree(n);
+       kfree(lvip);
+       return ret;
+}
diff --git a/block/partitions/aix.h b/block/partitions/aix.h
new file mode 100644 (file)
index 0000000..e0c66a9
--- /dev/null
@@ -0,0 +1 @@
+extern int aix_partition(struct parsed_partitions *state);
index 7681cd2..9123f25 100644 (file)
@@ -23,6 +23,7 @@
 #include "check.h"
 #include "msdos.h"
 #include "efi.h"
+#include "aix.h"
 
 /*
  * Many architectures don't like unaligned accesses, while
@@ -90,7 +91,7 @@ static int aix_magic_present(struct parsed_partitions *state, unsigned char *p)
                if (d[0] == '_' && d[1] == 'L' && d[2] == 'V' && d[3] == 'M')
                        ret = 1;
                put_dev_sector(sect);
-       };
+       }
        return ret;
 }
 
@@ -142,7 +143,7 @@ static void parse_extended(struct parsed_partitions *state,
                        return;
 
                if (!msdos_magic_present(data + 510))
-                       goto done; 
+                       goto done;
 
                p = (struct partition *) (data + 0x1be);
 
@@ -155,7 +156,7 @@ static void parse_extended(struct parsed_partitions *state,
                 * and OS/2 seems to use all four entries.
                 */
 
-               /* 
+               /*
                 * First process the data partition(s)
                 */
                for (i=0; i<4; i++, p++) {
@@ -263,7 +264,7 @@ static void parse_solaris_x86(struct parsed_partitions *state,
 }
 
 #if defined(CONFIG_BSD_DISKLABEL)
-/* 
+/*
  * Create devices for BSD partitions listed in a disklabel, under a
  * dos-like partition. See parse_extended() for more information.
  */
@@ -294,7 +295,7 @@ static void parse_bsd(struct parsed_partitions *state,
 
                if (state->next == state->limit)
                        break;
-               if (p->p_fstype == BSD_FS_UNUSED) 
+               if (p->p_fstype == BSD_FS_UNUSED)
                        continue;
                bsd_start = le32_to_cpu(p->p_offset);
                bsd_size = le32_to_cpu(p->p_size);
@@ -441,7 +442,7 @@ static struct {
        {NEW_SOLARIS_X86_PARTITION, parse_solaris_x86},
        {0, NULL},
 };
+
 int msdos_partition(struct parsed_partitions *state)
 {
        sector_t sector_size = bdev_logical_block_size(state->bdev) / 512;
@@ -462,8 +463,12 @@ int msdos_partition(struct parsed_partitions *state)
         */
        if (aix_magic_present(state, data)) {
                put_dev_sector(sect);
+#ifdef CONFIG_AIX_PARTITION
+               return aix_partition(state);
+#else
                strlcat(state->pp_buf, " [AIX]", PAGE_SIZE);
                return 0;
+#endif
        }
 
        if (!msdos_magic_present(data + 510)) {
index 904ffe8..69ce573 100644 (file)
@@ -1336,6 +1336,22 @@ config CRYPTO_842
        help
          This is the 842 algorithm.
 
+config CRYPTO_LZ4
+       tristate "LZ4 compression algorithm"
+       select CRYPTO_ALGAPI
+       select LZ4_COMPRESS
+       select LZ4_DECOMPRESS
+       help
+         This is the LZ4 algorithm.
+
+config CRYPTO_LZ4HC
+       tristate "LZ4HC compression algorithm"
+       select CRYPTO_ALGAPI
+       select LZ4HC_COMPRESS
+       select LZ4_DECOMPRESS
+       help
+         This is the LZ4 high compression mode algorithm.
+
 comment "Random Number Generation"
 
 config CRYPTO_ANSI_CPRNG
index 62af87d..2d5ed08 100644 (file)
@@ -86,6 +86,8 @@ obj-$(CONFIG_CRYPTO_CRC32) += crc32.o
 obj-$(CONFIG_CRYPTO_CRCT10DIF) += crct10dif.o
 obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o authencesn.o
 obj-$(CONFIG_CRYPTO_LZO) += lzo.o
+obj-$(CONFIG_CRYPTO_LZ4) += lz4.o
+obj-$(CONFIG_CRYPTO_LZ4HC) += lz4hc.o
 obj-$(CONFIG_CRYPTO_842) += 842.o
 obj-$(CONFIG_CRYPTO_RNG2) += rng.o
 obj-$(CONFIG_CRYPTO_RNG2) += krng.o
diff --git a/crypto/lz4.c b/crypto/lz4.c
new file mode 100644 (file)
index 0000000..4586dd1
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Cryptographic API.
+ *
+ * Copyright (c) 2013 Chanho Min <chanho.min@lge.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/init.h>
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/vmalloc.h>
+#include <linux/lz4.h>
+
+struct lz4_ctx {
+       void *lz4_comp_mem;
+};
+
+static int lz4_init(struct crypto_tfm *tfm)
+{
+       struct lz4_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       ctx->lz4_comp_mem = vmalloc(LZ4_MEM_COMPRESS);
+       if (!ctx->lz4_comp_mem)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void lz4_exit(struct crypto_tfm *tfm)
+{
+       struct lz4_ctx *ctx = crypto_tfm_ctx(tfm);
+       vfree(ctx->lz4_comp_mem);
+}
+
+static int lz4_compress_crypto(struct crypto_tfm *tfm, const u8 *src,
+                           unsigned int slen, u8 *dst, unsigned int *dlen)
+{
+       struct lz4_ctx *ctx = crypto_tfm_ctx(tfm);
+       size_t tmp_len = *dlen;
+       int err;
+
+       err = lz4_compress(src, slen, dst, &tmp_len, ctx->lz4_comp_mem);
+
+       if (err < 0)
+               return -EINVAL;
+
+       *dlen = tmp_len;
+       return 0;
+}
+
+static int lz4_decompress_crypto(struct crypto_tfm *tfm, const u8 *src,
+                             unsigned int slen, u8 *dst, unsigned int *dlen)
+{
+       int err;
+       size_t tmp_len = *dlen;
+       size_t __slen = slen;
+
+       err = lz4_decompress(src, &__slen, dst, tmp_len);
+       if (err < 0)
+               return -EINVAL;
+
+       *dlen = tmp_len;
+       return err;
+}
+
+static struct crypto_alg alg_lz4 = {
+       .cra_name               = "lz4",
+       .cra_flags              = CRYPTO_ALG_TYPE_COMPRESS,
+       .cra_ctxsize            = sizeof(struct lz4_ctx),
+       .cra_module             = THIS_MODULE,
+       .cra_list               = LIST_HEAD_INIT(alg_lz4.cra_list),
+       .cra_init               = lz4_init,
+       .cra_exit               = lz4_exit,
+       .cra_u                  = { .compress = {
+       .coa_compress           = lz4_compress_crypto,
+       .coa_decompress         = lz4_decompress_crypto } }
+};
+
+static int __init lz4_mod_init(void)
+{
+       return crypto_register_alg(&alg_lz4);
+}
+
+static void __exit lz4_mod_fini(void)
+{
+       crypto_unregister_alg(&alg_lz4);
+}
+
+module_init(lz4_mod_init);
+module_exit(lz4_mod_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LZ4 Compression Algorithm");
diff --git a/crypto/lz4hc.c b/crypto/lz4hc.c
new file mode 100644 (file)
index 0000000..151ba31
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Cryptographic API.
+ *
+ * Copyright (c) 2013 Chanho Min <chanho.min@lge.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/init.h>
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/vmalloc.h>
+#include <linux/lz4.h>
+
+struct lz4hc_ctx {
+       void *lz4hc_comp_mem;
+};
+
+static int lz4hc_init(struct crypto_tfm *tfm)
+{
+       struct lz4hc_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       ctx->lz4hc_comp_mem = vmalloc(LZ4HC_MEM_COMPRESS);
+       if (!ctx->lz4hc_comp_mem)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void lz4hc_exit(struct crypto_tfm *tfm)
+{
+       struct lz4hc_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       vfree(ctx->lz4hc_comp_mem);
+}
+
+static int lz4hc_compress_crypto(struct crypto_tfm *tfm, const u8 *src,
+                           unsigned int slen, u8 *dst, unsigned int *dlen)
+{
+       struct lz4hc_ctx *ctx = crypto_tfm_ctx(tfm);
+       size_t tmp_len = *dlen;
+       int err;
+
+       err = lz4hc_compress(src, slen, dst, &tmp_len, ctx->lz4hc_comp_mem);
+
+       if (err < 0)
+               return -EINVAL;
+
+       *dlen = tmp_len;
+       return 0;
+}
+
+static int lz4hc_decompress_crypto(struct crypto_tfm *tfm, const u8 *src,
+                             unsigned int slen, u8 *dst, unsigned int *dlen)
+{
+       int err;
+       size_t tmp_len = *dlen;
+       size_t __slen = slen;
+
+       err = lz4_decompress(src, &__slen, dst, tmp_len);
+       if (err < 0)
+               return -EINVAL;
+
+       *dlen = tmp_len;
+       return err;
+}
+
+static struct crypto_alg alg_lz4hc = {
+       .cra_name               = "lz4hc",
+       .cra_flags              = CRYPTO_ALG_TYPE_COMPRESS,
+       .cra_ctxsize            = sizeof(struct lz4hc_ctx),
+       .cra_module             = THIS_MODULE,
+       .cra_list               = LIST_HEAD_INIT(alg_lz4hc.cra_list),
+       .cra_init               = lz4hc_init,
+       .cra_exit               = lz4hc_exit,
+       .cra_u                  = { .compress = {
+       .coa_compress           = lz4hc_compress_crypto,
+       .coa_decompress         = lz4hc_decompress_crypto } }
+};
+
+static int __init lz4hc_mod_init(void)
+{
+       return crypto_register_alg(&alg_lz4hc);
+}
+
+static void __exit lz4hc_mod_fini(void)
+{
+       crypto_unregister_alg(&alg_lz4hc);
+}
+
+module_init(lz4hc_mod_init);
+module_exit(lz4hc_mod_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LZ4HC Compression Algorithm");
index 77a7480..62a7607 100644 (file)
@@ -1403,7 +1403,7 @@ static void amb_free_rx_skb (struct atm_vcc * atm_vcc, struct sk_buff * skb) {
   rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
   
   skb->data = skb->head;
-  skb->tail = skb->head;
+  skb_reset_tail_pointer(skb);
   skb->len = 0;
   
   if (!rx_give (dev, &rx, pool)) {
index 4e22ce3..48029aa 100644 (file)
@@ -10,7 +10,7 @@ obj-$(CONFIG_CMA) += dma-contiguous.o
 obj-y                  += power/
 obj-$(CONFIG_HAS_DMA)  += dma-mapping.o
 obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
-obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o
+obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o reservation.o
 obj-$(CONFIG_ISA)      += isa.o
 obj-$(CONFIG_FW_LOADER)        += firmware_class.o
 obj-$(CONFIG_NUMA)     += node.o
diff --git a/drivers/base/reservation.c b/drivers/base/reservation.c
new file mode 100644 (file)
index 0000000..a73fbf3
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012-2013 Canonical Ltd
+ *
+ * Based on bo.c which bears the following copyright notice,
+ * but is dual licensed:
+ *
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+/*
+ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
+ */
+
+#include <linux/reservation.h>
+#include <linux/export.h>
+
+DEFINE_WW_CLASS(reservation_ww_class);
+EXPORT_SYMBOL(reservation_ww_class);
index 8b4221c..380a200 100644 (file)
@@ -26,6 +26,7 @@ config BCMA_HOST_PCI_POSSIBLE
 config BCMA_HOST_PCI
        bool "Support for BCMA on PCI-host bus"
        depends on BCMA_HOST_PCI_POSSIBLE
+       default y
 
 config BCMA_DRIVER_PCI_HOSTMODE
        bool "Driver for PCI core working in hostmode"
index 79595a0..0215f9a 100644 (file)
@@ -22,6 +22,8 @@
 struct bcma_bus;
 
 /* main.c */
+bool bcma_wait_value(struct bcma_device *core, u16 reg, u32 mask, u32 value,
+                    int timeout);
 int bcma_bus_register(struct bcma_bus *bus);
 void bcma_bus_unregister(struct bcma_bus *bus);
 int __init bcma_bus_early_register(struct bcma_bus *bus,
index 17b26ce..37a5ffe 100644 (file)
@@ -9,6 +9,25 @@
 #include <linux/export.h>
 #include <linux/bcma/bcma.h>
 
+static bool bcma_core_wait_value(struct bcma_device *core, u16 reg, u32 mask,
+                                u32 value, int timeout)
+{
+       unsigned long deadline = jiffies + timeout;
+       u32 val;
+
+       do {
+               val = bcma_aread32(core, reg);
+               if ((val & mask) == value)
+                       return true;
+               cpu_relax();
+               udelay(10);
+       } while (!time_after_eq(jiffies, deadline));
+
+       bcma_warn(core->bus, "Timeout waiting for register 0x%04X!\n", reg);
+
+       return false;
+}
+
 bool bcma_core_is_enabled(struct bcma_device *core)
 {
        if ((bcma_aread32(core, BCMA_IOCTL) & (BCMA_IOCTL_CLK | BCMA_IOCTL_FGC))
@@ -25,13 +44,15 @@ void bcma_core_disable(struct bcma_device *core, u32 flags)
        if (bcma_aread32(core, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET)
                return;
 
-       bcma_awrite32(core, BCMA_IOCTL, flags);
-       bcma_aread32(core, BCMA_IOCTL);
-       udelay(10);
+       bcma_core_wait_value(core, BCMA_RESET_ST, ~0, 0, 300);
 
        bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
        bcma_aread32(core, BCMA_RESET_CTL);
        udelay(1);
+
+       bcma_awrite32(core, BCMA_IOCTL, flags);
+       bcma_aread32(core, BCMA_IOCTL);
+       udelay(10);
 }
 EXPORT_SYMBOL_GPL(bcma_core_disable);
 
@@ -43,6 +64,7 @@ int bcma_core_enable(struct bcma_device *core, u32 flags)
        bcma_aread32(core, BCMA_IOCTL);
 
        bcma_awrite32(core, BCMA_RESET_CTL, 0);
+       bcma_aread32(core, BCMA_RESET_CTL);
        udelay(1);
 
        bcma_awrite32(core, BCMA_IOCTL, (BCMA_IOCTL_CLK | flags));
index 036c674..b068f98 100644 (file)
@@ -140,8 +140,15 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
        bcma_core_chipcommon_early_init(cc);
 
        if (cc->core->id.rev >= 20) {
-               bcma_cc_write32(cc, BCMA_CC_GPIOPULLUP, 0);
-               bcma_cc_write32(cc, BCMA_CC_GPIOPULLDOWN, 0);
+               u32 pullup = 0, pulldown = 0;
+
+               if (cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM43142) {
+                       pullup = 0x402e0;
+                       pulldown = 0x20500;
+               }
+
+               bcma_cc_write32(cc, BCMA_CC_GPIOPULLUP, pullup);
+               bcma_cc_write32(cc, BCMA_CC_GPIOPULLDOWN, pulldown);
        }
 
        if (cc->capabilities & BCMA_CC_CAP_PMU)
index edca73a..5081a8c 100644 (file)
@@ -56,6 +56,109 @@ void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
 }
 EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset);
 
+static u32 bcma_pmu_xtalfreq(struct bcma_drv_cc *cc)
+{
+       u32 ilp_ctl, alp_hz;
+
+       if (!(bcma_cc_read32(cc, BCMA_CC_PMU_STAT) &
+             BCMA_CC_PMU_STAT_EXT_LPO_AVAIL))
+               return 0;
+
+       bcma_cc_write32(cc, BCMA_CC_PMU_XTAL_FREQ,
+                       BIT(BCMA_CC_PMU_XTAL_FREQ_MEASURE_SHIFT));
+       usleep_range(1000, 2000);
+
+       ilp_ctl = bcma_cc_read32(cc, BCMA_CC_PMU_XTAL_FREQ);
+       ilp_ctl &= BCMA_CC_PMU_XTAL_FREQ_ILPCTL_MASK;
+
+       bcma_cc_write32(cc, BCMA_CC_PMU_XTAL_FREQ, 0);
+
+       alp_hz = ilp_ctl * 32768 / 4;
+       return (alp_hz + 50000) / 100000 * 100;
+}
+
+static void bcma_pmu2_pll_init0(struct bcma_drv_cc *cc, u32 xtalfreq)
+{
+       struct bcma_bus *bus = cc->core->bus;
+       u32 freq_tgt_target = 0, freq_tgt_current;
+       u32 pll0, mask;
+
+       switch (bus->chipinfo.id) {
+       case BCMA_CHIP_ID_BCM43142:
+               /* pmu2_xtaltab0_adfll_485 */
+               switch (xtalfreq) {
+               case 12000:
+                       freq_tgt_target = 0x50D52;
+                       break;
+               case 20000:
+                       freq_tgt_target = 0x307FE;
+                       break;
+               case 26000:
+                       freq_tgt_target = 0x254EA;
+                       break;
+               case 37400:
+                       freq_tgt_target = 0x19EF8;
+                       break;
+               case 52000:
+                       freq_tgt_target = 0x12A75;
+                       break;
+               }
+               break;
+       }
+
+       if (!freq_tgt_target) {
+               bcma_err(bus, "Unknown TGT frequency for xtalfreq %d\n",
+                        xtalfreq);
+               return;
+       }
+
+       pll0 = bcma_chipco_pll_read(cc, BCMA_CC_PMU15_PLL_PLLCTL0);
+       freq_tgt_current = (pll0 & BCMA_CC_PMU15_PLL_PC0_FREQTGT_MASK) >>
+               BCMA_CC_PMU15_PLL_PC0_FREQTGT_SHIFT;
+
+       if (freq_tgt_current == freq_tgt_target) {
+               bcma_debug(bus, "Target TGT frequency already set\n");
+               return;
+       }
+
+       /* Turn off PLL */
+       switch (bus->chipinfo.id) {
+       case BCMA_CHIP_ID_BCM43142:
+               mask = (u32)~(BCMA_RES_4314_HT_AVAIL |
+                             BCMA_RES_4314_MACPHY_CLK_AVAIL);
+
+               bcma_cc_mask32(cc, BCMA_CC_PMU_MINRES_MSK, mask);
+               bcma_cc_mask32(cc, BCMA_CC_PMU_MAXRES_MSK, mask);
+               bcma_wait_value(cc->core, BCMA_CLKCTLST,
+                               BCMA_CLKCTLST_HAVEHT, 0, 20000);
+               break;
+       }
+
+       pll0 &= ~BCMA_CC_PMU15_PLL_PC0_FREQTGT_MASK;
+       pll0 |= freq_tgt_target << BCMA_CC_PMU15_PLL_PC0_FREQTGT_SHIFT;
+       bcma_chipco_pll_write(cc, BCMA_CC_PMU15_PLL_PLLCTL0, pll0);
+
+       /* Flush */
+       if (cc->pmu.rev >= 2)
+               bcma_cc_set32(cc, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
+
+       /* TODO: Do we need to update OTP? */
+}
+
+static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
+{
+       struct bcma_bus *bus = cc->core->bus;
+       u32 xtalfreq = bcma_pmu_xtalfreq(cc);
+
+       switch (bus->chipinfo.id) {
+       case BCMA_CHIP_ID_BCM43142:
+               if (xtalfreq == 0)
+                       xtalfreq = 20000;
+               bcma_pmu2_pll_init0(cc, xtalfreq);
+               break;
+       }
+}
+
 static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
 {
        struct bcma_bus *bus = cc->core->bus;
@@ -66,6 +169,25 @@ static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
                min_msk = 0x200D;
                max_msk = 0xFFFF;
                break;
+       case BCMA_CHIP_ID_BCM43142:
+               min_msk = BCMA_RES_4314_LPLDO_PU |
+                         BCMA_RES_4314_PMU_SLEEP_DIS |
+                         BCMA_RES_4314_PMU_BG_PU |
+                         BCMA_RES_4314_CBUCK_LPOM_PU |
+                         BCMA_RES_4314_CBUCK_PFM_PU |
+                         BCMA_RES_4314_CLDO_PU |
+                         BCMA_RES_4314_LPLDO2_LVM |
+                         BCMA_RES_4314_WL_PMU_PU |
+                         BCMA_RES_4314_LDO3P3_PU |
+                         BCMA_RES_4314_OTP_PU |
+                         BCMA_RES_4314_WL_PWRSW_PU |
+                         BCMA_RES_4314_LQ_AVAIL |
+                         BCMA_RES_4314_LOGIC_RET |
+                         BCMA_RES_4314_MEM_SLEEP |
+                         BCMA_RES_4314_MACPHY_RET |
+                         BCMA_RES_4314_WL_CORE_READY;
+               max_msk = 0x3FFFFFFF;
+               break;
        default:
                bcma_debug(bus, "PMU resource config unknown or not needed for device 0x%04X\n",
                           bus->chipinfo.id);
@@ -165,6 +287,7 @@ void bcma_pmu_init(struct bcma_drv_cc *cc)
                bcma_cc_set32(cc, BCMA_CC_PMU_CTL,
                             BCMA_CC_PMU_CTL_NOILPONW);
 
+       bcma_pmu_pll_init(cc);
        bcma_pmu_resources_init(cc);
        bcma_pmu_workarounds(cc);
 }
index e6ed4fe..4d07cce 100644 (file)
@@ -30,7 +30,7 @@ struct bcma_sflash_tbl_e {
        u16 numblocks;
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
        { "M25P20", 0x11, 0x10000, 4, },
        { "M25P40", 0x12, 0x10000, 8, },
 
@@ -41,7 +41,7 @@ static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
        { 0 },
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
        { "SST25WF512", 1, 0x1000, 16, },
        { "SST25VF512", 0x48, 0x1000, 16, },
        { "SST25WF010", 2, 0x1000, 32, },
@@ -59,7 +59,7 @@ static struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
        { 0 },
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = {
        { "AT45DB011", 0xc, 256, 512, },
        { "AT45DB021", 0x14, 256, 1024, },
        { "AT45DB041", 0x1c, 256, 2048, },
@@ -89,7 +89,7 @@ int bcma_sflash_init(struct bcma_drv_cc *cc)
 {
        struct bcma_bus *bus = cc->core->bus;
        struct bcma_sflash *sflash = &cc->sflash;
-       struct bcma_sflash_tbl_e *e;
+       const struct bcma_sflash_tbl_e *e;
        u32 id, id2;
 
        switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
index fbf2759..a355e63 100644 (file)
@@ -275,6 +275,7 @@ static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4358) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4365) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
        { 0, },
 };
index f72f52b..0067422 100644 (file)
@@ -93,6 +93,25 @@ struct bcma_device *bcma_find_core_unit(struct bcma_bus *bus, u16 coreid,
        return NULL;
 }
 
+bool bcma_wait_value(struct bcma_device *core, u16 reg, u32 mask, u32 value,
+                    int timeout)
+{
+       unsigned long deadline = jiffies + timeout;
+       u32 val;
+
+       do {
+               val = bcma_read32(core, reg);
+               if ((val & mask) == value)
+                       return true;
+               cpu_relax();
+               udelay(10);
+       } while (!time_after_eq(jiffies, deadline));
+
+       bcma_warn(core->bus, "Timeout waiting for register 0x%04X!\n", reg);
+
+       return false;
+}
+
 static void bcma_release_core_dev(struct device *dev)
 {
        struct bcma_device *core = container_of(dev, struct bcma_device, dev);
index 8934298..72bf454 100644 (file)
@@ -72,12 +72,12 @@ fail:
  * R/W ops.
  **************************************************/
 
-static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
+static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom,
+                           size_t words)
 {
        int i;
-       for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
-               sprom[i] = bcma_read16(bus->drv_cc.core,
-                                      offset + (i * 2));
+       for (i = 0; i < words; i++)
+               sprom[i] = bcma_read16(bus->drv_cc.core, offset + (i * 2));
 }
 
 /**************************************************
@@ -124,29 +124,29 @@ static inline u8 bcma_crc8(u8 crc, u8 data)
        return t[crc ^ data];
 }
 
-static u8 bcma_sprom_crc(const u16 *sprom)
+static u8 bcma_sprom_crc(const u16 *sprom, size_t words)
 {
        int word;
        u8 crc = 0xFF;
 
-       for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) {
+       for (word = 0; word < words - 1; word++) {
                crc = bcma_crc8(crc, sprom[word] & 0x00FF);
                crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
        }
-       crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
+       crc = bcma_crc8(crc, sprom[words - 1] & 0x00FF);
        crc ^= 0xFF;
 
        return crc;
 }
 
-static int bcma_sprom_check_crc(const u16 *sprom)
+static int bcma_sprom_check_crc(const u16 *sprom, size_t words)
 {
        u8 crc;
        u8 expected_crc;
        u16 tmp;
 
-       crc = bcma_sprom_crc(sprom);
-       tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC;
+       crc = bcma_sprom_crc(sprom, words);
+       tmp = sprom[words - 1] & SSB_SPROM_REVISION_CRC;
        expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
        if (crc != expected_crc)
                return -EPROTO;
@@ -154,21 +154,25 @@ static int bcma_sprom_check_crc(const u16 *sprom)
        return 0;
 }
 
-static int bcma_sprom_valid(const u16 *sprom)
+static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom,
+                           size_t words)
 {
        u16 revision;
        int err;
 
-       err = bcma_sprom_check_crc(sprom);
+       err = bcma_sprom_check_crc(sprom, words);
        if (err)
                return err;
 
-       revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV;
-       if (revision != 8 && revision != 9) {
+       revision = sprom[words - 1] & SSB_SPROM_REVISION_REV;
+       if (revision != 8 && revision != 9 && revision != 10) {
                pr_err("Unsupported SPROM revision: %d\n", revision);
                return -ENOENT;
        }
 
+       bus->sprom.revision = revision;
+       bcma_debug(bus, "Found SPROM revision %d\n", revision);
+
        return 0;
 }
 
@@ -208,9 +212,6 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
        BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
                        ARRAY_SIZE(bus->sprom.core_pwr_info));
 
-       bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
-               SSB_SPROM_REVISION_REV;
-
        for (i = 0; i < 3; i++) {
                v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
                *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
@@ -502,7 +503,7 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
        case BCMA_CHIP_ID_BCM4331:
                present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
                break;
-
+       case BCMA_CHIP_ID_BCM43142:
        case BCMA_CHIP_ID_BCM43224:
        case BCMA_CHIP_ID_BCM43225:
                /* for these chips OTP is always available */
@@ -550,7 +551,9 @@ int bcma_sprom_get(struct bcma_bus *bus)
 {
        u16 offset = BCMA_CC_SPROM;
        u16 *sprom;
-       int err = 0;
+       size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4,
+                                SSB_SPROMSIZE_WORDS_R10, };
+       int i, err = 0;
 
        if (!bus->drv_cc.core)
                return -EOPNOTSUPP;
@@ -579,32 +582,37 @@ int bcma_sprom_get(struct bcma_bus *bus)
                }
        }
 
-       sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
-                       GFP_KERNEL);
-       if (!sprom)
-               return -ENOMEM;
-
        if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
            bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
                bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
 
        bcma_debug(bus, "SPROM offset 0x%x\n", offset);
-       bcma_sprom_read(bus, offset, sprom);
+       for (i = 0; i < ARRAY_SIZE(sprom_sizes); i++) {
+               size_t words = sprom_sizes[i];
+
+               sprom = kcalloc(words, sizeof(u16), GFP_KERNEL);
+               if (!sprom)
+                       return -ENOMEM;
+
+               bcma_sprom_read(bus, offset, sprom, words);
+               err = bcma_sprom_valid(bus, sprom, words);
+               if (!err)
+                       break;
+
+               kfree(sprom);
+       }
 
        if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
            bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
                bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
 
-       err = bcma_sprom_valid(sprom);
        if (err) {
-               bcma_warn(bus, "invalid sprom read from the PCIe card, try to use fallback sprom\n");
+               bcma_warn(bus, "Invalid SPROM read from the PCIe card, trying to use fallback SPROM\n");
                err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
-               goto out;
+       } else {
+               bcma_sprom_extract_r8(bus, sprom);
+               kfree(sprom);
        }
 
-       bcma_sprom_extract_r8(bus, sprom);
-
-out:
-       kfree(sprom);
        return err;
 }
index aff789d..4ad2ad9 100644 (file)
@@ -372,7 +372,7 @@ enum rbd_dev_flags {
        RBD_DEV_FLAG_REMOVING,  /* this mapping is being removed */
 };
 
-static DEFINE_MUTEX(ctl_mutex);          /* Serialize open/close/setup/teardown */
+static DEFINE_MUTEX(client_mutex);     /* Serialize client creation */
 
 static LIST_HEAD(rbd_dev_list);    /* devices */
 static DEFINE_SPINLOCK(rbd_dev_list_lock);
@@ -489,10 +489,8 @@ static int rbd_open(struct block_device *bdev, fmode_t mode)
        if (removing)
                return -ENOENT;
 
-       mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
        (void) get_device(&rbd_dev->dev);
        set_device_ro(bdev, rbd_dev->mapping.read_only);
-       mutex_unlock(&ctl_mutex);
 
        return 0;
 }
@@ -507,9 +505,7 @@ static void rbd_release(struct gendisk *disk, fmode_t mode)
        spin_unlock_irq(&rbd_dev->lock);
        rbd_assert(open_count_before > 0);
 
-       mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
        put_device(&rbd_dev->dev);
-       mutex_unlock(&ctl_mutex);
 }
 
 static const struct block_device_operations rbd_bd_ops = {
@@ -520,7 +516,7 @@ static const struct block_device_operations rbd_bd_ops = {
 
 /*
  * Initialize an rbd client instance.  Success or not, this function
- * consumes ceph_opts.
+ * consumes ceph_opts.  Caller holds client_mutex.
  */
 static struct rbd_client *rbd_client_create(struct ceph_options *ceph_opts)
 {
@@ -535,30 +531,25 @@ static struct rbd_client *rbd_client_create(struct ceph_options *ceph_opts)
        kref_init(&rbdc->kref);
        INIT_LIST_HEAD(&rbdc->node);
 
-       mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-
        rbdc->client = ceph_create_client(ceph_opts, rbdc, 0, 0);
        if (IS_ERR(rbdc->client))
-               goto out_mutex;
+               goto out_rbdc;
        ceph_opts = NULL; /* Now rbdc->client is responsible for ceph_opts */
 
        ret = ceph_open_session(rbdc->client);
        if (ret < 0)
-               goto out_err;
+               goto out_client;
 
        spin_lock(&rbd_client_list_lock);
        list_add_tail(&rbdc->node, &rbd_client_list);
        spin_unlock(&rbd_client_list_lock);
 
-       mutex_unlock(&ctl_mutex);
        dout("%s: rbdc %p\n", __func__, rbdc);
 
        return rbdc;
-
-out_err:
+out_client:
        ceph_destroy_client(rbdc->client);
-out_mutex:
-       mutex_unlock(&ctl_mutex);
+out_rbdc:
        kfree(rbdc);
 out_opt:
        if (ceph_opts)
@@ -682,11 +673,13 @@ static struct rbd_client *rbd_get_client(struct ceph_options *ceph_opts)
 {
        struct rbd_client *rbdc;
 
+       mutex_lock_nested(&client_mutex, SINGLE_DEPTH_NESTING);
        rbdc = rbd_client_find(ceph_opts);
        if (rbdc)       /* using an existing client */
                ceph_destroy_options(ceph_opts);
        else
                rbdc = rbd_client_create(ceph_opts);
+       mutex_unlock(&client_mutex);
 
        return rbdc;
 }
@@ -840,7 +833,6 @@ static int rbd_header_from_disk(struct rbd_device *rbd_dev,
 
        /* We won't fail any more, fill in the header */
 
-       down_write(&rbd_dev->header_rwsem);
        if (first_time) {
                header->object_prefix = object_prefix;
                header->obj_order = ondisk->options.order;
@@ -869,8 +861,6 @@ static int rbd_header_from_disk(struct rbd_device *rbd_dev,
                if (rbd_dev->mapping.size != header->image_size)
                        rbd_dev->mapping.size = header->image_size;
 
-       up_write(&rbd_dev->header_rwsem);
-
        return 0;
 out_2big:
        ret = -EIO;
@@ -1126,6 +1116,7 @@ static void zero_bio_chain(struct bio *chain, int start_ofs)
                                buf = bvec_kmap_irq(bv, &flags);
                                memset(buf + remainder, 0,
                                       bv->bv_len - remainder);
+                               flush_dcache_page(bv->bv_page);
                                bvec_kunmap_irq(buf, &flags);
                        }
                        pos += bv->bv_len;
@@ -1153,11 +1144,12 @@ static void zero_pages(struct page **pages, u64 offset, u64 end)
                unsigned long flags;
                void *kaddr;
 
-               page_offset = (size_t)(offset & ~PAGE_MASK);
-               length = min(PAGE_SIZE - page_offset, (size_t)(end - offset));
+               page_offset = offset & ~PAGE_MASK;
+               length = min_t(size_t, PAGE_SIZE - page_offset, end - offset);
                local_irq_save(flags);
                kaddr = kmap_atomic(*page);
                memset(kaddr + page_offset, 0, length);
+               flush_dcache_page(*page);
                kunmap_atomic(kaddr);
                local_irq_restore(flags);
 
@@ -2171,9 +2163,9 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
        struct rbd_obj_request *obj_request = NULL;
        struct rbd_obj_request *next_obj_request;
        bool write_request = img_request_write_test(img_request);
-       struct bio *bio_list;
+       struct bio *bio_list = 0;
        unsigned int bio_offset = 0;
-       struct page **pages;
+       struct page **pages = 0;
        u64 img_offset;
        u64 resid;
        u16 opcode;
@@ -2535,6 +2527,7 @@ static void rbd_img_obj_exists_callback(struct rbd_obj_request *obj_request)
         */
        orig_request = obj_request->obj_request;
        obj_request->obj_request = NULL;
+       rbd_obj_request_put(orig_request);
        rbd_assert(orig_request);
        rbd_assert(orig_request->img_request);
 
@@ -2555,7 +2548,6 @@ static void rbd_img_obj_exists_callback(struct rbd_obj_request *obj_request)
        if (!rbd_dev->parent_overlap) {
                struct ceph_osd_client *osdc;
 
-               rbd_obj_request_put(orig_request);
                osdc = &rbd_dev->rbd_client->client->osdc;
                result = rbd_obj_request_submit(osdc, orig_request);
                if (!result)
@@ -2585,7 +2577,6 @@ static void rbd_img_obj_exists_callback(struct rbd_obj_request *obj_request)
 out:
        if (orig_request->result)
                rbd_obj_request_complete(orig_request);
-       rbd_obj_request_put(orig_request);
 }
 
 static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request)
@@ -2859,7 +2850,7 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
                (unsigned int)opcode);
        ret = rbd_dev_refresh(rbd_dev);
        if (ret)
-               rbd_warn(rbd_dev, "header refresh error (%d)\n", ret);
+               rbd_warn(rbd_dev, "header refresh error (%d)\n", ret);
 
        rbd_obj_notify_ack(rbd_dev, notify_id);
 }
@@ -3339,8 +3330,8 @@ static int rbd_dev_refresh(struct rbd_device *rbd_dev)
        int ret;
 
        rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
+       down_write(&rbd_dev->header_rwsem);
        mapping_size = rbd_dev->mapping.size;
-       mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
        if (rbd_dev->image_format == 1)
                ret = rbd_dev_v1_header_info(rbd_dev);
        else
@@ -3349,7 +3340,8 @@ static int rbd_dev_refresh(struct rbd_device *rbd_dev)
        /* If it's a mapped snapshot, validate its EXISTS flag */
 
        rbd_exists_validate(rbd_dev);
-       mutex_unlock(&ctl_mutex);
+       up_write(&rbd_dev->header_rwsem);
+
        if (mapping_size != rbd_dev->mapping.size) {
                sector_t size;
 
@@ -3813,6 +3805,7 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
        void *end;
        u64 pool_id;
        char *image_id;
+       u64 snap_id;
        u64 overlap;
        int ret;
 
@@ -3872,24 +3865,56 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
                        (unsigned long long)pool_id, U32_MAX);
                goto out_err;
        }
-       parent_spec->pool_id = pool_id;
 
        image_id = ceph_extract_encoded_string(&p, end, NULL, GFP_KERNEL);
        if (IS_ERR(image_id)) {
                ret = PTR_ERR(image_id);
                goto out_err;
        }
-       parent_spec->image_id = image_id;
-       ceph_decode_64_safe(&p, end, parent_spec->snap_id, out_err);
+       ceph_decode_64_safe(&p, end, snap_id, out_err);
        ceph_decode_64_safe(&p, end, overlap, out_err);
 
-       if (overlap) {
-               rbd_spec_put(rbd_dev->parent_spec);
+       /*
+        * The parent won't change (except when the clone is
+        * flattened, already handled that).  So we only need to
+        * record the parent spec we have not already done so.
+        */
+       if (!rbd_dev->parent_spec) {
+               parent_spec->pool_id = pool_id;
+               parent_spec->image_id = image_id;
+               parent_spec->snap_id = snap_id;
                rbd_dev->parent_spec = parent_spec;
                parent_spec = NULL;     /* rbd_dev now owns this */
-               rbd_dev->parent_overlap = overlap;
-       } else {
-               rbd_warn(rbd_dev, "ignoring parent of clone with overlap 0\n");
+       }
+
+       /*
+        * We always update the parent overlap.  If it's zero we
+        * treat it specially.
+        */
+       rbd_dev->parent_overlap = overlap;
+       smp_mb();
+       if (!overlap) {
+
+               /* A null parent_spec indicates it's the initial probe */
+
+               if (parent_spec) {
+                       /*
+                        * The overlap has become zero, so the clone
+                        * must have been resized down to 0 at some
+                        * point.  Treat this the same as a flatten.
+                        */
+                       rbd_dev_parent_put(rbd_dev);
+                       pr_info("%s: clone image now standalone\n",
+                               rbd_dev->disk->disk_name);
+               } else {
+                       /*
+                        * For the initial probe, if we find the
+                        * overlap is zero we just pretend there was
+                        * no parent image.
+                        */
+                       rbd_warn(rbd_dev, "ignoring parent of "
+                                               "clone with overlap 0\n");
+               }
        }
 out:
        ret = 0;
@@ -4245,16 +4270,14 @@ static int rbd_dev_v2_header_info(struct rbd_device *rbd_dev)
        bool first_time = rbd_dev->header.object_prefix == NULL;
        int ret;
 
-       down_write(&rbd_dev->header_rwsem);
-
        ret = rbd_dev_v2_image_size(rbd_dev);
        if (ret)
-               goto out;
+               return ret;
 
        if (first_time) {
                ret = rbd_dev_v2_header_onetime(rbd_dev);
                if (ret)
-                       goto out;
+                       return ret;
        }
 
        /*
@@ -4269,7 +4292,7 @@ static int rbd_dev_v2_header_info(struct rbd_device *rbd_dev)
 
                ret = rbd_dev_v2_parent_info(rbd_dev);
                if (ret)
-                       goto out;
+                       return ret;
 
                /*
                 * Print a warning if this is the initial probe and
@@ -4290,8 +4313,6 @@ static int rbd_dev_v2_header_info(struct rbd_device *rbd_dev)
 
        ret = rbd_dev_v2_snap_context(rbd_dev);
        dout("rbd_dev_v2_snap_context returned %d\n", ret);
-out:
-       up_write(&rbd_dev->header_rwsem);
 
        return ret;
 }
@@ -4301,8 +4322,6 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev)
        struct device *dev;
        int ret;
 
-       mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-
        dev = &rbd_dev->dev;
        dev->bus = &rbd_bus_type;
        dev->type = &rbd_device_type;
@@ -4311,8 +4330,6 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev)
        dev_set_name(dev, "%d", rbd_dev->dev_id);
        ret = device_register(dev);
 
-       mutex_unlock(&ctl_mutex);
-
        return ret;
 }
 
@@ -5059,23 +5076,6 @@ err_out_module:
        return (ssize_t)rc;
 }
 
-static struct rbd_device *__rbd_get_dev(unsigned long dev_id)
-{
-       struct list_head *tmp;
-       struct rbd_device *rbd_dev;
-
-       spin_lock(&rbd_dev_list_lock);
-       list_for_each(tmp, &rbd_dev_list) {
-               rbd_dev = list_entry(tmp, struct rbd_device, node);
-               if (rbd_dev->dev_id == dev_id) {
-                       spin_unlock(&rbd_dev_list_lock);
-                       return rbd_dev;
-               }
-       }
-       spin_unlock(&rbd_dev_list_lock);
-       return NULL;
-}
-
 static void rbd_dev_device_release(struct device *dev)
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
@@ -5120,8 +5120,10 @@ static ssize_t rbd_remove(struct bus_type *bus,
                          size_t count)
 {
        struct rbd_device *rbd_dev = NULL;
-       int target_id;
+       struct list_head *tmp;
+       int dev_id;
        unsigned long ul;
+       bool already = false;
        int ret;
 
        ret = strict_strtoul(buf, 10, &ul);
@@ -5129,37 +5131,40 @@ static ssize_t rbd_remove(struct bus_type *bus,
                return ret;
 
        /* convert to int; abort if we lost anything in the conversion */
-       target_id = (int) ul;
-       if (target_id != ul)
+       dev_id = (int)ul;
+       if (dev_id != ul)
                return -EINVAL;
 
-       mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-
-       rbd_dev = __rbd_get_dev(target_id);
-       if (!rbd_dev) {
-               ret = -ENOENT;
-               goto done;
+       ret = -ENOENT;
+       spin_lock(&rbd_dev_list_lock);
+       list_for_each(tmp, &rbd_dev_list) {
+               rbd_dev = list_entry(tmp, struct rbd_device, node);
+               if (rbd_dev->dev_id == dev_id) {
+                       ret = 0;
+                       break;
+               }
+       }
+       if (!ret) {
+               spin_lock_irq(&rbd_dev->lock);
+               if (rbd_dev->open_count)
+                       ret = -EBUSY;
+               else
+                       already = test_and_set_bit(RBD_DEV_FLAG_REMOVING,
+                                                       &rbd_dev->flags);
+               spin_unlock_irq(&rbd_dev->lock);
        }
+       spin_unlock(&rbd_dev_list_lock);
+       if (ret < 0 || already)
+               return ret;
 
-       spin_lock_irq(&rbd_dev->lock);
-       if (rbd_dev->open_count)
-               ret = -EBUSY;
-       else
-               set_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags);
-       spin_unlock_irq(&rbd_dev->lock);
-       if (ret < 0)
-               goto done;
        rbd_bus_del_dev(rbd_dev);
        ret = rbd_dev_header_watch_sync(rbd_dev, false);
        if (ret)
                rbd_warn(rbd_dev, "failed to cancel watch event (%d)\n", ret);
        rbd_dev_image_release(rbd_dev);
        module_put(THIS_MODULE);
-       ret = count;
-done:
-       mutex_unlock(&ctl_mutex);
 
-       return ret;
+       return count;
 }
 
 /*
@@ -5267,6 +5272,7 @@ static void __exit rbd_exit(void)
 module_init(rbd_init);
 module_exit(rbd_exit);
 
+MODULE_AUTHOR("Alex Elder <elder@inktank.com>");
 MODULE_AUTHOR("Sage Weil <sage@newdream.net>");
 MODULE_AUTHOR("Yehuda Sadeh <yehuda@hq.newdream.net>");
 MODULE_DESCRIPTION("rados block device");
index 13693b7..75c2626 100644 (file)
@@ -554,6 +554,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
        skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC);
        if (skb == NULL) {
                BT_ERR("No free skb");
+               ret = -ENOMEM;
                goto exit;
        }
 
index 7a7e5f8..de4cf4d 100644 (file)
@@ -57,6 +57,9 @@ static struct usb_device_id btusb_table[] = {
        /* Apple-specific (Broadcom) devices */
        { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) },
 
+       /* MediaTek MT76x0E */
+       { USB_DEVICE(0x0e8d, 0x763f) },
+
        /* Broadcom SoftSailing reporting vendor specific */
        { USB_DEVICE(0x0a5c, 0x21e1) },
 
@@ -1616,6 +1619,7 @@ static struct usb_driver btusb_driver = {
 #ifdef CONFIG_PM
        .suspend        = btusb_suspend,
        .resume         = btusb_resume,
+       .reset_resume   = btusb_resume,
 #endif
        .id_table       = btusb_table,
        .supports_autosuspend = 1,
index 0628d7b..03c1dc1 100644 (file)
@@ -236,14 +236,14 @@ static int ati_configure(void)
 static int agp_ati_suspend(struct pci_dev *dev, pm_message_t state)
 {
        pci_save_state(dev);
-       pci_set_power_state(dev, 3);
+       pci_set_power_state(dev, PCI_D3hot);
 
        return 0;
 }
 
 static int agp_ati_resume(struct pci_dev *dev)
 {
-       pci_set_power_state(dev, 0);
+       pci_set_power_state(dev, PCI_D0);
        pci_restore_state(dev);
 
        return ati_configure();
index 2e04433..1b19239 100644 (file)
@@ -603,7 +603,8 @@ static int agp_mmap(struct file *file, struct vm_area_struct *vma)
                        vma->vm_ops = kerninfo.vm_ops;
                } else if (io_remap_pfn_range(vma, vma->vm_start,
                                (kerninfo.aper_base + offset) >> PAGE_SHIFT,
-                                           size, vma->vm_page_prot)) {
+                               size,
+                               pgprot_writecombine(vma->vm_page_prot))) {
                        goto out_again;
                }
                mutex_unlock(&(agp_fe.agp_mutex));
@@ -618,8 +619,9 @@ static int agp_mmap(struct file *file, struct vm_area_struct *vma)
                if (kerninfo.vm_ops) {
                        vma->vm_ops = kerninfo.vm_ops;
                } else if (io_remap_pfn_range(vma, vma->vm_start,
-                                           kerninfo.aper_base >> PAGE_SHIFT,
-                                           size, vma->vm_page_prot)) {
+                               kerninfo.aper_base >> PAGE_SHIFT,
+                               size,
+                               pgprot_writecombine(vma->vm_page_prot))) {
                        goto out_again;
                }
                mutex_unlock(&(agp_fe.agp_mutex));
index 62be3ec..be42a23 100644 (file)
@@ -399,8 +399,8 @@ static void agp_nvidia_remove(struct pci_dev *pdev)
 #ifdef CONFIG_PM
 static int agp_nvidia_suspend(struct pci_dev *pdev, pm_message_t state)
 {
-       pci_save_state (pdev);
-       pci_set_power_state (pdev, 3);
+       pci_save_state(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
 
        return 0;
 }
@@ -408,7 +408,7 @@ static int agp_nvidia_suspend(struct pci_dev *pdev, pm_message_t state)
 static int agp_nvidia_resume(struct pci_dev *pdev)
 {
        /* set power state 0 and restore PCI space */
-       pci_set_power_state (pdev, 0);
+       pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
 
        /* reconfigure AGP hardware again */
index c689697..04e6d6a 100644 (file)
@@ -479,6 +479,7 @@ int tp3780I_QueryAbilities(THINKPAD_BD_DATA * pBDData, MW_ABILITIES * pAbilities
        PRINTK_2(TRACE_TP3780I,
                "tp3780i::tp3780I_QueryAbilities entry pBDData %p\n", pBDData);
 
+       memset(pAbilities, 0, sizeof(*pAbilities));
        /* fill out standard constant fields */
        pAbilities->instr_per_sec = pBDData->rDspSettings.uIps;
        pAbilities->data_size = pBDData->rDspSettings.uDStoreSize;
index 5b2b5e6..661dc3e 100644 (file)
@@ -1112,64 +1112,6 @@ static int sg_count(struct scatterlist *sg_list, int nbytes, bool *chained)
        return sg_nents;
 }
 
-/**
- * sg_copy_end_to_buffer - Copy end data from SG list to a linear buffer
- * @sgl:                The SG list
- * @nents:              Number of SG entries
- * @buf:                Where to copy to
- * @buflen:             The number of bytes to copy
- * @skip:               The number of bytes to skip before copying.
- *                       Note: skip + buflen should equal SG total size.
- *
- * Returns the number of copied bytes.
- *
- **/
-static size_t sg_copy_end_to_buffer(struct scatterlist *sgl, unsigned int nents,
-                                   void *buf, size_t buflen, unsigned int skip)
-{
-       unsigned int offset = 0;
-       unsigned int boffset = 0;
-       struct sg_mapping_iter miter;
-       unsigned long flags;
-       unsigned int sg_flags = SG_MITER_ATOMIC;
-       size_t total_buffer = buflen + skip;
-
-       sg_flags |= SG_MITER_FROM_SG;
-
-       sg_miter_start(&miter, sgl, nents, sg_flags);
-
-       local_irq_save(flags);
-
-       while (sg_miter_next(&miter) && offset < total_buffer) {
-               unsigned int len;
-               unsigned int ignore;
-
-               if ((offset + miter.length) > skip) {
-                       if (offset < skip) {
-                               /* Copy part of this segment */
-                               ignore = skip - offset;
-                               len = miter.length - ignore;
-                               if (boffset + len > buflen)
-                                       len = buflen - boffset;
-                               memcpy(buf + boffset, miter.addr + ignore, len);
-                       } else {
-                               /* Copy all of this segment (up to buflen) */
-                               len = miter.length;
-                               if (boffset + len > buflen)
-                                       len = buflen - boffset;
-                               memcpy(buf + boffset, miter.addr, len);
-                       }
-                       boffset += len;
-               }
-               offset += miter.length;
-       }
-
-       sg_miter_stop(&miter);
-
-       local_irq_restore(flags);
-       return boffset;
-}
-
 /*
  * allocate and map the extended descriptor
  */
@@ -1800,7 +1742,7 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
 
        if (to_hash_later) {
                int nents = sg_count(areq->src, nbytes, &chained);
-               sg_copy_end_to_buffer(areq->src, nents,
+               sg_pcopy_to_buffer(areq->src, nents,
                                      req_ctx->bufnext,
                                      to_hash_later,
                                      nbytes - to_hash_later);
index c9cc08c..cc727ec 100644 (file)
@@ -1017,7 +1017,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device)
        struct page *xor_srcs[IOP_ADMA_NUM_SRC_TEST];
        struct page *zero_sum_srcs[IOP_ADMA_NUM_SRC_TEST + 1];
        dma_addr_t dma_srcs[IOP_ADMA_NUM_SRC_TEST + 1];
-       dma_addr_t dma_addr, dest_dma;
+       dma_addr_t dest_dma;
        struct dma_async_tx_descriptor *tx;
        struct dma_chan *dma_chan;
        dma_cookie_t cookie;
@@ -1516,7 +1516,7 @@ static int iop_adma_probe(struct platform_device *pdev)
                        goto err_free_iop_chan;
        }
 
-       dev_info(&pdev->dev, "Intel(R) IOP: ( %s%s%s%s%s%s%s)\n",
+       dev_info(&pdev->dev, "Intel(R) IOP: ( %s%s%s%s%s%s)\n",
                 dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "pq " : "",
                 dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "",
                 dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "",
index b16c50e..a7c54c8 100644 (file)
@@ -139,6 +139,7 @@ config DRM_I915
        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
@@ -213,6 +214,8 @@ source "drivers/gpu/drm/mgag200/Kconfig"
 
 source "drivers/gpu/drm/cirrus/Kconfig"
 
+source "drivers/gpu/drm/rcar-du/Kconfig"
+
 source "drivers/gpu/drm/shmobile/Kconfig"
 
 source "drivers/gpu/drm/omapdrm/Kconfig"
index 1c9f243..801bcaf 100644 (file)
@@ -12,7 +12,8 @@ drm-y       :=        drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
                drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \
                drm_crtc.o drm_modes.o drm_edid.o \
                drm_info.o drm_debugfs.o drm_encoder_slave.o \
-               drm_trace_points.o drm_global.o drm_prime.o
+               drm_trace_points.o drm_global.o drm_prime.o \
+               drm_rect.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
@@ -48,6 +49,7 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
+obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_OMAP) += omapdrm/
 obj-$(CONFIG_DRM_TILCDC)       += tilcdc/
index 02e52d5..622d4ae 100644 (file)
@@ -348,8 +348,24 @@ int ast_gem_create(struct drm_device *dev,
 int ast_bo_pin(struct ast_bo *bo, u32 pl_flag, u64 *gpu_addr);
 int ast_bo_unpin(struct ast_bo *bo);
 
-int ast_bo_reserve(struct ast_bo *bo, bool no_wait);
-void ast_bo_unreserve(struct ast_bo *bo);
+static inline int ast_bo_reserve(struct ast_bo *bo, bool no_wait)
+{
+       int ret;
+
+       ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+       if (ret) {
+               if (ret != -ERESTARTSYS && ret != -EBUSY)
+                       DRM_ERROR("reserve failed %p\n", bo);
+               return ret;
+       }
+       return 0;
+}
+
+static inline void ast_bo_unreserve(struct ast_bo *bo)
+{
+       ttm_bo_unreserve(&bo->bo);
+}
+
 void ast_ttm_placement(struct ast_bo *bo, int domain);
 int ast_bo_push_sysram(struct ast_bo *bo);
 int ast_mmap(struct file *filp, struct vm_area_struct *vma);
index fbc0823..7b33e14 100644 (file)
@@ -51,7 +51,7 @@ static void ast_dirty_update(struct ast_fbdev *afbdev,
        struct ast_bo *bo;
        int src_offset, dst_offset;
        int bpp = (afbdev->afb.base.bits_per_pixel + 7)/8;
-       int ret;
+       int ret = -EBUSY;
        bool unmap = false;
        bool store_for_later = false;
        int x2, y2;
@@ -65,7 +65,8 @@ static void ast_dirty_update(struct ast_fbdev *afbdev,
         * then the BO is being moved and we should
         * store up the damage until later.
         */
-       ret = ast_bo_reserve(bo, true);
+       if (!in_interrupt())
+               ret = ast_bo_reserve(bo, true);
        if (ret) {
                if (ret != -EBUSY)
                        return;
index 09da339..98d6708 100644 (file)
@@ -271,26 +271,19 @@ int ast_mm_init(struct ast_private *ast)
                return ret;
        }
 
-       ast->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0),
-                                   pci_resource_len(dev->pdev, 0),
-                                   DRM_MTRR_WC);
+       ast->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0),
+                                       pci_resource_len(dev->pdev, 0));
 
        return 0;
 }
 
 void ast_mm_fini(struct ast_private *ast)
 {
-       struct drm_device *dev = ast->dev;
        ttm_bo_device_release(&ast->ttm.bdev);
 
        ast_ttm_global_release(ast);
 
-       if (ast->fb_mtrr >= 0) {
-               drm_mtrr_del(ast->fb_mtrr,
-                            pci_resource_start(dev->pdev, 0),
-                            pci_resource_len(dev->pdev, 0), DRM_MTRR_WC);
-               ast->fb_mtrr = -1;
-       }
+       arch_phys_wc_del(ast->fb_mtrr);
 }
 
 void ast_ttm_placement(struct ast_bo *bo, int domain)
@@ -310,24 +303,6 @@ void ast_ttm_placement(struct ast_bo *bo, int domain)
        bo->placement.num_busy_placement = c;
 }
 
-int ast_bo_reserve(struct ast_bo *bo, bool no_wait)
-{
-       int ret;
-
-       ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
-       if (ret) {
-               if (ret != -ERESTARTSYS && ret != -EBUSY)
-                       DRM_ERROR("reserve failed %p\n", bo);
-               return ret;
-       }
-       return 0;
-}
-
-void ast_bo_unreserve(struct ast_bo *bo)
-{
-       ttm_bo_unreserve(&bo->bo);
-}
-
 int ast_bo_create(struct drm_device *dev, int size, int align,
                  uint32_t flags, struct ast_bo **pastbo)
 {
index 7ca0595..bae5560 100644 (file)
@@ -240,8 +240,25 @@ void cirrus_ttm_placement(struct cirrus_bo *bo, int domain);
 int cirrus_bo_create(struct drm_device *dev, int size, int align,
                     uint32_t flags, struct cirrus_bo **pcirrusbo);
 int cirrus_mmap(struct file *filp, struct vm_area_struct *vma);
-int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait);
-void cirrus_bo_unreserve(struct cirrus_bo *bo);
+
+static inline int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait)
+{
+       int ret;
+
+       ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+       if (ret) {
+               if (ret != -ERESTARTSYS && ret != -EBUSY)
+                       DRM_ERROR("reserve failed %p\n", bo);
+               return ret;
+       }
+       return 0;
+}
+
+static inline void cirrus_bo_unreserve(struct cirrus_bo *bo)
+{
+       ttm_bo_unreserve(&bo->bo);
+}
+
 int cirrus_bo_push_sysram(struct cirrus_bo *bo);
 int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr);
 #endif                         /* __CIRRUS_DRV_H__ */
index 3541b56..b27e956 100644 (file)
@@ -25,7 +25,7 @@ static void cirrus_dirty_update(struct cirrus_fbdev *afbdev,
        struct cirrus_bo *bo;
        int src_offset, dst_offset;
        int bpp = (afbdev->gfb.base.bits_per_pixel + 7)/8;
-       int ret;
+       int ret = -EBUSY;
        bool unmap = false;
        bool store_for_later = false;
        int x2, y2;
@@ -39,7 +39,8 @@ static void cirrus_dirty_update(struct cirrus_fbdev *afbdev,
         * then the BO is being moved and we should
         * store up the damage until later.
         */
-       ret = cirrus_bo_reserve(bo, true);
+       if (!in_interrupt())
+               ret = cirrus_bo_reserve(bo, true);
        if (ret) {
                if (ret != -EBUSY)
                        return;
index 2ed8cfc..0047012 100644 (file)
@@ -271,9 +271,8 @@ int cirrus_mm_init(struct cirrus_device *cirrus)
                return ret;
        }
 
-       cirrus->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0),
-                                   pci_resource_len(dev->pdev, 0),
-                                   DRM_MTRR_WC);
+       cirrus->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0),
+                                          pci_resource_len(dev->pdev, 0));
 
        cirrus->mm_inited = true;
        return 0;
@@ -281,8 +280,6 @@ int cirrus_mm_init(struct cirrus_device *cirrus)
 
 void cirrus_mm_fini(struct cirrus_device *cirrus)
 {
-       struct drm_device *dev = cirrus->dev;
-
        if (!cirrus->mm_inited)
                return;
 
@@ -290,12 +287,8 @@ void cirrus_mm_fini(struct cirrus_device *cirrus)
 
        cirrus_ttm_global_release(cirrus);
 
-       if (cirrus->fb_mtrr >= 0) {
-               drm_mtrr_del(cirrus->fb_mtrr,
-                            pci_resource_start(dev->pdev, 0),
-                            pci_resource_len(dev->pdev, 0), DRM_MTRR_WC);
-               cirrus->fb_mtrr = -1;
-       }
+       arch_phys_wc_del(cirrus->fb_mtrr);
+       cirrus->fb_mtrr = 0;
 }
 
 void cirrus_ttm_placement(struct cirrus_bo *bo, int domain)
@@ -315,24 +308,6 @@ void cirrus_ttm_placement(struct cirrus_bo *bo, int domain)
        bo->placement.num_busy_placement = c;
 }
 
-int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait)
-{
-       int ret;
-
-       ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
-       if (ret) {
-               if (ret != -ERESTARTSYS && ret != -EBUSY)
-                       DRM_ERROR("reserve failed %p\n", bo);
-               return ret;
-       }
-       return 0;
-}
-
-void cirrus_bo_unreserve(struct cirrus_bo *bo)
-{
-       ttm_bo_unreserve(&bo->bo);
-}
-
 int cirrus_bo_create(struct drm_device *dev, int size, int align,
                  uint32_t flags, struct cirrus_bo **pcirrusbo)
 {
index 0128147..5a4dbb4 100644 (file)
@@ -210,12 +210,16 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
                if (drm_core_has_MTRR(dev)) {
                        if (map->type == _DRM_FRAME_BUFFER ||
                            (map->flags & _DRM_WRITE_COMBINING)) {
-                               map->mtrr = mtrr_add(map->offset, map->size,
-                                                    MTRR_TYPE_WRCOMB, 1);
+                               map->mtrr =
+                                       arch_phys_wc_add(map->offset, map->size);
                        }
                }
                if (map->type == _DRM_REGISTERS) {
-                       map->handle = ioremap(map->offset, map->size);
+                       if (map->flags & _DRM_WRITE_COMBINING)
+                               map->handle = ioremap_wc(map->offset,
+                                                        map->size);
+                       else
+                               map->handle = ioremap(map->offset, map->size);
                        if (!map->handle) {
                                kfree(map);
                                return -ENOMEM;
@@ -410,6 +414,15 @@ int drm_addmap_ioctl(struct drm_device *dev, void *data,
 
        /* avoid a warning on 64-bit, this casting isn't very nice, but the API is set so too late */
        map->handle = (void *)(unsigned long)maplist->user_token;
+
+       /*
+        * It appears that there are no users of this value whatsoever --
+        * drmAddMap just discards it.  Let's not encourage its use.
+        * (Keeping drm_addmap_core's returned mtrr value would be wrong --
+        *  it's not a real mtrr index anymore.)
+        */
+       map->mtrr = -1;
+
        return 0;
 }
 
@@ -451,11 +464,8 @@ int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map)
                iounmap(map->handle);
                /* FALLTHROUGH */
        case _DRM_FRAME_BUFFER:
-               if (drm_core_has_MTRR(dev) && map->mtrr >= 0) {
-                       int retcode;
-                       retcode = mtrr_del(map->mtrr, map->offset, map->size);
-                       DRM_DEBUG("mtrr_del=%d\n", retcode);
-               }
+               if (drm_core_has_MTRR(dev))
+                       arch_phys_wc_del(map->mtrr);
                break;
        case _DRM_SHM:
                vfree(map->handle);
index e7e9242..fc83bb9 100644 (file)
@@ -29,6 +29,7 @@
  *      Dave Airlie <airlied@linux.ie>
  *      Jesse Barnes <jesse.barnes@intel.com>
  */
+#include <linux/ctype.h>
 #include <linux/list.h>
 #include <linux/slab.h>
 #include <linux/export.h>
@@ -91,7 +92,7 @@ EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
 
 /* Avoid boilerplate.  I'm tired of typing. */
 #define DRM_ENUM_NAME_FN(fnname, list)                         \
-       char *fnname(int val)                                   \
+       const char *fnname(int val)                             \
        {                                                       \
                int i;                                          \
                for (i = 0; i < ARRAY_SIZE(list); i++) {        \
@@ -104,7 +105,7 @@ EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
 /*
  * Global properties
  */
-static struct drm_prop_enum_list drm_dpms_enum_list[] =
+static const struct drm_prop_enum_list drm_dpms_enum_list[] =
 {      { DRM_MODE_DPMS_ON, "On" },
        { DRM_MODE_DPMS_STANDBY, "Standby" },
        { DRM_MODE_DPMS_SUSPEND, "Suspend" },
@@ -116,7 +117,7 @@ DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
 /*
  * Optional properties
  */
-static struct drm_prop_enum_list drm_scaling_mode_enum_list[] =
+static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] =
 {
        { DRM_MODE_SCALE_NONE, "None" },
        { DRM_MODE_SCALE_FULLSCREEN, "Full" },
@@ -124,7 +125,7 @@ static struct drm_prop_enum_list drm_scaling_mode_enum_list[] =
        { DRM_MODE_SCALE_ASPECT, "Full aspect" },
 };
 
-static struct drm_prop_enum_list drm_dithering_mode_enum_list[] =
+static const struct drm_prop_enum_list drm_dithering_mode_enum_list[] =
 {
        { DRM_MODE_DITHERING_OFF, "Off" },
        { DRM_MODE_DITHERING_ON, "On" },
@@ -134,7 +135,7 @@ static struct drm_prop_enum_list drm_dithering_mode_enum_list[] =
 /*
  * Non-global properties, but "required" for certain connectors.
  */
-static struct drm_prop_enum_list drm_dvi_i_select_enum_list[] =
+static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] =
 {
        { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
        { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
@@ -143,7 +144,7 @@ static struct drm_prop_enum_list drm_dvi_i_select_enum_list[] =
 
 DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
 
-static struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] =
+static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] =
 {
        { DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
        { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
@@ -153,7 +154,7 @@ static struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] =
 DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
                 drm_dvi_i_subconnector_enum_list)
 
-static struct drm_prop_enum_list drm_tv_select_enum_list[] =
+static const struct drm_prop_enum_list drm_tv_select_enum_list[] =
 {
        { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
        { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
@@ -164,7 +165,7 @@ static struct drm_prop_enum_list drm_tv_select_enum_list[] =
 
 DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
 
-static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] =
+static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] =
 {
        { DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
        { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
@@ -176,7 +177,7 @@ static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] =
 DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
                 drm_tv_subconnector_enum_list)
 
-static struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
+static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
        { DRM_MODE_DIRTY_OFF,      "Off"      },
        { DRM_MODE_DIRTY_ON,       "On"       },
        { DRM_MODE_DIRTY_ANNOTATE, "Annotate" },
@@ -184,7 +185,7 @@ static struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
 
 struct drm_conn_prop_enum_list {
        int type;
-       char *name;
+       const char *name;
        int count;
 };
 
@@ -210,7 +211,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] =
        { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual", 0},
 };
 
-static struct drm_prop_enum_list drm_encoder_enum_list[] =
+static const struct drm_prop_enum_list drm_encoder_enum_list[] =
 {      { DRM_MODE_ENCODER_NONE, "None" },
        { DRM_MODE_ENCODER_DAC, "DAC" },
        { DRM_MODE_ENCODER_TMDS, "TMDS" },
@@ -219,7 +220,7 @@ static struct drm_prop_enum_list drm_encoder_enum_list[] =
        { DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
 };
 
-char *drm_get_encoder_name(struct drm_encoder *encoder)
+const char *drm_get_encoder_name(const struct drm_encoder *encoder)
 {
        static char buf[32];
 
@@ -230,7 +231,7 @@ char *drm_get_encoder_name(struct drm_encoder *encoder)
 }
 EXPORT_SYMBOL(drm_get_encoder_name);
 
-char *drm_get_connector_name(struct drm_connector *connector)
+const char *drm_get_connector_name(const struct drm_connector *connector)
 {
        static char buf[32];
 
@@ -241,7 +242,7 @@ char *drm_get_connector_name(struct drm_connector *connector)
 }
 EXPORT_SYMBOL(drm_get_connector_name);
 
-char *drm_get_connector_status_name(enum drm_connector_status status)
+const char *drm_get_connector_status_name(enum drm_connector_status status)
 {
        if (status == connector_status_connected)
                return "connected";
@@ -252,6 +253,28 @@ char *drm_get_connector_status_name(enum drm_connector_status status)
 }
 EXPORT_SYMBOL(drm_get_connector_status_name);
 
+static char printable_char(int c)
+{
+       return isascii(c) && isprint(c) ? c : '?';
+}
+
+const char *drm_get_format_name(uint32_t format)
+{
+       static char buf[32];
+
+       snprintf(buf, sizeof(buf),
+                "%c%c%c%c %s-endian (0x%08x)",
+                printable_char(format & 0xff),
+                printable_char((format >> 8) & 0xff),
+                printable_char((format >> 16) & 0xff),
+                printable_char((format >> 24) & 0x7f),
+                format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little",
+                format);
+
+       return buf;
+}
+EXPORT_SYMBOL(drm_get_format_name);
+
 /**
  * drm_mode_object_get - allocate a new modeset identifier
  * @dev: DRM device
@@ -569,16 +592,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
                }
 
                list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
-                       if (plane->fb == fb) {
-                               /* should turn off the crtc */
-                               ret = plane->funcs->disable_plane(plane);
-                               if (ret)
-                                       DRM_ERROR("failed to disable plane with busy fb\n");
-                               /* disconnect the plane from the fb and crtc: */
-                               __drm_framebuffer_unreference(plane->fb);
-                               plane->fb = NULL;
-                               plane->crtc = NULL;
-                       }
+                       if (plane->fb == fb)
+                               drm_plane_force_disable(plane);
                }
                drm_modeset_unlock_all(dev);
        }
@@ -593,7 +608,7 @@ EXPORT_SYMBOL(drm_framebuffer_remove);
  * @crtc: CRTC object to init
  * @funcs: callbacks for the new CRTC
  *
- * Inits a new object created as base part of an driver crtc object.
+ * Inits a new object created as base part of a driver crtc object.
  *
  * RETURNS:
  * Zero on success, error code on failure.
@@ -628,11 +643,12 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
 EXPORT_SYMBOL(drm_crtc_init);
 
 /**
- * drm_crtc_cleanup - Cleans up the core crtc usage.
+ * drm_crtc_cleanup - Clean up the core crtc usage
  * @crtc: CRTC to cleanup
  *
- * Cleanup @crtc. Removes from drm modesetting space
- * does NOT free object, caller does that.
+ * This function cleans up @crtc and removes it from the DRM mode setting
+ * core. Note that the function does *not* free the crtc structure itself,
+ * this is the responsibility of the caller.
  */
 void drm_crtc_cleanup(struct drm_crtc *crtc)
 {
@@ -657,7 +673,7 @@ EXPORT_SYMBOL(drm_crtc_cleanup);
 void drm_mode_probed_add(struct drm_connector *connector,
                         struct drm_display_mode *mode)
 {
-       list_add(&mode->head, &connector->probed_modes);
+       list_add_tail(&mode->head, &connector->probed_modes);
 }
 EXPORT_SYMBOL(drm_mode_probed_add);
 
@@ -803,6 +819,21 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
 }
 EXPORT_SYMBOL(drm_encoder_cleanup);
 
+/**
+ * drm_plane_init - Initialise a new plane object
+ * @dev: DRM device
+ * @plane: plane object to init
+ * @possible_crtcs: bitmask of possible CRTCs
+ * @funcs: callbacks for the new plane
+ * @formats: array of supported formats (%DRM_FORMAT_*)
+ * @format_count: number of elements in @formats
+ * @priv: plane is private (hidden from userspace)?
+ *
+ * Inits a new object created as base part of a driver plane object.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure.
+ */
 int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
                   unsigned long possible_crtcs,
                   const struct drm_plane_funcs *funcs,
@@ -851,6 +882,14 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 }
 EXPORT_SYMBOL(drm_plane_init);
 
+/**
+ * drm_plane_cleanup - Clean up the core plane usage
+ * @plane: plane to cleanup
+ *
+ * This function cleans up @plane and removes it from the DRM mode setting
+ * core. Note that the function does *not* free the plane structure itself,
+ * this is the responsibility of the caller.
+ */
 void drm_plane_cleanup(struct drm_plane *plane)
 {
        struct drm_device *dev = plane->dev;
@@ -867,6 +906,32 @@ void drm_plane_cleanup(struct drm_plane *plane)
 }
 EXPORT_SYMBOL(drm_plane_cleanup);
 
+/**
+ * drm_plane_force_disable - Forcibly disable a plane
+ * @plane: plane to disable
+ *
+ * Forces the plane to be disabled.
+ *
+ * Used when the plane's current framebuffer is destroyed,
+ * and when restoring fbdev mode.
+ */
+void drm_plane_force_disable(struct drm_plane *plane)
+{
+       int ret;
+
+       if (!plane->fb)
+               return;
+
+       ret = plane->funcs->disable_plane(plane);
+       if (ret)
+               DRM_ERROR("failed to disable plane with busy fb\n");
+       /* disconnect the plane from the fb and crtc: */
+       __drm_framebuffer_unreference(plane->fb);
+       plane->fb = NULL;
+       plane->crtc = NULL;
+}
+EXPORT_SYMBOL(drm_plane_force_disable);
+
 /**
  * drm_mode_create - create a new display mode
  * @dev: DRM device
@@ -1740,7 +1805,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
 
        plane_resp->plane_id = plane->base.id;
        plane_resp->possible_crtcs = plane->possible_crtcs;
-       plane_resp->gamma_size = plane->gamma_size;
+       plane_resp->gamma_size = 0;
 
        /*
         * This ioctl is called twice, once to determine how much space is
@@ -1834,7 +1899,8 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
                if (fb->pixel_format == plane->format_types[i])
                        break;
        if (i == plane->format_count) {
-               DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
+               DRM_DEBUG_KMS("Invalid pixel format %s\n",
+                             drm_get_format_name(fb->pixel_format));
                ret = -EINVAL;
                goto out;
        }
@@ -1906,18 +1972,31 @@ out:
 int drm_mode_set_config_internal(struct drm_mode_set *set)
 {
        struct drm_crtc *crtc = set->crtc;
-       struct drm_framebuffer *fb, *old_fb;
+       struct drm_framebuffer *fb;
+       struct drm_crtc *tmp;
        int ret;
 
-       old_fb = crtc->fb;
+       /*
+        * NOTE: ->set_config can also disable other crtcs (if we steal all
+        * connectors from it), hence we need to refcount the fbs across all
+        * crtcs. Atomic modeset will have saner semantics ...
+        */
+       list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head)
+               tmp->old_fb = tmp->fb;
+
        fb = set->fb;
 
        ret = crtc->funcs->set_config(set);
        if (ret == 0) {
-               if (old_fb)
-                       drm_framebuffer_unreference(old_fb);
-               if (fb)
-                       drm_framebuffer_reference(fb);
+               /* crtc->fb must be updated by ->set_config, enforces this. */
+               WARN_ON(fb != crtc->fb);
+       }
+
+       list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
+               if (tmp->fb)
+                       drm_framebuffer_reference(tmp->fb);
+               if (tmp->old_fb)
+                       drm_framebuffer_unreference(tmp->old_fb);
        }
 
        return ret;
@@ -2099,10 +2178,10 @@ out:
        return ret;
 }
 
-int drm_mode_cursor_ioctl(struct drm_device *dev,
-                       void *data, struct drm_file *file_priv)
+static int drm_mode_cursor_common(struct drm_device *dev,
+                                 struct drm_mode_cursor2 *req,
+                                 struct drm_file *file_priv)
 {
-       struct drm_mode_cursor *req = data;
        struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        int ret = 0;
@@ -2122,13 +2201,17 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
 
        mutex_lock(&crtc->mutex);
        if (req->flags & DRM_MODE_CURSOR_BO) {
-               if (!crtc->funcs->cursor_set) {
+               if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
                        ret = -ENXIO;
                        goto out;
                }
                /* Turns off the cursor if handle is 0 */
-               ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,
-                                             req->width, req->height);
+               if (crtc->funcs->cursor_set2)
+                       ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle,
+                                                     req->width, req->height, req->hot_x, req->hot_y);
+               else
+                       ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,
+                                                     req->width, req->height);
        }
 
        if (req->flags & DRM_MODE_CURSOR_MOVE) {
@@ -2143,6 +2226,25 @@ out:
        mutex_unlock(&crtc->mutex);
 
        return ret;
+
+}
+int drm_mode_cursor_ioctl(struct drm_device *dev,
+                       void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_cursor *req = data;
+       struct drm_mode_cursor2 new_req;
+
+       memcpy(&new_req, req, sizeof(struct drm_mode_cursor));
+       new_req.hot_x = new_req.hot_y = 0;
+
+       return drm_mode_cursor_common(dev, &new_req, file_priv);
+}
+
+int drm_mode_cursor2_ioctl(struct drm_device *dev,
+                          void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_cursor2 *req = data;
+       return drm_mode_cursor_common(dev, req, file_priv);
 }
 
 /* Original addfb only supported RGB formats, so figure out which one */
@@ -2312,7 +2414,8 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
 
        ret = format_check(r);
        if (ret) {
-               DRM_DEBUG_KMS("bad framebuffer format 0x%08x\n", r->pixel_format);
+               DRM_DEBUG_KMS("bad framebuffer format %s\n",
+                             drm_get_format_name(r->pixel_format));
                return ret;
        }
 
index ed1334e..738a429 100644 (file)
@@ -189,13 +189,14 @@ prune:
        if (list_empty(&connector->modes))
                return 0;
 
+       list_for_each_entry(mode, &connector->modes, head)
+               mode->vrefresh = drm_mode_vrefresh(mode);
+
        drm_mode_sort(&connector->modes);
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id,
                        drm_get_connector_name(connector));
        list_for_each_entry(mode, &connector->modes, head) {
-               mode->vrefresh = drm_mode_vrefresh(mode);
-
                drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
                drm_mode_debug_printmodeline(mode);
        }
@@ -564,14 +565,13 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
 
        DRM_DEBUG_KMS("\n");
 
-       if (!set)
-               return -EINVAL;
-
-       if (!set->crtc)
-               return -EINVAL;
+       BUG_ON(!set);
+       BUG_ON(!set->crtc);
+       BUG_ON(!set->crtc->helper_private);
 
-       if (!set->crtc->helper_private)
-               return -EINVAL;
+       /* Enforce sane interface api - has been abused by the fb helper. */
+       BUG_ON(!set->mode && set->fb);
+       BUG_ON(set->fb && set->num_connectors == 0);
 
        crtc_funcs = set->crtc->helper_private;
 
@@ -645,11 +645,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                        mode_changed = true;
                } else if (set->fb == NULL) {
                        mode_changed = true;
-               } else if (set->fb->depth != set->crtc->fb->depth) {
-                       mode_changed = true;
-               } else if (set->fb->bits_per_pixel !=
-                          set->crtc->fb->bits_per_pixel) {
-                       mode_changed = true;
                } else if (set->fb->pixel_format !=
                           set->crtc->fb->pixel_format) {
                        mode_changed = true;
@@ -759,12 +754,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                                ret = -EINVAL;
                                goto fail;
                        }
-                       DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
-                       for (i = 0; i < set->num_connectors; i++) {
-                               DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
-                                             drm_get_connector_name(set->connectors[i]));
-                               set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
-                       }
                }
                drm_helper_disable_unused_functions(dev);
        } else if (fb_changed) {
@@ -782,6 +771,22 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                }
        }
 
+       /*
+        * crtc set_config helpers implicit set the crtc and all connected
+        * encoders to DPMS on for a full mode set. But for just an fb update it
+        * doesn't do that. To not confuse userspace, do an explicit DPMS_ON
+        * unconditionally. This will also ensure driver internal dpms state is
+        * consistent again.
+        */
+       if (set->crtc->enabled) {
+               DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
+               for (i = 0; i < set->num_connectors; i++) {
+                       DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
+                                     drm_get_connector_name(set->connectors[i]));
+                       set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
+               }
+       }
+
        kfree(save_connectors);
        kfree(save_encoders);
        kfree(save_crtcs);
index 9cc247f..99fcd7c 100644 (file)
@@ -166,6 +166,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
 };
 
 #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
index 9e62bbe..95d6f4b 100644 (file)
@@ -968,6 +968,9 @@ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid)
        u8 csum = 0;
        struct edid *edid = (struct edid *)raw_edid;
 
+       if (WARN_ON(!raw_edid))
+               return false;
+
        if (edid_fixup > 8 || edid_fixup < 0)
                edid_fixup = 6;
 
@@ -1010,15 +1013,15 @@ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid)
                break;
        }
 
-       return 1;
+       return true;
 
 bad:
-       if (raw_edid && print_bad_edid) {
+       if (print_bad_edid) {
                printk(KERN_ERR "Raw EDID:\n");
                print_hex_dump(KERN_ERR, " \t", DUMP_PREFIX_NONE, 16, 1,
                               raw_edid, EDID_LENGTH, false);
        }
-       return 0;
+       return false;
 }
 EXPORT_SYMBOL(drm_edid_block_valid);
 
@@ -1706,11 +1709,11 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
                return NULL;
 
        if (pt->misc & DRM_EDID_PT_STEREO) {
-               printk(KERN_WARNING "stereo mode not supported\n");
+               DRM_DEBUG_KMS("stereo mode not supported\n");
                return NULL;
        }
        if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) {
-               printk(KERN_WARNING "composite sync not supported\n");
+               DRM_DEBUG_KMS("composite sync not supported\n");
        }
 
        /* it is incorrect if hsync/vsync width is zero */
@@ -2321,6 +2324,31 @@ u8 *drm_find_cea_extension(struct edid *edid)
 }
 EXPORT_SYMBOL(drm_find_cea_extension);
 
+/*
+ * Calculate the alternate clock for the CEA mode
+ * (60Hz vs. 59.94Hz etc.)
+ */
+static unsigned int
+cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
+{
+       unsigned int clock = cea_mode->clock;
+
+       if (cea_mode->vrefresh % 6 != 0)
+               return clock;
+
+       /*
+        * edid_cea_modes contains the 59.94Hz
+        * variant for 240 and 480 line modes,
+        * and the 60Hz variant otherwise.
+        */
+       if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480)
+               clock = clock * 1001 / 1000;
+       else
+               clock = DIV_ROUND_UP(clock * 1000, 1001);
+
+       return clock;
+}
+
 /**
  * drm_match_cea_mode - look for a CEA mode matching given mode
  * @to_match: display mode
@@ -2339,21 +2367,9 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
                const struct drm_display_mode *cea_mode = &edid_cea_modes[mode];
                unsigned int clock1, clock2;
 
-               clock1 = clock2 = cea_mode->clock;
-
                /* Check both 60Hz and 59.94Hz */
-               if (cea_mode->vrefresh % 6 == 0) {
-                       /*
-                        * edid_cea_modes contains the 59.94Hz
-                        * variant for 240 and 480 line modes,
-                        * and the 60Hz variant otherwise.
-                        */
-                       if (cea_mode->vdisplay == 240 ||
-                           cea_mode->vdisplay == 480)
-                               clock1 = clock1 * 1001 / 1000;
-                       else
-                               clock2 = DIV_ROUND_UP(clock2 * 1000, 1001);
-               }
+               clock1 = cea_mode->clock;
+               clock2 = cea_mode_alternate_clock(cea_mode);
 
                if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||
                     KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) &&
@@ -2364,6 +2380,66 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
 }
 EXPORT_SYMBOL(drm_match_cea_mode);
 
+static int
+add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_display_mode *mode, *tmp;
+       LIST_HEAD(list);
+       int modes = 0;
+
+       /* Don't add CEA modes if the CEA extension block is missing */
+       if (!drm_find_cea_extension(edid))
+               return 0;
+
+       /*
+        * Go through all probed modes and create a new mode
+        * with the alternate clock for certain CEA modes.
+        */
+       list_for_each_entry(mode, &connector->probed_modes, head) {
+               const struct drm_display_mode *cea_mode;
+               struct drm_display_mode *newmode;
+               u8 cea_mode_idx = drm_match_cea_mode(mode) - 1;
+               unsigned int clock1, clock2;
+
+               if (cea_mode_idx >= ARRAY_SIZE(edid_cea_modes))
+                       continue;
+
+               cea_mode = &edid_cea_modes[cea_mode_idx];
+
+               clock1 = cea_mode->clock;
+               clock2 = cea_mode_alternate_clock(cea_mode);
+
+               if (clock1 == clock2)
+                       continue;
+
+               if (mode->clock != clock1 && mode->clock != clock2)
+                       continue;
+
+               newmode = drm_mode_duplicate(dev, cea_mode);
+               if (!newmode)
+                       continue;
+
+               /*
+                * The current mode could be either variant. Make
+                * sure to pick the "other" clock for the new mode.
+                */
+               if (mode->clock != clock1)
+                       newmode->clock = clock1;
+               else
+                       newmode->clock = clock2;
+
+               list_add_tail(&newmode->head, &list);
+       }
+
+       list_for_each_entry_safe(mode, tmp, &list, head) {
+               list_del(&mode->head);
+               drm_mode_probed_add(connector, mode);
+               modes++;
+       }
+
+       return modes;
+}
 
 static int
 do_cea_modes (struct drm_connector *connector, u8 *db, u8 len)
@@ -2946,6 +3022,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
        if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
                num_modes += add_inferred_modes(connector, edid);
        num_modes += add_cea_modes(connector, edid);
+       num_modes += add_alternate_cea_modes(connector, edid);
 
        if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
                edid_fixup_preferred(connector, quirks);
index fa445dd..a4f5ce1 100644 (file)
@@ -186,12 +186,11 @@ static u8 *edid_load(struct drm_connector *connector, char *name,
                goto relfw_out;
        }
 
-       edid = kmalloc(fwsize, GFP_KERNEL);
+       edid = kmemdup(fwdata, fwsize, GFP_KERNEL);
        if (edid == NULL) {
                err = -ENOMEM;
                goto relfw_out;
        }
-       memcpy(edid, fwdata, fwsize);
 
        if (!drm_edid_block_valid(edid, 0, print_bad_edid)) {
                connector->bad_edid_counter++;
index b78cbe7..3d13ca6 100644 (file)
@@ -168,6 +168,9 @@ static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_h
        uint16_t *r_base, *g_base, *b_base;
        int i;
 
+       if (helper->funcs->gamma_get == NULL)
+               return;
+
        r_base = crtc->gamma_store;
        g_base = r_base + crtc->gamma_size;
        b_base = g_base + crtc->gamma_size;
@@ -284,13 +287,27 @@ EXPORT_SYMBOL(drm_fb_helper_debug_leave);
  */
 bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 {
+       struct drm_device *dev = fb_helper->dev;
+       struct drm_plane *plane;
        bool error = false;
-       int i, ret;
+       int i;
+
+       drm_warn_on_modeset_not_all_locked(dev);
 
-       drm_warn_on_modeset_not_all_locked(fb_helper->dev);
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head)
+               drm_plane_force_disable(plane);
 
        for (i = 0; i < fb_helper->crtc_count; i++) {
                struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
+               struct drm_crtc *crtc = mode_set->crtc;
+               int ret;
+
+               if (crtc->funcs->cursor_set) {
+                       ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
+                       if (ret)
+                               error = true;
+               }
+
                ret = drm_mode_set_config_internal(mode_set);
                if (ret)
                        error = true;
@@ -583,6 +600,14 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
                return 0;
        }
 
+       /*
+        * The driver really shouldn't advertise pseudo/directcolor
+        * visuals if it can't deal with the palette.
+        */
+       if (WARN_ON(!fb_helper->funcs->gamma_set ||
+                   !fb_helper->funcs->gamma_get))
+               return -EINVAL;
+
        pindex = regno;
 
        if (fb->bits_per_pixel == 16) {
@@ -626,12 +651,19 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 {
        struct drm_fb_helper *fb_helper = info->par;
+       struct drm_device *dev = fb_helper->dev;
        struct drm_crtc_helper_funcs *crtc_funcs;
        u16 *red, *green, *blue, *transp;
        struct drm_crtc *crtc;
        int i, j, rc = 0;
        int start;
 
+       drm_modeset_lock_all(dev);
+       if (!drm_fb_helper_is_bound(fb_helper)) {
+               drm_modeset_unlock_all(dev);
+               return -EBUSY;
+       }
+
        for (i = 0; i < fb_helper->crtc_count; i++) {
                crtc = fb_helper->crtc_info[i].mode_set.crtc;
                crtc_funcs = crtc->helper_private;
@@ -654,10 +686,13 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 
                        rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
                        if (rc)
-                               return rc;
+                               goto out;
                }
-               crtc_funcs->load_lut(crtc);
+               if (crtc_funcs->load_lut)
+                       crtc_funcs->load_lut(crtc);
        }
+ out:
+       drm_modeset_unlock_all(dev);
        return rc;
 }
 EXPORT_SYMBOL(drm_fb_helper_setcmap);
index 429e07d..3a24385 100644 (file)
@@ -271,6 +271,11 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
        priv->uid = current_euid();
        priv->pid = get_pid(task_pid(current));
        priv->minor = idr_find(&drm_minors_idr, minor_id);
+       if (!priv->minor) {
+               ret = -ENODEV;
+               goto out_put_pid;
+       }
+
        priv->ioctl_count = 0;
        /* for compatibility root is always authenticated */
        priv->authenticated = capable(CAP_SYS_ADMIN);
@@ -292,7 +297,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
        if (dev->driver->open) {
                ret = dev->driver->open(dev, priv);
                if (ret < 0)
-                       goto out_free;
+                       goto out_prime_destroy;
        }
 
 
@@ -304,7 +309,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
                if (!priv->minor->master) {
                        mutex_unlock(&dev->struct_mutex);
                        ret = -ENOMEM;
-                       goto out_free;
+                       goto out_close;
                }
 
                priv->is_master = 1;
@@ -322,7 +327,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
                                drm_master_put(&priv->minor->master);
                                drm_master_put(&priv->master);
                                mutex_unlock(&dev->struct_mutex);
-                               goto out_free;
+                               goto out_close;
                        }
                }
                mutex_lock(&dev->struct_mutex);
@@ -333,7 +338,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
                                drm_master_put(&priv->minor->master);
                                drm_master_put(&priv->master);
                                mutex_unlock(&dev->struct_mutex);
-                               goto out_free;
+                               goto out_close;
                        }
                }
                mutex_unlock(&dev->struct_mutex);
@@ -367,7 +372,17 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
 #endif
 
        return 0;
-      out_free:
+
+out_close:
+       if (dev->driver->postclose)
+               dev->driver->postclose(dev, priv);
+out_prime_destroy:
+       if (drm_core_check_feature(dev, DRIVER_PRIME))
+               drm_prime_destroy_file_private(&priv->prime);
+       if (dev->driver->driver_features & DRIVER_GEM)
+               drm_gem_release(dev, priv);
+out_put_pid:
+       put_pid(priv->pid);
        kfree(priv);
        filp->private_data = NULL;
        return ret;
index cf919e3..603f256 100644 (file)
@@ -108,12 +108,8 @@ drm_gem_init(struct drm_device *dev)
                return -ENOMEM;
        }
 
-       if (drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
-                       DRM_FILE_PAGE_OFFSET_SIZE)) {
-               drm_ht_remove(&mm->offset_hash);
-               kfree(mm);
-               return -ENOMEM;
-       }
+       drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
+                   DRM_FILE_PAGE_OFFSET_SIZE);
 
        return 0;
 }
@@ -453,25 +449,21 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data,
        spin_lock(&dev->object_name_lock);
        if (!obj->name) {
                ret = idr_alloc(&dev->object_name_idr, obj, 1, 0, GFP_NOWAIT);
-               obj->name = ret;
-               args->name = (uint64_t) obj->name;
-               spin_unlock(&dev->object_name_lock);
-               idr_preload_end();
-
                if (ret < 0)
                        goto err;
-               ret = 0;
+
+               obj->name = ret;
 
                /* Allocate a reference for the name table.  */
                drm_gem_object_reference(obj);
-       } else {
-               args->name = (uint64_t) obj->name;
-               spin_unlock(&dev->object_name_lock);
-               idr_preload_end();
-               ret = 0;
        }
 
+       args->name = (uint64_t) obj->name;
+       ret = 0;
+
 err:
+       spin_unlock(&dev->object_name_lock);
+       idr_preload_end();
        drm_gem_object_unreference_unlocked(obj);
        return ret;
 }
@@ -644,6 +636,59 @@ void drm_gem_vm_close(struct vm_area_struct *vma)
 }
 EXPORT_SYMBOL(drm_gem_vm_close);
 
+/**
+ * drm_gem_mmap_obj - memory map a GEM object
+ * @obj: the GEM object to map
+ * @obj_size: the object size to be mapped, in bytes
+ * @vma: VMA for the area to be mapped
+ *
+ * Set up the VMA to prepare mapping of the GEM object using the gem_vm_ops
+ * provided by the driver. Depending on their requirements, drivers can either
+ * provide a fault handler in their gem_vm_ops (in which case any accesses to
+ * the object will be trapped, to perform migration, GTT binding, surface
+ * register allocation, or performance monitoring), or mmap the buffer memory
+ * synchronously after calling drm_gem_mmap_obj.
+ *
+ * This function is mainly intended to implement the DMABUF mmap operation, when
+ * the GEM object is not looked up based on its fake offset. To implement the
+ * DRM mmap operation, drivers should use the drm_gem_mmap() function.
+ *
+ * NOTE: This function has to be protected with dev->struct_mutex
+ *
+ * Return 0 or success or -EINVAL if the object size is smaller than the VMA
+ * size, or if no gem_vm_ops are provided.
+ */
+int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size,
+                    struct vm_area_struct *vma)
+{
+       struct drm_device *dev = obj->dev;
+
+       lockdep_assert_held(&dev->struct_mutex);
+
+       /* Check for valid size. */
+       if (obj_size < vma->vm_end - vma->vm_start)
+               return -EINVAL;
+
+       if (!dev->driver->gem_vm_ops)
+               return -EINVAL;
+
+       vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
+       vma->vm_ops = dev->driver->gem_vm_ops;
+       vma->vm_private_data = obj;
+       vma->vm_page_prot =  pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+
+       /* Take a ref for this mapping of the object, so that the fault
+        * handler can dereference the mmap offset's pointer to the object.
+        * This reference is cleaned up by the corresponding vm_close
+        * (which should happen whether the vma was created by this call, or
+        * by a vm_open due to mremap or partial unmap or whatever).
+        */
+       drm_gem_object_reference(obj);
+
+       drm_vm_open_locked(dev, vma);
+       return 0;
+}
+EXPORT_SYMBOL(drm_gem_mmap_obj);
 
 /**
  * drm_gem_mmap - memory map routine for GEM objects
@@ -653,11 +698,9 @@ EXPORT_SYMBOL(drm_gem_vm_close);
  * If a driver supports GEM object mapping, mmap calls on the DRM file
  * descriptor will end up here.
  *
- * If we find the object based on the offset passed in (vma->vm_pgoff will
+ * Look up the GEM object based on the offset passed in (vma->vm_pgoff will
  * contain the fake offset we created when the GTT map ioctl was called on
- * the object), we set up the driver fault handler so that any accesses
- * to the object can be trapped, to perform migration, GTT binding, surface
- * register allocation, or performance monitoring.
+ * the object) and map it with a call to drm_gem_mmap_obj().
  */
 int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
 {
@@ -665,7 +708,6 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
        struct drm_device *dev = priv->minor->dev;
        struct drm_gem_mm *mm = dev->mm_private;
        struct drm_local_map *map = NULL;
-       struct drm_gem_object *obj;
        struct drm_hash_item *hash;
        int ret = 0;
 
@@ -686,32 +728,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
                goto out_unlock;
        }
 
-       /* Check for valid size. */
-       if (map->size < vma->vm_end - vma->vm_start) {
-               ret = -EINVAL;
-               goto out_unlock;
-       }
-
-       obj = map->handle;
-       if (!obj->dev->driver->gem_vm_ops) {
-               ret = -EINVAL;
-               goto out_unlock;
-       }
-
-       vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
-       vma->vm_ops = obj->dev->driver->gem_vm_ops;
-       vma->vm_private_data = map->handle;
-       vma->vm_page_prot =  pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
-
-       /* Take a ref for this mapping of the object, so that the fault
-        * handler can dereference the mmap offset's pointer to the object.
-        * This reference is cleaned up by the corresponding vm_close
-        * (which should happen whether the vma was created by this call, or
-        * by a vm_open due to mremap or partial unmap or whatever).
-        */
-       drm_gem_object_reference(obj);
-
-       drm_vm_open_locked(dev, vma);
+       ret = drm_gem_mmap_obj(map->handle, map->size, vma);
 
 out_unlock:
        mutex_unlock(&dev->struct_mutex);
index 0a7e011..ece72a8 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/slab.h>
 #include <linux/mutex.h>
 #include <linux/export.h>
+#include <linux/dma-buf.h>
 #include <linux/dma-mapping.h>
 
 #include <drm/drmP.h>
@@ -32,11 +33,44 @@ static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
        return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
 }
 
-static void drm_gem_cma_buf_destroy(struct drm_device *drm,
-               struct drm_gem_cma_object *cma_obj)
+/*
+ * __drm_gem_cma_create - Create a GEM CMA object without allocating memory
+ * @drm: The drm device
+ * @size: The GEM object size
+ *
+ * This function creates and initializes a GEM CMA object of the given size, but
+ * doesn't allocate any memory to back the object.
+ *
+ * Return a struct drm_gem_cma_object* on success or ERR_PTR values on failure.
+ */
+static struct drm_gem_cma_object *
+__drm_gem_cma_create(struct drm_device *drm, unsigned int size)
 {
-       dma_free_writecombine(drm->dev, cma_obj->base.size, cma_obj->vaddr,
-                       cma_obj->paddr);
+       struct drm_gem_cma_object *cma_obj;
+       struct drm_gem_object *gem_obj;
+       int ret;
+
+       cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
+       if (!cma_obj)
+               return ERR_PTR(-ENOMEM);
+
+       gem_obj = &cma_obj->base;
+
+       ret = drm_gem_object_init(drm, gem_obj, size);
+       if (ret)
+               goto error;
+
+       ret = drm_gem_create_mmap_offset(gem_obj);
+       if (ret) {
+               drm_gem_object_release(gem_obj);
+               goto error;
+       }
+
+       return cma_obj;
+
+error:
+       kfree(cma_obj);
+       return ERR_PTR(ret);
 }
 
 /*
@@ -49,44 +83,42 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm,
                unsigned int size)
 {
        struct drm_gem_cma_object *cma_obj;
-       struct drm_gem_object *gem_obj;
+       struct sg_table *sgt = NULL;
        int ret;
 
        size = round_up(size, PAGE_SIZE);
 
-       cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
-       if (!cma_obj)
-               return ERR_PTR(-ENOMEM);
+       cma_obj = __drm_gem_cma_create(drm, size);
+       if (IS_ERR(cma_obj))
+               return cma_obj;
 
        cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size,
                        &cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN);
        if (!cma_obj->vaddr) {
-               dev_err(drm->dev, "failed to allocate buffer with size %d\n", size);
+               dev_err(drm->dev, "failed to allocate buffer with size %d\n",
+                       size);
                ret = -ENOMEM;
-               goto err_dma_alloc;
+               goto error;
        }
 
-       gem_obj = &cma_obj->base;
+       sgt = kzalloc(sizeof(*cma_obj->sgt), GFP_KERNEL);
+       if (sgt == NULL) {
+               ret = -ENOMEM;
+               goto error;
+       }
 
-       ret = drm_gem_object_init(drm, gem_obj, size);
-       if (ret)
-               goto err_obj_init;
+       ret = dma_get_sgtable(drm->dev, sgt, cma_obj->vaddr,
+                             cma_obj->paddr, size);
+       if (ret < 0)
+               goto error;
 
-       ret = drm_gem_create_mmap_offset(gem_obj);
-       if (ret)
-               goto err_create_mmap_offset;
+       cma_obj->sgt = sgt;
 
        return cma_obj;
 
-err_create_mmap_offset:
-       drm_gem_object_release(gem_obj);
-
-err_obj_init:
-       drm_gem_cma_buf_destroy(drm, cma_obj);
-
-err_dma_alloc:
-       kfree(cma_obj);
-
+error:
+       kfree(sgt);
+       drm_gem_cma_free_object(&cma_obj->base);
        return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(drm_gem_cma_create);
@@ -143,11 +175,20 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
        if (gem_obj->map_list.map)
                drm_gem_free_mmap_offset(gem_obj);
 
-       drm_gem_object_release(gem_obj);
-
        cma_obj = to_drm_gem_cma_obj(gem_obj);
 
-       drm_gem_cma_buf_destroy(gem_obj->dev, cma_obj);
+       if (cma_obj->vaddr) {
+               dma_free_writecombine(gem_obj->dev->dev, cma_obj->base.size,
+                                     cma_obj->vaddr, cma_obj->paddr);
+               if (cma_obj->sgt) {
+                       sg_free_table(cma_obj->sgt);
+                       kfree(cma_obj->sgt);
+               }
+       } else if (gem_obj->import_attach) {
+               drm_prime_gem_destroy(gem_obj, cma_obj->sgt);
+       }
+
+       drm_gem_object_release(gem_obj);
 
        kfree(cma_obj);
 }
@@ -174,10 +215,7 @@ int drm_gem_cma_dumb_create(struct drm_file *file_priv,
 
        cma_obj = drm_gem_cma_create_with_handle(file_priv, dev,
                        args->size, &args->handle);
-       if (IS_ERR(cma_obj))
-               return PTR_ERR(cma_obj);
-
-       return 0;
+       return PTR_RET(cma_obj);
 }
 EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create);
 
@@ -215,13 +253,26 @@ const struct vm_operations_struct drm_gem_cma_vm_ops = {
 };
 EXPORT_SYMBOL_GPL(drm_gem_cma_vm_ops);
 
+static int drm_gem_cma_mmap_obj(struct drm_gem_cma_object *cma_obj,
+                               struct vm_area_struct *vma)
+{
+       int ret;
+
+       ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT,
+                       vma->vm_end - vma->vm_start, vma->vm_page_prot);
+       if (ret)
+               drm_gem_vm_close(vma);
+
+       return ret;
+}
+
 /*
  * drm_gem_cma_mmap - (struct file_operation)->mmap callback function
  */
 int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma)
 {
-       struct drm_gem_object *gem_obj;
        struct drm_gem_cma_object *cma_obj;
+       struct drm_gem_object *gem_obj;
        int ret;
 
        ret = drm_gem_mmap(filp, vma);
@@ -231,12 +282,7 @@ int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma)
        gem_obj = vma->vm_private_data;
        cma_obj = to_drm_gem_cma_obj(gem_obj);
 
-       ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT,
-                       vma->vm_end - vma->vm_start, vma->vm_page_prot);
-       if (ret)
-               drm_gem_vm_close(vma);
-
-       return ret;
+       return drm_gem_cma_mmap_obj(cma_obj, vma);
 }
 EXPORT_SYMBOL_GPL(drm_gem_cma_mmap);
 
@@ -270,3 +316,82 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m
 }
 EXPORT_SYMBOL_GPL(drm_gem_cma_describe);
 #endif
+
+/* low-level interface prime helpers */
+struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj)
+{
+       struct drm_gem_cma_object *cma_obj = to_drm_gem_cma_obj(obj);
+       struct sg_table *sgt;
+       int ret;
+
+       sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+       if (!sgt)
+               return NULL;
+
+       ret = dma_get_sgtable(obj->dev->dev, sgt, cma_obj->vaddr,
+                             cma_obj->paddr, obj->size);
+       if (ret < 0)
+               goto out;
+
+       return sgt;
+
+out:
+       kfree(sgt);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_prime_get_sg_table);
+
+struct drm_gem_object *
+drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size,
+                                 struct sg_table *sgt)
+{
+       struct drm_gem_cma_object *cma_obj;
+
+       if (sgt->nents != 1)
+               return ERR_PTR(-EINVAL);
+
+       /* Create a CMA GEM buffer. */
+       cma_obj = __drm_gem_cma_create(dev, size);
+       if (IS_ERR(cma_obj))
+               return ERR_PTR(PTR_ERR(cma_obj));
+
+       cma_obj->paddr = sg_dma_address(sgt->sgl);
+       cma_obj->sgt = sgt;
+
+       DRM_DEBUG_PRIME("dma_addr = 0x%x, size = %zu\n", cma_obj->paddr, size);
+
+       return &cma_obj->base;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_prime_import_sg_table);
+
+int drm_gem_cma_prime_mmap(struct drm_gem_object *obj,
+                          struct vm_area_struct *vma)
+{
+       struct drm_gem_cma_object *cma_obj;
+       struct drm_device *dev = obj->dev;
+       int ret;
+
+       mutex_lock(&dev->struct_mutex);
+       ret = drm_gem_mmap_obj(obj, obj->size, vma);
+       mutex_unlock(&dev->struct_mutex);
+       if (ret < 0)
+               return ret;
+
+       cma_obj = to_drm_gem_cma_obj(obj);
+       return drm_gem_cma_mmap_obj(cma_obj, vma);
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_prime_mmap);
+
+void *drm_gem_cma_prime_vmap(struct drm_gem_object *obj)
+{
+       struct drm_gem_cma_object *cma_obj = to_drm_gem_cma_obj(obj);
+
+       return cma_obj->vaddr;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_prime_vmap);
+
+void drm_gem_cma_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+       /* Nothing to do */
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_prime_vunmap);
index e77bd8b..ffd7a7b 100644 (file)
@@ -38,6 +38,9 @@
 
 #include <linux/pci.h>
 #include <linux/export.h>
+#ifdef CONFIG_X86
+#include <asm/mtrr.h>
+#endif
 
 /**
  * Get the bus id.
@@ -181,7 +184,17 @@ int drm_getmap(struct drm_device *dev, void *data,
        map->type = r_list->map->type;
        map->flags = r_list->map->flags;
        map->handle = (void *)(unsigned long) r_list->user_token;
-       map->mtrr = r_list->map->mtrr;
+
+#ifdef CONFIG_X86
+       /*
+        * There appears to be exactly one user of the mtrr index: dritest.
+        * It's easy enough to keep it working on non-PAT systems.
+        */
+       map->mtrr = phys_wc_to_mtrr_index(r_list->map->mtrr);
+#else
+       map->mtrr = -1;
+#endif
+
        mutex_unlock(&dev->struct_mutex);
 
        return 0;
index 07cf99c..543b9b3 100644 (file)
@@ -669,7 +669,7 @@ int drm_mm_clean(struct drm_mm * mm)
 }
 EXPORT_SYMBOL(drm_mm_clean);
 
-int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
+void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
 {
        INIT_LIST_HEAD(&mm->hole_stack);
        INIT_LIST_HEAD(&mm->unused_nodes);
@@ -690,8 +690,6 @@ int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
        list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack);
 
        mm->color_adjust = NULL;
-
-       return 0;
 }
 EXPORT_SYMBOL(drm_mm_init);
 
@@ -699,8 +697,8 @@ void drm_mm_takedown(struct drm_mm * mm)
 {
        struct drm_mm_node *entry, *next;
 
-       if (!list_empty(&mm->head_node.node_list)) {
-               DRM_ERROR("Memory manager not clean. Delaying takedown\n");
+       if (WARN(!list_empty(&mm->head_node.node_list),
+                "Memory manager not clean. Delaying takedown\n")) {
                return;
        }
 
@@ -716,36 +714,37 @@ void drm_mm_takedown(struct drm_mm * mm)
 }
 EXPORT_SYMBOL(drm_mm_takedown);
 
-void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
+static unsigned long drm_mm_debug_hole(struct drm_mm_node *entry,
+                                      const char *prefix)
 {
-       struct drm_mm_node *entry;
-       unsigned long total_used = 0, total_free = 0, total = 0;
        unsigned long hole_start, hole_end, hole_size;
 
-       hole_start = drm_mm_hole_node_start(&mm->head_node);
-       hole_end = drm_mm_hole_node_end(&mm->head_node);
-       hole_size = hole_end - hole_start;
-       if (hole_size)
+       if (entry->hole_follows) {
+               hole_start = drm_mm_hole_node_start(entry);
+               hole_end = drm_mm_hole_node_end(entry);
+               hole_size = hole_end - hole_start;
                printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
                        prefix, hole_start, hole_end,
                        hole_size);
-       total_free += hole_size;
+               return hole_size;
+       }
+
+       return 0;
+}
+
+void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
+{
+       struct drm_mm_node *entry;
+       unsigned long total_used = 0, total_free = 0, total = 0;
+
+       total_free += drm_mm_debug_hole(&mm->head_node, prefix);
 
        drm_mm_for_each_node(entry, mm) {
                printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: used\n",
                        prefix, entry->start, entry->start + entry->size,
                        entry->size);
                total_used += entry->size;
-
-               if (entry->hole_follows) {
-                       hole_start = drm_mm_hole_node_start(entry);
-                       hole_end = drm_mm_hole_node_end(entry);
-                       hole_size = hole_end - hole_start;
-                       printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
-                               prefix, hole_start, hole_end,
-                               hole_size);
-                       total_free += hole_size;
-               }
+               total_free += drm_mm_debug_hole(entry, prefix);
        }
        total = total_free + total_used;
 
index a371ff8..a6729bf 100644 (file)
@@ -535,6 +535,8 @@ int drm_display_mode_from_videomode(const struct videomode *vm,
                dmode->flags |= DRM_MODE_FLAG_INTERLACE;
        if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN)
                dmode->flags |= DRM_MODE_FLAG_DBLSCAN;
+       if (vm->flags & DISPLAY_FLAGS_DOUBLECLK)
+               dmode->flags |= DRM_MODE_FLAG_DBLCLK;
        drm_mode_set_name(dmode);
 
        return 0;
@@ -787,16 +789,17 @@ EXPORT_SYMBOL(drm_mode_set_crtcinfo);
  * LOCKING:
  * None.
  *
- * Copy an existing mode into another mode, preserving the object id
- * of the destination mode.
+ * Copy an existing mode into another mode, preserving the object id and
+ * list head of the destination mode.
  */
 void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src)
 {
        int id = dst->base.id;
+       struct list_head head = dst->head;
 
        *dst = *src;
        dst->base.id = id;
-       INIT_LIST_HEAD(&dst->head);
+       dst->head = head;
 }
 EXPORT_SYMBOL(drm_mode_copy);
 
@@ -1017,6 +1020,11 @@ static int drm_mode_compare(void *priv, struct list_head *lh_a, struct list_head
        diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay;
        if (diff)
                return diff;
+
+       diff = b->vrefresh - a->vrefresh;
+       if (diff)
+               return diff;
+
        diff = b->clock - a->clock;
        return diff;
 }
index 14194b6..80c0b2b 100644 (file)
@@ -278,10 +278,10 @@ static int drm_pci_agp_init(struct drm_device *dev)
                }
                if (drm_core_has_MTRR(dev)) {
                        if (dev->agp)
-                               dev->agp->agp_mtrr =
-                                       mtrr_add(dev->agp->agp_info.aper_base,
-                                                dev->agp->agp_info.aper_size *
-                                                1024 * 1024, MTRR_TYPE_WRCOMB, 1);
+                               dev->agp->agp_mtrr = arch_phys_wc_add(
+                                       dev->agp->agp_info.aper_base,
+                                       dev->agp->agp_info.aper_size *
+                                       1024 * 1024);
                }
        }
        return 0;
index 5b7b911..85e450e 100644 (file)
@@ -62,20 +62,125 @@ struct drm_prime_member {
        struct dma_buf *dma_buf;
        uint32_t handle;
 };
-static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle);
+
+struct drm_prime_attachment {
+       struct sg_table *sgt;
+       enum dma_data_direction dir;
+};
+
+static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle)
+{
+       struct drm_prime_member *member;
+
+       member = kmalloc(sizeof(*member), GFP_KERNEL);
+       if (!member)
+               return -ENOMEM;
+
+       get_dma_buf(dma_buf);
+       member->dma_buf = dma_buf;
+       member->handle = handle;
+       list_add(&member->entry, &prime_fpriv->head);
+       return 0;
+}
+
+static int drm_gem_map_attach(struct dma_buf *dma_buf,
+                             struct device *target_dev,
+                             struct dma_buf_attachment *attach)
+{
+       struct drm_prime_attachment *prime_attach;
+       struct drm_gem_object *obj = dma_buf->priv;
+       struct drm_device *dev = obj->dev;
+
+       prime_attach = kzalloc(sizeof(*prime_attach), GFP_KERNEL);
+       if (!prime_attach)
+               return -ENOMEM;
+
+       prime_attach->dir = DMA_NONE;
+       attach->priv = prime_attach;
+
+       if (!dev->driver->gem_prime_pin)
+               return 0;
+
+       return dev->driver->gem_prime_pin(obj);
+}
+
+static void drm_gem_map_detach(struct dma_buf *dma_buf,
+                              struct dma_buf_attachment *attach)
+{
+       struct drm_prime_attachment *prime_attach = attach->priv;
+       struct drm_gem_object *obj = dma_buf->priv;
+       struct drm_device *dev = obj->dev;
+       struct sg_table *sgt;
+
+       if (dev->driver->gem_prime_unpin)
+               dev->driver->gem_prime_unpin(obj);
+
+       if (!prime_attach)
+               return;
+
+       sgt = prime_attach->sgt;
+       if (sgt) {
+               if (prime_attach->dir != DMA_NONE)
+                       dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents,
+                                       prime_attach->dir);
+               sg_free_table(sgt);
+       }
+
+       kfree(sgt);
+       kfree(prime_attach);
+       attach->priv = NULL;
+}
+
+static void drm_prime_remove_buf_handle_locked(
+               struct drm_prime_file_private *prime_fpriv,
+               struct dma_buf *dma_buf)
+{
+       struct drm_prime_member *member, *safe;
+
+       list_for_each_entry_safe(member, safe, &prime_fpriv->head, entry) {
+               if (member->dma_buf == dma_buf) {
+                       dma_buf_put(dma_buf);
+                       list_del(&member->entry);
+                       kfree(member);
+               }
+       }
+}
 
 static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach,
                enum dma_data_direction dir)
 {
+       struct drm_prime_attachment *prime_attach = attach->priv;
        struct drm_gem_object *obj = attach->dmabuf->priv;
        struct sg_table *sgt;
 
+       if (WARN_ON(dir == DMA_NONE || !prime_attach))
+               return ERR_PTR(-EINVAL);
+
+       /* return the cached mapping when possible */
+       if (prime_attach->dir == dir)
+               return prime_attach->sgt;
+
+       /*
+        * two mappings with different directions for the same attachment are
+        * not allowed
+        */
+       if (WARN_ON(prime_attach->dir != DMA_NONE))
+               return ERR_PTR(-EBUSY);
+
        mutex_lock(&obj->dev->struct_mutex);
 
        sgt = obj->dev->driver->gem_prime_get_sg_table(obj);
 
-       if (!IS_ERR_OR_NULL(sgt))
-               dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir);
+       if (!IS_ERR(sgt)) {
+               if (!dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir)) {
+                       sg_free_table(sgt);
+                       kfree(sgt);
+                       sgt = ERR_PTR(-ENOMEM);
+               } else {
+                       prime_attach->sgt = sgt;
+                       prime_attach->dir = dir;
+               }
+       }
 
        mutex_unlock(&obj->dev->struct_mutex);
        return sgt;
@@ -84,9 +189,7 @@ static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach,
 static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
                struct sg_table *sgt, enum dma_data_direction dir)
 {
-       dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
-       sg_free_table(sgt);
-       kfree(sgt);
+       /* nothing to be done here */
 }
 
 static void drm_gem_dmabuf_release(struct dma_buf *dma_buf)
@@ -142,10 +245,18 @@ static void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf,
 static int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf,
                struct vm_area_struct *vma)
 {
-       return -EINVAL;
+       struct drm_gem_object *obj = dma_buf->priv;
+       struct drm_device *dev = obj->dev;
+
+       if (!dev->driver->gem_prime_mmap)
+               return -ENOSYS;
+
+       return dev->driver->gem_prime_mmap(obj, vma);
 }
 
 static const struct dma_buf_ops drm_gem_prime_dmabuf_ops =  {
+       .attach = drm_gem_map_attach,
+       .detach = drm_gem_map_detach,
        .map_dma_buf = drm_gem_map_dma_buf,
        .unmap_dma_buf = drm_gem_unmap_dma_buf,
        .release = drm_gem_dmabuf_release,
@@ -185,11 +296,6 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops =  {
 struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
                                     struct drm_gem_object *obj, int flags)
 {
-       if (dev->driver->gem_prime_pin) {
-               int ret = dev->driver->gem_prime_pin(obj);
-               if (ret)
-                       return ERR_PTR(ret);
-       }
        return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, flags);
 }
 EXPORT_SYMBOL(drm_gem_prime_export);
@@ -235,15 +341,34 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev,
        ret = drm_prime_add_buf_handle(&file_priv->prime,
                                       obj->export_dma_buf, handle);
        if (ret)
-               goto out;
+               goto fail_put_dmabuf;
+
+       ret = dma_buf_fd(buf, flags);
+       if (ret < 0)
+               goto fail_rm_handle;
 
-       *prime_fd = dma_buf_fd(buf, flags);
+       *prime_fd = ret;
        mutex_unlock(&file_priv->prime.lock);
        return 0;
 
 out_have_obj:
        get_dma_buf(dmabuf);
-       *prime_fd = dma_buf_fd(dmabuf, flags);
+       ret = dma_buf_fd(dmabuf, flags);
+       if (ret < 0) {
+               dma_buf_put(dmabuf);
+       } else {
+               *prime_fd = ret;
+               ret = 0;
+       }
+
+       goto out;
+
+fail_rm_handle:
+       drm_prime_remove_buf_handle_locked(&file_priv->prime, buf);
+fail_put_dmabuf:
+       /* clear NOT to be checked when releasing dma_buf */
+       obj->export_dma_buf = NULL;
+       dma_buf_put(buf);
 out:
        drm_gem_object_unreference_unlocked(obj);
        mutex_unlock(&file_priv->prime.lock);
@@ -276,7 +401,7 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
 
        attach = dma_buf_attach(dma_buf, dev->dev);
        if (IS_ERR(attach))
-               return ERR_PTR(PTR_ERR(attach));
+               return ERR_CAST(attach);
 
        get_dma_buf(dma_buf);
 
@@ -412,8 +537,10 @@ struct sg_table *drm_prime_pages_to_sg(struct page **pages, int nr_pages)
        int ret;
 
        sg = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
-       if (!sg)
+       if (!sg) {
+               ret = -ENOMEM;
                goto out;
+       }
 
        ret = sg_alloc_table_from_pages(sg, pages, nr_pages, 0,
                                nr_pages << PAGE_SHIFT, GFP_KERNEL);
@@ -423,7 +550,7 @@ struct sg_table *drm_prime_pages_to_sg(struct page **pages, int nr_pages)
        return sg;
 out:
        kfree(sg);
-       return NULL;
+       return ERR_PTR(ret);
 }
 EXPORT_SYMBOL(drm_prime_pages_to_sg);
 
@@ -492,21 +619,6 @@ void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv)
 }
 EXPORT_SYMBOL(drm_prime_destroy_file_private);
 
-static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle)
-{
-       struct drm_prime_member *member;
-
-       member = kmalloc(sizeof(*member), GFP_KERNEL);
-       if (!member)
-               return -ENOMEM;
-
-       get_dma_buf(dma_buf);
-       member->dma_buf = dma_buf;
-       member->handle = handle;
-       list_add(&member->entry, &prime_fpriv->head);
-       return 0;
-}
-
 int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle)
 {
        struct drm_prime_member *member;
@@ -523,16 +635,8 @@ EXPORT_SYMBOL(drm_prime_lookup_buf_handle);
 
 void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf)
 {
-       struct drm_prime_member *member, *safe;
-
        mutex_lock(&prime_fpriv->lock);
-       list_for_each_entry_safe(member, safe, &prime_fpriv->head, entry) {
-               if (member->dma_buf == dma_buf) {
-                       dma_buf_put(dma_buf);
-                       list_del(&member->entry);
-                       kfree(member);
-               }
-       }
+       drm_prime_remove_buf_handle_locked(prime_fpriv, dma_buf);
        mutex_unlock(&prime_fpriv->lock);
 }
 EXPORT_SYMBOL(drm_prime_remove_buf_handle);
diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c
new file mode 100644 (file)
index 0000000..7047ca0
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2011-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.
+ */
+
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <drm/drmP.h>
+#include <drm/drm_rect.h>
+
+/**
+ * drm_rect_intersect - intersect two rectangles
+ * @r1: first rectangle
+ * @r2: second rectangle
+ *
+ * Calculate the intersection of rectangles @r1 and @r2.
+ * @r1 will be overwritten with the intersection.
+ *
+ * RETURNS:
+ * %true if rectangle @r1 is still visible after the operation,
+ * %false otherwise.
+ */
+bool drm_rect_intersect(struct drm_rect *r1, const struct drm_rect *r2)
+{
+       r1->x1 = max(r1->x1, r2->x1);
+       r1->y1 = max(r1->y1, r2->y1);
+       r1->x2 = min(r1->x2, r2->x2);
+       r1->y2 = min(r1->y2, r2->y2);
+
+       return drm_rect_visible(r1);
+}
+EXPORT_SYMBOL(drm_rect_intersect);
+
+/**
+ * drm_rect_clip_scaled - perform a scaled clip operation
+ * @src: source window rectangle
+ * @dst: destination window rectangle
+ * @clip: clip rectangle
+ * @hscale: horizontal scaling factor
+ * @vscale: vertical scaling factor
+ *
+ * Clip rectangle @dst by rectangle @clip. Clip rectangle @src by the
+ * same amounts multiplied by @hscale and @vscale.
+ *
+ * RETURNS:
+ * %true if rectangle @dst is still visible after being clipped,
+ * %false otherwise
+ */
+bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst,
+                         const struct drm_rect *clip,
+                         int hscale, int vscale)
+{
+       int diff;
+
+       diff = clip->x1 - dst->x1;
+       if (diff > 0) {
+               int64_t tmp = src->x1 + (int64_t) diff * hscale;
+               src->x1 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX);
+       }
+       diff = clip->y1 - dst->y1;
+       if (diff > 0) {
+               int64_t tmp = src->y1 + (int64_t) diff * vscale;
+               src->y1 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX);
+       }
+       diff = dst->x2 - clip->x2;
+       if (diff > 0) {
+               int64_t tmp = src->x2 - (int64_t) diff * hscale;
+               src->x2 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX);
+       }
+       diff = dst->y2 - clip->y2;
+       if (diff > 0) {
+               int64_t tmp = src->y2 - (int64_t) diff * vscale;
+               src->y2 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX);
+       }
+
+       return drm_rect_intersect(dst, clip);
+}
+EXPORT_SYMBOL(drm_rect_clip_scaled);
+
+static int drm_calc_scale(int src, int dst)
+{
+       int scale = 0;
+
+       if (src < 0 || dst < 0)
+               return -EINVAL;
+
+       if (dst == 0)
+               return 0;
+
+       scale = src / dst;
+
+       return scale;
+}
+
+/**
+ * drm_rect_calc_hscale - calculate the horizontal scaling factor
+ * @src: source window rectangle
+ * @dst: destination window rectangle
+ * @min_hscale: minimum allowed horizontal scaling factor
+ * @max_hscale: maximum allowed horizontal scaling factor
+ *
+ * Calculate the horizontal scaling factor as
+ * (@src width) / (@dst width).
+ *
+ * RETURNS:
+ * The horizontal scaling factor, or errno of out of limits.
+ */
+int drm_rect_calc_hscale(const struct drm_rect *src,
+                        const struct drm_rect *dst,
+                        int min_hscale, int max_hscale)
+{
+       int src_w = drm_rect_width(src);
+       int dst_w = drm_rect_width(dst);
+       int hscale = drm_calc_scale(src_w, dst_w);
+
+       if (hscale < 0 || dst_w == 0)
+               return hscale;
+
+       if (hscale < min_hscale || hscale > max_hscale)
+               return -ERANGE;
+
+       return hscale;
+}
+EXPORT_SYMBOL(drm_rect_calc_hscale);
+
+/**
+ * drm_rect_calc_vscale - calculate the vertical scaling factor
+ * @src: source window rectangle
+ * @dst: destination window rectangle
+ * @min_vscale: minimum allowed vertical scaling factor
+ * @max_vscale: maximum allowed vertical scaling factor
+ *
+ * Calculate the vertical scaling factor as
+ * (@src height) / (@dst height).
+ *
+ * RETURNS:
+ * The vertical scaling factor, or errno of out of limits.
+ */
+int drm_rect_calc_vscale(const struct drm_rect *src,
+                        const struct drm_rect *dst,
+                        int min_vscale, int max_vscale)
+{
+       int src_h = drm_rect_height(src);
+       int dst_h = drm_rect_height(dst);
+       int vscale = drm_calc_scale(src_h, dst_h);
+
+       if (vscale < 0 || dst_h == 0)
+               return vscale;
+
+       if (vscale < min_vscale || vscale > max_vscale)
+               return -ERANGE;
+
+       return vscale;
+}
+EXPORT_SYMBOL(drm_rect_calc_vscale);
+
+/**
+ * drm_calc_hscale_relaxed - calculate the horizontal scaling factor
+ * @src: source window rectangle
+ * @dst: destination window rectangle
+ * @min_hscale: minimum allowed horizontal scaling factor
+ * @max_hscale: maximum allowed horizontal scaling factor
+ *
+ * Calculate the horizontal scaling factor as
+ * (@src width) / (@dst width).
+ *
+ * If the calculated scaling factor is below @min_vscale,
+ * decrease the height of rectangle @dst to compensate.
+ *
+ * If the calculated scaling factor is above @max_vscale,
+ * decrease the height of rectangle @src to compensate.
+ *
+ * RETURNS:
+ * The horizontal scaling factor.
+ */
+int drm_rect_calc_hscale_relaxed(struct drm_rect *src,
+                                struct drm_rect *dst,
+                                int min_hscale, int max_hscale)
+{
+       int src_w = drm_rect_width(src);
+       int dst_w = drm_rect_width(dst);
+       int hscale = drm_calc_scale(src_w, dst_w);
+
+       if (hscale < 0 || dst_w == 0)
+               return hscale;
+
+       if (hscale < min_hscale) {
+               int max_dst_w = src_w / min_hscale;
+
+               drm_rect_adjust_size(dst, max_dst_w - dst_w, 0);
+
+               return min_hscale;
+       }
+
+       if (hscale > max_hscale) {
+               int max_src_w = dst_w * max_hscale;
+
+               drm_rect_adjust_size(src, max_src_w - src_w, 0);
+
+               return max_hscale;
+       }
+
+       return hscale;
+}
+EXPORT_SYMBOL(drm_rect_calc_hscale_relaxed);
+
+/**
+ * drm_rect_calc_vscale_relaxed - calculate the vertical scaling factor
+ * @src: source window rectangle
+ * @dst: destination window rectangle
+ * @min_vscale: minimum allowed vertical scaling factor
+ * @max_vscale: maximum allowed vertical scaling factor
+ *
+ * Calculate the vertical scaling factor as
+ * (@src height) / (@dst height).
+ *
+ * If the calculated scaling factor is below @min_vscale,
+ * decrease the height of rectangle @dst to compensate.
+ *
+ * If the calculated scaling factor is above @max_vscale,
+ * decrease the height of rectangle @src to compensate.
+ *
+ * RETURNS:
+ * The vertical scaling factor.
+ */
+int drm_rect_calc_vscale_relaxed(struct drm_rect *src,
+                                struct drm_rect *dst,
+                                int min_vscale, int max_vscale)
+{
+       int src_h = drm_rect_height(src);
+       int dst_h = drm_rect_height(dst);
+       int vscale = drm_calc_scale(src_h, dst_h);
+
+       if (vscale < 0 || dst_h == 0)
+               return vscale;
+
+       if (vscale < min_vscale) {
+               int max_dst_h = src_h / min_vscale;
+
+               drm_rect_adjust_size(dst, 0, max_dst_h - dst_h);
+
+               return min_vscale;
+       }
+
+       if (vscale > max_vscale) {
+               int max_src_h = dst_h * max_vscale;
+
+               drm_rect_adjust_size(src, 0, max_src_h - src_h);
+
+               return max_vscale;
+       }
+
+       return vscale;
+}
+EXPORT_SYMBOL(drm_rect_calc_vscale_relaxed);
+
+/**
+ * drm_rect_debug_print - print the rectangle information
+ * @r: rectangle to print
+ * @fixed_point: rectangle is in 16.16 fixed point format
+ */
+void drm_rect_debug_print(const struct drm_rect *r, bool fixed_point)
+{
+       int w = drm_rect_width(r);
+       int h = drm_rect_height(r);
+
+       if (fixed_point)
+               DRM_DEBUG_KMS("%d.%06ux%d.%06u%+d.%06u%+d.%06u\n",
+                             w >> 16, ((w & 0xffff) * 15625) >> 10,
+                             h >> 16, ((h & 0xffff) * 15625) >> 10,
+                             r->x1 >> 16, ((r->x1 & 0xffff) * 15625) >> 10,
+                             r->y1 >> 16, ((r->y1 & 0xffff) * 15625) >> 10);
+       else
+               DRM_DEBUG_KMS("%dx%d%+d%+d\n", w, h, r->x1, r->y1);
+}
+EXPORT_SYMBOL(drm_rect_debug_print);
index 16f3ec5..327ca19 100644 (file)
@@ -203,7 +203,7 @@ EXPORT_SYMBOL(drm_master_put);
 int drm_setmaster_ioctl(struct drm_device *dev, void *data,
                        struct drm_file *file_priv)
 {
-       int ret;
+       int ret = 0;
 
        if (file_priv->is_master)
                return 0;
@@ -229,7 +229,7 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
        }
        mutex_unlock(&dev->struct_mutex);
 
-       return 0;
+       return ret;
 }
 
 int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
@@ -451,14 +451,8 @@ void drm_put_dev(struct drm_device *dev)
 
        drm_lastclose(dev);
 
-       if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) &&
-           dev->agp && dev->agp->agp_mtrr >= 0) {
-               int retval;
-               retval = mtrr_del(dev->agp->agp_mtrr,
-                                 dev->agp->agp_info.aper_base,
-                                 dev->agp->agp_info.aper_size * 1024 * 1024);
-               DRM_DEBUG("mtrr_del=%d\n", retval);
-       }
+       if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && dev->agp)
+               arch_phys_wc_del(dev->agp->agp_mtrr);
 
        if (dev->driver->unload)
                dev->driver->unload(dev);
index 0229665..2290b3b 100644 (file)
@@ -30,14 +30,14 @@ static struct device_type drm_sysfs_device_minor = {
 };
 
 /**
- * drm_class_suspend - DRM class suspend hook
+ * __drm_class_suspend - internal DRM class suspend routine
  * @dev: Linux device to suspend
  * @state: power state to enter
  *
  * Just figures out what the actual struct drm_device associated with
  * @dev is and calls its suspend hook, if present.
  */
-static int drm_class_suspend(struct device *dev, pm_message_t state)
+static int __drm_class_suspend(struct device *dev, pm_message_t state)
 {
        if (dev->type == &drm_sysfs_device_minor) {
                struct drm_minor *drm_minor = to_drm_minor(dev);
@@ -51,6 +51,26 @@ static int drm_class_suspend(struct device *dev, pm_message_t state)
        return 0;
 }
 
+/**
+ * drm_class_suspend - internal DRM class suspend hook. Simply calls
+ * __drm_class_suspend() with the correct pm state.
+ * @dev: Linux device to suspend
+ */
+static int drm_class_suspend(struct device *dev)
+{
+       return __drm_class_suspend(dev, PMSG_SUSPEND);
+}
+
+/**
+ * drm_class_freeze - internal DRM class freeze hook. Simply calls
+ * __drm_class_suspend() with the correct pm state.
+ * @dev: Linux device to freeze
+ */
+static int drm_class_freeze(struct device *dev)
+{
+       return __drm_class_suspend(dev, PMSG_FREEZE);
+}
+
 /**
  * drm_class_resume - DRM class resume hook
  * @dev: Linux device to resume
@@ -72,6 +92,12 @@ static int drm_class_resume(struct device *dev)
        return 0;
 }
 
+static const struct dev_pm_ops drm_class_dev_pm_ops = {
+       .suspend        = drm_class_suspend,
+       .resume         = drm_class_resume,
+       .freeze         = drm_class_freeze,
+};
+
 static char *drm_devnode(struct device *dev, umode_t *mode)
 {
        return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
@@ -106,8 +132,7 @@ struct class *drm_sysfs_create(struct module *owner, char *name)
                goto err_out;
        }
 
-       class->suspend = drm_class_suspend;
-       class->resume = drm_class_resume;
+       class->pm = &drm_class_dev_pm_ops;
 
        err = class_create_file(class, &class_attr_version.attr);
        if (err)
index 03ea964..27cc95f 100644 (file)
@@ -21,7 +21,7 @@ TRACE_EVENT(drm_vblank_event,
                    __entry->crtc = crtc;
                    __entry->seq = seq;
                    ),
-           TP_printk("crtc=%d, seq=%d", __entry->crtc, __entry->seq)
+           TP_printk("crtc=%d, seq=%u", __entry->crtc, __entry->seq)
 );
 
 TRACE_EVENT(drm_vblank_event_queued,
@@ -37,7 +37,7 @@ TRACE_EVENT(drm_vblank_event_queued,
                    __entry->crtc = crtc;
                    __entry->seq = seq;
                    ),
-           TP_printk("pid=%d, crtc=%d, seq=%d", __entry->pid, __entry->crtc, \
+           TP_printk("pid=%d, crtc=%d, seq=%u", __entry->pid, __entry->crtc, \
                      __entry->seq)
 );
 
@@ -54,7 +54,7 @@ TRACE_EVENT(drm_vblank_event_delivered,
                    __entry->crtc = crtc;
                    __entry->seq = seq;
                    ),
-           TP_printk("pid=%d, crtc=%d, seq=%d", __entry->pid, __entry->crtc, \
+           TP_printk("pid=%d, crtc=%d, seq=%u", __entry->pid, __entry->crtc, \
                      __entry->seq)
 );
 
index 67969e2..feb2003 100644 (file)
 static void drm_vm_open(struct vm_area_struct *vma);
 static void drm_vm_close(struct vm_area_struct *vma);
 
-static pgprot_t drm_io_prot(uint32_t map_type, struct vm_area_struct *vma)
+static pgprot_t drm_io_prot(struct drm_local_map *map,
+                           struct vm_area_struct *vma)
 {
        pgprot_t tmp = vm_get_page_prot(vma->vm_flags);
 
 #if defined(__i386__) || defined(__x86_64__)
-       if (boot_cpu_data.x86 > 3 && map_type != _DRM_AGP) {
-               pgprot_val(tmp) |= _PAGE_PCD;
-               pgprot_val(tmp) &= ~_PAGE_PWT;
-       }
+       if (map->type == _DRM_REGISTERS && !(map->flags & _DRM_WRITE_COMBINING))
+               tmp = pgprot_noncached(tmp);
+       else
+               tmp = pgprot_writecombine(tmp);
 #elif defined(__powerpc__)
        pgprot_val(tmp) |= _PAGE_NO_CACHE;
-       if (map_type == _DRM_REGISTERS)
+       if (map->type == _DRM_REGISTERS)
                pgprot_val(tmp) |= _PAGE_GUARDED;
 #elif defined(__ia64__)
        if (efi_range_is_wc(vma->vm_start, vma->vm_end -
@@ -250,13 +251,8 @@ static void drm_vm_shm_close(struct vm_area_struct *vma)
                        switch (map->type) {
                        case _DRM_REGISTERS:
                        case _DRM_FRAME_BUFFER:
-                               if (drm_core_has_MTRR(dev) && map->mtrr >= 0) {
-                                       int retcode;
-                                       retcode = mtrr_del(map->mtrr,
-                                                          map->offset,
-                                                          map->size);
-                                       DRM_DEBUG("mtrr_del = %d\n", retcode);
-                               }
+                               if (drm_core_has_MTRR(dev))
+                                       arch_phys_wc_del(map->mtrr);
                                iounmap(map->handle);
                                break;
                        case _DRM_SHM:
@@ -617,7 +613,7 @@ int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
        case _DRM_FRAME_BUFFER:
        case _DRM_REGISTERS:
                offset = drm_core_get_reg_ofs(dev);
-               vma->vm_page_prot = drm_io_prot(map->type, vma);
+               vma->vm_page_prot = drm_io_prot(map, vma);
                if (io_remap_pfn_range(vma, vma->vm_start,
                                       (map->offset + offset) >> PAGE_SHIFT,
                                       vma->vm_end - vma->vm_start,
index 4e9b5ba..95c75ed 100644 (file)
@@ -52,6 +52,8 @@ static struct i2c_device_id ddc_idtable[] = {
 static struct of_device_id hdmiddc_match_types[] = {
        {
                .compatible = "samsung,exynos5-hdmiddc",
+       }, {
+               .compatible = "samsung,exynos4210-hdmiddc",
        }, {
                /* end node */
        }
index 57affae..b8ac06d 100644 (file)
@@ -24,8 +24,6 @@ static int lowlevel_buffer_allocate(struct drm_device *dev,
        enum dma_attr attr;
        unsigned int nr_pages;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (buf->dma_addr) {
                DRM_DEBUG_KMS("already allocated.\n");
                return 0;
@@ -59,8 +57,7 @@ static int lowlevel_buffer_allocate(struct drm_device *dev,
                dma_addr_t start_addr;
                unsigned int i = 0;
 
-               buf->pages = kzalloc(sizeof(struct page) * nr_pages,
-                                       GFP_KERNEL);
+               buf->pages = drm_calloc_large(nr_pages, sizeof(struct page *));
                if (!buf->pages) {
                        DRM_ERROR("failed to allocate pages.\n");
                        return -ENOMEM;
@@ -71,8 +68,8 @@ static int lowlevel_buffer_allocate(struct drm_device *dev,
                                        &buf->dma_attrs);
                if (!buf->kvaddr) {
                        DRM_ERROR("failed to allocate buffer.\n");
-                       kfree(buf->pages);
-                       return -ENOMEM;
+                       ret = -ENOMEM;
+                       goto err_free;
                }
 
                start_addr = buf->dma_addr;
@@ -109,9 +106,9 @@ err_free_attrs:
        dma_free_attrs(dev->dev, buf->size, buf->pages,
                        (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
        buf->dma_addr = (dma_addr_t)NULL;
-
+err_free:
        if (!is_drm_iommu_supported(dev))
-               kfree(buf->pages);
+               drm_free_large(buf->pages);
 
        return ret;
 }
@@ -119,8 +116,6 @@ err_free_attrs:
 static void lowlevel_buffer_deallocate(struct drm_device *dev,
                unsigned int flags, struct exynos_drm_gem_buf *buf)
 {
-       DRM_DEBUG_KMS("%s.\n", __FILE__);
-
        if (!buf->dma_addr) {
                DRM_DEBUG_KMS("dma_addr is invalid.\n");
                return;
@@ -138,7 +133,7 @@ static void lowlevel_buffer_deallocate(struct drm_device *dev,
        if (!is_drm_iommu_supported(dev)) {
                dma_free_attrs(dev->dev, buf->size, buf->kvaddr,
                                (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
-               kfree(buf->pages);
+               drm_free_large(buf->pages);
        } else
                dma_free_attrs(dev->dev, buf->size, buf->pages,
                                (dma_addr_t)buf->dma_addr, &buf->dma_attrs);
@@ -151,7 +146,6 @@ struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
 {
        struct exynos_drm_gem_buf *buffer;
 
-       DRM_DEBUG_KMS("%s.\n", __FILE__);
        DRM_DEBUG_KMS("desired size = 0x%x\n", size);
 
        buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
@@ -167,8 +161,6 @@ struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
 void exynos_drm_fini_buf(struct drm_device *dev,
                                struct exynos_drm_gem_buf *buffer)
 {
-       DRM_DEBUG_KMS("%s.\n", __FILE__);
-
        if (!buffer) {
                DRM_DEBUG_KMS("buffer is null.\n");
                return;
index 8bcc13a..02a8bc5 100644 (file)
@@ -34,7 +34,6 @@ convert_to_display_mode(struct drm_display_mode *mode,
                        struct exynos_drm_panel_info *panel)
 {
        struct fb_videomode *timing = &panel->timing;
-       DRM_DEBUG_KMS("%s\n", __FILE__);
 
        mode->clock = timing->pixclock / 1000;
        mode->vrefresh = timing->refresh;
@@ -58,37 +57,6 @@ convert_to_display_mode(struct drm_display_mode *mode,
                mode->flags |= DRM_MODE_FLAG_DBLSCAN;
 }
 
-/* convert drm_display_mode to exynos_video_timings */
-static inline void
-convert_to_video_timing(struct fb_videomode *timing,
-                       struct drm_display_mode *mode)
-{
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
-       memset(timing, 0, sizeof(*timing));
-
-       timing->pixclock = mode->clock * 1000;
-       timing->refresh = drm_mode_vrefresh(mode);
-
-       timing->xres = mode->hdisplay;
-       timing->right_margin = mode->hsync_start - mode->hdisplay;
-       timing->hsync_len = mode->hsync_end - mode->hsync_start;
-       timing->left_margin = mode->htotal - mode->hsync_end;
-
-       timing->yres = mode->vdisplay;
-       timing->lower_margin = mode->vsync_start - mode->vdisplay;
-       timing->vsync_len = mode->vsync_end - mode->vsync_start;
-       timing->upper_margin = mode->vtotal - mode->vsync_end;
-
-       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
-               timing->vmode = FB_VMODE_INTERLACED;
-       else
-               timing->vmode = FB_VMODE_NONINTERLACED;
-
-       if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
-               timing->vmode |= FB_VMODE_DOUBLE;
-}
-
 static int exynos_drm_connector_get_modes(struct drm_connector *connector)
 {
        struct exynos_drm_connector *exynos_connector =
@@ -99,8 +67,6 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
        unsigned int count = 0;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (!display_ops) {
                DRM_DEBUG_KMS("display_ops is null.\n");
                return 0;
@@ -168,15 +134,12 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
                                        to_exynos_connector(connector);
        struct exynos_drm_manager *manager = exynos_connector->manager;
        struct exynos_drm_display_ops *display_ops = manager->display_ops;
-       struct fb_videomode timing;
        int ret = MODE_BAD;
 
        DRM_DEBUG_KMS("%s\n", __FILE__);
 
-       convert_to_video_timing(&timing, mode);
-
-       if (display_ops && display_ops->check_timing)
-               if (!display_ops->check_timing(manager->dev, (void *)&timing))
+       if (display_ops && display_ops->check_mode)
+               if (!display_ops->check_mode(manager->dev, mode))
                        ret = MODE_OK;
 
        return ret;
@@ -190,8 +153,6 @@ struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
        struct drm_mode_object *obj;
        struct drm_encoder *encoder;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        obj = drm_mode_object_find(dev, exynos_connector->encoder_id,
                                   DRM_MODE_OBJECT_ENCODER);
        if (!obj) {
@@ -234,8 +195,6 @@ void exynos_drm_display_power(struct drm_connector *connector, int mode)
 static void exynos_drm_connector_dpms(struct drm_connector *connector,
                                        int mode)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /*
         * in case that drm_crtc_helper_set_mode() is called,
         * encoder/crtc->funcs->dpms() will be just returned
@@ -282,8 +241,6 @@ exynos_drm_connector_detect(struct drm_connector *connector, bool force)
                                        manager->display_ops;
        enum drm_connector_status status = connector_status_disconnected;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (display_ops && display_ops->is_connected) {
                if (display_ops->is_connected(manager->dev))
                        status = connector_status_connected;
@@ -299,8 +256,6 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
        struct exynos_drm_connector *exynos_connector =
                to_exynos_connector(connector);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
        kfree(exynos_connector);
@@ -322,8 +277,6 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
        int type;
        int err;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL);
        if (!exynos_connector) {
                DRM_ERROR("failed to allocate connector\n");
index 4667c9f..1bef6dc 100644 (file)
@@ -27,8 +27,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
        struct drm_connector *connector;
        int ret;
 
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        subdrv->manager->dev = subdrv->dev;
 
        /* create and initialize a encoder for this sub driver. */
@@ -102,8 +100,6 @@ static int exynos_drm_subdrv_probe(struct drm_device *dev,
 static void exynos_drm_subdrv_remove(struct drm_device *dev,
                                      struct exynos_drm_subdrv *subdrv)
 {
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        if (subdrv->remove)
                subdrv->remove(dev, subdrv->dev);
 }
@@ -114,8 +110,6 @@ int exynos_drm_device_register(struct drm_device *dev)
        unsigned int fine_cnt = 0;
        int err;
 
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        if (!dev)
                return -EINVAL;
 
@@ -158,8 +152,6 @@ int exynos_drm_device_unregister(struct drm_device *dev)
 {
        struct exynos_drm_subdrv *subdrv;
 
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        if (!dev) {
                WARN(1, "Unexpected drm device unregister!\n");
                return -EINVAL;
@@ -176,8 +168,6 @@ EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
 
 int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
 {
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        if (!subdrv)
                return -EINVAL;
 
@@ -189,8 +179,6 @@ EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
 
 int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
 {
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        if (!subdrv)
                return -EINVAL;
 
index c200e4d..9a35d17 100644 (file)
@@ -76,8 +76,6 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
 
 static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* drm framework doesn't check NULL. */
 }
 
@@ -85,8 +83,6 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
 {
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
        exynos_plane_commit(exynos_crtc->plane);
        exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);
@@ -97,8 +93,6 @@ exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
                            const struct drm_display_mode *mode,
                            struct drm_display_mode *adjusted_mode)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* drm framework doesn't check NULL */
        return true;
 }
@@ -115,8 +109,6 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
        int pipe = exynos_crtc->pipe;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /*
         * copy the mode data adjusted by mode_fixup() into crtc->mode
         * so that hardware can be seet to proper mode.
@@ -139,7 +131,7 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
        return 0;
 }
 
-static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+static int exynos_drm_crtc_mode_set_commit(struct drm_crtc *crtc, int x, int y,
                                          struct drm_framebuffer *old_fb)
 {
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
@@ -148,8 +140,6 @@ static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
        unsigned int crtc_h;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* when framebuffer changing is requested, crtc's dpms should be on */
        if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
                DRM_ERROR("failed framebuffer changing request.\n");
@@ -169,18 +159,16 @@ static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
        return 0;
 }
 
-static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc)
+static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+                                         struct drm_framebuffer *old_fb)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-       /* drm framework doesn't check NULL */
+       return exynos_drm_crtc_mode_set_commit(crtc, x, y, old_fb);
 }
 
 static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
 {
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF);
        exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
 }
@@ -192,7 +180,6 @@ static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
        .mode_fixup     = exynos_drm_crtc_mode_fixup,
        .mode_set       = exynos_drm_crtc_mode_set,
        .mode_set_base  = exynos_drm_crtc_mode_set_base,
-       .load_lut       = exynos_drm_crtc_load_lut,
        .disable        = exynos_drm_crtc_disable,
 };
 
@@ -206,8 +193,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
        struct drm_framebuffer *old_fb = crtc->fb;
        int ret = -EINVAL;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* when the page flip is requested, crtc's dpms should be on */
        if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
                DRM_ERROR("failed page flip request.\n");
@@ -237,7 +222,7 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
                spin_unlock_irq(&dev->event_lock);
 
                crtc->fb = fb;
-               ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
+               ret = exynos_drm_crtc_mode_set_commit(crtc, crtc->x, crtc->y,
                                                    NULL);
                if (ret) {
                        crtc->fb = old_fb;
@@ -260,8 +245,6 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
        struct exynos_drm_private *private = crtc->dev->dev_private;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        private->crtc[exynos_crtc->pipe] = NULL;
 
        drm_crtc_cleanup(crtc);
@@ -276,8 +259,6 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
        struct exynos_drm_private *dev_priv = dev->dev_private;
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (property == dev_priv->crtc_mode_property) {
                enum exynos_crtc_mode mode = val;
 
@@ -322,8 +303,6 @@ static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
        struct exynos_drm_private *dev_priv = dev->dev_private;
        struct drm_property *prop;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        prop = dev_priv->crtc_mode_property;
        if (!prop) {
                prop = drm_property_create_enum(dev, 0, "mode", mode_names,
@@ -343,8 +322,6 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
        struct exynos_drm_private *private = dev->dev_private;
        struct drm_crtc *crtc;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
        if (!exynos_crtc) {
                DRM_ERROR("failed to allocate exynos crtc\n");
@@ -379,8 +356,6 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
        struct exynos_drm_crtc *exynos_crtc =
                to_exynos_crtc(private->crtc[crtc]);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
                return -EPERM;
 
@@ -396,8 +371,6 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
        struct exynos_drm_crtc *exynos_crtc =
                to_exynos_crtc(private->crtc[crtc]);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
                return;
 
@@ -413,8 +386,6 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc)
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc);
        unsigned long flags;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        spin_lock_irqsave(&dev->event_lock, flags);
 
        list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
index ff7f2a8..a0f997e 100644 (file)
@@ -71,8 +71,6 @@ static struct sg_table *
        unsigned int i;
        int nents, ret;
 
-       DRM_DEBUG_PRIME("%s\n", __FILE__);
-
        /* just return current sgt if already requested. */
        if (exynos_attach->dir == dir && exynos_attach->is_mapped)
                return &exynos_attach->sgt;
@@ -133,8 +131,6 @@ static void exynos_dmabuf_release(struct dma_buf *dmabuf)
 {
        struct exynos_drm_gem_obj *exynos_gem_obj = dmabuf->priv;
 
-       DRM_DEBUG_PRIME("%s\n", __FILE__);
-
        /*
         * exynos_dmabuf_release() call means that file object's
         * f_count is 0 and it calls drm_gem_object_handle_unreference()
@@ -219,8 +215,6 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
        struct exynos_drm_gem_buf *buffer;
        int ret;
 
-       DRM_DEBUG_PRIME("%s\n", __FILE__);
-
        /* is this one of own objects? */
        if (dma_buf->ops == &exynos_dmabuf_ops) {
                struct drm_gem_object *obj;
index ba6d995..ca2729a 100644 (file)
@@ -46,8 +46,6 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
        int ret;
        int nr;
 
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL);
        if (!private) {
                DRM_ERROR("failed to allocate private\n");
@@ -140,8 +138,6 @@ err_crtc:
 
 static int exynos_drm_unload(struct drm_device *dev)
 {
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        exynos_drm_fbdev_fini(dev);
        exynos_drm_device_unregister(dev);
        drm_vblank_cleanup(dev);
@@ -159,8 +155,7 @@ static int exynos_drm_unload(struct drm_device *dev)
 static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
 {
        struct drm_exynos_file_private *file_priv;
-
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
+       int ret;
 
        file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
        if (!file_priv)
@@ -168,7 +163,13 @@ static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
 
        file->driver_priv = file_priv;
 
-       return exynos_drm_subdrv_open(dev, file);
+       ret = exynos_drm_subdrv_open(dev, file);
+       if (ret) {
+               kfree(file_priv);
+               file->driver_priv = NULL;
+       }
+
+       return ret;
 }
 
 static void exynos_drm_preclose(struct drm_device *dev,
@@ -178,8 +179,6 @@ static void exynos_drm_preclose(struct drm_device *dev,
        struct drm_pending_vblank_event *e, *t;
        unsigned long flags;
 
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        /* release events of current file */
        spin_lock_irqsave(&dev->event_lock, flags);
        list_for_each_entry_safe(e, t, &private->pageflip_event_list,
@@ -196,8 +195,6 @@ static void exynos_drm_preclose(struct drm_device *dev,
 
 static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
 {
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        if (!file->driver_priv)
                return;
 
@@ -207,8 +204,6 @@ static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
 
 static void exynos_drm_lastclose(struct drm_device *dev)
 {
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        exynos_drm_fbdev_restore_mode(dev);
 }
 
@@ -292,8 +287,6 @@ static struct drm_driver exynos_drm_driver = {
 
 static int exynos_drm_platform_probe(struct platform_device *pdev)
 {
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
        exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls);
 
@@ -302,8 +295,6 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
 
 static int exynos_drm_platform_remove(struct platform_device *pdev)
 {
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        drm_platform_exit(&exynos_drm_driver, pdev);
 
        return 0;
@@ -322,8 +313,6 @@ static int __init exynos_drm_init(void)
 {
        int ret;
 
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
 #ifdef CONFIG_DRM_EXYNOS_FIMD
        ret = platform_driver_register(&fimd_driver);
        if (ret < 0)
@@ -455,8 +444,6 @@ out_fimd:
 
 static void __exit exynos_drm_exit(void)
 {
-       DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
        platform_device_unregister(exynos_drm_pdev);
 
        platform_driver_unregister(&exynos_drm_platform_driver);
index 680a7c1..eaa1966 100644 (file)
@@ -142,7 +142,7 @@ struct exynos_drm_overlay {
  * @is_connected: check for that display is connected or not.
  * @get_edid: get edid modes from display driver.
  * @get_panel: get panel object from display driver.
- * @check_timing: check if timing is valid or not.
+ * @check_mode: check if mode is valid or not.
  * @power_on: display device on or off.
  */
 struct exynos_drm_display_ops {
@@ -151,7 +151,7 @@ struct exynos_drm_display_ops {
        struct edid *(*get_edid)(struct device *dev,
                        struct drm_connector *connector);
        void *(*get_panel)(struct device *dev);
-       int (*check_timing)(struct device *dev, void *timing);
+       int (*check_mode)(struct device *dev, struct drm_display_mode *mode);
        int (*power_on)(struct device *dev, int mode);
 };
 
index c63721f..a99a033 100644 (file)
@@ -61,7 +61,7 @@ static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
        struct exynos_drm_manager_ops *manager_ops = manager->ops;
        struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
 
-       DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode);
+       DRM_DEBUG_KMS("encoder dpms: %d\n", mode);
 
        if (exynos_encoder->dpms == mode) {
                DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
@@ -104,8 +104,6 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
        struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
        struct exynos_drm_manager_ops *manager_ops = manager->ops;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                if (connector->encoder == encoder)
                        if (manager_ops && manager_ops->mode_fixup)
@@ -155,8 +153,6 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
        struct exynos_drm_manager *manager;
        struct exynos_drm_manager_ops *manager_ops;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                if (connector->encoder == encoder) {
                        struct exynos_drm_encoder *exynos_encoder;
@@ -189,8 +185,6 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
 
 static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* drm framework doesn't check NULL. */
 }
 
@@ -200,8 +194,6 @@ static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
        struct exynos_drm_manager *manager = exynos_encoder->manager;
        struct exynos_drm_manager_ops *manager_ops = manager->ops;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (manager_ops && manager_ops->commit)
                manager_ops->commit(manager->dev);
 
@@ -274,8 +266,6 @@ static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
        struct exynos_drm_encoder *exynos_encoder =
                to_exynos_encoder(encoder);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        exynos_encoder->manager->pipe = -1;
 
        drm_encoder_cleanup(encoder);
@@ -315,8 +305,6 @@ void exynos_drm_encoder_setup(struct drm_device *dev)
 {
        struct drm_encoder *encoder;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
                encoder->possible_clones = exynos_drm_encoder_clones(encoder);
 }
@@ -329,8 +317,6 @@ exynos_drm_encoder_create(struct drm_device *dev,
        struct drm_encoder *encoder;
        struct exynos_drm_encoder *exynos_encoder;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (!manager || !possible_crtcs)
                return NULL;
 
@@ -427,8 +413,6 @@ void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
        struct exynos_drm_manager_ops *manager_ops = manager->ops;
        int mode = *(int *)data;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (manager_ops && manager_ops->dpms)
                manager_ops->dpms(manager->dev, mode);
 
@@ -449,8 +433,6 @@ void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data)
                to_exynos_encoder(encoder)->manager;
        int pipe = *(int *)data;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /*
         * when crtc is detached from encoder, this pipe is used
         * to select manager operation
@@ -465,8 +447,6 @@ void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data)
        struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
        struct exynos_drm_overlay *overlay = data;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (overlay_ops && overlay_ops->mode_set)
                overlay_ops->mode_set(manager->dev, overlay);
 }
@@ -478,8 +458,6 @@ void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data)
        struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
        int zpos = DEFAULT_ZPOS;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (data)
                zpos = *(int *)data;
 
@@ -494,8 +472,6 @@ void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data)
        struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
        int zpos = DEFAULT_ZPOS;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (data)
                zpos = *(int *)data;
 
@@ -510,8 +486,6 @@ void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data)
        struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
        int zpos = DEFAULT_ZPOS;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (data)
                zpos = *(int *)data;
 
index 0e04f4e..c2d149f 100644 (file)
@@ -70,8 +70,6 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
        struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
        unsigned int i;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* make sure that overlay data are updated before relesing fb. */
        exynos_drm_encoder_complete_scanout(fb);
 
@@ -97,8 +95,6 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb,
 {
        struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* This fb should have only one gem object. */
        if (WARN_ON(exynos_fb->buf_cnt != 1))
                return -EINVAL;
@@ -112,8 +108,6 @@ static int exynos_drm_fb_dirty(struct drm_framebuffer *fb,
                                unsigned color, struct drm_clip_rect *clips,
                                unsigned num_clips)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* TODO */
 
        return 0;
@@ -225,8 +219,6 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
        struct exynos_drm_fb *exynos_fb;
        int i, ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL);
        if (!exynos_fb) {
                DRM_ERROR("failed to allocate exynos drm framebuffer\n");
@@ -293,8 +285,6 @@ struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb,
        struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
        struct exynos_drm_gem_buf *buffer;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (index >= MAX_FB_BUFFER)
                return NULL;
 
index 8f007aa..8e60bd6 100644 (file)
@@ -43,8 +43,6 @@ static int exynos_drm_fb_mmap(struct fb_info *info,
        unsigned long vm_size;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
 
        vm_size = vma->vm_end - vma->vm_start;
@@ -84,8 +82,6 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
        unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
        unsigned long offset;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
        drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
 
@@ -148,8 +144,6 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
        unsigned long size;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
                        sizes->surface_width, sizes->surface_height,
                        sizes->surface_bpp);
@@ -238,8 +232,6 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
        unsigned int num_crtc;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
                return 0;
 
index 4a1616a..61b094f 100644 (file)
@@ -175,8 +175,6 @@ static void fimc_sw_reset(struct fimc_context *ctx)
 {
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        /* stop dma operation */
        cfg = fimc_read(EXYNOS_CISTATUS);
        if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg)) {
@@ -210,8 +208,6 @@ static void fimc_sw_reset(struct fimc_context *ctx)
 
 static int fimc_set_camblk_fimd0_wb(struct fimc_context *ctx)
 {
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        return regmap_update_bits(ctx->sysreg, SYSREG_CAMERA_BLK,
                                  SYSREG_FIMD0WB_DEST_MASK,
                                  ctx->id << SYSREG_FIMD0WB_DEST_SHIFT);
@@ -221,7 +217,7 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb)
 {
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:wb[%d]\n", __func__, wb);
+       DRM_DEBUG_KMS("wb[%d]\n", wb);
 
        cfg = fimc_read(EXYNOS_CIGCTRL);
        cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK |
@@ -257,10 +253,10 @@ static void fimc_set_polarity(struct fimc_context *ctx,
 {
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:inv_pclk[%d]inv_vsync[%d]\n",
-               __func__, pol->inv_pclk, pol->inv_vsync);
-       DRM_DEBUG_KMS("%s:inv_href[%d]inv_hsync[%d]\n",
-               __func__, pol->inv_href, pol->inv_hsync);
+       DRM_DEBUG_KMS("inv_pclk[%d]inv_vsync[%d]\n",
+               pol->inv_pclk, pol->inv_vsync);
+       DRM_DEBUG_KMS("inv_href[%d]inv_hsync[%d]\n",
+               pol->inv_href, pol->inv_hsync);
 
        cfg = fimc_read(EXYNOS_CIGCTRL);
        cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC |
@@ -282,7 +278,7 @@ static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable)
 {
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable);
+       DRM_DEBUG_KMS("enable[%d]\n", enable);
 
        cfg = fimc_read(EXYNOS_CIGCTRL);
        if (enable)
@@ -298,7 +294,7 @@ static void fimc_handle_irq(struct fimc_context *ctx, bool enable,
 {
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:enable[%d]overflow[%d]level[%d]\n", __func__,
+       DRM_DEBUG_KMS("enable[%d]overflow[%d]level[%d]\n",
                        enable, overflow, level);
 
        cfg = fimc_read(EXYNOS_CIGCTRL);
@@ -319,8 +315,6 @@ static void fimc_clear_irq(struct fimc_context *ctx)
 {
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        cfg = fimc_read(EXYNOS_CIGCTRL);
        cfg |= EXYNOS_CIGCTRL_IRQ_CLR;
        fimc_write(cfg, EXYNOS_CIGCTRL);
@@ -335,7 +329,7 @@ static bool fimc_check_ovf(struct fimc_context *ctx)
        flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB |
                EXYNOS_CISTATUS_OVFICR;
 
-       DRM_DEBUG_KMS("%s:flag[0x%x]\n", __func__, flag);
+       DRM_DEBUG_KMS("flag[0x%x]\n", flag);
 
        if (status & flag) {
                cfg = fimc_read(EXYNOS_CIWDOFST);
@@ -364,7 +358,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx)
 
        cfg = fimc_read(EXYNOS_CISTATUS);
 
-       DRM_DEBUG_KMS("%s:cfg[0x%x]\n", __func__, cfg);
+       DRM_DEBUG_KMS("cfg[0x%x]\n", cfg);
 
        if (!(cfg & EXYNOS_CISTATUS_FRAMEEND))
                return false;
@@ -380,15 +374,13 @@ static int fimc_get_buf_id(struct fimc_context *ctx)
        u32 cfg;
        int frame_cnt, buf_id;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        cfg = fimc_read(EXYNOS_CISTATUS2);
        frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg);
 
        if (frame_cnt == 0)
                frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg);
 
-       DRM_DEBUG_KMS("%s:present[%d]before[%d]\n", __func__,
+       DRM_DEBUG_KMS("present[%d]before[%d]\n",
                EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg),
                EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg));
 
@@ -398,7 +390,7 @@ static int fimc_get_buf_id(struct fimc_context *ctx)
        }
 
        buf_id = frame_cnt - 1;
-       DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id);
+       DRM_DEBUG_KMS("buf_id[%d]\n", buf_id);
 
        return buf_id;
 }
@@ -407,7 +399,7 @@ static void fimc_handle_lastend(struct fimc_context *ctx, bool enable)
 {
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable);
+       DRM_DEBUG_KMS("enable[%d]\n", enable);
 
        cfg = fimc_read(EXYNOS_CIOCTRL);
        if (enable)
@@ -424,7 +416,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt);
+       DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
 
        /* RGB */
        cfg = fimc_read(EXYNOS_CISCCTRL);
@@ -497,7 +489,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt);
+       DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
 
        cfg = fimc_read(EXYNOS_MSCTRL);
        cfg &= ~EXYNOS_MSCTRL_INFORMAT_RGB;
@@ -557,8 +549,7 @@ static int fimc_src_set_transf(struct device *dev,
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg1, cfg2;
 
-       DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__,
-               degree, flip);
+       DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
 
        cfg1 = fimc_read(EXYNOS_MSCTRL);
        cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR |
@@ -621,10 +612,9 @@ static int fimc_set_window(struct fimc_context *ctx,
        v1 = pos->y;
        v2 = sz->vsize - pos->h - pos->y;
 
-       DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n",
-       __func__, pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize);
-       DRM_DEBUG_KMS("%s:h1[%d]h2[%d]v1[%d]v2[%d]\n", __func__,
-               h1, h2, v1, v2);
+       DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n",
+               pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize);
+       DRM_DEBUG_KMS("h1[%d]h2[%d]v1[%d]v2[%d]\n", h1, h2, v1, v2);
 
        /*
         * set window offset 1, 2 size
@@ -653,8 +643,8 @@ static int fimc_src_set_size(struct device *dev, int swap,
        struct drm_exynos_sz img_sz = *sz;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n",
-               __func__, swap, sz->hsize, sz->vsize);
+       DRM_DEBUG_KMS("swap[%d]hsize[%d]vsize[%d]\n",
+               swap, sz->hsize, sz->vsize);
 
        /* original size */
        cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) |
@@ -662,8 +652,7 @@ static int fimc_src_set_size(struct device *dev, int swap,
 
        fimc_write(cfg, EXYNOS_ORGISIZE);
 
-       DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", __func__,
-               pos->x, pos->y, pos->w, pos->h);
+       DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h);
 
        if (swap) {
                img_pos.w = pos->h;
@@ -720,7 +709,7 @@ static int fimc_src_set_addr(struct device *dev,
 
        property = &c_node->property;
 
-       DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__,
+       DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n",
                property->prop_id, buf_id, buf_type);
 
        if (buf_id > FIMC_MAX_SRC) {
@@ -772,7 +761,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt);
+       DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
 
        /* RGB */
        cfg = fimc_read(EXYNOS_CISCCTRL);
@@ -851,7 +840,7 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt);
+       DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
 
        cfg = fimc_read(EXYNOS_CIEXTEN);
 
@@ -919,8 +908,7 @@ static int fimc_dst_set_transf(struct device *dev,
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__,
-               degree, flip);
+       DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
 
        cfg = fimc_read(EXYNOS_CITRGFMT);
        cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK;
@@ -970,7 +958,7 @@ static int fimc_dst_set_transf(struct device *dev,
 
 static int fimc_get_ratio_shift(u32 src, u32 dst, u32 *ratio, u32 *shift)
 {
-       DRM_DEBUG_KMS("%s:src[%d]dst[%d]\n", __func__, src, dst);
+       DRM_DEBUG_KMS("src[%d]dst[%d]\n", src, dst);
 
        if (src >= dst * 64) {
                DRM_ERROR("failed to make ratio and shift.\n");
@@ -1039,20 +1027,20 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
 
        pre_dst_width = src_w / pre_hratio;
        pre_dst_height = src_h / pre_vratio;
-       DRM_DEBUG_KMS("%s:pre_dst_width[%d]pre_dst_height[%d]\n", __func__,
+       DRM_DEBUG_KMS("pre_dst_width[%d]pre_dst_height[%d]\n",
                pre_dst_width, pre_dst_height);
-       DRM_DEBUG_KMS("%s:pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n",
-               __func__, pre_hratio, hfactor, pre_vratio, vfactor);
+       DRM_DEBUG_KMS("pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n",
+               pre_hratio, hfactor, pre_vratio, vfactor);
 
        sc->hratio = (src_w << 14) / (dst_w << hfactor);
        sc->vratio = (src_h << 14) / (dst_h << vfactor);
        sc->up_h = (dst_w >= src_w) ? true : false;
        sc->up_v = (dst_h >= src_h) ? true : false;
-       DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]up_h[%d]up_v[%d]\n",
-       __func__, sc->hratio, sc->vratio, sc->up_h, sc->up_v);
+       DRM_DEBUG_KMS("hratio[%d]vratio[%d]up_h[%d]up_v[%d]\n",
+               sc->hratio, sc->vratio, sc->up_h, sc->up_v);
 
        shfactor = FIMC_SHFACTOR - (hfactor + vfactor);
-       DRM_DEBUG_KMS("%s:shfactor[%d]\n", __func__, shfactor);
+       DRM_DEBUG_KMS("shfactor[%d]\n", shfactor);
 
        cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) |
                EXYNOS_CISCPRERATIO_PREHORRATIO(pre_hratio) |
@@ -1070,10 +1058,10 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc)
 {
        u32 cfg, cfg_ext;
 
-       DRM_DEBUG_KMS("%s:range[%d]bypass[%d]up_h[%d]up_v[%d]\n",
-               __func__, sc->range, sc->bypass, sc->up_h, sc->up_v);
-       DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]\n",
-               __func__, sc->hratio, sc->vratio);
+       DRM_DEBUG_KMS("range[%d]bypass[%d]up_h[%d]up_v[%d]\n",
+               sc->range, sc->bypass, sc->up_h, sc->up_v);
+       DRM_DEBUG_KMS("hratio[%d]vratio[%d]\n",
+               sc->hratio, sc->vratio);
 
        cfg = fimc_read(EXYNOS_CISCCTRL);
        cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS |
@@ -1113,8 +1101,8 @@ static int fimc_dst_set_size(struct device *dev, int swap,
        struct drm_exynos_sz img_sz = *sz;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n",
-               __func__, swap, sz->hsize, sz->vsize);
+       DRM_DEBUG_KMS("swap[%d]hsize[%d]vsize[%d]\n",
+               swap, sz->hsize, sz->vsize);
 
        /* original size */
        cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) |
@@ -1122,8 +1110,7 @@ static int fimc_dst_set_size(struct device *dev, int swap,
 
        fimc_write(cfg, EXYNOS_ORGOSIZE);
 
-       DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n",
-               __func__, pos->x, pos->y, pos->w, pos->h);
+       DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h);
 
        /* CSC ITU */
        cfg = fimc_read(EXYNOS_CIGCTRL);
@@ -1180,7 +1167,7 @@ static int fimc_dst_get_buf_seq(struct fimc_context *ctx)
                if (cfg & (mask << i))
                        buf_num++;
 
-       DRM_DEBUG_KMS("%s:buf_num[%d]\n", __func__, buf_num);
+       DRM_DEBUG_KMS("buf_num[%d]\n", buf_num);
 
        return buf_num;
 }
@@ -1194,8 +1181,7 @@ static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
        u32 mask = 0x00000001 << buf_id;
        int ret = 0;
 
-       DRM_DEBUG_KMS("%s:buf_id[%d]buf_type[%d]\n", __func__,
-               buf_id, buf_type);
+       DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type);
 
        mutex_lock(&ctx->lock);
 
@@ -1252,7 +1238,7 @@ static int fimc_dst_set_addr(struct device *dev,
 
        property = &c_node->property;
 
-       DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__,
+       DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n",
                property->prop_id, buf_id, buf_type);
 
        if (buf_id > FIMC_MAX_DST) {
@@ -1302,7 +1288,7 @@ static struct exynos_drm_ipp_ops fimc_dst_ops = {
 
 static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable)
 {
-       DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable);
+       DRM_DEBUG_KMS("enable[%d]\n", enable);
 
        if (enable) {
                clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]);
@@ -1326,7 +1312,7 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id)
                c_node->event_work;
        int buf_id;
 
-       DRM_DEBUG_KMS("%s:fimc id[%d]\n", __func__, ctx->id);
+       DRM_DEBUG_KMS("fimc id[%d]\n", ctx->id);
 
        fimc_clear_irq(ctx);
        if (fimc_check_ovf(ctx))
@@ -1339,7 +1325,7 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id)
        if (buf_id < 0)
                return IRQ_HANDLED;
 
-       DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id);
+       DRM_DEBUG_KMS("buf_id[%d]\n", buf_id);
 
        if (fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE) < 0) {
                DRM_ERROR("failed to dequeue.\n");
@@ -1357,8 +1343,6 @@ static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
 {
        struct drm_exynos_ipp_prop_list *prop_list;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
        if (!prop_list) {
                DRM_ERROR("failed to alloc property list.\n");
@@ -1402,7 +1386,7 @@ static inline bool fimc_check_drm_flip(enum drm_exynos_flip flip)
        case EXYNOS_DRM_FLIP_BOTH:
                return true;
        default:
-               DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
+               DRM_DEBUG_KMS("invalid flip\n");
                return false;
        }
 }
@@ -1419,8 +1403,6 @@ static int fimc_ippdrv_check_property(struct device *dev,
        bool swap;
        int i;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        for_each_ipp_ops(i) {
                if ((i == EXYNOS_DRM_OPS_SRC) &&
                        (property->cmd == IPP_CMD_WB))
@@ -1526,8 +1508,6 @@ static void fimc_clear_addr(struct fimc_context *ctx)
 {
        int i;
 
-       DRM_DEBUG_KMS("%s:\n", __func__);
-
        for (i = 0; i < FIMC_MAX_SRC; i++) {
                fimc_write(0, EXYNOS_CIIYSA(i));
                fimc_write(0, EXYNOS_CIICBSA(i));
@@ -1545,8 +1525,6 @@ static int fimc_ippdrv_reset(struct device *dev)
 {
        struct fimc_context *ctx = get_fimc_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        /* reset h/w block */
        fimc_sw_reset(ctx);
 
@@ -1570,7 +1548,7 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
        int ret, i;
        u32 cfg0, cfg1;
 
-       DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd);
+       DRM_DEBUG_KMS("cmd[%d]\n", cmd);
 
        if (!c_node) {
                DRM_ERROR("failed to get c_node.\n");
@@ -1679,7 +1657,7 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
        struct drm_exynos_ipp_set_wb set_wb = {0, 0};
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd);
+       DRM_DEBUG_KMS("cmd[%d]\n", cmd);
 
        switch (cmd) {
        case IPP_CMD_M2M:
@@ -1869,8 +1847,7 @@ static int fimc_probe(struct platform_device *pdev)
                goto err_put_clk;
        }
 
-       DRM_DEBUG_KMS("%s:id[%d]ippdrv[0x%x]\n", __func__, ctx->id,
-               (int)ippdrv);
+       DRM_DEBUG_KMS("id[%d]ippdrv[0x%x]\n", ctx->id, (int)ippdrv);
 
        mutex_init(&ctx->lock);
        platform_set_drvdata(pdev, ctx);
@@ -1917,7 +1894,7 @@ static int fimc_suspend(struct device *dev)
 {
        struct fimc_context *ctx = get_fimc_context(dev);
 
-       DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id);
+       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
 
        if (pm_runtime_suspended(dev))
                return 0;
@@ -1929,7 +1906,7 @@ static int fimc_resume(struct device *dev)
 {
        struct fimc_context *ctx = get_fimc_context(dev);
 
-       DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id);
+       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
 
        if (!pm_runtime_suspended(dev))
                return fimc_clk_ctrl(ctx, true);
@@ -1943,7 +1920,7 @@ static int fimc_runtime_suspend(struct device *dev)
 {
        struct fimc_context *ctx = get_fimc_context(dev);
 
-       DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id);
+       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
 
        return  fimc_clk_ctrl(ctx, false);
 }
@@ -1952,7 +1929,7 @@ static int fimc_runtime_resume(struct device *dev)
 {
        struct fimc_context *ctx = get_fimc_context(dev);
 
-       DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id);
+       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
 
        return  fimc_clk_ctrl(ctx, true);
 }
index 97c61db..3e106be 100644 (file)
 
 struct fimd_driver_data {
        unsigned int timing_base;
+
+       unsigned int has_shadowcon:1;
+       unsigned int has_clksel:1;
+};
+
+static struct fimd_driver_data s3c64xx_fimd_driver_data = {
+       .timing_base = 0x0,
+       .has_clksel = 1,
 };
 
 static struct fimd_driver_data exynos4_fimd_driver_data = {
        .timing_base = 0x0,
+       .has_shadowcon = 1,
 };
 
 static struct fimd_driver_data exynos5_fimd_driver_data = {
        .timing_base = 0x20000,
+       .has_shadowcon = 1,
 };
 
 struct fimd_win_data {
@@ -107,10 +117,13 @@ struct fimd_context {
        atomic_t                        wait_vsync_event;
 
        struct exynos_drm_panel_info *panel;
+       struct fimd_driver_data *driver_data;
 };
 
 #ifdef CONFIG_OF
 static const struct of_device_id fimd_driver_dt_match[] = {
+       { .compatible = "samsung,s3c6400-fimd",
+         .data = &s3c64xx_fimd_driver_data },
        { .compatible = "samsung,exynos4210-fimd",
          .data = &exynos4_fimd_driver_data },
        { .compatible = "samsung,exynos5250-fimd",
@@ -137,8 +150,6 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
 
 static bool fimd_display_is_connected(struct device *dev)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* TODO. */
 
        return true;
@@ -148,15 +159,11 @@ static void *fimd_get_panel(struct device *dev)
 {
        struct fimd_context *ctx = get_fimd_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        return ctx->panel;
 }
 
-static int fimd_check_timing(struct device *dev, void *timing)
+static int fimd_check_mode(struct device *dev, struct drm_display_mode *mode)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* TODO. */
 
        return 0;
@@ -164,8 +171,6 @@ static int fimd_check_timing(struct device *dev, void *timing)
 
 static int fimd_display_power_on(struct device *dev, int mode)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* TODO */
 
        return 0;
@@ -175,7 +180,7 @@ static struct exynos_drm_display_ops fimd_display_ops = {
        .type = EXYNOS_DISPLAY_TYPE_LCD,
        .is_connected = fimd_display_is_connected,
        .get_panel = fimd_get_panel,
-       .check_timing = fimd_check_timing,
+       .check_mode = fimd_check_mode,
        .power_on = fimd_display_power_on,
 };
 
@@ -183,7 +188,7 @@ static void fimd_dpms(struct device *subdrv_dev, int mode)
 {
        struct fimd_context *ctx = get_fimd_context(subdrv_dev);
 
-       DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
+       DRM_DEBUG_KMS("%d\n", mode);
 
        mutex_lock(&ctx->lock);
 
@@ -221,8 +226,6 @@ static void fimd_apply(struct device *subdrv_dev)
        struct fimd_win_data *win_data;
        int i;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        for (i = 0; i < WINDOWS_NR; i++) {
                win_data = &ctx->win_data[i];
                if (win_data->enabled && (ovl_ops && ovl_ops->commit))
@@ -239,15 +242,12 @@ static void fimd_commit(struct device *dev)
        struct exynos_drm_panel_info *panel = ctx->panel;
        struct fb_videomode *timing = &panel->timing;
        struct fimd_driver_data *driver_data;
-       struct platform_device *pdev = to_platform_device(dev);
        u32 val;
 
-       driver_data = drm_fimd_get_driver_data(pdev);
+       driver_data = ctx->driver_data;
        if (ctx->suspended)
                return;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* setup polarity values from machine code. */
        writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
 
@@ -274,6 +274,11 @@ static void fimd_commit(struct device *dev)
        val = ctx->vidcon0;
        val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
 
+       if (ctx->driver_data->has_clksel) {
+               val &= ~VIDCON0_CLKSEL_MASK;
+               val |= VIDCON0_CLKSEL_LCD;
+       }
+
        if (ctx->clkdiv > 1)
                val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
        else
@@ -292,8 +297,6 @@ static int fimd_enable_vblank(struct device *dev)
        struct fimd_context *ctx = get_fimd_context(dev);
        u32 val;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (ctx->suspended)
                return -EPERM;
 
@@ -319,8 +322,6 @@ static void fimd_disable_vblank(struct device *dev)
        struct fimd_context *ctx = get_fimd_context(dev);
        u32 val;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (ctx->suspended)
                return;
 
@@ -370,8 +371,6 @@ static void fimd_win_mode_set(struct device *dev,
        int win;
        unsigned long offset;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (!overlay) {
                dev_err(dev, "overlay is NULL\n");
                return;
@@ -381,7 +380,7 @@ static void fimd_win_mode_set(struct device *dev,
        if (win == DEFAULT_ZPOS)
                win = ctx->default_win;
 
-       if (win < 0 || win > WINDOWS_NR)
+       if (win < 0 || win >= WINDOWS_NR)
                return;
 
        offset = overlay->fb_x * (overlay->bpp >> 3);
@@ -418,8 +417,6 @@ static void fimd_win_set_pixfmt(struct device *dev, unsigned int win)
        struct fimd_win_data *win_data = &ctx->win_data[win];
        unsigned long val;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        val = WINCONx_ENWIN;
 
        switch (win_data->bpp) {
@@ -478,8 +475,6 @@ static void fimd_win_set_colkey(struct device *dev, unsigned int win)
        struct fimd_context *ctx = get_fimd_context(dev);
        unsigned int keycon0 = 0, keycon1 = 0;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
                        WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
 
@@ -489,6 +484,33 @@ static void fimd_win_set_colkey(struct device *dev, unsigned int win)
        writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
 }
 
+/**
+ * shadow_protect_win() - disable updating values from shadow registers at vsync
+ *
+ * @win: window to protect registers for
+ * @protect: 1 to protect (disable updates)
+ */
+static void fimd_shadow_protect_win(struct fimd_context *ctx,
+                                                       int win, bool protect)
+{
+       u32 reg, bits, val;
+
+       if (ctx->driver_data->has_shadowcon) {
+               reg = SHADOWCON;
+               bits = SHADOWCON_WINx_PROTECT(win);
+       } else {
+               reg = PRTCON;
+               bits = PRTCON_PROTECT;
+       }
+
+       val = readl(ctx->regs + reg);
+       if (protect)
+               val |= bits;
+       else
+               val &= ~bits;
+       writel(val, ctx->regs + reg);
+}
+
 static void fimd_win_commit(struct device *dev, int zpos)
 {
        struct fimd_context *ctx = get_fimd_context(dev);
@@ -498,21 +520,19 @@ static void fimd_win_commit(struct device *dev, int zpos)
        unsigned int last_x;
        unsigned int last_y;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (ctx->suspended)
                return;
 
        if (win == DEFAULT_ZPOS)
                win = ctx->default_win;
 
-       if (win < 0 || win > WINDOWS_NR)
+       if (win < 0 || win >= WINDOWS_NR)
                return;
 
        win_data = &ctx->win_data[win];
 
        /*
-        * SHADOWCON register is used for enabling timing.
+        * SHADOWCON/PRTCON register is used for enabling timing.
         *
         * for example, once only width value of a register is set,
         * if the dma is started then fimd hardware could malfunction so
@@ -522,9 +542,7 @@ static void fimd_win_commit(struct device *dev, int zpos)
         */
 
        /* protect windows */
-       val = readl(ctx->regs + SHADOWCON);
-       val |= SHADOWCON_WINx_PROTECT(win);
-       writel(val, ctx->regs + SHADOWCON);
+       fimd_shadow_protect_win(ctx, win, true);
 
        /* buffer start address */
        val = (unsigned long)win_data->dma_addr;
@@ -602,10 +620,13 @@ static void fimd_win_commit(struct device *dev, int zpos)
        writel(val, ctx->regs + WINCON(win));
 
        /* Enable DMA channel and unprotect windows */
-       val = readl(ctx->regs + SHADOWCON);
-       val |= SHADOWCON_CHx_ENABLE(win);
-       val &= ~SHADOWCON_WINx_PROTECT(win);
-       writel(val, ctx->regs + SHADOWCON);
+       fimd_shadow_protect_win(ctx, win, false);
+
+       if (ctx->driver_data->has_shadowcon) {
+               val = readl(ctx->regs + SHADOWCON);
+               val |= SHADOWCON_CHx_ENABLE(win);
+               writel(val, ctx->regs + SHADOWCON);
+       }
 
        win_data->enabled = true;
 }
@@ -617,12 +638,10 @@ static void fimd_win_disable(struct device *dev, int zpos)
        int win = zpos;
        u32 val;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (win == DEFAULT_ZPOS)
                win = ctx->default_win;
 
-       if (win < 0 || win > WINDOWS_NR)
+       if (win < 0 || win >= WINDOWS_NR)
                return;
 
        win_data = &ctx->win_data[win];
@@ -634,9 +653,7 @@ static void fimd_win_disable(struct device *dev, int zpos)
        }
 
        /* protect windows */
-       val = readl(ctx->regs + SHADOWCON);
-       val |= SHADOWCON_WINx_PROTECT(win);
-       writel(val, ctx->regs + SHADOWCON);
+       fimd_shadow_protect_win(ctx, win, true);
 
        /* wincon */
        val = readl(ctx->regs + WINCON(win));
@@ -644,10 +661,13 @@ static void fimd_win_disable(struct device *dev, int zpos)
        writel(val, ctx->regs + WINCON(win));
 
        /* unprotect windows */
-       val = readl(ctx->regs + SHADOWCON);
-       val &= ~SHADOWCON_CHx_ENABLE(win);
-       val &= ~SHADOWCON_WINx_PROTECT(win);
-       writel(val, ctx->regs + SHADOWCON);
+       if (ctx->driver_data->has_shadowcon) {
+               val = readl(ctx->regs + SHADOWCON);
+               val &= ~SHADOWCON_CHx_ENABLE(win);
+               writel(val, ctx->regs + SHADOWCON);
+       }
+
+       fimd_shadow_protect_win(ctx, win, false);
 
        win_data->enabled = false;
 }
@@ -697,8 +717,6 @@ out:
 
 static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /*
         * enable drm irq mode.
         * - with irq_enabled = 1, we can use the vblank feature.
@@ -725,8 +743,6 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
 
 static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* detach this sub driver from iommu mapping if supported. */
        if (is_drm_iommu_supported(drm_dev))
                drm_iommu_detach_device(drm_dev, dev);
@@ -741,8 +757,6 @@ static int fimd_calc_clkdiv(struct fimd_context *ctx,
        u32 best_framerate = 0;
        u32 framerate;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        retrace = timing->left_margin + timing->hsync_len +
                                timing->right_margin + timing->xres;
        retrace *= timing->upper_margin + timing->vsync_len +
@@ -777,10 +791,6 @@ static int fimd_calc_clkdiv(struct fimd_context *ctx,
 
 static void fimd_clear_win(struct fimd_context *ctx, int win)
 {
-       u32 val;
-
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        writel(0, ctx->regs + WINCON(win));
        writel(0, ctx->regs + VIDOSD_A(win));
        writel(0, ctx->regs + VIDOSD_B(win));
@@ -789,15 +799,11 @@ static void fimd_clear_win(struct fimd_context *ctx, int win)
        if (win == 1 || win == 2)
                writel(0, ctx->regs + VIDOSD_D(win));
 
-       val = readl(ctx->regs + SHADOWCON);
-       val &= ~SHADOWCON_WINx_PROTECT(win);
-       writel(val, ctx->regs + SHADOWCON);
+       fimd_shadow_protect_win(ctx, win, false);
 }
 
 static int fimd_clock(struct fimd_context *ctx, bool enable)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (enable) {
                int ret;
 
@@ -883,8 +889,6 @@ static int fimd_probe(struct platform_device *pdev)
        int win;
        int ret = -EINVAL;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (dev->of_node) {
                pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
                if (!pdata) {
@@ -949,6 +953,7 @@ static int fimd_probe(struct platform_device *pdev)
                return ret;
        }
 
+       ctx->driver_data = drm_fimd_get_driver_data(pdev);
        ctx->vidcon0 = pdata->vidcon0;
        ctx->vidcon1 = pdata->vidcon1;
        ctx->default_win = pdata->default_win;
@@ -989,8 +994,6 @@ static int fimd_remove(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct fimd_context *ctx = platform_get_drvdata(pdev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        exynos_drm_subdrv_unregister(&ctx->subdrv);
 
        if (ctx->suspended)
@@ -1055,8 +1058,6 @@ static int fimd_runtime_suspend(struct device *dev)
 {
        struct fimd_context *ctx = get_fimd_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        return fimd_activate(ctx, false);
 }
 
@@ -1064,14 +1065,15 @@ static int fimd_runtime_resume(struct device *dev)
 {
        struct fimd_context *ctx = get_fimd_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        return fimd_activate(ctx, true);
 }
 #endif
 
 static struct platform_device_id fimd_driver_ids[] = {
        {
+               .name           = "s3c64xx-fb",
+               .driver_data    = (unsigned long)&s3c64xx_fimd_driver_data,
+       }, {
                .name           = "exynos4-fb",
                .driver_data    = (unsigned long)&exynos4_fimd_driver_data,
        }, {
index af75434..42a5a54 100644 (file)
@@ -388,12 +388,9 @@ out:
 
        sg_free_table(g2d_userptr->sgt);
        kfree(g2d_userptr->sgt);
-       g2d_userptr->sgt = NULL;
 
-       kfree(g2d_userptr->pages);
-       g2d_userptr->pages = NULL;
+       drm_free_large(g2d_userptr->pages);
        kfree(g2d_userptr);
-       g2d_userptr = NULL;
 }
 
 static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
@@ -463,11 +460,11 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
        npages = (end - start) >> PAGE_SHIFT;
        g2d_userptr->npages = npages;
 
-       pages = kzalloc(npages * sizeof(struct page *), GFP_KERNEL);
+       pages = drm_calloc_large(npages, sizeof(struct page *));
        if (!pages) {
                DRM_ERROR("failed to allocate pages.\n");
-               kfree(g2d_userptr);
-               return ERR_PTR(-ENOMEM);
+               ret = -ENOMEM;
+               goto err_free;
        }
 
        vma = find_vma(current->mm, userptr);
@@ -543,7 +540,6 @@ err_sg_free_table:
 
 err_free_sgt:
        kfree(sgt);
-       sgt = NULL;
 
 err_free_userptr:
        exynos_gem_put_pages_to_userptr(g2d_userptr->pages,
@@ -554,10 +550,10 @@ err_put_vma:
        exynos_gem_put_vma(g2d_userptr->vma);
 
 err_free_pages:
-       kfree(pages);
+       drm_free_large(pages);
+
+err_free:
        kfree(g2d_userptr);
-       pages = NULL;
-       g2d_userptr = NULL;
 
        return ERR_PTR(ret);
 }
index cf4543f..24c22a8 100644 (file)
@@ -132,8 +132,6 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
        struct drm_gem_object *obj;
        struct exynos_drm_gem_buf *buf;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        obj = &exynos_gem_obj->base;
        buf = exynos_gem_obj->buffer;
 
@@ -227,7 +225,6 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
        }
 
        size = roundup_gem_size(size, flags);
-       DRM_DEBUG_KMS("%s\n", __FILE__);
 
        ret = check_gem_flags(flags);
        if (ret)
@@ -249,13 +246,14 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
        exynos_gem_obj->flags = flags;
 
        ret = exynos_drm_alloc_buf(dev, buf, flags);
-       if (ret < 0) {
-               drm_gem_object_release(&exynos_gem_obj->base);
-               goto err_fini_buf;
-       }
+       if (ret < 0)
+               goto err_gem_fini;
 
        return exynos_gem_obj;
 
+err_gem_fini:
+       drm_gem_object_release(&exynos_gem_obj->base);
+       kfree(exynos_gem_obj);
 err_fini_buf:
        exynos_drm_fini_buf(dev, buf);
        return ERR_PTR(ret);
@@ -268,8 +266,6 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
        struct exynos_drm_gem_obj *exynos_gem_obj;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
        if (IS_ERR(exynos_gem_obj))
                return PTR_ERR(exynos_gem_obj);
@@ -331,8 +327,6 @@ int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_exynos_gem_map_off *args = data;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
                        args->handle, (unsigned long)args->offset);
 
@@ -371,8 +365,6 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
        unsigned long vm_size;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
        vma->vm_private_data = obj;
        vma->vm_ops = drm_dev->driver->gem_vm_ops;
@@ -429,9 +421,7 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_exynos_gem_mmap *args = data;
        struct drm_gem_object *obj;
-       unsigned int addr;
-
-       DRM_DEBUG_KMS("%s\n", __FILE__);
+       unsigned long addr;
 
        if (!(dev->driver->driver_features & DRIVER_GEM)) {
                DRM_ERROR("does not support GEM.\n");
@@ -473,14 +463,14 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
 
        drm_gem_object_unreference(obj);
 
-       if (IS_ERR((void *)addr)) {
+       if (IS_ERR_VALUE(addr)) {
                /* check filp->f_op, filp->private_data are restored */
                if (file_priv->filp->f_op == &exynos_drm_gem_fops) {
                        file_priv->filp->f_op = fops_get(dev->driver->fops);
                        file_priv->filp->private_data = file_priv;
                }
                mutex_unlock(&dev->struct_mutex);
-               return PTR_ERR((void *)addr);
+               return (int)addr;
        }
 
        mutex_unlock(&dev->struct_mutex);
@@ -643,8 +633,6 @@ void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
 
 int exynos_drm_gem_init_object(struct drm_gem_object *obj)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        return 0;
 }
 
@@ -653,8 +641,6 @@ void exynos_drm_gem_free_object(struct drm_gem_object *obj)
        struct exynos_drm_gem_obj *exynos_gem_obj;
        struct exynos_drm_gem_buf *buf;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        exynos_gem_obj = to_exynos_gem_obj(obj);
        buf = exynos_gem_obj->buffer;
 
@@ -671,8 +657,6 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
        struct exynos_drm_gem_obj *exynos_gem_obj;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /*
         * alocate memory to be used for framebuffer.
         * - this callback would be called by user application
@@ -704,8 +688,6 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
        struct drm_gem_object *obj;
        int ret = 0;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        mutex_lock(&dev->struct_mutex);
 
        /*
@@ -743,8 +725,6 @@ int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv,
 {
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /*
         * obj->refcount and obj->handle_count are decreased and
         * if both them are 0 then exynos_drm_gem_free_object()
@@ -788,8 +768,6 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
        struct drm_gem_object *obj;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* set vm_area_struct. */
        ret = drm_gem_mmap(filp, vma);
        if (ret < 0) {
index 762f40d..472e3b2 100644 (file)
@@ -400,8 +400,6 @@ static int gsc_sw_reset(struct gsc_context *ctx)
        u32 cfg;
        int count = GSC_RESET_TIMEOUT;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        /* s/w reset */
        cfg = (GSC_SW_RESET_SRESET);
        gsc_write(cfg, GSC_SW_RESET);
@@ -441,8 +439,6 @@ static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable)
 {
        u32 gscblk_cfg;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        gscblk_cfg = readl(SYSREG_GSCBLK_CFG1);
 
        if (enable)
@@ -460,7 +456,7 @@ static void gsc_handle_irq(struct gsc_context *ctx, bool enable,
 {
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:enable[%d]overflow[%d]level[%d]\n", __func__,
+       DRM_DEBUG_KMS("enable[%d]overflow[%d]level[%d]\n",
                        enable, overflow, done);
 
        cfg = gsc_read(GSC_IRQ);
@@ -491,7 +487,7 @@ static int gsc_src_set_fmt(struct device *dev, u32 fmt)
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt);
+       DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
 
        cfg = gsc_read(GSC_IN_CON);
        cfg &= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK |
@@ -567,8 +563,7 @@ static int gsc_src_set_transf(struct device *dev,
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__,
-               degree, flip);
+       DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
 
        cfg = gsc_read(GSC_IN_CON);
        cfg &= ~GSC_IN_ROT_MASK;
@@ -616,8 +611,8 @@ static int gsc_src_set_size(struct device *dev, int swap,
        struct gsc_scaler *sc = &ctx->sc;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:swap[%d]x[%d]y[%d]w[%d]h[%d]\n",
-               __func__, swap, pos->x, pos->y, pos->w, pos->h);
+       DRM_DEBUG_KMS("swap[%d]x[%d]y[%d]w[%d]h[%d]\n",
+               swap, pos->x, pos->y, pos->w, pos->h);
 
        if (swap) {
                img_pos.w = pos->h;
@@ -634,8 +629,7 @@ static int gsc_src_set_size(struct device *dev, int swap,
                GSC_CROPPED_HEIGHT(img_pos.h));
        gsc_write(cfg, GSC_CROPPED_SIZE);
 
-       DRM_DEBUG_KMS("%s:hsize[%d]vsize[%d]\n",
-               __func__, sz->hsize, sz->vsize);
+       DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", sz->hsize, sz->vsize);
 
        /* original size */
        cfg = gsc_read(GSC_SRCIMG_SIZE);
@@ -650,8 +644,7 @@ static int gsc_src_set_size(struct device *dev, int swap,
        cfg = gsc_read(GSC_IN_CON);
        cfg &= ~GSC_IN_RGB_TYPE_MASK;
 
-       DRM_DEBUG_KMS("%s:width[%d]range[%d]\n",
-               __func__, pos->w, sc->range);
+       DRM_DEBUG_KMS("width[%d]range[%d]\n", pos->w, sc->range);
 
        if (pos->w >= GSC_WIDTH_ITU_709)
                if (sc->range)
@@ -677,8 +670,7 @@ static int gsc_src_set_buf_seq(struct gsc_context *ctx, u32 buf_id,
        u32 cfg;
        u32 mask = 0x00000001 << buf_id;
 
-       DRM_DEBUG_KMS("%s:buf_id[%d]buf_type[%d]\n", __func__,
-               buf_id, buf_type);
+       DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type);
 
        /* mask register set */
        cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK);
@@ -721,7 +713,7 @@ static int gsc_src_set_addr(struct device *dev,
 
        property = &c_node->property;
 
-       DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__,
+       DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n",
                property->prop_id, buf_id, buf_type);
 
        if (buf_id > GSC_MAX_SRC) {
@@ -765,7 +757,7 @@ static int gsc_dst_set_fmt(struct device *dev, u32 fmt)
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt);
+       DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
 
        cfg = gsc_read(GSC_OUT_CON);
        cfg &= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK |
@@ -838,8 +830,7 @@ static int gsc_dst_set_transf(struct device *dev,
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__,
-               degree, flip);
+       DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
 
        cfg = gsc_read(GSC_IN_CON);
        cfg &= ~GSC_IN_ROT_MASK;
@@ -881,7 +872,7 @@ static int gsc_dst_set_transf(struct device *dev,
 
 static int gsc_get_ratio_shift(u32 src, u32 dst, u32 *ratio)
 {
-       DRM_DEBUG_KMS("%s:src[%d]dst[%d]\n", __func__, src, dst);
+       DRM_DEBUG_KMS("src[%d]dst[%d]\n", src, dst);
 
        if (src >= dst * 8) {
                DRM_ERROR("failed to make ratio and shift.\n");
@@ -944,20 +935,19 @@ static int gsc_set_prescaler(struct gsc_context *ctx, struct gsc_scaler *sc,
                return ret;
        }
 
-       DRM_DEBUG_KMS("%s:pre_hratio[%d]pre_vratio[%d]\n",
-               __func__, sc->pre_hratio, sc->pre_vratio);
+       DRM_DEBUG_KMS("pre_hratio[%d]pre_vratio[%d]\n",
+               sc->pre_hratio, sc->pre_vratio);
 
        sc->main_hratio = (src_w << 16) / dst_w;
        sc->main_vratio = (src_h << 16) / dst_h;
 
-       DRM_DEBUG_KMS("%s:main_hratio[%ld]main_vratio[%ld]\n",
-               __func__, sc->main_hratio, sc->main_vratio);
+       DRM_DEBUG_KMS("main_hratio[%ld]main_vratio[%ld]\n",
+               sc->main_hratio, sc->main_vratio);
 
        gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio,
                &sc->pre_shfactor);
 
-       DRM_DEBUG_KMS("%s:pre_shfactor[%d]\n", __func__,
-               sc->pre_shfactor);
+       DRM_DEBUG_KMS("pre_shfactor[%d]\n", sc->pre_shfactor);
 
        cfg = (GSC_PRESC_SHFACTOR(sc->pre_shfactor) |
                GSC_PRESC_H_RATIO(sc->pre_hratio) |
@@ -1023,8 +1013,8 @@ static void gsc_set_scaler(struct gsc_context *ctx, struct gsc_scaler *sc)
 {
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:main_hratio[%ld]main_vratio[%ld]\n",
-               __func__, sc->main_hratio, sc->main_vratio);
+       DRM_DEBUG_KMS("main_hratio[%ld]main_vratio[%ld]\n",
+               sc->main_hratio, sc->main_vratio);
 
        gsc_set_h_coef(ctx, sc->main_hratio);
        cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio);
@@ -1043,8 +1033,8 @@ static int gsc_dst_set_size(struct device *dev, int swap,
        struct gsc_scaler *sc = &ctx->sc;
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:swap[%d]x[%d]y[%d]w[%d]h[%d]\n",
-               __func__, swap, pos->x, pos->y, pos->w, pos->h);
+       DRM_DEBUG_KMS("swap[%d]x[%d]y[%d]w[%d]h[%d]\n",
+               swap, pos->x, pos->y, pos->w, pos->h);
 
        if (swap) {
                img_pos.w = pos->h;
@@ -1060,8 +1050,7 @@ static int gsc_dst_set_size(struct device *dev, int swap,
        cfg = (GSC_SCALED_WIDTH(img_pos.w) | GSC_SCALED_HEIGHT(img_pos.h));
        gsc_write(cfg, GSC_SCALED_SIZE);
 
-       DRM_DEBUG_KMS("%s:hsize[%d]vsize[%d]\n",
-               __func__, sz->hsize, sz->vsize);
+       DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", sz->hsize, sz->vsize);
 
        /* original size */
        cfg = gsc_read(GSC_DSTIMG_SIZE);
@@ -1074,8 +1063,7 @@ static int gsc_dst_set_size(struct device *dev, int swap,
        cfg = gsc_read(GSC_OUT_CON);
        cfg &= ~GSC_OUT_RGB_TYPE_MASK;
 
-       DRM_DEBUG_KMS("%s:width[%d]range[%d]\n",
-               __func__, pos->w, sc->range);
+       DRM_DEBUG_KMS("width[%d]range[%d]\n", pos->w, sc->range);
 
        if (pos->w >= GSC_WIDTH_ITU_709)
                if (sc->range)
@@ -1104,7 +1092,7 @@ static int gsc_dst_get_buf_seq(struct gsc_context *ctx)
                if (cfg & (mask << i))
                        buf_num--;
 
-       DRM_DEBUG_KMS("%s:buf_num[%d]\n", __func__, buf_num);
+       DRM_DEBUG_KMS("buf_num[%d]\n", buf_num);
 
        return buf_num;
 }
@@ -1118,8 +1106,7 @@ static int gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id,
        u32 mask = 0x00000001 << buf_id;
        int ret = 0;
 
-       DRM_DEBUG_KMS("%s:buf_id[%d]buf_type[%d]\n", __func__,
-               buf_id, buf_type);
+       DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type);
 
        mutex_lock(&ctx->lock);
 
@@ -1177,7 +1164,7 @@ static int gsc_dst_set_addr(struct device *dev,
 
        property = &c_node->property;
 
-       DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__,
+       DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n",
                property->prop_id, buf_id, buf_type);
 
        if (buf_id > GSC_MAX_DST) {
@@ -1217,7 +1204,7 @@ static struct exynos_drm_ipp_ops gsc_dst_ops = {
 
 static int gsc_clk_ctrl(struct gsc_context *ctx, bool enable)
 {
-       DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable);
+       DRM_DEBUG_KMS("enable[%d]\n", enable);
 
        if (enable) {
                clk_enable(ctx->gsc_clk);
@@ -1236,7 +1223,7 @@ static int gsc_get_src_buf_index(struct gsc_context *ctx)
        u32 buf_id = GSC_MAX_SRC;
        int ret;
 
-       DRM_DEBUG_KMS("%s:gsc id[%d]\n", __func__, ctx->id);
+       DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id);
 
        cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK);
        curr_index = GSC_IN_CURR_GET_INDEX(cfg);
@@ -1259,7 +1246,7 @@ static int gsc_get_src_buf_index(struct gsc_context *ctx)
                return ret;
        }
 
-       DRM_DEBUG_KMS("%s:cfg[0x%x]curr_index[%d]buf_id[%d]\n", __func__, cfg,
+       DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg,
                curr_index, buf_id);
 
        return buf_id;
@@ -1271,7 +1258,7 @@ static int gsc_get_dst_buf_index(struct gsc_context *ctx)
        u32 buf_id = GSC_MAX_DST;
        int ret;
 
-       DRM_DEBUG_KMS("%s:gsc id[%d]\n", __func__, ctx->id);
+       DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id);
 
        cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK);
        curr_index = GSC_OUT_CURR_GET_INDEX(cfg);
@@ -1294,7 +1281,7 @@ static int gsc_get_dst_buf_index(struct gsc_context *ctx)
                return ret;
        }
 
-       DRM_DEBUG_KMS("%s:cfg[0x%x]curr_index[%d]buf_id[%d]\n", __func__, cfg,
+       DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg,
                curr_index, buf_id);
 
        return buf_id;
@@ -1310,7 +1297,7 @@ static irqreturn_t gsc_irq_handler(int irq, void *dev_id)
        u32 status;
        int buf_id[EXYNOS_DRM_OPS_MAX];
 
-       DRM_DEBUG_KMS("%s:gsc id[%d]\n", __func__, ctx->id);
+       DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id);
 
        status = gsc_read(GSC_IRQ);
        if (status & GSC_IRQ_STATUS_OR_IRQ) {
@@ -1331,7 +1318,7 @@ static irqreturn_t gsc_irq_handler(int irq, void *dev_id)
                if (buf_id[EXYNOS_DRM_OPS_DST] < 0)
                        return IRQ_HANDLED;
 
-               DRM_DEBUG_KMS("%s:buf_id_src[%d]buf_id_dst[%d]\n", __func__,
+               DRM_DEBUG_KMS("buf_id_src[%d]buf_id_dst[%d]\n",
                        buf_id[EXYNOS_DRM_OPS_SRC], buf_id[EXYNOS_DRM_OPS_DST]);
 
                event_work->ippdrv = ippdrv;
@@ -1350,8 +1337,6 @@ static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
 {
        struct drm_exynos_ipp_prop_list *prop_list;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
        if (!prop_list) {
                DRM_ERROR("failed to alloc property list.\n");
@@ -1394,7 +1379,7 @@ static inline bool gsc_check_drm_flip(enum drm_exynos_flip flip)
        case EXYNOS_DRM_FLIP_BOTH:
                return true;
        default:
-               DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
+               DRM_DEBUG_KMS("invalid flip\n");
                return false;
        }
 }
@@ -1411,8 +1396,6 @@ static int gsc_ippdrv_check_property(struct device *dev,
        bool swap;
        int i;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        for_each_ipp_ops(i) {
                if ((i == EXYNOS_DRM_OPS_SRC) &&
                        (property->cmd == IPP_CMD_WB))
@@ -1521,8 +1504,6 @@ static int gsc_ippdrv_reset(struct device *dev)
        struct gsc_scaler *sc = &ctx->sc;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        /* reset h/w block */
        ret = gsc_sw_reset(ctx);
        if (ret < 0) {
@@ -1549,7 +1530,7 @@ static int gsc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
        u32 cfg;
        int ret, i;
 
-       DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd);
+       DRM_DEBUG_KMS("cmd[%d]\n", cmd);
 
        if (!c_node) {
                DRM_ERROR("failed to get c_node.\n");
@@ -1643,7 +1624,7 @@ static void gsc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
        struct drm_exynos_ipp_set_wb set_wb = {0, 0};
        u32 cfg;
 
-       DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd);
+       DRM_DEBUG_KMS("cmd[%d]\n", cmd);
 
        switch (cmd) {
        case IPP_CMD_M2M:
@@ -1728,8 +1709,7 @@ static int gsc_probe(struct platform_device *pdev)
                return ret;
        }
 
-       DRM_DEBUG_KMS("%s:id[%d]ippdrv[0x%x]\n", __func__, ctx->id,
-               (int)ippdrv);
+       DRM_DEBUG_KMS("id[%d]ippdrv[0x%x]\n", ctx->id, (int)ippdrv);
 
        mutex_init(&ctx->lock);
        platform_set_drvdata(pdev, ctx);
@@ -1772,7 +1752,7 @@ static int gsc_suspend(struct device *dev)
 {
        struct gsc_context *ctx = get_gsc_context(dev);
 
-       DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id);
+       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
 
        if (pm_runtime_suspended(dev))
                return 0;
@@ -1784,7 +1764,7 @@ static int gsc_resume(struct device *dev)
 {
        struct gsc_context *ctx = get_gsc_context(dev);
 
-       DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id);
+       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
 
        if (!pm_runtime_suspended(dev))
                return gsc_clk_ctrl(ctx, true);
@@ -1798,7 +1778,7 @@ static int gsc_runtime_suspend(struct device *dev)
 {
        struct gsc_context *ctx = get_gsc_context(dev);
 
-       DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id);
+       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
 
        return  gsc_clk_ctrl(ctx, false);
 }
@@ -1807,7 +1787,7 @@ static int gsc_runtime_resume(struct device *dev)
 {
        struct gsc_context *ctx = get_gsc_context(dev);
 
-       DRM_DEBUG_KMS("%s:id[%d]\n", __FILE__, ctx->id);
+       DRM_DEBUG_KMS("id[%d]\n", ctx->id);
 
        return  gsc_clk_ctrl(ctx, true);
 }
index 437fb94..aaa550d 100644 (file)
@@ -88,16 +88,12 @@ void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx)
 
 void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (ops)
                hdmi_ops = ops;
 }
 
 void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (ops)
                mixer_ops = ops;
 }
@@ -106,8 +102,6 @@ static bool drm_hdmi_is_connected(struct device *dev)
 {
        struct drm_hdmi_context *ctx = to_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (hdmi_ops && hdmi_ops->is_connected)
                return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx);
 
@@ -119,34 +113,31 @@ static struct edid *drm_hdmi_get_edid(struct device *dev,
 {
        struct drm_hdmi_context *ctx = to_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (hdmi_ops && hdmi_ops->get_edid)
                return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector);
 
        return NULL;
 }
 
-static int drm_hdmi_check_timing(struct device *dev, void *timing)
+static int drm_hdmi_check_mode(struct device *dev,
+               struct drm_display_mode *mode)
 {
        struct drm_hdmi_context *ctx = to_context(dev);
        int ret = 0;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /*
        * Both, mixer and hdmi should be able to handle the requested mode.
        * If any of the two fails, return mode as BAD.
        */
 
-       if (mixer_ops && mixer_ops->check_timing)
-               ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing);
+       if (mixer_ops && mixer_ops->check_mode)
+               ret = mixer_ops->check_mode(ctx->mixer_ctx->ctx, mode);
 
        if (ret)
                return ret;
 
-       if (hdmi_ops && hdmi_ops->check_timing)
-               return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
+       if (hdmi_ops && hdmi_ops->check_mode)
+               return hdmi_ops->check_mode(ctx->hdmi_ctx->ctx, mode);
 
        return 0;
 }
@@ -155,8 +146,6 @@ static int drm_hdmi_power_on(struct device *dev, int mode)
 {
        struct drm_hdmi_context *ctx = to_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (hdmi_ops && hdmi_ops->power_on)
                return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode);
 
@@ -167,7 +156,7 @@ static struct exynos_drm_display_ops drm_hdmi_display_ops = {
        .type = EXYNOS_DISPLAY_TYPE_HDMI,
        .is_connected = drm_hdmi_is_connected,
        .get_edid = drm_hdmi_get_edid,
-       .check_timing = drm_hdmi_check_timing,
+       .check_mode = drm_hdmi_check_mode,
        .power_on = drm_hdmi_power_on,
 };
 
@@ -177,8 +166,6 @@ static int drm_hdmi_enable_vblank(struct device *subdrv_dev)
        struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
        struct exynos_drm_manager *manager = subdrv->manager;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (mixer_ops && mixer_ops->enable_vblank)
                return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx,
                                                manager->pipe);
@@ -190,8 +177,6 @@ static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (mixer_ops && mixer_ops->disable_vblank)
                return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx);
 }
@@ -200,8 +185,6 @@ static void drm_hdmi_wait_for_vblank(struct device *subdrv_dev)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (mixer_ops && mixer_ops->wait_for_vblank)
                mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx);
 }
@@ -214,11 +197,9 @@ static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
        struct drm_display_mode *m;
        int mode_ok;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        drm_mode_set_crtcinfo(adjusted_mode, 0);
 
-       mode_ok = drm_hdmi_check_timing(subdrv_dev, adjusted_mode);
+       mode_ok = drm_hdmi_check_mode(subdrv_dev, adjusted_mode);
 
        /* just return if user desired mode exists. */
        if (mode_ok == 0)
@@ -229,7 +210,7 @@ static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
         * to adjusted_mode.
         */
        list_for_each_entry(m, &connector->modes, head) {
-               mode_ok = drm_hdmi_check_timing(subdrv_dev, m);
+               mode_ok = drm_hdmi_check_mode(subdrv_dev, m);
 
                if (mode_ok == 0) {
                        struct drm_mode_object base;
@@ -256,8 +237,6 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (hdmi_ops && hdmi_ops->mode_set)
                hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
 }
@@ -267,8 +246,6 @@ static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (hdmi_ops && hdmi_ops->get_max_resol)
                hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height);
 }
@@ -277,8 +254,6 @@ static void drm_hdmi_commit(struct device *subdrv_dev)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (hdmi_ops && hdmi_ops->commit)
                hdmi_ops->commit(ctx->hdmi_ctx->ctx);
 }
@@ -287,8 +262,6 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (mixer_ops && mixer_ops->dpms)
                mixer_ops->dpms(ctx->mixer_ctx->ctx, mode);
 
@@ -301,8 +274,6 @@ static void drm_hdmi_apply(struct device *subdrv_dev)
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
        int i;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        for (i = 0; i < MIXER_WIN_NR; i++) {
                if (!ctx->enabled[i])
                        continue;
@@ -331,8 +302,6 @@ static void drm_mixer_mode_set(struct device *subdrv_dev,
 {
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (mixer_ops && mixer_ops->win_mode_set)
                mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
 }
@@ -342,9 +311,7 @@ static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
        int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
-       if (win < 0 || win > MIXER_WIN_NR) {
+       if (win < 0 || win >= MIXER_WIN_NR) {
                DRM_ERROR("mixer window[%d] is wrong\n", win);
                return;
        }
@@ -360,9 +327,7 @@ static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
        struct drm_hdmi_context *ctx = to_context(subdrv_dev);
        int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
-       if (win < 0 || win > MIXER_WIN_NR) {
+       if (win < 0 || win >= MIXER_WIN_NR) {
                DRM_ERROR("mixer window[%d] is wrong\n", win);
                return;
        }
@@ -392,8 +357,6 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
        struct exynos_drm_subdrv *subdrv = to_subdrv(dev);
        struct drm_hdmi_context *ctx;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (!hdmi_ctx) {
                DRM_ERROR("hdmi context not initialized.\n");
                return -EFAULT;
@@ -440,8 +403,6 @@ static int exynos_drm_hdmi_probe(struct platform_device *pdev)
        struct exynos_drm_subdrv *subdrv;
        struct drm_hdmi_context *ctx;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
        if (!ctx) {
                DRM_LOG_KMS("failed to alloc common hdmi context.\n");
@@ -466,8 +427,6 @@ static int exynos_drm_hdmi_remove(struct platform_device *pdev)
 {
        struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        exynos_drm_subdrv_unregister(&ctx->subdrv);
 
        return 0;
index 6b70944..724cab1 100644 (file)
@@ -32,11 +32,11 @@ struct exynos_hdmi_ops {
        bool (*is_connected)(void *ctx);
        struct edid *(*get_edid)(void *ctx,
                        struct drm_connector *connector);
-       int (*check_timing)(void *ctx, struct fb_videomode *timing);
+       int (*check_mode)(void *ctx, struct drm_display_mode *mode);
        int (*power_on)(void *ctx, int mode);
 
        /* manager */
-       void (*mode_set)(void *ctx, void *mode);
+       void (*mode_set)(void *ctx, struct drm_display_mode *mode);
        void (*get_max_resol)(void *ctx, unsigned int *width,
                                unsigned int *height);
        void (*commit)(void *ctx);
@@ -57,7 +57,7 @@ struct exynos_mixer_ops {
        void (*win_disable)(void *ctx, int zpos);
 
        /* display */
-       int (*check_timing)(void *ctx, struct fb_videomode *timing);
+       int (*check_mode)(void *ctx, struct drm_display_mode *mode);
 };
 
 void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
index be1e884..b1ef8e7 100644 (file)
@@ -131,8 +131,6 @@ void exynos_platform_device_ipp_unregister(void)
 
 int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv)
 {
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (!ippdrv)
                return -EINVAL;
 
@@ -145,8 +143,6 @@ int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv)
 
 int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv)
 {
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (!ippdrv)
                return -EINVAL;
 
@@ -162,8 +158,6 @@ static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj,
 {
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        /* do the allocation under our mutexlock */
        mutex_lock(lock);
        ret = idr_alloc(id_idr, obj, 1, 0, GFP_KERNEL);
@@ -179,7 +173,7 @@ static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id)
 {
        void *obj;
 
-       DRM_DEBUG_KMS("%s:id[%d]\n", __func__, id);
+       DRM_DEBUG_KMS("id[%d]\n", id);
 
        mutex_lock(lock);
 
@@ -216,7 +210,7 @@ static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx,
        struct exynos_drm_ippdrv *ippdrv;
        u32 ipp_id = property->ipp_id;
 
-       DRM_DEBUG_KMS("%s:ipp_id[%d]\n", __func__, ipp_id);
+       DRM_DEBUG_KMS("ipp_id[%d]\n", ipp_id);
 
        if (ipp_id) {
                /* find ipp driver using idr */
@@ -257,14 +251,13 @@ static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx,
                 */
                list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
                        if (ipp_check_dedicated(ippdrv, property->cmd)) {
-                               DRM_DEBUG_KMS("%s:used device.\n", __func__);
+                               DRM_DEBUG_KMS("used device.\n");
                                continue;
                        }
 
                        if (ippdrv->check_property &&
                            ippdrv->check_property(ippdrv->dev, property)) {
-                               DRM_DEBUG_KMS("%s:not support property.\n",
-                                       __func__);
+                               DRM_DEBUG_KMS("not support property.\n");
                                continue;
                        }
 
@@ -283,10 +276,10 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id)
        struct drm_exynos_ipp_cmd_node *c_node;
        int count = 0;
 
-       DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id);
+       DRM_DEBUG_KMS("prop_id[%d]\n", prop_id);
 
        if (list_empty(&exynos_drm_ippdrv_list)) {
-               DRM_DEBUG_KMS("%s:ippdrv_list is empty.\n", __func__);
+               DRM_DEBUG_KMS("ippdrv_list is empty.\n");
                return ERR_PTR(-ENODEV);
        }
 
@@ -296,8 +289,7 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id)
         * e.g PAUSE state, queue buf, command contro.
         */
        list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
-               DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]\n", __func__,
-                       count++, (int)ippdrv);
+               DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", count++, (int)ippdrv);
 
                if (!list_empty(&ippdrv->cmd_list)) {
                        list_for_each_entry(c_node, &ippdrv->cmd_list, list)
@@ -320,8 +312,6 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
        struct exynos_drm_ippdrv *ippdrv;
        int count = 0;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (!ctx) {
                DRM_ERROR("invalid context.\n");
                return -EINVAL;
@@ -332,7 +322,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
                return -EINVAL;
        }
 
-       DRM_DEBUG_KMS("%s:ipp_id[%d]\n", __func__, prop_list->ipp_id);
+       DRM_DEBUG_KMS("ipp_id[%d]\n", prop_list->ipp_id);
 
        if (!prop_list->ipp_id) {
                list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list)
@@ -371,11 +361,11 @@ static void ipp_print_property(struct drm_exynos_ipp_property *property,
        struct drm_exynos_pos *pos = &config->pos;
        struct drm_exynos_sz *sz = &config->sz;
 
-       DRM_DEBUG_KMS("%s:prop_id[%d]ops[%s]fmt[0x%x]\n",
-               __func__, property->prop_id, idx ? "dst" : "src", config->fmt);
+       DRM_DEBUG_KMS("prop_id[%d]ops[%s]fmt[0x%x]\n",
+               property->prop_id, idx ? "dst" : "src", config->fmt);
 
-       DRM_DEBUG_KMS("%s:pos[%d %d %d %d]sz[%d %d]f[%d]r[%d]\n",
-               __func__, pos->x, pos->y, pos->w, pos->h,
+       DRM_DEBUG_KMS("pos[%d %d %d %d]sz[%d %d]f[%d]r[%d]\n",
+               pos->x, pos->y, pos->w, pos->h,
                sz->hsize, sz->vsize, config->flip, config->degree);
 }
 
@@ -385,7 +375,7 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property)
        struct drm_exynos_ipp_cmd_node *c_node;
        u32 prop_id = property->prop_id;
 
-       DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id);
+       DRM_DEBUG_KMS("prop_id[%d]\n", prop_id);
 
        ippdrv = ipp_find_drv_by_handle(prop_id);
        if (IS_ERR(ippdrv)) {
@@ -401,8 +391,8 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property)
        list_for_each_entry(c_node, &ippdrv->cmd_list, list) {
                if ((c_node->property.prop_id == prop_id) &&
                    (c_node->state == IPP_STATE_STOP)) {
-                       DRM_DEBUG_KMS("%s:found cmd[%d]ippdrv[0x%x]\n",
-                               __func__, property->cmd, (int)ippdrv);
+                       DRM_DEBUG_KMS("found cmd[%d]ippdrv[0x%x]\n",
+                               property->cmd, (int)ippdrv);
 
                        c_node->property = *property;
                        return 0;
@@ -418,8 +408,6 @@ static struct drm_exynos_ipp_cmd_work *ipp_create_cmd_work(void)
 {
        struct drm_exynos_ipp_cmd_work *cmd_work;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        cmd_work = kzalloc(sizeof(*cmd_work), GFP_KERNEL);
        if (!cmd_work) {
                DRM_ERROR("failed to alloc cmd_work.\n");
@@ -435,8 +423,6 @@ static struct drm_exynos_ipp_event_work *ipp_create_event_work(void)
 {
        struct drm_exynos_ipp_event_work *event_work;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        event_work = kzalloc(sizeof(*event_work), GFP_KERNEL);
        if (!event_work) {
                DRM_ERROR("failed to alloc event_work.\n");
@@ -460,8 +446,6 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
        struct drm_exynos_ipp_cmd_node *c_node;
        int ret, i;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (!ctx) {
                DRM_ERROR("invalid context.\n");
                return -EINVAL;
@@ -486,7 +470,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
         * instead of allocation.
         */
        if (property->prop_id) {
-               DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id);
+               DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id);
                return ipp_find_and_set_property(property);
        }
 
@@ -512,8 +496,8 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
                goto err_clear;
        }
 
-       DRM_DEBUG_KMS("%s:created prop_id[%d]cmd[%d]ippdrv[0x%x]\n",
-               __func__, property->prop_id, property->cmd, (int)ippdrv);
+       DRM_DEBUG_KMS("created prop_id[%d]cmd[%d]ippdrv[0x%x]\n",
+               property->prop_id, property->cmd, (int)ippdrv);
 
        /* stored property information and ippdrv in private data */
        c_node->priv = priv;
@@ -569,8 +553,6 @@ err_clear:
 
 static void ipp_clean_cmd_node(struct drm_exynos_ipp_cmd_node *c_node)
 {
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        /* delete list */
        list_del(&c_node->list);
 
@@ -593,8 +575,6 @@ static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node)
        struct list_head *head;
        int ret, i, count[EXYNOS_DRM_OPS_MAX] = { 0, };
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        mutex_lock(&c_node->mem_lock);
 
        for_each_ipp_ops(i) {
@@ -602,20 +582,19 @@ static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node)
                head = &c_node->mem_list[i];
 
                if (list_empty(head)) {
-                       DRM_DEBUG_KMS("%s:%s memory empty.\n", __func__,
-                               i ? "dst" : "src");
+                       DRM_DEBUG_KMS("%s memory empty.\n", i ? "dst" : "src");
                        continue;
                }
 
                /* find memory node entry */
                list_for_each_entry(m_node, head, list) {
-                       DRM_DEBUG_KMS("%s:%s,count[%d]m_node[0x%x]\n", __func__,
+                       DRM_DEBUG_KMS("%s,count[%d]m_node[0x%x]\n",
                                i ? "dst" : "src", count[i], (int)m_node);
                        count[i]++;
                }
        }
 
-       DRM_DEBUG_KMS("%s:min[%d]max[%d]\n", __func__,
+       DRM_DEBUG_KMS("min[%d]max[%d]\n",
                min(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST]),
                max(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST]));
 
@@ -644,15 +623,14 @@ static struct drm_exynos_ipp_mem_node
        struct list_head *head;
        int count = 0;
 
-       DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, qbuf->buf_id);
+       DRM_DEBUG_KMS("buf_id[%d]\n", qbuf->buf_id);
 
        /* source/destination memory list */
        head = &c_node->mem_list[qbuf->ops_id];
 
        /* find memory node from memory list */
        list_for_each_entry(m_node, head, list) {
-               DRM_DEBUG_KMS("%s:count[%d]m_node[0x%x]\n",
-                       __func__, count++, (int)m_node);
+               DRM_DEBUG_KMS("count[%d]m_node[0x%x]\n", count++, (int)m_node);
 
                /* compare buffer id */
                if (m_node->buf_id == qbuf->buf_id)
@@ -669,7 +647,7 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
        struct exynos_drm_ipp_ops *ops = NULL;
        int ret = 0;
 
-       DRM_DEBUG_KMS("%s:node[0x%x]\n", __func__, (int)m_node);
+       DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node);
 
        if (!m_node) {
                DRM_ERROR("invalid queue node.\n");
@@ -678,7 +656,7 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
 
        mutex_lock(&c_node->mem_lock);
 
-       DRM_DEBUG_KMS("%s:ops_id[%d]\n", __func__, m_node->ops_id);
+       DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
 
        /* get operations callback */
        ops = ippdrv->ops[m_node->ops_id];
@@ -714,8 +692,6 @@ static struct drm_exynos_ipp_mem_node
        void *addr;
        int i;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        mutex_lock(&c_node->mem_lock);
 
        m_node = kzalloc(sizeof(*m_node), GFP_KERNEL);
@@ -732,14 +708,11 @@ static struct drm_exynos_ipp_mem_node
        m_node->prop_id = qbuf->prop_id;
        m_node->buf_id = qbuf->buf_id;
 
-       DRM_DEBUG_KMS("%s:m_node[0x%x]ops_id[%d]\n", __func__,
-               (int)m_node, qbuf->ops_id);
-       DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]\n", __func__,
-               qbuf->prop_id, m_node->buf_id);
+       DRM_DEBUG_KMS("m_node[0x%x]ops_id[%d]\n", (int)m_node, qbuf->ops_id);
+       DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]\n", qbuf->prop_id, m_node->buf_id);
 
        for_each_ipp_planar(i) {
-               DRM_DEBUG_KMS("%s:i[%d]handle[0x%x]\n", __func__,
-                       i, qbuf->handle[i]);
+               DRM_DEBUG_KMS("i[%d]handle[0x%x]\n", i, qbuf->handle[i]);
 
                /* get dma address by handle */
                if (qbuf->handle[i]) {
@@ -752,9 +725,8 @@ static struct drm_exynos_ipp_mem_node
 
                        buf_info.handles[i] = qbuf->handle[i];
                        buf_info.base[i] = *(dma_addr_t *) addr;
-                       DRM_DEBUG_KMS("%s:i[%d]base[0x%x]hd[0x%x]\n",
-                               __func__, i, buf_info.base[i],
-                               (int)buf_info.handles[i]);
+                       DRM_DEBUG_KMS("i[%d]base[0x%x]hd[0x%x]\n",
+                               i, buf_info.base[i], (int)buf_info.handles[i]);
                }
        }
 
@@ -778,7 +750,7 @@ static int ipp_put_mem_node(struct drm_device *drm_dev,
 {
        int i;
 
-       DRM_DEBUG_KMS("%s:node[0x%x]\n", __func__, (int)m_node);
+       DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node);
 
        if (!m_node) {
                DRM_ERROR("invalid dequeue node.\n");
@@ -792,7 +764,7 @@ static int ipp_put_mem_node(struct drm_device *drm_dev,
 
        mutex_lock(&c_node->mem_lock);
 
-       DRM_DEBUG_KMS("%s:ops_id[%d]\n", __func__, m_node->ops_id);
+       DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
 
        /* put gem buffer */
        for_each_ipp_planar(i) {
@@ -824,8 +796,7 @@ static int ipp_get_event(struct drm_device *drm_dev,
        struct drm_exynos_ipp_send_event *e;
        unsigned long flags;
 
-       DRM_DEBUG_KMS("%s:ops_id[%d]buf_id[%d]\n", __func__,
-               qbuf->ops_id, qbuf->buf_id);
+       DRM_DEBUG_KMS("ops_id[%d]buf_id[%d]\n", qbuf->ops_id, qbuf->buf_id);
 
        e = kzalloc(sizeof(*e), GFP_KERNEL);
 
@@ -857,16 +828,13 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node,
        struct drm_exynos_ipp_send_event *e, *te;
        int count = 0;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (list_empty(&c_node->event_list)) {
-               DRM_DEBUG_KMS("%s:event_list is empty.\n", __func__);
+               DRM_DEBUG_KMS("event_list is empty.\n");
                return;
        }
 
        list_for_each_entry_safe(e, te, &c_node->event_list, base.link) {
-               DRM_DEBUG_KMS("%s:count[%d]e[0x%x]\n",
-                       __func__, count++, (int)e);
+               DRM_DEBUG_KMS("count[%d]e[0x%x]\n", count++, (int)e);
 
                /*
                 * quf == NULL condition means all event deletion.
@@ -912,8 +880,6 @@ static int ipp_queue_buf_with_run(struct device *dev,
        struct exynos_drm_ipp_ops *ops;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        ippdrv = ipp_find_drv_by_handle(qbuf->prop_id);
        if (IS_ERR(ippdrv)) {
                DRM_ERROR("failed to get ipp driver.\n");
@@ -929,12 +895,12 @@ static int ipp_queue_buf_with_run(struct device *dev,
        property = &c_node->property;
 
        if (c_node->state != IPP_STATE_START) {
-               DRM_DEBUG_KMS("%s:bypass for invalid state.\n" , __func__);
+               DRM_DEBUG_KMS("bypass for invalid state.\n");
                return 0;
        }
 
        if (!ipp_check_mem_list(c_node)) {
-               DRM_DEBUG_KMS("%s:empty memory.\n", __func__);
+               DRM_DEBUG_KMS("empty memory.\n");
                return 0;
        }
 
@@ -964,8 +930,6 @@ static void ipp_clean_queue_buf(struct drm_device *drm_dev,
 {
        struct drm_exynos_ipp_mem_node *m_node, *tm_node;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (!list_empty(&c_node->mem_list[qbuf->ops_id])) {
                /* delete list */
                list_for_each_entry_safe(m_node, tm_node,
@@ -989,8 +953,6 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
        struct drm_exynos_ipp_mem_node *m_node;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (!qbuf) {
                DRM_ERROR("invalid buf parameter.\n");
                return -EINVAL;
@@ -1001,8 +963,8 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
                return -EINVAL;
        }
 
-       DRM_DEBUG_KMS("%s:prop_id[%d]ops_id[%s]buf_id[%d]buf_type[%d]\n",
-               __func__, qbuf->prop_id, qbuf->ops_id ? "dst" : "src",
+       DRM_DEBUG_KMS("prop_id[%d]ops_id[%s]buf_id[%d]buf_type[%d]\n",
+               qbuf->prop_id, qbuf->ops_id ? "dst" : "src",
                qbuf->buf_id, qbuf->buf_type);
 
        /* find command node */
@@ -1075,8 +1037,6 @@ err_clean_node:
 static bool exynos_drm_ipp_check_valid(struct device *dev,
                enum drm_exynos_ipp_ctrl ctrl, enum drm_exynos_ipp_state state)
 {
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (ctrl != IPP_CTRL_PLAY) {
                if (pm_runtime_suspended(dev)) {
                        DRM_ERROR("pm:runtime_suspended.\n");
@@ -1104,7 +1064,6 @@ static bool exynos_drm_ipp_check_valid(struct device *dev,
        default:
                DRM_ERROR("invalid state.\n");
                goto err_status;
-               break;
        }
 
        return true;
@@ -1126,8 +1085,6 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
        struct drm_exynos_ipp_cmd_work *cmd_work;
        struct drm_exynos_ipp_cmd_node *c_node;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (!ctx) {
                DRM_ERROR("invalid context.\n");
                return -EINVAL;
@@ -1138,7 +1095,7 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
                return -EINVAL;
        }
 
-       DRM_DEBUG_KMS("%s:ctrl[%d]prop_id[%d]\n", __func__,
+       DRM_DEBUG_KMS("ctrl[%d]prop_id[%d]\n",
                cmd_ctrl->ctrl, cmd_ctrl->prop_id);
 
        ippdrv = ipp_find_drv_by_handle(cmd_ctrl->prop_id);
@@ -1213,7 +1170,7 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
                return -EINVAL;
        }
 
-       DRM_DEBUG_KMS("%s:done ctrl[%d]prop_id[%d]\n", __func__,
+       DRM_DEBUG_KMS("done ctrl[%d]prop_id[%d]\n",
                cmd_ctrl->ctrl, cmd_ctrl->prop_id);
 
        return 0;
@@ -1249,7 +1206,7 @@ static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv,
                return -EINVAL;
        }
 
-       DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id);
+       DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id);
 
        /* reset h/w block */
        if (ippdrv->reset &&
@@ -1310,13 +1267,13 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
        struct list_head *head;
        int ret, i;
 
-       DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id);
+       DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id);
 
        /* store command info in ippdrv */
        ippdrv->c_node = c_node;
 
        if (!ipp_check_mem_list(c_node)) {
-               DRM_DEBUG_KMS("%s:empty memory.\n", __func__);
+               DRM_DEBUG_KMS("empty memory.\n");
                return -ENOMEM;
        }
 
@@ -1343,8 +1300,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
                                return ret;
                        }
 
-                       DRM_DEBUG_KMS("%s:m_node[0x%x]\n",
-                               __func__, (int)m_node);
+                       DRM_DEBUG_KMS("m_node[0x%x]\n", (int)m_node);
 
                        ret = ipp_set_mem_node(ippdrv, c_node, m_node);
                        if (ret) {
@@ -1382,7 +1338,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
                return -EINVAL;
        }
 
-       DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, property->cmd);
+       DRM_DEBUG_KMS("cmd[%d]\n", property->cmd);
 
        /* start operations */
        if (ippdrv->start) {
@@ -1405,7 +1361,7 @@ static int ipp_stop_property(struct drm_device *drm_dev,
        struct list_head *head;
        int ret = 0, i;
 
-       DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id);
+       DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id);
 
        /* put event */
        ipp_put_event(c_node, NULL);
@@ -1418,8 +1374,7 @@ static int ipp_stop_property(struct drm_device *drm_dev,
                        head = &c_node->mem_list[i];
 
                        if (list_empty(head)) {
-                               DRM_DEBUG_KMS("%s:mem_list is empty.\n",
-                                       __func__);
+                               DRM_DEBUG_KMS("mem_list is empty.\n");
                                break;
                        }
 
@@ -1439,7 +1394,7 @@ static int ipp_stop_property(struct drm_device *drm_dev,
                head = &c_node->mem_list[EXYNOS_DRM_OPS_DST];
 
                if (list_empty(head)) {
-                       DRM_DEBUG_KMS("%s:mem_list is empty.\n", __func__);
+                       DRM_DEBUG_KMS("mem_list is empty.\n");
                        break;
                }
 
@@ -1456,7 +1411,7 @@ static int ipp_stop_property(struct drm_device *drm_dev,
                head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC];
 
                if (list_empty(head)) {
-                       DRM_DEBUG_KMS("%s:mem_list is empty.\n", __func__);
+                       DRM_DEBUG_KMS("mem_list is empty.\n");
                        break;
                }
 
@@ -1491,8 +1446,6 @@ void ipp_sched_cmd(struct work_struct *work)
        struct drm_exynos_ipp_property *property;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        ippdrv = cmd_work->ippdrv;
        if (!ippdrv) {
                DRM_ERROR("invalid ippdrv list.\n");
@@ -1550,7 +1503,7 @@ void ipp_sched_cmd(struct work_struct *work)
                break;
        }
 
-       DRM_DEBUG_KMS("%s:ctrl[%d] done.\n", __func__, cmd_work->ctrl);
+       DRM_DEBUG_KMS("ctrl[%d] done.\n", cmd_work->ctrl);
 
 err_unlock:
        mutex_unlock(&c_node->cmd_lock);
@@ -1571,8 +1524,7 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
        int ret, i;
 
        for_each_ipp_ops(i)
-               DRM_DEBUG_KMS("%s:%s buf_id[%d]\n", __func__,
-                       i ? "dst" : "src", buf_id[i]);
+               DRM_DEBUG_KMS("%s buf_id[%d]\n", i ? "dst" : "src", buf_id[i]);
 
        if (!drm_dev) {
                DRM_ERROR("failed to get drm_dev.\n");
@@ -1585,12 +1537,12 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
        }
 
        if (list_empty(&c_node->event_list)) {
-               DRM_DEBUG_KMS("%s:event list is empty.\n", __func__);
+               DRM_DEBUG_KMS("event list is empty.\n");
                return 0;
        }
 
        if (!ipp_check_mem_list(c_node)) {
-               DRM_DEBUG_KMS("%s:empty memory.\n", __func__);
+               DRM_DEBUG_KMS("empty memory.\n");
                return 0;
        }
 
@@ -1609,7 +1561,7 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
                        }
 
                        tbuf_id[i] = m_node->buf_id;
-                       DRM_DEBUG_KMS("%s:%s buf_id[%d]\n", __func__,
+                       DRM_DEBUG_KMS("%s buf_id[%d]\n",
                                i ? "dst" : "src", tbuf_id[i]);
 
                        ret = ipp_put_mem_node(drm_dev, c_node, m_node);
@@ -1677,8 +1629,7 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
        }
 
        do_gettimeofday(&now);
-       DRM_DEBUG_KMS("%s:tv_sec[%ld]tv_usec[%ld]\n"
-               , __func__, now.tv_sec, now.tv_usec);
+       DRM_DEBUG_KMS("tv_sec[%ld]tv_usec[%ld]\n", now.tv_sec, now.tv_usec);
        e->event.tv_sec = now.tv_sec;
        e->event.tv_usec = now.tv_usec;
        e->event.prop_id = property->prop_id;
@@ -1692,7 +1643,7 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
        wake_up_interruptible(&e->base.file_priv->event_wait);
        spin_unlock_irqrestore(&drm_dev->event_lock, flags);
 
-       DRM_DEBUG_KMS("%s:done cmd[%d]prop_id[%d]buf_id[%d]\n", __func__,
+       DRM_DEBUG_KMS("done cmd[%d]prop_id[%d]buf_id[%d]\n",
                property->cmd, property->prop_id, tbuf_id[EXYNOS_DRM_OPS_DST]);
 
        return 0;
@@ -1711,8 +1662,7 @@ void ipp_sched_event(struct work_struct *work)
                return;
        }
 
-       DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__,
-               event_work->buf_id[EXYNOS_DRM_OPS_DST]);
+       DRM_DEBUG_KMS("buf_id[%d]\n", event_work->buf_id[EXYNOS_DRM_OPS_DST]);
 
        ippdrv = event_work->ippdrv;
        if (!ippdrv) {
@@ -1733,8 +1683,8 @@ void ipp_sched_event(struct work_struct *work)
         * or going out operations.
         */
        if (c_node->state != IPP_STATE_START) {
-               DRM_DEBUG_KMS("%s:bypass state[%d]prop_id[%d]\n",
-                       __func__, c_node->state, c_node->property.prop_id);
+               DRM_DEBUG_KMS("bypass state[%d]prop_id[%d]\n",
+                       c_node->state, c_node->property.prop_id);
                goto err_completion;
        }
 
@@ -1759,8 +1709,6 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
        struct exynos_drm_ippdrv *ippdrv;
        int ret, count = 0;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        /* get ipp driver entry */
        list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
                ippdrv->drm_dev = drm_dev;
@@ -1772,7 +1720,7 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
                        goto err_idr;
                }
 
-               DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]ipp_id[%d]\n", __func__,
+               DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]ipp_id[%d]\n",
                        count++, (int)ippdrv, ippdrv->ipp_id);
 
                if (ippdrv->ipp_id == 0) {
@@ -1816,8 +1764,6 @@ static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
 {
        struct exynos_drm_ippdrv *ippdrv;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        /* get ipp driver entry */
        list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
                if (is_drm_iommu_supported(drm_dev))
@@ -1834,8 +1780,6 @@ static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev,
        struct drm_exynos_file_private *file_priv = file->driver_priv;
        struct exynos_drm_ipp_private *priv;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv) {
                DRM_ERROR("failed to allocate priv.\n");
@@ -1846,7 +1790,7 @@ static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev,
 
        INIT_LIST_HEAD(&priv->event_list);
 
-       DRM_DEBUG_KMS("%s:done priv[0x%x]\n", __func__, (int)priv);
+       DRM_DEBUG_KMS("done priv[0x%x]\n", (int)priv);
 
        return 0;
 }
@@ -1860,10 +1804,10 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
        struct drm_exynos_ipp_cmd_node *c_node, *tc_node;
        int count = 0;
 
-       DRM_DEBUG_KMS("%s:for priv[0x%x]\n", __func__, (int)priv);
+       DRM_DEBUG_KMS("for priv[0x%x]\n", (int)priv);
 
        if (list_empty(&exynos_drm_ippdrv_list)) {
-               DRM_DEBUG_KMS("%s:ippdrv_list is empty.\n", __func__);
+               DRM_DEBUG_KMS("ippdrv_list is empty.\n");
                goto err_clear;
        }
 
@@ -1873,8 +1817,8 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
 
                list_for_each_entry_safe(c_node, tc_node,
                        &ippdrv->cmd_list, list) {
-                       DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]\n",
-                               __func__, count++, (int)ippdrv);
+                       DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n",
+                               count++, (int)ippdrv);
 
                        if (c_node->priv == priv) {
                                /*
@@ -1913,8 +1857,6 @@ static int ipp_probe(struct platform_device *pdev)
        if (!ctx)
                return -ENOMEM;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        mutex_init(&ctx->ipp_lock);
        mutex_init(&ctx->prop_lock);
 
@@ -1978,8 +1920,6 @@ static int ipp_remove(struct platform_device *pdev)
 {
        struct ipp_context *ctx = platform_get_drvdata(pdev);
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        /* unregister sub driver */
        exynos_drm_subdrv_unregister(&ctx->subdrv);
 
@@ -1999,7 +1939,7 @@ static int ipp_remove(struct platform_device *pdev)
 
 static int ipp_power_ctrl(struct ipp_context *ctx, bool enable)
 {
-       DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable);
+       DRM_DEBUG_KMS("enable[%d]\n", enable);
 
        return 0;
 }
@@ -2009,8 +1949,6 @@ static int ipp_suspend(struct device *dev)
 {
        struct ipp_context *ctx = get_ipp_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (pm_runtime_suspended(dev))
                return 0;
 
@@ -2021,8 +1959,6 @@ static int ipp_resume(struct device *dev)
 {
        struct ipp_context *ctx = get_ipp_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (!pm_runtime_suspended(dev))
                return ipp_power_ctrl(ctx, true);
 
@@ -2035,8 +1971,6 @@ static int ipp_runtime_suspend(struct device *dev)
 {
        struct ipp_context *ctx = get_ipp_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        return ipp_power_ctrl(ctx, false);
 }
 
@@ -2044,8 +1978,6 @@ static int ipp_runtime_resume(struct device *dev)
 {
        struct ipp_context *ctx = get_ipp_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        return ipp_power_ctrl(ctx, true);
 }
 #endif
index 83efc66..6ee55e6 100644 (file)
@@ -81,8 +81,6 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
        int nr;
        int i;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        nr = exynos_drm_fb_get_buf_cnt(fb);
        for (i = 0; i < nr; i++) {
                struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i);
@@ -159,8 +157,6 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
        struct exynos_plane *exynos_plane = to_exynos_plane(plane);
        struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        if (mode == DRM_MODE_DPMS_ON) {
                if (exynos_plane->enabled)
                        return;
@@ -189,8 +185,6 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 {
        int ret;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        ret = exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y,
                        crtc_w, crtc_h, src_x >> 16, src_y >> 16,
                        src_w >> 16, src_h >> 16);
@@ -207,8 +201,6 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 
 static int exynos_disable_plane(struct drm_plane *plane)
 {
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        exynos_plane_dpms(plane, DRM_MODE_DPMS_OFF);
 
        return 0;
@@ -218,8 +210,6 @@ static void exynos_plane_destroy(struct drm_plane *plane)
 {
        struct exynos_plane *exynos_plane = to_exynos_plane(plane);
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        exynos_disable_plane(plane);
        drm_plane_cleanup(plane);
        kfree(exynos_plane);
@@ -233,8 +223,6 @@ static int exynos_plane_set_property(struct drm_plane *plane,
        struct exynos_plane *exynos_plane = to_exynos_plane(plane);
        struct exynos_drm_private *dev_priv = dev->dev_private;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        if (property == dev_priv->plane_zpos_property) {
                exynos_plane->overlay.zpos = val;
                return 0;
@@ -256,8 +244,6 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane)
        struct exynos_drm_private *dev_priv = dev->dev_private;
        struct drm_property *prop;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        prop = dev_priv->plane_zpos_property;
        if (!prop) {
                prop = drm_property_create_range(dev, 0, "zpos", 0,
@@ -277,8 +263,6 @@ struct drm_plane *exynos_plane_init(struct drm_device *dev,
        struct exynos_plane *exynos_plane;
        int err;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL);
        if (!exynos_plane) {
                DRM_ERROR("failed to allocate plane\n");
index 9b6c709..427640a 100644 (file)
@@ -244,7 +244,7 @@ static int rotator_src_set_size(struct device *dev, int swap,
        /* Get format */
        fmt = rotator_reg_get_fmt(rot);
        if (!rotator_check_reg_fmt(fmt)) {
-               DRM_ERROR("%s:invalid format.\n", __func__);
+               DRM_ERROR("invalid format.\n");
                return -EINVAL;
        }
 
@@ -287,7 +287,7 @@ static int rotator_src_set_addr(struct device *dev,
                /* Get format */
                fmt = rotator_reg_get_fmt(rot);
                if (!rotator_check_reg_fmt(fmt)) {
-                       DRM_ERROR("%s:invalid format.\n", __func__);
+                       DRM_ERROR("invalid format.\n");
                        return -EINVAL;
                }
 
@@ -381,7 +381,7 @@ static int rotator_dst_set_size(struct device *dev, int swap,
        /* Get format */
        fmt = rotator_reg_get_fmt(rot);
        if (!rotator_check_reg_fmt(fmt)) {
-               DRM_ERROR("%s:invalid format.\n", __func__);
+               DRM_ERROR("invalid format.\n");
                return -EINVAL;
        }
 
@@ -422,7 +422,7 @@ static int rotator_dst_set_addr(struct device *dev,
                /* Get format */
                fmt = rotator_reg_get_fmt(rot);
                if (!rotator_check_reg_fmt(fmt)) {
-                       DRM_ERROR("%s:invalid format.\n", __func__);
+                       DRM_ERROR("invalid format.\n");
                        return -EINVAL;
                }
 
@@ -471,8 +471,6 @@ static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
 {
        struct drm_exynos_ipp_prop_list *prop_list;
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
        if (!prop_list) {
                DRM_ERROR("failed to alloc property list.\n");
@@ -502,7 +500,7 @@ static inline bool rotator_check_drm_fmt(u32 fmt)
        case DRM_FORMAT_NV12:
                return true;
        default:
-               DRM_DEBUG_KMS("%s:not support format\n", __func__);
+               DRM_DEBUG_KMS("not support format\n");
                return false;
        }
 }
@@ -516,7 +514,7 @@ static inline bool rotator_check_drm_flip(enum drm_exynos_flip flip)
        case EXYNOS_DRM_FLIP_BOTH:
                return true;
        default:
-               DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
+               DRM_DEBUG_KMS("invalid flip\n");
                return false;
        }
 }
@@ -536,19 +534,18 @@ static int rotator_ippdrv_check_property(struct device *dev,
 
        /* Check format configuration */
        if (src_config->fmt != dst_config->fmt) {
-               DRM_DEBUG_KMS("%s:not support csc feature\n", __func__);
+               DRM_DEBUG_KMS("not support csc feature\n");
                return -EINVAL;
        }
 
        if (!rotator_check_drm_fmt(dst_config->fmt)) {
-               DRM_DEBUG_KMS("%s:invalid format\n", __func__);
+               DRM_DEBUG_KMS("invalid format\n");
                return -EINVAL;
        }
 
        /* Check transform configuration */
        if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
-               DRM_DEBUG_KMS("%s:not support source-side rotation\n",
-                       __func__);
+               DRM_DEBUG_KMS("not support source-side rotation\n");
                return -EINVAL;
        }
 
@@ -561,51 +558,47 @@ static int rotator_ippdrv_check_property(struct device *dev,
                /* No problem */
                break;
        default:
-               DRM_DEBUG_KMS("%s:invalid degree\n", __func__);
+               DRM_DEBUG_KMS("invalid degree\n");
                return -EINVAL;
        }
 
        if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
-               DRM_DEBUG_KMS("%s:not support source-side flip\n", __func__);
+               DRM_DEBUG_KMS("not support source-side flip\n");
                return -EINVAL;
        }
 
        if (!rotator_check_drm_flip(dst_config->flip)) {
-               DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
+               DRM_DEBUG_KMS("invalid flip\n");
                return -EINVAL;
        }
 
        /* Check size configuration */
        if ((src_pos->x + src_pos->w > src_sz->hsize) ||
                (src_pos->y + src_pos->h > src_sz->vsize)) {
-               DRM_DEBUG_KMS("%s:out of source buffer bound\n", __func__);
+               DRM_DEBUG_KMS("out of source buffer bound\n");
                return -EINVAL;
        }
 
        if (swap) {
                if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
                        (dst_pos->y + dst_pos->w > dst_sz->hsize)) {
-                       DRM_DEBUG_KMS("%s:out of destination buffer bound\n",
-                               __func__);
+                       DRM_DEBUG_KMS("out of destination buffer bound\n");
                        return -EINVAL;
                }
 
                if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) {
-                       DRM_DEBUG_KMS("%s:not support scale feature\n",
-                               __func__);
+                       DRM_DEBUG_KMS("not support scale feature\n");
                        return -EINVAL;
                }
        } else {
                if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
                        (dst_pos->y + dst_pos->h > dst_sz->vsize)) {
-                       DRM_DEBUG_KMS("%s:out of destination buffer bound\n",
-                               __func__);
+                       DRM_DEBUG_KMS("out of destination buffer bound\n");
                        return -EINVAL;
                }
 
                if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) {
-                       DRM_DEBUG_KMS("%s:not support scale feature\n",
-                               __func__);
+                       DRM_DEBUG_KMS("not support scale feature\n");
                        return -EINVAL;
                }
        }
@@ -693,7 +686,7 @@ static int rotator_probe(struct platform_device *pdev)
                goto err_ippdrv_register;
        }
 
-       DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv);
+       DRM_DEBUG_KMS("ippdrv[0x%x]\n", (int)ippdrv);
 
        platform_set_drvdata(pdev, rot);
 
@@ -752,8 +745,6 @@ static struct platform_device_id rotator_driver_ids[] = {
 
 static int rotator_clk_crtl(struct rot_context *rot, bool enable)
 {
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (enable) {
                clk_enable(rot->clock);
                rot->suspended = false;
@@ -771,8 +762,6 @@ static int rotator_suspend(struct device *dev)
 {
        struct rot_context *rot = dev_get_drvdata(dev);
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (pm_runtime_suspended(dev))
                return 0;
 
@@ -783,8 +772,6 @@ static int rotator_resume(struct device *dev)
 {
        struct rot_context *rot = dev_get_drvdata(dev);
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        if (!pm_runtime_suspended(dev))
                return rotator_clk_crtl(rot, true);
 
@@ -797,8 +784,6 @@ static int rotator_runtime_suspend(struct device *dev)
 {
        struct rot_context *rot = dev_get_drvdata(dev);
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        return  rotator_clk_crtl(rot, false);
 }
 
@@ -806,8 +791,6 @@ static int rotator_runtime_resume(struct device *dev)
 {
        struct rot_context *rot = dev_get_drvdata(dev);
 
-       DRM_DEBUG_KMS("%s\n", __func__);
-
        return  rotator_clk_crtl(rot, true);
 }
 #endif
index 24376c1..41cc74d 100644 (file)
@@ -89,8 +89,6 @@ static bool vidi_display_is_connected(struct device *dev)
 {
        struct vidi_context *ctx = get_vidi_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /*
         * connection request would come from user side
         * to do hotplug through specific ioctl.
@@ -105,8 +103,6 @@ static struct edid *vidi_get_edid(struct device *dev,
        struct edid *edid;
        int edid_len;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /*
         * the edid data comes from user side and it would be set
         * to ctx->raw_edid through specific ioctl.
@@ -128,17 +124,13 @@ static struct edid *vidi_get_edid(struct device *dev,
 
 static void *vidi_get_panel(struct device *dev)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* TODO. */
 
        return NULL;
 }
 
-static int vidi_check_timing(struct device *dev, void *timing)
+static int vidi_check_mode(struct device *dev, struct drm_display_mode *mode)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* TODO. */
 
        return 0;
@@ -146,8 +138,6 @@ static int vidi_check_timing(struct device *dev, void *timing)
 
 static int vidi_display_power_on(struct device *dev, int mode)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* TODO */
 
        return 0;
@@ -158,7 +148,7 @@ static struct exynos_drm_display_ops vidi_display_ops = {
        .is_connected = vidi_display_is_connected,
        .get_edid = vidi_get_edid,
        .get_panel = vidi_get_panel,
-       .check_timing = vidi_check_timing,
+       .check_mode = vidi_check_mode,
        .power_on = vidi_display_power_on,
 };
 
@@ -166,7 +156,7 @@ static void vidi_dpms(struct device *subdrv_dev, int mode)
 {
        struct vidi_context *ctx = get_vidi_context(subdrv_dev);
 
-       DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
+       DRM_DEBUG_KMS("%d\n", mode);
 
        mutex_lock(&ctx->lock);
 
@@ -196,8 +186,6 @@ static void vidi_apply(struct device *subdrv_dev)
        struct vidi_win_data *win_data;
        int i;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        for (i = 0; i < WINDOWS_NR; i++) {
                win_data = &ctx->win_data[i];
                if (win_data->enabled && (ovl_ops && ovl_ops->commit))
@@ -212,8 +200,6 @@ static void vidi_commit(struct device *dev)
 {
        struct vidi_context *ctx = get_vidi_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (ctx->suspended)
                return;
 }
@@ -222,8 +208,6 @@ static int vidi_enable_vblank(struct device *dev)
 {
        struct vidi_context *ctx = get_vidi_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (ctx->suspended)
                return -EPERM;
 
@@ -246,8 +230,6 @@ static void vidi_disable_vblank(struct device *dev)
 {
        struct vidi_context *ctx = get_vidi_context(dev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (ctx->suspended)
                return;
 
@@ -271,8 +253,6 @@ static void vidi_win_mode_set(struct device *dev,
        int win;
        unsigned long offset;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (!overlay) {
                dev_err(dev, "overlay is NULL\n");
                return;
@@ -282,7 +262,7 @@ static void vidi_win_mode_set(struct device *dev,
        if (win == DEFAULT_ZPOS)
                win = ctx->default_win;
 
-       if (win < 0 || win > WINDOWS_NR)
+       if (win < 0 || win >= WINDOWS_NR)
                return;
 
        offset = overlay->fb_x * (overlay->bpp >> 3);
@@ -324,15 +304,13 @@ static void vidi_win_commit(struct device *dev, int zpos)
        struct vidi_win_data *win_data;
        int win = zpos;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (ctx->suspended)
                return;
 
        if (win == DEFAULT_ZPOS)
                win = ctx->default_win;
 
-       if (win < 0 || win > WINDOWS_NR)
+       if (win < 0 || win >= WINDOWS_NR)
                return;
 
        win_data = &ctx->win_data[win];
@@ -351,12 +329,10 @@ static void vidi_win_disable(struct device *dev, int zpos)
        struct vidi_win_data *win_data;
        int win = zpos;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (win == DEFAULT_ZPOS)
                win = ctx->default_win;
 
-       if (win < 0 || win > WINDOWS_NR)
+       if (win < 0 || win >= WINDOWS_NR)
                return;
 
        win_data = &ctx->win_data[win];
@@ -407,8 +383,6 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
 
 static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /*
         * enable drm irq mode.
         * - with irq_enabled = 1, we can use the vblank feature.
@@ -431,8 +405,6 @@ static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
 
 static void vidi_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
 {
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        /* TODO. */
 }
 
@@ -441,11 +413,6 @@ static int vidi_power_on(struct vidi_context *ctx, bool enable)
        struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
        struct device *dev = subdrv->dev;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
-       if (enable != false && enable != true)
-               return -EINVAL;
-
        if (enable) {
                ctx->suspended = false;
 
@@ -483,8 +450,6 @@ static int vidi_store_connection(struct device *dev,
        struct vidi_context *ctx = get_vidi_context(dev);
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        ret = kstrtoint(buf, 0, &ctx->connected);
        if (ret)
                return ret;
@@ -522,8 +487,6 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
        struct drm_exynos_vidi_connection *vidi = data;
        int edid_len;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (!vidi) {
                DRM_DEBUG_KMS("user data for vidi is null.\n");
                return -EINVAL;
@@ -592,8 +555,6 @@ static int vidi_probe(struct platform_device *pdev)
        struct exynos_drm_subdrv *subdrv;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
        if (!ctx)
                return -ENOMEM;
@@ -625,8 +586,6 @@ static int vidi_remove(struct platform_device *pdev)
 {
        struct vidi_context *ctx = platform_get_drvdata(pdev);
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        exynos_drm_subdrv_unregister(&ctx->subdrv);
 
        if (ctx->raw_edid != (struct edid *)fake_edid_info) {
index fd1426d..62ef597 100644 (file)
@@ -83,6 +83,7 @@ struct hdmi_resources {
        struct clk                      *sclk_pixel;
        struct clk                      *sclk_hdmiphy;
        struct clk                      *hdmiphy;
+       struct clk                      *mout_hdmi;
        struct regulator_bulk_data      *regul_bulk;
        int                             regul_count;
 };
@@ -689,8 +690,6 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
        u32 mod;
        u32 vic;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        mod = hdmi_reg_read(hdata, HDMI_MODE_SEL);
        if (hdata->dvi_mode) {
                hdmi_reg_writeb(hdata, HDMI_VSI_CON,
@@ -755,8 +754,6 @@ static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
        struct edid *raw_edid;
        struct hdmi_context *hdata = ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        if (!hdata->ddc_port)
                return ERR_PTR(-ENODEV);
 
@@ -777,8 +774,6 @@ static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
        const struct hdmiphy_config *confs;
        int count, i;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        if (hdata->type == HDMI_TYPE13) {
                confs = hdmiphy_v13_configs;
                count = ARRAY_SIZE(hdmiphy_v13_configs);
@@ -796,18 +791,17 @@ static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
        return -EINVAL;
 }
 
-static int hdmi_check_timing(void *ctx, struct fb_videomode *timing)
+static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode)
 {
        struct hdmi_context *hdata = ctx;
        int ret;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
+               mode->hdisplay, mode->vdisplay, mode->vrefresh,
+               (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :
+               false, mode->clock * 1000);
 
-       DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", timing->xres,
-                       timing->yres, timing->refresh,
-                       timing->vmode);
-
-       ret = hdmi_find_phy_conf(hdata, timing->pixclock);
+       ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
        if (ret < 0)
                return ret;
        return 0;
@@ -1042,7 +1036,7 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
        }
 }
 
-static void hdmi_v13_timing_apply(struct hdmi_context *hdata)
+static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
 {
        const struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v13_conf.tg;
        const struct hdmi_v13_core_regs *core =
@@ -1118,9 +1112,9 @@ static void hdmi_v13_timing_apply(struct hdmi_context *hdata)
                hdmi_regs_dump(hdata, "timing apply");
        }
 
-       clk_disable(hdata->res.sclk_hdmi);
-       clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy);
-       clk_enable(hdata->res.sclk_hdmi);
+       clk_disable_unprepare(hdata->res.sclk_hdmi);
+       clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_hdmiphy);
+       clk_prepare_enable(hdata->res.sclk_hdmi);
 
        /* enable HDMI and timing generator */
        hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
@@ -1131,7 +1125,7 @@ static void hdmi_v13_timing_apply(struct hdmi_context *hdata)
                hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
 }
 
-static void hdmi_v14_timing_apply(struct hdmi_context *hdata)
+static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
 {
        const struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v14_conf.tg;
        const struct hdmi_v14_core_regs *core =
@@ -1285,9 +1279,9 @@ static void hdmi_v14_timing_apply(struct hdmi_context *hdata)
                hdmi_regs_dump(hdata, "timing apply");
        }
 
-       clk_disable(hdata->res.sclk_hdmi);
-       clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy);
-       clk_enable(hdata->res.sclk_hdmi);
+       clk_disable_unprepare(hdata->res.sclk_hdmi);
+       clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_hdmiphy);
+       clk_prepare_enable(hdata->res.sclk_hdmi);
 
        /* enable HDMI and timing generator */
        hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
@@ -1298,12 +1292,12 @@ static void hdmi_v14_timing_apply(struct hdmi_context *hdata)
                hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
 }
 
-static void hdmi_timing_apply(struct hdmi_context *hdata)
+static void hdmi_mode_apply(struct hdmi_context *hdata)
 {
        if (hdata->type == HDMI_TYPE13)
-               hdmi_v13_timing_apply(hdata);
+               hdmi_v13_mode_apply(hdata);
        else
-               hdmi_v14_timing_apply(hdata);
+               hdmi_v14_mode_apply(hdata);
 }
 
 static void hdmiphy_conf_reset(struct hdmi_context *hdata)
@@ -1311,9 +1305,9 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
        u8 buffer[2];
        u32 reg;
 
-       clk_disable(hdata->res.sclk_hdmi);
-       clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel);
-       clk_enable(hdata->res.sclk_hdmi);
+       clk_disable_unprepare(hdata->res.sclk_hdmi);
+       clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_pixel);
+       clk_prepare_enable(hdata->res.sclk_hdmi);
 
        /* operation mode */
        buffer[0] = 0x1f;
@@ -1336,8 +1330,6 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
 
 static void hdmiphy_poweron(struct hdmi_context *hdata)
 {
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        if (hdata->type == HDMI_TYPE14)
                hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
                        HDMI_PHY_POWER_OFF_EN);
@@ -1345,8 +1337,6 @@ static void hdmiphy_poweron(struct hdmi_context *hdata)
 
 static void hdmiphy_poweroff(struct hdmi_context *hdata)
 {
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        if (hdata->type == HDMI_TYPE14)
                hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
                        HDMI_PHY_POWER_OFF_EN);
@@ -1410,8 +1400,6 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
 
 static void hdmi_conf_apply(struct hdmi_context *hdata)
 {
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        hdmiphy_conf_reset(hdata);
        hdmiphy_conf_apply(hdata);
 
@@ -1423,7 +1411,7 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
        hdmi_audio_init(hdata);
 
        /* setting core registers */
-       hdmi_timing_apply(hdata);
+       hdmi_mode_apply(hdata);
        hdmi_audio_control(hdata, true);
 
        hdmi_regs_dump(hdata, "start");
@@ -1569,8 +1557,7 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
                        (m->vsync_start - m->vdisplay) / 2);
                hdmi_set_reg(core->v2_blank, 2, m->vtotal / 2);
                hdmi_set_reg(core->v1_blank, 2, (m->vtotal - m->vdisplay) / 2);
-               hdmi_set_reg(core->v_blank_f0, 2, (m->vtotal +
-                       ((m->vsync_end - m->vsync_start) * 4) + 5) / 2);
+               hdmi_set_reg(core->v_blank_f0, 2, m->vtotal - m->vdisplay / 2);
                hdmi_set_reg(core->v_blank_f1, 2, m->vtotal);
                hdmi_set_reg(core->v_sync_line_aft_2, 2, (m->vtotal / 2) + 7);
                hdmi_set_reg(core->v_sync_line_aft_1, 2, (m->vtotal / 2) + 2);
@@ -1580,7 +1567,10 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
                        (m->htotal / 2) + (m->hsync_start - m->hdisplay));
                hdmi_set_reg(tg->vact_st, 2, (m->vtotal - m->vdisplay) / 2);
                hdmi_set_reg(tg->vact_sz, 2, m->vdisplay / 2);
-               hdmi_set_reg(tg->vact_st2, 2, 0x249);/* Reset value + 1*/
+               hdmi_set_reg(tg->vact_st2, 2, m->vtotal - m->vdisplay / 2);
+               hdmi_set_reg(tg->vsync2, 2, (m->vtotal / 2) + 1);
+               hdmi_set_reg(tg->vsync_bot_hdmi, 2, (m->vtotal / 2) + 1);
+               hdmi_set_reg(tg->field_bot_hdmi, 2, (m->vtotal / 2) + 1);
                hdmi_set_reg(tg->vact_st3, 2, 0x0);
                hdmi_set_reg(tg->vact_st4, 2, 0x0);
        } else {
@@ -1602,6 +1592,9 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
                hdmi_set_reg(tg->vact_st2, 2, 0x248); /* Reset value */
                hdmi_set_reg(tg->vact_st3, 2, 0x47b); /* Reset value */
                hdmi_set_reg(tg->vact_st4, 2, 0x6ae); /* Reset value */
+               hdmi_set_reg(tg->vsync2, 2, 0x233); /* Reset value */
+               hdmi_set_reg(tg->vsync_bot_hdmi, 2, 0x233); /* Reset value */
+               hdmi_set_reg(tg->field_bot_hdmi, 2, 0x233); /* Reset value */
        }
 
        /* Following values & calculations are same irrespective of mode type */
@@ -1633,22 +1626,19 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
        hdmi_set_reg(tg->hact_sz, 2, m->hdisplay);
        hdmi_set_reg(tg->v_fsz, 2, m->vtotal);
        hdmi_set_reg(tg->vsync, 2, 0x1);
-       hdmi_set_reg(tg->vsync2, 2, 0x233); /* Reset value */
        hdmi_set_reg(tg->field_chg, 2, 0x233); /* Reset value */
        hdmi_set_reg(tg->vsync_top_hdmi, 2, 0x1); /* Reset value */
-       hdmi_set_reg(tg->vsync_bot_hdmi, 2, 0x233); /* Reset value */
        hdmi_set_reg(tg->field_top_hdmi, 2, 0x1); /* Reset value */
-       hdmi_set_reg(tg->field_bot_hdmi, 2, 0x233); /* Reset value */
        hdmi_set_reg(tg->tg_3d, 1, 0x0);
 }
 
-static void hdmi_mode_set(void *ctx, void *mode)
+static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode)
 {
        struct hdmi_context *hdata = ctx;
        struct drm_display_mode *m = mode;
 
-       DRM_DEBUG_KMS("[%s]: xres=%d, yres=%d, refresh=%d, intl=%s\n",
-               __func__, m->hdisplay, m->vdisplay,
+       DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
+               m->hdisplay, m->vdisplay,
                m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
                "INTERLACED" : "PROGERESSIVE");
 
@@ -1661,8 +1651,6 @@ static void hdmi_mode_set(void *ctx, void *mode)
 static void hdmi_get_max_resol(void *ctx, unsigned int *width,
                                        unsigned int *height)
 {
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        *width = MAX_WIDTH;
        *height = MAX_HEIGHT;
 }
@@ -1671,8 +1659,6 @@ static void hdmi_commit(void *ctx)
 {
        struct hdmi_context *hdata = ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        mutex_lock(&hdata->hdmi_mutex);
        if (!hdata->powered) {
                mutex_unlock(&hdata->hdmi_mutex);
@@ -1687,8 +1673,6 @@ static void hdmi_poweron(struct hdmi_context *hdata)
 {
        struct hdmi_resources *res = &hdata->res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        mutex_lock(&hdata->hdmi_mutex);
        if (hdata->powered) {
                mutex_unlock(&hdata->hdmi_mutex);
@@ -1699,10 +1683,12 @@ static void hdmi_poweron(struct hdmi_context *hdata)
 
        mutex_unlock(&hdata->hdmi_mutex);
 
-       regulator_bulk_enable(res->regul_count, res->regul_bulk);
-       clk_enable(res->hdmiphy);
-       clk_enable(res->hdmi);
-       clk_enable(res->sclk_hdmi);
+       if (regulator_bulk_enable(res->regul_count, res->regul_bulk))
+               DRM_DEBUG_KMS("failed to enable regulator bulk\n");
+
+       clk_prepare_enable(res->hdmiphy);
+       clk_prepare_enable(res->hdmi);
+       clk_prepare_enable(res->sclk_hdmi);
 
        hdmiphy_poweron(hdata);
 }
@@ -1711,8 +1697,6 @@ static void hdmi_poweroff(struct hdmi_context *hdata)
 {
        struct hdmi_resources *res = &hdata->res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        mutex_lock(&hdata->hdmi_mutex);
        if (!hdata->powered)
                goto out;
@@ -1725,9 +1709,9 @@ static void hdmi_poweroff(struct hdmi_context *hdata)
        hdmiphy_conf_reset(hdata);
        hdmiphy_poweroff(hdata);
 
-       clk_disable(res->sclk_hdmi);
-       clk_disable(res->hdmi);
-       clk_disable(res->hdmiphy);
+       clk_disable_unprepare(res->sclk_hdmi);
+       clk_disable_unprepare(res->hdmi);
+       clk_disable_unprepare(res->hdmiphy);
        regulator_bulk_disable(res->regul_count, res->regul_bulk);
 
        mutex_lock(&hdata->hdmi_mutex);
@@ -1742,7 +1726,7 @@ static void hdmi_dpms(void *ctx, int mode)
 {
        struct hdmi_context *hdata = ctx;
 
-       DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode);
+       DRM_DEBUG_KMS("mode %d\n", mode);
 
        switch (mode) {
        case DRM_MODE_DPMS_ON:
@@ -1765,7 +1749,7 @@ static struct exynos_hdmi_ops hdmi_ops = {
        /* display */
        .is_connected   = hdmi_is_connected,
        .get_edid       = hdmi_get_edid,
-       .check_timing   = hdmi_check_timing,
+       .check_mode     = hdmi_check_mode,
 
        /* manager */
        .mode_set       = hdmi_mode_set,
@@ -1831,8 +1815,13 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
                DRM_ERROR("failed to get clock 'hdmiphy'\n");
                goto fail;
        }
+       res->mout_hdmi = devm_clk_get(dev, "mout_hdmi");
+       if (IS_ERR(res->mout_hdmi)) {
+               DRM_ERROR("failed to get clock 'mout_hdmi'\n");
+               goto fail;
+       }
 
-       clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
+       clk_set_parent(res->mout_hdmi, res->sclk_pixel);
 
        res->regul_bulk = devm_kzalloc(dev, ARRAY_SIZE(supply) *
                sizeof(res->regul_bulk[0]), GFP_KERNEL);
@@ -1877,7 +1866,6 @@ static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
 {
        struct device_node *np = dev->of_node;
        struct s5p_hdmi_platform_data *pd;
-       enum of_gpio_flags flags;
        u32 value;
 
        pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
@@ -1891,7 +1879,7 @@ static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
                goto err_data;
        }
 
-       pd->hpd_gpio = of_get_named_gpio_flags(np, "hpd-gpio", 0, &flags);
+       pd->hpd_gpio = of_get_named_gpio(np, "hpd-gpio", 0);
 
        return pd;
 
@@ -1929,6 +1917,9 @@ static struct of_device_id hdmi_match_types[] = {
        {
                .compatible = "samsung,exynos5-hdmi",
                .data   = (void *)HDMI_TYPE14,
+       }, {
+               .compatible = "samsung,exynos4212-hdmi",
+               .data   = (void *)HDMI_TYPE14,
        }, {
                /* end node */
        }
@@ -1944,8 +1935,6 @@ static int hdmi_probe(struct platform_device *pdev)
        struct resource *res;
        int ret;
 
-       DRM_DEBUG_KMS("[%d]\n", __LINE__);
-
        if (dev->of_node) {
                pdata = drm_hdmi_dt_parse_pdata(dev);
                if (IS_ERR(pdata)) {
@@ -2071,8 +2060,6 @@ static int hdmi_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        pm_runtime_disable(dev);
 
        /* hdmiphy i2c driver */
@@ -2089,8 +2076,6 @@ static int hdmi_suspend(struct device *dev)
        struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
        struct hdmi_context *hdata = ctx->ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        disable_irq(hdata->irq);
 
        hdata->hpd = false;
@@ -2098,7 +2083,7 @@ static int hdmi_suspend(struct device *dev)
                drm_helper_hpd_irq_event(ctx->drm_dev);
 
        if (pm_runtime_suspended(dev)) {
-               DRM_DEBUG_KMS("%s : Already suspended\n", __func__);
+               DRM_DEBUG_KMS("Already suspended\n");
                return 0;
        }
 
@@ -2112,14 +2097,12 @@ static int hdmi_resume(struct device *dev)
        struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
        struct hdmi_context *hdata = ctx->ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        hdata->hpd = gpio_get_value(hdata->hpd_gpio);
 
        enable_irq(hdata->irq);
 
        if (!pm_runtime_suspended(dev)) {
-               DRM_DEBUG_KMS("%s : Already resumed\n", __func__);
+               DRM_DEBUG_KMS("Already resumed\n");
                return 0;
        }
 
@@ -2134,7 +2117,6 @@ static int hdmi_runtime_suspend(struct device *dev)
 {
        struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
        struct hdmi_context *hdata = ctx->ctx;
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
        hdmi_poweroff(hdata);
 
@@ -2145,7 +2127,6 @@ static int hdmi_runtime_resume(struct device *dev)
 {
        struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
        struct hdmi_context *hdata = ctx->ctx;
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
        hdmi_poweron(hdata);
 
index ea49d13..ef04255 100644 (file)
@@ -50,6 +50,10 @@ static const struct i2c_device_id hdmiphy_id[] = {
 static struct of_device_id hdmiphy_match_types[] = {
        {
                .compatible = "samsung,exynos5-hdmiphy",
+       }, {
+               .compatible = "samsung,exynos4210-hdmiphy",
+       }, {
+               .compatible = "samsung,exynos4212-hdmiphy",
        }, {
                /* end node */
        }
index 7c197d3..42ffb71 100644 (file)
@@ -78,6 +78,7 @@ struct mixer_resources {
 enum mixer_version_id {
        MXR_VER_0_0_0_16,
        MXR_VER_16_0_33_0,
+       MXR_VER_128_0_0_184,
 };
 
 struct mixer_context {
@@ -283,17 +284,19 @@ static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
        val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
                                MXR_CFG_SCAN_PROGRASSIVE);
 
-       /* choosing between porper HD and SD mode */
-       if (height <= 480)
-               val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
-       else if (height <= 576)
-               val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
-       else if (height <= 720)
-               val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
-       else if (height <= 1080)
-               val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
-       else
-               val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
+       if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
+               /* choosing between proper HD and SD mode */
+               if (height <= 480)
+                       val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
+               else if (height <= 576)
+                       val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
+               else if (height <= 720)
+                       val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
+               else if (height <= 1080)
+                       val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
+               else
+                       val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
+       }
 
        mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
 }
@@ -376,7 +379,7 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
        unsigned long flags;
        struct hdmi_win_data *win_data;
        unsigned int x_ratio, y_ratio;
-       unsigned int buf_num;
+       unsigned int buf_num = 1;
        dma_addr_t luma_addr[2], chroma_addr[2];
        bool tiled_mode = false;
        bool crcb_mode = false;
@@ -557,6 +560,14 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win)
        /* setup geometry */
        mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width);
 
+       /* setup display size */
+       if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
+               win == MIXER_DEFAULT_WIN) {
+               val  = MXR_MXR_RES_HEIGHT(win_data->fb_height);
+               val |= MXR_MXR_RES_WIDTH(win_data->fb_width);
+               mixer_reg_write(res, MXR_RESOLUTION, val);
+       }
+
        val  = MXR_GRP_WH_WIDTH(win_data->crtc_width);
        val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height);
        val |= MXR_GRP_WH_H_SCALE(x_ratio);
@@ -581,7 +592,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win)
        mixer_cfg_layer(ctx, win, true);
 
        /* layer update mandatory for mixer 16.0.33.0 */
-       if (ctx->mxr_ver == MXR_VER_16_0_33_0)
+       if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
+               ctx->mxr_ver == MXR_VER_128_0_0_184)
                mixer_layer_update(ctx);
 
        mixer_run(ctx);
@@ -696,8 +708,6 @@ static int mixer_enable_vblank(void *ctx, int pipe)
        struct mixer_context *mixer_ctx = ctx;
        struct mixer_resources *res = &mixer_ctx->mixer_res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        mixer_ctx->pipe = pipe;
 
        /* enable vsync interrupt */
@@ -712,8 +722,6 @@ static void mixer_disable_vblank(void *ctx)
        struct mixer_context *mixer_ctx = ctx;
        struct mixer_resources *res = &mixer_ctx->mixer_res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        /* disable vsync interrupt */
        mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
 }
@@ -725,8 +733,6 @@ static void mixer_win_mode_set(void *ctx,
        struct hdmi_win_data *win_data;
        int win;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        if (!overlay) {
                DRM_ERROR("overlay is NULL\n");
                return;
@@ -742,7 +748,7 @@ static void mixer_win_mode_set(void *ctx,
        if (win == DEFAULT_ZPOS)
                win = MIXER_DEFAULT_WIN;
 
-       if (win < 0 || win > MIXER_WIN_NR) {
+       if (win < 0 || win >= MIXER_WIN_NR) {
                DRM_ERROR("mixer window[%d] is wrong\n", win);
                return;
        }
@@ -776,7 +782,7 @@ static void mixer_win_commit(void *ctx, int win)
 {
        struct mixer_context *mixer_ctx = ctx;
 
-       DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
+       DRM_DEBUG_KMS("win: %d\n", win);
 
        mutex_lock(&mixer_ctx->mixer_mutex);
        if (!mixer_ctx->powered) {
@@ -799,7 +805,7 @@ static void mixer_win_disable(void *ctx, int win)
        struct mixer_resources *res = &mixer_ctx->mixer_res;
        unsigned long flags;
 
-       DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win);
+       DRM_DEBUG_KMS("win: %d\n", win);
 
        mutex_lock(&mixer_ctx->mixer_mutex);
        if (!mixer_ctx->powered) {
@@ -820,17 +826,21 @@ static void mixer_win_disable(void *ctx, int win)
        mixer_ctx->win_data[win].enabled = false;
 }
 
-static int mixer_check_timing(void *ctx, struct fb_videomode *timing)
+static int mixer_check_mode(void *ctx, struct drm_display_mode *mode)
 {
+       struct mixer_context *mixer_ctx = ctx;
        u32 w, h;
 
-       w = timing->xres;
-       h = timing->yres;
+       w = mode->hdisplay;
+       h = mode->vdisplay;
 
-       DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d\n",
-               __func__, timing->xres, timing->yres,
-               timing->refresh, (timing->vmode &
-               FB_VMODE_INTERLACED) ? true : false);
+       DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
+               mode->hdisplay, mode->vdisplay, mode->vrefresh,
+               (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
+
+       if (mixer_ctx->mxr_ver == MXR_VER_0_0_0_16 ||
+               mixer_ctx->mxr_ver == MXR_VER_128_0_0_184)
+               return 0;
 
        if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
                (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
@@ -891,8 +901,6 @@ static void mixer_poweron(struct mixer_context *ctx)
 {
        struct mixer_resources *res = &ctx->mixer_res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        mutex_lock(&ctx->mixer_mutex);
        if (ctx->powered) {
                mutex_unlock(&ctx->mixer_mutex);
@@ -901,10 +909,10 @@ static void mixer_poweron(struct mixer_context *ctx)
        ctx->powered = true;
        mutex_unlock(&ctx->mixer_mutex);
 
-       clk_enable(res->mixer);
+       clk_prepare_enable(res->mixer);
        if (ctx->vp_enabled) {
-               clk_enable(res->vp);
-               clk_enable(res->sclk_mixer);
+               clk_prepare_enable(res->vp);
+               clk_prepare_enable(res->sclk_mixer);
        }
 
        mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
@@ -917,8 +925,6 @@ static void mixer_poweroff(struct mixer_context *ctx)
 {
        struct mixer_resources *res = &ctx->mixer_res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        mutex_lock(&ctx->mixer_mutex);
        if (!ctx->powered)
                goto out;
@@ -928,10 +934,10 @@ static void mixer_poweroff(struct mixer_context *ctx)
 
        ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
 
-       clk_disable(res->mixer);
+       clk_disable_unprepare(res->mixer);
        if (ctx->vp_enabled) {
-               clk_disable(res->vp);
-               clk_disable(res->sclk_mixer);
+               clk_disable_unprepare(res->vp);
+               clk_disable_unprepare(res->sclk_mixer);
        }
 
        mutex_lock(&ctx->mixer_mutex);
@@ -945,8 +951,6 @@ static void mixer_dpms(void *ctx, int mode)
 {
        struct mixer_context *mixer_ctx = ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        switch (mode) {
        case DRM_MODE_DPMS_ON:
                if (pm_runtime_suspended(mixer_ctx->dev))
@@ -978,7 +982,7 @@ static struct exynos_mixer_ops mixer_ops = {
        .win_disable            = mixer_win_disable,
 
        /* display */
-       .check_timing           = mixer_check_timing,
+       .check_mode             = mixer_check_mode,
 };
 
 static irqreturn_t mixer_irq_handler(int irq, void *arg)
@@ -1128,12 +1132,17 @@ static int vp_resources_init(struct exynos_drm_hdmi_context *ctx,
        return 0;
 }
 
-static struct mixer_drv_data exynos5_mxr_drv_data = {
+static struct mixer_drv_data exynos5420_mxr_drv_data = {
+       .version = MXR_VER_128_0_0_184,
+       .is_vp_enabled = 0,
+};
+
+static struct mixer_drv_data exynos5250_mxr_drv_data = {
        .version = MXR_VER_16_0_33_0,
        .is_vp_enabled = 0,
 };
 
-static struct mixer_drv_data exynos4_mxr_drv_data = {
+static struct mixer_drv_data exynos4210_mxr_drv_data = {
        .version = MXR_VER_0_0_0_16,
        .is_vp_enabled = 1,
 };
@@ -1141,10 +1150,10 @@ static struct mixer_drv_data exynos4_mxr_drv_data = {
 static struct platform_device_id mixer_driver_types[] = {
        {
                .name           = "s5p-mixer",
-               .driver_data    = (unsigned long)&exynos4_mxr_drv_data,
+               .driver_data    = (unsigned long)&exynos4210_mxr_drv_data,
        }, {
                .name           = "exynos5-mixer",
-               .driver_data    = (unsigned long)&exynos5_mxr_drv_data,
+               .driver_data    = (unsigned long)&exynos5250_mxr_drv_data,
        }, {
                /* end node */
        }
@@ -1153,7 +1162,13 @@ static struct platform_device_id mixer_driver_types[] = {
 static struct of_device_id mixer_match_types[] = {
        {
                .compatible = "samsung,exynos5-mixer",
-               .data   = &exynos5_mxr_drv_data,
+               .data   = &exynos5250_mxr_drv_data,
+       }, {
+               .compatible = "samsung,exynos5250-mixer",
+               .data   = &exynos5250_mxr_drv_data,
+       }, {
+               .compatible = "samsung,exynos5420-mixer",
+               .data   = &exynos5420_mxr_drv_data,
        }, {
                /* end node */
        }
@@ -1186,8 +1201,7 @@ static int mixer_probe(struct platform_device *pdev)
 
        if (dev->of_node) {
                const struct of_device_id *match;
-               match = of_match_node(of_match_ptr(mixer_match_types),
-                                                         dev->of_node);
+               match = of_match_node(mixer_match_types, dev->of_node);
                drv = (struct mixer_drv_data *)match->data;
        } else {
                drv = (struct mixer_drv_data *)
@@ -1251,10 +1265,8 @@ static int mixer_suspend(struct device *dev)
        struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
        struct mixer_context *ctx = drm_hdmi_ctx->ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        if (pm_runtime_suspended(dev)) {
-               DRM_DEBUG_KMS("%s : Already suspended\n", __func__);
+               DRM_DEBUG_KMS("Already suspended\n");
                return 0;
        }
 
@@ -1268,10 +1280,8 @@ static int mixer_resume(struct device *dev)
        struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
        struct mixer_context *ctx = drm_hdmi_ctx->ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        if (!pm_runtime_suspended(dev)) {
-               DRM_DEBUG_KMS("%s : Already resumed\n", __func__);
+               DRM_DEBUG_KMS("Already resumed\n");
                return 0;
        }
 
@@ -1287,8 +1297,6 @@ static int mixer_runtime_suspend(struct device *dev)
        struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
        struct mixer_context *ctx = drm_hdmi_ctx->ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        mixer_poweroff(ctx);
 
        return 0;
@@ -1299,8 +1307,6 @@ static int mixer_runtime_resume(struct device *dev)
        struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev);
        struct mixer_context *ctx = drm_hdmi_ctx->ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        mixer_poweron(ctx);
 
        return 0;
index 5d8dbc0..4537026 100644 (file)
@@ -44,6 +44,9 @@
 #define MXR_CM_COEFF_Y                 0x0080
 #define MXR_CM_COEFF_CB                        0x0084
 #define MXR_CM_COEFF_CR                        0x0088
+#define MXR_MO                         0x0304
+#define MXR_RESOLUTION                 0x0310
+
 #define MXR_GRAPHIC0_BASE_S            0x2024
 #define MXR_GRAPHIC1_BASE_S            0x2044
 
 #define MXR_GRP_WH_WIDTH(x)            MXR_MASK_VAL(x, 26, 16)
 #define MXR_GRP_WH_HEIGHT(x)           MXR_MASK_VAL(x, 10, 0)
 
+/* bits for MXR_RESOLUTION */
+#define MXR_MXR_RES_HEIGHT(x)          MXR_MASK_VAL(x, 26, 16)
+#define MXR_MXR_RES_WIDTH(x)           MXR_MASK_VAL(x, 10, 0)
+
 /* bits for MXR_GRAPHICn_SXY */
 #define MXR_GRP_SXY_SX(x)              MXR_MASK_VAL(x, 26, 16)
 #define MXR_GRP_SXY_SY(x)              MXR_MASK_VAL(x, 10, 0)
index 91f3ac6..40034ec 100644 (file)
@@ -36,6 +36,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
          intel_overlay.o \
          intel_sprite.o \
          intel_opregion.o \
+         intel_sideband.o \
          dvo_ch7xxx.o \
          dvo_ch7017.o \
          dvo_ivch.o \
index 3edd981..757e0fa 100644 (file)
@@ -32,12 +32,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #define CH7xxx_REG_DID         0x4b
 
 #define CH7011_VID             0x83 /* 7010 as well */
+#define CH7010B_VID            0x05
 #define CH7009A_VID            0x84
 #define CH7009B_VID            0x85
 #define CH7301_VID             0x95
 
 #define CH7xxx_VID             0x84
 #define CH7xxx_DID             0x17
+#define CH7010_DID             0x16
 
 #define CH7xxx_NUM_REGS                0x4c
 
@@ -87,11 +89,20 @@ static struct ch7xxx_id_struct {
        char *name;
 } ch7xxx_ids[] = {
        { CH7011_VID, "CH7011" },
+       { CH7010B_VID, "CH7010B" },
        { CH7009A_VID, "CH7009A" },
        { CH7009B_VID, "CH7009B" },
        { CH7301_VID, "CH7301" },
 };
 
+static struct ch7xxx_did_struct {
+       uint8_t did;
+       char *name;
+} ch7xxx_dids[] = {
+       { CH7xxx_DID, "CH7XXX" },
+       { CH7010_DID, "CH7010B" },
+};
+
 struct ch7xxx_priv {
        bool quiet;
 };
@@ -108,6 +119,18 @@ static char *ch7xxx_get_id(uint8_t vid)
        return NULL;
 }
 
+static char *ch7xxx_get_did(uint8_t did)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ch7xxx_dids); i++) {
+               if (ch7xxx_dids[i].did == did)
+                       return ch7xxx_dids[i].name;
+       }
+
+       return NULL;
+}
+
 /** Reads an 8 bit register */
 static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
 {
@@ -179,7 +202,7 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo,
        /* this will detect the CH7xxx chip on the specified i2c bus */
        struct ch7xxx_priv *ch7xxx;
        uint8_t vendor, device;
-       char *name;
+       char *name, *devid;
 
        ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);
        if (ch7xxx == NULL)
@@ -204,7 +227,8 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo,
        if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
                goto out;
 
-       if (device != CH7xxx_DID) {
+       devid = ch7xxx_get_did(device);
+       if (!devid) {
                DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
                                "slave %d.\n",
                          vendor, adapter->name, dvo->slave_addr);
index e913d32..47d6c74 100644 (file)
@@ -61,11 +61,11 @@ static int i915_capabilities(struct seq_file *m, void *data)
 
        seq_printf(m, "gen: %d\n", info->gen);
        seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev));
-#define DEV_INFO_FLAG(x) seq_printf(m, #x ": %s\n", yesno(info->x))
-#define DEV_INFO_SEP ;
-       DEV_INFO_FLAGS;
-#undef DEV_INFO_FLAG
-#undef DEV_INFO_SEP
+#define PRINT_FLAG(x)  seq_printf(m, #x ": %s\n", yesno(info->x))
+#define SEP_SEMICOLON ;
+       DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_SEMICOLON);
+#undef PRINT_FLAG
+#undef SEP_SEMICOLON
 
        return 0;
 }
@@ -196,6 +196,32 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
        } \
 } while (0)
 
+struct file_stats {
+       int count;
+       size_t total, active, inactive, unbound;
+};
+
+static int per_file_stats(int id, void *ptr, void *data)
+{
+       struct drm_i915_gem_object *obj = ptr;
+       struct file_stats *stats = data;
+
+       stats->count++;
+       stats->total += obj->base.size;
+
+       if (obj->gtt_space) {
+               if (!list_empty(&obj->ring_list))
+                       stats->active += obj->base.size;
+               else
+                       stats->inactive += obj->base.size;
+       } else {
+               if (!list_empty(&obj->global_list))
+                       stats->unbound += obj->base.size;
+       }
+
+       return 0;
+}
+
 static int i915_gem_object_info(struct seq_file *m, void* data)
 {
        struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -204,6 +230,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
        u32 count, mappable_count, purgeable_count;
        size_t size, mappable_size, purgeable_size;
        struct drm_i915_gem_object *obj;
+       struct drm_file *file;
        int ret;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -215,7 +242,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
                   dev_priv->mm.object_memory);
 
        size = count = mappable_size = mappable_count = 0;
-       count_objects(&dev_priv->mm.bound_list, gtt_list);
+       count_objects(&dev_priv->mm.bound_list, global_list);
        seq_printf(m, "%u [%u] objects, %zu [%zu] bytes in gtt\n",
                   count, mappable_count, size, mappable_size);
 
@@ -230,7 +257,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
                   count, mappable_count, size, mappable_size);
 
        size = count = purgeable_size = purgeable_count = 0;
-       list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list) {
+       list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list) {
                size += obj->base.size, ++count;
                if (obj->madv == I915_MADV_DONTNEED)
                        purgeable_size += obj->base.size, ++purgeable_count;
@@ -238,7 +265,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
        seq_printf(m, "%u unbound objects, %zu bytes\n", count, size);
 
        size = count = mappable_size = mappable_count = 0;
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
                if (obj->fault_mappable) {
                        size += obj->gtt_space->size;
                        ++count;
@@ -263,6 +290,21 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
                   dev_priv->gtt.total,
                   dev_priv->gtt.mappable_end - dev_priv->gtt.start);
 
+       seq_printf(m, "\n");
+       list_for_each_entry_reverse(file, &dev->filelist, lhead) {
+               struct file_stats stats;
+
+               memset(&stats, 0, sizeof(stats));
+               idr_for_each(&file->object_idr, per_file_stats, &stats);
+               seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu unbound)\n",
+                          get_pid_task(file->pid, PIDTYPE_PID)->comm,
+                          stats.count,
+                          stats.total,
+                          stats.active,
+                          stats.inactive,
+                          stats.unbound);
+       }
+
        mutex_unlock(&dev->struct_mutex);
 
        return 0;
@@ -283,7 +325,7 @@ static int i915_gem_gtt_info(struct seq_file *m, void* data)
                return ret;
 
        total_obj_size = total_gtt_size = count = 0;
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
                if (list == PINNED_LIST && obj->pin_count == 0)
                        continue;
 
@@ -570,6 +612,7 @@ static const char *ring_str(int ring)
        case RCS: return "render";
        case VCS: return "bsd";
        case BCS: return "blt";
+       case VECS: return "vebox";
        default: return "";
        }
 }
@@ -604,73 +647,187 @@ static const char *purgeable_flag(int purgeable)
        return purgeable ? " purgeable" : "";
 }
 
-static void print_error_buffers(struct seq_file *m,
+static bool __i915_error_ok(struct drm_i915_error_state_buf *e)
+{
+
+       if (!e->err && WARN(e->bytes > (e->size - 1), "overflow")) {
+               e->err = -ENOSPC;
+               return false;
+       }
+
+       if (e->bytes == e->size - 1 || e->err)
+               return false;
+
+       return true;
+}
+
+static bool __i915_error_seek(struct drm_i915_error_state_buf *e,
+                             unsigned len)
+{
+       if (e->pos + len <= e->start) {
+               e->pos += len;
+               return false;
+       }
+
+       /* First vsnprintf needs to fit in its entirety for memmove */
+       if (len >= e->size) {
+               e->err = -EIO;
+               return false;
+       }
+
+       return true;
+}
+
+static void __i915_error_advance(struct drm_i915_error_state_buf *e,
+                                unsigned len)
+{
+       /* If this is first printf in this window, adjust it so that
+        * start position matches start of the buffer
+        */
+
+       if (e->pos < e->start) {
+               const size_t off = e->start - e->pos;
+
+               /* Should not happen but be paranoid */
+               if (off > len || e->bytes) {
+                       e->err = -EIO;
+                       return;
+               }
+
+               memmove(e->buf, e->buf + off, len - off);
+               e->bytes = len - off;
+               e->pos = e->start;
+               return;
+       }
+
+       e->bytes += len;
+       e->pos += len;
+}
+
+static void i915_error_vprintf(struct drm_i915_error_state_buf *e,
+                              const char *f, va_list args)
+{
+       unsigned len;
+
+       if (!__i915_error_ok(e))
+               return;
+
+       /* Seek the first printf which is hits start position */
+       if (e->pos < e->start) {
+               len = vsnprintf(NULL, 0, f, args);
+               if (!__i915_error_seek(e, len))
+                       return;
+       }
+
+       len = vsnprintf(e->buf + e->bytes, e->size - e->bytes, f, args);
+       if (len >= e->size - e->bytes)
+               len = e->size - e->bytes - 1;
+
+       __i915_error_advance(e, len);
+}
+
+static void i915_error_puts(struct drm_i915_error_state_buf *e,
+                           const char *str)
+{
+       unsigned len;
+
+       if (!__i915_error_ok(e))
+               return;
+
+       len = strlen(str);
+
+       /* Seek the first printf which is hits start position */
+       if (e->pos < e->start) {
+               if (!__i915_error_seek(e, len))
+                       return;
+       }
+
+       if (len >= e->size - e->bytes)
+               len = e->size - e->bytes - 1;
+       memcpy(e->buf + e->bytes, str, len);
+
+       __i915_error_advance(e, len);
+}
+
+void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...)
+{
+       va_list args;
+
+       va_start(args, f);
+       i915_error_vprintf(e, f, args);
+       va_end(args);
+}
+
+#define err_printf(e, ...) i915_error_printf(e, __VA_ARGS__)
+#define err_puts(e, s) i915_error_puts(e, s)
+
+static void print_error_buffers(struct drm_i915_error_state_buf *m,
                                const char *name,
                                struct drm_i915_error_buffer *err,
                                int count)
 {
-       seq_printf(m, "%s [%d]:\n", name, count);
+       err_printf(m, "%s [%d]:\n", name, count);
 
        while (count--) {
-               seq_printf(m, "  %08x %8u %02x %02x %x %x%s%s%s%s%s%s%s",
+               err_printf(m, "  %08x %8u %02x %02x %x %x",
                           err->gtt_offset,
                           err->size,
                           err->read_domains,
                           err->write_domain,
-                          err->rseqno, err->wseqno,
-                          pin_flag(err->pinned),
-                          tiling_flag(err->tiling),
-                          dirty_flag(err->dirty),
-                          purgeable_flag(err->purgeable),
-                          err->ring != -1 ? " " : "",
-                          ring_str(err->ring),
-                          cache_level_str(err->cache_level));
+                          err->rseqno, err->wseqno);
+               err_puts(m, pin_flag(err->pinned));
+               err_puts(m, tiling_flag(err->tiling));
+               err_puts(m, dirty_flag(err->dirty));
+               err_puts(m, purgeable_flag(err->purgeable));
+               err_puts(m, err->ring != -1 ? " " : "");
+               err_puts(m, ring_str(err->ring));
+               err_puts(m, cache_level_str(err->cache_level));
 
                if (err->name)
-                       seq_printf(m, " (name: %d)", err->name);
+                       err_printf(m, " (name: %d)", err->name);
                if (err->fence_reg != I915_FENCE_REG_NONE)
-                       seq_printf(m, " (fence: %d)", err->fence_reg);
+                       err_printf(m, " (fence: %d)", err->fence_reg);
 
-               seq_printf(m, "\n");
+               err_puts(m, "\n");
                err++;
        }
 }
 
-static void i915_ring_error_state(struct seq_file *m,
+static void i915_ring_error_state(struct drm_i915_error_state_buf *m,
                                  struct drm_device *dev,
                                  struct drm_i915_error_state *error,
                                  unsigned ring)
 {
        BUG_ON(ring >= I915_NUM_RINGS); /* shut up confused gcc */
-       seq_printf(m, "%s command stream:\n", ring_str(ring));
-       seq_printf(m, "  HEAD: 0x%08x\n", error->head[ring]);
-       seq_printf(m, "  TAIL: 0x%08x\n", error->tail[ring]);
-       seq_printf(m, "  CTL: 0x%08x\n", error->ctl[ring]);
-       seq_printf(m, "  ACTHD: 0x%08x\n", error->acthd[ring]);
-       seq_printf(m, "  IPEIR: 0x%08x\n", error->ipeir[ring]);
-       seq_printf(m, "  IPEHR: 0x%08x\n", error->ipehr[ring]);
-       seq_printf(m, "  INSTDONE: 0x%08x\n", error->instdone[ring]);
+       err_printf(m, "%s command stream:\n", ring_str(ring));
+       err_printf(m, "  HEAD: 0x%08x\n", error->head[ring]);
+       err_printf(m, "  TAIL: 0x%08x\n", error->tail[ring]);
+       err_printf(m, "  CTL: 0x%08x\n", error->ctl[ring]);
+       err_printf(m, "  ACTHD: 0x%08x\n", error->acthd[ring]);
+       err_printf(m, "  IPEIR: 0x%08x\n", error->ipeir[ring]);
+       err_printf(m, "  IPEHR: 0x%08x\n", error->ipehr[ring]);
+       err_printf(m, "  INSTDONE: 0x%08x\n", error->instdone[ring]);
        if (ring == RCS && INTEL_INFO(dev)->gen >= 4)
-               seq_printf(m, "  BBADDR: 0x%08llx\n", error->bbaddr);
+               err_printf(m, "  BBADDR: 0x%08llx\n", error->bbaddr);
 
        if (INTEL_INFO(dev)->gen >= 4)
-               seq_printf(m, "  INSTPS: 0x%08x\n", error->instps[ring]);
-       seq_printf(m, "  INSTPM: 0x%08x\n", error->instpm[ring]);
-       seq_printf(m, "  FADDR: 0x%08x\n", error->faddr[ring]);
+               err_printf(m, "  INSTPS: 0x%08x\n", error->instps[ring]);
+       err_printf(m, "  INSTPM: 0x%08x\n", error->instpm[ring]);
+       err_printf(m, "  FADDR: 0x%08x\n", error->faddr[ring]);
        if (INTEL_INFO(dev)->gen >= 6) {
-               seq_printf(m, "  RC PSMI: 0x%08x\n", error->rc_psmi[ring]);
-               seq_printf(m, "  FAULT_REG: 0x%08x\n", error->fault_reg[ring]);
-               seq_printf(m, "  SYNC_0: 0x%08x [last synced 0x%08x]\n",
+               err_printf(m, "  RC PSMI: 0x%08x\n", error->rc_psmi[ring]);
+               err_printf(m, "  FAULT_REG: 0x%08x\n", error->fault_reg[ring]);
+               err_printf(m, "  SYNC_0: 0x%08x [last synced 0x%08x]\n",
                           error->semaphore_mboxes[ring][0],
                           error->semaphore_seqno[ring][0]);
-               seq_printf(m, "  SYNC_1: 0x%08x [last synced 0x%08x]\n",
+               err_printf(m, "  SYNC_1: 0x%08x [last synced 0x%08x]\n",
                           error->semaphore_mboxes[ring][1],
                           error->semaphore_seqno[ring][1]);
        }
-       seq_printf(m, "  seqno: 0x%08x\n", error->seqno[ring]);
-       seq_printf(m, "  waiting: %s\n", yesno(error->waiting[ring]));
-       seq_printf(m, "  ring->head: 0x%08x\n", error->cpu_ring_head[ring]);
-       seq_printf(m, "  ring->tail: 0x%08x\n", error->cpu_ring_tail[ring]);
+       err_printf(m, "  seqno: 0x%08x\n", error->seqno[ring]);
+       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]);
 }
 
 struct i915_error_state_file_priv {
@@ -678,9 +835,11 @@ struct i915_error_state_file_priv {
        struct drm_i915_error_state *error;
 };
 
-static int i915_error_state(struct seq_file *m, void *unused)
+
+static int i915_error_state(struct i915_error_state_file_priv *error_priv,
+                           struct drm_i915_error_state_buf *m)
+
 {
-       struct i915_error_state_file_priv *error_priv = m->private;
        struct drm_device *dev = error_priv->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_error_state *error = error_priv->error;
@@ -688,34 +847,35 @@ static int i915_error_state(struct seq_file *m, void *unused)
        int i, j, page, offset, elt;
 
        if (!error) {
-               seq_printf(m, "no error state collected\n");
+               err_printf(m, "no error state collected\n");
                return 0;
        }
 
-       seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec,
+       err_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec,
                   error->time.tv_usec);
-       seq_printf(m, "Kernel: " UTS_RELEASE "\n");
-       seq_printf(m, "PCI ID: 0x%04x\n", dev->pci_device);
-       seq_printf(m, "EIR: 0x%08x\n", error->eir);
-       seq_printf(m, "IER: 0x%08x\n", error->ier);
-       seq_printf(m, "PGTBL_ER: 0x%08x\n", error->pgtbl_er);
-       seq_printf(m, "FORCEWAKE: 0x%08x\n", error->forcewake);
-       seq_printf(m, "DERRMR: 0x%08x\n", error->derrmr);
-       seq_printf(m, "CCID: 0x%08x\n", error->ccid);
+       err_printf(m, "Kernel: " UTS_RELEASE "\n");
+       err_printf(m, "PCI ID: 0x%04x\n", dev->pci_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);
 
        for (i = 0; i < dev_priv->num_fence_regs; i++)
-               seq_printf(m, "  fence[%d] = %08llx\n", i, error->fence[i]);
+               err_printf(m, "  fence[%d] = %08llx\n", i, error->fence[i]);
 
        for (i = 0; i < ARRAY_SIZE(error->extra_instdone); i++)
-               seq_printf(m, "  INSTDONE_%d: 0x%08x\n", i, error->extra_instdone[i]);
+               err_printf(m, "  INSTDONE_%d: 0x%08x\n", i,
+                          error->extra_instdone[i]);
 
        if (INTEL_INFO(dev)->gen >= 6) {
-               seq_printf(m, "ERROR: 0x%08x\n", error->error);
-               seq_printf(m, "DONE_REG: 0x%08x\n", error->done_reg);
+               err_printf(m, "ERROR: 0x%08x\n", error->error);
+               err_printf(m, "DONE_REG: 0x%08x\n", error->done_reg);
        }
 
        if (INTEL_INFO(dev)->gen == 7)
-               seq_printf(m, "ERR_INT: 0x%08x\n", error->err_int);
+               err_printf(m, "ERR_INT: 0x%08x\n", error->err_int);
 
        for_each_ring(ring, dev_priv, i)
                i915_ring_error_state(m, dev, error, i);
@@ -734,24 +894,25 @@ static int i915_error_state(struct seq_file *m, void *unused)
                struct drm_i915_error_object *obj;
 
                if ((obj = error->ring[i].batchbuffer)) {
-                       seq_printf(m, "%s --- gtt_offset = 0x%08x\n",
+                       err_printf(m, "%s --- gtt_offset = 0x%08x\n",
                                   dev_priv->ring[i].name,
                                   obj->gtt_offset);
                        offset = 0;
                        for (page = 0; page < obj->page_count; page++) {
                                for (elt = 0; elt < PAGE_SIZE/4; elt++) {
-                                       seq_printf(m, "%08x :  %08x\n", offset, obj->pages[page][elt]);
+                                       err_printf(m, "%08x :  %08x\n", offset,
+                                                  obj->pages[page][elt]);
                                        offset += 4;
                                }
                        }
                }
 
                if (error->ring[i].num_requests) {
-                       seq_printf(m, "%s --- %d requests\n",
+                       err_printf(m, "%s --- %d requests\n",
                                   dev_priv->ring[i].name,
                                   error->ring[i].num_requests);
                        for (j = 0; j < error->ring[i].num_requests; j++) {
-                               seq_printf(m, "  seqno 0x%08x, emitted %ld, tail 0x%08x\n",
+                               err_printf(m, "  seqno 0x%08x, emitted %ld, tail 0x%08x\n",
                                           error->ring[i].requests[j].seqno,
                                           error->ring[i].requests[j].jiffies,
                                           error->ring[i].requests[j].tail);
@@ -759,13 +920,13 @@ static int i915_error_state(struct seq_file *m, void *unused)
                }
 
                if ((obj = error->ring[i].ringbuffer)) {
-                       seq_printf(m, "%s --- ringbuffer = 0x%08x\n",
+                       err_printf(m, "%s --- ringbuffer = 0x%08x\n",
                                   dev_priv->ring[i].name,
                                   obj->gtt_offset);
                        offset = 0;
                        for (page = 0; page < obj->page_count; page++) {
                                for (elt = 0; elt < PAGE_SIZE/4; elt++) {
-                                       seq_printf(m, "%08x :  %08x\n",
+                                       err_printf(m, "%08x :  %08x\n",
                                                   offset,
                                                   obj->pages[page][elt]);
                                        offset += 4;
@@ -775,12 +936,12 @@ static int i915_error_state(struct seq_file *m, void *unused)
 
                obj = error->ring[i].ctx;
                if (obj) {
-                       seq_printf(m, "%s --- HW Context = 0x%08x\n",
+                       err_printf(m, "%s --- HW Context = 0x%08x\n",
                                   dev_priv->ring[i].name,
                                   obj->gtt_offset);
                        offset = 0;
                        for (elt = 0; elt < PAGE_SIZE/16; elt += 4) {
-                               seq_printf(m, "[%04x] %08x %08x %08x %08x\n",
+                               err_printf(m, "[%04x] %08x %08x %08x %08x\n",
                                           offset,
                                           obj->pages[0][elt],
                                           obj->pages[0][elt+1],
@@ -806,8 +967,7 @@ i915_error_state_write(struct file *filp,
                       size_t cnt,
                       loff_t *ppos)
 {
-       struct seq_file *m = filp->private_data;
-       struct i915_error_state_file_priv *error_priv = m->private;
+       struct i915_error_state_file_priv *error_priv = filp->private_data;
        struct drm_device *dev = error_priv->dev;
        int ret;
 
@@ -842,25 +1002,81 @@ static int i915_error_state_open(struct inode *inode, struct file *file)
                kref_get(&error_priv->error->ref);
        spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags);
 
-       return single_open(file, i915_error_state, error_priv);
+       file->private_data = error_priv;
+
+       return 0;
 }
 
 static int i915_error_state_release(struct inode *inode, struct file *file)
 {
-       struct seq_file *m = file->private_data;
-       struct i915_error_state_file_priv *error_priv = m->private;
+       struct i915_error_state_file_priv *error_priv = file->private_data;
 
        if (error_priv->error)
                kref_put(&error_priv->error->ref, i915_error_state_free);
        kfree(error_priv);
 
-       return single_release(inode, file);
+       return 0;
+}
+
+static ssize_t i915_error_state_read(struct file *file, char __user *userbuf,
+                                    size_t count, loff_t *pos)
+{
+       struct i915_error_state_file_priv *error_priv = file->private_data;
+       struct drm_i915_error_state_buf error_str;
+       loff_t tmp_pos = 0;
+       ssize_t ret_count = 0;
+       int ret = 0;
+
+       memset(&error_str, 0, sizeof(error_str));
+
+       /* We need to have enough room to store any i915_error_state printf
+        * so that we can move it to start position.
+        */
+       error_str.size = count + 1 > PAGE_SIZE ? count + 1 : PAGE_SIZE;
+       error_str.buf = kmalloc(error_str.size,
+                               GFP_TEMPORARY | __GFP_NORETRY | __GFP_NOWARN);
+
+       if (error_str.buf == NULL) {
+               error_str.size = PAGE_SIZE;
+               error_str.buf = kmalloc(error_str.size, GFP_TEMPORARY);
+       }
+
+       if (error_str.buf == NULL) {
+               error_str.size = 128;
+               error_str.buf = kmalloc(error_str.size, GFP_TEMPORARY);
+       }
+
+       if (error_str.buf == NULL)
+               return -ENOMEM;
+
+       error_str.start = *pos;
+
+       ret = i915_error_state(error_priv, &error_str);
+       if (ret)
+               goto out;
+
+       if (error_str.bytes == 0 && error_str.err) {
+               ret = error_str.err;
+               goto out;
+       }
+
+       ret_count = simple_read_from_buffer(userbuf, count, &tmp_pos,
+                                           error_str.buf,
+                                           error_str.bytes);
+
+       if (ret_count < 0)
+               ret = ret_count;
+       else
+               *pos = error_str.start + ret_count;
+out:
+       kfree(error_str.buf);
+       return ret ?: ret_count;
 }
 
 static const struct file_operations i915_error_state_fops = {
        .owner = THIS_MODULE,
        .open = i915_error_state_open,
-       .read = seq_read,
+       .read = i915_error_state_read,
        .write = i915_error_state_write,
        .llseek = default_llseek,
        .release = i915_error_state_release,
@@ -941,7 +1157,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
                           MEMSTAT_VID_SHIFT);
                seq_printf(m, "Current P-state: %d\n",
                           (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
-       } else if (IS_GEN6(dev) || IS_GEN7(dev)) {
+       } else if ((IS_GEN6(dev) || IS_GEN7(dev)) && !IS_VALLEYVIEW(dev)) {
                u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
                u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS);
                u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
@@ -1009,6 +1225,26 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
 
                seq_printf(m, "Max overclocked frequency: %dMHz\n",
                           dev_priv->rps.hw_max * GT_FREQUENCY_MULTIPLIER);
+       } else if (IS_VALLEYVIEW(dev)) {
+               u32 freq_sts, val;
+
+               mutex_lock(&dev_priv->rps.hw_lock);
+               freq_sts = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
+               seq_printf(m, "PUNIT_REG_GPU_FREQ_STS: 0x%08x\n", freq_sts);
+               seq_printf(m, "DDR freq: %d MHz\n", dev_priv->mem_freq);
+
+               val = vlv_punit_read(dev_priv, PUNIT_FUSE_BUS1);
+               seq_printf(m, "max GPU freq: %d MHz\n",
+                          vlv_gpu_freq(dev_priv->mem_freq, val));
+
+               val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM);
+               seq_printf(m, "min GPU freq: %d MHz\n",
+                          vlv_gpu_freq(dev_priv->mem_freq, val));
+
+               seq_printf(m, "current GPU freq: %d MHz\n",
+                          vlv_gpu_freq(dev_priv->mem_freq,
+                                       (freq_sts >> 8) & 0xff));
+               mutex_unlock(&dev_priv->rps.hw_lock);
        } else {
                seq_printf(m, "no P-state info available\n");
        }
@@ -1290,6 +1526,25 @@ static int i915_fbc_status(struct seq_file *m, void *unused)
        return 0;
 }
 
+static int i915_ips_status(struct seq_file *m, void *unused)
+{
+       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;
+
+       if (!HAS_IPS(dev)) {
+               seq_puts(m, "not supported\n");
+               return 0;
+       }
+
+       if (I915_READ(IPS_CTL) & IPS_ENABLE)
+               seq_puts(m, "enabled\n");
+       else
+               seq_puts(m, "disabled\n");
+
+       return 0;
+}
+
 static int i915_sr_status(struct seq_file *m, void *unused)
 {
        struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -1642,27 +1897,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",
-                  intel_dpio_read(dev_priv, _DPIO_DIV_A));
+                  vlv_dpio_read(dev_priv, _DPIO_DIV_A));
        seq_printf(m, "DPIO_DIV_B: 0x%08x\n",
-                  intel_dpio_read(dev_priv, _DPIO_DIV_B));
+                  vlv_dpio_read(dev_priv, _DPIO_DIV_B));
 
        seq_printf(m, "DPIO_REFSFR_A: 0x%08x\n",
-                  intel_dpio_read(dev_priv, _DPIO_REFSFR_A));
+                  vlv_dpio_read(dev_priv, _DPIO_REFSFR_A));
        seq_printf(m, "DPIO_REFSFR_B: 0x%08x\n",
-                  intel_dpio_read(dev_priv, _DPIO_REFSFR_B));
+                  vlv_dpio_read(dev_priv, _DPIO_REFSFR_B));
 
        seq_printf(m, "DPIO_CORE_CLK_A: 0x%08x\n",
-                  intel_dpio_read(dev_priv, _DPIO_CORE_CLK_A));
+                  vlv_dpio_read(dev_priv, _DPIO_CORE_CLK_A));
        seq_printf(m, "DPIO_CORE_CLK_B: 0x%08x\n",
-                  intel_dpio_read(dev_priv, _DPIO_CORE_CLK_B));
+                  vlv_dpio_read(dev_priv, _DPIO_CORE_CLK_B));
 
-       seq_printf(m, "DPIO_LFP_COEFF_A: 0x%08x\n",
-                  intel_dpio_read(dev_priv, _DPIO_LFP_COEFF_A));
-       seq_printf(m, "DPIO_LFP_COEFF_B: 0x%08x\n",
-                  intel_dpio_read(dev_priv, _DPIO_LFP_COEFF_B));
+       seq_printf(m, "DPIO_LPF_COEFF_A: 0x%08x\n",
+                  vlv_dpio_read(dev_priv, _DPIO_LPF_COEFF_A));
+       seq_printf(m, "DPIO_LPF_COEFF_B: 0x%08x\n",
+                  vlv_dpio_read(dev_priv, _DPIO_LPF_COEFF_B));
 
        seq_printf(m, "DPIO_FASTCLK_DISABLE: 0x%08x\n",
-                  intel_dpio_read(dev_priv, DPIO_FASTCLK_DISABLE));
+                  vlv_dpio_read(dev_priv, DPIO_FASTCLK_DISABLE));
 
        mutex_unlock(&dev_priv->dpio_lock);
 
@@ -1780,7 +2035,8 @@ i915_drop_caches_set(void *data, u64 val)
        }
 
        if (val & DROP_UNBOUND) {
-               list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list)
+               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)
@@ -1812,7 +2068,11 @@ i915_max_freq_get(void *data, u64 *val)
        if (ret)
                return ret;
 
-       *val = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER;
+       if (IS_VALLEYVIEW(dev))
+               *val = vlv_gpu_freq(dev_priv->mem_freq,
+                                   dev_priv->rps.max_delay);
+       else
+               *val = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER;
        mutex_unlock(&dev_priv->rps.hw_lock);
 
        return 0;
@@ -1837,9 +2097,16 @@ i915_max_freq_set(void *data, u64 val)
        /*
         * Turbo will still be enabled, but won't go above the set value.
         */
-       do_div(val, GT_FREQUENCY_MULTIPLIER);
-       dev_priv->rps.max_delay = val;
-       gen6_set_rps(dev, val);
+       if (IS_VALLEYVIEW(dev)) {
+               val = vlv_freq_opcode(dev_priv->mem_freq, val);
+               dev_priv->rps.max_delay = val;
+               gen6_set_rps(dev, val);
+       } else {
+               do_div(val, GT_FREQUENCY_MULTIPLIER);
+               dev_priv->rps.max_delay = val;
+               gen6_set_rps(dev, val);
+       }
+
        mutex_unlock(&dev_priv->rps.hw_lock);
 
        return 0;
@@ -1863,7 +2130,11 @@ i915_min_freq_get(void *data, u64 *val)
        if (ret)
                return ret;
 
-       *val = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER;
+       if (IS_VALLEYVIEW(dev))
+               *val = vlv_gpu_freq(dev_priv->mem_freq,
+                                   dev_priv->rps.min_delay);
+       else
+               *val = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER;
        mutex_unlock(&dev_priv->rps.hw_lock);
 
        return 0;
@@ -1888,9 +2159,15 @@ i915_min_freq_set(void *data, u64 val)
        /*
         * Turbo will still be enabled, but won't go below the set value.
         */
-       do_div(val, GT_FREQUENCY_MULTIPLIER);
-       dev_priv->rps.min_delay = val;
-       gen6_set_rps(dev, val);
+       if (IS_VALLEYVIEW(dev)) {
+               val = vlv_freq_opcode(dev_priv->mem_freq, val);
+               dev_priv->rps.min_delay = val;
+               valleyview_set_rps(dev, val);
+       } else {
+               do_div(val, GT_FREQUENCY_MULTIPLIER);
+               dev_priv->rps.min_delay = val;
+               gen6_set_rps(dev, val);
+       }
        mutex_unlock(&dev_priv->rps.hw_lock);
 
        return 0;
@@ -2057,6 +2334,7 @@ static struct drm_info_list i915_debugfs_list[] = {
        {"i915_gem_hws", i915_hws_info, 0, (void *)RCS},
        {"i915_gem_hws_blt", i915_hws_info, 0, (void *)BCS},
        {"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS},
+       {"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS},
        {"i915_rstdby_delays", i915_rstdby_delays, 0},
        {"i915_cur_delayinfo", i915_cur_delayinfo, 0},
        {"i915_delayfreq_table", i915_delayfreq_table, 0},
@@ -2066,6 +2344,7 @@ static struct drm_info_list i915_debugfs_list[] = {
        {"i915_ring_freq_table", i915_ring_freq_table, 0},
        {"i915_gfxec", i915_gfxec, 0},
        {"i915_fbc_status", i915_fbc_status, 0},
+       {"i915_ips_status", i915_ips_status, 0},
        {"i915_sr_status", i915_sr_status, 0},
        {"i915_opregion", i915_opregion, 0},
        {"i915_gem_framebuffer", i915_gem_framebuffer_info, 0},
index 3b315ba..adb319b 100644 (file)
@@ -42,7 +42,6 @@
 #include <linux/vga_switcheroo.h>
 #include <linux/slab.h>
 #include <acpi/video.h>
-#include <asm/pat.h>
 
 #define LP_RING(d) (&((struct drm_i915_private *)(d))->ring[RCS])
 
@@ -956,6 +955,9 @@ static int i915_getparam(struct drm_device *dev, void *data,
        case I915_PARAM_HAS_BLT:
                value = intel_ring_initialized(&dev_priv->ring[BCS]);
                break;
+       case I915_PARAM_HAS_VEBOX:
+               value = intel_ring_initialized(&dev_priv->ring[VECS]);
+               break;
        case I915_PARAM_HAS_RELAXED_FENCING:
                value = 1;
                break;
@@ -999,8 +1001,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
                value = 1;
                break;
        default:
-               DRM_DEBUG_DRIVER("Unknown parameter %d\n",
-                                param->param);
+               DRM_DEBUG("Unknown parameter %d\n", param->param);
                return -EINVAL;
        }
 
@@ -1359,8 +1360,10 @@ static int i915_load_modeset_init(struct drm_device *dev)
 cleanup_gem:
        mutex_lock(&dev->struct_mutex);
        i915_gem_cleanup_ringbuffer(dev);
+       i915_gem_context_fini(dev);
        mutex_unlock(&dev->struct_mutex);
        i915_gem_cleanup_aliasing_ppgtt(dev);
+       drm_mm_takedown(&dev_priv->mm.gtt_space);
 cleanup_irq:
        drm_irq_uninstall(dev);
 cleanup_gem_stolen:
@@ -1397,29 +1400,6 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master)
        master->driver_priv = NULL;
 }
 
-static void
-i915_mtrr_setup(struct drm_i915_private *dev_priv, unsigned long base,
-               unsigned long size)
-{
-       dev_priv->mm.gtt_mtrr = -1;
-
-#if defined(CONFIG_X86_PAT)
-       if (cpu_has_pat)
-               return;
-#endif
-
-       /* Set up a WC MTRR for non-PAT systems.  This is more common than
-        * one would think, because the kernel disables PAT on first
-        * generation Core chips because WC PAT gets overridden by a UC
-        * MTRR if present.  Even if a UC MTRR isn't present.
-        */
-       dev_priv->mm.gtt_mtrr = mtrr_add(base, size, MTRR_TYPE_WRCOMB, 1);
-       if (dev_priv->mm.gtt_mtrr < 0) {
-               DRM_INFO("MTRR allocation failed.  Graphics "
-                        "performance may suffer.\n");
-       }
-}
-
 static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
 {
        struct apertures_struct *ap;
@@ -1431,7 +1411,7 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
                return;
 
        ap->ranges[0].base = dev_priv->gtt.mappable_base;
-       ap->ranges[0].size = dev_priv->gtt.mappable_end - dev_priv->gtt.start;
+       ap->ranges[0].size = dev_priv->gtt.mappable_end;
 
        primary =
                pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
@@ -1445,15 +1425,19 @@ static void i915_dump_device_info(struct drm_i915_private *dev_priv)
 {
        const struct intel_device_info *info = dev_priv->info;
 
-#define DEV_INFO_FLAG(name) info->name ? #name "," : ""
-#define DEV_INFO_SEP ,
+#define PRINT_S(name) "%s"
+#define SEP_EMPTY
+#define PRINT_FLAG(name) info->name ? #name "," : ""
+#define SEP_COMMA ,
        DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x flags="
-                        "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+                        DEV_INFO_FOR_EACH_FLAG(PRINT_S, SEP_EMPTY),
                         info->gen,
                         dev_priv->dev->pdev->device,
-                        DEV_INFO_FLAGS);
-#undef DEV_INFO_FLAG
-#undef DEV_INFO_SEP
+                        DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_COMMA));
+#undef PRINT_S
+#undef SEP_EMPTY
+#undef PRINT_FLAG
+#undef SEP_COMMA
 }
 
 /**
@@ -1468,7 +1452,7 @@ static void intel_early_sanitize_regs(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (IS_HASWELL(dev))
+       if (HAS_FPGA_DBG_UNCLAIMED(dev))
                I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
 }
 
@@ -1574,8 +1558,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                goto out_rmmap;
        }
 
-       i915_mtrr_setup(dev_priv, dev_priv->gtt.mappable_base,
-                       aperture_size);
+       dev_priv->mm.gtt_mtrr = arch_phys_wc_add(dev_priv->gtt.mappable_base,
+                                                aperture_size);
 
        /* The i915 workqueue is primarily used for batched retirement of
         * requests (and thus managing bo) once the task has been completed
@@ -1629,6 +1613,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        spin_lock_init(&dev_priv->irq_lock);
        spin_lock_init(&dev_priv->gpu_error.lock);
        spin_lock_init(&dev_priv->rps.lock);
+       spin_lock_init(&dev_priv->backlight.lock);
        mutex_init(&dev_priv->dpio_lock);
 
        mutex_init(&dev_priv->rps.hw_lock);
@@ -1647,6 +1632,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        /* Start out suspended */
        dev_priv->mm.suspended = 1;
 
+       if (HAS_POWER_WELL(dev))
+               i915_init_power_well(dev);
+
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
                ret = i915_load_modeset_init(dev);
                if (ret < 0) {
@@ -1679,12 +1667,7 @@ out_gem_unload:
        intel_teardown_mchbar(dev);
        destroy_workqueue(dev_priv->wq);
 out_mtrrfree:
-       if (dev_priv->mm.gtt_mtrr >= 0) {
-               mtrr_del(dev_priv->mm.gtt_mtrr,
-                        dev_priv->gtt.mappable_base,
-                        aperture_size);
-               dev_priv->mm.gtt_mtrr = -1;
-       }
+       arch_phys_wc_del(dev_priv->mm.gtt_mtrr);
        io_mapping_free(dev_priv->gtt.mappable);
        dev_priv->gtt.gtt_remove(dev);
 out_rmmap:
@@ -1703,6 +1686,9 @@ int i915_driver_unload(struct drm_device *dev)
 
        intel_gpu_ips_teardown();
 
+       if (HAS_POWER_WELL(dev))
+               i915_remove_power_well(dev);
+
        i915_teardown_sysfs(dev);
 
        if (dev_priv->mm.inactive_shrinker.shrink)
@@ -1719,12 +1705,7 @@ int i915_driver_unload(struct drm_device *dev)
        cancel_delayed_work_sync(&dev_priv->mm.retire_work);
 
        io_mapping_free(dev_priv->gtt.mappable);
-       if (dev_priv->mm.gtt_mtrr >= 0) {
-               mtrr_del(dev_priv->mm.gtt_mtrr,
-                        dev_priv->gtt.mappable_base,
-                        dev_priv->gtt.mappable_end);
-               dev_priv->mm.gtt_mtrr = -1;
-       }
+       arch_phys_wc_del(dev_priv->mm.gtt_mtrr);
 
        acpi_video_unregister();
 
@@ -1737,10 +1718,10 @@ int i915_driver_unload(struct drm_device *dev)
                 * free the memory space allocated for the child device
                 * config parsed from VBT
                 */
-               if (dev_priv->child_dev && dev_priv->child_dev_num) {
-                       kfree(dev_priv->child_dev);
-                       dev_priv->child_dev = NULL;
-                       dev_priv->child_dev_num = 0;
+               if (dev_priv->vbt.child_dev && dev_priv->vbt.child_dev_num) {
+                       kfree(dev_priv->vbt.child_dev);
+                       dev_priv->vbt.child_dev = NULL;
+                       dev_priv->vbt.child_dev_num = 0;
                }
 
                vga_switcheroo_unregister_client(dev->pdev);
@@ -1773,6 +1754,7 @@ int i915_driver_unload(struct drm_device *dev)
                        i915_free_hws(dev);
        }
 
+       drm_mm_takedown(&dev_priv->mm.gtt_space);
        if (dev_priv->regs != NULL)
                pci_iounmap(dev->pdev, dev_priv->regs);
 
@@ -1782,6 +1764,8 @@ int i915_driver_unload(struct drm_device *dev)
        destroy_workqueue(dev_priv->wq);
        pm_qos_remove_request(&dev_priv->pm_qos);
 
+       dev_priv->gtt.gtt_remove(dev);
+
        if (dev_priv->slab)
                kmem_cache_destroy(dev_priv->slab);
 
@@ -1796,7 +1780,7 @@ 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 = kmalloc(sizeof(*file_priv), GFP_KERNEL);
+       file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
        if (!file_priv)
                return -ENOMEM;
 
index a2e4953..062cbda 100644 (file)
@@ -128,6 +128,10 @@ module_param_named(disable_power_well, i915_disable_power_well, int, 0600);
 MODULE_PARM_DESC(disable_power_well,
                 "Disable the power well when possible (default: false)");
 
+int i915_enable_ips __read_mostly = 1;
+module_param_named(enable_ips, i915_enable_ips, int, 0600);
+MODULE_PARM_DESC(enable_ips, "Enable IPS (default: true)");
+
 static struct drm_driver driver;
 extern int intel_agp_enabled;
 
@@ -280,6 +284,7 @@ static const struct intel_device_info intel_ivybridge_m_info = {
        GEN7_FEATURES,
        .is_ivybridge = 1,
        .is_mobile = 1,
+       .has_fbc = 1,
 };
 
 static const struct intel_device_info intel_ivybridge_q_info = {
@@ -308,12 +313,19 @@ static const struct intel_device_info intel_valleyview_d_info = {
 static const struct intel_device_info intel_haswell_d_info = {
        GEN7_FEATURES,
        .is_haswell = 1,
+       .has_ddi = 1,
+       .has_fpga_dbg = 1,
+       .has_vebox_ring = 1,
 };
 
 static const struct intel_device_info intel_haswell_m_info = {
        GEN7_FEATURES,
        .is_haswell = 1,
        .is_mobile = 1,
+       .has_ddi = 1,
+       .has_fpga_dbg = 1,
+       .has_fbc = 1,
+       .has_vebox_ring = 1,
 };
 
 static const struct pci_device_id pciidlist[] = {              /* aka */
@@ -445,7 +457,6 @@ void intel_detect_pch(struct drm_device *dev)
         */
        if (INTEL_INFO(dev)->num_pipes == 0) {
                dev_priv->pch_type = PCH_NOP;
-               dev_priv->num_pch_pll = 0;
                return;
        }
 
@@ -454,9 +465,15 @@ void intel_detect_pch(struct drm_device *dev)
         * make graphics device passthrough work easy for VMM, that only
         * need to expose ISA bridge to let driver know the real hardware
         * underneath. This is a requirement from virtualization team.
+        *
+        * In some virtualized environments (e.g. XEN), there is irrelevant
+        * ISA bridge in the system. To work reliably, we should scan trhough
+        * all the ISA bridge devices and check for the first match, instead
+        * of only checking the first one.
         */
        pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, NULL);
-       if (pch) {
+       while (pch) {
+               struct pci_dev *curr = pch;
                if (pch->vendor == PCI_VENDOR_ID_INTEL) {
                        unsigned short id;
                        id = pch->device & INTEL_PCH_DEVICE_ID_MASK;
@@ -464,37 +481,39 @@ void intel_detect_pch(struct drm_device *dev)
 
                        if (id == INTEL_PCH_IBX_DEVICE_ID_TYPE) {
                                dev_priv->pch_type = PCH_IBX;
-                               dev_priv->num_pch_pll = 2;
                                DRM_DEBUG_KMS("Found Ibex Peak PCH\n");
                                WARN_ON(!IS_GEN5(dev));
                        } else if (id == INTEL_PCH_CPT_DEVICE_ID_TYPE) {
                                dev_priv->pch_type = PCH_CPT;
-                               dev_priv->num_pch_pll = 2;
                                DRM_DEBUG_KMS("Found CougarPoint PCH\n");
                                WARN_ON(!(IS_GEN6(dev) || IS_IVYBRIDGE(dev)));
                        } else if (id == INTEL_PCH_PPT_DEVICE_ID_TYPE) {
                                /* PantherPoint is CPT compatible */
                                dev_priv->pch_type = PCH_CPT;
-                               dev_priv->num_pch_pll = 2;
                                DRM_DEBUG_KMS("Found PatherPoint 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;
-                               dev_priv->num_pch_pll = 0;
                                DRM_DEBUG_KMS("Found LynxPoint PCH\n");
                                WARN_ON(!IS_HASWELL(dev));
                                WARN_ON(IS_ULT(dev));
                        } else if (id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) {
                                dev_priv->pch_type = PCH_LPT;
-                               dev_priv->num_pch_pll = 0;
                                DRM_DEBUG_KMS("Found LynxPoint LP PCH\n");
                                WARN_ON(!IS_HASWELL(dev));
                                WARN_ON(!IS_ULT(dev));
+                       } else {
+                               goto check_next;
                        }
-                       BUG_ON(dev_priv->num_pch_pll > I915_NUM_PLLS);
+                       pci_dev_put(pch);
+                       break;
                }
-               pci_dev_put(pch);
+check_next:
+               pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, curr);
+               pci_dev_put(curr);
        }
+       if (!pch)
+               DRM_DEBUG_KMS("No PCH found?\n");
 }
 
 bool i915_semaphore_is_enabled(struct drm_device *dev)
@@ -549,6 +568,8 @@ static int i915_drm_freeze(struct drm_device *dev)
                 */
                list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
                        dev_priv->display.crtc_disable(crtc);
+
+               intel_modeset_suspend_hw(dev);
        }
 
        i915_save_state(dev);
@@ -556,7 +577,7 @@ static int i915_drm_freeze(struct drm_device *dev)
        intel_opregion_fini(dev);
 
        console_lock();
-       intel_fbdev_set_suspend(dev, 1);
+       intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED);
        console_unlock();
 
        return 0;
@@ -600,7 +621,7 @@ void intel_console_resume(struct work_struct *work)
        struct drm_device *dev = dev_priv->dev;
 
        console_lock();
-       intel_fbdev_set_suspend(dev, 0);
+       intel_fbdev_set_suspend(dev, FBINFO_STATE_RUNNING);
        console_unlock();
 }
 
@@ -669,7 +690,7 @@ static int __i915_drm_thaw(struct drm_device *dev)
         * path of resume if possible.
         */
        if (console_trylock()) {
-               intel_fbdev_set_suspend(dev, 0);
+               intel_fbdev_set_suspend(dev, FBINFO_STATE_RUNNING);
                console_unlock();
        } else {
                schedule_work(&dev_priv->console_resume_work);
@@ -855,37 +876,14 @@ static int gen6_do_reset(struct drm_device *dev)
 
 int intel_gpu_reset(struct drm_device *dev)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       int ret = -ENODEV;
-
        switch (INTEL_INFO(dev)->gen) {
        case 7:
-       case 6:
-               ret = gen6_do_reset(dev);
-               break;
-       case 5:
-               ret = ironlake_do_reset(dev);
-               break;
-       case 4:
-               ret = i965_do_reset(dev);
-               break;
-       case 2:
-               ret = i8xx_do_reset(dev);
-               break;
-       }
-
-       /* Also reset the gpu hangman. */
-       if (dev_priv->gpu_error.stop_rings) {
-               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;
-               }
+       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;
        }
-
-       return ret;
 }
 
 /**
@@ -906,6 +904,7 @@ int intel_gpu_reset(struct drm_device *dev)
 int i915_reset(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
+       bool simulated;
        int ret;
 
        if (!i915_try_reset)
@@ -915,13 +914,26 @@ int i915_reset(struct drm_device *dev)
 
        i915_gem_reset(dev);
 
-       ret = -ENODEV;
-       if (get_seconds() - dev_priv->gpu_error.last_reset < 5)
+       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");
-       else
+               ret = -ENODEV;
+       } else {
                ret = intel_gpu_reset(dev);
 
-       dev_priv->gpu_error.last_reset = get_seconds();
+               /* 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();
+       }
        if (ret) {
                DRM_ERROR("Failed to reset chip.\n");
                mutex_unlock(&dev->struct_mutex);
@@ -984,12 +996,6 @@ 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 (intel_info->is_valleyview)
-               if(!i915_preliminary_hw_support) {
-                       DRM_ERROR("Preliminary hardware support disabled\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
@@ -1218,16 +1224,16 @@ MODULE_LICENSE("GPL and additional rights");
 static void
 ilk_dummy_write(struct drm_i915_private *dev_priv)
 {
-       /* WaIssueDummyWriteToWakeupFromRC6: Issue a dummy write to wake up the
-        * chip from rc6 before touching it for real. MI_MODE is masked, hence
-        * harmless to write 0 into. */
+       /* WaIssueDummyWriteToWakeupFromRC6:ilk Issue a dummy write to wake up
+        * the chip from rc6 before touching it for real. MI_MODE is masked,
+        * hence harmless to write 0 into. */
        I915_WRITE_NOTRACE(MI_MODE, 0);
 }
 
 static void
 hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg)
 {
-       if (IS_HASWELL(dev_priv->dev) &&
+       if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) &&
            (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
                DRM_ERROR("Unknown unclaimed register before writing to %x\n",
                          reg);
@@ -1238,7 +1244,7 @@ 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 (IS_HASWELL(dev_priv->dev) &&
+       if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) &&
            (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
                DRM_ERROR("Unclaimed write to %x\n", reg);
                I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
index 9669a0b..a416645 100644 (file)
@@ -76,6 +76,8 @@ enum plane {
 };
 #define plane_name(p) ((p) + 'A')
 
+#define sprite_name(p, s) ((p) * dev_priv->num_plane + (s) + 'A')
+
 enum port {
        PORT_A = 0,
        PORT_B,
@@ -86,6 +88,24 @@ enum port {
 };
 #define port_name(p) ((p) + 'A')
 
+enum intel_display_power_domain {
+       POWER_DOMAIN_PIPE_A,
+       POWER_DOMAIN_PIPE_B,
+       POWER_DOMAIN_PIPE_C,
+       POWER_DOMAIN_PIPE_A_PANEL_FITTER,
+       POWER_DOMAIN_PIPE_B_PANEL_FITTER,
+       POWER_DOMAIN_PIPE_C_PANEL_FITTER,
+       POWER_DOMAIN_TRANSCODER_A,
+       POWER_DOMAIN_TRANSCODER_B,
+       POWER_DOMAIN_TRANSCODER_C,
+       POWER_DOMAIN_TRANSCODER_EDP = POWER_DOMAIN_TRANSCODER_A + 0xF,
+};
+
+#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)
+
 enum hpd_pin {
        HPD_NONE = 0,
        HPD_PORT_A = HPD_NONE, /* PORT_A is internal */
@@ -112,15 +132,38 @@ enum hpd_pin {
        list_for_each_entry((intel_encoder), &(dev)->mode_config.encoder_list, base.head) \
                if ((intel_encoder)->base.crtc == (__crtc))
 
-struct intel_pch_pll {
+struct drm_i915_private;
+
+enum intel_dpll_id {
+       DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */
+       /* real shared dpll ids must be >= 0 */
+       DPLL_ID_PCH_PLL_A,
+       DPLL_ID_PCH_PLL_B,
+};
+#define I915_NUM_PLLS 2
+
+struct intel_dpll_hw_state {
+       uint32_t dpll;
+       uint32_t fp0;
+       uint32_t fp1;
+};
+
+struct intel_shared_dpll {
        int refcount; /* count of number of CRTCs sharing this PLL */
        int active; /* count of number of active CRTCs (i.e. DPMS on) */
        bool on; /* is the PLL actually active? Disabled during modeset */
-       int pll_reg;
-       int fp0_reg;
-       int fp1_reg;
+       const char *name;
+       /* should match the index in the dev_priv->shared_dplls array */
+       enum intel_dpll_id id;
+       struct intel_dpll_hw_state hw_state;
+       void (*enable)(struct drm_i915_private *dev_priv,
+                      struct intel_shared_dpll *pll);
+       void (*disable)(struct drm_i915_private *dev_priv,
+                       struct intel_shared_dpll *pll);
+       bool (*get_hw_state)(struct drm_i915_private *dev_priv,
+                            struct intel_shared_dpll *pll,
+                            struct intel_dpll_hw_state *hw_state);
 };
-#define I915_NUM_PLLS 2
 
 /* Used by dp and fdi links */
 struct intel_link_m_n {
@@ -175,7 +218,6 @@ struct opregion_header;
 struct opregion_acpi;
 struct opregion_swsci;
 struct opregion_asle;
-struct drm_i915_private;
 
 struct intel_opregion {
        struct opregion_header __iomem *header;
@@ -286,6 +328,8 @@ struct drm_i915_error_state {
 
 struct intel_crtc_config;
 struct intel_crtc;
+struct intel_limit;
+struct dpll;
 
 struct drm_i915_display_funcs {
        bool (*fbc_enabled)(struct drm_device *dev);
@@ -293,11 +337,28 @@ struct drm_i915_display_funcs {
        void (*disable_fbc)(struct drm_device *dev);
        int (*get_display_clock_speed)(struct drm_device *dev);
        int (*get_fifo_size)(struct drm_device *dev, int plane);
+       /**
+        * find_dpll() - Find the best values for the PLL
+        * @limit: limits for the PLL
+        * @crtc: current CRTC
+        * @target: target frequency in kHz
+        * @refclk: reference clock frequency in kHz
+        * @match_clock: if provided, @best_clock P divider must
+        *               match the P divider from @match_clock
+        *               used for LVDS downclocking
+        * @best_clock: best PLL values found
+        *
+        * Returns true on success, false on failure.
+        */
+       bool (*find_dpll)(const struct intel_limit *limit,
+                         struct drm_crtc *crtc,
+                         int target, int refclk,
+                         struct dpll *match_clock,
+                         struct dpll *best_clock);
        void (*update_wm)(struct drm_device *dev);
        void (*update_sprite_wm)(struct drm_device *dev, int pipe,
-                                uint32_t sprite_width, int pixel_size);
-       void (*update_linetime_wm)(struct drm_device *dev, int pipe,
-                                struct drm_display_mode *mode);
+                                uint32_t sprite_width, int pixel_size,
+                                bool enable);
        void (*modeset_global_resources)(struct drm_device *dev);
        /* Returns the active state of the crtc, and if the crtc is active,
         * fills out the pipe-config with the hw state. */
@@ -331,68 +392,56 @@ struct drm_i915_gt_funcs {
        void (*force_wake_put)(struct drm_i915_private *dev_priv);
 };
 
-#define DEV_INFO_FLAGS \
-       DEV_INFO_FLAG(is_mobile) DEV_INFO_SEP \
-       DEV_INFO_FLAG(is_i85x) DEV_INFO_SEP \
-       DEV_INFO_FLAG(is_i915g) DEV_INFO_SEP \
-       DEV_INFO_FLAG(is_i945gm) DEV_INFO_SEP \
-       DEV_INFO_FLAG(is_g33) DEV_INFO_SEP \
-       DEV_INFO_FLAG(need_gfx_hws) DEV_INFO_SEP \
-       DEV_INFO_FLAG(is_g4x) DEV_INFO_SEP \
-       DEV_INFO_FLAG(is_pineview) DEV_INFO_SEP \
-       DEV_INFO_FLAG(is_broadwater) DEV_INFO_SEP \
-       DEV_INFO_FLAG(is_crestline) DEV_INFO_SEP \
-       DEV_INFO_FLAG(is_ivybridge) DEV_INFO_SEP \
-       DEV_INFO_FLAG(is_valleyview) DEV_INFO_SEP \
-       DEV_INFO_FLAG(is_haswell) DEV_INFO_SEP \
-       DEV_INFO_FLAG(has_force_wake) DEV_INFO_SEP \
-       DEV_INFO_FLAG(has_fbc) DEV_INFO_SEP \
-       DEV_INFO_FLAG(has_pipe_cxsr) DEV_INFO_SEP \
-       DEV_INFO_FLAG(has_hotplug) DEV_INFO_SEP \
-       DEV_INFO_FLAG(cursor_needs_physical) DEV_INFO_SEP \
-       DEV_INFO_FLAG(has_overlay) DEV_INFO_SEP \
-       DEV_INFO_FLAG(overlay_needs_physical) DEV_INFO_SEP \
-       DEV_INFO_FLAG(supports_tv) DEV_INFO_SEP \
-       DEV_INFO_FLAG(has_bsd_ring) DEV_INFO_SEP \
-       DEV_INFO_FLAG(has_blt_ring) DEV_INFO_SEP \
-       DEV_INFO_FLAG(has_llc)
+#define DEV_INFO_FOR_EACH_FLAG(func, sep) \
+       func(is_mobile) sep \
+       func(is_i85x) sep \
+       func(is_i915g) sep \
+       func(is_i945gm) sep \
+       func(is_g33) sep \
+       func(need_gfx_hws) sep \
+       func(is_g4x) sep \
+       func(is_pineview) sep \
+       func(is_broadwater) sep \
+       func(is_crestline) sep \
+       func(is_ivybridge) sep \
+       func(is_valleyview) sep \
+       func(is_haswell) sep \
+       func(has_force_wake) sep \
+       func(has_fbc) sep \
+       func(has_pipe_cxsr) sep \
+       func(has_hotplug) sep \
+       func(cursor_needs_physical) sep \
+       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)
+
+#define DEFINE_FLAG(name) u8 name:1
+#define SEP_SEMICOLON ;
 
 struct intel_device_info {
        u32 display_mmio_offset;
        u8 num_pipes:3;
        u8 gen;
-       u8 is_mobile:1;
-       u8 is_i85x:1;
-       u8 is_i915g:1;
-       u8 is_i945gm:1;
-       u8 is_g33:1;
-       u8 need_gfx_hws:1;
-       u8 is_g4x:1;
-       u8 is_pineview:1;
-       u8 is_broadwater:1;
-       u8 is_crestline:1;
-       u8 is_ivybridge:1;
-       u8 is_valleyview:1;
-       u8 has_force_wake:1;
-       u8 is_haswell:1;
-       u8 has_fbc:1;
-       u8 has_pipe_cxsr:1;
-       u8 has_hotplug:1;
-       u8 cursor_needs_physical:1;
-       u8 has_overlay:1;
-       u8 overlay_needs_physical:1;
-       u8 supports_tv:1;
-       u8 has_bsd_ring:1;
-       u8 has_blt_ring:1;
-       u8 has_llc:1;
+       DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG, SEP_SEMICOLON);
 };
 
+#undef DEFINE_FLAG
+#undef SEP_SEMICOLON
+
 enum i915_cache_level {
        I915_CACHE_NONE = 0,
        I915_CACHE_LLC,
        I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */
 };
 
+typedef uint32_t gen6_gtt_pte_t;
+
 /* The Graphics Translation Table is the way in which GEN hardware translates a
  * Graphics Virtual Address into a Physical Address. In addition to the normal
  * collateral associated with any va->pa translations GEN hardware also has a
@@ -428,6 +477,9 @@ struct i915_gtt {
                                   struct sg_table *st,
                                   unsigned int pg_start,
                                   enum i915_cache_level cache_level);
+       gen6_gtt_pte_t (*pte_encode)(struct drm_device *dev,
+                                    dma_addr_t addr,
+                                    enum i915_cache_level level);
 };
 #define gtt_total_entries(gtt) ((gtt).total >> PAGE_SHIFT)
 
@@ -449,19 +501,31 @@ struct i915_hw_ppgtt {
                               struct sg_table *st,
                               unsigned int pg_start,
                               enum i915_cache_level cache_level);
+       gen6_gtt_pte_t (*pte_encode)(struct drm_device *dev,
+                                    dma_addr_t addr,
+                                    enum i915_cache_level level);
        int (*enable)(struct drm_device *dev);
        void (*cleanup)(struct i915_hw_ppgtt *ppgtt);
 };
 
+struct i915_ctx_hang_stats {
+       /* This context had batch pending when hang was declared */
+       unsigned batch_pending;
+
+       /* This context had batch active when hang was declared */
+       unsigned batch_active;
+};
 
 /* This must match up with the value previously used for execbuf2.rsvd1. */
 #define DEFAULT_CONTEXT_ID 0
 struct i915_hw_context {
+       struct kref ref;
        int id;
        bool is_initialized;
        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;
 };
 
 enum no_fbc_reason {
@@ -658,6 +722,7 @@ struct i915_suspend_saved_registers {
 
 struct intel_gen6_power_mgmt {
        struct work_struct work;
+       struct delayed_work vlv_work;
        u32 pm_iir;
        /* lock - irqsave spinlock that protectects the work_struct and
         * pm_iir. */
@@ -668,6 +733,7 @@ struct intel_gen6_power_mgmt {
        u8 cur_delay;
        u8 min_delay;
        u8 max_delay;
+       u8 rpe_delay;
        u8 hw_max;
 
        struct delayed_work delayed_resume_work;
@@ -704,6 +770,15 @@ struct intel_ilk_power_mgmt {
        struct drm_i915_gem_object *renderctx;
 };
 
+/* 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;
+};
+
 struct i915_dri1_state {
        unsigned allow_batchbuffer : 1;
        u32 __iomem *gfx_hws_cpu_addr;
@@ -812,14 +887,20 @@ struct i915_gem_mm {
        u32 object_count;
 };
 
+struct drm_i915_error_state_buf {
+       unsigned bytes;
+       unsigned size;
+       int err;
+       u8 *buf;
+       loff_t start;
+       loff_t pos;
+};
+
 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)
        struct timer_list hangcheck_timer;
-       int hangcheck_count;
-       uint32_t last_acthd[I915_NUM_RINGS];
-       uint32_t prev_instdone[I915_NUM_INSTDONE_REG];
 
        /* For reset and error_state handling. */
        spinlock_t lock;
@@ -875,6 +956,37 @@ enum modeset_restore {
        MODESET_SUSPENDED,
 };
 
+struct intel_vbt_data {
+       struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
+       struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
+
+       /* Feature bits */
+       unsigned int int_tv_support:1;
+       unsigned int lvds_dither:1;
+       unsigned int lvds_vbt:1;
+       unsigned int int_crt_support:1;
+       unsigned int lvds_use_ssc:1;
+       unsigned int display_clock_mode:1;
+       unsigned int fdi_rx_polarity_inverted:1;
+       int lvds_ssc_freq;
+       unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */
+
+       /* eDP */
+       int edp_rate;
+       int edp_lanes;
+       int edp_preemphasis;
+       int edp_vswing;
+       bool edp_initialized;
+       bool edp_support;
+       int edp_bpp;
+       struct edp_power_seq edp_pps;
+
+       int crt_ddc_pin;
+
+       int child_dev_num;
+       struct child_device_config *child_dev;
+};
+
 typedef struct drm_i915_private {
        struct drm_device *dev;
        struct kmem_cache *slab;
@@ -941,9 +1053,9 @@ typedef struct drm_i915_private {
                        HPD_MARK_DISABLED = 2
                } hpd_mark;
        } hpd_stats[HPD_NUM_PINS];
+       u32 hpd_event_bits;
        struct timer_list hotplug_reenable_timer;
 
-       int num_pch_pll;
        int num_plane;
 
        unsigned long cfb_size;
@@ -953,6 +1065,7 @@ typedef struct drm_i915_private {
        struct intel_fbc_work *fbc_work;
 
        struct intel_opregion opregion;
+       struct intel_vbt_data vbt;
 
        /* overlay */
        struct intel_overlay *overlay;
@@ -962,37 +1075,15 @@ typedef struct drm_i915_private {
        struct {
                int level;
                bool enabled;
+               spinlock_t lock; /* bl registers and the above bl fields */
                struct backlight_device *device;
        } backlight;
 
        /* LVDS info */
        struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
        struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
-
-       /* Feature bits from the VBIOS */
-       unsigned int int_tv_support:1;
-       unsigned int lvds_dither:1;
-       unsigned int lvds_vbt:1;
-       unsigned int int_crt_support:1;
-       unsigned int lvds_use_ssc:1;
-       unsigned int display_clock_mode:1;
-       unsigned int fdi_rx_polarity_inverted:1;
-       int lvds_ssc_freq;
-       unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */
-       struct {
-               int rate;
-               int lanes;
-               int preemphasis;
-               int vswing;
-
-               bool initialized;
-               bool support;
-               int bpp;
-               struct edp_power_seq pps;
-       } edp;
        bool no_aux_handshake;
 
-       int crt_ddc_pin;
        struct drm_i915_fence_reg fence_regs[I915_MAX_NUM_FENCES]; /* assume 965 */
        int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
        int num_fence_regs; /* 8 on pre-965, 16 otherwise */
@@ -1020,16 +1111,13 @@ typedef struct drm_i915_private {
        /* Kernel Modesetting */
 
        struct sdvo_device_mapping sdvo_mappings[2];
-       /* indicate whether the LVDS_BORDER should be enabled or not */
-       unsigned int lvds_border_bits;
-       /* Panel fitter placement and size for Ironlake+ */
-       u32 pch_pf_pos, pch_pf_size;
 
        struct drm_crtc *plane_to_crtc_mapping[3];
        struct drm_crtc *pipe_to_crtc_mapping[3];
        wait_queue_head_t pending_flip_queue;
 
-       struct intel_pch_pll pch_plls[I915_NUM_PLLS];
+       int num_shared_dpll;
+       struct intel_shared_dpll shared_dplls[I915_NUM_PLLS];
        struct intel_ddi_plls ddi_plls;
 
        /* Reclocking support */
@@ -1038,8 +1126,6 @@ typedef struct drm_i915_private {
        /* indicates the reduced downclock for LVDS*/
        int lvds_downclock;
        u16 orig_clock;
-       int child_dev_num;
-       struct child_device_config *child_dev;
 
        bool mchbar_need_disable;
 
@@ -1052,6 +1138,9 @@ 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;
+
        enum no_fbc_reason no_fbc_reason;
 
        struct drm_mm_node *compressed_fb;
@@ -1059,6 +1148,8 @@ typedef struct drm_i915_private {
 
        struct i915_gpu_error gpu_error;
 
+       struct drm_i915_gem_object *vlv_pctx;
+
        /* list of fbdev register on this device */
        struct intel_fbdev *fbdev;
 
@@ -1124,7 +1215,7 @@ struct drm_i915_gem_object {
        struct drm_mm_node *gtt_space;
        /** Stolen memory for this object, instead of being backed by shmem. */
        struct drm_mm_node *stolen;
-       struct list_head gtt_list;
+       struct list_head global_list;
 
        /** This object's place on the active/inactive lists */
        struct list_head ring_list;
@@ -1271,9 +1362,18 @@ struct drm_i915_gem_request {
        /** GEM sequence number associated with this request. */
        uint32_t seqno;
 
-       /** Postion in the ringbuffer of the end of the request */
+       /** Position in the ringbuffer of the start of the request */
+       u32 head;
+
+       /** Position in the ringbuffer of the end of the request */
        u32 tail;
 
+       /** Context related to this request */
+       struct i915_hw_context *ctx;
+
+       /** Batch buffer related to this request if any */
+       struct drm_i915_gem_object *batch_obj;
+
        /** Time at which this request was emitted, in jiffies. */
        unsigned long emitted_jiffies;
 
@@ -1291,6 +1391,8 @@ struct drm_i915_file_private {
                struct list_head request_list;
        } mm;
        struct idr context_idr;
+
+       struct i915_ctx_hang_stats hang_stats;
 };
 
 #define INTEL_INFO(dev)        (((struct drm_i915_private *) (dev)->dev_private)->info)
@@ -1341,6 +1443,7 @@ struct drm_i915_file_private {
 
 #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 HAS_LLC(dev)            (INTEL_INFO(dev)->has_llc)
 #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
 
@@ -1371,10 +1474,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_PIPE_CONTROL(dev) (INTEL_INFO(dev)->gen >= 5)
 
-#define HAS_DDI(dev)           (IS_HASWELL(dev))
+#define HAS_DDI(dev)           (INTEL_INFO(dev)->has_ddi)
 #define HAS_POWER_WELL(dev)    (IS_HASWELL(dev))
+#define HAS_FPGA_DBG_UNCLAIMED(dev)    (INTEL_INFO(dev)->has_fpga_dbg)
 
 #define INTEL_PCH_DEVICE_ID_MASK               0xff00
 #define INTEL_PCH_IBX_DEVICE_ID_TYPE           0x3b00
@@ -1435,6 +1541,7 @@ extern bool i915_enable_hangcheck __read_mostly;
 extern int i915_enable_ppgtt __read_mostly;
 extern unsigned int i915_preliminary_hw_support __read_mostly;
 extern int i915_disable_power_well __read_mostly;
+extern int i915_enable_ips __read_mostly;
 
 extern int i915_suspend(struct drm_device *dev, pm_message_t state);
 extern int i915_resume(struct drm_device *dev);
@@ -1486,8 +1593,6 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
 void
 i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
 
-void intel_enable_asle(struct drm_device *dev);
-
 #ifdef CONFIG_DEBUG_FS
 extern void i915_destroy_error_state(struct drm_device *dev);
 #else
@@ -1626,6 +1731,7 @@ i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj)
 {
        if (obj->fence_reg != I915_FENCE_REG_NONE) {
                struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
+               WARN_ON(dev_priv->fence_regs[obj->fence_reg].pin_count <= 0);
                dev_priv->fence_regs[obj->fence_reg].pin_count--;
        }
 }
@@ -1658,9 +1764,12 @@ 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 i915_add_request(struct intel_ring_buffer *ring,
-                    struct drm_file *file,
-                    u32 *seqno);
+int __i915_add_request(struct intel_ring_buffer *ring,
+                      struct drm_file *file,
+                      struct drm_i915_gem_object *batch_obj,
+                      u32 *seqno);
+#define i915_add_request(ring, seqno) \
+       __i915_add_request(ring, NULL, NULL, seqno)
 int __must_check i915_wait_seqno(struct intel_ring_buffer *ring,
                                 uint32_t seqno);
 int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
@@ -1705,6 +1814,21 @@ void i915_gem_context_fini(struct drm_device *dev);
 void i915_gem_context_close(struct drm_device *dev, struct drm_file *file);
 int i915_switch_context(struct intel_ring_buffer *ring,
                        struct drm_file *file, int to_id);
+void i915_gem_context_free(struct kref *ctx_ref);
+static inline void i915_gem_context_reference(struct i915_hw_context *ctx)
+{
+       kref_get(&ctx->ref);
+}
+
+static inline void i915_gem_context_unreference(struct i915_hw_context *ctx)
+{
+       kref_put(&ctx->ref, i915_gem_context_free);
+}
+
+struct i915_ctx_hang_stats * __must_check
+i915_gem_context_get_hang_stats(struct intel_ring_buffer *ring,
+                               struct drm_file *file,
+                               u32 id);
 int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
                                  struct drm_file *file);
 int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
@@ -1786,6 +1910,8 @@ void i915_gem_dump_object(struct drm_i915_gem_object *obj, int len,
 /* i915_debugfs.c */
 int i915_debugfs_init(struct drm_minor *minor);
 void i915_debugfs_cleanup(struct drm_minor *minor);
+__printf(2, 3)
+void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...);
 
 /* i915_suspend.c */
 extern int i915_save_state(struct drm_device *dev);
@@ -1802,7 +1928,7 @@ void i915_teardown_sysfs(struct drm_device *dev_priv);
 /* intel_i2c.c */
 extern int intel_setup_gmbus(struct drm_device *dev);
 extern void intel_teardown_gmbus(struct drm_device *dev);
-extern inline bool intel_gmbus_is_port_valid(unsigned port)
+static inline bool intel_gmbus_is_port_valid(unsigned port)
 {
        return (port >= GMBUS_PORT_SSC && port <= GMBUS_PORT_DPD);
 }
@@ -1811,7 +1937,7 @@ extern struct i2c_adapter *intel_gmbus_get_adapter(
                struct drm_i915_private *dev_priv, unsigned port);
 extern void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed);
 extern void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit);
-extern inline bool intel_gmbus_is_forced_bit(struct i2c_adapter *adapter)
+static inline bool intel_gmbus_is_forced_bit(struct i2c_adapter *adapter)
 {
        return container_of(adapter, struct intel_gmbus, adapter)->force_bit;
 }
@@ -1823,14 +1949,10 @@ extern int intel_opregion_setup(struct drm_device *dev);
 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 void intel_opregion_gse_intr(struct drm_device *dev);
-extern void intel_opregion_enable_asle(struct drm_device *dev);
 #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 void intel_opregion_gse_intr(struct drm_device *dev) { return; }
-static inline void intel_opregion_enable_asle(struct drm_device *dev) { return; }
 #endif
 
 /* intel_acpi.c */
@@ -1844,6 +1966,7 @@ static inline void intel_unregister_dsm_handler(void) { return; }
 
 /* modesetting */
 extern void intel_modeset_init_hw(struct drm_device *dev);
+extern void intel_modeset_suspend_hw(struct drm_device *dev);
 extern void intel_modeset_init(struct drm_device *dev);
 extern void intel_modeset_gem_init(struct drm_device *dev);
 extern void intel_modeset_cleanup(struct drm_device *dev);
@@ -1856,6 +1979,9 @@ extern void intel_disable_fbc(struct drm_device *dev);
 extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
 extern void intel_init_pch_refclk(struct drm_device *dev);
 extern void gen6_set_rps(struct drm_device *dev, u8 val);
+extern void valleyview_set_rps(struct drm_device *dev, u8 val);
+extern int valleyview_rps_max_freq(struct drm_i915_private *dev_priv);
+extern int valleyview_rps_min_freq(struct drm_i915_private *dev_priv);
 extern void intel_detect_pch(struct drm_device *dev);
 extern int intel_trans_dp_port_sel(struct drm_crtc *crtc);
 extern int intel_enable_rc6(const struct drm_device *dev);
@@ -1867,10 +1993,11 @@ int i915_reg_read_ioctl(struct drm_device *dev, void *data,
 /* overlay */
 #ifdef CONFIG_DEBUG_FS
 extern struct intel_overlay_error_state *intel_overlay_capture_error_state(struct drm_device *dev);
-extern void intel_overlay_print_error_state(struct seq_file *m, struct intel_overlay_error_state *error);
+extern void intel_overlay_print_error_state(struct drm_i915_error_state_buf *e,
+                                           struct intel_overlay_error_state *error);
 
 extern struct intel_display_error_state *intel_display_capture_error_state(struct drm_device *dev);
-extern void intel_display_print_error_state(struct seq_file *m,
+extern void intel_display_print_error_state(struct drm_i915_error_state_buf *e,
                                            struct drm_device *dev,
                                            struct intel_display_error_state *error);
 #endif
@@ -1885,8 +2012,20 @@ int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv);
 
 int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val);
 int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val);
-int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val);
-int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val);
+
+/* intel_sideband.c */
+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 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,
+                    enum intel_sbi_destination destination);
+
+int vlv_gpu_freq(int ddr_freq, int val);
+int vlv_freq_opcode(int ddr_freq, int val);
 
 #define __i915_read(x, y) \
        u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg);
index 9e35daf..4200c32 100644 (file)
@@ -176,7 +176,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
 
        pinned = 0;
        mutex_lock(&dev->struct_mutex);
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list)
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list)
                if (obj->pin_count)
                        pinned += obj->gtt_space->size;
        mutex_unlock(&dev->struct_mutex);
@@ -956,7 +956,7 @@ i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno)
 
        ret = 0;
        if (seqno == ring->outstanding_lazy_request)
-               ret = i915_add_request(ring, NULL, NULL);
+               ret = i915_add_request(ring, NULL);
 
        return ret;
 }
@@ -1087,6 +1087,25 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
                            interruptible, NULL);
 }
 
+static int
+i915_gem_object_wait_rendering__tail(struct drm_i915_gem_object *obj,
+                                    struct intel_ring_buffer *ring)
+{
+       i915_gem_retire_requests_ring(ring);
+
+       /* Manually manage the write flush as we may have not yet
+        * retired the buffer.
+        *
+        * Note that the last_write_seqno is always the earlier of
+        * the two (read/write) seqno, so if we haved successfully waited,
+        * we know we have passed the last write.
+        */
+       obj->last_write_seqno = 0;
+       obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
+
+       return 0;
+}
+
 /**
  * Ensures that all rendering to the object has completed and the object is
  * safe to unbind from the GTT or access from the CPU.
@@ -1107,18 +1126,7 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
        if (ret)
                return ret;
 
-       i915_gem_retire_requests_ring(ring);
-
-       /* Manually manage the write flush as we may have not yet
-        * retired the buffer.
-        */
-       if (obj->last_write_seqno &&
-           i915_seqno_passed(seqno, obj->last_write_seqno)) {
-               obj->last_write_seqno = 0;
-               obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
-       }
-
-       return 0;
+       return i915_gem_object_wait_rendering__tail(obj, ring);
 }
 
 /* A nonblocking variant of the above wait. This is a highly dangerous routine
@@ -1154,19 +1162,10 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
        mutex_unlock(&dev->struct_mutex);
        ret = __wait_seqno(ring, seqno, reset_counter, true, NULL);
        mutex_lock(&dev->struct_mutex);
+       if (ret)
+               return ret;
 
-       i915_gem_retire_requests_ring(ring);
-
-       /* Manually manage the write flush as we may have not yet
-        * retired the buffer.
-        */
-       if (obj->last_write_seqno &&
-           i915_seqno_passed(seqno, obj->last_write_seqno)) {
-               obj->last_write_seqno = 0;
-               obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
-       }
-
-       return ret;
+       return i915_gem_object_wait_rendering__tail(obj, ring);
 }
 
 /**
@@ -1676,7 +1675,7 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
        /* ->put_pages might need to allocate memory for the bit17 swizzle
         * array, hence protect them from being reaped by removing them from gtt
         * lists early. */
-       list_del(&obj->gtt_list);
+       list_del(&obj->global_list);
 
        ops->put_pages(obj);
        obj->pages = NULL;
@@ -1696,7 +1695,7 @@ __i915_gem_shrink(struct drm_i915_private *dev_priv, long target,
 
        list_for_each_entry_safe(obj, next,
                                 &dev_priv->mm.unbound_list,
-                                gtt_list) {
+                                global_list) {
                if ((i915_gem_object_is_purgeable(obj) || !purgeable_only) &&
                    i915_gem_object_put_pages(obj) == 0) {
                        count += obj->base.size >> PAGE_SHIFT;
@@ -1733,7 +1732,8 @@ i915_gem_shrink_all(struct drm_i915_private *dev_priv)
 
        i915_gem_evict_everything(dev_priv->dev);
 
-       list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list)
+       list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list,
+                                global_list)
                i915_gem_object_put_pages(obj);
 }
 
@@ -1867,7 +1867,7 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
        if (ret)
                return ret;
 
-       list_add_tail(&obj->gtt_list, &dev_priv->mm.unbound_list);
+       list_add_tail(&obj->global_list, &dev_priv->mm.unbound_list);
        return 0;
 }
 
@@ -2005,17 +2005,18 @@ i915_gem_get_seqno(struct drm_device *dev, u32 *seqno)
        return 0;
 }
 
-int
-i915_add_request(struct intel_ring_buffer *ring,
-                struct drm_file *file,
-                u32 *out_seqno)
+int __i915_add_request(struct intel_ring_buffer *ring,
+                      struct drm_file *file,
+                      struct drm_i915_gem_object *obj,
+                      u32 *out_seqno)
 {
        drm_i915_private_t *dev_priv = ring->dev->dev_private;
        struct drm_i915_gem_request *request;
-       u32 request_ring_position;
+       u32 request_ring_position, request_start;
        int was_empty;
        int ret;
 
+       request_start = intel_ring_get_tail(ring);
        /*
         * Emit any outstanding flushes - execbuf can fail to emit the flush
         * after having emitted the batchbuffer command. Hence we need to fix
@@ -2047,7 +2048,21 @@ i915_add_request(struct intel_ring_buffer *ring,
 
        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
+        * request is retired will the the batch_obj be moved onto the
+        * inactive_list and lose its active reference. Hence we do not need
+        * to explicitly hold another reference here.
+        */
+
+       if (request->ctx)
+               i915_gem_context_reference(request->ctx);
+
        request->emitted_jiffies = jiffies;
        was_empty = list_empty(&ring->request_list);
        list_add_tail(&request->list, &ring->request_list);
@@ -2100,9 +2115,114 @@ i915_gem_request_remove_from_client(struct drm_i915_gem_request *request)
        spin_unlock(&file_priv->mm.lock);
 }
 
+static bool i915_head_inside_object(u32 acthd, struct drm_i915_gem_object *obj)
+{
+       if (acthd >= obj->gtt_offset &&
+           acthd < obj->gtt_offset + obj->base.size)
+               return true;
+
+       return false;
+}
+
+static bool i915_head_inside_request(const u32 acthd_unmasked,
+                                    const u32 request_start,
+                                    const u32 request_end)
+{
+       const u32 acthd = acthd_unmasked & HEAD_ADDR;
+
+       if (request_start < request_end) {
+               if (acthd >= request_start && acthd < request_end)
+                       return true;
+       } else if (request_start > request_end) {
+               if (acthd >= request_start || acthd < request_end)
+                       return true;
+       }
+
+       return false;
+}
+
+static bool i915_request_guilty(struct drm_i915_gem_request *request,
+                               const u32 acthd, bool *inside)
+{
+       /* There is a possibility that unmasked head address
+        * pointing inside the ring, matches the batch_obj address range.
+        * However this is extremely unlikely.
+        */
+
+       if (request->batch_obj) {
+               if (i915_head_inside_object(acthd, request->batch_obj)) {
+                       *inside = true;
+                       return true;
+               }
+       }
+
+       if (i915_head_inside_request(acthd, request->head, request->tail)) {
+               *inside = false;
+               return true;
+       }
+
+       return false;
+}
+
+static void i915_set_reset_status(struct intel_ring_buffer *ring,
+                                 struct drm_i915_gem_request *request,
+                                 u32 acthd)
+{
+       struct i915_ctx_hang_stats *hs = NULL;
+       bool inside, guilty;
+
+       /* Innocent until proven guilty */
+       guilty = false;
+
+       if (ring->hangcheck.action != wait &&
+           i915_request_guilty(request, acthd, &inside)) {
+               DRM_ERROR("%s hung %s bo (0x%x ctx %d) at 0x%x\n",
+                         ring->name,
+                         inside ? "inside" : "flushing",
+                         request->batch_obj ?
+                         request->batch_obj->gtt_offset : 0,
+                         request->ctx ? request->ctx->id : 0,
+                         acthd);
+
+               guilty = true;
+       }
+
+       /* If contexts are disabled or this is the default context, use
+        * file_priv->reset_state
+        */
+       if (request->ctx && request->ctx->id != DEFAULT_CONTEXT_ID)
+               hs = &request->ctx->hang_stats;
+       else if (request->file_priv)
+               hs = &request->file_priv->hang_stats;
+
+       if (hs) {
+               if (guilty)
+                       hs->batch_active++;
+               else
+                       hs->batch_pending++;
+       }
+}
+
+static void i915_gem_free_request(struct drm_i915_gem_request *request)
+{
+       list_del(&request->list);
+       i915_gem_request_remove_from_client(request);
+
+       if (request->ctx)
+               i915_gem_context_unreference(request->ctx);
+
+       kfree(request);
+}
+
 static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv,
                                      struct intel_ring_buffer *ring)
 {
+       u32 completed_seqno;
+       u32 acthd;
+
+       acthd = intel_ring_get_active_head(ring);
+       completed_seqno = ring->get_seqno(ring, false);
+
        while (!list_empty(&ring->request_list)) {
                struct drm_i915_gem_request *request;
 
@@ -2110,9 +2230,10 @@ static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv,
                                           struct drm_i915_gem_request,
                                           list);
 
-               list_del(&request->list);
-               i915_gem_request_remove_from_client(request);
-               kfree(request);
+               if (request->seqno > completed_seqno)
+                       i915_set_reset_status(ring, request, acthd);
+
+               i915_gem_free_request(request);
        }
 
        while (!list_empty(&ring->active_list)) {
@@ -2193,9 +2314,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
                 */
                ring->last_retired_head = request->tail;
 
-               list_del(&request->list);
-               i915_gem_request_remove_from_client(request);
-               kfree(request);
+               i915_gem_free_request(request);
        }
 
        /* Move any buffers on the active list that are no longer referenced
@@ -2262,7 +2381,7 @@ i915_gem_retire_work_handler(struct work_struct *work)
        idle = true;
        for_each_ring(ring, dev_priv, i) {
                if (ring->gpu_caches_dirty)
-                       i915_add_request(ring, NULL, NULL);
+                       i915_add_request(ring, NULL);
 
                idle &= list_empty(&ring->request_list);
        }
@@ -2494,9 +2613,10 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
                obj->has_aliasing_ppgtt_mapping = 0;
        }
        i915_gem_gtt_finish_object(obj);
+       i915_gem_object_unpin_pages(obj);
 
        list_del(&obj->mm_list);
-       list_move_tail(&obj->gtt_list, &dev_priv->mm.unbound_list);
+       list_move_tail(&obj->global_list, &dev_priv->mm.unbound_list);
        /* Avoid an unnecessary call to unbind on rebind. */
        obj->map_and_fenceable = true;
 
@@ -2676,18 +2796,33 @@ static inline int fence_number(struct drm_i915_private *dev_priv,
        return fence - dev_priv->fence_regs;
 }
 
+struct write_fence {
+       struct drm_device *dev;
+       struct drm_i915_gem_object *obj;
+       int fence;
+};
+
 static void i915_gem_write_fence__ipi(void *data)
 {
+       struct write_fence *args = data;
+
+       /* Required for SNB+ with LLC */
        wbinvd();
+
+       /* Required for VLV */
+       i915_gem_write_fence(args->dev, args->fence, args->obj);
 }
 
 static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj,
                                         struct drm_i915_fence_reg *fence,
                                         bool enable)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       int fence_reg = fence_number(dev_priv, fence);
+       struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
+       struct write_fence args = {
+               .dev = obj->base.dev,
+               .fence = fence_number(dev_priv, fence),
+               .obj = enable ? obj : NULL,
+       };
 
        /* In order to fully serialize access to the fenced region and
         * the update to the fence register we need to take extreme
@@ -2698,13 +2833,19 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj,
         * SNB+ we need to take a step further and emit an explicit wbinvd()
         * on each processor in order to manually flush all memory
         * transactions before updating the fence register.
+        *
+        * However, Valleyview complicates matter. There the wbinvd is
+        * insufficient and unlike SNB/IVB requires the serialising
+        * register write. (Note that that register write by itself is
+        * conversely not sufficient for SNB+.) To compromise, we do both.
         */
-       if (HAS_LLC(obj->base.dev))
-               on_each_cpu(i915_gem_write_fence__ipi, NULL, 1);
-       i915_gem_write_fence(dev, fence_reg, enable ? obj : NULL);
+       if (INTEL_INFO(args.dev)->gen >= 6)
+               on_each_cpu(i915_gem_write_fence__ipi, &args, 1);
+       else
+               i915_gem_write_fence(args.dev, args.fence, args.obj);
 
        if (enable) {
-               obj->fence_reg = fence_reg;
+               obj->fence_reg = args.fence;
                fence->obj = obj;
                list_move_tail(&fence->lru_list, &dev_priv->mm.fence_list);
        } else {
@@ -2883,7 +3024,7 @@ static void i915_gem_verify_gtt(struct drm_device *dev)
        struct drm_i915_gem_object *obj;
        int err = 0;
 
-       list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
+       list_for_each_entry(obj, &dev_priv->mm.gtt_list, global_list) {
                if (obj->gtt_space == NULL) {
                        printk(KERN_ERR "object found on GTT list with no space reserved\n");
                        err++;
@@ -2930,6 +3071,8 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
        struct drm_mm_node *node;
        u32 size, fence_size, fence_alignment, unfenced_alignment;
        bool mappable, fenceable;
+       size_t gtt_max = map_and_fenceable ?
+               dev_priv->gtt.mappable_end : dev_priv->gtt.total;
        int ret;
 
        fence_size = i915_gem_get_gtt_size(dev,
@@ -2956,9 +3099,11 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
        /* If the object is bigger than the entire aperture, reject it early
         * before evicting everything in a vain attempt to find space.
         */
-       if (obj->base.size >
-           (map_and_fenceable ? dev_priv->gtt.mappable_end : dev_priv->gtt.total)) {
-               DRM_ERROR("Attempting to bind an object larger than the aperture\n");
+       if (obj->base.size > gtt_max) {
+               DRM_ERROR("Attempting to bind an object larger than the aperture: object=%zd > %s aperture=%zu\n",
+                         obj->base.size,
+                         map_and_fenceable ? "mappable" : "total",
+                         gtt_max);
                return -E2BIG;
        }
 
@@ -2974,14 +3119,10 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
                return -ENOMEM;
        }
 
- search_free:
-       if (map_and_fenceable)
-               ret = drm_mm_insert_node_in_range_generic(&dev_priv->mm.gtt_space, node,
-                                                         size, alignment, obj->cache_level,
-                                                         0, dev_priv->gtt.mappable_end);
-       else
-               ret = drm_mm_insert_node_generic(&dev_priv->mm.gtt_space, node,
-                                                size, alignment, obj->cache_level);
+search_free:
+       ret = drm_mm_insert_node_in_range_generic(&dev_priv->mm.gtt_space, node,
+                                                 size, alignment,
+                                                 obj->cache_level, 0, gtt_max);
        if (ret) {
                ret = i915_gem_evict_something(dev, size, alignment,
                                               obj->cache_level,
@@ -3007,7 +3148,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
                return ret;
        }
 
-       list_move_tail(&obj->gtt_list, &dev_priv->mm.bound_list);
+       list_move_tail(&obj->global_list, &dev_priv->mm.bound_list);
        list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
 
        obj->gtt_space = node;
@@ -3022,7 +3163,6 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
 
        obj->map_and_fenceable = mappable && fenceable;
 
-       i915_gem_object_unpin_pages(obj);
        trace_i915_gem_object_bind(obj, map_and_fenceable);
        i915_gem_verify_gtt(dev);
        return 0;
@@ -3722,7 +3862,7 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
                          const struct drm_i915_gem_object_ops *ops)
 {
        INIT_LIST_HEAD(&obj->mm_list);
-       INIT_LIST_HEAD(&obj->gtt_list);
+       INIT_LIST_HEAD(&obj->global_list);
        INIT_LIST_HEAD(&obj->ring_list);
        INIT_LIST_HEAD(&obj->exec_list);
 
@@ -3822,7 +3962,13 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
                dev_priv->mm.interruptible = was_interruptible;
        }
 
-       obj->pages_pin_count = 0;
+       /* Stolen objects don't hold a ref, but do hold pin count. Fix that up
+        * before progressing. */
+       if (obj->stolen)
+               i915_gem_object_unpin_pages(obj);
+
+       if (WARN_ON(obj->pages_pin_count))
+               obj->pages_pin_count = 0;
        i915_gem_object_put_pages(obj);
        i915_gem_object_free_mmap_offset(obj);
        i915_gem_object_release_stolen(obj);
@@ -3973,12 +4119,21 @@ static int i915_gem_init_rings(struct drm_device *dev)
                        goto cleanup_bsd_ring;
        }
 
+       if (HAS_VEBOX(dev)) {
+               ret = intel_init_vebox_ring_buffer(dev);
+               if (ret)
+                       goto cleanup_blt_ring;
+       }
+
+
        ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000));
        if (ret)
-               goto cleanup_blt_ring;
+               goto cleanup_vebox_ring;
 
        return 0;
 
+cleanup_vebox_ring:
+       intel_cleanup_ring_buffer(&dev_priv->ring[VECS]);
 cleanup_blt_ring:
        intel_cleanup_ring_buffer(&dev_priv->ring[BCS]);
 cleanup_bsd_ring:
@@ -4453,10 +4608,10 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc)
        }
 
        cnt = 0;
-       list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list)
+       list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list)
                if (obj->pages_pin_count == 0)
                        cnt += obj->base.size >> PAGE_SHIFT;
-       list_for_each_entry(obj, &dev_priv->mm.inactive_list, gtt_list)
+       list_for_each_entry(obj, &dev_priv->mm.inactive_list, global_list)
                if (obj->pin_count == 0 && obj->pages_pin_count == 0)
                        cnt += obj->base.size >> PAGE_SHIFT;
 
index a1e8ecb..51b7a21 100644 (file)
@@ -113,7 +113,7 @@ static int get_context_size(struct drm_device *dev)
        case 7:
                reg = I915_READ(GEN7_CXT_SIZE);
                if (IS_HASWELL(dev))
-                       ret = HSW_CXT_TOTAL_SIZE(reg) * 64;
+                       ret = HSW_CXT_TOTAL_SIZE;
                else
                        ret = GEN7_CXT_TOTAL_SIZE(reg) * 64;
                break;
@@ -124,10 +124,10 @@ static int get_context_size(struct drm_device *dev)
        return ret;
 }
 
-static void do_destroy(struct i915_hw_context *ctx)
+void i915_gem_context_free(struct kref *ctx_ref)
 {
-       if (ctx->file_priv)
-               idr_remove(&ctx->file_priv->context_idr, ctx->id);
+       struct i915_hw_context *ctx = container_of(ctx_ref,
+                                                  typeof(*ctx), ref);
 
        drm_gem_object_unreference(&ctx->obj->base);
        kfree(ctx);
@@ -145,6 +145,7 @@ create_hw_context(struct drm_device *dev,
        if (ctx == NULL)
                return ERR_PTR(-ENOMEM);
 
+       kref_init(&ctx->ref);
        ctx->obj = i915_gem_alloc_object(dev, dev_priv->hw_context_size);
        if (ctx->obj == NULL) {
                kfree(ctx);
@@ -155,7 +156,8 @@ create_hw_context(struct drm_device *dev,
        if (INTEL_INFO(dev)->gen >= 7) {
                ret = i915_gem_object_set_cache_level(ctx->obj,
                                                      I915_CACHE_LLC_MLC);
-               if (ret)
+               /* Failure shouldn't ever happen this early */
+               if (WARN_ON(ret))
                        goto err_out;
        }
 
@@ -169,18 +171,18 @@ create_hw_context(struct drm_device *dev,
        if (file_priv == NULL)
                return ctx;
 
-       ctx->file_priv = file_priv;
-
        ret = idr_alloc(&file_priv->context_idr, ctx, DEFAULT_CONTEXT_ID + 1, 0,
                        GFP_KERNEL);
        if (ret < 0)
                goto err_out;
+
+       ctx->file_priv = file_priv;
        ctx->id = ret;
 
        return ctx;
 
 err_out:
-       do_destroy(ctx);
+       i915_gem_context_unreference(ctx);
        return ERR_PTR(ret);
 }
 
@@ -213,12 +215,16 @@ static int create_default_context(struct drm_i915_private *dev_priv)
         */
        dev_priv->ring[RCS].default_context = ctx;
        ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false, false);
-       if (ret)
+       if (ret) {
+               DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret);
                goto err_destroy;
+       }
 
        ret = do_switch(ctx);
-       if (ret)
+       if (ret) {
+               DRM_DEBUG_DRIVER("Switch failed %d\n", ret);
                goto err_unpin;
+       }
 
        DRM_DEBUG_DRIVER("Default HW context loaded\n");
        return 0;
@@ -226,7 +232,7 @@ static int create_default_context(struct drm_i915_private *dev_priv)
 err_unpin:
        i915_gem_object_unpin(ctx->obj);
 err_destroy:
-       do_destroy(ctx);
+       i915_gem_context_unreference(ctx);
        return ret;
 }
 
@@ -236,6 +242,7 @@ void i915_gem_context_init(struct drm_device *dev)
 
        if (!HAS_HW_CONTEXTS(dev)) {
                dev_priv->hw_contexts_disabled = true;
+               DRM_DEBUG_DRIVER("Disabling HW Contexts; old hardware\n");
                return;
        }
 
@@ -248,11 +255,13 @@ void i915_gem_context_init(struct drm_device *dev)
 
        if (dev_priv->hw_context_size > (1<<20)) {
                dev_priv->hw_contexts_disabled = true;
+               DRM_DEBUG_DRIVER("Disabling HW Contexts; invalid size\n");
                return;
        }
 
        if (create_default_context(dev_priv)) {
                dev_priv->hw_contexts_disabled = true;
+               DRM_DEBUG_DRIVER("Disabling HW Contexts; create failed\n");
                return;
        }
 
@@ -262,6 +271,7 @@ void i915_gem_context_init(struct drm_device *dev)
 void i915_gem_context_fini(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct i915_hw_context *dctx = dev_priv->ring[RCS].default_context;
 
        if (dev_priv->hw_contexts_disabled)
                return;
@@ -271,9 +281,16 @@ void i915_gem_context_fini(struct drm_device *dev)
         * other code, leading to spurious errors. */
        intel_gpu_reset(dev);
 
-       i915_gem_object_unpin(dev_priv->ring[RCS].default_context->obj);
+       i915_gem_object_unpin(dctx->obj);
 
-       do_destroy(dev_priv->ring[RCS].default_context);
+       /* 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);
+       i915_gem_context_unreference(dctx);
 }
 
 static int context_idr_cleanup(int id, void *p, void *data)
@@ -282,11 +299,38 @@ static int context_idr_cleanup(int id, void *p, void *data)
 
        BUG_ON(id == DEFAULT_CONTEXT_ID);
 
-       do_destroy(ctx);
-
+       i915_gem_context_unreference(ctx);
        return 0;
 }
 
+struct i915_ctx_hang_stats *
+i915_gem_context_get_hang_stats(struct intel_ring_buffer *ring,
+                               struct drm_file *file,
+                               u32 id)
+{
+       struct drm_i915_private *dev_priv = ring->dev->dev_private;
+       struct drm_i915_file_private *file_priv = file->driver_priv;
+       struct i915_hw_context *to;
+
+       if (dev_priv->hw_contexts_disabled)
+               return ERR_PTR(-ENOENT);
+
+       if (ring->id != RCS)
+               return ERR_PTR(-EINVAL);
+
+       if (file == NULL)
+               return ERR_PTR(-EINVAL);
+
+       if (id == DEFAULT_CONTEXT_ID)
+               return &file_priv->hang_stats;
+
+       to = i915_gem_context_get(file->driver_priv, id);
+       if (to == NULL)
+               return ERR_PTR(-ENOENT);
+
+       return &to->hang_stats;
+}
+
 void i915_gem_context_close(struct drm_device *dev, struct drm_file *file)
 {
        struct drm_i915_file_private *file_priv = file->driver_priv;
@@ -325,6 +369,7 @@ mi_set_context(struct intel_ring_buffer *ring,
        if (ret)
                return ret;
 
+       /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw */
        if (IS_GEN7(ring->dev))
                intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE);
        else
@@ -353,13 +398,13 @@ mi_set_context(struct intel_ring_buffer *ring,
 static int do_switch(struct i915_hw_context *to)
 {
        struct intel_ring_buffer *ring = to->ring;
-       struct drm_i915_gem_object *from_obj = ring->last_context_obj;
+       struct i915_hw_context *from = ring->last_context;
        u32 hw_flags = 0;
        int ret;
 
-       BUG_ON(from_obj != NULL && from_obj->pin_count == 0);
+       BUG_ON(from != NULL && from->obj != NULL && from->obj->pin_count == 0);
 
-       if (from_obj == to->obj)
+       if (from == to)
                return 0;
 
        ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false, false);
@@ -382,7 +427,7 @@ 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_obj == to->obj)) /* not yet expected */
+       else if (WARN_ON_ONCE(from == to)) /* not yet expected */
                hw_flags |= MI_FORCE_RESTORE;
 
        ret = mi_set_context(ring, to, hw_flags);
@@ -397,9 +442,9 @@ static int do_switch(struct i915_hw_context *to)
         * is a bit suboptimal because the retiring can occur simply after the
         * MI_SET_CONTEXT instead of when the next seqno has completed.
         */
-       if (from_obj != NULL) {
-               from_obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
-               i915_gem_object_move_to_active(from_obj, ring);
+       if (from != NULL) {
+               from->obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
+               i915_gem_object_move_to_active(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
@@ -407,15 +452,26 @@ static int do_switch(struct i915_hw_context *to)
                 * able to defer doing this until we know the object would be
                 * swapped, but there is no way to do that yet.
                 */
-               from_obj->dirty = 1;
-               BUG_ON(from_obj->ring != ring);
-               i915_gem_object_unpin(from_obj);
+               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;
+               }
 
-               drm_gem_object_unreference(&from_obj->base);
+               i915_gem_object_unpin(from->obj);
+               i915_gem_context_unreference(from);
        }
 
-       drm_gem_object_reference(&to->obj->base);
-       ring->last_context_obj = to->obj;
+       i915_gem_context_reference(to);
+       ring->last_context = to;
        to->is_initialized = true;
 
        return 0;
@@ -444,6 +500,8 @@ int i915_switch_context(struct intel_ring_buffer *ring,
        if (dev_priv->hw_contexts_disabled)
                return 0;
 
+       WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
+
        if (ring != &dev_priv->ring[RCS])
                return 0;
 
@@ -512,8 +570,8 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
                return -ENOENT;
        }
 
-       do_destroy(ctx);
-
+       idr_remove(&ctx->file_priv->context_idr, ctx->id);
+       i915_gem_context_unreference(ctx);
        mutex_unlock(&dev->struct_mutex);
 
        DRM_DEBUG_DRIVER("HW context %d destroyed\n", args->ctx_id);
index 117ce38..87a3227 100644 (file)
@@ -786,7 +786,7 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects,
                        obj->dirty = 1;
                        obj->last_write_seqno = intel_ring_get_seqno(ring);
                        if (obj->pin_count) /* check for potential scanout */
-                               intel_mark_fb_busy(obj);
+                               intel_mark_fb_busy(obj, ring);
                }
 
                trace_i915_gem_object_change_domain(obj, old_read, old_write);
@@ -796,13 +796,14 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects,
 static void
 i915_gem_execbuffer_retire_commands(struct drm_device *dev,
                                    struct drm_file *file,
-                                   struct intel_ring_buffer *ring)
+                                   struct intel_ring_buffer *ring,
+                                   struct drm_i915_gem_object *obj)
 {
        /* Unconditionally force add_request to emit a full flush. */
        ring->gpu_caches_dirty = true;
 
        /* Add a breadcrumb for the completion of the batch buffer */
-       (void)i915_add_request(ring, file, NULL);
+       (void)__i915_add_request(ring, file, obj, NULL);
 }
 
 static int
@@ -885,6 +886,15 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                        return -EPERM;
                }
                break;
+       case I915_EXEC_VEBOX:
+               ring = &dev_priv->ring[VECS];
+               if (ctx_id != 0) {
+                       DRM_DEBUG("Ring %s doesn't support contexts\n",
+                                 ring->name);
+                       return -EPERM;
+               }
+               break;
+
        default:
                DRM_DEBUG("execbuf with unknown ring: %d\n",
                          (int)(args->flags & I915_EXEC_RING_MASK));
@@ -1074,7 +1084,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, ring);
-       i915_gem_execbuffer_retire_commands(dev, file, ring);
+       i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj);
 
 err:
        eb_destroy(eb);
index bdb0d77..5101ab6 100644 (file)
@@ -28,8 +28,6 @@
 #include "i915_trace.h"
 #include "intel_drv.h"
 
-typedef uint32_t gen6_gtt_pte_t;
-
 /* PPGTT stuff */
 #define GEN6_GTT_ADDR_ENCODE(addr)     ((addr) | (((addr) >> 28) & 0xff0))
 
@@ -44,29 +42,22 @@ typedef uint32_t gen6_gtt_pte_t;
 #define GEN6_PTE_CACHE_LLC_MLC         (3 << 1)
 #define GEN6_PTE_ADDR_ENCODE(addr)     GEN6_GTT_ADDR_ENCODE(addr)
 
-static inline gen6_gtt_pte_t gen6_pte_encode(struct drm_device *dev,
-                                            dma_addr_t addr,
-                                            enum i915_cache_level level)
+static gen6_gtt_pte_t gen6_pte_encode(struct drm_device *dev,
+                                     dma_addr_t addr,
+                                     enum i915_cache_level level)
 {
        gen6_gtt_pte_t pte = GEN6_PTE_VALID;
        pte |= GEN6_PTE_ADDR_ENCODE(addr);
 
        switch (level) {
        case I915_CACHE_LLC_MLC:
-               /* Haswell doesn't set L3 this way */
-               if (IS_HASWELL(dev))
-                       pte |= GEN6_PTE_CACHE_LLC;
-               else
-                       pte |= GEN6_PTE_CACHE_LLC_MLC;
+               pte |= GEN6_PTE_CACHE_LLC_MLC;
                break;
        case I915_CACHE_LLC:
                pte |= GEN6_PTE_CACHE_LLC;
                break;
        case I915_CACHE_NONE:
-               if (IS_HASWELL(dev))
-                       pte |= HSW_PTE_UNCACHED;
-               else
-                       pte |= GEN6_PTE_UNCACHED;
+               pte |= GEN6_PTE_UNCACHED;
                break;
        default:
                BUG();
@@ -75,16 +66,48 @@ static inline gen6_gtt_pte_t gen6_pte_encode(struct drm_device *dev,
        return pte;
 }
 
-static int gen6_ppgtt_enable(struct drm_device *dev)
+#define BYT_PTE_WRITEABLE              (1 << 1)
+#define BYT_PTE_SNOOPED_BY_CPU_CACHES  (1 << 2)
+
+static gen6_gtt_pte_t byt_pte_encode(struct drm_device *dev,
+                                    dma_addr_t addr,
+                                    enum i915_cache_level level)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       uint32_t pd_offset;
-       struct intel_ring_buffer *ring;
-       struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
+       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       pte |= GEN6_PTE_ADDR_ENCODE(addr);
+
+       /* Mark the page as writeable.  Other platforms don't have a
+        * setting for read-only/writable, so this matches that behavior.
+        */
+       pte |= BYT_PTE_WRITEABLE;
+
+       if (level != I915_CACHE_NONE)
+               pte |= BYT_PTE_SNOOPED_BY_CPU_CACHES;
+
+       return pte;
+}
+
+static gen6_gtt_pte_t hsw_pte_encode(struct drm_device *dev,
+                                    dma_addr_t addr,
+                                    enum i915_cache_level level)
+{
+       gen6_gtt_pte_t pte = GEN6_PTE_VALID;
+       pte |= GEN6_PTE_ADDR_ENCODE(addr);
+
+       if (level != I915_CACHE_NONE)
+               pte |= GEN6_PTE_CACHE_LLC;
+
+       return pte;
+}
+
+static void gen6_write_pdes(struct i915_hw_ppgtt *ppgtt)
+{
+       struct drm_i915_private *dev_priv = ppgtt->dev->dev_private;
        gen6_gtt_pte_t __iomem *pd_addr;
        uint32_t pd_entry;
        int i;
 
+       WARN_ON(ppgtt->pd_offset & 0x3f);
        pd_addr = (gen6_gtt_pte_t __iomem*)dev_priv->gtt.gsm +
                ppgtt->pd_offset / sizeof(gen6_gtt_pte_t);
        for (i = 0; i < ppgtt->num_pd_entries; i++) {
@@ -97,6 +120,19 @@ static int gen6_ppgtt_enable(struct drm_device *dev)
                writel(pd_entry, pd_addr + i);
        }
        readl(pd_addr);
+}
+
+static int gen6_ppgtt_enable(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       uint32_t pd_offset;
+       struct intel_ring_buffer *ring;
+       struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
+       int i;
+
+       BUG_ON(ppgtt->pd_offset & 0x3f);
+
+       gen6_write_pdes(ppgtt);
 
        pd_offset = ppgtt->pd_offset;
        pd_offset /= 64; /* in cachelines, */
@@ -154,9 +190,9 @@ static void gen6_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt,
        unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
        unsigned last_pte, i;
 
-       scratch_pte = gen6_pte_encode(ppgtt->dev,
-                                     ppgtt->scratch_page_dma_addr,
-                                     I915_CACHE_LLC);
+       scratch_pte = ppgtt->pte_encode(ppgtt->dev,
+                                       ppgtt->scratch_page_dma_addr,
+                                       I915_CACHE_LLC);
 
        while (num_entries) {
                last_pte = first_pte + num_entries;
@@ -191,8 +227,8 @@ static void gen6_ppgtt_insert_entries(struct i915_hw_ppgtt *ppgtt,
                dma_addr_t page_addr;
 
                page_addr = sg_page_iter_dma_address(&sg_iter);
-               pt_vaddr[act_pte] = gen6_pte_encode(ppgtt->dev, page_addr,
-                                                   cache_level);
+               pt_vaddr[act_pte] = ppgtt->pte_encode(ppgtt->dev, page_addr,
+                                                     cache_level);
                if (++act_pte == I915_PPGTT_PT_ENTRIES) {
                        kunmap_atomic(pt_vaddr);
                        act_pt++;
@@ -233,8 +269,15 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
        /* ppgtt PDEs reside in the global gtt pagetable, which has 512*1024
         * entries. For aliasing ppgtt support we just steal them at the end for
         * now. */
-       first_pd_entry_in_global_pt = gtt_total_entries(dev_priv->gtt);
+       first_pd_entry_in_global_pt = gtt_total_entries(dev_priv->gtt);
 
+       if (IS_HASWELL(dev)) {
+               ppgtt->pte_encode = hsw_pte_encode;
+       } else if (IS_VALLEYVIEW(dev)) {
+               ppgtt->pte_encode = byt_pte_encode;
+       } else {
+               ppgtt->pte_encode = gen6_pte_encode;
+       }
        ppgtt->num_pd_entries = I915_PPGTT_PD_ENTRIES;
        ppgtt->enable = gen6_ppgtt_enable;
        ppgtt->clear_range = gen6_ppgtt_clear_range;
@@ -396,7 +439,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
        dev_priv->gtt.gtt_clear_range(dev, dev_priv->gtt.start / PAGE_SIZE,
                                      dev_priv->gtt.total / PAGE_SIZE);
 
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
                i915_gem_clflush_object(obj);
                i915_gem_gtt_bind_object(obj, obj->cache_level);
        }
@@ -437,7 +480,8 @@ static void gen6_ggtt_insert_entries(struct drm_device *dev,
 
        for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) {
                addr = sg_page_iter_dma_address(&sg_iter);
-               iowrite32(gen6_pte_encode(dev, addr, level), &gtt_entries[i]);
+               iowrite32(dev_priv->gtt.pte_encode(dev, addr, level),
+                         &gtt_entries[i]);
                i++;
        }
 
@@ -449,7 +493,7 @@ static void gen6_ggtt_insert_entries(struct drm_device *dev,
         */
        if (i != 0)
                WARN_ON(readl(&gtt_entries[i-1])
-                       != gen6_pte_encode(dev, addr, level));
+                       != dev_priv->gtt.pte_encode(dev, addr, level));
 
        /* 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
@@ -474,8 +518,9 @@ static void gen6_ggtt_clear_range(struct drm_device *dev,
                 first_entry, num_entries, max_entries))
                num_entries = max_entries;
 
-       scratch_pte = gen6_pte_encode(dev, dev_priv->gtt.scratch_page_dma,
-                                     I915_CACHE_LLC);
+       scratch_pte = dev_priv->gtt.pte_encode(dev,
+                                              dev_priv->gtt.scratch_page_dma,
+                                              I915_CACHE_LLC);
        for (i = 0; i < num_entries; i++)
                iowrite32(scratch_pte, &gtt_base[i]);
        readl(gtt_base);
@@ -586,7 +631,7 @@ void i915_gem_setup_global_gtt(struct drm_device *dev,
                dev_priv->mm.gtt_space.color_adjust = i915_gtt_color_adjust;
 
        /* Mark any preallocated objects as occupied */
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
                DRM_DEBUG_KMS("reserving preallocated space: %x + %zx\n",
                              obj->gtt_offset, obj->base.size);
 
@@ -809,6 +854,13 @@ int i915_gem_gtt_init(struct drm_device *dev)
        } else {
                dev_priv->gtt.gtt_probe = gen6_gmch_probe;
                dev_priv->gtt.gtt_remove = gen6_gmch_remove;
+               if (IS_HASWELL(dev)) {
+                       dev_priv->gtt.pte_encode = hsw_pte_encode;
+               } else if (IS_VALLEYVIEW(dev)) {
+                       dev_priv->gtt.pte_encode = byt_pte_encode;
+               } else {
+                       dev_priv->gtt.pte_encode = gen6_pte_encode;
+               }
        }
 
        ret = dev_priv->gtt.gtt_probe(dev, &dev_priv->gtt.total,
index 130d1db..982d473 100644 (file)
@@ -62,7 +62,10 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
         * its value of TOLUD.
         */
        base = 0;
-       if (INTEL_INFO(dev)->gen >= 6) {
+       if (IS_VALLEYVIEW(dev)) {
+               pci_read_config_dword(dev->pdev, 0x5c, &base);
+               base &= ~((1<<20) - 1);
+       } else if (INTEL_INFO(dev)->gen >= 6) {
                /* Read Base Data of Stolen Memory Register (BDSM) directly.
                 * Note that there is also a MCHBAR miror at 0x1080c0 or
                 * we could use device 2:0x5c instead.
@@ -136,6 +139,7 @@ static int i915_setup_compression(struct drm_device *dev, int size)
 err_fb:
        drm_mm_put_block(compressed_fb);
 err:
+       pr_info_once("drm: not enough stolen space for compressed buffer (need %d more bytes), disabling. Hint: you may be able to increase stolen memory size in the BIOS to avoid this.\n", size);
        return -ENOSPC;
 }
 
@@ -143,7 +147,7 @@ int i915_gem_stolen_setup_compression(struct drm_device *dev, int size)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (dev_priv->mm.stolen_base == 0)
+       if (!drm_mm_initialized(&dev_priv->mm.stolen))
                return -ENODEV;
 
        if (size < dev_priv->cfb_size)
@@ -175,6 +179,9 @@ void i915_gem_cleanup_stolen(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       if (!drm_mm_initialized(&dev_priv->mm.stolen))
+               return;
+
        i915_gem_stolen_cleanup_compression(dev);
        drm_mm_takedown(&dev_priv->mm.stolen);
 }
@@ -182,6 +189,7 @@ void i915_gem_cleanup_stolen(struct drm_device *dev)
 int i915_gem_init_stolen(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       int bios_reserved = 0;
 
        dev_priv->mm.stolen_base = i915_stolen_to_physical(dev);
        if (dev_priv->mm.stolen_base == 0)
@@ -190,8 +198,12 @@ int i915_gem_init_stolen(struct drm_device *dev)
        DRM_DEBUG_KMS("found %zd bytes of stolen memory at %08lx\n",
                      dev_priv->gtt.stolen_size, dev_priv->mm.stolen_base);
 
+       if (IS_VALLEYVIEW(dev))
+               bios_reserved = 1024*1024; /* top 1M on VLV/BYT */
+
        /* Basic memrange allocator for stolen space */
-       drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size);
+       drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size -
+                   bios_reserved);
 
        return 0;
 }
@@ -270,7 +282,7 @@ _i915_gem_object_create_stolen(struct drm_device *dev,
                goto cleanup;
 
        obj->has_dma_mapping = true;
-       obj->pages_pin_count = 1;
+       i915_gem_object_pin_pages(obj);
        obj->stolen = stolen;
 
        obj->base.write_domain = I915_GEM_DOMAIN_GTT;
@@ -291,7 +303,7 @@ i915_gem_object_create_stolen(struct drm_device *dev, u32 size)
        struct drm_i915_gem_object *obj;
        struct drm_mm_node *stolen;
 
-       if (dev_priv->mm.stolen_base == 0)
+       if (!drm_mm_initialized(&dev_priv->mm.stolen))
                return NULL;
 
        DRM_DEBUG_KMS("creating stolen object: size=%x\n", size);
@@ -322,7 +334,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
        struct drm_i915_gem_object *obj;
        struct drm_mm_node *stolen;
 
-       if (dev_priv->mm.stolen_base == 0)
+       if (!drm_mm_initialized(&dev_priv->mm.stolen))
                return NULL;
 
        DRM_DEBUG_KMS("creating preallocated stolen object: stolen_offset=%x, gtt_offset=%x, size=%x\n",
@@ -330,7 +342,6 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
 
        /* KISS and expect everything to be page-aligned */
        BUG_ON(stolen_offset & 4095);
-       BUG_ON(gtt_offset & 4095);
        BUG_ON(size & 4095);
 
        if (WARN_ON(size == 0))
@@ -351,6 +362,10 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
                return NULL;
        }
 
+       /* Some objects just need physical mem from stolen space */
+       if (gtt_offset == -1)
+               return obj;
+
        /* To simplify the initialisation sequence between KMS and GTT,
         * we allow construction of the stolen object prior to
         * setting up the GTT space. The actual reservation will occur
@@ -371,7 +386,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
        obj->gtt_offset = gtt_offset;
        obj->has_global_gtt_mapping = 1;
 
-       list_add_tail(&obj->gtt_list, &dev_priv->mm.bound_list);
+       list_add_tail(&obj->global_list, &dev_priv->mm.bound_list);
        list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
 
        return obj;
index 0aa2ef0..3d92a7c 100644 (file)
@@ -70,15 +70,6 @@ static const u32 hpd_status_gen4[] = {
        [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
 };
 
-static const u32 hpd_status_i965[] = {
-        [HPD_CRT] = CRT_HOTPLUG_INT_STATUS,
-        [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I965,
-        [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_I965,
-        [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS,
-        [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS,
-        [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
-};
-
 static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */
        [HPD_CRT] = CRT_HOTPLUG_INT_STATUS,
        [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I915,
@@ -88,13 +79,12 @@ static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */
        [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
 };
 
-static void ibx_hpd_irq_setup(struct drm_device *dev);
-static void i915_hpd_irq_setup(struct drm_device *dev);
-
 /* For display hotplug interrupt */
 static void
 ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
 {
+       assert_spin_locked(&dev_priv->irq_lock);
+
        if ((dev_priv->irq_mask & mask) != 0) {
                dev_priv->irq_mask &= ~mask;
                I915_WRITE(DEIMR, dev_priv->irq_mask);
@@ -105,6 +95,8 @@ ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
 static void
 ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
 {
+       assert_spin_locked(&dev_priv->irq_lock);
+
        if ((dev_priv->irq_mask & mask) != mask) {
                dev_priv->irq_mask |= mask;
                I915_WRITE(DEIMR, dev_priv->irq_mask);
@@ -112,6 +104,215 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
        }
 }
 
+static bool ivb_can_enable_err_int(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *crtc;
+       enum pipe pipe;
+
+       assert_spin_locked(&dev_priv->irq_lock);
+
+       for_each_pipe(pipe) {
+               crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+
+               if (crtc->cpu_fifo_underrun_disabled)
+                       return false;
+       }
+
+       return true;
+}
+
+static bool cpt_can_enable_serr_int(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe;
+       struct intel_crtc *crtc;
+
+       for_each_pipe(pipe) {
+               crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+
+               if (crtc->pch_fifo_underrun_disabled)
+                       return false;
+       }
+
+       return true;
+}
+
+static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev,
+                                                enum pipe pipe, bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t bit = (pipe == PIPE_A) ? DE_PIPEA_FIFO_UNDERRUN :
+                                         DE_PIPEB_FIFO_UNDERRUN;
+
+       if (enable)
+               ironlake_enable_display_irq(dev_priv, bit);
+       else
+               ironlake_disable_display_irq(dev_priv, bit);
+}
+
+static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
+                                                 bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (enable) {
+               if (!ivb_can_enable_err_int(dev))
+                       return;
+
+               I915_WRITE(GEN7_ERR_INT, ERR_INT_FIFO_UNDERRUN_A |
+                                        ERR_INT_FIFO_UNDERRUN_B |
+                                        ERR_INT_FIFO_UNDERRUN_C);
+
+               ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
+       } else {
+               ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
+       }
+}
+
+static void ibx_set_fifo_underrun_reporting(struct intel_crtc *crtc,
+                                           bool enable)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t bit = (crtc->pipe == PIPE_A) ? SDE_TRANSA_FIFO_UNDER :
+                                               SDE_TRANSB_FIFO_UNDER;
+
+       if (enable)
+               I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~bit);
+       else
+               I915_WRITE(SDEIMR, I915_READ(SDEIMR) | bit);
+
+       POSTING_READ(SDEIMR);
+}
+
+static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
+                                           enum transcoder pch_transcoder,
+                                           bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (enable) {
+               if (!cpt_can_enable_serr_int(dev))
+                       return;
+
+               I915_WRITE(SERR_INT, SERR_INT_TRANS_A_FIFO_UNDERRUN |
+                                    SERR_INT_TRANS_B_FIFO_UNDERRUN |
+                                    SERR_INT_TRANS_C_FIFO_UNDERRUN);
+
+               I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~SDE_ERROR_CPT);
+       } else {
+               I915_WRITE(SDEIMR, I915_READ(SDEIMR) | SDE_ERROR_CPT);
+       }
+
+       POSTING_READ(SDEIMR);
+}
+
+/**
+ * intel_set_cpu_fifo_underrun_reporting - enable/disable FIFO underrun messages
+ * @dev: drm device
+ * @pipe: pipe
+ * @enable: true if we want to report FIFO underrun errors, false otherwise
+ *
+ * This function makes us disable or enable CPU fifo underruns for a specific
+ * pipe. Notice that on some Gens (e.g. IVB, HSW), disabling FIFO underrun
+ * reporting for one pipe may also disable all the other CPU error interruts for
+ * the other pipes, due to the fact that there's just one interrupt mask/enable
+ * bit for all the pipes.
+ *
+ * Returns the previous state of underrun reporting.
+ */
+bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
+                                          enum pipe pipe, bool enable)
+{
+       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);
+       unsigned long flags;
+       bool ret;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, flags);
+
+       ret = !intel_crtc->cpu_fifo_underrun_disabled;
+
+       if (enable == ret)
+               goto done;
+
+       intel_crtc->cpu_fifo_underrun_disabled = !enable;
+
+       if (IS_GEN5(dev) || IS_GEN6(dev))
+               ironlake_set_fifo_underrun_reporting(dev, pipe, enable);
+       else if (IS_GEN7(dev))
+               ivybridge_set_fifo_underrun_reporting(dev, enable);
+
+done:
+       spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+       return ret;
+}
+
+/**
+ * intel_set_pch_fifo_underrun_reporting - enable/disable FIFO underrun messages
+ * @dev: drm device
+ * @pch_transcoder: the PCH transcoder (same as pipe on IVB and older)
+ * @enable: true if we want to report FIFO underrun errors, false otherwise
+ *
+ * This function makes us disable or enable PCH fifo underruns for a specific
+ * PCH transcoder. Notice that on some PCHs (e.g. CPT/PPT), disabling FIFO
+ * underrun reporting for one transcoder may also disable all the other PCH
+ * error interruts for the other transcoders, due to the fact that there's just
+ * one interrupt mask/enable bit for all the transcoders.
+ *
+ * Returns the previous state of underrun reporting.
+ */
+bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
+                                          enum transcoder pch_transcoder,
+                                          bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe p;
+       struct drm_crtc *crtc;
+       struct intel_crtc *intel_crtc;
+       unsigned long flags;
+       bool ret;
+
+       if (HAS_PCH_LPT(dev)) {
+               crtc = NULL;
+               for_each_pipe(p) {
+                       struct drm_crtc *c = dev_priv->pipe_to_crtc_mapping[p];
+                       if (intel_pipe_has_type(c, INTEL_OUTPUT_ANALOG)) {
+                               crtc = c;
+                               break;
+                       }
+               }
+               if (!crtc) {
+                       DRM_ERROR("PCH FIFO underrun, but no CRTC using the PCH found\n");
+                       return false;
+               }
+       } else {
+               crtc = dev_priv->pipe_to_crtc_mapping[pch_transcoder];
+       }
+       intel_crtc = to_intel_crtc(crtc);
+
+       spin_lock_irqsave(&dev_priv->irq_lock, flags);
+
+       ret = !intel_crtc->pch_fifo_underrun_disabled;
+
+       if (enable == ret)
+               goto done;
+
+       intel_crtc->pch_fifo_underrun_disabled = !enable;
+
+       if (HAS_PCH_IBX(dev))
+               ibx_set_fifo_underrun_reporting(intel_crtc, enable);
+       else
+               cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable);
+
+done:
+       spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+       return ret;
+}
+
+
 void
 i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
 {
@@ -142,28 +343,21 @@ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
 }
 
 /**
- * intel_enable_asle - enable ASLE interrupt for OpRegion
+ * i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion
  */
-void intel_enable_asle(struct drm_device *dev)
+static void i915_enable_asle_pipestat(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        unsigned long irqflags;
 
-       /* FIXME: opregion/asle for VLV */
-       if (IS_VALLEYVIEW(dev))
+       if (!dev_priv->opregion.asle || !IS_MOBILE(dev))
                return;
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
 
-       if (HAS_PCH_SPLIT(dev))
-               ironlake_enable_display_irq(dev_priv, DE_GSE);
-       else {
-               i915_enable_pipestat(dev_priv, 1,
-                                    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, 1, PIPE_LEGACY_BLC_EVENT_ENABLE);
+       if (INTEL_INFO(dev)->gen >= 4)
+               i915_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE);
 
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
@@ -181,10 +375,16 @@ static int
 i915_pipe_enabled(struct drm_device *dev, int pipe)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
-                                                                     pipe);
 
-       return I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE;
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               /* Locking is horribly broken here, but whatever. */
+               struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+               return intel_crtc->active;
+       } else {
+               return I915_READ(PIPECONF(pipe)) & PIPECONF_ENABLE;
+       }
 }
 
 /* Called from drm generic code, passed a 'crtc', which
@@ -334,6 +534,21 @@ 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)
+{
+       enum drm_connector_status old_status;
+
+       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+       old_status = connector->status;
+
+       connector->status = connector->funcs->detect(connector, false);
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
+                     connector->base.id,
+                     drm_get_connector_name(connector),
+                     old_status, connector->status);
+       return (old_status != connector->status);
+}
+
 /*
  * Handle hotplug events outside the interrupt handler proper.
  */
@@ -350,6 +565,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
        struct drm_connector *connector;
        unsigned long irqflags;
        bool hpd_disabled = false;
+       bool changed = false;
+       u32 hpd_event_bits;
 
        /* HPD irq before everything is fully set up. */
        if (!dev_priv->enable_hotplug_processing)
@@ -359,6 +576,9 @@ static void i915_hotplug_work_func(struct work_struct *work)
        DRM_DEBUG_KMS("running encoder hotplug functions\n");
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+
+       hpd_event_bits = dev_priv->hpd_event_bits;
+       dev_priv->hpd_event_bits = 0;
        list_for_each_entry(connector, &mode_config->connector_list, head) {
                intel_connector = to_intel_connector(connector);
                intel_encoder = intel_connector->encoder;
@@ -373,6 +593,10 @@ static void i915_hotplug_work_func(struct work_struct *work)
                                | DRM_CONNECTOR_POLL_DISCONNECT;
                        hpd_disabled = true;
                }
+               if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
+                       DRM_DEBUG_KMS("Connector %s (pin %i) received hotplug event.\n",
+                                     drm_get_connector_name(connector), intel_encoder->hpd_pin);
+               }
        }
         /* if there were no outputs to poll, poll was disabled,
          * therefore make sure it's enabled when disabling HPD on
@@ -385,14 +609,20 @@ static void i915_hotplug_work_func(struct work_struct *work)
 
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 
-       list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head)
-               if (intel_encoder->hot_plug)
-                       intel_encoder->hot_plug(intel_encoder);
-
+       list_for_each_entry(connector, &mode_config->connector_list, head) {
+               intel_connector = to_intel_connector(connector);
+               intel_encoder = intel_connector->encoder;
+               if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
+                       if (intel_encoder->hot_plug)
+                               intel_encoder->hot_plug(intel_encoder);
+                       if (intel_hpd_irq_event(dev, connector))
+                               changed = true;
+               }
+       }
        mutex_unlock(&mode_config->mutex);
 
-       /* Just fire off a uevent and let userspace tell us what to do */
-       drm_helper_hpd_irq_event(dev);
+       if (changed)
+               drm_kms_helper_hotplug_event(dev);
 }
 
 static void ironlake_handle_rps_change(struct drm_device *dev)
@@ -447,7 +677,6 @@ static void notify_ring(struct drm_device *dev,
 
        wake_up_all(&ring->irq_queue);
        if (i915_enable_hangcheck) {
-               dev_priv->gpu_error.hangcheck_count = 0;
                mod_timer(&dev_priv->gpu_error.hangcheck_timer,
                          round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
        }
@@ -464,25 +693,48 @@ static void gen6_pm_rps_work(struct work_struct *work)
        pm_iir = dev_priv->rps.pm_iir;
        dev_priv->rps.pm_iir = 0;
        pm_imr = I915_READ(GEN6_PMIMR);
-       I915_WRITE(GEN6_PMIMR, 0);
+       /* Make sure not to corrupt PMIMR state used by ringbuffer code */
+       I915_WRITE(GEN6_PMIMR, pm_imr & ~GEN6_PM_RPS_EVENTS);
        spin_unlock_irq(&dev_priv->rps.lock);
 
-       if ((pm_iir & GEN6_PM_DEFERRED_EVENTS) == 0)
+       if ((pm_iir & GEN6_PM_RPS_EVENTS) == 0)
                return;
 
        mutex_lock(&dev_priv->rps.hw_lock);
 
-       if (pm_iir & GEN6_PM_RP_UP_THRESHOLD)
+       if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
                new_delay = dev_priv->rps.cur_delay + 1;
-       else
+
+               /*
+                * 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)
+                       new_delay = dev_priv->rps.rpe_delay;
+       } else
                new_delay = dev_priv->rps.cur_delay - 1;
 
        /* sysfs frequency interfaces may have snuck in while servicing the
         * interrupt
         */
-       if (!(new_delay > dev_priv->rps.max_delay ||
-             new_delay < dev_priv->rps.min_delay)) {
-               gen6_set_rps(dev_priv->dev, new_delay);
+       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));
        }
 
        mutex_unlock(&dev_priv->rps.hw_lock);
@@ -529,7 +781,7 @@ static void ivybridge_parity_work(struct work_struct *work)
        I915_WRITE(GEN7_MISCCPCTL, misccpctl);
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       dev_priv->gt_irq_mask &= ~GT_GEN7_L3_PARITY_ERROR_INTERRUPT;
+       dev_priv->gt_irq_mask &= ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
        I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
        spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
 
@@ -561,7 +813,7 @@ static void ivybridge_handle_parity_error(struct drm_device *dev)
                return;
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       dev_priv->gt_irq_mask |= GT_GEN7_L3_PARITY_ERROR_INTERRUPT;
+       dev_priv->gt_irq_mask |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
        I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
        spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
 
@@ -573,25 +825,26 @@ static void snb_gt_irq_handler(struct drm_device *dev,
                               u32 gt_iir)
 {
 
-       if (gt_iir & (GEN6_RENDER_USER_INTERRUPT |
-                     GEN6_RENDER_PIPE_CONTROL_NOTIFY_INTERRUPT))
+       if (gt_iir &
+           (GT_RENDER_USER_INTERRUPT | GT_RENDER_PIPECTL_NOTIFY_INTERRUPT))
                notify_ring(dev, &dev_priv->ring[RCS]);
-       if (gt_iir & GEN6_BSD_USER_INTERRUPT)
+       if (gt_iir & GT_BSD_USER_INTERRUPT)
                notify_ring(dev, &dev_priv->ring[VCS]);
-       if (gt_iir & GEN6_BLITTER_USER_INTERRUPT)
+       if (gt_iir & GT_BLT_USER_INTERRUPT)
                notify_ring(dev, &dev_priv->ring[BCS]);
 
-       if (gt_iir & (GT_GEN6_BLT_CS_ERROR_INTERRUPT |
-                     GT_GEN6_BSD_CS_ERROR_INTERRUPT |
-                     GT_RENDER_CS_ERROR_INTERRUPT)) {
+       if (gt_iir & (GT_BLT_CS_ERROR_INTERRUPT |
+                     GT_BSD_CS_ERROR_INTERRUPT |
+                     GT_RENDER_CS_MASTER_ERROR_INTERRUPT)) {
                DRM_ERROR("GT error interrupt 0x%08x\n", gt_iir);
                i915_handle_error(dev, false);
        }
 
-       if (gt_iir & GT_GEN7_L3_PARITY_ERROR_INTERRUPT)
+       if (gt_iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT)
                ivybridge_handle_parity_error(dev);
 }
 
+/* Legacy way of handling PM interrupts */
 static void gen6_queue_rps_work(struct drm_i915_private *dev_priv,
                                u32 pm_iir)
 {
@@ -619,23 +872,25 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv,
 #define HPD_STORM_DETECT_PERIOD 1000
 #define HPD_STORM_THRESHOLD 5
 
-static inline bool hotplug_irq_storm_detect(struct drm_device *dev,
-                                           u32 hotplug_trigger,
-                                           const u32 *hpd)
+static inline void intel_hpd_irq_handler(struct drm_device *dev,
+                                        u32 hotplug_trigger,
+                                        const u32 *hpd)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-       unsigned long irqflags;
        int i;
-       bool ret = false;
+       bool storm_detected = false;
 
-       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       if (!hotplug_trigger)
+               return;
 
+       spin_lock(&dev_priv->irq_lock);
        for (i = 1; i < HPD_NUM_PINS; i++) {
 
                if (!(hpd[i] & hotplug_trigger) ||
                    dev_priv->hpd_stats[i].hpd_mark != HPD_ENABLED)
                        continue;
 
+               dev_priv->hpd_event_bits |= (1 << i);
                if (!time_in_range(jiffies, dev_priv->hpd_stats[i].hpd_last_jiffies,
                                   dev_priv->hpd_stats[i].hpd_last_jiffies
                                   + msecs_to_jiffies(HPD_STORM_DETECT_PERIOD))) {
@@ -643,16 +898,20 @@ static inline bool hotplug_irq_storm_detect(struct drm_device *dev,
                        dev_priv->hpd_stats[i].hpd_cnt = 0;
                } else if (dev_priv->hpd_stats[i].hpd_cnt > HPD_STORM_THRESHOLD) {
                        dev_priv->hpd_stats[i].hpd_mark = HPD_MARK_DISABLED;
+                       dev_priv->hpd_event_bits &= ~(1 << i);
                        DRM_DEBUG_KMS("HPD interrupt storm detected on PIN %d\n", i);
-                       ret = true;
+                       storm_detected = true;
                } else {
                        dev_priv->hpd_stats[i].hpd_cnt++;
                }
        }
 
-       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+       if (storm_detected)
+               dev_priv->display.hpd_irq_setup(dev);
+       spin_unlock(&dev_priv->irq_lock);
 
-       return ret;
+       queue_work(dev_priv->wq,
+                  &dev_priv->hotplug_work);
 }
 
 static void gmbus_irq_handler(struct drm_device *dev)
@@ -669,6 +928,38 @@ static void dp_aux_irq_handler(struct drm_device *dev)
        wake_up_all(&dev_priv->gmbus_wait_queue);
 }
 
+/* Unlike gen6_queue_rps_work() from which this function is originally derived,
+ * we must be able to deal with other PM interrupts. This is complicated because
+ * of the way in which we use the masks to defer the RPS work (which for
+ * posterity is necessary because of forcewake).
+ */
+static void hsw_pm_irq_handler(struct drm_i915_private *dev_priv,
+                              u32 pm_iir)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->rps.lock, flags);
+       dev_priv->rps.pm_iir |= pm_iir & GEN6_PM_RPS_EVENTS;
+       if (dev_priv->rps.pm_iir) {
+               I915_WRITE(GEN6_PMIMR, dev_priv->rps.pm_iir);
+               /* never want to mask useful interrupts. (also posting read) */
+               WARN_ON(I915_READ_NOTRACE(GEN6_PMIMR) & ~GEN6_PM_RPS_EVENTS);
+               /* TODO: if queue_work is slow, move it out of the spinlock */
+               queue_work(dev_priv->wq, &dev_priv->rps.work);
+       }
+       spin_unlock_irqrestore(&dev_priv->rps.lock, flags);
+
+       if (pm_iir & ~GEN6_PM_RPS_EVENTS) {
+               if (pm_iir & PM_VEBOX_USER_INTERRUPT)
+                       notify_ring(dev_priv->dev, &dev_priv->ring[VECS]);
+
+               if (pm_iir & PM_VEBOX_CS_ERROR_INTERRUPT) {
+                       DRM_ERROR("VEBOX CS error interrupt 0x%08x\n", pm_iir);
+                       i915_handle_error(dev_priv->dev, false);
+               }
+       }
+}
+
 static irqreturn_t valleyview_irq_handler(int irq, void *arg)
 {
        struct drm_device *dev = (struct drm_device *) arg;
@@ -727,12 +1018,9 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
 
                        DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
                                         hotplug_status);
-                       if (hotplug_trigger) {
-                               if (hotplug_irq_storm_detect(dev, hotplug_trigger, hpd_status_i915))
-                                       i915_hpd_irq_setup(dev);
-                               queue_work(dev_priv->wq,
-                                          &dev_priv->hotplug_work);
-                       }
+
+                       intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915);
+
                        I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
                        I915_READ(PORT_HOTPLUG_STAT);
                }
@@ -740,7 +1028,7 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
                if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
                        gmbus_irq_handler(dev);
 
-               if (pm_iir & GEN6_PM_DEFERRED_EVENTS)
+               if (pm_iir & GEN6_PM_RPS_EVENTS)
                        gen6_queue_rps_work(dev_priv, pm_iir);
 
                I915_WRITE(GTIIR, gt_iir);
@@ -758,15 +1046,14 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
        int pipe;
        u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK;
 
-       if (hotplug_trigger) {
-               if (hotplug_irq_storm_detect(dev, hotplug_trigger, hpd_ibx))
-                       ibx_hpd_irq_setup(dev);
-               queue_work(dev_priv->wq, &dev_priv->hotplug_work);
-       }
-       if (pch_iir & SDE_AUDIO_POWER_MASK)
+       intel_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx);
+
+       if (pch_iir & SDE_AUDIO_POWER_MASK) {
+               int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >>
+                              SDE_AUDIO_POWER_SHIFT);
                DRM_DEBUG_DRIVER("PCH audio power change on port %d\n",
-                                (pch_iir & SDE_AUDIO_POWER_MASK) >>
-                                SDE_AUDIO_POWER_SHIFT);
+                                port_name(port));
+       }
 
        if (pch_iir & SDE_AUX_MASK)
                dp_aux_irq_handler(dev);
@@ -795,10 +1082,64 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
        if (pch_iir & (SDE_TRANSB_CRC_ERR | SDE_TRANSA_CRC_ERR))
                DRM_DEBUG_DRIVER("PCH transcoder CRC error interrupt\n");
 
-       if (pch_iir & SDE_TRANSB_FIFO_UNDER)
-               DRM_DEBUG_DRIVER("PCH transcoder B underrun interrupt\n");
        if (pch_iir & SDE_TRANSA_FIFO_UNDER)
-               DRM_DEBUG_DRIVER("PCH transcoder A underrun interrupt\n");
+               if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A,
+                                                         false))
+                       DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n");
+
+       if (pch_iir & SDE_TRANSB_FIFO_UNDER)
+               if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B,
+                                                         false))
+                       DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n");
+}
+
+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);
+
+       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");
+
+       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");
+
+       I915_WRITE(GEN7_ERR_INT, err_int);
+}
+
+static void cpt_serr_int_handler(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 serr_int = I915_READ(SERR_INT);
+
+       if (serr_int & SERR_INT_POISON)
+               DRM_ERROR("PCH poison interrupt\n");
+
+       if (serr_int & SERR_INT_TRANS_A_FIFO_UNDERRUN)
+               if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A,
+                                                         false))
+                       DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n");
+
+       if (serr_int & SERR_INT_TRANS_B_FIFO_UNDERRUN)
+               if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B,
+                                                         false))
+                       DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n");
+
+       if (serr_int & SERR_INT_TRANS_C_FIFO_UNDERRUN)
+               if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_C,
+                                                         false))
+                       DRM_DEBUG_DRIVER("PCH transcoder C FIFO underrun\n");
+
+       I915_WRITE(SERR_INT, serr_int);
 }
 
 static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
@@ -807,15 +1148,14 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
        int pipe;
        u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT;
 
-       if (hotplug_trigger) {
-               if (hotplug_irq_storm_detect(dev, hotplug_trigger, hpd_cpt))
-                       ibx_hpd_irq_setup(dev);
-               queue_work(dev_priv->wq, &dev_priv->hotplug_work);
+       intel_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt);
+
+       if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) {
+               int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >>
+                              SDE_AUDIO_POWER_SHIFT_CPT);
+               DRM_DEBUG_DRIVER("PCH audio power change on port %c\n",
+                                port_name(port));
        }
-       if (pch_iir & SDE_AUDIO_POWER_MASK_CPT)
-               DRM_DEBUG_DRIVER("PCH audio power change on port %d\n",
-                                (pch_iir & SDE_AUDIO_POWER_MASK_CPT) >>
-                                SDE_AUDIO_POWER_SHIFT_CPT);
 
        if (pch_iir & SDE_AUX_MASK_CPT)
                dp_aux_irq_handler(dev);
@@ -834,6 +1174,9 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
                        DRM_DEBUG_DRIVER("  pipe %c FDI IIR: 0x%08x\n",
                                         pipe_name(pipe),
                                         I915_READ(FDI_RX_IIR(pipe)));
+
+       if (pch_iir & SDE_ERROR_CPT)
+               cpt_serr_int_handler(dev);
 }
 
 static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
@@ -846,6 +1189,14 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
 
        atomic_inc(&dev_priv->irq_received);
 
+       /* We get interrupts on unclaimed registers, so check for this before we
+        * do any I915_{READ,WRITE}. */
+       if (IS_HASWELL(dev) &&
+           (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
+               DRM_ERROR("Unclaimed register before interrupt\n");
+               I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
+       }
+
        /* disable master interrupt before clearing iir  */
        de_ier = I915_READ(DEIER);
        I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
@@ -861,6 +1212,15 @@ static irqreturn_t ivybridge_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);
+               ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
+               spin_unlock(&dev_priv->irq_lock);
+       }
+
        gt_iir = I915_READ(GTIIR);
        if (gt_iir) {
                snb_gt_irq_handler(dev, dev_priv, gt_iir);
@@ -870,11 +1230,14 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
 
        de_iir = I915_READ(DEIIR);
        if (de_iir) {
+               if (de_iir & DE_ERR_INT_IVB)
+                       ivb_err_int_handler(dev);
+
                if (de_iir & DE_AUX_CHANNEL_A_IVB)
                        dp_aux_irq_handler(dev);
 
                if (de_iir & DE_GSE_IVB)
-                       intel_opregion_gse_intr(dev);
+                       intel_opregion_asle_intr(dev);
 
                for (i = 0; i < 3; i++) {
                        if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
@@ -901,12 +1264,21 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
 
        pm_iir = I915_READ(GEN6_PMIIR);
        if (pm_iir) {
-               if (pm_iir & GEN6_PM_DEFERRED_EVENTS)
+               if (IS_HASWELL(dev))
+                       hsw_pm_irq_handler(dev_priv, pm_iir);
+               else if (pm_iir & GEN6_PM_RPS_EVENTS)
                        gen6_queue_rps_work(dev_priv, pm_iir);
                I915_WRITE(GEN6_PMIIR, pm_iir);
                ret = IRQ_HANDLED;
        }
 
+       if (IS_HASWELL(dev)) {
+               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)) {
@@ -921,9 +1293,10 @@ static void ilk_gt_irq_handler(struct drm_device *dev,
                               struct drm_i915_private *dev_priv,
                               u32 gt_iir)
 {
-       if (gt_iir & (GT_USER_INTERRUPT | GT_PIPE_NOTIFY))
+       if (gt_iir &
+           (GT_RENDER_USER_INTERRUPT | GT_RENDER_PIPECTL_NOTIFY_INTERRUPT))
                notify_ring(dev, &dev_priv->ring[RCS]);
-       if (gt_iir & GT_BSD_USER_INTERRUPT)
+       if (gt_iir & ILK_BSD_USER_INTERRUPT)
                notify_ring(dev, &dev_priv->ring[VCS]);
 }
 
@@ -968,7 +1341,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
                dp_aux_irq_handler(dev);
 
        if (de_iir & DE_GSE)
-               intel_opregion_gse_intr(dev);
+               intel_opregion_asle_intr(dev);
 
        if (de_iir & DE_PIPEA_VBLANK)
                drm_handle_vblank(dev, 0);
@@ -976,6 +1349,17 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        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");
+
+       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_PLANEA_FLIP_DONE) {
                intel_prepare_page_flip(dev, 0);
                intel_finish_page_flip_plane(dev, 0);
@@ -1002,7 +1386,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        if (IS_GEN5(dev) &&  de_iir & DE_PCU_EVENT)
                ironlake_handle_rps_change(dev);
 
-       if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS)
+       if (IS_GEN6(dev) && pm_iir & GEN6_PM_RPS_EVENTS)
                gen6_queue_rps_work(dev_priv, pm_iir);
 
        I915_WRITE(GTIIR, gt_iir);
@@ -1222,11 +1606,13 @@ i915_error_state_free(struct kref *error_ref)
        for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
                i915_error_object_free(error->ring[i].batchbuffer);
                i915_error_object_free(error->ring[i].ringbuffer);
+               i915_error_object_free(error->ring[i].ctx);
                kfree(error->ring[i].requests);
        }
 
        kfree(error->active_bo);
        kfree(error->overlay);
+       kfree(error->display);
        kfree(error);
 }
 static void capture_bo(struct drm_i915_error_buffer *err,
@@ -1273,7 +1659,7 @@ static u32 capture_pinned_bo(struct drm_i915_error_buffer *err,
        struct drm_i915_gem_object *obj;
        int i = 0;
 
-       list_for_each_entry(obj, head, gtt_list) {
+       list_for_each_entry(obj, head, global_list) {
                if (obj->pin_count == 0)
                        continue;
 
@@ -1415,7 +1801,7 @@ static void i915_gem_record_active_context(struct intel_ring_buffer *ring,
        if (ring->id != RCS || !error->ccid)
                return;
 
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
                if ((error->ccid & PAGE_MASK) == obj->gtt_offset) {
                        ering->ctx = i915_error_object_create_sized(dev_priv,
                                                                    obj, 1);
@@ -1552,7 +1938,7 @@ static void i915_capture_error_state(struct drm_device *dev)
        list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list)
                i++;
        error->active_bo_count = i;
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list)
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list)
                if (obj->pin_count)
                        i++;
        error->pinned_bo_count = i - error->active_bo_count;
@@ -1932,38 +2318,28 @@ ring_last_seqno(struct intel_ring_buffer *ring)
                          struct drm_i915_gem_request, list)->seqno;
 }
 
-static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, bool *err)
+static bool
+ring_idle(struct intel_ring_buffer *ring, u32 seqno)
 {
-       if (list_empty(&ring->request_list) ||
-           i915_seqno_passed(ring->get_seqno(ring, false),
-                             ring_last_seqno(ring))) {
-               /* Issue a wake-up to catch stuck h/w. */
-               if (waitqueue_active(&ring->irq_queue)) {
-                       DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
-                                 ring->name);
-                       wake_up_all(&ring->irq_queue);
-                       *err = true;
-               }
-               return true;
-       }
-       return false;
+       return (list_empty(&ring->request_list) ||
+               i915_seqno_passed(seqno, ring_last_seqno(ring)));
 }
 
-static bool semaphore_passed(struct intel_ring_buffer *ring)
+static struct intel_ring_buffer *
+semaphore_waits_for(struct intel_ring_buffer *ring, u32 *seqno)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
-       u32 acthd = intel_ring_get_active_head(ring) & HEAD_ADDR;
-       struct intel_ring_buffer *signaller;
-       u32 cmd, ipehr, acthd_min;
+       u32 cmd, ipehr, acthd, acthd_min;
 
        ipehr = I915_READ(RING_IPEHR(ring->mmio_base));
        if ((ipehr & ~(0x3 << 16)) !=
            (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE | MI_SEMAPHORE_REGISTER))
-               return false;
+               return NULL;
 
        /* ACTHD is likely pointing to the dword after the actual command,
         * so scan backwards until we find the MBOX.
         */
+       acthd = intel_ring_get_active_head(ring) & HEAD_ADDR;
        acthd_min = max((int)acthd - 3 * 4, 0);
        do {
                cmd = ioread32(ring->virtual_start + acthd);
@@ -1972,124 +2348,216 @@ static bool semaphore_passed(struct intel_ring_buffer *ring)
 
                acthd -= 4;
                if (acthd < acthd_min)
-                       return false;
+                       return NULL;
        } while (1);
 
-       signaller = &dev_priv->ring[(ring->id + (((ipehr >> 17) & 1) + 1)) % 3];
-       return i915_seqno_passed(signaller->get_seqno(signaller, false),
-                                ioread32(ring->virtual_start+acthd+4)+1);
+       *seqno = ioread32(ring->virtual_start+acthd+4)+1;
+       return &dev_priv->ring[(ring->id + (((ipehr >> 17) & 1) + 1)) % 3];
 }
 
-static bool kick_ring(struct intel_ring_buffer *ring)
+static int semaphore_passed(struct intel_ring_buffer *ring)
 {
-       struct drm_device *dev = ring->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 tmp = I915_READ_CTL(ring);
-       if (tmp & RING_WAIT) {
-               DRM_ERROR("Kicking stuck wait on %s\n",
-                         ring->name);
-               I915_WRITE_CTL(ring, tmp);
-               return true;
-       }
+       struct drm_i915_private *dev_priv = ring->dev->dev_private;
+       struct intel_ring_buffer *signaller;
+       u32 seqno, ctl;
 
-       if (INTEL_INFO(dev)->gen >= 6 &&
-           tmp & RING_WAIT_SEMAPHORE &&
-           semaphore_passed(ring)) {
-               DRM_ERROR("Kicking stuck semaphore on %s\n",
-                         ring->name);
-               I915_WRITE_CTL(ring, tmp);
-               return true;
-       }
-       return false;
+       ring->hangcheck.deadlock = true;
+
+       signaller = semaphore_waits_for(ring, &seqno);
+       if (signaller == NULL || signaller->hangcheck.deadlock)
+               return -1;
+
+       /* cursory check for an unkickable deadlock */
+       ctl = I915_READ_CTL(signaller);
+       if (ctl & RING_WAIT_SEMAPHORE && semaphore_passed(signaller) < 0)
+               return -1;
+
+       return i915_seqno_passed(signaller->get_seqno(signaller, false), seqno);
 }
 
-static bool i915_hangcheck_hung(struct drm_device *dev)
+static void semaphore_clear_deadlocks(struct drm_i915_private *dev_priv)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
-
-       if (dev_priv->gpu_error.hangcheck_count++ > 1) {
-               bool hung = true;
+       struct intel_ring_buffer *ring;
+       int i;
 
-               DRM_ERROR("Hangcheck timer elapsed... GPU hung\n");
-               i915_handle_error(dev, true);
+       for_each_ring(ring, dev_priv, i)
+               ring->hangcheck.deadlock = false;
+}
 
-               if (!IS_GEN2(dev)) {
-                       struct intel_ring_buffer *ring;
-                       int i;
+static enum intel_ring_hangcheck_action
+ring_stuck(struct intel_ring_buffer *ring, u32 acthd)
+{
+       struct drm_device *dev = ring->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 tmp;
 
-                       /* Is the chip hanging on a WAIT_FOR_EVENT?
-                        * If so we can simply poke the RB_WAIT bit
-                        * and break the hang. This should work on
-                        * all but the second generation chipsets.
-                        */
-                       for_each_ring(ring, dev_priv, i)
-                               hung &= !kick_ring(ring);
-               }
+       if (ring->hangcheck.acthd != acthd)
+               return active;
 
+       if (IS_GEN2(dev))
                return hung;
+
+       /* Is the chip hanging on a WAIT_FOR_EVENT?
+        * If so we can simply poke the RB_WAIT bit
+        * and break the hang. This should work on
+        * all but the second generation chipsets.
+        */
+       tmp = I915_READ_CTL(ring);
+       if (tmp & RING_WAIT) {
+               DRM_ERROR("Kicking stuck wait on %s\n",
+                         ring->name);
+               I915_WRITE_CTL(ring, tmp);
+               return kick;
        }
 
-       return false;
+       if (INTEL_INFO(dev)->gen >= 6 && tmp & RING_WAIT_SEMAPHORE) {
+               switch (semaphore_passed(ring)) {
+               default:
+                       return hung;
+               case 1:
+                       DRM_ERROR("Kicking stuck semaphore on %s\n",
+                                 ring->name);
+                       I915_WRITE_CTL(ring, tmp);
+                       return kick;
+               case 0:
+                       return wait;
+               }
+       }
+
+       return hung;
 }
 
 /**
  * This is called when the chip hasn't reported back with completed
- * batchbuffers in a long time. The first time this is called we simply record
- * ACTHD. If ACTHD hasn't changed by the time the hangcheck timer elapses
- * again, we assume the chip is wedged and try to fix it.
+ * batchbuffers in a long time. We keep track per ring seqno progress and
+ * if there are no progress, hangcheck score for that ring is increased.
+ * Further, acthd is inspected to see if the ring is stuck. On stuck case
+ * we kick the ring. If we see no progress on three subsequent calls
+ * we assume chip is wedged and try to fix it by resetting the chip.
  */
 void i915_hangcheck_elapsed(unsigned long data)
 {
        struct drm_device *dev = (struct drm_device *)data;
        drm_i915_private_t *dev_priv = dev->dev_private;
-       uint32_t acthd[I915_NUM_RINGS], instdone[I915_NUM_INSTDONE_REG];
        struct intel_ring_buffer *ring;
-       bool err = false, idle;
        int i;
+       int busy_count = 0, rings_hung = 0;
+       bool stuck[I915_NUM_RINGS] = { 0 };
+#define BUSY 1
+#define KICK 5
+#define HUNG 20
+#define FIRE 30
 
        if (!i915_enable_hangcheck)
                return;
 
-       memset(acthd, 0, sizeof(acthd));
-       idle = true;
        for_each_ring(ring, dev_priv, i) {
-           idle &= i915_hangcheck_ring_idle(ring, &err);
-           acthd[i] = intel_ring_get_active_head(ring);
-       }
+               u32 seqno, acthd;
+               bool busy = true;
+
+               semaphore_clear_deadlocks(dev_priv);
+
+               seqno = ring->get_seqno(ring, false);
+               acthd = intel_ring_get_active_head(ring);
+
+               if (ring->hangcheck.seqno == seqno) {
+                       if (ring_idle(ring, seqno)) {
+                               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;
+                               } else
+                                       busy = false;
+                       } else {
+                               int score;
+
+                               /* We always increment the hangcheck score
+                                * if the ring is busy and still processing
+                                * the same request, so that no single request
+                                * can run indefinitely (such as a chain of
+                                * batches). The only time we do not increment
+                                * the hangcheck score on this ring, if this
+                                * ring is in a legitimate wait for another
+                                * ring. In that case the waiting ring is a
+                                * victim and we want to be sure we catch the
+                                * right culprit. Then every time we do kick
+                                * the ring, add a small increment to the
+                                * score so that we can catch a batch that is
+                                * being repeatedly kicked and so responsible
+                                * for stalling the machine.
+                                */
+                               ring->hangcheck.action = ring_stuck(ring,
+                                                                   acthd);
+
+                               switch (ring->hangcheck.action) {
+                               case wait:
+                                       score = 0;
+                                       break;
+                               case active:
+                                       score = BUSY;
+                                       break;
+                               case kick:
+                                       score = KICK;
+                                       break;
+                               case hung:
+                                       score = HUNG;
+                                       stuck[i] = true;
+                                       break;
+                               }
+                               ring->hangcheck.score += score;
+                       }
+               } else {
+                       /* Gradually reduce the count so that we catch DoS
+                        * attempts across multiple batches.
+                        */
+                       if (ring->hangcheck.score > 0)
+                               ring->hangcheck.score--;
+               }
 
-       /* If all work is done then ACTHD clearly hasn't advanced. */
-       if (idle) {
-               if (err) {
-                       if (i915_hangcheck_hung(dev))
-                               return;
+               ring->hangcheck.seqno = seqno;
+               ring->hangcheck.acthd = acthd;
+               busy_count += busy;
+       }
 
-                       goto repeat;
+       for_each_ring(ring, dev_priv, i) {
+               if (ring->hangcheck.score > FIRE) {
+                       DRM_ERROR("%s on %s\n",
+                                 stuck[i] ? "stuck" : "no progress",
+                                 ring->name);
+                       rings_hung++;
                }
-
-               dev_priv->gpu_error.hangcheck_count = 0;
-               return;
        }
 
-       i915_get_extra_instdone(dev, instdone);
-       if (memcmp(dev_priv->gpu_error.last_acthd, acthd,
-                  sizeof(acthd)) == 0 &&
-           memcmp(dev_priv->gpu_error.prev_instdone, instdone,
-                  sizeof(instdone)) == 0) {
-               if (i915_hangcheck_hung(dev))
-                       return;
-       } else {
-               dev_priv->gpu_error.hangcheck_count = 0;
+       if (rings_hung)
+               return i915_handle_error(dev, true);
 
-               memcpy(dev_priv->gpu_error.last_acthd, acthd,
-                      sizeof(acthd));
-               memcpy(dev_priv->gpu_error.prev_instdone, instdone,
-                      sizeof(instdone));
-       }
+       if (busy_count)
+               /* Reset timer case chip hangs without another request
+                * being added */
+               mod_timer(&dev_priv->gpu_error.hangcheck_timer,
+                         round_jiffies_up(jiffies +
+                                          DRM_I915_HANGCHECK_JIFFIES));
+}
+
+static void ibx_irq_preinstall(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (HAS_PCH_NOP(dev))
+               return;
 
-repeat:
-       /* Reset timer case chip hangs without another request being added */
-       mod_timer(&dev_priv->gpu_error.hangcheck_timer,
-                 round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
+       /* south display irq */
+       I915_WRITE(SDEIMR, 0xffffffff);
+       /*
+        * SDEIER is also touched by the interrupt handler to work around missed
+        * PCH interrupts. Hence we can't update it after the interrupt handler
+        * is enabled - instead we unconditionally enable all PCH interrupt
+        * sources here, but then only unmask them as needed with SDEIMR.
+        */
+       I915_WRITE(SDEIER, 0xffffffff);
+       POSTING_READ(SDEIER);
 }
 
 /* drm_dma.h hooks
@@ -2113,19 +2581,34 @@ static void ironlake_irq_preinstall(struct drm_device *dev)
        I915_WRITE(GTIER, 0x0);
        POSTING_READ(GTIER);
 
-       if (HAS_PCH_NOP(dev))
-               return;
+       ibx_irq_preinstall(dev);
+}
 
-       /* south display irq */
-       I915_WRITE(SDEIMR, 0xffffffff);
-       /*
-        * SDEIER is also touched by the interrupt handler to work around missed
-        * PCH interrupts. Hence we can't update it after the interrupt handler
-        * is enabled - instead we unconditionally enable all PCH interrupt
-        * sources here, but then only unmask them as needed with SDEIMR.
-        */
-       I915_WRITE(SDEIER, 0xffffffff);
-       POSTING_READ(SDEIER);
+static void ivybridge_irq_preinstall(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       atomic_set(&dev_priv->irq_received, 0);
+
+       I915_WRITE(HWSTAM, 0xeffe);
+
+       /* XXX hotplug from PCH */
+
+       I915_WRITE(DEIMR, 0xffffffff);
+       I915_WRITE(DEIER, 0x0);
+       POSTING_READ(DEIER);
+
+       /* and GT */
+       I915_WRITE(GTIMR, 0xffffffff);
+       I915_WRITE(GTIER, 0x0);
+       POSTING_READ(GTIER);
+
+       /* Power management */
+       I915_WRITE(GEN6_PMIMR, 0xffffffff);
+       I915_WRITE(GEN6_PMIER, 0x0);
+       POSTING_READ(GEN6_PMIER);
+
+       ibx_irq_preinstall(dev);
 }
 
 static void valleyview_irq_preinstall(struct drm_device *dev)
@@ -2201,33 +2684,41 @@ static void ibx_irq_postinstall(struct drm_device *dev)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 mask;
 
-       if (HAS_PCH_IBX(dev))
-               mask = SDE_GMBUS | SDE_AUX_MASK;
-       else
-               mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
-
        if (HAS_PCH_NOP(dev))
                return;
 
+       if (HAS_PCH_IBX(dev)) {
+               mask = SDE_GMBUS | SDE_AUX_MASK | SDE_TRANSB_FIFO_UNDER |
+                      SDE_TRANSA_FIFO_UNDER | SDE_POISON;
+       } else {
+               mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT | SDE_ERROR_CPT;
+
+               I915_WRITE(SERR_INT, I915_READ(SERR_INT));
+       }
+
        I915_WRITE(SDEIIR, I915_READ(SDEIIR));
        I915_WRITE(SDEIMR, ~mask);
 }
 
 static int ironlake_irq_postinstall(struct drm_device *dev)
 {
+       unsigned long irqflags;
+
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        /* enable kind of interrupts always enabled */
        u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
                           DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
-                          DE_AUX_CHANNEL_A;
-       u32 render_irqs;
+                          DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN |
+                          DE_PIPEA_FIFO_UNDERRUN | DE_POISON;
+       u32 gt_irqs;
 
        dev_priv->irq_mask = ~display_mask;
 
        /* should always can generate irq */
        I915_WRITE(DEIIR, I915_READ(DEIIR));
        I915_WRITE(DEIMR, dev_priv->irq_mask);
-       I915_WRITE(DEIER, display_mask | DE_PIPEA_VBLANK | DE_PIPEB_VBLANK);
+       I915_WRITE(DEIER, display_mask |
+                         DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT);
        POSTING_READ(DEIER);
 
        dev_priv->gt_irq_mask = ~0;
@@ -2235,26 +2726,28 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
        I915_WRITE(GTIIR, I915_READ(GTIIR));
        I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
 
+       gt_irqs = GT_RENDER_USER_INTERRUPT;
+
        if (IS_GEN6(dev))
-               render_irqs =
-                       GT_USER_INTERRUPT |
-                       GEN6_BSD_USER_INTERRUPT |
-                       GEN6_BLITTER_USER_INTERRUPT;
+               gt_irqs |= GT_BLT_USER_INTERRUPT | GT_BSD_USER_INTERRUPT;
        else
-               render_irqs =
-                       GT_USER_INTERRUPT |
-                       GT_PIPE_NOTIFY |
-                       GT_BSD_USER_INTERRUPT;
-       I915_WRITE(GTIER, render_irqs);
+               gt_irqs |= GT_RENDER_PIPECTL_NOTIFY_INTERRUPT |
+                          ILK_BSD_USER_INTERRUPT;
+
+       I915_WRITE(GTIER, gt_irqs);
        POSTING_READ(GTIER);
 
        ibx_irq_postinstall(dev);
 
        if (IS_IRONLAKE_M(dev)) {
-               /* Clear & enable PCU event interrupts */
-               I915_WRITE(DEIIR, DE_PCU_EVENT);
-               I915_WRITE(DEIER, I915_READ(DEIER) | DE_PCU_EVENT);
+               /* Enable PCU event interrupts
+                *
+                * spinlocking not required here for correctness since interrupt
+                * setup is guaranteed to run in single-threaded context. But we
+                * need it to make the assert_spin_locked happy. */
+               spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
                ironlake_enable_display_irq(dev_priv, DE_PCU_EVENT);
+               spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
        }
 
        return 0;
@@ -2269,12 +2762,15 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
                DE_PLANEC_FLIP_DONE_IVB |
                DE_PLANEB_FLIP_DONE_IVB |
                DE_PLANEA_FLIP_DONE_IVB |
-               DE_AUX_CHANNEL_A_IVB;
-       u32 render_irqs;
+               DE_AUX_CHANNEL_A_IVB |
+               DE_ERR_INT_IVB;
+       u32 pm_irqs = GEN6_PM_RPS_EVENTS;
+       u32 gt_irqs;
 
        dev_priv->irq_mask = ~display_mask;
 
        /* should always can generate irq */
+       I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
        I915_WRITE(DEIIR, I915_READ(DEIIR));
        I915_WRITE(DEIMR, dev_priv->irq_mask);
        I915_WRITE(DEIER,
@@ -2284,16 +2780,32 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
                   DE_PIPEA_VBLANK_IVB);
        POSTING_READ(DEIER);
 
-       dev_priv->gt_irq_mask = ~GT_GEN7_L3_PARITY_ERROR_INTERRUPT;
+       dev_priv->gt_irq_mask = ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
 
        I915_WRITE(GTIIR, I915_READ(GTIIR));
        I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
 
-       render_irqs = GT_USER_INTERRUPT | GEN6_BSD_USER_INTERRUPT |
-               GEN6_BLITTER_USER_INTERRUPT | GT_GEN7_L3_PARITY_ERROR_INTERRUPT;
-       I915_WRITE(GTIER, render_irqs);
+       gt_irqs = GT_RENDER_USER_INTERRUPT | GT_BSD_USER_INTERRUPT |
+                 GT_BLT_USER_INTERRUPT | GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
+       I915_WRITE(GTIER, gt_irqs);
        POSTING_READ(GTIER);
 
+       I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
+       if (HAS_VEBOX(dev))
+               pm_irqs |= PM_VEBOX_USER_INTERRUPT |
+                       PM_VEBOX_CS_ERROR_INTERRUPT;
+
+       /* Our enable/disable rps functions may touch these registers so
+        * make sure to set a known state for only the non-RPS bits.
+        * The RMW is extra paranoia since this should be called after being set
+        * to a known state in preinstall.
+        * */
+       I915_WRITE(GEN6_PMIMR,
+                  (I915_READ(GEN6_PMIMR) | ~GEN6_PM_RPS_EVENTS) & ~pm_irqs);
+       I915_WRITE(GEN6_PMIER,
+                  (I915_READ(GEN6_PMIER) & GEN6_PM_RPS_EVENTS) | pm_irqs);
+       POSTING_READ(GEN6_PMIER);
+
        ibx_irq_postinstall(dev);
 
        return 0;
@@ -2302,10 +2814,9 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
 static int valleyview_irq_postinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       u32 gt_irqs;
        u32 enable_mask;
        u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV;
-       u32 render_irqs;
-       u16 msid;
 
        enable_mask = I915_DISPLAY_PORT_INTERRUPT;
        enable_mask |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
@@ -2321,13 +2832,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
                I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
                I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
 
-       /* Hack for broken MSIs on VLV */
-       pci_write_config_dword(dev_priv->dev->pdev, 0x94, 0xfee00000);
-       pci_read_config_word(dev->pdev, 0x98, &msid);
-       msid &= 0xff; /* mask out delivery bits */
-       msid |= (1<<14);
-       pci_write_config_word(dev_priv->dev->pdev, 0x98, msid);
-
        I915_WRITE(PORT_HOTPLUG_EN, 0);
        POSTING_READ(PORT_HOTPLUG_EN);
 
@@ -2348,9 +2852,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        I915_WRITE(GTIIR, I915_READ(GTIIR));
        I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
 
-       render_irqs = GT_USER_INTERRUPT | GEN6_BSD_USER_INTERRUPT |
-               GEN6_BLITTER_USER_INTERRUPT;
-       I915_WRITE(GTIER, render_irqs);
+       gt_irqs = GT_RENDER_USER_INTERRUPT | GT_BSD_USER_INTERRUPT |
+               GT_BLT_USER_INTERRUPT;
+       I915_WRITE(GTIER, gt_irqs);
        POSTING_READ(GTIER);
 
        /* ack & enable invalid PTE error interrupts */
@@ -2402,6 +2906,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
        I915_WRITE(DEIMR, 0xffffffff);
        I915_WRITE(DEIER, 0x0);
        I915_WRITE(DEIIR, I915_READ(DEIIR));
+       if (IS_GEN7(dev))
+               I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
 
        I915_WRITE(GTIMR, 0xffffffff);
        I915_WRITE(GTIER, 0x0);
@@ -2413,6 +2919,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
        I915_WRITE(SDEIMR, 0xffffffff);
        I915_WRITE(SDEIER, 0x0);
        I915_WRITE(SDEIIR, I915_READ(SDEIIR));
+       if (HAS_PCH_CPT(dev) || HAS_PCH_LPT(dev))
+               I915_WRITE(SERR_INT, I915_READ(SERR_INT));
 }
 
 static void i8xx_irq_preinstall(struct drm_device * dev)
@@ -2626,7 +3134,7 @@ static int i915_irq_postinstall(struct drm_device *dev)
        I915_WRITE(IER, enable_mask);
        POSTING_READ(IER);
 
-       intel_opregion_enable_asle(dev);
+       i915_enable_asle_pipestat(dev);
 
        return 0;
 }
@@ -2715,12 +3223,9 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
 
                        DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
                                  hotplug_status);
-                       if (hotplug_trigger) {
-                               if (hotplug_irq_storm_detect(dev, hotplug_trigger, hpd_status_i915))
-                                       i915_hpd_irq_setup(dev);
-                               queue_work(dev_priv->wq,
-                                          &dev_priv->hotplug_work);
-                       }
+
+                       intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915);
+
                        I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
                        POSTING_READ(PORT_HOTPLUG_STAT);
                }
@@ -2860,7 +3365,7 @@ static int i965_irq_postinstall(struct drm_device *dev)
        I915_WRITE(PORT_HOTPLUG_EN, 0);
        POSTING_READ(PORT_HOTPLUG_EN);
 
-       intel_opregion_enable_asle(dev);
+       i915_enable_asle_pipestat(dev);
 
        return 0;
 }
@@ -2872,6 +3377,8 @@ static void i915_hpd_irq_setup(struct drm_device *dev)
        struct intel_encoder *intel_encoder;
        u32 hotplug_en;
 
+       assert_spin_locked(&dev_priv->irq_lock);
+
        if (I915_HAS_HOTPLUG(dev)) {
                hotplug_en = I915_READ(PORT_HOTPLUG_EN);
                hotplug_en &= ~HOTPLUG_INT_EN_MASK;
@@ -2952,17 +3459,14 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
                        u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
                        u32 hotplug_trigger = hotplug_status & (IS_G4X(dev) ?
                                                                  HOTPLUG_INT_STATUS_G4X :
-                                                                 HOTPLUG_INT_STATUS_I965);
+                                                                 HOTPLUG_INT_STATUS_I915);
 
                        DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
                                  hotplug_status);
-                       if (hotplug_trigger) {
-                               if (hotplug_irq_storm_detect(dev, hotplug_trigger,
-                                                           IS_G4X(dev) ? hpd_status_gen4 : hpd_status_i965))
-                                       i915_hpd_irq_setup(dev);
-                               queue_work(dev_priv->wq,
-                                          &dev_priv->hotplug_work);
-                       }
+
+                       intel_hpd_irq_handler(dev, hotplug_trigger,
+                                             IS_G4X(dev) ? hpd_status_gen4 : hpd_status_i915);
+
                        I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
                        I915_READ(PORT_HOTPLUG_STAT);
                }
@@ -3113,9 +3617,9 @@ void intel_irq_init(struct drm_device *dev)
                dev->driver->disable_vblank = valleyview_disable_vblank;
                dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
        } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) {
-               /* Share pre & uninstall handlers with ILK/SNB */
+               /* Share uninstall handlers with ILK/SNB */
                dev->driver->irq_handler = ivybridge_irq_handler;
-               dev->driver->irq_preinstall = ironlake_irq_preinstall;
+               dev->driver->irq_preinstall = ivybridge_irq_preinstall;
                dev->driver->irq_postinstall = ivybridge_irq_postinstall;
                dev->driver->irq_uninstall = ironlake_irq_uninstall;
                dev->driver->enable_vblank = ivybridge_enable_vblank;
@@ -3158,6 +3662,7 @@ void intel_hpd_init(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_mode_config *mode_config = &dev->mode_config;
        struct drm_connector *connector;
+       unsigned long irqflags;
        int i;
 
        for (i = 1; i < HPD_NUM_PINS; i++) {
@@ -3170,6 +3675,11 @@ void intel_hpd_init(struct drm_device *dev)
                if (!connector->polled && I915_HAS_HOTPLUG(dev) && intel_connector->encoder->hpd_pin > HPD_NONE)
                        connector->polled = DRM_CONNECTOR_POLL_HPD;
        }
+
+       /* Interrupt setup is already guaranteed to be single-threaded, this is
+        * just to make the assert_spin_locked checks happy. */
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
        if (dev_priv->display.hpd_irq_setup)
                dev_priv->display.hpd_irq_setup(dev);
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
index 2d6b62e..f2326fc 100644 (file)
 #define   VGA_MSR_MEM_EN (1<<1)
 #define   VGA_MSR_CGA_MODE (1<<0)
 
-/*
- * SR01 is the only VGA register touched on non-UMS setups.
- * VLV doesn't do UMS, so the sequencer index/data registers
- * are the only VGA registers which need to include
- * display_mmio_offset.
- */
-#define VGA_SR_INDEX (dev_priv->info->display_mmio_offset + 0x3c4)
+#define VGA_SR_INDEX 0x3c4
 #define SR01                   1
-#define VGA_SR_DATA (dev_priv->info->display_mmio_offset + 0x3c5)
+#define VGA_SR_DATA 0x3c5
 
 #define VGA_AR_INDEX 0x3c0
 #define   VGA_AR_VID_EN (1<<5)
 #define  MI_SEMAPHORE_UPDATE       (1<<21)
 #define  MI_SEMAPHORE_COMPARE      (1<<20)
 #define  MI_SEMAPHORE_REGISTER     (1<<18)
-#define  MI_SEMAPHORE_SYNC_RV      (2<<16)
-#define  MI_SEMAPHORE_SYNC_RB      (0<<16)
-#define  MI_SEMAPHORE_SYNC_VR      (0<<16)
-#define  MI_SEMAPHORE_SYNC_VB      (2<<16)
-#define  MI_SEMAPHORE_SYNC_BR      (2<<16)
-#define  MI_SEMAPHORE_SYNC_BV      (0<<16)
-#define  MI_SEMAPHORE_SYNC_INVALID  (1<<0)
+#define  MI_SEMAPHORE_SYNC_VR      (0<<16) /* RCS  wait for VCS  (RVSYNC) */
+#define  MI_SEMAPHORE_SYNC_VER     (1<<16) /* RCS  wait for VECS (RVESYNC) */
+#define  MI_SEMAPHORE_SYNC_BR      (2<<16) /* RCS  wait for BCS  (RBSYNC) */
+#define  MI_SEMAPHORE_SYNC_BV      (0<<16) /* VCS  wait for BCS  (VBSYNC) */
+#define  MI_SEMAPHORE_SYNC_VEV     (1<<16) /* VCS  wait for VECS (VVESYNC) */
+#define  MI_SEMAPHORE_SYNC_RV      (2<<16) /* VCS  wait for RCS  (VRSYNC) */
+#define  MI_SEMAPHORE_SYNC_RB      (0<<16) /* BCS  wait for RCS  (BRSYNC) */
+#define  MI_SEMAPHORE_SYNC_VEB     (1<<16) /* BCS  wait for VECS (BVESYNC) */
+#define  MI_SEMAPHORE_SYNC_VB      (2<<16) /* BCS  wait for VCS  (BVSYNC) */
+#define  MI_SEMAPHORE_SYNC_BVE     (0<<16) /* VECS wait for BCS  (VEBSYNC) */
+#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)
 /*
  * 3D instructions used by the kernel
  */
 #define  DEBUG_RESET_DISPLAY           (1<<9)
 
 /*
- * DPIO - a special bus for various display related registers to hide behind:
- *  0x800c: m1, m2, n, p1, p2, k dividers
- *  0x8014: REF and SFR select
- *  0x8014: N divider, VCO select
- *  0x801c/3c: core clock bits
- *  0x8048/68: low pass filter coefficients
- *  0x8100: fast clock controls
+ * IOSF sideband
+ */
+#define VLV_IOSF_DOORBELL_REQ                  (VLV_DISPLAY_BASE + 0x2100)
+#define   IOSF_DEVFN_SHIFT                     24
+#define   IOSF_OPCODE_SHIFT                    16
+#define   IOSF_PORT_SHIFT                      8
+#define   IOSF_BYTE_ENABLES_SHIFT              4
+#define   IOSF_BAR_SHIFT                       1
+#define   IOSF_SB_BUSY                         (1<<0)
+#define   IOSF_PORT_PUNIT                      0x4
+#define   IOSF_PORT_NC                         0x11
+#define   IOSF_PORT_DPIO                       0x12
+#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_GPU_LFM                      0xd3
+#define PUNIT_REG_GPU_FREQ_REQ                 0xd4
+#define PUNIT_REG_GPU_FREQ_STS                 0xd8
+#define PUNIT_REG_MEDIA_TURBO_FREQ_REQ         0xdc
+
+#define PUNIT_FUSE_BUS2                                0xf6 /* bits 47:40 */
+#define PUNIT_FUSE_BUS1                                0xf5 /* bits 55:48 */
+
+#define IOSF_NC_FB_GFX_FREQ_FUSE               0x1c
+#define   FB_GFX_MAX_FREQ_FUSE_SHIFT           3
+#define   FB_GFX_MAX_FREQ_FUSE_MASK            0x000007f8
+#define   FB_GFX_FGUARANTEED_FREQ_FUSE_SHIFT   11
+#define   FB_GFX_FGUARANTEED_FREQ_FUSE_MASK    0x0007f800
+#define IOSF_NC_FB_GFX_FMAX_FUSE_HI            0x34
+#define   FB_FMAX_VMIN_FREQ_HI_MASK            0x00000007
+#define IOSF_NC_FB_GFX_FMAX_FUSE_LO            0x30
+#define   FB_FMAX_VMIN_FREQ_LO_SHIFT           27
+#define   FB_FMAX_VMIN_FREQ_LO_MASK            0xf8000000
+
+/*
+ * DPIO - a special bus for various display related registers to hide behind
  *
  * DPIO is VLV only.
+ *
+ * Note: digital port B is DDI0, digital pot C is DDI1
  */
-#define DPIO_PKT                       (VLV_DISPLAY_BASE + 0x2100)
-#define  DPIO_RID                      (0<<24)
-#define  DPIO_OP_WRITE                 (1<<16)
-#define  DPIO_OP_READ                  (0<<16)
-#define  DPIO_PORTID                   (0x12<<8)
-#define  DPIO_BYTE                     (0xf<<4)
-#define  DPIO_BUSY                     (1<<0) /* status only */
-#define DPIO_DATA                      (VLV_DISPLAY_BASE + 0x2104)
-#define DPIO_REG                       (VLV_DISPLAY_BASE + 0x2108)
+#define DPIO_DEVFN                     0
+#define DPIO_OPCODE_REG_WRITE          1
+#define DPIO_OPCODE_REG_READ           0
+
 #define DPIO_CTL                       (VLV_DISPLAY_BASE + 0x2110)
 #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_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, \
+                                       _DPIO_TX3_SWING_CTL4_B)
+
+/*
+ * Per pipe/PLL DPIO regs
+ */
 #define _DPIO_DIV_A                    0x800c
 #define   DPIO_POST_DIV_SHIFT          (28) /* 3 bits */
+#define   DPIO_POST_DIV_DAC            0
+#define   DPIO_POST_DIV_HDMIDP         1 /* DAC 225-400M rate */
+#define   DPIO_POST_DIV_LVDS1          2
+#define   DPIO_POST_DIV_LVDS2          3
 #define   DPIO_K_SHIFT                 (24) /* 4 bits */
 #define   DPIO_P1_SHIFT                        (21) /* 3 bits */
 #define   DPIO_P2_SHIFT                        (16) /* 5 bits */
 #define _DPIO_CORE_CLK_B               0x803c
 #define DPIO_CORE_CLK(pipe) _PIPE(pipe, _DPIO_CORE_CLK_A, _DPIO_CORE_CLK_B)
 
-#define _DPIO_LFP_COEFF_A              0x8048
-#define _DPIO_LFP_COEFF_B              0x8068
-#define DPIO_LFP_COEFF(pipe) _PIPE(pipe, _DPIO_LFP_COEFF_A, _DPIO_LFP_COEFF_B)
+#define _DPIO_IREF_CTL_A               0x8040
+#define _DPIO_IREF_CTL_B               0x8060
+#define DPIO_IREF_CTL(pipe) _PIPE(pipe, _DPIO_IREF_CTL_A, _DPIO_IREF_CTL_B)
+
+#define DPIO_IREF_BCAST                        0xc044
+#define _DPIO_IREF_A                   0x8044
+#define _DPIO_IREF_B                   0x8064
+#define DPIO_IREF(pipe) _PIPE(pipe, _DPIO_IREF_A, _DPIO_IREF_B)
+
+#define _DPIO_PLL_CML_A                        0x804c
+#define _DPIO_PLL_CML_B                        0x806c
+#define DPIO_PLL_CML(pipe) _PIPE(pipe, _DPIO_PLL_CML_A, _DPIO_PLL_CML_B)
+
+#define _DPIO_LPF_COEFF_A              0x8048
+#define _DPIO_LPF_COEFF_B              0x8068
+#define DPIO_LPF_COEFF(pipe) _PIPE(pipe, _DPIO_LPF_COEFF_A, _DPIO_LPF_COEFF_B)
+
+#define DPIO_CALIBRATION               0x80ac
 
 #define DPIO_FASTCLK_DISABLE           0x8100
 
-#define DPIO_DATA_CHANNEL1             0x8220
-#define DPIO_DATA_CHANNEL2             0x8420
+/*
+ * Per DDI channel DPIO regs
+ */
+
+#define _DPIO_PCS_TX_0                 0x8200
+#define _DPIO_PCS_TX_1                 0x8400
+#define   DPIO_PCS_TX_LANE2_RESET      (1<<16)
+#define   DPIO_PCS_TX_LANE1_RESET      (1<<7)
+#define DPIO_PCS_TX(port) _PORT(port, _DPIO_PCS_TX_0, _DPIO_PCS_TX_1)
+
+#define _DPIO_PCS_CLK_0                        0x8204
+#define _DPIO_PCS_CLK_1                        0x8404
+#define   DPIO_PCS_CLK_CRI_RXEB_EIOS_EN        (1<<22)
+#define   DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN (1<<21)
+#define   DPIO_PCS_CLK_DATAWIDTH_SHIFT (6)
+#define   DPIO_PCS_CLK_SOFT_RESET      (1<<5)
+#define DPIO_PCS_CLK(port) _PORT(port, _DPIO_PCS_CLK_0, _DPIO_PCS_CLK_1)
+
+#define _DPIO_PCS_CTL_OVR1_A           0x8224
+#define _DPIO_PCS_CTL_OVR1_B           0x8424
+#define DPIO_PCS_CTL_OVER1(port) _PORT(port, _DPIO_PCS_CTL_OVR1_A, \
+                                      _DPIO_PCS_CTL_OVR1_B)
+
+#define _DPIO_PCS_STAGGER0_A           0x822c
+#define _DPIO_PCS_STAGGER0_B           0x842c
+#define DPIO_PCS_STAGGER0(port) _PORT(port, _DPIO_PCS_STAGGER0_A, \
+                                     _DPIO_PCS_STAGGER0_B)
+
+#define _DPIO_PCS_STAGGER1_A           0x8230
+#define _DPIO_PCS_STAGGER1_B           0x8430
+#define DPIO_PCS_STAGGER1(port) _PORT(port, _DPIO_PCS_STAGGER1_A, \
+                                     _DPIO_PCS_STAGGER1_B)
+
+#define _DPIO_PCS_CLOCKBUF0_A          0x8238
+#define _DPIO_PCS_CLOCKBUF0_B          0x8438
+#define DPIO_PCS_CLOCKBUF0(port) _PORT(port, _DPIO_PCS_CLOCKBUF0_A, \
+                                      _DPIO_PCS_CLOCKBUF0_B)
+
+#define _DPIO_PCS_CLOCKBUF8_A          0x825c
+#define _DPIO_PCS_CLOCKBUF8_B          0x845c
+#define DPIO_PCS_CLOCKBUF8(port) _PORT(port, _DPIO_PCS_CLOCKBUF8_A, \
+                                      _DPIO_PCS_CLOCKBUF8_B)
+
+#define _DPIO_TX_SWING_CTL2_A          0x8288
+#define _DPIO_TX_SWING_CTL2_B          0x8488
+#define DPIO_TX_SWING_CTL2(port) _PORT(port, _DPIO_TX_SWING_CTL2_A, \
+                                      _DPIO_TX_SWING_CTL2_B)
+
+#define _DPIO_TX_SWING_CTL3_A          0x828c
+#define _DPIO_TX_SWING_CTL3_B          0x848c
+#define DPIO_TX_SWING_CTL3(port) _PORT(port, _DPIO_TX_SWING_CTL3_A, \
+                                      _DPIO_TX_SWING_CTL3_B)
+
+#define _DPIO_TX_SWING_CTL4_A          0x8290
+#define _DPIO_TX_SWING_CTL4_B          0x8490
+#define DPIO_TX_SWING_CTL4(port) _PORT(port, _DPIO_TX_SWING_CTL4_A, \
+                                      _DPIO_TX_SWING_CTL4_B)
+
+#define _DPIO_TX_OCALINIT_0            0x8294
+#define _DPIO_TX_OCALINIT_1            0x8494
+#define   DPIO_TX_OCALINIT_EN          (1<<31)
+#define DPIO_TX_OCALINIT(port) _PORT(port, _DPIO_TX_OCALINIT_0, \
+                                    _DPIO_TX_OCALINIT_1)
+
+#define _DPIO_TX_CTL_0                 0x82ac
+#define _DPIO_TX_CTL_1                 0x84ac
+#define DPIO_TX_CTL(port) _PORT(port, _DPIO_TX_CTL_0, _DPIO_TX_CTL_1)
+
+#define _DPIO_TX_LANE_0                        0x82b8
+#define _DPIO_TX_LANE_1                        0x84b8
+#define DPIO_TX_LANE(port) _PORT(port, _DPIO_TX_LANE_0, _DPIO_TX_LANE_1)
+
+#define _DPIO_DATA_CHANNEL1            0x8220
+#define _DPIO_DATA_CHANNEL2            0x8420
+#define DPIO_DATA_CHANNEL(port) _PORT(port, _DPIO_DATA_CHANNEL1, _DPIO_DATA_CHANNEL2)
+
+#define _DPIO_PORT0_PCS0               0x0220
+#define _DPIO_PORT0_PCS1               0x0420
+#define _DPIO_PORT1_PCS2               0x2620
+#define _DPIO_PORT1_PCS3               0x2820
+#define DPIO_DATA_LANE_A(port) _PORT(port, _DPIO_PORT0_PCS0, _DPIO_PORT1_PCS2)
+#define DPIO_DATA_LANE_B(port) _PORT(port, _DPIO_PORT0_PCS1, _DPIO_PORT1_PCS3)
+#define DPIO_DATA_CHANNEL1              0x8220
+#define DPIO_DATA_CHANNEL2              0x8420
 
 /*
  * Fence registers
 #define RENDER_RING_BASE       0x02000
 #define BSD_RING_BASE          0x04000
 #define GEN6_BSD_RING_BASE     0x12000
+#define VEBOX_RING_BASE                0x1a000
 #define BLT_RING_BASE          0x22000
 #define RING_TAIL(base)                ((base)+0x30)
 #define RING_HEAD(base)                ((base)+0x34)
 #define RING_CTL(base)         ((base)+0x3c)
 #define RING_SYNC_0(base)      ((base)+0x40)
 #define RING_SYNC_1(base)      ((base)+0x44)
-#define GEN6_RVSYNC (RING_SYNC_0(RENDER_RING_BASE))
-#define GEN6_RBSYNC (RING_SYNC_1(RENDER_RING_BASE))
-#define GEN6_VRSYNC (RING_SYNC_1(GEN6_BSD_RING_BASE))
-#define GEN6_VBSYNC (RING_SYNC_0(GEN6_BSD_RING_BASE))
-#define GEN6_BRSYNC (RING_SYNC_0(BLT_RING_BASE))
-#define GEN6_BVSYNC (RING_SYNC_1(BLT_RING_BASE))
+#define RING_SYNC_2(base)      ((base)+0x48)
+#define GEN6_RVSYNC    (RING_SYNC_0(RENDER_RING_BASE))
+#define GEN6_RBSYNC    (RING_SYNC_1(RENDER_RING_BASE))
+#define GEN6_RVESYNC   (RING_SYNC_2(RENDER_RING_BASE))
+#define GEN6_VBSYNC    (RING_SYNC_0(GEN6_BSD_RING_BASE))
+#define GEN6_VRSYNC    (RING_SYNC_1(GEN6_BSD_RING_BASE))
+#define GEN6_VVESYNC   (RING_SYNC_2(GEN6_BSD_RING_BASE))
+#define GEN6_BRSYNC    (RING_SYNC_0(BLT_RING_BASE))
+#define GEN6_BVSYNC    (RING_SYNC_1(BLT_RING_BASE))
+#define GEN6_BVESYNC   (RING_SYNC_2(BLT_RING_BASE))
+#define GEN6_VEBSYNC   (RING_SYNC_0(VEBOX_RING_BASE))
+#define GEN6_VERSYNC   (RING_SYNC_1(VEBOX_RING_BASE))
+#define GEN6_VEVSYNC   (RING_SYNC_2(VEBOX_RING_BASE))
+#define GEN6_NOSYNC 0
 #define RING_MAX_IDLE(base)    ((base)+0x54)
 #define RING_HWS_PGA(base)     ((base)+0x80)
 #define RING_HWS_PGA_GEN6(base)        ((base)+0x2080)
 #define DONE_REG               0x40b0
 #define BSD_HWS_PGA_GEN7       (0x04180)
 #define BLT_HWS_PGA_GEN7       (0x04280)
+#define VEBOX_HWS_PGA_GEN7     (0x04380)
 #define RING_ACTHD(base)       ((base)+0x74)
 #define RING_NOPID(base)       ((base)+0x94)
 #define RING_IMR(base)         ((base)+0xa8)
 
 #define ERROR_GEN6     0x040a0
 #define GEN7_ERR_INT   0x44040
-#define   ERR_INT_MMIO_UNCLAIMED (1<<13)
+#define   ERR_INT_POISON               (1<<31)
+#define   ERR_INT_MMIO_UNCLAIMED       (1<<13)
+#define   ERR_INT_FIFO_UNDERRUN_C      (1<<6)
+#define   ERR_INT_FIFO_UNDERRUN_B      (1<<3)
+#define   ERR_INT_FIFO_UNDERRUN_A      (1<<0)
 
 #define FPGA_DBG               0x42300
 #define   FPGA_DBG_RM_NOCLAIM  (1<<31)
 #define VLV_IIR                (VLV_DISPLAY_BASE + 0x20a4)
 #define VLV_IMR                (VLV_DISPLAY_BASE + 0x20a8)
 #define VLV_ISR                (VLV_DISPLAY_BASE + 0x20ac)
-#define   I915_PIPE_CONTROL_NOTIFY_INTERRUPT           (1<<18)
-#define   I915_DISPLAY_PORT_INTERRUPT                  (1<<17)
-#define   I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT   (1<<15)
-#define   I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT     (1<<14) /* p-state */
-#define   I915_HWB_OOM_INTERRUPT                       (1<<13)
-#define   I915_SYNC_STATUS_INTERRUPT                   (1<<12)
-#define   I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT  (1<<11)
-#define   I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT  (1<<10)
-#define   I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT    (1<<9)
-#define   I915_DISPLAY_PLANE_C_FLIP_PENDING_INTERRUPT  (1<<8)
-#define   I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT         (1<<7)
-#define   I915_DISPLAY_PIPE_A_EVENT_INTERRUPT          (1<<6)
-#define   I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT         (1<<5)
-#define   I915_DISPLAY_PIPE_B_EVENT_INTERRUPT          (1<<4)
-#define   I915_DEBUG_INTERRUPT                         (1<<2)
-#define   I915_USER_INTERRUPT                          (1<<1)
-#define   I915_ASLE_INTERRUPT                          (1<<0)
-#define   I915_BSD_USER_INTERRUPT                      (1<<25)
+#define VLV_PCBR       (VLV_DISPLAY_BASE + 0x2120)
 #define   DISPLAY_PLANE_FLIP_PENDING(plane) (1<<(11-(plane))) /* A and B only */
 #define EIR            0x020b0
 #define EMR            0x020b4
 #define CACHE_MODE_1           0x7004 /* IVB+ */
 #define   PIXEL_SUBSPAN_COLLECT_OPT_DISABLE (1<<6)
 
-/* GEN6 interrupt control
- * Note that the per-ring interrupt bits do alias with the global interrupt bits
- * in GTIMR. */
-#define GEN6_RENDER_HWSTAM     0x2098
-#define GEN6_RENDER_IMR                0x20a8
-#define   GEN6_RENDER_CONTEXT_SWITCH_INTERRUPT         (1 << 8)
-#define   GEN6_RENDER_PPGTT_PAGE_FAULT                 (1 << 7)
-#define   GEN6_RENDER_TIMEOUT_COUNTER_EXPIRED          (1 << 6)
-#define   GEN6_RENDER_L3_PARITY_ERROR                  (1 << 5)
-#define   GEN6_RENDER_PIPE_CONTROL_NOTIFY_INTERRUPT    (1 << 4)
-#define   GEN6_RENDER_COMMAND_PARSER_MASTER_ERROR      (1 << 3)
-#define   GEN6_RENDER_SYNC_STATUS                      (1 << 2)
-#define   GEN6_RENDER_DEBUG_INTERRUPT                  (1 << 1)
-#define   GEN6_RENDER_USER_INTERRUPT                   (1 << 0)
-
-#define GEN6_BLITTER_HWSTAM    0x22098
-#define GEN6_BLITTER_IMR       0x220a8
-#define   GEN6_BLITTER_MI_FLUSH_DW_NOTIFY_INTERRUPT    (1 << 26)
-#define   GEN6_BLITTER_COMMAND_PARSER_MASTER_ERROR     (1 << 25)
-#define   GEN6_BLITTER_SYNC_STATUS                     (1 << 24)
-#define   GEN6_BLITTER_USER_INTERRUPT                  (1 << 22)
-
 #define GEN6_BLITTER_ECOSKPD   0x221d0
 #define   GEN6_BLITTER_LOCK_SHIFT                      16
 #define   GEN6_BLITTER_FBC_NOTIFY                      (1<<3)
 #define   GEN6_BSD_SLEEP_INDICATOR     (1 << 3)
 #define   GEN6_BSD_GO_INDICATOR                (1 << 4)
 
-#define GEN6_BSD_HWSTAM                        0x12098
-#define GEN6_BSD_IMR                   0x120a8
-#define   GEN6_BSD_USER_INTERRUPT      (1 << 12)
+/* On modern GEN architectures interrupt control consists of two sets
+ * of registers. The first set pertains to the ring generating the
+ * interrupt. The second control is for the functional block generating the
+ * interrupt. These are PM, GT, DE, etc.
+ *
+ * Luckily *knocks on wood* all the ring interrupt bits match up with the
+ * GT interrupt bits, so we don't need to duplicate the defines.
+ *
+ * These defines should cover us well from SNB->HSW with minor exceptions
+ * it can also work on ILK.
+ */
+#define GT_BLT_FLUSHDW_NOTIFY_INTERRUPT                (1 << 26)
+#define GT_BLT_CS_ERROR_INTERRUPT              (1 << 25)
+#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    (1 <<  5) /* !snb */
+#define GT_RENDER_PIPECTL_NOTIFY_INTERRUPT     (1 <<  4)
+#define GT_RENDER_CS_MASTER_ERROR_INTERRUPT    (1 <<  3)
+#define GT_RENDER_SYNC_STATUS_INTERRUPT                (1 <<  2)
+#define GT_RENDER_DEBUG_INTERRUPT              (1 <<  1)
+#define GT_RENDER_USER_INTERRUPT               (1 <<  0)
+
+#define PM_VEBOX_CS_ERROR_INTERRUPT            (1 << 12) /* hsw+ */
+#define PM_VEBOX_USER_INTERRUPT                        (1 << 10) /* hsw+ */
+
+/* These are all the "old" interrupts */
+#define ILK_BSD_USER_INTERRUPT                         (1<<5)
+#define I915_PIPE_CONTROL_NOTIFY_INTERRUPT             (1<<18)
+#define I915_DISPLAY_PORT_INTERRUPT                    (1<<17)
+#define I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT     (1<<15)
+#define I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT       (1<<14) /* p-state */
+#define I915_HWB_OOM_INTERRUPT                         (1<<13)
+#define I915_SYNC_STATUS_INTERRUPT                     (1<<12)
+#define I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT    (1<<11)
+#define I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT    (1<<10)
+#define I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT      (1<<9)
+#define I915_DISPLAY_PLANE_C_FLIP_PENDING_INTERRUPT    (1<<8)
+#define I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT           (1<<7)
+#define I915_DISPLAY_PIPE_A_EVENT_INTERRUPT            (1<<6)
+#define I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT           (1<<5)
+#define I915_DISPLAY_PIPE_B_EVENT_INTERRUPT            (1<<4)
+#define I915_DEBUG_INTERRUPT                           (1<<2)
+#define I915_USER_INTERRUPT                            (1<<1)
+#define I915_ASLE_INTERRUPT                            (1<<0)
+#define I915_BSD_USER_INTERRUPT                                (1 << 25)
 
 #define GEN6_BSD_RNCID                 0x12198
 
 #define   DPFC_CTL_EN          (1<<31)
 #define   DPFC_CTL_PLANEA      (0<<30)
 #define   DPFC_CTL_PLANEB      (1<<30)
+#define   IVB_DPFC_CTL_PLANE_SHIFT     (29)
 #define   DPFC_CTL_FENCE_EN    (1<<29)
+#define   IVB_DPFC_CTL_FENCE_EN        (1<<28)
 #define   DPFC_CTL_PERSISTENT_MODE     (1<<25)
 #define   DPFC_SR_EN           (1<<10)
 #define   DPFC_CTL_LIMIT_1X    (0<<6)
 #define ILK_DPFC_CHICKEN       0x43224
 #define ILK_FBC_RT_BASE                0x2128
 #define   ILK_FBC_RT_VALID     (1<<0)
+#define   SNB_FBC_FRONT_BUFFER (1<<1)
 
 #define ILK_DISPLAY_CHICKEN1   0x42000
 #define   ILK_FBCQ_DIS         (1<<22)
 #define   SNB_CPU_FENCE_ENABLE (1<<29)
 #define DPFC_CPU_FENCE_OFFSET  0x100104
 
+/* Framebuffer compression for Ivybridge */
+#define IVB_FBC_RT_BASE                        0x7020
+
+#define IPS_CTL                0x43408
+#define   IPS_ENABLE   (1 << 31)
+
+#define MSG_FBC_REND_STATE     0x50380
+#define   FBC_REND_NUKE                (1<<2)
+#define   FBC_REND_CACHE_CLEAN (1<<1)
+
+#define _HSW_PIPE_SLICE_CHICKEN_1_A    0x420B0
+#define _HSW_PIPE_SLICE_CHICKEN_1_B    0x420B4
+#define   HSW_BYPASS_FBC_QUEUE         (1<<22)
+#define HSW_PIPE_SLICE_CHICKEN_1(pipe) _PIPE(pipe, + \
+                                            _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   DPLL_FPA01_P1_POST_DIV_MASK  0x00ff0000 /* i915 */
 #define   DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW 0x00ff8000 /* Pineview */
 #define   DPLL_LOCK_VLV                        (1<<15)
+#define   DPLL_INTEGRATED_CRI_CLK_VLV  (1<<14)
 #define   DPLL_INTEGRATED_CLOCK_VLV    (1<<13)
+#define   DPLL_PORTC_READY_MASK                (0xf << 4)
+#define   DPLL_PORTB_READY_MASK                (0xf)
 
 #define   DPLL_FPA01_P1_POST_DIV_MASK_I830     0x001f0000
 /*
 #define  DSTATE_PLL_D3_OFF                     (1<<3)
 #define  DSTATE_GFX_CLOCK_GATING               (1<<1)
 #define  DSTATE_DOT_CLOCK_GATING               (1<<0)
-#define DSPCLK_GATE_D          0x6200
+#define DSPCLK_GATE_D  (dev_priv->info->display_mmio_offset + 0x6200)
 # define DPUNIT_B_CLOCK_GATE_DISABLE           (1 << 30) /* 965 */
 # define VSUNIT_CLOCK_GATE_DISABLE             (1 << 29) /* 965 */
 # define VRHUNIT_CLOCK_GATE_DISABLE            (1 << 28) /* 965 */
 #define FW_BLC_SELF_VLV                (VLV_DISPLAY_BASE + 0x6500)
 #define  FW_CSPWRDWNEN         (1<<15)
 
+#define MI_ARB_VLV             (VLV_DISPLAY_BASE + 0x6504)
+
 /*
  * Palette regs
  */
                                         GEN7_CXT_EXTENDED_SIZE(ctx_reg) + \
                                         GEN7_CXT_GT1_SIZE(ctx_reg) + \
                                         GEN7_CXT_VFSTATE_SIZE(ctx_reg))
-#define HSW_CXT_POWER_SIZE(ctx_reg)    ((ctx_reg >> 26) & 0x3f)
-#define HSW_CXT_RING_SIZE(ctx_reg)     ((ctx_reg >> 23) & 0x7)
-#define HSW_CXT_RENDER_SIZE(ctx_reg)   ((ctx_reg >> 15) & 0xff)
-#define HSW_CXT_TOTAL_SIZE(ctx_reg)    (HSW_CXT_POWER_SIZE(ctx_reg) + \
-                                        HSW_CXT_RING_SIZE(ctx_reg) + \
-                                        HSW_CXT_RENDER_SIZE(ctx_reg) + \
-                                        GEN7_CXT_VFSTATE_SIZE(ctx_reg))
-
+/* Haswell does have the CXT_SIZE register however it does not appear to be
+ * valid. Now, docs explain in dwords what is in the context object. The full
+ * size is 70720 bytes, however, the power context and execlist context will
+ * never be saved (power context is stored elsewhere, and execlists don't work
+ * on HSW) - so the final size is 66944 bytes, which rounds to 17 pages.
+ */
+#define HSW_CXT_TOTAL_SIZE             (17 * PAGE_SIZE)
 
 /*
  * Overlay regs
 /* SDVO is different across gen3/4 */
 #define   SDVOC_HOTPLUG_INT_STATUS_G4X         (1 << 3)
 #define   SDVOB_HOTPLUG_INT_STATUS_G4X         (1 << 2)
+/*
+ * Bspec seems to be seriously misleaded about the SDVO hpd bits on i965g/gm,
+ * since reality corrobates that they're the same as on gen3. But keep these
+ * bits here (and the comment!) to help any other lost wanderers back onto the
+ * right tracks.
+ */
 #define   SDVOC_HOTPLUG_INT_STATUS_I965                (3 << 4)
 #define   SDVOB_HOTPLUG_INT_STATUS_I965                (3 << 2)
 #define   SDVOC_HOTPLUG_INT_STATUS_I915                (1 << 7)
                                                 PORTC_HOTPLUG_INT_STATUS | \
                                                 PORTD_HOTPLUG_INT_STATUS)
 
-#define HOTPLUG_INT_STATUS_I965                        (CRT_HOTPLUG_INT_STATUS | \
-                                                SDVOB_HOTPLUG_INT_STATUS_I965 | \
-                                                SDVOC_HOTPLUG_INT_STATUS_I965 | \
-                                                PORTB_HOTPLUG_INT_STATUS | \
-                                                PORTC_HOTPLUG_INT_STATUS | \
-                                                PORTD_HOTPLUG_INT_STATUS)
-
 #define HOTPLUG_INT_STATUS_I915                        (CRT_HOTPLUG_INT_STATUS | \
                                                 SDVOB_HOTPLUG_INT_STATUS_I915 | \
                                                 SDVOC_HOTPLUG_INT_STATUS_I915 | \
 #define   BLM_PIPE_A                   (0 << 29)
 #define   BLM_PIPE_B                   (1 << 29)
 #define   BLM_PIPE_C                   (2 << 29) /* ivb + */
+#define   BLM_TRANSCODER_A             BLM_PIPE_A /* hsw */
+#define   BLM_TRANSCODER_B             BLM_PIPE_B
+#define   BLM_TRANSCODER_C             BLM_PIPE_C
+#define   BLM_TRANSCODER_EDP           (3 << 29)
 #define   BLM_PIPE(pipe)               ((pipe) << 29)
 #define   BLM_POLARITY_I965            (1 << 28) /* gen4 only */
 #define   BLM_PHASE_IN_INTERUPT_STATUS (1 << 26)
 #define   DP_PRE_EMPHASIS_SHIFT                22
 
 /* How many wires to use. I guess 3 was too hard */
-#define   DP_PORT_WIDTH_1              (0 << 19)
-#define   DP_PORT_WIDTH_2              (1 << 19)
-#define   DP_PORT_WIDTH_4              (3 << 19)
+#define   DP_PORT_WIDTH(width)         (((width) - 1) << 19)
 #define   DP_PORT_WIDTH_MASK           (7 << 19)
 
 /* Mystic DPCD version 1.1 special mode */
  * which is after the LUTs, so we want the bytes for our color format.
  * For our current usage, this is always 3, one byte for R, G and B.
  */
-#define _PIPEA_GMCH_DATA_M                     0x70050
-#define _PIPEB_GMCH_DATA_M                     0x71050
+#define _PIPEA_DATA_M_G4X      0x70050
+#define _PIPEB_DATA_M_G4X      0x71050
 
 /* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */
 #define  TU_SIZE(x)             (((x)-1) << 25) /* default size 64 */
+#define  TU_SIZE_SHIFT         25
 #define  TU_SIZE_MASK           (0x3f << 25)
 
 #define  DATA_LINK_M_N_MASK    (0xffffff)
 #define  DATA_LINK_N_MAX       (0x800000)
 
-#define _PIPEA_GMCH_DATA_N                     0x70054
-#define _PIPEB_GMCH_DATA_N                     0x71054
+#define _PIPEA_DATA_N_G4X      0x70054
+#define _PIPEB_DATA_N_G4X      0x71054
+#define   PIPE_GMCH_DATA_N_MASK                        (0xffffff)
 
 /*
  * Computing Link M and N values for the Display Port link
  * Attributes and VB-ID.
  */
 
-#define _PIPEA_DP_LINK_M                               0x70060
-#define _PIPEB_DP_LINK_M                               0x71060
+#define _PIPEA_LINK_M_G4X      0x70060
+#define _PIPEB_LINK_M_G4X      0x71060
+#define   PIPEA_DP_LINK_M_MASK                 (0xffffff)
 
-#define _PIPEA_DP_LINK_N                               0x70064
-#define _PIPEB_DP_LINK_N                               0x71064
+#define _PIPEA_LINK_N_G4X      0x70064
+#define _PIPEB_LINK_N_G4X      0x71064
+#define   PIPEA_DP_LINK_N_MASK                 (0xffffff)
 
-#define PIPE_GMCH_DATA_M(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_M, _PIPEB_GMCH_DATA_M)
-#define PIPE_GMCH_DATA_N(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_N, _PIPEB_GMCH_DATA_N)
-#define PIPE_DP_LINK_M(pipe) _PIPE(pipe, _PIPEA_DP_LINK_M, _PIPEB_DP_LINK_M)
-#define PIPE_DP_LINK_N(pipe) _PIPE(pipe, _PIPEA_DP_LINK_N, _PIPEB_DP_LINK_N)
+#define PIPE_DATA_M_G4X(pipe) _PIPE(pipe, _PIPEA_DATA_M_G4X, _PIPEB_DATA_M_G4X)
+#define PIPE_DATA_N_G4X(pipe) _PIPE(pipe, _PIPEA_DATA_N_G4X, _PIPEB_DATA_N_G4X)
+#define PIPE_LINK_M_G4X(pipe) _PIPE(pipe, _PIPEA_LINK_M_G4X, _PIPEB_LINK_M_G4X)
+#define PIPE_LINK_N_G4X(pipe) _PIPE(pipe, _PIPEA_LINK_N_G4X, _PIPEB_LINK_N_G4X)
 
 /* Display & cursor control */
 
 #define   PIPECONF_INTERLACED_ILK              (3 << 21)
 #define   PIPECONF_INTERLACED_DBL_ILK          (4 << 21) /* ilk/snb only */
 #define   PIPECONF_PFIT_PF_INTERLACED_DBL_ILK  (5 << 21) /* ilk/snb only */
+#define   PIPECONF_INTERLACE_MODE_MASK         (7 << 21)
 #define   PIPECONF_CXSR_DOWNCLOCK      (1<<16)
 #define   PIPECONF_COLOR_RANGE_SELECT  (1 << 13)
 #define   PIPECONF_BPC_MASK    (0x7 << 5)
 #define WM3S_LP_IVB            0x45128
 #define  WM1S_LP_EN            (1<<31)
 
+#define HSW_WM_LP_VAL(lat, fbc, pri, cur) \
+       (WM3_LP_EN | ((lat) << WM1_LP_LATENCY_SHIFT) | \
+        ((fbc) << WM1_LP_FBC_SHIFT) | ((pri) << WM1_LP_SR_SHIFT) | (cur))
+
 /* Memory latency timer register */
 #define MLTR_ILK               0x11222
 #define  MLTR_WM1_SHIFT                0
 #define SPRGAMC(pipe) _PIPE(pipe, _SPRA_GAMC, _SPRB_GAMC)
 #define SPRSURFLIVE(pipe) _PIPE(pipe, _SPRA_SURFLIVE, _SPRB_SURFLIVE)
 
-#define _SPACNTR               0x72180
+#define _SPACNTR               (VLV_DISPLAY_BASE + 0x72180)
 #define   SP_ENABLE                    (1<<31)
 #define   SP_GEAMMA_ENABLE             (1<<30)
 #define   SP_PIXFORMAT_MASK            (0xf<<26)
 #define   SP_YUV_ORDER_YVYU            (2<<16)
 #define   SP_YUV_ORDER_VYUY            (3<<16)
 #define   SP_TILED                     (1<<10)
-#define _SPALINOFF             0x72184
-#define _SPASTRIDE             0x72188
-#define _SPAPOS                        0x7218c
-#define _SPASIZE               0x72190
-#define _SPAKEYMINVAL          0x72194
-#define _SPAKEYMSK             0x72198
-#define _SPASURF               0x7219c
-#define _SPAKEYMAXVAL          0x721a0
-#define _SPATILEOFF            0x721a4
-#define _SPACONSTALPHA         0x721a8
-#define _SPAGAMC               0x721f4
-
-#define _SPBCNTR               0x72280
-#define _SPBLINOFF             0x72284
-#define _SPBSTRIDE             0x72288
-#define _SPBPOS                        0x7228c
-#define _SPBSIZE               0x72290
-#define _SPBKEYMINVAL          0x72294
-#define _SPBKEYMSK             0x72298
-#define _SPBSURF               0x7229c
-#define _SPBKEYMAXVAL          0x722a0
-#define _SPBTILEOFF            0x722a4
-#define _SPBCONSTALPHA         0x722a8
-#define _SPBGAMC               0x722f4
+#define _SPALINOFF             (VLV_DISPLAY_BASE + 0x72184)
+#define _SPASTRIDE             (VLV_DISPLAY_BASE + 0x72188)
+#define _SPAPOS                        (VLV_DISPLAY_BASE + 0x7218c)
+#define _SPASIZE               (VLV_DISPLAY_BASE + 0x72190)
+#define _SPAKEYMINVAL          (VLV_DISPLAY_BASE + 0x72194)
+#define _SPAKEYMSK             (VLV_DISPLAY_BASE + 0x72198)
+#define _SPASURF               (VLV_DISPLAY_BASE + 0x7219c)
+#define _SPAKEYMAXVAL          (VLV_DISPLAY_BASE + 0x721a0)
+#define _SPATILEOFF            (VLV_DISPLAY_BASE + 0x721a4)
+#define _SPACONSTALPHA         (VLV_DISPLAY_BASE + 0x721a8)
+#define _SPAGAMC               (VLV_DISPLAY_BASE + 0x721f4)
+
+#define _SPBCNTR               (VLV_DISPLAY_BASE + 0x72280)
+#define _SPBLINOFF             (VLV_DISPLAY_BASE + 0x72284)
+#define _SPBSTRIDE             (VLV_DISPLAY_BASE + 0x72288)
+#define _SPBPOS                        (VLV_DISPLAY_BASE + 0x7228c)
+#define _SPBSIZE               (VLV_DISPLAY_BASE + 0x72290)
+#define _SPBKEYMINVAL          (VLV_DISPLAY_BASE + 0x72294)
+#define _SPBKEYMSK             (VLV_DISPLAY_BASE + 0x72298)
+#define _SPBSURF               (VLV_DISPLAY_BASE + 0x7229c)
+#define _SPBKEYMAXVAL          (VLV_DISPLAY_BASE + 0x722a0)
+#define _SPBTILEOFF            (VLV_DISPLAY_BASE + 0x722a4)
+#define _SPBCONSTALPHA         (VLV_DISPLAY_BASE + 0x722a8)
+#define _SPBGAMC               (VLV_DISPLAY_BASE + 0x722f4)
 
 #define SPCNTR(pipe, plane) _PIPE(pipe * 2 + plane, _SPACNTR, _SPBCNTR)
 #define SPLINOFF(pipe, plane) _PIPE(pipe * 2 + plane, _SPALINOFF, _SPBLINOFF)
 #define _LGC_PALETTE_B           0x4a800
 #define LGC_PALETTE(pipe) _PIPE(pipe, _LGC_PALETTE_A, _LGC_PALETTE_B)
 
+#define _GAMMA_MODE_A          0x4a480
+#define _GAMMA_MODE_B          0x4ac80
+#define GAMMA_MODE(pipe) _PIPE(pipe, _GAMMA_MODE_A, _GAMMA_MODE_B)
+#define GAMMA_MODE_MODE_MASK   (3 << 0)
+#define GAMMA_MODE_MODE_8BIT   (0 << 0)
+#define GAMMA_MODE_MODE_10BIT  (1 << 0)
+#define GAMMA_MODE_MODE_12BIT  (2 << 0)
+#define GAMMA_MODE_MODE_SPLIT  (3 << 0)
+
 /* interrupts */
 #define DE_MASTER_IRQ_CONTROL   (1 << 31)
 #define DE_SPRITEB_FLIP_DONE    (1 << 29)
 #define DE_PIPEA_FIFO_UNDERRUN  (1 << 0)
 
 /* More Ivybridge lolz */
-#define DE_ERR_DEBUG_IVB               (1<<30)
+#define DE_ERR_INT_IVB                 (1<<30)
 #define DE_GSE_IVB                     (1<<29)
 #define DE_PCH_EVENT_IVB               (1<<28)
 #define DE_DP_A_HOTPLUG_IVB            (1<<27)
 #define DEIIR   0x44008
 #define DEIER   0x4400c
 
-/* GT interrupt.
- * Note that for gen6+ the ring-specific interrupt bits do alias with the
- * corresponding bits in the per-ring interrupt control registers. */
-#define GT_GEN6_BLT_FLUSHDW_NOTIFY_INTERRUPT   (1 << 26)
-#define GT_GEN6_BLT_CS_ERROR_INTERRUPT         (1 << 25)
-#define GT_GEN6_BLT_USER_INTERRUPT             (1 << 22)
-#define GT_GEN6_BSD_CS_ERROR_INTERRUPT         (1 << 15)
-#define GT_GEN6_BSD_USER_INTERRUPT             (1 << 12)
-#define GT_BSD_USER_INTERRUPT                  (1 << 5) /* ilk only */
-#define GT_GEN7_L3_PARITY_ERROR_INTERRUPT      (1 << 5)
-#define GT_PIPE_NOTIFY                         (1 << 4)
-#define GT_RENDER_CS_ERROR_INTERRUPT           (1 << 3)
-#define GT_SYNC_STATUS                         (1 << 2)
-#define GT_USER_INTERRUPT                      (1 << 0)
-
 #define GTISR   0x44010
 #define GTIMR   0x44014
 #define GTIIR   0x44018
 # define CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE     (1 << 5)
 # define CHICKEN3_DGMG_DONE_FIX_DISABLE                (1 << 2)
 
+#define CHICKEN_PAR1_1         0x42080
+#define  FORCE_ARB_IDLE_PLANES (1 << 14)
+
 #define DISP_ARB_CTL   0x45000
 #define  DISP_TILE_SURFACE_SWIZZLING   (1<<13)
 #define  DISP_FBC_WM_DIS               (1<<15)
                                 SDE_PORTC_HOTPLUG_CPT |        \
                                 SDE_PORTB_HOTPLUG_CPT)
 #define SDE_GMBUS_CPT          (1 << 17)
+#define SDE_ERROR_CPT          (1 << 16)
 #define SDE_AUDIO_CP_REQ_C_CPT (1 << 10)
 #define SDE_AUDIO_CP_CHG_C_CPT (1 << 9)
 #define SDE_FDI_RXC_CPT                (1 << 8)
 #define SDEIIR  0xc4008
 #define SDEIER  0xc400c
 
+#define SERR_INT                       0xc4040
+#define  SERR_INT_POISON               (1<<31)
+#define  SERR_INT_TRANS_C_FIFO_UNDERRUN        (1<<6)
+#define  SERR_INT_TRANS_B_FIFO_UNDERRUN        (1<<3)
+#define  SERR_INT_TRANS_A_FIFO_UNDERRUN        (1<<0)
+
 /* digital port hotplug */
 #define PCH_PORT_HOTPLUG        0xc4030                /* SHOTPLUG_CTL */
 #define PORTD_HOTPLUG_ENABLE            (1 << 20)
 
 #define _PCH_DPLL_A              0xc6014
 #define _PCH_DPLL_B              0xc6018
-#define _PCH_DPLL(pll) (pll == 0 ? _PCH_DPLL_A : _PCH_DPLL_B)
+#define PCH_DPLL(pll) (pll == 0 ? _PCH_DPLL_A : _PCH_DPLL_B)
 
 #define _PCH_FPA0                0xc6040
 #define  FP_CB_TUNE            (0x3<<22)
 #define _PCH_FPA1                0xc6044
 #define _PCH_FPB0                0xc6048
 #define _PCH_FPB1                0xc604c
-#define _PCH_FP0(pll) (pll == 0 ? _PCH_FPA0 : _PCH_FPB0)
-#define _PCH_FP1(pll) (pll == 0 ? _PCH_FPA1 : _PCH_FPB1)
+#define PCH_FP0(pll) (pll == 0 ? _PCH_FPA0 : _PCH_FPB0)
+#define PCH_FP1(pll) (pll == 0 ? _PCH_FPA1 : _PCH_FPB1)
 
 #define PCH_DPLL_TEST           0xc606c
 
 #define PCH_SSC4_AUX_PARMS      0xc6214
 
 #define PCH_DPLL_SEL           0xc7000
-#define  TRANSA_DPLL_ENABLE    (1<<3)
-#define         TRANSA_DPLLB_SEL       (1<<0)
-#define         TRANSA_DPLLA_SEL       0
-#define  TRANSB_DPLL_ENABLE    (1<<7)
-#define         TRANSB_DPLLB_SEL       (1<<4)
-#define         TRANSB_DPLLA_SEL       (0)
-#define  TRANSC_DPLL_ENABLE    (1<<11)
-#define         TRANSC_DPLLB_SEL       (1<<8)
-#define         TRANSC_DPLLA_SEL       (0)
+#define         TRANS_DPLLB_SEL(pipe)          (1 << (pipe * 4))
+#define         TRANS_DPLLA_SEL(pipe)          0
+#define  TRANS_DPLL_ENABLE(pipe)       (1 << (pipe * 4 + 3))
 
 /* transcoder */
 
-#define _TRANS_HTOTAL_A          0xe0000
-#define  TRANS_HTOTAL_SHIFT     16
-#define  TRANS_HACTIVE_SHIFT    0
-#define _TRANS_HBLANK_A          0xe0004
-#define  TRANS_HBLANK_END_SHIFT 16
-#define  TRANS_HBLANK_START_SHIFT 0
-#define _TRANS_HSYNC_A           0xe0008
-#define  TRANS_HSYNC_END_SHIFT  16
-#define  TRANS_HSYNC_START_SHIFT 0
-#define _TRANS_VTOTAL_A          0xe000c
-#define  TRANS_VTOTAL_SHIFT     16
-#define  TRANS_VACTIVE_SHIFT    0
-#define _TRANS_VBLANK_A          0xe0010
-#define  TRANS_VBLANK_END_SHIFT 16
-#define  TRANS_VBLANK_START_SHIFT 0
-#define _TRANS_VSYNC_A           0xe0014
-#define  TRANS_VSYNC_END_SHIFT  16
-#define  TRANS_VSYNC_START_SHIFT 0
-#define _TRANS_VSYNCSHIFT_A    0xe0028
-
-#define _TRANSA_DATA_M1          0xe0030
-#define _TRANSA_DATA_N1          0xe0034
-#define _TRANSA_DATA_M2          0xe0038
-#define _TRANSA_DATA_N2          0xe003c
-#define _TRANSA_DP_LINK_M1       0xe0040
-#define _TRANSA_DP_LINK_N1       0xe0044
-#define _TRANSA_DP_LINK_M2       0xe0048
-#define _TRANSA_DP_LINK_N2       0xe004c
+#define _PCH_TRANS_HTOTAL_A            0xe0000
+#define  TRANS_HTOTAL_SHIFT            16
+#define  TRANS_HACTIVE_SHIFT           0
+#define _PCH_TRANS_HBLANK_A            0xe0004
+#define  TRANS_HBLANK_END_SHIFT                16
+#define  TRANS_HBLANK_START_SHIFT      0
+#define _PCH_TRANS_HSYNC_A             0xe0008
+#define  TRANS_HSYNC_END_SHIFT         16
+#define  TRANS_HSYNC_START_SHIFT       0
+#define _PCH_TRANS_VTOTAL_A            0xe000c
+#define  TRANS_VTOTAL_SHIFT            16
+#define  TRANS_VACTIVE_SHIFT           0
+#define _PCH_TRANS_VBLANK_A            0xe0010
+#define  TRANS_VBLANK_END_SHIFT                16
+#define  TRANS_VBLANK_START_SHIFT      0
+#define _PCH_TRANS_VSYNC_A             0xe0014
+#define  TRANS_VSYNC_END_SHIFT         16
+#define  TRANS_VSYNC_START_SHIFT       0
+#define _PCH_TRANS_VSYNCSHIFT_A                0xe0028
+
+#define _PCH_TRANSA_DATA_M1    0xe0030
+#define _PCH_TRANSA_DATA_N1    0xe0034
+#define _PCH_TRANSA_DATA_M2    0xe0038
+#define _PCH_TRANSA_DATA_N2    0xe003c
+#define _PCH_TRANSA_LINK_M1    0xe0040
+#define _PCH_TRANSA_LINK_N1    0xe0044
+#define _PCH_TRANSA_LINK_M2    0xe0048
+#define _PCH_TRANSA_LINK_N2    0xe004c
 
 /* Per-transcoder DIP controls */
 
 #define HSW_TVIDEO_DIP_VSC_DATA(trans) \
         _TRANSCODER(trans, HSW_VIDEO_DIP_VSC_DATA_A, HSW_VIDEO_DIP_VSC_DATA_B)
 
-#define _TRANS_HTOTAL_B          0xe1000
-#define _TRANS_HBLANK_B          0xe1004
-#define _TRANS_HSYNC_B           0xe1008
-#define _TRANS_VTOTAL_B          0xe100c
-#define _TRANS_VBLANK_B          0xe1010
-#define _TRANS_VSYNC_B           0xe1014
-#define _TRANS_VSYNCSHIFT_B     0xe1028
-
-#define TRANS_HTOTAL(pipe) _PIPE(pipe, _TRANS_HTOTAL_A, _TRANS_HTOTAL_B)
-#define TRANS_HBLANK(pipe) _PIPE(pipe, _TRANS_HBLANK_A, _TRANS_HBLANK_B)
-#define TRANS_HSYNC(pipe) _PIPE(pipe, _TRANS_HSYNC_A, _TRANS_HSYNC_B)
-#define TRANS_VTOTAL(pipe) _PIPE(pipe, _TRANS_VTOTAL_A, _TRANS_VTOTAL_B)
-#define TRANS_VBLANK(pipe) _PIPE(pipe, _TRANS_VBLANK_A, _TRANS_VBLANK_B)
-#define TRANS_VSYNC(pipe) _PIPE(pipe, _TRANS_VSYNC_A, _TRANS_VSYNC_B)
-#define TRANS_VSYNCSHIFT(pipe) _PIPE(pipe, _TRANS_VSYNCSHIFT_A, \
-                                    _TRANS_VSYNCSHIFT_B)
-
-#define _TRANSB_DATA_M1          0xe1030
-#define _TRANSB_DATA_N1          0xe1034
-#define _TRANSB_DATA_M2          0xe1038
-#define _TRANSB_DATA_N2          0xe103c
-#define _TRANSB_DP_LINK_M1       0xe1040
-#define _TRANSB_DP_LINK_N1       0xe1044
-#define _TRANSB_DP_LINK_M2       0xe1048
-#define _TRANSB_DP_LINK_N2       0xe104c
-
-#define TRANSDATA_M1(pipe) _PIPE(pipe, _TRANSA_DATA_M1, _TRANSB_DATA_M1)
-#define TRANSDATA_N1(pipe) _PIPE(pipe, _TRANSA_DATA_N1, _TRANSB_DATA_N1)
-#define TRANSDATA_M2(pipe) _PIPE(pipe, _TRANSA_DATA_M2, _TRANSB_DATA_M2)
-#define TRANSDATA_N2(pipe) _PIPE(pipe, _TRANSA_DATA_N2, _TRANSB_DATA_N2)
-#define TRANSDPLINK_M1(pipe) _PIPE(pipe, _TRANSA_DP_LINK_M1, _TRANSB_DP_LINK_M1)
-#define TRANSDPLINK_N1(pipe) _PIPE(pipe, _TRANSA_DP_LINK_N1, _TRANSB_DP_LINK_N1)
-#define TRANSDPLINK_M2(pipe) _PIPE(pipe, _TRANSA_DP_LINK_M2, _TRANSB_DP_LINK_M2)
-#define TRANSDPLINK_N2(pipe) _PIPE(pipe, _TRANSA_DP_LINK_N2, _TRANSB_DP_LINK_N2)
-
-#define _TRANSACONF              0xf0008
-#define _TRANSBCONF              0xf1008
-#define TRANSCONF(plane) _PIPE(plane, _TRANSACONF, _TRANSBCONF)
+#define _PCH_TRANS_HTOTAL_B          0xe1000
+#define _PCH_TRANS_HBLANK_B          0xe1004
+#define _PCH_TRANS_HSYNC_B           0xe1008
+#define _PCH_TRANS_VTOTAL_B          0xe100c
+#define _PCH_TRANS_VBLANK_B          0xe1010
+#define _PCH_TRANS_VSYNC_B           0xe1014
+#define _PCH_TRANS_VSYNCSHIFT_B         0xe1028
+
+#define PCH_TRANS_HTOTAL(pipe) _PIPE(pipe, _PCH_TRANS_HTOTAL_A, _PCH_TRANS_HTOTAL_B)
+#define PCH_TRANS_HBLANK(pipe) _PIPE(pipe, _PCH_TRANS_HBLANK_A, _PCH_TRANS_HBLANK_B)
+#define PCH_TRANS_HSYNC(pipe) _PIPE(pipe, _PCH_TRANS_HSYNC_A, _PCH_TRANS_HSYNC_B)
+#define PCH_TRANS_VTOTAL(pipe) _PIPE(pipe, _PCH_TRANS_VTOTAL_A, _PCH_TRANS_VTOTAL_B)
+#define PCH_TRANS_VBLANK(pipe) _PIPE(pipe, _PCH_TRANS_VBLANK_A, _PCH_TRANS_VBLANK_B)
+#define PCH_TRANS_VSYNC(pipe) _PIPE(pipe, _PCH_TRANS_VSYNC_A, _PCH_TRANS_VSYNC_B)
+#define PCH_TRANS_VSYNCSHIFT(pipe) _PIPE(pipe, _PCH_TRANS_VSYNCSHIFT_A, \
+                                        _PCH_TRANS_VSYNCSHIFT_B)
+
+#define _PCH_TRANSB_DATA_M1    0xe1030
+#define _PCH_TRANSB_DATA_N1    0xe1034
+#define _PCH_TRANSB_DATA_M2    0xe1038
+#define _PCH_TRANSB_DATA_N2    0xe103c
+#define _PCH_TRANSB_LINK_M1    0xe1040
+#define _PCH_TRANSB_LINK_N1    0xe1044
+#define _PCH_TRANSB_LINK_M2    0xe1048
+#define _PCH_TRANSB_LINK_N2    0xe104c
+
+#define PCH_TRANS_DATA_M1(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_M1, _PCH_TRANSB_DATA_M1)
+#define PCH_TRANS_DATA_N1(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_N1, _PCH_TRANSB_DATA_N1)
+#define PCH_TRANS_DATA_M2(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_M2, _PCH_TRANSB_DATA_M2)
+#define PCH_TRANS_DATA_N2(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_N2, _PCH_TRANSB_DATA_N2)
+#define PCH_TRANS_LINK_M1(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_M1, _PCH_TRANSB_LINK_M1)
+#define PCH_TRANS_LINK_N1(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_N1, _PCH_TRANSB_LINK_N1)
+#define PCH_TRANS_LINK_M2(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_M2, _PCH_TRANSB_LINK_M2)
+#define PCH_TRANS_LINK_N2(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_N2, _PCH_TRANSB_LINK_N2)
+
+#define _PCH_TRANSACONF              0xf0008
+#define _PCH_TRANSBCONF              0xf1008
+#define PCH_TRANSCONF(pipe) _PIPE(pipe, _PCH_TRANSACONF, _PCH_TRANSBCONF)
+#define LPT_TRANSCONF          _PCH_TRANSACONF /* lpt has only one transcoder */
 #define  TRANS_DISABLE          (0<<31)
 #define  TRANS_ENABLE           (1<<31)
 #define  TRANS_STATE_MASK       (1<<30)
 #define  FDI_LINK_TRAIN_600MV_3_5DB_SNB_B      (0x39<<22)
 #define  FDI_LINK_TRAIN_800MV_0DB_SNB_B                (0x38<<22)
 #define  FDI_LINK_TRAIN_VOL_EMP_MASK           (0x3f<<22)
-#define  FDI_DP_PORT_WIDTH_X1           (0<<19)
-#define  FDI_DP_PORT_WIDTH_X2           (1<<19)
-#define  FDI_DP_PORT_WIDTH_X3           (2<<19)
-#define  FDI_DP_PORT_WIDTH_X4           (3<<19)
+#define  FDI_DP_PORT_WIDTH_SHIFT               19
+#define  FDI_DP_PORT_WIDTH_MASK                        (7 << FDI_DP_PORT_WIDTH_SHIFT)
+#define  FDI_DP_PORT_WIDTH(width)           (((width) - 1) << FDI_DP_PORT_WIDTH_SHIFT)
 #define  FDI_TX_ENHANCE_FRAME_ENABLE    (1<<18)
 /* Ironlake: hardwired to 1 */
 #define  FDI_TX_PLL_ENABLE              (1<<14)
 /* train, dp width same as FDI_TX */
 #define  FDI_FS_ERRC_ENABLE            (1<<27)
 #define  FDI_FE_ERRC_ENABLE            (1<<26)
-#define  FDI_DP_PORT_WIDTH_X8           (7<<19)
 #define  FDI_RX_POLARITY_REVERSED_LPT  (1<<16)
 #define  FDI_8BPC                       (0<<16)
 #define  FDI_10BPC                      (1<<16)
 #define  FDI_LINK_TRAIN_PATTERN_IDLE_CPT       (2<<8)
 #define  FDI_LINK_TRAIN_NORMAL_CPT             (3<<8)
 #define  FDI_LINK_TRAIN_PATTERN_MASK_CPT       (3<<8)
-/* LPT */
-#define  FDI_PORT_WIDTH_2X_LPT                 (1<<19)
-#define  FDI_PORT_WIDTH_1X_LPT                 (0<<19)
 
 #define _FDI_RXA_MISC                  0xf0010
 #define _FDI_RXB_MISC                  0xf1010
 #define   GEN6_RC_CTL_RC6_ENABLE               (1<<18)
 #define   GEN6_RC_CTL_RC1e_ENABLE              (1<<20)
 #define   GEN6_RC_CTL_RC7_ENABLE               (1<<22)
+#define   GEN7_RC_CTL_TO_MODE                  (1<<28)
 #define   GEN6_RC_CTL_EI_MODE(x)               ((x)<<27)
 #define   GEN6_RC_CTL_HW_ENABLE                        (1<<31)
 #define GEN6_RP_DOWN_TIMEOUT                   0xA010
 #define  GEN6_PM_RP_DOWN_THRESHOLD             (1<<4)
 #define  GEN6_PM_RP_UP_EI_EXPIRED              (1<<2)
 #define  GEN6_PM_RP_DOWN_EI_EXPIRED            (1<<1)
-#define  GEN6_PM_DEFERRED_EVENTS               (GEN6_PM_RP_UP_THRESHOLD | \
+#define  GEN6_PM_RPS_EVENTS                    (GEN6_PM_RP_UP_THRESHOLD | \
                                                 GEN6_PM_RP_DOWN_THRESHOLD | \
                                                 GEN6_PM_RP_DOWN_TIMEOUT)
 
 #define   GEN6_PCODE_FREQ_IA_RATIO_SHIFT       8
 #define   GEN6_PCODE_FREQ_RING_RATIO_SHIFT     16
 
-#define VLV_IOSF_DOORBELL_REQ                  0x182100
-#define   IOSF_DEVFN_SHIFT                     24
-#define   IOSF_OPCODE_SHIFT                    16
-#define   IOSF_PORT_SHIFT                      8
-#define   IOSF_BYTE_ENABLES_SHIFT              4
-#define   IOSF_BAR_SHIFT                       1
-#define   IOSF_SB_BUSY                         (1<<0)
-#define   IOSF_PORT_PUNIT                      0x4
-#define VLV_IOSF_DATA                          0x182104
-#define VLV_IOSF_ADDR                          0x182108
-
-#define PUNIT_OPCODE_REG_READ                  6
-#define PUNIT_OPCODE_REG_WRITE                 7
-
 #define GEN6_GT_CORE_STATUS            0x138060
 #define   GEN6_CORE_CPD_STATE_MASK     (7<<4)
 #define   GEN6_RCn_MASK                        7
 #define  TRANS_DDI_EDP_INPUT_B_ONOFF   (5<<12)
 #define  TRANS_DDI_EDP_INPUT_C_ONOFF   (6<<12)
 #define  TRANS_DDI_BFI_ENABLE          (1<<4)
-#define  TRANS_DDI_PORT_WIDTH_X1       (0<<1)
-#define  TRANS_DDI_PORT_WIDTH_X2       (1<<1)
-#define  TRANS_DDI_PORT_WIDTH_X4       (3<<1)
 
 /* DisplayPort Transport Control */
 #define DP_TP_CTL_A                    0x64040
 #define  DDI_BUF_PORT_REVERSAL                 (1<<16)
 #define  DDI_BUF_IS_IDLE                       (1<<7)
 #define  DDI_A_4_LANES                         (1<<4)
-#define  DDI_PORT_WIDTH_X1                     (0<<1)
-#define  DDI_PORT_WIDTH_X2                     (1<<1)
-#define  DDI_PORT_WIDTH_X4                     (3<<1)
+#define  DDI_PORT_WIDTH(width)                 (((width) - 1) << 1)
 #define  DDI_INIT_DISPLAY_DETECTED             (1<<0)
 
 /* DDI Buffer Translations */
 #define  SFUSE_STRAP_DDIC_DETECTED     (1<<1)
 #define  SFUSE_STRAP_DDID_DETECTED     (1<<0)
 
+#define WM_MISC                                0x45260
+#define  WM_MISC_DATA_PARTITION_5_6    (1 << 0)
+
 #define WM_DBG                         0x45280
 #define  WM_DBG_DISALLOW_MULTIPLE_LP   (1<<0)
 #define  WM_DBG_DISALLOW_MAXFIFO       (1<<1)
 #define _PIPE_A_CSC_COEFF_RV_GV        0x49020
 #define _PIPE_A_CSC_COEFF_BV   0x49024
 #define _PIPE_A_CSC_MODE       0x49028
+#define   CSC_BLACK_SCREEN_OFFSET      (1 << 2)
+#define   CSC_POSITION_BEFORE_GAMMA    (1 << 1)
+#define   CSC_MODE_YUV_TO_RGB          (1 << 0)
 #define _PIPE_A_CSC_PREOFF_HI  0x49030
 #define _PIPE_A_CSC_PREOFF_ME  0x49034
 #define _PIPE_A_CSC_PREOFF_LO  0x49038
 #define _PIPE_B_CSC_POSTOFF_ME 0x49144
 #define _PIPE_B_CSC_POSTOFF_LO 0x49148
 
-#define CSC_BLACK_SCREEN_OFFSET (1 << 2)
-#define CSC_POSITION_BEFORE_GAMMA (1 << 1)
-#define CSC_MODE_YUV_TO_RGB (1 << 0)
-
 #define PIPE_CSC_COEFF_RY_GY(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RY_GY, _PIPE_B_CSC_COEFF_RY_GY)
 #define PIPE_CSC_COEFF_BY(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_BY, _PIPE_B_CSC_COEFF_BY)
 #define PIPE_CSC_COEFF_RU_GU(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RU_GU, _PIPE_B_CSC_COEFF_RU_GU)
index 369b3d8..70db618 100644 (file)
@@ -192,6 +192,7 @@ static void i915_restore_vga(struct drm_device *dev)
 static void i915_save_display(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long flags;
 
        /* Display arbitration control */
        if (INTEL_INFO(dev)->gen <= 4)
@@ -202,6 +203,8 @@ static void i915_save_display(struct drm_device *dev)
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                i915_save_display_reg(dev);
 
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
+
        /* LVDS state */
        if (HAS_PCH_SPLIT(dev)) {
                dev_priv->regfile.savePP_CONTROL = I915_READ(PCH_PP_CONTROL);
@@ -222,6 +225,8 @@ static void i915_save_display(struct drm_device *dev)
                        dev_priv->regfile.saveLVDS = I915_READ(LVDS);
        }
 
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
+
        if (!IS_I830(dev) && !IS_845G(dev) && !HAS_PCH_SPLIT(dev))
                dev_priv->regfile.savePFIT_CONTROL = I915_READ(PFIT_CONTROL);
 
@@ -257,6 +262,7 @@ static void i915_restore_display(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 mask = 0xffffffff;
+       unsigned long flags;
 
        /* Display arbitration */
        if (INTEL_INFO(dev)->gen <= 4)
@@ -265,6 +271,8 @@ static void i915_restore_display(struct drm_device *dev)
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                i915_restore_display_reg(dev);
 
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
+
        /* LVDS state */
        if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
                I915_WRITE(BLC_PWM_CTL2, dev_priv->regfile.saveBLC_PWM_CTL2);
@@ -304,6 +312,8 @@ static void i915_restore_display(struct drm_device *dev)
                I915_WRITE(PP_CONTROL, dev_priv->regfile.savePP_CONTROL);
        }
 
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
+
        /* only restore FBC info on the platform that supports FBC*/
        intel_disable_fbc(dev);
        if (I915_HAS_FBC(dev)) {
index d5e1890..6875b56 100644 (file)
@@ -212,7 +212,13 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev,
        int ret;
 
        mutex_lock(&dev_priv->rps.hw_lock);
-       ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER;
+       if (IS_VALLEYVIEW(dev_priv->dev)) {
+               u32 freq;
+               freq = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
+               ret = vlv_gpu_freq(dev_priv->mem_freq, (freq >> 8) & 0xff);
+       } else {
+               ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER;
+       }
        mutex_unlock(&dev_priv->rps.hw_lock);
 
        return snprintf(buf, PAGE_SIZE, "%d\n", ret);
@@ -226,7 +232,10 @@ static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute
        int ret;
 
        mutex_lock(&dev_priv->rps.hw_lock);
-       ret = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER;
+       if (IS_VALLEYVIEW(dev_priv->dev))
+               ret = vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.max_delay);
+       else
+               ret = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER;
        mutex_unlock(&dev_priv->rps.hw_lock);
 
        return snprintf(buf, PAGE_SIZE, "%d\n", ret);
@@ -246,16 +255,25 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev,
        if (ret)
                return ret;
 
-       val /= GT_FREQUENCY_MULTIPLIER;
-
        mutex_lock(&dev_priv->rps.hw_lock);
 
-       rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
-       hw_max = dev_priv->rps.hw_max;
-       non_oc_max = (rp_state_cap & 0xff);
-       hw_min = ((rp_state_cap & 0xff0000) >> 16);
+       if (IS_VALLEYVIEW(dev_priv->dev)) {
+               val = vlv_freq_opcode(dev_priv->mem_freq, val);
+
+               hw_max = valleyview_rps_max_freq(dev_priv);
+               hw_min = valleyview_rps_min_freq(dev_priv);
+               non_oc_max = hw_max;
+       } else {
+               val /= GT_FREQUENCY_MULTIPLIER;
+
+               rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+               hw_max = dev_priv->rps.hw_max;
+               non_oc_max = (rp_state_cap & 0xff);
+               hw_min = ((rp_state_cap & 0xff0000) >> 16);
+       }
 
-       if (val < hw_min || val > hw_max || val < dev_priv->rps.min_delay) {
+       if (val < hw_min || val > hw_max ||
+           val < dev_priv->rps.min_delay) {
                mutex_unlock(&dev_priv->rps.hw_lock);
                return -EINVAL;
        }
@@ -264,8 +282,12 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev,
                DRM_DEBUG("User requested overclocking to %d\n",
                          val * GT_FREQUENCY_MULTIPLIER);
 
-       if (dev_priv->rps.cur_delay > val)
-               gen6_set_rps(dev_priv->dev, val);
+       if (dev_priv->rps.cur_delay > val) {
+               if (IS_VALLEYVIEW(dev_priv->dev))
+                       valleyview_set_rps(dev_priv->dev, val);
+               else
+                       gen6_set_rps(dev_priv->dev, val);
+       }
 
        dev_priv->rps.max_delay = val;
 
@@ -282,7 +304,10 @@ static ssize_t gt_min_freq_mhz_show(struct device *kdev, struct device_attribute
        int ret;
 
        mutex_lock(&dev_priv->rps.hw_lock);
-       ret = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER;
+       if (IS_VALLEYVIEW(dev_priv->dev))
+               ret = vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.min_delay);
+       else
+               ret = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER;
        mutex_unlock(&dev_priv->rps.hw_lock);
 
        return snprintf(buf, PAGE_SIZE, "%d\n", ret);
@@ -302,21 +327,32 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev,
        if (ret)
                return ret;
 
-       val /= GT_FREQUENCY_MULTIPLIER;
-
        mutex_lock(&dev_priv->rps.hw_lock);
 
-       rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
-       hw_max = dev_priv->rps.hw_max;
-       hw_min = ((rp_state_cap & 0xff0000) >> 16);
+       if (IS_VALLEYVIEW(dev)) {
+               val = vlv_freq_opcode(dev_priv->mem_freq, val);
+
+               hw_max = valleyview_rps_max_freq(dev_priv);
+               hw_min = valleyview_rps_min_freq(dev_priv);
+       } else {
+               val /= GT_FREQUENCY_MULTIPLIER;
+
+               rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+               hw_max = dev_priv->rps.hw_max;
+               hw_min = ((rp_state_cap & 0xff0000) >> 16);
+       }
 
        if (val < hw_min || val > hw_max || val > dev_priv->rps.max_delay) {
                mutex_unlock(&dev_priv->rps.hw_lock);
                return -EINVAL;
        }
 
-       if (dev_priv->rps.cur_delay < val)
-               gen6_set_rps(dev_priv->dev, val);
+       if (dev_priv->rps.cur_delay < val) {
+               if (IS_VALLEYVIEW(dev))
+                       valleyview_set_rps(dev, val);
+               else
+                       gen6_set_rps(dev_priv->dev, val);
+       }
 
        dev_priv->rps.min_delay = val;
 
index 985a097..967da47 100644 (file)
@@ -41,7 +41,7 @@ static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe)
                return false;
 
        if (HAS_PCH_SPLIT(dev))
-               dpll_reg = _PCH_DPLL(pipe);
+               dpll_reg = PCH_DPLL(pipe);
        else
                dpll_reg = (pipe == PIPE_A) ? _DPLL_A : _DPLL_B;
 
@@ -148,13 +148,13 @@ void i915_save_display_reg(struct drm_device *dev)
                dev_priv->regfile.savePFA_WIN_SZ = I915_READ(_PFA_WIN_SZ);
                dev_priv->regfile.savePFA_WIN_POS = I915_READ(_PFA_WIN_POS);
 
-               dev_priv->regfile.saveTRANSACONF = I915_READ(_TRANSACONF);
-               dev_priv->regfile.saveTRANS_HTOTAL_A = I915_READ(_TRANS_HTOTAL_A);
-               dev_priv->regfile.saveTRANS_HBLANK_A = I915_READ(_TRANS_HBLANK_A);
-               dev_priv->regfile.saveTRANS_HSYNC_A = I915_READ(_TRANS_HSYNC_A);
-               dev_priv->regfile.saveTRANS_VTOTAL_A = I915_READ(_TRANS_VTOTAL_A);
-               dev_priv->regfile.saveTRANS_VBLANK_A = I915_READ(_TRANS_VBLANK_A);
-               dev_priv->regfile.saveTRANS_VSYNC_A = I915_READ(_TRANS_VSYNC_A);
+               dev_priv->regfile.saveTRANSACONF = I915_READ(_PCH_TRANSACONF);
+               dev_priv->regfile.saveTRANS_HTOTAL_A = I915_READ(_PCH_TRANS_HTOTAL_A);
+               dev_priv->regfile.saveTRANS_HBLANK_A = I915_READ(_PCH_TRANS_HBLANK_A);
+               dev_priv->regfile.saveTRANS_HSYNC_A = I915_READ(_PCH_TRANS_HSYNC_A);
+               dev_priv->regfile.saveTRANS_VTOTAL_A = I915_READ(_PCH_TRANS_VTOTAL_A);
+               dev_priv->regfile.saveTRANS_VBLANK_A = I915_READ(_PCH_TRANS_VBLANK_A);
+               dev_priv->regfile.saveTRANS_VSYNC_A = I915_READ(_PCH_TRANS_VSYNC_A);
        }
 
        dev_priv->regfile.saveDSPACNTR = I915_READ(_DSPACNTR);
@@ -205,13 +205,13 @@ void i915_save_display_reg(struct drm_device *dev)
                dev_priv->regfile.savePFB_WIN_SZ = I915_READ(_PFB_WIN_SZ);
                dev_priv->regfile.savePFB_WIN_POS = I915_READ(_PFB_WIN_POS);
 
-               dev_priv->regfile.saveTRANSBCONF = I915_READ(_TRANSBCONF);
-               dev_priv->regfile.saveTRANS_HTOTAL_B = I915_READ(_TRANS_HTOTAL_B);
-               dev_priv->regfile.saveTRANS_HBLANK_B = I915_READ(_TRANS_HBLANK_B);
-               dev_priv->regfile.saveTRANS_HSYNC_B = I915_READ(_TRANS_HSYNC_B);
-               dev_priv->regfile.saveTRANS_VTOTAL_B = I915_READ(_TRANS_VTOTAL_B);
-               dev_priv->regfile.saveTRANS_VBLANK_B = I915_READ(_TRANS_VBLANK_B);
-               dev_priv->regfile.saveTRANS_VSYNC_B = I915_READ(_TRANS_VSYNC_B);
+               dev_priv->regfile.saveTRANSBCONF = I915_READ(_PCH_TRANSBCONF);
+               dev_priv->regfile.saveTRANS_HTOTAL_B = I915_READ(_PCH_TRANS_HTOTAL_B);
+               dev_priv->regfile.saveTRANS_HBLANK_B = I915_READ(_PCH_TRANS_HBLANK_B);
+               dev_priv->regfile.saveTRANS_HSYNC_B = I915_READ(_PCH_TRANS_HSYNC_B);
+               dev_priv->regfile.saveTRANS_VTOTAL_B = I915_READ(_PCH_TRANS_VTOTAL_B);
+               dev_priv->regfile.saveTRANS_VBLANK_B = I915_READ(_PCH_TRANS_VBLANK_B);
+               dev_priv->regfile.saveTRANS_VSYNC_B = I915_READ(_PCH_TRANS_VSYNC_B);
        }
 
        dev_priv->regfile.saveDSPBCNTR = I915_READ(_DSPBCNTR);
@@ -259,14 +259,14 @@ void i915_save_display_reg(struct drm_device *dev)
                dev_priv->regfile.saveDP_B = I915_READ(DP_B);
                dev_priv->regfile.saveDP_C = I915_READ(DP_C);
                dev_priv->regfile.saveDP_D = I915_READ(DP_D);
-               dev_priv->regfile.savePIPEA_GMCH_DATA_M = I915_READ(_PIPEA_GMCH_DATA_M);
-               dev_priv->regfile.savePIPEB_GMCH_DATA_M = I915_READ(_PIPEB_GMCH_DATA_M);
-               dev_priv->regfile.savePIPEA_GMCH_DATA_N = I915_READ(_PIPEA_GMCH_DATA_N);
-               dev_priv->regfile.savePIPEB_GMCH_DATA_N = I915_READ(_PIPEB_GMCH_DATA_N);
-               dev_priv->regfile.savePIPEA_DP_LINK_M = I915_READ(_PIPEA_DP_LINK_M);
-               dev_priv->regfile.savePIPEB_DP_LINK_M = I915_READ(_PIPEB_DP_LINK_M);
-               dev_priv->regfile.savePIPEA_DP_LINK_N = I915_READ(_PIPEA_DP_LINK_N);
-               dev_priv->regfile.savePIPEB_DP_LINK_N = I915_READ(_PIPEB_DP_LINK_N);
+               dev_priv->regfile.savePIPEA_GMCH_DATA_M = I915_READ(_PIPEA_DATA_M_G4X);
+               dev_priv->regfile.savePIPEB_GMCH_DATA_M = I915_READ(_PIPEB_DATA_M_G4X);
+               dev_priv->regfile.savePIPEA_GMCH_DATA_N = I915_READ(_PIPEA_DATA_N_G4X);
+               dev_priv->regfile.savePIPEB_GMCH_DATA_N = I915_READ(_PIPEB_DATA_N_G4X);
+               dev_priv->regfile.savePIPEA_DP_LINK_M = I915_READ(_PIPEA_LINK_M_G4X);
+               dev_priv->regfile.savePIPEB_DP_LINK_M = I915_READ(_PIPEB_LINK_M_G4X);
+               dev_priv->regfile.savePIPEA_DP_LINK_N = I915_READ(_PIPEA_LINK_N_G4X);
+               dev_priv->regfile.savePIPEB_DP_LINK_N = I915_READ(_PIPEB_LINK_N_G4X);
        }
        /* FIXME: regfile.save TV & SDVO state */
 
@@ -282,14 +282,14 @@ void i915_restore_display_reg(struct drm_device *dev)
 
        /* Display port ratios (must be done before clock is set) */
        if (SUPPORTS_INTEGRATED_DP(dev)) {
-               I915_WRITE(_PIPEA_GMCH_DATA_M, dev_priv->regfile.savePIPEA_GMCH_DATA_M);
-               I915_WRITE(_PIPEB_GMCH_DATA_M, dev_priv->regfile.savePIPEB_GMCH_DATA_M);
-               I915_WRITE(_PIPEA_GMCH_DATA_N, dev_priv->regfile.savePIPEA_GMCH_DATA_N);
-               I915_WRITE(_PIPEB_GMCH_DATA_N, dev_priv->regfile.savePIPEB_GMCH_DATA_N);
-               I915_WRITE(_PIPEA_DP_LINK_M, dev_priv->regfile.savePIPEA_DP_LINK_M);
-               I915_WRITE(_PIPEB_DP_LINK_M, dev_priv->regfile.savePIPEB_DP_LINK_M);
-               I915_WRITE(_PIPEA_DP_LINK_N, dev_priv->regfile.savePIPEA_DP_LINK_N);
-               I915_WRITE(_PIPEB_DP_LINK_N, dev_priv->regfile.savePIPEB_DP_LINK_N);
+               I915_WRITE(_PIPEA_DATA_M_G4X, dev_priv->regfile.savePIPEA_GMCH_DATA_M);
+               I915_WRITE(_PIPEB_DATA_M_G4X, dev_priv->regfile.savePIPEB_GMCH_DATA_M);
+               I915_WRITE(_PIPEA_DATA_N_G4X, dev_priv->regfile.savePIPEA_GMCH_DATA_N);
+               I915_WRITE(_PIPEB_DATA_N_G4X, dev_priv->regfile.savePIPEB_GMCH_DATA_N);
+               I915_WRITE(_PIPEA_LINK_M_G4X, dev_priv->regfile.savePIPEA_DP_LINK_M);
+               I915_WRITE(_PIPEB_LINK_M_G4X, dev_priv->regfile.savePIPEB_DP_LINK_M);
+               I915_WRITE(_PIPEA_LINK_N_G4X, dev_priv->regfile.savePIPEA_DP_LINK_N);
+               I915_WRITE(_PIPEB_LINK_N_G4X, dev_priv->regfile.savePIPEB_DP_LINK_N);
        }
 
        /* Fences */
@@ -379,13 +379,13 @@ void i915_restore_display_reg(struct drm_device *dev)
                I915_WRITE(_PFA_WIN_SZ, dev_priv->regfile.savePFA_WIN_SZ);
                I915_WRITE(_PFA_WIN_POS, dev_priv->regfile.savePFA_WIN_POS);
 
-               I915_WRITE(_TRANSACONF, dev_priv->regfile.saveTRANSACONF);
-               I915_WRITE(_TRANS_HTOTAL_A, dev_priv->regfile.saveTRANS_HTOTAL_A);
-               I915_WRITE(_TRANS_HBLANK_A, dev_priv->regfile.saveTRANS_HBLANK_A);
-               I915_WRITE(_TRANS_HSYNC_A, dev_priv->regfile.saveTRANS_HSYNC_A);
-               I915_WRITE(_TRANS_VTOTAL_A, dev_priv->regfile.saveTRANS_VTOTAL_A);
-               I915_WRITE(_TRANS_VBLANK_A, dev_priv->regfile.saveTRANS_VBLANK_A);
-               I915_WRITE(_TRANS_VSYNC_A, dev_priv->regfile.saveTRANS_VSYNC_A);
+               I915_WRITE(_PCH_TRANSACONF, dev_priv->regfile.saveTRANSACONF);
+               I915_WRITE(_PCH_TRANS_HTOTAL_A, dev_priv->regfile.saveTRANS_HTOTAL_A);
+               I915_WRITE(_PCH_TRANS_HBLANK_A, dev_priv->regfile.saveTRANS_HBLANK_A);
+               I915_WRITE(_PCH_TRANS_HSYNC_A, dev_priv->regfile.saveTRANS_HSYNC_A);
+               I915_WRITE(_PCH_TRANS_VTOTAL_A, dev_priv->regfile.saveTRANS_VTOTAL_A);
+               I915_WRITE(_PCH_TRANS_VBLANK_A, dev_priv->regfile.saveTRANS_VBLANK_A);
+               I915_WRITE(_PCH_TRANS_VSYNC_A, dev_priv->regfile.saveTRANS_VSYNC_A);
        }
 
        /* Restore plane info */
@@ -448,13 +448,13 @@ void i915_restore_display_reg(struct drm_device *dev)
                I915_WRITE(_PFB_WIN_SZ, dev_priv->regfile.savePFB_WIN_SZ);
                I915_WRITE(_PFB_WIN_POS, dev_priv->regfile.savePFB_WIN_POS);
 
-               I915_WRITE(_TRANSBCONF, dev_priv->regfile.saveTRANSBCONF);
-               I915_WRITE(_TRANS_HTOTAL_B, dev_priv->regfile.saveTRANS_HTOTAL_B);
-               I915_WRITE(_TRANS_HBLANK_B, dev_priv->regfile.saveTRANS_HBLANK_B);
-               I915_WRITE(_TRANS_HSYNC_B, dev_priv->regfile.saveTRANS_HSYNC_B);
-               I915_WRITE(_TRANS_VTOTAL_B, dev_priv->regfile.saveTRANS_VTOTAL_B);
-               I915_WRITE(_TRANS_VBLANK_B, dev_priv->regfile.saveTRANS_VBLANK_B);
-               I915_WRITE(_TRANS_VSYNC_B, dev_priv->regfile.saveTRANS_VSYNC_B);
+               I915_WRITE(_PCH_TRANSBCONF, dev_priv->regfile.saveTRANSBCONF);
+               I915_WRITE(_PCH_TRANS_HTOTAL_B, dev_priv->regfile.saveTRANS_HTOTAL_B);
+               I915_WRITE(_PCH_TRANS_HBLANK_B, dev_priv->regfile.saveTRANS_HBLANK_B);
+               I915_WRITE(_PCH_TRANS_HSYNC_B, dev_priv->regfile.saveTRANS_HSYNC_B);
+               I915_WRITE(_PCH_TRANS_VTOTAL_B, dev_priv->regfile.saveTRANS_VTOTAL_B);
+               I915_WRITE(_PCH_TRANS_VBLANK_B, dev_priv->regfile.saveTRANS_VBLANK_B);
+               I915_WRITE(_PCH_TRANS_VSYNC_B, dev_priv->regfile.saveTRANS_VSYNC_B);
        }
 
        /* Restore plane info */
index 95070b2..53f2bed 100644 (file)
@@ -212,7 +212,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
        if (!lvds_options)
                return;
 
-       dev_priv->lvds_dither = lvds_options->pixel_dither;
+       dev_priv->vbt.lvds_dither = lvds_options->pixel_dither;
        if (lvds_options->panel_type == 0xff)
                return;
 
@@ -226,7 +226,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
        if (!lvds_lfp_data_ptrs)
                return;
 
-       dev_priv->lvds_vbt = 1;
+       dev_priv->vbt.lvds_vbt = 1;
 
        panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
                                               lvds_lfp_data_ptrs,
@@ -238,7 +238,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
 
        fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing);
 
-       dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode;
+       dev_priv->vbt.lfp_lvds_vbt_mode = panel_fixed_mode;
 
        DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n");
        drm_mode_debug_printmodeline(panel_fixed_mode);
@@ -274,9 +274,9 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
                /* check the resolution, just to be sure */
                if (fp_timing->x_res == panel_fixed_mode->hdisplay &&
                    fp_timing->y_res == panel_fixed_mode->vdisplay) {
-                       dev_priv->bios_lvds_val = fp_timing->lvds_reg_val;
+                       dev_priv->vbt.bios_lvds_val = fp_timing->lvds_reg_val;
                        DRM_DEBUG_KMS("VBT initial LVDS value %x\n",
-                                     dev_priv->bios_lvds_val);
+                                     dev_priv->vbt.bios_lvds_val);
                }
        }
 }
@@ -316,7 +316,7 @@ parse_sdvo_panel_data(struct drm_i915_private *dev_priv,
 
        fill_detail_timing_data(panel_fixed_mode, dvo_timing + index);
 
-       dev_priv->sdvo_lvds_vbt_mode = panel_fixed_mode;
+       dev_priv->vbt.sdvo_lvds_vbt_mode = panel_fixed_mode;
 
        DRM_DEBUG_KMS("Found SDVO panel mode in BIOS VBT tables:\n");
        drm_mode_debug_printmodeline(panel_fixed_mode);
@@ -345,20 +345,20 @@ parse_general_features(struct drm_i915_private *dev_priv,
 
        general = find_section(bdb, BDB_GENERAL_FEATURES);
        if (general) {
-               dev_priv->int_tv_support = general->int_tv_support;
-               dev_priv->int_crt_support = general->int_crt_support;
-               dev_priv->lvds_use_ssc = general->enable_ssc;
-               dev_priv->lvds_ssc_freq =
+               dev_priv->vbt.int_tv_support = general->int_tv_support;
+               dev_priv->vbt.int_crt_support = general->int_crt_support;
+               dev_priv->vbt.lvds_use_ssc = general->enable_ssc;
+               dev_priv->vbt.lvds_ssc_freq =
                        intel_bios_ssc_frequency(dev, general->ssc_freq);
-               dev_priv->display_clock_mode = general->display_clock_mode;
-               dev_priv->fdi_rx_polarity_inverted = general->fdi_rx_polarity_inverted;
+               dev_priv->vbt.display_clock_mode = general->display_clock_mode;
+               dev_priv->vbt.fdi_rx_polarity_inverted = general->fdi_rx_polarity_inverted;
                DRM_DEBUG_KMS("BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d display_clock_mode %d fdi_rx_polarity_inverted %d\n",
-                             dev_priv->int_tv_support,
-                             dev_priv->int_crt_support,
-                             dev_priv->lvds_use_ssc,
-                             dev_priv->lvds_ssc_freq,
-                             dev_priv->display_clock_mode,
-                             dev_priv->fdi_rx_polarity_inverted);
+                             dev_priv->vbt.int_tv_support,
+                             dev_priv->vbt.int_crt_support,
+                             dev_priv->vbt.lvds_use_ssc,
+                             dev_priv->vbt.lvds_ssc_freq,
+                             dev_priv->vbt.display_clock_mode,
+                             dev_priv->vbt.fdi_rx_polarity_inverted);
        }
 }
 
@@ -375,7 +375,7 @@ parse_general_definitions(struct drm_i915_private *dev_priv,
                        int bus_pin = general->crt_ddc_gmbus_pin;
                        DRM_DEBUG_KMS("crt_ddc_bus_pin: %d\n", bus_pin);
                        if (intel_gmbus_is_port_valid(bus_pin))
-                               dev_priv->crt_ddc_pin = bus_pin;
+                               dev_priv->vbt.crt_ddc_pin = bus_pin;
                } else {
                        DRM_DEBUG_KMS("BDB_GD too small (%d). Invalid.\n",
                                      block_size);
@@ -486,7 +486,7 @@ parse_driver_features(struct drm_i915_private *dev_priv,
 
        if (SUPPORTS_EDP(dev) &&
            driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
-               dev_priv->edp.support = 1;
+               dev_priv->vbt.edp_support = 1;
 
        if (driver->dual_frequency)
                dev_priv->render_reclock_avail = true;
@@ -501,20 +501,20 @@ 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->edp.support)
+               if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->vbt.edp_support)
                        DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported.\n");
                return;
        }
 
        switch ((edp->color_depth >> (panel_type * 2)) & 3) {
        case EDP_18BPP:
-               dev_priv->edp.bpp = 18;
+               dev_priv->vbt.edp_bpp = 18;
                break;
        case EDP_24BPP:
-               dev_priv->edp.bpp = 24;
+               dev_priv->vbt.edp_bpp = 24;
                break;
        case EDP_30BPP:
-               dev_priv->edp.bpp = 30;
+               dev_priv->vbt.edp_bpp = 30;
                break;
        }
 
@@ -522,48 +522,48 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
        edp_pps = &edp->power_seqs[panel_type];
        edp_link_params = &edp->link_params[panel_type];
 
-       dev_priv->edp.pps = *edp_pps;
+       dev_priv->vbt.edp_pps = *edp_pps;
 
-       dev_priv->edp.rate = edp_link_params->rate ? DP_LINK_BW_2_7 :
+       dev_priv->vbt.edp_rate = edp_link_params->rate ? DP_LINK_BW_2_7 :
                DP_LINK_BW_1_62;
        switch (edp_link_params->lanes) {
        case 0:
-               dev_priv->edp.lanes = 1;
+               dev_priv->vbt.edp_lanes = 1;
                break;
        case 1:
-               dev_priv->edp.lanes = 2;
+               dev_priv->vbt.edp_lanes = 2;
                break;
        case 3:
        default:
-               dev_priv->edp.lanes = 4;
+               dev_priv->vbt.edp_lanes = 4;
                break;
        }
        switch (edp_link_params->preemphasis) {
        case 0:
-               dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_0;
+               dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_0;
                break;
        case 1:
-               dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5;
+               dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5;
                break;
        case 2:
-               dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_6;
+               dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_6;
                break;
        case 3:
-               dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5;
+               dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5;
                break;
        }
        switch (edp_link_params->vswing) {
        case 0:
-               dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_400;
+               dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_400;
                break;
        case 1:
-               dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_600;
+               dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_600;
                break;
        case 2:
-               dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_800;
+               dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_800;
                break;
        case 3:
-               dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_1200;
+               dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_1200;
                break;
        }
 }
@@ -611,13 +611,13 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
                DRM_DEBUG_KMS("no child dev is parsed from VBT\n");
                return;
        }
-       dev_priv->child_dev = kcalloc(count, sizeof(*p_child), GFP_KERNEL);
-       if (!dev_priv->child_dev) {
+       dev_priv->vbt.child_dev = kcalloc(count, sizeof(*p_child), GFP_KERNEL);
+       if (!dev_priv->vbt.child_dev) {
                DRM_DEBUG_KMS("No memory space for child device\n");
                return;
        }
 
-       dev_priv->child_dev_num = count;
+       dev_priv->vbt.child_dev_num = count;
        count = 0;
        for (i = 0; i < child_device_num; i++) {
                p_child = &(p_defs->devices[i]);
@@ -625,7 +625,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
                        /* skip the device block if device type is invalid */
                        continue;
                }
-               child_dev_ptr = dev_priv->child_dev + count;
+               child_dev_ptr = dev_priv->vbt.child_dev + count;
                count++;
                memcpy((void *)child_dev_ptr, (void *)p_child,
                                        sizeof(*p_child));
@@ -638,23 +638,23 @@ init_vbt_defaults(struct drm_i915_private *dev_priv)
 {
        struct drm_device *dev = dev_priv->dev;
 
-       dev_priv->crt_ddc_pin = GMBUS_PORT_VGADDC;
+       dev_priv->vbt.crt_ddc_pin = GMBUS_PORT_VGADDC;
 
        /* LFP panel data */
-       dev_priv->lvds_dither = 1;
-       dev_priv->lvds_vbt = 0;
+       dev_priv->vbt.lvds_dither = 1;
+       dev_priv->vbt.lvds_vbt = 0;
 
        /* SDVO panel data */
-       dev_priv->sdvo_lvds_vbt_mode = NULL;
+       dev_priv->vbt.sdvo_lvds_vbt_mode = NULL;
 
        /* general features */
-       dev_priv->int_tv_support = 1;
-       dev_priv->int_crt_support = 1;
+       dev_priv->vbt.int_tv_support = 1;
+       dev_priv->vbt.int_crt_support = 1;
 
        /* Default to using SSC */
-       dev_priv->lvds_use_ssc = 1;
-       dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1);
-       DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq);
+       dev_priv->vbt.lvds_use_ssc = 1;
+       dev_priv->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1);
+       DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->vbt.lvds_ssc_freq);
 }
 
 static int __init intel_no_opregion_vbt_callback(const struct dmi_system_id *id)
index 58b4a53..3acec8c 100644 (file)
@@ -84,6 +84,28 @@ static bool intel_crt_get_hw_state(struct intel_encoder *encoder,
        return true;
 }
 
+static void intel_crt_get_config(struct intel_encoder *encoder,
+                                struct intel_crtc_config *pipe_config)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_crt *crt = intel_encoder_to_crt(encoder);
+       u32 tmp, flags = 0;
+
+       tmp = I915_READ(crt->adpa_reg);
+
+       if (tmp & ADPA_HSYNC_ACTIVE_HIGH)
+               flags |= DRM_MODE_FLAG_PHSYNC;
+       else
+               flags |= DRM_MODE_FLAG_NHSYNC;
+
+       if (tmp & ADPA_VSYNC_ACTIVE_HIGH)
+               flags |= DRM_MODE_FLAG_PVSYNC;
+       else
+               flags |= DRM_MODE_FLAG_NVSYNC;
+
+       pipe_config->adjusted_mode.flags |= flags;
+}
+
 /* Note: The caller is required to filter out dpms modes not supported by the
  * platform. */
 static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode)
@@ -127,7 +149,7 @@ static void intel_enable_crt(struct intel_encoder *encoder)
        intel_crt_set_dpms(encoder, crt->connector->base.dpms);
 }
 
-
+/* Special dpms function to support cloning between dvo/sdvo/crt. */
 static void intel_crt_dpms(struct drm_connector *connector, int mode)
 {
        struct drm_device *dev = connector->dev;
@@ -158,6 +180,8 @@ static void intel_crt_dpms(struct drm_connector *connector, int mode)
        else
                encoder->connectors_active = true;
 
+       /* We call connector dpms manually below in case pipe dpms doesn't
+        * change due to cloning. */
        if (mode < old_dpms) {
                /* From off to on, enable the pipe first. */
                intel_crtc_update_dpms(crtc);
@@ -207,6 +231,10 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder,
        if (HAS_PCH_SPLIT(dev))
                pipe_config->has_pch_encoder = true;
 
+       /* LPT FDI RX only supports 8bpc. */
+       if (HAS_PCH_LPT(dev))
+               pipe_config->pipe_bpp = 24;
+
        return true;
 }
 
@@ -431,7 +459,7 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector)
 
        BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG);
 
-       i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin);
+       i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin);
        edid = intel_crt_get_edid(connector, i2c);
 
        if (edid) {
@@ -637,7 +665,7 @@ static int intel_crt_get_modes(struct drm_connector *connector)
        int ret;
        struct i2c_adapter *i2c;
 
-       i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin);
+       i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin);
        ret = intel_crt_ddc_get_modes(connector, i2c);
        if (ret || !IS_G4X(dev))
                return ret;
@@ -774,6 +802,7 @@ void intel_crt_init(struct drm_device *dev)
        crt->base.compute_config = intel_crt_compute_config;
        crt->base.disable = intel_disable_crt;
        crt->base.enable = intel_enable_crt;
+       crt->base.get_config = intel_crt_get_config;
        if (I915_HAS_HOTPLUG(dev))
                crt->base.hpd_pin = HPD_CRT;
        if (HAS_DDI(dev))
index fb961bb..324211a 100644 (file)
@@ -174,6 +174,8 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
         * mode set "sequence for CRT port" document:
         * - TP1 to TP2 time with the default value
         * - FDI delay to 90h
+        *
+        * WaFDIAutoLinkSetTimingOverrride:hsw
         */
        I915_WRITE(_FDI_RXA_MISC, FDI_RX_PWRDN_LANE1_VAL(2) |
                                  FDI_RX_PWRDN_LANE0_VAL(2) |
@@ -181,7 +183,8 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
 
        /* Enable the PCH Receiver FDI PLL */
        rx_ctl_val = dev_priv->fdi_rx_config | FDI_RX_ENHANCE_FRAME_ENABLE |
-                    FDI_RX_PLL_ENABLE | ((intel_crtc->fdi_lanes - 1) << 19);
+                    FDI_RX_PLL_ENABLE |
+                    FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes);
        I915_WRITE(_FDI_RXA_CTL, rx_ctl_val);
        POSTING_READ(_FDI_RXA_CTL);
        udelay(220);
@@ -209,7 +212,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
                 * port reversal bit */
                I915_WRITE(DDI_BUF_CTL(PORT_E),
                           DDI_BUF_CTL_ENABLE |
-                          ((intel_crtc->fdi_lanes - 1) << 1) |
+                          ((intel_crtc->config.fdi_lanes - 1) << 1) |
                           hsw_ddi_buf_ctl_values[i / 2]);
                POSTING_READ(DDI_BUF_CTL(PORT_E));
 
@@ -278,392 +281,6 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
        DRM_ERROR("FDI link training failed!\n");
 }
 
-/* WRPLL clock dividers */
-struct wrpll_tmds_clock {
-       u32 clock;
-       u16 p;          /* Post divider */
-       u16 n2;         /* Feedback divider */
-       u16 r2;         /* Reference divider */
-};
-
-/* Table of matching values for WRPLL clocks programming for each frequency.
- * The code assumes this table is sorted. */
-static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = {
-       {19750, 38,     25,     18},
-       {20000, 48,     32,     18},
-       {21000, 36,     21,     15},
-       {21912, 42,     29,     17},
-       {22000, 36,     22,     15},
-       {23000, 36,     23,     15},
-       {23500, 40,     40,     23},
-       {23750, 26,     16,     14},
-       {24000, 36,     24,     15},
-       {25000, 36,     25,     15},
-       {25175, 26,     40,     33},
-       {25200, 30,     21,     15},
-       {26000, 36,     26,     15},
-       {27000, 30,     21,     14},
-       {27027, 18,     100,    111},
-       {27500, 30,     29,     19},
-       {28000, 34,     30,     17},
-       {28320, 26,     30,     22},
-       {28322, 32,     42,     25},
-       {28750, 24,     23,     18},
-       {29000, 30,     29,     18},
-       {29750, 32,     30,     17},
-       {30000, 30,     25,     15},
-       {30750, 30,     41,     24},
-       {31000, 30,     31,     18},
-       {31500, 30,     28,     16},
-       {32000, 30,     32,     18},
-       {32500, 28,     32,     19},
-       {33000, 24,     22,     15},
-       {34000, 28,     30,     17},
-       {35000, 26,     32,     19},
-       {35500, 24,     30,     19},
-       {36000, 26,     26,     15},
-       {36750, 26,     46,     26},
-       {37000, 24,     23,     14},
-       {37762, 22,     40,     26},
-       {37800, 20,     21,     15},
-       {38000, 24,     27,     16},
-       {38250, 24,     34,     20},
-       {39000, 24,     26,     15},
-       {40000, 24,     32,     18},
-       {40500, 20,     21,     14},
-       {40541, 22,     147,    89},
-       {40750, 18,     19,     14},
-       {41000, 16,     17,     14},
-       {41500, 22,     44,     26},
-       {41540, 22,     44,     26},
-       {42000, 18,     21,     15},
-       {42500, 22,     45,     26},
-       {43000, 20,     43,     27},
-       {43163, 20,     24,     15},
-       {44000, 18,     22,     15},
-       {44900, 20,     108,    65},
-       {45000, 20,     25,     15},
-       {45250, 20,     52,     31},
-       {46000, 18,     23,     15},
-       {46750, 20,     45,     26},
-       {47000, 20,     40,     23},
-       {48000, 18,     24,     15},
-       {49000, 18,     49,     30},
-       {49500, 16,     22,     15},
-       {50000, 18,     25,     15},
-       {50500, 18,     32,     19},
-       {51000, 18,     34,     20},
-       {52000, 18,     26,     15},
-       {52406, 14,     34,     25},
-       {53000, 16,     22,     14},
-       {54000, 16,     24,     15},
-       {54054, 16,     173,    108},
-       {54500, 14,     24,     17},
-       {55000, 12,     22,     18},
-       {56000, 14,     45,     31},
-       {56250, 16,     25,     15},
-       {56750, 14,     25,     17},
-       {57000, 16,     27,     16},
-       {58000, 16,     43,     25},
-       {58250, 16,     38,     22},
-       {58750, 16,     40,     23},
-       {59000, 14,     26,     17},
-       {59341, 14,     40,     26},
-       {59400, 16,     44,     25},
-       {60000, 16,     32,     18},
-       {60500, 12,     39,     29},
-       {61000, 14,     49,     31},
-       {62000, 14,     37,     23},
-       {62250, 14,     42,     26},
-       {63000, 12,     21,     15},
-       {63500, 14,     28,     17},
-       {64000, 12,     27,     19},
-       {65000, 14,     32,     19},
-       {65250, 12,     29,     20},
-       {65500, 12,     32,     22},
-       {66000, 12,     22,     15},
-       {66667, 14,     38,     22},
-       {66750, 10,     21,     17},
-       {67000, 14,     33,     19},
-       {67750, 14,     58,     33},
-       {68000, 14,     30,     17},
-       {68179, 14,     46,     26},
-       {68250, 14,     46,     26},
-       {69000, 12,     23,     15},
-       {70000, 12,     28,     18},
-       {71000, 12,     30,     19},
-       {72000, 12,     24,     15},
-       {73000, 10,     23,     17},
-       {74000, 12,     23,     14},
-       {74176, 8,      100,    91},
-       {74250, 10,     22,     16},
-       {74481, 12,     43,     26},
-       {74500, 10,     29,     21},
-       {75000, 12,     25,     15},
-       {75250, 10,     39,     28},
-       {76000, 12,     27,     16},
-       {77000, 12,     53,     31},
-       {78000, 12,     26,     15},
-       {78750, 12,     28,     16},
-       {79000, 10,     38,     26},
-       {79500, 10,     28,     19},
-       {80000, 12,     32,     18},
-       {81000, 10,     21,     14},
-       {81081, 6,      100,    111},
-       {81624, 8,      29,     24},
-       {82000, 8,      17,     14},
-       {83000, 10,     40,     26},
-       {83950, 10,     28,     18},
-       {84000, 10,     28,     18},
-       {84750, 6,      16,     17},
-       {85000, 6,      17,     18},
-       {85250, 10,     30,     19},
-       {85750, 10,     27,     17},
-       {86000, 10,     43,     27},
-       {87000, 10,     29,     18},
-       {88000, 10,     44,     27},
-       {88500, 10,     41,     25},
-       {89000, 10,     28,     17},
-       {89012, 6,      90,     91},
-       {89100, 10,     33,     20},
-       {90000, 10,     25,     15},
-       {91000, 10,     32,     19},
-       {92000, 10,     46,     27},
-       {93000, 10,     31,     18},
-       {94000, 10,     40,     23},
-       {94500, 10,     28,     16},
-       {95000, 10,     44,     25},
-       {95654, 10,     39,     22},
-       {95750, 10,     39,     22},
-       {96000, 10,     32,     18},
-       {97000, 8,      23,     16},
-       {97750, 8,      42,     29},
-       {98000, 8,      45,     31},
-       {99000, 8,      22,     15},
-       {99750, 8,      34,     23},
-       {100000,        6,      20,     18},
-       {100500,        6,      19,     17},
-       {101000,        6,      37,     33},
-       {101250,        8,      21,     14},
-       {102000,        6,      17,     15},
-       {102250,        6,      25,     22},
-       {103000,        8,      29,     19},
-       {104000,        8,      37,     24},
-       {105000,        8,      28,     18},
-       {106000,        8,      22,     14},
-       {107000,        8,      46,     29},
-       {107214,        8,      27,     17},
-       {108000,        8,      24,     15},
-       {108108,        8,      173,    108},
-       {109000,        6,      23,     19},
-       {110000,        6,      22,     18},
-       {110013,        6,      22,     18},
-       {110250,        8,      49,     30},
-       {110500,        8,      36,     22},
-       {111000,        8,      23,     14},
-       {111264,        8,      150,    91},
-       {111375,        8,      33,     20},
-       {112000,        8,      63,     38},
-       {112500,        8,      25,     15},
-       {113100,        8,      57,     34},
-       {113309,        8,      42,     25},
-       {114000,        8,      27,     16},
-       {115000,        6,      23,     18},
-       {116000,        8,      43,     25},
-       {117000,        8,      26,     15},
-       {117500,        8,      40,     23},
-       {118000,        6,      38,     29},
-       {119000,        8,      30,     17},
-       {119500,        8,      46,     26},
-       {119651,        8,      39,     22},
-       {120000,        8,      32,     18},
-       {121000,        6,      39,     29},
-       {121250,        6,      31,     23},
-       {121750,        6,      23,     17},
-       {122000,        6,      42,     31},
-       {122614,        6,      30,     22},
-       {123000,        6,      41,     30},
-       {123379,        6,      37,     27},
-       {124000,        6,      51,     37},
-       {125000,        6,      25,     18},
-       {125250,        4,      13,     14},
-       {125750,        4,      27,     29},
-       {126000,        6,      21,     15},
-       {127000,        6,      24,     17},
-       {127250,        6,      41,     29},
-       {128000,        6,      27,     19},
-       {129000,        6,      43,     30},
-       {129859,        4,      25,     26},
-       {130000,        6,      26,     18},
-       {130250,        6,      42,     29},
-       {131000,        6,      32,     22},
-       {131500,        6,      38,     26},
-       {131850,        6,      41,     28},
-       {132000,        6,      22,     15},
-       {132750,        6,      28,     19},
-       {133000,        6,      34,     23},
-       {133330,        6,      37,     25},
-       {134000,        6,      61,     41},
-       {135000,        6,      21,     14},
-       {135250,        6,      167,    111},
-       {136000,        6,      62,     41},
-       {137000,        6,      35,     23},
-       {138000,        6,      23,     15},
-       {138500,        6,      40,     26},
-       {138750,        6,      37,     24},
-       {139000,        6,      34,     22},
-       {139050,        6,      34,     22},
-       {139054,        6,      34,     22},
-       {140000,        6,      28,     18},
-       {141000,        6,      36,     23},
-       {141500,        6,      22,     14},
-       {142000,        6,      30,     19},
-       {143000,        6,      27,     17},
-       {143472,        4,      17,     16},
-       {144000,        6,      24,     15},
-       {145000,        6,      29,     18},
-       {146000,        6,      47,     29},
-       {146250,        6,      26,     16},
-       {147000,        6,      49,     30},
-       {147891,        6,      23,     14},
-       {148000,        6,      23,     14},
-       {148250,        6,      28,     17},
-       {148352,        4,      100,    91},
-       {148500,        6,      33,     20},
-       {149000,        6,      48,     29},
-       {150000,        6,      25,     15},
-       {151000,        4,      19,     17},
-       {152000,        6,      27,     16},
-       {152280,        6,      44,     26},
-       {153000,        6,      34,     20},
-       {154000,        6,      53,     31},
-       {155000,        6,      31,     18},
-       {155250,        6,      50,     29},
-       {155750,        6,      45,     26},
-       {156000,        6,      26,     15},
-       {157000,        6,      61,     35},
-       {157500,        6,      28,     16},
-       {158000,        6,      65,     37},
-       {158250,        6,      44,     25},
-       {159000,        6,      53,     30},
-       {159500,        6,      39,     22},
-       {160000,        6,      32,     18},
-       {161000,        4,      31,     26},
-       {162000,        4,      18,     15},
-       {162162,        4,      131,    109},
-       {162500,        4,      53,     44},
-       {163000,        4,      29,     24},
-       {164000,        4,      17,     14},
-       {165000,        4,      22,     18},
-       {166000,        4,      32,     26},
-       {167000,        4,      26,     21},
-       {168000,        4,      46,     37},
-       {169000,        4,      104,    83},
-       {169128,        4,      64,     51},
-       {169500,        4,      39,     31},
-       {170000,        4,      34,     27},
-       {171000,        4,      19,     15},
-       {172000,        4,      51,     40},
-       {172750,        4,      32,     25},
-       {172800,        4,      32,     25},
-       {173000,        4,      41,     32},
-       {174000,        4,      49,     38},
-       {174787,        4,      22,     17},
-       {175000,        4,      35,     27},
-       {176000,        4,      30,     23},
-       {177000,        4,      38,     29},
-       {178000,        4,      29,     22},
-       {178500,        4,      37,     28},
-       {179000,        4,      53,     40},
-       {179500,        4,      73,     55},
-       {180000,        4,      20,     15},
-       {181000,        4,      55,     41},
-       {182000,        4,      31,     23},
-       {183000,        4,      42,     31},
-       {184000,        4,      30,     22},
-       {184750,        4,      26,     19},
-       {185000,        4,      37,     27},
-       {186000,        4,      51,     37},
-       {187000,        4,      36,     26},
-       {188000,        4,      32,     23},
-       {189000,        4,      21,     15},
-       {190000,        4,      38,     27},
-       {190960,        4,      41,     29},
-       {191000,        4,      41,     29},
-       {192000,        4,      27,     19},
-       {192250,        4,      37,     26},
-       {193000,        4,      20,     14},
-       {193250,        4,      53,     37},
-       {194000,        4,      23,     16},
-       {194208,        4,      23,     16},
-       {195000,        4,      26,     18},
-       {196000,        4,      45,     31},
-       {197000,        4,      35,     24},
-       {197750,        4,      41,     28},
-       {198000,        4,      22,     15},
-       {198500,        4,      25,     17},
-       {199000,        4,      28,     19},
-       {200000,        4,      37,     25},
-       {201000,        4,      61,     41},
-       {202000,        4,      112,    75},
-       {202500,        4,      21,     14},
-       {203000,        4,      146,    97},
-       {204000,        4,      62,     41},
-       {204750,        4,      44,     29},
-       {205000,        4,      38,     25},
-       {206000,        4,      29,     19},
-       {207000,        4,      23,     15},
-       {207500,        4,      40,     26},
-       {208000,        4,      37,     24},
-       {208900,        4,      48,     31},
-       {209000,        4,      48,     31},
-       {209250,        4,      31,     20},
-       {210000,        4,      28,     18},
-       {211000,        4,      25,     16},
-       {212000,        4,      22,     14},
-       {213000,        4,      30,     19},
-       {213750,        4,      38,     24},
-       {214000,        4,      46,     29},
-       {214750,        4,      35,     22},
-       {215000,        4,      43,     27},
-       {216000,        4,      24,     15},
-       {217000,        4,      37,     23},
-       {218000,        4,      42,     26},
-       {218250,        4,      42,     26},
-       {218750,        4,      34,     21},
-       {219000,        4,      47,     29},
-       {220000,        4,      44,     27},
-       {220640,        4,      49,     30},
-       {220750,        4,      36,     22},
-       {221000,        4,      36,     22},
-       {222000,        4,      23,     14},
-       {222525,        4,      28,     17},
-       {222750,        4,      33,     20},
-       {227000,        4,      37,     22},
-       {230250,        4,      29,     17},
-       {233500,        4,      38,     22},
-       {235000,        4,      40,     23},
-       {238000,        4,      30,     17},
-       {241500,        2,      17,     19},
-       {245250,        2,      20,     22},
-       {247750,        2,      22,     24},
-       {253250,        2,      15,     16},
-       {256250,        2,      18,     19},
-       {262500,        2,      31,     32},
-       {267250,        2,      66,     67},
-       {268500,        2,      94,     95},
-       {270000,        2,      14,     14},
-       {272500,        2,      77,     76},
-       {273750,        2,      57,     56},
-       {280750,        2,      24,     23},
-       {281250,        2,      23,     22},
-       {286000,        2,      17,     16},
-       {291750,        2,      26,     24},
-       {296703,        2,      56,     51},
-       {297000,        2,      22,     20},
-       {298000,        2,      21,     19},
-};
-
 static void intel_ddi_mode_set(struct drm_encoder *encoder,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted_mode)
@@ -675,7 +292,7 @@ static void intel_ddi_mode_set(struct drm_encoder *encoder,
        int pipe = intel_crtc->pipe;
        int type = intel_encoder->type;
 
-       DRM_DEBUG_KMS("Preparing DDI mode for Haswell on port %c, pipe %c\n",
+       DRM_DEBUG_KMS("Preparing DDI mode on port %c, pipe %c\n",
                      port_name(port), pipe_name(pipe));
 
        intel_crtc->eld_vld = false;
@@ -686,22 +303,7 @@ static void intel_ddi_mode_set(struct drm_encoder *encoder,
 
                intel_dp->DP = intel_dig_port->port_reversal |
                               DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
-               switch (intel_dp->lane_count) {
-               case 1:
-                       intel_dp->DP |= DDI_PORT_WIDTH_X1;
-                       break;
-               case 2:
-                       intel_dp->DP |= DDI_PORT_WIDTH_X2;
-                       break;
-               case 4:
-                       intel_dp->DP |= DDI_PORT_WIDTH_X4;
-                       break;
-               default:
-                       intel_dp->DP |= DDI_PORT_WIDTH_X4;
-                       WARN(1, "Unexpected DP lane count %d\n",
-                            intel_dp->lane_count);
-                       break;
-               }
+               intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
 
                if (intel_dp->has_audio) {
                        DRM_DEBUG_DRIVER("DP audio on pipe %c on DDI\n",
@@ -748,8 +350,8 @@ intel_ddi_get_crtc_encoder(struct drm_crtc *crtc)
        }
 
        if (num_encoders != 1)
-               WARN(1, "%d encoders on crtc for pipe %d\n", num_encoders,
-                    intel_crtc->pipe);
+               WARN(1, "%d encoders on crtc for pipe %c\n", num_encoders,
+                    pipe_name(intel_crtc->pipe));
 
        BUG_ON(ret == NULL);
        return ret;
@@ -802,30 +404,227 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc)
        intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE;
 }
 
-static void intel_ddi_calculate_wrpll(int clock, int *p, int *n2, int *r2)
+#define LC_FREQ 2700
+#define LC_FREQ_2K (LC_FREQ * 2000)
+
+#define P_MIN 2
+#define P_MAX 64
+#define P_INC 2
+
+/* Constraints for PLL good behavior */
+#define REF_MIN 48
+#define REF_MAX 400
+#define VCO_MIN 2400
+#define VCO_MAX 4800
+
+#define ABS_DIFF(a, b) ((a > b) ? (a - b) : (b - a))
+
+struct wrpll_rnp {
+       unsigned p, n2, r2;
+};
+
+static unsigned wrpll_get_budget_for_freq(int clock)
+{
+       unsigned budget;
+
+       switch (clock) {
+       case 25175000:
+       case 25200000:
+       case 27000000:
+       case 27027000:
+       case 37762500:
+       case 37800000:
+       case 40500000:
+       case 40541000:
+       case 54000000:
+       case 54054000:
+       case 59341000:
+       case 59400000:
+       case 72000000:
+       case 74176000:
+       case 74250000:
+       case 81000000:
+       case 81081000:
+       case 89012000:
+       case 89100000:
+       case 108000000:
+       case 108108000:
+       case 111264000:
+       case 111375000:
+       case 148352000:
+       case 148500000:
+       case 162000000:
+       case 162162000:
+       case 222525000:
+       case 222750000:
+       case 296703000:
+       case 297000000:
+               budget = 0;
+               break;
+       case 233500000:
+       case 245250000:
+       case 247750000:
+       case 253250000:
+       case 298000000:
+               budget = 1500;
+               break;
+       case 169128000:
+       case 169500000:
+       case 179500000:
+       case 202000000:
+               budget = 2000;
+               break;
+       case 256250000:
+       case 262500000:
+       case 270000000:
+       case 272500000:
+       case 273750000:
+       case 280750000:
+       case 281250000:
+       case 286000000:
+       case 291750000:
+               budget = 4000;
+               break;
+       case 267250000:
+       case 268500000:
+               budget = 5000;
+               break;
+       default:
+               budget = 1000;
+               break;
+       }
+
+       return budget;
+}
+
+static void wrpll_update_rnp(uint64_t freq2k, unsigned budget,
+                            unsigned r2, unsigned n2, unsigned p,
+                            struct wrpll_rnp *best)
 {
-       u32 i;
+       uint64_t a, b, c, d, diff, diff_best;
 
-       for (i = 0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++)
-               if (clock <= wrpll_tmds_clock_table[i].clock)
-                       break;
+       /* No best (r,n,p) yet */
+       if (best->p == 0) {
+               best->p = p;
+               best->n2 = n2;
+               best->r2 = r2;
+               return;
+       }
+
+       /*
+        * Output clock is (LC_FREQ_2K / 2000) * N / (P * R), which compares to
+        * freq2k.
+        *
+        * delta = 1e6 *
+        *         abs(freq2k - (LC_FREQ_2K * n2/(p * r2))) /
+        *         freq2k;
+        *
+        * and we would like delta <= budget.
+        *
+        * If the discrepancy is above the PPM-based budget, always prefer to
+        * improve upon the previous solution.  However, if you're within the
+        * budget, try to maximize Ref * VCO, that is N / (P * R^2).
+        */
+       a = freq2k * budget * p * r2;
+       b = freq2k * budget * best->p * best->r2;
+       diff = ABS_DIFF((freq2k * p * r2), (LC_FREQ_2K * n2));
+       diff_best = ABS_DIFF((freq2k * best->p * best->r2),
+                            (LC_FREQ_2K * best->n2));
+       c = 1000000 * diff;
+       d = 1000000 * diff_best;
+
+       if (a < c && b < d) {
+               /* If both are above the budget, pick the closer */
+               if (best->p * best->r2 * diff < p * r2 * diff_best) {
+                       best->p = p;
+                       best->n2 = n2;
+                       best->r2 = r2;
+               }
+       } else if (a >= c && b < d) {
+               /* If A is below the threshold but B is above it?  Update. */
+               best->p = p;
+               best->n2 = n2;
+               best->r2 = r2;
+       } else if (a >= c && b >= d) {
+               /* Both are below the limit, so pick the higher n2/(r2*r2) */
+               if (n2 * best->r2 * best->r2 > best->n2 * r2 * r2) {
+                       best->p = p;
+                       best->n2 = n2;
+                       best->r2 = r2;
+               }
+       }
+       /* Otherwise a < c && b >= d, do nothing */
+}
+
+static void
+intel_ddi_calculate_wrpll(int clock /* in Hz */,
+                         unsigned *r2_out, unsigned *n2_out, unsigned *p_out)
+{
+       uint64_t freq2k;
+       unsigned p, n2, r2;
+       struct wrpll_rnp best = { 0, 0, 0 };
+       unsigned budget;
 
-       if (i == ARRAY_SIZE(wrpll_tmds_clock_table))
-               i--;
+       freq2k = clock / 100;
 
-       *p = wrpll_tmds_clock_table[i].p;
-       *n2 = wrpll_tmds_clock_table[i].n2;
-       *r2 = wrpll_tmds_clock_table[i].r2;
+       budget = wrpll_get_budget_for_freq(clock);
 
-       if (wrpll_tmds_clock_table[i].clock != clock)
-               DRM_INFO("WRPLL: using settings for %dKHz on %dKHz mode\n",
-                        wrpll_tmds_clock_table[i].clock, clock);
+       /* Special case handling for 540 pixel clock: bypass WR PLL entirely
+        * and directly pass the LC PLL to it. */
+       if (freq2k == 5400000) {
+               *n2_out = 2;
+               *p_out = 1;
+               *r2_out = 2;
+               return;
+       }
+
+       /*
+        * Ref = LC_FREQ / R, where Ref is the actual reference input seen by
+        * the WR PLL.
+        *
+        * We want R so that REF_MIN <= Ref <= REF_MAX.
+        * Injecting R2 = 2 * R gives:
+        *   REF_MAX * r2 > LC_FREQ * 2 and
+        *   REF_MIN * r2 < LC_FREQ * 2
+        *
+        * Which means the desired boundaries for r2 are:
+        *  LC_FREQ * 2 / REF_MAX < r2 < LC_FREQ * 2 / REF_MIN
+        *
+        */
+       for (r2 = LC_FREQ * 2 / REF_MAX + 1;
+            r2 <= LC_FREQ * 2 / REF_MIN;
+            r2++) {
+
+               /*
+                * VCO = N * Ref, that is: VCO = N * LC_FREQ / R
+                *
+                * Once again we want VCO_MIN <= VCO <= VCO_MAX.
+                * Injecting R2 = 2 * R and N2 = 2 * N, we get:
+                *   VCO_MAX * r2 > n2 * LC_FREQ and
+                *   VCO_MIN * r2 < n2 * LC_FREQ)
+                *
+                * Which means the desired boundaries for n2 are:
+                * VCO_MIN * r2 / LC_FREQ < n2 < VCO_MAX * r2 / LC_FREQ
+                */
+               for (n2 = VCO_MIN * r2 / LC_FREQ + 1;
+                    n2 <= VCO_MAX * r2 / LC_FREQ;
+                    n2++) {
 
-       DRM_DEBUG_KMS("WRPLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n",
-                     clock, *p, *n2, *r2);
+                       for (p = P_MIN; p <= P_MAX; p += P_INC)
+                               wrpll_update_rnp(freq2k, budget,
+                                                r2, n2, p, &best);
+               }
+       }
+
+       *n2_out = best.n2;
+       *p_out = best.p;
+       *r2_out = best.r2;
+
+       DRM_DEBUG_KMS("WRPLL: %dHz refresh rate with p=%d, n2=%d r2=%d\n",
+                     clock, *p_out, *n2_out, *r2_out);
 }
 
-bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock)
+bool intel_ddi_pll_mode_set(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
@@ -835,6 +634,7 @@ bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock)
        int type = intel_encoder->type;
        enum pipe pipe = intel_crtc->pipe;
        uint32_t reg, val;
+       int clock = intel_crtc->config.port_clock;
 
        /* TODO: reuse PLLs when possible (compare values) */
 
@@ -863,7 +663,7 @@ bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock)
                return true;
 
        } else if (type == INTEL_OUTPUT_HDMI) {
-               int p, n2, r2;
+               unsigned p, n2, r2;
 
                if (plls->wrpll1_refcount == 0) {
                        DRM_DEBUG_KMS("Using WRPLL 1 on pipe %c\n",
@@ -885,7 +685,7 @@ bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock)
                WARN(I915_READ(reg) & WRPLL_PLL_ENABLE,
                     "WRPLL already enabled\n");
 
-               intel_ddi_calculate_wrpll(clock, &p, &n2, &r2);
+               intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p);
 
                val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 |
                      WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) |
@@ -995,7 +795,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
                        /* 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 (dev_priv->pch_pf_size)
+                       if (intel_crtc->config.pch_pfit.size)
                                temp |= TRANS_DDI_EDP_INPUT_A_ONOFF;
                        else
                                temp |= TRANS_DDI_EDP_INPUT_A_ON;
@@ -1022,7 +822,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
 
        } else if (type == INTEL_OUTPUT_ANALOG) {
                temp |= TRANS_DDI_MODE_SELECT_FDI;
-               temp |= (intel_crtc->fdi_lanes - 1) << 1;
+               temp |= (intel_crtc->config.fdi_lanes - 1) << 1;
 
        } else if (type == INTEL_OUTPUT_DISPLAYPORT ||
                   type == INTEL_OUTPUT_EDP) {
@@ -1030,25 +830,10 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
 
                temp |= TRANS_DDI_MODE_SELECT_DP_SST;
 
-               switch (intel_dp->lane_count) {
-               case 1:
-                       temp |= TRANS_DDI_PORT_WIDTH_X1;
-                       break;
-               case 2:
-                       temp |= TRANS_DDI_PORT_WIDTH_X2;
-                       break;
-               case 4:
-                       temp |= TRANS_DDI_PORT_WIDTH_X4;
-                       break;
-               default:
-                       temp |= TRANS_DDI_PORT_WIDTH_X4;
-                       WARN(1, "Unsupported lane count %d\n",
-                            intel_dp->lane_count);
-               }
-
+               temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
        } else {
-               WARN(1, "Invalid encoder type %d for pipe %d\n",
-                    intel_encoder->type, pipe);
+               WARN(1, "Invalid encoder type %d for pipe %c\n",
+                    intel_encoder->type, pipe_name(pipe));
        }
 
        I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp);
@@ -1148,7 +933,7 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
                }
        }
 
-       DRM_DEBUG_KMS("No pipe for ddi port %i found\n", port);
+       DRM_DEBUG_KMS("No pipe for ddi port %c found\n", port_name(port));
 
        return false;
 }
@@ -1334,7 +1119,7 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder)
                ironlake_edp_backlight_on(intel_dp);
        }
 
-       if (intel_crtc->eld_vld) {
+       if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) {
                tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
                tmp |= ((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4));
                I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
@@ -1352,9 +1137,12 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder)
        struct drm_i915_private *dev_priv = dev->dev_private;
        uint32_t tmp;
 
-       tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
-       tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4));
-       I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
+       if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) {
+               tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
+               tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) <<
+                        (pipe * 4));
+               I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
+       }
 
        if (type == INTEL_OUTPUT_EDP) {
                struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
@@ -1366,14 +1154,14 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder)
 int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv)
 {
        if (I915_READ(HSW_FUSE_STRAP) & HSW_CDCLK_LIMIT)
-               return 450;
+               return 450000;
        else if ((I915_READ(LCPLL_CTL) & LCPLL_CLK_FREQ_MASK) ==
                 LCPLL_CLK_FREQ_450)
-               return 450;
+               return 450000;
        else if (IS_ULT(dev_priv->dev))
-               return 338;
+               return 337500;
        else
-               return 540;
+               return 540000;
 }
 
 void intel_ddi_pll_init(struct drm_device *dev)
@@ -1386,7 +1174,7 @@ void intel_ddi_pll_init(struct drm_device *dev)
         * Don't even try to turn it on.
         */
 
-       DRM_DEBUG_KMS("CDCLK running at %dMHz\n",
+       DRM_DEBUG_KMS("CDCLK running at %dKHz\n",
                      intel_ddi_get_cdclk_freq(dev_priv));
 
        if (val & LCPLL_CD_SOURCE_FCLK)
@@ -1472,6 +1260,27 @@ static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder)
                intel_dp_check_link_status(intel_dp);
 }
 
+static void intel_ddi_get_config(struct intel_encoder *encoder,
+                                struct intel_crtc_config *pipe_config)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
+       enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
+       u32 temp, flags = 0;
+
+       temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
+       if (temp & TRANS_DDI_PHSYNC)
+               flags |= DRM_MODE_FLAG_PHSYNC;
+       else
+               flags |= DRM_MODE_FLAG_NHSYNC;
+       if (temp & TRANS_DDI_PVSYNC)
+               flags |= DRM_MODE_FLAG_PVSYNC;
+       else
+               flags |= DRM_MODE_FLAG_NVSYNC;
+
+       pipe_config->adjusted_mode.flags |= flags;
+}
+
 static void intel_ddi_destroy(struct drm_encoder *encoder)
 {
        /* HDMI has nothing special to destroy, so we can go with this. */
@@ -1482,9 +1291,13 @@ static bool intel_ddi_compute_config(struct intel_encoder *encoder,
                                     struct intel_crtc_config *pipe_config)
 {
        int type = encoder->type;
+       int port = intel_ddi_get_encoder_port(encoder);
 
        WARN(type == INTEL_OUTPUT_UNKNOWN, "compute_config() on unknown output!\n");
 
+       if (port == PORT_A)
+               pipe_config->cpu_transcoder = TRANSCODER_EDP;
+
        if (type == INTEL_OUTPUT_HDMI)
                return intel_hdmi_compute_config(encoder, pipe_config);
        else
@@ -1518,16 +1331,6 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
                return;
        }
 
-       if (port != PORT_A) {
-               hdmi_connector = kzalloc(sizeof(struct intel_connector),
-                                        GFP_KERNEL);
-               if (!hdmi_connector) {
-                       kfree(dp_connector);
-                       kfree(intel_dig_port);
-                       return;
-               }
-       }
-
        intel_encoder = &intel_dig_port->base;
        encoder = &intel_encoder->base;
 
@@ -1541,12 +1344,11 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
        intel_encoder->disable = intel_disable_ddi;
        intel_encoder->post_disable = intel_ddi_post_disable;
        intel_encoder->get_hw_state = intel_ddi_get_hw_state;
+       intel_encoder->get_config = intel_ddi_get_config;
 
        intel_dig_port->port = port;
        intel_dig_port->port_reversal = I915_READ(DDI_BUF_CTL(port)) &
                                        DDI_BUF_PORT_REVERSAL;
-       if (hdmi_connector)
-               intel_dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port);
        intel_dig_port->dp.output_reg = DDI_BUF_CTL(port);
 
        intel_encoder->type = INTEL_OUTPUT_UNKNOWN;
@@ -1554,7 +1356,21 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
        intel_encoder->cloneable = false;
        intel_encoder->hot_plug = intel_ddi_hot_plug;
 
-       if (hdmi_connector)
+       if (!intel_dp_init_connector(intel_dig_port, dp_connector)) {
+               drm_encoder_cleanup(encoder);
+               kfree(intel_dig_port);
+               kfree(dp_connector);
+               return;
+       }
+
+       if (intel_encoder->type != INTEL_OUTPUT_EDP) {
+               hdmi_connector = kzalloc(sizeof(struct intel_connector),
+                                        GFP_KERNEL);
+               if (!hdmi_connector) {
+                       return;
+               }
+
+               intel_dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port);
                intel_hdmi_init_connector(intel_dig_port, hdmi_connector);
-       intel_dp_init_connector(intel_dig_port, dp_connector);
+       }
 }
index 56746dc..85f3eb7 100644 (file)
@@ -45,18 +45,6 @@ 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);
 
-typedef struct {
-       /* given values */
-       int n;
-       int m1, m2;
-       int p1, p2;
-       /* derived values */
-       int     dot;
-       int     vco;
-       int     m;
-       int     p;
-} intel_clock_t;
-
 typedef struct {
        int     min, max;
 } intel_range_t;
@@ -71,24 +59,6 @@ typedef struct intel_limit intel_limit_t;
 struct intel_limit {
        intel_range_t   dot, vco, n, m, m1, m2, p, p1;
        intel_p2_t          p2;
-       /**
-        * find_pll() - Find the best values for the PLL
-        * @limit: limits for the PLL
-        * @crtc: current CRTC
-        * @target: target frequency in kHz
-        * @refclk: reference clock frequency in kHz
-        * @match_clock: if provided, @best_clock P divider must
-        *               match the P divider from @match_clock
-        *               used for LVDS downclocking
-        * @best_clock: best PLL values found
-        *
-        * Returns true on success, false on failure.
-        */
-       bool (*find_pll)(const intel_limit_t *limit,
-                        struct drm_crtc *crtc,
-                        int target, int refclk,
-                        intel_clock_t *match_clock,
-                        intel_clock_t *best_clock);
 };
 
 /* FDI */
@@ -104,29 +74,6 @@ intel_pch_rawclk(struct drm_device *dev)
        return I915_READ(PCH_RAWCLK_FREQ) & RAWCLK_FREQ_MASK;
 }
 
-static bool
-intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
-                   int target, int refclk, intel_clock_t *match_clock,
-                   intel_clock_t *best_clock);
-static bool
-intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
-                       int target, int refclk, intel_clock_t *match_clock,
-                       intel_clock_t *best_clock);
-
-static bool
-intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc,
-                     int target, int refclk, intel_clock_t *match_clock,
-                     intel_clock_t *best_clock);
-static bool
-intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc,
-                          int target, int refclk, intel_clock_t *match_clock,
-                          intel_clock_t *best_clock);
-
-static bool
-intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc,
-                       int target, int refclk, intel_clock_t *match_clock,
-                       intel_clock_t *best_clock);
-
 static inline u32 /* units of 100MHz */
 intel_fdi_link_freq(struct drm_device *dev)
 {
@@ -148,7 +95,6 @@ static const intel_limit_t intel_limits_i8xx_dvo = {
        .p1 = { .min = 2, .max = 33 },
        .p2 = { .dot_limit = 165000,
                .p2_slow = 4, .p2_fast = 2 },
-       .find_pll = intel_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_i8xx_lvds = {
@@ -162,7 +108,6 @@ static const intel_limit_t intel_limits_i8xx_lvds = {
        .p1 = { .min = 1, .max = 6 },
        .p2 = { .dot_limit = 165000,
                .p2_slow = 14, .p2_fast = 7 },
-       .find_pll = intel_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_i9xx_sdvo = {
@@ -176,7 +121,6 @@ static const intel_limit_t intel_limits_i9xx_sdvo = {
        .p1 = { .min = 1, .max = 8 },
        .p2 = { .dot_limit = 200000,
                .p2_slow = 10, .p2_fast = 5 },
-       .find_pll = intel_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_i9xx_lvds = {
@@ -190,7 +134,6 @@ static const intel_limit_t intel_limits_i9xx_lvds = {
        .p1 = { .min = 1, .max = 8 },
        .p2 = { .dot_limit = 112000,
                .p2_slow = 14, .p2_fast = 7 },
-       .find_pll = intel_find_best_PLL,
 };
 
 
@@ -207,7 +150,6 @@ static const intel_limit_t intel_limits_g4x_sdvo = {
                .p2_slow = 10,
                .p2_fast = 10
        },
-       .find_pll = intel_g4x_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_g4x_hdmi = {
@@ -221,7 +163,6 @@ static const intel_limit_t intel_limits_g4x_hdmi = {
        .p1 = { .min = 1, .max = 8},
        .p2 = { .dot_limit = 165000,
                .p2_slow = 10, .p2_fast = 5 },
-       .find_pll = intel_g4x_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_g4x_single_channel_lvds = {
@@ -236,7 +177,6 @@ static const intel_limit_t intel_limits_g4x_single_channel_lvds = {
        .p2 = { .dot_limit = 0,
                .p2_slow = 14, .p2_fast = 14
        },
-       .find_pll = intel_g4x_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_g4x_dual_channel_lvds = {
@@ -251,21 +191,6 @@ static const intel_limit_t intel_limits_g4x_dual_channel_lvds = {
        .p2 = { .dot_limit = 0,
                .p2_slow = 7, .p2_fast = 7
        },
-       .find_pll = intel_g4x_find_best_PLL,
-};
-
-static const intel_limit_t intel_limits_g4x_display_port = {
-       .dot = { .min = 161670, .max = 227000 },
-       .vco = { .min = 1750000, .max = 3500000},
-       .n = { .min = 1, .max = 2 },
-       .m = { .min = 97, .max = 108 },
-       .m1 = { .min = 0x10, .max = 0x12 },
-       .m2 = { .min = 0x05, .max = 0x06 },
-       .p = { .min = 10, .max = 20 },
-       .p1 = { .min = 1, .max = 2},
-       .p2 = { .dot_limit = 0,
-               .p2_slow = 10, .p2_fast = 10 },
-       .find_pll = intel_find_pll_g4x_dp,
 };
 
 static const intel_limit_t intel_limits_pineview_sdvo = {
@@ -281,7 +206,6 @@ static const intel_limit_t intel_limits_pineview_sdvo = {
        .p1 = { .min = 1, .max = 8 },
        .p2 = { .dot_limit = 200000,
                .p2_slow = 10, .p2_fast = 5 },
-       .find_pll = intel_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_pineview_lvds = {
@@ -295,7 +219,6 @@ static const intel_limit_t intel_limits_pineview_lvds = {
        .p1 = { .min = 1, .max = 8 },
        .p2 = { .dot_limit = 112000,
                .p2_slow = 14, .p2_fast = 14 },
-       .find_pll = intel_find_best_PLL,
 };
 
 /* Ironlake / Sandybridge
@@ -314,7 +237,6 @@ static const intel_limit_t intel_limits_ironlake_dac = {
        .p1 = { .min = 1, .max = 8 },
        .p2 = { .dot_limit = 225000,
                .p2_slow = 10, .p2_fast = 5 },
-       .find_pll = intel_g4x_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_ironlake_single_lvds = {
@@ -328,7 +250,6 @@ static const intel_limit_t intel_limits_ironlake_single_lvds = {
        .p1 = { .min = 2, .max = 8 },
        .p2 = { .dot_limit = 225000,
                .p2_slow = 14, .p2_fast = 14 },
-       .find_pll = intel_g4x_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_ironlake_dual_lvds = {
@@ -342,7 +263,6 @@ static const intel_limit_t intel_limits_ironlake_dual_lvds = {
        .p1 = { .min = 2, .max = 8 },
        .p2 = { .dot_limit = 225000,
                .p2_slow = 7, .p2_fast = 7 },
-       .find_pll = intel_g4x_find_best_PLL,
 };
 
 /* LVDS 100mhz refclk limits. */
@@ -357,7 +277,6 @@ static const intel_limit_t intel_limits_ironlake_single_lvds_100m = {
        .p1 = { .min = 2, .max = 8 },
        .p2 = { .dot_limit = 225000,
                .p2_slow = 14, .p2_fast = 14 },
-       .find_pll = intel_g4x_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
@@ -371,21 +290,6 @@ static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
        .p1 = { .min = 2, .max = 6 },
        .p2 = { .dot_limit = 225000,
                .p2_slow = 7, .p2_fast = 7 },
-       .find_pll = intel_g4x_find_best_PLL,
-};
-
-static const intel_limit_t intel_limits_ironlake_display_port = {
-       .dot = { .min = 25000, .max = 350000 },
-       .vco = { .min = 1760000, .max = 3510000},
-       .n = { .min = 1, .max = 2 },
-       .m = { .min = 81, .max = 90 },
-       .m1 = { .min = 12, .max = 22 },
-       .m2 = { .min = 5, .max = 9 },
-       .p = { .min = 10, .max = 20 },
-       .p1 = { .min = 1, .max = 2},
-       .p2 = { .dot_limit = 0,
-               .p2_slow = 10, .p2_fast = 10 },
-       .find_pll = intel_find_pll_ironlake_dp,
 };
 
 static const intel_limit_t intel_limits_vlv_dac = {
@@ -396,15 +300,14 @@ static const intel_limit_t intel_limits_vlv_dac = {
        .m1 = { .min = 2, .max = 3 },
        .m2 = { .min = 11, .max = 156 },
        .p = { .min = 10, .max = 30 },
-       .p1 = { .min = 2, .max = 3 },
+       .p1 = { .min = 1, .max = 3 },
        .p2 = { .dot_limit = 270000,
                .p2_slow = 2, .p2_fast = 20 },
-       .find_pll = intel_vlv_find_best_pll,
 };
 
 static const intel_limit_t intel_limits_vlv_hdmi = {
-       .dot = { .min = 20000, .max = 165000 },
-       .vco = { .min = 4000000, .max = 5994000},
+       .dot = { .min = 25000, .max = 270000 },
+       .vco = { .min = 4000000, .max = 6000000 },
        .n = { .min = 1, .max = 7 },
        .m = { .min = 60, .max = 300 }, /* guess */
        .m1 = { .min = 2, .max = 3 },
@@ -413,7 +316,6 @@ static const intel_limit_t intel_limits_vlv_hdmi = {
        .p1 = { .min = 2, .max = 3 },
        .p2 = { .dot_limit = 270000,
                .p2_slow = 2, .p2_fast = 20 },
-       .find_pll = intel_vlv_find_best_pll,
 };
 
 static const intel_limit_t intel_limits_vlv_dp = {
@@ -424,61 +326,11 @@ static const intel_limit_t intel_limits_vlv_dp = {
        .m1 = { .min = 2, .max = 3 },
        .m2 = { .min = 11, .max = 156 },
        .p = { .min = 10, .max = 30 },
-       .p1 = { .min = 2, .max = 3 },
+       .p1 = { .min = 1, .max = 3 },
        .p2 = { .dot_limit = 270000,
                .p2_slow = 2, .p2_fast = 20 },
-       .find_pll = intel_vlv_find_best_pll,
 };
 
-u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg)
-{
-       WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock));
-
-       if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) {
-               DRM_ERROR("DPIO idle wait timed out\n");
-               return 0;
-       }
-
-       I915_WRITE(DPIO_REG, reg);
-       I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_READ | DPIO_PORTID |
-                  DPIO_BYTE);
-       if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) {
-               DRM_ERROR("DPIO read wait timed out\n");
-               return 0;
-       }
-
-       return I915_READ(DPIO_DATA);
-}
-
-static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg,
-                            u32 val)
-{
-       WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock));
-
-       if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) {
-               DRM_ERROR("DPIO idle wait timed out\n");
-               return;
-       }
-
-       I915_WRITE(DPIO_DATA, val);
-       I915_WRITE(DPIO_REG, reg);
-       I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_WRITE | DPIO_PORTID |
-                  DPIO_BYTE);
-       if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100))
-               DRM_ERROR("DPIO write wait timed out\n");
-}
-
-static void vlv_init_dpio(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       /* Reset the DPIO config */
-       I915_WRITE(DPIO_CTL, 0);
-       POSTING_READ(DPIO_CTL);
-       I915_WRITE(DPIO_CTL, 1);
-       POSTING_READ(DPIO_CTL);
-}
-
 static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
                                                int refclk)
 {
@@ -497,10 +349,7 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
                        else
                                limit = &intel_limits_ironlake_single_lvds;
                }
-       } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
-                  intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))
-               limit = &intel_limits_ironlake_display_port;
-       else
+       } else
                limit = &intel_limits_ironlake_dac;
 
        return limit;
@@ -521,8 +370,6 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
                limit = &intel_limits_g4x_hdmi;
        } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) {
                limit = &intel_limits_g4x_sdvo;
-       } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
-               limit = &intel_limits_g4x_display_port;
        } else /* The option is for other outputs */
                limit = &intel_limits_i9xx_sdvo;
 
@@ -573,13 +420,14 @@ static void pineview_clock(int refclk, intel_clock_t *clock)
        clock->dot = clock->vco / clock->p;
 }
 
-static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock)
+static uint32_t i9xx_dpll_compute_m(struct dpll *dpll)
 {
-       if (IS_PINEVIEW(dev)) {
-               pineview_clock(refclk, clock);
-               return;
-       }
-       clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+       return 5 * (dpll->m1 + 2) + (dpll->m2 + 2);
+}
+
+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;
@@ -636,10 +484,9 @@ static bool intel_PLL_is_valid(struct drm_device *dev,
 }
 
 static bool
-intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+i9xx_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)
-
 {
        struct drm_device *dev = crtc->dev;
        intel_clock_t clock;
@@ -668,8 +515,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
             clock.m1++) {
                for (clock.m2 = limit->m2.min;
                     clock.m2 <= limit->m2.max; clock.m2++) {
-                       /* m1 is always 0 in Pineview */
-                       if (clock.m2 >= clock.m1 && !IS_PINEVIEW(dev))
+                       if (clock.m2 >= clock.m1)
                                break;
                        for (clock.n = limit->n.min;
                             clock.n <= limit->n.max; clock.n++) {
@@ -677,7 +523,66 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                                        clock.p1 <= limit->p1.max; clock.p1++) {
                                        int this_err;
 
-                                       intel_clock(dev, refclk, &clock);
+                                       i9xx_clock(refclk, &clock);
+                                       if (!intel_PLL_is_valid(dev, limit,
+                                                               &clock))
+                                               continue;
+                                       if (match_clock &&
+                                           clock.p != match_clock->p)
+                                               continue;
+
+                                       this_err = abs(clock.dot - target);
+                                       if (this_err < err) {
+                                               *best_clock = clock;
+                                               err = this_err;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return (err != target);
+}
+
+static bool
+pnv_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)
+{
+       struct drm_device *dev = crtc->dev;
+       intel_clock_t clock;
+       int err = target;
+
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+               /*
+                * For LVDS just rely on its current settings for dual-channel.
+                * We haven't figured out how to reliably set up different
+                * single/dual channel state, if we even can.
+                */
+               if (intel_is_dual_link_lvds(dev))
+                       clock.p2 = limit->p2.p2_fast;
+               else
+                       clock.p2 = limit->p2.p2_slow;
+       } else {
+               if (target < limit->p2.dot_limit)
+                       clock.p2 = limit->p2.p2_slow;
+               else
+                       clock.p2 = limit->p2.p2_fast;
+       }
+
+       memset(best_clock, 0, sizeof(*best_clock));
+
+       for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
+            clock.m1++) {
+               for (clock.m2 = limit->m2.min;
+                    clock.m2 <= limit->m2.max; clock.m2++) {
+                       for (clock.n = limit->n.min;
+                            clock.n <= limit->n.max; clock.n++) {
+                               for (clock.p1 = limit->p1.min;
+                                       clock.p1 <= limit->p1.max; clock.p1++) {
+                                       int this_err;
+
+                                       pineview_clock(refclk, &clock);
                                        if (!intel_PLL_is_valid(dev, limit,
                                                                &clock))
                                                continue;
@@ -699,9 +604,9 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 }
 
 static bool
-intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
-                       int target, int refclk, intel_clock_t *match_clock,
-                       intel_clock_t *best_clock)
+g4x_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)
 {
        struct drm_device *dev = crtc->dev;
        intel_clock_t clock;
@@ -712,12 +617,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
        found = false;
 
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
-               int lvds_reg;
-
-               if (HAS_PCH_SPLIT(dev))
-                       lvds_reg = PCH_LVDS;
-               else
-                       lvds_reg = LVDS;
                if (intel_is_dual_link_lvds(dev))
                        clock.p2 = limit->p2.p2_fast;
                else
@@ -742,13 +641,10 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                                     clock.p1 >= limit->p1.min; clock.p1--) {
                                        int this_err;
 
-                                       intel_clock(dev, refclk, &clock);
+                                       i9xx_clock(refclk, &clock);
                                        if (!intel_PLL_is_valid(dev, limit,
                                                                &clock))
                                                continue;
-                                       if (match_clock &&
-                                           clock.p != match_clock->p)
-                                               continue;
 
                                        this_err = abs(clock.dot - target);
                                        if (this_err < err_most) {
@@ -765,62 +661,9 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 }
 
 static bool
-intel_find_pll_ironlake_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
-                          int target, int refclk, intel_clock_t *match_clock,
-                          intel_clock_t *best_clock)
-{
-       struct drm_device *dev = crtc->dev;
-       intel_clock_t clock;
-
-       if (target < 200000) {
-               clock.n = 1;
-               clock.p1 = 2;
-               clock.p2 = 10;
-               clock.m1 = 12;
-               clock.m2 = 9;
-       } else {
-               clock.n = 2;
-               clock.p1 = 1;
-               clock.p2 = 10;
-               clock.m1 = 14;
-               clock.m2 = 8;
-       }
-       intel_clock(dev, refclk, &clock);
-       memcpy(best_clock, &clock, sizeof(intel_clock_t));
-       return true;
-}
-
-/* DisplayPort has only two frequencies, 162MHz and 270MHz */
-static bool
-intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
-                     int target, int refclk, intel_clock_t *match_clock,
-                     intel_clock_t *best_clock)
-{
-       intel_clock_t clock;
-       if (target < 200000) {
-               clock.p1 = 2;
-               clock.p2 = 10;
-               clock.n = 2;
-               clock.m1 = 23;
-               clock.m2 = 8;
-       } else {
-               clock.p1 = 1;
-               clock.p2 = 10;
-               clock.n = 1;
-               clock.m1 = 14;
-               clock.m2 = 2;
-       }
-       clock.m = 5 * (clock.m1 + 2) + (clock.m2 + 2);
-       clock.p = (clock.p1 * clock.p2);
-       clock.dot = 96000 * clock.m / (clock.n + 2) / clock.p;
-       clock.vco = 0;
-       memcpy(best_clock, &clock, sizeof(intel_clock_t));
-       return true;
-}
-static bool
-intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc,
-                       int target, int refclk, intel_clock_t *match_clock,
-                       intel_clock_t *best_clock)
+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;
@@ -1066,14 +909,24 @@ static void assert_pll(struct drm_i915_private *dev_priv,
 #define assert_pll_enabled(d, p) assert_pll(d, p, true)
 #define assert_pll_disabled(d, p) assert_pll(d, p, false)
 
+static struct intel_shared_dpll *
+intel_crtc_to_shared_dpll(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+
+       if (crtc->config.shared_dpll < 0)
+               return NULL;
+
+       return &dev_priv->shared_dplls[crtc->config.shared_dpll];
+}
+
 /* For ILK+ */
-static void assert_pch_pll(struct drm_i915_private *dev_priv,
-                          struct intel_pch_pll *pll,
-                          struct intel_crtc *crtc,
-                          bool state)
+static void assert_shared_dpll(struct drm_i915_private *dev_priv,
+                              struct intel_shared_dpll *pll,
+                              bool state)
 {
-       u32 val;
        bool cur_state;
+       struct intel_dpll_hw_state hw_state;
 
        if (HAS_PCH_LPT(dev_priv->dev)) {
                DRM_DEBUG_DRIVER("LPT detected: skipping PCH PLL test\n");
@@ -1081,36 +934,16 @@ static void assert_pch_pll(struct drm_i915_private *dev_priv,
        }
 
        if (WARN (!pll,
-                 "asserting PCH PLL %s with no PLL\n", state_string(state)))
+                 "asserting DPLL %s with no DPLL\n", state_string(state)))
                return;
 
-       val = I915_READ(pll->pll_reg);
-       cur_state = !!(val & DPLL_VCO_ENABLE);
+       cur_state = pll->get_hw_state(dev_priv, pll, &hw_state);
        WARN(cur_state != state,
-            "PCH PLL state for reg %x assertion failure (expected %s, current %s), val=%08x\n",
-            pll->pll_reg, state_string(state), state_string(cur_state), val);
-
-       /* Make sure the selected PLL is correctly attached to the transcoder */
-       if (crtc && HAS_PCH_CPT(dev_priv->dev)) {
-               u32 pch_dpll;
-
-               pch_dpll = I915_READ(PCH_DPLL_SEL);
-               cur_state = pll->pll_reg == _PCH_DPLL_B;
-               if (!WARN(((pch_dpll >> (4 * crtc->pipe)) & 1) != cur_state,
-                         "PLL[%d] not attached to this transcoder %d: %08x\n",
-                         cur_state, crtc->pipe, pch_dpll)) {
-                       cur_state = !!(val >> (4*crtc->pipe + 3));
-                       WARN(cur_state != state,
-                            "PLL[%d] not %s on this transcoder %d: %08x\n",
-                            pll->pll_reg == _PCH_DPLL_B,
-                            state_string(state),
-                            crtc->pipe,
-                            val);
-               }
-       }
+            "%s assertion failure (expected %s, current %s)\n",
+            pll->name, state_string(state), state_string(cur_state));
 }
-#define assert_pch_pll_enabled(d, p, c) assert_pch_pll(d, p, c, true)
-#define assert_pch_pll_disabled(d, p, c) assert_pch_pll(d, p, c, false)
+#define assert_shared_dpll_enabled(d, p) assert_shared_dpll(d, p, true)
+#define assert_shared_dpll_disabled(d, p) assert_shared_dpll(d, p, false)
 
 static void assert_fdi_tx(struct drm_i915_private *dev_priv,
                          enum pipe pipe, bool state)
@@ -1227,8 +1060,8 @@ void assert_pipe(struct drm_i915_private *dev_priv,
        if (pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE)
                state = true;
 
-       if (!intel_using_power_well(dev_priv->dev) &&
-           cpu_transcoder != TRANSCODER_EDP) {
+       if (!intel_display_power_enabled(dev_priv->dev,
+                               POWER_DOMAIN_TRANSCODER(cpu_transcoder))) {
                cur_state = false;
        } else {
                reg = PIPECONF(cpu_transcoder);
@@ -1262,12 +1095,13 @@ static void assert_plane(struct drm_i915_private *dev_priv,
 static void assert_planes_disabled(struct drm_i915_private *dev_priv,
                                   enum pipe pipe)
 {
+       struct drm_device *dev = dev_priv->dev;
        int reg, i;
        u32 val;
        int cur_pipe;
 
-       /* Planes are fixed to pipes on ILK+ */
-       if (HAS_PCH_SPLIT(dev_priv->dev) || IS_VALLEYVIEW(dev_priv->dev)) {
+       /* Primary planes are fixed to pipes on gen4+ */
+       if (INTEL_INFO(dev)->gen >= 4) {
                reg = DSPCNTR(pipe);
                val = I915_READ(reg);
                WARN((val & DISPLAY_PLANE_ENABLE),
@@ -1277,7 +1111,7 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv,
        }
 
        /* Need to check both planes against the pipe */
-       for (i = 0; i < 2; i++) {
+       for (i = 0; i < INTEL_INFO(dev)->num_pipes; i++) {
                reg = DSPCNTR(i);
                val = I915_READ(reg);
                cur_pipe = (val & DISPPLANE_SEL_PIPE_MASK) >>
@@ -1291,19 +1125,30 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv,
 static void assert_sprites_disabled(struct drm_i915_private *dev_priv,
                                    enum pipe pipe)
 {
+       struct drm_device *dev = dev_priv->dev;
        int reg, i;
        u32 val;
 
-       if (!IS_VALLEYVIEW(dev_priv->dev))
-               return;
-
-       /* Need to check both planes against the pipe */
-       for (i = 0; i < dev_priv->num_plane; i++) {
-               reg = SPCNTR(pipe, i);
+       if (IS_VALLEYVIEW(dev)) {
+               for (i = 0; i < dev_priv->num_plane; i++) {
+                       reg = SPCNTR(pipe, i);
+                       val = I915_READ(reg);
+                       WARN((val & SP_ENABLE),
+                            "sprite %c assertion failure, should be off on pipe %c but is still active\n",
+                            sprite_name(pipe, i), pipe_name(pipe));
+               }
+       } else if (INTEL_INFO(dev)->gen >= 7) {
+               reg = SPRCTL(pipe);
                val = I915_READ(reg);
-               WARN((val & SP_ENABLE),
-                    "sprite %d assertion failure, should be off on pipe %c but is still active\n",
-                    pipe * 2 + i, pipe_name(pipe));
+               WARN((val & SPRITE_ENABLE),
+                    "sprite %c assertion failure, should be off on pipe %c but is still active\n",
+                    plane_name(pipe), pipe_name(pipe));
+       } else if (INTEL_INFO(dev)->gen >= 5) {
+               reg = DVSCNTR(pipe);
+               val = I915_READ(reg);
+               WARN((val & DVS_ENABLE),
+                    "sprite %c assertion failure, should be off on pipe %c but is still active\n",
+                    plane_name(pipe), pipe_name(pipe));
        }
 }
 
@@ -1323,14 +1168,14 @@ static void assert_pch_refclk_enabled(struct drm_i915_private *dev_priv)
        WARN(!enabled, "PCH refclk assertion failure, should be active but is disabled\n");
 }
 
-static void assert_transcoder_disabled(struct drm_i915_private *dev_priv,
-                                      enum pipe pipe)
+static void assert_pch_transcoder_disabled(struct drm_i915_private *dev_priv,
+                                          enum pipe pipe)
 {
        int reg;
        u32 val;
        bool enabled;
 
-       reg = TRANSCONF(pipe);
+       reg = PCH_TRANSCONF(pipe);
        val = I915_READ(reg);
        enabled = !!(val & TRANS_ENABLE);
        WARN(enabled,
@@ -1474,6 +1319,8 @@ static void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
        int reg;
        u32 val;
 
+       assert_pipe_disabled(dev_priv, pipe);
+
        /* No really, not for ILK+ */
        BUG_ON(!IS_VALLEYVIEW(dev_priv->dev) && dev_priv->info->gen >= 5);
 
@@ -1525,156 +1372,86 @@ static void intel_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
        POSTING_READ(reg);
 }
 
-/* SBI access */
-static void
-intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value,
-               enum intel_sbi_destination destination)
-{
-       u32 tmp;
-
-       WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock));
-
-       if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0,
-                               100)) {
-               DRM_ERROR("timeout waiting for SBI to become ready\n");
-               return;
-       }
-
-       I915_WRITE(SBI_ADDR, (reg << 16));
-       I915_WRITE(SBI_DATA, value);
-
-       if (destination == SBI_ICLK)
-               tmp = SBI_CTL_DEST_ICLK | SBI_CTL_OP_CRWR;
-       else
-               tmp = SBI_CTL_DEST_MPHY | SBI_CTL_OP_IOWR;
-       I915_WRITE(SBI_CTL_STAT, SBI_BUSY | tmp);
-
-       if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0,
-                               100)) {
-               DRM_ERROR("timeout waiting for SBI to complete write transaction\n");
-               return;
-       }
-}
-
-static u32
-intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg,
-              enum intel_sbi_destination destination)
+void vlv_wait_port_ready(struct drm_i915_private *dev_priv, int port)
 {
-       u32 value = 0;
-       WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock));
-
-       if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0,
-                               100)) {
-               DRM_ERROR("timeout waiting for SBI to become ready\n");
-               return 0;
-       }
-
-       I915_WRITE(SBI_ADDR, (reg << 16));
+       u32 port_mask;
 
-       if (destination == SBI_ICLK)
-               value = SBI_CTL_DEST_ICLK | SBI_CTL_OP_CRRD;
+       if (!port)
+               port_mask = DPLL_PORTB_READY_MASK;
        else
-               value = SBI_CTL_DEST_MPHY | SBI_CTL_OP_IORD;
-       I915_WRITE(SBI_CTL_STAT, value | SBI_BUSY);
-
-       if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0,
-                               100)) {
-               DRM_ERROR("timeout waiting for SBI to complete read transaction\n");
-               return 0;
-       }
+               port_mask = DPLL_PORTC_READY_MASK;
 
-       return I915_READ(SBI_DATA);
+       if (wait_for((I915_READ(DPLL(0)) & port_mask) == 0, 1000))
+               WARN(1, "timed out waiting for port %c ready: 0x%08x\n",
+                    'B' + port, I915_READ(DPLL(0)));
 }
 
 /**
- * ironlake_enable_pch_pll - enable PCH PLL
+ * ironlake_enable_shared_dpll - enable PCH PLL
  * @dev_priv: i915 private structure
  * @pipe: pipe PLL to enable
  *
  * The PCH PLL needs to be enabled before the PCH transcoder, since it
  * drives the transcoder clock.
  */
-static void ironlake_enable_pch_pll(struct intel_crtc *intel_crtc)
+static void ironlake_enable_shared_dpll(struct intel_crtc *crtc)
 {
-       struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private;
-       struct intel_pch_pll *pll;
-       int reg;
-       u32 val;
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
 
        /* PCH PLLs only available on ILK, SNB and IVB */
        BUG_ON(dev_priv->info->gen < 5);
-       pll = intel_crtc->pch_pll;
-       if (pll == NULL)
+       if (WARN_ON(pll == NULL))
                return;
 
        if (WARN_ON(pll->refcount == 0))
                return;
 
-       DRM_DEBUG_KMS("enable PCH PLL %x (active %d, on? %d)for crtc %d\n",
-                     pll->pll_reg, pll->active, pll->on,
-                     intel_crtc->base.base.id);
+       DRM_DEBUG_KMS("enable %s (active %d, on? %d)for crtc %d\n",
+                     pll->name, pll->active, pll->on,
+                     crtc->base.base.id);
 
-       /* PCH refclock must be enabled first */
-       assert_pch_refclk_enabled(dev_priv);
-
-       if (pll->active++ && pll->on) {
-               assert_pch_pll_enabled(dev_priv, pll, NULL);
+       if (pll->active++) {
+               WARN_ON(!pll->on);
+               assert_shared_dpll_enabled(dev_priv, pll);
                return;
        }
+       WARN_ON(pll->on);
 
-       DRM_DEBUG_KMS("enabling PCH PLL %x\n", pll->pll_reg);
-
-       reg = pll->pll_reg;
-       val = I915_READ(reg);
-       val |= DPLL_VCO_ENABLE;
-       I915_WRITE(reg, val);
-       POSTING_READ(reg);
-       udelay(200);
-
+       DRM_DEBUG_KMS("enabling %s\n", pll->name);
+       pll->enable(dev_priv, pll);
        pll->on = true;
 }
 
-static void intel_disable_pch_pll(struct intel_crtc *intel_crtc)
+static void intel_disable_shared_dpll(struct intel_crtc *crtc)
 {
-       struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private;
-       struct intel_pch_pll *pll = intel_crtc->pch_pll;
-       int reg;
-       u32 val;
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
 
        /* PCH only available on ILK+ */
        BUG_ON(dev_priv->info->gen < 5);
-       if (pll == NULL)
+       if (WARN_ON(pll == NULL))
               return;
 
        if (WARN_ON(pll->refcount == 0))
                return;
 
-       DRM_DEBUG_KMS("disable PCH PLL %x (active %d, on? %d) for crtc %d\n",
-                     pll->pll_reg, pll->active, pll->on,
-                     intel_crtc->base.base.id);
+       DRM_DEBUG_KMS("disable %s (active %d, on? %d) for crtc %d\n",
+                     pll->name, pll->active, pll->on,
+                     crtc->base.base.id);
 
        if (WARN_ON(pll->active == 0)) {
-               assert_pch_pll_disabled(dev_priv, pll, NULL);
+               assert_shared_dpll_disabled(dev_priv, pll);
                return;
        }
 
-       if (--pll->active) {
-               assert_pch_pll_enabled(dev_priv, pll, NULL);
+       assert_shared_dpll_enabled(dev_priv, pll);
+       WARN_ON(!pll->on);
+       if (--pll->active)
                return;
-       }
-
-       DRM_DEBUG_KMS("disabling PCH PLL %x\n", pll->pll_reg);
-
-       /* Make sure transcoder isn't still depending on us */
-       assert_transcoder_disabled(dev_priv, intel_crtc->pipe);
-
-       reg = pll->pll_reg;
-       val = I915_READ(reg);
-       val &= ~DPLL_VCO_ENABLE;
-       I915_WRITE(reg, val);
-       POSTING_READ(reg);
-       udelay(200);
 
+       DRM_DEBUG_KMS("disabling %s\n", pll->name);
+       pll->disable(dev_priv, pll);
        pll->on = false;
 }
 
@@ -1683,15 +1460,15 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv,
 {
        struct drm_device *dev = dev_priv->dev;
        struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        uint32_t reg, val, pipeconf_val;
 
        /* PCH only available on ILK+ */
        BUG_ON(dev_priv->info->gen < 5);
 
        /* Make sure PCH DPLL is enabled */
-       assert_pch_pll_enabled(dev_priv,
-                              to_intel_crtc(crtc)->pch_pll,
-                              to_intel_crtc(crtc));
+       assert_shared_dpll_enabled(dev_priv,
+                                  intel_crtc_to_shared_dpll(intel_crtc));
 
        /* FDI must be feeding us bits for PCH ports */
        assert_fdi_tx_enabled(dev_priv, pipe);
@@ -1706,7 +1483,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv,
                I915_WRITE(reg, val);
        }
 
-       reg = TRANSCONF(pipe);
+       reg = PCH_TRANSCONF(pipe);
        val = I915_READ(reg);
        pipeconf_val = I915_READ(PIPECONF(pipe));
 
@@ -1731,7 +1508,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv,
 
        I915_WRITE(reg, val | TRANS_ENABLE);
        if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100))
-               DRM_ERROR("failed to enable transcoder %d\n", pipe);
+               DRM_ERROR("failed to enable transcoder %c\n", pipe_name(pipe));
 }
 
 static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv,
@@ -1760,8 +1537,8 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv,
        else
                val |= TRANS_PROGRESSIVE;
 
-       I915_WRITE(TRANSCONF(TRANSCODER_A), val);
-       if (wait_for(I915_READ(_TRANSACONF) & TRANS_STATE_ENABLE, 100))
+       I915_WRITE(LPT_TRANSCONF, val);
+       if (wait_for(I915_READ(LPT_TRANSCONF) & TRANS_STATE_ENABLE, 100))
                DRM_ERROR("Failed to enable PCH transcoder\n");
 }
 
@@ -1778,13 +1555,13 @@ static void ironlake_disable_pch_transcoder(struct drm_i915_private *dev_priv,
        /* Ports must be off as well */
        assert_pch_ports_disabled(dev_priv, pipe);
 
-       reg = TRANSCONF(pipe);
+       reg = PCH_TRANSCONF(pipe);
        val = I915_READ(reg);
        val &= ~TRANS_ENABLE;
        I915_WRITE(reg, val);
        /* wait for PCH transcoder off, transcoder state */
        if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50))
-               DRM_ERROR("failed to disable transcoder %d\n", pipe);
+               DRM_ERROR("failed to disable transcoder %c\n", pipe_name(pipe));
 
        if (!HAS_PCH_IBX(dev)) {
                /* Workaround: Clear the timing override chicken bit again. */
@@ -1799,11 +1576,11 @@ static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv)
 {
        u32 val;
 
-       val = I915_READ(_TRANSACONF);
+       val = I915_READ(LPT_TRANSCONF);
        val &= ~TRANS_ENABLE;
-       I915_WRITE(_TRANSACONF, val);
+       I915_WRITE(LPT_TRANSCONF, val);
        /* wait for PCH transcoder off, transcoder state */
-       if (wait_for((I915_READ(_TRANSACONF) & TRANS_STATE_ENABLE) == 0, 50))
+       if (wait_for((I915_READ(LPT_TRANSCONF) & TRANS_STATE_ENABLE) == 0, 50))
                DRM_ERROR("Failed to disable PCH transcoder\n");
 
        /* Workaround: clear timing override bit. */
@@ -1835,6 +1612,9 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
        int reg;
        u32 val;
 
+       assert_planes_disabled(dev_priv, pipe);
+       assert_sprites_disabled(dev_priv, pipe);
+
        if (HAS_PCH_LPT(dev_priv->dev))
                pch_transcoder = TRANSCODER_A;
        else
@@ -2096,7 +1876,7 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        case 1:
                break;
        default:
-               DRM_ERROR("Can't update plane %d in SAREA\n", plane);
+               DRM_ERROR("Can't update plane %c in SAREA\n", plane_name(plane));
                return -EINVAL;
        }
 
@@ -2145,6 +1925,9 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                        dspcntr &= ~DISPPLANE_TILED;
        }
 
+       if (IS_G4X(dev))
+               dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
+
        I915_WRITE(reg, dspcntr);
 
        linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8);
@@ -2193,7 +1976,7 @@ static int ironlake_update_plane(struct drm_crtc *crtc,
        case 2:
                break;
        default:
-               DRM_ERROR("Can't update plane %d in SAREA\n", plane);
+               DRM_ERROR("Can't update plane %c in SAREA\n", plane_name(plane));
                return -EINVAL;
        }
 
@@ -2384,9 +2167,9 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        }
 
        if (intel_crtc->plane > INTEL_INFO(dev)->num_pipes) {
-               DRM_ERROR("no plane for crtc: plane %d, num_pipes %d\n",
-                               intel_crtc->plane,
-                               INTEL_INFO(dev)->num_pipes);
+               DRM_ERROR("no plane for crtc: plane %c, num_pipes %d\n",
+                         plane_name(intel_crtc->plane),
+                         INTEL_INFO(dev)->num_pipes);
                return -EINVAL;
        }
 
@@ -2414,7 +2197,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        crtc->y = y;
 
        if (old_fb) {
-               intel_wait_for_vblank(dev, intel_crtc->pipe);
+               if (intel_crtc->active && old_fb != fb)
+                       intel_wait_for_vblank(dev, intel_crtc->pipe);
                intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj);
        }
 
@@ -2467,6 +2251,11 @@ static void intel_fdi_normal_train(struct drm_crtc *crtc)
                           FDI_FE_ERRC_ENABLE);
 }
 
+static bool pipe_has_enabled_pch(struct intel_crtc *intel_crtc)
+{
+       return intel_crtc->base.enabled && intel_crtc->config.has_pch_encoder;
+}
+
 static void ivb_modeset_global_resources(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2476,10 +2265,13 @@ static void ivb_modeset_global_resources(struct drm_device *dev)
                to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_C]);
        uint32_t temp;
 
-       /* When everything is off disable fdi C so that we could enable fdi B
-        * with all lanes. XXX: This misses the case where a pipe is not using
-        * any pch resources and so doesn't need any fdi lanes. */
-       if (!pipe_B_crtc->base.enabled && !pipe_C_crtc->base.enabled) {
+       /*
+        * When everything is off disable fdi C so that we could enable fdi B
+        * with all lanes. Note that we don't care about enabled pipes without
+        * an enabled pch encoder.
+        */
+       if (!pipe_has_enabled_pch(pipe_B_crtc) &&
+           !pipe_has_enabled_pch(pipe_C_crtc)) {
                WARN_ON(I915_READ(FDI_RX_CTL(PIPE_B)) & FDI_RX_ENABLE);
                WARN_ON(I915_READ(FDI_RX_CTL(PIPE_C)) & FDI_RX_ENABLE);
 
@@ -2517,8 +2309,8 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc)
        /* enable CPU FDI TX and PCH FDI RX */
        reg = FDI_TX_CTL(pipe);
        temp = I915_READ(reg);
-       temp &= ~(7 << 19);
-       temp |= (intel_crtc->fdi_lanes - 1) << 19;
+       temp &= ~FDI_DP_PORT_WIDTH_MASK;
+       temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes);
        temp &= ~FDI_LINK_TRAIN_NONE;
        temp |= FDI_LINK_TRAIN_PATTERN_1;
        I915_WRITE(reg, temp | FDI_TX_ENABLE);
@@ -2615,8 +2407,8 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
        /* enable CPU FDI TX and PCH FDI RX */
        reg = FDI_TX_CTL(pipe);
        temp = I915_READ(reg);
-       temp &= ~(7 << 19);
-       temp |= (intel_crtc->fdi_lanes - 1) << 19;
+       temp &= ~FDI_DP_PORT_WIDTH_MASK;
+       temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes);
        temp &= ~FDI_LINK_TRAIN_NONE;
        temp |= FDI_LINK_TRAIN_PATTERN_1;
        temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
@@ -2750,8 +2542,8 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
        /* enable CPU FDI TX and PCH FDI RX */
        reg = FDI_TX_CTL(pipe);
        temp = I915_READ(reg);
-       temp &= ~(7 << 19);
-       temp |= (intel_crtc->fdi_lanes - 1) << 19;
+       temp &= ~FDI_DP_PORT_WIDTH_MASK;
+       temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes);
        temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB);
        temp |= FDI_LINK_TRAIN_PATTERN_1_IVB;
        temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
@@ -2852,8 +2644,8 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc)
        /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
        reg = FDI_RX_CTL(pipe);
        temp = I915_READ(reg);
-       temp &= ~((0x7 << 19) | (0x7 << 16));
-       temp |= (intel_crtc->fdi_lanes - 1) << 19;
+       temp &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16));
+       temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes);
        temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11;
        I915_WRITE(reg, temp | FDI_RX_PLL_ENABLE);
 
@@ -3085,6 +2877,30 @@ static void lpt_program_iclkip(struct drm_crtc *crtc)
        mutex_unlock(&dev_priv->dpio_lock);
 }
 
+static void ironlake_pch_transcoder_set_timings(struct intel_crtc *crtc,
+                                               enum pipe pch_transcoder)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum transcoder cpu_transcoder = crtc->config.cpu_transcoder;
+
+       I915_WRITE(PCH_TRANS_HTOTAL(pch_transcoder),
+                  I915_READ(HTOTAL(cpu_transcoder)));
+       I915_WRITE(PCH_TRANS_HBLANK(pch_transcoder),
+                  I915_READ(HBLANK(cpu_transcoder)));
+       I915_WRITE(PCH_TRANS_HSYNC(pch_transcoder),
+                  I915_READ(HSYNC(cpu_transcoder)));
+
+       I915_WRITE(PCH_TRANS_VTOTAL(pch_transcoder),
+                  I915_READ(VTOTAL(cpu_transcoder)));
+       I915_WRITE(PCH_TRANS_VBLANK(pch_transcoder),
+                  I915_READ(VBLANK(cpu_transcoder)));
+       I915_WRITE(PCH_TRANS_VSYNC(pch_transcoder),
+                  I915_READ(VSYNC(cpu_transcoder)));
+       I915_WRITE(PCH_TRANS_VSYNCSHIFT(pch_transcoder),
+                  I915_READ(VSYNCSHIFT(cpu_transcoder)));
+}
+
 /*
  * Enable PCH resources required for PCH ports:
  *   - PCH PLLs
@@ -3101,7 +2917,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
        int pipe = intel_crtc->pipe;
        u32 reg, temp;
 
-       assert_transcoder_disabled(dev_priv, pipe);
+       assert_pch_transcoder_disabled(dev_priv, pipe);
 
        /* Write the TU size bits before fdi link training, so that error
         * detection works. */
@@ -3115,31 +2931,18 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
         * transcoder, and we actually should do this to not upset any PCH
         * transcoder that already use the clock when we share it.
         *
-        * Note that enable_pch_pll tries to do the right thing, but get_pch_pll
-        * unconditionally resets the pll - we need that to have the right LVDS
-        * enable sequence. */
-       ironlake_enable_pch_pll(intel_crtc);
+        * Note that enable_shared_dpll tries to do the right thing, but
+        * get_shared_dpll unconditionally resets the pll - we need that to have
+        * the right LVDS enable sequence. */
+       ironlake_enable_shared_dpll(intel_crtc);
 
        if (HAS_PCH_CPT(dev)) {
                u32 sel;
 
                temp = I915_READ(PCH_DPLL_SEL);
-               switch (pipe) {
-               default:
-               case 0:
-                       temp |= TRANSA_DPLL_ENABLE;
-                       sel = TRANSA_DPLLB_SEL;
-                       break;
-               case 1:
-                       temp |= TRANSB_DPLL_ENABLE;
-                       sel = TRANSB_DPLLB_SEL;
-                       break;
-               case 2:
-                       temp |= TRANSC_DPLL_ENABLE;
-                       sel = TRANSC_DPLLB_SEL;
-                       break;
-               }
-               if (intel_crtc->pch_pll->pll_reg == _PCH_DPLL_B)
+               temp |= TRANS_DPLL_ENABLE(pipe);
+               sel = TRANS_DPLLB_SEL(pipe);
+               if (intel_crtc->config.shared_dpll == DPLL_ID_PCH_PLL_B)
                        temp |= sel;
                else
                        temp &= ~sel;
@@ -3148,14 +2951,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
 
        /* set transcoder timing, panel must allow it */
        assert_panel_unlocked(dev_priv, pipe);
-       I915_WRITE(TRANS_HTOTAL(pipe), I915_READ(HTOTAL(pipe)));
-       I915_WRITE(TRANS_HBLANK(pipe), I915_READ(HBLANK(pipe)));
-       I915_WRITE(TRANS_HSYNC(pipe),  I915_READ(HSYNC(pipe)));
-
-       I915_WRITE(TRANS_VTOTAL(pipe), I915_READ(VTOTAL(pipe)));
-       I915_WRITE(TRANS_VBLANK(pipe), I915_READ(VBLANK(pipe)));
-       I915_WRITE(TRANS_VSYNC(pipe),  I915_READ(VSYNC(pipe)));
-       I915_WRITE(TRANS_VSYNCSHIFT(pipe),  I915_READ(VSYNCSHIFT(pipe)));
+       ironlake_pch_transcoder_set_timings(intel_crtc, pipe);
 
        intel_fdi_normal_train(crtc);
 
@@ -3205,86 +3001,82 @@ static void lpt_pch_enable(struct drm_crtc *crtc)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
 
-       assert_transcoder_disabled(dev_priv, TRANSCODER_A);
+       assert_pch_transcoder_disabled(dev_priv, TRANSCODER_A);
 
        lpt_program_iclkip(crtc);
 
        /* Set transcoder timing. */
-       I915_WRITE(_TRANS_HTOTAL_A, I915_READ(HTOTAL(cpu_transcoder)));
-       I915_WRITE(_TRANS_HBLANK_A, I915_READ(HBLANK(cpu_transcoder)));
-       I915_WRITE(_TRANS_HSYNC_A,  I915_READ(HSYNC(cpu_transcoder)));
-
-       I915_WRITE(_TRANS_VTOTAL_A, I915_READ(VTOTAL(cpu_transcoder)));
-       I915_WRITE(_TRANS_VBLANK_A, I915_READ(VBLANK(cpu_transcoder)));
-       I915_WRITE(_TRANS_VSYNC_A,  I915_READ(VSYNC(cpu_transcoder)));
-       I915_WRITE(_TRANS_VSYNCSHIFT_A, I915_READ(VSYNCSHIFT(cpu_transcoder)));
+       ironlake_pch_transcoder_set_timings(intel_crtc, PIPE_A);
 
        lpt_enable_pch_transcoder(dev_priv, cpu_transcoder);
 }
 
-static void intel_put_pch_pll(struct intel_crtc *intel_crtc)
+static void intel_put_shared_dpll(struct intel_crtc *crtc)
 {
-       struct intel_pch_pll *pll = intel_crtc->pch_pll;
+       struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
 
        if (pll == NULL)
                return;
 
        if (pll->refcount == 0) {
-               WARN(1, "bad PCH PLL refcount\n");
+               WARN(1, "bad %s refcount\n", pll->name);
                return;
        }
 
-       --pll->refcount;
-       intel_crtc->pch_pll = NULL;
+       if (--pll->refcount == 0) {
+               WARN_ON(pll->on);
+               WARN_ON(pll->active);
+       }
+
+       crtc->config.shared_dpll = DPLL_ID_PRIVATE;
 }
 
-static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u32 dpll, u32 fp)
+static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc, u32 dpll, u32 fp)
 {
-       struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private;
-       struct intel_pch_pll *pll;
-       int i;
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
+       enum intel_dpll_id i;
 
-       pll = intel_crtc->pch_pll;
        if (pll) {
-               DRM_DEBUG_KMS("CRTC:%d reusing existing PCH PLL %x\n",
-                             intel_crtc->base.base.id, pll->pll_reg);
-               goto prepare;
+               DRM_DEBUG_KMS("CRTC:%d dropping existing %s\n",
+                             crtc->base.base.id, pll->name);
+               intel_put_shared_dpll(crtc);
        }
 
        if (HAS_PCH_IBX(dev_priv->dev)) {
                /* Ironlake PCH has a fixed PLL->PCH pipe mapping. */
-               i = intel_crtc->pipe;
-               pll = &dev_priv->pch_plls[i];
+               i = crtc->pipe;
+               pll = &dev_priv->shared_dplls[i];
 
-               DRM_DEBUG_KMS("CRTC:%d using pre-allocated PCH PLL %x\n",
-                             intel_crtc->base.base.id, pll->pll_reg);
+               DRM_DEBUG_KMS("CRTC:%d using pre-allocated %s\n",
+                             crtc->base.base.id, pll->name);
 
                goto found;
        }
 
-       for (i = 0; i < dev_priv->num_pch_pll; i++) {
-               pll = &dev_priv->pch_plls[i];
+       for (i = 0; i < dev_priv->num_shared_dpll; i++) {
+               pll = &dev_priv->shared_dplls[i];
 
                /* Only want to check enabled timings first */
                if (pll->refcount == 0)
                        continue;
 
-               if (dpll == (I915_READ(pll->pll_reg) & 0x7fffffff) &&
-                   fp == I915_READ(pll->fp0_reg)) {
-                       DRM_DEBUG_KMS("CRTC:%d sharing existing PCH PLL %x (refcount %d, ative %d)\n",
-                                     intel_crtc->base.base.id,
-                                     pll->pll_reg, pll->refcount, pll->active);
+               if (dpll == (I915_READ(PCH_DPLL(pll->id)) & 0x7fffffff) &&
+                   fp == I915_READ(PCH_FP0(pll->id))) {
+                       DRM_DEBUG_KMS("CRTC:%d sharing existing %s (refcount %d, ative %d)\n",
+                                     crtc->base.base.id,
+                                     pll->name, pll->refcount, pll->active);
 
                        goto found;
                }
        }
 
        /* Ok no matching timings, maybe there's a free one? */
-       for (i = 0; i < dev_priv->num_pch_pll; i++) {
-               pll = &dev_priv->pch_plls[i];
+       for (i = 0; i < dev_priv->num_shared_dpll; i++) {
+               pll = &dev_priv->shared_dplls[i];
                if (pll->refcount == 0) {
-                       DRM_DEBUG_KMS("CRTC:%d allocated PCH PLL %x\n",
-                                     intel_crtc->base.base.id, pll->pll_reg);
+                       DRM_DEBUG_KMS("CRTC:%d allocated %s\n",
+                                     crtc->base.base.id, pll->name);
                        goto found;
                }
        }
@@ -3292,24 +3084,32 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3
        return NULL;
 
 found:
-       intel_crtc->pch_pll = pll;
-       pll->refcount++;
-       DRM_DEBUG_DRIVER("using pll %d for pipe %d\n", i, intel_crtc->pipe);
-prepare: /* separate function? */
-       DRM_DEBUG_DRIVER("switching PLL %x off\n", pll->pll_reg);
+       crtc->config.shared_dpll = i;
+       DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name,
+                        pipe_name(crtc->pipe));
 
-       /* Wait for the clocks to stabilize before rewriting the regs */
-       I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE);
-       POSTING_READ(pll->pll_reg);
-       udelay(150);
+       if (pll->active == 0) {
+               memcpy(&pll->hw_state, &crtc->config.dpll_hw_state,
+                      sizeof(pll->hw_state));
+
+               DRM_DEBUG_DRIVER("setting up %s\n", pll->name);
+               WARN_ON(pll->on);
+               assert_shared_dpll_disabled(dev_priv, pll);
+
+               /* Wait for the clocks to stabilize before rewriting the regs */
+               I915_WRITE(PCH_DPLL(pll->id), dpll & ~DPLL_VCO_ENABLE);
+               POSTING_READ(PCH_DPLL(pll->id));
+               udelay(150);
+
+               I915_WRITE(PCH_FP0(pll->id), fp);
+               I915_WRITE(PCH_DPLL(pll->id), dpll & ~DPLL_VCO_ENABLE);
+       }
+       pll->refcount++;
 
-       I915_WRITE(pll->fp0_reg, fp);
-       I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE);
-       pll->on = false;
        return pll;
 }
 
-void intel_cpt_verify_modeset(struct drm_device *dev, int pipe)
+static void cpt_verify_modeset(struct drm_device *dev, int pipe)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int dslreg = PIPEDSL(pipe);
@@ -3319,26 +3119,73 @@ void intel_cpt_verify_modeset(struct drm_device *dev, int pipe)
        udelay(500);
        if (wait_for(I915_READ(dslreg) != temp, 5)) {
                if (wait_for(I915_READ(dslreg) != temp, 5))
-                       DRM_ERROR("mode set failed: pipe %d stuck\n", pipe);
+                       DRM_ERROR("mode set failed: pipe %c stuck\n", pipe_name(pipe));
        }
 }
 
-static void ironlake_crtc_enable(struct drm_crtc *crtc)
+static void ironlake_pfit_enable(struct intel_crtc *crtc)
 {
-       struct drm_device *dev = crtc->dev;
+       struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct intel_encoder *encoder;
-       int pipe = intel_crtc->pipe;
-       int plane = intel_crtc->plane;
-       u32 temp;
-
-       WARN_ON(!crtc->enabled);
+       int pipe = crtc->pipe;
 
-       if (intel_crtc->active)
-               return;
+       if (crtc->config.pch_pfit.size) {
+               /* Force use of hard-coded filter coefficients
+                * as some pre-programmed values are broken,
+                * e.g. x201.
+                */
+               if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
+                       I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3 |
+                                                PF_PIPE_SEL_IVB(pipe));
+               else
+                       I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3);
+               I915_WRITE(PF_WIN_POS(pipe), crtc->config.pch_pfit.pos);
+               I915_WRITE(PF_WIN_SZ(pipe), crtc->config.pch_pfit.size);
+       }
+}
+
+static void intel_enable_planes(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       enum pipe pipe = to_intel_crtc(crtc)->pipe;
+       struct intel_plane *intel_plane;
+
+       list_for_each_entry(intel_plane, &dev->mode_config.plane_list, base.head)
+               if (intel_plane->pipe == pipe)
+                       intel_plane_restore(&intel_plane->base);
+}
+
+static void intel_disable_planes(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       enum pipe pipe = to_intel_crtc(crtc)->pipe;
+       struct intel_plane *intel_plane;
+
+       list_for_each_entry(intel_plane, &dev->mode_config.plane_list, base.head)
+               if (intel_plane->pipe == pipe)
+                       intel_plane_disable(&intel_plane->base);
+}
+
+static void ironlake_crtc_enable(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 intel_encoder *encoder;
+       int pipe = intel_crtc->pipe;
+       int plane = intel_crtc->plane;
+       u32 temp;
+
+       WARN_ON(!crtc->enabled);
+
+       if (intel_crtc->active)
+               return;
 
        intel_crtc->active = true;
+
+       intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
+       intel_set_pch_fifo_underrun_reporting(dev, pipe, true);
+
        intel_update_watermarks(dev);
 
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
@@ -3362,22 +3209,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
                if (encoder->pre_enable)
                        encoder->pre_enable(encoder);
 
-       /* Enable panel fitting for LVDS */
-       if (dev_priv->pch_pf_size &&
-           (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) ||
-            intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))) {
-               /* Force use of hard-coded filter coefficients
-                * as some pre-programmed values are broken,
-                * e.g. x201.
-                */
-               if (IS_IVYBRIDGE(dev))
-                       I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3 |
-                                                PF_PIPE_SEL_IVB(pipe));
-               else
-                       I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3);
-               I915_WRITE(PF_WIN_POS(pipe), dev_priv->pch_pf_pos);
-               I915_WRITE(PF_WIN_SZ(pipe), dev_priv->pch_pf_size);
-       }
+       ironlake_pfit_enable(intel_crtc);
 
        /*
         * On ILK+ LUT must be loaded before the pipe is running but with
@@ -3388,6 +3220,8 @@ static void ironlake_crtc_enable(struct drm_crtc *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);
 
        if (intel_crtc->config.has_pch_encoder)
                ironlake_pch_enable(crtc);
@@ -3396,13 +3230,11 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        intel_update_fbc(dev);
        mutex_unlock(&dev->struct_mutex);
 
-       intel_crtc_update_cursor(crtc, true);
-
        for_each_encoder_on_crtc(dev, crtc, encoder)
                encoder->enable(encoder);
 
        if (HAS_PCH_CPT(dev))
-               intel_cpt_verify_modeset(dev, intel_crtc->pipe);
+               cpt_verify_modeset(dev, intel_crtc->pipe);
 
        /*
         * There seems to be a race in PCH platform hw (at least on some
@@ -3415,6 +3247,42 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        intel_wait_for_vblank(dev, intel_crtc->pipe);
 }
 
+/* IPS only exists on ULT machines and is tied to pipe A. */
+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)
+{
+       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);
+       I915_WRITE(IPS_CTL, IPS_ENABLE);
+}
+
+static 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);
+       I915_WRITE(IPS_CTL, 0);
+
+       /* We need to wait for a vblank before we can disable the plane. */
+       intel_wait_for_vblank(dev, crtc->pipe);
+}
+
 static void haswell_crtc_enable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -3430,6 +3298,11 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
                return;
 
        intel_crtc->active = true;
+
+       intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
+       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)
@@ -3441,18 +3314,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
 
        intel_ddi_enable_pipe_clock(intel_crtc);
 
-       /* Enable panel fitting for eDP */
-       if (dev_priv->pch_pf_size &&
-           intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) {
-               /* Force use of hard-coded filter coefficients
-                * as some pre-programmed values are broken,
-                * e.g. x201.
-                */
-               I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3 |
-                                        PF_PIPE_SEL_IVB(pipe));
-               I915_WRITE(PF_WIN_POS(pipe), dev_priv->pch_pf_pos);
-               I915_WRITE(PF_WIN_SZ(pipe), dev_priv->pch_pf_size);
-       }
+       ironlake_pfit_enable(intel_crtc);
 
        /*
         * On ILK+ LUT must be loaded before the pipe is running but with
@@ -3466,6 +3328,10 @@ static void haswell_crtc_enable(struct drm_crtc *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);
 
        if (intel_crtc->config.has_pch_encoder)
                lpt_pch_enable(crtc);
@@ -3474,8 +3340,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        intel_update_fbc(dev);
        mutex_unlock(&dev->struct_mutex);
 
-       intel_crtc_update_cursor(crtc, true);
-
        for_each_encoder_on_crtc(dev, crtc, encoder)
                encoder->enable(encoder);
 
@@ -3490,6 +3354,21 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        intel_wait_for_vblank(dev, intel_crtc->pipe);
 }
 
+static void ironlake_pfit_disable(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe = crtc->pipe;
+
+       /* To avoid upsetting the power well on haswell only disable the pfit if
+        * it's in use. The hw state code will make sure we get this right. */
+       if (crtc->config.pch_pfit.size) {
+               I915_WRITE(PF_CTL(pipe), 0);
+               I915_WRITE(PF_WIN_POS(pipe), 0);
+               I915_WRITE(PF_WIN_SZ(pipe), 0);
+       }
+}
+
 static void ironlake_crtc_disable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -3509,58 +3388,51 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
 
        intel_crtc_wait_for_pending_flips(crtc);
        drm_vblank_off(dev, pipe);
-       intel_crtc_update_cursor(crtc, false);
-
-       intel_disable_plane(dev_priv, plane, pipe);
 
        if (dev_priv->cfb_plane == plane)
                intel_disable_fbc(dev);
 
+       intel_crtc_update_cursor(crtc, false);
+       intel_disable_planes(crtc);
+       intel_disable_plane(dev_priv, plane, pipe);
+
+       if (intel_crtc->config.has_pch_encoder)
+               intel_set_pch_fifo_underrun_reporting(dev, pipe, false);
+
        intel_disable_pipe(dev_priv, pipe);
 
-       /* Disable PF */
-       I915_WRITE(PF_CTL(pipe), 0);
-       I915_WRITE(PF_WIN_SZ(pipe), 0);
+       ironlake_pfit_disable(intel_crtc);
 
        for_each_encoder_on_crtc(dev, crtc, encoder)
                if (encoder->post_disable)
                        encoder->post_disable(encoder);
 
-       ironlake_fdi_disable(crtc);
-
-       ironlake_disable_pch_transcoder(dev_priv, pipe);
+       if (intel_crtc->config.has_pch_encoder) {
+               ironlake_fdi_disable(crtc);
 
-       if (HAS_PCH_CPT(dev)) {
-               /* disable TRANS_DP_CTL */
-               reg = TRANS_DP_CTL(pipe);
-               temp = I915_READ(reg);
-               temp &= ~(TRANS_DP_OUTPUT_ENABLE | TRANS_DP_PORT_SEL_MASK);
-               temp |= TRANS_DP_PORT_SEL_NONE;
-               I915_WRITE(reg, temp);
+               ironlake_disable_pch_transcoder(dev_priv, pipe);
+               intel_set_pch_fifo_underrun_reporting(dev, pipe, true);
 
-               /* disable DPLL_SEL */
-               temp = I915_READ(PCH_DPLL_SEL);
-               switch (pipe) {
-               case 0:
-                       temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL);
-                       break;
-               case 1:
-                       temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
-                       break;
-               case 2:
-                       /* C shares PLL A or B */
-                       temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL);
-                       break;
-               default:
-                       BUG(); /* wtf */
+               if (HAS_PCH_CPT(dev)) {
+                       /* disable TRANS_DP_CTL */
+                       reg = TRANS_DP_CTL(pipe);
+                       temp = I915_READ(reg);
+                       temp &= ~(TRANS_DP_OUTPUT_ENABLE |
+                                 TRANS_DP_PORT_SEL_MASK);
+                       temp |= TRANS_DP_PORT_SEL_NONE;
+                       I915_WRITE(reg, temp);
+
+                       /* disable DPLL_SEL */
+                       temp = I915_READ(PCH_DPLL_SEL);
+                       temp &= ~(TRANS_DPLL_ENABLE(pipe) | TRANS_DPLLB_SEL(pipe));
+                       I915_WRITE(PCH_DPLL_SEL, temp);
                }
-               I915_WRITE(PCH_DPLL_SEL, temp);
-       }
 
-       /* disable PCH DPLL */
-       intel_disable_pch_pll(intel_crtc);
+               /* disable PCH DPLL */
+               intel_disable_shared_dpll(intel_crtc);
 
-       ironlake_fdi_pll_disable(intel_crtc);
+               ironlake_fdi_pll_disable(intel_crtc);
+       }
 
        intel_crtc->active = false;
        intel_update_watermarks(dev);
@@ -3588,24 +3460,24 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
 
        intel_crtc_wait_for_pending_flips(crtc);
        drm_vblank_off(dev, pipe);
-       intel_crtc_update_cursor(crtc, false);
-
-       intel_disable_plane(dev_priv, plane, pipe);
 
+       /* FBC must be disabled before disabling the plane on HSW. */
        if (dev_priv->cfb_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);
+
+       if (intel_crtc->config.has_pch_encoder)
+               intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, false);
        intel_disable_pipe(dev_priv, pipe);
 
        intel_ddi_disable_transcoder_func(dev_priv, cpu_transcoder);
 
-       /* XXX: Once we have proper panel fitter state tracking implemented with
-        * hardware state read/check support we should switch to only disable
-        * the panel fitter when we know it's used. */
-       if (intel_using_power_well(dev)) {
-               I915_WRITE(PF_CTL(pipe), 0);
-               I915_WRITE(PF_WIN_SZ(pipe), 0);
-       }
+       ironlake_pfit_disable(intel_crtc);
 
        intel_ddi_disable_pipe_clock(intel_crtc);
 
@@ -3615,6 +3487,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
 
        if (intel_crtc->config.has_pch_encoder) {
                lpt_disable_pch_transcoder(dev_priv);
+               intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true);
                intel_ddi_fdi_disable(crtc);
        }
 
@@ -3629,17 +3502,11 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
 static void ironlake_crtc_off(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       intel_put_pch_pll(intel_crtc);
+       intel_put_shared_dpll(intel_crtc);
 }
 
 static void haswell_crtc_off(struct drm_crtc *crtc)
 {
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-
-       /* Stop saying we're using TRANSCODER_EDP because some other CRTC might
-        * start using it. */
-       intel_crtc->config.cpu_transcoder = (enum transcoder) intel_crtc->pipe;
-
        intel_ddi_put_crtc_pll(crtc);
 }
 
@@ -3685,6 +3552,77 @@ g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe)
        }
 }
 
+static void i9xx_pfit_enable(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc_config *pipe_config = &crtc->config;
+
+       if (!crtc->config.gmch_pfit.control)
+               return;
+
+       /*
+        * The panel fitter should only be adjusted whilst the pipe is disabled,
+        * according to register description and PRM.
+        */
+       WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE);
+       assert_pipe_disabled(dev_priv, crtc->pipe);
+
+       I915_WRITE(PFIT_PGM_RATIOS, pipe_config->gmch_pfit.pgm_ratios);
+       I915_WRITE(PFIT_CONTROL, pipe_config->gmch_pfit.control);
+
+       /* Border color in case we don't scale up to the full screen. Black by
+        * default, change to something else for debugging. */
+       I915_WRITE(BCLRPAT(crtc->pipe), 0);
+}
+
+static void valleyview_crtc_enable(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 intel_encoder *encoder;
+       int pipe = intel_crtc->pipe;
+       int plane = intel_crtc->plane;
+
+       WARN_ON(!crtc->enabled);
+
+       if (intel_crtc->active)
+               return;
+
+       intel_crtc->active = true;
+       intel_update_watermarks(dev);
+
+       mutex_lock(&dev_priv->dpio_lock);
+
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               if (encoder->pre_pll_enable)
+                       encoder->pre_pll_enable(encoder);
+
+       intel_enable_pll(dev_priv, pipe);
+
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               if (encoder->pre_enable)
+                       encoder->pre_enable(encoder);
+
+       /* VLV wants encoder enabling _before_ the pipe is up. */
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               encoder->enable(encoder);
+
+       i9xx_pfit_enable(intel_crtc);
+
+       intel_crtc_load_lut(crtc);
+
+       intel_enable_pipe(dev_priv, pipe, false);
+       intel_enable_plane(dev_priv, plane, pipe);
+       intel_enable_planes(crtc);
+       intel_crtc_update_cursor(crtc, true);
+
+       intel_update_fbc(dev);
+
+       mutex_unlock(&dev_priv->dpio_lock);
+}
+
 static void i9xx_crtc_enable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -3708,17 +3646,22 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
                if (encoder->pre_enable)
                        encoder->pre_enable(encoder);
 
+       i9xx_pfit_enable(intel_crtc);
+
+       intel_crtc_load_lut(crtc);
+
        intel_enable_pipe(dev_priv, pipe, false);
        intel_enable_plane(dev_priv, plane, pipe);
+       intel_enable_planes(crtc);
+       /* The fixup needs to happen before cursor is enabled */
        if (IS_G4X(dev))
                g4x_fixup_plane(dev_priv, pipe);
-
-       intel_crtc_load_lut(crtc);
-       intel_update_fbc(dev);
+       intel_crtc_update_cursor(crtc, true);
 
        /* Give the overlay scaler a chance to enable if it's on this pipe */
        intel_crtc_dpms_overlay(intel_crtc, true);
-       intel_crtc_update_cursor(crtc, true);
+
+       intel_update_fbc(dev);
 
        for_each_encoder_on_crtc(dev, crtc, encoder)
                encoder->enable(encoder);
@@ -3728,20 +3671,15 @@ static void i9xx_pfit_disable(struct intel_crtc *crtc)
 {
        struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       enum pipe pipe;
-       uint32_t pctl = I915_READ(PFIT_CONTROL);
 
-       assert_pipe_disabled(dev_priv, crtc->pipe);
+       if (!crtc->config.gmch_pfit.control)
+               return;
 
-       if (INTEL_INFO(dev)->gen >= 4)
-               pipe = (pctl & PFIT_PIPE_MASK) >> PFIT_PIPE_SHIFT;
-       else
-               pipe = PIPE_B;
+       assert_pipe_disabled(dev_priv, crtc->pipe);
 
-       if (pipe == crtc->pipe) {
-               DRM_DEBUG_DRIVER("disabling pfit, current: 0x%08x\n", pctl);
-               I915_WRITE(PFIT_CONTROL, 0);
-       }
+       DRM_DEBUG_DRIVER("disabling pfit, current: 0x%08x\n",
+                        I915_READ(PFIT_CONTROL));
+       I915_WRITE(PFIT_CONTROL, 0);
 }
 
 static void i9xx_crtc_disable(struct drm_crtc *crtc)
@@ -3762,17 +3700,23 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
        /* Give the overlay scaler a chance to disable if it's on this pipe */
        intel_crtc_wait_for_pending_flips(crtc);
        drm_vblank_off(dev, pipe);
-       intel_crtc_dpms_overlay(intel_crtc, false);
-       intel_crtc_update_cursor(crtc, false);
 
        if (dev_priv->cfb_plane == plane)
                intel_disable_fbc(dev);
 
+       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_pipe(dev_priv, pipe);
 
        i9xx_pfit_disable(intel_crtc);
 
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               if (encoder->post_disable)
+                       encoder->post_disable(encoder);
+
        intel_disable_pll(dev_priv, pipe);
 
        intel_crtc->active = false;
@@ -3845,8 +3789,8 @@ static void intel_crtc_disable(struct drm_crtc *crtc)
        /* crtc should still be enabled when we disable it. */
        WARN_ON(!crtc->enabled);
 
-       intel_crtc->eld_vld = false;
        dev_priv->display.crtc_disable(crtc);
+       intel_crtc->eld_vld = false;
        intel_crtc_update_sarea(crtc, false);
        dev_priv->display.off(crtc);
 
@@ -3977,17 +3921,131 @@ bool intel_connector_get_hw_state(struct intel_connector *connector)
        return encoder->get_hw_state(encoder, &pipe);
 }
 
-static bool intel_crtc_compute_config(struct drm_crtc *crtc,
-                                     struct intel_crtc_config *pipe_config)
+static bool ironlake_check_fdi_lanes(struct drm_device *dev, enum pipe pipe,
+                                    struct intel_crtc_config *pipe_config)
 {
-       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *pipe_B_crtc =
+               to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_B]);
+
+       DRM_DEBUG_KMS("checking fdi config on pipe %c, lanes %i\n",
+                     pipe_name(pipe), pipe_config->fdi_lanes);
+       if (pipe_config->fdi_lanes > 4) {
+               DRM_DEBUG_KMS("invalid fdi lane config on pipe %c: %i lanes\n",
+                             pipe_name(pipe), pipe_config->fdi_lanes);
+               return false;
+       }
+
+       if (IS_HASWELL(dev)) {
+               if (pipe_config->fdi_lanes > 2) {
+                       DRM_DEBUG_KMS("only 2 lanes on haswell, required: %i lanes\n",
+                                     pipe_config->fdi_lanes);
+                       return false;
+               } else {
+                       return true;
+               }
+       }
+
+       if (INTEL_INFO(dev)->num_pipes == 2)
+               return true;
+
+       /* Ivybridge 3 pipe is really complicated */
+       switch (pipe) {
+       case PIPE_A:
+               return true;
+       case PIPE_B:
+               if (dev_priv->pipe_to_crtc_mapping[PIPE_C]->enabled &&
+                   pipe_config->fdi_lanes > 2) {
+                       DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n",
+                                     pipe_name(pipe), pipe_config->fdi_lanes);
+                       return false;
+               }
+               return true;
+       case PIPE_C:
+               if (!pipe_has_enabled_pch(pipe_B_crtc) ||
+                   pipe_B_crtc->config.fdi_lanes <= 2) {
+                       if (pipe_config->fdi_lanes > 2) {
+                               DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n",
+                                             pipe_name(pipe), pipe_config->fdi_lanes);
+                               return false;
+                       }
+               } else {
+                       DRM_DEBUG_KMS("fdi link B uses too many lanes to enable link C\n");
+                       return false;
+               }
+               return true;
+       default:
+               BUG();
+       }
+}
+
+#define RETRY 1
+static int ironlake_fdi_compute_config(struct intel_crtc *intel_crtc,
+                                      struct intel_crtc_config *pipe_config)
+{
+       struct drm_device *dev = intel_crtc->base.dev;
+       struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
+       int lane, link_bw, fdi_dotclock;
+       bool setup_ok, needs_recompute = false;
+
+retry:
+       /* FDI is a binary signal running at ~2.7GHz, encoding
+        * each output octet as 10 bits. The actual frequency
+        * is stored as a divider into a 100MHz clock, and the
+        * mode pixel clock is stored in units of 1KHz.
+        * Hence the bw of each lane in terms of the mode signal
+        * is:
+        */
+       link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
+
+       fdi_dotclock = adjusted_mode->clock;
+       fdi_dotclock /= pipe_config->pixel_multiplier;
+
+       lane = ironlake_get_lanes_required(fdi_dotclock, link_bw,
+                                          pipe_config->pipe_bpp);
+
+       pipe_config->fdi_lanes = lane;
+
+       intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock,
+                              link_bw, &pipe_config->fdi_m_n);
+
+       setup_ok = ironlake_check_fdi_lanes(intel_crtc->base.dev,
+                                           intel_crtc->pipe, pipe_config);
+       if (!setup_ok && pipe_config->pipe_bpp > 6*3) {
+               pipe_config->pipe_bpp -= 2*3;
+               DRM_DEBUG_KMS("fdi link bw constraint, reducing pipe bpp to %i\n",
+                             pipe_config->pipe_bpp);
+               needs_recompute = true;
+               pipe_config->bw_constrained = true;
+
+               goto retry;
+       }
+
+       if (needs_recompute)
+               return RETRY;
+
+       return setup_ok ? 0 : -EINVAL;
+}
+
+static void hsw_compute_ips_config(struct intel_crtc *crtc,
+                                  struct intel_crtc_config *pipe_config)
+{
+       pipe_config->ips_enabled = i915_enable_ips &&
+                                  hsw_crtc_supports_ips(crtc) &&
+                                  pipe_config->pipe_bpp == 24;
+}
+
+static int intel_crtc_compute_config(struct intel_crtc *crtc,
+                                    struct intel_crtc_config *pipe_config)
+{
+       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)
-                       return false;
+                       return -EINVAL;
        }
 
        /* All interlaced capable intel hw wants timings in frames. Note though
@@ -3996,12 +4054,12 @@ static bool intel_crtc_compute_config(struct drm_crtc *crtc,
        if (!pipe_config->timings_set)
                drm_mode_set_crtcinfo(adjusted_mode, 0);
 
-       /* WaPruneModeWithIncorrectHsyncOffset: Cantiga+ cannot handle modes
-        * with a hsync front porch of 0.
+       /* Cantiga+ cannot handle modes with a hsync front porch of 0.
+        * WaPruneModeWithIncorrectHsyncOffset:ctg,elk,ilk,snb,ivb,vlv,hsw.
         */
        if ((INTEL_INFO(dev)->gen > 4 || IS_G4X(dev)) &&
                adjusted_mode->hsync_start == adjusted_mode->hdisplay)
-               return false;
+               return -EINVAL;
 
        if ((IS_G4X(dev) || IS_VALLEYVIEW(dev)) && pipe_config->pipe_bpp > 10*3) {
                pipe_config->pipe_bpp = 10*3; /* 12bpc is gen5+ */
@@ -4011,7 +4069,18 @@ static bool intel_crtc_compute_config(struct drm_crtc *crtc,
                pipe_config->pipe_bpp = 8*3;
        }
 
-       return true;
+       if (HAS_IPS(dev))
+               hsw_compute_ips_config(crtc, pipe_config);
+
+       /* XXX: PCH clock sharing is done in ->mode_set, so make sure the old
+        * clock survives for now. */
+       if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
+               pipe_config->shared_dpll = crtc->config.shared_dpll;
+
+       if (pipe_config->has_pch_encoder)
+               return ironlake_fdi_compute_config(crtc, pipe_config);
+
+       return 0;
 }
 
 static int valleyview_get_display_clock_speed(struct drm_device *dev)
@@ -4120,7 +4189,7 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
 {
        if (i915_panel_use_ssc >= 0)
                return i915_panel_use_ssc != 0;
-       return dev_priv->lvds_use_ssc
+       return dev_priv->vbt.lvds_use_ssc
                && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
 }
 
@@ -4156,7 +4225,7 @@ static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors)
                refclk = vlv_get_refclk(crtc);
        } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
            intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
-               refclk = dev_priv->lvds_ssc_freq * 1000;
+               refclk = dev_priv->vbt.lvds_ssc_freq * 1000;
                DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
                              refclk / 1000);
        } else if (!IS_GEN2(dev)) {
@@ -4168,28 +4237,14 @@ static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors)
        return refclk;
 }
 
-static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc *crtc)
+static uint32_t pnv_dpll_compute_fp(struct dpll *dpll)
 {
-       unsigned dotclock = crtc->config.adjusted_mode.clock;
-       struct dpll *clock = &crtc->config.dpll;
-
-       /* SDVO TV has fixed PLL values depend on its clock range,
-          this mirrors vbios setting. */
-       if (dotclock >= 100000 && dotclock < 140500) {
-               clock->p1 = 2;
-               clock->p2 = 10;
-               clock->n = 3;
-               clock->m1 = 16;
-               clock->m2 = 8;
-       } else if (dotclock >= 140500 && dotclock <= 200000) {
-               clock->p1 = 1;
-               clock->p2 = 10;
-               clock->n = 6;
-               clock->m1 = 12;
-               clock->m2 = 8;
-       }
+       return (1 << dpll->n) << 16 | dpll->m2;
+}
 
-       crtc->config.clock_set = true;
+static uint32_t i9xx_dpll_compute_fp(struct dpll *dpll)
+{
+       return dpll->n << 16 | dpll->m1 << 8 | dpll->m2;
 }
 
 static void i9xx_update_pll_dividers(struct intel_crtc *crtc,
@@ -4199,18 +4254,15 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc,
        struct drm_i915_private *dev_priv = dev->dev_private;
        int pipe = crtc->pipe;
        u32 fp, fp2 = 0;
-       struct dpll *clock = &crtc->config.dpll;
 
        if (IS_PINEVIEW(dev)) {
-               fp = (1 << clock->n) << 16 | clock->m1 << 8 | clock->m2;
+               fp = pnv_dpll_compute_fp(&crtc->config.dpll);
                if (reduced_clock)
-                       fp2 = (1 << reduced_clock->n) << 16 |
-                               reduced_clock->m1 << 8 | reduced_clock->m2;
+                       fp2 = pnv_dpll_compute_fp(reduced_clock);
        } else {
-               fp = clock->n << 16 | clock->m1 << 8 | clock->m2;
+               fp = i9xx_dpll_compute_fp(&crtc->config.dpll);
                if (reduced_clock)
-                       fp2 = reduced_clock->n << 16 | reduced_clock->m1 << 8 |
-                               reduced_clock->m2;
+                       fp2 = i9xx_dpll_compute_fp(reduced_clock);
        }
 
        I915_WRITE(FP0(pipe), fp);
@@ -4225,6 +4277,68 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc,
        }
 }
 
+static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv)
+{
+       u32 reg_val;
+
+       /*
+        * 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 &= 0xffffff00;
+       reg_val |= 0x00000030;
+       vlv_dpio_write(dev_priv, DPIO_IREF(1), reg_val);
+
+       reg_val = vlv_dpio_read(dev_priv, DPIO_CALIBRATION);
+       reg_val &= 0x8cffffff;
+       reg_val = 0x8c000000;
+       vlv_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val);
+
+       reg_val = vlv_dpio_read(dev_priv, DPIO_IREF(1));
+       reg_val &= 0xffffff00;
+       vlv_dpio_write(dev_priv, DPIO_IREF(1), reg_val);
+
+       reg_val = vlv_dpio_read(dev_priv, DPIO_CALIBRATION);
+       reg_val &= 0x00ffffff;
+       reg_val |= 0xb0000000;
+       vlv_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val);
+}
+
+static void intel_pch_transcoder_set_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;
+       int pipe = crtc->pipe;
+
+       I915_WRITE(PCH_TRANS_DATA_M1(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m);
+       I915_WRITE(PCH_TRANS_DATA_N1(pipe), m_n->gmch_n);
+       I915_WRITE(PCH_TRANS_LINK_M1(pipe), m_n->link_m);
+       I915_WRITE(PCH_TRANS_LINK_N1(pipe), m_n->link_n);
+}
+
+static void intel_cpu_transcoder_set_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;
+       int pipe = crtc->pipe;
+       enum transcoder transcoder = crtc->config.cpu_transcoder;
+
+       if (INTEL_INFO(dev)->gen >= 5) {
+               I915_WRITE(PIPE_DATA_M1(transcoder), TU_SIZE(m_n->tu) | m_n->gmch_m);
+               I915_WRITE(PIPE_DATA_N1(transcoder), m_n->gmch_n);
+               I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m);
+               I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n);
+       } else {
+               I915_WRITE(PIPE_DATA_M_G4X(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m);
+               I915_WRITE(PIPE_DATA_N_G4X(pipe), m_n->gmch_n);
+               I915_WRITE(PIPE_LINK_M_G4X(pipe), m_n->link_m);
+               I915_WRITE(PIPE_LINK_N_G4X(pipe), m_n->link_n);
+       }
+}
+
 static void intel_dp_set_m_n(struct intel_crtc *crtc)
 {
        if (crtc->config.has_pch_encoder)
@@ -4237,24 +4351,16 @@ static void vlv_update_pll(struct intel_crtc *crtc)
 {
        struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_encoder *encoder;
        int pipe = crtc->pipe;
-       u32 dpll, mdiv, pdiv;
+       u32 dpll, mdiv;
        u32 bestn, bestm1, bestm2, bestp1, bestp2;
-       bool is_sdvo;
-       u32 temp;
+       bool is_hdmi;
+       u32 coreclk, reg_val, dpll_md;
 
        mutex_lock(&dev_priv->dpio_lock);
 
-       is_sdvo = intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_SDVO) ||
-               intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI);
-
-       dpll = DPLL_VGA_MODE_DIS;
-       dpll |= DPLL_EXT_BUFFER_ENABLE_VLV;
-       dpll |= DPLL_REFA_CLK_ENABLE_VLV;
-       dpll |= DPLL_INTEGRATED_CLOCK_VLV;
-
-       I915_WRITE(DPLL(pipe), dpll);
-       POSTING_READ(DPLL(pipe));
+       is_hdmi = intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI);
 
        bestn = crtc->config.dpll.n;
        bestm1 = crtc->config.dpll.m1;
@@ -4262,72 +4368,104 @@ static void vlv_update_pll(struct intel_crtc *crtc)
        bestp1 = crtc->config.dpll.p1;
        bestp2 = crtc->config.dpll.p2;
 
-       /*
-        * In Valleyview PLL and program lane counter registers are exposed
-        * through DPIO interface
-        */
+       /* See eDP HDMI DPIO driver vbios notes doc */
+
+       /* PLL B needs special handling */
+       if (pipe)
+               vlv_pllb_recal_opamp(dev_priv);
+
+       /* Set up Tx target for periodic Rcomp update */
+       vlv_dpio_write(dev_priv, DPIO_IREF_BCAST, 0x0100000f);
+
+       /* Disable target IRef on PLL */
+       reg_val = vlv_dpio_read(dev_priv, DPIO_IREF_CTL(pipe));
+       reg_val &= 0x00ffffff;
+       vlv_dpio_write(dev_priv, DPIO_IREF_CTL(pipe), reg_val);
+
+       /* Disable fast lock */
+       vlv_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x610);
+
+       /* Set idtafcrecal before PLL is enabled */
        mdiv = ((bestm1 << DPIO_M1DIV_SHIFT) | (bestm2 & DPIO_M2DIV_MASK));
        mdiv |= ((bestp1 << DPIO_P1_SHIFT) | (bestp2 << DPIO_P2_SHIFT));
        mdiv |= ((bestn << DPIO_N_SHIFT));
-       mdiv |= (1 << DPIO_POST_DIV_SHIFT);
        mdiv |= (1 << DPIO_K_SHIFT);
+
+       /*
+        * Post divider depends on pixel clock rate, DAC vs digital (and LVDS,
+        * but we don't support that).
+        * 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);
+
        mdiv |= DPIO_ENABLE_CALIBRATION;
-       intel_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv);
+       vlv_dpio_write(dev_priv, 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),
+                                0x005f0021);
+       else
+               vlv_dpio_write(dev_priv, 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),
+                                        0x0df40000);
+               else
+                       vlv_dpio_write(dev_priv, DPIO_REFSFR(pipe),
+                                        0x0df70000);
+       } else { /* HDMI or VGA */
+               /* Use bend source */
+               if (!pipe)
+                       vlv_dpio_write(dev_priv, DPIO_REFSFR(pipe),
+                                        0x0df70000);
+               else
+                       vlv_dpio_write(dev_priv, DPIO_REFSFR(pipe),
+                                        0x0df40000);
+       }
 
-       intel_dpio_write(dev_priv, DPIO_CORE_CLK(pipe), 0x01000000);
+       coreclk = vlv_dpio_read(dev_priv, 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);
 
-       pdiv = (1 << DPIO_REFSEL_OVERRIDE) | (5 << DPIO_PLL_MODESEL_SHIFT) |
-               (3 << DPIO_BIAS_CURRENT_CTL_SHIFT) | (1<<20) |
-               (7 << DPIO_PLL_REFCLK_SEL_SHIFT) | (8 << DPIO_DRIVER_CTL_SHIFT) |
-               (5 << DPIO_CLK_BIAS_CTL_SHIFT);
-       intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), pdiv);
+       vlv_dpio_write(dev_priv, DPIO_PLL_CML(pipe), 0x87871000);
 
-       intel_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), 0x005f003b);
+       for_each_encoder_on_crtc(dev, &crtc->base, encoder)
+               if (encoder->pre_pll_enable)
+                       encoder->pre_pll_enable(encoder);
+
+       /* 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)
+               dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
 
        dpll |= DPLL_VCO_ENABLE;
        I915_WRITE(DPLL(pipe), dpll);
        POSTING_READ(DPLL(pipe));
+       udelay(150);
+
        if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1))
                DRM_ERROR("DPLL %d failed to lock\n", pipe);
 
-       intel_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x620);
+       dpll_md = (crtc->config.pixel_multiplier - 1)
+               << DPLL_MD_UDI_MULTIPLIER_SHIFT;
+       I915_WRITE(DPLL_MD(pipe), dpll_md);
+       POSTING_READ(DPLL_MD(pipe));
 
        if (crtc->config.has_dp_encoder)
                intel_dp_set_m_n(crtc);
 
-       I915_WRITE(DPLL(pipe), dpll);
-
-       /* Wait for the clocks to stabilize. */
-       POSTING_READ(DPLL(pipe));
-       udelay(150);
-
-       temp = 0;
-       if (is_sdvo) {
-               temp = 0;
-               if (crtc->config.pixel_multiplier > 1) {
-                       temp = (crtc->config.pixel_multiplier - 1)
-                               << DPLL_MD_UDI_MULTIPLIER_SHIFT;
-               }
-       }
-       I915_WRITE(DPLL_MD(pipe), temp);
-       POSTING_READ(DPLL_MD(pipe));
-
-       /* Now program lane control registers */
-       if(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)
-          || intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) {
-               temp = 0x1000C4;
-               if(pipe == 1)
-                       temp |= (1 << 21);
-               intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL1, temp);
-       }
-
-       if(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP)) {
-               temp = 0x1000C4;
-               if(pipe == 1)
-                       temp |= (1 << 21);
-               intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL2, temp);
-       }
-
        mutex_unlock(&dev_priv->dpio_lock);
 }
 
@@ -4355,14 +4493,14 @@ static void i9xx_update_pll(struct intel_crtc *crtc,
        else
                dpll |= DPLLB_MODE_DAC_SERIAL;
 
-       if (is_sdvo) {
-               if ((crtc->config.pixel_multiplier > 1) &&
-                   (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))) {
-                       dpll |= (crtc->config.pixel_multiplier - 1)
-                               << SDVO_MULTIPLIER_SHIFT_HIRES;
-               }
-               dpll |= DPLL_DVO_HIGH_SPEED;
+       if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) {
+               dpll |= (crtc->config.pixel_multiplier - 1)
+                       << SDVO_MULTIPLIER_SHIFT_HIRES;
        }
+
+       if (is_sdvo)
+               dpll |= DPLL_DVO_HIGH_SPEED;
+
        if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT))
                dpll |= DPLL_DVO_HIGH_SPEED;
 
@@ -4391,12 +4529,8 @@ static void i9xx_update_pll(struct intel_crtc *crtc,
        if (INTEL_INFO(dev)->gen >= 4)
                dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
 
-       if (is_sdvo && intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_TVOUT))
+       if (crtc->config.sdvo_tv_clock)
                dpll |= PLL_REF_INPUT_TVCLKINBC;
-       else if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_TVOUT))
-               /* XXX: just matching BIOS for now */
-               /*      dpll |= PLL_REF_INPUT_TVCLKINBC; */
-               dpll |= 3;
        else if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) &&
                 intel_panel_use_ssc(dev_priv) && num_connectors < 2)
                dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
@@ -4422,15 +4556,9 @@ static void i9xx_update_pll(struct intel_crtc *crtc,
        udelay(150);
 
        if (INTEL_INFO(dev)->gen >= 4) {
-               u32 temp = 0;
-               if (is_sdvo) {
-                       temp = 0;
-                       if (crtc->config.pixel_multiplier > 1) {
-                               temp = (crtc->config.pixel_multiplier - 1)
-                                       << DPLL_MD_UDI_MULTIPLIER_SHIFT;
-                       }
-               }
-               I915_WRITE(DPLL_MD(pipe), temp);
+               u32 dpll_md = (crtc->config.pixel_multiplier - 1)
+                       << DPLL_MD_UDI_MULTIPLIER_SHIFT;
+               I915_WRITE(DPLL_MD(pipe), dpll_md);
        } else {
                /* The pixel multiplier can only be updated once the
                 * DPLL is enabled and the clocks are stable.
@@ -4442,7 +4570,6 @@ static void i9xx_update_pll(struct intel_crtc *crtc,
 }
 
 static void i8xx_update_pll(struct intel_crtc *crtc,
-                           struct drm_display_mode *adjusted_mode,
                            intel_clock_t *reduced_clock,
                            int num_connectors)
 {
@@ -4497,20 +4624,26 @@ static void i8xx_update_pll(struct intel_crtc *crtc,
        I915_WRITE(DPLL(pipe), dpll);
 }
 
-static void intel_set_pipe_timings(struct intel_crtc *intel_crtc,
-                                  struct drm_display_mode *mode,
-                                  struct drm_display_mode *adjusted_mode)
+static void intel_set_pipe_timings(struct intel_crtc *intel_crtc)
 {
        struct drm_device *dev = intel_crtc->base.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;
-       uint32_t vsyncshift;
+       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
+        * the hw state checker will get angry at the mismatch. */
+       crtc_vtotal = adjusted_mode->crtc_vtotal;
+       crtc_vblank_end = adjusted_mode->crtc_vblank_end;
 
        if (!IS_GEN2(dev) && adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
                /* the chip adds 2 halflines automatically */
-               adjusted_mode->crtc_vtotal -= 1;
-               adjusted_mode->crtc_vblank_end -= 1;
+               crtc_vtotal -= 1;
+               crtc_vblank_end -= 1;
                vsyncshift = adjusted_mode->crtc_hsync_start
                             - adjusted_mode->crtc_htotal / 2;
        } else {
@@ -4532,10 +4665,10 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc,
 
        I915_WRITE(VTOTAL(cpu_transcoder),
                   (adjusted_mode->crtc_vdisplay - 1) |
-                  ((adjusted_mode->crtc_vtotal - 1) << 16));
+                  ((crtc_vtotal - 1) << 16));
        I915_WRITE(VBLANK(cpu_transcoder),
                   (adjusted_mode->crtc_vblank_start - 1) |
-                  ((adjusted_mode->crtc_vblank_end - 1) << 16));
+                  ((crtc_vblank_end - 1) << 16));
        I915_WRITE(VSYNC(cpu_transcoder),
                   (adjusted_mode->crtc_vsync_start - 1) |
                   ((adjusted_mode->crtc_vsync_end - 1) << 16));
@@ -4548,11 +4681,50 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc,
            (pipe == PIPE_B || pipe == PIPE_C))
                I915_WRITE(VTOTAL(pipe), I915_READ(VTOTAL(cpu_transcoder)));
 
-       /* pipesrc controls the size that is scaled from, which should
-        * always be the user's requested size.
-        */
-       I915_WRITE(PIPESRC(pipe),
-                  ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+       /* pipesrc controls the size that is scaled from, which should
+        * always be the user's requested size.
+        */
+       I915_WRITE(PIPESRC(pipe),
+                  ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+}
+
+static void intel_get_pipe_timings(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;
+       enum transcoder cpu_transcoder = pipe_config->cpu_transcoder;
+       uint32_t tmp;
+
+       tmp = I915_READ(HTOTAL(cpu_transcoder));
+       pipe_config->adjusted_mode.crtc_hdisplay = (tmp & 0xffff) + 1;
+       pipe_config->adjusted_mode.crtc_htotal = ((tmp >> 16) & 0xffff) + 1;
+       tmp = I915_READ(HBLANK(cpu_transcoder));
+       pipe_config->adjusted_mode.crtc_hblank_start = (tmp & 0xffff) + 1;
+       pipe_config->adjusted_mode.crtc_hblank_end = ((tmp >> 16) & 0xffff) + 1;
+       tmp = I915_READ(HSYNC(cpu_transcoder));
+       pipe_config->adjusted_mode.crtc_hsync_start = (tmp & 0xffff) + 1;
+       pipe_config->adjusted_mode.crtc_hsync_end = ((tmp >> 16) & 0xffff) + 1;
+
+       tmp = I915_READ(VTOTAL(cpu_transcoder));
+       pipe_config->adjusted_mode.crtc_vdisplay = (tmp & 0xffff) + 1;
+       pipe_config->adjusted_mode.crtc_vtotal = ((tmp >> 16) & 0xffff) + 1;
+       tmp = I915_READ(VBLANK(cpu_transcoder));
+       pipe_config->adjusted_mode.crtc_vblank_start = (tmp & 0xffff) + 1;
+       pipe_config->adjusted_mode.crtc_vblank_end = ((tmp >> 16) & 0xffff) + 1;
+       tmp = I915_READ(VSYNC(cpu_transcoder));
+       pipe_config->adjusted_mode.crtc_vsync_start = (tmp & 0xffff) + 1;
+       pipe_config->adjusted_mode.crtc_vsync_end = ((tmp >> 16) & 0xffff) + 1;
+
+       if (I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_INTERLACE_MASK) {
+               pipe_config->adjusted_mode.flags |= DRM_MODE_FLAG_INTERLACE;
+               pipe_config->adjusted_mode.crtc_vtotal += 1;
+               pipe_config->adjusted_mode.crtc_vblank_end += 1;
+       }
+
+       tmp = I915_READ(PIPESRC(crtc->pipe));
+       pipe_config->requested_mode.vdisplay = (tmp & 0xffff) + 1;
+       pipe_config->requested_mode.hdisplay = ((tmp >> 16) & 0xffff) + 1;
 }
 
 static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc)
@@ -4561,7 +4733,7 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc)
        struct drm_i915_private *dev_priv = dev->dev_private;
        uint32_t pipeconf;
 
-       pipeconf = I915_READ(PIPECONF(intel_crtc->pipe));
+       pipeconf = 0;
 
        if (intel_crtc->pipe == 0 && INTEL_INFO(dev)->gen < 4) {
                /* Enable pixel doubling when the dot clock is > 90% of the (display)
@@ -4573,26 +4745,28 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc)
                if (intel_crtc->config.requested_mode.clock >
                    dev_priv->display.get_display_clock_speed(dev) * 9 / 10)
                        pipeconf |= PIPECONF_DOUBLE_WIDE;
-               else
-                       pipeconf &= ~PIPECONF_DOUBLE_WIDE;
        }
 
-       /* default to 8bpc */
-       pipeconf &= ~(PIPECONF_BPC_MASK | PIPECONF_DITHER_EN);
-       if (intel_crtc->config.has_dp_encoder) {
-               if (intel_crtc->config.dither) {
-                       pipeconf |= PIPECONF_6BPC |
-                                   PIPECONF_DITHER_EN |
+       /* only g4x and later have fancy bpc/dither controls */
+       if (IS_G4X(dev) || IS_VALLEYVIEW(dev)) {
+               /* Bspec claims that we can't use dithering for 30bpp pipes. */
+               if (intel_crtc->config.dither && intel_crtc->config.pipe_bpp != 30)
+                       pipeconf |= PIPECONF_DITHER_EN |
                                    PIPECONF_DITHER_TYPE_SP;
-               }
-       }
 
-       if (IS_VALLEYVIEW(dev) && intel_pipe_has_type(&intel_crtc->base,
-                                                     INTEL_OUTPUT_EDP)) {
-               if (intel_crtc->config.dither) {
-                       pipeconf |= PIPECONF_6BPC |
-                                       PIPECONF_ENABLE |
-                                       I965_PIPECONF_ACTIVE;
+               switch (intel_crtc->config.pipe_bpp) {
+               case 18:
+                       pipeconf |= PIPECONF_6BPC;
+                       break;
+               case 24:
+                       pipeconf |= PIPECONF_8BPC;
+                       break;
+               case 30:
+                       pipeconf |= PIPECONF_10BPC;
+                       break;
+               default:
+                       /* Case prevented by intel_choose_pipe_bpp_dither. */
+                       BUG();
                }
        }
 
@@ -4602,23 +4776,17 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc)
                        pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
                } else {
                        DRM_DEBUG_KMS("disabling CxSR downclocking\n");
-                       pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
                }
        }
 
-       pipeconf &= ~PIPECONF_INTERLACE_MASK;
        if (!IS_GEN2(dev) &&
            intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE)
                pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION;
        else
                pipeconf |= PIPECONF_PROGRESSIVE;
 
-       if (IS_VALLEYVIEW(dev)) {
-               if (intel_crtc->config.limited_color_range)
-                       pipeconf |= PIPECONF_COLOR_RANGE_SELECT;
-               else
-                       pipeconf &= ~PIPECONF_COLOR_RANGE_SELECT;
-       }
+       if (IS_VALLEYVIEW(dev) && intel_crtc->config.limited_color_range)
+               pipeconf |= PIPECONF_COLOR_RANGE_SELECT;
 
        I915_WRITE(PIPECONF(intel_crtc->pipe), pipeconf);
        POSTING_READ(PIPECONF(intel_crtc->pipe));
@@ -4631,16 +4799,14 @@ 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 *adjusted_mode =
-               &intel_crtc->config.adjusted_mode;
        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, is_sdvo = false;
-       bool is_lvds = false, is_tv = false;
+       bool ok, has_reduced_clock = false;
+       bool is_lvds = false;
        struct intel_encoder *encoder;
        const intel_limit_t *limit;
        int ret;
@@ -4650,15 +4816,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
                case INTEL_OUTPUT_LVDS:
                        is_lvds = true;
                        break;
-               case INTEL_OUTPUT_SDVO:
-               case INTEL_OUTPUT_HDMI:
-                       is_sdvo = true;
-                       if (encoder->needs_tv_clock)
-                               is_tv = true;
-                       break;
-               case INTEL_OUTPUT_TVOUT:
-                       is_tv = true;
-                       break;
                }
 
                num_connectors++;
@@ -4672,9 +4829,10 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
         * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
         */
        limit = intel_limit(crtc, refclk);
-       ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL,
-                            &clock);
-       if (!ok) {
+       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;
        }
@@ -4689,10 +4847,10 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
                 * by using the FP0/FP1. In such case we will disable the LVDS
                 * downclock feature.
                */
-               has_reduced_clock = limit->find_pll(limit, crtc,
+               has_reduced_clock =
+                       dev_priv->display.find_dpll(limit, crtc,
                                                    dev_priv->lvds_downclock,
-                                                   refclk,
-                                                   &clock,
+                                                   refclk, &clock,
                                                    &reduced_clock);
        }
        /* Compat-code for transition, will disappear. */
@@ -4704,11 +4862,8 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
                intel_crtc->config.dpll.p2 = clock.p2;
        }
 
-       if (is_sdvo && is_tv)
-               i9xx_adjust_sdvo_tv_clock(intel_crtc);
-
        if (IS_GEN2(dev))
-               i8xx_update_pll(intel_crtc, adjusted_mode,
+               i8xx_update_pll(intel_crtc,
                                has_reduced_clock ? &reduced_clock : NULL,
                                num_connectors);
        else if (IS_VALLEYVIEW(dev))
@@ -4716,7 +4871,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
        else
                i9xx_update_pll(intel_crtc,
                                has_reduced_clock ? &reduced_clock : NULL,
-                               num_connectors);
+                                num_connectors);
 
        /* Set up the display plane register */
        dspcntr = DISPPLANE_GAMMA_ENABLE;
@@ -4728,10 +4883,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
                        dspcntr |= DISPPLANE_SEL_PIPE_B;
        }
 
-       DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
-       drm_mode_debug_printmodeline(mode);
-
-       intel_set_pipe_timings(intel_crtc, mode, adjusted_mode);
+       intel_set_pipe_timings(intel_crtc);
 
        /* pipesrc and dspsize control the size that is scaled from,
         * which should always be the user's requested size.
@@ -4743,10 +4895,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
 
        i9xx_set_pipeconf(intel_crtc);
 
-       intel_enable_pipe(dev_priv, pipe, false);
-
-       intel_wait_for_vblank(dev, pipe);
-
        I915_WRITE(DSPCNTR(plane), dspcntr);
        POSTING_READ(DSPCNTR(plane));
 
@@ -4757,6 +4905,36 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
        return ret;
 }
 
+static void i9xx_get_pfit_config(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;
+       uint32_t tmp;
+
+       tmp = I915_READ(PFIT_CONTROL);
+
+       if (INTEL_INFO(dev)->gen < 4) {
+               if (crtc->pipe != PIPE_B)
+                       return;
+
+               /* gen2/3 store dither state in pfit control, needs to match */
+               pipe_config->gmch_pfit.control = tmp & PANEL_8TO6_DITHER_ENABLE;
+       } else {
+               if ((tmp & PFIT_PIPE_MASK) != (crtc->pipe << PFIT_PIPE_SHIFT))
+                       return;
+       }
+
+       if (!(tmp & PFIT_ENABLE))
+               return;
+
+       pipe_config->gmch_pfit.control = I915_READ(PFIT_CONTROL);
+       pipe_config->gmch_pfit.pgm_ratios = I915_READ(PFIT_PGM_RATIOS);
+       if (INTEL_INFO(dev)->gen < 5)
+               pipe_config->gmch_pfit.lvds_border_bits =
+                       I915_READ(LVDS) & LVDS_BORDER_ENABLE;
+}
+
 static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
                                 struct intel_crtc_config *pipe_config)
 {
@@ -4764,10 +4942,34 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
        struct drm_i915_private *dev_priv = dev->dev_private;
        uint32_t tmp;
 
+       pipe_config->cpu_transcoder = crtc->pipe;
+       pipe_config->shared_dpll = DPLL_ID_PRIVATE;
+
        tmp = I915_READ(PIPECONF(crtc->pipe));
        if (!(tmp & PIPECONF_ENABLE))
                return false;
 
+       intel_get_pipe_timings(crtc, pipe_config);
+
+       i9xx_get_pfit_config(crtc, pipe_config);
+
+       if (INTEL_INFO(dev)->gen >= 4) {
+               tmp = I915_READ(DPLL_MD(crtc->pipe));
+               pipe_config->pixel_multiplier =
+                       ((tmp & DPLL_MD_UDI_MULTIPLIER_MASK)
+                        >> DPLL_MD_UDI_MULTIPLIER_SHIFT) + 1;
+       } else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) {
+               tmp = I915_READ(DPLL(crtc->pipe));
+               pipe_config->pixel_multiplier =
+                       ((tmp & SDVO_MULTIPLIER_MASK)
+                        >> SDVO_MULTIPLIER_SHIFT_HIRES) + 1;
+       } else {
+               /* Note that on i915G/GM the pixel multiplier is in the sdvo
+                * port and will be fixed up in the encoder->get_config
+                * function. */
+               pipe_config->pixel_multiplier = 1;
+       }
+
        return true;
 }
 
@@ -4779,7 +4981,6 @@ static void ironlake_init_pch_refclk(struct drm_device *dev)
        u32 val, final;
        bool has_lvds = false;
        bool has_cpu_edp = false;
-       bool has_pch_edp = false;
        bool has_panel = false;
        bool has_ck505 = false;
        bool can_ssc = false;
@@ -4794,25 +4995,22 @@ static void ironlake_init_pch_refclk(struct drm_device *dev)
                        break;
                case INTEL_OUTPUT_EDP:
                        has_panel = true;
-                       if (intel_encoder_is_pch_edp(&encoder->base))
-                               has_pch_edp = true;
-                       else
+                       if (enc_to_dig_port(&encoder->base)->port == PORT_A)
                                has_cpu_edp = true;
                        break;
                }
        }
 
        if (HAS_PCH_IBX(dev)) {
-               has_ck505 = dev_priv->display_clock_mode;
+               has_ck505 = dev_priv->vbt.display_clock_mode;
                can_ssc = has_ck505;
        } else {
                has_ck505 = false;
                can_ssc = true;
        }
 
-       DRM_DEBUG_KMS("has_panel %d has_lvds %d has_pch_edp %d has_cpu_edp %d has_ck505 %d\n",
-                     has_panel, has_lvds, has_pch_edp, has_cpu_edp,
-                     has_ck505);
+       DRM_DEBUG_KMS("has_panel %d has_lvds %d has_ck505 %d\n",
+                     has_panel, has_lvds, has_ck505);
 
        /* Ironlake: try to setup display ref clock before DPLL
         * enabling. This is only under driver's control after
@@ -5102,7 +5300,6 @@ static int ironlake_get_refclk(struct drm_crtc *crtc)
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_encoder *encoder;
-       struct intel_encoder *edp_encoder = NULL;
        int num_connectors = 0;
        bool is_lvds = false;
 
@@ -5111,34 +5308,28 @@ static int ironlake_get_refclk(struct drm_crtc *crtc)
                case INTEL_OUTPUT_LVDS:
                        is_lvds = true;
                        break;
-               case INTEL_OUTPUT_EDP:
-                       edp_encoder = encoder;
-                       break;
                }
                num_connectors++;
        }
 
        if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
                DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
-                             dev_priv->lvds_ssc_freq);
-               return dev_priv->lvds_ssc_freq * 1000;
+                             dev_priv->vbt.lvds_ssc_freq);
+               return dev_priv->vbt.lvds_ssc_freq * 1000;
        }
 
        return 120000;
 }
 
-static void ironlake_set_pipeconf(struct drm_crtc *crtc,
-                                 struct drm_display_mode *adjusted_mode,
-                                 bool dither)
+static void ironlake_set_pipeconf(struct drm_crtc *crtc)
 {
        struct drm_i915_private *dev_priv = crtc->dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
        uint32_t val;
 
-       val = I915_READ(PIPECONF(pipe));
+       val = 0;
 
-       val &= ~PIPECONF_BPC_MASK;
        switch (intel_crtc->config.pipe_bpp) {
        case 18:
                val |= PIPECONF_6BPC;
@@ -5157,20 +5348,16 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc,
                BUG();
        }
 
-       val &= ~(PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_MASK);
-       if (dither)
+       if (intel_crtc->config.dither)
                val |= (PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP);
 
-       val &= ~PIPECONF_INTERLACE_MASK;
-       if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
+       if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE)
                val |= PIPECONF_INTERLACED_ILK;
        else
                val |= PIPECONF_PROGRESSIVE;
 
        if (intel_crtc->config.limited_color_range)
                val |= PIPECONF_COLOR_RANGE_SELECT;
-       else
-               val &= ~PIPECONF_COLOR_RANGE_SELECT;
 
        I915_WRITE(PIPECONF(pipe), val);
        POSTING_READ(PIPECONF(pipe));
@@ -5240,33 +5427,31 @@ static void intel_set_pipe_csc(struct drm_crtc *crtc)
        }
 }
 
-static void haswell_set_pipeconf(struct drm_crtc *crtc,
-                                struct drm_display_mode *adjusted_mode,
-                                bool dither)
+static void haswell_set_pipeconf(struct drm_crtc *crtc)
 {
        struct drm_i915_private *dev_priv = crtc->dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
        uint32_t val;
 
-       val = I915_READ(PIPECONF(cpu_transcoder));
+       val = 0;
 
-       val &= ~(PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_MASK);
-       if (dither)
+       if (intel_crtc->config.dither)
                val |= (PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP);
 
-       val &= ~PIPECONF_INTERLACE_MASK_HSW;
-       if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
+       if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE)
                val |= PIPECONF_INTERLACED_ILK;
        else
                val |= PIPECONF_PROGRESSIVE;
 
        I915_WRITE(PIPECONF(cpu_transcoder), val);
        POSTING_READ(PIPECONF(cpu_transcoder));
+
+       I915_WRITE(GAMMA_MODE(intel_crtc->pipe), GAMMA_MODE_MODE_8BIT);
+       POSTING_READ(GAMMA_MODE(intel_crtc->pipe));
 }
 
 static bool ironlake_compute_clocks(struct drm_crtc *crtc,
-                                   struct drm_display_mode *adjusted_mode,
                                    intel_clock_t *clock,
                                    bool *has_reduced_clock,
                                    intel_clock_t *reduced_clock)
@@ -5276,22 +5461,13 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc,
        struct intel_encoder *intel_encoder;
        int refclk;
        const intel_limit_t *limit;
-       bool ret, is_sdvo = false, is_tv = false, is_lvds = false;
+       bool ret, is_lvds = false;
 
        for_each_encoder_on_crtc(dev, crtc, intel_encoder) {
                switch (intel_encoder->type) {
                case INTEL_OUTPUT_LVDS:
                        is_lvds = true;
                        break;
-               case INTEL_OUTPUT_SDVO:
-               case INTEL_OUTPUT_HDMI:
-                       is_sdvo = true;
-                       if (intel_encoder->needs_tv_clock)
-                               is_tv = true;
-                       break;
-               case INTEL_OUTPUT_TVOUT:
-                       is_tv = true;
-                       break;
                }
        }
 
@@ -5303,8 +5479,9 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc,
         * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
         */
        limit = intel_limit(crtc, refclk);
-       ret = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL,
-                             clock);
+       ret = dev_priv->display.find_dpll(limit, crtc,
+                                         to_intel_crtc(crtc)->config.port_clock,
+                                         refclk, NULL, clock);
        if (!ret)
                return false;
 
@@ -5315,16 +5492,13 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc,
                 * by using the FP0/FP1. In such case we will disable the LVDS
                 * downclock feature.
                */
-               *has_reduced_clock = limit->find_pll(limit, crtc,
-                                                    dev_priv->lvds_downclock,
-                                                    refclk,
-                                                    clock,
-                                                    reduced_clock);
+               *has_reduced_clock =
+                       dev_priv->display.find_dpll(limit, crtc,
+                                                   dev_priv->lvds_downclock,
+                                                   refclk, clock,
+                                                   reduced_clock);
        }
 
-       if (is_sdvo && is_tv)
-               i9xx_adjust_sdvo_tv_clock(to_intel_crtc(crtc));
-
        return true;
 }
 
@@ -5346,65 +5520,25 @@ static void cpt_enable_fdi_bc_bifurcation(struct drm_device *dev)
        POSTING_READ(SOUTH_CHICKEN1);
 }
 
-static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc)
+static void ivybridge_update_fdi_bc_bifurcation(struct intel_crtc *intel_crtc)
 {
        struct drm_device *dev = intel_crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *pipe_B_crtc =
-               to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_B]);
-
-       DRM_DEBUG_KMS("checking fdi config on pipe %i, lanes %i\n",
-                     intel_crtc->pipe, intel_crtc->fdi_lanes);
-       if (intel_crtc->fdi_lanes > 4) {
-               DRM_DEBUG_KMS("invalid fdi lane config on pipe %i: %i lanes\n",
-                             intel_crtc->pipe, intel_crtc->fdi_lanes);
-               /* Clamp lanes to avoid programming the hw with bogus values. */
-               intel_crtc->fdi_lanes = 4;
-
-               return false;
-       }
-
-       if (INTEL_INFO(dev)->num_pipes == 2)
-               return true;
 
        switch (intel_crtc->pipe) {
        case PIPE_A:
-               return true;
+               break;
        case PIPE_B:
-               if (dev_priv->pipe_to_crtc_mapping[PIPE_C]->enabled &&
-                   intel_crtc->fdi_lanes > 2) {
-                       DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %i: %i lanes\n",
-                                     intel_crtc->pipe, intel_crtc->fdi_lanes);
-                       /* Clamp lanes to avoid programming the hw with bogus values. */
-                       intel_crtc->fdi_lanes = 2;
-
-                       return false;
-               }
-
-               if (intel_crtc->fdi_lanes > 2)
+               if (intel_crtc->config.fdi_lanes > 2)
                        WARN_ON(I915_READ(SOUTH_CHICKEN1) & FDI_BC_BIFURCATION_SELECT);
                else
                        cpt_enable_fdi_bc_bifurcation(dev);
 
-               return true;
+               break;
        case PIPE_C:
-               if (!pipe_B_crtc->base.enabled || pipe_B_crtc->fdi_lanes <= 2) {
-                       if (intel_crtc->fdi_lanes > 2) {
-                               DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %i: %i lanes\n",
-                                             intel_crtc->pipe, intel_crtc->fdi_lanes);
-                               /* Clamp lanes to avoid programming the hw with bogus values. */
-                               intel_crtc->fdi_lanes = 2;
-
-                               return false;
-                       }
-               } else {
-                       DRM_DEBUG_KMS("fdi link B uses too many lanes to enable link C\n");
-                       return false;
-               }
-
                cpt_enable_fdi_bc_bifurcation(dev);
 
-               return true;
+               break;
        default:
                BUG();
        }
@@ -5421,78 +5555,13 @@ int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp)
        return bps / (link_bw * 8) + 1;
 }
 
-void intel_pch_transcoder_set_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;
-       int pipe = crtc->pipe;
-
-       I915_WRITE(TRANSDATA_M1(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m);
-       I915_WRITE(TRANSDATA_N1(pipe), m_n->gmch_n);
-       I915_WRITE(TRANSDPLINK_M1(pipe), m_n->link_m);
-       I915_WRITE(TRANSDPLINK_N1(pipe), m_n->link_n);
-}
-
-void intel_cpu_transcoder_set_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;
-       int pipe = crtc->pipe;
-       enum transcoder transcoder = crtc->config.cpu_transcoder;
-
-       if (INTEL_INFO(dev)->gen >= 5) {
-               I915_WRITE(PIPE_DATA_M1(transcoder), TU_SIZE(m_n->tu) | m_n->gmch_m);
-               I915_WRITE(PIPE_DATA_N1(transcoder), m_n->gmch_n);
-               I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m);
-               I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n);
-       } else {
-               I915_WRITE(PIPE_GMCH_DATA_M(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m);
-               I915_WRITE(PIPE_GMCH_DATA_N(pipe), m_n->gmch_n);
-               I915_WRITE(PIPE_DP_LINK_M(pipe), m_n->link_m);
-               I915_WRITE(PIPE_DP_LINK_N(pipe), m_n->link_n);
-       }
-}
-
-static void ironlake_fdi_set_m_n(struct drm_crtc *crtc)
+static bool ironlake_needs_fb_cb_tune(struct dpll *dpll, int factor)
 {
-       struct drm_device *dev = crtc->dev;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct drm_display_mode *adjusted_mode =
-               &intel_crtc->config.adjusted_mode;
-       struct intel_link_m_n m_n = {0};
-       int target_clock, lane, link_bw;
-
-       /* FDI is a binary signal running at ~2.7GHz, encoding
-        * each output octet as 10 bits. The actual frequency
-        * is stored as a divider into a 100MHz clock, and the
-        * mode pixel clock is stored in units of 1KHz.
-        * Hence the bw of each lane in terms of the mode signal
-        * is:
-        */
-       link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
-
-       if (intel_crtc->config.pixel_target_clock)
-               target_clock = intel_crtc->config.pixel_target_clock;
-       else
-               target_clock = adjusted_mode->clock;
-
-       lane = ironlake_get_lanes_required(target_clock, link_bw,
-                                          intel_crtc->config.pipe_bpp);
-
-       intel_crtc->fdi_lanes = lane;
-
-       if (intel_crtc->config.pixel_multiplier > 1)
-               link_bw *= intel_crtc->config.pixel_multiplier;
-       intel_link_compute_m_n(intel_crtc->config.pipe_bpp, lane, target_clock,
-                              link_bw, &m_n);
-
-       intel_cpu_transcoder_set_m_n(intel_crtc, &m_n);
+       return i9xx_dpll_compute_m(dpll) < factor * dpll->n;
 }
 
 static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc,
-                                     intel_clock_t *clock, u32 *fp,
+                                     u32 *fp,
                                      intel_clock_t *reduced_clock, u32 *fp2)
 {
        struct drm_crtc *crtc = &intel_crtc->base;
@@ -5501,7 +5570,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc,
        struct intel_encoder *intel_encoder;
        uint32_t dpll;
        int factor, num_connectors = 0;
-       bool is_lvds = false, is_sdvo = false, is_tv = false;
+       bool is_lvds = false, is_sdvo = false;
 
        for_each_encoder_on_crtc(dev, crtc, intel_encoder) {
                switch (intel_encoder->type) {
@@ -5511,11 +5580,6 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc,
                case INTEL_OUTPUT_SDVO:
                case INTEL_OUTPUT_HDMI:
                        is_sdvo = true;
-                       if (intel_encoder->needs_tv_clock)
-                               is_tv = true;
-                       break;
-               case INTEL_OUTPUT_TVOUT:
-                       is_tv = true;
                        break;
                }
 
@@ -5526,13 +5590,13 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc,
        factor = 21;
        if (is_lvds) {
                if ((intel_panel_use_ssc(dev_priv) &&
-                    dev_priv->lvds_ssc_freq == 100) ||
+                    dev_priv->vbt.lvds_ssc_freq == 100) ||
                    (HAS_PCH_IBX(dev) && intel_is_dual_link_lvds(dev)))
                        factor = 25;
-       } else if (is_sdvo && is_tv)
+       } else if (intel_crtc->config.sdvo_tv_clock)
                factor = 20;
 
-       if (clock->m < factor * clock->n)
+       if (ironlake_needs_fb_cb_tune(&intel_crtc->config.dpll, factor))
                *fp |= FP_CB_TUNE;
 
        if (fp2 && (reduced_clock->m < factor * reduced_clock->n))
@@ -5544,23 +5608,21 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc,
                dpll |= DPLLB_MODE_LVDS;
        else
                dpll |= DPLLB_MODE_DAC_SERIAL;
-       if (is_sdvo) {
-               if (intel_crtc->config.pixel_multiplier > 1) {
-                       dpll |= (intel_crtc->config.pixel_multiplier - 1)
-                               << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
-               }
+
+       dpll |= (intel_crtc->config.pixel_multiplier - 1)
+               << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
+
+       if (is_sdvo)
                dpll |= DPLL_DVO_HIGH_SPEED;
-       }
-       if (intel_crtc->config.has_dp_encoder &&
-           intel_crtc->config.has_pch_encoder)
+       if (intel_crtc->config.has_dp_encoder)
                dpll |= DPLL_DVO_HIGH_SPEED;
 
        /* compute bitmask from p1 value */
-       dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+       dpll |= (1 << (intel_crtc->config.dpll.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
        /* also FPA1 */
-       dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
+       dpll |= (1 << (intel_crtc->config.dpll.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
 
-       switch (clock->p2) {
+       switch (intel_crtc->config.dpll.p2) {
        case 5:
                dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
                break;
@@ -5575,18 +5637,12 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc,
                break;
        }
 
-       if (is_sdvo && is_tv)
-               dpll |= PLL_REF_INPUT_TVCLKINBC;
-       else if (is_tv)
-               /* XXX: just matching BIOS for now */
-               /*      dpll |= PLL_REF_INPUT_TVCLKINBC; */
-               dpll |= 3;
-       else if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2)
+       if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2)
                dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
        else
                dpll |= PLL_REF_INPUT_DREFCLK;
 
-       return dpll;
+       return dpll | DPLL_VCO_ENABLE;
 }
 
 static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
@@ -5596,19 +5652,16 @@ static int ironlake_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 *adjusted_mode =
-               &intel_crtc->config.adjusted_mode;
-       struct drm_display_mode *mode = &intel_crtc->config.requested_mode;
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
        int num_connectors = 0;
        intel_clock_t clock, reduced_clock;
-       u32 dpll, fp = 0, fp2 = 0;
+       u32 dpll = 0, fp = 0, fp2 = 0;
        bool ok, has_reduced_clock = false;
        bool is_lvds = false;
        struct intel_encoder *encoder;
+       struct intel_shared_dpll *pll;
        int ret;
-       bool dither, fdi_config_ok;
 
        for_each_encoder_on_crtc(dev, crtc, encoder) {
                switch (encoder->type) {
@@ -5623,11 +5676,9 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        WARN(!(HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)),
             "Unexpected PCH type %d\n", INTEL_PCH_TYPE(dev));
 
-       intel_crtc->config.cpu_transcoder = pipe;
-
-       ok = ironlake_compute_clocks(crtc, adjusted_mode, &clock,
+       ok = ironlake_compute_clocks(crtc, &clock,
                                     &has_reduced_clock, &reduced_clock);
-       if (!ok) {
+       if (!ok && !intel_crtc->config.clock_set) {
                DRM_ERROR("Couldn't find PLL settings for mode!\n");
                return -EINVAL;
        }
@@ -5643,34 +5694,31 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        /* Ensure that the cursor is valid for the new mode before changing... */
        intel_crtc_update_cursor(crtc, true);
 
-       /* determine panel color depth */
-       dither = intel_crtc->config.dither;
-       if (is_lvds && dev_priv->lvds_dither)
-               dither = true;
-
-       fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
-       if (has_reduced_clock)
-               fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 |
-                       reduced_clock.m2;
-
-       dpll = ironlake_compute_dpll(intel_crtc, &clock, &fp, &reduced_clock,
-                                    has_reduced_clock ? &fp2 : NULL);
-
-       DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe);
-       drm_mode_debug_printmodeline(mode);
-
        /* CPU eDP is the only output that doesn't need a PCH PLL of its own. */
        if (intel_crtc->config.has_pch_encoder) {
-               struct intel_pch_pll *pll;
+               fp = i9xx_dpll_compute_fp(&intel_crtc->config.dpll);
+               if (has_reduced_clock)
+                       fp2 = i9xx_dpll_compute_fp(&reduced_clock);
+
+               dpll = ironlake_compute_dpll(intel_crtc,
+                                            &fp, &reduced_clock,
+                                            has_reduced_clock ? &fp2 : NULL);
+
+               intel_crtc->config.dpll_hw_state.dpll = dpll;
+               intel_crtc->config.dpll_hw_state.fp0 = fp;
+               if (has_reduced_clock)
+                       intel_crtc->config.dpll_hw_state.fp1 = fp2;
+               else
+                       intel_crtc->config.dpll_hw_state.fp1 = fp;
 
-               pll = intel_get_pch_pll(intel_crtc, dpll, fp);
+               pll = intel_get_shared_dpll(intel_crtc, dpll, fp);
                if (pll == NULL) {
-                       DRM_DEBUG_DRIVER("failed to find PLL for pipe %d\n",
-                                        pipe);
+                       DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n",
+                                        pipe_name(pipe));
                        return -EINVAL;
                }
        } else
-               intel_put_pch_pll(intel_crtc);
+               intel_put_shared_dpll(intel_crtc);
 
        if (intel_crtc->config.has_dp_encoder)
                intel_dp_set_m_n(intel_crtc);
@@ -5679,11 +5727,18 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                if (encoder->pre_pll_enable)
                        encoder->pre_pll_enable(encoder);
 
-       if (intel_crtc->pch_pll) {
-               I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll);
+       if (is_lvds && has_reduced_clock && i915_powersave)
+               intel_crtc->lowfreq_avail = true;
+       else
+               intel_crtc->lowfreq_avail = false;
+
+       if (intel_crtc->config.has_pch_encoder) {
+               pll = intel_crtc_to_shared_dpll(intel_crtc);
+
+               I915_WRITE(PCH_DPLL(pll->id), dpll);
 
                /* Wait for the clocks to stabilize. */
-               POSTING_READ(intel_crtc->pch_pll->pll_reg);
+               POSTING_READ(PCH_DPLL(pll->id));
                udelay(150);
 
                /* The pixel multiplier can only be updated once the
@@ -5691,32 +5746,25 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                 *
                 * So write it again.
                 */
-               I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll);
-       }
+               I915_WRITE(PCH_DPLL(pll->id), dpll);
 
-       intel_crtc->lowfreq_avail = false;
-       if (intel_crtc->pch_pll) {
-               if (is_lvds && has_reduced_clock && i915_powersave) {
-                       I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp2);
-                       intel_crtc->lowfreq_avail = true;
-               } else {
-                       I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp);
-               }
+               if (has_reduced_clock)
+                       I915_WRITE(PCH_FP1(pll->id), fp2);
+               else
+                       I915_WRITE(PCH_FP1(pll->id), fp);
        }
 
-       intel_set_pipe_timings(intel_crtc, mode, adjusted_mode);
+       intel_set_pipe_timings(intel_crtc);
 
-       /* Note, this also computes intel_crtc->fdi_lanes which is used below in
-        * ironlake_check_fdi_lanes. */
-       intel_crtc->fdi_lanes = 0;
-       if (intel_crtc->config.has_pch_encoder)
-               ironlake_fdi_set_m_n(crtc);
-
-       fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc);
+       if (intel_crtc->config.has_pch_encoder) {
+               intel_cpu_transcoder_set_m_n(intel_crtc,
+                                            &intel_crtc->config.fdi_m_n);
+       }
 
-       ironlake_set_pipeconf(crtc, adjusted_mode, dither);
+       if (IS_IVYBRIDGE(dev))
+               ivybridge_update_fdi_bc_bifurcation(intel_crtc);
 
-       intel_wait_for_vblank(dev, pipe);
+       ironlake_set_pipeconf(crtc);
 
        /* Set up the display plane register */
        I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE);
@@ -5726,9 +5774,46 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
 
        intel_update_watermarks(dev);
 
-       intel_update_linetime_watermarks(dev, pipe, adjusted_mode);
+       return ret;
+}
+
+static void ironlake_get_fdi_m_n_config(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;
+       enum transcoder transcoder = pipe_config->cpu_transcoder;
+
+       pipe_config->fdi_m_n.link_m = I915_READ(PIPE_LINK_M1(transcoder));
+       pipe_config->fdi_m_n.link_n = I915_READ(PIPE_LINK_N1(transcoder));
+       pipe_config->fdi_m_n.gmch_m = I915_READ(PIPE_DATA_M1(transcoder))
+                                       & ~TU_SIZE_MASK;
+       pipe_config->fdi_m_n.gmch_n = I915_READ(PIPE_DATA_N1(transcoder));
+       pipe_config->fdi_m_n.tu = ((I915_READ(PIPE_DATA_M1(transcoder))
+                                  & TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1;
+}
+
+static void ironlake_get_pfit_config(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;
+       uint32_t tmp;
+
+       tmp = I915_READ(PF_CTL(crtc->pipe));
+
+       if (tmp & PF_ENABLE) {
+               pipe_config->pch_pfit.pos = I915_READ(PF_WIN_POS(crtc->pipe));
+               pipe_config->pch_pfit.size = I915_READ(PF_WIN_SZ(crtc->pipe));
 
-       return fdi_config_ok ? ret : -EINVAL;
+               /* We currently do not free assignements of panel fitters on
+                * ivb/hsw (since we don't use the higher upscaling modes which
+                * differentiates them) so just WARN about this case for now. */
+               if (IS_GEN7(dev)) {
+                       WARN_ON((tmp & PF_PIPE_SEL_MASK_IVB) !=
+                               PF_PIPE_SEL_IVB(crtc->pipe));
+               }
+       }
 }
 
 static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
@@ -5738,42 +5823,67 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
        struct drm_i915_private *dev_priv = dev->dev_private;
        uint32_t tmp;
 
+       pipe_config->cpu_transcoder = crtc->pipe;
+       pipe_config->shared_dpll = DPLL_ID_PRIVATE;
+
        tmp = I915_READ(PIPECONF(crtc->pipe));
        if (!(tmp & PIPECONF_ENABLE))
                return false;
 
-       if (I915_READ(TRANSCONF(crtc->pipe)) & TRANS_ENABLE)
+       if (I915_READ(PCH_TRANSCONF(crtc->pipe)) & TRANS_ENABLE) {
+               struct intel_shared_dpll *pll;
+
                pipe_config->has_pch_encoder = true;
 
+               tmp = I915_READ(FDI_RX_CTL(crtc->pipe));
+               pipe_config->fdi_lanes = ((FDI_DP_PORT_WIDTH_MASK & tmp) >>
+                                         FDI_DP_PORT_WIDTH_SHIFT) + 1;
+
+               ironlake_get_fdi_m_n_config(crtc, pipe_config);
+
+               /* XXX: Can't properly read out the pch dpll pixel multiplier
+                * since we don't have state tracking for pch clocks yet. */
+               pipe_config->pixel_multiplier = 1;
+
+               if (HAS_PCH_IBX(dev_priv->dev)) {
+                       pipe_config->shared_dpll = crtc->pipe;
+               } else {
+                       tmp = I915_READ(PCH_DPLL_SEL);
+                       if (tmp & TRANS_DPLLB_SEL(crtc->pipe))
+                               pipe_config->shared_dpll = DPLL_ID_PCH_PLL_B;
+                       else
+                               pipe_config->shared_dpll = DPLL_ID_PCH_PLL_A;
+               }
+
+               pll = &dev_priv->shared_dplls[pipe_config->shared_dpll];
+
+               WARN_ON(!pll->get_hw_state(dev_priv, pll,
+                                          &pipe_config->dpll_hw_state));
+       } else {
+               pipe_config->pixel_multiplier = 1;
+       }
+
+       intel_get_pipe_timings(crtc, pipe_config);
+
+       ironlake_get_pfit_config(crtc, pipe_config);
+
        return true;
 }
 
 static void haswell_modeset_global_resources(struct drm_device *dev)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
        bool enable = false;
        struct intel_crtc *crtc;
-       struct intel_encoder *encoder;
 
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
-               if (crtc->pipe != PIPE_A && crtc->base.enabled)
-                       enable = true;
-               /* XXX: Should check for edp transcoder here, but thanks to init
-                * sequence that's not yet available. Just in case desktop eDP
-                * on PORT D is possible on haswell, too. */
-       }
+               if (!crtc->base.enabled)
+                       continue;
 
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
-                           base.head) {
-               if (encoder->type != INTEL_OUTPUT_EDP &&
-                   encoder->connectors_active)
+               if (crtc->pipe != PIPE_A || crtc->config.pch_pfit.size ||
+                   crtc->config.cpu_transcoder != TRANSCODER_EDP)
                        enable = true;
        }
 
-       /* Even the eDP panel fitter is outside the always-on well. */
-       if (dev_priv->pch_pf_size)
-               enable = true;
-
        intel_set_power_well(dev, enable);
 }
 
@@ -5784,68 +5894,28 @@ static int haswell_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 *adjusted_mode =
-               &intel_crtc->config.adjusted_mode;
-       struct drm_display_mode *mode = &intel_crtc->config.requested_mode;
-       int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
-       int num_connectors = 0;
-       bool is_cpu_edp = false;
-       struct intel_encoder *encoder;
        int ret;
-       bool dither;
-
-       for_each_encoder_on_crtc(dev, crtc, encoder) {
-               switch (encoder->type) {
-               case INTEL_OUTPUT_EDP:
-                       if (!intel_encoder_is_pch_edp(&encoder->base))
-                               is_cpu_edp = true;
-                       break;
-               }
-
-               num_connectors++;
-       }
-
-       if (is_cpu_edp)
-               intel_crtc->config.cpu_transcoder = TRANSCODER_EDP;
-       else
-               intel_crtc->config.cpu_transcoder = pipe;
-
-       /* We are not sure yet this won't happen. */
-       WARN(!HAS_PCH_LPT(dev), "Unexpected PCH type %d\n",
-            INTEL_PCH_TYPE(dev));
-
-       WARN(num_connectors != 1, "%d connectors attached to pipe %c\n",
-            num_connectors, pipe_name(pipe));
-
-       WARN_ON(I915_READ(PIPECONF(intel_crtc->config.cpu_transcoder)) &
-               (PIPECONF_ENABLE | I965_PIPECONF_ACTIVE));
-
-       WARN_ON(I915_READ(DSPCNTR(plane)) & DISPLAY_PLANE_ENABLE);
 
-       if (!intel_ddi_pll_mode_set(crtc, adjusted_mode->clock))
+       if (!intel_ddi_pll_mode_set(crtc))
                return -EINVAL;
 
        /* Ensure that the cursor is valid for the new mode before changing... */
        intel_crtc_update_cursor(crtc, true);
 
-       /* determine panel color depth */
-       dither = intel_crtc->config.dither;
-
-       DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe);
-       drm_mode_debug_printmodeline(mode);
-
        if (intel_crtc->config.has_dp_encoder)
                intel_dp_set_m_n(intel_crtc);
 
        intel_crtc->lowfreq_avail = false;
 
-       intel_set_pipe_timings(intel_crtc, mode, adjusted_mode);
+       intel_set_pipe_timings(intel_crtc);
 
-       if (intel_crtc->config.has_pch_encoder)
-               ironlake_fdi_set_m_n(crtc);
+       if (intel_crtc->config.has_pch_encoder) {
+               intel_cpu_transcoder_set_m_n(intel_crtc,
+                                            &intel_crtc->config.fdi_m_n);
+       }
 
-       haswell_set_pipeconf(crtc, adjusted_mode, dither);
+       haswell_set_pipeconf(crtc);
 
        intel_set_pipe_csc(crtc);
 
@@ -5857,8 +5927,6 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc,
 
        intel_update_watermarks(dev);
 
-       intel_update_linetime_watermarks(dev, pipe, adjusted_mode);
-
        return ret;
 }
 
@@ -5867,22 +5935,69 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
 {
        struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       enum intel_display_power_domain pfit_domain;
        uint32_t tmp;
 
-       tmp = I915_READ(PIPECONF(crtc->config.cpu_transcoder));
+       pipe_config->cpu_transcoder = crtc->pipe;
+       pipe_config->shared_dpll = DPLL_ID_PRIVATE;
+
+       tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP));
+       if (tmp & TRANS_DDI_FUNC_ENABLE) {
+               enum pipe trans_edp_pipe;
+               switch (tmp & TRANS_DDI_EDP_INPUT_MASK) {
+               default:
+                       WARN(1, "unknown pipe linked to edp transcoder\n");
+               case TRANS_DDI_EDP_INPUT_A_ONOFF:
+               case TRANS_DDI_EDP_INPUT_A_ON:
+                       trans_edp_pipe = PIPE_A;
+                       break;
+               case TRANS_DDI_EDP_INPUT_B_ONOFF:
+                       trans_edp_pipe = PIPE_B;
+                       break;
+               case TRANS_DDI_EDP_INPUT_C_ONOFF:
+                       trans_edp_pipe = PIPE_C;
+                       break;
+               }
+
+               if (trans_edp_pipe == crtc->pipe)
+                       pipe_config->cpu_transcoder = TRANSCODER_EDP;
+       }
+
+       if (!intel_display_power_enabled(dev,
+                       POWER_DOMAIN_TRANSCODER(pipe_config->cpu_transcoder)))
+               return false;
+
+       tmp = I915_READ(PIPECONF(pipe_config->cpu_transcoder));
        if (!(tmp & PIPECONF_ENABLE))
                return false;
 
        /*
-        * aswell has only FDI/PCH transcoder A. It is which is connected to
+        * Haswell has only FDI/PCH transcoder A. It is which is connected to
         * DDI E. So just check whether this pipe is wired to DDI E and whether
         * the PCH transcoder is on.
         */
-       tmp = I915_READ(TRANS_DDI_FUNC_CTL(crtc->pipe));
+       tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe_config->cpu_transcoder));
        if ((tmp & TRANS_DDI_PORT_MASK) == TRANS_DDI_SELECT_PORT(PORT_E) &&
-           I915_READ(TRANSCONF(PIPE_A)) & TRANS_ENABLE)
+           I915_READ(LPT_TRANSCONF) & TRANS_ENABLE) {
                pipe_config->has_pch_encoder = true;
 
+               tmp = I915_READ(FDI_RX_CTL(PIPE_A));
+               pipe_config->fdi_lanes = ((FDI_DP_PORT_WIDTH_MASK & tmp) >>
+                                         FDI_DP_PORT_WIDTH_SHIFT) + 1;
+
+               ironlake_get_fdi_m_n_config(crtc, pipe_config);
+       }
+
+       intel_get_pipe_timings(crtc, pipe_config);
+
+       pfit_domain = POWER_DOMAIN_PIPE_PANEL_FITTER(crtc->pipe);
+       if (intel_display_power_enabled(dev, pfit_domain))
+               ironlake_get_pfit_config(crtc, pipe_config);
+
+       pipe_config->ips_enabled = hsw_crtc_supports_ips(crtc) &&
+                                  (I915_READ(IPS_CTL) & IPS_ENABLE);
+
+       pipe_config->pixel_multiplier = 1;
 
        return true;
 }
@@ -6120,7 +6235,7 @@ static void ironlake_write_eld(struct drm_connector *connector,
                eldv |= IBX_ELD_VALIDB << 4;
                eldv |= IBX_ELD_VALIDB << 8;
        } else {
-               DRM_DEBUG_DRIVER("ELD on port %c\n", 'A' + i);
+               DRM_DEBUG_DRIVER("ELD on port %c\n", port_name(i));
                eldv = IBX_ELD_VALIDB << ((i - 1) * 4);
        }
 
@@ -6188,16 +6303,31 @@ 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);
-       int palreg = PALETTE(intel_crtc->pipe);
+       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(intel_crtc->pipe);
+               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,
@@ -6205,6 +6335,9 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
                           (intel_crtc->lut_g[i] << 8) |
                           intel_crtc->lut_b[i]);
        }
+
+       if (reenable_ips)
+               hsw_enable_ips(intel_crtc);
 }
 
 static void i845_update_cursor(struct drm_crtc *crtc, u32 base)
@@ -6451,7 +6584,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
        intel_crtc->cursor_width = width;
        intel_crtc->cursor_height = height;
 
-       intel_crtc_update_cursor(crtc, true);
+       intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL);
 
        return 0;
 fail_unpin:
@@ -6470,7 +6603,7 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
        intel_crtc->cursor_x = x;
        intel_crtc->cursor_y = y;
 
-       intel_crtc_update_cursor(crtc, true);
+       intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL);
 
        return 0;
 }
@@ -6791,8 +6924,10 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
                        return 0;
                }
 
-               /* XXX: Handle the 100Mhz refclk */
-               intel_clock(dev, 96000, &clock);
+               if (IS_PINEVIEW(dev))
+                       pineview_clock(96000, &clock);
+               else
+                       i9xx_clock(96000, &clock);
        } else {
                bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN);
 
@@ -6804,9 +6939,9 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
                        if ((dpll & PLL_REF_INPUT_MASK) ==
                            PLLB_REF_INPUT_SPREADSPECTRUMIN) {
                                /* XXX: might not be 66MHz */
-                               intel_clock(dev, 66000, &clock);
+                               i9xx_clock(66000, &clock);
                        } else
-                               intel_clock(dev, 48000, &clock);
+                               i9xx_clock(48000, &clock);
                } else {
                        if (dpll & PLL_P1_DIVIDE_BY_TWO)
                                clock.p1 = 2;
@@ -6819,7 +6954,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
                        else
                                clock.p2 = 2;
 
-                       intel_clock(dev, 48000, &clock);
+                       i9xx_clock(48000, &clock);
                }
        }
 
@@ -6950,7 +7085,8 @@ void intel_mark_idle(struct drm_device *dev)
        }
 }
 
-void intel_mark_fb_busy(struct drm_i915_gem_object *obj)
+void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
+                       struct intel_ring_buffer *ring)
 {
        struct drm_device *dev = obj->base.dev;
        struct drm_crtc *crtc;
@@ -6962,8 +7098,12 @@ void intel_mark_fb_busy(struct drm_i915_gem_object *obj)
                if (!crtc->fb)
                        continue;
 
-               if (to_intel_framebuffer(crtc->fb)->obj == obj)
-                       intel_increase_pllclock(crtc);
+               if (to_intel_framebuffer(crtc->fb)->obj != obj)
+                       continue;
+
+               intel_increase_pllclock(crtc);
+               if (ring && intel_fbc_enabled(dev))
+                       ring->fbc_dirty = true;
        }
 }
 
@@ -6984,6 +7124,8 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
                kfree(work);
        }
 
+       intel_crtc_cursor_set(crtc, NULL, 0, 0, 0);
+
        drm_crtc_cleanup(crtc);
 
        kfree(intel_crtc);
@@ -7411,7 +7553,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
                goto cleanup_pending;
 
        intel_disable_fbc(dev);
-       intel_mark_fb_busy(obj);
+       intel_mark_fb_busy(obj, NULL);
        mutex_unlock(&dev->struct_mutex);
 
        trace_i915_flip_request(intel_crtc->plane, obj);
@@ -7442,28 +7584,6 @@ static struct drm_crtc_helper_funcs intel_helper_funcs = {
        .load_lut = intel_crtc_load_lut,
 };
 
-bool intel_encoder_check_is_cloned(struct intel_encoder *encoder)
-{
-       struct intel_encoder *other_encoder;
-       struct drm_crtc *crtc = &encoder->new_crtc->base;
-
-       if (WARN_ON(!crtc))
-               return false;
-
-       list_for_each_entry(other_encoder,
-                           &crtc->dev->mode_config.encoder_list,
-                           base.head) {
-
-               if (&other_encoder->new_crtc->base != crtc ||
-                   encoder == other_encoder)
-                       continue;
-               else
-                       return true;
-       }
-
-       return false;
-}
-
 static bool intel_encoder_crtc_ok(struct drm_encoder *encoder,
                                  struct drm_crtc *crtc)
 {
@@ -7531,13 +7651,39 @@ static void intel_modeset_commit_output_state(struct drm_device *dev)
        }
 }
 
+static void
+connected_sink_compute_bpp(struct intel_connector * connector,
+                          struct intel_crtc_config *pipe_config)
+{
+       int bpp = pipe_config->pipe_bpp;
+
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] checking for sink bpp constrains\n",
+               connector->base.base.id,
+               drm_get_connector_name(&connector->base));
+
+       /* Don't use an invalid EDID bpc value */
+       if (connector->base.display_info.bpc &&
+           connector->base.display_info.bpc * 3 < bpp) {
+               DRM_DEBUG_KMS("clamping display bpp (was %d) to EDID reported max of %d\n",
+                             bpp, connector->base.display_info.bpc*3);
+               pipe_config->pipe_bpp = connector->base.display_info.bpc*3;
+       }
+
+       /* Clamp bpp to 8 on screens without EDID 1.4 */
+       if (connector->base.display_info.bpc == 0 && bpp > 24) {
+               DRM_DEBUG_KMS("clamping display bpp (was %d) to default limit of 24\n",
+                             bpp);
+               pipe_config->pipe_bpp = 24;
+       }
+}
+
 static int
-pipe_config_set_bpp(struct drm_crtc *crtc,
-                   struct drm_framebuffer *fb,
-                   struct intel_crtc_config *pipe_config)
+compute_baseline_pipe_bpp(struct intel_crtc *crtc,
+                         struct drm_framebuffer *fb,
+                         struct intel_crtc_config *pipe_config)
 {
-       struct drm_device *dev = crtc->dev;
-       struct drm_connector *connector;
+       struct drm_device *dev = crtc->base.dev;
+       struct intel_connector *connector;
        int bpp;
 
        switch (fb->pixel_format) {
@@ -7580,22 +7726,66 @@ pipe_config_set_bpp(struct drm_crtc *crtc,
 
        /* Clamp display bpp to EDID value */
        list_for_each_entry(connector, &dev->mode_config.connector_list,
-                           head) {
-               if (connector->encoder && connector->encoder->crtc != crtc)
+                           base.head) {
+               if (!connector->new_encoder ||
+                   connector->new_encoder->new_crtc != crtc)
                        continue;
 
-               /* Don't use an invalid EDID bpc value */
-               if (connector->display_info.bpc &&
-                   connector->display_info.bpc * 3 < bpp) {
-                       DRM_DEBUG_KMS("clamping display bpp (was %d) to EDID reported max of %d\n",
-                                     bpp, connector->display_info.bpc*3);
-                       pipe_config->pipe_bpp = connector->display_info.bpc*3;
-               }
+               connected_sink_compute_bpp(connector, pipe_config);
        }
 
        return bpp;
 }
 
+static void intel_dump_pipe_config(struct intel_crtc *crtc,
+                                  struct intel_crtc_config *pipe_config,
+                                  const char *context)
+{
+       DRM_DEBUG_KMS("[CRTC:%d]%s config for pipe %c\n", crtc->base.base.id,
+                     context, pipe_name(crtc->pipe));
+
+       DRM_DEBUG_KMS("cpu_transcoder: %c\n", transcoder_name(pipe_config->cpu_transcoder));
+       DRM_DEBUG_KMS("pipe bpp: %i, dithering: %i\n",
+                     pipe_config->pipe_bpp, pipe_config->dither);
+       DRM_DEBUG_KMS("fdi/pch: %i, lanes: %i, gmch_m: %u, gmch_n: %u, link_m: %u, link_n: %u, tu: %u\n",
+                     pipe_config->has_pch_encoder,
+                     pipe_config->fdi_lanes,
+                     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("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);
+       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,
+                     pipe_config->gmch_pfit.lvds_border_bits);
+       DRM_DEBUG_KMS("pch pfit: pos: 0x%08x, size: 0x%08x\n",
+                     pipe_config->pch_pfit.pos,
+                     pipe_config->pch_pfit.size);
+       DRM_DEBUG_KMS("ips: %i\n", pipe_config->ips_enabled);
+}
+
+static bool check_encoder_cloning(struct drm_crtc *crtc)
+{
+       int num_encoders = 0;
+       bool uncloneable_encoders = false;
+       struct intel_encoder *encoder;
+
+       list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list,
+                           base.head) {
+               if (&encoder->new_crtc->base != crtc)
+                       continue;
+
+               num_encoders++;
+               if (!encoder->cloneable)
+                       uncloneable_encoders = true;
+       }
+
+       return !(num_encoders > 1 && uncloneable_encoders);
+}
+
 static struct intel_crtc_config *
 intel_modeset_pipe_config(struct drm_crtc *crtc,
                          struct drm_framebuffer *fb,
@@ -7605,7 +7795,13 @@ intel_modeset_pipe_config(struct drm_crtc *crtc,
        struct drm_encoder_helper_funcs *encoder_funcs;
        struct intel_encoder *encoder;
        struct intel_crtc_config *pipe_config;
-       int plane_bpp;
+       int plane_bpp, ret = -EINVAL;
+       bool retry = true;
+
+       if (!check_encoder_cloning(crtc)) {
+               DRM_DEBUG_KMS("rejecting invalid cloning configuration\n");
+               return ERR_PTR(-EINVAL);
+       }
 
        pipe_config = kzalloc(sizeof(*pipe_config), GFP_KERNEL);
        if (!pipe_config)
@@ -7613,11 +7809,23 @@ intel_modeset_pipe_config(struct drm_crtc *crtc,
 
        drm_mode_copy(&pipe_config->adjusted_mode, mode);
        drm_mode_copy(&pipe_config->requested_mode, mode);
-
-       plane_bpp = pipe_config_set_bpp(crtc, fb, pipe_config);
+       pipe_config->cpu_transcoder = to_intel_crtc(crtc)->pipe;
+       pipe_config->shared_dpll = DPLL_ID_PRIVATE;
+
+       /* Compute a starting value for pipe_config->pipe_bpp taking the source
+        * plane pixel format and any sink constraints into account. Returns the
+        * source plane bpp so that dithering can be selected on mismatches
+        * after encoders and crtc also have had their say. */
+       plane_bpp = compute_baseline_pipe_bpp(to_intel_crtc(crtc),
+                                             fb, pipe_config);
        if (plane_bpp < 0)
                goto fail;
 
+encoder_retry:
+       /* Ensure the port clock defaults are reset when retrying. */
+       pipe_config->port_clock = 0;
+       pipe_config->pixel_multiplier = 1;
+
        /* 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
         * a chance to reject the mode entirely.
@@ -7646,11 +7854,27 @@ intel_modeset_pipe_config(struct drm_crtc *crtc,
                }
        }
 
-       if (!(intel_crtc_compute_config(crtc, pipe_config))) {
+       /* 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;
+
+       ret = intel_crtc_compute_config(to_intel_crtc(crtc), pipe_config);
+       if (ret < 0) {
                DRM_DEBUG_KMS("CRTC fixup failed\n");
                goto fail;
        }
-       DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
+
+       if (ret == RETRY) {
+               if (WARN(!retry, "loop in pipe configuration computation\n")) {
+                       ret = -EINVAL;
+                       goto fail;
+               }
+
+               DRM_DEBUG_KMS("CRTC bw constrained, retrying\n");
+               retry = false;
+               goto encoder_retry;
+       }
 
        pipe_config->dither = pipe_config->pipe_bpp != plane_bpp;
        DRM_DEBUG_KMS("plane bpp: %i, pipe bpp: %i, dithering: %i\n",
@@ -7659,7 +7883,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc,
        return pipe_config;
 fail:
        kfree(pipe_config);
-       return ERR_PTR(-EINVAL);
+       return ERR_PTR(ret);
 }
 
 /* Computes which crtcs are affected and sets the relevant bits in the mask. For
@@ -7755,6 +7979,9 @@ intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes,
         */
        *modeset_pipes &= 1 << intel_crtc->pipe;
        *prepare_pipes &= 1 << intel_crtc->pipe;
+
+       DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n",
+                     *modeset_pipes, *prepare_pipes, *disable_pipes);
 }
 
 static bool intel_crtc_in_use(struct drm_crtc *crtc)
@@ -7821,31 +8048,114 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes)
        list_for_each_entry((intel_crtc), \
                            &(dev)->mode_config.crtc_list, \
                            base.head) \
-               if (mask & (1 <<(intel_crtc)->pipe)) \
+               if (mask & (1 <<(intel_crtc)->pipe))
 
 static bool
-intel_pipe_config_compare(struct intel_crtc_config *current_config,
+intel_pipe_config_compare(struct drm_device *dev,
+                         struct intel_crtc_config *current_config,
                          struct intel_crtc_config *pipe_config)
 {
-       if (current_config->has_pch_encoder != pipe_config->has_pch_encoder) {
-               DRM_ERROR("mismatch in has_pch_encoder "
-                         "(expected %i, found %i)\n",
-                         current_config->has_pch_encoder,
-                         pipe_config->has_pch_encoder);
-               return false;
-       }
+#define PIPE_CONF_CHECK_X(name)        \
+       if (current_config->name != pipe_config->name) { \
+               DRM_ERROR("mismatch in " #name " " \
+                         "(expected 0x%08x, found 0x%08x)\n", \
+                         current_config->name, \
+                         pipe_config->name); \
+               return false; \
+       }
+
+#define PIPE_CONF_CHECK_I(name)        \
+       if (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_CHECK_FLAGS(name, mask)      \
+       if ((current_config->name ^ pipe_config->name) & (mask)) { \
+               DRM_ERROR("mismatch in " #name " " \
+                         "(expected %i, found %i)\n", \
+                         current_config->name & (mask), \
+                         pipe_config->name & (mask)); \
+               return false; \
+       }
+
+#define PIPE_CONF_QUIRK(quirk) \
+       ((current_config->quirks | pipe_config->quirks) & (quirk))
+
+       PIPE_CONF_CHECK_I(cpu_transcoder);
+
+       PIPE_CONF_CHECK_I(has_pch_encoder);
+       PIPE_CONF_CHECK_I(fdi_lanes);
+       PIPE_CONF_CHECK_I(fdi_m_n.gmch_m);
+       PIPE_CONF_CHECK_I(fdi_m_n.gmch_n);
+       PIPE_CONF_CHECK_I(fdi_m_n.link_m);
+       PIPE_CONF_CHECK_I(fdi_m_n.link_n);
+       PIPE_CONF_CHECK_I(fdi_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);
+       PIPE_CONF_CHECK_I(adjusted_mode.crtc_hblank_end);
+       PIPE_CONF_CHECK_I(adjusted_mode.crtc_hsync_start);
+       PIPE_CONF_CHECK_I(adjusted_mode.crtc_hsync_end);
+
+       PIPE_CONF_CHECK_I(adjusted_mode.crtc_vdisplay);
+       PIPE_CONF_CHECK_I(adjusted_mode.crtc_vtotal);
+       PIPE_CONF_CHECK_I(adjusted_mode.crtc_vblank_start);
+       PIPE_CONF_CHECK_I(adjusted_mode.crtc_vblank_end);
+       PIPE_CONF_CHECK_I(adjusted_mode.crtc_vsync_start);
+       PIPE_CONF_CHECK_I(adjusted_mode.crtc_vsync_end);
+
+       if (!HAS_PCH_SPLIT(dev))
+               PIPE_CONF_CHECK_I(pixel_multiplier);
+
+       PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags,
+                             DRM_MODE_FLAG_INTERLACE);
+
+       if (!PIPE_CONF_QUIRK(PIPE_CONFIG_QUIRK_MODE_SYNC_FLAGS)) {
+               PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags,
+                                     DRM_MODE_FLAG_PHSYNC);
+               PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags,
+                                     DRM_MODE_FLAG_NHSYNC);
+               PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags,
+                                     DRM_MODE_FLAG_PVSYNC);
+               PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags,
+                                     DRM_MODE_FLAG_NVSYNC);
+       }
+
+       PIPE_CONF_CHECK_I(requested_mode.hdisplay);
+       PIPE_CONF_CHECK_I(requested_mode.vdisplay);
+
+       PIPE_CONF_CHECK_I(gmch_pfit.control);
+       /* pfit ratios are autocomputed by the hw on gen4+ */
+       if (INTEL_INFO(dev)->gen < 4)
+               PIPE_CONF_CHECK_I(gmch_pfit.pgm_ratios);
+       PIPE_CONF_CHECK_I(gmch_pfit.lvds_border_bits);
+       PIPE_CONF_CHECK_I(pch_pfit.pos);
+       PIPE_CONF_CHECK_I(pch_pfit.size);
+
+       PIPE_CONF_CHECK_I(ips_enabled);
+
+       PIPE_CONF_CHECK_I(shared_dpll);
+       PIPE_CONF_CHECK_X(dpll_hw_state.dpll);
+       PIPE_CONF_CHECK_X(dpll_hw_state.fp0);
+       PIPE_CONF_CHECK_X(dpll_hw_state.fp1);
+
+#undef PIPE_CONF_CHECK_X
+#undef PIPE_CONF_CHECK_I
+#undef PIPE_CONF_CHECK_FLAGS
+#undef PIPE_CONF_QUIRK
 
        return true;
 }
 
-void
-intel_modeset_check_state(struct drm_device *dev)
+static void
+check_connector_state(struct drm_device *dev)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       struct intel_crtc *crtc;
-       struct intel_encoder *encoder;
        struct intel_connector *connector;
-       struct intel_crtc_config pipe_config;
 
        list_for_each_entry(connector, &dev->mode_config.connector_list,
                            base.head) {
@@ -7856,6 +8166,13 @@ intel_modeset_check_state(struct drm_device *dev)
                WARN(&connector->new_encoder->base != connector->base.encoder,
                     "connector's staged encoder doesn't match current encoder\n");
        }
+}
+
+static void
+check_encoder_state(struct drm_device *dev)
+{
+       struct intel_encoder *encoder;
+       struct intel_connector *connector;
 
        list_for_each_entry(encoder, &dev->mode_config.encoder_list,
                            base.head) {
@@ -7907,12 +8224,23 @@ intel_modeset_check_state(struct drm_device *dev)
                     tracked_pipe, pipe);
 
        }
+}
+
+static void
+check_crtc_state(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_crtc *crtc;
+       struct intel_encoder *encoder;
+       struct intel_crtc_config pipe_config;
 
        list_for_each_entry(crtc, &dev->mode_config.crtc_list,
                            base.head) {
                bool enabled = false;
                bool active = false;
 
+               memset(&pipe_config, 0, sizeof(pipe_config));
+
                DRM_DEBUG_KMS("[CRTC:%d]\n",
                              crtc->base.base.id);
 
@@ -7927,6 +8255,7 @@ intel_modeset_check_state(struct drm_device *dev)
                        if (encoder->connectors_active)
                                active = true;
                }
+
                WARN(active != crtc->active,
                     "crtc's computed active state doesn't match tracked active state "
                     "(expected %i, found %i)\n", active, crtc->active);
@@ -7934,7 +8263,6 @@ intel_modeset_check_state(struct drm_device *dev)
                     "crtc's computed enabled state doesn't match tracked enabled state "
                     "(expected %i, found %i)\n", enabled, crtc->base.enabled);
 
-               memset(&pipe_config, 0, sizeof(pipe_config));
                active = dev_priv->display.get_pipe_config(crtc,
                                                           &pipe_config);
 
@@ -7942,16 +8270,86 @@ intel_modeset_check_state(struct drm_device *dev)
                if (crtc->pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE)
                        active = crtc->active;
 
+               list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                                   base.head) {
+                       if (encoder->base.crtc != &crtc->base)
+                               continue;
+                       if (encoder->get_config)
+                               encoder->get_config(encoder, &pipe_config);
+               }
+
                WARN(crtc->active != active,
                     "crtc active state doesn't match with hw state "
                     "(expected %i, found %i)\n", crtc->active, active);
 
-               WARN(active &&
-                    !intel_pipe_config_compare(&crtc->config, &pipe_config),
-                    "pipe state doesn't match!\n");
+               if (active &&
+                   !intel_pipe_config_compare(dev, &crtc->config, &pipe_config)) {
+                       WARN(1, "pipe state doesn't match!\n");
+                       intel_dump_pipe_config(crtc, &pipe_config,
+                                              "[hw state]");
+                       intel_dump_pipe_config(crtc, &crtc->config,
+                                              "[sw state]");
+               }
+       }
+}
+
+static void
+check_shared_dpll_state(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_crtc *crtc;
+       struct intel_dpll_hw_state dpll_hw_state;
+       int i;
+
+       for (i = 0; i < dev_priv->num_shared_dpll; i++) {
+               struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i];
+               int enabled_crtcs = 0, active_crtcs = 0;
+               bool active;
+
+               memset(&dpll_hw_state, 0, sizeof(dpll_hw_state));
+
+               DRM_DEBUG_KMS("%s\n", pll->name);
+
+               active = pll->get_hw_state(dev_priv, pll, &dpll_hw_state);
+
+               WARN(pll->active > pll->refcount,
+                    "more active pll users than references: %i vs %i\n",
+                    pll->active, pll->refcount);
+               WARN(pll->active && !pll->on,
+                    "pll in active use but not on in sw tracking\n");
+               WARN(pll->on != active,
+                    "pll on state mismatch (expected %i, found %i)\n",
+                    pll->on, active);
+
+               list_for_each_entry(crtc, &dev->mode_config.crtc_list,
+                                   base.head) {
+                       if (crtc->base.enabled && intel_crtc_to_shared_dpll(crtc) == pll)
+                               enabled_crtcs++;
+                       if (crtc->active && intel_crtc_to_shared_dpll(crtc) == pll)
+                               active_crtcs++;
+               }
+               WARN(pll->active != active_crtcs,
+                    "pll active crtcs mismatch (expected %i, found %i)\n",
+                    pll->active, active_crtcs);
+               WARN(pll->refcount != enabled_crtcs,
+                    "pll enabled crtcs mismatch (expected %i, found %i)\n",
+                    pll->refcount, enabled_crtcs);
+
+               WARN(pll->on && memcmp(&pll->hw_state, &dpll_hw_state,
+                                      sizeof(dpll_hw_state)),
+                    "pll hw state mismatch\n");
        }
 }
 
+void
+intel_modeset_check_state(struct drm_device *dev)
+{
+       check_connector_state(dev);
+       check_encoder_state(dev);
+       check_crtc_state(dev);
+       check_shared_dpll_state(dev);
+}
+
 static int __intel_set_mode(struct drm_crtc *crtc,
                            struct drm_display_mode *mode,
                            int x, int y, struct drm_framebuffer *fb)
@@ -7988,11 +8386,10 @@ static int __intel_set_mode(struct drm_crtc *crtc,
 
                        goto out;
                }
+               intel_dump_pipe_config(to_intel_crtc(crtc), pipe_config,
+                                      "[modeset]");
        }
 
-       DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n",
-                     modeset_pipes, prepare_pipes, disable_pipes);
-
        for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc)
                intel_crtc_disable(&intel_crtc->base);
 
@@ -8005,12 +8402,10 @@ static int __intel_set_mode(struct drm_crtc *crtc,
         * to set it here already despite that we pass it down the callchain.
         */
        if (modeset_pipes) {
-               enum transcoder tmp = to_intel_crtc(crtc)->config.cpu_transcoder;
                crtc->mode = *mode;
                /* mode_set/enable/disable functions rely on a correct pipe
                 * config. */
                to_intel_crtc(crtc)->config = *pipe_config;
-               to_intel_crtc(crtc)->config.cpu_transcoder = tmp;
        }
 
        /* Only after disabling all output pipelines that will be changed can we
@@ -8349,12 +8744,6 @@ static int intel_crtc_set_config(struct drm_mode_set *set)
                goto fail;
 
        if (config->mode_changed) {
-               if (set->mode) {
-                       DRM_DEBUG_KMS("attempting to set mode from"
-                                       " userspace\n");
-                       drm_mode_debug_printmodeline(set->mode);
-               }
-
                ret = intel_set_mode(set->crtc, set->mode,
                                     set->x, set->y, set->fb);
        } else if (config->fb_changed) {
@@ -8365,8 +8754,8 @@ static int intel_crtc_set_config(struct drm_mode_set *set)
        }
 
        if (ret) {
-               DRM_ERROR("failed to set mode on [CRTC:%d], err = %d\n",
-                         set->crtc->base.id, ret);
+               DRM_DEBUG_KMS("failed to set mode on [CRTC:%d], err = %d\n",
+                             set->crtc->base.id, ret);
 fail:
                intel_set_config_restore_state(dev, config);
 
@@ -8397,23 +8786,93 @@ static void intel_cpu_pll_init(struct drm_device *dev)
                intel_ddi_pll_init(dev);
 }
 
-static void intel_pch_pll_init(struct drm_device *dev)
+static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv,
+                                     struct intel_shared_dpll *pll,
+                                     struct intel_dpll_hw_state *hw_state)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       int i;
+       uint32_t val;
 
-       if (dev_priv->num_pch_pll == 0) {
-               DRM_DEBUG_KMS("No PCH PLLs on this hardware, skipping initialisation\n");
-               return;
+       val = I915_READ(PCH_DPLL(pll->id));
+       hw_state->dpll = val;
+       hw_state->fp0 = I915_READ(PCH_FP0(pll->id));
+       hw_state->fp1 = I915_READ(PCH_FP1(pll->id));
+
+       return val & DPLL_VCO_ENABLE;
+}
+
+static void ibx_pch_dpll_enable(struct drm_i915_private *dev_priv,
+                               struct intel_shared_dpll *pll)
+{
+       uint32_t reg, val;
+
+       /* PCH refclock must be enabled first */
+       assert_pch_refclk_enabled(dev_priv);
+
+       reg = PCH_DPLL(pll->id);
+       val = I915_READ(reg);
+       val |= DPLL_VCO_ENABLE;
+       I915_WRITE(reg, val);
+       POSTING_READ(reg);
+       udelay(200);
+}
+
+static void ibx_pch_dpll_disable(struct drm_i915_private *dev_priv,
+                                struct intel_shared_dpll *pll)
+{
+       struct drm_device *dev = dev_priv->dev;
+       struct intel_crtc *crtc;
+       uint32_t reg, val;
+
+       /* Make sure no transcoder isn't still depending on us. */
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+               if (intel_crtc_to_shared_dpll(crtc) == pll)
+                       assert_pch_transcoder_disabled(dev_priv, crtc->pipe);
        }
 
-       for (i = 0; i < dev_priv->num_pch_pll; i++) {
-               dev_priv->pch_plls[i].pll_reg = _PCH_DPLL(i);
-               dev_priv->pch_plls[i].fp0_reg = _PCH_FP0(i);
-               dev_priv->pch_plls[i].fp1_reg = _PCH_FP1(i);
+       reg = PCH_DPLL(pll->id);
+       val = I915_READ(reg);
+       val &= ~DPLL_VCO_ENABLE;
+       I915_WRITE(reg, val);
+       POSTING_READ(reg);
+       udelay(200);
+}
+
+static char *ibx_pch_dpll_names[] = {
+       "PCH DPLL A",
+       "PCH DPLL B",
+};
+
+static void ibx_pch_dpll_init(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int i;
+
+       dev_priv->num_shared_dpll = 2;
+
+       for (i = 0; i < dev_priv->num_shared_dpll; i++) {
+               dev_priv->shared_dplls[i].id = i;
+               dev_priv->shared_dplls[i].name = ibx_pch_dpll_names[i];
+               dev_priv->shared_dplls[i].enable = ibx_pch_dpll_enable;
+               dev_priv->shared_dplls[i].disable = ibx_pch_dpll_disable;
+               dev_priv->shared_dplls[i].get_hw_state =
+                       ibx_pch_dpll_get_hw_state;
        }
 }
 
+static void intel_shared_dpll_init(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
+               ibx_pch_dpll_init(dev);
+       else
+               dev_priv->num_shared_dpll = 0;
+
+       BUG_ON(dev_priv->num_shared_dpll > I915_NUM_PLLS);
+       DRM_DEBUG_KMS("%i shared PLLs initialized\n",
+                     dev_priv->num_shared_dpll);
+}
+
 static void intel_crtc_init(struct drm_device *dev, int pipe)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
@@ -8436,7 +8895,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
        /* Swap pipes & planes for FBC on pre-965 */
        intel_crtc->pipe = pipe;
        intel_crtc->plane = pipe;
-       intel_crtc->config.cpu_transcoder = pipe;
        if (IS_MOBILE(dev) && IS_GEN3(dev)) {
                DRM_DEBUG_KMS("swapping pipes & planes for FBC\n");
                intel_crtc->plane = !pipe;
@@ -8519,13 +8977,8 @@ static void intel_setup_outputs(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_encoder *encoder;
        bool dpd_is_edp = false;
-       bool has_lvds;
 
-       has_lvds = intel_lvds_init(dev);
-       if (!has_lvds && !HAS_PCH_SPLIT(dev)) {
-               /* disable the panel fitter on everything but LVDS */
-               I915_WRITE(PFIT_CONTROL, 0);
-       }
+       intel_lvds_init(dev);
 
        if (!IS_ULT(dev))
                intel_crt_init(dev);
@@ -8598,10 +9051,8 @@ static void intel_setup_outputs(struct drm_device *dev)
                                intel_hdmi_init(dev, GEN4_HDMIB, PORT_B);
                        }
 
-                       if (!found && SUPPORTS_INTEGRATED_DP(dev)) {
-                               DRM_DEBUG_KMS("probing DP_B\n");
+                       if (!found && SUPPORTS_INTEGRATED_DP(dev))
                                intel_dp_init(dev, DP_B, PORT_B);
-                       }
                }
 
                /* Before G4X SDVOC doesn't have its own detect register */
@@ -8617,17 +9068,13 @@ static void intel_setup_outputs(struct drm_device *dev)
                                DRM_DEBUG_KMS("probing HDMI on SDVOC\n");
                                intel_hdmi_init(dev, GEN4_HDMIC, PORT_C);
                        }
-                       if (SUPPORTS_INTEGRATED_DP(dev)) {
-                               DRM_DEBUG_KMS("probing DP_C\n");
+                       if (SUPPORTS_INTEGRATED_DP(dev))
                                intel_dp_init(dev, DP_C, PORT_C);
-                       }
                }
 
                if (SUPPORTS_INTEGRATED_DP(dev) &&
-                   (I915_READ(DP_D) & DP_DETECTED)) {
-                       DRM_DEBUG_KMS("probing DP_D\n");
+                   (I915_READ(DP_D) & DP_DETECTED))
                        intel_dp_init(dev, DP_D, PORT_D);
-               }
        } else if (IS_GEN2(dev))
                intel_dvo_init(dev);
 
@@ -8675,6 +9122,7 @@ int intel_framebuffer_init(struct drm_device *dev,
                           struct drm_mode_fb_cmd2 *mode_cmd,
                           struct drm_i915_gem_object *obj)
 {
+       int pitch_limit;
        int ret;
 
        if (obj->tiling_mode == I915_TILING_Y) {
@@ -8688,10 +9136,26 @@ int intel_framebuffer_init(struct drm_device *dev,
                return -EINVAL;
        }
 
-       /* FIXME <= Gen4 stride limits are bit unclear */
-       if (mode_cmd->pitches[0] > 32768) {
-               DRM_DEBUG("pitch (%d) must be at less than 32768\n",
-                         mode_cmd->pitches[0]);
+       if (INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev)) {
+               pitch_limit = 32*1024;
+       } else if (INTEL_INFO(dev)->gen >= 4) {
+               if (obj->tiling_mode)
+                       pitch_limit = 16*1024;
+               else
+                       pitch_limit = 32*1024;
+       } else if (INTEL_INFO(dev)->gen >= 3) {
+               if (obj->tiling_mode)
+                       pitch_limit = 8*1024;
+               else
+                       pitch_limit = 16*1024;
+       } else
+               /* XXX DSPC is limited to 4k tiled */
+               pitch_limit = 8*1024;
+
+       if (mode_cmd->pitches[0] > pitch_limit) {
+               DRM_DEBUG("%s pitch (%d) must be at less than %d\n",
+                         obj->tiling_mode ? "tiled" : "linear",
+                         mode_cmd->pitches[0], pitch_limit);
                return -EINVAL;
        }
 
@@ -8712,7 +9176,8 @@ int intel_framebuffer_init(struct drm_device *dev,
        case DRM_FORMAT_XRGB1555:
        case DRM_FORMAT_ARGB1555:
                if (INTEL_INFO(dev)->gen > 3) {
-                       DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format);
+                       DRM_DEBUG("unsupported pixel format: %s\n",
+                                 drm_get_format_name(mode_cmd->pixel_format));
                        return -EINVAL;
                }
                break;
@@ -8723,7 +9188,8 @@ int intel_framebuffer_init(struct drm_device *dev,
        case DRM_FORMAT_XBGR2101010:
        case DRM_FORMAT_ABGR2101010:
                if (INTEL_INFO(dev)->gen < 4) {
-                       DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format);
+                       DRM_DEBUG("unsupported pixel format: %s\n",
+                                 drm_get_format_name(mode_cmd->pixel_format));
                        return -EINVAL;
                }
                break;
@@ -8732,12 +9198,14 @@ int intel_framebuffer_init(struct drm_device *dev,
        case DRM_FORMAT_YVYU:
        case DRM_FORMAT_VYUY:
                if (INTEL_INFO(dev)->gen < 5) {
-                       DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format);
+                       DRM_DEBUG("unsupported pixel format: %s\n",
+                                 drm_get_format_name(mode_cmd->pixel_format));
                        return -EINVAL;
                }
                break;
        default:
-               DRM_DEBUG("unsupported pixel format 0x%08x\n", mode_cmd->pixel_format);
+               DRM_DEBUG("unsupported pixel format: %s\n",
+                         drm_get_format_name(mode_cmd->pixel_format));
                return -EINVAL;
        }
 
@@ -8782,6 +9250,15 @@ static void intel_init_display(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       if (HAS_PCH_SPLIT(dev) || IS_G4X(dev))
+               dev_priv->display.find_dpll = g4x_find_best_dpll;
+       else if (IS_VALLEYVIEW(dev))
+               dev_priv->display.find_dpll = vlv_find_best_dpll;
+       else if (IS_PINEVIEW(dev))
+               dev_priv->display.find_dpll = pnv_find_best_dpll;
+       else
+               dev_priv->display.find_dpll = i9xx_find_best_dpll;
+
        if (HAS_DDI(dev)) {
                dev_priv->display.get_pipe_config = haswell_get_pipe_config;
                dev_priv->display.crtc_mode_set = haswell_crtc_mode_set;
@@ -8796,6 +9273,13 @@ static void intel_init_display(struct drm_device *dev)
                dev_priv->display.crtc_disable = ironlake_crtc_disable;
                dev_priv->display.off = ironlake_crtc_off;
                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.crtc_mode_set = i9xx_crtc_mode_set;
+               dev_priv->display.crtc_enable = valleyview_crtc_enable;
+               dev_priv->display.crtc_disable = i9xx_crtc_disable;
+               dev_priv->display.off = i9xx_crtc_off;
+               dev_priv->display.update_plane = i9xx_update_plane;
        } else {
                dev_priv->display.get_pipe_config = i9xx_get_pipe_config;
                dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
@@ -9037,6 +9521,11 @@ void intel_modeset_init_hw(struct drm_device *dev)
        mutex_unlock(&dev->struct_mutex);
 }
 
+void intel_modeset_suspend_hw(struct drm_device *dev)
+{
+       intel_suspend_hw(dev);
+}
+
 void intel_modeset_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -9082,13 +9571,13 @@ void intel_modeset_init(struct drm_device *dev)
                for (j = 0; j < dev_priv->num_plane; j++) {
                        ret = intel_plane_init(dev, i, j);
                        if (ret)
-                               DRM_DEBUG_KMS("pipe %d plane %d init failed: %d\n",
-                                             i, j, ret);
+                               DRM_DEBUG_KMS("pipe %c sprite %c init failed: %d\n",
+                                             pipe_name(i), sprite_name(i, j), ret);
                }
        }
 
        intel_cpu_pll_init(dev);
-       intel_pch_pll_init(dev);
+       intel_shared_dpll_init(dev);
 
        /* Just disable it once at startup */
        i915_disable_vga(dev);
@@ -9289,57 +9778,18 @@ void i915_redisable_vga(struct drm_device *dev)
        }
 }
 
-/* Scan out the current hw modeset state, sanitizes it and maps it into the drm
- * and i915 state tracking structures. */
-void intel_modeset_setup_hw_state(struct drm_device *dev,
-                                 bool force_restore)
+static void intel_modeset_readout_hw_state(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        enum pipe pipe;
-       u32 tmp;
-       struct drm_plane *plane;
        struct intel_crtc *crtc;
        struct intel_encoder *encoder;
        struct intel_connector *connector;
+       int i;
 
-       if (HAS_DDI(dev)) {
-               tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP));
-
-               if (tmp & TRANS_DDI_FUNC_ENABLE) {
-                       switch (tmp & TRANS_DDI_EDP_INPUT_MASK) {
-                       case TRANS_DDI_EDP_INPUT_A_ON:
-                       case TRANS_DDI_EDP_INPUT_A_ONOFF:
-                               pipe = PIPE_A;
-                               break;
-                       case TRANS_DDI_EDP_INPUT_B_ONOFF:
-                               pipe = PIPE_B;
-                               break;
-                       case TRANS_DDI_EDP_INPUT_C_ONOFF:
-                               pipe = PIPE_C;
-                               break;
-                       default:
-                               /* A bogus value has been programmed, disable
-                                * the transcoder */
-                               WARN(1, "Bogus eDP source %08x\n", tmp);
-                               intel_ddi_disable_transcoder_func(dev_priv,
-                                               TRANSCODER_EDP);
-                               goto setup_pipes;
-                       }
-
-                       crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
-                       crtc->config.cpu_transcoder = TRANSCODER_EDP;
-
-                       DRM_DEBUG_KMS("Pipe %c using transcoder EDP\n",
-                                     pipe_name(pipe));
-               }
-       }
-
-setup_pipes:
        list_for_each_entry(crtc, &dev->mode_config.crtc_list,
                            base.head) {
-               enum transcoder tmp = crtc->config.cpu_transcoder;
                memset(&crtc->config, 0, sizeof(crtc->config));
-               crtc->config.cpu_transcoder = tmp;
 
                crtc->active = dev_priv->display.get_pipe_config(crtc,
                                                                 &crtc->config);
@@ -9351,16 +9801,35 @@ setup_pipes:
                              crtc->active ? "enabled" : "disabled");
        }
 
+       /* FIXME: Smash this into the new shared dpll infrastructure. */
        if (HAS_DDI(dev))
                intel_ddi_setup_hw_pll_state(dev);
 
+       for (i = 0; i < dev_priv->num_shared_dpll; i++) {
+               struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i];
+
+               pll->on = pll->get_hw_state(dev_priv, pll, &pll->hw_state);
+               pll->active = 0;
+               list_for_each_entry(crtc, &dev->mode_config.crtc_list,
+                                   base.head) {
+                       if (crtc->active && intel_crtc_to_shared_dpll(crtc) == pll)
+                               pll->active++;
+               }
+               pll->refcount = pll->active;
+
+               DRM_DEBUG_KMS("%s hw state readout: refcount %i\n",
+                             pll->name, pll->refcount);
+       }
+
        list_for_each_entry(encoder, &dev->mode_config.encoder_list,
                            base.head) {
                pipe = 0;
 
                if (encoder->get_hw_state(encoder, &pipe)) {
-                       encoder->base.crtc =
-                               dev_priv->pipe_to_crtc_mapping[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);
                } else {
                        encoder->base.crtc = NULL;
                }
@@ -9388,6 +9857,20 @@ setup_pipes:
                              drm_get_connector_name(&connector->base),
                              connector->base.encoder ? "enabled" : "disabled");
        }
+}
+
+/* Scan out the current hw modeset state, sanitizes it and maps it into the drm
+ * and i915 state tracking structures. */
+void intel_modeset_setup_hw_state(struct drm_device *dev,
+                                 bool force_restore)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe;
+       struct drm_plane *plane;
+       struct intel_crtc *crtc;
+       struct intel_encoder *encoder;
+
+       intel_modeset_readout_hw_state(dev);
 
        /* HW state is read out, now we need to sanitize this mess. */
        list_for_each_entry(encoder, &dev->mode_config.encoder_list,
@@ -9398,6 +9881,7 @@ setup_pipes:
        for_each_pipe(pipe) {
                crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
                intel_sanitize_crtc(crtc);
+               intel_dump_pipe_config(crtc, &crtc->config, "[setup_hw_state]");
        }
 
        if (force_restore) {
@@ -9440,12 +9924,23 @@ void intel_modeset_cleanup(struct drm_device *dev)
        struct drm_crtc *crtc;
        struct intel_crtc *intel_crtc;
 
+       /*
+        * Interrupts and polling as the first thing to avoid creating havoc.
+        * Too much stuff here (turning of rps, connectors, ...) would
+        * experience fancy races otherwise.
+        */
+       drm_irq_uninstall(dev);
+       cancel_work_sync(&dev_priv->hotplug_work);
+       /*
+        * Due to the hpd irq storm handling the hotplug work can re-arm the
+        * poll handlers. Hence disable polling after hpd handling is shut down.
+        */
        drm_kms_helper_poll_fini(dev);
+
        mutex_lock(&dev->struct_mutex);
 
        intel_unregister_dsm_handler();
 
-
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                /* Skip inactive CRTCs */
                if (!crtc->fb)
@@ -9461,17 +9956,8 @@ void intel_modeset_cleanup(struct drm_device *dev)
 
        ironlake_teardown_rc6(dev);
 
-       if (IS_VALLEYVIEW(dev))
-               vlv_init_dpio(dev);
-
        mutex_unlock(&dev->struct_mutex);
 
-       /* Disable the irq before mode object teardown, for the irq might
-        * enqueue unpin/hotplug work. */
-       drm_irq_uninstall(dev);
-       cancel_work_sync(&dev_priv->hotplug_work);
-       cancel_work_sync(&dev_priv->rps.work);
-
        /* flush any delayed tasks or pending work */
        flush_scheduled_work();
 
@@ -9520,6 +10006,9 @@ int intel_modeset_vga_set_state(struct drm_device *dev, bool state)
 #include <linux/seq_file.h>
 
 struct intel_display_error_state {
+
+       u32 power_well_driver;
+
        struct intel_cursor_error_state {
                u32 control;
                u32 position;
@@ -9528,6 +10017,7 @@ struct intel_display_error_state {
        } cursor[I915_MAX_PIPES];
 
        struct intel_pipe_error_state {
+               enum transcoder cpu_transcoder;
                u32 conf;
                u32 source;
 
@@ -9562,8 +10052,12 @@ intel_display_capture_error_state(struct drm_device *dev)
        if (error == NULL)
                return NULL;
 
+       if (HAS_POWER_WELL(dev))
+               error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER);
+
        for_each_pipe(i) {
                cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, i);
+               error->pipe[i].cpu_transcoder = cpu_transcoder;
 
                if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) {
                        error->cursor[i].control = I915_READ(CURCNTR(i));
@@ -9598,46 +10092,60 @@ intel_display_capture_error_state(struct drm_device *dev)
                error->pipe[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. */
+       if (HAS_POWER_WELL(dev))
+               I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
+
        return error;
 }
 
+#define err_printf(e, ...) i915_error_printf(e, __VA_ARGS__)
+
 void
-intel_display_print_error_state(struct seq_file *m,
+intel_display_print_error_state(struct drm_i915_error_state_buf *m,
                                struct drm_device *dev,
                                struct intel_display_error_state *error)
 {
        int i;
 
-       seq_printf(m, "Num Pipes: %d\n", INTEL_INFO(dev)->num_pipes);
+       err_printf(m, "Num Pipes: %d\n", INTEL_INFO(dev)->num_pipes);
+       if (HAS_POWER_WELL(dev))
+               err_printf(m, "PWR_WELL_CTL2: %08x\n",
+                          error->power_well_driver);
        for_each_pipe(i) {
-               seq_printf(m, "Pipe [%d]:\n", i);
-               seq_printf(m, "  CONF: %08x\n", error->pipe[i].conf);
-               seq_printf(m, "  SRC: %08x\n", error->pipe[i].source);
-               seq_printf(m, "  HTOTAL: %08x\n", error->pipe[i].htotal);
-               seq_printf(m, "  HBLANK: %08x\n", error->pipe[i].hblank);
-               seq_printf(m, "  HSYNC: %08x\n", error->pipe[i].hsync);
-               seq_printf(m, "  VTOTAL: %08x\n", error->pipe[i].vtotal);
-               seq_printf(m, "  VBLANK: %08x\n", error->pipe[i].vblank);
-               seq_printf(m, "  VSYNC: %08x\n", error->pipe[i].vsync);
-
-               seq_printf(m, "Plane [%d]:\n", i);
-               seq_printf(m, "  CNTR: %08x\n", error->plane[i].control);
-               seq_printf(m, "  STRIDE: %08x\n", error->plane[i].stride);
+               err_printf(m, "Pipe [%d]:\n", i);
+               err_printf(m, "  CPU transcoder: %c\n",
+                          transcoder_name(error->pipe[i].cpu_transcoder));
+               err_printf(m, "  CONF: %08x\n", error->pipe[i].conf);
+               err_printf(m, "  SRC: %08x\n", error->pipe[i].source);
+               err_printf(m, "  HTOTAL: %08x\n", error->pipe[i].htotal);
+               err_printf(m, "  HBLANK: %08x\n", error->pipe[i].hblank);
+               err_printf(m, "  HSYNC: %08x\n", error->pipe[i].hsync);
+               err_printf(m, "  VTOTAL: %08x\n", error->pipe[i].vtotal);
+               err_printf(m, "  VBLANK: %08x\n", error->pipe[i].vblank);
+               err_printf(m, "  VSYNC: %08x\n", error->pipe[i].vsync);
+
+               err_printf(m, "Plane [%d]:\n", i);
+               err_printf(m, "  CNTR: %08x\n", error->plane[i].control);
+               err_printf(m, "  STRIDE: %08x\n", error->plane[i].stride);
                if (INTEL_INFO(dev)->gen <= 3) {
-                       seq_printf(m, "  SIZE: %08x\n", error->plane[i].size);
-                       seq_printf(m, "  POS: %08x\n", error->plane[i].pos);
+                       err_printf(m, "  SIZE: %08x\n", error->plane[i].size);
+                       err_printf(m, "  POS: %08x\n", error->plane[i].pos);
                }
                if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev))
-                       seq_printf(m, "  ADDR: %08x\n", error->plane[i].addr);
+                       err_printf(m, "  ADDR: %08x\n", error->plane[i].addr);
                if (INTEL_INFO(dev)->gen >= 4) {
-                       seq_printf(m, "  SURF: %08x\n", error->plane[i].surface);
-                       seq_printf(m, "  TILEOFF: %08x\n", error->plane[i].tile_offset);
+                       err_printf(m, "  SURF: %08x\n", error->plane[i].surface);
+                       err_printf(m, "  TILEOFF: %08x\n", error->plane[i].tile_offset);
                }
 
-               seq_printf(m, "Cursor [%d]:\n", i);
-               seq_printf(m, "  CNTR: %08x\n", error->cursor[i].control);
-               seq_printf(m, "  POS: %08x\n", error->cursor[i].position);
-               seq_printf(m, "  BASE: %08x\n", error->cursor[i].base);
+               err_printf(m, "Cursor [%d]:\n", i);
+               err_printf(m, "  CNTR: %08x\n", error->cursor[i].control);
+               err_printf(m, "  POS: %08x\n", error->cursor[i].position);
+               err_printf(m, "  BASE: %08x\n", error->cursor[i].base);
        }
 }
 #endif
index 70789b1..b739712 100644 (file)
@@ -52,30 +52,6 @@ static bool is_edp(struct intel_dp *intel_dp)
        return intel_dig_port->base.type == INTEL_OUTPUT_EDP;
 }
 
-/**
- * is_pch_edp - is the port on the PCH and attached to an eDP panel?
- * @intel_dp: DP struct
- *
- * Returns true if the given DP struct corresponds to a PCH DP port attached
- * to an eDP panel, false otherwise.  Helpful for determining whether we
- * may need FDI resources for a given DP output or not.
- */
-static bool is_pch_edp(struct intel_dp *intel_dp)
-{
-       return intel_dp->is_pch_edp;
-}
-
-/**
- * is_cpu_edp - is the port on the CPU and attached to an eDP panel?
- * @intel_dp: DP struct
- *
- * Returns true if the given DP struct corresponds to a CPU eDP port.
- */
-static bool is_cpu_edp(struct intel_dp *intel_dp)
-{
-       return is_edp(intel_dp) && !is_pch_edp(intel_dp);
-}
-
 static struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
 {
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -88,25 +64,6 @@ static struct intel_dp *intel_attached_dp(struct drm_connector *connector)
        return enc_to_intel_dp(&intel_attached_encoder(connector)->base);
 }
 
-/**
- * intel_encoder_is_pch_edp - is the given encoder a PCH attached eDP?
- * @encoder: DRM encoder
- *
- * Return true if @encoder corresponds to a PCH attached eDP panel.  Needed
- * by intel_display.c.
- */
-bool intel_encoder_is_pch_edp(struct drm_encoder *encoder)
-{
-       struct intel_dp *intel_dp;
-
-       if (!encoder)
-               return false;
-
-       intel_dp = enc_to_intel_dp(encoder);
-
-       return is_pch_edp(intel_dp);
-}
-
 static void intel_dp_link_down(struct intel_dp *intel_dp);
 
 static int
@@ -344,11 +301,12 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
         * Note that PCH attached eDP panels should use a 125MHz input
         * clock divider.
         */
-       if (is_cpu_edp(intel_dp)) {
+       if (IS_VALLEYVIEW(dev)) {
+               aux_clock_divider = 100;
+       } else if (intel_dig_port->port == PORT_A) {
                if (HAS_DDI(dev))
-                       aux_clock_divider = intel_ddi_get_cdclk_freq(dev_priv) >> 1;
-               else if (IS_VALLEYVIEW(dev))
-                       aux_clock_divider = 100;
+                       aux_clock_divider = DIV_ROUND_CLOSEST(
+                               intel_ddi_get_cdclk_freq(dev_priv), 2000);
                else if (IS_GEN6(dev) || IS_GEN7(dev))
                        aux_clock_divider = 200; /* SNB & IVB eDP input clock at 400Mhz */
                else
@@ -660,6 +618,49 @@ intel_dp_i2c_init(struct intel_dp *intel_dp,
        return ret;
 }
 
+static void
+intel_dp_set_clock(struct intel_encoder *encoder,
+                  struct intel_crtc_config *pipe_config, int link_bw)
+{
+       struct drm_device *dev = encoder->base.dev;
+
+       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;
+       } 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;
+       } else if (IS_VALLEYVIEW(dev)) {
+               /* FIXME: Need to figure out optimized DP clocks for vlv. */
+       }
+}
+
 bool
 intel_dp_compute_config(struct intel_encoder *encoder,
                        struct intel_crtc_config *pipe_config)
@@ -667,17 +668,18 @@ intel_dp_compute_config(struct intel_encoder *encoder,
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
-       struct drm_display_mode *mode = &pipe_config->requested_mode;
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+       enum port port = dp_to_dig_port(intel_dp)->port;
+       struct intel_crtc *intel_crtc = encoder->new_crtc;
        struct intel_connector *intel_connector = intel_dp->attached_connector;
        int lane_count, clock;
        int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
        int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0;
        int bpp, mode_rate;
        static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 };
-       int target_clock, link_avail, link_clock;
+       int link_avail, link_clock;
 
-       if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev) && !is_cpu_edp(intel_dp))
+       if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev) && port != PORT_A)
                pipe_config->has_pch_encoder = true;
 
        pipe_config->has_dp_encoder = true;
@@ -685,12 +687,13 @@ intel_dp_compute_config(struct intel_encoder *encoder,
        if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) {
                intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
                                       adjusted_mode);
-               intel_pch_panel_fitting(dev,
-                                       intel_connector->panel.fitting_mode,
-                                       mode, adjusted_mode);
+               if (!HAS_PCH_SPLIT(dev))
+                       intel_gmch_panel_fitting(intel_crtc, pipe_config,
+                                                intel_connector->panel.fitting_mode);
+               else
+                       intel_pch_panel_fitting(intel_crtc, pipe_config,
+                                               intel_connector->panel.fitting_mode);
        }
-       /* We need to take the panel's fixed mode into account. */
-       target_clock = adjusted_mode->clock;
 
        if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK)
                return false;
@@ -701,12 +704,12 @@ intel_dp_compute_config(struct intel_encoder *encoder,
 
        /* Walk through all bpp values. Luckily they're all nicely spaced with 2
         * bpc in between. */
-       bpp = min_t(int, 8*3, pipe_config->pipe_bpp);
-       if (is_edp(intel_dp) && dev_priv->edp.bpp)
-               bpp = min_t(int, bpp, dev_priv->edp.bpp);
+       bpp = pipe_config->pipe_bpp;
+       if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp)
+               bpp = min_t(int, bpp, dev_priv->vbt.edp_bpp);
 
        for (; bpp >= 6*3; bpp -= 2*3) {
-               mode_rate = intel_dp_link_required(target_clock, bpp);
+               mode_rate = intel_dp_link_required(adjusted_mode->clock, bpp);
 
                for (clock = 0; clock <= max_clock; clock++) {
                        for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
@@ -741,20 +744,21 @@ found:
 
        intel_dp->link_bw = bws[clock];
        intel_dp->lane_count = lane_count;
-       adjusted_mode->clock = drm_dp_bw_code_to_link_rate(intel_dp->link_bw);
        pipe_config->pipe_bpp = bpp;
-       pipe_config->pixel_target_clock = target_clock;
+       pipe_config->port_clock = drm_dp_bw_code_to_link_rate(intel_dp->link_bw);
 
        DRM_DEBUG_KMS("DP link bw %02x lane count %d clock %d bpp %d\n",
                      intel_dp->link_bw, intel_dp->lane_count,
-                     adjusted_mode->clock, bpp);
+                     pipe_config->port_clock, bpp);
        DRM_DEBUG_KMS("DP link bw required %i available %i\n",
                      mode_rate, link_avail);
 
        intel_link_compute_m_n(bpp, lane_count,
-                              target_clock, adjusted_mode->clock,
+                              adjusted_mode->clock, pipe_config->port_clock,
                               &pipe_config->dp_m_n);
 
+       intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw);
+
        return true;
 }
 
@@ -773,24 +777,28 @@ void intel_dp_init_link_config(struct intel_dp *intel_dp)
        }
 }
 
-static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock)
+static void ironlake_set_pll_cpu_edp(struct intel_dp *intel_dp)
 {
-       struct drm_device *dev = crtc->dev;
+       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+       struct intel_crtc *crtc = to_intel_crtc(dig_port->base.base.crtc);
+       struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 dpa_ctl;
 
-       DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock);
+       DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", crtc->config.port_clock);
        dpa_ctl = I915_READ(DP_A);
        dpa_ctl &= ~DP_PLL_FREQ_MASK;
 
-       if (clock < 200000) {
+       if (crtc->config.port_clock == 162000) {
                /* For a long time we've carried around a ILK-DevA w/a for the
                 * 160MHz clock. If we're really unlucky, it's still required.
                 */
                DRM_DEBUG_KMS("160MHz cpu eDP clock, might need ilk devA w/a\n");
                dpa_ctl |= DP_PLL_FREQ_160MHZ;
+               intel_dp->DP |= DP_PLL_FREQ_160MHZ;
        } else {
                dpa_ctl |= DP_PLL_FREQ_270MHZ;
+               intel_dp->DP |= DP_PLL_FREQ_270MHZ;
        }
 
        I915_WRITE(DP_A, dpa_ctl);
@@ -806,8 +814,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
        struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
-       struct drm_crtc *crtc = encoder->crtc;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       enum port port = dp_to_dig_port(intel_dp)->port;
+       struct intel_crtc *crtc = to_intel_crtc(encoder->crtc);
 
        /*
         * There are four kinds of DP registers:
@@ -833,21 +841,11 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
 
        /* Handle DP bits in common between all three register formats */
        intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
+       intel_dp->DP |= DP_PORT_WIDTH(intel_dp->lane_count);
 
-       switch (intel_dp->lane_count) {
-       case 1:
-               intel_dp->DP |= DP_PORT_WIDTH_1;
-               break;
-       case 2:
-               intel_dp->DP |= DP_PORT_WIDTH_2;
-               break;
-       case 4:
-               intel_dp->DP |= DP_PORT_WIDTH_4;
-               break;
-       }
        if (intel_dp->has_audio) {
                DRM_DEBUG_DRIVER("Enabling DP audio on pipe %c\n",
-                                pipe_name(intel_crtc->pipe));
+                                pipe_name(crtc->pipe));
                intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE;
                intel_write_eld(encoder, adjusted_mode);
        }
@@ -856,7 +854,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
 
        /* Split out the IBX/CPU vs CPT settings */
 
-       if (is_cpu_edp(intel_dp) && IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) {
+       if (port == PORT_A && IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) {
                if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
                        intel_dp->DP |= DP_SYNC_HS_HIGH;
                if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
@@ -866,14 +864,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
                if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN)
                        intel_dp->DP |= DP_ENHANCED_FRAMING;
 
-               intel_dp->DP |= intel_crtc->pipe << 29;
-
-               /* don't miss out required setting for eDP */
-               if (adjusted_mode->clock < 200000)
-                       intel_dp->DP |= DP_PLL_FREQ_160MHZ;
-               else
-                       intel_dp->DP |= DP_PLL_FREQ_270MHZ;
-       } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) {
+               intel_dp->DP |= crtc->pipe << 29;
+       } else if (!HAS_PCH_CPT(dev) || port == PORT_A) {
                if (!HAS_PCH_SPLIT(dev) && !IS_VALLEYVIEW(dev))
                        intel_dp->DP |= intel_dp->color_range;
 
@@ -886,22 +878,14 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
                if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN)
                        intel_dp->DP |= DP_ENHANCED_FRAMING;
 
-               if (intel_crtc->pipe == 1)
+               if (crtc->pipe == 1)
                        intel_dp->DP |= DP_PIPEB_SELECT;
-
-               if (is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) {
-                       /* don't miss out required setting for eDP */
-                       if (adjusted_mode->clock < 200000)
-                               intel_dp->DP |= DP_PLL_FREQ_160MHZ;
-                       else
-                               intel_dp->DP |= DP_PLL_FREQ_270MHZ;
-               }
        } else {
                intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
        }
 
-       if (is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev))
-               ironlake_set_pll_edp(crtc, adjusted_mode->clock);
+       if (port == PORT_A && !IS_VALLEYVIEW(dev))
+               ironlake_set_pll_cpu_edp(intel_dp);
 }
 
 #define IDLE_ON_MASK           (PP_ON | 0        | PP_SEQUENCE_MASK | 0                     | PP_SEQUENCE_STATE_MASK)
@@ -1290,6 +1274,7 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
                                  enum pipe *pipe)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+       enum port port = dp_to_dig_port(intel_dp)->port;
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 tmp = I915_READ(intel_dp->output_reg);
@@ -1297,9 +1282,9 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
        if (!(tmp & DP_PORT_EN))
                return false;
 
-       if (is_cpu_edp(intel_dp) && IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) {
+       if (port == PORT_A && IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) {
                *pipe = PORT_TO_PIPE_CPT(tmp);
-       } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) {
+       } else if (!HAS_PCH_CPT(dev) || port == PORT_A) {
                *pipe = PORT_TO_PIPE(tmp);
        } else {
                u32 trans_sel;
@@ -1335,9 +1320,48 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
        return true;
 }
 
+static void intel_dp_get_config(struct intel_encoder *encoder,
+                               struct intel_crtc_config *pipe_config)
+{
+       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+       u32 tmp, flags = 0;
+       struct drm_device *dev = encoder->base.dev;
+       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);
+
+       if ((port == PORT_A) || !HAS_PCH_CPT(dev)) {
+               tmp = I915_READ(intel_dp->output_reg);
+               if (tmp & DP_SYNC_HS_HIGH)
+                       flags |= DRM_MODE_FLAG_PHSYNC;
+               else
+                       flags |= DRM_MODE_FLAG_NHSYNC;
+
+               if (tmp & DP_SYNC_VS_HIGH)
+                       flags |= DRM_MODE_FLAG_PVSYNC;
+               else
+                       flags |= DRM_MODE_FLAG_NVSYNC;
+       } else {
+               tmp = I915_READ(TRANS_DP_CTL(crtc->pipe));
+               if (tmp & TRANS_DP_HSYNC_ACTIVE_HIGH)
+                       flags |= DRM_MODE_FLAG_PHSYNC;
+               else
+                       flags |= DRM_MODE_FLAG_NHSYNC;
+
+               if (tmp & TRANS_DP_VSYNC_ACTIVE_HIGH)
+                       flags |= DRM_MODE_FLAG_PVSYNC;
+               else
+                       flags |= DRM_MODE_FLAG_NVSYNC;
+       }
+
+       pipe_config->adjusted_mode.flags |= flags;
+}
+
 static void intel_disable_dp(struct intel_encoder *encoder)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+       enum port port = dp_to_dig_port(intel_dp)->port;
+       struct drm_device *dev = encoder->base.dev;
 
        /* Make sure the panel is off before trying to change the mode. But also
         * ensure that we have vdd while we switch off the panel. */
@@ -1347,16 +1371,17 @@ static void intel_disable_dp(struct intel_encoder *encoder)
        ironlake_edp_panel_off(intel_dp);
 
        /* cpu edp my only be disable _after_ the cpu pipe/plane is disabled. */
-       if (!is_cpu_edp(intel_dp))
+       if (!(port == PORT_A || IS_VALLEYVIEW(dev)))
                intel_dp_link_down(intel_dp);
 }
 
 static void intel_post_disable_dp(struct intel_encoder *encoder)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+       enum port port = dp_to_dig_port(intel_dp)->port;
        struct drm_device *dev = encoder->base.dev;
 
-       if (is_cpu_edp(intel_dp)) {
+       if (port == PORT_A || IS_VALLEYVIEW(dev)) {
                intel_dp_link_down(intel_dp);
                if (!IS_VALLEYVIEW(dev))
                        ironlake_edp_pll_off(intel_dp);
@@ -1381,15 +1406,73 @@ static void intel_enable_dp(struct intel_encoder *encoder)
        intel_dp_complete_link_train(intel_dp);
        intel_dp_stop_link_train(intel_dp);
        ironlake_edp_backlight_on(intel_dp);
+
+       if (IS_VALLEYVIEW(dev)) {
+               struct intel_digital_port *dport =
+                       enc_to_dig_port(&encoder->base);
+               int channel = vlv_dport_to_channel(dport);
+
+               vlv_wait_port_ready(dev_priv, channel);
+       }
 }
 
 static void intel_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);
        struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev))
+       if (dport->port == PORT_A && !IS_VALLEYVIEW(dev))
                ironlake_edp_pll_on(intel_dp);
+
+       if (IS_VALLEYVIEW(dev)) {
+               struct intel_crtc *intel_crtc =
+                       to_intel_crtc(encoder->base.crtc);
+               int port = vlv_dport_to_channel(dport);
+               int pipe = intel_crtc->pipe;
+               u32 val;
+
+               val = vlv_dpio_read(dev_priv, 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);
+       }
+}
+
+static void intel_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;
+       int port = vlv_dport_to_channel(dport);
+
+       if (!IS_VALLEYVIEW(dev))
+               return;
+
+       /* Program Tx lane resets to default */
+       vlv_dpio_write(dev_priv, DPIO_PCS_TX(port),
+                        DPIO_PCS_TX_LANE2_RESET |
+                        DPIO_PCS_TX_LANE1_RESET);
+       vlv_dpio_write(dev_priv, 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);
 }
 
 /*
@@ -1451,10 +1534,13 @@ static uint8_t
 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_GEN7(dev) && is_cpu_edp(intel_dp))
+       if (IS_VALLEYVIEW(dev))
+               return DP_TRAIN_VOLTAGE_SWING_1200;
+       else if (IS_GEN7(dev) && port == PORT_A)
                return DP_TRAIN_VOLTAGE_SWING_800;
-       else if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp))
+       else if (HAS_PCH_CPT(dev) && port != PORT_A)
                return DP_TRAIN_VOLTAGE_SWING_1200;
        else
                return DP_TRAIN_VOLTAGE_SWING_800;
@@ -1464,6 +1550,7 @@ static uint8_t
 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)) {
                switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
@@ -1477,7 +1564,19 @@ intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
                default:
                        return DP_TRAIN_PRE_EMPHASIS_0;
                }
-       } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) {
+       } else if (IS_VALLEYVIEW(dev)) {
+               switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+               case DP_TRAIN_VOLTAGE_SWING_400:
+                       return DP_TRAIN_PRE_EMPHASIS_9_5;
+               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_GEN7(dev) && port == PORT_A) {
                switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
                case DP_TRAIN_VOLTAGE_SWING_400:
                        return DP_TRAIN_PRE_EMPHASIS_6;
@@ -1502,6 +1601,101 @@ intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
        }
 }
 
+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);
+       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);
+
+       switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
+       case DP_TRAIN_PRE_EMPHASIS_0:
+               preemph_reg_value = 0x0004000;
+               switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+               case DP_TRAIN_VOLTAGE_SWING_400:
+                       demph_reg_value = 0x2B405555;
+                       uniqtranscale_reg_value = 0x552AB83A;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_600:
+                       demph_reg_value = 0x2B404040;
+                       uniqtranscale_reg_value = 0x5548B83A;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_800:
+                       demph_reg_value = 0x2B245555;
+                       uniqtranscale_reg_value = 0x5560B83A;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_1200:
+                       demph_reg_value = 0x2B405555;
+                       uniqtranscale_reg_value = 0x5598DA3A;
+                       break;
+               default:
+                       return 0;
+               }
+               break;
+       case DP_TRAIN_PRE_EMPHASIS_3_5:
+               preemph_reg_value = 0x0002000;
+               switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+               case DP_TRAIN_VOLTAGE_SWING_400:
+                       demph_reg_value = 0x2B404040;
+                       uniqtranscale_reg_value = 0x5552B83A;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_600:
+                       demph_reg_value = 0x2B404848;
+                       uniqtranscale_reg_value = 0x5580B83A;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_800:
+                       demph_reg_value = 0x2B404040;
+                       uniqtranscale_reg_value = 0x55ADDA3A;
+                       break;
+               default:
+                       return 0;
+               }
+               break;
+       case DP_TRAIN_PRE_EMPHASIS_6:
+               preemph_reg_value = 0x0000000;
+               switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+               case DP_TRAIN_VOLTAGE_SWING_400:
+                       demph_reg_value = 0x2B305555;
+                       uniqtranscale_reg_value = 0x5570B83A;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_600:
+                       demph_reg_value = 0x2B2B4040;
+                       uniqtranscale_reg_value = 0x55ADDA3A;
+                       break;
+               default:
+                       return 0;
+               }
+               break;
+       case DP_TRAIN_PRE_EMPHASIS_9_5:
+               preemph_reg_value = 0x0006000;
+               switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+               case DP_TRAIN_VOLTAGE_SWING_400:
+                       demph_reg_value = 0x1B405555;
+                       uniqtranscale_reg_value = 0x55ADDA3A;
+                       break;
+               default:
+                       return 0;
+               }
+               break;
+       default:
+               return 0;
+       }
+
+       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),
+                        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);
+
+       return 0;
+}
+
 static void
 intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
 {
@@ -1669,6 +1863,7 @@ static void
 intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
 {
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       enum port port = intel_dig_port->port;
        struct drm_device *dev = intel_dig_port->base.base.dev;
        uint32_t signal_levels, mask;
        uint8_t train_set = intel_dp->train_set[0];
@@ -1676,10 +1871,13 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
        if (HAS_DDI(dev)) {
                signal_levels = intel_hsw_signal_levels(train_set);
                mask = DDI_BUF_EMP_MASK;
-       } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) {
+       } else if (IS_VALLEYVIEW(dev)) {
+               signal_levels = intel_vlv_signal_levels(intel_dp);
+               mask = 0;
+       } else if (IS_GEN7(dev) && port == PORT_A) {
                signal_levels = intel_gen7_edp_signal_levels(train_set);
                mask = EDP_LINK_TRAIN_VOL_EMP_MASK_IVB;
-       } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) {
+       } else if (IS_GEN6(dev) && port == PORT_A) {
                signal_levels = intel_gen6_edp_signal_levels(train_set);
                mask = EDP_LINK_TRAIN_VOL_EMP_MASK_SNB;
        } else {
@@ -1729,8 +1927,7 @@ 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) || !is_cpu_edp(intel_dp))) {
+       } else if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || port != PORT_A)) {
                dp_reg_value &= ~DP_LINK_TRAIN_MASK_CPT;
 
                switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
@@ -1981,6 +2178,7 @@ static void
 intel_dp_link_down(struct intel_dp *intel_dp)
 {
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       enum port port = intel_dig_port->port;
        struct drm_device *dev = intel_dig_port->base.base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc =
@@ -2010,7 +2208,7 @@ intel_dp_link_down(struct intel_dp *intel_dp)
 
        DRM_DEBUG_KMS("\n");
 
-       if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) {
+       if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || port != PORT_A)) {
                DP &= ~DP_LINK_TRAIN_MASK_CPT;
                I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT);
        } else {
@@ -2301,11 +2499,10 @@ intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
                        return NULL;
 
                size = (intel_connector->edid->extensions + 1) * EDID_LENGTH;
-               edid = kmalloc(size, GFP_KERNEL);
+               edid = kmemdup(intel_connector->edid, size, GFP_KERNEL);
                if (!edid)
                        return NULL;
 
-               memcpy(edid, intel_connector->edid, size);
                return edid;
        }
 
@@ -2499,15 +2696,16 @@ done:
 }
 
 static void
-intel_dp_destroy(struct drm_connector *connector)
+intel_dp_connector_destroy(struct drm_connector *connector)
 {
-       struct intel_dp *intel_dp = intel_attached_dp(connector);
        struct intel_connector *intel_connector = to_intel_connector(connector);
 
        if (!IS_ERR_OR_NULL(intel_connector->edid))
                kfree(intel_connector->edid);
 
-       if (is_edp(intel_dp))
+       /* Can't call is_edp() since the encoder may have been destroyed
+        * already. */
+       if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
                intel_panel_fini(&intel_connector->panel);
 
        drm_sysfs_connector_remove(connector);
@@ -2541,7 +2739,7 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = {
        .detect = intel_dp_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = intel_dp_set_property,
-       .destroy = intel_dp_destroy,
+       .destroy = intel_dp_connector_destroy,
 };
 
 static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = {
@@ -2588,11 +2786,11 @@ bool intel_dpd_is_edp(struct drm_device *dev)
        struct child_device_config *p_child;
        int i;
 
-       if (!dev_priv->child_dev_num)
+       if (!dev_priv->vbt.child_dev_num)
                return false;
 
-       for (i = 0; i < dev_priv->child_dev_num; i++) {
-               p_child = dev_priv->child_dev + i;
+       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)
@@ -2670,7 +2868,7 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev,
        DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
                      cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12);
 
-       vbt = dev_priv->edp.pps;
+       vbt = dev_priv->vbt.edp_pps;
 
        /* Upper limits from eDP 1.3 spec. Note that we use the clunky units of
         * our hw here, which are all in 100usec. */
@@ -2738,9 +2936,6 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
                pp_div_reg = PIPEA_PP_DIVISOR;
        }
 
-       if (IS_VALLEYVIEW(dev))
-               port_sel = I915_READ(pp_on_reg) & 0xc0000000;
-
        /* And finally store the new values in the power sequencer. */
        pp_on = (seq->t1_t3 << PANEL_POWER_UP_DELAY_SHIFT) |
                (seq->t8 << PANEL_LIGHT_ON_DELAY_SHIFT);
@@ -2754,8 +2949,10 @@ 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 (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) {
-               if (is_cpu_edp(intel_dp))
+       if (IS_VALLEYVIEW(dev)) {
+               port_sel = I915_READ(pp_on_reg) & 0xc0000000;
+       } 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;
                else
                        port_sel = PANEL_POWER_PORT_DP_D;
@@ -2773,7 +2970,85 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
                      I915_READ(pp_div_reg));
 }
 
-void
+static bool intel_edp_init_connector(struct intel_dp *intel_dp,
+                                    struct intel_connector *intel_connector)
+{
+       struct drm_connector *connector = &intel_connector->base;
+       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;
+       struct drm_display_mode *fixed_mode = NULL;
+       struct edp_power_seq power_seq = { 0 };
+       bool has_dpcd;
+       struct drm_display_mode *scan;
+       struct edid *edid;
+
+       if (!is_edp(intel_dp))
+               return true;
+
+       intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
+
+       /* Cache DPCD and EDID for edp. */
+       ironlake_edp_panel_vdd_on(intel_dp);
+       has_dpcd = intel_dp_get_dpcd(intel_dp);
+       ironlake_edp_panel_vdd_off(intel_dp, false);
+
+       if (has_dpcd) {
+               if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
+                       dev_priv->no_aux_handshake =
+                               intel_dp->dpcd[DP_MAX_DOWNSPREAD] &
+                               DP_NO_AUX_HANDSHAKE_LINK_TRAINING;
+       } else {
+               /* if this fails, presume the device is a ghost */
+               DRM_INFO("failed to retrieve link info, disabling eDP\n");
+               return false;
+       }
+
+       /* We now know it's not a ghost, init power sequence regs. */
+       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)) {
+                       drm_mode_connector_update_edid_property(connector,
+                                                               edid);
+                       drm_edid_to_eld(connector, edid);
+               } else {
+                       kfree(edid);
+                       edid = ERR_PTR(-EINVAL);
+               }
+       } else {
+               edid = ERR_PTR(-ENOENT);
+       }
+       intel_connector->edid = edid;
+
+       /* prefer fixed mode from EDID if available */
+       list_for_each_entry(scan, &connector->probed_modes, head) {
+               if ((scan->type & DRM_MODE_TYPE_PREFERRED)) {
+                       fixed_mode = drm_mode_duplicate(dev, scan);
+                       break;
+               }
+       }
+
+       /* fallback to VBT if available for eDP */
+       if (!fixed_mode && dev_priv->vbt.lfp_lvds_vbt_mode) {
+               fixed_mode = drm_mode_duplicate(dev,
+                                       dev_priv->vbt.lfp_lvds_vbt_mode);
+               if (fixed_mode)
+                       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);
+
+       return true;
+}
+
+bool
 intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
                        struct intel_connector *intel_connector)
 {
@@ -2782,38 +3057,47 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
        struct intel_encoder *intel_encoder = &intel_dig_port->base;
        struct drm_device *dev = intel_encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_display_mode *fixed_mode = NULL;
-       struct edp_power_seq power_seq = { 0 };
        enum port port = intel_dig_port->port;
        const char *name = NULL;
-       int type;
+       int type, error;
 
        /* Preserve the current hw state. */
        intel_dp->DP = I915_READ(intel_dp->output_reg);
        intel_dp->attached_connector = intel_connector;
 
-       if (HAS_PCH_SPLIT(dev) && port == PORT_D)
-               if (intel_dpd_is_edp(dev))
-                       intel_dp->is_pch_edp = true;
-
+       type = DRM_MODE_CONNECTOR_DisplayPort;
        /*
         * FIXME : We need to initialize built-in panels before external panels.
         * For X0, DP_C is fixed as eDP. Revisit this as part of VLV eDP cleanup
         */
-       if (IS_VALLEYVIEW(dev) && port == PORT_C) {
-               type = DRM_MODE_CONNECTOR_eDP;
-               intel_encoder->type = INTEL_OUTPUT_EDP;
-       } else if (port == PORT_A || is_pch_edp(intel_dp)) {
+       switch (port) {
+       case PORT_A:
                type = DRM_MODE_CONNECTOR_eDP;
-               intel_encoder->type = INTEL_OUTPUT_EDP;
-       } else {
-               /* The intel_encoder->type value may be INTEL_OUTPUT_UNKNOWN for
-                * DDI or INTEL_OUTPUT_DISPLAYPORT for the older gens, so don't
-                * rewrite it.
-                */
-               type = DRM_MODE_CONNECTOR_DisplayPort;
+               break;
+       case PORT_C:
+               if (IS_VALLEYVIEW(dev))
+                       type = DRM_MODE_CONNECTOR_eDP;
+               break;
+       case PORT_D:
+               if (HAS_PCH_SPLIT(dev) && intel_dpd_is_edp(dev))
+                       type = DRM_MODE_CONNECTOR_eDP;
+               break;
+       default:        /* silence GCC warning */
+               break;
        }
 
+       /*
+        * For eDP we always set the encoder type to INTEL_OUTPUT_EDP, but
+        * for DP the encoder type can be set by the caller to
+        * INTEL_OUTPUT_UNKNOWN for DDI, so don't rewrite it.
+        */
+       if (type == DRM_MODE_CONNECTOR_eDP)
+               intel_encoder->type = INTEL_OUTPUT_EDP;
+
+       DRM_DEBUG_KMS("Adding %s connector on port %c\n",
+                       type == DRM_MODE_CONNECTOR_eDP ? "eDP" : "DP",
+                       port_name(port));
+
        drm_connector_init(dev, connector, &intel_dp_connector_funcs, type);
        drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs);
 
@@ -2873,74 +3157,21 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
                BUG();
        }
 
-       if (is_edp(intel_dp))
-               intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
-
-       intel_dp_i2c_init(intel_dp, intel_connector, name);
-
-       /* Cache DPCD and EDID for edp. */
-       if (is_edp(intel_dp)) {
-               bool ret;
-               struct drm_display_mode *scan;
-               struct edid *edid;
-
-               ironlake_edp_panel_vdd_on(intel_dp);
-               ret = intel_dp_get_dpcd(intel_dp);
-               ironlake_edp_panel_vdd_off(intel_dp, false);
+       error = intel_dp_i2c_init(intel_dp, intel_connector, name);
+       WARN(error, "intel_dp_i2c_init failed with error %d for port %c\n",
+            error, port_name(port));
 
-               if (ret) {
-                       if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
-                               dev_priv->no_aux_handshake =
-                                       intel_dp->dpcd[DP_MAX_DOWNSPREAD] &
-                                       DP_NO_AUX_HANDSHAKE_LINK_TRAINING;
-               } else {
-                       /* if this fails, presume the device is a ghost */
-                       DRM_INFO("failed to retrieve link info, disabling eDP\n");
-                       intel_dp_encoder_destroy(&intel_encoder->base);
-                       intel_dp_destroy(connector);
-                       return;
-               }
-
-               /* We now know it's not a ghost, init power sequence regs. */
-               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)) {
-                               drm_mode_connector_update_edid_property(connector, edid);
-                               drm_edid_to_eld(connector, edid);
-                       } else {
-                               kfree(edid);
-                               edid = ERR_PTR(-EINVAL);
-                       }
-               } else {
-                       edid = ERR_PTR(-ENOENT);
+       if (!intel_edp_init_connector(intel_dp, intel_connector)) {
+               i2c_del_adapter(&intel_dp->adapter);
+               if (is_edp(intel_dp)) {
+                       cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
+                       mutex_lock(&dev->mode_config.mutex);
+                       ironlake_panel_vdd_off_sync(intel_dp);
+                       mutex_unlock(&dev->mode_config.mutex);
                }
-               intel_connector->edid = edid;
-
-               /* prefer fixed mode from EDID if available */
-               list_for_each_entry(scan, &connector->probed_modes, head) {
-                       if ((scan->type & DRM_MODE_TYPE_PREFERRED)) {
-                               fixed_mode = drm_mode_duplicate(dev, scan);
-                               break;
-                       }
-               }
-
-               /* fallback to VBT if available for eDP */
-               if (!fixed_mode && dev_priv->lfp_lvds_vbt_mode) {
-                       fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
-                       if (fixed_mode)
-                               fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
-               }
-
-               ironlake_edp_panel_vdd_off(intel_dp, false);
-       }
-
-       if (is_edp(intel_dp)) {
-               intel_panel_init(&intel_connector->panel, fixed_mode);
-               intel_panel_setup_backlight(connector);
+               drm_sysfs_connector_remove(connector);
+               drm_connector_cleanup(connector);
+               return false;
        }
 
        intel_dp_add_properties(intel_dp, connector);
@@ -2953,6 +3184,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
                u32 temp = I915_READ(PEG_BAND_GAP_DATA);
                I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
        }
+
+       return true;
 }
 
 void
@@ -2986,6 +3219,9 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
        intel_encoder->disable = intel_disable_dp;
        intel_encoder->post_disable = intel_post_disable_dp;
        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_dig_port->port = port;
        intel_dig_port->dp.output_reg = output_reg;
@@ -2995,5 +3231,9 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
        intel_encoder->cloneable = false;
        intel_encoder->hot_plug = intel_dp_hot_plug;
 
-       intel_dp_init_connector(intel_dig_port, intel_connector);
+       if (!intel_dp_init_connector(intel_dig_port, intel_connector)) {
+               drm_encoder_cleanup(encoder);
+               kfree(intel_dig_port);
+               kfree(intel_connector);
+       }
 }
index 624a9e6..c8c9b6f 100644 (file)
@@ -120,7 +120,6 @@ struct intel_encoder {
        struct intel_crtc *new_crtc;
 
        int type;
-       bool needs_tv_clock;
        /*
         * Intel hw has only one MUX where encoders could be clone, hence a
         * simple flag is enough to compute the possible_clones mask.
@@ -140,6 +139,12 @@ struct intel_encoder {
         * the encoder is active. If the encoder is enabled it also set the pipe
         * it is connected to in the pipe parameter. */
        bool (*get_hw_state)(struct intel_encoder *, enum pipe *pipe);
+       /* Reconstructs the equivalent mode flags for the current hardware
+        * state. This must be called _after_ display->get_pipe_config has
+        * pre-filled the pipe config. Note that intel_encoder->base.crtc must
+        * be set correctly before calling this function. */
+       void (*get_config)(struct intel_encoder *,
+                          struct intel_crtc_config *pipe_config);
        int crtc_mask;
        enum hpd_pin hpd_pin;
 };
@@ -177,7 +182,30 @@ struct intel_connector {
        u8 polled;
 };
 
+typedef struct dpll {
+       /* given values */
+       int n;
+       int m1, m2;
+       int p1, p2;
+       /* derived values */
+       int     dot;
+       int     vco;
+       int     m;
+       int     p;
+} intel_clock_t;
+
 struct intel_crtc_config {
+       /**
+        * quirks - bitfield with hw state readout quirks
+        *
+        * For various reasons the hw state readout code might not be able to
+        * completely faithfully read out the current state. These cases are
+        * tracked with quirk flags so that fastboot and state checker can act
+        * accordingly.
+        */
+#define PIPE_CONFIG_QUIRK_MODE_SYNC_FLAGS (1<<0) /* unreliable sync mode.flags */
+       unsigned long quirks;
+
        struct drm_display_mode requested_mode;
        struct drm_display_mode adjusted_mode;
        /* This flag must be set by the encoder's compute_config callback if it
@@ -201,29 +229,67 @@ struct intel_crtc_config {
        /* DP has a bunch of special case unfortunately, so mark the pipe
         * accordingly. */
        bool has_dp_encoder;
+
+       /*
+        * Enable dithering, used when the selected pipe bpp doesn't match the
+        * plane bpp.
+        */
        bool dither;
 
        /* Controls for the clock computation, to override various stages. */
        bool clock_set;
 
+       /* SDVO TV has a bunch of special case. To make multifunction encoders
+        * work correctly, we need to track this at runtime.*/
+       bool sdvo_tv_clock;
+
+       /*
+        * crtc bandwidth limit, don't increase pipe bpp or clock if not really
+        * required. This is set in the 2nd loop of calling encoder's
+        * ->compute_config if the first pick doesn't work out.
+        */
+       bool bw_constrained;
+
        /* Settings for the intel dpll used on pretty much everything but
         * haswell. */
-       struct dpll {
-               unsigned n;
-               unsigned m1, m2;
-               unsigned p1, p2;
-       } dpll;
+       struct dpll dpll;
+
+       /* Selected dpll when shared or DPLL_ID_PRIVATE. */
+       enum intel_dpll_id shared_dpll;
+
+       /* Actual register state of the dpll, for shared dpll cross-checking. */
+       struct intel_dpll_hw_state dpll_hw_state;
 
        int pipe_bpp;
        struct intel_link_m_n dp_m_n;
-       /**
-        * This is currently used by DP and HDMI encoders since those can have a
-        * target pixel clock != the port link clock (which is currently stored
-        * in adjusted_mode->clock).
+
+       /*
+        * Frequence the dpll for the port should run at. Differs from the
+        * adjusted dotclock e.g. for DP or 12bpc hdmi mode.
         */
-       int pixel_target_clock;
+       int port_clock;
+
        /* Used by SDVO (and if we ever fix it, HDMI). */
        unsigned pixel_multiplier;
+
+       /* Panel fitter controls for gen2-gen4 + VLV */
+       struct {
+               u32 control;
+               u32 pgm_ratios;
+               u32 lvds_border_bits;
+       } gmch_pfit;
+
+       /* Panel fitter placement and size for Ironlake+ */
+       struct {
+               u32 pos;
+               u32 size;
+       } pch_pfit;
+
+       /* FDI configuration, only valid if has_pch_encoder is set. */
+       int fdi_lanes;
+       struct intel_link_m_n fdi_m_n;
+
+       bool ips_enabled;
 };
 
 struct intel_crtc {
@@ -242,7 +308,6 @@ struct intel_crtc {
        bool lowfreq_avail;
        struct intel_overlay *overlay;
        struct intel_unpin_work *unpin_work;
-       int fdi_lanes;
 
        atomic_t unpin_work_count;
 
@@ -259,12 +324,14 @@ struct intel_crtc {
 
        struct intel_crtc_config config;
 
-       /* We can share PLLs across outputs if the timings match */
-       struct intel_pch_pll *pch_pll;
        uint32_t ddi_pll_sel;
 
        /* reset counter value when the last flip was submitted */
        unsigned int reset_counter;
+
+       /* Access to these should be protected by dev_priv->irq_lock. */
+       bool cpu_fifo_underrun_disabled;
+       bool pch_fifo_underrun_disabled;
 };
 
 struct intel_plane {
@@ -279,6 +346,18 @@ struct intel_plane {
        unsigned int crtc_w, crtc_h;
        uint32_t src_x, src_y;
        uint32_t src_w, src_h;
+
+       /* Since we need to change the watermarks before/after
+        * enabling/disabling the planes, we need to store the parameters here
+        * as the other pieces of the struct may not reflect the values we want
+        * for the watermark calculations. Currently only Haswell uses this.
+        */
+       struct {
+               bool enable;
+               uint8_t bytes_per_pixel;
+               uint32_t horiz_pixels;
+       } wm;
+
        void (*update_plane)(struct drm_plane *plane,
                             struct drm_framebuffer *fb,
                             struct drm_i915_gem_object *obj,
@@ -411,7 +490,6 @@ struct intel_dp {
        uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
        struct i2c_adapter adapter;
        struct i2c_algo_dp_aux_data algo;
-       bool is_pch_edp;
        uint8_t train_set[4];
        int panel_power_up_delay;
        int panel_power_down_delay;
@@ -431,6 +509,19 @@ struct intel_digital_port {
        struct intel_hdmi hdmi;
 };
 
+static inline int
+vlv_dport_to_channel(struct intel_digital_port *dport)
+{
+       switch (dport->port) {
+       case PORT_B:
+               return 0;
+       case PORT_C:
+               return 1;
+       default:
+               BUG();
+       }
+}
+
 static inline struct drm_crtc *
 intel_get_crtc_for_pipe(struct drm_device *dev, int pipe)
 {
@@ -474,6 +565,7 @@ 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);
@@ -488,13 +580,14 @@ extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg,
 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);
+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 bool intel_lvds_init(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 void intel_dp_init_connector(struct intel_digital_port *intel_dig_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);
@@ -512,7 +605,6 @@ 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 bool intel_encoder_is_pch_edp(struct drm_encoder *encoder);
 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);
@@ -524,12 +616,14 @@ extern void intel_panel_fini(struct intel_panel *panel);
 
 extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
                                   struct drm_display_mode *adjusted_mode);
-extern void intel_pch_panel_fitting(struct drm_device *dev,
-                                   int fitting_mode,
-                                   const struct drm_display_mode *mode,
-                                   struct drm_display_mode *adjusted_mode);
-extern u32 intel_panel_get_max_backlight(struct drm_device *dev);
-extern void intel_panel_set_backlight(struct drm_device *dev, u32 level);
+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);
@@ -553,11 +647,11 @@ 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_encoder_dpms(struct intel_encoder *encoder, int mode);
-extern bool intel_encoder_check_is_cloned(struct intel_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);
 
 
 static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector)
@@ -565,19 +659,17 @@ static inline struct intel_encoder *intel_attached_encoder(struct drm_connector
        return to_intel_connector(connector)->encoder;
 }
 
-static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
-{
-       struct intel_digital_port *intel_dig_port =
-               container_of(encoder, struct intel_digital_port, base.base);
-       return &intel_dig_port->dp;
-}
-
 static inline struct intel_digital_port *
 enc_to_dig_port(struct drm_encoder *encoder)
 {
        return container_of(encoder, struct intel_digital_port, base.base);
 }
 
+static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
+{
+       return &enc_to_dig_port(encoder)->dp;
+}
+
 static inline struct intel_digital_port *
 dp_to_dig_port(struct intel_dp *intel_dp)
 {
@@ -607,6 +699,7 @@ intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv,
 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;
@@ -660,13 +753,9 @@ extern void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
 #define assert_pipe_disabled(d, p) assert_pipe(d, p, false)
 
 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_cpt_verify_modeset(struct drm_device *dev, int pipe);
-extern void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc,
-                                        struct intel_link_m_n *m_n);
-extern void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc,
-                                        struct intel_link_m_n *m_n);
 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);
@@ -675,9 +764,7 @@ extern void intel_ddi_init(struct drm_device *dev, enum port port);
 extern void intel_update_watermarks(struct drm_device *dev);
 extern void intel_update_sprite_watermarks(struct drm_device *dev, int pipe,
                                           uint32_t sprite_width,
-                                          int pixel_size);
-extern void intel_update_linetime_watermarks(struct drm_device *dev, int pipe,
-                        struct drm_display_mode *mode);
+                                          int pixel_size, bool enable);
 
 extern unsigned long intel_gen4_compute_page_offset(int *x, int *y,
                                                    unsigned int tiling_mode,
@@ -689,8 +776,6 @@ extern int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
 extern int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
                                     struct drm_file *file_priv);
 
-extern u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg);
-
 /* Power-related functions, located in intel_pm.c */
 extern void intel_init_pm(struct drm_device *dev);
 /* FBC */
@@ -701,7 +786,12 @@ extern void intel_update_fbc(struct drm_device *dev);
 extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv);
 extern void intel_gpu_ips_teardown(void);
 
-extern bool intel_using_power_well(struct drm_device *dev);
+/* 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);
@@ -719,7 +809,7 @@ extern void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
 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, int clock);
+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);
@@ -728,5 +818,11 @@ intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector);
 extern void intel_ddi_fdi_disable(struct drm_crtc *crtc);
 
 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);
 
 #endif /* __INTEL_DRV_H__ */
index cc70b16..eb2020e 100644 (file)
@@ -53,6 +53,13 @@ static const struct intel_dvo_device intel_dvo_devices[] = {
                .slave_addr = CH7xxx_ADDR,
                .dev_ops = &ch7xxx_ops,
        },
+       {
+               .type = INTEL_DVO_CHIP_TMDS,
+               .name = "ch7xxx",
+               .dvo_reg = DVOC,
+               .slave_addr = 0x75, /* For some ch7010 */
+               .dev_ops = &ch7xxx_ops,
+       },
        {
                .type = INTEL_DVO_CHIP_LVDS,
                .name = "ivch",
@@ -129,6 +136,26 @@ static bool intel_dvo_get_hw_state(struct intel_encoder *encoder,
        return true;
 }
 
+static void intel_dvo_get_config(struct intel_encoder *encoder,
+                                struct intel_crtc_config *pipe_config)
+{
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+       struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
+       u32 tmp, flags = 0;
+
+       tmp = I915_READ(intel_dvo->dev.dvo_reg);
+       if (tmp & DVO_HSYNC_ACTIVE_HIGH)
+               flags |= DRM_MODE_FLAG_PHSYNC;
+       else
+               flags |= DRM_MODE_FLAG_NHSYNC;
+       if (tmp & DVO_VSYNC_ACTIVE_HIGH)
+               flags |= DRM_MODE_FLAG_PVSYNC;
+       else
+               flags |= DRM_MODE_FLAG_NVSYNC;
+
+       pipe_config->adjusted_mode.flags |= flags;
+}
+
 static void intel_disable_dvo(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
@@ -153,6 +180,7 @@ static void intel_enable_dvo(struct intel_encoder *encoder)
        intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
 }
 
+/* Special dpms function to support cloning between dvo/sdvo/crt. */
 static void intel_dvo_dpms(struct drm_connector *connector, int mode)
 {
        struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
@@ -174,6 +202,8 @@ static void intel_dvo_dpms(struct drm_connector *connector, int mode)
                return;
        }
 
+       /* We call connector dpms manually below in case pipe dpms doesn't
+        * change due to cloning. */
        if (mode == DRM_MODE_DPMS_ON) {
                intel_dvo->base.connectors_active = true;
 
@@ -440,6 +470,7 @@ void intel_dvo_init(struct drm_device *dev)
        intel_encoder->disable = intel_disable_dvo;
        intel_encoder->enable = intel_enable_dvo;
        intel_encoder->get_hw_state = intel_dvo_get_hw_state;
+       intel_encoder->get_config = intel_dvo_get_config;
        intel_connector->get_hw_state = intel_dvo_connector_get_hw_state;
 
        /* Now, try to find a controller */
index 6b7c3ca..dff669e 100644 (file)
@@ -60,8 +60,9 @@ static struct fb_ops intelfb_ops = {
 static int intelfb_create(struct drm_fb_helper *helper,
                          struct drm_fb_helper_surface_size *sizes)
 {
-       struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper;
-       struct drm_device *dev = ifbdev->helper.dev;
+       struct intel_fbdev *ifbdev =
+               container_of(helper, struct intel_fbdev, helper);
+       struct drm_device *dev = helper->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct fb_info *info;
        struct drm_framebuffer *fb;
@@ -108,7 +109,7 @@ static int intelfb_create(struct drm_fb_helper *helper,
                goto out_unpin;
        }
 
-       info->par = ifbdev;
+       info->par = helper;
 
        ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, obj);
        if (ret)
@@ -217,7 +218,7 @@ static void intel_fbdev_destroy(struct drm_device *dev,
 int intel_fbdev_init(struct drm_device *dev)
 {
        struct intel_fbdev *ifbdev;
-       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
 
        ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL);
@@ -242,7 +243,7 @@ int intel_fbdev_init(struct drm_device *dev)
 
 void intel_fbdev_initial_config(struct drm_device *dev)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
 
        /* Due to peculiar init order wrt to hpd handling this is separate. */
        drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32);
@@ -250,7 +251,7 @@ void intel_fbdev_initial_config(struct drm_device *dev)
 
 void intel_fbdev_fini(struct drm_device *dev)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        if (!dev_priv->fbdev)
                return;
 
@@ -261,7 +262,7 @@ void intel_fbdev_fini(struct drm_device *dev)
 
 void intel_fbdev_set_suspend(struct drm_device *dev, int state)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_fbdev *ifbdev = dev_priv->fbdev;
        struct fb_info *info;
 
@@ -274,7 +275,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state)
         * been restored from swap. If the object is stolen however, it will be
         * full of whatever garbage was left in there.
         */
-       if (!state && ifbdev->ifb.obj->stolen)
+       if (state == FBINFO_STATE_RUNNING && ifbdev->ifb.obj->stolen)
                memset_io(info->screen_base, 0, info->screen_size);
 
        fb_set_suspend(info, state);
@@ -284,16 +285,14 @@ MODULE_LICENSE("GPL and additional rights");
 
 void intel_fb_output_poll_changed(struct drm_device *dev)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
+       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)
 {
        int ret;
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       struct drm_mode_config *config = &dev->mode_config;
-       struct drm_plane *plane;
+       struct drm_i915_private *dev_priv = dev->dev_private;
 
        if (INTEL_INFO(dev)->num_pipes == 0)
                return;
@@ -304,10 +303,5 @@ void intel_fb_restore_mode(struct drm_device *dev)
        if (ret)
                DRM_DEBUG("failed to restore crtc mode\n");
 
-       /* Be sure to shut off any planes that may be active */
-       list_for_each_entry(plane, &config->plane_list, head)
-               if (plane->enabled)
-                       plane->funcs->disable_plane(plane);
-
        drm_modeset_unlock_all(dev);
 }
index a905793..98df2a0 100644 (file)
@@ -602,7 +602,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
        u32 hdmi_val;
 
        hdmi_val = SDVO_ENCODING_HDMI;
-       if (!HAS_PCH_SPLIT(dev) && !IS_VALLEYVIEW(dev))
+       if (!HAS_PCH_SPLIT(dev))
                hdmi_val |= intel_hdmi->color_range;
        if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
                hdmi_val |= SDVO_VSYNC_ACTIVE_HIGH;
@@ -658,6 +658,28 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
        return true;
 }
 
+static void intel_hdmi_get_config(struct intel_encoder *encoder,
+                                 struct intel_crtc_config *pipe_config)
+{
+       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;
+
+       tmp = I915_READ(intel_hdmi->hdmi_reg);
+
+       if (tmp & SDVO_HSYNC_ACTIVE_HIGH)
+               flags |= DRM_MODE_FLAG_PHSYNC;
+       else
+               flags |= DRM_MODE_FLAG_NHSYNC;
+
+       if (tmp & SDVO_VSYNC_ACTIVE_HIGH)
+               flags |= DRM_MODE_FLAG_PVSYNC;
+       else
+               flags |= DRM_MODE_FLAG_NVSYNC;
+
+       pipe_config->adjusted_mode.flags |= flags;
+}
+
 static void intel_enable_hdmi(struct intel_encoder *encoder)
 {
        struct drm_device *dev = encoder->base.dev;
@@ -697,6 +719,14 @@ static void intel_enable_hdmi(struct intel_encoder *encoder)
                I915_WRITE(intel_hdmi->hdmi_reg, temp);
                POSTING_READ(intel_hdmi->hdmi_reg);
        }
+
+       if (IS_VALLEYVIEW(dev)) {
+               struct intel_digital_port *dport =
+                       enc_to_dig_port(&encoder->base);
+               int channel = vlv_dport_to_channel(dport);
+
+               vlv_wait_port_ready(dev_priv, channel);
+       }
 }
 
 static void intel_disable_hdmi(struct intel_encoder *encoder)
@@ -775,6 +805,8 @@ 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 desired_bpp;
 
        if (intel_hdmi->color_range_auto) {
                /* See CEA-861-E - 5.1 Default Encoding Parameters */
@@ -794,14 +826,29 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
        /*
         * HDMI is either 12 or 8, so if the display lets 10bpc sneak
         * through, clamp it down. Note that g4x/vlv don't support 12bpc hdmi
-        * outputs.
+        * outputs. We also need to check that the higher clock still fits
+        * within limits.
         */
-       if (pipe_config->pipe_bpp > 8*3 && HAS_PCH_SPLIT(dev)) {
-               DRM_DEBUG_KMS("forcing bpc to 12 for HDMI\n");
-               pipe_config->pipe_bpp = 12*3;
+       if (pipe_config->pipe_bpp > 8*3 && clock_12bpc <= 225000
+           && HAS_PCH_SPLIT(dev)) {
+               DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
+               desired_bpp = 12*3;
+
+               /* Need to adjust the port link by 1.5x for 12bpc. */
+               pipe_config->port_clock = clock_12bpc;
        } else {
-               DRM_DEBUG_KMS("forcing bpc to 8 for HDMI\n");
-               pipe_config->pipe_bpp = 8*3;
+               DRM_DEBUG_KMS("picking bpc to 8 for HDMI output\n");
+               desired_bpp = 8*3;
+       }
+
+       if (!pipe_config->bw_constrained) {
+               DRM_DEBUG_KMS("forcing pipe bpc to %i for HDMI\n", desired_bpp);
+               pipe_config->pipe_bpp = desired_bpp;
+       }
+
+       if (adjusted_mode->clock > 225000) {
+               DRM_DEBUG_KMS("too high HDMI clock, rejecting mode\n");
+               return false;
        }
 
        return true;
@@ -955,6 +1002,97 @@ done:
        return 0;
 }
 
+static void intel_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;
+       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;
+       u32 val;
+
+       if (!IS_VALLEYVIEW(dev))
+               return;
+
+       /* Enable clock channels for this port */
+       val = vlv_dpio_read(dev_priv, 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);
+
+       /* HDMI 1.0V-2dB */
+       vlv_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0);
+       vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL4(port),
+                        0x2b245f5f);
+       vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL2(port),
+                        0x5578b83a);
+       vlv_dpio_write(dev_priv, DPIO_TX_SWING_CTL3(port),
+                        0x0c782040);
+       vlv_dpio_write(dev_priv, 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),
+                        0x00002000);
+       vlv_dpio_write(dev_priv, DPIO_TX_OCALINIT(port),
+                        DPIO_TX_OCALINIT_EN);
+
+       /* Program lane clock */
+       vlv_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF0(port),
+                        0x00760018);
+       vlv_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF8(port),
+                        0x00400888);
+}
+
+static void intel_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;
+       int port = vlv_dport_to_channel(dport);
+
+       if (!IS_VALLEYVIEW(dev))
+               return;
+
+       /* Program Tx lane resets to default */
+       vlv_dpio_write(dev_priv, DPIO_PCS_TX(port),
+                        DPIO_PCS_TX_LANE2_RESET |
+                        DPIO_PCS_TX_LANE1_RESET);
+       vlv_dpio_write(dev_priv, 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, DPIO_PCS_CTL_OVER1(port),
+                        0x00002000);
+       vlv_dpio_write(dev_priv, DPIO_TX_OCALINIT(port),
+                        DPIO_TX_OCALINIT_EN);
+}
+
+static void intel_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;
+       int port = vlv_dport_to_channel(dport);
+
+       /* 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);
+       mutex_unlock(&dev_priv->dpio_lock);
+}
+
 static void intel_hdmi_destroy(struct drm_connector *connector)
 {
        drm_sysfs_connector_remove(connector);
@@ -1094,6 +1232,12 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
        intel_encoder->enable = intel_enable_hdmi;
        intel_encoder->disable = intel_disable_hdmi;
        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_enable = intel_hdmi_pre_enable;
+               intel_encoder->pre_pll_enable = intel_hdmi_pre_pll_enable;
+               intel_encoder->post_disable = intel_hdmi_post_disable;
+       }
 
        intel_encoder->type = INTEL_OUTPUT_HDMI;
        intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
index 817f936..021e8da 100644 (file)
@@ -49,8 +49,6 @@ struct intel_lvds_connector {
 struct intel_lvds_encoder {
        struct intel_encoder base;
 
-       u32 pfit_control;
-       u32 pfit_pgm_ratios;
        bool is_dual_link;
        u32 reg;
 
@@ -88,6 +86,31 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
        return true;
 }
 
+static void intel_lvds_get_config(struct intel_encoder *encoder,
+                                 struct intel_crtc_config *pipe_config)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 lvds_reg, tmp, flags = 0;
+
+       if (HAS_PCH_SPLIT(dev))
+               lvds_reg = PCH_LVDS;
+       else
+               lvds_reg = LVDS;
+
+       tmp = I915_READ(lvds_reg);
+       if (tmp & LVDS_HSYNC_POLARITY)
+               flags |= DRM_MODE_FLAG_NHSYNC;
+       else
+               flags |= DRM_MODE_FLAG_PHSYNC;
+       if (tmp & LVDS_VSYNC_POLARITY)
+               flags |= DRM_MODE_FLAG_NVSYNC;
+       else
+               flags |= DRM_MODE_FLAG_PVSYNC;
+
+       pipe_config->adjusted_mode.flags |= flags;
+}
+
 /* The LVDS pin pair needs to be on before the DPLLs are enabled.
  * This is an exception to the general rule that mode_set doesn't turn
  * things on.
@@ -118,7 +141,8 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder)
        }
 
        /* set the corresponsding LVDS_BORDER bit */
-       temp |= dev_priv->lvds_border_bits;
+       temp &= ~LVDS_BORDER_ENABLE;
+       temp |= intel_crtc->config.gmch_pfit.lvds_border_bits;
        /* Set the B0-B3 data pairs corresponding to whether we're going to
         * set the DPLLs for dual-channel mode or not.
         */
@@ -136,7 +160,10 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder)
         * special lvds dither control bit on pch-split platforms, dithering is
         * only controlled through the PIPECONF reg. */
        if (INTEL_INFO(dev)->gen == 4) {
-               if (dev_priv->lvds_dither)
+               /* Bspec wording suggests that LVDS port dithering only exists
+                * for 18bpp panels. */
+               if (intel_crtc->config.dither &&
+                   intel_crtc->config.pipe_bpp == 18)
                        temp |= LVDS_ENABLE_DITHER;
                else
                        temp &= ~LVDS_ENABLE_DITHER;
@@ -150,29 +177,6 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder)
        I915_WRITE(lvds_encoder->reg, temp);
 }
 
-static void intel_pre_enable_lvds(struct intel_encoder *encoder)
-{
-       struct drm_device *dev = encoder->base.dev;
-       struct intel_lvds_encoder *enc = to_lvds_encoder(&encoder->base);
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       if (HAS_PCH_SPLIT(dev) || !enc->pfit_control)
-               return;
-
-       /*
-        * Enable automatic panel scaling so that non-native modes
-        * fill the screen.  The panel fitter should only be
-        * adjusted whilst the pipe is disabled, according to
-        * register description and PRM.
-        */
-       DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
-                     enc->pfit_control,
-                     enc->pfit_pgm_ratios);
-
-       I915_WRITE(PFIT_PGM_RATIOS, enc->pfit_pgm_ratios);
-       I915_WRITE(PFIT_CONTROL, enc->pfit_control);
-}
-
 /**
  * Sets the power state for the panel.
  */
@@ -241,62 +245,6 @@ static int intel_lvds_mode_valid(struct drm_connector *connector,
        return MODE_OK;
 }
 
-static void
-centre_horizontally(struct drm_display_mode *mode,
-                   int width)
-{
-       u32 border, sync_pos, blank_width, sync_width;
-
-       /* keep the hsync and hblank widths constant */
-       sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start;
-       blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start;
-       sync_pos = (blank_width - sync_width + 1) / 2;
-
-       border = (mode->hdisplay - width + 1) / 2;
-       border += border & 1; /* make the border even */
-
-       mode->crtc_hdisplay = width;
-       mode->crtc_hblank_start = width + border;
-       mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width;
-
-       mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos;
-       mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width;
-}
-
-static void
-centre_vertically(struct drm_display_mode *mode,
-                 int height)
-{
-       u32 border, sync_pos, blank_width, sync_width;
-
-       /* keep the vsync and vblank widths constant */
-       sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start;
-       blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start;
-       sync_pos = (blank_width - sync_width + 1) / 2;
-
-       border = (mode->vdisplay - height + 1) / 2;
-
-       mode->crtc_vdisplay = height;
-       mode->crtc_vblank_start = height + border;
-       mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width;
-
-       mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos;
-       mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width;
-}
-
-static inline u32 panel_fitter_scaling(u32 source, u32 target)
-{
-       /*
-        * Floating point operation is not supported. So the FACTOR
-        * is defined, which can avoid the floating point computation
-        * when calculating the panel ratio.
-        */
-#define ACCURACY 12
-#define FACTOR (1 << ACCURACY)
-       u32 ratio = source * FACTOR / target;
-       return (FACTOR * ratio + FACTOR/2) / FACTOR;
-}
-
 static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
                                      struct intel_crtc_config *pipe_config)
 {
@@ -307,11 +255,8 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
        struct intel_connector *intel_connector =
                &lvds_encoder->attached_connector->base;
        struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
-       struct drm_display_mode *mode = &pipe_config->requested_mode;
        struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc;
-       u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
        unsigned int lvds_bpp;
-       int pipe;
 
        /* Should never happen!! */
        if (INTEL_INFO(dev)->gen < 4 && intel_crtc->pipe == 0) {
@@ -319,20 +264,18 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
                return false;
        }
 
-       if (intel_encoder_check_is_cloned(&lvds_encoder->base))
-               return false;
-
        if ((I915_READ(lvds_encoder->reg) & LVDS_A3_POWER_MASK) ==
            LVDS_A3_POWER_UP)
                lvds_bpp = 8*3;
        else
                lvds_bpp = 6*3;
 
-       if (lvds_bpp != pipe_config->pipe_bpp) {
+       if (lvds_bpp != pipe_config->pipe_bpp && !pipe_config->bw_constrained) {
                DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n",
                              pipe_config->pipe_bpp, lvds_bpp);
                pipe_config->pipe_bpp = lvds_bpp;
        }
+
        /*
         * We have timings from the BIOS for the panel, put them in
         * to the adjusted mode.  The CRTC will be set up for this mode,
@@ -345,139 +288,17 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
        if (HAS_PCH_SPLIT(dev)) {
                pipe_config->has_pch_encoder = true;
 
-               intel_pch_panel_fitting(dev,
-                                       intel_connector->panel.fitting_mode,
-                                       mode, adjusted_mode);
+               intel_pch_panel_fitting(intel_crtc, pipe_config,
+                                       intel_connector->panel.fitting_mode);
                return true;
+       } else {
+               intel_gmch_panel_fitting(intel_crtc, pipe_config,
+                                        intel_connector->panel.fitting_mode);
        }
 
-       /* Native modes don't need fitting */
-       if (adjusted_mode->hdisplay == mode->hdisplay &&
-           adjusted_mode->vdisplay == mode->vdisplay)
-               goto out;
-
-       /* 965+ wants fuzzy fitting */
-       if (INTEL_INFO(dev)->gen >= 4)
-               pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
-                                PFIT_FILTER_FUZZY);
-
-       /*
-        * Enable automatic panel scaling for non-native modes so that they fill
-        * the screen.  Should be enabled before the pipe is enabled, according
-        * to register description and PRM.
-        * Change the value here to see the borders for debugging
-        */
-       for_each_pipe(pipe)
-               I915_WRITE(BCLRPAT(pipe), 0);
-
        drm_mode_set_crtcinfo(adjusted_mode, 0);
        pipe_config->timings_set = true;
 
-       switch (intel_connector->panel.fitting_mode) {
-       case DRM_MODE_SCALE_CENTER:
-               /*
-                * 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);
-               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);
-               }
-               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) {
-                       pfit_control |= PFIT_ENABLE;
-                       if (INTEL_INFO(dev)->gen >= 4)
-                               pfit_control |= PFIT_SCALING_AUTO;
-                       else
-                               pfit_control |= (VERT_AUTO_SCALE |
-                                                VERT_INTERP_BILINEAR |
-                                                HORIZ_AUTO_SCALE |
-                                                HORIZ_INTERP_BILINEAR);
-               }
-               break;
-
-       default:
-               break;
-       }
-
-out:
-       /* If not enabling scaling, be consistent and always use 0. */
-       if ((pfit_control & PFIT_ENABLE) == 0) {
-               pfit_control = 0;
-               pfit_pgm_ratios = 0;
-       }
-
-       /* Make sure pre-965 set dither correctly */
-       if (INTEL_INFO(dev)->gen < 4 && dev_priv->lvds_dither)
-               pfit_control |= PANEL_8TO6_DITHER_ENABLE;
-
-       if (pfit_control != lvds_encoder->pfit_control ||
-           pfit_pgm_ratios != lvds_encoder->pfit_pgm_ratios) {
-               lvds_encoder->pfit_control = pfit_control;
-               lvds_encoder->pfit_pgm_ratios = pfit_pgm_ratios;
-       }
-       dev_priv->lvds_border_bits = border;
-
        /*
         * XXX: It would be nice to support lower refresh rates on the
         * panels to reduce power consumption, and perhaps match the
@@ -953,11 +774,11 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev,
        struct drm_i915_private *dev_priv = dev->dev_private;
        int i;
 
-       if (!dev_priv->child_dev_num)
+       if (!dev_priv->vbt.child_dev_num)
                return true;
 
-       for (i = 0; i < dev_priv->child_dev_num; i++) {
-               struct child_device_config *child = dev_priv->child_dev + i;
+       for (i = 0; i < dev_priv->vbt.child_dev_num; i++) {
+               struct child_device_config *child = dev_priv->vbt.child_dev + i;
 
                /* If the device type is not LFP, continue.
                 * We have to check both the new identifiers as well as the
@@ -1045,7 +866,7 @@ static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder)
         */
        val = I915_READ(lvds_encoder->reg);
        if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED)))
-               val = dev_priv->bios_lvds_val;
+               val = dev_priv->vbt.bios_lvds_val;
 
        return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP;
 }
@@ -1072,7 +893,7 @@ static bool intel_lvds_supported(struct drm_device *dev)
  * Create the connector, register the LVDS DDC bus, and try to figure out what
  * modes we can display on the LVDS panel (if present).
  */
-bool intel_lvds_init(struct drm_device *dev)
+void intel_lvds_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_lvds_encoder *lvds_encoder;
@@ -1090,43 +911,39 @@ bool intel_lvds_init(struct drm_device *dev)
        u8 pin;
 
        if (!intel_lvds_supported(dev))
-               return false;
+               return;
 
        /* Skip init on machines we know falsely report LVDS */
        if (dmi_check_system(intel_no_lvds))
-               return false;
+               return;
 
        pin = GMBUS_PORT_PANEL;
        if (!lvds_is_present_in_vbt(dev, &pin)) {
                DRM_DEBUG_KMS("LVDS is not present in VBT\n");
-               return false;
+               return;
        }
 
        if (HAS_PCH_SPLIT(dev)) {
                if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
-                       return false;
-               if (dev_priv->edp.support) {
+                       return;
+               if (dev_priv->vbt.edp_support) {
                        DRM_DEBUG_KMS("disable LVDS for eDP support\n");
-                       return false;
+                       return;
                }
        }
 
        lvds_encoder = kzalloc(sizeof(struct intel_lvds_encoder), GFP_KERNEL);
        if (!lvds_encoder)
-               return false;
+               return;
 
        lvds_connector = kzalloc(sizeof(struct intel_lvds_connector), GFP_KERNEL);
        if (!lvds_connector) {
                kfree(lvds_encoder);
-               return false;
+               return;
        }
 
        lvds_encoder->attached_connector = lvds_connector;
 
-       if (!HAS_PCH_SPLIT(dev)) {
-               lvds_encoder->pfit_control = I915_READ(PFIT_CONTROL);
-       }
-
        intel_encoder = &lvds_encoder->base;
        encoder = &intel_encoder->base;
        intel_connector = &lvds_connector->base;
@@ -1138,11 +955,11 @@ bool intel_lvds_init(struct drm_device *dev)
                         DRM_MODE_ENCODER_LVDS);
 
        intel_encoder->enable = intel_enable_lvds;
-       intel_encoder->pre_enable = intel_pre_enable_lvds;
        intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds;
        intel_encoder->compute_config = intel_lvds_compute_config;
        intel_encoder->disable = intel_disable_lvds;
        intel_encoder->get_hw_state = intel_lvds_get_hw_state;
+       intel_encoder->get_config = intel_lvds_get_config;
        intel_connector->get_hw_state = intel_connector_get_hw_state;
 
        intel_connector_attach_encoder(intel_connector, intel_encoder);
@@ -1228,11 +1045,11 @@ bool intel_lvds_init(struct drm_device *dev)
        }
 
        /* Failed to get EDID, what about VBT? */
-       if (dev_priv->lfp_lvds_vbt_mode) {
+       if (dev_priv->vbt.lfp_lvds_vbt_mode) {
                DRM_DEBUG_KMS("using mode from VBT: ");
-               drm_mode_debug_printmodeline(dev_priv->lfp_lvds_vbt_mode);
+               drm_mode_debug_printmodeline(dev_priv->vbt.lfp_lvds_vbt_mode);
 
-               fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
+               fixed_mode = drm_mode_duplicate(dev, dev_priv->vbt.lfp_lvds_vbt_mode);
                if (fixed_mode) {
                        fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
                        goto out;
@@ -1293,7 +1110,7 @@ out:
        intel_panel_init(&intel_connector->panel, fixed_mode);
        intel_panel_setup_backlight(connector);
 
-       return true;
+       return;
 
 failed:
        DRM_DEBUG_KMS("No LVDS modes found, disabling.\n");
@@ -1303,5 +1120,5 @@ failed:
                drm_mode_destroy(dev, fixed_mode);
        kfree(lvds_encoder);
        kfree(lvds_connector);
-       return false;
+       return;
 }
index a8117e6..cfb8fb6 100644 (file)
@@ -110,6 +110,10 @@ struct opregion_asle {
        u8 rsvd[102];
 } __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)
@@ -123,6 +127,12 @@ struct opregion_asle {
 #define ASLE_PFIT_FAILED       (1<<14)
 #define ASLE_PWM_FREQ_FAILED   (1<<16)
 
+/* Technology enabled indicator */
+#define ASLE_TCHE_ALS_EN       (1 << 0)
+#define ASLE_TCHE_BLC_EN       (1 << 1)
+#define ASLE_TCHE_PFIT_EN      (1 << 2)
+#define ASLE_TCHE_PFMB_EN      (1 << 3)
+
 /* ASLE backlight brightness to set */
 #define ASLE_BCLP_VALID                (1<<31)
 #define ASLE_BCLP_MSK          (~(1<<31))
@@ -152,7 +162,6 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct opregion_asle __iomem *asle = dev_priv->opregion.asle;
-       u32 max;
 
        DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp);
 
@@ -163,8 +172,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
        if (bclp > 255)
                return ASLE_BACKLIGHT_FAILED;
 
-       max = intel_panel_get_max_backlight(dev);
-       intel_panel_set_backlight(dev, bclp * max / 255);
+       intel_panel_set_backlight(dev, bclp, 255);
        iowrite32((bclp*0x64)/0xff | ASLE_CBLV_VALID, &asle->cblv);
 
        return 0;
@@ -174,29 +182,22 @@ 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 */
-       return 0;
+       DRM_DEBUG_DRIVER("Illum is not supported\n");
+       return ASLE_ALS_ILLUM_FAILED;
 }
 
 static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       if (pfmb & ASLE_PFMB_PWM_VALID) {
-               u32 blc_pwm_ctl = I915_READ(BLC_PWM_CTL);
-               u32 pwm = pfmb & ASLE_PFMB_PWM_MASK;
-               blc_pwm_ctl &= BACKLIGHT_DUTY_CYCLE_MASK;
-               pwm = pwm >> 9;
-               /* FIXME - what do we do with the PWM? */
-       }
-       return 0;
+       DRM_DEBUG_DRIVER("PWM freq is not supported\n");
+       return ASLE_PWM_FREQ_FAILED;
 }
 
 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 */
-       if (!(pfit & ASLE_PFIT_VALID))
-               return ASLE_PFIT_FAILED;
-       return 0;
+       DRM_DEBUG_DRIVER("Pfit is not supported\n");
+       return ASLE_PFIT_FAILED;
 }
 
 void intel_opregion_asle_intr(struct drm_device *dev)
@@ -231,64 +232,6 @@ void intel_opregion_asle_intr(struct drm_device *dev)
        iowrite32(asle_stat, &asle->aslc);
 }
 
-void intel_opregion_gse_intr(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct opregion_asle __iomem *asle = dev_priv->opregion.asle;
-       u32 asle_stat = 0;
-       u32 asle_req;
-
-       if (!asle)
-               return;
-
-       asle_req = ioread32(&asle->aslc) & ASLE_REQ_MSK;
-
-       if (!asle_req) {
-               DRM_DEBUG_DRIVER("non asle set request??\n");
-               return;
-       }
-
-       if (asle_req & ASLE_SET_ALS_ILLUM) {
-               DRM_DEBUG_DRIVER("Illum is not supported\n");
-               asle_stat |= ASLE_ALS_ILLUM_FAILED;
-       }
-
-       if (asle_req & ASLE_SET_BACKLIGHT)
-               asle_stat |= asle_set_backlight(dev, ioread32(&asle->bclp));
-
-       if (asle_req & ASLE_SET_PFIT) {
-               DRM_DEBUG_DRIVER("Pfit is not supported\n");
-               asle_stat |= ASLE_PFIT_FAILED;
-       }
-
-       if (asle_req & ASLE_SET_PWM_FREQ) {
-               DRM_DEBUG_DRIVER("PWM freq is not supported\n");
-               asle_stat |= ASLE_PWM_FREQ_FAILED;
-       }
-
-       iowrite32(asle_stat, &asle->aslc);
-}
-#define ASLE_ALS_EN    (1<<0)
-#define ASLE_BLC_EN    (1<<1)
-#define ASLE_PFIT_EN   (1<<2)
-#define ASLE_PFMB_EN   (1<<3)
-
-void intel_opregion_enable_asle(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct opregion_asle __iomem *asle = dev_priv->opregion.asle;
-
-       if (asle) {
-               if (IS_MOBILE(dev))
-                       intel_enable_asle(dev);
-
-               iowrite32(ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN |
-                         ASLE_PFMB_EN,
-                         &asle->tche);
-               iowrite32(1, &asle->ardy);
-       }
-}
-
 #define ACPI_EV_DISPLAY_SWITCH (1<<0)
 #define ACPI_EV_LID            (1<<1)
 #define ACPI_EV_DOCK           (1<<2)
@@ -368,8 +311,8 @@ static void intel_didl_outputs(struct drm_device *dev)
 
        list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) {
                if (i >= 8) {
-                       dev_printk(KERN_ERR, &dev->pdev->dev,
-                                   "More than 8 outputs detected\n");
+                       dev_dbg(&dev->pdev->dev,
+                               "More than 8 outputs detected via ACPI\n");
                        return;
                }
                status =
@@ -395,8 +338,8 @@ blind_set:
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                int output_type = ACPI_OTHER_OUTPUT;
                if (i >= 8) {
-                       dev_printk(KERN_ERR, &dev->pdev->dev,
-                                   "More than 8 outputs detected\n");
+                       dev_dbg(&dev->pdev->dev,
+                               "More than 8 outputs in connector list\n");
                        return;
                }
                switch (connector->connector_type) {
@@ -472,8 +415,10 @@ void intel_opregion_init(struct drm_device *dev)
                register_acpi_notifier(&intel_opregion_notifier);
        }
 
-       if (opregion->asle)
-               intel_opregion_enable_asle(dev);
+       if (opregion->asle) {
+               iowrite32(ASLE_TCHE_BLC_EN, &opregion->asle->tche);
+               iowrite32(ASLE_ARDY_READY, &opregion->asle->ardy);
+       }
 }
 
 void intel_opregion_fini(struct drm_device *dev)
@@ -484,6 +429,9 @@ void intel_opregion_fini(struct drm_device *dev)
        if (!opregion->header)
                return;
 
+       if (opregion->asle)
+               iowrite32(ASLE_ARDY_NOT_READY, &opregion->asle->ardy);
+
        if (opregion->acpi) {
                iowrite32(0, &opregion->acpi->drdy);
 
@@ -546,6 +494,8 @@ int intel_opregion_setup(struct drm_device *dev)
        if (mboxes & MBOX_ASLE) {
                DRM_DEBUG_DRIVER("ASLE supported\n");
                opregion->asle = base + OPREGION_ASLE_OFFSET;
+
+               iowrite32(ASLE_ARDY_NOT_READY, &opregion->asle->ardy);
        }
 
        return 0;
index 67a2501..a369881 100644 (file)
@@ -217,7 +217,7 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
        int ret;
 
        BUG_ON(overlay->last_flip_req);
-       ret = i915_add_request(ring, NULL, &overlay->last_flip_req);
+       ret = i915_add_request(ring, &overlay->last_flip_req);
        if (ret)
                return ret;
 
@@ -286,7 +286,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
        intel_ring_emit(ring, flip_addr);
        intel_ring_advance(ring);
 
-       return i915_add_request(ring, NULL, &overlay->last_flip_req);
+       return i915_add_request(ring, &overlay->last_flip_req);
 }
 
 static void intel_overlay_release_old_vid_tail(struct intel_overlay *overlay)
@@ -1485,14 +1485,15 @@ err:
 }
 
 void
-intel_overlay_print_error_state(struct seq_file *m, struct intel_overlay_error_state *error)
+intel_overlay_print_error_state(struct drm_i915_error_state_buf *m,
+                               struct intel_overlay_error_state *error)
 {
-       seq_printf(m, "Overlay, status: 0x%08x, interrupt: 0x%08x\n",
-                  error->dovsta, error->isr);
-       seq_printf(m, "  Register file at 0x%08lx:\n",
-                  error->base);
+       i915_error_printf(m, "Overlay, status: 0x%08x, interrupt: 0x%08x\n",
+                         error->dovsta, error->isr);
+       i915_error_printf(m, "  Register file at 0x%08lx:\n",
+                         error->base);
 
-#define P(x) seq_printf(m, "    " #x ":        0x%08x\n", error->regs.x)
+#define P(x) i915_error_printf(m, "    " #x ": 0x%08x\n", error->regs.x)
        P(OBUF_0Y);
        P(OBUF_1Y);
        P(OBUF_0U);
index eb5e6e9..80bea1d 100644 (file)
@@ -54,14 +54,16 @@ intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
 
 /* adjusted_mode has been preset to be the panel's fixed mode */
 void
-intel_pch_panel_fitting(struct drm_device *dev,
-                       int fitting_mode,
-                       const struct drm_display_mode *mode,
-                       struct drm_display_mode *adjusted_mode)
+intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
+                       struct intel_crtc_config *pipe_config,
+                       int fitting_mode)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_display_mode *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 */
@@ -104,17 +106,209 @@ intel_pch_panel_fitting(struct drm_device *dev,
                }
                break;
 
-       default:
        case DRM_MODE_SCALE_FULLSCREEN:
                x = y = 0;
                width = adjusted_mode->hdisplay;
                height = adjusted_mode->vdisplay;
                break;
+
+       default:
+               WARN(1, "bad panel fit mode: %d\n", fitting_mode);
+               return;
        }
 
 done:
-       dev_priv->pch_pf_pos = (x << 16) | y;
-       dev_priv->pch_pf_size = (width << 16) | height;
+       pipe_config->pch_pfit.pos = (x << 16) | y;
+       pipe_config->pch_pfit.size = (width << 16) | height;
+}
+
+static void
+centre_horizontally(struct drm_display_mode *mode,
+                   int width)
+{
+       u32 border, sync_pos, blank_width, sync_width;
+
+       /* keep the hsync and hblank widths constant */
+       sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start;
+       blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start;
+       sync_pos = (blank_width - sync_width + 1) / 2;
+
+       border = (mode->hdisplay - width + 1) / 2;
+       border += border & 1; /* make the border even */
+
+       mode->crtc_hdisplay = width;
+       mode->crtc_hblank_start = width + border;
+       mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width;
+
+       mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos;
+       mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width;
+}
+
+static void
+centre_vertically(struct drm_display_mode *mode,
+                 int height)
+{
+       u32 border, sync_pos, blank_width, sync_width;
+
+       /* keep the vsync and vblank widths constant */
+       sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start;
+       blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start;
+       sync_pos = (blank_width - sync_width + 1) / 2;
+
+       border = (mode->vdisplay - height + 1) / 2;
+
+       mode->crtc_vdisplay = height;
+       mode->crtc_vblank_start = height + border;
+       mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width;
+
+       mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos;
+       mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width;
+}
+
+static inline u32 panel_fitter_scaling(u32 source, u32 target)
+{
+       /*
+        * Floating point operation is not supported. So the FACTOR
+        * is defined, which can avoid the floating point computation
+        * when calculating the panel ratio.
+        */
+#define ACCURACY 12
+#define FACTOR (1 << ACCURACY)
+       u32 ratio = source * FACTOR / target;
+       return (FACTOR * ratio + FACTOR/2) / FACTOR;
+}
+
+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;
+
+       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)
+               goto out;
+
+       switch (fitting_mode) {
+       case DRM_MODE_SCALE_CENTER:
+               /*
+                * 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);
+               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);
+                       }
+               }
+               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) {
+                       pfit_control |= PFIT_ENABLE;
+                       if (INTEL_INFO(dev)->gen >= 4)
+                               pfit_control |= PFIT_SCALING_AUTO;
+                       else
+                               pfit_control |= (VERT_AUTO_SCALE |
+                                                VERT_INTERP_BILINEAR |
+                                                HORIZ_AUTO_SCALE |
+                                                HORIZ_INTERP_BILINEAR);
+               }
+               break;
+       default:
+               WARN(1, "bad panel fit mode: %d\n", fitting_mode);
+               return;
+       }
+
+       /* 965+ wants fuzzy fitting */
+       /* FIXME: handle multiple panels by failing gracefully */
+       if (INTEL_INFO(dev)->gen >= 4)
+               pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
+                                PFIT_FILTER_FUZZY);
+
+out:
+       if ((pfit_control & PFIT_ENABLE) == 0) {
+               pfit_control = 0;
+               pfit_pgm_ratios = 0;
+       }
+
+       /* Make sure pre-965 set dither correctly for 18bpp panels. */
+       if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18)
+               pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+
+       pipe_config->gmch_pfit.control = pfit_control;
+       pipe_config->gmch_pfit.pgm_ratios = pfit_pgm_ratios;
+       pipe_config->gmch_pfit.lvds_border_bits = border;
 }
 
 static int is_backlight_combination_mode(struct drm_device *dev)
@@ -130,11 +324,16 @@ static int is_backlight_combination_mode(struct drm_device *dev)
        return 0;
 }
 
+/* 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)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 val;
 
+       WARN_ON_SMP(!spin_is_locked(&dev_priv->backlight.lock));
+
        /* Restore the CTL value if it lost, e.g. GPU reset */
 
        if (HAS_PCH_SPLIT(dev_priv->dev)) {
@@ -164,7 +363,7 @@ 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)
 {
        u32 max;
 
@@ -182,23 +381,8 @@ static u32 _intel_panel_get_max_backlight(struct drm_device *dev)
                        max *= 0xff;
        }
 
-       return max;
-}
-
-u32 intel_panel_get_max_backlight(struct drm_device *dev)
-{
-       u32 max;
-
-       max = _intel_panel_get_max_backlight(dev);
-       if (max == 0) {
-               /* XXX add code here to query mode clock or hardware clock
-                * and program max PWM appropriately.
-                */
-               pr_warn_once("fixme: max PWM is zero\n");
-               return 1;
-       }
-
        DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
+
        return max;
 }
 
@@ -217,8 +401,11 @@ static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val)
                return val;
 
        if (i915_panel_invert_brightness > 0 ||
-           dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS)
-               return intel_panel_get_max_backlight(dev) - val;
+           dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) {
+               u32 max = intel_panel_get_max_backlight(dev);
+               if (max)
+                       return max - val;
+       }
 
        return val;
 }
@@ -227,6 +414,9 @@ static u32 intel_panel_get_backlight(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 
        if (HAS_PCH_SPLIT(dev)) {
                val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
@@ -244,6 +434,9 @@ static u32 intel_panel_get_backlight(struct drm_device *dev)
        }
 
        val = intel_panel_compute_brightness(dev, val);
+
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
+
        DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val);
        return val;
 }
@@ -270,6 +463,10 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level
                u32 max = intel_panel_get_max_backlight(dev);
                u8 lbpc;
 
+               /* we're screwed, but keep behaviour backwards compatible */
+               if (!max)
+                       max = 1;
+
                lbpc = level * 0xfe / max + 1;
                level /= lbpc;
                pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc);
@@ -282,9 +479,23 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level
        I915_WRITE(BLC_PWM_CTL, tmp | level);
 }
 
-void intel_panel_set_backlight(struct drm_device *dev, u32 level)
+/* set backlight brightness to level in range [0..max] */
+void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 freq;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
+
+       freq = intel_panel_get_max_backlight(dev);
+       if (!freq) {
+               /* we are screwed, bail out */
+               goto out;
+       }
+
+       /* scale to hardware */
+       level = level * freq / max;
 
        dev_priv->backlight.level = level;
        if (dev_priv->backlight.device)
@@ -292,11 +503,16 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level)
 
        if (dev_priv->backlight.enabled)
                intel_panel_actually_set_backlight(dev, level);
+out:
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 }
 
 void intel_panel_disable_backlight(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 
        dev_priv->backlight.enabled = false;
        intel_panel_actually_set_backlight(dev, 0);
@@ -314,12 +530,19 @@ void intel_panel_disable_backlight(struct drm_device *dev)
                        I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
                }
        }
+
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 }
 
 void intel_panel_enable_backlight(struct drm_device *dev,
                                  enum pipe pipe)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       enum transcoder cpu_transcoder =
+               intel_pipe_to_cpu_transcoder(dev_priv, pipe);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 
        if (dev_priv->backlight.level == 0) {
                dev_priv->backlight.level = intel_panel_get_max_backlight(dev);
@@ -347,7 +570,10 @@ void intel_panel_enable_backlight(struct drm_device *dev,
                else
                        tmp &= ~BLM_PIPE_SELECT;
 
-               tmp |= BLM_PIPE(pipe);
+               if (cpu_transcoder == TRANSCODER_EDP)
+                       tmp |= BLM_TRANSCODER_EDP;
+               else
+                       tmp |= BLM_PIPE(cpu_transcoder);
                tmp &= ~BLM_PWM_ENABLE;
 
                I915_WRITE(reg, tmp);
@@ -369,6 +595,8 @@ set_level:
         */
        dev_priv->backlight.enabled = true;
        intel_panel_actually_set_backlight(dev, dev_priv->backlight.level);
+
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 }
 
 static void intel_panel_init_backlight(struct drm_device *dev)
@@ -405,7 +633,8 @@ intel_panel_detect(struct drm_device *dev)
 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);
+       intel_panel_set_backlight(dev, bd->props.brightness,
+                                 bd->props.max_brightness);
        return 0;
 }
 
@@ -425,6 +654,7 @@ int intel_panel_setup_backlight(struct drm_connector *connector)
        struct drm_device *dev = connector->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct backlight_properties props;
+       unsigned long flags;
 
        intel_panel_init_backlight(dev);
 
@@ -434,7 +664,11 @@ int intel_panel_setup_backlight(struct drm_connector *connector)
        memset(&props, 0, sizeof(props));
        props.type = BACKLIGHT_RAW;
        props.brightness = dev_priv->backlight.level;
-       props.max_brightness = _intel_panel_get_max_backlight(dev);
+
+       spin_lock_irqsave(&dev_priv->backlight.lock, flags);
+       props.max_brightness = intel_panel_get_max_backlight(dev);
+       spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
+
        if (props.max_brightness == 0) {
                DRM_DEBUG_DRIVER("Failed to get maximum backlight value\n");
                return -ENODEV;
index aa01128..ccbdd83 100644 (file)
@@ -113,8 +113,8 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        fbc_ctl |= obj->fence_reg;
        I915_WRITE(FBC_CONTROL, fbc_ctl);
 
-       DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ",
-                     cfb_pitch, crtc->y, intel_crtc->plane);
+       DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %c, ",
+                     cfb_pitch, crtc->y, plane_name(intel_crtc->plane));
 }
 
 static bool i8xx_fbc_enabled(struct drm_device *dev)
@@ -148,7 +148,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        /* enable it... */
        I915_WRITE(DPFC_CONTROL, I915_READ(DPFC_CONTROL) | DPFC_CTL_EN);
 
-       DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
+       DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(intel_crtc->plane));
 }
 
 static void g4x_disable_fbc(struct drm_device *dev)
@@ -228,7 +228,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
                sandybridge_blit_fbc_update(dev);
        }
 
-       DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
+       DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(intel_crtc->plane));
 }
 
 static void ironlake_disable_fbc(struct drm_device *dev)
@@ -242,6 +242,18 @@ 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");
        }
 }
@@ -253,6 +265,47 @@ static bool ironlake_fbc_enabled(struct drm_device *dev)
        return I915_READ(ILK_DPFC_CONTROL) & DPFC_CTL_EN;
 }
 
+static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_framebuffer *fb = crtc->fb;
+       struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+       struct drm_i915_gem_object *obj = intel_fb->obj;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       I915_WRITE(IVB_FBC_RT_BASE, obj->gtt_offset);
+
+       I915_WRITE(ILK_DPFC_CONTROL, DPFC_CTL_EN | DPFC_CTL_LIMIT_1X |
+                  IVB_DPFC_CTL_FENCE_EN |
+                  intel_crtc->plane << IVB_DPFC_CTL_PLANE_SHIFT);
+
+       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,
+                  SNB_CPU_FENCE_ENABLE | obj->fence_reg);
+       I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y);
+
+       sandybridge_blit_fbc_update(dev);
+
+       DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
+}
+
 bool intel_fbc_enabled(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -378,7 +431,7 @@ void intel_disable_fbc(struct drm_device *dev)
  *   - no pixel mulitply/line duplication
  *   - no alpha buffer discard
  *   - no dual wide
- *   - framebuffer <= 2048 in width, 1536 in height
+ *   - framebuffer <= max_hdisplay in width, max_vdisplay in height
  *
  * We can't assume that any compression will take place (worst case),
  * so the compressed buffer has to be the same size as the uncompressed
@@ -396,6 +449,7 @@ void intel_update_fbc(struct drm_device *dev)
        struct intel_framebuffer *intel_fb;
        struct drm_i915_gem_object *obj;
        int enable_fbc;
+       unsigned int max_hdisplay, max_vdisplay;
 
        if (!i915_powersave)
                return;
@@ -439,7 +493,7 @@ void intel_update_fbc(struct drm_device *dev)
        if (enable_fbc < 0) {
                DRM_DEBUG_KMS("fbc set to per-chip default\n");
                enable_fbc = 1;
-               if (INTEL_INFO(dev)->gen <= 6)
+               if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev))
                        enable_fbc = 0;
        }
        if (!enable_fbc) {
@@ -454,13 +508,22 @@ void intel_update_fbc(struct drm_device *dev)
                dev_priv->no_fbc_reason = FBC_UNSUPPORTED_MODE;
                goto out_disable;
        }
-       if ((crtc->mode.hdisplay > 2048) ||
-           (crtc->mode.vdisplay > 1536)) {
+
+       if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
+               max_hdisplay = 4096;
+               max_vdisplay = 2048;
+       } else {
+               max_hdisplay = 2048;
+               max_vdisplay = 1536;
+       }
+       if ((crtc->mode.hdisplay > max_hdisplay) ||
+           (crtc->mode.vdisplay > max_vdisplay)) {
                DRM_DEBUG_KMS("mode too large for compression, disabling\n");
                dev_priv->no_fbc_reason = FBC_MODE_TOO_LARGE;
                goto out_disable;
        }
-       if ((IS_I915GM(dev) || IS_I945GM(dev)) && intel_crtc->plane != 0) {
+       if ((IS_I915GM(dev) || IS_I945GM(dev) || IS_HASWELL(dev)) &&
+           intel_crtc->plane != 0) {
                DRM_DEBUG_KMS("plane not 0, disabling compression\n");
                dev_priv->no_fbc_reason = FBC_BAD_PLANE;
                goto out_disable;
@@ -481,8 +544,6 @@ void intel_update_fbc(struct drm_device *dev)
                goto out_disable;
 
        if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size)) {
-               DRM_INFO("not enough stolen space for compressed buffer (need %zd bytes), disabling\n", intel_fb->obj->base.size);
-               DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n");
                DRM_DEBUG_KMS("framebuffer too large, disabling compression\n");
                dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
                goto out_disable;
@@ -1633,6 +1694,10 @@ static bool ironlake_check_srwm(struct drm_device *dev, int level,
                I915_WRITE(DISP_ARB_CTL,
                           I915_READ(DISP_ARB_CTL) | DISP_FBC_WM_DIS);
                return false;
+       } else if (INTEL_INFO(dev)->gen >= 6) {
+               /* enable FBC WM (except on ILK, where it must remain off) */
+               I915_WRITE(DISP_ARB_CTL,
+                          I915_READ(DISP_ARB_CTL) & ~DISP_FBC_WM_DIS);
        }
 
        if (display_wm > display->max_wm) {
@@ -2016,31 +2081,558 @@ static void ivybridge_update_wm(struct drm_device *dev)
                   cursor_wm);
 }
 
-static void
-haswell_update_linetime_wm(struct drm_device *dev, int pipe,
-                                struct drm_display_mode *mode)
+static uint32_t hsw_wm_get_pixel_rate(struct drm_device *dev,
+                                     struct drm_crtc *crtc)
+{
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       uint32_t pixel_rate, pfit_size;
+
+       pixel_rate = intel_crtc->config.adjusted_mode.clock;
+
+       /* We only use IF-ID interlacing. If we ever use PF-ID we'll need to
+        * adjust the pixel_rate here. */
+
+       pfit_size = intel_crtc->config.pch_pfit.size;
+       if (pfit_size) {
+               uint64_t pipe_w, pipe_h, pfit_w, pfit_h;
+
+               pipe_w = intel_crtc->config.requested_mode.hdisplay;
+               pipe_h = intel_crtc->config.requested_mode.vdisplay;
+               pfit_w = (pfit_size >> 16) & 0xFFFF;
+               pfit_h = pfit_size & 0xFFFF;
+               if (pipe_w < pfit_w)
+                       pipe_w = pfit_w;
+               if (pipe_h < pfit_h)
+                       pipe_h = pfit_h;
+
+               pixel_rate = div_u64((uint64_t) pixel_rate * pipe_w * pipe_h,
+                                    pfit_w * pfit_h);
+       }
+
+       return pixel_rate;
+}
+
+static uint32_t hsw_wm_method1(uint32_t pixel_rate, uint8_t bytes_per_pixel,
+                              uint32_t latency)
+{
+       uint64_t ret;
+
+       ret = (uint64_t) pixel_rate * bytes_per_pixel * latency;
+       ret = DIV_ROUND_UP_ULL(ret, 64 * 10000) + 2;
+
+       return ret;
+}
+
+static uint32_t hsw_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal,
+                              uint32_t horiz_pixels, uint8_t bytes_per_pixel,
+                              uint32_t latency)
+{
+       uint32_t ret;
+
+       ret = (latency * pixel_rate) / (pipe_htotal * 10000);
+       ret = (ret + 1) * horiz_pixels * bytes_per_pixel;
+       ret = DIV_ROUND_UP(ret, 64) + 2;
+       return ret;
+}
+
+static uint32_t hsw_wm_fbc(uint32_t pri_val, uint32_t horiz_pixels,
+                          uint8_t bytes_per_pixel)
+{
+       return DIV_ROUND_UP(pri_val * 64, horiz_pixels * bytes_per_pixel) + 2;
+}
+
+struct hsw_pipe_wm_parameters {
+       bool active;
+       bool sprite_enabled;
+       uint8_t pri_bytes_per_pixel;
+       uint8_t spr_bytes_per_pixel;
+       uint8_t cur_bytes_per_pixel;
+       uint32_t pri_horiz_pixels;
+       uint32_t spr_horiz_pixels;
+       uint32_t cur_horiz_pixels;
+       uint32_t pipe_htotal;
+       uint32_t pixel_rate;
+};
+
+struct hsw_wm_maximums {
+       uint16_t pri;
+       uint16_t spr;
+       uint16_t cur;
+       uint16_t fbc;
+};
+
+struct hsw_lp_wm_result {
+       bool enable;
+       bool fbc_enable;
+       uint32_t pri_val;
+       uint32_t spr_val;
+       uint32_t cur_val;
+       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 hsw_data_buf_partitioning {
+       HSW_DATA_BUF_PART_1_2,
+       HSW_DATA_BUF_PART_5_6,
+};
+
+/* For both WM_PIPE and WM_LP. */
+static uint32_t hsw_compute_pri_wm(struct hsw_pipe_wm_parameters *params,
+                                  uint32_t mem_value,
+                                  bool is_lp)
+{
+       uint32_t method1, method2;
+
+       /* TODO: for now, assume the primary plane is always enabled. */
+       if (!params->active)
+               return 0;
+
+       method1 = hsw_wm_method1(params->pixel_rate,
+                                params->pri_bytes_per_pixel,
+                                mem_value);
+
+       if (!is_lp)
+               return method1;
+
+       method2 = hsw_wm_method2(params->pixel_rate,
+                                params->pipe_htotal,
+                                params->pri_horiz_pixels,
+                                params->pri_bytes_per_pixel,
+                                mem_value);
+
+       return min(method1, method2);
+}
+
+/* For both WM_PIPE and WM_LP. */
+static uint32_t hsw_compute_spr_wm(struct hsw_pipe_wm_parameters *params,
+                                  uint32_t mem_value)
+{
+       uint32_t method1, method2;
+
+       if (!params->active || !params->sprite_enabled)
+               return 0;
+
+       method1 = hsw_wm_method1(params->pixel_rate,
+                                params->spr_bytes_per_pixel,
+                                mem_value);
+       method2 = hsw_wm_method2(params->pixel_rate,
+                                params->pipe_htotal,
+                                params->spr_horiz_pixels,
+                                params->spr_bytes_per_pixel,
+                                mem_value);
+       return min(method1, method2);
+}
+
+/* For both WM_PIPE and WM_LP. */
+static uint32_t hsw_compute_cur_wm(struct hsw_pipe_wm_parameters *params,
+                                  uint32_t mem_value)
+{
+       if (!params->active)
+               return 0;
+
+       return hsw_wm_method2(params->pixel_rate,
+                             params->pipe_htotal,
+                             params->cur_horiz_pixels,
+                             params->cur_bytes_per_pixel,
+                             mem_value);
+}
+
+/* Only for WM_LP. */
+static uint32_t hsw_compute_fbc_wm(struct hsw_pipe_wm_parameters *params,
+                                  uint32_t pri_val,
+                                  uint32_t mem_value)
+{
+       if (!params->active)
+               return 0;
+
+       return hsw_wm_fbc(pri_val,
+                         params->pri_horiz_pixels,
+                         params->pri_bytes_per_pixel);
+}
+
+static bool hsw_compute_lp_wm(uint32_t mem_value, struct hsw_wm_maximums *max,
+                             struct hsw_pipe_wm_parameters *params,
+                             struct hsw_lp_wm_result *result)
+{
+       enum pipe pipe;
+       uint32_t pri_val[3], spr_val[3], cur_val[3], fbc_val[3];
+
+       for (pipe = PIPE_A; pipe <= PIPE_C; pipe++) {
+               struct hsw_pipe_wm_parameters *p = &params[pipe];
+
+               pri_val[pipe] = hsw_compute_pri_wm(p, mem_value, true);
+               spr_val[pipe] = hsw_compute_spr_wm(p, mem_value);
+               cur_val[pipe] = hsw_compute_cur_wm(p, mem_value);
+               fbc_val[pipe] = hsw_compute_fbc_wm(p, pri_val[pipe], mem_value);
+       }
+
+       result->pri_val = max3(pri_val[0], pri_val[1], pri_val[2]);
+       result->spr_val = max3(spr_val[0], spr_val[1], spr_val[2]);
+       result->cur_val = max3(cur_val[0], cur_val[1], cur_val[2]);
+       result->fbc_val = max3(fbc_val[0], fbc_val[1], fbc_val[2]);
+
+       if (result->fbc_val > max->fbc) {
+               result->fbc_enable = false;
+               result->fbc_val = 0;
+       } else {
+               result->fbc_enable = true;
+       }
+
+       result->enable = result->pri_val <= max->pri &&
+                        result->spr_val <= max->spr &&
+                        result->cur_val <= max->cur;
+       return result->enable;
+}
+
+static uint32_t hsw_compute_wm_pipe(struct drm_i915_private *dev_priv,
+                                   uint32_t mem_value, enum pipe pipe,
+                                   struct hsw_pipe_wm_parameters *params)
+{
+       uint32_t pri_val, cur_val, spr_val;
+
+       pri_val = hsw_compute_pri_wm(params, mem_value, false);
+       spr_val = hsw_compute_spr_wm(params, mem_value);
+       cur_val = hsw_compute_cur_wm(params, mem_value);
+
+       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)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 temp;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode;
+       u32 linetime, ips_linetime;
 
-       temp = I915_READ(PIPE_WM_LINETIME(pipe));
-       temp &= ~PIPE_WM_LINETIME_MASK;
+       if (!intel_crtc_active(crtc))
+               return 0;
 
        /* The WM are computed with base on how long it takes to fill a single
         * row at the given clock rate, multiplied by 8.
         * */
-       temp |= PIPE_WM_LINETIME_TIME(
-               ((mode->crtc_hdisplay * 1000) / mode->clock) * 8);
+       linetime = DIV_ROUND_CLOSEST(mode->htotal * 1000 * 8, mode->clock);
+       ips_linetime = DIV_ROUND_CLOSEST(mode->htotal * 1000 * 8,
+                                        intel_ddi_get_cdclk_freq(dev_priv));
 
-       /* IPS watermarks are only used by pipe A, and are ignored by
-        * pipes B and C.  They are calculated similarly to the common
-        * linetime values, except that we are using CD clock frequency
-        * in MHz instead of pixel rate for the division.
-        *
-        * This is a placeholder for the IPS watermark calculation code.
-        */
+       return PIPE_WM_LINETIME_IPS_LINETIME(ips_linetime) |
+              PIPE_WM_LINETIME_TIME(linetime);
+}
+
+static void hsw_compute_wm_parameters(struct drm_device *dev,
+                                     struct hsw_pipe_wm_parameters *params,
+                                     uint32_t *wm,
+                                     struct hsw_wm_maximums *lp_max_1_2,
+                                     struct hsw_wm_maximums *lp_max_5_6)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc;
+       struct drm_plane *plane;
+       uint64_t sskpd = I915_READ64(MCH_SSKPD);
+       enum pipe pipe;
+       int pipes_active = 0, sprites_enabled = 0;
+
+       if ((sskpd >> 56) & 0xFF)
+               wm[0] = (sskpd >> 56) & 0xFF;
+       else
+               wm[0] = sskpd & 0xF;
+       wm[1] = ((sskpd >> 4) & 0xFF) * 5;
+       wm[2] = ((sskpd >> 12) & 0xFF) * 5;
+       wm[3] = ((sskpd >> 20) & 0x1FF) * 5;
+       wm[4] = ((sskpd >> 32) & 0x1FF) * 5;
+
+       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;
+
+               pipes_active++;
+
+               p->pipe_htotal = intel_crtc->config.adjusted_mode.htotal;
+               p->pixel_rate = hsw_wm_get_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->cur_horiz_pixels = 64;
+       }
+
+       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];
+
+               p->sprite_enabled = intel_plane->wm.enable;
+               p->spr_bytes_per_pixel = intel_plane->wm.bytes_per_pixel;
+               p->spr_horiz_pixels = intel_plane->wm.horiz_pixels;
+
+               if (p->sprite_enabled)
+                       sprites_enabled++;
+       }
 
-       I915_WRITE(PIPE_WM_LINETIME(pipe), temp);
+       if (pipes_active > 1) {
+               lp_max_1_2->pri = lp_max_5_6->pri = sprites_enabled ? 128 : 256;
+               lp_max_1_2->spr = lp_max_5_6->spr = 128;
+               lp_max_1_2->cur = lp_max_5_6->cur = 64;
+       } else {
+               lp_max_1_2->pri = sprites_enabled ? 384 : 768;
+               lp_max_5_6->pri = sprites_enabled ? 128 : 768;
+               lp_max_1_2->spr = 384;
+               lp_max_5_6->spr = 640;
+               lp_max_1_2->cur = lp_max_5_6->cur = 255;
+       }
+       lp_max_1_2->fbc = lp_max_5_6->fbc = 15;
+}
+
+static void hsw_compute_wm_results(struct drm_device *dev,
+                                  struct hsw_pipe_wm_parameters *params,
+                                  uint32_t *wm,
+                                  struct hsw_wm_maximums *lp_maximums,
+                                  struct hsw_wm_values *results)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc;
+       struct hsw_lp_wm_result lp_results[4] = {};
+       enum pipe pipe;
+       int level, max_level, wm_lp;
+
+       for (level = 1; level <= 4; level++)
+               if (!hsw_compute_lp_wm(wm[level], lp_maximums, params,
+                                      &lp_results[level - 1]))
+                       break;
+       max_level = level - 1;
+
+       /* The spec says it is preferred to disable FBC WMs instead of disabling
+        * a WM level. */
+       results->enable_fbc_wm = true;
+       for (level = 1; level <= max_level; level++) {
+               if (!lp_results[level - 1].fbc_enable) {
+                       results->enable_fbc_wm = false;
+                       break;
+               }
+       }
+
+       memset(results, 0, sizeof(*results));
+       for (wm_lp = 1; wm_lp <= 3; wm_lp++) {
+               const struct hsw_lp_wm_result *r;
+
+               level = (max_level == 4 && wm_lp > 1) ? wm_lp + 1 : wm_lp;
+               if (level > max_level)
+                       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_spr[wm_lp - 1] = r->spr_val;
+       }
+
+       for_each_pipe(pipe)
+               results->wm_pipe[pipe] = hsw_compute_wm_pipe(dev_priv, wm[0],
+                                                            pipe,
+                                                            &params[pipe]);
+
+       for_each_pipe(pipe) {
+               crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+               results->wm_linetime[pipe] = hsw_compute_linetime_wm(dev, crtc);
+       }
+}
+
+/* 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. */
+struct hsw_wm_values *hsw_find_best_result(struct hsw_wm_values *r1,
+                                          struct hsw_wm_values *r2)
+{
+       int i, val_r1 = 0, val_r2 = 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;
+       }
+
+       if (val_r1 == val_r2) {
+               if (r2->enable_fbc_wm && !r1->enable_fbc_wm)
+                       return r2;
+               else
+                       return r1;
+       } else if (val_r1 > val_r2) {
+               return r1;
+       } else {
+               return r2;
+       }
+}
+
+/*
+ * 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 hsw_data_buf_partitioning partitioning)
+{
+       struct hsw_wm_values previous;
+       uint32_t val;
+       enum hsw_data_buf_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) ?
+                           HSW_DATA_BUF_PART_5_6 : HSW_DATA_BUF_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)
+               return;
+
+       if (previous.wm_lp[2] != 0)
+               I915_WRITE(WM3_LP_ILK, 0);
+       if (previous.wm_lp[1] != 0)
+               I915_WRITE(WM2_LP_ILK, 0);
+       if (previous.wm_lp[0] != 0)
+               I915_WRITE(WM1_LP_ILK, 0);
+
+       if (previous.wm_pipe[0] != results->wm_pipe[0])
+               I915_WRITE(WM0_PIPEA_ILK, results->wm_pipe[0]);
+       if (previous.wm_pipe[1] != results->wm_pipe[1])
+               I915_WRITE(WM0_PIPEB_ILK, results->wm_pipe[1]);
+       if (previous.wm_pipe[2] != results->wm_pipe[2])
+               I915_WRITE(WM0_PIPEC_IVB, results->wm_pipe[2]);
+
+       if (previous.wm_linetime[0] != results->wm_linetime[0])
+               I915_WRITE(PIPE_WM_LINETIME(PIPE_A), results->wm_linetime[0]);
+       if (previous.wm_linetime[1] != results->wm_linetime[1])
+               I915_WRITE(PIPE_WM_LINETIME(PIPE_B), results->wm_linetime[1]);
+       if (previous.wm_linetime[2] != results->wm_linetime[2])
+               I915_WRITE(PIPE_WM_LINETIME(PIPE_C), results->wm_linetime[2]);
+
+       if (prev_partitioning != partitioning) {
+               val = I915_READ(WM_MISC);
+               if (partitioning == HSW_DATA_BUF_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) {
+               val = I915_READ(DISP_ARB_CTL);
+               if (results->enable_fbc_wm)
+                       val &= ~DISP_FBC_WM_DIS;
+               else
+                       val |= DISP_FBC_WM_DIS;
+               I915_WRITE(DISP_ARB_CTL, val);
+       }
+
+       if (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])
+               I915_WRITE(WM2S_LP_IVB, results->wm_lp_spr[1]);
+       if (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)
+               I915_WRITE(WM1_LP_ILK, results->wm_lp[0]);
+       if (results->wm_lp[1] != 0)
+               I915_WRITE(WM2_LP_ILK, results->wm_lp[1]);
+       if (results->wm_lp[2] != 0)
+               I915_WRITE(WM3_LP_ILK, results->wm_lp[2]);
+}
+
+static void haswell_update_wm(struct drm_device *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;
+       uint32_t wm[5];
+       enum hsw_data_buf_partitioning partitioning;
+
+       hsw_compute_wm_parameters(dev, params, wm, &lp_max_1_2, &lp_max_5_6);
+
+       hsw_compute_wm_results(dev, params, wm, &lp_max_1_2, &results_1_2);
+       if (lp_max_1_2.pri != lp_max_5_6.pri) {
+               hsw_compute_wm_results(dev, params, wm, &lp_max_5_6,
+                                      &results_5_6);
+               best_results = hsw_find_best_result(&results_1_2, &results_5_6);
+       } else {
+               best_results = &results_1_2;
+       }
+
+       partitioning = (best_results == &results_1_2) ?
+                      HSW_DATA_BUF_PART_1_2 : HSW_DATA_BUF_PART_5_6;
+
+       hsw_write_wm_values(dev_priv, best_results, partitioning);
+}
+
+static void haswell_update_sprite_wm(struct drm_device *dev, int pipe,
+                                    uint32_t sprite_width, int pixel_size,
+                                    bool enable)
+{
+       struct drm_plane *plane;
+
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+               struct intel_plane *intel_plane = to_intel_plane(plane);
+
+               if (intel_plane->pipe == pipe) {
+                       intel_plane->wm.enable = enable;
+                       intel_plane->wm.horiz_pixels = sprite_width + 1;
+                       intel_plane->wm.bytes_per_pixel = pixel_size;
+                       break;
+               }
+       }
+
+       haswell_update_wm(dev);
 }
 
 static bool
@@ -2120,7 +2712,8 @@ sandybridge_compute_sprite_srwm(struct drm_device *dev, int plane,
 }
 
 static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe,
-                                        uint32_t sprite_width, int pixel_size)
+                                        uint32_t sprite_width, int pixel_size,
+                                        bool enable)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int latency = SNB_READ_WM0_LATENCY() * 100;     /* In unit 0.1us */
@@ -2128,6 +2721,9 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe,
        int sprite_wm, reg;
        int ret;
 
+       if (!enable)
+               return;
+
        switch (pipe) {
        case 0:
                reg = WM0_PIPEA_ILK;
@@ -2146,15 +2742,15 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe,
                                            &sandybridge_display_wm_info,
                                            latency, &sprite_wm);
        if (!ret) {
-               DRM_DEBUG_KMS("failed to compute sprite wm for pipe %d\n",
-                             pipe);
+               DRM_DEBUG_KMS("failed to compute sprite wm for pipe %c\n",
+                             pipe_name(pipe));
                return;
        }
 
        val = I915_READ(reg);
        val &= ~WM0_PIPE_SPRITE_MASK;
        I915_WRITE(reg, val | (sprite_wm << WM0_PIPE_SPRITE_SHIFT));
-       DRM_DEBUG_KMS("sprite watermarks For pipe %d - %d\n", pipe, sprite_wm);
+       DRM_DEBUG_KMS("sprite watermarks For pipe %c - %d\n", pipe_name(pipe), sprite_wm);
 
 
        ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width,
@@ -2163,8 +2759,8 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe,
                                              SNB_READ_WM1_LATENCY() * 500,
                                              &sprite_wm);
        if (!ret) {
-               DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %d\n",
-                             pipe);
+               DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %c\n",
+                             pipe_name(pipe));
                return;
        }
        I915_WRITE(WM1S_LP_ILK, sprite_wm);
@@ -2179,8 +2775,8 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe,
                                              SNB_READ_WM2_LATENCY() * 500,
                                              &sprite_wm);
        if (!ret) {
-               DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %d\n",
-                             pipe);
+               DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %c\n",
+                             pipe_name(pipe));
                return;
        }
        I915_WRITE(WM2S_LP_IVB, sprite_wm);
@@ -2191,8 +2787,8 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe,
                                              SNB_READ_WM3_LATENCY() * 500,
                                              &sprite_wm);
        if (!ret) {
-               DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %d\n",
-                             pipe);
+               DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %c\n",
+                             pipe_name(pipe));
                return;
        }
        I915_WRITE(WM3S_LP_IVB, sprite_wm);
@@ -2238,23 +2834,15 @@ void intel_update_watermarks(struct drm_device *dev)
                dev_priv->display.update_wm(dev);
 }
 
-void intel_update_linetime_watermarks(struct drm_device *dev,
-               int pipe, struct drm_display_mode *mode)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       if (dev_priv->display.update_linetime_wm)
-               dev_priv->display.update_linetime_wm(dev, pipe, mode);
-}
-
 void intel_update_sprite_watermarks(struct drm_device *dev, int pipe,
-                                   uint32_t sprite_width, int pixel_size)
+                                   uint32_t sprite_width, int pixel_size,
+                                   bool enable)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        if (dev_priv->display.update_sprite_wm)
                dev_priv->display.update_sprite_wm(dev, pipe, sprite_width,
-                                                  pixel_size);
+                                                  pixel_size, enable);
 }
 
 static struct drm_i915_gem_object *
@@ -2481,6 +3069,67 @@ void gen6_set_rps(struct drm_device *dev, u8 val)
        trace_intel_gpu_freq_change(val * 50);
 }
 
+/*
+ * Wait until the previous freq change has completed,
+ * or the timeout elapsed, and then update our notion
+ * of the current GPU frequency.
+ */
+static void vlv_update_rps_cur_delay(struct drm_i915_private *dev_priv)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(10);
+       u32 pval;
+
+       WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
+
+       do {
+               pval = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
+               if (time_after(jiffies, timeout)) {
+                       DRM_DEBUG_DRIVER("timed out waiting for Punit\n");
+                       break;
+               }
+               udelay(10);
+       } while (pval & 1);
+
+       pval >>= 8;
+
+       if (pval != dev_priv->rps.cur_delay)
+               DRM_DEBUG_DRIVER("Punit overrode GPU freq: %d MHz (%u) requested, but got %d Mhz (%u)\n",
+                                vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.cur_delay),
+                                dev_priv->rps.cur_delay,
+                                vlv_gpu_freq(dev_priv->mem_freq, pval), pval);
+
+       dev_priv->rps.cur_delay = pval;
+}
+
+void valleyview_set_rps(struct drm_device *dev, u8 val)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       gen6_rps_limits(dev_priv, &val);
+
+       WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
+       WARN_ON(val > dev_priv->rps.max_delay);
+       WARN_ON(val < dev_priv->rps.min_delay);
+
+       vlv_update_rps_cur_delay(dev_priv);
+
+       DRM_DEBUG_DRIVER("GPU freq request from %d MHz (%u) to %d MHz (%u)\n",
+                        vlv_gpu_freq(dev_priv->mem_freq,
+                                     dev_priv->rps.cur_delay),
+                        dev_priv->rps.cur_delay,
+                        vlv_gpu_freq(dev_priv->mem_freq, val), val);
+
+       if (val == dev_priv->rps.cur_delay)
+               return;
+
+       vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val);
+
+       dev_priv->rps.cur_delay = val;
+
+       trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv->mem_freq, val));
+}
+
+
 static void gen6_disable_rps(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2488,6 +3137,25 @@ static void gen6_disable_rps(struct drm_device *dev)
        I915_WRITE(GEN6_RC_CONTROL, 0);
        I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
        I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
+       I915_WRITE(GEN6_PMIER, I915_READ(GEN6_PMIER) & ~GEN6_PM_RPS_EVENTS);
+       /* Complete PM interrupt masking here doesn't race with the rps work
+        * item again unmasking PM interrupts because that is using a different
+        * register (PMIMR) to mask PM interrupts. The only risk is in leaving
+        * stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */
+
+       spin_lock_irq(&dev_priv->rps.lock);
+       dev_priv->rps.pm_iir = 0;
+       spin_unlock_irq(&dev_priv->rps.lock);
+
+       I915_WRITE(GEN6_PMIIR, GEN6_PM_RPS_EVENTS);
+}
+
+static void valleyview_disable_rps(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       I915_WRITE(GEN6_RC_CONTROL, 0);
+       I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
        I915_WRITE(GEN6_PMIER, 0);
        /* Complete PM interrupt masking here doesn't race with the rps work
         * item again unmasking PM interrupts because that is using a different
@@ -2499,6 +3167,11 @@ static void gen6_disable_rps(struct drm_device *dev)
        spin_unlock_irq(&dev_priv->rps.lock);
 
        I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
+
+       if (dev_priv->vlv_pctx) {
+               drm_gem_object_unreference(&dev_priv->vlv_pctx->base);
+               dev_priv->vlv_pctx = NULL;
+       }
 }
 
 int intel_enable_rc6(const struct drm_device *dev)
@@ -2655,12 +3328,15 @@ static void gen6_enable_rps(struct drm_device *dev)
        gen6_set_rps(dev_priv->dev, (gt_perf_status & 0xff00) >> 8);
 
        /* requires MSI enabled */
-       I915_WRITE(GEN6_PMIER, GEN6_PM_DEFERRED_EVENTS);
+       I915_WRITE(GEN6_PMIER, I915_READ(GEN6_PMIER) | GEN6_PM_RPS_EVENTS);
        spin_lock_irq(&dev_priv->rps.lock);
-       WARN_ON(dev_priv->rps.pm_iir != 0);
-       I915_WRITE(GEN6_PMIMR, 0);
+       /* FIXME: Our interrupt enabling sequence is bonghits.
+        * dev_priv->rps.pm_iir really should be 0 here. */
+       dev_priv->rps.pm_iir = 0;
+       I915_WRITE(GEN6_PMIMR, I915_READ(GEN6_PMIMR) & ~GEN6_PM_RPS_EVENTS);
+       I915_WRITE(GEN6_PMIIR, GEN6_PM_RPS_EVENTS);
        spin_unlock_irq(&dev_priv->rps.lock);
-       /* enable all PM interrupts */
+       /* unmask all PM interrupts */
        I915_WRITE(GEN6_PMINTRMSK, 0);
 
        rc6vids = 0;
@@ -2742,6 +3418,207 @@ static void gen6_update_ring_freq(struct drm_device *dev)
        }
 }
 
+int valleyview_rps_max_freq(struct drm_i915_private *dev_priv)
+{
+       u32 val, rp0;
+
+       val = vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FREQ_FUSE);
+
+       rp0 = (val & FB_GFX_MAX_FREQ_FUSE_MASK) >> FB_GFX_MAX_FREQ_FUSE_SHIFT;
+       /* Clamp to max */
+       rp0 = min_t(u32, rp0, 0xea);
+
+       return rp0;
+}
+
+static int valleyview_rps_rpe_freq(struct drm_i915_private *dev_priv)
+{
+       u32 val, rpe;
+
+       val = vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_LO);
+       rpe = (val & FB_FMAX_VMIN_FREQ_LO_MASK) >> FB_FMAX_VMIN_FREQ_LO_SHIFT;
+       val = vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_HI);
+       rpe |= (val & FB_FMAX_VMIN_FREQ_HI_MASK) << 5;
+
+       return rpe;
+}
+
+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;
+       struct drm_i915_gem_object *pctx;
+       unsigned long pctx_paddr;
+       u32 pcbr;
+       int pctx_size = 24*1024;
+
+       pcbr = I915_READ(VLV_PCBR);
+       if (pcbr) {
+               /* BIOS set it up already, grab the pre-alloc'd space */
+               int pcbr_offset;
+
+               pcbr_offset = (pcbr & (~4095)) - dev_priv->mm.stolen_base;
+               pctx = i915_gem_object_create_stolen_for_preallocated(dev_priv->dev,
+                                                                     pcbr_offset,
+                                                                     -1,
+                                                                     pctx_size);
+               goto out;
+       }
+
+       /*
+        * From the Gunit register HAS:
+        * The Gfx driver is expected to program this register and ensure
+        * proper allocation within Gfx stolen memory.  For example, this
+        * register should be programmed such than the PCBR range does not
+        * overlap with other ranges, such as the frame buffer, protected
+        * memory, or any other relevant ranges.
+        */
+       pctx = i915_gem_object_create_stolen(dev, pctx_size);
+       if (!pctx) {
+               DRM_DEBUG("not enough stolen space for PCTX, disabling\n");
+               return;
+       }
+
+       pctx_paddr = dev_priv->mm.stolen_base + pctx->stolen->start;
+       I915_WRITE(VLV_PCBR, pctx_paddr);
+
+out:
+       dev_priv->vlv_pctx = pctx;
+}
+
+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;
+       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);
+               I915_WRITE(GTFIFODBG, gtfifodbg);
+       }
+
+       valleyview_setup_pctx(dev);
+
+       gen6_gt_force_wake_get(dev_priv);
+
+       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);
+
+       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 |
+                  GEN6_RP_DOWN_IDLE_CONT);
+
+       I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 0x00280000);
+       I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000);
+       I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25);
+
+       for_each_ring(ring, dev_priv, i)
+               I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10);
+
+       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);
+
+       val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
+       switch ((val >> 6) & 3) {
+       case 0:
+       case 1:
+               dev_priv->mem_freq = 800;
+               break;
+       case 2:
+               dev_priv->mem_freq = 1066;
+               break;
+       case 3:
+               dev_priv->mem_freq = 1333;
+               break;
+       }
+       DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq);
+
+       DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & 0x10 ? "yes" : "no");
+       DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val);
+
+       dev_priv->rps.cur_delay = (val >> 8) & 0xff;
+       DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n",
+                        vlv_gpu_freq(dev_priv->mem_freq,
+                                     dev_priv->rps.cur_delay),
+                        dev_priv->rps.cur_delay);
+
+       dev_priv->rps.max_delay = valleyview_rps_max_freq(dev_priv);
+       dev_priv->rps.hw_max = dev_priv->rps.max_delay;
+       DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n",
+                        vlv_gpu_freq(dev_priv->mem_freq,
+                                     dev_priv->rps.max_delay),
+                        dev_priv->rps.max_delay);
+
+       dev_priv->rps.rpe_delay = valleyview_rps_rpe_freq(dev_priv);
+       DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n",
+                        vlv_gpu_freq(dev_priv->mem_freq,
+                                     dev_priv->rps.rpe_delay),
+                        dev_priv->rps.rpe_delay);
+
+       dev_priv->rps.min_delay = valleyview_rps_min_freq(dev_priv);
+       DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n",
+                        vlv_gpu_freq(dev_priv->mem_freq,
+                                     dev_priv->rps.min_delay),
+                        dev_priv->rps.min_delay);
+
+       DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n",
+                        vlv_gpu_freq(dev_priv->mem_freq,
+                                     dev_priv->rps.rpe_delay),
+                        dev_priv->rps.rpe_delay);
+
+       INIT_DELAYED_WORK(&dev_priv->rps.vlv_work, vlv_rps_timer_work);
+
+       valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay);
+
+       /* requires MSI enabled */
+       I915_WRITE(GEN6_PMIER, GEN6_PM_RPS_EVENTS);
+       spin_lock_irq(&dev_priv->rps.lock);
+       WARN_ON(dev_priv->rps.pm_iir != 0);
+       I915_WRITE(GEN6_PMIMR, 0);
+       spin_unlock_irq(&dev_priv->rps.lock);
+       /* enable all PM interrupts */
+       I915_WRITE(GEN6_PMINTRMSK, 0);
+
+       gen6_gt_force_wake_put(dev_priv);
+}
+
 void ironlake_teardown_rc6(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3465,13 +4342,22 @@ void intel_disable_gt_powersave(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       /* Interrupts should be disabled already to avoid re-arming. */
+       WARN_ON(dev->irq_enabled);
+
        if (IS_IRONLAKE_M(dev)) {
                ironlake_disable_drps(dev);
                ironlake_disable_rc6(dev);
-       } else if (INTEL_INFO(dev)->gen >= 6 && !IS_VALLEYVIEW(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);
-               gen6_disable_rps(dev);
+               if (IS_VALLEYVIEW(dev))
+                       valleyview_disable_rps(dev);
+               else
+                       gen6_disable_rps(dev);
                mutex_unlock(&dev_priv->rps.hw_lock);
        }
 }
@@ -3484,8 +4370,13 @@ static void intel_gen6_powersave_work(struct work_struct *work)
        struct drm_device *dev = dev_priv->dev;
 
        mutex_lock(&dev_priv->rps.hw_lock);
-       gen6_enable_rps(dev);
-       gen6_update_ring_freq(dev);
+
+       if (IS_VALLEYVIEW(dev)) {
+               valleyview_enable_rps(dev);
+       } else {
+               gen6_enable_rps(dev);
+               gen6_update_ring_freq(dev);
+       }
        mutex_unlock(&dev_priv->rps.hw_lock);
 }
 
@@ -3497,7 +4388,7 @@ void intel_enable_gt_powersave(struct drm_device *dev)
                ironlake_enable_drps(dev);
                ironlake_enable_rc6(dev);
                intel_init_emon(dev);
-       } else if ((IS_GEN6(dev) || IS_GEN7(dev)) && !IS_VALLEYVIEW(dev)) {
+       } else if (IS_GEN6(dev) || IS_GEN7(dev)) {
                /*
                 * PCU communication is slow and this doesn't need to be
                 * done at any specific time, so do this out of our fast path
@@ -3520,6 +4411,19 @@ static void ibx_init_clock_gating(struct drm_device *dev)
        I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
 }
 
+static void g4x_disable_trickle_feed(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe;
+
+       for_each_pipe(pipe) {
+               I915_WRITE(DSPCNTR(pipe),
+                          I915_READ(DSPCNTR(pipe)) |
+                          DISPPLANE_TRICKLE_FEED_DISABLE);
+               intel_flush_display_plane(dev_priv, pipe);
+       }
+}
+
 static void ironlake_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3579,10 +4483,12 @@ static void ironlake_init_clock_gating(struct drm_device *dev)
                   _3D_CHICKEN2_WM_READ_PIPELINED << 16 |
                   _3D_CHICKEN2_WM_READ_PIPELINED);
 
-       /* WaDisableRenderCachePipelinedFlush */
+       /* WaDisableRenderCachePipelinedFlush:ilk */
        I915_WRITE(CACHE_MODE_0,
                   _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE));
 
+       g4x_disable_trickle_feed(dev);
+
        ibx_init_clock_gating(dev);
 }
 
@@ -3607,7 +4513,7 @@ static void cpt_init_clock_gating(struct drm_device *dev)
                val = I915_READ(TRANS_CHICKEN2(pipe));
                val |= TRANS_CHICKEN2_TIMING_OVERRIDE;
                val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED;
-               if (dev_priv->fdi_rx_polarity_inverted)
+               if (dev_priv->vbt.fdi_rx_polarity_inverted)
                        val |= TRANS_CHICKEN2_FDI_POLARITY_REVERSED;
                val &= ~TRANS_CHICKEN2_FRAME_START_DELAY_MASK;
                val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_COUNTER;
@@ -3637,7 +4543,6 @@ static void gen6_check_mch_setup(struct drm_device *dev)
 static void gen6_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int pipe;
        uint32_t dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE;
 
        I915_WRITE(ILK_DSPCLK_GATE_D, dspclk_gate);
@@ -3646,11 +4551,11 @@ static void gen6_init_clock_gating(struct drm_device *dev)
                   I915_READ(ILK_DISPLAY_CHICKEN2) |
                   ILK_ELPIN_409_SELECT);
 
-       /* WaDisableHiZPlanesWhenMSAAEnabled */
+       /* WaDisableHiZPlanesWhenMSAAEnabled:snb */
        I915_WRITE(_3D_CHICKEN,
                   _MASKED_BIT_ENABLE(_3D_CHICKEN_HIZ_PLANE_DISABLE_MSAA_4X_SNB));
 
-       /* WaSetupGtModeTdRowDispatch */
+       /* WaSetupGtModeTdRowDispatch:snb */
        if (IS_SNB_GT1(dev))
                I915_WRITE(GEN6_GT_MODE,
                           _MASKED_BIT_ENABLE(GEN6_TD_FOUR_ROW_DISPATCH_DISABLE));
@@ -3677,8 +4582,8 @@ static void gen6_init_clock_gating(struct drm_device *dev)
         * According to the spec, bit 11 (RCCUNIT) must also be set,
         * but we didn't debug actual testcases to find it out.
         *
-        * Also apply WaDisableVDSUnitClockGating and
-        * WaDisableRCPBUnitClockGating.
+        * Also apply WaDisableVDSUnitClockGating:snb and
+        * WaDisableRCPBUnitClockGating:snb.
         */
        I915_WRITE(GEN6_UCGCTL2,
                   GEN7_VDSUNIT_CLOCK_GATE_DISABLE |
@@ -3709,16 +4614,11 @@ static void gen6_init_clock_gating(struct drm_device *dev)
                   ILK_DPARBUNIT_CLOCK_GATE_ENABLE  |
                   ILK_DPFDUNIT_CLOCK_GATE_ENABLE);
 
-       /* WaMbcDriverBootEnable */
+       /* WaMbcDriverBootEnable:snb */
        I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) |
                   GEN6_MBCTL_ENABLE_BOOT_FETCH);
 
-       for_each_pipe(pipe) {
-               I915_WRITE(DSPCNTR(pipe),
-                          I915_READ(DSPCNTR(pipe)) |
-                          DISPPLANE_TRICKLE_FEED_DISABLE);
-               intel_flush_display_plane(dev_priv, pipe);
-       }
+       g4x_disable_trickle_feed(dev);
 
        /* The default value should be 0x200 according to docs, but the two
         * platforms I checked have a 0 for this. (Maybe BIOS overrides?) */
@@ -3739,7 +4639,6 @@ static void gen7_setup_fixed_func_scheduler(struct drm_i915_private *dev_priv)
        reg |= GEN7_FF_VS_SCHED_HW;
        reg |= GEN7_FF_DS_SCHED_HW;
 
-       /* WaVSRefCountFullforceMissDisable */
        if (IS_HASWELL(dev_priv->dev))
                reg &= ~GEN7_FF_VS_REF_CNT_FFME;
 
@@ -3758,65 +4657,72 @@ static void lpt_init_clock_gating(struct drm_device *dev)
                I915_WRITE(SOUTH_DSPCLK_GATE_D,
                           I915_READ(SOUTH_DSPCLK_GATE_D) |
                           PCH_LP_PARTITION_LEVEL_DISABLE);
+
+       /* WADPOClockGatingDisable:hsw */
+       I915_WRITE(_TRANSA_CHICKEN1,
+                  I915_READ(_TRANSA_CHICKEN1) |
+                  TRANS_CHICKEN1_DP0UNIT_GC_DISABLE);
+}
+
+static void lpt_suspend_hw(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) {
+               uint32_t val = I915_READ(SOUTH_DSPCLK_GATE_D);
+
+               val &= ~PCH_LP_PARTITION_LEVEL_DISABLE;
+               I915_WRITE(SOUTH_DSPCLK_GATE_D, val);
+       }
 }
 
 static void haswell_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int pipe;
 
        I915_WRITE(WM3_LP_ILK, 0);
        I915_WRITE(WM2_LP_ILK, 0);
        I915_WRITE(WM1_LP_ILK, 0);
 
        /* According to the spec, bit 13 (RCZUNIT) must be set on IVB.
-        * This implements the WaDisableRCZUnitClockGating workaround.
+        * This implements the WaDisableRCZUnitClockGating:hsw workaround.
         */
        I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE);
 
-       /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */
+       /* Apply the WaDisableRHWOOptimizationForRenderHang:hsw workaround. */
        I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1,
                   GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC);
 
-       /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */
+       /* WaApplyL3ControlAndL3ChickenMode:hsw */
        I915_WRITE(GEN7_L3CNTLREG1,
                        GEN7_WA_FOR_GEN7_L3_CONTROL);
        I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER,
                        GEN7_WA_L3_CHICKEN_MODE);
 
-       /* This is required by WaCatErrorRejectionIssue */
+       /* This is required by WaCatErrorRejectionIssue:hsw */
        I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG,
                        I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) |
                        GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB);
 
-       for_each_pipe(pipe) {
-               I915_WRITE(DSPCNTR(pipe),
-                          I915_READ(DSPCNTR(pipe)) |
-                          DISPPLANE_TRICKLE_FEED_DISABLE);
-               intel_flush_display_plane(dev_priv, pipe);
-       }
+       g4x_disable_trickle_feed(dev);
 
+       /* WaVSRefCountFullforceMissDisable:hsw */
        gen7_setup_fixed_func_scheduler(dev_priv);
 
-       /* WaDisable4x2SubspanOptimization */
+       /* WaDisable4x2SubspanOptimization:hsw */
        I915_WRITE(CACHE_MODE_1,
                   _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE));
 
-       /* WaMbcDriverBootEnable */
+       /* WaMbcDriverBootEnable:hsw */
        I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) |
                   GEN6_MBCTL_ENABLE_BOOT_FETCH);
 
-       /* WaSwitchSolVfFArbitrationPriority */
+       /* WaSwitchSolVfFArbitrationPriority:hsw */
        I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL);
 
-       /* XXX: This is a workaround for early silicon revisions and should be
-        * removed later.
-        */
-       I915_WRITE(WM_DBG,
-                       I915_READ(WM_DBG) |
-                       WM_DBG_DISALLOW_MULTIPLE_LP |
-                       WM_DBG_DISALLOW_SPRITE |
-                       WM_DBG_DISALLOW_MAXFIFO);
+       /* WaRsPkgCStateDisplayPMReq:hsw */
+       I915_WRITE(CHICKEN_PAR1_1,
+                  I915_READ(CHICKEN_PAR1_1) | FORCE_ARB_IDLE_PLANES);
 
        lpt_init_clock_gating(dev);
 }
@@ -3824,7 +4730,6 @@ static void haswell_init_clock_gating(struct drm_device *dev)
 static void ivybridge_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int pipe;
        uint32_t snpcr;
 
        I915_WRITE(WM3_LP_ILK, 0);
@@ -3833,16 +4738,16 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
 
        I915_WRITE(ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE);
 
-       /* WaDisableEarlyCull */
+       /* WaDisableEarlyCull:ivb */
        I915_WRITE(_3D_CHICKEN3,
                   _MASKED_BIT_ENABLE(_3D_CHICKEN_SF_DISABLE_OBJEND_CULL));
 
-       /* WaDisableBackToBackFlipFix */
+       /* WaDisableBackToBackFlipFix:ivb */
        I915_WRITE(IVB_CHICKEN3,
                   CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE |
                   CHICKEN3_DGMG_DONE_FIX_DISABLE);
 
-       /* WaDisablePSDDualDispatchEnable */
+       /* WaDisablePSDDualDispatchEnable:ivb */
        if (IS_IVB_GT1(dev))
                I915_WRITE(GEN7_HALF_SLICE_CHICKEN1,
                           _MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE));
@@ -3850,11 +4755,11 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
                I915_WRITE(GEN7_HALF_SLICE_CHICKEN1_GT2,
                           _MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE));
 
-       /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */
+       /* Apply the WaDisableRHWOOptimizationForRenderHang:ivb workaround. */
        I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1,
                   GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC);
 
-       /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */
+       /* WaApplyL3ControlAndL3ChickenMode:ivb */
        I915_WRITE(GEN7_L3CNTLREG1,
                        GEN7_WA_FOR_GEN7_L3_CONTROL);
        I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER,
@@ -3867,7 +4772,7 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
                           _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE));
 
 
-       /* WaForceL3Serialization */
+       /* WaForceL3Serialization:ivb */
        I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) &
                   ~L3SQ_URB_READ_CAM_MATCH_DISABLE);
 
@@ -3882,31 +4787,27 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
         * but we didn't debug actual testcases to find it out.
         *
         * According to the spec, bit 13 (RCZUNIT) must be set on IVB.
-        * This implements the WaDisableRCZUnitClockGating workaround.
+        * This implements the WaDisableRCZUnitClockGating:ivb workaround.
         */
        I915_WRITE(GEN6_UCGCTL2,
                   GEN6_RCZUNIT_CLOCK_GATE_DISABLE |
                   GEN6_RCCUNIT_CLOCK_GATE_DISABLE);
 
-       /* This is required by WaCatErrorRejectionIssue */
+       /* This is required by WaCatErrorRejectionIssue:ivb */
        I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG,
                        I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) |
                        GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB);
 
-       for_each_pipe(pipe) {
-               I915_WRITE(DSPCNTR(pipe),
-                          I915_READ(DSPCNTR(pipe)) |
-                          DISPPLANE_TRICKLE_FEED_DISABLE);
-               intel_flush_display_plane(dev_priv, pipe);
-       }
+       g4x_disable_trickle_feed(dev);
 
-       /* WaMbcDriverBootEnable */
+       /* WaMbcDriverBootEnable:ivb */
        I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) |
                   GEN6_MBCTL_ENABLE_BOOT_FETCH);
 
+       /* WaVSRefCountFullforceMissDisable:ivb */
        gen7_setup_fixed_func_scheduler(dev_priv);
 
-       /* WaDisable4x2SubspanOptimization */
+       /* WaDisable4x2SubspanOptimization:ivb */
        I915_WRITE(CACHE_MODE_1,
                   _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE));
 
@@ -3924,54 +4825,45 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
 static void valleyview_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int pipe;
-
-       I915_WRITE(WM3_LP_ILK, 0);
-       I915_WRITE(WM2_LP_ILK, 0);
-       I915_WRITE(WM1_LP_ILK, 0);
 
-       I915_WRITE(ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE);
+       I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE);
 
-       /* WaDisableEarlyCull */
+       /* WaDisableEarlyCull:vlv */
        I915_WRITE(_3D_CHICKEN3,
                   _MASKED_BIT_ENABLE(_3D_CHICKEN_SF_DISABLE_OBJEND_CULL));
 
-       /* WaDisableBackToBackFlipFix */
+       /* WaDisableBackToBackFlipFix:vlv */
        I915_WRITE(IVB_CHICKEN3,
                   CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE |
                   CHICKEN3_DGMG_DONE_FIX_DISABLE);
 
-       /* WaDisablePSDDualDispatchEnable */
+       /* WaDisablePSDDualDispatchEnable:vlv */
        I915_WRITE(GEN7_HALF_SLICE_CHICKEN1,
                   _MASKED_BIT_ENABLE(GEN7_MAX_PS_THREAD_DEP |
                                      GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE));
 
-       /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */
+       /* Apply the WaDisableRHWOOptimizationForRenderHang:vlv workaround. */
        I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1,
                   GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC);
 
-       /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */
+       /* WaApplyL3ControlAndL3ChickenMode:vlv */
        I915_WRITE(GEN7_L3CNTLREG1, I915_READ(GEN7_L3CNTLREG1) | GEN7_L3AGDIS);
        I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, GEN7_WA_L3_CHICKEN_MODE);
 
-       /* WaForceL3Serialization */
+       /* WaForceL3Serialization:vlv */
        I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) &
                   ~L3SQ_URB_READ_CAM_MATCH_DISABLE);
 
-       /* WaDisableDopClockGating */
+       /* WaDisableDopClockGating:vlv */
        I915_WRITE(GEN7_ROW_CHICKEN2,
                   _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE));
 
-       /* WaForceL3Serialization */
-       I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) &
-                  ~L3SQ_URB_READ_CAM_MATCH_DISABLE);
-
-       /* This is required by WaCatErrorRejectionIssue */
+       /* This is required by WaCatErrorRejectionIssue:vlv */
        I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG,
                   I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) |
                   GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB);
 
-       /* WaMbcDriverBootEnable */
+       /* WaMbcDriverBootEnable:vlv */
        I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) |
                   GEN6_MBCTL_ENABLE_BOOT_FETCH);
 
@@ -3987,10 +4879,10 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
         * but we didn't debug actual testcases to find it out.
         *
         * According to the spec, bit 13 (RCZUNIT) must be set on IVB.
-        * This implements the WaDisableRCZUnitClockGating workaround.
+        * This implements the WaDisableRCZUnitClockGating:vlv workaround.
         *
-        * Also apply WaDisableVDSUnitClockGating and
-        * WaDisableRCPBUnitClockGating.
+        * Also apply WaDisableVDSUnitClockGating:vlv and
+        * WaDisableRCPBUnitClockGating:vlv.
         */
        I915_WRITE(GEN6_UCGCTL2,
                   GEN7_VDSUNIT_CLOCK_GATE_DISABLE |
@@ -4001,18 +4893,13 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
 
        I915_WRITE(GEN7_UCGCTL4, GEN7_L3BANK2X_CLOCK_GATE_DISABLE);
 
-       for_each_pipe(pipe) {
-               I915_WRITE(DSPCNTR(pipe),
-                          I915_READ(DSPCNTR(pipe)) |
-                          DISPPLANE_TRICKLE_FEED_DISABLE);
-               intel_flush_display_plane(dev_priv, pipe);
-       }
+       I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE);
 
        I915_WRITE(CACHE_MODE_1,
                   _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE));
 
        /*
-        * WaDisableVLVClockGating_VBIIssue
+        * WaDisableVLVClockGating_VBIIssue:vlv
         * Disable clock gating on th GCFG unit to prevent a delay
         * in the reporting of vblank events.
         */
@@ -4048,6 +4935,8 @@ static void g4x_init_clock_gating(struct drm_device *dev)
        /* WaDisableRenderCachePipelinedFlush */
        I915_WRITE(CACHE_MODE_0,
                   _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE));
+
+       g4x_disable_trickle_feed(dev);
 }
 
 static void crestline_init_clock_gating(struct drm_device *dev)
@@ -4059,6 +4948,8 @@ static void crestline_init_clock_gating(struct drm_device *dev)
        I915_WRITE(DSPCLK_GATE_D, 0);
        I915_WRITE(RAMCLK_GATE_D, 0);
        I915_WRITE16(DEUC, 0);
+       I915_WRITE(MI_ARB_STATE,
+                  _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE));
 }
 
 static void broadwater_init_clock_gating(struct drm_device *dev)
@@ -4071,6 +4962,8 @@ static void broadwater_init_clock_gating(struct drm_device *dev)
                   I965_ISC_CLOCK_GATE_DISABLE |
                   I965_FBC_CLOCK_GATE_DISABLE);
        I915_WRITE(RENCLK_GATE_D2, 0);
+       I915_WRITE(MI_ARB_STATE,
+                  _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE));
 }
 
 static void gen3_init_clock_gating(struct drm_device *dev)
@@ -4110,34 +5003,50 @@ void intel_init_clock_gating(struct drm_device *dev)
        dev_priv->display.init_clock_gating(dev);
 }
 
+void intel_suspend_hw(struct drm_device *dev)
+{
+       if (HAS_PCH_LPT(dev))
+               lpt_suspend_hw(dev);
+}
+
 /**
  * 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
  * be enabled.
  */
-bool intel_using_power_well(struct drm_device *dev)
+bool intel_display_power_enabled(struct drm_device *dev,
+                                enum intel_display_power_domain domain)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (IS_HASWELL(dev))
+       if (!HAS_POWER_WELL(dev))
+               return true;
+
+       switch (domain) {
+       case POWER_DOMAIN_PIPE_A:
+       case POWER_DOMAIN_TRANSCODER_EDP:
+               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) ==
                       (HSW_PWR_WELL_ENABLE | HSW_PWR_WELL_STATE);
-       else
-               return true;
+       default:
+               BUG();
+       }
 }
 
-void intel_set_power_well(struct drm_device *dev, bool enable)
+static void __intel_set_power_well(struct drm_device *dev, bool enable)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        bool is_enabled, enable_requested;
        uint32_t tmp;
 
-       if (!HAS_POWER_WELL(dev))
-               return;
-
-       if (!i915_disable_power_well && !enable)
-               return;
-
        tmp = I915_READ(HSW_PWR_WELL_DRIVER);
        is_enabled = tmp & HSW_PWR_WELL_STATE;
        enable_requested = tmp & HSW_PWR_WELL_ENABLE;
@@ -4160,6 +5069,79 @@ void intel_set_power_well(struct drm_device *dev, bool enable)
        }
 }
 
+static struct i915_power_well *hsw_pwr;
+
+/* Display audio driver power well request */
+void i915_request_power_well(void)
+{
+       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);
+}
+EXPORT_SYMBOL_GPL(i915_request_power_well);
+
+/* Display audio driver power well release */
+void i915_release_power_well(void)
+{
+       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);
+}
+EXPORT_SYMBOL_GPL(i915_release_power_well);
+
+int i915_init_power_well(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       hsw_pwr = &dev_priv->power_well;
+
+       hsw_pwr->device = dev;
+       spin_lock_init(&hsw_pwr->lock);
+       hsw_pwr->count = 0;
+
+       return 0;
+}
+
+void i915_remove_power_well(struct drm_device *dev)
+{
+       hsw_pwr = NULL;
+}
+
+void intel_set_power_well(struct drm_device *dev, bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct i915_power_well *power_well = &dev_priv->power_well;
+
+       if (!HAS_POWER_WELL(dev))
+               return;
+
+       if (!i915_disable_power_well && !enable)
+               return;
+
+       spin_lock_irq(&power_well->lock);
+       power_well->i915_request = enable;
+
+       /* 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);
+}
+
 /*
  * Starting with Haswell, we have a "Power Down Well" that can be turned off
  * when not needed anymore. We have 4 registers that can request the power well
@@ -4190,7 +5172,12 @@ void intel_init_pm(struct drm_device *dev)
        if (I915_HAS_FBC(dev)) {
                if (HAS_PCH_SPLIT(dev)) {
                        dev_priv->display.fbc_enabled = ironlake_fbc_enabled;
-                       dev_priv->display.enable_fbc = ironlake_enable_fbc;
+                       if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
+                               dev_priv->display.enable_fbc =
+                                       gen7_enable_fbc;
+                       else
+                               dev_priv->display.enable_fbc =
+                                       ironlake_enable_fbc;
                        dev_priv->display.disable_fbc = ironlake_disable_fbc;
                } else if (IS_GM45(dev)) {
                        dev_priv->display.fbc_enabled = g4x_fbc_enabled;
@@ -4242,10 +5229,10 @@ void intel_init_pm(struct drm_device *dev)
                        }
                        dev_priv->display.init_clock_gating = ivybridge_init_clock_gating;
                } else if (IS_HASWELL(dev)) {
-                       if (SNB_READ_WM0_LATENCY()) {
-                               dev_priv->display.update_wm = sandybridge_update_wm;
-                               dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm;
-                               dev_priv->display.update_linetime_wm = haswell_update_linetime_wm;
+                       if (I915_READ64(MCH_SSKPD)) {
+                               dev_priv->display.update_wm = haswell_update_wm;
+                               dev_priv->display.update_sprite_wm =
+                                       haswell_update_sprite_wm;
                        } else {
                                DRM_DEBUG_KMS("Failed to read display plane latency. "
                                              "Disable CxSR\n");
@@ -4340,6 +5327,7 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
                            FORCEWAKE_ACK_TIMEOUT_MS))
                DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
 
+       /* WaRsForcewakeWaitTC0:snb */
        __gen6_gt_wait_for_thread_c0(dev_priv);
 }
 
@@ -4371,6 +5359,7 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv)
                            FORCEWAKE_ACK_TIMEOUT_MS))
                DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
 
+       /* WaRsForcewakeWaitTC0:ivb,hsw */
        __gen6_gt_wait_for_thread_c0(dev_priv);
 }
 
@@ -4474,6 +5463,7 @@ static void vlv_force_wake_get(struct drm_i915_private *dev_priv)
                            FORCEWAKE_ACK_TIMEOUT_MS))
                DRM_ERROR("Timed out waiting for media to ack forcewake request.\n");
 
+       /* WaRsForcewakeWaitTC0:vlv */
        __gen6_gt_wait_for_thread_c0(dev_priv);
 }
 
@@ -4568,55 +5558,58 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val)
        return 0;
 }
 
-static int vlv_punit_rw(struct drm_i915_private *dev_priv, u8 opcode,
-                       u8 addr, u32 *val)
+int vlv_gpu_freq(int ddr_freq, int val)
 {
-       u32 cmd, devfn, port, be, bar;
-
-       bar = 0;
-       be = 0xf;
-       port = IOSF_PORT_PUNIT;
-       devfn = PCI_DEVFN(2, 0);
+       int mult, base;
 
-       cmd = (devfn << IOSF_DEVFN_SHIFT) | (opcode << IOSF_OPCODE_SHIFT) |
-               (port << IOSF_PORT_SHIFT) | (be << IOSF_BYTE_ENABLES_SHIFT) |
-               (bar << IOSF_BAR_SHIFT);
-
-       WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
-
-       if (I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) {
-               DRM_DEBUG_DRIVER("warning: pcode (%s) mailbox access failed\n",
-                                opcode == PUNIT_OPCODE_REG_READ ?
-                                "read" : "write");
-               return -EAGAIN;
+       switch (ddr_freq) {
+       case 800:
+               mult = 20;
+               base = 120;
+               break;
+       case 1066:
+               mult = 22;
+               base = 133;
+               break;
+       case 1333:
+               mult = 21;
+               base = 125;
+               break;
+       default:
+               return -1;
        }
 
-       I915_WRITE(VLV_IOSF_ADDR, addr);
-       if (opcode == PUNIT_OPCODE_REG_WRITE)
-               I915_WRITE(VLV_IOSF_DATA, *val);
-       I915_WRITE(VLV_IOSF_DOORBELL_REQ, cmd);
+       return ((val - 0xbd) * mult) + base;
+}
 
-       if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0,
-                    500)) {
-               DRM_ERROR("timeout waiting for pcode %s (%d) to finish\n",
-                         opcode == PUNIT_OPCODE_REG_READ ? "read" : "write",
-                         addr);
-               return -ETIMEDOUT;
+int vlv_freq_opcode(int ddr_freq, int val)
+{
+       int mult, base;
+
+       switch (ddr_freq) {
+       case 800:
+               mult = 20;
+               base = 120;
+               break;
+       case 1066:
+               mult = 22;
+               base = 133;
+               break;
+       case 1333:
+               mult = 21;
+               base = 125;
+               break;
+       default:
+               return -1;
        }
 
-       if (opcode == PUNIT_OPCODE_REG_READ)
-               *val = I915_READ(VLV_IOSF_DATA);
-       I915_WRITE(VLV_IOSF_DATA, 0);
+       val /= mult;
+       val -= base / mult;
+       val += 0xbd;
 
-       return 0;
-}
+       if (val > 0xea)
+               val = 0xea;
 
-int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val)
-{
-       return vlv_punit_rw(dev_priv, PUNIT_OPCODE_REG_READ, addr, val);
+       return val;
 }
 
-int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val)
-{
-       return vlv_punit_rw(dev_priv, PUNIT_OPCODE_REG_WRITE, addr, &val);
-}
index 1d5d613..e51ab55 100644 (file)
@@ -280,6 +280,27 @@ gen7_render_ring_cs_stall_wa(struct intel_ring_buffer *ring)
        return 0;
 }
 
+static int gen7_ring_fbc_flush(struct intel_ring_buffer *ring, u32 value)
+{
+       int ret;
+
+       if (!ring->fbc_dirty)
+               return 0;
+
+       ret = intel_ring_begin(ring, 4);
+       if (ret)
+               return ret;
+       intel_ring_emit(ring, MI_NOOP);
+       /* WaFbcNukeOn3DBlt:ivb/hsw */
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+       intel_ring_emit(ring, MSG_FBC_REND_STATE);
+       intel_ring_emit(ring, value);
+       intel_ring_advance(ring);
+
+       ring->fbc_dirty = false;
+       return 0;
+}
+
 static int
 gen7_render_ring_flush(struct intel_ring_buffer *ring,
                       u32 invalidate_domains, u32 flush_domains)
@@ -336,6 +357,9 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring,
        intel_ring_emit(ring, 0);
        intel_ring_advance(ring);
 
+       if (flush_domains)
+               return gen7_ring_fbc_flush(ring, FBC_REND_NUKE);
+
        return 0;
 }
 
@@ -429,6 +453,8 @@ static int init_ring_common(struct intel_ring_buffer *ring)
                ring->last_retired_head = -1;
        }
 
+       memset(&ring->hangcheck, 0, sizeof(ring->hangcheck));
+
 out:
        if (HAS_FORCE_WAKE(dev))
                gen6_gt_force_wake_put(dev_priv);
@@ -464,9 +490,11 @@ init_pipe_control(struct intel_ring_buffer *ring)
                goto err_unref;
 
        pc->gtt_offset = obj->gtt_offset;
-       pc->cpu_page =  kmap(sg_page(obj->pages->sgl));
-       if (pc->cpu_page == NULL)
+       pc->cpu_page = kmap(sg_page(obj->pages->sgl));
+       if (pc->cpu_page == NULL) {
+               ret = -ENOMEM;
                goto err_unpin;
+       }
 
        DRM_DEBUG_DRIVER("%s pipe control offset: 0x%08x\n",
                         ring->name, pc->gtt_offset);
@@ -515,6 +543,8 @@ static int init_render_ring(struct intel_ring_buffer *ring)
        /* We need to disable the AsyncFlip performance optimisations in order
         * to use MI_WAIT_FOR_EVENT within the CS. It should already be
         * programmed to '1' on all products.
+        *
+        * WaDisableAsyncFlipPerfMode:snb,ivb,hsw,vlv
         */
        if (INTEL_INFO(dev)->gen >= 6)
                I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(ASYNC_FLIP_PERF_DISABLE));
@@ -556,7 +586,7 @@ static int init_render_ring(struct intel_ring_buffer *ring)
                I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_FORCE_ORDERING));
 
        if (HAS_L3_GPU_CACHE(dev))
-               I915_WRITE_IMR(ring, ~GEN6_RENDER_L3_PARITY_ERROR);
+               I915_WRITE_IMR(ring, ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT);
 
        return ret;
 }
@@ -578,9 +608,16 @@ static void
 update_mboxes(struct intel_ring_buffer *ring,
              u32 mmio_offset)
 {
+/* NB: In order to be able to do semaphore MBOX updates for varying number
+ * of rings, it's easiest if we round up each individual update to a
+ * multiple of 2 (since ring updates must always be a multiple of 2)
+ * even though the actual update only requires 3 dwords.
+ */
+#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, MI_NOOP);
 }
 
 /**
@@ -595,19 +632,24 @@ update_mboxes(struct intel_ring_buffer *ring,
 static int
 gen6_add_request(struct intel_ring_buffer *ring)
 {
-       u32 mbox1_reg;
-       u32 mbox2_reg;
-       int ret;
+       struct drm_device *dev = ring->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *useless;
+       int i, ret;
 
-       ret = intel_ring_begin(ring, 10);
+       ret = intel_ring_begin(ring, ((I915_NUM_RINGS-1) *
+                                     MBOX_UPDATE_DWORDS) +
+                                     4);
        if (ret)
                return ret;
+#undef MBOX_UPDATE_DWORDS
 
-       mbox1_reg = ring->signal_mbox[0];
-       mbox2_reg = ring->signal_mbox[1];
+       for_each_ring(useless, dev_priv, i) {
+               u32 mbox_reg = ring->signal_mbox[i];
+               if (mbox_reg != GEN6_NOSYNC)
+                       update_mboxes(ring, mbox_reg);
+       }
 
-       update_mboxes(ring, mbox1_reg);
-       update_mboxes(ring, mbox2_reg);
        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);
@@ -779,7 +821,7 @@ gen5_ring_get_irq(struct intel_ring_buffer *ring)
                return false;
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       if (ring->irq_refcount++ == 0) {
+       if (ring->irq_refcount.gt++ == 0) {
                dev_priv->gt_irq_mask &= ~ring->irq_enable_mask;
                I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
                POSTING_READ(GTIMR);
@@ -797,7 +839,7 @@ gen5_ring_put_irq(struct intel_ring_buffer *ring)
        unsigned long flags;
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       if (--ring->irq_refcount == 0) {
+       if (--ring->irq_refcount.gt == 0) {
                dev_priv->gt_irq_mask |= ring->irq_enable_mask;
                I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
                POSTING_READ(GTIMR);
@@ -816,7 +858,7 @@ i9xx_ring_get_irq(struct intel_ring_buffer *ring)
                return false;
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       if (ring->irq_refcount++ == 0) {
+       if (ring->irq_refcount.gt++ == 0) {
                dev_priv->irq_mask &= ~ring->irq_enable_mask;
                I915_WRITE(IMR, dev_priv->irq_mask);
                POSTING_READ(IMR);
@@ -834,7 +876,7 @@ i9xx_ring_put_irq(struct intel_ring_buffer *ring)
        unsigned long flags;
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       if (--ring->irq_refcount == 0) {
+       if (--ring->irq_refcount.gt == 0) {
                dev_priv->irq_mask |= ring->irq_enable_mask;
                I915_WRITE(IMR, dev_priv->irq_mask);
                POSTING_READ(IMR);
@@ -853,7 +895,7 @@ i8xx_ring_get_irq(struct intel_ring_buffer *ring)
                return false;
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       if (ring->irq_refcount++ == 0) {
+       if (ring->irq_refcount.gt++ == 0) {
                dev_priv->irq_mask &= ~ring->irq_enable_mask;
                I915_WRITE16(IMR, dev_priv->irq_mask);
                POSTING_READ16(IMR);
@@ -871,7 +913,7 @@ i8xx_ring_put_irq(struct intel_ring_buffer *ring)
        unsigned long flags;
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       if (--ring->irq_refcount == 0) {
+       if (--ring->irq_refcount.gt == 0) {
                dev_priv->irq_mask |= ring->irq_enable_mask;
                I915_WRITE16(IMR, dev_priv->irq_mask);
                POSTING_READ16(IMR);
@@ -899,6 +941,9 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
                case VCS:
                        mmio = BSD_HWS_PGA_GEN7;
                        break;
+               case VECS:
+                       mmio = VEBOX_HWS_PGA_GEN7;
+                       break;
                }
        } else if (IS_GEN6(ring->dev)) {
                mmio = RING_HWS_PGA_GEN6(ring->mmio_base);
@@ -961,10 +1006,11 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring)
        gen6_gt_force_wake_get(dev_priv);
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       if (ring->irq_refcount++ == 0) {
+       if (ring->irq_refcount.gt++ == 0) {
                if (HAS_L3_GPU_CACHE(dev) && ring->id == RCS)
-                       I915_WRITE_IMR(ring, ~(ring->irq_enable_mask |
-                                               GEN6_RENDER_L3_PARITY_ERROR));
+                       I915_WRITE_IMR(ring,
+                                      ~(ring->irq_enable_mask |
+                                        GT_RENDER_L3_PARITY_ERROR_INTERRUPT));
                else
                        I915_WRITE_IMR(ring, ~ring->irq_enable_mask);
                dev_priv->gt_irq_mask &= ~ring->irq_enable_mask;
@@ -984,9 +1030,10 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring)
        unsigned long flags;
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
-       if (--ring->irq_refcount == 0) {
+       if (--ring->irq_refcount.gt == 0) {
                if (HAS_L3_GPU_CACHE(dev) && ring->id == RCS)
-                       I915_WRITE_IMR(ring, ~GEN6_RENDER_L3_PARITY_ERROR);
+                       I915_WRITE_IMR(ring,
+                                      ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT);
                else
                        I915_WRITE_IMR(ring, ~0);
                dev_priv->gt_irq_mask |= ring->irq_enable_mask;
@@ -998,6 +1045,48 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring)
        gen6_gt_force_wake_put(dev_priv);
 }
 
+static bool
+hsw_vebox_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->rps.lock, flags);
+       if (ring->irq_refcount.pm++ == 0) {
+               u32 pm_imr = I915_READ(GEN6_PMIMR);
+               I915_WRITE_IMR(ring, ~ring->irq_enable_mask);
+               I915_WRITE(GEN6_PMIMR, pm_imr & ~ring->irq_enable_mask);
+               POSTING_READ(GEN6_PMIMR);
+       }
+       spin_unlock_irqrestore(&dev_priv->rps.lock, flags);
+
+       return true;
+}
+
+static void
+hsw_vebox_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;
+
+       if (!dev->irq_enabled)
+               return;
+
+       spin_lock_irqsave(&dev_priv->rps.lock, flags);
+       if (--ring->irq_refcount.pm == 0) {
+               u32 pm_imr = I915_READ(GEN6_PMIMR);
+               I915_WRITE_IMR(ring, ~0);
+               I915_WRITE(GEN6_PMIMR, pm_imr | ring->irq_enable_mask);
+               POSTING_READ(GEN6_PMIMR);
+       }
+       spin_unlock_irqrestore(&dev_priv->rps.lock, flags);
+}
+
 static int
 i965_dispatch_execbuffer(struct intel_ring_buffer *ring,
                         u32 offset, u32 length,
@@ -1423,7 +1512,7 @@ int intel_ring_idle(struct intel_ring_buffer *ring)
 
        /* We need to add any requests required to flush the objects and ring */
        if (ring->outstanding_lazy_request) {
-               ret = i915_add_request(ring, NULL, NULL);
+               ret = i915_add_request(ring, NULL);
                if (ret)
                        return ret;
        }
@@ -1500,6 +1589,7 @@ void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno)
        }
 
        ring->set_seqno(ring, seqno);
+       ring->hangcheck.seqno = seqno;
 }
 
 void intel_ring_advance(struct intel_ring_buffer *ring)
@@ -1546,8 +1636,8 @@ static void gen6_bsd_ring_write_tail(struct intel_ring_buffer *ring,
                   _MASKED_BIT_DISABLE(GEN6_BSD_SLEEP_MSG_DISABLE));
 }
 
-static int gen6_ring_flush(struct intel_ring_buffer *ring,
-                          u32 invalidate, u32 flush)
+static int gen6_bsd_ring_flush(struct intel_ring_buffer *ring,
+                              u32 invalidate, u32 flush)
 {
        uint32_t cmd;
        int ret;
@@ -1618,9 +1708,10 @@ gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
 
 /* Blitter support (SandyBridge+) */
 
-static int blt_ring_flush(struct intel_ring_buffer *ring,
-                         u32 invalidate, u32 flush)
+static int gen6_ring_flush(struct intel_ring_buffer *ring,
+                          u32 invalidate, u32 flush)
 {
+       struct drm_device *dev = ring->dev;
        uint32_t cmd;
        int ret;
 
@@ -1643,6 +1734,10 @@ static int blt_ring_flush(struct intel_ring_buffer *ring,
        intel_ring_emit(ring, 0);
        intel_ring_emit(ring, MI_NOOP);
        intel_ring_advance(ring);
+
+       if (IS_GEN7(dev) && flush)
+               return gen7_ring_fbc_flush(ring, FBC_REND_CACHE_CLEAN);
+
        return 0;
 }
 
@@ -1662,15 +1757,18 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
                        ring->flush = gen6_render_ring_flush;
                ring->irq_get = gen6_ring_get_irq;
                ring->irq_put = gen6_ring_put_irq;
-               ring->irq_enable_mask = GT_USER_INTERRUPT;
+               ring->irq_enable_mask = GT_RENDER_USER_INTERRUPT;
                ring->get_seqno = gen6_ring_get_seqno;
                ring->set_seqno = ring_set_seqno;
                ring->sync_to = gen6_ring_sync;
-               ring->semaphore_register[0] = MI_SEMAPHORE_SYNC_INVALID;
-               ring->semaphore_register[1] = MI_SEMAPHORE_SYNC_RV;
-               ring->semaphore_register[2] = MI_SEMAPHORE_SYNC_RB;
-               ring->signal_mbox[0] = GEN6_VRSYNC;
-               ring->signal_mbox[1] = GEN6_BRSYNC;
+               ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_INVALID;
+               ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_RV;
+               ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_RB;
+               ring->semaphore_register[VECS] = MI_SEMAPHORE_SYNC_RVE;
+               ring->signal_mbox[RCS] = GEN6_NOSYNC;
+               ring->signal_mbox[VCS] = GEN6_VRSYNC;
+               ring->signal_mbox[BCS] = GEN6_BRSYNC;
+               ring->signal_mbox[VECS] = GEN6_VERSYNC;
        } else if (IS_GEN5(dev)) {
                ring->add_request = pc_render_add_request;
                ring->flush = gen4_render_ring_flush;
@@ -1678,7 +1776,8 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
                ring->set_seqno = pc_render_set_seqno;
                ring->irq_get = gen5_ring_get_irq;
                ring->irq_put = gen5_ring_put_irq;
-               ring->irq_enable_mask = GT_USER_INTERRUPT | GT_PIPE_NOTIFY;
+               ring->irq_enable_mask = GT_RENDER_USER_INTERRUPT |
+                                       GT_RENDER_PIPECTL_NOTIFY_INTERRUPT;
        } else {
                ring->add_request = i9xx_add_request;
                if (INTEL_INFO(dev)->gen < 4)
@@ -1816,20 +1915,23 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
                /* gen6 bsd needs a special wa for tail updates */
                if (IS_GEN6(dev))
                        ring->write_tail = gen6_bsd_ring_write_tail;
-               ring->flush = gen6_ring_flush;
+               ring->flush = gen6_bsd_ring_flush;
                ring->add_request = gen6_add_request;
                ring->get_seqno = gen6_ring_get_seqno;
                ring->set_seqno = ring_set_seqno;
-               ring->irq_enable_mask = GEN6_BSD_USER_INTERRUPT;
+               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[0] = MI_SEMAPHORE_SYNC_VR;
-               ring->semaphore_register[1] = MI_SEMAPHORE_SYNC_INVALID;
-               ring->semaphore_register[2] = MI_SEMAPHORE_SYNC_VB;
-               ring->signal_mbox[0] = GEN6_RVSYNC;
-               ring->signal_mbox[1] = GEN6_BVSYNC;
+               ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_VR;
+               ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_INVALID;
+               ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_VB;
+               ring->semaphore_register[VECS] = MI_SEMAPHORE_SYNC_VVE;
+               ring->signal_mbox[RCS] = GEN6_RVSYNC;
+               ring->signal_mbox[VCS] = GEN6_NOSYNC;
+               ring->signal_mbox[BCS] = GEN6_BVSYNC;
+               ring->signal_mbox[VECS] = GEN6_VEVSYNC;
        } else {
                ring->mmio_base = BSD_RING_BASE;
                ring->flush = bsd_ring_flush;
@@ -1837,7 +1939,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
                ring->get_seqno = ring_get_seqno;
                ring->set_seqno = ring_set_seqno;
                if (IS_GEN5(dev)) {
-                       ring->irq_enable_mask = GT_BSD_USER_INTERRUPT;
+                       ring->irq_enable_mask = ILK_BSD_USER_INTERRUPT;
                        ring->irq_get = gen5_ring_get_irq;
                        ring->irq_put = gen5_ring_put_irq;
                } else {
@@ -1862,20 +1964,56 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
 
        ring->mmio_base = BLT_RING_BASE;
        ring->write_tail = ring_write_tail;
-       ring->flush = blt_ring_flush;
+       ring->flush = gen6_ring_flush;
        ring->add_request = gen6_add_request;
        ring->get_seqno = gen6_ring_get_seqno;
        ring->set_seqno = ring_set_seqno;
-       ring->irq_enable_mask = GEN6_BLITTER_USER_INTERRUPT;
+       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[0] = MI_SEMAPHORE_SYNC_BR;
-       ring->semaphore_register[1] = MI_SEMAPHORE_SYNC_BV;
-       ring->semaphore_register[2] = MI_SEMAPHORE_SYNC_INVALID;
-       ring->signal_mbox[0] = GEN6_RBSYNC;
-       ring->signal_mbox[1] = GEN6_VBSYNC;
+       ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_BR;
+       ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_BV;
+       ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_INVALID;
+       ring->semaphore_register[VECS] = MI_SEMAPHORE_SYNC_BVE;
+       ring->signal_mbox[RCS] = GEN6_RBSYNC;
+       ring->signal_mbox[VCS] = GEN6_VBSYNC;
+       ring->signal_mbox[BCS] = GEN6_NOSYNC;
+       ring->signal_mbox[VECS] = GEN6_VEBSYNC;
+       ring->init = init_ring_common;
+
+       return intel_init_ring_buffer(dev, ring);
+}
+
+int intel_init_vebox_ring_buffer(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring = &dev_priv->ring[VECS];
+
+       ring->name = "video enhancement ring";
+       ring->id = VECS;
+
+       ring->mmio_base = VEBOX_RING_BASE;
+       ring->write_tail = ring_write_tail;
+       ring->flush = gen6_ring_flush;
+       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 |
+               PM_VEBOX_CS_ERROR_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;
+       ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_VEB;
+       ring->semaphore_register[VECS] = MI_SEMAPHORE_SYNC_INVALID;
+       ring->signal_mbox[RCS] = GEN6_RVESYNC;
+       ring->signal_mbox[VCS] = GEN6_VVESYNC;
+       ring->signal_mbox[BCS] = GEN6_BVESYNC;
+       ring->signal_mbox[VECS] = GEN6_NOSYNC;
        ring->init = init_ring_common;
 
        return intel_init_ring_buffer(dev, ring);
index d66208c..799f04c 100644 (file)
@@ -37,14 +37,25 @@ struct  intel_hw_status_page {
 #define I915_READ_SYNC_0(ring) I915_READ(RING_SYNC_0((ring)->mmio_base))
 #define I915_READ_SYNC_1(ring) I915_READ(RING_SYNC_1((ring)->mmio_base))
 
+enum intel_ring_hangcheck_action { wait, active, kick, hung };
+
+struct intel_ring_hangcheck {
+       bool deadlock;
+       u32 seqno;
+       u32 acthd;
+       int score;
+       enum intel_ring_hangcheck_action action;
+};
+
 struct  intel_ring_buffer {
        const char      *name;
        enum intel_ring_id {
                RCS = 0x0,
                VCS,
                BCS,
+               VECS,
        } id;
-#define I915_NUM_RINGS 3
+#define I915_NUM_RINGS 4
        u32             mmio_base;
        void            __iomem *virtual_start;
        struct          drm_device *dev;
@@ -67,7 +78,10 @@ struct  intel_ring_buffer {
         */
        u32             last_retired_head;
 
-       u32             irq_refcount;           /* protected by dev_priv->irq_lock */
+       struct {
+               u32     gt; /*  protected by dev_priv->irq_lock */
+               u32     pm; /*  protected by dev_priv->rps.lock (sucks) */
+       } irq_refcount;
        u32             irq_enable_mask;        /* bitmask to enable ring interrupt */
        u32             trace_irq_seqno;
        u32             sync_seqno[I915_NUM_RINGS-1];
@@ -102,8 +116,11 @@ struct  intel_ring_buffer {
                                   struct intel_ring_buffer *to,
                                   u32 seqno);
 
-       u32             semaphore_register[3]; /*our mbox written by others */
-       u32             signal_mbox[2]; /* mboxes this ring signals to */
+       /* our mbox written by others */
+       u32             semaphore_register[I915_NUM_RINGS];
+       /* mboxes this ring signals to */
+       u32             signal_mbox[I915_NUM_RINGS];
+
        /**
         * List of objects currently involved in rendering from the
         * ringbuffer.
@@ -127,6 +144,7 @@ struct  intel_ring_buffer {
         */
        u32 outstanding_lazy_request;
        bool gpu_caches_dirty;
+       bool fbc_dirty;
 
        wait_queue_head_t irq_queue;
 
@@ -135,7 +153,9 @@ struct  intel_ring_buffer {
         */
        bool itlb_before_ctx_switch;
        struct i915_hw_context *default_context;
-       struct drm_i915_gem_object *last_context_obj;
+       struct i915_hw_context *last_context;
+
+       struct intel_ring_hangcheck hangcheck;
 
        void *private;
 };
@@ -224,6 +244,7 @@ int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring);
 int intel_init_render_ring_buffer(struct drm_device *dev);
 int intel_init_bsd_ring_buffer(struct drm_device *dev);
 int intel_init_blt_ring_buffer(struct drm_device *dev);
+int intel_init_vebox_ring_buffer(struct drm_device *dev);
 
 u32 intel_ring_get_active_head(struct intel_ring_buffer *ring);
 void intel_ring_setup_status_page(struct intel_ring_buffer *ring);
index d4ea6c2..2628d56 100644 (file)
@@ -80,7 +80,7 @@ struct intel_sdvo {
 
        /*
         * Capabilities of the SDVO device returned by
-        * i830_sdvo_get_capabilities()
+        * intel_sdvo_get_capabilities()
         */
        struct intel_sdvo_caps caps;
 
@@ -712,6 +712,13 @@ static bool intel_sdvo_set_timing(struct intel_sdvo *intel_sdvo, u8 cmd,
                intel_sdvo_set_value(intel_sdvo, cmd + 1, &dtd->part2, sizeof(dtd->part2));
 }
 
+static bool intel_sdvo_get_timing(struct intel_sdvo *intel_sdvo, u8 cmd,
+                                 struct intel_sdvo_dtd *dtd)
+{
+       return intel_sdvo_get_value(intel_sdvo, cmd, &dtd->part1, sizeof(dtd->part1)) &&
+               intel_sdvo_get_value(intel_sdvo, cmd + 1, &dtd->part2, sizeof(dtd->part2));
+}
+
 static bool intel_sdvo_set_input_timing(struct intel_sdvo *intel_sdvo,
                                         struct intel_sdvo_dtd *dtd)
 {
@@ -726,6 +733,13 @@ static bool intel_sdvo_set_output_timing(struct intel_sdvo *intel_sdvo,
                                     SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd);
 }
 
+static bool intel_sdvo_get_input_timing(struct intel_sdvo *intel_sdvo,
+                                       struct intel_sdvo_dtd *dtd)
+{
+       return intel_sdvo_get_timing(intel_sdvo,
+                                    SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd);
+}
+
 static bool
 intel_sdvo_create_preferred_input_timing(struct intel_sdvo *intel_sdvo,
                                         uint16_t clock,
@@ -1041,6 +1055,32 @@ intel_sdvo_get_preferred_input_mode(struct intel_sdvo *intel_sdvo,
        return true;
 }
 
+static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc_config *pipe_config)
+{
+       unsigned dotclock = pipe_config->adjusted_mode.clock;
+       struct dpll *clock = &pipe_config->dpll;
+
+       /* SDVO TV has fixed PLL values depend on its clock range,
+          this mirrors vbios setting. */
+       if (dotclock >= 100000 && dotclock < 140500) {
+               clock->p1 = 2;
+               clock->p2 = 10;
+               clock->n = 3;
+               clock->m1 = 16;
+               clock->m2 = 8;
+       } else if (dotclock >= 140500 && dotclock <= 200000) {
+               clock->p1 = 1;
+               clock->p2 = 10;
+               clock->n = 6;
+               clock->m1 = 12;
+               clock->m2 = 8;
+       } else {
+               WARN(1, "SDVO TV clock out of range: %i\n", dotclock);
+       }
+
+       pipe_config->clock_set = true;
+}
+
 static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
                                      struct intel_crtc_config *pipe_config)
 {
@@ -1066,6 +1106,7 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
                (void) intel_sdvo_get_preferred_input_mode(intel_sdvo,
                                                           mode,
                                                           adjusted_mode);
+               pipe_config->sdvo_tv_clock = true;
        } else if (intel_sdvo->is_lvds) {
                if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo,
                                                             intel_sdvo->sdvo_lvds_fixed_mode))
@@ -1097,6 +1138,10 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
        if (intel_sdvo->color_range)
                pipe_config->limited_color_range = true;
 
+       /* Clock computation needs to happen after pixel multiplier. */
+       if (intel_sdvo->is_tv)
+               i9xx_adjust_sdvo_tv_clock(pipe_config);
+
        return true;
 }
 
@@ -1174,6 +1219,7 @@ static void intel_sdvo_mode_set(struct intel_encoder *intel_encoder)
 
        switch (intel_crtc->config.pixel_multiplier) {
        default:
+               WARN(1, "unknown pixel mutlipler specified\n");
        case 1: rate = SDVO_CLOCK_RATE_MULT_1X; break;
        case 2: rate = SDVO_CLOCK_RATE_MULT_2X; break;
        case 4: rate = SDVO_CLOCK_RATE_MULT_4X; break;
@@ -1231,7 +1277,7 @@ static bool intel_sdvo_connector_get_hw_state(struct intel_connector *connector)
        struct intel_sdvo_connector *intel_sdvo_connector =
                to_intel_sdvo_connector(&connector->base);
        struct intel_sdvo *intel_sdvo = intel_attached_sdvo(&connector->base);
-       u16 active_outputs;
+       u16 active_outputs = 0;
 
        intel_sdvo_get_active_outputs(intel_sdvo, &active_outputs);
 
@@ -1247,7 +1293,7 @@ static bool intel_sdvo_get_hw_state(struct intel_encoder *encoder,
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
-       u16 active_outputs;
+       u16 active_outputs = 0;
        u32 tmp;
 
        tmp = I915_READ(intel_sdvo->sdvo_reg);
@@ -1264,6 +1310,74 @@ static bool intel_sdvo_get_hw_state(struct intel_encoder *encoder,
        return true;
 }
 
+static void intel_sdvo_get_config(struct intel_encoder *encoder,
+                                 struct intel_crtc_config *pipe_config)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
+       struct intel_sdvo_dtd dtd;
+       int encoder_pixel_multiplier = 0;
+       u32 flags = 0, sdvox;
+       u8 val;
+       bool ret;
+
+       ret = intel_sdvo_get_input_timing(intel_sdvo, &dtd);
+       if (!ret) {
+               /* Some sdvo encoders are not spec compliant and don't
+                * implement the mandatory get_timings function. */
+               DRM_DEBUG_DRIVER("failed to retrieve SDVO DTD\n");
+               pipe_config->quirks |= PIPE_CONFIG_QUIRK_MODE_SYNC_FLAGS;
+       } else {
+               if (dtd.part2.dtd_flags & DTD_FLAG_HSYNC_POSITIVE)
+                       flags |= DRM_MODE_FLAG_PHSYNC;
+               else
+                       flags |= DRM_MODE_FLAG_NHSYNC;
+
+               if (dtd.part2.dtd_flags & DTD_FLAG_VSYNC_POSITIVE)
+                       flags |= DRM_MODE_FLAG_PVSYNC;
+               else
+                       flags |= DRM_MODE_FLAG_NVSYNC;
+       }
+
+       pipe_config->adjusted_mode.flags |= flags;
+
+       /*
+        * pixel multiplier readout is tricky: Only on i915g/gm it is stored in
+        * the sdvo port register, on all other platforms it is part of the dpll
+        * state. Since the general pipe state readout happens before the
+        * encoder->get_config we so already have a valid pixel multplier on all
+        * other platfroms.
+        */
+       if (IS_I915G(dev) || IS_I915GM(dev)) {
+               sdvox = I915_READ(intel_sdvo->sdvo_reg);
+               pipe_config->pixel_multiplier =
+                       ((sdvox & SDVO_PORT_MULTIPLY_MASK)
+                        >> SDVO_PORT_MULTIPLY_SHIFT) + 1;
+       }
+
+       /* Cross check the port pixel multiplier with the sdvo encoder state. */
+       intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_CLOCK_RATE_MULT, &val, 1);
+       switch (val) {
+       case SDVO_CLOCK_RATE_MULT_1X:
+               encoder_pixel_multiplier = 1;
+               break;
+       case SDVO_CLOCK_RATE_MULT_2X:
+               encoder_pixel_multiplier = 2;
+               break;
+       case SDVO_CLOCK_RATE_MULT_4X:
+               encoder_pixel_multiplier = 4;
+               break;
+       }
+
+       if(HAS_PCH_SPLIT(dev))
+               return; /* no pixel multiplier readout support yet */
+
+       WARN(encoder_pixel_multiplier != pipe_config->pixel_multiplier,
+            "SDVO pixel multiplier mismatch, port: %i, encoder: %i\n",
+            pipe_config->pixel_multiplier, encoder_pixel_multiplier);
+}
+
 static void intel_disable_sdvo(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
@@ -1344,6 +1458,7 @@ static void intel_enable_sdvo(struct intel_encoder *encoder)
        intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output);
 }
 
+/* Special dpms function to support cloning between dvo/sdvo/crt. */
 static void intel_sdvo_dpms(struct drm_connector *connector, int mode)
 {
        struct drm_crtc *crtc;
@@ -1365,6 +1480,8 @@ static void intel_sdvo_dpms(struct drm_connector *connector, int mode)
                return;
        }
 
+       /* We set active outputs manually below in case pipe dpms doesn't change
+        * due to cloning. */
        if (mode != DRM_MODE_DPMS_ON) {
                intel_sdvo_set_active_outputs(intel_sdvo, 0);
                if (0)
@@ -1495,7 +1612,7 @@ intel_sdvo_get_analog_edid(struct drm_connector *connector)
 
        return drm_get_edid(connector,
                            intel_gmbus_get_adapter(dev_priv,
-                                                   dev_priv->crt_ddc_pin));
+                                                   dev_priv->vbt.crt_ddc_pin));
 }
 
 static enum drm_connector_status
@@ -1625,12 +1742,9 @@ intel_sdvo_detect(struct drm_connector *connector, bool force)
        if (ret == connector_status_connected) {
                intel_sdvo->is_tv = false;
                intel_sdvo->is_lvds = false;
-               intel_sdvo->base.needs_tv_clock = false;
 
-               if (response & SDVO_TV_MASK) {
+               if (response & SDVO_TV_MASK)
                        intel_sdvo->is_tv = true;
-                       intel_sdvo->base.needs_tv_clock = true;
-               }
                if (response & SDVO_LVDS_MASK)
                        intel_sdvo->is_lvds = intel_sdvo->sdvo_lvds_fixed_mode != NULL;
        }
@@ -1771,22 +1885,13 @@ 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;
 
-       /*
-        * Attempt to get the mode list from DDC.
-        * Assume that the preferred modes are
-        * arranged in priority order.
-        */
-       intel_ddc_get_modes(connector, &intel_sdvo->ddc);
-
        /*
         * Fetch modes from VBT. For SDVO prefer the VBT mode since some
-        * SDVO->LVDS transcoders can't cope with the EDID mode. Since
-        * drm_mode_probed_add adds the mode at the head of the list we add it
-        * last.
+        * SDVO->LVDS transcoders can't cope with the EDID mode.
         */
-       if (dev_priv->sdvo_lvds_vbt_mode != NULL) {
+       if (dev_priv->vbt.sdvo_lvds_vbt_mode != NULL) {
                newmode = drm_mode_duplicate(connector->dev,
-                                            dev_priv->sdvo_lvds_vbt_mode);
+                                            dev_priv->vbt.sdvo_lvds_vbt_mode);
                if (newmode != NULL) {
                        /* Guarantee the mode is preferred */
                        newmode->type = (DRM_MODE_TYPE_PREFERRED |
@@ -1795,6 +1900,13 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
                }
        }
 
+       /*
+        * Attempt to get the mode list from DDC.
+        * Assume that the preferred modes are
+        * arranged in priority order.
+        */
+       intel_ddc_get_modes(connector, &intel_sdvo->ddc);
+
        list_for_each_entry(newmode, &connector->probed_modes, head) {
                if (newmode->type & DRM_MODE_TYPE_PREFERRED) {
                        intel_sdvo->sdvo_lvds_fixed_mode =
@@ -2329,7 +2441,6 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
        intel_sdvo_connector->output_flag = type;
 
        intel_sdvo->is_tv = true;
-       intel_sdvo->base.needs_tv_clock = true;
 
        intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
 
@@ -2417,7 +2528,6 @@ static bool
 intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo, uint16_t flags)
 {
        intel_sdvo->is_tv = false;
-       intel_sdvo->base.needs_tv_clock = false;
        intel_sdvo->is_lvds = false;
 
        /* SDVO requires XXX1 function may not exist unless it has XXX0 function.*/
@@ -2751,7 +2861,6 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_encoder *intel_encoder;
        struct intel_sdvo *intel_sdvo;
-       u32 hotplug_mask;
        int i;
        intel_sdvo = kzalloc(sizeof(struct intel_sdvo), GFP_KERNEL);
        if (!intel_sdvo)
@@ -2780,23 +2889,12 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
                }
        }
 
-       hotplug_mask = 0;
-       if (IS_G4X(dev)) {
-               hotplug_mask = intel_sdvo->is_sdvob ?
-                       SDVOB_HOTPLUG_INT_STATUS_G4X : SDVOC_HOTPLUG_INT_STATUS_G4X;
-       } else if (IS_GEN4(dev)) {
-               hotplug_mask = intel_sdvo->is_sdvob ?
-                       SDVOB_HOTPLUG_INT_STATUS_I965 : SDVOC_HOTPLUG_INT_STATUS_I965;
-       } else {
-               hotplug_mask = intel_sdvo->is_sdvob ?
-                       SDVOB_HOTPLUG_INT_STATUS_I915 : SDVOC_HOTPLUG_INT_STATUS_I915;
-       }
-
        intel_encoder->compute_config = intel_sdvo_compute_config;
        intel_encoder->disable = intel_disable_sdvo;
        intel_encoder->mode_set = intel_sdvo_mode_set;
        intel_encoder->enable = intel_enable_sdvo;
        intel_encoder->get_hw_state = intel_sdvo_get_hw_state;
+       intel_encoder->get_config = intel_sdvo_get_config;
 
        /* In default case sdvo lvds is false */
        if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps))
diff --git a/drivers/gpu/drm/i915/intel_sideband.c b/drivers/gpu/drm/i915/intel_sideband.c
new file mode 100644 (file)
index 0000000..9a0e6c5
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ *
+ */
+
+#include "i915_drv.h"
+#include "intel_drv.h"
+
+/* IOSF sideband */
+static int vlv_sideband_rw(struct drm_i915_private *dev_priv, u32 devfn,
+                          u32 port, u32 opcode, u32 addr, u32 *val)
+{
+       u32 cmd, be = 0xf, bar = 0;
+       bool is_read = (opcode == PUNIT_OPCODE_REG_READ ||
+                       opcode == DPIO_OPCODE_REG_READ);
+
+       cmd = (devfn << IOSF_DEVFN_SHIFT) | (opcode << IOSF_OPCODE_SHIFT) |
+               (port << IOSF_PORT_SHIFT) | (be << IOSF_BYTE_ENABLES_SHIFT) |
+               (bar << IOSF_BAR_SHIFT);
+
+       WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock));
+
+       if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0, 5)) {
+               DRM_DEBUG_DRIVER("IOSF sideband idle wait (%s) timed out\n",
+                                is_read ? "read" : "write");
+               return -EAGAIN;
+       }
+
+       I915_WRITE(VLV_IOSF_ADDR, addr);
+       if (!is_read)
+               I915_WRITE(VLV_IOSF_DATA, *val);
+       I915_WRITE(VLV_IOSF_DOORBELL_REQ, cmd);
+
+       if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0, 5)) {
+               DRM_DEBUG_DRIVER("IOSF sideband finish wait (%s) timed out\n",
+                                is_read ? "read" : "write");
+               return -ETIMEDOUT;
+       }
+
+       if (is_read)
+               *val = I915_READ(VLV_IOSF_DATA);
+       I915_WRITE(VLV_IOSF_DATA, 0);
+
+       return 0;
+}
+
+u32 vlv_punit_read(struct drm_i915_private *dev_priv, u8 addr)
+{
+       u32 val = 0;
+
+       WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
+
+       mutex_lock(&dev_priv->dpio_lock);
+       vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT,
+                       PUNIT_OPCODE_REG_READ, addr, &val);
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       return val;
+}
+
+void vlv_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val)
+{
+       WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
+
+       mutex_lock(&dev_priv->dpio_lock);
+       vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT,
+                       PUNIT_OPCODE_REG_WRITE, addr, &val);
+       mutex_unlock(&dev_priv->dpio_lock);
+}
+
+u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr)
+{
+       u32 val = 0;
+
+       WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
+
+       mutex_lock(&dev_priv->dpio_lock);
+       vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_NC,
+                       PUNIT_OPCODE_REG_READ, addr, &val);
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       return val;
+}
+
+u32 vlv_dpio_read(struct drm_i915_private *dev_priv, int reg)
+{
+       u32 val = 0;
+
+       vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_DPIO,
+                       DPIO_OPCODE_REG_READ, reg, &val);
+
+       return val;
+}
+
+void vlv_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val)
+{
+       vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_DPIO,
+                       DPIO_OPCODE_REG_WRITE, reg, &val);
+}
+
+/* SBI access */
+u32 intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg,
+                  enum intel_sbi_destination destination)
+{
+       u32 value = 0;
+       WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock));
+
+       if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0,
+                               100)) {
+               DRM_ERROR("timeout waiting for SBI to become ready\n");
+               return 0;
+       }
+
+       I915_WRITE(SBI_ADDR, (reg << 16));
+
+       if (destination == SBI_ICLK)
+               value = SBI_CTL_DEST_ICLK | SBI_CTL_OP_CRRD;
+       else
+               value = SBI_CTL_DEST_MPHY | SBI_CTL_OP_IORD;
+       I915_WRITE(SBI_CTL_STAT, value | SBI_BUSY);
+
+       if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0,
+                               100)) {
+               DRM_ERROR("timeout waiting for SBI to complete read transaction\n");
+               return 0;
+       }
+
+       return I915_READ(SBI_DATA);
+}
+
+void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value,
+                    enum intel_sbi_destination destination)
+{
+       u32 tmp;
+
+       WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock));
+
+       if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0,
+                               100)) {
+               DRM_ERROR("timeout waiting for SBI to become ready\n");
+               return;
+       }
+
+       I915_WRITE(SBI_ADDR, (reg << 16));
+       I915_WRITE(SBI_DATA, value);
+
+       if (destination == SBI_ICLK)
+               tmp = SBI_CTL_DEST_ICLK | SBI_CTL_OP_CRWR;
+       else
+               tmp = SBI_CTL_DEST_MPHY | SBI_CTL_OP_IOWR;
+       I915_WRITE(SBI_CTL_STAT, SBI_BUSY | tmp);
+
+       if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0,
+                               100)) {
+               DRM_ERROR("timeout waiting for SBI to complete write transaction\n");
+               return;
+       }
+}
index c7d25c5..1fa5612 100644 (file)
@@ -32,6 +32,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_rect.h>
 #include "intel_drv.h"
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
@@ -113,7 +114,7 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_framebuffer *fb,
        crtc_w--;
        crtc_h--;
 
-       intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size);
+       intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size, true);
 
        I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]);
        I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
@@ -267,7 +268,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
        crtc_w--;
        crtc_h--;
 
-       intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size);
+       intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size, true);
 
        /*
         * IVB workaround: must disable low power watermarks for at least
@@ -334,6 +335,8 @@ ivb_disable_plane(struct drm_plane *plane)
 
        dev_priv->sprite_scaling_enabled &= ~(1 << pipe);
 
+       intel_update_sprite_watermarks(dev, pipe, 0, 0, false);
+
        /* potentially re-enable LP watermarks */
        if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled)
                intel_update_watermarks(dev);
@@ -452,7 +455,7 @@ ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
        crtc_w--;
        crtc_h--;
 
-       intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size);
+       intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size, true);
 
        dvsscale = 0;
        if (IS_GEN5(dev) || crtc_w != src_w || crtc_h != src_h)
@@ -583,6 +586,20 @@ ilk_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key)
                key->flags = I915_SET_COLORKEY_NONE;
 }
 
+static bool
+format_is_yuv(uint32_t format)
+{
+       switch (format) {
+       case DRM_FORMAT_YUYV:
+       case DRM_FORMAT_UYVY:
+       case DRM_FORMAT_VYUY:
+       case DRM_FORMAT_YVYU:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static int
 intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                   struct drm_framebuffer *fb, int crtc_x, int crtc_y,
@@ -600,9 +617,29 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
        enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
                                                                      pipe);
        int ret = 0;
-       int x = src_x >> 16, y = src_y >> 16;
-       int primary_w = crtc->mode.hdisplay, primary_h = crtc->mode.vdisplay;
        bool disable_primary = false;
+       bool visible;
+       int hscale, vscale;
+       int max_scale, min_scale;
+       int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
+       struct drm_rect src = {
+               /* sample coordinates in 16.16 fixed point */
+               .x1 = src_x,
+               .x2 = src_x + src_w,
+               .y1 = src_y,
+               .y2 = src_y + src_h,
+       };
+       struct drm_rect dst = {
+               /* integer pixels */
+               .x1 = crtc_x,
+               .x2 = crtc_x + crtc_w,
+               .y1 = crtc_y,
+               .y2 = crtc_y + crtc_h,
+       };
+       const struct drm_rect clip = {
+               .x2 = crtc->mode.hdisplay,
+               .y2 = crtc->mode.vdisplay,
+       };
 
        intel_fb = to_intel_framebuffer(fb);
        obj = intel_fb->obj;
@@ -618,19 +655,23 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
        intel_plane->src_w = src_w;
        intel_plane->src_h = src_h;
 
-       src_w = src_w >> 16;
-       src_h = src_h >> 16;
-
        /* Pipe must be running... */
-       if (!(I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE))
+       if (!(I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE)) {
+               DRM_DEBUG_KMS("Pipe disabled\n");
                return -EINVAL;
+       }
 
-       if (crtc_x >= primary_w || crtc_y >= primary_h)
+       /* Don't modify another pipe's plane */
+       if (intel_plane->pipe != intel_crtc->pipe) {
+               DRM_DEBUG_KMS("Wrong plane <-> crtc mapping\n");
                return -EINVAL;
+       }
 
-       /* Don't modify another pipe's plane */
-       if (intel_plane->pipe != intel_crtc->pipe)
+       /* FIXME check all gen limits */
+       if (fb->width < 3 || fb->height < 3 || fb->pitches[0] > 16384) {
+               DRM_DEBUG_KMS("Unsuitable framebuffer for plane\n");
                return -EINVAL;
+       }
 
        /* Sprite planes can be linear or x-tiled surfaces */
        switch (obj->tiling_mode) {
@@ -638,55 +679,123 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                case I915_TILING_X:
                        break;
                default:
+                       DRM_DEBUG_KMS("Unsupported tiling mode\n");
                        return -EINVAL;
        }
 
        /*
-        * Clamp the width & height into the visible area.  Note we don't
-        * try to scale the source if part of the visible region is offscreen.
-        * The caller must handle that by adjusting source offset and size.
+        * FIXME the following code does a bunch of fuzzy adjustments to the
+        * coordinates and sizes. We probably need some way to decide whether
+        * more strict checking should be done instead.
         */
-       if ((crtc_x < 0) && ((crtc_x + crtc_w) > 0)) {
-               crtc_w += crtc_x;
-               crtc_x = 0;
-       }
-       if ((crtc_x + crtc_w) <= 0) /* Nothing to display */
-               goto out;
-       if ((crtc_x + crtc_w) > primary_w)
-               crtc_w = primary_w - crtc_x;
+       max_scale = intel_plane->max_downscale << 16;
+       min_scale = intel_plane->can_scale ? 1 : (1 << 16);
+
+       hscale = drm_rect_calc_hscale_relaxed(&src, &dst, min_scale, max_scale);
+       BUG_ON(hscale < 0);
+
+       vscale = drm_rect_calc_vscale_relaxed(&src, &dst, min_scale, max_scale);
+       BUG_ON(vscale < 0);
+
+       visible = drm_rect_clip_scaled(&src, &dst, &clip, hscale, vscale);
+
+       crtc_x = dst.x1;
+       crtc_y = dst.y1;
+       crtc_w = drm_rect_width(&dst);
+       crtc_h = drm_rect_height(&dst);
+
+       if (visible) {
+               /* check again in case clipping clamped the results */
+               hscale = drm_rect_calc_hscale(&src, &dst, min_scale, max_scale);
+               if (hscale < 0) {
+                       DRM_DEBUG_KMS("Horizontal scaling factor out of limits\n");
+                       drm_rect_debug_print(&src, true);
+                       drm_rect_debug_print(&dst, false);
+
+                       return hscale;
+               }
 
-       if ((crtc_y < 0) && ((crtc_y + crtc_h) > 0)) {
-               crtc_h += crtc_y;
-               crtc_y = 0;
+               vscale = drm_rect_calc_vscale(&src, &dst, min_scale, max_scale);
+               if (vscale < 0) {
+                       DRM_DEBUG_KMS("Vertical scaling factor out of limits\n");
+                       drm_rect_debug_print(&src, true);
+                       drm_rect_debug_print(&dst, false);
+
+                       return vscale;
+               }
+
+               /* Make the source viewport size an exact multiple of the scaling factors. */
+               drm_rect_adjust_size(&src,
+                                    drm_rect_width(&dst) * hscale - drm_rect_width(&src),
+                                    drm_rect_height(&dst) * vscale - drm_rect_height(&src));
+
+               /* sanity check to make sure the src viewport wasn't enlarged */
+               WARN_ON(src.x1 < (int) src_x ||
+                       src.y1 < (int) src_y ||
+                       src.x2 > (int) (src_x + src_w) ||
+                       src.y2 > (int) (src_y + src_h));
+
+               /*
+                * Hardware doesn't handle subpixel coordinates.
+                * Adjust to (macro)pixel boundary, but be careful not to
+                * increase the source viewport size, because that could
+                * push the downscaling factor out of bounds.
+                */
+               src_x = src.x1 >> 16;
+               src_w = drm_rect_width(&src) >> 16;
+               src_y = src.y1 >> 16;
+               src_h = drm_rect_height(&src) >> 16;
+
+               if (format_is_yuv(fb->pixel_format)) {
+                       src_x &= ~1;
+                       src_w &= ~1;
+
+                       /*
+                        * Must keep src and dst the
+                        * same if we can't scale.
+                        */
+                       if (!intel_plane->can_scale)
+                               crtc_w &= ~1;
+
+                       if (crtc_w == 0)
+                               visible = false;
+               }
        }
-       if ((crtc_y + crtc_h) <= 0) /* Nothing to display */
-               goto out;
-       if (crtc_y + crtc_h > primary_h)
-               crtc_h = primary_h - crtc_y;
 
-       if (!crtc_w || !crtc_h) /* Again, nothing to display */
-               goto out;
+       /* Check size restrictions when scaling */
+       if (visible && (src_w != crtc_w || src_h != crtc_h)) {
+               unsigned int width_bytes;
 
-       /*
-        * We may not have a scaler, eg. HSW does not have it any more
-        */
-       if (!intel_plane->can_scale && (crtc_w != src_w || crtc_h != src_h))
-               return -EINVAL;
+               WARN_ON(!intel_plane->can_scale);
 
-       /*
-        * We can take a larger source and scale it down, but
-        * only so much...  16x is the max on SNB.
-        */
-       if (((src_w * src_h) / (crtc_w * crtc_h)) > intel_plane->max_downscale)
-               return -EINVAL;
+               /* FIXME interlacing min height is 6 */
+
+               if (crtc_w < 3 || crtc_h < 3)
+                       visible = false;
+
+               if (src_w < 3 || src_h < 3)
+                       visible = false;
+
+               width_bytes = ((src_x * pixel_size) & 63) + src_w * pixel_size;
+
+               if (src_w > 2048 || src_h > 2048 ||
+                   width_bytes > 4096 || fb->pitches[0] > 4096) {
+                       DRM_DEBUG_KMS("Source dimensions exceed hardware limits\n");
+                       return -EINVAL;
+               }
+       }
+
+       dst.x1 = crtc_x;
+       dst.x2 = crtc_x + crtc_w;
+       dst.y1 = crtc_y;
+       dst.y2 = crtc_y + crtc_h;
 
        /*
         * If the sprite is completely covering the primary plane,
         * we can disable the primary and save power.
         */
-       if ((crtc_x == 0) && (crtc_y == 0) &&
-           (crtc_w == primary_w) && (crtc_h == primary_h))
-               disable_primary = true;
+       disable_primary = drm_rect_equals(&dst, &clip);
+       WARN_ON(disable_primary && !visible);
 
        mutex_lock(&dev->struct_mutex);
 
@@ -708,8 +817,12 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
        if (!disable_primary)
                intel_enable_primary(crtc);
 
-       intel_plane->update_plane(plane, fb, obj, crtc_x, crtc_y,
-                                 crtc_w, crtc_h, x, y, src_w, src_h);
+       if (visible)
+               intel_plane->update_plane(plane, fb, obj,
+                                         crtc_x, crtc_y, crtc_w, crtc_h,
+                                         src_x, src_y, src_w, src_h);
+       else
+               intel_plane->disable_plane(plane);
 
        if (disable_primary)
                intel_disable_primary(crtc);
@@ -732,7 +845,6 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 
 out_unlock:
        mutex_unlock(&dev->struct_mutex);
-out:
        return ret;
 }
 
@@ -845,6 +957,14 @@ void intel_plane_restore(struct drm_plane *plane)
                           intel_plane->src_w, intel_plane->src_h);
 }
 
+void intel_plane_disable(struct drm_plane *plane)
+{
+       if (!plane->crtc || !plane->fb)
+               return;
+
+       intel_disable_plane(plane);
+}
+
 static const struct drm_plane_funcs intel_plane_funcs = {
        .update_plane = intel_update_plane,
        .disable_plane = intel_disable_plane,
@@ -918,13 +1038,15 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane)
                break;
 
        case 7:
-               if (IS_HASWELL(dev) || IS_VALLEYVIEW(dev))
-                       intel_plane->can_scale = false;
-               else
+               if (IS_IVYBRIDGE(dev)) {
                        intel_plane->can_scale = true;
+                       intel_plane->max_downscale = 2;
+               } else {
+                       intel_plane->can_scale = false;
+                       intel_plane->max_downscale = 1;
+               }
 
                if (IS_VALLEYVIEW(dev)) {
-                       intel_plane->max_downscale = 1;
                        intel_plane->update_plane = vlv_update_plane;
                        intel_plane->disable_plane = vlv_disable_plane;
                        intel_plane->update_colorkey = vlv_update_colorkey;
@@ -933,7 +1055,6 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane)
                        plane_formats = vlv_plane_formats;
                        num_plane_formats = ARRAY_SIZE(vlv_plane_formats);
                } else {
-                       intel_plane->max_downscale = 2;
                        intel_plane->update_plane = ivb_update_plane;
                        intel_plane->disable_plane = ivb_disable_plane;
                        intel_plane->update_colorkey = ivb_update_colorkey;
index b945bc5..39debd8 100644 (file)
@@ -914,9 +914,6 @@ intel_tv_compute_config(struct intel_encoder *encoder,
        if (!tv_mode)
                return false;
 
-       if (intel_encoder_check_is_cloned(&intel_tv->base))
-               return false;
-
        pipe_config->adjusted_mode.clock = tv_mode->clock;
        DRM_DEBUG_KMS("forcing bpc to 8 for TV\n");
        pipe_config->pipe_bpp = 8*3;
@@ -1521,12 +1518,12 @@ static int tv_is_present_in_vbt(struct drm_device *dev)
        struct child_device_config *p_child;
        int i, ret;
 
-       if (!dev_priv->child_dev_num)
+       if (!dev_priv->vbt.child_dev_num)
                return 1;
 
        ret = 0;
-       for (i = 0; i < dev_priv->child_dev_num; i++) {
-               p_child = dev_priv->child_dev + i;
+       for (i = 0; i < dev_priv->vbt.child_dev_num; i++) {
+               p_child = dev_priv->vbt.child_dev + i;
                /*
                 * If the device type is not TV, continue.
                 */
@@ -1564,7 +1561,7 @@ intel_tv_init(struct drm_device *dev)
                return;
        }
        /* Even if we have an encoder we may not have a connector */
-       if (!dev_priv->int_tv_support)
+       if (!dev_priv->vbt.int_tv_support)
                return;
 
        /*
index 7db592e..a9a0300 100644 (file)
@@ -1,5 +1,5 @@
 ccflags-y := -Iinclude/drm
-mgag200-y   := mgag200_main.o mgag200_mode.o \
+mgag200-y   := mgag200_main.o mgag200_mode.o mgag200_cursor.o \
        mgag200_drv.o mgag200_fb.o mgag200_i2c.o mgag200_ttm.o
 
 obj-$(CONFIG_DRM_MGAG200) += mgag200.o
diff --git a/drivers/gpu/drm/mgag200/mgag200_cursor.c b/drivers/gpu/drm/mgag200/mgag200_cursor.c
new file mode 100644 (file)
index 0000000..801731a
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2013 Matrox Graphics
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License version 2. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * Author: Christopher Harvey <charvey@matrox.com>
+ */
+
+#include <drm/drmP.h>
+#include "mgag200_drv.h"
+
+static bool warn_transparent = true;
+static bool warn_palette = true;
+
+/*
+  Hide the cursor off screen. We can't disable the cursor hardware because it
+  takes too long to re-activate and causes momentary corruption
+*/
+static void mga_hide_cursor(struct mga_device *mdev)
+{
+       WREG8(MGA_CURPOSXL, 0);
+       WREG8(MGA_CURPOSXH, 0);
+       mgag200_bo_unpin(mdev->cursor.pixels_1);
+       mgag200_bo_unpin(mdev->cursor.pixels_2);
+}
+
+int mga_crtc_cursor_set(struct drm_crtc *crtc,
+                       struct drm_file *file_priv,
+                       uint32_t handle,
+                       uint32_t width,
+                       uint32_t height)
+{
+       struct drm_device *dev = (struct drm_device *)file_priv->minor->dev;
+       struct mga_device *mdev = (struct mga_device *)dev->dev_private;
+       struct mgag200_bo *pixels_1 = mdev->cursor.pixels_1;
+       struct mgag200_bo *pixels_2 = mdev->cursor.pixels_2;
+       struct mgag200_bo *pixels_current = mdev->cursor.pixels_current;
+       struct mgag200_bo *pixels_prev = mdev->cursor.pixels_prev;
+       struct drm_gem_object *obj;
+       struct mgag200_bo *bo = NULL;
+       int ret = 0;
+       unsigned int i, row, col;
+       uint32_t colour_set[16];
+       uint32_t *next_space = &colour_set[0];
+       uint32_t *palette_iter;
+       uint32_t this_colour;
+       bool found = false;
+       int colour_count = 0;
+       u64 gpu_addr;
+       u8 reg_index;
+       u8 this_row[48];
+
+       if (!pixels_1 || !pixels_2) {
+               WREG8(MGA_CURPOSXL, 0);
+               WREG8(MGA_CURPOSXH, 0);
+               return -ENOTSUPP; /* Didn't allocate space for cursors */
+       }
+
+       if ((width != 64 || height != 64) && handle) {
+               WREG8(MGA_CURPOSXL, 0);
+               WREG8(MGA_CURPOSXH, 0);
+               return -EINVAL;
+       }
+
+       BUG_ON(pixels_1 != pixels_current && pixels_1 != pixels_prev);
+       BUG_ON(pixels_2 != pixels_current && pixels_2 != pixels_prev);
+       BUG_ON(pixels_current == pixels_prev);
+
+       ret = mgag200_bo_reserve(pixels_1, true);
+       if (ret) {
+               WREG8(MGA_CURPOSXL, 0);
+               WREG8(MGA_CURPOSXH, 0);
+               return ret;
+       }
+       ret = mgag200_bo_reserve(pixels_2, true);
+       if (ret) {
+               WREG8(MGA_CURPOSXL, 0);
+               WREG8(MGA_CURPOSXH, 0);
+               mgag200_bo_unreserve(pixels_1);
+               return ret;
+       }
+
+       if (!handle) {
+               mga_hide_cursor(mdev);
+               ret = 0;
+               goto out1;
+       }
+
+       /* Move cursor buffers into VRAM if they aren't already */
+       if (!pixels_1->pin_count) {
+               ret = mgag200_bo_pin(pixels_1, TTM_PL_FLAG_VRAM,
+                                    &mdev->cursor.pixels_1_gpu_addr);
+               if (ret)
+                       goto out1;
+       }
+       if (!pixels_2->pin_count) {
+               ret = mgag200_bo_pin(pixels_2, TTM_PL_FLAG_VRAM,
+                                    &mdev->cursor.pixels_2_gpu_addr);
+               if (ret) {
+                       mgag200_bo_unpin(pixels_1);
+                       goto out1;
+               }
+       }
+
+       mutex_lock(&dev->struct_mutex);
+       obj = drm_gem_object_lookup(dev, file_priv, handle);
+       if (!obj) {
+               mutex_unlock(&dev->struct_mutex);
+               ret = -ENOENT;
+               goto out1;
+       }
+       drm_gem_object_unreference(obj);
+       mutex_unlock(&dev->struct_mutex);
+
+       bo = gem_to_mga_bo(obj);
+       ret = mgag200_bo_reserve(bo, true);
+       if (ret) {
+               dev_err(&dev->pdev->dev, "failed to reserve user bo\n");
+               goto out1;
+       }
+       if (!bo->kmap.virtual) {
+               ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
+               if (ret) {
+                       dev_err(&dev->pdev->dev, "failed to kmap user buffer updates\n");
+                       goto out2;
+               }
+       }
+
+       memset(&colour_set[0], 0, sizeof(uint32_t)*16);
+       /* width*height*4 = 16384 */
+       for (i = 0; i < 16384; i += 4) {
+               this_colour = ioread32(bo->kmap.virtual + i);
+               /* No transparency */
+               if (this_colour>>24 != 0xff &&
+                       this_colour>>24 != 0x0) {
+                       if (warn_transparent) {
+                               dev_info(&dev->pdev->dev, "Video card doesn't support cursors with partial transparency.\n");
+                               dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n");
+                               warn_transparent = false; /* Only tell the user once. */
+                       }
+                       ret = -EINVAL;
+                       goto out3;
+               }
+               /* Don't need to store transparent pixels as colours */
+               if (this_colour>>24 == 0x0)
+                       continue;
+               found = false;
+               for (palette_iter = &colour_set[0]; palette_iter != next_space; palette_iter++) {
+                       if (*palette_iter == this_colour) {
+                               found = true;
+                               break;
+                       }
+               }
+               if (found)
+                       continue;
+               /* We only support 4bit paletted cursors */
+               if (colour_count >= 16) {
+                       if (warn_palette) {
+                               dev_info(&dev->pdev->dev, "Video card only supports cursors with up to 16 colours.\n");
+                               dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n");
+                               warn_palette = false; /* Only tell the user once. */
+                       }
+                       ret = -EINVAL;
+                       goto out3;
+               }
+               *next_space = this_colour;
+               next_space++;
+               colour_count++;
+       }
+
+       /* Program colours from cursor icon into palette */
+       for (i = 0; i < colour_count; i++) {
+               if (i <= 2)
+                       reg_index = 0x8 + i*0x4;
+               else
+                       reg_index = 0x60 + i*0x3;
+               WREG_DAC(reg_index, colour_set[i] & 0xff);
+               WREG_DAC(reg_index+1, colour_set[i]>>8 & 0xff);
+               WREG_DAC(reg_index+2, colour_set[i]>>16 & 0xff);
+               BUG_ON((colour_set[i]>>24 & 0xff) != 0xff);
+       }
+
+       /* Map up-coming buffer to write colour indices */
+       if (!pixels_prev->kmap.virtual) {
+               ret = ttm_bo_kmap(&pixels_prev->bo, 0,
+                                 pixels_prev->bo.num_pages,
+                                 &pixels_prev->kmap);
+               if (ret) {
+                       dev_err(&dev->pdev->dev, "failed to kmap cursor updates\n");
+                       goto out3;
+               }
+       }
+
+       /* now write colour indices into hardware cursor buffer */
+       for (row = 0; row < 64; row++) {
+               memset(&this_row[0], 0, 48);
+               for (col = 0; col < 64; col++) {
+                       this_colour = ioread32(bo->kmap.virtual + 4*(col + 64*row));
+                       /* write transparent pixels */
+                       if (this_colour>>24 == 0x0) {
+                               this_row[47 - col/8] |= 0x80>>(col%8);
+                               continue;
+                       }
+
+                       /* write colour index here */
+                       for (i = 0; i < colour_count; i++) {
+                               if (colour_set[i] == this_colour) {
+                                       if (col % 2)
+                                               this_row[col/2] |= i<<4;
+                                       else
+                                               this_row[col/2] |= i;
+                                       break;
+                               }
+                       }
+               }
+               memcpy_toio(pixels_prev->kmap.virtual + row*48, &this_row[0], 48);
+       }
+
+       /* Program gpu address of cursor buffer */
+       if (pixels_prev == pixels_1)
+               gpu_addr = mdev->cursor.pixels_1_gpu_addr;
+       else
+               gpu_addr = mdev->cursor.pixels_2_gpu_addr;
+       WREG_DAC(MGA1064_CURSOR_BASE_ADR_LOW, (u8)((gpu_addr>>10) & 0xff));
+       WREG_DAC(MGA1064_CURSOR_BASE_ADR_HI, (u8)((gpu_addr>>18) & 0x3f));
+
+       /* Adjust cursor control register to turn on the cursor */
+       WREG_DAC(MGA1064_CURSOR_CTL, 4); /* 16-colour palletized cursor mode */
+
+       /* Now swap internal buffer pointers */
+       if (mdev->cursor.pixels_1 == mdev->cursor.pixels_prev) {
+               mdev->cursor.pixels_prev = mdev->cursor.pixels_2;
+               mdev->cursor.pixels_current = mdev->cursor.pixels_1;
+       } else if (mdev->cursor.pixels_1 == mdev->cursor.pixels_current) {
+               mdev->cursor.pixels_prev = mdev->cursor.pixels_1;
+               mdev->cursor.pixels_current = mdev->cursor.pixels_2;
+       } else {
+               BUG();
+       }
+       ret = 0;
+
+       ttm_bo_kunmap(&pixels_prev->kmap);
+ out3:
+       ttm_bo_kunmap(&bo->kmap);
+ out2:
+       mgag200_bo_unreserve(bo);
+ out1:
+       if (ret)
+               mga_hide_cursor(mdev);
+       mgag200_bo_unreserve(pixels_1);
+       mgag200_bo_unreserve(pixels_2);
+       return ret;
+}
+
+int mga_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+       struct mga_device *mdev = (struct mga_device *)crtc->dev->dev_private;
+       /* Our origin is at (64,64) */
+       x += 64;
+       y += 64;
+
+       BUG_ON(x <= 0);
+       BUG_ON(y <= 0);
+       BUG_ON(x & ~0xffff);
+       BUG_ON(y & ~0xffff);
+
+       WREG8(MGA_CURPOSXL, x & 0xff);
+       WREG8(MGA_CURPOSXH, (x>>8) & 0xff);
+
+       WREG8(MGA_CURPOSYL, y & 0xff);
+       WREG8(MGA_CURPOSYH, (y>>8) & 0xff);
+       return 0;
+}
index bf29b2f..12e2499 100644 (file)
@@ -149,6 +149,21 @@ struct mga_connector {
        struct mga_i2c_chan *i2c;
 };
 
+struct mga_cursor {
+       /*
+          We have to have 2 buffers for the cursor to avoid occasional
+          corruption while switching cursor icons.
+          If either of these is NULL, then don't do hardware cursors, and
+          fall back to software.
+       */
+       struct mgag200_bo *pixels_1;
+       struct mgag200_bo *pixels_2;
+       u64 pixels_1_gpu_addr, pixels_2_gpu_addr;
+       /* The currently displayed icon, this points to one of pixels_1, or pixels_2 */
+       struct mgag200_bo *pixels_current;
+       /* The previously displayed icon */
+       struct mgag200_bo *pixels_prev;
+};
 
 struct mga_mc {
        resource_size_t                 vram_size;
@@ -181,6 +196,7 @@ struct mga_device {
        struct mga_mode_info            mode_info;
 
        struct mga_fbdev *mfbdev;
+       struct mga_cursor cursor;
 
        bool                            suspended;
        int                             num_crtc;
@@ -198,7 +214,8 @@ struct mga_device {
                struct ttm_bo_device bdev;
        } ttm;
 
-       u32 reg_1e24; /* SE model number */
+       /* SE model number stored in reg 0x1e24 */
+       u32 unique_rev_id;
 };
 
 
@@ -263,8 +280,24 @@ void mgag200_i2c_destroy(struct mga_i2c_chan *i2c);
 #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
 void mgag200_ttm_placement(struct mgag200_bo *bo, int domain);
 
-int mgag200_bo_reserve(struct mgag200_bo *bo, bool no_wait);
-void mgag200_bo_unreserve(struct mgag200_bo *bo);
+static inline int mgag200_bo_reserve(struct mgag200_bo *bo, bool no_wait)
+{
+       int ret;
+
+       ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+       if (ret) {
+               if (ret != -ERESTARTSYS && ret != -EBUSY)
+                       DRM_ERROR("reserve failed %p\n", bo);
+               return ret;
+       }
+       return 0;
+}
+
+static inline void mgag200_bo_unreserve(struct mgag200_bo *bo)
+{
+       ttm_bo_unreserve(&bo->bo);
+}
+
 int mgag200_bo_create(struct drm_device *dev, int size, int align,
                      uint32_t flags, struct mgag200_bo **pastbo);
 int mgag200_mm_init(struct mga_device *mdev);
@@ -273,4 +306,9 @@ int mgag200_mmap(struct file *filp, struct vm_area_struct *vma);
 int mgag200_bo_pin(struct mgag200_bo *bo, u32 pl_flag, u64 *gpu_addr);
 int mgag200_bo_unpin(struct mgag200_bo *bo);
 int mgag200_bo_push_sysram(struct mgag200_bo *bo);
+                          /* mgag200_cursor.c */
+int mga_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
+                                               uint32_t handle, uint32_t width, uint32_t height);
+int mga_crtc_cursor_move(struct drm_crtc *crtc, int x, int y);
+
 #endif                         /* __MGAG200_DRV_H__ */
index 5da824c..964f58c 100644 (file)
@@ -27,7 +27,7 @@ static void mga_dirty_update(struct mga_fbdev *mfbdev,
        struct mgag200_bo *bo;
        int src_offset, dst_offset;
        int bpp = (mfbdev->mfb.base.bits_per_pixel + 7)/8;
-       int ret;
+       int ret = -EBUSY;
        bool unmap = false;
        bool store_for_later = false;
        int x2, y2;
@@ -41,7 +41,8 @@ static void mga_dirty_update(struct mga_fbdev *mfbdev,
         * then the BO is being moved and we should
         * store up the damage until later.
         */
-       ret = mgag200_bo_reserve(bo, true);
+       if (!in_interrupt())
+               ret = mgag200_bo_reserve(bo, true);
        if (ret) {
                if (ret != -EBUSY)
                        return;
index 9905923..9fa5685 100644 (file)
@@ -176,7 +176,7 @@ static int mgag200_device_init(struct drm_device *dev,
 
        /* stash G200 SE model number for later use */
        if (IS_G200_SE(mdev))
-               mdev->reg_1e24 = RREG32(0x1e24);
+               mdev->unique_rev_id = RREG32(0x1e24);
 
        ret = mga_vram_init(mdev);
        if (ret)
@@ -209,7 +209,7 @@ int mgag200_driver_load(struct drm_device *dev, unsigned long flags)
        r = mgag200_device_init(dev, flags);
        if (r) {
                dev_err(&dev->pdev->dev, "Fatal error during GPU init: %d\n", r);
-               goto out;
+               return r;
        }
        r = mgag200_mm_init(mdev);
        if (r)
@@ -221,8 +221,27 @@ int mgag200_driver_load(struct drm_device *dev, unsigned long flags)
        dev->mode_config.prefer_shadow = 1;
 
        r = mgag200_modeset_init(mdev);
-       if (r)
+       if (r) {
                dev_err(&dev->pdev->dev, "Fatal error during modeset init: %d\n", r);
+               goto out;
+       }
+
+       /* Make small buffers to store a hardware cursor (double buffered icon updates) */
+       mgag200_bo_create(dev, roundup(48*64, PAGE_SIZE), 0, 0,
+                                         &mdev->cursor.pixels_1);
+       mgag200_bo_create(dev, roundup(48*64, PAGE_SIZE), 0, 0,
+                                         &mdev->cursor.pixels_2);
+       if (!mdev->cursor.pixels_2 || !mdev->cursor.pixels_1)
+               goto cursor_nospace;
+       mdev->cursor.pixels_current = mdev->cursor.pixels_1;
+       mdev->cursor.pixels_prev = mdev->cursor.pixels_2;
+       goto cursor_done;
+ cursor_nospace:
+       mdev->cursor.pixels_1 = NULL;
+       mdev->cursor.pixels_2 = NULL;
+       dev_warn(&dev->pdev->dev, "Could not allocate space for cursors. Not doing hardware cursors.\n");
+ cursor_done:
+
 out:
        if (r)
                mgag200_driver_unload(dev);
index ee66bad..251784a 100644 (file)
@@ -1008,7 +1008,7 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc,
 
 
        if (IS_G200_SE(mdev)) {
-               if (mdev->reg_1e24 >= 0x02) {
+               if (mdev->unique_rev_id >= 0x02) {
                        u8 hi_pri_lvl;
                        u32 bpp;
                        u32 mb;
@@ -1038,7 +1038,7 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc,
                        WREG8(MGAREG_CRTCEXT_DATA, hi_pri_lvl);
                } else {
                        WREG8(MGAREG_CRTCEXT_INDEX, 0x06);
-                       if (mdev->reg_1e24 >= 0x01)
+                       if (mdev->unique_rev_id >= 0x01)
                                WREG8(MGAREG_CRTCEXT_DATA, 0x03);
                        else
                                WREG8(MGAREG_CRTCEXT_DATA, 0x04);
@@ -1253,6 +1253,8 @@ static void mga_crtc_destroy(struct drm_crtc *crtc)
 
 /* These provide the minimum set of functions required to handle a CRTC */
 static const struct drm_crtc_funcs mga_crtc_funcs = {
+       .cursor_set = mga_crtc_cursor_set,
+       .cursor_move = mga_crtc_cursor_move,
        .gamma_set = mga_crtc_gamma_set,
        .set_config = drm_crtc_helper_set_config,
        .destroy = mga_crtc_destroy,
@@ -1410,6 +1412,32 @@ static int mga_vga_get_modes(struct drm_connector *connector)
        return ret;
 }
 
+static uint32_t mga_vga_calculate_mode_bandwidth(struct drm_display_mode *mode,
+                                                       int bits_per_pixel)
+{
+       uint32_t total_area, divisor;
+       int64_t active_area, pixels_per_second, bandwidth;
+       uint64_t bytes_per_pixel = (bits_per_pixel + 7) / 8;
+
+       divisor = 1024;
+
+       if (!mode->htotal || !mode->vtotal || !mode->clock)
+               return 0;
+
+       active_area = mode->hdisplay * mode->vdisplay;
+       total_area = mode->htotal * mode->vtotal;
+
+       pixels_per_second = active_area * mode->clock * 1000;
+       do_div(pixels_per_second, total_area);
+
+       bandwidth = pixels_per_second * bytes_per_pixel * 100;
+       do_div(bandwidth, divisor);
+
+       return (uint32_t)(bandwidth);
+}
+
+#define MODE_BANDWIDTH MODE_BAD
+
 static int mga_vga_mode_valid(struct drm_connector *connector,
                                 struct drm_display_mode *mode)
 {
@@ -1421,7 +1449,45 @@ static int mga_vga_mode_valid(struct drm_connector *connector,
        int bpp = 32;
        int i = 0;
 
-       /* FIXME: Add bandwidth and g200se limitations */
+       if (IS_G200_SE(mdev)) {
+               if (mdev->unique_rev_id == 0x01) {
+                       if (mode->hdisplay > 1600)
+                               return MODE_VIRTUAL_X;
+                       if (mode->vdisplay > 1200)
+                               return MODE_VIRTUAL_Y;
+                       if (mga_vga_calculate_mode_bandwidth(mode, bpp)
+                               > (24400 * 1024))
+                               return MODE_BANDWIDTH;
+               } else if (mdev->unique_rev_id >= 0x02) {
+                       if (mode->hdisplay > 1920)
+                               return MODE_VIRTUAL_X;
+                       if (mode->vdisplay > 1200)
+                               return MODE_VIRTUAL_Y;
+                       if (mga_vga_calculate_mode_bandwidth(mode, bpp)
+                               > (30100 * 1024))
+                               return MODE_BANDWIDTH;
+               }
+       } else if (mdev->type == G200_WB) {
+               if (mode->hdisplay > 1280)
+                       return MODE_VIRTUAL_X;
+               if (mode->vdisplay > 1024)
+                       return MODE_VIRTUAL_Y;
+               if (mga_vga_calculate_mode_bandwidth(mode,
+                       bpp > (31877 * 1024)))
+                       return MODE_BANDWIDTH;
+       } else if (mdev->type == G200_EV &&
+               (mga_vga_calculate_mode_bandwidth(mode, bpp)
+                       > (32700 * 1024))) {
+               return MODE_BANDWIDTH;
+       } else if (mode->type == G200_EH &&
+               (mga_vga_calculate_mode_bandwidth(mode, bpp)
+                       > (37500 * 1024))) {
+               return MODE_BANDWIDTH;
+       } else if (mode->type == G200_ER &&
+               (mga_vga_calculate_mode_bandwidth(mode,
+                       bpp) > (55000 * 1024))) {
+               return MODE_BANDWIDTH;
+       }
 
        if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 ||
            mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 ||
index fb24d86..3ae442a 100644 (file)
 #define MGAREG_CRTCEXT_INDEX   0x1fde
 #define MGAREG_CRTCEXT_DATA    0x1fdf
 
-
+/* Cursor X and Y position */
+#define MGA_CURPOSXL 0x3c0c
+#define MGA_CURPOSXH 0x3c0d
+#define MGA_CURPOSYL 0x3c0e
+#define MGA_CURPOSYH 0x3c0f
 
 /* MGA bits for registers PCI_OPTION_REG */
 #define MGA1064_OPT_SYS_CLK_PCI                ( 0x00 << 0 )
index 401c989..3acb2b0 100644 (file)
@@ -270,26 +270,20 @@ int mgag200_mm_init(struct mga_device *mdev)
                return ret;
        }
 
-       mdev->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0),
-                                   pci_resource_len(dev->pdev, 0),
-                                   DRM_MTRR_WC);
+       mdev->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0),
+                                        pci_resource_len(dev->pdev, 0));
 
        return 0;
 }
 
 void mgag200_mm_fini(struct mga_device *mdev)
 {
-       struct drm_device *dev = mdev->dev;
        ttm_bo_device_release(&mdev->ttm.bdev);
 
        mgag200_ttm_global_release(mdev);
 
-       if (mdev->fb_mtrr >= 0) {
-               drm_mtrr_del(mdev->fb_mtrr,
-                            pci_resource_start(dev->pdev, 0),
-                            pci_resource_len(dev->pdev, 0), DRM_MTRR_WC);
-               mdev->fb_mtrr = -1;
-       }
+       arch_phys_wc_del(mdev->fb_mtrr);
+       mdev->fb_mtrr = 0;
 }
 
 void mgag200_ttm_placement(struct mgag200_bo *bo, int domain)
@@ -309,24 +303,6 @@ void mgag200_ttm_placement(struct mgag200_bo *bo, int domain)
        bo->placement.num_busy_placement = c;
 }
 
-int mgag200_bo_reserve(struct mgag200_bo *bo, bool no_wait)
-{
-       int ret;
-
-       ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
-       if (ret) {
-               if (ret != -ERESTARTSYS && ret != -EBUSY)
-                       DRM_ERROR("reserve failed %p %d\n", bo, ret);
-               return ret;
-       }
-       return 0;
-}
-
-void mgag200_bo_unreserve(struct mgag200_bo *bo)
-{
-       ttm_bo_unreserve(&bo->bo);
-}
-
 int mgag200_bo_create(struct drm_device *dev, int size, int align,
                  uint32_t flags, struct mgag200_bo **pmgabo)
 {
index a7ff6d5..ff80f12 100644 (file)
@@ -15,6 +15,13 @@ config DRM_NOUVEAU
        select ACPI_WMI if ACPI && X86
        select MXM_WMI if ACPI && X86
        select POWER_SUPPLY
+       # Similar to i915, we need to select ACPI_VIDEO and it's dependencies
+       select BACKLIGHT_LCD_SUPPORT if ACPI && X86
+       select BACKLIGHT_CLASS_DEVICE if ACPI && X86
+       select VIDEO_OUTPUT_CONTROL if ACPI && X86
+       select INPUT if ACPI && X86
+       select THERMAL if ACPI && X86
+       select ACPI_VIDEO if ACPI && X86
        help
          Choose this option for open-source nVidia support.
 
index 998e8b4..d939a1d 100644 (file)
@@ -12,7 +12,6 @@ nouveau-y += core/core/engctx.o
 nouveau-y += core/core/engine.o
 nouveau-y += core/core/enum.o
 nouveau-y += core/core/event.o
-nouveau-y += core/core/falcon.o
 nouveau-y += core/core/gpuobj.o
 nouveau-y += core/core/handle.o
 nouveau-y += core/core/mm.o
@@ -60,6 +59,8 @@ nouveau-y += core/subdev/devinit/nv10.o
 nouveau-y += core/subdev/devinit/nv1a.o
 nouveau-y += core/subdev/devinit/nv20.o
 nouveau-y += core/subdev/devinit/nv50.o
+nouveau-y += core/subdev/devinit/nva3.o
+nouveau-y += core/subdev/devinit/nvc0.o
 nouveau-y += core/subdev/fb/base.o
 nouveau-y += core/subdev/fb/nv04.o
 nouveau-y += core/subdev/fb/nv10.o
@@ -78,6 +79,17 @@ 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/nvc0.o
+nouveau-y += core/subdev/fb/ramnv04.o
+nouveau-y += core/subdev/fb/ramnv10.o
+nouveau-y += core/subdev/fb/ramnv1a.o
+nouveau-y += core/subdev/fb/ramnv20.o
+nouveau-y += core/subdev/fb/ramnv40.o
+nouveau-y += core/subdev/fb/ramnv41.o
+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/ramnvc0.o
 nouveau-y += core/subdev/gpio/base.o
 nouveau-y += core/subdev/gpio/nv10.o
 nouveau-y += core/subdev/gpio/nv50.o
@@ -129,12 +141,15 @@ nouveau-y += core/subdev/vm/nv44.o
 nouveau-y += core/subdev/vm/nv50.o
 nouveau-y += core/subdev/vm/nvc0.o
 
+nouveau-y += core/engine/falcon.o
+nouveau-y += core/engine/xtensa.o
 nouveau-y += core/engine/dmaobj/base.o
 nouveau-y += core/engine/dmaobj/nv04.o
 nouveau-y += core/engine/dmaobj/nv50.o
 nouveau-y += core/engine/dmaobj/nvc0.o
 nouveau-y += core/engine/dmaobj/nvd0.o
 nouveau-y += core/engine/bsp/nv84.o
+nouveau-y += core/engine/bsp/nv98.o
 nouveau-y += core/engine/bsp/nvc0.o
 nouveau-y += core/engine/bsp/nve0.o
 nouveau-y += core/engine/copy/nva3.o
@@ -185,7 +200,13 @@ nouveau-y += core/engine/fifo/nve0.o
 nouveau-y += core/engine/graph/ctxnv40.o
 nouveau-y += core/engine/graph/ctxnv50.o
 nouveau-y += core/engine/graph/ctxnvc0.o
-nouveau-y += core/engine/graph/ctxnve0.o
+nouveau-y += core/engine/graph/ctxnvc1.o
+nouveau-y += core/engine/graph/ctxnvc3.o
+nouveau-y += core/engine/graph/ctxnvc8.o
+nouveau-y += core/engine/graph/ctxnvd7.o
+nouveau-y += core/engine/graph/ctxnvd9.o
+nouveau-y += core/engine/graph/ctxnve4.o
+nouveau-y += core/engine/graph/ctxnvf0.o
 nouveau-y += core/engine/graph/nv04.o
 nouveau-y += core/engine/graph/nv10.o
 nouveau-y += core/engine/graph/nv20.o
@@ -197,7 +218,13 @@ nouveau-y += core/engine/graph/nv35.o
 nouveau-y += core/engine/graph/nv40.o
 nouveau-y += core/engine/graph/nv50.o
 nouveau-y += core/engine/graph/nvc0.o
-nouveau-y += core/engine/graph/nve0.o
+nouveau-y += core/engine/graph/nvc1.o
+nouveau-y += core/engine/graph/nvc3.o
+nouveau-y += core/engine/graph/nvc8.o
+nouveau-y += core/engine/graph/nvd7.o
+nouveau-y += core/engine/graph/nvd9.o
+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/nv50.o
@@ -209,6 +236,7 @@ nouveau-y += core/engine/software/nv10.o
 nouveau-y += core/engine/software/nv50.o
 nouveau-y += core/engine/software/nvc0.o
 nouveau-y += core/engine/vp/nv84.o
+nouveau-y += core/engine/vp/nv98.o
 nouveau-y += core/engine/vp/nvc0.o
 nouveau-y += core/engine/vp/nve0.o
 
diff --git a/drivers/gpu/drm/nouveau/core/core/falcon.c b/drivers/gpu/drm/nouveau/core/core/falcon.c
deleted file mode 100644 (file)
index e05c157..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * 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.
- */
-
-#include <core/falcon.h>
-
-#include <subdev/timer.h>
-
-u32
-_nouveau_falcon_rd32(struct nouveau_object *object, u64 addr)
-{
-       struct nouveau_falcon *falcon = (void *)object;
-       return nv_rd32(falcon, falcon->addr + addr);
-}
-
-void
-_nouveau_falcon_wr32(struct nouveau_object *object, u64 addr, u32 data)
-{
-       struct nouveau_falcon *falcon = (void *)object;
-       nv_wr32(falcon, falcon->addr + addr, data);
-}
-
-int
-_nouveau_falcon_init(struct nouveau_object *object)
-{
-       struct nouveau_device *device = nv_device(object);
-       struct nouveau_falcon *falcon = (void *)object;
-       const struct firmware *fw;
-       char name[32] = "internal";
-       int ret, i;
-       u32 caps;
-
-       /* enable engine, and determine its capabilities */
-       ret = nouveau_engine_init(&falcon->base);
-       if (ret)
-               return ret;
-
-       if (device->chipset <  0xa3 ||
-           device->chipset == 0xaa || device->chipset == 0xac) {
-               falcon->version = 0;
-               falcon->secret  = (falcon->addr == 0x087000) ? 1 : 0;
-       } else {
-               caps = nv_ro32(falcon, 0x12c);
-               falcon->version = (caps & 0x0000000f);
-               falcon->secret  = (caps & 0x00000030) >> 4;
-       }
-
-       caps = nv_ro32(falcon, 0x108);
-       falcon->code.limit = (caps & 0x000001ff) << 8;
-       falcon->data.limit = (caps & 0x0003fe00) >> 1;
-
-       nv_debug(falcon, "falcon version: %d\n", falcon->version);
-       nv_debug(falcon, "secret level: %d\n", falcon->secret);
-       nv_debug(falcon, "code limit: %d\n", falcon->code.limit);
-       nv_debug(falcon, "data limit: %d\n", falcon->data.limit);
-
-       /* wait for 'uc halted' to be signalled before continuing */
-       if (falcon->secret && falcon->version < 4) {
-               if (!falcon->version)
-                       nv_wait(falcon, 0x008, 0x00000010, 0x00000010);
-               else
-                       nv_wait(falcon, 0x180, 0x80000000, 0);
-               nv_wo32(falcon, 0x004, 0x00000010);
-       }
-
-       /* disable all interrupts */
-       nv_wo32(falcon, 0x014, 0xffffffff);
-
-       /* no default ucode provided by the engine implementation, try and
-        * locate a "self-bootstrapping" firmware image for the engine
-        */
-       if (!falcon->code.data) {
-               snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03x",
-                        device->chipset, falcon->addr >> 12);
-
-               ret = request_firmware(&fw, name, &device->pdev->dev);
-               if (ret == 0) {
-                       falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL);
-                       falcon->code.size = fw->size;
-                       falcon->data.data = NULL;
-                       falcon->data.size = 0;
-                       release_firmware(fw);
-               }
-
-               falcon->external = true;
-       }
-
-       /* next step is to try and load "static code/data segment" firmware
-        * images for the engine
-        */
-       if (!falcon->code.data) {
-               snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xd",
-                        device->chipset, falcon->addr >> 12);
-
-               ret = request_firmware(&fw, name, &device->pdev->dev);
-               if (ret) {
-                       nv_error(falcon, "unable to load firmware data\n");
-                       return ret;
-               }
-
-               falcon->data.data = kmemdup(fw->data, fw->size, GFP_KERNEL);
-               falcon->data.size = fw->size;
-               release_firmware(fw);
-               if (!falcon->data.data)
-                       return -ENOMEM;
-
-               snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xc",
-                        device->chipset, falcon->addr >> 12);
-
-               ret = request_firmware(&fw, name, &device->pdev->dev);
-               if (ret) {
-                       nv_error(falcon, "unable to load firmware code\n");
-                       return ret;
-               }
-
-               falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL);
-               falcon->code.size = fw->size;
-               release_firmware(fw);
-               if (!falcon->code.data)
-                       return -ENOMEM;
-       }
-
-       nv_debug(falcon, "firmware: %s (%s)\n", name, falcon->data.data ?
-                "static code/data segments" : "self-bootstrapping");
-
-       /* ensure any "self-bootstrapping" firmware image is in vram */
-       if (!falcon->data.data && !falcon->core) {
-               ret = nouveau_gpuobj_new(object->parent, NULL,
-                                        falcon->code.size, 256, 0,
-                                       &falcon->core);
-               if (ret) {
-                       nv_error(falcon, "core allocation failed, %d\n", ret);
-                       return ret;
-               }
-
-               for (i = 0; i < falcon->code.size; i += 4)
-                       nv_wo32(falcon->core, i, falcon->code.data[i / 4]);
-       }
-
-       /* upload firmware bootloader (or the full code segments) */
-       if (falcon->core) {
-               if (device->card_type < NV_C0)
-                       nv_wo32(falcon, 0x618, 0x04000000);
-               else
-                       nv_wo32(falcon, 0x618, 0x00000114);
-               nv_wo32(falcon, 0x11c, 0);
-               nv_wo32(falcon, 0x110, falcon->core->addr >> 8);
-               nv_wo32(falcon, 0x114, 0);
-               nv_wo32(falcon, 0x118, 0x00006610);
-       } else {
-               if (falcon->code.size > falcon->code.limit ||
-                   falcon->data.size > falcon->data.limit) {
-                       nv_error(falcon, "ucode exceeds falcon limit(s)\n");
-                       return -EINVAL;
-               }
-
-               if (falcon->version < 3) {
-                       nv_wo32(falcon, 0xff8, 0x00100000);
-                       for (i = 0; i < falcon->code.size / 4; i++)
-                               nv_wo32(falcon, 0xff4, falcon->code.data[i]);
-               } else {
-                       nv_wo32(falcon, 0x180, 0x01000000);
-                       for (i = 0; i < falcon->code.size / 4; i++) {
-                               if ((i & 0x3f) == 0)
-                                       nv_wo32(falcon, 0x188, i >> 6);
-                               nv_wo32(falcon, 0x184, falcon->code.data[i]);
-                       }
-               }
-       }
-
-       /* upload data segment (if necessary), zeroing the remainder */
-       if (falcon->version < 3) {
-               nv_wo32(falcon, 0xff8, 0x00000000);
-               for (i = 0; !falcon->core && i < falcon->data.size / 4; i++)
-                       nv_wo32(falcon, 0xff4, falcon->data.data[i]);
-               for (; i < falcon->data.limit; i += 4)
-                       nv_wo32(falcon, 0xff4, 0x00000000);
-       } else {
-               nv_wo32(falcon, 0x1c0, 0x01000000);
-               for (i = 0; !falcon->core && i < falcon->data.size / 4; i++)
-                       nv_wo32(falcon, 0x1c4, falcon->data.data[i]);
-               for (; i < falcon->data.limit / 4; i++)
-                       nv_wo32(falcon, 0x1c4, 0x00000000);
-       }
-
-       /* start it running */
-       nv_wo32(falcon, 0x10c, 0x00000001); /* BLOCK_ON_FIFO */
-       nv_wo32(falcon, 0x104, 0x00000000); /* ENTRY */
-       nv_wo32(falcon, 0x100, 0x00000002); /* TRIGGER */
-       nv_wo32(falcon, 0x048, 0x00000003); /* FIFO | CHSW */
-       return 0;
-}
-
-int
-_nouveau_falcon_fini(struct nouveau_object *object, bool suspend)
-{
-       struct nouveau_falcon *falcon = (void *)object;
-
-       if (!suspend) {
-               nouveau_gpuobj_ref(NULL, &falcon->core);
-               if (falcon->external) {
-                       kfree(falcon->data.data);
-                       kfree(falcon->code.data);
-                       falcon->code.data = NULL;
-               }
-       }
-
-       nv_mo32(falcon, 0x048, 0x00000003, 0x00000000);
-       nv_wo32(falcon, 0x014, 0xffffffff);
-
-       return nouveau_engine_fini(&falcon->base, suspend);
-}
-
-int
-nouveau_falcon_create_(struct nouveau_object *parent,
-                      struct nouveau_object *engine,
-                      struct nouveau_oclass *oclass, u32 addr, bool enable,
-                      const char *iname, const char *fname,
-                      int length, void **pobject)
-{
-       struct nouveau_falcon *falcon;
-       int ret;
-
-       ret = nouveau_engine_create_(parent, engine, oclass, enable, iname,
-                                    fname, length, pobject);
-       falcon = *pobject;
-       if (ret)
-               return ret;
-
-       falcon->addr = addr;
-       return 0;
-}
index 0261a11..d829172 100644 (file)
@@ -208,7 +208,6 @@ nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
        struct nouveau_mm_node *node;
 
        if (block) {
-               mutex_init(&mm->mutex);
                INIT_LIST_HEAD(&mm->nodes);
                INIT_LIST_HEAD(&mm->free);
                mm->block_size = block;
index 1d9f614..1e8e75c 100644 (file)
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  * OTHER DEALINGS IN THE SOFTWARE.
  *
- * Authors: Ben Skeggs
+ * Authors: Ben Skeggs, Ilia Mirkin
  */
 
-#include <core/engctx.h>
-#include <core/class.h>
-
+#include <engine/xtensa.h>
 #include <engine/bsp.h>
 
-struct nv84_bsp_priv {
-       struct nouveau_engine base;
-};
-
 /*******************************************************************************
  * BSP object classes
  ******************************************************************************/
 
 static struct nouveau_oclass
 nv84_bsp_sclass[] = {
+       { 0x74b0, &nouveau_object_ofuncs },
        {},
 };
 
@@ -48,7 +43,7 @@ static struct nouveau_oclass
 nv84_bsp_cclass = {
        .handle = NV_ENGCTX(BSP, 0x84),
        .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = _nouveau_engctx_ctor,
+               .ctor = _nouveau_xtensa_engctx_ctor,
                .dtor = _nouveau_engctx_dtor,
                .init = _nouveau_engctx_init,
                .fini = _nouveau_engctx_fini,
@@ -66,10 +61,10 @@ nv84_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
              struct nouveau_oclass *oclass, void *data, u32 size,
              struct nouveau_object **pobject)
 {
-       struct nv84_bsp_priv *priv;
+       struct nouveau_xtensa *priv;
        int ret;
 
-       ret = nouveau_engine_create(parent, engine, oclass, true,
+       ret = nouveau_xtensa_create(parent, engine, oclass, 0x103000, true,
                                    "PBSP", "bsp", &priv);
        *pobject = nv_object(priv);
        if (ret)
@@ -78,6 +73,8 @@ nv84_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_subdev(priv)->unit = 0x04008000;
        nv_engine(priv)->cclass = &nv84_bsp_cclass;
        nv_engine(priv)->sclass = nv84_bsp_sclass;
+       priv->fifo_val = 0x1111;
+       priv->unkd28 = 0x90044;
        return 0;
 }
 
@@ -86,8 +83,10 @@ nv84_bsp_oclass = {
        .handle = NV_ENGINE(BSP, 0x84),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv84_bsp_ctor,
-               .dtor = _nouveau_engine_dtor,
-               .init = _nouveau_engine_init,
-               .fini = _nouveau_engine_fini,
+               .dtor = _nouveau_xtensa_dtor,
+               .init = _nouveau_xtensa_init,
+               .fini = _nouveau_xtensa_fini,
+               .rd32 = _nouveau_xtensa_rd32,
+               .wr32 = _nouveau_xtensa_wr32,
        },
 };
diff --git a/drivers/gpu/drm/nouveau/core/engine/bsp/nv98.c b/drivers/gpu/drm/nouveau/core/engine/bsp/nv98.c
new file mode 100644 (file)
index 0000000..8bf92b0
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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/engctx.h>
+#include <core/class.h>
+
+#include <engine/bsp.h>
+
+struct nv98_bsp_priv {
+       struct nouveau_engine base;
+};
+
+/*******************************************************************************
+ * BSP object classes
+ ******************************************************************************/
+
+static struct nouveau_oclass
+nv98_bsp_sclass[] = {
+       {},
+};
+
+/*******************************************************************************
+ * BSP context
+ ******************************************************************************/
+
+static struct nouveau_oclass
+nv98_bsp_cclass = {
+       .handle = NV_ENGCTX(BSP, 0x98),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_engctx_ctor,
+               .dtor = _nouveau_engctx_dtor,
+               .init = _nouveau_engctx_init,
+               .fini = _nouveau_engctx_fini,
+               .rd32 = _nouveau_engctx_rd32,
+               .wr32 = _nouveau_engctx_wr32,
+       },
+};
+
+/*******************************************************************************
+ * BSP engine/subdev functions
+ ******************************************************************************/
+
+static int
+nv98_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 size,
+             struct nouveau_object **pobject)
+{
+       struct nv98_bsp_priv *priv;
+       int ret;
+
+       ret = nouveau_engine_create(parent, engine, oclass, true,
+                                   "PBSP", "bsp", &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       nv_subdev(priv)->unit = 0x04008000;
+       nv_engine(priv)->cclass = &nv98_bsp_cclass;
+       nv_engine(priv)->sclass = nv98_bsp_sclass;
+       return 0;
+}
+
+struct nouveau_oclass
+nv98_bsp_oclass = {
+       .handle = NV_ENGINE(BSP, 0x98),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv98_bsp_ctor,
+               .dtor = _nouveau_engine_dtor,
+               .init = _nouveau_engine_init,
+               .fini = _nouveau_engine_fini,
+       },
+};
index 0a5aa6b..262c9f5 100644 (file)
@@ -22,8 +22,7 @@
  * Authors: Maarten Lankhorst
  */
 
-#include <core/falcon.h>
-
+#include <engine/falcon.h>
 #include <engine/bsp.h>
 
 struct nvc0_bsp_priv {
index d4f23bb..c46882c 100644 (file)
@@ -22,8 +22,7 @@
  * Authors: Ben Skeggs
  */
 
-#include <core/falcon.h>
-
+#include <engine/falcon.h>
 #include <engine/bsp.h>
 
 struct nve0_bsp_priv {
index c92520f..241b272 100644 (file)
@@ -1,4 +1,4 @@
-static u32 nva3_pcopy_data[] = {
+uint32_t nva3_pcopy_data[] = {
 /* 0x0000: ctx_object */
        0x00000000,
 /* 0x0004: ctx_dma */
@@ -183,7 +183,7 @@ static u32 nva3_pcopy_data[] = {
        0x00000800,
 };
 
-static u32 nva3_pcopy_code[] = {
+uint32_t nva3_pcopy_code[] = {
 /* 0x0000: main */
        0x04fe04bd,
        0x3517f000,
index 0d98c6c..98cc421 100644 (file)
@@ -1,4 +1,4 @@
-static u32 nvc0_pcopy_data[] = {
+uint32_t nvc0_pcopy_data[] = {
 /* 0x0000: ctx_object */
        0x00000000,
 /* 0x0004: ctx_query_address_high */
@@ -171,7 +171,7 @@ static u32 nvc0_pcopy_data[] = {
        0x00000800,
 };
 
-static u32 nvc0_pcopy_code[] = {
+uint32_t nvc0_pcopy_code[] = {
 /* 0x0000: main */
        0x04fe04bd,
        0x3517f000,
index d6dc2a6..f315277 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <core/client.h>
-#include <core/falcon.h>
-#include <core/class.h>
-#include <core/enum.h>
+#include <engine/falcon.h>
+#include <engine/fifo.h>
+#include <engine/copy.h>
 
 #include <subdev/fb.h>
 #include <subdev/vm.h>
 
-#include <engine/fifo.h>
-#include <engine/copy.h>
+#include <core/client.h>
+#include <core/class.h>
+#include <core/enum.h>
+
 
 #include "fuc/nva3.fuc.h"
 
@@ -116,13 +117,6 @@ nva3_copy_intr(struct nouveau_subdev *subdev)
        nouveau_engctx_put(engctx);
 }
 
-static int
-nva3_copy_tlb_flush(struct nouveau_engine *engine)
-{
-       nv50_vm_flush_engine(&engine->base, 0x0d);
-       return 0;
-}
-
 static int
 nva3_copy_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_oclass *oclass, void *data, u32 size,
@@ -142,7 +136,6 @@ nva3_copy_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_subdev(priv)->intr = nva3_copy_intr;
        nv_engine(priv)->cclass = &nva3_copy_cclass;
        nv_engine(priv)->sclass = nva3_copy_sclass;
-       nv_engine(priv)->tlb_flush = nva3_copy_tlb_flush;
        nv_falcon(priv)->code.data = nva3_pcopy_code;
        nv_falcon(priv)->code.size = sizeof(nva3_pcopy_code);
        nv_falcon(priv)->data.data = nva3_pcopy_data;
index b3ed273..993df09 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <core/falcon.h>
-#include <core/class.h>
-#include <core/enum.h>
-
+#include <engine/falcon.h>
 #include <engine/fifo.h>
 #include <engine/copy.h>
 
+#include <core/class.h>
+#include <core/enum.h>
+#include <core/class.h>
+#include <core/enum.h>
+
 #include "fuc/nvc0.fuc.h"
 
 struct nvc0_copy_priv {
index dbbe9e8..30f1ef1 100644 (file)
@@ -67,6 +67,19 @@ nve0_copy_cclass = {
  * PCOPY engine/subdev functions
  ******************************************************************************/
 
+static void
+nve0_copy_intr(struct nouveau_subdev *subdev)
+{
+       const int ce = nv_subidx(nv_object(subdev)) - NVDEV_ENGINE_COPY0;
+       struct nve0_copy_priv *priv = (void *)subdev;
+       u32 stat = nv_rd32(priv, 0x104908 + (ce * 0x1000));
+
+       if (stat) {
+               nv_warn(priv, "unhandled intr 0x%08x\n", stat);
+               nv_wr32(priv, 0x104908 + (ce * 0x1000), stat);
+       }
+}
+
 static int
 nve0_copy0_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_oclass *oclass, void *data, u32 size,
@@ -85,6 +98,7 @@ nve0_copy0_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        nv_subdev(priv)->unit = 0x00000040;
+       nv_subdev(priv)->intr = nve0_copy_intr;
        nv_engine(priv)->cclass = &nve0_copy_cclass;
        nv_engine(priv)->sclass = nve0_copy_sclass;
        return 0;
@@ -108,6 +122,28 @@ nve0_copy1_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        nv_subdev(priv)->unit = 0x00000080;
+       nv_subdev(priv)->intr = nve0_copy_intr;
+       nv_engine(priv)->cclass = &nve0_copy_cclass;
+       nv_engine(priv)->sclass = nve0_copy_sclass;
+       return 0;
+}
+
+static int
+nve0_copy2_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+               struct nouveau_oclass *oclass, void *data, u32 size,
+               struct nouveau_object **pobject)
+{
+       struct nve0_copy_priv *priv;
+       int ret;
+
+       ret = nouveau_engine_create(parent, engine, oclass, true,
+                                   "PCE2", "copy2", &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       nv_subdev(priv)->unit = 0x00200000;
+       nv_subdev(priv)->intr = nve0_copy_intr;
        nv_engine(priv)->cclass = &nve0_copy_cclass;
        nv_engine(priv)->sclass = nve0_copy_sclass;
        return 0;
@@ -134,3 +170,14 @@ nve0_copy1_oclass = {
                .fini = _nouveau_engine_fini,
        },
 };
+
+struct nouveau_oclass
+nve0_copy2_oclass = {
+       .handle = NV_ENGINE(COPY2, 0xe0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nve0_copy2_ctor,
+               .dtor = _nouveau_engine_dtor,
+               .init = _nouveau_engine_init,
+               .fini = _nouveau_engine_fini,
+       },
+};
index 09962e4..38676c7 100644 (file)
@@ -1,4 +1,4 @@
-static uint32_t nv98_pcrypt_data[] = {
+uint32_t nv98_pcrypt_data[] = {
 /* 0x0000: ctx_dma */
 /* 0x0000: ctx_dma_query */
        0x00000000,
@@ -150,7 +150,7 @@ static uint32_t nv98_pcrypt_data[] = {
        0x00000000,
 };
 
-static uint32_t nv98_pcrypt_code[] = {
+uint32_t nv98_pcrypt_code[] = {
        0x17f004bd,
        0x0010fe35,
        0xf10004fe,
index 5bc021f..2551daf 100644 (file)
@@ -140,13 +140,6 @@ nv84_crypt_intr(struct nouveau_subdev *subdev)
        nouveau_engctx_put(engctx);
 }
 
-static int
-nv84_crypt_tlb_flush(struct nouveau_engine *engine)
-{
-       nv50_vm_flush_engine(&engine->base, 0x0a);
-       return 0;
-}
-
 static int
 nv84_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_oclass *oclass, void *data, u32 size,
@@ -165,7 +158,6 @@ nv84_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_subdev(priv)->intr = nv84_crypt_intr;
        nv_engine(priv)->cclass = &nv84_crypt_cclass;
        nv_engine(priv)->sclass = nv84_crypt_sclass;
-       nv_engine(priv)->tlb_flush = nv84_crypt_tlb_flush;
        return 0;
 }
 
index 8bf8955..c708237 100644 (file)
 #include <core/enum.h>
 #include <core/class.h>
 #include <core/engctx.h>
-#include <core/falcon.h>
 
 #include <subdev/timer.h>
 #include <subdev/fb.h>
 
+#include <engine/falcon.h>
 #include <engine/fifo.h>
 #include <engine/crypt.h>
 
@@ -118,13 +118,6 @@ nv98_crypt_intr(struct nouveau_subdev *subdev)
        nouveau_engctx_put(engctx);
 }
 
-static int
-nv98_crypt_tlb_flush(struct nouveau_engine *engine)
-{
-       nv50_vm_flush_engine(&engine->base, 0x0a);
-       return 0;
-}
-
 static int
 nv98_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_oclass *oclass, void *data, u32 size,
@@ -143,7 +136,6 @@ nv98_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_subdev(priv)->intr = nv98_crypt_intr;
        nv_engine(priv)->cclass = &nv98_crypt_cclass;
        nv_engine(priv)->sclass = nv98_crypt_sclass;
-       nv_engine(priv)->tlb_flush = nv98_crypt_tlb_flush;
        nv_falcon(priv)->code.data = nv98_pcrypt_code;
        nv_falcon(priv)->code.size = sizeof(nv98_pcrypt_code);
        nv_falcon(priv)->data.data = nv98_pcrypt_data;
index 5e8c3de..ffc18b8 100644 (file)
@@ -227,9 +227,9 @@ nv50_identify(struct nouveau_device *device)
                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     ] = &nv84_vp_oclass;
+               device->oclass[NVDEV_ENGINE_VP     ] = &nv98_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv98_crypt_oclass;
-               device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_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;
                break;
@@ -279,9 +279,9 @@ nv50_identify(struct nouveau_device *device)
                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     ] = &nv84_vp_oclass;
+               device->oclass[NVDEV_ENGINE_VP     ] = &nv98_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv98_crypt_oclass;
-               device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_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;
                break;
@@ -305,9 +305,9 @@ nv50_identify(struct nouveau_device *device)
                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     ] = &nv84_vp_oclass;
+               device->oclass[NVDEV_ENGINE_VP     ] = &nv98_vp_oclass;
                device->oclass[NVDEV_ENGINE_CRYPT  ] = &nv98_crypt_oclass;
-               device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_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;
                break;
@@ -319,7 +319,7 @@ nv50_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -332,8 +332,8 @@ nv50_identify(struct nouveau_device *device)
                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_BSP    ] = &nv84_bsp_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;
@@ -346,7 +346,7 @@ nv50_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -358,8 +358,8 @@ nv50_identify(struct nouveau_device *device)
                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     ] = &nv84_vp_oclass;
-               device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_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;
@@ -372,7 +372,7 @@ nv50_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -384,8 +384,8 @@ nv50_identify(struct nouveau_device *device)
                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     ] = &nv84_vp_oclass;
-               device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_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;
@@ -398,7 +398,7 @@ nv50_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -410,8 +410,8 @@ nv50_identify(struct nouveau_device *device)
                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     ] = &nv84_vp_oclass;
-               device->oclass[NVDEV_ENGINE_BSP    ] = &nv84_bsp_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;
index a36e64e..418f51f 100644 (file)
@@ -62,7 +62,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -75,7 +75,7 @@ nvc0_identify(struct nouveau_device *device)
                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_GR     ] = &nvc0_graph_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;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
@@ -91,7 +91,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -104,7 +104,7 @@ nvc0_identify(struct nouveau_device *device)
                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_GR     ] = &nvc0_graph_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;
@@ -120,7 +120,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -133,7 +133,7 @@ nvc0_identify(struct nouveau_device *device)
                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_GR     ] = &nvc0_graph_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;
@@ -148,7 +148,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -161,7 +161,7 @@ nvc0_identify(struct nouveau_device *device)
                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_GR     ] = &nvc0_graph_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;
@@ -177,7 +177,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -190,7 +190,7 @@ nvc0_identify(struct nouveau_device *device)
                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_GR     ] = &nvc0_graph_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;
@@ -206,7 +206,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -219,7 +219,7 @@ nvc0_identify(struct nouveau_device *device)
                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_GR     ] = &nvc0_graph_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;
@@ -234,7 +234,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -247,7 +247,7 @@ nvc0_identify(struct nouveau_device *device)
                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_GR     ] = &nvc0_graph_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;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
@@ -263,7 +263,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -276,7 +276,7 @@ nvc0_identify(struct nouveau_device *device)
                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_GR     ] = &nvc0_graph_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;
@@ -291,7 +291,7 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -304,7 +304,7 @@ nvc0_identify(struct nouveau_device *device)
                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_GR     ] = &nvc0_graph_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;
index a354e40..7aca187 100644 (file)
@@ -62,7 +62,7 @@ nve0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -75,10 +75,11 @@ nve0_identify(struct nouveau_device *device)
                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_GR     ] = &nve0_graph_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;
                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;
@@ -91,7 +92,7 @@ nve0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -104,10 +105,11 @@ nve0_identify(struct nouveau_device *device)
                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_GR     ] = &nve0_graph_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;
                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;
@@ -120,7 +122,7 @@ nve0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -133,10 +135,11 @@ nve0_identify(struct nouveau_device *device)
                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_GR     ] = &nve0_graph_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;
                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;
@@ -149,7 +152,7 @@ nve0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
-               device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_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_TIMER  ] = &nv04_timer_oclass;
@@ -160,16 +163,14 @@ nve0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
                device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_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     ] = &nve0_graph_oclass;
-#endif
+               device->oclass[NVDEV_ENGINE_GR     ] =  nvf0_graph_oclass;
                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;
+#if 0
                device->oclass[NVDEV_ENGINE_BSP    ] = &nve0_bsp_oclass;
                device->oclass[NVDEV_ENGINE_VP     ] = &nve0_vp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
index f065fc2..db8c6fd 100644 (file)
@@ -55,6 +55,10 @@ nva3_hdmi_ctrl(struct nv50_disp_priv *priv, int head, int or, u32 data)
        nv_wr32(priv, 0x61c510 + soff, 0x00000000);
        nv_mask(priv, 0x61c500 + soff, 0x00000001, 0x00000001);
 
+       nv_mask(priv, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */
+       nv_mask(priv, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */
+       nv_mask(priv, 0x61c578 + soff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */
+
        /* ??? */
        nv_mask(priv, 0x61733c, 0x00100000, 0x00100000); /* RESETF */
        nv_mask(priv, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */
index 6a38402..7ffe2f3 100644 (file)
@@ -34,9 +34,9 @@
 #include <subdev/bios/disp.h>
 #include <subdev/bios/init.h>
 #include <subdev/bios/pll.h>
+#include <subdev/devinit.h>
 #include <subdev/timer.h>
 #include <subdev/fb.h>
-#include <subdev/clock.h>
 
 #include "nv50.h"
 
@@ -987,10 +987,10 @@ nv50_disp_intr_unk20_0(struct nv50_disp_priv *priv, int head)
 static void
 nv50_disp_intr_unk20_1(struct nv50_disp_priv *priv, int head)
 {
-       struct nouveau_clock *clk = nouveau_clock(priv);
+       struct nouveau_devinit *devinit = nouveau_devinit(priv);
        u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
        if (pclk)
-               clk->pll_set(clk, PLL_VPLL0 + head, pclk);
+               devinit->pll_set(devinit, PLL_VPLL0 + head, pclk);
 }
 
 static void
@@ -1107,6 +1107,7 @@ nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head)
        u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
        u32 hval, hreg = 0x614200 + (head * 0x800);
        u32 oval, oreg;
+       u32 mask;
        u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
        if (conf != ~0) {
                if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) {
@@ -1133,6 +1134,7 @@ nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head)
                        oreg = 0x614280 + (ffs(outp.or) - 1) * 0x800;
                        oval = 0x00000000;
                        hval = 0x00000000;
+                       mask = 0xffffffff;
                } else
                if (!outp.location) {
                        if (outp.type == DCB_OUTPUT_DP)
@@ -1140,14 +1142,16 @@ nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head)
                        oreg = 0x614300 + (ffs(outp.or) - 1) * 0x800;
                        oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
                        hval = 0x00000000;
+                       mask = 0x00000707;
                } else {
                        oreg = 0x614380 + (ffs(outp.or) - 1) * 0x800;
                        oval = 0x00000001;
                        hval = 0x00000001;
+                       mask = 0x00000707;
                }
 
                nv_mask(priv, hreg, 0x0000000f, hval);
-               nv_mask(priv, oreg, 0x00000707, oval);
+               nv_mask(priv, oreg, mask, oval);
        }
 }
 
index 019eacd..52dd7a1 100644 (file)
 
 #include <engine/disp.h>
 
-#include <subdev/timer.h>
-#include <subdev/fb.h>
-#include <subdev/clock.h>
-
 #include <subdev/bios.h>
 #include <subdev/bios/dcb.h>
 #include <subdev/bios/disp.h>
 #include <subdev/bios/init.h>
 #include <subdev/bios/pll.h>
+#include <subdev/devinit.h>
+#include <subdev/fb.h>
+#include <subdev/timer.h>
 
 #include "nv50.h"
 
@@ -738,10 +737,10 @@ nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head)
 static void
 nvd0_disp_intr_unk2_1(struct nv50_disp_priv *priv, int head)
 {
-       struct nouveau_clock *clk = nouveau_clock(priv);
+       struct nouveau_devinit *devinit = nouveau_devinit(priv);
        u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
        if (pclk)
-               clk->pll_set(clk, PLL_VPLL0 + head, pclk);
+               devinit->pll_set(devinit, PLL_VPLL0 + head, pclk);
        nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000);
 }
 
@@ -959,6 +958,9 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        int heads = nv_rd32(parent, 0x022448);
        int ret;
 
+       if (nv_rd32(parent, 0x022500) & 0x00000001)
+               return -ENODEV;
+
        ret = nouveau_disp_create(parent, engine, oclass, heads,
                                  "PDISP", "display", &priv);
        *pobject = nv_object(priv);
index 20725b3..fb1fe6a 100644 (file)
@@ -54,6 +54,9 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        int heads = nv_rd32(parent, 0x022448);
        int ret;
 
+       if (nv_rd32(parent, 0x022500) & 0x00000001)
+               return -ENODEV;
+
        ret = nouveau_disp_create(parent, engine, oclass, heads,
                                  "PDISP", "display", &priv);
        *pobject = nv_object(priv);
index a488c36..42aa6b9 100644 (file)
@@ -54,6 +54,9 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        int heads = nv_rd32(parent, 0x022448);
        int ret;
 
+       if (nv_rd32(parent, 0x022500) & 0x00000001)
+               return -ENODEV;
+
        ret = nouveau_disp_create(parent, engine, oclass, heads,
                                  "PDISP", "display", &priv);
        *pobject = nv_object(priv);
diff --git a/drivers/gpu/drm/nouveau/core/engine/falcon.c b/drivers/gpu/drm/nouveau/core/engine/falcon.c
new file mode 100644 (file)
index 0000000..3c7a31f
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+#include <engine/falcon.h>
+#include <subdev/timer.h>
+
+u32
+_nouveau_falcon_rd32(struct nouveau_object *object, u64 addr)
+{
+       struct nouveau_falcon *falcon = (void *)object;
+       return nv_rd32(falcon, falcon->addr + addr);
+}
+
+void
+_nouveau_falcon_wr32(struct nouveau_object *object, u64 addr, u32 data)
+{
+       struct nouveau_falcon *falcon = (void *)object;
+       nv_wr32(falcon, falcon->addr + addr, data);
+}
+
+int
+_nouveau_falcon_init(struct nouveau_object *object)
+{
+       struct nouveau_device *device = nv_device(object);
+       struct nouveau_falcon *falcon = (void *)object;
+       const struct firmware *fw;
+       char name[32] = "internal";
+       int ret, i;
+       u32 caps;
+
+       /* enable engine, and determine its capabilities */
+       ret = nouveau_engine_init(&falcon->base);
+       if (ret)
+               return ret;
+
+       if (device->chipset <  0xa3 ||
+           device->chipset == 0xaa || device->chipset == 0xac) {
+               falcon->version = 0;
+               falcon->secret  = (falcon->addr == 0x087000) ? 1 : 0;
+       } else {
+               caps = nv_ro32(falcon, 0x12c);
+               falcon->version = (caps & 0x0000000f);
+               falcon->secret  = (caps & 0x00000030) >> 4;
+       }
+
+       caps = nv_ro32(falcon, 0x108);
+       falcon->code.limit = (caps & 0x000001ff) << 8;
+       falcon->data.limit = (caps & 0x0003fe00) >> 1;
+
+       nv_debug(falcon, "falcon version: %d\n", falcon->version);
+       nv_debug(falcon, "secret level: %d\n", falcon->secret);
+       nv_debug(falcon, "code limit: %d\n", falcon->code.limit);
+       nv_debug(falcon, "data limit: %d\n", falcon->data.limit);
+
+       /* wait for 'uc halted' to be signalled before continuing */
+       if (falcon->secret && falcon->version < 4) {
+               if (!falcon->version)
+                       nv_wait(falcon, 0x008, 0x00000010, 0x00000010);
+               else
+                       nv_wait(falcon, 0x180, 0x80000000, 0);
+               nv_wo32(falcon, 0x004, 0x00000010);
+       }
+
+       /* disable all interrupts */
+       nv_wo32(falcon, 0x014, 0xffffffff);
+
+       /* no default ucode provided by the engine implementation, try and
+        * locate a "self-bootstrapping" firmware image for the engine
+        */
+       if (!falcon->code.data) {
+               snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03x",
+                        device->chipset, falcon->addr >> 12);
+
+               ret = request_firmware(&fw, name, &device->pdev->dev);
+               if (ret == 0) {
+                       falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL);
+                       falcon->code.size = fw->size;
+                       falcon->data.data = NULL;
+                       falcon->data.size = 0;
+                       release_firmware(fw);
+               }
+
+               falcon->external = true;
+       }
+
+       /* next step is to try and load "static code/data segment" firmware
+        * images for the engine
+        */
+       if (!falcon->code.data) {
+               snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xd",
+                        device->chipset, falcon->addr >> 12);
+
+               ret = request_firmware(&fw, name, &device->pdev->dev);
+               if (ret) {
+                       nv_error(falcon, "unable to load firmware data\n");
+                       return ret;
+               }
+
+               falcon->data.data = kmemdup(fw->data, fw->size, GFP_KERNEL);
+               falcon->data.size = fw->size;
+               release_firmware(fw);
+               if (!falcon->data.data)
+                       return -ENOMEM;
+
+               snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xc",
+                        device->chipset, falcon->addr >> 12);
+
+               ret = request_firmware(&fw, name, &device->pdev->dev);
+               if (ret) {
+                       nv_error(falcon, "unable to load firmware code\n");
+                       return ret;
+               }
+
+               falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL);
+               falcon->code.size = fw->size;
+               release_firmware(fw);
+               if (!falcon->code.data)
+                       return -ENOMEM;
+       }
+
+       nv_debug(falcon, "firmware: %s (%s)\n", name, falcon->data.data ?
+                "static code/data segments" : "self-bootstrapping");
+
+       /* ensure any "self-bootstrapping" firmware image is in vram */
+       if (!falcon->data.data && !falcon->core) {
+               ret = nouveau_gpuobj_new(object->parent, NULL,
+                                        falcon->code.size, 256, 0,
+                                       &falcon->core);
+               if (ret) {
+                       nv_error(falcon, "core allocation failed, %d\n", ret);
+                       return ret;
+               }
+
+               for (i = 0; i < falcon->code.size; i += 4)
+                       nv_wo32(falcon->core, i, falcon->code.data[i / 4]);
+       }
+
+       /* upload firmware bootloader (or the full code segments) */
+       if (falcon->core) {
+               if (device->card_type < NV_C0)
+                       nv_wo32(falcon, 0x618, 0x04000000);
+               else
+                       nv_wo32(falcon, 0x618, 0x00000114);
+               nv_wo32(falcon, 0x11c, 0);
+               nv_wo32(falcon, 0x110, falcon->core->addr >> 8);
+               nv_wo32(falcon, 0x114, 0);
+               nv_wo32(falcon, 0x118, 0x00006610);
+       } else {
+               if (falcon->code.size > falcon->code.limit ||
+                   falcon->data.size > falcon->data.limit) {
+                       nv_error(falcon, "ucode exceeds falcon limit(s)\n");
+                       return -EINVAL;
+               }
+
+               if (falcon->version < 3) {
+                       nv_wo32(falcon, 0xff8, 0x00100000);
+                       for (i = 0; i < falcon->code.size / 4; i++)
+                               nv_wo32(falcon, 0xff4, falcon->code.data[i]);
+               } else {
+                       nv_wo32(falcon, 0x180, 0x01000000);
+                       for (i = 0; i < falcon->code.size / 4; i++) {
+                               if ((i & 0x3f) == 0)
+                                       nv_wo32(falcon, 0x188, i >> 6);
+                               nv_wo32(falcon, 0x184, falcon->code.data[i]);
+                       }
+               }
+       }
+
+       /* upload data segment (if necessary), zeroing the remainder */
+       if (falcon->version < 3) {
+               nv_wo32(falcon, 0xff8, 0x00000000);
+               for (i = 0; !falcon->core && i < falcon->data.size / 4; i++)
+                       nv_wo32(falcon, 0xff4, falcon->data.data[i]);
+               for (; i < falcon->data.limit; i += 4)
+                       nv_wo32(falcon, 0xff4, 0x00000000);
+       } else {
+               nv_wo32(falcon, 0x1c0, 0x01000000);
+               for (i = 0; !falcon->core && i < falcon->data.size / 4; i++)
+                       nv_wo32(falcon, 0x1c4, falcon->data.data[i]);
+               for (; i < falcon->data.limit / 4; i++)
+                       nv_wo32(falcon, 0x1c4, 0x00000000);
+       }
+
+       /* start it running */
+       nv_wo32(falcon, 0x10c, 0x00000001); /* BLOCK_ON_FIFO */
+       nv_wo32(falcon, 0x104, 0x00000000); /* ENTRY */
+       nv_wo32(falcon, 0x100, 0x00000002); /* TRIGGER */
+       nv_wo32(falcon, 0x048, 0x00000003); /* FIFO | CHSW */
+       return 0;
+}
+
+int
+_nouveau_falcon_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nouveau_falcon *falcon = (void *)object;
+
+       if (!suspend) {
+               nouveau_gpuobj_ref(NULL, &falcon->core);
+               if (falcon->external) {
+                       kfree(falcon->data.data);
+                       kfree(falcon->code.data);
+                       falcon->code.data = NULL;
+               }
+       }
+
+       nv_mo32(falcon, 0x048, 0x00000003, 0x00000000);
+       nv_wo32(falcon, 0x014, 0xffffffff);
+
+       return nouveau_engine_fini(&falcon->base, suspend);
+}
+
+int
+nouveau_falcon_create_(struct nouveau_object *parent,
+                      struct nouveau_object *engine,
+                      struct nouveau_oclass *oclass, u32 addr, bool enable,
+                      const char *iname, const char *fname,
+                      int length, void **pobject)
+{
+       struct nouveau_falcon *falcon;
+       int ret;
+
+       ret = nouveau_engine_create_(parent, engine, oclass, enable, iname,
+                                    fname, length, pobject);
+       falcon = *pobject;
+       if (ret)
+               return ret;
+
+       falcon->addr = addr;
+       return 0;
+}
index 2b1f917..5c7433d 100644 (file)
@@ -320,7 +320,7 @@ nv40_fifo_init(struct nouveau_object *object)
                break;
        default:
                nv_wr32(priv, 0x002230, 0x00000000);
-               nv_wr32(priv, 0x002220, ((pfb->ram.size - 512 * 1024 +
+               nv_wr32(priv, 0x002220, ((pfb->ram->size - 512 * 1024 +
                                         priv->ramfc->addr) >> 16) |
                                        0x00030000);
                break;
index 35b94bd..7f53196 100644 (file)
@@ -56,7 +56,9 @@ nv84_fifo_context_attach(struct nouveau_object *parent,
        switch (nv_engidx(object->engine)) {
        case NVDEV_ENGINE_SW   : return 0;
        case NVDEV_ENGINE_GR   : addr = 0x0020; break;
+       case NVDEV_ENGINE_VP   : addr = 0x0040; break;
        case NVDEV_ENGINE_MPEG : addr = 0x0060; break;
+       case NVDEV_ENGINE_BSP  : addr = 0x0080; break;
        case NVDEV_ENGINE_CRYPT: addr = 0x00a0; break;
        case NVDEV_ENGINE_COPY0: addr = 0x00c0; break;
        default:
@@ -89,7 +91,9 @@ nv84_fifo_context_detach(struct nouveau_object *parent, bool suspend,
        switch (nv_engidx(object->engine)) {
        case NVDEV_ENGINE_SW   : return 0;
        case NVDEV_ENGINE_GR   : engn = 0; addr = 0x0020; break;
+       case NVDEV_ENGINE_VP   : engn = 3; addr = 0x0040; break;
        case NVDEV_ENGINE_MPEG : engn = 1; addr = 0x0060; break;
+       case NVDEV_ENGINE_BSP  : engn = 5; addr = 0x0080; break;
        case NVDEV_ENGINE_CRYPT: engn = 4; addr = 0x00a0; break;
        case NVDEV_ENGINE_COPY0: engn = 2; addr = 0x00c0; break;
        default:
index 56192a7..09644fa 100644 (file)
@@ -44,7 +44,8 @@ static const struct {
        u64 subdev;
        u64 mask;
 } fifo_engine[] = {
-       _(NVDEV_ENGINE_GR      , (1ULL << NVDEV_ENGINE_SW)),
+       _(NVDEV_ENGINE_GR      , (1ULL << NVDEV_ENGINE_SW) |
+                                (1ULL << NVDEV_ENGINE_COPY2)),
        _(NVDEV_ENGINE_VP      , 0),
        _(NVDEV_ENGINE_PPP     , 0),
        _(NVDEV_ENGINE_BSP     , 0),
@@ -96,18 +97,6 @@ nve0_fifo_playlist_update(struct nve0_fifo_priv *priv, u32 engine)
 
        mutex_lock(&nv_subdev(priv)->mutex);
        cur = engn->playlist[engn->cur_playlist];
-       if (unlikely(cur == NULL)) {
-               int ret = nouveau_gpuobj_new(nv_object(priv), NULL,
-                                            0x8000, 0x1000, 0, &cur);
-               if (ret) {
-                       mutex_unlock(&nv_subdev(priv)->mutex);
-                       nv_error(priv, "playlist alloc failed\n");
-                       return;
-               }
-
-               engn->playlist[engn->cur_playlist] = cur;
-       }
-
        engn->cur_playlist = !engn->cur_playlist;
 
        for (i = 0, p = 0; i < priv->base.max; i++) {
@@ -138,10 +127,12 @@ nve0_fifo_context_attach(struct nouveau_object *parent,
        int ret;
 
        switch (nv_engidx(object->engine)) {
-       case NVDEV_ENGINE_SW   : return 0;
-       case NVDEV_ENGINE_GR   :
+       case NVDEV_ENGINE_SW   :
        case NVDEV_ENGINE_COPY0:
-       case NVDEV_ENGINE_COPY1: addr = 0x0210; break;
+       case NVDEV_ENGINE_COPY1:
+       case NVDEV_ENGINE_COPY2:
+               return 0;
+       case NVDEV_ENGINE_GR   : addr = 0x0210; break;
        case NVDEV_ENGINE_BSP  : addr = 0x0270; break;
        case NVDEV_ENGINE_VP   : addr = 0x0250; break;
        case NVDEV_ENGINE_PPP  : addr = 0x0260; break;
@@ -176,9 +167,10 @@ nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend,
 
        switch (nv_engidx(object->engine)) {
        case NVDEV_ENGINE_SW   : return 0;
-       case NVDEV_ENGINE_GR   :
        case NVDEV_ENGINE_COPY0:
-       case NVDEV_ENGINE_COPY1: addr = 0x0210; break;
+       case NVDEV_ENGINE_COPY1:
+       case NVDEV_ENGINE_COPY2: addr = 0x0000; break;
+       case NVDEV_ENGINE_GR   : addr = 0x0210; break;
        case NVDEV_ENGINE_BSP  : addr = 0x0270; break;
        case NVDEV_ENGINE_VP   : addr = 0x0250; break;
        case NVDEV_ENGINE_PPP  : addr = 0x0260; break;
@@ -194,9 +186,12 @@ nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend,
                        return -EBUSY;
        }
 
-       nv_wo32(base, addr + 0x00, 0x00000000);
-       nv_wo32(base, addr + 0x04, 0x00000000);
-       bar->flush(bar);
+       if (addr) {
+               nv_wo32(base, addr + 0x00, 0x00000000);
+               nv_wo32(base, addr + 0x04, 0x00000000);
+               bar->flush(bar);
+       }
+
        return 0;
 }
 
@@ -226,8 +221,10 @@ nve0_fifo_chan_ctor(struct nouveau_object *parent,
                }
        }
 
-       if (i == FIFO_ENGINE_NR)
+       if (i == FIFO_ENGINE_NR) {
+               nv_error(priv, "unsupported engines 0x%08x\n", args->engine);
                return -ENODEV;
+       }
 
        ret = nouveau_fifo_channel_create(parent, engine, oclass, 1,
                                          priv->user.bar.offset, 0x200,
@@ -592,13 +589,25 @@ nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
               struct nouveau_object **pobject)
 {
        struct nve0_fifo_priv *priv;
-       int ret;
+       int ret, i;
 
        ret = nouveau_fifo_create(parent, engine, oclass, 0, 4095, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
+       for (i = 0; i < FIFO_ENGINE_NR; i++) {
+               ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0x1000,
+                                        0, &priv->engine[i].playlist[0]);
+               if (ret)
+                       return ret;
+
+               ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0x1000,
+                                        0, &priv->engine[i].playlist[1]);
+               if (ret)
+                       return ret;
+       }
+
        ret = nouveau_gpuobj_new(nv_object(priv), NULL, 4096 * 0x200, 0x1000,
                                 NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem);
        if (ret)
@@ -629,7 +638,7 @@ nve0_fifo_dtor(struct nouveau_object *object)
        nouveau_gpuobj_unmap(&priv->user.bar);
        nouveau_gpuobj_ref(NULL, &priv->user.mem);
 
-       for (i = 0; i < ARRAY_SIZE(priv->engine); i++) {
+       for (i = 0; i < FIFO_ENGINE_NR; i++) {
                nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[1]);
                nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[0]);
        }
index 4cc6269..64dca26 100644 (file)
 
 #include "nvc0.h"
 
-void
-nv_icmd(struct nvc0_graph_priv *priv, u32 icmd, u32 data)
-{
-       nv_wr32(priv, 0x400204, data);
-       nv_wr32(priv, 0x400200, icmd);
-       while (nv_rd32(priv, 0x400700) & 2) {}
-}
+struct nvc0_graph_init
+nvc0_grctx_init_icmd[] = {
+       { 0x001000,   1, 0x01, 0x00000004 },
+       { 0x0000a9,   1, 0x01, 0x0000ffff },
+       { 0x000038,   1, 0x01, 0x0fac6881 },
+       { 0x00003d,   1, 0x01, 0x00000001 },
+       { 0x0000e8,   8, 0x01, 0x00000400 },
+       { 0x000078,   8, 0x01, 0x00000300 },
+       { 0x000050,   1, 0x01, 0x00000011 },
+       { 0x000058,   8, 0x01, 0x00000008 },
+       { 0x000208,   8, 0x01, 0x00000001 },
+       { 0x000081,   1, 0x01, 0x00000001 },
+       { 0x000085,   1, 0x01, 0x00000004 },
+       { 0x000088,   1, 0x01, 0x00000400 },
+       { 0x000090,   1, 0x01, 0x00000300 },
+       { 0x000098,   1, 0x01, 0x00001001 },
+       { 0x0000e3,   1, 0x01, 0x00000001 },
+       { 0x0000da,   1, 0x01, 0x00000001 },
+       { 0x0000f8,   1, 0x01, 0x00000003 },
+       { 0x0000fa,   1, 0x01, 0x00000001 },
+       { 0x00009f,   4, 0x01, 0x0000ffff },
+       { 0x0000b1,   1, 0x01, 0x00000001 },
+       { 0x0000b2,  40, 0x01, 0x00000000 },
+       { 0x000210,   8, 0x01, 0x00000040 },
+       { 0x000218,   8, 0x01, 0x0000c080 },
+       { 0x0000ad,   1, 0x01, 0x0000013e },
+       { 0x0000e1,   1, 0x01, 0x00000010 },
+       { 0x000290,  16, 0x01, 0x00000000 },
+       { 0x0003b0,  16, 0x01, 0x00000000 },
+       { 0x0002a0,  16, 0x01, 0x00000000 },
+       { 0x000420,  16, 0x01, 0x00000000 },
+       { 0x0002b0,  16, 0x01, 0x00000000 },
+       { 0x000430,  16, 0x01, 0x00000000 },
+       { 0x0002c0,  16, 0x01, 0x00000000 },
+       { 0x0004d0,  16, 0x01, 0x00000000 },
+       { 0x000720,  16, 0x01, 0x00000000 },
+       { 0x0008c0,  16, 0x01, 0x00000000 },
+       { 0x000890,  16, 0x01, 0x00000000 },
+       { 0x0008e0,  16, 0x01, 0x00000000 },
+       { 0x0008a0,  16, 0x01, 0x00000000 },
+       { 0x0008f0,  16, 0x01, 0x00000000 },
+       { 0x00094c,   1, 0x01, 0x000000ff },
+       { 0x00094d,   1, 0x01, 0xffffffff },
+       { 0x00094e,   1, 0x01, 0x00000002 },
+       { 0x0002ec,   1, 0x01, 0x00000001 },
+       { 0x000303,   1, 0x01, 0x00000001 },
+       { 0x0002e6,   1, 0x01, 0x00000001 },
+       { 0x000466,   1, 0x01, 0x00000052 },
+       { 0x000301,   1, 0x01, 0x3f800000 },
+       { 0x000304,   1, 0x01, 0x30201000 },
+       { 0x000305,   1, 0x01, 0x70605040 },
+       { 0x000306,   1, 0x01, 0xb8a89888 },
+       { 0x000307,   1, 0x01, 0xf8e8d8c8 },
+       { 0x00030a,   1, 0x01, 0x00ffff00 },
+       { 0x00030b,   1, 0x01, 0x0000001a },
+       { 0x00030c,   1, 0x01, 0x00000001 },
+       { 0x000318,   1, 0x01, 0x00000001 },
+       { 0x000340,   1, 0x01, 0x00000000 },
+       { 0x000375,   1, 0x01, 0x00000001 },
+       { 0x000351,   1, 0x01, 0x00000100 },
+       { 0x00037d,   1, 0x01, 0x00000006 },
+       { 0x0003a0,   1, 0x01, 0x00000002 },
+       { 0x0003aa,   1, 0x01, 0x00000001 },
+       { 0x0003a9,   1, 0x01, 0x00000001 },
+       { 0x000380,   1, 0x01, 0x00000001 },
+       { 0x000360,   1, 0x01, 0x00000040 },
+       { 0x000366,   2, 0x01, 0x00000000 },
+       { 0x000368,   1, 0x01, 0x00001fff },
+       { 0x000370,   2, 0x01, 0x00000000 },
+       { 0x000372,   1, 0x01, 0x003fffff },
+       { 0x00037a,   1, 0x01, 0x00000012 },
+       { 0x0005e0,   5, 0x01, 0x00000022 },
+       { 0x000619,   1, 0x01, 0x00000003 },
+       { 0x000811,   1, 0x01, 0x00000003 },
+       { 0x000812,   1, 0x01, 0x00000004 },
+       { 0x000813,   1, 0x01, 0x00000006 },
+       { 0x000814,   1, 0x01, 0x00000008 },
+       { 0x000815,   1, 0x01, 0x0000000b },
+       { 0x000800,   6, 0x01, 0x00000001 },
+       { 0x000632,   1, 0x01, 0x00000001 },
+       { 0x000633,   1, 0x01, 0x00000002 },
+       { 0x000634,   1, 0x01, 0x00000003 },
+       { 0x000635,   1, 0x01, 0x00000004 },
+       { 0x000654,   1, 0x01, 0x3f800000 },
+       { 0x000657,   1, 0x01, 0x3f800000 },
+       { 0x000655,   2, 0x01, 0x3f800000 },
+       { 0x0006cd,   1, 0x01, 0x3f800000 },
+       { 0x0007f5,   1, 0x01, 0x3f800000 },
+       { 0x0007dc,   1, 0x01, 0x39291909 },
+       { 0x0007dd,   1, 0x01, 0x79695949 },
+       { 0x0007de,   1, 0x01, 0xb9a99989 },
+       { 0x0007df,   1, 0x01, 0xf9e9d9c9 },
+       { 0x0007e8,   1, 0x01, 0x00003210 },
+       { 0x0007e9,   1, 0x01, 0x00007654 },
+       { 0x0007ea,   1, 0x01, 0x00000098 },
+       { 0x0007ec,   1, 0x01, 0x39291909 },
+       { 0x0007ed,   1, 0x01, 0x79695949 },
+       { 0x0007ee,   1, 0x01, 0xb9a99989 },
+       { 0x0007ef,   1, 0x01, 0xf9e9d9c9 },
+       { 0x0007f0,   1, 0x01, 0x00003210 },
+       { 0x0007f1,   1, 0x01, 0x00007654 },
+       { 0x0007f2,   1, 0x01, 0x00000098 },
+       { 0x0005a5,   1, 0x01, 0x00000001 },
+       { 0x000980, 128, 0x01, 0x00000000 },
+       { 0x000468,   1, 0x01, 0x00000004 },
+       { 0x00046c,   1, 0x01, 0x00000001 },
+       { 0x000470,  96, 0x01, 0x00000000 },
+       { 0x000510,  16, 0x01, 0x3f800000 },
+       { 0x000520,   1, 0x01, 0x000002b6 },
+       { 0x000529,   1, 0x01, 0x00000001 },
+       { 0x000530,  16, 0x01, 0xffff0000 },
+       { 0x000585,   1, 0x01, 0x0000003f },
+       { 0x000576,   1, 0x01, 0x00000003 },
+       { 0x000586,   1, 0x01, 0x00000040 },
+       { 0x000582,   2, 0x01, 0x00000080 },
+       { 0x0005c2,   1, 0x01, 0x00000001 },
+       { 0x000638,   1, 0x01, 0x00000001 },
+       { 0x000639,   1, 0x01, 0x00000001 },
+       { 0x00063a,   1, 0x01, 0x00000002 },
+       { 0x00063b,   2, 0x01, 0x00000001 },
+       { 0x00063d,   1, 0x01, 0x00000002 },
+       { 0x00063e,   1, 0x01, 0x00000001 },
+       { 0x0008b8,   8, 0x01, 0x00000001 },
+       { 0x000900,   8, 0x01, 0x00000001 },
+       { 0x000908,   8, 0x01, 0x00000002 },
+       { 0x000910,  16, 0x01, 0x00000001 },
+       { 0x000920,   8, 0x01, 0x00000002 },
+       { 0x000928,   8, 0x01, 0x00000001 },
+       { 0x000648,   9, 0x01, 0x00000001 },
+       { 0x000658,   1, 0x01, 0x0000000f },
+       { 0x0007ff,   1, 0x01, 0x0000000a },
+       { 0x00066a,   1, 0x01, 0x40000000 },
+       { 0x00066b,   1, 0x01, 0x10000000 },
+       { 0x00066c,   2, 0x01, 0xffff0000 },
+       { 0x0007af,   2, 0x01, 0x00000008 },
+       { 0x0007f6,   1, 0x01, 0x00000001 },
+       { 0x0006b2,   1, 0x01, 0x00000055 },
+       { 0x0007ad,   1, 0x01, 0x00000003 },
+       { 0x000937,   1, 0x01, 0x00000001 },
+       { 0x000971,   1, 0x01, 0x00000008 },
+       { 0x000972,   1, 0x01, 0x00000040 },
+       { 0x000973,   1, 0x01, 0x0000012c },
+       { 0x00097c,   1, 0x01, 0x00000040 },
+       { 0x000979,   1, 0x01, 0x00000003 },
+       { 0x000975,   1, 0x01, 0x00000020 },
+       { 0x000976,   1, 0x01, 0x00000001 },
+       { 0x000977,   1, 0x01, 0x00000020 },
+       { 0x000978,   1, 0x01, 0x00000001 },
+       { 0x000957,   1, 0x01, 0x00000003 },
+       { 0x00095e,   1, 0x01, 0x20164010 },
+       { 0x00095f,   1, 0x01, 0x00000020 },
+       { 0x000683,   1, 0x01, 0x00000006 },
+       { 0x000685,   1, 0x01, 0x003fffff },
+       { 0x000687,   1, 0x01, 0x00000c48 },
+       { 0x0006a0,   1, 0x01, 0x00000005 },
+       { 0x000840,   1, 0x01, 0x00300008 },
+       { 0x000841,   1, 0x01, 0x04000080 },
+       { 0x000842,   1, 0x01, 0x00300008 },
+       { 0x000843,   1, 0x01, 0x04000080 },
+       { 0x000818,   8, 0x01, 0x00000000 },
+       { 0x000848,  16, 0x01, 0x00000000 },
+       { 0x000738,   1, 0x01, 0x00000000 },
+       { 0x0006aa,   1, 0x01, 0x00000001 },
+       { 0x0006ab,   1, 0x01, 0x00000002 },
+       { 0x0006ac,   1, 0x01, 0x00000080 },
+       { 0x0006ad,   2, 0x01, 0x00000100 },
+       { 0x0006b1,   1, 0x01, 0x00000011 },
+       { 0x0006bb,   1, 0x01, 0x000000cf },
+       { 0x0006ce,   1, 0x01, 0x2a712488 },
+       { 0x000739,   1, 0x01, 0x4085c000 },
+       { 0x00073a,   1, 0x01, 0x00000080 },
+       { 0x000786,   1, 0x01, 0x80000100 },
+       { 0x00073c,   1, 0x01, 0x00010100 },
+       { 0x00073d,   1, 0x01, 0x02800000 },
+       { 0x000787,   1, 0x01, 0x000000cf },
+       { 0x00078c,   1, 0x01, 0x00000008 },
+       { 0x000792,   1, 0x01, 0x00000001 },
+       { 0x000794,   1, 0x01, 0x00000001 },
+       { 0x000795,   2, 0x01, 0x00000001 },
+       { 0x000797,   1, 0x01, 0x000000cf },
+       { 0x000836,   1, 0x01, 0x00000001 },
+       { 0x00079a,   1, 0x01, 0x00000002 },
+       { 0x000833,   1, 0x01, 0x04444480 },
+       { 0x0007a1,   1, 0x01, 0x00000001 },
+       { 0x0007a3,   1, 0x01, 0x00000001 },
+       { 0x0007a4,   2, 0x01, 0x00000001 },
+       { 0x000831,   1, 0x01, 0x00000004 },
+       { 0x00080c,   1, 0x01, 0x00000002 },
+       { 0x00080d,   2, 0x01, 0x00000100 },
+       { 0x00080f,   1, 0x01, 0x00000001 },
+       { 0x000823,   1, 0x01, 0x00000002 },
+       { 0x000824,   2, 0x01, 0x00000100 },
+       { 0x000826,   1, 0x01, 0x00000001 },
+       { 0x00095d,   1, 0x01, 0x00000001 },
+       { 0x00082b,   1, 0x01, 0x00000004 },
+       { 0x000942,   1, 0x01, 0x00010001 },
+       { 0x000943,   1, 0x01, 0x00000001 },
+       { 0x000944,   1, 0x01, 0x00000022 },
+       { 0x0007c5,   1, 0x01, 0x00010001 },
+       { 0x000834,   1, 0x01, 0x00000001 },
+       { 0x0007c7,   1, 0x01, 0x00000001 },
+       { 0x00c1b0,   8, 0x01, 0x0000000f },
+       { 0x00c1b8,   1, 0x01, 0x0fac6881 },
+       { 0x00c1b9,   1, 0x01, 0x00fac688 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000002 },
+       { 0x0006aa,   1, 0x01, 0x00000001 },
+       { 0x0006ad,   2, 0x01, 0x00000100 },
+       { 0x0006b1,   1, 0x01, 0x00000011 },
+       { 0x00078c,   1, 0x01, 0x00000008 },
+       { 0x000792,   1, 0x01, 0x00000001 },
+       { 0x000794,   1, 0x01, 0x00000001 },
+       { 0x000795,   2, 0x01, 0x00000001 },
+       { 0x000797,   1, 0x01, 0x000000cf },
+       { 0x00079a,   1, 0x01, 0x00000002 },
+       { 0x000833,   1, 0x01, 0x04444480 },
+       { 0x0007a1,   1, 0x01, 0x00000001 },
+       { 0x0007a3,   1, 0x01, 0x00000001 },
+       { 0x0007a4,   2, 0x01, 0x00000001 },
+       { 0x000831,   1, 0x01, 0x00000004 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000014 },
+       { 0x000351,   1, 0x01, 0x00000100 },
+       { 0x000957,   1, 0x01, 0x00000003 },
+       { 0x00095d,   1, 0x01, 0x00000001 },
+       { 0x00082b,   1, 0x01, 0x00000004 },
+       { 0x000942,   1, 0x01, 0x00010001 },
+       { 0x000943,   1, 0x01, 0x00000001 },
+       { 0x0007c5,   1, 0x01, 0x00010001 },
+       { 0x000834,   1, 0x01, 0x00000001 },
+       { 0x0007c7,   1, 0x01, 0x00000001 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000001 },
+       { 0x00080c,   1, 0x01, 0x00000002 },
+       { 0x00080d,   2, 0x01, 0x00000100 },
+       { 0x00080f,   1, 0x01, 0x00000001 },
+       { 0x000823,   1, 0x01, 0x00000002 },
+       { 0x000824,   2, 0x01, 0x00000100 },
+       { 0x000826,   1, 0x01, 0x00000001 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_9097[] = {
+       { 0x000800,   8, 0x40, 0x00000000 },
+       { 0x000804,   8, 0x40, 0x00000000 },
+       { 0x000808,   8, 0x40, 0x00000400 },
+       { 0x00080c,   8, 0x40, 0x00000300 },
+       { 0x000810,   1, 0x04, 0x000000cf },
+       { 0x000850,   7, 0x40, 0x00000000 },
+       { 0x000814,   8, 0x40, 0x00000040 },
+       { 0x000818,   8, 0x40, 0x00000001 },
+       { 0x00081c,   8, 0x40, 0x00000000 },
+       { 0x000820,   8, 0x40, 0x00000000 },
+       { 0x002700,   8, 0x20, 0x00000000 },
+       { 0x002704,   8, 0x20, 0x00000000 },
+       { 0x002708,   8, 0x20, 0x00000000 },
+       { 0x00270c,   8, 0x20, 0x00000000 },
+       { 0x002710,   8, 0x20, 0x00014000 },
+       { 0x002714,   8, 0x20, 0x00000040 },
+       { 0x001c00,  16, 0x10, 0x00000000 },
+       { 0x001c04,  16, 0x10, 0x00000000 },
+       { 0x001c08,  16, 0x10, 0x00000000 },
+       { 0x001c0c,  16, 0x10, 0x00000000 },
+       { 0x001d00,  16, 0x10, 0x00000000 },
+       { 0x001d04,  16, 0x10, 0x00000000 },
+       { 0x001d08,  16, 0x10, 0x00000000 },
+       { 0x001d0c,  16, 0x10, 0x00000000 },
+       { 0x001f00,  16, 0x08, 0x00000000 },
+       { 0x001f04,  16, 0x08, 0x00000000 },
+       { 0x001f80,  16, 0x08, 0x00000000 },
+       { 0x001f84,  16, 0x08, 0x00000000 },
+       { 0x002200,   5, 0x10, 0x00000022 },
+       { 0x002000,   1, 0x04, 0x00000000 },
+       { 0x002040,   1, 0x04, 0x00000011 },
+       { 0x002080,   1, 0x04, 0x00000020 },
+       { 0x0020c0,   1, 0x04, 0x00000030 },
+       { 0x002100,   1, 0x04, 0x00000040 },
+       { 0x002140,   1, 0x04, 0x00000051 },
+       { 0x00200c,   6, 0x40, 0x00000001 },
+       { 0x002010,   1, 0x04, 0x00000000 },
+       { 0x002050,   1, 0x04, 0x00000000 },
+       { 0x002090,   1, 0x04, 0x00000001 },
+       { 0x0020d0,   1, 0x04, 0x00000002 },
+       { 0x002110,   1, 0x04, 0x00000003 },
+       { 0x002150,   1, 0x04, 0x00000004 },
+       { 0x000380,   4, 0x20, 0x00000000 },
+       { 0x000384,   4, 0x20, 0x00000000 },
+       { 0x000388,   4, 0x20, 0x00000000 },
+       { 0x00038c,   4, 0x20, 0x00000000 },
+       { 0x000700,   4, 0x10, 0x00000000 },
+       { 0x000704,   4, 0x10, 0x00000000 },
+       { 0x000708,   4, 0x10, 0x00000000 },
+       { 0x002800, 128, 0x04, 0x00000000 },
+       { 0x000a00,  16, 0x20, 0x00000000 },
+       { 0x000a04,  16, 0x20, 0x00000000 },
+       { 0x000a08,  16, 0x20, 0x00000000 },
+       { 0x000a0c,  16, 0x20, 0x00000000 },
+       { 0x000a10,  16, 0x20, 0x00000000 },
+       { 0x000a14,  16, 0x20, 0x00000000 },
+       { 0x000c00,  16, 0x10, 0x00000000 },
+       { 0x000c04,  16, 0x10, 0x00000000 },
+       { 0x000c08,  16, 0x10, 0x00000000 },
+       { 0x000c0c,  16, 0x10, 0x3f800000 },
+       { 0x000d00,   8, 0x08, 0xffff0000 },
+       { 0x000d04,   8, 0x08, 0xffff0000 },
+       { 0x000e00,  16, 0x10, 0x00000000 },
+       { 0x000e04,  16, 0x10, 0xffff0000 },
+       { 0x000e08,  16, 0x10, 0xffff0000 },
+       { 0x000d40,   4, 0x08, 0x00000000 },
+       { 0x000d44,   4, 0x08, 0x00000000 },
+       { 0x001e00,   8, 0x20, 0x00000001 },
+       { 0x001e04,   8, 0x20, 0x00000001 },
+       { 0x001e08,   8, 0x20, 0x00000002 },
+       { 0x001e0c,   8, 0x20, 0x00000001 },
+       { 0x001e10,   8, 0x20, 0x00000001 },
+       { 0x001e14,   8, 0x20, 0x00000002 },
+       { 0x001e18,   8, 0x20, 0x00000001 },
+       { 0x003400, 128, 0x04, 0x00000000 },
+       { 0x00030c,   1, 0x04, 0x00000001 },
+       { 0x001944,   1, 0x04, 0x00000000 },
+       { 0x001514,   1, 0x04, 0x00000000 },
+       { 0x000d68,   1, 0x04, 0x0000ffff },
+       { 0x00121c,   1, 0x04, 0x0fac6881 },
+       { 0x000fac,   1, 0x04, 0x00000001 },
+       { 0x001538,   1, 0x04, 0x00000001 },
+       { 0x000fe0,   2, 0x04, 0x00000000 },
+       { 0x000fe8,   1, 0x04, 0x00000014 },
+       { 0x000fec,   1, 0x04, 0x00000040 },
+       { 0x000ff0,   1, 0x04, 0x00000000 },
+       { 0x00179c,   1, 0x04, 0x00000000 },
+       { 0x001228,   1, 0x04, 0x00000400 },
+       { 0x00122c,   1, 0x04, 0x00000300 },
+       { 0x001230,   1, 0x04, 0x00010001 },
+       { 0x0007f8,   1, 0x04, 0x00000000 },
+       { 0x0015b4,   1, 0x04, 0x00000001 },
+       { 0x0015cc,   1, 0x04, 0x00000000 },
+       { 0x001534,   1, 0x04, 0x00000000 },
+       { 0x000fb0,   1, 0x04, 0x00000000 },
+       { 0x0015d0,   1, 0x04, 0x00000000 },
+       { 0x00153c,   1, 0x04, 0x00000000 },
+       { 0x0016b4,   1, 0x04, 0x00000003 },
+       { 0x000fbc,   4, 0x04, 0x0000ffff },
+       { 0x000df8,   2, 0x04, 0x00000000 },
+       { 0x001948,   1, 0x04, 0x00000000 },
+       { 0x001970,   1, 0x04, 0x00000001 },
+       { 0x00161c,   1, 0x04, 0x000009f0 },
+       { 0x000dcc,   1, 0x04, 0x00000010 },
+       { 0x00163c,   1, 0x04, 0x00000000 },
+       { 0x0015e4,   1, 0x04, 0x00000000 },
+       { 0x001160,  32, 0x04, 0x25e00040 },
+       { 0x001880,  32, 0x04, 0x00000000 },
+       { 0x000f84,   2, 0x04, 0x00000000 },
+       { 0x0017c8,   2, 0x04, 0x00000000 },
+       { 0x0017d0,   1, 0x04, 0x000000ff },
+       { 0x0017d4,   1, 0x04, 0xffffffff },
+       { 0x0017d8,   1, 0x04, 0x00000002 },
+       { 0x0017dc,   1, 0x04, 0x00000000 },
+       { 0x0015f4,   2, 0x04, 0x00000000 },
+       { 0x001434,   2, 0x04, 0x00000000 },
+       { 0x000d74,   1, 0x04, 0x00000000 },
+       { 0x000dec,   1, 0x04, 0x00000001 },
+       { 0x0013a4,   1, 0x04, 0x00000000 },
+       { 0x001318,   1, 0x04, 0x00000001 },
+       { 0x001644,   1, 0x04, 0x00000000 },
+       { 0x000748,   1, 0x04, 0x00000000 },
+       { 0x000de8,   1, 0x04, 0x00000000 },
+       { 0x001648,   1, 0x04, 0x00000000 },
+       { 0x0012a4,   1, 0x04, 0x00000000 },
+       { 0x001120,   4, 0x04, 0x00000000 },
+       { 0x001118,   1, 0x04, 0x00000000 },
+       { 0x00164c,   1, 0x04, 0x00000000 },
+       { 0x001658,   1, 0x04, 0x00000000 },
+       { 0x001910,   1, 0x04, 0x00000290 },
+       { 0x001518,   1, 0x04, 0x00000000 },
+       { 0x00165c,   1, 0x04, 0x00000001 },
+       { 0x001520,   1, 0x04, 0x00000000 },
+       { 0x001604,   1, 0x04, 0x00000000 },
+       { 0x001570,   1, 0x04, 0x00000000 },
+       { 0x0013b0,   2, 0x04, 0x3f800000 },
+       { 0x00020c,   1, 0x04, 0x00000000 },
+       { 0x001670,   1, 0x04, 0x30201000 },
+       { 0x001674,   1, 0x04, 0x70605040 },
+       { 0x001678,   1, 0x04, 0xb8a89888 },
+       { 0x00167c,   1, 0x04, 0xf8e8d8c8 },
+       { 0x00166c,   1, 0x04, 0x00000000 },
+       { 0x001680,   1, 0x04, 0x00ffff00 },
+       { 0x0012d0,   1, 0x04, 0x00000003 },
+       { 0x0012d4,   1, 0x04, 0x00000002 },
+       { 0x001684,   2, 0x04, 0x00000000 },
+       { 0x000dac,   2, 0x04, 0x00001b02 },
+       { 0x000db4,   1, 0x04, 0x00000000 },
+       { 0x00168c,   1, 0x04, 0x00000000 },
+       { 0x0015bc,   1, 0x04, 0x00000000 },
+       { 0x00156c,   1, 0x04, 0x00000000 },
+       { 0x00187c,   1, 0x04, 0x00000000 },
+       { 0x001110,   1, 0x04, 0x00000001 },
+       { 0x000dc0,   3, 0x04, 0x00000000 },
+       { 0x001234,   1, 0x04, 0x00000000 },
+       { 0x001690,   1, 0x04, 0x00000000 },
+       { 0x0012ac,   1, 0x04, 0x00000001 },
+       { 0x0002c4,   1, 0x04, 0x00000000 },
+       { 0x000790,   5, 0x04, 0x00000000 },
+       { 0x00077c,   1, 0x04, 0x00000000 },
+       { 0x001000,   1, 0x04, 0x00000010 },
+       { 0x0010fc,   1, 0x04, 0x00000000 },
+       { 0x001290,   1, 0x04, 0x00000000 },
+       { 0x000218,   1, 0x04, 0x00000010 },
+       { 0x0012d8,   1, 0x04, 0x00000000 },
+       { 0x0012dc,   1, 0x04, 0x00000010 },
+       { 0x000d94,   1, 0x04, 0x00000001 },
+       { 0x00155c,   2, 0x04, 0x00000000 },
+       { 0x001564,   1, 0x04, 0x00001fff },
+       { 0x001574,   2, 0x04, 0x00000000 },
+       { 0x00157c,   1, 0x04, 0x003fffff },
+       { 0x001354,   1, 0x04, 0x00000000 },
+       { 0x001664,   1, 0x04, 0x00000000 },
+       { 0x001610,   1, 0x04, 0x00000012 },
+       { 0x001608,   2, 0x04, 0x00000000 },
+       { 0x00162c,   1, 0x04, 0x00000003 },
+       { 0x000210,   1, 0x04, 0x00000000 },
+       { 0x000320,   1, 0x04, 0x00000000 },
+       { 0x000324,   6, 0x04, 0x3f800000 },
+       { 0x000750,   1, 0x04, 0x00000000 },
+       { 0x000760,   1, 0x04, 0x39291909 },
+       { 0x000764,   1, 0x04, 0x79695949 },
+       { 0x000768,   1, 0x04, 0xb9a99989 },
+       { 0x00076c,   1, 0x04, 0xf9e9d9c9 },
+       { 0x000770,   1, 0x04, 0x30201000 },
+       { 0x000774,   1, 0x04, 0x70605040 },
+       { 0x000778,   1, 0x04, 0x00009080 },
+       { 0x000780,   1, 0x04, 0x39291909 },
+       { 0x000784,   1, 0x04, 0x79695949 },
+       { 0x000788,   1, 0x04, 0xb9a99989 },
+       { 0x00078c,   1, 0x04, 0xf9e9d9c9 },
+       { 0x0007d0,   1, 0x04, 0x30201000 },
+       { 0x0007d4,   1, 0x04, 0x70605040 },
+       { 0x0007d8,   1, 0x04, 0x00009080 },
+       { 0x00037c,   1, 0x04, 0x00000001 },
+       { 0x000740,   2, 0x04, 0x00000000 },
+       { 0x002600,   1, 0x04, 0x00000000 },
+       { 0x001918,   1, 0x04, 0x00000000 },
+       { 0x00191c,   1, 0x04, 0x00000900 },
+       { 0x001920,   1, 0x04, 0x00000405 },
+       { 0x001308,   1, 0x04, 0x00000001 },
+       { 0x001924,   1, 0x04, 0x00000000 },
+       { 0x0013ac,   1, 0x04, 0x00000000 },
+       { 0x00192c,   1, 0x04, 0x00000001 },
+       { 0x00193c,   1, 0x04, 0x00002c1c },
+       { 0x000d7c,   1, 0x04, 0x00000000 },
+       { 0x000f8c,   1, 0x04, 0x00000000 },
+       { 0x0002c0,   1, 0x04, 0x00000001 },
+       { 0x001510,   1, 0x04, 0x00000000 },
+       { 0x001940,   1, 0x04, 0x00000000 },
+       { 0x000ff4,   2, 0x04, 0x00000000 },
+       { 0x00194c,   2, 0x04, 0x00000000 },
+       { 0x001968,   1, 0x04, 0x00000000 },
+       { 0x001590,   1, 0x04, 0x0000003f },
+       { 0x0007e8,   4, 0x04, 0x00000000 },
+       { 0x00196c,   1, 0x04, 0x00000011 },
+       { 0x00197c,   1, 0x04, 0x00000000 },
+       { 0x000fcc,   2, 0x04, 0x00000000 },
+       { 0x0002d8,   1, 0x04, 0x00000040 },
+       { 0x001980,   1, 0x04, 0x00000080 },
+       { 0x001504,   1, 0x04, 0x00000080 },
+       { 0x001984,   1, 0x04, 0x00000000 },
+       { 0x000300,   1, 0x04, 0x00000001 },
+       { 0x0013a8,   1, 0x04, 0x00000000 },
+       { 0x0012ec,   1, 0x04, 0x00000000 },
+       { 0x001310,   1, 0x04, 0x00000000 },
+       { 0x001314,   1, 0x04, 0x00000001 },
+       { 0x001380,   1, 0x04, 0x00000000 },
+       { 0x001384,   4, 0x04, 0x00000001 },
+       { 0x001394,   1, 0x04, 0x00000000 },
+       { 0x00139c,   1, 0x04, 0x00000000 },
+       { 0x001398,   1, 0x04, 0x00000000 },
+       { 0x001594,   1, 0x04, 0x00000000 },
+       { 0x001598,   4, 0x04, 0x00000001 },
+       { 0x000f54,   3, 0x04, 0x00000000 },
+       { 0x0019bc,   1, 0x04, 0x00000000 },
+       { 0x000f9c,   2, 0x04, 0x00000000 },
+       { 0x0012cc,   1, 0x04, 0x00000000 },
+       { 0x0012e8,   1, 0x04, 0x00000000 },
+       { 0x00130c,   1, 0x04, 0x00000001 },
+       { 0x001360,   8, 0x04, 0x00000000 },
+       { 0x00133c,   2, 0x04, 0x00000001 },
+       { 0x001344,   1, 0x04, 0x00000002 },
+       { 0x001348,   2, 0x04, 0x00000001 },
+       { 0x001350,   1, 0x04, 0x00000002 },
+       { 0x001358,   1, 0x04, 0x00000001 },
+       { 0x0012e4,   1, 0x04, 0x00000000 },
+       { 0x00131c,   1, 0x04, 0x00000000 },
+       { 0x001320,   3, 0x04, 0x00000000 },
+       { 0x0019c0,   1, 0x04, 0x00000000 },
+       { 0x001140,   1, 0x04, 0x00000000 },
+       { 0x0019c4,   1, 0x04, 0x00000000 },
+       { 0x0019c8,   1, 0x04, 0x00001500 },
+       { 0x00135c,   1, 0x04, 0x00000000 },
+       { 0x000f90,   1, 0x04, 0x00000000 },
+       { 0x0019e0,   8, 0x04, 0x00000001 },
+       { 0x0019cc,   1, 0x04, 0x00000001 },
+       { 0x0015b8,   1, 0x04, 0x00000000 },
+       { 0x001a00,   1, 0x04, 0x00001111 },
+       { 0x001a04,   7, 0x04, 0x00000000 },
+       { 0x000d6c,   2, 0x04, 0xffff0000 },
+       { 0x0010f8,   1, 0x04, 0x00001010 },
+       { 0x000d80,   5, 0x04, 0x00000000 },
+       { 0x000da0,   1, 0x04, 0x00000000 },
+       { 0x001508,   1, 0x04, 0x80000000 },
+       { 0x00150c,   1, 0x04, 0x40000000 },
+       { 0x001668,   1, 0x04, 0x00000000 },
+       { 0x000318,   2, 0x04, 0x00000008 },
+       { 0x000d9c,   1, 0x04, 0x00000001 },
+       { 0x0007dc,   1, 0x04, 0x00000000 },
+       { 0x00074c,   1, 0x04, 0x00000055 },
+       { 0x001420,   1, 0x04, 0x00000003 },
+       { 0x0017bc,   2, 0x04, 0x00000000 },
+       { 0x0017c4,   1, 0x04, 0x00000001 },
+       { 0x001008,   1, 0x04, 0x00000008 },
+       { 0x00100c,   1, 0x04, 0x00000040 },
+       { 0x001010,   1, 0x04, 0x0000012c },
+       { 0x000d60,   1, 0x04, 0x00000040 },
+       { 0x00075c,   1, 0x04, 0x00000003 },
+       { 0x001018,   1, 0x04, 0x00000020 },
+       { 0x00101c,   1, 0x04, 0x00000001 },
+       { 0x001020,   1, 0x04, 0x00000020 },
+       { 0x001024,   1, 0x04, 0x00000001 },
+       { 0x001444,   3, 0x04, 0x00000000 },
+       { 0x000360,   1, 0x04, 0x20164010 },
+       { 0x000364,   1, 0x04, 0x00000020 },
+       { 0x000368,   1, 0x04, 0x00000000 },
+       { 0x000de4,   1, 0x04, 0x00000000 },
+       { 0x000204,   1, 0x04, 0x00000006 },
+       { 0x000208,   1, 0x04, 0x00000000 },
+       { 0x0002cc,   1, 0x04, 0x003fffff },
+       { 0x0002d0,   1, 0x04, 0x00000c48 },
+       { 0x001220,   1, 0x04, 0x00000005 },
+       { 0x000fdc,   1, 0x04, 0x00000000 },
+       { 0x000f98,   1, 0x04, 0x00300008 },
+       { 0x001284,   1, 0x04, 0x04000080 },
+       { 0x001450,   1, 0x04, 0x00300008 },
+       { 0x001454,   1, 0x04, 0x04000080 },
+       { 0x000214,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_902d[] = {
+       { 0x000200,   1, 0x04, 0x000000cf },
+       { 0x000204,   1, 0x04, 0x00000001 },
+       { 0x000208,   1, 0x04, 0x00000020 },
+       { 0x00020c,   1, 0x04, 0x00000001 },
+       { 0x000210,   1, 0x04, 0x00000000 },
+       { 0x000214,   1, 0x04, 0x00000080 },
+       { 0x000218,   2, 0x04, 0x00000100 },
+       { 0x000220,   2, 0x04, 0x00000000 },
+       { 0x000230,   1, 0x04, 0x000000cf },
+       { 0x000234,   1, 0x04, 0x00000001 },
+       { 0x000238,   1, 0x04, 0x00000020 },
+       { 0x00023c,   1, 0x04, 0x00000001 },
+       { 0x000244,   1, 0x04, 0x00000080 },
+       { 0x000248,   2, 0x04, 0x00000100 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_9039[] = {
+       { 0x00030c,   3, 0x04, 0x00000000 },
+       { 0x000320,   1, 0x04, 0x00000000 },
+       { 0x000238,   2, 0x04, 0x00000000 },
+       { 0x000318,   2, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_90c0[] = {
+       { 0x00270c,   8, 0x20, 0x00000000 },
+       { 0x00030c,   1, 0x04, 0x00000001 },
+       { 0x001944,   1, 0x04, 0x00000000 },
+       { 0x000758,   1, 0x04, 0x00000100 },
+       { 0x0002c4,   1, 0x04, 0x00000000 },
+       { 0x000790,   5, 0x04, 0x00000000 },
+       { 0x00077c,   1, 0x04, 0x00000000 },
+       { 0x000204,   3, 0x04, 0x00000000 },
+       { 0x000214,   1, 0x04, 0x00000000 },
+       { 0x00024c,   1, 0x04, 0x00000000 },
+       { 0x000d94,   1, 0x04, 0x00000001 },
+       { 0x001608,   2, 0x04, 0x00000000 },
+       { 0x001664,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_base[] = {
+       { 0x400204,   2, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_unk40xx[] = {
+       { 0x404004,  10, 0x04, 0x00000000 },
+       { 0x404044,   1, 0x04, 0x00000000 },
+       { 0x404094,   1, 0x04, 0x00000000 },
+       { 0x404098,  12, 0x04, 0x00000000 },
+       { 0x4040c8,   1, 0x04, 0xf0000087 },
+       { 0x4040d0,   6, 0x04, 0x00000000 },
+       { 0x4040e8,   1, 0x04, 0x00001000 },
+       { 0x4040f8,   1, 0x04, 0x00000000 },
+       { 0x404130,   1, 0x04, 0x00000000 },
+       { 0x404134,   1, 0x04, 0x00000000 },
+       { 0x404138,   1, 0x04, 0x20000040 },
+       { 0x404150,   1, 0x04, 0x0000002e },
+       { 0x404154,   1, 0x04, 0x00000400 },
+       { 0x404158,   1, 0x04, 0x00000200 },
+       { 0x404164,   1, 0x04, 0x00000055 },
+       { 0x404168,   1, 0x04, 0x00000000 },
+       { 0x404174,   1, 0x04, 0x00000000 },
+       { 0x404178,   2, 0x04, 0x00000000 },
+       { 0x404200,   8, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_unk44xx[] = {
+       { 0x404404,  14, 0x04, 0x00000000 },
+       { 0x404460,   2, 0x04, 0x00000000 },
+       { 0x404468,   1, 0x04, 0x00ffffff },
+       { 0x40446c,   1, 0x04, 0x00000000 },
+       { 0x404480,   1, 0x04, 0x00000001 },
+       { 0x404498,   1, 0x04, 0x00000001 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_unk46xx[] = {
+       { 0x404604,   1, 0x04, 0x00000015 },
+       { 0x404608,   1, 0x04, 0x00000000 },
+       { 0x40460c,   1, 0x04, 0x00002e00 },
+       { 0x404610,   1, 0x04, 0x00000100 },
+       { 0x404618,   8, 0x04, 0x00000000 },
+       { 0x404638,   1, 0x04, 0x00000004 },
+       { 0x40463c,   8, 0x04, 0x00000000 },
+       { 0x40465c,   1, 0x04, 0x007f0100 },
+       { 0x404660,   7, 0x04, 0x00000000 },
+       { 0x40467c,   1, 0x04, 0x00000002 },
+       { 0x404680,   8, 0x04, 0x00000000 },
+       { 0x4046a0,   1, 0x04, 0x007f0080 },
+       { 0x4046a4,  18, 0x04, 0x00000000 },
+       { 0x4046f0,   2, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_unk47xx[] = {
+       { 0x404700,  13, 0x04, 0x00000000 },
+       { 0x404734,   1, 0x04, 0x00000100 },
+       { 0x404738,   8, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_unk58xx[] = {
+       { 0x405800,   1, 0x04, 0x078000bf },
+       { 0x405830,   1, 0x04, 0x02180000 },
+       { 0x405834,   2, 0x04, 0x00000000 },
+       { 0x405854,   1, 0x04, 0x00000000 },
+       { 0x405870,   4, 0x04, 0x00000001 },
+       { 0x405a00,   2, 0x04, 0x00000000 },
+       { 0x405a18,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_unk60xx[] = {
+       { 0x406020,   1, 0x04, 0x000103c1 },
+       { 0x406028,   4, 0x04, 0x00000001 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_unk64xx[] = {
+       { 0x4064a8,   1, 0x04, 0x00000000 },
+       { 0x4064ac,   1, 0x04, 0x00003fff },
+       { 0x4064b4,   2, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_unk78xx[] = {
+       { 0x407804,   1, 0x04, 0x00000023 },
+       { 0x40780c,   1, 0x04, 0x0a418820 },
+       { 0x407810,   1, 0x04, 0x062080e6 },
+       { 0x407814,   1, 0x04, 0x020398a4 },
+       { 0x407818,   1, 0x04, 0x0e629062 },
+       { 0x40781c,   1, 0x04, 0x0a418820 },
+       { 0x407820,   1, 0x04, 0x000000e6 },
+       { 0x4078bc,   1, 0x04, 0x00000103 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_unk80xx[] = {
+       { 0x408000,   2, 0x04, 0x00000000 },
+       { 0x408008,   1, 0x04, 0x00000018 },
+       { 0x40800c,   2, 0x04, 0x00000000 },
+       { 0x408014,   1, 0x04, 0x00000069 },
+       { 0x408018,   1, 0x04, 0xe100e100 },
+       { 0x408064,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_rop[] = {
+       { 0x408800,   1, 0x04, 0x02802a3c },
+       { 0x408804,   1, 0x04, 0x00000040 },
+       { 0x408808,   1, 0x04, 0x0003e00d },
+       { 0x408900,   1, 0x04, 0x3080b801 },
+       { 0x408904,   1, 0x04, 0x02000001 },
+       { 0x408908,   1, 0x04, 0x00c80929 },
+       { 0x408980,   1, 0x04, 0x0000011d },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_gpc_0[] = {
+       { 0x418380,   1, 0x04, 0x00000016 },
+       { 0x418400,   1, 0x04, 0x38004e00 },
+       { 0x418404,   1, 0x04, 0x71e0ffff },
+       { 0x418408,   1, 0x04, 0x00000000 },
+       { 0x41840c,   1, 0x04, 0x00001008 },
+       { 0x418410,   1, 0x04, 0x0fff0fff },
+       { 0x418414,   1, 0x04, 0x00200fff },
+       { 0x418450,   6, 0x04, 0x00000000 },
+       { 0x418468,   1, 0x04, 0x00000001 },
+       { 0x41846c,   2, 0x04, 0x00000000 },
+       { 0x418600,   1, 0x04, 0x0000001f },
+       { 0x418684,   1, 0x04, 0x0000000f },
+       { 0x418700,   1, 0x04, 0x00000002 },
+       { 0x418704,   1, 0x04, 0x00000080 },
+       { 0x418708,   1, 0x04, 0x00000000 },
+       { 0x41870c,   1, 0x04, 0x07c80000 },
+       { 0x418710,   1, 0x04, 0x00000000 },
+       { 0x418800,   1, 0x04, 0x0006860a },
+       { 0x418808,   3, 0x04, 0x00000000 },
+       { 0x418828,   1, 0x04, 0x00008442 },
+       { 0x418830,   1, 0x04, 0x00000001 },
+       { 0x4188d8,   1, 0x04, 0x00000008 },
+       { 0x4188e0,   1, 0x04, 0x01000000 },
+       { 0x4188e8,   5, 0x04, 0x00000000 },
+       { 0x4188fc,   1, 0x04, 0x00100000 },
+       { 0x41891c,   1, 0x04, 0x00ff00ff },
+       { 0x418924,   1, 0x04, 0x00000000 },
+       { 0x418928,   1, 0x04, 0x00ffff00 },
+       { 0x41892c,   1, 0x04, 0x0000ff00 },
+       { 0x418b00,   1, 0x04, 0x00000000 },
+       { 0x418b08,   1, 0x04, 0x0a418820 },
+       { 0x418b0c,   1, 0x04, 0x062080e6 },
+       { 0x418b10,   1, 0x04, 0x020398a4 },
+       { 0x418b14,   1, 0x04, 0x0e629062 },
+       { 0x418b18,   1, 0x04, 0x0a418820 },
+       { 0x418b1c,   1, 0x04, 0x000000e6 },
+       { 0x418bb8,   1, 0x04, 0x00000103 },
+       { 0x418c08,   1, 0x04, 0x00000001 },
+       { 0x418c10,   8, 0x04, 0x00000000 },
+       { 0x418c80,   1, 0x04, 0x20200004 },
+       { 0x418c8c,   1, 0x04, 0x00000001 },
+       { 0x419000,   1, 0x04, 0x00000780 },
+       { 0x419004,   2, 0x04, 0x00000000 },
+       { 0x419014,   1, 0x04, 0x00000004 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_gpc_1[] = {
+       { 0x418a00,   3, 0x04, 0x00000000 },
+       { 0x418a0c,   1, 0x04, 0x00010000 },
+       { 0x418a10,   3, 0x04, 0x00000000 },
+       { 0x418a20,   3, 0x04, 0x00000000 },
+       { 0x418a2c,   1, 0x04, 0x00010000 },
+       { 0x418a30,   3, 0x04, 0x00000000 },
+       { 0x418a40,   3, 0x04, 0x00000000 },
+       { 0x418a4c,   1, 0x04, 0x00010000 },
+       { 0x418a50,   3, 0x04, 0x00000000 },
+       { 0x418a60,   3, 0x04, 0x00000000 },
+       { 0x418a6c,   1, 0x04, 0x00010000 },
+       { 0x418a70,   3, 0x04, 0x00000000 },
+       { 0x418a80,   3, 0x04, 0x00000000 },
+       { 0x418a8c,   1, 0x04, 0x00010000 },
+       { 0x418a90,   3, 0x04, 0x00000000 },
+       { 0x418aa0,   3, 0x04, 0x00000000 },
+       { 0x418aac,   1, 0x04, 0x00010000 },
+       { 0x418ab0,   3, 0x04, 0x00000000 },
+       { 0x418ac0,   3, 0x04, 0x00000000 },
+       { 0x418acc,   1, 0x04, 0x00010000 },
+       { 0x418ad0,   3, 0x04, 0x00000000 },
+       { 0x418ae0,   3, 0x04, 0x00000000 },
+       { 0x418aec,   1, 0x04, 0x00010000 },
+       { 0x418af0,   3, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_tpc[] = {
+       { 0x419818,   1, 0x04, 0x00000000 },
+       { 0x41983c,   1, 0x04, 0x00038bc7 },
+       { 0x419848,   1, 0x04, 0x00000000 },
+       { 0x419864,   1, 0x04, 0x0000012a },
+       { 0x419888,   1, 0x04, 0x00000000 },
+       { 0x419a00,   1, 0x04, 0x000001f0 },
+       { 0x419a04,   1, 0x04, 0x00000001 },
+       { 0x419a08,   1, 0x04, 0x00000023 },
+       { 0x419a0c,   1, 0x04, 0x00020000 },
+       { 0x419a10,   1, 0x04, 0x00000000 },
+       { 0x419a14,   1, 0x04, 0x00000200 },
+       { 0x419b00,   1, 0x04, 0x0a418820 },
+       { 0x419b04,   1, 0x04, 0x062080e6 },
+       { 0x419b08,   1, 0x04, 0x020398a4 },
+       { 0x419b0c,   1, 0x04, 0x0e629062 },
+       { 0x419b10,   1, 0x04, 0x0a418820 },
+       { 0x419b14,   1, 0x04, 0x000000e6 },
+       { 0x419bd0,   1, 0x04, 0x00900103 },
+       { 0x419be0,   1, 0x04, 0x00000001 },
+       { 0x419be4,   1, 0x04, 0x00000000 },
+       { 0x419c00,   1, 0x04, 0x00000002 },
+       { 0x419c04,   1, 0x04, 0x00000006 },
+       { 0x419c08,   1, 0x04, 0x00000002 },
+       { 0x419c20,   1, 0x04, 0x00000000 },
+       { 0x419cb0,   1, 0x04, 0x00060048 },
+       { 0x419ce8,   1, 0x04, 0x00000000 },
+       { 0x419cf4,   1, 0x04, 0x00000183 },
+       { 0x419d20,   1, 0x04, 0x02180000 },
+       { 0x419d24,   1, 0x04, 0x00001fff },
+       { 0x419e04,   3, 0x04, 0x00000000 },
+       { 0x419e10,   1, 0x04, 0x00000002 },
+       { 0x419e44,   1, 0x04, 0x001beff2 },
+       { 0x419e48,   1, 0x04, 0x00000000 },
+       { 0x419e4c,   1, 0x04, 0x0000000f },
+       { 0x419e50,  17, 0x04, 0x00000000 },
+       { 0x419e98,   1, 0x04, 0x00000000 },
+       { 0x419f50,   2, 0x04, 0x00000000 },
+       {}
+};
 
-int
-nvc0_grctx_init(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+void
+nvc0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
 {
-       struct nouveau_bar *bar = nouveau_bar(priv);
-       struct nouveau_gpuobj *chan;
-       u32 size = (0x80000 + priv->size + 4095) & ~4095;
-       int ret, i;
-
-       /* allocate memory to for a "channel", which we'll use to generate
-        * the default context values
-        */
-       ret = nouveau_gpuobj_new(nv_object(priv), NULL, size, 0x1000,
-                                NVOBJ_FLAG_ZERO_ALLOC, &info->chan);
-       chan = info->chan;
-       if (ret) {
-               nv_error(priv, "failed to allocate channel memory, %d\n", ret);
-               return ret;
-       }
-
-       /* PGD pointer */
-       nv_wo32(chan, 0x0200, lower_32_bits(chan->addr + 0x1000));
-       nv_wo32(chan, 0x0204, upper_32_bits(chan->addr + 0x1000));
-       nv_wo32(chan, 0x0208, 0xffffffff);
-       nv_wo32(chan, 0x020c, 0x000000ff);
-
-       /* PGT[0] pointer */
-       nv_wo32(chan, 0x1000, 0x00000000);
-       nv_wo32(chan, 0x1004, 0x00000001 | (chan->addr + 0x2000) >> 8);
-
-       /* identity-map the whole "channel" into its own vm */
-       for (i = 0; i < size / 4096; i++) {
-               u64 addr = ((chan->addr + (i * 4096)) >> 8) | 1;
-               nv_wo32(chan, 0x2000 + (i * 8), lower_32_bits(addr));
-               nv_wo32(chan, 0x2004 + (i * 8), upper_32_bits(addr));
-       }
-
-       /* context pointer (virt) */
-       nv_wo32(chan, 0x0210, 0x00080004);
-       nv_wo32(chan, 0x0214, 0x00000000);
+       int gpc, tpc;
+       u32 offset;
 
-       bar->flush(bar);
-
-       nv_wr32(priv, 0x100cb8, (chan->addr + 0x1000) >> 8);
-       nv_wr32(priv, 0x100cbc, 0x80000001);
-       nv_wait(priv, 0x100c80, 0x00008000, 0x00008000);
-
-       /* setup default state for mmio list construction */
-       info->data = priv->mmio_data;
-       info->mmio = priv->mmio_list;
-       info->addr = 0x2000 + (i * 8);
-       info->priv = priv;
-       info->buffer_nr = 0;
+       mmio_data(0x002000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+       mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+       mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
 
-       if (priv->firmware) {
-               nv_wr32(priv, 0x409840, 0x00000030);
-               nv_wr32(priv, 0x409500, 0x80000000 | chan->addr >> 12);
-               nv_wr32(priv, 0x409504, 0x00000003);
-               if (!nv_wait(priv, 0x409800, 0x00000010, 0x00000010))
-                       nv_error(priv, "load_ctx timeout\n");
+       mmio_list(0x408004, 0x00000000,  8, 0);
+       mmio_list(0x408008, 0x80000018,  0, 0);
+       mmio_list(0x40800c, 0x00000000,  8, 1);
+       mmio_list(0x408010, 0x80000000,  0, 0);
+       mmio_list(0x418810, 0x80000000, 12, 2);
+       mmio_list(0x419848, 0x10000000, 12, 2);
+       mmio_list(0x419004, 0x00000000,  8, 1);
+       mmio_list(0x419008, 0x00000000,  0, 0);
+       mmio_list(0x418808, 0x00000000,  8, 0);
+       mmio_list(0x41880c, 0x80000018,  0, 0);
 
-               nv_wo32(chan, 0x8001c, 1);
-               nv_wo32(chan, 0x80020, 0);
-               nv_wo32(chan, 0x80028, 0);
-               nv_wo32(chan, 0x8002c, 0);
-               bar->flush(bar);
-               return 0;
-       }
+       mmio_list(0x405830, 0x02180000, 0, 0);
 
-       /* HUB_FUC(SET_CHAN) */
-       nv_wr32(priv, 0x409840, 0x80000000);
-       nv_wr32(priv, 0x409500, 0x80000000 | chan->addr >> 12);
-       nv_wr32(priv, 0x409504, 0x00000001);
-       if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) {
-               nv_error(priv, "HUB_SET_CHAN timeout\n");
-               nvc0_graph_ctxctl_debug(priv);
-               nouveau_gpuobj_ref(NULL, &info->chan);
-               return -EBUSY;
+       for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
+               for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
+                       u32 addr = TPC_UNIT(gpc, tpc, 0x0520);
+                       mmio_list(addr, 0x02180000 | offset, 0, 0);
+                       offset += 0x0324;
+               }
        }
-
-       return 0;
 }
 
 void
-nvc0_grctx_data(struct nvc0_grctx *info, u32 size, u32 align, u32 access)
+nvc0_grctx_generate_unkn(struct nvc0_graph_priv *priv)
 {
-       info->buffer[info->buffer_nr]  = info->addr;
-       info->buffer[info->buffer_nr] +=  (align - 1);
-       info->buffer[info->buffer_nr] &= ~(align - 1);
-       info->addr = info->buffer[info->buffer_nr++] + size;
-
-       info->data->size = size;
-       info->data->align = align;
-       info->data->access = access;
-       info->data++;
 }
 
 void
-nvc0_grctx_mmio(struct nvc0_grctx *info, u32 addr, u32 data, u32 shift, u32 buf)
+nvc0_grctx_generate_tpcid(struct nvc0_graph_priv *priv)
 {
-       struct nvc0_graph_priv *priv = info->priv;
-
-       info->mmio->addr = addr;
-       info->mmio->data = data;
-       info->mmio->shift = shift;
-       info->mmio->buffer = buf;
-       info->mmio++;
+       int gpc, tpc, id;
 
-       if (shift)
-               data |= info->buffer[buf] >> shift;
-       nv_wr32(priv, addr, data);
-}
-
-int
-nvc0_grctx_fini(struct nvc0_grctx *info)
-{
-       struct nvc0_graph_priv *priv = info->priv;
-       int i;
-
-       /* trigger a context unload by unsetting the "next channel valid" bit
-        * and faking a context switch interrupt
-        */
-       nv_mask(priv, 0x409b04, 0x80000000, 0x00000000);
-       nv_wr32(priv, 0x409000, 0x00000100);
-       if (!nv_wait(priv, 0x409b00, 0x80000000, 0x00000000)) {
-               nv_error(priv, "grctx template channel unload timeout\n");
-               return -EBUSY;
-       }
-
-       priv->data = kmalloc(priv->size, GFP_KERNEL);
-       if (priv->data) {
-               for (i = 0; i < priv->size; i += 4)
-                       priv->data[i / 4] = nv_ro32(info->chan, 0x80000 + i);
-       }
-
-       nouveau_gpuobj_ref(NULL, &info->chan);
-       return priv->data ? 0 : -ENOMEM;
-}
+       for (tpc = 0, id = 0; tpc < 4; tpc++) {
+               for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+                       if (tpc < priv->tpc_nr[gpc]) {
+                               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x698), id);
+                               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x4e8), id);
+                               nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id);
+                               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x088), id);
+                               id++;
+                       }
 
-static void
-nvc0_grctx_generate_9097(struct nvc0_graph_priv *priv)
-{
-       u32 fermi = nvc0_graph_class(priv);
-       u32 mthd;
-
-       nv_mthd(priv, 0x9097, 0x0800, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0840, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0880, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x08c0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0900, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0940, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0980, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x09c0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0804, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0844, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0884, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x08c4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0904, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0944, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0984, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x09c4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0808, 0x00000400);
-       nv_mthd(priv, 0x9097, 0x0848, 0x00000400);
-       nv_mthd(priv, 0x9097, 0x0888, 0x00000400);
-       nv_mthd(priv, 0x9097, 0x08c8, 0x00000400);
-       nv_mthd(priv, 0x9097, 0x0908, 0x00000400);
-       nv_mthd(priv, 0x9097, 0x0948, 0x00000400);
-       nv_mthd(priv, 0x9097, 0x0988, 0x00000400);
-       nv_mthd(priv, 0x9097, 0x09c8, 0x00000400);
-       nv_mthd(priv, 0x9097, 0x080c, 0x00000300);
-       nv_mthd(priv, 0x9097, 0x084c, 0x00000300);
-       nv_mthd(priv, 0x9097, 0x088c, 0x00000300);
-       nv_mthd(priv, 0x9097, 0x08cc, 0x00000300);
-       nv_mthd(priv, 0x9097, 0x090c, 0x00000300);
-       nv_mthd(priv, 0x9097, 0x094c, 0x00000300);
-       nv_mthd(priv, 0x9097, 0x098c, 0x00000300);
-       nv_mthd(priv, 0x9097, 0x09cc, 0x00000300);
-       nv_mthd(priv, 0x9097, 0x0810, 0x000000cf);
-       nv_mthd(priv, 0x9097, 0x0850, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0890, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x08d0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0910, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0950, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0990, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x09d0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0814, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x0854, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x0894, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x08d4, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x0914, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x0954, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x0994, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x09d4, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x0818, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x0858, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x0898, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x08d8, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x0918, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x0958, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x0998, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x09d8, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x081c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x085c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x089c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x08dc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x091c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x095c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x099c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x09dc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0820, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0860, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x08a0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x08e0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0920, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0960, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x09a0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x09e0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2700, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2720, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2740, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2760, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2780, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27a0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27c0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27e0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2704, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2724, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2744, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2764, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2784, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27a4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27c4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27e4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2708, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2728, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2748, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2768, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2788, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27a8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27c8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27e8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x270c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x272c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x274c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x276c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x278c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27ac, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27cc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x27ec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2710, 0x00014000);
-       nv_mthd(priv, 0x9097, 0x2730, 0x00014000);
-       nv_mthd(priv, 0x9097, 0x2750, 0x00014000);
-       nv_mthd(priv, 0x9097, 0x2770, 0x00014000);
-       nv_mthd(priv, 0x9097, 0x2790, 0x00014000);
-       nv_mthd(priv, 0x9097, 0x27b0, 0x00014000);
-       nv_mthd(priv, 0x9097, 0x27d0, 0x00014000);
-       nv_mthd(priv, 0x9097, 0x27f0, 0x00014000);
-       nv_mthd(priv, 0x9097, 0x2714, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x2734, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x2754, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x2774, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x2794, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x27b4, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x27d4, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x27f4, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x1c00, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c10, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c20, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c30, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c40, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c50, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c60, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c70, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c80, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c90, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ca0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cb0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cc0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cd0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ce0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cf0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c04, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c14, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c24, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c34, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c44, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c54, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c64, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c74, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c84, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c94, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ca4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cb4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cc4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cd4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ce4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cf4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c08, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c18, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c28, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c38, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c48, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c58, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c68, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c78, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c88, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c98, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ca8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cb8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cc8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cd8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ce8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cf8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c0c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c1c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c2c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c3c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c4c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c5c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c6c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c7c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c8c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1c9c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cac, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cbc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ccc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cdc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1cfc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d00, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d10, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d20, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d30, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d40, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d50, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d60, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d70, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d80, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d90, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1da0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1db0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1dc0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1dd0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1de0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1df0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d04, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d14, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d24, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d34, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d44, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d54, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d64, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d74, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d84, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d94, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1da4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1db4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1dc4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1dd4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1de4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1df4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d08, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d18, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d28, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d38, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d48, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d58, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d68, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d78, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d88, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d98, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1da8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1db8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1dc8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1dd8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1de8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1df8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d0c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d1c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d2c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d3c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d4c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d5c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d6c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d7c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d8c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1d9c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1dac, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1dbc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1dcc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ddc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1dec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1dfc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f00, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f08, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f10, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f18, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f20, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f28, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f30, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f38, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f40, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f48, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f50, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f58, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f60, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f68, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f70, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f78, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f04, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f0c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f14, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f1c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f24, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f2c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f34, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f3c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f44, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f4c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f54, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f5c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f64, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f6c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f74, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f7c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f80, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f88, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f90, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f98, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fa0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fa8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fb0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fb8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fc0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fc8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fd0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fd8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fe0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fe8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ff0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ff8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f84, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f8c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f94, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1f9c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fa4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fac, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fb4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fbc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fc4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fcc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fd4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fdc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fe4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1fec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ff4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1ffc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2200, 0x00000022);
-       nv_mthd(priv, 0x9097, 0x2210, 0x00000022);
-       nv_mthd(priv, 0x9097, 0x2220, 0x00000022);
-       nv_mthd(priv, 0x9097, 0x2230, 0x00000022);
-       nv_mthd(priv, 0x9097, 0x2240, 0x00000022);
-       nv_mthd(priv, 0x9097, 0x2000, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2040, 0x00000011);
-       nv_mthd(priv, 0x9097, 0x2080, 0x00000020);
-       nv_mthd(priv, 0x9097, 0x20c0, 0x00000030);
-       nv_mthd(priv, 0x9097, 0x2100, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x2140, 0x00000051);
-       nv_mthd(priv, 0x9097, 0x200c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x204c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x208c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x20cc, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x210c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x214c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x2010, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2050, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2090, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x20d0, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x2110, 0x00000003);
-       nv_mthd(priv, 0x9097, 0x2150, 0x00000004);
-       nv_mthd(priv, 0x9097, 0x0380, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03a0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03c0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03e0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0384, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03a4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03c4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03e4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0388, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03a8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03c8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03e8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x038c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03ac, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03cc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x03ec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0700, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0710, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0720, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0730, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0704, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0714, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0724, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0734, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0708, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0718, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0728, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0738, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2800, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2804, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2808, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x280c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2810, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2814, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2818, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x281c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2820, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2824, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2828, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x282c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2830, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2834, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2838, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x283c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2840, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2844, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2848, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x284c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2850, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2854, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2858, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x285c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2860, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2864, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2868, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x286c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2870, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2874, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2878, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x287c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2880, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2884, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2888, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x288c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2890, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2894, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2898, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x289c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28a0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28a4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28a8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28ac, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28b0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28b4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28b8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28bc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28c0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28c4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28c8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28cc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28d0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28d4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28d8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28dc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28e0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28e4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28e8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28ec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28f0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28f4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28f8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x28fc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2900, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2904, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2908, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x290c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2910, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2914, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2918, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x291c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2920, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2924, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2928, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x292c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2930, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2934, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2938, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x293c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2940, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2944, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2948, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x294c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2950, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2954, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2958, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x295c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2960, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2964, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2968, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x296c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2970, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2974, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2978, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x297c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2980, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2984, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2988, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x298c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2990, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2994, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2998, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x299c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29a0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29a4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29a8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29ac, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29b0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29b4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29b8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29bc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29c0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29c4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29c8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29cc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29d0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29d4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29d8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29dc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29e0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29e4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29e8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29ec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29f0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29f4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29f8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x29fc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a00, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a20, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a40, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a60, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a80, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0aa0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ac0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ae0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b00, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b20, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b40, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b60, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b80, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ba0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bc0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0be0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a04, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a24, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a44, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a64, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a84, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0aa4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ac4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ae4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b04, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b24, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b44, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b64, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b84, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ba4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bc4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0be4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a08, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a28, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a48, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a68, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a88, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0aa8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ac8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ae8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b08, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b28, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b48, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b68, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b88, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ba8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bc8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0be8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a0c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a2c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a4c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a6c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a8c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0aac, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0acc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0aec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b0c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b2c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b4c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b6c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b8c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bac, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bcc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a10, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a30, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a50, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a70, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a90, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ab0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ad0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0af0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b10, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b30, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b50, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b70, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b90, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bb0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bd0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bf0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a14, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a34, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a54, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a74, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0a94, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ab4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ad4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0af4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b14, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b34, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b54, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b74, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0b94, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bb4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bd4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0bf4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c00, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c10, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c20, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c30, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c40, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c50, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c60, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c70, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c80, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c90, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ca0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cb0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cc0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cd0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ce0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cf0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c04, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c14, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c24, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c34, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c44, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c54, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c64, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c74, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c84, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c94, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ca4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cb4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cc4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cd4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ce4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cf4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c08, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c18, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c28, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c38, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c48, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c58, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c68, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c78, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c88, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c98, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ca8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cb8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cc8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cd8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ce8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0cf8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0c0c, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0c1c, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0c2c, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0c3c, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0c4c, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0c5c, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0c6c, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0c7c, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0c8c, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0c9c, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0cac, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0cbc, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0ccc, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0cdc, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0cec, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0cfc, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0d00, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d08, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d10, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d18, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d20, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d28, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d30, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d38, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d04, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d0c, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d14, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d1c, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d24, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d2c, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d34, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d3c, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e00, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0e10, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0e20, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0e30, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0e40, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0e50, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0e60, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0e70, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0e80, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0e90, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ea0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0eb0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ec0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ed0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ee0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ef0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0e04, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e14, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e24, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e34, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e44, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e54, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e64, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e74, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e84, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e94, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0ea4, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0eb4, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0ec4, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0ed4, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0ee4, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0ef4, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e08, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e18, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e28, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e38, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e48, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e58, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e68, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e78, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e88, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0e98, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0ea8, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0eb8, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0ec8, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0ed8, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0ee8, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0ef8, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d40, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d48, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d50, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d58, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d44, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d4c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d54, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d5c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1e00, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e20, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e40, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e60, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e80, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1ea0, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1ec0, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1ee0, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e04, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e24, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e44, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e64, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e84, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1ea4, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1ec4, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1ee4, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e08, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1e28, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1e48, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1e68, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1e88, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1ea8, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1ec8, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1ee8, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1e0c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e2c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e4c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e6c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e8c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1eac, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1ecc, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1eec, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e10, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e30, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e50, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e70, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e90, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1eb0, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1ed0, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1ef0, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e14, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1e34, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1e54, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1e74, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1e94, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1eb4, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1ed4, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1ef4, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1e18, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e38, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e58, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e78, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1e98, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1eb8, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1ed8, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1ef8, 0x00000001);
-       if (fermi == 0x9097) {
-               for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4)
-                       nv_mthd(priv, 0x9097, mthd, 0x00000000);
+                       nv_wr32(priv, GPC_UNIT(gpc, 0x0c08), priv->tpc_nr[gpc]);
+                       nv_wr32(priv, GPC_UNIT(gpc, 0x0c8c), priv->tpc_nr[gpc]);
+               }
        }
-       nv_mthd(priv, 0x9097, 0x030c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1944, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1514, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d68, 0x0000ffff);
-       nv_mthd(priv, 0x9097, 0x121c, 0x0fac6881);
-       nv_mthd(priv, 0x9097, 0x0fac, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1538, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x0fe0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0fe4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0fe8, 0x00000014);
-       nv_mthd(priv, 0x9097, 0x0fec, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x0ff0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x179c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1228, 0x00000400);
-       nv_mthd(priv, 0x9097, 0x122c, 0x00000300);
-       nv_mthd(priv, 0x9097, 0x1230, 0x00010001);
-       nv_mthd(priv, 0x9097, 0x07f8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x15b4, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x15cc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1534, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0fb0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x15d0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x153c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x16b4, 0x00000003);
-       nv_mthd(priv, 0x9097, 0x0fbc, 0x0000ffff);
-       nv_mthd(priv, 0x9097, 0x0fc0, 0x0000ffff);
-       nv_mthd(priv, 0x9097, 0x0fc4, 0x0000ffff);
-       nv_mthd(priv, 0x9097, 0x0fc8, 0x0000ffff);
-       nv_mthd(priv, 0x9097, 0x0df8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0dfc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1948, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1970, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x161c, 0x000009f0);
-       nv_mthd(priv, 0x9097, 0x0dcc, 0x00000010);
-       nv_mthd(priv, 0x9097, 0x163c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x15e4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1160, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1164, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1168, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x116c, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1170, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1174, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1178, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x117c, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1180, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1184, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1188, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x118c, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1190, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1194, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1198, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x119c, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11a0, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11a4, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11a8, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11ac, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11b0, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11b4, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11b8, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11bc, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11c0, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11c4, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11c8, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11cc, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11d0, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11d4, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11d8, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x11dc, 0x25e00040);
-       nv_mthd(priv, 0x9097, 0x1880, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1884, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1888, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x188c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1890, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1894, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1898, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x189c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18a0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18a4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18a8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18ac, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18b0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18b4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18b8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18bc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18c0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18c4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18c8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18cc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18d0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18d4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18d8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18dc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18e0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18e4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18e8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18ec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18f0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18f4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18f8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x18fc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0f84, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0f88, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x17c8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x17cc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x17d0, 0x000000ff);
-       nv_mthd(priv, 0x9097, 0x17d4, 0xffffffff);
-       nv_mthd(priv, 0x9097, 0x17d8, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x17dc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x15f4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x15f8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1434, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1438, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d74, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0dec, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x13a4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1318, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1644, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0748, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0de8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1648, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x12a4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1120, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1124, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1128, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x112c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1118, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x164c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1658, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1910, 0x00000290);
-       nv_mthd(priv, 0x9097, 0x1518, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x165c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1520, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1604, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1570, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x13b0, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x13b4, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x020c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1670, 0x30201000);
-       nv_mthd(priv, 0x9097, 0x1674, 0x70605040);
-       nv_mthd(priv, 0x9097, 0x1678, 0xb8a89888);
-       nv_mthd(priv, 0x9097, 0x167c, 0xf8e8d8c8);
-       nv_mthd(priv, 0x9097, 0x166c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1680, 0x00ffff00);
-       nv_mthd(priv, 0x9097, 0x12d0, 0x00000003);
-       nv_mthd(priv, 0x9097, 0x12d4, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1684, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1688, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0dac, 0x00001b02);
-       nv_mthd(priv, 0x9097, 0x0db0, 0x00001b02);
-       nv_mthd(priv, 0x9097, 0x0db4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x168c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x15bc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x156c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x187c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1110, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x0dc0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0dc4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0dc8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1234, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1690, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x12ac, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x02c4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0790, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0794, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0798, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x079c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x07a0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x077c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1000, 0x00000010);
-       nv_mthd(priv, 0x9097, 0x10fc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1290, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0218, 0x00000010);
-       nv_mthd(priv, 0x9097, 0x12d8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x12dc, 0x00000010);
-       nv_mthd(priv, 0x9097, 0x0d94, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x155c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1560, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1564, 0x00001fff);
-       nv_mthd(priv, 0x9097, 0x1574, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1578, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x157c, 0x003fffff);
-       nv_mthd(priv, 0x9097, 0x1354, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1664, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1610, 0x00000012);
-       nv_mthd(priv, 0x9097, 0x1608, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x160c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x162c, 0x00000003);
-       nv_mthd(priv, 0x9097, 0x0210, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0320, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0324, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0328, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x032c, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0330, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0334, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0338, 0x3f800000);
-       nv_mthd(priv, 0x9097, 0x0750, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0760, 0x39291909);
-       nv_mthd(priv, 0x9097, 0x0764, 0x79695949);
-       nv_mthd(priv, 0x9097, 0x0768, 0xb9a99989);
-       nv_mthd(priv, 0x9097, 0x076c, 0xf9e9d9c9);
-       nv_mthd(priv, 0x9097, 0x0770, 0x30201000);
-       nv_mthd(priv, 0x9097, 0x0774, 0x70605040);
-       nv_mthd(priv, 0x9097, 0x0778, 0x00009080);
-       nv_mthd(priv, 0x9097, 0x0780, 0x39291909);
-       nv_mthd(priv, 0x9097, 0x0784, 0x79695949);
-       nv_mthd(priv, 0x9097, 0x0788, 0xb9a99989);
-       nv_mthd(priv, 0x9097, 0x078c, 0xf9e9d9c9);
-       nv_mthd(priv, 0x9097, 0x07d0, 0x30201000);
-       nv_mthd(priv, 0x9097, 0x07d4, 0x70605040);
-       nv_mthd(priv, 0x9097, 0x07d8, 0x00009080);
-       nv_mthd(priv, 0x9097, 0x037c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x0740, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0744, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x2600, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1918, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x191c, 0x00000900);
-       nv_mthd(priv, 0x9097, 0x1920, 0x00000405);
-       nv_mthd(priv, 0x9097, 0x1308, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1924, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x13ac, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x192c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x193c, 0x00002c1c);
-       nv_mthd(priv, 0x9097, 0x0d7c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0f8c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x02c0, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1510, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1940, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ff4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0ff8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x194c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1950, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1968, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1590, 0x0000003f);
-       nv_mthd(priv, 0x9097, 0x07e8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x07ec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x07f0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x07f4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x196c, 0x00000011);
-       nv_mthd(priv, 0x9097, 0x197c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0fcc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0fd0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x02d8, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x1980, 0x00000080);
-       nv_mthd(priv, 0x9097, 0x1504, 0x00000080);
-       nv_mthd(priv, 0x9097, 0x1984, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0300, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x13a8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x12ec, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1310, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1314, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1380, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1384, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1388, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x138c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1390, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1394, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x139c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1398, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1594, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1598, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x159c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x15a0, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x15a4, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x0f54, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0f58, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0f5c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x19bc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0f9c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0fa0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x12cc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x12e8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x130c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1360, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1364, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1368, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x136c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1370, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1374, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1378, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x137c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x133c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1340, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1344, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1348, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x134c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1350, 0x00000002);
-       nv_mthd(priv, 0x9097, 0x1358, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x12e4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x131c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1320, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1324, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1328, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x19c0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1140, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x19c4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x19c8, 0x00001500);
-       nv_mthd(priv, 0x9097, 0x135c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0f90, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x19e0, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x19e4, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x19e8, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x19ec, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x19f0, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x19f4, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x19f8, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x19fc, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x19cc, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x15b8, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1a00, 0x00001111);
-       nv_mthd(priv, 0x9097, 0x1a04, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1a08, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1a0c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1a10, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1a14, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1a18, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1a1c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d6c, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x0d70, 0xffff0000);
-       nv_mthd(priv, 0x9097, 0x10f8, 0x00001010);
-       nv_mthd(priv, 0x9097, 0x0d80, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d84, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d88, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d8c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0d90, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0da0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1508, 0x80000000);
-       nv_mthd(priv, 0x9097, 0x150c, 0x40000000);
-       nv_mthd(priv, 0x9097, 0x1668, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0318, 0x00000008);
-       nv_mthd(priv, 0x9097, 0x031c, 0x00000008);
-       nv_mthd(priv, 0x9097, 0x0d9c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x07dc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x074c, 0x00000055);
-       nv_mthd(priv, 0x9097, 0x1420, 0x00000003);
-       nv_mthd(priv, 0x9097, 0x17bc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x17c0, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x17c4, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1008, 0x00000008);
-       nv_mthd(priv, 0x9097, 0x100c, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x1010, 0x0000012c);
-       nv_mthd(priv, 0x9097, 0x0d60, 0x00000040);
-       nv_mthd(priv, 0x9097, 0x075c, 0x00000003);
-       nv_mthd(priv, 0x9097, 0x1018, 0x00000020);
-       nv_mthd(priv, 0x9097, 0x101c, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1020, 0x00000020);
-       nv_mthd(priv, 0x9097, 0x1024, 0x00000001);
-       nv_mthd(priv, 0x9097, 0x1444, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x1448, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x144c, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0360, 0x20164010);
-       nv_mthd(priv, 0x9097, 0x0364, 0x00000020);
-       nv_mthd(priv, 0x9097, 0x0368, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0de4, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0204, 0x00000006);
-       nv_mthd(priv, 0x9097, 0x0208, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x02cc, 0x003fffff);
-       nv_mthd(priv, 0x9097, 0x02d0, 0x00000c48);
-       nv_mthd(priv, 0x9097, 0x1220, 0x00000005);
-       nv_mthd(priv, 0x9097, 0x0fdc, 0x00000000);
-       nv_mthd(priv, 0x9097, 0x0f98, 0x00300008);
-       nv_mthd(priv, 0x9097, 0x1284, 0x04000080);
-       nv_mthd(priv, 0x9097, 0x1450, 0x00300008);
-       nv_mthd(priv, 0x9097, 0x1454, 0x04000080);
-       nv_mthd(priv, 0x9097, 0x0214, 0x00000000);
-       /* in trace, right after 0x90c0, not here */
-       nv_mthd(priv, 0x9097, 0x3410, 0x80002006);
 }
 
-static void
-nvc0_grctx_generate_9197(struct nvc0_graph_priv *priv)
+void
+nvc0_grctx_generate_r406028(struct nvc0_graph_priv *priv)
 {
-       u32 fermi = nvc0_graph_class(priv);
-       u32 mthd;
-
-       if (fermi == 0x9197) {
-               for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4)
-                       nv_mthd(priv, 0x9197, mthd, 0x00000000);
+       u32 tmp[GPC_MAX / 8] = {}, i = 0;
+       for (i = 0; i < priv->gpc_nr; i++)
+               tmp[i / 8] |= priv->tpc_nr[i] << ((i % 8) * 4);
+       for (i = 0; i < 4; i++) {
+               nv_wr32(priv, 0x406028 + (i * 4), tmp[i]);
+               nv_wr32(priv, 0x405870 + (i * 4), tmp[i]);
        }
-       nv_mthd(priv, 0x9197, 0x02e4, 0x0000b001);
 }
 
-static void
-nvc0_grctx_generate_9297(struct nvc0_graph_priv *priv)
+void
+nvc0_grctx_generate_r4060a8(struct nvc0_graph_priv *priv)
 {
-       u32 fermi = nvc0_graph_class(priv);
-       u32 mthd;
-
-       if (fermi == 0x9297) {
-               for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4)
-                       nv_mthd(priv, 0x9297, mthd, 0x00000000);
+       u8  tpcnr[GPC_MAX], data[TPC_MAX];
+       int gpc, tpc, i;
+
+       memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
+       memset(data, 0x1f, sizeof(data));
+
+       gpc = -1;
+       for (tpc = 0; tpc < priv->tpc_total; tpc++) {
+               do {
+                       gpc = (gpc + 1) % priv->gpc_nr;
+               } while (!tpcnr[gpc]);
+               tpcnr[gpc]--;
+               data[tpc] = gpc;
        }
-       nv_mthd(priv, 0x9297, 0x036c, 0x00000000);
-       nv_mthd(priv, 0x9297, 0x0370, 0x00000000);
-       nv_mthd(priv, 0x9297, 0x07a4, 0x00000000);
-       nv_mthd(priv, 0x9297, 0x07a8, 0x00000000);
-       nv_mthd(priv, 0x9297, 0x0374, 0x00000000);
-       nv_mthd(priv, 0x9297, 0x0378, 0x00000020);
-}
 
-static void
-nvc0_grctx_generate_902d(struct nvc0_graph_priv *priv)
-{
-       nv_mthd(priv, 0x902d, 0x0200, 0x000000cf);
-       nv_mthd(priv, 0x902d, 0x0204, 0x00000001);
-       nv_mthd(priv, 0x902d, 0x0208, 0x00000020);
-       nv_mthd(priv, 0x902d, 0x020c, 0x00000001);
-       nv_mthd(priv, 0x902d, 0x0210, 0x00000000);
-       nv_mthd(priv, 0x902d, 0x0214, 0x00000080);
-       nv_mthd(priv, 0x902d, 0x0218, 0x00000100);
-       nv_mthd(priv, 0x902d, 0x021c, 0x00000100);
-       nv_mthd(priv, 0x902d, 0x0220, 0x00000000);
-       nv_mthd(priv, 0x902d, 0x0224, 0x00000000);
-       nv_mthd(priv, 0x902d, 0x0230, 0x000000cf);
-       nv_mthd(priv, 0x902d, 0x0234, 0x00000001);
-       nv_mthd(priv, 0x902d, 0x0238, 0x00000020);
-       nv_mthd(priv, 0x902d, 0x023c, 0x00000001);
-       nv_mthd(priv, 0x902d, 0x0244, 0x00000080);
-       nv_mthd(priv, 0x902d, 0x0248, 0x00000100);
-       nv_mthd(priv, 0x902d, 0x024c, 0x00000100);
-}
-
-static void
-nvc0_grctx_generate_9039(struct nvc0_graph_priv *priv)
-{
-       nv_mthd(priv, 0x9039, 0x030c, 0x00000000);
-       nv_mthd(priv, 0x9039, 0x0310, 0x00000000);
-       nv_mthd(priv, 0x9039, 0x0314, 0x00000000);
-       nv_mthd(priv, 0x9039, 0x0320, 0x00000000);
-       nv_mthd(priv, 0x9039, 0x0238, 0x00000000);
-       nv_mthd(priv, 0x9039, 0x023c, 0x00000000);
-       nv_mthd(priv, 0x9039, 0x0318, 0x00000000);
-       nv_mthd(priv, 0x9039, 0x031c, 0x00000000);
+       for (i = 0; i < 4; i++)
+               nv_wr32(priv, 0x4060a8 + (i * 4), ((u32 *)data)[i]);
 }
 
-static void
-nvc0_grctx_generate_90c0(struct nvc0_graph_priv *priv)
+void
+nvc0_grctx_generate_r418bb8(struct nvc0_graph_priv *priv)
 {
-       int i;
-
-       for (i = 0; nv_device(priv)->chipset >= 0xd0 && i < 4; i++) {
-               nv_mthd(priv, 0x90c0, 0x2700 + (i * 0x40), 0x00000000);
-               nv_mthd(priv, 0x90c0, 0x2720 + (i * 0x40), 0x00000000);
-               nv_mthd(priv, 0x90c0, 0x2704 + (i * 0x40), 0x00000000);
-               nv_mthd(priv, 0x90c0, 0x2724 + (i * 0x40), 0x00000000);
-               nv_mthd(priv, 0x90c0, 0x2708 + (i * 0x40), 0x00000000);
-               nv_mthd(priv, 0x90c0, 0x2728 + (i * 0x40), 0x00000000);
+       u32 data[6] = {}, data2[2] = {};
+       u8  tpcnr[GPC_MAX];
+       u8  shift, ntpcv;
+       int gpc, tpc, i;
+
+       /* calculate first set of magics */
+       memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
+
+       gpc = -1;
+       for (tpc = 0; tpc < priv->tpc_total; tpc++) {
+               do {
+                       gpc = (gpc + 1) % priv->gpc_nr;
+               } while (!tpcnr[gpc]);
+               tpcnr[gpc]--;
+
+               data[tpc / 6] |= gpc << ((tpc % 6) * 5);
        }
-       nv_mthd(priv, 0x90c0, 0x270c, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x272c, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x274c, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x276c, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x278c, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x27ac, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x27cc, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x27ec, 0x00000000);
-       for (i = 0; nv_device(priv)->chipset >= 0xd0 && i < 4; i++) {
-               nv_mthd(priv, 0x90c0, 0x2710 + (i * 0x40), 0x00014000);
-               nv_mthd(priv, 0x90c0, 0x2730 + (i * 0x40), 0x00014000);
-               nv_mthd(priv, 0x90c0, 0x2714 + (i * 0x40), 0x00000040);
-               nv_mthd(priv, 0x90c0, 0x2734 + (i * 0x40), 0x00000040);
-       }
-       nv_mthd(priv, 0x90c0, 0x030c, 0x00000001);
-       nv_mthd(priv, 0x90c0, 0x1944, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x0758, 0x00000100);
-       nv_mthd(priv, 0x90c0, 0x02c4, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x0790, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x0794, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x0798, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x079c, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x07a0, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x077c, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x0204, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x0208, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x020c, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x0214, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x024c, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x0d94, 0x00000001);
-       nv_mthd(priv, 0x90c0, 0x1608, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x160c, 0x00000000);
-       nv_mthd(priv, 0x90c0, 0x1664, 0x00000000);
-}
 
-static void
-nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv)
-{
-       int i;
+       for (; tpc < 32; tpc++)
+               data[tpc / 6] |= 7 << ((tpc % 6) * 5);
 
-       nv_wr32(priv, 0x404004, 0x00000000);
-       nv_wr32(priv, 0x404008, 0x00000000);
-       nv_wr32(priv, 0x40400c, 0x00000000);
-       nv_wr32(priv, 0x404010, 0x00000000);
-       nv_wr32(priv, 0x404014, 0x00000000);
-       nv_wr32(priv, 0x404018, 0x00000000);
-       nv_wr32(priv, 0x40401c, 0x00000000);
-       nv_wr32(priv, 0x404020, 0x00000000);
-       nv_wr32(priv, 0x404024, 0x00000000);
-       nv_wr32(priv, 0x404028, 0x00000000);
-       nv_wr32(priv, 0x40402c, 0x00000000);
-       nv_wr32(priv, 0x404044, 0x00000000);
-       nv_wr32(priv, 0x404094, 0x00000000);
-       nv_wr32(priv, 0x404098, 0x00000000);
-       nv_wr32(priv, 0x40409c, 0x00000000);
-       nv_wr32(priv, 0x4040a0, 0x00000000);
-       nv_wr32(priv, 0x4040a4, 0x00000000);
-       nv_wr32(priv, 0x4040a8, 0x00000000);
-       nv_wr32(priv, 0x4040ac, 0x00000000);
-       nv_wr32(priv, 0x4040b0, 0x00000000);
-       nv_wr32(priv, 0x4040b4, 0x00000000);
-       nv_wr32(priv, 0x4040b8, 0x00000000);
-       nv_wr32(priv, 0x4040bc, 0x00000000);
-       nv_wr32(priv, 0x4040c0, 0x00000000);
-       nv_wr32(priv, 0x4040c4, 0x00000000);
-       nv_wr32(priv, 0x4040c8, 0xf0000087);
-       nv_wr32(priv, 0x4040d4, 0x00000000);
-       nv_wr32(priv, 0x4040d8, 0x00000000);
-       nv_wr32(priv, 0x4040dc, 0x00000000);
-       nv_wr32(priv, 0x4040e0, 0x00000000);
-       nv_wr32(priv, 0x4040e4, 0x00000000);
-       nv_wr32(priv, 0x4040e8, 0x00001000);
-       nv_wr32(priv, 0x4040f8, 0x00000000);
-       nv_wr32(priv, 0x404130, 0x00000000);
-       nv_wr32(priv, 0x404134, 0x00000000);
-       nv_wr32(priv, 0x404138, 0x20000040);
-       nv_wr32(priv, 0x404150, 0x0000002e);
-       nv_wr32(priv, 0x404154, 0x00000400);
-       nv_wr32(priv, 0x404158, 0x00000200);
-       nv_wr32(priv, 0x404164, 0x00000055);
-       nv_wr32(priv, 0x404168, 0x00000000);
-       nv_wr32(priv, 0x404174, 0x00000000);
-       nv_wr32(priv, 0x404178, 0x00000000);
-       nv_wr32(priv, 0x40417c, 0x00000000);
-       for (i = 0; i < 8; i++)
-               nv_wr32(priv, 0x404200 + (i * 4), 0x00000000); /* subc */
-}
-
-static void
-nvc0_grctx_generate_macro(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x404404, 0x00000000);
-       nv_wr32(priv, 0x404408, 0x00000000);
-       nv_wr32(priv, 0x40440c, 0x00000000);
-       nv_wr32(priv, 0x404410, 0x00000000);
-       nv_wr32(priv, 0x404414, 0x00000000);
-       nv_wr32(priv, 0x404418, 0x00000000);
-       nv_wr32(priv, 0x40441c, 0x00000000);
-       nv_wr32(priv, 0x404420, 0x00000000);
-       nv_wr32(priv, 0x404424, 0x00000000);
-       nv_wr32(priv, 0x404428, 0x00000000);
-       nv_wr32(priv, 0x40442c, 0x00000000);
-       nv_wr32(priv, 0x404430, 0x00000000);
-       nv_wr32(priv, 0x404434, 0x00000000);
-       nv_wr32(priv, 0x404438, 0x00000000);
-       nv_wr32(priv, 0x404460, 0x00000000);
-       nv_wr32(priv, 0x404464, 0x00000000);
-       nv_wr32(priv, 0x404468, 0x00ffffff);
-       nv_wr32(priv, 0x40446c, 0x00000000);
-       nv_wr32(priv, 0x404480, 0x00000001);
-       nv_wr32(priv, 0x404498, 0x00000001);
-}
-
-static void
-nvc0_grctx_generate_m2mf(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x404604, 0x00000015);
-       nv_wr32(priv, 0x404608, 0x00000000);
-       nv_wr32(priv, 0x40460c, 0x00002e00);
-       nv_wr32(priv, 0x404610, 0x00000100);
-       nv_wr32(priv, 0x404618, 0x00000000);
-       nv_wr32(priv, 0x40461c, 0x00000000);
-       nv_wr32(priv, 0x404620, 0x00000000);
-       nv_wr32(priv, 0x404624, 0x00000000);
-       nv_wr32(priv, 0x404628, 0x00000000);
-       nv_wr32(priv, 0x40462c, 0x00000000);
-       nv_wr32(priv, 0x404630, 0x00000000);
-       nv_wr32(priv, 0x404634, 0x00000000);
-       nv_wr32(priv, 0x404638, 0x00000004);
-       nv_wr32(priv, 0x40463c, 0x00000000);
-       nv_wr32(priv, 0x404640, 0x00000000);
-       nv_wr32(priv, 0x404644, 0x00000000);
-       nv_wr32(priv, 0x404648, 0x00000000);
-       nv_wr32(priv, 0x40464c, 0x00000000);
-       nv_wr32(priv, 0x404650, 0x00000000);
-       nv_wr32(priv, 0x404654, 0x00000000);
-       nv_wr32(priv, 0x404658, 0x00000000);
-       nv_wr32(priv, 0x40465c, 0x007f0100);
-       nv_wr32(priv, 0x404660, 0x00000000);
-       nv_wr32(priv, 0x404664, 0x00000000);
-       nv_wr32(priv, 0x404668, 0x00000000);
-       nv_wr32(priv, 0x40466c, 0x00000000);
-       nv_wr32(priv, 0x404670, 0x00000000);
-       nv_wr32(priv, 0x404674, 0x00000000);
-       nv_wr32(priv, 0x404678, 0x00000000);
-       nv_wr32(priv, 0x40467c, 0x00000002);
-       nv_wr32(priv, 0x404680, 0x00000000);
-       nv_wr32(priv, 0x404684, 0x00000000);
-       nv_wr32(priv, 0x404688, 0x00000000);
-       nv_wr32(priv, 0x40468c, 0x00000000);
-       nv_wr32(priv, 0x404690, 0x00000000);
-       nv_wr32(priv, 0x404694, 0x00000000);
-       nv_wr32(priv, 0x404698, 0x00000000);
-       nv_wr32(priv, 0x40469c, 0x00000000);
-       nv_wr32(priv, 0x4046a0, 0x007f0080);
-       nv_wr32(priv, 0x4046a4, 0x00000000);
-       nv_wr32(priv, 0x4046a8, 0x00000000);
-       nv_wr32(priv, 0x4046ac, 0x00000000);
-       nv_wr32(priv, 0x4046b0, 0x00000000);
-       nv_wr32(priv, 0x4046b4, 0x00000000);
-       nv_wr32(priv, 0x4046b8, 0x00000000);
-       nv_wr32(priv, 0x4046bc, 0x00000000);
-       nv_wr32(priv, 0x4046c0, 0x00000000);
-       nv_wr32(priv, 0x4046c4, 0x00000000);
-       nv_wr32(priv, 0x4046c8, 0x00000000);
-       nv_wr32(priv, 0x4046cc, 0x00000000);
-       nv_wr32(priv, 0x4046d0, 0x00000000);
-       nv_wr32(priv, 0x4046d4, 0x00000000);
-       nv_wr32(priv, 0x4046d8, 0x00000000);
-       nv_wr32(priv, 0x4046dc, 0x00000000);
-       nv_wr32(priv, 0x4046e0, 0x00000000);
-       nv_wr32(priv, 0x4046e4, 0x00000000);
-       nv_wr32(priv, 0x4046e8, 0x00000000);
-       nv_wr32(priv, 0x4046f0, 0x00000000);
-       nv_wr32(priv, 0x4046f4, 0x00000000);
-}
+       /* and the second... */
+       shift = 0;
+       ntpcv = priv->tpc_total;
+       while (!(ntpcv & (1 << 4))) {
+               ntpcv <<= 1;
+               shift++;
+       }
 
-static void
-nvc0_grctx_generate_unk47xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x404700, 0x00000000);
-       nv_wr32(priv, 0x404704, 0x00000000);
-       nv_wr32(priv, 0x404708, 0x00000000);
-       nv_wr32(priv, 0x40470c, 0x00000000);
-       nv_wr32(priv, 0x404710, 0x00000000);
-       nv_wr32(priv, 0x404714, 0x00000000);
-       nv_wr32(priv, 0x404718, 0x00000000);
-       nv_wr32(priv, 0x40471c, 0x00000000);
-       nv_wr32(priv, 0x404720, 0x00000000);
-       nv_wr32(priv, 0x404724, 0x00000000);
-       nv_wr32(priv, 0x404728, 0x00000000);
-       nv_wr32(priv, 0x40472c, 0x00000000);
-       nv_wr32(priv, 0x404730, 0x00000000);
-       nv_wr32(priv, 0x404734, 0x00000100);
-       nv_wr32(priv, 0x404738, 0x00000000);
-       nv_wr32(priv, 0x40473c, 0x00000000);
-       nv_wr32(priv, 0x404740, 0x00000000);
-       nv_wr32(priv, 0x404744, 0x00000000);
-       nv_wr32(priv, 0x404748, 0x00000000);
-       nv_wr32(priv, 0x40474c, 0x00000000);
-       nv_wr32(priv, 0x404750, 0x00000000);
-       nv_wr32(priv, 0x404754, 0x00000000);
-}
+       data2[0]  = (ntpcv << 16);
+       data2[0] |= (shift << 21);
+       data2[0] |= (((1 << (0 + 5)) % ntpcv) << 24);
+       for (i = 1; i < 7; i++)
+               data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5);
 
-static void
-nvc0_grctx_generate_shaders(struct nvc0_graph_priv *priv)
-{
+       /* GPC_BROADCAST */
+       nv_wr32(priv, 0x418bb8, (priv->tpc_total << 8) |
+                                priv->magic_not_rop_nr);
+       for (i = 0; i < 6; i++)
+               nv_wr32(priv, 0x418b08 + (i * 4), data[i]);
 
-       if (nv_device(priv)->chipset >= 0xd0) {
-               nv_wr32(priv, 0x405800, 0x0f8000bf);
-               nv_wr32(priv, 0x405830, 0x02180218);
-               nv_wr32(priv, 0x405834, 0x08000000);
-       } else
-       if (nv_device(priv)->chipset == 0xc1) {
-               nv_wr32(priv, 0x405800, 0x0f8000bf);
-               nv_wr32(priv, 0x405830, 0x02180218);
-               nv_wr32(priv, 0x405834, 0x00000000);
-       } else {
-               nv_wr32(priv, 0x405800, 0x078000bf);
-               nv_wr32(priv, 0x405830, 0x02180000);
-               nv_wr32(priv, 0x405834, 0x00000000);
-       }
-       nv_wr32(priv, 0x405838, 0x00000000);
-       nv_wr32(priv, 0x405854, 0x00000000);
-       nv_wr32(priv, 0x405870, 0x00000001);
-       nv_wr32(priv, 0x405874, 0x00000001);
-       nv_wr32(priv, 0x405878, 0x00000001);
-       nv_wr32(priv, 0x40587c, 0x00000001);
-       nv_wr32(priv, 0x405a00, 0x00000000);
-       nv_wr32(priv, 0x405a04, 0x00000000);
-       nv_wr32(priv, 0x405a18, 0x00000000);
+       /* GPC_BROADCAST.TP_BROADCAST */
+       nv_wr32(priv, 0x419bd0, (priv->tpc_total << 8) |
+                                priv->magic_not_rop_nr | data2[0]);
+       nv_wr32(priv, 0x419be4, data2[1]);
+       for (i = 0; i < 6; i++)
+               nv_wr32(priv, 0x419b00 + (i * 4), data[i]);
+
+       /* UNK78xx */
+       nv_wr32(priv, 0x4078bc, (priv->tpc_total << 8) |
+                                priv->magic_not_rop_nr);
+       for (i = 0; i < 6; i++)
+               nv_wr32(priv, 0x40780c + (i * 4), data[i]);
 }
 
-static void
-nvc0_grctx_generate_unk60xx(struct nvc0_graph_priv *priv)
+void
+nvc0_grctx_generate_r406800(struct nvc0_graph_priv *priv)
 {
-       nv_wr32(priv, 0x406020, 0x000103c1);
-       nv_wr32(priv, 0x406028, 0x00000001);
-       nv_wr32(priv, 0x40602c, 0x00000001);
-       nv_wr32(priv, 0x406030, 0x00000001);
-       nv_wr32(priv, 0x406034, 0x00000001);
-}
+       u64 tpc_mask = 0, tpc_set = 0;
+       u8  tpcnr[GPC_MAX];
+       int gpc, tpc;
+       int i, a, b;
+
+       memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
+       for (gpc = 0; gpc < priv->gpc_nr; gpc++)
+               tpc_mask |= ((1ULL << priv->tpc_nr[gpc]) - 1) << (gpc * 8);
+
+       for (i = 0, gpc = -1, b = -1; i < 32; i++) {
+               a = (i * (priv->tpc_total - 1)) / 32;
+               if (a != b) {
+                       b = a;
+                       do {
+                               gpc = (gpc + 1) % priv->gpc_nr;
+                       } while (!tpcnr[gpc]);
+                       tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--;
 
-static void
-nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv)
-{
+                       tpc_set |= 1 << ((gpc * 8) + tpc);
+               }
 
-       nv_wr32(priv, 0x4064a8, 0x00000000);
-       nv_wr32(priv, 0x4064ac, 0x00003fff);
-       nv_wr32(priv, 0x4064b4, 0x00000000);
-       nv_wr32(priv, 0x4064b8, 0x00000000);
-       if (nv_device(priv)->chipset >= 0xd0)
-               nv_wr32(priv, 0x4064bc, 0x00000000);
-       if (nv_device(priv)->chipset == 0xc1 ||
-           nv_device(priv)->chipset >= 0xd0) {
-               nv_wr32(priv, 0x4064c0, 0x80140078);
-               nv_wr32(priv, 0x4064c4, 0x0086ffff);
+               nv_wr32(priv, 0x406800 + (i * 0x20), lower_32_bits(tpc_set));
+               nv_wr32(priv, 0x406c00 + (i * 0x20), lower_32_bits(tpc_set ^ tpc_mask));
+               if (priv->gpc_nr > 4) {
+                       nv_wr32(priv, 0x406804 + (i * 0x20), upper_32_bits(tpc_set));
+                       nv_wr32(priv, 0x406c04 + (i * 0x20), upper_32_bits(tpc_set ^ tpc_mask));
+               }
        }
 }
 
-static void
-nvc0_grctx_generate_tpbus(struct nvc0_graph_priv *priv)
+void
+nvc0_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
 {
-       nv_wr32(priv, 0x407804, 0x00000023);
-       nv_wr32(priv, 0x40780c, 0x0a418820);
-       nv_wr32(priv, 0x407810, 0x062080e6);
-       nv_wr32(priv, 0x407814, 0x020398a4);
-       nv_wr32(priv, 0x407818, 0x0e629062);
-       nv_wr32(priv, 0x40781c, 0x0a418820);
-       nv_wr32(priv, 0x407820, 0x000000e6);
-       nv_wr32(priv, 0x4078bc, 0x00000103);
-}
+       struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass;
+       int i;
 
-static void
-nvc0_grctx_generate_ccache(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x408000, 0x00000000);
-       nv_wr32(priv, 0x408004, 0x00000000);
-       nv_wr32(priv, 0x408008, 0x00000018);
-       nv_wr32(priv, 0x40800c, 0x00000000);
-       nv_wr32(priv, 0x408010, 0x00000000);
-       nv_wr32(priv, 0x408014, 0x00000069);
-       nv_wr32(priv, 0x408018, 0xe100e100);
-       nv_wr32(priv, 0x408064, 0x00000000);
-}
+       nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
 
-static void
-nvc0_grctx_generate_rop(struct nvc0_graph_priv *priv)
-{
-       int chipset = nv_device(priv)->chipset;
-
-       /* ROPC_BROADCAST */
-       nv_wr32(priv, 0x408800, 0x02802a3c);
-       nv_wr32(priv, 0x408804, 0x00000040);
-       if (chipset >= 0xd0) {
-               nv_wr32(priv, 0x408808, 0x1043e005);
-               nv_wr32(priv, 0x408900, 0x3080b801);
-               nv_wr32(priv, 0x408904, 0x1043e005);
-               nv_wr32(priv, 0x408908, 0x00c8102f);
-       } else
-       if (chipset == 0xc1) {
-               nv_wr32(priv, 0x408808, 0x1003e005);
-               nv_wr32(priv, 0x408900, 0x3080b801);
-               nv_wr32(priv, 0x408904, 0x62000001);
-               nv_wr32(priv, 0x408908, 0x00c80929);
-       } else {
-               nv_wr32(priv, 0x408808, 0x0003e00d);
-               nv_wr32(priv, 0x408900, 0x3080b801);
-               nv_wr32(priv, 0x408904, 0x02000001);
-               nv_wr32(priv, 0x408908, 0x00c80929);
-       }
-       nv_wr32(priv, 0x40890c, 0x00000000);
-       nv_wr32(priv, 0x408980, 0x0000011d);
-}
+       for (i = 0; oclass->hub[i]; i++)
+               nvc0_graph_mmio(priv, oclass->hub[i]);
+       for (i = 0; oclass->gpc[i]; i++)
+               nvc0_graph_mmio(priv, oclass->gpc[i]);
 
-static void
-nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv)
-{
-       int chipset = nv_device(priv)->chipset;
-       int i;
+       nv_wr32(priv, 0x404154, 0x00000000);
 
-       /* GPC_BROADCAST */
-       nv_wr32(priv, 0x418380, 0x00000016);
-       nv_wr32(priv, 0x418400, 0x38004e00);
-       nv_wr32(priv, 0x418404, 0x71e0ffff);
-       nv_wr32(priv, 0x418408, 0x00000000);
-       nv_wr32(priv, 0x41840c, 0x00001008);
-       nv_wr32(priv, 0x418410, 0x0fff0fff);
-       nv_wr32(priv, 0x418414, chipset < 0xd0 ? 0x00200fff : 0x02200fff);
-       nv_wr32(priv, 0x418450, 0x00000000);
-       nv_wr32(priv, 0x418454, 0x00000000);
-       nv_wr32(priv, 0x418458, 0x00000000);
-       nv_wr32(priv, 0x41845c, 0x00000000);
-       nv_wr32(priv, 0x418460, 0x00000000);
-       nv_wr32(priv, 0x418464, 0x00000000);
-       nv_wr32(priv, 0x418468, 0x00000001);
-       nv_wr32(priv, 0x41846c, 0x00000000);
-       nv_wr32(priv, 0x418470, 0x00000000);
-       nv_wr32(priv, 0x418600, 0x0000001f);
-       nv_wr32(priv, 0x418684, 0x0000000f);
-       nv_wr32(priv, 0x418700, 0x00000002);
-       nv_wr32(priv, 0x418704, 0x00000080);
-       nv_wr32(priv, 0x418708, 0x00000000);
-       nv_wr32(priv, 0x41870c, chipset < 0xd0 ? 0x07c80000 : 0x00000000);
-       nv_wr32(priv, 0x418710, 0x00000000);
-       nv_wr32(priv, 0x418800, chipset < 0xd0 ? 0x0006860a : 0x7006860a);
-       nv_wr32(priv, 0x418808, 0x00000000);
-       nv_wr32(priv, 0x41880c, 0x00000000);
-       nv_wr32(priv, 0x418810, 0x00000000);
-       nv_wr32(priv, 0x418828, 0x00008442);
-       if (chipset == 0xc1 || chipset >= 0xd0)
-               nv_wr32(priv, 0x418830, 0x10000001);
-       else
-               nv_wr32(priv, 0x418830, 0x00000001);
-       nv_wr32(priv, 0x4188d8, 0x00000008);
-       nv_wr32(priv, 0x4188e0, 0x01000000);
-       nv_wr32(priv, 0x4188e8, 0x00000000);
-       nv_wr32(priv, 0x4188ec, 0x00000000);
-       nv_wr32(priv, 0x4188f0, 0x00000000);
-       nv_wr32(priv, 0x4188f4, 0x00000000);
-       nv_wr32(priv, 0x4188f8, 0x00000000);
-       if (chipset >= 0xd0)
-               nv_wr32(priv, 0x4188fc, 0x20100008);
-       else if (chipset == 0xc1)
-               nv_wr32(priv, 0x4188fc, 0x00100018);
-       else
-               nv_wr32(priv, 0x4188fc, 0x00100000);
-       nv_wr32(priv, 0x41891c, 0x00ff00ff);
-       nv_wr32(priv, 0x418924, 0x00000000);
-       nv_wr32(priv, 0x418928, 0x00ffff00);
-       nv_wr32(priv, 0x41892c, 0x0000ff00);
-       for (i = 0; i < 8; i++) {
-               nv_wr32(priv, 0x418a00 + (i * 0x20), 0x00000000);
-               nv_wr32(priv, 0x418a04 + (i * 0x20), 0x00000000);
-               nv_wr32(priv, 0x418a08 + (i * 0x20), 0x00000000);
-               nv_wr32(priv, 0x418a0c + (i * 0x20), 0x00010000);
-               nv_wr32(priv, 0x418a10 + (i * 0x20), 0x00000000);
-               nv_wr32(priv, 0x418a14 + (i * 0x20), 0x00000000);
-               nv_wr32(priv, 0x418a18 + (i * 0x20), 0x00000000);
-       }
-       nv_wr32(priv, 0x418b00, chipset < 0xd0 ? 0x00000000 : 0x00000006);
-       nv_wr32(priv, 0x418b08, 0x0a418820);
-       nv_wr32(priv, 0x418b0c, 0x062080e6);
-       nv_wr32(priv, 0x418b10, 0x020398a4);
-       nv_wr32(priv, 0x418b14, 0x0e629062);
-       nv_wr32(priv, 0x418b18, 0x0a418820);
-       nv_wr32(priv, 0x418b1c, 0x000000e6);
-       nv_wr32(priv, 0x418bb8, 0x00000103);
-       nv_wr32(priv, 0x418c08, 0x00000001);
-       nv_wr32(priv, 0x418c10, 0x00000000);
-       nv_wr32(priv, 0x418c14, 0x00000000);
-       nv_wr32(priv, 0x418c18, 0x00000000);
-       nv_wr32(priv, 0x418c1c, 0x00000000);
-       nv_wr32(priv, 0x418c20, 0x00000000);
-       nv_wr32(priv, 0x418c24, 0x00000000);
-       nv_wr32(priv, 0x418c28, 0x00000000);
-       nv_wr32(priv, 0x418c2c, 0x00000000);
-       if (chipset == 0xc1 || chipset >= 0xd0)
-               nv_wr32(priv, 0x418c6c, 0x00000001);
-       nv_wr32(priv, 0x418c80, 0x20200004);
-       nv_wr32(priv, 0x418c8c, 0x00000001);
-       nv_wr32(priv, 0x419000, 0x00000780);
-       nv_wr32(priv, 0x419004, 0x00000000);
-       nv_wr32(priv, 0x419008, 0x00000000);
-       nv_wr32(priv, 0x419014, 0x00000004);
-}
+       oclass->mods(priv, info);
+       oclass->unkn(priv);
 
-static void
-nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv)
-{
-       int chipset = nv_device(priv)->chipset;
+       nvc0_grctx_generate_tpcid(priv);
+       nvc0_grctx_generate_r406028(priv);
+       nvc0_grctx_generate_r4060a8(priv);
+       nvc0_grctx_generate_r418bb8(priv);
+       nvc0_grctx_generate_r406800(priv);
 
-       /* GPC_BROADCAST.TP_BROADCAST */
-       nv_wr32(priv, 0x419818, 0x00000000);
-       nv_wr32(priv, 0x41983c, 0x00038bc7);
-       nv_wr32(priv, 0x419848, 0x00000000);
-       if (chipset == 0xc1 || chipset >= 0xd0)
-               nv_wr32(priv, 0x419864, 0x00000129);
-       else
-               nv_wr32(priv, 0x419864, 0x0000012a);
-       nv_wr32(priv, 0x419888, 0x00000000);
-       nv_wr32(priv, 0x419a00, 0x000001f0);
-       nv_wr32(priv, 0x419a04, 0x00000001);
-       nv_wr32(priv, 0x419a08, 0x00000023);
-       nv_wr32(priv, 0x419a0c, 0x00020000);
-       nv_wr32(priv, 0x419a10, 0x00000000);
-       nv_wr32(priv, 0x419a14, 0x00000200);
-       nv_wr32(priv, 0x419a1c, 0x00000000);
-       nv_wr32(priv, 0x419a20, 0x00000800);
-       if (chipset >= 0xd0)
-               nv_wr32(priv, 0x00419ac4, 0x0017f440);
-       else if (chipset != 0xc0 && chipset != 0xc8)
-               nv_wr32(priv, 0x00419ac4, 0x0007f440);
-       nv_wr32(priv, 0x419b00, 0x0a418820);
-       nv_wr32(priv, 0x419b04, 0x062080e6);
-       nv_wr32(priv, 0x419b08, 0x020398a4);
-       nv_wr32(priv, 0x419b0c, 0x0e629062);
-       nv_wr32(priv, 0x419b10, 0x0a418820);
-       nv_wr32(priv, 0x419b14, 0x000000e6);
-       nv_wr32(priv, 0x419bd0, 0x00900103);
-       if (chipset == 0xc1 || chipset >= 0xd0)
-               nv_wr32(priv, 0x419be0, 0x00400001);
-       else
-               nv_wr32(priv, 0x419be0, 0x00000001);
-       nv_wr32(priv, 0x419be4, 0x00000000);
-       nv_wr32(priv, 0x419c00, chipset < 0xd0 ? 0x00000002 : 0x0000000a);
-       nv_wr32(priv, 0x419c04, 0x00000006);
-       nv_wr32(priv, 0x419c08, 0x00000002);
-       nv_wr32(priv, 0x419c20, 0x00000000);
-       if (nv_device(priv)->chipset >= 0xd0) {
-               nv_wr32(priv, 0x419c24, 0x00084210);
-               nv_wr32(priv, 0x419c28, 0x3cf3cf3c);
-               nv_wr32(priv, 0x419cb0, 0x00020048);
-       } else
-       if (chipset == 0xce || chipset == 0xcf) {
-               nv_wr32(priv, 0x419cb0, 0x00020048);
-       } else {
-               nv_wr32(priv, 0x419cb0, 0x00060048);
-       }
-       nv_wr32(priv, 0x419ce8, 0x00000000);
-       nv_wr32(priv, 0x419cf4, 0x00000183);
-       if (chipset == 0xc1 || chipset >= 0xd0)
-               nv_wr32(priv, 0x419d20, 0x12180000);
-       else
-               nv_wr32(priv, 0x419d20, 0x02180000);
-       nv_wr32(priv, 0x419d24, 0x00001fff);
-       if (chipset == 0xc1 || chipset >= 0xd0)
-               nv_wr32(priv, 0x419d44, 0x02180218);
-       nv_wr32(priv, 0x419e04, 0x00000000);
-       nv_wr32(priv, 0x419e08, 0x00000000);
-       nv_wr32(priv, 0x419e0c, 0x00000000);
-       nv_wr32(priv, 0x419e10, 0x00000002);
-       nv_wr32(priv, 0x419e44, 0x001beff2);
-       nv_wr32(priv, 0x419e48, 0x00000000);
-       nv_wr32(priv, 0x419e4c, 0x0000000f);
-       nv_wr32(priv, 0x419e50, 0x00000000);
-       nv_wr32(priv, 0x419e54, 0x00000000);
-       nv_wr32(priv, 0x419e58, 0x00000000);
-       nv_wr32(priv, 0x419e5c, 0x00000000);
-       nv_wr32(priv, 0x419e60, 0x00000000);
-       nv_wr32(priv, 0x419e64, 0x00000000);
-       nv_wr32(priv, 0x419e68, 0x00000000);
-       nv_wr32(priv, 0x419e6c, 0x00000000);
-       nv_wr32(priv, 0x419e70, 0x00000000);
-       nv_wr32(priv, 0x419e74, 0x00000000);
-       nv_wr32(priv, 0x419e78, 0x00000000);
-       nv_wr32(priv, 0x419e7c, 0x00000000);
-       nv_wr32(priv, 0x419e80, 0x00000000);
-       nv_wr32(priv, 0x419e84, 0x00000000);
-       nv_wr32(priv, 0x419e88, 0x00000000);
-       nv_wr32(priv, 0x419e8c, 0x00000000);
-       nv_wr32(priv, 0x419e90, 0x00000000);
-       nv_wr32(priv, 0x419e98, 0x00000000);
-       if (chipset != 0xc0 && chipset != 0xc8)
-               nv_wr32(priv, 0x419ee0, 0x00011110);
-       nv_wr32(priv, 0x419f50, 0x00000000);
-       nv_wr32(priv, 0x419f54, 0x00000000);
-       if (chipset != 0xc0 && chipset != 0xc8)
-               nv_wr32(priv, 0x419f58, 0x00000000);
+       nvc0_graph_icmd(priv, oclass->icmd);
+       nv_wr32(priv, 0x404154, 0x00000400);
+       nvc0_graph_mthd(priv, oclass->mthd);
+       nv_mask(priv, 0x000260, 0x00000001, 0x00000001);
 }
 
 int
 nvc0_grctx_generate(struct nvc0_graph_priv *priv)
 {
+       struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass;
+       struct nouveau_bar *bar = nouveau_bar(priv);
+       struct nouveau_gpuobj *chan;
        struct nvc0_grctx info;
-       int ret, i, gpc, tpc, id;
-       u32 fermi = nvc0_graph_class(priv);
-       u32 r000260, tmp;
+       int ret, i;
 
-       ret = nvc0_grctx_init(priv, &info);
-       if (ret)
+       /* allocate memory to for a "channel", which we'll use to generate
+        * the default context values
+        */
+       ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x80000 + priv->size,
+                                0x1000, NVOBJ_FLAG_ZERO_ALLOC, &chan);
+       if (ret) {
+               nv_error(priv, "failed to allocate channel memory, %d\n", ret);
                return ret;
-
-       r000260 = nv_rd32(priv, 0x000260);
-       nv_wr32(priv, 0x000260, r000260 & ~1);
-       nv_wr32(priv, 0x400208, 0x00000000);
-
-       nvc0_grctx_generate_dispatch(priv);
-       nvc0_grctx_generate_macro(priv);
-       nvc0_grctx_generate_m2mf(priv);
-       nvc0_grctx_generate_unk47xx(priv);
-       nvc0_grctx_generate_shaders(priv);
-       nvc0_grctx_generate_unk60xx(priv);
-       nvc0_grctx_generate_unk64xx(priv);
-       nvc0_grctx_generate_tpbus(priv);
-       nvc0_grctx_generate_ccache(priv);
-       nvc0_grctx_generate_rop(priv);
-       nvc0_grctx_generate_gpc(priv);
-       nvc0_grctx_generate_tp(priv);
-
-       nv_wr32(priv, 0x404154, 0x00000000);
-
-       /* generate per-context mmio list data */
-       mmio_data(0x002000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
-       mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
-       mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
-       mmio_list(0x408004, 0x00000000,  8, 0);
-       mmio_list(0x408008, 0x80000018,  0, 0);
-       mmio_list(0x40800c, 0x00000000,  8, 1);
-       mmio_list(0x408010, 0x80000000,  0, 0);
-       mmio_list(0x418810, 0x80000000, 12, 2);
-       mmio_list(0x419848, 0x10000000, 12, 2);
-       mmio_list(0x419004, 0x00000000,  8, 1);
-       mmio_list(0x419008, 0x00000000,  0, 0);
-       mmio_list(0x418808, 0x00000000,  8, 0);
-       mmio_list(0x41880c, 0x80000018,  0, 0);
-       if (nv_device(priv)->chipset != 0xc1) {
-               tmp = 0x02180000;
-               mmio_list(0x405830, tmp, 0, 0);
-               for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
-                       for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
-                               u32 reg = TPC_UNIT(gpc, tpc, 0x0520);
-                               mmio_list(reg, tmp, 0, 0);
-                               tmp += 0x0324;
-                       }
-               }
-       } else {
-               tmp = 0x02180000;
-               mmio_list(0x405830, 0x00000218 | tmp, 0, 0);
-               mmio_list(0x4064c4, 0x0086ffff, 0, 0);
-               for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
-                       for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
-                               u32 reg = TPC_UNIT(gpc, tpc, 0x0520);
-                               mmio_list(reg, 0x10000000 | tmp, 0, 0);
-                               tmp += 0x0324;
-                       }
-                       for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
-                               u32 reg = TPC_UNIT(gpc, tpc, 0x0544);
-                               mmio_list(reg, tmp, 0, 0);
-                               tmp += 0x0324;
-                       }
-               }
        }
 
-       for (tpc = 0, id = 0; tpc < 4; tpc++) {
-               for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
-                       if (tpc < priv->tpc_nr[gpc]) {
-                               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x698), id);
-                               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x4e8), id);
-                               nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id);
-                               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x088), id);
-                               id++;
-                       }
-
-                       nv_wr32(priv, GPC_UNIT(gpc, 0x0c08), priv->tpc_nr[gpc]);
-                       nv_wr32(priv, GPC_UNIT(gpc, 0x0c8c), priv->tpc_nr[gpc]);
-               }
-       }
-
-       tmp = 0;
-       for (i = 0; i < priv->gpc_nr; i++)
-               tmp |= priv->tpc_nr[i] << (i * 4);
-       nv_wr32(priv, 0x406028, tmp);
-       nv_wr32(priv, 0x405870, tmp);
-
-       nv_wr32(priv, 0x40602c, 0x00000000);
-       nv_wr32(priv, 0x405874, 0x00000000);
-       nv_wr32(priv, 0x406030, 0x00000000);
-       nv_wr32(priv, 0x405878, 0x00000000);
-       nv_wr32(priv, 0x406034, 0x00000000);
-       nv_wr32(priv, 0x40587c, 0x00000000);
-
-       if (1) {
-               u8 tpcnr[GPC_MAX], data[TPC_MAX];
-
-               memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
-               memset(data, 0x1f, sizeof(data));
+       /* PGD pointer */
+       nv_wo32(chan, 0x0200, lower_32_bits(chan->addr + 0x1000));
+       nv_wo32(chan, 0x0204, upper_32_bits(chan->addr + 0x1000));
+       nv_wo32(chan, 0x0208, 0xffffffff);
+       nv_wo32(chan, 0x020c, 0x000000ff);
 
-               gpc = -1;
-               for (tpc = 0; tpc < priv->tpc_total; tpc++) {
-                       do {
-                               gpc = (gpc + 1) % priv->gpc_nr;
-                       } while (!tpcnr[gpc]);
-                       tpcnr[gpc]--;
-                       data[tpc] = gpc;
-               }
+       /* PGT[0] pointer */
+       nv_wo32(chan, 0x1000, 0x00000000);
+       nv_wo32(chan, 0x1004, 0x00000001 | (chan->addr + 0x2000) >> 8);
 
-               for (i = 0; i < 4; i++)
-                       nv_wr32(priv, 0x4060a8 + (i * 4), ((u32 *)data)[i]);
+       /* identity-map the whole "channel" into its own vm */
+       for (i = 0; i < chan->size / 4096; i++) {
+               u64 addr = ((chan->addr + (i * 4096)) >> 8) | 1;
+               nv_wo32(chan, 0x2000 + (i * 8), lower_32_bits(addr));
+               nv_wo32(chan, 0x2004 + (i * 8), upper_32_bits(addr));
        }
 
-       if (1) {
-               u32 data[6] = {}, data2[2] = {};
-               u8 tpcnr[GPC_MAX];
-               u8 shift, ntpcv;
-
-               /* calculate first set of magics */
-               memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
+       /* context pointer (virt) */
+       nv_wo32(chan, 0x0210, 0x00080004);
+       nv_wo32(chan, 0x0214, 0x00000000);
 
-               gpc = -1;
-               for (tpc = 0; tpc < priv->tpc_total; tpc++) {
-                       do {
-                               gpc = (gpc + 1) % priv->gpc_nr;
-                       } while (!tpcnr[gpc]);
-                       tpcnr[gpc]--;
+       bar->flush(bar);
 
-                       data[tpc / 6] |= gpc << ((tpc % 6) * 5);
-               }
+       nv_wr32(priv, 0x100cb8, (chan->addr + 0x1000) >> 8);
+       nv_wr32(priv, 0x100cbc, 0x80000001);
+       nv_wait(priv, 0x100c80, 0x00008000, 0x00008000);
 
-               for (; tpc < 32; tpc++)
-                       data[tpc / 6] |= 7 << ((tpc % 6) * 5);
+       /* setup default state for mmio list construction */
+       info.priv = priv;
+       info.data = priv->mmio_data;
+       info.mmio = priv->mmio_list;
+       info.addr = 0x2000 + (i * 8);
+       info.buffer_nr = 0;
 
-               /* and the second... */
-               shift = 0;
-               ntpcv = priv->tpc_total;
-               while (!(ntpcv & (1 << 4))) {
-                       ntpcv <<= 1;
-                       shift++;
-               }
+       /* make channel current */
+       if (priv->firmware) {
+               nv_wr32(priv, 0x409840, 0x00000030);
+               nv_wr32(priv, 0x409500, 0x80000000 | chan->addr >> 12);
+               nv_wr32(priv, 0x409504, 0x00000003);
+               if (!nv_wait(priv, 0x409800, 0x00000010, 0x00000010))
+                       nv_error(priv, "load_ctx timeout\n");
 
-               data2[0]  = (ntpcv << 16);
-               data2[0] |= (shift << 21);
-               data2[0] |= (((1 << (0 + 5)) % ntpcv) << 24);
-               for (i = 1; i < 7; i++)
-                       data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5);
-
-               /* GPC_BROADCAST */
-               nv_wr32(priv, 0x418bb8, (priv->tpc_total << 8) |
-                                       priv->magic_not_rop_nr);
-               for (i = 0; i < 6; i++)
-                       nv_wr32(priv, 0x418b08 + (i * 4), data[i]);
-
-               /* GPC_BROADCAST.TP_BROADCAST */
-               nv_wr32(priv, 0x419bd0, (priv->tpc_total << 8) |
-                                      priv->magic_not_rop_nr |
-                                      data2[0]);
-               nv_wr32(priv, 0x419be4, data2[1]);
-               for (i = 0; i < 6; i++)
-                       nv_wr32(priv, 0x419b00 + (i * 4), data[i]);
-
-               /* UNK78xx */
-               nv_wr32(priv, 0x4078bc, (priv->tpc_total << 8) |
-                                       priv->magic_not_rop_nr);
-               for (i = 0; i < 6; i++)
-                       nv_wr32(priv, 0x40780c + (i * 4), data[i]);
+               nv_wo32(chan, 0x8001c, 1);
+               nv_wo32(chan, 0x80020, 0);
+               nv_wo32(chan, 0x80028, 0);
+               nv_wo32(chan, 0x8002c, 0);
+               bar->flush(bar);
+       } else {
+               nv_wr32(priv, 0x409840, 0x80000000);
+               nv_wr32(priv, 0x409500, 0x80000000 | chan->addr >> 12);
+               nv_wr32(priv, 0x409504, 0x00000001);
+               if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000))
+                       nv_error(priv, "HUB_SET_CHAN timeout\n");
        }
 
-       if (1) {
-               u32 tpc_mask = 0, tpc_set = 0;
-               u8  tpcnr[GPC_MAX], a, b;
-
-               memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
-               for (gpc = 0; gpc < priv->gpc_nr; gpc++)
-                       tpc_mask |= ((1 << priv->tpc_nr[gpc]) - 1) << (gpc * 8);
-
-               for (i = 0, gpc = -1, b = -1; i < 32; i++) {
-                       a = (i * (priv->tpc_total - 1)) / 32;
-                       if (a != b) {
-                               b = a;
-                               do {
-                                       gpc = (gpc + 1) % priv->gpc_nr;
-                               } while (!tpcnr[gpc]);
-                               tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--;
-
-                               tpc_set |= 1 << ((gpc * 8) + tpc);
-                       }
+       oclass->main(priv, &info);
 
-                       nv_wr32(priv, 0x406800 + (i * 0x20), tpc_set);
-                       nv_wr32(priv, 0x406c00 + (i * 0x20), tpc_set ^ tpc_mask);
-               }
+       /* trigger a context unload by unsetting the "next channel valid" bit
+        * and faking a context switch interrupt
+        */
+       nv_mask(priv, 0x409b04, 0x80000000, 0x00000000);
+       nv_wr32(priv, 0x409000, 0x00000100);
+       if (!nv_wait(priv, 0x409b00, 0x80000000, 0x00000000)) {
+               nv_error(priv, "grctx template channel unload timeout\n");
+               ret = -EBUSY;
+               goto done;
        }
 
-       nv_wr32(priv, 0x400208, 0x80000000);
-
-       nv_icmd(priv, 0x00001000, 0x00000004);
-       nv_icmd(priv, 0x000000a9, 0x0000ffff);
-       nv_icmd(priv, 0x00000038, 0x0fac6881);
-       nv_icmd(priv, 0x0000003d, 0x00000001);
-       nv_icmd(priv, 0x000000e8, 0x00000400);
-       nv_icmd(priv, 0x000000e9, 0x00000400);
-       nv_icmd(priv, 0x000000ea, 0x00000400);
-       nv_icmd(priv, 0x000000eb, 0x00000400);
-       nv_icmd(priv, 0x000000ec, 0x00000400);
-       nv_icmd(priv, 0x000000ed, 0x00000400);
-       nv_icmd(priv, 0x000000ee, 0x00000400);
-       nv_icmd(priv, 0x000000ef, 0x00000400);
-       nv_icmd(priv, 0x00000078, 0x00000300);
-       nv_icmd(priv, 0x00000079, 0x00000300);
-       nv_icmd(priv, 0x0000007a, 0x00000300);
-       nv_icmd(priv, 0x0000007b, 0x00000300);
-       nv_icmd(priv, 0x0000007c, 0x00000300);
-       nv_icmd(priv, 0x0000007d, 0x00000300);
-       nv_icmd(priv, 0x0000007e, 0x00000300);
-       nv_icmd(priv, 0x0000007f, 0x00000300);
-       nv_icmd(priv, 0x00000050, 0x00000011);
-       nv_icmd(priv, 0x00000058, 0x00000008);
-       nv_icmd(priv, 0x00000059, 0x00000008);
-       nv_icmd(priv, 0x0000005a, 0x00000008);
-       nv_icmd(priv, 0x0000005b, 0x00000008);
-       nv_icmd(priv, 0x0000005c, 0x00000008);
-       nv_icmd(priv, 0x0000005d, 0x00000008);
-       nv_icmd(priv, 0x0000005e, 0x00000008);
-       nv_icmd(priv, 0x0000005f, 0x00000008);
-       nv_icmd(priv, 0x00000208, 0x00000001);
-       nv_icmd(priv, 0x00000209, 0x00000001);
-       nv_icmd(priv, 0x0000020a, 0x00000001);
-       nv_icmd(priv, 0x0000020b, 0x00000001);
-       nv_icmd(priv, 0x0000020c, 0x00000001);
-       nv_icmd(priv, 0x0000020d, 0x00000001);
-       nv_icmd(priv, 0x0000020e, 0x00000001);
-       nv_icmd(priv, 0x0000020f, 0x00000001);
-       nv_icmd(priv, 0x00000081, 0x00000001);
-       nv_icmd(priv, 0x00000085, 0x00000004);
-       nv_icmd(priv, 0x00000088, 0x00000400);
-       nv_icmd(priv, 0x00000090, 0x00000300);
-       nv_icmd(priv, 0x00000098, 0x00001001);
-       nv_icmd(priv, 0x000000e3, 0x00000001);
-       nv_icmd(priv, 0x000000da, 0x00000001);
-       nv_icmd(priv, 0x000000f8, 0x00000003);
-       nv_icmd(priv, 0x000000fa, 0x00000001);
-       nv_icmd(priv, 0x0000009f, 0x0000ffff);
-       nv_icmd(priv, 0x000000a0, 0x0000ffff);
-       nv_icmd(priv, 0x000000a1, 0x0000ffff);
-       nv_icmd(priv, 0x000000a2, 0x0000ffff);
-       nv_icmd(priv, 0x000000b1, 0x00000001);
-       nv_icmd(priv, 0x000000b2, 0x00000000);
-       nv_icmd(priv, 0x000000b3, 0x00000000);
-       nv_icmd(priv, 0x000000b4, 0x00000000);
-       nv_icmd(priv, 0x000000b5, 0x00000000);
-       nv_icmd(priv, 0x000000b6, 0x00000000);
-       nv_icmd(priv, 0x000000b7, 0x00000000);
-       nv_icmd(priv, 0x000000b8, 0x00000000);
-       nv_icmd(priv, 0x000000b9, 0x00000000);
-       nv_icmd(priv, 0x000000ba, 0x00000000);
-       nv_icmd(priv, 0x000000bb, 0x00000000);
-       nv_icmd(priv, 0x000000bc, 0x00000000);
-       nv_icmd(priv, 0x000000bd, 0x00000000);
-       nv_icmd(priv, 0x000000be, 0x00000000);
-       nv_icmd(priv, 0x000000bf, 0x00000000);
-       nv_icmd(priv, 0x000000c0, 0x00000000);
-       nv_icmd(priv, 0x000000c1, 0x00000000);
-       nv_icmd(priv, 0x000000c2, 0x00000000);
-       nv_icmd(priv, 0x000000c3, 0x00000000);
-       nv_icmd(priv, 0x000000c4, 0x00000000);
-       nv_icmd(priv, 0x000000c5, 0x00000000);
-       nv_icmd(priv, 0x000000c6, 0x00000000);
-       nv_icmd(priv, 0x000000c7, 0x00000000);
-       nv_icmd(priv, 0x000000c8, 0x00000000);
-       nv_icmd(priv, 0x000000c9, 0x00000000);
-       nv_icmd(priv, 0x000000ca, 0x00000000);
-       nv_icmd(priv, 0x000000cb, 0x00000000);
-       nv_icmd(priv, 0x000000cc, 0x00000000);
-       nv_icmd(priv, 0x000000cd, 0x00000000);
-       nv_icmd(priv, 0x000000ce, 0x00000000);
-       nv_icmd(priv, 0x000000cf, 0x00000000);
-       nv_icmd(priv, 0x000000d0, 0x00000000);
-       nv_icmd(priv, 0x000000d1, 0x00000000);
-       nv_icmd(priv, 0x000000d2, 0x00000000);
-       nv_icmd(priv, 0x000000d3, 0x00000000);
-       nv_icmd(priv, 0x000000d4, 0x00000000);
-       nv_icmd(priv, 0x000000d5, 0x00000000);
-       nv_icmd(priv, 0x000000d6, 0x00000000);
-       nv_icmd(priv, 0x000000d7, 0x00000000);
-       nv_icmd(priv, 0x000000d8, 0x00000000);
-       nv_icmd(priv, 0x000000d9, 0x00000000);
-       nv_icmd(priv, 0x00000210, 0x00000040);
-       nv_icmd(priv, 0x00000211, 0x00000040);
-       nv_icmd(priv, 0x00000212, 0x00000040);
-       nv_icmd(priv, 0x00000213, 0x00000040);
-       nv_icmd(priv, 0x00000214, 0x00000040);
-       nv_icmd(priv, 0x00000215, 0x00000040);
-       nv_icmd(priv, 0x00000216, 0x00000040);
-       nv_icmd(priv, 0x00000217, 0x00000040);
-       if (nv_device(priv)->chipset >= 0xd0) {
-               for (i = 0x0400; i <= 0x0417; i++)
-                       nv_icmd(priv, i, 0x00000040);
-       }
-       nv_icmd(priv, 0x00000218, 0x0000c080);
-       nv_icmd(priv, 0x00000219, 0x0000c080);
-       nv_icmd(priv, 0x0000021a, 0x0000c080);
-       nv_icmd(priv, 0x0000021b, 0x0000c080);
-       nv_icmd(priv, 0x0000021c, 0x0000c080);
-       nv_icmd(priv, 0x0000021d, 0x0000c080);
-       nv_icmd(priv, 0x0000021e, 0x0000c080);
-       nv_icmd(priv, 0x0000021f, 0x0000c080);
-       if (nv_device(priv)->chipset >= 0xd0) {
-               for (i = 0x0440; i <= 0x0457; i++)
-                       nv_icmd(priv, i, 0x0000c080);
+       priv->data = kmalloc(priv->size, GFP_KERNEL);
+       if (priv->data) {
+               for (i = 0; i < priv->size; i += 4)
+                       priv->data[i / 4] = nv_ro32(chan, 0x80000 + i);
+               ret = 0;
+       } else {
+               ret = -ENOMEM;
        }
-       nv_icmd(priv, 0x000000ad, 0x0000013e);
-       nv_icmd(priv, 0x000000e1, 0x00000010);
-       nv_icmd(priv, 0x00000290, 0x00000000);
-       nv_icmd(priv, 0x00000291, 0x00000000);
-       nv_icmd(priv, 0x00000292, 0x00000000);
-       nv_icmd(priv, 0x00000293, 0x00000000);
-       nv_icmd(priv, 0x00000294, 0x00000000);
-       nv_icmd(priv, 0x00000295, 0x00000000);
-       nv_icmd(priv, 0x00000296, 0x00000000);
-       nv_icmd(priv, 0x00000297, 0x00000000);
-       nv_icmd(priv, 0x00000298, 0x00000000);
-       nv_icmd(priv, 0x00000299, 0x00000000);
-       nv_icmd(priv, 0x0000029a, 0x00000000);
-       nv_icmd(priv, 0x0000029b, 0x00000000);
-       nv_icmd(priv, 0x0000029c, 0x00000000);
-       nv_icmd(priv, 0x0000029d, 0x00000000);
-       nv_icmd(priv, 0x0000029e, 0x00000000);
-       nv_icmd(priv, 0x0000029f, 0x00000000);
-       nv_icmd(priv, 0x000003b0, 0x00000000);
-       nv_icmd(priv, 0x000003b1, 0x00000000);
-       nv_icmd(priv, 0x000003b2, 0x00000000);
-       nv_icmd(priv, 0x000003b3, 0x00000000);
-       nv_icmd(priv, 0x000003b4, 0x00000000);
-       nv_icmd(priv, 0x000003b5, 0x00000000);
-       nv_icmd(priv, 0x000003b6, 0x00000000);
-       nv_icmd(priv, 0x000003b7, 0x00000000);
-       nv_icmd(priv, 0x000003b8, 0x00000000);
-       nv_icmd(priv, 0x000003b9, 0x00000000);
-       nv_icmd(priv, 0x000003ba, 0x00000000);
-       nv_icmd(priv, 0x000003bb, 0x00000000);
-       nv_icmd(priv, 0x000003bc, 0x00000000);
-       nv_icmd(priv, 0x000003bd, 0x00000000);
-       nv_icmd(priv, 0x000003be, 0x00000000);
-       nv_icmd(priv, 0x000003bf, 0x00000000);
-       nv_icmd(priv, 0x000002a0, 0x00000000);
-       nv_icmd(priv, 0x000002a1, 0x00000000);
-       nv_icmd(priv, 0x000002a2, 0x00000000);
-       nv_icmd(priv, 0x000002a3, 0x00000000);
-       nv_icmd(priv, 0x000002a4, 0x00000000);
-       nv_icmd(priv, 0x000002a5, 0x00000000);
-       nv_icmd(priv, 0x000002a6, 0x00000000);
-       nv_icmd(priv, 0x000002a7, 0x00000000);
-       nv_icmd(priv, 0x000002a8, 0x00000000);
-       nv_icmd(priv, 0x000002a9, 0x00000000);
-       nv_icmd(priv, 0x000002aa, 0x00000000);
-       nv_icmd(priv, 0x000002ab, 0x00000000);
-       nv_icmd(priv, 0x000002ac, 0x00000000);
-       nv_icmd(priv, 0x000002ad, 0x00000000);
-       nv_icmd(priv, 0x000002ae, 0x00000000);
-       nv_icmd(priv, 0x000002af, 0x00000000);
-       nv_icmd(priv, 0x00000420, 0x00000000);
-       nv_icmd(priv, 0x00000421, 0x00000000);
-       nv_icmd(priv, 0x00000422, 0x00000000);
-       nv_icmd(priv, 0x00000423, 0x00000000);
-       nv_icmd(priv, 0x00000424, 0x00000000);
-       nv_icmd(priv, 0x00000425, 0x00000000);
-       nv_icmd(priv, 0x00000426, 0x00000000);
-       nv_icmd(priv, 0x00000427, 0x00000000);
-       nv_icmd(priv, 0x00000428, 0x00000000);
-       nv_icmd(priv, 0x00000429, 0x00000000);
-       nv_icmd(priv, 0x0000042a, 0x00000000);
-       nv_icmd(priv, 0x0000042b, 0x00000000);
-       nv_icmd(priv, 0x0000042c, 0x00000000);
-       nv_icmd(priv, 0x0000042d, 0x00000000);
-       nv_icmd(priv, 0x0000042e, 0x00000000);
-       nv_icmd(priv, 0x0000042f, 0x00000000);
-       nv_icmd(priv, 0x000002b0, 0x00000000);
-       nv_icmd(priv, 0x000002b1, 0x00000000);
-       nv_icmd(priv, 0x000002b2, 0x00000000);
-       nv_icmd(priv, 0x000002b3, 0x00000000);
-       nv_icmd(priv, 0x000002b4, 0x00000000);
-       nv_icmd(priv, 0x000002b5, 0x00000000);
-       nv_icmd(priv, 0x000002b6, 0x00000000);
-       nv_icmd(priv, 0x000002b7, 0x00000000);
-       nv_icmd(priv, 0x000002b8, 0x00000000);
-       nv_icmd(priv, 0x000002b9, 0x00000000);
-       nv_icmd(priv, 0x000002ba, 0x00000000);
-       nv_icmd(priv, 0x000002bb, 0x00000000);
-       nv_icmd(priv, 0x000002bc, 0x00000000);
-       nv_icmd(priv, 0x000002bd, 0x00000000);
-       nv_icmd(priv, 0x000002be, 0x00000000);
-       nv_icmd(priv, 0x000002bf, 0x00000000);
-       nv_icmd(priv, 0x00000430, 0x00000000);
-       nv_icmd(priv, 0x00000431, 0x00000000);
-       nv_icmd(priv, 0x00000432, 0x00000000);
-       nv_icmd(priv, 0x00000433, 0x00000000);
-       nv_icmd(priv, 0x00000434, 0x00000000);
-       nv_icmd(priv, 0x00000435, 0x00000000);
-       nv_icmd(priv, 0x00000436, 0x00000000);
-       nv_icmd(priv, 0x00000437, 0x00000000);
-       nv_icmd(priv, 0x00000438, 0x00000000);
-       nv_icmd(priv, 0x00000439, 0x00000000);
-       nv_icmd(priv, 0x0000043a, 0x00000000);
-       nv_icmd(priv, 0x0000043b, 0x00000000);
-       nv_icmd(priv, 0x0000043c, 0x00000000);
-       nv_icmd(priv, 0x0000043d, 0x00000000);
-       nv_icmd(priv, 0x0000043e, 0x00000000);
-       nv_icmd(priv, 0x0000043f, 0x00000000);
-       nv_icmd(priv, 0x000002c0, 0x00000000);
-       nv_icmd(priv, 0x000002c1, 0x00000000);
-       nv_icmd(priv, 0x000002c2, 0x00000000);
-       nv_icmd(priv, 0x000002c3, 0x00000000);
-       nv_icmd(priv, 0x000002c4, 0x00000000);
-       nv_icmd(priv, 0x000002c5, 0x00000000);
-       nv_icmd(priv, 0x000002c6, 0x00000000);
-       nv_icmd(priv, 0x000002c7, 0x00000000);
-       nv_icmd(priv, 0x000002c8, 0x00000000);
-       nv_icmd(priv, 0x000002c9, 0x00000000);
-       nv_icmd(priv, 0x000002ca, 0x00000000);
-       nv_icmd(priv, 0x000002cb, 0x00000000);
-       nv_icmd(priv, 0x000002cc, 0x00000000);
-       nv_icmd(priv, 0x000002cd, 0x00000000);
-       nv_icmd(priv, 0x000002ce, 0x00000000);
-       nv_icmd(priv, 0x000002cf, 0x00000000);
-       nv_icmd(priv, 0x000004d0, 0x00000000);
-       nv_icmd(priv, 0x000004d1, 0x00000000);
-       nv_icmd(priv, 0x000004d2, 0x00000000);
-       nv_icmd(priv, 0x000004d3, 0x00000000);
-       nv_icmd(priv, 0x000004d4, 0x00000000);
-       nv_icmd(priv, 0x000004d5, 0x00000000);
-       nv_icmd(priv, 0x000004d6, 0x00000000);
-       nv_icmd(priv, 0x000004d7, 0x00000000);
-       nv_icmd(priv, 0x000004d8, 0x00000000);
-       nv_icmd(priv, 0x000004d9, 0x00000000);
-       nv_icmd(priv, 0x000004da, 0x00000000);
-       nv_icmd(priv, 0x000004db, 0x00000000);
-       nv_icmd(priv, 0x000004dc, 0x00000000);
-       nv_icmd(priv, 0x000004dd, 0x00000000);
-       nv_icmd(priv, 0x000004de, 0x00000000);
-       nv_icmd(priv, 0x000004df, 0x00000000);
-       nv_icmd(priv, 0x00000720, 0x00000000);
-       nv_icmd(priv, 0x00000721, 0x00000000);
-       nv_icmd(priv, 0x00000722, 0x00000000);
-       nv_icmd(priv, 0x00000723, 0x00000000);
-       nv_icmd(priv, 0x00000724, 0x00000000);
-       nv_icmd(priv, 0x00000725, 0x00000000);
-       nv_icmd(priv, 0x00000726, 0x00000000);
-       nv_icmd(priv, 0x00000727, 0x00000000);
-       nv_icmd(priv, 0x00000728, 0x00000000);
-       nv_icmd(priv, 0x00000729, 0x00000000);
-       nv_icmd(priv, 0x0000072a, 0x00000000);
-       nv_icmd(priv, 0x0000072b, 0x00000000);
-       nv_icmd(priv, 0x0000072c, 0x00000000);
-       nv_icmd(priv, 0x0000072d, 0x00000000);
-       nv_icmd(priv, 0x0000072e, 0x00000000);
-       nv_icmd(priv, 0x0000072f, 0x00000000);
-       nv_icmd(priv, 0x000008c0, 0x00000000);
-       nv_icmd(priv, 0x000008c1, 0x00000000);
-       nv_icmd(priv, 0x000008c2, 0x00000000);
-       nv_icmd(priv, 0x000008c3, 0x00000000);
-       nv_icmd(priv, 0x000008c4, 0x00000000);
-       nv_icmd(priv, 0x000008c5, 0x00000000);
-       nv_icmd(priv, 0x000008c6, 0x00000000);
-       nv_icmd(priv, 0x000008c7, 0x00000000);
-       nv_icmd(priv, 0x000008c8, 0x00000000);
-       nv_icmd(priv, 0x000008c9, 0x00000000);
-       nv_icmd(priv, 0x000008ca, 0x00000000);
-       nv_icmd(priv, 0x000008cb, 0x00000000);
-       nv_icmd(priv, 0x000008cc, 0x00000000);
-       nv_icmd(priv, 0x000008cd, 0x00000000);
-       nv_icmd(priv, 0x000008ce, 0x00000000);
-       nv_icmd(priv, 0x000008cf, 0x00000000);
-       nv_icmd(priv, 0x00000890, 0x00000000);
-       nv_icmd(priv, 0x00000891, 0x00000000);
-       nv_icmd(priv, 0x00000892, 0x00000000);
-       nv_icmd(priv, 0x00000893, 0x00000000);
-       nv_icmd(priv, 0x00000894, 0x00000000);
-       nv_icmd(priv, 0x00000895, 0x00000000);
-       nv_icmd(priv, 0x00000896, 0x00000000);
-       nv_icmd(priv, 0x00000897, 0x00000000);
-       nv_icmd(priv, 0x00000898, 0x00000000);
-       nv_icmd(priv, 0x00000899, 0x00000000);
-       nv_icmd(priv, 0x0000089a, 0x00000000);
-       nv_icmd(priv, 0x0000089b, 0x00000000);
-       nv_icmd(priv, 0x0000089c, 0x00000000);
-       nv_icmd(priv, 0x0000089d, 0x00000000);
-       nv_icmd(priv, 0x0000089e, 0x00000000);
-       nv_icmd(priv, 0x0000089f, 0x00000000);
-       nv_icmd(priv, 0x000008e0, 0x00000000);
-       nv_icmd(priv, 0x000008e1, 0x00000000);
-       nv_icmd(priv, 0x000008e2, 0x00000000);
-       nv_icmd(priv, 0x000008e3, 0x00000000);
-       nv_icmd(priv, 0x000008e4, 0x00000000);
-       nv_icmd(priv, 0x000008e5, 0x00000000);
-       nv_icmd(priv, 0x000008e6, 0x00000000);
-       nv_icmd(priv, 0x000008e7, 0x00000000);
-       nv_icmd(priv, 0x000008e8, 0x00000000);
-       nv_icmd(priv, 0x000008e9, 0x00000000);
-       nv_icmd(priv, 0x000008ea, 0x00000000);
-       nv_icmd(priv, 0x000008eb, 0x00000000);
-       nv_icmd(priv, 0x000008ec, 0x00000000);
-       nv_icmd(priv, 0x000008ed, 0x00000000);
-       nv_icmd(priv, 0x000008ee, 0x00000000);
-       nv_icmd(priv, 0x000008ef, 0x00000000);
-       nv_icmd(priv, 0x000008a0, 0x00000000);
-       nv_icmd(priv, 0x000008a1, 0x00000000);
-       nv_icmd(priv, 0x000008a2, 0x00000000);
-       nv_icmd(priv, 0x000008a3, 0x00000000);
-       nv_icmd(priv, 0x000008a4, 0x00000000);
-       nv_icmd(priv, 0x000008a5, 0x00000000);
-       nv_icmd(priv, 0x000008a6, 0x00000000);
-       nv_icmd(priv, 0x000008a7, 0x00000000);
-       nv_icmd(priv, 0x000008a8, 0x00000000);
-       nv_icmd(priv, 0x000008a9, 0x00000000);
-       nv_icmd(priv, 0x000008aa, 0x00000000);
-       nv_icmd(priv, 0x000008ab, 0x00000000);
-       nv_icmd(priv, 0x000008ac, 0x00000000);
-       nv_icmd(priv, 0x000008ad, 0x00000000);
-       nv_icmd(priv, 0x000008ae, 0x00000000);
-       nv_icmd(priv, 0x000008af, 0x00000000);
-       nv_icmd(priv, 0x000008f0, 0x00000000);
-       nv_icmd(priv, 0x000008f1, 0x00000000);
-       nv_icmd(priv, 0x000008f2, 0x00000000);
-       nv_icmd(priv, 0x000008f3, 0x00000000);
-       nv_icmd(priv, 0x000008f4, 0x00000000);
-       nv_icmd(priv, 0x000008f5, 0x00000000);
-       nv_icmd(priv, 0x000008f6, 0x00000000);
-       nv_icmd(priv, 0x000008f7, 0x00000000);
-       nv_icmd(priv, 0x000008f8, 0x00000000);
-       nv_icmd(priv, 0x000008f9, 0x00000000);
-       nv_icmd(priv, 0x000008fa, 0x00000000);
-       nv_icmd(priv, 0x000008fb, 0x00000000);
-       nv_icmd(priv, 0x000008fc, 0x00000000);
-       nv_icmd(priv, 0x000008fd, 0x00000000);
-       nv_icmd(priv, 0x000008fe, 0x00000000);
-       nv_icmd(priv, 0x000008ff, 0x00000000);
-       nv_icmd(priv, 0x0000094c, 0x000000ff);
-       nv_icmd(priv, 0x0000094d, 0xffffffff);
-       nv_icmd(priv, 0x0000094e, 0x00000002);
-       nv_icmd(priv, 0x000002ec, 0x00000001);
-       nv_icmd(priv, 0x00000303, 0x00000001);
-       nv_icmd(priv, 0x000002e6, 0x00000001);
-       nv_icmd(priv, 0x00000466, 0x00000052);
-       nv_icmd(priv, 0x00000301, 0x3f800000);
-       nv_icmd(priv, 0x00000304, 0x30201000);
-       nv_icmd(priv, 0x00000305, 0x70605040);
-       nv_icmd(priv, 0x00000306, 0xb8a89888);
-       nv_icmd(priv, 0x00000307, 0xf8e8d8c8);
-       nv_icmd(priv, 0x0000030a, 0x00ffff00);
-       nv_icmd(priv, 0x0000030b, 0x0000001a);
-       nv_icmd(priv, 0x0000030c, 0x00000001);
-       nv_icmd(priv, 0x00000318, 0x00000001);
-       nv_icmd(priv, 0x00000340, 0x00000000);
-       nv_icmd(priv, 0x00000375, 0x00000001);
-       nv_icmd(priv, 0x00000351, 0x00000100);
-       nv_icmd(priv, 0x0000037d, 0x00000006);
-       nv_icmd(priv, 0x000003a0, 0x00000002);
-       nv_icmd(priv, 0x000003aa, 0x00000001);
-       nv_icmd(priv, 0x000003a9, 0x00000001);
-       nv_icmd(priv, 0x00000380, 0x00000001);
-       nv_icmd(priv, 0x00000360, 0x00000040);
-       nv_icmd(priv, 0x00000366, 0x00000000);
-       nv_icmd(priv, 0x00000367, 0x00000000);
-       nv_icmd(priv, 0x00000368, 0x00001fff);
-       nv_icmd(priv, 0x00000370, 0x00000000);
-       nv_icmd(priv, 0x00000371, 0x00000000);
-       nv_icmd(priv, 0x00000372, 0x003fffff);
-       nv_icmd(priv, 0x0000037a, 0x00000012);
-       nv_icmd(priv, 0x000005e0, 0x00000022);
-       nv_icmd(priv, 0x000005e1, 0x00000022);
-       nv_icmd(priv, 0x000005e2, 0x00000022);
-       nv_icmd(priv, 0x000005e3, 0x00000022);
-       nv_icmd(priv, 0x000005e4, 0x00000022);
-       nv_icmd(priv, 0x00000619, 0x00000003);
-       nv_icmd(priv, 0x00000811, 0x00000003);
-       nv_icmd(priv, 0x00000812, 0x00000004);
-       nv_icmd(priv, 0x00000813, 0x00000006);
-       nv_icmd(priv, 0x00000814, 0x00000008);
-       nv_icmd(priv, 0x00000815, 0x0000000b);
-       nv_icmd(priv, 0x00000800, 0x00000001);
-       nv_icmd(priv, 0x00000801, 0x00000001);
-       nv_icmd(priv, 0x00000802, 0x00000001);
-       nv_icmd(priv, 0x00000803, 0x00000001);
-       nv_icmd(priv, 0x00000804, 0x00000001);
-       nv_icmd(priv, 0x00000805, 0x00000001);
-       nv_icmd(priv, 0x00000632, 0x00000001);
-       nv_icmd(priv, 0x00000633, 0x00000002);
-       nv_icmd(priv, 0x00000634, 0x00000003);
-       nv_icmd(priv, 0x00000635, 0x00000004);
-       nv_icmd(priv, 0x00000654, 0x3f800000);
-       nv_icmd(priv, 0x00000657, 0x3f800000);
-       nv_icmd(priv, 0x00000655, 0x3f800000);
-       nv_icmd(priv, 0x00000656, 0x3f800000);
-       nv_icmd(priv, 0x000006cd, 0x3f800000);
-       nv_icmd(priv, 0x000007f5, 0x3f800000);
-       nv_icmd(priv, 0x000007dc, 0x39291909);
-       nv_icmd(priv, 0x000007dd, 0x79695949);
-       nv_icmd(priv, 0x000007de, 0xb9a99989);
-       nv_icmd(priv, 0x000007df, 0xf9e9d9c9);
-       nv_icmd(priv, 0x000007e8, 0x00003210);
-       nv_icmd(priv, 0x000007e9, 0x00007654);
-       nv_icmd(priv, 0x000007ea, 0x00000098);
-       nv_icmd(priv, 0x000007ec, 0x39291909);
-       nv_icmd(priv, 0x000007ed, 0x79695949);
-       nv_icmd(priv, 0x000007ee, 0xb9a99989);
-       nv_icmd(priv, 0x000007ef, 0xf9e9d9c9);
-       nv_icmd(priv, 0x000007f0, 0x00003210);
-       nv_icmd(priv, 0x000007f1, 0x00007654);
-       nv_icmd(priv, 0x000007f2, 0x00000098);
-       nv_icmd(priv, 0x000005a5, 0x00000001);
-       nv_icmd(priv, 0x00000980, 0x00000000);
-       nv_icmd(priv, 0x00000981, 0x00000000);
-       nv_icmd(priv, 0x00000982, 0x00000000);
-       nv_icmd(priv, 0x00000983, 0x00000000);
-       nv_icmd(priv, 0x00000984, 0x00000000);
-       nv_icmd(priv, 0x00000985, 0x00000000);
-       nv_icmd(priv, 0x00000986, 0x00000000);
-       nv_icmd(priv, 0x00000987, 0x00000000);
-       nv_icmd(priv, 0x00000988, 0x00000000);
-       nv_icmd(priv, 0x00000989, 0x00000000);
-       nv_icmd(priv, 0x0000098a, 0x00000000);
-       nv_icmd(priv, 0x0000098b, 0x00000000);
-       nv_icmd(priv, 0x0000098c, 0x00000000);
-       nv_icmd(priv, 0x0000098d, 0x00000000);
-       nv_icmd(priv, 0x0000098e, 0x00000000);
-       nv_icmd(priv, 0x0000098f, 0x00000000);
-       nv_icmd(priv, 0x00000990, 0x00000000);
-       nv_icmd(priv, 0x00000991, 0x00000000);
-       nv_icmd(priv, 0x00000992, 0x00000000);
-       nv_icmd(priv, 0x00000993, 0x00000000);
-       nv_icmd(priv, 0x00000994, 0x00000000);
-       nv_icmd(priv, 0x00000995, 0x00000000);
-       nv_icmd(priv, 0x00000996, 0x00000000);
-       nv_icmd(priv, 0x00000997, 0x00000000);
-       nv_icmd(priv, 0x00000998, 0x00000000);
-       nv_icmd(priv, 0x00000999, 0x00000000);
-       nv_icmd(priv, 0x0000099a, 0x00000000);
-       nv_icmd(priv, 0x0000099b, 0x00000000);
-       nv_icmd(priv, 0x0000099c, 0x00000000);
-       nv_icmd(priv, 0x0000099d, 0x00000000);
-       nv_icmd(priv, 0x0000099e, 0x00000000);
-       nv_icmd(priv, 0x0000099f, 0x00000000);
-       nv_icmd(priv, 0x000009a0, 0x00000000);
-       nv_icmd(priv, 0x000009a1, 0x00000000);
-       nv_icmd(priv, 0x000009a2, 0x00000000);
-       nv_icmd(priv, 0x000009a3, 0x00000000);
-       nv_icmd(priv, 0x000009a4, 0x00000000);
-       nv_icmd(priv, 0x000009a5, 0x00000000);
-       nv_icmd(priv, 0x000009a6, 0x00000000);
-       nv_icmd(priv, 0x000009a7, 0x00000000);
-       nv_icmd(priv, 0x000009a8, 0x00000000);
-       nv_icmd(priv, 0x000009a9, 0x00000000);
-       nv_icmd(priv, 0x000009aa, 0x00000000);
-       nv_icmd(priv, 0x000009ab, 0x00000000);
-       nv_icmd(priv, 0x000009ac, 0x00000000);
-       nv_icmd(priv, 0x000009ad, 0x00000000);
-       nv_icmd(priv, 0x000009ae, 0x00000000);
-       nv_icmd(priv, 0x000009af, 0x00000000);
-       nv_icmd(priv, 0x000009b0, 0x00000000);
-       nv_icmd(priv, 0x000009b1, 0x00000000);
-       nv_icmd(priv, 0x000009b2, 0x00000000);
-       nv_icmd(priv, 0x000009b3, 0x00000000);
-       nv_icmd(priv, 0x000009b4, 0x00000000);
-       nv_icmd(priv, 0x000009b5, 0x00000000);
-       nv_icmd(priv, 0x000009b6, 0x00000000);
-       nv_icmd(priv, 0x000009b7, 0x00000000);
-       nv_icmd(priv, 0x000009b8, 0x00000000);
-       nv_icmd(priv, 0x000009b9, 0x00000000);
-       nv_icmd(priv, 0x000009ba, 0x00000000);
-       nv_icmd(priv, 0x000009bb, 0x00000000);
-       nv_icmd(priv, 0x000009bc, 0x00000000);
-       nv_icmd(priv, 0x000009bd, 0x00000000);
-       nv_icmd(priv, 0x000009be, 0x00000000);
-       nv_icmd(priv, 0x000009bf, 0x00000000);
-       nv_icmd(priv, 0x000009c0, 0x00000000);
-       nv_icmd(priv, 0x000009c1, 0x00000000);
-       nv_icmd(priv, 0x000009c2, 0x00000000);
-       nv_icmd(priv, 0x000009c3, 0x00000000);
-       nv_icmd(priv, 0x000009c4, 0x00000000);
-       nv_icmd(priv, 0x000009c5, 0x00000000);
-       nv_icmd(priv, 0x000009c6, 0x00000000);
-       nv_icmd(priv, 0x000009c7, 0x00000000);
-       nv_icmd(priv, 0x000009c8, 0x00000000);
-       nv_icmd(priv, 0x000009c9, 0x00000000);
-       nv_icmd(priv, 0x000009ca, 0x00000000);
-       nv_icmd(priv, 0x000009cb, 0x00000000);
-       nv_icmd(priv, 0x000009cc, 0x00000000);
-       nv_icmd(priv, 0x000009cd, 0x00000000);
-       nv_icmd(priv, 0x000009ce, 0x00000000);
-       nv_icmd(priv, 0x000009cf, 0x00000000);
-       nv_icmd(priv, 0x000009d0, 0x00000000);
-       nv_icmd(priv, 0x000009d1, 0x00000000);
-       nv_icmd(priv, 0x000009d2, 0x00000000);
-       nv_icmd(priv, 0x000009d3, 0x00000000);
-       nv_icmd(priv, 0x000009d4, 0x00000000);
-       nv_icmd(priv, 0x000009d5, 0x00000000);
-       nv_icmd(priv, 0x000009d6, 0x00000000);
-       nv_icmd(priv, 0x000009d7, 0x00000000);
-       nv_icmd(priv, 0x000009d8, 0x00000000);
-       nv_icmd(priv, 0x000009d9, 0x00000000);
-       nv_icmd(priv, 0x000009da, 0x00000000);
-       nv_icmd(priv, 0x000009db, 0x00000000);
-       nv_icmd(priv, 0x000009dc, 0x00000000);
-       nv_icmd(priv, 0x000009dd, 0x00000000);
-       nv_icmd(priv, 0x000009de, 0x00000000);
-       nv_icmd(priv, 0x000009df, 0x00000000);
-       nv_icmd(priv, 0x000009e0, 0x00000000);
-       nv_icmd(priv, 0x000009e1, 0x00000000);
-       nv_icmd(priv, 0x000009e2, 0x00000000);
-       nv_icmd(priv, 0x000009e3, 0x00000000);
-       nv_icmd(priv, 0x000009e4, 0x00000000);
-       nv_icmd(priv, 0x000009e5, 0x00000000);
-       nv_icmd(priv, 0x000009e6, 0x00000000);
-       nv_icmd(priv, 0x000009e7, 0x00000000);
-       nv_icmd(priv, 0x000009e8, 0x00000000);
-       nv_icmd(priv, 0x000009e9, 0x00000000);
-       nv_icmd(priv, 0x000009ea, 0x00000000);
-       nv_icmd(priv, 0x000009eb, 0x00000000);
-       nv_icmd(priv, 0x000009ec, 0x00000000);
-       nv_icmd(priv, 0x000009ed, 0x00000000);
-       nv_icmd(priv, 0x000009ee, 0x00000000);
-       nv_icmd(priv, 0x000009ef, 0x00000000);
-       nv_icmd(priv, 0x000009f0, 0x00000000);
-       nv_icmd(priv, 0x000009f1, 0x00000000);
-       nv_icmd(priv, 0x000009f2, 0x00000000);
-       nv_icmd(priv, 0x000009f3, 0x00000000);
-       nv_icmd(priv, 0x000009f4, 0x00000000);
-       nv_icmd(priv, 0x000009f5, 0x00000000);
-       nv_icmd(priv, 0x000009f6, 0x00000000);
-       nv_icmd(priv, 0x000009f7, 0x00000000);
-       nv_icmd(priv, 0x000009f8, 0x00000000);
-       nv_icmd(priv, 0x000009f9, 0x00000000);
-       nv_icmd(priv, 0x000009fa, 0x00000000);
-       nv_icmd(priv, 0x000009fb, 0x00000000);
-       nv_icmd(priv, 0x000009fc, 0x00000000);
-       nv_icmd(priv, 0x000009fd, 0x00000000);
-       nv_icmd(priv, 0x000009fe, 0x00000000);
-       nv_icmd(priv, 0x000009ff, 0x00000000);
-       nv_icmd(priv, 0x00000468, 0x00000004);
-       nv_icmd(priv, 0x0000046c, 0x00000001);
-       nv_icmd(priv, 0x00000470, 0x00000000);
-       nv_icmd(priv, 0x00000471, 0x00000000);
-       nv_icmd(priv, 0x00000472, 0x00000000);
-       nv_icmd(priv, 0x00000473, 0x00000000);
-       nv_icmd(priv, 0x00000474, 0x00000000);
-       nv_icmd(priv, 0x00000475, 0x00000000);
-       nv_icmd(priv, 0x00000476, 0x00000000);
-       nv_icmd(priv, 0x00000477, 0x00000000);
-       nv_icmd(priv, 0x00000478, 0x00000000);
-       nv_icmd(priv, 0x00000479, 0x00000000);
-       nv_icmd(priv, 0x0000047a, 0x00000000);
-       nv_icmd(priv, 0x0000047b, 0x00000000);
-       nv_icmd(priv, 0x0000047c, 0x00000000);
-       nv_icmd(priv, 0x0000047d, 0x00000000);
-       nv_icmd(priv, 0x0000047e, 0x00000000);
-       nv_icmd(priv, 0x0000047f, 0x00000000);
-       nv_icmd(priv, 0x00000480, 0x00000000);
-       nv_icmd(priv, 0x00000481, 0x00000000);
-       nv_icmd(priv, 0x00000482, 0x00000000);
-       nv_icmd(priv, 0x00000483, 0x00000000);
-       nv_icmd(priv, 0x00000484, 0x00000000);
-       nv_icmd(priv, 0x00000485, 0x00000000);
-       nv_icmd(priv, 0x00000486, 0x00000000);
-       nv_icmd(priv, 0x00000487, 0x00000000);
-       nv_icmd(priv, 0x00000488, 0x00000000);
-       nv_icmd(priv, 0x00000489, 0x00000000);
-       nv_icmd(priv, 0x0000048a, 0x00000000);
-       nv_icmd(priv, 0x0000048b, 0x00000000);
-       nv_icmd(priv, 0x0000048c, 0x00000000);
-       nv_icmd(priv, 0x0000048d, 0x00000000);
-       nv_icmd(priv, 0x0000048e, 0x00000000);
-       nv_icmd(priv, 0x0000048f, 0x00000000);
-       nv_icmd(priv, 0x00000490, 0x00000000);
-       nv_icmd(priv, 0x00000491, 0x00000000);
-       nv_icmd(priv, 0x00000492, 0x00000000);
-       nv_icmd(priv, 0x00000493, 0x00000000);
-       nv_icmd(priv, 0x00000494, 0x00000000);
-       nv_icmd(priv, 0x00000495, 0x00000000);
-       nv_icmd(priv, 0x00000496, 0x00000000);
-       nv_icmd(priv, 0x00000497, 0x00000000);
-       nv_icmd(priv, 0x00000498, 0x00000000);
-       nv_icmd(priv, 0x00000499, 0x00000000);
-       nv_icmd(priv, 0x0000049a, 0x00000000);
-       nv_icmd(priv, 0x0000049b, 0x00000000);
-       nv_icmd(priv, 0x0000049c, 0x00000000);
-       nv_icmd(priv, 0x0000049d, 0x00000000);
-       nv_icmd(priv, 0x0000049e, 0x00000000);
-       nv_icmd(priv, 0x0000049f, 0x00000000);
-       nv_icmd(priv, 0x000004a0, 0x00000000);
-       nv_icmd(priv, 0x000004a1, 0x00000000);
-       nv_icmd(priv, 0x000004a2, 0x00000000);
-       nv_icmd(priv, 0x000004a3, 0x00000000);
-       nv_icmd(priv, 0x000004a4, 0x00000000);
-       nv_icmd(priv, 0x000004a5, 0x00000000);
-       nv_icmd(priv, 0x000004a6, 0x00000000);
-       nv_icmd(priv, 0x000004a7, 0x00000000);
-       nv_icmd(priv, 0x000004a8, 0x00000000);
-       nv_icmd(priv, 0x000004a9, 0x00000000);
-       nv_icmd(priv, 0x000004aa, 0x00000000);
-       nv_icmd(priv, 0x000004ab, 0x00000000);
-       nv_icmd(priv, 0x000004ac, 0x00000000);
-       nv_icmd(priv, 0x000004ad, 0x00000000);
-       nv_icmd(priv, 0x000004ae, 0x00000000);
-       nv_icmd(priv, 0x000004af, 0x00000000);
-       nv_icmd(priv, 0x000004b0, 0x00000000);
-       nv_icmd(priv, 0x000004b1, 0x00000000);
-       nv_icmd(priv, 0x000004b2, 0x00000000);
-       nv_icmd(priv, 0x000004b3, 0x00000000);
-       nv_icmd(priv, 0x000004b4, 0x00000000);
-       nv_icmd(priv, 0x000004b5, 0x00000000);
-       nv_icmd(priv, 0x000004b6, 0x00000000);
-       nv_icmd(priv, 0x000004b7, 0x00000000);
-       nv_icmd(priv, 0x000004b8, 0x00000000);
-       nv_icmd(priv, 0x000004b9, 0x00000000);
-       nv_icmd(priv, 0x000004ba, 0x00000000);
-       nv_icmd(priv, 0x000004bb, 0x00000000);
-       nv_icmd(priv, 0x000004bc, 0x00000000);
-       nv_icmd(priv, 0x000004bd, 0x00000000);
-       nv_icmd(priv, 0x000004be, 0x00000000);
-       nv_icmd(priv, 0x000004bf, 0x00000000);
-       nv_icmd(priv, 0x000004c0, 0x00000000);
-       nv_icmd(priv, 0x000004c1, 0x00000000);
-       nv_icmd(priv, 0x000004c2, 0x00000000);
-       nv_icmd(priv, 0x000004c3, 0x00000000);
-       nv_icmd(priv, 0x000004c4, 0x00000000);
-       nv_icmd(priv, 0x000004c5, 0x00000000);
-       nv_icmd(priv, 0x000004c6, 0x00000000);
-       nv_icmd(priv, 0x000004c7, 0x00000000);
-       nv_icmd(priv, 0x000004c8, 0x00000000);
-       nv_icmd(priv, 0x000004c9, 0x00000000);
-       nv_icmd(priv, 0x000004ca, 0x00000000);
-       nv_icmd(priv, 0x000004cb, 0x00000000);
-       nv_icmd(priv, 0x000004cc, 0x00000000);
-       nv_icmd(priv, 0x000004cd, 0x00000000);
-       nv_icmd(priv, 0x000004ce, 0x00000000);
-       nv_icmd(priv, 0x000004cf, 0x00000000);
-       nv_icmd(priv, 0x00000510, 0x3f800000);
-       nv_icmd(priv, 0x00000511, 0x3f800000);
-       nv_icmd(priv, 0x00000512, 0x3f800000);
-       nv_icmd(priv, 0x00000513, 0x3f800000);
-       nv_icmd(priv, 0x00000514, 0x3f800000);
-       nv_icmd(priv, 0x00000515, 0x3f800000);
-       nv_icmd(priv, 0x00000516, 0x3f800000);
-       nv_icmd(priv, 0x00000517, 0x3f800000);
-       nv_icmd(priv, 0x00000518, 0x3f800000);
-       nv_icmd(priv, 0x00000519, 0x3f800000);
-       nv_icmd(priv, 0x0000051a, 0x3f800000);
-       nv_icmd(priv, 0x0000051b, 0x3f800000);
-       nv_icmd(priv, 0x0000051c, 0x3f800000);
-       nv_icmd(priv, 0x0000051d, 0x3f800000);
-       nv_icmd(priv, 0x0000051e, 0x3f800000);
-       nv_icmd(priv, 0x0000051f, 0x3f800000);
-       nv_icmd(priv, 0x00000520, 0x000002b6);
-       nv_icmd(priv, 0x00000529, 0x00000001);
-       nv_icmd(priv, 0x00000530, 0xffff0000);
-       nv_icmd(priv, 0x00000531, 0xffff0000);
-       nv_icmd(priv, 0x00000532, 0xffff0000);
-       nv_icmd(priv, 0x00000533, 0xffff0000);
-       nv_icmd(priv, 0x00000534, 0xffff0000);
-       nv_icmd(priv, 0x00000535, 0xffff0000);
-       nv_icmd(priv, 0x00000536, 0xffff0000);
-       nv_icmd(priv, 0x00000537, 0xffff0000);
-       nv_icmd(priv, 0x00000538, 0xffff0000);
-       nv_icmd(priv, 0x00000539, 0xffff0000);
-       nv_icmd(priv, 0x0000053a, 0xffff0000);
-       nv_icmd(priv, 0x0000053b, 0xffff0000);
-       nv_icmd(priv, 0x0000053c, 0xffff0000);
-       nv_icmd(priv, 0x0000053d, 0xffff0000);
-       nv_icmd(priv, 0x0000053e, 0xffff0000);
-       nv_icmd(priv, 0x0000053f, 0xffff0000);
-       nv_icmd(priv, 0x00000585, 0x0000003f);
-       nv_icmd(priv, 0x00000576, 0x00000003);
-       if (nv_device(priv)->chipset == 0xc1 ||
-           nv_device(priv)->chipset >= 0xd0)
-               nv_icmd(priv, 0x0000057b, 0x00000059);
-       nv_icmd(priv, 0x00000586, 0x00000040);
-       nv_icmd(priv, 0x00000582, 0x00000080);
-       nv_icmd(priv, 0x00000583, 0x00000080);
-       nv_icmd(priv, 0x000005c2, 0x00000001);
-       nv_icmd(priv, 0x00000638, 0x00000001);
-       nv_icmd(priv, 0x00000639, 0x00000001);
-       nv_icmd(priv, 0x0000063a, 0x00000002);
-       nv_icmd(priv, 0x0000063b, 0x00000001);
-       nv_icmd(priv, 0x0000063c, 0x00000001);
-       nv_icmd(priv, 0x0000063d, 0x00000002);
-       nv_icmd(priv, 0x0000063e, 0x00000001);
-       nv_icmd(priv, 0x000008b8, 0x00000001);
-       nv_icmd(priv, 0x000008b9, 0x00000001);
-       nv_icmd(priv, 0x000008ba, 0x00000001);
-       nv_icmd(priv, 0x000008bb, 0x00000001);
-       nv_icmd(priv, 0x000008bc, 0x00000001);
-       nv_icmd(priv, 0x000008bd, 0x00000001);
-       nv_icmd(priv, 0x000008be, 0x00000001);
-       nv_icmd(priv, 0x000008bf, 0x00000001);
-       nv_icmd(priv, 0x00000900, 0x00000001);
-       nv_icmd(priv, 0x00000901, 0x00000001);
-       nv_icmd(priv, 0x00000902, 0x00000001);
-       nv_icmd(priv, 0x00000903, 0x00000001);
-       nv_icmd(priv, 0x00000904, 0x00000001);
-       nv_icmd(priv, 0x00000905, 0x00000001);
-       nv_icmd(priv, 0x00000906, 0x00000001);
-       nv_icmd(priv, 0x00000907, 0x00000001);
-       nv_icmd(priv, 0x00000908, 0x00000002);
-       nv_icmd(priv, 0x00000909, 0x00000002);
-       nv_icmd(priv, 0x0000090a, 0x00000002);
-       nv_icmd(priv, 0x0000090b, 0x00000002);
-       nv_icmd(priv, 0x0000090c, 0x00000002);
-       nv_icmd(priv, 0x0000090d, 0x00000002);
-       nv_icmd(priv, 0x0000090e, 0x00000002);
-       nv_icmd(priv, 0x0000090f, 0x00000002);
-       nv_icmd(priv, 0x00000910, 0x00000001);
-       nv_icmd(priv, 0x00000911, 0x00000001);
-       nv_icmd(priv, 0x00000912, 0x00000001);
-       nv_icmd(priv, 0x00000913, 0x00000001);
-       nv_icmd(priv, 0x00000914, 0x00000001);
-       nv_icmd(priv, 0x00000915, 0x00000001);
-       nv_icmd(priv, 0x00000916, 0x00000001);
-       nv_icmd(priv, 0x00000917, 0x00000001);
-       nv_icmd(priv, 0x00000918, 0x00000001);
-       nv_icmd(priv, 0x00000919, 0x00000001);
-       nv_icmd(priv, 0x0000091a, 0x00000001);
-       nv_icmd(priv, 0x0000091b, 0x00000001);
-       nv_icmd(priv, 0x0000091c, 0x00000001);
-       nv_icmd(priv, 0x0000091d, 0x00000001);
-       nv_icmd(priv, 0x0000091e, 0x00000001);
-       nv_icmd(priv, 0x0000091f, 0x00000001);
-       nv_icmd(priv, 0x00000920, 0x00000002);
-       nv_icmd(priv, 0x00000921, 0x00000002);
-       nv_icmd(priv, 0x00000922, 0x00000002);
-       nv_icmd(priv, 0x00000923, 0x00000002);
-       nv_icmd(priv, 0x00000924, 0x00000002);
-       nv_icmd(priv, 0x00000925, 0x00000002);
-       nv_icmd(priv, 0x00000926, 0x00000002);
-       nv_icmd(priv, 0x00000927, 0x00000002);
-       nv_icmd(priv, 0x00000928, 0x00000001);
-       nv_icmd(priv, 0x00000929, 0x00000001);
-       nv_icmd(priv, 0x0000092a, 0x00000001);
-       nv_icmd(priv, 0x0000092b, 0x00000001);
-       nv_icmd(priv, 0x0000092c, 0x00000001);
-       nv_icmd(priv, 0x0000092d, 0x00000001);
-       nv_icmd(priv, 0x0000092e, 0x00000001);
-       nv_icmd(priv, 0x0000092f, 0x00000001);
-       nv_icmd(priv, 0x00000648, 0x00000001);
-       nv_icmd(priv, 0x00000649, 0x00000001);
-       nv_icmd(priv, 0x0000064a, 0x00000001);
-       nv_icmd(priv, 0x0000064b, 0x00000001);
-       nv_icmd(priv, 0x0000064c, 0x00000001);
-       nv_icmd(priv, 0x0000064d, 0x00000001);
-       nv_icmd(priv, 0x0000064e, 0x00000001);
-       nv_icmd(priv, 0x0000064f, 0x00000001);
-       nv_icmd(priv, 0x00000650, 0x00000001);
-       nv_icmd(priv, 0x00000658, 0x0000000f);
-       nv_icmd(priv, 0x000007ff, 0x0000000a);
-       nv_icmd(priv, 0x0000066a, 0x40000000);
-       nv_icmd(priv, 0x0000066b, 0x10000000);
-       nv_icmd(priv, 0x0000066c, 0xffff0000);
-       nv_icmd(priv, 0x0000066d, 0xffff0000);
-       nv_icmd(priv, 0x000007af, 0x00000008);
-       nv_icmd(priv, 0x000007b0, 0x00000008);
-       nv_icmd(priv, 0x000007f6, 0x00000001);
-       nv_icmd(priv, 0x000006b2, 0x00000055);
-       nv_icmd(priv, 0x000007ad, 0x00000003);
-       nv_icmd(priv, 0x00000937, 0x00000001);
-       nv_icmd(priv, 0x00000971, 0x00000008);
-       nv_icmd(priv, 0x00000972, 0x00000040);
-       nv_icmd(priv, 0x00000973, 0x0000012c);
-       nv_icmd(priv, 0x0000097c, 0x00000040);
-       nv_icmd(priv, 0x00000979, 0x00000003);
-       nv_icmd(priv, 0x00000975, 0x00000020);
-       nv_icmd(priv, 0x00000976, 0x00000001);
-       nv_icmd(priv, 0x00000977, 0x00000020);
-       nv_icmd(priv, 0x00000978, 0x00000001);
-       nv_icmd(priv, 0x00000957, 0x00000003);
-       nv_icmd(priv, 0x0000095e, 0x20164010);
-       nv_icmd(priv, 0x0000095f, 0x00000020);
-       if (nv_device(priv)->chipset >= 0xd0)
-               nv_icmd(priv, 0x0000097d, 0x00000020);
-       nv_icmd(priv, 0x00000683, 0x00000006);
-       nv_icmd(priv, 0x00000685, 0x003fffff);
-       nv_icmd(priv, 0x00000687, 0x00000c48);
-       nv_icmd(priv, 0x000006a0, 0x00000005);
-       nv_icmd(priv, 0x00000840, 0x00300008);
-       nv_icmd(priv, 0x00000841, 0x04000080);
-       nv_icmd(priv, 0x00000842, 0x00300008);
-       nv_icmd(priv, 0x00000843, 0x04000080);
-       nv_icmd(priv, 0x00000818, 0x00000000);
-       nv_icmd(priv, 0x00000819, 0x00000000);
-       nv_icmd(priv, 0x0000081a, 0x00000000);
-       nv_icmd(priv, 0x0000081b, 0x00000000);
-       nv_icmd(priv, 0x0000081c, 0x00000000);
-       nv_icmd(priv, 0x0000081d, 0x00000000);
-       nv_icmd(priv, 0x0000081e, 0x00000000);
-       nv_icmd(priv, 0x0000081f, 0x00000000);
-       nv_icmd(priv, 0x00000848, 0x00000000);
-       nv_icmd(priv, 0x00000849, 0x00000000);
-       nv_icmd(priv, 0x0000084a, 0x00000000);
-       nv_icmd(priv, 0x0000084b, 0x00000000);
-       nv_icmd(priv, 0x0000084c, 0x00000000);
-       nv_icmd(priv, 0x0000084d, 0x00000000);
-       nv_icmd(priv, 0x0000084e, 0x00000000);
-       nv_icmd(priv, 0x0000084f, 0x00000000);
-       nv_icmd(priv, 0x00000850, 0x00000000);
-       nv_icmd(priv, 0x00000851, 0x00000000);
-       nv_icmd(priv, 0x00000852, 0x00000000);
-       nv_icmd(priv, 0x00000853, 0x00000000);
-       nv_icmd(priv, 0x00000854, 0x00000000);
-       nv_icmd(priv, 0x00000855, 0x00000000);
-       nv_icmd(priv, 0x00000856, 0x00000000);
-       nv_icmd(priv, 0x00000857, 0x00000000);
-       nv_icmd(priv, 0x00000738, 0x00000000);
-       nv_icmd(priv, 0x000006aa, 0x00000001);
-       nv_icmd(priv, 0x000006ab, 0x00000002);
-       nv_icmd(priv, 0x000006ac, 0x00000080);
-       nv_icmd(priv, 0x000006ad, 0x00000100);
-       nv_icmd(priv, 0x000006ae, 0x00000100);
-       nv_icmd(priv, 0x000006b1, 0x00000011);
-       nv_icmd(priv, 0x000006bb, 0x000000cf);
-       nv_icmd(priv, 0x000006ce, 0x2a712488);
-       nv_icmd(priv, 0x00000739, 0x4085c000);
-       nv_icmd(priv, 0x0000073a, 0x00000080);
-       nv_icmd(priv, 0x00000786, 0x80000100);
-       nv_icmd(priv, 0x0000073c, 0x00010100);
-       nv_icmd(priv, 0x0000073d, 0x02800000);
-       nv_icmd(priv, 0x00000787, 0x000000cf);
-       nv_icmd(priv, 0x0000078c, 0x00000008);
-       nv_icmd(priv, 0x00000792, 0x00000001);
-       nv_icmd(priv, 0x00000794, 0x00000001);
-       nv_icmd(priv, 0x00000795, 0x00000001);
-       nv_icmd(priv, 0x00000796, 0x00000001);
-       nv_icmd(priv, 0x00000797, 0x000000cf);
-       nv_icmd(priv, 0x00000836, 0x00000001);
-       nv_icmd(priv, 0x0000079a, 0x00000002);
-       nv_icmd(priv, 0x00000833, 0x04444480);
-       nv_icmd(priv, 0x000007a1, 0x00000001);
-       nv_icmd(priv, 0x000007a3, 0x00000001);
-       nv_icmd(priv, 0x000007a4, 0x00000001);
-       nv_icmd(priv, 0x000007a5, 0x00000001);
-       nv_icmd(priv, 0x00000831, 0x00000004);
-       nv_icmd(priv, 0x0000080c, 0x00000002);
-       nv_icmd(priv, 0x0000080d, 0x00000100);
-       nv_icmd(priv, 0x0000080e, 0x00000100);
-       nv_icmd(priv, 0x0000080f, 0x00000001);
-       nv_icmd(priv, 0x00000823, 0x00000002);
-       nv_icmd(priv, 0x00000824, 0x00000100);
-       nv_icmd(priv, 0x00000825, 0x00000100);
-       nv_icmd(priv, 0x00000826, 0x00000001);
-       nv_icmd(priv, 0x0000095d, 0x00000001);
-       nv_icmd(priv, 0x0000082b, 0x00000004);
-       nv_icmd(priv, 0x00000942, 0x00010001);
-       nv_icmd(priv, 0x00000943, 0x00000001);
-       nv_icmd(priv, 0x00000944, 0x00000022);
-       nv_icmd(priv, 0x000007c5, 0x00010001);
-       nv_icmd(priv, 0x00000834, 0x00000001);
-       nv_icmd(priv, 0x000007c7, 0x00000001);
-       nv_icmd(priv, 0x0000c1b0, 0x0000000f);
-       nv_icmd(priv, 0x0000c1b1, 0x0000000f);
-       nv_icmd(priv, 0x0000c1b2, 0x0000000f);
-       nv_icmd(priv, 0x0000c1b3, 0x0000000f);
-       nv_icmd(priv, 0x0000c1b4, 0x0000000f);
-       nv_icmd(priv, 0x0000c1b5, 0x0000000f);
-       nv_icmd(priv, 0x0000c1b6, 0x0000000f);
-       nv_icmd(priv, 0x0000c1b7, 0x0000000f);
-       nv_icmd(priv, 0x0000c1b8, 0x0fac6881);
-       nv_icmd(priv, 0x0000c1b9, 0x00fac688);
-       nv_icmd(priv, 0x0001e100, 0x00000001);
-       nv_icmd(priv, 0x00001000, 0x00000002);
-       nv_icmd(priv, 0x000006aa, 0x00000001);
-       nv_icmd(priv, 0x000006ad, 0x00000100);
-       nv_icmd(priv, 0x000006ae, 0x00000100);
-       nv_icmd(priv, 0x000006b1, 0x00000011);
-       nv_icmd(priv, 0x0000078c, 0x00000008);
-       nv_icmd(priv, 0x00000792, 0x00000001);
-       nv_icmd(priv, 0x00000794, 0x00000001);
-       nv_icmd(priv, 0x00000795, 0x00000001);
-       nv_icmd(priv, 0x00000796, 0x00000001);
-       nv_icmd(priv, 0x00000797, 0x000000cf);
-       nv_icmd(priv, 0x0000079a, 0x00000002);
-       nv_icmd(priv, 0x00000833, 0x04444480);
-       nv_icmd(priv, 0x000007a1, 0x00000001);
-       nv_icmd(priv, 0x000007a3, 0x00000001);
-       nv_icmd(priv, 0x000007a4, 0x00000001);
-       nv_icmd(priv, 0x000007a5, 0x00000001);
-       nv_icmd(priv, 0x00000831, 0x00000004);
-       nv_icmd(priv, 0x0001e100, 0x00000001);
-       nv_icmd(priv, 0x00001000, 0x00000014);
-       nv_icmd(priv, 0x00000351, 0x00000100);
-       nv_icmd(priv, 0x00000957, 0x00000003);
-       nv_icmd(priv, 0x0000095d, 0x00000001);
-       nv_icmd(priv, 0x0000082b, 0x00000004);
-       nv_icmd(priv, 0x00000942, 0x00010001);
-       nv_icmd(priv, 0x00000943, 0x00000001);
-       nv_icmd(priv, 0x000007c5, 0x00010001);
-       nv_icmd(priv, 0x00000834, 0x00000001);
-       nv_icmd(priv, 0x000007c7, 0x00000001);
-       nv_icmd(priv, 0x0001e100, 0x00000001);
-       nv_icmd(priv, 0x00001000, 0x00000001);
-       nv_icmd(priv, 0x0000080c, 0x00000002);
-       nv_icmd(priv, 0x0000080d, 0x00000100);
-       nv_icmd(priv, 0x0000080e, 0x00000100);
-       nv_icmd(priv, 0x0000080f, 0x00000001);
-       nv_icmd(priv, 0x00000823, 0x00000002);
-       nv_icmd(priv, 0x00000824, 0x00000100);
-       nv_icmd(priv, 0x00000825, 0x00000100);
-       nv_icmd(priv, 0x00000826, 0x00000001);
-       nv_icmd(priv, 0x0001e100, 0x00000001);
-       nv_wr32(priv, 0x400208, 0x00000000);
-       nv_wr32(priv, 0x404154, 0x00000400);
-
-       nvc0_grctx_generate_9097(priv);
-       if (fermi >= 0x9197)
-               nvc0_grctx_generate_9197(priv);
-       if (fermi >= 0x9297)
-               nvc0_grctx_generate_9297(priv);
-       nvc0_grctx_generate_902d(priv);
-       nvc0_grctx_generate_9039(priv);
-       nvc0_grctx_generate_90c0(priv);
 
-       nv_wr32(priv, 0x000260, r000260);
-
-       return nvc0_grctx_fini(&info);
+done:
+       nouveau_gpuobj_ref(NULL, &chan);
+       return ret;
 }
+
+struct nvc0_graph_init *
+nvc0_grctx_init_hub[] = {
+       nvc0_grctx_init_base,
+       nvc0_grctx_init_unk40xx,
+       nvc0_grctx_init_unk44xx,
+       nvc0_grctx_init_unk46xx,
+       nvc0_grctx_init_unk47xx,
+       nvc0_grctx_init_unk58xx,
+       nvc0_grctx_init_unk60xx,
+       nvc0_grctx_init_unk64xx,
+       nvc0_grctx_init_unk78xx,
+       nvc0_grctx_init_unk80xx,
+       nvc0_grctx_init_rop,
+       NULL
+};
+
+static struct nvc0_graph_init *
+nvc0_grctx_init_gpc[] = {
+       nvc0_grctx_init_gpc_0,
+       nvc0_grctx_init_gpc_1,
+       nvc0_grctx_init_tpc,
+       NULL
+};
+
+struct nvc0_graph_init
+nvc0_grctx_init_mthd_magic[] = {
+       { 0x3410, 1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_mthd
+nvc0_grctx_init_mthd[] = {
+       { 0x9097, nvc0_grctx_init_9097, },
+       { 0x902d, nvc0_grctx_init_902d, },
+       { 0x9039, nvc0_grctx_init_9039, },
+       { 0x90c0, nvc0_grctx_init_90c0, },
+       { 0x902d, nvc0_grctx_init_mthd_magic, },
+       {}
+};
+
+struct nouveau_oclass *
+nvc0_grctx_oclass = &(struct nvc0_grctx_oclass) {
+       .base.handle = NV_ENGCTX(GR, 0xc0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_context_ctor,
+               .dtor = nvc0_graph_context_dtor,
+               .init = _nouveau_graph_context_init,
+               .fini = _nouveau_graph_context_fini,
+               .rd32 = _nouveau_graph_context_rd32,
+               .wr32 = _nouveau_graph_context_wr32,
+       },
+       .main = nvc0_grctx_generate_main,
+       .mods = nvc0_grctx_generate_mods,
+       .unkn = nvc0_grctx_generate_unkn,
+       .hub  = nvc0_grctx_init_hub,
+       .gpc  = nvc0_grctx_init_gpc,
+       .icmd = nvc0_grctx_init_icmd,
+       .mthd = nvc0_grctx_init_mthd,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c
new file mode 100644 (file)
index 0000000..e5be3ee
--- /dev/null
@@ -0,0 +1,823 @@
+/*
+ * 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 "nvc0.h"
+
+static struct nvc0_graph_init
+nvc1_grctx_init_icmd[] = {
+       { 0x001000,   1, 0x01, 0x00000004 },
+       { 0x0000a9,   1, 0x01, 0x0000ffff },
+       { 0x000038,   1, 0x01, 0x0fac6881 },
+       { 0x00003d,   1, 0x01, 0x00000001 },
+       { 0x0000e8,   8, 0x01, 0x00000400 },
+       { 0x000078,   8, 0x01, 0x00000300 },
+       { 0x000050,   1, 0x01, 0x00000011 },
+       { 0x000058,   8, 0x01, 0x00000008 },
+       { 0x000208,   8, 0x01, 0x00000001 },
+       { 0x000081,   1, 0x01, 0x00000001 },
+       { 0x000085,   1, 0x01, 0x00000004 },
+       { 0x000088,   1, 0x01, 0x00000400 },
+       { 0x000090,   1, 0x01, 0x00000300 },
+       { 0x000098,   1, 0x01, 0x00001001 },
+       { 0x0000e3,   1, 0x01, 0x00000001 },
+       { 0x0000da,   1, 0x01, 0x00000001 },
+       { 0x0000f8,   1, 0x01, 0x00000003 },
+       { 0x0000fa,   1, 0x01, 0x00000001 },
+       { 0x00009f,   4, 0x01, 0x0000ffff },
+       { 0x0000b1,   1, 0x01, 0x00000001 },
+       { 0x0000b2,  40, 0x01, 0x00000000 },
+       { 0x000210,   8, 0x01, 0x00000040 },
+       { 0x000218,   8, 0x01, 0x0000c080 },
+       { 0x0000ad,   1, 0x01, 0x0000013e },
+       { 0x0000e1,   1, 0x01, 0x00000010 },
+       { 0x000290,  16, 0x01, 0x00000000 },
+       { 0x0003b0,  16, 0x01, 0x00000000 },
+       { 0x0002a0,  16, 0x01, 0x00000000 },
+       { 0x000420,  16, 0x01, 0x00000000 },
+       { 0x0002b0,  16, 0x01, 0x00000000 },
+       { 0x000430,  16, 0x01, 0x00000000 },
+       { 0x0002c0,  16, 0x01, 0x00000000 },
+       { 0x0004d0,  16, 0x01, 0x00000000 },
+       { 0x000720,  16, 0x01, 0x00000000 },
+       { 0x0008c0,  16, 0x01, 0x00000000 },
+       { 0x000890,  16, 0x01, 0x00000000 },
+       { 0x0008e0,  16, 0x01, 0x00000000 },
+       { 0x0008a0,  16, 0x01, 0x00000000 },
+       { 0x0008f0,  16, 0x01, 0x00000000 },
+       { 0x00094c,   1, 0x01, 0x000000ff },
+       { 0x00094d,   1, 0x01, 0xffffffff },
+       { 0x00094e,   1, 0x01, 0x00000002 },
+       { 0x0002ec,   1, 0x01, 0x00000001 },
+       { 0x000303,   1, 0x01, 0x00000001 },
+       { 0x0002e6,   1, 0x01, 0x00000001 },
+       { 0x000466,   1, 0x01, 0x00000052 },
+       { 0x000301,   1, 0x01, 0x3f800000 },
+       { 0x000304,   1, 0x01, 0x30201000 },
+       { 0x000305,   1, 0x01, 0x70605040 },
+       { 0x000306,   1, 0x01, 0xb8a89888 },
+       { 0x000307,   1, 0x01, 0xf8e8d8c8 },
+       { 0x00030a,   1, 0x01, 0x00ffff00 },
+       { 0x00030b,   1, 0x01, 0x0000001a },
+       { 0x00030c,   1, 0x01, 0x00000001 },
+       { 0x000318,   1, 0x01, 0x00000001 },
+       { 0x000340,   1, 0x01, 0x00000000 },
+       { 0x000375,   1, 0x01, 0x00000001 },
+       { 0x000351,   1, 0x01, 0x00000100 },
+       { 0x00037d,   1, 0x01, 0x00000006 },
+       { 0x0003a0,   1, 0x01, 0x00000002 },
+       { 0x0003aa,   1, 0x01, 0x00000001 },
+       { 0x0003a9,   1, 0x01, 0x00000001 },
+       { 0x000380,   1, 0x01, 0x00000001 },
+       { 0x000360,   1, 0x01, 0x00000040 },
+       { 0x000366,   2, 0x01, 0x00000000 },
+       { 0x000368,   1, 0x01, 0x00001fff },
+       { 0x000370,   2, 0x01, 0x00000000 },
+       { 0x000372,   1, 0x01, 0x003fffff },
+       { 0x00037a,   1, 0x01, 0x00000012 },
+       { 0x0005e0,   5, 0x01, 0x00000022 },
+       { 0x000619,   1, 0x01, 0x00000003 },
+       { 0x000811,   1, 0x01, 0x00000003 },
+       { 0x000812,   1, 0x01, 0x00000004 },
+       { 0x000813,   1, 0x01, 0x00000006 },
+       { 0x000814,   1, 0x01, 0x00000008 },
+       { 0x000815,   1, 0x01, 0x0000000b },
+       { 0x000800,   6, 0x01, 0x00000001 },
+       { 0x000632,   1, 0x01, 0x00000001 },
+       { 0x000633,   1, 0x01, 0x00000002 },
+       { 0x000634,   1, 0x01, 0x00000003 },
+       { 0x000635,   1, 0x01, 0x00000004 },
+       { 0x000654,   1, 0x01, 0x3f800000 },
+       { 0x000657,   1, 0x01, 0x3f800000 },
+       { 0x000655,   2, 0x01, 0x3f800000 },
+       { 0x0006cd,   1, 0x01, 0x3f800000 },
+       { 0x0007f5,   1, 0x01, 0x3f800000 },
+       { 0x0007dc,   1, 0x01, 0x39291909 },
+       { 0x0007dd,   1, 0x01, 0x79695949 },
+       { 0x0007de,   1, 0x01, 0xb9a99989 },
+       { 0x0007df,   1, 0x01, 0xf9e9d9c9 },
+       { 0x0007e8,   1, 0x01, 0x00003210 },
+       { 0x0007e9,   1, 0x01, 0x00007654 },
+       { 0x0007ea,   1, 0x01, 0x00000098 },
+       { 0x0007ec,   1, 0x01, 0x39291909 },
+       { 0x0007ed,   1, 0x01, 0x79695949 },
+       { 0x0007ee,   1, 0x01, 0xb9a99989 },
+       { 0x0007ef,   1, 0x01, 0xf9e9d9c9 },
+       { 0x0007f0,   1, 0x01, 0x00003210 },
+       { 0x0007f1,   1, 0x01, 0x00007654 },
+       { 0x0007f2,   1, 0x01, 0x00000098 },
+       { 0x0005a5,   1, 0x01, 0x00000001 },
+       { 0x000980, 128, 0x01, 0x00000000 },
+       { 0x000468,   1, 0x01, 0x00000004 },
+       { 0x00046c,   1, 0x01, 0x00000001 },
+       { 0x000470,  96, 0x01, 0x00000000 },
+       { 0x000510,  16, 0x01, 0x3f800000 },
+       { 0x000520,   1, 0x01, 0x000002b6 },
+       { 0x000529,   1, 0x01, 0x00000001 },
+       { 0x000530,  16, 0x01, 0xffff0000 },
+       { 0x000585,   1, 0x01, 0x0000003f },
+       { 0x000576,   1, 0x01, 0x00000003 },
+       { 0x00057b,   1, 0x01, 0x00000059 },
+       { 0x000586,   1, 0x01, 0x00000040 },
+       { 0x000582,   2, 0x01, 0x00000080 },
+       { 0x0005c2,   1, 0x01, 0x00000001 },
+       { 0x000638,   1, 0x01, 0x00000001 },
+       { 0x000639,   1, 0x01, 0x00000001 },
+       { 0x00063a,   1, 0x01, 0x00000002 },
+       { 0x00063b,   2, 0x01, 0x00000001 },
+       { 0x00063d,   1, 0x01, 0x00000002 },
+       { 0x00063e,   1, 0x01, 0x00000001 },
+       { 0x0008b8,   8, 0x01, 0x00000001 },
+       { 0x000900,   8, 0x01, 0x00000001 },
+       { 0x000908,   8, 0x01, 0x00000002 },
+       { 0x000910,  16, 0x01, 0x00000001 },
+       { 0x000920,   8, 0x01, 0x00000002 },
+       { 0x000928,   8, 0x01, 0x00000001 },
+       { 0x000648,   9, 0x01, 0x00000001 },
+       { 0x000658,   1, 0x01, 0x0000000f },
+       { 0x0007ff,   1, 0x01, 0x0000000a },
+       { 0x00066a,   1, 0x01, 0x40000000 },
+       { 0x00066b,   1, 0x01, 0x10000000 },
+       { 0x00066c,   2, 0x01, 0xffff0000 },
+       { 0x0007af,   2, 0x01, 0x00000008 },
+       { 0x0007f6,   1, 0x01, 0x00000001 },
+       { 0x0006b2,   1, 0x01, 0x00000055 },
+       { 0x0007ad,   1, 0x01, 0x00000003 },
+       { 0x000937,   1, 0x01, 0x00000001 },
+       { 0x000971,   1, 0x01, 0x00000008 },
+       { 0x000972,   1, 0x01, 0x00000040 },
+       { 0x000973,   1, 0x01, 0x0000012c },
+       { 0x00097c,   1, 0x01, 0x00000040 },
+       { 0x000979,   1, 0x01, 0x00000003 },
+       { 0x000975,   1, 0x01, 0x00000020 },
+       { 0x000976,   1, 0x01, 0x00000001 },
+       { 0x000977,   1, 0x01, 0x00000020 },
+       { 0x000978,   1, 0x01, 0x00000001 },
+       { 0x000957,   1, 0x01, 0x00000003 },
+       { 0x00095e,   1, 0x01, 0x20164010 },
+       { 0x00095f,   1, 0x01, 0x00000020 },
+       { 0x000683,   1, 0x01, 0x00000006 },
+       { 0x000685,   1, 0x01, 0x003fffff },
+       { 0x000687,   1, 0x01, 0x00000c48 },
+       { 0x0006a0,   1, 0x01, 0x00000005 },
+       { 0x000840,   1, 0x01, 0x00300008 },
+       { 0x000841,   1, 0x01, 0x04000080 },
+       { 0x000842,   1, 0x01, 0x00300008 },
+       { 0x000843,   1, 0x01, 0x04000080 },
+       { 0x000818,   8, 0x01, 0x00000000 },
+       { 0x000848,  16, 0x01, 0x00000000 },
+       { 0x000738,   1, 0x01, 0x00000000 },
+       { 0x0006aa,   1, 0x01, 0x00000001 },
+       { 0x0006ab,   1, 0x01, 0x00000002 },
+       { 0x0006ac,   1, 0x01, 0x00000080 },
+       { 0x0006ad,   2, 0x01, 0x00000100 },
+       { 0x0006b1,   1, 0x01, 0x00000011 },
+       { 0x0006bb,   1, 0x01, 0x000000cf },
+       { 0x0006ce,   1, 0x01, 0x2a712488 },
+       { 0x000739,   1, 0x01, 0x4085c000 },
+       { 0x00073a,   1, 0x01, 0x00000080 },
+       { 0x000786,   1, 0x01, 0x80000100 },
+       { 0x00073c,   1, 0x01, 0x00010100 },
+       { 0x00073d,   1, 0x01, 0x02800000 },
+       { 0x000787,   1, 0x01, 0x000000cf },
+       { 0x00078c,   1, 0x01, 0x00000008 },
+       { 0x000792,   1, 0x01, 0x00000001 },
+       { 0x000794,   1, 0x01, 0x00000001 },
+       { 0x000795,   2, 0x01, 0x00000001 },
+       { 0x000797,   1, 0x01, 0x000000cf },
+       { 0x000836,   1, 0x01, 0x00000001 },
+       { 0x00079a,   1, 0x01, 0x00000002 },
+       { 0x000833,   1, 0x01, 0x04444480 },
+       { 0x0007a1,   1, 0x01, 0x00000001 },
+       { 0x0007a3,   1, 0x01, 0x00000001 },
+       { 0x0007a4,   2, 0x01, 0x00000001 },
+       { 0x000831,   1, 0x01, 0x00000004 },
+       { 0x00080c,   1, 0x01, 0x00000002 },
+       { 0x00080d,   2, 0x01, 0x00000100 },
+       { 0x00080f,   1, 0x01, 0x00000001 },
+       { 0x000823,   1, 0x01, 0x00000002 },
+       { 0x000824,   2, 0x01, 0x00000100 },
+       { 0x000826,   1, 0x01, 0x00000001 },
+       { 0x00095d,   1, 0x01, 0x00000001 },
+       { 0x00082b,   1, 0x01, 0x00000004 },
+       { 0x000942,   1, 0x01, 0x00010001 },
+       { 0x000943,   1, 0x01, 0x00000001 },
+       { 0x000944,   1, 0x01, 0x00000022 },
+       { 0x0007c5,   1, 0x01, 0x00010001 },
+       { 0x000834,   1, 0x01, 0x00000001 },
+       { 0x0007c7,   1, 0x01, 0x00000001 },
+       { 0x00c1b0,   8, 0x01, 0x0000000f },
+       { 0x00c1b8,   1, 0x01, 0x0fac6881 },
+       { 0x00c1b9,   1, 0x01, 0x00fac688 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000002 },
+       { 0x0006aa,   1, 0x01, 0x00000001 },
+       { 0x0006ad,   2, 0x01, 0x00000100 },
+       { 0x0006b1,   1, 0x01, 0x00000011 },
+       { 0x00078c,   1, 0x01, 0x00000008 },
+       { 0x000792,   1, 0x01, 0x00000001 },
+       { 0x000794,   1, 0x01, 0x00000001 },
+       { 0x000795,   2, 0x01, 0x00000001 },
+       { 0x000797,   1, 0x01, 0x000000cf },
+       { 0x00079a,   1, 0x01, 0x00000002 },
+       { 0x000833,   1, 0x01, 0x04444480 },
+       { 0x0007a1,   1, 0x01, 0x00000001 },
+       { 0x0007a3,   1, 0x01, 0x00000001 },
+       { 0x0007a4,   2, 0x01, 0x00000001 },
+       { 0x000831,   1, 0x01, 0x00000004 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000014 },
+       { 0x000351,   1, 0x01, 0x00000100 },
+       { 0x000957,   1, 0x01, 0x00000003 },
+       { 0x00095d,   1, 0x01, 0x00000001 },
+       { 0x00082b,   1, 0x01, 0x00000004 },
+       { 0x000942,   1, 0x01, 0x00010001 },
+       { 0x000943,   1, 0x01, 0x00000001 },
+       { 0x0007c5,   1, 0x01, 0x00010001 },
+       { 0x000834,   1, 0x01, 0x00000001 },
+       { 0x0007c7,   1, 0x01, 0x00000001 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000001 },
+       { 0x00080c,   1, 0x01, 0x00000002 },
+       { 0x00080d,   2, 0x01, 0x00000100 },
+       { 0x00080f,   1, 0x01, 0x00000001 },
+       { 0x000823,   1, 0x01, 0x00000002 },
+       { 0x000824,   2, 0x01, 0x00000100 },
+       { 0x000826,   1, 0x01, 0x00000001 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc1_grctx_init_9097[] = {
+       { 0x000800,   8, 0x40, 0x00000000 },
+       { 0x000804,   8, 0x40, 0x00000000 },
+       { 0x000808,   8, 0x40, 0x00000400 },
+       { 0x00080c,   8, 0x40, 0x00000300 },
+       { 0x000810,   1, 0x04, 0x000000cf },
+       { 0x000850,   7, 0x40, 0x00000000 },
+       { 0x000814,   8, 0x40, 0x00000040 },
+       { 0x000818,   8, 0x40, 0x00000001 },
+       { 0x00081c,   8, 0x40, 0x00000000 },
+       { 0x000820,   8, 0x40, 0x00000000 },
+       { 0x002700,   8, 0x20, 0x00000000 },
+       { 0x002704,   8, 0x20, 0x00000000 },
+       { 0x002708,   8, 0x20, 0x00000000 },
+       { 0x00270c,   8, 0x20, 0x00000000 },
+       { 0x002710,   8, 0x20, 0x00014000 },
+       { 0x002714,   8, 0x20, 0x00000040 },
+       { 0x001c00,  16, 0x10, 0x00000000 },
+       { 0x001c04,  16, 0x10, 0x00000000 },
+       { 0x001c08,  16, 0x10, 0x00000000 },
+       { 0x001c0c,  16, 0x10, 0x00000000 },
+       { 0x001d00,  16, 0x10, 0x00000000 },
+       { 0x001d04,  16, 0x10, 0x00000000 },
+       { 0x001d08,  16, 0x10, 0x00000000 },
+       { 0x001d0c,  16, 0x10, 0x00000000 },
+       { 0x001f00,  16, 0x08, 0x00000000 },
+       { 0x001f04,  16, 0x08, 0x00000000 },
+       { 0x001f80,  16, 0x08, 0x00000000 },
+       { 0x001f84,  16, 0x08, 0x00000000 },
+       { 0x002200,   5, 0x10, 0x00000022 },
+       { 0x002000,   1, 0x04, 0x00000000 },
+       { 0x002040,   1, 0x04, 0x00000011 },
+       { 0x002080,   1, 0x04, 0x00000020 },
+       { 0x0020c0,   1, 0x04, 0x00000030 },
+       { 0x002100,   1, 0x04, 0x00000040 },
+       { 0x002140,   1, 0x04, 0x00000051 },
+       { 0x00200c,   6, 0x40, 0x00000001 },
+       { 0x002010,   1, 0x04, 0x00000000 },
+       { 0x002050,   1, 0x04, 0x00000000 },
+       { 0x002090,   1, 0x04, 0x00000001 },
+       { 0x0020d0,   1, 0x04, 0x00000002 },
+       { 0x002110,   1, 0x04, 0x00000003 },
+       { 0x002150,   1, 0x04, 0x00000004 },
+       { 0x000380,   4, 0x20, 0x00000000 },
+       { 0x000384,   4, 0x20, 0x00000000 },
+       { 0x000388,   4, 0x20, 0x00000000 },
+       { 0x00038c,   4, 0x20, 0x00000000 },
+       { 0x000700,   4, 0x10, 0x00000000 },
+       { 0x000704,   4, 0x10, 0x00000000 },
+       { 0x000708,   4, 0x10, 0x00000000 },
+       { 0x002800, 128, 0x04, 0x00000000 },
+       { 0x000a00,  16, 0x20, 0x00000000 },
+       { 0x000a04,  16, 0x20, 0x00000000 },
+       { 0x000a08,  16, 0x20, 0x00000000 },
+       { 0x000a0c,  16, 0x20, 0x00000000 },
+       { 0x000a10,  16, 0x20, 0x00000000 },
+       { 0x000a14,  16, 0x20, 0x00000000 },
+       { 0x000c00,  16, 0x10, 0x00000000 },
+       { 0x000c04,  16, 0x10, 0x00000000 },
+       { 0x000c08,  16, 0x10, 0x00000000 },
+       { 0x000c0c,  16, 0x10, 0x3f800000 },
+       { 0x000d00,   8, 0x08, 0xffff0000 },
+       { 0x000d04,   8, 0x08, 0xffff0000 },
+       { 0x000e00,  16, 0x10, 0x00000000 },
+       { 0x000e04,  16, 0x10, 0xffff0000 },
+       { 0x000e08,  16, 0x10, 0xffff0000 },
+       { 0x000d40,   4, 0x08, 0x00000000 },
+       { 0x000d44,   4, 0x08, 0x00000000 },
+       { 0x001e00,   8, 0x20, 0x00000001 },
+       { 0x001e04,   8, 0x20, 0x00000001 },
+       { 0x001e08,   8, 0x20, 0x00000002 },
+       { 0x001e0c,   8, 0x20, 0x00000001 },
+       { 0x001e10,   8, 0x20, 0x00000001 },
+       { 0x001e14,   8, 0x20, 0x00000002 },
+       { 0x001e18,   8, 0x20, 0x00000001 },
+       { 0x00030c,   1, 0x04, 0x00000001 },
+       { 0x001944,   1, 0x04, 0x00000000 },
+       { 0x001514,   1, 0x04, 0x00000000 },
+       { 0x000d68,   1, 0x04, 0x0000ffff },
+       { 0x00121c,   1, 0x04, 0x0fac6881 },
+       { 0x000fac,   1, 0x04, 0x00000001 },
+       { 0x001538,   1, 0x04, 0x00000001 },
+       { 0x000fe0,   2, 0x04, 0x00000000 },
+       { 0x000fe8,   1, 0x04, 0x00000014 },
+       { 0x000fec,   1, 0x04, 0x00000040 },
+       { 0x000ff0,   1, 0x04, 0x00000000 },
+       { 0x00179c,   1, 0x04, 0x00000000 },
+       { 0x001228,   1, 0x04, 0x00000400 },
+       { 0x00122c,   1, 0x04, 0x00000300 },
+       { 0x001230,   1, 0x04, 0x00010001 },
+       { 0x0007f8,   1, 0x04, 0x00000000 },
+       { 0x0015b4,   1, 0x04, 0x00000001 },
+       { 0x0015cc,   1, 0x04, 0x00000000 },
+       { 0x001534,   1, 0x04, 0x00000000 },
+       { 0x000fb0,   1, 0x04, 0x00000000 },
+       { 0x0015d0,   1, 0x04, 0x00000000 },
+       { 0x00153c,   1, 0x04, 0x00000000 },
+       { 0x0016b4,   1, 0x04, 0x00000003 },
+       { 0x000fbc,   4, 0x04, 0x0000ffff },
+       { 0x000df8,   2, 0x04, 0x00000000 },
+       { 0x001948,   1, 0x04, 0x00000000 },
+       { 0x001970,   1, 0x04, 0x00000001 },
+       { 0x00161c,   1, 0x04, 0x000009f0 },
+       { 0x000dcc,   1, 0x04, 0x00000010 },
+       { 0x00163c,   1, 0x04, 0x00000000 },
+       { 0x0015e4,   1, 0x04, 0x00000000 },
+       { 0x001160,  32, 0x04, 0x25e00040 },
+       { 0x001880,  32, 0x04, 0x00000000 },
+       { 0x000f84,   2, 0x04, 0x00000000 },
+       { 0x0017c8,   2, 0x04, 0x00000000 },
+       { 0x0017d0,   1, 0x04, 0x000000ff },
+       { 0x0017d4,   1, 0x04, 0xffffffff },
+       { 0x0017d8,   1, 0x04, 0x00000002 },
+       { 0x0017dc,   1, 0x04, 0x00000000 },
+       { 0x0015f4,   2, 0x04, 0x00000000 },
+       { 0x001434,   2, 0x04, 0x00000000 },
+       { 0x000d74,   1, 0x04, 0x00000000 },
+       { 0x000dec,   1, 0x04, 0x00000001 },
+       { 0x0013a4,   1, 0x04, 0x00000000 },
+       { 0x001318,   1, 0x04, 0x00000001 },
+       { 0x001644,   1, 0x04, 0x00000000 },
+       { 0x000748,   1, 0x04, 0x00000000 },
+       { 0x000de8,   1, 0x04, 0x00000000 },
+       { 0x001648,   1, 0x04, 0x00000000 },
+       { 0x0012a4,   1, 0x04, 0x00000000 },
+       { 0x001120,   4, 0x04, 0x00000000 },
+       { 0x001118,   1, 0x04, 0x00000000 },
+       { 0x00164c,   1, 0x04, 0x00000000 },
+       { 0x001658,   1, 0x04, 0x00000000 },
+       { 0x001910,   1, 0x04, 0x00000290 },
+       { 0x001518,   1, 0x04, 0x00000000 },
+       { 0x00165c,   1, 0x04, 0x00000001 },
+       { 0x001520,   1, 0x04, 0x00000000 },
+       { 0x001604,   1, 0x04, 0x00000000 },
+       { 0x001570,   1, 0x04, 0x00000000 },
+       { 0x0013b0,   2, 0x04, 0x3f800000 },
+       { 0x00020c,   1, 0x04, 0x00000000 },
+       { 0x001670,   1, 0x04, 0x30201000 },
+       { 0x001674,   1, 0x04, 0x70605040 },
+       { 0x001678,   1, 0x04, 0xb8a89888 },
+       { 0x00167c,   1, 0x04, 0xf8e8d8c8 },
+       { 0x00166c,   1, 0x04, 0x00000000 },
+       { 0x001680,   1, 0x04, 0x00ffff00 },
+       { 0x0012d0,   1, 0x04, 0x00000003 },
+       { 0x0012d4,   1, 0x04, 0x00000002 },
+       { 0x001684,   2, 0x04, 0x00000000 },
+       { 0x000dac,   2, 0x04, 0x00001b02 },
+       { 0x000db4,   1, 0x04, 0x00000000 },
+       { 0x00168c,   1, 0x04, 0x00000000 },
+       { 0x0015bc,   1, 0x04, 0x00000000 },
+       { 0x00156c,   1, 0x04, 0x00000000 },
+       { 0x00187c,   1, 0x04, 0x00000000 },
+       { 0x001110,   1, 0x04, 0x00000001 },
+       { 0x000dc0,   3, 0x04, 0x00000000 },
+       { 0x001234,   1, 0x04, 0x00000000 },
+       { 0x001690,   1, 0x04, 0x00000000 },
+       { 0x0012ac,   1, 0x04, 0x00000001 },
+       { 0x0002c4,   1, 0x04, 0x00000000 },
+       { 0x000790,   5, 0x04, 0x00000000 },
+       { 0x00077c,   1, 0x04, 0x00000000 },
+       { 0x001000,   1, 0x04, 0x00000010 },
+       { 0x0010fc,   1, 0x04, 0x00000000 },
+       { 0x001290,   1, 0x04, 0x00000000 },
+       { 0x000218,   1, 0x04, 0x00000010 },
+       { 0x0012d8,   1, 0x04, 0x00000000 },
+       { 0x0012dc,   1, 0x04, 0x00000010 },
+       { 0x000d94,   1, 0x04, 0x00000001 },
+       { 0x00155c,   2, 0x04, 0x00000000 },
+       { 0x001564,   1, 0x04, 0x00001fff },
+       { 0x001574,   2, 0x04, 0x00000000 },
+       { 0x00157c,   1, 0x04, 0x003fffff },
+       { 0x001354,   1, 0x04, 0x00000000 },
+       { 0x001664,   1, 0x04, 0x00000000 },
+       { 0x001610,   1, 0x04, 0x00000012 },
+       { 0x001608,   2, 0x04, 0x00000000 },
+       { 0x00162c,   1, 0x04, 0x00000003 },
+       { 0x000210,   1, 0x04, 0x00000000 },
+       { 0x000320,   1, 0x04, 0x00000000 },
+       { 0x000324,   6, 0x04, 0x3f800000 },
+       { 0x000750,   1, 0x04, 0x00000000 },
+       { 0x000760,   1, 0x04, 0x39291909 },
+       { 0x000764,   1, 0x04, 0x79695949 },
+       { 0x000768,   1, 0x04, 0xb9a99989 },
+       { 0x00076c,   1, 0x04, 0xf9e9d9c9 },
+       { 0x000770,   1, 0x04, 0x30201000 },
+       { 0x000774,   1, 0x04, 0x70605040 },
+       { 0x000778,   1, 0x04, 0x00009080 },
+       { 0x000780,   1, 0x04, 0x39291909 },
+       { 0x000784,   1, 0x04, 0x79695949 },
+       { 0x000788,   1, 0x04, 0xb9a99989 },
+       { 0x00078c,   1, 0x04, 0xf9e9d9c9 },
+       { 0x0007d0,   1, 0x04, 0x30201000 },
+       { 0x0007d4,   1, 0x04, 0x70605040 },
+       { 0x0007d8,   1, 0x04, 0x00009080 },
+       { 0x00037c,   1, 0x04, 0x00000001 },
+       { 0x000740,   2, 0x04, 0x00000000 },
+       { 0x002600,   1, 0x04, 0x00000000 },
+       { 0x001918,   1, 0x04, 0x00000000 },
+       { 0x00191c,   1, 0x04, 0x00000900 },
+       { 0x001920,   1, 0x04, 0x00000405 },
+       { 0x001308,   1, 0x04, 0x00000001 },
+       { 0x001924,   1, 0x04, 0x00000000 },
+       { 0x0013ac,   1, 0x04, 0x00000000 },
+       { 0x00192c,   1, 0x04, 0x00000001 },
+       { 0x00193c,   1, 0x04, 0x00002c1c },
+       { 0x000d7c,   1, 0x04, 0x00000000 },
+       { 0x000f8c,   1, 0x04, 0x00000000 },
+       { 0x0002c0,   1, 0x04, 0x00000001 },
+       { 0x001510,   1, 0x04, 0x00000000 },
+       { 0x001940,   1, 0x04, 0x00000000 },
+       { 0x000ff4,   2, 0x04, 0x00000000 },
+       { 0x00194c,   2, 0x04, 0x00000000 },
+       { 0x001968,   1, 0x04, 0x00000000 },
+       { 0x001590,   1, 0x04, 0x0000003f },
+       { 0x0007e8,   4, 0x04, 0x00000000 },
+       { 0x00196c,   1, 0x04, 0x00000011 },
+       { 0x00197c,   1, 0x04, 0x00000000 },
+       { 0x000fcc,   2, 0x04, 0x00000000 },
+       { 0x0002d8,   1, 0x04, 0x00000040 },
+       { 0x001980,   1, 0x04, 0x00000080 },
+       { 0x001504,   1, 0x04, 0x00000080 },
+       { 0x001984,   1, 0x04, 0x00000000 },
+       { 0x000300,   1, 0x04, 0x00000001 },
+       { 0x0013a8,   1, 0x04, 0x00000000 },
+       { 0x0012ec,   1, 0x04, 0x00000000 },
+       { 0x001310,   1, 0x04, 0x00000000 },
+       { 0x001314,   1, 0x04, 0x00000001 },
+       { 0x001380,   1, 0x04, 0x00000000 },
+       { 0x001384,   4, 0x04, 0x00000001 },
+       { 0x001394,   1, 0x04, 0x00000000 },
+       { 0x00139c,   1, 0x04, 0x00000000 },
+       { 0x001398,   1, 0x04, 0x00000000 },
+       { 0x001594,   1, 0x04, 0x00000000 },
+       { 0x001598,   4, 0x04, 0x00000001 },
+       { 0x000f54,   3, 0x04, 0x00000000 },
+       { 0x0019bc,   1, 0x04, 0x00000000 },
+       { 0x000f9c,   2, 0x04, 0x00000000 },
+       { 0x0012cc,   1, 0x04, 0x00000000 },
+       { 0x0012e8,   1, 0x04, 0x00000000 },
+       { 0x00130c,   1, 0x04, 0x00000001 },
+       { 0x001360,   8, 0x04, 0x00000000 },
+       { 0x00133c,   2, 0x04, 0x00000001 },
+       { 0x001344,   1, 0x04, 0x00000002 },
+       { 0x001348,   2, 0x04, 0x00000001 },
+       { 0x001350,   1, 0x04, 0x00000002 },
+       { 0x001358,   1, 0x04, 0x00000001 },
+       { 0x0012e4,   1, 0x04, 0x00000000 },
+       { 0x00131c,   1, 0x04, 0x00000000 },
+       { 0x001320,   3, 0x04, 0x00000000 },
+       { 0x0019c0,   1, 0x04, 0x00000000 },
+       { 0x001140,   1, 0x04, 0x00000000 },
+       { 0x0019c4,   1, 0x04, 0x00000000 },
+       { 0x0019c8,   1, 0x04, 0x00001500 },
+       { 0x00135c,   1, 0x04, 0x00000000 },
+       { 0x000f90,   1, 0x04, 0x00000000 },
+       { 0x0019e0,   8, 0x04, 0x00000001 },
+       { 0x0019cc,   1, 0x04, 0x00000001 },
+       { 0x0015b8,   1, 0x04, 0x00000000 },
+       { 0x001a00,   1, 0x04, 0x00001111 },
+       { 0x001a04,   7, 0x04, 0x00000000 },
+       { 0x000d6c,   2, 0x04, 0xffff0000 },
+       { 0x0010f8,   1, 0x04, 0x00001010 },
+       { 0x000d80,   5, 0x04, 0x00000000 },
+       { 0x000da0,   1, 0x04, 0x00000000 },
+       { 0x001508,   1, 0x04, 0x80000000 },
+       { 0x00150c,   1, 0x04, 0x40000000 },
+       { 0x001668,   1, 0x04, 0x00000000 },
+       { 0x000318,   2, 0x04, 0x00000008 },
+       { 0x000d9c,   1, 0x04, 0x00000001 },
+       { 0x0007dc,   1, 0x04, 0x00000000 },
+       { 0x00074c,   1, 0x04, 0x00000055 },
+       { 0x001420,   1, 0x04, 0x00000003 },
+       { 0x0017bc,   2, 0x04, 0x00000000 },
+       { 0x0017c4,   1, 0x04, 0x00000001 },
+       { 0x001008,   1, 0x04, 0x00000008 },
+       { 0x00100c,   1, 0x04, 0x00000040 },
+       { 0x001010,   1, 0x04, 0x0000012c },
+       { 0x000d60,   1, 0x04, 0x00000040 },
+       { 0x00075c,   1, 0x04, 0x00000003 },
+       { 0x001018,   1, 0x04, 0x00000020 },
+       { 0x00101c,   1, 0x04, 0x00000001 },
+       { 0x001020,   1, 0x04, 0x00000020 },
+       { 0x001024,   1, 0x04, 0x00000001 },
+       { 0x001444,   3, 0x04, 0x00000000 },
+       { 0x000360,   1, 0x04, 0x20164010 },
+       { 0x000364,   1, 0x04, 0x00000020 },
+       { 0x000368,   1, 0x04, 0x00000000 },
+       { 0x000de4,   1, 0x04, 0x00000000 },
+       { 0x000204,   1, 0x04, 0x00000006 },
+       { 0x000208,   1, 0x04, 0x00000000 },
+       { 0x0002cc,   1, 0x04, 0x003fffff },
+       { 0x0002d0,   1, 0x04, 0x00000c48 },
+       { 0x001220,   1, 0x04, 0x00000005 },
+       { 0x000fdc,   1, 0x04, 0x00000000 },
+       { 0x000f98,   1, 0x04, 0x00300008 },
+       { 0x001284,   1, 0x04, 0x04000080 },
+       { 0x001450,   1, 0x04, 0x00300008 },
+       { 0x001454,   1, 0x04, 0x04000080 },
+       { 0x000214,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvc1_grctx_init_9197[] = {
+       { 0x003400, 128, 0x04, 0x00000000 },
+       { 0x0002e4,   1, 0x04, 0x0000b001 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvc1_grctx_init_unk58xx[] = {
+       { 0x405800,   1, 0x04, 0x0f8000bf },
+       { 0x405830,   1, 0x04, 0x02180218 },
+       { 0x405834,   2, 0x04, 0x00000000 },
+       { 0x405854,   1, 0x04, 0x00000000 },
+       { 0x405870,   4, 0x04, 0x00000001 },
+       { 0x405a00,   2, 0x04, 0x00000000 },
+       { 0x405a18,   1, 0x04, 0x00000000 },
+};
+
+static struct nvc0_graph_init
+nvc1_grctx_init_rop[] = {
+       { 0x408800,   1, 0x04, 0x02802a3c },
+       { 0x408804,   1, 0x04, 0x00000040 },
+       { 0x408808,   1, 0x04, 0x1003e005 },
+       { 0x408900,   1, 0x04, 0x3080b801 },
+       { 0x408904,   1, 0x04, 0x62000001 },
+       { 0x408908,   1, 0x04, 0x00c80929 },
+       { 0x408980,   1, 0x04, 0x0000011d },
+};
+
+static struct nvc0_graph_init
+nvc1_grctx_init_gpc_0[] = {
+       { 0x418380,   1, 0x04, 0x00000016 },
+       { 0x418400,   1, 0x04, 0x38004e00 },
+       { 0x418404,   1, 0x04, 0x71e0ffff },
+       { 0x418408,   1, 0x04, 0x00000000 },
+       { 0x41840c,   1, 0x04, 0x00001008 },
+       { 0x418410,   1, 0x04, 0x0fff0fff },
+       { 0x418414,   1, 0x04, 0x00200fff },
+       { 0x418450,   6, 0x04, 0x00000000 },
+       { 0x418468,   1, 0x04, 0x00000001 },
+       { 0x41846c,   2, 0x04, 0x00000000 },
+       { 0x418600,   1, 0x04, 0x0000001f },
+       { 0x418684,   1, 0x04, 0x0000000f },
+       { 0x418700,   1, 0x04, 0x00000002 },
+       { 0x418704,   1, 0x04, 0x00000080 },
+       { 0x418708,   1, 0x04, 0x00000000 },
+       { 0x41870c,   1, 0x04, 0x07c80000 },
+       { 0x418710,   1, 0x04, 0x00000000 },
+       { 0x418800,   1, 0x04, 0x0006860a },
+       { 0x418808,   3, 0x04, 0x00000000 },
+       { 0x418828,   1, 0x04, 0x00008442 },
+       { 0x418830,   1, 0x04, 0x10000001 },
+       { 0x4188d8,   1, 0x04, 0x00000008 },
+       { 0x4188e0,   1, 0x04, 0x01000000 },
+       { 0x4188e8,   5, 0x04, 0x00000000 },
+       { 0x4188fc,   1, 0x04, 0x00100018 },
+       { 0x41891c,   1, 0x04, 0x00ff00ff },
+       { 0x418924,   1, 0x04, 0x00000000 },
+       { 0x418928,   1, 0x04, 0x00ffff00 },
+       { 0x41892c,   1, 0x04, 0x0000ff00 },
+       { 0x418a00,   3, 0x04, 0x00000000 },
+       { 0x418a0c,   1, 0x04, 0x00010000 },
+       { 0x418a10,   3, 0x04, 0x00000000 },
+       { 0x418a20,   3, 0x04, 0x00000000 },
+       { 0x418a2c,   1, 0x04, 0x00010000 },
+       { 0x418a30,   3, 0x04, 0x00000000 },
+       { 0x418a40,   3, 0x04, 0x00000000 },
+       { 0x418a4c,   1, 0x04, 0x00010000 },
+       { 0x418a50,   3, 0x04, 0x00000000 },
+       { 0x418a60,   3, 0x04, 0x00000000 },
+       { 0x418a6c,   1, 0x04, 0x00010000 },
+       { 0x418a70,   3, 0x04, 0x00000000 },
+       { 0x418a80,   3, 0x04, 0x00000000 },
+       { 0x418a8c,   1, 0x04, 0x00010000 },
+       { 0x418a90,   3, 0x04, 0x00000000 },
+       { 0x418aa0,   3, 0x04, 0x00000000 },
+       { 0x418aac,   1, 0x04, 0x00010000 },
+       { 0x418ab0,   3, 0x04, 0x00000000 },
+       { 0x418ac0,   3, 0x04, 0x00000000 },
+       { 0x418acc,   1, 0x04, 0x00010000 },
+       { 0x418ad0,   3, 0x04, 0x00000000 },
+       { 0x418ae0,   3, 0x04, 0x00000000 },
+       { 0x418aec,   1, 0x04, 0x00010000 },
+       { 0x418af0,   3, 0x04, 0x00000000 },
+       { 0x418b00,   1, 0x04, 0x00000000 },
+       { 0x418b08,   1, 0x04, 0x0a418820 },
+       { 0x418b0c,   1, 0x04, 0x062080e6 },
+       { 0x418b10,   1, 0x04, 0x020398a4 },
+       { 0x418b14,   1, 0x04, 0x0e629062 },
+       { 0x418b18,   1, 0x04, 0x0a418820 },
+       { 0x418b1c,   1, 0x04, 0x000000e6 },
+       { 0x418bb8,   1, 0x04, 0x00000103 },
+       { 0x418c08,   1, 0x04, 0x00000001 },
+       { 0x418c10,   8, 0x04, 0x00000000 },
+       { 0x418c6c,   1, 0x04, 0x00000001 },
+       { 0x418c80,   1, 0x04, 0x20200004 },
+       { 0x418c8c,   1, 0x04, 0x00000001 },
+       { 0x419000,   1, 0x04, 0x00000780 },
+       { 0x419004,   2, 0x04, 0x00000000 },
+       { 0x419014,   1, 0x04, 0x00000004 },
+};
+
+static struct nvc0_graph_init
+nvc1_grctx_init_tpc[] = {
+       { 0x419818,   1, 0x04, 0x00000000 },
+       { 0x41983c,   1, 0x04, 0x00038bc7 },
+       { 0x419848,   1, 0x04, 0x00000000 },
+       { 0x419864,   1, 0x04, 0x00000129 },
+       { 0x419888,   1, 0x04, 0x00000000 },
+       { 0x419a00,   1, 0x04, 0x000001f0 },
+       { 0x419a04,   1, 0x04, 0x00000001 },
+       { 0x419a08,   1, 0x04, 0x00000023 },
+       { 0x419a0c,   1, 0x04, 0x00020000 },
+       { 0x419a10,   1, 0x04, 0x00000000 },
+       { 0x419a14,   1, 0x04, 0x00000200 },
+       { 0x419a1c,   1, 0x04, 0x00000000 },
+       { 0x419a20,   1, 0x04, 0x00000800 },
+       { 0x419ac4,   1, 0x04, 0x0007f440 },
+       { 0x419b00,   1, 0x04, 0x0a418820 },
+       { 0x419b04,   1, 0x04, 0x062080e6 },
+       { 0x419b08,   1, 0x04, 0x020398a4 },
+       { 0x419b0c,   1, 0x04, 0x0e629062 },
+       { 0x419b10,   1, 0x04, 0x0a418820 },
+       { 0x419b14,   1, 0x04, 0x000000e6 },
+       { 0x419bd0,   1, 0x04, 0x00900103 },
+       { 0x419be0,   1, 0x04, 0x00400001 },
+       { 0x419be4,   1, 0x04, 0x00000000 },
+       { 0x419c00,   1, 0x04, 0x00000002 },
+       { 0x419c04,   1, 0x04, 0x00000006 },
+       { 0x419c08,   1, 0x04, 0x00000002 },
+       { 0x419c20,   1, 0x04, 0x00000000 },
+       { 0x419cb0,   1, 0x04, 0x00020048 },
+       { 0x419ce8,   1, 0x04, 0x00000000 },
+       { 0x419cf4,   1, 0x04, 0x00000183 },
+       { 0x419d20,   1, 0x04, 0x12180000 },
+       { 0x419d24,   1, 0x04, 0x00001fff },
+       { 0x419d44,   1, 0x04, 0x02180218 },
+       { 0x419e04,   3, 0x04, 0x00000000 },
+       { 0x419e10,   1, 0x04, 0x00000002 },
+       { 0x419e44,   1, 0x04, 0x001beff2 },
+       { 0x419e48,   1, 0x04, 0x00000000 },
+       { 0x419e4c,   1, 0x04, 0x0000000f },
+       { 0x419e50,  17, 0x04, 0x00000000 },
+       { 0x419e98,   1, 0x04, 0x00000000 },
+       { 0x419ee0,   1, 0x04, 0x00011110 },
+       { 0x419f30,  11, 0x04, 0x00000000 },
+};
+
+void
+nvc1_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+{
+       int gpc, tpc;
+       u32 offset;
+
+       mmio_data(0x002000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+       mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+       mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
+       mmio_list(0x408004, 0x00000000,  8, 0);
+       mmio_list(0x408008, 0x80000018,  0, 0);
+       mmio_list(0x40800c, 0x00000000,  8, 1);
+       mmio_list(0x408010, 0x80000000,  0, 0);
+       mmio_list(0x418810, 0x80000000, 12, 2);
+       mmio_list(0x419848, 0x10000000, 12, 2);
+       mmio_list(0x419004, 0x00000000,  8, 1);
+       mmio_list(0x419008, 0x00000000,  0, 0);
+       mmio_list(0x418808, 0x00000000,  8, 0);
+       mmio_list(0x41880c, 0x80000018,  0, 0);
+
+       mmio_list(0x405830, 0x02180218, 0, 0);
+       mmio_list(0x4064c4, 0x0086ffff, 0, 0);
+
+       for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
+               for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
+                       u32 addr = TPC_UNIT(gpc, tpc, 0x0520);
+                       mmio_list(addr, 0x12180000 | offset, 0, 0);
+                       offset += 0x0324;
+               }
+               for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
+                       u32 addr = TPC_UNIT(gpc, tpc, 0x0544);
+                       mmio_list(addr, 0x02180000 | offset, 0, 0);
+                       offset += 0x0324;
+               }
+       }
+}
+
+void
+nvc1_grctx_generate_unkn(struct nvc0_graph_priv *priv)
+{
+       nv_mask(priv, 0x418c6c, 0x00000001, 0x00000001);
+       nv_mask(priv, 0x41980c, 0x00000010, 0x00000010);
+       nv_mask(priv, 0x419814, 0x00000004, 0x00000004);
+       nv_mask(priv, 0x4064c0, 0x80000000, 0x80000000);
+       nv_mask(priv, 0x405800, 0x08000000, 0x08000000);
+       nv_mask(priv, 0x419c00, 0x00000008, 0x00000008);
+}
+
+static struct nvc0_graph_init *
+nvc1_grctx_init_hub[] = {
+       nvc0_grctx_init_base,
+       nvc0_grctx_init_unk40xx,
+       nvc0_grctx_init_unk44xx,
+       nvc0_grctx_init_unk46xx,
+       nvc0_grctx_init_unk47xx,
+       nvc1_grctx_init_unk58xx,
+       nvc0_grctx_init_unk60xx,
+       nvc0_grctx_init_unk64xx,
+       nvc0_grctx_init_unk78xx,
+       nvc0_grctx_init_unk80xx,
+       nvc1_grctx_init_rop,
+       NULL
+};
+
+struct nvc0_graph_init *
+nvc1_grctx_init_gpc[] = {
+       nvc1_grctx_init_gpc_0,
+       nvc0_grctx_init_gpc_1,
+       nvc1_grctx_init_tpc,
+       NULL
+};
+
+static struct nvc0_graph_mthd
+nvc1_grctx_init_mthd[] = {
+       { 0x9097, nvc1_grctx_init_9097, },
+       { 0x9197, nvc1_grctx_init_9197, },
+       { 0x902d, nvc0_grctx_init_902d, },
+       { 0x9039, nvc0_grctx_init_9039, },
+       { 0x90c0, nvc0_grctx_init_90c0, },
+       { 0x902d, nvc0_grctx_init_mthd_magic, },
+       {}
+};
+
+struct nouveau_oclass *
+nvc1_grctx_oclass = &(struct nvc0_grctx_oclass) {
+       .base.handle = NV_ENGCTX(GR, 0xc1),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_context_ctor,
+               .dtor = nvc0_graph_context_dtor,
+               .init = _nouveau_graph_context_init,
+               .fini = _nouveau_graph_context_fini,
+               .rd32 = _nouveau_graph_context_rd32,
+               .wr32 = _nouveau_graph_context_wr32,
+       },
+       .main = nvc0_grctx_generate_main,
+       .mods = nvc1_grctx_generate_mods,
+       .unkn = nvc1_grctx_generate_unkn,
+       .hub  = nvc1_grctx_init_hub,
+       .gpc  = nvc1_grctx_init_gpc,
+       .icmd = nvc1_grctx_init_icmd,
+       .mthd = nvc1_grctx_init_mthd,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c
new file mode 100644 (file)
index 0000000..8f237b3
--- /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 "nvc0.h"
+
+static struct nvc0_graph_init
+nvc3_grctx_init_tpc[] = {
+       { 0x419818,   1, 0x04, 0x00000000 },
+       { 0x41983c,   1, 0x04, 0x00038bc7 },
+       { 0x419848,   1, 0x04, 0x00000000 },
+       { 0x419864,   1, 0x04, 0x0000012a },
+       { 0x419888,   1, 0x04, 0x00000000 },
+       { 0x419a00,   1, 0x04, 0x000001f0 },
+       { 0x419a04,   1, 0x04, 0x00000001 },
+       { 0x419a08,   1, 0x04, 0x00000023 },
+       { 0x419a0c,   1, 0x04, 0x00020000 },
+       { 0x419a10,   1, 0x04, 0x00000000 },
+       { 0x419a14,   1, 0x04, 0x00000200 },
+       { 0x419a1c,   1, 0x04, 0x00000000 },
+       { 0x419a20,   1, 0x04, 0x00000800 },
+       { 0x419ac4,   1, 0x04, 0x0007f440 },
+       { 0x419b00,   1, 0x04, 0x0a418820 },
+       { 0x419b04,   1, 0x04, 0x062080e6 },
+       { 0x419b08,   1, 0x04, 0x020398a4 },
+       { 0x419b0c,   1, 0x04, 0x0e629062 },
+       { 0x419b10,   1, 0x04, 0x0a418820 },
+       { 0x419b14,   1, 0x04, 0x000000e6 },
+       { 0x419bd0,   1, 0x04, 0x00900103 },
+       { 0x419be0,   1, 0x04, 0x00000001 },
+       { 0x419be4,   1, 0x04, 0x00000000 },
+       { 0x419c00,   1, 0x04, 0x00000002 },
+       { 0x419c04,   1, 0x04, 0x00000006 },
+       { 0x419c08,   1, 0x04, 0x00000002 },
+       { 0x419c20,   1, 0x04, 0x00000000 },
+       { 0x419cb0,   1, 0x04, 0x00020048 },
+       { 0x419ce8,   1, 0x04, 0x00000000 },
+       { 0x419cf4,   1, 0x04, 0x00000183 },
+       { 0x419d20,   1, 0x04, 0x02180000 },
+       { 0x419d24,   1, 0x04, 0x00001fff },
+       { 0x419e04,   3, 0x04, 0x00000000 },
+       { 0x419e10,   1, 0x04, 0x00000002 },
+       { 0x419e44,   1, 0x04, 0x001beff2 },
+       { 0x419e48,   1, 0x04, 0x00000000 },
+       { 0x419e4c,   1, 0x04, 0x0000000f },
+       { 0x419e50,  17, 0x04, 0x00000000 },
+       { 0x419e98,   1, 0x04, 0x00000000 },
+       { 0x419ee0,   1, 0x04, 0x00011110 },
+       { 0x419f30,  11, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init *
+nvc3_grctx_init_gpc[] = {
+       nvc0_grctx_init_gpc_0,
+       nvc0_grctx_init_gpc_1,
+       nvc3_grctx_init_tpc,
+       NULL
+};
+
+struct nouveau_oclass *
+nvc3_grctx_oclass = &(struct nvc0_grctx_oclass) {
+       .base.handle = NV_ENGCTX(GR, 0xc3),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_context_ctor,
+               .dtor = nvc0_graph_context_dtor,
+               .init = _nouveau_graph_context_init,
+               .fini = _nouveau_graph_context_fini,
+               .rd32 = _nouveau_graph_context_rd32,
+               .wr32 = _nouveau_graph_context_wr32,
+       },
+       .main = nvc0_grctx_generate_main,
+       .mods = nvc0_grctx_generate_mods,
+       .unkn = nvc0_grctx_generate_unkn,
+       .hub  = nvc0_grctx_init_hub,
+       .gpc  = nvc3_grctx_init_gpc,
+       .icmd = nvc0_grctx_init_icmd,
+       .mthd = nvc0_grctx_init_mthd,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c
new file mode 100644 (file)
index 0000000..d0d4ce3
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * 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 "nvc0.h"
+
+static struct nvc0_graph_init
+nvc8_grctx_init_icmd[] = {
+       { 0x001000,   1, 0x01, 0x00000004 },
+       { 0x0000a9,   1, 0x01, 0x0000ffff },
+       { 0x000038,   1, 0x01, 0x0fac6881 },
+       { 0x00003d,   1, 0x01, 0x00000001 },
+       { 0x0000e8,   8, 0x01, 0x00000400 },
+       { 0x000078,   8, 0x01, 0x00000300 },
+       { 0x000050,   1, 0x01, 0x00000011 },
+       { 0x000058,   8, 0x01, 0x00000008 },
+       { 0x000208,   8, 0x01, 0x00000001 },
+       { 0x000081,   1, 0x01, 0x00000001 },
+       { 0x000085,   1, 0x01, 0x00000004 },
+       { 0x000088,   1, 0x01, 0x00000400 },
+       { 0x000090,   1, 0x01, 0x00000300 },
+       { 0x000098,   1, 0x01, 0x00001001 },
+       { 0x0000e3,   1, 0x01, 0x00000001 },
+       { 0x0000da,   1, 0x01, 0x00000001 },
+       { 0x0000f8,   1, 0x01, 0x00000003 },
+       { 0x0000fa,   1, 0x01, 0x00000001 },
+       { 0x00009f,   4, 0x01, 0x0000ffff },
+       { 0x0000b1,   1, 0x01, 0x00000001 },
+       { 0x0000b2,  40, 0x01, 0x00000000 },
+       { 0x000210,   8, 0x01, 0x00000040 },
+       { 0x000218,   8, 0x01, 0x0000c080 },
+       { 0x0000ad,   1, 0x01, 0x0000013e },
+       { 0x0000e1,   1, 0x01, 0x00000010 },
+       { 0x000290,  16, 0x01, 0x00000000 },
+       { 0x0003b0,  16, 0x01, 0x00000000 },
+       { 0x0002a0,  16, 0x01, 0x00000000 },
+       { 0x000420,  16, 0x01, 0x00000000 },
+       { 0x0002b0,  16, 0x01, 0x00000000 },
+       { 0x000430,  16, 0x01, 0x00000000 },
+       { 0x0002c0,  16, 0x01, 0x00000000 },
+       { 0x0004d0,  16, 0x01, 0x00000000 },
+       { 0x000720,  16, 0x01, 0x00000000 },
+       { 0x0008c0,  16, 0x01, 0x00000000 },
+       { 0x000890,  16, 0x01, 0x00000000 },
+       { 0x0008e0,  16, 0x01, 0x00000000 },
+       { 0x0008a0,  16, 0x01, 0x00000000 },
+       { 0x0008f0,  16, 0x01, 0x00000000 },
+       { 0x00094c,   1, 0x01, 0x000000ff },
+       { 0x00094d,   1, 0x01, 0xffffffff },
+       { 0x00094e,   1, 0x01, 0x00000002 },
+       { 0x0002ec,   1, 0x01, 0x00000001 },
+       { 0x000303,   1, 0x01, 0x00000001 },
+       { 0x0002e6,   1, 0x01, 0x00000001 },
+       { 0x000466,   1, 0x01, 0x00000052 },
+       { 0x000301,   1, 0x01, 0x3f800000 },
+       { 0x000304,   1, 0x01, 0x30201000 },
+       { 0x000305,   1, 0x01, 0x70605040 },
+       { 0x000306,   1, 0x01, 0xb8a89888 },
+       { 0x000307,   1, 0x01, 0xf8e8d8c8 },
+       { 0x00030a,   1, 0x01, 0x00ffff00 },
+       { 0x00030b,   1, 0x01, 0x0000001a },
+       { 0x00030c,   1, 0x01, 0x00000001 },
+       { 0x000318,   1, 0x01, 0x00000001 },
+       { 0x000340,   1, 0x01, 0x00000000 },
+       { 0x000375,   1, 0x01, 0x00000001 },
+       { 0x000351,   1, 0x01, 0x00000100 },
+       { 0x00037d,   1, 0x01, 0x00000006 },
+       { 0x0003a0,   1, 0x01, 0x00000002 },
+       { 0x0003aa,   1, 0x01, 0x00000001 },
+       { 0x0003a9,   1, 0x01, 0x00000001 },
+       { 0x000380,   1, 0x01, 0x00000001 },
+       { 0x000360,   1, 0x01, 0x00000040 },
+       { 0x000366,   2, 0x01, 0x00000000 },
+       { 0x000368,   1, 0x01, 0x00001fff },
+       { 0x000370,   2, 0x01, 0x00000000 },
+       { 0x000372,   1, 0x01, 0x003fffff },
+       { 0x00037a,   1, 0x01, 0x00000012 },
+       { 0x0005e0,   5, 0x01, 0x00000022 },
+       { 0x000619,   1, 0x01, 0x00000003 },
+       { 0x000811,   1, 0x01, 0x00000003 },
+       { 0x000812,   1, 0x01, 0x00000004 },
+       { 0x000813,   1, 0x01, 0x00000006 },
+       { 0x000814,   1, 0x01, 0x00000008 },
+       { 0x000815,   1, 0x01, 0x0000000b },
+       { 0x000800,   6, 0x01, 0x00000001 },
+       { 0x000632,   1, 0x01, 0x00000001 },
+       { 0x000633,   1, 0x01, 0x00000002 },
+       { 0x000634,   1, 0x01, 0x00000003 },
+       { 0x000635,   1, 0x01, 0x00000004 },
+       { 0x000654,   1, 0x01, 0x3f800000 },
+       { 0x000657,   1, 0x01, 0x3f800000 },
+       { 0x000655,   2, 0x01, 0x3f800000 },
+       { 0x0006cd,   1, 0x01, 0x3f800000 },
+       { 0x0007f5,   1, 0x01, 0x3f800000 },
+       { 0x0007dc,   1, 0x01, 0x39291909 },
+       { 0x0007dd,   1, 0x01, 0x79695949 },
+       { 0x0007de,   1, 0x01, 0xb9a99989 },
+       { 0x0007df,   1, 0x01, 0xf9e9d9c9 },
+       { 0x0007e8,   1, 0x01, 0x00003210 },
+       { 0x0007e9,   1, 0x01, 0x00007654 },
+       { 0x0007ea,   1, 0x01, 0x00000098 },
+       { 0x0007ec,   1, 0x01, 0x39291909 },
+       { 0x0007ed,   1, 0x01, 0x79695949 },
+       { 0x0007ee,   1, 0x01, 0xb9a99989 },
+       { 0x0007ef,   1, 0x01, 0xf9e9d9c9 },
+       { 0x0007f0,   1, 0x01, 0x00003210 },
+       { 0x0007f1,   1, 0x01, 0x00007654 },
+       { 0x0007f2,   1, 0x01, 0x00000098 },
+       { 0x0005a5,   1, 0x01, 0x00000001 },
+       { 0x000980, 128, 0x01, 0x00000000 },
+       { 0x000468,   1, 0x01, 0x00000004 },
+       { 0x00046c,   1, 0x01, 0x00000001 },
+       { 0x000470,  96, 0x01, 0x00000000 },
+       { 0x000510,  16, 0x01, 0x3f800000 },
+       { 0x000520,   1, 0x01, 0x000002b6 },
+       { 0x000529,   1, 0x01, 0x00000001 },
+       { 0x000530,  16, 0x01, 0xffff0000 },
+       { 0x000585,   1, 0x01, 0x0000003f },
+       { 0x000576,   1, 0x01, 0x00000003 },
+       { 0x00057b,   1, 0x01, 0x00000059 },
+       { 0x000586,   1, 0x01, 0x00000040 },
+       { 0x000582,   2, 0x01, 0x00000080 },
+       { 0x0005c2,   1, 0x01, 0x00000001 },
+       { 0x000638,   1, 0x01, 0x00000001 },
+       { 0x000639,   1, 0x01, 0x00000001 },
+       { 0x00063a,   1, 0x01, 0x00000002 },
+       { 0x00063b,   2, 0x01, 0x00000001 },
+       { 0x00063d,   1, 0x01, 0x00000002 },
+       { 0x00063e,   1, 0x01, 0x00000001 },
+       { 0x0008b8,   8, 0x01, 0x00000001 },
+       { 0x000900,   8, 0x01, 0x00000001 },
+       { 0x000908,   8, 0x01, 0x00000002 },
+       { 0x000910,  16, 0x01, 0x00000001 },
+       { 0x000920,   8, 0x01, 0x00000002 },
+       { 0x000928,   8, 0x01, 0x00000001 },
+       { 0x000648,   9, 0x01, 0x00000001 },
+       { 0x000658,   1, 0x01, 0x0000000f },
+       { 0x0007ff,   1, 0x01, 0x0000000a },
+       { 0x00066a,   1, 0x01, 0x40000000 },
+       { 0x00066b,   1, 0x01, 0x10000000 },
+       { 0x00066c,   2, 0x01, 0xffff0000 },
+       { 0x0007af,   2, 0x01, 0x00000008 },
+       { 0x0007f6,   1, 0x01, 0x00000001 },
+       { 0x0006b2,   1, 0x01, 0x00000055 },
+       { 0x0007ad,   1, 0x01, 0x00000003 },
+       { 0x000937,   1, 0x01, 0x00000001 },
+       { 0x000971,   1, 0x01, 0x00000008 },
+       { 0x000972,   1, 0x01, 0x00000040 },
+       { 0x000973,   1, 0x01, 0x0000012c },
+       { 0x00097c,   1, 0x01, 0x00000040 },
+       { 0x000979,   1, 0x01, 0x00000003 },
+       { 0x000975,   1, 0x01, 0x00000020 },
+       { 0x000976,   1, 0x01, 0x00000001 },
+       { 0x000977,   1, 0x01, 0x00000020 },
+       { 0x000978,   1, 0x01, 0x00000001 },
+       { 0x000957,   1, 0x01, 0x00000003 },
+       { 0x00095e,   1, 0x01, 0x20164010 },
+       { 0x00095f,   1, 0x01, 0x00000020 },
+       { 0x00097d,   1, 0x01, 0x00000020 },
+       { 0x000683,   1, 0x01, 0x00000006 },
+       { 0x000685,   1, 0x01, 0x003fffff },
+       { 0x000687,   1, 0x01, 0x00000c48 },
+       { 0x0006a0,   1, 0x01, 0x00000005 },
+       { 0x000840,   1, 0x01, 0x00300008 },
+       { 0x000841,   1, 0x01, 0x04000080 },
+       { 0x000842,   1, 0x01, 0x00300008 },
+       { 0x000843,   1, 0x01, 0x04000080 },
+       { 0x000818,   8, 0x01, 0x00000000 },
+       { 0x000848,  16, 0x01, 0x00000000 },
+       { 0x000738,   1, 0x01, 0x00000000 },
+       { 0x0006aa,   1, 0x01, 0x00000001 },
+       { 0x0006ab,   1, 0x01, 0x00000002 },
+       { 0x0006ac,   1, 0x01, 0x00000080 },
+       { 0x0006ad,   2, 0x01, 0x00000100 },
+       { 0x0006b1,   1, 0x01, 0x00000011 },
+       { 0x0006bb,   1, 0x01, 0x000000cf },
+       { 0x0006ce,   1, 0x01, 0x2a712488 },
+       { 0x000739,   1, 0x01, 0x4085c000 },
+       { 0x00073a,   1, 0x01, 0x00000080 },
+       { 0x000786,   1, 0x01, 0x80000100 },
+       { 0x00073c,   1, 0x01, 0x00010100 },
+       { 0x00073d,   1, 0x01, 0x02800000 },
+       { 0x000787,   1, 0x01, 0x000000cf },
+       { 0x00078c,   1, 0x01, 0x00000008 },
+       { 0x000792,   1, 0x01, 0x00000001 },
+       { 0x000794,   1, 0x01, 0x00000001 },
+       { 0x000795,   2, 0x01, 0x00000001 },
+       { 0x000797,   1, 0x01, 0x000000cf },
+       { 0x000836,   1, 0x01, 0x00000001 },
+       { 0x00079a,   1, 0x01, 0x00000002 },
+       { 0x000833,   1, 0x01, 0x04444480 },
+       { 0x0007a1,   1, 0x01, 0x00000001 },
+       { 0x0007a3,   1, 0x01, 0x00000001 },
+       { 0x0007a4,   2, 0x01, 0x00000001 },
+       { 0x000831,   1, 0x01, 0x00000004 },
+       { 0x00080c,   1, 0x01, 0x00000002 },
+       { 0x00080d,   2, 0x01, 0x00000100 },
+       { 0x00080f,   1, 0x01, 0x00000001 },
+       { 0x000823,   1, 0x01, 0x00000002 },
+       { 0x000824,   2, 0x01, 0x00000100 },
+       { 0x000826,   1, 0x01, 0x00000001 },
+       { 0x00095d,   1, 0x01, 0x00000001 },
+       { 0x00082b,   1, 0x01, 0x00000004 },
+       { 0x000942,   1, 0x01, 0x00010001 },
+       { 0x000943,   1, 0x01, 0x00000001 },
+       { 0x000944,   1, 0x01, 0x00000022 },
+       { 0x0007c5,   1, 0x01, 0x00010001 },
+       { 0x000834,   1, 0x01, 0x00000001 },
+       { 0x0007c7,   1, 0x01, 0x00000001 },
+       { 0x00c1b0,   8, 0x01, 0x0000000f },
+       { 0x00c1b8,   1, 0x01, 0x0fac6881 },
+       { 0x00c1b9,   1, 0x01, 0x00fac688 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000002 },
+       { 0x0006aa,   1, 0x01, 0x00000001 },
+       { 0x0006ad,   2, 0x01, 0x00000100 },
+       { 0x0006b1,   1, 0x01, 0x00000011 },
+       { 0x00078c,   1, 0x01, 0x00000008 },
+       { 0x000792,   1, 0x01, 0x00000001 },
+       { 0x000794,   1, 0x01, 0x00000001 },
+       { 0x000795,   2, 0x01, 0x00000001 },
+       { 0x000797,   1, 0x01, 0x000000cf },
+       { 0x00079a,   1, 0x01, 0x00000002 },
+       { 0x000833,   1, 0x01, 0x04444480 },
+       { 0x0007a1,   1, 0x01, 0x00000001 },
+       { 0x0007a3,   1, 0x01, 0x00000001 },
+       { 0x0007a4,   2, 0x01, 0x00000001 },
+       { 0x000831,   1, 0x01, 0x00000004 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000014 },
+       { 0x000351,   1, 0x01, 0x00000100 },
+       { 0x000957,   1, 0x01, 0x00000003 },
+       { 0x00095d,   1, 0x01, 0x00000001 },
+       { 0x00082b,   1, 0x01, 0x00000004 },
+       { 0x000942,   1, 0x01, 0x00010001 },
+       { 0x000943,   1, 0x01, 0x00000001 },
+       { 0x0007c5,   1, 0x01, 0x00010001 },
+       { 0x000834,   1, 0x01, 0x00000001 },
+       { 0x0007c7,   1, 0x01, 0x00000001 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000001 },
+       { 0x00080c,   1, 0x01, 0x00000002 },
+       { 0x00080d,   2, 0x01, 0x00000100 },
+       { 0x00080f,   1, 0x01, 0x00000001 },
+       { 0x000823,   1, 0x01, 0x00000002 },
+       { 0x000824,   2, 0x01, 0x00000100 },
+       { 0x000826,   1, 0x01, 0x00000001 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvc8_grctx_init_tpc[] = {
+       { 0x419818,   1, 0x04, 0x00000000 },
+       { 0x41983c,   1, 0x04, 0x00038bc7 },
+       { 0x419848,   1, 0x04, 0x00000000 },
+       { 0x419864,   1, 0x04, 0x0000012a },
+       { 0x419888,   1, 0x04, 0x00000000 },
+       { 0x419a00,   1, 0x04, 0x000001f0 },
+       { 0x419a04,   1, 0x04, 0x00000001 },
+       { 0x419a08,   1, 0x04, 0x00000023 },
+       { 0x419a0c,   1, 0x04, 0x00020000 },
+       { 0x419a10,   1, 0x04, 0x00000000 },
+       { 0x419a14,   1, 0x04, 0x00000200 },
+       { 0x419a1c,   1, 0x04, 0x00000000 },
+       { 0x419a20,   1, 0x04, 0x00000800 },
+       { 0x419b00,   1, 0x04, 0x0a418820 },
+       { 0x419b04,   1, 0x04, 0x062080e6 },
+       { 0x419b08,   1, 0x04, 0x020398a4 },
+       { 0x419b0c,   1, 0x04, 0x0e629062 },
+       { 0x419b10,   1, 0x04, 0x0a418820 },
+       { 0x419b14,   1, 0x04, 0x000000e6 },
+       { 0x419bd0,   1, 0x04, 0x00900103 },
+       { 0x419be0,   1, 0x04, 0x00000001 },
+       { 0x419be4,   1, 0x04, 0x00000000 },
+       { 0x419c00,   1, 0x04, 0x00000002 },
+       { 0x419c04,   1, 0x04, 0x00000006 },
+       { 0x419c08,   1, 0x04, 0x00000002 },
+       { 0x419c20,   1, 0x04, 0x00000000 },
+       { 0x419cb0,   1, 0x04, 0x00060048 },
+       { 0x419ce8,   1, 0x04, 0x00000000 },
+       { 0x419cf4,   1, 0x04, 0x00000183 },
+       { 0x419d20,   1, 0x04, 0x02180000 },
+       { 0x419d24,   1, 0x04, 0x00001fff },
+       { 0x419e04,   3, 0x04, 0x00000000 },
+       { 0x419e10,   1, 0x04, 0x00000002 },
+       { 0x419e44,   1, 0x04, 0x001beff2 },
+       { 0x419e48,   1, 0x04, 0x00000000 },
+       { 0x419e4c,   1, 0x04, 0x0000000f },
+       { 0x419e50,  17, 0x04, 0x00000000 },
+       { 0x419e98,   1, 0x04, 0x00000000 },
+       { 0x419f50,   2, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc8_grctx_init_9197[] = {
+       { 0x0002e4,   1, 0x04, 0x0000b001 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc8_grctx_init_9297[] = {
+       { 0x003400, 128, 0x04, 0x00000000 },
+       { 0x00036c,   2, 0x04, 0x00000000 },
+       { 0x0007a4,   2, 0x04, 0x00000000 },
+       { 0x000374,   1, 0x04, 0x00000000 },
+       { 0x000378,   1, 0x04, 0x00000020 },
+       {}
+};
+
+static struct nvc0_graph_mthd
+nvc8_grctx_init_mthd[] = {
+       { 0x9097, nvc1_grctx_init_9097, },
+       { 0x9197, nvc8_grctx_init_9197, },
+       { 0x9297, nvc8_grctx_init_9297, },
+       { 0x902d, nvc0_grctx_init_902d, },
+       { 0x9039, nvc0_grctx_init_9039, },
+       { 0x90c0, nvc0_grctx_init_90c0, },
+       { 0x902d, nvc0_grctx_init_mthd_magic, },
+       {}
+};
+
+static struct nvc0_graph_init *
+nvc8_grctx_init_gpc[] = {
+       nvc0_grctx_init_gpc_0,
+       nvc0_grctx_init_gpc_1,
+       nvc8_grctx_init_tpc,
+       NULL
+};
+
+struct nouveau_oclass *
+nvc8_grctx_oclass = &(struct nvc0_grctx_oclass) {
+       .base.handle = NV_ENGCTX(GR, 0xc8),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_context_ctor,
+               .dtor = nvc0_graph_context_dtor,
+               .init = _nouveau_graph_context_init,
+               .fini = _nouveau_graph_context_fini,
+               .rd32 = _nouveau_graph_context_rd32,
+               .wr32 = _nouveau_graph_context_wr32,
+       },
+       .main = nvc0_grctx_generate_main,
+       .mods = nvc0_grctx_generate_mods,
+       .unkn = nvc0_grctx_generate_unkn,
+       .hub  = nvc0_grctx_init_hub,
+       .gpc  = nvc8_grctx_init_gpc,
+       .icmd = nvc8_grctx_init_icmd,
+       .mthd = nvc8_grctx_init_mthd,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c
new file mode 100644 (file)
index 0000000..438e784
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * 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 "nvc0.h"
+
+struct nvc0_graph_init
+nvd7_grctx_init_unk40xx[] = {
+       { 0x404004,  10, 0x04, 0x00000000 },
+       { 0x404044,   1, 0x04, 0x00000000 },
+       { 0x404094,   1, 0x04, 0x00000000 },
+       { 0x404098,  12, 0x04, 0x00000000 },
+       { 0x4040c8,   1, 0x04, 0xf0000087 },
+       { 0x4040d0,   6, 0x04, 0x00000000 },
+       { 0x4040e8,   1, 0x04, 0x00001000 },
+       { 0x4040f8,   1, 0x04, 0x00000000 },
+       { 0x404130,   1, 0x04, 0x00000000 },
+       { 0x404134,   1, 0x04, 0x00000000 },
+       { 0x404138,   1, 0x04, 0x20000040 },
+       { 0x404150,   1, 0x04, 0x0000002e },
+       { 0x404154,   1, 0x04, 0x00000400 },
+       { 0x404158,   1, 0x04, 0x00000200 },
+       { 0x404164,   1, 0x04, 0x00000055 },
+       { 0x404168,   1, 0x04, 0x00000000 },
+       { 0x404178,   2, 0x04, 0x00000000 },
+       { 0x404200,   8, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd7_grctx_init_unk58xx[] = {
+       { 0x405800,   1, 0x04, 0x0f8000bf },
+       { 0x405830,   1, 0x04, 0x02180324 },
+       { 0x405834,   1, 0x04, 0x08000000 },
+       { 0x405838,   1, 0x04, 0x00000000 },
+       { 0x405854,   1, 0x04, 0x00000000 },
+       { 0x405870,   4, 0x04, 0x00000001 },
+       { 0x405a00,   2, 0x04, 0x00000000 },
+       { 0x405a18,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd7_grctx_init_unk64xx[] = {
+       { 0x4064a8,   1, 0x04, 0x00000000 },
+       { 0x4064ac,   1, 0x04, 0x00003fff },
+       { 0x4064b4,   3, 0x04, 0x00000000 },
+       { 0x4064c0,   1, 0x04, 0x801a0078 },
+       { 0x4064c4,   1, 0x04, 0x00c9ffff },
+       { 0x4064d0,   8, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd7_grctx_init_gpc_0[] = {
+       { 0x418380,   1, 0x04, 0x00000016 },
+       { 0x418400,   1, 0x04, 0x38004e00 },
+       { 0x418404,   1, 0x04, 0x71e0ffff },
+       { 0x41840c,   1, 0x04, 0x00001008 },
+       { 0x418410,   1, 0x04, 0x0fff0fff },
+       { 0x418414,   1, 0x04, 0x02200fff },
+       { 0x418450,   6, 0x04, 0x00000000 },
+       { 0x418468,   1, 0x04, 0x00000001 },
+       { 0x41846c,   2, 0x04, 0x00000000 },
+       { 0x418600,   1, 0x04, 0x0000001f },
+       { 0x418684,   1, 0x04, 0x0000000f },
+       { 0x418700,   1, 0x04, 0x00000002 },
+       { 0x418704,   1, 0x04, 0x00000080 },
+       { 0x418708,   3, 0x04, 0x00000000 },
+       { 0x418800,   1, 0x04, 0x7006860a },
+       { 0x418808,   3, 0x04, 0x00000000 },
+       { 0x418828,   1, 0x04, 0x00008442 },
+       { 0x418830,   1, 0x04, 0x10000001 },
+       { 0x4188d8,   1, 0x04, 0x00000008 },
+       { 0x4188e0,   1, 0x04, 0x01000000 },
+       { 0x4188e8,   5, 0x04, 0x00000000 },
+       { 0x4188fc,   1, 0x04, 0x20100018 },
+       { 0x41891c,   1, 0x04, 0x00ff00ff },
+       { 0x418924,   1, 0x04, 0x00000000 },
+       { 0x418928,   1, 0x04, 0x00ffff00 },
+       { 0x41892c,   1, 0x04, 0x0000ff00 },
+       { 0x418b00,   1, 0x04, 0x00000006 },
+       { 0x418b08,   1, 0x04, 0x0a418820 },
+       { 0x418b0c,   1, 0x04, 0x062080e6 },
+       { 0x418b10,   1, 0x04, 0x020398a4 },
+       { 0x418b14,   1, 0x04, 0x0e629062 },
+       { 0x418b18,   1, 0x04, 0x0a418820 },
+       { 0x418b1c,   1, 0x04, 0x000000e6 },
+       { 0x418bb8,   1, 0x04, 0x00000103 },
+       { 0x418c08,   1, 0x04, 0x00000001 },
+       { 0x418c10,   8, 0x04, 0x00000000 },
+       { 0x418c6c,   1, 0x04, 0x00000001 },
+       { 0x418c80,   1, 0x04, 0x20200004 },
+       { 0x418c8c,   1, 0x04, 0x00000001 },
+       { 0x419000,   1, 0x04, 0x00000780 },
+       { 0x419004,   2, 0x04, 0x00000000 },
+       { 0x419014,   1, 0x04, 0x00000004 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd7_grctx_init_tpc[] = {
+       { 0x419848,   1, 0x04, 0x00000000 },
+       { 0x419864,   1, 0x04, 0x00000129 },
+       { 0x419888,   1, 0x04, 0x00000000 },
+       { 0x419a00,   1, 0x04, 0x000001f0 },
+       { 0x419a04,   1, 0x04, 0x00000001 },
+       { 0x419a08,   1, 0x04, 0x00000023 },
+       { 0x419a0c,   1, 0x04, 0x00020000 },
+       { 0x419a10,   1, 0x04, 0x00000000 },
+       { 0x419a14,   1, 0x04, 0x00000200 },
+       { 0x419a1c,   1, 0x04, 0x00008000 },
+       { 0x419a20,   1, 0x04, 0x00000800 },
+       { 0x419ac4,   1, 0x04, 0x0017f440 },
+       { 0x419c00,   1, 0x04, 0x0000000a },
+       { 0x419c04,   1, 0x04, 0x00000006 },
+       { 0x419c08,   1, 0x04, 0x00000002 },
+       { 0x419c20,   1, 0x04, 0x00000000 },
+       { 0x419c24,   1, 0x04, 0x00084210 },
+       { 0x419c28,   1, 0x04, 0x3efbefbe },
+       { 0x419cb0,   1, 0x04, 0x00020048 },
+       { 0x419ce8,   1, 0x04, 0x00000000 },
+       { 0x419cf4,   1, 0x04, 0x00000183 },
+       { 0x419e04,   3, 0x04, 0x00000000 },
+       { 0x419e10,   1, 0x04, 0x00000002 },
+       { 0x419e44,   1, 0x04, 0x001beff2 },
+       { 0x419e48,   1, 0x04, 0x00000000 },
+       { 0x419e4c,   1, 0x04, 0x0000000f },
+       { 0x419e50,  17, 0x04, 0x00000000 },
+       { 0x419e98,   1, 0x04, 0x00000000 },
+       { 0x419ee0,   1, 0x04, 0x00010110 },
+       { 0x419f30,  11, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd7_grctx_init_unk[] = {
+       { 0x41be24,   1, 0x04, 0x00000002 },
+       { 0x41bec0,   1, 0x04, 0x12180000 },
+       { 0x41bec4,   1, 0x04, 0x00003fff },
+       { 0x41bee4,   1, 0x04, 0x03240218 },
+       { 0x41bf00,   1, 0x04, 0x0a418820 },
+       { 0x41bf04,   1, 0x04, 0x062080e6 },
+       { 0x41bf08,   1, 0x04, 0x020398a4 },
+       { 0x41bf0c,   1, 0x04, 0x0e629062 },
+       { 0x41bf10,   1, 0x04, 0x0a418820 },
+       { 0x41bf14,   1, 0x04, 0x000000e6 },
+       { 0x41bfd0,   1, 0x04, 0x00900103 },
+       { 0x41bfe0,   1, 0x04, 0x00400001 },
+       { 0x41bfe4,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static void
+nvd7_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+{
+       u32 magic[GPC_MAX][2];
+       u32 offset;
+       int gpc;
+
+       mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+       mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+       mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
+       mmio_list(0x40800c, 0x00000000,  8, 1);
+       mmio_list(0x408010, 0x80000000,  0, 0);
+       mmio_list(0x419004, 0x00000000,  8, 1);
+       mmio_list(0x419008, 0x00000000,  0, 0);
+       mmio_list(0x408004, 0x00000000,  8, 0);
+       mmio_list(0x408008, 0x80000018,  0, 0);
+       mmio_list(0x418808, 0x00000000,  8, 0);
+       mmio_list(0x41880c, 0x80000018,  0, 0);
+       mmio_list(0x418810, 0x80000000, 12, 2);
+       mmio_list(0x419848, 0x10000000, 12, 2);
+
+       mmio_list(0x405830, 0x02180324,  0, 0);
+       mmio_list(0x4064c4, 0x00c9ffff,  0, 0);
+
+       for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
+               u16 magic0 = 0x0218 * priv->tpc_nr[gpc];
+               u16 magic1 = 0x0324 * priv->tpc_nr[gpc];
+               magic[gpc][0]  = 0x10000000 | (magic0 << 16) | offset;
+               magic[gpc][1]  = 0x00000000 | (magic1 << 16);
+               offset += 0x0324 * priv->tpc_nr[gpc];
+       }
+
+       for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+               mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0);
+               mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0);
+               offset += 0x07ff * priv->tpc_nr[gpc];
+       }
+       mmio_list(0x17e91c, 0x03060609, 0, 0); /* different from kepler */
+}
+
+void
+nvd7_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+{
+       struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass;
+       int i;
+
+       nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
+
+       for (i = 0; oclass->hub[i]; i++)
+               nvc0_graph_mmio(priv, oclass->hub[i]);
+       for (i = 0; oclass->gpc[i]; i++)
+               nvc0_graph_mmio(priv, oclass->gpc[i]);
+
+       nv_wr32(priv, 0x404154, 0x00000000);
+
+       oclass->mods(priv, info);
+       oclass->unkn(priv);
+
+       nvc0_grctx_generate_tpcid(priv);
+       nvc0_grctx_generate_r406028(priv);
+       nvc0_grctx_generate_r4060a8(priv);
+       nve4_grctx_generate_r418bb8(priv);
+       nvc0_grctx_generate_r406800(priv);
+
+       for (i = 0; i < 8; i++)
+               nv_wr32(priv, 0x4064d0 + (i * 0x04), 0x00000000);
+
+       nvc0_graph_icmd(priv, oclass->icmd);
+       nv_wr32(priv, 0x404154, 0x00000400);
+       nvc0_graph_mthd(priv, oclass->mthd);
+       nv_mask(priv, 0x000260, 0x00000001, 0x00000001);
+}
+
+
+static struct nvc0_graph_init *
+nvd7_grctx_init_hub[] = {
+       nvc0_grctx_init_base,
+       nvd7_grctx_init_unk40xx,
+       nvc0_grctx_init_unk44xx,
+       nvc0_grctx_init_unk46xx,
+       nvc0_grctx_init_unk47xx,
+       nvd7_grctx_init_unk58xx,
+       nvc0_grctx_init_unk60xx,
+       nvd7_grctx_init_unk64xx,
+       nvc0_grctx_init_unk78xx,
+       nvc0_grctx_init_unk80xx,
+       nvd9_grctx_init_rop,
+};
+
+struct nvc0_graph_init *
+nvd7_grctx_init_gpc[] = {
+       nvd7_grctx_init_gpc_0,
+       nvc0_grctx_init_gpc_1,
+       nvd7_grctx_init_tpc,
+       nvd7_grctx_init_unk,
+       NULL
+};
+
+struct nouveau_oclass *
+nvd7_grctx_oclass = &(struct nvc0_grctx_oclass) {
+       .base.handle = NV_ENGCTX(GR, 0xd7),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_context_ctor,
+               .dtor = nvc0_graph_context_dtor,
+               .init = _nouveau_graph_context_init,
+               .fini = _nouveau_graph_context_fini,
+               .rd32 = _nouveau_graph_context_rd32,
+               .wr32 = _nouveau_graph_context_wr32,
+       },
+       .main = nvd7_grctx_generate_main,
+       .mods = nvd7_grctx_generate_mods,
+       .unkn = nve4_grctx_generate_unkn,
+       .hub  = nvd7_grctx_init_hub,
+       .gpc  = nvd7_grctx_init_gpc,
+       .icmd = nvd9_grctx_init_icmd,
+       .mthd = nvd9_grctx_init_mthd,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c
new file mode 100644 (file)
index 0000000..818a475
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ * 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 "nvc0.h"
+
+struct nvc0_graph_init
+nvd9_grctx_init_90c0[] = {
+       { 0x002700,   4, 0x40, 0x00000000 },
+       { 0x002720,   4, 0x40, 0x00000000 },
+       { 0x002704,   4, 0x40, 0x00000000 },
+       { 0x002724,   4, 0x40, 0x00000000 },
+       { 0x002708,   4, 0x40, 0x00000000 },
+       { 0x002728,   4, 0x40, 0x00000000 },
+       { 0x00270c,   8, 0x20, 0x00000000 },
+       { 0x002710,   4, 0x40, 0x00014000 },
+       { 0x002730,   4, 0x40, 0x00014000 },
+       { 0x002714,   4, 0x40, 0x00000040 },
+       { 0x002734,   4, 0x40, 0x00000040 },
+       { 0x00030c,   1, 0x04, 0x00000001 },
+       { 0x001944,   1, 0x04, 0x00000000 },
+       { 0x000758,   1, 0x04, 0x00000100 },
+       { 0x0002c4,   1, 0x04, 0x00000000 },
+       { 0x000790,   5, 0x04, 0x00000000 },
+       { 0x00077c,   1, 0x04, 0x00000000 },
+       { 0x000204,   3, 0x04, 0x00000000 },
+       { 0x000214,   1, 0x04, 0x00000000 },
+       { 0x00024c,   1, 0x04, 0x00000000 },
+       { 0x000d94,   1, 0x04, 0x00000001 },
+       { 0x001608,   2, 0x04, 0x00000000 },
+       { 0x001664,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvd9_grctx_init_icmd[] = {
+       { 0x001000,   1, 0x01, 0x00000004 },
+       { 0x0000a9,   1, 0x01, 0x0000ffff },
+       { 0x000038,   1, 0x01, 0x0fac6881 },
+       { 0x00003d,   1, 0x01, 0x00000001 },
+       { 0x0000e8,   8, 0x01, 0x00000400 },
+       { 0x000078,   8, 0x01, 0x00000300 },
+       { 0x000050,   1, 0x01, 0x00000011 },
+       { 0x000058,   8, 0x01, 0x00000008 },
+       { 0x000208,   8, 0x01, 0x00000001 },
+       { 0x000081,   1, 0x01, 0x00000001 },
+       { 0x000085,   1, 0x01, 0x00000004 },
+       { 0x000088,   1, 0x01, 0x00000400 },
+       { 0x000090,   1, 0x01, 0x00000300 },
+       { 0x000098,   1, 0x01, 0x00001001 },
+       { 0x0000e3,   1, 0x01, 0x00000001 },
+       { 0x0000da,   1, 0x01, 0x00000001 },
+       { 0x0000f8,   1, 0x01, 0x00000003 },
+       { 0x0000fa,   1, 0x01, 0x00000001 },
+       { 0x00009f,   4, 0x01, 0x0000ffff },
+       { 0x0000b1,   1, 0x01, 0x00000001 },
+       { 0x0000b2,  40, 0x01, 0x00000000 },
+       { 0x000210,   8, 0x01, 0x00000040 },
+       { 0x000400,  24, 0x01, 0x00000040 },
+       { 0x000218,   8, 0x01, 0x0000c080 },
+       { 0x000440,  24, 0x01, 0x0000c080 },
+       { 0x0000ad,   1, 0x01, 0x0000013e },
+       { 0x0000e1,   1, 0x01, 0x00000010 },
+       { 0x000290,  16, 0x01, 0x00000000 },
+       { 0x0003b0,  16, 0x01, 0x00000000 },
+       { 0x0002a0,  16, 0x01, 0x00000000 },
+       { 0x000420,  16, 0x01, 0x00000000 },
+       { 0x0002b0,  16, 0x01, 0x00000000 },
+       { 0x000430,  16, 0x01, 0x00000000 },
+       { 0x0002c0,  16, 0x01, 0x00000000 },
+       { 0x0004d0,  16, 0x01, 0x00000000 },
+       { 0x000720,  16, 0x01, 0x00000000 },
+       { 0x0008c0,  16, 0x01, 0x00000000 },
+       { 0x000890,  16, 0x01, 0x00000000 },
+       { 0x0008e0,  16, 0x01, 0x00000000 },
+       { 0x0008a0,  16, 0x01, 0x00000000 },
+       { 0x0008f0,  16, 0x01, 0x00000000 },
+       { 0x00094c,   1, 0x01, 0x000000ff },
+       { 0x00094d,   1, 0x01, 0xffffffff },
+       { 0x00094e,   1, 0x01, 0x00000002 },
+       { 0x0002ec,   1, 0x01, 0x00000001 },
+       { 0x000303,   1, 0x01, 0x00000001 },
+       { 0x0002e6,   1, 0x01, 0x00000001 },
+       { 0x000466,   1, 0x01, 0x00000052 },
+       { 0x000301,   1, 0x01, 0x3f800000 },
+       { 0x000304,   1, 0x01, 0x30201000 },
+       { 0x000305,   1, 0x01, 0x70605040 },
+       { 0x000306,   1, 0x01, 0xb8a89888 },
+       { 0x000307,   1, 0x01, 0xf8e8d8c8 },
+       { 0x00030a,   1, 0x01, 0x00ffff00 },
+       { 0x00030b,   1, 0x01, 0x0000001a },
+       { 0x00030c,   1, 0x01, 0x00000001 },
+       { 0x000318,   1, 0x01, 0x00000001 },
+       { 0x000340,   1, 0x01, 0x00000000 },
+       { 0x000375,   1, 0x01, 0x00000001 },
+       { 0x000351,   1, 0x01, 0x00000100 },
+       { 0x00037d,   1, 0x01, 0x00000006 },
+       { 0x0003a0,   1, 0x01, 0x00000002 },
+       { 0x0003aa,   1, 0x01, 0x00000001 },
+       { 0x0003a9,   1, 0x01, 0x00000001 },
+       { 0x000380,   1, 0x01, 0x00000001 },
+       { 0x000360,   1, 0x01, 0x00000040 },
+       { 0x000366,   2, 0x01, 0x00000000 },
+       { 0x000368,   1, 0x01, 0x00001fff },
+       { 0x000370,   2, 0x01, 0x00000000 },
+       { 0x000372,   1, 0x01, 0x003fffff },
+       { 0x00037a,   1, 0x01, 0x00000012 },
+       { 0x0005e0,   5, 0x01, 0x00000022 },
+       { 0x000619,   1, 0x01, 0x00000003 },
+       { 0x000811,   1, 0x01, 0x00000003 },
+       { 0x000812,   1, 0x01, 0x00000004 },
+       { 0x000813,   1, 0x01, 0x00000006 },
+       { 0x000814,   1, 0x01, 0x00000008 },
+       { 0x000815,   1, 0x01, 0x0000000b },
+       { 0x000800,   6, 0x01, 0x00000001 },
+       { 0x000632,   1, 0x01, 0x00000001 },
+       { 0x000633,   1, 0x01, 0x00000002 },
+       { 0x000634,   1, 0x01, 0x00000003 },
+       { 0x000635,   1, 0x01, 0x00000004 },
+       { 0x000654,   1, 0x01, 0x3f800000 },
+       { 0x000657,   1, 0x01, 0x3f800000 },
+       { 0x000655,   2, 0x01, 0x3f800000 },
+       { 0x0006cd,   1, 0x01, 0x3f800000 },
+       { 0x0007f5,   1, 0x01, 0x3f800000 },
+       { 0x0007dc,   1, 0x01, 0x39291909 },
+       { 0x0007dd,   1, 0x01, 0x79695949 },
+       { 0x0007de,   1, 0x01, 0xb9a99989 },
+       { 0x0007df,   1, 0x01, 0xf9e9d9c9 },
+       { 0x0007e8,   1, 0x01, 0x00003210 },
+       { 0x0007e9,   1, 0x01, 0x00007654 },
+       { 0x0007ea,   1, 0x01, 0x00000098 },
+       { 0x0007ec,   1, 0x01, 0x39291909 },
+       { 0x0007ed,   1, 0x01, 0x79695949 },
+       { 0x0007ee,   1, 0x01, 0xb9a99989 },
+       { 0x0007ef,   1, 0x01, 0xf9e9d9c9 },
+       { 0x0007f0,   1, 0x01, 0x00003210 },
+       { 0x0007f1,   1, 0x01, 0x00007654 },
+       { 0x0007f2,   1, 0x01, 0x00000098 },
+       { 0x0005a5,   1, 0x01, 0x00000001 },
+       { 0x000980, 128, 0x01, 0x00000000 },
+       { 0x000468,   1, 0x01, 0x00000004 },
+       { 0x00046c,   1, 0x01, 0x00000001 },
+       { 0x000470,  96, 0x01, 0x00000000 },
+       { 0x000510,  16, 0x01, 0x3f800000 },
+       { 0x000520,   1, 0x01, 0x000002b6 },
+       { 0x000529,   1, 0x01, 0x00000001 },
+       { 0x000530,  16, 0x01, 0xffff0000 },
+       { 0x000585,   1, 0x01, 0x0000003f },
+       { 0x000576,   1, 0x01, 0x00000003 },
+       { 0x00057b,   1, 0x01, 0x00000059 },
+       { 0x000586,   1, 0x01, 0x00000040 },
+       { 0x000582,   2, 0x01, 0x00000080 },
+       { 0x0005c2,   1, 0x01, 0x00000001 },
+       { 0x000638,   1, 0x01, 0x00000001 },
+       { 0x000639,   1, 0x01, 0x00000001 },
+       { 0x00063a,   1, 0x01, 0x00000002 },
+       { 0x00063b,   2, 0x01, 0x00000001 },
+       { 0x00063d,   1, 0x01, 0x00000002 },
+       { 0x00063e,   1, 0x01, 0x00000001 },
+       { 0x0008b8,   8, 0x01, 0x00000001 },
+       { 0x000900,   8, 0x01, 0x00000001 },
+       { 0x000908,   8, 0x01, 0x00000002 },
+       { 0x000910,  16, 0x01, 0x00000001 },
+       { 0x000920,   8, 0x01, 0x00000002 },
+       { 0x000928,   8, 0x01, 0x00000001 },
+       { 0x000648,   9, 0x01, 0x00000001 },
+       { 0x000658,   1, 0x01, 0x0000000f },
+       { 0x0007ff,   1, 0x01, 0x0000000a },
+       { 0x00066a,   1, 0x01, 0x40000000 },
+       { 0x00066b,   1, 0x01, 0x10000000 },
+       { 0x00066c,   2, 0x01, 0xffff0000 },
+       { 0x0007af,   2, 0x01, 0x00000008 },
+       { 0x0007f6,   1, 0x01, 0x00000001 },
+       { 0x0006b2,   1, 0x01, 0x00000055 },
+       { 0x0007ad,   1, 0x01, 0x00000003 },
+       { 0x000937,   1, 0x01, 0x00000001 },
+       { 0x000971,   1, 0x01, 0x00000008 },
+       { 0x000972,   1, 0x01, 0x00000040 },
+       { 0x000973,   1, 0x01, 0x0000012c },
+       { 0x00097c,   1, 0x01, 0x00000040 },
+       { 0x000979,   1, 0x01, 0x00000003 },
+       { 0x000975,   1, 0x01, 0x00000020 },
+       { 0x000976,   1, 0x01, 0x00000001 },
+       { 0x000977,   1, 0x01, 0x00000020 },
+       { 0x000978,   1, 0x01, 0x00000001 },
+       { 0x000957,   1, 0x01, 0x00000003 },
+       { 0x00095e,   1, 0x01, 0x20164010 },
+       { 0x00095f,   1, 0x01, 0x00000020 },
+       { 0x00097d,   1, 0x01, 0x00000020 },
+       { 0x000683,   1, 0x01, 0x00000006 },
+       { 0x000685,   1, 0x01, 0x003fffff },
+       { 0x000687,   1, 0x01, 0x00000c48 },
+       { 0x0006a0,   1, 0x01, 0x00000005 },
+       { 0x000840,   1, 0x01, 0x00300008 },
+       { 0x000841,   1, 0x01, 0x04000080 },
+       { 0x000842,   1, 0x01, 0x00300008 },
+       { 0x000843,   1, 0x01, 0x04000080 },
+       { 0x000818,   8, 0x01, 0x00000000 },
+       { 0x000848,  16, 0x01, 0x00000000 },
+       { 0x000738,   1, 0x01, 0x00000000 },
+       { 0x0006aa,   1, 0x01, 0x00000001 },
+       { 0x0006ab,   1, 0x01, 0x00000002 },
+       { 0x0006ac,   1, 0x01, 0x00000080 },
+       { 0x0006ad,   2, 0x01, 0x00000100 },
+       { 0x0006b1,   1, 0x01, 0x00000011 },
+       { 0x0006bb,   1, 0x01, 0x000000cf },
+       { 0x0006ce,   1, 0x01, 0x2a712488 },
+       { 0x000739,   1, 0x01, 0x4085c000 },
+       { 0x00073a,   1, 0x01, 0x00000080 },
+       { 0x000786,   1, 0x01, 0x80000100 },
+       { 0x00073c,   1, 0x01, 0x00010100 },
+       { 0x00073d,   1, 0x01, 0x02800000 },
+       { 0x000787,   1, 0x01, 0x000000cf },
+       { 0x00078c,   1, 0x01, 0x00000008 },
+       { 0x000792,   1, 0x01, 0x00000001 },
+       { 0x000794,   1, 0x01, 0x00000001 },
+       { 0x000795,   2, 0x01, 0x00000001 },
+       { 0x000797,   1, 0x01, 0x000000cf },
+       { 0x000836,   1, 0x01, 0x00000001 },
+       { 0x00079a,   1, 0x01, 0x00000002 },
+       { 0x000833,   1, 0x01, 0x04444480 },
+       { 0x0007a1,   1, 0x01, 0x00000001 },
+       { 0x0007a3,   1, 0x01, 0x00000001 },
+       { 0x0007a4,   2, 0x01, 0x00000001 },
+       { 0x000831,   1, 0x01, 0x00000004 },
+       { 0x00080c,   1, 0x01, 0x00000002 },
+       { 0x00080d,   2, 0x01, 0x00000100 },
+       { 0x00080f,   1, 0x01, 0x00000001 },
+       { 0x000823,   1, 0x01, 0x00000002 },
+       { 0x000824,   2, 0x01, 0x00000100 },
+       { 0x000826,   1, 0x01, 0x00000001 },
+       { 0x00095d,   1, 0x01, 0x00000001 },
+       { 0x00082b,   1, 0x01, 0x00000004 },
+       { 0x000942,   1, 0x01, 0x00010001 },
+       { 0x000943,   1, 0x01, 0x00000001 },
+       { 0x000944,   1, 0x01, 0x00000022 },
+       { 0x0007c5,   1, 0x01, 0x00010001 },
+       { 0x000834,   1, 0x01, 0x00000001 },
+       { 0x0007c7,   1, 0x01, 0x00000001 },
+       { 0x00c1b0,   8, 0x01, 0x0000000f },
+       { 0x00c1b8,   1, 0x01, 0x0fac6881 },
+       { 0x00c1b9,   1, 0x01, 0x00fac688 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000002 },
+       { 0x0006aa,   1, 0x01, 0x00000001 },
+       { 0x0006ad,   2, 0x01, 0x00000100 },
+       { 0x0006b1,   1, 0x01, 0x00000011 },
+       { 0x00078c,   1, 0x01, 0x00000008 },
+       { 0x000792,   1, 0x01, 0x00000001 },
+       { 0x000794,   1, 0x01, 0x00000001 },
+       { 0x000795,   2, 0x01, 0x00000001 },
+       { 0x000797,   1, 0x01, 0x000000cf },
+       { 0x00079a,   1, 0x01, 0x00000002 },
+       { 0x000833,   1, 0x01, 0x04444480 },
+       { 0x0007a1,   1, 0x01, 0x00000001 },
+       { 0x0007a3,   1, 0x01, 0x00000001 },
+       { 0x0007a4,   2, 0x01, 0x00000001 },
+       { 0x000831,   1, 0x01, 0x00000004 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000014 },
+       { 0x000351,   1, 0x01, 0x00000100 },
+       { 0x000957,   1, 0x01, 0x00000003 },
+       { 0x00095d,   1, 0x01, 0x00000001 },
+       { 0x00082b,   1, 0x01, 0x00000004 },
+       { 0x000942,   1, 0x01, 0x00010001 },
+       { 0x000943,   1, 0x01, 0x00000001 },
+       { 0x0007c5,   1, 0x01, 0x00010001 },
+       { 0x000834,   1, 0x01, 0x00000001 },
+       { 0x0007c7,   1, 0x01, 0x00000001 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000001 },
+       { 0x00080c,   1, 0x01, 0x00000002 },
+       { 0x00080d,   2, 0x01, 0x00000100 },
+       { 0x00080f,   1, 0x01, 0x00000001 },
+       { 0x000823,   1, 0x01, 0x00000002 },
+       { 0x000824,   2, 0x01, 0x00000100 },
+       { 0x000826,   1, 0x01, 0x00000001 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       {}
+};
+
+struct nvc0_graph_init
+nvd9_grctx_init_unk40xx[] = {
+       { 0x404004,  11, 0x04, 0x00000000 },
+       { 0x404044,   1, 0x04, 0x00000000 },
+       { 0x404094,   1, 0x04, 0x00000000 },
+       { 0x404098,  12, 0x04, 0x00000000 },
+       { 0x4040c8,   1, 0x04, 0xf0000087 },
+       { 0x4040d0,   6, 0x04, 0x00000000 },
+       { 0x4040e8,   1, 0x04, 0x00001000 },
+       { 0x4040f8,   1, 0x04, 0x00000000 },
+       { 0x404130,   1, 0x04, 0x00000000 },
+       { 0x404134,   1, 0x04, 0x00000000 },
+       { 0x404138,   1, 0x04, 0x20000040 },
+       { 0x404150,   1, 0x04, 0x0000002e },
+       { 0x404154,   1, 0x04, 0x00000400 },
+       { 0x404158,   1, 0x04, 0x00000200 },
+       { 0x404164,   1, 0x04, 0x00000055 },
+       { 0x404168,   1, 0x04, 0x00000000 },
+       { 0x404178,   2, 0x04, 0x00000000 },
+       { 0x404200,   8, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd9_grctx_init_unk58xx[] = {
+       { 0x405800,   1, 0x04, 0x0f8000bf },
+       { 0x405830,   1, 0x04, 0x02180218 },
+       { 0x405834,   1, 0x04, 0x08000000 },
+       { 0x405838,   1, 0x04, 0x00000000 },
+       { 0x405854,   1, 0x04, 0x00000000 },
+       { 0x405870,   4, 0x04, 0x00000001 },
+       { 0x405a00,   2, 0x04, 0x00000000 },
+       { 0x405a18,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd9_grctx_init_unk64xx[] = {
+       { 0x4064a8,   1, 0x04, 0x00000000 },
+       { 0x4064ac,   1, 0x04, 0x00003fff },
+       { 0x4064b4,   3, 0x04, 0x00000000 },
+       { 0x4064c0,   1, 0x04, 0x80140078 },
+       { 0x4064c4,   1, 0x04, 0x0086ffff },
+       {}
+};
+
+struct nvc0_graph_init
+nvd9_grctx_init_rop[] = {
+       { 0x408800,   1, 0x04, 0x02802a3c },
+       { 0x408804,   1, 0x04, 0x00000040 },
+       { 0x408808,   1, 0x04, 0x1043e005 },
+       { 0x408900,   1, 0x04, 0x3080b801 },
+       { 0x408904,   1, 0x04, 0x1043e005 },
+       { 0x408908,   1, 0x04, 0x00c8102f },
+       { 0x408980,   1, 0x04, 0x0000011d },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd9_grctx_init_gpc_0[] = {
+       { 0x418380,   1, 0x04, 0x00000016 },
+       { 0x418400,   1, 0x04, 0x38004e00 },
+       { 0x418404,   1, 0x04, 0x71e0ffff },
+       { 0x41840c,   1, 0x04, 0x00001008 },
+       { 0x418410,   1, 0x04, 0x0fff0fff },
+       { 0x418414,   1, 0x04, 0x02200fff },
+       { 0x418450,   6, 0x04, 0x00000000 },
+       { 0x418468,   1, 0x04, 0x00000001 },
+       { 0x41846c,   2, 0x04, 0x00000000 },
+       { 0x418600,   1, 0x04, 0x0000001f },
+       { 0x418684,   1, 0x04, 0x0000000f },
+       { 0x418700,   1, 0x04, 0x00000002 },
+       { 0x418704,   1, 0x04, 0x00000080 },
+       { 0x418708,   3, 0x04, 0x00000000 },
+       { 0x418800,   1, 0x04, 0x7006860a },
+       { 0x418808,   3, 0x04, 0x00000000 },
+       { 0x418828,   1, 0x04, 0x00008442 },
+       { 0x418830,   1, 0x04, 0x10000001 },
+       { 0x4188d8,   1, 0x04, 0x00000008 },
+       { 0x4188e0,   1, 0x04, 0x01000000 },
+       { 0x4188e8,   5, 0x04, 0x00000000 },
+       { 0x4188fc,   1, 0x04, 0x20100008 },
+       { 0x41891c,   1, 0x04, 0x00ff00ff },
+       { 0x418924,   1, 0x04, 0x00000000 },
+       { 0x418928,   1, 0x04, 0x00ffff00 },
+       { 0x41892c,   1, 0x04, 0x0000ff00 },
+       { 0x418b00,   1, 0x04, 0x00000006 },
+       { 0x418b08,   1, 0x04, 0x0a418820 },
+       { 0x418b0c,   1, 0x04, 0x062080e6 },
+       { 0x418b10,   1, 0x04, 0x020398a4 },
+       { 0x418b14,   1, 0x04, 0x0e629062 },
+       { 0x418b18,   1, 0x04, 0x0a418820 },
+       { 0x418b1c,   1, 0x04, 0x000000e6 },
+       { 0x418bb8,   1, 0x04, 0x00000103 },
+       { 0x418c08,   1, 0x04, 0x00000001 },
+       { 0x418c10,   8, 0x04, 0x00000000 },
+       { 0x418c6c,   1, 0x04, 0x00000001 },
+       { 0x418c80,   1, 0x04, 0x20200004 },
+       { 0x418c8c,   1, 0x04, 0x00000001 },
+       { 0x419000,   1, 0x04, 0x00000780 },
+       { 0x419004,   2, 0x04, 0x00000000 },
+       { 0x419014,   1, 0x04, 0x00000004 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd9_grctx_init_tpc[] = {
+       { 0x419818,   1, 0x04, 0x00000000 },
+       { 0x41983c,   1, 0x04, 0x00038bc7 },
+       { 0x419848,   1, 0x04, 0x00000000 },
+       { 0x419864,   1, 0x04, 0x00000129 },
+       { 0x419888,   1, 0x04, 0x00000000 },
+       { 0x419a00,   1, 0x04, 0x000001f0 },
+       { 0x419a04,   1, 0x04, 0x00000001 },
+       { 0x419a08,   1, 0x04, 0x00000023 },
+       { 0x419a0c,   1, 0x04, 0x00020000 },
+       { 0x419a10,   1, 0x04, 0x00000000 },
+       { 0x419a14,   1, 0x04, 0x00000200 },
+       { 0x419a1c,   1, 0x04, 0x00000000 },
+       { 0x419a20,   1, 0x04, 0x00000800 },
+       { 0x419ac4,   1, 0x04, 0x0017f440 },
+       { 0x419b00,   1, 0x04, 0x0a418820 },
+       { 0x419b04,   1, 0x04, 0x062080e6 },
+       { 0x419b08,   1, 0x04, 0x020398a4 },
+       { 0x419b0c,   1, 0x04, 0x0e629062 },
+       { 0x419b10,   1, 0x04, 0x0a418820 },
+       { 0x419b14,   1, 0x04, 0x000000e6 },
+       { 0x419bd0,   1, 0x04, 0x00900103 },
+       { 0x419be0,   1, 0x04, 0x00400001 },
+       { 0x419be4,   1, 0x04, 0x00000000 },
+       { 0x419c00,   1, 0x04, 0x0000000a },
+       { 0x419c04,   1, 0x04, 0x00000006 },
+       { 0x419c08,   1, 0x04, 0x00000002 },
+       { 0x419c20,   1, 0x04, 0x00000000 },
+       { 0x419c24,   1, 0x04, 0x00084210 },
+       { 0x419c28,   1, 0x04, 0x3cf3cf3c },
+       { 0x419cb0,   1, 0x04, 0x00020048 },
+       { 0x419ce8,   1, 0x04, 0x00000000 },
+       { 0x419cf4,   1, 0x04, 0x00000183 },
+       { 0x419d20,   1, 0x04, 0x12180000 },
+       { 0x419d24,   1, 0x04, 0x00001fff },
+       { 0x419d44,   1, 0x04, 0x02180218 },
+       { 0x419e04,   3, 0x04, 0x00000000 },
+       { 0x419e10,   1, 0x04, 0x00000002 },
+       { 0x419e44,   1, 0x04, 0x001beff2 },
+       { 0x419e48,   1, 0x04, 0x00000000 },
+       { 0x419e4c,   1, 0x04, 0x0000000f },
+       { 0x419e50,  17, 0x04, 0x00000000 },
+       { 0x419e98,   1, 0x04, 0x00000000 },
+       { 0x419ee0,   1, 0x04, 0x00010110 },
+       { 0x419f30,  11, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init *
+nvd9_grctx_init_hub[] = {
+       nvc0_grctx_init_base,
+       nvd9_grctx_init_unk40xx,
+       nvc0_grctx_init_unk44xx,
+       nvc0_grctx_init_unk46xx,
+       nvc0_grctx_init_unk47xx,
+       nvd9_grctx_init_unk58xx,
+       nvc0_grctx_init_unk60xx,
+       nvd9_grctx_init_unk64xx,
+       nvc0_grctx_init_unk78xx,
+       nvc0_grctx_init_unk80xx,
+       nvd9_grctx_init_rop,
+};
+
+struct nvc0_graph_init *
+nvd9_grctx_init_gpc[] = {
+       nvd9_grctx_init_gpc_0,
+       nvc0_grctx_init_gpc_1,
+       nvd9_grctx_init_tpc,
+       NULL
+};
+
+struct nvc0_graph_init
+nvd9_grctx_init_mthd_magic[] = {
+       { 0x3410, 1, 0x04, 0x80002006 },
+       {}
+};
+
+struct nvc0_graph_mthd
+nvd9_grctx_init_mthd[] = {
+       { 0x9097, nvc1_grctx_init_9097, },
+       { 0x9197, nvc8_grctx_init_9197, },
+       { 0x9297, nvc8_grctx_init_9297, },
+       { 0x902d, nvc0_grctx_init_902d, },
+       { 0x9039, nvc0_grctx_init_9039, },
+       { 0x90c0, nvd9_grctx_init_90c0, },
+       { 0x902d, nvd9_grctx_init_mthd_magic, },
+       {}
+};
+
+struct nouveau_oclass *
+nvd9_grctx_oclass = &(struct nvc0_grctx_oclass) {
+       .base.handle = NV_ENGCTX(GR, 0xd9),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_context_ctor,
+               .dtor = nvc0_graph_context_dtor,
+               .init = _nouveau_graph_context_init,
+               .fini = _nouveau_graph_context_fini,
+               .rd32 = _nouveau_graph_context_rd32,
+               .wr32 = _nouveau_graph_context_wr32,
+       },
+       .main = nvc0_grctx_generate_main,
+       .mods = nvc1_grctx_generate_mods,
+       .unkn = nvc1_grctx_generate_unkn,
+       .hub  = nvd9_grctx_init_hub,
+       .gpc  = nvd9_grctx_init_gpc,
+       .icmd = nvd9_grctx_init_icmd,
+       .mthd = nvd9_grctx_init_mthd,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c
deleted file mode 100644 (file)
index ae27dae..0000000
+++ /dev/null
@@ -1,2793 +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 "nvc0.h"
-
-static void
-nve0_grctx_generate_icmd(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x400208, 0x80000000);
-       nv_icmd(priv, 0x001000, 0x00000004);
-       nv_icmd(priv, 0x000039, 0x00000000);
-       nv_icmd(priv, 0x00003a, 0x00000000);
-       nv_icmd(priv, 0x00003b, 0x00000000);
-       nv_icmd(priv, 0x0000a9, 0x0000ffff);
-       nv_icmd(priv, 0x000038, 0x0fac6881);
-       nv_icmd(priv, 0x00003d, 0x00000001);
-       nv_icmd(priv, 0x0000e8, 0x00000400);
-       nv_icmd(priv, 0x0000e9, 0x00000400);
-       nv_icmd(priv, 0x0000ea, 0x00000400);
-       nv_icmd(priv, 0x0000eb, 0x00000400);
-       nv_icmd(priv, 0x0000ec, 0x00000400);
-       nv_icmd(priv, 0x0000ed, 0x00000400);
-       nv_icmd(priv, 0x0000ee, 0x00000400);
-       nv_icmd(priv, 0x0000ef, 0x00000400);
-       nv_icmd(priv, 0x000078, 0x00000300);
-       nv_icmd(priv, 0x000079, 0x00000300);
-       nv_icmd(priv, 0x00007a, 0x00000300);
-       nv_icmd(priv, 0x00007b, 0x00000300);
-       nv_icmd(priv, 0x00007c, 0x00000300);
-       nv_icmd(priv, 0x00007d, 0x00000300);
-       nv_icmd(priv, 0x00007e, 0x00000300);
-       nv_icmd(priv, 0x00007f, 0x00000300);
-       nv_icmd(priv, 0x000050, 0x00000011);
-       nv_icmd(priv, 0x000058, 0x00000008);
-       nv_icmd(priv, 0x000059, 0x00000008);
-       nv_icmd(priv, 0x00005a, 0x00000008);
-       nv_icmd(priv, 0x00005b, 0x00000008);
-       nv_icmd(priv, 0x00005c, 0x00000008);
-       nv_icmd(priv, 0x00005d, 0x00000008);
-       nv_icmd(priv, 0x00005e, 0x00000008);
-       nv_icmd(priv, 0x00005f, 0x00000008);
-       nv_icmd(priv, 0x000208, 0x00000001);
-       nv_icmd(priv, 0x000209, 0x00000001);
-       nv_icmd(priv, 0x00020a, 0x00000001);
-       nv_icmd(priv, 0x00020b, 0x00000001);
-       nv_icmd(priv, 0x00020c, 0x00000001);
-       nv_icmd(priv, 0x00020d, 0x00000001);
-       nv_icmd(priv, 0x00020e, 0x00000001);
-       nv_icmd(priv, 0x00020f, 0x00000001);
-       nv_icmd(priv, 0x000081, 0x00000001);
-       nv_icmd(priv, 0x000085, 0x00000004);
-       nv_icmd(priv, 0x000088, 0x00000400);
-       nv_icmd(priv, 0x000090, 0x00000300);
-       nv_icmd(priv, 0x000098, 0x00001001);
-       nv_icmd(priv, 0x0000e3, 0x00000001);
-       nv_icmd(priv, 0x0000da, 0x00000001);
-       nv_icmd(priv, 0x0000f8, 0x00000003);
-       nv_icmd(priv, 0x0000fa, 0x00000001);
-       nv_icmd(priv, 0x00009f, 0x0000ffff);
-       nv_icmd(priv, 0x0000a0, 0x0000ffff);
-       nv_icmd(priv, 0x0000a1, 0x0000ffff);
-       nv_icmd(priv, 0x0000a2, 0x0000ffff);
-       nv_icmd(priv, 0x0000b1, 0x00000001);
-       nv_icmd(priv, 0x0000ad, 0x0000013e);
-       nv_icmd(priv, 0x0000e1, 0x00000010);
-       nv_icmd(priv, 0x000290, 0x00000000);
-       nv_icmd(priv, 0x000291, 0x00000000);
-       nv_icmd(priv, 0x000292, 0x00000000);
-       nv_icmd(priv, 0x000293, 0x00000000);
-       nv_icmd(priv, 0x000294, 0x00000000);
-       nv_icmd(priv, 0x000295, 0x00000000);
-       nv_icmd(priv, 0x000296, 0x00000000);
-       nv_icmd(priv, 0x000297, 0x00000000);
-       nv_icmd(priv, 0x000298, 0x00000000);
-       nv_icmd(priv, 0x000299, 0x00000000);
-       nv_icmd(priv, 0x00029a, 0x00000000);
-       nv_icmd(priv, 0x00029b, 0x00000000);
-       nv_icmd(priv, 0x00029c, 0x00000000);
-       nv_icmd(priv, 0x00029d, 0x00000000);
-       nv_icmd(priv, 0x00029e, 0x00000000);
-       nv_icmd(priv, 0x00029f, 0x00000000);
-       nv_icmd(priv, 0x0003b0, 0x00000000);
-       nv_icmd(priv, 0x0003b1, 0x00000000);
-       nv_icmd(priv, 0x0003b2, 0x00000000);
-       nv_icmd(priv, 0x0003b3, 0x00000000);
-       nv_icmd(priv, 0x0003b4, 0x00000000);
-       nv_icmd(priv, 0x0003b5, 0x00000000);
-       nv_icmd(priv, 0x0003b6, 0x00000000);
-       nv_icmd(priv, 0x0003b7, 0x00000000);
-       nv_icmd(priv, 0x0003b8, 0x00000000);
-       nv_icmd(priv, 0x0003b9, 0x00000000);
-       nv_icmd(priv, 0x0003ba, 0x00000000);
-       nv_icmd(priv, 0x0003bb, 0x00000000);
-       nv_icmd(priv, 0x0003bc, 0x00000000);
-       nv_icmd(priv, 0x0003bd, 0x00000000);
-       nv_icmd(priv, 0x0003be, 0x00000000);
-       nv_icmd(priv, 0x0003bf, 0x00000000);
-       nv_icmd(priv, 0x0002a0, 0x00000000);
-       nv_icmd(priv, 0x0002a1, 0x00000000);
-       nv_icmd(priv, 0x0002a2, 0x00000000);
-       nv_icmd(priv, 0x0002a3, 0x00000000);
-       nv_icmd(priv, 0x0002a4, 0x00000000);
-       nv_icmd(priv, 0x0002a5, 0x00000000);
-       nv_icmd(priv, 0x0002a6, 0x00000000);
-       nv_icmd(priv, 0x0002a7, 0x00000000);
-       nv_icmd(priv, 0x0002a8, 0x00000000);
-       nv_icmd(priv, 0x0002a9, 0x00000000);
-       nv_icmd(priv, 0x0002aa, 0x00000000);
-       nv_icmd(priv, 0x0002ab, 0x00000000);
-       nv_icmd(priv, 0x0002ac, 0x00000000);
-       nv_icmd(priv, 0x0002ad, 0x00000000);
-       nv_icmd(priv, 0x0002ae, 0x00000000);
-       nv_icmd(priv, 0x0002af, 0x00000000);
-       nv_icmd(priv, 0x000420, 0x00000000);
-       nv_icmd(priv, 0x000421, 0x00000000);
-       nv_icmd(priv, 0x000422, 0x00000000);
-       nv_icmd(priv, 0x000423, 0x00000000);
-       nv_icmd(priv, 0x000424, 0x00000000);
-       nv_icmd(priv, 0x000425, 0x00000000);
-       nv_icmd(priv, 0x000426, 0x00000000);
-       nv_icmd(priv, 0x000427, 0x00000000);
-       nv_icmd(priv, 0x000428, 0x00000000);
-       nv_icmd(priv, 0x000429, 0x00000000);
-       nv_icmd(priv, 0x00042a, 0x00000000);
-       nv_icmd(priv, 0x00042b, 0x00000000);
-       nv_icmd(priv, 0x00042c, 0x00000000);
-       nv_icmd(priv, 0x00042d, 0x00000000);
-       nv_icmd(priv, 0x00042e, 0x00000000);
-       nv_icmd(priv, 0x00042f, 0x00000000);
-       nv_icmd(priv, 0x0002b0, 0x00000000);
-       nv_icmd(priv, 0x0002b1, 0x00000000);
-       nv_icmd(priv, 0x0002b2, 0x00000000);
-       nv_icmd(priv, 0x0002b3, 0x00000000);
-       nv_icmd(priv, 0x0002b4, 0x00000000);
-       nv_icmd(priv, 0x0002b5, 0x00000000);
-       nv_icmd(priv, 0x0002b6, 0x00000000);
-       nv_icmd(priv, 0x0002b7, 0x00000000);
-       nv_icmd(priv, 0x0002b8, 0x00000000);
-       nv_icmd(priv, 0x0002b9, 0x00000000);
-       nv_icmd(priv, 0x0002ba, 0x00000000);
-       nv_icmd(priv, 0x0002bb, 0x00000000);
-       nv_icmd(priv, 0x0002bc, 0x00000000);
-       nv_icmd(priv, 0x0002bd, 0x00000000);
-       nv_icmd(priv, 0x0002be, 0x00000000);
-       nv_icmd(priv, 0x0002bf, 0x00000000);
-       nv_icmd(priv, 0x000430, 0x00000000);
-       nv_icmd(priv, 0x000431, 0x00000000);
-       nv_icmd(priv, 0x000432, 0x00000000);
-       nv_icmd(priv, 0x000433, 0x00000000);
-       nv_icmd(priv, 0x000434, 0x00000000);
-       nv_icmd(priv, 0x000435, 0x00000000);
-       nv_icmd(priv, 0x000436, 0x00000000);
-       nv_icmd(priv, 0x000437, 0x00000000);
-       nv_icmd(priv, 0x000438, 0x00000000);
-       nv_icmd(priv, 0x000439, 0x00000000);
-       nv_icmd(priv, 0x00043a, 0x00000000);
-       nv_icmd(priv, 0x00043b, 0x00000000);
-       nv_icmd(priv, 0x00043c, 0x00000000);
-       nv_icmd(priv, 0x00043d, 0x00000000);
-       nv_icmd(priv, 0x00043e, 0x00000000);
-       nv_icmd(priv, 0x00043f, 0x00000000);
-       nv_icmd(priv, 0x0002c0, 0x00000000);
-       nv_icmd(priv, 0x0002c1, 0x00000000);
-       nv_icmd(priv, 0x0002c2, 0x00000000);
-       nv_icmd(priv, 0x0002c3, 0x00000000);
-       nv_icmd(priv, 0x0002c4, 0x00000000);
-       nv_icmd(priv, 0x0002c5, 0x00000000);
-       nv_icmd(priv, 0x0002c6, 0x00000000);
-       nv_icmd(priv, 0x0002c7, 0x00000000);
-       nv_icmd(priv, 0x0002c8, 0x00000000);
-       nv_icmd(priv, 0x0002c9, 0x00000000);
-       nv_icmd(priv, 0x0002ca, 0x00000000);
-       nv_icmd(priv, 0x0002cb, 0x00000000);
-       nv_icmd(priv, 0x0002cc, 0x00000000);
-       nv_icmd(priv, 0x0002cd, 0x00000000);
-       nv_icmd(priv, 0x0002ce, 0x00000000);
-       nv_icmd(priv, 0x0002cf, 0x00000000);
-       nv_icmd(priv, 0x0004d0, 0x00000000);
-       nv_icmd(priv, 0x0004d1, 0x00000000);
-       nv_icmd(priv, 0x0004d2, 0x00000000);
-       nv_icmd(priv, 0x0004d3, 0x00000000);
-       nv_icmd(priv, 0x0004d4, 0x00000000);
-       nv_icmd(priv, 0x0004d5, 0x00000000);
-       nv_icmd(priv, 0x0004d6, 0x00000000);
-       nv_icmd(priv, 0x0004d7, 0x00000000);
-       nv_icmd(priv, 0x0004d8, 0x00000000);
-       nv_icmd(priv, 0x0004d9, 0x00000000);
-       nv_icmd(priv, 0x0004da, 0x00000000);
-       nv_icmd(priv, 0x0004db, 0x00000000);
-       nv_icmd(priv, 0x0004dc, 0x00000000);
-       nv_icmd(priv, 0x0004dd, 0x00000000);
-       nv_icmd(priv, 0x0004de, 0x00000000);
-       nv_icmd(priv, 0x0004df, 0x00000000);
-       nv_icmd(priv, 0x000720, 0x00000000);
-       nv_icmd(priv, 0x000721, 0x00000000);
-       nv_icmd(priv, 0x000722, 0x00000000);
-       nv_icmd(priv, 0x000723, 0x00000000);
-       nv_icmd(priv, 0x000724, 0x00000000);
-       nv_icmd(priv, 0x000725, 0x00000000);
-       nv_icmd(priv, 0x000726, 0x00000000);
-       nv_icmd(priv, 0x000727, 0x00000000);
-       nv_icmd(priv, 0x000728, 0x00000000);
-       nv_icmd(priv, 0x000729, 0x00000000);
-       nv_icmd(priv, 0x00072a, 0x00000000);
-       nv_icmd(priv, 0x00072b, 0x00000000);
-       nv_icmd(priv, 0x00072c, 0x00000000);
-       nv_icmd(priv, 0x00072d, 0x00000000);
-       nv_icmd(priv, 0x00072e, 0x00000000);
-       nv_icmd(priv, 0x00072f, 0x00000000);
-       nv_icmd(priv, 0x0008c0, 0x00000000);
-       nv_icmd(priv, 0x0008c1, 0x00000000);
-       nv_icmd(priv, 0x0008c2, 0x00000000);
-       nv_icmd(priv, 0x0008c3, 0x00000000);
-       nv_icmd(priv, 0x0008c4, 0x00000000);
-       nv_icmd(priv, 0x0008c5, 0x00000000);
-       nv_icmd(priv, 0x0008c6, 0x00000000);
-       nv_icmd(priv, 0x0008c7, 0x00000000);
-       nv_icmd(priv, 0x0008c8, 0x00000000);
-       nv_icmd(priv, 0x0008c9, 0x00000000);
-       nv_icmd(priv, 0x0008ca, 0x00000000);
-       nv_icmd(priv, 0x0008cb, 0x00000000);
-       nv_icmd(priv, 0x0008cc, 0x00000000);
-       nv_icmd(priv, 0x0008cd, 0x00000000);
-       nv_icmd(priv, 0x0008ce, 0x00000000);
-       nv_icmd(priv, 0x0008cf, 0x00000000);
-       nv_icmd(priv, 0x000890, 0x00000000);
-       nv_icmd(priv, 0x000891, 0x00000000);
-       nv_icmd(priv, 0x000892, 0x00000000);
-       nv_icmd(priv, 0x000893, 0x00000000);
-       nv_icmd(priv, 0x000894, 0x00000000);
-       nv_icmd(priv, 0x000895, 0x00000000);
-       nv_icmd(priv, 0x000896, 0x00000000);
-       nv_icmd(priv, 0x000897, 0x00000000);
-       nv_icmd(priv, 0x000898, 0x00000000);
-       nv_icmd(priv, 0x000899, 0x00000000);
-       nv_icmd(priv, 0x00089a, 0x00000000);
-       nv_icmd(priv, 0x00089b, 0x00000000);
-       nv_icmd(priv, 0x00089c, 0x00000000);
-       nv_icmd(priv, 0x00089d, 0x00000000);
-       nv_icmd(priv, 0x00089e, 0x00000000);
-       nv_icmd(priv, 0x00089f, 0x00000000);
-       nv_icmd(priv, 0x0008e0, 0x00000000);
-       nv_icmd(priv, 0x0008e1, 0x00000000);
-       nv_icmd(priv, 0x0008e2, 0x00000000);
-       nv_icmd(priv, 0x0008e3, 0x00000000);
-       nv_icmd(priv, 0x0008e4, 0x00000000);
-       nv_icmd(priv, 0x0008e5, 0x00000000);
-       nv_icmd(priv, 0x0008e6, 0x00000000);
-       nv_icmd(priv, 0x0008e7, 0x00000000);
-       nv_icmd(priv, 0x0008e8, 0x00000000);
-       nv_icmd(priv, 0x0008e9, 0x00000000);
-       nv_icmd(priv, 0x0008ea, 0x00000000);
-       nv_icmd(priv, 0x0008eb, 0x00000000);
-       nv_icmd(priv, 0x0008ec, 0x00000000);
-       nv_icmd(priv, 0x0008ed, 0x00000000);
-       nv_icmd(priv, 0x0008ee, 0x00000000);
-       nv_icmd(priv, 0x0008ef, 0x00000000);
-       nv_icmd(priv, 0x0008a0, 0x00000000);
-       nv_icmd(priv, 0x0008a1, 0x00000000);
-       nv_icmd(priv, 0x0008a2, 0x00000000);
-       nv_icmd(priv, 0x0008a3, 0x00000000);
-       nv_icmd(priv, 0x0008a4, 0x00000000);
-       nv_icmd(priv, 0x0008a5, 0x00000000);
-       nv_icmd(priv, 0x0008a6, 0x00000000);
-       nv_icmd(priv, 0x0008a7, 0x00000000);
-       nv_icmd(priv, 0x0008a8, 0x00000000);
-       nv_icmd(priv, 0x0008a9, 0x00000000);
-       nv_icmd(priv, 0x0008aa, 0x00000000);
-       nv_icmd(priv, 0x0008ab, 0x00000000);
-       nv_icmd(priv, 0x0008ac, 0x00000000);
-       nv_icmd(priv, 0x0008ad, 0x00000000);
-       nv_icmd(priv, 0x0008ae, 0x00000000);
-       nv_icmd(priv, 0x0008af, 0x00000000);
-       nv_icmd(priv, 0x0008f0, 0x00000000);
-       nv_icmd(priv, 0x0008f1, 0x00000000);
-       nv_icmd(priv, 0x0008f2, 0x00000000);
-       nv_icmd(priv, 0x0008f3, 0x00000000);
-       nv_icmd(priv, 0x0008f4, 0x00000000);
-       nv_icmd(priv, 0x0008f5, 0x00000000);
-       nv_icmd(priv, 0x0008f6, 0x00000000);
-       nv_icmd(priv, 0x0008f7, 0x00000000);
-       nv_icmd(priv, 0x0008f8, 0x00000000);
-       nv_icmd(priv, 0x0008f9, 0x00000000);
-       nv_icmd(priv, 0x0008fa, 0x00000000);
-       nv_icmd(priv, 0x0008fb, 0x00000000);
-       nv_icmd(priv, 0x0008fc, 0x00000000);
-       nv_icmd(priv, 0x0008fd, 0x00000000);
-       nv_icmd(priv, 0x0008fe, 0x00000000);
-       nv_icmd(priv, 0x0008ff, 0x00000000);
-       nv_icmd(priv, 0x00094c, 0x000000ff);
-       nv_icmd(priv, 0x00094d, 0xffffffff);
-       nv_icmd(priv, 0x00094e, 0x00000002);
-       nv_icmd(priv, 0x0002ec, 0x00000001);
-       nv_icmd(priv, 0x000303, 0x00000001);
-       nv_icmd(priv, 0x0002e6, 0x00000001);
-       nv_icmd(priv, 0x000466, 0x00000052);
-       nv_icmd(priv, 0x000301, 0x3f800000);
-       nv_icmd(priv, 0x000304, 0x30201000);
-       nv_icmd(priv, 0x000305, 0x70605040);
-       nv_icmd(priv, 0x000306, 0xb8a89888);
-       nv_icmd(priv, 0x000307, 0xf8e8d8c8);
-       nv_icmd(priv, 0x00030a, 0x00ffff00);
-       nv_icmd(priv, 0x00030b, 0x0000001a);
-       nv_icmd(priv, 0x00030c, 0x00000001);
-       nv_icmd(priv, 0x000318, 0x00000001);
-       nv_icmd(priv, 0x000340, 0x00000000);
-       nv_icmd(priv, 0x000375, 0x00000001);
-       nv_icmd(priv, 0x00037d, 0x00000006);
-       nv_icmd(priv, 0x0003a0, 0x00000002);
-       nv_icmd(priv, 0x0003aa, 0x00000001);
-       nv_icmd(priv, 0x0003a9, 0x00000001);
-       nv_icmd(priv, 0x000380, 0x00000001);
-       nv_icmd(priv, 0x000383, 0x00000011);
-       nv_icmd(priv, 0x000360, 0x00000040);
-       nv_icmd(priv, 0x000366, 0x00000000);
-       nv_icmd(priv, 0x000367, 0x00000000);
-       nv_icmd(priv, 0x000368, 0x00000fff);
-       nv_icmd(priv, 0x000370, 0x00000000);
-       nv_icmd(priv, 0x000371, 0x00000000);
-       nv_icmd(priv, 0x000372, 0x000fffff);
-       nv_icmd(priv, 0x00037a, 0x00000012);
-       nv_icmd(priv, 0x000619, 0x00000003);
-       nv_icmd(priv, 0x000811, 0x00000003);
-       nv_icmd(priv, 0x000812, 0x00000004);
-       nv_icmd(priv, 0x000813, 0x00000006);
-       nv_icmd(priv, 0x000814, 0x00000008);
-       nv_icmd(priv, 0x000815, 0x0000000b);
-       nv_icmd(priv, 0x000800, 0x00000001);
-       nv_icmd(priv, 0x000801, 0x00000001);
-       nv_icmd(priv, 0x000802, 0x00000001);
-       nv_icmd(priv, 0x000803, 0x00000001);
-       nv_icmd(priv, 0x000804, 0x00000001);
-       nv_icmd(priv, 0x000805, 0x00000001);
-       nv_icmd(priv, 0x000632, 0x00000001);
-       nv_icmd(priv, 0x000633, 0x00000002);
-       nv_icmd(priv, 0x000634, 0x00000003);
-       nv_icmd(priv, 0x000635, 0x00000004);
-       nv_icmd(priv, 0x000654, 0x3f800000);
-       nv_icmd(priv, 0x000657, 0x3f800000);
-       nv_icmd(priv, 0x000655, 0x3f800000);
-       nv_icmd(priv, 0x000656, 0x3f800000);
-       nv_icmd(priv, 0x0006cd, 0x3f800000);
-       nv_icmd(priv, 0x0007f5, 0x3f800000);
-       nv_icmd(priv, 0x0007dc, 0x39291909);
-       nv_icmd(priv, 0x0007dd, 0x79695949);
-       nv_icmd(priv, 0x0007de, 0xb9a99989);
-       nv_icmd(priv, 0x0007df, 0xf9e9d9c9);
-       nv_icmd(priv, 0x0007e8, 0x00003210);
-       nv_icmd(priv, 0x0007e9, 0x00007654);
-       nv_icmd(priv, 0x0007ea, 0x00000098);
-       nv_icmd(priv, 0x0007ec, 0x39291909);
-       nv_icmd(priv, 0x0007ed, 0x79695949);
-       nv_icmd(priv, 0x0007ee, 0xb9a99989);
-       nv_icmd(priv, 0x0007ef, 0xf9e9d9c9);
-       nv_icmd(priv, 0x0007f0, 0x00003210);
-       nv_icmd(priv, 0x0007f1, 0x00007654);
-       nv_icmd(priv, 0x0007f2, 0x00000098);
-       nv_icmd(priv, 0x0005a5, 0x00000001);
-       nv_icmd(priv, 0x000980, 0x00000000);
-       nv_icmd(priv, 0x000981, 0x00000000);
-       nv_icmd(priv, 0x000982, 0x00000000);
-       nv_icmd(priv, 0x000983, 0x00000000);
-       nv_icmd(priv, 0x000984, 0x00000000);
-       nv_icmd(priv, 0x000985, 0x00000000);
-       nv_icmd(priv, 0x000986, 0x00000000);
-       nv_icmd(priv, 0x000987, 0x00000000);
-       nv_icmd(priv, 0x000988, 0x00000000);
-       nv_icmd(priv, 0x000989, 0x00000000);
-       nv_icmd(priv, 0x00098a, 0x00000000);
-       nv_icmd(priv, 0x00098b, 0x00000000);
-       nv_icmd(priv, 0x00098c, 0x00000000);
-       nv_icmd(priv, 0x00098d, 0x00000000);
-       nv_icmd(priv, 0x00098e, 0x00000000);
-       nv_icmd(priv, 0x00098f, 0x00000000);
-       nv_icmd(priv, 0x000990, 0x00000000);
-       nv_icmd(priv, 0x000991, 0x00000000);
-       nv_icmd(priv, 0x000992, 0x00000000);
-       nv_icmd(priv, 0x000993, 0x00000000);
-       nv_icmd(priv, 0x000994, 0x00000000);
-       nv_icmd(priv, 0x000995, 0x00000000);
-       nv_icmd(priv, 0x000996, 0x00000000);
-       nv_icmd(priv, 0x000997, 0x00000000);
-       nv_icmd(priv, 0x000998, 0x00000000);
-       nv_icmd(priv, 0x000999, 0x00000000);
-       nv_icmd(priv, 0x00099a, 0x00000000);
-       nv_icmd(priv, 0x00099b, 0x00000000);
-       nv_icmd(priv, 0x00099c, 0x00000000);
-       nv_icmd(priv, 0x00099d, 0x00000000);
-       nv_icmd(priv, 0x00099e, 0x00000000);
-       nv_icmd(priv, 0x00099f, 0x00000000);
-       nv_icmd(priv, 0x0009a0, 0x00000000);
-       nv_icmd(priv, 0x0009a1, 0x00000000);
-       nv_icmd(priv, 0x0009a2, 0x00000000);
-       nv_icmd(priv, 0x0009a3, 0x00000000);
-       nv_icmd(priv, 0x0009a4, 0x00000000);
-       nv_icmd(priv, 0x0009a5, 0x00000000);
-       nv_icmd(priv, 0x0009a6, 0x00000000);
-       nv_icmd(priv, 0x0009a7, 0x00000000);
-       nv_icmd(priv, 0x0009a8, 0x00000000);
-       nv_icmd(priv, 0x0009a9, 0x00000000);
-       nv_icmd(priv, 0x0009aa, 0x00000000);
-       nv_icmd(priv, 0x0009ab, 0x00000000);
-       nv_icmd(priv, 0x0009ac, 0x00000000);
-       nv_icmd(priv, 0x0009ad, 0x00000000);
-       nv_icmd(priv, 0x0009ae, 0x00000000);
-       nv_icmd(priv, 0x0009af, 0x00000000);
-       nv_icmd(priv, 0x0009b0, 0x00000000);
-       nv_icmd(priv, 0x0009b1, 0x00000000);
-       nv_icmd(priv, 0x0009b2, 0x00000000);
-       nv_icmd(priv, 0x0009b3, 0x00000000);
-       nv_icmd(priv, 0x0009b4, 0x00000000);
-       nv_icmd(priv, 0x0009b5, 0x00000000);
-       nv_icmd(priv, 0x0009b6, 0x00000000);
-       nv_icmd(priv, 0x0009b7, 0x00000000);
-       nv_icmd(priv, 0x0009b8, 0x00000000);
-       nv_icmd(priv, 0x0009b9, 0x00000000);
-       nv_icmd(priv, 0x0009ba, 0x00000000);
-       nv_icmd(priv, 0x0009bb, 0x00000000);
-       nv_icmd(priv, 0x0009bc, 0x00000000);
-       nv_icmd(priv, 0x0009bd, 0x00000000);
-       nv_icmd(priv, 0x0009be, 0x00000000);
-       nv_icmd(priv, 0x0009bf, 0x00000000);
-       nv_icmd(priv, 0x0009c0, 0x00000000);
-       nv_icmd(priv, 0x0009c1, 0x00000000);
-       nv_icmd(priv, 0x0009c2, 0x00000000);
-       nv_icmd(priv, 0x0009c3, 0x00000000);
-       nv_icmd(priv, 0x0009c4, 0x00000000);
-       nv_icmd(priv, 0x0009c5, 0x00000000);
-       nv_icmd(priv, 0x0009c6, 0x00000000);
-       nv_icmd(priv, 0x0009c7, 0x00000000);
-       nv_icmd(priv, 0x0009c8, 0x00000000);
-       nv_icmd(priv, 0x0009c9, 0x00000000);
-       nv_icmd(priv, 0x0009ca, 0x00000000);
-       nv_icmd(priv, 0x0009cb, 0x00000000);
-       nv_icmd(priv, 0x0009cc, 0x00000000);
-       nv_icmd(priv, 0x0009cd, 0x00000000);
-       nv_icmd(priv, 0x0009ce, 0x00000000);
-       nv_icmd(priv, 0x0009cf, 0x00000000);
-       nv_icmd(priv, 0x0009d0, 0x00000000);
-       nv_icmd(priv, 0x0009d1, 0x00000000);
-       nv_icmd(priv, 0x0009d2, 0x00000000);
-       nv_icmd(priv, 0x0009d3, 0x00000000);
-       nv_icmd(priv, 0x0009d4, 0x00000000);
-       nv_icmd(priv, 0x0009d5, 0x00000000);
-       nv_icmd(priv, 0x0009d6, 0x00000000);
-       nv_icmd(priv, 0x0009d7, 0x00000000);
-       nv_icmd(priv, 0x0009d8, 0x00000000);
-       nv_icmd(priv, 0x0009d9, 0x00000000);
-       nv_icmd(priv, 0x0009da, 0x00000000);
-       nv_icmd(priv, 0x0009db, 0x00000000);
-       nv_icmd(priv, 0x0009dc, 0x00000000);
-       nv_icmd(priv, 0x0009dd, 0x00000000);
-       nv_icmd(priv, 0x0009de, 0x00000000);
-       nv_icmd(priv, 0x0009df, 0x00000000);
-       nv_icmd(priv, 0x0009e0, 0x00000000);
-       nv_icmd(priv, 0x0009e1, 0x00000000);
-       nv_icmd(priv, 0x0009e2, 0x00000000);
-       nv_icmd(priv, 0x0009e3, 0x00000000);
-       nv_icmd(priv, 0x0009e4, 0x00000000);
-       nv_icmd(priv, 0x0009e5, 0x00000000);
-       nv_icmd(priv, 0x0009e6, 0x00000000);
-       nv_icmd(priv, 0x0009e7, 0x00000000);
-       nv_icmd(priv, 0x0009e8, 0x00000000);
-       nv_icmd(priv, 0x0009e9, 0x00000000);
-       nv_icmd(priv, 0x0009ea, 0x00000000);
-       nv_icmd(priv, 0x0009eb, 0x00000000);
-       nv_icmd(priv, 0x0009ec, 0x00000000);
-       nv_icmd(priv, 0x0009ed, 0x00000000);
-       nv_icmd(priv, 0x0009ee, 0x00000000);
-       nv_icmd(priv, 0x0009ef, 0x00000000);
-       nv_icmd(priv, 0x0009f0, 0x00000000);
-       nv_icmd(priv, 0x0009f1, 0x00000000);
-       nv_icmd(priv, 0x0009f2, 0x00000000);
-       nv_icmd(priv, 0x0009f3, 0x00000000);
-       nv_icmd(priv, 0x0009f4, 0x00000000);
-       nv_icmd(priv, 0x0009f5, 0x00000000);
-       nv_icmd(priv, 0x0009f6, 0x00000000);
-       nv_icmd(priv, 0x0009f7, 0x00000000);
-       nv_icmd(priv, 0x0009f8, 0x00000000);
-       nv_icmd(priv, 0x0009f9, 0x00000000);
-       nv_icmd(priv, 0x0009fa, 0x00000000);
-       nv_icmd(priv, 0x0009fb, 0x00000000);
-       nv_icmd(priv, 0x0009fc, 0x00000000);
-       nv_icmd(priv, 0x0009fd, 0x00000000);
-       nv_icmd(priv, 0x0009fe, 0x00000000);
-       nv_icmd(priv, 0x0009ff, 0x00000000);
-       nv_icmd(priv, 0x000468, 0x00000004);
-       nv_icmd(priv, 0x00046c, 0x00000001);
-       nv_icmd(priv, 0x000470, 0x00000000);
-       nv_icmd(priv, 0x000471, 0x00000000);
-       nv_icmd(priv, 0x000472, 0x00000000);
-       nv_icmd(priv, 0x000473, 0x00000000);
-       nv_icmd(priv, 0x000474, 0x00000000);
-       nv_icmd(priv, 0x000475, 0x00000000);
-       nv_icmd(priv, 0x000476, 0x00000000);
-       nv_icmd(priv, 0x000477, 0x00000000);
-       nv_icmd(priv, 0x000478, 0x00000000);
-       nv_icmd(priv, 0x000479, 0x00000000);
-       nv_icmd(priv, 0x00047a, 0x00000000);
-       nv_icmd(priv, 0x00047b, 0x00000000);
-       nv_icmd(priv, 0x00047c, 0x00000000);
-       nv_icmd(priv, 0x00047d, 0x00000000);
-       nv_icmd(priv, 0x00047e, 0x00000000);
-       nv_icmd(priv, 0x00047f, 0x00000000);
-       nv_icmd(priv, 0x000480, 0x00000000);
-       nv_icmd(priv, 0x000481, 0x00000000);
-       nv_icmd(priv, 0x000482, 0x00000000);
-       nv_icmd(priv, 0x000483, 0x00000000);
-       nv_icmd(priv, 0x000484, 0x00000000);
-       nv_icmd(priv, 0x000485, 0x00000000);
-       nv_icmd(priv, 0x000486, 0x00000000);
-       nv_icmd(priv, 0x000487, 0x00000000);
-       nv_icmd(priv, 0x000488, 0x00000000);
-       nv_icmd(priv, 0x000489, 0x00000000);
-       nv_icmd(priv, 0x00048a, 0x00000000);
-       nv_icmd(priv, 0x00048b, 0x00000000);
-       nv_icmd(priv, 0x00048c, 0x00000000);
-       nv_icmd(priv, 0x00048d, 0x00000000);
-       nv_icmd(priv, 0x00048e, 0x00000000);
-       nv_icmd(priv, 0x00048f, 0x00000000);
-       nv_icmd(priv, 0x000490, 0x00000000);
-       nv_icmd(priv, 0x000491, 0x00000000);
-       nv_icmd(priv, 0x000492, 0x00000000);
-       nv_icmd(priv, 0x000493, 0x00000000);
-       nv_icmd(priv, 0x000494, 0x00000000);
-       nv_icmd(priv, 0x000495, 0x00000000);
-       nv_icmd(priv, 0x000496, 0x00000000);
-       nv_icmd(priv, 0x000497, 0x00000000);
-       nv_icmd(priv, 0x000498, 0x00000000);
-       nv_icmd(priv, 0x000499, 0x00000000);
-       nv_icmd(priv, 0x00049a, 0x00000000);
-       nv_icmd(priv, 0x00049b, 0x00000000);
-       nv_icmd(priv, 0x00049c, 0x00000000);
-       nv_icmd(priv, 0x00049d, 0x00000000);
-       nv_icmd(priv, 0x00049e, 0x00000000);
-       nv_icmd(priv, 0x00049f, 0x00000000);
-       nv_icmd(priv, 0x0004a0, 0x00000000);
-       nv_icmd(priv, 0x0004a1, 0x00000000);
-       nv_icmd(priv, 0x0004a2, 0x00000000);
-       nv_icmd(priv, 0x0004a3, 0x00000000);
-       nv_icmd(priv, 0x0004a4, 0x00000000);
-       nv_icmd(priv, 0x0004a5, 0x00000000);
-       nv_icmd(priv, 0x0004a6, 0x00000000);
-       nv_icmd(priv, 0x0004a7, 0x00000000);
-       nv_icmd(priv, 0x0004a8, 0x00000000);
-       nv_icmd(priv, 0x0004a9, 0x00000000);
-       nv_icmd(priv, 0x0004aa, 0x00000000);
-       nv_icmd(priv, 0x0004ab, 0x00000000);
-       nv_icmd(priv, 0x0004ac, 0x00000000);
-       nv_icmd(priv, 0x0004ad, 0x00000000);
-       nv_icmd(priv, 0x0004ae, 0x00000000);
-       nv_icmd(priv, 0x0004af, 0x00000000);
-       nv_icmd(priv, 0x0004b0, 0x00000000);
-       nv_icmd(priv, 0x0004b1, 0x00000000);
-       nv_icmd(priv, 0x0004b2, 0x00000000);
-       nv_icmd(priv, 0x0004b3, 0x00000000);
-       nv_icmd(priv, 0x0004b4, 0x00000000);
-       nv_icmd(priv, 0x0004b5, 0x00000000);
-       nv_icmd(priv, 0x0004b6, 0x00000000);
-       nv_icmd(priv, 0x0004b7, 0x00000000);
-       nv_icmd(priv, 0x0004b8, 0x00000000);
-       nv_icmd(priv, 0x0004b9, 0x00000000);
-       nv_icmd(priv, 0x0004ba, 0x00000000);
-       nv_icmd(priv, 0x0004bb, 0x00000000);
-       nv_icmd(priv, 0x0004bc, 0x00000000);
-       nv_icmd(priv, 0x0004bd, 0x00000000);
-       nv_icmd(priv, 0x0004be, 0x00000000);
-       nv_icmd(priv, 0x0004bf, 0x00000000);
-       nv_icmd(priv, 0x0004c0, 0x00000000);
-       nv_icmd(priv, 0x0004c1, 0x00000000);
-       nv_icmd(priv, 0x0004c2, 0x00000000);
-       nv_icmd(priv, 0x0004c3, 0x00000000);
-       nv_icmd(priv, 0x0004c4, 0x00000000);
-       nv_icmd(priv, 0x0004c5, 0x00000000);
-       nv_icmd(priv, 0x0004c6, 0x00000000);
-       nv_icmd(priv, 0x0004c7, 0x00000000);
-       nv_icmd(priv, 0x0004c8, 0x00000000);
-       nv_icmd(priv, 0x0004c9, 0x00000000);
-       nv_icmd(priv, 0x0004ca, 0x00000000);
-       nv_icmd(priv, 0x0004cb, 0x00000000);
-       nv_icmd(priv, 0x0004cc, 0x00000000);
-       nv_icmd(priv, 0x0004cd, 0x00000000);
-       nv_icmd(priv, 0x0004ce, 0x00000000);
-       nv_icmd(priv, 0x0004cf, 0x00000000);
-       nv_icmd(priv, 0x000510, 0x3f800000);
-       nv_icmd(priv, 0x000511, 0x3f800000);
-       nv_icmd(priv, 0x000512, 0x3f800000);
-       nv_icmd(priv, 0x000513, 0x3f800000);
-       nv_icmd(priv, 0x000514, 0x3f800000);
-       nv_icmd(priv, 0x000515, 0x3f800000);
-       nv_icmd(priv, 0x000516, 0x3f800000);
-       nv_icmd(priv, 0x000517, 0x3f800000);
-       nv_icmd(priv, 0x000518, 0x3f800000);
-       nv_icmd(priv, 0x000519, 0x3f800000);
-       nv_icmd(priv, 0x00051a, 0x3f800000);
-       nv_icmd(priv, 0x00051b, 0x3f800000);
-       nv_icmd(priv, 0x00051c, 0x3f800000);
-       nv_icmd(priv, 0x00051d, 0x3f800000);
-       nv_icmd(priv, 0x00051e, 0x3f800000);
-       nv_icmd(priv, 0x00051f, 0x3f800000);
-       nv_icmd(priv, 0x000520, 0x000002b6);
-       nv_icmd(priv, 0x000529, 0x00000001);
-       nv_icmd(priv, 0x000530, 0xffff0000);
-       nv_icmd(priv, 0x000531, 0xffff0000);
-       nv_icmd(priv, 0x000532, 0xffff0000);
-       nv_icmd(priv, 0x000533, 0xffff0000);
-       nv_icmd(priv, 0x000534, 0xffff0000);
-       nv_icmd(priv, 0x000535, 0xffff0000);
-       nv_icmd(priv, 0x000536, 0xffff0000);
-       nv_icmd(priv, 0x000537, 0xffff0000);
-       nv_icmd(priv, 0x000538, 0xffff0000);
-       nv_icmd(priv, 0x000539, 0xffff0000);
-       nv_icmd(priv, 0x00053a, 0xffff0000);
-       nv_icmd(priv, 0x00053b, 0xffff0000);
-       nv_icmd(priv, 0x00053c, 0xffff0000);
-       nv_icmd(priv, 0x00053d, 0xffff0000);
-       nv_icmd(priv, 0x00053e, 0xffff0000);
-       nv_icmd(priv, 0x00053f, 0xffff0000);
-       nv_icmd(priv, 0x000585, 0x0000003f);
-       nv_icmd(priv, 0x000576, 0x00000003);
-       nv_icmd(priv, 0x00057b, 0x00000059);
-       nv_icmd(priv, 0x000586, 0x00000040);
-       nv_icmd(priv, 0x000582, 0x00000080);
-       nv_icmd(priv, 0x000583, 0x00000080);
-       nv_icmd(priv, 0x0005c2, 0x00000001);
-       nv_icmd(priv, 0x000638, 0x00000001);
-       nv_icmd(priv, 0x000639, 0x00000001);
-       nv_icmd(priv, 0x00063a, 0x00000002);
-       nv_icmd(priv, 0x00063b, 0x00000001);
-       nv_icmd(priv, 0x00063c, 0x00000001);
-       nv_icmd(priv, 0x00063d, 0x00000002);
-       nv_icmd(priv, 0x00063e, 0x00000001);
-       nv_icmd(priv, 0x0008b8, 0x00000001);
-       nv_icmd(priv, 0x0008b9, 0x00000001);
-       nv_icmd(priv, 0x0008ba, 0x00000001);
-       nv_icmd(priv, 0x0008bb, 0x00000001);
-       nv_icmd(priv, 0x0008bc, 0x00000001);
-       nv_icmd(priv, 0x0008bd, 0x00000001);
-       nv_icmd(priv, 0x0008be, 0x00000001);
-       nv_icmd(priv, 0x0008bf, 0x00000001);
-       nv_icmd(priv, 0x000900, 0x00000001);
-       nv_icmd(priv, 0x000901, 0x00000001);
-       nv_icmd(priv, 0x000902, 0x00000001);
-       nv_icmd(priv, 0x000903, 0x00000001);
-       nv_icmd(priv, 0x000904, 0x00000001);
-       nv_icmd(priv, 0x000905, 0x00000001);
-       nv_icmd(priv, 0x000906, 0x00000001);
-       nv_icmd(priv, 0x000907, 0x00000001);
-       nv_icmd(priv, 0x000908, 0x00000002);
-       nv_icmd(priv, 0x000909, 0x00000002);
-       nv_icmd(priv, 0x00090a, 0x00000002);
-       nv_icmd(priv, 0x00090b, 0x00000002);
-       nv_icmd(priv, 0x00090c, 0x00000002);
-       nv_icmd(priv, 0x00090d, 0x00000002);
-       nv_icmd(priv, 0x00090e, 0x00000002);
-       nv_icmd(priv, 0x00090f, 0x00000002);
-       nv_icmd(priv, 0x000910, 0x00000001);
-       nv_icmd(priv, 0x000911, 0x00000001);
-       nv_icmd(priv, 0x000912, 0x00000001);
-       nv_icmd(priv, 0x000913, 0x00000001);
-       nv_icmd(priv, 0x000914, 0x00000001);
-       nv_icmd(priv, 0x000915, 0x00000001);
-       nv_icmd(priv, 0x000916, 0x00000001);
-       nv_icmd(priv, 0x000917, 0x00000001);
-       nv_icmd(priv, 0x000918, 0x00000001);
-       nv_icmd(priv, 0x000919, 0x00000001);
-       nv_icmd(priv, 0x00091a, 0x00000001);
-       nv_icmd(priv, 0x00091b, 0x00000001);
-       nv_icmd(priv, 0x00091c, 0x00000001);
-       nv_icmd(priv, 0x00091d, 0x00000001);
-       nv_icmd(priv, 0x00091e, 0x00000001);
-       nv_icmd(priv, 0x00091f, 0x00000001);
-       nv_icmd(priv, 0x000920, 0x00000002);
-       nv_icmd(priv, 0x000921, 0x00000002);
-       nv_icmd(priv, 0x000922, 0x00000002);
-       nv_icmd(priv, 0x000923, 0x00000002);
-       nv_icmd(priv, 0x000924, 0x00000002);
-       nv_icmd(priv, 0x000925, 0x00000002);
-       nv_icmd(priv, 0x000926, 0x00000002);
-       nv_icmd(priv, 0x000927, 0x00000002);
-       nv_icmd(priv, 0x000928, 0x00000001);
-       nv_icmd(priv, 0x000929, 0x00000001);
-       nv_icmd(priv, 0x00092a, 0x00000001);
-       nv_icmd(priv, 0x00092b, 0x00000001);
-       nv_icmd(priv, 0x00092c, 0x00000001);
-       nv_icmd(priv, 0x00092d, 0x00000001);
-       nv_icmd(priv, 0x00092e, 0x00000001);
-       nv_icmd(priv, 0x00092f, 0x00000001);
-       nv_icmd(priv, 0x000648, 0x00000001);
-       nv_icmd(priv, 0x000649, 0x00000001);
-       nv_icmd(priv, 0x00064a, 0x00000001);
-       nv_icmd(priv, 0x00064b, 0x00000001);
-       nv_icmd(priv, 0x00064c, 0x00000001);
-       nv_icmd(priv, 0x00064d, 0x00000001);
-       nv_icmd(priv, 0x00064e, 0x00000001);
-       nv_icmd(priv, 0x00064f, 0x00000001);
-       nv_icmd(priv, 0x000650, 0x00000001);
-       nv_icmd(priv, 0x000658, 0x0000000f);
-       nv_icmd(priv, 0x0007ff, 0x0000000a);
-       nv_icmd(priv, 0x00066a, 0x40000000);
-       nv_icmd(priv, 0x00066b, 0x10000000);
-       nv_icmd(priv, 0x00066c, 0xffff0000);
-       nv_icmd(priv, 0x00066d, 0xffff0000);
-       nv_icmd(priv, 0x0007af, 0x00000008);
-       nv_icmd(priv, 0x0007b0, 0x00000008);
-       nv_icmd(priv, 0x0007f6, 0x00000001);
-       nv_icmd(priv, 0x0006b2, 0x00000055);
-       nv_icmd(priv, 0x0007ad, 0x00000003);
-       nv_icmd(priv, 0x000937, 0x00000001);
-       nv_icmd(priv, 0x000971, 0x00000008);
-       nv_icmd(priv, 0x000972, 0x00000040);
-       nv_icmd(priv, 0x000973, 0x0000012c);
-       nv_icmd(priv, 0x00097c, 0x00000040);
-       nv_icmd(priv, 0x000979, 0x00000003);
-       nv_icmd(priv, 0x000975, 0x00000020);
-       nv_icmd(priv, 0x000976, 0x00000001);
-       nv_icmd(priv, 0x000977, 0x00000020);
-       nv_icmd(priv, 0x000978, 0x00000001);
-       nv_icmd(priv, 0x000957, 0x00000003);
-       nv_icmd(priv, 0x00095e, 0x20164010);
-       nv_icmd(priv, 0x00095f, 0x00000020);
-       nv_icmd(priv, 0x00097d, 0x00000020);
-       nv_icmd(priv, 0x000683, 0x00000006);
-       nv_icmd(priv, 0x000685, 0x003fffff);
-       nv_icmd(priv, 0x000687, 0x003fffff);
-       nv_icmd(priv, 0x0006a0, 0x00000005);
-       nv_icmd(priv, 0x000840, 0x00400008);
-       nv_icmd(priv, 0x000841, 0x08000080);
-       nv_icmd(priv, 0x000842, 0x00400008);
-       nv_icmd(priv, 0x000843, 0x08000080);
-       nv_icmd(priv, 0x000818, 0x00000000);
-       nv_icmd(priv, 0x000819, 0x00000000);
-       nv_icmd(priv, 0x00081a, 0x00000000);
-       nv_icmd(priv, 0x00081b, 0x00000000);
-       nv_icmd(priv, 0x00081c, 0x00000000);
-       nv_icmd(priv, 0x00081d, 0x00000000);
-       nv_icmd(priv, 0x00081e, 0x00000000);
-       nv_icmd(priv, 0x00081f, 0x00000000);
-       nv_icmd(priv, 0x000848, 0x00000000);
-       nv_icmd(priv, 0x000849, 0x00000000);
-       nv_icmd(priv, 0x00084a, 0x00000000);
-       nv_icmd(priv, 0x00084b, 0x00000000);
-       nv_icmd(priv, 0x00084c, 0x00000000);
-       nv_icmd(priv, 0x00084d, 0x00000000);
-       nv_icmd(priv, 0x00084e, 0x00000000);
-       nv_icmd(priv, 0x00084f, 0x00000000);
-       nv_icmd(priv, 0x000850, 0x00000000);
-       nv_icmd(priv, 0x000851, 0x00000000);
-       nv_icmd(priv, 0x000852, 0x00000000);
-       nv_icmd(priv, 0x000853, 0x00000000);
-       nv_icmd(priv, 0x000854, 0x00000000);
-       nv_icmd(priv, 0x000855, 0x00000000);
-       nv_icmd(priv, 0x000856, 0x00000000);
-       nv_icmd(priv, 0x000857, 0x00000000);
-       nv_icmd(priv, 0x000738, 0x00000000);
-       nv_icmd(priv, 0x0006aa, 0x00000001);
-       nv_icmd(priv, 0x0006ab, 0x00000002);
-       nv_icmd(priv, 0x0006ac, 0x00000080);
-       nv_icmd(priv, 0x0006ad, 0x00000100);
-       nv_icmd(priv, 0x0006ae, 0x00000100);
-       nv_icmd(priv, 0x0006b1, 0x00000011);
-       nv_icmd(priv, 0x0006bb, 0x000000cf);
-       nv_icmd(priv, 0x0006ce, 0x2a712488);
-       nv_icmd(priv, 0x000739, 0x4085c000);
-       nv_icmd(priv, 0x00073a, 0x00000080);
-       nv_icmd(priv, 0x000786, 0x80000100);
-       nv_icmd(priv, 0x00073c, 0x00010100);
-       nv_icmd(priv, 0x00073d, 0x02800000);
-       nv_icmd(priv, 0x000787, 0x000000cf);
-       nv_icmd(priv, 0x00078c, 0x00000008);
-       nv_icmd(priv, 0x000792, 0x00000001);
-       nv_icmd(priv, 0x000794, 0x00000001);
-       nv_icmd(priv, 0x000795, 0x00000001);
-       nv_icmd(priv, 0x000796, 0x00000001);
-       nv_icmd(priv, 0x000797, 0x000000cf);
-       nv_icmd(priv, 0x000836, 0x00000001);
-       nv_icmd(priv, 0x00079a, 0x00000002);
-       nv_icmd(priv, 0x000833, 0x04444480);
-       nv_icmd(priv, 0x0007a1, 0x00000001);
-       nv_icmd(priv, 0x0007a3, 0x00000001);
-       nv_icmd(priv, 0x0007a4, 0x00000001);
-       nv_icmd(priv, 0x0007a5, 0x00000001);
-       nv_icmd(priv, 0x000831, 0x00000004);
-       nv_icmd(priv, 0x000b07, 0x00000002);
-       nv_icmd(priv, 0x000b08, 0x00000100);
-       nv_icmd(priv, 0x000b09, 0x00000100);
-       nv_icmd(priv, 0x000b0a, 0x00000001);
-       nv_icmd(priv, 0x000a04, 0x000000ff);
-       nv_icmd(priv, 0x000a0b, 0x00000040);
-       nv_icmd(priv, 0x00097f, 0x00000100);
-       nv_icmd(priv, 0x000a02, 0x00000001);
-       nv_icmd(priv, 0x000809, 0x00000007);
-       nv_icmd(priv, 0x00c221, 0x00000040);
-       nv_icmd(priv, 0x00c1b0, 0x0000000f);
-       nv_icmd(priv, 0x00c1b1, 0x0000000f);
-       nv_icmd(priv, 0x00c1b2, 0x0000000f);
-       nv_icmd(priv, 0x00c1b3, 0x0000000f);
-       nv_icmd(priv, 0x00c1b4, 0x0000000f);
-       nv_icmd(priv, 0x00c1b5, 0x0000000f);
-       nv_icmd(priv, 0x00c1b6, 0x0000000f);
-       nv_icmd(priv, 0x00c1b7, 0x0000000f);
-       nv_icmd(priv, 0x00c1b8, 0x0fac6881);
-       nv_icmd(priv, 0x00c1b9, 0x00fac688);
-       nv_icmd(priv, 0x00c401, 0x00000001);
-       nv_icmd(priv, 0x00c402, 0x00010001);
-       nv_icmd(priv, 0x00c403, 0x00000001);
-       nv_icmd(priv, 0x00c404, 0x00000001);
-       nv_icmd(priv, 0x00c40e, 0x00000020);
-       nv_icmd(priv, 0x00c500, 0x00000003);
-       nv_icmd(priv, 0x01e100, 0x00000001);
-       nv_icmd(priv, 0x001000, 0x00000002);
-       nv_icmd(priv, 0x0006aa, 0x00000001);
-       nv_icmd(priv, 0x0006ad, 0x00000100);
-       nv_icmd(priv, 0x0006ae, 0x00000100);
-       nv_icmd(priv, 0x0006b1, 0x00000011);
-       nv_icmd(priv, 0x00078c, 0x00000008);
-       nv_icmd(priv, 0x000792, 0x00000001);
-       nv_icmd(priv, 0x000794, 0x00000001);
-       nv_icmd(priv, 0x000795, 0x00000001);
-       nv_icmd(priv, 0x000796, 0x00000001);
-       nv_icmd(priv, 0x000797, 0x000000cf);
-       nv_icmd(priv, 0x00079a, 0x00000002);
-       nv_icmd(priv, 0x000833, 0x04444480);
-       nv_icmd(priv, 0x0007a1, 0x00000001);
-       nv_icmd(priv, 0x0007a3, 0x00000001);
-       nv_icmd(priv, 0x0007a4, 0x00000001);
-       nv_icmd(priv, 0x0007a5, 0x00000001);
-       nv_icmd(priv, 0x000831, 0x00000004);
-       nv_icmd(priv, 0x01e100, 0x00000001);
-       nv_icmd(priv, 0x001000, 0x00000008);
-       nv_icmd(priv, 0x000039, 0x00000000);
-       nv_icmd(priv, 0x00003a, 0x00000000);
-       nv_icmd(priv, 0x00003b, 0x00000000);
-       nv_icmd(priv, 0x000380, 0x00000001);
-       nv_icmd(priv, 0x000366, 0x00000000);
-       nv_icmd(priv, 0x000367, 0x00000000);
-       nv_icmd(priv, 0x000368, 0x00000fff);
-       nv_icmd(priv, 0x000370, 0x00000000);
-       nv_icmd(priv, 0x000371, 0x00000000);
-       nv_icmd(priv, 0x000372, 0x000fffff);
-       nv_icmd(priv, 0x000813, 0x00000006);
-       nv_icmd(priv, 0x000814, 0x00000008);
-       nv_icmd(priv, 0x000957, 0x00000003);
-       nv_icmd(priv, 0x000818, 0x00000000);
-       nv_icmd(priv, 0x000819, 0x00000000);
-       nv_icmd(priv, 0x00081a, 0x00000000);
-       nv_icmd(priv, 0x00081b, 0x00000000);
-       nv_icmd(priv, 0x00081c, 0x00000000);
-       nv_icmd(priv, 0x00081d, 0x00000000);
-       nv_icmd(priv, 0x00081e, 0x00000000);
-       nv_icmd(priv, 0x00081f, 0x00000000);
-       nv_icmd(priv, 0x000848, 0x00000000);
-       nv_icmd(priv, 0x000849, 0x00000000);
-       nv_icmd(priv, 0x00084a, 0x00000000);
-       nv_icmd(priv, 0x00084b, 0x00000000);
-       nv_icmd(priv, 0x00084c, 0x00000000);
-       nv_icmd(priv, 0x00084d, 0x00000000);
-       nv_icmd(priv, 0x00084e, 0x00000000);
-       nv_icmd(priv, 0x00084f, 0x00000000);
-       nv_icmd(priv, 0x000850, 0x00000000);
-       nv_icmd(priv, 0x000851, 0x00000000);
-       nv_icmd(priv, 0x000852, 0x00000000);
-       nv_icmd(priv, 0x000853, 0x00000000);
-       nv_icmd(priv, 0x000854, 0x00000000);
-       nv_icmd(priv, 0x000855, 0x00000000);
-       nv_icmd(priv, 0x000856, 0x00000000);
-       nv_icmd(priv, 0x000857, 0x00000000);
-       nv_icmd(priv, 0x000738, 0x00000000);
-       nv_icmd(priv, 0x000b07, 0x00000002);
-       nv_icmd(priv, 0x000b08, 0x00000100);
-       nv_icmd(priv, 0x000b09, 0x00000100);
-       nv_icmd(priv, 0x000b0a, 0x00000001);
-       nv_icmd(priv, 0x000a04, 0x000000ff);
-       nv_icmd(priv, 0x00097f, 0x00000100);
-       nv_icmd(priv, 0x000a02, 0x00000001);
-       nv_icmd(priv, 0x000809, 0x00000007);
-       nv_icmd(priv, 0x00c221, 0x00000040);
-       nv_icmd(priv, 0x00c401, 0x00000001);
-       nv_icmd(priv, 0x00c402, 0x00010001);
-       nv_icmd(priv, 0x00c403, 0x00000001);
-       nv_icmd(priv, 0x00c404, 0x00000001);
-       nv_icmd(priv, 0x00c40e, 0x00000020);
-       nv_icmd(priv, 0x00c500, 0x00000003);
-       nv_icmd(priv, 0x01e100, 0x00000001);
-       nv_icmd(priv, 0x001000, 0x00000001);
-       nv_icmd(priv, 0x000b07, 0x00000002);
-       nv_icmd(priv, 0x000b08, 0x00000100);
-       nv_icmd(priv, 0x000b09, 0x00000100);
-       nv_icmd(priv, 0x000b0a, 0x00000001);
-       nv_icmd(priv, 0x01e100, 0x00000001);
-       nv_wr32(priv, 0x400208, 0x00000000);
-}
-
-static void
-nve0_grctx_generate_a097(struct nvc0_graph_priv *priv)
-{
-       nv_mthd(priv, 0xa097, 0x0800, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0840, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0880, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x08c0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0900, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0940, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0980, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x09c0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0804, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0844, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0884, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x08c4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0904, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0944, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0984, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x09c4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0808, 0x00000400);
-       nv_mthd(priv, 0xa097, 0x0848, 0x00000400);
-       nv_mthd(priv, 0xa097, 0x0888, 0x00000400);
-       nv_mthd(priv, 0xa097, 0x08c8, 0x00000400);
-       nv_mthd(priv, 0xa097, 0x0908, 0x00000400);
-       nv_mthd(priv, 0xa097, 0x0948, 0x00000400);
-       nv_mthd(priv, 0xa097, 0x0988, 0x00000400);
-       nv_mthd(priv, 0xa097, 0x09c8, 0x00000400);
-       nv_mthd(priv, 0xa097, 0x080c, 0x00000300);
-       nv_mthd(priv, 0xa097, 0x084c, 0x00000300);
-       nv_mthd(priv, 0xa097, 0x088c, 0x00000300);
-       nv_mthd(priv, 0xa097, 0x08cc, 0x00000300);
-       nv_mthd(priv, 0xa097, 0x090c, 0x00000300);
-       nv_mthd(priv, 0xa097, 0x094c, 0x00000300);
-       nv_mthd(priv, 0xa097, 0x098c, 0x00000300);
-       nv_mthd(priv, 0xa097, 0x09cc, 0x00000300);
-       nv_mthd(priv, 0xa097, 0x0810, 0x000000cf);
-       nv_mthd(priv, 0xa097, 0x0850, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0890, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x08d0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0910, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0950, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0990, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x09d0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0814, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x0854, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x0894, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x08d4, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x0914, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x0954, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x0994, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x09d4, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x0818, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x0858, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x0898, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x08d8, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x0918, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x0958, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x0998, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x09d8, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x081c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x085c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x089c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x08dc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x091c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x095c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x099c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x09dc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0820, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0860, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x08a0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x08e0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0920, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0960, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x09a0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x09e0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c00, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c10, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c20, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c30, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c40, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c50, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c60, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c70, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c80, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c90, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ca0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cb0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cc0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cd0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ce0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cf0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c04, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c14, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c24, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c34, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c44, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c54, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c64, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c74, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c84, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c94, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ca4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cb4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cc4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cd4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ce4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cf4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c08, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c18, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c28, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c38, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c48, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c58, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c68, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c78, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c88, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c98, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ca8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cb8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cc8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cd8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ce8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cf8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c0c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c1c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c2c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c3c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c4c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c5c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c6c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c7c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c8c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1c9c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cbc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ccc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cdc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1cfc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d00, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d10, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d20, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d30, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d40, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d50, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d60, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d70, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d80, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d90, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1da0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1db0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1dc0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1dd0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1de0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1df0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d04, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d14, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d24, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d34, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d44, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d54, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d64, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d74, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d84, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d94, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1da4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1db4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1dc4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1dd4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1de4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1df4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d08, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d18, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d28, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d38, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d48, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d58, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d68, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d78, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d88, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d98, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1da8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1db8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1dc8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1dd8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1de8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1df8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d0c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d1c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d2c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d3c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d4c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d5c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d6c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d7c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d8c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1d9c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1dac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1dbc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1dcc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ddc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1dec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1dfc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f00, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f08, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f10, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f18, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f20, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f28, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f30, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f38, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f40, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f48, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f50, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f58, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f60, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f68, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f70, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f78, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f04, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f0c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f14, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f1c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f24, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f2c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f34, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f3c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f44, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f4c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f54, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f5c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f64, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f6c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f74, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f7c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f80, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f88, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f90, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f98, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fa0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fa8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fb0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fb8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fc0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fc8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fd0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fd8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fe0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fe8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ff0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ff8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f84, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f8c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f94, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1f9c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fa4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fb4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fbc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fc4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fcc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fd4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fdc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fe4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1fec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ff4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1ffc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2000, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2040, 0x00000011);
-       nv_mthd(priv, 0xa097, 0x2080, 0x00000020);
-       nv_mthd(priv, 0xa097, 0x20c0, 0x00000030);
-       nv_mthd(priv, 0xa097, 0x2100, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x2140, 0x00000051);
-       nv_mthd(priv, 0xa097, 0x200c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x204c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x208c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x20cc, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x210c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x214c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x2010, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2050, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2090, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x20d0, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x2110, 0x00000003);
-       nv_mthd(priv, 0xa097, 0x2150, 0x00000004);
-       nv_mthd(priv, 0xa097, 0x0380, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03a0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03c0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03e0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0384, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03a4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03c4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03e4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0388, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03a8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03c8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03e8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x038c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03ac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03cc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x03ec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0700, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0710, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0720, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0730, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0704, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0714, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0724, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0734, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0708, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0718, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0728, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0738, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2800, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2804, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2808, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x280c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2810, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2814, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2818, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x281c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2820, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2824, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2828, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x282c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2830, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2834, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2838, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x283c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2840, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2844, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2848, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x284c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2850, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2854, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2858, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x285c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2860, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2864, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2868, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x286c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2870, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2874, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2878, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x287c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2880, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2884, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2888, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x288c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2890, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2894, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2898, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x289c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28a0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28a4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28a8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28ac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28b0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28b4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28b8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28bc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28c0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28c4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28c8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28cc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28d0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28d4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28d8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28dc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28e0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28e4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28e8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28ec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28f0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28f4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28f8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x28fc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2900, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2904, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2908, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x290c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2910, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2914, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2918, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x291c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2920, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2924, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2928, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x292c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2930, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2934, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2938, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x293c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2940, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2944, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2948, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x294c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2950, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2954, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2958, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x295c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2960, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2964, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2968, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x296c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2970, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2974, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2978, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x297c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2980, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2984, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2988, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x298c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2990, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2994, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2998, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x299c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29a0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29a4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29a8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29ac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29b0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29b4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29b8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29bc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29c0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29c4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29c8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29cc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29d0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29d4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29d8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29dc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29e0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29e4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29e8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29ec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29f0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29f4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29f8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x29fc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a00, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a20, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a40, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a60, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a80, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0aa0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ac0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ae0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b00, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b20, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b40, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b60, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b80, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ba0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bc0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0be0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a04, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a24, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a44, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a64, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a84, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0aa4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ac4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ae4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b04, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b24, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b44, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b64, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b84, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ba4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bc4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0be4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a08, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a28, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a48, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a68, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a88, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0aa8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ac8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ae8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b08, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b28, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b48, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b68, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b88, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ba8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bc8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0be8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a0c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a2c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a4c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a6c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a8c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0aac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0acc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0aec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b0c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b2c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b4c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b6c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b8c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bcc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a10, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a30, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a50, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a70, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a90, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ab0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ad0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0af0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b10, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b30, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b50, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b70, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b90, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bb0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bd0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bf0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a14, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a34, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a54, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a74, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0a94, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ab4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ad4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0af4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b14, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b34, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b54, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b74, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0b94, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bb4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bd4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0bf4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c00, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c10, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c20, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c30, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c40, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c50, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c60, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c70, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c80, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c90, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ca0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cb0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cc0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cd0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ce0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cf0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c04, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c14, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c24, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c34, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c44, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c54, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c64, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c74, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c84, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c94, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ca4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cb4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cc4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cd4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ce4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cf4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c08, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c18, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c28, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c38, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c48, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c58, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c68, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c78, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c88, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c98, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ca8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cb8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cc8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cd8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ce8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0cf8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0c0c, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0c1c, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0c2c, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0c3c, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0c4c, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0c5c, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0c6c, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0c7c, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0c8c, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0c9c, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0cac, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0cbc, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0ccc, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0cdc, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0cec, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0cfc, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0d00, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d08, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d10, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d18, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d20, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d28, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d30, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d38, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d04, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d0c, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d14, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d1c, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d24, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d2c, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d34, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d3c, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e00, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0e10, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0e20, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0e30, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0e40, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0e50, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0e60, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0e70, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0e80, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0e90, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ea0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0eb0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ec0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ed0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ee0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ef0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0e04, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e14, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e24, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e34, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e44, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e54, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e64, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e74, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e84, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e94, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0ea4, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0eb4, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0ec4, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0ed4, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0ee4, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0ef4, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e08, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e18, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e28, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e38, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e48, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e58, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e68, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e78, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e88, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0e98, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0ea8, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0eb8, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0ec8, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0ed8, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0ee8, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0ef8, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d40, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d48, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d50, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d58, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d44, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d4c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d54, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d5c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1e00, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e20, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e40, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e60, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e80, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1ea0, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1ec0, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1ee0, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e04, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e24, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e44, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e64, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e84, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1ea4, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1ec4, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1ee4, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e08, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1e28, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1e48, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1e68, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1e88, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1ea8, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1ec8, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1ee8, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1e0c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e2c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e4c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e6c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e8c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1eac, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1ecc, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1eec, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e10, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e30, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e50, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e70, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e90, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1eb0, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1ed0, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1ef0, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e14, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1e34, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1e54, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1e74, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1e94, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1eb4, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1ed4, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1ef4, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1e18, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e38, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e58, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e78, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1e98, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1eb8, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1ed8, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1ef8, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x3400, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3404, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3408, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x340c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3410, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3414, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3418, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x341c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3420, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3424, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3428, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x342c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3430, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3434, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3438, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x343c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3440, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3444, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3448, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x344c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3450, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3454, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3458, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x345c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3460, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3464, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3468, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x346c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3470, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3474, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3478, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x347c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3480, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3484, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3488, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x348c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3490, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3494, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3498, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x349c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34a0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34a4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34a8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34ac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34b0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34b4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34b8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34bc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34c0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34c4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34c8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34cc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34d0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34d4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34d8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34dc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34e0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34e4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34e8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34ec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34f0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34f4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34f8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x34fc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3500, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3504, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3508, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x350c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3510, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3514, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3518, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x351c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3520, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3524, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3528, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x352c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3530, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3534, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3538, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x353c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3540, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3544, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3548, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x354c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3550, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3554, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3558, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x355c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3560, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3564, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3568, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x356c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3570, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3574, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3578, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x357c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3580, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3584, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3588, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x358c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3590, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3594, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x3598, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x359c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35a0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35a4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35a8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35ac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35b0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35b4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35b8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35bc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35c0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35c4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35c8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35cc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35d0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35d4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35d8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35dc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35e0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35e4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35e8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35ec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35f0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35f4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35f8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x35fc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x030c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1944, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1514, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d68, 0x0000ffff);
-       nv_mthd(priv, 0xa097, 0x121c, 0x0fac6881);
-       nv_mthd(priv, 0xa097, 0x0fac, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1538, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x0fe0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0fe4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0fe8, 0x00000014);
-       nv_mthd(priv, 0xa097, 0x0fec, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x0ff0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x179c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1228, 0x00000400);
-       nv_mthd(priv, 0xa097, 0x122c, 0x00000300);
-       nv_mthd(priv, 0xa097, 0x1230, 0x00010001);
-       nv_mthd(priv, 0xa097, 0x07f8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x15b4, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x15cc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1534, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0fb0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x15d0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x153c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x16b4, 0x00000003);
-       nv_mthd(priv, 0xa097, 0x0fbc, 0x0000ffff);
-       nv_mthd(priv, 0xa097, 0x0fc0, 0x0000ffff);
-       nv_mthd(priv, 0xa097, 0x0fc4, 0x0000ffff);
-       nv_mthd(priv, 0xa097, 0x0fc8, 0x0000ffff);
-       nv_mthd(priv, 0xa097, 0x0df8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0dfc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1948, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1970, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x161c, 0x000009f0);
-       nv_mthd(priv, 0xa097, 0x0dcc, 0x00000010);
-       nv_mthd(priv, 0xa097, 0x163c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x15e4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1160, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1164, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1168, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x116c, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1170, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1174, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1178, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x117c, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1180, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1184, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1188, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x118c, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1190, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1194, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1198, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x119c, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11a0, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11a4, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11a8, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11ac, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11b0, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11b4, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11b8, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11bc, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11c0, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11c4, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11c8, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11cc, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11d0, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11d4, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11d8, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x11dc, 0x25e00040);
-       nv_mthd(priv, 0xa097, 0x1880, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1884, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1888, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x188c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1890, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1894, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1898, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x189c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18a0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18a4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18a8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18ac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18b0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18b4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18b8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18bc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18c0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18c4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18c8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18cc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18d0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18d4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18d8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18dc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18e0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18e4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18e8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18ec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18f0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18f4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18f8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x18fc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0f84, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0f88, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x17c8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x17cc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x17d0, 0x000000ff);
-       nv_mthd(priv, 0xa097, 0x17d4, 0xffffffff);
-       nv_mthd(priv, 0xa097, 0x17d8, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x17dc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x15f4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x15f8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1434, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1438, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d74, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0dec, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x13a4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1318, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1644, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0748, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0de8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1648, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x12a4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1120, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1124, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1128, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x112c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1118, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x164c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1658, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1910, 0x00000290);
-       nv_mthd(priv, 0xa097, 0x1518, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x165c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1520, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1604, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1570, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x13b0, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x13b4, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x020c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1670, 0x30201000);
-       nv_mthd(priv, 0xa097, 0x1674, 0x70605040);
-       nv_mthd(priv, 0xa097, 0x1678, 0xb8a89888);
-       nv_mthd(priv, 0xa097, 0x167c, 0xf8e8d8c8);
-       nv_mthd(priv, 0xa097, 0x166c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1680, 0x00ffff00);
-       nv_mthd(priv, 0xa097, 0x12d0, 0x00000003);
-       nv_mthd(priv, 0xa097, 0x12d4, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1684, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1688, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0dac, 0x00001b02);
-       nv_mthd(priv, 0xa097, 0x0db0, 0x00001b02);
-       nv_mthd(priv, 0xa097, 0x0db4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x168c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x15bc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x156c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x187c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1110, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x0dc0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0dc4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0dc8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1234, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1690, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x12ac, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x0790, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0794, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0798, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x079c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x07a0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x077c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1000, 0x00000010);
-       nv_mthd(priv, 0xa097, 0x10fc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1290, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0218, 0x00000010);
-       nv_mthd(priv, 0xa097, 0x12d8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x12dc, 0x00000010);
-       nv_mthd(priv, 0xa097, 0x0d94, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x155c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1560, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1564, 0x00000fff);
-       nv_mthd(priv, 0xa097, 0x1574, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1578, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x157c, 0x000fffff);
-       nv_mthd(priv, 0xa097, 0x1354, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1610, 0x00000012);
-       nv_mthd(priv, 0xa097, 0x1608, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x160c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x260c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x07ac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x162c, 0x00000003);
-       nv_mthd(priv, 0xa097, 0x0210, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0320, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0324, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0328, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x032c, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0330, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0334, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0338, 0x3f800000);
-       nv_mthd(priv, 0xa097, 0x0750, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0760, 0x39291909);
-       nv_mthd(priv, 0xa097, 0x0764, 0x79695949);
-       nv_mthd(priv, 0xa097, 0x0768, 0xb9a99989);
-       nv_mthd(priv, 0xa097, 0x076c, 0xf9e9d9c9);
-       nv_mthd(priv, 0xa097, 0x0770, 0x30201000);
-       nv_mthd(priv, 0xa097, 0x0774, 0x70605040);
-       nv_mthd(priv, 0xa097, 0x0778, 0x00009080);
-       nv_mthd(priv, 0xa097, 0x0780, 0x39291909);
-       nv_mthd(priv, 0xa097, 0x0784, 0x79695949);
-       nv_mthd(priv, 0xa097, 0x0788, 0xb9a99989);
-       nv_mthd(priv, 0xa097, 0x078c, 0xf9e9d9c9);
-       nv_mthd(priv, 0xa097, 0x07d0, 0x30201000);
-       nv_mthd(priv, 0xa097, 0x07d4, 0x70605040);
-       nv_mthd(priv, 0xa097, 0x07d8, 0x00009080);
-       nv_mthd(priv, 0xa097, 0x037c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x0740, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0744, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x2600, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1918, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x191c, 0x00000900);
-       nv_mthd(priv, 0xa097, 0x1920, 0x00000405);
-       nv_mthd(priv, 0xa097, 0x1308, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1924, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x13ac, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x192c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x193c, 0x00002c1c);
-       nv_mthd(priv, 0xa097, 0x0d7c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0f8c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x02c0, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1510, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1940, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ff4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0ff8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x194c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1950, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1968, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1590, 0x0000003f);
-       nv_mthd(priv, 0xa097, 0x07e8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x07ec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x07f0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x07f4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x196c, 0x00000011);
-       nv_mthd(priv, 0xa097, 0x02e4, 0x0000b001);
-       nv_mthd(priv, 0xa097, 0x036c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0370, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x197c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0fcc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0fd0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x02d8, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x1980, 0x00000080);
-       nv_mthd(priv, 0xa097, 0x1504, 0x00000080);
-       nv_mthd(priv, 0xa097, 0x1984, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0300, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x13a8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x12ec, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1310, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1314, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1380, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1384, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1388, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x138c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1390, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1394, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x139c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1398, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1594, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1598, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x159c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x15a0, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x15a4, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x0f54, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0f58, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0f5c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x19bc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0f9c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0fa0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x12cc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x12e8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x130c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1360, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1364, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1368, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x136c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1370, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1374, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1378, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x137c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x133c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1340, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1344, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1348, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x134c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1350, 0x00000002);
-       nv_mthd(priv, 0xa097, 0x1358, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x12e4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x131c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1320, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1324, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1328, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x19c0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1140, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x19c4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x19c8, 0x00001500);
-       nv_mthd(priv, 0xa097, 0x135c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0f90, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x19e0, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x19e4, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x19e8, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x19ec, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x19f0, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x19f4, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x19f8, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x19fc, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x19cc, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x15b8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1a00, 0x00001111);
-       nv_mthd(priv, 0xa097, 0x1a04, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1a08, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1a0c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1a10, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1a14, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1a18, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1a1c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d6c, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x0d70, 0xffff0000);
-       nv_mthd(priv, 0xa097, 0x10f8, 0x00001010);
-       nv_mthd(priv, 0xa097, 0x0d80, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d84, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d88, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d8c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0d90, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0da0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x07a4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x07a8, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1508, 0x80000000);
-       nv_mthd(priv, 0xa097, 0x150c, 0x40000000);
-       nv_mthd(priv, 0xa097, 0x1668, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0318, 0x00000008);
-       nv_mthd(priv, 0xa097, 0x031c, 0x00000008);
-       nv_mthd(priv, 0xa097, 0x0d9c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x0374, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0378, 0x00000020);
-       nv_mthd(priv, 0xa097, 0x07dc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x074c, 0x00000055);
-       nv_mthd(priv, 0xa097, 0x1420, 0x00000003);
-       nv_mthd(priv, 0xa097, 0x17bc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x17c0, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x17c4, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1008, 0x00000008);
-       nv_mthd(priv, 0xa097, 0x100c, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x1010, 0x0000012c);
-       nv_mthd(priv, 0xa097, 0x0d60, 0x00000040);
-       nv_mthd(priv, 0xa097, 0x075c, 0x00000003);
-       nv_mthd(priv, 0xa097, 0x1018, 0x00000020);
-       nv_mthd(priv, 0xa097, 0x101c, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1020, 0x00000020);
-       nv_mthd(priv, 0xa097, 0x1024, 0x00000001);
-       nv_mthd(priv, 0xa097, 0x1444, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x1448, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x144c, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0360, 0x20164010);
-       nv_mthd(priv, 0xa097, 0x0364, 0x00000020);
-       nv_mthd(priv, 0xa097, 0x0368, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0de4, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0204, 0x00000006);
-       nv_mthd(priv, 0xa097, 0x0208, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x02cc, 0x003fffff);
-       nv_mthd(priv, 0xa097, 0x02d0, 0x003fffff);
-       nv_mthd(priv, 0xa097, 0x1220, 0x00000005);
-       nv_mthd(priv, 0xa097, 0x0fdc, 0x00000000);
-       nv_mthd(priv, 0xa097, 0x0f98, 0x00400008);
-       nv_mthd(priv, 0xa097, 0x1284, 0x08000080);
-       nv_mthd(priv, 0xa097, 0x1450, 0x00400008);
-       nv_mthd(priv, 0xa097, 0x1454, 0x08000080);
-       nv_mthd(priv, 0xa097, 0x0214, 0x00000000);
-}
-
-static void
-nve0_grctx_generate_902d(struct nvc0_graph_priv *priv)
-{
-       nv_mthd(priv, 0x902d, 0x0200, 0x000000cf);
-       nv_mthd(priv, 0x902d, 0x0204, 0x00000001);
-       nv_mthd(priv, 0x902d, 0x0208, 0x00000020);
-       nv_mthd(priv, 0x902d, 0x020c, 0x00000001);
-       nv_mthd(priv, 0x902d, 0x0210, 0x00000000);
-       nv_mthd(priv, 0x902d, 0x0214, 0x00000080);
-       nv_mthd(priv, 0x902d, 0x0218, 0x00000100);
-       nv_mthd(priv, 0x902d, 0x021c, 0x00000100);
-       nv_mthd(priv, 0x902d, 0x0220, 0x00000000);
-       nv_mthd(priv, 0x902d, 0x0224, 0x00000000);
-       nv_mthd(priv, 0x902d, 0x0230, 0x000000cf);
-       nv_mthd(priv, 0x902d, 0x0234, 0x00000001);
-       nv_mthd(priv, 0x902d, 0x0238, 0x00000020);
-       nv_mthd(priv, 0x902d, 0x023c, 0x00000001);
-       nv_mthd(priv, 0x902d, 0x0244, 0x00000080);
-       nv_mthd(priv, 0x902d, 0x0248, 0x00000100);
-       nv_mthd(priv, 0x902d, 0x024c, 0x00000100);
-       nv_mthd(priv, 0x902d, 0x3410, 0x00000000);
-}
-
-static void
-nve0_graph_generate_unk40xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x404010, 0x0);
-       nv_wr32(priv, 0x404014, 0x0);
-       nv_wr32(priv, 0x404018, 0x0);
-       nv_wr32(priv, 0x40401c, 0x0);
-       nv_wr32(priv, 0x404020, 0x0);
-       nv_wr32(priv, 0x404024, 0xe000);
-       nv_wr32(priv, 0x404028, 0x0);
-       nv_wr32(priv, 0x4040a8, 0x0);
-       nv_wr32(priv, 0x4040ac, 0x0);
-       nv_wr32(priv, 0x4040b0, 0x0);
-       nv_wr32(priv, 0x4040b4, 0x0);
-       nv_wr32(priv, 0x4040b8, 0x0);
-       nv_wr32(priv, 0x4040bc, 0x0);
-       nv_wr32(priv, 0x4040c0, 0x0);
-       nv_wr32(priv, 0x4040c4, 0x0);
-       nv_wr32(priv, 0x4040c8, 0xf800008f);
-       nv_wr32(priv, 0x4040d0, 0x0);
-       nv_wr32(priv, 0x4040d4, 0x0);
-       nv_wr32(priv, 0x4040d8, 0x0);
-       nv_wr32(priv, 0x4040dc, 0x0);
-       nv_wr32(priv, 0x4040e0, 0x0);
-       nv_wr32(priv, 0x4040e4, 0x0);
-       nv_wr32(priv, 0x4040e8, 0x1000);
-       nv_wr32(priv, 0x4040f8, 0x0);
-       nv_wr32(priv, 0x404130, 0x0);
-       nv_wr32(priv, 0x404134, 0x0);
-       nv_wr32(priv, 0x404138, 0x20000040);
-       nv_wr32(priv, 0x404150, 0x2e);
-       nv_wr32(priv, 0x404154, 0x400);
-       nv_wr32(priv, 0x404158, 0x200);
-       nv_wr32(priv, 0x404164, 0x55);
-       nv_wr32(priv, 0x4041a0, 0x0);
-       nv_wr32(priv, 0x4041a4, 0x0);
-       nv_wr32(priv, 0x4041a8, 0x0);
-       nv_wr32(priv, 0x4041ac, 0x0);
-       nv_wr32(priv, 0x404200, 0x0);
-       nv_wr32(priv, 0x404204, 0x0);
-       nv_wr32(priv, 0x404208, 0x0);
-       nv_wr32(priv, 0x40420c, 0x0);
-}
-
-static void
-nve0_graph_generate_unk44xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x404404, 0x0);
-       nv_wr32(priv, 0x404408, 0x0);
-       nv_wr32(priv, 0x40440c, 0x0);
-       nv_wr32(priv, 0x404410, 0x0);
-       nv_wr32(priv, 0x404414, 0x0);
-       nv_wr32(priv, 0x404418, 0x0);
-       nv_wr32(priv, 0x40441c, 0x0);
-       nv_wr32(priv, 0x404420, 0x0);
-       nv_wr32(priv, 0x404424, 0x0);
-       nv_wr32(priv, 0x404428, 0x0);
-       nv_wr32(priv, 0x40442c, 0x0);
-       nv_wr32(priv, 0x404430, 0x0);
-       nv_wr32(priv, 0x404434, 0x0);
-       nv_wr32(priv, 0x404438, 0x0);
-       nv_wr32(priv, 0x404460, 0x0);
-       nv_wr32(priv, 0x404464, 0x0);
-       nv_wr32(priv, 0x404468, 0xffffff);
-       nv_wr32(priv, 0x40446c, 0x0);
-       nv_wr32(priv, 0x404480, 0x1);
-       nv_wr32(priv, 0x404498, 0x1);
-}
-
-static void
-nve0_graph_generate_unk46xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x404604, 0x14);
-       nv_wr32(priv, 0x404608, 0x0);
-       nv_wr32(priv, 0x40460c, 0x3fff);
-       nv_wr32(priv, 0x404610, 0x100);
-       nv_wr32(priv, 0x404618, 0x0);
-       nv_wr32(priv, 0x40461c, 0x0);
-       nv_wr32(priv, 0x404620, 0x0);
-       nv_wr32(priv, 0x404624, 0x0);
-       nv_wr32(priv, 0x40462c, 0x0);
-       nv_wr32(priv, 0x404630, 0x0);
-       nv_wr32(priv, 0x404640, 0x0);
-       nv_wr32(priv, 0x404654, 0x0);
-       nv_wr32(priv, 0x404660, 0x0);
-       nv_wr32(priv, 0x404678, 0x0);
-       nv_wr32(priv, 0x40467c, 0x2);
-       nv_wr32(priv, 0x404680, 0x0);
-       nv_wr32(priv, 0x404684, 0x0);
-       nv_wr32(priv, 0x404688, 0x0);
-       nv_wr32(priv, 0x40468c, 0x0);
-       nv_wr32(priv, 0x404690, 0x0);
-       nv_wr32(priv, 0x404694, 0x0);
-       nv_wr32(priv, 0x404698, 0x0);
-       nv_wr32(priv, 0x40469c, 0x0);
-       nv_wr32(priv, 0x4046a0, 0x7f0080);
-       nv_wr32(priv, 0x4046a4, 0x0);
-       nv_wr32(priv, 0x4046a8, 0x0);
-       nv_wr32(priv, 0x4046ac, 0x0);
-       nv_wr32(priv, 0x4046b0, 0x0);
-       nv_wr32(priv, 0x4046b4, 0x0);
-       nv_wr32(priv, 0x4046b8, 0x0);
-       nv_wr32(priv, 0x4046bc, 0x0);
-       nv_wr32(priv, 0x4046c0, 0x0);
-       nv_wr32(priv, 0x4046c8, 0x0);
-       nv_wr32(priv, 0x4046cc, 0x0);
-       nv_wr32(priv, 0x4046d0, 0x0);
-}
-
-static void
-nve0_graph_generate_unk47xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x404700, 0x0);
-       nv_wr32(priv, 0x404704, 0x0);
-       nv_wr32(priv, 0x404708, 0x0);
-       nv_wr32(priv, 0x404718, 0x0);
-       nv_wr32(priv, 0x40471c, 0x0);
-       nv_wr32(priv, 0x404720, 0x0);
-       nv_wr32(priv, 0x404724, 0x0);
-       nv_wr32(priv, 0x404728, 0x0);
-       nv_wr32(priv, 0x40472c, 0x0);
-       nv_wr32(priv, 0x404730, 0x0);
-       nv_wr32(priv, 0x404734, 0x100);
-       nv_wr32(priv, 0x404738, 0x0);
-       nv_wr32(priv, 0x40473c, 0x0);
-       nv_wr32(priv, 0x404744, 0x0);
-       nv_wr32(priv, 0x404748, 0x0);
-       nv_wr32(priv, 0x404754, 0x0);
-}
-
-static void
-nve0_graph_generate_unk58xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x405800, 0xf8000bf);
-       nv_wr32(priv, 0x405830, 0x2180648);
-       nv_wr32(priv, 0x405834, 0x8000000);
-       nv_wr32(priv, 0x405838, 0x0);
-       nv_wr32(priv, 0x405854, 0x0);
-       nv_wr32(priv, 0x405870, 0x1);
-       nv_wr32(priv, 0x405874, 0x1);
-       nv_wr32(priv, 0x405878, 0x1);
-       nv_wr32(priv, 0x40587c, 0x1);
-       nv_wr32(priv, 0x405a00, 0x0);
-       nv_wr32(priv, 0x405a04, 0x0);
-       nv_wr32(priv, 0x405a18, 0x0);
-       nv_wr32(priv, 0x405b00, 0x0);
-       nv_wr32(priv, 0x405b10, 0x1000);
-}
-
-static void
-nve0_graph_generate_unk60xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x406020, 0x4103c1);
-       nv_wr32(priv, 0x406028, 0x1);
-       nv_wr32(priv, 0x40602c, 0x1);
-       nv_wr32(priv, 0x406030, 0x1);
-       nv_wr32(priv, 0x406034, 0x1);
-}
-
-static void
-nve0_graph_generate_unk64xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x4064a8, 0x0);
-       nv_wr32(priv, 0x4064ac, 0x3fff);
-       nv_wr32(priv, 0x4064b4, 0x0);
-       nv_wr32(priv, 0x4064b8, 0x0);
-       nv_wr32(priv, 0x4064c0, 0x801a00f0);
-       nv_wr32(priv, 0x4064c4, 0x192ffff);
-       nv_wr32(priv, 0x4064c8, 0x1800600);
-       nv_wr32(priv, 0x4064cc, 0x0);
-       nv_wr32(priv, 0x4064d0, 0x0);
-       nv_wr32(priv, 0x4064d4, 0x0);
-       nv_wr32(priv, 0x4064d8, 0x0);
-       nv_wr32(priv, 0x4064dc, 0x0);
-       nv_wr32(priv, 0x4064e0, 0x0);
-       nv_wr32(priv, 0x4064e4, 0x0);
-       nv_wr32(priv, 0x4064e8, 0x0);
-       nv_wr32(priv, 0x4064ec, 0x0);
-       nv_wr32(priv, 0x4064fc, 0x22a);
-}
-
-static void
-nve0_graph_generate_unk70xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x407040, 0x0);
-}
-
-static void
-nve0_graph_generate_unk78xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x407804, 0x23);
-       nv_wr32(priv, 0x40780c, 0xa418820);
-       nv_wr32(priv, 0x407810, 0x62080e6);
-       nv_wr32(priv, 0x407814, 0x20398a4);
-       nv_wr32(priv, 0x407818, 0xe629062);
-       nv_wr32(priv, 0x40781c, 0xa418820);
-       nv_wr32(priv, 0x407820, 0xe6);
-       nv_wr32(priv, 0x4078bc, 0x103);
-}
-
-static void
-nve0_graph_generate_unk80xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x408000, 0x0);
-       nv_wr32(priv, 0x408004, 0x0);
-       nv_wr32(priv, 0x408008, 0x30);
-       nv_wr32(priv, 0x40800c, 0x0);
-       nv_wr32(priv, 0x408010, 0x0);
-       nv_wr32(priv, 0x408014, 0x69);
-       nv_wr32(priv, 0x408018, 0xe100e100);
-       nv_wr32(priv, 0x408064, 0x0);
-}
-
-static void
-nve0_graph_generate_unk88xx(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x408800, 0x2802a3c);
-       nv_wr32(priv, 0x408804, 0x40);
-       nv_wr32(priv, 0x408808, 0x1043e005);
-       nv_wr32(priv, 0x408840, 0xb);
-       nv_wr32(priv, 0x408900, 0x3080b801);
-       nv_wr32(priv, 0x408904, 0x62000001);
-       nv_wr32(priv, 0x408908, 0xc8102f);
-       nv_wr32(priv, 0x408980, 0x11d);
-}
-
-static void
-nve0_graph_generate_gpc(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x418380, 0x16);
-       nv_wr32(priv, 0x418400, 0x38004e00);
-       nv_wr32(priv, 0x418404, 0x71e0ffff);
-       nv_wr32(priv, 0x41840c, 0x1008);
-       nv_wr32(priv, 0x418410, 0xfff0fff);
-       nv_wr32(priv, 0x418414, 0x2200fff);
-       nv_wr32(priv, 0x418450, 0x0);
-       nv_wr32(priv, 0x418454, 0x0);
-       nv_wr32(priv, 0x418458, 0x0);
-       nv_wr32(priv, 0x41845c, 0x0);
-       nv_wr32(priv, 0x418460, 0x0);
-       nv_wr32(priv, 0x418464, 0x0);
-       nv_wr32(priv, 0x418468, 0x1);
-       nv_wr32(priv, 0x41846c, 0x0);
-       nv_wr32(priv, 0x418470, 0x0);
-       nv_wr32(priv, 0x418600, 0x1f);
-       nv_wr32(priv, 0x418684, 0xf);
-       nv_wr32(priv, 0x418700, 0x2);
-       nv_wr32(priv, 0x418704, 0x80);
-       nv_wr32(priv, 0x418708, 0x0);
-       nv_wr32(priv, 0x41870c, 0x0);
-       nv_wr32(priv, 0x418710, 0x0);
-       nv_wr32(priv, 0x418800, 0x7006860a);
-       nv_wr32(priv, 0x418808, 0x0);
-       nv_wr32(priv, 0x41880c, 0x0);
-       nv_wr32(priv, 0x418810, 0x0);
-       nv_wr32(priv, 0x418828, 0x44);
-       nv_wr32(priv, 0x418830, 0x10000001);
-       nv_wr32(priv, 0x4188d8, 0x8);
-       nv_wr32(priv, 0x4188e0, 0x1000000);
-       nv_wr32(priv, 0x4188e8, 0x0);
-       nv_wr32(priv, 0x4188ec, 0x0);
-       nv_wr32(priv, 0x4188f0, 0x0);
-       nv_wr32(priv, 0x4188f4, 0x0);
-       nv_wr32(priv, 0x4188f8, 0x0);
-       nv_wr32(priv, 0x4188fc, 0x20100018);
-       nv_wr32(priv, 0x41891c, 0xff00ff);
-       nv_wr32(priv, 0x418924, 0x0);
-       nv_wr32(priv, 0x418928, 0xffff00);
-       nv_wr32(priv, 0x41892c, 0xff00);
-       nv_wr32(priv, 0x418a00, 0x0);
-       nv_wr32(priv, 0x418a04, 0x0);
-       nv_wr32(priv, 0x418a08, 0x0);
-       nv_wr32(priv, 0x418a0c, 0x10000);
-       nv_wr32(priv, 0x418a10, 0x0);
-       nv_wr32(priv, 0x418a14, 0x0);
-       nv_wr32(priv, 0x418a18, 0x0);
-       nv_wr32(priv, 0x418a20, 0x0);
-       nv_wr32(priv, 0x418a24, 0x0);
-       nv_wr32(priv, 0x418a28, 0x0);
-       nv_wr32(priv, 0x418a2c, 0x10000);
-       nv_wr32(priv, 0x418a30, 0x0);
-       nv_wr32(priv, 0x418a34, 0x0);
-       nv_wr32(priv, 0x418a38, 0x0);
-       nv_wr32(priv, 0x418a40, 0x0);
-       nv_wr32(priv, 0x418a44, 0x0);
-       nv_wr32(priv, 0x418a48, 0x0);
-       nv_wr32(priv, 0x418a4c, 0x10000);
-       nv_wr32(priv, 0x418a50, 0x0);
-       nv_wr32(priv, 0x418a54, 0x0);
-       nv_wr32(priv, 0x418a58, 0x0);
-       nv_wr32(priv, 0x418a60, 0x0);
-       nv_wr32(priv, 0x418a64, 0x0);
-       nv_wr32(priv, 0x418a68, 0x0);
-       nv_wr32(priv, 0x418a6c, 0x10000);
-       nv_wr32(priv, 0x418a70, 0x0);
-       nv_wr32(priv, 0x418a74, 0x0);
-       nv_wr32(priv, 0x418a78, 0x0);
-       nv_wr32(priv, 0x418a80, 0x0);
-       nv_wr32(priv, 0x418a84, 0x0);
-       nv_wr32(priv, 0x418a88, 0x0);
-       nv_wr32(priv, 0x418a8c, 0x10000);
-       nv_wr32(priv, 0x418a90, 0x0);
-       nv_wr32(priv, 0x418a94, 0x0);
-       nv_wr32(priv, 0x418a98, 0x0);
-       nv_wr32(priv, 0x418aa0, 0x0);
-       nv_wr32(priv, 0x418aa4, 0x0);
-       nv_wr32(priv, 0x418aa8, 0x0);
-       nv_wr32(priv, 0x418aac, 0x10000);
-       nv_wr32(priv, 0x418ab0, 0x0);
-       nv_wr32(priv, 0x418ab4, 0x0);
-       nv_wr32(priv, 0x418ab8, 0x0);
-       nv_wr32(priv, 0x418ac0, 0x0);
-       nv_wr32(priv, 0x418ac4, 0x0);
-       nv_wr32(priv, 0x418ac8, 0x0);
-       nv_wr32(priv, 0x418acc, 0x10000);
-       nv_wr32(priv, 0x418ad0, 0x0);
-       nv_wr32(priv, 0x418ad4, 0x0);
-       nv_wr32(priv, 0x418ad8, 0x0);
-       nv_wr32(priv, 0x418ae0, 0x0);
-       nv_wr32(priv, 0x418ae4, 0x0);
-       nv_wr32(priv, 0x418ae8, 0x0);
-       nv_wr32(priv, 0x418aec, 0x10000);
-       nv_wr32(priv, 0x418af0, 0x0);
-       nv_wr32(priv, 0x418af4, 0x0);
-       nv_wr32(priv, 0x418af8, 0x0);
-       nv_wr32(priv, 0x418b00, 0x6);
-       nv_wr32(priv, 0x418b08, 0xa418820);
-       nv_wr32(priv, 0x418b0c, 0x62080e6);
-       nv_wr32(priv, 0x418b10, 0x20398a4);
-       nv_wr32(priv, 0x418b14, 0xe629062);
-       nv_wr32(priv, 0x418b18, 0xa418820);
-       nv_wr32(priv, 0x418b1c, 0xe6);
-       nv_wr32(priv, 0x418bb8, 0x103);
-       nv_wr32(priv, 0x418c08, 0x1);
-       nv_wr32(priv, 0x418c10, 0x0);
-       nv_wr32(priv, 0x418c14, 0x0);
-       nv_wr32(priv, 0x418c18, 0x0);
-       nv_wr32(priv, 0x418c1c, 0x0);
-       nv_wr32(priv, 0x418c20, 0x0);
-       nv_wr32(priv, 0x418c24, 0x0);
-       nv_wr32(priv, 0x418c28, 0x0);
-       nv_wr32(priv, 0x418c2c, 0x0);
-       nv_wr32(priv, 0x418c40, 0xffffffff);
-       nv_wr32(priv, 0x418c6c, 0x1);
-       nv_wr32(priv, 0x418c80, 0x20200004);
-       nv_wr32(priv, 0x418c8c, 0x1);
-       nv_wr32(priv, 0x419000, 0x780);
-       nv_wr32(priv, 0x419004, 0x0);
-       nv_wr32(priv, 0x419008, 0x0);
-       nv_wr32(priv, 0x419014, 0x4);
-}
-
-static void
-nve0_graph_generate_tpc(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x419848, 0x0);
-       nv_wr32(priv, 0x419864, 0x129);
-       nv_wr32(priv, 0x419888, 0x0);
-       nv_wr32(priv, 0x419a00, 0xf0);
-       nv_wr32(priv, 0x419a04, 0x1);
-       nv_wr32(priv, 0x419a08, 0x21);
-       nv_wr32(priv, 0x419a0c, 0x20000);
-       nv_wr32(priv, 0x419a10, 0x0);
-       nv_wr32(priv, 0x419a14, 0x200);
-       nv_wr32(priv, 0x419a1c, 0xc000);
-       nv_wr32(priv, 0x419a20, 0x800);
-       nv_wr32(priv, 0x419a30, 0x1);
-       nv_wr32(priv, 0x419ac4, 0x37f440);
-       nv_wr32(priv, 0x419c00, 0xa);
-       nv_wr32(priv, 0x419c04, 0x80000006);
-       nv_wr32(priv, 0x419c08, 0x2);
-       nv_wr32(priv, 0x419c20, 0x0);
-       nv_wr32(priv, 0x419c24, 0x84210);
-       nv_wr32(priv, 0x419c28, 0x3efbefbe);
-       nv_wr32(priv, 0x419ce8, 0x0);
-       nv_wr32(priv, 0x419cf4, 0x3203);
-       nv_wr32(priv, 0x419e04, 0x0);
-       nv_wr32(priv, 0x419e08, 0x0);
-       nv_wr32(priv, 0x419e0c, 0x0);
-       nv_wr32(priv, 0x419e10, 0x402);
-       nv_wr32(priv, 0x419e44, 0x13eff2);
-       nv_wr32(priv, 0x419e48, 0x0);
-       nv_wr32(priv, 0x419e4c, 0x7f);
-       nv_wr32(priv, 0x419e50, 0x0);
-       nv_wr32(priv, 0x419e54, 0x0);
-       nv_wr32(priv, 0x419e58, 0x0);
-       nv_wr32(priv, 0x419e5c, 0x0);
-       nv_wr32(priv, 0x419e60, 0x0);
-       nv_wr32(priv, 0x419e64, 0x0);
-       nv_wr32(priv, 0x419e68, 0x0);
-       nv_wr32(priv, 0x419e6c, 0x0);
-       nv_wr32(priv, 0x419e70, 0x0);
-       nv_wr32(priv, 0x419e74, 0x0);
-       nv_wr32(priv, 0x419e78, 0x0);
-       nv_wr32(priv, 0x419e7c, 0x0);
-       nv_wr32(priv, 0x419e80, 0x0);
-       nv_wr32(priv, 0x419e84, 0x0);
-       nv_wr32(priv, 0x419e88, 0x0);
-       nv_wr32(priv, 0x419e8c, 0x0);
-       nv_wr32(priv, 0x419e90, 0x0);
-       nv_wr32(priv, 0x419e94, 0x0);
-       nv_wr32(priv, 0x419e98, 0x0);
-       nv_wr32(priv, 0x419eac, 0x1fcf);
-       nv_wr32(priv, 0x419eb0, 0xd3f);
-       nv_wr32(priv, 0x419ec8, 0x1304f);
-       nv_wr32(priv, 0x419f30, 0x0);
-       nv_wr32(priv, 0x419f34, 0x0);
-       nv_wr32(priv, 0x419f38, 0x0);
-       nv_wr32(priv, 0x419f3c, 0x0);
-       nv_wr32(priv, 0x419f40, 0x0);
-       nv_wr32(priv, 0x419f44, 0x0);
-       nv_wr32(priv, 0x419f48, 0x0);
-       nv_wr32(priv, 0x419f4c, 0x0);
-       nv_wr32(priv, 0x419f58, 0x0);
-       nv_wr32(priv, 0x419f78, 0xb);
-}
-
-static void
-nve0_graph_generate_tpcunk(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x41be24, 0x6);
-       nv_wr32(priv, 0x41bec0, 0x12180000);
-       nv_wr32(priv, 0x41bec4, 0x37f7f);
-       nv_wr32(priv, 0x41bee4, 0x6480430);
-       nv_wr32(priv, 0x41bf00, 0xa418820);
-       nv_wr32(priv, 0x41bf04, 0x62080e6);
-       nv_wr32(priv, 0x41bf08, 0x20398a4);
-       nv_wr32(priv, 0x41bf0c, 0xe629062);
-       nv_wr32(priv, 0x41bf10, 0xa418820);
-       nv_wr32(priv, 0x41bf14, 0xe6);
-       nv_wr32(priv, 0x41bfd0, 0x900103);
-       nv_wr32(priv, 0x41bfe0, 0x400001);
-       nv_wr32(priv, 0x41bfe4, 0x0);
-}
-
-int
-nve0_grctx_generate(struct nvc0_graph_priv *priv)
-{
-       struct nvc0_grctx info;
-       int ret, i, gpc, tpc, id;
-       u32 data[6] = {}, data2[2] = {}, tmp;
-       u32 tpc_set = 0, tpc_mask = 0;
-       u32 magic[GPC_MAX][2], offset;
-       u8 tpcnr[GPC_MAX], a, b;
-       u8 shift, ntpcv;
-
-       ret = nvc0_grctx_init(priv, &info);
-       if (ret)
-               return ret;
-
-       nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
-       nv_wr32(priv, 0x400204, 0x00000000);
-       nv_wr32(priv, 0x400208, 0x00000000);
-
-       nve0_graph_generate_unk40xx(priv);
-       nve0_graph_generate_unk44xx(priv);
-       nve0_graph_generate_unk46xx(priv);
-       nve0_graph_generate_unk47xx(priv);
-       nve0_graph_generate_unk58xx(priv);
-       nve0_graph_generate_unk60xx(priv);
-       nve0_graph_generate_unk64xx(priv);
-       nve0_graph_generate_unk70xx(priv);
-       nve0_graph_generate_unk78xx(priv);
-       nve0_graph_generate_unk80xx(priv);
-       nve0_graph_generate_unk88xx(priv);
-       nve0_graph_generate_gpc(priv);
-       nve0_graph_generate_tpc(priv);
-       nve0_graph_generate_tpcunk(priv);
-
-       nv_wr32(priv, 0x404154, 0x0);
-
-       mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
-       mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
-       mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
-       mmio_list(0x40800c, 0x00000000,  8, 1);
-       mmio_list(0x408010, 0x80000000,  0, 0);
-       mmio_list(0x419004, 0x00000000,  8, 1);
-       mmio_list(0x419008, 0x00000000,  0, 0);
-       mmio_list(0x4064cc, 0x80000000,  0, 0);
-       mmio_list(0x408004, 0x00000000,  8, 0);
-       mmio_list(0x408008, 0x80000030,  0, 0);
-       mmio_list(0x418808, 0x00000000,  8, 0);
-       mmio_list(0x41880c, 0x80000030,  0, 0);
-       mmio_list(0x4064c8, 0x01800600,  0, 0);
-       mmio_list(0x418810, 0x80000000, 12, 2);
-       mmio_list(0x419848, 0x10000000, 12, 2);
-       mmio_list(0x405830, 0x02180648,  0, 0);
-       mmio_list(0x4064c4, 0x0192ffff,  0, 0);
-       for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
-               u16 magic0 = 0x0218 * priv->tpc_nr[gpc];
-               u16 magic1 = 0x0648 * priv->tpc_nr[gpc];
-               magic[gpc][0]  = 0x10000000 | (magic0 << 16) | offset;
-               magic[gpc][1]  = 0x00000000 | (magic1 << 16);
-               offset += 0x0324 * priv->tpc_nr[gpc];
-       }
-       for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
-               mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0);
-               mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0);
-               offset += 0x07ff * priv->tpc_nr[gpc];
-       }
-       mmio_list(0x17e91c, 0x06060609, 0, 0);
-       mmio_list(0x17e920, 0x00090a05, 0, 0);
-
-       nv_wr32(priv, 0x418c6c, 0x1);
-       nv_wr32(priv, 0x41980c, 0x10);
-       nv_wr32(priv, 0x41be08, 0x4);
-       nv_wr32(priv, 0x4064c0, 0x801a00f0);
-       nv_wr32(priv, 0x405800, 0xf8000bf);
-       nv_wr32(priv, 0x419c00, 0xa);
-
-       for (tpc = 0, id = 0; tpc < 4; tpc++) {
-               for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
-                       if (tpc < priv->tpc_nr[gpc]) {
-                               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0698), id);
-                               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x04e8), id);
-                               nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id);
-                               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0088), id++);
-                       }
-
-                       nv_wr32(priv, GPC_UNIT(gpc, 0x0c08), priv->tpc_nr[gpc]);
-                       nv_wr32(priv, GPC_UNIT(gpc, 0x0c8c), priv->tpc_nr[gpc]);
-               }
-       }
-
-       tmp = 0;
-       for (i = 0; i < priv->gpc_nr; i++)
-               tmp |= priv->tpc_nr[i] << (i * 4);
-       nv_wr32(priv, 0x406028, tmp);
-       nv_wr32(priv, 0x405870, tmp);
-
-       nv_wr32(priv, 0x40602c, 0x0);
-       nv_wr32(priv, 0x405874, 0x0);
-       nv_wr32(priv, 0x406030, 0x0);
-       nv_wr32(priv, 0x405878, 0x0);
-       nv_wr32(priv, 0x406034, 0x0);
-       nv_wr32(priv, 0x40587c, 0x0);
-
-       /* calculate first set of magics */
-       memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
-
-       gpc = -1;
-       for (tpc = 0; tpc < priv->tpc_total; tpc++) {
-               do {
-                       gpc = (gpc + 1) % priv->gpc_nr;
-               } while (!tpcnr[gpc]);
-               tpcnr[gpc]--;
-
-               data[tpc / 6] |= gpc << ((tpc % 6) * 5);
-       }
-
-       for (; tpc < 32; tpc++)
-               data[tpc / 6] |= 7 << ((tpc % 6) * 5);
-
-       /* and the second... */
-       shift = 0;
-       ntpcv = priv->tpc_total;
-       while (!(ntpcv & (1 << 4))) {
-               ntpcv <<= 1;
-               shift++;
-       }
-
-       data2[0]  = ntpcv << 16;
-       data2[0] |= shift << 21;
-       data2[0] |= (((1 << (0 + 5)) % ntpcv) << 24);
-       data2[0] |= priv->tpc_total << 8;
-       data2[0] |= priv->magic_not_rop_nr;
-       for (i = 1; i < 7; i++)
-               data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5);
-
-       /* and write it all the various parts of PGRAPH */
-       nv_wr32(priv, 0x418bb8, (priv->tpc_total << 8) | priv->magic_not_rop_nr);
-       for (i = 0; i < 6; i++)
-               nv_wr32(priv, 0x418b08 + (i * 4), data[i]);
-
-       nv_wr32(priv, 0x41bfd0, data2[0]);
-       nv_wr32(priv, 0x41bfe4, data2[1]);
-       for (i = 0; i < 6; i++)
-               nv_wr32(priv, 0x41bf00 + (i * 4), data[i]);
-
-       nv_wr32(priv, 0x4078bc, (priv->tpc_total << 8) | priv->magic_not_rop_nr);
-       for (i = 0; i < 6; i++)
-               nv_wr32(priv, 0x40780c + (i * 4), data[i]);
-
-
-       memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
-       for (gpc = 0; gpc < priv->gpc_nr; gpc++)
-               tpc_mask |= ((1 << priv->tpc_nr[gpc]) - 1) << (gpc * 8);
-
-       for (i = 0, gpc = -1, b = -1; i < 32; i++) {
-               a = (i * (priv->tpc_total - 1)) / 32;
-               if (a != b) {
-                       b = a;
-                       do {
-                               gpc = (gpc + 1) % priv->gpc_nr;
-                       } while (!tpcnr[gpc]);
-                       tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--;
-
-                       tpc_set |= 1 << ((gpc * 8) + tpc);
-               }
-
-               nv_wr32(priv, 0x406800 + (i * 0x20), tpc_set);
-               nv_wr32(priv, 0x406c00 + (i * 0x20), tpc_set ^ tpc_mask);
-       }
-
-       for (i = 0; i < 8; i++)
-               nv_wr32(priv, 0x4064d0 + (i * 0x04), 0x00000000);
-
-       nv_wr32(priv, 0x405b00, (priv->tpc_total << 8) | priv->gpc_nr);
-       if (priv->gpc_nr == 1) {
-               nv_mask(priv, 0x408850, 0x0000000f, priv->tpc_nr[0]);
-               nv_mask(priv, 0x408958, 0x0000000f, priv->tpc_nr[0]);
-       } else {
-               nv_mask(priv, 0x408850, 0x0000000f, priv->gpc_nr);
-               nv_mask(priv, 0x408958, 0x0000000f, priv->gpc_nr);
-       }
-       nv_mask(priv, 0x419f78, 0x00000001, 0x00000000);
-
-       nve0_grctx_generate_icmd(priv);
-       nve0_grctx_generate_a097(priv);
-       nve0_grctx_generate_902d(priv);
-
-       nv_mask(priv, 0x000260, 0x00000001, 0x00000001);
-       nv_wr32(priv, 0x418800, 0x7026860a); //XXX
-       nv_wr32(priv, 0x41be10, 0x00bb8bc7); //XXX
-       return nvc0_grctx_fini(&info);
-}
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c
new file mode 100644 (file)
index 0000000..e2de73e
--- /dev/null
@@ -0,0 +1,1018 @@
+/*
+ * 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 "nvc0.h"
+
+struct nvc0_graph_init
+nve4_grctx_init_icmd[] = {
+       { 0x001000,   1, 0x01, 0x00000004 },
+       { 0x000039,   3, 0x01, 0x00000000 },
+       { 0x0000a9,   1, 0x01, 0x0000ffff },
+       { 0x000038,   1, 0x01, 0x0fac6881 },
+       { 0x00003d,   1, 0x01, 0x00000001 },
+       { 0x0000e8,   8, 0x01, 0x00000400 },
+       { 0x000078,   8, 0x01, 0x00000300 },
+       { 0x000050,   1, 0x01, 0x00000011 },
+       { 0x000058,   8, 0x01, 0x00000008 },
+       { 0x000208,   8, 0x01, 0x00000001 },
+       { 0x000081,   1, 0x01, 0x00000001 },
+       { 0x000085,   1, 0x01, 0x00000004 },
+       { 0x000088,   1, 0x01, 0x00000400 },
+       { 0x000090,   1, 0x01, 0x00000300 },
+       { 0x000098,   1, 0x01, 0x00001001 },
+       { 0x0000e3,   1, 0x01, 0x00000001 },
+       { 0x0000da,   1, 0x01, 0x00000001 },
+       { 0x0000f8,   1, 0x01, 0x00000003 },
+       { 0x0000fa,   1, 0x01, 0x00000001 },
+       { 0x00009f,   4, 0x01, 0x0000ffff },
+       { 0x0000b1,   1, 0x01, 0x00000001 },
+       { 0x0000ad,   1, 0x01, 0x0000013e },
+       { 0x0000e1,   1, 0x01, 0x00000010 },
+       { 0x000290,  16, 0x01, 0x00000000 },
+       { 0x0003b0,  16, 0x01, 0x00000000 },
+       { 0x0002a0,  16, 0x01, 0x00000000 },
+       { 0x000420,  16, 0x01, 0x00000000 },
+       { 0x0002b0,  16, 0x01, 0x00000000 },
+       { 0x000430,  16, 0x01, 0x00000000 },
+       { 0x0002c0,  16, 0x01, 0x00000000 },
+       { 0x0004d0,  16, 0x01, 0x00000000 },
+       { 0x000720,  16, 0x01, 0x00000000 },
+       { 0x0008c0,  16, 0x01, 0x00000000 },
+       { 0x000890,  16, 0x01, 0x00000000 },
+       { 0x0008e0,  16, 0x01, 0x00000000 },
+       { 0x0008a0,  16, 0x01, 0x00000000 },
+       { 0x0008f0,  16, 0x01, 0x00000000 },
+       { 0x00094c,   1, 0x01, 0x000000ff },
+       { 0x00094d,   1, 0x01, 0xffffffff },
+       { 0x00094e,   1, 0x01, 0x00000002 },
+       { 0x0002ec,   1, 0x01, 0x00000001 },
+       { 0x000303,   1, 0x01, 0x00000001 },
+       { 0x0002e6,   1, 0x01, 0x00000001 },
+       { 0x000466,   1, 0x01, 0x00000052 },
+       { 0x000301,   1, 0x01, 0x3f800000 },
+       { 0x000304,   1, 0x01, 0x30201000 },
+       { 0x000305,   1, 0x01, 0x70605040 },
+       { 0x000306,   1, 0x01, 0xb8a89888 },
+       { 0x000307,   1, 0x01, 0xf8e8d8c8 },
+       { 0x00030a,   1, 0x01, 0x00ffff00 },
+       { 0x00030b,   1, 0x01, 0x0000001a },
+       { 0x00030c,   1, 0x01, 0x00000001 },
+       { 0x000318,   1, 0x01, 0x00000001 },
+       { 0x000340,   1, 0x01, 0x00000000 },
+       { 0x000375,   1, 0x01, 0x00000001 },
+       { 0x00037d,   1, 0x01, 0x00000006 },
+       { 0x0003a0,   1, 0x01, 0x00000002 },
+       { 0x0003aa,   1, 0x01, 0x00000001 },
+       { 0x0003a9,   1, 0x01, 0x00000001 },
+       { 0x000380,   1, 0x01, 0x00000001 },
+       { 0x000383,   1, 0x01, 0x00000011 },
+       { 0x000360,   1, 0x01, 0x00000040 },
+       { 0x000366,   2, 0x01, 0x00000000 },
+       { 0x000368,   1, 0x01, 0x00000fff },
+       { 0x000370,   2, 0x01, 0x00000000 },
+       { 0x000372,   1, 0x01, 0x000fffff },
+       { 0x00037a,   1, 0x01, 0x00000012 },
+       { 0x000619,   1, 0x01, 0x00000003 },
+       { 0x000811,   1, 0x01, 0x00000003 },
+       { 0x000812,   1, 0x01, 0x00000004 },
+       { 0x000813,   1, 0x01, 0x00000006 },
+       { 0x000814,   1, 0x01, 0x00000008 },
+       { 0x000815,   1, 0x01, 0x0000000b },
+       { 0x000800,   6, 0x01, 0x00000001 },
+       { 0x000632,   1, 0x01, 0x00000001 },
+       { 0x000633,   1, 0x01, 0x00000002 },
+       { 0x000634,   1, 0x01, 0x00000003 },
+       { 0x000635,   1, 0x01, 0x00000004 },
+       { 0x000654,   1, 0x01, 0x3f800000 },
+       { 0x000657,   1, 0x01, 0x3f800000 },
+       { 0x000655,   2, 0x01, 0x3f800000 },
+       { 0x0006cd,   1, 0x01, 0x3f800000 },
+       { 0x0007f5,   1, 0x01, 0x3f800000 },
+       { 0x0007dc,   1, 0x01, 0x39291909 },
+       { 0x0007dd,   1, 0x01, 0x79695949 },
+       { 0x0007de,   1, 0x01, 0xb9a99989 },
+       { 0x0007df,   1, 0x01, 0xf9e9d9c9 },
+       { 0x0007e8,   1, 0x01, 0x00003210 },
+       { 0x0007e9,   1, 0x01, 0x00007654 },
+       { 0x0007ea,   1, 0x01, 0x00000098 },
+       { 0x0007ec,   1, 0x01, 0x39291909 },
+       { 0x0007ed,   1, 0x01, 0x79695949 },
+       { 0x0007ee,   1, 0x01, 0xb9a99989 },
+       { 0x0007ef,   1, 0x01, 0xf9e9d9c9 },
+       { 0x0007f0,   1, 0x01, 0x00003210 },
+       { 0x0007f1,   1, 0x01, 0x00007654 },
+       { 0x0007f2,   1, 0x01, 0x00000098 },
+       { 0x0005a5,   1, 0x01, 0x00000001 },
+       { 0x000980, 128, 0x01, 0x00000000 },
+       { 0x000468,   1, 0x01, 0x00000004 },
+       { 0x00046c,   1, 0x01, 0x00000001 },
+       { 0x000470,  96, 0x01, 0x00000000 },
+       { 0x000510,  16, 0x01, 0x3f800000 },
+       { 0x000520,   1, 0x01, 0x000002b6 },
+       { 0x000529,   1, 0x01, 0x00000001 },
+       { 0x000530,  16, 0x01, 0xffff0000 },
+       { 0x000585,   1, 0x01, 0x0000003f },
+       { 0x000576,   1, 0x01, 0x00000003 },
+       { 0x00057b,   1, 0x01, 0x00000059 },
+       { 0x000586,   1, 0x01, 0x00000040 },
+       { 0x000582,   2, 0x01, 0x00000080 },
+       { 0x0005c2,   1, 0x01, 0x00000001 },
+       { 0x000638,   1, 0x01, 0x00000001 },
+       { 0x000639,   1, 0x01, 0x00000001 },
+       { 0x00063a,   1, 0x01, 0x00000002 },
+       { 0x00063b,   2, 0x01, 0x00000001 },
+       { 0x00063d,   1, 0x01, 0x00000002 },
+       { 0x00063e,   1, 0x01, 0x00000001 },
+       { 0x0008b8,   8, 0x01, 0x00000001 },
+       { 0x000900,   8, 0x01, 0x00000001 },
+       { 0x000908,   8, 0x01, 0x00000002 },
+       { 0x000910,  16, 0x01, 0x00000001 },
+       { 0x000920,   8, 0x01, 0x00000002 },
+       { 0x000928,   8, 0x01, 0x00000001 },
+       { 0x000648,   9, 0x01, 0x00000001 },
+       { 0x000658,   1, 0x01, 0x0000000f },
+       { 0x0007ff,   1, 0x01, 0x0000000a },
+       { 0x00066a,   1, 0x01, 0x40000000 },
+       { 0x00066b,   1, 0x01, 0x10000000 },
+       { 0x00066c,   2, 0x01, 0xffff0000 },
+       { 0x0007af,   2, 0x01, 0x00000008 },
+       { 0x0007f6,   1, 0x01, 0x00000001 },
+       { 0x0006b2,   1, 0x01, 0x00000055 },
+       { 0x0007ad,   1, 0x01, 0x00000003 },
+       { 0x000937,   1, 0x01, 0x00000001 },
+       { 0x000971,   1, 0x01, 0x00000008 },
+       { 0x000972,   1, 0x01, 0x00000040 },
+       { 0x000973,   1, 0x01, 0x0000012c },
+       { 0x00097c,   1, 0x01, 0x00000040 },
+       { 0x000979,   1, 0x01, 0x00000003 },
+       { 0x000975,   1, 0x01, 0x00000020 },
+       { 0x000976,   1, 0x01, 0x00000001 },
+       { 0x000977,   1, 0x01, 0x00000020 },
+       { 0x000978,   1, 0x01, 0x00000001 },
+       { 0x000957,   1, 0x01, 0x00000003 },
+       { 0x00095e,   1, 0x01, 0x20164010 },
+       { 0x00095f,   1, 0x01, 0x00000020 },
+       { 0x00097d,   1, 0x01, 0x00000020 },
+       { 0x000683,   1, 0x01, 0x00000006 },
+       { 0x000685,   1, 0x01, 0x003fffff },
+       { 0x000687,   1, 0x01, 0x003fffff },
+       { 0x0006a0,   1, 0x01, 0x00000005 },
+       { 0x000840,   1, 0x01, 0x00400008 },
+       { 0x000841,   1, 0x01, 0x08000080 },
+       { 0x000842,   1, 0x01, 0x00400008 },
+       { 0x000843,   1, 0x01, 0x08000080 },
+       { 0x0006aa,   1, 0x01, 0x00000001 },
+       { 0x0006ab,   1, 0x01, 0x00000002 },
+       { 0x0006ac,   1, 0x01, 0x00000080 },
+       { 0x0006ad,   2, 0x01, 0x00000100 },
+       { 0x0006b1,   1, 0x01, 0x00000011 },
+       { 0x0006bb,   1, 0x01, 0x000000cf },
+       { 0x0006ce,   1, 0x01, 0x2a712488 },
+       { 0x000739,   1, 0x01, 0x4085c000 },
+       { 0x00073a,   1, 0x01, 0x00000080 },
+       { 0x000786,   1, 0x01, 0x80000100 },
+       { 0x00073c,   1, 0x01, 0x00010100 },
+       { 0x00073d,   1, 0x01, 0x02800000 },
+       { 0x000787,   1, 0x01, 0x000000cf },
+       { 0x00078c,   1, 0x01, 0x00000008 },
+       { 0x000792,   1, 0x01, 0x00000001 },
+       { 0x000794,   1, 0x01, 0x00000001 },
+       { 0x000795,   2, 0x01, 0x00000001 },
+       { 0x000797,   1, 0x01, 0x000000cf },
+       { 0x000836,   1, 0x01, 0x00000001 },
+       { 0x00079a,   1, 0x01, 0x00000002 },
+       { 0x000833,   1, 0x01, 0x04444480 },
+       { 0x0007a1,   1, 0x01, 0x00000001 },
+       { 0x0007a3,   1, 0x01, 0x00000001 },
+       { 0x0007a4,   2, 0x01, 0x00000001 },
+       { 0x000831,   1, 0x01, 0x00000004 },
+       { 0x000b07,   1, 0x01, 0x00000002 },
+       { 0x000b08,   2, 0x01, 0x00000100 },
+       { 0x000b0a,   1, 0x01, 0x00000001 },
+       { 0x000a04,   1, 0x01, 0x000000ff },
+       { 0x000a0b,   1, 0x01, 0x00000040 },
+       { 0x00097f,   1, 0x01, 0x00000100 },
+       { 0x000a02,   1, 0x01, 0x00000001 },
+       { 0x000809,   1, 0x01, 0x00000007 },
+       { 0x00c221,   1, 0x01, 0x00000040 },
+       { 0x00c1b0,   8, 0x01, 0x0000000f },
+       { 0x00c1b8,   1, 0x01, 0x0fac6881 },
+       { 0x00c1b9,   1, 0x01, 0x00fac688 },
+       { 0x00c401,   1, 0x01, 0x00000001 },
+       { 0x00c402,   1, 0x01, 0x00010001 },
+       { 0x00c403,   2, 0x01, 0x00000001 },
+       { 0x00c40e,   1, 0x01, 0x00000020 },
+       { 0x00c500,   1, 0x01, 0x00000003 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000002 },
+       { 0x0006aa,   1, 0x01, 0x00000001 },
+       { 0x0006ad,   2, 0x01, 0x00000100 },
+       { 0x0006b1,   1, 0x01, 0x00000011 },
+       { 0x00078c,   1, 0x01, 0x00000008 },
+       { 0x000792,   1, 0x01, 0x00000001 },
+       { 0x000794,   1, 0x01, 0x00000001 },
+       { 0x000795,   2, 0x01, 0x00000001 },
+       { 0x000797,   1, 0x01, 0x000000cf },
+       { 0x00079a,   1, 0x01, 0x00000002 },
+       { 0x000833,   1, 0x01, 0x04444480 },
+       { 0x0007a1,   1, 0x01, 0x00000001 },
+       { 0x0007a3,   1, 0x01, 0x00000001 },
+       { 0x0007a4,   2, 0x01, 0x00000001 },
+       { 0x000831,   1, 0x01, 0x00000004 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000008 },
+       { 0x000039,   3, 0x01, 0x00000000 },
+       { 0x000380,   1, 0x01, 0x00000001 },
+       { 0x000366,   2, 0x01, 0x00000000 },
+       { 0x000368,   1, 0x01, 0x00000fff },
+       { 0x000370,   2, 0x01, 0x00000000 },
+       { 0x000372,   1, 0x01, 0x000fffff },
+       { 0x000813,   1, 0x01, 0x00000006 },
+       { 0x000814,   1, 0x01, 0x00000008 },
+       { 0x000957,   1, 0x01, 0x00000003 },
+       { 0x000b07,   1, 0x01, 0x00000002 },
+       { 0x000b08,   2, 0x01, 0x00000100 },
+       { 0x000b0a,   1, 0x01, 0x00000001 },
+       { 0x000a04,   1, 0x01, 0x000000ff },
+       { 0x00097f,   1, 0x01, 0x00000100 },
+       { 0x000a02,   1, 0x01, 0x00000001 },
+       { 0x000809,   1, 0x01, 0x00000007 },
+       { 0x00c221,   1, 0x01, 0x00000040 },
+       { 0x00c401,   1, 0x01, 0x00000001 },
+       { 0x00c402,   1, 0x01, 0x00010001 },
+       { 0x00c403,   2, 0x01, 0x00000001 },
+       { 0x00c40e,   1, 0x01, 0x00000020 },
+       { 0x00c500,   1, 0x01, 0x00000003 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       { 0x001000,   1, 0x01, 0x00000001 },
+       { 0x000b07,   1, 0x01, 0x00000002 },
+       { 0x000b08,   2, 0x01, 0x00000100 },
+       { 0x000b0a,   1, 0x01, 0x00000001 },
+       { 0x01e100,   1, 0x01, 0x00000001 },
+       {}
+};
+
+struct nvc0_graph_init
+nve4_grctx_init_a097[] = {
+       { 0x000800,   8, 0x40, 0x00000000 },
+       { 0x000804,   8, 0x40, 0x00000000 },
+       { 0x000808,   8, 0x40, 0x00000400 },
+       { 0x00080c,   8, 0x40, 0x00000300 },
+       { 0x000810,   1, 0x04, 0x000000cf },
+       { 0x000850,   7, 0x40, 0x00000000 },
+       { 0x000814,   8, 0x40, 0x00000040 },
+       { 0x000818,   8, 0x40, 0x00000001 },
+       { 0x00081c,   8, 0x40, 0x00000000 },
+       { 0x000820,   8, 0x40, 0x00000000 },
+       { 0x001c00,  16, 0x10, 0x00000000 },
+       { 0x001c04,  16, 0x10, 0x00000000 },
+       { 0x001c08,  16, 0x10, 0x00000000 },
+       { 0x001c0c,  16, 0x10, 0x00000000 },
+       { 0x001d00,  16, 0x10, 0x00000000 },
+       { 0x001d04,  16, 0x10, 0x00000000 },
+       { 0x001d08,  16, 0x10, 0x00000000 },
+       { 0x001d0c,  16, 0x10, 0x00000000 },
+       { 0x001f00,  16, 0x08, 0x00000000 },
+       { 0x001f04,  16, 0x08, 0x00000000 },
+       { 0x001f80,  16, 0x08, 0x00000000 },
+       { 0x001f84,  16, 0x08, 0x00000000 },
+       { 0x002000,   1, 0x04, 0x00000000 },
+       { 0x002040,   1, 0x04, 0x00000011 },
+       { 0x002080,   1, 0x04, 0x00000020 },
+       { 0x0020c0,   1, 0x04, 0x00000030 },
+       { 0x002100,   1, 0x04, 0x00000040 },
+       { 0x002140,   1, 0x04, 0x00000051 },
+       { 0x00200c,   6, 0x40, 0x00000001 },
+       { 0x002010,   1, 0x04, 0x00000000 },
+       { 0x002050,   1, 0x04, 0x00000000 },
+       { 0x002090,   1, 0x04, 0x00000001 },
+       { 0x0020d0,   1, 0x04, 0x00000002 },
+       { 0x002110,   1, 0x04, 0x00000003 },
+       { 0x002150,   1, 0x04, 0x00000004 },
+       { 0x000380,   4, 0x20, 0x00000000 },
+       { 0x000384,   4, 0x20, 0x00000000 },
+       { 0x000388,   4, 0x20, 0x00000000 },
+       { 0x00038c,   4, 0x20, 0x00000000 },
+       { 0x000700,   4, 0x10, 0x00000000 },
+       { 0x000704,   4, 0x10, 0x00000000 },
+       { 0x000708,   4, 0x10, 0x00000000 },
+       { 0x002800, 128, 0x04, 0x00000000 },
+       { 0x000a00,  16, 0x20, 0x00000000 },
+       { 0x000a04,  16, 0x20, 0x00000000 },
+       { 0x000a08,  16, 0x20, 0x00000000 },
+       { 0x000a0c,  16, 0x20, 0x00000000 },
+       { 0x000a10,  16, 0x20, 0x00000000 },
+       { 0x000a14,  16, 0x20, 0x00000000 },
+       { 0x000c00,  16, 0x10, 0x00000000 },
+       { 0x000c04,  16, 0x10, 0x00000000 },
+       { 0x000c08,  16, 0x10, 0x00000000 },
+       { 0x000c0c,  16, 0x10, 0x3f800000 },
+       { 0x000d00,   8, 0x08, 0xffff0000 },
+       { 0x000d04,   8, 0x08, 0xffff0000 },
+       { 0x000e00,  16, 0x10, 0x00000000 },
+       { 0x000e04,  16, 0x10, 0xffff0000 },
+       { 0x000e08,  16, 0x10, 0xffff0000 },
+       { 0x000d40,   4, 0x08, 0x00000000 },
+       { 0x000d44,   4, 0x08, 0x00000000 },
+       { 0x001e00,   8, 0x20, 0x00000001 },
+       { 0x001e04,   8, 0x20, 0x00000001 },
+       { 0x001e08,   8, 0x20, 0x00000002 },
+       { 0x001e0c,   8, 0x20, 0x00000001 },
+       { 0x001e10,   8, 0x20, 0x00000001 },
+       { 0x001e14,   8, 0x20, 0x00000002 },
+       { 0x001e18,   8, 0x20, 0x00000001 },
+       { 0x003400, 128, 0x04, 0x00000000 },
+       { 0x00030c,   1, 0x04, 0x00000001 },
+       { 0x001944,   1, 0x04, 0x00000000 },
+       { 0x001514,   1, 0x04, 0x00000000 },
+       { 0x000d68,   1, 0x04, 0x0000ffff },
+       { 0x00121c,   1, 0x04, 0x0fac6881 },
+       { 0x000fac,   1, 0x04, 0x00000001 },
+       { 0x001538,   1, 0x04, 0x00000001 },
+       { 0x000fe0,   2, 0x04, 0x00000000 },
+       { 0x000fe8,   1, 0x04, 0x00000014 },
+       { 0x000fec,   1, 0x04, 0x00000040 },
+       { 0x000ff0,   1, 0x04, 0x00000000 },
+       { 0x00179c,   1, 0x04, 0x00000000 },
+       { 0x001228,   1, 0x04, 0x00000400 },
+       { 0x00122c,   1, 0x04, 0x00000300 },
+       { 0x001230,   1, 0x04, 0x00010001 },
+       { 0x0007f8,   1, 0x04, 0x00000000 },
+       { 0x0015b4,   1, 0x04, 0x00000001 },
+       { 0x0015cc,   1, 0x04, 0x00000000 },
+       { 0x001534,   1, 0x04, 0x00000000 },
+       { 0x000fb0,   1, 0x04, 0x00000000 },
+       { 0x0015d0,   1, 0x04, 0x00000000 },
+       { 0x00153c,   1, 0x04, 0x00000000 },
+       { 0x0016b4,   1, 0x04, 0x00000003 },
+       { 0x000fbc,   4, 0x04, 0x0000ffff },
+       { 0x000df8,   2, 0x04, 0x00000000 },
+       { 0x001948,   1, 0x04, 0x00000000 },
+       { 0x001970,   1, 0x04, 0x00000001 },
+       { 0x00161c,   1, 0x04, 0x000009f0 },
+       { 0x000dcc,   1, 0x04, 0x00000010 },
+       { 0x00163c,   1, 0x04, 0x00000000 },
+       { 0x0015e4,   1, 0x04, 0x00000000 },
+       { 0x001160,  32, 0x04, 0x25e00040 },
+       { 0x001880,  32, 0x04, 0x00000000 },
+       { 0x000f84,   2, 0x04, 0x00000000 },
+       { 0x0017c8,   2, 0x04, 0x00000000 },
+       { 0x0017d0,   1, 0x04, 0x000000ff },
+       { 0x0017d4,   1, 0x04, 0xffffffff },
+       { 0x0017d8,   1, 0x04, 0x00000002 },
+       { 0x0017dc,   1, 0x04, 0x00000000 },
+       { 0x0015f4,   2, 0x04, 0x00000000 },
+       { 0x001434,   2, 0x04, 0x00000000 },
+       { 0x000d74,   1, 0x04, 0x00000000 },
+       { 0x000dec,   1, 0x04, 0x00000001 },
+       { 0x0013a4,   1, 0x04, 0x00000000 },
+       { 0x001318,   1, 0x04, 0x00000001 },
+       { 0x001644,   1, 0x04, 0x00000000 },
+       { 0x000748,   1, 0x04, 0x00000000 },
+       { 0x000de8,   1, 0x04, 0x00000000 },
+       { 0x001648,   1, 0x04, 0x00000000 },
+       { 0x0012a4,   1, 0x04, 0x00000000 },
+       { 0x001120,   4, 0x04, 0x00000000 },
+       { 0x001118,   1, 0x04, 0x00000000 },
+       { 0x00164c,   1, 0x04, 0x00000000 },
+       { 0x001658,   1, 0x04, 0x00000000 },
+       { 0x001910,   1, 0x04, 0x00000290 },
+       { 0x001518,   1, 0x04, 0x00000000 },
+       { 0x00165c,   1, 0x04, 0x00000001 },
+       { 0x001520,   1, 0x04, 0x00000000 },
+       { 0x001604,   1, 0x04, 0x00000000 },
+       { 0x001570,   1, 0x04, 0x00000000 },
+       { 0x0013b0,   2, 0x04, 0x3f800000 },
+       { 0x00020c,   1, 0x04, 0x00000000 },
+       { 0x001670,   1, 0x04, 0x30201000 },
+       { 0x001674,   1, 0x04, 0x70605040 },
+       { 0x001678,   1, 0x04, 0xb8a89888 },
+       { 0x00167c,   1, 0x04, 0xf8e8d8c8 },
+       { 0x00166c,   1, 0x04, 0x00000000 },
+       { 0x001680,   1, 0x04, 0x00ffff00 },
+       { 0x0012d0,   1, 0x04, 0x00000003 },
+       { 0x0012d4,   1, 0x04, 0x00000002 },
+       { 0x001684,   2, 0x04, 0x00000000 },
+       { 0x000dac,   2, 0x04, 0x00001b02 },
+       { 0x000db4,   1, 0x04, 0x00000000 },
+       { 0x00168c,   1, 0x04, 0x00000000 },
+       { 0x0015bc,   1, 0x04, 0x00000000 },
+       { 0x00156c,   1, 0x04, 0x00000000 },
+       { 0x00187c,   1, 0x04, 0x00000000 },
+       { 0x001110,   1, 0x04, 0x00000001 },
+       { 0x000dc0,   3, 0x04, 0x00000000 },
+       { 0x001234,   1, 0x04, 0x00000000 },
+       { 0x001690,   1, 0x04, 0x00000000 },
+       { 0x0012ac,   1, 0x04, 0x00000001 },
+       { 0x000790,   5, 0x04, 0x00000000 },
+       { 0x00077c,   1, 0x04, 0x00000000 },
+       { 0x001000,   1, 0x04, 0x00000010 },
+       { 0x0010fc,   1, 0x04, 0x00000000 },
+       { 0x001290,   1, 0x04, 0x00000000 },
+       { 0x000218,   1, 0x04, 0x00000010 },
+       { 0x0012d8,   1, 0x04, 0x00000000 },
+       { 0x0012dc,   1, 0x04, 0x00000010 },
+       { 0x000d94,   1, 0x04, 0x00000001 },
+       { 0x00155c,   2, 0x04, 0x00000000 },
+       { 0x001564,   1, 0x04, 0x00000fff },
+       { 0x001574,   2, 0x04, 0x00000000 },
+       { 0x00157c,   1, 0x04, 0x000fffff },
+       { 0x001354,   1, 0x04, 0x00000000 },
+       { 0x001610,   1, 0x04, 0x00000012 },
+       { 0x001608,   2, 0x04, 0x00000000 },
+       { 0x00260c,   1, 0x04, 0x00000000 },
+       { 0x0007ac,   1, 0x04, 0x00000000 },
+       { 0x00162c,   1, 0x04, 0x00000003 },
+       { 0x000210,   1, 0x04, 0x00000000 },
+       { 0x000320,   1, 0x04, 0x00000000 },
+       { 0x000324,   6, 0x04, 0x3f800000 },
+       { 0x000750,   1, 0x04, 0x00000000 },
+       { 0x000760,   1, 0x04, 0x39291909 },
+       { 0x000764,   1, 0x04, 0x79695949 },
+       { 0x000768,   1, 0x04, 0xb9a99989 },
+       { 0x00076c,   1, 0x04, 0xf9e9d9c9 },
+       { 0x000770,   1, 0x04, 0x30201000 },
+       { 0x000774,   1, 0x04, 0x70605040 },
+       { 0x000778,   1, 0x04, 0x00009080 },
+       { 0x000780,   1, 0x04, 0x39291909 },
+       { 0x000784,   1, 0x04, 0x79695949 },
+       { 0x000788,   1, 0x04, 0xb9a99989 },
+       { 0x00078c,   1, 0x04, 0xf9e9d9c9 },
+       { 0x0007d0,   1, 0x04, 0x30201000 },
+       { 0x0007d4,   1, 0x04, 0x70605040 },
+       { 0x0007d8,   1, 0x04, 0x00009080 },
+       { 0x00037c,   1, 0x04, 0x00000001 },
+       { 0x000740,   2, 0x04, 0x00000000 },
+       { 0x002600,   1, 0x04, 0x00000000 },
+       { 0x001918,   1, 0x04, 0x00000000 },
+       { 0x00191c,   1, 0x04, 0x00000900 },
+       { 0x001920,   1, 0x04, 0x00000405 },
+       { 0x001308,   1, 0x04, 0x00000001 },
+       { 0x001924,   1, 0x04, 0x00000000 },
+       { 0x0013ac,   1, 0x04, 0x00000000 },
+       { 0x00192c,   1, 0x04, 0x00000001 },
+       { 0x00193c,   1, 0x04, 0x00002c1c },
+       { 0x000d7c,   1, 0x04, 0x00000000 },
+       { 0x000f8c,   1, 0x04, 0x00000000 },
+       { 0x0002c0,   1, 0x04, 0x00000001 },
+       { 0x001510,   1, 0x04, 0x00000000 },
+       { 0x001940,   1, 0x04, 0x00000000 },
+       { 0x000ff4,   2, 0x04, 0x00000000 },
+       { 0x00194c,   2, 0x04, 0x00000000 },
+       { 0x001968,   1, 0x04, 0x00000000 },
+       { 0x001590,   1, 0x04, 0x0000003f },
+       { 0x0007e8,   4, 0x04, 0x00000000 },
+       { 0x00196c,   1, 0x04, 0x00000011 },
+       { 0x0002e4,   1, 0x04, 0x0000b001 },
+       { 0x00036c,   2, 0x04, 0x00000000 },
+       { 0x00197c,   1, 0x04, 0x00000000 },
+       { 0x000fcc,   2, 0x04, 0x00000000 },
+       { 0x0002d8,   1, 0x04, 0x00000040 },
+       { 0x001980,   1, 0x04, 0x00000080 },
+       { 0x001504,   1, 0x04, 0x00000080 },
+       { 0x001984,   1, 0x04, 0x00000000 },
+       { 0x000300,   1, 0x04, 0x00000001 },
+       { 0x0013a8,   1, 0x04, 0x00000000 },
+       { 0x0012ec,   1, 0x04, 0x00000000 },
+       { 0x001310,   1, 0x04, 0x00000000 },
+       { 0x001314,   1, 0x04, 0x00000001 },
+       { 0x001380,   1, 0x04, 0x00000000 },
+       { 0x001384,   4, 0x04, 0x00000001 },
+       { 0x001394,   1, 0x04, 0x00000000 },
+       { 0x00139c,   1, 0x04, 0x00000000 },
+       { 0x001398,   1, 0x04, 0x00000000 },
+       { 0x001594,   1, 0x04, 0x00000000 },
+       { 0x001598,   4, 0x04, 0x00000001 },
+       { 0x000f54,   3, 0x04, 0x00000000 },
+       { 0x0019bc,   1, 0x04, 0x00000000 },
+       { 0x000f9c,   2, 0x04, 0x00000000 },
+       { 0x0012cc,   1, 0x04, 0x00000000 },
+       { 0x0012e8,   1, 0x04, 0x00000000 },
+       { 0x00130c,   1, 0x04, 0x00000001 },
+       { 0x001360,   8, 0x04, 0x00000000 },
+       { 0x00133c,   2, 0x04, 0x00000001 },
+       { 0x001344,   1, 0x04, 0x00000002 },
+       { 0x001348,   2, 0x04, 0x00000001 },
+       { 0x001350,   1, 0x04, 0x00000002 },
+       { 0x001358,   1, 0x04, 0x00000001 },
+       { 0x0012e4,   1, 0x04, 0x00000000 },
+       { 0x00131c,   1, 0x04, 0x00000000 },
+       { 0x001320,   3, 0x04, 0x00000000 },
+       { 0x0019c0,   1, 0x04, 0x00000000 },
+       { 0x001140,   1, 0x04, 0x00000000 },
+       { 0x0019c4,   1, 0x04, 0x00000000 },
+       { 0x0019c8,   1, 0x04, 0x00001500 },
+       { 0x00135c,   1, 0x04, 0x00000000 },
+       { 0x000f90,   1, 0x04, 0x00000000 },
+       { 0x0019e0,   8, 0x04, 0x00000001 },
+       { 0x0019cc,   1, 0x04, 0x00000001 },
+       { 0x0015b8,   1, 0x04, 0x00000000 },
+       { 0x001a00,   1, 0x04, 0x00001111 },
+       { 0x001a04,   7, 0x04, 0x00000000 },
+       { 0x000d6c,   2, 0x04, 0xffff0000 },
+       { 0x0010f8,   1, 0x04, 0x00001010 },
+       { 0x000d80,   5, 0x04, 0x00000000 },
+       { 0x000da0,   1, 0x04, 0x00000000 },
+       { 0x0007a4,   2, 0x04, 0x00000000 },
+       { 0x001508,   1, 0x04, 0x80000000 },
+       { 0x00150c,   1, 0x04, 0x40000000 },
+       { 0x001668,   1, 0x04, 0x00000000 },
+       { 0x000318,   2, 0x04, 0x00000008 },
+       { 0x000d9c,   1, 0x04, 0x00000001 },
+       { 0x000374,   1, 0x04, 0x00000000 },
+       { 0x000378,   1, 0x04, 0x00000020 },
+       { 0x0007dc,   1, 0x04, 0x00000000 },
+       { 0x00074c,   1, 0x04, 0x00000055 },
+       { 0x001420,   1, 0x04, 0x00000003 },
+       { 0x0017bc,   2, 0x04, 0x00000000 },
+       { 0x0017c4,   1, 0x04, 0x00000001 },
+       { 0x001008,   1, 0x04, 0x00000008 },
+       { 0x00100c,   1, 0x04, 0x00000040 },
+       { 0x001010,   1, 0x04, 0x0000012c },
+       { 0x000d60,   1, 0x04, 0x00000040 },
+       { 0x00075c,   1, 0x04, 0x00000003 },
+       { 0x001018,   1, 0x04, 0x00000020 },
+       { 0x00101c,   1, 0x04, 0x00000001 },
+       { 0x001020,   1, 0x04, 0x00000020 },
+       { 0x001024,   1, 0x04, 0x00000001 },
+       { 0x001444,   3, 0x04, 0x00000000 },
+       { 0x000360,   1, 0x04, 0x20164010 },
+       { 0x000364,   1, 0x04, 0x00000020 },
+       { 0x000368,   1, 0x04, 0x00000000 },
+       { 0x000de4,   1, 0x04, 0x00000000 },
+       { 0x000204,   1, 0x04, 0x00000006 },
+       { 0x000208,   1, 0x04, 0x00000000 },
+       { 0x0002cc,   2, 0x04, 0x003fffff },
+       { 0x001220,   1, 0x04, 0x00000005 },
+       { 0x000fdc,   1, 0x04, 0x00000000 },
+       { 0x000f98,   1, 0x04, 0x00400008 },
+       { 0x001284,   1, 0x04, 0x08000080 },
+       { 0x001450,   1, 0x04, 0x00400008 },
+       { 0x001454,   1, 0x04, 0x08000080 },
+       { 0x000214,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_grctx_init_unk40xx[] = {
+       { 0x404010,   5, 0x04, 0x00000000 },
+       { 0x404024,   1, 0x04, 0x0000e000 },
+       { 0x404028,   1, 0x04, 0x00000000 },
+       { 0x4040a8,   1, 0x04, 0x00000000 },
+       { 0x4040ac,   7, 0x04, 0x00000000 },
+       { 0x4040c8,   1, 0x04, 0xf800008f },
+       { 0x4040d0,   6, 0x04, 0x00000000 },
+       { 0x4040e8,   1, 0x04, 0x00001000 },
+       { 0x4040f8,   1, 0x04, 0x00000000 },
+       { 0x404130,   1, 0x04, 0x00000000 },
+       { 0x404134,   1, 0x04, 0x00000000 },
+       { 0x404138,   1, 0x04, 0x20000040 },
+       { 0x404150,   1, 0x04, 0x0000002e },
+       { 0x404154,   1, 0x04, 0x00000400 },
+       { 0x404158,   1, 0x04, 0x00000200 },
+       { 0x404164,   1, 0x04, 0x00000055 },
+       { 0x4041a0,   4, 0x04, 0x00000000 },
+       { 0x404200,   4, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nve4_grctx_init_unk46xx[] = {
+       { 0x404604,   1, 0x04, 0x00000014 },
+       { 0x404608,   1, 0x04, 0x00000000 },
+       { 0x40460c,   1, 0x04, 0x00003fff },
+       { 0x404610,   1, 0x04, 0x00000100 },
+       { 0x404618,   4, 0x04, 0x00000000 },
+       { 0x40462c,   2, 0x04, 0x00000000 },
+       { 0x404640,   1, 0x04, 0x00000000 },
+       { 0x404654,   1, 0x04, 0x00000000 },
+       { 0x404660,   1, 0x04, 0x00000000 },
+       { 0x404678,   1, 0x04, 0x00000000 },
+       { 0x40467c,   1, 0x04, 0x00000002 },
+       { 0x404680,   8, 0x04, 0x00000000 },
+       { 0x4046a0,   1, 0x04, 0x007f0080 },
+       { 0x4046a4,   8, 0x04, 0x00000000 },
+       { 0x4046c8,   3, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nve4_grctx_init_unk47xx[] = {
+       { 0x404700,   3, 0x04, 0x00000000 },
+       { 0x404718,   7, 0x04, 0x00000000 },
+       { 0x404734,   1, 0x04, 0x00000100 },
+       { 0x404738,   2, 0x04, 0x00000000 },
+       { 0x404744,   2, 0x04, 0x00000000 },
+       { 0x404754,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nve4_grctx_init_unk58xx[] = {
+       { 0x405800,   1, 0x04, 0x0f8000bf },
+       { 0x405830,   1, 0x04, 0x02180648 },
+       { 0x405834,   1, 0x04, 0x08000000 },
+       { 0x405838,   1, 0x04, 0x00000000 },
+       { 0x405854,   1, 0x04, 0x00000000 },
+       { 0x405870,   4, 0x04, 0x00000001 },
+       { 0x405a00,   2, 0x04, 0x00000000 },
+       { 0x405a18,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_grctx_init_unk5bxx[] = {
+       { 0x405b00,   1, 0x04, 0x00000000 },
+       { 0x405b10,   1, 0x04, 0x00001000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_grctx_init_unk60xx[] = {
+       { 0x406020,   1, 0x04, 0x004103c1 },
+       { 0x406028,   4, 0x04, 0x00000001 },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_grctx_init_unk64xx[] = {
+       { 0x4064a8,   1, 0x04, 0x00000000 },
+       { 0x4064ac,   1, 0x04, 0x00003fff },
+       { 0x4064b4,   2, 0x04, 0x00000000 },
+       { 0x4064c0,   1, 0x04, 0x801a00f0 },
+       { 0x4064c4,   1, 0x04, 0x0192ffff },
+       { 0x4064c8,   1, 0x04, 0x01800600 },
+       { 0x4064cc,   9, 0x04, 0x00000000 },
+       { 0x4064fc,   1, 0x04, 0x0000022a },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_grctx_init_unk70xx[] = {
+       { 0x407040,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nve4_grctx_init_unk80xx[] = {
+       { 0x408000,   2, 0x04, 0x00000000 },
+       { 0x408008,   1, 0x04, 0x00000030 },
+       { 0x40800c,   2, 0x04, 0x00000000 },
+       { 0x408014,   1, 0x04, 0x00000069 },
+       { 0x408018,   1, 0x04, 0xe100e100 },
+       { 0x408064,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_grctx_init_rop[] = {
+       { 0x408800,   1, 0x04, 0x02802a3c },
+       { 0x408804,   1, 0x04, 0x00000040 },
+       { 0x408808,   1, 0x04, 0x1043e005 },
+       { 0x408840,   1, 0x04, 0x0000000b },
+       { 0x408900,   1, 0x04, 0x3080b801 },
+       { 0x408904,   1, 0x04, 0x62000001 },
+       { 0x408908,   1, 0x04, 0x00c8102f },
+       { 0x408980,   1, 0x04, 0x0000011d },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_grctx_init_gpc_0[] = {
+       { 0x418380,   1, 0x04, 0x00000016 },
+       { 0x418400,   1, 0x04, 0x38004e00 },
+       { 0x418404,   1, 0x04, 0x71e0ffff },
+       { 0x41840c,   1, 0x04, 0x00001008 },
+       { 0x418410,   1, 0x04, 0x0fff0fff },
+       { 0x418414,   1, 0x04, 0x02200fff },
+       { 0x418450,   6, 0x04, 0x00000000 },
+       { 0x418468,   1, 0x04, 0x00000001 },
+       { 0x41846c,   2, 0x04, 0x00000000 },
+       { 0x418600,   1, 0x04, 0x0000001f },
+       { 0x418684,   1, 0x04, 0x0000000f },
+       { 0x418700,   1, 0x04, 0x00000002 },
+       { 0x418704,   1, 0x04, 0x00000080 },
+       { 0x418708,   3, 0x04, 0x00000000 },
+       { 0x418800,   1, 0x04, 0x7006860a },
+       { 0x418808,   3, 0x04, 0x00000000 },
+       { 0x418828,   1, 0x04, 0x00000044 },
+       { 0x418830,   1, 0x04, 0x10000001 },
+       { 0x4188d8,   1, 0x04, 0x00000008 },
+       { 0x4188e0,   1, 0x04, 0x01000000 },
+       { 0x4188e8,   5, 0x04, 0x00000000 },
+       { 0x4188fc,   1, 0x04, 0x20100018 },
+       { 0x41891c,   1, 0x04, 0x00ff00ff },
+       { 0x418924,   1, 0x04, 0x00000000 },
+       { 0x418928,   1, 0x04, 0x00ffff00 },
+       { 0x41892c,   1, 0x04, 0x0000ff00 },
+       { 0x418b00,   1, 0x04, 0x00000006 },
+       { 0x418b08,   1, 0x04, 0x0a418820 },
+       { 0x418b0c,   1, 0x04, 0x062080e6 },
+       { 0x418b10,   1, 0x04, 0x020398a4 },
+       { 0x418b14,   1, 0x04, 0x0e629062 },
+       { 0x418b18,   1, 0x04, 0x0a418820 },
+       { 0x418b1c,   1, 0x04, 0x000000e6 },
+       { 0x418bb8,   1, 0x04, 0x00000103 },
+       { 0x418c08,   1, 0x04, 0x00000001 },
+       { 0x418c10,   8, 0x04, 0x00000000 },
+       { 0x418c40,   1, 0x04, 0xffffffff },
+       { 0x418c6c,   1, 0x04, 0x00000001 },
+       { 0x418c80,   1, 0x04, 0x20200004 },
+       { 0x418c8c,   1, 0x04, 0x00000001 },
+       { 0x419000,   1, 0x04, 0x00000780 },
+       { 0x419004,   2, 0x04, 0x00000000 },
+       { 0x419014,   1, 0x04, 0x00000004 },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_grctx_init_tpc[] = {
+       { 0x419848,   1, 0x04, 0x00000000 },
+       { 0x419864,   1, 0x04, 0x00000129 },
+       { 0x419888,   1, 0x04, 0x00000000 },
+       { 0x419a00,   1, 0x04, 0x000000f0 },
+       { 0x419a04,   1, 0x04, 0x00000001 },
+       { 0x419a08,   1, 0x04, 0x00000021 },
+       { 0x419a0c,   1, 0x04, 0x00020000 },
+       { 0x419a10,   1, 0x04, 0x00000000 },
+       { 0x419a14,   1, 0x04, 0x00000200 },
+       { 0x419a1c,   1, 0x04, 0x0000c000 },
+       { 0x419a20,   1, 0x04, 0x00000800 },
+       { 0x419a30,   1, 0x04, 0x00000001 },
+       { 0x419ac4,   1, 0x04, 0x0037f440 },
+       { 0x419c00,   1, 0x04, 0x0000000a },
+       { 0x419c04,   1, 0x04, 0x80000006 },
+       { 0x419c08,   1, 0x04, 0x00000002 },
+       { 0x419c20,   1, 0x04, 0x00000000 },
+       { 0x419c24,   1, 0x04, 0x00084210 },
+       { 0x419c28,   1, 0x04, 0x3efbefbe },
+       { 0x419ce8,   1, 0x04, 0x00000000 },
+       { 0x419cf4,   1, 0x04, 0x00003203 },
+       { 0x419e04,   3, 0x04, 0x00000000 },
+       { 0x419e10,   1, 0x04, 0x00000402 },
+       { 0x419e44,   1, 0x04, 0x0013eff2 },
+       { 0x419e48,   1, 0x04, 0x00000000 },
+       { 0x419e4c,   1, 0x04, 0x0000007f },
+       { 0x419e50,  19, 0x04, 0x00000000 },
+       { 0x419eac,   1, 0x04, 0x00001f8f },
+       { 0x419eb0,   1, 0x04, 0x00000d3f },
+       { 0x419ec8,   1, 0x04, 0x0001304f },
+       { 0x419f30,   8, 0x04, 0x00000000 },
+       { 0x419f58,   1, 0x04, 0x00000000 },
+       { 0x419f70,   1, 0x04, 0x00000000 },
+       { 0x419f78,   1, 0x04, 0x0000000b },
+       { 0x419f7c,   1, 0x04, 0x0000027a },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_grctx_init_unk[] = {
+       { 0x41be24,   1, 0x04, 0x00000006 },
+       { 0x41bec0,   1, 0x04, 0x12180000 },
+       { 0x41bec4,   1, 0x04, 0x00037f7f },
+       { 0x41bee4,   1, 0x04, 0x06480430 },
+       { 0x41bf00,   1, 0x04, 0x0a418820 },
+       { 0x41bf04,   1, 0x04, 0x062080e6 },
+       { 0x41bf08,   1, 0x04, 0x020398a4 },
+       { 0x41bf0c,   1, 0x04, 0x0e629062 },
+       { 0x41bf10,   1, 0x04, 0x0a418820 },
+       { 0x41bf14,   1, 0x04, 0x000000e6 },
+       { 0x41bfd0,   1, 0x04, 0x00900103 },
+       { 0x41bfe0,   1, 0x04, 0x00400001 },
+       { 0x41bfe4,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static void
+nve4_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+{
+       u32 magic[GPC_MAX][2];
+       u32 offset;
+       int gpc;
+
+       mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+       mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+       mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
+       mmio_list(0x40800c, 0x00000000,  8, 1);
+       mmio_list(0x408010, 0x80000000,  0, 0);
+       mmio_list(0x419004, 0x00000000,  8, 1);
+       mmio_list(0x419008, 0x00000000,  0, 0);
+       mmio_list(0x4064cc, 0x80000000,  0, 0);
+       mmio_list(0x408004, 0x00000000,  8, 0);
+       mmio_list(0x408008, 0x80000030,  0, 0);
+       mmio_list(0x418808, 0x00000000,  8, 0);
+       mmio_list(0x41880c, 0x80000030,  0, 0);
+       mmio_list(0x4064c8, 0x01800600,  0, 0);
+       mmio_list(0x418810, 0x80000000, 12, 2);
+       mmio_list(0x419848, 0x10000000, 12, 2);
+
+       mmio_list(0x405830, 0x02180648,  0, 0);
+       mmio_list(0x4064c4, 0x0192ffff,  0, 0);
+
+       for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
+               u16 magic0 = 0x0218 * priv->tpc_nr[gpc];
+               u16 magic1 = 0x0648 * priv->tpc_nr[gpc];
+               magic[gpc][0]  = 0x10000000 | (magic0 << 16) | offset;
+               magic[gpc][1]  = 0x00000000 | (magic1 << 16);
+               offset += 0x0324 * priv->tpc_nr[gpc];
+       }
+
+       for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+               mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0);
+               mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0);
+               offset += 0x07ff * priv->tpc_nr[gpc];
+       }
+
+       mmio_list(0x17e91c, 0x06060609, 0, 0);
+       mmio_list(0x17e920, 0x00090a05, 0, 0);
+}
+
+void
+nve4_grctx_generate_unkn(struct nvc0_graph_priv *priv)
+{
+       nv_mask(priv, 0x418c6c, 0x00000001, 0x00000001);
+       nv_mask(priv, 0x41980c, 0x00000010, 0x00000010);
+       nv_mask(priv, 0x41be08, 0x00000004, 0x00000004);
+       nv_mask(priv, 0x4064c0, 0x80000000, 0x80000000);
+       nv_mask(priv, 0x405800, 0x08000000, 0x08000000);
+       nv_mask(priv, 0x419c00, 0x00000008, 0x00000008);
+}
+
+void
+nve4_grctx_generate_r418bb8(struct nvc0_graph_priv *priv)
+{
+       u32 data[6] = {}, data2[2] = {};
+       u8  tpcnr[GPC_MAX];
+       u8  shift, ntpcv;
+       int gpc, tpc, i;
+
+       /* calculate first set of magics */
+       memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
+
+       gpc = -1;
+       for (tpc = 0; tpc < priv->tpc_total; tpc++) {
+               do {
+                       gpc = (gpc + 1) % priv->gpc_nr;
+               } while (!tpcnr[gpc]);
+               tpcnr[gpc]--;
+
+               data[tpc / 6] |= gpc << ((tpc % 6) * 5);
+       }
+
+       for (; tpc < 32; tpc++)
+               data[tpc / 6] |= 7 << ((tpc % 6) * 5);
+
+       /* and the second... */
+       shift = 0;
+       ntpcv = priv->tpc_total;
+       while (!(ntpcv & (1 << 4))) {
+               ntpcv <<= 1;
+               shift++;
+       }
+
+       data2[0]  = (ntpcv << 16);
+       data2[0] |= (shift << 21);
+       data2[0] |= (((1 << (0 + 5)) % ntpcv) << 24);
+       for (i = 1; i < 7; i++)
+               data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5);
+
+       /* GPC_BROADCAST */
+       nv_wr32(priv, 0x418bb8, (priv->tpc_total << 8) |
+                                priv->magic_not_rop_nr);
+       for (i = 0; i < 6; i++)
+               nv_wr32(priv, 0x418b08 + (i * 4), data[i]);
+
+       /* GPC_BROADCAST.TP_BROADCAST */
+       nv_wr32(priv, 0x41bfd0, (priv->tpc_total << 8) |
+                                priv->magic_not_rop_nr | data2[0]);
+       nv_wr32(priv, 0x41bfe4, data2[1]);
+       for (i = 0; i < 6; i++)
+               nv_wr32(priv, 0x41bf00 + (i * 4), data[i]);
+
+       /* UNK78xx */
+       nv_wr32(priv, 0x4078bc, (priv->tpc_total << 8) |
+                                priv->magic_not_rop_nr);
+       for (i = 0; i < 6; i++)
+               nv_wr32(priv, 0x40780c + (i * 4), data[i]);
+}
+
+void
+nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+{
+       struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass;
+       int i;
+
+       nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
+
+       for (i = 0; oclass->hub[i]; i++)
+               nvc0_graph_mmio(priv, oclass->hub[i]);
+       for (i = 0; oclass->gpc[i]; i++)
+               nvc0_graph_mmio(priv, oclass->gpc[i]);
+
+       nv_wr32(priv, 0x404154, 0x00000000);
+
+       oclass->mods(priv, info);
+       oclass->unkn(priv);
+
+       nvc0_grctx_generate_tpcid(priv);
+       nvc0_grctx_generate_r406028(priv);
+       nve4_grctx_generate_r418bb8(priv);
+       nvc0_grctx_generate_r406800(priv);
+
+       for (i = 0; i < 8; i++)
+               nv_wr32(priv, 0x4064d0 + (i * 0x04), 0x00000000);
+
+       nv_wr32(priv, 0x405b00, (priv->tpc_total << 8) | priv->gpc_nr);
+       if (priv->gpc_nr == 1) {
+               nv_mask(priv, 0x408850, 0x0000000f, priv->tpc_nr[0]);
+               nv_mask(priv, 0x408958, 0x0000000f, priv->tpc_nr[0]);
+       } else {
+               nv_mask(priv, 0x408850, 0x0000000f, priv->gpc_nr);
+               nv_mask(priv, 0x408958, 0x0000000f, priv->gpc_nr);
+       }
+       nv_mask(priv, 0x419f78, 0x00000001, 0x00000000);
+
+       nvc0_graph_icmd(priv, oclass->icmd);
+       nv_wr32(priv, 0x404154, 0x00000400);
+       nvc0_graph_mthd(priv, oclass->mthd);
+       nv_mask(priv, 0x000260, 0x00000001, 0x00000001);
+
+       nv_mask(priv, 0x418800, 0x00200000, 0x00200000);
+       nv_mask(priv, 0x41be10, 0x00800000, 0x00800000);
+}
+
+static struct nvc0_graph_init *
+nve4_grctx_init_hub[] = {
+       nvc0_grctx_init_base,
+       nve4_grctx_init_unk40xx,
+       nvc0_grctx_init_unk44xx,
+       nve4_grctx_init_unk46xx,
+       nve4_grctx_init_unk47xx,
+       nve4_grctx_init_unk58xx,
+       nve4_grctx_init_unk5bxx,
+       nve4_grctx_init_unk60xx,
+       nve4_grctx_init_unk64xx,
+       nve4_grctx_init_unk70xx,
+       nvc0_grctx_init_unk78xx,
+       nve4_grctx_init_unk80xx,
+       nve4_grctx_init_rop,
+       NULL
+};
+
+struct nvc0_graph_init *
+nve4_grctx_init_gpc[] = {
+       nve4_grctx_init_gpc_0,
+       nvc0_grctx_init_gpc_1,
+       nve4_grctx_init_tpc,
+       nve4_grctx_init_unk,
+       NULL
+};
+
+static struct nvc0_graph_mthd
+nve4_grctx_init_mthd[] = {
+       { 0xa097, nve4_grctx_init_a097, },
+       { 0x902d, nvc0_grctx_init_902d, },
+       { 0x902d, nvc0_grctx_init_mthd_magic, },
+       {}
+};
+
+struct nouveau_oclass *
+nve4_grctx_oclass = &(struct nvc0_grctx_oclass) {
+       .base.handle = NV_ENGCTX(GR, 0xe4),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_context_ctor,
+               .dtor = nvc0_graph_context_dtor,
+               .init = _nouveau_graph_context_init,
+               .fini = _nouveau_graph_context_fini,
+               .rd32 = _nouveau_graph_context_rd32,
+               .wr32 = _nouveau_graph_context_wr32,
+       },
+       .main = nve4_grctx_generate_main,
+       .mods = nve4_grctx_generate_mods,
+       .unkn = nve4_grctx_generate_unkn,
+       .hub  = nve4_grctx_init_hub,
+       .gpc  = nve4_grctx_init_gpc,
+       .icmd = nve4_grctx_init_icmd,
+       .mthd = nve4_grctx_init_mthd,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c
new file mode 100644 (file)
index 0000000..dcb2ebb
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * 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 "nvc0.h"
+
+static struct nvc0_graph_init
+nvf0_grctx_init_unk40xx[] = {
+       { 0x404004,   8, 0x04, 0x00000000 },
+       { 0x404024,   1, 0x04, 0x0000e000 },
+       { 0x404028,   8, 0x04, 0x00000000 },
+       { 0x4040a8,   8, 0x04, 0x00000000 },
+       { 0x4040c8,   1, 0x04, 0xf800008f },
+       { 0x4040d0,   6, 0x04, 0x00000000 },
+       { 0x4040e8,   1, 0x04, 0x00001000 },
+       { 0x4040f8,   1, 0x04, 0x00000000 },
+       { 0x404100,  10, 0x04, 0x00000000 },
+       { 0x404130,   2, 0x04, 0x00000000 },
+       { 0x404138,   1, 0x04, 0x20000040 },
+       { 0x404150,   1, 0x04, 0x0000002e },
+       { 0x404154,   1, 0x04, 0x00000400 },
+       { 0x404158,   1, 0x04, 0x00000200 },
+       { 0x404164,   1, 0x04, 0x00000055 },
+       { 0x40417c,   2, 0x04, 0x00000000 },
+       { 0x4041a0,   4, 0x04, 0x00000000 },
+       { 0x404200,   1, 0x04, 0x0000a197 },
+       { 0x404204,   1, 0x04, 0x0000a1c0 },
+       { 0x404208,   1, 0x04, 0x0000a140 },
+       { 0x40420c,   1, 0x04, 0x0000902d },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_grctx_init_unk44xx[] = {
+       { 0x404404,  12, 0x04, 0x00000000 },
+       { 0x404438,   1, 0x04, 0x00000000 },
+       { 0x404460,   2, 0x04, 0x00000000 },
+       { 0x404468,   1, 0x04, 0x00ffffff },
+       { 0x40446c,   1, 0x04, 0x00000000 },
+       { 0x404480,   1, 0x04, 0x00000001 },
+       { 0x404498,   1, 0x04, 0x00000001 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_grctx_init_unk5bxx[] = {
+       { 0x405b00,   1, 0x04, 0x00000000 },
+       { 0x405b10,   1, 0x04, 0x00001000 },
+       { 0x405b20,   1, 0x04, 0x04000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_grctx_init_unk60xx[] = {
+       { 0x406020,   1, 0x04, 0x034103c1 },
+       { 0x406028,   4, 0x04, 0x00000001 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_grctx_init_unk64xx[] = {
+       { 0x4064a8,   1, 0x04, 0x00000000 },
+       { 0x4064ac,   1, 0x04, 0x00003fff },
+       { 0x4064b0,   3, 0x04, 0x00000000 },
+       { 0x4064c0,   1, 0x04, 0x802000f0 },
+       { 0x4064c4,   1, 0x04, 0x0192ffff },
+       { 0x4064c8,   1, 0x04, 0x018007c0 },
+       { 0x4064cc,   9, 0x04, 0x00000000 },
+       { 0x4064fc,   1, 0x04, 0x0000022a },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_grctx_init_unk88xx[] = {
+       { 0x408800,   1, 0x04, 0x12802a3c },
+       { 0x408804,   1, 0x04, 0x00000040 },
+       { 0x408808,   1, 0x04, 0x1003e005 },
+       { 0x408840,   1, 0x04, 0x0000000b },
+       { 0x408900,   1, 0x04, 0x3080b801 },
+       { 0x408904,   1, 0x04, 0x62000001 },
+       { 0x408908,   1, 0x04, 0x00c8102f },
+       { 0x408980,   1, 0x04, 0x0000011d },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_grctx_init_gpc_0[] = {
+       { 0x418380,   1, 0x04, 0x00000016 },
+       { 0x418400,   1, 0x04, 0x38004e00 },
+       { 0x418404,   1, 0x04, 0x71e0ffff },
+       { 0x41840c,   1, 0x04, 0x00001008 },
+       { 0x418410,   1, 0x04, 0x0fff0fff },
+       { 0x418414,   1, 0x04, 0x02200fff },
+       { 0x418450,   6, 0x04, 0x00000000 },
+       { 0x418468,   1, 0x04, 0x00000001 },
+       { 0x41846c,   2, 0x04, 0x00000000 },
+       { 0x418600,   1, 0x04, 0x0000001f },
+       { 0x418684,   1, 0x04, 0x0000000f },
+       { 0x418700,   1, 0x04, 0x00000002 },
+       { 0x418704,   1, 0x04, 0x00000080 },
+       { 0x418708,   3, 0x04, 0x00000000 },
+       { 0x418800,   1, 0x04, 0x7006860a },
+       { 0x418808,   1, 0x04, 0x00000000 },
+       { 0x41880c,   1, 0x04, 0x00000030 },
+       { 0x418810,   1, 0x04, 0x00000000 },
+       { 0x418828,   1, 0x04, 0x00000044 },
+       { 0x418830,   1, 0x04, 0x10000001 },
+       { 0x4188d8,   1, 0x04, 0x00000008 },
+       { 0x4188e0,   1, 0x04, 0x01000000 },
+       { 0x4188e8,   5, 0x04, 0x00000000 },
+       { 0x4188fc,   1, 0x04, 0x20100018 },
+       { 0x41891c,   1, 0x04, 0x00ff00ff },
+       { 0x418924,   1, 0x04, 0x00000000 },
+       { 0x418928,   1, 0x04, 0x00ffff00 },
+       { 0x41892c,   1, 0x04, 0x0000ff00 },
+       { 0x418b00,   1, 0x04, 0x00000006 },
+       { 0x418b08,   1, 0x04, 0x0a418820 },
+       { 0x418b0c,   1, 0x04, 0x062080e6 },
+       { 0x418b10,   1, 0x04, 0x020398a4 },
+       { 0x418b14,   1, 0x04, 0x0e629062 },
+       { 0x418b18,   1, 0x04, 0x0a418820 },
+       { 0x418b1c,   1, 0x04, 0x000000e6 },
+       { 0x418bb8,   1, 0x04, 0x00000103 },
+       { 0x418c08,   1, 0x04, 0x00000001 },
+       { 0x418c10,   8, 0x04, 0x00000000 },
+       { 0x418c40,   1, 0x04, 0xffffffff },
+       { 0x418c6c,   1, 0x04, 0x00000001 },
+       { 0x418c80,   1, 0x04, 0x20200004 },
+       { 0x418c8c,   1, 0x04, 0x00000001 },
+       { 0x418d24,   1, 0x04, 0x00000000 },
+       { 0x419000,   1, 0x04, 0x00000780 },
+       { 0x419004,   2, 0x04, 0x00000000 },
+       { 0x419014,   1, 0x04, 0x00000004 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_grctx_init_tpc[] = {
+       { 0x419848,   1, 0x04, 0x00000000 },
+       { 0x419864,   1, 0x04, 0x00000129 },
+       { 0x419888,   1, 0x04, 0x00000000 },
+       { 0x419a00,   1, 0x04, 0x000000f0 },
+       { 0x419a04,   1, 0x04, 0x00000001 },
+       { 0x419a08,   1, 0x04, 0x00000021 },
+       { 0x419a0c,   1, 0x04, 0x00020000 },
+       { 0x419a10,   1, 0x04, 0x00000000 },
+       { 0x419a14,   1, 0x04, 0x00000200 },
+       { 0x419a1c,   1, 0x04, 0x0000c000 },
+       { 0x419a20,   1, 0x04, 0x00020800 },
+       { 0x419a30,   1, 0x04, 0x00000001 },
+       { 0x419ac4,   1, 0x04, 0x0037f440 },
+       { 0x419c00,   1, 0x04, 0x0000001a },
+       { 0x419c04,   1, 0x04, 0x80000006 },
+       { 0x419c08,   1, 0x04, 0x00000002 },
+       { 0x419c20,   1, 0x04, 0x00000000 },
+       { 0x419c24,   1, 0x04, 0x00084210 },
+       { 0x419c28,   1, 0x04, 0x3efbefbe },
+       { 0x419ce8,   1, 0x04, 0x00000000 },
+       { 0x419cf4,   1, 0x04, 0x00000203 },
+       { 0x419e04,   1, 0x04, 0x00000000 },
+       { 0x419e08,   1, 0x04, 0x0000001d },
+       { 0x419e0c,   1, 0x04, 0x00000000 },
+       { 0x419e10,   1, 0x04, 0x00001c02 },
+       { 0x419e44,   1, 0x04, 0x0013eff2 },
+       { 0x419e48,   1, 0x04, 0x00000000 },
+       { 0x419e4c,   1, 0x04, 0x0000007f },
+       { 0x419e50,   2, 0x04, 0x00000000 },
+       { 0x419e58,   1, 0x04, 0x00000001 },
+       { 0x419e5c,   3, 0x04, 0x00000000 },
+       { 0x419e68,   1, 0x04, 0x00000002 },
+       { 0x419e6c,  12, 0x04, 0x00000000 },
+       { 0x419eac,   1, 0x04, 0x00001fcf },
+       { 0x419eb0,   1, 0x04, 0x0db00da0 },
+       { 0x419eb8,   1, 0x04, 0x00000000 },
+       { 0x419ec8,   1, 0x04, 0x0001304f },
+       { 0x419f30,   4, 0x04, 0x00000000 },
+       { 0x419f40,   1, 0x04, 0x00000018 },
+       { 0x419f44,   3, 0x04, 0x00000000 },
+       { 0x419f58,   1, 0x04, 0x00000000 },
+       { 0x419f70,   1, 0x04, 0x00007300 },
+       { 0x419f78,   1, 0x04, 0x000000eb },
+       { 0x419f7c,   1, 0x04, 0x00000404 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_grctx_init_unk[] = {
+       { 0x41be24,   1, 0x04, 0x00000006 },
+       { 0x41bec0,   1, 0x04, 0x10000000 },
+       { 0x41bec4,   1, 0x04, 0x00037f7f },
+       { 0x41bee4,   1, 0x04, 0x00000000 },
+       { 0x41bf00,   1, 0x04, 0x0a418820 },
+       { 0x41bf04,   1, 0x04, 0x062080e6 },
+       { 0x41bf08,   1, 0x04, 0x020398a4 },
+       { 0x41bf0c,   1, 0x04, 0x0e629062 },
+       { 0x41bf10,   1, 0x04, 0x0a418820 },
+       { 0x41bf14,   1, 0x04, 0x000000e6 },
+       { 0x41bfd0,   1, 0x04, 0x00900103 },
+       { 0x41bfe0,   1, 0x04, 0x00400001 },
+       { 0x41bfe4,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static void
+nvf0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+{
+       u32 magic[GPC_MAX][4];
+       u32 offset;
+       int gpc;
+
+       mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+       mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
+       mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
+       mmio_list(0x40800c, 0x00000000,  8, 1);
+       mmio_list(0x408010, 0x80000000,  0, 0);
+       mmio_list(0x419004, 0x00000000,  8, 1);
+       mmio_list(0x419008, 0x00000000,  0, 0);
+       mmio_list(0x4064cc, 0x80000000,  0, 0);
+       mmio_list(0x408004, 0x00000000,  8, 0);
+       mmio_list(0x408008, 0x80000030,  0, 0);
+       mmio_list(0x418808, 0x00000000,  8, 0);
+       mmio_list(0x41880c, 0x80000030,  0, 0);
+       mmio_list(0x4064c8, 0x01800600,  0, 0);
+       mmio_list(0x418810, 0x80000000, 12, 2);
+       mmio_list(0x419848, 0x10000000, 12, 2);
+
+       mmio_list(0x405830, 0x02180648,  0, 0);
+       mmio_list(0x4064c4, 0x0192ffff,  0, 0);
+
+       for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
+               u16 magic0 = 0x0218 * (priv->tpc_nr[gpc] - 1);
+               u16 magic1 = 0x0648 * (priv->tpc_nr[gpc] - 1);
+               u16 magic2 = 0x0218;
+               u16 magic3 = 0x0648;
+               magic[gpc][0]  = 0x10000000 | (magic0 << 16) | offset;
+               magic[gpc][1]  = 0x00000000 | (magic1 << 16);
+               offset += 0x0324 * (priv->tpc_nr[gpc] - 1);;
+               magic[gpc][2]  = 0x10000000 | (magic2 << 16) | offset;
+               magic[gpc][3]  = 0x00000000 | (magic3 << 16);
+               offset += 0x0324;
+       }
+
+       for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+               mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0);
+               mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0);
+               offset += 0x07ff * (priv->tpc_nr[gpc] - 1);
+               mmio_list(GPC_UNIT(gpc, 0x32c0), magic[gpc][2], 0, 0);
+               mmio_list(GPC_UNIT(gpc, 0x32e4), magic[gpc][3] | offset, 0, 0);
+               offset += 0x07ff;
+       }
+
+       mmio_list(0x17e91c, 0x06060609, 0, 0);
+       mmio_list(0x17e920, 0x00090a05, 0, 0);
+}
+
+static struct nvc0_graph_init *
+nvf0_grctx_init_hub[] = {
+       nvc0_grctx_init_base,
+       nvf0_grctx_init_unk40xx,
+       nvf0_grctx_init_unk44xx,
+       nve4_grctx_init_unk46xx,
+       nve4_grctx_init_unk47xx,
+       nve4_grctx_init_unk58xx,
+       nvf0_grctx_init_unk5bxx,
+       nvf0_grctx_init_unk60xx,
+       nvf0_grctx_init_unk64xx,
+       nve4_grctx_init_unk80xx,
+       nvf0_grctx_init_unk88xx,
+       nvd9_grctx_init_rop,
+       NULL
+};
+
+struct nvc0_graph_init *
+nvf0_grctx_init_gpc[] = {
+       nvf0_grctx_init_gpc_0,
+       nvc0_grctx_init_gpc_1,
+       nvf0_grctx_init_tpc,
+       nvf0_grctx_init_unk,
+       NULL
+};
+
+static struct nvc0_graph_mthd
+nvf0_grctx_init_mthd[] = {
+       { 0xa197, nvc1_grctx_init_9097, },
+       { 0x902d, nvc0_grctx_init_902d, },
+       { 0x902d, nvc0_grctx_init_mthd_magic, },
+       {}
+};
+
+struct nouveau_oclass *
+nvf0_grctx_oclass = &(struct nvc0_grctx_oclass) {
+       .base.handle = NV_ENGCTX(GR, 0xf0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_context_ctor,
+               .dtor = nvc0_graph_context_dtor,
+               .init = _nouveau_graph_context_init,
+               .fini = _nouveau_graph_context_fini,
+               .rd32 = _nouveau_graph_context_rd32,
+               .wr32 = _nouveau_graph_context_wr32,
+       },
+       .main = nve4_grctx_generate_main,
+       .mods = nvf0_grctx_generate_mods,
+       .unkn = nve4_grctx_generate_unkn,
+       .hub  = nvf0_grctx_init_hub,
+       .gpc  = nvf0_grctx_init_gpc,
+       .icmd = nvc0_grctx_init_icmd,
+       .mthd = nvf0_grctx_init_mthd,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc
new file mode 100644 (file)
index 0000000..5d24b6d
--- /dev/null
@@ -0,0 +1,375 @@
+/* fuc microcode util functions for nvc0 PGRAPH
+ *
+ * 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
+ */
+
+#ifdef INCLUDE_CODE
+// queue_put - add request to queue
+//
+// In : $r13 queue pointer
+//     $r14 command
+//     $r15 data
+//
+queue_put:
+       // make sure we have space..
+       ld b32 $r8 D[$r13 + 0x0]        // GET
+       ld b32 $r9 D[$r13 + 0x4]        // PUT
+       xor $r8 8
+       cmpu b32 $r8 $r9
+       bra ne #queue_put_next
+               mov $r15 E_CMD_OVERFLOW
+               call #error
+               ret
+
+       // store cmd/data on queue
+       queue_put_next:
+       and $r8 $r9 7
+       shl b32 $r8 3
+       add b32 $r8 $r13
+       add b32 $r8 8
+       st b32 D[$r8 + 0x0] $r14
+       st b32 D[$r8 + 0x4] $r15
+
+       // update PUT
+       add b32 $r9 1
+       and $r9 0xf
+       st b32 D[$r13 + 0x4] $r9
+       ret
+
+// queue_get - fetch request from queue
+//
+// In : $r13 queue pointer
+//
+// Out:        $p1  clear on success (data available)
+//     $r14 command
+//     $r15 data
+//
+queue_get:
+       bset $flags $p1
+       ld b32 $r8 D[$r13 + 0x0]        // GET
+       ld b32 $r9 D[$r13 + 0x4]        // PUT
+       cmpu b32 $r8 $r9
+       bra e #queue_get_done
+               // fetch first cmd/data pair
+               and $r9 $r8 7
+               shl b32 $r9 3
+               add b32 $r9 $r13
+               add b32 $r9 8
+               ld b32 $r14 D[$r9 + 0x0]
+               ld b32 $r15 D[$r9 + 0x4]
+
+               // update GET
+               add b32 $r8 1
+               and $r8 0xf
+               st b32 D[$r13 + 0x0] $r8
+               bclr $flags $p1
+queue_get_done:
+       ret
+
+// nv_rd32 - read 32-bit value from nv register
+//
+// In : $r14 register
+// Out: $r15 value
+//
+nv_rd32:
+       mov $r11 0x728
+       shl b32 $r11 6
+       mov b32 $r12 $r14
+       bset $r12 31                    // MMIO_CTRL_PENDING
+       iowr I[$r11 + 0x000] $r12       // MMIO_CTRL
+       nv_rd32_wait:
+               iord $r12 I[$r11 + 0x000]
+               xbit $r12 $r12 31
+               bra ne #nv_rd32_wait
+       mov $r10 6                      // DONE_MMIO_RD
+       call #wait_doneo
+       iord $r15 I[$r11 + 0x100]       // MMIO_RDVAL
+       ret
+
+// nv_wr32 - write 32-bit value to nv register
+//
+// In : $r14 register
+//      $r15 value
+//
+nv_wr32:
+       mov $r11 0x728
+       shl b32 $r11 6
+       iowr I[$r11 + 0x200] $r15       // MMIO_WRVAL
+       mov b32 $r12 $r14
+       bset $r12 31                    // MMIO_CTRL_PENDING
+       bset $r12 30                    // MMIO_CTRL_WRITE
+       iowr I[$r11 + 0x000] $r12       // MMIO_CTRL
+       nv_wr32_wait:
+               iord $r12 I[$r11 + 0x000]
+               xbit $r12 $r12 31
+               bra ne #nv_wr32_wait
+       ret
+
+// (re)set watchdog timer
+//
+// In : $r15 timeout
+//
+watchdog_reset:
+       mov $r8 0x430
+       shl b32 $r8 6
+       bset $r15 31
+       iowr I[$r8 + 0x000] $r15
+       ret
+
+// clear watchdog timer
+watchdog_clear:
+       mov $r8 0x430
+       shl b32 $r8 6
+       iowr I[$r8 + 0x000] $r0
+       ret
+
+// wait_donez - wait on FUC_DONE bit to become clear
+//
+// In : $r10 bit to wait on
+//
+wait_donez:
+       trace_set(T_WAIT);
+       nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(6), 0, $r10)
+       wait_donez_ne:
+               nv_iord($r8, NV_PGRAPH_FECS_SIGNAL, 0)
+               xbit $r8 $r8 $r10
+               bra ne #wait_donez_ne
+       trace_clr(T_WAIT)
+       ret
+
+// wait_doneo - wait on FUC_DONE bit to become set
+//
+// In : $r10 bit to wait on
+//
+wait_doneo:
+       trace_set(T_WAIT);
+       mov $r8 0x818
+       shl b32 $r8 6
+       iowr I[$r8 + 0x000] $r10
+       wait_doneo_e:
+               mov $r8 0x400
+               shl b32 $r8 6
+               iord $r8 I[$r8 + 0x000]
+               xbit $r8 $r8 $r10
+               bra e #wait_doneo_e
+       trace_clr(T_WAIT)
+       ret
+
+// mmctx_size - determine size of a mmio list transfer
+//
+// In : $r14 mmio list head
+//      $r15 mmio list tail
+// Out: $r15 transfer size (in bytes)
+//
+mmctx_size:
+       clear b32 $r9
+       nv_mmctx_size_loop:
+               ld b32 $r8 D[$r14]
+               shr b32 $r8 26
+               add b32 $r8 1
+               shl b32 $r8 2
+               add b32 $r9 $r8
+               add b32 $r14 4
+               cmpu b32 $r14 $r15
+               bra ne #nv_mmctx_size_loop
+       mov b32 $r15 $r9
+       ret
+
+// mmctx_xfer - execute a list of mmio transfers
+//
+// In : $r10 flags
+//             bit 0: direction (0 = save, 1 = load)
+//             bit 1: set if first transfer
+//             bit 2: set if last transfer
+//     $r11 base
+//     $r12 mmio list head
+//     $r13 mmio list tail
+//     $r14 multi_stride
+//     $r15 multi_mask
+//
+mmctx_xfer:
+       trace_set(T_MMCTX)
+       mov $r8 0x710
+       shl b32 $r8 6
+       clear b32 $r9
+       or $r11 $r11
+       bra e #mmctx_base_disabled
+               iowr I[$r8 + 0x000] $r11        // MMCTX_BASE
+               bset $r9 0                      // BASE_EN
+       mmctx_base_disabled:
+       or $r14 $r14
+       bra e #mmctx_multi_disabled
+               iowr I[$r8 + 0x200] $r14        // MMCTX_MULTI_STRIDE
+               iowr I[$r8 + 0x300] $r15        // MMCTX_MULTI_MASK
+               bset $r9 1                      // MULTI_EN
+       mmctx_multi_disabled:
+       add b32 $r8 0x100
+
+       xbit $r11 $r10 0
+       shl b32 $r11 16                 // DIR
+       bset $r11 12                    // QLIMIT = 0x10
+       xbit $r14 $r10 1
+       shl b32 $r14 17
+       or $r11 $r14                    // START_TRIGGER
+       iowr I[$r8 + 0x000] $r11        // MMCTX_CTRL
+
+       // loop over the mmio list, and send requests to the hw
+       mmctx_exec_loop:
+               // wait for space in mmctx queue
+               mmctx_wait_free:
+                       iord $r14 I[$r8 + 0x000] // MMCTX_CTRL
+                       and $r14 0x1f
+                       bra e #mmctx_wait_free
+
+               // queue up an entry
+               ld b32 $r14 D[$r12]
+               or $r14 $r9
+               iowr I[$r8 + 0x300] $r14
+               add b32 $r12 4
+               cmpu b32 $r12 $r13
+               bra ne #mmctx_exec_loop
+
+       xbit $r11 $r10 2
+       bra ne #mmctx_stop
+               // wait for queue to empty
+               mmctx_fini_wait:
+                       iord $r11 I[$r8 + 0x000]        // MMCTX_CTRL
+                       and $r11 0x1f
+                       cmpu b32 $r11 0x10
+                       bra ne #mmctx_fini_wait
+               mov $r10 2                              // DONE_MMCTX
+               call #wait_donez
+               bra #mmctx_done
+       mmctx_stop:
+               xbit $r11 $r10 0
+               shl b32 $r11 16                 // DIR
+               bset $r11 12                    // QLIMIT = 0x10
+               bset $r11 18                    // STOP_TRIGGER
+               iowr I[$r8 + 0x000] $r11        // MMCTX_CTRL
+               mmctx_stop_wait:
+                       // wait for STOP_TRIGGER to clear
+                       iord $r11 I[$r8 + 0x000] // MMCTX_CTRL
+                       xbit $r11 $r11 18
+                       bra ne #mmctx_stop_wait
+       mmctx_done:
+       trace_clr(T_MMCTX)
+       ret
+
+// Wait for DONE_STRAND
+//
+strand_wait:
+       push $r10
+       mov $r10 2
+       call #wait_donez
+       pop $r10
+       ret
+
+// unknown - call before issuing strand commands
+//
+strand_pre:
+       mov $r8 0x4afc
+       sethi $r8 0x20000
+       mov $r9 0xc
+       iowr I[$r8] $r9
+       call #strand_wait
+       ret
+
+// unknown - call after issuing strand commands
+//
+strand_post:
+       mov $r8 0x4afc
+       sethi $r8 0x20000
+       mov $r9 0xd
+       iowr I[$r8] $r9
+       call #strand_wait
+       ret
+
+// Selects strand set?!
+//
+// In: $r14 id
+//
+strand_set:
+       mov $r10 0x4ffc
+       sethi $r10 0x20000
+       sub b32 $r11 $r10 0x500
+       mov $r12 0xf
+       iowr I[$r10 + 0x000] $r12               // 0x93c = 0xf
+       mov $r12 0xb
+       iowr I[$r11 + 0x000] $r12               // 0x928 = 0xb
+       call #strand_wait
+       iowr I[$r10 + 0x000] $r14               // 0x93c = <id>
+       mov $r12 0xa
+       iowr I[$r11 + 0x000] $r12               // 0x928 = 0xa
+       call #strand_wait
+       ret
+
+// Initialise strand context data
+//
+// In : $r15 context base
+// Out: $r15 context size (in bytes)
+//
+// Strandset(?) 3 hardcoded currently
+//
+strand_ctx_init:
+       trace_set(T_STRINIT)
+       call #strand_pre
+       mov $r14 3
+       call #strand_set
+       mov $r10 0x46fc
+       sethi $r10 0x20000
+       add b32 $r11 $r10 0x400
+       iowr I[$r10 + 0x100] $r0        // STRAND_FIRST_GENE = 0
+       mov $r12 1
+       iowr I[$r11 + 0x000] $r12       // STRAND_CMD = LATCH_FIRST_GENE
+       call #strand_wait
+       sub b32 $r12 $r0 1
+       iowr I[$r10 + 0x000] $r12       // STRAND_GENE_CNT = 0xffffffff
+       mov $r12 2
+       iowr I[$r11 + 0x000] $r12       // STRAND_CMD = LATCH_GENE_CNT
+       call #strand_wait
+       call #strand_post
+
+       // read the size of each strand, poke the context offset of
+       // each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry
+       // about it later then.
+       mov $r8 0x880
+       shl b32 $r8 6
+       iord $r9 I[$r8 + 0x000]         // STRANDS
+       add b32 $r8 0x2200
+       shr b32 $r14 $r15 8
+       ctx_init_strand_loop:
+               iowr I[$r8 + 0x000] $r14        // STRAND_SAVE_SWBASE
+               iowr I[$r8 + 0x100] $r14        // STRAND_LOAD_SWBASE
+               iord $r10 I[$r8 + 0x200]        // STRAND_SIZE
+               shr b32 $r10 6
+               add b32 $r10 1
+               add b32 $r14 $r10
+               add b32 $r8 4
+               sub b32 $r9 1
+               bra ne #ctx_init_strand_loop
+
+       shl b32 $r14 8
+       sub b32 $r15 $r14 $r15
+       trace_clr(T_STRINIT)
+       ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc
new file mode 100644 (file)
index 0000000..5547c1b
--- /dev/null
@@ -0,0 +1,404 @@
+/* fuc microcode for nvc0 PGRAPH/GPC
+ *
+ * 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
+ */
+
+/* TODO
+ * - bracket certain functions with scratch writes, useful for debugging
+ * - watchdog timer around ctx operations
+ */
+
+#ifdef INCLUDE_DATA
+gpc_mmio_list_head:    .b32 #mmio_list_base
+gpc_mmio_list_tail:
+tpc_mmio_list_head:    .b32 #mmio_list_base
+tpc_mmio_list_tail:
+unk_mmio_list_head:    .b32 #mmio_list_base
+unk_mmio_list_tail:    .b32 #mmio_list_base
+
+gpc_id:                        .b32 0
+
+tpc_count:             .b32 0
+tpc_mask:              .b32 0
+
+#if NV_PGRAPH_GPCX_UNK__SIZE > 0
+unk_count:             .b32 0
+unk_mask:              .b32 0
+#endif
+
+cmd_queue:             queue_init
+
+mmio_list_base:
+#endif
+
+#ifdef INCLUDE_CODE
+// reports an exception to the host
+//
+// In: $r15 error code (see nvc0.fuc)
+//
+error:
+       push $r14
+       mov $r14 -0x67ec        // 0x9814
+       sethi $r14 0x400000
+       call #nv_wr32           // HUB_CTXCTL_CC_SCRATCH[5] = error code
+       add b32 $r14 0x41c
+       mov $r15 1
+       call #nv_wr32           // HUB_CTXCTL_INTR_UP_SET
+       pop $r14
+       ret
+
+// GPC fuc initialisation, executed by triggering ucode start, will
+// fall through to main loop after completion.
+//
+// Input:
+//   CC_SCRATCH[1]: context base
+//
+// Output:
+//   CC_SCRATCH[0]:
+//          31:31: set to signal completion
+//   CC_SCRATCH[1]:
+//           31:0: GPC context size
+//
+init:
+       clear b32 $r0
+       mov $sp $r0
+
+       // enable fifo access
+       mov $r1 0x1200
+       mov $r2 2
+       iowr I[$r1 + 0x000] $r2         // FIFO_ENABLE
+
+       // setup i0 handler, and route all interrupts to it
+       mov $r1 #ih
+       mov $iv0 $r1
+       mov $r1 0x400
+       iowr I[$r1 + 0x300] $r0         // INTR_DISPATCH
+
+       // enable fifo interrupt
+       mov $r2 4
+       iowr I[$r1 + 0x000] $r2         // INTR_EN_SET
+
+       // enable interrupts
+       bset $flags ie0
+
+       // figure out which GPC we are, and how many TPCs we have
+       mov $r1 0x608
+       shl b32 $r1 6
+       iord $r2 I[$r1 + 0x000]         // UNITS
+       mov $r3 1
+       and $r2 0x1f
+       shl b32 $r3 $r2
+       sub b32 $r3 1
+       st b32 D[$r0 + #tpc_count] $r2
+       st b32 D[$r0 + #tpc_mask] $r3
+       add b32 $r1 0x400
+       iord $r2 I[$r1 + 0x000]         // MYINDEX
+       st b32 D[$r0 + #gpc_id] $r2
+
+#if NV_PGRAPH_GPCX_UNK__SIZE > 0
+       // figure out which, and how many, UNKs are actually present
+       mov $r14 0x0c30
+       sethi $r14 0x500000
+       clear b32 $r2
+       clear b32 $r3
+       clear b32 $r4
+       init_unk_loop:
+               call #nv_rd32
+               cmp b32 $r15 0
+               bra z #init_unk_next
+                       mov $r15 1
+                       shl b32 $r15 $r2
+                       or $r4 $r15
+                       add b32 $r3 1
+               init_unk_next:
+               add b32 $r2 1
+               add b32 $r14 4
+               cmp b32 $r2 NV_PGRAPH_GPCX_UNK__SIZE
+               bra ne #init_unk_loop
+       init_unk_done:
+       st b32 D[$r0 + #unk_count] $r3
+       st b32 D[$r0 + #unk_mask] $r4
+#endif
+
+       // initialise context base, and size tracking
+       nv_iord($r2, NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(1), 0)
+       clear b32 $r3           // track GPC context size here
+
+       // set mmctx base addresses now so we don't have to do it later,
+       // they don't currently ever change
+       mov $r4 0x700
+       shl b32 $r4 6
+       shr b32 $r5 $r2 8
+       iowr I[$r4 + 0x000] $r5         // MMCTX_SAVE_SWBASE
+       iowr I[$r4 + 0x100] $r5         // MMCTX_LOAD_SWBASE
+
+       // calculate GPC mmio context size
+       ld b32 $r14 D[$r0 + #gpc_mmio_list_head]
+       ld b32 $r15 D[$r0 + #gpc_mmio_list_tail]
+       call #mmctx_size
+       add b32 $r2 $r15
+       add b32 $r3 $r15
+
+       // calculate per-TPC mmio context size
+       ld b32 $r14 D[$r0 + #tpc_mmio_list_head]
+       ld b32 $r15 D[$r0 + #tpc_mmio_list_tail]
+       call #mmctx_size
+       ld b32 $r14 D[$r0 + #tpc_count]
+       mulu $r14 $r15
+       add b32 $r2 $r14
+       add b32 $r3 $r14
+
+#if NV_PGRAPH_GPCX_UNK__SIZE > 0
+       // calculate per-UNK mmio context size
+       ld b32 $r14 D[$r0 + #unk_mmio_list_head]
+       ld b32 $r15 D[$r0 + #unk_mmio_list_tail]
+       call #mmctx_size
+       ld b32 $r14 D[$r0 + #unk_count]
+       mulu $r14 $r15
+       add b32 $r2 $r14
+       add b32 $r3 $r14
+#endif
+
+       // round up base/size to 256 byte boundary (for strand SWBASE)
+       add b32 $r4 0x1300
+       shr b32 $r3 2
+       iowr I[$r4 + 0x000] $r3         // MMCTX_LOAD_COUNT, wtf for?!?
+       shr b32 $r2 8
+       shr b32 $r3 6
+       add b32 $r2 1
+       add b32 $r3 1
+       shl b32 $r2 8
+       shl b32 $r3 8
+
+       // calculate size of strand context data
+       mov b32 $r15 $r2
+       call #strand_ctx_init
+       add b32 $r3 $r15
+
+       // save context size, and tell HUB we're done
+       nv_iowr(NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(1), 0, $r3)
+       clear b32 $r2
+       bset $r2 31
+       nv_iowr(NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_SET(0), 0, $r2)
+
+// Main program loop, very simple, sleeps until woken up by the interrupt
+// handler, pulls a command from the queue and executes its handler
+//
+main:
+       bset $flags $p0
+       sleep $p0
+       mov $r13 #cmd_queue
+       call #queue_get
+       bra $p1 #main
+
+       // 0x0000-0x0003 are all context transfers
+       cmpu b32 $r14 0x04
+       bra nc #main_not_ctx_xfer
+               // fetch $flags and mask off $p1/$p2
+               mov $r1 $flags
+               mov $r2 0x0006
+               not b32 $r2
+               and $r1 $r2
+               // set $p1/$p2 according to transfer type
+               shl b32 $r14 1
+               or $r1 $r14
+               mov $flags $r1
+               // transfer context data
+               call #ctx_xfer
+               bra #main
+
+       main_not_ctx_xfer:
+       shl b32 $r15 $r14 16
+       or $r15 E_BAD_COMMAND
+       call #error
+       bra #main
+
+// interrupt handler
+ih:
+       push $r8
+       mov $r8 $flags
+       push $r8
+       push $r9
+       push $r10
+       push $r11
+       push $r13
+       push $r14
+       push $r15
+       clear b32 $r0
+
+       // incoming fifo command?
+       iord $r10 I[$r0 + 0x200]        // INTR
+       and $r11 $r10 0x00000004
+       bra e #ih_no_fifo
+               // queue incoming fifo command for later processing
+               mov $r11 0x1900
+               mov $r13 #cmd_queue
+               iord $r14 I[$r11 + 0x100]       // FIFO_CMD
+               iord $r15 I[$r11 + 0x000]       // FIFO_DATA
+               call #queue_put
+               add b32 $r11 0x400
+               mov $r14 1
+               iowr I[$r11 + 0x000] $r14       // FIFO_ACK
+
+       // ack, and wake up main()
+       ih_no_fifo:
+       iowr I[$r0 + 0x100] $r10        // INTR_ACK
+
+       pop $r15
+       pop $r14
+       pop $r13
+       pop $r11
+       pop $r10
+       pop $r9
+       pop $r8
+       mov $flags $r8
+       pop $r8
+       bclr $flags $p0
+       iret
+
+// Set this GPC's bit in HUB_BAR, used to signal completion of various
+// activities to the HUB fuc
+//
+hub_barrier_done:
+       mov $r15 1
+       ld b32 $r14 D[$r0 + #gpc_id]
+       shl b32 $r15 $r14
+       mov $r14 -0x6be8        // 0x409418 - HUB_BAR_SET
+       sethi $r14 0x400000
+       call #nv_wr32
+       ret
+
+// Disables various things, waits a bit, and re-enables them..
+//
+// Not sure how exactly this helps, perhaps "ENABLE" is not such a
+// good description for the bits we turn off?  Anyways, without this,
+// funny things happen.
+//
+ctx_redswitch:
+       mov $r14 0x614
+       shl b32 $r14 6
+       mov $r15 0x020
+       iowr I[$r14] $r15       // GPC_RED_SWITCH = POWER
+       mov $r15 8
+       ctx_redswitch_delay:
+               sub b32 $r15 1
+               bra ne #ctx_redswitch_delay
+       mov $r15 0xa20
+       iowr I[$r14] $r15       // GPC_RED_SWITCH = UNK11, ENABLE, POWER
+       ret
+
+// Transfer GPC context data between GPU and storage area
+//
+// In: $r15 context base address
+//     $p1 clear on save, set on load
+//     $p2 set if opposite direction done/will be done, so:
+//             on save it means: "a load will follow this save"
+//             on load it means: "a save preceeded this load"
+//
+ctx_xfer:
+       // set context base address
+       mov $r1 0xa04
+       shl b32 $r1 6
+       iowr I[$r1 + 0x000] $r15// MEM_BASE
+       bra not $p1 #ctx_xfer_not_load
+               call #ctx_redswitch
+       ctx_xfer_not_load:
+
+       // strands
+       mov $r1 0x4afc
+       sethi $r1 0x20000
+       mov $r2 0xc
+       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x0c
+       call #strand_wait
+       mov $r2 0x47fc
+       sethi $r2 0x20000
+       iowr I[$r2] $r0         // STRAND_FIRST_GENE(0x3f) = 0x00
+       xbit $r2 $flags $p1
+       add b32 $r2 3
+       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD)
+
+       // mmio context
+       xbit $r10 $flags $p1    // direction
+       or $r10 2               // first
+       mov $r11 0x0000
+       sethi $r11 0x500000
+       ld b32 $r12 D[$r0 + #gpc_id]
+       shl b32 $r12 15
+       add b32 $r11 $r12       // base = NV_PGRAPH_GPCn
+       ld b32 $r12 D[$r0 + #gpc_mmio_list_head]
+       ld b32 $r13 D[$r0 + #gpc_mmio_list_tail]
+       mov $r14 0              // not multi
+       call #mmctx_xfer
+
+       // per-TPC mmio context
+       xbit $r10 $flags $p1    // direction
+#if !NV_PGRAPH_GPCX_UNK__SIZE
+       or $r10 4               // last
+#endif
+       mov $r11 0x4000
+       sethi $r11 0x500000     // base = NV_PGRAPH_GPC0_TPC0
+       ld b32 $r12 D[$r0 + #gpc_id]
+       shl b32 $r12 15
+       add b32 $r11 $r12       // base = NV_PGRAPH_GPCn_TPC0
+       ld b32 $r12 D[$r0 + #tpc_mmio_list_head]
+       ld b32 $r13 D[$r0 + #tpc_mmio_list_tail]
+       ld b32 $r15 D[$r0 + #tpc_mask]
+       mov $r14 0x800          // stride = 0x800
+       call #mmctx_xfer
+
+#if NV_PGRAPH_GPCX_UNK__SIZE > 0
+       // per-UNK mmio context
+       xbit $r10 $flags $p1    // direction
+       or $r10 4               // last
+       mov $r11 0x3000
+       sethi $r11 0x500000     // base = NV_PGRAPH_GPC0_UNK0
+       ld b32 $r12 D[$r0 + #gpc_id]
+       shl b32 $r12 15
+       add b32 $r11 $r12       // base = NV_PGRAPH_GPCn_UNK0
+       ld b32 $r12 D[$r0 + #unk_mmio_list_head]
+       ld b32 $r13 D[$r0 + #unk_mmio_list_tail]
+       ld b32 $r15 D[$r0 + #unk_mask]
+       mov $r14 0x200          // stride = 0x200
+       call #mmctx_xfer
+#endif
+
+       // wait for strands to finish
+       call #strand_wait
+
+       // if load, or a save without a load following, do some
+       // unknown stuff that's done after finishing a block of
+       // strand commands
+       bra $p1 #ctx_xfer_post
+       bra not $p2 #ctx_xfer_done
+       ctx_xfer_post:
+               mov $r1 0x4afc
+               sethi $r1 0x20000
+               mov $r2 0xd
+               iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x0d
+               call #strand_wait
+
+       // mark completion in HUB's barrier
+       ctx_xfer_done:
+       call #hub_barrier_done
+       ret
+#endif
index f7055af..5ae06a2 100644 (file)
@@ -1,6 +1,5 @@
-/* fuc microcode for nvc0 PGRAPH/GPC
- *
- * Copyright 2011 Red Hat Inc.
+/*
+ * 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"),
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  * OTHER DEALINGS IN THE SOFTWARE.
  *
- * Authors: Ben Skeggs
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
  */
 
-/* To build:
- *    m4 gpcnvc0.fuc | envyas -a -w -m fuc -V fuc3 -o gpcnvc0.fuc.h
- */
+#define NV_PGRAPH_GPCX_UNK__SIZE                                     0x00000000
 
-/* TODO
- * - bracket certain functions with scratch writes, useful for debugging
- * - watchdog timer around ctx operations
- */
+#define CHIPSET GF100
+#include "macros.fuc"
 
 .section #nvc0_grgpc_data
-include(`nvc0.fuc')
-gpc_id:                        .b32 0
-gpc_mmio_list_head:    .b32 0
-gpc_mmio_list_tail:    .b32 0
-
-tpc_count:             .b32 0
-tpc_mask:              .b32 0
-tpc_mmio_list_head:    .b32 0
-tpc_mmio_list_tail:    .b32 0
-
-cmd_queue:             queue_init
-
-// chipset descriptions
-chipsets:
-.b8  0xc0 0 0 0
-.b16 #nvc0_gpc_mmio_head
-.b16 #nvc0_gpc_mmio_tail
-.b16 #nvc0_tpc_mmio_head
-.b16 #nvc0_tpc_mmio_tail
-.b8  0xc1 0 0 0
-.b16 #nvc0_gpc_mmio_head
-.b16 #nvc1_gpc_mmio_tail
-.b16 #nvc0_tpc_mmio_head
-.b16 #nvc1_tpc_mmio_tail
-.b8  0xc3 0 0 0
-.b16 #nvc0_gpc_mmio_head
-.b16 #nvc0_gpc_mmio_tail
-.b16 #nvc0_tpc_mmio_head
-.b16 #nvc3_tpc_mmio_tail
-.b8  0xc4 0 0 0
-.b16 #nvc0_gpc_mmio_head
-.b16 #nvc0_gpc_mmio_tail
-.b16 #nvc0_tpc_mmio_head
-.b16 #nvc3_tpc_mmio_tail
-.b8  0xc8 0 0 0
-.b16 #nvc0_gpc_mmio_head
-.b16 #nvc0_gpc_mmio_tail
-.b16 #nvc0_tpc_mmio_head
-.b16 #nvc0_tpc_mmio_tail
-.b8  0xce 0 0 0
-.b16 #nvc0_gpc_mmio_head
-.b16 #nvc0_gpc_mmio_tail
-.b16 #nvc0_tpc_mmio_head
-.b16 #nvc3_tpc_mmio_tail
-.b8  0xcf 0 0 0
-.b16 #nvc0_gpc_mmio_head
-.b16 #nvc0_gpc_mmio_tail
-.b16 #nvc0_tpc_mmio_head
-.b16 #nvcf_tpc_mmio_tail
-.b8  0xd9 0 0 0
-.b16 #nvd9_gpc_mmio_head
-.b16 #nvd9_gpc_mmio_tail
-.b16 #nvd9_tpc_mmio_head
-.b16 #nvd9_tpc_mmio_tail
-.b8  0xd7 0 0 0
-.b16 #nvd9_gpc_mmio_head
-.b16 #nvd9_gpc_mmio_tail
-.b16 #nvd9_tpc_mmio_head
-.b16 #nvd9_tpc_mmio_tail
-.b8  0 0 0 0
-
-// GPC mmio lists
-nvc0_gpc_mmio_head:
-mmctx_data(0x000380, 1)
-mmctx_data(0x000400, 6)
-mmctx_data(0x000450, 9)
-mmctx_data(0x000600, 1)
-mmctx_data(0x000684, 1)
-mmctx_data(0x000700, 5)
-mmctx_data(0x000800, 1)
-mmctx_data(0x000808, 3)
-mmctx_data(0x000828, 1)
-mmctx_data(0x000830, 1)
-mmctx_data(0x0008d8, 1)
-mmctx_data(0x0008e0, 1)
-mmctx_data(0x0008e8, 6)
-mmctx_data(0x00091c, 1)
-mmctx_data(0x000924, 3)
-mmctx_data(0x000b00, 1)
-mmctx_data(0x000b08, 6)
-mmctx_data(0x000bb8, 1)
-mmctx_data(0x000c08, 1)
-mmctx_data(0x000c10, 8)
-mmctx_data(0x000c80, 1)
-mmctx_data(0x000c8c, 1)
-mmctx_data(0x001000, 3)
-mmctx_data(0x001014, 1)
-nvc0_gpc_mmio_tail:
-mmctx_data(0x000c6c, 1);
-nvc1_gpc_mmio_tail:
-
-nvd9_gpc_mmio_head:
-mmctx_data(0x000380, 1)
-mmctx_data(0x000400, 2)
-mmctx_data(0x00040c, 3)
-mmctx_data(0x000450, 9)
-mmctx_data(0x000600, 1)
-mmctx_data(0x000684, 1)
-mmctx_data(0x000700, 5)
-mmctx_data(0x000800, 1)
-mmctx_data(0x000808, 3)
-mmctx_data(0x000828, 1)
-mmctx_data(0x000830, 1)
-mmctx_data(0x0008d8, 1)
-mmctx_data(0x0008e0, 1)
-mmctx_data(0x0008e8, 6)
-mmctx_data(0x00091c, 1)
-mmctx_data(0x000924, 3)
-mmctx_data(0x000b00, 1)
-mmctx_data(0x000b08, 6)
-mmctx_data(0x000bb8, 1)
-mmctx_data(0x000c08, 1)
-mmctx_data(0x000c10, 8)
-mmctx_data(0x000c6c, 1)
-mmctx_data(0x000c80, 1)
-mmctx_data(0x000c8c, 1)
-mmctx_data(0x001000, 3)
-mmctx_data(0x001014, 1)
-nvd9_gpc_mmio_tail:
-
-// TPC mmio lists
-nvc0_tpc_mmio_head:
-mmctx_data(0x000018, 1)
-mmctx_data(0x00003c, 1)
-mmctx_data(0x000048, 1)
-mmctx_data(0x000064, 1)
-mmctx_data(0x000088, 1)
-mmctx_data(0x000200, 6)
-mmctx_data(0x00021c, 2)
-mmctx_data(0x000300, 6)
-mmctx_data(0x0003d0, 1)
-mmctx_data(0x0003e0, 2)
-mmctx_data(0x000400, 3)
-mmctx_data(0x000420, 1)
-mmctx_data(0x0004b0, 1)
-mmctx_data(0x0004e8, 1)
-mmctx_data(0x0004f4, 1)
-mmctx_data(0x000520, 2)
-mmctx_data(0x000604, 4)
-mmctx_data(0x000644, 20)
-mmctx_data(0x000698, 1)
-mmctx_data(0x000750, 2)
-nvc0_tpc_mmio_tail:
-mmctx_data(0x000758, 1)
-mmctx_data(0x0002c4, 1)
-mmctx_data(0x0006e0, 1)
-nvcf_tpc_mmio_tail:
-mmctx_data(0x0004bc, 1)
-nvc3_tpc_mmio_tail:
-mmctx_data(0x000544, 1)
-nvc1_tpc_mmio_tail:
-
-nvd9_tpc_mmio_head:
-mmctx_data(0x000018, 1)
-mmctx_data(0x00003c, 1)
-mmctx_data(0x000048, 1)
-mmctx_data(0x000064, 1)
-mmctx_data(0x000088, 1)
-mmctx_data(0x000200, 6)
-mmctx_data(0x00021c, 2)
-mmctx_data(0x0002c4, 1)
-mmctx_data(0x000300, 6)
-mmctx_data(0x0003d0, 1)
-mmctx_data(0x0003e0, 2)
-mmctx_data(0x000400, 3)
-mmctx_data(0x000420, 3)
-mmctx_data(0x0004b0, 1)
-mmctx_data(0x0004e8, 1)
-mmctx_data(0x0004f4, 1)
-mmctx_data(0x000520, 2)
-mmctx_data(0x000544, 1)
-mmctx_data(0x000604, 4)
-mmctx_data(0x000644, 20)
-mmctx_data(0x000698, 1)
-mmctx_data(0x0006e0, 1)
-mmctx_data(0x000750, 3)
-nvd9_tpc_mmio_tail:
+#define INCLUDE_DATA
+#include "com.fuc"
+#include "gpc.fuc"
+#undef INCLUDE_DATA
 
 .section #nvc0_grgpc_code
+#define INCLUDE_CODE
 bra #init
-define(`include_code')
-include(`nvc0.fuc')
-
-// reports an exception to the host
-//
-// In: $r15 error code (see nvc0.fuc)
-//
-error:
-       push $r14
-       mov $r14 -0x67ec        // 0x9814
-       sethi $r14 0x400000
-       call #nv_wr32           // HUB_CTXCTL_CC_SCRATCH[5] = error code
-       add b32 $r14 0x41c
-       mov $r15 1
-       call #nv_wr32           // HUB_CTXCTL_INTR_UP_SET
-       pop $r14
-       ret
-
-// GPC fuc initialisation, executed by triggering ucode start, will
-// fall through to main loop after completion.
-//
-// Input:
-//   CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh)
-//   CC_SCRATCH[1]: context base
-//
-// Output:
-//   CC_SCRATCH[0]:
-//          31:31: set to signal completion
-//   CC_SCRATCH[1]:
-//           31:0: GPC context size
-//
-init:
-       clear b32 $r0
-       mov $sp $r0
-
-       // enable fifo access
-       mov $r1 0x1200
-       mov $r2 2
-       iowr I[$r1 + 0x000] $r2         // FIFO_ENABLE
-
-       // setup i0 handler, and route all interrupts to it
-       mov $r1 #ih
-       mov $iv0 $r1
-       mov $r1 0x400
-       iowr I[$r1 + 0x300] $r0         // INTR_DISPATCH
-
-       // enable fifo interrupt
-       mov $r2 4
-       iowr I[$r1 + 0x000] $r2         // INTR_EN_SET
-
-       // enable interrupts
-       bset $flags ie0
-
-       // figure out which GPC we are, and how many TPCs we have
-       mov $r1 0x608
-       shl b32 $r1 6
-       iord $r2 I[$r1 + 0x000]         // UNITS
-       mov $r3 1
-       and $r2 0x1f
-       shl b32 $r3 $r2
-       sub b32 $r3 1
-       st b32 D[$r0 + #tpc_count] $r2
-       st b32 D[$r0 + #tpc_mask] $r3
-       add b32 $r1 0x400
-       iord $r2 I[$r1 + 0x000]         // MYINDEX
-       st b32 D[$r0 + #gpc_id] $r2
-
-       // find context data for this chipset
-       mov $r2 0x800
-       shl b32 $r2 6
-       iord $r2 I[$r2 + 0x000]         // CC_SCRATCH[0]
-       mov $r1 #chipsets - 12
-       init_find_chipset:
-               add b32 $r1 12
-               ld b32 $r3 D[$r1 + 0x00]
-               cmpu b32 $r3 $r2
-               bra e #init_context
-               cmpu b32 $r3 0
-               bra ne #init_find_chipset
-               // unknown chipset
-               ret
-
-       // initialise context base, and size tracking
-       init_context:
-       mov $r2 0x800
-       shl b32 $r2 6
-       iord $r2 I[$r2 + 0x100] // CC_SCRATCH[1], initial base
-       clear b32 $r3           // track GPC context size here
-
-       // set mmctx base addresses now so we don't have to do it later,
-       // they don't currently ever change
-       mov $r4 0x700
-       shl b32 $r4 6
-       shr b32 $r5 $r2 8
-       iowr I[$r4 + 0x000] $r5         // MMCTX_SAVE_SWBASE
-       iowr I[$r4 + 0x100] $r5         // MMCTX_LOAD_SWBASE
-
-       // calculate GPC mmio context size, store the chipset-specific
-       // mmio list pointers somewhere we can get at them later without
-       // re-parsing the chipset list
-       clear b32 $r14
-       clear b32 $r15
-       ld b16 $r14 D[$r1 + 4]
-       ld b16 $r15 D[$r1 + 6]
-       st b16 D[$r0 + #gpc_mmio_list_head] $r14
-       st b16 D[$r0 + #gpc_mmio_list_tail] $r15
-       call #mmctx_size
-       add b32 $r2 $r15
-       add b32 $r3 $r15
-
-       // calculate per-TPC mmio context size, store the list pointers
-       ld b16 $r14 D[$r1 + 8]
-       ld b16 $r15 D[$r1 + 10]
-       st b16 D[$r0 + #tpc_mmio_list_head] $r14
-       st b16 D[$r0 + #tpc_mmio_list_tail] $r15
-       call #mmctx_size
-       ld b32 $r14 D[$r0 + #tpc_count]
-       mulu $r14 $r15
-       add b32 $r2 $r14
-       add b32 $r3 $r14
-
-       // round up base/size to 256 byte boundary (for strand SWBASE)
-       add b32 $r4 0x1300
-       shr b32 $r3 2
-       iowr I[$r4 + 0x000] $r3         // MMCTX_LOAD_COUNT, wtf for?!?
-       shr b32 $r2 8
-       shr b32 $r3 6
-       add b32 $r2 1
-       add b32 $r3 1
-       shl b32 $r2 8
-       shl b32 $r3 8
-
-       // calculate size of strand context data
-       mov b32 $r15 $r2
-       call #strand_ctx_init
-       add b32 $r3 $r15
-
-       // save context size, and tell HUB we're done
-       mov $r1 0x800
-       shl b32 $r1 6
-       iowr I[$r1 + 0x100] $r3         // CC_SCRATCH[1]  = context size
-       add b32 $r1 0x800
-       clear b32 $r2
-       bset $r2 31
-       iowr I[$r1 + 0x000] $r2         // CC_SCRATCH[0] |= 0x80000000
-
-// Main program loop, very simple, sleeps until woken up by the interrupt
-// handler, pulls a command from the queue and executes its handler
-//
-main:
-       bset $flags $p0
-       sleep $p0
-       mov $r13 #cmd_queue
-       call #queue_get
-       bra $p1 #main
-
-       // 0x0000-0x0003 are all context transfers
-       cmpu b32 $r14 0x04
-       bra nc #main_not_ctx_xfer
-               // fetch $flags and mask off $p1/$p2
-               mov $r1 $flags
-               mov $r2 0x0006
-               not b32 $r2
-               and $r1 $r2
-               // set $p1/$p2 according to transfer type
-               shl b32 $r14 1
-               or $r1 $r14
-               mov $flags $r1
-               // transfer context data
-               call #ctx_xfer
-               bra #main
-
-       main_not_ctx_xfer:
-       shl b32 $r15 $r14 16
-       or $r15 E_BAD_COMMAND
-       call #error
-       bra #main
-
-// interrupt handler
-ih:
-       push $r8
-       mov $r8 $flags
-       push $r8
-       push $r9
-       push $r10
-       push $r11
-       push $r13
-       push $r14
-       push $r15
-
-       // incoming fifo command?
-       iord $r10 I[$r0 + 0x200]        // INTR
-       and $r11 $r10 0x00000004
-       bra e #ih_no_fifo
-               // queue incoming fifo command for later processing
-               mov $r11 0x1900
-               mov $r13 #cmd_queue
-               iord $r14 I[$r11 + 0x100]       // FIFO_CMD
-               iord $r15 I[$r11 + 0x000]       // FIFO_DATA
-               call #queue_put
-               add b32 $r11 0x400
-               mov $r14 1
-               iowr I[$r11 + 0x000] $r14       // FIFO_ACK
-
-       // ack, and wake up main()
-       ih_no_fifo:
-       iowr I[$r0 + 0x100] $r10        // INTR_ACK
-
-       pop $r15
-       pop $r14
-       pop $r13
-       pop $r11
-       pop $r10
-       pop $r9
-       pop $r8
-       mov $flags $r8
-       pop $r8
-       bclr $flags $p0
-       iret
-
-// Set this GPC's bit in HUB_BAR, used to signal completion of various
-// activities to the HUB fuc
-//
-hub_barrier_done:
-       mov $r15 1
-       ld b32 $r14 D[$r0 + #gpc_id]
-       shl b32 $r15 $r14
-       mov $r14 -0x6be8        // 0x409418 - HUB_BAR_SET
-       sethi $r14 0x400000
-       call #nv_wr32
-       ret
-
-// Disables various things, waits a bit, and re-enables them..
-//
-// Not sure how exactly this helps, perhaps "ENABLE" is not such a
-// good description for the bits we turn off?  Anyways, without this,
-// funny things happen.
-//
-ctx_redswitch:
-       mov $r14 0x614
-       shl b32 $r14 6
-       mov $r15 0x020
-       iowr I[$r14] $r15       // GPC_RED_SWITCH = POWER
-       mov $r15 8
-       ctx_redswitch_delay:
-               sub b32 $r15 1
-               bra ne #ctx_redswitch_delay
-       mov $r15 0xa20
-       iowr I[$r14] $r15       // GPC_RED_SWITCH = UNK11, ENABLE, POWER
-       ret
-
-// Transfer GPC context data between GPU and storage area
-//
-// In: $r15 context base address
-//     $p1 clear on save, set on load
-//     $p2 set if opposite direction done/will be done, so:
-//             on save it means: "a load will follow this save"
-//             on load it means: "a save preceeded this load"
-//
-ctx_xfer:
-       // set context base address
-       mov $r1 0xa04
-       shl b32 $r1 6
-       iowr I[$r1 + 0x000] $r15// MEM_BASE
-       bra not $p1 #ctx_xfer_not_load
-               call #ctx_redswitch
-       ctx_xfer_not_load:
-
-       // strands
-       mov $r1 0x4afc
-       sethi $r1 0x20000
-       mov $r2 0xc
-       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x0c
-       call #strand_wait
-       mov $r2 0x47fc
-       sethi $r2 0x20000
-       iowr I[$r2] $r0         // STRAND_FIRST_GENE(0x3f) = 0x00
-       xbit $r2 $flags $p1
-       add b32 $r2 3
-       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD)
-
-       // mmio context
-       xbit $r10 $flags $p1    // direction
-       or $r10 2               // first
-       mov $r11 0x0000
-       sethi $r11 0x500000
-       ld b32 $r12 D[$r0 + #gpc_id]
-       shl b32 $r12 15
-       add b32 $r11 $r12       // base = NV_PGRAPH_GPCn
-       ld b32 $r12 D[$r0 + #gpc_mmio_list_head]
-       ld b32 $r13 D[$r0 + #gpc_mmio_list_tail]
-       mov $r14 0              // not multi
-       call #mmctx_xfer
-
-       // per-TPC mmio context
-       xbit $r10 $flags $p1    // direction
-       or $r10 4               // last
-       mov $r11 0x4000
-       sethi $r11 0x500000     // base = NV_PGRAPH_GPC0_TPC0
-       ld b32 $r12 D[$r0 + #gpc_id]
-       shl b32 $r12 15
-       add b32 $r11 $r12       // base = NV_PGRAPH_GPCn_TPC0
-       ld b32 $r12 D[$r0 + #tpc_mmio_list_head]
-       ld b32 $r13 D[$r0 + #tpc_mmio_list_tail]
-       ld b32 $r15 D[$r0 + #tpc_mask]
-       mov $r14 0x800          // stride = 0x800
-       call #mmctx_xfer
-
-       // wait for strands to finish
-       call #strand_wait
-
-       // if load, or a save without a load following, do some
-       // unknown stuff that's done after finishing a block of
-       // strand commands
-       bra $p1 #ctx_xfer_post
-       bra not $p2 #ctx_xfer_done
-       ctx_xfer_post:
-               mov $r1 0x4afc
-               sethi $r1 0x20000
-               mov $r2 0xd
-               iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x0d
-               call #strand_wait
-
-       // mark completion in HUB's barrier
-       ctx_xfer_done:
-       call #hub_barrier_done
-       ret
-
+#include "com.fuc"
+#include "gpc.fuc"
 .align 256
+#undef INCLUDE_CODE
index 96050dd..f2b0dea 100644 (file)
@@ -1,17 +1,19 @@
 uint32_t nvc0_grgpc_data[] = {
-/* 0x0000: gpc_id */
-       0x00000000,
-/* 0x0004: gpc_mmio_list_head */
-       0x00000000,
-/* 0x0008: gpc_mmio_list_tail */
-       0x00000000,
-/* 0x000c: tpc_count */
-       0x00000000,
-/* 0x0010: tpc_mask */
+/* 0x0000: gpc_mmio_list_head */
+       0x00000064,
+/* 0x0004: gpc_mmio_list_tail */
+/* 0x0004: tpc_mmio_list_head */
+       0x00000064,
+/* 0x0008: tpc_mmio_list_tail */
+/* 0x0008: unk_mmio_list_head */
+       0x00000064,
+/* 0x000c: unk_mmio_list_tail */
+       0x00000064,
+/* 0x0010: gpc_id */
        0x00000000,
-/* 0x0014: tpc_mmio_list_head */
+/* 0x0014: tpc_count */
        0x00000000,
-/* 0x0018: tpc_mmio_list_tail */
+/* 0x0018: tpc_mask */
        0x00000000,
 /* 0x001c: cmd_queue */
        0x00000000,
@@ -32,153 +34,17 @@ uint32_t nvc0_grgpc_data[] = {
        0x00000000,
        0x00000000,
        0x00000000,
-/* 0x0064: chipsets */
-       0x000000c0,
-       0x012800c8,
-       0x01e40194,
-       0x000000c1,
-       0x012c00c8,
-       0x01f80194,
-       0x000000c3,
-       0x012800c8,
-       0x01f40194,
-       0x000000c4,
-       0x012800c8,
-       0x01f40194,
-       0x000000c8,
-       0x012800c8,
-       0x01e40194,
-       0x000000ce,
-       0x012800c8,
-       0x01f40194,
-       0x000000cf,
-       0x012800c8,
-       0x01f00194,
-       0x000000d9,
-       0x0194012c,
-       0x025401f8,
-       0x00000000,
-/* 0x00c8: nvc0_gpc_mmio_head */
-       0x00000380,
-       0x14000400,
-       0x20000450,
-       0x00000600,
-       0x00000684,
-       0x10000700,
-       0x00000800,
-       0x08000808,
-       0x00000828,
-       0x00000830,
-       0x000008d8,
-       0x000008e0,
-       0x140008e8,
-       0x0000091c,
-       0x08000924,
-       0x00000b00,
-       0x14000b08,
-       0x00000bb8,
-       0x00000c08,
-       0x1c000c10,
-       0x00000c80,
-       0x00000c8c,
-       0x08001000,
-       0x00001014,
-/* 0x0128: nvc0_gpc_mmio_tail */
-       0x00000c6c,
-/* 0x012c: nvc1_gpc_mmio_tail */
-/* 0x012c: nvd9_gpc_mmio_head */
-       0x00000380,
-       0x04000400,
-       0x0800040c,
-       0x20000450,
-       0x00000600,
-       0x00000684,
-       0x10000700,
-       0x00000800,
-       0x08000808,
-       0x00000828,
-       0x00000830,
-       0x000008d8,
-       0x000008e0,
-       0x140008e8,
-       0x0000091c,
-       0x08000924,
-       0x00000b00,
-       0x14000b08,
-       0x00000bb8,
-       0x00000c08,
-       0x1c000c10,
-       0x00000c6c,
-       0x00000c80,
-       0x00000c8c,
-       0x08001000,
-       0x00001014,
-/* 0x0194: nvd9_gpc_mmio_tail */
-/* 0x0194: nvc0_tpc_mmio_head */
-       0x00000018,
-       0x0000003c,
-       0x00000048,
-       0x00000064,
-       0x00000088,
-       0x14000200,
-       0x0400021c,
-       0x14000300,
-       0x000003d0,
-       0x040003e0,
-       0x08000400,
-       0x00000420,
-       0x000004b0,
-       0x000004e8,
-       0x000004f4,
-       0x04000520,
-       0x0c000604,
-       0x4c000644,
-       0x00000698,
-       0x04000750,
-/* 0x01e4: nvc0_tpc_mmio_tail */
-       0x00000758,
-       0x000002c4,
-       0x000006e0,
-/* 0x01f0: nvcf_tpc_mmio_tail */
-       0x000004bc,
-/* 0x01f4: nvc3_tpc_mmio_tail */
-       0x00000544,
-/* 0x01f8: nvc1_tpc_mmio_tail */
-/* 0x01f8: nvd9_tpc_mmio_head */
-       0x00000018,
-       0x0000003c,
-       0x00000048,
-       0x00000064,
-       0x00000088,
-       0x14000200,
-       0x0400021c,
-       0x000002c4,
-       0x14000300,
-       0x000003d0,
-       0x040003e0,
-       0x08000400,
-       0x08000420,
-       0x000004b0,
-       0x000004e8,
-       0x000004f4,
-       0x04000520,
-       0x00000544,
-       0x0c000604,
-       0x4c000644,
-       0x00000698,
-       0x000006e0,
-       0x08000750,
 };
 
 uint32_t nvc0_grgpc_code[] = {
-       0x03060ef5,
+       0x03180ef5,
 /* 0x0004: queue_put */
        0x9800d898,
        0x86f001d9,
        0x0489b808,
        0xf00c1bf4,
        0x21f502f7,
-       0x00f802ec,
+       0x00f802fe,
 /* 0x001c: queue_put_next */
        0xb60798c4,
        0x8dbb0384,
@@ -210,7 +76,7 @@ uint32_t nvc0_grgpc_code[] = {
        0xc800bccf,
        0x1bf41fcc,
        0x06a7f0fa,
-       0x010321f5,
+       0x010921f5,
        0xf840bfcf,
 /* 0x008d: nv_wr32 */
        0x28b7f100,
@@ -232,63 +98,66 @@ uint32_t nvc0_grgpc_code[] = {
        0x0684b604,
        0xf80080d0,
 /* 0x00c9: wait_donez */
-       0x3c87f100,
-       0x0684b608,
-       0x99f094bd,
-       0x0089d000,
-       0x081887f1,
-       0xd00684b6,
-/* 0x00e2: wait_done_wait_donez */
-       0x87f1008a,
-       0x84b60400,
-       0x0088cf06,
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x07f104bd,
+       0x03f00600,
+       0x000ad002,
+/* 0x00e6: wait_donez_ne */
+       0x87f104bd,
+       0x83f00000,
+       0x0088cf01,
        0xf4888aff,
-       0x87f1f31b,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00099,
-/* 0x0103: wait_doneo */
-       0xf100f800,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00099f0,
-       0x87f10089,
+       0x94bdf31b,
+       0xf10099f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0109: wait_doneo */
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x87f104bd,
        0x84b60818,
        0x008ad006,
-/* 0x011c: wait_done_wait_doneo */
+/* 0x0124: wait_doneo_e */
        0x040087f1,
        0xcf0684b6,
        0x8aff0088,
        0xf30bf488,
-       0x085c87f1,
-       0xbd0684b6,
-       0x0099f094,
-       0xf80089d0,
-/* 0x013d: mmctx_size */
-/* 0x013f: nv_mmctx_size_loop */
-       0x9894bd00,
-       0x85b600e8,
-       0x0180b61a,
-       0xbb0284b6,
-       0xe0b60098,
-       0x04efb804,
-       0xb9eb1bf4,
-       0x00f8029f,
-/* 0x015c: mmctx_xfer */
-       0x083c87f1,
-       0xbd0684b6,
-       0x0199f094,
-       0xf10089d0,
+       0x99f094bd,
+       0x0007f100,
+       0x0203f017,
+       0xbd0009d0,
+/* 0x0147: mmctx_size */
+       0xbd00f804,
+/* 0x0149: nv_mmctx_size_loop */
+       0x00e89894,
+       0xb61a85b6,
+       0x84b60180,
+       0x0098bb02,
+       0xb804e0b6,
+       0x1bf404ef,
+       0x029fb9eb,
+/* 0x0166: mmctx_xfer */
+       0x94bd00f8,
+       0xf10199f0,
+       0xf00f0007,
+       0x09d00203,
+       0xf104bd00,
        0xb6071087,
        0x94bd0684,
        0xf405bbfd,
        0x8bd0090b,
        0x0099f000,
-/* 0x0180: mmctx_base_disabled */
+/* 0x018c: mmctx_base_disabled */
        0xf405eefd,
        0x8ed00c0b,
        0xc08fd080,
-/* 0x018f: mmctx_multi_disabled */
+/* 0x019b: mmctx_multi_disabled */
        0xb70199f0,
        0xc8010080,
        0xb4b600ab,
@@ -296,8 +165,8 @@ uint32_t nvc0_grgpc_code[] = {
        0xb601aec8,
        0xbefd11e4,
        0x008bd005,
-/* 0x01a8: mmctx_exec_loop */
-/* 0x01a8: mmctx_wait_free */
+/* 0x01b4: mmctx_exec_loop */
+/* 0x01b4: mmctx_wait_free */
        0xf0008ecf,
        0x0bf41fe4,
        0x00ce98fa,
@@ -306,76 +175,77 @@ uint32_t nvc0_grgpc_code[] = {
        0x04cdb804,
        0xc8e81bf4,
        0x1bf402ab,
-/* 0x01c9: mmctx_fini_wait */
+/* 0x01d5: mmctx_fini_wait */
        0x008bcf18,
        0xb01fb4f0,
        0x1bf410b4,
        0x02a7f0f7,
        0xf4c921f4,
-/* 0x01de: mmctx_stop */
+/* 0x01ea: mmctx_stop */
        0xabc81b0e,
        0x10b4b600,
        0xf00cb9f0,
        0x8bd012b9,
-/* 0x01ed: mmctx_stop_wait */
+/* 0x01f9: mmctx_stop_wait */
        0x008bcf00,
        0xf412bbc8,
-/* 0x01f6: mmctx_done */
-       0x87f1fa1b,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00199,
-/* 0x0207: strand_wait */
-       0xf900f800,
-       0x02a7f0a0,
-       0xfcc921f4,
-/* 0x0213: strand_pre */
-       0xf100f8a0,
-       0xf04afc87,
-       0x97f00283,
-       0x0089d00c,
-       0x020721f5,
-/* 0x0226: strand_post */
-       0x87f100f8,
-       0x83f04afc,
-       0x0d97f002,
-       0xf50089d0,
-       0xf8020721,
-/* 0x0239: strand_set */
-       0xfca7f100,
-       0x02a3f04f,
-       0x0500aba2,
-       0xd00fc7f0,
-       0xc7f000ac,
-       0x00bcd00b,
-       0x020721f5,
-       0xf000aed0,
-       0xbcd00ac7,
-       0x0721f500,
-/* 0x0263: strand_ctx_init */
-       0xf100f802,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00399f0,
+/* 0x0202: mmctx_done */
+       0x94bdfa1b,
+       0xf10199f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0215: strand_wait */
+       0xf0a0f900,
+       0x21f402a7,
+       0xf8a0fcc9,
+/* 0x0221: strand_pre */
+       0xfc87f100,
+       0x0283f04a,
+       0xd00c97f0,
        0x21f50089,
-       0xe7f00213,
-       0x3921f503,
+       0x00f80215,
+/* 0x0234: strand_post */
+       0x4afc87f1,
+       0xf00283f0,
+       0x89d00d97,
+       0x1521f500,
+/* 0x0247: strand_set */
+       0xf100f802,
+       0xf04ffca7,
+       0xaba202a3,
+       0xc7f00500,
+       0x00acd00f,
+       0xd00bc7f0,
+       0x21f500bc,
+       0xaed00215,
+       0x0ac7f000,
+       0xf500bcd0,
+       0xf8021521,
+/* 0x0271: strand_ctx_init */
+       0xf094bd00,
+       0x07f10399,
+       0x03f00f00,
+       0x0009d002,
+       0x21f504bd,
+       0xe7f00221,
+       0x4721f503,
        0xfca7f102,
        0x02a3f046,
        0x0400aba0,
        0xf040a0d0,
        0xbcd001c7,
-       0x0721f500,
+       0x1521f500,
        0x010c9202,
        0xf000acd0,
        0xbcd002c7,
-       0x0721f500,
-       0x2621f502,
+       0x1521f500,
+       0x3421f502,
        0x8087f102,
        0x0684b608,
        0xb70089cf,
        0x95220080,
-/* 0x02ba: ctx_init_strand_loop */
+/* 0x02ca: ctx_init_strand_loop */
        0x8ed008fe,
        0x408ed000,
        0xb6808acf,
@@ -384,86 +254,74 @@ uint32_t nvc0_grgpc_code[] = {
        0xb60480b6,
        0x1bf40192,
        0x08e4b6e8,
-       0xf1f2efbc,
-       0xb6085c87,
-       0x94bd0684,
-       0xd00399f0,
-       0x00f80089,
-/* 0x02ec: error */
-       0xe7f1e0f9,
-       0xe3f09814,
-       0x8d21f440,
-       0x041ce0b7,
-       0xf401f7f0,
-       0xe0fc8d21,
-/* 0x0306: init */
-       0x04bd00f8,
-       0xf10004fe,
-       0xf0120017,
-       0x12d00227,
-       0x3e17f100,
-       0x0010fe04,
-       0x040017f1,
-       0xf0c010d0,
-       0x12d00427,
-       0x1031f400,
-       0x060817f1,
-       0xcf0614b6,
-       0x37f00012,
-       0x1f24f001,
-       0xb60432bb,
-       0x02800132,
-       0x04038003,
-       0x040010b7,
-       0x800012cf,
-       0x27f10002,
-       0x24b60800,
-       0x0022cf06,
-/* 0x035f: init_find_chipset */
-       0xb65817f0,
-       0x13980c10,
-       0x0432b800,
-       0xb00b0bf4,
-       0x1bf40034,
-/* 0x0373: init_context */
-       0xf100f8f1,
-       0xb6080027,
-       0x22cf0624,
-       0xf134bd40,
-       0xb6070047,
-       0x25950644,
-       0x0045d008,
-       0xbd4045d0,
-       0x58f4bde4,
-       0x1f58021e,
-       0x020e4003,
-       0xf5040f40,
-       0xbb013d21,
-       0x3fbb002f,
-       0x041e5800,
-       0x40051f58,
-       0x0f400a0e,
-       0x3d21f50c,
-       0x030e9801,
-       0xbb00effd,
-       0x3ebb002e,
-       0x0040b700,
-       0x0235b613,
-       0xb60043d0,
-       0x35b60825,
-       0x0120b606,
-       0xb60130b6,
-       0x34b60824,
-       0x022fb908,
-       0x026321f5,
-       0xf1003fbb,
-       0xb6080017,
-       0x13d00614,
-       0x0010b740,
-       0xf024bd08,
-       0x12d01f29,
-/* 0x0401: main */
-       0x0031f400,
+       0xbdf2efbc,
+       0x0399f094,
+       0x170007f1,
+       0xd00203f0,
+       0x04bd0009,
+/* 0x02fe: error */
+       0xe0f900f8,
+       0x9814e7f1,
+       0xf440e3f0,
+       0xe0b78d21,
+       0xf7f0041c,
+       0x8d21f401,
+       0x00f8e0fc,
+/* 0x0318: init */
+       0x04fe04bd,
+       0x0017f100,
+       0x0227f012,
+       0xf10012d0,
+       0xfe042617,
+       0x17f10010,
+       0x10d00400,
+       0x0427f0c0,
+       0xf40012d0,
+       0x17f11031,
+       0x14b60608,
+       0x0012cf06,
+       0xf00137f0,
+       0x32bb1f24,
+       0x0132b604,
+       0x80050280,
+       0x10b70603,
+       0x12cf0400,
+       0x04028000,
+       0x010027f1,
+       0xcf0223f0,
+       0x34bd0022,
+       0x070047f1,
+       0x950644b6,
+       0x45d00825,
+       0x4045d000,
+       0x98000e98,
+       0x21f5010f,
+       0x2fbb0147,
+       0x003fbb00,
+       0x98010e98,
+       0x21f5020f,
+       0x0e980147,
+       0x00effd05,
+       0xbb002ebb,
+       0x40b7003e,
+       0x35b61300,
+       0x0043d002,
+       0xb60825b6,
+       0x20b60635,
+       0x0130b601,
+       0xb60824b6,
+       0x2fb90834,
+       0x7121f502,
+       0x003fbb02,
+       0x010007f1,
+       0xd00203f0,
+       0x04bd0003,
+       0x29f024bd,
+       0x0007f11f,
+       0x0203f008,
+       0xbd0002d0,
+/* 0x03e9: main */
+       0x0031f404,
        0xf00028f4,
        0x21f41cd7,
        0xf401f439,
@@ -474,94 +332,100 @@ uint32_t nvc0_grgpc_code[] = {
        0x01e4b604,
        0xfe051efd,
        0x21f50018,
-       0x0ef404c3,
-/* 0x0431: main_not_ctx_xfer */
+       0x0ef404ad,
+/* 0x0419: main_not_ctx_xfer */
        0x10ef94d3,
        0xf501f5f0,
-       0xf402ec21,
-/* 0x043e: ih */
+       0xf402fe21,
+/* 0x0426: ih */
        0x80f9c60e,
        0xf90188fe,
        0xf990f980,
        0xf9b0f9a0,
        0xf9e0f9d0,
-       0x800acff0,
-       0xf404abc4,
-       0xb7f11d0b,
-       0xd7f01900,
-       0x40becf1c,
-       0xf400bfcf,
-       0xb0b70421,
-       0xe7f00400,
-       0x00bed001,
-/* 0x0474: ih_no_fifo */
-       0xfc400ad0,
-       0xfce0fcf0,
-       0xfcb0fcd0,
-       0xfc90fca0,
-       0x0088fe80,
-       0x32f480fc,
-/* 0x048f: hub_barrier_done */
-       0xf001f800,
-       0x0e9801f7,
-       0x04febb00,
-       0x9418e7f1,
-       0xf440e3f0,
-       0x00f88d21,
-/* 0x04a4: ctx_redswitch */
-       0x0614e7f1,
-       0xf006e4b6,
-       0xefd020f7,
-       0x08f7f000,
-/* 0x04b4: ctx_redswitch_delay */
-       0xf401f2b6,
-       0xf7f1fd1b,
-       0xefd00a20,
-/* 0x04c3: ctx_xfer */
-       0xf100f800,
-       0xb60a0417,
-       0x1fd00614,
-       0x0711f400,
-       0x04a421f5,
-/* 0x04d4: ctx_xfer_not_load */
-       0x4afc17f1,
-       0xf00213f0,
-       0x12d00c27,
-       0x0721f500,
-       0xfc27f102,
-       0x0223f047,
-       0xf00020d0,
-       0x20b6012c,
-       0x0012d003,
+       0xcf04bdf0,
+       0xabc4800a,
+       0x1d0bf404,
+       0x1900b7f1,
+       0xcf1cd7f0,
+       0xbfcf40be,
+       0x0421f400,
+       0x0400b0b7,
+       0xd001e7f0,
+/* 0x045e: ih_no_fifo */
+       0x0ad000be,
+       0xfcf0fc40,
+       0xfcd0fce0,
+       0xfca0fcb0,
+       0xfe80fc90,
+       0x80fc0088,
+       0xf80032f4,
+/* 0x0479: hub_barrier_done */
+       0x01f7f001,
+       0xbb040e98,
+       0xe7f104fe,
+       0xe3f09418,
+       0x8d21f440,
+/* 0x048e: ctx_redswitch */
+       0xe7f100f8,
+       0xe4b60614,
+       0x20f7f006,
+       0xf000efd0,
+/* 0x049e: ctx_redswitch_delay */
+       0xf2b608f7,
+       0xfd1bf401,
+       0x0a20f7f1,
+       0xf800efd0,
+/* 0x04ad: ctx_xfer */
+       0x0417f100,
+       0x0614b60a,
+       0xf4001fd0,
+       0x21f50711,
+/* 0x04be: ctx_xfer_not_load */
+       0x17f1048e,
+       0x13f04afc,
+       0x0c27f002,
+       0xf50012d0,
+       0xf1021521,
+       0xf047fc27,
+       0x20d00223,
+       0x012cf000,
+       0xd00320b6,
+       0xacf00012,
+       0x02a5f001,
+       0xf000b7f0,
+       0x0c9850b3,
+       0x0fc4b604,
+       0x9800bcbb,
+       0x0d98000c,
+       0x00e7f001,
+       0x016621f5,
        0xf001acf0,
-       0xb7f002a5,
-       0x50b3f000,
-       0xb6000c98,
-       0xbcbb0fc4,
-       0x010c9800,
-       0xf0020d98,
-       0x21f500e7,
-       0xacf0015c,
-       0x04a5f001,
-       0x4000b7f1,
-       0x9850b3f0,
-       0xc4b6000c,
-       0x00bcbb0f,
-       0x98050c98,
-       0x0f98060d,
-       0x00e7f104,
-       0x5c21f508,
-       0x0721f501,
-       0x0601f402,
-/* 0x054b: ctx_xfer_post */
-       0xf11412f4,
-       0xf04afc17,
-       0x27f00213,
-       0x0012d00d,
-       0x020721f5,
-/* 0x055c: ctx_xfer_done */
-       0x048f21f5,
-       0x000000f8,
+       0xb7f104a5,
+       0xb3f04000,
+       0x040c9850,
+       0xbb0fc4b6,
+       0x0c9800bc,
+       0x020d9801,
+       0xf1060f98,
+       0xf50800e7,
+       0xf5016621,
+       0xf4021521,
+       0x12f40601,
+/* 0x0535: ctx_xfer_post */
+       0xfc17f114,
+       0x0213f04a,
+       0xd00d27f0,
+       0x21f50012,
+/* 0x0546: ctx_xfer_done */
+       0x21f50215,
+       0x00f80479,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
        0x00000000,
        0x00000000,
        0x00000000,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc
new file mode 100644 (file)
index 0000000..c2f754e
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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>
+ */
+
+#define NV_PGRAPH_GPCX_UNK__SIZE                                     0x00000001
+
+#define CHIPSET GF117
+#include "macros.fuc"
+
+.section #nvd7_grgpc_data
+#define INCLUDE_DATA
+#include "com.fuc"
+#include "gpc.fuc"
+#undef INCLUDE_DATA
+
+.section #nvd7_grgpc_code
+#define INCLUDE_CODE
+bra #init
+#include "com.fuc"
+#include "gpc.fuc"
+.align 256
+#undef INCLUDE_CODE
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h
new file mode 100644 (file)
index 0000000..dd346c2
--- /dev/null
@@ -0,0 +1,475 @@
+uint32_t nvd7_grgpc_data[] = {
+/* 0x0000: gpc_mmio_list_head */
+       0x0000006c,
+/* 0x0004: gpc_mmio_list_tail */
+/* 0x0004: tpc_mmio_list_head */
+       0x0000006c,
+/* 0x0008: tpc_mmio_list_tail */
+/* 0x0008: unk_mmio_list_head */
+       0x0000006c,
+/* 0x000c: unk_mmio_list_tail */
+       0x0000006c,
+/* 0x0010: gpc_id */
+       0x00000000,
+/* 0x0014: tpc_count */
+       0x00000000,
+/* 0x0018: tpc_mask */
+       0x00000000,
+/* 0x001c: unk_count */
+       0x00000000,
+/* 0x0020: unk_mask */
+       0x00000000,
+/* 0x0024: cmd_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
+
+uint32_t nvd7_grgpc_code[] = {
+       0x03180ef5,
+/* 0x0004: queue_put */
+       0x9800d898,
+       0x86f001d9,
+       0x0489b808,
+       0xf00c1bf4,
+       0x21f502f7,
+       0x00f802fe,
+/* 0x001c: queue_put_next */
+       0xb60798c4,
+       0x8dbb0384,
+       0x0880b600,
+       0x80008e80,
+       0x90b6018f,
+       0x0f94f001,
+       0xf801d980,
+/* 0x0039: queue_get */
+       0x0131f400,
+       0x9800d898,
+       0x89b801d9,
+       0x210bf404,
+       0xb60789c4,
+       0x9dbb0394,
+       0x0890b600,
+       0x98009e98,
+       0x80b6019f,
+       0x0f84f001,
+       0xf400d880,
+/* 0x0066: queue_get_done */
+       0x00f80132,
+/* 0x0068: nv_rd32 */
+       0x0728b7f1,
+       0xb906b4b6,
+       0xc9f002ec,
+       0x00bcd01f,
+/* 0x0078: nv_rd32_wait */
+       0xc800bccf,
+       0x1bf41fcc,
+       0x06a7f0fa,
+       0x010921f5,
+       0xf840bfcf,
+/* 0x008d: nv_wr32 */
+       0x28b7f100,
+       0x06b4b607,
+       0xb980bfd0,
+       0xc9f002ec,
+       0x1ec9f01f,
+/* 0x00a3: nv_wr32_wait */
+       0xcf00bcd0,
+       0xccc800bc,
+       0xfa1bf41f,
+/* 0x00ae: watchdog_reset */
+       0x87f100f8,
+       0x84b60430,
+       0x1ff9f006,
+       0xf8008fd0,
+/* 0x00bd: watchdog_clear */
+       0x3087f100,
+       0x0684b604,
+       0xf80080d0,
+/* 0x00c9: wait_donez */
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x07f104bd,
+       0x03f00600,
+       0x000ad002,
+/* 0x00e6: wait_donez_ne */
+       0x87f104bd,
+       0x83f00000,
+       0x0088cf01,
+       0xf4888aff,
+       0x94bdf31b,
+       0xf10099f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0109: wait_doneo */
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x87f104bd,
+       0x84b60818,
+       0x008ad006,
+/* 0x0124: wait_doneo_e */
+       0x040087f1,
+       0xcf0684b6,
+       0x8aff0088,
+       0xf30bf488,
+       0x99f094bd,
+       0x0007f100,
+       0x0203f017,
+       0xbd0009d0,
+/* 0x0147: mmctx_size */
+       0xbd00f804,
+/* 0x0149: nv_mmctx_size_loop */
+       0x00e89894,
+       0xb61a85b6,
+       0x84b60180,
+       0x0098bb02,
+       0xb804e0b6,
+       0x1bf404ef,
+       0x029fb9eb,
+/* 0x0166: mmctx_xfer */
+       0x94bd00f8,
+       0xf10199f0,
+       0xf00f0007,
+       0x09d00203,
+       0xf104bd00,
+       0xb6071087,
+       0x94bd0684,
+       0xf405bbfd,
+       0x8bd0090b,
+       0x0099f000,
+/* 0x018c: mmctx_base_disabled */
+       0xf405eefd,
+       0x8ed00c0b,
+       0xc08fd080,
+/* 0x019b: mmctx_multi_disabled */
+       0xb70199f0,
+       0xc8010080,
+       0xb4b600ab,
+       0x0cb9f010,
+       0xb601aec8,
+       0xbefd11e4,
+       0x008bd005,
+/* 0x01b4: mmctx_exec_loop */
+/* 0x01b4: mmctx_wait_free */
+       0xf0008ecf,
+       0x0bf41fe4,
+       0x00ce98fa,
+       0xd005e9fd,
+       0xc0b6c08e,
+       0x04cdb804,
+       0xc8e81bf4,
+       0x1bf402ab,
+/* 0x01d5: mmctx_fini_wait */
+       0x008bcf18,
+       0xb01fb4f0,
+       0x1bf410b4,
+       0x02a7f0f7,
+       0xf4c921f4,
+/* 0x01ea: mmctx_stop */
+       0xabc81b0e,
+       0x10b4b600,
+       0xf00cb9f0,
+       0x8bd012b9,
+/* 0x01f9: mmctx_stop_wait */
+       0x008bcf00,
+       0xf412bbc8,
+/* 0x0202: mmctx_done */
+       0x94bdfa1b,
+       0xf10199f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0215: strand_wait */
+       0xf0a0f900,
+       0x21f402a7,
+       0xf8a0fcc9,
+/* 0x0221: strand_pre */
+       0xfc87f100,
+       0x0283f04a,
+       0xd00c97f0,
+       0x21f50089,
+       0x00f80215,
+/* 0x0234: strand_post */
+       0x4afc87f1,
+       0xf00283f0,
+       0x89d00d97,
+       0x1521f500,
+/* 0x0247: strand_set */
+       0xf100f802,
+       0xf04ffca7,
+       0xaba202a3,
+       0xc7f00500,
+       0x00acd00f,
+       0xd00bc7f0,
+       0x21f500bc,
+       0xaed00215,
+       0x0ac7f000,
+       0xf500bcd0,
+       0xf8021521,
+/* 0x0271: strand_ctx_init */
+       0xf094bd00,
+       0x07f10399,
+       0x03f00f00,
+       0x0009d002,
+       0x21f504bd,
+       0xe7f00221,
+       0x4721f503,
+       0xfca7f102,
+       0x02a3f046,
+       0x0400aba0,
+       0xf040a0d0,
+       0xbcd001c7,
+       0x1521f500,
+       0x010c9202,
+       0xf000acd0,
+       0xbcd002c7,
+       0x1521f500,
+       0x3421f502,
+       0x8087f102,
+       0x0684b608,
+       0xb70089cf,
+       0x95220080,
+/* 0x02ca: ctx_init_strand_loop */
+       0x8ed008fe,
+       0x408ed000,
+       0xb6808acf,
+       0xa0b606a5,
+       0x00eabb01,
+       0xb60480b6,
+       0x1bf40192,
+       0x08e4b6e8,
+       0xbdf2efbc,
+       0x0399f094,
+       0x170007f1,
+       0xd00203f0,
+       0x04bd0009,
+/* 0x02fe: error */
+       0xe0f900f8,
+       0x9814e7f1,
+       0xf440e3f0,
+       0xe0b78d21,
+       0xf7f0041c,
+       0x8d21f401,
+       0x00f8e0fc,
+/* 0x0318: init */
+       0x04fe04bd,
+       0x0017f100,
+       0x0227f012,
+       0xf10012d0,
+       0xfe047017,
+       0x17f10010,
+       0x10d00400,
+       0x0427f0c0,
+       0xf40012d0,
+       0x17f11031,
+       0x14b60608,
+       0x0012cf06,
+       0xf00137f0,
+       0x32bb1f24,
+       0x0132b604,
+       0x80050280,
+       0x10b70603,
+       0x12cf0400,
+       0x04028000,
+       0x0c30e7f1,
+       0xbd50e3f0,
+       0xbd34bd24,
+/* 0x0371: init_unk_loop */
+       0x6821f444,
+       0xf400f6b0,
+       0xf7f00f0b,
+       0x04f2bb01,
+       0xb6054ffd,
+/* 0x0386: init_unk_next */
+       0x20b60130,
+       0x04e0b601,
+       0xf40126b0,
+/* 0x0392: init_unk_done */
+       0x0380e21b,
+       0x08048007,
+       0x010027f1,
+       0xcf0223f0,
+       0x34bd0022,
+       0x070047f1,
+       0x950644b6,
+       0x45d00825,
+       0x4045d000,
+       0x98000e98,
+       0x21f5010f,
+       0x2fbb0147,
+       0x003fbb00,
+       0x98010e98,
+       0x21f5020f,
+       0x0e980147,
+       0x00effd05,
+       0xbb002ebb,
+       0x0e98003e,
+       0x030f9802,
+       0x014721f5,
+       0xfd070e98,
+       0x2ebb00ef,
+       0x003ebb00,
+       0x130040b7,
+       0xd00235b6,
+       0x25b60043,
+       0x0635b608,
+       0xb60120b6,
+       0x24b60130,
+       0x0834b608,
+       0xf5022fb9,
+       0xbb027121,
+       0x07f1003f,
+       0x03f00100,
+       0x0003d002,
+       0x24bd04bd,
+       0xf11f29f0,
+       0xf0080007,
+       0x02d00203,
+/* 0x0433: main */
+       0xf404bd00,
+       0x28f40031,
+       0x24d7f000,
+       0xf43921f4,
+       0xe4b0f401,
+       0x1e18f404,
+       0xf00181fe,
+       0x20bd0627,
+       0xb60412fd,
+       0x1efd01e4,
+       0x0018fe05,
+       0x04f721f5,
+/* 0x0463: main_not_ctx_xfer */
+       0x94d30ef4,
+       0xf5f010ef,
+       0xfe21f501,
+       0xc60ef402,
+/* 0x0470: ih */
+       0x88fe80f9,
+       0xf980f901,
+       0xf9a0f990,
+       0xf9d0f9b0,
+       0xbdf0f9e0,
+       0x800acf04,
+       0xf404abc4,
+       0xb7f11d0b,
+       0xd7f01900,
+       0x40becf24,
+       0xf400bfcf,
+       0xb0b70421,
+       0xe7f00400,
+       0x00bed001,
+/* 0x04a8: ih_no_fifo */
+       0xfc400ad0,
+       0xfce0fcf0,
+       0xfcb0fcd0,
+       0xfc90fca0,
+       0x0088fe80,
+       0x32f480fc,
+/* 0x04c3: hub_barrier_done */
+       0xf001f800,
+       0x0e9801f7,
+       0x04febb04,
+       0x9418e7f1,
+       0xf440e3f0,
+       0x00f88d21,
+/* 0x04d8: ctx_redswitch */
+       0x0614e7f1,
+       0xf006e4b6,
+       0xefd020f7,
+       0x08f7f000,
+/* 0x04e8: ctx_redswitch_delay */
+       0xf401f2b6,
+       0xf7f1fd1b,
+       0xefd00a20,
+/* 0x04f7: ctx_xfer */
+       0xf100f800,
+       0xb60a0417,
+       0x1fd00614,
+       0x0711f400,
+       0x04d821f5,
+/* 0x0508: ctx_xfer_not_load */
+       0x4afc17f1,
+       0xf00213f0,
+       0x12d00c27,
+       0x1521f500,
+       0xfc27f102,
+       0x0223f047,
+       0xf00020d0,
+       0x20b6012c,
+       0x0012d003,
+       0xf001acf0,
+       0xb7f002a5,
+       0x50b3f000,
+       0xb6040c98,
+       0xbcbb0fc4,
+       0x000c9800,
+       0xf0010d98,
+       0x21f500e7,
+       0xacf00166,
+       0x00b7f101,
+       0x50b3f040,
+       0xb6040c98,
+       0xbcbb0fc4,
+       0x010c9800,
+       0x98020d98,
+       0xe7f1060f,
+       0x21f50800,
+       0xacf00166,
+       0x04a5f001,
+       0x3000b7f1,
+       0x9850b3f0,
+       0xc4b6040c,
+       0x00bcbb0f,
+       0x98020c98,
+       0x0f98030d,
+       0x00e7f108,
+       0x6621f502,
+       0x1521f501,
+       0x0601f402,
+/* 0x05a3: ctx_xfer_post */
+       0xf11412f4,
+       0xf04afc17,
+       0x27f00213,
+       0x0012d00d,
+       0x021521f5,
+/* 0x05b4: ctx_xfer_done */
+       0x04c321f5,
+       0x000000f8,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
index 62ab231..6b906cd 100644 (file)
@@ -1,6 +1,5 @@
-/* fuc microcode for nve0 PGRAPH/GPC
- *
- * Copyright 2011 Red Hat Inc.
+/*
+ * 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"),
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  * OTHER DEALINGS IN THE SOFTWARE.
  *
- * Authors: Ben Skeggs
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
  */
 
-/* To build:
- *    m4 nve0_grgpc.fuc | envyas -a -w -m fuc -V nva3 -o nve0_grgpc.fuc.h
- */
+#define NV_PGRAPH_GPCX_UNK__SIZE                                     0x00000001
 
-/* TODO
- * - bracket certain functions with scratch writes, useful for debugging
- * - watchdog timer around ctx operations
- */
+#define CHIPSET GK100
+#include "macros.fuc"
 
 .section #nve0_grgpc_data
-include(`nve0.fuc')
-gpc_id:                        .b32 0
-gpc_mmio_list_head:    .b32 0
-gpc_mmio_list_tail:    .b32 0
-
-tpc_count:             .b32 0
-tpc_mask:              .b32 0
-tpc_mmio_list_head:    .b32 0
-tpc_mmio_list_tail:    .b32 0
-
-cmd_queue:             queue_init
-
-// chipset descriptions
-chipsets:
-.b8  0xe4 0 0 0
-.b16 #nve4_gpc_mmio_head
-.b16 #nve4_gpc_mmio_tail
-.b16 #nve4_tpc_mmio_head
-.b16 #nve4_tpc_mmio_tail
-.b8  0xe7 0 0 0
-.b16 #nve4_gpc_mmio_head
-.b16 #nve4_gpc_mmio_tail
-.b16 #nve4_tpc_mmio_head
-.b16 #nve4_tpc_mmio_tail
-.b8  0xe6 0 0 0
-.b16 #nve4_gpc_mmio_head
-.b16 #nve4_gpc_mmio_tail
-.b16 #nve4_tpc_mmio_head
-.b16 #nve4_tpc_mmio_tail
-.b8  0 0 0 0
-
-// GPC mmio lists
-nve4_gpc_mmio_head:
-mmctx_data(0x000380, 1)
-mmctx_data(0x000400, 2)
-mmctx_data(0x00040c, 3)
-mmctx_data(0x000450, 9)
-mmctx_data(0x000600, 1)
-mmctx_data(0x000684, 1)
-mmctx_data(0x000700, 5)
-mmctx_data(0x000800, 1)
-mmctx_data(0x000808, 3)
-mmctx_data(0x000828, 1)
-mmctx_data(0x000830, 1)
-mmctx_data(0x0008d8, 1)
-mmctx_data(0x0008e0, 1)
-mmctx_data(0x0008e8, 6)
-mmctx_data(0x00091c, 1)
-mmctx_data(0x000924, 3)
-mmctx_data(0x000b00, 1)
-mmctx_data(0x000b08, 6)
-mmctx_data(0x000bb8, 1)
-mmctx_data(0x000c08, 1)
-mmctx_data(0x000c10, 8)
-mmctx_data(0x000c40, 1)
-mmctx_data(0x000c6c, 1)
-mmctx_data(0x000c80, 1)
-mmctx_data(0x000c8c, 1)
-mmctx_data(0x001000, 3)
-mmctx_data(0x001014, 1)
-mmctx_data(0x003024, 1)
-mmctx_data(0x0030c0, 2)
-mmctx_data(0x0030e4, 1)
-mmctx_data(0x003100, 6)
-mmctx_data(0x0031d0, 1)
-mmctx_data(0x0031e0, 2)
-nve4_gpc_mmio_tail:
-
-// TPC mmio lists
-nve4_tpc_mmio_head:
-mmctx_data(0x000048, 1)
-mmctx_data(0x000064, 1)
-mmctx_data(0x000088, 1)
-mmctx_data(0x000200, 6)
-mmctx_data(0x00021c, 2)
-mmctx_data(0x000230, 1)
-mmctx_data(0x0002c4, 1)
-mmctx_data(0x000400, 3)
-mmctx_data(0x000420, 3)
-mmctx_data(0x0004e8, 1)
-mmctx_data(0x0004f4, 1)
-mmctx_data(0x000604, 4)
-mmctx_data(0x000644, 22)
-mmctx_data(0x0006ac, 2)
-mmctx_data(0x0006c8, 1)
-mmctx_data(0x000730, 8)
-mmctx_data(0x000758, 1)
-mmctx_data(0x000778, 1)
-nve4_tpc_mmio_tail:
+#define INCLUDE_DATA
+#include "com.fuc"
+#include "gpc.fuc"
+#undef INCLUDE_DATA
 
 .section #nve0_grgpc_code
+#define INCLUDE_CODE
 bra #init
-define(`include_code')
-include(`nve0.fuc')
-
-// reports an exception to the host
-//
-// In: $r15 error code (see nve0.fuc)
-//
-error:
-       push $r14
-       mov $r14 -0x67ec        // 0x9814
-       sethi $r14 0x400000
-       call #nv_wr32           // HUB_CTXCTL_CC_SCRATCH[5] = error code
-       add b32 $r14 0x41c
-       mov $r15 1
-       call #nv_wr32           // HUB_CTXCTL_INTR_UP_SET
-       pop $r14
-       ret
-
-// GPC fuc initialisation, executed by triggering ucode start, will
-// fall through to main loop after completion.
-//
-// Input:
-//   CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh)
-//   CC_SCRATCH[1]: context base
-//
-// Output:
-//   CC_SCRATCH[0]:
-//          31:31: set to signal completion
-//   CC_SCRATCH[1]:
-//           31:0: GPC context size
-//
-init:
-       clear b32 $r0
-       mov $sp $r0
-
-       // enable fifo access
-       mov $r1 0x1200
-       mov $r2 2
-       iowr I[$r1 + 0x000] $r2         // FIFO_ENABLE
-
-       // setup i0 handler, and route all interrupts to it
-       mov $r1 #ih
-       mov $iv0 $r1
-       mov $r1 0x400
-       iowr I[$r1 + 0x300] $r0         // INTR_DISPATCH
-
-       // enable fifo interrupt
-       mov $r2 4
-       iowr I[$r1 + 0x000] $r2         // INTR_EN_SET
-
-       // enable interrupts
-       bset $flags ie0
-
-       // figure out which GPC we are, and how many TPCs we have
-       mov $r1 0x608
-       shl b32 $r1 6
-       iord $r2 I[$r1 + 0x000]         // UNITS
-       mov $r3 1
-       and $r2 0x1f
-       shl b32 $r3 $r2
-       sub b32 $r3 1
-       st b32 D[$r0 + #tpc_count] $r2
-       st b32 D[$r0 + #tpc_mask] $r3
-       add b32 $r1 0x400
-       iord $r2 I[$r1 + 0x000]         // MYINDEX
-       st b32 D[$r0 + #gpc_id] $r2
-
-       // find context data for this chipset
-       mov $r2 0x800
-       shl b32 $r2 6
-       iord $r2 I[$r2 + 0x000]         // CC_SCRATCH[0]
-       mov $r1 #chipsets - 12
-       init_find_chipset:
-               add b32 $r1 12
-               ld b32 $r3 D[$r1 + 0x00]
-               cmpu b32 $r3 $r2
-               bra e #init_context
-               cmpu b32 $r3 0
-               bra ne #init_find_chipset
-               // unknown chipset
-               ret
-
-       // initialise context base, and size tracking
-       init_context:
-       mov $r2 0x800
-       shl b32 $r2 6
-       iord $r2 I[$r2 + 0x100] // CC_SCRATCH[1], initial base
-       clear b32 $r3           // track GPC context size here
-
-       // set mmctx base addresses now so we don't have to do it later,
-       // they don't currently ever change
-       mov $r4 0x700
-       shl b32 $r4 6
-       shr b32 $r5 $r2 8
-       iowr I[$r4 + 0x000] $r5         // MMCTX_SAVE_SWBASE
-       iowr I[$r4 + 0x100] $r5         // MMCTX_LOAD_SWBASE
-
-       // calculate GPC mmio context size, store the chipset-specific
-       // mmio list pointers somewhere we can get at them later without
-       // re-parsing the chipset list
-       clear b32 $r14
-       clear b32 $r15
-       ld b16 $r14 D[$r1 + 4]
-       ld b16 $r15 D[$r1 + 6]
-       st b16 D[$r0 + #gpc_mmio_list_head] $r14
-       st b16 D[$r0 + #gpc_mmio_list_tail] $r15
-       call #mmctx_size
-       add b32 $r2 $r15
-       add b32 $r3 $r15
-
-       // calculate per-TPC mmio context size, store the list pointers
-       ld b16 $r14 D[$r1 + 8]
-       ld b16 $r15 D[$r1 + 10]
-       st b16 D[$r0 + #tpc_mmio_list_head] $r14
-       st b16 D[$r0 + #tpc_mmio_list_tail] $r15
-       call #mmctx_size
-       ld b32 $r14 D[$r0 + #tpc_count]
-       mulu $r14 $r15
-       add b32 $r2 $r14
-       add b32 $r3 $r14
-
-       // round up base/size to 256 byte boundary (for strand SWBASE)
-       add b32 $r4 0x1300
-       shr b32 $r3 2
-       iowr I[$r4 + 0x000] $r3         // MMCTX_LOAD_COUNT, wtf for?!?
-       shr b32 $r2 8
-       shr b32 $r3 6
-       add b32 $r2 1
-       add b32 $r3 1
-       shl b32 $r2 8
-       shl b32 $r3 8
-
-       // calculate size of strand context data
-       mov b32 $r15 $r2
-       call #strand_ctx_init
-       add b32 $r3 $r15
-
-       // save context size, and tell HUB we're done
-       mov $r1 0x800
-       shl b32 $r1 6
-       iowr I[$r1 + 0x100] $r3         // CC_SCRATCH[1]  = context size
-       add b32 $r1 0x800
-       clear b32 $r2
-       bset $r2 31
-       iowr I[$r1 + 0x000] $r2         // CC_SCRATCH[0] |= 0x80000000
-
-// Main program loop, very simple, sleeps until woken up by the interrupt
-// handler, pulls a command from the queue and executes its handler
-//
-main:
-       bset $flags $p0
-       sleep $p0
-       mov $r13 #cmd_queue
-       call #queue_get
-       bra $p1 #main
-
-       // 0x0000-0x0003 are all context transfers
-       cmpu b32 $r14 0x04
-       bra nc #main_not_ctx_xfer
-               // fetch $flags and mask off $p1/$p2
-               mov $r1 $flags
-               mov $r2 0x0006
-               not b32 $r2
-               and $r1 $r2
-               // set $p1/$p2 according to transfer type
-               shl b32 $r14 1
-               or $r1 $r14
-               mov $flags $r1
-               // transfer context data
-               call #ctx_xfer
-               bra #main
-
-       main_not_ctx_xfer:
-       shl b32 $r15 $r14 16
-       or $r15 E_BAD_COMMAND
-       call #error
-       bra #main
-
-// interrupt handler
-ih:
-       push $r8
-       mov $r8 $flags
-       push $r8
-       push $r9
-       push $r10
-       push $r11
-       push $r13
-       push $r14
-       push $r15
-
-       // incoming fifo command?
-       iord $r10 I[$r0 + 0x200]        // INTR
-       and $r11 $r10 0x00000004
-       bra e #ih_no_fifo
-               // queue incoming fifo command for later processing
-               mov $r11 0x1900
-               mov $r13 #cmd_queue
-               iord $r14 I[$r11 + 0x100]       // FIFO_CMD
-               iord $r15 I[$r11 + 0x000]       // FIFO_DATA
-               call #queue_put
-               add b32 $r11 0x400
-               mov $r14 1
-               iowr I[$r11 + 0x000] $r14       // FIFO_ACK
-
-       // ack, and wake up main()
-       ih_no_fifo:
-       iowr I[$r0 + 0x100] $r10        // INTR_ACK
-
-       pop $r15
-       pop $r14
-       pop $r13
-       pop $r11
-       pop $r10
-       pop $r9
-       pop $r8
-       mov $flags $r8
-       pop $r8
-       bclr $flags $p0
-       iret
-
-// Set this GPC's bit in HUB_BAR, used to signal completion of various
-// activities to the HUB fuc
-//
-hub_barrier_done:
-       mov $r15 1
-       ld b32 $r14 D[$r0 + #gpc_id]
-       shl b32 $r15 $r14
-       mov $r14 -0x6be8        // 0x409418 - HUB_BAR_SET
-       sethi $r14 0x400000
-       call #nv_wr32
-       ret
-
-// Disables various things, waits a bit, and re-enables them..
-//
-// Not sure how exactly this helps, perhaps "ENABLE" is not such a
-// good description for the bits we turn off?  Anyways, without this,
-// funny things happen.
-//
-ctx_redswitch:
-       mov $r14 0x614
-       shl b32 $r14 6
-       mov $r15 0x020
-       iowr I[$r14] $r15       // GPC_RED_SWITCH = POWER
-       mov $r15 8
-       ctx_redswitch_delay:
-               sub b32 $r15 1
-               bra ne #ctx_redswitch_delay
-       mov $r15 0xa20
-       iowr I[$r14] $r15       // GPC_RED_SWITCH = UNK11, ENABLE, POWER
-       ret
-
-// Transfer GPC context data between GPU and storage area
-//
-// In: $r15 context base address
-//     $p1 clear on save, set on load
-//     $p2 set if opposite direction done/will be done, so:
-//             on save it means: "a load will follow this save"
-//             on load it means: "a save preceeded this load"
-//
-ctx_xfer:
-       // set context base address
-       mov $r1 0xa04
-       shl b32 $r1 6
-       iowr I[$r1 + 0x000] $r15// MEM_BASE
-       bra not $p1 #ctx_xfer_not_load
-               call #ctx_redswitch
-       ctx_xfer_not_load:
-
-       // strands
-       mov $r1 0x4afc
-       sethi $r1 0x20000
-       mov $r2 0xc
-       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x0c
-       call #strand_wait
-       mov $r2 0x47fc
-       sethi $r2 0x20000
-       iowr I[$r2] $r0         // STRAND_FIRST_GENE(0x3f) = 0x00
-       xbit $r2 $flags $p1
-       add b32 $r2 3
-       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD)
-
-       // mmio context
-       xbit $r10 $flags $p1    // direction
-       or $r10 2               // first
-       mov $r11 0x0000
-       sethi $r11 0x500000
-       ld b32 $r12 D[$r0 + #gpc_id]
-       shl b32 $r12 15
-       add b32 $r11 $r12       // base = NV_PGRAPH_GPCn
-       ld b32 $r12 D[$r0 + #gpc_mmio_list_head]
-       ld b32 $r13 D[$r0 + #gpc_mmio_list_tail]
-       mov $r14 0              // not multi
-       call #mmctx_xfer
-
-       // per-TPC mmio context
-       xbit $r10 $flags $p1    // direction
-       or $r10 4               // last
-       mov $r11 0x4000
-       sethi $r11 0x500000     // base = NV_PGRAPH_GPC0_TPC0
-       ld b32 $r12 D[$r0 + #gpc_id]
-       shl b32 $r12 15
-       add b32 $r11 $r12       // base = NV_PGRAPH_GPCn_TPC0
-       ld b32 $r12 D[$r0 + #tpc_mmio_list_head]
-       ld b32 $r13 D[$r0 + #tpc_mmio_list_tail]
-       ld b32 $r15 D[$r0 + #tpc_mask]
-       mov $r14 0x800          // stride = 0x800
-       call #mmctx_xfer
-
-       // wait for strands to finish
-       call #strand_wait
-
-       // if load, or a save without a load following, do some
-       // unknown stuff that's done after finishing a block of
-       // strand commands
-       bra $p1 #ctx_xfer_post
-       bra not $p2 #ctx_xfer_done
-       ctx_xfer_post:
-               mov $r1 0x4afc
-               sethi $r1 0x20000
-               mov $r2 0xd
-               iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x0d
-               call #strand_wait
-
-       // mark completion in HUB's barrier
-       ctx_xfer_done:
-       call #hub_barrier_done
-       ret
-
+#include "com.fuc"
+#include "gpc.fuc"
 .align 256
+#undef INCLUDE_CODE
index 09ee470..7ff5ef6 100644 (file)
@@ -1,19 +1,27 @@
 uint32_t nve0_grgpc_data[] = {
-/* 0x0000: gpc_id */
+/* 0x0000: gpc_mmio_list_head */
+       0x0000006c,
+/* 0x0004: gpc_mmio_list_tail */
+/* 0x0004: tpc_mmio_list_head */
+       0x0000006c,
+/* 0x0008: tpc_mmio_list_tail */
+/* 0x0008: unk_mmio_list_head */
+       0x0000006c,
+/* 0x000c: unk_mmio_list_tail */
+       0x0000006c,
+/* 0x0010: gpc_id */
        0x00000000,
-/* 0x0004: gpc_mmio_list_head */
+/* 0x0014: tpc_count */
        0x00000000,
-/* 0x0008: gpc_mmio_list_tail */
+/* 0x0018: tpc_mask */
        0x00000000,
-/* 0x000c: tpc_count */
+/* 0x001c: unk_count */
        0x00000000,
-/* 0x0010: tpc_mask */
+/* 0x0020: unk_mask */
        0x00000000,
-/* 0x0014: tpc_mmio_list_head */
+/* 0x0024: cmd_queue */
        0x00000000,
-/* 0x0018: tpc_mmio_list_tail */
        0x00000000,
-/* 0x001c: cmd_queue */
        0x00000000,
        0x00000000,
        0x00000000,
@@ -30,84 +38,17 @@ uint32_t nve0_grgpc_data[] = {
        0x00000000,
        0x00000000,
        0x00000000,
-       0x00000000,
-       0x00000000,
-/* 0x0064: chipsets */
-       0x000000e4,
-       0x0110008c,
-       0x01580110,
-       0x000000e7,
-       0x0110008c,
-       0x01580110,
-       0x000000e6,
-       0x0110008c,
-       0x01580110,
-       0x00000000,
-/* 0x008c: nve4_gpc_mmio_head */
-       0x00000380,
-       0x04000400,
-       0x0800040c,
-       0x20000450,
-       0x00000600,
-       0x00000684,
-       0x10000700,
-       0x00000800,
-       0x08000808,
-       0x00000828,
-       0x00000830,
-       0x000008d8,
-       0x000008e0,
-       0x140008e8,
-       0x0000091c,
-       0x08000924,
-       0x00000b00,
-       0x14000b08,
-       0x00000bb8,
-       0x00000c08,
-       0x1c000c10,
-       0x00000c40,
-       0x00000c6c,
-       0x00000c80,
-       0x00000c8c,
-       0x08001000,
-       0x00001014,
-       0x00003024,
-       0x040030c0,
-       0x000030e4,
-       0x14003100,
-       0x000031d0,
-       0x040031e0,
-/* 0x0110: nve4_gpc_mmio_tail */
-/* 0x0110: nve4_tpc_mmio_head */
-       0x00000048,
-       0x00000064,
-       0x00000088,
-       0x14000200,
-       0x0400021c,
-       0x00000230,
-       0x000002c4,
-       0x08000400,
-       0x08000420,
-       0x000004e8,
-       0x000004f4,
-       0x0c000604,
-       0x54000644,
-       0x040006ac,
-       0x000006c8,
-       0x1c000730,
-       0x00000758,
-       0x00000778,
 };
 
 uint32_t nve0_grgpc_code[] = {
-       0x03060ef5,
+       0x03180ef5,
 /* 0x0004: queue_put */
        0x9800d898,
        0x86f001d9,
        0x0489b808,
        0xf00c1bf4,
        0x21f502f7,
-       0x00f802ec,
+       0x00f802fe,
 /* 0x001c: queue_put_next */
        0xb60798c4,
        0x8dbb0384,
@@ -139,7 +80,7 @@ uint32_t nve0_grgpc_code[] = {
        0xc800bccf,
        0x1bf41fcc,
        0x06a7f0fa,
-       0x010321f5,
+       0x010921f5,
        0xf840bfcf,
 /* 0x008d: nv_wr32 */
        0x28b7f100,
@@ -161,63 +102,66 @@ uint32_t nve0_grgpc_code[] = {
        0x0684b604,
        0xf80080d0,
 /* 0x00c9: wait_donez */
-       0x3c87f100,
-       0x0684b608,
-       0x99f094bd,
-       0x0089d000,
-       0x081887f1,
-       0xd00684b6,
-/* 0x00e2: wait_done_wait_donez */
-       0x87f1008a,
-       0x84b60400,
-       0x0088cf06,
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x07f104bd,
+       0x03f00600,
+       0x000ad002,
+/* 0x00e6: wait_donez_ne */
+       0x87f104bd,
+       0x83f00000,
+       0x0088cf01,
        0xf4888aff,
-       0x87f1f31b,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00099,
-/* 0x0103: wait_doneo */
-       0xf100f800,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00099f0,
-       0x87f10089,
+       0x94bdf31b,
+       0xf10099f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0109: wait_doneo */
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x87f104bd,
        0x84b60818,
        0x008ad006,
-/* 0x011c: wait_done_wait_doneo */
+/* 0x0124: wait_doneo_e */
        0x040087f1,
        0xcf0684b6,
        0x8aff0088,
        0xf30bf488,
-       0x085c87f1,
-       0xbd0684b6,
-       0x0099f094,
-       0xf80089d0,
-/* 0x013d: mmctx_size */
-/* 0x013f: nv_mmctx_size_loop */
-       0x9894bd00,
-       0x85b600e8,
-       0x0180b61a,
-       0xbb0284b6,
-       0xe0b60098,
-       0x04efb804,
-       0xb9eb1bf4,
-       0x00f8029f,
-/* 0x015c: mmctx_xfer */
-       0x083c87f1,
-       0xbd0684b6,
-       0x0199f094,
-       0xf10089d0,
+       0x99f094bd,
+       0x0007f100,
+       0x0203f017,
+       0xbd0009d0,
+/* 0x0147: mmctx_size */
+       0xbd00f804,
+/* 0x0149: nv_mmctx_size_loop */
+       0x00e89894,
+       0xb61a85b6,
+       0x84b60180,
+       0x0098bb02,
+       0xb804e0b6,
+       0x1bf404ef,
+       0x029fb9eb,
+/* 0x0166: mmctx_xfer */
+       0x94bd00f8,
+       0xf10199f0,
+       0xf00f0007,
+       0x09d00203,
+       0xf104bd00,
        0xb6071087,
        0x94bd0684,
        0xf405bbfd,
        0x8bd0090b,
        0x0099f000,
-/* 0x0180: mmctx_base_disabled */
+/* 0x018c: mmctx_base_disabled */
        0xf405eefd,
        0x8ed00c0b,
        0xc08fd080,
-/* 0x018f: mmctx_multi_disabled */
+/* 0x019b: mmctx_multi_disabled */
        0xb70199f0,
        0xc8010080,
        0xb4b600ab,
@@ -225,8 +169,8 @@ uint32_t nve0_grgpc_code[] = {
        0xb601aec8,
        0xbefd11e4,
        0x008bd005,
-/* 0x01a8: mmctx_exec_loop */
-/* 0x01a8: mmctx_wait_free */
+/* 0x01b4: mmctx_exec_loop */
+/* 0x01b4: mmctx_wait_free */
        0xf0008ecf,
        0x0bf41fe4,
        0x00ce98fa,
@@ -235,76 +179,77 @@ uint32_t nve0_grgpc_code[] = {
        0x04cdb804,
        0xc8e81bf4,
        0x1bf402ab,
-/* 0x01c9: mmctx_fini_wait */
+/* 0x01d5: mmctx_fini_wait */
        0x008bcf18,
        0xb01fb4f0,
        0x1bf410b4,
        0x02a7f0f7,
        0xf4c921f4,
-/* 0x01de: mmctx_stop */
+/* 0x01ea: mmctx_stop */
        0xabc81b0e,
        0x10b4b600,
        0xf00cb9f0,
        0x8bd012b9,
-/* 0x01ed: mmctx_stop_wait */
+/* 0x01f9: mmctx_stop_wait */
        0x008bcf00,
        0xf412bbc8,
-/* 0x01f6: mmctx_done */
-       0x87f1fa1b,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00199,
-/* 0x0207: strand_wait */
-       0xf900f800,
-       0x02a7f0a0,
-       0xfcc921f4,
-/* 0x0213: strand_pre */
-       0xf100f8a0,
-       0xf04afc87,
-       0x97f00283,
-       0x0089d00c,
-       0x020721f5,
-/* 0x0226: strand_post */
-       0x87f100f8,
-       0x83f04afc,
-       0x0d97f002,
-       0xf50089d0,
-       0xf8020721,
-/* 0x0239: strand_set */
-       0xfca7f100,
-       0x02a3f04f,
-       0x0500aba2,
-       0xd00fc7f0,
-       0xc7f000ac,
-       0x00bcd00b,
-       0x020721f5,
-       0xf000aed0,
-       0xbcd00ac7,
-       0x0721f500,
-/* 0x0263: strand_ctx_init */
-       0xf100f802,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00399f0,
+/* 0x0202: mmctx_done */
+       0x94bdfa1b,
+       0xf10199f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0215: strand_wait */
+       0xf0a0f900,
+       0x21f402a7,
+       0xf8a0fcc9,
+/* 0x0221: strand_pre */
+       0xfc87f100,
+       0x0283f04a,
+       0xd00c97f0,
        0x21f50089,
-       0xe7f00213,
-       0x3921f503,
+       0x00f80215,
+/* 0x0234: strand_post */
+       0x4afc87f1,
+       0xf00283f0,
+       0x89d00d97,
+       0x1521f500,
+/* 0x0247: strand_set */
+       0xf100f802,
+       0xf04ffca7,
+       0xaba202a3,
+       0xc7f00500,
+       0x00acd00f,
+       0xd00bc7f0,
+       0x21f500bc,
+       0xaed00215,
+       0x0ac7f000,
+       0xf500bcd0,
+       0xf8021521,
+/* 0x0271: strand_ctx_init */
+       0xf094bd00,
+       0x07f10399,
+       0x03f00f00,
+       0x0009d002,
+       0x21f504bd,
+       0xe7f00221,
+       0x4721f503,
        0xfca7f102,
        0x02a3f046,
        0x0400aba0,
        0xf040a0d0,
        0xbcd001c7,
-       0x0721f500,
+       0x1521f500,
        0x010c9202,
        0xf000acd0,
        0xbcd002c7,
-       0x0721f500,
-       0x2621f502,
+       0x1521f500,
+       0x3421f502,
        0x8087f102,
        0x0684b608,
        0xb70089cf,
        0x95220080,
-/* 0x02ba: ctx_init_strand_loop */
+/* 0x02ca: ctx_init_strand_loop */
        0x8ed008fe,
        0x408ed000,
        0xb6808acf,
@@ -313,150 +258,160 @@ uint32_t nve0_grgpc_code[] = {
        0xb60480b6,
        0x1bf40192,
        0x08e4b6e8,
-       0xf1f2efbc,
-       0xb6085c87,
-       0x94bd0684,
-       0xd00399f0,
-       0x00f80089,
-/* 0x02ec: error */
-       0xe7f1e0f9,
-       0xe3f09814,
-       0x8d21f440,
-       0x041ce0b7,
-       0xf401f7f0,
-       0xe0fc8d21,
-/* 0x0306: init */
-       0x04bd00f8,
-       0xf10004fe,
-       0xf0120017,
-       0x12d00227,
-       0x3e17f100,
-       0x0010fe04,
-       0x040017f1,
-       0xf0c010d0,
-       0x12d00427,
-       0x1031f400,
-       0x060817f1,
-       0xcf0614b6,
-       0x37f00012,
-       0x1f24f001,
-       0xb60432bb,
-       0x02800132,
-       0x04038003,
-       0x040010b7,
-       0x800012cf,
-       0x27f10002,
-       0x24b60800,
-       0x0022cf06,
-/* 0x035f: init_find_chipset */
-       0xb65817f0,
-       0x13980c10,
-       0x0432b800,
-       0xb00b0bf4,
-       0x1bf40034,
-/* 0x0373: init_context */
-       0xf100f8f1,
-       0xb6080027,
-       0x22cf0624,
-       0xf134bd40,
-       0xb6070047,
-       0x25950644,
-       0x0045d008,
-       0xbd4045d0,
-       0x58f4bde4,
-       0x1f58021e,
-       0x020e4003,
-       0xf5040f40,
-       0xbb013d21,
-       0x3fbb002f,
-       0x041e5800,
-       0x40051f58,
-       0x0f400a0e,
-       0x3d21f50c,
-       0x030e9801,
-       0xbb00effd,
-       0x3ebb002e,
-       0x0040b700,
-       0x0235b613,
-       0xb60043d0,
-       0x35b60825,
-       0x0120b606,
-       0xb60130b6,
-       0x34b60824,
-       0x022fb908,
-       0x026321f5,
-       0xf1003fbb,
-       0xb6080017,
-       0x13d00614,
-       0x0010b740,
-       0xf024bd08,
-       0x12d01f29,
-/* 0x0401: main */
-       0x0031f400,
-       0xf00028f4,
-       0x21f41cd7,
-       0xf401f439,
-       0xf404e4b0,
-       0x81fe1e18,
-       0x0627f001,
-       0x12fd20bd,
-       0x01e4b604,
-       0xfe051efd,
-       0x21f50018,
-       0x0ef404c3,
-/* 0x0431: main_not_ctx_xfer */
-       0x10ef94d3,
-       0xf501f5f0,
-       0xf402ec21,
-/* 0x043e: ih */
-       0x80f9c60e,
-       0xf90188fe,
-       0xf990f980,
-       0xf9b0f9a0,
-       0xf9e0f9d0,
-       0x800acff0,
+       0xbdf2efbc,
+       0x0399f094,
+       0x170007f1,
+       0xd00203f0,
+       0x04bd0009,
+/* 0x02fe: error */
+       0xe0f900f8,
+       0x9814e7f1,
+       0xf440e3f0,
+       0xe0b78d21,
+       0xf7f0041c,
+       0x8d21f401,
+       0x00f8e0fc,
+/* 0x0318: init */
+       0x04fe04bd,
+       0x0017f100,
+       0x0227f012,
+       0xf10012d0,
+       0xfe047017,
+       0x17f10010,
+       0x10d00400,
+       0x0427f0c0,
+       0xf40012d0,
+       0x17f11031,
+       0x14b60608,
+       0x0012cf06,
+       0xf00137f0,
+       0x32bb1f24,
+       0x0132b604,
+       0x80050280,
+       0x10b70603,
+       0x12cf0400,
+       0x04028000,
+       0x0c30e7f1,
+       0xbd50e3f0,
+       0xbd34bd24,
+/* 0x0371: init_unk_loop */
+       0x6821f444,
+       0xf400f6b0,
+       0xf7f00f0b,
+       0x04f2bb01,
+       0xb6054ffd,
+/* 0x0386: init_unk_next */
+       0x20b60130,
+       0x04e0b601,
+       0xf40126b0,
+/* 0x0392: init_unk_done */
+       0x0380e21b,
+       0x08048007,
+       0x010027f1,
+       0xcf0223f0,
+       0x34bd0022,
+       0x070047f1,
+       0x950644b6,
+       0x45d00825,
+       0x4045d000,
+       0x98000e98,
+       0x21f5010f,
+       0x2fbb0147,
+       0x003fbb00,
+       0x98010e98,
+       0x21f5020f,
+       0x0e980147,
+       0x00effd05,
+       0xbb002ebb,
+       0x0e98003e,
+       0x030f9802,
+       0x014721f5,
+       0xfd070e98,
+       0x2ebb00ef,
+       0x003ebb00,
+       0x130040b7,
+       0xd00235b6,
+       0x25b60043,
+       0x0635b608,
+       0xb60120b6,
+       0x24b60130,
+       0x0834b608,
+       0xf5022fb9,
+       0xbb027121,
+       0x07f1003f,
+       0x03f00100,
+       0x0003d002,
+       0x24bd04bd,
+       0xf11f29f0,
+       0xf0080007,
+       0x02d00203,
+/* 0x0433: main */
+       0xf404bd00,
+       0x28f40031,
+       0x24d7f000,
+       0xf43921f4,
+       0xe4b0f401,
+       0x1e18f404,
+       0xf00181fe,
+       0x20bd0627,
+       0xb60412fd,
+       0x1efd01e4,
+       0x0018fe05,
+       0x04f721f5,
+/* 0x0463: main_not_ctx_xfer */
+       0x94d30ef4,
+       0xf5f010ef,
+       0xfe21f501,
+       0xc60ef402,
+/* 0x0470: ih */
+       0x88fe80f9,
+       0xf980f901,
+       0xf9a0f990,
+       0xf9d0f9b0,
+       0xbdf0f9e0,
+       0x800acf04,
        0xf404abc4,
        0xb7f11d0b,
        0xd7f01900,
-       0x40becf1c,
+       0x40becf24,
        0xf400bfcf,
        0xb0b70421,
        0xe7f00400,
        0x00bed001,
-/* 0x0474: ih_no_fifo */
+/* 0x04a8: ih_no_fifo */
        0xfc400ad0,
        0xfce0fcf0,
        0xfcb0fcd0,
        0xfc90fca0,
        0x0088fe80,
        0x32f480fc,
-/* 0x048f: hub_barrier_done */
+/* 0x04c3: hub_barrier_done */
        0xf001f800,
        0x0e9801f7,
-       0x04febb00,
+       0x04febb04,
        0x9418e7f1,
        0xf440e3f0,
        0x00f88d21,
-/* 0x04a4: ctx_redswitch */
+/* 0x04d8: ctx_redswitch */
        0x0614e7f1,
        0xf006e4b6,
        0xefd020f7,
        0x08f7f000,
-/* 0x04b4: ctx_redswitch_delay */
+/* 0x04e8: ctx_redswitch_delay */
        0xf401f2b6,
        0xf7f1fd1b,
        0xefd00a20,
-/* 0x04c3: ctx_xfer */
+/* 0x04f7: ctx_xfer */
        0xf100f800,
        0xb60a0417,
        0x1fd00614,
        0x0711f400,
-       0x04a421f5,
-/* 0x04d4: ctx_xfer_not_load */
+       0x04d821f5,
+/* 0x0508: ctx_xfer_not_load */
        0x4afc17f1,
        0xf00213f0,
        0x12d00c27,
-       0x0721f500,
+       0x1521f500,
        0xfc27f102,
        0x0223f047,
        0xf00020d0,
@@ -465,31 +420,40 @@ uint32_t nve0_grgpc_code[] = {
        0xf001acf0,
        0xb7f002a5,
        0x50b3f000,
-       0xb6000c98,
+       0xb6040c98,
        0xbcbb0fc4,
-       0x010c9800,
-       0xf0020d98,
+       0x000c9800,
+       0xf0010d98,
        0x21f500e7,
-       0xacf0015c,
+       0xacf00166,
+       0x00b7f101,
+       0x50b3f040,
+       0xb6040c98,
+       0xbcbb0fc4,
+       0x010c9800,
+       0x98020d98,
+       0xe7f1060f,
+       0x21f50800,
+       0xacf00166,
        0x04a5f001,
-       0x4000b7f1,
+       0x3000b7f1,
        0x9850b3f0,
-       0xc4b6000c,
+       0xc4b6040c,
        0x00bcbb0f,
-       0x98050c98,
-       0x0f98060d,
-       0x00e7f104,
-       0x5c21f508,
-       0x0721f501,
+       0x98020c98,
+       0x0f98030d,
+       0x00e7f108,
+       0x6621f502,
+       0x1521f501,
        0x0601f402,
-/* 0x054b: ctx_xfer_post */
+/* 0x05a3: ctx_xfer_post */
        0xf11412f4,
        0xf04afc17,
        0x27f00213,
        0x0012d00d,
-       0x020721f5,
-/* 0x055c: ctx_xfer_done */
-       0x048f21f5,
+       0x021521f5,
+/* 0x05b4: ctx_xfer_done */
+       0x04c321f5,
        0x000000f8,
        0x00000000,
        0x00000000,
@@ -508,26 +472,4 @@ uint32_t nve0_grgpc_code[] = {
        0x00000000,
        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/engine/graph/fuc/gpcnvf0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc
new file mode 100644 (file)
index 0000000..90bbe52
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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>
+ */
+
+#define NV_PGRAPH_GPCX_UNK__SIZE                                     0x00000002
+
+#define CHIPSET GK110
+#include "macros.fuc"
+
+.section #nvf0_grgpc_data
+#define INCLUDE_DATA
+#include "com.fuc"
+#include "gpc.fuc"
+#undef INCLUDE_DATA
+
+.section #nvf0_grgpc_code
+#define INCLUDE_CODE
+bra #init
+#include "com.fuc"
+#include "gpc.fuc"
+.align 256
+#undef INCLUDE_CODE
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h
new file mode 100644 (file)
index 0000000..f870507
--- /dev/null
@@ -0,0 +1,475 @@
+uint32_t nvf0_grgpc_data[] = {
+/* 0x0000: gpc_mmio_list_head */
+       0x0000006c,
+/* 0x0004: gpc_mmio_list_tail */
+/* 0x0004: tpc_mmio_list_head */
+       0x0000006c,
+/* 0x0008: tpc_mmio_list_tail */
+/* 0x0008: unk_mmio_list_head */
+       0x0000006c,
+/* 0x000c: unk_mmio_list_tail */
+       0x0000006c,
+/* 0x0010: gpc_id */
+       0x00000000,
+/* 0x0014: tpc_count */
+       0x00000000,
+/* 0x0018: tpc_mask */
+       0x00000000,
+/* 0x001c: unk_count */
+       0x00000000,
+/* 0x0020: unk_mask */
+       0x00000000,
+/* 0x0024: cmd_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
+
+uint32_t nvf0_grgpc_code[] = {
+       0x03180ef5,
+/* 0x0004: queue_put */
+       0x9800d898,
+       0x86f001d9,
+       0x0489b808,
+       0xf00c1bf4,
+       0x21f502f7,
+       0x00f802fe,
+/* 0x001c: queue_put_next */
+       0xb60798c4,
+       0x8dbb0384,
+       0x0880b600,
+       0x80008e80,
+       0x90b6018f,
+       0x0f94f001,
+       0xf801d980,
+/* 0x0039: queue_get */
+       0x0131f400,
+       0x9800d898,
+       0x89b801d9,
+       0x210bf404,
+       0xb60789c4,
+       0x9dbb0394,
+       0x0890b600,
+       0x98009e98,
+       0x80b6019f,
+       0x0f84f001,
+       0xf400d880,
+/* 0x0066: queue_get_done */
+       0x00f80132,
+/* 0x0068: nv_rd32 */
+       0x0728b7f1,
+       0xb906b4b6,
+       0xc9f002ec,
+       0x00bcd01f,
+/* 0x0078: nv_rd32_wait */
+       0xc800bccf,
+       0x1bf41fcc,
+       0x06a7f0fa,
+       0x010921f5,
+       0xf840bfcf,
+/* 0x008d: nv_wr32 */
+       0x28b7f100,
+       0x06b4b607,
+       0xb980bfd0,
+       0xc9f002ec,
+       0x1ec9f01f,
+/* 0x00a3: nv_wr32_wait */
+       0xcf00bcd0,
+       0xccc800bc,
+       0xfa1bf41f,
+/* 0x00ae: watchdog_reset */
+       0x87f100f8,
+       0x84b60430,
+       0x1ff9f006,
+       0xf8008fd0,
+/* 0x00bd: watchdog_clear */
+       0x3087f100,
+       0x0684b604,
+       0xf80080d0,
+/* 0x00c9: wait_donez */
+       0xf094bd00,
+       0x07f10099,
+       0x03f03700,
+       0x0009d002,
+       0x07f104bd,
+       0x03f00600,
+       0x000ad002,
+/* 0x00e6: wait_donez_ne */
+       0x87f104bd,
+       0x83f00000,
+       0x0088cf01,
+       0xf4888aff,
+       0x94bdf31b,
+       0xf10099f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0109: wait_doneo */
+       0xf094bd00,
+       0x07f10099,
+       0x03f03700,
+       0x0009d002,
+       0x87f104bd,
+       0x84b60818,
+       0x008ad006,
+/* 0x0124: wait_doneo_e */
+       0x040087f1,
+       0xcf0684b6,
+       0x8aff0088,
+       0xf30bf488,
+       0x99f094bd,
+       0x0007f100,
+       0x0203f017,
+       0xbd0009d0,
+/* 0x0147: mmctx_size */
+       0xbd00f804,
+/* 0x0149: nv_mmctx_size_loop */
+       0x00e89894,
+       0xb61a85b6,
+       0x84b60180,
+       0x0098bb02,
+       0xb804e0b6,
+       0x1bf404ef,
+       0x029fb9eb,
+/* 0x0166: mmctx_xfer */
+       0x94bd00f8,
+       0xf10199f0,
+       0xf0370007,
+       0x09d00203,
+       0xf104bd00,
+       0xb6071087,
+       0x94bd0684,
+       0xf405bbfd,
+       0x8bd0090b,
+       0x0099f000,
+/* 0x018c: mmctx_base_disabled */
+       0xf405eefd,
+       0x8ed00c0b,
+       0xc08fd080,
+/* 0x019b: mmctx_multi_disabled */
+       0xb70199f0,
+       0xc8010080,
+       0xb4b600ab,
+       0x0cb9f010,
+       0xb601aec8,
+       0xbefd11e4,
+       0x008bd005,
+/* 0x01b4: mmctx_exec_loop */
+/* 0x01b4: mmctx_wait_free */
+       0xf0008ecf,
+       0x0bf41fe4,
+       0x00ce98fa,
+       0xd005e9fd,
+       0xc0b6c08e,
+       0x04cdb804,
+       0xc8e81bf4,
+       0x1bf402ab,
+/* 0x01d5: mmctx_fini_wait */
+       0x008bcf18,
+       0xb01fb4f0,
+       0x1bf410b4,
+       0x02a7f0f7,
+       0xf4c921f4,
+/* 0x01ea: mmctx_stop */
+       0xabc81b0e,
+       0x10b4b600,
+       0xf00cb9f0,
+       0x8bd012b9,
+/* 0x01f9: mmctx_stop_wait */
+       0x008bcf00,
+       0xf412bbc8,
+/* 0x0202: mmctx_done */
+       0x94bdfa1b,
+       0xf10199f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0215: strand_wait */
+       0xf0a0f900,
+       0x21f402a7,
+       0xf8a0fcc9,
+/* 0x0221: strand_pre */
+       0xfc87f100,
+       0x0283f04a,
+       0xd00c97f0,
+       0x21f50089,
+       0x00f80215,
+/* 0x0234: strand_post */
+       0x4afc87f1,
+       0xf00283f0,
+       0x89d00d97,
+       0x1521f500,
+/* 0x0247: strand_set */
+       0xf100f802,
+       0xf04ffca7,
+       0xaba202a3,
+       0xc7f00500,
+       0x00acd00f,
+       0xd00bc7f0,
+       0x21f500bc,
+       0xaed00215,
+       0x0ac7f000,
+       0xf500bcd0,
+       0xf8021521,
+/* 0x0271: strand_ctx_init */
+       0xf094bd00,
+       0x07f10399,
+       0x03f03700,
+       0x0009d002,
+       0x21f504bd,
+       0xe7f00221,
+       0x4721f503,
+       0xfca7f102,
+       0x02a3f046,
+       0x0400aba0,
+       0xf040a0d0,
+       0xbcd001c7,
+       0x1521f500,
+       0x010c9202,
+       0xf000acd0,
+       0xbcd002c7,
+       0x1521f500,
+       0x3421f502,
+       0x8087f102,
+       0x0684b608,
+       0xb70089cf,
+       0x95220080,
+/* 0x02ca: ctx_init_strand_loop */
+       0x8ed008fe,
+       0x408ed000,
+       0xb6808acf,
+       0xa0b606a5,
+       0x00eabb01,
+       0xb60480b6,
+       0x1bf40192,
+       0x08e4b6e8,
+       0xbdf2efbc,
+       0x0399f094,
+       0x170007f1,
+       0xd00203f0,
+       0x04bd0009,
+/* 0x02fe: error */
+       0xe0f900f8,
+       0x9814e7f1,
+       0xf440e3f0,
+       0xe0b78d21,
+       0xf7f0041c,
+       0x8d21f401,
+       0x00f8e0fc,
+/* 0x0318: init */
+       0x04fe04bd,
+       0x0017f100,
+       0x0227f012,
+       0xf10012d0,
+       0xfe047017,
+       0x17f10010,
+       0x10d00400,
+       0x0427f0c0,
+       0xf40012d0,
+       0x17f11031,
+       0x14b60608,
+       0x0012cf06,
+       0xf00137f0,
+       0x32bb1f24,
+       0x0132b604,
+       0x80050280,
+       0x10b70603,
+       0x12cf0400,
+       0x04028000,
+       0x0c30e7f1,
+       0xbd50e3f0,
+       0xbd34bd24,
+/* 0x0371: init_unk_loop */
+       0x6821f444,
+       0xf400f6b0,
+       0xf7f00f0b,
+       0x04f2bb01,
+       0xb6054ffd,
+/* 0x0386: init_unk_next */
+       0x20b60130,
+       0x04e0b601,
+       0xf40226b0,
+/* 0x0392: init_unk_done */
+       0x0380e21b,
+       0x08048007,
+       0x010027f1,
+       0xcf0223f0,
+       0x34bd0022,
+       0x070047f1,
+       0x950644b6,
+       0x45d00825,
+       0x4045d000,
+       0x98000e98,
+       0x21f5010f,
+       0x2fbb0147,
+       0x003fbb00,
+       0x98010e98,
+       0x21f5020f,
+       0x0e980147,
+       0x00effd05,
+       0xbb002ebb,
+       0x0e98003e,
+       0x030f9802,
+       0x014721f5,
+       0xfd070e98,
+       0x2ebb00ef,
+       0x003ebb00,
+       0x130040b7,
+       0xd00235b6,
+       0x25b60043,
+       0x0635b608,
+       0xb60120b6,
+       0x24b60130,
+       0x0834b608,
+       0xf5022fb9,
+       0xbb027121,
+       0x07f1003f,
+       0x03f00100,
+       0x0003d002,
+       0x24bd04bd,
+       0xf11f29f0,
+       0xf0300007,
+       0x02d00203,
+/* 0x0433: main */
+       0xf404bd00,
+       0x28f40031,
+       0x24d7f000,
+       0xf43921f4,
+       0xe4b0f401,
+       0x1e18f404,
+       0xf00181fe,
+       0x20bd0627,
+       0xb60412fd,
+       0x1efd01e4,
+       0x0018fe05,
+       0x04f721f5,
+/* 0x0463: main_not_ctx_xfer */
+       0x94d30ef4,
+       0xf5f010ef,
+       0xfe21f501,
+       0xc60ef402,
+/* 0x0470: ih */
+       0x88fe80f9,
+       0xf980f901,
+       0xf9a0f990,
+       0xf9d0f9b0,
+       0xbdf0f9e0,
+       0x800acf04,
+       0xf404abc4,
+       0xb7f11d0b,
+       0xd7f01900,
+       0x40becf24,
+       0xf400bfcf,
+       0xb0b70421,
+       0xe7f00400,
+       0x00bed001,
+/* 0x04a8: ih_no_fifo */
+       0xfc400ad0,
+       0xfce0fcf0,
+       0xfcb0fcd0,
+       0xfc90fca0,
+       0x0088fe80,
+       0x32f480fc,
+/* 0x04c3: hub_barrier_done */
+       0xf001f800,
+       0x0e9801f7,
+       0x04febb04,
+       0x9418e7f1,
+       0xf440e3f0,
+       0x00f88d21,
+/* 0x04d8: ctx_redswitch */
+       0x0614e7f1,
+       0xf006e4b6,
+       0xefd020f7,
+       0x08f7f000,
+/* 0x04e8: ctx_redswitch_delay */
+       0xf401f2b6,
+       0xf7f1fd1b,
+       0xefd00a20,
+/* 0x04f7: ctx_xfer */
+       0xf100f800,
+       0xb60a0417,
+       0x1fd00614,
+       0x0711f400,
+       0x04d821f5,
+/* 0x0508: ctx_xfer_not_load */
+       0x4afc17f1,
+       0xf00213f0,
+       0x12d00c27,
+       0x1521f500,
+       0xfc27f102,
+       0x0223f047,
+       0xf00020d0,
+       0x20b6012c,
+       0x0012d003,
+       0xf001acf0,
+       0xb7f002a5,
+       0x50b3f000,
+       0xb6040c98,
+       0xbcbb0fc4,
+       0x000c9800,
+       0xf0010d98,
+       0x21f500e7,
+       0xacf00166,
+       0x00b7f101,
+       0x50b3f040,
+       0xb6040c98,
+       0xbcbb0fc4,
+       0x010c9800,
+       0x98020d98,
+       0xe7f1060f,
+       0x21f50800,
+       0xacf00166,
+       0x04a5f001,
+       0x3000b7f1,
+       0x9850b3f0,
+       0xc4b6040c,
+       0x00bcbb0f,
+       0x98020c98,
+       0x0f98030d,
+       0x00e7f108,
+       0x6621f502,
+       0x1521f501,
+       0x0601f402,
+/* 0x05a3: ctx_xfer_post */
+       0xf11412f4,
+       0xf04afc17,
+       0x27f00213,
+       0x0012d00d,
+       0x021521f5,
+/* 0x05b4: ctx_xfer_done */
+       0x04c321f5,
+       0x000000f8,
+       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/engine/graph/fuc/hub.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc
new file mode 100644 (file)
index 0000000..b82d2ae
--- /dev/null
@@ -0,0 +1,724 @@
+/* fuc microcode for nvc0 PGRAPH/HUB
+ *
+ * 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
+ */
+
+#ifdef INCLUDE_DATA
+hub_mmio_list_head:    .b32 #hub_mmio_list_base
+hub_mmio_list_tail:    .b32 #hub_mmio_list_next
+
+gpc_count:             .b32 0
+rop_count:             .b32 0
+cmd_queue:             queue_init
+
+ctx_current:           .b32 0
+
+.align 256
+chan_data:
+chan_mmio_count:       .b32 0
+chan_mmio_address:     .b32 0
+
+.align 256
+xfer_data:             .skip 256
+
+hub_mmio_list_base:
+.b32 0x0417e91c // 0x17e91c, 2
+hub_mmio_list_next:
+#endif
+
+#ifdef INCLUDE_CODE
+// reports an exception to the host
+//
+// In: $r15 error code (see nvc0.fuc)
+//
+error:
+       nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(5), 0, $r15)
+       mov $r15 1
+       nv_iowr(NV_PGRAPH_FECS_INTR_UP_SET, 0, $r15)
+       ret
+
+// HUB fuc initialisation, executed by triggering ucode start, will
+// fall through to main loop after completion.
+//
+// Output:
+//   CC_SCRATCH[0]:
+//          31:31: set to signal completion
+//   CC_SCRATCH[1]:
+//           31:0: total PGRAPH context size
+//
+init:
+       clear b32 $r0
+       mov $sp $r0
+       mov $xdbase $r0
+
+       // enable fifo access
+       mov $r1 0x1200
+       mov $r2 2
+       iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE
+
+       // setup i0 handler, and route all interrupts to it
+       mov $r1 #ih
+       mov $iv0 $r1
+       mov $r1 0x400
+       iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH
+
+       // route HUB_CHANNEL_SWITCH to fuc interrupt 8
+       mov $r3 0x404
+       shl b32 $r3 6
+       mov $r2 0x2003          // { HUB_CHANNEL_SWITCH, ZERO } -> intr 8
+       iowr I[$r3 + 0x000] $r2
+
+       // not sure what these are, route them because NVIDIA does, and
+       // the IRQ handler will signal the host if we ever get one.. we
+       // may find out if/why we need to handle these if so..
+       //
+       mov $r2 0x2004
+       iowr I[$r3 + 0x004] $r2 // { 0x04, ZERO } -> intr 9
+       mov $r2 0x200b
+       iowr I[$r3 + 0x008] $r2 // { 0x0b, ZERO } -> intr 10
+       mov $r2 0x200c
+       iowr I[$r3 + 0x01c] $r2 // { 0x0c, ZERO } -> intr 15
+
+       // enable all INTR_UP interrupts
+       mov $r2 0xc24
+       shl b32 $r2 6
+       not b32 $r3 $r0
+       iowr I[$r2] $r3
+
+       // enable fifo, ctxsw, 9, 10, 15 interrupts
+       mov $r2 -0x78fc         // 0x8704
+       sethi $r2 0
+       iowr I[$r1 + 0x000] $r2 // INTR_EN_SET
+
+       // fifo level triggered, rest edge
+       sub b32 $r1 0x100
+       mov $r2 4
+       iowr I[$r1] $r2
+
+       // enable interrupts
+       bset $flags ie0
+
+       // fetch enabled GPC/ROP counts
+       mov $r14 -0x69fc        // 0x409604
+       sethi $r14 0x400000
+       call #nv_rd32
+       extr $r1 $r15 16:20
+       st b32 D[$r0 + #rop_count] $r1
+       and $r15 0x1f
+       st b32 D[$r0 + #gpc_count] $r15
+
+       // set BAR_REQMASK to GPC mask
+       mov $r1 1
+       shl b32 $r1 $r15
+       sub b32 $r1 1
+       mov $r2 0x40c
+       shl b32 $r2 6
+       iowr I[$r2 + 0x000] $r1
+       iowr I[$r2 + 0x100] $r1
+
+       // context size calculation, reserve first 256 bytes for use by fuc
+       mov $r1 256
+
+       // calculate size of mmio context data
+       ld b32 $r14 D[$r0 + #hub_mmio_list_head]
+       ld b32 $r15 D[$r0 + #hub_mmio_list_tail]
+       call #mmctx_size
+
+       // set mmctx base addresses now so we don't have to do it later,
+       // they don't (currently) ever change
+       mov $r3 0x700
+       shl b32 $r3 6
+       shr b32 $r4 $r1 8
+       iowr I[$r3 + 0x000] $r4         // MMCTX_SAVE_SWBASE
+       iowr I[$r3 + 0x100] $r4         // MMCTX_LOAD_SWBASE
+       add b32 $r3 0x1300
+       add b32 $r1 $r15
+       shr b32 $r15 2
+       iowr I[$r3 + 0x000] $r15        // MMCTX_LOAD_COUNT, wtf for?!?
+
+       // strands, base offset needs to be aligned to 256 bytes
+       shr b32 $r1 8
+       add b32 $r1 1
+       shl b32 $r1 8
+       mov b32 $r15 $r1
+       call #strand_ctx_init
+       add b32 $r1 $r15
+
+       // initialise each GPC in sequence by passing in the offset of its
+       // context data in GPCn_CC_SCRATCH[1], and starting its FUC (which
+       // has previously been uploaded by the host) running.
+       //
+       // the GPC fuc init sequence will set GPCn_CC_SCRATCH[0] bit 31
+       // when it has completed, and return the size of its context data
+       // in GPCn_CC_SCRATCH[1]
+       //
+       ld b32 $r3 D[$r0 + #gpc_count]
+       mov $r4 0x2000
+       sethi $r4 0x500000
+       init_gpc:
+               // setup, and start GPC ucode running
+               add b32 $r14 $r4 0x804
+               mov b32 $r15 $r1
+               call #nv_wr32                   // CC_SCRATCH[1] = ctx offset
+               add b32 $r14 $r4 0x10c
+               clear b32 $r15
+               call #nv_wr32
+               add b32 $r14 $r4 0x104
+               call #nv_wr32                   // ENTRY
+               add b32 $r14 $r4 0x100
+               mov $r15 2                      // CTRL_START_TRIGGER
+               call #nv_wr32                   // CTRL
+
+               // wait for it to complete, and adjust context size
+               add b32 $r14 $r4 0x800
+               init_gpc_wait:
+                       call #nv_rd32
+                       xbit $r15 $r15 31
+                       bra e #init_gpc_wait
+               add b32 $r14 $r4 0x804
+               call #nv_rd32
+               add b32 $r1 $r15
+
+               // next!
+               add b32 $r4 0x8000
+               sub b32 $r3 1
+               bra ne #init_gpc
+
+       // save context size, and tell host we're ready
+       nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(1), 0, $r1)
+       clear b32 $r1
+       bset $r1 31
+       nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_SET(0), 0, $r1)
+
+// Main program loop, very simple, sleeps until woken up by the interrupt
+// handler, pulls a command from the queue and executes its handler
+//
+main:
+       // sleep until we have something to do
+       bset $flags $p0
+       sleep $p0
+       mov $r13 #cmd_queue
+       call #queue_get
+       bra $p1 #main
+
+       // context switch, requested by GPU?
+       cmpu b32 $r14 0x4001
+       bra ne #main_not_ctx_switch
+               trace_set(T_AUTO)
+               mov $r1 0xb00
+               shl b32 $r1 6
+               iord $r2 I[$r1 + 0x100]         // CHAN_NEXT
+               iord $r1 I[$r1 + 0x000]         // CHAN_CUR
+
+               xbit $r3 $r1 31
+               bra e #chsw_no_prev
+                       xbit $r3 $r2 31
+                       bra e #chsw_prev_no_next
+                               push $r2
+                               mov b32 $r2 $r1
+                               trace_set(T_SAVE)
+                               bclr $flags $p1
+                               bset $flags $p2
+                               call #ctx_xfer
+                               trace_clr(T_SAVE);
+                               pop $r2
+                               trace_set(T_LOAD);
+                               bset $flags $p1
+                               call #ctx_xfer
+                               trace_clr(T_LOAD);
+                               bra #chsw_done
+                       chsw_prev_no_next:
+                               push $r2
+                               mov b32 $r2 $r1
+                               bclr $flags $p1
+                               bclr $flags $p2
+                               call #ctx_xfer
+                               pop $r2
+                               mov $r1 0xb00
+                               shl b32 $r1 6
+                               iowr I[$r1] $r2
+                               bra #chsw_done
+               chsw_no_prev:
+                       xbit $r3 $r2 31
+                       bra e #chsw_done
+                               bset $flags $p1
+                               bclr $flags $p2
+                               call #ctx_xfer
+
+               // ack the context switch request
+               chsw_done:
+               mov $r1 0xb0c
+               shl b32 $r1 6
+               mov $r2 1
+               iowr I[$r1 + 0x000] $r2         // 0x409b0c
+               trace_clr(T_AUTO)
+               bra #main
+
+       // request to set current channel? (*not* a context switch)
+       main_not_ctx_switch:
+       cmpu b32 $r14 0x0001
+       bra ne #main_not_ctx_chan
+               mov b32 $r2 $r15
+               call #ctx_chan
+               bra #main_done
+
+       // request to store current channel context?
+       main_not_ctx_chan:
+       cmpu b32 $r14 0x0002
+       bra ne #main_not_ctx_save
+               trace_set(T_SAVE)
+               bclr $flags $p1
+               bclr $flags $p2
+               call #ctx_xfer
+               trace_clr(T_SAVE)
+               bra #main_done
+
+       main_not_ctx_save:
+               shl b32 $r15 $r14 16
+               or $r15 E_BAD_COMMAND
+               call #error
+               bra #main
+
+       main_done:
+       clear b32 $r2
+       bset $r2 31
+       nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_SET(0), 0, $r2)
+       bra #main
+
+// interrupt handler
+ih:
+       push $r8
+       mov $r8 $flags
+       push $r8
+       push $r9
+       push $r10
+       push $r11
+       push $r13
+       push $r14
+       push $r15
+       clear b32 $r0
+
+       // incoming fifo command?
+       iord $r10 I[$r0 + 0x200]        // INTR
+       and $r11 $r10 0x00000004
+       bra e #ih_no_fifo
+               // queue incoming fifo command for later processing
+               mov $r11 0x1900
+               mov $r13 #cmd_queue
+               iord $r14 I[$r11 + 0x100]       // FIFO_CMD
+               iord $r15 I[$r11 + 0x000]       // FIFO_DATA
+               call #queue_put
+               add b32 $r11 0x400
+               mov $r14 1
+               iowr I[$r11 + 0x000] $r14       // FIFO_ACK
+
+       // context switch request?
+       ih_no_fifo:
+       and $r11 $r10 0x00000100
+       bra e #ih_no_ctxsw
+               // enqueue a context switch for later processing
+               mov $r13 #cmd_queue
+               mov $r14 0x4001
+               call #queue_put
+
+       // anything we didn't handle, bring it to the host's attention
+       ih_no_ctxsw:
+       mov $r11 0x104
+       not b32 $r11
+       and $r11 $r10 $r11
+       bra e #ih_no_other
+               mov $r10 0xc1c
+               shl b32 $r10 6
+               iowr I[$r10] $r11       // INTR_UP_SET
+
+       // ack, and wake up main()
+       ih_no_other:
+       iowr I[$r0 + 0x100] $r10        // INTR_ACK
+
+       pop $r15
+       pop $r14
+       pop $r13
+       pop $r11
+       pop $r10
+       pop $r9
+       pop $r8
+       mov $flags $r8
+       pop $r8
+       bclr $flags $p0
+       iret
+
+#if CHIPSET < GK100
+// Not real sure, but, MEM_CMD 7 will hang forever if this isn't done
+ctx_4160s:
+       mov $r14 0x4160
+       sethi $r14 0x400000
+       mov $r15 1
+       call #nv_wr32
+       ctx_4160s_wait:
+               call #nv_rd32
+               xbit $r15 $r15 4
+               bra e #ctx_4160s_wait
+       ret
+
+// Without clearing again at end of xfer, some things cause PGRAPH
+// to hang with STATUS=0x00000007 until it's cleared.. fbcon can
+// still function with it set however...
+ctx_4160c:
+       mov $r14 0x4160
+       sethi $r14 0x400000
+       clear b32 $r15
+       call #nv_wr32
+       ret
+#endif
+
+// Again, not real sure
+//
+// In: $r15 value to set 0x404170 to
+//
+ctx_4170s:
+       mov $r14 0x4170
+       sethi $r14 0x400000
+       or $r15 0x10
+       call #nv_wr32
+       ret
+
+// Waits for a ctx_4170s() call to complete
+//
+ctx_4170w:
+       mov $r14 0x4170
+       sethi $r14 0x400000
+       call #nv_rd32
+       and $r15 0x10
+       bra ne #ctx_4170w
+       ret
+
+// Disables various things, waits a bit, and re-enables them..
+//
+// Not sure how exactly this helps, perhaps "ENABLE" is not such a
+// good description for the bits we turn off?  Anyways, without this,
+// funny things happen.
+//
+ctx_redswitch:
+       mov $r14 0x614
+       shl b32 $r14 6
+       mov $r15 0x270
+       iowr I[$r14] $r15       // HUB_RED_SWITCH = ENABLE_GPC, POWER_ALL
+       mov $r15 8
+       ctx_redswitch_delay:
+               sub b32 $r15 1
+               bra ne #ctx_redswitch_delay
+       mov $r15 0x770
+       iowr I[$r14] $r15       // HUB_RED_SWITCH = ENABLE_ALL, POWER_ALL
+       ret
+
+// Not a clue what this is for, except that unless the value is 0x10, the
+// strand context is saved (and presumably restored) incorrectly..
+//
+// In: $r15 value to set to (0x00/0x10 are used)
+//
+ctx_86c:
+       mov $r14 0x86c
+       shl b32 $r14 6
+       iowr I[$r14] $r15       // HUB(0x86c) = val
+       mov $r14 -0x75ec
+       sethi $r14 0x400000
+       call #nv_wr32           // ROP(0xa14) = val
+       mov $r14 -0x5794
+       sethi $r14 0x410000
+       call #nv_wr32           // GPC(0x86c) = val
+       ret
+
+// ctx_load - load's a channel's ctxctl data, and selects its vm
+//
+// In: $r2 channel address
+//
+ctx_load:
+       trace_set(T_CHAN)
+
+       // switch to channel, somewhat magic in parts..
+       mov $r10 12             // DONE_UNK12
+       call #wait_donez
+       mov $r1 0xa24
+       shl b32 $r1 6
+       iowr I[$r1 + 0x000] $r0 // 0x409a24
+       mov $r3 0xb00
+       shl b32 $r3 6
+       iowr I[$r3 + 0x100] $r2 // CHAN_NEXT
+       mov $r1 0xa0c
+       shl b32 $r1 6
+       mov $r4 7
+       iowr I[$r1 + 0x000] $r2 // MEM_CHAN
+       iowr I[$r1 + 0x100] $r4 // MEM_CMD
+       ctx_chan_wait_0:
+               iord $r4 I[$r1 + 0x100]
+               and $r4 0x1f
+               bra ne #ctx_chan_wait_0
+       iowr I[$r3 + 0x000] $r2 // CHAN_CUR
+
+       // load channel header, fetch PGRAPH context pointer
+       mov $xtargets $r0
+       bclr $r2 31
+       shl b32 $r2 4
+       add b32 $r2 2
+
+       trace_set(T_LCHAN)
+       mov $r1 0xa04
+       shl b32 $r1 6
+       iowr I[$r1 + 0x000] $r2         // MEM_BASE
+       mov $r1 0xa20
+       shl b32 $r1 6
+       mov $r2 0x0002
+       sethi $r2 0x80000000
+       iowr I[$r1 + 0x000] $r2         // MEM_TARGET = vram
+       mov $r1 0x10                    // chan + 0x0210
+       mov $r2 #xfer_data
+       sethi $r2 0x00020000            // 16 bytes
+       xdld $r1 $r2
+       xdwait
+       trace_clr(T_LCHAN)
+
+       // update current context
+       ld b32 $r1 D[$r0 + #xfer_data + 4]
+       shl b32 $r1 24
+       ld b32 $r2 D[$r0 + #xfer_data + 0]
+       shr b32 $r2 8
+       or $r1 $r2
+       st b32 D[$r0 + #ctx_current] $r1
+
+       // set transfer base to start of context, and fetch context header
+       trace_set(T_LCTXH)
+       mov $r2 0xa04
+       shl b32 $r2 6
+       iowr I[$r2 + 0x000] $r1         // MEM_BASE
+       mov $r2 1
+       mov $r1 0xa20
+       shl b32 $r1 6
+       iowr I[$r1 + 0x000] $r2         // MEM_TARGET = vm
+       mov $r1 #chan_data
+       sethi $r1 0x00060000            // 256 bytes
+       xdld $r0 $r1
+       xdwait
+       trace_clr(T_LCTXH)
+
+       trace_clr(T_CHAN)
+       ret
+
+// ctx_chan - handler for HUB_SET_CHAN command, will set a channel as
+//            the active channel for ctxctl, but not actually transfer
+//            any context data.  intended for use only during initial
+//            context construction.
+//
+// In: $r2 channel address
+//
+ctx_chan:
+#if CHIPSET < GK100
+       call #ctx_4160s
+#endif
+       call #ctx_load
+       mov $r10 12                     // DONE_UNK12
+       call #wait_donez
+       mov $r1 0xa10
+       shl b32 $r1 6
+       mov $r2 5
+       iowr I[$r1 + 0x000] $r2         // MEM_CMD = 5 (???)
+       ctx_chan_wait:
+               iord $r2 I[$r1 + 0x000]
+               or $r2 $r2
+               bra ne #ctx_chan_wait
+#if CHIPSET < GK100
+       call #ctx_4160c
+#endif
+       ret
+
+// Execute per-context state overrides list
+//
+// Only executed on the first load of a channel.  Might want to look into
+// removing this and having the host directly modify the channel's context
+// to change this state...  The nouveau DRM already builds this list as
+// it's definitely needed for NVIDIA's, so we may as well use it for now
+//
+// Input: $r1 mmio list length
+//
+ctx_mmio_exec:
+       // set transfer base to be the mmio list
+       ld b32 $r3 D[$r0 + #chan_mmio_address]
+       mov $r2 0xa04
+       shl b32 $r2 6
+       iowr I[$r2 + 0x000] $r3         // MEM_BASE
+
+       clear b32 $r3
+       ctx_mmio_loop:
+               // fetch next 256 bytes of mmio list if necessary
+               and $r4 $r3 0xff
+               bra ne #ctx_mmio_pull
+                       mov $r5 #xfer_data
+                       sethi $r5 0x00060000    // 256 bytes
+                       xdld $r3 $r5
+                       xdwait
+
+               // execute a single list entry
+               ctx_mmio_pull:
+               ld b32 $r14 D[$r4 + #xfer_data + 0x00]
+               ld b32 $r15 D[$r4 + #xfer_data + 0x04]
+               call #nv_wr32
+
+               // next!
+               add b32 $r3 8
+               sub b32 $r1 1
+               bra ne #ctx_mmio_loop
+
+       // set transfer base back to the current context
+       ctx_mmio_done:
+       ld b32 $r3 D[$r0 + #ctx_current]
+       iowr I[$r2 + 0x000] $r3         // MEM_BASE
+
+       // disable the mmio list now, we don't need/want to execute it again
+       st b32 D[$r0 + #chan_mmio_count] $r0
+       mov $r1 #chan_data
+       sethi $r1 0x00060000            // 256 bytes
+       xdst $r0 $r1
+       xdwait
+       ret
+
+// Transfer HUB context data between GPU and storage area
+//
+// In: $r2 channel address
+//     $p1 clear on save, set on load
+//     $p2 set if opposite direction done/will be done, so:
+//             on save it means: "a load will follow this save"
+//             on load it means: "a save preceeded this load"
+//
+ctx_xfer:
+       // according to mwk, some kind of wait for idle
+       mov $r15 0xc00
+       shl b32 $r15 6
+       mov $r14 4
+       iowr I[$r15 + 0x200] $r14
+       ctx_xfer_idle:
+               iord $r14 I[$r15 + 0x000]
+               and $r14 0x2000
+               bra ne #ctx_xfer_idle
+
+       bra not $p1 #ctx_xfer_pre
+       bra $p2 #ctx_xfer_pre_load
+       ctx_xfer_pre:
+               mov $r15 0x10
+               call #ctx_86c
+#if CHIPSET < GK100
+               call #ctx_4160s
+#endif
+               bra not $p1 #ctx_xfer_exec
+
+       ctx_xfer_pre_load:
+               mov $r15 2
+               call #ctx_4170s
+               call #ctx_4170w
+               call #ctx_redswitch
+               clear b32 $r15
+               call #ctx_4170s
+               call #ctx_load
+
+       // fetch context pointer, and initiate xfer on all GPCs
+       ctx_xfer_exec:
+       ld b32 $r1 D[$r0 + #ctx_current]
+       mov $r2 0x414
+       shl b32 $r2 6
+       iowr I[$r2 + 0x000] $r0 // BAR_STATUS = reset
+       mov $r14 -0x5b00
+       sethi $r14 0x410000
+       mov b32 $r15 $r1
+       call #nv_wr32           // GPC_BCAST_WRCMD_DATA = ctx pointer
+       add b32 $r14 4
+       xbit $r15 $flags $p1
+       xbit $r2 $flags $p2
+       shl b32 $r2 1
+       or $r15 $r2
+       call #nv_wr32           // GPC_BCAST_WRCMD_CMD = GPC_XFER(type)
+
+       // strands
+       mov $r1 0x4afc
+       sethi $r1 0x20000
+       mov $r2 0xc
+       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x0c
+       call #strand_wait
+       mov $r2 0x47fc
+       sethi $r2 0x20000
+       iowr I[$r2] $r0         // STRAND_FIRST_GENE(0x3f) = 0x00
+       xbit $r2 $flags $p1
+       add b32 $r2 3
+       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD)
+
+       // mmio context
+       xbit $r10 $flags $p1    // direction
+       or $r10 6               // first, last
+       mov $r11 0              // base = 0
+       ld b32 $r12 D[$r0 + #hub_mmio_list_head]
+       ld b32 $r13 D[$r0 + #hub_mmio_list_tail]
+       mov $r14 0              // not multi
+       call #mmctx_xfer
+
+       // wait for GPCs to all complete
+       mov $r10 8              // DONE_BAR
+       call #wait_doneo
+
+       // wait for strand xfer to complete
+       call #strand_wait
+
+       // post-op
+       bra $p1 #ctx_xfer_post
+               mov $r10 12             // DONE_UNK12
+               call #wait_donez
+               mov $r1 0xa10
+               shl b32 $r1 6
+               mov $r2 5
+               iowr I[$r1] $r2         // MEM_CMD
+               ctx_xfer_post_save_wait:
+                       iord $r2 I[$r1]
+                       or $r2 $r2
+                       bra ne #ctx_xfer_post_save_wait
+
+       bra $p2 #ctx_xfer_done
+       ctx_xfer_post:
+               mov $r15 2
+               call #ctx_4170s
+               clear b32 $r15
+               call #ctx_86c
+               call #strand_post
+               call #ctx_4170w
+               clear b32 $r15
+               call #ctx_4170s
+
+               bra not $p1 #ctx_xfer_no_post_mmio
+               ld b32 $r1 D[$r0 + #chan_mmio_count]
+               or $r1 $r1
+               bra e #ctx_xfer_no_post_mmio
+                       call #ctx_mmio_exec
+
+               ctx_xfer_no_post_mmio:
+#if CHIPSET < GK100
+               call #ctx_4160c
+#endif
+
+       ctx_xfer_done:
+       ret
+#endif
index 7fbdebb..3ff52ba 100644 (file)
@@ -1,6 +1,5 @@
-/* fuc microcode for nvc0 PGRAPH/HUB
- *
- * Copyright 2011 Red Hat Inc.
+/*
+ * 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"),
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  * OTHER DEALINGS IN THE SOFTWARE.
  *
- * Authors: Ben Skeggs
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
  */
 
-/* To build:
- *    m4 hubnvc0.fuc | envyas -a -w -m fuc -V fuc3 -o hubnvc0.fuc.h
- */
+#define CHIPSET GF100
+#include "macros.fuc"
 
 .section #nvc0_grhub_data
-include(`nvc0.fuc')
-gpc_count:             .b32 0
-rop_count:             .b32 0
-cmd_queue:             queue_init
-hub_mmio_list_head:    .b32 0
-hub_mmio_list_tail:    .b32 0
-
-ctx_current:           .b32 0
-
-chipsets:
-.b8  0xc0 0 0 0
-.b16 #nvc0_hub_mmio_head
-.b16 #nvc0_hub_mmio_tail
-.b8  0xc1 0 0 0
-.b16 #nvc0_hub_mmio_head
-.b16 #nvc1_hub_mmio_tail
-.b8  0xc3 0 0 0
-.b16 #nvc0_hub_mmio_head
-.b16 #nvc0_hub_mmio_tail
-.b8  0xc4 0 0 0
-.b16 #nvc0_hub_mmio_head
-.b16 #nvc0_hub_mmio_tail
-.b8  0xc8 0 0 0
-.b16 #nvc0_hub_mmio_head
-.b16 #nvc0_hub_mmio_tail
-.b8  0xce 0 0 0
-.b16 #nvc0_hub_mmio_head
-.b16 #nvc0_hub_mmio_tail
-.b8  0xcf 0 0 0
-.b16 #nvc0_hub_mmio_head
-.b16 #nvc0_hub_mmio_tail
-.b8  0xd9 0 0 0
-.b16 #nvd9_hub_mmio_head
-.b16 #nvd9_hub_mmio_tail
-.b8  0xd7 0 0 0
-.b16 #nvd9_hub_mmio_head
-.b16 #nvd9_hub_mmio_tail
-.b8  0 0 0 0
-
-nvc0_hub_mmio_head:
-mmctx_data(0x17e91c, 2)
-mmctx_data(0x400204, 2)
-mmctx_data(0x404004, 11)
-mmctx_data(0x404044, 1)
-mmctx_data(0x404094, 14)
-mmctx_data(0x4040d0, 7)
-mmctx_data(0x4040f8, 1)
-mmctx_data(0x404130, 3)
-mmctx_data(0x404150, 3)
-mmctx_data(0x404164, 2)
-mmctx_data(0x404174, 3)
-mmctx_data(0x404200, 8)
-mmctx_data(0x404404, 14)
-mmctx_data(0x404460, 4)
-mmctx_data(0x404480, 1)
-mmctx_data(0x404498, 1)
-mmctx_data(0x404604, 4)
-mmctx_data(0x404618, 32)
-mmctx_data(0x404698, 21)
-mmctx_data(0x4046f0, 2)
-mmctx_data(0x404700, 22)
-mmctx_data(0x405800, 1)
-mmctx_data(0x405830, 3)
-mmctx_data(0x405854, 1)
-mmctx_data(0x405870, 4)
-mmctx_data(0x405a00, 2)
-mmctx_data(0x405a18, 1)
-mmctx_data(0x406020, 1)
-mmctx_data(0x406028, 4)
-mmctx_data(0x4064a8, 2)
-mmctx_data(0x4064b4, 2)
-mmctx_data(0x407804, 1)
-mmctx_data(0x40780c, 6)
-mmctx_data(0x4078bc, 1)
-mmctx_data(0x408000, 7)
-mmctx_data(0x408064, 1)
-mmctx_data(0x408800, 3)
-mmctx_data(0x408900, 4)
-mmctx_data(0x408980, 1)
-nvc0_hub_mmio_tail:
-mmctx_data(0x4064c0, 2)
-nvc1_hub_mmio_tail:
-
-nvd9_hub_mmio_head:
-mmctx_data(0x17e91c, 2)
-mmctx_data(0x400204, 2)
-mmctx_data(0x404004, 10)
-mmctx_data(0x404044, 1)
-mmctx_data(0x404094, 14)
-mmctx_data(0x4040d0, 7)
-mmctx_data(0x4040f8, 1)
-mmctx_data(0x404130, 3)
-mmctx_data(0x404150, 3)
-mmctx_data(0x404164, 2)
-mmctx_data(0x404178, 2)
-mmctx_data(0x404200, 8)
-mmctx_data(0x404404, 14)
-mmctx_data(0x404460, 4)
-mmctx_data(0x404480, 1)
-mmctx_data(0x404498, 1)
-mmctx_data(0x404604, 4)
-mmctx_data(0x404618, 32)
-mmctx_data(0x404698, 21)
-mmctx_data(0x4046f0, 2)
-mmctx_data(0x404700, 22)
-mmctx_data(0x405800, 1)
-mmctx_data(0x405830, 3)
-mmctx_data(0x405854, 1)
-mmctx_data(0x405870, 4)
-mmctx_data(0x405a00, 2)
-mmctx_data(0x405a18, 1)
-mmctx_data(0x406020, 1)
-mmctx_data(0x406028, 4)
-mmctx_data(0x4064a8, 2)
-mmctx_data(0x4064b4, 5)
-mmctx_data(0x407804, 1)
-mmctx_data(0x40780c, 6)
-mmctx_data(0x4078bc, 1)
-mmctx_data(0x408000, 7)
-mmctx_data(0x408064, 1)
-mmctx_data(0x408800, 3)
-mmctx_data(0x408900, 4)
-mmctx_data(0x408980, 1)
-nvd9_hub_mmio_tail:
-
-.align 256
-chan_data:
-chan_mmio_count:       .b32 0
-chan_mmio_address:     .b32 0
-
-.align 256
-xfer_data:             .b32 0
+#define INCLUDE_DATA
+#include "com.fuc"
+#include "hub.fuc"
+#undef INCLUDE_DATA
 
 .section #nvc0_grhub_code
+#define INCLUDE_CODE
 bra #init
-define(`include_code')
-include(`nvc0.fuc')
-
-// reports an exception to the host
-//
-// In: $r15 error code (see nvc0.fuc)
-//
-error:
-       push $r14
-       mov $r14 0x814
-       shl b32 $r14 6
-       iowr I[$r14 + 0x000] $r15       // CC_SCRATCH[5] = error code
-       mov $r14 0xc1c
-       shl b32 $r14 6
-       mov $r15 1
-       iowr I[$r14 + 0x000] $r15       // INTR_UP_SET
-       pop $r14
-       ret
-
-// HUB fuc initialisation, executed by triggering ucode start, will
-// fall through to main loop after completion.
-//
-// Input:
-//   CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh)
-//
-// Output:
-//   CC_SCRATCH[0]:
-//          31:31: set to signal completion
-//   CC_SCRATCH[1]:
-//           31:0: total PGRAPH context size
-//
-init:
-       clear b32 $r0
-       mov $sp $r0
-       mov $xdbase $r0
-
-       // enable fifo access
-       mov $r1 0x1200
-       mov $r2 2
-       iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE
-
-       // setup i0 handler, and route all interrupts to it
-       mov $r1 #ih
-       mov $iv0 $r1
-       mov $r1 0x400
-       iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH
-
-       // route HUB_CHANNEL_SWITCH to fuc interrupt 8
-       mov $r3 0x404
-       shl b32 $r3 6
-       mov $r2 0x2003          // { HUB_CHANNEL_SWITCH, ZERO } -> intr 8
-       iowr I[$r3 + 0x000] $r2
-
-       // not sure what these are, route them because NVIDIA does, and
-       // the IRQ handler will signal the host if we ever get one.. we
-       // may find out if/why we need to handle these if so..
-       //
-       mov $r2 0x2004
-       iowr I[$r3 + 0x004] $r2 // { 0x04, ZERO } -> intr 9
-       mov $r2 0x200b
-       iowr I[$r3 + 0x008] $r2 // { 0x0b, ZERO } -> intr 10
-       mov $r2 0x200c
-       iowr I[$r3 + 0x01c] $r2 // { 0x0c, ZERO } -> intr 15
-
-       // enable all INTR_UP interrupts
-       mov $r2 0xc24
-       shl b32 $r2 6
-       not b32 $r3 $r0
-       iowr I[$r2] $r3
-
-       // enable fifo, ctxsw, 9, 10, 15 interrupts
-       mov $r2 -0x78fc         // 0x8704
-       sethi $r2 0
-       iowr I[$r1 + 0x000] $r2 // INTR_EN_SET
-
-       // fifo level triggered, rest edge
-       sub b32 $r1 0x100
-       mov $r2 4
-       iowr I[$r1] $r2
-
-       // enable interrupts
-       bset $flags ie0
-
-       // fetch enabled GPC/ROP counts
-       mov $r14 -0x69fc        // 0x409604
-       sethi $r14 0x400000
-       call #nv_rd32
-       extr $r1 $r15 16:20
-       st b32 D[$r0 + #rop_count] $r1
-       and $r15 0x1f
-       st b32 D[$r0 + #gpc_count] $r15
-
-       // set BAR_REQMASK to GPC mask
-       mov $r1 1
-       shl b32 $r1 $r15
-       sub b32 $r1 1
-       mov $r2 0x40c
-       shl b32 $r2 6
-       iowr I[$r2 + 0x000] $r1
-       iowr I[$r2 + 0x100] $r1
-
-       // find context data for this chipset
-       mov $r2 0x800
-       shl b32 $r2 6
-       iord $r2 I[$r2 + 0x000]         // CC_SCRATCH[0]
-       mov $r15 #chipsets - 8
-       init_find_chipset:
-               add b32 $r15 8
-               ld b32 $r3 D[$r15 + 0x00]
-               cmpu b32 $r3 $r2
-               bra e #init_context
-               cmpu b32 $r3 0
-               bra ne #init_find_chipset
-               // unknown chipset
-               ret
-
-       // context size calculation, reserve first 256 bytes for use by fuc
-       init_context:
-       mov $r1 256
-
-       // calculate size of mmio context data
-       ld b16 $r14 D[$r15 + 4]
-       ld b16 $r15 D[$r15 + 6]
-       sethi $r14 0
-       st b32 D[$r0 + #hub_mmio_list_head] $r14
-       st b32 D[$r0 + #hub_mmio_list_tail] $r15
-       call #mmctx_size
-
-       // set mmctx base addresses now so we don't have to do it later,
-       // they don't (currently) ever change
-       mov $r3 0x700
-       shl b32 $r3 6
-       shr b32 $r4 $r1 8
-       iowr I[$r3 + 0x000] $r4         // MMCTX_SAVE_SWBASE
-       iowr I[$r3 + 0x100] $r4         // MMCTX_LOAD_SWBASE
-       add b32 $r3 0x1300
-       add b32 $r1 $r15
-       shr b32 $r15 2
-       iowr I[$r3 + 0x000] $r15        // MMCTX_LOAD_COUNT, wtf for?!?
-
-       // strands, base offset needs to be aligned to 256 bytes
-       shr b32 $r1 8
-       add b32 $r1 1
-       shl b32 $r1 8
-       mov b32 $r15 $r1
-       call #strand_ctx_init
-       add b32 $r1 $r15
-
-       // initialise each GPC in sequence by passing in the offset of its
-       // context data in GPCn_CC_SCRATCH[1], and starting its FUC (which
-       // has previously been uploaded by the host) running.
-       //
-       // the GPC fuc init sequence will set GPCn_CC_SCRATCH[0] bit 31
-       // when it has completed, and return the size of its context data
-       // in GPCn_CC_SCRATCH[1]
-       //
-       ld b32 $r3 D[$r0 + #gpc_count]
-       mov $r4 0x2000
-       sethi $r4 0x500000
-       init_gpc:
-               // setup, and start GPC ucode running
-               add b32 $r14 $r4 0x804
-               mov b32 $r15 $r1
-               call #nv_wr32                   // CC_SCRATCH[1] = ctx offset
-               add b32 $r14 $r4 0x800
-               mov b32 $r15 $r2
-               call #nv_wr32                   // CC_SCRATCH[0] = chipset
-               add b32 $r14 $r4 0x10c
-               clear b32 $r15
-               call #nv_wr32
-               add b32 $r14 $r4 0x104
-               call #nv_wr32                   // ENTRY
-               add b32 $r14 $r4 0x100
-               mov $r15 2                      // CTRL_START_TRIGGER
-               call #nv_wr32                   // CTRL
-
-               // wait for it to complete, and adjust context size
-               add b32 $r14 $r4 0x800
-               init_gpc_wait:
-                       call #nv_rd32
-                       xbit $r15 $r15 31
-                       bra e #init_gpc_wait
-               add b32 $r14 $r4 0x804
-               call #nv_rd32
-               add b32 $r1 $r15
-
-               // next!
-               add b32 $r4 0x8000
-               sub b32 $r3 1
-               bra ne #init_gpc
-
-       // save context size, and tell host we're ready
-       mov $r2 0x800
-       shl b32 $r2 6
-       iowr I[$r2 + 0x100] $r1         // CC_SCRATCH[1]  = context size
-       add b32 $r2 0x800
-       clear b32 $r1
-       bset $r1 31
-       iowr I[$r2 + 0x000] $r1         // CC_SCRATCH[0] |= 0x80000000
-
-// Main program loop, very simple, sleeps until woken up by the interrupt
-// handler, pulls a command from the queue and executes its handler
-//
-main:
-       // sleep until we have something to do
-       bset $flags $p0
-       sleep $p0
-       mov $r13 #cmd_queue
-       call #queue_get
-       bra $p1 #main
-
-       // context switch, requested by GPU?
-       cmpu b32 $r14 0x4001
-       bra ne #main_not_ctx_switch
-               trace_set(T_AUTO)
-               mov $r1 0xb00
-               shl b32 $r1 6
-               iord $r2 I[$r1 + 0x100]         // CHAN_NEXT
-               iord $r1 I[$r1 + 0x000]         // CHAN_CUR
-
-               xbit $r3 $r1 31
-               bra e #chsw_no_prev
-                       xbit $r3 $r2 31
-                       bra e #chsw_prev_no_next
-                               push $r2
-                               mov b32 $r2 $r1
-                               trace_set(T_SAVE)
-                               bclr $flags $p1
-                               bset $flags $p2
-                               call #ctx_xfer
-                               trace_clr(T_SAVE);
-                               pop $r2
-                               trace_set(T_LOAD);
-                               bset $flags $p1
-                               call #ctx_xfer
-                               trace_clr(T_LOAD);
-                               bra #chsw_done
-                       chsw_prev_no_next:
-                               push $r2
-                               mov b32 $r2 $r1
-                               bclr $flags $p1
-                               bclr $flags $p2
-                               call #ctx_xfer
-                               pop $r2
-                               mov $r1 0xb00
-                               shl b32 $r1 6
-                               iowr I[$r1] $r2
-                               bra #chsw_done
-               chsw_no_prev:
-                       xbit $r3 $r2 31
-                       bra e #chsw_done
-                               bset $flags $p1
-                               bclr $flags $p2
-                               call #ctx_xfer
-
-               // ack the context switch request
-               chsw_done:
-               mov $r1 0xb0c
-               shl b32 $r1 6
-               mov $r2 1
-               iowr I[$r1 + 0x000] $r2         // 0x409b0c
-               trace_clr(T_AUTO)
-               bra #main
-
-       // request to set current channel? (*not* a context switch)
-       main_not_ctx_switch:
-       cmpu b32 $r14 0x0001
-       bra ne #main_not_ctx_chan
-               mov b32 $r2 $r15
-               call #ctx_chan
-               bra #main_done
-
-       // request to store current channel context?
-       main_not_ctx_chan:
-       cmpu b32 $r14 0x0002
-       bra ne #main_not_ctx_save
-               trace_set(T_SAVE)
-               bclr $flags $p1
-               bclr $flags $p2
-               call #ctx_xfer
-               trace_clr(T_SAVE)
-               bra #main_done
-
-       main_not_ctx_save:
-               shl b32 $r15 $r14 16
-               or $r15 E_BAD_COMMAND
-               call #error
-               bra #main
-
-       main_done:
-       mov $r1 0x820
-       shl b32 $r1 6
-       clear b32 $r2
-       bset $r2 31
-       iowr I[$r1 + 0x000] $r2         // CC_SCRATCH[0] |= 0x80000000
-       bra #main
-
-// interrupt handler
-ih:
-       push $r8
-       mov $r8 $flags
-       push $r8
-       push $r9
-       push $r10
-       push $r11
-       push $r13
-       push $r14
-       push $r15
-
-       // incoming fifo command?
-       iord $r10 I[$r0 + 0x200]        // INTR
-       and $r11 $r10 0x00000004
-       bra e #ih_no_fifo
-               // queue incoming fifo command for later processing
-               mov $r11 0x1900
-               mov $r13 #cmd_queue
-               iord $r14 I[$r11 + 0x100]       // FIFO_CMD
-               iord $r15 I[$r11 + 0x000]       // FIFO_DATA
-               call #queue_put
-               add b32 $r11 0x400
-               mov $r14 1
-               iowr I[$r11 + 0x000] $r14       // FIFO_ACK
-
-       // context switch request?
-       ih_no_fifo:
-       and $r11 $r10 0x00000100
-       bra e #ih_no_ctxsw
-               // enqueue a context switch for later processing
-               mov $r13 #cmd_queue
-               mov $r14 0x4001
-               call #queue_put
-
-       // anything we didn't handle, bring it to the host's attention
-       ih_no_ctxsw:
-       mov $r11 0x104
-       not b32 $r11
-       and $r11 $r10 $r11
-       bra e #ih_no_other
-               mov $r10 0xc1c
-               shl b32 $r10 6
-               iowr I[$r10] $r11       // INTR_UP_SET
-
-       // ack, and wake up main()
-       ih_no_other:
-       iowr I[$r0 + 0x100] $r10        // INTR_ACK
-
-       pop $r15
-       pop $r14
-       pop $r13
-       pop $r11
-       pop $r10
-       pop $r9
-       pop $r8
-       mov $flags $r8
-       pop $r8
-       bclr $flags $p0
-       iret
-
-// Not real sure, but, MEM_CMD 7 will hang forever if this isn't done
-ctx_4160s:
-       mov $r14 0x4160
-       sethi $r14 0x400000
-       mov $r15 1
-       call #nv_wr32
-       ctx_4160s_wait:
-               call #nv_rd32
-               xbit $r15 $r15 4
-               bra e #ctx_4160s_wait
-       ret
-
-// Without clearing again at end of xfer, some things cause PGRAPH
-// to hang with STATUS=0x00000007 until it's cleared.. fbcon can
-// still function with it set however...
-ctx_4160c:
-       mov $r14 0x4160
-       sethi $r14 0x400000
-       clear b32 $r15
-       call #nv_wr32
-       ret
-
-// Again, not real sure
-//
-// In: $r15 value to set 0x404170 to
-//
-ctx_4170s:
-       mov $r14 0x4170
-       sethi $r14 0x400000
-       or $r15 0x10
-       call #nv_wr32
-       ret
-
-// Waits for a ctx_4170s() call to complete
-//
-ctx_4170w:
-       mov $r14 0x4170
-       sethi $r14 0x400000
-       call #nv_rd32
-       and $r15 0x10
-       bra ne #ctx_4170w
-       ret
-
-// Disables various things, waits a bit, and re-enables them..
-//
-// Not sure how exactly this helps, perhaps "ENABLE" is not such a
-// good description for the bits we turn off?  Anyways, without this,
-// funny things happen.
-//
-ctx_redswitch:
-       mov $r14 0x614
-       shl b32 $r14 6
-       mov $r15 0x270
-       iowr I[$r14] $r15       // HUB_RED_SWITCH = ENABLE_GPC, POWER_ALL
-       mov $r15 8
-       ctx_redswitch_delay:
-               sub b32 $r15 1
-               bra ne #ctx_redswitch_delay
-       mov $r15 0x770
-       iowr I[$r14] $r15       // HUB_RED_SWITCH = ENABLE_ALL, POWER_ALL
-       ret
-
-// Not a clue what this is for, except that unless the value is 0x10, the
-// strand context is saved (and presumably restored) incorrectly..
-//
-// In: $r15 value to set to (0x00/0x10 are used)
-//
-ctx_86c:
-       mov $r14 0x86c
-       shl b32 $r14 6
-       iowr I[$r14] $r15       // HUB(0x86c) = val
-       mov $r14 -0x75ec
-       sethi $r14 0x400000
-       call #nv_wr32           // ROP(0xa14) = val
-       mov $r14 -0x5794
-       sethi $r14 0x410000
-       call #nv_wr32           // GPC(0x86c) = val
-       ret
-
-// ctx_load - load's a channel's ctxctl data, and selects its vm
-//
-// In: $r2 channel address
-//
-ctx_load:
-       trace_set(T_CHAN)
-
-       // switch to channel, somewhat magic in parts..
-       mov $r10 12             // DONE_UNK12
-       call #wait_donez
-       mov $r1 0xa24
-       shl b32 $r1 6
-       iowr I[$r1 + 0x000] $r0 // 0x409a24
-       mov $r3 0xb00
-       shl b32 $r3 6
-       iowr I[$r3 + 0x100] $r2 // CHAN_NEXT
-       mov $r1 0xa0c
-       shl b32 $r1 6
-       mov $r4 7
-       iowr I[$r1 + 0x000] $r2 // MEM_CHAN
-       iowr I[$r1 + 0x100] $r4 // MEM_CMD
-       ctx_chan_wait_0:
-               iord $r4 I[$r1 + 0x100]
-               and $r4 0x1f
-               bra ne #ctx_chan_wait_0
-       iowr I[$r3 + 0x000] $r2 // CHAN_CUR
-
-       // load channel header, fetch PGRAPH context pointer
-       mov $xtargets $r0
-       bclr $r2 31
-       shl b32 $r2 4
-       add b32 $r2 2
-
-       trace_set(T_LCHAN)
-       mov $r1 0xa04
-       shl b32 $r1 6
-       iowr I[$r1 + 0x000] $r2         // MEM_BASE
-       mov $r1 0xa20
-       shl b32 $r1 6
-       mov $r2 0x0002
-       sethi $r2 0x80000000
-       iowr I[$r1 + 0x000] $r2         // MEM_TARGET = vram
-       mov $r1 0x10                    // chan + 0x0210
-       mov $r2 #xfer_data
-       sethi $r2 0x00020000            // 16 bytes
-       xdld $r1 $r2
-       xdwait
-       trace_clr(T_LCHAN)
-
-       // update current context
-       ld b32 $r1 D[$r0 + #xfer_data + 4]
-       shl b32 $r1 24
-       ld b32 $r2 D[$r0 + #xfer_data + 0]
-       shr b32 $r2 8
-       or $r1 $r2
-       st b32 D[$r0 + #ctx_current] $r1
-
-       // set transfer base to start of context, and fetch context header
-       trace_set(T_LCTXH)
-       mov $r2 0xa04
-       shl b32 $r2 6
-       iowr I[$r2 + 0x000] $r1         // MEM_BASE
-       mov $r2 1
-       mov $r1 0xa20
-       shl b32 $r1 6
-       iowr I[$r1 + 0x000] $r2         // MEM_TARGET = vm
-       mov $r1 #chan_data
-       sethi $r1 0x00060000            // 256 bytes
-       xdld $r0 $r1
-       xdwait
-       trace_clr(T_LCTXH)
-
-       trace_clr(T_CHAN)
-       ret
-
-// ctx_chan - handler for HUB_SET_CHAN command, will set a channel as
-//            the active channel for ctxctl, but not actually transfer
-//            any context data.  intended for use only during initial
-//            context construction.
-//
-// In: $r2 channel address
-//
-ctx_chan:
-       call #ctx_4160s
-       call #ctx_load
-       mov $r10 12                     // DONE_UNK12
-       call #wait_donez
-       mov $r1 0xa10
-       shl b32 $r1 6
-       mov $r2 5
-       iowr I[$r1 + 0x000] $r2         // MEM_CMD = 5 (???)
-       ctx_chan_wait:
-               iord $r2 I[$r1 + 0x000]
-               or $r2 $r2
-               bra ne #ctx_chan_wait
-       call #ctx_4160c
-       ret
-
-// Execute per-context state overrides list
-//
-// Only executed on the first load of a channel.  Might want to look into
-// removing this and having the host directly modify the channel's context
-// to change this state...  The nouveau DRM already builds this list as
-// it's definitely needed for NVIDIA's, so we may as well use it for now
-//
-// Input: $r1 mmio list length
-//
-ctx_mmio_exec:
-       // set transfer base to be the mmio list
-       ld b32 $r3 D[$r0 + #chan_mmio_address]
-       mov $r2 0xa04
-       shl b32 $r2 6
-       iowr I[$r2 + 0x000] $r3         // MEM_BASE
-
-       clear b32 $r3
-       ctx_mmio_loop:
-               // fetch next 256 bytes of mmio list if necessary
-               and $r4 $r3 0xff
-               bra ne #ctx_mmio_pull
-                       mov $r5 #xfer_data
-                       sethi $r5 0x00060000    // 256 bytes
-                       xdld $r3 $r5
-                       xdwait
-
-               // execute a single list entry
-               ctx_mmio_pull:
-               ld b32 $r14 D[$r4 + #xfer_data + 0x00]
-               ld b32 $r15 D[$r4 + #xfer_data + 0x04]
-               call #nv_wr32
-
-               // next!
-               add b32 $r3 8
-               sub b32 $r1 1
-               bra ne #ctx_mmio_loop
-
-       // set transfer base back to the current context
-       ctx_mmio_done:
-       ld b32 $r3 D[$r0 + #ctx_current]
-       iowr I[$r2 + 0x000] $r3         // MEM_BASE
-
-       // disable the mmio list now, we don't need/want to execute it again
-       st b32 D[$r0 + #chan_mmio_count] $r0
-       mov $r1 #chan_data
-       sethi $r1 0x00060000            // 256 bytes
-       xdst $r0 $r1
-       xdwait
-       ret
-
-// Transfer HUB context data between GPU and storage area
-//
-// In: $r2 channel address
-//     $p1 clear on save, set on load
-//     $p2 set if opposite direction done/will be done, so:
-//             on save it means: "a load will follow this save"
-//             on load it means: "a save preceeded this load"
-//
-ctx_xfer:
-       // according to mwk, some kind of wait for idle
-       mov $r15 0xc00
-       shl b32 $r15 6
-       mov $r14 4
-       iowr I[$r15 + 0x200] $r14
-       ctx_xfer_idle:
-               iord $r14 I[$r15 + 0x000]
-               and $r14 0x2000
-               bra ne #ctx_xfer_idle
-
-       bra not $p1 #ctx_xfer_pre
-       bra $p2 #ctx_xfer_pre_load
-       ctx_xfer_pre:
-               mov $r15 0x10
-               call #ctx_86c
-               call #ctx_4160s
-               bra not $p1 #ctx_xfer_exec
-
-       ctx_xfer_pre_load:
-               mov $r15 2
-               call #ctx_4170s
-               call #ctx_4170w
-               call #ctx_redswitch
-               clear b32 $r15
-               call #ctx_4170s
-               call #ctx_load
-
-       // fetch context pointer, and initiate xfer on all GPCs
-       ctx_xfer_exec:
-       ld b32 $r1 D[$r0 + #ctx_current]
-       mov $r2 0x414
-       shl b32 $r2 6
-       iowr I[$r2 + 0x000] $r0 // BAR_STATUS = reset
-       mov $r14 -0x5b00
-       sethi $r14 0x410000
-       mov b32 $r15 $r1
-       call #nv_wr32           // GPC_BCAST_WRCMD_DATA = ctx pointer
-       add b32 $r14 4
-       xbit $r15 $flags $p1
-       xbit $r2 $flags $p2
-       shl b32 $r2 1
-       or $r15 $r2
-       call #nv_wr32           // GPC_BCAST_WRCMD_CMD = GPC_XFER(type)
-
-       // strands
-       mov $r1 0x4afc
-       sethi $r1 0x20000
-       mov $r2 0xc
-       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x0c
-       call #strand_wait
-       mov $r2 0x47fc
-       sethi $r2 0x20000
-       iowr I[$r2] $r0         // STRAND_FIRST_GENE(0x3f) = 0x00
-       xbit $r2 $flags $p1
-       add b32 $r2 3
-       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD)
-
-       // mmio context
-       xbit $r10 $flags $p1    // direction
-       or $r10 6               // first, last
-       mov $r11 0              // base = 0
-       ld b32 $r12 D[$r0 + #hub_mmio_list_head]
-       ld b32 $r13 D[$r0 + #hub_mmio_list_tail]
-       mov $r14 0              // not multi
-       call #mmctx_xfer
-
-       // wait for GPCs to all complete
-       mov $r10 8              // DONE_BAR
-       call #wait_doneo
-
-       // wait for strand xfer to complete
-       call #strand_wait
-
-       // post-op
-       bra $p1 #ctx_xfer_post
-               mov $r10 12             // DONE_UNK12
-               call #wait_donez
-               mov $r1 0xa10
-               shl b32 $r1 6
-               mov $r2 5
-               iowr I[$r1] $r2         // MEM_CMD
-               ctx_xfer_post_save_wait:
-                       iord $r2 I[$r1]
-                       or $r2 $r2
-                       bra ne #ctx_xfer_post_save_wait
-
-       bra $p2 #ctx_xfer_done
-       ctx_xfer_post:
-               mov $r15 2
-               call #ctx_4170s
-               clear b32 $r15
-               call #ctx_86c
-               call #strand_post
-               call #ctx_4170w
-               clear b32 $r15
-               call #ctx_4170s
-
-               bra not $p1 #ctx_xfer_no_post_mmio
-               ld b32 $r1 D[$r0 + #chan_mmio_count]
-               or $r1 $r1
-               bra e #ctx_xfer_no_post_mmio
-                       call #ctx_mmio_exec
-
-               ctx_xfer_no_post_mmio:
-               call #ctx_4160c
-
-       ctx_xfer_done:
-       ret
-
+#include "com.fuc"
+#include "hub.fuc"
 .align 256
+#undef INCLUDE_CODE
index bb03d2a..b59f694 100644 (file)
@@ -1,9 +1,90 @@
 uint32_t nvc0_grhub_data[] = {
-/* 0x0000: gpc_count */
+/* 0x0000: hub_mmio_list_head */
+       0x00000300,
+/* 0x0004: hub_mmio_list_tail */
+       0x00000304,
+/* 0x0008: gpc_count */
+       0x00000000,
+/* 0x000c: rop_count */
+       0x00000000,
+/* 0x0010: cmd_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0058: ctx_current */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0100: chan_data */
+/* 0x0100: chan_mmio_count */
+       0x00000000,
+/* 0x0104: chan_mmio_address */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
        0x00000000,
-/* 0x0004: rop_count */
        0x00000000,
-/* 0x0008: cmd_queue */
        0x00000000,
        0x00000000,
        0x00000000,
@@ -22,114 +103,9 @@ uint32_t nvc0_grhub_data[] = {
        0x00000000,
        0x00000000,
        0x00000000,
-/* 0x0050: hub_mmio_list_head */
        0x00000000,
-/* 0x0054: hub_mmio_list_tail */
        0x00000000,
-/* 0x0058: ctx_current */
        0x00000000,
-/* 0x005c: chipsets */
-       0x000000c0,
-       0x013c00a0,
-       0x000000c1,
-       0x014000a0,
-       0x000000c3,
-       0x013c00a0,
-       0x000000c4,
-       0x013c00a0,
-       0x000000c8,
-       0x013c00a0,
-       0x000000ce,
-       0x013c00a0,
-       0x000000cf,
-       0x013c00a0,
-       0x000000d9,
-       0x01dc0140,
-       0x00000000,
-/* 0x00a0: nvc0_hub_mmio_head */
-       0x0417e91c,
-       0x04400204,
-       0x28404004,
-       0x00404044,
-       0x34404094,
-       0x184040d0,
-       0x004040f8,
-       0x08404130,
-       0x08404150,
-       0x04404164,
-       0x08404174,
-       0x1c404200,
-       0x34404404,
-       0x0c404460,
-       0x00404480,
-       0x00404498,
-       0x0c404604,
-       0x7c404618,
-       0x50404698,
-       0x044046f0,
-       0x54404700,
-       0x00405800,
-       0x08405830,
-       0x00405854,
-       0x0c405870,
-       0x04405a00,
-       0x00405a18,
-       0x00406020,
-       0x0c406028,
-       0x044064a8,
-       0x044064b4,
-       0x00407804,
-       0x1440780c,
-       0x004078bc,
-       0x18408000,
-       0x00408064,
-       0x08408800,
-       0x0c408900,
-       0x00408980,
-/* 0x013c: nvc0_hub_mmio_tail */
-       0x044064c0,
-/* 0x0140: nvc1_hub_mmio_tail */
-/* 0x0140: nvd9_hub_mmio_head */
-       0x0417e91c,
-       0x04400204,
-       0x24404004,
-       0x00404044,
-       0x34404094,
-       0x184040d0,
-       0x004040f8,
-       0x08404130,
-       0x08404150,
-       0x04404164,
-       0x04404178,
-       0x1c404200,
-       0x34404404,
-       0x0c404460,
-       0x00404480,
-       0x00404498,
-       0x0c404604,
-       0x7c404618,
-       0x50404698,
-       0x044046f0,
-       0x54404700,
-       0x00405800,
-       0x08405830,
-       0x00405854,
-       0x0c405870,
-       0x04405a00,
-       0x00405a18,
-       0x00406020,
-       0x0c406028,
-       0x044064a8,
-       0x104064b4,
-       0x00407804,
-       0x1440780c,
-       0x004078bc,
-       0x18408000,
-       0x00408064,
-       0x08408800,
-       0x0c408900,
-       0x00408980,
-/* 0x01dc: nvd9_hub_mmio_tail */
        0x00000000,
        0x00000000,
        0x00000000,
@@ -139,10 +115,7 @@ uint32_t nvc0_grhub_data[] = {
        0x00000000,
        0x00000000,
        0x00000000,
-/* 0x0200: chan_data */
-/* 0x0200: chan_mmio_count */
        0x00000000,
-/* 0x0204: chan_mmio_address */
        0x00000000,
        0x00000000,
        0x00000000,
@@ -163,6 +136,7 @@ uint32_t nvc0_grhub_data[] = {
        0x00000000,
        0x00000000,
        0x00000000,
+/* 0x0200: xfer_data */
        0x00000000,
        0x00000000,
        0x00000000,
@@ -206,19 +180,40 @@ uint32_t nvc0_grhub_data[] = {
        0x00000000,
        0x00000000,
        0x00000000,
-/* 0x0300: xfer_data */
        0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0300: hub_mmio_list_base */
+       0x0417e91c,
 };
 
 uint32_t nvc0_grhub_code[] = {
-       0x03090ef5,
+       0x031b0ef5,
 /* 0x0004: queue_put */
        0x9800d898,
        0x86f001d9,
        0x0489b808,
        0xf00c1bf4,
        0x21f502f7,
-       0x00f802ec,
+       0x00f802fe,
 /* 0x001c: queue_put_next */
        0xb60798c4,
        0x8dbb0384,
@@ -250,7 +245,7 @@ uint32_t nvc0_grhub_code[] = {
        0xc800bccf,
        0x1bf41fcc,
        0x06a7f0fa,
-       0x010321f5,
+       0x010921f5,
        0xf840bfcf,
 /* 0x008d: nv_wr32 */
        0x28b7f100,
@@ -272,63 +267,66 @@ uint32_t nvc0_grhub_code[] = {
        0x0684b604,
        0xf80080d0,
 /* 0x00c9: wait_donez */
-       0x3c87f100,
-       0x0684b608,
-       0x99f094bd,
-       0x0089d000,
-       0x081887f1,
-       0xd00684b6,
-/* 0x00e2: wait_done_wait_donez */
-       0x87f1008a,
-       0x84b60400,
-       0x0088cf06,
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x07f104bd,
+       0x03f00600,
+       0x000ad002,
+/* 0x00e6: wait_donez_ne */
+       0x87f104bd,
+       0x83f00000,
+       0x0088cf01,
        0xf4888aff,
-       0x87f1f31b,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00099,
-/* 0x0103: wait_doneo */
-       0xf100f800,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00099f0,
-       0x87f10089,
+       0x94bdf31b,
+       0xf10099f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0109: wait_doneo */
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x87f104bd,
        0x84b60818,
        0x008ad006,
-/* 0x011c: wait_done_wait_doneo */
+/* 0x0124: wait_doneo_e */
        0x040087f1,
        0xcf0684b6,
        0x8aff0088,
        0xf30bf488,
-       0x085c87f1,
-       0xbd0684b6,
-       0x0099f094,
-       0xf80089d0,
-/* 0x013d: mmctx_size */
-/* 0x013f: nv_mmctx_size_loop */
-       0x9894bd00,
-       0x85b600e8,
-       0x0180b61a,
-       0xbb0284b6,
-       0xe0b60098,
-       0x04efb804,
-       0xb9eb1bf4,
-       0x00f8029f,
-/* 0x015c: mmctx_xfer */
-       0x083c87f1,
-       0xbd0684b6,
-       0x0199f094,
-       0xf10089d0,
+       0x99f094bd,
+       0x0007f100,
+       0x0203f017,
+       0xbd0009d0,
+/* 0x0147: mmctx_size */
+       0xbd00f804,
+/* 0x0149: nv_mmctx_size_loop */
+       0x00e89894,
+       0xb61a85b6,
+       0x84b60180,
+       0x0098bb02,
+       0xb804e0b6,
+       0x1bf404ef,
+       0x029fb9eb,
+/* 0x0166: mmctx_xfer */
+       0x94bd00f8,
+       0xf10199f0,
+       0xf00f0007,
+       0x09d00203,
+       0xf104bd00,
        0xb6071087,
        0x94bd0684,
        0xf405bbfd,
        0x8bd0090b,
        0x0099f000,
-/* 0x0180: mmctx_base_disabled */
+/* 0x018c: mmctx_base_disabled */
        0xf405eefd,
        0x8ed00c0b,
        0xc08fd080,
-/* 0x018f: mmctx_multi_disabled */
+/* 0x019b: mmctx_multi_disabled */
        0xb70199f0,
        0xc8010080,
        0xb4b600ab,
@@ -336,8 +334,8 @@ uint32_t nvc0_grhub_code[] = {
        0xb601aec8,
        0xbefd11e4,
        0x008bd005,
-/* 0x01a8: mmctx_exec_loop */
-/* 0x01a8: mmctx_wait_free */
+/* 0x01b4: mmctx_exec_loop */
+/* 0x01b4: mmctx_wait_free */
        0xf0008ecf,
        0x0bf41fe4,
        0x00ce98fa,
@@ -346,76 +344,77 @@ uint32_t nvc0_grhub_code[] = {
        0x04cdb804,
        0xc8e81bf4,
        0x1bf402ab,
-/* 0x01c9: mmctx_fini_wait */
+/* 0x01d5: mmctx_fini_wait */
        0x008bcf18,
        0xb01fb4f0,
        0x1bf410b4,
        0x02a7f0f7,
        0xf4c921f4,
-/* 0x01de: mmctx_stop */
+/* 0x01ea: mmctx_stop */
        0xabc81b0e,
        0x10b4b600,
        0xf00cb9f0,
        0x8bd012b9,
-/* 0x01ed: mmctx_stop_wait */
+/* 0x01f9: mmctx_stop_wait */
        0x008bcf00,
        0xf412bbc8,
-/* 0x01f6: mmctx_done */
-       0x87f1fa1b,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00199,
-/* 0x0207: strand_wait */
-       0xf900f800,
-       0x02a7f0a0,
-       0xfcc921f4,
-/* 0x0213: strand_pre */
-       0xf100f8a0,
-       0xf04afc87,
-       0x97f00283,
-       0x0089d00c,
-       0x020721f5,
-/* 0x0226: strand_post */
-       0x87f100f8,
-       0x83f04afc,
-       0x0d97f002,
-       0xf50089d0,
-       0xf8020721,
-/* 0x0239: strand_set */
-       0xfca7f100,
-       0x02a3f04f,
-       0x0500aba2,
-       0xd00fc7f0,
-       0xc7f000ac,
-       0x00bcd00b,
-       0x020721f5,
-       0xf000aed0,
-       0xbcd00ac7,
-       0x0721f500,
-/* 0x0263: strand_ctx_init */
-       0xf100f802,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00399f0,
+/* 0x0202: mmctx_done */
+       0x94bdfa1b,
+       0xf10199f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0215: strand_wait */
+       0xf0a0f900,
+       0x21f402a7,
+       0xf8a0fcc9,
+/* 0x0221: strand_pre */
+       0xfc87f100,
+       0x0283f04a,
+       0xd00c97f0,
        0x21f50089,
-       0xe7f00213,
-       0x3921f503,
+       0x00f80215,
+/* 0x0234: strand_post */
+       0x4afc87f1,
+       0xf00283f0,
+       0x89d00d97,
+       0x1521f500,
+/* 0x0247: strand_set */
+       0xf100f802,
+       0xf04ffca7,
+       0xaba202a3,
+       0xc7f00500,
+       0x00acd00f,
+       0xd00bc7f0,
+       0x21f500bc,
+       0xaed00215,
+       0x0ac7f000,
+       0xf500bcd0,
+       0xf8021521,
+/* 0x0271: strand_ctx_init */
+       0xf094bd00,
+       0x07f10399,
+       0x03f00f00,
+       0x0009d002,
+       0x21f504bd,
+       0xe7f00221,
+       0x4721f503,
        0xfca7f102,
        0x02a3f046,
        0x0400aba0,
        0xf040a0d0,
        0xbcd001c7,
-       0x0721f500,
+       0x1521f500,
        0x010c9202,
        0xf000acd0,
        0xbcd002c7,
-       0x0721f500,
-       0x2621f502,
+       0x1521f500,
+       0x3421f502,
        0x8087f102,
        0x0684b608,
        0xb70089cf,
        0x95220080,
-/* 0x02ba: ctx_init_strand_loop */
+/* 0x02ca: ctx_init_strand_loop */
        0x8ed008fe,
        0x408ed000,
        0xb6808acf,
@@ -424,73 +423,61 @@ uint32_t nvc0_grhub_code[] = {
        0xb60480b6,
        0x1bf40192,
        0x08e4b6e8,
-       0xf1f2efbc,
-       0xb6085c87,
-       0x94bd0684,
-       0xd00399f0,
-       0x00f80089,
-/* 0x02ec: error */
-       0xe7f1e0f9,
-       0xe4b60814,
-       0x00efd006,
-       0x0c1ce7f1,
-       0xf006e4b6,
-       0xefd001f7,
-       0xf8e0fc00,
-/* 0x0309: init */
-       0xfe04bd00,
-       0x07fe0004,
-       0x0017f100,
-       0x0227f012,
-       0xf10012d0,
-       0xfe05b917,
-       0x17f10010,
-       0x10d00400,
-       0x0437f1c0,
-       0x0634b604,
-       0x200327f1,
-       0xf10032d0,
-       0xd0200427,
-       0x27f10132,
-       0x32d0200b,
-       0x0c27f102,
-       0x0732d020,
-       0x0c2427f1,
-       0xb90624b6,
-       0x23d00003,
+       0xbdf2efbc,
+       0x0399f094,
+       0x170007f1,
+       0xd00203f0,
+       0x04bd0009,
+/* 0x02fe: error */
+       0x07f100f8,
+       0x03f00500,
+       0x000fd002,
+       0xf7f004bd,
+       0x0007f101,
+       0x0303f007,
+       0xbd000fd0,
+/* 0x031b: init */
+       0xbd00f804,
+       0x0004fe04,
+       0xf10007fe,
+       0xf0120017,
+       0x12d00227,
+       0xb117f100,
+       0x0010fe05,
+       0x040017f1,
+       0xf1c010d0,
+       0xb6040437,
+       0x27f10634,
+       0x32d02003,
        0x0427f100,
-       0x0023f087,
-       0xb70012d0,
-       0xf0010012,
-       0x12d00427,
-       0x1031f400,
-       0x9604e7f1,
-       0xf440e3f0,
-       0xf1c76821,
-       0x01018090,
-       0x801ff4f0,
-       0x17f0000f,
-       0x041fbb01,
-       0xf10112b6,
-       0xb6040c27,
-       0x21d00624,
-       0x4021d000,
-       0x080027f1,
-       0xcf0624b6,
-       0xf7f00022,
-/* 0x03a9: init_find_chipset */
-       0x08f0b654,
-       0xb800f398,
-       0x0bf40432,
-       0x0034b00b,
-       0xf8f11bf4,
-/* 0x03bd: init_context */
-       0x0017f100,
-       0x02fe5801,
-       0xf003ff58,
-       0x0e8000e3,
-       0x150f8014,
-       0x013d21f5,
+       0x0132d020,
+       0x200b27f1,
+       0xf10232d0,
+       0xd0200c27,
+       0x27f10732,
+       0x24b60c24,
+       0x0003b906,
+       0xf10023d0,
+       0xf0870427,
+       0x12d00023,
+       0x0012b700,
+       0x0427f001,
+       0xf40012d0,
+       0xe7f11031,
+       0xe3f09604,
+       0x6821f440,
+       0x8090f1c7,
+       0xf4f00301,
+       0x020f801f,
+       0xbb0117f0,
+       0x12b6041f,
+       0x0c27f101,
+       0x0624b604,
+       0xd00021d0,
+       0x17f14021,
+       0x0e980100,
+       0x010f9800,
+       0x014721f5,
        0x070037f1,
        0x950634b6,
        0x34d00814,
@@ -501,208 +488,213 @@ uint32_t nvc0_grhub_code[] = {
        0x0815b600,
        0xb60110b6,
        0x1fb90814,
-       0x6321f502,
+       0x7121f502,
        0x001fbb02,
-       0xf1000398,
+       0xf1020398,
        0xf0200047,
-/* 0x040e: init_gpc */
+/* 0x03f6: init_gpc */
        0x4ea05043,
        0x1fb90804,
        0x8d21f402,
-       0x08004ea0,
-       0xf4022fb9,
-       0x4ea08d21,
-       0xf4bd010c,
-       0xa08d21f4,
-       0xf401044e,
+       0x010c4ea0,
+       0x21f4f4bd,
+       0x044ea08d,
+       0x8d21f401,
+       0x01004ea0,
+       0xf402f7f0,
        0x4ea08d21,
-       0xf7f00100,
-       0x8d21f402,
-       0x08004ea0,
-/* 0x0440: init_gpc_wait */
-       0xc86821f4,
-       0x0bf41fff,
-       0x044ea0fa,
-       0x6821f408,
-       0xb7001fbb,
-       0xb6800040,
-       0x1bf40132,
-       0x0027f1b4,
-       0x0624b608,
-       0xb74021d0,
-       0xbd080020,
+/* 0x041e: init_gpc_wait */
+       0x21f40800,
+       0x1fffc868,
+       0xa0fa0bf4,
+       0xf408044e,
+       0x1fbb6821,
+       0x0040b700,
+       0x0132b680,
+       0xf1be1bf4,
+       0xf0010007,
+       0x01d00203,
+       0xbd04bd00,
        0x1f19f014,
-/* 0x0473: main */
-       0xf40021d0,
-       0x28f40031,
-       0x08d7f000,
-       0xf43921f4,
-       0xe4b1f401,
-       0x1bf54001,
-       0x87f100d1,
-       0x84b6083c,
-       0xf094bd06,
-       0x89d00499,
-       0x0017f100,
-       0x0614b60b,
-       0xcf4012cf,
-       0x13c80011,
-       0x7e0bf41f,
+       0x080007f1,
+       0xd00203f0,
+       0x04bd0001,
+/* 0x0458: main */
+       0xf40031f4,
+       0xd7f00028,
+       0x3921f410,
+       0xb1f401f4,
+       0xf54001e4,
+       0xbd00de1b,
+       0x0499f094,
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0x0b0017f1,
+       0xcf0614b6,
+       0x11cf4012,
+       0x1f13c800,
+       0x00870bf5,
        0xf41f23c8,
-       0x20f95a0b,
-       0xf10212b9,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00799f0,
-       0x32f40089,
-       0x0231f401,
-       0x082921f5,
-       0x085c87f1,
-       0xbd0684b6,
+       0x20f9620b,
+       0xbd0212b9,
        0x0799f094,
-       0xfc0089d0,
-       0x3c87f120,
-       0x0684b608,
-       0x99f094bd,
-       0x0089d006,
-       0xf50131f4,
-       0xf1082921,
-       0xb6085c87,
-       0x94bd0684,
-       0xd00699f0,
-       0x0ef40089,
-/* 0x0509: chsw_prev_no_next */
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0xf40132f4,
+       0x21f50231,
+       0x94bd082f,
+       0xf10799f0,
+       0xf0170007,
+       0x09d00203,
+       0xfc04bd00,
+       0xf094bd20,
+       0x07f10699,
+       0x03f00f00,
+       0x0009d002,
+       0x31f404bd,
+       0x2f21f501,
+       0xf094bd08,
+       0x07f10699,
+       0x03f01700,
+       0x0009d002,
+       0x0ef404bd,
+/* 0x04f9: chsw_prev_no_next */
        0xb920f931,
        0x32f40212,
        0x0232f401,
-       0x082921f5,
+       0x082f21f5,
        0x17f120fc,
        0x14b60b00,
        0x0012d006,
-/* 0x0527: chsw_no_prev */
+/* 0x0517: chsw_no_prev */
        0xc8130ef4,
        0x0bf41f23,
        0x0131f40d,
        0xf50232f4,
-/* 0x0537: chsw_done */
-       0xf1082921,
+/* 0x0527: chsw_done */
+       0xf1082f21,
        0xb60b0c17,
        0x27f00614,
        0x0012d001,
-       0x085c87f1,
-       0xbd0684b6,
-       0x0499f094,
-       0xf50089d0,
-/* 0x0557: main_not_ctx_switch */
-       0xb0ff200e,
-       0x1bf401e4,
-       0x02f2b90d,
-       0x07b521f5,
-/* 0x0567: main_not_ctx_chan */
-       0xb0420ef4,
-       0x1bf402e4,
-       0x3c87f12e,
-       0x0684b608,
        0x99f094bd,
-       0x0089d007,
+       0x0007f104,
+       0x0203f017,
+       0xbd0009d0,
+       0x130ef504,
+/* 0x0549: main_not_ctx_switch */
+       0x01e4b0ff,
+       0xb90d1bf4,
+       0x21f502f2,
+       0x0ef407bb,
+/* 0x0559: main_not_ctx_chan */
+       0x02e4b046,
+       0xbd321bf4,
+       0x0799f094,
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
        0xf40132f4,
        0x21f50232,
-       0x87f10829,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00799,
-       0x110ef400,
-/* 0x0598: main_not_ctx_save */
-       0xf010ef94,
-       0x21f501f5,
-       0x0ef502ec,
-/* 0x05a6: main_done */
-       0x17f1fed1,
-       0x14b60820,
-       0xf024bd06,
-       0x12d01f29,
-       0xbe0ef500,
-/* 0x05b9: ih */
+       0x94bd082f,
+       0xf10799f0,
+       0xf0170007,
+       0x09d00203,
+       0xf404bd00,
+/* 0x058e: main_not_ctx_save */
+       0xef94110e,
+       0x01f5f010,
+       0x02fe21f5,
+       0xfec00ef5,
+/* 0x059c: main_done */
+       0x29f024bd,
+       0x0007f11f,
+       0x0203f008,
+       0xbd0002d0,
+       0xab0ef504,
+/* 0x05b1: ih */
        0xfe80f9fe,
        0x80f90188,
        0xa0f990f9,
        0xd0f9b0f9,
        0xf0f9e0f9,
-       0xc4800acf,
-       0x0bf404ab,
-       0x00b7f11d,
-       0x08d7f019,
-       0xcf40becf,
-       0x21f400bf,
-       0x00b0b704,
-       0x01e7f004,
-/* 0x05ef: ih_no_fifo */
-       0xe400bed0,
-       0xf40100ab,
-       0xd7f00d0b,
-       0x01e7f108,
-       0x0421f440,
-/* 0x0600: ih_no_ctxsw */
-       0x0104b7f1,
-       0xabffb0bd,
-       0x0d0bf4b4,
-       0x0c1ca7f1,
-       0xd006a4b6,
-/* 0x0616: ih_no_other */
-       0x0ad000ab,
-       0xfcf0fc40,
-       0xfcd0fce0,
-       0xfca0fcb0,
-       0xfe80fc90,
-       0x80fc0088,
-       0xf80032f4,
-/* 0x0631: ctx_4160s */
-       0x60e7f101,
-       0x40e3f041,
-       0xf401f7f0,
-/* 0x063e: ctx_4160s_wait */
-       0x21f48d21,
-       0x04ffc868,
-       0xf8fa0bf4,
-/* 0x0649: ctx_4160c */
-       0x60e7f100,
+       0x0acf04bd,
+       0x04abc480,
+       0xf11d0bf4,
+       0xf01900b7,
+       0xbecf10d7,
+       0x00bfcf40,
+       0xb70421f4,
+       0xf00400b0,
+       0xbed001e7,
+/* 0x05e9: ih_no_fifo */
+       0x00abe400,
+       0x0d0bf401,
+       0xf110d7f0,
+       0xf44001e7,
+/* 0x05fa: ih_no_ctxsw */
+       0xb7f10421,
+       0xb0bd0104,
+       0xf4b4abff,
+       0xa7f10d0b,
+       0xa4b60c1c,
+       0x00abd006,
+/* 0x0610: ih_no_other */
+       0xfc400ad0,
+       0xfce0fcf0,
+       0xfcb0fcd0,
+       0xfc90fca0,
+       0x0088fe80,
+       0x32f480fc,
+/* 0x062b: ctx_4160s */
+       0xf101f800,
+       0xf04160e7,
+       0xf7f040e3,
+       0x8d21f401,
+/* 0x0638: ctx_4160s_wait */
+       0xc86821f4,
+       0x0bf404ff,
+/* 0x0643: ctx_4160c */
+       0xf100f8fa,
+       0xf04160e7,
+       0xf4bd40e3,
+       0xf88d21f4,
+/* 0x0651: ctx_4170s */
+       0x70e7f100,
        0x40e3f041,
-       0x21f4f4bd,
-/* 0x0657: ctx_4170s */
-       0xf100f88d,
-       0xf04170e7,
-       0xf5f040e3,
-       0x8d21f410,
-/* 0x0666: ctx_4170w */
-       0xe7f100f8,
-       0xe3f04170,
-       0x6821f440,
-       0xf410f4f0,
-       0x00f8f31b,
-/* 0x0678: ctx_redswitch */
-       0x0614e7f1,
-       0xf106e4b6,
-       0xd00270f7,
-       0xf7f000ef,
-/* 0x0689: ctx_redswitch_delay */
-       0x01f2b608,
-       0xf1fd1bf4,
-       0xd00770f7,
-       0x00f800ef,
-/* 0x0698: ctx_86c */
-       0x086ce7f1,
-       0xd006e4b6,
-       0xe7f100ef,
-       0xe3f08a14,
-       0x8d21f440,
-       0xa86ce7f1,
-       0xf441e3f0,
+       0xf410f5f0,
        0x00f88d21,
-/* 0x06b8: ctx_load */
-       0x083c87f1,
-       0xbd0684b6,
-       0x0599f094,
-       0xf00089d0,
+/* 0x0660: ctx_4170w */
+       0x4170e7f1,
+       0xf440e3f0,
+       0xf4f06821,
+       0xf31bf410,
+/* 0x0672: ctx_redswitch */
+       0xe7f100f8,
+       0xe4b60614,
+       0x70f7f106,
+       0x00efd002,
+/* 0x0683: ctx_redswitch_delay */
+       0xb608f7f0,
+       0x1bf401f2,
+       0x70f7f1fd,
+       0x00efd007,
+/* 0x0692: ctx_86c */
+       0xe7f100f8,
+       0xe4b6086c,
+       0x00efd006,
+       0x8a14e7f1,
+       0xf440e3f0,
+       0xe7f18d21,
+       0xe3f0a86c,
+       0x8d21f441,
+/* 0x06b2: ctx_load */
+       0x94bd00f8,
+       0xf10599f0,
+       0xf00f0007,
+       0x09d00203,
+       0xf004bd00,
        0x21f40ca7,
        0x2417f1c9,
        0x0614b60a,
@@ -713,168 +705,169 @@ uint32_t nvc0_grhub_code[] = {
        0x0614b60a,
        0xd00747f0,
        0x14d00012,
-/* 0x06f1: ctx_chan_wait_0 */
+/* 0x06ed: ctx_chan_wait_0 */
        0x4014cf40,
        0xf41f44f0,
        0x32d0fa1b,
        0x000bfe00,
        0xb61f2af0,
        0x20b60424,
-       0x3c87f102,
-       0x0684b608,
+       0xf094bd02,
+       0x07f10899,
+       0x03f00f00,
+       0x0009d002,
+       0x17f104bd,
+       0x14b60a04,
+       0x0012d006,
+       0x0a2017f1,
+       0xf00614b6,
+       0x23f10227,
+       0x12d08000,
+       0x1017f000,
+       0x020027f1,
+       0xfa0223f0,
+       0x03f80512,
        0x99f094bd,
-       0x0089d008,
-       0x0a0417f1,
-       0xd00614b6,
-       0x17f10012,
-       0x14b60a20,
-       0x0227f006,
-       0x800023f1,
-       0xf00012d0,
-       0x27f11017,
-       0x23f00300,
-       0x0512fa02,
-       0x87f103f8,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00899,
-       0xc1019800,
+       0x0007f108,
+       0x0203f017,
+       0xbd0009d0,
+       0x81019804,
        0x981814b6,
-       0x25b6c002,
+       0x25b68002,
        0x0512fd08,
-       0xf1160180,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00999f0,
-       0x27f10089,
-       0x24b60a04,
-       0x0021d006,
-       0xf10127f0,
-       0xb60a2017,
-       0x12d00614,
-       0x0017f100,
-       0x0613f002,
-       0xf80501fa,
-       0x5c87f103,
-       0x0684b608,
-       0x99f094bd,
-       0x0089d009,
-       0x085c87f1,
-       0xbd0684b6,
-       0x0599f094,
-       0xf80089d0,
-/* 0x07b5: ctx_chan */
-       0x3121f500,
-       0xb821f506,
-       0x0ca7f006,
-       0xf1c921f4,
-       0xb60a1017,
-       0x27f00614,
-       0x0012d005,
-/* 0x07d0: ctx_chan_wait */
-       0xfd0012cf,
-       0x1bf40522,
-       0x4921f5fa,
-/* 0x07df: ctx_mmio_exec */
-       0x9800f806,
-       0x27f18103,
-       0x24b60a04,
-       0x0023d006,
-/* 0x07ee: ctx_mmio_loop */
-       0x34c434bd,
-       0x0f1bf4ff,
-       0x030057f1,
-       0xfa0653f0,
-       0x03f80535,
-/* 0x0800: ctx_mmio_pull */
-       0x98c04e98,
-       0x21f4c14f,
-       0x0830b68d,
-       0xf40112b6,
-/* 0x0812: ctx_mmio_done */
-       0x0398df1b,
-       0x0023d016,
-       0xf1800080,
-       0xf0020017,
+       0xbd160180,
+       0x0999f094,
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0x0a0427f1,
+       0xd00624b6,
+       0x27f00021,
+       0x2017f101,
+       0x0614b60a,
+       0xf10012d0,
+       0xf0010017,
        0x01fa0613,
-       0xf803f806,
-/* 0x0829: ctx_xfer */
-       0x00f7f100,
-       0x06f4b60c,
-       0xd004e7f0,
-/* 0x0836: ctx_xfer_idle */
-       0xfecf80fe,
-       0x00e4f100,
-       0xf91bf420,
-       0xf40611f4,
-/* 0x0846: ctx_xfer_pre */
-       0xf7f01102,
-       0x9821f510,
-       0x3121f506,
-       0x1c11f406,
-/* 0x0854: ctx_xfer_pre_load */
-       0xf502f7f0,
-       0xf5065721,
-       0xf5066621,
-       0xbd067821,
-       0x5721f5f4,
-       0xb821f506,
-/* 0x086d: ctx_xfer_exec */
-       0x16019806,
-       0x041427f1,
+       0xbd03f805,
+       0x0999f094,
+       0x170007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0x99f094bd,
+       0x0007f105,
+       0x0203f017,
+       0xbd0009d0,
+/* 0x07bb: ctx_chan */
+       0xf500f804,
+       0xf5062b21,
+       0xf006b221,
+       0x21f40ca7,
+       0x1017f1c9,
+       0x0614b60a,
+       0xd00527f0,
+/* 0x07d6: ctx_chan_wait */
+       0x12cf0012,
+       0x0522fd00,
+       0xf5fa1bf4,
+       0xf8064321,
+/* 0x07e5: ctx_mmio_exec */
+       0x41039800,
+       0x0a0427f1,
        0xd00624b6,
-       0xe7f10020,
-       0xe3f0a500,
-       0x021fb941,
+       0x34bd0023,
+/* 0x07f4: ctx_mmio_loop */
+       0xf4ff34c4,
+       0x57f10f1b,
+       0x53f00200,
+       0x0535fa06,
+/* 0x0806: ctx_mmio_pull */
+       0x4e9803f8,
+       0x814f9880,
        0xb68d21f4,
-       0xfcf004e0,
-       0x022cf001,
-       0xfd0124b6,
-       0x21f405f2,
-       0xfc17f18d,
-       0x0213f04a,
-       0xd00c27f0,
-       0x21f50012,
-       0x27f10207,
-       0x23f047fc,
-       0x0020d002,
-       0xb6012cf0,
-       0x12d00320,
-       0x01acf000,
-       0xf006a5f0,
-       0x0c9800b7,
-       0x150d9814,
-       0xf500e7f0,
-       0xf0015c21,
-       0x21f508a7,
-       0x21f50103,
-       0x01f40207,
-       0x0ca7f022,
-       0xf1c921f4,
-       0xb60a1017,
-       0x27f00614,
-       0x0012d005,
-/* 0x08f4: ctx_xfer_post_save_wait */
-       0xfd0012cf,
-       0x1bf40522,
-       0x3202f4fa,
-/* 0x0900: ctx_xfer_post */
-       0xf502f7f0,
-       0xbd065721,
-       0x9821f5f4,
-       0x2621f506,
-       0x6621f502,
+       0x12b60830,
+       0xdf1bf401,
+/* 0x0818: ctx_mmio_done */
+       0xd0160398,
+       0x00800023,
+       0x0017f140,
+       0x0613f001,
+       0xf80601fa,
+/* 0x082f: ctx_xfer */
+       0xf100f803,
+       0xb60c00f7,
+       0xe7f006f4,
+       0x80fed004,
+/* 0x083c: ctx_xfer_idle */
+       0xf100fecf,
+       0xf42000e4,
+       0x11f4f91b,
+       0x1102f406,
+/* 0x084c: ctx_xfer_pre */
+       0xf510f7f0,
+       0xf5069221,
+       0xf4062b21,
+/* 0x085a: ctx_xfer_pre_load */
+       0xf7f01c11,
+       0x5121f502,
+       0x6021f506,
+       0x7221f506,
        0xf5f4bd06,
-       0xf4065721,
-       0x01981011,
-       0x0511fd80,
-       0xf5070bf4,
-/* 0x092b: ctx_xfer_no_post_mmio */
-       0xf507df21,
-/* 0x092f: ctx_xfer_done */
-       0xf8064921,
-       0x00000000,
-       0x00000000,
+       0xf5065121,
+/* 0x0873: ctx_xfer_exec */
+       0x9806b221,
+       0x27f11601,
+       0x24b60414,
+       0x0020d006,
+       0xa500e7f1,
+       0xb941e3f0,
+       0x21f4021f,
+       0x04e0b68d,
+       0xf001fcf0,
+       0x24b6022c,
+       0x05f2fd01,
+       0xf18d21f4,
+       0xf04afc17,
+       0x27f00213,
+       0x0012d00c,
+       0x021521f5,
+       0x47fc27f1,
+       0xd00223f0,
+       0x2cf00020,
+       0x0320b601,
+       0xf00012d0,
+       0xa5f001ac,
+       0x00b7f006,
+       0x98000c98,
+       0xe7f0010d,
+       0x6621f500,
+       0x08a7f001,
+       0x010921f5,
+       0x021521f5,
+       0xf02201f4,
+       0x21f40ca7,
+       0x1017f1c9,
+       0x0614b60a,
+       0xd00527f0,
+/* 0x08fa: ctx_xfer_post_save_wait */
+       0x12cf0012,
+       0x0522fd00,
+       0xf4fa1bf4,
+/* 0x0906: ctx_xfer_post */
+       0xf7f03202,
+       0x5121f502,
+       0xf5f4bd06,
+       0xf5069221,
+       0xf5023421,
+       0xbd066021,
+       0x5121f5f4,
+       0x1011f406,
+       0xfd400198,
+       0x0bf40511,
+       0xe521f507,
+/* 0x0931: ctx_xfer_no_post_mmio */
+       0x4321f507,
+/* 0x0935: ctx_xfer_done */
+       0x0000f806,
        0x00000000,
        0x00000000,
        0x00000000,
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc
new file mode 100644 (file)
index 0000000..afbe03a
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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>
+ */
+
+#define CHIPSET GF117
+#include "macros.fuc"
+
+.section #nvd7_grhub_data
+#define INCLUDE_DATA
+#include "com.fuc"
+#include "hub.fuc"
+#undef INCLUDE_DATA
+
+.section #nvd7_grhub_code
+#define INCLUDE_CODE
+bra #init
+#include "com.fuc"
+#include "hub.fuc"
+.align 256
+#undef INCLUDE_CODE
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h
new file mode 100644 (file)
index 0000000..a1b9f76
--- /dev/null
@@ -0,0 +1,921 @@
+uint32_t nvd7_grhub_data[] = {
+/* 0x0000: hub_mmio_list_head */
+       0x00000300,
+/* 0x0004: hub_mmio_list_tail */
+       0x00000304,
+/* 0x0008: gpc_count */
+       0x00000000,
+/* 0x000c: rop_count */
+       0x00000000,
+/* 0x0010: cmd_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0058: ctx_current */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0100: chan_data */
+/* 0x0100: chan_mmio_count */
+       0x00000000,
+/* 0x0104: chan_mmio_address */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0200: xfer_data */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0300: hub_mmio_list_base */
+       0x0417e91c,
+};
+
+uint32_t nvd7_grhub_code[] = {
+       0x031b0ef5,
+/* 0x0004: queue_put */
+       0x9800d898,
+       0x86f001d9,
+       0x0489b808,
+       0xf00c1bf4,
+       0x21f502f7,
+       0x00f802fe,
+/* 0x001c: queue_put_next */
+       0xb60798c4,
+       0x8dbb0384,
+       0x0880b600,
+       0x80008e80,
+       0x90b6018f,
+       0x0f94f001,
+       0xf801d980,
+/* 0x0039: queue_get */
+       0x0131f400,
+       0x9800d898,
+       0x89b801d9,
+       0x210bf404,
+       0xb60789c4,
+       0x9dbb0394,
+       0x0890b600,
+       0x98009e98,
+       0x80b6019f,
+       0x0f84f001,
+       0xf400d880,
+/* 0x0066: queue_get_done */
+       0x00f80132,
+/* 0x0068: nv_rd32 */
+       0x0728b7f1,
+       0xb906b4b6,
+       0xc9f002ec,
+       0x00bcd01f,
+/* 0x0078: nv_rd32_wait */
+       0xc800bccf,
+       0x1bf41fcc,
+       0x06a7f0fa,
+       0x010921f5,
+       0xf840bfcf,
+/* 0x008d: nv_wr32 */
+       0x28b7f100,
+       0x06b4b607,
+       0xb980bfd0,
+       0xc9f002ec,
+       0x1ec9f01f,
+/* 0x00a3: nv_wr32_wait */
+       0xcf00bcd0,
+       0xccc800bc,
+       0xfa1bf41f,
+/* 0x00ae: watchdog_reset */
+       0x87f100f8,
+       0x84b60430,
+       0x1ff9f006,
+       0xf8008fd0,
+/* 0x00bd: watchdog_clear */
+       0x3087f100,
+       0x0684b604,
+       0xf80080d0,
+/* 0x00c9: wait_donez */
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x07f104bd,
+       0x03f00600,
+       0x000ad002,
+/* 0x00e6: wait_donez_ne */
+       0x87f104bd,
+       0x83f00000,
+       0x0088cf01,
+       0xf4888aff,
+       0x94bdf31b,
+       0xf10099f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0109: wait_doneo */
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x87f104bd,
+       0x84b60818,
+       0x008ad006,
+/* 0x0124: wait_doneo_e */
+       0x040087f1,
+       0xcf0684b6,
+       0x8aff0088,
+       0xf30bf488,
+       0x99f094bd,
+       0x0007f100,
+       0x0203f017,
+       0xbd0009d0,
+/* 0x0147: mmctx_size */
+       0xbd00f804,
+/* 0x0149: nv_mmctx_size_loop */
+       0x00e89894,
+       0xb61a85b6,
+       0x84b60180,
+       0x0098bb02,
+       0xb804e0b6,
+       0x1bf404ef,
+       0x029fb9eb,
+/* 0x0166: mmctx_xfer */
+       0x94bd00f8,
+       0xf10199f0,
+       0xf00f0007,
+       0x09d00203,
+       0xf104bd00,
+       0xb6071087,
+       0x94bd0684,
+       0xf405bbfd,
+       0x8bd0090b,
+       0x0099f000,
+/* 0x018c: mmctx_base_disabled */
+       0xf405eefd,
+       0x8ed00c0b,
+       0xc08fd080,
+/* 0x019b: mmctx_multi_disabled */
+       0xb70199f0,
+       0xc8010080,
+       0xb4b600ab,
+       0x0cb9f010,
+       0xb601aec8,
+       0xbefd11e4,
+       0x008bd005,
+/* 0x01b4: mmctx_exec_loop */
+/* 0x01b4: mmctx_wait_free */
+       0xf0008ecf,
+       0x0bf41fe4,
+       0x00ce98fa,
+       0xd005e9fd,
+       0xc0b6c08e,
+       0x04cdb804,
+       0xc8e81bf4,
+       0x1bf402ab,
+/* 0x01d5: mmctx_fini_wait */
+       0x008bcf18,
+       0xb01fb4f0,
+       0x1bf410b4,
+       0x02a7f0f7,
+       0xf4c921f4,
+/* 0x01ea: mmctx_stop */
+       0xabc81b0e,
+       0x10b4b600,
+       0xf00cb9f0,
+       0x8bd012b9,
+/* 0x01f9: mmctx_stop_wait */
+       0x008bcf00,
+       0xf412bbc8,
+/* 0x0202: mmctx_done */
+       0x94bdfa1b,
+       0xf10199f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0215: strand_wait */
+       0xf0a0f900,
+       0x21f402a7,
+       0xf8a0fcc9,
+/* 0x0221: strand_pre */
+       0xfc87f100,
+       0x0283f04a,
+       0xd00c97f0,
+       0x21f50089,
+       0x00f80215,
+/* 0x0234: strand_post */
+       0x4afc87f1,
+       0xf00283f0,
+       0x89d00d97,
+       0x1521f500,
+/* 0x0247: strand_set */
+       0xf100f802,
+       0xf04ffca7,
+       0xaba202a3,
+       0xc7f00500,
+       0x00acd00f,
+       0xd00bc7f0,
+       0x21f500bc,
+       0xaed00215,
+       0x0ac7f000,
+       0xf500bcd0,
+       0xf8021521,
+/* 0x0271: strand_ctx_init */
+       0xf094bd00,
+       0x07f10399,
+       0x03f00f00,
+       0x0009d002,
+       0x21f504bd,
+       0xe7f00221,
+       0x4721f503,
+       0xfca7f102,
+       0x02a3f046,
+       0x0400aba0,
+       0xf040a0d0,
+       0xbcd001c7,
+       0x1521f500,
+       0x010c9202,
+       0xf000acd0,
+       0xbcd002c7,
+       0x1521f500,
+       0x3421f502,
+       0x8087f102,
+       0x0684b608,
+       0xb70089cf,
+       0x95220080,
+/* 0x02ca: ctx_init_strand_loop */
+       0x8ed008fe,
+       0x408ed000,
+       0xb6808acf,
+       0xa0b606a5,
+       0x00eabb01,
+       0xb60480b6,
+       0x1bf40192,
+       0x08e4b6e8,
+       0xbdf2efbc,
+       0x0399f094,
+       0x170007f1,
+       0xd00203f0,
+       0x04bd0009,
+/* 0x02fe: error */
+       0x07f100f8,
+       0x03f00500,
+       0x000fd002,
+       0xf7f004bd,
+       0x0007f101,
+       0x0303f007,
+       0xbd000fd0,
+/* 0x031b: init */
+       0xbd00f804,
+       0x0004fe04,
+       0xf10007fe,
+       0xf0120017,
+       0x12d00227,
+       0xb117f100,
+       0x0010fe05,
+       0x040017f1,
+       0xf1c010d0,
+       0xb6040437,
+       0x27f10634,
+       0x32d02003,
+       0x0427f100,
+       0x0132d020,
+       0x200b27f1,
+       0xf10232d0,
+       0xd0200c27,
+       0x27f10732,
+       0x24b60c24,
+       0x0003b906,
+       0xf10023d0,
+       0xf0870427,
+       0x12d00023,
+       0x0012b700,
+       0x0427f001,
+       0xf40012d0,
+       0xe7f11031,
+       0xe3f09604,
+       0x6821f440,
+       0x8090f1c7,
+       0xf4f00301,
+       0x020f801f,
+       0xbb0117f0,
+       0x12b6041f,
+       0x0c27f101,
+       0x0624b604,
+       0xd00021d0,
+       0x17f14021,
+       0x0e980100,
+       0x010f9800,
+       0x014721f5,
+       0x070037f1,
+       0x950634b6,
+       0x34d00814,
+       0x4034d000,
+       0x130030b7,
+       0xb6001fbb,
+       0x3fd002f5,
+       0x0815b600,
+       0xb60110b6,
+       0x1fb90814,
+       0x7121f502,
+       0x001fbb02,
+       0xf1020398,
+       0xf0200047,
+/* 0x03f6: init_gpc */
+       0x4ea05043,
+       0x1fb90804,
+       0x8d21f402,
+       0x010c4ea0,
+       0x21f4f4bd,
+       0x044ea08d,
+       0x8d21f401,
+       0x01004ea0,
+       0xf402f7f0,
+       0x4ea08d21,
+/* 0x041e: init_gpc_wait */
+       0x21f40800,
+       0x1fffc868,
+       0xa0fa0bf4,
+       0xf408044e,
+       0x1fbb6821,
+       0x0040b700,
+       0x0132b680,
+       0xf1be1bf4,
+       0xf0010007,
+       0x01d00203,
+       0xbd04bd00,
+       0x1f19f014,
+       0x080007f1,
+       0xd00203f0,
+       0x04bd0001,
+/* 0x0458: main */
+       0xf40031f4,
+       0xd7f00028,
+       0x3921f410,
+       0xb1f401f4,
+       0xf54001e4,
+       0xbd00de1b,
+       0x0499f094,
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0x0b0017f1,
+       0xcf0614b6,
+       0x11cf4012,
+       0x1f13c800,
+       0x00870bf5,
+       0xf41f23c8,
+       0x20f9620b,
+       0xbd0212b9,
+       0x0799f094,
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0xf40132f4,
+       0x21f50231,
+       0x94bd082f,
+       0xf10799f0,
+       0xf0170007,
+       0x09d00203,
+       0xfc04bd00,
+       0xf094bd20,
+       0x07f10699,
+       0x03f00f00,
+       0x0009d002,
+       0x31f404bd,
+       0x2f21f501,
+       0xf094bd08,
+       0x07f10699,
+       0x03f01700,
+       0x0009d002,
+       0x0ef404bd,
+/* 0x04f9: chsw_prev_no_next */
+       0xb920f931,
+       0x32f40212,
+       0x0232f401,
+       0x082f21f5,
+       0x17f120fc,
+       0x14b60b00,
+       0x0012d006,
+/* 0x0517: chsw_no_prev */
+       0xc8130ef4,
+       0x0bf41f23,
+       0x0131f40d,
+       0xf50232f4,
+/* 0x0527: chsw_done */
+       0xf1082f21,
+       0xb60b0c17,
+       0x27f00614,
+       0x0012d001,
+       0x99f094bd,
+       0x0007f104,
+       0x0203f017,
+       0xbd0009d0,
+       0x130ef504,
+/* 0x0549: main_not_ctx_switch */
+       0x01e4b0ff,
+       0xb90d1bf4,
+       0x21f502f2,
+       0x0ef407bb,
+/* 0x0559: main_not_ctx_chan */
+       0x02e4b046,
+       0xbd321bf4,
+       0x0799f094,
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0xf40132f4,
+       0x21f50232,
+       0x94bd082f,
+       0xf10799f0,
+       0xf0170007,
+       0x09d00203,
+       0xf404bd00,
+/* 0x058e: main_not_ctx_save */
+       0xef94110e,
+       0x01f5f010,
+       0x02fe21f5,
+       0xfec00ef5,
+/* 0x059c: main_done */
+       0x29f024bd,
+       0x0007f11f,
+       0x0203f008,
+       0xbd0002d0,
+       0xab0ef504,
+/* 0x05b1: ih */
+       0xfe80f9fe,
+       0x80f90188,
+       0xa0f990f9,
+       0xd0f9b0f9,
+       0xf0f9e0f9,
+       0x0acf04bd,
+       0x04abc480,
+       0xf11d0bf4,
+       0xf01900b7,
+       0xbecf10d7,
+       0x00bfcf40,
+       0xb70421f4,
+       0xf00400b0,
+       0xbed001e7,
+/* 0x05e9: ih_no_fifo */
+       0x00abe400,
+       0x0d0bf401,
+       0xf110d7f0,
+       0xf44001e7,
+/* 0x05fa: ih_no_ctxsw */
+       0xb7f10421,
+       0xb0bd0104,
+       0xf4b4abff,
+       0xa7f10d0b,
+       0xa4b60c1c,
+       0x00abd006,
+/* 0x0610: ih_no_other */
+       0xfc400ad0,
+       0xfce0fcf0,
+       0xfcb0fcd0,
+       0xfc90fca0,
+       0x0088fe80,
+       0x32f480fc,
+/* 0x062b: ctx_4160s */
+       0xf101f800,
+       0xf04160e7,
+       0xf7f040e3,
+       0x8d21f401,
+/* 0x0638: ctx_4160s_wait */
+       0xc86821f4,
+       0x0bf404ff,
+/* 0x0643: ctx_4160c */
+       0xf100f8fa,
+       0xf04160e7,
+       0xf4bd40e3,
+       0xf88d21f4,
+/* 0x0651: ctx_4170s */
+       0x70e7f100,
+       0x40e3f041,
+       0xf410f5f0,
+       0x00f88d21,
+/* 0x0660: ctx_4170w */
+       0x4170e7f1,
+       0xf440e3f0,
+       0xf4f06821,
+       0xf31bf410,
+/* 0x0672: ctx_redswitch */
+       0xe7f100f8,
+       0xe4b60614,
+       0x70f7f106,
+       0x00efd002,
+/* 0x0683: ctx_redswitch_delay */
+       0xb608f7f0,
+       0x1bf401f2,
+       0x70f7f1fd,
+       0x00efd007,
+/* 0x0692: ctx_86c */
+       0xe7f100f8,
+       0xe4b6086c,
+       0x00efd006,
+       0x8a14e7f1,
+       0xf440e3f0,
+       0xe7f18d21,
+       0xe3f0a86c,
+       0x8d21f441,
+/* 0x06b2: ctx_load */
+       0x94bd00f8,
+       0xf10599f0,
+       0xf00f0007,
+       0x09d00203,
+       0xf004bd00,
+       0x21f40ca7,
+       0x2417f1c9,
+       0x0614b60a,
+       0xf10010d0,
+       0xb60b0037,
+       0x32d00634,
+       0x0c17f140,
+       0x0614b60a,
+       0xd00747f0,
+       0x14d00012,
+/* 0x06ed: ctx_chan_wait_0 */
+       0x4014cf40,
+       0xf41f44f0,
+       0x32d0fa1b,
+       0x000bfe00,
+       0xb61f2af0,
+       0x20b60424,
+       0xf094bd02,
+       0x07f10899,
+       0x03f00f00,
+       0x0009d002,
+       0x17f104bd,
+       0x14b60a04,
+       0x0012d006,
+       0x0a2017f1,
+       0xf00614b6,
+       0x23f10227,
+       0x12d08000,
+       0x1017f000,
+       0x020027f1,
+       0xfa0223f0,
+       0x03f80512,
+       0x99f094bd,
+       0x0007f108,
+       0x0203f017,
+       0xbd0009d0,
+       0x81019804,
+       0x981814b6,
+       0x25b68002,
+       0x0512fd08,
+       0xbd160180,
+       0x0999f094,
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0x0a0427f1,
+       0xd00624b6,
+       0x27f00021,
+       0x2017f101,
+       0x0614b60a,
+       0xf10012d0,
+       0xf0010017,
+       0x01fa0613,
+       0xbd03f805,
+       0x0999f094,
+       0x170007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0x99f094bd,
+       0x0007f105,
+       0x0203f017,
+       0xbd0009d0,
+/* 0x07bb: ctx_chan */
+       0xf500f804,
+       0xf5062b21,
+       0xf006b221,
+       0x21f40ca7,
+       0x1017f1c9,
+       0x0614b60a,
+       0xd00527f0,
+/* 0x07d6: ctx_chan_wait */
+       0x12cf0012,
+       0x0522fd00,
+       0xf5fa1bf4,
+       0xf8064321,
+/* 0x07e5: ctx_mmio_exec */
+       0x41039800,
+       0x0a0427f1,
+       0xd00624b6,
+       0x34bd0023,
+/* 0x07f4: ctx_mmio_loop */
+       0xf4ff34c4,
+       0x57f10f1b,
+       0x53f00200,
+       0x0535fa06,
+/* 0x0806: ctx_mmio_pull */
+       0x4e9803f8,
+       0x814f9880,
+       0xb68d21f4,
+       0x12b60830,
+       0xdf1bf401,
+/* 0x0818: ctx_mmio_done */
+       0xd0160398,
+       0x00800023,
+       0x0017f140,
+       0x0613f001,
+       0xf80601fa,
+/* 0x082f: ctx_xfer */
+       0xf100f803,
+       0xb60c00f7,
+       0xe7f006f4,
+       0x80fed004,
+/* 0x083c: ctx_xfer_idle */
+       0xf100fecf,
+       0xf42000e4,
+       0x11f4f91b,
+       0x1102f406,
+/* 0x084c: ctx_xfer_pre */
+       0xf510f7f0,
+       0xf5069221,
+       0xf4062b21,
+/* 0x085a: ctx_xfer_pre_load */
+       0xf7f01c11,
+       0x5121f502,
+       0x6021f506,
+       0x7221f506,
+       0xf5f4bd06,
+       0xf5065121,
+/* 0x0873: ctx_xfer_exec */
+       0x9806b221,
+       0x27f11601,
+       0x24b60414,
+       0x0020d006,
+       0xa500e7f1,
+       0xb941e3f0,
+       0x21f4021f,
+       0x04e0b68d,
+       0xf001fcf0,
+       0x24b6022c,
+       0x05f2fd01,
+       0xf18d21f4,
+       0xf04afc17,
+       0x27f00213,
+       0x0012d00c,
+       0x021521f5,
+       0x47fc27f1,
+       0xd00223f0,
+       0x2cf00020,
+       0x0320b601,
+       0xf00012d0,
+       0xa5f001ac,
+       0x00b7f006,
+       0x98000c98,
+       0xe7f0010d,
+       0x6621f500,
+       0x08a7f001,
+       0x010921f5,
+       0x021521f5,
+       0xf02201f4,
+       0x21f40ca7,
+       0x1017f1c9,
+       0x0614b60a,
+       0xd00527f0,
+/* 0x08fa: ctx_xfer_post_save_wait */
+       0x12cf0012,
+       0x0522fd00,
+       0xf4fa1bf4,
+/* 0x0906: ctx_xfer_post */
+       0xf7f03202,
+       0x5121f502,
+       0xf5f4bd06,
+       0xf5069221,
+       0xf5023421,
+       0xbd066021,
+       0x5121f5f4,
+       0x1011f406,
+       0xfd400198,
+       0x0bf40511,
+       0xe521f507,
+/* 0x0931: ctx_xfer_no_post_mmio */
+       0x4321f507,
+/* 0x0935: ctx_xfer_done */
+       0x0000f806,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
index 7fe9d7c..d4840f1 100644 (file)
@@ -1,6 +1,5 @@
-/* fuc microcode for nve0 PGRAPH/HUB
- *
- * Copyright 2011 Red Hat Inc.
+/*
+ * 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"),
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  * OTHER DEALINGS IN THE SOFTWARE.
  *
- * Authors: Ben Skeggs
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
  */
 
-/* To build:
- *    m4 nve0_grhub.fuc | envyas -a -w -m fuc -V nva3 -o nve0_grhub.fuc.h
- */
+#define CHIPSET GK100
+#include "macros.fuc"
 
 .section #nve0_grhub_data
-include(`nve0.fuc')
-gpc_count:             .b32 0
-rop_count:             .b32 0
-cmd_queue:             queue_init
-hub_mmio_list_head:    .b32 0
-hub_mmio_list_tail:    .b32 0
-
-ctx_current:           .b32 0
-
-chipsets:
-.b8  0xe4 0 0 0
-.b16 #nve4_hub_mmio_head
-.b16 #nve4_hub_mmio_tail
-.b8  0xe7 0 0 0
-.b16 #nve4_hub_mmio_head
-.b16 #nve4_hub_mmio_tail
-.b8  0xe6 0 0 0
-.b16 #nve4_hub_mmio_head
-.b16 #nve4_hub_mmio_tail
-.b8  0 0 0 0
-
-nve4_hub_mmio_head:
-mmctx_data(0x17e91c, 2)
-mmctx_data(0x400204, 2)
-mmctx_data(0x404010, 7)
-mmctx_data(0x4040a8, 9)
-mmctx_data(0x4040d0, 7)
-mmctx_data(0x4040f8, 1)
-mmctx_data(0x404130, 3)
-mmctx_data(0x404150, 3)
-mmctx_data(0x404164, 1)
-mmctx_data(0x4041a0, 4)
-mmctx_data(0x404200, 4)
-mmctx_data(0x404404, 14)
-mmctx_data(0x404460, 4)
-mmctx_data(0x404480, 1)
-mmctx_data(0x404498, 1)
-mmctx_data(0x404604, 4)
-mmctx_data(0x404618, 4)
-mmctx_data(0x40462c, 2)
-mmctx_data(0x404640, 1)
-mmctx_data(0x404654, 1)
-mmctx_data(0x404660, 1)
-mmctx_data(0x404678, 19)
-mmctx_data(0x4046c8, 3)
-mmctx_data(0x404700, 3)
-mmctx_data(0x404718, 10)
-mmctx_data(0x404744, 2)
-mmctx_data(0x404754, 1)
-mmctx_data(0x405800, 1)
-mmctx_data(0x405830, 3)
-mmctx_data(0x405854, 1)
-mmctx_data(0x405870, 4)
-mmctx_data(0x405a00, 2)
-mmctx_data(0x405a18, 1)
-mmctx_data(0x405b00, 1)
-mmctx_data(0x405b10, 1)
-mmctx_data(0x406020, 1)
-mmctx_data(0x406028, 4)
-mmctx_data(0x4064a8, 2)
-mmctx_data(0x4064b4, 2)
-mmctx_data(0x4064c0, 12)
-mmctx_data(0x4064fc, 1)
-mmctx_data(0x407040, 1)
-mmctx_data(0x407804, 1)
-mmctx_data(0x40780c, 6)
-mmctx_data(0x4078bc, 1)
-mmctx_data(0x408000, 7)
-mmctx_data(0x408064, 1)
-mmctx_data(0x408800, 3)
-mmctx_data(0x408840, 1)
-mmctx_data(0x408900, 3)
-mmctx_data(0x408980, 1)
-nve4_hub_mmio_tail:
-
-.align 256
-chan_data:
-chan_mmio_count:       .b32 0
-chan_mmio_address:     .b32 0
-
-.align 256
-xfer_data:             .b32 0
+#define INCLUDE_DATA
+#include "com.fuc"
+#include "hub.fuc"
+#undef INCLUDE_DATA
 
 .section #nve0_grhub_code
+#define INCLUDE_CODE
 bra #init
-define(`include_code')
-include(`nve0.fuc')
-
-// reports an exception to the host
-//
-// In: $r15 error code (see nve0.fuc)
-//
-error:
-       push $r14
-       mov $r14 0x814
-       shl b32 $r14 6
-       iowr I[$r14 + 0x000] $r15       // CC_SCRATCH[5] = error code
-       mov $r14 0xc1c
-       shl b32 $r14 6
-       mov $r15 1
-       iowr I[$r14 + 0x000] $r15       // INTR_UP_SET
-       pop $r14
-       ret
-
-// HUB fuc initialisation, executed by triggering ucode start, will
-// fall through to main loop after completion.
-//
-// Input:
-//   CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh)
-//
-// Output:
-//   CC_SCRATCH[0]:
-//          31:31: set to signal completion
-//   CC_SCRATCH[1]:
-//           31:0: total PGRAPH context size
-//
-init:
-       clear b32 $r0
-       mov $sp $r0
-       mov $xdbase $r0
-
-       // enable fifo access
-       mov $r1 0x1200
-       mov $r2 2
-       iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE
-
-       // setup i0 handler, and route all interrupts to it
-       mov $r1 #ih
-       mov $iv0 $r1
-       mov $r1 0x400
-       iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH
-
-       // route HUB_CHANNEL_SWITCH to fuc interrupt 8
-       mov $r3 0x404
-       shl b32 $r3 6
-       mov $r2 0x2003          // { HUB_CHANNEL_SWITCH, ZERO } -> intr 8
-       iowr I[$r3 + 0x000] $r2
-
-       // not sure what these are, route them because NVIDIA does, and
-       // the IRQ handler will signal the host if we ever get one.. we
-       // may find out if/why we need to handle these if so..
-       //
-       mov $r2 0x2004
-       iowr I[$r3 + 0x004] $r2 // { 0x04, ZERO } -> intr 9
-       mov $r2 0x200b
-       iowr I[$r3 + 0x008] $r2 // { 0x0b, ZERO } -> intr 10
-       mov $r2 0x200c
-       iowr I[$r3 + 0x01c] $r2 // { 0x0c, ZERO } -> intr 15
-
-       // enable all INTR_UP interrupts
-       mov $r2 0xc24
-       shl b32 $r2 6
-       not b32 $r3 $r0
-       iowr I[$r2] $r3
-
-       // enable fifo, ctxsw, 9, 10, 15 interrupts
-       mov $r2 -0x78fc         // 0x8704
-       sethi $r2 0
-       iowr I[$r1 + 0x000] $r2 // INTR_EN_SET
-
-       // fifo level triggered, rest edge
-       sub b32 $r1 0x100
-       mov $r2 4
-       iowr I[$r1] $r2
-
-       // enable interrupts
-       bset $flags ie0
-
-       // fetch enabled GPC/ROP counts
-       mov $r14 -0x69fc        // 0x409604
-       sethi $r14 0x400000
-       call #nv_rd32
-       extr $r1 $r15 16:20
-       st b32 D[$r0 + #rop_count] $r1
-       and $r15 0x1f
-       st b32 D[$r0 + #gpc_count] $r15
-
-       // set BAR_REQMASK to GPC mask
-       mov $r1 1
-       shl b32 $r1 $r15
-       sub b32 $r1 1
-       mov $r2 0x40c
-       shl b32 $r2 6
-       iowr I[$r2 + 0x000] $r1
-       iowr I[$r2 + 0x100] $r1
-
-       // find context data for this chipset
-       mov $r2 0x800
-       shl b32 $r2 6
-       iord $r2 I[$r2 + 0x000]         // CC_SCRATCH[0]
-       mov $r15 #chipsets - 8
-       init_find_chipset:
-               add b32 $r15 8
-               ld b32 $r3 D[$r15 + 0x00]
-               cmpu b32 $r3 $r2
-               bra e #init_context
-               cmpu b32 $r3 0
-               bra ne #init_find_chipset
-               // unknown chipset
-               ret
-
-       // context size calculation, reserve first 256 bytes for use by fuc
-       init_context:
-       mov $r1 256
-
-       // calculate size of mmio context data
-       ld b16 $r14 D[$r15 + 4]
-       ld b16 $r15 D[$r15 + 6]
-       sethi $r14 0
-       st b32 D[$r0 + #hub_mmio_list_head] $r14
-       st b32 D[$r0 + #hub_mmio_list_tail] $r15
-       call #mmctx_size
-
-       // set mmctx base addresses now so we don't have to do it later,
-       // they don't (currently) ever change
-       mov $r3 0x700
-       shl b32 $r3 6
-       shr b32 $r4 $r1 8
-       iowr I[$r3 + 0x000] $r4         // MMCTX_SAVE_SWBASE
-       iowr I[$r3 + 0x100] $r4         // MMCTX_LOAD_SWBASE
-       add b32 $r3 0x1300
-       add b32 $r1 $r15
-       shr b32 $r15 2
-       iowr I[$r3 + 0x000] $r15        // MMCTX_LOAD_COUNT, wtf for?!?
-
-       // strands, base offset needs to be aligned to 256 bytes
-       shr b32 $r1 8
-       add b32 $r1 1
-       shl b32 $r1 8
-       mov b32 $r15 $r1
-       call #strand_ctx_init
-       add b32 $r1 $r15
-
-       // initialise each GPC in sequence by passing in the offset of its
-       // context data in GPCn_CC_SCRATCH[1], and starting its FUC (which
-       // has previously been uploaded by the host) running.
-       //
-       // the GPC fuc init sequence will set GPCn_CC_SCRATCH[0] bit 31
-       // when it has completed, and return the size of its context data
-       // in GPCn_CC_SCRATCH[1]
-       //
-       ld b32 $r3 D[$r0 + #gpc_count]
-       mov $r4 0x2000
-       sethi $r4 0x500000
-       init_gpc:
-               // setup, and start GPC ucode running
-               add b32 $r14 $r4 0x804
-               mov b32 $r15 $r1
-               call #nv_wr32                   // CC_SCRATCH[1] = ctx offset
-               add b32 $r14 $r4 0x800
-               mov b32 $r15 $r2
-               call #nv_wr32                   // CC_SCRATCH[0] = chipset
-               add b32 $r14 $r4 0x10c
-               clear b32 $r15
-               call #nv_wr32
-               add b32 $r14 $r4 0x104
-               call #nv_wr32                   // ENTRY
-               add b32 $r14 $r4 0x100
-               mov $r15 2                      // CTRL_START_TRIGGER
-               call #nv_wr32                   // CTRL
-
-               // wait for it to complete, and adjust context size
-               add b32 $r14 $r4 0x800
-               init_gpc_wait:
-                       call #nv_rd32
-                       xbit $r15 $r15 31
-                       bra e #init_gpc_wait
-               add b32 $r14 $r4 0x804
-               call #nv_rd32
-               add b32 $r1 $r15
-
-               // next!
-               add b32 $r4 0x8000
-               sub b32 $r3 1
-               bra ne #init_gpc
-
-       // save context size, and tell host we're ready
-       mov $r2 0x800
-       shl b32 $r2 6
-       iowr I[$r2 + 0x100] $r1         // CC_SCRATCH[1]  = context size
-       add b32 $r2 0x800
-       clear b32 $r1
-       bset $r1 31
-       iowr I[$r2 + 0x000] $r1         // CC_SCRATCH[0] |= 0x80000000
-
-// Main program loop, very simple, sleeps until woken up by the interrupt
-// handler, pulls a command from the queue and executes its handler
-//
-main:
-       // sleep until we have something to do
-       bset $flags $p0
-       sleep $p0
-       mov $r13 #cmd_queue
-       call #queue_get
-       bra $p1 #main
-
-       // context switch, requested by GPU?
-       cmpu b32 $r14 0x4001
-       bra ne #main_not_ctx_switch
-               trace_set(T_AUTO)
-               mov $r1 0xb00
-               shl b32 $r1 6
-               iord $r2 I[$r1 + 0x100]         // CHAN_NEXT
-               iord $r1 I[$r1 + 0x000]         // CHAN_CUR
-
-               xbit $r3 $r1 31
-               bra e #chsw_no_prev
-                       xbit $r3 $r2 31
-                       bra e #chsw_prev_no_next
-                               push $r2
-                               mov b32 $r2 $r1
-                               trace_set(T_SAVE)
-                               bclr $flags $p1
-                               bset $flags $p2
-                               call #ctx_xfer
-                               trace_clr(T_SAVE);
-                               pop $r2
-                               trace_set(T_LOAD);
-                               bset $flags $p1
-                               call #ctx_xfer
-                               trace_clr(T_LOAD);
-                               bra #chsw_done
-                       chsw_prev_no_next:
-                               push $r2
-                               mov b32 $r2 $r1
-                               bclr $flags $p1
-                               bclr $flags $p2
-                               call #ctx_xfer
-                               pop $r2
-                               mov $r1 0xb00
-                               shl b32 $r1 6
-                               iowr I[$r1] $r2
-                               bra #chsw_done
-               chsw_no_prev:
-                       xbit $r3 $r2 31
-                       bra e #chsw_done
-                               bset $flags $p1
-                               bclr $flags $p2
-                               call #ctx_xfer
-
-               // ack the context switch request
-               chsw_done:
-               mov $r1 0xb0c
-               shl b32 $r1 6
-               mov $r2 1
-               iowr I[$r1 + 0x000] $r2         // 0x409b0c
-               trace_clr(T_AUTO)
-               bra #main
-
-       // request to set current channel? (*not* a context switch)
-       main_not_ctx_switch:
-       cmpu b32 $r14 0x0001
-       bra ne #main_not_ctx_chan
-               mov b32 $r2 $r15
-               call #ctx_chan
-               bra #main_done
-
-       // request to store current channel context?
-       main_not_ctx_chan:
-       cmpu b32 $r14 0x0002
-       bra ne #main_not_ctx_save
-               trace_set(T_SAVE)
-               bclr $flags $p1
-               bclr $flags $p2
-               call #ctx_xfer
-               trace_clr(T_SAVE)
-               bra #main_done
-
-       main_not_ctx_save:
-               shl b32 $r15 $r14 16
-               or $r15 E_BAD_COMMAND
-               call #error
-               bra #main
-
-       main_done:
-       mov $r1 0x820
-       shl b32 $r1 6
-       clear b32 $r2
-       bset $r2 31
-       iowr I[$r1 + 0x000] $r2         // CC_SCRATCH[0] |= 0x80000000
-       bra #main
-
-// interrupt handler
-ih:
-       push $r8
-       mov $r8 $flags
-       push $r8
-       push $r9
-       push $r10
-       push $r11
-       push $r13
-       push $r14
-       push $r15
-
-       // incoming fifo command?
-       iord $r10 I[$r0 + 0x200]        // INTR
-       and $r11 $r10 0x00000004
-       bra e #ih_no_fifo
-               // queue incoming fifo command for later processing
-               mov $r11 0x1900
-               mov $r13 #cmd_queue
-               iord $r14 I[$r11 + 0x100]       // FIFO_CMD
-               iord $r15 I[$r11 + 0x000]       // FIFO_DATA
-               call #queue_put
-               add b32 $r11 0x400
-               mov $r14 1
-               iowr I[$r11 + 0x000] $r14       // FIFO_ACK
-
-       // context switch request?
-       ih_no_fifo:
-       and $r11 $r10 0x00000100
-       bra e #ih_no_ctxsw
-               // enqueue a context switch for later processing
-               mov $r13 #cmd_queue
-               mov $r14 0x4001
-               call #queue_put
-
-       // anything we didn't handle, bring it to the host's attention
-       ih_no_ctxsw:
-       mov $r11 0x104
-       not b32 $r11
-       and $r11 $r10 $r11
-       bra e #ih_no_other
-               mov $r10 0xc1c
-               shl b32 $r10 6
-               iowr I[$r10] $r11       // INTR_UP_SET
-
-       // ack, and wake up main()
-       ih_no_other:
-       iowr I[$r0 + 0x100] $r10        // INTR_ACK
-
-       pop $r15
-       pop $r14
-       pop $r13
-       pop $r11
-       pop $r10
-       pop $r9
-       pop $r8
-       mov $flags $r8
-       pop $r8
-       bclr $flags $p0
-       iret
-
-// Again, not real sure
-//
-// In: $r15 value to set 0x404170 to
-//
-ctx_4170s:
-       mov $r14 0x4170
-       sethi $r14 0x400000
-       or $r15 0x10
-       call #nv_wr32
-       ret
-
-// Waits for a ctx_4170s() call to complete
-//
-ctx_4170w:
-       mov $r14 0x4170
-       sethi $r14 0x400000
-       call #nv_rd32
-       and $r15 0x10
-       bra ne #ctx_4170w
-       ret
-
-// Disables various things, waits a bit, and re-enables them..
-//
-// Not sure how exactly this helps, perhaps "ENABLE" is not such a
-// good description for the bits we turn off?  Anyways, without this,
-// funny things happen.
-//
-ctx_redswitch:
-       mov $r14 0x614
-       shl b32 $r14 6
-       mov $r15 0x270
-       iowr I[$r14] $r15       // HUB_RED_SWITCH = ENABLE_GPC, POWER_ALL
-       mov $r15 8
-       ctx_redswitch_delay:
-               sub b32 $r15 1
-               bra ne #ctx_redswitch_delay
-       mov $r15 0x770
-       iowr I[$r14] $r15       // HUB_RED_SWITCH = ENABLE_ALL, POWER_ALL
-       ret
-
-// Not a clue what this is for, except that unless the value is 0x10, the
-// strand context is saved (and presumably restored) incorrectly..
-//
-// In: $r15 value to set to (0x00/0x10 are used)
-//
-ctx_86c:
-       mov $r14 0x86c
-       shl b32 $r14 6
-       iowr I[$r14] $r15       // HUB(0x86c) = val
-       mov $r14 -0x75ec
-       sethi $r14 0x400000
-       call #nv_wr32           // ROP(0xa14) = val
-       mov $r14 -0x5794
-       sethi $r14 0x410000
-       call #nv_wr32           // GPC(0x86c) = val
-       ret
-
-// ctx_load - load's a channel's ctxctl data, and selects its vm
-//
-// In: $r2 channel address
-//
-ctx_load:
-       trace_set(T_CHAN)
-
-       // switch to channel, somewhat magic in parts..
-       mov $r10 12             // DONE_UNK12
-       call #wait_donez
-       mov $r1 0xa24
-       shl b32 $r1 6
-       iowr I[$r1 + 0x000] $r0 // 0x409a24
-       mov $r3 0xb00
-       shl b32 $r3 6
-       iowr I[$r3 + 0x100] $r2 // CHAN_NEXT
-       mov $r1 0xa0c
-       shl b32 $r1 6
-       mov $r4 7
-       iowr I[$r1 + 0x000] $r2 // MEM_CHAN
-       iowr I[$r1 + 0x100] $r4 // MEM_CMD
-       ctx_chan_wait_0:
-               iord $r4 I[$r1 + 0x100]
-               and $r4 0x1f
-               bra ne #ctx_chan_wait_0
-       iowr I[$r3 + 0x000] $r2 // CHAN_CUR
-
-       // load channel header, fetch PGRAPH context pointer
-       mov $xtargets $r0
-       bclr $r2 31
-       shl b32 $r2 4
-       add b32 $r2 2
-
-       trace_set(T_LCHAN)
-       mov $r1 0xa04
-       shl b32 $r1 6
-       iowr I[$r1 + 0x000] $r2         // MEM_BASE
-       mov $r1 0xa20
-       shl b32 $r1 6
-       mov $r2 0x0002
-       sethi $r2 0x80000000
-       iowr I[$r1 + 0x000] $r2         // MEM_TARGET = vram
-       mov $r1 0x10                    // chan + 0x0210
-       mov $r2 #xfer_data
-       sethi $r2 0x00020000            // 16 bytes
-       xdld $r1 $r2
-       xdwait
-       trace_clr(T_LCHAN)
-
-       // update current context
-       ld b32 $r1 D[$r0 + #xfer_data + 4]
-       shl b32 $r1 24
-       ld b32 $r2 D[$r0 + #xfer_data + 0]
-       shr b32 $r2 8
-       or $r1 $r2
-       st b32 D[$r0 + #ctx_current] $r1
-
-       // set transfer base to start of context, and fetch context header
-       trace_set(T_LCTXH)
-       mov $r2 0xa04
-       shl b32 $r2 6
-       iowr I[$r2 + 0x000] $r1         // MEM_BASE
-       mov $r2 1
-       mov $r1 0xa20
-       shl b32 $r1 6
-       iowr I[$r1 + 0x000] $r2         // MEM_TARGET = vm
-       mov $r1 #chan_data
-       sethi $r1 0x00060000            // 256 bytes
-       xdld $r0 $r1
-       xdwait
-       trace_clr(T_LCTXH)
-
-       trace_clr(T_CHAN)
-       ret
-
-// ctx_chan - handler for HUB_SET_CHAN command, will set a channel as
-//            the active channel for ctxctl, but not actually transfer
-//            any context data.  intended for use only during initial
-//            context construction.
-//
-// In: $r2 channel address
-//
-ctx_chan:
-       call #ctx_load
-       mov $r10 12                     // DONE_UNK12
-       call #wait_donez
-       mov $r1 0xa10
-       shl b32 $r1 6
-       mov $r2 5
-       iowr I[$r1 + 0x000] $r2         // MEM_CMD = 5 (???)
-       ctx_chan_wait:
-               iord $r2 I[$r1 + 0x000]
-               or $r2 $r2
-               bra ne #ctx_chan_wait
-       ret
-
-// Execute per-context state overrides list
-//
-// Only executed on the first load of a channel.  Might want to look into
-// removing this and having the host directly modify the channel's context
-// to change this state...  The nouveau DRM already builds this list as
-// it's definitely needed for NVIDIA's, so we may as well use it for now
-//
-// Input: $r1 mmio list length
-//
-ctx_mmio_exec:
-       // set transfer base to be the mmio list
-       ld b32 $r3 D[$r0 + #chan_mmio_address]
-       mov $r2 0xa04
-       shl b32 $r2 6
-       iowr I[$r2 + 0x000] $r3         // MEM_BASE
-
-       clear b32 $r3
-       ctx_mmio_loop:
-               // fetch next 256 bytes of mmio list if necessary
-               and $r4 $r3 0xff
-               bra ne #ctx_mmio_pull
-                       mov $r5 #xfer_data
-                       sethi $r5 0x00060000    // 256 bytes
-                       xdld $r3 $r5
-                       xdwait
-
-               // execute a single list entry
-               ctx_mmio_pull:
-               ld b32 $r14 D[$r4 + #xfer_data + 0x00]
-               ld b32 $r15 D[$r4 + #xfer_data + 0x04]
-               call #nv_wr32
-
-               // next!
-               add b32 $r3 8
-               sub b32 $r1 1
-               bra ne #ctx_mmio_loop
-
-       // set transfer base back to the current context
-       ctx_mmio_done:
-       ld b32 $r3 D[$r0 + #ctx_current]
-       iowr I[$r2 + 0x000] $r3         // MEM_BASE
-
-       // disable the mmio list now, we don't need/want to execute it again
-       st b32 D[$r0 + #chan_mmio_count] $r0
-       mov $r1 #chan_data
-       sethi $r1 0x00060000            // 256 bytes
-       xdst $r0 $r1
-       xdwait
-       ret
-
-// Transfer HUB context data between GPU and storage area
-//
-// In: $r2 channel address
-//     $p1 clear on save, set on load
-//     $p2 set if opposite direction done/will be done, so:
-//             on save it means: "a load will follow this save"
-//             on load it means: "a save preceeded this load"
-//
-ctx_xfer:
-       // according to mwk, some kind of wait for idle
-       mov $r15 0xc00
-       shl b32 $r15 6
-       mov $r14 4
-       iowr I[$r15 + 0x200] $r14
-       ctx_xfer_idle:
-               iord $r14 I[$r15 + 0x000]
-               and $r14 0x2000
-               bra ne #ctx_xfer_idle
-
-       bra not $p1 #ctx_xfer_pre
-       bra $p2 #ctx_xfer_pre_load
-       ctx_xfer_pre:
-               mov $r15 0x10
-               call #ctx_86c
-               bra not $p1 #ctx_xfer_exec
-
-       ctx_xfer_pre_load:
-               mov $r15 2
-               call #ctx_4170s
-               call #ctx_4170w
-               call #ctx_redswitch
-               clear b32 $r15
-               call #ctx_4170s
-               call #ctx_load
-
-       // fetch context pointer, and initiate xfer on all GPCs
-       ctx_xfer_exec:
-       ld b32 $r1 D[$r0 + #ctx_current]
-       mov $r2 0x414
-       shl b32 $r2 6
-       iowr I[$r2 + 0x000] $r0 // BAR_STATUS = reset
-       mov $r14 -0x5b00
-       sethi $r14 0x410000
-       mov b32 $r15 $r1
-       call #nv_wr32           // GPC_BCAST_WRCMD_DATA = ctx pointer
-       add b32 $r14 4
-       xbit $r15 $flags $p1
-       xbit $r2 $flags $p2
-       shl b32 $r2 1
-       or $r15 $r2
-       call #nv_wr32           // GPC_BCAST_WRCMD_CMD = GPC_XFER(type)
-
-       // strands
-       mov $r1 0x4afc
-       sethi $r1 0x20000
-       mov $r2 0xc
-       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x0c
-       call #strand_wait
-       mov $r2 0x47fc
-       sethi $r2 0x20000
-       iowr I[$r2] $r0         // STRAND_FIRST_GENE(0x3f) = 0x00
-       xbit $r2 $flags $p1
-       add b32 $r2 3
-       iowr I[$r1] $r2         // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD)
-
-       // mmio context
-       xbit $r10 $flags $p1    // direction
-       or $r10 6               // first, last
-       mov $r11 0              // base = 0
-       ld b32 $r12 D[$r0 + #hub_mmio_list_head]
-       ld b32 $r13 D[$r0 + #hub_mmio_list_tail]
-       mov $r14 0              // not multi
-       call #mmctx_xfer
-
-       // wait for GPCs to all complete
-       mov $r10 8              // DONE_BAR
-       call #wait_doneo
-
-       // wait for strand xfer to complete
-       call #strand_wait
-
-       // post-op
-       bra $p1 #ctx_xfer_post
-               mov $r10 12             // DONE_UNK12
-               call #wait_donez
-               mov $r1 0xa10
-               shl b32 $r1 6
-               mov $r2 5
-               iowr I[$r1] $r2         // MEM_CMD
-               ctx_xfer_post_save_wait:
-                       iord $r2 I[$r1]
-                       or $r2 $r2
-                       bra ne #ctx_xfer_post_save_wait
-
-       bra $p2 #ctx_xfer_done
-       ctx_xfer_post:
-               mov $r15 2
-               call #ctx_4170s
-               clear b32 $r15
-               call #ctx_86c
-               call #strand_post
-               call #ctx_4170w
-               clear b32 $r15
-               call #ctx_4170s
-
-               bra not $p1 #ctx_xfer_no_post_mmio
-               ld b32 $r1 D[$r0 + #chan_mmio_count]
-               or $r1 $r1
-               bra e #ctx_xfer_no_post_mmio
-                       call #ctx_mmio_exec
-
-               ctx_xfer_no_post_mmio:
-
-       ctx_xfer_done:
-       ret
-
+#include "com.fuc"
+#include "hub.fuc"
 .align 256
+#undef INCLUDE_CODE
index e3421af..eb7bc0e 100644 (file)
@@ -1,9 +1,13 @@
 uint32_t nve0_grhub_data[] = {
-/* 0x0000: gpc_count */
+/* 0x0000: hub_mmio_list_head */
+       0x00000300,
+/* 0x0004: hub_mmio_list_tail */
+       0x00000304,
+/* 0x0008: gpc_count */
        0x00000000,
-/* 0x0004: rop_count */
+/* 0x000c: rop_count */
        0x00000000,
-/* 0x0008: cmd_queue */
+/* 0x0010: cmd_queue */
        0x00000000,
        0x00000000,
        0x00000000,
@@ -22,73 +26,11 @@ uint32_t nve0_grhub_data[] = {
        0x00000000,
        0x00000000,
        0x00000000,
-/* 0x0050: hub_mmio_list_head */
+/* 0x0058: ctx_current */
        0x00000000,
-/* 0x0054: hub_mmio_list_tail */
        0x00000000,
-/* 0x0058: ctx_current */
        0x00000000,
-/* 0x005c: chipsets */
-       0x000000e4,
-       0x01440078,
-       0x000000e7,
-       0x01440078,
-       0x000000e6,
-       0x01440078,
        0x00000000,
-/* 0x0078: nve4_hub_mmio_head */
-       0x0417e91c,
-       0x04400204,
-       0x18404010,
-       0x204040a8,
-       0x184040d0,
-       0x004040f8,
-       0x08404130,
-       0x08404150,
-       0x00404164,
-       0x0c4041a0,
-       0x0c404200,
-       0x34404404,
-       0x0c404460,
-       0x00404480,
-       0x00404498,
-       0x0c404604,
-       0x0c404618,
-       0x0440462c,
-       0x00404640,
-       0x00404654,
-       0x00404660,
-       0x48404678,
-       0x084046c8,
-       0x08404700,
-       0x24404718,
-       0x04404744,
-       0x00404754,
-       0x00405800,
-       0x08405830,
-       0x00405854,
-       0x0c405870,
-       0x04405a00,
-       0x00405a18,
-       0x00405b00,
-       0x00405b10,
-       0x00406020,
-       0x0c406028,
-       0x044064a8,
-       0x044064b4,
-       0x2c4064c0,
-       0x004064fc,
-       0x00407040,
-       0x00407804,
-       0x1440780c,
-       0x004078bc,
-       0x18408000,
-       0x00408064,
-       0x08408800,
-       0x00408840,
-       0x08408900,
-       0x00408980,
-/* 0x0144: nve4_hub_mmio_tail */
        0x00000000,
        0x00000000,
        0x00000000,
@@ -127,6 +69,47 @@ uint32_t nve0_grhub_data[] = {
        0x00000000,
        0x00000000,
        0x00000000,
+/* 0x0100: chan_data */
+/* 0x0100: chan_mmio_count */
+       0x00000000,
+/* 0x0104: chan_mmio_address */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
        0x00000000,
        0x00000000,
        0x00000000,
@@ -136,10 +119,7 @@ uint32_t nve0_grhub_data[] = {
        0x00000000,
        0x00000000,
        0x00000000,
-/* 0x0200: chan_data */
-/* 0x0200: chan_mmio_count */
        0x00000000,
-/* 0x0204: chan_mmio_address */
        0x00000000,
        0x00000000,
        0x00000000,
@@ -156,6 +136,7 @@ uint32_t nve0_grhub_data[] = {
        0x00000000,
        0x00000000,
        0x00000000,
+/* 0x0200: xfer_data */
        0x00000000,
        0x00000000,
        0x00000000,
@@ -203,19 +184,36 @@ uint32_t nve0_grhub_data[] = {
        0x00000000,
        0x00000000,
        0x00000000,
-/* 0x0300: xfer_data */
        0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0300: hub_mmio_list_base */
+       0x0417e91c,
 };
 
 uint32_t nve0_grhub_code[] = {
-       0x03090ef5,
+       0x031b0ef5,
 /* 0x0004: queue_put */
        0x9800d898,
        0x86f001d9,
        0x0489b808,
        0xf00c1bf4,
        0x21f502f7,
-       0x00f802ec,
+       0x00f802fe,
 /* 0x001c: queue_put_next */
        0xb60798c4,
        0x8dbb0384,
@@ -247,7 +245,7 @@ uint32_t nve0_grhub_code[] = {
        0xc800bccf,
        0x1bf41fcc,
        0x06a7f0fa,
-       0x010321f5,
+       0x010921f5,
        0xf840bfcf,
 /* 0x008d: nv_wr32 */
        0x28b7f100,
@@ -269,63 +267,66 @@ uint32_t nve0_grhub_code[] = {
        0x0684b604,
        0xf80080d0,
 /* 0x00c9: wait_donez */
-       0x3c87f100,
-       0x0684b608,
-       0x99f094bd,
-       0x0089d000,
-       0x081887f1,
-       0xd00684b6,
-/* 0x00e2: wait_done_wait_donez */
-       0x87f1008a,
-       0x84b60400,
-       0x0088cf06,
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x07f104bd,
+       0x03f00600,
+       0x000ad002,
+/* 0x00e6: wait_donez_ne */
+       0x87f104bd,
+       0x83f00000,
+       0x0088cf01,
        0xf4888aff,
-       0x87f1f31b,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00099,
-/* 0x0103: wait_doneo */
-       0xf100f800,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00099f0,
-       0x87f10089,
+       0x94bdf31b,
+       0xf10099f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0109: wait_doneo */
+       0xf094bd00,
+       0x07f10099,
+       0x03f00f00,
+       0x0009d002,
+       0x87f104bd,
        0x84b60818,
        0x008ad006,
-/* 0x011c: wait_done_wait_doneo */
+/* 0x0124: wait_doneo_e */
        0x040087f1,
        0xcf0684b6,
        0x8aff0088,
        0xf30bf488,
-       0x085c87f1,
-       0xbd0684b6,
-       0x0099f094,
-       0xf80089d0,
-/* 0x013d: mmctx_size */
-/* 0x013f: nv_mmctx_size_loop */
-       0x9894bd00,
-       0x85b600e8,
-       0x0180b61a,
-       0xbb0284b6,
-       0xe0b60098,
-       0x04efb804,
-       0xb9eb1bf4,
-       0x00f8029f,
-/* 0x015c: mmctx_xfer */
-       0x083c87f1,
-       0xbd0684b6,
-       0x0199f094,
-       0xf10089d0,
+       0x99f094bd,
+       0x0007f100,
+       0x0203f017,
+       0xbd0009d0,
+/* 0x0147: mmctx_size */
+       0xbd00f804,
+/* 0x0149: nv_mmctx_size_loop */
+       0x00e89894,
+       0xb61a85b6,
+       0x84b60180,
+       0x0098bb02,
+       0xb804e0b6,
+       0x1bf404ef,
+       0x029fb9eb,
+/* 0x0166: mmctx_xfer */
+       0x94bd00f8,
+       0xf10199f0,
+       0xf00f0007,
+       0x09d00203,
+       0xf104bd00,
        0xb6071087,
        0x94bd0684,
        0xf405bbfd,
        0x8bd0090b,
        0x0099f000,
-/* 0x0180: mmctx_base_disabled */
+/* 0x018c: mmctx_base_disabled */
        0xf405eefd,
        0x8ed00c0b,
        0xc08fd080,
-/* 0x018f: mmctx_multi_disabled */
+/* 0x019b: mmctx_multi_disabled */
        0xb70199f0,
        0xc8010080,
        0xb4b600ab,
@@ -333,8 +334,8 @@ uint32_t nve0_grhub_code[] = {
        0xb601aec8,
        0xbefd11e4,
        0x008bd005,
-/* 0x01a8: mmctx_exec_loop */
-/* 0x01a8: mmctx_wait_free */
+/* 0x01b4: mmctx_exec_loop */
+/* 0x01b4: mmctx_wait_free */
        0xf0008ecf,
        0x0bf41fe4,
        0x00ce98fa,
@@ -343,76 +344,77 @@ uint32_t nve0_grhub_code[] = {
        0x04cdb804,
        0xc8e81bf4,
        0x1bf402ab,
-/* 0x01c9: mmctx_fini_wait */
+/* 0x01d5: mmctx_fini_wait */
        0x008bcf18,
        0xb01fb4f0,
        0x1bf410b4,
        0x02a7f0f7,
        0xf4c921f4,
-/* 0x01de: mmctx_stop */
+/* 0x01ea: mmctx_stop */
        0xabc81b0e,
        0x10b4b600,
        0xf00cb9f0,
        0x8bd012b9,
-/* 0x01ed: mmctx_stop_wait */
+/* 0x01f9: mmctx_stop_wait */
        0x008bcf00,
        0xf412bbc8,
-/* 0x01f6: mmctx_done */
-       0x87f1fa1b,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00199,
-/* 0x0207: strand_wait */
-       0xf900f800,
-       0x02a7f0a0,
-       0xfcc921f4,
-/* 0x0213: strand_pre */
-       0xf100f8a0,
-       0xf04afc87,
-       0x97f00283,
-       0x0089d00c,
-       0x020721f5,
-/* 0x0226: strand_post */
-       0x87f100f8,
-       0x83f04afc,
-       0x0d97f002,
-       0xf50089d0,
-       0xf8020721,
-/* 0x0239: strand_set */
-       0xfca7f100,
-       0x02a3f04f,
-       0x0500aba2,
-       0xd00fc7f0,
-       0xc7f000ac,
-       0x00bcd00b,
-       0x020721f5,
-       0xf000aed0,
-       0xbcd00ac7,
-       0x0721f500,
-/* 0x0263: strand_ctx_init */
-       0xf100f802,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00399f0,
+/* 0x0202: mmctx_done */
+       0x94bdfa1b,
+       0xf10199f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0215: strand_wait */
+       0xf0a0f900,
+       0x21f402a7,
+       0xf8a0fcc9,
+/* 0x0221: strand_pre */
+       0xfc87f100,
+       0x0283f04a,
+       0xd00c97f0,
        0x21f50089,
-       0xe7f00213,
-       0x3921f503,
+       0x00f80215,
+/* 0x0234: strand_post */
+       0x4afc87f1,
+       0xf00283f0,
+       0x89d00d97,
+       0x1521f500,
+/* 0x0247: strand_set */
+       0xf100f802,
+       0xf04ffca7,
+       0xaba202a3,
+       0xc7f00500,
+       0x00acd00f,
+       0xd00bc7f0,
+       0x21f500bc,
+       0xaed00215,
+       0x0ac7f000,
+       0xf500bcd0,
+       0xf8021521,
+/* 0x0271: strand_ctx_init */
+       0xf094bd00,
+       0x07f10399,
+       0x03f00f00,
+       0x0009d002,
+       0x21f504bd,
+       0xe7f00221,
+       0x4721f503,
        0xfca7f102,
        0x02a3f046,
        0x0400aba0,
        0xf040a0d0,
        0xbcd001c7,
-       0x0721f500,
+       0x1521f500,
        0x010c9202,
        0xf000acd0,
        0xbcd002c7,
-       0x0721f500,
-       0x2621f502,
+       0x1521f500,
+       0x3421f502,
        0x8087f102,
        0x0684b608,
        0xb70089cf,
        0x95220080,
-/* 0x02ba: ctx_init_strand_loop */
+/* 0x02ca: ctx_init_strand_loop */
        0x8ed008fe,
        0x408ed000,
        0xb6808acf,
@@ -421,73 +423,61 @@ uint32_t nve0_grhub_code[] = {
        0xb60480b6,
        0x1bf40192,
        0x08e4b6e8,
-       0xf1f2efbc,
-       0xb6085c87,
-       0x94bd0684,
-       0xd00399f0,
-       0x00f80089,
-/* 0x02ec: error */
-       0xe7f1e0f9,
-       0xe4b60814,
-       0x00efd006,
-       0x0c1ce7f1,
-       0xf006e4b6,
-       0xefd001f7,
-       0xf8e0fc00,
-/* 0x0309: init */
-       0xfe04bd00,
-       0x07fe0004,
-       0x0017f100,
-       0x0227f012,
-       0xf10012d0,
-       0xfe05b917,
-       0x17f10010,
-       0x10d00400,
-       0x0437f1c0,
-       0x0634b604,
-       0x200327f1,
-       0xf10032d0,
-       0xd0200427,
-       0x27f10132,
-       0x32d0200b,
-       0x0c27f102,
-       0x0732d020,
-       0x0c2427f1,
-       0xb90624b6,
-       0x23d00003,
+       0xbdf2efbc,
+       0x0399f094,
+       0x170007f1,
+       0xd00203f0,
+       0x04bd0009,
+/* 0x02fe: error */
+       0x07f100f8,
+       0x03f00500,
+       0x000fd002,
+       0xf7f004bd,
+       0x0007f101,
+       0x0303f007,
+       0xbd000fd0,
+/* 0x031b: init */
+       0xbd00f804,
+       0x0004fe04,
+       0xf10007fe,
+       0xf0120017,
+       0x12d00227,
+       0xb117f100,
+       0x0010fe05,
+       0x040017f1,
+       0xf1c010d0,
+       0xb6040437,
+       0x27f10634,
+       0x32d02003,
        0x0427f100,
-       0x0023f087,
-       0xb70012d0,
-       0xf0010012,
-       0x12d00427,
-       0x1031f400,
-       0x9604e7f1,
-       0xf440e3f0,
-       0xf1c76821,
-       0x01018090,
-       0x801ff4f0,
-       0x17f0000f,
-       0x041fbb01,
-       0xf10112b6,
-       0xb6040c27,
-       0x21d00624,
-       0x4021d000,
-       0x080027f1,
-       0xcf0624b6,
-       0xf7f00022,
-/* 0x03a9: init_find_chipset */
-       0x08f0b654,
-       0xb800f398,
-       0x0bf40432,
-       0x0034b00b,
-       0xf8f11bf4,
-/* 0x03bd: init_context */
-       0x0017f100,
-       0x02fe5801,
-       0xf003ff58,
-       0x0e8000e3,
-       0x150f8014,
-       0x013d21f5,
+       0x0132d020,
+       0x200b27f1,
+       0xf10232d0,
+       0xd0200c27,
+       0x27f10732,
+       0x24b60c24,
+       0x0003b906,
+       0xf10023d0,
+       0xf0870427,
+       0x12d00023,
+       0x0012b700,
+       0x0427f001,
+       0xf40012d0,
+       0xe7f11031,
+       0xe3f09604,
+       0x6821f440,
+       0x8090f1c7,
+       0xf4f00301,
+       0x020f801f,
+       0xbb0117f0,
+       0x12b6041f,
+       0x0c27f101,
+       0x0624b604,
+       0xd00021d0,
+       0x17f14021,
+       0x0e980100,
+       0x010f9800,
+       0x014721f5,
        0x070037f1,
        0x950634b6,
        0x34d00814,
@@ -498,196 +488,201 @@ uint32_t nve0_grhub_code[] = {
        0x0815b600,
        0xb60110b6,
        0x1fb90814,
-       0x6321f502,
+       0x7121f502,
        0x001fbb02,
-       0xf1000398,
+       0xf1020398,
        0xf0200047,
-/* 0x040e: init_gpc */
+/* 0x03f6: init_gpc */
        0x4ea05043,
        0x1fb90804,
        0x8d21f402,
-       0x08004ea0,
-       0xf4022fb9,
-       0x4ea08d21,
-       0xf4bd010c,
-       0xa08d21f4,
-       0xf401044e,
+       0x010c4ea0,
+       0x21f4f4bd,
+       0x044ea08d,
+       0x8d21f401,
+       0x01004ea0,
+       0xf402f7f0,
        0x4ea08d21,
-       0xf7f00100,
-       0x8d21f402,
-       0x08004ea0,
-/* 0x0440: init_gpc_wait */
-       0xc86821f4,
-       0x0bf41fff,
-       0x044ea0fa,
-       0x6821f408,
-       0xb7001fbb,
-       0xb6800040,
-       0x1bf40132,
-       0x0027f1b4,
-       0x0624b608,
-       0xb74021d0,
-       0xbd080020,
+/* 0x041e: init_gpc_wait */
+       0x21f40800,
+       0x1fffc868,
+       0xa0fa0bf4,
+       0xf408044e,
+       0x1fbb6821,
+       0x0040b700,
+       0x0132b680,
+       0xf1be1bf4,
+       0xf0010007,
+       0x01d00203,
+       0xbd04bd00,
        0x1f19f014,
-/* 0x0473: main */
-       0xf40021d0,
-       0x28f40031,
-       0x08d7f000,
-       0xf43921f4,
-       0xe4b1f401,
-       0x1bf54001,
-       0x87f100d1,
-       0x84b6083c,
-       0xf094bd06,
-       0x89d00499,
-       0x0017f100,
-       0x0614b60b,
-       0xcf4012cf,
-       0x13c80011,
-       0x7e0bf41f,
+       0x080007f1,
+       0xd00203f0,
+       0x04bd0001,
+/* 0x0458: main */
+       0xf40031f4,
+       0xd7f00028,
+       0x3921f410,
+       0xb1f401f4,
+       0xf54001e4,
+       0xbd00de1b,
+       0x0499f094,
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0x0b0017f1,
+       0xcf0614b6,
+       0x11cf4012,
+       0x1f13c800,
+       0x00870bf5,
        0xf41f23c8,
-       0x20f95a0b,
-       0xf10212b9,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00799f0,
-       0x32f40089,
-       0x0231f401,
-       0x07fb21f5,
-       0x085c87f1,
-       0xbd0684b6,
+       0x20f9620b,
+       0xbd0212b9,
        0x0799f094,
-       0xfc0089d0,
-       0x3c87f120,
-       0x0684b608,
-       0x99f094bd,
-       0x0089d006,
-       0xf50131f4,
-       0xf107fb21,
-       0xb6085c87,
-       0x94bd0684,
-       0xd00699f0,
-       0x0ef40089,
-/* 0x0509: chsw_prev_no_next */
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0xf40132f4,
+       0x21f50231,
+       0x94bd0801,
+       0xf10799f0,
+       0xf0170007,
+       0x09d00203,
+       0xfc04bd00,
+       0xf094bd20,
+       0x07f10699,
+       0x03f00f00,
+       0x0009d002,
+       0x31f404bd,
+       0x0121f501,
+       0xf094bd08,
+       0x07f10699,
+       0x03f01700,
+       0x0009d002,
+       0x0ef404bd,
+/* 0x04f9: chsw_prev_no_next */
        0xb920f931,
        0x32f40212,
        0x0232f401,
-       0x07fb21f5,
+       0x080121f5,
        0x17f120fc,
        0x14b60b00,
        0x0012d006,
-/* 0x0527: chsw_no_prev */
+/* 0x0517: chsw_no_prev */
        0xc8130ef4,
        0x0bf41f23,
        0x0131f40d,
        0xf50232f4,
-/* 0x0537: chsw_done */
-       0xf107fb21,
+/* 0x0527: chsw_done */
+       0xf1080121,
        0xb60b0c17,
        0x27f00614,
        0x0012d001,
-       0x085c87f1,
-       0xbd0684b6,
-       0x0499f094,
-       0xf50089d0,
-/* 0x0557: main_not_ctx_switch */
-       0xb0ff200e,
-       0x1bf401e4,
-       0x02f2b90d,
-       0x078f21f5,
-/* 0x0567: main_not_ctx_chan */
-       0xb0420ef4,
-       0x1bf402e4,
-       0x3c87f12e,
-       0x0684b608,
        0x99f094bd,
-       0x0089d007,
+       0x0007f104,
+       0x0203f017,
+       0xbd0009d0,
+       0x130ef504,
+/* 0x0549: main_not_ctx_switch */
+       0x01e4b0ff,
+       0xb90d1bf4,
+       0x21f502f2,
+       0x0ef40795,
+/* 0x0559: main_not_ctx_chan */
+       0x02e4b046,
+       0xbd321bf4,
+       0x0799f094,
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
        0xf40132f4,
        0x21f50232,
-       0x87f107fb,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00799,
-       0x110ef400,
-/* 0x0598: main_not_ctx_save */
-       0xf010ef94,
-       0x21f501f5,
-       0x0ef502ec,
-/* 0x05a6: main_done */
-       0x17f1fed1,
-       0x14b60820,
-       0xf024bd06,
-       0x12d01f29,
-       0xbe0ef500,
-/* 0x05b9: ih */
+       0x94bd0801,
+       0xf10799f0,
+       0xf0170007,
+       0x09d00203,
+       0xf404bd00,
+/* 0x058e: main_not_ctx_save */
+       0xef94110e,
+       0x01f5f010,
+       0x02fe21f5,
+       0xfec00ef5,
+/* 0x059c: main_done */
+       0x29f024bd,
+       0x0007f11f,
+       0x0203f008,
+       0xbd0002d0,
+       0xab0ef504,
+/* 0x05b1: ih */
        0xfe80f9fe,
        0x80f90188,
        0xa0f990f9,
        0xd0f9b0f9,
        0xf0f9e0f9,
-       0xc4800acf,
-       0x0bf404ab,
-       0x00b7f11d,
-       0x08d7f019,
-       0xcf40becf,
-       0x21f400bf,
-       0x00b0b704,
-       0x01e7f004,
-/* 0x05ef: ih_no_fifo */
-       0xe400bed0,
-       0xf40100ab,
-       0xd7f00d0b,
-       0x01e7f108,
-       0x0421f440,
-/* 0x0600: ih_no_ctxsw */
-       0x0104b7f1,
-       0xabffb0bd,
-       0x0d0bf4b4,
-       0x0c1ca7f1,
-       0xd006a4b6,
-/* 0x0616: ih_no_other */
-       0x0ad000ab,
-       0xfcf0fc40,
-       0xfcd0fce0,
-       0xfca0fcb0,
-       0xfe80fc90,
-       0x80fc0088,
-       0xf80032f4,
-/* 0x0631: ctx_4170s */
-       0x70e7f101,
-       0x40e3f041,
-       0xf410f5f0,
-       0x00f88d21,
-/* 0x0640: ctx_4170w */
-       0x4170e7f1,
-       0xf440e3f0,
-       0xf4f06821,
-       0xf31bf410,
-/* 0x0652: ctx_redswitch */
+       0x0acf04bd,
+       0x04abc480,
+       0xf11d0bf4,
+       0xf01900b7,
+       0xbecf10d7,
+       0x00bfcf40,
+       0xb70421f4,
+       0xf00400b0,
+       0xbed001e7,
+/* 0x05e9: ih_no_fifo */
+       0x00abe400,
+       0x0d0bf401,
+       0xf110d7f0,
+       0xf44001e7,
+/* 0x05fa: ih_no_ctxsw */
+       0xb7f10421,
+       0xb0bd0104,
+       0xf4b4abff,
+       0xa7f10d0b,
+       0xa4b60c1c,
+       0x00abd006,
+/* 0x0610: ih_no_other */
+       0xfc400ad0,
+       0xfce0fcf0,
+       0xfcb0fcd0,
+       0xfc90fca0,
+       0x0088fe80,
+       0x32f480fc,
+/* 0x062b: ctx_4170s */
+       0xf101f800,
+       0xf04170e7,
+       0xf5f040e3,
+       0x8d21f410,
+/* 0x063a: ctx_4170w */
        0xe7f100f8,
-       0xe4b60614,
-       0x70f7f106,
-       0x00efd002,
-/* 0x0663: ctx_redswitch_delay */
-       0xb608f7f0,
-       0x1bf401f2,
-       0x70f7f1fd,
-       0x00efd007,
-/* 0x0672: ctx_86c */
-       0xe7f100f8,
-       0xe4b6086c,
-       0x00efd006,
-       0x8a14e7f1,
-       0xf440e3f0,
-       0xe7f18d21,
-       0xe3f0a86c,
-       0x8d21f441,
-/* 0x0692: ctx_load */
-       0x87f100f8,
-       0x84b6083c,
-       0xf094bd06,
-       0x89d00599,
-       0x0ca7f000,
+       0xe3f04170,
+       0x6821f440,
+       0xf410f4f0,
+       0x00f8f31b,
+/* 0x064c: ctx_redswitch */
+       0x0614e7f1,
+       0xf106e4b6,
+       0xd00270f7,
+       0xf7f000ef,
+/* 0x065d: ctx_redswitch_delay */
+       0x01f2b608,
+       0xf1fd1bf4,
+       0xd00770f7,
+       0x00f800ef,
+/* 0x066c: ctx_86c */
+       0x086ce7f1,
+       0xd006e4b6,
+       0xe7f100ef,
+       0xe3f08a14,
+       0x8d21f440,
+       0xa86ce7f1,
+       0xf441e3f0,
+       0x00f88d21,
+/* 0x068c: ctx_load */
+       0x99f094bd,
+       0x0007f105,
+       0x0203f00f,
+       0xbd0009d0,
+       0x0ca7f004,
        0xf1c921f4,
        0xb60a2417,
        0x10d00614,
@@ -697,162 +692,227 @@ uint32_t nve0_grhub_code[] = {
        0xb60a0c17,
        0x47f00614,
        0x0012d007,
-/* 0x06cb: ctx_chan_wait_0 */
+/* 0x06c7: ctx_chan_wait_0 */
        0xcf4014d0,
        0x44f04014,
        0xfa1bf41f,
        0xfe0032d0,
        0x2af0000b,
        0x0424b61f,
-       0xf10220b6,
-       0xb6083c87,
-       0x94bd0684,
-       0xd00899f0,
-       0x17f10089,
-       0x14b60a04,
-       0x0012d006,
-       0x0a2017f1,
-       0xf00614b6,
-       0x23f10227,
-       0x12d08000,
-       0x1017f000,
-       0x030027f1,
-       0xfa0223f0,
-       0x03f80512,
-       0x085c87f1,
-       0xbd0684b6,
+       0xbd0220b6,
        0x0899f094,
-       0x980089d0,
-       0x14b6c101,
-       0xc0029818,
+       0x0f0007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0x0a0417f1,
+       0xd00614b6,
+       0x17f10012,
+       0x14b60a20,
+       0x0227f006,
+       0x800023f1,
+       0xf00012d0,
+       0x27f11017,
+       0x23f00200,
+       0x0512fa02,
+       0x94bd03f8,
+       0xf10899f0,
+       0xf0170007,
+       0x09d00203,
+       0x9804bd00,
+       0x14b68101,
+       0x80029818,
        0xfd0825b6,
        0x01800512,
-       0x3c87f116,
-       0x0684b608,
-       0x99f094bd,
-       0x0089d009,
-       0x0a0427f1,
-       0xd00624b6,
-       0x27f00021,
-       0x2017f101,
-       0x0614b60a,
-       0xf10012d0,
-       0xf0020017,
+       0xf094bd16,
+       0x07f10999,
+       0x03f00f00,
+       0x0009d002,
+       0x27f104bd,
+       0x24b60a04,
+       0x0021d006,
+       0xf10127f0,
+       0xb60a2017,
+       0x12d00614,
+       0x0017f100,
+       0x0613f001,
+       0xf80501fa,
+       0xf094bd03,
+       0x07f10999,
+       0x03f01700,
+       0x0009d002,
+       0x94bd04bd,
+       0xf10599f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0795: ctx_chan */
+       0x8c21f500,
+       0x0ca7f006,
+       0xf1c921f4,
+       0xb60a1017,
+       0x27f00614,
+       0x0012d005,
+/* 0x07ac: ctx_chan_wait */
+       0xfd0012cf,
+       0x1bf40522,
+/* 0x07b7: ctx_mmio_exec */
+       0x9800f8fa,
+       0x27f14103,
+       0x24b60a04,
+       0x0023d006,
+/* 0x07c6: ctx_mmio_loop */
+       0x34c434bd,
+       0x0f1bf4ff,
+       0x020057f1,
+       0xfa0653f0,
+       0x03f80535,
+/* 0x07d8: ctx_mmio_pull */
+       0x98804e98,
+       0x21f4814f,
+       0x0830b68d,
+       0xf40112b6,
+/* 0x07ea: ctx_mmio_done */
+       0x0398df1b,
+       0x0023d016,
+       0xf1400080,
+       0xf0010017,
        0x01fa0613,
-       0xf103f805,
-       0xb6085c87,
-       0x94bd0684,
-       0xd00999f0,
-       0x87f10089,
-       0x84b6085c,
-       0xf094bd06,
-       0x89d00599,
-/* 0x078f: ctx_chan */
-       0xf500f800,
-       0xf0069221,
-       0x21f40ca7,
-       0x1017f1c9,
-       0x0614b60a,
-       0xd00527f0,
-/* 0x07a6: ctx_chan_wait */
-       0x12cf0012,
-       0x0522fd00,
-       0xf8fa1bf4,
-/* 0x07b1: ctx_mmio_exec */
-       0x81039800,
-       0x0a0427f1,
+       0xf803f806,
+/* 0x0801: ctx_xfer */
+       0x00f7f100,
+       0x06f4b60c,
+       0xd004e7f0,
+/* 0x080e: ctx_xfer_idle */
+       0xfecf80fe,
+       0x00e4f100,
+       0xf91bf420,
+       0xf40611f4,
+/* 0x081e: ctx_xfer_pre */
+       0xf7f00d02,
+       0x6c21f510,
+       0x1c11f406,
+/* 0x0828: ctx_xfer_pre_load */
+       0xf502f7f0,
+       0xf5062b21,
+       0xf5063a21,
+       0xbd064c21,
+       0x2b21f5f4,
+       0x8c21f506,
+/* 0x0841: ctx_xfer_exec */
+       0x16019806,
+       0x041427f1,
        0xd00624b6,
-       0x34bd0023,
-/* 0x07c0: ctx_mmio_loop */
-       0xf4ff34c4,
-       0x57f10f1b,
-       0x53f00300,
-       0x0535fa06,
-/* 0x07d2: ctx_mmio_pull */
-       0x4e9803f8,
-       0xc14f98c0,
+       0xe7f10020,
+       0xe3f0a500,
+       0x021fb941,
        0xb68d21f4,
-       0x12b60830,
-       0xdf1bf401,
-/* 0x07e4: ctx_mmio_done */
-       0xd0160398,
-       0x00800023,
-       0x0017f180,
-       0x0613f002,
-       0xf80601fa,
-/* 0x07fb: ctx_xfer */
-       0xf100f803,
-       0xb60c00f7,
-       0xe7f006f4,
-       0x80fed004,
-/* 0x0808: ctx_xfer_idle */
-       0xf100fecf,
-       0xf42000e4,
-       0x11f4f91b,
-       0x0d02f406,
-/* 0x0818: ctx_xfer_pre */
-       0xf510f7f0,
-       0xf4067221,
-/* 0x0822: ctx_xfer_pre_load */
-       0xf7f01c11,
-       0x3121f502,
-       0x4021f506,
-       0x5221f506,
-       0xf5f4bd06,
-       0xf5063121,
-/* 0x083b: ctx_xfer_exec */
-       0x98069221,
-       0x27f11601,
-       0x24b60414,
-       0x0020d006,
-       0xa500e7f1,
-       0xb941e3f0,
-       0x21f4021f,
-       0x04e0b68d,
-       0xf001fcf0,
-       0x24b6022c,
-       0x05f2fd01,
-       0xf18d21f4,
-       0xf04afc17,
-       0x27f00213,
-       0x0012d00c,
-       0x020721f5,
-       0x47fc27f1,
-       0xd00223f0,
-       0x2cf00020,
-       0x0320b601,
-       0xf00012d0,
-       0xa5f001ac,
-       0x00b7f006,
-       0x98140c98,
-       0xe7f0150d,
-       0x5c21f500,
-       0x08a7f001,
-       0x010321f5,
-       0x020721f5,
-       0xf02201f4,
-       0x21f40ca7,
-       0x1017f1c9,
-       0x0614b60a,
-       0xd00527f0,
-/* 0x08c2: ctx_xfer_post_save_wait */
-       0x12cf0012,
-       0x0522fd00,
-       0xf4fa1bf4,
-/* 0x08ce: ctx_xfer_post */
-       0xf7f02e02,
-       0x3121f502,
+       0xfcf004e0,
+       0x022cf001,
+       0xfd0124b6,
+       0x21f405f2,
+       0xfc17f18d,
+       0x0213f04a,
+       0xd00c27f0,
+       0x21f50012,
+       0x27f10215,
+       0x23f047fc,
+       0x0020d002,
+       0xb6012cf0,
+       0x12d00320,
+       0x01acf000,
+       0xf006a5f0,
+       0x0c9800b7,
+       0x010d9800,
+       0xf500e7f0,
+       0xf0016621,
+       0x21f508a7,
+       0x21f50109,
+       0x01f40215,
+       0x0ca7f022,
+       0xf1c921f4,
+       0xb60a1017,
+       0x27f00614,
+       0x0012d005,
+/* 0x08c8: ctx_xfer_post_save_wait */
+       0xfd0012cf,
+       0x1bf40522,
+       0x2e02f4fa,
+/* 0x08d4: ctx_xfer_post */
+       0xf502f7f0,
+       0xbd062b21,
+       0x6c21f5f4,
+       0x3421f506,
+       0x3a21f502,
        0xf5f4bd06,
-       0xf5067221,
-       0xf5022621,
-       0xbd064021,
-       0x3121f5f4,
-       0x1011f406,
-       0xfd800198,
-       0x0bf40511,
-       0xb121f507,
-/* 0x08f9: ctx_xfer_no_post_mmio */
-/* 0x08f9: ctx_xfer_done */
-       0x0000f807,
+       0xf4062b21,
+       0x01981011,
+       0x0511fd40,
+       0xf5070bf4,
+/* 0x08ff: ctx_xfer_no_post_mmio */
+/* 0x08ff: ctx_xfer_done */
+       0xf807b721,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       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/engine/graph/fuc/hubnvf0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc
new file mode 100644 (file)
index 0000000..ec42ed2
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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>
+ */
+
+#define CHIPSET GK110
+#include "macros.fuc"
+
+.section #nvf0_grhub_data
+#define INCLUDE_DATA
+#include "com.fuc"
+#include "hub.fuc"
+#undef INCLUDE_DATA
+
+.section #nvf0_grhub_code
+#define INCLUDE_CODE
+bra #init
+#include "com.fuc"
+#include "hub.fuc"
+.align 256
+#undef INCLUDE_CODE
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h
new file mode 100644 (file)
index 0000000..438506d
--- /dev/null
@@ -0,0 +1,918 @@
+uint32_t nvf0_grhub_data[] = {
+/* 0x0000: hub_mmio_list_head */
+       0x00000300,
+/* 0x0004: hub_mmio_list_tail */
+       0x00000304,
+/* 0x0008: gpc_count */
+       0x00000000,
+/* 0x000c: rop_count */
+       0x00000000,
+/* 0x0010: cmd_queue */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0058: ctx_current */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0100: chan_data */
+/* 0x0100: chan_mmio_count */
+       0x00000000,
+/* 0x0104: chan_mmio_address */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0200: xfer_data */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+/* 0x0300: hub_mmio_list_base */
+       0x0417e91c,
+};
+
+uint32_t nvf0_grhub_code[] = {
+       0x031b0ef5,
+/* 0x0004: queue_put */
+       0x9800d898,
+       0x86f001d9,
+       0x0489b808,
+       0xf00c1bf4,
+       0x21f502f7,
+       0x00f802fe,
+/* 0x001c: queue_put_next */
+       0xb60798c4,
+       0x8dbb0384,
+       0x0880b600,
+       0x80008e80,
+       0x90b6018f,
+       0x0f94f001,
+       0xf801d980,
+/* 0x0039: queue_get */
+       0x0131f400,
+       0x9800d898,
+       0x89b801d9,
+       0x210bf404,
+       0xb60789c4,
+       0x9dbb0394,
+       0x0890b600,
+       0x98009e98,
+       0x80b6019f,
+       0x0f84f001,
+       0xf400d880,
+/* 0x0066: queue_get_done */
+       0x00f80132,
+/* 0x0068: nv_rd32 */
+       0x0728b7f1,
+       0xb906b4b6,
+       0xc9f002ec,
+       0x00bcd01f,
+/* 0x0078: nv_rd32_wait */
+       0xc800bccf,
+       0x1bf41fcc,
+       0x06a7f0fa,
+       0x010921f5,
+       0xf840bfcf,
+/* 0x008d: nv_wr32 */
+       0x28b7f100,
+       0x06b4b607,
+       0xb980bfd0,
+       0xc9f002ec,
+       0x1ec9f01f,
+/* 0x00a3: nv_wr32_wait */
+       0xcf00bcd0,
+       0xccc800bc,
+       0xfa1bf41f,
+/* 0x00ae: watchdog_reset */
+       0x87f100f8,
+       0x84b60430,
+       0x1ff9f006,
+       0xf8008fd0,
+/* 0x00bd: watchdog_clear */
+       0x3087f100,
+       0x0684b604,
+       0xf80080d0,
+/* 0x00c9: wait_donez */
+       0xf094bd00,
+       0x07f10099,
+       0x03f03700,
+       0x0009d002,
+       0x07f104bd,
+       0x03f00600,
+       0x000ad002,
+/* 0x00e6: wait_donez_ne */
+       0x87f104bd,
+       0x83f00000,
+       0x0088cf01,
+       0xf4888aff,
+       0x94bdf31b,
+       0xf10099f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0109: wait_doneo */
+       0xf094bd00,
+       0x07f10099,
+       0x03f03700,
+       0x0009d002,
+       0x87f104bd,
+       0x84b60818,
+       0x008ad006,
+/* 0x0124: wait_doneo_e */
+       0x040087f1,
+       0xcf0684b6,
+       0x8aff0088,
+       0xf30bf488,
+       0x99f094bd,
+       0x0007f100,
+       0x0203f017,
+       0xbd0009d0,
+/* 0x0147: mmctx_size */
+       0xbd00f804,
+/* 0x0149: nv_mmctx_size_loop */
+       0x00e89894,
+       0xb61a85b6,
+       0x84b60180,
+       0x0098bb02,
+       0xb804e0b6,
+       0x1bf404ef,
+       0x029fb9eb,
+/* 0x0166: mmctx_xfer */
+       0x94bd00f8,
+       0xf10199f0,
+       0xf0370007,
+       0x09d00203,
+       0xf104bd00,
+       0xb6071087,
+       0x94bd0684,
+       0xf405bbfd,
+       0x8bd0090b,
+       0x0099f000,
+/* 0x018c: mmctx_base_disabled */
+       0xf405eefd,
+       0x8ed00c0b,
+       0xc08fd080,
+/* 0x019b: mmctx_multi_disabled */
+       0xb70199f0,
+       0xc8010080,
+       0xb4b600ab,
+       0x0cb9f010,
+       0xb601aec8,
+       0xbefd11e4,
+       0x008bd005,
+/* 0x01b4: mmctx_exec_loop */
+/* 0x01b4: mmctx_wait_free */
+       0xf0008ecf,
+       0x0bf41fe4,
+       0x00ce98fa,
+       0xd005e9fd,
+       0xc0b6c08e,
+       0x04cdb804,
+       0xc8e81bf4,
+       0x1bf402ab,
+/* 0x01d5: mmctx_fini_wait */
+       0x008bcf18,
+       0xb01fb4f0,
+       0x1bf410b4,
+       0x02a7f0f7,
+       0xf4c921f4,
+/* 0x01ea: mmctx_stop */
+       0xabc81b0e,
+       0x10b4b600,
+       0xf00cb9f0,
+       0x8bd012b9,
+/* 0x01f9: mmctx_stop_wait */
+       0x008bcf00,
+       0xf412bbc8,
+/* 0x0202: mmctx_done */
+       0x94bdfa1b,
+       0xf10199f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0215: strand_wait */
+       0xf0a0f900,
+       0x21f402a7,
+       0xf8a0fcc9,
+/* 0x0221: strand_pre */
+       0xfc87f100,
+       0x0283f04a,
+       0xd00c97f0,
+       0x21f50089,
+       0x00f80215,
+/* 0x0234: strand_post */
+       0x4afc87f1,
+       0xf00283f0,
+       0x89d00d97,
+       0x1521f500,
+/* 0x0247: strand_set */
+       0xf100f802,
+       0xf04ffca7,
+       0xaba202a3,
+       0xc7f00500,
+       0x00acd00f,
+       0xd00bc7f0,
+       0x21f500bc,
+       0xaed00215,
+       0x0ac7f000,
+       0xf500bcd0,
+       0xf8021521,
+/* 0x0271: strand_ctx_init */
+       0xf094bd00,
+       0x07f10399,
+       0x03f03700,
+       0x0009d002,
+       0x21f504bd,
+       0xe7f00221,
+       0x4721f503,
+       0xfca7f102,
+       0x02a3f046,
+       0x0400aba0,
+       0xf040a0d0,
+       0xbcd001c7,
+       0x1521f500,
+       0x010c9202,
+       0xf000acd0,
+       0xbcd002c7,
+       0x1521f500,
+       0x3421f502,
+       0x8087f102,
+       0x0684b608,
+       0xb70089cf,
+       0x95220080,
+/* 0x02ca: ctx_init_strand_loop */
+       0x8ed008fe,
+       0x408ed000,
+       0xb6808acf,
+       0xa0b606a5,
+       0x00eabb01,
+       0xb60480b6,
+       0x1bf40192,
+       0x08e4b6e8,
+       0xbdf2efbc,
+       0x0399f094,
+       0x170007f1,
+       0xd00203f0,
+       0x04bd0009,
+/* 0x02fe: error */
+       0x07f100f8,
+       0x03f00500,
+       0x000fd002,
+       0xf7f004bd,
+       0x0007f101,
+       0x0303f007,
+       0xbd000fd0,
+/* 0x031b: init */
+       0xbd00f804,
+       0x0004fe04,
+       0xf10007fe,
+       0xf0120017,
+       0x12d00227,
+       0xb117f100,
+       0x0010fe05,
+       0x040017f1,
+       0xf1c010d0,
+       0xb6040437,
+       0x27f10634,
+       0x32d02003,
+       0x0427f100,
+       0x0132d020,
+       0x200b27f1,
+       0xf10232d0,
+       0xd0200c27,
+       0x27f10732,
+       0x24b60c24,
+       0x0003b906,
+       0xf10023d0,
+       0xf0870427,
+       0x12d00023,
+       0x0012b700,
+       0x0427f001,
+       0xf40012d0,
+       0xe7f11031,
+       0xe3f09604,
+       0x6821f440,
+       0x8090f1c7,
+       0xf4f00301,
+       0x020f801f,
+       0xbb0117f0,
+       0x12b6041f,
+       0x0c27f101,
+       0x0624b604,
+       0xd00021d0,
+       0x17f14021,
+       0x0e980100,
+       0x010f9800,
+       0x014721f5,
+       0x070037f1,
+       0x950634b6,
+       0x34d00814,
+       0x4034d000,
+       0x130030b7,
+       0xb6001fbb,
+       0x3fd002f5,
+       0x0815b600,
+       0xb60110b6,
+       0x1fb90814,
+       0x7121f502,
+       0x001fbb02,
+       0xf1020398,
+       0xf0200047,
+/* 0x03f6: init_gpc */
+       0x4ea05043,
+       0x1fb90804,
+       0x8d21f402,
+       0x010c4ea0,
+       0x21f4f4bd,
+       0x044ea08d,
+       0x8d21f401,
+       0x01004ea0,
+       0xf402f7f0,
+       0x4ea08d21,
+/* 0x041e: init_gpc_wait */
+       0x21f40800,
+       0x1fffc868,
+       0xa0fa0bf4,
+       0xf408044e,
+       0x1fbb6821,
+       0x0040b700,
+       0x0132b680,
+       0xf1be1bf4,
+       0xf0010007,
+       0x01d00203,
+       0xbd04bd00,
+       0x1f19f014,
+       0x300007f1,
+       0xd00203f0,
+       0x04bd0001,
+/* 0x0458: main */
+       0xf40031f4,
+       0xd7f00028,
+       0x3921f410,
+       0xb1f401f4,
+       0xf54001e4,
+       0xbd00de1b,
+       0x0499f094,
+       0x370007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0x0b0017f1,
+       0xcf0614b6,
+       0x11cf4012,
+       0x1f13c800,
+       0x00870bf5,
+       0xf41f23c8,
+       0x20f9620b,
+       0xbd0212b9,
+       0x0799f094,
+       0x370007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0xf40132f4,
+       0x21f50231,
+       0x94bd0801,
+       0xf10799f0,
+       0xf0170007,
+       0x09d00203,
+       0xfc04bd00,
+       0xf094bd20,
+       0x07f10699,
+       0x03f03700,
+       0x0009d002,
+       0x31f404bd,
+       0x0121f501,
+       0xf094bd08,
+       0x07f10699,
+       0x03f01700,
+       0x0009d002,
+       0x0ef404bd,
+/* 0x04f9: chsw_prev_no_next */
+       0xb920f931,
+       0x32f40212,
+       0x0232f401,
+       0x080121f5,
+       0x17f120fc,
+       0x14b60b00,
+       0x0012d006,
+/* 0x0517: chsw_no_prev */
+       0xc8130ef4,
+       0x0bf41f23,
+       0x0131f40d,
+       0xf50232f4,
+/* 0x0527: chsw_done */
+       0xf1080121,
+       0xb60b0c17,
+       0x27f00614,
+       0x0012d001,
+       0x99f094bd,
+       0x0007f104,
+       0x0203f017,
+       0xbd0009d0,
+       0x130ef504,
+/* 0x0549: main_not_ctx_switch */
+       0x01e4b0ff,
+       0xb90d1bf4,
+       0x21f502f2,
+       0x0ef40795,
+/* 0x0559: main_not_ctx_chan */
+       0x02e4b046,
+       0xbd321bf4,
+       0x0799f094,
+       0x370007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0xf40132f4,
+       0x21f50232,
+       0x94bd0801,
+       0xf10799f0,
+       0xf0170007,
+       0x09d00203,
+       0xf404bd00,
+/* 0x058e: main_not_ctx_save */
+       0xef94110e,
+       0x01f5f010,
+       0x02fe21f5,
+       0xfec00ef5,
+/* 0x059c: main_done */
+       0x29f024bd,
+       0x0007f11f,
+       0x0203f030,
+       0xbd0002d0,
+       0xab0ef504,
+/* 0x05b1: ih */
+       0xfe80f9fe,
+       0x80f90188,
+       0xa0f990f9,
+       0xd0f9b0f9,
+       0xf0f9e0f9,
+       0x0acf04bd,
+       0x04abc480,
+       0xf11d0bf4,
+       0xf01900b7,
+       0xbecf10d7,
+       0x00bfcf40,
+       0xb70421f4,
+       0xf00400b0,
+       0xbed001e7,
+/* 0x05e9: ih_no_fifo */
+       0x00abe400,
+       0x0d0bf401,
+       0xf110d7f0,
+       0xf44001e7,
+/* 0x05fa: ih_no_ctxsw */
+       0xb7f10421,
+       0xb0bd0104,
+       0xf4b4abff,
+       0xa7f10d0b,
+       0xa4b60c1c,
+       0x00abd006,
+/* 0x0610: ih_no_other */
+       0xfc400ad0,
+       0xfce0fcf0,
+       0xfcb0fcd0,
+       0xfc90fca0,
+       0x0088fe80,
+       0x32f480fc,
+/* 0x062b: ctx_4170s */
+       0xf101f800,
+       0xf04170e7,
+       0xf5f040e3,
+       0x8d21f410,
+/* 0x063a: ctx_4170w */
+       0xe7f100f8,
+       0xe3f04170,
+       0x6821f440,
+       0xf410f4f0,
+       0x00f8f31b,
+/* 0x064c: ctx_redswitch */
+       0x0614e7f1,
+       0xf106e4b6,
+       0xd00270f7,
+       0xf7f000ef,
+/* 0x065d: ctx_redswitch_delay */
+       0x01f2b608,
+       0xf1fd1bf4,
+       0xd00770f7,
+       0x00f800ef,
+/* 0x066c: ctx_86c */
+       0x086ce7f1,
+       0xd006e4b6,
+       0xe7f100ef,
+       0xe3f08a14,
+       0x8d21f440,
+       0xa86ce7f1,
+       0xf441e3f0,
+       0x00f88d21,
+/* 0x068c: ctx_load */
+       0x99f094bd,
+       0x0007f105,
+       0x0203f037,
+       0xbd0009d0,
+       0x0ca7f004,
+       0xf1c921f4,
+       0xb60a2417,
+       0x10d00614,
+       0x0037f100,
+       0x0634b60b,
+       0xf14032d0,
+       0xb60a0c17,
+       0x47f00614,
+       0x0012d007,
+/* 0x06c7: ctx_chan_wait_0 */
+       0xcf4014d0,
+       0x44f04014,
+       0xfa1bf41f,
+       0xfe0032d0,
+       0x2af0000b,
+       0x0424b61f,
+       0xbd0220b6,
+       0x0899f094,
+       0x370007f1,
+       0xd00203f0,
+       0x04bd0009,
+       0x0a0417f1,
+       0xd00614b6,
+       0x17f10012,
+       0x14b60a20,
+       0x0227f006,
+       0x800023f1,
+       0xf00012d0,
+       0x27f11017,
+       0x23f00200,
+       0x0512fa02,
+       0x94bd03f8,
+       0xf10899f0,
+       0xf0170007,
+       0x09d00203,
+       0x9804bd00,
+       0x14b68101,
+       0x80029818,
+       0xfd0825b6,
+       0x01800512,
+       0xf094bd16,
+       0x07f10999,
+       0x03f03700,
+       0x0009d002,
+       0x27f104bd,
+       0x24b60a04,
+       0x0021d006,
+       0xf10127f0,
+       0xb60a2017,
+       0x12d00614,
+       0x0017f100,
+       0x0613f001,
+       0xf80501fa,
+       0xf094bd03,
+       0x07f10999,
+       0x03f01700,
+       0x0009d002,
+       0x94bd04bd,
+       0xf10599f0,
+       0xf0170007,
+       0x09d00203,
+       0xf804bd00,
+/* 0x0795: ctx_chan */
+       0x8c21f500,
+       0x0ca7f006,
+       0xf1c921f4,
+       0xb60a1017,
+       0x27f00614,
+       0x0012d005,
+/* 0x07ac: ctx_chan_wait */
+       0xfd0012cf,
+       0x1bf40522,
+/* 0x07b7: ctx_mmio_exec */
+       0x9800f8fa,
+       0x27f14103,
+       0x24b60a04,
+       0x0023d006,
+/* 0x07c6: ctx_mmio_loop */
+       0x34c434bd,
+       0x0f1bf4ff,
+       0x020057f1,
+       0xfa0653f0,
+       0x03f80535,
+/* 0x07d8: ctx_mmio_pull */
+       0x98804e98,
+       0x21f4814f,
+       0x0830b68d,
+       0xf40112b6,
+/* 0x07ea: ctx_mmio_done */
+       0x0398df1b,
+       0x0023d016,
+       0xf1400080,
+       0xf0010017,
+       0x01fa0613,
+       0xf803f806,
+/* 0x0801: ctx_xfer */
+       0x00f7f100,
+       0x06f4b60c,
+       0xd004e7f0,
+/* 0x080e: ctx_xfer_idle */
+       0xfecf80fe,
+       0x00e4f100,
+       0xf91bf420,
+       0xf40611f4,
+/* 0x081e: ctx_xfer_pre */
+       0xf7f00d02,
+       0x6c21f510,
+       0x1c11f406,
+/* 0x0828: ctx_xfer_pre_load */
+       0xf502f7f0,
+       0xf5062b21,
+       0xf5063a21,
+       0xbd064c21,
+       0x2b21f5f4,
+       0x8c21f506,
+/* 0x0841: ctx_xfer_exec */
+       0x16019806,
+       0x041427f1,
+       0xd00624b6,
+       0xe7f10020,
+       0xe3f0a500,
+       0x021fb941,
+       0xb68d21f4,
+       0xfcf004e0,
+       0x022cf001,
+       0xfd0124b6,
+       0x21f405f2,
+       0xfc17f18d,
+       0x0213f04a,
+       0xd00c27f0,
+       0x21f50012,
+       0x27f10215,
+       0x23f047fc,
+       0x0020d002,
+       0xb6012cf0,
+       0x12d00320,
+       0x01acf000,
+       0xf006a5f0,
+       0x0c9800b7,
+       0x010d9800,
+       0xf500e7f0,
+       0xf0016621,
+       0x21f508a7,
+       0x21f50109,
+       0x01f40215,
+       0x0ca7f022,
+       0xf1c921f4,
+       0xb60a1017,
+       0x27f00614,
+       0x0012d005,
+/* 0x08c8: ctx_xfer_post_save_wait */
+       0xfd0012cf,
+       0x1bf40522,
+       0x2e02f4fa,
+/* 0x08d4: ctx_xfer_post */
+       0xf502f7f0,
+       0xbd062b21,
+       0x6c21f5f4,
+       0x3421f506,
+       0x3a21f502,
+       0xf5f4bd06,
+       0xf4062b21,
+       0x01981011,
+       0x0511fd40,
+       0xf5070bf4,
+/* 0x08ff: ctx_xfer_no_post_mmio */
+/* 0x08ff: ctx_xfer_done */
+       0xf807b721,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       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/engine/graph/fuc/macros.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc
new file mode 100644 (file)
index 0000000..33a5a82
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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 "os.h"
+
+#define GF100 0xc0
+#define GF117 0xd7
+#define GK100 0xe0
+#define GK110 0xf0
+
+#define NV_PGRAPH_FECS_SIGNAL                                          0x409400
+#if CHIPSET < GK110
+#define NV_PGRAPH_FECS_CC_SCRATCH_VAL(n)                    ((n) * 4 + 0x409800)
+#define NV_PGRAPH_FECS_CC_SCRATCH_SET(n)                    ((n) * 4 + 0x409820)
+#define NV_PGRAPH_FECS_CC_SCRATCH_CLR(n)                    ((n) * 4 + 0x409840)
+#else
+#define NV_PGRAPH_FECS_CC_SCRATCH_VAL(n)                    ((n) * 4 + 0x409800)
+#define NV_PGRAPH_FECS_CC_SCRATCH_CLR(n)                    ((n) * 4 + 0x409840)
+#define NV_PGRAPH_FECS_CC_SCRATCH_SET(n)                    ((n) * 4 + 0x4098c0)
+#endif
+#define NV_PGRAPH_FECS_INTR_UP_SET                                     0x409c1c
+
+#if CHIPSET < GK110
+#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(n)              ((n) * 4 + 0x41a800)
+#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_SET(n)              ((n) * 4 + 0x41a820)
+#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_CLR(n)              ((n) * 4 + 0x41a840)
+#else
+#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(n)              ((n) * 4 + 0x41a800)
+#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_CLR(n)              ((n) * 4 + 0x41a840)
+#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_SET(n)              ((n) * 4 + 0x41a8c0)
+#endif
+
+#define mmctx_data(r,c) .b32 (((c - 1) << 26) | r)
+#define queue_init      .skip 72 // (2 * 4) + ((8 * 4) * 2)
+
+#define T_WAIT    0
+#define T_MMCTX   1
+#define T_STRWAIT 2
+#define T_STRINIT 3
+#define T_AUTO    4
+#define T_CHAN    5
+#define T_LOAD    6
+#define T_SAVE    7
+#define T_LCHAN   8
+#define T_LCTXH   9
+
+#define nv_mkmm(rv,r) /*
+*/     movw rv  ((r) & 0x0000fffc) /*
+*/     sethi rv ((r) & 0x00ff0000)
+#define nv_mkio(rv,r,i) /*
+*/     nv_mkmm(rv, (((r) & 0xffc) << 6) | ((i) << 2))
+
+#define nv_iord(rv,r,i) /*
+*/     nv_mkio(rv,r,i) /*
+*/     iord rv I[rv]
+#define nv_iowr(r,i,rv) /*
+*/     nv_mkio($r0,r,i) /*
+*/     iowr I[$r0] rv /*
+*/     clear b32 $r0
+
+#define trace_set(bit) /*
+*/     clear b32 $r9 /*
+*/     bset $r9 bit /*
+*/     nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_SET(7), 0, $r9)
+#define trace_clr(bit) /*
+*/     clear b32 $r9 /*
+*/     bset $r9 bit /*
+*/     nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_CLR(7), 0, $r9)
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/nvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/nvc0.fuc
deleted file mode 100644 (file)
index e6b2288..0000000
+++ /dev/null
@@ -1,400 +0,0 @@
-/* fuc microcode util functions for nvc0 PGRAPH
- *
- * 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
- */
-
-define(`mmctx_data', `.b32 eval((($2 - 1) << 26) | $1)')
-define(`queue_init', `.skip eval((2 * 4) + ((8 * 4) * 2))')
-
-ifdef(`include_code', `
-// Error codes
-define(`E_BAD_COMMAND', 0x01)
-define(`E_CMD_OVERFLOW', 0x02)
-
-// Util macros to help with debugging ucode hangs etc
-define(`T_WAIT', 0)
-define(`T_MMCTX', 1)
-define(`T_STRWAIT', 2)
-define(`T_STRINIT', 3)
-define(`T_AUTO', 4)
-define(`T_CHAN', 5)
-define(`T_LOAD', 6)
-define(`T_SAVE', 7)
-define(`T_LCHAN', 8)
-define(`T_LCTXH', 9)
-
-define(`trace_set', `
-       mov $r8 0x83c
-       shl b32 $r8 6
-       clear b32 $r9
-       bset $r9 $1
-       iowr I[$r8 + 0x000] $r9         // CC_SCRATCH[7]
-')
-
-define(`trace_clr', `
-       mov $r8 0x85c
-       shl b32 $r8 6
-       clear b32 $r9
-       bset $r9 $1
-       iowr I[$r8 + 0x000] $r9         // CC_SCRATCH[7]
-')
-
-// queue_put - add request to queue
-//
-// In : $r13 queue pointer
-//     $r14 command
-//     $r15 data
-//
-queue_put:
-       // make sure we have space..
-       ld b32 $r8 D[$r13 + 0x0]        // GET
-       ld b32 $r9 D[$r13 + 0x4]        // PUT
-       xor $r8 8
-       cmpu b32 $r8 $r9
-       bra ne #queue_put_next
-               mov $r15 E_CMD_OVERFLOW
-               call #error
-               ret
-
-       // store cmd/data on queue
-       queue_put_next:
-       and $r8 $r9 7
-       shl b32 $r8 3
-       add b32 $r8 $r13
-       add b32 $r8 8
-       st b32 D[$r8 + 0x0] $r14
-       st b32 D[$r8 + 0x4] $r15
-
-       // update PUT
-       add b32 $r9 1
-       and $r9 0xf
-       st b32 D[$r13 + 0x4] $r9
-       ret
-
-// queue_get - fetch request from queue
-//
-// In : $r13 queue pointer
-//
-// Out:        $p1  clear on success (data available)
-//     $r14 command
-//     $r15 data
-//
-queue_get:
-       bset $flags $p1
-       ld b32 $r8 D[$r13 + 0x0]        // GET
-       ld b32 $r9 D[$r13 + 0x4]        // PUT
-       cmpu b32 $r8 $r9
-       bra e #queue_get_done
-               // fetch first cmd/data pair
-               and $r9 $r8 7
-               shl b32 $r9 3
-               add b32 $r9 $r13
-               add b32 $r9 8
-               ld b32 $r14 D[$r9 + 0x0]
-               ld b32 $r15 D[$r9 + 0x4]
-
-               // update GET
-               add b32 $r8 1
-               and $r8 0xf
-               st b32 D[$r13 + 0x0] $r8
-               bclr $flags $p1
-queue_get_done:
-       ret
-
-// nv_rd32 - read 32-bit value from nv register
-//
-// In : $r14 register
-// Out: $r15 value
-//
-nv_rd32:
-       mov $r11 0x728
-       shl b32 $r11 6
-       mov b32 $r12 $r14
-       bset $r12 31                    // MMIO_CTRL_PENDING
-       iowr I[$r11 + 0x000] $r12       // MMIO_CTRL
-       nv_rd32_wait:
-               iord $r12 I[$r11 + 0x000]
-               xbit $r12 $r12 31
-               bra ne #nv_rd32_wait
-       mov $r10 6                      // DONE_MMIO_RD
-       call #wait_doneo
-       iord $r15 I[$r11 + 0x100]       // MMIO_RDVAL
-       ret
-
-// nv_wr32 - write 32-bit value to nv register
-//
-// In : $r14 register
-//      $r15 value
-//
-nv_wr32:
-       mov $r11 0x728
-       shl b32 $r11 6
-       iowr I[$r11 + 0x200] $r15       // MMIO_WRVAL
-       mov b32 $r12 $r14
-       bset $r12 31                    // MMIO_CTRL_PENDING
-       bset $r12 30                    // MMIO_CTRL_WRITE
-       iowr I[$r11 + 0x000] $r12       // MMIO_CTRL
-       nv_wr32_wait:
-               iord $r12 I[$r11 + 0x000]
-               xbit $r12 $r12 31
-               bra ne #nv_wr32_wait
-       ret
-
-// (re)set watchdog timer
-//
-// In : $r15 timeout
-//
-watchdog_reset:
-       mov $r8 0x430
-       shl b32 $r8 6
-       bset $r15 31
-       iowr I[$r8 + 0x000] $r15
-       ret
-
-// clear watchdog timer
-watchdog_clear:
-       mov $r8 0x430
-       shl b32 $r8 6
-       iowr I[$r8 + 0x000] $r0
-       ret
-
-// wait_done{z,o} - wait on FUC_DONE bit to become clear/set
-//
-// In : $r10 bit to wait on
-//
-define(`wait_done', `
-$1:
-       trace_set(T_WAIT);
-       mov $r8 0x818
-       shl b32 $r8 6
-       iowr I[$r8 + 0x000] $r10        // CC_SCRATCH[6] = wait bit
-       wait_done_$1:
-               mov $r8 0x400
-               shl b32 $r8 6
-               iord $r8 I[$r8 + 0x000] // DONE
-               xbit $r8 $r8 $r10
-               bra $2 #wait_done_$1
-       trace_clr(T_WAIT)
-       ret
-')
-wait_done(wait_donez, ne)
-wait_done(wait_doneo, e)
-
-// mmctx_size - determine size of a mmio list transfer
-//
-// In : $r14 mmio list head
-//      $r15 mmio list tail
-// Out: $r15 transfer size (in bytes)
-//
-mmctx_size:
-       clear b32 $r9
-       nv_mmctx_size_loop:
-               ld b32 $r8 D[$r14]
-               shr b32 $r8 26
-               add b32 $r8 1
-               shl b32 $r8 2
-               add b32 $r9 $r8
-               add b32 $r14 4
-               cmpu b32 $r14 $r15
-               bra ne #nv_mmctx_size_loop
-       mov b32 $r15 $r9
-       ret
-
-// mmctx_xfer - execute a list of mmio transfers
-//
-// In : $r10 flags
-//             bit 0: direction (0 = save, 1 = load)
-//             bit 1: set if first transfer
-//             bit 2: set if last transfer
-//     $r11 base
-//     $r12 mmio list head
-//     $r13 mmio list tail
-//     $r14 multi_stride
-//     $r15 multi_mask
-//
-mmctx_xfer:
-       trace_set(T_MMCTX)
-       mov $r8 0x710
-       shl b32 $r8 6
-       clear b32 $r9
-       or $r11 $r11
-       bra e #mmctx_base_disabled
-               iowr I[$r8 + 0x000] $r11        // MMCTX_BASE
-               bset $r9 0                      // BASE_EN
-       mmctx_base_disabled:
-       or $r14 $r14
-       bra e #mmctx_multi_disabled
-               iowr I[$r8 + 0x200] $r14        // MMCTX_MULTI_STRIDE
-               iowr I[$r8 + 0x300] $r15        // MMCTX_MULTI_MASK
-               bset $r9 1                      // MULTI_EN
-       mmctx_multi_disabled:
-       add b32 $r8 0x100
-
-       xbit $r11 $r10 0
-       shl b32 $r11 16                 // DIR
-       bset $r11 12                    // QLIMIT = 0x10
-       xbit $r14 $r10 1
-       shl b32 $r14 17
-       or $r11 $r14                    // START_TRIGGER
-       iowr I[$r8 + 0x000] $r11        // MMCTX_CTRL
-
-       // loop over the mmio list, and send requests to the hw
-       mmctx_exec_loop:
-               // wait for space in mmctx queue
-               mmctx_wait_free:
-                       iord $r14 I[$r8 + 0x000] // MMCTX_CTRL
-                       and $r14 0x1f
-                       bra e #mmctx_wait_free
-
-               // queue up an entry
-               ld b32 $r14 D[$r12]
-               or $r14 $r9
-               iowr I[$r8 + 0x300] $r14
-               add b32 $r12 4
-               cmpu b32 $r12 $r13
-               bra ne #mmctx_exec_loop
-
-       xbit $r11 $r10 2
-       bra ne #mmctx_stop
-               // wait for queue to empty
-               mmctx_fini_wait:
-                       iord $r11 I[$r8 + 0x000]        // MMCTX_CTRL
-                       and $r11 0x1f
-                       cmpu b32 $r11 0x10
-                       bra ne #mmctx_fini_wait
-               mov $r10 2                              // DONE_MMCTX
-               call #wait_donez
-               bra #mmctx_done
-       mmctx_stop:
-               xbit $r11 $r10 0
-               shl b32 $r11 16                 // DIR
-               bset $r11 12                    // QLIMIT = 0x10
-               bset $r11 18                    // STOP_TRIGGER
-               iowr I[$r8 + 0x000] $r11        // MMCTX_CTRL
-               mmctx_stop_wait:
-                       // wait for STOP_TRIGGER to clear
-                       iord $r11 I[$r8 + 0x000] // MMCTX_CTRL
-                       xbit $r11 $r11 18
-                       bra ne #mmctx_stop_wait
-       mmctx_done:
-       trace_clr(T_MMCTX)
-       ret
-
-// Wait for DONE_STRAND
-//
-strand_wait:
-       push $r10
-       mov $r10 2
-       call #wait_donez
-       pop $r10
-       ret
-
-// unknown - call before issuing strand commands
-//
-strand_pre:
-       mov $r8 0x4afc
-       sethi $r8 0x20000
-       mov $r9 0xc
-       iowr I[$r8] $r9
-       call #strand_wait
-       ret
-
-// unknown - call after issuing strand commands
-//
-strand_post:
-       mov $r8 0x4afc
-       sethi $r8 0x20000
-       mov $r9 0xd
-       iowr I[$r8] $r9
-       call #strand_wait
-       ret
-
-// Selects strand set?!
-//
-// In: $r14 id
-//
-strand_set:
-       mov $r10 0x4ffc
-       sethi $r10 0x20000
-       sub b32 $r11 $r10 0x500
-       mov $r12 0xf
-       iowr I[$r10 + 0x000] $r12               // 0x93c = 0xf
-       mov $r12 0xb
-       iowr I[$r11 + 0x000] $r12               // 0x928 = 0xb
-       call #strand_wait
-       iowr I[$r10 + 0x000] $r14               // 0x93c = <id>
-       mov $r12 0xa
-       iowr I[$r11 + 0x000] $r12               // 0x928 = 0xa
-       call #strand_wait
-       ret
-
-// Initialise strand context data
-//
-// In : $r15 context base
-// Out: $r15 context size (in bytes)
-//
-// Strandset(?) 3 hardcoded currently
-//
-strand_ctx_init:
-       trace_set(T_STRINIT)
-       call #strand_pre
-       mov $r14 3
-       call #strand_set
-       mov $r10 0x46fc
-       sethi $r10 0x20000
-       add b32 $r11 $r10 0x400
-       iowr I[$r10 + 0x100] $r0        // STRAND_FIRST_GENE = 0
-       mov $r12 1
-       iowr I[$r11 + 0x000] $r12       // STRAND_CMD = LATCH_FIRST_GENE
-       call #strand_wait
-       sub b32 $r12 $r0 1
-       iowr I[$r10 + 0x000] $r12       // STRAND_GENE_CNT = 0xffffffff
-       mov $r12 2
-       iowr I[$r11 + 0x000] $r12       // STRAND_CMD = LATCH_GENE_CNT
-       call #strand_wait
-       call #strand_post
-
-       // read the size of each strand, poke the context offset of
-       // each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry
-       // about it later then.
-       mov $r8 0x880
-       shl b32 $r8 6
-       iord $r9 I[$r8 + 0x000]         // STRANDS
-       add b32 $r8 0x2200
-       shr b32 $r14 $r15 8
-       ctx_init_strand_loop:
-               iowr I[$r8 + 0x000] $r14        // STRAND_SAVE_SWBASE
-               iowr I[$r8 + 0x100] $r14        // STRAND_LOAD_SWBASE
-               iord $r10 I[$r8 + 0x200]        // STRAND_SIZE
-               shr b32 $r10 6
-               add b32 $r10 1
-               add b32 $r14 $r10
-               add b32 $r8 4
-               sub b32 $r9 1
-               bra ne #ctx_init_strand_loop
-
-       shl b32 $r14 8
-       sub b32 $r15 $r14 $r15
-       trace_clr(T_STRINIT)
-       ret
-')
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/nve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/nve0.fuc
deleted file mode 100644 (file)
index f16a5d5..0000000
+++ /dev/null
@@ -1,400 +0,0 @@
-/* fuc microcode util functions for nve0 PGRAPH
- *
- * 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
- */
-
-define(`mmctx_data', `.b32 eval((($2 - 1) << 26) | $1)')
-define(`queue_init', `.skip eval((2 * 4) + ((8 * 4) * 2))')
-
-ifdef(`include_code', `
-// Error codes
-define(`E_BAD_COMMAND', 0x01)
-define(`E_CMD_OVERFLOW', 0x02)
-
-// Util macros to help with debugging ucode hangs etc
-define(`T_WAIT', 0)
-define(`T_MMCTX', 1)
-define(`T_STRWAIT', 2)
-define(`T_STRINIT', 3)
-define(`T_AUTO', 4)
-define(`T_CHAN', 5)
-define(`T_LOAD', 6)
-define(`T_SAVE', 7)
-define(`T_LCHAN', 8)
-define(`T_LCTXH', 9)
-
-define(`trace_set', `
-       mov $r8 0x83c
-       shl b32 $r8 6
-       clear b32 $r9
-       bset $r9 $1
-       iowr I[$r8 + 0x000] $r9         // CC_SCRATCH[7]
-')
-
-define(`trace_clr', `
-       mov $r8 0x85c
-       shl b32 $r8 6
-       clear b32 $r9
-       bset $r9 $1
-       iowr I[$r8 + 0x000] $r9         // CC_SCRATCH[7]
-')
-
-// queue_put - add request to queue
-//
-// In : $r13 queue pointer
-//     $r14 command
-//     $r15 data
-//
-queue_put:
-       // make sure we have space..
-       ld b32 $r8 D[$r13 + 0x0]        // GET
-       ld b32 $r9 D[$r13 + 0x4]        // PUT
-       xor $r8 8
-       cmpu b32 $r8 $r9
-       bra ne #queue_put_next
-               mov $r15 E_CMD_OVERFLOW
-               call #error
-               ret
-
-       // store cmd/data on queue
-       queue_put_next:
-       and $r8 $r9 7
-       shl b32 $r8 3
-       add b32 $r8 $r13
-       add b32 $r8 8
-       st b32 D[$r8 + 0x0] $r14
-       st b32 D[$r8 + 0x4] $r15
-
-       // update PUT
-       add b32 $r9 1
-       and $r9 0xf
-       st b32 D[$r13 + 0x4] $r9
-       ret
-
-// queue_get - fetch request from queue
-//
-// In : $r13 queue pointer
-//
-// Out:        $p1  clear on success (data available)
-//     $r14 command
-//     $r15 data
-//
-queue_get:
-       bset $flags $p1
-       ld b32 $r8 D[$r13 + 0x0]        // GET
-       ld b32 $r9 D[$r13 + 0x4]        // PUT
-       cmpu b32 $r8 $r9
-       bra e #queue_get_done
-               // fetch first cmd/data pair
-               and $r9 $r8 7
-               shl b32 $r9 3
-               add b32 $r9 $r13
-               add b32 $r9 8
-               ld b32 $r14 D[$r9 + 0x0]
-               ld b32 $r15 D[$r9 + 0x4]
-
-               // update GET
-               add b32 $r8 1
-               and $r8 0xf
-               st b32 D[$r13 + 0x0] $r8
-               bclr $flags $p1
-queue_get_done:
-       ret
-
-// nv_rd32 - read 32-bit value from nv register
-//
-// In : $r14 register
-// Out: $r15 value
-//
-nv_rd32:
-       mov $r11 0x728
-       shl b32 $r11 6
-       mov b32 $r12 $r14
-       bset $r12 31                    // MMIO_CTRL_PENDING
-       iowr I[$r11 + 0x000] $r12       // MMIO_CTRL
-       nv_rd32_wait:
-               iord $r12 I[$r11 + 0x000]
-               xbit $r12 $r12 31
-               bra ne #nv_rd32_wait
-       mov $r10 6                      // DONE_MMIO_RD
-       call #wait_doneo
-       iord $r15 I[$r11 + 0x100]       // MMIO_RDVAL
-       ret
-
-// nv_wr32 - write 32-bit value to nv register
-//
-// In : $r14 register
-//      $r15 value
-//
-nv_wr32:
-       mov $r11 0x728
-       shl b32 $r11 6
-       iowr I[$r11 + 0x200] $r15       // MMIO_WRVAL
-       mov b32 $r12 $r14
-       bset $r12 31                    // MMIO_CTRL_PENDING
-       bset $r12 30                    // MMIO_CTRL_WRITE
-       iowr I[$r11 + 0x000] $r12       // MMIO_CTRL
-       nv_wr32_wait:
-               iord $r12 I[$r11 + 0x000]
-               xbit $r12 $r12 31
-               bra ne #nv_wr32_wait
-       ret
-
-// (re)set watchdog timer
-//
-// In : $r15 timeout
-//
-watchdog_reset:
-       mov $r8 0x430
-       shl b32 $r8 6
-       bset $r15 31
-       iowr I[$r8 + 0x000] $r15
-       ret
-
-// clear watchdog timer
-watchdog_clear:
-       mov $r8 0x430
-       shl b32 $r8 6
-       iowr I[$r8 + 0x000] $r0
-       ret
-
-// wait_done{z,o} - wait on FUC_DONE bit to become clear/set
-//
-// In : $r10 bit to wait on
-//
-define(`wait_done', `
-$1:
-       trace_set(T_WAIT);
-       mov $r8 0x818
-       shl b32 $r8 6
-       iowr I[$r8 + 0x000] $r10        // CC_SCRATCH[6] = wait bit
-       wait_done_$1:
-               mov $r8 0x400
-               shl b32 $r8 6
-               iord $r8 I[$r8 + 0x000] // DONE
-               xbit $r8 $r8 $r10
-               bra $2 #wait_done_$1
-       trace_clr(T_WAIT)
-       ret
-')
-wait_done(wait_donez, ne)
-wait_done(wait_doneo, e)
-
-// mmctx_size - determine size of a mmio list transfer
-//
-// In : $r14 mmio list head
-//      $r15 mmio list tail
-// Out: $r15 transfer size (in bytes)
-//
-mmctx_size:
-       clear b32 $r9
-       nv_mmctx_size_loop:
-               ld b32 $r8 D[$r14]
-               shr b32 $r8 26
-               add b32 $r8 1
-               shl b32 $r8 2
-               add b32 $r9 $r8
-               add b32 $r14 4
-               cmpu b32 $r14 $r15
-               bra ne #nv_mmctx_size_loop
-       mov b32 $r15 $r9
-       ret
-
-// mmctx_xfer - execute a list of mmio transfers
-//
-// In : $r10 flags
-//             bit 0: direction (0 = save, 1 = load)
-//             bit 1: set if first transfer
-//             bit 2: set if last transfer
-//     $r11 base
-//     $r12 mmio list head
-//     $r13 mmio list tail
-//     $r14 multi_stride
-//     $r15 multi_mask
-//
-mmctx_xfer:
-       trace_set(T_MMCTX)
-       mov $r8 0x710
-       shl b32 $r8 6
-       clear b32 $r9
-       or $r11 $r11
-       bra e #mmctx_base_disabled
-               iowr I[$r8 + 0x000] $r11        // MMCTX_BASE
-               bset $r9 0                      // BASE_EN
-       mmctx_base_disabled:
-       or $r14 $r14
-       bra e #mmctx_multi_disabled
-               iowr I[$r8 + 0x200] $r14        // MMCTX_MULTI_STRIDE
-               iowr I[$r8 + 0x300] $r15        // MMCTX_MULTI_MASK
-               bset $r9 1                      // MULTI_EN
-       mmctx_multi_disabled:
-       add b32 $r8 0x100
-
-       xbit $r11 $r10 0
-       shl b32 $r11 16                 // DIR
-       bset $r11 12                    // QLIMIT = 0x10
-       xbit $r14 $r10 1
-       shl b32 $r14 17
-       or $r11 $r14                    // START_TRIGGER
-       iowr I[$r8 + 0x000] $r11        // MMCTX_CTRL
-
-       // loop over the mmio list, and send requests to the hw
-       mmctx_exec_loop:
-               // wait for space in mmctx queue
-               mmctx_wait_free:
-                       iord $r14 I[$r8 + 0x000] // MMCTX_CTRL
-                       and $r14 0x1f
-                       bra e #mmctx_wait_free
-
-               // queue up an entry
-               ld b32 $r14 D[$r12]
-               or $r14 $r9
-               iowr I[$r8 + 0x300] $r14
-               add b32 $r12 4
-               cmpu b32 $r12 $r13
-               bra ne #mmctx_exec_loop
-
-       xbit $r11 $r10 2
-       bra ne #mmctx_stop
-               // wait for queue to empty
-               mmctx_fini_wait:
-                       iord $r11 I[$r8 + 0x000]        // MMCTX_CTRL
-                       and $r11 0x1f
-                       cmpu b32 $r11 0x10
-                       bra ne #mmctx_fini_wait
-               mov $r10 2                              // DONE_MMCTX
-               call #wait_donez
-               bra #mmctx_done
-       mmctx_stop:
-               xbit $r11 $r10 0
-               shl b32 $r11 16                 // DIR
-               bset $r11 12                    // QLIMIT = 0x10
-               bset $r11 18                    // STOP_TRIGGER
-               iowr I[$r8 + 0x000] $r11        // MMCTX_CTRL
-               mmctx_stop_wait:
-                       // wait for STOP_TRIGGER to clear
-                       iord $r11 I[$r8 + 0x000] // MMCTX_CTRL
-                       xbit $r11 $r11 18
-                       bra ne #mmctx_stop_wait
-       mmctx_done:
-       trace_clr(T_MMCTX)
-       ret
-
-// Wait for DONE_STRAND
-//
-strand_wait:
-       push $r10
-       mov $r10 2
-       call #wait_donez
-       pop $r10
-       ret
-
-// unknown - call before issuing strand commands
-//
-strand_pre:
-       mov $r8 0x4afc
-       sethi $r8 0x20000
-       mov $r9 0xc
-       iowr I[$r8] $r9
-       call #strand_wait
-       ret
-
-// unknown - call after issuing strand commands
-//
-strand_post:
-       mov $r8 0x4afc
-       sethi $r8 0x20000
-       mov $r9 0xd
-       iowr I[$r8] $r9
-       call #strand_wait
-       ret
-
-// Selects strand set?!
-//
-// In: $r14 id
-//
-strand_set:
-       mov $r10 0x4ffc
-       sethi $r10 0x20000
-       sub b32 $r11 $r10 0x500
-       mov $r12 0xf
-       iowr I[$r10 + 0x000] $r12               // 0x93c = 0xf
-       mov $r12 0xb
-       iowr I[$r11 + 0x000] $r12               // 0x928 = 0xb
-       call #strand_wait
-       iowr I[$r10 + 0x000] $r14               // 0x93c = <id>
-       mov $r12 0xa
-       iowr I[$r11 + 0x000] $r12               // 0x928 = 0xa
-       call #strand_wait
-       ret
-
-// Initialise strand context data
-//
-// In : $r15 context base
-// Out: $r15 context size (in bytes)
-//
-// Strandset(?) 3 hardcoded currently
-//
-strand_ctx_init:
-       trace_set(T_STRINIT)
-       call #strand_pre
-       mov $r14 3
-       call #strand_set
-       mov $r10 0x46fc
-       sethi $r10 0x20000
-       add b32 $r11 $r10 0x400
-       iowr I[$r10 + 0x100] $r0        // STRAND_FIRST_GENE = 0
-       mov $r12 1
-       iowr I[$r11 + 0x000] $r12       // STRAND_CMD = LATCH_FIRST_GENE
-       call #strand_wait
-       sub b32 $r12 $r0 1
-       iowr I[$r10 + 0x000] $r12       // STRAND_GENE_CNT = 0xffffffff
-       mov $r12 2
-       iowr I[$r11 + 0x000] $r12       // STRAND_CMD = LATCH_GENE_CNT
-       call #strand_wait
-       call #strand_post
-
-       // read the size of each strand, poke the context offset of
-       // each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry
-       // about it later then.
-       mov $r8 0x880
-       shl b32 $r8 6
-       iord $r9 I[$r8 + 0x000]         // STRANDS
-       add b32 $r8 0x2200
-       shr b32 $r14 $r15 8
-       ctx_init_strand_loop:
-               iowr I[$r8 + 0x000] $r14        // STRAND_SAVE_SWBASE
-               iowr I[$r8 + 0x100] $r14        // STRAND_LOAD_SWBASE
-               iord $r10 I[$r8 + 0x200]        // STRAND_SIZE
-               shr b32 $r10 6
-               add b32 $r10 1
-               add b32 $r14 $r10
-               add b32 $r8 4
-               sub b32 $r9 1
-               bra ne #ctx_init_strand_loop
-
-       shl b32 $r14 8
-       sub b32 $r15 $r14 $r15
-       trace_clr(T_STRINIT)
-       ret
-')
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h
new file mode 100644 (file)
index 0000000..fd1d380
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __NVKM_GRAPH_OS_H__
+#define __NVKM_GRAPH_OS_H__
+
+#define E_BAD_COMMAND  0x00000001
+#define E_CMD_OVERFLOW 0x00000002
+
+#endif
index 1ac3611..03de517 100644 (file)
@@ -186,13 +186,6 @@ nv50_graph_cclass = {
  * PGRAPH engine/subdev functions
  ******************************************************************************/
 
-static int
-nv50_graph_tlb_flush(struct nouveau_engine *engine)
-{
-       nv50_vm_flush_engine(&engine->base, 0x00);
-       return 0;
-}
-
 static const struct nouveau_bitfield nv50_pgraph_status[] = {
        { 0x00000001, "BUSY" }, /* set when any bit is set */
        { 0x00000002, "DISPATCH" },
@@ -302,8 +295,10 @@ nv84_graph_tlb_flush(struct nouveau_engine *engine)
                                nv_rd32(priv, 0x400388));
        }
 
-       nv50_vm_flush_engine(&engine->base, 0x00);
 
+       nv_wr32(priv, 0x100c80, 0x00000001);
+       if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000))
+               nv_error(priv, "vm flush timeout\n");
        nv_mask(priv, 0x400500, 0x00000001, 0x00000001);
        spin_unlock_irqrestore(&priv->lock, flags);
        return timeout ? -EBUSY : 0;
@@ -857,10 +852,9 @@ nv50_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 
        };
 
-       if (nv_device(priv)->chipset == 0x50 ||
-           nv_device(priv)->chipset == 0xac)
-               nv_engine(priv)->tlb_flush = nv50_graph_tlb_flush;
-       else
+       /* unfortunate hw bug workaround... */
+       if (nv_device(priv)->chipset != 0x50 &&
+           nv_device(priv)->chipset != 0xac)
                nv_engine(priv)->tlb_flush = nv84_graph_tlb_flush;
 
        spin_lock_init(&priv->lock);
index f9b9d82..3f4f35c 100644 (file)
  */
 
 #include "nvc0.h"
-#include "fuc/hubnvc0.fuc.h"
-#include "fuc/gpcnvc0.fuc.h"
 
 /*******************************************************************************
  * Graphics object classes
  ******************************************************************************/
 
-static struct nouveau_oclass
+struct nouveau_oclass
 nvc0_graph_sclass[] = {
        { 0x902d, &nouveau_object_ofuncs },
        { 0x9039, &nouveau_object_ofuncs },
@@ -39,40 +37,6 @@ nvc0_graph_sclass[] = {
        {}
 };
 
-static struct nouveau_oclass
-nvc1_graph_sclass[] = {
-       { 0x902d, &nouveau_object_ofuncs },
-       { 0x9039, &nouveau_object_ofuncs },
-       { 0x9097, &nouveau_object_ofuncs },
-       { 0x90c0, &nouveau_object_ofuncs },
-       { 0x9197, &nouveau_object_ofuncs },
-       {}
-};
-
-static struct nouveau_oclass
-nvc8_graph_sclass[] = {
-       { 0x902d, &nouveau_object_ofuncs },
-       { 0x9039, &nouveau_object_ofuncs },
-       { 0x9097, &nouveau_object_ofuncs },
-       { 0x90c0, &nouveau_object_ofuncs },
-       { 0x9197, &nouveau_object_ofuncs },
-       { 0x9297, &nouveau_object_ofuncs },
-       {}
-};
-
-u64
-nvc0_graph_units(struct nouveau_graph *graph)
-{
-       struct nvc0_graph_priv *priv = (void *)graph;
-       u64 cfg;
-
-       cfg  = (u32)priv->gpc_nr;
-       cfg |= (u32)priv->tpc_total << 8;
-       cfg |= (u64)priv->rop_nr << 32;
-
-       return cfg;
-}
-
 /*******************************************************************************
  * PGRAPH context
  ******************************************************************************/
@@ -181,60 +145,308 @@ nvc0_graph_context_dtor(struct nouveau_object *object)
        nouveau_graph_context_destroy(&chan->base);
 }
 
-static struct nouveau_oclass
-nvc0_graph_cclass = {
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nvc0_graph_context_ctor,
-               .dtor = nvc0_graph_context_dtor,
-               .init = _nouveau_graph_context_init,
-               .fini = _nouveau_graph_context_fini,
-               .rd32 = _nouveau_graph_context_rd32,
-               .wr32 = _nouveau_graph_context_wr32,
-       },
-};
-
 /*******************************************************************************
  * PGRAPH engine/subdev functions
  ******************************************************************************/
 
-static void
-nvc0_graph_ctxctl_debug_unit(struct nvc0_graph_priv *priv, u32 base)
+struct nvc0_graph_init
+nvc0_graph_init_regs[] = {
+       { 0x400080,   1, 0x04, 0x003083c2 },
+       { 0x400088,   1, 0x04, 0x00006fe7 },
+       { 0x40008c,   1, 0x04, 0x00000000 },
+       { 0x400090,   1, 0x04, 0x00000030 },
+       { 0x40013c,   1, 0x04, 0x013901f7 },
+       { 0x400140,   1, 0x04, 0x00000100 },
+       { 0x400144,   1, 0x04, 0x00000000 },
+       { 0x400148,   1, 0x04, 0x00000110 },
+       { 0x400138,   1, 0x04, 0x00000000 },
+       { 0x400130,   2, 0x04, 0x00000000 },
+       { 0x400124,   1, 0x04, 0x00000002 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_graph_init_unk40xx[] = {
+       { 0x40415c,   1, 0x04, 0x00000000 },
+       { 0x404170,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_graph_init_unk44xx[] = {
+       { 0x404488,   2, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_graph_init_unk78xx[] = {
+       { 0x407808,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_graph_init_unk60xx[] = {
+       { 0x406024,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_graph_init_unk58xx[] = {
+       { 0x405844,   1, 0x04, 0x00ffffff },
+       { 0x405850,   1, 0x04, 0x00000000 },
+       { 0x405908,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_graph_init_unk80xx[] = {
+       { 0x40803c,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_graph_init_gpc[] = {
+       { 0x4184a0,   1, 0x04, 0x00000000 },
+       { 0x418604,   1, 0x04, 0x00000000 },
+       { 0x418680,   1, 0x04, 0x00000000 },
+       { 0x418714,   1, 0x04, 0x80000000 },
+       { 0x418384,   1, 0x04, 0x00000000 },
+       { 0x418814,   3, 0x04, 0x00000000 },
+       { 0x418b04,   1, 0x04, 0x00000000 },
+       { 0x4188c8,   1, 0x04, 0x80000000 },
+       { 0x4188cc,   1, 0x04, 0x00000000 },
+       { 0x4188d0,   1, 0x04, 0x00010000 },
+       { 0x4188d4,   1, 0x04, 0x00000001 },
+       { 0x418910,   1, 0x04, 0x00010001 },
+       { 0x418914,   1, 0x04, 0x00000301 },
+       { 0x418918,   1, 0x04, 0x00800000 },
+       { 0x418980,   1, 0x04, 0x77777770 },
+       { 0x418984,   3, 0x04, 0x77777777 },
+       { 0x418c04,   1, 0x04, 0x00000000 },
+       { 0x418c88,   1, 0x04, 0x00000000 },
+       { 0x418d00,   1, 0x04, 0x00000000 },
+       { 0x418f08,   1, 0x04, 0x00000000 },
+       { 0x418e00,   1, 0x04, 0x00000050 },
+       { 0x418e08,   1, 0x04, 0x00000000 },
+       { 0x41900c,   1, 0x04, 0x00000000 },
+       { 0x419018,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvc0_graph_init_tpc[] = {
+       { 0x419d08,   2, 0x04, 0x00000000 },
+       { 0x419d10,   1, 0x04, 0x00000014 },
+       { 0x419ab0,   1, 0x04, 0x00000000 },
+       { 0x419ab8,   1, 0x04, 0x000000e7 },
+       { 0x419abc,   2, 0x04, 0x00000000 },
+       { 0x41980c,   3, 0x04, 0x00000000 },
+       { 0x419844,   1, 0x04, 0x00000000 },
+       { 0x41984c,   1, 0x04, 0x00005bc5 },
+       { 0x419850,   4, 0x04, 0x00000000 },
+       { 0x419c98,   1, 0x04, 0x00000000 },
+       { 0x419ca8,   1, 0x04, 0x80000000 },
+       { 0x419cb4,   1, 0x04, 0x00000000 },
+       { 0x419cb8,   1, 0x04, 0x00008bf4 },
+       { 0x419cbc,   1, 0x04, 0x28137606 },
+       { 0x419cc0,   2, 0x04, 0x00000000 },
+       { 0x419bd4,   1, 0x04, 0x00800000 },
+       { 0x419bdc,   1, 0x04, 0x00000000 },
+       { 0x419d2c,   1, 0x04, 0x00000000 },
+       { 0x419c0c,   1, 0x04, 0x00000000 },
+       { 0x419e00,   1, 0x04, 0x00000000 },
+       { 0x419ea0,   1, 0x04, 0x00000000 },
+       { 0x419ea4,   1, 0x04, 0x00000100 },
+       { 0x419ea8,   1, 0x04, 0x00001100 },
+       { 0x419eac,   1, 0x04, 0x11100702 },
+       { 0x419eb0,   1, 0x04, 0x00000003 },
+       { 0x419eb4,   4, 0x04, 0x00000000 },
+       { 0x419ec8,   1, 0x04, 0x06060618 },
+       { 0x419ed0,   1, 0x04, 0x0eff0e38 },
+       { 0x419ed4,   1, 0x04, 0x011104f1 },
+       { 0x419edc,   1, 0x04, 0x00000000 },
+       { 0x419f00,   1, 0x04, 0x00000000 },
+       { 0x419f2c,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_graph_init_unk88xx[] = {
+       { 0x40880c,   1, 0x04, 0x00000000 },
+       { 0x408910,   9, 0x04, 0x00000000 },
+       { 0x408950,   1, 0x04, 0x00000000 },
+       { 0x408954,   1, 0x04, 0x0000ffff },
+       { 0x408984,   1, 0x04, 0x00000000 },
+       { 0x408988,   1, 0x04, 0x08040201 },
+       { 0x40898c,   1, 0x04, 0x80402010 },
+       {}
+};
+
+struct nvc0_graph_init
+nvc0_graph_tpc_0[] = {
+       { 0x50405c,   1, 0x04, 0x00000001 },
+       {}
+};
+
+void
+nvc0_graph_mmio(struct nvc0_graph_priv *priv, struct nvc0_graph_init *init)
 {
-       nv_error(priv, "%06x - done 0x%08x\n", base,
-                nv_rd32(priv, base + 0x400));
-       nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base,
-                nv_rd32(priv, base + 0x800), nv_rd32(priv, base + 0x804),
-                nv_rd32(priv, base + 0x808), nv_rd32(priv, base + 0x80c));
-       nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base,
-                nv_rd32(priv, base + 0x810), nv_rd32(priv, base + 0x814),
-                nv_rd32(priv, base + 0x818), nv_rd32(priv, base + 0x81c));
+       for (; init && init->count; init++) {
+               u32 addr = init->addr, i;
+               for (i = 0; i < init->count; i++) {
+                       nv_wr32(priv, addr, init->data);
+                       addr += init->pitch;
+               }
+       }
 }
 
 void
-nvc0_graph_ctxctl_debug(struct nvc0_graph_priv *priv)
+nvc0_graph_icmd(struct nvc0_graph_priv *priv, struct nvc0_graph_init *init)
 {
-       u32 gpcnr = nv_rd32(priv, 0x409604) & 0xffff;
-       u32 gpc;
+       u32 addr, data;
+       int i, j;
+
+       nv_wr32(priv, 0x400208, 0x80000000);
+       for (i = 0; init->count; init++, i++) {
+               if (!i || data != init->data) {
+                       nv_wr32(priv, 0x400204, init->data);
+                       data = init->data;
+               }
 
-       nvc0_graph_ctxctl_debug_unit(priv, 0x409000);
-       for (gpc = 0; gpc < gpcnr; gpc++)
-               nvc0_graph_ctxctl_debug_unit(priv, 0x502000 + (gpc * 0x8000));
+               addr = init->addr;
+               for (j = 0; j < init->count; j++) {
+                       nv_wr32(priv, 0x400200, addr);
+                       addr += init->pitch;
+                       while (nv_rd32(priv, 0x400700) & 0x00000002) {}
+               }
+       }
+       nv_wr32(priv, 0x400208, 0x00000000);
+}
+
+void
+nvc0_graph_mthd(struct nvc0_graph_priv *priv, struct nvc0_graph_mthd *mthds)
+{
+       struct nvc0_graph_mthd *mthd;
+       struct nvc0_graph_init *init;
+       int i = 0, j;
+       u32 data;
+
+       while ((mthd = &mthds[i++]) && (init = mthd->init)) {
+               u32  addr = 0x80000000 | mthd->oclass;
+               for (data = 0; init->count; init++) {
+                       if (data != init->data) {
+                               nv_wr32(priv, 0x40448c, init->data);
+                               data = init->data;
+                       }
+
+                       addr = (addr & 0x8000ffff) | (init->addr << 14);
+                       for (j = 0; j < init->count; j++) {
+                               nv_wr32(priv, 0x404488, addr);
+                               addr += init->pitch << 14;
+                       }
+               }
+       }
+}
+
+u64
+nvc0_graph_units(struct nouveau_graph *graph)
+{
+       struct nvc0_graph_priv *priv = (void *)graph;
+       u64 cfg;
+
+       cfg  = (u32)priv->gpc_nr;
+       cfg |= (u32)priv->tpc_total << 8;
+       cfg |= (u64)priv->rop_nr << 32;
+
+       return cfg;
 }
 
+static const struct nouveau_enum nve0_sked_error[] = {
+       { 7, "CONSTANT_BUFFER_SIZE" },
+       { 9, "LOCAL_MEMORY_SIZE_POS" },
+       { 10, "LOCAL_MEMORY_SIZE_NEG" },
+       { 11, "WARP_CSTACK_SIZE" },
+       { 12, "TOTAL_TEMP_SIZE" },
+       { 13, "REGISTER_COUNT" },
+       { 18, "TOTAL_THREADS" },
+       { 20, "PROGRAM_OFFSET" },
+       { 21, "SHARED_MEMORY_SIZE" },
+       { 25, "SHARED_CONFIG_TOO_SMALL" },
+       { 26, "TOTAL_REGISTER_COUNT" },
+       {}
+};
+
+static const struct nouveau_enum nvc0_gpc_rop_error[] = {
+       { 1, "RT_PITCH_OVERRUN" },
+       { 4, "RT_WIDTH_OVERRUN" },
+       { 5, "RT_HEIGHT_OVERRUN" },
+       { 7, "ZETA_STORAGE_TYPE_MISMATCH" },
+       { 8, "RT_STORAGE_TYPE_MISMATCH" },
+       { 10, "RT_LINEAR_MISMATCH" },
+       {}
+};
+
 static void
-nvc0_graph_ctxctl_isr(struct nvc0_graph_priv *priv)
+nvc0_graph_trap_gpc_rop(struct nvc0_graph_priv *priv, int gpc)
 {
-       u32 ustat = nv_rd32(priv, 0x409c18);
+       u32 trap[4];
+       int i;
 
-       if (ustat & 0x00000001)
-               nv_error(priv, "CTXCTRL ucode error\n");
-       if (ustat & 0x00080000)
-               nv_error(priv, "CTXCTRL watchdog timeout\n");
-       if (ustat & ~0x00080001)
-               nv_error(priv, "CTXCTRL 0x%08x\n", ustat);
+       trap[0] = nv_rd32(priv, GPC_UNIT(gpc, 0x0420));
+       trap[1] = nv_rd32(priv, GPC_UNIT(gpc, 0x0434));
+       trap[2] = nv_rd32(priv, GPC_UNIT(gpc, 0x0438));
+       trap[3] = nv_rd32(priv, GPC_UNIT(gpc, 0x043c));
+
+       nv_error(priv, "GPC%d/PROP trap:", gpc);
+       for (i = 0; i <= 29; ++i) {
+               if (!(trap[0] & (1 << i)))
+                       continue;
+               pr_cont(" ");
+               nouveau_enum_print(nvc0_gpc_rop_error, i);
+       }
+       pr_cont("\n");
 
-       nvc0_graph_ctxctl_debug(priv);
-       nv_wr32(priv, 0x409c20, ustat);
+       nv_error(priv, "x = %u, y = %u, format = %x, storage type = %x\n",
+                trap[1] & 0xffff, trap[1] >> 16, (trap[2] >> 8) & 0x3f,
+                trap[3] & 0xff);
+       nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000);
+}
+
+static const struct nouveau_enum nvc0_mp_warp_error[] = {
+       { 0x00, "NO_ERROR" },
+       { 0x01, "STACK_MISMATCH" },
+       { 0x05, "MISALIGNED_PC" },
+       { 0x08, "MISALIGNED_GPR" },
+       { 0x09, "INVALID_OPCODE" },
+       { 0x0d, "GPR_OUT_OF_BOUNDS" },
+       { 0x0e, "MEM_OUT_OF_BOUNDS" },
+       { 0x0f, "UNALIGNED_MEM_ACCESS" },
+       { 0x11, "INVALID_PARAM" },
+       {}
+};
+
+static const struct nouveau_bitfield nvc0_mp_global_error[] = {
+       { 0x00000004, "MULTIPLE_WARP_ERRORS" },
+       { 0x00000008, "OUT_OF_STACK_SPACE" },
+       {}
+};
+
+static void
+nvc0_graph_trap_mp(struct nvc0_graph_priv *priv, int gpc, int tpc)
+{
+       u32 werr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x648));
+       u32 gerr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x650));
+
+       nv_error(priv, "GPC%i/TPC%i/MP trap:", gpc, tpc);
+       nouveau_bitfield_print(nvc0_mp_global_error, gerr);
+       if (werr) {
+               pr_cont(" ");
+               nouveau_enum_print(nvc0_mp_warp_error, werr & 0xffff);
+       }
+       pr_cont("\n");
+
+       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x648), 0x00000000);
+       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x650), gerr);
 }
 
 static void
@@ -246,18 +458,11 @@ nvc0_graph_trap_tpc(struct nvc0_graph_priv *priv, int gpc, int tpc)
                u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0224));
                nv_error(priv, "GPC%d/TPC%d/TEX: 0x%08x\n", gpc, tpc, trap);
                nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0224), 0xc0000000);
-               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0508), 0x00000001);
                stat &= ~0x00000001;
        }
 
        if (stat & 0x00000002) {
-               u32 trap0 = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0644));
-               u32 trap1 = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x064c));
-               nv_error(priv, "GPC%d/TPC%d/MP: 0x%08x 0x%08x\n",
-                              gpc, tpc, trap0, trap1);
-               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0644), 0x001ffffe);
-               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x064c), 0x0000000f);
-               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0508), 0x00000002);
+               nvc0_graph_trap_mp(priv, gpc, tpc);
                stat &= ~0x00000002;
        }
 
@@ -265,7 +470,6 @@ nvc0_graph_trap_tpc(struct nvc0_graph_priv *priv, int gpc, int tpc)
                u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0084));
                nv_error(priv, "GPC%d/TPC%d/POLY: 0x%08x\n", gpc, tpc, trap);
                nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0084), 0xc0000000);
-               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0508), 0x00000004);
                stat &= ~0x00000004;
        }
 
@@ -273,13 +477,11 @@ nvc0_graph_trap_tpc(struct nvc0_graph_priv *priv, int gpc, int tpc)
                u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x048c));
                nv_error(priv, "GPC%d/TPC%d/L1C: 0x%08x\n", gpc, tpc, trap);
                nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x048c), 0xc0000000);
-               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0508), 0x00000008);
                stat &= ~0x00000008;
        }
 
        if (stat) {
                nv_error(priv, "GPC%d/TPC%d/0x%08x: unknown\n", gpc, tpc, stat);
-               nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0508), stat);
        }
 }
 
@@ -290,10 +492,7 @@ nvc0_graph_trap_gpc(struct nvc0_graph_priv *priv, int gpc)
        int tpc;
 
        if (stat & 0x00000001) {
-               u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0420));
-               nv_error(priv, "GPC%d/PROP: 0x%08x\n", gpc, trap);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0x00000001);
+               nvc0_graph_trap_gpc_rop(priv, gpc);
                stat &= ~0x00000001;
        }
 
@@ -301,7 +500,6 @@ nvc0_graph_trap_gpc(struct nvc0_graph_priv *priv, int gpc)
                u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0900));
                nv_error(priv, "GPC%d/ZCULL: 0x%08x\n", gpc, trap);
                nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0x00000002);
                stat &= ~0x00000002;
        }
 
@@ -309,7 +507,6 @@ nvc0_graph_trap_gpc(struct nvc0_graph_priv *priv, int gpc)
                u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x1028));
                nv_error(priv, "GPC%d/CCACHE: 0x%08x\n", gpc, trap);
                nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0x00000004);
                stat &= ~0x00000004;
        }
 
@@ -317,7 +514,6 @@ nvc0_graph_trap_gpc(struct nvc0_graph_priv *priv, int gpc)
                u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0824));
                nv_error(priv, "GPC%d/ESETUP: 0x%08x\n", gpc, trap);
                nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0x00000008);
                stat &= ~0x00000009;
        }
 
@@ -332,7 +528,6 @@ nvc0_graph_trap_gpc(struct nvc0_graph_priv *priv, int gpc)
 
        if (stat) {
                nv_error(priv, "GPC%d/0x%08x: unknown\n", gpc, stat);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), stat);
        }
 }
 
@@ -340,7 +535,7 @@ static void
 nvc0_graph_trap_intr(struct nvc0_graph_priv *priv)
 {
        u32 trap = nv_rd32(priv, 0x400108);
-       int rop, gpc;
+       int rop, gpc, i;
 
        if (trap & 0x00000001) {
                u32 stat = nv_rd32(priv, 0x404000);
@@ -390,6 +585,24 @@ nvc0_graph_trap_intr(struct nvc0_graph_priv *priv)
                trap &= ~0x00000080;
        }
 
+       if (trap & 0x00000100) {
+               u32 stat = nv_rd32(priv, 0x407020);
+
+               nv_error(priv, "SKED:");
+               for (i = 0; i <= 29; ++i) {
+                       if (!(stat & (1 << i)))
+                               continue;
+                       pr_cont(" ");
+                       nouveau_enum_print(nve0_sked_error, i);
+               }
+               pr_cont("\n");
+
+               if (stat & 0x3fffffff)
+                       nv_wr32(priv, 0x407020, 0x40000000);
+               nv_wr32(priv, 0x400108, 0x00000100);
+               trap &= ~0x00000100;
+       }
+
        if (trap & 0x01000000) {
                u32 stat = nv_rd32(priv, 0x400118);
                for (gpc = 0; stat && gpc < priv->gpc_nr; gpc++) {
@@ -423,6 +636,46 @@ nvc0_graph_trap_intr(struct nvc0_graph_priv *priv)
        }
 }
 
+static void
+nvc0_graph_ctxctl_debug_unit(struct nvc0_graph_priv *priv, u32 base)
+{
+       nv_error(priv, "%06x - done 0x%08x\n", base,
+                nv_rd32(priv, base + 0x400));
+       nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base,
+                nv_rd32(priv, base + 0x800), nv_rd32(priv, base + 0x804),
+                nv_rd32(priv, base + 0x808), nv_rd32(priv, base + 0x80c));
+       nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base,
+                nv_rd32(priv, base + 0x810), nv_rd32(priv, base + 0x814),
+                nv_rd32(priv, base + 0x818), nv_rd32(priv, base + 0x81c));
+}
+
+void
+nvc0_graph_ctxctl_debug(struct nvc0_graph_priv *priv)
+{
+       u32 gpcnr = nv_rd32(priv, 0x409604) & 0xffff;
+       u32 gpc;
+
+       nvc0_graph_ctxctl_debug_unit(priv, 0x409000);
+       for (gpc = 0; gpc < gpcnr; gpc++)
+               nvc0_graph_ctxctl_debug_unit(priv, 0x502000 + (gpc * 0x8000));
+}
+
+static void
+nvc0_graph_ctxctl_isr(struct nvc0_graph_priv *priv)
+{
+       u32 ustat = nv_rd32(priv, 0x409c18);
+
+       if (ustat & 0x00000001)
+               nv_error(priv, "CTXCTL ucode error\n");
+       if (ustat & 0x00080000)
+               nv_error(priv, "CTXCTL watchdog timeout\n");
+       if (ustat & ~0x00080001)
+               nv_error(priv, "CTXCTL 0x%08x\n", ustat);
+
+       nvc0_graph_ctxctl_debug(priv);
+       nv_wr32(priv, 0x409c20, ustat);
+}
+
 static void
 nvc0_graph_intr(struct nouveau_subdev *subdev)
 {
@@ -499,211 +752,242 @@ nvc0_graph_intr(struct nouveau_subdev *subdev)
        nouveau_engctx_put(engctx);
 }
 
-int
-nvc0_graph_ctor_fw(struct nvc0_graph_priv *priv, const char *fwname,
-                  struct nvc0_graph_fuc *fuc)
+void
+nvc0_graph_init_fw(struct nvc0_graph_priv *priv, u32 fuc_base,
+                  struct nvc0_graph_fuc *code, struct nvc0_graph_fuc *data)
 {
-       struct nouveau_device *device = nv_device(priv);
-       const struct firmware *fw;
-       char f[32];
-       int ret;
+       int i;
 
-       snprintf(f, sizeof(f), "nouveau/nv%02x_%s", device->chipset, fwname);
-       ret = request_firmware(&fw, f, &device->pdev->dev);
-       if (ret) {
-               snprintf(f, sizeof(f), "nouveau/%s", fwname);
-               ret = request_firmware(&fw, f, &device->pdev->dev);
-               if (ret) {
-                       nv_error(priv, "failed to load %s\n", fwname);
-                       return ret;
-               }
+       nv_wr32(priv, fuc_base + 0x01c0, 0x01000000);
+       for (i = 0; i < data->size / 4; i++)
+               nv_wr32(priv, fuc_base + 0x01c4, data->data[i]);
+
+       nv_wr32(priv, fuc_base + 0x0180, 0x01000000);
+       for (i = 0; i < code->size / 4; i++) {
+               if ((i & 0x3f) == 0)
+                       nv_wr32(priv, fuc_base + 0x0188, i >> 6);
+               nv_wr32(priv, fuc_base + 0x0184, code->data[i]);
        }
+}
 
-       fuc->size = fw->size;
-       fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL);
-       release_firmware(fw);
-       return (fuc->data != NULL) ? 0 : -ENOMEM;
+static void
+nvc0_graph_init_csdata(struct nvc0_graph_priv *priv,
+                      struct nvc0_graph_init *init,
+                      u32 falcon, u32 starstar, u32 base)
+{
+       u32 addr = init->addr;
+       u32 next = addr;
+       u32 star, temp;
+
+       nv_wr32(priv, falcon + 0x01c0, 0x02000000 + starstar);
+       star = nv_rd32(priv, falcon + 0x01c4);
+       temp = nv_rd32(priv, falcon + 0x01c4);
+       if (temp > star)
+               star = temp;
+       nv_wr32(priv, falcon + 0x01c0, 0x01000000 + star);
+
+       do {
+               if (init->addr != next) {
+                       while (addr < next) {
+                               u32 nr = min((int)(next - addr) / 4, 32);
+                               nv_wr32(priv, falcon + 0x01c4,
+                                       ((nr - 1) << 26) | (addr - base));
+                               addr += nr * 4;
+                               star += 4;
+                       }
+                       addr = next = init->addr;
+               }
+               next += init->count * 4;
+       } while ((init++)->count);
+
+       nv_wr32(priv, falcon + 0x01c0, 0x01000004 + starstar);
+       nv_wr32(priv, falcon + 0x01c4, star);
 }
 
-static int
-nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-               struct nouveau_oclass *oclass, void *data, u32 size,
-               struct nouveau_object **pobject)
+int
+nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv)
 {
-       struct nouveau_device *device = nv_device(parent);
-       struct nvc0_graph_priv *priv;
-       bool enable = device->chipset != 0xd7;
-       int ret, i;
+       struct nvc0_graph_oclass *oclass = (void *)nv_object(priv)->oclass;
+       struct nvc0_grctx_oclass *cclass = (void *)nv_engine(priv)->cclass;
+       struct nvc0_graph_init *init;
+       u32 r000260;
+       int i;
 
-       ret = nouveau_graph_create(parent, engine, oclass, enable, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
+       if (priv->firmware) {
+               /* load fuc microcode */
+               r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
+               nvc0_graph_init_fw(priv, 0x409000, &priv->fuc409c,
+                                                  &priv->fuc409d);
+               nvc0_graph_init_fw(priv, 0x41a000, &priv->fuc41ac,
+                                                  &priv->fuc41ad);
+               nv_wr32(priv, 0x000260, r000260);
 
-       nv_subdev(priv)->unit = 0x18001000;
-       nv_subdev(priv)->intr = nvc0_graph_intr;
-       nv_engine(priv)->cclass = &nvc0_graph_cclass;
+               /* start both of them running */
+               nv_wr32(priv, 0x409840, 0xffffffff);
+               nv_wr32(priv, 0x41a10c, 0x00000000);
+               nv_wr32(priv, 0x40910c, 0x00000000);
+               nv_wr32(priv, 0x41a100, 0x00000002);
+               nv_wr32(priv, 0x409100, 0x00000002);
+               if (!nv_wait(priv, 0x409800, 0x00000001, 0x00000001))
+                       nv_warn(priv, "0x409800 wait failed\n");
 
-       priv->base.units = nvc0_graph_units;
+               nv_wr32(priv, 0x409840, 0xffffffff);
+               nv_wr32(priv, 0x409500, 0x7fffffff);
+               nv_wr32(priv, 0x409504, 0x00000021);
 
-       if (nouveau_boolopt(device->cfgopt, "NvGrUseFW", false)) {
-               nv_info(priv, "using external firmware\n");
-               if (nvc0_graph_ctor_fw(priv, "fuc409c", &priv->fuc409c) ||
-                   nvc0_graph_ctor_fw(priv, "fuc409d", &priv->fuc409d) ||
-                   nvc0_graph_ctor_fw(priv, "fuc41ac", &priv->fuc41ac) ||
-                   nvc0_graph_ctor_fw(priv, "fuc41ad", &priv->fuc41ad))
-                       return -EINVAL;
-               priv->firmware = true;
+               nv_wr32(priv, 0x409840, 0xffffffff);
+               nv_wr32(priv, 0x409500, 0x00000000);
+               nv_wr32(priv, 0x409504, 0x00000010);
+               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
+                       nv_error(priv, "fuc09 req 0x10 timeout\n");
+                       return -EBUSY;
+               }
+               priv->size = nv_rd32(priv, 0x409800);
+
+               nv_wr32(priv, 0x409840, 0xffffffff);
+               nv_wr32(priv, 0x409500, 0x00000000);
+               nv_wr32(priv, 0x409504, 0x00000016);
+               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
+                       nv_error(priv, "fuc09 req 0x16 timeout\n");
+                       return -EBUSY;
+               }
+
+               nv_wr32(priv, 0x409840, 0xffffffff);
+               nv_wr32(priv, 0x409500, 0x00000000);
+               nv_wr32(priv, 0x409504, 0x00000025);
+               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
+                       nv_error(priv, "fuc09 req 0x25 timeout\n");
+                       return -EBUSY;
+               }
+
+               if (nv_device(priv)->chipset >= 0xe0) {
+                       nv_wr32(priv, 0x409800, 0x00000000);
+                       nv_wr32(priv, 0x409500, 0x00000001);
+                       nv_wr32(priv, 0x409504, 0x00000030);
+                       if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
+                               nv_error(priv, "fuc09 req 0x30 timeout\n");
+                               return -EBUSY;
+                       }
+
+                       nv_wr32(priv, 0x409810, 0xb00095c8);
+                       nv_wr32(priv, 0x409800, 0x00000000);
+                       nv_wr32(priv, 0x409500, 0x00000001);
+                       nv_wr32(priv, 0x409504, 0x00000031);
+                       if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
+                               nv_error(priv, "fuc09 req 0x31 timeout\n");
+                               return -EBUSY;
+                       }
+
+                       nv_wr32(priv, 0x409810, 0x00080420);
+                       nv_wr32(priv, 0x409800, 0x00000000);
+                       nv_wr32(priv, 0x409500, 0x00000001);
+                       nv_wr32(priv, 0x409504, 0x00000032);
+                       if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
+                               nv_error(priv, "fuc09 req 0x32 timeout\n");
+                               return -EBUSY;
+                       }
+
+                       nv_wr32(priv, 0x409614, 0x00000070);
+                       nv_wr32(priv, 0x409614, 0x00000770);
+                       nv_wr32(priv, 0x40802c, 0x00000001);
+               }
+
+               if (priv->data == NULL) {
+                       int ret = nvc0_grctx_generate(priv);
+                       if (ret) {
+                               nv_error(priv, "failed to construct context\n");
+                               return ret;
+                       }
+               }
+
+               return 0;
        }
 
-       switch (nvc0_graph_class(priv)) {
-       case 0x9097:
-               nv_engine(priv)->sclass = nvc0_graph_sclass;
-               break;
-       case 0x9197:
-               nv_engine(priv)->sclass = nvc1_graph_sclass;
-               break;
-       case 0x9297:
-               nv_engine(priv)->sclass = nvc8_graph_sclass;
-               break;
+       /* load HUB microcode */
+       r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
+       nv_wr32(priv, 0x4091c0, 0x01000000);
+       for (i = 0; i < oclass->fecs.ucode->data.size / 4; i++)
+               nv_wr32(priv, 0x4091c4, oclass->fecs.ucode->data.data[i]);
+
+       nv_wr32(priv, 0x409180, 0x01000000);
+       for (i = 0; i < oclass->fecs.ucode->code.size / 4; i++) {
+               if ((i & 0x3f) == 0)
+                       nv_wr32(priv, 0x409188, i >> 6);
+               nv_wr32(priv, 0x409184, oclass->fecs.ucode->code.data[i]);
        }
 
-       ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0,
-                               &priv->unk4188b4);
-       if (ret)
-               return ret;
+       for (i = 0; (init = cclass->hub[i]); i++) {
+               nvc0_graph_init_csdata(priv, init, 0x409000, 0x000, 0x000000);
+       }
 
-       ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0,
-                               &priv->unk4188b8);
-       if (ret)
-               return ret;
+       /* load GPC microcode */
+       nv_wr32(priv, 0x41a1c0, 0x01000000);
+       for (i = 0; i < oclass->gpccs.ucode->data.size / 4; i++)
+               nv_wr32(priv, 0x41a1c4, oclass->gpccs.ucode->data.data[i]);
 
-       for (i = 0; i < 0x1000; i += 4) {
-               nv_wo32(priv->unk4188b4, i, 0x00000010);
-               nv_wo32(priv->unk4188b8, i, 0x00000010);
+       nv_wr32(priv, 0x41a180, 0x01000000);
+       for (i = 0; i < oclass->gpccs.ucode->code.size / 4; i++) {
+               if ((i & 0x3f) == 0)
+                       nv_wr32(priv, 0x41a188, i >> 6);
+               nv_wr32(priv, 0x41a184, oclass->gpccs.ucode->code.data[i]);
        }
+       nv_wr32(priv, 0x000260, r000260);
 
-       priv->rop_nr = (nv_rd32(priv, 0x409604) & 0x001f0000) >> 16;
-       priv->gpc_nr =  nv_rd32(priv, 0x409604) & 0x0000001f;
-       for (i = 0; i < priv->gpc_nr; i++) {
-               priv->tpc_nr[i]  = nv_rd32(priv, GPC_UNIT(i, 0x2608));
-               priv->tpc_total += priv->tpc_nr[i];
+       if ((init = cclass->gpc[0]))
+               nvc0_graph_init_csdata(priv, init, 0x41a000, 0x000, 0x418000);
+       if ((init = cclass->gpc[2]))
+               nvc0_graph_init_csdata(priv, init, 0x41a000, 0x004, 0x419800);
+       if ((init = cclass->gpc[3]))
+               nvc0_graph_init_csdata(priv, init, 0x41a000, 0x008, 0x41be00);
+
+       /* start HUB ucode running, it'll init the GPCs */
+       nv_wr32(priv, 0x40910c, 0x00000000);
+       nv_wr32(priv, 0x409100, 0x00000002);
+       if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) {
+               nv_error(priv, "HUB_INIT timed out\n");
+               nvc0_graph_ctxctl_debug(priv);
+               return -EBUSY;
        }
 
-       /*XXX: these need figuring out... though it might not even matter */
-       switch (nv_device(priv)->chipset) {
-       case 0xc0:
-               if (priv->tpc_total == 11) { /* 465, 3/4/4/0, 4 */
-                       priv->magic_not_rop_nr = 0x07;
-               } else
-               if (priv->tpc_total == 14) { /* 470, 3/3/4/4, 5 */
-                       priv->magic_not_rop_nr = 0x05;
-               } else
-               if (priv->tpc_total == 15) { /* 480, 3/4/4/4, 6 */
-                       priv->magic_not_rop_nr = 0x06;
+       priv->size = nv_rd32(priv, 0x409804);
+       if (priv->data == NULL) {
+               int ret = nvc0_grctx_generate(priv);
+               if (ret) {
+                       nv_error(priv, "failed to construct context\n");
+                       return ret;
                }
-               break;
-       case 0xc3: /* 450, 4/0/0/0, 2 */
-               priv->magic_not_rop_nr = 0x03;
-               break;
-       case 0xc4: /* 460, 3/4/0/0, 4 */
-               priv->magic_not_rop_nr = 0x01;
-               break;
-       case 0xc1: /* 2/0/0/0, 1 */
-               priv->magic_not_rop_nr = 0x01;
-               break;
-       case 0xc8: /* 4/4/3/4, 5 */
-               priv->magic_not_rop_nr = 0x06;
-               break;
-       case 0xce: /* 4/4/0/0, 4 */
-               priv->magic_not_rop_nr = 0x03;
-               break;
-       case 0xcf: /* 4/0/0/0, 3 */
-               priv->magic_not_rop_nr = 0x03;
-               break;
-       case 0xd9: /* 1/0/0/0, 1 */
-               priv->magic_not_rop_nr = 0x01;
-               break;
        }
 
        return 0;
 }
 
-static void
-nvc0_graph_dtor_fw(struct nvc0_graph_fuc *fuc)
-{
-       kfree(fuc->data);
-       fuc->data = NULL;
-}
-
-void
-nvc0_graph_dtor(struct nouveau_object *object)
+int
+nvc0_graph_init(struct nouveau_object *object)
 {
+       struct nvc0_graph_oclass *oclass = (void *)object->oclass;
        struct nvc0_graph_priv *priv = (void *)object;
+       const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total);
+       u32 data[TPC_MAX / 8] = {};
+       u8  tpcnr[GPC_MAX];
+       int gpc, tpc, rop;
+       int ret, i;
 
-       kfree(priv->data);
-
-       nvc0_graph_dtor_fw(&priv->fuc409c);
-       nvc0_graph_dtor_fw(&priv->fuc409d);
-       nvc0_graph_dtor_fw(&priv->fuc41ac);
-       nvc0_graph_dtor_fw(&priv->fuc41ad);
-
-       nouveau_gpuobj_ref(NULL, &priv->unk4188b8);
-       nouveau_gpuobj_ref(NULL, &priv->unk4188b4);
-
-       nouveau_graph_destroy(&priv->base);
-}
-
-static void
-nvc0_graph_init_obj418880(struct nvc0_graph_priv *priv)
-{
-       int i;
+       ret = nouveau_graph_init(&priv->base);
+       if (ret)
+               return ret;
 
        nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000);
        nv_wr32(priv, GPC_BCAST(0x08a4), 0x00000000);
-       for (i = 0; i < 4; i++)
-               nv_wr32(priv, GPC_BCAST(0x0888) + (i * 4), 0x00000000);
+       nv_wr32(priv, GPC_BCAST(0x0888), 0x00000000);
+       nv_wr32(priv, GPC_BCAST(0x088c), 0x00000000);
+       nv_wr32(priv, GPC_BCAST(0x0890), 0x00000000);
+       nv_wr32(priv, GPC_BCAST(0x0894), 0x00000000);
        nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8);
        nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8);
-}
 
-static void
-nvc0_graph_init_regs(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x400080, 0x003083c2);
-       nv_wr32(priv, 0x400088, 0x00006fe7);
-       nv_wr32(priv, 0x40008c, 0x00000000);
-       nv_wr32(priv, 0x400090, 0x00000030);
-       nv_wr32(priv, 0x40013c, 0x013901f7);
-       nv_wr32(priv, 0x400140, 0x00000100);
-       nv_wr32(priv, 0x400144, 0x00000000);
-       nv_wr32(priv, 0x400148, 0x00000110);
-       nv_wr32(priv, 0x400138, 0x00000000);
-       nv_wr32(priv, 0x400130, 0x00000000);
-       nv_wr32(priv, 0x400134, 0x00000000);
-       nv_wr32(priv, 0x400124, 0x00000002);
-}
-
-static void
-nvc0_graph_init_gpc_0(struct nvc0_graph_priv *priv)
-{
-       const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total);
-       u32 data[TPC_MAX / 8];
-       u8  tpcnr[GPC_MAX];
-       int i, gpc, tpc;
+       for (i = 0; oclass->mmio[i]; i++)
+               nvc0_graph_mmio(priv, oclass->mmio[i]);
 
-       nv_wr32(priv, TPC_UNIT(0, 0, 0x5c), 1); /* affects TFB offset queries */
-
-       /*
-        *      TP      ROP UNKVAL(magic_not_rop_nr)
-        * 450: 4/0/0/0 2        3
-        * 460: 3/4/0/0 4        1
-        * 465: 3/4/4/0 4        7
-        * 470: 3/3/4/4 5        5
-        * 480: 3/4/4/4 6        6
-        */
-
-       memset(data, 0x00, sizeof(data));
        memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
        for (i = 0, gpc = -1; i < priv->tpc_total; i++) {
                do {
@@ -720,36 +1004,36 @@ nvc0_graph_init_gpc_0(struct nvc0_graph_priv *priv)
        nv_wr32(priv, GPC_BCAST(0x098c), data[3]);
 
        for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
-               nv_wr32(priv, GPC_UNIT(gpc, 0x0914), priv->magic_not_rop_nr << 8 |
-                                                 priv->tpc_nr[gpc]);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | priv->tpc_total);
+               nv_wr32(priv, GPC_UNIT(gpc, 0x0914),
+                       priv->magic_not_rop_nr << 8 | priv->tpc_nr[gpc]);
+               nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 |
+                       priv->tpc_total);
                nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918);
        }
 
-       nv_wr32(priv, GPC_BCAST(0x1bd4), magicgpc918);
+       if (nv_device(priv)->chipset != 0xd7)
+               nv_wr32(priv, GPC_BCAST(0x1bd4), magicgpc918);
+       else
+               nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918);
+
        nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800));
-}
 
-static void
-nvc0_graph_init_units(struct nvc0_graph_priv *priv)
-{
+       nv_wr32(priv, 0x400500, 0x00010001);
+
+       nv_wr32(priv, 0x400100, 0xffffffff);
+       nv_wr32(priv, 0x40013c, 0xffffffff);
+
        nv_wr32(priv, 0x409c24, 0x000f0000);
-       nv_wr32(priv, 0x404000, 0xc0000000); /* DISPATCH */
-       nv_wr32(priv, 0x404600, 0xc0000000); /* M2MF */
+       nv_wr32(priv, 0x404000, 0xc0000000);
+       nv_wr32(priv, 0x404600, 0xc0000000);
        nv_wr32(priv, 0x408030, 0xc0000000);
        nv_wr32(priv, 0x40601c, 0xc0000000);
-       nv_wr32(priv, 0x404490, 0xc0000000); /* MACRO */
+       nv_wr32(priv, 0x404490, 0xc0000000);
        nv_wr32(priv, 0x406018, 0xc0000000);
        nv_wr32(priv, 0x405840, 0xc0000000);
        nv_wr32(priv, 0x405844, 0x00ffffff);
        nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008);
        nv_mask(priv, 0x419eb4, 0x00001000, 0x00001000);
-}
-
-static void
-nvc0_graph_init_gpc_1(struct nvc0_graph_priv *priv)
-{
-       int gpc, tpc;
 
        for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
                nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000);
@@ -768,12 +1052,6 @@ nvc0_graph_init_gpc_1(struct nvc0_graph_priv *priv)
                nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff);
                nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff);
        }
-}
-
-static void
-nvc0_graph_init_rop(struct nvc0_graph_priv *priv)
-{
-       int rop;
 
        for (rop = 0; rop < priv->rop_nr; rop++) {
                nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000);
@@ -781,184 +1059,212 @@ nvc0_graph_init_rop(struct nvc0_graph_priv *priv)
                nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff);
                nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff);
        }
+
+       nv_wr32(priv, 0x400108, 0xffffffff);
+       nv_wr32(priv, 0x400138, 0xffffffff);
+       nv_wr32(priv, 0x400118, 0xffffffff);
+       nv_wr32(priv, 0x400130, 0xffffffff);
+       nv_wr32(priv, 0x40011c, 0xffffffff);
+       nv_wr32(priv, 0x400134, 0xffffffff);
+
+       nv_wr32(priv, 0x400054, 0x34ce3464);
+       return nvc0_graph_init_ctxctl(priv);
 }
 
-void
-nvc0_graph_init_fw(struct nvc0_graph_priv *priv, u32 fuc_base,
-                  struct nvc0_graph_fuc *code, struct nvc0_graph_fuc *data)
+static void
+nvc0_graph_dtor_fw(struct nvc0_graph_fuc *fuc)
 {
-       int i;
+       kfree(fuc->data);
+       fuc->data = NULL;
+}
 
-       nv_wr32(priv, fuc_base + 0x01c0, 0x01000000);
-       for (i = 0; i < data->size / 4; i++)
-               nv_wr32(priv, fuc_base + 0x01c4, data->data[i]);
+int
+nvc0_graph_ctor_fw(struct nvc0_graph_priv *priv, const char *fwname,
+                  struct nvc0_graph_fuc *fuc)
+{
+       struct nouveau_device *device = nv_device(priv);
+       const struct firmware *fw;
+       char f[32];
+       int ret;
 
-       nv_wr32(priv, fuc_base + 0x0180, 0x01000000);
-       for (i = 0; i < code->size / 4; i++) {
-               if ((i & 0x3f) == 0)
-                       nv_wr32(priv, fuc_base + 0x0188, i >> 6);
-               nv_wr32(priv, fuc_base + 0x0184, code->data[i]);
+       snprintf(f, sizeof(f), "nouveau/nv%02x_%s", device->chipset, fwname);
+       ret = request_firmware(&fw, f, &device->pdev->dev);
+       if (ret) {
+               snprintf(f, sizeof(f), "nouveau/%s", fwname);
+               ret = request_firmware(&fw, f, &device->pdev->dev);
+               if (ret) {
+                       nv_error(priv, "failed to load %s\n", fwname);
+                       return ret;
+               }
        }
+
+       fuc->size = fw->size;
+       fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL);
+       release_firmware(fw);
+       return (fuc->data != NULL) ? 0 : -ENOMEM;
 }
 
-static int
-nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv)
+void
+nvc0_graph_dtor(struct nouveau_object *object)
 {
-       u32 r000260;
-       int i;
-
-       if (priv->firmware) {
-               /* load fuc microcode */
-               r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
-               nvc0_graph_init_fw(priv, 0x409000, &priv->fuc409c,
-                                                  &priv->fuc409d);
-               nvc0_graph_init_fw(priv, 0x41a000, &priv->fuc41ac,
-                                                  &priv->fuc41ad);
-               nv_wr32(priv, 0x000260, r000260);
+       struct nvc0_graph_priv *priv = (void *)object;
 
-               /* start both of them running */
-               nv_wr32(priv, 0x409840, 0xffffffff);
-               nv_wr32(priv, 0x41a10c, 0x00000000);
-               nv_wr32(priv, 0x40910c, 0x00000000);
-               nv_wr32(priv, 0x41a100, 0x00000002);
-               nv_wr32(priv, 0x409100, 0x00000002);
-               if (!nv_wait(priv, 0x409800, 0x00000001, 0x00000001))
-                       nv_warn(priv, "0x409800 wait failed\n");
+       kfree(priv->data);
 
-               nv_wr32(priv, 0x409840, 0xffffffff);
-               nv_wr32(priv, 0x409500, 0x7fffffff);
-               nv_wr32(priv, 0x409504, 0x00000021);
+       nvc0_graph_dtor_fw(&priv->fuc409c);
+       nvc0_graph_dtor_fw(&priv->fuc409d);
+       nvc0_graph_dtor_fw(&priv->fuc41ac);
+       nvc0_graph_dtor_fw(&priv->fuc41ad);
 
-               nv_wr32(priv, 0x409840, 0xffffffff);
-               nv_wr32(priv, 0x409500, 0x00000000);
-               nv_wr32(priv, 0x409504, 0x00000010);
-               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
-                       nv_error(priv, "fuc09 req 0x10 timeout\n");
-                       return -EBUSY;
-               }
-               priv->size = nv_rd32(priv, 0x409800);
+       nouveau_gpuobj_ref(NULL, &priv->unk4188b8);
+       nouveau_gpuobj_ref(NULL, &priv->unk4188b4);
 
-               nv_wr32(priv, 0x409840, 0xffffffff);
-               nv_wr32(priv, 0x409500, 0x00000000);
-               nv_wr32(priv, 0x409504, 0x00000016);
-               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
-                       nv_error(priv, "fuc09 req 0x16 timeout\n");
-                       return -EBUSY;
-               }
+       nouveau_graph_destroy(&priv->base);
+}
 
-               nv_wr32(priv, 0x409840, 0xffffffff);
-               nv_wr32(priv, 0x409500, 0x00000000);
-               nv_wr32(priv, 0x409504, 0x00000025);
-               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
-                       nv_error(priv, "fuc09 req 0x25 timeout\n");
-                       return -EBUSY;
-               }
+int
+nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+               struct nouveau_oclass *bclass, void *data, u32 size,
+               struct nouveau_object **pobject)
+{
+       struct nvc0_graph_oclass *oclass = (void *)bclass;
+       struct nouveau_device *device = nv_device(parent);
+       struct nvc0_graph_priv *priv;
+       int ret, i;
 
-               if (priv->data == NULL) {
-                       int ret = nvc0_grctx_generate(priv);
-                       if (ret) {
-                               nv_error(priv, "failed to construct context\n");
-                               return ret;
-                       }
-               }
+       ret = nouveau_graph_create(parent, engine, bclass,
+                                  (oclass->fecs.ucode != NULL), &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
 
-               return 0;
-       }
+       nv_subdev(priv)->unit = 0x18001000;
+       nv_subdev(priv)->intr = nvc0_graph_intr;
 
-       /* load HUB microcode */
-       r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
-       nv_wr32(priv, 0x4091c0, 0x01000000);
-       for (i = 0; i < sizeof(nvc0_grhub_data) / 4; i++)
-               nv_wr32(priv, 0x4091c4, nvc0_grhub_data[i]);
+       priv->base.units = nvc0_graph_units;
 
-       nv_wr32(priv, 0x409180, 0x01000000);
-       for (i = 0; i < sizeof(nvc0_grhub_code) / 4; i++) {
-               if ((i & 0x3f) == 0)
-                       nv_wr32(priv, 0x409188, i >> 6);
-               nv_wr32(priv, 0x409184, nvc0_grhub_code[i]);
+       if (nouveau_boolopt(device->cfgopt, "NvGrUseFW", false)) {
+               nv_info(priv, "using external firmware\n");
+               if (nvc0_graph_ctor_fw(priv, "fuc409c", &priv->fuc409c) ||
+                   nvc0_graph_ctor_fw(priv, "fuc409d", &priv->fuc409d) ||
+                   nvc0_graph_ctor_fw(priv, "fuc41ac", &priv->fuc41ac) ||
+                   nvc0_graph_ctor_fw(priv, "fuc41ad", &priv->fuc41ad))
+                       return -EINVAL;
+               priv->firmware = true;
        }
 
-       /* load GPC microcode */
-       nv_wr32(priv, 0x41a1c0, 0x01000000);
-       for (i = 0; i < sizeof(nvc0_grgpc_data) / 4; i++)
-               nv_wr32(priv, 0x41a1c4, nvc0_grgpc_data[i]);
+       ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0,
+                               &priv->unk4188b4);
+       if (ret)
+               return ret;
 
-       nv_wr32(priv, 0x41a180, 0x01000000);
-       for (i = 0; i < sizeof(nvc0_grgpc_code) / 4; i++) {
-               if ((i & 0x3f) == 0)
-                       nv_wr32(priv, 0x41a188, i >> 6);
-               nv_wr32(priv, 0x41a184, nvc0_grgpc_code[i]);
+       ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0,
+                               &priv->unk4188b8);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < 0x1000; i += 4) {
+               nv_wo32(priv->unk4188b4, i, 0x00000010);
+               nv_wo32(priv->unk4188b8, i, 0x00000010);
        }
-       nv_wr32(priv, 0x000260, r000260);
 
-       /* start HUB ucode running, it'll init the GPCs */
-       nv_wr32(priv, 0x409800, nv_device(priv)->chipset);
-       nv_wr32(priv, 0x40910c, 0x00000000);
-       nv_wr32(priv, 0x409100, 0x00000002);
-       if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) {
-               nv_error(priv, "HUB_INIT timed out\n");
-               nvc0_graph_ctxctl_debug(priv);
-               return -EBUSY;
+       priv->rop_nr = (nv_rd32(priv, 0x409604) & 0x001f0000) >> 16;
+       priv->gpc_nr =  nv_rd32(priv, 0x409604) & 0x0000001f;
+       for (i = 0; i < priv->gpc_nr; i++) {
+               priv->tpc_nr[i]  = nv_rd32(priv, GPC_UNIT(i, 0x2608));
+               priv->tpc_total += priv->tpc_nr[i];
        }
 
-       priv->size = nv_rd32(priv, 0x409804);
-       if (priv->data == NULL) {
-               int ret = nvc0_grctx_generate(priv);
-               if (ret) {
-                       nv_error(priv, "failed to construct context\n");
-                       return ret;
+       /*XXX: these need figuring out... though it might not even matter */
+       switch (nv_device(priv)->chipset) {
+       case 0xc0:
+               if (priv->tpc_total == 11) { /* 465, 3/4/4/0, 4 */
+                       priv->magic_not_rop_nr = 0x07;
+               } else
+               if (priv->tpc_total == 14) { /* 470, 3/3/4/4, 5 */
+                       priv->magic_not_rop_nr = 0x05;
+               } else
+               if (priv->tpc_total == 15) { /* 480, 3/4/4/4, 6 */
+                       priv->magic_not_rop_nr = 0x06;
                }
+               break;
+       case 0xc3: /* 450, 4/0/0/0, 2 */
+               priv->magic_not_rop_nr = 0x03;
+               break;
+       case 0xc4: /* 460, 3/4/0/0, 4 */
+               priv->magic_not_rop_nr = 0x01;
+               break;
+       case 0xc1: /* 2/0/0/0, 1 */
+               priv->magic_not_rop_nr = 0x01;
+               break;
+       case 0xc8: /* 4/4/3/4, 5 */
+               priv->magic_not_rop_nr = 0x06;
+               break;
+       case 0xce: /* 4/4/0/0, 4 */
+               priv->magic_not_rop_nr = 0x03;
+               break;
+       case 0xcf: /* 4/0/0/0, 3 */
+               priv->magic_not_rop_nr = 0x03;
+               break;
+       case 0xd7:
+       case 0xd9: /* 1/0/0/0, 1 */
+               priv->magic_not_rop_nr = 0x01;
+               break;
        }
 
+       nv_engine(priv)->cclass = *oclass->cclass;
+       nv_engine(priv)->sclass =  oclass->sclass;
        return 0;
 }
 
-static int
-nvc0_graph_init(struct nouveau_object *object)
-{
-       struct nvc0_graph_priv *priv = (void *)object;
-       int ret;
-
-       ret = nouveau_graph_init(&priv->base);
-       if (ret)
-               return ret;
-
-       nvc0_graph_init_obj418880(priv);
-       nvc0_graph_init_regs(priv);
-       /*nvc0_graph_init_unitplemented_magics(priv);*/
-       nvc0_graph_init_gpc_0(priv);
-       /*nvc0_graph_init_unitplemented_c242(priv);*/
-
-       nv_wr32(priv, 0x400500, 0x00010001);
-       nv_wr32(priv, 0x400100, 0xffffffff);
-       nv_wr32(priv, 0x40013c, 0xffffffff);
+struct nvc0_graph_init *
+nvc0_graph_init_mmio[] = {
+       nvc0_graph_init_regs,
+       nvc0_graph_init_unk40xx,
+       nvc0_graph_init_unk44xx,
+       nvc0_graph_init_unk78xx,
+       nvc0_graph_init_unk60xx,
+       nvc0_graph_init_unk58xx,
+       nvc0_graph_init_unk80xx,
+       nvc0_graph_init_gpc,
+       nvc0_graph_init_tpc,
+       nvc0_graph_init_unk88xx,
+       nvc0_graph_tpc_0,
+       NULL
+};
 
-       nvc0_graph_init_units(priv);
-       nvc0_graph_init_gpc_1(priv);
-       nvc0_graph_init_rop(priv);
+#include "fuc/hubnvc0.fuc.h"
 
-       nv_wr32(priv, 0x400108, 0xffffffff);
-       nv_wr32(priv, 0x400138, 0xffffffff);
-       nv_wr32(priv, 0x400118, 0xffffffff);
-       nv_wr32(priv, 0x400130, 0xffffffff);
-       nv_wr32(priv, 0x40011c, 0xffffffff);
-       nv_wr32(priv, 0x400134, 0xffffffff);
-       nv_wr32(priv, 0x400054, 0x34ce3464);
+struct nvc0_graph_ucode
+nvc0_graph_fecs_ucode = {
+       .code.data = nvc0_grhub_code,
+       .code.size = sizeof(nvc0_grhub_code),
+       .data.data = nvc0_grhub_data,
+       .data.size = sizeof(nvc0_grhub_data),
+};
 
-       ret = nvc0_graph_init_ctxctl(priv);
-       if (ret)
-               return ret;
+#include "fuc/gpcnvc0.fuc.h"
 
-       return 0;
-}
+struct nvc0_graph_ucode
+nvc0_graph_gpccs_ucode = {
+       .code.data = nvc0_grgpc_code,
+       .code.size = sizeof(nvc0_grgpc_code),
+       .data.data = nvc0_grgpc_data,
+       .data.size = sizeof(nvc0_grgpc_data),
+};
 
-struct nouveau_oclass
-nvc0_graph_oclass = {
-       .handle = NV_ENGINE(GR, 0xc0),
-       .ofuncs = &(struct nouveau_ofuncs) {
+struct nouveau_oclass *
+nvc0_graph_oclass = &(struct nvc0_graph_oclass) {
+       .base.handle = NV_ENGINE(GR, 0xc0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nvc0_graph_ctor,
                .dtor = nvc0_graph_dtor,
                .init = nvc0_graph_init,
                .fini = _nouveau_graph_fini,
        },
-};
+       .cclass = &nvc0_grctx_oclass,
+       .sclass =  nvc0_graph_sclass,
+       .mmio = nvc0_graph_init_mmio,
+       .fecs.ucode = &nvc0_graph_fecs_ucode,
+       .gpccs.ucode = &nvc0_graph_gpccs_ucode,
+}.base;
index c870dad..ea17a80 100644 (file)
@@ -38,8 +38,8 @@
 #include <engine/fifo.h>
 #include <engine/graph.h>
 
-#define GPC_MAX 4
-#define TPC_MAX 32
+#define GPC_MAX 32
+#define TPC_MAX (GPC_MAX * 8)
 
 #define ROP_BCAST(r)      (0x408800 + (r))
 #define ROP_UNIT(u, r)    (0x410000 + (u) * 0x400 + (r))
@@ -102,74 +102,187 @@ struct nvc0_graph_chan {
        } data[4];
 };
 
-static inline u32
-nvc0_graph_class(void *obj)
-{
-       struct nouveau_device *device = nv_device(obj);
-
-       switch (device->chipset) {
-       case 0xc0:
-       case 0xc3:
-       case 0xc4:
-       case 0xce: /* guess, mmio trace shows only 0x9097 state */
-       case 0xcf: /* guess, mmio trace shows only 0x9097 state */
-               return 0x9097;
-       case 0xc1:
-               return 0x9197;
-       case 0xc8:
-       case 0xd9:
-       case 0xd7:
-               return 0x9297;
-       case 0xe4:
-       case 0xe7:
-       case 0xe6:
-               return 0xa097;
-       default:
-               return 0;
-       }
-}
-
-void nv_icmd(struct nvc0_graph_priv *priv, u32 icmd, u32 data);
-
-static inline void
-nv_mthd(struct nvc0_graph_priv *priv, u32 class, u32 mthd, u32 data)
-{
-       nv_wr32(priv, 0x40448c, data);
-       nv_wr32(priv, 0x404488, 0x80000000 | (mthd << 14) | class);
-}
+int  nvc0_grctx_generate(struct nvc0_graph_priv *);
+
+int  nvc0_graph_context_ctor(struct nouveau_object *, struct nouveau_object *,
+                            struct nouveau_oclass *, void *, u32,
+                            struct nouveau_object **);
+void nvc0_graph_context_dtor(struct nouveau_object *);
+
+void nvc0_graph_ctxctl_debug(struct nvc0_graph_priv *);
+
+u64  nvc0_graph_units(struct nouveau_graph *);
+int  nvc0_graph_ctor(struct nouveau_object *, struct nouveau_object *,
+                    struct nouveau_oclass *, void *data, u32 size,
+                    struct nouveau_object **);
+void nvc0_graph_dtor(struct nouveau_object *);
+int  nvc0_graph_init(struct nouveau_object *);
+int  nve4_graph_init(struct nouveau_object *);
+
+extern struct nouveau_oclass nvc0_graph_sclass[];
+
+extern struct nouveau_oclass nvc8_graph_sclass[];
+
+struct nvc0_graph_init {
+       u32 addr;
+       u8  count;
+       u8  pitch;
+       u32 data;
+};
+
+struct nvc0_graph_mthd {
+       u16 oclass;
+       struct nvc0_graph_init *init;
+};
 
 struct nvc0_grctx {
        struct nvc0_graph_priv *priv;
        struct nvc0_graph_data *data;
        struct nvc0_graph_mmio *mmio;
-       struct nouveau_gpuobj *chan;
        int buffer_nr;
        u64 buffer[4];
        u64 addr;
 };
 
+struct nvc0_grctx_oclass {
+       struct nouveau_oclass base;
+       /* main context generation function */
+       void  (*main)(struct nvc0_graph_priv *, struct nvc0_grctx *);
+       /* context-specific modify-on-first-load list generation function */
+       void  (*mods)(struct nvc0_graph_priv *, struct nvc0_grctx *);
+       void  (*unkn)(struct nvc0_graph_priv *);
+       /* mmio context data */
+       struct nvc0_graph_init **hub;
+       struct nvc0_graph_init **gpc;
+       /* indirect context data, generated with icmds/mthds */
+       struct nvc0_graph_init *icmd;
+       struct nvc0_graph_mthd *mthd;
+};
+
+struct nvc0_graph_ucode {
+       struct nvc0_graph_fuc code;
+       struct nvc0_graph_fuc data;
+};
+
+extern struct nvc0_graph_ucode nvc0_graph_fecs_ucode;
+extern struct nvc0_graph_ucode nvc0_graph_gpccs_ucode;
+
+struct nvc0_graph_oclass {
+       struct nouveau_oclass base;
+       struct nouveau_oclass **cclass;
+       struct nouveau_oclass *sclass;
+       struct nvc0_graph_init **mmio;
+       struct {
+               struct nvc0_graph_ucode *ucode;
+       } fecs;
+       struct {
+               struct nvc0_graph_ucode *ucode;
+       } gpccs;
+};
+
+void nvc0_graph_mmio(struct nvc0_graph_priv *, struct nvc0_graph_init *);
+void nvc0_graph_icmd(struct nvc0_graph_priv *, struct nvc0_graph_init *);
+void nvc0_graph_mthd(struct nvc0_graph_priv *, struct nvc0_graph_mthd *);
+int  nvc0_graph_init_ctxctl(struct nvc0_graph_priv *);
+
+extern struct nvc0_graph_init nvc0_graph_init_regs[];
+extern struct nvc0_graph_init nvc0_graph_init_unk40xx[];
+extern struct nvc0_graph_init nvc0_graph_init_unk44xx[];
+extern struct nvc0_graph_init nvc0_graph_init_unk78xx[];
+extern struct nvc0_graph_init nvc0_graph_init_unk60xx[];
+extern struct nvc0_graph_init nvc0_graph_init_unk58xx[];
+extern struct nvc0_graph_init nvc0_graph_init_unk80xx[];
+extern struct nvc0_graph_init nvc0_graph_init_gpc[];
+extern struct nvc0_graph_init nvc0_graph_init_unk88xx[];
+extern struct nvc0_graph_init nvc0_graph_tpc_0[];
+
+extern struct nvc0_graph_init nvc3_graph_init_unk58xx[];
+
+extern struct nvc0_graph_init nvd9_graph_init_unk58xx[];
+extern struct nvc0_graph_init nvd9_graph_init_unk64xx[];
+
+extern struct nvc0_graph_init nve4_graph_init_regs[];
+extern struct nvc0_graph_init nve4_graph_init_unk[];
+extern struct nvc0_graph_init nve4_graph_init_unk88xx[];
+
 int  nvc0_grctx_generate(struct nvc0_graph_priv *);
-int  nvc0_grctx_init(struct nvc0_graph_priv *, struct nvc0_grctx *);
-void nvc0_grctx_data(struct nvc0_grctx *, u32, u32, u32);
-void nvc0_grctx_mmio(struct nvc0_grctx *, u32, u32, u32, u32);
-int  nvc0_grctx_fini(struct nvc0_grctx *);
+void nvc0_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nvc0_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nvc0_grctx_generate_unkn(struct nvc0_graph_priv *);
+void nvc0_grctx_generate_tpcid(struct nvc0_graph_priv *);
+void nvc0_grctx_generate_r406028(struct nvc0_graph_priv *);
+void nvc0_grctx_generate_r4060a8(struct nvc0_graph_priv *);
+void nvc0_grctx_generate_r418bb8(struct nvc0_graph_priv *);
+void nve4_grctx_generate_r418bb8(struct nvc0_graph_priv *);
+void nvc0_grctx_generate_r406800(struct nvc0_graph_priv *);
 
-int  nve0_grctx_generate(struct nvc0_graph_priv *);
+extern struct nouveau_oclass *nvc0_grctx_oclass;
+extern struct nvc0_graph_init *nvc0_grctx_init_hub[];
+extern struct nvc0_graph_init nvc0_grctx_init_base[];
+extern struct nvc0_graph_init nvc0_grctx_init_unk40xx[];
+extern struct nvc0_graph_init nvc0_grctx_init_unk44xx[];
+extern struct nvc0_graph_init nvc0_grctx_init_unk46xx[];
+extern struct nvc0_graph_init nvc0_grctx_init_unk47xx[];
+extern struct nvc0_graph_init nvc0_grctx_init_unk60xx[];
+extern struct nvc0_graph_init nvc0_grctx_init_unk64xx[];
+extern struct nvc0_graph_init nvc0_grctx_init_unk78xx[];
+extern struct nvc0_graph_init nvc0_grctx_init_unk80xx[];
+extern struct nvc0_graph_init nvc0_grctx_init_gpc_0[];
+extern struct nvc0_graph_init nvc0_grctx_init_gpc_1[];
+extern struct nvc0_graph_init nvc0_grctx_init_tpc[];
+extern struct nvc0_graph_init nvc0_grctx_init_icmd[];
+extern struct nvc0_graph_init nvd9_grctx_init_icmd[]; //
 
-#define mmio_data(s,a,p) nvc0_grctx_data(&info, (s), (a), (p))
-#define mmio_list(r,d,s,b) nvc0_grctx_mmio(&info, (r), (d), (s), (b))
+extern struct nvc0_graph_mthd nvc0_grctx_init_mthd[];
+extern struct nvc0_graph_init nvc0_grctx_init_902d[];
+extern struct nvc0_graph_init nvc0_grctx_init_9039[];
+extern struct nvc0_graph_init nvc0_grctx_init_90c0[];
+extern struct nvc0_graph_init nvc0_grctx_init_mthd_magic[];
 
-void nvc0_graph_ctxctl_debug(struct nvc0_graph_priv *);
-int  nvc0_graph_ctor_fw(struct nvc0_graph_priv *, const char *,
-                       struct nvc0_graph_fuc *);
-void nvc0_graph_dtor(struct nouveau_object *);
-void nvc0_graph_init_fw(struct nvc0_graph_priv *, u32 base,
-                       struct nvc0_graph_fuc *, struct nvc0_graph_fuc *);
-int  nvc0_graph_context_ctor(struct nouveau_object *, struct nouveau_object *,
-                            struct nouveau_oclass *, void *, u32,
-                            struct nouveau_object **);
-void nvc0_graph_context_dtor(struct nouveau_object *);
+void nvc1_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nvc1_grctx_generate_unkn(struct nvc0_graph_priv *);
+extern struct nouveau_oclass *nvc1_grctx_oclass;
+extern struct nvc0_graph_init nvc1_grctx_init_9097[];
+
+extern struct nouveau_oclass *nvc3_grctx_oclass;
+
+extern struct nouveau_oclass *nvc8_grctx_oclass;
+extern struct nvc0_graph_init nvc8_grctx_init_9197[];
+extern struct nvc0_graph_init nvc8_grctx_init_9297[];
+
+extern struct nouveau_oclass *nvd7_grctx_oclass;
+
+extern struct nouveau_oclass *nvd9_grctx_oclass;
+extern struct nvc0_graph_init nvd9_grctx_init_rop[];
+extern struct nvc0_graph_mthd nvd9_grctx_init_mthd[];
+
+void nve4_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nve4_grctx_generate_unkn(struct nvc0_graph_priv *);
+extern struct nouveau_oclass *nve4_grctx_oclass;
+extern struct nvc0_graph_init nve4_grctx_init_unk46xx[];
+extern struct nvc0_graph_init nve4_grctx_init_unk47xx[];
+extern struct nvc0_graph_init nve4_grctx_init_unk58xx[];
+extern struct nvc0_graph_init nve4_grctx_init_unk80xx[];
+extern struct nvc0_graph_init nve4_grctx_init_unk90xx[];
+
+extern struct nouveau_oclass *nvf0_grctx_oclass;
+
+#define mmio_data(s,a,p) do {                                                  \
+       info->buffer[info->buffer_nr] = round_up(info->addr, (a));             \
+       info->addr = info->buffer[info->buffer_nr++] + (s);                    \
+       info->data->size = (s);                                                \
+       info->data->align = (a);                                               \
+       info->data->access = (p);                                              \
+       info->data++;                                                          \
+} while(0)
 
-u64 nvc0_graph_units(struct nouveau_graph *);
+#define mmio_list(r,d,s,b) do {                                                \
+       info->mmio->addr = (r);                                                \
+       info->mmio->data = (d);                                                \
+       info->mmio->shift = (s);                                               \
+       info->mmio->buffer = (b);                                              \
+       info->mmio++;                                                          \
+       nv_wr32(priv, (r), (d) | ((s) ? (info->buffer[(b)] >> (s)) : 0));      \
+} while(0)
 
 #endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c
new file mode 100644 (file)
index 0000000..bc4a469
--- /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 "nvc0.h"
+
+/*******************************************************************************
+ * Graphics object classes
+ ******************************************************************************/
+
+static struct nouveau_oclass
+nvc1_graph_sclass[] = {
+       { 0x902d, &nouveau_object_ofuncs },
+       { 0x9039, &nouveau_object_ofuncs },
+       { 0x9097, &nouveau_object_ofuncs },
+       { 0x90c0, &nouveau_object_ofuncs },
+       { 0x9197, &nouveau_object_ofuncs },
+       {}
+};
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
+static struct nvc0_graph_init
+nvc1_graph_init_gpc[] = {
+       { 0x4184a0,   1, 0x04, 0x00000000 },
+       { 0x418604,   1, 0x04, 0x00000000 },
+       { 0x418680,   1, 0x04, 0x00000000 },
+       { 0x418714,   1, 0x04, 0x00000000 },
+       { 0x418384,   1, 0x04, 0x00000000 },
+       { 0x418814,   3, 0x04, 0x00000000 },
+       { 0x418b04,   1, 0x04, 0x00000000 },
+       { 0x4188c8,   2, 0x04, 0x00000000 },
+       { 0x4188d0,   1, 0x04, 0x00010000 },
+       { 0x4188d4,   1, 0x04, 0x00000001 },
+       { 0x418910,   1, 0x04, 0x00010001 },
+       { 0x418914,   1, 0x04, 0x00000301 },
+       { 0x418918,   1, 0x04, 0x00800000 },
+       { 0x418980,   1, 0x04, 0x77777770 },
+       { 0x418984,   3, 0x04, 0x77777777 },
+       { 0x418c04,   1, 0x04, 0x00000000 },
+       { 0x418c88,   1, 0x04, 0x00000000 },
+       { 0x418d00,   1, 0x04, 0x00000000 },
+       { 0x418f08,   1, 0x04, 0x00000000 },
+       { 0x418e00,   1, 0x04, 0x00000003 },
+       { 0x418e08,   1, 0x04, 0x00000000 },
+       { 0x41900c,   1, 0x04, 0x00000000 },
+       { 0x419018,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvc1_graph_init_tpc[] = {
+       { 0x419d08,   2, 0x04, 0x00000000 },
+       { 0x419d10,   1, 0x04, 0x00000014 },
+       { 0x419ab0,   1, 0x04, 0x00000000 },
+       { 0x419ac8,   1, 0x04, 0x00000000 },
+       { 0x419ab8,   1, 0x04, 0x000000e7 },
+       { 0x419abc,   2, 0x04, 0x00000000 },
+       { 0x41980c,   2, 0x04, 0x00000000 },
+       { 0x419814,   1, 0x04, 0x00000004 },
+       { 0x419844,   1, 0x04, 0x00000000 },
+       { 0x41984c,   1, 0x04, 0x00005bc5 },
+       { 0x419850,   4, 0x04, 0x00000000 },
+       { 0x419880,   1, 0x04, 0x00000002 },
+       { 0x419c98,   1, 0x04, 0x00000000 },
+       { 0x419ca8,   1, 0x04, 0x80000000 },
+       { 0x419cb4,   1, 0x04, 0x00000000 },
+       { 0x419cb8,   1, 0x04, 0x00008bf4 },
+       { 0x419cbc,   1, 0x04, 0x28137606 },
+       { 0x419cc0,   2, 0x04, 0x00000000 },
+       { 0x419bd4,   1, 0x04, 0x00800000 },
+       { 0x419bdc,   1, 0x04, 0x00000000 },
+       { 0x419d2c,   1, 0x04, 0x00000000 },
+       { 0x419c0c,   1, 0x04, 0x00000000 },
+       { 0x419e00,   1, 0x04, 0x00000000 },
+       { 0x419ea0,   1, 0x04, 0x00000000 },
+       { 0x419ea4,   1, 0x04, 0x00000100 },
+       { 0x419ea8,   1, 0x04, 0x00001100 },
+       { 0x419eac,   1, 0x04, 0x11100702 },
+       { 0x419eb0,   1, 0x04, 0x00000003 },
+       { 0x419eb4,   4, 0x04, 0x00000000 },
+       { 0x419ec8,   1, 0x04, 0x0e063818 },
+       { 0x419ecc,   1, 0x04, 0x0e060e06 },
+       { 0x419ed0,   1, 0x04, 0x00003818 },
+       { 0x419ed4,   1, 0x04, 0x011104f1 },
+       { 0x419edc,   1, 0x04, 0x00000000 },
+       { 0x419f00,   1, 0x04, 0x00000000 },
+       { 0x419f2c,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init *
+nvc1_graph_init_mmio[] = {
+       nvc0_graph_init_regs,
+       nvc0_graph_init_unk40xx,
+       nvc0_graph_init_unk44xx,
+       nvc0_graph_init_unk78xx,
+       nvc0_graph_init_unk60xx,
+       nvc3_graph_init_unk58xx,
+       nvc0_graph_init_unk80xx,
+       nvc1_graph_init_gpc,
+       nvc1_graph_init_tpc,
+       nvc0_graph_init_unk88xx,
+       nvc0_graph_tpc_0,
+       NULL
+};
+
+struct nouveau_oclass *
+nvc1_graph_oclass = &(struct nvc0_graph_oclass) {
+       .base.handle = NV_ENGINE(GR, 0xc1),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_ctor,
+               .dtor = nvc0_graph_dtor,
+               .init = nvc0_graph_init,
+               .fini = _nouveau_graph_fini,
+       },
+       .cclass = &nvc1_grctx_oclass,
+       .sclass = nvc1_graph_sclass,
+       .mmio = nvc1_graph_init_mmio,
+       .fecs.ucode = &nvc0_graph_fecs_ucode,
+       .gpccs.ucode = &nvc0_graph_gpccs_ucode,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c
new file mode 100644 (file)
index 0000000..d44b3b3
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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 "nvc0.h"
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
+struct nvc0_graph_init
+nvc3_graph_init_unk58xx[] = {
+       { 0x405844,   1, 0x04, 0x00ffffff },
+       { 0x405850,   1, 0x04, 0x00000000 },
+       { 0x405900,   1, 0x04, 0x00002834 },
+       { 0x405908,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvc3_graph_init_tpc[] = {
+       { 0x419d08,   2, 0x04, 0x00000000 },
+       { 0x419d10,   1, 0x04, 0x00000014 },
+       { 0x419ab0,   1, 0x04, 0x00000000 },
+       { 0x419ac8,   1, 0x04, 0x00000000 },
+       { 0x419ab8,   1, 0x04, 0x000000e7 },
+       { 0x419abc,   2, 0x04, 0x00000000 },
+       { 0x41980c,   3, 0x04, 0x00000000 },
+       { 0x419844,   1, 0x04, 0x00000000 },
+       { 0x41984c,   1, 0x04, 0x00005bc5 },
+       { 0x419850,   4, 0x04, 0x00000000 },
+       { 0x419880,   1, 0x04, 0x00000002 },
+       { 0x419c98,   1, 0x04, 0x00000000 },
+       { 0x419ca8,   1, 0x04, 0x80000000 },
+       { 0x419cb4,   1, 0x04, 0x00000000 },
+       { 0x419cb8,   1, 0x04, 0x00008bf4 },
+       { 0x419cbc,   1, 0x04, 0x28137606 },
+       { 0x419cc0,   2, 0x04, 0x00000000 },
+       { 0x419bd4,   1, 0x04, 0x00800000 },
+       { 0x419bdc,   1, 0x04, 0x00000000 },
+       { 0x419d2c,   1, 0x04, 0x00000000 },
+       { 0x419c0c,   1, 0x04, 0x00000000 },
+       { 0x419e00,   1, 0x04, 0x00000000 },
+       { 0x419ea0,   1, 0x04, 0x00000000 },
+       { 0x419ea4,   1, 0x04, 0x00000100 },
+       { 0x419ea8,   1, 0x04, 0x00001100 },
+       { 0x419eac,   1, 0x04, 0x11100702 },
+       { 0x419eb0,   1, 0x04, 0x00000003 },
+       { 0x419eb4,   4, 0x04, 0x00000000 },
+       { 0x419ec8,   1, 0x04, 0x0e063818 },
+       { 0x419ecc,   1, 0x04, 0x0e060e06 },
+       { 0x419ed0,   1, 0x04, 0x00003818 },
+       { 0x419ed4,   1, 0x04, 0x011104f1 },
+       { 0x419edc,   1, 0x04, 0x00000000 },
+       { 0x419f00,   1, 0x04, 0x00000000 },
+       { 0x419f2c,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init *
+nvc3_graph_init_mmio[] = {
+       nvc0_graph_init_regs,
+       nvc0_graph_init_unk40xx,
+       nvc0_graph_init_unk44xx,
+       nvc0_graph_init_unk78xx,
+       nvc0_graph_init_unk60xx,
+       nvc3_graph_init_unk58xx,
+       nvc0_graph_init_unk80xx,
+       nvc0_graph_init_gpc,
+       nvc3_graph_init_tpc,
+       nvc0_graph_init_unk88xx,
+       nvc0_graph_tpc_0,
+       NULL
+};
+
+struct nouveau_oclass *
+nvc3_graph_oclass = &(struct nvc0_graph_oclass) {
+       .base.handle = NV_ENGINE(GR, 0xc3),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_ctor,
+               .dtor = nvc0_graph_dtor,
+               .init = nvc0_graph_init,
+               .fini = _nouveau_graph_fini,
+       },
+       .cclass = &nvc3_grctx_oclass,
+       .sclass = nvc0_graph_sclass,
+       .mmio = nvc3_graph_init_mmio,
+       .fecs.ucode = &nvc0_graph_fecs_ucode,
+       .gpccs.ucode = &nvc0_graph_gpccs_ucode,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c
new file mode 100644 (file)
index 0000000..02845e5
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * 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 "nvc0.h"
+
+/*******************************************************************************
+ * Graphics object classes
+ ******************************************************************************/
+
+struct nouveau_oclass
+nvc8_graph_sclass[] = {
+       { 0x902d, &nouveau_object_ofuncs },
+       { 0x9039, &nouveau_object_ofuncs },
+       { 0x9097, &nouveau_object_ofuncs },
+       { 0x90c0, &nouveau_object_ofuncs },
+       { 0x9197, &nouveau_object_ofuncs },
+       { 0x9297, &nouveau_object_ofuncs },
+       {}
+};
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
+static struct nvc0_graph_init
+nvc8_graph_init_gpc[] = {
+       { 0x4184a0,   1, 0x04, 0x00000000 },
+       { 0x418604,   1, 0x04, 0x00000000 },
+       { 0x418680,   1, 0x04, 0x00000000 },
+       { 0x418714,   1, 0x04, 0x80000000 },
+       { 0x418384,   1, 0x04, 0x00000000 },
+       { 0x418814,   3, 0x04, 0x00000000 },
+       { 0x418b04,   1, 0x04, 0x00000000 },
+       { 0x4188c8,   2, 0x04, 0x00000000 },
+       { 0x4188d0,   1, 0x04, 0x00010000 },
+       { 0x4188d4,   1, 0x04, 0x00000001 },
+       { 0x418910,   1, 0x04, 0x00010001 },
+       { 0x418914,   1, 0x04, 0x00000301 },
+       { 0x418918,   1, 0x04, 0x00800000 },
+       { 0x418980,   1, 0x04, 0x77777770 },
+       { 0x418984,   3, 0x04, 0x77777777 },
+       { 0x418c04,   1, 0x04, 0x00000000 },
+       { 0x418c88,   1, 0x04, 0x00000000 },
+       { 0x418d00,   1, 0x04, 0x00000000 },
+       { 0x418f08,   1, 0x04, 0x00000000 },
+       { 0x418e00,   1, 0x04, 0x00000050 },
+       { 0x418e08,   1, 0x04, 0x00000000 },
+       { 0x41900c,   1, 0x04, 0x00000000 },
+       { 0x419018,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvc8_graph_init_tpc[] = {
+       { 0x419d08,   2, 0x04, 0x00000000 },
+       { 0x419d10,   1, 0x04, 0x00000014 },
+       { 0x419ab0,   1, 0x04, 0x00000000 },
+       { 0x419ab8,   1, 0x04, 0x000000e7 },
+       { 0x419abc,   2, 0x04, 0x00000000 },
+       { 0x41980c,   3, 0x04, 0x00000000 },
+       { 0x419844,   1, 0x04, 0x00000000 },
+       { 0x41984c,   1, 0x04, 0x00005bc5 },
+       { 0x419850,   4, 0x04, 0x00000000 },
+       { 0x419c98,   1, 0x04, 0x00000000 },
+       { 0x419ca8,   1, 0x04, 0x80000000 },
+       { 0x419cb4,   1, 0x04, 0x00000000 },
+       { 0x419cb8,   1, 0x04, 0x00008bf4 },
+       { 0x419cbc,   1, 0x04, 0x28137606 },
+       { 0x419cc0,   2, 0x04, 0x00000000 },
+       { 0x419bd4,   1, 0x04, 0x00800000 },
+       { 0x419bdc,   1, 0x04, 0x00000000 },
+       { 0x419d2c,   1, 0x04, 0x00000000 },
+       { 0x419c0c,   1, 0x04, 0x00000000 },
+       { 0x419e00,   1, 0x04, 0x00000000 },
+       { 0x419ea0,   1, 0x04, 0x00000000 },
+       { 0x419ea4,   1, 0x04, 0x00000100 },
+       { 0x419ea8,   1, 0x04, 0x00001100 },
+       { 0x419eac,   1, 0x04, 0x11100f02 },
+       { 0x419eb0,   1, 0x04, 0x00000003 },
+       { 0x419eb4,   4, 0x04, 0x00000000 },
+       { 0x419ec8,   1, 0x04, 0x06060618 },
+       { 0x419ed0,   1, 0x04, 0x0eff0e38 },
+       { 0x419ed4,   1, 0x04, 0x011104f1 },
+       { 0x419edc,   1, 0x04, 0x00000000 },
+       { 0x419f00,   1, 0x04, 0x00000000 },
+       { 0x419f2c,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init *
+nvc8_graph_init_mmio[] = {
+       nvc0_graph_init_regs,
+       nvc0_graph_init_unk40xx,
+       nvc0_graph_init_unk44xx,
+       nvc0_graph_init_unk78xx,
+       nvc0_graph_init_unk60xx,
+       nvc0_graph_init_unk58xx,
+       nvc0_graph_init_unk80xx,
+       nvc8_graph_init_gpc,
+       nvc8_graph_init_tpc,
+       nvc0_graph_init_unk88xx,
+       nvc0_graph_tpc_0,
+       NULL
+};
+
+struct nouveau_oclass *
+nvc8_graph_oclass = &(struct nvc0_graph_oclass) {
+       .base.handle = NV_ENGINE(GR, 0xc8),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_ctor,
+               .dtor = nvc0_graph_dtor,
+               .init = nvc0_graph_init,
+               .fini = _nouveau_graph_fini,
+       },
+       .cclass = &nvc8_grctx_oclass,
+       .sclass = nvc8_graph_sclass,
+       .mmio = nvc8_graph_init_mmio,
+       .fecs.ucode = &nvc0_graph_fecs_ucode,
+       .gpccs.ucode = &nvc0_graph_gpccs_ucode,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c
new file mode 100644 (file)
index 0000000..5052d7a
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * 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 "nvc0.h"
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
+#include "fuc/hubnvd7.fuc.h"
+
+struct nvc0_graph_ucode
+nvd7_graph_fecs_ucode = {
+       .code.data = nvd7_grhub_code,
+       .code.size = sizeof(nvd7_grhub_code),
+       .data.data = nvd7_grhub_data,
+       .data.size = sizeof(nvd7_grhub_data),
+};
+
+#include "fuc/gpcnvd7.fuc.h"
+
+struct nvc0_graph_ucode
+nvd7_graph_gpccs_ucode = {
+       .code.data = nvd7_grgpc_code,
+       .code.size = sizeof(nvd7_grgpc_code),
+       .data.data = nvd7_grgpc_data,
+       .data.size = sizeof(nvd7_grgpc_data),
+};
+
+static struct nvc0_graph_init
+nvd7_graph_init_gpc[] = {
+       { 0x418408,   1, 0x04, 0x00000000 },
+       { 0x4184a0,   1, 0x04, 0x00000000 },
+       { 0x4184a4,   2, 0x04, 0x00000000 },
+       { 0x418604,   1, 0x04, 0x00000000 },
+       { 0x418680,   1, 0x04, 0x00000000 },
+       { 0x418714,   1, 0x04, 0x00000000 },
+       { 0x418384,   1, 0x04, 0x00000000 },
+       { 0x418814,   3, 0x04, 0x00000000 },
+       { 0x418b04,   1, 0x04, 0x00000000 },
+       { 0x4188c8,   2, 0x04, 0x00000000 },
+       { 0x4188d0,   1, 0x04, 0x00010000 },
+       { 0x4188d4,   1, 0x04, 0x00000001 },
+       { 0x418910,   1, 0x04, 0x00010001 },
+       { 0x418914,   1, 0x04, 0x00000301 },
+       { 0x418918,   1, 0x04, 0x00800000 },
+       { 0x418980,   1, 0x04, 0x77777770 },
+       { 0x418984,   3, 0x04, 0x77777777 },
+       { 0x418c04,   1, 0x04, 0x00000000 },
+       { 0x418c64,   1, 0x04, 0x00000000 },
+       { 0x418c68,   1, 0x04, 0x00000000 },
+       { 0x418c88,   1, 0x04, 0x00000000 },
+       { 0x418cb4,   2, 0x04, 0x00000000 },
+       { 0x418d00,   1, 0x04, 0x00000000 },
+       { 0x418d28,   1, 0x04, 0x00000000 },
+       { 0x418f00,   1, 0x04, 0x00000000 },
+       { 0x418f08,   1, 0x04, 0x00000000 },
+       { 0x418f20,   2, 0x04, 0x00000000 },
+       { 0x418e00,   1, 0x04, 0x00000003 },
+       { 0x418e08,   1, 0x04, 0x00000000 },
+       { 0x418e1c,   1, 0x04, 0x00000000 },
+       { 0x418e20,   1, 0x04, 0x00000000 },
+       { 0x41900c,   1, 0x04, 0x00000000 },
+       { 0x419018,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd7_graph_init_tpc[] = {
+       { 0x419d08,   2, 0x04, 0x00000000 },
+       { 0x419d10,   1, 0x04, 0x00000014 },
+       { 0x419ab0,   1, 0x04, 0x00000000 },
+       { 0x419ac8,   1, 0x04, 0x00000000 },
+       { 0x419ab8,   1, 0x04, 0x000000e7 },
+       { 0x419abc,   2, 0x04, 0x00000000 },
+       { 0x419ab4,   1, 0x04, 0x00000000 },
+       { 0x41980c,   1, 0x04, 0x00000010 },
+       { 0x419844,   1, 0x04, 0x00000000 },
+       { 0x41984c,   1, 0x04, 0x00005bc8 },
+       { 0x419850,   2, 0x04, 0x00000000 },
+       { 0x419c98,   1, 0x04, 0x00000000 },
+       { 0x419ca8,   1, 0x04, 0x80000000 },
+       { 0x419cb4,   1, 0x04, 0x00000000 },
+       { 0x419cb8,   1, 0x04, 0x00008bf4 },
+       { 0x419cbc,   1, 0x04, 0x28137606 },
+       { 0x419cc0,   2, 0x04, 0x00000000 },
+       { 0x419c0c,   1, 0x04, 0x00000000 },
+       { 0x419e00,   1, 0x04, 0x00000000 },
+       { 0x419ea0,   1, 0x04, 0x00000000 },
+       { 0x419ea4,   1, 0x04, 0x00000100 },
+       { 0x419ea8,   1, 0x04, 0x02001100 },
+       { 0x419eac,   1, 0x04, 0x11100702 },
+       { 0x419eb0,   1, 0x04, 0x00000003 },
+       { 0x419eb4,   4, 0x04, 0x00000000 },
+       { 0x419ec8,   1, 0x04, 0x0e063818 },
+       { 0x419ecc,   1, 0x04, 0x0e060e06 },
+       { 0x419ed0,   1, 0x04, 0x00003818 },
+       { 0x419ed4,   1, 0x04, 0x011104f1 },
+       { 0x419edc,   1, 0x04, 0x00000000 },
+       { 0x419f00,   1, 0x04, 0x00000000 },
+       { 0x419f2c,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd7_graph_init_tpc_0[] = {
+       { 0x40402c,   1, 0x04, 0x00000000 },
+       { 0x4040f0,   1, 0x04, 0x00000000 },
+       { 0x404174,   1, 0x04, 0x00000000 },
+       { 0x503018,   1, 0x04, 0x00000001 },
+       {}
+};
+
+static struct nvc0_graph_init *
+nvd7_graph_init_mmio[] = {
+       nvc0_graph_init_regs,
+       nvc0_graph_init_unk40xx,
+       nvc0_graph_init_unk44xx,
+       nvc0_graph_init_unk78xx,
+       nvc0_graph_init_unk60xx,
+       nvd9_graph_init_unk64xx,
+       nvd9_graph_init_unk58xx,
+       nvc0_graph_init_unk80xx,
+       nvd7_graph_init_gpc,
+       nvd7_graph_init_tpc,
+       nve4_graph_init_unk,
+       nvc0_graph_init_unk88xx,
+       nvd7_graph_init_tpc_0,
+       NULL
+};
+
+struct nouveau_oclass *
+nvd7_graph_oclass = &(struct nvc0_graph_oclass) {
+       .base.handle = NV_ENGINE(GR, 0xd7),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_ctor,
+               .dtor = nvc0_graph_dtor,
+               .init = nvc0_graph_init,
+               .fini = _nouveau_graph_fini,
+       },
+       .cclass = &nvd7_grctx_oclass,
+       .sclass = nvc8_graph_sclass,
+       .mmio = nvd7_graph_init_mmio,
+       .fecs.ucode = &nvd7_graph_fecs_ucode,
+       .gpccs.ucode = &nvd7_graph_gpccs_ucode,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c
new file mode 100644 (file)
index 0000000..652098e
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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 "nvc0.h"
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
+struct nvc0_graph_init
+nvd9_graph_init_unk64xx[] = {
+       { 0x4064f0,   3, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nvd9_graph_init_unk58xx[] = {
+       { 0x405844,   1, 0x04, 0x00ffffff },
+       { 0x405850,   1, 0x04, 0x00000000 },
+       { 0x405900,   1, 0x04, 0x00002834 },
+       { 0x405908,   1, 0x04, 0x00000000 },
+       { 0x405928,   1, 0x04, 0x00000000 },
+       { 0x40592c,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd9_graph_init_gpc[] = {
+       { 0x418408,   1, 0x04, 0x00000000 },
+       { 0x4184a0,   1, 0x04, 0x00000000 },
+       { 0x4184a4,   2, 0x04, 0x00000000 },
+       { 0x418604,   1, 0x04, 0x00000000 },
+       { 0x418680,   1, 0x04, 0x00000000 },
+       { 0x418714,   1, 0x04, 0x00000000 },
+       { 0x418384,   1, 0x04, 0x00000000 },
+       { 0x418814,   3, 0x04, 0x00000000 },
+       { 0x418b04,   1, 0x04, 0x00000000 },
+       { 0x4188c8,   2, 0x04, 0x00000000 },
+       { 0x4188d0,   1, 0x04, 0x00010000 },
+       { 0x4188d4,   1, 0x04, 0x00000001 },
+       { 0x418910,   1, 0x04, 0x00010001 },
+       { 0x418914,   1, 0x04, 0x00000301 },
+       { 0x418918,   1, 0x04, 0x00800000 },
+       { 0x418980,   1, 0x04, 0x77777770 },
+       { 0x418984,   3, 0x04, 0x77777777 },
+       { 0x418c04,   1, 0x04, 0x00000000 },
+       { 0x418c64,   1, 0x04, 0x00000000 },
+       { 0x418c68,   1, 0x04, 0x00000000 },
+       { 0x418c88,   1, 0x04, 0x00000000 },
+       { 0x418cb4,   2, 0x04, 0x00000000 },
+       { 0x418d00,   1, 0x04, 0x00000000 },
+       { 0x418d28,   1, 0x04, 0x00000000 },
+       { 0x418d2c,   1, 0x04, 0x00000000 },
+       { 0x418f00,   1, 0x04, 0x00000000 },
+       { 0x418f08,   1, 0x04, 0x00000000 },
+       { 0x418f20,   2, 0x04, 0x00000000 },
+       { 0x418e00,   1, 0x04, 0x00000003 },
+       { 0x418e08,   1, 0x04, 0x00000000 },
+       { 0x418e1c,   1, 0x04, 0x00000000 },
+       { 0x418e20,   1, 0x04, 0x00000000 },
+       { 0x41900c,   1, 0x04, 0x00000000 },
+       { 0x419018,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvd9_graph_init_tpc[] = {
+       { 0x419d08,   2, 0x04, 0x00000000 },
+       { 0x419d10,   1, 0x04, 0x00000014 },
+       { 0x419ab0,   1, 0x04, 0x00000000 },
+       { 0x419ac8,   1, 0x04, 0x00000000 },
+       { 0x419ab8,   1, 0x04, 0x000000e7 },
+       { 0x419abc,   2, 0x04, 0x00000000 },
+       { 0x419ab4,   1, 0x04, 0x00000000 },
+       { 0x41980c,   1, 0x04, 0x00000010 },
+       { 0x419810,   1, 0x04, 0x00000000 },
+       { 0x419814,   1, 0x04, 0x00000004 },
+       { 0x419844,   1, 0x04, 0x00000000 },
+       { 0x41984c,   1, 0x04, 0x0000a918 },
+       { 0x419850,   4, 0x04, 0x00000000 },
+       { 0x419880,   1, 0x04, 0x00000002 },
+       { 0x419c98,   1, 0x04, 0x00000000 },
+       { 0x419ca8,   1, 0x04, 0x80000000 },
+       { 0x419cb4,   1, 0x04, 0x00000000 },
+       { 0x419cb8,   1, 0x04, 0x00008bf4 },
+       { 0x419cbc,   1, 0x04, 0x28137606 },
+       { 0x419cc0,   2, 0x04, 0x00000000 },
+       { 0x419bd4,   1, 0x04, 0x00800000 },
+       { 0x419bdc,   1, 0x04, 0x00000000 },
+       { 0x419bf8,   1, 0x04, 0x00000000 },
+       { 0x419bfc,   1, 0x04, 0x00000000 },
+       { 0x419d2c,   1, 0x04, 0x00000000 },
+       { 0x419d48,   1, 0x04, 0x00000000 },
+       { 0x419d4c,   1, 0x04, 0x00000000 },
+       { 0x419c0c,   1, 0x04, 0x00000000 },
+       { 0x419e00,   1, 0x04, 0x00000000 },
+       { 0x419ea0,   1, 0x04, 0x00000000 },
+       { 0x419ea4,   1, 0x04, 0x00000100 },
+       { 0x419ea8,   1, 0x04, 0x02001100 },
+       { 0x419eac,   1, 0x04, 0x11100702 },
+       { 0x419eb0,   1, 0x04, 0x00000003 },
+       { 0x419eb4,   4, 0x04, 0x00000000 },
+       { 0x419ec8,   1, 0x04, 0x0e063818 },
+       { 0x419ecc,   1, 0x04, 0x0e060e06 },
+       { 0x419ed0,   1, 0x04, 0x00003818 },
+       { 0x419ed4,   1, 0x04, 0x011104f1 },
+       { 0x419edc,   1, 0x04, 0x00000000 },
+       { 0x419f00,   1, 0x04, 0x00000000 },
+       { 0x419f2c,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init *
+nvd9_graph_init_mmio[] = {
+       nvc0_graph_init_regs,
+       nvc0_graph_init_unk40xx,
+       nvc0_graph_init_unk44xx,
+       nvc0_graph_init_unk78xx,
+       nvc0_graph_init_unk60xx,
+       nvd9_graph_init_unk64xx,
+       nvd9_graph_init_unk58xx,
+       nvc0_graph_init_unk80xx,
+       nvd9_graph_init_gpc,
+       nvd9_graph_init_tpc,
+       nvc0_graph_init_unk88xx,
+       nvc0_graph_tpc_0,
+       NULL
+};
+
+struct nouveau_oclass *
+nvd9_graph_oclass = &(struct nvc0_graph_oclass) {
+       .base.handle = NV_ENGINE(GR, 0xd9),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_ctor,
+               .dtor = nvc0_graph_dtor,
+               .init = nvc0_graph_init,
+               .fini = _nouveau_graph_fini,
+       },
+       .cclass = &nvd9_grctx_oclass,
+       .sclass = nvc8_graph_sclass,
+       .mmio = nvd9_graph_init_mmio,
+       .fecs.ucode = &nvc0_graph_fecs_ucode,
+       .gpccs.ucode = &nvc0_graph_gpccs_ucode,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c
deleted file mode 100644 (file)
index 678c16f..0000000
+++ /dev/null
@@ -1,807 +0,0 @@
-/*
- * 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"
-#include "fuc/hubnve0.fuc.h"
-#include "fuc/gpcnve0.fuc.h"
-
-/*******************************************************************************
- * Graphics object classes
- ******************************************************************************/
-
-static struct nouveau_oclass
-nve0_graph_sclass[] = {
-       { 0x902d, &nouveau_object_ofuncs },
-       { 0xa040, &nouveau_object_ofuncs },
-       { 0xa097, &nouveau_object_ofuncs },
-       { 0xa0c0, &nouveau_object_ofuncs },
-       { 0xa0b5, &nouveau_object_ofuncs },
-       {}
-};
-
-/*******************************************************************************
- * PGRAPH context
- ******************************************************************************/
-
-static struct nouveau_oclass
-nve0_graph_cclass = {
-       .handle = NV_ENGCTX(GR, 0xe0),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nvc0_graph_context_ctor,
-               .dtor = nvc0_graph_context_dtor,
-               .init = _nouveau_graph_context_init,
-               .fini = _nouveau_graph_context_fini,
-               .rd32 = _nouveau_graph_context_rd32,
-               .wr32 = _nouveau_graph_context_wr32,
-       },
-};
-
-/*******************************************************************************
- * PGRAPH engine/subdev functions
- ******************************************************************************/
-
-static void
-nve0_graph_ctxctl_isr(struct nvc0_graph_priv *priv)
-{
-       u32 ustat = nv_rd32(priv, 0x409c18);
-
-       if (ustat & 0x00000001)
-               nv_error(priv, "CTXCTRL ucode error\n");
-       if (ustat & 0x00080000)
-               nv_error(priv, "CTXCTRL watchdog timeout\n");
-       if (ustat & ~0x00080001)
-               nv_error(priv, "CTXCTRL 0x%08x\n", ustat);
-
-       nvc0_graph_ctxctl_debug(priv);
-       nv_wr32(priv, 0x409c20, ustat);
-}
-
-static const struct nouveau_enum nve0_mp_warp_error[] = {
-       { 0x00, "NO_ERROR" },
-       { 0x01, "STACK_MISMATCH" },
-       { 0x05, "MISALIGNED_PC" },
-       { 0x08, "MISALIGNED_GPR" },
-       { 0x09, "INVALID_OPCODE" },
-       { 0x0d, "GPR_OUT_OF_BOUNDS" },
-       { 0x0e, "MEM_OUT_OF_BOUNDS" },
-       { 0x0f, "UNALIGNED_MEM_ACCESS" },
-       { 0x11, "INVALID_PARAM" },
-       {}
-};
-
-static const struct nouveau_enum nve0_mp_global_error[] = {
-       { 2, "MULTIPLE_WARP_ERRORS" },
-       { 3, "OUT_OF_STACK_SPACE" },
-       {}
-};
-
-static const struct nouveau_enum nve0_gpc_rop_error[] = {
-       { 1, "RT_PITCH_OVERRUN" },
-       { 4, "RT_WIDTH_OVERRUN" },
-       { 5, "RT_HEIGHT_OVERRUN" },
-       { 7, "ZETA_STORAGE_TYPE_MISMATCH" },
-       { 8, "RT_STORAGE_TYPE_MISMATCH" },
-       { 10, "RT_LINEAR_MISMATCH" },
-       {}
-};
-
-static const struct nouveau_enum nve0_sked_error[] = {
-       { 7, "CONSTANT_BUFFER_SIZE" },
-       { 9, "LOCAL_MEMORY_SIZE_POS" },
-       { 10, "LOCAL_MEMORY_SIZE_NEG" },
-       { 11, "WARP_CSTACK_SIZE" },
-       { 12, "TOTAL_TEMP_SIZE" },
-       { 13, "REGISTER_COUNT" },
-       { 18, "TOTAL_THREADS" },
-       { 20, "PROGRAM_OFFSET" },
-       { 21, "SHARED_MEMORY_SIZE" },
-       { 25, "SHARED_CONFIG_TOO_SMALL" },
-       { 26, "TOTAL_REGISTER_COUNT" },
-       {}
-};
-
-static void
-nve0_graph_mp_trap(struct nvc0_graph_priv *priv, int gpc, int tp)
-{
-       int i;
-       u32 werr = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x648));
-       u32 gerr = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x650));
-
-       nv_error(priv, "GPC%i/TP%i/MP trap:", gpc, tp);
-
-       for (i = 0; i <= 31; ++i) {
-               if (!(gerr & (1 << i)))
-                       continue;
-               pr_cont(" ");
-               nouveau_enum_print(nve0_mp_global_error, i);
-       }
-       if (werr) {
-               pr_cont(" ");
-               nouveau_enum_print(nve0_mp_warp_error, werr & 0xffff);
-       }
-       pr_cont("\n");
-
-       /* disable MP trap to avoid spam */
-       nv_mask(priv, TPC_UNIT(gpc, tp, 0x50c), 0x2, 0x0);
-
-       /* TODO: figure out how to resume after an MP trap */
-}
-
-static void
-nve0_graph_tp_trap(struct nvc0_graph_priv *priv, int gpc, int tp)
-{
-       u32 stat = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x508));
-
-       if (stat & 0x1) {
-               u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x224));
-               nv_error(priv, "GPC%i/TP%i/TEX trap: %08x\n",
-                        gpc, tp, trap);
-
-               nv_wr32(priv, TPC_UNIT(gpc, tp, 0x224), 0xc0000000);
-               stat &= ~0x1;
-       }
-
-       if (stat & 0x2) {
-               nve0_graph_mp_trap(priv, gpc, tp);
-               stat &= ~0x2;
-       }
-
-       if (stat & 0x4) {
-               u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x084));
-               nv_error(priv, "GPC%i/TP%i/POLY trap: %08x\n",
-                        gpc, tp, trap);
-
-               nv_wr32(priv, TPC_UNIT(gpc, tp, 0x084), 0xc0000000);
-               stat &= ~0x4;
-       }
-
-       if (stat & 0x8) {
-               u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x48c));
-               nv_error(priv, "GPC%i/TP%i/L1C trap: %08x\n",
-                        gpc, tp, trap);
-
-               nv_wr32(priv, TPC_UNIT(gpc, tp, 0x48c), 0xc0000000);
-               stat &= ~0x8;
-       }
-
-       if (stat) {
-               nv_error(priv, "GPC%i/TP%i: unknown stat %08x\n",
-                        gpc, tp, stat);
-       }
-}
-
-static void
-nve0_graph_gpc_trap(struct nvc0_graph_priv *priv)
-{
-       const u32 mask = nv_rd32(priv, 0x400118);
-       int gpc;
-
-       for (gpc = 0; gpc < 4; ++gpc) {
-               u32 stat;
-               int tp;
-
-               if (!(mask & (1 << gpc)))
-                       continue;
-               stat = nv_rd32(priv, GPC_UNIT(gpc, 0x2c90));
-
-               if (stat & 0x0001) {
-                       u32 trap[4];
-                       int i;
-
-                       trap[0] = nv_rd32(priv, GPC_UNIT(gpc, 0x0420));
-                       trap[1] = nv_rd32(priv, GPC_UNIT(gpc, 0x0434));
-                       trap[2] = nv_rd32(priv, GPC_UNIT(gpc, 0x0438));
-                       trap[3] = nv_rd32(priv, GPC_UNIT(gpc, 0x043c));
-
-                       nv_error(priv, "GPC%i/PROP trap:", gpc);
-                       for (i = 0; i <= 29; ++i) {
-                               if (!(trap[0] & (1 << i)))
-                                       continue;
-                               pr_cont(" ");
-                               nouveau_enum_print(nve0_gpc_rop_error, i);
-                       }
-                       pr_cont("\n");
-
-                       nv_error(priv, "x = %u, y = %u, "
-                                "format = %x, storage type = %x\n",
-                                trap[1] & 0xffff,
-                                trap[1] >> 16,
-                                (trap[2] >> 8) & 0x3f,
-                                trap[3] & 0xff);
-
-                       nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000);
-                       stat &= ~0x0001;
-               }
-
-               if (stat & 0x0002) {
-                       u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0900));
-                       nv_error(priv, "GPC%i/ZCULL trap: %08x\n", gpc,
-                                trap);
-                       nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000);
-                       stat &= ~0x0002;
-               }
-
-               if (stat & 0x0004) {
-                       u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x1028));
-                       nv_error(priv, "GPC%i/CCACHE trap: %08x\n", gpc,
-                                trap);
-                       nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000);
-                       stat &= ~0x0004;
-               }
-
-               if (stat & 0x0008) {
-                       u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0824));
-                       nv_error(priv, "GPC%i/ESETUP trap %08x\n", gpc,
-                                trap);
-                       nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000);
-                       stat &= ~0x0008;
-               }
-
-               for (tp = 0; tp < 8; ++tp) {
-                       if (stat & (1 << (16 + tp)))
-                               nve0_graph_tp_trap(priv, gpc, tp);
-               }
-               stat &= ~0xff0000;
-
-               if (stat) {
-                       nv_error(priv, "GPC%i: unknown stat %08x\n",
-                                gpc, stat);
-               }
-       }
-}
-
-
-static void
-nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst,
-               struct nouveau_object *engctx)
-{
-       u32 trap = nv_rd32(priv, 0x400108);
-       int i;
-       int rop;
-
-       if (trap & 0x00000001) {
-               u32 stat = nv_rd32(priv, 0x404000);
-               nv_error(priv, "DISPATCH ch %d [0x%010llx %s] 0x%08x\n",
-                        chid, inst, nouveau_client_name(engctx), stat);
-               nv_wr32(priv, 0x404000, 0xc0000000);
-               nv_wr32(priv, 0x400108, 0x00000001);
-               trap &= ~0x00000001;
-       }
-
-       if (trap & 0x00000010) {
-               u32 stat = nv_rd32(priv, 0x405840);
-               nv_error(priv, "SHADER ch %d [0x%010llx %s] 0x%08x\n",
-                        chid, inst, nouveau_client_name(engctx), stat);
-               nv_wr32(priv, 0x405840, 0xc0000000);
-               nv_wr32(priv, 0x400108, 0x00000010);
-               trap &= ~0x00000010;
-       }
-
-       if (trap & 0x00000100) {
-               u32 stat = nv_rd32(priv, 0x407020);
-               nv_error(priv, "SKED ch %d [0x%010llx %s]:",
-                        chid, inst, nouveau_client_name(engctx));
-
-               for (i = 0; i <= 29; ++i) {
-                       if (!(stat & (1 << i)))
-                               continue;
-                       pr_cont(" ");
-                       nouveau_enum_print(nve0_sked_error, i);
-               }
-               pr_cont("\n");
-
-               if (stat & 0x3fffffff)
-                       nv_wr32(priv, 0x407020, 0x40000000);
-               nv_wr32(priv, 0x400108, 0x00000100);
-               trap &= ~0x00000100;
-       }
-
-       if (trap & 0x01000000) {
-               nv_error(priv, "GPC ch %d [0x%010llx %s]:\n",
-                        chid, inst, nouveau_client_name(engctx));
-               nve0_graph_gpc_trap(priv);
-               trap &= ~0x01000000;
-       }
-
-       if (trap & 0x02000000) {
-               for (rop = 0; rop < priv->rop_nr; rop++) {
-                       u32 statz = nv_rd32(priv, ROP_UNIT(rop, 0x070));
-                       u32 statc = nv_rd32(priv, ROP_UNIT(rop, 0x144));
-                       nv_error(priv,
-                                "ROP%d ch %d [0x%010llx %s] 0x%08x 0x%08x\n",
-                                rop, chid, inst, nouveau_client_name(engctx),
-                                statz, statc);
-                       nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000);
-                       nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000);
-               }
-               nv_wr32(priv, 0x400108, 0x02000000);
-               trap &= ~0x02000000;
-       }
-
-       if (trap) {
-               nv_error(priv, "TRAP ch %d [0x%010llx %s] 0x%08x\n",
-                        chid, inst, nouveau_client_name(engctx), trap);
-               nv_wr32(priv, 0x400108, trap);
-       }
-}
-
-static void
-nve0_graph_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 nvc0_graph_priv *priv = (void *)subdev;
-       u64 inst = nv_rd32(priv, 0x409b00) & 0x0fffffff;
-       u32 stat = nv_rd32(priv, 0x400100);
-       u32 addr = nv_rd32(priv, 0x400704);
-       u32 mthd = (addr & 0x00003ffc);
-       u32 subc = (addr & 0x00070000) >> 16;
-       u32 data = nv_rd32(priv, 0x400708);
-       u32 code = nv_rd32(priv, 0x400110);
-       u32 class = nv_rd32(priv, 0x404200 + (subc * 4));
-       int chid;
-
-       engctx = nouveau_engctx_get(engine, inst);
-       chid   = pfifo->chid(pfifo, engctx);
-
-       if (stat & 0x00000010) {
-               handle = nouveau_handle_get_class(engctx, class);
-               if (!handle || nv_call(handle->object, mthd, data)) {
-                       nv_error(priv,
-                                "ILLEGAL_MTHD ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
-                                chid, inst, nouveau_client_name(engctx), subc,
-                                class, mthd, data);
-               }
-               nouveau_handle_put(handle);
-               nv_wr32(priv, 0x400100, 0x00000010);
-               stat &= ~0x00000010;
-       }
-
-       if (stat & 0x00000020) {
-               nv_error(priv,
-                        "ILLEGAL_CLASS ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
-                        chid, inst, nouveau_client_name(engctx), subc, class,
-                        mthd, data);
-               nv_wr32(priv, 0x400100, 0x00000020);
-               stat &= ~0x00000020;
-       }
-
-       if (stat & 0x00100000) {
-               nv_error(priv, "DATA_ERROR [");
-               nouveau_enum_print(nv50_data_error_names, code);
-               pr_cont("] ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n",
-                       chid, inst, nouveau_client_name(engctx), subc, class,
-                       mthd, data);
-               nv_wr32(priv, 0x400100, 0x00100000);
-               stat &= ~0x00100000;
-       }
-
-       if (stat & 0x00200000) {
-               nve0_graph_trap_isr(priv, chid, inst, engctx);
-               nv_wr32(priv, 0x400100, 0x00200000);
-               stat &= ~0x00200000;
-       }
-
-       if (stat & 0x00080000) {
-               nve0_graph_ctxctl_isr(priv);
-               nv_wr32(priv, 0x400100, 0x00080000);
-               stat &= ~0x00080000;
-       }
-
-       if (stat) {
-               nv_error(priv, "unknown stat 0x%08x\n", stat);
-               nv_wr32(priv, 0x400100, stat);
-       }
-
-       nv_wr32(priv, 0x400500, 0x00010001);
-       nouveau_engctx_put(engctx);
-}
-
-static int
-nve0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-              struct nouveau_oclass *oclass, void *data, u32 size,
-              struct nouveau_object **pobject)
-{
-       struct nouveau_device *device = nv_device(parent);
-       struct nvc0_graph_priv *priv;
-       int ret, i;
-
-       ret = nouveau_graph_create(parent, engine, oclass, true, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       nv_subdev(priv)->unit = 0x18001000;
-       nv_subdev(priv)->intr = nve0_graph_intr;
-       nv_engine(priv)->cclass = &nve0_graph_cclass;
-       nv_engine(priv)->sclass = nve0_graph_sclass;
-
-       priv->base.units = nvc0_graph_units;
-
-       if (nouveau_boolopt(device->cfgopt, "NvGrUseFW", false)) {
-               nv_info(priv, "using external firmware\n");
-               if (nvc0_graph_ctor_fw(priv, "fuc409c", &priv->fuc409c) ||
-                   nvc0_graph_ctor_fw(priv, "fuc409d", &priv->fuc409d) ||
-                   nvc0_graph_ctor_fw(priv, "fuc41ac", &priv->fuc41ac) ||
-                   nvc0_graph_ctor_fw(priv, "fuc41ad", &priv->fuc41ad))
-                       return -EINVAL;
-               priv->firmware = true;
-       }
-
-       ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0,
-                               &priv->unk4188b4);
-       if (ret)
-               return ret;
-
-       ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0,
-                               &priv->unk4188b8);
-       if (ret)
-               return ret;
-
-       for (i = 0; i < 0x1000; i += 4) {
-               nv_wo32(priv->unk4188b4, i, 0x00000010);
-               nv_wo32(priv->unk4188b8, i, 0x00000010);
-       }
-
-       priv->gpc_nr =  nv_rd32(priv, 0x409604) & 0x0000001f;
-       priv->rop_nr = (nv_rd32(priv, 0x409604) & 0x001f0000) >> 16;
-       for (i = 0; i < priv->gpc_nr; i++) {
-               priv->tpc_nr[i] = nv_rd32(priv, GPC_UNIT(i, 0x2608));
-               priv->tpc_total += priv->tpc_nr[i];
-       }
-
-       switch (nv_device(priv)->chipset) {
-       case 0xe4:
-               if (priv->tpc_total == 8)
-                       priv->magic_not_rop_nr = 3;
-               else
-               if (priv->tpc_total == 7)
-                       priv->magic_not_rop_nr = 1;
-               break;
-       case 0xe7:
-       case 0xe6:
-               priv->magic_not_rop_nr = 1;
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-static void
-nve0_graph_init_obj418880(struct nvc0_graph_priv *priv)
-{
-       int i;
-
-       nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000);
-       nv_wr32(priv, GPC_BCAST(0x08a4), 0x00000000);
-       for (i = 0; i < 4; i++)
-               nv_wr32(priv, GPC_BCAST(0x0888) + (i * 4), 0x00000000);
-       nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8);
-       nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8);
-}
-
-static void
-nve0_graph_init_regs(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x400080, 0x003083c2);
-       nv_wr32(priv, 0x400088, 0x0001ffe7);
-       nv_wr32(priv, 0x40008c, 0x00000000);
-       nv_wr32(priv, 0x400090, 0x00000030);
-       nv_wr32(priv, 0x40013c, 0x003901f7);
-       nv_wr32(priv, 0x400140, 0x00000100);
-       nv_wr32(priv, 0x400144, 0x00000000);
-       nv_wr32(priv, 0x400148, 0x00000110);
-       nv_wr32(priv, 0x400138, 0x00000000);
-       nv_wr32(priv, 0x400130, 0x00000000);
-       nv_wr32(priv, 0x400134, 0x00000000);
-       nv_wr32(priv, 0x400124, 0x00000002);
-}
-
-static void
-nve0_graph_init_units(struct nvc0_graph_priv *priv)
-{
-       nv_wr32(priv, 0x409ffc, 0x00000000);
-       nv_wr32(priv, 0x409c14, 0x00003e3e);
-       nv_wr32(priv, 0x409c24, 0x000f0000);
-
-       nv_wr32(priv, 0x404000, 0xc0000000);
-       nv_wr32(priv, 0x404600, 0xc0000000);
-       nv_wr32(priv, 0x408030, 0xc0000000);
-       nv_wr32(priv, 0x404490, 0xc0000000);
-       nv_wr32(priv, 0x406018, 0xc0000000);
-       nv_wr32(priv, 0x407020, 0xc0000000);
-       nv_wr32(priv, 0x405840, 0xc0000000);
-       nv_wr32(priv, 0x405844, 0x00ffffff);
-
-       nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008);
-       nv_mask(priv, 0x419eb4, 0x00001000, 0x00001000);
-
-}
-
-static void
-nve0_graph_init_gpc_0(struct nvc0_graph_priv *priv)
-{
-       const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total);
-       u32 data[TPC_MAX / 8];
-       u8  tpcnr[GPC_MAX];
-       int i, gpc, tpc;
-
-       nv_wr32(priv, GPC_UNIT(0, 0x3018), 0x00000001);
-
-       memset(data, 0x00, sizeof(data));
-       memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
-       for (i = 0, gpc = -1; i < priv->tpc_total; i++) {
-               do {
-                       gpc = (gpc + 1) % priv->gpc_nr;
-               } while (!tpcnr[gpc]);
-               tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--;
-
-               data[i / 8] |= tpc << ((i % 8) * 4);
-       }
-
-       nv_wr32(priv, GPC_BCAST(0x0980), data[0]);
-       nv_wr32(priv, GPC_BCAST(0x0984), data[1]);
-       nv_wr32(priv, GPC_BCAST(0x0988), data[2]);
-       nv_wr32(priv, GPC_BCAST(0x098c), data[3]);
-
-       for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
-               nv_wr32(priv, GPC_UNIT(gpc, 0x0914), priv->magic_not_rop_nr << 8 |
-                                                 priv->tpc_nr[gpc]);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | priv->tpc_total);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918);
-       }
-
-       nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918);
-       nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800));
-}
-
-static void
-nve0_graph_init_gpc_1(struct nvc0_graph_priv *priv)
-{
-       int gpc, tpc;
-
-       for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
-               nv_wr32(priv, GPC_UNIT(gpc, 0x3038), 0xc0000000);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000);
-               for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
-                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff);
-                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff);
-                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000);
-                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000);
-                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000);
-                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe);
-                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f);
-               }
-               nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff);
-               nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff);
-       }
-}
-
-static void
-nve0_graph_init_rop(struct nvc0_graph_priv *priv)
-{
-       int rop;
-
-       for (rop = 0; rop < priv->rop_nr; rop++) {
-               nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000);
-               nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000);
-               nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff);
-               nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff);
-       }
-}
-
-static int
-nve0_graph_init_ctxctl(struct nvc0_graph_priv *priv)
-{
-       u32 r000260;
-       int i;
-
-       if (priv->firmware) {
-               /* load fuc microcode */
-               r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
-               nvc0_graph_init_fw(priv, 0x409000, &priv->fuc409c, &priv->fuc409d);
-               nvc0_graph_init_fw(priv, 0x41a000, &priv->fuc41ac, &priv->fuc41ad);
-               nv_wr32(priv, 0x000260, r000260);
-
-               /* start both of them running */
-               nv_wr32(priv, 0x409840, 0xffffffff);
-               nv_wr32(priv, 0x41a10c, 0x00000000);
-               nv_wr32(priv, 0x40910c, 0x00000000);
-               nv_wr32(priv, 0x41a100, 0x00000002);
-               nv_wr32(priv, 0x409100, 0x00000002);
-               if (!nv_wait(priv, 0x409800, 0x00000001, 0x00000001))
-                       nv_error(priv, "0x409800 wait failed\n");
-
-               nv_wr32(priv, 0x409840, 0xffffffff);
-               nv_wr32(priv, 0x409500, 0x7fffffff);
-               nv_wr32(priv, 0x409504, 0x00000021);
-
-               nv_wr32(priv, 0x409840, 0xffffffff);
-               nv_wr32(priv, 0x409500, 0x00000000);
-               nv_wr32(priv, 0x409504, 0x00000010);
-               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
-                       nv_error(priv, "fuc09 req 0x10 timeout\n");
-                       return -EBUSY;
-               }
-               priv->size = nv_rd32(priv, 0x409800);
-
-               nv_wr32(priv, 0x409840, 0xffffffff);
-               nv_wr32(priv, 0x409500, 0x00000000);
-               nv_wr32(priv, 0x409504, 0x00000016);
-               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
-                       nv_error(priv, "fuc09 req 0x16 timeout\n");
-                       return -EBUSY;
-               }
-
-               nv_wr32(priv, 0x409840, 0xffffffff);
-               nv_wr32(priv, 0x409500, 0x00000000);
-               nv_wr32(priv, 0x409504, 0x00000025);
-               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
-                       nv_error(priv, "fuc09 req 0x25 timeout\n");
-                       return -EBUSY;
-               }
-
-               nv_wr32(priv, 0x409800, 0x00000000);
-               nv_wr32(priv, 0x409500, 0x00000001);
-               nv_wr32(priv, 0x409504, 0x00000030);
-               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
-                       nv_error(priv, "fuc09 req 0x30 timeout\n");
-                       return -EBUSY;
-               }
-
-               nv_wr32(priv, 0x409810, 0xb00095c8);
-               nv_wr32(priv, 0x409800, 0x00000000);
-               nv_wr32(priv, 0x409500, 0x00000001);
-               nv_wr32(priv, 0x409504, 0x00000031);
-               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
-                       nv_error(priv, "fuc09 req 0x31 timeout\n");
-                       return -EBUSY;
-               }
-
-               nv_wr32(priv, 0x409810, 0x00080420);
-               nv_wr32(priv, 0x409800, 0x00000000);
-               nv_wr32(priv, 0x409500, 0x00000001);
-               nv_wr32(priv, 0x409504, 0x00000032);
-               if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) {
-                       nv_error(priv, "fuc09 req 0x32 timeout\n");
-                       return -EBUSY;
-               }
-
-               nv_wr32(priv, 0x409614, 0x00000070);
-               nv_wr32(priv, 0x409614, 0x00000770);
-               nv_wr32(priv, 0x40802c, 0x00000001);
-
-               if (priv->data == NULL) {
-                       int ret = nve0_grctx_generate(priv);
-                       if (ret) {
-                               nv_error(priv, "failed to construct context\n");
-                               return ret;
-                       }
-               }
-
-               return 0;
-       }
-
-       /* load HUB microcode */
-       r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
-       nv_wr32(priv, 0x4091c0, 0x01000000);
-       for (i = 0; i < sizeof(nve0_grhub_data) / 4; i++)
-               nv_wr32(priv, 0x4091c4, nve0_grhub_data[i]);
-
-       nv_wr32(priv, 0x409180, 0x01000000);
-       for (i = 0; i < sizeof(nve0_grhub_code) / 4; i++) {
-               if ((i & 0x3f) == 0)
-                       nv_wr32(priv, 0x409188, i >> 6);
-               nv_wr32(priv, 0x409184, nve0_grhub_code[i]);
-       }
-
-       /* load GPC microcode */
-       nv_wr32(priv, 0x41a1c0, 0x01000000);
-       for (i = 0; i < sizeof(nve0_grgpc_data) / 4; i++)
-               nv_wr32(priv, 0x41a1c4, nve0_grgpc_data[i]);
-
-       nv_wr32(priv, 0x41a180, 0x01000000);
-       for (i = 0; i < sizeof(nve0_grgpc_code) / 4; i++) {
-               if ((i & 0x3f) == 0)
-                       nv_wr32(priv, 0x41a188, i >> 6);
-               nv_wr32(priv, 0x41a184, nve0_grgpc_code[i]);
-       }
-       nv_wr32(priv, 0x000260, r000260);
-
-       /* start HUB ucode running, it'll init the GPCs */
-       nv_wr32(priv, 0x409800, nv_device(priv)->chipset);
-       nv_wr32(priv, 0x40910c, 0x00000000);
-       nv_wr32(priv, 0x409100, 0x00000002);
-       if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) {
-               nv_error(priv, "HUB_INIT timed out\n");
-               nvc0_graph_ctxctl_debug(priv);
-               return -EBUSY;
-       }
-
-       priv->size = nv_rd32(priv, 0x409804);
-       if (priv->data == NULL) {
-               int ret = nve0_grctx_generate(priv);
-               if (ret) {
-                       nv_error(priv, "failed to construct context\n");
-                       return ret;
-               }
-       }
-
-       return 0;
-}
-
-static int
-nve0_graph_init(struct nouveau_object *object)
-{
-       struct nvc0_graph_priv *priv = (void *)object;
-       int ret;
-
-       ret = nouveau_graph_init(&priv->base);
-       if (ret)
-               return ret;
-
-       nve0_graph_init_obj418880(priv);
-       nve0_graph_init_regs(priv);
-       nve0_graph_init_gpc_0(priv);
-
-       nv_wr32(priv, 0x400500, 0x00010001);
-       nv_wr32(priv, 0x400100, 0xffffffff);
-       nv_wr32(priv, 0x40013c, 0xffffffff);
-
-       nve0_graph_init_units(priv);
-       nve0_graph_init_gpc_1(priv);
-       nve0_graph_init_rop(priv);
-
-       nv_wr32(priv, 0x400108, 0xffffffff);
-       nv_wr32(priv, 0x400138, 0xffffffff);
-       nv_wr32(priv, 0x400118, 0xffffffff);
-       nv_wr32(priv, 0x400130, 0xffffffff);
-       nv_wr32(priv, 0x40011c, 0xffffffff);
-       nv_wr32(priv, 0x400134, 0xffffffff);
-       nv_wr32(priv, 0x400054, 0x34ce3464);
-
-       ret = nve0_graph_init_ctxctl(priv);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-struct nouveau_oclass
-nve0_graph_oclass = {
-       .handle = NV_ENGINE(GR, 0xe0),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nve0_graph_ctor,
-               .dtor = nvc0_graph_dtor,
-               .init = nve0_graph_init,
-               .fini = _nouveau_graph_fini,
-       },
-};
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c
new file mode 100644 (file)
index 0000000..05ec09c
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * 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 "nvc0.h"
+
+/*******************************************************************************
+ * Graphics object classes
+ ******************************************************************************/
+
+static struct nouveau_oclass
+nve4_graph_sclass[] = {
+       { 0x902d, &nouveau_object_ofuncs },
+       { 0xa040, &nouveau_object_ofuncs },
+       { 0xa097, &nouveau_object_ofuncs },
+       { 0xa0c0, &nouveau_object_ofuncs },
+       {}
+};
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
+struct nvc0_graph_init
+nve4_graph_init_regs[] = {
+       { 0x400080,   1, 0x04, 0x003083c2 },
+       { 0x400088,   1, 0x04, 0x0001ffe7 },
+       { 0x40008c,   1, 0x04, 0x00000000 },
+       { 0x400090,   1, 0x04, 0x00000030 },
+       { 0x40013c,   1, 0x04, 0x003901f7 },
+       { 0x400140,   1, 0x04, 0x00000100 },
+       { 0x400144,   1, 0x04, 0x00000000 },
+       { 0x400148,   1, 0x04, 0x00000110 },
+       { 0x400138,   1, 0x04, 0x00000000 },
+       { 0x400130,   2, 0x04, 0x00000000 },
+       { 0x400124,   1, 0x04, 0x00000002 },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_graph_init_unk58xx[] = {
+       { 0x405844,   1, 0x04, 0x00ffffff },
+       { 0x405850,   1, 0x04, 0x00000000 },
+       { 0x405900,   1, 0x04, 0x0000ff34 },
+       { 0x405908,   1, 0x04, 0x00000000 },
+       { 0x405928,   1, 0x04, 0x00000000 },
+       { 0x40592c,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_graph_init_unk70xx[] = {
+       { 0x407010,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nve4_graph_init_unk5bxx[] = {
+       { 0x405b50,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_graph_init_gpc[] = {
+       { 0x418408,   1, 0x04, 0x00000000 },
+       { 0x4184a0,   1, 0x04, 0x00000000 },
+       { 0x4184a4,   2, 0x04, 0x00000000 },
+       { 0x418604,   1, 0x04, 0x00000000 },
+       { 0x418680,   1, 0x04, 0x00000000 },
+       { 0x418714,   1, 0x04, 0x00000000 },
+       { 0x418384,   1, 0x04, 0x00000000 },
+       { 0x418814,   3, 0x04, 0x00000000 },
+       { 0x418b04,   1, 0x04, 0x00000000 },
+       { 0x4188c8,   2, 0x04, 0x00000000 },
+       { 0x4188d0,   1, 0x04, 0x00010000 },
+       { 0x4188d4,   1, 0x04, 0x00000001 },
+       { 0x418910,   1, 0x04, 0x00010001 },
+       { 0x418914,   1, 0x04, 0x00000301 },
+       { 0x418918,   1, 0x04, 0x00800000 },
+       { 0x418980,   1, 0x04, 0x77777770 },
+       { 0x418984,   3, 0x04, 0x77777777 },
+       { 0x418c04,   1, 0x04, 0x00000000 },
+       { 0x418c64,   1, 0x04, 0x00000000 },
+       { 0x418c68,   1, 0x04, 0x00000000 },
+       { 0x418c88,   1, 0x04, 0x00000000 },
+       { 0x418cb4,   2, 0x04, 0x00000000 },
+       { 0x418d00,   1, 0x04, 0x00000000 },
+       { 0x418d28,   1, 0x04, 0x00000000 },
+       { 0x418d2c,   1, 0x04, 0x00000000 },
+       { 0x418f00,   1, 0x04, 0x00000000 },
+       { 0x418f08,   1, 0x04, 0x00000000 },
+       { 0x418f20,   2, 0x04, 0x00000000 },
+       { 0x418e00,   1, 0x04, 0x00000060 },
+       { 0x418e08,   1, 0x04, 0x00000000 },
+       { 0x418e1c,   1, 0x04, 0x00000000 },
+       { 0x418e20,   1, 0x04, 0x00000000 },
+       { 0x41900c,   1, 0x04, 0x00000000 },
+       { 0x419018,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nve4_graph_init_tpc[] = {
+       { 0x419d0c,   1, 0x04, 0x00000000 },
+       { 0x419d10,   1, 0x04, 0x00000014 },
+       { 0x419ab0,   1, 0x04, 0x00000000 },
+       { 0x419ac8,   1, 0x04, 0x00000000 },
+       { 0x419ab8,   1, 0x04, 0x000000e7 },
+       { 0x419abc,   2, 0x04, 0x00000000 },
+       { 0x419ab4,   1, 0x04, 0x00000000 },
+       { 0x41980c,   1, 0x04, 0x00000010 },
+       { 0x419844,   1, 0x04, 0x00000000 },
+       { 0x419850,   1, 0x04, 0x00000004 },
+       { 0x419854,   2, 0x04, 0x00000000 },
+       { 0x419c98,   1, 0x04, 0x00000000 },
+       { 0x419ca8,   1, 0x04, 0x00000000 },
+       { 0x419cb0,   1, 0x04, 0x01000000 },
+       { 0x419cb4,   1, 0x04, 0x00000000 },
+       { 0x419cb8,   1, 0x04, 0x00b08bea },
+       { 0x419c84,   1, 0x04, 0x00010384 },
+       { 0x419cbc,   1, 0x04, 0x28137646 },
+       { 0x419cc0,   2, 0x04, 0x00000000 },
+       { 0x419c80,   1, 0x04, 0x00020232 },
+       { 0x419c0c,   1, 0x04, 0x00000000 },
+       { 0x419e00,   1, 0x04, 0x00000000 },
+       { 0x419ea0,   1, 0x04, 0x00000000 },
+       { 0x419ee4,   1, 0x04, 0x00000000 },
+       { 0x419ea4,   1, 0x04, 0x00000100 },
+       { 0x419ea8,   1, 0x04, 0x00000000 },
+       { 0x419eb4,   1, 0x04, 0x00000000 },
+       { 0x419eb8,   3, 0x04, 0x00000000 },
+       { 0x419edc,   1, 0x04, 0x00000000 },
+       { 0x419f00,   1, 0x04, 0x00000000 },
+       { 0x419f74,   1, 0x04, 0x00000555 },
+       {}
+};
+
+struct nvc0_graph_init
+nve4_graph_init_unk[] = {
+       { 0x41be04,   1, 0x04, 0x00000000 },
+       { 0x41be08,   1, 0x04, 0x00000004 },
+       { 0x41be0c,   1, 0x04, 0x00000000 },
+       { 0x41be10,   1, 0x04, 0x003b8bc7 },
+       { 0x41be14,   2, 0x04, 0x00000000 },
+       { 0x41bfd4,   1, 0x04, 0x00800000 },
+       { 0x41bfdc,   1, 0x04, 0x00000000 },
+       { 0x41bff8,   1, 0x04, 0x00000000 },
+       { 0x41bffc,   1, 0x04, 0x00000000 },
+       { 0x41becc,   1, 0x04, 0x00000000 },
+       { 0x41bee8,   1, 0x04, 0x00000000 },
+       { 0x41beec,   1, 0x04, 0x00000000 },
+       {}
+};
+
+struct nvc0_graph_init
+nve4_graph_init_unk88xx[] = {
+       { 0x40880c,   1, 0x04, 0x00000000 },
+       { 0x408850,   1, 0x04, 0x00000004 },
+       { 0x408910,   9, 0x04, 0x00000000 },
+       { 0x408950,   1, 0x04, 0x00000000 },
+       { 0x408954,   1, 0x04, 0x0000ffff },
+       { 0x408958,   1, 0x04, 0x00000034 },
+       { 0x408984,   1, 0x04, 0x00000000 },
+       { 0x408988,   1, 0x04, 0x08040201 },
+       { 0x40898c,   1, 0x04, 0x80402010 },
+       {}
+};
+
+int
+nve4_graph_init(struct nouveau_object *object)
+{
+       struct nvc0_graph_oclass *oclass = (void *)object->oclass;
+       struct nvc0_graph_priv *priv = (void *)object;
+       const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total);
+       u32 data[TPC_MAX / 8] = {};
+       u8  tpcnr[GPC_MAX];
+       int gpc, tpc, rop;
+       int ret, i;
+
+       ret = nouveau_graph_init(&priv->base);
+       if (ret)
+               return ret;
+
+       nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000);
+       nv_wr32(priv, GPC_BCAST(0x08a4), 0x00000000);
+       nv_wr32(priv, GPC_BCAST(0x0888), 0x00000000);
+       nv_wr32(priv, GPC_BCAST(0x088c), 0x00000000);
+       nv_wr32(priv, GPC_BCAST(0x0890), 0x00000000);
+       nv_wr32(priv, GPC_BCAST(0x0894), 0x00000000);
+       nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8);
+       nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8);
+
+       for (i = 0; oclass->mmio[i]; i++)
+               nvc0_graph_mmio(priv, oclass->mmio[i]);
+
+       nv_wr32(priv, GPC_UNIT(0, 0x3018), 0x00000001);
+
+       memset(data, 0x00, sizeof(data));
+       memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr));
+       for (i = 0, gpc = -1; i < priv->tpc_total; i++) {
+               do {
+                       gpc = (gpc + 1) % priv->gpc_nr;
+               } while (!tpcnr[gpc]);
+               tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--;
+
+               data[i / 8] |= tpc << ((i % 8) * 4);
+       }
+
+       nv_wr32(priv, GPC_BCAST(0x0980), data[0]);
+       nv_wr32(priv, GPC_BCAST(0x0984), data[1]);
+       nv_wr32(priv, GPC_BCAST(0x0988), data[2]);
+       nv_wr32(priv, GPC_BCAST(0x098c), data[3]);
+
+       for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+               nv_wr32(priv, GPC_UNIT(gpc, 0x0914),
+                       priv->magic_not_rop_nr << 8 | priv->tpc_nr[gpc]);
+               nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 |
+                       priv->tpc_total);
+               nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918);
+       }
+
+       nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918);
+       nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800));
+
+       nv_wr32(priv, 0x400500, 0x00010001);
+
+       nv_wr32(priv, 0x400100, 0xffffffff);
+       nv_wr32(priv, 0x40013c, 0xffffffff);
+
+       nv_wr32(priv, 0x409ffc, 0x00000000);
+       nv_wr32(priv, 0x409c14, 0x00003e3e);
+       nv_wr32(priv, 0x409c24, 0x000f0001);
+       nv_wr32(priv, 0x404000, 0xc0000000);
+       nv_wr32(priv, 0x404600, 0xc0000000);
+       nv_wr32(priv, 0x408030, 0xc0000000);
+       nv_wr32(priv, 0x404490, 0xc0000000);
+       nv_wr32(priv, 0x406018, 0xc0000000);
+       nv_wr32(priv, 0x407020, 0x40000000);
+       nv_wr32(priv, 0x405840, 0xc0000000);
+       nv_wr32(priv, 0x405844, 0x00ffffff);
+       nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008);
+       nv_mask(priv, 0x419eb4, 0x00001000, 0x00001000);
+
+       for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+               nv_wr32(priv, GPC_UNIT(gpc, 0x3038), 0xc0000000);
+               nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000);
+               nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000);
+               nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000);
+               nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000);
+               for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
+                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff);
+                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff);
+                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000);
+                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000);
+                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000);
+                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe);
+                       nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f);
+               }
+               nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff);
+               nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff);
+       }
+
+       for (rop = 0; rop < priv->rop_nr; rop++) {
+               nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000);
+               nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000);
+               nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff);
+               nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff);
+       }
+
+       nv_wr32(priv, 0x400108, 0xffffffff);
+       nv_wr32(priv, 0x400138, 0xffffffff);
+       nv_wr32(priv, 0x400118, 0xffffffff);
+       nv_wr32(priv, 0x400130, 0xffffffff);
+       nv_wr32(priv, 0x40011c, 0xffffffff);
+       nv_wr32(priv, 0x400134, 0xffffffff);
+
+       nv_wr32(priv, 0x400054, 0x34ce3464);
+       return nvc0_graph_init_ctxctl(priv);
+}
+
+static struct nvc0_graph_init *
+nve4_graph_init_mmio[] = {
+       nve4_graph_init_regs,
+       nvc0_graph_init_unk40xx,
+       nvc0_graph_init_unk44xx,
+       nvc0_graph_init_unk78xx,
+       nvc0_graph_init_unk60xx,
+       nvd9_graph_init_unk64xx,
+       nve4_graph_init_unk58xx,
+       nvc0_graph_init_unk80xx,
+       nve4_graph_init_unk70xx,
+       nve4_graph_init_unk5bxx,
+       nve4_graph_init_gpc,
+       nve4_graph_init_tpc,
+       nve4_graph_init_unk,
+       nve4_graph_init_unk88xx,
+       NULL
+};
+
+#include "fuc/hubnve0.fuc.h"
+
+static struct nvc0_graph_ucode
+nve4_graph_fecs_ucode = {
+       .code.data = nve0_grhub_code,
+       .code.size = sizeof(nve0_grhub_code),
+       .data.data = nve0_grhub_data,
+       .data.size = sizeof(nve0_grhub_data),
+};
+
+#include "fuc/gpcnve0.fuc.h"
+
+static struct nvc0_graph_ucode
+nve4_graph_gpccs_ucode = {
+       .code.data = nve0_grgpc_code,
+       .code.size = sizeof(nve0_grgpc_code),
+       .data.data = nve0_grgpc_data,
+       .data.size = sizeof(nve0_grgpc_data),
+};
+
+struct nouveau_oclass *
+nve4_graph_oclass = &(struct nvc0_graph_oclass) {
+       .base.handle = NV_ENGINE(GR, 0xe4),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_ctor,
+               .dtor = nvc0_graph_dtor,
+               .init = nve4_graph_init,
+               .fini = _nouveau_graph_fini,
+       },
+       .cclass = &nve4_grctx_oclass,
+       .sclass = nve4_graph_sclass,
+       .mmio = nve4_graph_init_mmio,
+       .fecs.ucode = &nve4_graph_fecs_ucode,
+       .gpccs.ucode = &nve4_graph_gpccs_ucode,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c
new file mode 100644 (file)
index 0000000..2f0ac78
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * 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 "nvc0.h"
+
+/*******************************************************************************
+ * Graphics object classes
+ ******************************************************************************/
+
+static struct nouveau_oclass
+nvf0_graph_sclass[] = {
+       { 0x902d, &nouveau_object_ofuncs },
+       { 0xa140, &nouveau_object_ofuncs },
+       { 0xa197, &nouveau_object_ofuncs },
+       { 0xa1c0, &nouveau_object_ofuncs },
+       {}
+};
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
+static struct nvc0_graph_init
+nvf0_graph_init_unk40xx[] = {
+       { 0x40415c,   1, 0x04, 0x00000000 },
+       { 0x404170,   1, 0x04, 0x00000000 },
+       { 0x4041b4,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_graph_init_unk58xx[] = {
+       { 0x405844,   1, 0x04, 0x00ffffff },
+       { 0x405850,   1, 0x04, 0x00000000 },
+       { 0x405900,   1, 0x04, 0x0000ff00 },
+       { 0x405908,   1, 0x04, 0x00000000 },
+       { 0x405928,   1, 0x04, 0x00000000 },
+       { 0x40592c,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_graph_init_unk70xx[] = {
+       { 0x407010,   1, 0x04, 0x00000000 },
+       { 0x407040,   1, 0x04, 0x80440424 },
+       { 0x407048,   1, 0x04, 0x0000000a },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_graph_init_unk5bxx[] = {
+       { 0x405b44,   1, 0x04, 0x00000000 },
+       { 0x405b50,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_graph_init_gpc[] = {
+       { 0x418408,   1, 0x04, 0x00000000 },
+       { 0x4184a0,   1, 0x04, 0x00000000 },
+       { 0x4184a4,   2, 0x04, 0x00000000 },
+       { 0x418604,   1, 0x04, 0x00000000 },
+       { 0x418680,   1, 0x04, 0x00000000 },
+       { 0x418714,   1, 0x04, 0x00000000 },
+       { 0x418384,   1, 0x04, 0x00000000 },
+       { 0x418814,   3, 0x04, 0x00000000 },
+       { 0x418b04,   1, 0x04, 0x00000000 },
+       { 0x4188c8,   2, 0x04, 0x00000000 },
+       { 0x4188d0,   1, 0x04, 0x00010000 },
+       { 0x4188d4,   1, 0x04, 0x00000001 },
+       { 0x418910,   1, 0x04, 0x00010001 },
+       { 0x418914,   1, 0x04, 0x00000301 },
+       { 0x418918,   1, 0x04, 0x00800000 },
+       { 0x418980,   1, 0x04, 0x77777770 },
+       { 0x418984,   3, 0x04, 0x77777777 },
+       { 0x418c04,   1, 0x04, 0x00000000 },
+       { 0x418c64,   1, 0x04, 0x00000000 },
+       { 0x418c68,   1, 0x04, 0x00000000 },
+       { 0x418c88,   1, 0x04, 0x00000000 },
+       { 0x418cb4,   2, 0x04, 0x00000000 },
+       { 0x418d00,   1, 0x04, 0x00000000 },
+       { 0x418d28,   1, 0x04, 0x00000000 },
+       { 0x418d2c,   1, 0x04, 0x00000000 },
+       { 0x418f00,   1, 0x04, 0x00000400 },
+       { 0x418f08,   1, 0x04, 0x00000000 },
+       { 0x418f20,   1, 0x04, 0x00000000 },
+       { 0x418f24,   1, 0x04, 0x00000000 },
+       { 0x418e00,   1, 0x04, 0x00000000 },
+       { 0x418e08,   1, 0x04, 0x00000000 },
+       { 0x418e1c,   2, 0x04, 0x00000000 },
+       { 0x41900c,   1, 0x04, 0x00000000 },
+       { 0x419018,   1, 0x04, 0x00000000 },
+       {}
+};
+
+static struct nvc0_graph_init
+nvf0_graph_init_tpc[] = {
+       { 0x419d0c,   1, 0x04, 0x00000000 },
+       { 0x419d10,   1, 0x04, 0x00000014 },
+       { 0x419ab0,   1, 0x04, 0x00000000 },
+       { 0x419ac8,   1, 0x04, 0x00000000 },
+       { 0x419ab8,   1, 0x04, 0x000000e7 },
+       { 0x419aec,   1, 0x04, 0x00000000 },
+       { 0x419abc,   2, 0x04, 0x00000000 },
+       { 0x419ab4,   1, 0x04, 0x00000000 },
+       { 0x419aa8,   2, 0x04, 0x00000000 },
+       { 0x41980c,   1, 0x04, 0x00000010 },
+       { 0x419844,   1, 0x04, 0x00000000 },
+       { 0x419850,   1, 0x04, 0x00000004 },
+       { 0x419854,   2, 0x04, 0x00000000 },
+       { 0x419c98,   1, 0x04, 0x00000000 },
+       { 0x419ca8,   1, 0x04, 0x00000000 },
+       { 0x419cb0,   1, 0x04, 0x01000000 },
+       { 0x419cb4,   1, 0x04, 0x00000000 },
+       { 0x419cb8,   1, 0x04, 0x00b08bea },
+       { 0x419c84,   1, 0x04, 0x00010384 },
+       { 0x419cbc,   1, 0x04, 0x281b3646 },
+       { 0x419cc0,   2, 0x04, 0x00000000 },
+       { 0x419c80,   1, 0x04, 0x00020230 },
+       { 0x419ccc,   2, 0x04, 0x00000000 },
+       { 0x419c0c,   1, 0x04, 0x00000000 },
+       { 0x419e00,   1, 0x04, 0x00000080 },
+       { 0x419ea0,   1, 0x04, 0x00000000 },
+       { 0x419ee4,   1, 0x04, 0x00000000 },
+       { 0x419ea4,   1, 0x04, 0x00000100 },
+       { 0x419ea8,   1, 0x04, 0x00000000 },
+       { 0x419eb4,   1, 0x04, 0x00000000 },
+       { 0x419ebc,   2, 0x04, 0x00000000 },
+       { 0x419edc,   1, 0x04, 0x00000000 },
+       { 0x419f00,   1, 0x04, 0x00000000 },
+       { 0x419ed0,   1, 0x04, 0x00003234 },
+       { 0x419f74,   1, 0x04, 0x00015555 },
+       { 0x419f80,   4, 0x04, 0x00000000 },
+       {}
+};
+
+static int
+nvf0_graph_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nvc0_graph_priv *priv = (void *)object;
+       static const struct {
+               u32 addr;
+               u32 data;
+       } magic[] = {
+               { 0x020520, 0xfffffffc },
+               { 0x020524, 0xfffffffe },
+               { 0x020524, 0xfffffffc },
+               { 0x020524, 0xfffffff8 },
+               { 0x020524, 0xffffffe0 },
+               { 0x020530, 0xfffffffe },
+               { 0x02052c, 0xfffffffa },
+               { 0x02052c, 0xfffffff0 },
+               { 0x02052c, 0xffffffc0 },
+               { 0x02052c, 0xffffff00 },
+               { 0x02052c, 0xfffffc00 },
+               { 0x02052c, 0xfffcfc00 },
+               { 0x02052c, 0xfff0fc00 },
+               { 0x02052c, 0xff80fc00 },
+               { 0x020528, 0xfffffffe },
+               { 0x020528, 0xfffffffc },
+       };
+       int i;
+
+       nv_mask(priv, 0x000200, 0x08001000, 0x00000000);
+       nv_mask(priv, 0x0206b4, 0x00000000, 0x00000000);
+       for (i = 0; i < ARRAY_SIZE(magic); i++) {
+               nv_wr32(priv, magic[i].addr, magic[i].data);
+               nv_wait(priv, magic[i].addr, 0x80000000, 0x00000000);
+       }
+
+       return nouveau_graph_fini(&priv->base, suspend);
+}
+
+static struct nvc0_graph_init *
+nvf0_graph_init_mmio[] = {
+       nve4_graph_init_regs,
+       nvf0_graph_init_unk40xx,
+       nvc0_graph_init_unk44xx,
+       nvc0_graph_init_unk78xx,
+       nvc0_graph_init_unk60xx,
+       nvd9_graph_init_unk64xx,
+       nvf0_graph_init_unk58xx,
+       nvc0_graph_init_unk80xx,
+       nvf0_graph_init_unk70xx,
+       nvf0_graph_init_unk5bxx,
+       nvf0_graph_init_gpc,
+       nvf0_graph_init_tpc,
+       nve4_graph_init_unk,
+       nve4_graph_init_unk88xx,
+       NULL
+};
+
+#include "fuc/hubnvf0.fuc.h"
+
+static struct nvc0_graph_ucode
+nvf0_graph_fecs_ucode = {
+       .code.data = nvf0_grhub_code,
+       .code.size = sizeof(nvf0_grhub_code),
+       .data.data = nvf0_grhub_data,
+       .data.size = sizeof(nvf0_grhub_data),
+};
+
+#include "fuc/gpcnvf0.fuc.h"
+
+static struct nvc0_graph_ucode
+nvf0_graph_gpccs_ucode = {
+       .code.data = nvf0_grgpc_code,
+       .code.size = sizeof(nvf0_grgpc_code),
+       .data.data = nvf0_grgpc_data,
+       .data.size = sizeof(nvf0_grgpc_data),
+};
+
+struct nouveau_oclass *
+nvf0_graph_oclass = &(struct nvc0_graph_oclass) {
+       .base.handle = NV_ENGINE(GR, 0xf0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_ctor,
+               .dtor = nvc0_graph_dtor,
+               .init = nve4_graph_init,
+               .fini = nvf0_graph_fini,
+       },
+       .cclass = &nvf0_grctx_oclass,
+       .sclass =  nvf0_graph_sclass,
+       .mmio = nvf0_graph_init_mmio,
+       .fecs.ucode = 0 ? &nvf0_graph_fecs_ucode : NULL,
+       .gpccs.ucode = &nvf0_graph_gpccs_ucode,
+}.base;
index bc7d12b..37a2bd9 100644 (file)
@@ -125,13 +125,6 @@ nv50_mpeg_cclass = {
  * PMPEG engine/subdev functions
  ******************************************************************************/
 
-int
-nv50_mpeg_tlb_flush(struct nouveau_engine *engine)
-{
-       nv50_vm_flush_engine(&engine->base, 0x08);
-       return 0;
-}
-
 void
 nv50_mpeg_intr(struct nouveau_subdev *subdev)
 {
@@ -191,7 +184,6 @@ nv50_mpeg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_subdev(priv)->intr = nv50_vpe_intr;
        nv_engine(priv)->cclass = &nv50_mpeg_cclass;
        nv_engine(priv)->sclass = nv50_mpeg_sclass;
-       nv_engine(priv)->tlb_flush = nv50_mpeg_tlb_flush;
        return 0;
 }
 
index 8f805b4..96f5aa9 100644 (file)
@@ -88,7 +88,6 @@ nv84_mpeg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_subdev(priv)->intr = nv50_mpeg_intr;
        nv_engine(priv)->cclass = &nv84_mpeg_cclass;
        nv_engine(priv)->sclass = nv84_mpeg_sclass;
-       nv_engine(priv)->tlb_flush = nv50_mpeg_tlb_flush;
        return 0;
 }
 
index ebf0d86..98072c1 100644 (file)
@@ -22,8 +22,7 @@
  * Authors: Maarten Lankhorst
  */
 
-#include <core/falcon.h>
-
+#include <engine/falcon.h>
 #include <engine/ppp.h>
 
 struct nvc0_ppp_priv {
index 261cd96..fd6272b 100644 (file)
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  * OTHER DEALINGS IN THE SOFTWARE.
  *
- * Authors: Ben Skeggs
+ * Authors: Ben Skeggs, Ilia Mirkin
  */
 
-#include <core/engctx.h>
-#include <core/class.h>
-
+#include <engine/xtensa.h>
 #include <engine/vp.h>
 
-struct nv84_vp_priv {
-       struct nouveau_engine base;
-};
-
 /*******************************************************************************
  * VP object classes
  ******************************************************************************/
 
 static struct nouveau_oclass
 nv84_vp_sclass[] = {
+       { 0x7476, &nouveau_object_ofuncs },
        {},
 };
 
@@ -48,7 +43,7 @@ static struct nouveau_oclass
 nv84_vp_cclass = {
        .handle = NV_ENGCTX(VP, 0x84),
        .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = _nouveau_engctx_ctor,
+               .ctor = _nouveau_xtensa_engctx_ctor,
                .dtor = _nouveau_engctx_dtor,
                .init = _nouveau_engctx_init,
                .fini = _nouveau_engctx_fini,
@@ -66,10 +61,10 @@ nv84_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
             struct nouveau_oclass *oclass, void *data, u32 size,
             struct nouveau_object **pobject)
 {
-       struct nv84_vp_priv *priv;
+       struct nouveau_xtensa *priv;
        int ret;
 
-       ret = nouveau_engine_create(parent, engine, oclass, true,
+       ret = nouveau_xtensa_create(parent, engine, oclass, 0xf000, true,
                                    "PVP", "vp", &priv);
        *pobject = nv_object(priv);
        if (ret)
@@ -78,6 +73,8 @@ nv84_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        nv_subdev(priv)->unit = 0x01020000;
        nv_engine(priv)->cclass = &nv84_vp_cclass;
        nv_engine(priv)->sclass = nv84_vp_sclass;
+       priv->fifo_val = 0x111;
+       priv->unkd28 = 0x9c544;
        return 0;
 }
 
@@ -86,8 +83,10 @@ nv84_vp_oclass = {
        .handle = NV_ENGINE(VP, 0x84),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv84_vp_ctor,
-               .dtor = _nouveau_engine_dtor,
-               .init = _nouveau_engine_init,
-               .fini = _nouveau_engine_fini,
+               .dtor = _nouveau_xtensa_dtor,
+               .init = _nouveau_xtensa_init,
+               .fini = _nouveau_xtensa_fini,
+               .rd32 = _nouveau_xtensa_rd32,
+               .wr32 = _nouveau_xtensa_wr32,
        },
 };
diff --git a/drivers/gpu/drm/nouveau/core/engine/vp/nv98.c b/drivers/gpu/drm/nouveau/core/engine/vp/nv98.c
new file mode 100644 (file)
index 0000000..8a8236b
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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/engctx.h>
+#include <core/class.h>
+
+#include <engine/vp.h>
+
+struct nv98_vp_priv {
+       struct nouveau_engine base;
+};
+
+/*******************************************************************************
+ * VP object classes
+ ******************************************************************************/
+
+static struct nouveau_oclass
+nv98_vp_sclass[] = {
+       {},
+};
+
+/*******************************************************************************
+ * PVP context
+ ******************************************************************************/
+
+static struct nouveau_oclass
+nv98_vp_cclass = {
+       .handle = NV_ENGCTX(VP, 0x98),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_engctx_ctor,
+               .dtor = _nouveau_engctx_dtor,
+               .init = _nouveau_engctx_init,
+               .fini = _nouveau_engctx_fini,
+               .rd32 = _nouveau_engctx_rd32,
+               .wr32 = _nouveau_engctx_wr32,
+       },
+};
+
+/*******************************************************************************
+ * PVP engine/subdev functions
+ ******************************************************************************/
+
+static int
+nv98_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+            struct nouveau_oclass *oclass, void *data, u32 size,
+            struct nouveau_object **pobject)
+{
+       struct nv98_vp_priv *priv;
+       int ret;
+
+       ret = nouveau_engine_create(parent, engine, oclass, true,
+                                   "PVP", "vp", &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       nv_subdev(priv)->unit = 0x01020000;
+       nv_engine(priv)->cclass = &nv98_vp_cclass;
+       nv_engine(priv)->sclass = nv98_vp_sclass;
+       return 0;
+}
+
+struct nouveau_oclass
+nv98_vp_oclass = {
+       .handle = NV_ENGINE(VP, 0x98),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv98_vp_ctor,
+               .dtor = _nouveau_engine_dtor,
+               .init = _nouveau_engine_init,
+               .fini = _nouveau_engine_fini,
+       },
+};
index f761949..1879229 100644 (file)
@@ -22,8 +22,7 @@
  * Authors: Maarten Lankhorst
  */
 
-#include <core/falcon.h>
-
+#include <engine/falcon.h>
 #include <engine/vp.h>
 
 struct nvc0_vp_priv {
index 2384ce5..d28ecbf 100644 (file)
@@ -22,8 +22,7 @@
  * Authors: Ben Skeggs
  */
 
-#include <core/falcon.h>
-
+#include <engine/falcon.h>
 #include <engine/vp.h>
 
 struct nve0_vp_priv {
diff --git a/drivers/gpu/drm/nouveau/core/engine/xtensa.c b/drivers/gpu/drm/nouveau/core/engine/xtensa.c
new file mode 100644 (file)
index 0000000..0639bc5
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * 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 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.
+ */
+
+#include <engine/xtensa.h>
+
+u32
+_nouveau_xtensa_rd32(struct nouveau_object *object, u64 addr)
+{
+       struct nouveau_xtensa *xtensa = (void *)object;
+       return nv_rd32(xtensa, xtensa->addr + addr);
+}
+
+void
+_nouveau_xtensa_wr32(struct nouveau_object *object, u64 addr, u32 data)
+{
+       struct nouveau_xtensa *xtensa = (void *)object;
+       nv_wr32(xtensa, xtensa->addr + addr, data);
+}
+
+int
+_nouveau_xtensa_engctx_ctor(struct nouveau_object *parent,
+                           struct nouveau_object *engine,
+                           struct nouveau_oclass *oclass, void *data, u32 size,
+                           struct nouveau_object **pobject)
+{
+       struct nouveau_engctx *engctx;
+       int ret;
+
+       ret = nouveau_engctx_create(parent, engine, oclass, NULL,
+                                   0x10000, 0x1000,
+                                   NVOBJ_FLAG_ZERO_ALLOC, &engctx);
+       *pobject = nv_object(engctx);
+       return ret;
+}
+
+void
+_nouveau_xtensa_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_xtensa *xtensa = (void *)subdev;
+       u32 unk104 = nv_ro32(xtensa, 0xd04);
+       u32 intr = nv_ro32(xtensa, 0xc20);
+       u32 chan = nv_ro32(xtensa, 0xc28);
+       u32 unk10c = nv_ro32(xtensa, 0xd0c);
+
+       if (intr & 0x10)
+               nv_warn(xtensa, "Watchdog interrupt, engine hung.\n");
+       nv_wo32(xtensa, 0xc20, intr);
+       intr = nv_ro32(xtensa, 0xc20);
+       if (unk104 == 0x10001 && unk10c == 0x200 && chan && !intr) {
+               nv_debug(xtensa, "Enabling FIFO_CTRL\n");
+               nv_mask(xtensa, xtensa->addr + 0xd94, 0, xtensa->fifo_val);
+       }
+}
+
+int
+nouveau_xtensa_create_(struct nouveau_object *parent,
+                      struct nouveau_object *engine,
+                      struct nouveau_oclass *oclass, u32 addr, bool enable,
+                      const char *iname, const char *fname,
+                      int length, void **pobject)
+{
+       struct nouveau_xtensa *xtensa;
+       int ret;
+
+       ret = nouveau_engine_create_(parent, engine, oclass, enable, iname,
+                                    fname, length, pobject);
+       xtensa = *pobject;
+       if (ret)
+               return ret;
+
+       nv_subdev(xtensa)->intr = _nouveau_xtensa_intr;
+
+       xtensa->addr = addr;
+
+       return 0;
+}
+
+int
+_nouveau_xtensa_init(struct nouveau_object *object)
+{
+       struct nouveau_device *device = nv_device(object);
+       struct nouveau_xtensa *xtensa = (void *)object;
+       const struct firmware *fw;
+       char name[32];
+       int i, ret;
+       u32 tmp;
+
+       ret = nouveau_engine_init(&xtensa->base);
+       if (ret)
+               return ret;
+
+       if (!xtensa->gpu_fw) {
+               snprintf(name, sizeof(name), "nouveau/nv84_xuc%03x",
+                        xtensa->addr >> 12);
+
+               ret = request_firmware(&fw, name, &device->pdev->dev);
+               if (ret) {
+                       nv_warn(xtensa, "unable to load firmware %s\n", name);
+                       return ret;
+               }
+
+               ret = nouveau_gpuobj_new(object, NULL, fw->size, 0x1000, 0,
+                                        &xtensa->gpu_fw);
+               if (ret) {
+                       release_firmware(fw);
+                       return ret;
+               }
+
+               nv_debug(xtensa, "Loading firmware to address: 0x%llx\n",
+                        xtensa->gpu_fw->addr);
+
+               for (i = 0; i < fw->size / 4; i++)
+                       nv_wo32(xtensa->gpu_fw, i * 4, *((u32 *)fw->data + i));
+               release_firmware(fw);
+       }
+
+       nv_wo32(xtensa, 0xd10, 0x1fffffff); /* ?? */
+       nv_wo32(xtensa, 0xd08, 0x0fffffff); /* ?? */
+
+       nv_wo32(xtensa, 0xd28, xtensa->unkd28); /* ?? */
+       nv_wo32(xtensa, 0xc20, 0x3f); /* INTR */
+       nv_wo32(xtensa, 0xd84, 0x3f); /* INTR_EN */
+
+       nv_wo32(xtensa, 0xcc0, xtensa->gpu_fw->addr >> 8); /* XT_REGION_BASE */
+       nv_wo32(xtensa, 0xcc4, 0x1c); /* XT_REGION_SETUP */
+       nv_wo32(xtensa, 0xcc8, xtensa->gpu_fw->size >> 8); /* XT_REGION_LIMIT */
+
+       tmp = nv_rd32(xtensa, 0x0);
+       nv_wo32(xtensa, 0xde0, tmp); /* SCRATCH_H2X */
+
+       nv_wo32(xtensa, 0xce8, 0xf); /* XT_REGION_SETUP */
+
+       nv_wo32(xtensa, 0xc20, 0x3f); /* INTR */
+       nv_wo32(xtensa, 0xd84, 0x3f); /* INTR_EN */
+
+       return 0;
+}
+
+int
+_nouveau_xtensa_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nouveau_xtensa *xtensa = (void *)object;
+
+       nv_wo32(xtensa, 0xd84, 0); /* INTR_EN */
+       nv_wo32(xtensa, 0xd94, 0); /* FIFO_CTRL */
+
+       if (!suspend)
+               nouveau_gpuobj_ref(NULL, &xtensa->gpu_fw);
+
+       return nouveau_engine_fini(&xtensa->base, suspend);
+}
index 05840f3..99b6600 100644 (file)
@@ -17,8 +17,7 @@ enum nv_subdev_type {
        NVDEV_SUBDEV_DEVINIT,
        NVDEV_SUBDEV_GPIO,
        NVDEV_SUBDEV_I2C,
-       NVDEV_SUBDEV_CLOCK,
-       NVDEV_SUBDEV_DEVINIT_LAST = NVDEV_SUBDEV_CLOCK,
+       NVDEV_SUBDEV_DEVINIT_LAST = NVDEV_SUBDEV_I2C,
 
        /* This grouping of subdevs are initialised right after they've
         * been created, and are allowed to assume any subdevs in the
@@ -35,6 +34,7 @@ enum nv_subdev_type {
        NVDEV_SUBDEV_VM,
        NVDEV_SUBDEV_BAR,
        NVDEV_SUBDEV_VOLT,
+       NVDEV_SUBDEV_CLOCK,
        NVDEV_SUBDEV_THERM,
 
        NVDEV_ENGINE_DMAOBJ,
@@ -49,6 +49,7 @@ enum nv_subdev_type {
        NVDEV_ENGINE_PPP,
        NVDEV_ENGINE_COPY0,
        NVDEV_ENGINE_COPY1,
+       NVDEV_ENGINE_COPY2,
        NVDEV_ENGINE_UNK1C1,
        NVDEV_ENGINE_VENC,
        NVDEV_ENGINE_DISP,
diff --git a/drivers/gpu/drm/nouveau/core/include/core/falcon.h b/drivers/gpu/drm/nouveau/core/include/core/falcon.h
deleted file mode 100644 (file)
index 1edec38..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-#ifndef __NOUVEAU_FALCON_H__
-#define __NOUVEAU_FALCON_H__
-
-#include <core/engine.h>
-#include <core/engctx.h>
-#include <core/gpuobj.h>
-
-struct nouveau_falcon_chan {
-       struct nouveau_engctx base;
-};
-
-#define nouveau_falcon_context_create(p,e,c,g,s,a,f,d)                         \
-       nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d))
-#define nouveau_falcon_context_destroy(d)                                      \
-       nouveau_engctx_destroy(&(d)->base)
-#define nouveau_falcon_context_init(d)                                         \
-       nouveau_engctx_init(&(d)->base)
-#define nouveau_falcon_context_fini(d,s)                                       \
-       nouveau_engctx_fini(&(d)->base, (s))
-
-#define _nouveau_falcon_context_ctor _nouveau_engctx_ctor
-#define _nouveau_falcon_context_dtor _nouveau_engctx_dtor
-#define _nouveau_falcon_context_init _nouveau_engctx_init
-#define _nouveau_falcon_context_fini _nouveau_engctx_fini
-#define _nouveau_falcon_context_rd32 _nouveau_engctx_rd32
-#define _nouveau_falcon_context_wr32 _nouveau_engctx_wr32
-
-struct nouveau_falcon_data {
-       bool external;
-};
-
-struct nouveau_falcon {
-       struct nouveau_engine base;
-
-       u32 addr;
-       u8  version;
-       u8  secret;
-
-       struct nouveau_gpuobj *core;
-       bool external;
-
-       struct {
-               u32 limit;
-               u32 *data;
-               u32  size;
-       } code;
-
-       struct {
-               u32 limit;
-               u32 *data;
-               u32  size;
-       } data;
-};
-
-#define nv_falcon(priv) (&(priv)->base)
-
-#define nouveau_falcon_create(p,e,c,b,d,i,f,r)                                 \
-       nouveau_falcon_create_((p), (e), (c), (b), (d), (i), (f),              \
-                              sizeof(**r),(void **)r)
-#define nouveau_falcon_destroy(p)                                              \
-       nouveau_engine_destroy(&(p)->base)
-#define nouveau_falcon_init(p) ({                                              \
-       struct nouveau_falcon *falcon = (p);                                   \
-       _nouveau_falcon_init(nv_object(falcon));                               \
-})
-#define nouveau_falcon_fini(p,s) ({                                            \
-       struct nouveau_falcon *falcon = (p);                                   \
-       _nouveau_falcon_fini(nv_object(falcon), (s));                          \
-})
-
-int nouveau_falcon_create_(struct nouveau_object *, struct nouveau_object *,
-                          struct nouveau_oclass *, u32, bool, const char *,
-                          const char *, int, void **);
-
-#define _nouveau_falcon_dtor _nouveau_engine_dtor
-int  _nouveau_falcon_init(struct nouveau_object *);
-int  _nouveau_falcon_fini(struct nouveau_object *, bool);
-u32  _nouveau_falcon_rd32(struct nouveau_object *, u64);
-void _nouveau_falcon_wr32(struct nouveau_object *, u64, u32);
-
-#endif
index 2514e81..2bf7d0e 100644 (file)
@@ -15,8 +15,6 @@ struct nouveau_mm {
        struct list_head nodes;
        struct list_head free;
 
-       struct mutex mutex;
-
        u32 block_size;
        int heap_nodes;
 };
index 13ccdf5..67662e2 100644 (file)
@@ -2,6 +2,7 @@
 #define __NOUVEAU_BSP_H__
 
 extern struct nouveau_oclass nv84_bsp_oclass;
+extern struct nouveau_oclass nv98_bsp_oclass;
 extern struct nouveau_oclass nvc0_bsp_oclass;
 extern struct nouveau_oclass nve0_bsp_oclass;
 
index 8cad2cf..316a28a 100644 (file)
@@ -8,5 +8,6 @@ extern struct nouveau_oclass nvc0_copy0_oclass;
 extern struct nouveau_oclass nvc0_copy1_oclass;
 extern struct nouveau_oclass nve0_copy0_oclass;
 extern struct nouveau_oclass nve0_copy1_oclass;
+extern struct nouveau_oclass nve0_copy2_oclass;
 
 #endif
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/falcon.h b/drivers/gpu/drm/nouveau/core/include/engine/falcon.h
new file mode 100644 (file)
index 0000000..1edec38
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef __NOUVEAU_FALCON_H__
+#define __NOUVEAU_FALCON_H__
+
+#include <core/engine.h>
+#include <core/engctx.h>
+#include <core/gpuobj.h>
+
+struct nouveau_falcon_chan {
+       struct nouveau_engctx base;
+};
+
+#define nouveau_falcon_context_create(p,e,c,g,s,a,f,d)                         \
+       nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d))
+#define nouveau_falcon_context_destroy(d)                                      \
+       nouveau_engctx_destroy(&(d)->base)
+#define nouveau_falcon_context_init(d)                                         \
+       nouveau_engctx_init(&(d)->base)
+#define nouveau_falcon_context_fini(d,s)                                       \
+       nouveau_engctx_fini(&(d)->base, (s))
+
+#define _nouveau_falcon_context_ctor _nouveau_engctx_ctor
+#define _nouveau_falcon_context_dtor _nouveau_engctx_dtor
+#define _nouveau_falcon_context_init _nouveau_engctx_init
+#define _nouveau_falcon_context_fini _nouveau_engctx_fini
+#define _nouveau_falcon_context_rd32 _nouveau_engctx_rd32
+#define _nouveau_falcon_context_wr32 _nouveau_engctx_wr32
+
+struct nouveau_falcon_data {
+       bool external;
+};
+
+struct nouveau_falcon {
+       struct nouveau_engine base;
+
+       u32 addr;
+       u8  version;
+       u8  secret;
+
+       struct nouveau_gpuobj *core;
+       bool external;
+
+       struct {
+               u32 limit;
+               u32 *data;
+               u32  size;
+       } code;
+
+       struct {
+               u32 limit;
+               u32 *data;
+               u32  size;
+       } data;
+};
+
+#define nv_falcon(priv) (&(priv)->base)
+
+#define nouveau_falcon_create(p,e,c,b,d,i,f,r)                                 \
+       nouveau_falcon_create_((p), (e), (c), (b), (d), (i), (f),              \
+                              sizeof(**r),(void **)r)
+#define nouveau_falcon_destroy(p)                                              \
+       nouveau_engine_destroy(&(p)->base)
+#define nouveau_falcon_init(p) ({                                              \
+       struct nouveau_falcon *falcon = (p);                                   \
+       _nouveau_falcon_init(nv_object(falcon));                               \
+})
+#define nouveau_falcon_fini(p,s) ({                                            \
+       struct nouveau_falcon *falcon = (p);                                   \
+       _nouveau_falcon_fini(nv_object(falcon), (s));                          \
+})
+
+int nouveau_falcon_create_(struct nouveau_object *, struct nouveau_object *,
+                          struct nouveau_oclass *, u32, bool, const char *,
+                          const char *, int, void **);
+
+#define _nouveau_falcon_dtor _nouveau_engine_dtor
+int  _nouveau_falcon_init(struct nouveau_object *);
+int  _nouveau_falcon_fini(struct nouveau_object *, bool);
+u32  _nouveau_falcon_rd32(struct nouveau_object *, u64);
+void _nouveau_falcon_wr32(struct nouveau_object *, u64, u32);
+
+#endif
index 5d39243..8e1b523 100644 (file)
@@ -61,8 +61,14 @@ extern struct nouveau_oclass nv34_graph_oclass;
 extern struct nouveau_oclass nv35_graph_oclass;
 extern struct nouveau_oclass nv40_graph_oclass;
 extern struct nouveau_oclass nv50_graph_oclass;
-extern struct nouveau_oclass nvc0_graph_oclass;
-extern struct nouveau_oclass nve0_graph_oclass;
+extern struct nouveau_oclass *nvc0_graph_oclass;
+extern struct nouveau_oclass *nvc1_graph_oclass;
+extern struct nouveau_oclass *nvc3_graph_oclass;
+extern struct nouveau_oclass *nvc8_graph_oclass;
+extern struct nouveau_oclass *nvd7_graph_oclass;
+extern struct nouveau_oclass *nvd9_graph_oclass;
+extern struct nouveau_oclass *nve4_graph_oclass;
+extern struct nouveau_oclass *nvf0_graph_oclass;
 
 extern const struct nouveau_bitfield nv04_graph_nsource[];
 extern struct nouveau_ofuncs nv04_graph_ofuncs;
index bbf0d4a..1d1a89a 100644 (file)
@@ -54,7 +54,6 @@ extern struct nouveau_ofuncs nv50_mpeg_ofuncs;
 int  nv50_mpeg_context_ctor(struct nouveau_object *, struct nouveau_object *,
                            struct nouveau_oclass *, void *, u32,
                            struct nouveau_object **);
-int  nv50_mpeg_tlb_flush(struct nouveau_engine *);
 void nv50_mpeg_intr(struct nouveau_subdev *);
 int  nv50_mpeg_init(struct nouveau_object *);
 
index d7b287b..39baebe 100644 (file)
@@ -2,6 +2,7 @@
 #define __NOUVEAU_VP_H__
 
 extern struct nouveau_oclass nv84_vp_oclass;
+extern struct nouveau_oclass nv98_vp_oclass;
 extern struct nouveau_oclass nvc0_vp_oclass;
 extern struct nouveau_oclass nve0_vp_oclass;
 
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/xtensa.h b/drivers/gpu/drm/nouveau/core/include/engine/xtensa.h
new file mode 100644 (file)
index 0000000..306100f
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef __NOUVEAU_XTENSA_H__
+#define __NOUVEAU_XTENSA_H__
+
+#include <core/engine.h>
+#include <core/engctx.h>
+#include <core/gpuobj.h>
+
+struct nouveau_xtensa {
+       struct nouveau_engine base;
+
+       u32 addr;
+       struct nouveau_gpuobj *gpu_fw;
+       u32 fifo_val;
+       u32 unkd28;
+};
+
+#define nouveau_xtensa_create(p,e,c,b,d,i,f,r)                         \
+       nouveau_xtensa_create_((p), (e), (c), (b), (d), (i), (f),       \
+                              sizeof(**r),(void **)r)
+
+int _nouveau_xtensa_engctx_ctor(struct nouveau_object *,
+                               struct nouveau_object *,
+                               struct nouveau_oclass *, void *, u32,
+                               struct nouveau_object **);
+
+void _nouveau_xtensa_intr(struct nouveau_subdev *);
+int nouveau_xtensa_create_(struct nouveau_object *,
+                          struct nouveau_object *,
+                          struct nouveau_oclass *, u32, bool,
+                          const char *, const char *,
+                          int, void **);
+#define _nouveau_xtensa_dtor _nouveau_engine_dtor
+int _nouveau_xtensa_init(struct nouveau_object *);
+int _nouveau_xtensa_fini(struct nouveau_object *, bool);
+u32  _nouveau_xtensa_rd32(struct nouveau_object *, u64);
+void _nouveau_xtensa_wr32(struct nouveau_object *, u64, u32);
+
+#endif
index 41b7a6a..89ee289 100644 (file)
@@ -10,8 +10,6 @@ struct nvbios_pll;
 struct nouveau_clock {
        struct nouveau_subdev base;
 
-       int (*pll_set)(struct nouveau_clock *, u32 type, u32 freq);
-
        /*XXX: die, these are here *only* to support the completely
         *     bat-shit insane what-was-nouveau_hw.c code
         */
index 29e4cc1..685c9b1 100644 (file)
@@ -8,6 +8,8 @@ struct nouveau_devinit {
        struct nouveau_subdev base;
        bool post;
        void (*meminit)(struct nouveau_devinit *);
+       int  (*pll_set)(struct nouveau_devinit *, u32 type, u32 freq);
+
 };
 
 static inline struct nouveau_devinit *
@@ -20,11 +22,20 @@ nouveau_devinit(void *obj)
        nouveau_devinit_create_((p), (e), (o), sizeof(**d), (void **)d)
 #define nouveau_devinit_destroy(p)                                             \
        nouveau_subdev_destroy(&(p)->base)
+#define nouveau_devinit_init(p) ({                                             \
+       struct nouveau_devinit *d = (p);                                       \
+       _nouveau_devinit_init(nv_object(d));                                   \
+})
+#define nouveau_devinit_fini(p,s) ({                                           \
+       struct nouveau_devinit *d = (p);                                       \
+       _nouveau_devinit_fini(nv_object(d), (s));                              \
+})
 
 int nouveau_devinit_create_(struct nouveau_object *, struct nouveau_object *,
                            struct nouveau_oclass *, int, void **);
-int nouveau_devinit_init(struct nouveau_devinit *);
-int nouveau_devinit_fini(struct nouveau_devinit *, bool suspend);
+#define _nouveau_devinit_dtor _nouveau_subdev_dtor
+int _nouveau_devinit_init(struct nouveau_object *);
+int _nouveau_devinit_fini(struct nouveau_object *, bool suspend);
 
 extern struct nouveau_oclass nv04_devinit_oclass;
 extern struct nouveau_oclass nv05_devinit_oclass;
@@ -32,9 +43,7 @@ extern struct nouveau_oclass nv10_devinit_oclass;
 extern struct nouveau_oclass nv1a_devinit_oclass;
 extern struct nouveau_oclass nv20_devinit_oclass;
 extern struct nouveau_oclass nv50_devinit_oclass;
-
-void nv04_devinit_dtor(struct nouveau_object *);
-int  nv04_devinit_init(struct nouveau_object *);
-int  nv04_devinit_fini(struct nouveau_object *, bool);
+extern struct nouveau_oclass nva3_devinit_oclass;
+extern struct nouveau_oclass nvc0_devinit_oclass;
 
 #endif
index da470e6..2e74050 100644 (file)
@@ -53,31 +53,7 @@ struct nouveau_fb {
 
        bool (*memtype_valid)(struct nouveau_fb *, u32 memtype);
 
-       struct {
-               enum {
-                       NV_MEM_TYPE_UNKNOWN = 0,
-                       NV_MEM_TYPE_STOLEN,
-                       NV_MEM_TYPE_SGRAM,
-                       NV_MEM_TYPE_SDRAM,
-                       NV_MEM_TYPE_DDR1,
-                       NV_MEM_TYPE_DDR2,
-                       NV_MEM_TYPE_DDR3,
-                       NV_MEM_TYPE_GDDR2,
-                       NV_MEM_TYPE_GDDR3,
-                       NV_MEM_TYPE_GDDR4,
-                       NV_MEM_TYPE_GDDR5
-               } type;
-               u64 stolen;
-               u64 size;
-
-               int ranks;
-               int parts;
-
-               int  (*init)(struct nouveau_fb *);
-               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 **);
-       } ram;
+       struct nouveau_ram *ram;
 
        struct nouveau_mm vram;
        struct nouveau_mm tags;
@@ -102,18 +78,6 @@ nouveau_fb(void *obj)
        return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_FB];
 }
 
-#define nouveau_fb_create(p,e,c,d)                                             \
-       nouveau_subdev_create((p), (e), (c), 0, "PFB", "fb", (d))
-int  nouveau_fb_preinit(struct nouveau_fb *);
-void nouveau_fb_destroy(struct nouveau_fb *);
-int  nouveau_fb_init(struct nouveau_fb *);
-#define nouveau_fb_fini(p,s)                                                   \
-       nouveau_subdev_fini(&(p)->base, (s))
-
-void _nouveau_fb_dtor(struct nouveau_object *);
-int  _nouveau_fb_init(struct nouveau_object *);
-#define _nouveau_fb_fini _nouveau_subdev_fini
-
 extern struct nouveau_oclass nv04_fb_oclass;
 extern struct nouveau_oclass nv10_fb_oclass;
 extern struct nouveau_oclass nv1a_fb_oclass;
@@ -132,40 +96,31 @@ extern struct nouveau_oclass nv4e_fb_oclass;
 extern struct nouveau_oclass nv50_fb_oclass;
 extern struct nouveau_oclass nvc0_fb_oclass;
 
-struct nouveau_bios;
-int  nouveau_fb_bios_memtype(struct nouveau_bios *);
-
-bool nv04_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 *);
-
-int  nv20_fb_vram_init(struct nouveau_fb *);
-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_vram_init(struct nouveau_fb *);
-int  nv41_fb_init(struct nouveau_object *);
-void nv41_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *);
-
-int  nv44_fb_vram_init(struct nouveau_fb *);
-int  nv44_fb_init(struct nouveau_object *);
-void nv44_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *);
+struct nouveau_ram {
+       struct nouveau_object base;
+       enum {
+               NV_MEM_TYPE_UNKNOWN = 0,
+               NV_MEM_TYPE_STOLEN,
+               NV_MEM_TYPE_SGRAM,
+               NV_MEM_TYPE_SDRAM,
+               NV_MEM_TYPE_DDR1,
+               NV_MEM_TYPE_DDR2,
+               NV_MEM_TYPE_DDR3,
+               NV_MEM_TYPE_GDDR2,
+               NV_MEM_TYPE_GDDR3,
+               NV_MEM_TYPE_GDDR4,
+               NV_MEM_TYPE_GDDR5
+       } type;
+       u64 stolen;
+       u64 size;
+       u32 tags;
 
-void nv46_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size,
-                      u32 pitch, u32 flags, struct nouveau_fb_tile *);
+       int ranks;
+       int parts;
 
-void nv50_fb_vram_del(struct nouveau_fb *, struct nouveau_mem **);
+       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 **);
+};
 
 #endif
index 9d595ef..f2e87b1 100644 (file)
@@ -58,7 +58,7 @@ struct nouveau_vm {
        int refcount;
 
        struct list_head pgd_list;
-       atomic_t engref[64]; //NVDEV_SUBDEV_NR];
+       atomic_t engref[NVDEV_SUBDEV_NR];
 
        struct nouveau_vm_pgt *pgt;
        u32 fpde;
@@ -117,9 +117,6 @@ int  nv04_vm_create(struct nouveau_vmmgr *, u64, u64, u64,
                    struct nouveau_vm **);
 void nv04_vmmgr_dtor(struct nouveau_object *);
 
-void nv50_vm_flush_engine(struct nouveau_subdev *, int engine);
-void nvc0_vm_flush_engine(struct nouveau_subdev *, u64 addr, int type);
-
 /* nouveau_vm.c */
 int  nouveau_vm_create(struct nouveau_vmmgr *, u64 offset, u64 length,
                       u64 mm_offset, u32 block, struct nouveau_vm **);
index 649f1ce..160d27f 100644 (file)
@@ -53,7 +53,6 @@ nv50_bar_kmap(struct nouveau_bar *bar, struct nouveau_mem *mem,
                return ret;
 
        nouveau_vm_map(vma, mem);
-       nv50_vm_flush_engine(nv_subdev(bar), 6);
        return 0;
 }
 
@@ -69,7 +68,6 @@ nv50_bar_umap(struct nouveau_bar *bar, struct nouveau_mem *mem,
                return ret;
 
        nouveau_vm_map(vma, mem);
-       nv50_vm_flush_engine(nv_subdev(bar), 6);
        return 0;
 }
 
@@ -77,7 +75,6 @@ static void
 nv50_bar_unmap(struct nouveau_bar *bar, struct nouveau_vma *vma)
 {
        nouveau_vm_unmap(vma);
-       nv50_vm_flush_engine(nv_subdev(bar), 6);
        nouveau_vm_put(vma);
 }
 
@@ -147,6 +144,8 @@ nv50_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
+
        ret = nouveau_gpuobj_new(nv_object(priv), heap,
                                 ((limit-- - start) >> 12) * 8, 0x1000,
                                 NVOBJ_FLAG_ZERO_ALLOC, &vm->pgt[0].obj[0]);
@@ -179,6 +178,8 @@ nv50_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
+
        ret = nouveau_vm_ref(vm, &priv->bar1_vm, priv->pgd);
        nouveau_vm_ref(NULL, &vm, NULL);
        if (ret)
@@ -237,7 +238,11 @@ nv50_bar_init(struct nouveau_object *object)
 
        nv_mask(priv, 0x000200, 0x00000100, 0x00000000);
        nv_mask(priv, 0x000200, 0x00000100, 0x00000100);
-       nv50_vm_flush_engine(nv_subdev(priv), 6);
+       nv_wr32(priv, 0x100c80, 0x00060001);
+       if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000)) {
+               nv_error(priv, "vm flush timeout\n");
+               return -EBUSY;
+       }
 
        nv_wr32(priv, 0x001704, 0x00000000 | priv->mem->addr >> 12);
        nv_wr32(priv, 0x001704, 0x40000000 | priv->mem->addr >> 12);
index f8a4495..b2ec741 100644 (file)
@@ -51,7 +51,6 @@ nvc0_bar_kmap(struct nouveau_bar *bar, struct nouveau_mem *mem,
                return ret;
 
        nouveau_vm_map(vma, mem);
-       nvc0_vm_flush_engine(nv_subdev(bar), priv->bar[0].pgd->addr, 5);
        return 0;
 }
 
@@ -68,18 +67,13 @@ nvc0_bar_umap(struct nouveau_bar *bar, struct nouveau_mem *mem,
                return ret;
 
        nouveau_vm_map(vma, mem);
-       nvc0_vm_flush_engine(nv_subdev(bar), priv->bar[1].pgd->addr, 5);
        return 0;
 }
 
 static void
 nvc0_bar_unmap(struct nouveau_bar *bar, struct nouveau_vma *vma)
 {
-       struct nvc0_bar_priv *priv = (void *)bar;
-       int i = !(vma->vm == priv->bar[0].vm);
-
        nouveau_vm_unmap(vma);
-       nvc0_vm_flush_engine(nv_subdev(bar), priv->bar[i].pgd->addr, 5);
        nouveau_vm_put(vma);
 }
 
@@ -116,6 +110,8 @@ nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
+
        ret = nouveau_gpuobj_new(nv_object(priv), NULL,
                                 (pci_resource_len(pdev, 3) >> 12) * 8,
                                 0x1000, NVOBJ_FLAG_ZERO_ALLOC,
@@ -150,6 +146,8 @@ nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
+
        ret = nouveau_vm_ref(vm, &priv->bar[1].vm, priv->bar[1].pgd);
        nouveau_vm_ref(NULL, &vm, NULL);
        if (ret)
index 0e2c1a4..aa0fbbe 100644 (file)
@@ -85,11 +85,15 @@ static void
 nouveau_bios_shadow_pramin(struct nouveau_bios *bios)
 {
        struct nouveau_device *device = nv_device(bios);
+       u64 addr = 0;
        u32 bar0 = 0;
        int i;
 
        if (device->card_type >= NV_50) {
-               u64 addr = (u64)(nv_rd32(bios, 0x619f04) & 0xffffff00) << 8;
+               if (  device->card_type < NV_C0 ||
+                   !(nv_rd32(bios, 0x022500) & 0x00000001))
+                       addr = (u64)(nv_rd32(bios, 0x619f04) & 0xffffff00) << 8;
+
                if (!addr) {
                        addr  = (u64)nv_rd32(bios, 0x001700) << 16;
                        addr += 0xf0000;
index c434d39..0687e64 100644 (file)
@@ -10,7 +10,6 @@
 #include <subdev/bios/gpio.h>
 #include <subdev/bios/init.h>
 #include <subdev/devinit.h>
-#include <subdev/clock.h>
 #include <subdev/i2c.h>
 #include <subdev/vga.h>
 #include <subdev/gpio.h>
@@ -300,9 +299,9 @@ init_wrauxr(struct nvbios_init *init, u32 addr, u8 data)
 static void
 init_prog_pll(struct nvbios_init *init, u32 id, u32 freq)
 {
-       struct nouveau_clock *clk = nouveau_clock(init->bios);
-       if (clk && clk->pll_set && init_exec(init)) {
-               int ret = clk->pll_set(clk, id, freq);
+       struct nouveau_devinit *devinit = nouveau_devinit(init->bios);
+       if (devinit->pll_set && init_exec(init)) {
+               int ret = devinit->pll_set(devinit, id, freq);
                if (ret)
                        warn("failed to prog pll 0x%08x to %dkHz\n", id, freq);
        }
index b7fd115..a142775 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/clock.h>
 #include <subdev/bios.h>
 #include <subdev/bios/pll.h>
+#include <subdev/clock.h>
+#include <subdev/devinit/priv.h>
 
 #include "pll.h"
 
@@ -32,272 +33,12 @@ struct nv04_clock_priv {
        struct nouveau_clock base;
 };
 
-static int
-powerctrl_1_shift(int chip_version, int reg)
-{
-       int shift = -4;
-
-       if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20)
-               return shift;
-
-       switch (reg) {
-       case 0x680520:
-               shift += 4;
-       case 0x680508:
-               shift += 4;
-       case 0x680504:
-               shift += 4;
-       case 0x680500:
-               shift += 4;
-       }
-
-       /*
-        * the shift for vpll regs is only used for nv3x chips with a single
-        * stage pll
-        */
-       if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 ||
-                         chip_version == 0x36 || chip_version >= 0x40))
-               shift = -4;
-
-       return shift;
-}
-
-static void
-setPLL_single(struct nv04_clock_priv *priv, u32 reg,
-             struct nouveau_pll_vals *pv)
-{
-       int chip_version = nouveau_bios(priv)->version.chip;
-       uint32_t oldpll = nv_rd32(priv, reg);
-       int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
-       uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
-       uint32_t saved_powerctrl_1 = 0;
-       int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg);
-
-       if (oldpll == pll)
-               return; /* already set */
-
-       if (shift_powerctrl_1 >= 0) {
-               saved_powerctrl_1 = nv_rd32(priv, 0x001584);
-               nv_wr32(priv, 0x001584,
-                       (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
-                       1 << shift_powerctrl_1);
-       }
-
-       if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1))
-               /* upclock -- write new post divider first */
-               nv_wr32(priv, reg, pv->log2P << 16 | (oldpll & 0xffff));
-       else
-               /* downclock -- write new NM first */
-               nv_wr32(priv, reg, (oldpll & 0xffff0000) | pv->NM1);
-
-       if (chip_version < 0x17 && chip_version != 0x11)
-               /* wait a bit on older chips */
-               msleep(64);
-       nv_rd32(priv, reg);
-
-       /* then write the other half as well */
-       nv_wr32(priv, reg, pll);
-
-       if (shift_powerctrl_1 >= 0)
-               nv_wr32(priv, 0x001584, saved_powerctrl_1);
-}
-
-static uint32_t
-new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580)
-{
-       bool head_a = (reg1 == 0x680508);
-
-       if (ss) /* single stage pll mode */
-               ramdac580 |= head_a ? 0x00000100 : 0x10000000;
-       else
-               ramdac580 &= head_a ? 0xfffffeff : 0xefffffff;
-
-       return ramdac580;
-}
-
-static void
-setPLL_double_highregs(struct nv04_clock_priv *priv, u32 reg1,
-                      struct nouveau_pll_vals *pv)
-{
-       int chip_version = nouveau_bios(priv)->version.chip;
-       bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
-       uint32_t reg2 = reg1 + ((reg1 == 0x680520) ? 0x5c : 0x70);
-       uint32_t oldpll1 = nv_rd32(priv, reg1);
-       uint32_t oldpll2 = !nv3035 ? nv_rd32(priv, reg2) : 0;
-       uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1;
-       uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2;
-       uint32_t oldramdac580 = 0, ramdac580 = 0;
-       bool single_stage = !pv->NM2 || pv->N2 == pv->M2;       /* nv41+ only */
-       uint32_t saved_powerctrl_1 = 0, savedc040 = 0;
-       int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1);
-
-       /* model specific additions to generic pll1 and pll2 set up above */
-       if (nv3035) {
-               pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 |
-                      (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4;
-               pll2 = 0;
-       }
-       if (chip_version > 0x40 && reg1 >= 0x680508) { /* !nv40 */
-               oldramdac580 = nv_rd32(priv, 0x680580);
-               ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580);
-               if (oldramdac580 != ramdac580)
-                       oldpll1 = ~0;   /* force mismatch */
-               if (single_stage)
-                       /* magic value used by nvidia in single stage mode */
-                       pll2 |= 0x011f;
-       }
-       if (chip_version > 0x70)
-               /* magic bits set by the blob (but not the bios) on g71-73 */
-               pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28;
-
-       if (oldpll1 == pll1 && oldpll2 == pll2)
-               return; /* already set */
-
-       if (shift_powerctrl_1 >= 0) {
-               saved_powerctrl_1 = nv_rd32(priv, 0x001584);
-               nv_wr32(priv, 0x001584,
-                       (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
-                       1 << shift_powerctrl_1);
-       }
-
-       if (chip_version >= 0x40) {
-               int shift_c040 = 14;
-
-               switch (reg1) {
-               case 0x680504:
-                       shift_c040 += 2;
-               case 0x680500:
-                       shift_c040 += 2;
-               case 0x680520:
-                       shift_c040 += 2;
-               case 0x680508:
-                       shift_c040 += 2;
-               }
-
-               savedc040 = nv_rd32(priv, 0xc040);
-               if (shift_c040 != 14)
-                       nv_wr32(priv, 0xc040, savedc040 & ~(3 << shift_c040));
-       }
-
-       if (oldramdac580 != ramdac580)
-               nv_wr32(priv, 0x680580, ramdac580);
-
-       if (!nv3035)
-               nv_wr32(priv, reg2, pll2);
-       nv_wr32(priv, reg1, pll1);
-
-       if (shift_powerctrl_1 >= 0)
-               nv_wr32(priv, 0x001584, saved_powerctrl_1);
-       if (chip_version >= 0x40)
-               nv_wr32(priv, 0xc040, savedc040);
-}
-
-static void
-setPLL_double_lowregs(struct nv04_clock_priv *priv, u32 NMNMreg,
-                     struct nouveau_pll_vals *pv)
-{
-       /* When setting PLLs, there is a merry game of disabling and enabling
-        * various bits of hardware during the process. This function is a
-        * synthesis of six nv4x traces, nearly each card doing a subtly
-        * different thing. With luck all the necessary bits for each card are
-        * combined herein. Without luck it deviates from each card's formula
-        * so as to not work on any :)
-        */
-
-       uint32_t Preg = NMNMreg - 4;
-       bool mpll = Preg == 0x4020;
-       uint32_t oldPval = nv_rd32(priv, Preg);
-       uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
-       uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) |
-                       0xc << 28 | pv->log2P << 16;
-       uint32_t saved4600 = 0;
-       /* some cards have different maskc040s */
-       uint32_t maskc040 = ~(3 << 14), savedc040;
-       bool single_stage = !pv->NM2 || pv->N2 == pv->M2;
-
-       if (nv_rd32(priv, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval)
-               return;
-
-       if (Preg == 0x4000)
-               maskc040 = ~0x333;
-       if (Preg == 0x4058)
-               maskc040 = ~(0xc << 24);
-
-       if (mpll) {
-               struct nvbios_pll info;
-               uint8_t Pval2;
-
-               if (nvbios_pll_parse(nouveau_bios(priv), Preg, &info))
-                       return;
-
-               Pval2 = pv->log2P + info.bias_p;
-               if (Pval2 > info.max_p)
-                       Pval2 = info.max_p;
-               Pval |= 1 << 28 | Pval2 << 20;
-
-               saved4600 = nv_rd32(priv, 0x4600);
-               nv_wr32(priv, 0x4600, saved4600 | 8 << 28);
-       }
-       if (single_stage)
-               Pval |= mpll ? 1 << 12 : 1 << 8;
-
-       nv_wr32(priv, Preg, oldPval | 1 << 28);
-       nv_wr32(priv, Preg, Pval & ~(4 << 28));
-       if (mpll) {
-               Pval |= 8 << 20;
-               nv_wr32(priv, 0x4020, Pval & ~(0xc << 28));
-               nv_wr32(priv, 0x4038, Pval & ~(0xc << 28));
-       }
-
-       savedc040 = nv_rd32(priv, 0xc040);
-       nv_wr32(priv, 0xc040, savedc040 & maskc040);
-
-       nv_wr32(priv, NMNMreg, NMNM);
-       if (NMNMreg == 0x4024)
-               nv_wr32(priv, 0x403c, NMNM);
-
-       nv_wr32(priv, Preg, Pval);
-       if (mpll) {
-               Pval &= ~(8 << 20);
-               nv_wr32(priv, 0x4020, Pval);
-               nv_wr32(priv, 0x4038, Pval);
-               nv_wr32(priv, 0x4600, saved4600);
-       }
-
-       nv_wr32(priv, 0xc040, savedc040);
-
-       if (mpll) {
-               nv_wr32(priv, 0x4020, Pval & ~(1 << 28));
-               nv_wr32(priv, 0x4038, Pval & ~(1 << 28));
-       }
-}
-
-int
-nv04_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
-{
-       struct nv04_clock_priv *priv = (void *)clk;
-       struct nouveau_pll_vals pv;
-       struct nvbios_pll info;
-       int ret;
-
-       ret = nvbios_pll_parse(nouveau_bios(priv), type > 0x405c ?
-                              type : type - 4, &info);
-       if (ret)
-               return ret;
-
-       ret = clk->pll_calc(clk, &info, freq, &pv);
-       if (!ret)
-               return ret;
-
-       return clk->pll_prog(clk, type, &pv);
-}
-
 int
 nv04_clock_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
                    int clk, struct nouveau_pll_vals *pv)
 {
        int N1, M1, N2, M2, P;
-       int ret = nv04_pll_calc(clock, info, clk, &N1, &M1, &N2, &M2, &P);
+       int ret = nv04_pll_calc(nv_subdev(clock), info, clk, &N1, &M1, &N2, &M2, &P);
        if (ret) {
                pv->refclk = info->refclk;
                pv->N1 = N1;
@@ -313,17 +54,17 @@ int
 nv04_clock_pll_prog(struct nouveau_clock *clk, u32 reg1,
                    struct nouveau_pll_vals *pv)
 {
-       struct nv04_clock_priv *priv = (void *)clk;
+       struct nouveau_devinit *devinit = nouveau_devinit(clk);
        int cv = nouveau_bios(clk)->version.chip;
 
        if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
            cv >= 0x40) {
                if (reg1 > 0x405c)
-                       setPLL_double_highregs(priv, reg1, pv);
+                       setPLL_double_highregs(devinit, reg1, pv);
                else
-                       setPLL_double_lowregs(priv, reg1, pv);
+                       setPLL_double_lowregs(devinit, reg1, pv);
        } else
-               setPLL_single(priv, reg1, pv);
+               setPLL_single(devinit, reg1, pv);
 
        return 0;
 }
@@ -341,7 +82,6 @@ nv04_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
-       priv->base.pll_set = nv04_clock_pll_set;
        priv->base.pll_calc = nv04_clock_pll_calc;
        priv->base.pll_prog = nv04_clock_pll_prog;
        return 0;
index a4b2b7e..0db5dbf 100644 (file)
@@ -41,7 +41,6 @@ nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
-       priv->base.pll_set = nv04_clock_pll_set;
        priv->base.pll_calc = nv04_clock_pll_calc;
        priv->base.pll_prog = nv04_clock_pll_prog;
        return 0;
index f4147f6..d09d3e7 100644 (file)
@@ -32,50 +32,6 @@ struct nv50_clock_priv {
        struct nouveau_clock base;
 };
 
-static int
-nv50_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
-{
-       struct nv50_clock_priv *priv = (void *)clk;
-       struct nouveau_bios *bios = nouveau_bios(priv);
-       struct nvbios_pll info;
-       int N1, M1, N2, M2, P;
-       int ret;
-
-       ret = nvbios_pll_parse(bios, type, &info);
-       if (ret) {
-               nv_error(clk, "failed to retrieve pll data, %d\n", ret);
-               return ret;
-       }
-
-       ret = nv04_pll_calc(clk, &info, freq, &N1, &M1, &N2, &M2, &P);
-       if (!ret) {
-               nv_error(clk, "failed pll calculation\n");
-               return ret;
-       }
-
-       switch (info.type) {
-       case PLL_VPLL0:
-       case PLL_VPLL1:
-               nv_wr32(priv, info.reg + 0, 0x10000611);
-               nv_mask(priv, info.reg + 4, 0x00ff00ff, (M1 << 16) | N1);
-               nv_mask(priv, info.reg + 8, 0x7fff00ff, (P  << 28) |
-                                                       (M2 << 16) | N2);
-               break;
-       case PLL_MEMORY:
-               nv_mask(priv, info.reg + 0, 0x01ff0000, (P << 22) |
-                                                       (info.bias_p << 19) |
-                                                       (P << 16));
-               nv_wr32(priv, info.reg + 4, (N1 << 8) | M1);
-               break;
-       default:
-               nv_mask(priv, info.reg + 0, 0x00070000, (P << 16));
-               nv_wr32(priv, info.reg + 4, (N1 << 8) | M1);
-               break;
-       }
-
-       return 0;
-}
-
 static int
 nv50_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_oclass *oclass, void *data, u32 size,
@@ -89,7 +45,6 @@ nv50_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
-       priv->base.pll_set = nv50_clock_pll_set;
        priv->base.pll_calc = nv04_clock_pll_calc;
        return 0;
 }
index 9068c98..f074cd2 100644 (file)
@@ -32,47 +32,13 @@ struct nva3_clock_priv {
        struct nouveau_clock base;
 };
 
-static int
-nva3_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
-{
-       struct nva3_clock_priv *priv = (void *)clk;
-       struct nouveau_bios *bios = nouveau_bios(priv);
-       struct nvbios_pll info;
-       int N, fN, M, P;
-       int ret;
-
-       ret = nvbios_pll_parse(bios, type, &info);
-       if (ret)
-               return ret;
-
-       ret = nva3_pll_calc(clk, &info, freq, &N, &fN, &M, &P);
-       if (ret < 0)
-               return ret;
-
-       switch (info.type) {
-       case PLL_VPLL0:
-       case PLL_VPLL1:
-               nv_wr32(priv, info.reg + 0, 0x50000610);
-               nv_mask(priv, info.reg + 4, 0x003fffff,
-                                           (P << 16) | (M << 8) | N);
-               nv_wr32(priv, info.reg + 8, fN);
-               break;
-       default:
-               nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
 int
 nva3_clock_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
                    int clk, struct nouveau_pll_vals *pv)
 {
        int ret, N, M, P;
 
-       ret = nva3_pll_calc(clock, info, clk, &N, NULL, &M, &P);
+       ret = nva3_pll_calc(nv_subdev(clock), info, clk, &N, NULL, &M, &P);
 
        if (ret > 0) {
                pv->refclk = info->refclk;
@@ -97,7 +63,6 @@ nva3_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
-       priv->base.pll_set = nva3_clock_pll_set;
        priv->base.pll_calc = nva3_clock_pll_calc;
        return 0;
 }
index 7c96262..439d81c 100644 (file)
@@ -32,41 +32,6 @@ struct nvc0_clock_priv {
        struct nouveau_clock base;
 };
 
-static int
-nvc0_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq)
-{
-       struct nvc0_clock_priv *priv = (void *)clk;
-       struct nouveau_bios *bios = nouveau_bios(priv);
-       struct nvbios_pll info;
-       int N, fN, M, P;
-       int ret;
-
-       ret = nvbios_pll_parse(bios, type, &info);
-       if (ret)
-               return ret;
-
-       ret = nva3_pll_calc(clk, &info, freq, &N, &fN, &M, &P);
-       if (ret < 0)
-               return ret;
-
-       switch (info.type) {
-       case PLL_VPLL0:
-       case PLL_VPLL1:
-       case PLL_VPLL2:
-       case PLL_VPLL3:
-               nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100);
-               nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M);
-               nv_wr32(priv, info.reg + 0x10, fN << 16);
-               break;
-       default:
-               nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
 static int
 nvc0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                struct nouveau_oclass *oclass, void *data, u32 size,
@@ -80,7 +45,6 @@ nvc0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
-       priv->base.pll_set = nvc0_clock_pll_set;
        priv->base.pll_calc = nva3_clock_pll_calc;
        return 0;
 }
index ef2c007..445b14c 100644 (file)
@@ -1,9 +1,9 @@
 #ifndef __NOUVEAU_PLL_H__
 #define __NOUVEAU_PLL_H__
 
-int nv04_pll_calc(struct nouveau_clock *, struct nvbios_pll *, u32 freq,
+int nv04_pll_calc(struct nouveau_subdev *, struct nvbios_pll *, u32 freq,
                  int *N1, int *M1, int *N2, int *M2, int *P);
-int nva3_pll_calc(struct nouveau_clock *, struct nvbios_pll *, u32 freq,
+int nva3_pll_calc(struct nouveau_subdev *, struct nvbios_pll *, u32 freq,
                  int *N, int *fN, int *M, int *P);
 
 #endif
index a2ab6d0..cf1ed0d 100644 (file)
  * SOFTWARE.
  */
 
-#include <subdev/clock.h>
 #include <subdev/bios.h>
 #include <subdev/bios/pll.h>
 
 #include "pll.h"
 
 static int
-getMNP_single(struct nouveau_clock *clock, struct nvbios_pll *info, int clk,
+getMNP_single(struct nouveau_subdev *subdev, struct nvbios_pll *info, int clk,
              int *pN, int *pM, int *pP)
 {
        /* Find M, N and P for a single stage PLL
@@ -39,7 +38,7 @@ getMNP_single(struct nouveau_clock *clock, struct nvbios_pll *info, int clk,
         * "clk" parameter in kHz
         * returns calculated clock
         */
-       int cv = nouveau_bios(clock)->version.chip;
+       int cv = nouveau_bios(subdev)->version.chip;
        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;
@@ -124,7 +123,7 @@ getMNP_single(struct nouveau_clock *clock, struct nvbios_pll *info, int clk,
 }
 
 static int
-getMNP_double(struct nouveau_clock *clock, struct nvbios_pll *info, int clk,
+getMNP_double(struct nouveau_subdev *subdev, struct nvbios_pll *info, int clk,
              int *pN1, int *pM1, int *pN2, int *pM2, int *pP)
 {
        /* Find M, N and P for a two stage PLL
@@ -135,7 +134,7 @@ getMNP_double(struct nouveau_clock *clock, struct nvbios_pll *info, int clk,
         * "clk" parameter in kHz
         * returns calculated clock
         */
-       int chip_version = nouveau_bios(clock)->version.chip;
+       int chip_version = nouveau_bios(subdev)->version.chip;
        int minvco1 = info->vco1.min_freq, maxvco1 = info->vco1.max_freq;
        int minvco2 = info->vco2.min_freq, maxvco2 = info->vco2.max_freq;
        int minU1 = info->vco1.min_inputfreq, minU2 = info->vco2.min_inputfreq;
@@ -223,20 +222,20 @@ getMNP_double(struct nouveau_clock *clock, struct nvbios_pll *info, int clk,
 }
 
 int
-nv04_pll_calc(struct nouveau_clock *clk, struct nvbios_pll *info, u32 freq,
+nv04_pll_calc(struct nouveau_subdev *subdev, struct nvbios_pll *info, u32 freq,
              int *N1, int *M1, int *N2, int *M2, int *P)
 {
        int ret;
 
        if (!info->vco2.max_freq) {
-               ret = getMNP_single(clk, info, freq, N1, M1, P);
+               ret = getMNP_single(subdev, info, freq, N1, M1, P);
                *N2 = 1;
                *M2 = 1;
        } else {
-               ret = getMNP_double(clk, info, freq, N1, M1, N2, M2, P);
+               ret = getMNP_double(subdev, info, freq, N1, M1, N2, M2, P);
        }
 
        if (!ret)
-               nv_error(clk, "unable to compute acceptable pll values\n");
+               nv_error(subdev, "unable to compute acceptable pll values\n");
        return ret;
 }
index eed5c16..2fe1f71 100644 (file)
@@ -29,7 +29,7 @@
 #include "pll.h"
 
 int
-nva3_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
+nva3_pll_calc(struct nouveau_subdev *subdev, struct nvbios_pll *info,
              u32 freq, int *pN, int *pfN, int *pM, int *P)
 {
        u32 best_err = ~0, err;
@@ -50,8 +50,15 @@ nva3_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
                u32 tmp = freq * *P * M;
                N  = tmp / info->refclk;
                fN = tmp % info->refclk;
-               if (!pfN && fN >= info->refclk / 2)
-                       N++;
+
+               if (!pfN) {
+                       if (fN >= info->refclk / 2)
+                               N++;
+               } else {
+                       if (fN <  info->refclk / 2)
+                               N--;
+                       fN = tmp - (N * info->refclk);
+               }
 
                if (N < info->vco1.min_n)
                        continue;
@@ -66,13 +73,14 @@ nva3_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info,
                }
 
                if (pfN) {
-                       *pfN = (((fN << 13) / info->refclk) - 4096) & 0xffff;
+                       *pfN = ((fN << 13) + info->refclk / 2) / info->refclk;
+                       *pfN = (*pfN - 4096) & 0xffff;
                        return freq;
                }
        }
 
        if (unlikely(best_err == ~0)) {
-               nv_error(clock, "unable to find matching pll values\n");
+               nv_error(subdev, "unable to find matching pll values\n");
                return -EINVAL;
        }
 
index 5a07a39..79c81d3 100644 (file)
 #include <subdev/bios/init.h>
 
 int
-nouveau_devinit_init(struct nouveau_devinit *devinit)
+_nouveau_devinit_fini(struct nouveau_object *object, bool suspend)
 {
-       int ret = nouveau_subdev_init(&devinit->base);
-       if (ret)
-               return ret;
+       struct nouveau_devinit *devinit = (void *)object;
 
-       return nvbios_init(&devinit->base, devinit->post);
-}
-
-int
-nouveau_devinit_fini(struct nouveau_devinit *devinit, bool suspend)
-{
        /* force full reinit on resume */
        if (suspend)
                devinit->post = true;
@@ -48,6 +40,17 @@ nouveau_devinit_fini(struct nouveau_devinit *devinit, bool suspend)
        return nouveau_subdev_fini(&devinit->base, suspend);
 }
 
+int
+_nouveau_devinit_init(struct nouveau_object *object)
+{
+       struct nouveau_devinit *devinit = (void *)object;
+       int ret = nouveau_subdev_init(&devinit->base);
+       if (ret)
+               return ret;
+
+       return nvbios_init(&devinit->base, devinit->post);
+}
+
 int
 nouveau_devinit_create_(struct nouveau_object *parent,
                        struct nouveau_object *engine,
index 7a72d93..b22357d 100644 (file)
  *
  */
 
-#include <subdev/devinit.h>
 #include <subdev/vga.h>
 
 #include "fbmem.h"
+#include "priv.h"
 
 struct nv04_devinit_priv {
        struct nouveau_devinit base;
@@ -111,33 +111,298 @@ nv04_devinit_meminit(struct nouveau_devinit *devinit)
 }
 
 static int
-nv04_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-                 struct nouveau_oclass *oclass, void *data, u32 size,
-                 struct nouveau_object **pobject)
+powerctrl_1_shift(int chip_version, int reg)
 {
-       struct nv04_devinit_priv *priv;
+       int shift = -4;
+
+       if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20)
+               return shift;
+
+       switch (reg) {
+       case 0x680520:
+               shift += 4;
+       case 0x680508:
+               shift += 4;
+       case 0x680504:
+               shift += 4;
+       case 0x680500:
+               shift += 4;
+       }
+
+       /*
+        * the shift for vpll regs is only used for nv3x chips with a single
+        * stage pll
+        */
+       if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 ||
+                         chip_version == 0x36 || chip_version >= 0x40))
+               shift = -4;
+
+       return shift;
+}
+
+void
+setPLL_single(struct nouveau_devinit *devinit, u32 reg,
+             struct nouveau_pll_vals *pv)
+{
+       int chip_version = nouveau_bios(devinit)->version.chip;
+       uint32_t oldpll = nv_rd32(devinit, reg);
+       int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
+       uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
+       uint32_t saved_powerctrl_1 = 0;
+       int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg);
+
+       if (oldpll == pll)
+               return; /* already set */
+
+       if (shift_powerctrl_1 >= 0) {
+               saved_powerctrl_1 = nv_rd32(devinit, 0x001584);
+               nv_wr32(devinit, 0x001584,
+                       (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
+                       1 << shift_powerctrl_1);
+       }
+
+       if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1))
+               /* upclock -- write new post divider first */
+               nv_wr32(devinit, reg, pv->log2P << 16 | (oldpll & 0xffff));
+       else
+               /* downclock -- write new NM first */
+               nv_wr32(devinit, reg, (oldpll & 0xffff0000) | pv->NM1);
+
+       if (chip_version < 0x17 && chip_version != 0x11)
+               /* wait a bit on older chips */
+               msleep(64);
+       nv_rd32(devinit, reg);
+
+       /* then write the other half as well */
+       nv_wr32(devinit, reg, pll);
+
+       if (shift_powerctrl_1 >= 0)
+               nv_wr32(devinit, 0x001584, saved_powerctrl_1);
+}
+
+static uint32_t
+new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580)
+{
+       bool head_a = (reg1 == 0x680508);
+
+       if (ss) /* single stage pll mode */
+               ramdac580 |= head_a ? 0x00000100 : 0x10000000;
+       else
+               ramdac580 &= head_a ? 0xfffffeff : 0xefffffff;
+
+       return ramdac580;
+}
+
+void
+setPLL_double_highregs(struct nouveau_devinit *devinit, u32 reg1,
+                      struct nouveau_pll_vals *pv)
+{
+       int chip_version = nouveau_bios(devinit)->version.chip;
+       bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
+       uint32_t reg2 = reg1 + ((reg1 == 0x680520) ? 0x5c : 0x70);
+       uint32_t oldpll1 = nv_rd32(devinit, reg1);
+       uint32_t oldpll2 = !nv3035 ? nv_rd32(devinit, reg2) : 0;
+       uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1;
+       uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2;
+       uint32_t oldramdac580 = 0, ramdac580 = 0;
+       bool single_stage = !pv->NM2 || pv->N2 == pv->M2;       /* nv41+ only */
+       uint32_t saved_powerctrl_1 = 0, savedc040 = 0;
+       int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1);
+
+       /* model specific additions to generic pll1 and pll2 set up above */
+       if (nv3035) {
+               pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 |
+                      (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4;
+               pll2 = 0;
+       }
+       if (chip_version > 0x40 && reg1 >= 0x680508) { /* !nv40 */
+               oldramdac580 = nv_rd32(devinit, 0x680580);
+               ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580);
+               if (oldramdac580 != ramdac580)
+                       oldpll1 = ~0;   /* force mismatch */
+               if (single_stage)
+                       /* magic value used by nvidia in single stage mode */
+                       pll2 |= 0x011f;
+       }
+       if (chip_version > 0x70)
+               /* magic bits set by the blob (but not the bios) on g71-73 */
+               pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28;
+
+       if (oldpll1 == pll1 && oldpll2 == pll2)
+               return; /* already set */
+
+       if (shift_powerctrl_1 >= 0) {
+               saved_powerctrl_1 = nv_rd32(devinit, 0x001584);
+               nv_wr32(devinit, 0x001584,
+                       (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
+                       1 << shift_powerctrl_1);
+       }
+
+       if (chip_version >= 0x40) {
+               int shift_c040 = 14;
+
+               switch (reg1) {
+               case 0x680504:
+                       shift_c040 += 2;
+               case 0x680500:
+                       shift_c040 += 2;
+               case 0x680520:
+                       shift_c040 += 2;
+               case 0x680508:
+                       shift_c040 += 2;
+               }
+
+               savedc040 = nv_rd32(devinit, 0xc040);
+               if (shift_c040 != 14)
+                       nv_wr32(devinit, 0xc040, savedc040 & ~(3 << shift_c040));
+       }
+
+       if (oldramdac580 != ramdac580)
+               nv_wr32(devinit, 0x680580, ramdac580);
+
+       if (!nv3035)
+               nv_wr32(devinit, reg2, pll2);
+       nv_wr32(devinit, reg1, pll1);
+
+       if (shift_powerctrl_1 >= 0)
+               nv_wr32(devinit, 0x001584, saved_powerctrl_1);
+       if (chip_version >= 0x40)
+               nv_wr32(devinit, 0xc040, savedc040);
+}
+
+void
+setPLL_double_lowregs(struct nouveau_devinit *devinit, u32 NMNMreg,
+                     struct nouveau_pll_vals *pv)
+{
+       /* When setting PLLs, there is a merry game of disabling and enabling
+        * various bits of hardware during the process. This function is a
+        * synthesis of six nv4x traces, nearly each card doing a subtly
+        * different thing. With luck all the necessary bits for each card are
+        * combined herein. Without luck it deviates from each card's formula
+        * so as to not work on any :)
+        */
+
+       uint32_t Preg = NMNMreg - 4;
+       bool mpll = Preg == 0x4020;
+       uint32_t oldPval = nv_rd32(devinit, Preg);
+       uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
+       uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) |
+                       0xc << 28 | pv->log2P << 16;
+       uint32_t saved4600 = 0;
+       /* some cards have different maskc040s */
+       uint32_t maskc040 = ~(3 << 14), savedc040;
+       bool single_stage = !pv->NM2 || pv->N2 == pv->M2;
+
+       if (nv_rd32(devinit, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval)
+               return;
+
+       if (Preg == 0x4000)
+               maskc040 = ~0x333;
+       if (Preg == 0x4058)
+               maskc040 = ~(0xc << 24);
+
+       if (mpll) {
+               struct nvbios_pll info;
+               uint8_t Pval2;
+
+               if (nvbios_pll_parse(nouveau_bios(devinit), Preg, &info))
+                       return;
+
+               Pval2 = pv->log2P + info.bias_p;
+               if (Pval2 > info.max_p)
+                       Pval2 = info.max_p;
+               Pval |= 1 << 28 | Pval2 << 20;
+
+               saved4600 = nv_rd32(devinit, 0x4600);
+               nv_wr32(devinit, 0x4600, saved4600 | 8 << 28);
+       }
+       if (single_stage)
+               Pval |= mpll ? 1 << 12 : 1 << 8;
+
+       nv_wr32(devinit, Preg, oldPval | 1 << 28);
+       nv_wr32(devinit, Preg, Pval & ~(4 << 28));
+       if (mpll) {
+               Pval |= 8 << 20;
+               nv_wr32(devinit, 0x4020, Pval & ~(0xc << 28));
+               nv_wr32(devinit, 0x4038, Pval & ~(0xc << 28));
+       }
+
+       savedc040 = nv_rd32(devinit, 0xc040);
+       nv_wr32(devinit, 0xc040, savedc040 & maskc040);
+
+       nv_wr32(devinit, NMNMreg, NMNM);
+       if (NMNMreg == 0x4024)
+               nv_wr32(devinit, 0x403c, NMNM);
+
+       nv_wr32(devinit, Preg, Pval);
+       if (mpll) {
+               Pval &= ~(8 << 20);
+               nv_wr32(devinit, 0x4020, Pval);
+               nv_wr32(devinit, 0x4038, Pval);
+               nv_wr32(devinit, 0x4600, saved4600);
+       }
+
+       nv_wr32(devinit, 0xc040, savedc040);
+
+       if (mpll) {
+               nv_wr32(devinit, 0x4020, Pval & ~(1 << 28));
+               nv_wr32(devinit, 0x4038, Pval & ~(1 << 28));
+       }
+}
+
+int
+nv04_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq)
+{
+       struct nouveau_bios *bios = nouveau_bios(devinit);
+       struct nouveau_pll_vals pv;
+       struct nvbios_pll info;
+       int cv = bios->version.chip;
+       int N1, M1, N2, M2, P;
        int ret;
 
-       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
-       *pobject = nv_object(priv);
+       ret = nvbios_pll_parse(bios, type > 0x405c ? type : type - 4, &info);
        if (ret)
                return ret;
 
-       priv->base.meminit = nv04_devinit_meminit;
-       priv->owner = -1;
+       ret = nv04_pll_calc(nv_subdev(devinit), &info, freq,
+                          &N1, &M1, &N2, &M2, &P);
+       if (!ret)
+               return -EINVAL;
+
+       pv.refclk = info.refclk;
+       pv.N1 = N1;
+       pv.M1 = M1;
+       pv.N2 = N2;
+       pv.M2 = M2;
+       pv.log2P = P;
+
+       if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
+           cv >= 0x40) {
+               if (type > 0x405c)
+                       setPLL_double_highregs(devinit, type, &pv);
+               else
+                       setPLL_double_lowregs(devinit, type, &pv);
+       } else
+               setPLL_single(devinit, type, &pv);
+
        return 0;
 }
 
-void
-nv04_devinit_dtor(struct nouveau_object *object)
+int
+nv04_devinit_fini(struct nouveau_object *object, bool suspend)
 {
        struct nv04_devinit_priv *priv = (void *)object;
 
-       /* restore vga owner saved at first init, and lock crtc regs  */
-       nv_wrvgaowner(priv, priv->owner);
-       nv_lockvgac(priv, true);
+       /* make i2c busses accessible */
+       nv_mask(priv, 0x000200, 0x00000001, 0x00000001);
 
-       nouveau_devinit_destroy(&priv->base);
+       /* unlock extended vga crtc regs, and unslave crtcs */
+       nv_lockvgac(priv, false);
+       if (priv->owner < 0)
+               priv->owner = nv_rdvgaowner(priv);
+       nv_wrvgaowner(priv, 0);
+
+       return nouveau_devinit_fini(&priv->base, suspend);
 }
 
 int
@@ -160,21 +425,35 @@ nv04_devinit_init(struct nouveau_object *object)
        return nouveau_devinit_init(&priv->base);
 }
 
-int
-nv04_devinit_fini(struct nouveau_object *object, bool suspend)
+void
+nv04_devinit_dtor(struct nouveau_object *object)
 {
        struct nv04_devinit_priv *priv = (void *)object;
 
-       /* make i2c busses accessible */
-       nv_mask(priv, 0x000200, 0x00000001, 0x00000001);
+       /* restore vga owner saved at first init, and lock crtc regs  */
+       nv_wrvgaowner(priv, priv->owner);
+       nv_lockvgac(priv, true);
 
-       /* unlock extended vga crtc regs, and unslave crtcs */
-       nv_lockvgac(priv, false);
-       if (priv->owner < 0)
-               priv->owner = nv_rdvgaowner(priv);
-       nv_wrvgaowner(priv, 0);
+       nouveau_devinit_destroy(&priv->base);
+}
 
-       return nouveau_devinit_fini(&priv->base, suspend);
+static int
+nv04_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
+{
+       struct nv04_devinit_priv *priv;
+       int ret;
+
+       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.meminit = nv04_devinit_meminit;
+       priv->base.pll_set = nv04_devinit_pll_set;
+       priv->owner = -1;
+       return 0;
 }
 
 struct nouveau_oclass
index 191447d..b1912a8 100644 (file)
  *
  */
 
-#include <subdev/devinit.h>
 #include <subdev/bios.h>
 #include <subdev/bios/bmp.h>
 #include <subdev/vga.h>
 
 #include "fbmem.h"
+#include "priv.h"
 
 struct nv05_devinit_priv {
        struct nouveau_devinit base;
@@ -144,6 +144,7 @@ nv05_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        priv->base.meminit = nv05_devinit_meminit;
+       priv->base.pll_set = nv04_devinit_pll_set;
        return 0;
 }
 
index eb76ffa..463b08f 100644 (file)
  *
  */
 
-#include <subdev/devinit.h>
 #include <subdev/vga.h>
 
 #include "fbmem.h"
+#include "priv.h"
 
 struct nv10_devinit_priv {
        struct nouveau_devinit base;
@@ -109,6 +109,7 @@ nv10_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        priv->base.meminit = nv10_devinit_meminit;
+       priv->base.pll_set = nv04_devinit_pll_set;
        return 0;
 }
 
index 5b2ba63..e9743cd 100644 (file)
@@ -22,8 +22,7 @@
  * Authors: Ben Skeggs
  */
 
-#include <subdev/devinit.h>
-#include <subdev/vga.h>
+#include "priv.h"
 
 struct nv1a_devinit_priv {
        struct nouveau_devinit base;
@@ -43,6 +42,7 @@ nv1a_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       priv->base.pll_set = nv04_devinit_pll_set;
        return 0;
 }
 
index eb32e99..6cc6080 100644 (file)
@@ -24,9 +24,7 @@
  *
  */
 
-#include <subdev/devinit.h>
-#include <subdev/vga.h>
-
+#include "priv.h"
 #include "fbmem.h"
 
 struct nv20_devinit_priv {
@@ -81,6 +79,7 @@ nv20_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        priv->base.meminit = nv20_devinit_meminit;
+       priv->base.pll_set = nv04_devinit_pll_set;
        return 0;
 }
 
index 4a85778..6df7224 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Red Hat Inc.
+ * 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"),
 #include <subdev/bios/dcb.h>
 #include <subdev/bios/disp.h>
 #include <subdev/bios/init.h>
-#include <subdev/devinit.h>
 #include <subdev/vga.h>
 
-struct nv50_devinit_priv {
-       struct nouveau_devinit base;
-};
+#include "priv.h"
 
 static int
-nv50_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-                 struct nouveau_oclass *oclass, void *data, u32 size,
-                 struct nouveau_object **pobject)
+nv50_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq)
 {
-       struct nv50_devinit_priv *priv;
+       struct nv50_devinit_priv *priv = (void *)devinit;
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_pll info;
+       int N1, M1, N2, M2, P;
        int ret;
 
-       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
+       ret = nvbios_pll_parse(bios, type, &info);
+       if (ret) {
+               nv_error(devinit, "failed to retrieve pll data, %d\n", ret);
                return ret;
+       }
 
-       return 0;
-}
+       ret = nv04_pll_calc(nv_subdev(devinit), &info, freq, &N1, &M1, &N2, &M2, &P);
+       if (!ret) {
+               nv_error(devinit, "failed pll calculation\n");
+               return ret;
+       }
 
-static void
-nv50_devinit_dtor(struct nouveau_object *object)
-{
-       struct nv50_devinit_priv *priv = (void *)object;
-       nouveau_devinit_destroy(&priv->base);
+       switch (info.type) {
+       case PLL_VPLL0:
+       case PLL_VPLL1:
+               nv_wr32(priv, info.reg + 0, 0x10000611);
+               nv_mask(priv, info.reg + 4, 0x00ff00ff, (M1 << 16) | N1);
+               nv_mask(priv, info.reg + 8, 0x7fff00ff, (P  << 28) |
+                                                       (M2 << 16) | N2);
+               break;
+       case PLL_MEMORY:
+               nv_mask(priv, info.reg + 0, 0x01ff0000, (P << 22) |
+                                                       (info.bias_p << 19) |
+                                                       (P << 16));
+               nv_wr32(priv, info.reg + 4, (N1 << 8) | M1);
+               break;
+       default:
+               nv_mask(priv, info.reg + 0, 0x00070000, (P << 16));
+               nv_wr32(priv, info.reg + 4, (N1 << 8) | M1);
+               break;
+       }
+
+       return 0;
 }
 
-static int
+int
 nv50_devinit_init(struct nouveau_object *object)
 {
        struct nouveau_bios *bios = nouveau_bios(object);
@@ -103,10 +121,20 @@ nv50_devinit_init(struct nouveau_object *object)
 }
 
 static int
-nv50_devinit_fini(struct nouveau_object *object, bool suspend)
+nv50_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
 {
-       struct nv50_devinit_priv *priv = (void *)object;
-       return nouveau_devinit_fini(&priv->base, suspend);
+       struct nv50_devinit_priv *priv;
+       int ret;
+
+       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.pll_set = nv50_devinit_pll_set;
+       return 0;
 }
 
 struct nouveau_oclass
@@ -114,8 +142,8 @@ nv50_devinit_oclass = {
        .handle = NV_SUBDEV(DEVINIT, 0x50),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nv50_devinit_ctor,
-               .dtor = nv50_devinit_dtor,
+               .dtor = _nouveau_devinit_dtor,
                .init = nv50_devinit_init,
-               .fini = nv50_devinit_fini,
+               .fini = _nouveau_devinit_fini,
        },
 };
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c
new file mode 100644 (file)
index 0000000..76a68b2
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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 int
+nva3_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq)
+{
+       struct nva3_devinit_priv *priv = (void *)devinit;
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_pll info;
+       int N, fN, M, P;
+       int ret;
+
+       ret = nvbios_pll_parse(bios, type, &info);
+       if (ret)
+               return ret;
+
+       ret = nva3_pll_calc(nv_subdev(devinit), &info, freq, &N, &fN, &M, &P);
+       if (ret < 0)
+               return ret;
+
+       switch (info.type) {
+       case PLL_VPLL0:
+       case PLL_VPLL1:
+               nv_wr32(priv, info.reg + 0, 0x50000610);
+               nv_mask(priv, info.reg + 4, 0x003fffff,
+                                           (P << 16) | (M << 8) | N);
+               nv_wr32(priv, info.reg + 8, fN);
+               break;
+       default:
+               nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int
+nva3_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
+{
+       struct nv50_devinit_priv *priv;
+       int ret;
+
+       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.pll_set = nva3_devinit_pll_set;
+       return 0;
+}
+
+struct nouveau_oclass
+nva3_devinit_oclass = {
+       .handle = NV_SUBDEV(DEVINIT, 0xa3),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nva3_devinit_ctor,
+               .dtor = _nouveau_devinit_dtor,
+               .init = nv50_devinit_init,
+               .fini = _nouveau_devinit_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c
new file mode 100644 (file)
index 0000000..19e265b
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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 int
+nvc0_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq)
+{
+       struct nvc0_devinit_priv *priv = (void *)devinit;
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_pll info;
+       int N, fN, M, P;
+       int ret;
+
+       ret = nvbios_pll_parse(bios, type, &info);
+       if (ret)
+               return ret;
+
+       ret = nva3_pll_calc(nv_subdev(devinit), &info, freq, &N, &fN, &M, &P);
+       if (ret < 0)
+               return ret;
+
+       switch (info.type) {
+       case PLL_VPLL0:
+       case PLL_VPLL1:
+       case PLL_VPLL2:
+       case PLL_VPLL3:
+               nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100);
+               nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M);
+               nv_wr32(priv, info.reg + 0x10, fN << 16);
+               break;
+       default:
+               nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int
+nvc0_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
+{
+       struct nv50_devinit_priv *priv;
+       int ret;
+
+       ret = nouveau_devinit_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       priv->base.pll_set = nvc0_devinit_pll_set;
+       if (nv_rd32(priv, 0x022500) & 0x00000001)
+               priv->base.post = true;
+       return 0;
+}
+
+struct nouveau_oclass
+nvc0_devinit_oclass = {
+       .handle = NV_SUBDEV(DEVINIT, 0xc0),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_devinit_ctor,
+               .dtor = _nouveau_devinit_dtor,
+               .init = nv50_devinit_init,
+               .fini = _nouveau_devinit_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h b/drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h
new file mode 100644 (file)
index 0000000..7d622e2
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef __NVKM_DEVINIT_PRIV_H__
+#define __NVKM_DEVINIT_PRIV_H__
+
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+#include <subdev/clock/pll.h>
+#include <subdev/devinit.h>
+
+void nv04_devinit_dtor(struct nouveau_object *);
+int  nv04_devinit_init(struct nouveau_object *);
+int  nv04_devinit_fini(struct nouveau_object *, bool);
+int  nv04_devinit_pll_set(struct nouveau_devinit *, u32, u32);
+
+void setPLL_single(struct nouveau_devinit *, u32, struct nouveau_pll_vals *);
+void setPLL_double_highregs(struct nouveau_devinit *, u32, struct nouveau_pll_vals *);
+void setPLL_double_lowregs(struct nouveau_devinit *, u32, struct nouveau_pll_vals *);
+
+
+struct nv50_devinit_priv {
+       struct nouveau_devinit base;
+};
+
+int  nv50_devinit_init(struct nouveau_object *);
+
+#endif
index d62045f..821cd75 100644 (file)
@@ -57,7 +57,57 @@ nouveau_fb_bios_memtype(struct nouveau_bios *bios)
 }
 
 int
-nouveau_fb_preinit(struct nouveau_fb *pfb)
+_nouveau_fb_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nouveau_fb *pfb = (void *)object;
+       int ret;
+
+       ret = nv_ofuncs(pfb->ram)->fini(nv_object(pfb->ram), suspend);
+       if (ret && suspend)
+               return ret;
+
+       return nouveau_subdev_fini(&pfb->base, suspend);
+}
+
+int
+_nouveau_fb_init(struct nouveau_object *object)
+{
+       struct nouveau_fb *pfb = (void *)object;
+       int ret, i;
+
+       ret = nouveau_subdev_init(&pfb->base);
+       if (ret)
+               return ret;
+
+       ret = nv_ofuncs(pfb->ram)->init(nv_object(pfb->ram));
+       if (ret)
+               return ret;
+
+       for (i = 0; i < pfb->tile.regions; i++)
+               pfb->tile.prog(pfb, i, &pfb->tile.region[i]);
+
+       return 0;
+}
+
+void
+_nouveau_fb_dtor(struct nouveau_object *object)
+{
+       struct nouveau_fb *pfb = (void *)object;
+       int i;
+
+       for (i = 0; i < pfb->tile.regions; i++)
+               pfb->tile.fini(pfb, i, &pfb->tile.region[i]);
+       nouveau_mm_fini(&pfb->tags);
+       nouveau_mm_fini(&pfb->vram);
+
+       nouveau_object_ref(NULL, (struct nouveau_object **)&pfb->ram);
+       nouveau_subdev_destroy(&pfb->base);
+}
+
+int
+nouveau_fb_create_(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, struct nouveau_oclass *ramcls,
+                  int length, void **pobject)
 {
        static const char *name[] = {
                [NV_MEM_TYPE_UNKNOWN] = "unknown",
@@ -72,69 +122,42 @@ nouveau_fb_preinit(struct nouveau_fb *pfb)
                [NV_MEM_TYPE_GDDR4  ] = "GDDR4",
                [NV_MEM_TYPE_GDDR5  ] = "GDDR5",
        };
-       int ret, tags;
+       struct nouveau_object *ram;
+       struct nouveau_fb *pfb;
+       int ret;
 
-       tags = pfb->ram.init(pfb);
-       if (tags < 0 || !pfb->ram.size) {
+       ret = nouveau_subdev_create_(parent, engine, oclass, 0, "PFB", "fb",
+                                    length, pobject);
+       pfb = *pobject;
+       if (ret)
+               return ret;
+
+       ret = nouveau_object_ctor(nv_object(pfb), nv_object(pfb),
+                                 ramcls, NULL, 0, &ram);
+       if (ret) {
                nv_fatal(pfb, "error detecting memory configuration!!\n");
-               return (tags < 0) ? tags : -ERANGE;
+               return ret;
        }
 
+       atomic_dec(&ram->parent->refcount);
+       atomic_dec(&ram->engine->refcount);
+       pfb->ram = (void *)ram;
+
        if (!nouveau_mm_initialised(&pfb->vram)) {
-               ret = nouveau_mm_init(&pfb->vram, 0, pfb->ram.size >> 12, 1);
+               ret = nouveau_mm_init(&pfb->vram, 0, pfb->ram->size >> 12, 1);
                if (ret)
                        return ret;
        }
 
        if (!nouveau_mm_initialised(&pfb->tags)) {
-               ret = nouveau_mm_init(&pfb->tags, 0, tags ? ++tags : 0, 1);
+               ret = nouveau_mm_init(&pfb->tags, 0, pfb->ram->tags ?
+                                    ++pfb->ram->tags : 0, 1);
                if (ret)
                        return ret;
        }
 
-       nv_info(pfb, "RAM type: %s\n", name[pfb->ram.type]);
-       nv_info(pfb, "RAM size: %d MiB\n", (int)(pfb->ram.size >> 20));
-       nv_info(pfb, "   ZCOMP: %d tags\n", tags);
+       nv_info(pfb, "RAM type: %s\n", name[pfb->ram->type]);
+       nv_info(pfb, "RAM size: %d MiB\n", (int)(pfb->ram->size >> 20));
+       nv_info(pfb, "   ZCOMP: %d tags\n", pfb->ram->tags);
        return 0;
 }
-
-void
-nouveau_fb_destroy(struct nouveau_fb *pfb)
-{
-       int i;
-
-       for (i = 0; i < pfb->tile.regions; i++)
-               pfb->tile.fini(pfb, i, &pfb->tile.region[i]);
-       nouveau_mm_fini(&pfb->tags);
-       nouveau_mm_fini(&pfb->vram);
-
-       nouveau_subdev_destroy(&pfb->base);
-}
-
-void
-_nouveau_fb_dtor(struct nouveau_object *object)
-{
-       struct nouveau_fb *pfb = (void *)object;
-       nouveau_fb_destroy(pfb);
-}
-int
-nouveau_fb_init(struct nouveau_fb *pfb)
-{
-       int ret, i;
-
-       ret = nouveau_subdev_init(&pfb->base);
-       if (ret)
-               return ret;
-
-       for (i = 0; i < pfb->tile.regions; i++)
-               pfb->tile.prog(pfb, i, &pfb->tile.region[i]);
-
-       return 0;
-}
-
-int
-_nouveau_fb_init(struct nouveau_object *object)
-{
-       struct nouveau_fb *pfb = (void *)object;
-       return nouveau_fb_init(pfb);
-}
index 6e369f8..1f103c7 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
-#define NV04_PFB_BOOT_0                                                0x00100000
-#      define NV04_PFB_BOOT_0_RAM_AMOUNT                       0x00000003
-#      define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB                  0x00000000
-#      define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB                   0x00000001
-#      define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB                   0x00000002
-#      define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB                  0x00000003
-#      define NV04_PFB_BOOT_0_RAM_WIDTH_128                    0x00000004
-#      define NV04_PFB_BOOT_0_RAM_TYPE                         0x00000028
-#      define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT             0x00000000
-#      define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT            0x00000008
-#      define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK      0x00000010
-#      define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT            0x00000018
-#      define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT            0x00000020
-#      define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16         0x00000028
-#      define NV04_PFB_BOOT_0_UMA_ENABLE                       0x00000100
-#      define NV04_PFB_BOOT_0_UMA_SIZE                         0x0000f000
 #define NV04_PFB_CFG0                                          0x00100200
 
 struct nv04_fb_priv {
@@ -55,37 +39,6 @@ nv04_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags)
        return false;
 }
 
-static int
-nv04_fb_vram_init(struct nouveau_fb *pfb)
-{
-       u32 boot0 = nv_rd32(pfb, NV04_PFB_BOOT_0);
-       if (boot0 & 0x00000100) {
-               pfb->ram.size  = ((boot0 >> 12) & 0xf) * 2 + 2;
-               pfb->ram.size *= 1024 * 1024;
-       } else {
-               switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) {
-               case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB:
-                       pfb->ram.size = 32 * 1024 * 1024;
-                       break;
-               case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB:
-                       pfb->ram.size = 16 * 1024 * 1024;
-                       break;
-               case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB:
-                       pfb->ram.size = 8 * 1024 * 1024;
-                       break;
-               case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB:
-                       pfb->ram.size = 4 * 1024 * 1024;
-                       break;
-               }
-       }
-
-       if ((boot0 & 0x00000038) <= 0x10)
-               pfb->ram.type = NV_MEM_TYPE_SGRAM;
-       else
-               pfb->ram.type = NV_MEM_TYPE_SDRAM;
-       return 0;
-}
-
 static int
 nv04_fb_init(struct nouveau_object *object)
 {
@@ -112,14 +65,13 @@ nv04_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv04_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       ret = nouveau_fb_create(parent, engine, oclass, &nv04_ram_oclass, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
        priv->base.memtype_valid = nv04_fb_memtype_valid;
-       priv->base.ram.init = nv04_fb_vram_init;
-       return nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 struct nouveau_oclass
index edbbe26..be069b5 100644 (file)
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv10_fb_priv {
        struct nouveau_fb base;
 };
 
-static int
-nv10_fb_vram_init(struct nouveau_fb *pfb)
-{
-       u32 cfg0 = nv_rd32(pfb, 0x100200);
-       if (cfg0 & 0x00000001)
-               pfb->ram.type = NV_MEM_TYPE_DDR1;
-       else
-               pfb->ram.type = NV_MEM_TYPE_SDRAM;
-
-       pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000;
-       return 0;
-}
-
 void
 nv10_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
                  u32 flags, struct nouveau_fb_tile *tile)
@@ -78,18 +65,17 @@ nv10_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv10_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv10_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 struct nouveau_oclass
index 4836684..57a2af0 100644 (file)
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv1a_fb_priv {
        struct nouveau_fb base;
 };
 
-static int
-nv1a_fb_vram_init(struct nouveau_fb *pfb)
-{
-       struct pci_dev *bridge;
-       u32 mem, mib;
-
-       bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1));
-       if (!bridge) {
-               nv_fatal(pfb, "no bridge device\n");
-               return -ENODEV;
-       }
-
-       if (nv_device(pfb)->chipset == 0x1a) {
-               pci_read_config_dword(bridge, 0x7c, &mem);
-               mib = ((mem >> 6) & 31) + 1;
-       } else {
-               pci_read_config_dword(bridge, 0x84, &mem);
-               mib = ((mem >> 4) & 127) + 1;
-       }
-
-       pfb->ram.type = NV_MEM_TYPE_STOLEN;
-       pfb->ram.size = mib * 1024 * 1024;
-       return 0;
-}
-
 static int
 nv1a_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
             struct nouveau_oclass *oclass, void *data, u32 size,
@@ -63,18 +38,17 @@ nv1a_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv1a_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv1a_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 struct nouveau_oclass
index 5d14612..b18c4e6 100644 (file)
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv20_fb_priv {
        struct nouveau_fb base;
 };
 
-int
-nv20_fb_vram_init(struct nouveau_fb *pfb)
-{
-       u32 pbus1218 = nv_rd32(pfb, 0x001218);
-
-       switch (pbus1218 & 0x00000300) {
-       case 0x00000000: pfb->ram.type = NV_MEM_TYPE_SDRAM; break;
-       case 0x00000100: pfb->ram.type = NV_MEM_TYPE_DDR1; break;
-       case 0x00000200: pfb->ram.type = NV_MEM_TYPE_GDDR3; break;
-       case 0x00000300: pfb->ram.type = NV_MEM_TYPE_GDDR2; break;
-       }
-       pfb->ram.size  = (nv_rd32(pfb, 0x10020c) & 0xff000000);
-       pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
-
-       return nv_rd32(pfb, 0x100320);
-}
-
 void
 nv20_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
                  u32 flags, struct nouveau_fb_tile *tile)
@@ -65,7 +48,7 @@ nv20_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
                  struct nouveau_fb_tile *tile)
 {
        u32 tiles = DIV_ROUND_UP(size, 0x40);
-       u32 tags  = round_up(tiles / pfb->ram.parts, 0x40);
+       u32 tags  = round_up(tiles / pfb->ram->parts, 0x40);
        if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
                if (!(flags & 2)) tile->zcomp = 0x00000000; /* Z16 */
                else              tile->zcomp = 0x04000000; /* Z24S8 */
@@ -105,19 +88,18 @@ nv20_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv20_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv20_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 struct nouveau_oclass
index 0042ace..32ccabf 100644 (file)
@@ -24,7 +24,7 @@
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv25_fb_priv {
        struct nouveau_fb base;
@@ -35,7 +35,7 @@ nv25_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
                  struct nouveau_fb_tile *tile)
 {
        u32 tiles = DIV_ROUND_UP(size, 0x40);
-       u32 tags  = round_up(tiles / pfb->ram.parts, 0x40);
+       u32 tags  = round_up(tiles / pfb->ram->parts, 0x40);
        if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
                if (!(flags & 2)) tile->zcomp = 0x00100000; /* Z16 */
                else              tile->zcomp = 0x00200000; /* Z24S8 */
@@ -54,19 +54,18 @@ nv25_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv25_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv20_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 struct nouveau_oclass
index a7ba0d0..bef756d 100644 (file)
@@ -24,7 +24,7 @@
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv30_fb_priv {
        struct nouveau_fb base;
@@ -54,7 +54,7 @@ nv30_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
                  struct nouveau_fb_tile *tile)
 {
        u32 tiles = DIV_ROUND_UP(size, 0x40);
-       u32 tags  = round_up(tiles / pfb->ram.parts, 0x40);
+       u32 tags  = round_up(tiles / pfb->ram->parts, 0x40);
        if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
                if (flags & 2) tile->zcomp |= 0x01000000; /* Z16 */
                else           tile->zcomp |= 0x02000000; /* Z24S8 */
@@ -132,19 +132,18 @@ nv30_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv30_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv20_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 struct nouveau_oclass
index 092f6f4..097d8e3 100644 (file)
@@ -24,7 +24,7 @@
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv35_fb_priv {
        struct nouveau_fb base;
@@ -35,7 +35,7 @@ nv35_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
                  struct nouveau_fb_tile *tile)
 {
        u32 tiles = DIV_ROUND_UP(size, 0x40);
-       u32 tags  = round_up(tiles / pfb->ram.parts, 0x40);
+       u32 tags  = round_up(tiles / pfb->ram->parts, 0x40);
        if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
                if (flags & 2) tile->zcomp |= 0x04000000; /* Z16 */
                else           tile->zcomp |= 0x08000000; /* Z24S8 */
@@ -55,19 +55,18 @@ nv35_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv35_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv20_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 struct nouveau_oclass
index 797ab3b..9d6d9df 100644 (file)
@@ -24,7 +24,7 @@
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv36_fb_priv {
        struct nouveau_fb base;
@@ -35,7 +35,7 @@ nv36_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
                  struct nouveau_fb_tile *tile)
 {
        u32 tiles = DIV_ROUND_UP(size, 0x40);
-       u32 tags  = round_up(tiles / pfb->ram.parts, 0x40);
+       u32 tags  = round_up(tiles / pfb->ram->parts, 0x40);
        if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
                if (flags & 2) tile->zcomp |= 0x10000000; /* Z16 */
                else           tile->zcomp |= 0x20000000; /* Z24S8 */
@@ -55,19 +55,18 @@ nv36_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv36_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv20_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 struct nouveau_oclass
index 65e131b..33b4393 100644 (file)
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv40_fb_priv {
        struct nouveau_fb base;
 };
 
-static int
-nv40_fb_vram_init(struct nouveau_fb *pfb)
-{
-       u32 pbus1218 = nv_rd32(pfb, 0x001218);
-       switch (pbus1218 & 0x00000300) {
-       case 0x00000000: pfb->ram.type = NV_MEM_TYPE_SDRAM; break;
-       case 0x00000100: pfb->ram.type = NV_MEM_TYPE_DDR1; break;
-       case 0x00000200: pfb->ram.type = NV_MEM_TYPE_GDDR3; break;
-       case 0x00000300: pfb->ram.type = NV_MEM_TYPE_DDR2; break;
-       }
-
-       pfb->ram.size  =  nv_rd32(pfb, 0x10020c) & 0xff000000;
-       pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
-       return nv_rd32(pfb, 0x100320);
-}
-
 void
 nv40_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
                  struct nouveau_fb_tile *tile)
 {
        u32 tiles = DIV_ROUND_UP(size, 0x80);
-       u32 tags  = round_up(tiles / pfb->ram.parts, 0x100);
+       u32 tags  = round_up(tiles / pfb->ram->parts, 0x100);
        if ( (flags & 2) &&
            !nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
                tile->zcomp  = 0x28000000; /* Z24S8_SPLIT_GRAD */
@@ -85,19 +69,18 @@ nv40_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv40_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv40_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 
index e9e5a08..02cd837 100644 (file)
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv41_fb_priv {
        struct nouveau_fb base;
 };
 
-int
-nv41_fb_vram_init(struct nouveau_fb *pfb)
-{
-       u32 pfb474 = nv_rd32(pfb, 0x100474);
-       if (pfb474 & 0x00000004)
-               pfb->ram.type = NV_MEM_TYPE_GDDR3;
-       if (pfb474 & 0x00000002)
-               pfb->ram.type = NV_MEM_TYPE_DDR2;
-       if (pfb474 & 0x00000001)
-               pfb->ram.type = NV_MEM_TYPE_DDR1;
-
-       pfb->ram.size =   nv_rd32(pfb, 0x10020c) & 0xff000000;
-       pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
-       return nv_rd32(pfb, 0x100320);
-}
-
 void
 nv41_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
 {
@@ -78,19 +62,18 @@ nv41_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv41_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv41_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 
index ae89b50..c5246c2 100644 (file)
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv44_fb_priv {
        struct nouveau_fb base;
 };
 
-int
-nv44_fb_vram_init(struct nouveau_fb *pfb)
-{
-       u32 pfb474 = nv_rd32(pfb, 0x100474);
-       if (pfb474 & 0x00000004)
-               pfb->ram.type = NV_MEM_TYPE_GDDR3;
-       if (pfb474 & 0x00000002)
-               pfb->ram.type = NV_MEM_TYPE_DDR2;
-       if (pfb474 & 0x00000001)
-               pfb->ram.type = NV_MEM_TYPE_DDR1;
-
-       pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000;
-       return 0;
-}
-
 static void
 nv44_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
                  u32 flags, struct nouveau_fb_tile *tile)
@@ -87,18 +72,17 @@ nv44_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv44_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv44_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 
index 589b93e..e2b5790 100644 (file)
@@ -24,7 +24,7 @@
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv46_fb_priv {
        struct nouveau_fb base;
@@ -52,18 +52,17 @@ nv46_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv46_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv44_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 
index 818bba3..fe6a227 100644 (file)
@@ -24,7 +24,7 @@
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv47_fb_priv {
        struct nouveau_fb base;
@@ -38,19 +38,18 @@ nv47_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv47_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv41_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 
index 84a31af..5eca99b 100644 (file)
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv49_fb_priv {
        struct nouveau_fb base;
 };
 
-static int
-nv49_fb_vram_init(struct nouveau_fb *pfb)
-{
-       u32 pfb914 = nv_rd32(pfb, 0x100914);
-
-       switch (pfb914 & 0x00000003) {
-       case 0x00000000: pfb->ram.type = NV_MEM_TYPE_DDR1; break;
-       case 0x00000001: pfb->ram.type = NV_MEM_TYPE_DDR2; break;
-       case 0x00000002: pfb->ram.type = NV_MEM_TYPE_GDDR3; break;
-       case 0x00000003: break;
-       }
-
-       pfb->ram.size =   nv_rd32(pfb, 0x10020c) & 0xff000000;
-       pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
-       return nv_rd32(pfb, 0x100320);
-}
-
 static int
 nv49_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
             struct nouveau_oclass *oclass, void *data, u32 size,
@@ -55,20 +38,18 @@ nv49_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv49_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv49_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 
index 797fd55..1190b78 100644 (file)
  *
  */
 
-#include <subdev/fb.h>
+#include "priv.h"
 
 struct nv4e_fb_priv {
        struct nouveau_fb base;
 };
 
-static int
-nv4e_fb_vram_init(struct nouveau_fb *pfb)
-{
-       pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000;
-       pfb->ram.type = NV_MEM_TYPE_STOLEN;
-       return 0;
-}
-
 static int
 nv4e_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
             struct nouveau_oclass *oclass, void *data, u32 size,
@@ -46,18 +38,17 @@ nv4e_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv4e_fb_priv *priv;
        int ret;
 
-       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       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.ram.init = nv4e_fb_vram_init;
        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 nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 struct nouveau_oclass
index 0772ec9..da614ec 100644 (file)
@@ -27,7 +27,7 @@
 #include <core/engctx.h>
 #include <core/object.h>
 
-#include <subdev/fb.h>
+#include "priv.h"
 #include <subdev/bios.h>
 
 struct nv50_fb_priv {
@@ -36,7 +36,8 @@ struct nv50_fb_priv {
        dma_addr_t r100c08;
 };
 
-static int types[0x80] = {
+int
+nv50_fb_memtype[0x80] = {
        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0,
        1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 0,
@@ -50,192 +51,7 @@ static int types[0x80] = {
 static bool
 nv50_fb_memtype_valid(struct nouveau_fb *pfb, u32 memtype)
 {
-       return types[(memtype & 0xff00) >> 8] != 0;
-}
-
-static u32
-nv50_fb_vram_rblock(struct nouveau_fb *pfb)
-{
-       int i, parts, colbits, rowbitsa, rowbitsb, banks;
-       u64 rowsize, predicted;
-       u32 r0, r4, rt, ru, rblock_size;
-
-       r0 = nv_rd32(pfb, 0x100200);
-       r4 = nv_rd32(pfb, 0x100204);
-       rt = nv_rd32(pfb, 0x100250);
-       ru = nv_rd32(pfb, 0x001540);
-       nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru);
-
-       for (i = 0, parts = 0; i < 8; i++) {
-               if (ru & (0x00010000 << i))
-                       parts++;
-       }
-
-       colbits  =  (r4 & 0x0000f000) >> 12;
-       rowbitsa = ((r4 & 0x000f0000) >> 16) + 8;
-       rowbitsb = ((r4 & 0x00f00000) >> 20) + 8;
-       banks    = 1 << (((r4 & 0x03000000) >> 24) + 2);
-
-       rowsize = parts * banks * (1 << colbits) * 8;
-       predicted = rowsize << rowbitsa;
-       if (r0 & 0x00000004)
-               predicted += rowsize << rowbitsb;
-
-       if (predicted != pfb->ram.size) {
-               nv_warn(pfb, "memory controller reports %d MiB VRAM\n",
-                       (u32)(pfb->ram.size >> 20));
-       }
-
-       rblock_size = rowsize;
-       if (rt & 1)
-               rblock_size *= 3;
-
-       nv_debug(pfb, "rblock %d bytes\n", rblock_size);
-       return rblock_size;
-}
-
-static int
-nv50_fb_vram_init(struct nouveau_fb *pfb)
-{
-       struct nouveau_device *device = nv_device(pfb);
-       struct nouveau_bios *bios = nouveau_bios(device);
-       const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
-       const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
-       u32 size, tags = 0;
-       int ret;
-
-       pfb->ram.size = nv_rd32(pfb, 0x10020c);
-       pfb->ram.size = (pfb->ram.size & 0xffffff00) |
-                      ((pfb->ram.size & 0x000000ff) << 32);
-
-       size = (pfb->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;
-
-               pfb->ram.type   = NV_MEM_TYPE_STOLEN;
-               pfb->ram.stolen = (u64)nv_rd32(pfb, 0x100e10) << 12;
-               break;
-       default:
-               switch (nv_rd32(pfb, 0x100714) & 0x00000007) {
-               case 0: pfb->ram.type = NV_MEM_TYPE_DDR1; break;
-               case 1:
-                       if (nouveau_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3)
-                               pfb->ram.type = NV_MEM_TYPE_DDR3;
-                       else
-                               pfb->ram.type = NV_MEM_TYPE_DDR2;
-                       break;
-               case 2: pfb->ram.type = NV_MEM_TYPE_GDDR3; break;
-               case 3: pfb->ram.type = NV_MEM_TYPE_GDDR4; break;
-               case 4: pfb->ram.type = NV_MEM_TYPE_GDDR5; break;
-               default:
-                       break;
-               }
-
-               ret = nouveau_mm_init(&pfb->vram, rsvd_head, size,
-                                     nv50_fb_vram_rblock(pfb) >> 12);
-               if (ret)
-                       return ret;
-
-               pfb->ram.ranks = (nv_rd32(pfb, 0x100200) & 0x4) ? 2 : 1;
-               tags = nv_rd32(pfb, 0x100320);
-               break;
-       }
-
-       return tags;
-}
-
-static int
-nv50_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
-                u32 memtype, struct nouveau_mem **pmem)
-{
-       struct nv50_fb_priv *priv = (void *)pfb;
-       struct nouveau_mm *heap = &priv->base.vram;
-       struct nouveau_mm *tags = &priv->base.tags;
-       struct nouveau_mm_node *r;
-       struct nouveau_mem *mem;
-       int comp = (memtype & 0x300) >> 8;
-       int type = (memtype & 0x07f);
-       int back = (memtype & 0x800);
-       int min, max, ret;
-
-       max = (size >> 12);
-       min = ncmin ? (ncmin >> 12) : max;
-       align >>= 12;
-
-       mem = kzalloc(sizeof(*mem), GFP_KERNEL);
-       if (!mem)
-               return -ENOMEM;
-
-       mutex_lock(&pfb->base.mutex);
-       if (comp) {
-               if (align == 16) {
-                       int n = (max >> 4) * comp;
-
-                       ret = nouveau_mm_head(tags, 1, n, n, 1, &mem->tag);
-                       if (ret)
-                               mem->tag = NULL;
-               }
-
-               if (unlikely(!mem->tag))
-                       comp = 0;
-       }
-
-       INIT_LIST_HEAD(&mem->regions);
-       mem->memtype = (comp << 7) | type;
-       mem->size = max;
-
-       type = types[type];
-       do {
-               if (back)
-                       ret = nouveau_mm_tail(heap, type, max, min, align, &r);
-               else
-                       ret = nouveau_mm_head(heap, type, max, min, align, &r);
-               if (ret) {
-                       mutex_unlock(&pfb->base.mutex);
-                       pfb->ram.put(pfb, &mem);
-                       return ret;
-               }
-
-               list_add_tail(&r->rl_entry, &mem->regions);
-               max -= r->length;
-       } while (max);
-       mutex_unlock(&pfb->base.mutex);
-
-       r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);
-       mem->offset = (u64)r->offset << 12;
-       *pmem = mem;
-       return 0;
-}
-
-void
-nv50_fb_vram_del(struct nouveau_fb *pfb, struct nouveau_mem **pmem)
-{
-       struct nv50_fb_priv *priv = (void *)pfb;
-       struct nouveau_mm_node *this;
-       struct nouveau_mem *mem;
-
-       mem = *pmem;
-       *pmem = NULL;
-       if (unlikely(mem == NULL))
-               return;
-
-       mutex_lock(&pfb->base.mutex);
-       while (!list_empty(&mem->regions)) {
-               this = list_first_entry(&mem->regions, typeof(*this), rl_entry);
-
-               list_del(&this->rl_entry);
-               nouveau_mm_free(&priv->base.vram, &this);
-       }
-
-       nouveau_mm_free(&priv->base.tags, &mem->tag);
-       mutex_unlock(&pfb->base.mutex);
-
-       kfree(mem);
+       return nv50_fb_memtype[(memtype & 0xff00) >> 8] != 0;
 }
 
 static const struct nouveau_enum vm_dispatch_subclients[] = {
@@ -432,7 +248,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, &priv);
+       ret = nouveau_fb_create(parent, engine, oclass, &nv50_ram_oclass, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
@@ -449,11 +265,8 @@ nv50_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        }
 
        priv->base.memtype_valid = nv50_fb_memtype_valid;
-       priv->base.ram.init = nv50_fb_vram_init;
-       priv->base.ram.get = nv50_fb_vram_new;
-       priv->base.ram.put = nv50_fb_vram_del;
        nv_subdev(priv)->intr = nv50_fb_intr;
-       return nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 static void
index 86ad592..f35d76f 100644 (file)
@@ -22,9 +22,7 @@
  * Authors: Ben Skeggs
  */
 
-#include <subdev/fb.h>
-#include <subdev/ltcg.h>
-#include <subdev/bios.h>
+#include "priv.h"
 
 struct nvc0_fb_priv {
        struct nouveau_fb base;
@@ -34,7 +32,6 @@ struct nvc0_fb_priv {
 
 extern const u8 nvc0_pte_storage_type_map[256];
 
-
 static bool
 nvc0_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags)
 {
@@ -42,137 +39,6 @@ nvc0_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags)
        return likely((nvc0_pte_storage_type_map[memtype] != 0xff));
 }
 
-static int
-nvc0_fb_vram_init(struct nouveau_fb *pfb)
-{
-       struct nouveau_bios *bios = nouveau_bios(pfb);
-       const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
-       const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
-       u32 parts = nv_rd32(pfb, 0x022438);
-       u32 pmask = nv_rd32(pfb, 0x022554);
-       u32 bsize = nv_rd32(pfb, 0x10f20c);
-       u32 offset, length;
-       bool uniform = true;
-       int ret, part;
-
-       nv_debug(pfb, "0x100800: 0x%08x\n", nv_rd32(pfb, 0x100800));
-       nv_debug(pfb, "parts 0x%08x mask 0x%08x\n", parts, pmask);
-
-       pfb->ram.type = nouveau_fb_bios_memtype(bios);
-       pfb->ram.ranks = (nv_rd32(pfb, 0x10f200) & 0x00000004) ? 2 : 1;
-
-       /* read amount of vram attached to each memory controller */
-       for (part = 0; part < parts; part++) {
-               if (!(pmask & (1 << part))) {
-                       u32 psize = nv_rd32(pfb, 0x11020c + (part * 0x1000));
-                       if (psize != bsize) {
-                               if (psize < bsize)
-                                       bsize = psize;
-                               uniform = false;
-                       }
-
-                       nv_debug(pfb, "%d: mem_amount 0x%08x\n", part, psize);
-                       pfb->ram.size += (u64)psize << 20;
-               }
-       }
-
-       /* if all controllers have the same amount attached, there's no holes */
-       if (uniform) {
-               offset = rsvd_head;
-               length = (pfb->ram.size >> 12) - rsvd_head - rsvd_tail;
-               return nouveau_mm_init(&pfb->vram, offset, length, 1);
-       }
-
-       /* otherwise, address lowest common amount from 0GiB */
-       ret = nouveau_mm_init(&pfb->vram, rsvd_head, (bsize << 8) * parts, 1);
-       if (ret)
-               return ret;
-
-       /* and the rest starting from (8GiB + common_size) */
-       offset = (0x0200000000ULL >> 12) + (bsize << 8);
-       length = (pfb->ram.size >> 12) - (bsize << 8) - rsvd_tail;
-
-       ret = nouveau_mm_init(&pfb->vram, offset, length, 0);
-       if (ret) {
-               nouveau_mm_fini(&pfb->vram);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int
-nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
-                u32 memtype, struct nouveau_mem **pmem)
-{
-       struct nouveau_mm *mm = &pfb->vram;
-       struct nouveau_mm_node *r;
-       struct nouveau_mem *mem;
-       int type = (memtype & 0x0ff);
-       int back = (memtype & 0x800);
-       int ret;
-       const bool comp = nvc0_pte_storage_type_map[type] != type;
-
-       size  >>= 12;
-       align >>= 12;
-       ncmin >>= 12;
-       if (!ncmin)
-               ncmin = size;
-
-       mem = kzalloc(sizeof(*mem), GFP_KERNEL);
-       if (!mem)
-               return -ENOMEM;
-
-       INIT_LIST_HEAD(&mem->regions);
-       mem->size = size;
-
-       mutex_lock(&pfb->base.mutex);
-       if (comp) {
-               struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb->base.base.parent);
-
-               /* compression only works with lpages */
-               if (align == (1 << (17 - 12))) {
-                       int n = size >> 5;
-                       ltcg->tags_alloc(ltcg, n, &mem->tag);
-               }
-               if (unlikely(!mem->tag))
-                       type = nvc0_pte_storage_type_map[type];
-       }
-       mem->memtype = type;
-
-       do {
-               if (back)
-                       ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r);
-               else
-                       ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r);
-               if (ret) {
-                       mutex_unlock(&pfb->base.mutex);
-                       pfb->ram.put(pfb, &mem);
-                       return ret;
-               }
-
-               list_add_tail(&r->rl_entry, &mem->regions);
-               size -= r->length;
-       } while (size);
-       mutex_unlock(&pfb->base.mutex);
-
-       r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);
-       mem->offset = (u64)r->offset << 12;
-       *pmem = mem;
-       return 0;
-}
-
-static void
-nvc0_fb_vram_del(struct nouveau_fb *pfb, struct nouveau_mem **pmem)
-{
-       struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb->base.base.parent);
-
-       if ((*pmem)->tag)
-               ltcg->tags_free(ltcg, &(*pmem)->tag);
-
-       nv50_fb_vram_del(pfb, pmem);
-}
-
 static int
 nvc0_fb_init(struct nouveau_object *object)
 {
@@ -212,15 +78,12 @@ 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, &priv);
+       ret = nouveau_fb_create(parent, engine, oclass, &nvc0_ram_oclass, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
        priv->base.memtype_valid = nvc0_fb_memtype_valid;
-       priv->base.ram.init = nvc0_fb_vram_init;
-       priv->base.ram.get = nvc0_fb_vram_new;
-       priv->base.ram.put = nvc0_fb_vram_del;
 
        priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
        if (priv->r100c10_page) {
@@ -231,7 +94,7 @@ nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                        return -EFAULT;
        }
 
-       return nouveau_fb_preinit(&priv->base);
+       return 0;
 }
 
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
new file mode 100644 (file)
index 0000000..6c974dd
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef __NVKM_FB_PRIV_H__
+#define __NVKM_FB_PRIV_H__
+
+#include <subdev/fb.h>
+
+#define nouveau_ram_create(p,e,o,d)                                            \
+       nouveau_object_create_((p), (e), (o), 0, sizeof(**d), (void **)d)
+#define nouveau_ram_destroy(p)                                                 \
+       nouveau_object_destroy(&(p)->base)
+#define nouveau_ram_init(p)                                                    \
+       nouveau_object_init(&(p)->base)
+#define nouveau_ram_fini(p,s)                                                  \
+       nouveau_object_fini(&(p)->base, (s))
+
+#define _nouveau_ram_dtor nouveau_object_destroy
+#define _nouveau_ram_init nouveau_object_init
+#define _nouveau_ram_fini nouveau_object_fini
+
+extern struct nouveau_oclass nv04_ram_oclass;
+extern struct nouveau_oclass nv10_ram_oclass;
+extern struct nouveau_oclass nv1a_ram_oclass;
+extern struct nouveau_oclass nv20_ram_oclass;
+extern struct nouveau_oclass nv40_ram_oclass;
+extern struct nouveau_oclass nv41_ram_oclass;
+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 nvc0_ram_oclass;
+
+#define nouveau_fb_create(p,e,c,r,d)                                           \
+       nouveau_fb_create_((p), (e), (c), (r), sizeof(**d), (void **)d)
+#define nouveau_fb_destroy(p) ({                                               \
+       struct nouveau_fb *pfb = (p);                                          \
+       _nouveau_fb_dtor(nv_object(pfb));                                      \
+})
+#define nouveau_fb_init(p) ({                                                  \
+       struct nouveau_fb *pfb = (p);                                          \
+       _nouveau_fb_init(nv_object(pfb));                                      \
+})
+#define nouveau_fb_fini(p,s) ({                                                \
+       struct nouveau_fb *pfb = (p);                                          \
+       _nouveau_fb_fini(nv_object(pfb), (s));                                 \
+})
+
+int nouveau_fb_create_(struct nouveau_object *, struct nouveau_object *,
+                      struct nouveau_oclass *, struct nouveau_oclass *,
+                      int length, void **pobject);
+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 *);
+
+bool nv04_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];
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c
new file mode 100644 (file)
index 0000000..e781080
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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 NV04_PFB_BOOT_0                                                0x00100000
+#      define NV04_PFB_BOOT_0_RAM_AMOUNT                       0x00000003
+#      define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB                  0x00000000
+#      define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB                   0x00000001
+#      define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB                   0x00000002
+#      define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB                  0x00000003
+#      define NV04_PFB_BOOT_0_RAM_WIDTH_128                    0x00000004
+#      define NV04_PFB_BOOT_0_RAM_TYPE                         0x00000028
+#      define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT             0x00000000
+#      define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT            0x00000008
+#      define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK      0x00000010
+#      define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT            0x00000018
+#      define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT            0x00000020
+#      define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16         0x00000028
+#      define NV04_PFB_BOOT_0_UMA_ENABLE                       0x00000100
+#      define NV04_PFB_BOOT_0_UMA_SIZE                         0x0000f000
+
+#include "priv.h"
+
+static int
+nv04_ram_create(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_ram *ram;
+       u32 boot0 = nv_rd32(pfb, NV04_PFB_BOOT_0);
+       int ret;
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       if (boot0 & 0x00000100) {
+               ram->size  = ((boot0 >> 12) & 0xf) * 2 + 2;
+               ram->size *= 1024 * 1024;
+       } else {
+               switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) {
+               case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB:
+                       ram->size = 32 * 1024 * 1024;
+                       break;
+               case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB:
+                       ram->size = 16 * 1024 * 1024;
+                       break;
+               case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB:
+                       ram->size = 8 * 1024 * 1024;
+                       break;
+               case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB:
+                       ram->size = 4 * 1024 * 1024;
+                       break;
+               }
+       }
+
+       if ((boot0 & 0x00000038) <= 0x10)
+               ram->type = NV_MEM_TYPE_SGRAM;
+       else
+               ram->type = NV_MEM_TYPE_SDRAM;
+       return 0;
+}
+
+struct nouveau_oclass
+nv04_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv04_ram_create,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv10.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv10.c
new file mode 100644 (file)
index 0000000..8311f37
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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 int
+nv10_ram_create(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_ram *ram;
+       u32 cfg0 = nv_rd32(pfb, 0x100200);
+       int ret;
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       if (cfg0 & 0x00000001)
+               ram->type = NV_MEM_TYPE_DDR1;
+       else
+               ram->type = NV_MEM_TYPE_SDRAM;
+
+       ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+       return 0;
+}
+
+
+struct nouveau_oclass
+nv10_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv10_ram_create,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv1a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv1a.c
new file mode 100644 (file)
index 0000000..d0caddf
--- /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 "priv.h"
+
+static int
+nv1a_ram_create(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_ram *ram;
+       struct pci_dev *bridge;
+       u32 mem, mib;
+       int ret;
+
+       bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1));
+       if (!bridge) {
+               nv_fatal(pfb, "no bridge device\n");
+               return -ENODEV;
+       }
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       if (nv_device(pfb)->chipset == 0x1a) {
+               pci_read_config_dword(bridge, 0x7c, &mem);
+               mib = ((mem >> 6) & 31) + 1;
+       } else {
+               pci_read_config_dword(bridge, 0x84, &mem);
+               mib = ((mem >> 4) & 127) + 1;
+       }
+
+       ram->type = NV_MEM_TYPE_STOLEN;
+       ram->size = mib * 1024 * 1024;
+       return 0;
+}
+
+struct nouveau_oclass
+nv1a_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv1a_ram_create,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv20.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv20.c
new file mode 100644 (file)
index 0000000..fdc11bb
--- /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
+ */
+
+#include "priv.h"
+
+static int
+nv20_ram_create(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_ram *ram;
+       u32 pbus1218 = nv_rd32(pfb, 0x001218);
+       int ret;
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               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_GDDR2; break;
+       }
+       ram->size  = (nv_rd32(pfb, 0x10020c) & 0xff000000);
+       ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+       ram->tags  = nv_rd32(pfb, 0x100320);
+       return 0;
+}
+
+struct nouveau_oclass
+nv20_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv20_ram_create,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c
new file mode 100644 (file)
index 0000000..ee49ac4
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 int
+nv40_ram_create(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_ram *ram;
+       u32 pbus1218 = nv_rd32(pfb, 0x001218);
+       int ret;
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               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;
+       }
+
+       ram->size  =  nv_rd32(pfb, 0x10020c) & 0xff000000;
+       ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+       ram->tags  =  nv_rd32(pfb, 0x100320);
+       return 0;
+}
+
+
+struct nouveau_oclass
+nv40_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv40_ram_create,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c
new file mode 100644 (file)
index 0000000..1dab7e1
--- /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
+ */
+
+#include "priv.h"
+
+static int
+nv41_ram_create(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_ram *ram;
+       u32 pfb474 = nv_rd32(pfb, 0x100474);
+       int ret;
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       if (pfb474 & 0x00000004)
+               ram->type = NV_MEM_TYPE_GDDR3;
+       if (pfb474 & 0x00000002)
+               ram->type = NV_MEM_TYPE_DDR2;
+       if (pfb474 & 0x00000001)
+               ram->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);
+       return 0;
+}
+
+struct nouveau_oclass
+nv41_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv41_ram_create,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c
new file mode 100644 (file)
index 0000000..25fff84
--- /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 "priv.h"
+
+static int
+nv44_ram_create(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_ram *ram;
+       u32 pfb474 = nv_rd32(pfb, 0x100474);
+       int ret;
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       if (pfb474 & 0x00000004)
+               ram->type = NV_MEM_TYPE_GDDR3;
+       if (pfb474 & 0x00000002)
+               ram->type = NV_MEM_TYPE_DDR2;
+       if (pfb474 & 0x00000001)
+               ram->type = NV_MEM_TYPE_DDR1;
+
+       ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+       return 0;
+}
+
+struct nouveau_oclass
+nv44_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv44_ram_create,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c
new file mode 100644 (file)
index 0000000..19e3a9a
--- /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
+ */
+
+#include "priv.h"
+
+static int
+nv49_ram_create(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_ram *ram;
+       u32 pfb914 = nv_rd32(pfb, 0x100914);
+       int ret;
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       switch (pfb914 & 0x00000003) {
+       case 0x00000000: pfb->ram->type = NV_MEM_TYPE_DDR1; break;
+       case 0x00000001: pfb->ram->type = NV_MEM_TYPE_DDR2; break;
+       case 0x00000002: pfb->ram->type = NV_MEM_TYPE_GDDR3; break;
+       case 0x00000003: break;
+       }
+
+       pfb->ram->size  =  nv_rd32(pfb, 0x10020c) & 0xff000000;
+       pfb->ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+       pfb->ram->tags  =  nv_rd32(pfb, 0x100320);
+       return 0;
+}
+
+struct nouveau_oclass
+nv49_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv49_ram_create,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c
new file mode 100644 (file)
index 0000000..7192aa6
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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 int
+nv4e_ram_create(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_ram *ram;
+       int ret;
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       pfb->ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+       pfb->ram->type = NV_MEM_TYPE_STOLEN;
+       return 0;
+}
+
+struct nouveau_oclass
+nv4e_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv4e_ram_create,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
new file mode 100644 (file)
index 0000000..af5aa7e
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * 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 <core/mm.h>
+#include "priv.h"
+
+void
+nv50_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem)
+{
+       struct nouveau_mm_node *this;
+       struct nouveau_mem *mem;
+
+       mem = *pmem;
+       *pmem = NULL;
+       if (unlikely(mem == NULL))
+               return;
+
+       mutex_lock(&pfb->base.mutex);
+       while (!list_empty(&mem->regions)) {
+               this = list_first_entry(&mem->regions, typeof(*this), rl_entry);
+
+               list_del(&this->rl_entry);
+               nouveau_mm_free(&pfb->vram, &this);
+       }
+
+       nouveau_mm_free(&pfb->tags, &mem->tag);
+       mutex_unlock(&pfb->base.mutex);
+
+       kfree(mem);
+}
+
+static int
+nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
+            u32 memtype, struct nouveau_mem **pmem)
+{
+       struct nouveau_mm *heap = &pfb->vram;
+       struct nouveau_mm *tags = &pfb->tags;
+       struct nouveau_mm_node *r;
+       struct nouveau_mem *mem;
+       int comp = (memtype & 0x300) >> 8;
+       int type = (memtype & 0x07f);
+       int back = (memtype & 0x800);
+       int min, max, ret;
+
+       max = (size >> 12);
+       min = ncmin ? (ncmin >> 12) : max;
+       align >>= 12;
+
+       mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+       if (!mem)
+               return -ENOMEM;
+
+       mutex_lock(&pfb->base.mutex);
+       if (comp) {
+               if (align == 16) {
+                       int n = (max >> 4) * comp;
+
+                       ret = nouveau_mm_head(tags, 1, n, n, 1, &mem->tag);
+                       if (ret)
+                               mem->tag = NULL;
+               }
+
+               if (unlikely(!mem->tag))
+                       comp = 0;
+       }
+
+       INIT_LIST_HEAD(&mem->regions);
+       mem->memtype = (comp << 7) | type;
+       mem->size = max;
+
+       type = nv50_fb_memtype[type];
+       do {
+               if (back)
+                       ret = nouveau_mm_tail(heap, type, max, min, align, &r);
+               else
+                       ret = nouveau_mm_head(heap, type, max, min, align, &r);
+               if (ret) {
+                       mutex_unlock(&pfb->base.mutex);
+                       pfb->ram->put(pfb, &mem);
+                       return ret;
+               }
+
+               list_add_tail(&r->rl_entry, &mem->regions);
+               max -= r->length;
+       } while (max);
+       mutex_unlock(&pfb->base.mutex);
+
+       r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);
+       mem->offset = (u64)r->offset << 12;
+       *pmem = mem;
+       return 0;
+}
+
+static u32
+nv50_fb_vram_rblock(struct nouveau_fb *pfb, struct nouveau_ram *ram)
+{
+       int i, parts, colbits, rowbitsa, rowbitsb, banks;
+       u64 rowsize, predicted;
+       u32 r0, r4, rt, ru, rblock_size;
+
+       r0 = nv_rd32(pfb, 0x100200);
+       r4 = nv_rd32(pfb, 0x100204);
+       rt = nv_rd32(pfb, 0x100250);
+       ru = nv_rd32(pfb, 0x001540);
+       nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru);
+
+       for (i = 0, parts = 0; i < 8; i++) {
+               if (ru & (0x00010000 << i))
+                       parts++;
+       }
+
+       colbits  =  (r4 & 0x0000f000) >> 12;
+       rowbitsa = ((r4 & 0x000f0000) >> 16) + 8;
+       rowbitsb = ((r4 & 0x00f00000) >> 20) + 8;
+       banks    = 1 << (((r4 & 0x03000000) >> 24) + 2);
+
+       rowsize = parts * banks * (1 << colbits) * 8;
+       predicted = rowsize << rowbitsa;
+       if (r0 & 0x00000004)
+               predicted += rowsize << rowbitsb;
+
+       if (predicted != ram->size) {
+               nv_warn(pfb, "memory controller reports %d MiB VRAM\n",
+                       (u32)(ram->size >> 20));
+       }
+
+       rblock_size = rowsize;
+       if (rt & 1)
+               rblock_size *= 3;
+
+       nv_debug(pfb, "rblock %d bytes\n", rblock_size);
+       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)
+{
+       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;
+       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);
+
+       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->type   = NV_MEM_TYPE_STOLEN;
+               ram->stolen = (u64)nv_rd32(pfb, 0x100e10) << 12;
+               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;
+       }
+
+       ram->get = nv50_ram_get;
+       ram->put = nv50_ram_put;
+       return 0;
+}
+
+struct nouveau_oclass
+nv50_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_ram_create,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
new file mode 100644 (file)
index 0000000..9c3634a
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * 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/ltcg.h>
+
+#include "priv.h"
+
+extern const u8 nvc0_pte_storage_type_map[256];
+
+void
+nvc0_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem)
+{
+       struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb);
+
+       if ((*pmem)->tag)
+               ltcg->tags_free(ltcg, &(*pmem)->tag);
+
+       nv50_ram_put(pfb, pmem);
+}
+
+int
+nvc0_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
+            u32 memtype, struct nouveau_mem **pmem)
+{
+       struct nouveau_mm *mm = &pfb->vram;
+       struct nouveau_mm_node *r;
+       struct nouveau_mem *mem;
+       int type = (memtype & 0x0ff);
+       int back = (memtype & 0x800);
+       const bool comp = nvc0_pte_storage_type_map[type] != type;
+       int ret;
+
+       size  >>= 12;
+       align >>= 12;
+       ncmin >>= 12;
+       if (!ncmin)
+               ncmin = size;
+
+       mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+       if (!mem)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&mem->regions);
+       mem->size = size;
+
+       mutex_lock(&pfb->base.mutex);
+       if (comp) {
+               struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb);
+
+               /* compression only works with lpages */
+               if (align == (1 << (17 - 12))) {
+                       int n = size >> 5;
+                       ltcg->tags_alloc(ltcg, n, &mem->tag);
+               }
+
+               if (unlikely(!mem->tag))
+                       type = nvc0_pte_storage_type_map[type];
+       }
+       mem->memtype = type;
+
+       do {
+               if (back)
+                       ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r);
+               else
+                       ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r);
+               if (ret) {
+                       mutex_unlock(&pfb->base.mutex);
+                       pfb->ram->put(pfb, &mem);
+                       return ret;
+               }
+
+               list_add_tail(&r->rl_entry, &mem->regions);
+               size -= r->length;
+       } while (size);
+       mutex_unlock(&pfb->base.mutex);
+
+       r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);
+       mem->offset = (u64)r->offset << 12;
+       *pmem = mem;
+       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)
+{
+       struct nouveau_fb *pfb = nouveau_fb(parent);
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       struct nouveau_ram *ram;
+       const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
+       const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+       u32 parts = nv_rd32(pfb, 0x022438);
+       u32 pmask = nv_rd32(pfb, 0x022554);
+       u32 bsize = nv_rd32(pfb, 0x10f20c);
+       u32 offset, length;
+       bool uniform = true;
+       int ret, part;
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+
+       nv_debug(pfb, "0x100800: 0x%08x\n", nv_rd32(pfb, 0x100800));
+       nv_debug(pfb, "parts 0x%08x mask 0x%08x\n", parts, pmask);
+
+       ram->type = nouveau_fb_bios_memtype(bios);
+       ram->ranks = (nv_rd32(pfb, 0x10f200) & 0x00000004) ? 2 : 1;
+
+       /* read amount of vram attached to each memory controller */
+       for (part = 0; part < parts; part++) {
+               if (!(pmask & (1 << part))) {
+                       u32 psize = nv_rd32(pfb, 0x11020c + (part * 0x1000));
+                       if (psize != bsize) {
+                               if (psize < bsize)
+                                       bsize = psize;
+                               uniform = false;
+                       }
+
+                       nv_debug(pfb, "%d: mem_amount 0x%08x\n", part, psize);
+                       ram->size += (u64)psize << 20;
+               }
+       }
+
+       /* if all controllers have the same amount attached, there's no holes */
+       if (uniform) {
+               offset = rsvd_head;
+               length = (ram->size >> 12) - rsvd_head - rsvd_tail;
+               ret = nouveau_mm_init(&pfb->vram, offset, length, 1);
+       } else {
+               /* otherwise, address lowest common amount from 0GiB */
+               ret = nouveau_mm_init(&pfb->vram, rsvd_head,
+                                     (bsize << 8) * parts, 1);
+               if (ret)
+                       return ret;
+
+               /* and the rest starting from (8GiB + common_size) */
+               offset = (0x0200000000ULL >> 12) + (bsize << 8);
+               length = (ram->size >> 12) - (bsize << 8) - rsvd_tail;
+
+               ret = nouveau_mm_init(&pfb->vram, offset, length, 0);
+               if (ret)
+                       nouveau_mm_fini(&pfb->vram);
+       }
+
+       if (ret)
+               return ret;
+
+       ram->get = nvc0_ram_get;
+       ram->put = nvc0_ram_put;
+       return 0;
+}
+
+struct nouveau_oclass
+nvc0_ram_oclass = {
+       .handle = 0,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_ram_create,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       }
+};
index cfc7e31..97bc5df 100644 (file)
@@ -56,7 +56,7 @@ nv50_instobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
-       ret = pfb->ram.get(pfb, size, align, 0, 0x800, &node->mem);
+       ret = pfb->ram->get(pfb, size, align, 0, 0x800, &node->mem);
        if (ret)
                return ret;
 
@@ -71,7 +71,7 @@ nv50_instobj_dtor(struct nouveau_object *object)
 {
        struct nv50_instobj_priv *node = (void *)object;
        struct nouveau_fb *pfb = nouveau_fb(object);
-       pfb->ram.put(pfb, &node->mem);
+       pfb->ram->put(pfb, &node->mem);
        nouveau_instobj_destroy(&node->base);
 }
 
index fb794e9..bcca883 100644 (file)
@@ -122,7 +122,7 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv)
                nv_wr32(priv, 0x17e000, priv->part_nr);
 
        /* tags for 1/4 of VRAM should be enough (8192/4 per GiB of VRAM) */
-       priv->num_tags = (pfb->ram.size >> 17) / 4;
+       priv->num_tags = (pfb->ram->size >> 17) / 4;
        if (priv->num_tags > (1 << 17))
                priv->num_tags = 1 << 17; /* we have 17 bits in PTE */
        priv->num_tags = (priv->num_tags + 63) & ~63; /* round up to 64 */
index d796924..0cb322a 100644 (file)
@@ -35,6 +35,7 @@ nv50_mc_intr[] = {
        { 0x00001000, NVDEV_ENGINE_GR },
        { 0x00004000, NVDEV_ENGINE_CRYPT },     /* NV84- */
        { 0x00008000, NVDEV_ENGINE_BSP },       /* NV84- */
+       { 0x00020000, NVDEV_ENGINE_VP },        /* NV84- */
        { 0x00100000, NVDEV_SUBDEV_TIMER },
        { 0x00200000, NVDEV_SUBDEV_GPIO },
        { 0x04000000, NVDEV_ENGINE_DISP },
index 737bd4b..c5da3ba 100644 (file)
@@ -33,6 +33,7 @@ nvc0_mc_intr[] = {
        { 0x00000001, NVDEV_ENGINE_PPP },
        { 0x00000020, NVDEV_ENGINE_COPY0 },
        { 0x00000040, NVDEV_ENGINE_COPY1 },
+       { 0x00000080, NVDEV_ENGINE_COPY2 },
        { 0x00000100, NVDEV_ENGINE_FIFO },
        { 0x00001000, NVDEV_ENGINE_GR },
        { 0x00008000, NVDEV_ENGINE_BSP },
index 77c67fc..67fcb6c 100644 (file)
@@ -236,9 +236,9 @@ nouveau_vm_unmap_pgt(struct nouveau_vm *vm, int big, u32 fpde, u32 lpde)
                        vmm->map_pgt(vpgd->obj, pde, vpgt->obj);
                }
 
-               mutex_unlock(&vm->mm.mutex);
+               mutex_unlock(&nv_subdev(vmm)->mutex);
                nouveau_gpuobj_ref(NULL, &pgt);
-               mutex_lock(&vm->mm.mutex);
+               mutex_lock(&nv_subdev(vmm)->mutex);
        }
 }
 
@@ -256,18 +256,18 @@ nouveau_vm_map_pgt(struct nouveau_vm *vm, u32 pde, u32 type)
        pgt_size  = (1 << (vmm->pgt_bits + 12)) >> type;
        pgt_size *= 8;
 
-       mutex_unlock(&vm->mm.mutex);
+       mutex_unlock(&nv_subdev(vmm)->mutex);
        ret = nouveau_gpuobj_new(nv_object(vm->vmm), NULL, pgt_size, 0x1000,
                                 NVOBJ_FLAG_ZERO_ALLOC, &pgt);
-       mutex_lock(&vm->mm.mutex);
+       mutex_lock(&nv_subdev(vmm)->mutex);
        if (unlikely(ret))
                return ret;
 
        /* someone beat us to filling the PDE while we didn't have the lock */
        if (unlikely(vpgt->refcount[big]++)) {
-               mutex_unlock(&vm->mm.mutex);
+               mutex_unlock(&nv_subdev(vmm)->mutex);
                nouveau_gpuobj_ref(NULL, &pgt);
-               mutex_lock(&vm->mm.mutex);
+               mutex_lock(&nv_subdev(vmm)->mutex);
                return 0;
        }
 
@@ -289,11 +289,11 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,
        u32 fpde, lpde, pde;
        int ret;
 
-       mutex_lock(&vm->mm.mutex);
+       mutex_lock(&nv_subdev(vmm)->mutex);
        ret = nouveau_mm_head(&vm->mm, page_shift, msize, msize, align,
                             &vma->node);
        if (unlikely(ret != 0)) {
-               mutex_unlock(&vm->mm.mutex);
+               mutex_unlock(&nv_subdev(vmm)->mutex);
                return ret;
        }
 
@@ -314,13 +314,14 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,
                        if (pde != fpde)
                                nouveau_vm_unmap_pgt(vm, big, fpde, pde - 1);
                        nouveau_mm_free(&vm->mm, &vma->node);
-                       mutex_unlock(&vm->mm.mutex);
+                       mutex_unlock(&nv_subdev(vmm)->mutex);
                        return ret;
                }
        }
-       mutex_unlock(&vm->mm.mutex);
+       mutex_unlock(&nv_subdev(vmm)->mutex);
 
-       vma->vm     = vm;
+       vma->vm = NULL;
+       nouveau_vm_ref(vm, &vma->vm, NULL);
        vma->offset = (u64)vma->node->offset << 12;
        vma->access = access;
        return 0;
@@ -338,10 +339,12 @@ nouveau_vm_put(struct nouveau_vma *vma)
        fpde = (vma->node->offset >> vmm->pgt_bits);
        lpde = (vma->node->offset + vma->node->length - 1) >> vmm->pgt_bits;
 
-       mutex_lock(&vm->mm.mutex);
+       mutex_lock(&nv_subdev(vmm)->mutex);
        nouveau_vm_unmap_pgt(vm, vma->node->type != vmm->spg_shift, fpde, lpde);
        nouveau_mm_free(&vm->mm, &vma->node);
-       mutex_unlock(&vm->mm.mutex);
+       mutex_unlock(&nv_subdev(vmm)->mutex);
+
+       nouveau_vm_ref(NULL, &vma->vm, NULL);
 }
 
 int
@@ -362,7 +365,7 @@ nouveau_vm_create(struct nouveau_vmmgr *vmm, u64 offset, u64 length,
        vm->fpde = offset >> (vmm->pgt_bits + 12);
        vm->lpde = (offset + length - 1) >> (vmm->pgt_bits + 12);
 
-       vm->pgt  = kcalloc(vm->lpde - vm->fpde + 1, sizeof(*vm->pgt), GFP_KERNEL);
+       vm->pgt  = vzalloc((vm->lpde - vm->fpde + 1) * sizeof(*vm->pgt));
        if (!vm->pgt) {
                kfree(vm);
                return -ENOMEM;
@@ -371,7 +374,7 @@ nouveau_vm_create(struct nouveau_vmmgr *vmm, u64 offset, u64 length,
        ret = nouveau_mm_init(&vm->mm, mm_offset >> 12, mm_length >> 12,
                              block >> 12);
        if (ret) {
-               kfree(vm->pgt);
+               vfree(vm->pgt);
                kfree(vm);
                return ret;
        }
@@ -405,24 +408,25 @@ nouveau_vm_link(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd)
 
        nouveau_gpuobj_ref(pgd, &vpgd->obj);
 
-       mutex_lock(&vm->mm.mutex);
+       mutex_lock(&nv_subdev(vmm)->mutex);
        for (i = vm->fpde; i <= vm->lpde; i++)
                vmm->map_pgt(pgd, i, vm->pgt[i - vm->fpde].obj);
        list_add(&vpgd->head, &vm->pgd_list);
-       mutex_unlock(&vm->mm.mutex);
+       mutex_unlock(&nv_subdev(vmm)->mutex);
        return 0;
 }
 
 static void
 nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd)
 {
+       struct nouveau_vmmgr *vmm = vm->vmm;
        struct nouveau_vm_pgd *vpgd, *tmp;
        struct nouveau_gpuobj *pgd = NULL;
 
        if (!mpgd)
                return;
 
-       mutex_lock(&vm->mm.mutex);
+       mutex_lock(&nv_subdev(vmm)->mutex);
        list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) {
                if (vpgd->obj == mpgd) {
                        pgd = vpgd->obj;
@@ -431,7 +435,7 @@ nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd)
                        break;
                }
        }
-       mutex_unlock(&vm->mm.mutex);
+       mutex_unlock(&nv_subdev(vmm)->mutex);
 
        nouveau_gpuobj_ref(NULL, &pgd);
 }
@@ -446,7 +450,7 @@ nouveau_vm_del(struct nouveau_vm *vm)
        }
 
        nouveau_mm_fini(&vm->mm);
-       kfree(vm->pgt);
+       vfree(vm->pgt);
        kfree(vm);
 }
 
index e067f81..07dd1fe 100644 (file)
 
 #include <subdev/timer.h>
 #include <subdev/fb.h>
+#include <subdev/bar.h>
 #include <subdev/vm.h>
 
 struct nv50_vmmgr_priv {
        struct nouveau_vmmgr base;
-       spinlock_t lock;
 };
 
 static void
@@ -86,8 +86,8 @@ nv50_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt,
 
        /* IGPs don't have real VRAM, re-target to stolen system memory */
        target = 0;
-       if (nouveau_fb(vma->vm->vmm)->ram.stolen) {
-               phys += nouveau_fb(vma->vm->vmm)->ram.stolen;
+       if (nouveau_fb(vma->vm->vmm)->ram->stolen) {
+               phys += nouveau_fb(vma->vm->vmm)->ram->stolen;
                target = 3;
        }
 
@@ -151,29 +151,42 @@ nv50_vm_unmap(struct nouveau_gpuobj *pgt, u32 pte, u32 cnt)
 static void
 nv50_vm_flush(struct nouveau_vm *vm)
 {
+       struct nv50_vmmgr_priv *priv = (void *)vm->vmm;
+       struct nouveau_bar *bar = nouveau_bar(priv);
        struct nouveau_engine *engine;
-       int i;
+       int i, vme;
+
+       bar->flush(bar);
 
+       mutex_lock(&nv_subdev(priv)->mutex);
        for (i = 0; i < NVDEV_SUBDEV_NR; i++) {
-               if (atomic_read(&vm->engref[i])) {
-                       engine = nouveau_engine(vm->vmm, i);
-                       if (engine && engine->tlb_flush)
-                               engine->tlb_flush(engine);
+               if (!atomic_read(&vm->engref[i]))
+                       continue;
+
+               /* unfortunate hw bug workaround... */
+               engine = nouveau_engine(priv, i);
+               if (engine && engine->tlb_flush) {
+                       engine->tlb_flush(engine);
+                       continue;
                }
-       }
-}
 
-void
-nv50_vm_flush_engine(struct nouveau_subdev *subdev, int engine)
-{
-       struct nv50_vmmgr_priv *priv = (void *)nouveau_vmmgr(subdev);
-       unsigned long flags;
-
-       spin_lock_irqsave(&priv->lock, flags);
-       nv_wr32(subdev, 0x100c80, (engine << 16) | 1);
-       if (!nv_wait(subdev, 0x100c80, 0x00000001, 0x00000000))
-               nv_error(subdev, "vm flush timeout: engine %d\n", engine);
-       spin_unlock_irqrestore(&priv->lock, flags);
+               switch (i) {
+               case NVDEV_ENGINE_GR   : vme = 0x00; break;
+               case NVDEV_ENGINE_VP   : vme = 0x01; break;
+               case NVDEV_SUBDEV_BAR  : vme = 0x06; break;
+               case NVDEV_ENGINE_MPEG : vme = 0x08; break;
+               case NVDEV_ENGINE_BSP  : vme = 0x09; break;
+               case NVDEV_ENGINE_CRYPT: vme = 0x0a; break;
+               case NVDEV_ENGINE_COPY0: vme = 0x0d; break;
+               default:
+                       continue;
+               }
+
+               nv_wr32(priv, 0x100c80, (vme << 16) | 1);
+               if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000))
+                       nv_error(priv, "vm flush timeout: engine %d\n", vme);
+       }
+       mutex_unlock(&nv_subdev(priv)->mutex);
 }
 
 static int
@@ -211,7 +224,6 @@ nv50_vmmgr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->base.map_sg = nv50_vm_map_sg;
        priv->base.unmap = nv50_vm_unmap;
        priv->base.flush = nv50_vm_flush;
-       spin_lock_init(&priv->lock);
        return 0;
 }
 
index 4c3b0a2..668cf96 100644 (file)
 #include <subdev/fb.h>
 #include <subdev/vm.h>
 #include <subdev/ltcg.h>
+#include <subdev/bar.h>
 
 struct nvc0_vmmgr_priv {
        struct nouveau_vmmgr base;
-       spinlock_t lock;
 };
 
 
@@ -160,40 +160,40 @@ nvc0_vm_unmap(struct nouveau_gpuobj *pgt, u32 pte, u32 cnt)
        }
 }
 
-void
-nvc0_vm_flush_engine(struct nouveau_subdev *subdev, u64 addr, int type)
-{
-       struct nvc0_vmmgr_priv *priv = (void *)nouveau_vmmgr(subdev);
-       unsigned long flags;
-
-       /* looks like maybe a "free flush slots" counter, the
-        * faster you write to 0x100cbc to more it decreases
-        */
-       spin_lock_irqsave(&priv->lock, flags);
-       if (!nv_wait_ne(subdev, 0x100c80, 0x00ff0000, 0x00000000)) {
-               nv_error(subdev, "vm timeout 0: 0x%08x %d\n",
-                        nv_rd32(subdev, 0x100c80), type);
-       }
-
-       nv_wr32(subdev, 0x100cb8, addr >> 8);
-       nv_wr32(subdev, 0x100cbc, 0x80000000 | type);
-
-       /* wait for flush to be queued? */
-       if (!nv_wait(subdev, 0x100c80, 0x00008000, 0x00008000)) {
-               nv_error(subdev, "vm timeout 1: 0x%08x %d\n",
-                        nv_rd32(subdev, 0x100c80), type);
-       }
-       spin_unlock_irqrestore(&priv->lock, flags);
-}
-
 static void
 nvc0_vm_flush(struct nouveau_vm *vm)
 {
+       struct nvc0_vmmgr_priv *priv = (void *)vm->vmm;
+       struct nouveau_bar *bar = nouveau_bar(priv);
        struct nouveau_vm_pgd *vpgd;
+       u32 type;
+
+       bar->flush(bar);
+
+       type = 0x00000001; /* PAGE_ALL */
+       if (atomic_read(&vm->engref[NVDEV_SUBDEV_BAR]))
+               type |= 0x00000004; /* HUB_ONLY */
 
+       mutex_lock(&nv_subdev(priv)->mutex);
        list_for_each_entry(vpgd, &vm->pgd_list, head) {
-               nvc0_vm_flush_engine(nv_subdev(vm->vmm), vpgd->obj->addr, 1);
+               /* looks like maybe a "free flush slots" counter, the
+                * faster you write to 0x100cbc to more it decreases
+                */
+               if (!nv_wait_ne(priv, 0x100c80, 0x00ff0000, 0x00000000)) {
+                       nv_error(priv, "vm timeout 0: 0x%08x %d\n",
+                                nv_rd32(priv, 0x100c80), type);
+               }
+
+               nv_wr32(priv, 0x100cb8, vpgd->obj->addr >> 8);
+               nv_wr32(priv, 0x100cbc, 0x80000000 | type);
+
+               /* wait for flush to be queued? */
+               if (!nv_wait(priv, 0x100c80, 0x00008000, 0x00008000)) {
+                       nv_error(priv, "vm timeout 1: 0x%08x %d\n",
+                                nv_rd32(priv, 0x100c80), type);
+               }
        }
+       mutex_unlock(&nv_subdev(priv)->mutex);
 }
 
 static int
@@ -227,7 +227,6 @@ nvc0_vmmgr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->base.map_sg = nvc0_vm_map_sg;
        priv->base.unmap = nvc0_vm_unmap;
        priv->base.flush = nvc0_vm_flush;
-       spin_lock_init(&priv->lock);
        return 0;
 }
 
index 1c4c6c9..8f467e7 100644 (file)
@@ -129,6 +129,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);
        }
 
index 6aa2137..3e72876 100644 (file)
@@ -1878,9 +1878,6 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
        if (dcb->version < 0x21)
                merge_like_dcb_entries(dev, dcb);
 
-       if (!dcb->entries)
-               return -ENXIO;
-
        /* dump connector table entries to log, if any exist */
        idx = -1;
        while ((conn = olddcb_conn(dev, ++idx))) {
@@ -2054,19 +2051,14 @@ nouveau_bios_posted(struct drm_device *dev)
        struct nouveau_drm *drm = nouveau_drm(dev);
        unsigned htotal;
 
-       if (nv_device(drm->device)->card_type >= NV_50) {
-               if (NVReadVgaCrtc(dev, 0, 0x00) == 0 &&
-                   NVReadVgaCrtc(dev, 0, 0x1a) == 0)
-                       return false;
+       if (nv_device(drm->device)->card_type >= NV_50)
                return true;
-       }
 
        htotal  = NVReadVgaCrtc(dev, 0, 0x06);
        htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x01) << 8;
        htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x20) << 4;
        htotal |= (NVReadVgaCrtc(dev, 0, 0x25) & 0x01) << 10;
        htotal |= (NVReadVgaCrtc(dev, 0, 0x41) & 0x01) << 11;
-
        return (htotal != 0);
 }
 
index 7ff1071..4b1afb1 100644 (file)
@@ -255,7 +255,7 @@ set_placement_range(struct nouveau_bo *nvbo, uint32_t type)
 {
        struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
        struct nouveau_fb *pfb = nouveau_fb(drm->device);
-       u32 vram_pages = pfb->ram.size >> PAGE_SHIFT;
+       u32 vram_pages = pfb->ram->size >> PAGE_SHIFT;
 
        if (nv_device(drm->device)->card_type == NV_10 &&
            nvbo->tile_mode && (type & TTM_PL_FLAG_VRAM) &&
@@ -968,7 +968,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->channel;
+       struct nouveau_channel *chan = chan = drm->ttm.chan;
        struct nouveau_bo *nvbo = nouveau_bo(bo);
        struct ttm_mem_reg *old_mem = &bo->mem;
        int ret;
@@ -1052,6 +1052,7 @@ nouveau_bo_move_init(struct nouveau_drm *drm)
                        }
 
                        drm->ttm.move = mthd->exec;
+                       drm->ttm.chan = chan;
                        name = mthd->name;
                        break;
                }
@@ -1550,13 +1551,8 @@ void
 nouveau_bo_vma_del(struct nouveau_bo *nvbo, struct nouveau_vma *vma)
 {
        if (vma->node) {
-               if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM) {
-                       spin_lock(&nvbo->bo.bdev->fence_lock);
-                       ttm_bo_wait(&nvbo->bo, false, false, false);
-                       spin_unlock(&nvbo->bo.bdev->fence_lock);
+               if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM)
                        nouveau_vm_unmap(vma);
-               }
-
                nouveau_vm_put(vma);
                list_del(&vma->head);
        }
index eaa80a2..e84f4c3 100644 (file)
@@ -147,7 +147,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nouveau_cli *cli,
                args.limit = client->vm->vmm->limit - 1;
        } else
        if (chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM) {
-               u64 limit = pfb->ram.size - imem->reserved - 1;
+               u64 limit = pfb->ram->size - imem->reserved - 1;
                if (device->card_type == NV_04) {
                        /* nv04 vram pushbuf hack, retarget to its location in
                         * the framebuffer bar rather than direct vram access..
@@ -282,7 +282,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart)
                } else {
                        args.flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR;
                        args.start = 0;
-                       args.limit = pfb->ram.size - imem->reserved - 1;
+                       args.limit = pfb->ram->size - imem->reserved - 1;
                }
 
                ret = nouveau_object_new(nv_object(client), chan->handle, vram,
index f17dc2a..708b2d1 100644 (file)
@@ -26,6 +26,7 @@
 
 #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"
@@ -332,10 +333,15 @@ nouveau_display_create(struct drm_device *dev)
 
        if (nouveau_modeset == 1 ||
            (nouveau_modeset < 0 && pclass == PCI_CLASS_DISPLAY_VGA)) {
-               if (nv_device(drm->device)->card_type < NV_50)
-                       ret = nv04_display_create(dev);
-               else
-                       ret = nv50_display_create(dev);
+               if (drm->vbios.dcb.entries) {
+                       if (nv_device(drm->device)->card_type < NV_50)
+                               ret = nv04_display_create(dev);
+                       else
+                               ret = nv50_display_create(dev);
+               } else {
+                       ret = 0;
+               }
+
                if (ret)
                        goto disp_create_err;
 
@@ -456,51 +462,6 @@ nouveau_display_resume(struct drm_device *dev)
        }
 }
 
-static int
-nouveau_page_flip_reserve(struct nouveau_bo *old_bo,
-                         struct nouveau_bo *new_bo)
-{
-       int ret;
-
-       ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM);
-       if (ret)
-               return ret;
-
-       ret = ttm_bo_reserve(&new_bo->bo, false, false, false, 0);
-       if (ret)
-               goto fail;
-
-       if (likely(old_bo != new_bo)) {
-               ret = ttm_bo_reserve(&old_bo->bo, false, false, false, 0);
-               if (ret)
-                       goto fail_unreserve;
-       }
-
-       return 0;
-
-fail_unreserve:
-       ttm_bo_unreserve(&new_bo->bo);
-fail:
-       nouveau_bo_unpin(new_bo);
-       return ret;
-}
-
-static void
-nouveau_page_flip_unreserve(struct nouveau_bo *old_bo,
-                           struct nouveau_bo *new_bo,
-                           struct nouveau_fence *fence)
-{
-       nouveau_bo_fence(new_bo, fence);
-       ttm_bo_unreserve(&new_bo->bo);
-
-       if (likely(old_bo != new_bo)) {
-               nouveau_bo_fence(old_bo, fence);
-               ttm_bo_unreserve(&old_bo->bo);
-       }
-
-       nouveau_bo_unpin(old_bo);
-}
-
 static int
 nouveau_page_flip_emit(struct nouveau_channel *chan,
                       struct nouveau_bo *old_bo,
@@ -563,6 +524,9 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        struct nouveau_page_flip_state *s;
        struct nouveau_channel *chan = NULL;
        struct nouveau_fence *fence;
+       struct list_head res;
+       struct ttm_validate_buffer res_val[2];
+       struct ww_acquire_ctx ticket;
        int ret;
 
        if (!drm->channel)
@@ -572,25 +536,43 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        if (!s)
                return -ENOMEM;
 
-       /* Don't let the buffers go away while we flip */
-       ret = nouveau_page_flip_reserve(old_bo, new_bo);
-       if (ret)
-               goto fail_free;
-
-       /* Initialize a page flip struct */
-       *s = (struct nouveau_page_flip_state)
-               { { }, event, nouveau_crtc(crtc)->index,
-                 fb->bits_per_pixel, fb->pitches[0], crtc->x, crtc->y,
-                 new_bo->bo.offset };
-
        /* 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);
+
        mutex_lock(&chan->cli->mutex);
 
+       if (new_bo != old_bo) {
+               ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM);
+               if (likely(!ret)) {
+                       res_val[0].bo = &old_bo->bo;
+                       res_val[1].bo = &new_bo->bo;
+                       INIT_LIST_HEAD(&res);
+                       list_add_tail(&res_val[0].head, &res);
+                       list_add_tail(&res_val[1].head, &res);
+                       ret = ttm_eu_reserve_buffers(&ticket, &res);
+                       if (ret)
+                               nouveau_bo_unpin(new_bo);
+               }
+       } else
+               ret = ttm_bo_reserve(&new_bo->bo, false, false, false, 0);
+
+       if (ret) {
+               mutex_unlock(&chan->cli->mutex);
+               goto fail_free;
+       }
+
+       /* Initialize a page flip struct */
+       *s = (struct nouveau_page_flip_state)
+               { { }, event, nouveau_crtc(crtc)->index,
+                 fb->bits_per_pixel, fb->pitches[0], crtc->x, crtc->y,
+                 new_bo->bo.offset };
+
        /* Emit a page flip */
        if (nv_device(drm->device)->card_type >= NV_50) {
                ret = nv50_display_flip_next(crtc, fb, chan, 0);
@@ -608,12 +590,22 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        /* Update the crtc struct and cleanup */
        crtc->fb = fb;
 
-       nouveau_page_flip_unreserve(old_bo, new_bo, fence);
+       if (old_bo != new_bo) {
+               ttm_eu_fence_buffer_objects(&ticket, &res, fence);
+               nouveau_bo_unpin(old_bo);
+       } else {
+               nouveau_bo_fence(new_bo, fence);
+               ttm_bo_unreserve(&new_bo->bo);
+       }
        nouveau_fence_unref(&fence);
        return 0;
 
 fail_unreserve:
-       nouveau_page_flip_unreserve(old_bo, new_bo, NULL);
+       if (old_bo != new_bo) {
+               ttm_eu_backoff_reservation(&ticket, &res);
+               nouveau_bo_unpin(new_bo);
+       } else
+               ttm_bo_unreserve(&new_bo->bo);
 fail_free:
        kfree(s);
        return ret;
index 383f4e6..218a4b5 100644 (file)
@@ -702,6 +702,7 @@ driver = {
        .gem_prime_export = drm_gem_prime_export,
        .gem_prime_import = drm_gem_prime_import,
        .gem_prime_pin = nouveau_gem_prime_pin,
+       .gem_prime_unpin = nouveau_gem_prime_unpin,
        .gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table,
        .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table,
        .gem_prime_vmap = nouveau_gem_prime_vmap,
index f2b30f8..41ff7e0 100644 (file)
@@ -96,6 +96,7 @@ struct nouveau_drm {
                int (*move)(struct nouveau_channel *,
                            struct ttm_buffer_object *,
                            struct ttm_mem_reg *, struct ttm_mem_reg *);
+               struct nouveau_channel *chan;
                int mtrr;
        } ttm;
 
index b035317..9352010 100644 (file)
@@ -289,16 +289,13 @@ nouveau_fbcon_create(struct drm_fb_helper *helper,
        ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM);
        if (ret) {
                NV_ERROR(drm, "failed to pin fb: %d\n", ret);
-               nouveau_bo_ref(NULL, &nvbo);
-               goto out;
+               goto out_unref;
        }
 
        ret = nouveau_bo_map(nvbo);
        if (ret) {
                NV_ERROR(drm, "failed to map fb: %d\n", ret);
-               nouveau_bo_unpin(nvbo);
-               nouveau_bo_ref(NULL, &nvbo);
-               goto out;
+               goto out_unpin;
        }
 
        chan = nouveau_nofbaccel ? NULL : drm->channel;
@@ -316,13 +313,14 @@ nouveau_fbcon_create(struct drm_fb_helper *helper,
        info = framebuffer_alloc(0, &pdev->dev);
        if (!info) {
                ret = -ENOMEM;
-               goto out_unref;
+               goto out_unlock;
        }
 
        ret = fb_alloc_cmap(&info->cmap, 256, 0);
        if (ret) {
                ret = -ENOMEM;
-               goto out_unref;
+               framebuffer_release(info);
+               goto out_unlock;
        }
 
        info->par = fbcon;
@@ -337,7 +335,7 @@ nouveau_fbcon_create(struct drm_fb_helper *helper,
        fbcon->helper.fbdev = info;
 
        strcpy(info->fix.id, "nouveaufb");
-       if (nouveau_nofbaccel)
+       if (!chan)
                info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_DISABLED;
        else
                info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
@@ -383,8 +381,14 @@ nouveau_fbcon_create(struct drm_fb_helper *helper,
        vga_switcheroo_client_fb_set(dev->pdev, info);
        return 0;
 
-out_unref:
+out_unlock:
        mutex_unlock(&dev->struct_mutex);
+       if (chan)
+               nouveau_bo_vma_del(nvbo, &fbcon->nouveau_fb.vma);
+out_unpin:
+       nouveau_bo_unpin(nvbo);
+out_unref:
+       nouveau_bo_ref(NULL, &nvbo);
 out:
        return ret;
 }
@@ -413,6 +417,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon)
        if (nouveau_fb->nvbo) {
                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);
                nouveau_fb->nvbo = NULL;
        }
@@ -467,10 +472,10 @@ nouveau_fbcon_init(struct drm_device *dev)
 
        drm_fb_helper_single_add_all_connectors(&fbcon->helper);
 
-       if (pfb->ram.size <= 32 * 1024 * 1024)
+       if (pfb->ram->size <= 32 * 1024 * 1024)
                preferred_bpp = 8;
        else
-       if (pfb->ram.size <= 64 * 1024 * 1024)
+       if (pfb->ram->size <= 64 * 1024 * 1024)
                preferred_bpp = 16;
        else
                preferred_bpp = 32;
index 6c94683..1680d91 100644 (file)
 
 #include <engine/fifo.h>
 
+struct fence_work {
+       struct work_struct base;
+       struct list_head head;
+       void (*func)(void *);
+       void *data;
+};
+
+static void
+nouveau_fence_signal(struct nouveau_fence *fence)
+{
+       struct fence_work *work, *temp;
+
+       list_for_each_entry_safe(work, temp, &fence->work, head) {
+               schedule_work(&work->base);
+               list_del(&work->head);
+       }
+
+       fence->channel = NULL;
+       list_del(&fence->head);
+}
+
 void
 nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
 {
        struct nouveau_fence *fence, *fnext;
        spin_lock(&fctx->lock);
        list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
-               fence->channel = NULL;
-               list_del(&fence->head);
-               nouveau_fence_unref(&fence);
+               nouveau_fence_signal(fence);
        }
        spin_unlock(&fctx->lock);
 }
@@ -56,6 +75,50 @@ nouveau_fence_context_new(struct nouveau_fence_chan *fctx)
        spin_lock_init(&fctx->lock);
 }
 
+static void
+nouveau_fence_work_handler(struct work_struct *kwork)
+{
+       struct fence_work *work = container_of(kwork, typeof(*work), base);
+       work->func(work->data);
+       kfree(work);
+}
+
+void
+nouveau_fence_work(struct nouveau_fence *fence,
+                  void (*func)(void *), void *data)
+{
+       struct nouveau_channel *chan = fence->channel;
+       struct nouveau_fence_chan *fctx;
+       struct fence_work *work = NULL;
+
+       if (nouveau_fence_done(fence)) {
+               func(data);
+               return;
+       }
+
+       fctx = chan->fence;
+       work = kmalloc(sizeof(*work), GFP_KERNEL);
+       if (!work) {
+               WARN_ON(nouveau_fence_wait(fence, false, false));
+               func(data);
+               return;
+       }
+
+       spin_lock(&fctx->lock);
+       if (!fence->channel) {
+               spin_unlock(&fctx->lock);
+               kfree(work);
+               func(data);
+               return;
+       }
+
+       INIT_WORK(&work->base, nouveau_fence_work_handler);
+       work->func = func;
+       work->data = data;
+       list_add(&work->head, &fence->work);
+       spin_unlock(&fctx->lock);
+}
+
 static void
 nouveau_fence_update(struct nouveau_channel *chan)
 {
@@ -67,8 +130,7 @@ nouveau_fence_update(struct nouveau_channel *chan)
                if (fctx->read(chan) < fence->sequence)
                        break;
 
-               fence->channel = NULL;
-               list_del(&fence->head);
+               nouveau_fence_signal(fence);
                nouveau_fence_unref(&fence);
        }
        spin_unlock(&fctx->lock);
@@ -265,6 +327,7 @@ nouveau_fence_new(struct nouveau_channel *chan, bool sysmem,
        if (!fence)
                return -ENOMEM;
 
+       INIT_LIST_HEAD(&fence->work);
        fence->sysmem = sysmem;
        kref_init(&fence->kref);
 
index c899434..c57bb61 100644 (file)
@@ -5,6 +5,7 @@ struct nouveau_drm;
 
 struct nouveau_fence {
        struct list_head head;
+       struct list_head work;
        struct kref kref;
 
        bool sysmem;
@@ -22,6 +23,7 @@ void nouveau_fence_unref(struct nouveau_fence **);
 
 int  nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *);
 bool nouveau_fence_done(struct nouveau_fence *);
+void nouveau_fence_work(struct nouveau_fence *, void (*)(void *), void *);
 int  nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr);
 int  nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *);
 
index b4b4d0c..e72d09c 100644 (file)
@@ -50,7 +50,8 @@ nouveau_gem_object_del(struct drm_gem_object *gem)
                return;
        nvbo->gem = NULL;
 
-       if (unlikely(nvbo->pin_refcnt)) {
+       /* Lockdep hates you for doing reserve with gem object lock held */
+       if (WARN_ON_ONCE(nvbo->pin_refcnt)) {
                nvbo->pin_refcnt = 1;
                nouveau_bo_unpin(nvbo);
        }
@@ -101,6 +102,41 @@ out:
        return ret;
 }
 
+static void
+nouveau_gem_object_delete(void *data)
+{
+       struct nouveau_vma *vma = data;
+       nouveau_vm_unmap(vma);
+       nouveau_vm_put(vma);
+       kfree(vma);
+}
+
+static void
+nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nouveau_vma *vma)
+{
+       const bool mapped = nvbo->bo.mem.mem_type != TTM_PL_SYSTEM;
+       struct nouveau_fence *fence = NULL;
+
+       list_del(&vma->head);
+
+       if (mapped) {
+               spin_lock(&nvbo->bo.bdev->fence_lock);
+               if (nvbo->bo.sync_obj)
+                       fence = nouveau_fence_ref(nvbo->bo.sync_obj);
+               spin_unlock(&nvbo->bo.bdev->fence_lock);
+       }
+
+       if (fence) {
+               nouveau_fence_work(fence, nouveau_gem_object_delete, vma);
+       } else {
+               if (mapped)
+                       nouveau_vm_unmap(vma);
+               nouveau_vm_put(vma);
+               kfree(vma);
+       }
+       nouveau_fence_unref(&fence);
+}
+
 void
 nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
 {
@@ -118,10 +154,8 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
 
        vma = nouveau_bo_vma_find(nvbo, cli->base.vm);
        if (vma) {
-               if (--vma->refcount == 0) {
-                       nouveau_bo_vma_del(nvbo, vma);
-                       kfree(vma);
-               }
+               if (--vma->refcount == 0)
+                       nouveau_gem_object_unmap(nvbo, vma);
        }
        ttm_bo_unreserve(&nvbo->bo);
 }
@@ -276,10 +310,12 @@ struct validate_op {
        struct list_head vram_list;
        struct list_head gart_list;
        struct list_head both_list;
+       struct ww_acquire_ctx ticket;
 };
 
 static void
-validate_fini_list(struct list_head *list, struct nouveau_fence *fence)
+validate_fini_list(struct list_head *list, struct nouveau_fence *fence,
+                  struct ww_acquire_ctx *ticket)
 {
        struct list_head *entry, *tmp;
        struct nouveau_bo *nvbo;
@@ -296,17 +332,24 @@ validate_fini_list(struct list_head *list, struct nouveau_fence *fence)
 
                list_del(&nvbo->entry);
                nvbo->reserved_by = NULL;
-               ttm_bo_unreserve(&nvbo->bo);
+               ttm_bo_unreserve_ticket(&nvbo->bo, ticket);
                drm_gem_object_unreference_unlocked(nvbo->gem);
        }
 }
 
 static void
-validate_fini(struct validate_op *op, struct nouveau_fence* fence)
+validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence)
 {
-       validate_fini_list(&op->vram_list, fence);
-       validate_fini_list(&op->gart_list, fence);
-       validate_fini_list(&op->both_list, fence);
+       validate_fini_list(&op->vram_list, fence, &op->ticket);
+       validate_fini_list(&op->gart_list, fence, &op->ticket);
+       validate_fini_list(&op->both_list, fence, &op->ticket);
+}
+
+static void
+validate_fini(struct validate_op *op, struct nouveau_fence *fence)
+{
+       validate_fini_no_ticket(op, fence);
+       ww_acquire_fini(&op->ticket);
 }
 
 static int
@@ -316,13 +359,11 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
 {
        struct nouveau_cli *cli = nouveau_cli(file_priv);
        struct drm_device *dev = chan->drm->dev;
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       uint32_t sequence;
        int trycnt = 0;
        int ret, i;
        struct nouveau_bo *res_bo = NULL;
 
-       sequence = atomic_add_return(1, &drm->ttm.validate_sequence);
+       ww_acquire_init(&op->ticket, &reservation_ww_class);
 retry:
        if (++trycnt > 100000) {
                NV_ERROR(cli, "%s failed and gave up.\n", __func__);
@@ -337,6 +378,7 @@ retry:
                gem = drm_gem_object_lookup(dev, file_priv, b->handle);
                if (!gem) {
                        NV_ERROR(cli, "Unknown handle 0x%08x\n", b->handle);
+                       ww_acquire_done(&op->ticket);
                        validate_fini(op, NULL);
                        return -ENOENT;
                }
@@ -351,21 +393,23 @@ retry:
                        NV_ERROR(cli, "multiple instances of buffer %d on "
                                      "validation list\n", b->handle);
                        drm_gem_object_unreference_unlocked(gem);
+                       ww_acquire_done(&op->ticket);
                        validate_fini(op, NULL);
                        return -EINVAL;
                }
 
-               ret = ttm_bo_reserve(&nvbo->bo, true, false, true, sequence);
+               ret = ttm_bo_reserve(&nvbo->bo, true, false, true, &op->ticket);
                if (ret) {
-                       validate_fini(op, NULL);
-                       if (unlikely(ret == -EAGAIN)) {
-                               sequence = atomic_add_return(1, &drm->ttm.validate_sequence);
+                       validate_fini_no_ticket(op, NULL);
+                       if (unlikely(ret == -EDEADLK)) {
                                ret = ttm_bo_reserve_slowpath(&nvbo->bo, true,
-                                                             sequence);
+                                                             &op->ticket);
                                if (!ret)
                                        res_bo = nvbo;
                        }
                        if (unlikely(ret)) {
+                               ww_acquire_done(&op->ticket);
+                               ww_acquire_fini(&op->ticket);
                                drm_gem_object_unreference_unlocked(gem);
                                if (ret != -ERESTARTSYS)
                                        NV_ERROR(cli, "fail reserve\n");
@@ -389,6 +433,7 @@ retry:
                        NV_ERROR(cli, "invalid valid domains: 0x%08x\n",
                                 b->valid_domains);
                        list_add_tail(&nvbo->entry, &op->both_list);
+                       ww_acquire_done(&op->ticket);
                        validate_fini(op, NULL);
                        return -EINVAL;
                }
@@ -396,6 +441,7 @@ retry:
                        goto retry;
        }
 
+       ww_acquire_done(&op->ticket);
        return 0;
 }
 
index 8d7a3f0..502e429 100644 (file)
@@ -36,6 +36,7 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *,
                                  struct drm_file *);
 
 extern int nouveau_gem_prime_pin(struct drm_gem_object *);
+extern void nouveau_gem_prime_unpin(struct drm_gem_object *);
 extern struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *);
 extern struct drm_gem_object *nouveau_gem_prime_import_sg_table(
        struct drm_device *, size_t size, struct sg_table *);
index 7e0ff10..4f6a572 100644 (file)
@@ -125,7 +125,7 @@ nv50_mem_timing_calc(struct drm_device *dev, u32 freq,
                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) {
+               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);
@@ -428,7 +428,7 @@ nouveau_mem_timing_calc(struct drm_device *dev, u32 freq,
                break;
        }
 
-       switch (pfb->ram.type * !ret) {
+       switch (pfb->ram->type * !ret) {
        case NV_MEM_TYPE_GDDR3:
                ret = nouveau_mem_gddr3_mr(dev, freq, e, len, boot, t);
                break;
@@ -455,7 +455,7 @@ nouveau_mem_timing_calc(struct drm_device *dev, u32 freq,
                else
                        dll_off = !!(ramcfg[2] & 0x40);
 
-               switch (pfb->ram.type) {
+               switch (pfb->ram->type) {
                case NV_MEM_TYPE_GDDR3:
                        t->mr[1] &= ~0x00000040;
                        t->mr[1] |=  0x00000040 * dll_off;
@@ -522,7 +522,7 @@ nouveau_mem_timing_read(struct drm_device *dev, struct nouveau_pm_memtiming *t)
        t->odt = 0;
        t->drive_strength = 0;
 
-       switch (pfb->ram.type) {
+       switch (pfb->ram->type) {
        case NV_MEM_TYPE_DDR3:
                t->odt |= (t->mr[1] & 0x200) >> 7;
        case NV_MEM_TYPE_DDR2:
@@ -551,7 +551,7 @@ nouveau_mem_exec(struct nouveau_mem_exec_func *exec,
        u32 mr[3] = { info->mr[0], info->mr[1], info->mr[2] };
        u32 mr1_dlloff;
 
-       switch (pfb->ram.type) {
+       switch (pfb->ram->type) {
        case NV_MEM_TYPE_DDR2:
                tDLLK = 2000;
                mr1_dlloff = 0x00000001;
@@ -572,7 +572,7 @@ nouveau_mem_exec(struct nouveau_mem_exec_func *exec,
        }
 
        /* fetch current MRs */
-       switch (pfb->ram.type) {
+       switch (pfb->ram->type) {
        case NV_MEM_TYPE_GDDR3:
        case NV_MEM_TYPE_DDR3:
                mr[2] = exec->mrg(exec, 2);
@@ -639,7 +639,7 @@ nouveau_mem_exec(struct nouveau_mem_exec_func *exec,
                exec->mrs (exec, 0, info->mr[0] | 0x00000000);
                exec->wait(exec, tMRD);
                exec->wait(exec, tDLLK);
-               if (pfb->ram.type == NV_MEM_TYPE_GDDR3)
+               if (pfb->ram->type == NV_MEM_TYPE_GDDR3)
                        exec->precharge(exec);
        }
 
index f53e108..e90468d 100644 (file)
@@ -84,7 +84,7 @@ struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev,
 int nouveau_gem_prime_pin(struct drm_gem_object *obj)
 {
        struct nouveau_bo *nvbo = nouveau_gem_object(obj);
-       int ret = 0;
+       int ret;
 
        /* pin buffer into GTT */
        ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_TT);
@@ -93,3 +93,10 @@ int nouveau_gem_prime_pin(struct drm_gem_object *obj)
 
        return 0;
 }
+
+void nouveau_gem_prime_unpin(struct drm_gem_object *obj)
+{
+       struct nouveau_bo *nvbo = nouveau_gem_object(obj);
+
+       nouveau_bo_unpin(nvbo);
+}
index f19a15a..19e3757 100644 (file)
@@ -69,7 +69,7 @@ nouveau_vram_manager_del(struct ttm_mem_type_manager *man,
        struct nouveau_drm *drm = nouveau_bdev(man->bdev);
        struct nouveau_fb *pfb = nouveau_fb(drm->device);
        nouveau_mem_node_cleanup(mem->mm_node);
-       pfb->ram.put(pfb, (struct nouveau_mem **)&mem->mm_node);
+       pfb->ram->put(pfb, (struct nouveau_mem **)&mem->mm_node);
 }
 
 static int
@@ -88,7 +88,7 @@ nouveau_vram_manager_new(struct ttm_mem_type_manager *man,
        if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG)
                size_nc = 1 << nvbo->page_shift;
 
-       ret = pfb->ram.get(pfb, mem->num_pages << PAGE_SHIFT,
+       ret = pfb->ram->get(pfb, mem->num_pages << PAGE_SHIFT,
                           mem->page_alignment << PAGE_SHIFT, size_nc,
                           (nvbo->tile_flags >> 8) & 0x3ff, &node);
        if (ret) {
@@ -111,7 +111,7 @@ nouveau_vram_manager_debug(struct ttm_mem_type_manager *man, const char *prefix)
        struct nouveau_mm_node *r;
        u32 total = 0, free = 0;
 
-       mutex_lock(&mm->mutex);
+       mutex_lock(&nv_subdev(pfb)->mutex);
        list_for_each_entry(r, &mm->nodes, nl_entry) {
                printk(KERN_DEBUG "%s %d: 0x%010llx 0x%010llx\n",
                       prefix, r->type, ((u64)r->offset << 12),
@@ -121,7 +121,7 @@ nouveau_vram_manager_debug(struct ttm_mem_type_manager *man, const char *prefix)
                if (!r->type)
                        free += r->length;
        }
-       mutex_unlock(&mm->mutex);
+       mutex_unlock(&nv_subdev(pfb)->mutex);
 
        printk(KERN_DEBUG "%s  total: 0x%010llx free: 0x%010llx\n",
               prefix, (u64)total << 12, (u64)free << 12);
@@ -168,9 +168,6 @@ nouveau_gart_manager_new(struct ttm_mem_type_manager *man,
        struct nouveau_bo *nvbo = nouveau_bo(bo);
        struct nouveau_mem *node;
 
-       if (unlikely((mem->num_pages << PAGE_SHIFT) >= 512 * 1024 * 1024))
-               return -ENOMEM;
-
        node = kzalloc(sizeof(*node), GFP_KERNEL);
        if (!node)
                return -ENOMEM;
@@ -386,7 +383,7 @@ nouveau_ttm_init(struct nouveau_drm *drm)
        }
 
        /* VRAM init */
-       drm->gem.vram_available  = nouveau_fb(drm->device)->ram.size;
+       drm->gem.vram_available  = nouveau_fb(drm->device)->ram->size;
        drm->gem.vram_available -= nouveau_instmem(drm->device)->reserved;
 
        ret = ttm_bo_init_mm(&drm->ttm.bdev, TTM_PL_VRAM,
@@ -396,15 +393,12 @@ nouveau_ttm_init(struct nouveau_drm *drm)
                return ret;
        }
 
-       drm->ttm.mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 1),
-                                    pci_resource_len(dev->pdev, 1),
-                                    DRM_MTRR_WC);
+       drm->ttm.mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 1),
+                                        pci_resource_len(dev->pdev, 1));
 
        /* GART init */
        if (drm->agp.stat != ENABLED) {
                drm->gem.gart_available = nouveau_vmmgr(drm->device)->limit;
-               if (drm->gem.gart_available > 512 * 1024 * 1024)
-                       drm->gem.gart_available = 512 * 1024 * 1024;
        } else {
                drm->gem.gart_available = drm->agp.size;
        }
@@ -433,10 +427,6 @@ nouveau_ttm_fini(struct nouveau_drm *drm)
 
        nouveau_ttm_global_release(drm);
 
-       if (drm->ttm.mtrr >= 0) {
-               drm_mtrr_del(drm->ttm.mtrr,
-                            pci_resource_start(drm->dev->pdev, 1),
-                            pci_resource_len(drm->dev->pdev, 1), DRM_MTRR_WC);
-               drm->ttm.mtrr = -1;
-       }
+       arch_phys_wc_del(drm->ttm.mtrr);
+       drm->ttm.mtrr = 0;
 }
index dd5e01f..54dc635 100644 (file)
@@ -159,7 +159,7 @@ nv50_dmac_create_fbdma(struct nouveau_object *core, u32 parent)
                                        .flags = NV_DMA_TARGET_VRAM |
                                                 NV_DMA_ACCESS_RDWR,
                                        .start = 0,
-                                       .limit = pfb->ram.size - 1,
+                                       .limit = pfb->ram->size - 1,
                                        .conf0 = NV50_DMA_CONF0_ENABLE |
                                                 NV50_DMA_CONF0_PART_256,
                                     }, sizeof(struct nv_dma_class), &object);
@@ -172,7 +172,7 @@ nv50_dmac_create_fbdma(struct nouveau_object *core, u32 parent)
                                        .flags = NV_DMA_TARGET_VRAM |
                                                 NV_DMA_ACCESS_RDWR,
                                        .start = 0,
-                                       .limit = pfb->ram.size - 1,
+                                       .limit = pfb->ram->size - 1,
                                        .conf0 = NV50_DMA_CONF0_ENABLE | 0x70 |
                                                 NV50_DMA_CONF0_PART_256,
                                 }, sizeof(struct nv_dma_class), &object);
@@ -185,7 +185,7 @@ nv50_dmac_create_fbdma(struct nouveau_object *core, u32 parent)
                                        .flags = NV_DMA_TARGET_VRAM |
                                                 NV_DMA_ACCESS_RDWR,
                                        .start = 0,
-                                       .limit = pfb->ram.size - 1,
+                                       .limit = pfb->ram->size - 1,
                                        .conf0 = NV50_DMA_CONF0_ENABLE | 0x7a |
                                                 NV50_DMA_CONF0_PART_256,
                                 }, sizeof(struct nv_dma_class), &object);
@@ -204,7 +204,7 @@ nvc0_dmac_create_fbdma(struct nouveau_object *core, u32 parent)
                                        .flags = NV_DMA_TARGET_VRAM |
                                                 NV_DMA_ACCESS_RDWR,
                                        .start = 0,
-                                       .limit = pfb->ram.size - 1,
+                                       .limit = pfb->ram->size - 1,
                                        .conf0 = NVC0_DMA_CONF0_ENABLE,
                                     }, sizeof(struct nv_dma_class), &object);
        if (ret)
@@ -216,7 +216,7 @@ nvc0_dmac_create_fbdma(struct nouveau_object *core, u32 parent)
                                        .flags = NV_DMA_TARGET_VRAM |
                                                 NV_DMA_ACCESS_RDWR,
                                        .start = 0,
-                                       .limit = pfb->ram.size - 1,
+                                       .limit = pfb->ram->size - 1,
                                        .conf0 = NVC0_DMA_CONF0_ENABLE | 0xfe,
                                 }, sizeof(struct nv_dma_class), &object);
        if (ret)
@@ -228,7 +228,7 @@ nvc0_dmac_create_fbdma(struct nouveau_object *core, u32 parent)
                                        .flags = NV_DMA_TARGET_VRAM |
                                                 NV_DMA_ACCESS_RDWR,
                                        .start = 0,
-                                       .limit = pfb->ram.size - 1,
+                                       .limit = pfb->ram->size - 1,
                                        .conf0 = NVC0_DMA_CONF0_ENABLE | 0xfe,
                                 }, sizeof(struct nv_dma_class), &object);
        return ret;
@@ -246,7 +246,7 @@ nvd0_dmac_create_fbdma(struct nouveau_object *core, u32 parent)
                                        .flags = NV_DMA_TARGET_VRAM |
                                                 NV_DMA_ACCESS_RDWR,
                                        .start = 0,
-                                       .limit = pfb->ram.size - 1,
+                                       .limit = pfb->ram->size - 1,
                                        .conf0 = NVD0_DMA_CONF0_ENABLE |
                                                 NVD0_DMA_CONF0_PAGE_LP,
                                     }, sizeof(struct nv_dma_class), &object);
@@ -259,7 +259,7 @@ nvd0_dmac_create_fbdma(struct nouveau_object *core, u32 parent)
                                        .flags = NV_DMA_TARGET_VRAM |
                                                 NV_DMA_ACCESS_RDWR,
                                        .start = 0,
-                                       .limit = pfb->ram.size - 1,
+                                       .limit = pfb->ram->size - 1,
                                        .conf0 = NVD0_DMA_CONF0_ENABLE | 0xfe |
                                                 NVD0_DMA_CONF0_PAGE_LP,
                                 }, sizeof(struct nv_dma_class), &object);
@@ -316,7 +316,7 @@ nv50_dmac_create(struct nouveau_object *core, u32 bclass, u8 head,
                                        .flags = NV_DMA_TARGET_VRAM |
                                                 NV_DMA_ACCESS_RDWR,
                                        .start = 0,
-                                       .limit = pfb->ram.size - 1,
+                                       .limit = pfb->ram->size - 1,
                                 }, sizeof(struct nv_dma_class), &object);
        if (ret)
                return ret;
index 69620e3..4efc33f 100644 (file)
@@ -493,12 +493,12 @@ mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data)
        struct hwsq_ucode *hwsq = &info->mclk_hwsq;
 
        if (mr <= 1) {
-               if (pfb->ram.ranks > 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)
+               if (pfb->ram->ranks > 1)
                        hwsq_wr32(hwsq, 0x1002e8 + ((mr - 2) * 4), data);
                hwsq_wr32(hwsq, 0x1002e0 + ((mr - 2) * 4), data);
        }
index 863f010..0d0ed59 100644 (file)
@@ -389,12 +389,12 @@ 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)
+               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)
+               if (pfb->ram->ranks > 1)
                        nv_wr32(device, 0x1002e8 + ((mr - 2) * 4), data);
                nv_wr32(device, 0x1002e0 + ((mr - 2) * 4), data);
        }
index 0d34eb5..3b7041c 100644 (file)
@@ -477,7 +477,7 @@ 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 (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));
@@ -496,15 +496,15 @@ 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 (pfb->ram->type != NV_MEM_TYPE_GDDR5) {
                if (mr <= 1) {
                        nv_wr32(device, 0x10f300 + ((mr - 0) * 4), data);
-                       if (pfb->ram.ranks > 1)
+                       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)
+                       if (pfb->ram->ranks > 1)
                                nv_wr32(device, 0x10f328 + ((mr - 2) * 4), data);
                }
        } else {
index 09f65dc..20c41e7 100644 (file)
@@ -1,7 +1,7 @@
 
 config DRM_OMAP
        tristate "OMAP DRM"
-       depends on DRM && !CONFIG_FB_OMAP2
+       depends on DRM
        depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM
        depends on OMAP2_DSS
        select DRM_KMS_HELPER
index 79b200a..11a5263 100644 (file)
@@ -40,7 +40,7 @@ struct omap_crtc {
         * mgr->id.)  Eventually this will be replaced w/ something
         * more common-panel-framework-y
         */
-       struct omap_overlay_manager mgr;
+       struct omap_overlay_manager *mgr;
 
        struct omap_video_timings timings;
        bool enabled;
@@ -90,7 +90,32 @@ uint32_t pipe2vbl(struct drm_crtc *crtc)
  * job of sequencing the setup of the video pipe in the proper order
  */
 
+/* ovl-mgr-id -> crtc */
+static struct omap_crtc *omap_crtcs[8];
+
 /* we can probably ignore these until we support command-mode panels: */
+static int omap_crtc_connect(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dst)
+{
+       if (mgr->output)
+               return -EINVAL;
+
+       if ((mgr->supported_outputs & dst->id) == 0)
+               return -EINVAL;
+
+       dst->manager = mgr;
+       mgr->output = dst;
+
+       return 0;
+}
+
+static void omap_crtc_disconnect(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dst)
+{
+       mgr->output->manager = NULL;
+       mgr->output = NULL;
+}
+
 static void omap_crtc_start_update(struct omap_overlay_manager *mgr)
 {
 }
@@ -107,7 +132,7 @@ static void omap_crtc_disable(struct omap_overlay_manager *mgr)
 static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
                const struct omap_video_timings *timings)
 {
-       struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr);
+       struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
        DBG("%s", omap_crtc->name);
        omap_crtc->timings = *timings;
        omap_crtc->full_update = true;
@@ -116,7 +141,7 @@ static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
 static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr,
                const struct dss_lcd_mgr_config *config)
 {
-       struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr);
+       struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
        DBG("%s", omap_crtc->name);
        dispc_mgr_set_lcd_config(omap_crtc->channel, config);
 }
@@ -135,6 +160,8 @@ static void omap_crtc_unregister_framedone_handler(
 }
 
 static const struct dss_mgr_ops mgr_ops = {
+               .connect = omap_crtc_connect,
+               .disconnect = omap_crtc_disconnect,
                .start_update = omap_crtc_start_update,
                .enable = omap_crtc_enable,
                .disable = omap_crtc_disable,
@@ -253,10 +280,6 @@ static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
                        NULL, NULL);
 }
 
-static void omap_crtc_load_lut(struct drm_crtc *crtc)
-{
-}
-
 static void vblank_cb(void *arg)
 {
        struct drm_crtc *crtc = arg;
@@ -366,7 +389,6 @@ static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
        .prepare = omap_crtc_prepare,
        .commit = omap_crtc_commit,
        .mode_set_base = omap_crtc_mode_set_base,
-       .load_lut = omap_crtc_load_lut,
 };
 
 const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
@@ -569,7 +591,7 @@ static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
        } else {
                if (encoder) {
                        omap_encoder_set_enabled(encoder, false);
-                       omap_encoder_update(encoder, &omap_crtc->mgr,
+                       omap_encoder_update(encoder, omap_crtc->mgr,
                                        &omap_crtc->timings);
                        omap_encoder_set_enabled(encoder, true);
                        omap_crtc->full_update = false;
@@ -595,6 +617,11 @@ static const char *channel_names[] = {
                [OMAP_DSS_CHANNEL_LCD2] = "lcd2",
 };
 
+void omap_crtc_pre_init(void)
+{
+       dss_install_mgr_ops(&mgr_ops);
+}
+
 /* initialize crtc */
 struct drm_crtc *omap_crtc_init(struct drm_device *dev,
                struct drm_plane *plane, enum omap_channel channel, int id)
@@ -635,9 +662,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
        omap_irq_register(dev, &omap_crtc->error_irq);
 
        /* temporary: */
-       omap_crtc->mgr.id = channel;
-
-       dss_install_mgr_ops(&mgr_ops);
+       omap_crtc->mgr = omap_dss_get_overlay_manager(channel);
 
        /* TODO: fix hard-coded setup.. add properties! */
        info = &omap_crtc->info;
@@ -651,6 +676,8 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 
        omap_plane_install_properties(omap_crtc->plane, &crtc->base);
 
+       omap_crtcs[channel] = omap_crtc;
+
        return crtc;
 
 fail:
index 826586f..a3004f1 100644 (file)
@@ -65,10 +65,8 @@ static int get_connector_type(struct omap_dss_device *dssdev)
        switch (dssdev->type) {
        case OMAP_DISPLAY_TYPE_HDMI:
                return DRM_MODE_CONNECTOR_HDMIA;
-       case OMAP_DISPLAY_TYPE_DPI:
-               if (!strcmp(dssdev->name, "dvi"))
-                       return DRM_MODE_CONNECTOR_DVID;
-               /* fallthrough */
+       case OMAP_DISPLAY_TYPE_DVI:
+               return DRM_MODE_CONNECTOR_DVID;
        default:
                return DRM_MODE_CONNECTOR_Unknown;
        }
@@ -97,6 +95,9 @@ static int omap_modeset_init(struct drm_device *dev)
        int num_mgrs = dss_feat_get_num_mgrs();
        int num_crtcs;
        int i, id = 0;
+       int r;
+
+       omap_crtc_pre_init();
 
        drm_mode_config_init(dev);
 
@@ -116,6 +117,7 @@ static int omap_modeset_init(struct drm_device *dev)
                struct drm_connector *connector;
                struct drm_encoder *encoder;
                enum omap_channel channel;
+               struct omap_overlay_manager *mgr;
 
                if (!dssdev->driver) {
                        dev_warn(dev->dev, "%s has no driver.. skipping it\n",
@@ -131,6 +133,13 @@ static int omap_modeset_init(struct drm_device *dev)
                        continue;
                }
 
+               r = dssdev->driver->connect(dssdev);
+               if (r) {
+                       dev_err(dev->dev, "could not connect display: %s\n",
+                                       dssdev->name);
+                       continue;
+               }
+
                encoder = omap_encoder_init(dev, dssdev);
 
                if (!encoder) {
@@ -172,8 +181,9 @@ static int omap_modeset_init(struct drm_device *dev)
                 * other possible channels to which the encoder can connect are
                 * not considered.
                 */
-               channel = dssdev->output->dispc_channel;
 
+               mgr = omapdss_find_mgr_from_display(dssdev);
+               channel = mgr->id;
                /*
                 * if this channel hasn't already been taken by a previously
                 * allocated crtc, we create a new crtc for it
@@ -247,6 +257,9 @@ static int omap_modeset_init(struct drm_device *dev)
                struct drm_encoder *encoder = priv->encoders[i];
                struct omap_dss_device *dssdev =
                                        omap_encoder_get_dssdev(encoder);
+               struct omap_dss_device *output;
+
+               output = omapdss_find_output_from_display(dssdev);
 
                /* figure out which crtc's we can connect the encoder to: */
                encoder->possible_crtcs = 0;
@@ -259,9 +272,11 @@ static int omap_modeset_init(struct drm_device *dev)
                        supported_outputs =
                                dss_feat_get_supported_outputs(crtc_channel);
 
-                       if (supported_outputs & dssdev->output->id)
+                       if (supported_outputs & output->id)
                                encoder->possible_crtcs |= (1 << id);
                }
+
+               omap_dss_put_device(output);
        }
 
        DBG("registered %d planes, %d crtcs, %d encoders and %d connectors\n",
index 215a20d..14f17da 100644 (file)
@@ -157,6 +157,7 @@ const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc);
 enum omap_channel omap_crtc_channel(struct drm_crtc *crtc);
 int omap_crtc_apply(struct drm_crtc *crtc,
                struct omap_drm_apply *apply);
+void omap_crtc_pre_init(void);
 struct drm_crtc *omap_crtc_init(struct drm_device *dev,
                struct drm_plane *plane, enum omap_channel channel, int id);
 
index b11ce60..002988d 100644 (file)
@@ -281,21 +281,7 @@ fail:
        return ret;
 }
 
-static void omap_crtc_fb_gamma_set(struct drm_crtc *crtc,
-               u16 red, u16 green, u16 blue, int regno)
-{
-       DBG("fbdev: set gamma");
-}
-
-static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc,
-               u16 *red, u16 *green, u16 *blue, int regno)
-{
-       DBG("fbdev: get gamma");
-}
-
 static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
-       .gamma_set = omap_crtc_fb_gamma_set,
-       .gamma_get = omap_crtc_fb_gamma_get,
        .fb_probe = omap_fbdev_create,
 };
 
index be7cd97..4fcca8d 100644 (file)
@@ -136,44 +136,21 @@ static void omap_gem_dmabuf_kunmap(struct dma_buf *buffer,
        kunmap(pages[page_num]);
 }
 
-/*
- * TODO maybe we can split up drm_gem_mmap to avoid duplicating
- * some here.. or at least have a drm_dmabuf_mmap helper.
- */
 static int omap_gem_dmabuf_mmap(struct dma_buf *buffer,
                struct vm_area_struct *vma)
 {
        struct drm_gem_object *obj = buffer->priv;
+       struct drm_device *dev = obj->dev;
        int ret = 0;
 
        if (WARN_ON(!obj->filp))
                return -EINVAL;
 
-       /* Check for valid size. */
-       if (omap_gem_mmap_size(obj) < vma->vm_end - vma->vm_start) {
-               ret = -EINVAL;
-               goto out_unlock;
-       }
-
-       if (!obj->dev->driver->gem_vm_ops) {
-               ret = -EINVAL;
-               goto out_unlock;
-       }
-
-       vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
-       vma->vm_ops = obj->dev->driver->gem_vm_ops;
-       vma->vm_private_data = obj;
-       vma->vm_page_prot =  pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
-
-       /* Take a ref for this mapping of the object, so that the fault
-        * handler can dereference the mmap offset's pointer to the object.
-        * This reference is cleaned up by the corresponding vm_close
-        * (which should happen whether the vma was created by this call, or
-        * by a vm_open due to mremap or partial unmap or whatever).
-        */
-       vma->vm_ops->open(vma);
-
-out_unlock:
+       mutex_lock(&dev->struct_mutex);
+       ret = drm_gem_mmap_obj(obj, omap_gem_mmap_size(obj), vma);
+       mutex_unlock(&dev->struct_mutex);
+       if (ret < 0)
+               return ret;
 
        return omap_gem_mmap_obj(obj, vma);
 }
index f867714..93c2f2c 100644 (file)
@@ -49,6 +49,11 @@ void qxl_ring_free(struct qxl_ring *ring)
        kfree(ring);
 }
 
+void qxl_ring_init_hdr(struct qxl_ring *ring)
+{
+       ring->ring->header.notify_on_prod = ring->n_elements;
+}
+
 struct qxl_ring *
 qxl_ring_create(struct qxl_ring_header *header,
                int element_size,
@@ -69,7 +74,7 @@ qxl_ring_create(struct qxl_ring_header *header,
        ring->prod_notify = prod_notify;
        ring->push_event = push_event;
        if (set_prod_notify)
-               header->notify_on_prod = ring->n_elements;
+               qxl_ring_init_hdr(ring);
        spin_lock_init(&ring->lock);
        return ring;
 }
@@ -87,7 +92,7 @@ static int qxl_check_header(struct qxl_ring *ring)
        return ret;
 }
 
-static int qxl_check_idle(struct qxl_ring *ring)
+int qxl_check_idle(struct qxl_ring *ring)
 {
        int ret;
        struct qxl_ring_header *header = &(ring->ring->header);
@@ -375,8 +380,8 @@ void qxl_io_destroy_primary(struct qxl_device *qdev)
        wait_for_io_cmd(qdev, 0, QXL_IO_DESTROY_PRIMARY_ASYNC);
 }
 
-void qxl_io_create_primary(struct qxl_device *qdev, unsigned width,
-                          unsigned height, unsigned offset, struct qxl_bo *bo)
+void qxl_io_create_primary(struct qxl_device *qdev,
+                          unsigned offset, struct qxl_bo *bo)
 {
        struct qxl_surface_create *create;
 
@@ -384,8 +389,8 @@ void qxl_io_create_primary(struct qxl_device *qdev, unsigned width,
                 qdev->ram_header);
        create = &qdev->ram_header->create_surface;
        create->format = bo->surf.format;
-       create->width = width;
-       create->height = height;
+       create->width = bo->surf.width;
+       create->height = bo->surf.height;
        create->stride = bo->surf.stride;
        create->mem = qxl_bo_physical_address(qdev, bo, offset);
 
index 823d29e..f76f5dd 100644 (file)
 #include "qxl_object.h"
 #include "drm_crtc_helper.h"
 
-static void qxl_crtc_set_to_mode(struct qxl_device *qdev,
-                                struct drm_connector *connector,
-                                struct qxl_head *head)
+static bool qxl_head_enabled(struct qxl_head *head)
 {
-       struct drm_device *dev = connector->dev;
-       struct drm_display_mode *mode, *t;
-       int width = head->width;
-       int height = head->height;
-
-       if (width < 320 || height < 240) {
-               qxl_io_log(qdev, "%s: bad head: %dx%d", width, height);
-               width = 1024;
-               height = 768;
-       }
-       if (width * height * 4 > 16*1024*1024) {
-               width = 1024;
-               height = 768;
-       }
-       /* TODO: go over regular modes and removed preferred? */
-       list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
-               drm_mode_remove(connector, mode);
-       mode = drm_cvt_mode(dev, width, height, 60, false, false, false);
-       mode->type |= DRM_MODE_TYPE_PREFERRED;
-       mode->status = MODE_OK;
-       drm_mode_probed_add(connector, mode);
-       qxl_io_log(qdev, "%s: %d x %d\n", __func__, width, height);
-}
-
-void qxl_crtc_set_from_monitors_config(struct qxl_device *qdev)
-{
-       struct drm_connector *connector;
-       int i;
-       struct drm_device *dev = qdev->ddev;
-
-       i = 0;
-       qxl_io_log(qdev, "%s: %d, %d\n", __func__,
-                  dev->mode_config.num_connector,
-                  qdev->monitors_config->count);
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               if (i > qdev->monitors_config->count) {
-                       /* crtc will be reported as disabled */
-                       continue;
-               }
-               qxl_crtc_set_to_mode(qdev, connector,
-                                    &qdev->monitors_config->heads[i]);
-               ++i;
-       }
+       return head->width && head->height;
 }
 
 void qxl_alloc_client_monitors_config(struct qxl_device *qdev, unsigned count)
@@ -106,7 +62,6 @@ static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev)
        int num_monitors;
        uint32_t crc;
 
-       BUG_ON(!qdev->monitors_config);
        num_monitors = qdev->rom->client_monitors_config.count;
        crc = crc32(0, (const uint8_t *)&qdev->rom->client_monitors_config,
                  sizeof(qdev->rom->client_monitors_config));
@@ -117,8 +72,8 @@ static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev)
                return 1;
        }
        if (num_monitors > qdev->monitors_config->max_allowed) {
-               DRM_INFO("client monitors list will be truncated: %d < %d\n",
-                        qdev->monitors_config->max_allowed, num_monitors);
+               DRM_DEBUG_KMS("client monitors list will be truncated: %d < %d\n",
+                             qdev->monitors_config->max_allowed, num_monitors);
                num_monitors = qdev->monitors_config->max_allowed;
        } else {
                num_monitors = qdev->rom->client_monitors_config.count;
@@ -132,18 +87,15 @@ static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev)
                        &qdev->rom->client_monitors_config.heads[i];
                struct qxl_head *client_head =
                        &qdev->client_monitors_config->heads[i];
-               struct qxl_head *head = &qdev->monitors_config->heads[i];
-               client_head->x = head->x = c_rect->left;
-               client_head->y = head->y = c_rect->top;
-               client_head->width = head->width =
-                                               c_rect->right - c_rect->left;
-               client_head->height = head->height =
-                                               c_rect->bottom - c_rect->top;
-               client_head->surface_id = head->surface_id = 0;
-               client_head->id = head->id = i;
-               client_head->flags = head->flags = 0;
-               QXL_DEBUG(qdev, "read %dx%d+%d+%d\n", head->width, head->height,
-                         head->x, head->y);
+               client_head->x = c_rect->left;
+               client_head->y = c_rect->top;
+               client_head->width = c_rect->right - c_rect->left;
+               client_head->height = c_rect->bottom - c_rect->top;
+               client_head->surface_id = 0;
+               client_head->id = i;
+               client_head->flags = 0;
+               DRM_DEBUG_KMS("read %dx%d+%d+%d\n", client_head->width, client_head->height,
+                         client_head->x, client_head->y);
        }
        return 0;
 }
@@ -155,10 +107,7 @@ void qxl_display_read_client_monitors_config(struct qxl_device *qdev)
                qxl_io_log(qdev, "failed crc check for client_monitors_config,"
                                 " retrying\n");
        }
-       qxl_crtc_set_from_monitors_config(qdev);
-       /* fire off a uevent and let userspace tell us what to do */
-       qxl_io_log(qdev, "calling drm_sysfs_hotplug_event\n");
-       drm_sysfs_hotplug_event(qdev->ddev);
+       drm_helper_hpd_irq_event(qdev->ddev);
 }
 
 static int qxl_add_monitors_config_modes(struct drm_connector *connector)
@@ -170,9 +119,9 @@ static int qxl_add_monitors_config_modes(struct drm_connector *connector)
        struct drm_display_mode *mode = NULL;
        struct qxl_head *head;
 
-       if (!qdev->monitors_config)
+       if (!qdev->client_monitors_config)
                return 0;
-       head = &qdev->monitors_config->heads[h];
+       head = &qdev->client_monitors_config->heads[h];
 
        mode = drm_cvt_mode(dev, head->width, head->height, 60, false, false,
                            false);
@@ -222,12 +171,6 @@ static int qxl_add_common_modes(struct drm_connector *connector)
        return i - 1;
 }
 
-static void qxl_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
-                              u16 *blue, uint32_t start, uint32_t size)
-{
-       /* TODO */
-}
-
 static void qxl_crtc_destroy(struct drm_crtc *crtc)
 {
        struct qxl_crtc *qxl_crtc = to_qxl_crtc(crtc);
@@ -255,11 +198,11 @@ qxl_hide_cursor(struct qxl_device *qdev)
        qxl_release_unreserve(qdev, release);
 }
 
-static int qxl_crtc_cursor_set(struct drm_crtc *crtc,
-                              struct drm_file *file_priv,
-                              uint32_t handle,
-                              uint32_t width,
-                              uint32_t height)
+static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
+                               struct drm_file *file_priv,
+                               uint32_t handle,
+                               uint32_t width,
+                               uint32_t height, int32_t hot_x, int32_t hot_y)
 {
        struct drm_device *dev = crtc->dev;
        struct qxl_device *qdev = dev->dev_private;
@@ -315,8 +258,8 @@ static int qxl_crtc_cursor_set(struct drm_crtc *crtc,
        cursor->header.type = SPICE_CURSOR_TYPE_ALPHA;
        cursor->header.width = 64;
        cursor->header.height = 64;
-       cursor->header.hot_spot_x = 0;
-       cursor->header.hot_spot_y = 0;
+       cursor->header.hot_spot_x = hot_x;
+       cursor->header.hot_spot_y = hot_y;
        cursor->data_size = size;
        cursor->chunk.next_chunk = 0;
        cursor->chunk.prev_chunk = 0;
@@ -397,9 +340,8 @@ static int qxl_crtc_cursor_move(struct drm_crtc *crtc,
 
 
 static const struct drm_crtc_funcs qxl_crtc_funcs = {
-       .cursor_set = qxl_crtc_cursor_set,
+       .cursor_set2 = qxl_crtc_cursor_set2,
        .cursor_move = qxl_crtc_cursor_move,
-       .gamma_set = qxl_crtc_gamma_set,
        .set_config = drm_crtc_helper_set_config,
        .destroy = qxl_crtc_destroy,
 };
@@ -506,7 +448,7 @@ qxl_send_monitors_config(struct qxl_device *qdev)
        for (i = 0 ; i < qdev->monitors_config->count ; ++i) {
                struct qxl_head *head = &qdev->monitors_config->heads[i];
 
-               if (head->y > 8192 || head->y < head->x ||
+               if (head->y > 8192 || head->x > 8192 ||
                    head->width > 8192 || head->height > 8192) {
                        DRM_ERROR("head %d wrong: %dx%d+%d+%d\n",
                                  i, head->width, head->height,
@@ -517,16 +459,19 @@ qxl_send_monitors_config(struct qxl_device *qdev)
        qxl_io_monitors_config(qdev);
 }
 
-static void qxl_monitors_config_set_single(struct qxl_device *qdev,
-                                          unsigned x, unsigned y,
-                                          unsigned width, unsigned height)
+static void qxl_monitors_config_set(struct qxl_device *qdev,
+                                   int index,
+                                   unsigned x, unsigned y,
+                                   unsigned width, unsigned height,
+                                   unsigned surf_id)
 {
-       DRM_DEBUG("%dx%d+%d+%d\n", width, height, x, y);
-       qdev->monitors_config->count = 1;
-       qdev->monitors_config->heads[0].x = x;
-       qdev->monitors_config->heads[0].y = y;
-       qdev->monitors_config->heads[0].width = width;
-       qdev->monitors_config->heads[0].height = height;
+       DRM_DEBUG_KMS("%d:%dx%d+%d+%d\n", index, width, height, x, y);
+       qdev->monitors_config->heads[index].x = x;
+       qdev->monitors_config->heads[index].y = y;
+       qdev->monitors_config->heads[index].width = width;
+       qdev->monitors_config->heads[index].height = height;
+       qdev->monitors_config->heads[index].surface_id = surf_id;
+
 }
 
 static int qxl_crtc_mode_set(struct drm_crtc *crtc,
@@ -540,10 +485,11 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
        struct qxl_mode *m = (void *)mode->private;
        struct qxl_framebuffer *qfb;
        struct qxl_bo *bo, *old_bo = NULL;
+       struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
        uint32_t width, height, base_offset;
        bool recreate_primary = false;
        int ret;
-
+       int surf_id;
        if (!crtc->fb) {
                DRM_DEBUG_KMS("No FB bound\n");
                return 0;
@@ -567,7 +513,8 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
                  adjusted_mode->hdisplay,
                  adjusted_mode->vdisplay);
 
-       recreate_primary = true;
+       if (qcrtc->index == 0)
+               recreate_primary = true;
 
        width = mode->hdisplay;
        height = mode->vdisplay;
@@ -588,8 +535,11 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
                           "recreate primary: %dx%d (was %dx%d,%d,%d)\n",
                           width, height, bo->surf.width,
                           bo->surf.height, bo->surf.stride, bo->surf.format);
-               qxl_io_create_primary(qdev, width, height, base_offset, bo);
+               qxl_io_create_primary(qdev, base_offset, bo);
                bo->is_primary = true;
+               surf_id = 0;
+       } else {
+               surf_id = bo->surface_id;
        }
 
        if (old_bo && old_bo != bo) {
@@ -599,11 +549,9 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
                qxl_bo_unreserve(old_bo);
        }
 
-       if (qdev->monitors_config->count == 0) {
-               qxl_monitors_config_set_single(qdev, x, y,
-                                              mode->hdisplay,
-                                              mode->vdisplay);
-       }
+       qxl_monitors_config_set(qdev, qcrtc->index, x, y,
+                               mode->hdisplay,
+                               mode->vdisplay, surf_id);
        return 0;
 }
 
@@ -619,21 +567,36 @@ static void qxl_crtc_commit(struct drm_crtc *crtc)
        DRM_DEBUG("\n");
 }
 
-static void qxl_crtc_load_lut(struct drm_crtc *crtc)
+static void qxl_crtc_disable(struct drm_crtc *crtc)
 {
-       DRM_DEBUG("\n");
+       struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct qxl_device *qdev = dev->dev_private;
+       if (crtc->fb) {
+               struct qxl_framebuffer *qfb = to_qxl_framebuffer(crtc->fb);
+               struct qxl_bo *bo = gem_to_qxl_bo(qfb->obj);
+               int ret;
+               ret = qxl_bo_reserve(bo, false);
+               qxl_bo_unpin(bo);
+               qxl_bo_unreserve(bo);
+               crtc->fb = NULL;
+       }
+
+       qxl_monitors_config_set(qdev, qcrtc->index, 0, 0, 0, 0, 0);
+
+       qxl_send_monitors_config(qdev);
 }
 
 static const struct drm_crtc_helper_funcs qxl_crtc_helper_funcs = {
        .dpms = qxl_crtc_dpms,
+       .disable = qxl_crtc_disable,
        .mode_fixup = qxl_crtc_mode_fixup,
        .mode_set = qxl_crtc_mode_set,
        .prepare = qxl_crtc_prepare,
        .commit = qxl_crtc_commit,
-       .load_lut = qxl_crtc_load_lut,
 };
 
-static int qdev_crtc_init(struct drm_device *dev, int num_crtc)
+static int qdev_crtc_init(struct drm_device *dev, int crtc_id)
 {
        struct qxl_crtc *qxl_crtc;
 
@@ -642,7 +605,7 @@ static int qdev_crtc_init(struct drm_device *dev, int num_crtc)
                return -ENOMEM;
 
        drm_crtc_init(dev, &qxl_crtc->base, &qxl_crtc_funcs);
-
+       qxl_crtc->index = crtc_id;
        drm_mode_crtc_set_gamma_size(&qxl_crtc->base, 256);
        drm_crtc_helper_add(&qxl_crtc->base, &qxl_crtc_helper_funcs);
        return 0;
@@ -670,18 +633,13 @@ static void qxl_write_monitors_config_for_encoder(struct qxl_device *qdev,
                struct drm_encoder *encoder)
 {
        int i;
+       struct qxl_output *output = drm_encoder_to_qxl_output(encoder);
        struct qxl_head *head;
        struct drm_display_mode *mode;
 
        BUG_ON(!encoder);
        /* TODO: ugly, do better */
-       for (i = 0 ; (encoder->possible_crtcs != (1 << i)) && i < 32; ++i)
-               ;
-       if (encoder->possible_crtcs != (1 << i)) {
-               DRM_ERROR("encoder has wrong possible_crtcs: %x\n",
-                         encoder->possible_crtcs);
-               return;
-       }
+       i = output->index;
        if (!qdev->monitors_config ||
            qdev->monitors_config->max_allowed <= i) {
                DRM_ERROR(
@@ -699,7 +657,6 @@ static void qxl_write_monitors_config_for_encoder(struct qxl_device *qdev,
                DRM_DEBUG("missing for multiple monitors: no head holes\n");
        head = &qdev->monitors_config->heads[i];
        head->id = i;
-       head->surface_id = 0;
        if (encoder->crtc->enabled) {
                mode = &encoder->crtc->mode;
                head->width = mode->hdisplay;
@@ -714,8 +671,8 @@ static void qxl_write_monitors_config_for_encoder(struct qxl_device *qdev,
                head->x = 0;
                head->y = 0;
        }
-       DRM_DEBUG("setting head %d to +%d+%d %dx%d\n",
-                 i, head->x, head->y, head->width, head->height);
+       DRM_DEBUG_KMS("setting head %d to +%d+%d %dx%d out of %d\n",
+                     i, head->x, head->y, head->width, head->height, qdev->monitors_config->count);
        head->flags = 0;
        /* TODO - somewhere else to call this for multiple monitors
         * (config_commit?) */
@@ -810,8 +767,9 @@ static enum drm_connector_status qxl_conn_detect(
 
        /* The first monitor is always connected */
        connected = (output->index == 0) ||
-                   (qdev->monitors_config &&
-                    qdev->monitors_config->count > output->index);
+                   (qdev->client_monitors_config &&
+                    qdev->client_monitors_config->count > output->index &&
+                    qxl_head_enabled(&qdev->client_monitors_config->heads[output->index]));
 
        DRM_DEBUG("\n");
        return connected ? connector_status_connected
@@ -875,6 +833,8 @@ static int qdev_output_init(struct drm_device *dev, int num_output)
        drm_encoder_init(dev, &qxl_output->enc, &qxl_enc_funcs,
                         DRM_MODE_ENCODER_VIRTUAL);
 
+       /* we get HPD via client monitors config */
+       connector->polled = DRM_CONNECTOR_POLL_HPD;
        encoder->possible_crtcs = 1 << num_output;
        drm_mode_connector_attach_encoder(&qxl_output->base,
                                          &qxl_output->enc);
@@ -914,16 +874,14 @@ static const struct drm_mode_config_funcs qxl_mode_funcs = {
        .fb_create = qxl_user_framebuffer_create,
 };
 
-int qxl_modeset_init(struct qxl_device *qdev)
+int qxl_create_monitors_object(struct qxl_device *qdev)
 {
-       int i;
        int ret;
        struct drm_gem_object *gobj;
-       int max_allowed = QXL_NUM_OUTPUTS;
+       int max_allowed = qxl_num_crtc;
        int monitors_config_size = sizeof(struct qxl_monitors_config) +
-                                  max_allowed * sizeof(struct qxl_head);
+               max_allowed * sizeof(struct qxl_head);
 
-       drm_mode_config_init(qdev->ddev);
        ret = qxl_gem_object_create(qdev, monitors_config_size, 0,
                                    QXL_GEM_DOMAIN_VRAM,
                                    false, false, NULL, &gobj);
@@ -932,13 +890,59 @@ int qxl_modeset_init(struct qxl_device *qdev)
                return -ENOMEM;
        }
        qdev->monitors_config_bo = gem_to_qxl_bo(gobj);
+
+       ret = qxl_bo_reserve(qdev->monitors_config_bo, false);
+       if (ret)
+               return ret;
+
+       ret = qxl_bo_pin(qdev->monitors_config_bo, QXL_GEM_DOMAIN_VRAM, NULL);
+       if (ret) {
+               qxl_bo_unreserve(qdev->monitors_config_bo);
+               return ret;
+       }
+
+       qxl_bo_unreserve(qdev->monitors_config_bo);
+
        qxl_bo_kmap(qdev->monitors_config_bo, NULL);
+
        qdev->monitors_config = qdev->monitors_config_bo->kptr;
        qdev->ram_header->monitors_config =
                qxl_bo_physical_address(qdev, qdev->monitors_config_bo, 0);
 
        memset(qdev->monitors_config, 0, monitors_config_size);
        qdev->monitors_config->max_allowed = max_allowed;
+       return 0;
+}
+
+int qxl_destroy_monitors_object(struct qxl_device *qdev)
+{
+       int ret;
+
+       qdev->monitors_config = NULL;
+       qdev->ram_header->monitors_config = 0;
+
+       qxl_bo_kunmap(qdev->monitors_config_bo);
+       ret = qxl_bo_reserve(qdev->monitors_config_bo, false);
+       if (ret)
+               return ret;
+
+       qxl_bo_unpin(qdev->monitors_config_bo);
+       qxl_bo_unreserve(qdev->monitors_config_bo);
+
+       qxl_bo_unref(&qdev->monitors_config_bo);
+       return 0;
+}
+
+int qxl_modeset_init(struct qxl_device *qdev)
+{
+       int i;
+       int ret;
+
+       drm_mode_config_init(qdev->ddev);
+
+       ret = qxl_create_monitors_object(qdev);
+       if (ret)
+               return ret;
 
        qdev->ddev->mode_config.funcs = (void *)&qxl_mode_funcs;
 
@@ -949,7 +953,7 @@ int qxl_modeset_init(struct qxl_device *qdev)
        qdev->ddev->mode_config.max_height = 8192;
 
        qdev->ddev->mode_config.fb_base = qdev->vram_base;
-       for (i = 0 ; i < QXL_NUM_OUTPUTS; ++i) {
+       for (i = 0 ; i < qxl_num_crtc; ++i) {
                qdev_crtc_init(qdev->ddev, i);
                qdev_output_init(qdev->ddev, i);
        }
@@ -966,6 +970,8 @@ int qxl_modeset_init(struct qxl_device *qdev)
 void qxl_modeset_fini(struct qxl_device *qdev)
 {
        qxl_fbdev_fini(qdev);
+
+       qxl_destroy_monitors_object(qdev);
        if (qdev->mode_info.mode_config_initialized) {
                drm_mode_config_cleanup(qdev->ddev);
                qdev->mode_info.mode_config_initialized = false;
index aa291d8..df0b577 100644 (file)
@@ -33,8 +33,9 @@
 
 #include "drmP.h"
 #include "drm/drm.h"
-
+#include "drm_crtc_helper.h"
 #include "qxl_drv.h"
+#include "qxl_object.h"
 
 extern int qxl_max_ioctls;
 static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
@@ -47,10 +48,14 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
 MODULE_DEVICE_TABLE(pci, pciidlist);
 
 static int qxl_modeset = -1;
+int qxl_num_crtc = 4;
 
 MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
 module_param_named(modeset, qxl_modeset, int, 0400);
 
+MODULE_PARM_DESC(num_heads, "Number of virtual crtcs to expose (default 4)");
+module_param_named(num_heads, qxl_num_crtc, int, 0400);
+
 static struct drm_driver qxl_driver;
 static struct pci_driver qxl_pci_driver;
 
@@ -73,13 +78,6 @@ qxl_pci_remove(struct pci_dev *pdev)
        drm_put_dev(dev);
 }
 
-static struct pci_driver qxl_pci_driver = {
-        .name = DRIVER_NAME,
-        .id_table = pciidlist,
-        .probe = qxl_pci_probe,
-        .remove = qxl_pci_remove,
-};
-
 static const struct file_operations qxl_fops = {
        .owner = THIS_MODULE,
        .open = drm_open,
@@ -90,6 +88,130 @@ static const struct file_operations qxl_fops = {
        .mmap = qxl_mmap,
 };
 
+static int qxl_drm_freeze(struct drm_device *dev)
+{
+       struct pci_dev *pdev = dev->pdev;
+       struct qxl_device *qdev = dev->dev_private;
+       struct drm_crtc *crtc;
+
+       drm_kms_helper_poll_disable(dev);
+
+       console_lock();
+       qxl_fbdev_set_suspend(qdev, 1);
+       console_unlock();
+
+       /* unpin the front buffers */
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+               if (crtc->enabled)
+                       (*crtc_funcs->disable)(crtc);
+       }
+
+       qxl_destroy_monitors_object(qdev);
+       qxl_surf_evict(qdev);
+       qxl_vram_evict(qdev);
+
+       while (!qxl_check_idle(qdev->command_ring));
+       while (!qxl_check_idle(qdev->release_ring))
+               qxl_queue_garbage_collect(qdev, 1);
+
+       pci_save_state(pdev);
+
+       return 0;
+}
+
+static int qxl_drm_resume(struct drm_device *dev, bool thaw)
+{
+       struct qxl_device *qdev = dev->dev_private;
+
+       qdev->ram_header->int_mask = QXL_INTERRUPT_MASK;
+       if (!thaw) {
+               qxl_reinit_memslots(qdev);
+               qxl_ring_init_hdr(qdev->release_ring);
+       }
+
+       qxl_create_monitors_object(qdev);
+       drm_helper_resume_force_mode(dev);
+
+       console_lock();
+       qxl_fbdev_set_suspend(qdev, 0);
+       console_unlock();
+
+       drm_kms_helper_poll_enable(dev);
+       return 0;
+}
+
+static int qxl_pm_suspend(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       int error;
+
+       error = qxl_drm_freeze(drm_dev);
+       if (error)
+               return error;
+
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
+       return 0;
+}
+
+static int qxl_pm_resume(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       if (pci_enable_device(pdev)) {
+               return -EIO;
+       }
+
+       return qxl_drm_resume(drm_dev, false);
+}
+
+static int qxl_pm_thaw(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+       return qxl_drm_resume(drm_dev, true);
+}
+
+static int qxl_pm_freeze(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+       return qxl_drm_freeze(drm_dev);
+}
+
+static int qxl_pm_restore(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       struct qxl_device *qdev = drm_dev->dev_private;
+
+       qxl_io_reset(qdev);
+       return qxl_drm_resume(drm_dev, false);
+}
+
+static const struct dev_pm_ops qxl_pm_ops = {
+       .suspend = qxl_pm_suspend,
+       .resume = qxl_pm_resume,
+       .freeze = qxl_pm_freeze,
+       .thaw = qxl_pm_thaw,
+       .poweroff = qxl_pm_freeze,
+       .restore = qxl_pm_restore,
+};
+static struct pci_driver qxl_pci_driver = {
+        .name = DRIVER_NAME,
+        .id_table = pciidlist,
+        .probe = qxl_pci_probe,
+        .remove = qxl_pci_remove,
+        .driver.pm = &qxl_pm_ops,
+};
+
 static struct drm_driver qxl_driver = {
        .driver_features = DRIVER_GEM | DRIVER_MODESET |
                           DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
index 43d06ab..aacb791 100644 (file)
 #define DRIVER_MINOR 1
 #define DRIVER_PATCHLEVEL 0
 
-#define QXL_NUM_OUTPUTS 1
-
 #define QXL_DEBUGFS_MAX_COMPONENTS             32
 
 extern int qxl_log_level;
+extern int qxl_num_crtc;
 
 enum {
        QXL_INFO_LEVEL = 1,
@@ -139,6 +138,7 @@ struct qxl_reloc_list {
 
 struct qxl_crtc {
        struct drm_crtc base;
+       int index;
        int cur_x;
        int cur_y;
 };
@@ -156,7 +156,7 @@ struct qxl_framebuffer {
 
 #define to_qxl_crtc(x) container_of(x, struct qxl_crtc, base)
 #define drm_connector_to_qxl_output(x) container_of(x, struct qxl_output, base)
-#define drm_encoder_to_qxl_output(x) container_of(x, struct qxl_output, base)
+#define drm_encoder_to_qxl_output(x) container_of(x, struct qxl_output, enc)
 #define to_qxl_framebuffer(x) container_of(x, struct qxl_framebuffer, base)
 
 struct qxl_mman {
@@ -331,6 +331,10 @@ void qxl_modeset_fini(struct qxl_device *qdev);
 int qxl_bo_init(struct qxl_device *qdev);
 void qxl_bo_fini(struct qxl_device *qdev);
 
+void qxl_reinit_memslots(struct qxl_device *qdev);
+int qxl_surf_evict(struct qxl_device *qdev);
+int qxl_vram_evict(struct qxl_device *qdev);
+
 struct qxl_ring *qxl_ring_create(struct qxl_ring_header *header,
                                 int element_size,
                                 int n_elements,
@@ -338,6 +342,8 @@ struct qxl_ring *qxl_ring_create(struct qxl_ring_header *header,
                                 bool set_prod_notify,
                                 wait_queue_head_t *push_event);
 void qxl_ring_free(struct qxl_ring *ring);
+void qxl_ring_init_hdr(struct qxl_ring *ring);
+int qxl_check_idle(struct qxl_ring *ring);
 
 static inline void *
 qxl_fb_virtual_address(struct qxl_device *qdev, unsigned long physical)
@@ -365,6 +371,7 @@ void qxl_fbdev_fini(struct qxl_device *qdev);
 int qxl_get_handle_for_primary_fb(struct qxl_device *qdev,
                                  struct drm_file *file_priv,
                                  uint32_t *handle);
+void qxl_fbdev_set_suspend(struct qxl_device *qdev, int state);
 
 /* qxl_display.c */
 int
@@ -374,6 +381,8 @@ qxl_framebuffer_init(struct drm_device *dev,
                     struct drm_gem_object *obj);
 void qxl_display_read_client_monitors_config(struct qxl_device *qdev);
 void qxl_send_monitors_config(struct qxl_device *qdev);
+int qxl_create_monitors_object(struct qxl_device *qdev);
+int qxl_destroy_monitors_object(struct qxl_device *qdev);
 
 /* used by qxl_debugfs only */
 void qxl_crtc_set_from_monitors_config(struct qxl_device *qdev);
@@ -435,7 +444,7 @@ void qxl_update_screen(struct qxl_device *qxl);
 /* qxl io operations (qxl_cmd.c) */
 
 void qxl_io_create_primary(struct qxl_device *qdev,
-                          unsigned width, unsigned height, unsigned offset,
+                          unsigned offset,
                           struct qxl_bo *bo);
 void qxl_io_destroy_primary(struct qxl_device *qdev);
 void qxl_io_memslot_add(struct qxl_device *qdev, uint8_t id);
@@ -528,6 +537,7 @@ irqreturn_t qxl_irq_handler(DRM_IRQ_ARGS);
 
 /* qxl_fb.c */
 int qxl_fb_init(struct qxl_device *qdev);
+bool qxl_fbdev_qobj_is_fb(struct qxl_device *qdev, struct qxl_bo *qobj);
 
 int qxl_debugfs_add_files(struct qxl_device *qdev,
                          struct drm_info_list *files,
index b3c5127..76f39d8 100644 (file)
@@ -520,10 +520,6 @@ static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev)
 }
 
 static struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
-       /* TODO
-       .gamma_set = qxl_crtc_fb_gamma_set,
-       .gamma_get = qxl_crtc_fb_gamma_get,
-       */
        .fb_probe = qxl_fb_find_or_create_single,
 };
 
@@ -542,7 +538,7 @@ int qxl_fbdev_init(struct qxl_device *qdev)
        qfbdev->helper.funcs = &qxl_fb_helper_funcs;
 
        ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
-                                1 /* num_crtc - QXL supports just 1 */,
+                                qxl_num_crtc /* num_crtc - QXL supports just 1 */,
                                 QXLFB_CONN_LIMIT);
        if (ret) {
                kfree(qfbdev);
@@ -564,4 +560,14 @@ void qxl_fbdev_fini(struct qxl_device *qdev)
        qdev->mode_info.qfbdev = NULL;
 }
 
+void qxl_fbdev_set_suspend(struct qxl_device *qdev, int state)
+{
+       fb_set_suspend(qdev->mode_info.qfbdev->helper.fbdev, state);
+}
 
+bool qxl_fbdev_qobj_is_fb(struct qxl_device *qdev, struct qxl_bo *qobj)
+{
+       if (qobj == gem_to_qxl_bo(qdev->mode_info.qfbdev->qfb.obj))
+               return true;
+       return false;
+}
index a30f294..27f45e4 100644 (file)
@@ -188,6 +188,12 @@ static int qxl_execbuffer_ioctl(struct drm_device *dev, void *data,
                /* TODO copy slow path code from i915 */
                fb_cmd = qxl_bo_kmap_atomic_page(qdev, cmd_bo, (release->release_offset & PAGE_SIZE));
                unwritten = __copy_from_user_inatomic_nocache(fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_SIZE), (void *)(unsigned long)user_cmd.command, user_cmd.command_size);
+
+               {
+                       struct qxl_drawable *draw = fb_cmd;
+
+                       draw->mm_time = qdev->rom->mm_clock;
+               }
                qxl_bo_kunmap_atomic_page(qdev, cmd_bo, fb_cmd);
                if (unwritten) {
                        DRM_ERROR("got unwritten %d\n", unwritten);
index e27ce2a..9e8da9e 100644 (file)
@@ -26,6 +26,7 @@
 #include "qxl_drv.h"
 #include "qxl_object.h"
 
+#include <drm/drm_crtc_helper.h>
 #include <linux/io-mapping.h>
 
 int qxl_log_level;
@@ -72,21 +73,28 @@ static bool qxl_check_device(struct qxl_device *qdev)
        return true;
 }
 
+static void setup_hw_slot(struct qxl_device *qdev, int slot_index,
+                         struct qxl_memslot *slot)
+{
+       qdev->ram_header->mem_slot.mem_start = slot->start_phys_addr;
+       qdev->ram_header->mem_slot.mem_end = slot->end_phys_addr;
+       qxl_io_memslot_add(qdev, slot_index);
+}
+
 static uint8_t setup_slot(struct qxl_device *qdev, uint8_t slot_index_offset,
        unsigned long start_phys_addr, unsigned long end_phys_addr)
 {
        uint64_t high_bits;
        struct qxl_memslot *slot;
        uint8_t slot_index;
-       struct qxl_ram_header *ram_header = qdev->ram_header;
 
        slot_index = qdev->rom->slots_start + slot_index_offset;
        slot = &qdev->mem_slots[slot_index];
        slot->start_phys_addr = start_phys_addr;
        slot->end_phys_addr = end_phys_addr;
-       ram_header->mem_slot.mem_start = slot->start_phys_addr;
-       ram_header->mem_slot.mem_end = slot->end_phys_addr;
-       qxl_io_memslot_add(qdev, slot_index);
+
+       setup_hw_slot(qdev, slot_index, slot);
+
        slot->generation = qdev->rom->slot_generation;
        high_bits = slot_index << qdev->slot_gen_bits;
        high_bits |= slot->generation;
@@ -95,6 +103,12 @@ static uint8_t setup_slot(struct qxl_device *qdev, uint8_t slot_index_offset,
        return slot_index;
 }
 
+void qxl_reinit_memslots(struct qxl_device *qdev)
+{
+       setup_hw_slot(qdev, qdev->main_mem_slot, &qdev->mem_slots[qdev->main_mem_slot]);
+       setup_hw_slot(qdev, qdev->surfaces_mem_slot, &qdev->mem_slots[qdev->surfaces_mem_slot]);
+}
+
 static void qxl_gc_work(struct work_struct *work)
 {
        struct qxl_device *qdev = container_of(work, struct qxl_device, gc_work);
@@ -294,6 +308,8 @@ int qxl_driver_load(struct drm_device *dev, unsigned long flags)
                goto out;
        }
 
+       drm_kms_helper_poll_init(qdev->ddev);
+
        return 0;
 out:
        kfree(qdev);
index d9b12e7..1191fe7 100644 (file)
@@ -363,3 +363,13 @@ int qxl_bo_list_add(struct qxl_reloc_list *reloc_list, struct qxl_bo *bo)
                return ret;
        return 0;
 }
+
+int qxl_surf_evict(struct qxl_device *qdev)
+{
+       return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_PRIV0);
+}
+
+int qxl_vram_evict(struct qxl_device *qdev)
+{
+       return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_VRAM);
+}
index b4fd89f..ee7ad79 100644 (file)
@@ -57,11 +57,6 @@ static inline unsigned long qxl_bo_size(struct qxl_bo *bo)
        return bo->tbo.num_pages << PAGE_SHIFT;
 }
 
-static inline bool qxl_bo_is_reserved(struct qxl_bo *bo)
-{
-       return !!atomic_read(&bo->tbo.reserved);
-}
-
 static inline u64 qxl_bo_mmap_offset(struct qxl_bo *bo)
 {
        return bo->tbo.addr_space_offset;
index 86c5e36..c3df52c 100644 (file)
@@ -76,7 +76,10 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
        evergreen.o evergreen_cs.o evergreen_blit_shaders.o evergreen_blit_kms.o \
        evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \
        atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \
-       si_blit_shaders.o radeon_prime.o radeon_uvd.o
+       si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \
+       r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \
+       rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \
+       trinity_smc.o ni_dpm.o si_smc.o si_dpm.o
 
 radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
 radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
index ca4b038..0619269 100644 (file)
@@ -69,6 +69,8 @@
 #define ENCODER_OBJECT_ID_ALMOND                  0x22
 #define ENCODER_OBJECT_ID_TRAVIS                  0x23
 #define ENCODER_OBJECT_ID_NUTMEG                  0x22
+#define ENCODER_OBJECT_ID_HDMI_ANX9805            0x26
+
 /* Kaleidoscope (KLDSCP) Class Display Hardware (internal) */
 #define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1   0x13
 #define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1    0x14
@@ -86,6 +88,8 @@
 #define ENCODER_OBJECT_ID_INTERNAL_UNIPHY1        0x20
 #define ENCODER_OBJECT_ID_INTERNAL_UNIPHY2        0x21
 #define ENCODER_OBJECT_ID_INTERNAL_VCE            0x24
+#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY3        0x25
+#define ENCODER_OBJECT_ID_INTERNAL_AMCLK          0x27
 
 #define ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO    0xFF
 
                                                  GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
                                                  ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT)
 
+#define ENCODER_INTERNAL_UNIPHY3_ENUM_ID1         ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+                                                 GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+                                                 ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 << OBJECT_ID_SHIFT)
+
+#define ENCODER_INTERNAL_UNIPHY3_ENUM_ID2         ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+                                                 GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+                                                 ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 << OBJECT_ID_SHIFT)
+
 #define ENCODER_GENERAL_EXTERNAL_DVO_ENUM_ID1    ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
                                                   GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
                                                   ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO << OBJECT_ID_SHIFT)
                                                   GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
                                                   ENCODER_OBJECT_ID_INTERNAL_VCE << OBJECT_ID_SHIFT)
 
+#define ENCODER_HDMI_ANX9805_ENUM_ID1            ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+                                                  GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+                                                  ENCODER_OBJECT_ID_HDMI_ANX9805 << OBJECT_ID_SHIFT)
+
 /****************************************************/
 /* Connector Object ID definition - Shared with BIOS */
 /****************************************************/
                                                  GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\
                                                  CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
 
+#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID5   ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+                                                 GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\
+                                                 CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
+
+#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID6   ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+                                                 GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\
+                                                 CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
+
 #define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1     ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
                                                  GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
                                                  CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
                                                  GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
                                                  CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
 
+#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID4     ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+                                                 GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\
+                                                 CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
+
 #define CONNECTOR_VGA_ENUM_ID1                 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
                                                  GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
                                                  CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT)
                                                  GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
                                                  CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
 
+#define CONNECTOR_HDMI_TYPE_A_ENUM_ID4         ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+                                                 GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\
+                                                 CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
+
+#define CONNECTOR_HDMI_TYPE_A_ENUM_ID5         ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+                                                 GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\
+                                                 CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
+
+#define CONNECTOR_HDMI_TYPE_A_ENUM_ID6         ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+                                                 GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\
+                                                 CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
+
 #define CONNECTOR_HDMI_TYPE_B_ENUM_ID1         ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
                                                  GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
                                                  CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT)
index 0ee5737..16b120c 100644 (file)
@@ -74,6 +74,8 @@
 #define ATOM_PPLL2            1
 #define ATOM_DCPLL            2
 #define ATOM_PPLL0            2
+#define ATOM_PPLL3            3
+
 #define ATOM_EXT_PLL1         8
 #define ATOM_EXT_PLL2         9
 #define ATOM_EXT_CLOCK        10
@@ -259,7 +261,7 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{
   USHORT AdjustDisplayPll;                                                                                      //Atomic Table,  used by various SW componentes. 
   USHORT AdjustMemoryController;                 //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock                
   USHORT EnableASIC_StaticPwrMgt;                //Atomic Table,  only used by Bios
-  USHORT ASIC_StaticPwrMgtStatusChange;          //Obsolete ,     only used by Bios   
+  USHORT SetUniphyInstance;                      //Atomic Table,  only used by Bios   
   USHORT DAC_LoadDetection;                      //Atomic Table,  directly used by various SW components,latest version 1.2  
   USHORT LVTMAEncoderControl;                    //Atomic Table,directly used by various SW components,latest version 1.3
   USHORT HW_Misc_Operation;                      //Atomic Table,  directly used by various SW components,latest version 1.1 
@@ -271,7 +273,7 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{
   USHORT TVEncoderControl;                       //Function Table,directly used by various SW components,latest version 1.1
   USHORT PatchMCSetting;                         //only used by BIOS
   USHORT MC_SEQ_Control;                         //only used by BIOS
-  USHORT TV1OutputControl;                       //Atomic Table,  Obsolete from Ry6xx, use DAC2 Output instead
+  USHORT Gfx_Harvesting;                         //Atomic Table,  Obsolete from Ry6xx, Now only used by BIOS for GFX harvesting
   USHORT EnableScaler;                           //Atomic Table,  used only by Bios
   USHORT BlankCRTC;                              //Atomic Table,  directly used by various SW components,latest version 1.1 
   USHORT EnableCRTC;                             //Atomic Table,  directly used by various SW components,latest version 1.1 
@@ -328,7 +330,7 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{
 #define UNIPHYTransmitterControl                            DIG1TransmitterControl
 #define LVTMATransmitterControl                                     DIG2TransmitterControl
 #define SetCRTC_DPM_State                        GetConditionalGoldenSetting
-#define SetUniphyInstance                        ASIC_StaticPwrMgtStatusChange
+#define ASIC_StaticPwrMgtStatusChange            SetUniphyInstance 
 #define HPDInterruptService                      ReadHWAssistedI2CStatus
 #define EnableVGA_Access                         GetSCLKOverMCLKRatio
 #define EnableYUV                                GetDispObjectInfo                         
@@ -338,7 +340,7 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{
 #define TMDSAEncoderControl                      PatchMCSetting
 #define LVDSEncoderControl                       MC_SEQ_Control
 #define LCD1OutputControl                        HW_Misc_Operation
-
+#define TV1OutputControl                         Gfx_Harvesting
 
 typedef struct _ATOM_MASTER_COMMAND_TABLE
 {
@@ -478,11 +480,11 @@ typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3
 typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4
 {
 #if ATOM_BIG_ENDIAN
-  ULONG  ucPostDiv;          //return parameter: post divider which is used to program to register directly
+  ULONG  ucPostDiv:8;        //return parameter: post divider which is used to program to register directly
   ULONG  ulClock:24;         //Input= target clock, output = actual clock 
 #else
   ULONG  ulClock:24;         //Input= target clock, output = actual clock 
-  ULONG  ucPostDiv;          //return parameter: post divider which is used to program to register directly
+  ULONG  ucPostDiv:8;        //return parameter: post divider which is used to program to register directly
 #endif
 }COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4;
 
@@ -504,6 +506,32 @@ typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5
   UCHAR   ucReserved;                       
 }COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5;
 
+
+typedef struct _COMPUTE_GPU_CLOCK_INPUT_PARAMETERS_V1_6
+{
+  ATOM_COMPUTE_CLOCK_FREQ  ulClock;         //Input Parameter
+  ULONG   ulReserved[2];
+}COMPUTE_GPU_CLOCK_INPUT_PARAMETERS_V1_6;
+
+//ATOM_COMPUTE_CLOCK_FREQ.ulComputeClockFlag
+#define COMPUTE_GPUCLK_INPUT_FLAG_CLK_TYPE_MASK            0x0f
+#define COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK           0x00
+#define COMPUTE_GPUCLK_INPUT_FLAG_SCLK                     0x01
+
+typedef struct _COMPUTE_GPU_CLOCK_OUTPUT_PARAMETERS_V1_6
+{
+  COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4  ulClock;         //Output Parameter: ucPostDiv=DFS divider
+  ATOM_S_MPLL_FB_DIVIDER   ulFbDiv;         //Output Parameter: PLL FB divider
+  UCHAR   ucPllRefDiv;                      //Output Parameter: PLL ref divider      
+  UCHAR   ucPllPostDiv;                     //Output Parameter: PLL post divider      
+  UCHAR   ucPllCntlFlag;                    //Output Flags: control flag
+  UCHAR   ucReserved;                       
+}COMPUTE_GPU_CLOCK_OUTPUT_PARAMETERS_V1_6;
+
+//ucPllCntlFlag
+#define SPLL_CNTL_FLAG_VCO_MODE_MASK            0x03 
+
+
 // ucInputFlag
 #define ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN  1   // 1-StrobeMode, 0-PerformanceMode
 
@@ -1686,6 +1714,7 @@ typedef struct _PIXEL_CLOCK_PARAMETERS_V6
 #define PIXEL_CLOCK_V6_MISC_HDMI_30BPP              0x08
 #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
 
 typedef struct _GET_DISP_PLL_STATUS_INPUT_PARAMETERS_V2
 {
@@ -2102,6 +2131,17 @@ typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V3
 }DVO_ENCODER_CONTROL_PARAMETERS_V3;
 #define DVO_ENCODER_CONTROL_PS_ALLOCATION_V3   DVO_ENCODER_CONTROL_PARAMETERS_V3
 
+typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V1_4
+{
+  USHORT usPixelClock; 
+  UCHAR  ucDVOConfig;
+  UCHAR  ucAction;                                                                                                             //ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT
+  UCHAR  ucBitPerColor;                       //please refer to definition of PANEL_xBIT_PER_COLOR
+  UCHAR  ucReseved[3];
+}DVO_ENCODER_CONTROL_PARAMETERS_V1_4;
+#define DVO_ENCODER_CONTROL_PS_ALLOCATION_V1_4 DVO_ENCODER_CONTROL_PARAMETERS_V1_4
+
+
 //ucTableFormatRevision=1
 //ucTableContentRevision=3 structure is not changed but usMisc add bit 1 as another input for 
 // bit1=0: non-coherent mode
@@ -2165,7 +2205,7 @@ typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V3
 #define SET_ASIC_VOLTAGE_MODE_SOURCE_B         0x4
 
 #define        SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE      0x0
-#define        SET_ASIC_VOLTAGE_MODE_GET_GPIOVAL      0x1      
+#define        SET_ASIC_VOLTAGE_MODE_GET_GPIOVAL      0x1
 #define        SET_ASIC_VOLTAGE_MODE_GET_GPIOMASK     0x2
 
 typedef struct _SET_VOLTAGE_PARAMETERS
@@ -2200,15 +2240,20 @@ typedef struct  _SET_VOLTAGE_PARAMETERS_V1_3
 //SET_VOLTAGE_PARAMETERS_V3.ucVoltageMode
 #define ATOM_SET_VOLTAGE                     0        //Set voltage Level
 #define ATOM_INIT_VOLTAGE_REGULATOR          3        //Init Regulator
-#define ATOM_SET_VOLTAGE_PHASE               4        //Set Vregulator Phase
-#define ATOM_GET_MAX_VOLTAGE                 6        //Get Max Voltage, not used in SetVoltageTable v1.3
-#define ATOM_GET_VOLTAGE_LEVEL               6        //Get Voltage level from vitual voltage ID
+#define ATOM_SET_VOLTAGE_PHASE               4        //Set Vregulator Phase, only for SVID/PVID regulator
+#define ATOM_GET_MAX_VOLTAGE                 6        //Get Max Voltage, not used from SetVoltageTable v1.3
+#define ATOM_GET_VOLTAGE_LEVEL               6        //Get Voltage level from vitual voltage ID, not used for SetVoltage v1.4
+#define ATOM_GET_LEAKAGE_ID                  8        //Get Leakage Voltage Id ( starting from SMU7x IP ), SetVoltage v1.4 
 
 // define vitual voltage id in usVoltageLevel
 #define ATOM_VIRTUAL_VOLTAGE_ID0             0xff01
 #define ATOM_VIRTUAL_VOLTAGE_ID1             0xff02
 #define ATOM_VIRTUAL_VOLTAGE_ID2             0xff03
 #define ATOM_VIRTUAL_VOLTAGE_ID3             0xff04
+#define ATOM_VIRTUAL_VOLTAGE_ID4             0xff05
+#define ATOM_VIRTUAL_VOLTAGE_ID5             0xff06
+#define ATOM_VIRTUAL_VOLTAGE_ID6             0xff07
+#define ATOM_VIRTUAL_VOLTAGE_ID7             0xff08
 
 typedef struct _SET_VOLTAGE_PS_ALLOCATION
 {
@@ -2628,7 +2673,8 @@ typedef struct _ATOM_FIRMWARE_INFO_V2_2
   ULONG                           ulFirmwareRevision;
   ULONG                           ulDefaultEngineClock;       //In 10Khz unit
   ULONG                           ulDefaultMemoryClock;       //In 10Khz unit
-  ULONG                           ulReserved[2];
+  ULONG                           ulSPLL_OutputFreq;          //In 10Khz unit  
+  ULONG                           ulGPUPLL_OutputFreq;        //In 10Khz unit
   ULONG                           ulReserved1;                //Was ulMaxEngineClockPLL_Output; //In 10Khz unit*
   ULONG                           ulReserved2;                //Was ulMaxMemoryClockPLL_Output; //In 10Khz unit*
   ULONG                           ulMaxPixelClockPLL_Output;  //In 10Khz unit
@@ -3813,6 +3859,12 @@ typedef struct _ATOM_GPIO_PIN_ASSIGNMENT
   UCHAR                    ucGPIO_ID;
 }ATOM_GPIO_PIN_ASSIGNMENT;
 
+//ucGPIO_ID pre-define id for multiple usage
+//from SMU7.x, if ucGPIO_ID=PP_AC_DC_SWITCH_GPIO_PINID in GPIO_LUTTable, AC/DC swithing feature is enable
+#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
+
 typedef struct _ATOM_GPIO_PIN_LUT
 {
   ATOM_COMMON_TABLE_HEADER  sHeader;
@@ -4074,17 +4126,19 @@ typedef struct _EXT_DISPLAY_PATH
 
 //usCaps
 #define  EXT_DISPLAY_PATH_CAPS__HBR2_DISABLE          0x01
+#define  EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN        0x02
 
 typedef  struct _ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO
 {
   ATOM_COMMON_TABLE_HEADER sHeader;
   UCHAR                    ucGuid [NUMBER_OF_UCHAR_FOR_GUID];     // a GUID is a 16 byte long string
   EXT_DISPLAY_PATH         sPath[MAX_NUMBER_OF_EXT_DISPLAY_PATH]; // total of fixed 7 entries.
-  UCHAR                    ucChecksum;                            // a  simple Checksum of the sum of whole structure equal to 0x0. 
+  UCHAR                    ucChecksum;                            // a simple Checksum of the sum of whole structure equal to 0x0. 
   UCHAR                    uc3DStereoPinId;                       // use for eDP panel
   UCHAR                    ucRemoteDisplayConfig;
   UCHAR                    uceDPToLVDSRxId;
-  UCHAR                    Reserved[4];                           // for potential expansion
+  UCHAR                    ucFixDPVoltageSwing;                   // usCaps[1]=1, this indicate DP_LANE_SET value
+  UCHAR                    Reserved[3];                           // for potential expansion
 }ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO;
 
 //Related definitions, all records are different but they have a commond header
@@ -4416,6 +4470,13 @@ typedef struct _ATOM_VOLTAGE_CONTROL
 #define        VOLTAGE_CONTROL_ID_CHL822x                                              0x08                                                                    
 #define        VOLTAGE_CONTROL_ID_VT1586M                                              0x09
 #define VOLTAGE_CONTROL_ID_UP1637                                              0x0A
+#define        VOLTAGE_CONTROL_ID_CHL8214            0x0B
+#define        VOLTAGE_CONTROL_ID_UP1801             0x0C
+#define        VOLTAGE_CONTROL_ID_ST6788A            0x0D
+#define VOLTAGE_CONTROL_ID_CHLIR3564SVI2      0x0E
+#define VOLTAGE_CONTROL_ID_AD527x                    0x0F
+#define VOLTAGE_CONTROL_ID_NCP81022                  0x10
+#define VOLTAGE_CONTROL_ID_LTC2635                       0x11
 
 typedef struct  _ATOM_VOLTAGE_OBJECT
 {
@@ -4458,6 +4519,15 @@ typedef struct _ATOM_VOLTAGE_OBJECT_HEADER_V3{
         USHORT         usSize;                                                                                                 //Size of Object        
 }ATOM_VOLTAGE_OBJECT_HEADER_V3;
 
+// ATOM_VOLTAGE_OBJECT_HEADER_V3.ucVoltageMode
+#define VOLTAGE_OBJ_GPIO_LUT                 0        //VOLTAGE and GPIO Lookup table ->ATOM_GPIO_VOLTAGE_OBJECT_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_HIGH1_STATE_LEAKAGE_LUT  0x12     //High1 voltage state Voltage and LeakageId lookup table->ATOM_LEAKAGE_VOLTAGE_OBJECT_V3
+
 typedef struct  _VOLTAGE_LUT_ENTRY_V2
 {
         ULONG          ulVoltageId;                                                                      // The Voltage ID which is used to program GPIO register
@@ -4473,7 +4543,7 @@ typedef struct  _LEAKAGE_VOLTAGE_LUT_ENTRY_V2
 
 typedef struct  _ATOM_I2C_VOLTAGE_OBJECT_V3
 {
-   ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader;
+   ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader;    // voltage mode = VOLTAGE_OBJ_VR_I2C_INIT_SEQ
    UCHAR       ucVoltageRegulatorId;                                     //Indicate Voltage Regulator Id
    UCHAR    ucVoltageControlI2cLine;
    UCHAR    ucVoltageControlAddress;
@@ -4484,7 +4554,7 @@ typedef struct  _ATOM_I2C_VOLTAGE_OBJECT_V3
 
 typedef struct  _ATOM_GPIO_VOLTAGE_OBJECT_V3
 {
-   ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader;   
+   ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader;   // voltage mode = VOLTAGE_OBJ_GPIO_LUT or VOLTAGE_OBJ_PHASE_LUT
    UCHAR    ucVoltageGpioCntlId;         // default is 0 which indicate control through CG VID mode 
    UCHAR    ucGpioEntryNum;              // indiate the entry numbers of Votlage/Gpio value Look up table
    UCHAR    ucPhaseDelay;                // phase delay in unit of micro second
@@ -4495,7 +4565,7 @@ typedef struct  _ATOM_GPIO_VOLTAGE_OBJECT_V3
 
 typedef struct  _ATOM_LEAKAGE_VOLTAGE_OBJECT_V3
 {
-   ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader;
+   ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader;    // voltage mode = 0x10/0x11/0x12
    UCHAR    ucLeakageCntlId;             // default is 0
    UCHAR    ucLeakageEntryNum;           // indicate the entry number of LeakageId/Voltage Lut table
    UCHAR    ucReserved[2];               
@@ -4503,10 +4573,26 @@ typedef struct  _ATOM_LEAKAGE_VOLTAGE_OBJECT_V3
    LEAKAGE_VOLTAGE_LUT_ENTRY_V2 asLeakageIdLut[1];   
 }ATOM_LEAKAGE_VOLTAGE_OBJECT_V3;
 
+
+typedef struct  _ATOM_SVID2_VOLTAGE_OBJECT_V3
+{
+   ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader;    // voltage mode = VOLTAGE_OBJ_SVID2
+// 14:7 \96 PSI0_VID
+// 6 \96 PSI0_EN
+// 5 \96 PSI1
+// 4:2 \96 load line slope trim. 
+// 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];
+   ULONG    ulReserved;
+}ATOM_SVID2_VOLTAGE_OBJECT_V3;
+
 typedef union _ATOM_VOLTAGE_OBJECT_V3{
   ATOM_GPIO_VOLTAGE_OBJECT_V3 asGpioVoltageObj;
   ATOM_I2C_VOLTAGE_OBJECT_V3 asI2cVoltageObj;
   ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 asLeakageObj;
+  ATOM_SVID2_VOLTAGE_OBJECT_V3 asSVID2Obj;
 }ATOM_VOLTAGE_OBJECT_V3;
 
 typedef struct  _ATOM_VOLTAGE_OBJECT_INFO_V3_1
@@ -4536,6 +4622,21 @@ typedef struct  _ATOM_ASIC_PROFILING_INFO
        ATOM_ASIC_PROFILE_VOLTAGE                       asVoltage;
 }ATOM_ASIC_PROFILING_INFO;
 
+typedef struct  _ATOM_ASIC_PROFILING_INFO_V2_1
+{
+  ATOM_COMMON_TABLE_HEADER                     asHeader; 
+  UCHAR  ucLeakageBinNum;                // indicate the entry number of LeakageId/Voltage Lut table
+  USHORT usLeakageBinArrayOffset;        // offset of USHORT Leakage Bin list array ( from lower LeakageId to higher) 
+
+  UCHAR  ucElbVDDC_Num;               
+  USHORT usElbVDDC_IdArrayOffset;        // offset of USHORT virtual VDDC voltage id ( 0xff01~0xff08 )
+  USHORT usElbVDDC_LevelArrayOffset;     // offset of 2 dimension voltage level USHORT array
+
+  UCHAR  ucElbVDDCI_Num;
+  USHORT usElbVDDCI_IdArrayOffset;       // offset of USHORT virtual VDDCI voltage id ( 0xff01~0xff08 )
+  USHORT usElbVDDCI_LevelArrayOffset;    // offset of 2 dimension voltage level USHORT array
+}ATOM_ASIC_PROFILING_INFO_V2_1;
+
 typedef struct _ATOM_POWER_SOURCE_OBJECT
 {
        UCHAR   ucPwrSrcId;                                                                                                     // Power source
@@ -4652,6 +4753,8 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V6
 #define SYS_INFO_LVDSMISC__888_BPC                                                   0x04
 #define SYS_INFO_LVDSMISC__OVERRIDE_EN                                               0x08
 #define SYS_INFO_LVDSMISC__BLON_ACTIVE_LOW                                           0x10
+// new since Trinity
+#define SYS_INFO_LVDSMISC__TRAVIS_LVDS_VOL_OVERRIDE_EN                               0x20
 
 // not used any more
 #define SYS_INFO_LVDSMISC__VSYNC_ACTIVE_LOW                                          0x04
@@ -4752,6 +4855,29 @@ typedef struct _ATOM_FUSION_SYSTEM_INFO_V1
   ATOM_INTEGRATED_SYSTEM_INFO_V6    sIntegratedSysInfo;   
   ULONG  ulPowerplayTable[128];  
 }ATOM_FUSION_SYSTEM_INFO_V1; 
+
+
+typedef struct _ATOM_TDP_CONFIG_BITS
+{
+#if ATOM_BIG_ENDIAN
+  ULONG   uReserved:2;
+  ULONG   uTDP_Value:14;  // Original TDP value in tens of milli watts
+  ULONG   uCTDP_Value:14; // Override value in tens of milli watts
+  ULONG   uCTDP_Enable:2; // = (uCTDP_Value > uTDP_Value? 2: (uCTDP_Value < uTDP_Value))
+#else
+  ULONG   uCTDP_Enable:2; // = (uCTDP_Value > uTDP_Value? 2: (uCTDP_Value < uTDP_Value))
+  ULONG   uCTDP_Value:14; // Override value in tens of milli watts
+  ULONG   uTDP_Value:14;  // Original TDP value in tens of milli watts
+  ULONG   uReserved:2;
+#endif
+}ATOM_TDP_CONFIG_BITS;
+
+typedef union _ATOM_TDP_CONFIG
+{
+  ATOM_TDP_CONFIG_BITS TDP_config;
+  ULONG            TDP_config_all;
+}ATOM_TDP_CONFIG;
+
 /**********************************************************************************************************************
   ATOM_FUSION_SYSTEM_INFO_V1 Description
 sIntegratedSysInfo:               refer to ATOM_INTEGRATED_SYSTEM_INFO_V6 definition.
@@ -4784,7 +4910,8 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7
   UCHAR  ucMemoryType;  
   UCHAR  ucUMAChannelNumber;
   UCHAR  strVBIOSMsg[40];
-  ULONG  ulReserved[20];
+  ATOM_TDP_CONFIG  asTdpConfig;
+  ULONG  ulReserved[19];
   ATOM_AVAILABLE_SCLK_LIST   sAvail_SCLK[5];
   ULONG  ulGMCRestoreResetTime;
   ULONG  ulMinimumNClk;
@@ -4809,7 +4936,7 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7
   USHORT GnbTdpLimit;
   USHORT usMaxLVDSPclkFreqInSingleLink;
   UCHAR  ucLvdsMisc;
-  UCHAR  ucLVDSReserved;
+  UCHAR  ucTravisLVDSVolAdjust;
   UCHAR  ucLVDSPwrOnSeqDIGONtoDE_in4Ms;
   UCHAR  ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms;
   UCHAR  ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms;
@@ -4817,7 +4944,7 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7
   UCHAR  ucLVDSOffToOnDelay_in4Ms;
   UCHAR  ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms;
   UCHAR  ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms;
-  UCHAR  ucLVDSReserved1;
+  UCHAR  ucMinAllowedBL_Level;
   ULONG  ulLCDBitDepthControlVal;
   ULONG  ulNbpStateMemclkFreq[4];
   USHORT usNBP2Voltage;               
@@ -4846,6 +4973,7 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7
 #define SYS_INFO_GPUCAPS__TMDSHDMI_COHERENT_SINGLEPLL_MODE                0x01
 #define SYS_INFO_GPUCAPS__DP_SINGLEPLL_MODE                               0x02
 #define SYS_INFO_GPUCAPS__DISABLE_AUX_MODE_DETECT                         0x08
+#define SYS_INFO_GPUCAPS__ENABEL_DFS_BYPASS                               0x10
 
 /**********************************************************************************************************************
   ATOM_INTEGRATED_SYSTEM_INFO_V1_7 Description
@@ -4945,6 +5073,9 @@ ucLVDSMisc:                       [bit0] LVDS 888bit panel mode =0: LVDS 888 pan
                                   [bit2] LVDS 888bit per color mode  =0: 666 bit per color =1:888 bit per color
                                   [bit3] LVDS parameter override enable  =0: ucLvdsMisc parameter are not used =1: ucLvdsMisc parameter should be used
                                   [bit4] Polarity of signal sent to digital BLON output pin. =0: not inverted(active high) =1: inverted ( active low )
+                                  [bit5] Travid LVDS output voltage override enable, when =1, use ucTravisLVDSVolAdjust value to overwrite Traivs register LVDS_CTRL_4
+ucTravisLVDSVolAdjust             When ucLVDSMisc[5]=1,it means platform SBIOS want to overwrite TravisLVDSVoltage. Then VBIOS will use ucTravisLVDSVolAdjust 
+                                  value to program Travis register LVDS_CTRL_4
 ucLVDSPwrOnSeqDIGONtoDE_in4Ms:    LVDS power up sequence time in unit of 4ms, time delay from DIGON signal active to data enable signal active( DE ).
                                   =0 mean use VBIOS default which is 8 ( 32ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON. 
                                   This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
@@ -4964,18 +5095,241 @@ ucLVDSOffToOnDelay_in4Ms:         LVDS power down sequence time in unit of 4ms.
                                   =0 means to use VBIOS default delay which is 125 ( 500ms ).
                                   This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
 
-ucLVDSPwrOnVARY_BLtoBLON_in4Ms:   LVDS power up sequence time in unit of 4ms. Time delay from VARY_BL signal on to DLON signal active. 
+ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms:
+                                  LVDS power up sequence time in unit of 4ms. Time delay from VARY_BL signal on to DLON signal active. 
                                   =0 means to use VBIOS default delay which is 0 ( 0ms ).
                                   This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
 
-ucLVDSPwrOffBLONtoVARY_BL_in4Ms:  LVDS power down sequence time in unit of 4ms. Time delay from BLON signal off to VARY_BL signal off. 
+ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms:  
+                                  LVDS power down sequence time in unit of 4ms. Time delay from BLON signal off to VARY_BL signal off. 
                                   =0 means to use VBIOS default delay which is 0 ( 0ms ).
                                   This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
 
+ucMinAllowedBL_Level:             Lowest LCD backlight PWM level. This is customer platform specific parameters. By default it is 0. 
+
 ulNbpStateMemclkFreq[4]:          system memory clock frequncey in unit of 10Khz in different NB pstate. 
 
 **********************************************************************************************************************/
 
+// this IntegrateSystemInfoTable is used for Kaveri & Kabini APU
+typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_8
+{
+  ATOM_COMMON_TABLE_HEADER   sHeader;
+  ULONG  ulBootUpEngineClock;
+  ULONG  ulDentistVCOFreq;
+  ULONG  ulBootUpUMAClock;
+  ATOM_CLK_VOLT_CAPABILITY   sDISPCLK_Voltage[4];
+  ULONG  ulBootUpReqDisplayVector;
+  ULONG  ulVBIOSMisc;
+  ULONG  ulGPUCapInfo;
+  ULONG  ulDISP_CLK2Freq;
+  USHORT usRequestedPWMFreqInHz;
+  UCHAR  ucHtcTmpLmt;
+  UCHAR  ucHtcHystLmt;
+  ULONG  ulReserved2;
+  ULONG  ulSystemConfig;            
+  ULONG  ulCPUCapInfo;
+  ULONG  ulReserved3;
+  USHORT usGPUReservedSysMemSize;
+  USHORT usExtDispConnInfoOffset;
+  USHORT usPanelRefreshRateRange;     
+  UCHAR  ucMemoryType;  
+  UCHAR  ucUMAChannelNumber;
+  UCHAR  strVBIOSMsg[40];
+  ATOM_TDP_CONFIG  asTdpConfig;
+  ULONG  ulReserved[19];
+  ATOM_AVAILABLE_SCLK_LIST   sAvail_SCLK[5];
+  ULONG  ulGMCRestoreResetTime;
+  ULONG  ulReserved4;
+  ULONG  ulIdleNClk;
+  ULONG  ulDDR_DLL_PowerUpTime;
+  ULONG  ulDDR_PLL_PowerUpTime;
+  USHORT usPCIEClkSSPercentage;
+  USHORT usPCIEClkSSType;
+  USHORT usLvdsSSPercentage;
+  USHORT usLvdsSSpreadRateIn10Hz;
+  USHORT usHDMISSPercentage;
+  USHORT usHDMISSpreadRateIn10Hz;
+  USHORT usDVISSPercentage;
+  USHORT usDVISSpreadRateIn10Hz;
+  ULONG  ulGPUReservedSysMemBaseAddrLo;
+  ULONG  ulGPUReservedSysMemBaseAddrHi;
+  ULONG  ulReserved5[3];
+  USHORT usMaxLVDSPclkFreqInSingleLink;
+  UCHAR  ucLvdsMisc;
+  UCHAR  ucTravisLVDSVolAdjust;
+  UCHAR  ucLVDSPwrOnSeqDIGONtoDE_in4Ms;
+  UCHAR  ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms;
+  UCHAR  ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms;
+  UCHAR  ucLVDSPwrOffSeqDEtoDIGON_in4Ms;
+  UCHAR  ucLVDSOffToOnDelay_in4Ms;
+  UCHAR  ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms;
+  UCHAR  ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms;
+  UCHAR  ucMinAllowedBL_Level;
+  ULONG  ulLCDBitDepthControlVal;
+  ULONG  ulNbpStateMemclkFreq[4];
+  ULONG  ulReserved6;               
+  ULONG  ulNbpStateNClkFreq[4];
+  USHORT usNBPStateVoltage[4];            
+  USHORT usBootUpNBVoltage;   
+  USHORT usReserved2; 
+  ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO sExtDispConnInfo;
+}ATOM_INTEGRATED_SYSTEM_INFO_V1_8;
+
+/**********************************************************************************************************************
+  ATOM_INTEGRATED_SYSTEM_INFO_V1_8 Description
+ulBootUpEngineClock:              VBIOS bootup Engine clock frequency, in 10kHz unit. if it is equal 0, then VBIOS use pre-defined bootup engine clock
+ulDentistVCOFreq:                 Dentist VCO clock in 10kHz unit. 
+ulBootUpUMAClock:                 System memory boot up clock frequency in 10Khz unit. 
+sDISPCLK_Voltage:                 Report Display clock frequency requirement on GNB voltage(up to 4 voltage levels).
+ulBootUpReqDisplayVector:         VBIOS boot up display IDs, following are supported devices in Trinity projects:
+                                  ATOM_DEVICE_CRT1_SUPPORT                  0x0001
+                                  ATOM_DEVICE_DFP1_SUPPORT                  0x0008
+                                  ATOM_DEVICE_DFP6_SUPPORT                  0x0040
+                                  ATOM_DEVICE_DFP2_SUPPORT                  0x0080
+                                  ATOM_DEVICE_DFP3_SUPPORT                  0x0200
+                                  ATOM_DEVICE_DFP4_SUPPORT                  0x0400
+                                  ATOM_DEVICE_DFP5_SUPPORT                  0x0800
+                                  ATOM_DEVICE_LCD1_SUPPORT                  0x0002
+
+ulVBIOSMisc:                         Miscellenous flags for VBIOS requirement and interface 
+                                  bit[0]=0: INT15 callback function Get LCD EDID ( ax=4e08, bl=1b ) is not supported by SBIOS. 
+                                        =1: INT15 callback function Get LCD EDID ( ax=4e08, bl=1b ) is supported by SBIOS.
+                                  bit[1]=0: INT15 callback function Get boot display( ax=4e08, bl=01h) is not supported by SBIOS
+                                        =1: INT15 callback function Get boot display( ax=4e08, bl=01h) is supported by SBIOS
+                                  bit[2]=0: INT15 callback function Get panel Expansion ( ax=4e08, bl=02h) is not supported by SBIOS
+                                        =1: INT15 callback function Get panel Expansion ( ax=4e08, bl=02h) is supported by SBIOS
+                                  bit[3]=0: VBIOS fast boot is disable
+                                        =1: VBIOS fast boot is enable. ( VBIOS skip display device detection in every set mode if LCD panel is connect and LID is open)
+
+ulGPUCapInfo:                     bit[0~2]= Reserved
+                                  bit[3]=0: Enable AUX HW mode detection logic
+                                        =1: Disable AUX HW mode detection logic
+                                  bit[4]=0: Disable DFS bypass feature
+                                        =1: Enable DFS bypass feature
+
+usRequestedPWMFreqInHz:           When it's set to 0x0 by SBIOS: the LCD BackLight is not controlled by GPU(SW). 
+                                  Any attempt to change BL using VBIOS function or enable VariBri from PP table is not effective since ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==0;
+                                  
+                                  When it's set to a non-zero frequency, the BackLight is controlled by GPU (SW) in one of two ways below:
+                                  1. SW uses the GPU BL PWM output to control the BL, in chis case, this non-zero frequency determines what freq GPU should use;
+                                  VBIOS will set up proper PWM frequency and ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1,as the result,
+                                  Changing BL using VBIOS function is functional in both driver and non-driver present environment; 
+                                  and enabling VariBri under the driver environment from PP table is optional.
+
+                                  2. SW uses other means to control BL (like DPCD),this non-zero frequency serves as a flag only indicating
+                                  that BL control from GPU is expected.
+                                  VBIOS will NOT set up PWM frequency but make ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1
+                                  Changing BL using VBIOS function could be functional in both driver and non-driver present environment,but
+                                  it's per platform 
+                                  and enabling VariBri under the driver environment from PP table is optional.
+
+ucHtcTmpLmt:                      Refer to D18F3x64 bit[22:16], HtcTmpLmt. Threshold on value to enter HTC_active state.
+ucHtcHystLmt:                     Refer to D18F3x64 bit[27:24], HtcHystLmt. 
+                                  To calculate threshold off value to exit HTC_active state, which is Threshold on vlaue minus ucHtcHystLmt.
+
+ulSystemConfig:                   Bit[0]=0: PCIE Power Gating Disabled 
+                                        =1: PCIE Power Gating Enabled
+                                  Bit[1]=0: DDR-DLL shut-down feature disabled.
+                                         1: DDR-DLL shut-down feature enabled.
+                                  Bit[2]=0: DDR-PLL Power down feature disabled.
+                                         1: DDR-PLL Power down feature enabled. 
+                                  Bit[3]=0: GNB DPM is disabled
+                                        =1: GNB DPM is enabled                                
+ulCPUCapInfo:                     TBD
+
+usExtDispConnInfoOffset:          Offset to sExtDispConnInfo inside the structure
+usPanelRefreshRateRange:          Bit vector for LCD supported refresh rate range. If DRR is requestd by the platform, at least two bits need to be set
+                                  to indicate a range.
+                                  SUPPORTED_LCD_REFRESHRATE_30Hz          0x0004
+                                  SUPPORTED_LCD_REFRESHRATE_40Hz          0x0008
+                                  SUPPORTED_LCD_REFRESHRATE_50Hz          0x0010
+                                  SUPPORTED_LCD_REFRESHRATE_60Hz          0x0020
+
+ucMemoryType:                     [3:0]=1:DDR1;=2:DDR2;=3:DDR3;=5:GDDR5; [7:4] is reserved.
+ucUMAChannelNumber:                    System memory channel numbers. 
+
+strVBIOSMsg[40]:                  VBIOS boot up customized message string 
+
+sAvail_SCLK[5]:                   Arrays to provide availabe list of SLCK and corresponding voltage, order from low to high  
+
+ulGMCRestoreResetTime:            GMC power restore and GMC reset time to calculate data reconnection latency. Unit in ns. 
+ulIdleNClk:                       NCLK speed while memory runs in self-refresh state, used to calculate self-refresh latency. Unit in 10kHz.
+ulDDR_DLL_PowerUpTime:            DDR PHY DLL power up time. Unit in ns.
+ulDDR_PLL_PowerUpTime:            DDR PHY PLL power up time. Unit in ns.
+
+usPCIEClkSSPercentage:            PCIE Clock Spread Spectrum Percentage in unit 0.01%; 100 mean 1%.
+usPCIEClkSSType:                  PCIE Clock Spread Spectrum Type. 0 for Down spread(default); 1 for Center spread.
+usLvdsSSPercentage:               LVDS panel ( not include eDP ) Spread Spectrum Percentage in unit of 0.01%, =0, use VBIOS default setting. 
+usLvdsSSpreadRateIn10Hz:          LVDS panel ( not include eDP ) Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. 
+usHDMISSPercentage:               HDMI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%,  =0, use VBIOS default setting. 
+usHDMISSpreadRateIn10Hz:          HDMI Spread Spectrum frequency in unit of 10Hz,  =0, use VBIOS default setting. 
+usDVISSPercentage:                DVI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%,  =0, use VBIOS default setting. 
+usDVISSpreadRateIn10Hz:           DVI Spread Spectrum frequency in unit of 10Hz,  =0, use VBIOS default setting. 
+
+usGPUReservedSysMemSize:          Reserved system memory size for ACP engine in APU GNB, units in MB. 0/2/4MB based on CMOS options, current default could be 0MB. KV only, not on KB.
+ulGPUReservedSysMemBaseAddrLo:    Low 32 bits base address to the reserved system memory. 
+ulGPUReservedSysMemBaseAddrHi:    High 32 bits base address to the reserved system memory. 
+
+usMaxLVDSPclkFreqInSingleLink:    Max pixel clock LVDS panel single link, if=0 means VBIOS use default threhold, right now it is 85Mhz
+ucLVDSMisc:                       [bit0] LVDS 888bit panel mode =0: LVDS 888 panel in LDI mode, =1: LVDS 888 panel in FPDI mode
+                                  [bit1] LVDS panel lower and upper link mapping =0: lower link and upper link not swap, =1: lower link and upper link are swapped
+                                  [bit2] LVDS 888bit per color mode  =0: 666 bit per color =1:888 bit per color
+                                  [bit3] LVDS parameter override enable  =0: ucLvdsMisc parameter are not used =1: ucLvdsMisc parameter should be used
+                                  [bit4] Polarity of signal sent to digital BLON output pin. =0: not inverted(active high) =1: inverted ( active low )
+                                  [bit5] Travid LVDS output voltage override enable, when =1, use ucTravisLVDSVolAdjust value to overwrite Traivs register LVDS_CTRL_4
+ucTravisLVDSVolAdjust             When ucLVDSMisc[5]=1,it means platform SBIOS want to overwrite TravisLVDSVoltage. Then VBIOS will use ucTravisLVDSVolAdjust 
+                                  value to program Travis register LVDS_CTRL_4
+ucLVDSPwrOnSeqDIGONtoDE_in4Ms:    
+                                  LVDS power up sequence time in unit of 4ms, time delay from DIGON signal active to data enable signal active( DE ).
+                                  =0 mean use VBIOS default which is 8 ( 32ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON. 
+                                  This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+ucLVDSPwrOnDEtoVARY_BL_in4Ms:     
+                                  LVDS power up sequence time in unit of 4ms., time delay from DE( data enable ) active to Vary Brightness enable signal active( VARY_BL ).  
+                                  =0 mean use VBIOS default which is 90 ( 360ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON. 
+                                  This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+ucLVDSPwrOffVARY_BLtoDE_in4Ms:    
+                                  LVDS power down sequence time in unit of 4ms, time delay from data enable ( DE ) signal off to LCDVCC (DIGON) off. 
+                                  =0 mean use VBIOS default delay which is 8 ( 32ms ). The LVDS power down sequence is as following: BLON->VARY_BL->DE->DIGON
+                                  This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+ucLVDSPwrOffDEtoDIGON_in4Ms:      
+                                   LVDS power down sequence time in unit of 4ms, time delay from vary brightness enable signal( VARY_BL) off to data enable ( DE ) signal off. 
+                                  =0 mean use VBIOS default which is 90 ( 360ms ). The LVDS power down sequence is as following: BLON->VARY_BL->DE->DIGON
+                                  This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+ucLVDSOffToOnDelay_in4Ms:         
+                                  LVDS power down sequence time in unit of 4ms. Time delay from DIGON signal off to DIGON signal active. 
+                                  =0 means to use VBIOS default delay which is 125 ( 500ms ).
+                                  This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms:
+                                  LVDS power up sequence time in unit of 4ms. Time delay from VARY_BL signal on to DLON signal active. 
+                                  =0 means to use VBIOS default delay which is 0 ( 0ms ).
+                                  This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+
+ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms:  
+                                  LVDS power down sequence time in unit of 4ms. Time delay from BLON signal off to VARY_BL signal off. 
+                                  =0 means to use VBIOS default delay which is 0 ( 0ms ).
+                                  This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+ucMinAllowedBL_Level:             Lowest LCD backlight PWM level. This is customer platform specific parameters. By default it is 0. 
+
+ulLCDBitDepthControlVal:          GPU display control encoder bit dither control setting, used to program register mmFMT_BIT_DEPTH_CONTROL
+
+ulNbpStateMemclkFreq[4]:          system memory clock frequncey in unit of 10Khz in different NB P-State(P0, P1, P2 & P3).
+ulNbpStateNClkFreq[4]:            NB P-State NClk frequency in different NB P-State
+usNBPStateVoltage[4]:             NB P-State (P0/P1 & P2/P3) voltage; NBP3 refers to lowes voltage
+usBootUpNBVoltage:                NB P-State voltage during boot up before driver loaded 
+sExtDispConnInfo:                 Display connector information table provided to VBIOS
+
+**********************************************************************************************************************/
+
+// this Table is used for Kaveri/Kabini APU
+typedef struct _ATOM_FUSION_SYSTEM_INFO_V2
+{
+  ATOM_INTEGRATED_SYSTEM_INFO_V1_8    sIntegratedSysInfo;       // refer to ATOM_INTEGRATED_SYSTEM_INFO_V1_8 definition
+  ULONG                               ulPowerplayTable[128];    // Update comments here to link new powerplay table definition structure
+}ATOM_FUSION_SYSTEM_INFO_V2; 
+
+
 /**************************************************************************/
 // This portion is only used when ext thermal chip or engine/memory clock SS chip is populated on a design
 //Memory SS Info Table
@@ -5026,22 +5380,24 @@ typedef struct _ATOM_ASIC_SS_ASSIGNMENT
 
 //Define ucClockIndication, SW uses the IDs below to search if the SS is required/enabled on a clock branch/signal type.
 //SS is not required or enabled if a match is not found.
-#define ASIC_INTERNAL_MEMORY_SS                        1
-#define ASIC_INTERNAL_ENGINE_SS                        2
-#define ASIC_INTERNAL_UVD_SS        3
-#define ASIC_INTERNAL_SS_ON_TMDS    4
-#define ASIC_INTERNAL_SS_ON_HDMI    5
-#define ASIC_INTERNAL_SS_ON_LVDS    6
-#define ASIC_INTERNAL_SS_ON_DP      7
-#define ASIC_INTERNAL_SS_ON_DCPLL   8
-#define ASIC_EXTERNAL_SS_ON_DP_CLOCK 9
-#define ASIC_INTERNAL_VCE_SS        10
+#define ASIC_INTERNAL_MEMORY_SS                 1
+#define ASIC_INTERNAL_ENGINE_SS                 2
+#define ASIC_INTERNAL_UVD_SS             3
+#define ASIC_INTERNAL_SS_ON_TMDS         4
+#define ASIC_INTERNAL_SS_ON_HDMI         5
+#define ASIC_INTERNAL_SS_ON_LVDS         6
+#define ASIC_INTERNAL_SS_ON_DP           7
+#define ASIC_INTERNAL_SS_ON_DCPLL        8
+#define ASIC_EXTERNAL_SS_ON_DP_CLOCK     9
+#define ASIC_INTERNAL_VCE_SS             10
+#define ASIC_INTERNAL_GPUPLL_SS          11
+
 
 typedef struct _ATOM_ASIC_SS_ASSIGNMENT_V2
 {
        ULONG                                                           ulTargetClockRange;                                             //For mem/engine/uvd, Clock Out frequence (VCO ), in unit of 10Khz
                                                     //For TMDS/HDMI/LVDS, it is pixel clock , for DP, it is link clock ( 27000 or 16200 )
-  USHORT              usSpreadSpectrumPercentage;              //in unit of 0.01%
+  USHORT              usSpreadSpectrumPercentage;              //in unit of 0.01% or 0.001%, decided by ucSpreadSpectrumMode bit4
        USHORT                                                  usSpreadRateIn10Hz;                                             //in unit of 10Hz, modulation freq
   UCHAR               ucClockIndication;                                         //Indicate which clock source needs SS
        UCHAR                                                           ucSpreadSpectrumMode;                                   //Bit0=0 Down Spread,=1 Center Spread, bit1=0: internal SS bit1=1: external SS
@@ -5079,6 +5435,11 @@ typedef struct _ATOM_ASIC_SS_ASSIGNMENT_V3
        UCHAR                                                           ucReserved[2];
 }ATOM_ASIC_SS_ASSIGNMENT_V3;
 
+//ATOM_ASIC_SS_ASSIGNMENT_V3.ucSpreadSpectrumMode
+#define SS_MODE_V3_CENTRE_SPREAD_MASK             0x01
+#define SS_MODE_V3_EXTERNAL_SS_MASK               0x02
+#define SS_MODE_V3_PERCENTAGE_DIV_BY_1000_MASK    0x10
+
 typedef struct _ATOM_ASIC_INTERNAL_SS_INFO_V3
 {
   ATOM_COMMON_TABLE_HEADER           sHeader; 
@@ -5719,6 +6080,7 @@ typedef struct _INDIRECT_IO_ACCESS
 #define INDIRECT_IO_PCIE           3
 #define INDIRECT_IO_PCIEP          4
 #define INDIRECT_IO_NBMISC         5
+#define INDIRECT_IO_SMU            5
 
 #define INDIRECT_IO_PLL_READ       INDIRECT_IO_PLL   | INDIRECT_READ
 #define INDIRECT_IO_PLL_WRITE      INDIRECT_IO_PLL   | INDIRECT_WRITE
@@ -5730,6 +6092,8 @@ typedef struct _INDIRECT_IO_ACCESS
 #define INDIRECT_IO_PCIEP_WRITE    INDIRECT_IO_PCIEP | INDIRECT_WRITE
 #define INDIRECT_IO_NBMISC_READ    INDIRECT_IO_NBMISC | INDIRECT_READ
 #define INDIRECT_IO_NBMISC_WRITE   INDIRECT_IO_NBMISC | INDIRECT_WRITE
+#define INDIRECT_IO_SMU_READ       INDIRECT_IO_SMU | INDIRECT_READ
+#define INDIRECT_IO_SMU_WRITE      INDIRECT_IO_SMU | INDIRECT_WRITE
 
 typedef struct _ATOM_OEM_INFO
 { 
@@ -5875,6 +6239,7 @@ typedef struct _ATOM_MC_INIT_PARAM_TABLE
 #define _64Mx32             0x43
 #define _128Mx8             0x51
 #define _128Mx16            0x52
+#define _128Mx32            0x53
 #define _256Mx8             0x61
 #define _256Mx16            0x62
 
@@ -5893,6 +6258,8 @@ typedef struct _ATOM_MC_INIT_PARAM_TABLE
 #define PROMOS              MOSEL
 #define KRETON              INFINEON
 #define ELIXIR              NANYA
+#define MEZZA               ELPIDA
+
 
 /////////////Support for GDDR5 MC uCode to reside in upper 64K of ROM/////////////
 
@@ -6625,6 +6992,10 @@ typedef struct _ATOM_DISP_OUT_INFO_V3
        ASIC_TRANSMITTER_INFO_V2  asTransmitterInfo[1];     // for alligment only
 }ATOM_DISP_OUT_INFO_V3;
 
+//ucDispCaps
+#define DISPLAY_CAPS__DP_PCLK_FROM_PPLL        0x01
+#define DISPLAY_CAPS__FORCE_DISPDEV_CONNECTED  0x02
+
 typedef enum CORE_REF_CLK_SOURCE{
   CLOCK_SRC_XTALIN=0,
   CLOCK_SRC_XO_IN=1,
@@ -6829,6 +7200,17 @@ typedef struct _DIG_TRANSMITTER_INFO_HEADER_V3_1{
   USHORT usPhyPllSettingOffset;          // offset of CLOCK_CONDITION_SETTING_ENTRY* with Phy Pll Settings
 }DIG_TRANSMITTER_INFO_HEADER_V3_1;
 
+typedef struct _DIG_TRANSMITTER_INFO_HEADER_V3_2{  
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  USHORT usDPVsPreEmphSettingOffset;     // offset of PHY_ANALOG_SETTING_INFO * with DP Voltage Swing and Pre-Emphasis for each Link clock 
+  USHORT usPhyAnalogRegListOffset;       // offset of CLOCK_CONDITION_REGESTER_INFO* with None-DP mode Analog Setting's register Info 
+  USHORT usPhyAnalogSettingOffset;       // offset of CLOCK_CONDITION_SETTING_ENTRY* with None-DP mode Analog Setting for each link clock range
+  USHORT usPhyPllRegListOffset;          // offset of CLOCK_CONDITION_REGESTER_INFO* with Phy Pll register Info 
+  USHORT usPhyPllSettingOffset;          // offset of CLOCK_CONDITION_SETTING_ENTRY* with Phy Pll Settings
+  USHORT usDPSSRegListOffset;            // offset of CLOCK_CONDITION_REGESTER_INFO* with Phy SS Pll register Info 
+  USHORT usDPSSSettingOffset;            // offset of CLOCK_CONDITION_SETTING_ENTRY* with Phy SS Pll Settings
+}DIG_TRANSMITTER_INFO_HEADER_V3_2;
+
 typedef struct _CLOCK_CONDITION_REGESTER_INFO{
   USHORT usRegisterIndex;
   UCHAR  ucStartBit;
@@ -6852,12 +7234,24 @@ typedef struct _PHY_CONDITION_REG_VAL{
   ULONG  ulRegVal;
 }PHY_CONDITION_REG_VAL;
 
+typedef struct _PHY_CONDITION_REG_VAL_V2{
+  ULONG  ulCondition;
+  UCHAR  ucCondition2;
+  ULONG  ulRegVal;
+}PHY_CONDITION_REG_VAL_V2;
+
 typedef struct _PHY_CONDITION_REG_INFO{
   USHORT usRegIndex;
   USHORT usSize;
   PHY_CONDITION_REG_VAL asRegVal[1];
 }PHY_CONDITION_REG_INFO;
 
+typedef struct _PHY_CONDITION_REG_INFO_V2{
+  USHORT usRegIndex;
+  USHORT usSize;
+  PHY_CONDITION_REG_VAL_V2 asRegVal[1];
+}PHY_CONDITION_REG_INFO_V2;
+
 typedef struct _PHY_ANALOG_SETTING_INFO{
   UCHAR  ucEncodeMode;
   UCHAR  ucPhySel;
@@ -6865,6 +7259,25 @@ typedef struct _PHY_ANALOG_SETTING_INFO{
   PHY_CONDITION_REG_INFO  asAnalogSetting[1];
 }PHY_ANALOG_SETTING_INFO;
 
+typedef struct _PHY_ANALOG_SETTING_INFO_V2{
+  UCHAR  ucEncodeMode;
+  UCHAR  ucPhySel;
+  USHORT usSize;
+  PHY_CONDITION_REG_INFO_V2  asAnalogSetting[1];
+}PHY_ANALOG_SETTING_INFO_V2;
+
+typedef struct _GFX_HAVESTING_PARAMETERS {
+  UCHAR ucGfxBlkId;                        //GFX blk id to be harvested, like CU, RB or PRIM
+  UCHAR ucReserved;                        //reserved 
+  UCHAR ucActiveUnitNumPerSH;              //requested active CU/RB/PRIM number per shader array
+  UCHAR ucMaxUnitNumPerSH;                 //max CU/RB/PRIM number per shader array   
+} GFX_HAVESTING_PARAMETERS;
+
+//ucGfxBlkId
+#define GFX_HARVESTING_CU_ID               0
+#define GFX_HARVESTING_RB_ID               1
+#define GFX_HARVESTING_PRIM_ID             2
+
 /****************************************************************************/ 
 //Portion VI: Definitinos for vbios MC scratch registers that driver used
 /****************************************************************************/
@@ -6875,8 +7288,17 @@ typedef struct _PHY_ANALOG_SETTING_INFO{
 #define MC_MISC0__MEMORY_TYPE__GDDR3  0x30000000
 #define MC_MISC0__MEMORY_TYPE__GDDR4  0x40000000
 #define MC_MISC0__MEMORY_TYPE__GDDR5  0x50000000
+#define MC_MISC0__MEMORY_TYPE__HBM    0x60000000
 #define MC_MISC0__MEMORY_TYPE__DDR3   0xB0000000
 
+#define ATOM_MEM_TYPE_DDR_STRING      "DDR"
+#define ATOM_MEM_TYPE_DDR2_STRING     "DDR2"
+#define ATOM_MEM_TYPE_GDDR3_STRING    "GDDR3"
+#define ATOM_MEM_TYPE_GDDR4_STRING    "GDDR4"
+#define ATOM_MEM_TYPE_GDDR5_STRING    "GDDR5"
+#define ATOM_MEM_TYPE_HBM_STRING      "HBM"
+#define ATOM_MEM_TYPE_DDR3_STRING     "DDR3"
+
 /****************************************************************************/ 
 //Portion VI: Definitinos being oboselete
 /****************************************************************************/
@@ -7274,6 +7696,7 @@ typedef struct _ATOM_PPLIB_THERMALCONTROLLER
 #define ATOM_PP_THERMALCONTROLLER_NISLANDS  15
 #define ATOM_PP_THERMALCONTROLLER_SISLANDS  16
 #define ATOM_PP_THERMALCONTROLLER_LM96163   17
+#define ATOM_PP_THERMALCONTROLLER_CISLANDS  18
 
 // Thermal controller 'combo type' to use an external controller for Fan control and an internal controller for thermal.
 // We probably should reserve the bit 0x80 for this use.
@@ -7316,6 +7739,8 @@ typedef struct _ATOM_PPLIB_EXTENDEDHEADER
     // Add extra system parameters here, always adjust size to include all fields.
     USHORT  usVCETableOffset; //points to ATOM_PPLIB_VCE_Table
     USHORT  usUVDTableOffset;   //points to ATOM_PPLIB_UVD_Table
+    USHORT  usSAMUTableOffset;  //points to ATOM_PPLIB_SAMU_Table
+    USHORT  usPPMTableOffset;   //points to ATOM_PPLIB_PPM_Table
 } ATOM_PPLIB_EXTENDEDHEADER;
 
 //// ATOM_PPLIB_POWERPLAYTABLE::ulPlatformCaps
@@ -7337,7 +7762,10 @@ typedef struct _ATOM_PPLIB_EXTENDEDHEADER
 #define ATOM_PP_PLATFORM_CAP_VDDCI_CONTROL 0x8000                   // Does the driver control VDDCI independently from VDDC.
 #define ATOM_PP_PLATFORM_CAP_REGULATOR_HOT 0x00010000               // Enable the 'regulator hot' feature.
 #define ATOM_PP_PLATFORM_CAP_BACO          0x00020000               // Does the driver supports BACO state.
-
+#define ATOM_PP_PLATFORM_CAP_NEW_CAC_VOLTAGE   0x00040000           // Does the driver supports new CAC voltage table.
+#define ATOM_PP_PLATFORM_CAP_REVERT_GPIO5_POLARITY   0x00080000     // Does the driver supports revert GPIO5 polarity.
+#define ATOM_PP_PLATFORM_CAP_OUTPUT_THERMAL2GPIO17   0x00100000     // Does the driver supports thermal2GPIO17.
+#define ATOM_PP_PLATFORM_CAP_VRHOT_GPIO_CONFIGURABLE   0x00200000   // Does the driver supports VR HOT GPIO Configurable.
 
 typedef struct _ATOM_PPLIB_POWERPLAYTABLE
 {
@@ -7398,7 +7826,7 @@ typedef struct _ATOM_PPLIB_POWERPLAYTABLE4
     USHORT                     usVddcDependencyOnMCLKOffset;
     USHORT                     usMaxClockVoltageOnDCOffset;
     USHORT                     usVddcPhaseShedLimitsTableOffset;    // Points to ATOM_PPLIB_PhaseSheddingLimits_Table
-    USHORT                     usReserved;  
+    USHORT                     usMvddDependencyOnMCLKOffset;  
 } ATOM_PPLIB_POWERPLAYTABLE4, *LPATOM_PPLIB_POWERPLAYTABLE4;
 
 typedef struct _ATOM_PPLIB_POWERPLAYTABLE5
@@ -7563,6 +7991,17 @@ typedef struct _ATOM_PPLIB_SI_CLOCK_INFO
 
 } ATOM_PPLIB_SI_CLOCK_INFO;
 
+typedef struct _ATOM_PPLIB_CI_CLOCK_INFO
+{
+      USHORT usEngineClockLow;
+      UCHAR  ucEngineClockHigh;
+
+      USHORT usMemoryClockLow;
+      UCHAR  ucMemoryClockHigh;
+      
+      UCHAR  ucPCIEGen;
+      USHORT usPCIELane;
+} ATOM_PPLIB_CI_CLOCK_INFO;
 
 typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
 
@@ -7680,8 +8119,8 @@ typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Table
 
 typedef struct _ATOM_PPLIB_CAC_Leakage_Record
 {
-    USHORT usVddc;  // We use this field for the "fake" standardized VDDC for power calculations                                                  
-    ULONG  ulLeakageValue;
+    USHORT usVddc;  // We use this field for the "fake" standardized VDDC for power calculations; For CI and newer, we use this as the real VDDC value.
+    ULONG  ulLeakageValue;  // For CI and newer we use this as the "fake" standar VDDC value.
 }ATOM_PPLIB_CAC_Leakage_Record;
 
 typedef struct _ATOM_PPLIB_CAC_Leakage_Table
@@ -7796,6 +8235,42 @@ typedef struct _ATOM_PPLIB_UVD_Table
 //    ATOM_PPLIB_UVD_State_Table states;
 }ATOM_PPLIB_UVD_Table;
 
+
+typedef struct _ATOM_PPLIB_SAMClk_Voltage_Limit_Record
+{
+      USHORT usVoltage;
+      USHORT usSAMClockLow;
+      UCHAR  ucSAMClockHigh;
+}ATOM_PPLIB_SAMClk_Voltage_Limit_Record;
+
+typedef struct _ATOM_PPLIB_SAMClk_Voltage_Limit_Table{
+    UCHAR numEntries;
+    ATOM_PPLIB_SAMClk_Voltage_Limit_Record entries[1];
+}ATOM_PPLIB_SAMClk_Voltage_Limit_Table;
+
+typedef struct _ATOM_PPLIB_SAMU_Table
+{
+      UCHAR revid;
+      ATOM_PPLIB_SAMClk_Voltage_Limit_Table limits;
+}ATOM_PPLIB_SAMU_Table;
+
+#define ATOM_PPM_A_A    1
+#define ATOM_PPM_A_I    2
+typedef struct _ATOM_PPLIB_PPM_Table
+{
+      UCHAR  ucRevId;
+      UCHAR  ucPpmDesign;          //A+I or A+A
+      USHORT usCpuCoreNumber;
+      ULONG  ulPlatformTDP;
+      ULONG  ulSmallACPlatformTDP;
+      ULONG  ulPlatformTDC;
+      ULONG  ulSmallACPlatformTDC;
+      ULONG  ulApuTDP;
+      ULONG  ulDGpuTDP;  
+      ULONG  ulDGpuUlvPower;
+      ULONG  ulTjmax;
+} ATOM_PPLIB_PPM_Table;
+
 /**************************************************************************/
 
 
index d5df8fd..b9d3b43 100644 (file)
@@ -555,7 +555,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                if (rdev->family < CHIP_RV770)
                        radeon_crtc->pll_flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP;
                /* use frac fb div on APUs */
-               if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev))
+               if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev) || ASIC_IS_DCE8(rdev))
                        radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV;
                /* use frac fb div on RS780/RS880 */
                if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880))
@@ -743,7 +743,7 @@ static void atombios_crtc_set_disp_eng_pll(struct radeon_device *rdev,
                         * SetPixelClock provides the dividers
                         */
                        args.v6.ulDispEngClkFreq = cpu_to_le32(dispclk);
-                       if (ASIC_IS_DCE61(rdev))
+                       if (ASIC_IS_DCE61(rdev) || ASIC_IS_DCE8(rdev))
                                args.v6.ucPpll = ATOM_EXT_PLL1;
                        else if (ASIC_IS_DCE6(rdev))
                                args.v6.ucPpll = ATOM_PPLL0;
@@ -1143,7 +1143,9 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
        }
 
        if (tiling_flags & RADEON_TILING_MACRO) {
-               if (rdev->family >= CHIP_TAHITI)
+               if (rdev->family >= CHIP_BONAIRE)
+                       tmp = rdev->config.cik.tile_config;
+               else if (rdev->family >= CHIP_TAHITI)
                        tmp = rdev->config.si.tile_config;
                else if (rdev->family >= CHIP_CAYMAN)
                        tmp = rdev->config.cayman.tile_config;
@@ -1170,11 +1172,29 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
                fb_format |= EVERGREEN_GRPH_BANK_WIDTH(bankw);
                fb_format |= EVERGREEN_GRPH_BANK_HEIGHT(bankh);
                fb_format |= EVERGREEN_GRPH_MACRO_TILE_ASPECT(mtaspect);
+               if (rdev->family >= CHIP_BONAIRE) {
+                       /* XXX need to know more about the surface tiling mode */
+                       fb_format |= CIK_GRPH_MICRO_TILE_MODE(CIK_DISPLAY_MICRO_TILING);
+               }
        } else if (tiling_flags & RADEON_TILING_MICRO)
                fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_1D_TILED_THIN1);
 
-       if ((rdev->family == CHIP_TAHITI) ||
-           (rdev->family == CHIP_PITCAIRN))
+       if (rdev->family >= CHIP_BONAIRE) {
+               u32 num_pipe_configs = rdev->config.cik.max_tile_pipes;
+               u32 num_rb = rdev->config.cik.max_backends_per_se;
+               if (num_pipe_configs > 8)
+                       num_pipe_configs = 8;
+               if (num_pipe_configs == 8)
+                       fb_format |= CIK_GRPH_PIPE_CONFIG(CIK_ADDR_SURF_P8_32x32_16x16);
+               else if (num_pipe_configs == 4) {
+                       if (num_rb == 4)
+                               fb_format |= CIK_GRPH_PIPE_CONFIG(CIK_ADDR_SURF_P4_16x16);
+                       else if (num_rb < 4)
+                               fb_format |= CIK_GRPH_PIPE_CONFIG(CIK_ADDR_SURF_P4_8x16);
+               } else if (num_pipe_configs == 2)
+                       fb_format |= CIK_GRPH_PIPE_CONFIG(CIK_ADDR_SURF_P2);
+       } else if ((rdev->family == CHIP_TAHITI) ||
+                  (rdev->family == CHIP_PITCAIRN))
                fb_format |= SI_GRPH_PIPE_CONFIG(SI_ADDR_SURF_P8_32x32_8x16);
        else if (rdev->family == CHIP_VERDE)
                fb_format |= SI_GRPH_PIPE_CONFIG(SI_ADDR_SURF_P4_8x16);
@@ -1224,8 +1244,12 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
        WREG32(EVERGREEN_GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels);
        WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 1);
 
-       WREG32(EVERGREEN_DESKTOP_HEIGHT + radeon_crtc->crtc_offset,
-              target_fb->height);
+       if (rdev->family >= CHIP_BONAIRE)
+               WREG32(CIK_LB_DESKTOP_HEIGHT + radeon_crtc->crtc_offset,
+                      target_fb->height);
+       else
+               WREG32(EVERGREEN_DESKTOP_HEIGHT + radeon_crtc->crtc_offset,
+                      target_fb->height);
        x &= ~3;
        y &= ~1;
        WREG32(EVERGREEN_VIEWPORT_START + radeon_crtc->crtc_offset,
@@ -1597,6 +1621,12 @@ static int radeon_get_shared_nondp_ppll(struct drm_crtc *crtc)
  *
  * Asic specific PLL information
  *
+ * DCE 8.x
+ * KB/KV
+ * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP)
+ * CI
+ * - PPLL0, PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC
+ *
  * DCE 6.1
  * - PPLL2 is only available to UNIPHYA (both DP and non-DP)
  * - PPLL0, PPLL1 are available for UNIPHYB/C/D/E/F (both DP and non-DP)
@@ -1623,7 +1653,47 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
        u32 pll_in_use;
        int pll;
 
-       if (ASIC_IS_DCE61(rdev)) {
+       if (ASIC_IS_DCE8(rdev)) {
+               if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(radeon_crtc->encoder))) {
+                       if (rdev->clock.dp_extclk)
+                               /* skip PPLL programming if using ext clock */
+                               return ATOM_PPLL_INVALID;
+                       else {
+                               /* use the same PPLL for all DP monitors */
+                               pll = radeon_get_shared_dp_ppll(crtc);
+                               if (pll != ATOM_PPLL_INVALID)
+                                       return pll;
+                       }
+               } else {
+                       /* use the same PPLL for all monitors with the same clock */
+                       pll = radeon_get_shared_nondp_ppll(crtc);
+                       if (pll != ATOM_PPLL_INVALID)
+                               return pll;
+               }
+               /* otherwise, pick one of the plls */
+               if ((rdev->family == CHIP_KAVERI) ||
+                   (rdev->family == CHIP_KABINI)) {
+                       /* KB/KV has PPLL1 and PPLL2 */
+                       pll_in_use = radeon_get_pll_use_mask(crtc);
+                       if (!(pll_in_use & (1 << ATOM_PPLL2)))
+                               return ATOM_PPLL2;
+                       if (!(pll_in_use & (1 << ATOM_PPLL1)))
+                               return ATOM_PPLL1;
+                       DRM_ERROR("unable to allocate a PPLL\n");
+                       return ATOM_PPLL_INVALID;
+               } else {
+                       /* CI has PPLL0, PPLL1, and PPLL2 */
+                       pll_in_use = radeon_get_pll_use_mask(crtc);
+                       if (!(pll_in_use & (1 << ATOM_PPLL2)))
+                               return ATOM_PPLL2;
+                       if (!(pll_in_use & (1 << ATOM_PPLL1)))
+                               return ATOM_PPLL1;
+                       if (!(pll_in_use & (1 << ATOM_PPLL0)))
+                               return ATOM_PPLL0;
+                       DRM_ERROR("unable to allocate a PPLL\n");
+                       return ATOM_PPLL_INVALID;
+               }
+       } else if (ASIC_IS_DCE61(rdev)) {
                struct radeon_encoder_atom_dig *dig =
                        radeon_encoder->enc_priv;
 
@@ -1771,6 +1841,9 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc,
        atombios_crtc_set_base(crtc, x, y, old_fb);
        atombios_overscan_setup(crtc, mode, adjusted_mode);
        atombios_scaler_setup(crtc);
+       /* update the hw version fpr dpm */
+       radeon_crtc->hw_mode = *adjusted_mode;
+
        return 0;
 }
 
@@ -1861,7 +1934,7 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
                break;
        case ATOM_PPLL0:
                /* disable the ppll */
-               if (ASIC_IS_DCE61(rdev))
+               if ((rdev->family == CHIP_ARUBA) || (rdev->family == CHIP_BONAIRE))
                        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 8406c82..092275d 100644 (file)
@@ -186,6 +186,13 @@ void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder,
        u8 backlight_level;
        char bl_name[16];
 
+       /* Mac laptops with multiple GPUs use the gmux driver for backlight
+        * so don't register a backlight device
+        */
+       if ((rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE) &&
+           (rdev->pdev->device == 0x6741))
+               return;
+
        if (!radeon_encoder->enc_priv)
                return;
 
@@ -296,6 +303,7 @@ static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder)
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
                return true;
        default:
                return false;
@@ -479,11 +487,11 @@ static u8 radeon_atom_get_bpc(struct drm_encoder *encoder)
        }
 }
 
-
 union dvo_encoder_control {
        ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION ext_tmds;
        DVO_ENCODER_CONTROL_PS_ALLOCATION dvo;
        DVO_ENCODER_CONTROL_PS_ALLOCATION_V3 dvo_v3;
+       DVO_ENCODER_CONTROL_PS_ALLOCATION_V1_4 dvo_v4;
 };
 
 void
@@ -533,6 +541,13 @@ atombios_dvo_setup(struct drm_encoder *encoder, int action)
                        args.dvo_v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
                        args.dvo_v3.ucDVOConfig = 0; /* XXX */
                        break;
+               case 4:
+                       /* DCE8 */
+                       args.dvo_v4.ucAction = action;
+                       args.dvo_v4.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+                       args.dvo_v4.ucDVOConfig = 0; /* XXX */
+                       args.dvo_v4.ucBitPerColor = radeon_atom_get_bpc(encoder);
+                       break;
                default:
                        DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
                        break;
@@ -915,10 +930,14 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mo
                                args.v4.ucLaneNum = 4;
 
                        if (ENCODER_MODE_IS_DP(args.v4.ucEncoderMode)) {
-                               if (dp_clock == 270000)
-                                       args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ;
-                               else if (dp_clock == 540000)
+                               if (dp_clock == 540000)
                                        args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_5_40GHZ;
+                               else if (dp_clock == 324000)
+                                       args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_3_24GHZ;
+                               else if (dp_clock == 270000)
+                                       args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ;
+                               else
+                                       args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_1_62GHZ;
                        }
                        args.v4.acConfig.ucDigSel = dig->dig_encoder;
                        args.v4.ucBitPerColor = radeon_atom_get_bpc(encoder);
@@ -1012,6 +1031,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
                index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl);
                break;
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
@@ -1271,6 +1291,9 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
                                else
                                        args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYE;
                                break;
+                       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
+                               args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYG;
+                               break;
                        }
                        if (is_dp)
                                args.v5.ucLaneNum = dp_lane_count;
@@ -1735,6 +1758,7 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
                radeon_atom_encoder_dpms_dig(encoder, mode);
                break;
@@ -1872,6 +1896,7 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
                        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
                        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
                        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+                       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
                        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
                                dig = radeon_encoder->enc_priv;
                                switch (dig->dig_encoder) {
@@ -1893,6 +1918,9 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
                                case 5:
                                        args.v2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID;
                                        break;
+                               case 6:
+                                       args.v2.ucEncoderID = ASIC_INT_DIG7_ENCODER_ID;
+                                       break;
                                }
                                break;
                        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
@@ -1955,7 +1983,13 @@ atombios_apply_encoder_quirks(struct drm_encoder *encoder,
        /* set scaler clears this on some chips */
        if (ASIC_IS_AVIVO(rdev) &&
            (!(radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)))) {
-               if (ASIC_IS_DCE4(rdev)) {
+               if (ASIC_IS_DCE8(rdev)) {
+                       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+                               WREG32(CIK_LB_DATA_FORMAT + radeon_crtc->crtc_offset,
+                                      CIK_INTERLEAVE_EN);
+                       else
+                               WREG32(CIK_LB_DATA_FORMAT + radeon_crtc->crtc_offset, 0);
+               } else if (ASIC_IS_DCE4(rdev)) {
                        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
                                WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset,
                                       EVERGREEN_INTERLEAVE_EN);
@@ -2002,6 +2036,9 @@ static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder)
                        else
                                return 4;
                        break;
+               case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
+                       return 6;
+                       break;
                }
        } else if (ASIC_IS_DCE4(rdev)) {
                /* DCE4/5 */
@@ -2086,6 +2123,7 @@ radeon_atom_encoder_init(struct radeon_device *rdev)
                case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
                case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
                case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+               case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
                case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
                        atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
                        break;
@@ -2130,6 +2168,7 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
                /* handled in dpms */
                break;
@@ -2395,6 +2434,7 @@ static void radeon_atom_encoder_disable(struct drm_encoder *encoder)
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
                /* handled in dpms */
                break;
@@ -2626,6 +2666,7 @@ radeon_add_atom_encoder(struct drm_device *dev,
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
                if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
                        radeon_encoder->rmx_type = RMX_FULL;
                        drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS);
diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c
new file mode 100644 (file)
index 0000000..0bfd55e
--- /dev/null
@@ -0,0 +1,2751 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "btcd.h"
+#include "r600_dpm.h"
+#include "cypress_dpm.h"
+#include "btc_dpm.h"
+#include "atom.h"
+
+#define MC_CG_ARB_FREQ_F0           0x0a
+#define MC_CG_ARB_FREQ_F1           0x0b
+#define MC_CG_ARB_FREQ_F2           0x0c
+#define MC_CG_ARB_FREQ_F3           0x0d
+
+#define MC_CG_SEQ_DRAMCONF_S0       0x05
+#define MC_CG_SEQ_DRAMCONF_S1       0x06
+#define MC_CG_SEQ_YCLK_SUSPEND      0x04
+#define MC_CG_SEQ_YCLK_RESUME       0x0a
+
+#define SMC_RAM_END 0x8000
+
+#ifndef BTC_MGCG_SEQUENCE
+#define BTC_MGCG_SEQUENCE  300
+
+struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps);
+struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev);
+struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev);
+
+
+//********* BARTS **************//
+static const u32 barts_cgcg_cgls_default[] =
+{
+       /* Register,   Value,     Mask bits */
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff
+};
+#define BARTS_CGCG_CGLS_DEFAULT_LENGTH sizeof(barts_cgcg_cgls_default) / (3 * sizeof(u32))
+
+static const u32 barts_cgcg_cgls_disable[] =
+{
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x00000644, 0x000f7912, 0x001f4180,
+       0x00000644, 0x000f3812, 0x001f4180
+};
+#define BARTS_CGCG_CGLS_DISABLE_LENGTH sizeof(barts_cgcg_cgls_disable) / (3 * sizeof(u32))
+
+static const u32 barts_cgcg_cgls_enable[] =
+{
+       /* 0x0000c124, 0x84180000, 0x00180000, */
+       0x00000644, 0x000f7892, 0x001f4080,
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff
+};
+#define BARTS_CGCG_CGLS_ENABLE_LENGTH sizeof(barts_cgcg_cgls_enable) / (3 * sizeof(u32))
+
+static const u32 barts_mgcg_default[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x00005448, 0x00000100, 0xffffffff,
+       0x000055e4, 0x00600100, 0xffffffff,
+       0x0000160c, 0x00000100, 0xffffffff,
+       0x0000c164, 0x00000100, 0xffffffff,
+       0x00008a18, 0x00000100, 0xffffffff,
+       0x0000897c, 0x06000100, 0xffffffff,
+       0x00008b28, 0x00000100, 0xffffffff,
+       0x00009144, 0x00000100, 0xffffffff,
+       0x00009a60, 0x00000100, 0xffffffff,
+       0x00009868, 0x00000100, 0xffffffff,
+       0x00008d58, 0x00000100, 0xffffffff,
+       0x00009510, 0x00000100, 0xffffffff,
+       0x0000949c, 0x00000100, 0xffffffff,
+       0x00009654, 0x00000100, 0xffffffff,
+       0x00009030, 0x00000100, 0xffffffff,
+       0x00009034, 0x00000100, 0xffffffff,
+       0x00009038, 0x00000100, 0xffffffff,
+       0x0000903c, 0x00000100, 0xffffffff,
+       0x00009040, 0x00000100, 0xffffffff,
+       0x0000a200, 0x00000100, 0xffffffff,
+       0x0000a204, 0x00000100, 0xffffffff,
+       0x0000a208, 0x00000100, 0xffffffff,
+       0x0000a20c, 0x00000100, 0xffffffff,
+       0x0000977c, 0x00000100, 0xffffffff,
+       0x00003f80, 0x00000100, 0xffffffff,
+       0x0000a210, 0x00000100, 0xffffffff,
+       0x0000a214, 0x00000100, 0xffffffff,
+       0x000004d8, 0x00000100, 0xffffffff,
+       0x00009784, 0x00000100, 0xffffffff,
+       0x00009698, 0x00000100, 0xffffffff,
+       0x000004d4, 0x00000200, 0xffffffff,
+       0x000004d0, 0x00000000, 0xffffffff,
+       0x000030cc, 0x00000100, 0xffffffff,
+       0x0000d0c0, 0xff000100, 0xffffffff,
+       0x0000802c, 0x40000000, 0xffffffff,
+       0x0000915c, 0x00010000, 0xffffffff,
+       0x00009160, 0x00030002, 0xffffffff,
+       0x00009164, 0x00050004, 0xffffffff,
+       0x00009168, 0x00070006, 0xffffffff,
+       0x00009178, 0x00070000, 0xffffffff,
+       0x0000917c, 0x00030002, 0xffffffff,
+       0x00009180, 0x00050004, 0xffffffff,
+       0x0000918c, 0x00010006, 0xffffffff,
+       0x00009190, 0x00090008, 0xffffffff,
+       0x00009194, 0x00070000, 0xffffffff,
+       0x00009198, 0x00030002, 0xffffffff,
+       0x0000919c, 0x00050004, 0xffffffff,
+       0x000091a8, 0x00010006, 0xffffffff,
+       0x000091ac, 0x00090008, 0xffffffff,
+       0x000091b0, 0x00070000, 0xffffffff,
+       0x000091b4, 0x00030002, 0xffffffff,
+       0x000091b8, 0x00050004, 0xffffffff,
+       0x000091c4, 0x00010006, 0xffffffff,
+       0x000091c8, 0x00090008, 0xffffffff,
+       0x000091cc, 0x00070000, 0xffffffff,
+       0x000091d0, 0x00030002, 0xffffffff,
+       0x000091d4, 0x00050004, 0xffffffff,
+       0x000091e0, 0x00010006, 0xffffffff,
+       0x000091e4, 0x00090008, 0xffffffff,
+       0x000091e8, 0x00000000, 0xffffffff,
+       0x000091ec, 0x00070000, 0xffffffff,
+       0x000091f0, 0x00030002, 0xffffffff,
+       0x000091f4, 0x00050004, 0xffffffff,
+       0x00009200, 0x00010006, 0xffffffff,
+       0x00009204, 0x00090008, 0xffffffff,
+       0x00009208, 0x00070000, 0xffffffff,
+       0x0000920c, 0x00030002, 0xffffffff,
+       0x00009210, 0x00050004, 0xffffffff,
+       0x0000921c, 0x00010006, 0xffffffff,
+       0x00009220, 0x00090008, 0xffffffff,
+       0x00009224, 0x00070000, 0xffffffff,
+       0x00009228, 0x00030002, 0xffffffff,
+       0x0000922c, 0x00050004, 0xffffffff,
+       0x00009238, 0x00010006, 0xffffffff,
+       0x0000923c, 0x00090008, 0xffffffff,
+       0x00009294, 0x00000000, 0xffffffff,
+       0x0000802c, 0x40010000, 0xffffffff,
+       0x0000915c, 0x00010000, 0xffffffff,
+       0x00009160, 0x00030002, 0xffffffff,
+       0x00009164, 0x00050004, 0xffffffff,
+       0x00009168, 0x00070006, 0xffffffff,
+       0x00009178, 0x00070000, 0xffffffff,
+       0x0000917c, 0x00030002, 0xffffffff,
+       0x00009180, 0x00050004, 0xffffffff,
+       0x0000918c, 0x00010006, 0xffffffff,
+       0x00009190, 0x00090008, 0xffffffff,
+       0x00009194, 0x00070000, 0xffffffff,
+       0x00009198, 0x00030002, 0xffffffff,
+       0x0000919c, 0x00050004, 0xffffffff,
+       0x000091a8, 0x00010006, 0xffffffff,
+       0x000091ac, 0x00090008, 0xffffffff,
+       0x000091b0, 0x00070000, 0xffffffff,
+       0x000091b4, 0x00030002, 0xffffffff,
+       0x000091b8, 0x00050004, 0xffffffff,
+       0x000091c4, 0x00010006, 0xffffffff,
+       0x000091c8, 0x00090008, 0xffffffff,
+       0x000091cc, 0x00070000, 0xffffffff,
+       0x000091d0, 0x00030002, 0xffffffff,
+       0x000091d4, 0x00050004, 0xffffffff,
+       0x000091e0, 0x00010006, 0xffffffff,
+       0x000091e4, 0x00090008, 0xffffffff,
+       0x000091e8, 0x00000000, 0xffffffff,
+       0x000091ec, 0x00070000, 0xffffffff,
+       0x000091f0, 0x00030002, 0xffffffff,
+       0x000091f4, 0x00050004, 0xffffffff,
+       0x00009200, 0x00010006, 0xffffffff,
+       0x00009204, 0x00090008, 0xffffffff,
+       0x00009208, 0x00070000, 0xffffffff,
+       0x0000920c, 0x00030002, 0xffffffff,
+       0x00009210, 0x00050004, 0xffffffff,
+       0x0000921c, 0x00010006, 0xffffffff,
+       0x00009220, 0x00090008, 0xffffffff,
+       0x00009224, 0x00070000, 0xffffffff,
+       0x00009228, 0x00030002, 0xffffffff,
+       0x0000922c, 0x00050004, 0xffffffff,
+       0x00009238, 0x00010006, 0xffffffff,
+       0x0000923c, 0x00090008, 0xffffffff,
+       0x00009294, 0x00000000, 0xffffffff,
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff
+};
+#define BARTS_MGCG_DEFAULT_LENGTH sizeof(barts_mgcg_default) / (3 * sizeof(u32))
+
+static const u32 barts_mgcg_disable[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x000008f8, 0x00000000, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000001, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000002, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000003, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x00009150, 0x00600000, 0xffffffff
+};
+#define BARTS_MGCG_DISABLE_LENGTH sizeof(barts_mgcg_disable) / (3 * sizeof(u32))
+
+static const u32 barts_mgcg_enable[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x000008f8, 0x00000000, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000001, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000002, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000003, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x00009150, 0x81944000, 0xffffffff
+};
+#define BARTS_MGCG_ENABLE_LENGTH sizeof(barts_mgcg_enable) / (3 * sizeof(u32))
+
+//********* CAICOS **************//
+static const u32 caicos_cgcg_cgls_default[] =
+{
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff
+};
+#define CAICOS_CGCG_CGLS_DEFAULT_LENGTH sizeof(caicos_cgcg_cgls_default) / (3 * sizeof(u32))
+
+static const u32 caicos_cgcg_cgls_disable[] =
+{
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x00000644, 0x000f7912, 0x001f4180,
+       0x00000644, 0x000f3812, 0x001f4180
+};
+#define CAICOS_CGCG_CGLS_DISABLE_LENGTH sizeof(caicos_cgcg_cgls_disable) / (3 * sizeof(u32))
+
+static const u32 caicos_cgcg_cgls_enable[] =
+{
+       /* 0x0000c124, 0x84180000, 0x00180000, */
+       0x00000644, 0x000f7892, 0x001f4080,
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff
+};
+#define CAICOS_CGCG_CGLS_ENABLE_LENGTH sizeof(caicos_cgcg_cgls_enable) / (3 * sizeof(u32))
+
+static const u32 caicos_mgcg_default[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x00005448, 0x00000100, 0xffffffff,
+       0x000055e4, 0x00600100, 0xffffffff,
+       0x0000160c, 0x00000100, 0xffffffff,
+       0x0000c164, 0x00000100, 0xffffffff,
+       0x00008a18, 0x00000100, 0xffffffff,
+       0x0000897c, 0x06000100, 0xffffffff,
+       0x00008b28, 0x00000100, 0xffffffff,
+       0x00009144, 0x00000100, 0xffffffff,
+       0x00009a60, 0x00000100, 0xffffffff,
+       0x00009868, 0x00000100, 0xffffffff,
+       0x00008d58, 0x00000100, 0xffffffff,
+       0x00009510, 0x00000100, 0xffffffff,
+       0x0000949c, 0x00000100, 0xffffffff,
+       0x00009654, 0x00000100, 0xffffffff,
+       0x00009030, 0x00000100, 0xffffffff,
+       0x00009034, 0x00000100, 0xffffffff,
+       0x00009038, 0x00000100, 0xffffffff,
+       0x0000903c, 0x00000100, 0xffffffff,
+       0x00009040, 0x00000100, 0xffffffff,
+       0x0000a200, 0x00000100, 0xffffffff,
+       0x0000a204, 0x00000100, 0xffffffff,
+       0x0000a208, 0x00000100, 0xffffffff,
+       0x0000a20c, 0x00000100, 0xffffffff,
+       0x0000977c, 0x00000100, 0xffffffff,
+       0x00003f80, 0x00000100, 0xffffffff,
+       0x0000a210, 0x00000100, 0xffffffff,
+       0x0000a214, 0x00000100, 0xffffffff,
+       0x000004d8, 0x00000100, 0xffffffff,
+       0x00009784, 0x00000100, 0xffffffff,
+       0x00009698, 0x00000100, 0xffffffff,
+       0x000004d4, 0x00000200, 0xffffffff,
+       0x000004d0, 0x00000000, 0xffffffff,
+       0x000030cc, 0x00000100, 0xffffffff,
+       0x0000d0c0, 0xff000100, 0xffffffff,
+       0x0000915c, 0x00010000, 0xffffffff,
+       0x00009160, 0x00030002, 0xffffffff,
+       0x00009164, 0x00050004, 0xffffffff,
+       0x00009168, 0x00070006, 0xffffffff,
+       0x00009178, 0x00070000, 0xffffffff,
+       0x0000917c, 0x00030002, 0xffffffff,
+       0x00009180, 0x00050004, 0xffffffff,
+       0x0000918c, 0x00010006, 0xffffffff,
+       0x00009190, 0x00090008, 0xffffffff,
+       0x00009194, 0x00070000, 0xffffffff,
+       0x00009198, 0x00030002, 0xffffffff,
+       0x0000919c, 0x00050004, 0xffffffff,
+       0x000091a8, 0x00010006, 0xffffffff,
+       0x000091ac, 0x00090008, 0xffffffff,
+       0x000091e8, 0x00000000, 0xffffffff,
+       0x00009294, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff
+};
+#define CAICOS_MGCG_DEFAULT_LENGTH sizeof(caicos_mgcg_default) / (3 * sizeof(u32))
+
+static const u32 caicos_mgcg_disable[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x000008f8, 0x00000000, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000001, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000002, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000003, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x00009150, 0x00600000, 0xffffffff
+};
+#define CAICOS_MGCG_DISABLE_LENGTH sizeof(caicos_mgcg_disable) / (3 * sizeof(u32))
+
+static const u32 caicos_mgcg_enable[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x000008f8, 0x00000000, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000001, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000002, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000003, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x00009150, 0x46944040, 0xffffffff
+};
+#define CAICOS_MGCG_ENABLE_LENGTH sizeof(caicos_mgcg_enable) / (3 * sizeof(u32))
+
+//********* TURKS **************//
+static const u32 turks_cgcg_cgls_default[] =
+{
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff
+};
+#define TURKS_CGCG_CGLS_DEFAULT_LENGTH  sizeof(turks_cgcg_cgls_default) / (3 * sizeof(u32))
+
+static const u32 turks_cgcg_cgls_disable[] =
+{
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x00000644, 0x000f7912, 0x001f4180,
+       0x00000644, 0x000f3812, 0x001f4180
+};
+#define TURKS_CGCG_CGLS_DISABLE_LENGTH sizeof(turks_cgcg_cgls_disable) / (3 * sizeof(u32))
+
+static const u32 turks_cgcg_cgls_enable[] =
+{
+       /* 0x0000c124, 0x84180000, 0x00180000, */
+       0x00000644, 0x000f7892, 0x001f4080,
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff
+};
+#define TURKS_CGCG_CGLS_ENABLE_LENGTH sizeof(turks_cgcg_cgls_enable) / (3 * sizeof(u32))
+
+// These are the sequences for turks_mgcg_shls
+static const u32 turks_mgcg_default[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x00005448, 0x00000100, 0xffffffff,
+       0x000055e4, 0x00600100, 0xffffffff,
+       0x0000160c, 0x00000100, 0xffffffff,
+       0x0000c164, 0x00000100, 0xffffffff,
+       0x00008a18, 0x00000100, 0xffffffff,
+       0x0000897c, 0x06000100, 0xffffffff,
+       0x00008b28, 0x00000100, 0xffffffff,
+       0x00009144, 0x00000100, 0xffffffff,
+       0x00009a60, 0x00000100, 0xffffffff,
+       0x00009868, 0x00000100, 0xffffffff,
+       0x00008d58, 0x00000100, 0xffffffff,
+       0x00009510, 0x00000100, 0xffffffff,
+       0x0000949c, 0x00000100, 0xffffffff,
+       0x00009654, 0x00000100, 0xffffffff,
+       0x00009030, 0x00000100, 0xffffffff,
+       0x00009034, 0x00000100, 0xffffffff,
+       0x00009038, 0x00000100, 0xffffffff,
+       0x0000903c, 0x00000100, 0xffffffff,
+       0x00009040, 0x00000100, 0xffffffff,
+       0x0000a200, 0x00000100, 0xffffffff,
+       0x0000a204, 0x00000100, 0xffffffff,
+       0x0000a208, 0x00000100, 0xffffffff,
+       0x0000a20c, 0x00000100, 0xffffffff,
+       0x0000977c, 0x00000100, 0xffffffff,
+       0x00003f80, 0x00000100, 0xffffffff,
+       0x0000a210, 0x00000100, 0xffffffff,
+       0x0000a214, 0x00000100, 0xffffffff,
+       0x000004d8, 0x00000100, 0xffffffff,
+       0x00009784, 0x00000100, 0xffffffff,
+       0x00009698, 0x00000100, 0xffffffff,
+       0x000004d4, 0x00000200, 0xffffffff,
+       0x000004d0, 0x00000000, 0xffffffff,
+       0x000030cc, 0x00000100, 0xffffffff,
+       0x0000d0c0, 0x00000100, 0xffffffff,
+       0x0000915c, 0x00010000, 0xffffffff,
+       0x00009160, 0x00030002, 0xffffffff,
+       0x00009164, 0x00050004, 0xffffffff,
+       0x00009168, 0x00070006, 0xffffffff,
+       0x00009178, 0x00070000, 0xffffffff,
+       0x0000917c, 0x00030002, 0xffffffff,
+       0x00009180, 0x00050004, 0xffffffff,
+       0x0000918c, 0x00010006, 0xffffffff,
+       0x00009190, 0x00090008, 0xffffffff,
+       0x00009194, 0x00070000, 0xffffffff,
+       0x00009198, 0x00030002, 0xffffffff,
+       0x0000919c, 0x00050004, 0xffffffff,
+       0x000091a8, 0x00010006, 0xffffffff,
+       0x000091ac, 0x00090008, 0xffffffff,
+       0x000091b0, 0x00070000, 0xffffffff,
+       0x000091b4, 0x00030002, 0xffffffff,
+       0x000091b8, 0x00050004, 0xffffffff,
+       0x000091c4, 0x00010006, 0xffffffff,
+       0x000091c8, 0x00090008, 0xffffffff,
+       0x000091cc, 0x00070000, 0xffffffff,
+       0x000091d0, 0x00030002, 0xffffffff,
+       0x000091d4, 0x00050004, 0xffffffff,
+       0x000091e0, 0x00010006, 0xffffffff,
+       0x000091e4, 0x00090008, 0xffffffff,
+       0x000091e8, 0x00000000, 0xffffffff,
+       0x000091ec, 0x00070000, 0xffffffff,
+       0x000091f0, 0x00030002, 0xffffffff,
+       0x000091f4, 0x00050004, 0xffffffff,
+       0x00009200, 0x00010006, 0xffffffff,
+       0x00009204, 0x00090008, 0xffffffff,
+       0x00009208, 0x00070000, 0xffffffff,
+       0x0000920c, 0x00030002, 0xffffffff,
+       0x00009210, 0x00050004, 0xffffffff,
+       0x0000921c, 0x00010006, 0xffffffff,
+       0x00009220, 0x00090008, 0xffffffff,
+       0x00009294, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff
+};
+#define TURKS_MGCG_DEFAULT_LENGTH sizeof(turks_mgcg_default) / (3 * sizeof(u32))
+
+static const u32 turks_mgcg_disable[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x000008f8, 0x00000000, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000001, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000002, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000003, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x00009150, 0x00600000, 0xffffffff
+};
+#define TURKS_MGCG_DISABLE_LENGTH sizeof(turks_mgcg_disable) / (3 * sizeof(u32))
+
+static const u32 turks_mgcg_enable[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x000008f8, 0x00000000, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000001, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000002, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000003, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x00009150, 0x6e944000, 0xffffffff
+};
+#define TURKS_MGCG_ENABLE_LENGTH sizeof(turks_mgcg_enable) / (3 * sizeof(u32))
+
+#endif
+
+#ifndef BTC_SYSLS_SEQUENCE
+#define BTC_SYSLS_SEQUENCE  100
+
+
+//********* BARTS **************//
+static const u32 barts_sysls_default[] =
+{
+       /* Register,   Value,     Mask bits */
+       0x000055e8, 0x00000000, 0xffffffff,
+       0x0000d0bc, 0x00000000, 0xffffffff,
+       0x000015c0, 0x000c1401, 0xffffffff,
+       0x0000264c, 0x000c0400, 0xffffffff,
+       0x00002648, 0x000c0400, 0xffffffff,
+       0x00002650, 0x000c0400, 0xffffffff,
+       0x000020b8, 0x000c0400, 0xffffffff,
+       0x000020bc, 0x000c0400, 0xffffffff,
+       0x000020c0, 0x000c0c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680fff, 0xffffffff,
+       0x000004c8, 0x00000001, 0xffffffff,
+       0x000064ec, 0x00000000, 0xffffffff,
+       0x00000c7c, 0x00000000, 0xffffffff,
+       0x00006dfc, 0x00000000, 0xffffffff
+};
+#define BARTS_SYSLS_DEFAULT_LENGTH sizeof(barts_sysls_default) / (3 * sizeof(u32))
+
+static const u32 barts_sysls_disable[] =
+{
+       0x000055e8, 0x00000000, 0xffffffff,
+       0x0000d0bc, 0x00000000, 0xffffffff,
+       0x000015c0, 0x00041401, 0xffffffff,
+       0x0000264c, 0x00040400, 0xffffffff,
+       0x00002648, 0x00040400, 0xffffffff,
+       0x00002650, 0x00040400, 0xffffffff,
+       0x000020b8, 0x00040400, 0xffffffff,
+       0x000020bc, 0x00040400, 0xffffffff,
+       0x000020c0, 0x00040c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680000, 0xffffffff,
+       0x000004c8, 0x00000001, 0xffffffff,
+       0x000064ec, 0x00007ffd, 0xffffffff,
+       0x00000c7c, 0x0000ff00, 0xffffffff,
+       0x00006dfc, 0x0000007f, 0xffffffff
+};
+#define BARTS_SYSLS_DISABLE_LENGTH sizeof(barts_sysls_disable) / (3 * sizeof(u32))
+
+static const u32 barts_sysls_enable[] =
+{
+       0x000055e8, 0x00000001, 0xffffffff,
+       0x0000d0bc, 0x00000100, 0xffffffff,
+       0x000015c0, 0x000c1401, 0xffffffff,
+       0x0000264c, 0x000c0400, 0xffffffff,
+       0x00002648, 0x000c0400, 0xffffffff,
+       0x00002650, 0x000c0400, 0xffffffff,
+       0x000020b8, 0x000c0400, 0xffffffff,
+       0x000020bc, 0x000c0400, 0xffffffff,
+       0x000020c0, 0x000c0c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680fff, 0xffffffff,
+       0x000004c8, 0x00000000, 0xffffffff,
+       0x000064ec, 0x00000000, 0xffffffff,
+       0x00000c7c, 0x00000000, 0xffffffff,
+       0x00006dfc, 0x00000000, 0xffffffff
+};
+#define BARTS_SYSLS_ENABLE_LENGTH sizeof(barts_sysls_enable) / (3 * sizeof(u32))
+
+//********* CAICOS **************//
+static const u32 caicos_sysls_default[] =
+{
+       0x000055e8, 0x00000000, 0xffffffff,
+       0x0000d0bc, 0x00000000, 0xffffffff,
+       0x000015c0, 0x000c1401, 0xffffffff,
+       0x0000264c, 0x000c0400, 0xffffffff,
+       0x00002648, 0x000c0400, 0xffffffff,
+       0x00002650, 0x000c0400, 0xffffffff,
+       0x000020b8, 0x000c0400, 0xffffffff,
+       0x000020bc, 0x000c0400, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680fff, 0xffffffff,
+       0x000004c8, 0x00000001, 0xffffffff,
+       0x000064ec, 0x00000000, 0xffffffff,
+       0x00000c7c, 0x00000000, 0xffffffff,
+       0x00006dfc, 0x00000000, 0xffffffff
+};
+#define CAICOS_SYSLS_DEFAULT_LENGTH sizeof(caicos_sysls_default) / (3 * sizeof(u32))
+
+static const u32 caicos_sysls_disable[] =
+{
+       0x000055e8, 0x00000000, 0xffffffff,
+       0x0000d0bc, 0x00000000, 0xffffffff,
+       0x000015c0, 0x00041401, 0xffffffff,
+       0x0000264c, 0x00040400, 0xffffffff,
+       0x00002648, 0x00040400, 0xffffffff,
+       0x00002650, 0x00040400, 0xffffffff,
+       0x000020b8, 0x00040400, 0xffffffff,
+       0x000020bc, 0x00040400, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680000, 0xffffffff,
+       0x000004c8, 0x00000001, 0xffffffff,
+       0x000064ec, 0x00007ffd, 0xffffffff,
+       0x00000c7c, 0x0000ff00, 0xffffffff,
+       0x00006dfc, 0x0000007f, 0xffffffff
+};
+#define CAICOS_SYSLS_DISABLE_LENGTH sizeof(caicos_sysls_disable) / (3 * sizeof(u32))
+
+static const u32 caicos_sysls_enable[] =
+{
+       0x000055e8, 0x00000001, 0xffffffff,
+       0x0000d0bc, 0x00000100, 0xffffffff,
+       0x000015c0, 0x000c1401, 0xffffffff,
+       0x0000264c, 0x000c0400, 0xffffffff,
+       0x00002648, 0x000c0400, 0xffffffff,
+       0x00002650, 0x000c0400, 0xffffffff,
+       0x000020b8, 0x000c0400, 0xffffffff,
+       0x000020bc, 0x000c0400, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680fff, 0xffffffff,
+       0x000064ec, 0x00000000, 0xffffffff,
+       0x00000c7c, 0x00000000, 0xffffffff,
+       0x00006dfc, 0x00000000, 0xffffffff,
+       0x000004c8, 0x00000000, 0xffffffff
+};
+#define CAICOS_SYSLS_ENABLE_LENGTH sizeof(caicos_sysls_enable) / (3 * sizeof(u32))
+
+//********* TURKS **************//
+static const u32 turks_sysls_default[] =
+{
+       0x000055e8, 0x00000000, 0xffffffff,
+       0x0000d0bc, 0x00000000, 0xffffffff,
+       0x000015c0, 0x000c1401, 0xffffffff,
+       0x0000264c, 0x000c0400, 0xffffffff,
+       0x00002648, 0x000c0400, 0xffffffff,
+       0x00002650, 0x000c0400, 0xffffffff,
+       0x000020b8, 0x000c0400, 0xffffffff,
+       0x000020bc, 0x000c0400, 0xffffffff,
+       0x000020c0, 0x000c0c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680fff, 0xffffffff,
+       0x000004c8, 0x00000001, 0xffffffff,
+       0x000064ec, 0x00000000, 0xffffffff,
+       0x00000c7c, 0x00000000, 0xffffffff,
+       0x00006dfc, 0x00000000, 0xffffffff
+};
+#define TURKS_SYSLS_DEFAULT_LENGTH sizeof(turks_sysls_default) / (3 * sizeof(u32))
+
+static const u32 turks_sysls_disable[] =
+{
+       0x000055e8, 0x00000000, 0xffffffff,
+       0x0000d0bc, 0x00000000, 0xffffffff,
+       0x000015c0, 0x00041401, 0xffffffff,
+       0x0000264c, 0x00040400, 0xffffffff,
+       0x00002648, 0x00040400, 0xffffffff,
+       0x00002650, 0x00040400, 0xffffffff,
+       0x000020b8, 0x00040400, 0xffffffff,
+       0x000020bc, 0x00040400, 0xffffffff,
+       0x000020c0, 0x00040c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680000, 0xffffffff,
+       0x000004c8, 0x00000001, 0xffffffff,
+       0x000064ec, 0x00007ffd, 0xffffffff,
+       0x00000c7c, 0x0000ff00, 0xffffffff,
+       0x00006dfc, 0x0000007f, 0xffffffff
+};
+#define TURKS_SYSLS_DISABLE_LENGTH sizeof(turks_sysls_disable) / (3 * sizeof(u32))
+
+static const u32 turks_sysls_enable[] =
+{
+       0x000055e8, 0x00000001, 0xffffffff,
+       0x0000d0bc, 0x00000100, 0xffffffff,
+       0x000015c0, 0x000c1401, 0xffffffff,
+       0x0000264c, 0x000c0400, 0xffffffff,
+       0x00002648, 0x000c0400, 0xffffffff,
+       0x00002650, 0x000c0400, 0xffffffff,
+       0x000020b8, 0x000c0400, 0xffffffff,
+       0x000020bc, 0x000c0400, 0xffffffff,
+       0x000020c0, 0x000c0c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680fff, 0xffffffff,
+       0x000004c8, 0x00000000, 0xffffffff,
+       0x000064ec, 0x00000000, 0xffffffff,
+       0x00000c7c, 0x00000000, 0xffffffff,
+       0x00006dfc, 0x00000000, 0xffffffff
+};
+#define TURKS_SYSLS_ENABLE_LENGTH sizeof(turks_sysls_enable) / (3 * sizeof(u32))
+
+#endif
+
+u32 btc_valid_sclk[40] =
+{
+       5000,   10000,  15000,  20000,  25000,  30000,  35000,  40000,  45000,  50000,
+       55000,  60000,  65000,  70000,  75000,  80000,  85000,  90000,  95000,  100000,
+       105000, 110000, 11500,  120000, 125000, 130000, 135000, 140000, 145000, 150000,
+       155000, 160000, 165000, 170000, 175000, 180000, 185000, 190000, 195000, 200000
+};
+
+static const struct radeon_blacklist_clocks btc_blacklist_clocks[] =
+{
+        { 10000, 30000, RADEON_SCLK_UP },
+        { 15000, 30000, RADEON_SCLK_UP },
+        { 20000, 30000, RADEON_SCLK_UP },
+        { 25000, 30000, RADEON_SCLK_UP }
+};
+
+void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table,
+                                       u32 clock, u16 max_voltage, u16 *voltage)
+{
+       u32 i;
+
+       if ((table == NULL) || (table->count == 0))
+               return;
+
+       for (i= 0; i < table->count; i++) {
+               if (clock <= table->entries[i].clk) {
+                       if (*voltage < table->entries[i].v)
+                               *voltage = (u16)((table->entries[i].v < max_voltage) ?
+                                                 table->entries[i].v : max_voltage);
+                       return;
+               }
+       }
+
+       *voltage = (*voltage > max_voltage) ? *voltage : max_voltage;
+}
+
+static u32 btc_find_valid_clock(struct radeon_clock_array *clocks,
+                               u32 max_clock, u32 requested_clock)
+{
+       unsigned int i;
+
+       if ((clocks == NULL) || (clocks->count == 0))
+               return (requested_clock < max_clock) ? requested_clock : max_clock;
+
+       for (i = 0; i < clocks->count; i++) {
+               if (clocks->values[i] >= requested_clock)
+                       return (clocks->values[i] < max_clock) ? clocks->values[i] : max_clock;
+       }
+
+       return (clocks->values[clocks->count - 1] < max_clock) ?
+               clocks->values[clocks->count - 1] : max_clock;
+}
+
+static u32 btc_get_valid_mclk(struct radeon_device *rdev,
+                             u32 max_mclk, u32 requested_mclk)
+{
+       return btc_find_valid_clock(&rdev->pm.dpm.dyn_state.valid_mclk_values,
+                                   max_mclk, requested_mclk);
+}
+
+static u32 btc_get_valid_sclk(struct radeon_device *rdev,
+                             u32 max_sclk, u32 requested_sclk)
+{
+       return btc_find_valid_clock(&rdev->pm.dpm.dyn_state.valid_sclk_values,
+                                   max_sclk, requested_sclk);
+}
+
+void btc_skip_blacklist_clocks(struct radeon_device *rdev,
+                              const u32 max_sclk, const u32 max_mclk,
+                              u32 *sclk, u32 *mclk)
+{
+       int i, num_blacklist_clocks;
+
+       if ((sclk == NULL) || (mclk == NULL))
+               return;
+
+       num_blacklist_clocks = ARRAY_SIZE(btc_blacklist_clocks);
+
+       for (i = 0; i < num_blacklist_clocks; i++) {
+               if ((btc_blacklist_clocks[i].sclk == *sclk) &&
+                   (btc_blacklist_clocks[i].mclk == *mclk))
+                       break;
+       }
+
+       if (i < num_blacklist_clocks) {
+               if (btc_blacklist_clocks[i].action == RADEON_SCLK_UP) {
+                       *sclk = btc_get_valid_sclk(rdev, max_sclk, *sclk + 1);
+
+                       if (*sclk < max_sclk)
+                               btc_skip_blacklist_clocks(rdev, max_sclk, max_mclk, sclk, mclk);
+               }
+       }
+}
+
+void btc_adjust_clock_combinations(struct radeon_device *rdev,
+                                  const struct radeon_clock_and_voltage_limits *max_limits,
+                                  struct rv7xx_pl *pl)
+{
+
+       if ((pl->mclk == 0) || (pl->sclk == 0))
+               return;
+
+       if (pl->mclk == pl->sclk)
+               return;
+
+       if (pl->mclk > pl->sclk) {
+               if (((pl->mclk + (pl->sclk - 1)) / pl->sclk) > rdev->pm.dpm.dyn_state.mclk_sclk_ratio)
+                       pl->sclk = btc_get_valid_sclk(rdev,
+                                                     max_limits->sclk,
+                                                     (pl->mclk +
+                                                      (rdev->pm.dpm.dyn_state.mclk_sclk_ratio - 1)) /
+                                                     rdev->pm.dpm.dyn_state.mclk_sclk_ratio);
+       } else {
+               if ((pl->sclk - pl->mclk) > rdev->pm.dpm.dyn_state.sclk_mclk_delta)
+                       pl->mclk = btc_get_valid_mclk(rdev,
+                                                     max_limits->mclk,
+                                                     pl->sclk -
+                                                     rdev->pm.dpm.dyn_state.sclk_mclk_delta);
+       }
+}
+
+static u16 btc_find_voltage(struct atom_voltage_table *table, u16 voltage)
+{
+       unsigned int i;
+
+       for (i = 0; i < table->count; i++) {
+               if (voltage <= table->entries[i].value)
+                       return table->entries[i].value;
+       }
+
+       return table->entries[table->count - 1].value;
+}
+
+void btc_apply_voltage_delta_rules(struct radeon_device *rdev,
+                                  u16 max_vddc, u16 max_vddci,
+                                  u16 *vddc, u16 *vddci)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u16 new_voltage;
+
+       if ((0 == *vddc) || (0 == *vddci))
+               return;
+
+       if (*vddc > *vddci) {
+               if ((*vddc - *vddci) > rdev->pm.dpm.dyn_state.vddc_vddci_delta) {
+                       new_voltage = btc_find_voltage(&eg_pi->vddci_voltage_table,
+                                                      (*vddc - rdev->pm.dpm.dyn_state.vddc_vddci_delta));
+                       *vddci = (new_voltage < max_vddci) ? new_voltage : max_vddci;
+               }
+       } else {
+               if ((*vddci - *vddc) > rdev->pm.dpm.dyn_state.vddc_vddci_delta) {
+                       new_voltage = btc_find_voltage(&eg_pi->vddc_voltage_table,
+                                                      (*vddci - rdev->pm.dpm.dyn_state.vddc_vddci_delta));
+                       *vddc = (new_voltage < max_vddc) ? new_voltage : max_vddc;
+               }
+       }
+}
+
+static void btc_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev,
+                                            bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 tmp, bif;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+       if (enable) {
+               if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) &&
+                   (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) {
+                       if (!pi->boot_in_gen2) {
+                               bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK;
+                               bif |= CG_CLIENT_REQ(0xd);
+                               WREG32(CG_BIF_REQ_AND_RSP, bif);
+
+                               tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
+                               tmp |= LC_HW_VOLTAGE_IF_CONTROL(1);
+                               tmp |= LC_GEN2_EN_STRAP;
+
+                               tmp |= LC_CLR_FAILED_SPD_CHANGE_CNT;
+                               WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+                               udelay(10);
+                               tmp &= ~LC_CLR_FAILED_SPD_CHANGE_CNT;
+                               WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+                       }
+               }
+       } else {
+               if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) ||
+                   (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) {
+                       if (!pi->boot_in_gen2) {
+                               bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK;
+                               bif |= CG_CLIENT_REQ(0xd);
+                               WREG32(CG_BIF_REQ_AND_RSP, bif);
+
+                               tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
+                               tmp &= ~LC_GEN2_EN_STRAP;
+                       }
+                       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+               }
+       }
+}
+
+static void btc_enable_dynamic_pcie_gen2(struct radeon_device *rdev,
+                                        bool enable)
+{
+       btc_enable_bif_dynamic_pcie_gen2(rdev, enable);
+
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, ENABLE_GEN2PCIE, ~ENABLE_GEN2PCIE);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE);
+}
+
+static int btc_disable_ulv(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       if (eg_pi->ulv.supported) {
+               if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_DisableULV) != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+static int btc_populate_ulv_state(struct radeon_device *rdev,
+                                 RV770_SMC_STATETABLE *table)
+{
+       int ret = -EINVAL;
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct rv7xx_pl *ulv_pl = eg_pi->ulv.pl;
+
+       if (ulv_pl->vddc) {
+               ret = cypress_convert_power_level_to_smc(rdev,
+                                                        ulv_pl,
+                                                        &table->ULVState.levels[0],
+                                                        PPSMC_DISPLAY_WATERMARK_LOW);
+               if (ret == 0) {
+                       table->ULVState.levels[0].arbValue = MC_CG_ARB_FREQ_F0;
+                       table->ULVState.levels[0].ACIndex = 1;
+
+                       table->ULVState.levels[1] = table->ULVState.levels[0];
+                       table->ULVState.levels[2] = table->ULVState.levels[0];
+
+                       table->ULVState.flags |= PPSMC_SWSTATE_FLAG_DC;
+
+                       WREG32(CG_ULV_CONTROL, BTC_CGULVCONTROL_DFLT);
+                       WREG32(CG_ULV_PARAMETER, BTC_CGULVPARAMETER_DFLT);
+               }
+       }
+
+       return ret;
+}
+
+static int btc_populate_smc_acpi_state(struct radeon_device *rdev,
+                                      RV770_SMC_STATETABLE *table)
+{
+       int ret = cypress_populate_smc_acpi_state(rdev, table);
+
+       if (ret == 0) {
+               table->ACPIState.levels[0].ACIndex = 0;
+               table->ACPIState.levels[1].ACIndex = 0;
+               table->ACPIState.levels[2].ACIndex = 0;
+       }
+
+       return ret;
+}
+
+void btc_program_mgcg_hw_sequence(struct radeon_device *rdev,
+                                 const u32 *sequence, u32 count)
+{
+       u32 i, length = count * 3;
+       u32 tmp;
+
+       for (i = 0; i < length; i+=3) {
+               tmp = RREG32(sequence[i]);
+               tmp &= ~sequence[i+2];
+               tmp |= sequence[i+1] & sequence[i+2];
+               WREG32(sequence[i], tmp);
+       }
+}
+
+static void btc_cg_clock_gating_default(struct radeon_device *rdev)
+{
+       u32 count;
+       const u32 *p = NULL;
+
+       if (rdev->family == CHIP_BARTS) {
+               p = (const u32 *)&barts_cgcg_cgls_default;
+               count = BARTS_CGCG_CGLS_DEFAULT_LENGTH;
+       } else if (rdev->family == CHIP_TURKS) {
+               p = (const u32 *)&turks_cgcg_cgls_default;
+               count = TURKS_CGCG_CGLS_DEFAULT_LENGTH;
+       } else if (rdev->family == CHIP_CAICOS) {
+               p = (const u32 *)&caicos_cgcg_cgls_default;
+               count = CAICOS_CGCG_CGLS_DEFAULT_LENGTH;
+       } else
+               return;
+
+       btc_program_mgcg_hw_sequence(rdev, p, count);
+}
+
+static void btc_cg_clock_gating_enable(struct radeon_device *rdev,
+                                      bool enable)
+{
+       u32 count;
+       const u32 *p = NULL;
+
+       if (enable) {
+               if (rdev->family == CHIP_BARTS) {
+                       p = (const u32 *)&barts_cgcg_cgls_enable;
+                       count = BARTS_CGCG_CGLS_ENABLE_LENGTH;
+               } else if (rdev->family == CHIP_TURKS) {
+                       p = (const u32 *)&turks_cgcg_cgls_enable;
+                       count = TURKS_CGCG_CGLS_ENABLE_LENGTH;
+               } else if (rdev->family == CHIP_CAICOS) {
+                       p = (const u32 *)&caicos_cgcg_cgls_enable;
+                       count = CAICOS_CGCG_CGLS_ENABLE_LENGTH;
+               } else
+                       return;
+       } else {
+               if (rdev->family == CHIP_BARTS) {
+                       p = (const u32 *)&barts_cgcg_cgls_disable;
+                       count = BARTS_CGCG_CGLS_DISABLE_LENGTH;
+               } else if (rdev->family == CHIP_TURKS) {
+                       p = (const u32 *)&turks_cgcg_cgls_disable;
+                       count = TURKS_CGCG_CGLS_DISABLE_LENGTH;
+               } else if (rdev->family == CHIP_CAICOS) {
+                       p = (const u32 *)&caicos_cgcg_cgls_disable;
+                       count = CAICOS_CGCG_CGLS_DISABLE_LENGTH;
+               } else
+                       return;
+       }
+
+       btc_program_mgcg_hw_sequence(rdev, p, count);
+}
+
+static void btc_mg_clock_gating_default(struct radeon_device *rdev)
+{
+       u32 count;
+       const u32 *p = NULL;
+
+       if (rdev->family == CHIP_BARTS) {
+               p = (const u32 *)&barts_mgcg_default;
+               count = BARTS_MGCG_DEFAULT_LENGTH;
+       } else if (rdev->family == CHIP_TURKS) {
+               p = (const u32 *)&turks_mgcg_default;
+               count = TURKS_MGCG_DEFAULT_LENGTH;
+       } else if (rdev->family == CHIP_CAICOS) {
+               p = (const u32 *)&caicos_mgcg_default;
+               count = CAICOS_MGCG_DEFAULT_LENGTH;
+       } else
+               return;
+
+       btc_program_mgcg_hw_sequence(rdev, p, count);
+}
+
+static void btc_mg_clock_gating_enable(struct radeon_device *rdev,
+                                      bool enable)
+{
+       u32 count;
+       const u32 *p = NULL;
+
+       if (enable) {
+               if (rdev->family == CHIP_BARTS) {
+                       p = (const u32 *)&barts_mgcg_enable;
+                       count = BARTS_MGCG_ENABLE_LENGTH;
+               } else if (rdev->family == CHIP_TURKS) {
+                       p = (const u32 *)&turks_mgcg_enable;
+                       count = TURKS_MGCG_ENABLE_LENGTH;
+               } else if (rdev->family == CHIP_CAICOS) {
+                       p = (const u32 *)&caicos_mgcg_enable;
+                       count = CAICOS_MGCG_ENABLE_LENGTH;
+               } else
+                       return;
+       } else {
+               if (rdev->family == CHIP_BARTS) {
+                       p = (const u32 *)&barts_mgcg_disable[0];
+                       count = BARTS_MGCG_DISABLE_LENGTH;
+               } else if (rdev->family == CHIP_TURKS) {
+                       p = (const u32 *)&turks_mgcg_disable[0];
+                       count = TURKS_MGCG_DISABLE_LENGTH;
+               } else if (rdev->family == CHIP_CAICOS) {
+                       p = (const u32 *)&caicos_mgcg_disable[0];
+                       count = CAICOS_MGCG_DISABLE_LENGTH;
+               } else
+                       return;
+       }
+
+       btc_program_mgcg_hw_sequence(rdev, p, count);
+}
+
+static void btc_ls_clock_gating_default(struct radeon_device *rdev)
+{
+       u32 count;
+       const u32 *p = NULL;
+
+       if (rdev->family == CHIP_BARTS) {
+               p = (const u32 *)&barts_sysls_default;
+               count = BARTS_SYSLS_DEFAULT_LENGTH;
+       } else if (rdev->family == CHIP_TURKS) {
+               p = (const u32 *)&turks_sysls_default;
+               count = TURKS_SYSLS_DEFAULT_LENGTH;
+       } else if (rdev->family == CHIP_CAICOS) {
+               p = (const u32 *)&caicos_sysls_default;
+               count = CAICOS_SYSLS_DEFAULT_LENGTH;
+       } else
+               return;
+
+       btc_program_mgcg_hw_sequence(rdev, p, count);
+}
+
+static void btc_ls_clock_gating_enable(struct radeon_device *rdev,
+                                      bool enable)
+{
+       u32 count;
+       const u32 *p = NULL;
+
+       if (enable) {
+               if (rdev->family == CHIP_BARTS) {
+                       p = (const u32 *)&barts_sysls_enable;
+                       count = BARTS_SYSLS_ENABLE_LENGTH;
+               } else if (rdev->family == CHIP_TURKS) {
+                       p = (const u32 *)&turks_sysls_enable;
+                       count = TURKS_SYSLS_ENABLE_LENGTH;
+               } else if (rdev->family == CHIP_CAICOS) {
+                       p = (const u32 *)&caicos_sysls_enable;
+                       count = CAICOS_SYSLS_ENABLE_LENGTH;
+               } else
+                       return;
+       } else {
+               if (rdev->family == CHIP_BARTS) {
+                       p = (const u32 *)&barts_sysls_disable;
+                       count = BARTS_SYSLS_DISABLE_LENGTH;
+               } else if (rdev->family == CHIP_TURKS) {
+                       p = (const u32 *)&turks_sysls_disable;
+                       count = TURKS_SYSLS_DISABLE_LENGTH;
+               } else if (rdev->family == CHIP_CAICOS) {
+                       p = (const u32 *)&caicos_sysls_disable;
+                       count = CAICOS_SYSLS_DISABLE_LENGTH;
+               } else
+                       return;
+       }
+
+       btc_program_mgcg_hw_sequence(rdev, p, count);
+}
+
+bool btc_dpm_enabled(struct radeon_device *rdev)
+{
+       if (rv770_is_smc_running(rdev))
+               return true;
+       else
+               return false;
+}
+
+static int btc_init_smc_table(struct radeon_device *rdev,
+                             struct radeon_ps *radeon_boot_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       RV770_SMC_STATETABLE *table = &pi->smc_statetable;
+       int ret;
+
+       memset(table, 0, sizeof(RV770_SMC_STATETABLE));
+
+       cypress_populate_smc_voltage_tables(rdev, table);
+
+       switch (rdev->pm.int_thermal_type) {
+        case THERMAL_TYPE_EVERGREEN:
+        case THERMAL_TYPE_EMC2103_WITH_INTERNAL:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL;
+               break;
+        case THERMAL_TYPE_NONE:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE;
+               break;
+        default:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL;
+               break;
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (pi->mem_gddr5)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+       ret = cypress_populate_smc_initial_state(rdev, radeon_boot_state, table);
+       if (ret)
+               return ret;
+
+       if (eg_pi->sclk_deep_sleep)
+               WREG32_P(SCLK_PSKIP_CNTL, PSKIP_ON_ALLOW_STOP_HI(32),
+                        ~PSKIP_ON_ALLOW_STOP_HI_MASK);
+
+       ret = btc_populate_smc_acpi_state(rdev, table);
+       if (ret)
+               return ret;
+
+       if (eg_pi->ulv.supported) {
+               ret = btc_populate_ulv_state(rdev, table);
+               if (ret)
+                       eg_pi->ulv.supported = false;
+       }
+
+       table->driverState = table->initialState;
+
+       return rv770_copy_bytes_to_smc(rdev,
+                                      pi->state_table_start,
+                                      (u8 *)table,
+                                      sizeof(RV770_SMC_STATETABLE),
+                                      pi->sram_end);
+}
+
+static void btc_set_at_for_uvd(struct radeon_device *rdev,
+                              struct radeon_ps *radeon_new_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       int idx = 0;
+
+       if (r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2))
+               idx = 1;
+
+       if ((idx == 1) && !eg_pi->smu_uvd_hs) {
+               pi->rlp = 10;
+               pi->rmp = 100;
+               pi->lhp = 100;
+               pi->lmp = 10;
+       } else {
+               pi->rlp = eg_pi->ats[idx].rlp;
+               pi->rmp = eg_pi->ats[idx].rmp;
+               pi->lhp = eg_pi->ats[idx].lhp;
+               pi->lmp = eg_pi->ats[idx].lmp;
+       }
+
+}
+
+void btc_notify_uvd_to_smc(struct radeon_device *rdev,
+                          struct radeon_ps *radeon_new_state)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       if (r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) {
+               rv770_write_smc_soft_register(rdev,
+                                             RV770_SMC_SOFT_REGISTER_uvd_enabled, 1);
+               eg_pi->uvd_enabled = true;
+       } else {
+               rv770_write_smc_soft_register(rdev,
+                                             RV770_SMC_SOFT_REGISTER_uvd_enabled, 0);
+               eg_pi->uvd_enabled = false;
+       }
+}
+
+int btc_reset_to_default(struct radeon_device *rdev)
+{
+       if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_ResetToDefaults) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return 0;
+}
+
+static void btc_stop_smc(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (((RREG32(LB_SYNC_RESET_SEL) & LB_SYNC_RESET_SEL_MASK) >> LB_SYNC_RESET_SEL_SHIFT) != 1)
+                       break;
+               udelay(1);
+       }
+       udelay(100);
+
+       r7xx_stop_smc(rdev);
+}
+
+void btc_read_arb_registers(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct evergreen_arb_registers *arb_registers =
+               &eg_pi->bootup_arb_registers;
+
+       arb_registers->mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING);
+       arb_registers->mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+       arb_registers->mc_arb_rfsh_rate = RREG32(MC_ARB_RFSH_RATE);
+       arb_registers->mc_arb_burst_time = RREG32(MC_ARB_BURST_TIME);
+}
+
+
+static void btc_set_arb0_registers(struct radeon_device *rdev,
+                                  struct evergreen_arb_registers *arb_registers)
+{
+       u32 val;
+
+       WREG32(MC_ARB_DRAM_TIMING,  arb_registers->mc_arb_dram_timing);
+       WREG32(MC_ARB_DRAM_TIMING2, arb_registers->mc_arb_dram_timing2);
+
+       val = (arb_registers->mc_arb_rfsh_rate & POWERMODE0_MASK) >>
+               POWERMODE0_SHIFT;
+       WREG32_P(MC_ARB_RFSH_RATE, POWERMODE0(val), ~POWERMODE0_MASK);
+
+       val = (arb_registers->mc_arb_burst_time & STATE0_MASK) >>
+               STATE0_SHIFT;
+       WREG32_P(MC_ARB_BURST_TIME, STATE0(val), ~STATE0_MASK);
+}
+
+static void btc_set_boot_state_timing(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       if (eg_pi->ulv.supported)
+               btc_set_arb0_registers(rdev, &eg_pi->bootup_arb_registers);
+}
+
+static bool btc_is_state_ulv_compatible(struct radeon_device *rdev,
+                                       struct radeon_ps *radeon_state)
+{
+       struct rv7xx_ps *state = rv770_get_ps(radeon_state);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct rv7xx_pl *ulv_pl = eg_pi->ulv.pl;
+
+       if (state->low.mclk != ulv_pl->mclk)
+               return false;
+
+       if (state->low.vddci != ulv_pl->vddci)
+               return false;
+
+       /* XXX check minclocks, etc. */
+
+       return true;
+}
+
+
+static int btc_set_ulv_dram_timing(struct radeon_device *rdev)
+{
+       u32 val;
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct rv7xx_pl *ulv_pl = eg_pi->ulv.pl;
+
+       radeon_atom_set_engine_dram_timings(rdev,
+                                           ulv_pl->sclk,
+                                           ulv_pl->mclk);
+
+       val = rv770_calculate_memory_refresh_rate(rdev, ulv_pl->sclk);
+       WREG32_P(MC_ARB_RFSH_RATE, POWERMODE0(val), ~POWERMODE0_MASK);
+
+       val = cypress_calculate_burst_time(rdev, ulv_pl->sclk, ulv_pl->mclk);
+       WREG32_P(MC_ARB_BURST_TIME, STATE0(val), ~STATE0_MASK);
+
+       return 0;
+}
+
+static int btc_enable_ulv(struct radeon_device *rdev)
+{
+       if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableULV) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int btc_set_power_state_conditionally_enable_ulv(struct radeon_device *rdev,
+                                                       struct radeon_ps *radeon_new_state)
+{
+       int ret = 0;
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       if (eg_pi->ulv.supported) {
+               if (btc_is_state_ulv_compatible(rdev, radeon_new_state)) {
+                       // Set ARB[0] to reflect the DRAM timing needed for ULV.
+                       ret = btc_set_ulv_dram_timing(rdev);
+                       if (ret == 0)
+                               ret = btc_enable_ulv(rdev);
+               }
+       }
+
+       return ret;
+}
+
+static bool btc_check_s0_mc_reg_index(u16 in_reg, u16 *out_reg)
+{
+       bool result = true;
+
+       switch (in_reg) {
+       case MC_SEQ_RAS_TIMING >> 2:
+               *out_reg = MC_SEQ_RAS_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_CAS_TIMING >> 2:
+               *out_reg = MC_SEQ_CAS_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_MISC_TIMING >> 2:
+               *out_reg = MC_SEQ_MISC_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_MISC_TIMING2 >> 2:
+               *out_reg = MC_SEQ_MISC_TIMING2_LP >> 2;
+               break;
+        case MC_SEQ_RD_CTL_D0 >> 2:
+               *out_reg = MC_SEQ_RD_CTL_D0_LP >> 2;
+               break;
+        case MC_SEQ_RD_CTL_D1 >> 2:
+               *out_reg = MC_SEQ_RD_CTL_D1_LP >> 2;
+               break;
+        case MC_SEQ_WR_CTL_D0 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_D0_LP >> 2;
+               break;
+        case MC_SEQ_WR_CTL_D1 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_D1_LP >> 2;
+               break;
+        case MC_PMG_CMD_EMRS >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
+               break;
+        case MC_PMG_CMD_MRS >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS_LP >> 2;
+               break;
+        case MC_PMG_CMD_MRS1 >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
+               break;
+        default:
+               result = false;
+               break;
+       }
+
+       return result;
+}
+
+static void btc_set_valid_flag(struct evergreen_mc_reg_table *table)
+{
+       u8 i, j;
+
+       for (i = 0; i < table->last; i++) {
+               for (j = 1; j < table->num_entries; j++) {
+                       if (table->mc_reg_table_entry[j-1].mc_data[i] !=
+                           table->mc_reg_table_entry[j].mc_data[i]) {
+                               table->valid_flag |= (1 << i);
+                               break;
+                       }
+               }
+       }
+}
+
+static int btc_set_mc_special_registers(struct radeon_device *rdev,
+                                       struct evergreen_mc_reg_table *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u8 i, j, k;
+       u32 tmp;
+
+       for (i = 0, j = table->last; i < table->last; i++) {
+               switch (table->mc_reg_address[i].s1) {
+               case MC_SEQ_MISC1 >> 2:
+                       tmp = RREG32(MC_PMG_CMD_EMRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_EMRS >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++) {
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       ((tmp & 0xffff0000)) |
+                                       ((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16);
+                       }
+                       j++;
+
+                       if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+
+                       tmp = RREG32(MC_PMG_CMD_MRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++) {
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       (tmp & 0xffff0000) |
+                                       (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+                               if (!pi->mem_gddr5)
+                                       table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+                       }
+                       j++;
+
+                       if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+                       break;
+               case MC_SEQ_RESERVE_M >> 2:
+                       tmp = RREG32(MC_PMG_CMD_MRS1);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS1 >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++) {
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       (tmp & 0xffff0000) |
+                                       (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+                       }
+                       j++;
+
+                       if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       table->last = j;
+
+       return 0;
+}
+
+static void btc_set_s0_mc_reg_index(struct evergreen_mc_reg_table *table)
+{
+       u32 i;
+       u16 address;
+
+       for (i = 0; i < table->last; i++) {
+               table->mc_reg_address[i].s0 =
+                       btc_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address) ?
+                       address : table->mc_reg_address[i].s1;
+       }
+}
+
+static int btc_copy_vbios_mc_reg_table(struct atom_mc_reg_table *table,
+                                      struct evergreen_mc_reg_table *eg_table)
+{
+       u8 i, j;
+
+       if (table->last > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
+               return -EINVAL;
+
+       if (table->num_entries > MAX_AC_TIMING_ENTRIES)
+               return -EINVAL;
+
+       for (i = 0; i < table->last; i++)
+               eg_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1;
+       eg_table->last = table->last;
+
+       for (i = 0; i < table->num_entries; i++) {
+               eg_table->mc_reg_table_entry[i].mclk_max =
+                       table->mc_reg_table_entry[i].mclk_max;
+               for(j = 0; j < table->last; j++)
+                       eg_table->mc_reg_table_entry[i].mc_data[j] =
+                               table->mc_reg_table_entry[i].mc_data[j];
+       }
+       eg_table->num_entries = table->num_entries;
+
+       return 0;
+}
+
+static int btc_initialize_mc_reg_table(struct radeon_device *rdev)
+{
+       int ret;
+       struct atom_mc_reg_table *table;
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct evergreen_mc_reg_table *eg_table = &eg_pi->mc_reg_table;
+       u8 module_index = rv770_get_memory_module_index(rdev);
+
+       table = kzalloc(sizeof(struct atom_mc_reg_table), GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
+
+       /* Program additional LP registers that are no longer programmed by VBIOS */
+       WREG32(MC_SEQ_RAS_TIMING_LP, RREG32(MC_SEQ_RAS_TIMING));
+       WREG32(MC_SEQ_CAS_TIMING_LP, RREG32(MC_SEQ_CAS_TIMING));
+       WREG32(MC_SEQ_MISC_TIMING_LP, RREG32(MC_SEQ_MISC_TIMING));
+       WREG32(MC_SEQ_MISC_TIMING2_LP, RREG32(MC_SEQ_MISC_TIMING2));
+       WREG32(MC_SEQ_RD_CTL_D0_LP, RREG32(MC_SEQ_RD_CTL_D0));
+       WREG32(MC_SEQ_RD_CTL_D1_LP, RREG32(MC_SEQ_RD_CTL_D1));
+       WREG32(MC_SEQ_WR_CTL_D0_LP, RREG32(MC_SEQ_WR_CTL_D0));
+       WREG32(MC_SEQ_WR_CTL_D1_LP, RREG32(MC_SEQ_WR_CTL_D1));
+       WREG32(MC_SEQ_PMG_CMD_EMRS_LP, RREG32(MC_PMG_CMD_EMRS));
+       WREG32(MC_SEQ_PMG_CMD_MRS_LP, RREG32(MC_PMG_CMD_MRS));
+       WREG32(MC_SEQ_PMG_CMD_MRS1_LP, RREG32(MC_PMG_CMD_MRS1));
+
+       ret = radeon_atom_init_mc_reg_table(rdev, module_index, table);
+
+       if (ret)
+               goto init_mc_done;
+
+       ret = btc_copy_vbios_mc_reg_table(table, eg_table);
+
+       if (ret)
+               goto init_mc_done;
+
+       btc_set_s0_mc_reg_index(eg_table);
+       ret = btc_set_mc_special_registers(rdev, eg_table);
+
+       if (ret)
+               goto init_mc_done;
+
+       btc_set_valid_flag(eg_table);
+
+init_mc_done:
+       kfree(table);
+
+       return ret;
+}
+
+static void btc_init_stutter_mode(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 tmp;
+
+       if (pi->mclk_stutter_mode_threshold) {
+               if (pi->mem_gddr5) {
+                       tmp = RREG32(MC_PMG_AUTO_CFG);
+                       if ((0x200 & tmp) == 0) {
+                               tmp = (tmp & 0xfffffc0b) | 0x204;
+                               WREG32(MC_PMG_AUTO_CFG, tmp);
+                       }
+               }
+       }
+}
+
+bool btc_dpm_vblank_too_short(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 vblank_time = r600_dpm_get_vblank_time(rdev);
+       u32 switch_limit = pi->mem_gddr5 ? 450 : 100;
+
+       if (vblank_time < switch_limit)
+               return true;
+       else
+               return false;
+
+}
+
+static void btc_apply_state_adjust_rules(struct radeon_device *rdev,
+                                        struct radeon_ps *rps)
+{
+       struct rv7xx_ps *ps = rv770_get_ps(rps);
+       struct radeon_clock_and_voltage_limits *max_limits;
+       bool disable_mclk_switching;
+       u32 mclk, sclk;
+       u16 vddc, vddci;
+
+       if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
+           btc_dpm_vblank_too_short(rdev))
+               disable_mclk_switching = true;
+       else
+               disable_mclk_switching = false;
+
+       if (rdev->pm.dpm.ac_power)
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+       else
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
+
+       if (rdev->pm.dpm.ac_power == false) {
+               if (ps->high.mclk > max_limits->mclk)
+                       ps->high.mclk = max_limits->mclk;
+               if (ps->high.sclk > max_limits->sclk)
+                       ps->high.sclk = max_limits->sclk;
+               if (ps->high.vddc > max_limits->vddc)
+                       ps->high.vddc = max_limits->vddc;
+               if (ps->high.vddci > max_limits->vddci)
+                       ps->high.vddci = max_limits->vddci;
+
+               if (ps->medium.mclk > max_limits->mclk)
+                       ps->medium.mclk = max_limits->mclk;
+               if (ps->medium.sclk > max_limits->sclk)
+                       ps->medium.sclk = max_limits->sclk;
+               if (ps->medium.vddc > max_limits->vddc)
+                       ps->medium.vddc = max_limits->vddc;
+               if (ps->medium.vddci > max_limits->vddci)
+                       ps->medium.vddci = max_limits->vddci;
+
+               if (ps->low.mclk > max_limits->mclk)
+                       ps->low.mclk = max_limits->mclk;
+               if (ps->low.sclk > max_limits->sclk)
+                       ps->low.sclk = max_limits->sclk;
+               if (ps->low.vddc > max_limits->vddc)
+                       ps->low.vddc = max_limits->vddc;
+               if (ps->low.vddci > max_limits->vddci)
+                       ps->low.vddci = max_limits->vddci;
+       }
+
+       /* XXX validate the min clocks required for display */
+
+       if (disable_mclk_switching) {
+               sclk = ps->low.sclk;
+               mclk = ps->high.mclk;
+               vddc = ps->low.vddc;
+               vddci = ps->high.vddci;
+       } else {
+               sclk = ps->low.sclk;
+               mclk = ps->low.mclk;
+               vddc = ps->low.vddc;
+               vddci = ps->low.vddci;
+       }
+
+       /* adjusted low state */
+       ps->low.sclk = sclk;
+       ps->low.mclk = mclk;
+       ps->low.vddc = vddc;
+       ps->low.vddci = vddci;
+
+       btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk,
+                                 &ps->low.sclk, &ps->low.mclk);
+
+       /* adjusted medium, high states */
+       if (ps->medium.sclk < ps->low.sclk)
+               ps->medium.sclk = ps->low.sclk;
+       if (ps->medium.vddc < ps->low.vddc)
+               ps->medium.vddc = ps->low.vddc;
+       if (ps->high.sclk < ps->medium.sclk)
+               ps->high.sclk = ps->medium.sclk;
+       if (ps->high.vddc < ps->medium.vddc)
+               ps->high.vddc = ps->medium.vddc;
+
+       if (disable_mclk_switching) {
+               mclk = ps->low.mclk;
+               if (mclk < ps->medium.mclk)
+                       mclk = ps->medium.mclk;
+               if (mclk < ps->high.mclk)
+                       mclk = ps->high.mclk;
+               ps->low.mclk = mclk;
+               ps->low.vddci = vddci;
+               ps->medium.mclk = mclk;
+               ps->medium.vddci = vddci;
+               ps->high.mclk = mclk;
+               ps->high.vddci = vddci;
+       } else {
+               if (ps->medium.mclk < ps->low.mclk)
+                       ps->medium.mclk = ps->low.mclk;
+               if (ps->medium.vddci < ps->low.vddci)
+                       ps->medium.vddci = ps->low.vddci;
+               if (ps->high.mclk < ps->medium.mclk)
+                       ps->high.mclk = ps->medium.mclk;
+               if (ps->high.vddci < ps->medium.vddci)
+                       ps->high.vddci = ps->medium.vddci;
+       }
+
+       btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk,
+                                 &ps->medium.sclk, &ps->medium.mclk);
+       btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk,
+                                 &ps->high.sclk, &ps->high.mclk);
+
+       btc_adjust_clock_combinations(rdev, max_limits, &ps->low);
+       btc_adjust_clock_combinations(rdev, max_limits, &ps->medium);
+       btc_adjust_clock_combinations(rdev, max_limits, &ps->high);
+
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                          ps->low.sclk, max_limits->vddc, &ps->low.vddc);
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                          ps->low.mclk, max_limits->vddci, &ps->low.vddci);
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                          ps->low.mclk, max_limits->vddc, &ps->low.vddc);
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk,
+                                          rdev->clock.current_dispclk, max_limits->vddc, &ps->low.vddc);
+
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                          ps->medium.sclk, max_limits->vddc, &ps->medium.vddc);
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                          ps->medium.mclk, max_limits->vddci, &ps->medium.vddci);
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                          ps->medium.mclk, max_limits->vddc, &ps->medium.vddc);
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk,
+                                          rdev->clock.current_dispclk, max_limits->vddc, &ps->medium.vddc);
+
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                          ps->high.sclk, max_limits->vddc, &ps->high.vddc);
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                          ps->high.mclk, max_limits->vddci, &ps->high.vddci);
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                          ps->high.mclk, max_limits->vddc, &ps->high.vddc);
+       btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk,
+                                          rdev->clock.current_dispclk, max_limits->vddc, &ps->high.vddc);
+
+       btc_apply_voltage_delta_rules(rdev, max_limits->vddc, max_limits->vddci,
+                                     &ps->low.vddc, &ps->low.vddci);
+       btc_apply_voltage_delta_rules(rdev, max_limits->vddc, max_limits->vddci,
+                                     &ps->medium.vddc, &ps->medium.vddci);
+       btc_apply_voltage_delta_rules(rdev, max_limits->vddc, max_limits->vddci,
+                                     &ps->high.vddc, &ps->high.vddci);
+
+       if ((ps->high.vddc <= rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc) &&
+           (ps->medium.vddc <= rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc) &&
+           (ps->low.vddc <= rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc))
+               ps->dc_compatible = true;
+       else
+               ps->dc_compatible = false;
+
+       if (ps->low.vddc < rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2)
+               ps->low.flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2;
+       if (ps->medium.vddc < rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2)
+               ps->medium.flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2;
+       if (ps->high.vddc < rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2)
+               ps->high.flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2;
+}
+
+static void btc_update_current_ps(struct radeon_device *rdev,
+                                 struct radeon_ps *rps)
+{
+       struct rv7xx_ps *new_ps = rv770_get_ps(rps);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       eg_pi->current_rps = *rps;
+       eg_pi->current_ps = *new_ps;
+       eg_pi->current_rps.ps_priv = &eg_pi->current_ps;
+}
+
+static void btc_update_requested_ps(struct radeon_device *rdev,
+                                   struct radeon_ps *rps)
+{
+       struct rv7xx_ps *new_ps = rv770_get_ps(rps);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       eg_pi->requested_rps = *rps;
+       eg_pi->requested_ps = *new_ps;
+       eg_pi->requested_rps.ps_priv = &eg_pi->requested_ps;
+}
+
+void btc_dpm_reset_asic(struct radeon_device *rdev)
+{
+       rv770_restrict_performance_levels_before_switch(rdev);
+       btc_disable_ulv(rdev);
+       btc_set_boot_state_timing(rdev);
+       rv770_set_boot_state(rdev);
+}
+
+int btc_dpm_pre_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps requested_ps = *rdev->pm.dpm.requested_ps;
+       struct radeon_ps *new_ps = &requested_ps;
+
+       btc_update_requested_ps(rdev, new_ps);
+
+       btc_apply_state_adjust_rules(rdev, &eg_pi->requested_rps);
+
+       return 0;
+}
+
+int btc_dpm_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *new_ps = &eg_pi->requested_rps;
+       struct radeon_ps *old_ps = &eg_pi->current_rps;
+       int ret;
+
+       ret = btc_disable_ulv(rdev);
+       btc_set_boot_state_timing(rdev);
+       ret = rv770_restrict_performance_levels_before_switch(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_restrict_performance_levels_before_switch failed\n");
+               return ret;
+       }
+       if (eg_pi->pcie_performance_request)
+               cypress_notify_link_speed_change_before_state_change(rdev, new_ps, old_ps);
+
+       rv770_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps);
+       ret = rv770_halt_smc(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_halt_smc failed\n");
+               return ret;
+       }
+       btc_set_at_for_uvd(rdev, new_ps);
+       if (eg_pi->smu_uvd_hs)
+               btc_notify_uvd_to_smc(rdev, new_ps);
+       ret = cypress_upload_sw_state(rdev, new_ps);
+       if (ret) {
+               DRM_ERROR("cypress_upload_sw_state failed\n");
+               return ret;
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = cypress_upload_mc_reg_table(rdev, new_ps);
+               if (ret) {
+                       DRM_ERROR("cypress_upload_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+
+       cypress_program_memory_timing_parameters(rdev, new_ps);
+
+       ret = rv770_resume_smc(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_resume_smc failed\n");
+               return ret;
+       }
+       ret = rv770_set_sw_state(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_set_sw_state failed\n");
+               return ret;
+       }
+       rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+
+       if (eg_pi->pcie_performance_request)
+               cypress_notify_link_speed_change_after_state_change(rdev, new_ps, old_ps);
+
+       ret = btc_set_power_state_conditionally_enable_ulv(rdev, new_ps);
+       if (ret) {
+               DRM_ERROR("btc_set_power_state_conditionally_enable_ulv failed\n");
+               return ret;
+       }
+
+       ret = rv770_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
+       if (ret) {
+               DRM_ERROR("rv770_dpm_force_performance_level failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+void btc_dpm_post_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *new_ps = &eg_pi->requested_rps;
+
+       btc_update_current_ps(rdev, new_ps);
+}
+
+int btc_dpm_enable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+       int ret;
+
+       if (pi->gfx_clock_gating)
+               btc_cg_clock_gating_default(rdev);
+
+       if (btc_dpm_enabled(rdev))
+               return -EINVAL;
+
+       if (pi->mg_clock_gating)
+               btc_mg_clock_gating_default(rdev);
+
+       if (eg_pi->ls_clock_gating)
+               btc_ls_clock_gating_default(rdev);
+
+       if (pi->voltage_control) {
+               rv770_enable_voltage_control(rdev, true);
+               ret = cypress_construct_voltage_tables(rdev);
+               if (ret) {
+                       DRM_ERROR("cypress_construct_voltage_tables failed\n");
+                       return ret;
+               }
+       }
+
+       if (pi->mvdd_control) {
+               ret = cypress_get_mvdd_configuration(rdev);
+               if (ret) {
+                       DRM_ERROR("cypress_get_mvdd_configuration failed\n");
+                       return ret;
+               }
+       }
+
+       if (eg_pi->dynamic_ac_timing) {
+               ret = btc_initialize_mc_reg_table(rdev);
+               if (ret)
+                       eg_pi->dynamic_ac_timing = false;
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
+               rv770_enable_backbias(rdev, true);
+
+       if (pi->dynamic_ss)
+               cypress_enable_spread_spectrum(rdev, true);
+
+       if (pi->thermal_protection)
+               rv770_enable_thermal_protection(rdev, true);
+
+       rv770_setup_bsp(rdev);
+       rv770_program_git(rdev);
+       rv770_program_tp(rdev);
+       rv770_program_tpp(rdev);
+       rv770_program_sstp(rdev);
+       rv770_program_engine_speed_parameters(rdev);
+       cypress_enable_display_gap(rdev);
+       rv770_program_vc(rdev);
+
+       if (pi->dynamic_pcie_gen2)
+               btc_enable_dynamic_pcie_gen2(rdev, true);
+
+       ret = rv770_upload_firmware(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_upload_firmware failed\n");
+               return ret;
+       }
+       ret = cypress_get_table_locations(rdev);
+       if (ret) {
+               DRM_ERROR("cypress_get_table_locations failed\n");
+               return ret;
+       }
+       ret = btc_init_smc_table(rdev, boot_ps);
+       if (ret)
+               return ret;
+
+       if (eg_pi->dynamic_ac_timing) {
+               ret = cypress_populate_mc_reg_table(rdev, boot_ps);
+               if (ret) {
+                       DRM_ERROR("cypress_populate_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+
+       cypress_program_response_times(rdev);
+       r7xx_start_smc(rdev);
+       ret = cypress_notify_smc_display_change(rdev, false);
+       if (ret) {
+               DRM_ERROR("cypress_notify_smc_display_change failed\n");
+               return ret;
+       }
+       cypress_enable_sclk_control(rdev, true);
+
+       if (eg_pi->memory_transition)
+               cypress_enable_mclk_control(rdev, true);
+
+       cypress_start_dpm(rdev);
+
+       if (pi->gfx_clock_gating)
+               btc_cg_clock_gating_enable(rdev, true);
+
+       if (pi->mg_clock_gating)
+               btc_mg_clock_gating_enable(rdev, true);
+
+       if (eg_pi->ls_clock_gating)
+               btc_ls_clock_gating_enable(rdev, true);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               PPSMC_Result result;
+
+               ret = rv770_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+               if (ret)
+                       return ret;
+               rdev->irq.dpm_thermal = true;
+               radeon_irq_set(rdev);
+               result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt);
+
+               if (result != PPSMC_Result_OK)
+                       DRM_DEBUG_KMS("Could not enable thermal interrupts.\n");
+       }
+
+       rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+
+       btc_init_stutter_mode(rdev);
+
+       btc_update_current_ps(rdev, rdev->pm.dpm.boot_ps);
+
+       return 0;
+};
+
+void btc_dpm_disable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       if (!btc_dpm_enabled(rdev))
+               return;
+
+       rv770_clear_vc(rdev);
+
+       if (pi->thermal_protection)
+               rv770_enable_thermal_protection(rdev, false);
+
+       if (pi->dynamic_pcie_gen2)
+               btc_enable_dynamic_pcie_gen2(rdev, false);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               rdev->irq.dpm_thermal = false;
+               radeon_irq_set(rdev);
+       }
+
+       if (pi->gfx_clock_gating)
+               btc_cg_clock_gating_enable(rdev, false);
+
+       if (pi->mg_clock_gating)
+               btc_mg_clock_gating_enable(rdev, false);
+
+       if (eg_pi->ls_clock_gating)
+               btc_ls_clock_gating_enable(rdev, false);
+
+       rv770_stop_dpm(rdev);
+       btc_reset_to_default(rdev);
+       btc_stop_smc(rdev);
+       cypress_enable_spread_spectrum(rdev, false);
+
+       btc_update_current_ps(rdev, rdev->pm.dpm.boot_ps);
+}
+
+void btc_dpm_setup_asic(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       rv770_get_memory_type(rdev);
+       rv740_read_clock_registers(rdev);
+       btc_read_arb_registers(rdev);
+       rv770_read_voltage_smio_registers(rdev);
+
+       if (eg_pi->pcie_performance_request)
+               cypress_advertise_gen2_capability(rdev);
+
+       rv770_get_pcie_gen2_status(rdev);
+       rv770_enable_acpi_pm(rdev);
+}
+
+int btc_dpm_init(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi;
+       struct evergreen_power_info *eg_pi;
+       int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
+       u16 data_offset, size;
+       u8 frev, crev;
+       struct atom_clock_dividers dividers;
+       int ret;
+
+       eg_pi = kzalloc(sizeof(struct evergreen_power_info), GFP_KERNEL);
+       if (eg_pi == NULL)
+               return -ENOMEM;
+       rdev->pm.dpm.priv = eg_pi;
+       pi = &eg_pi->rv7xx;
+
+       rv770_get_max_vddc(rdev);
+
+       eg_pi->ulv.supported = false;
+       pi->acpi_vddc = 0;
+       eg_pi->acpi_vddci = 0;
+       pi->min_vddc_in_table = 0;
+       pi->max_vddc_in_table = 0;
+
+       ret = rv7xx_parse_power_table(rdev);
+       if (ret)
+               return ret;
+       ret = r600_parse_extended_power_table(rdev);
+       if (ret)
+               return ret;
+
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries =
+               kzalloc(4 * sizeof(struct radeon_clock_voltage_dependency_entry), GFP_KERNEL);
+       if (!rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) {
+               r600_free_extended_power_table(rdev);
+               return -ENOMEM;
+       }
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.count = 4;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].clk = 0;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].v = 0;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].clk = 36000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].v = 800;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].clk = 54000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].v = 800;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].clk = 72000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].v = 800;
+
+       if (rdev->pm.dpm.voltage_response_time == 0)
+               rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT;
+       if (rdev->pm.dpm.backbias_response_time == 0)
+               rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            0, false, &dividers);
+       if (ret)
+               pi->ref_div = dividers.ref_div + 1;
+       else
+               pi->ref_div = R600_REFERENCEDIVIDER_DFLT;
+
+       pi->mclk_strobe_mode_threshold = 40000;
+       pi->mclk_edc_enable_threshold = 40000;
+       eg_pi->mclk_edc_wr_enable_threshold = 40000;
+
+       pi->rlp = RV770_RLP_DFLT;
+       pi->rmp = RV770_RMP_DFLT;
+       pi->lhp = RV770_LHP_DFLT;
+       pi->lmp = RV770_LMP_DFLT;
+
+       eg_pi->ats[0].rlp = RV770_RLP_DFLT;
+       eg_pi->ats[0].rmp = RV770_RMP_DFLT;
+       eg_pi->ats[0].lhp = RV770_LHP_DFLT;
+       eg_pi->ats[0].lmp = RV770_LMP_DFLT;
+
+       eg_pi->ats[1].rlp = BTC_RLP_UVD_DFLT;
+       eg_pi->ats[1].rmp = BTC_RMP_UVD_DFLT;
+       eg_pi->ats[1].lhp = BTC_LHP_UVD_DFLT;
+       eg_pi->ats[1].lmp = BTC_LMP_UVD_DFLT;
+
+       eg_pi->smu_uvd_hs = true;
+
+       pi->voltage_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, 0);
+
+       pi->mvdd_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC, 0);
+
+       eg_pi->vddci_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI, 0);
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                   &frev, &crev, &data_offset)) {
+               pi->sclk_ss = true;
+               pi->mclk_ss = true;
+               pi->dynamic_ss = true;
+       } else {
+               pi->sclk_ss = false;
+               pi->mclk_ss = false;
+               pi->dynamic_ss = true;
+       }
+
+       pi->asi = RV770_ASI_DFLT;
+       pi->pasi = CYPRESS_HASI_DFLT;
+       pi->vrc = CYPRESS_VRC_DFLT;
+
+       pi->power_gating = false;
+
+       pi->gfx_clock_gating = true;
+
+       pi->mg_clock_gating = true;
+       pi->mgcgtssm = true;
+       eg_pi->ls_clock_gating = false;
+       eg_pi->sclk_deep_sleep = false;
+
+       pi->dynamic_pcie_gen2 = true;
+
+       if (pi->gfx_clock_gating &&
+           (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE))
+               pi->thermal_protection = true;
+       else
+               pi->thermal_protection = false;
+
+       pi->display_gap = true;
+
+       if (rdev->flags & RADEON_IS_MOBILITY)
+               pi->dcodt = true;
+       else
+               pi->dcodt = false;
+
+       pi->ulps = true;
+
+       eg_pi->dynamic_ac_timing = true;
+       eg_pi->abm = true;
+       eg_pi->mcls = true;
+       eg_pi->light_sleep = true;
+       eg_pi->memory_transition = true;
+#if defined(CONFIG_ACPI)
+       eg_pi->pcie_performance_request =
+               radeon_acpi_is_pcie_performance_request_supported(rdev);
+#else
+       eg_pi->pcie_performance_request = false;
+#endif
+
+       if (rdev->family == CHIP_BARTS)
+               eg_pi->dll_default_on = true;
+       else
+               eg_pi->dll_default_on = false;
+
+       eg_pi->sclk_deep_sleep = false;
+       if (ASIC_IS_LOMBOK(rdev))
+               pi->mclk_stutter_mode_threshold = 30000;
+       else
+               pi->mclk_stutter_mode_threshold = 0;
+
+       pi->sram_end = SMC_RAM_END;
+
+       rdev->pm.dpm.dyn_state.mclk_sclk_ratio = 4;
+       rdev->pm.dpm.dyn_state.vddc_vddci_delta = 200;
+       rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2 = 900;
+       rdev->pm.dpm.dyn_state.valid_sclk_values.count = ARRAY_SIZE(btc_valid_sclk);
+       rdev->pm.dpm.dyn_state.valid_sclk_values.values = btc_valid_sclk;
+       rdev->pm.dpm.dyn_state.valid_mclk_values.count = 0;
+       rdev->pm.dpm.dyn_state.valid_mclk_values.values = NULL;
+
+       if (rdev->family == CHIP_TURKS)
+               rdev->pm.dpm.dyn_state.sclk_mclk_delta = 15000;
+       else
+               rdev->pm.dpm.dyn_state.sclk_mclk_delta = 10000;
+
+       return 0;
+}
+
+void btc_dpm_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               kfree(rdev->pm.dpm.ps[i].ps_priv);
+       }
+       kfree(rdev->pm.dpm.ps);
+       kfree(rdev->pm.dpm.priv);
+       kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries);
+       r600_free_extended_power_table(rdev);
+}
+
+u32 btc_dpm_get_sclk(struct radeon_device *rdev, bool low)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct rv7xx_ps *requested_state = rv770_get_ps(&eg_pi->requested_rps);
+
+       if (low)
+               return requested_state->low.sclk;
+       else
+               return requested_state->high.sclk;
+}
+
+u32 btc_dpm_get_mclk(struct radeon_device *rdev, bool low)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct rv7xx_ps *requested_state = rv770_get_ps(&eg_pi->requested_rps);
+
+       if (low)
+               return requested_state->low.mclk;
+       else
+               return requested_state->high.mclk;
+}
diff --git a/drivers/gpu/drm/radeon/btc_dpm.h b/drivers/gpu/drm/radeon/btc_dpm.h
new file mode 100644 (file)
index 0000000..1a15e0e
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __BTC_DPM_H__
+#define __BTC_DPM_H__
+
+#define BTC_RLP_UVD_DFLT                              20
+#define BTC_RMP_UVD_DFLT                              50
+#define BTC_LHP_UVD_DFLT                              50
+#define BTC_LMP_UVD_DFLT                              20
+#define BARTS_MGCGCGTSSMCTRL_DFLT                     0x81944000
+#define TURKS_MGCGCGTSSMCTRL_DFLT                     0x6e944000
+#define CAICOS_MGCGCGTSSMCTRL_DFLT                    0x46944040
+#define BTC_CGULVPARAMETER_DFLT                       0x00040035
+#define BTC_CGULVCONTROL_DFLT                         0x00001450
+
+extern u32 btc_valid_sclk[40];
+
+void btc_read_arb_registers(struct radeon_device *rdev);
+void btc_program_mgcg_hw_sequence(struct radeon_device *rdev,
+                                 const u32 *sequence, u32 count);
+void btc_skip_blacklist_clocks(struct radeon_device *rdev,
+                              const u32 max_sclk, const u32 max_mclk,
+                              u32 *sclk, u32 *mclk);
+void btc_adjust_clock_combinations(struct radeon_device *rdev,
+                                  const struct radeon_clock_and_voltage_limits *max_limits,
+                                  struct rv7xx_pl *pl);
+void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table,
+                                       u32 clock, u16 max_voltage, u16 *voltage);
+void btc_apply_voltage_delta_rules(struct radeon_device *rdev,
+                                  u16 max_vddc, u16 max_vddci,
+                                  u16 *vddc, u16 *vddci);
+bool btc_dpm_enabled(struct radeon_device *rdev);
+int btc_reset_to_default(struct radeon_device *rdev);
+void btc_notify_uvd_to_smc(struct radeon_device *rdev,
+                          struct radeon_ps *radeon_new_state);
+
+#endif
diff --git a/drivers/gpu/drm/radeon/btcd.h b/drivers/gpu/drm/radeon/btcd.h
new file mode 100644 (file)
index 0000000..29e32de
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2010 Advanced Micro Devices, 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: Alex Deucher
+ */
+#ifndef _BTCD_H_
+#define _BTCD_H_
+
+/* pm registers */
+
+#define GENERAL_PWRMGT                                  0x63c
+#       define GLOBAL_PWRMGT_EN                         (1 << 0)
+#       define STATIC_PM_EN                             (1 << 1)
+#       define THERMAL_PROTECTION_DIS                   (1 << 2)
+#       define THERMAL_PROTECTION_TYPE                  (1 << 3)
+#       define ENABLE_GEN2PCIE                          (1 << 4)
+#       define ENABLE_GEN2XSP                           (1 << 5)
+#       define SW_SMIO_INDEX(x)                         ((x) << 6)
+#       define SW_SMIO_INDEX_MASK                       (3 << 6)
+#       define SW_SMIO_INDEX_SHIFT                      6
+#       define LOW_VOLT_D2_ACPI                         (1 << 8)
+#       define LOW_VOLT_D3_ACPI                         (1 << 9)
+#       define VOLT_PWRMGT_EN                           (1 << 10)
+#       define BACKBIAS_PAD_EN                          (1 << 18)
+#       define BACKBIAS_VALUE                           (1 << 19)
+#       define DYN_SPREAD_SPECTRUM_EN                   (1 << 23)
+#       define AC_DC_SW                                 (1 << 24)
+
+#define        CG_BIF_REQ_AND_RSP                              0x7f4
+#define                CG_CLIENT_REQ(x)                        ((x) << 0)
+#define                CG_CLIENT_REQ_MASK                      (0xff << 0)
+#define                CG_CLIENT_REQ_SHIFT                     0
+#define                CG_CLIENT_RESP(x)                       ((x) << 8)
+#define                CG_CLIENT_RESP_MASK                     (0xff << 8)
+#define                CG_CLIENT_RESP_SHIFT                    8
+#define                CLIENT_CG_REQ(x)                        ((x) << 16)
+#define                CLIENT_CG_REQ_MASK                      (0xff << 16)
+#define                CLIENT_CG_REQ_SHIFT                     16
+#define                CLIENT_CG_RESP(x)                       ((x) << 24)
+#define                CLIENT_CG_RESP_MASK                     (0xff << 24)
+#define                CLIENT_CG_RESP_SHIFT                    24
+
+#define        SCLK_PSKIP_CNTL                                 0x8c0
+#define                PSKIP_ON_ALLOW_STOP_HI(x)               ((x) << 16)
+#define                PSKIP_ON_ALLOW_STOP_HI_MASK             (0xff << 16)
+#define                PSKIP_ON_ALLOW_STOP_HI_SHIFT            16
+
+#define        CG_ULV_CONTROL                                  0x8c8
+#define        CG_ULV_PARAMETER                                0x8cc
+
+#define        MC_ARB_DRAM_TIMING                              0x2774
+#define        MC_ARB_DRAM_TIMING2                             0x2778
+
+#define        MC_ARB_RFSH_RATE                                0x27b0
+#define                POWERMODE0(x)                           ((x) << 0)
+#define                POWERMODE0_MASK                         (0xff << 0)
+#define                POWERMODE0_SHIFT                        0
+#define                POWERMODE1(x)                           ((x) << 8)
+#define                POWERMODE1_MASK                         (0xff << 8)
+#define                POWERMODE1_SHIFT                        8
+#define                POWERMODE2(x)                           ((x) << 16)
+#define                POWERMODE2_MASK                         (0xff << 16)
+#define                POWERMODE2_SHIFT                        16
+#define                POWERMODE3(x)                           ((x) << 24)
+#define                POWERMODE3_MASK                         (0xff << 24)
+#define                POWERMODE3_SHIFT                        24
+
+#define MC_ARB_BURST_TIME                               0x2808
+#define                STATE0(x)                               ((x) << 0)
+#define                STATE0_MASK                             (0x1f << 0)
+#define                STATE0_SHIFT                            0
+#define                STATE1(x)                               ((x) << 5)
+#define                STATE1_MASK                             (0x1f << 5)
+#define                STATE1_SHIFT                            5
+#define                STATE2(x)                               ((x) << 10)
+#define                STATE2_MASK                             (0x1f << 10)
+#define                STATE2_SHIFT                            10
+#define                STATE3(x)                               ((x) << 15)
+#define                STATE3_MASK                             (0x1f << 15)
+#define                STATE3_SHIFT                            15
+
+#define MC_SEQ_RAS_TIMING                               0x28a0
+#define MC_SEQ_CAS_TIMING                               0x28a4
+#define MC_SEQ_MISC_TIMING                              0x28a8
+#define MC_SEQ_MISC_TIMING2                             0x28ac
+
+#define MC_SEQ_RD_CTL_D0                                0x28b4
+#define MC_SEQ_RD_CTL_D1                                0x28b8
+#define MC_SEQ_WR_CTL_D0                                0x28bc
+#define MC_SEQ_WR_CTL_D1                                0x28c0
+
+#define MC_PMG_AUTO_CFG                                 0x28d4
+
+#define MC_SEQ_STATUS_M                                 0x29f4
+#       define PMG_PWRSTATE                             (1 << 16)
+
+#define MC_SEQ_MISC0                                    0x2a00
+#define         MC_SEQ_MISC0_GDDR5_SHIFT                28
+#define         MC_SEQ_MISC0_GDDR5_MASK                 0xf0000000
+#define         MC_SEQ_MISC0_GDDR5_VALUE                5
+#define MC_SEQ_MISC1                                    0x2a04
+#define MC_SEQ_RESERVE_M                                0x2a08
+#define MC_PMG_CMD_EMRS                                 0x2a0c
+
+#define MC_SEQ_MISC3                                    0x2a2c
+
+#define MC_SEQ_MISC5                                    0x2a54
+#define MC_SEQ_MISC6                                    0x2a58
+
+#define MC_SEQ_MISC7                                    0x2a64
+
+#define MC_SEQ_CG                                       0x2a68
+#define                CG_SEQ_REQ(x)                           ((x) << 0)
+#define                CG_SEQ_REQ_MASK                         (0xff << 0)
+#define                CG_SEQ_REQ_SHIFT                        0
+#define                CG_SEQ_RESP(x)                          ((x) << 8)
+#define                CG_SEQ_RESP_MASK                        (0xff << 8)
+#define                CG_SEQ_RESP_SHIFT                       8
+#define                SEQ_CG_REQ(x)                           ((x) << 16)
+#define                SEQ_CG_REQ_MASK                         (0xff << 16)
+#define                SEQ_CG_REQ_SHIFT                        16
+#define                SEQ_CG_RESP(x)                          ((x) << 24)
+#define                SEQ_CG_RESP_MASK                        (0xff << 24)
+#define                SEQ_CG_RESP_SHIFT                       24
+#define MC_SEQ_RAS_TIMING_LP                            0x2a6c
+#define MC_SEQ_CAS_TIMING_LP                            0x2a70
+#define MC_SEQ_MISC_TIMING_LP                           0x2a74
+#define MC_SEQ_MISC_TIMING2_LP                          0x2a78
+#define MC_SEQ_WR_CTL_D0_LP                             0x2a7c
+#define MC_SEQ_WR_CTL_D1_LP                             0x2a80
+#define MC_SEQ_PMG_CMD_EMRS_LP                          0x2a84
+#define MC_SEQ_PMG_CMD_MRS_LP                           0x2a88
+
+#define MC_PMG_CMD_MRS                                  0x2aac
+
+#define MC_SEQ_RD_CTL_D0_LP                             0x2b1c
+#define MC_SEQ_RD_CTL_D1_LP                             0x2b20
+
+#define MC_PMG_CMD_MRS1                                 0x2b44
+#define MC_SEQ_PMG_CMD_MRS1_LP                          0x2b48
+
+#define        LB_SYNC_RESET_SEL                               0x6b28
+#define                LB_SYNC_RESET_SEL_MASK                  (3 << 0)
+#define                LB_SYNC_RESET_SEL_SHIFT                 0
+
+/* PCIE link stuff */
+#define PCIE_LC_SPEED_CNTL                                0xa4 /* PCIE_P */
+#       define LC_GEN2_EN_STRAP                           (1 << 0)
+#       define LC_TARGET_LINK_SPEED_OVERRIDE_EN           (1 << 1)
+#       define LC_FORCE_EN_HW_SPEED_CHANGE                (1 << 5)
+#       define LC_FORCE_DIS_HW_SPEED_CHANGE               (1 << 6)
+#       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK      (0x3 << 8)
+#       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT     3
+#       define LC_CURRENT_DATA_RATE                       (1 << 11)
+#       define LC_HW_VOLTAGE_IF_CONTROL(x)                ((x) << 12)
+#       define LC_HW_VOLTAGE_IF_CONTROL_MASK              (3 << 12)
+#       define LC_HW_VOLTAGE_IF_CONTROL_SHIFT             12
+#       define LC_VOLTAGE_TIMER_SEL_MASK                  (0xf << 14)
+#       define LC_CLR_FAILED_SPD_CHANGE_CNT               (1 << 21)
+#       define LC_OTHER_SIDE_EVER_SENT_GEN2               (1 << 23)
+#       define LC_OTHER_SIDE_SUPPORTS_GEN2                (1 << 24)
+
+#endif
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
new file mode 100644 (file)
index 0000000..ed1d910
--- /dev/null
@@ -0,0 +1,6987 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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: Alex Deucher
+ */
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include "drmP.h"
+#include "radeon.h"
+#include "radeon_asic.h"
+#include "cikd.h"
+#include "atom.h"
+#include "cik_blit_shaders.h"
+
+/* GFX */
+#define CIK_PFP_UCODE_SIZE 2144
+#define CIK_ME_UCODE_SIZE 2144
+#define CIK_CE_UCODE_SIZE 2144
+/* compute */
+#define CIK_MEC_UCODE_SIZE 4192
+/* interrupts */
+#define BONAIRE_RLC_UCODE_SIZE 2048
+#define KB_RLC_UCODE_SIZE 2560
+#define KV_RLC_UCODE_SIZE 2560
+/* gddr controller */
+#define CIK_MC_UCODE_SIZE 7866
+/* sdma */
+#define CIK_SDMA_UCODE_SIZE 1050
+#define CIK_SDMA_UCODE_VERSION 64
+
+MODULE_FIRMWARE("radeon/BONAIRE_pfp.bin");
+MODULE_FIRMWARE("radeon/BONAIRE_me.bin");
+MODULE_FIRMWARE("radeon/BONAIRE_ce.bin");
+MODULE_FIRMWARE("radeon/BONAIRE_mec.bin");
+MODULE_FIRMWARE("radeon/BONAIRE_mc.bin");
+MODULE_FIRMWARE("radeon/BONAIRE_rlc.bin");
+MODULE_FIRMWARE("radeon/BONAIRE_sdma.bin");
+MODULE_FIRMWARE("radeon/KAVERI_pfp.bin");
+MODULE_FIRMWARE("radeon/KAVERI_me.bin");
+MODULE_FIRMWARE("radeon/KAVERI_ce.bin");
+MODULE_FIRMWARE("radeon/KAVERI_mec.bin");
+MODULE_FIRMWARE("radeon/KAVERI_rlc.bin");
+MODULE_FIRMWARE("radeon/KAVERI_sdma.bin");
+MODULE_FIRMWARE("radeon/KABINI_pfp.bin");
+MODULE_FIRMWARE("radeon/KABINI_me.bin");
+MODULE_FIRMWARE("radeon/KABINI_ce.bin");
+MODULE_FIRMWARE("radeon/KABINI_mec.bin");
+MODULE_FIRMWARE("radeon/KABINI_rlc.bin");
+MODULE_FIRMWARE("radeon/KABINI_sdma.bin");
+
+extern int r600_ih_ring_alloc(struct radeon_device *rdev);
+extern void r600_ih_ring_fini(struct radeon_device *rdev);
+extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save);
+extern void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save);
+extern bool evergreen_is_display_hung(struct radeon_device *rdev);
+extern void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
+extern void si_rlc_fini(struct radeon_device *rdev);
+extern int si_rlc_init(struct radeon_device *rdev);
+static void cik_rlc_stop(struct radeon_device *rdev);
+
+/*
+ * Indirect registers accessor
+ */
+u32 cik_pciep_rreg(struct radeon_device *rdev, u32 reg)
+{
+       u32 r;
+
+       WREG32(PCIE_INDEX, reg);
+       (void)RREG32(PCIE_INDEX);
+       r = RREG32(PCIE_DATA);
+       return r;
+}
+
+void cik_pciep_wreg(struct radeon_device *rdev, u32 reg, u32 v)
+{
+       WREG32(PCIE_INDEX, reg);
+       (void)RREG32(PCIE_INDEX);
+       WREG32(PCIE_DATA, v);
+       (void)RREG32(PCIE_DATA);
+}
+
+static const u32 bonaire_golden_spm_registers[] =
+{
+       0x30800, 0xe0ffffff, 0xe0000000
+};
+
+static const u32 bonaire_golden_common_registers[] =
+{
+       0xc770, 0xffffffff, 0x00000800,
+       0xc774, 0xffffffff, 0x00000800,
+       0xc798, 0xffffffff, 0x00007fbf,
+       0xc79c, 0xffffffff, 0x00007faf
+};
+
+static const u32 bonaire_golden_registers[] =
+{
+       0x3354, 0x00000333, 0x00000333,
+       0x3350, 0x000c0fc0, 0x00040200,
+       0x9a10, 0x00010000, 0x00058208,
+       0x3c000, 0xffff1fff, 0x00140000,
+       0x3c200, 0xfdfc0fff, 0x00000100,
+       0x3c234, 0x40000000, 0x40000200,
+       0x9830, 0xffffffff, 0x00000000,
+       0x9834, 0xf00fffff, 0x00000400,
+       0x9838, 0x0002021c, 0x00020200,
+       0xc78, 0x00000080, 0x00000000,
+       0x5bb0, 0x000000f0, 0x00000070,
+       0x5bc0, 0xf0311fff, 0x80300000,
+       0x98f8, 0x73773777, 0x12010001,
+       0x350c, 0x00810000, 0x408af000,
+       0x7030, 0x31000111, 0x00000011,
+       0x2f48, 0x73773777, 0x12010001,
+       0x220c, 0x00007fb6, 0x0021a1b1,
+       0x2210, 0x00007fb6, 0x002021b1,
+       0x2180, 0x00007fb6, 0x00002191,
+       0x2218, 0x00007fb6, 0x002121b1,
+       0x221c, 0x00007fb6, 0x002021b1,
+       0x21dc, 0x00007fb6, 0x00002191,
+       0x21e0, 0x00007fb6, 0x00002191,
+       0x3628, 0x0000003f, 0x0000000a,
+       0x362c, 0x0000003f, 0x0000000a,
+       0x2ae4, 0x00073ffe, 0x000022a2,
+       0x240c, 0x000007ff, 0x00000000,
+       0x8a14, 0xf000003f, 0x00000007,
+       0x8bf0, 0x00002001, 0x00000001,
+       0x8b24, 0xffffffff, 0x00ffffff,
+       0x30a04, 0x0000ff0f, 0x00000000,
+       0x28a4c, 0x07ffffff, 0x06000000,
+       0x4d8, 0x00000fff, 0x00000100,
+       0x3e78, 0x00000001, 0x00000002,
+       0x9100, 0x03000000, 0x0362c688,
+       0x8c00, 0x000000ff, 0x00000001,
+       0xe40, 0x00001fff, 0x00001fff,
+       0x9060, 0x0000007f, 0x00000020,
+       0x9508, 0x00010000, 0x00010000,
+       0xac14, 0x000003ff, 0x000000f3,
+       0xac0c, 0xffffffff, 0x00001032
+};
+
+static const u32 bonaire_mgcg_cgcg_init[] =
+{
+       0xc420, 0xffffffff, 0xfffffffc,
+       0x30800, 0xffffffff, 0xe0000000,
+       0x3c2a0, 0xffffffff, 0x00000100,
+       0x3c208, 0xffffffff, 0x00000100,
+       0x3c2c0, 0xffffffff, 0xc0000100,
+       0x3c2c8, 0xffffffff, 0xc0000100,
+       0x3c2c4, 0xffffffff, 0xc0000100,
+       0x55e4, 0xffffffff, 0x00600100,
+       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,
+       0x3c000, 0xffffffff, 0x96e00200,
+       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 const u32 spectre_golden_spm_registers[] =
+{
+       0x30800, 0xe0ffffff, 0xe0000000
+};
+
+static const u32 spectre_golden_common_registers[] =
+{
+       0xc770, 0xffffffff, 0x00000800,
+       0xc774, 0xffffffff, 0x00000800,
+       0xc798, 0xffffffff, 0x00007fbf,
+       0xc79c, 0xffffffff, 0x00007faf
+};
+
+static const u32 spectre_golden_registers[] =
+{
+       0x3c000, 0xffff1fff, 0x96940200,
+       0x3c00c, 0xffff0001, 0xff000000,
+       0x3c200, 0xfffc0fff, 0x00000100,
+       0x6ed8, 0x00010101, 0x00010000,
+       0x9834, 0xf00fffff, 0x00000400,
+       0x9838, 0xfffffffc, 0x00020200,
+       0x5bb0, 0x000000f0, 0x00000070,
+       0x5bc0, 0xf0311fff, 0x80300000,
+       0x98f8, 0x73773777, 0x12010001,
+       0x9b7c, 0x00ff0000, 0x00fc0000,
+       0x2f48, 0x73773777, 0x12010001,
+       0x8a14, 0xf000003f, 0x00000007,
+       0x8b24, 0xffffffff, 0x00ffffff,
+       0x28350, 0x3f3f3fff, 0x00000082,
+       0x28355, 0x0000003f, 0x00000000,
+       0x3e78, 0x00000001, 0x00000002,
+       0x913c, 0xffff03df, 0x00000004,
+       0xc768, 0x00000008, 0x00000008,
+       0x8c00, 0x000008ff, 0x00000800,
+       0x9508, 0x00010000, 0x00010000,
+       0xac0c, 0xffffffff, 0x54763210,
+       0x214f8, 0x01ff01ff, 0x00000002,
+       0x21498, 0x007ff800, 0x00200000,
+       0x2015c, 0xffffffff, 0x00000f40,
+       0x30934, 0xffffffff, 0x00000001
+};
+
+static const u32 spectre_mgcg_cgcg_init[] =
+{
+       0xc420, 0xffffffff, 0xfffffffc,
+       0x30800, 0xffffffff, 0xe0000000,
+       0x3c2a0, 0xffffffff, 0x00000100,
+       0x3c208, 0xffffffff, 0x00000100,
+       0x3c2c0, 0xffffffff, 0x00000100,
+       0x3c2c8, 0xffffffff, 0x00000100,
+       0x3c2c4, 0xffffffff, 0x00000100,
+       0x55e4, 0xffffffff, 0x00600100,
+       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,
+       0x3c000, 0xffffffff, 0x96e00200,
+       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 const u32 kalindi_golden_spm_registers[] =
+{
+       0x30800, 0xe0ffffff, 0xe0000000
+};
+
+static const u32 kalindi_golden_common_registers[] =
+{
+       0xc770, 0xffffffff, 0x00000800,
+       0xc774, 0xffffffff, 0x00000800,
+       0xc798, 0xffffffff, 0x00007fbf,
+       0xc79c, 0xffffffff, 0x00007faf
+};
+
+static const u32 kalindi_golden_registers[] =
+{
+       0x3c000, 0xffffdfff, 0x6e944040,
+       0x55e4, 0xff607fff, 0xfc000100,
+       0x3c220, 0xff000fff, 0x00000100,
+       0x3c224, 0xff000fff, 0x00000100,
+       0x3c200, 0xfffc0fff, 0x00000100,
+       0x6ed8, 0x00010101, 0x00010000,
+       0x9830, 0xffffffff, 0x00000000,
+       0x9834, 0xf00fffff, 0x00000400,
+       0x5bb0, 0x000000f0, 0x00000070,
+       0x5bc0, 0xf0311fff, 0x80300000,
+       0x98f8, 0x73773777, 0x12010001,
+       0x98fc, 0xffffffff, 0x00000010,
+       0x9b7c, 0x00ff0000, 0x00fc0000,
+       0x8030, 0x00001f0f, 0x0000100a,
+       0x2f48, 0x73773777, 0x12010001,
+       0x2408, 0x000fffff, 0x000c007f,
+       0x8a14, 0xf000003f, 0x00000007,
+       0x8b24, 0x3fff3fff, 0x00ffcfff,
+       0x30a04, 0x0000ff0f, 0x00000000,
+       0x28a4c, 0x07ffffff, 0x06000000,
+       0x4d8, 0x00000fff, 0x00000100,
+       0x3e78, 0x00000001, 0x00000002,
+       0xc768, 0x00000008, 0x00000008,
+       0x8c00, 0x000000ff, 0x00000003,
+       0x214f8, 0x01ff01ff, 0x00000002,
+       0x21498, 0x007ff800, 0x00200000,
+       0x2015c, 0xffffffff, 0x00000f40,
+       0x88c4, 0x001f3ae3, 0x00000082,
+       0x88d4, 0x0000001f, 0x00000010,
+       0x30934, 0xffffffff, 0x00000000
+};
+
+static const u32 kalindi_mgcg_cgcg_init[] =
+{
+       0xc420, 0xffffffff, 0xfffffffc,
+       0x30800, 0xffffffff, 0xe0000000,
+       0x3c2a0, 0xffffffff, 0x00000100,
+       0x3c208, 0xffffffff, 0x00000100,
+       0x3c2c0, 0xffffffff, 0x00000100,
+       0x3c2c8, 0xffffffff, 0x00000100,
+       0x3c2c4, 0xffffffff, 0x00000100,
+       0x55e4, 0xffffffff, 0x00600100,
+       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,
+       0x3c000, 0xffffffff, 0x96e00200,
+       0x8708, 0xffffffff, 0x00900100,
+       0xc424, 0xffffffff, 0x0020003f,
+       0x38, 0xffffffff, 0x0140001c,
+       0x3c, 0x000f0000, 0x000f0000,
+       0x220, 0xffffffff, 0xC060000C,
+       0x224, 0xc0000fff, 0x00000100,
+       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) {
+       case CHIP_BONAIRE:
+               radeon_program_register_sequence(rdev,
+                                                bonaire_mgcg_cgcg_init,
+                                                (const u32)ARRAY_SIZE(bonaire_mgcg_cgcg_init));
+               radeon_program_register_sequence(rdev,
+                                                bonaire_golden_registers,
+                                                (const u32)ARRAY_SIZE(bonaire_golden_registers));
+               radeon_program_register_sequence(rdev,
+                                                bonaire_golden_common_registers,
+                                                (const u32)ARRAY_SIZE(bonaire_golden_common_registers));
+               radeon_program_register_sequence(rdev,
+                                                bonaire_golden_spm_registers,
+                                                (const u32)ARRAY_SIZE(bonaire_golden_spm_registers));
+               break;
+       case CHIP_KABINI:
+               radeon_program_register_sequence(rdev,
+                                                kalindi_mgcg_cgcg_init,
+                                                (const u32)ARRAY_SIZE(kalindi_mgcg_cgcg_init));
+               radeon_program_register_sequence(rdev,
+                                                kalindi_golden_registers,
+                                                (const u32)ARRAY_SIZE(kalindi_golden_registers));
+               radeon_program_register_sequence(rdev,
+                                                kalindi_golden_common_registers,
+                                                (const u32)ARRAY_SIZE(kalindi_golden_common_registers));
+               radeon_program_register_sequence(rdev,
+                                                kalindi_golden_spm_registers,
+                                                (const u32)ARRAY_SIZE(kalindi_golden_spm_registers));
+               break;
+       case CHIP_KAVERI:
+               radeon_program_register_sequence(rdev,
+                                                spectre_mgcg_cgcg_init,
+                                                (const u32)ARRAY_SIZE(spectre_mgcg_cgcg_init));
+               radeon_program_register_sequence(rdev,
+                                                spectre_golden_registers,
+                                                (const u32)ARRAY_SIZE(spectre_golden_registers));
+               radeon_program_register_sequence(rdev,
+                                                spectre_golden_common_registers,
+                                                (const u32)ARRAY_SIZE(spectre_golden_common_registers));
+               radeon_program_register_sequence(rdev,
+                                                spectre_golden_spm_registers,
+                                                (const u32)ARRAY_SIZE(spectre_golden_spm_registers));
+               break;
+       default:
+               break;
+       }
+}
+
+/**
+ * cik_get_xclk - get the xclk
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Returns the reference clock used by the gfx engine
+ * (CIK).
+ */
+u32 cik_get_xclk(struct radeon_device *rdev)
+{
+        u32 reference_clock = rdev->clock.spll.reference_freq;
+
+       if (rdev->flags & RADEON_IS_IGP) {
+               if (RREG32_SMC(GENERAL_PWRMGT) & GPU_COUNTER_CLK)
+                       return reference_clock / 2;
+       } else {
+               if (RREG32_SMC(CG_CLKPIN_CNTL) & XTALIN_DIVIDE)
+                       return reference_clock / 4;
+       }
+       return reference_clock;
+}
+
+/**
+ * cik_mm_rdoorbell - read a doorbell dword
+ *
+ * @rdev: radeon_device pointer
+ * @offset: byte offset into the aperture
+ *
+ * Returns the value in the doorbell aperture at the
+ * requested offset (CIK).
+ */
+u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset)
+{
+       if (offset < rdev->doorbell.size) {
+               return readl(((void __iomem *)rdev->doorbell.ptr) + offset);
+       } else {
+               DRM_ERROR("reading beyond doorbell aperture: 0x%08x!\n", offset);
+               return 0;
+       }
+}
+
+/**
+ * cik_mm_wdoorbell - write a doorbell dword
+ *
+ * @rdev: radeon_device pointer
+ * @offset: byte offset into the aperture
+ * @v: value to write
+ *
+ * Writes @v to the doorbell aperture at the
+ * requested offset (CIK).
+ */
+void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v)
+{
+       if (offset < rdev->doorbell.size) {
+               writel(v, ((void __iomem *)rdev->doorbell.ptr) + offset);
+       } else {
+               DRM_ERROR("writing beyond doorbell aperture: 0x%08x!\n", offset);
+       }
+}
+
+#define BONAIRE_IO_MC_REGS_SIZE 36
+
+static const u32 bonaire_io_mc_regs[BONAIRE_IO_MC_REGS_SIZE][2] =
+{
+       {0x00000070, 0x04400000},
+       {0x00000071, 0x80c01803},
+       {0x00000072, 0x00004004},
+       {0x00000073, 0x00000100},
+       {0x00000074, 0x00ff0000},
+       {0x00000075, 0x34000000},
+       {0x00000076, 0x08000014},
+       {0x00000077, 0x00cc08ec},
+       {0x00000078, 0x00000400},
+       {0x00000079, 0x00000000},
+       {0x0000007a, 0x04090000},
+       {0x0000007c, 0x00000000},
+       {0x0000007e, 0x4408a8e8},
+       {0x0000007f, 0x00000304},
+       {0x00000080, 0x00000000},
+       {0x00000082, 0x00000001},
+       {0x00000083, 0x00000002},
+       {0x00000084, 0xf3e4f400},
+       {0x00000085, 0x052024e3},
+       {0x00000087, 0x00000000},
+       {0x00000088, 0x01000000},
+       {0x0000008a, 0x1c0a0000},
+       {0x0000008b, 0xff010000},
+       {0x0000008d, 0xffffefff},
+       {0x0000008e, 0xfff3efff},
+       {0x0000008f, 0xfff3efbf},
+       {0x00000092, 0xf7ffffff},
+       {0x00000093, 0xffffff7f},
+       {0x00000095, 0x00101101},
+       {0x00000096, 0x00000fff},
+       {0x00000097, 0x00116fff},
+       {0x00000098, 0x60010000},
+       {0x00000099, 0x10010000},
+       {0x0000009a, 0x00006000},
+       {0x0000009b, 0x00001000},
+       {0x0000009f, 0x00b48000}
+};
+
+/**
+ * cik_srbm_select - select specific register instances
+ *
+ * @rdev: radeon_device pointer
+ * @me: selected ME (micro engine)
+ * @pipe: pipe
+ * @queue: queue
+ * @vmid: VMID
+ *
+ * Switches the currently active registers instances.  Some
+ * registers are instanced per VMID, others are instanced per
+ * me/pipe/queue combination.
+ */
+static void cik_srbm_select(struct radeon_device *rdev,
+                           u32 me, u32 pipe, u32 queue, u32 vmid)
+{
+       u32 srbm_gfx_cntl = (PIPEID(pipe & 0x3) |
+                            MEID(me & 0x3) |
+                            VMID(vmid & 0xf) |
+                            QUEUEID(queue & 0x7));
+       WREG32(SRBM_GFX_CNTL, srbm_gfx_cntl);
+}
+
+/* ucode loading */
+/**
+ * ci_mc_load_microcode - load MC ucode into the hw
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Load the GDDR MC ucode into the hw (CIK).
+ * Returns 0 on success, error on failure.
+ */
+static int ci_mc_load_microcode(struct radeon_device *rdev)
+{
+       const __be32 *fw_data;
+       u32 running, blackout = 0;
+       u32 *io_mc_regs;
+       int i, ucode_size, regs_size;
+
+       if (!rdev->mc_fw)
+               return -EINVAL;
+
+       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;
+       }
+
+       running = RREG32(MC_SEQ_SUP_CNTL) & RUN_MASK;
+
+       if (running == 0) {
+               if (running) {
+                       blackout = RREG32(MC_SHARED_BLACKOUT_CNTL);
+                       WREG32(MC_SHARED_BLACKOUT_CNTL, blackout | 1);
+               }
+
+               /* reset the engine and set to writable */
+               WREG32(MC_SEQ_SUP_CNTL, 0x00000008);
+               WREG32(MC_SEQ_SUP_CNTL, 0x00000010);
+
+               /* load mc io regs */
+               for (i = 0; i < regs_size; i++) {
+                       WREG32(MC_SEQ_IO_DEBUG_INDEX, io_mc_regs[(i << 1)]);
+                       WREG32(MC_SEQ_IO_DEBUG_DATA, io_mc_regs[(i << 1) + 1]);
+               }
+               /* load the MC ucode */
+               fw_data = (const __be32 *)rdev->mc_fw->data;
+               for (i = 0; i < ucode_size; i++)
+                       WREG32(MC_SEQ_SUP_PGM, be32_to_cpup(fw_data++));
+
+               /* put the engine back into the active state */
+               WREG32(MC_SEQ_SUP_CNTL, 0x00000008);
+               WREG32(MC_SEQ_SUP_CNTL, 0x00000004);
+               WREG32(MC_SEQ_SUP_CNTL, 0x00000001);
+
+               /* wait for training to complete */
+               for (i = 0; i < rdev->usec_timeout; i++) {
+                       if (RREG32(MC_SEQ_TRAIN_WAKEUP_CNTL) & TRAIN_DONE_D0)
+                               break;
+                       udelay(1);
+               }
+               for (i = 0; i < rdev->usec_timeout; i++) {
+                       if (RREG32(MC_SEQ_TRAIN_WAKEUP_CNTL) & TRAIN_DONE_D1)
+                               break;
+                       udelay(1);
+               }
+
+               if (running)
+                       WREG32(MC_SHARED_BLACKOUT_CNTL, blackout);
+       }
+
+       return 0;
+}
+
+/**
+ * cik_init_microcode - load ucode images from disk
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Use the firmware interface to load the ucode images into
+ * the driver (not loaded into hw).
+ * Returns 0 on success, error on failure.
+ */
+static int cik_init_microcode(struct radeon_device *rdev)
+{
+       struct platform_device *pdev;
+       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;
+       char fw_name[30];
+       int err;
+
+       DRM_DEBUG("\n");
+
+       pdev = platform_device_register_simple("radeon_cp", 0, NULL, 0);
+       err = IS_ERR(pdev);
+       if (err) {
+               printk(KERN_ERR "radeon_cp: Failed to register firmware\n");
+               return -EINVAL;
+       }
+
+       switch (rdev->family) {
+       case CHIP_BONAIRE:
+               chip_name = "BONAIRE";
+               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 = CIK_MC_UCODE_SIZE * 4;
+               sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
+               break;
+       case CHIP_KAVERI:
+               chip_name = "KAVERI";
+               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 = KV_RLC_UCODE_SIZE * 4;
+               sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
+               break;
+       case CHIP_KABINI:
+               chip_name = "KABINI";
+               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 = KB_RLC_UCODE_SIZE * 4;
+               sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
+               break;
+       default: BUG();
+       }
+
+       DRM_INFO("Loading %s Microcode\n", chip_name);
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", chip_name);
+       err = request_firmware(&rdev->pfp_fw, fw_name, &pdev->dev);
+       if (err)
+               goto out;
+       if (rdev->pfp_fw->size != pfp_req_size) {
+               printk(KERN_ERR
+                      "cik_cp: Bogus length %zu in firmware \"%s\"\n",
+                      rdev->pfp_fw->size, fw_name);
+               err = -EINVAL;
+               goto out;
+       }
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_me.bin", chip_name);
+       err = request_firmware(&rdev->me_fw, fw_name, &pdev->dev);
+       if (err)
+               goto out;
+       if (rdev->me_fw->size != me_req_size) {
+               printk(KERN_ERR
+                      "cik_cp: Bogus length %zu in firmware \"%s\"\n",
+                      rdev->me_fw->size, fw_name);
+               err = -EINVAL;
+       }
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_ce.bin", chip_name);
+       err = request_firmware(&rdev->ce_fw, fw_name, &pdev->dev);
+       if (err)
+               goto out;
+       if (rdev->ce_fw->size != ce_req_size) {
+               printk(KERN_ERR
+                      "cik_cp: Bogus length %zu in firmware \"%s\"\n",
+                      rdev->ce_fw->size, fw_name);
+               err = -EINVAL;
+       }
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_mec.bin", chip_name);
+       err = request_firmware(&rdev->mec_fw, fw_name, &pdev->dev);
+       if (err)
+               goto out;
+       if (rdev->mec_fw->size != mec_req_size) {
+               printk(KERN_ERR
+                      "cik_cp: Bogus length %zu in firmware \"%s\"\n",
+                      rdev->mec_fw->size, fw_name);
+               err = -EINVAL;
+       }
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", chip_name);
+       err = request_firmware(&rdev->rlc_fw, fw_name, &pdev->dev);
+       if (err)
+               goto out;
+       if (rdev->rlc_fw->size != rlc_req_size) {
+               printk(KERN_ERR
+                      "cik_rlc: Bogus length %zu in firmware \"%s\"\n",
+                      rdev->rlc_fw->size, fw_name);
+               err = -EINVAL;
+       }
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_sdma.bin", chip_name);
+       err = request_firmware(&rdev->sdma_fw, fw_name, &pdev->dev);
+       if (err)
+               goto out;
+       if (rdev->sdma_fw->size != sdma_req_size) {
+               printk(KERN_ERR
+                      "cik_sdma: Bogus length %zu in firmware \"%s\"\n",
+                      rdev->sdma_fw->size, fw_name);
+               err = -EINVAL;
+       }
+
+       /* No MC ucode on APUs */
+       if (!(rdev->flags & RADEON_IS_IGP)) {
+               snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
+               err = request_firmware(&rdev->mc_fw, fw_name, &pdev->dev);
+               if (err)
+                       goto out;
+               if (rdev->mc_fw->size != mc_req_size) {
+                       printk(KERN_ERR
+                              "cik_mc: Bogus length %zu in firmware \"%s\"\n",
+                              rdev->mc_fw->size, fw_name);
+                       err = -EINVAL;
+               }
+       }
+
+out:
+       platform_device_unregister(pdev);
+
+       if (err) {
+               if (err != -EINVAL)
+                       printk(KERN_ERR
+                              "cik_cp: Failed to load firmware \"%s\"\n",
+                              fw_name);
+               release_firmware(rdev->pfp_fw);
+               rdev->pfp_fw = NULL;
+               release_firmware(rdev->me_fw);
+               rdev->me_fw = NULL;
+               release_firmware(rdev->ce_fw);
+               rdev->ce_fw = NULL;
+               release_firmware(rdev->rlc_fw);
+               rdev->rlc_fw = NULL;
+               release_firmware(rdev->mc_fw);
+               rdev->mc_fw = NULL;
+       }
+       return err;
+}
+
+/*
+ * Core functions
+ */
+/**
+ * cik_tiling_mode_table_init - init the hw tiling table
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Starting with SI, the tiling setup is done globally in a
+ * set of 32 tiling modes.  Rather than selecting each set of
+ * parameters per surface as on older asics, we just select
+ * which index in the tiling table we want to use, and the
+ * surface uses those parameters (CIK).
+ */
+static void cik_tiling_mode_table_init(struct radeon_device *rdev)
+{
+       const u32 num_tile_mode_states = 32;
+       const u32 num_secondary_tile_mode_states = 16;
+       u32 reg_offset, gb_tile_moden, split_equal_to_row_size;
+       u32 num_pipe_configs;
+       u32 num_rbs = rdev->config.cik.max_backends_per_se *
+               rdev->config.cik.max_shader_engines;
+
+       switch (rdev->config.cik.mem_row_size_in_kb) {
+       case 1:
+               split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_1KB;
+               break;
+       case 2:
+       default:
+               split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_2KB;
+               break;
+       case 4:
+               split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_4KB;
+               break;
+       }
+
+       num_pipe_configs = rdev->config.cik.max_tile_pipes;
+       if (num_pipe_configs > 8)
+               num_pipe_configs = 8; /* ??? */
+
+       if (num_pipe_configs == 8) {
+               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_P8_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_P8_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_P8_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_P8_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_P8_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_P8_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_P8_32x32_16x16) |
+                                                TILE_SPLIT(split_equal_to_row_size));
+                               break;
+                       case 8:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_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_P8_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_P8_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_P8_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_P8_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_P8_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_P8_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_P8_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_P8_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_P8_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_4) |
+                                                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_2) |
+                                                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_2) |
+                                                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_8) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                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_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                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_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) |
+                                                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_2) |
+                                                NUM_BANKS(ADDR_SURF_16_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_8_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_4_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 == 4) {
+               if (num_rbs == 4) {
+                       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_P4_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_P4_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_P4_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_P4_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_P4_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_P4_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_P4_16x16) |
+                                                        TILE_SPLIT(split_equal_to_row_size));
+                                       break;
+                               case 8:
+                                       gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) |
+                                                        PIPE_CONFIG(ADDR_SURF_P4_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_P4_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_P4_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_P4_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_P4_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_P4_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_P4_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_PRT_2D_TILED_THIN1) |
+                                                        MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) |
+                                                        PIPE_CONFIG(ADDR_SURF_P4_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_P4_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_P4_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);
+                       }
+               } else if (num_rbs < 4) {
+                       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_P4_8x16) |
+                                                        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_P4_8x16) |
+                                                        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_P4_8x16) |
+                                                        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_P4_8x16) |
+                                                        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_P4_8x16) |
+                                                        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_P4_8x16) |
+                                                        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_P4_8x16) |
+                                                        TILE_SPLIT(split_equal_to_row_size));
+                                       break;
+                               case 8:
+                                       gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16));
+                                       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_P4_8x16) |
+                                                        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_P4_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_P4_8x16) |
+                                                        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_P4_8x16) |
+                                                        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_P4_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_P4_8x16) |
+                                                        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_PRT_2D_TILED_THIN1) |
+                                                        MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) |
+                                                        PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                        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_P4_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_P4_8x16) |
+                                                        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_4) |
+                                                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_4) |
+                                                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_2) |
+                                                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_2) |
+                                                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_2) |
+                                                NUM_BANKS(ADDR_SURF_16_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_2) |
+                                                NUM_BANKS(ADDR_SURF_8_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_4_BANK));
+                               break;
+                       case 8:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_8) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 9:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                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_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                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_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                NUM_BANKS(ADDR_SURF_16_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_2) |
+                                                NUM_BANKS(ADDR_SURF_16_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_2) |
+                                                NUM_BANKS(ADDR_SURF_8_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_4_BANK));
+                               break;
+                       default:
+                               gb_tile_moden = 0;
+                               break;
+                       }
+                       WREG32(GB_MACROTILE_MODE0 + (reg_offset * 4), gb_tile_moden);
+               }
+       } else if (num_pipe_configs == 2) {
+               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_P2) |
+                                                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_P2) |
+                                                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_P2) |
+                                                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_P2) |
+                                                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_P2) |
+                                                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_P2) |
+                                                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_P2) |
+                                                TILE_SPLIT(split_equal_to_row_size));
+                               break;
+                       case 8:
+                               gb_tile_moden = ARRAY_MODE(ARRAY_LINEAR_ALIGNED);
+                               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_P2) |
+                                                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_P2) |
+                                                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_P2) |
+                                                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_P2) |
+                                                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_P2) |
+                                                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_P2) |
+                                                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_PRT_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P2) |
+                                                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_P2) |
+                                                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_P2) |
+                                                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_2) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 1:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                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_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                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_4) |
+                                                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_4) |
+                                                NUM_BANKS(ADDR_SURF_16_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_4) |
+                                                NUM_BANKS(ADDR_SURF_16_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_2) |
+                                                NUM_BANKS(ADDR_SURF_8_BANK));
+                               break;
+                       case 8:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_4) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_8) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 9:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_4) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 10:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 11:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK));
+                               break;
+                       case 12:
+                               gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) |
+                                                NUM_BANKS(ADDR_SURF_16_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_4) |
+                                                NUM_BANKS(ADDR_SURF_16_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_2) |
+                                                NUM_BANKS(ADDR_SURF_8_BANK));
+                               break;
+                       default:
+                               gb_tile_moden = 0;
+                               break;
+                       }
+                       WREG32(GB_MACROTILE_MODE0 + (reg_offset * 4), gb_tile_moden);
+               }
+       } else
+               DRM_ERROR("unknown num pipe config: 0x%x\n", num_pipe_configs);
+}
+
+/**
+ * cik_select_se_sh - select which SE, SH to address
+ *
+ * @rdev: radeon_device pointer
+ * @se_num: shader engine to address
+ * @sh_num: sh block to address
+ *
+ * Select which SE, SH combinations to address. Certain
+ * registers are instanced per SE or SH.  0xffffffff means
+ * broadcast to all SEs or SHs (CIK).
+ */
+static void cik_select_se_sh(struct radeon_device *rdev,
+                            u32 se_num, u32 sh_num)
+{
+       u32 data = INSTANCE_BROADCAST_WRITES;
+
+       if ((se_num == 0xffffffff) && (sh_num == 0xffffffff))
+               data |= SH_BROADCAST_WRITES | SE_BROADCAST_WRITES;
+       else if (se_num == 0xffffffff)
+               data |= SE_BROADCAST_WRITES | SH_INDEX(sh_num);
+       else if (sh_num == 0xffffffff)
+               data |= SH_BROADCAST_WRITES | SE_INDEX(se_num);
+       else
+               data |= SH_INDEX(sh_num) | SE_INDEX(se_num);
+       WREG32(GRBM_GFX_INDEX, data);
+}
+
+/**
+ * cik_create_bitmask - create a bitmask
+ *
+ * @bit_width: length of the mask
+ *
+ * create a variable length bit mask (CIK).
+ * Returns the bitmask.
+ */
+static u32 cik_create_bitmask(u32 bit_width)
+{
+       u32 i, mask = 0;
+
+       for (i = 0; i < bit_width; i++) {
+               mask <<= 1;
+               mask |= 1;
+       }
+       return mask;
+}
+
+/**
+ * cik_select_se_sh - select which SE, SH to address
+ *
+ * @rdev: radeon_device pointer
+ * @max_rb_num: max RBs (render backends) for the asic
+ * @se_num: number of SEs (shader engines) for the asic
+ * @sh_per_se: number of SH blocks per SE for the asic
+ *
+ * Calculates the bitmask of disabled RBs (CIK).
+ * Returns the disabled RB bitmask.
+ */
+static u32 cik_get_rb_disabled(struct radeon_device *rdev,
+                             u32 max_rb_num, u32 se_num,
+                             u32 sh_per_se)
+{
+       u32 data, mask;
+
+       data = RREG32(CC_RB_BACKEND_DISABLE);
+       if (data & 1)
+               data &= BACKEND_DISABLE_MASK;
+       else
+               data = 0;
+       data |= RREG32(GC_USER_RB_BACKEND_DISABLE);
+
+       data >>= BACKEND_DISABLE_SHIFT;
+
+       mask = cik_create_bitmask(max_rb_num / se_num / sh_per_se);
+
+       return data & mask;
+}
+
+/**
+ * cik_setup_rb - setup the RBs on the asic
+ *
+ * @rdev: radeon_device pointer
+ * @se_num: number of SEs (shader engines) for the asic
+ * @sh_per_se: number of SH blocks per SE for the asic
+ * @max_rb_num: max RBs (render backends) for the asic
+ *
+ * Configures per-SE/SH RB registers (CIK).
+ */
+static void cik_setup_rb(struct radeon_device *rdev,
+                        u32 se_num, u32 sh_per_se,
+                        u32 max_rb_num)
+{
+       int i, j;
+       u32 data, mask;
+       u32 disabled_rbs = 0;
+       u32 enabled_rbs = 0;
+
+       for (i = 0; i < se_num; i++) {
+               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);
+               }
+       }
+       cik_select_se_sh(rdev, 0xffffffff, 0xffffffff);
+
+       mask = 1;
+       for (i = 0; i < max_rb_num; i++) {
+               if (!(disabled_rbs & mask))
+                       enabled_rbs |= mask;
+               mask <<= 1;
+       }
+
+       for (i = 0; i < se_num; i++) {
+               cik_select_se_sh(rdev, i, 0xffffffff);
+               data = 0;
+               for (j = 0; j < sh_per_se; j++) {
+                       switch (enabled_rbs & 3) {
+                       case 1:
+                               data |= (RASTER_CONFIG_RB_MAP_0 << (i * sh_per_se + j) * 2);
+                               break;
+                       case 2:
+                               data |= (RASTER_CONFIG_RB_MAP_3 << (i * sh_per_se + j) * 2);
+                               break;
+                       case 3:
+                       default:
+                               data |= (RASTER_CONFIG_RB_MAP_2 << (i * sh_per_se + j) * 2);
+                               break;
+                       }
+                       enabled_rbs >>= 2;
+               }
+               WREG32(PA_SC_RASTER_CONFIG, data);
+       }
+       cik_select_se_sh(rdev, 0xffffffff, 0xffffffff);
+}
+
+/**
+ * cik_gpu_init - setup the 3D engine
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Configures the 3D engine and tiling configuration
+ * registers so that the 3D engine is usable.
+ */
+static void cik_gpu_init(struct radeon_device *rdev)
+{
+       u32 gb_addr_config = RREG32(GB_ADDR_CONFIG);
+       u32 mc_shared_chmap, mc_arb_ramcfg;
+       u32 hdp_host_path_cntl;
+       u32 tmp;
+       int i, j;
+
+       switch (rdev->family) {
+       case CHIP_BONAIRE:
+               rdev->config.cik.max_shader_engines = 2;
+               rdev->config.cik.max_tile_pipes = 4;
+               rdev->config.cik.max_cu_per_sh = 7;
+               rdev->config.cik.max_sh_per_se = 1;
+               rdev->config.cik.max_backends_per_se = 2;
+               rdev->config.cik.max_texture_channel_caches = 4;
+               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 = BONAIRE_GB_ADDR_CONFIG_GOLDEN;
+               break;
+       case CHIP_KAVERI:
+               /* TODO */
+               break;
+       case CHIP_KABINI:
+       default:
+               rdev->config.cik.max_shader_engines = 1;
+               rdev->config.cik.max_tile_pipes = 2;
+               rdev->config.cik.max_cu_per_sh = 2;
+               rdev->config.cik.max_sh_per_se = 1;
+               rdev->config.cik.max_backends_per_se = 1;
+               rdev->config.cik.max_texture_channel_caches = 2;
+               rdev->config.cik.max_gprs = 256;
+               rdev->config.cik.max_gs_threads = 16;
+               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 = BONAIRE_GB_ADDR_CONFIG_GOLDEN;
+               break;
+       }
+
+       /* Initialize HDP */
+       for (i = 0, j = 0; i < 32; i++, j += 0x18) {
+               WREG32((0x2c14 + j), 0x00000000);
+               WREG32((0x2c18 + j), 0x00000000);
+               WREG32((0x2c1c + j), 0x00000000);
+               WREG32((0x2c20 + j), 0x00000000);
+               WREG32((0x2c24 + j), 0x00000000);
+       }
+
+       WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff));
+
+       WREG32(BIF_FB_EN, FB_READ_EN | FB_WRITE_EN);
+
+       mc_shared_chmap = RREG32(MC_SHARED_CHMAP);
+       mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG);
+
+       rdev->config.cik.num_tile_pipes = rdev->config.cik.max_tile_pipes;
+       rdev->config.cik.mem_max_burst_length_bytes = 256;
+       tmp = (mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT;
+       rdev->config.cik.mem_row_size_in_kb = (4 * (1 << (8 + tmp))) / 1024;
+       if (rdev->config.cik.mem_row_size_in_kb > 4)
+               rdev->config.cik.mem_row_size_in_kb = 4;
+       /* XXX use MC settings? */
+       rdev->config.cik.shader_engine_tile_size = 32;
+       rdev->config.cik.num_gpus = 1;
+       rdev->config.cik.multi_gpu_tile_size = 64;
+
+       /* fix up row size */
+       gb_addr_config &= ~ROW_SIZE_MASK;
+       switch (rdev->config.cik.mem_row_size_in_kb) {
+       case 1:
+       default:
+               gb_addr_config |= ROW_SIZE(0);
+               break;
+       case 2:
+               gb_addr_config |= ROW_SIZE(1);
+               break;
+       case 4:
+               gb_addr_config |= ROW_SIZE(2);
+               break;
+       }
+
+       /* setup tiling info dword.  gb_addr_config is not adequate since it does
+        * not have bank info, so create a custom tiling dword.
+        * bits 3:0   num_pipes
+        * bits 7:4   num_banks
+        * bits 11:8  group_size
+        * bits 15:12 row_size
+        */
+       rdev->config.cik.tile_config = 0;
+       switch (rdev->config.cik.num_tile_pipes) {
+       case 1:
+               rdev->config.cik.tile_config |= (0 << 0);
+               break;
+       case 2:
+               rdev->config.cik.tile_config |= (1 << 0);
+               break;
+       case 4:
+               rdev->config.cik.tile_config |= (2 << 0);
+               break;
+       case 8:
+       default:
+               /* XXX what about 12? */
+               rdev->config.cik.tile_config |= (3 << 0);
+               break;
+       }
+       if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT)
+               rdev->config.cik.tile_config |= 1 << 4;
+       else
+               rdev->config.cik.tile_config |= 0 << 4;
+       rdev->config.cik.tile_config |=
+               ((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8;
+       rdev->config.cik.tile_config |=
+               ((gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT) << 12;
+
+       WREG32(GB_ADDR_CONFIG, gb_addr_config);
+       WREG32(HDP_ADDR_CONFIG, gb_addr_config);
+       WREG32(DMIF_ADDR_CALC, gb_addr_config);
+       WREG32(SDMA0_TILING_CONFIG + SDMA0_REGISTER_OFFSET, gb_addr_config & 0x70);
+       WREG32(SDMA0_TILING_CONFIG + SDMA1_REGISTER_OFFSET, gb_addr_config & 0x70);
+       WREG32(UVD_UDEC_ADDR_CONFIG, gb_addr_config);
+       WREG32(UVD_UDEC_DB_ADDR_CONFIG, gb_addr_config);
+       WREG32(UVD_UDEC_DBW_ADDR_CONFIG, gb_addr_config);
+
+       cik_tiling_mode_table_init(rdev);
+
+       cik_setup_rb(rdev, rdev->config.cik.max_shader_engines,
+                    rdev->config.cik.max_sh_per_se,
+                    rdev->config.cik.max_backends_per_se);
+
+       /* set HW defaults for 3D engine */
+       WREG32(CP_MEQ_THRESHOLDS, MEQ1_START(0x30) | MEQ2_START(0x60));
+
+       WREG32(SX_DEBUG_1, 0x20);
+
+       WREG32(TA_CNTL_AUX, 0x00010000);
+
+       tmp = RREG32(SPI_CONFIG_CNTL);
+       tmp |= 0x03000000;
+       WREG32(SPI_CONFIG_CNTL, tmp);
+
+       WREG32(SQ_CONFIG, 1);
+
+       WREG32(DB_DEBUG, 0);
+
+       tmp = RREG32(DB_DEBUG2) & ~0xf00fffff;
+       tmp |= 0x00000400;
+       WREG32(DB_DEBUG2, tmp);
+
+       tmp = RREG32(DB_DEBUG3) & ~0x0002021c;
+       tmp |= 0x00020200;
+       WREG32(DB_DEBUG3, tmp);
+
+       tmp = RREG32(CB_HW_CONTROL) & ~0x00010000;
+       tmp |= 0x00018208;
+       WREG32(CB_HW_CONTROL, tmp);
+
+       WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(4));
+
+       WREG32(PA_SC_FIFO_SIZE, (SC_FRONTEND_PRIM_FIFO_SIZE(rdev->config.cik.sc_prim_fifo_size_frontend) |
+                                SC_BACKEND_PRIM_FIFO_SIZE(rdev->config.cik.sc_prim_fifo_size_backend) |
+                                SC_HIZ_TILE_FIFO_SIZE(rdev->config.cik.sc_hiz_tile_fifo_size) |
+                                SC_EARLYZ_TILE_FIFO_SIZE(rdev->config.cik.sc_earlyz_tile_fifo_size)));
+
+       WREG32(VGT_NUM_INSTANCES, 1);
+
+       WREG32(CP_PERFMON_CNTL, 0);
+
+       WREG32(SQ_CONFIG, 0);
+
+       WREG32(PA_SC_FORCE_EOV_MAX_CNTS, (FORCE_EOV_MAX_CLK_CNT(4095) |
+                                         FORCE_EOV_MAX_REZ_CNT(255)));
+
+       WREG32(VGT_CACHE_INVALIDATION, CACHE_INVALIDATION(VC_AND_TC) |
+              AUTO_INVLD_EN(ES_AND_GS_AUTO));
+
+       WREG32(VGT_GS_VERTEX_REUSE, 16);
+       WREG32(PA_SC_LINE_STIPPLE_STATE, 0);
+
+       tmp = RREG32(HDP_MISC_CNTL);
+       tmp |= HDP_FLUSH_INVALIDATE_CACHE;
+       WREG32(HDP_MISC_CNTL, tmp);
+
+       hdp_host_path_cntl = RREG32(HDP_HOST_PATH_CNTL);
+       WREG32(HDP_HOST_PATH_CNTL, hdp_host_path_cntl);
+
+       WREG32(PA_CL_ENHANCE, CLIP_VTX_REORDER_ENA | NUM_CLIP_SEQ(3));
+       WREG32(PA_SC_ENHANCE, ENABLE_PA_SC_OUT_OF_ORDER);
+
+       udelay(50);
+}
+
+/*
+ * GPU scratch registers helpers function.
+ */
+/**
+ * cik_scratch_init - setup driver info for CP scratch regs
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Set up the number and offset of the CP scratch registers.
+ * NOTE: use of CP scratch registers is a legacy inferface and
+ * is not used by default on newer asics (r6xx+).  On newer asics,
+ * memory buffers are used for fences rather than scratch regs.
+ */
+static void cik_scratch_init(struct radeon_device *rdev)
+{
+       int i;
+
+       rdev->scratch.num_reg = 7;
+       rdev->scratch.reg_base = SCRATCH_REG0;
+       for (i = 0; i < rdev->scratch.num_reg; i++) {
+               rdev->scratch.free[i] = true;
+               rdev->scratch.reg[i] = rdev->scratch.reg_base + (i * 4);
+       }
+}
+
+/**
+ * cik_ring_test - basic gfx ring test
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon_ring structure holding ring information
+ *
+ * Allocate a scratch register and write to it using the gfx ring (CIK).
+ * Provides a basic gfx ring test to verify that the ring is working.
+ * Used by cik_cp_gfx_resume();
+ * Returns 0 on success, error on failure.
+ */
+int cik_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
+{
+       uint32_t scratch;
+       uint32_t tmp = 0;
+       unsigned i;
+       int r;
+
+       r = radeon_scratch_get(rdev, &scratch);
+       if (r) {
+               DRM_ERROR("radeon: cp failed to get scratch reg (%d).\n", r);
+               return r;
+       }
+       WREG32(scratch, 0xCAFEDEAD);
+       r = radeon_ring_lock(rdev, ring, 3);
+       if (r) {
+               DRM_ERROR("radeon: cp failed to lock ring %d (%d).\n", ring->idx, r);
+               radeon_scratch_free(rdev, scratch);
+               return r;
+       }
+       radeon_ring_write(ring, PACKET3(PACKET3_SET_UCONFIG_REG, 1));
+       radeon_ring_write(ring, ((scratch - PACKET3_SET_UCONFIG_REG_START) >> 2));
+       radeon_ring_write(ring, 0xDEADBEEF);
+       radeon_ring_unlock_commit(rdev, ring);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               tmp = RREG32(scratch);
+               if (tmp == 0xDEADBEEF)
+                       break;
+               DRM_UDELAY(1);
+       }
+       if (i < rdev->usec_timeout) {
+               DRM_INFO("ring test on %d succeeded in %d usecs\n", ring->idx, i);
+       } else {
+               DRM_ERROR("radeon: ring %d test failed (scratch(0x%04X)=0x%08X)\n",
+                         ring->idx, scratch, tmp);
+               r = -EINVAL;
+       }
+       radeon_scratch_free(rdev, scratch);
+       return r;
+}
+
+/**
+ * cik_fence_gfx_ring_emit - emit a fence on the gfx ring
+ *
+ * @rdev: radeon_device pointer
+ * @fence: radeon fence object
+ *
+ * Emits a fence sequnce number on the gfx ring and flushes
+ * GPU caches.
+ */
+void cik_fence_gfx_ring_emit(struct radeon_device *rdev,
+                            struct radeon_fence *fence)
+{
+       struct radeon_ring *ring = &rdev->ring[fence->ring];
+       u64 addr = rdev->fence_drv[fence->ring].gpu_addr;
+
+       /* EVENT_WRITE_EOP - flush caches, send int */
+       radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
+       radeon_ring_write(ring, (EOP_TCL1_ACTION_EN |
+                                EOP_TC_ACTION_EN |
+                                EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) |
+                                EVENT_INDEX(5)));
+       radeon_ring_write(ring, addr & 0xfffffffc);
+       radeon_ring_write(ring, (upper_32_bits(addr) & 0xffff) | DATA_SEL(1) | INT_SEL(2));
+       radeon_ring_write(ring, fence->seq);
+       radeon_ring_write(ring, 0);
+       /* HDP flush */
+       /* We should be using the new WAIT_REG_MEM special op packet here
+        * but it causes the CP to hang
+        */
+       radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+       radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+                                WRITE_DATA_DST_SEL(0)));
+       radeon_ring_write(ring, HDP_MEM_COHERENCY_FLUSH_CNTL >> 2);
+       radeon_ring_write(ring, 0);
+       radeon_ring_write(ring, 0);
+}
+
+/**
+ * cik_fence_compute_ring_emit - emit a fence on the compute ring
+ *
+ * @rdev: radeon_device pointer
+ * @fence: radeon fence object
+ *
+ * Emits a fence sequnce number on the compute ring and flushes
+ * GPU caches.
+ */
+void cik_fence_compute_ring_emit(struct radeon_device *rdev,
+                                struct radeon_fence *fence)
+{
+       struct radeon_ring *ring = &rdev->ring[fence->ring];
+       u64 addr = rdev->fence_drv[fence->ring].gpu_addr;
+
+       /* RELEASE_MEM - flush caches, send int */
+       radeon_ring_write(ring, PACKET3(PACKET3_RELEASE_MEM, 5));
+       radeon_ring_write(ring, (EOP_TCL1_ACTION_EN |
+                                EOP_TC_ACTION_EN |
+                                EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) |
+                                EVENT_INDEX(5)));
+       radeon_ring_write(ring, DATA_SEL(1) | INT_SEL(2));
+       radeon_ring_write(ring, addr & 0xfffffffc);
+       radeon_ring_write(ring, upper_32_bits(addr));
+       radeon_ring_write(ring, fence->seq);
+       radeon_ring_write(ring, 0);
+       /* HDP flush */
+       /* We should be using the new WAIT_REG_MEM special op packet here
+        * but it causes the CP to hang
+        */
+       radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+       radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+                                WRITE_DATA_DST_SEL(0)));
+       radeon_ring_write(ring, HDP_MEM_COHERENCY_FLUSH_CNTL >> 2);
+       radeon_ring_write(ring, 0);
+       radeon_ring_write(ring, 0);
+}
+
+void cik_semaphore_ring_emit(struct radeon_device *rdev,
+                            struct radeon_ring *ring,
+                            struct radeon_semaphore *semaphore,
+                            bool emit_wait)
+{
+       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);
+}
+
+/*
+ * IB stuff
+ */
+/**
+ * cik_ring_ib_execute - emit an IB (Indirect Buffer) on the gfx ring
+ *
+ * @rdev: radeon_device pointer
+ * @ib: radeon indirect buffer object
+ *
+ * Emits an DE (drawing engine) or CE (constant engine) IB
+ * on the gfx ring.  IBs are usually generated by userspace
+ * acceleration drivers and submitted to the kernel for
+ * sheduling on the ring.  This function schedules the IB
+ * on the gfx ring for execution by the GPU.
+ */
+void cik_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
+{
+       struct radeon_ring *ring = &rdev->ring[ib->ring];
+       u32 header, control = INDIRECT_BUFFER_VALID;
+
+       if (ib->is_const_ib) {
+               /* set switch buffer packet before const IB */
+               radeon_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
+               radeon_ring_write(ring, 0);
+
+               header = PACKET3(PACKET3_INDIRECT_BUFFER_CONST, 2);
+       } else {
+               u32 next_rptr;
+               if (ring->rptr_save_reg) {
+                       next_rptr = ring->wptr + 3 + 4;
+                       radeon_ring_write(ring, PACKET3(PACKET3_SET_UCONFIG_REG, 1));
+                       radeon_ring_write(ring, ((ring->rptr_save_reg -
+                                                 PACKET3_SET_UCONFIG_REG_START) >> 2));
+                       radeon_ring_write(ring, next_rptr);
+               } else if (rdev->wb.enabled) {
+                       next_rptr = ring->wptr + 5 + 4;
+                       radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+                       radeon_ring_write(ring, WRITE_DATA_DST_SEL(1));
+                       radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc);
+                       radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xffffffff);
+                       radeon_ring_write(ring, next_rptr);
+               }
+
+               header = PACKET3(PACKET3_INDIRECT_BUFFER, 2);
+       }
+
+       control |= ib->length_dw |
+               (ib->vm ? (ib->vm->id << 24) : 0);
+
+       radeon_ring_write(ring, header);
+       radeon_ring_write(ring,
+#ifdef __BIG_ENDIAN
+                         (2 << 0) |
+#endif
+                         (ib->gpu_addr & 0xFFFFFFFC));
+       radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xFFFF);
+       radeon_ring_write(ring, control);
+}
+
+/**
+ * cik_ib_test - basic gfx ring IB test
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon_ring structure holding ring information
+ *
+ * Allocate an IB and execute it on the gfx ring (CIK).
+ * Provides a basic gfx ring test to verify that IBs are working.
+ * Returns 0 on success, error on failure.
+ */
+int cik_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
+{
+       struct radeon_ib ib;
+       uint32_t scratch;
+       uint32_t tmp = 0;
+       unsigned i;
+       int r;
+
+       r = radeon_scratch_get(rdev, &scratch);
+       if (r) {
+               DRM_ERROR("radeon: failed to get scratch reg (%d).\n", r);
+               return r;
+       }
+       WREG32(scratch, 0xCAFEDEAD);
+       r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256);
+       if (r) {
+               DRM_ERROR("radeon: failed to get ib (%d).\n", r);
+               return r;
+       }
+       ib.ptr[0] = PACKET3(PACKET3_SET_UCONFIG_REG, 1);
+       ib.ptr[1] = ((scratch - PACKET3_SET_UCONFIG_REG_START) >> 2);
+       ib.ptr[2] = 0xDEADBEEF;
+       ib.length_dw = 3;
+       r = radeon_ib_schedule(rdev, &ib, NULL);
+       if (r) {
+               radeon_scratch_free(rdev, scratch);
+               radeon_ib_free(rdev, &ib);
+               DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
+               return r;
+       }
+       r = radeon_fence_wait(ib.fence, false);
+       if (r) {
+               DRM_ERROR("radeon: fence wait failed (%d).\n", r);
+               return r;
+       }
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               tmp = RREG32(scratch);
+               if (tmp == 0xDEADBEEF)
+                       break;
+               DRM_UDELAY(1);
+       }
+       if (i < rdev->usec_timeout) {
+               DRM_INFO("ib test on ring %d succeeded in %u usecs\n", ib.fence->ring, i);
+       } else {
+               DRM_ERROR("radeon: ib test failed (scratch(0x%04X)=0x%08X)\n",
+                         scratch, tmp);
+               r = -EINVAL;
+       }
+       radeon_scratch_free(rdev, scratch);
+       radeon_ib_free(rdev, &ib);
+       return r;
+}
+
+/*
+ * CP.
+ * On CIK, gfx and compute now have independant command processors.
+ *
+ * GFX
+ * Gfx consists of a single ring and can process both gfx jobs and
+ * compute jobs.  The gfx CP consists of three microengines (ME):
+ * PFP - Pre-Fetch Parser
+ * ME - Micro Engine
+ * CE - Constant Engine
+ * The PFP and ME make up what is considered the Drawing Engine (DE).
+ * The CE is an asynchronous engine used for updating buffer desciptors
+ * used by the DE so that they can be loaded into cache in parallel
+ * while the DE is processing state update packets.
+ *
+ * Compute
+ * The compute CP consists of two microengines (ME):
+ * MEC1 - Compute MicroEngine 1
+ * MEC2 - Compute MicroEngine 2
+ * Each MEC supports 4 compute pipes and each pipe supports 8 queues.
+ * The queues are exposed to userspace and are programmed directly
+ * by the compute runtime.
+ */
+/**
+ * cik_cp_gfx_enable - enable/disable the gfx CP MEs
+ *
+ * @rdev: radeon_device pointer
+ * @enable: enable or disable the MEs
+ *
+ * Halts or unhalts the gfx MEs.
+ */
+static void cik_cp_gfx_enable(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32(CP_ME_CNTL, 0);
+       else {
+               WREG32(CP_ME_CNTL, (CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT));
+               rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false;
+       }
+       udelay(50);
+}
+
+/**
+ * cik_cp_gfx_load_microcode - load the gfx CP ME ucode
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Loads the gfx PFP, ME, and CE ucode.
+ * Returns 0 for success, -EINVAL if the ucode is not available.
+ */
+static int cik_cp_gfx_load_microcode(struct radeon_device *rdev)
+{
+       const __be32 *fw_data;
+       int i;
+
+       if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw)
+               return -EINVAL;
+
+       cik_cp_gfx_enable(rdev, false);
+
+       /* PFP */
+       fw_data = (const __be32 *)rdev->pfp_fw->data;
+       WREG32(CP_PFP_UCODE_ADDR, 0);
+       for (i = 0; i < CIK_PFP_UCODE_SIZE; i++)
+               WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++));
+       WREG32(CP_PFP_UCODE_ADDR, 0);
+
+       /* CE */
+       fw_data = (const __be32 *)rdev->ce_fw->data;
+       WREG32(CP_CE_UCODE_ADDR, 0);
+       for (i = 0; i < CIK_CE_UCODE_SIZE; i++)
+               WREG32(CP_CE_UCODE_DATA, be32_to_cpup(fw_data++));
+       WREG32(CP_CE_UCODE_ADDR, 0);
+
+       /* ME */
+       fw_data = (const __be32 *)rdev->me_fw->data;
+       WREG32(CP_ME_RAM_WADDR, 0);
+       for (i = 0; i < CIK_ME_UCODE_SIZE; i++)
+               WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++));
+       WREG32(CP_ME_RAM_WADDR, 0);
+
+       WREG32(CP_PFP_UCODE_ADDR, 0);
+       WREG32(CP_CE_UCODE_ADDR, 0);
+       WREG32(CP_ME_RAM_WADDR, 0);
+       WREG32(CP_ME_RAM_RADDR, 0);
+       return 0;
+}
+
+/**
+ * cik_cp_gfx_start - start the gfx ring
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Enables the ring and loads the clear state context and other
+ * packets required to init the ring.
+ * Returns 0 for success, error for failure.
+ */
+static int cik_cp_gfx_start(struct radeon_device *rdev)
+{
+       struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
+       int r, i;
+
+       /* init the CP */
+       WREG32(CP_MAX_CONTEXT, rdev->config.cik.max_hw_contexts - 1);
+       WREG32(CP_ENDIAN_SWAP, 0);
+       WREG32(CP_DEVICE_ID, 1);
+
+       cik_cp_gfx_enable(rdev, true);
+
+       r = radeon_ring_lock(rdev, ring, cik_default_size + 17);
+       if (r) {
+               DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r);
+               return r;
+       }
+
+       /* init the CE partitions.  CE only used for gfx on CIK */
+       radeon_ring_write(ring, PACKET3(PACKET3_SET_BASE, 2));
+       radeon_ring_write(ring, PACKET3_BASE_INDEX(CE_PARTITION_BASE));
+       radeon_ring_write(ring, 0xc000);
+       radeon_ring_write(ring, 0xc000);
+
+       /* setup clear context state */
+       radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+       radeon_ring_write(ring, PACKET3_PREAMBLE_BEGIN_CLEAR_STATE);
+
+       radeon_ring_write(ring, PACKET3(PACKET3_CONTEXT_CONTROL, 1));
+       radeon_ring_write(ring, 0x80000000);
+       radeon_ring_write(ring, 0x80000000);
+
+       for (i = 0; i < cik_default_size; i++)
+               radeon_ring_write(ring, cik_default_state[i]);
+
+       radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+       radeon_ring_write(ring, PACKET3_PREAMBLE_END_CLEAR_STATE);
+
+       /* set clear context state */
+       radeon_ring_write(ring, PACKET3(PACKET3_CLEAR_STATE, 0));
+       radeon_ring_write(ring, 0);
+
+       radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 2));
+       radeon_ring_write(ring, 0x00000316);
+       radeon_ring_write(ring, 0x0000000e); /* VGT_VERTEX_REUSE_BLOCK_CNTL */
+       radeon_ring_write(ring, 0x00000010); /* VGT_OUT_DEALLOC_CNTL */
+
+       radeon_ring_unlock_commit(rdev, ring);
+
+       return 0;
+}
+
+/**
+ * cik_cp_gfx_fini - stop the gfx ring
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Stop the gfx ring and tear down the driver ring
+ * info.
+ */
+static void cik_cp_gfx_fini(struct radeon_device *rdev)
+{
+       cik_cp_gfx_enable(rdev, false);
+       radeon_ring_fini(rdev, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
+}
+
+/**
+ * cik_cp_gfx_resume - setup the gfx ring buffer registers
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Program the location and size of the gfx ring buffer
+ * and test it to make sure it's working.
+ * Returns 0 for success, error for failure.
+ */
+static int cik_cp_gfx_resume(struct radeon_device *rdev)
+{
+       struct radeon_ring *ring;
+       u32 tmp;
+       u32 rb_bufsz;
+       u64 rb_addr;
+       int r;
+
+       WREG32(CP_SEM_WAIT_TIMER, 0x0);
+       WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0);
+
+       /* Set the write pointer delay */
+       WREG32(CP_RB_WPTR_DELAY, 0);
+
+       /* set the RB to use vmid 0 */
+       WREG32(CP_RB_VMID, 0);
+
+       WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF);
+
+       /* ring 0 - compute and gfx */
+       /* Set ring buffer size */
+       ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
+       rb_bufsz = drm_order(ring->ring_size / 8);
+       tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
+#ifdef __BIG_ENDIAN
+       tmp |= BUF_SWAP_32BIT;
+#endif
+       WREG32(CP_RB0_CNTL, tmp);
+
+       /* Initialize the ring buffer's read and write pointers */
+       WREG32(CP_RB0_CNTL, tmp | RB_RPTR_WR_ENA);
+       ring->wptr = 0;
+       WREG32(CP_RB0_WPTR, ring->wptr);
+
+       /* set the wb address wether it's enabled or not */
+       WREG32(CP_RB0_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC);
+       WREG32(CP_RB0_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF);
+
+       /* scratch register shadowing is no longer supported */
+       WREG32(SCRATCH_UMSK, 0);
+
+       if (!rdev->wb.enabled)
+               tmp |= RB_NO_UPDATE;
+
+       mdelay(1);
+       WREG32(CP_RB0_CNTL, tmp);
+
+       rb_addr = ring->gpu_addr >> 8;
+       WREG32(CP_RB0_BASE, rb_addr);
+       WREG32(CP_RB0_BASE_HI, upper_32_bits(rb_addr));
+
+       ring->rptr = RREG32(CP_RB0_RPTR);
+
+       /* start the ring */
+       cik_cp_gfx_start(rdev);
+       rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = true;
+       r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
+       if (r) {
+               rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false;
+               return r;
+       }
+       return 0;
+}
+
+u32 cik_compute_ring_get_rptr(struct radeon_device *rdev,
+                             struct radeon_ring *ring)
+{
+       u32 rptr;
+
+
+
+       if (rdev->wb.enabled) {
+               rptr = le32_to_cpu(rdev->wb.wb[ring->rptr_offs/4]);
+       } else {
+               cik_srbm_select(rdev, ring->me, ring->pipe, ring->queue, 0);
+               rptr = RREG32(CP_HQD_PQ_RPTR);
+               cik_srbm_select(rdev, 0, 0, 0, 0);
+       }
+       rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
+
+       return rptr;
+}
+
+u32 cik_compute_ring_get_wptr(struct radeon_device *rdev,
+                             struct radeon_ring *ring)
+{
+       u32 wptr;
+
+       if (rdev->wb.enabled) {
+               wptr = le32_to_cpu(rdev->wb.wb[ring->wptr_offs/4]);
+       } else {
+               cik_srbm_select(rdev, ring->me, ring->pipe, ring->queue, 0);
+               wptr = RREG32(CP_HQD_PQ_WPTR);
+               cik_srbm_select(rdev, 0, 0, 0, 0);
+       }
+       wptr = (wptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
+
+       return wptr;
+}
+
+void cik_compute_ring_set_wptr(struct radeon_device *rdev,
+                              struct radeon_ring *ring)
+{
+       u32 wptr = (ring->wptr << ring->ptr_reg_shift) & ring->ptr_reg_mask;
+
+       rdev->wb.wb[ring->wptr_offs/4] = cpu_to_le32(wptr);
+       WDOORBELL32(ring->doorbell_offset, wptr);
+}
+
+/**
+ * cik_cp_compute_enable - enable/disable the compute CP MEs
+ *
+ * @rdev: radeon_device pointer
+ * @enable: enable or disable the MEs
+ *
+ * Halts or unhalts the compute MEs.
+ */
+static void cik_cp_compute_enable(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32(CP_MEC_CNTL, 0);
+       else
+               WREG32(CP_MEC_CNTL, (MEC_ME1_HALT | MEC_ME2_HALT));
+       udelay(50);
+}
+
+/**
+ * cik_cp_compute_load_microcode - load the compute CP ME ucode
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Loads the compute MEC1&2 ucode.
+ * Returns 0 for success, -EINVAL if the ucode is not available.
+ */
+static int cik_cp_compute_load_microcode(struct radeon_device *rdev)
+{
+       const __be32 *fw_data;
+       int i;
+
+       if (!rdev->mec_fw)
+               return -EINVAL;
+
+       cik_cp_compute_enable(rdev, false);
+
+       /* MEC1 */
+       fw_data = (const __be32 *)rdev->mec_fw->data;
+       WREG32(CP_MEC_ME1_UCODE_ADDR, 0);
+       for (i = 0; i < CIK_MEC_UCODE_SIZE; i++)
+               WREG32(CP_MEC_ME1_UCODE_DATA, be32_to_cpup(fw_data++));
+       WREG32(CP_MEC_ME1_UCODE_ADDR, 0);
+
+       if (rdev->family == CHIP_KAVERI) {
+               /* MEC2 */
+               fw_data = (const __be32 *)rdev->mec_fw->data;
+               WREG32(CP_MEC_ME2_UCODE_ADDR, 0);
+               for (i = 0; i < CIK_MEC_UCODE_SIZE; i++)
+                       WREG32(CP_MEC_ME2_UCODE_DATA, be32_to_cpup(fw_data++));
+               WREG32(CP_MEC_ME2_UCODE_ADDR, 0);
+       }
+
+       return 0;
+}
+
+/**
+ * cik_cp_compute_start - start the compute queues
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Enable the compute queues.
+ * Returns 0 for success, error for failure.
+ */
+static int cik_cp_compute_start(struct radeon_device *rdev)
+{
+       cik_cp_compute_enable(rdev, true);
+
+       return 0;
+}
+
+/**
+ * cik_cp_compute_fini - stop the compute queues
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Stop the compute queues and tear down the driver queue
+ * info.
+ */
+static void cik_cp_compute_fini(struct radeon_device *rdev)
+{
+       int i, idx, r;
+
+       cik_cp_compute_enable(rdev, false);
+
+       for (i = 0; i < 2; i++) {
+               if (i == 0)
+                       idx = CAYMAN_RING_TYPE_CP1_INDEX;
+               else
+                       idx = CAYMAN_RING_TYPE_CP2_INDEX;
+
+               if (rdev->ring[idx].mqd_obj) {
+                       r = radeon_bo_reserve(rdev->ring[idx].mqd_obj, false);
+                       if (unlikely(r != 0))
+                               dev_warn(rdev->dev, "(%d) reserve MQD bo failed\n", r);
+
+                       radeon_bo_unpin(rdev->ring[idx].mqd_obj);
+                       radeon_bo_unreserve(rdev->ring[idx].mqd_obj);
+
+                       radeon_bo_unref(&rdev->ring[idx].mqd_obj);
+                       rdev->ring[idx].mqd_obj = NULL;
+               }
+       }
+}
+
+static void cik_mec_fini(struct radeon_device *rdev)
+{
+       int r;
+
+       if (rdev->mec.hpd_eop_obj) {
+               r = radeon_bo_reserve(rdev->mec.hpd_eop_obj, false);
+               if (unlikely(r != 0))
+                       dev_warn(rdev->dev, "(%d) reserve HPD EOP bo failed\n", r);
+               radeon_bo_unpin(rdev->mec.hpd_eop_obj);
+               radeon_bo_unreserve(rdev->mec.hpd_eop_obj);
+
+               radeon_bo_unref(&rdev->mec.hpd_eop_obj);
+               rdev->mec.hpd_eop_obj = NULL;
+       }
+}
+
+#define MEC_HPD_SIZE 2048
+
+static int cik_mec_init(struct radeon_device *rdev)
+{
+       int r;
+       u32 *hpd;
+
+       /*
+        * KV:    2 MEC, 4 Pipes/MEC, 8 Queues/Pipe - 64 Queues total
+        * CI/KB: 1 MEC, 4 Pipes/MEC, 8 Queues/Pipe - 32 Queues total
+        */
+       if (rdev->family == CHIP_KAVERI)
+               rdev->mec.num_mec = 2;
+       else
+               rdev->mec.num_mec = 1;
+       rdev->mec.num_pipe = 4;
+       rdev->mec.num_queue = rdev->mec.num_mec * rdev->mec.num_pipe * 8;
+
+       if (rdev->mec.hpd_eop_obj == NULL) {
+               r = radeon_bo_create(rdev,
+                                    rdev->mec.num_mec *rdev->mec.num_pipe * MEC_HPD_SIZE * 2,
+                                    PAGE_SIZE, true,
+                                    RADEON_GEM_DOMAIN_GTT, NULL,
+                                    &rdev->mec.hpd_eop_obj);
+               if (r) {
+                       dev_warn(rdev->dev, "(%d) create HDP EOP bo failed\n", r);
+                       return r;
+               }
+       }
+
+       r = radeon_bo_reserve(rdev->mec.hpd_eop_obj, false);
+       if (unlikely(r != 0)) {
+               cik_mec_fini(rdev);
+               return r;
+       }
+       r = radeon_bo_pin(rdev->mec.hpd_eop_obj, RADEON_GEM_DOMAIN_GTT,
+                         &rdev->mec.hpd_eop_gpu_addr);
+       if (r) {
+               dev_warn(rdev->dev, "(%d) pin HDP EOP bo failed\n", r);
+               cik_mec_fini(rdev);
+               return r;
+       }
+       r = radeon_bo_kmap(rdev->mec.hpd_eop_obj, (void **)&hpd);
+       if (r) {
+               dev_warn(rdev->dev, "(%d) map HDP EOP bo failed\n", r);
+               cik_mec_fini(rdev);
+               return r;
+       }
+
+       /* clear memory.  Not sure if this is required or not */
+       memset(hpd, 0, rdev->mec.num_mec *rdev->mec.num_pipe * MEC_HPD_SIZE * 2);
+
+       radeon_bo_kunmap(rdev->mec.hpd_eop_obj);
+       radeon_bo_unreserve(rdev->mec.hpd_eop_obj);
+
+       return 0;
+}
+
+struct hqd_registers
+{
+       u32 cp_mqd_base_addr;
+       u32 cp_mqd_base_addr_hi;
+       u32 cp_hqd_active;
+       u32 cp_hqd_vmid;
+       u32 cp_hqd_persistent_state;
+       u32 cp_hqd_pipe_priority;
+       u32 cp_hqd_queue_priority;
+       u32 cp_hqd_quantum;
+       u32 cp_hqd_pq_base;
+       u32 cp_hqd_pq_base_hi;
+       u32 cp_hqd_pq_rptr;
+       u32 cp_hqd_pq_rptr_report_addr;
+       u32 cp_hqd_pq_rptr_report_addr_hi;
+       u32 cp_hqd_pq_wptr_poll_addr;
+       u32 cp_hqd_pq_wptr_poll_addr_hi;
+       u32 cp_hqd_pq_doorbell_control;
+       u32 cp_hqd_pq_wptr;
+       u32 cp_hqd_pq_control;
+       u32 cp_hqd_ib_base_addr;
+       u32 cp_hqd_ib_base_addr_hi;
+       u32 cp_hqd_ib_rptr;
+       u32 cp_hqd_ib_control;
+       u32 cp_hqd_iq_timer;
+       u32 cp_hqd_iq_rptr;
+       u32 cp_hqd_dequeue_request;
+       u32 cp_hqd_dma_offload;
+       u32 cp_hqd_sema_cmd;
+       u32 cp_hqd_msg_type;
+       u32 cp_hqd_atomic0_preop_lo;
+       u32 cp_hqd_atomic0_preop_hi;
+       u32 cp_hqd_atomic1_preop_lo;
+       u32 cp_hqd_atomic1_preop_hi;
+       u32 cp_hqd_hq_scheduler0;
+       u32 cp_hqd_hq_scheduler1;
+       u32 cp_mqd_control;
+};
+
+struct bonaire_mqd
+{
+       u32 header;
+       u32 dispatch_initiator;
+       u32 dimensions[3];
+       u32 start_idx[3];
+       u32 num_threads[3];
+       u32 pipeline_stat_enable;
+       u32 perf_counter_enable;
+       u32 pgm[2];
+       u32 tba[2];
+       u32 tma[2];
+       u32 pgm_rsrc[2];
+       u32 vmid;
+       u32 resource_limits;
+       u32 static_thread_mgmt01[2];
+       u32 tmp_ring_size;
+       u32 static_thread_mgmt23[2];
+       u32 restart[3];
+       u32 thread_trace_enable;
+       u32 reserved1;
+       u32 user_data[16];
+       u32 vgtcs_invoke_count[2];
+       struct hqd_registers queue_state;
+       u32 dequeue_cntr;
+       u32 interrupt_queue[64];
+};
+
+/**
+ * cik_cp_compute_resume - setup the compute queue registers
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Program the compute queues and test them to make sure they
+ * are working.
+ * Returns 0 for success, error for failure.
+ */
+static int cik_cp_compute_resume(struct radeon_device *rdev)
+{
+       int r, i, idx;
+       u32 tmp;
+       bool use_doorbell = true;
+       u64 hqd_gpu_addr;
+       u64 mqd_gpu_addr;
+       u64 eop_gpu_addr;
+       u64 wb_gpu_addr;
+       u32 *buf;
+       struct bonaire_mqd *mqd;
+
+       r = cik_cp_compute_start(rdev);
+       if (r)
+               return r;
+
+       /* fix up chicken bits */
+       tmp = RREG32(CP_CPF_DEBUG);
+       tmp |= (1 << 23);
+       WREG32(CP_CPF_DEBUG, tmp);
+
+       /* init the pipes */
+       for (i = 0; i < (rdev->mec.num_pipe * rdev->mec.num_mec); i++) {
+               int me = (i < 4) ? 1 : 2;
+               int pipe = (i < 4) ? i : (i - 4);
+
+               eop_gpu_addr = rdev->mec.hpd_eop_gpu_addr + (i * MEC_HPD_SIZE * 2);
+
+               cik_srbm_select(rdev, me, pipe, 0, 0);
+
+               /* write the EOP addr */
+               WREG32(CP_HPD_EOP_BASE_ADDR, eop_gpu_addr >> 8);
+               WREG32(CP_HPD_EOP_BASE_ADDR_HI, upper_32_bits(eop_gpu_addr) >> 8);
+
+               /* set the VMID assigned */
+               WREG32(CP_HPD_EOP_VMID, 0);
+
+               /* set the EOP size, register value is 2^(EOP_SIZE+1) dwords */
+               tmp = RREG32(CP_HPD_EOP_CONTROL);
+               tmp &= ~EOP_SIZE_MASK;
+               tmp |= drm_order(MEC_HPD_SIZE / 8);
+               WREG32(CP_HPD_EOP_CONTROL, tmp);
+       }
+       cik_srbm_select(rdev, 0, 0, 0, 0);
+
+       /* init the queues.  Just two for now. */
+       for (i = 0; i < 2; i++) {
+               if (i == 0)
+                       idx = CAYMAN_RING_TYPE_CP1_INDEX;
+               else
+                       idx = CAYMAN_RING_TYPE_CP2_INDEX;
+
+               if (rdev->ring[idx].mqd_obj == NULL) {
+                       r = radeon_bo_create(rdev,
+                                            sizeof(struct bonaire_mqd),
+                                            PAGE_SIZE, true,
+                                            RADEON_GEM_DOMAIN_GTT, NULL,
+                                            &rdev->ring[idx].mqd_obj);
+                       if (r) {
+                               dev_warn(rdev->dev, "(%d) create MQD bo failed\n", r);
+                               return r;
+                       }
+               }
+
+               r = radeon_bo_reserve(rdev->ring[idx].mqd_obj, false);
+               if (unlikely(r != 0)) {
+                       cik_cp_compute_fini(rdev);
+                       return r;
+               }
+               r = radeon_bo_pin(rdev->ring[idx].mqd_obj, RADEON_GEM_DOMAIN_GTT,
+                                 &mqd_gpu_addr);
+               if (r) {
+                       dev_warn(rdev->dev, "(%d) pin MQD bo failed\n", r);
+                       cik_cp_compute_fini(rdev);
+                       return r;
+               }
+               r = radeon_bo_kmap(rdev->ring[idx].mqd_obj, (void **)&buf);
+               if (r) {
+                       dev_warn(rdev->dev, "(%d) map MQD bo failed\n", r);
+                       cik_cp_compute_fini(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));
+
+               mqd = (struct bonaire_mqd *)buf;
+               mqd->header = 0xC0310800;
+               mqd->static_thread_mgmt01[0] = 0xffffffff;
+               mqd->static_thread_mgmt01[1] = 0xffffffff;
+               mqd->static_thread_mgmt23[0] = 0xffffffff;
+               mqd->static_thread_mgmt23[1] = 0xffffffff;
+
+               cik_srbm_select(rdev, rdev->ring[idx].me,
+                               rdev->ring[idx].pipe,
+                               rdev->ring[idx].queue, 0);
+
+               /* disable wptr polling */
+               tmp = RREG32(CP_PQ_WPTR_POLL_CNTL);
+               tmp &= ~WPTR_POLL_EN;
+               WREG32(CP_PQ_WPTR_POLL_CNTL, tmp);
+
+               /* enable doorbell? */
+               mqd->queue_state.cp_hqd_pq_doorbell_control =
+                       RREG32(CP_HQD_PQ_DOORBELL_CONTROL);
+               if (use_doorbell)
+                       mqd->queue_state.cp_hqd_pq_doorbell_control |= DOORBELL_EN;
+               else
+                       mqd->queue_state.cp_hqd_pq_doorbell_control &= ~DOORBELL_EN;
+               WREG32(CP_HQD_PQ_DOORBELL_CONTROL,
+                      mqd->queue_state.cp_hqd_pq_doorbell_control);
+
+               /* disable the queue if it's active */
+               mqd->queue_state.cp_hqd_dequeue_request = 0;
+               mqd->queue_state.cp_hqd_pq_rptr = 0;
+               mqd->queue_state.cp_hqd_pq_wptr= 0;
+               if (RREG32(CP_HQD_ACTIVE) & 1) {
+                       WREG32(CP_HQD_DEQUEUE_REQUEST, 1);
+                       for (i = 0; i < rdev->usec_timeout; i++) {
+                               if (!(RREG32(CP_HQD_ACTIVE) & 1))
+                                       break;
+                               udelay(1);
+                       }
+                       WREG32(CP_HQD_DEQUEUE_REQUEST, mqd->queue_state.cp_hqd_dequeue_request);
+                       WREG32(CP_HQD_PQ_RPTR, mqd->queue_state.cp_hqd_pq_rptr);
+                       WREG32(CP_HQD_PQ_WPTR, mqd->queue_state.cp_hqd_pq_wptr);
+               }
+
+               /* set the pointer to the MQD */
+               mqd->queue_state.cp_mqd_base_addr = mqd_gpu_addr & 0xfffffffc;
+               mqd->queue_state.cp_mqd_base_addr_hi = upper_32_bits(mqd_gpu_addr);
+               WREG32(CP_MQD_BASE_ADDR, mqd->queue_state.cp_mqd_base_addr);
+               WREG32(CP_MQD_BASE_ADDR_HI, mqd->queue_state.cp_mqd_base_addr_hi);
+               /* set MQD vmid to 0 */
+               mqd->queue_state.cp_mqd_control = RREG32(CP_MQD_CONTROL);
+               mqd->queue_state.cp_mqd_control &= ~MQD_VMID_MASK;
+               WREG32(CP_MQD_CONTROL, mqd->queue_state.cp_mqd_control);
+
+               /* set the pointer to the HQD, this is similar CP_RB0_BASE/_HI */
+               hqd_gpu_addr = rdev->ring[idx].gpu_addr >> 8;
+               mqd->queue_state.cp_hqd_pq_base = hqd_gpu_addr;
+               mqd->queue_state.cp_hqd_pq_base_hi = upper_32_bits(hqd_gpu_addr);
+               WREG32(CP_HQD_PQ_BASE, mqd->queue_state.cp_hqd_pq_base);
+               WREG32(CP_HQD_PQ_BASE_HI, mqd->queue_state.cp_hqd_pq_base_hi);
+
+               /* set up the HQD, this is similar to CP_RB0_CNTL */
+               mqd->queue_state.cp_hqd_pq_control = RREG32(CP_HQD_PQ_CONTROL);
+               mqd->queue_state.cp_hqd_pq_control &=
+                       ~(QUEUE_SIZE_MASK | RPTR_BLOCK_SIZE_MASK);
+
+               mqd->queue_state.cp_hqd_pq_control |=
+                       drm_order(rdev->ring[idx].ring_size / 8);
+               mqd->queue_state.cp_hqd_pq_control |=
+                       (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8);
+#ifdef __BIG_ENDIAN
+               mqd->queue_state.cp_hqd_pq_control |= BUF_SWAP_32BIT;
+#endif
+               mqd->queue_state.cp_hqd_pq_control &=
+                       ~(UNORD_DISPATCH | ROQ_PQ_IB_FLIP | PQ_VOLATILE);
+               mqd->queue_state.cp_hqd_pq_control |=
+                       PRIV_STATE | KMD_QUEUE; /* assuming kernel queue control */
+               WREG32(CP_HQD_PQ_CONTROL, mqd->queue_state.cp_hqd_pq_control);
+
+               /* only used if CP_PQ_WPTR_POLL_CNTL.WPTR_POLL_EN=1 */
+               if (i == 0)
+                       wb_gpu_addr = rdev->wb.gpu_addr + CIK_WB_CP1_WPTR_OFFSET;
+               else
+                       wb_gpu_addr = rdev->wb.gpu_addr + CIK_WB_CP2_WPTR_OFFSET;
+               mqd->queue_state.cp_hqd_pq_wptr_poll_addr = wb_gpu_addr & 0xfffffffc;
+               mqd->queue_state.cp_hqd_pq_wptr_poll_addr_hi = upper_32_bits(wb_gpu_addr) & 0xffff;
+               WREG32(CP_HQD_PQ_WPTR_POLL_ADDR, mqd->queue_state.cp_hqd_pq_wptr_poll_addr);
+               WREG32(CP_HQD_PQ_WPTR_POLL_ADDR_HI,
+                      mqd->queue_state.cp_hqd_pq_wptr_poll_addr_hi);
+
+               /* set the wb address wether it's enabled or not */
+               if (i == 0)
+                       wb_gpu_addr = rdev->wb.gpu_addr + RADEON_WB_CP1_RPTR_OFFSET;
+               else
+                       wb_gpu_addr = rdev->wb.gpu_addr + RADEON_WB_CP2_RPTR_OFFSET;
+               mqd->queue_state.cp_hqd_pq_rptr_report_addr = wb_gpu_addr & 0xfffffffc;
+               mqd->queue_state.cp_hqd_pq_rptr_report_addr_hi =
+                       upper_32_bits(wb_gpu_addr) & 0xffff;
+               WREG32(CP_HQD_PQ_RPTR_REPORT_ADDR,
+                      mqd->queue_state.cp_hqd_pq_rptr_report_addr);
+               WREG32(CP_HQD_PQ_RPTR_REPORT_ADDR_HI,
+                      mqd->queue_state.cp_hqd_pq_rptr_report_addr_hi);
+
+               /* enable the doorbell if requested */
+               if (use_doorbell) {
+                       mqd->queue_state.cp_hqd_pq_doorbell_control =
+                               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);
+                       mqd->queue_state.cp_hqd_pq_doorbell_control |= DOORBELL_EN;
+                       mqd->queue_state.cp_hqd_pq_doorbell_control &=
+                               ~(DOORBELL_SOURCE | DOORBELL_HIT);
+
+               } else {
+                       mqd->queue_state.cp_hqd_pq_doorbell_control = 0;
+               }
+               WREG32(CP_HQD_PQ_DOORBELL_CONTROL,
+                      mqd->queue_state.cp_hqd_pq_doorbell_control);
+
+               /* read and write pointers, similar to CP_RB0_WPTR/_RPTR */
+               rdev->ring[idx].wptr = 0;
+               mqd->queue_state.cp_hqd_pq_wptr = rdev->ring[idx].wptr;
+               WREG32(CP_HQD_PQ_WPTR, mqd->queue_state.cp_hqd_pq_wptr);
+               rdev->ring[idx].rptr = RREG32(CP_HQD_PQ_RPTR);
+               mqd->queue_state.cp_hqd_pq_rptr = rdev->ring[idx].rptr;
+
+               /* set the vmid for the queue */
+               mqd->queue_state.cp_hqd_vmid = 0;
+               WREG32(CP_HQD_VMID, mqd->queue_state.cp_hqd_vmid);
+
+               /* activate the queue */
+               mqd->queue_state.cp_hqd_active = 1;
+               WREG32(CP_HQD_ACTIVE, mqd->queue_state.cp_hqd_active);
+
+               cik_srbm_select(rdev, 0, 0, 0, 0);
+
+               radeon_bo_kunmap(rdev->ring[idx].mqd_obj);
+               radeon_bo_unreserve(rdev->ring[idx].mqd_obj);
+
+               rdev->ring[idx].ready = true;
+               r = radeon_ring_test(rdev, idx, &rdev->ring[idx]);
+               if (r)
+                       rdev->ring[idx].ready = false;
+       }
+
+       return 0;
+}
+
+static void cik_cp_enable(struct radeon_device *rdev, bool enable)
+{
+       cik_cp_gfx_enable(rdev, enable);
+       cik_cp_compute_enable(rdev, enable);
+}
+
+static int cik_cp_load_microcode(struct radeon_device *rdev)
+{
+       int r;
+
+       r = cik_cp_gfx_load_microcode(rdev);
+       if (r)
+               return r;
+       r = cik_cp_compute_load_microcode(rdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static void cik_cp_fini(struct radeon_device *rdev)
+{
+       cik_cp_gfx_fini(rdev);
+       cik_cp_compute_fini(rdev);
+}
+
+static int cik_cp_resume(struct radeon_device *rdev)
+{
+       int r;
+
+       /* Reset all cp blocks */
+       WREG32(GRBM_SOFT_RESET, SOFT_RESET_CP);
+       RREG32(GRBM_SOFT_RESET);
+       mdelay(15);
+       WREG32(GRBM_SOFT_RESET, 0);
+       RREG32(GRBM_SOFT_RESET);
+
+       r = cik_cp_load_microcode(rdev);
+       if (r)
+               return r;
+
+       r = cik_cp_gfx_resume(rdev);
+       if (r)
+               return r;
+       r = cik_cp_compute_resume(rdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+/*
+ * sDMA - System DMA
+ * Starting with CIK, the GPU has new asynchronous
+ * DMA engines.  These engines are used for compute
+ * and gfx.  There are two DMA engines (SDMA0, SDMA1)
+ * and each one supports 1 ring buffer used for gfx
+ * and 2 queues used for compute.
+ *
+ * The programming model is very similar to the CP
+ * (ring buffer, IBs, etc.), but sDMA has it's own
+ * packet format that is different from the PM4 format
+ * used by the CP. sDMA supports copying data, writing
+ * embedded data, solid fills, and a number of other
+ * things.  It also has support for tiling/detiling of
+ * buffers.
+ */
+/**
+ * cik_sdma_ring_ib_execute - Schedule an IB on the DMA engine
+ *
+ * @rdev: radeon_device pointer
+ * @ib: IB object to schedule
+ *
+ * Schedule an IB in the DMA ring (CIK).
+ */
+void cik_sdma_ring_ib_execute(struct radeon_device *rdev,
+                             struct radeon_ib *ib)
+{
+       struct radeon_ring *ring = &rdev->ring[ib->ring];
+       u32 extra_bits = (ib->vm ? ib->vm->id : 0) & 0xf;
+
+       if (rdev->wb.enabled) {
+               u32 next_rptr = ring->wptr + 5;
+               while ((next_rptr & 7) != 4)
+                       next_rptr++;
+               next_rptr += 4;
+               radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0));
+               radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc);
+               radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xffffffff);
+               radeon_ring_write(ring, 1); /* number of DWs to follow */
+               radeon_ring_write(ring, next_rptr);
+       }
+
+       /* IB packet must end on a 8 DW boundary */
+       while ((ring->wptr & 7) != 4)
+               radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0));
+       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_INDIRECT_BUFFER, 0, extra_bits));
+       radeon_ring_write(ring, ib->gpu_addr & 0xffffffe0); /* base must be 32 byte aligned */
+       radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xffffffff);
+       radeon_ring_write(ring, ib->length_dw);
+
+}
+
+/**
+ * cik_sdma_fence_ring_emit - emit a fence on the DMA ring
+ *
+ * @rdev: radeon_device pointer
+ * @fence: radeon fence object
+ *
+ * Add a DMA fence packet to the ring to write
+ * the fence seq number and DMA trap packet to generate
+ * an interrupt if needed (CIK).
+ */
+void cik_sdma_fence_ring_emit(struct radeon_device *rdev,
+                             struct radeon_fence *fence)
+{
+       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));
+       radeon_ring_write(ring, addr & 0xffffffff);
+       radeon_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
+       radeon_ring_write(ring, fence->seq);
+       /* 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 */
+}
+
+/**
+ * cik_sdma_semaphore_ring_emit - emit a semaphore on the dma ring
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon_ring structure holding ring information
+ * @semaphore: radeon semaphore object
+ * @emit_wait: wait or signal semaphore
+ *
+ * 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,
+                                 struct radeon_ring *ring,
+                                 struct radeon_semaphore *semaphore,
+                                 bool emit_wait)
+{
+       u64 addr = semaphore->gpu_addr;
+       u32 extra_bits = emit_wait ? 0 : SDMA_SEMAPHORE_EXTRA_S;
+
+       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);
+}
+
+/**
+ * cik_sdma_gfx_stop - stop the gfx async dma engines
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Stop the gfx async dma ring buffers (CIK).
+ */
+static void cik_sdma_gfx_stop(struct radeon_device *rdev)
+{
+       u32 rb_cntl, reg_offset;
+       int i;
+
+       radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size);
+
+       for (i = 0; i < 2; i++) {
+               if (i == 0)
+                       reg_offset = SDMA0_REGISTER_OFFSET;
+               else
+                       reg_offset = SDMA1_REGISTER_OFFSET;
+               rb_cntl = RREG32(SDMA0_GFX_RB_CNTL + reg_offset);
+               rb_cntl &= ~SDMA_RB_ENABLE;
+               WREG32(SDMA0_GFX_RB_CNTL + reg_offset, rb_cntl);
+               WREG32(SDMA0_GFX_IB_CNTL + reg_offset, 0);
+       }
+}
+
+/**
+ * cik_sdma_rlc_stop - stop the compute async dma engines
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Stop the compute async dma queues (CIK).
+ */
+static void cik_sdma_rlc_stop(struct radeon_device *rdev)
+{
+       /* XXX todo */
+}
+
+/**
+ * cik_sdma_enable - stop the async dma engines
+ *
+ * @rdev: radeon_device pointer
+ * @enable: enable/disable the DMA MEs.
+ *
+ * Halt or unhalt the async dma engines (CIK).
+ */
+static void cik_sdma_enable(struct radeon_device *rdev, bool enable)
+{
+       u32 me_cntl, reg_offset;
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               if (i == 0)
+                       reg_offset = SDMA0_REGISTER_OFFSET;
+               else
+                       reg_offset = SDMA1_REGISTER_OFFSET;
+               me_cntl = RREG32(SDMA0_ME_CNTL + reg_offset);
+               if (enable)
+                       me_cntl &= ~SDMA_HALT;
+               else
+                       me_cntl |= SDMA_HALT;
+               WREG32(SDMA0_ME_CNTL + reg_offset, me_cntl);
+       }
+}
+
+/**
+ * cik_sdma_gfx_resume - setup and start the async dma engines
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Set up the gfx DMA ring buffers and enable them (CIK).
+ * Returns 0 for success, error for failure.
+ */
+static int cik_sdma_gfx_resume(struct radeon_device *rdev)
+{
+       struct radeon_ring *ring;
+       u32 rb_cntl, ib_cntl;
+       u32 rb_bufsz;
+       u32 reg_offset, wb_offset;
+       int i, r;
+
+       for (i = 0; i < 2; i++) {
+               if (i == 0) {
+                       ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX];
+                       reg_offset = SDMA0_REGISTER_OFFSET;
+                       wb_offset = R600_WB_DMA_RPTR_OFFSET;
+               } else {
+                       ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX];
+                       reg_offset = SDMA1_REGISTER_OFFSET;
+                       wb_offset = CAYMAN_WB_DMA1_RPTR_OFFSET;
+               }
+
+               WREG32(SDMA0_SEM_INCOMPLETE_TIMER_CNTL + reg_offset, 0);
+               WREG32(SDMA0_SEM_WAIT_FAIL_TIMER_CNTL + reg_offset, 0);
+
+               /* Set ring buffer size in dwords */
+               rb_bufsz = drm_order(ring->ring_size / 4);
+               rb_cntl = rb_bufsz << 1;
+#ifdef __BIG_ENDIAN
+               rb_cntl |= SDMA_RB_SWAP_ENABLE | SDMA_RPTR_WRITEBACK_SWAP_ENABLE;
+#endif
+               WREG32(SDMA0_GFX_RB_CNTL + reg_offset, rb_cntl);
+
+               /* Initialize the ring buffer's read and write pointers */
+               WREG32(SDMA0_GFX_RB_RPTR + reg_offset, 0);
+               WREG32(SDMA0_GFX_RB_WPTR + reg_offset, 0);
+
+               /* set the wb address whether it's enabled or not */
+               WREG32(SDMA0_GFX_RB_RPTR_ADDR_HI + reg_offset,
+                      upper_32_bits(rdev->wb.gpu_addr + wb_offset) & 0xFFFFFFFF);
+               WREG32(SDMA0_GFX_RB_RPTR_ADDR_LO + reg_offset,
+                      ((rdev->wb.gpu_addr + wb_offset) & 0xFFFFFFFC));
+
+               if (rdev->wb.enabled)
+                       rb_cntl |= SDMA_RPTR_WRITEBACK_ENABLE;
+
+               WREG32(SDMA0_GFX_RB_BASE + reg_offset, ring->gpu_addr >> 8);
+               WREG32(SDMA0_GFX_RB_BASE_HI + reg_offset, ring->gpu_addr >> 40);
+
+               ring->wptr = 0;
+               WREG32(SDMA0_GFX_RB_WPTR + reg_offset, ring->wptr << 2);
+
+               ring->rptr = RREG32(SDMA0_GFX_RB_RPTR + reg_offset) >> 2;
+
+               /* enable DMA RB */
+               WREG32(SDMA0_GFX_RB_CNTL + reg_offset, rb_cntl | SDMA_RB_ENABLE);
+
+               ib_cntl = SDMA_IB_ENABLE;
+#ifdef __BIG_ENDIAN
+               ib_cntl |= SDMA_IB_SWAP_ENABLE;
+#endif
+               /* enable DMA IBs */
+               WREG32(SDMA0_GFX_IB_CNTL + reg_offset, ib_cntl);
+
+               ring->ready = true;
+
+               r = radeon_ring_test(rdev, ring->idx, ring);
+               if (r) {
+                       ring->ready = false;
+                       return r;
+               }
+       }
+
+       radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size);
+
+       return 0;
+}
+
+/**
+ * cik_sdma_rlc_resume - setup and start the async dma engines
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Set up the compute DMA queues and enable them (CIK).
+ * Returns 0 for success, error for failure.
+ */
+static int cik_sdma_rlc_resume(struct radeon_device *rdev)
+{
+       /* XXX todo */
+       return 0;
+}
+
+/**
+ * cik_sdma_load_microcode - load the sDMA ME ucode
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Loads the sDMA0/1 ucode.
+ * Returns 0 for success, -EINVAL if the ucode is not available.
+ */
+static int cik_sdma_load_microcode(struct radeon_device *rdev)
+{
+       const __be32 *fw_data;
+       int i;
+
+       if (!rdev->sdma_fw)
+               return -EINVAL;
+
+       /* stop the gfx rings and rlc compute queues */
+       cik_sdma_gfx_stop(rdev);
+       cik_sdma_rlc_stop(rdev);
+
+       /* halt the MEs */
+       cik_sdma_enable(rdev, false);
+
+       /* sdma0 */
+       fw_data = (const __be32 *)rdev->sdma_fw->data;
+       WREG32(SDMA0_UCODE_ADDR + SDMA0_REGISTER_OFFSET, 0);
+       for (i = 0; i < CIK_SDMA_UCODE_SIZE; i++)
+               WREG32(SDMA0_UCODE_DATA + SDMA0_REGISTER_OFFSET, be32_to_cpup(fw_data++));
+       WREG32(SDMA0_UCODE_DATA + SDMA0_REGISTER_OFFSET, CIK_SDMA_UCODE_VERSION);
+
+       /* sdma1 */
+       fw_data = (const __be32 *)rdev->sdma_fw->data;
+       WREG32(SDMA0_UCODE_ADDR + SDMA1_REGISTER_OFFSET, 0);
+       for (i = 0; i < CIK_SDMA_UCODE_SIZE; i++)
+               WREG32(SDMA0_UCODE_DATA + SDMA1_REGISTER_OFFSET, be32_to_cpup(fw_data++));
+       WREG32(SDMA0_UCODE_DATA + SDMA1_REGISTER_OFFSET, CIK_SDMA_UCODE_VERSION);
+
+       WREG32(SDMA0_UCODE_ADDR + SDMA0_REGISTER_OFFSET, 0);
+       WREG32(SDMA0_UCODE_ADDR + SDMA1_REGISTER_OFFSET, 0);
+       return 0;
+}
+
+/**
+ * cik_sdma_resume - setup and start the async dma engines
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Set up the DMA engines and enable them (CIK).
+ * Returns 0 for success, error for failure.
+ */
+static int cik_sdma_resume(struct radeon_device *rdev)
+{
+       int r;
+
+       /* Reset dma */
+       WREG32(SRBM_SOFT_RESET, SOFT_RESET_SDMA | SOFT_RESET_SDMA1);
+       RREG32(SRBM_SOFT_RESET);
+       udelay(50);
+       WREG32(SRBM_SOFT_RESET, 0);
+       RREG32(SRBM_SOFT_RESET);
+
+       r = cik_sdma_load_microcode(rdev);
+       if (r)
+               return r;
+
+       /* unhalt the MEs */
+       cik_sdma_enable(rdev, true);
+
+       /* start the gfx rings and rlc compute queues */
+       r = cik_sdma_gfx_resume(rdev);
+       if (r)
+               return r;
+       r = cik_sdma_rlc_resume(rdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+/**
+ * cik_sdma_fini - tear down the async dma engines
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Stop the async dma engines and free the rings (CIK).
+ */
+static void cik_sdma_fini(struct radeon_device *rdev)
+{
+       /* stop the gfx rings and rlc compute queues */
+       cik_sdma_gfx_stop(rdev);
+       cik_sdma_rlc_stop(rdev);
+       /* halt the MEs */
+       cik_sdma_enable(rdev, false);
+       radeon_ring_fini(rdev, &rdev->ring[R600_RING_TYPE_DMA_INDEX]);
+       radeon_ring_fini(rdev, &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX]);
+       /* XXX - compute dma queue tear down */
+}
+
+/**
+ * cik_copy_dma - copy pages using the 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 DMA engine (CIK).
+ * Used by the radeon ttm implementation to move pages if
+ * registered as the asic copy callback.
+ */
+int cik_copy_dma(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.dma_ring_index;
+       struct radeon_ring *ring = &rdev->ring[ring_index];
+       u32 size_in_bytes, cur_size_in_bytes;
+       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 + 14);
+       if (r) {
+               DRM_ERROR("radeon: moving bo (%d).\n", r);
+               radeon_semaphore_free(rdev, &sem, NULL);
+               return r;
+       }
+
+       if (radeon_fence_need_sync(*fence, ring->idx)) {
+               radeon_semaphore_sync_rings(rdev, sem, (*fence)->ring,
+                                           ring->idx);
+               radeon_fence_note_sync(*fence, ring->idx);
+       } else {
+               radeon_semaphore_free(rdev, &sem, NULL);
+       }
+
+       for (i = 0; i < num_loops; i++) {
+               cur_size_in_bytes = size_in_bytes;
+               if (cur_size_in_bytes > 0x1fffff)
+                       cur_size_in_bytes = 0x1fffff;
+               size_in_bytes -= cur_size_in_bytes;
+               radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_COPY, SDMA_COPY_SUB_OPCODE_LINEAR, 0));
+               radeon_ring_write(ring, cur_size_in_bytes);
+               radeon_ring_write(ring, 0); /* src/dst endian swap */
+               radeon_ring_write(ring, src_offset & 0xffffffff);
+               radeon_ring_write(ring, upper_32_bits(src_offset) & 0xffffffff);
+               radeon_ring_write(ring, dst_offset & 0xfffffffc);
+               radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xffffffff);
+               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;
+}
+
+/**
+ * cik_sdma_ring_test - simple async dma engine test
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon_ring structure holding ring information
+ *
+ * Test the DMA engine by writing using it to write an
+ * value to memory. (CIK).
+ * Returns 0 for success, error for failure.
+ */
+int cik_sdma_ring_test(struct radeon_device *rdev,
+                      struct radeon_ring *ring)
+{
+       unsigned i;
+       int r;
+       void __iomem *ptr = (void *)rdev->vram_scratch.ptr;
+       u32 tmp;
+
+       if (!ptr) {
+               DRM_ERROR("invalid vram scratch pointer\n");
+               return -EINVAL;
+       }
+
+       tmp = 0xCAFEDEAD;
+       writel(tmp, ptr);
+
+       r = radeon_ring_lock(rdev, ring, 4);
+       if (r) {
+               DRM_ERROR("radeon: dma failed to lock ring %d (%d).\n", ring->idx, r);
+               return r;
+       }
+       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0));
+       radeon_ring_write(ring, rdev->vram_scratch.gpu_addr & 0xfffffffc);
+       radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xffffffff);
+       radeon_ring_write(ring, 1); /* number of DWs to follow */
+       radeon_ring_write(ring, 0xDEADBEEF);
+       radeon_ring_unlock_commit(rdev, ring);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               tmp = readl(ptr);
+               if (tmp == 0xDEADBEEF)
+                       break;
+               DRM_UDELAY(1);
+       }
+
+       if (i < rdev->usec_timeout) {
+               DRM_INFO("ring test on %d succeeded in %d usecs\n", ring->idx, i);
+       } else {
+               DRM_ERROR("radeon: ring %d test failed (0x%08X)\n",
+                         ring->idx, tmp);
+               r = -EINVAL;
+       }
+       return r;
+}
+
+/**
+ * cik_sdma_ib_test - test an IB on the DMA engine
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon_ring structure holding ring information
+ *
+ * Test a simple IB in the DMA ring (CIK).
+ * Returns 0 on success, error on failure.
+ */
+int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
+{
+       struct radeon_ib ib;
+       unsigned i;
+       int r;
+       void __iomem *ptr = (void *)rdev->vram_scratch.ptr;
+       u32 tmp = 0;
+
+       if (!ptr) {
+               DRM_ERROR("invalid vram scratch pointer\n");
+               return -EINVAL;
+       }
+
+       tmp = 0xCAFEDEAD;
+       writel(tmp, ptr);
+
+       r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256);
+       if (r) {
+               DRM_ERROR("radeon: failed to get ib (%d).\n", r);
+               return r;
+       }
+
+       ib.ptr[0] = SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
+       ib.ptr[1] = rdev->vram_scratch.gpu_addr & 0xfffffffc;
+       ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xffffffff;
+       ib.ptr[3] = 1;
+       ib.ptr[4] = 0xDEADBEEF;
+       ib.length_dw = 5;
+
+       r = radeon_ib_schedule(rdev, &ib, NULL);
+       if (r) {
+               radeon_ib_free(rdev, &ib);
+               DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
+               return r;
+       }
+       r = radeon_fence_wait(ib.fence, false);
+       if (r) {
+               DRM_ERROR("radeon: fence wait failed (%d).\n", r);
+               return r;
+       }
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               tmp = readl(ptr);
+               if (tmp == 0xDEADBEEF)
+                       break;
+               DRM_UDELAY(1);
+       }
+       if (i < rdev->usec_timeout) {
+               DRM_INFO("ib test on ring %d succeeded in %u usecs\n", ib.fence->ring, i);
+       } else {
+               DRM_ERROR("radeon: ib test failed (0x%08X)\n", tmp);
+               r = -EINVAL;
+       }
+       radeon_ib_free(rdev, &ib);
+       return r;
+}
+
+
+static void cik_print_gpu_status_regs(struct radeon_device *rdev)
+{
+       dev_info(rdev->dev, "  GRBM_STATUS=0x%08X\n",
+               RREG32(GRBM_STATUS));
+       dev_info(rdev->dev, "  GRBM_STATUS2=0x%08X\n",
+               RREG32(GRBM_STATUS2));
+       dev_info(rdev->dev, "  GRBM_STATUS_SE0=0x%08X\n",
+               RREG32(GRBM_STATUS_SE0));
+       dev_info(rdev->dev, "  GRBM_STATUS_SE1=0x%08X\n",
+               RREG32(GRBM_STATUS_SE1));
+       dev_info(rdev->dev, "  GRBM_STATUS_SE2=0x%08X\n",
+               RREG32(GRBM_STATUS_SE2));
+       dev_info(rdev->dev, "  GRBM_STATUS_SE3=0x%08X\n",
+               RREG32(GRBM_STATUS_SE3));
+       dev_info(rdev->dev, "  SRBM_STATUS=0x%08X\n",
+               RREG32(SRBM_STATUS));
+       dev_info(rdev->dev, "  SRBM_STATUS2=0x%08X\n",
+               RREG32(SRBM_STATUS2));
+       dev_info(rdev->dev, "  SDMA0_STATUS_REG   = 0x%08X\n",
+               RREG32(SDMA0_STATUS_REG + SDMA0_REGISTER_OFFSET));
+       dev_info(rdev->dev, "  SDMA1_STATUS_REG   = 0x%08X\n",
+                RREG32(SDMA0_STATUS_REG + SDMA1_REGISTER_OFFSET));
+       dev_info(rdev->dev, "  CP_STAT = 0x%08x\n", RREG32(CP_STAT));
+       dev_info(rdev->dev, "  CP_STALLED_STAT1 = 0x%08x\n",
+                RREG32(CP_STALLED_STAT1));
+       dev_info(rdev->dev, "  CP_STALLED_STAT2 = 0x%08x\n",
+                RREG32(CP_STALLED_STAT2));
+       dev_info(rdev->dev, "  CP_STALLED_STAT3 = 0x%08x\n",
+                RREG32(CP_STALLED_STAT3));
+       dev_info(rdev->dev, "  CP_CPF_BUSY_STAT = 0x%08x\n",
+                RREG32(CP_CPF_BUSY_STAT));
+       dev_info(rdev->dev, "  CP_CPF_STALLED_STAT1 = 0x%08x\n",
+                RREG32(CP_CPF_STALLED_STAT1));
+       dev_info(rdev->dev, "  CP_CPF_STATUS = 0x%08x\n", RREG32(CP_CPF_STATUS));
+       dev_info(rdev->dev, "  CP_CPC_BUSY_STAT = 0x%08x\n", RREG32(CP_CPC_BUSY_STAT));
+       dev_info(rdev->dev, "  CP_CPC_STALLED_STAT1 = 0x%08x\n",
+                RREG32(CP_CPC_STALLED_STAT1));
+       dev_info(rdev->dev, "  CP_CPC_STATUS = 0x%08x\n", RREG32(CP_CPC_STATUS));
+}
+
+/**
+ * cik_gpu_check_soft_reset - check which blocks are busy
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Check which blocks are busy and return the relevant reset
+ * mask to be used by cik_gpu_soft_reset().
+ * Returns a mask of the blocks to be reset.
+ */
+static u32 cik_gpu_check_soft_reset(struct radeon_device *rdev)
+{
+       u32 reset_mask = 0;
+       u32 tmp;
+
+       /* GRBM_STATUS */
+       tmp = RREG32(GRBM_STATUS);
+       if (tmp & (PA_BUSY | SC_BUSY |
+                  BCI_BUSY | SX_BUSY |
+                  TA_BUSY | VGT_BUSY |
+                  DB_BUSY | CB_BUSY |
+                  GDS_BUSY | SPI_BUSY |
+                  IA_BUSY | IA_BUSY_NO_DMA))
+               reset_mask |= RADEON_RESET_GFX;
+
+       if (tmp & (CP_BUSY | CP_COHERENCY_BUSY))
+               reset_mask |= RADEON_RESET_CP;
+
+       /* GRBM_STATUS2 */
+       tmp = RREG32(GRBM_STATUS2);
+       if (tmp & RLC_BUSY)
+               reset_mask |= RADEON_RESET_RLC;
+
+       /* SDMA0_STATUS_REG */
+       tmp = RREG32(SDMA0_STATUS_REG + SDMA0_REGISTER_OFFSET);
+       if (!(tmp & SDMA_IDLE))
+               reset_mask |= RADEON_RESET_DMA;
+
+       /* SDMA1_STATUS_REG */
+       tmp = RREG32(SDMA0_STATUS_REG + SDMA1_REGISTER_OFFSET);
+       if (!(tmp & SDMA_IDLE))
+               reset_mask |= RADEON_RESET_DMA1;
+
+       /* SRBM_STATUS2 */
+       tmp = RREG32(SRBM_STATUS2);
+       if (tmp & SDMA_BUSY)
+               reset_mask |= RADEON_RESET_DMA;
+
+       if (tmp & SDMA1_BUSY)
+               reset_mask |= RADEON_RESET_DMA1;
+
+       /* SRBM_STATUS */
+       tmp = RREG32(SRBM_STATUS);
+
+       if (tmp & IH_BUSY)
+               reset_mask |= RADEON_RESET_IH;
+
+       if (tmp & SEM_BUSY)
+               reset_mask |= RADEON_RESET_SEM;
+
+       if (tmp & GRBM_RQ_PENDING)
+               reset_mask |= RADEON_RESET_GRBM;
+
+       if (tmp & VMC_BUSY)
+               reset_mask |= RADEON_RESET_VMC;
+
+       if (tmp & (MCB_BUSY | MCB_NON_DISPLAY_BUSY |
+                  MCC_BUSY | MCD_BUSY))
+               reset_mask |= RADEON_RESET_MC;
+
+       if (evergreen_is_display_hung(rdev))
+               reset_mask |= RADEON_RESET_DISPLAY;
+
+       /* Skip MC reset as it's mostly likely not hung, just busy */
+       if (reset_mask & RADEON_RESET_MC) {
+               DRM_DEBUG("MC busy: 0x%08X, clearing.\n", reset_mask);
+               reset_mask &= ~RADEON_RESET_MC;
+       }
+
+       return reset_mask;
+}
+
+/**
+ * cik_gpu_soft_reset - soft reset GPU
+ *
+ * @rdev: radeon_device pointer
+ * @reset_mask: mask of which blocks to reset
+ *
+ * Soft reset the blocks specified in @reset_mask.
+ */
+static void cik_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask)
+{
+       struct evergreen_mc_save save;
+       u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
+       u32 tmp;
+
+       if (reset_mask == 0)
+               return;
+
+       dev_info(rdev->dev, "GPU softreset: 0x%08X\n", reset_mask);
+
+       cik_print_gpu_status_regs(rdev);
+       dev_info(rdev->dev, "  VM_CONTEXT1_PROTECTION_FAULT_ADDR   0x%08X\n",
+                RREG32(VM_CONTEXT1_PROTECTION_FAULT_ADDR));
+       dev_info(rdev->dev, "  VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n",
+                RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS));
+
+       /* stop the rlc */
+       cik_rlc_stop(rdev);
+
+       /* Disable GFX parsing/prefetching */
+       WREG32(CP_ME_CNTL, CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT);
+
+       /* Disable MEC parsing/prefetching */
+       WREG32(CP_MEC_CNTL, MEC_ME1_HALT | MEC_ME2_HALT);
+
+       if (reset_mask & RADEON_RESET_DMA) {
+               /* sdma0 */
+               tmp = RREG32(SDMA0_ME_CNTL + SDMA0_REGISTER_OFFSET);
+               tmp |= SDMA_HALT;
+               WREG32(SDMA0_ME_CNTL + SDMA0_REGISTER_OFFSET, tmp);
+       }
+       if (reset_mask & RADEON_RESET_DMA1) {
+               /* sdma1 */
+               tmp = RREG32(SDMA0_ME_CNTL + SDMA1_REGISTER_OFFSET);
+               tmp |= SDMA_HALT;
+               WREG32(SDMA0_ME_CNTL + SDMA1_REGISTER_OFFSET, tmp);
+       }
+
+       evergreen_mc_stop(rdev, &save);
+       if (evergreen_mc_wait_for_idle(rdev)) {
+               dev_warn(rdev->dev, "Wait for MC idle timedout !\n");
+       }
+
+       if (reset_mask & (RADEON_RESET_GFX | RADEON_RESET_COMPUTE | RADEON_RESET_CP))
+               grbm_soft_reset = SOFT_RESET_CP | SOFT_RESET_GFX;
+
+       if (reset_mask & RADEON_RESET_CP) {
+               grbm_soft_reset |= SOFT_RESET_CP;
+
+               srbm_soft_reset |= SOFT_RESET_GRBM;
+       }
+
+       if (reset_mask & RADEON_RESET_DMA)
+               srbm_soft_reset |= SOFT_RESET_SDMA;
+
+       if (reset_mask & RADEON_RESET_DMA1)
+               srbm_soft_reset |= SOFT_RESET_SDMA1;
+
+       if (reset_mask & RADEON_RESET_DISPLAY)
+               srbm_soft_reset |= SOFT_RESET_DC;
+
+       if (reset_mask & RADEON_RESET_RLC)
+               grbm_soft_reset |= SOFT_RESET_RLC;
+
+       if (reset_mask & RADEON_RESET_SEM)
+               srbm_soft_reset |= SOFT_RESET_SEM;
+
+       if (reset_mask & RADEON_RESET_IH)
+               srbm_soft_reset |= SOFT_RESET_IH;
+
+       if (reset_mask & RADEON_RESET_GRBM)
+               srbm_soft_reset |= SOFT_RESET_GRBM;
+
+       if (reset_mask & RADEON_RESET_VMC)
+               srbm_soft_reset |= SOFT_RESET_VMC;
+
+       if (!(rdev->flags & RADEON_IS_IGP)) {
+               if (reset_mask & RADEON_RESET_MC)
+                       srbm_soft_reset |= SOFT_RESET_MC;
+       }
+
+       if (grbm_soft_reset) {
+               tmp = RREG32(GRBM_SOFT_RESET);
+               tmp |= grbm_soft_reset;
+               dev_info(rdev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp);
+               WREG32(GRBM_SOFT_RESET, tmp);
+               tmp = RREG32(GRBM_SOFT_RESET);
+
+               udelay(50);
+
+               tmp &= ~grbm_soft_reset;
+               WREG32(GRBM_SOFT_RESET, tmp);
+               tmp = RREG32(GRBM_SOFT_RESET);
+       }
+
+       if (srbm_soft_reset) {
+               tmp = RREG32(SRBM_SOFT_RESET);
+               tmp |= srbm_soft_reset;
+               dev_info(rdev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
+               WREG32(SRBM_SOFT_RESET, tmp);
+               tmp = RREG32(SRBM_SOFT_RESET);
+
+               udelay(50);
+
+               tmp &= ~srbm_soft_reset;
+               WREG32(SRBM_SOFT_RESET, tmp);
+               tmp = RREG32(SRBM_SOFT_RESET);
+       }
+
+       /* Wait a little for things to settle down */
+       udelay(50);
+
+       evergreen_mc_resume(rdev, &save);
+       udelay(50);
+
+       cik_print_gpu_status_regs(rdev);
+}
+
+/**
+ * cik_asic_reset - soft reset GPU
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Look up which blocks are hung and attempt
+ * to reset them.
+ * Returns 0 for success.
+ */
+int cik_asic_reset(struct radeon_device *rdev)
+{
+       u32 reset_mask;
+
+       reset_mask = cik_gpu_check_soft_reset(rdev);
+
+       if (reset_mask)
+               r600_set_bios_scratch_engine_hung(rdev, true);
+
+       cik_gpu_soft_reset(rdev, reset_mask);
+
+       reset_mask = cik_gpu_check_soft_reset(rdev);
+
+       if (!reset_mask)
+               r600_set_bios_scratch_engine_hung(rdev, false);
+
+       return 0;
+}
+
+/**
+ * cik_gfx_is_lockup - check if the 3D engine is locked up
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon_ring structure holding ring information
+ *
+ * Check if the 3D engine is locked up (CIK).
+ * Returns true if the engine is locked, false if not.
+ */
+bool cik_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
+{
+       u32 reset_mask = cik_gpu_check_soft_reset(rdev);
+
+       if (!(reset_mask & (RADEON_RESET_GFX |
+                           RADEON_RESET_COMPUTE |
+                           RADEON_RESET_CP))) {
+               radeon_ring_lockup_update(ring);
+               return false;
+       }
+       /* force CP activities */
+       radeon_ring_force_activity(rdev, ring);
+       return radeon_ring_test_lockup(rdev, ring);
+}
+
+/**
+ * cik_sdma_is_lockup - Check if the DMA engine is locked up
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon_ring structure holding ring information
+ *
+ * Check if the async DMA engine is locked up (CIK).
+ * Returns true if the engine appears to be locked up, false if not.
+ */
+bool cik_sdma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
+{
+       u32 reset_mask = cik_gpu_check_soft_reset(rdev);
+       u32 mask;
+
+       if (ring->idx == R600_RING_TYPE_DMA_INDEX)
+               mask = RADEON_RESET_DMA;
+       else
+               mask = RADEON_RESET_DMA1;
+
+       if (!(reset_mask & mask)) {
+               radeon_ring_lockup_update(ring);
+               return false;
+       }
+       /* force ring activities */
+       radeon_ring_force_activity(rdev, ring);
+       return radeon_ring_test_lockup(rdev, ring);
+}
+
+/* MC */
+/**
+ * cik_mc_program - program the GPU memory controller
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Set the location of vram, gart, and AGP in the GPU's
+ * physical address space (CIK).
+ */
+static void cik_mc_program(struct radeon_device *rdev)
+{
+       struct evergreen_mc_save save;
+       u32 tmp;
+       int i, j;
+
+       /* Initialize HDP */
+       for (i = 0, j = 0; i < 32; i++, j += 0x18) {
+               WREG32((0x2c14 + j), 0x00000000);
+               WREG32((0x2c18 + j), 0x00000000);
+               WREG32((0x2c1c + j), 0x00000000);
+               WREG32((0x2c20 + j), 0x00000000);
+               WREG32((0x2c24 + j), 0x00000000);
+       }
+       WREG32(HDP_REG_COHERENCY_FLUSH_CNTL, 0);
+
+       evergreen_mc_stop(rdev, &save);
+       if (radeon_mc_wait_for_idle(rdev)) {
+               dev_warn(rdev->dev, "Wait for MC idle timedout !\n");
+       }
+       /* Lockout access through VGA aperture*/
+       WREG32(VGA_HDP_CONTROL, VGA_MEMORY_DISABLE);
+       /* Update configuration */
+       WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR,
+              rdev->mc.vram_start >> 12);
+       WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR,
+              rdev->mc.vram_end >> 12);
+       WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR,
+              rdev->vram_scratch.gpu_addr >> 12);
+       tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16;
+       tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF);
+       WREG32(MC_VM_FB_LOCATION, tmp);
+       /* XXX double check these! */
+       WREG32(HDP_NONSURFACE_BASE, (rdev->mc.vram_start >> 8));
+       WREG32(HDP_NONSURFACE_INFO, (2 << 7) | (1 << 30));
+       WREG32(HDP_NONSURFACE_SIZE, 0x3FFFFFFF);
+       WREG32(MC_VM_AGP_BASE, 0);
+       WREG32(MC_VM_AGP_TOP, 0x0FFFFFFF);
+       WREG32(MC_VM_AGP_BOT, 0x0FFFFFFF);
+       if (radeon_mc_wait_for_idle(rdev)) {
+               dev_warn(rdev->dev, "Wait for MC idle timedout !\n");
+       }
+       evergreen_mc_resume(rdev, &save);
+       /* we need to own VRAM, so turn off the VGA renderer here
+        * to stop it overwriting our objects */
+       rv515_vga_render_disable(rdev);
+}
+
+/**
+ * cik_mc_init - initialize the memory controller driver params
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Look up the amount of vram, vram width, and decide how to place
+ * vram and gart within the GPU's physical address space (CIK).
+ * Returns 0 for success.
+ */
+static int cik_mc_init(struct radeon_device *rdev)
+{
+       u32 tmp;
+       int chansize, numchan;
+
+       /* Get VRAM informations */
+       rdev->mc.vram_is_ddr = true;
+       tmp = RREG32(MC_ARB_RAMCFG);
+       if (tmp & CHANSIZE_MASK) {
+               chansize = 64;
+       } else {
+               chansize = 32;
+       }
+       tmp = RREG32(MC_SHARED_CHMAP);
+       switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) {
+       case 0:
+       default:
+               numchan = 1;
+               break;
+       case 1:
+               numchan = 2;
+               break;
+       case 2:
+               numchan = 4;
+               break;
+       case 3:
+               numchan = 8;
+               break;
+       case 4:
+               numchan = 3;
+               break;
+       case 5:
+               numchan = 6;
+               break;
+       case 6:
+               numchan = 10;
+               break;
+       case 7:
+               numchan = 12;
+               break;
+       case 8:
+               numchan = 16;
+               break;
+       }
+       rdev->mc.vram_width = numchan * chansize;
+       /* Could aper size report 0 ? */
+       rdev->mc.aper_base = pci_resource_start(rdev->pdev, 0);
+       rdev->mc.aper_size = pci_resource_len(rdev->pdev, 0);
+       /* size in MB on si */
+       rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024;
+       rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024;
+       rdev->mc.visible_vram_size = rdev->mc.aper_size;
+       si_vram_gtt_location(rdev, &rdev->mc);
+       radeon_update_bandwidth_info(rdev);
+
+       return 0;
+}
+
+/*
+ * GART
+ * VMID 0 is the physical GPU addresses as used by the kernel.
+ * VMIDs 1-15 are used for userspace clients and are handled
+ * by the radeon vm/hsa code.
+ */
+/**
+ * cik_pcie_gart_tlb_flush - gart tlb flush callback
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Flush the TLB for the VMID 0 page table (CIK).
+ */
+void cik_pcie_gart_tlb_flush(struct radeon_device *rdev)
+{
+       /* flush hdp cache */
+       WREG32(HDP_MEM_COHERENCY_FLUSH_CNTL, 0);
+
+       /* bits 0-15 are the VM contexts0-15 */
+       WREG32(VM_INVALIDATE_REQUEST, 0x1);
+}
+
+/**
+ * cik_pcie_gart_enable - gart enable
+ *
+ * @rdev: radeon_device pointer
+ *
+ * This sets up the TLBs, programs the page tables for VMID0,
+ * sets up the hw for VMIDs 1-15 which are allocated on
+ * demand, and sets up the global locations for the LDS, GDS,
+ * and GPUVM for FSA64 clients (CIK).
+ * Returns 0 for success, errors for failure.
+ */
+static int cik_pcie_gart_enable(struct radeon_device *rdev)
+{
+       int r, i;
+
+       if (rdev->gart.robj == NULL) {
+               dev_err(rdev->dev, "No VRAM object for PCIE GART.\n");
+               return -EINVAL;
+       }
+       r = radeon_gart_table_vram_pin(rdev);
+       if (r)
+               return r;
+       radeon_gart_restore(rdev);
+       /* Setup TLB control */
+       WREG32(MC_VM_MX_L1_TLB_CNTL,
+              (0xA << 7) |
+              ENABLE_L1_TLB |
+              SYSTEM_ACCESS_MODE_NOT_IN_SYS |
+              ENABLE_ADVANCED_DRIVER_MODEL |
+              SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU);
+       /* Setup L2 cache */
+       WREG32(VM_L2_CNTL, ENABLE_L2_CACHE |
+              ENABLE_L2_FRAGMENT_PROCESSING |
+              ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE |
+              ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE |
+              EFFECTIVE_L2_QUEUE_SIZE(7) |
+              CONTEXT1_IDENTITY_ACCESS_MODE(1));
+       WREG32(VM_L2_CNTL2, INVALIDATE_ALL_L1_TLBS | INVALIDATE_L2_CACHE);
+       WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY |
+              L2_CACHE_BIGK_FRAGMENT_SIZE(6));
+       /* setup context0 */
+       WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12);
+       WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12);
+       WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, rdev->gart.table_addr >> 12);
+       WREG32(VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR,
+                       (u32)(rdev->dummy_page.addr >> 12));
+       WREG32(VM_CONTEXT0_CNTL2, 0);
+       WREG32(VM_CONTEXT0_CNTL, (ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) |
+                                 RANGE_PROTECTION_FAULT_ENABLE_DEFAULT));
+
+       WREG32(0x15D4, 0);
+       WREG32(0x15D8, 0);
+       WREG32(0x15DC, 0);
+
+       /* empty context1-15 */
+       /* FIXME start with 4G, once using 2 level pt switch to full
+        * vm size space
+        */
+       /* set vm size, must be a multiple of 4 */
+       WREG32(VM_CONTEXT1_PAGE_TABLE_START_ADDR, 0);
+       WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, rdev->vm_manager.max_pfn);
+       for (i = 1; i < 16; i++) {
+               if (i < 8)
+                       WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2),
+                              rdev->gart.table_addr >> 12);
+               else
+                       WREG32(VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2),
+                              rdev->gart.table_addr >> 12);
+       }
+
+       /* enable context1-15 */
+       WREG32(VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR,
+              (u32)(rdev->dummy_page.addr >> 12));
+       WREG32(VM_CONTEXT1_CNTL2, 4);
+       WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) |
+                               RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
+                               RANGE_PROTECTION_FAULT_ENABLE_DEFAULT |
+                               DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
+                               DUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT |
+                               PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT |
+                               PDE0_PROTECTION_FAULT_ENABLE_DEFAULT |
+                               VALID_PROTECTION_FAULT_ENABLE_INTERRUPT |
+                               VALID_PROTECTION_FAULT_ENABLE_DEFAULT |
+                               READ_PROTECTION_FAULT_ENABLE_INTERRUPT |
+                               READ_PROTECTION_FAULT_ENABLE_DEFAULT |
+                               WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT |
+                               WRITE_PROTECTION_FAULT_ENABLE_DEFAULT);
+
+       /* TC cache setup ??? */
+       WREG32(TC_CFG_L1_LOAD_POLICY0, 0);
+       WREG32(TC_CFG_L1_LOAD_POLICY1, 0);
+       WREG32(TC_CFG_L1_STORE_POLICY, 0);
+
+       WREG32(TC_CFG_L2_LOAD_POLICY0, 0);
+       WREG32(TC_CFG_L2_LOAD_POLICY1, 0);
+       WREG32(TC_CFG_L2_STORE_POLICY0, 0);
+       WREG32(TC_CFG_L2_STORE_POLICY1, 0);
+       WREG32(TC_CFG_L2_ATOMIC_POLICY, 0);
+
+       WREG32(TC_CFG_L1_VOLATILE, 0);
+       WREG32(TC_CFG_L2_VOLATILE, 0);
+
+       if (rdev->family == CHIP_KAVERI) {
+               u32 tmp = RREG32(CHUB_CONTROL);
+               tmp &= ~BYPASS_VM;
+               WREG32(CHUB_CONTROL, tmp);
+       }
+
+       /* XXX SH_MEM regs */
+       /* where to put LDS, scratch, GPUVM in FSA64 space */
+       for (i = 0; i < 16; i++) {
+               cik_srbm_select(rdev, 0, 0, 0, i);
+               /* CP and shaders */
+               WREG32(SH_MEM_CONFIG, 0);
+               WREG32(SH_MEM_APE1_BASE, 1);
+               WREG32(SH_MEM_APE1_LIMIT, 0);
+               WREG32(SH_MEM_BASES, 0);
+               /* SDMA GFX */
+               WREG32(SDMA0_GFX_VIRTUAL_ADDR + SDMA0_REGISTER_OFFSET, 0);
+               WREG32(SDMA0_GFX_APE1_CNTL + SDMA0_REGISTER_OFFSET, 0);
+               WREG32(SDMA0_GFX_VIRTUAL_ADDR + SDMA1_REGISTER_OFFSET, 0);
+               WREG32(SDMA0_GFX_APE1_CNTL + SDMA1_REGISTER_OFFSET, 0);
+               /* XXX SDMA RLC - todo */
+       }
+       cik_srbm_select(rdev, 0, 0, 0, 0);
+
+       cik_pcie_gart_tlb_flush(rdev);
+       DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
+                (unsigned)(rdev->mc.gtt_size >> 20),
+                (unsigned long long)rdev->gart.table_addr);
+       rdev->gart.ready = true;
+       return 0;
+}
+
+/**
+ * cik_pcie_gart_disable - gart disable
+ *
+ * @rdev: radeon_device pointer
+ *
+ * This disables all VM page table (CIK).
+ */
+static void cik_pcie_gart_disable(struct radeon_device *rdev)
+{
+       /* Disable all tables */
+       WREG32(VM_CONTEXT0_CNTL, 0);
+       WREG32(VM_CONTEXT1_CNTL, 0);
+       /* Setup TLB control */
+       WREG32(MC_VM_MX_L1_TLB_CNTL, SYSTEM_ACCESS_MODE_NOT_IN_SYS |
+              SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU);
+       /* Setup L2 cache */
+       WREG32(VM_L2_CNTL,
+              ENABLE_L2_FRAGMENT_PROCESSING |
+              ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE |
+              ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE |
+              EFFECTIVE_L2_QUEUE_SIZE(7) |
+              CONTEXT1_IDENTITY_ACCESS_MODE(1));
+       WREG32(VM_L2_CNTL2, 0);
+       WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY |
+              L2_CACHE_BIGK_FRAGMENT_SIZE(6));
+       radeon_gart_table_vram_unpin(rdev);
+}
+
+/**
+ * cik_pcie_gart_fini - vm fini callback
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Tears down the driver GART/VM setup (CIK).
+ */
+static void cik_pcie_gart_fini(struct radeon_device *rdev)
+{
+       cik_pcie_gart_disable(rdev);
+       radeon_gart_table_vram_free(rdev);
+       radeon_gart_fini(rdev);
+}
+
+/* vm parser */
+/**
+ * cik_ib_parse - vm ib_parse callback
+ *
+ * @rdev: radeon_device pointer
+ * @ib: indirect buffer pointer
+ *
+ * CIK uses hw IB checking so this is a nop (CIK).
+ */
+int cik_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib)
+{
+       return 0;
+}
+
+/*
+ * vm
+ * VMID 0 is the physical GPU addresses as used by the kernel.
+ * VMIDs 1-15 are used for userspace clients and are handled
+ * by the radeon vm/hsa code.
+ */
+/**
+ * cik_vm_init - cik vm init callback
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Inits cik specific vm parameters (number of VMs, base of vram for
+ * VMIDs 1-15) (CIK).
+ * Returns 0 for success.
+ */
+int cik_vm_init(struct radeon_device *rdev)
+{
+       /* number of VMs */
+       rdev->vm_manager.nvm = 16;
+       /* base offset of vram pages */
+       if (rdev->flags & RADEON_IS_IGP) {
+               u64 tmp = RREG32(MC_VM_FB_OFFSET);
+               tmp <<= 22;
+               rdev->vm_manager.vram_base_offset = tmp;
+       } else
+               rdev->vm_manager.vram_base_offset = 0;
+
+       return 0;
+}
+
+/**
+ * cik_vm_fini - cik vm fini callback
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Tear down any asic specific VM setup (CIK).
+ */
+void cik_vm_fini(struct radeon_device *rdev)
+{
+}
+
+/**
+ * cik_vm_flush - cik vm flush using the CP
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Update the page table base and flush the VM TLB
+ * using the CP (CIK).
+ */
+void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
+{
+       struct radeon_ring *ring = &rdev->ring[ridx];
+
+       if (vm == NULL)
+               return;
+
+       radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+       radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+                                WRITE_DATA_DST_SEL(0)));
+       if (vm->id < 8) {
+               radeon_ring_write(ring,
+                                 (VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (vm->id << 2)) >> 2);
+       } else {
+               radeon_ring_write(ring,
+                                 (VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((vm->id - 8) << 2)) >> 2);
+       }
+       radeon_ring_write(ring, 0);
+       radeon_ring_write(ring, vm->pd_gpu_addr >> 12);
+
+       /* update SH_MEM_* regs */
+       radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+       radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+                                WRITE_DATA_DST_SEL(0)));
+       radeon_ring_write(ring, SRBM_GFX_CNTL >> 2);
+       radeon_ring_write(ring, 0);
+       radeon_ring_write(ring, VMID(vm->id));
+
+       radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 6));
+       radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+                                WRITE_DATA_DST_SEL(0)));
+       radeon_ring_write(ring, SH_MEM_BASES >> 2);
+       radeon_ring_write(ring, 0);
+
+       radeon_ring_write(ring, 0); /* SH_MEM_BASES */
+       radeon_ring_write(ring, 0); /* SH_MEM_CONFIG */
+       radeon_ring_write(ring, 1); /* SH_MEM_APE1_BASE */
+       radeon_ring_write(ring, 0); /* SH_MEM_APE1_LIMIT */
+
+       radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+       radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+                                WRITE_DATA_DST_SEL(0)));
+       radeon_ring_write(ring, SRBM_GFX_CNTL >> 2);
+       radeon_ring_write(ring, 0);
+       radeon_ring_write(ring, VMID(0));
+
+       /* HDP flush */
+       /* We should be using the WAIT_REG_MEM packet here like in
+        * cik_fence_ring_emit(), but it causes the CP to hang in this
+        * context...
+        */
+       radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+       radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+                                WRITE_DATA_DST_SEL(0)));
+       radeon_ring_write(ring, HDP_MEM_COHERENCY_FLUSH_CNTL >> 2);
+       radeon_ring_write(ring, 0);
+       radeon_ring_write(ring, 0);
+
+       /* bits 0-15 are the VM contexts0-15 */
+       radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+       radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+                                WRITE_DATA_DST_SEL(0)));
+       radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2);
+       radeon_ring_write(ring, 0);
+       radeon_ring_write(ring, 1 << vm->id);
+
+       /* compute doesn't have PFP */
+       if (ridx == RADEON_RING_TYPE_GFX_INDEX) {
+               /* sync PFP to ME, otherwise we might get invalid PFP reads */
+               radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0));
+               radeon_ring_write(ring, 0x0);
+       }
+}
+
+/**
+ * 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 */
+               if (flags & RADEON_VM_PAGE_SYSTEM) {
+                       while (count) {
+                               ndw = count * 2;
+                               if (ndw > 0xFFFFE)
+                                       ndw = 0xFFFFE;
+
+                               /* for non-physically contiguous pages (system) */
+                               ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
+                               ib->ptr[ib->length_dw++] = pe;
+                               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;
+                                       }
+                                       addr += incr;
+                                       value |= r600_flags;
+                                       ib->ptr[ib->length_dw++] = value;
+                                       ib->ptr[ib->length_dw++] = upper_32_bits(value);
+                               }
+                       }
+               } else {
+                       while (count) {
+                               ndw = count;
+                               if (ndw > 0x7FFFF)
+                                       ndw = 0x7FFFF;
+
+                               if (flags & RADEON_VM_PAGE_VALID)
+                                       value = addr;
+                               else
+                                       value = 0;
+                               /* for physically contiguous pages (vram) */
+                               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++] = 0;
+                               ib->ptr[ib->length_dw++] = value; /* value */
+                               ib->ptr[ib->length_dw++] = upper_32_bits(value);
+                               ib->ptr[ib->length_dw++] = incr; /* increment size */
+                               ib->ptr[ib->length_dw++] = 0;
+                               ib->ptr[ib->length_dw++] = ndw; /* number of entries */
+                               pe += ndw * 8;
+                               addr += ndw * incr;
+                               count -= ndw;
+                       }
+               }
+               while (ib->length_dw & 0x7)
+                       ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0);
+       }
+}
+
+/**
+ * cik_dma_vm_flush - cik vm flush using sDMA
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Update the page table base and flush the VM TLB
+ * using sDMA (CIK).
+ */
+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);
+       } else {
+               radeon_ring_write(ring, (VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((vm->id - 8) << 2)) >> 2);
+       }
+       radeon_ring_write(ring, vm->pd_gpu_addr >> 12);
+
+       /* update SH_MEM_* regs */
+       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000));
+       radeon_ring_write(ring, SRBM_GFX_CNTL >> 2);
+       radeon_ring_write(ring, VMID(vm->id));
+
+       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000));
+       radeon_ring_write(ring, SH_MEM_BASES >> 2);
+       radeon_ring_write(ring, 0);
+
+       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000));
+       radeon_ring_write(ring, SH_MEM_CONFIG >> 2);
+       radeon_ring_write(ring, 0);
+
+       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000));
+       radeon_ring_write(ring, SH_MEM_APE1_BASE >> 2);
+       radeon_ring_write(ring, 1);
+
+       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000));
+       radeon_ring_write(ring, SH_MEM_APE1_LIMIT >> 2);
+       radeon_ring_write(ring, 0);
+
+       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000));
+       radeon_ring_write(ring, SRBM_GFX_CNTL >> 2);
+       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 */
+
+       /* flush TLB */
+       radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000));
+       radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2);
+       radeon_ring_write(ring, 1 << vm->id);
+}
+
+/*
+ * RLC
+ * The RLC is a multi-purpose microengine that handles a
+ * variety of functions, the most important of which is
+ * the interrupt controller.
+ */
+/**
+ * cik_rlc_stop - stop the RLC ME
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Halt the RLC ME (MicroEngine) (CIK).
+ */
+static void cik_rlc_stop(struct radeon_device *rdev)
+{
+       int i, j, k;
+       u32 mask, tmp;
+
+       tmp = RREG32(CP_INT_CNTL_RING0);
+       tmp &= ~(CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE);
+       WREG32(CP_INT_CNTL_RING0, tmp);
+
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+
+       tmp = RREG32(RLC_CGCG_CGLS_CTRL) & 0xfffffffc;
+       WREG32(RLC_CGCG_CGLS_CTRL, tmp);
+
+       WREG32(RLC_CNTL, 0);
+
+       for (i = 0; i < rdev->config.cik.max_shader_engines; i++) {
+               for (j = 0; j < rdev->config.cik.max_sh_per_se; j++) {
+                       cik_select_se_sh(rdev, i, j);
+                       for (k = 0; k < rdev->usec_timeout; k++) {
+                               if (RREG32(RLC_SERDES_CU_MASTER_BUSY) == 0)
+                                       break;
+                               udelay(1);
+                       }
+               }
+       }
+       cik_select_se_sh(rdev, 0xffffffff, 0xffffffff);
+
+       mask = SE_MASTER_BUSY_MASK | GC_MASTER_BUSY | TC0_MASTER_BUSY | TC1_MASTER_BUSY;
+       for (k = 0; k < rdev->usec_timeout; k++) {
+               if ((RREG32(RLC_SERDES_NONCU_MASTER_BUSY) & mask) == 0)
+                       break;
+               udelay(1);
+       }
+}
+
+/**
+ * cik_rlc_start - start the RLC ME
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Unhalt the RLC ME (MicroEngine) (CIK).
+ */
+static void cik_rlc_start(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       WREG32(RLC_CNTL, RLC_ENABLE);
+
+       tmp = RREG32(CP_INT_CNTL_RING0);
+       tmp |= (CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE);
+       WREG32(CP_INT_CNTL_RING0, tmp);
+
+       udelay(50);
+}
+
+/**
+ * cik_rlc_resume - setup the RLC hw
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Initialize the RLC registers, load the ucode,
+ * and start the RLC (CIK).
+ * Returns 0 for success, -EINVAL if the ucode is not available.
+ */
+static int cik_rlc_resume(struct radeon_device *rdev)
+{
+       u32 i, size;
+       u32 clear_state_info[3];
+       const __be32 *fw_data;
+
+       if (!rdev->rlc_fw)
+               return -EINVAL;
+
+       switch (rdev->family) {
+       case CHIP_BONAIRE:
+       default:
+               size = BONAIRE_RLC_UCODE_SIZE;
+               break;
+       case CHIP_KAVERI:
+               size = KV_RLC_UCODE_SIZE;
+               break;
+       case CHIP_KABINI:
+               size = KB_RLC_UCODE_SIZE;
+               break;
+       }
+
+       cik_rlc_stop(rdev);
+
+       WREG32(GRBM_SOFT_RESET, SOFT_RESET_RLC);
+       RREG32(GRBM_SOFT_RESET);
+       udelay(50);
+       WREG32(GRBM_SOFT_RESET, 0);
+       RREG32(GRBM_SOFT_RESET);
+       udelay(50);
+
+       WREG32(RLC_LB_CNTR_INIT, 0);
+       WREG32(RLC_LB_CNTR_MAX, 0x00008000);
+
+       cik_select_se_sh(rdev, 0xffffffff, 0xffffffff);
+       WREG32(RLC_LB_INIT_CU_MASK, 0xffffffff);
+       WREG32(RLC_LB_PARAMS, 0x00600408);
+       WREG32(RLC_LB_CNTL, 0x80000004);
+
+       WREG32(RLC_MC_CNTL, 0);
+       WREG32(RLC_UCODE_CNTL, 0);
+
+       fw_data = (const __be32 *)rdev->rlc_fw->data;
+               WREG32(RLC_GPM_UCODE_ADDR, 0);
+       for (i = 0; i < size; i++)
+               WREG32(RLC_GPM_UCODE_DATA, be32_to_cpup(fw_data++));
+       WREG32(RLC_GPM_UCODE_ADDR, 0);
+
+       /* XXX */
+       clear_state_info[0] = 0;//upper_32_bits(rdev->rlc.save_restore_gpu_addr);
+       clear_state_info[1] = 0;//rdev->rlc.save_restore_gpu_addr;
+       clear_state_info[2] = 0;//cik_default_size;
+       WREG32(RLC_GPM_SCRATCH_ADDR, 0x3d);
+       for (i = 0; i < 3; i++)
+               WREG32(RLC_GPM_SCRATCH_DATA, clear_state_info[i]);
+       WREG32(RLC_DRIVER_DMA_STATUS, 0);
+
+       cik_rlc_start(rdev);
+
+       return 0;
+}
+
+/*
+ * Interrupts
+ * Starting with r6xx, interrupts are handled via a ring buffer.
+ * Ring buffers are areas of GPU accessible memory that the GPU
+ * writes interrupt vectors into and the host reads vectors out of.
+ * There is a rptr (read pointer) that determines where the
+ * host is currently reading, and a wptr (write pointer)
+ * which determines where the GPU has written.  When the
+ * pointers are equal, the ring is idle.  When the GPU
+ * writes vectors to the ring buffer, it increments the
+ * wptr.  When there is an interrupt, the host then starts
+ * fetching commands and processing them until the pointers are
+ * equal again at which point it updates the rptr.
+ */
+
+/**
+ * cik_enable_interrupts - Enable the interrupt ring buffer
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Enable the interrupt ring buffer (CIK).
+ */
+static void cik_enable_interrupts(struct radeon_device *rdev)
+{
+       u32 ih_cntl = RREG32(IH_CNTL);
+       u32 ih_rb_cntl = RREG32(IH_RB_CNTL);
+
+       ih_cntl |= ENABLE_INTR;
+       ih_rb_cntl |= IH_RB_ENABLE;
+       WREG32(IH_CNTL, ih_cntl);
+       WREG32(IH_RB_CNTL, ih_rb_cntl);
+       rdev->ih.enabled = true;
+}
+
+/**
+ * cik_disable_interrupts - Disable the interrupt ring buffer
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Disable the interrupt ring buffer (CIK).
+ */
+static void cik_disable_interrupts(struct radeon_device *rdev)
+{
+       u32 ih_rb_cntl = RREG32(IH_RB_CNTL);
+       u32 ih_cntl = RREG32(IH_CNTL);
+
+       ih_rb_cntl &= ~IH_RB_ENABLE;
+       ih_cntl &= ~ENABLE_INTR;
+       WREG32(IH_RB_CNTL, ih_rb_cntl);
+       WREG32(IH_CNTL, ih_cntl);
+       /* set rptr, wptr to 0 */
+       WREG32(IH_RB_RPTR, 0);
+       WREG32(IH_RB_WPTR, 0);
+       rdev->ih.enabled = false;
+       rdev->ih.rptr = 0;
+}
+
+/**
+ * cik_disable_interrupt_state - Disable all interrupt sources
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Clear all interrupt enable bits used by the driver (CIK).
+ */
+static void cik_disable_interrupt_state(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       /* gfx ring */
+       WREG32(CP_INT_CNTL_RING0, CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE);
+       /* sdma */
+       tmp = RREG32(SDMA0_CNTL + SDMA0_REGISTER_OFFSET) & ~TRAP_ENABLE;
+       WREG32(SDMA0_CNTL + SDMA0_REGISTER_OFFSET, tmp);
+       tmp = RREG32(SDMA0_CNTL + SDMA1_REGISTER_OFFSET) & ~TRAP_ENABLE;
+       WREG32(SDMA0_CNTL + SDMA1_REGISTER_OFFSET, tmp);
+       /* compute queues */
+       WREG32(CP_ME1_PIPE0_INT_CNTL, 0);
+       WREG32(CP_ME1_PIPE1_INT_CNTL, 0);
+       WREG32(CP_ME1_PIPE2_INT_CNTL, 0);
+       WREG32(CP_ME1_PIPE3_INT_CNTL, 0);
+       WREG32(CP_ME2_PIPE0_INT_CNTL, 0);
+       WREG32(CP_ME2_PIPE1_INT_CNTL, 0);
+       WREG32(CP_ME2_PIPE2_INT_CNTL, 0);
+       WREG32(CP_ME2_PIPE3_INT_CNTL, 0);
+       /* grbm */
+       WREG32(GRBM_INT_CNTL, 0);
+       /* vline/vblank, etc. */
+       WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
+       WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
+       if (rdev->num_crtc >= 4) {
+               WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
+               WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
+       }
+       if (rdev->num_crtc >= 6) {
+               WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
+               WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
+       }
+
+       /* dac hotplug */
+       WREG32(DAC_AUTODETECT_INT_CONTROL, 0);
+
+       /* digital hotplug */
+       tmp = RREG32(DC_HPD1_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+       WREG32(DC_HPD1_INT_CONTROL, tmp);
+       tmp = RREG32(DC_HPD2_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+       WREG32(DC_HPD2_INT_CONTROL, tmp);
+       tmp = RREG32(DC_HPD3_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+       WREG32(DC_HPD3_INT_CONTROL, tmp);
+       tmp = RREG32(DC_HPD4_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+       WREG32(DC_HPD4_INT_CONTROL, tmp);
+       tmp = RREG32(DC_HPD5_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+       WREG32(DC_HPD5_INT_CONTROL, tmp);
+       tmp = RREG32(DC_HPD6_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+       WREG32(DC_HPD6_INT_CONTROL, tmp);
+
+}
+
+/**
+ * cik_irq_init - init and enable the interrupt ring
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Allocate a ring buffer for the interrupt controller,
+ * enable the RLC, disable interrupts, enable the IH
+ * ring buffer and enable it (CIK).
+ * Called at device load and reume.
+ * Returns 0 for success, errors for failure.
+ */
+static int cik_irq_init(struct radeon_device *rdev)
+{
+       int ret = 0;
+       int rb_bufsz;
+       u32 interrupt_cntl, ih_cntl, ih_rb_cntl;
+
+       /* allocate ring */
+       ret = r600_ih_ring_alloc(rdev);
+       if (ret)
+               return ret;
+
+       /* disable irqs */
+       cik_disable_interrupts(rdev);
+
+       /* init rlc */
+       ret = cik_rlc_resume(rdev);
+       if (ret) {
+               r600_ih_ring_fini(rdev);
+               return ret;
+       }
+
+       /* setup interrupt control */
+       /* XXX this should actually be a bus address, not an MC address. same on older asics */
+       WREG32(INTERRUPT_CNTL2, rdev->ih.gpu_addr >> 8);
+       interrupt_cntl = RREG32(INTERRUPT_CNTL);
+       /* IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled without msi
+        * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN
+        */
+       interrupt_cntl &= ~IH_DUMMY_RD_OVERRIDE;
+       /* IH_REQ_NONSNOOP_EN=1 if ring is in non-cacheable memory, e.g., vram */
+       interrupt_cntl &= ~IH_REQ_NONSNOOP_EN;
+       WREG32(INTERRUPT_CNTL, interrupt_cntl);
+
+       WREG32(IH_RB_BASE, rdev->ih.gpu_addr >> 8);
+       rb_bufsz = drm_order(rdev->ih.ring_size / 4);
+
+       ih_rb_cntl = (IH_WPTR_OVERFLOW_ENABLE |
+                     IH_WPTR_OVERFLOW_CLEAR |
+                     (rb_bufsz << 1));
+
+       if (rdev->wb.enabled)
+               ih_rb_cntl |= IH_WPTR_WRITEBACK_ENABLE;
+
+       /* set the writeback address whether it's enabled or not */
+       WREG32(IH_RB_WPTR_ADDR_LO, (rdev->wb.gpu_addr + R600_WB_IH_WPTR_OFFSET) & 0xFFFFFFFC);
+       WREG32(IH_RB_WPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + R600_WB_IH_WPTR_OFFSET) & 0xFF);
+
+       WREG32(IH_RB_CNTL, ih_rb_cntl);
+
+       /* set rptr, wptr to 0 */
+       WREG32(IH_RB_RPTR, 0);
+       WREG32(IH_RB_WPTR, 0);
+
+       /* Default settings for IH_CNTL (disabled at first) */
+       ih_cntl = MC_WRREQ_CREDIT(0x10) | MC_WR_CLEAN_CNT(0x10) | MC_VMID(0);
+       /* RPTR_REARM only works if msi's are enabled */
+       if (rdev->msi_enabled)
+               ih_cntl |= RPTR_REARM;
+       WREG32(IH_CNTL, ih_cntl);
+
+       /* force the active interrupt state to all disabled */
+       cik_disable_interrupt_state(rdev);
+
+       pci_set_master(rdev->pdev);
+
+       /* enable irqs */
+       cik_enable_interrupts(rdev);
+
+       return ret;
+}
+
+/**
+ * cik_irq_set - enable/disable interrupt sources
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Enable interrupt sources on the GPU (vblanks, hpd,
+ * etc.) (CIK).
+ * Returns 0 for success, errors for failure.
+ */
+int cik_irq_set(struct radeon_device *rdev)
+{
+       u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE |
+               PRIV_INSTR_INT_ENABLE | PRIV_REG_INT_ENABLE;
+       u32 cp_m1p0, cp_m1p1, cp_m1p2, cp_m1p3;
+       u32 cp_m2p0, cp_m2p1, cp_m2p2, cp_m2p3;
+       u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0;
+       u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6;
+       u32 grbm_int_cntl = 0;
+       u32 dma_cntl, dma_cntl1;
+
+       if (!rdev->irq.installed) {
+               WARN(1, "Can't enable IRQ/MSI because no handler is installed\n");
+               return -EINVAL;
+       }
+       /* don't enable anything if the ih is disabled */
+       if (!rdev->ih.enabled) {
+               cik_disable_interrupts(rdev);
+               /* force the active interrupt state to all disabled */
+               cik_disable_interrupt_state(rdev);
+               return 0;
+       }
+
+       hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN;
+       hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN;
+       hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN;
+       hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN;
+       hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN;
+       hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN;
+
+       dma_cntl = RREG32(SDMA0_CNTL + SDMA0_REGISTER_OFFSET) & ~TRAP_ENABLE;
+       dma_cntl1 = RREG32(SDMA0_CNTL + SDMA1_REGISTER_OFFSET) & ~TRAP_ENABLE;
+
+       cp_m1p0 = RREG32(CP_ME1_PIPE0_INT_CNTL) & ~TIME_STAMP_INT_ENABLE;
+       cp_m1p1 = RREG32(CP_ME1_PIPE1_INT_CNTL) & ~TIME_STAMP_INT_ENABLE;
+       cp_m1p2 = RREG32(CP_ME1_PIPE2_INT_CNTL) & ~TIME_STAMP_INT_ENABLE;
+       cp_m1p3 = RREG32(CP_ME1_PIPE3_INT_CNTL) & ~TIME_STAMP_INT_ENABLE;
+       cp_m2p0 = RREG32(CP_ME2_PIPE0_INT_CNTL) & ~TIME_STAMP_INT_ENABLE;
+       cp_m2p1 = RREG32(CP_ME2_PIPE1_INT_CNTL) & ~TIME_STAMP_INT_ENABLE;
+       cp_m2p2 = RREG32(CP_ME2_PIPE2_INT_CNTL) & ~TIME_STAMP_INT_ENABLE;
+       cp_m2p3 = RREG32(CP_ME2_PIPE3_INT_CNTL) & ~TIME_STAMP_INT_ENABLE;
+
+       /* enable CP interrupts on all rings */
+       if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) {
+               DRM_DEBUG("cik_irq_set: sw int gfx\n");
+               cp_int_cntl |= TIME_STAMP_INT_ENABLE;
+       }
+       if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_CP1_INDEX])) {
+               struct radeon_ring *ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX];
+               DRM_DEBUG("si_irq_set: sw int cp1\n");
+               if (ring->me == 1) {
+                       switch (ring->pipe) {
+                       case 0:
+                               cp_m1p0 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 1:
+                               cp_m1p1 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 2:
+                               cp_m1p2 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 3:
+                               cp_m1p2 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       default:
+                               DRM_DEBUG("si_irq_set: sw int cp1 invalid pipe %d\n", ring->pipe);
+                               break;
+                       }
+               } else if (ring->me == 2) {
+                       switch (ring->pipe) {
+                       case 0:
+                               cp_m2p0 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 1:
+                               cp_m2p1 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 2:
+                               cp_m2p2 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 3:
+                               cp_m2p2 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       default:
+                               DRM_DEBUG("si_irq_set: sw int cp1 invalid pipe %d\n", ring->pipe);
+                               break;
+                       }
+               } else {
+                       DRM_DEBUG("si_irq_set: sw int cp1 invalid me %d\n", ring->me);
+               }
+       }
+       if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_CP2_INDEX])) {
+               struct radeon_ring *ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX];
+               DRM_DEBUG("si_irq_set: sw int cp2\n");
+               if (ring->me == 1) {
+                       switch (ring->pipe) {
+                       case 0:
+                               cp_m1p0 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 1:
+                               cp_m1p1 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 2:
+                               cp_m1p2 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 3:
+                               cp_m1p2 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       default:
+                               DRM_DEBUG("si_irq_set: sw int cp2 invalid pipe %d\n", ring->pipe);
+                               break;
+                       }
+               } else if (ring->me == 2) {
+                       switch (ring->pipe) {
+                       case 0:
+                               cp_m2p0 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 1:
+                               cp_m2p1 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 2:
+                               cp_m2p2 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       case 3:
+                               cp_m2p2 |= TIME_STAMP_INT_ENABLE;
+                               break;
+                       default:
+                               DRM_DEBUG("si_irq_set: sw int cp2 invalid pipe %d\n", ring->pipe);
+                               break;
+                       }
+               } else {
+                       DRM_DEBUG("si_irq_set: sw int cp2 invalid me %d\n", ring->me);
+               }
+       }
+
+       if (atomic_read(&rdev->irq.ring_int[R600_RING_TYPE_DMA_INDEX])) {
+               DRM_DEBUG("cik_irq_set: sw int dma\n");
+               dma_cntl |= TRAP_ENABLE;
+       }
+
+       if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_DMA1_INDEX])) {
+               DRM_DEBUG("cik_irq_set: sw int dma1\n");
+               dma_cntl1 |= TRAP_ENABLE;
+       }
+
+       if (rdev->irq.crtc_vblank_int[0] ||
+           atomic_read(&rdev->irq.pflip[0])) {
+               DRM_DEBUG("cik_irq_set: vblank 0\n");
+               crtc1 |= VBLANK_INTERRUPT_MASK;
+       }
+       if (rdev->irq.crtc_vblank_int[1] ||
+           atomic_read(&rdev->irq.pflip[1])) {
+               DRM_DEBUG("cik_irq_set: vblank 1\n");
+               crtc2 |= VBLANK_INTERRUPT_MASK;
+       }
+       if (rdev->irq.crtc_vblank_int[2] ||
+           atomic_read(&rdev->irq.pflip[2])) {
+               DRM_DEBUG("cik_irq_set: vblank 2\n");
+               crtc3 |= VBLANK_INTERRUPT_MASK;
+       }
+       if (rdev->irq.crtc_vblank_int[3] ||
+           atomic_read(&rdev->irq.pflip[3])) {
+               DRM_DEBUG("cik_irq_set: vblank 3\n");
+               crtc4 |= VBLANK_INTERRUPT_MASK;
+       }
+       if (rdev->irq.crtc_vblank_int[4] ||
+           atomic_read(&rdev->irq.pflip[4])) {
+               DRM_DEBUG("cik_irq_set: vblank 4\n");
+               crtc5 |= VBLANK_INTERRUPT_MASK;
+       }
+       if (rdev->irq.crtc_vblank_int[5] ||
+           atomic_read(&rdev->irq.pflip[5])) {
+               DRM_DEBUG("cik_irq_set: vblank 5\n");
+               crtc6 |= VBLANK_INTERRUPT_MASK;
+       }
+       if (rdev->irq.hpd[0]) {
+               DRM_DEBUG("cik_irq_set: hpd 1\n");
+               hpd1 |= DC_HPDx_INT_EN;
+       }
+       if (rdev->irq.hpd[1]) {
+               DRM_DEBUG("cik_irq_set: hpd 2\n");
+               hpd2 |= DC_HPDx_INT_EN;
+       }
+       if (rdev->irq.hpd[2]) {
+               DRM_DEBUG("cik_irq_set: hpd 3\n");
+               hpd3 |= DC_HPDx_INT_EN;
+       }
+       if (rdev->irq.hpd[3]) {
+               DRM_DEBUG("cik_irq_set: hpd 4\n");
+               hpd4 |= DC_HPDx_INT_EN;
+       }
+       if (rdev->irq.hpd[4]) {
+               DRM_DEBUG("cik_irq_set: hpd 5\n");
+               hpd5 |= DC_HPDx_INT_EN;
+       }
+       if (rdev->irq.hpd[5]) {
+               DRM_DEBUG("cik_irq_set: hpd 6\n");
+               hpd6 |= DC_HPDx_INT_EN;
+       }
+
+       WREG32(CP_INT_CNTL_RING0, cp_int_cntl);
+
+       WREG32(SDMA0_CNTL + SDMA0_REGISTER_OFFSET, dma_cntl);
+       WREG32(SDMA0_CNTL + SDMA1_REGISTER_OFFSET, dma_cntl1);
+
+       WREG32(CP_ME1_PIPE0_INT_CNTL, cp_m1p0);
+       WREG32(CP_ME1_PIPE1_INT_CNTL, cp_m1p1);
+       WREG32(CP_ME1_PIPE2_INT_CNTL, cp_m1p2);
+       WREG32(CP_ME1_PIPE3_INT_CNTL, cp_m1p3);
+       WREG32(CP_ME2_PIPE0_INT_CNTL, cp_m2p0);
+       WREG32(CP_ME2_PIPE1_INT_CNTL, cp_m2p1);
+       WREG32(CP_ME2_PIPE2_INT_CNTL, cp_m2p2);
+       WREG32(CP_ME2_PIPE3_INT_CNTL, cp_m2p3);
+
+       WREG32(GRBM_INT_CNTL, grbm_int_cntl);
+
+       WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, crtc1);
+       WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, crtc2);
+       if (rdev->num_crtc >= 4) {
+               WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC2_REGISTER_OFFSET, crtc3);
+               WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC3_REGISTER_OFFSET, crtc4);
+       }
+       if (rdev->num_crtc >= 6) {
+               WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, crtc5);
+               WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, crtc6);
+       }
+
+       WREG32(DC_HPD1_INT_CONTROL, hpd1);
+       WREG32(DC_HPD2_INT_CONTROL, hpd2);
+       WREG32(DC_HPD3_INT_CONTROL, hpd3);
+       WREG32(DC_HPD4_INT_CONTROL, hpd4);
+       WREG32(DC_HPD5_INT_CONTROL, hpd5);
+       WREG32(DC_HPD6_INT_CONTROL, hpd6);
+
+       return 0;
+}
+
+/**
+ * cik_irq_ack - ack interrupt sources
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Ack interrupt sources on the GPU (vblanks, hpd,
+ * etc.) (CIK).  Certain interrupts sources are sw
+ * generated and do not require an explicit ack.
+ */
+static inline void cik_irq_ack(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       rdev->irq.stat_regs.cik.disp_int = RREG32(DISP_INTERRUPT_STATUS);
+       rdev->irq.stat_regs.cik.disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE);
+       rdev->irq.stat_regs.cik.disp_int_cont2 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE2);
+       rdev->irq.stat_regs.cik.disp_int_cont3 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE3);
+       rdev->irq.stat_regs.cik.disp_int_cont4 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE4);
+       rdev->irq.stat_regs.cik.disp_int_cont5 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE5);
+       rdev->irq.stat_regs.cik.disp_int_cont6 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE6);
+
+       if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VBLANK_INTERRUPT)
+               WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VBLANK_ACK);
+       if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VLINE_INTERRUPT)
+               WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VLINE_ACK);
+       if (rdev->irq.stat_regs.cik.disp_int_cont & LB_D2_VBLANK_INTERRUPT)
+               WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VBLANK_ACK);
+       if (rdev->irq.stat_regs.cik.disp_int_cont & LB_D2_VLINE_INTERRUPT)
+               WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VLINE_ACK);
+
+       if (rdev->num_crtc >= 4) {
+               if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT)
+                       WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VBLANK_ACK);
+               if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VLINE_INTERRUPT)
+                       WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VLINE_ACK);
+               if (rdev->irq.stat_regs.cik.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT)
+                       WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VBLANK_ACK);
+               if (rdev->irq.stat_regs.cik.disp_int_cont3 & LB_D4_VLINE_INTERRUPT)
+                       WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VLINE_ACK);
+       }
+
+       if (rdev->num_crtc >= 6) {
+               if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT)
+                       WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VBLANK_ACK);
+               if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VLINE_INTERRUPT)
+                       WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VLINE_ACK);
+               if (rdev->irq.stat_regs.cik.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT)
+                       WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VBLANK_ACK);
+               if (rdev->irq.stat_regs.cik.disp_int_cont5 & LB_D6_VLINE_INTERRUPT)
+                       WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VLINE_ACK);
+       }
+
+       if (rdev->irq.stat_regs.cik.disp_int & DC_HPD1_INTERRUPT) {
+               tmp = RREG32(DC_HPD1_INT_CONTROL);
+               tmp |= DC_HPDx_INT_ACK;
+               WREG32(DC_HPD1_INT_CONTROL, tmp);
+       }
+       if (rdev->irq.stat_regs.cik.disp_int_cont & DC_HPD2_INTERRUPT) {
+               tmp = RREG32(DC_HPD2_INT_CONTROL);
+               tmp |= DC_HPDx_INT_ACK;
+               WREG32(DC_HPD2_INT_CONTROL, tmp);
+       }
+       if (rdev->irq.stat_regs.cik.disp_int_cont2 & DC_HPD3_INTERRUPT) {
+               tmp = RREG32(DC_HPD3_INT_CONTROL);
+               tmp |= DC_HPDx_INT_ACK;
+               WREG32(DC_HPD3_INT_CONTROL, tmp);
+       }
+       if (rdev->irq.stat_regs.cik.disp_int_cont3 & DC_HPD4_INTERRUPT) {
+               tmp = RREG32(DC_HPD4_INT_CONTROL);
+               tmp |= DC_HPDx_INT_ACK;
+               WREG32(DC_HPD4_INT_CONTROL, tmp);
+       }
+       if (rdev->irq.stat_regs.cik.disp_int_cont4 & DC_HPD5_INTERRUPT) {
+               tmp = RREG32(DC_HPD5_INT_CONTROL);
+               tmp |= DC_HPDx_INT_ACK;
+               WREG32(DC_HPD5_INT_CONTROL, tmp);
+       }
+       if (rdev->irq.stat_regs.cik.disp_int_cont5 & DC_HPD6_INTERRUPT) {
+               tmp = RREG32(DC_HPD5_INT_CONTROL);
+               tmp |= DC_HPDx_INT_ACK;
+               WREG32(DC_HPD6_INT_CONTROL, tmp);
+       }
+}
+
+/**
+ * cik_irq_disable - disable interrupts
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Disable interrupts on the hw (CIK).
+ */
+static void cik_irq_disable(struct radeon_device *rdev)
+{
+       cik_disable_interrupts(rdev);
+       /* Wait and acknowledge irq */
+       mdelay(1);
+       cik_irq_ack(rdev);
+       cik_disable_interrupt_state(rdev);
+}
+
+/**
+ * cik_irq_disable - disable interrupts for suspend
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Disable interrupts and stop the RLC (CIK).
+ * Used for suspend.
+ */
+static void cik_irq_suspend(struct radeon_device *rdev)
+{
+       cik_irq_disable(rdev);
+       cik_rlc_stop(rdev);
+}
+
+/**
+ * cik_irq_fini - tear down interrupt support
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Disable interrupts on the hw and free the IH ring
+ * buffer (CIK).
+ * Used for driver unload.
+ */
+static void cik_irq_fini(struct radeon_device *rdev)
+{
+       cik_irq_suspend(rdev);
+       r600_ih_ring_fini(rdev);
+}
+
+/**
+ * cik_get_ih_wptr - get the IH ring buffer wptr
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Get the IH ring buffer wptr from either the register
+ * or the writeback memory buffer (CIK).  Also check for
+ * ring buffer overflow and deal with it.
+ * Used by cik_irq_process().
+ * Returns the value of the wptr.
+ */
+static inline u32 cik_get_ih_wptr(struct radeon_device *rdev)
+{
+       u32 wptr, tmp;
+
+       if (rdev->wb.enabled)
+               wptr = le32_to_cpu(rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4]);
+       else
+               wptr = RREG32(IH_RB_WPTR);
+
+       if (wptr & RB_OVERFLOW) {
+               /* When a ring buffer overflow happen start parsing interrupt
+                * from the last not overwritten vector (wptr + 16). Hopefully
+                * this should allow us to catchup.
+                */
+               dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n",
+                       wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask);
+               rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask;
+               tmp = RREG32(IH_RB_CNTL);
+               tmp |= IH_WPTR_OVERFLOW_CLEAR;
+               WREG32(IH_RB_CNTL, tmp);
+       }
+       return (wptr & rdev->ih.ptr_mask);
+}
+
+/*        CIK IV Ring
+ * Each IV ring entry is 128 bits:
+ * [7:0]    - interrupt source id
+ * [31:8]   - reserved
+ * [59:32]  - interrupt source data
+ * [63:60]  - reserved
+ * [71:64]  - RINGID
+ *            CP:
+ *            ME_ID [1:0], PIPE_ID[1:0], QUEUE_ID[2:0]
+ *            QUEUE_ID - for compute, which of the 8 queues owned by the dispatcher
+ *                     - for gfx, hw shader state (0=PS...5=LS, 6=CS)
+ *            ME_ID - 0 = gfx, 1 = first 4 CS pipes, 2 = second 4 CS pipes
+ *            PIPE_ID - ME0 0=3D
+ *                    - ME1&2 compute dispatcher (4 pipes each)
+ *            SDMA:
+ *            INSTANCE_ID [1:0], QUEUE_ID[1:0]
+ *            INSTANCE_ID - 0 = sdma0, 1 = sdma1
+ *            QUEUE_ID - 0 = gfx, 1 = rlc0, 2 = rlc1
+ * [79:72]  - VMID
+ * [95:80]  - PASID
+ * [127:96] - reserved
+ */
+/**
+ * cik_irq_process - interrupt handler
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Interrupt hander (CIK).  Walk the IH ring,
+ * ack interrupts and schedule work to handle
+ * interrupt events.
+ * Returns irq process return code.
+ */
+int cik_irq_process(struct radeon_device *rdev)
+{
+       struct radeon_ring *cp1_ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX];
+       struct radeon_ring *cp2_ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX];
+       u32 wptr;
+       u32 rptr;
+       u32 src_id, src_data, ring_id;
+       u8 me_id, pipe_id, queue_id;
+       u32 ring_index;
+       bool queue_hotplug = false;
+       bool queue_reset = false;
+
+       if (!rdev->ih.enabled || rdev->shutdown)
+               return IRQ_NONE;
+
+       wptr = cik_get_ih_wptr(rdev);
+
+restart_ih:
+       /* is somebody else already processing irqs? */
+       if (atomic_xchg(&rdev->ih.lock, 1))
+               return IRQ_NONE;
+
+       rptr = rdev->ih.rptr;
+       DRM_DEBUG("cik_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
+
+       /* Order reading of wptr vs. reading of IH ring data */
+       rmb();
+
+       /* display interrupts */
+       cik_irq_ack(rdev);
+
+       while (rptr != wptr) {
+               /* wptr/rptr are in bytes! */
+               ring_index = rptr / 4;
+               src_id =  le32_to_cpu(rdev->ih.ring[ring_index]) & 0xff;
+               src_data = le32_to_cpu(rdev->ih.ring[ring_index + 1]) & 0xfffffff;
+               ring_id = le32_to_cpu(rdev->ih.ring[ring_index + 2]) & 0xff;
+
+               switch (src_id) {
+               case 1: /* D1 vblank/vline */
+                       switch (src_data) {
+                       case 0: /* D1 vblank */
+                               if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VBLANK_INTERRUPT) {
+                                       if (rdev->irq.crtc_vblank_int[0]) {
+                                               drm_handle_vblank(rdev->ddev, 0);
+                                               rdev->pm.vblank_sync = true;
+                                               wake_up(&rdev->irq.vblank_queue);
+                                       }
+                                       if (atomic_read(&rdev->irq.pflip[0]))
+                                               radeon_crtc_handle_flip(rdev, 0);
+                                       rdev->irq.stat_regs.cik.disp_int &= ~LB_D1_VBLANK_INTERRUPT;
+                                       DRM_DEBUG("IH: D1 vblank\n");
+                               }
+                               break;
+                       case 1: /* D1 vline */
+                               if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VLINE_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int &= ~LB_D1_VLINE_INTERRUPT;
+                                       DRM_DEBUG("IH: D1 vline\n");
+                               }
+                               break;
+                       default:
+                               DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+                               break;
+                       }
+                       break;
+               case 2: /* D2 vblank/vline */
+                       switch (src_data) {
+                       case 0: /* D2 vblank */
+                               if (rdev->irq.stat_regs.cik.disp_int_cont & LB_D2_VBLANK_INTERRUPT) {
+                                       if (rdev->irq.crtc_vblank_int[1]) {
+                                               drm_handle_vblank(rdev->ddev, 1);
+                                               rdev->pm.vblank_sync = true;
+                                               wake_up(&rdev->irq.vblank_queue);
+                                       }
+                                       if (atomic_read(&rdev->irq.pflip[1]))
+                                               radeon_crtc_handle_flip(rdev, 1);
+                                       rdev->irq.stat_regs.cik.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT;
+                                       DRM_DEBUG("IH: D2 vblank\n");
+                               }
+                               break;
+                       case 1: /* D2 vline */
+                               if (rdev->irq.stat_regs.cik.disp_int_cont & LB_D2_VLINE_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int_cont &= ~LB_D2_VLINE_INTERRUPT;
+                                       DRM_DEBUG("IH: D2 vline\n");
+                               }
+                               break;
+                       default:
+                               DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+                               break;
+                       }
+                       break;
+               case 3: /* D3 vblank/vline */
+                       switch (src_data) {
+                       case 0: /* D3 vblank */
+                               if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) {
+                                       if (rdev->irq.crtc_vblank_int[2]) {
+                                               drm_handle_vblank(rdev->ddev, 2);
+                                               rdev->pm.vblank_sync = true;
+                                               wake_up(&rdev->irq.vblank_queue);
+                                       }
+                                       if (atomic_read(&rdev->irq.pflip[2]))
+                                               radeon_crtc_handle_flip(rdev, 2);
+                                       rdev->irq.stat_regs.cik.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT;
+                                       DRM_DEBUG("IH: D3 vblank\n");
+                               }
+                               break;
+                       case 1: /* D3 vline */
+                               if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int_cont2 &= ~LB_D3_VLINE_INTERRUPT;
+                                       DRM_DEBUG("IH: D3 vline\n");
+                               }
+                               break;
+                       default:
+                               DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+                               break;
+                       }
+                       break;
+               case 4: /* D4 vblank/vline */
+                       switch (src_data) {
+                       case 0: /* D4 vblank */
+                               if (rdev->irq.stat_regs.cik.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) {
+                                       if (rdev->irq.crtc_vblank_int[3]) {
+                                               drm_handle_vblank(rdev->ddev, 3);
+                                               rdev->pm.vblank_sync = true;
+                                               wake_up(&rdev->irq.vblank_queue);
+                                       }
+                                       if (atomic_read(&rdev->irq.pflip[3]))
+                                               radeon_crtc_handle_flip(rdev, 3);
+                                       rdev->irq.stat_regs.cik.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT;
+                                       DRM_DEBUG("IH: D4 vblank\n");
+                               }
+                               break;
+                       case 1: /* D4 vline */
+                               if (rdev->irq.stat_regs.cik.disp_int_cont3 & LB_D4_VLINE_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int_cont3 &= ~LB_D4_VLINE_INTERRUPT;
+                                       DRM_DEBUG("IH: D4 vline\n");
+                               }
+                               break;
+                       default:
+                               DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+                               break;
+                       }
+                       break;
+               case 5: /* D5 vblank/vline */
+                       switch (src_data) {
+                       case 0: /* D5 vblank */
+                               if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) {
+                                       if (rdev->irq.crtc_vblank_int[4]) {
+                                               drm_handle_vblank(rdev->ddev, 4);
+                                               rdev->pm.vblank_sync = true;
+                                               wake_up(&rdev->irq.vblank_queue);
+                                       }
+                                       if (atomic_read(&rdev->irq.pflip[4]))
+                                               radeon_crtc_handle_flip(rdev, 4);
+                                       rdev->irq.stat_regs.cik.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT;
+                                       DRM_DEBUG("IH: D5 vblank\n");
+                               }
+                               break;
+                       case 1: /* D5 vline */
+                               if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int_cont4 &= ~LB_D5_VLINE_INTERRUPT;
+                                       DRM_DEBUG("IH: D5 vline\n");
+                               }
+                               break;
+                       default:
+                               DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+                               break;
+                       }
+                       break;
+               case 6: /* D6 vblank/vline */
+                       switch (src_data) {
+                       case 0: /* D6 vblank */
+                               if (rdev->irq.stat_regs.cik.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) {
+                                       if (rdev->irq.crtc_vblank_int[5]) {
+                                               drm_handle_vblank(rdev->ddev, 5);
+                                               rdev->pm.vblank_sync = true;
+                                               wake_up(&rdev->irq.vblank_queue);
+                                       }
+                                       if (atomic_read(&rdev->irq.pflip[5]))
+                                               radeon_crtc_handle_flip(rdev, 5);
+                                       rdev->irq.stat_regs.cik.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT;
+                                       DRM_DEBUG("IH: D6 vblank\n");
+                               }
+                               break;
+                       case 1: /* D6 vline */
+                               if (rdev->irq.stat_regs.cik.disp_int_cont5 & LB_D6_VLINE_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int_cont5 &= ~LB_D6_VLINE_INTERRUPT;
+                                       DRM_DEBUG("IH: D6 vline\n");
+                               }
+                               break;
+                       default:
+                               DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+                               break;
+                       }
+                       break;
+               case 42: /* HPD hotplug */
+                       switch (src_data) {
+                       case 0:
+                               if (rdev->irq.stat_regs.cik.disp_int & DC_HPD1_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int &= ~DC_HPD1_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD1\n");
+                               }
+                               break;
+                       case 1:
+                               if (rdev->irq.stat_regs.cik.disp_int_cont & DC_HPD2_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int_cont &= ~DC_HPD2_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD2\n");
+                               }
+                               break;
+                       case 2:
+                               if (rdev->irq.stat_regs.cik.disp_int_cont2 & DC_HPD3_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int_cont2 &= ~DC_HPD3_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD3\n");
+                               }
+                               break;
+                       case 3:
+                               if (rdev->irq.stat_regs.cik.disp_int_cont3 & DC_HPD4_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int_cont3 &= ~DC_HPD4_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD4\n");
+                               }
+                               break;
+                       case 4:
+                               if (rdev->irq.stat_regs.cik.disp_int_cont4 & DC_HPD5_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int_cont4 &= ~DC_HPD5_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD5\n");
+                               }
+                               break;
+                       case 5:
+                               if (rdev->irq.stat_regs.cik.disp_int_cont5 & DC_HPD6_INTERRUPT) {
+                                       rdev->irq.stat_regs.cik.disp_int_cont5 &= ~DC_HPD6_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD6\n");
+                               }
+                               break;
+                       default:
+                               DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+                               break;
+                       }
+                       break;
+               case 146:
+               case 147:
+                       dev_err(rdev->dev, "GPU fault detected: %d 0x%08x\n", src_id, src_data);
+                       dev_err(rdev->dev, "  VM_CONTEXT1_PROTECTION_FAULT_ADDR   0x%08X\n",
+                               RREG32(VM_CONTEXT1_PROTECTION_FAULT_ADDR));
+                       dev_err(rdev->dev, "  VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n",
+                               RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS));
+                       /* reset addr and status */
+                       WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1);
+                       break;
+               case 176: /* GFX RB CP_INT */
+               case 177: /* GFX IB CP_INT */
+                       radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX);
+                       break;
+               case 181: /* CP EOP event */
+                       DRM_DEBUG("IH: CP EOP\n");
+                       /* XXX check the bitfield order! */
+                       me_id = (ring_id & 0x60) >> 5;
+                       pipe_id = (ring_id & 0x18) >> 3;
+                       queue_id = (ring_id & 0x7) >> 0;
+                       switch (me_id) {
+                       case 0:
+                               radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX);
+                               break;
+                       case 1:
+                       case 2:
+                               if ((cp1_ring->me == me_id) & (cp1_ring->pipe == pipe_id))
+                                       radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP1_INDEX);
+                               if ((cp2_ring->me == me_id) & (cp2_ring->pipe == pipe_id))
+                                       radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP2_INDEX);
+                               break;
+                       }
+                       break;
+               case 184: /* CP Privileged reg access */
+                       DRM_ERROR("Illegal register access in command stream\n");
+                       /* XXX check the bitfield order! */
+                       me_id = (ring_id & 0x60) >> 5;
+                       pipe_id = (ring_id & 0x18) >> 3;
+                       queue_id = (ring_id & 0x7) >> 0;
+                       switch (me_id) {
+                       case 0:
+                               /* This results in a full GPU reset, but all we need to do is soft
+                                * reset the CP for gfx
+                                */
+                               queue_reset = true;
+                               break;
+                       case 1:
+                               /* XXX compute */
+                               queue_reset = true;
+                               break;
+                       case 2:
+                               /* XXX compute */
+                               queue_reset = true;
+                               break;
+                       }
+                       break;
+               case 185: /* CP Privileged inst */
+                       DRM_ERROR("Illegal instruction in command stream\n");
+                       /* XXX check the bitfield order! */
+                       me_id = (ring_id & 0x60) >> 5;
+                       pipe_id = (ring_id & 0x18) >> 3;
+                       queue_id = (ring_id & 0x7) >> 0;
+                       switch (me_id) {
+                       case 0:
+                               /* This results in a full GPU reset, but all we need to do is soft
+                                * reset the CP for gfx
+                                */
+                               queue_reset = true;
+                               break;
+                       case 1:
+                               /* XXX compute */
+                               queue_reset = true;
+                               break;
+                       case 2:
+                               /* XXX compute */
+                               queue_reset = true;
+                               break;
+                       }
+                       break;
+               case 224: /* SDMA trap event */
+                       /* XXX check the bitfield order! */
+                       me_id = (ring_id & 0x3) >> 0;
+                       queue_id = (ring_id & 0xc) >> 2;
+                       DRM_DEBUG("IH: SDMA trap\n");
+                       switch (me_id) {
+                       case 0:
+                               switch (queue_id) {
+                               case 0:
+                                       radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX);
+                                       break;
+                               case 1:
+                                       /* XXX compute */
+                                       break;
+                               case 2:
+                                       /* XXX compute */
+                                       break;
+                               }
+                               break;
+                       case 1:
+                               switch (queue_id) {
+                               case 0:
+                                       radeon_fence_process(rdev, CAYMAN_RING_TYPE_DMA1_INDEX);
+                                       break;
+                               case 1:
+                                       /* XXX compute */
+                                       break;
+                               case 2:
+                                       /* XXX compute */
+                                       break;
+                               }
+                               break;
+                       }
+                       break;
+               case 241: /* SDMA Privileged inst */
+               case 247: /* SDMA Privileged inst */
+                       DRM_ERROR("Illegal instruction in SDMA command stream\n");
+                       /* XXX check the bitfield order! */
+                       me_id = (ring_id & 0x3) >> 0;
+                       queue_id = (ring_id & 0xc) >> 2;
+                       switch (me_id) {
+                       case 0:
+                               switch (queue_id) {
+                               case 0:
+                                       queue_reset = true;
+                                       break;
+                               case 1:
+                                       /* XXX compute */
+                                       queue_reset = true;
+                                       break;
+                               case 2:
+                                       /* XXX compute */
+                                       queue_reset = true;
+                                       break;
+                               }
+                               break;
+                       case 1:
+                               switch (queue_id) {
+                               case 0:
+                                       queue_reset = true;
+                                       break;
+                               case 1:
+                                       /* XXX compute */
+                                       queue_reset = true;
+                                       break;
+                               case 2:
+                                       /* XXX compute */
+                                       queue_reset = true;
+                                       break;
+                               }
+                               break;
+                       }
+                       break;
+               case 233: /* GUI IDLE */
+                       DRM_DEBUG("IH: GUI idle\n");
+                       break;
+               default:
+                       DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+                       break;
+               }
+
+               /* wptr/rptr are in bytes! */
+               rptr += 16;
+               rptr &= rdev->ih.ptr_mask;
+       }
+       if (queue_hotplug)
+               schedule_work(&rdev->hotplug_work);
+       if (queue_reset)
+               schedule_work(&rdev->reset_work);
+       rdev->ih.rptr = rptr;
+       WREG32(IH_RB_RPTR, rdev->ih.rptr);
+       atomic_set(&rdev->ih.lock, 0);
+
+       /* make sure wptr hasn't changed while processing */
+       wptr = cik_get_ih_wptr(rdev);
+       if (wptr != rptr)
+               goto restart_ih;
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * startup/shutdown callbacks
+ */
+/**
+ * cik_startup - program the asic to a functional state
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Programs the asic to a functional state (CIK).
+ * Called by cik_init() and cik_resume().
+ * Returns 0 for success, error for failure.
+ */
+static int cik_startup(struct radeon_device *rdev)
+{
+       struct radeon_ring *ring;
+       int r;
+
+       if (rdev->flags & RADEON_IS_IGP) {
+               if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw ||
+                   !rdev->mec_fw || !rdev->sdma_fw || !rdev->rlc_fw) {
+                       r = cik_init_microcode(rdev);
+                       if (r) {
+                               DRM_ERROR("Failed to load firmware!\n");
+                               return r;
+                       }
+               }
+       } else {
+               if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw ||
+                   !rdev->mec_fw || !rdev->sdma_fw || !rdev->rlc_fw ||
+                   !rdev->mc_fw) {
+                       r = cik_init_microcode(rdev);
+                       if (r) {
+                               DRM_ERROR("Failed to load firmware!\n");
+                               return r;
+                       }
+               }
+
+               r = ci_mc_load_microcode(rdev);
+               if (r) {
+                       DRM_ERROR("Failed to load MC firmware!\n");
+                       return r;
+               }
+       }
+
+       r = r600_vram_scratch_init(rdev);
+       if (r)
+               return r;
+
+       cik_mc_program(rdev);
+       r = cik_pcie_gart_enable(rdev);
+       if (r)
+               return r;
+       cik_gpu_init(rdev);
+
+       /* allocate rlc buffers */
+       r = si_rlc_init(rdev);
+       if (r) {
+               DRM_ERROR("Failed to init rlc BOs!\n");
+               return r;
+       }
+
+       /* allocate wb buffer */
+       r = radeon_wb_init(rdev);
+       if (r)
+               return r;
+
+       /* allocate mec buffers */
+       r = cik_mec_init(rdev);
+       if (r) {
+               DRM_ERROR("Failed to init MEC BOs!\n");
+               return r;
+       }
+
+       r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX);
+       if (r) {
+               dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r);
+               return r;
+       }
+
+       r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP1_INDEX);
+       if (r) {
+               dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r);
+               return r;
+       }
+
+       r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP2_INDEX);
+       if (r) {
+               dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r);
+               return r;
+       }
+
+       r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_DMA_INDEX);
+       if (r) {
+               dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r);
+               return r;
+       }
+
+       r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_DMA1_INDEX);
+       if (r) {
+               dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r);
+               return r;
+       }
+
+       r = cik_uvd_resume(rdev);
+       if (!r) {
+               r = radeon_fence_driver_start_ring(rdev,
+                                                  R600_RING_TYPE_UVD_INDEX);
+               if (r)
+                       dev_err(rdev->dev, "UVD fences init error (%d).\n", r);
+       }
+       if (r)
+               rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_size = 0;
+
+       /* Enable IRQ */
+       if (!rdev->irq.installed) {
+               r = radeon_irq_kms_init(rdev);
+               if (r)
+                       return r;
+       }
+
+       r = cik_irq_init(rdev);
+       if (r) {
+               DRM_ERROR("radeon: IH init failed (%d).\n", r);
+               radeon_irq_kms_fini(rdev);
+               return r;
+       }
+       cik_irq_set(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,
+                            0, 0xfffff, RADEON_CP_PACKET2);
+       if (r)
+               return r;
+
+       /* set up the compute queues */
+       /* type-2 packets are deprecated on MEC, use type-3 instead */
+       ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX];
+       r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP1_RPTR_OFFSET,
+                            CP_HQD_PQ_RPTR, CP_HQD_PQ_WPTR,
+                            0, 0xfffff, PACKET3(PACKET3_NOP, 0x3FFF));
+       if (r)
+               return r;
+       ring->me = 1; /* first MEC */
+       ring->pipe = 0; /* first pipe */
+       ring->queue = 0; /* first queue */
+       ring->wptr_offs = CIK_WB_CP1_WPTR_OFFSET;
+
+       /* type-2 packets are deprecated on MEC, use type-3 instead */
+       ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX];
+       r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP2_RPTR_OFFSET,
+                            CP_HQD_PQ_RPTR, CP_HQD_PQ_WPTR,
+                            0, 0xffffffff, PACKET3(PACKET3_NOP, 0x3FFF));
+       if (r)
+               return r;
+       /* dGPU only have 1 MEC */
+       ring->me = 1; /* first MEC */
+       ring->pipe = 0; /* first pipe */
+       ring->queue = 1; /* second queue */
+       ring->wptr_offs = CIK_WB_CP2_WPTR_OFFSET;
+
+       ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX];
+       r = radeon_ring_init(rdev, ring, ring->ring_size, R600_WB_DMA_RPTR_OFFSET,
+                            SDMA0_GFX_RB_RPTR + SDMA0_REGISTER_OFFSET,
+                            SDMA0_GFX_RB_WPTR + SDMA0_REGISTER_OFFSET,
+                            2, 0xfffffffc, SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0));
+       if (r)
+               return r;
+
+       ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX];
+       r = radeon_ring_init(rdev, ring, ring->ring_size, CAYMAN_WB_DMA1_RPTR_OFFSET,
+                            SDMA0_GFX_RB_RPTR + SDMA1_REGISTER_OFFSET,
+                            SDMA0_GFX_RB_WPTR + SDMA1_REGISTER_OFFSET,
+                            2, 0xfffffffc, SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0));
+       if (r)
+               return r;
+
+       r = cik_cp_resume(rdev);
+       if (r)
+               return r;
+
+       r = cik_sdma_resume(rdev);
+       if (r)
+               return r;
+
+       ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX];
+       if (ring->ring_size) {
+               r = radeon_ring_init(rdev, ring, ring->ring_size,
+                                    R600_WB_UVD_RPTR_OFFSET,
+                                    UVD_RBC_RB_RPTR, UVD_RBC_RB_WPTR,
+                                    0, 0xfffff, RADEON_CP_PACKET2);
+               if (!r)
+                       r = r600_uvd_init(rdev);
+               if (r)
+                       DRM_ERROR("radeon: failed initializing UVD (%d).\n", r);
+       }
+
+       r = radeon_ib_pool_init(rdev);
+       if (r) {
+               dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
+               return r;
+       }
+
+       r = radeon_vm_manager_init(rdev);
+       if (r) {
+               dev_err(rdev->dev, "vm manager initialization failed (%d).\n", r);
+               return r;
+       }
+
+       return 0;
+}
+
+/**
+ * cik_resume - resume the asic to a functional state
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Programs the asic to a functional state (CIK).
+ * Called at resume.
+ * Returns 0 for success, error for failure.
+ */
+int cik_resume(struct radeon_device *rdev)
+{
+       int r;
+
+       /* post card */
+       atom_asic_init(rdev->mode_info.atom_context);
+
+       /* init golden registers */
+       cik_init_golden_registers(rdev);
+
+       rdev->accel_working = true;
+       r = cik_startup(rdev);
+       if (r) {
+               DRM_ERROR("cik startup failed on resume\n");
+               rdev->accel_working = false;
+               return r;
+       }
+
+       return r;
+
+}
+
+/**
+ * cik_suspend - suspend the asic
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Bring the chip into a state suitable for suspend (CIK).
+ * Called at suspend.
+ * Returns 0 for success.
+ */
+int cik_suspend(struct radeon_device *rdev)
+{
+       radeon_vm_manager_fini(rdev);
+       cik_cp_enable(rdev, false);
+       cik_sdma_enable(rdev, false);
+       r600_uvd_rbc_stop(rdev);
+       radeon_uvd_suspend(rdev);
+       cik_irq_suspend(rdev);
+       radeon_wb_disable(rdev);
+       cik_pcie_gart_disable(rdev);
+       return 0;
+}
+
+/* Plan is to move initialization in that function and use
+ * helper function so that radeon_device_init pretty much
+ * do nothing more than calling asic specific function. This
+ * should also allow to remove a bunch of callback function
+ * like vram_info.
+ */
+/**
+ * cik_init - asic specific driver and hw init
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Setup asic specific driver variables and program the hw
+ * to a functional state (CIK).
+ * Called at driver startup.
+ * Returns 0 for success, errors for failure.
+ */
+int cik_init(struct radeon_device *rdev)
+{
+       struct radeon_ring *ring;
+       int r;
+
+       /* Read BIOS */
+       if (!radeon_get_bios(rdev)) {
+               if (ASIC_IS_AVIVO(rdev))
+                       return -EINVAL;
+       }
+       /* Must be an ATOMBIOS */
+       if (!rdev->is_atom_bios) {
+               dev_err(rdev->dev, "Expecting atombios for cayman GPU\n");
+               return -EINVAL;
+       }
+       r = radeon_atombios_init(rdev);
+       if (r)
+               return r;
+
+       /* Post card if necessary */
+       if (!radeon_card_posted(rdev)) {
+               if (!rdev->bios) {
+                       dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
+                       return -EINVAL;
+               }
+               DRM_INFO("GPU not posted. posting now...\n");
+               atom_asic_init(rdev->mode_info.atom_context);
+       }
+       /* init golden registers */
+       cik_init_golden_registers(rdev);
+       /* Initialize scratch registers */
+       cik_scratch_init(rdev);
+       /* Initialize surface registers */
+       radeon_surface_init(rdev);
+       /* Initialize clocks */
+       radeon_get_clock_info(rdev->ddev);
+
+       /* Fence driver */
+       r = radeon_fence_driver_init(rdev);
+       if (r)
+               return r;
+
+       /* initialize memory controller */
+       r = cik_mc_init(rdev);
+       if (r)
+               return r;
+       /* Memory manager */
+       r = radeon_bo_init(rdev);
+       if (r)
+               return r;
+
+       ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
+       ring->ring_obj = NULL;
+       r600_ring_init(rdev, ring, 1024 * 1024);
+
+       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);
+       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);
+       if (r)
+               return r;
+
+       ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX];
+       ring->ring_obj = NULL;
+       r600_ring_init(rdev, ring, 256 * 1024);
+
+       ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX];
+       ring->ring_obj = NULL;
+       r600_ring_init(rdev, ring, 256 * 1024);
+
+       r = radeon_uvd_init(rdev);
+       if (!r) {
+               ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX];
+               ring->ring_obj = NULL;
+               r600_ring_init(rdev, ring, 4096);
+       }
+
+       rdev->ih.ring_obj = NULL;
+       r600_ih_ring_init(rdev, 64 * 1024);
+
+       r = r600_pcie_gart_init(rdev);
+       if (r)
+               return r;
+
+       rdev->accel_working = true;
+       r = cik_startup(rdev);
+       if (r) {
+               dev_err(rdev->dev, "disabling GPU acceleration\n");
+               cik_cp_fini(rdev);
+               cik_sdma_fini(rdev);
+               cik_irq_fini(rdev);
+               si_rlc_fini(rdev);
+               cik_mec_fini(rdev);
+               radeon_wb_fini(rdev);
+               radeon_ib_pool_fini(rdev);
+               radeon_vm_manager_fini(rdev);
+               radeon_irq_kms_fini(rdev);
+               cik_pcie_gart_fini(rdev);
+               rdev->accel_working = false;
+       }
+
+       /* Don't start up if the MC ucode is missing.
+        * The default clocks and voltages before the MC ucode
+        * is loaded are not suffient for advanced operations.
+        */
+       if (!rdev->mc_fw && !(rdev->flags & RADEON_IS_IGP)) {
+               DRM_ERROR("radeon: MC ucode required for NI+.\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * cik_fini - asic specific driver and hw fini
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Tear down the asic specific driver variables and program the hw
+ * to an idle state (CIK).
+ * Called at driver unload.
+ */
+void cik_fini(struct radeon_device *rdev)
+{
+       cik_cp_fini(rdev);
+       cik_sdma_fini(rdev);
+       cik_irq_fini(rdev);
+       si_rlc_fini(rdev);
+       cik_mec_fini(rdev);
+       radeon_wb_fini(rdev);
+       radeon_vm_manager_fini(rdev);
+       radeon_ib_pool_fini(rdev);
+       radeon_irq_kms_fini(rdev);
+       radeon_uvd_fini(rdev);
+       cik_pcie_gart_fini(rdev);
+       r600_vram_scratch_fini(rdev);
+       radeon_gem_fini(rdev);
+       radeon_fence_driver_fini(rdev);
+       radeon_bo_fini(rdev);
+       radeon_atombios_fini(rdev);
+       kfree(rdev->bios);
+       rdev->bios = NULL;
+}
+
+/* display watermark setup */
+/**
+ * dce8_line_buffer_adjust - Set up the line buffer
+ *
+ * @rdev: radeon_device pointer
+ * @radeon_crtc: the selected display controller
+ * @mode: the current display mode on the selected display
+ * controller
+ *
+ * Setup up the line buffer allocation for
+ * the selected display controller (CIK).
+ * Returns the line buffer size in pixels.
+ */
+static u32 dce8_line_buffer_adjust(struct radeon_device *rdev,
+                                  struct radeon_crtc *radeon_crtc,
+                                  struct drm_display_mode *mode)
+{
+       u32 tmp;
+
+       /*
+        * Line Buffer Setup
+        * There are 6 line buffers, one for each display controllers.
+        * There are 3 partitions per LB. Select the number of partitions
+        * to enable based on the display width.  For display widths larger
+        * than 4096, you need use to use 2 display controllers and combine
+        * them using the stereo blender.
+        */
+       if (radeon_crtc->base.enabled && mode) {
+               if (mode->crtc_hdisplay < 1920)
+                       tmp = 1;
+               else if (mode->crtc_hdisplay < 2560)
+                       tmp = 2;
+               else if (mode->crtc_hdisplay < 4096)
+                       tmp = 0;
+               else {
+                       DRM_DEBUG_KMS("Mode too big for LB!\n");
+                       tmp = 0;
+               }
+       } else
+               tmp = 1;
+
+       WREG32(LB_MEMORY_CTRL + radeon_crtc->crtc_offset,
+              LB_MEMORY_CONFIG(tmp) | LB_MEMORY_SIZE(0x6B0));
+
+       if (radeon_crtc->base.enabled && mode) {
+               switch (tmp) {
+               case 0:
+               default:
+                       return 4096 * 2;
+               case 1:
+                       return 1920 * 2;
+               case 2:
+                       return 2560 * 2;
+               }
+       }
+
+       /* controller not enabled, so no lb used */
+       return 0;
+}
+
+/**
+ * cik_get_number_of_dram_channels - get the number of dram channels
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Look up the number of video ram channels (CIK).
+ * Used for display watermark bandwidth calculations
+ * Returns the number of dram channels
+ */
+static u32 cik_get_number_of_dram_channels(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32(MC_SHARED_CHMAP);
+
+       switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) {
+       case 0:
+       default:
+               return 1;
+       case 1:
+               return 2;
+       case 2:
+               return 4;
+       case 3:
+               return 8;
+       case 4:
+               return 3;
+       case 5:
+               return 6;
+       case 6:
+               return 10;
+       case 7:
+               return 12;
+       case 8:
+               return 16;
+       }
+}
+
+struct dce8_wm_params {
+       u32 dram_channels; /* number of dram channels */
+       u32 yclk;          /* bandwidth per dram data pin in kHz */
+       u32 sclk;          /* engine clock in kHz */
+       u32 disp_clk;      /* display clock in kHz */
+       u32 src_width;     /* viewport width */
+       u32 active_time;   /* active display time in ns */
+       u32 blank_time;    /* blank time in ns */
+       bool interlaced;    /* mode is interlaced */
+       fixed20_12 vsc;    /* vertical scale ratio */
+       u32 num_heads;     /* number of active crtcs */
+       u32 bytes_per_pixel; /* bytes per pixel display + overlay */
+       u32 lb_size;       /* line buffer allocated to pipe */
+       u32 vtaps;         /* vertical scaler taps */
+};
+
+/**
+ * dce8_dram_bandwidth - get the dram bandwidth
+ *
+ * @wm: watermark calculation data
+ *
+ * Calculate the raw dram bandwidth (CIK).
+ * Used for display watermark bandwidth calculations
+ * Returns the dram bandwidth in MBytes/s
+ */
+static u32 dce8_dram_bandwidth(struct dce8_wm_params *wm)
+{
+       /* Calculate raw DRAM Bandwidth */
+       fixed20_12 dram_efficiency; /* 0.7 */
+       fixed20_12 yclk, dram_channels, bandwidth;
+       fixed20_12 a;
+
+       a.full = dfixed_const(1000);
+       yclk.full = dfixed_const(wm->yclk);
+       yclk.full = dfixed_div(yclk, a);
+       dram_channels.full = dfixed_const(wm->dram_channels * 4);
+       a.full = dfixed_const(10);
+       dram_efficiency.full = dfixed_const(7);
+       dram_efficiency.full = dfixed_div(dram_efficiency, a);
+       bandwidth.full = dfixed_mul(dram_channels, yclk);
+       bandwidth.full = dfixed_mul(bandwidth, dram_efficiency);
+
+       return dfixed_trunc(bandwidth);
+}
+
+/**
+ * dce8_dram_bandwidth_for_display - get the dram bandwidth for display
+ *
+ * @wm: watermark calculation data
+ *
+ * Calculate the dram bandwidth used for display (CIK).
+ * Used for display watermark bandwidth calculations
+ * Returns the dram bandwidth for display in MBytes/s
+ */
+static u32 dce8_dram_bandwidth_for_display(struct dce8_wm_params *wm)
+{
+       /* Calculate DRAM Bandwidth and the part allocated to display. */
+       fixed20_12 disp_dram_allocation; /* 0.3 to 0.7 */
+       fixed20_12 yclk, dram_channels, bandwidth;
+       fixed20_12 a;
+
+       a.full = dfixed_const(1000);
+       yclk.full = dfixed_const(wm->yclk);
+       yclk.full = dfixed_div(yclk, a);
+       dram_channels.full = dfixed_const(wm->dram_channels * 4);
+       a.full = dfixed_const(10);
+       disp_dram_allocation.full = dfixed_const(3); /* XXX worse case value 0.3 */
+       disp_dram_allocation.full = dfixed_div(disp_dram_allocation, a);
+       bandwidth.full = dfixed_mul(dram_channels, yclk);
+       bandwidth.full = dfixed_mul(bandwidth, disp_dram_allocation);
+
+       return dfixed_trunc(bandwidth);
+}
+
+/**
+ * dce8_data_return_bandwidth - get the data return bandwidth
+ *
+ * @wm: watermark calculation data
+ *
+ * Calculate the data return bandwidth used for display (CIK).
+ * Used for display watermark bandwidth calculations
+ * Returns the data return bandwidth in MBytes/s
+ */
+static u32 dce8_data_return_bandwidth(struct dce8_wm_params *wm)
+{
+       /* Calculate the display Data return Bandwidth */
+       fixed20_12 return_efficiency; /* 0.8 */
+       fixed20_12 sclk, bandwidth;
+       fixed20_12 a;
+
+       a.full = dfixed_const(1000);
+       sclk.full = dfixed_const(wm->sclk);
+       sclk.full = dfixed_div(sclk, a);
+       a.full = dfixed_const(10);
+       return_efficiency.full = dfixed_const(8);
+       return_efficiency.full = dfixed_div(return_efficiency, a);
+       a.full = dfixed_const(32);
+       bandwidth.full = dfixed_mul(a, sclk);
+       bandwidth.full = dfixed_mul(bandwidth, return_efficiency);
+
+       return dfixed_trunc(bandwidth);
+}
+
+/**
+ * dce8_dmif_request_bandwidth - get the dmif bandwidth
+ *
+ * @wm: watermark calculation data
+ *
+ * Calculate the dmif bandwidth used for display (CIK).
+ * Used for display watermark bandwidth calculations
+ * Returns the dmif bandwidth in MBytes/s
+ */
+static u32 dce8_dmif_request_bandwidth(struct dce8_wm_params *wm)
+{
+       /* Calculate the DMIF Request Bandwidth */
+       fixed20_12 disp_clk_request_efficiency; /* 0.8 */
+       fixed20_12 disp_clk, bandwidth;
+       fixed20_12 a, b;
+
+       a.full = dfixed_const(1000);
+       disp_clk.full = dfixed_const(wm->disp_clk);
+       disp_clk.full = dfixed_div(disp_clk, a);
+       a.full = dfixed_const(32);
+       b.full = dfixed_mul(a, disp_clk);
+
+       a.full = dfixed_const(10);
+       disp_clk_request_efficiency.full = dfixed_const(8);
+       disp_clk_request_efficiency.full = dfixed_div(disp_clk_request_efficiency, a);
+
+       bandwidth.full = dfixed_mul(b, disp_clk_request_efficiency);
+
+       return dfixed_trunc(bandwidth);
+}
+
+/**
+ * dce8_available_bandwidth - get the min available bandwidth
+ *
+ * @wm: watermark calculation data
+ *
+ * Calculate the min available bandwidth used for display (CIK).
+ * Used for display watermark bandwidth calculations
+ * Returns the min available bandwidth in MBytes/s
+ */
+static u32 dce8_available_bandwidth(struct dce8_wm_params *wm)
+{
+       /* Calculate the Available bandwidth. Display can use this temporarily but not in average. */
+       u32 dram_bandwidth = dce8_dram_bandwidth(wm);
+       u32 data_return_bandwidth = dce8_data_return_bandwidth(wm);
+       u32 dmif_req_bandwidth = dce8_dmif_request_bandwidth(wm);
+
+       return min(dram_bandwidth, min(data_return_bandwidth, dmif_req_bandwidth));
+}
+
+/**
+ * dce8_average_bandwidth - get the average available bandwidth
+ *
+ * @wm: watermark calculation data
+ *
+ * Calculate the average available bandwidth used for display (CIK).
+ * Used for display watermark bandwidth calculations
+ * Returns the average available bandwidth in MBytes/s
+ */
+static u32 dce8_average_bandwidth(struct dce8_wm_params *wm)
+{
+       /* Calculate the display mode Average Bandwidth
+        * DisplayMode should contain the source and destination dimensions,
+        * timing, etc.
+        */
+       fixed20_12 bpp;
+       fixed20_12 line_time;
+       fixed20_12 src_width;
+       fixed20_12 bandwidth;
+       fixed20_12 a;
+
+       a.full = dfixed_const(1000);
+       line_time.full = dfixed_const(wm->active_time + wm->blank_time);
+       line_time.full = dfixed_div(line_time, a);
+       bpp.full = dfixed_const(wm->bytes_per_pixel);
+       src_width.full = dfixed_const(wm->src_width);
+       bandwidth.full = dfixed_mul(src_width, bpp);
+       bandwidth.full = dfixed_mul(bandwidth, wm->vsc);
+       bandwidth.full = dfixed_div(bandwidth, line_time);
+
+       return dfixed_trunc(bandwidth);
+}
+
+/**
+ * dce8_latency_watermark - get the latency watermark
+ *
+ * @wm: watermark calculation data
+ *
+ * Calculate the latency watermark (CIK).
+ * Used for display watermark bandwidth calculations
+ * Returns the latency watermark in ns
+ */
+static u32 dce8_latency_watermark(struct dce8_wm_params *wm)
+{
+       /* First calculate the latency in ns */
+       u32 mc_latency = 2000; /* 2000 ns. */
+       u32 available_bandwidth = dce8_available_bandwidth(wm);
+       u32 worst_chunk_return_time = (512 * 8 * 1000) / available_bandwidth;
+       u32 cursor_line_pair_return_time = (128 * 4 * 1000) / available_bandwidth;
+       u32 dc_latency = 40000000 / wm->disp_clk; /* dc pipe latency */
+       u32 other_heads_data_return_time = ((wm->num_heads + 1) * worst_chunk_return_time) +
+               (wm->num_heads * cursor_line_pair_return_time);
+       u32 latency = mc_latency + other_heads_data_return_time + dc_latency;
+       u32 max_src_lines_per_dst_line, lb_fill_bw, line_fill_time;
+       u32 tmp, dmif_size = 12288;
+       fixed20_12 a, b, c;
+
+       if (wm->num_heads == 0)
+               return 0;
+
+       a.full = dfixed_const(2);
+       b.full = dfixed_const(1);
+       if ((wm->vsc.full > a.full) ||
+           ((wm->vsc.full > b.full) && (wm->vtaps >= 3)) ||
+           (wm->vtaps >= 5) ||
+           ((wm->vsc.full >= a.full) && wm->interlaced))
+               max_src_lines_per_dst_line = 4;
+       else
+               max_src_lines_per_dst_line = 2;
+
+       a.full = dfixed_const(available_bandwidth);
+       b.full = dfixed_const(wm->num_heads);
+       a.full = dfixed_div(a, b);
+
+       b.full = dfixed_const(mc_latency + 512);
+       c.full = dfixed_const(wm->disp_clk);
+       b.full = dfixed_div(b, c);
+
+       c.full = dfixed_const(dmif_size);
+       b.full = dfixed_div(c, b);
+
+       tmp = min(dfixed_trunc(a), dfixed_trunc(b));
+
+       b.full = dfixed_const(1000);
+       c.full = dfixed_const(wm->disp_clk);
+       b.full = dfixed_div(c, b);
+       c.full = dfixed_const(wm->bytes_per_pixel);
+       b.full = dfixed_mul(b, c);
+
+       lb_fill_bw = min(tmp, dfixed_trunc(b));
+
+       a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel);
+       b.full = dfixed_const(1000);
+       c.full = dfixed_const(lb_fill_bw);
+       b.full = dfixed_div(c, b);
+       a.full = dfixed_div(a, b);
+       line_fill_time = dfixed_trunc(a);
+
+       if (line_fill_time < wm->active_time)
+               return latency;
+       else
+               return latency + (line_fill_time - wm->active_time);
+
+}
+
+/**
+ * dce8_average_bandwidth_vs_dram_bandwidth_for_display - check
+ * average and available dram bandwidth
+ *
+ * @wm: watermark calculation data
+ *
+ * Check if the display average bandwidth fits in the display
+ * dram bandwidth (CIK).
+ * Used for display watermark bandwidth calculations
+ * Returns true if the display fits, false if not.
+ */
+static bool dce8_average_bandwidth_vs_dram_bandwidth_for_display(struct dce8_wm_params *wm)
+{
+       if (dce8_average_bandwidth(wm) <=
+           (dce8_dram_bandwidth_for_display(wm) / wm->num_heads))
+               return true;
+       else
+               return false;
+}
+
+/**
+ * dce8_average_bandwidth_vs_available_bandwidth - check
+ * average and available bandwidth
+ *
+ * @wm: watermark calculation data
+ *
+ * Check if the display average bandwidth fits in the display
+ * available bandwidth (CIK).
+ * Used for display watermark bandwidth calculations
+ * Returns true if the display fits, false if not.
+ */
+static bool dce8_average_bandwidth_vs_available_bandwidth(struct dce8_wm_params *wm)
+{
+       if (dce8_average_bandwidth(wm) <=
+           (dce8_available_bandwidth(wm) / wm->num_heads))
+               return true;
+       else
+               return false;
+}
+
+/**
+ * dce8_check_latency_hiding - check latency hiding
+ *
+ * @wm: watermark calculation data
+ *
+ * Check latency hiding (CIK).
+ * Used for display watermark bandwidth calculations
+ * Returns true if the display fits, false if not.
+ */
+static bool dce8_check_latency_hiding(struct dce8_wm_params *wm)
+{
+       u32 lb_partitions = wm->lb_size / wm->src_width;
+       u32 line_time = wm->active_time + wm->blank_time;
+       u32 latency_tolerant_lines;
+       u32 latency_hiding;
+       fixed20_12 a;
+
+       a.full = dfixed_const(1);
+       if (wm->vsc.full > a.full)
+               latency_tolerant_lines = 1;
+       else {
+               if (lb_partitions <= (wm->vtaps + 1))
+                       latency_tolerant_lines = 1;
+               else
+                       latency_tolerant_lines = 2;
+       }
+
+       latency_hiding = (latency_tolerant_lines * line_time + wm->blank_time);
+
+       if (dce8_latency_watermark(wm) <= latency_hiding)
+               return true;
+       else
+               return false;
+}
+
+/**
+ * dce8_program_watermarks - program display watermarks
+ *
+ * @rdev: radeon_device pointer
+ * @radeon_crtc: the selected display controller
+ * @lb_size: line buffer size
+ * @num_heads: number of display controllers in use
+ *
+ * Calculate and program the display watermarks for the
+ * selected display controller (CIK).
+ */
+static void dce8_program_watermarks(struct radeon_device *rdev,
+                                   struct radeon_crtc *radeon_crtc,
+                                   u32 lb_size, u32 num_heads)
+{
+       struct drm_display_mode *mode = &radeon_crtc->base.mode;
+       struct dce8_wm_params wm;
+       u32 pixel_period;
+       u32 line_time = 0;
+       u32 latency_watermark_a = 0, latency_watermark_b = 0;
+       u32 tmp, wm_mask;
+
+       if (radeon_crtc->base.enabled && num_heads && mode) {
+               pixel_period = 1000000 / (u32)mode->clock;
+               line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535);
+
+               wm.yclk = rdev->pm.current_mclk * 10;
+               wm.sclk = rdev->pm.current_sclk * 10;
+               wm.disp_clk = mode->clock;
+               wm.src_width = mode->crtc_hdisplay;
+               wm.active_time = mode->crtc_hdisplay * pixel_period;
+               wm.blank_time = line_time - wm.active_time;
+               wm.interlaced = false;
+               if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+                       wm.interlaced = true;
+               wm.vsc = radeon_crtc->vsc;
+               wm.vtaps = 1;
+               if (radeon_crtc->rmx_type != RMX_OFF)
+                       wm.vtaps = 2;
+               wm.bytes_per_pixel = 4; /* XXX: get this from fb config */
+               wm.lb_size = lb_size;
+               wm.dram_channels = cik_get_number_of_dram_channels(rdev);
+               wm.num_heads = num_heads;
+
+               /* set for high clocks */
+               latency_watermark_a = min(dce8_latency_watermark(&wm), (u32)65535);
+               /* set for low clocks */
+               /* wm.yclk = low clk; wm.sclk = low clk */
+               latency_watermark_b = min(dce8_latency_watermark(&wm), (u32)65535);
+
+               /* possibly force display priority to high */
+               /* should really do this at mode validation time... */
+               if (!dce8_average_bandwidth_vs_dram_bandwidth_for_display(&wm) ||
+                   !dce8_average_bandwidth_vs_available_bandwidth(&wm) ||
+                   !dce8_check_latency_hiding(&wm) ||
+                   (rdev->disp_priority == 2)) {
+                       DRM_DEBUG_KMS("force priority to high\n");
+               }
+       }
+
+       /* select wm A */
+       wm_mask = RREG32(DPG_WATERMARK_MASK_CONTROL + radeon_crtc->crtc_offset);
+       tmp = wm_mask;
+       tmp &= ~LATENCY_WATERMARK_MASK(3);
+       tmp |= LATENCY_WATERMARK_MASK(1);
+       WREG32(DPG_WATERMARK_MASK_CONTROL + radeon_crtc->crtc_offset, tmp);
+       WREG32(DPG_PIPE_LATENCY_CONTROL + radeon_crtc->crtc_offset,
+              (LATENCY_LOW_WATERMARK(latency_watermark_a) |
+               LATENCY_HIGH_WATERMARK(line_time)));
+       /* select wm B */
+       tmp = RREG32(DPG_WATERMARK_MASK_CONTROL + radeon_crtc->crtc_offset);
+       tmp &= ~LATENCY_WATERMARK_MASK(3);
+       tmp |= LATENCY_WATERMARK_MASK(2);
+       WREG32(DPG_WATERMARK_MASK_CONTROL + radeon_crtc->crtc_offset, tmp);
+       WREG32(DPG_PIPE_LATENCY_CONTROL + radeon_crtc->crtc_offset,
+              (LATENCY_LOW_WATERMARK(latency_watermark_b) |
+               LATENCY_HIGH_WATERMARK(line_time)));
+       /* restore original selection */
+       WREG32(DPG_WATERMARK_MASK_CONTROL + radeon_crtc->crtc_offset, wm_mask);
+}
+
+/**
+ * dce8_bandwidth_update - program display watermarks
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Calculate and program the display watermarks and line
+ * buffer allocation (CIK).
+ */
+void dce8_bandwidth_update(struct radeon_device *rdev)
+{
+       struct drm_display_mode *mode = NULL;
+       u32 num_heads = 0, lb_size;
+       int i;
+
+       radeon_update_display_priority(rdev);
+
+       for (i = 0; i < rdev->num_crtc; i++) {
+               if (rdev->mode_info.crtcs[i]->base.enabled)
+                       num_heads++;
+       }
+       for (i = 0; i < rdev->num_crtc; i++) {
+               mode = &rdev->mode_info.crtcs[i]->base.mode;
+               lb_size = dce8_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i], mode);
+               dce8_program_watermarks(rdev, rdev->mode_info.crtcs[i], lb_size, num_heads);
+       }
+}
+
+/**
+ * cik_get_gpu_clock_counter - return GPU clock counter snapshot
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Fetches a GPU clock counter snapshot (SI).
+ * Returns the 64 bit clock counter snapshot.
+ */
+uint64_t cik_get_gpu_clock_counter(struct radeon_device *rdev)
+{
+       uint64_t clock;
+
+       mutex_lock(&rdev->gpu_clock_mutex);
+       WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1);
+       clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) |
+               ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL);
+       mutex_unlock(&rdev->gpu_clock_mutex);
+       return clock;
+}
+
+static int cik_set_uvd_clock(struct radeon_device *rdev, u32 clock,
+                              u32 cntl_reg, u32 status_reg)
+{
+       int r, i;
+       struct atom_clock_dividers dividers;
+       uint32_t tmp;
+
+       r = radeon_atom_get_clock_dividers(rdev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+                                          clock, false, &dividers);
+       if (r)
+               return r;
+
+       tmp = RREG32_SMC(cntl_reg);
+       tmp &= ~(DCLK_DIR_CNTL_EN|DCLK_DIVIDER_MASK);
+       tmp |= dividers.post_divider;
+       WREG32_SMC(cntl_reg, tmp);
+
+       for (i = 0; i < 100; i++) {
+               if (RREG32_SMC(status_reg) & DCLK_STATUS)
+                       break;
+               mdelay(10);
+       }
+       if (i == 100)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+int cik_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
+{
+       int r = 0;
+
+       r = cik_set_uvd_clock(rdev, vclk, CG_VCLK_CNTL, CG_VCLK_STATUS);
+       if (r)
+               return r;
+
+       r = cik_set_uvd_clock(rdev, dclk, CG_DCLK_CNTL, CG_DCLK_STATUS);
+       return r;
+}
+
+int cik_uvd_resume(struct radeon_device *rdev)
+{
+       uint64_t addr;
+       uint32_t size;
+       int r;
+
+       r = radeon_uvd_resume(rdev);
+       if (r)
+               return r;
+
+       /* programm the VCPU memory controller bits 0-27 */
+       addr = rdev->uvd.gpu_addr >> 3;
+       size = RADEON_GPU_PAGE_ALIGN(rdev->uvd_fw->size + 4) >> 3;
+       WREG32(UVD_VCPU_CACHE_OFFSET0, addr);
+       WREG32(UVD_VCPU_CACHE_SIZE0, size);
+
+       addr += size;
+       size = RADEON_UVD_STACK_SIZE >> 3;
+       WREG32(UVD_VCPU_CACHE_OFFSET1, addr);
+       WREG32(UVD_VCPU_CACHE_SIZE1, size);
+
+       addr += size;
+       size = RADEON_UVD_HEAP_SIZE >> 3;
+       WREG32(UVD_VCPU_CACHE_OFFSET2, addr);
+       WREG32(UVD_VCPU_CACHE_SIZE2, size);
+
+       /* bits 28-31 */
+       addr = (rdev->uvd.gpu_addr >> 28) & 0xF;
+       WREG32(UVD_LMI_ADDR_EXT, (addr << 12) | (addr << 0));
+
+       /* bits 32-39 */
+       addr = (rdev->uvd.gpu_addr >> 32) & 0xFF;
+       WREG32(UVD_LMI_EXT40_ADDR, addr | (0x9 << 16) | (0x1 << 31));
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/radeon/cik_blit_shaders.c b/drivers/gpu/drm/radeon/cik_blit_shaders.c
new file mode 100644 (file)
index 0000000..ff13118
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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 (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 COPYRIGHT HOLDER(S) 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:
+ *     Alex Deucher <alexander.deucher@amd.com>
+ */
+
+#include <linux/types.h>
+#include <linux/bug.h>
+#include <linux/kernel.h>
+
+const u32 cik_default_state[] =
+{
+       0xc0066900,
+       0x00000000,
+       0x00000060, /* DB_RENDER_CONTROL */
+       0x00000000, /* DB_COUNT_CONTROL */
+       0x00000000, /* DB_DEPTH_VIEW */
+       0x0000002a, /* DB_RENDER_OVERRIDE */
+       0x00000000, /* DB_RENDER_OVERRIDE2 */
+       0x00000000, /* DB_HTILE_DATA_BASE */
+
+       0xc0046900,
+       0x00000008,
+       0x00000000, /* DB_DEPTH_BOUNDS_MIN */
+       0x00000000, /* DB_DEPTH_BOUNDS_MAX */
+       0x00000000, /* DB_STENCIL_CLEAR */
+       0x00000000, /* DB_DEPTH_CLEAR */
+
+       0xc0036900,
+       0x0000000f,
+       0x00000000, /* DB_DEPTH_INFO */
+       0x00000000, /* DB_Z_INFO */
+       0x00000000, /* DB_STENCIL_INFO */
+
+       0xc0016900,
+       0x00000080,
+       0x00000000, /* PA_SC_WINDOW_OFFSET */
+
+       0xc00d6900,
+       0x00000083,
+       0x0000ffff, /* PA_SC_CLIPRECT_RULE */
+       0x00000000, /* PA_SC_CLIPRECT_0_TL */
+       0x20002000, /* PA_SC_CLIPRECT_0_BR */
+       0x00000000,
+       0x20002000,
+       0x00000000,
+       0x20002000,
+       0x00000000,
+       0x20002000,
+       0xaaaaaaaa, /* PA_SC_EDGERULE */
+       0x00000000, /* PA_SU_HARDWARE_SCREEN_OFFSET */
+       0x0000000f, /* CB_TARGET_MASK */
+       0x0000000f, /* CB_SHADER_MASK */
+
+       0xc0226900,
+       0x00000094,
+       0x80000000, /* PA_SC_VPORT_SCISSOR_0_TL */
+       0x20002000, /* PA_SC_VPORT_SCISSOR_0_BR */
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x80000000,
+       0x20002000,
+       0x00000000, /* PA_SC_VPORT_ZMIN_0 */
+       0x3f800000, /* PA_SC_VPORT_ZMAX_0 */
+
+       0xc0046900,
+       0x00000100,
+       0xffffffff, /* VGT_MAX_VTX_INDX */
+       0x00000000, /* VGT_MIN_VTX_INDX */
+       0x00000000, /* VGT_INDX_OFFSET */
+       0x00000000, /* VGT_MULTI_PRIM_IB_RESET_INDX */
+
+       0xc0046900,
+       0x00000105,
+       0x00000000, /* CB_BLEND_RED */
+       0x00000000, /* CB_BLEND_GREEN */
+       0x00000000, /* CB_BLEND_BLUE */
+       0x00000000, /* CB_BLEND_ALPHA */
+
+       0xc0016900,
+       0x000001e0,
+       0x00000000, /* CB_BLEND0_CONTROL */
+
+       0xc00c6900,
+       0x00000200,
+       0x00000000, /* DB_DEPTH_CONTROL */
+       0x00000000, /* DB_EQAA */
+       0x00cc0010, /* CB_COLOR_CONTROL */
+       0x00000210, /* DB_SHADER_CONTROL */
+       0x00010000, /* PA_CL_CLIP_CNTL */
+       0x00000004, /* PA_SU_SC_MODE_CNTL */
+       0x00000100, /* PA_CL_VTE_CNTL */
+       0x00000000, /* PA_CL_VS_OUT_CNTL */
+       0x00000000, /* PA_CL_NANINF_CNTL */
+       0x00000000, /* PA_SU_LINE_STIPPLE_CNTL */
+       0x00000000, /* PA_SU_LINE_STIPPLE_SCALE */
+       0x00000000, /* PA_SU_PRIM_FILTER_CNTL */
+
+       0xc0116900,
+       0x00000280,
+       0x00000000, /* PA_SU_POINT_SIZE */
+       0x00000000, /* PA_SU_POINT_MINMAX */
+       0x00000008, /* PA_SU_LINE_CNTL */
+       0x00000000, /* PA_SC_LINE_STIPPLE */
+       0x00000000, /* VGT_OUTPUT_PATH_CNTL */
+       0x00000000, /* VGT_HOS_CNTL */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000, /* VGT_GS_MODE */
+
+       0xc0026900,
+       0x00000292,
+       0x00000000, /* PA_SC_MODE_CNTL_0 */
+       0x00000000, /* PA_SC_MODE_CNTL_1 */
+
+       0xc0016900,
+       0x000002a1,
+       0x00000000, /* VGT_PRIMITIVEID_EN */
+
+       0xc0016900,
+       0x000002a5,
+       0x00000000, /* VGT_MULTI_PRIM_IB_RESET_EN */
+
+       0xc0026900,
+       0x000002a8,
+       0x00000000, /* VGT_INSTANCE_STEP_RATE_0 */
+       0x00000000,
+
+       0xc0026900,
+       0x000002ad,
+       0x00000000, /* VGT_REUSE_OFF */
+       0x00000000,
+
+       0xc0016900,
+       0x000002d5,
+       0x00000000, /* VGT_SHADER_STAGES_EN */
+
+       0xc0016900,
+       0x000002dc,
+       0x0000aa00, /* DB_ALPHA_TO_MASK */
+
+       0xc0066900,
+       0x000002de,
+       0x00000000, /* PA_SU_POLY_OFFSET_DB_FMT_CNTL */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+
+       0xc0026900,
+       0x000002e5,
+       0x00000000, /* VGT_STRMOUT_CONFIG */
+       0x00000000,
+
+       0xc01b6900,
+       0x000002f5,
+       0x76543210, /* PA_SC_CENTROID_PRIORITY_0 */
+       0xfedcba98, /* PA_SC_CENTROID_PRIORITY_1 */
+       0x00000000, /* PA_SC_LINE_CNTL */
+       0x00000000, /* PA_SC_AA_CONFIG */
+       0x00000005, /* PA_SU_VTX_CNTL */
+       0x3f800000, /* PA_CL_GB_VERT_CLIP_ADJ */
+       0x3f800000, /* PA_CL_GB_VERT_DISC_ADJ */
+       0x3f800000, /* PA_CL_GB_HORZ_CLIP_ADJ */
+       0x3f800000, /* PA_CL_GB_HORZ_DISC_ADJ */
+       0x00000000, /* PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0 */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0xffffffff, /* PA_SC_AA_MASK_X0Y0_X1Y0 */
+       0xffffffff,
+
+       0xc0026900,
+       0x00000316,
+       0x0000000e, /* VGT_VERTEX_REUSE_BLOCK_CNTL */
+       0x00000010, /*  */
+};
+
+const u32 cik_default_size = ARRAY_SIZE(cik_default_state);
diff --git a/drivers/gpu/drm/radeon/cik_blit_shaders.h b/drivers/gpu/drm/radeon/cik_blit_shaders.h
new file mode 100644 (file)
index 0000000..dfe7314
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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 (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 COPYRIGHT HOLDER(S) 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.
+ *
+ */
+
+#ifndef CIK_BLIT_SHADERS_H
+#define CIK_BLIT_SHADERS_H
+
+extern const u32 cik_default_state[];
+
+extern const u32 cik_default_size;
+
+#endif
diff --git a/drivers/gpu/drm/radeon/cik_reg.h b/drivers/gpu/drm/radeon/cik_reg.h
new file mode 100644 (file)
index 0000000..d71e46d
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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: Alex Deucher
+ */
+#ifndef __CIK_REG_H__
+#define __CIK_REG_H__
+
+#define CIK_DC_GPIO_HPD_MASK                      0x65b0
+#define CIK_DC_GPIO_HPD_A                         0x65b4
+#define CIK_DC_GPIO_HPD_EN                        0x65b8
+#define CIK_DC_GPIO_HPD_Y                         0x65bc
+
+#define CIK_GRPH_CONTROL                          0x6804
+#       define CIK_GRPH_DEPTH(x)                  (((x) & 0x3) << 0)
+#       define CIK_GRPH_DEPTH_8BPP                0
+#       define CIK_GRPH_DEPTH_16BPP               1
+#       define CIK_GRPH_DEPTH_32BPP               2
+#       define CIK_GRPH_NUM_BANKS(x)              (((x) & 0x3) << 2)
+#       define CIK_ADDR_SURF_2_BANK               0
+#       define CIK_ADDR_SURF_4_BANK               1
+#       define CIK_ADDR_SURF_8_BANK               2
+#       define CIK_ADDR_SURF_16_BANK              3
+#       define CIK_GRPH_Z(x)                      (((x) & 0x3) << 4)
+#       define CIK_GRPH_BANK_WIDTH(x)             (((x) & 0x3) << 6)
+#       define CIK_ADDR_SURF_BANK_WIDTH_1         0
+#       define CIK_ADDR_SURF_BANK_WIDTH_2         1
+#       define CIK_ADDR_SURF_BANK_WIDTH_4         2
+#       define CIK_ADDR_SURF_BANK_WIDTH_8         3
+#       define CIK_GRPH_FORMAT(x)                 (((x) & 0x7) << 8)
+/* 8 BPP */
+#       define CIK_GRPH_FORMAT_INDEXED            0
+/* 16 BPP */
+#       define CIK_GRPH_FORMAT_ARGB1555           0
+#       define CIK_GRPH_FORMAT_ARGB565            1
+#       define CIK_GRPH_FORMAT_ARGB4444           2
+#       define CIK_GRPH_FORMAT_AI88               3
+#       define CIK_GRPH_FORMAT_MONO16             4
+#       define CIK_GRPH_FORMAT_BGRA5551           5
+/* 32 BPP */
+#       define CIK_GRPH_FORMAT_ARGB8888           0
+#       define CIK_GRPH_FORMAT_ARGB2101010        1
+#       define CIK_GRPH_FORMAT_32BPP_DIG          2
+#       define CIK_GRPH_FORMAT_8B_ARGB2101010     3
+#       define CIK_GRPH_FORMAT_BGRA1010102        4
+#       define CIK_GRPH_FORMAT_8B_BGRA1010102     5
+#       define CIK_GRPH_FORMAT_RGB111110          6
+#       define CIK_GRPH_FORMAT_BGR101111          7
+#       define CIK_GRPH_BANK_HEIGHT(x)            (((x) & 0x3) << 11)
+#       define CIK_ADDR_SURF_BANK_HEIGHT_1        0
+#       define CIK_ADDR_SURF_BANK_HEIGHT_2        1
+#       define CIK_ADDR_SURF_BANK_HEIGHT_4        2
+#       define CIK_ADDR_SURF_BANK_HEIGHT_8        3
+#       define CIK_GRPH_TILE_SPLIT(x)             (((x) & 0x7) << 13)
+#       define CIK_ADDR_SURF_TILE_SPLIT_64B       0
+#       define CIK_ADDR_SURF_TILE_SPLIT_128B      1
+#       define CIK_ADDR_SURF_TILE_SPLIT_256B      2
+#       define CIK_ADDR_SURF_TILE_SPLIT_512B      3
+#       define CIK_ADDR_SURF_TILE_SPLIT_1KB       4
+#       define CIK_ADDR_SURF_TILE_SPLIT_2KB       5
+#       define CIK_ADDR_SURF_TILE_SPLIT_4KB       6
+#       define CIK_GRPH_MACRO_TILE_ASPECT(x)      (((x) & 0x3) << 18)
+#       define CIK_ADDR_SURF_MACRO_TILE_ASPECT_1  0
+#       define CIK_ADDR_SURF_MACRO_TILE_ASPECT_2  1
+#       define CIK_ADDR_SURF_MACRO_TILE_ASPECT_4  2
+#       define CIK_ADDR_SURF_MACRO_TILE_ASPECT_8  3
+#       define CIK_GRPH_ARRAY_MODE(x)             (((x) & 0x7) << 20)
+#       define CIK_GRPH_ARRAY_LINEAR_GENERAL      0
+#       define CIK_GRPH_ARRAY_LINEAR_ALIGNED      1
+#       define CIK_GRPH_ARRAY_1D_TILED_THIN1      2
+#       define CIK_GRPH_ARRAY_2D_TILED_THIN1      4
+#       define CIK_GRPH_PIPE_CONFIG(x)          (((x) & 0x1f) << 24)
+#       define CIK_ADDR_SURF_P2                         0
+#       define CIK_ADDR_SURF_P4_8x16            4
+#       define CIK_ADDR_SURF_P4_16x16           5
+#       define CIK_ADDR_SURF_P4_16x32           6
+#       define CIK_ADDR_SURF_P4_32x32           7
+#       define CIK_ADDR_SURF_P8_16x16_8x16      8
+#       define CIK_ADDR_SURF_P8_16x32_8x16      9
+#       define CIK_ADDR_SURF_P8_32x32_8x16      10
+#       define CIK_ADDR_SURF_P8_16x32_16x16     11
+#       define CIK_ADDR_SURF_P8_32x32_16x16     12
+#       define CIK_ADDR_SURF_P8_32x32_16x32     13
+#       define CIK_ADDR_SURF_P8_32x64_32x32     14
+#       define CIK_GRPH_MICRO_TILE_MODE(x)       (((x) & 0x7) << 29)
+#       define CIK_DISPLAY_MICRO_TILING          0
+#       define CIK_THIN_MICRO_TILING             1
+#       define CIK_DEPTH_MICRO_TILING            2
+#       define CIK_ROTATED_MICRO_TILING          4
+
+/* CUR blocks at 0x6998, 0x7598, 0x10198, 0x10d98, 0x11998, 0x12598 */
+#define CIK_CUR_CONTROL                           0x6998
+#       define CIK_CURSOR_EN                      (1 << 0)
+#       define CIK_CURSOR_MODE(x)                 (((x) & 0x3) << 8)
+#       define CIK_CURSOR_MONO                    0
+#       define CIK_CURSOR_24_1                    1
+#       define CIK_CURSOR_24_8_PRE_MULT           2
+#       define CIK_CURSOR_24_8_UNPRE_MULT         3
+#       define CIK_CURSOR_2X_MAGNIFY              (1 << 16)
+#       define CIK_CURSOR_FORCE_MC_ON             (1 << 20)
+#       define CIK_CURSOR_URGENT_CONTROL(x)       (((x) & 0x7) << 24)
+#       define CIK_CURSOR_URGENT_ALWAYS           0
+#       define CIK_CURSOR_URGENT_1_8              1
+#       define CIK_CURSOR_URGENT_1_4              2
+#       define CIK_CURSOR_URGENT_3_8              3
+#       define CIK_CURSOR_URGENT_1_2              4
+#define CIK_CUR_SURFACE_ADDRESS                   0x699c
+#       define CIK_CUR_SURFACE_ADDRESS_MASK       0xfffff000
+#define CIK_CUR_SIZE                              0x69a0
+#define CIK_CUR_SURFACE_ADDRESS_HIGH              0x69a4
+#define CIK_CUR_POSITION                          0x69a8
+#define CIK_CUR_HOT_SPOT                          0x69ac
+#define CIK_CUR_COLOR1                            0x69b0
+#define CIK_CUR_COLOR2                            0x69b4
+#define CIK_CUR_UPDATE                            0x69b8
+#       define CIK_CURSOR_UPDATE_PENDING          (1 << 0)
+#       define CIK_CURSOR_UPDATE_TAKEN            (1 << 1)
+#       define CIK_CURSOR_UPDATE_LOCK             (1 << 16)
+#       define CIK_CURSOR_DISABLE_MULTIPLE_UPDATE (1 << 24)
+
+#define CIK_ALPHA_CONTROL                         0x6af0
+#       define CIK_CURSOR_ALPHA_BLND_ENA          (1 << 1)
+
+#define CIK_LB_DATA_FORMAT                        0x6b00
+#       define CIK_INTERLEAVE_EN                  (1 << 3)
+
+#define CIK_LB_DESKTOP_HEIGHT                     0x6b0c
+
+#endif
diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h
new file mode 100644 (file)
index 0000000..63514b9
--- /dev/null
@@ -0,0 +1,1297 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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: Alex Deucher
+ */
+#ifndef CIK_H
+#define CIK_H
+
+#define BONAIRE_GB_ADDR_CONFIG_GOLDEN        0x12010001
+
+#define CIK_RB_BITMAP_WIDTH_PER_SH  2
+
+/* SMC IND registers */
+#define GENERAL_PWRMGT                                    0xC0200000
+#       define GPU_COUNTER_CLK                            (1 << 15)
+
+#define CG_CLKPIN_CNTL                                    0xC05001A0
+#       define XTALIN_DIVIDE                              (1 << 1)
+
+#define PCIE_INDEX                                     0x38
+#define PCIE_DATA                                      0x3C
+
+#define VGA_HDP_CONTROL                                0x328
+#define                VGA_MEMORY_DISABLE                              (1 << 4)
+
+#define DMIF_ADDR_CALC                                 0xC00
+
+#define        SRBM_GFX_CNTL                                   0xE44
+#define                PIPEID(x)                                       ((x) << 0)
+#define                MEID(x)                                         ((x) << 2)
+#define                VMID(x)                                         ((x) << 4)
+#define                QUEUEID(x)                                      ((x) << 8)
+
+#define        SRBM_STATUS2                                    0xE4C
+#define                SDMA_BUSY                               (1 << 5)
+#define                SDMA1_BUSY                              (1 << 6)
+#define        SRBM_STATUS                                     0xE50
+#define                UVD_RQ_PENDING                          (1 << 1)
+#define                GRBM_RQ_PENDING                         (1 << 5)
+#define                VMC_BUSY                                (1 << 8)
+#define                MCB_BUSY                                (1 << 9)
+#define                MCB_NON_DISPLAY_BUSY                    (1 << 10)
+#define                MCC_BUSY                                (1 << 11)
+#define                MCD_BUSY                                (1 << 12)
+#define                SEM_BUSY                                (1 << 14)
+#define                IH_BUSY                                 (1 << 17)
+#define                UVD_BUSY                                (1 << 19)
+
+#define        SRBM_SOFT_RESET                                 0xE60
+#define                SOFT_RESET_BIF                          (1 << 1)
+#define                SOFT_RESET_R0PLL                        (1 << 4)
+#define                SOFT_RESET_DC                           (1 << 5)
+#define                SOFT_RESET_SDMA1                        (1 << 6)
+#define                SOFT_RESET_GRBM                         (1 << 8)
+#define                SOFT_RESET_HDP                          (1 << 9)
+#define                SOFT_RESET_IH                           (1 << 10)
+#define                SOFT_RESET_MC                           (1 << 11)
+#define                SOFT_RESET_ROM                          (1 << 14)
+#define                SOFT_RESET_SEM                          (1 << 15)
+#define                SOFT_RESET_VMC                          (1 << 17)
+#define                SOFT_RESET_SDMA                         (1 << 20)
+#define                SOFT_RESET_TST                          (1 << 21)
+#define                SOFT_RESET_REGBB                        (1 << 22)
+#define                SOFT_RESET_ORB                          (1 << 23)
+#define                SOFT_RESET_VCE                          (1 << 24)
+
+#define VM_L2_CNTL                                     0x1400
+#define                ENABLE_L2_CACHE                                 (1 << 0)
+#define                ENABLE_L2_FRAGMENT_PROCESSING                   (1 << 1)
+#define                L2_CACHE_PTE_ENDIAN_SWAP_MODE(x)                ((x) << 2)
+#define                L2_CACHE_PDE_ENDIAN_SWAP_MODE(x)                ((x) << 4)
+#define                ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE         (1 << 9)
+#define                ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE        (1 << 10)
+#define                EFFECTIVE_L2_QUEUE_SIZE(x)                      (((x) & 7) << 15)
+#define                CONTEXT1_IDENTITY_ACCESS_MODE(x)                (((x) & 3) << 19)
+#define VM_L2_CNTL2                                    0x1404
+#define                INVALIDATE_ALL_L1_TLBS                          (1 << 0)
+#define                INVALIDATE_L2_CACHE                             (1 << 1)
+#define                INVALIDATE_CACHE_MODE(x)                        ((x) << 26)
+#define                        INVALIDATE_PTE_AND_PDE_CACHES           0
+#define                        INVALIDATE_ONLY_PTE_CACHES              1
+#define                        INVALIDATE_ONLY_PDE_CACHES              2
+#define VM_L2_CNTL3                                    0x1408
+#define                BANK_SELECT(x)                                  ((x) << 0)
+#define                L2_CACHE_UPDATE_MODE(x)                         ((x) << 6)
+#define                L2_CACHE_BIGK_FRAGMENT_SIZE(x)                  ((x) << 15)
+#define                L2_CACHE_BIGK_ASSOCIATIVITY                     (1 << 20)
+#define        VM_L2_STATUS                                    0x140C
+#define                L2_BUSY                                         (1 << 0)
+#define VM_CONTEXT0_CNTL                               0x1410
+#define                ENABLE_CONTEXT                                  (1 << 0)
+#define                PAGE_TABLE_DEPTH(x)                             (((x) & 3) << 1)
+#define                RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT         (1 << 3)
+#define                RANGE_PROTECTION_FAULT_ENABLE_DEFAULT           (1 << 4)
+#define                DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT    (1 << 6)
+#define                DUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT      (1 << 7)
+#define                PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT          (1 << 9)
+#define                PDE0_PROTECTION_FAULT_ENABLE_DEFAULT            (1 << 10)
+#define                VALID_PROTECTION_FAULT_ENABLE_INTERRUPT         (1 << 12)
+#define                VALID_PROTECTION_FAULT_ENABLE_DEFAULT           (1 << 13)
+#define                READ_PROTECTION_FAULT_ENABLE_INTERRUPT          (1 << 15)
+#define                READ_PROTECTION_FAULT_ENABLE_DEFAULT            (1 << 16)
+#define                WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT         (1 << 18)
+#define                WRITE_PROTECTION_FAULT_ENABLE_DEFAULT           (1 << 19)
+#define VM_CONTEXT1_CNTL                               0x1414
+#define VM_CONTEXT0_CNTL2                              0x1430
+#define VM_CONTEXT1_CNTL2                              0x1434
+#define        VM_CONTEXT8_PAGE_TABLE_BASE_ADDR                0x1438
+#define        VM_CONTEXT9_PAGE_TABLE_BASE_ADDR                0x143c
+#define        VM_CONTEXT10_PAGE_TABLE_BASE_ADDR               0x1440
+#define        VM_CONTEXT11_PAGE_TABLE_BASE_ADDR               0x1444
+#define        VM_CONTEXT12_PAGE_TABLE_BASE_ADDR               0x1448
+#define        VM_CONTEXT13_PAGE_TABLE_BASE_ADDR               0x144c
+#define        VM_CONTEXT14_PAGE_TABLE_BASE_ADDR               0x1450
+#define        VM_CONTEXT15_PAGE_TABLE_BASE_ADDR               0x1454
+
+#define VM_INVALIDATE_REQUEST                          0x1478
+#define VM_INVALIDATE_RESPONSE                         0x147c
+
+#define        VM_CONTEXT1_PROTECTION_FAULT_STATUS             0x14DC
+
+#define        VM_CONTEXT1_PROTECTION_FAULT_ADDR               0x14FC
+
+#define VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR      0x1518
+#define VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR      0x151c
+
+#define        VM_CONTEXT0_PAGE_TABLE_BASE_ADDR                0x153c
+#define        VM_CONTEXT1_PAGE_TABLE_BASE_ADDR                0x1540
+#define        VM_CONTEXT2_PAGE_TABLE_BASE_ADDR                0x1544
+#define        VM_CONTEXT3_PAGE_TABLE_BASE_ADDR                0x1548
+#define        VM_CONTEXT4_PAGE_TABLE_BASE_ADDR                0x154c
+#define        VM_CONTEXT5_PAGE_TABLE_BASE_ADDR                0x1550
+#define        VM_CONTEXT6_PAGE_TABLE_BASE_ADDR                0x1554
+#define        VM_CONTEXT7_PAGE_TABLE_BASE_ADDR                0x1558
+#define        VM_CONTEXT0_PAGE_TABLE_START_ADDR               0x155c
+#define        VM_CONTEXT1_PAGE_TABLE_START_ADDR               0x1560
+
+#define        VM_CONTEXT0_PAGE_TABLE_END_ADDR                 0x157C
+#define        VM_CONTEXT1_PAGE_TABLE_END_ADDR                 0x1580
+
+#define MC_SHARED_CHMAP                                                0x2004
+#define                NOOFCHAN_SHIFT                                  12
+#define                NOOFCHAN_MASK                                   0x0000f000
+#define MC_SHARED_CHREMAP                                      0x2008
+
+#define CHUB_CONTROL                                   0x1864
+#define                BYPASS_VM                                       (1 << 0)
+
+#define        MC_VM_FB_LOCATION                               0x2024
+#define        MC_VM_AGP_TOP                                   0x2028
+#define        MC_VM_AGP_BOT                                   0x202C
+#define        MC_VM_AGP_BASE                                  0x2030
+#define        MC_VM_SYSTEM_APERTURE_LOW_ADDR                  0x2034
+#define        MC_VM_SYSTEM_APERTURE_HIGH_ADDR                 0x2038
+#define        MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR              0x203C
+
+#define        MC_VM_MX_L1_TLB_CNTL                            0x2064
+#define                ENABLE_L1_TLB                                   (1 << 0)
+#define                ENABLE_L1_FRAGMENT_PROCESSING                   (1 << 1)
+#define                SYSTEM_ACCESS_MODE_PA_ONLY                      (0 << 3)
+#define                SYSTEM_ACCESS_MODE_USE_SYS_MAP                  (1 << 3)
+#define                SYSTEM_ACCESS_MODE_IN_SYS                       (2 << 3)
+#define                SYSTEM_ACCESS_MODE_NOT_IN_SYS                   (3 << 3)
+#define                SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU       (0 << 5)
+#define                ENABLE_ADVANCED_DRIVER_MODEL                    (1 << 6)
+#define        MC_VM_FB_OFFSET                                 0x2068
+
+#define MC_SHARED_BLACKOUT_CNTL                        0x20ac
+
+#define        MC_ARB_RAMCFG                                   0x2760
+#define                NOOFBANK_SHIFT                                  0
+#define                NOOFBANK_MASK                                   0x00000003
+#define                NOOFRANK_SHIFT                                  2
+#define                NOOFRANK_MASK                                   0x00000004
+#define                NOOFROWS_SHIFT                                  3
+#define                NOOFROWS_MASK                                   0x00000038
+#define                NOOFCOLS_SHIFT                                  6
+#define                NOOFCOLS_MASK                                   0x000000C0
+#define                CHANSIZE_SHIFT                                  8
+#define                CHANSIZE_MASK                                   0x00000100
+#define                NOOFGROUPS_SHIFT                                12
+#define                NOOFGROUPS_MASK                                 0x00001000
+
+#define MC_SEQ_SUP_CNTL                                0x28c8
+#define                RUN_MASK                                (1 << 0)
+#define MC_SEQ_SUP_PGM                                 0x28cc
+
+#define        MC_SEQ_TRAIN_WAKEUP_CNTL                        0x28e8
+#define                TRAIN_DONE_D0                           (1 << 30)
+#define                TRAIN_DONE_D1                           (1 << 31)
+
+#define MC_IO_PAD_CNTL_D0                              0x29d0
+#define                MEM_FALL_OUT_CMD                        (1 << 8)
+
+#define MC_SEQ_IO_DEBUG_INDEX                          0x2a44
+#define MC_SEQ_IO_DEBUG_DATA                                   0x2a48
+
+#define        HDP_HOST_PATH_CNTL                              0x2C00
+#define        HDP_NONSURFACE_BASE                             0x2C04
+#define        HDP_NONSURFACE_INFO                             0x2C08
+#define        HDP_NONSURFACE_SIZE                             0x2C0C
+
+#define HDP_ADDR_CONFIG                                0x2F48
+#define HDP_MISC_CNTL                                  0x2F4C
+#define        HDP_FLUSH_INVALIDATE_CACHE                      (1 << 0)
+
+#define IH_RB_CNTL                                        0x3e00
+#       define IH_RB_ENABLE                               (1 << 0)
+#       define IH_RB_SIZE(x)                              ((x) << 1) /* log2 */
+#       define IH_RB_FULL_DRAIN_ENABLE                    (1 << 6)
+#       define IH_WPTR_WRITEBACK_ENABLE                   (1 << 8)
+#       define IH_WPTR_WRITEBACK_TIMER(x)                 ((x) << 9) /* log2 */
+#       define IH_WPTR_OVERFLOW_ENABLE                    (1 << 16)
+#       define IH_WPTR_OVERFLOW_CLEAR                     (1 << 31)
+#define IH_RB_BASE                                        0x3e04
+#define IH_RB_RPTR                                        0x3e08
+#define IH_RB_WPTR                                        0x3e0c
+#       define RB_OVERFLOW                                (1 << 0)
+#       define WPTR_OFFSET_MASK                           0x3fffc
+#define IH_RB_WPTR_ADDR_HI                                0x3e10
+#define IH_RB_WPTR_ADDR_LO                                0x3e14
+#define IH_CNTL                                           0x3e18
+#       define ENABLE_INTR                                (1 << 0)
+#       define IH_MC_SWAP(x)                              ((x) << 1)
+#       define IH_MC_SWAP_NONE                            0
+#       define IH_MC_SWAP_16BIT                           1
+#       define IH_MC_SWAP_32BIT                           2
+#       define IH_MC_SWAP_64BIT                           3
+#       define RPTR_REARM                                 (1 << 4)
+#       define MC_WRREQ_CREDIT(x)                         ((x) << 15)
+#       define MC_WR_CLEAN_CNT(x)                         ((x) << 20)
+#       define MC_VMID(x)                                 ((x) << 25)
+
+#define        CONFIG_MEMSIZE                                  0x5428
+
+#define INTERRUPT_CNTL                                    0x5468
+#       define IH_DUMMY_RD_OVERRIDE                       (1 << 0)
+#       define IH_DUMMY_RD_EN                             (1 << 1)
+#       define IH_REQ_NONSNOOP_EN                         (1 << 3)
+#       define GEN_IH_INT_EN                              (1 << 8)
+#define INTERRUPT_CNTL2                                   0x546c
+
+#define HDP_MEM_COHERENCY_FLUSH_CNTL                   0x5480
+
+#define        BIF_FB_EN                                               0x5490
+#define                FB_READ_EN                                      (1 << 0)
+#define                FB_WRITE_EN                                     (1 << 1)
+
+#define HDP_REG_COHERENCY_FLUSH_CNTL                   0x54A0
+
+#define GPU_HDP_FLUSH_REQ                              0x54DC
+#define GPU_HDP_FLUSH_DONE                             0x54E0
+#define                CP0                                     (1 << 0)
+#define                CP1                                     (1 << 1)
+#define                CP2                                     (1 << 2)
+#define                CP3                                     (1 << 3)
+#define                CP4                                     (1 << 4)
+#define                CP5                                     (1 << 5)
+#define                CP6                                     (1 << 6)
+#define                CP7                                     (1 << 7)
+#define                CP8                                     (1 << 8)
+#define                CP9                                     (1 << 9)
+#define                SDMA0                                   (1 << 10)
+#define                SDMA1                                   (1 << 11)
+
+/* 0x6b04, 0x7704, 0x10304, 0x10f04, 0x11b04, 0x12704 */
+#define        LB_MEMORY_CTRL                                  0x6b04
+#define                LB_MEMORY_SIZE(x)                       ((x) << 0)
+#define                LB_MEMORY_CONFIG(x)                     ((x) << 20)
+
+#define        DPG_WATERMARK_MASK_CONTROL                      0x6cc8
+#       define LATENCY_WATERMARK_MASK(x)               ((x) << 8)
+#define        DPG_PIPE_LATENCY_CONTROL                        0x6ccc
+#       define LATENCY_LOW_WATERMARK(x)                        ((x) << 0)
+#       define LATENCY_HIGH_WATERMARK(x)               ((x) << 16)
+
+/* 0x6b24, 0x7724, 0x10324, 0x10f24, 0x11b24, 0x12724 */
+#define LB_VLINE_STATUS                                 0x6b24
+#       define VLINE_OCCURRED                           (1 << 0)
+#       define VLINE_ACK                                (1 << 4)
+#       define VLINE_STAT                               (1 << 12)
+#       define VLINE_INTERRUPT                          (1 << 16)
+#       define VLINE_INTERRUPT_TYPE                     (1 << 17)
+/* 0x6b2c, 0x772c, 0x1032c, 0x10f2c, 0x11b2c, 0x1272c */
+#define LB_VBLANK_STATUS                                0x6b2c
+#       define VBLANK_OCCURRED                          (1 << 0)
+#       define VBLANK_ACK                               (1 << 4)
+#       define VBLANK_STAT                              (1 << 12)
+#       define VBLANK_INTERRUPT                         (1 << 16)
+#       define VBLANK_INTERRUPT_TYPE                    (1 << 17)
+
+/* 0x6b20, 0x7720, 0x10320, 0x10f20, 0x11b20, 0x12720 */
+#define LB_INTERRUPT_MASK                               0x6b20
+#       define VBLANK_INTERRUPT_MASK                    (1 << 0)
+#       define VLINE_INTERRUPT_MASK                     (1 << 4)
+#       define VLINE2_INTERRUPT_MASK                    (1 << 8)
+
+#define DISP_INTERRUPT_STATUS                           0x60f4
+#       define LB_D1_VLINE_INTERRUPT                    (1 << 2)
+#       define LB_D1_VBLANK_INTERRUPT                   (1 << 3)
+#       define DC_HPD1_INTERRUPT                        (1 << 17)
+#       define DC_HPD1_RX_INTERRUPT                     (1 << 18)
+#       define DACA_AUTODETECT_INTERRUPT                (1 << 22)
+#       define DACB_AUTODETECT_INTERRUPT                (1 << 23)
+#       define DC_I2C_SW_DONE_INTERRUPT                 (1 << 24)
+#       define DC_I2C_HW_DONE_INTERRUPT                 (1 << 25)
+#define DISP_INTERRUPT_STATUS_CONTINUE                  0x60f8
+#       define LB_D2_VLINE_INTERRUPT                    (1 << 2)
+#       define LB_D2_VBLANK_INTERRUPT                   (1 << 3)
+#       define DC_HPD2_INTERRUPT                        (1 << 17)
+#       define DC_HPD2_RX_INTERRUPT                     (1 << 18)
+#       define DISP_TIMER_INTERRUPT                     (1 << 24)
+#define DISP_INTERRUPT_STATUS_CONTINUE2                 0x60fc
+#       define LB_D3_VLINE_INTERRUPT                    (1 << 2)
+#       define LB_D3_VBLANK_INTERRUPT                   (1 << 3)
+#       define DC_HPD3_INTERRUPT                        (1 << 17)
+#       define DC_HPD3_RX_INTERRUPT                     (1 << 18)
+#define DISP_INTERRUPT_STATUS_CONTINUE3                 0x6100
+#       define LB_D4_VLINE_INTERRUPT                    (1 << 2)
+#       define LB_D4_VBLANK_INTERRUPT                   (1 << 3)
+#       define DC_HPD4_INTERRUPT                        (1 << 17)
+#       define DC_HPD4_RX_INTERRUPT                     (1 << 18)
+#define DISP_INTERRUPT_STATUS_CONTINUE4                 0x614c
+#       define LB_D5_VLINE_INTERRUPT                    (1 << 2)
+#       define LB_D5_VBLANK_INTERRUPT                   (1 << 3)
+#       define DC_HPD5_INTERRUPT                        (1 << 17)
+#       define DC_HPD5_RX_INTERRUPT                     (1 << 18)
+#define DISP_INTERRUPT_STATUS_CONTINUE5                 0x6150
+#       define LB_D6_VLINE_INTERRUPT                    (1 << 2)
+#       define LB_D6_VBLANK_INTERRUPT                   (1 << 3)
+#       define DC_HPD6_INTERRUPT                        (1 << 17)
+#       define DC_HPD6_RX_INTERRUPT                     (1 << 18)
+#define DISP_INTERRUPT_STATUS_CONTINUE6                 0x6780
+
+#define        DAC_AUTODETECT_INT_CONTROL                      0x67c8
+
+#define DC_HPD1_INT_STATUS                              0x601c
+#define DC_HPD2_INT_STATUS                              0x6028
+#define DC_HPD3_INT_STATUS                              0x6034
+#define DC_HPD4_INT_STATUS                              0x6040
+#define DC_HPD5_INT_STATUS                              0x604c
+#define DC_HPD6_INT_STATUS                              0x6058
+#       define DC_HPDx_INT_STATUS                       (1 << 0)
+#       define DC_HPDx_SENSE                            (1 << 1)
+#       define DC_HPDx_SENSE_DELAYED                    (1 << 4)
+#       define DC_HPDx_RX_INT_STATUS                    (1 << 8)
+
+#define DC_HPD1_INT_CONTROL                             0x6020
+#define DC_HPD2_INT_CONTROL                             0x602c
+#define DC_HPD3_INT_CONTROL                             0x6038
+#define DC_HPD4_INT_CONTROL                             0x6044
+#define DC_HPD5_INT_CONTROL                             0x6050
+#define DC_HPD6_INT_CONTROL                             0x605c
+#       define DC_HPDx_INT_ACK                          (1 << 0)
+#       define DC_HPDx_INT_POLARITY                     (1 << 8)
+#       define DC_HPDx_INT_EN                           (1 << 16)
+#       define DC_HPDx_RX_INT_ACK                       (1 << 20)
+#       define DC_HPDx_RX_INT_EN                        (1 << 24)
+
+#define DC_HPD1_CONTROL                                   0x6024
+#define DC_HPD2_CONTROL                                   0x6030
+#define DC_HPD3_CONTROL                                   0x603c
+#define DC_HPD4_CONTROL                                   0x6048
+#define DC_HPD5_CONTROL                                   0x6054
+#define DC_HPD6_CONTROL                                   0x6060
+#       define DC_HPDx_CONNECTION_TIMER(x)                ((x) << 0)
+#       define DC_HPDx_RX_INT_TIMER(x)                    ((x) << 16)
+#       define DC_HPDx_EN                                 (1 << 28)
+
+#define        GRBM_CNTL                                       0x8000
+#define                GRBM_READ_TIMEOUT(x)                            ((x) << 0)
+
+#define        GRBM_STATUS2                                    0x8008
+#define                ME0PIPE1_CMDFIFO_AVAIL_MASK                     0x0000000F
+#define                ME0PIPE1_CF_RQ_PENDING                          (1 << 4)
+#define                ME0PIPE1_PF_RQ_PENDING                          (1 << 5)
+#define                ME1PIPE0_RQ_PENDING                             (1 << 6)
+#define                ME1PIPE1_RQ_PENDING                             (1 << 7)
+#define                ME1PIPE2_RQ_PENDING                             (1 << 8)
+#define                ME1PIPE3_RQ_PENDING                             (1 << 9)
+#define                ME2PIPE0_RQ_PENDING                             (1 << 10)
+#define                ME2PIPE1_RQ_PENDING                             (1 << 11)
+#define                ME2PIPE2_RQ_PENDING                             (1 << 12)
+#define                ME2PIPE3_RQ_PENDING                             (1 << 13)
+#define                RLC_RQ_PENDING                                  (1 << 14)
+#define                RLC_BUSY                                        (1 << 24)
+#define                TC_BUSY                                         (1 << 25)
+#define                CPF_BUSY                                        (1 << 28)
+#define                CPC_BUSY                                        (1 << 29)
+#define                CPG_BUSY                                        (1 << 30)
+
+#define        GRBM_STATUS                                     0x8010
+#define                ME0PIPE0_CMDFIFO_AVAIL_MASK                     0x0000000F
+#define                SRBM_RQ_PENDING                                 (1 << 5)
+#define                ME0PIPE0_CF_RQ_PENDING                          (1 << 7)
+#define                ME0PIPE0_PF_RQ_PENDING                          (1 << 8)
+#define                GDS_DMA_RQ_PENDING                              (1 << 9)
+#define                DB_CLEAN                                        (1 << 12)
+#define                CB_CLEAN                                        (1 << 13)
+#define                TA_BUSY                                         (1 << 14)
+#define                GDS_BUSY                                        (1 << 15)
+#define                WD_BUSY_NO_DMA                                  (1 << 16)
+#define                VGT_BUSY                                        (1 << 17)
+#define                IA_BUSY_NO_DMA                                  (1 << 18)
+#define                IA_BUSY                                         (1 << 19)
+#define                SX_BUSY                                         (1 << 20)
+#define                WD_BUSY                                         (1 << 21)
+#define                SPI_BUSY                                        (1 << 22)
+#define                BCI_BUSY                                        (1 << 23)
+#define                SC_BUSY                                         (1 << 24)
+#define                PA_BUSY                                         (1 << 25)
+#define                DB_BUSY                                         (1 << 26)
+#define                CP_COHERENCY_BUSY                               (1 << 28)
+#define                CP_BUSY                                         (1 << 29)
+#define                CB_BUSY                                         (1 << 30)
+#define                GUI_ACTIVE                                      (1 << 31)
+#define        GRBM_STATUS_SE0                                 0x8014
+#define        GRBM_STATUS_SE1                                 0x8018
+#define        GRBM_STATUS_SE2                                 0x8038
+#define        GRBM_STATUS_SE3                                 0x803C
+#define                SE_DB_CLEAN                                     (1 << 1)
+#define                SE_CB_CLEAN                                     (1 << 2)
+#define                SE_BCI_BUSY                                     (1 << 22)
+#define                SE_VGT_BUSY                                     (1 << 23)
+#define                SE_PA_BUSY                                      (1 << 24)
+#define                SE_TA_BUSY                                      (1 << 25)
+#define                SE_SX_BUSY                                      (1 << 26)
+#define                SE_SPI_BUSY                                     (1 << 27)
+#define                SE_SC_BUSY                                      (1 << 29)
+#define                SE_DB_BUSY                                      (1 << 30)
+#define                SE_CB_BUSY                                      (1 << 31)
+
+#define        GRBM_SOFT_RESET                                 0x8020
+#define                SOFT_RESET_CP                                   (1 << 0)  /* All CP blocks */
+#define                SOFT_RESET_RLC                                  (1 << 2)  /* RLC */
+#define                SOFT_RESET_GFX                                  (1 << 16) /* GFX */
+#define                SOFT_RESET_CPF                                  (1 << 17) /* CP fetcher shared by gfx and compute */
+#define                SOFT_RESET_CPC                                  (1 << 18) /* CP Compute (MEC1/2) */
+#define                SOFT_RESET_CPG                                  (1 << 19) /* CP GFX (PFP, ME, CE) */
+
+#define GRBM_INT_CNTL                                   0x8060
+#       define RDERR_INT_ENABLE                         (1 << 0)
+#       define GUI_IDLE_INT_ENABLE                      (1 << 19)
+
+#define CP_CPC_STATUS                                  0x8210
+#define CP_CPC_BUSY_STAT                               0x8214
+#define CP_CPC_STALLED_STAT1                           0x8218
+#define CP_CPF_STATUS                                  0x821c
+#define CP_CPF_BUSY_STAT                               0x8220
+#define CP_CPF_STALLED_STAT1                           0x8224
+
+#define CP_MEC_CNTL                                    0x8234
+#define                MEC_ME2_HALT                                    (1 << 28)
+#define                MEC_ME1_HALT                                    (1 << 30)
+
+#define CP_MEC_CNTL                                    0x8234
+#define                MEC_ME2_HALT                                    (1 << 28)
+#define                MEC_ME1_HALT                                    (1 << 30)
+
+#define CP_STALLED_STAT3                               0x8670
+#define CP_STALLED_STAT1                               0x8674
+#define CP_STALLED_STAT2                               0x8678
+
+#define CP_STAT                                                0x8680
+
+#define CP_ME_CNTL                                     0x86D8
+#define                CP_CE_HALT                                      (1 << 24)
+#define                CP_PFP_HALT                                     (1 << 26)
+#define                CP_ME_HALT                                      (1 << 28)
+
+#define        CP_RB0_RPTR                                     0x8700
+#define        CP_RB_WPTR_DELAY                                0x8704
+
+#define CP_MEQ_THRESHOLDS                              0x8764
+#define                MEQ1_START(x)                           ((x) << 0)
+#define                MEQ2_START(x)                           ((x) << 8)
+
+#define        VGT_VTX_VECT_EJECT_REG                          0x88B0
+
+#define        VGT_CACHE_INVALIDATION                          0x88C4
+#define                CACHE_INVALIDATION(x)                           ((x) << 0)
+#define                        VC_ONLY                                         0
+#define                        TC_ONLY                                         1
+#define                        VC_AND_TC                                       2
+#define                AUTO_INVLD_EN(x)                                ((x) << 6)
+#define                        NO_AUTO                                         0
+#define                        ES_AUTO                                         1
+#define                        GS_AUTO                                         2
+#define                        ES_AND_GS_AUTO                                  3
+
+#define        VGT_GS_VERTEX_REUSE                             0x88D4
+
+#define CC_GC_SHADER_ARRAY_CONFIG                      0x89bc
+#define                INACTIVE_CUS_MASK                       0xFFFF0000
+#define                INACTIVE_CUS_SHIFT                      16
+#define GC_USER_SHADER_ARRAY_CONFIG                    0x89c0
+
+#define        PA_CL_ENHANCE                                   0x8A14
+#define                CLIP_VTX_REORDER_ENA                            (1 << 0)
+#define                NUM_CLIP_SEQ(x)                                 ((x) << 1)
+
+#define        PA_SC_FORCE_EOV_MAX_CNTS                        0x8B24
+#define                FORCE_EOV_MAX_CLK_CNT(x)                        ((x) << 0)
+#define                FORCE_EOV_MAX_REZ_CNT(x)                        ((x) << 16)
+
+#define        PA_SC_FIFO_SIZE                                 0x8BCC
+#define                SC_FRONTEND_PRIM_FIFO_SIZE(x)                   ((x) << 0)
+#define                SC_BACKEND_PRIM_FIFO_SIZE(x)                    ((x) << 6)
+#define                SC_HIZ_TILE_FIFO_SIZE(x)                        ((x) << 15)
+#define                SC_EARLYZ_TILE_FIFO_SIZE(x)                     ((x) << 23)
+
+#define        PA_SC_ENHANCE                                   0x8BF0
+#define                ENABLE_PA_SC_OUT_OF_ORDER                       (1 << 0)
+#define                DISABLE_PA_SC_GUIDANCE                          (1 << 13)
+
+#define        SQ_CONFIG                                       0x8C00
+
+#define        SH_MEM_BASES                                    0x8C28
+/* if PTR32, these are the bases for scratch and lds */
+#define                PRIVATE_BASE(x)                                 ((x) << 0) /* scratch */
+#define                SHARED_BASE(x)                                  ((x) << 16) /* LDS */
+#define        SH_MEM_APE1_BASE                                0x8C2C
+/* if PTR32, this is the base location of GPUVM */
+#define        SH_MEM_APE1_LIMIT                               0x8C30
+/* if PTR32, this is the upper limit of GPUVM */
+#define        SH_MEM_CONFIG                                   0x8C34
+#define                PTR32                                           (1 << 0)
+#define                ALIGNMENT_MODE(x)                               ((x) << 2)
+#define                        SH_MEM_ALIGNMENT_MODE_DWORD                     0
+#define                        SH_MEM_ALIGNMENT_MODE_DWORD_STRICT              1
+#define                        SH_MEM_ALIGNMENT_MODE_STRICT                    2
+#define                        SH_MEM_ALIGNMENT_MODE_UNALIGNED                 3
+#define                DEFAULT_MTYPE(x)                                ((x) << 4)
+#define                APE1_MTYPE(x)                                   ((x) << 7)
+
+#define        SX_DEBUG_1                                      0x9060
+
+#define        SPI_CONFIG_CNTL                                 0x9100
+
+#define        SPI_CONFIG_CNTL_1                               0x913C
+#define                VTX_DONE_DELAY(x)                               ((x) << 0)
+#define                INTERP_ONE_PRIM_PER_ROW                         (1 << 4)
+
+#define        TA_CNTL_AUX                                     0x9508
+
+#define DB_DEBUG                                       0x9830
+#define DB_DEBUG2                                      0x9834
+#define DB_DEBUG3                                      0x9838
+
+#define CC_RB_BACKEND_DISABLE                          0x98F4
+#define                BACKEND_DISABLE(x)                      ((x) << 16)
+#define GB_ADDR_CONFIG                                 0x98F8
+#define                NUM_PIPES(x)                            ((x) << 0)
+#define                NUM_PIPES_MASK                          0x00000007
+#define                NUM_PIPES_SHIFT                         0
+#define                PIPE_INTERLEAVE_SIZE(x)                 ((x) << 4)
+#define                PIPE_INTERLEAVE_SIZE_MASK               0x00000070
+#define                PIPE_INTERLEAVE_SIZE_SHIFT              4
+#define                NUM_SHADER_ENGINES(x)                   ((x) << 12)
+#define                NUM_SHADER_ENGINES_MASK                 0x00003000
+#define                NUM_SHADER_ENGINES_SHIFT                12
+#define                SHADER_ENGINE_TILE_SIZE(x)              ((x) << 16)
+#define                SHADER_ENGINE_TILE_SIZE_MASK            0x00070000
+#define                SHADER_ENGINE_TILE_SIZE_SHIFT           16
+#define                ROW_SIZE(x)                             ((x) << 28)
+#define                ROW_SIZE_MASK                           0x30000000
+#define                ROW_SIZE_SHIFT                          28
+
+#define        GB_TILE_MODE0                                   0x9910
+#       define ARRAY_MODE(x)                                   ((x) << 2)
+#              define  ARRAY_LINEAR_GENERAL                    0
+#              define  ARRAY_LINEAR_ALIGNED                    1
+#              define  ARRAY_1D_TILED_THIN1                    2
+#              define  ARRAY_2D_TILED_THIN1                    4
+#              define  ARRAY_PRT_TILED_THIN1                   5
+#              define  ARRAY_PRT_2D_TILED_THIN1                6
+#       define PIPE_CONFIG(x)                                  ((x) << 6)
+#              define  ADDR_SURF_P2                            0
+#              define  ADDR_SURF_P4_8x16                       4
+#              define  ADDR_SURF_P4_16x16                      5
+#              define  ADDR_SURF_P4_16x32                      6
+#              define  ADDR_SURF_P4_32x32                      7
+#              define  ADDR_SURF_P8_16x16_8x16                 8
+#              define  ADDR_SURF_P8_16x32_8x16                 9
+#              define  ADDR_SURF_P8_32x32_8x16                 10
+#              define  ADDR_SURF_P8_16x32_16x16                11
+#              define  ADDR_SURF_P8_32x32_16x16                12
+#              define  ADDR_SURF_P8_32x32_16x32                13
+#              define  ADDR_SURF_P8_32x64_32x32                14
+#       define TILE_SPLIT(x)                                   ((x) << 11)
+#              define  ADDR_SURF_TILE_SPLIT_64B                0
+#              define  ADDR_SURF_TILE_SPLIT_128B               1
+#              define  ADDR_SURF_TILE_SPLIT_256B               2
+#              define  ADDR_SURF_TILE_SPLIT_512B               3
+#              define  ADDR_SURF_TILE_SPLIT_1KB                4
+#              define  ADDR_SURF_TILE_SPLIT_2KB                5
+#              define  ADDR_SURF_TILE_SPLIT_4KB                6
+#       define MICRO_TILE_MODE_NEW(x)                          ((x) << 22)
+#              define  ADDR_SURF_DISPLAY_MICRO_TILING          0
+#              define  ADDR_SURF_THIN_MICRO_TILING             1
+#              define  ADDR_SURF_DEPTH_MICRO_TILING            2
+#              define  ADDR_SURF_ROTATED_MICRO_TILING          3
+#       define SAMPLE_SPLIT(x)                                 ((x) << 25)
+#              define  ADDR_SURF_SAMPLE_SPLIT_1                0
+#              define  ADDR_SURF_SAMPLE_SPLIT_2                1
+#              define  ADDR_SURF_SAMPLE_SPLIT_4                2
+#              define  ADDR_SURF_SAMPLE_SPLIT_8                3
+
+#define        GB_MACROTILE_MODE0                                      0x9990
+#       define BANK_WIDTH(x)                                   ((x) << 0)
+#              define  ADDR_SURF_BANK_WIDTH_1                  0
+#              define  ADDR_SURF_BANK_WIDTH_2                  1
+#              define  ADDR_SURF_BANK_WIDTH_4                  2
+#              define  ADDR_SURF_BANK_WIDTH_8                  3
+#       define BANK_HEIGHT(x)                                  ((x) << 2)
+#              define  ADDR_SURF_BANK_HEIGHT_1                 0
+#              define  ADDR_SURF_BANK_HEIGHT_2                 1
+#              define  ADDR_SURF_BANK_HEIGHT_4                 2
+#              define  ADDR_SURF_BANK_HEIGHT_8                 3
+#       define MACRO_TILE_ASPECT(x)                            ((x) << 4)
+#              define  ADDR_SURF_MACRO_ASPECT_1                0
+#              define  ADDR_SURF_MACRO_ASPECT_2                1
+#              define  ADDR_SURF_MACRO_ASPECT_4                2
+#              define  ADDR_SURF_MACRO_ASPECT_8                3
+#       define NUM_BANKS(x)                                    ((x) << 6)
+#              define  ADDR_SURF_2_BANK                        0
+#              define  ADDR_SURF_4_BANK                        1
+#              define  ADDR_SURF_8_BANK                        2
+#              define  ADDR_SURF_16_BANK                       3
+
+#define        CB_HW_CONTROL                                   0x9A10
+
+#define        GC_USER_RB_BACKEND_DISABLE                      0x9B7C
+#define                BACKEND_DISABLE_MASK                    0x00FF0000
+#define                BACKEND_DISABLE_SHIFT                   16
+
+#define        TCP_CHAN_STEER_LO                               0xac0c
+#define        TCP_CHAN_STEER_HI                               0xac10
+
+#define        TC_CFG_L1_LOAD_POLICY0                          0xAC68
+#define        TC_CFG_L1_LOAD_POLICY1                          0xAC6C
+#define        TC_CFG_L1_STORE_POLICY                          0xAC70
+#define        TC_CFG_L2_LOAD_POLICY0                          0xAC74
+#define        TC_CFG_L2_LOAD_POLICY1                          0xAC78
+#define        TC_CFG_L2_STORE_POLICY0                         0xAC7C
+#define        TC_CFG_L2_STORE_POLICY1                         0xAC80
+#define        TC_CFG_L2_ATOMIC_POLICY                         0xAC84
+#define        TC_CFG_L1_VOLATILE                              0xAC88
+#define        TC_CFG_L2_VOLATILE                              0xAC8C
+
+#define        CP_RB0_BASE                                     0xC100
+#define        CP_RB0_CNTL                                     0xC104
+#define                RB_BUFSZ(x)                                     ((x) << 0)
+#define                RB_BLKSZ(x)                                     ((x) << 8)
+#define                BUF_SWAP_32BIT                                  (2 << 16)
+#define                RB_NO_UPDATE                                    (1 << 27)
+#define                RB_RPTR_WR_ENA                                  (1 << 31)
+
+#define        CP_RB0_RPTR_ADDR                                0xC10C
+#define                RB_RPTR_SWAP_32BIT                              (2 << 0)
+#define        CP_RB0_RPTR_ADDR_HI                             0xC110
+#define        CP_RB0_WPTR                                     0xC114
+
+#define        CP_DEVICE_ID                                    0xC12C
+#define        CP_ENDIAN_SWAP                                  0xC140
+#define        CP_RB_VMID                                      0xC144
+
+#define        CP_PFP_UCODE_ADDR                               0xC150
+#define        CP_PFP_UCODE_DATA                               0xC154
+#define        CP_ME_RAM_RADDR                                 0xC158
+#define        CP_ME_RAM_WADDR                                 0xC15C
+#define        CP_ME_RAM_DATA                                  0xC160
+
+#define        CP_CE_UCODE_ADDR                                0xC168
+#define        CP_CE_UCODE_DATA                                0xC16C
+#define        CP_MEC_ME1_UCODE_ADDR                           0xC170
+#define        CP_MEC_ME1_UCODE_DATA                           0xC174
+#define        CP_MEC_ME2_UCODE_ADDR                           0xC178
+#define        CP_MEC_ME2_UCODE_DATA                           0xC17C
+
+#define CP_INT_CNTL_RING0                               0xC1A8
+#       define CNTX_BUSY_INT_ENABLE                     (1 << 19)
+#       define CNTX_EMPTY_INT_ENABLE                    (1 << 20)
+#       define PRIV_INSTR_INT_ENABLE                    (1 << 22)
+#       define PRIV_REG_INT_ENABLE                      (1 << 23)
+#       define TIME_STAMP_INT_ENABLE                    (1 << 26)
+#       define CP_RINGID2_INT_ENABLE                    (1 << 29)
+#       define CP_RINGID1_INT_ENABLE                    (1 << 30)
+#       define CP_RINGID0_INT_ENABLE                    (1 << 31)
+
+#define CP_INT_STATUS_RING0                             0xC1B4
+#       define PRIV_INSTR_INT_STAT                      (1 << 22)
+#       define PRIV_REG_INT_STAT                        (1 << 23)
+#       define TIME_STAMP_INT_STAT                      (1 << 26)
+#       define CP_RINGID2_INT_STAT                      (1 << 29)
+#       define CP_RINGID1_INT_STAT                      (1 << 30)
+#       define CP_RINGID0_INT_STAT                      (1 << 31)
+
+#define CP_CPF_DEBUG                                    0xC200
+
+#define CP_PQ_WPTR_POLL_CNTL                            0xC20C
+#define                WPTR_POLL_EN                            (1 << 31)
+
+#define CP_ME1_PIPE0_INT_CNTL                           0xC214
+#define CP_ME1_PIPE1_INT_CNTL                           0xC218
+#define CP_ME1_PIPE2_INT_CNTL                           0xC21C
+#define CP_ME1_PIPE3_INT_CNTL                           0xC220
+#define CP_ME2_PIPE0_INT_CNTL                           0xC224
+#define CP_ME2_PIPE1_INT_CNTL                           0xC228
+#define CP_ME2_PIPE2_INT_CNTL                           0xC22C
+#define CP_ME2_PIPE3_INT_CNTL                           0xC230
+#       define DEQUEUE_REQUEST_INT_ENABLE               (1 << 13)
+#       define WRM_POLL_TIMEOUT_INT_ENABLE              (1 << 17)
+#       define PRIV_REG_INT_ENABLE                      (1 << 23)
+#       define TIME_STAMP_INT_ENABLE                    (1 << 26)
+#       define GENERIC2_INT_ENABLE                      (1 << 29)
+#       define GENERIC1_INT_ENABLE                      (1 << 30)
+#       define GENERIC0_INT_ENABLE                      (1 << 31)
+#define CP_ME1_PIPE0_INT_STATUS                         0xC214
+#define CP_ME1_PIPE1_INT_STATUS                         0xC218
+#define CP_ME1_PIPE2_INT_STATUS                         0xC21C
+#define CP_ME1_PIPE3_INT_STATUS                         0xC220
+#define CP_ME2_PIPE0_INT_STATUS                         0xC224
+#define CP_ME2_PIPE1_INT_STATUS                         0xC228
+#define CP_ME2_PIPE2_INT_STATUS                         0xC22C
+#define CP_ME2_PIPE3_INT_STATUS                         0xC230
+#       define DEQUEUE_REQUEST_INT_STATUS               (1 << 13)
+#       define WRM_POLL_TIMEOUT_INT_STATUS              (1 << 17)
+#       define PRIV_REG_INT_STATUS                      (1 << 23)
+#       define TIME_STAMP_INT_STATUS                    (1 << 26)
+#       define GENERIC2_INT_STATUS                      (1 << 29)
+#       define GENERIC1_INT_STATUS                      (1 << 30)
+#       define GENERIC0_INT_STATUS                      (1 << 31)
+
+#define        CP_MAX_CONTEXT                                  0xC2B8
+
+#define        CP_RB0_BASE_HI                                  0xC2C4
+
+#define RLC_CNTL                                          0xC300
+#       define RLC_ENABLE                                 (1 << 0)
+
+#define RLC_MC_CNTL                                       0xC30C
+
+#define RLC_LB_CNTR_MAX                                   0xC348
+
+#define RLC_LB_CNTL                                       0xC364
+
+#define RLC_LB_CNTR_INIT                                  0xC36C
+
+#define RLC_SAVE_AND_RESTORE_BASE                         0xC374
+#define RLC_DRIVER_DMA_STATUS                             0xC378
+
+#define RLC_GPM_UCODE_ADDR                                0xC388
+#define RLC_GPM_UCODE_DATA                                0xC38C
+#define RLC_GPU_CLOCK_COUNT_LSB                           0xC390
+#define RLC_GPU_CLOCK_COUNT_MSB                           0xC394
+#define RLC_CAPTURE_GPU_CLOCK_COUNT                       0xC398
+#define RLC_UCODE_CNTL                                    0xC39C
+
+#define RLC_CGCG_CGLS_CTRL                                0xC424
+
+#define RLC_LB_INIT_CU_MASK                               0xC43C
+
+#define RLC_LB_PARAMS                                     0xC444
+
+#define RLC_SERDES_CU_MASTER_BUSY                         0xC484
+#define RLC_SERDES_NONCU_MASTER_BUSY                      0xC488
+#       define SE_MASTER_BUSY_MASK                        0x0000ffff
+#       define GC_MASTER_BUSY                             (1 << 16)
+#       define TC0_MASTER_BUSY                            (1 << 17)
+#       define TC1_MASTER_BUSY                            (1 << 18)
+
+#define RLC_GPM_SCRATCH_ADDR                              0xC4B0
+#define RLC_GPM_SCRATCH_DATA                              0xC4B4
+
+#define CP_HPD_EOP_BASE_ADDR                              0xC904
+#define CP_HPD_EOP_BASE_ADDR_HI                           0xC908
+#define CP_HPD_EOP_VMID                                   0xC90C
+#define CP_HPD_EOP_CONTROL                                0xC910
+#define                EOP_SIZE(x)                             ((x) << 0)
+#define                EOP_SIZE_MASK                           (0x3f << 0)
+#define CP_MQD_BASE_ADDR                                  0xC914
+#define CP_MQD_BASE_ADDR_HI                               0xC918
+#define CP_HQD_ACTIVE                                     0xC91C
+#define CP_HQD_VMID                                       0xC920
+
+#define CP_HQD_PQ_BASE                                    0xC934
+#define CP_HQD_PQ_BASE_HI                                 0xC938
+#define CP_HQD_PQ_RPTR                                    0xC93C
+#define CP_HQD_PQ_RPTR_REPORT_ADDR                        0xC940
+#define CP_HQD_PQ_RPTR_REPORT_ADDR_HI                     0xC944
+#define CP_HQD_PQ_WPTR_POLL_ADDR                          0xC948
+#define CP_HQD_PQ_WPTR_POLL_ADDR_HI                       0xC94C
+#define CP_HQD_PQ_DOORBELL_CONTROL                        0xC950
+#define                DOORBELL_OFFSET(x)                      ((x) << 2)
+#define                DOORBELL_OFFSET_MASK                    (0x1fffff << 2)
+#define                DOORBELL_SOURCE                         (1 << 28)
+#define                DOORBELL_SCHD_HIT                       (1 << 29)
+#define                DOORBELL_EN                             (1 << 30)
+#define                DOORBELL_HIT                            (1 << 31)
+#define CP_HQD_PQ_WPTR                                    0xC954
+#define CP_HQD_PQ_CONTROL                                 0xC958
+#define                QUEUE_SIZE(x)                           ((x) << 0)
+#define                QUEUE_SIZE_MASK                         (0x3f << 0)
+#define                RPTR_BLOCK_SIZE(x)                      ((x) << 8)
+#define                RPTR_BLOCK_SIZE_MASK                    (0x3f << 8)
+#define                PQ_VOLATILE                             (1 << 26)
+#define                NO_UPDATE_RPTR                          (1 << 27)
+#define                UNORD_DISPATCH                          (1 << 28)
+#define                ROQ_PQ_IB_FLIP                          (1 << 29)
+#define                PRIV_STATE                              (1 << 30)
+#define                KMD_QUEUE                               (1 << 31)
+
+#define CP_HQD_DEQUEUE_REQUEST                          0xC974
+
+#define CP_MQD_CONTROL                                  0xC99C
+#define                MQD_VMID(x)                             ((x) << 0)
+#define                MQD_VMID_MASK                           (0xf << 0)
+
+#define PA_SC_RASTER_CONFIG                             0x28350
+#       define RASTER_CONFIG_RB_MAP_0                   0
+#       define RASTER_CONFIG_RB_MAP_1                   1
+#       define RASTER_CONFIG_RB_MAP_2                   2
+#       define RASTER_CONFIG_RB_MAP_3                   3
+
+#define VGT_EVENT_INITIATOR                             0x28a90
+#       define SAMPLE_STREAMOUTSTATS1                   (1 << 0)
+#       define SAMPLE_STREAMOUTSTATS2                   (2 << 0)
+#       define SAMPLE_STREAMOUTSTATS3                   (3 << 0)
+#       define CACHE_FLUSH_TS                           (4 << 0)
+#       define CACHE_FLUSH                              (6 << 0)
+#       define CS_PARTIAL_FLUSH                         (7 << 0)
+#       define VGT_STREAMOUT_RESET                      (10 << 0)
+#       define END_OF_PIPE_INCR_DE                      (11 << 0)
+#       define END_OF_PIPE_IB_END                       (12 << 0)
+#       define RST_PIX_CNT                              (13 << 0)
+#       define VS_PARTIAL_FLUSH                         (15 << 0)
+#       define PS_PARTIAL_FLUSH                         (16 << 0)
+#       define CACHE_FLUSH_AND_INV_TS_EVENT             (20 << 0)
+#       define ZPASS_DONE                               (21 << 0)
+#       define CACHE_FLUSH_AND_INV_EVENT                (22 << 0)
+#       define PERFCOUNTER_START                        (23 << 0)
+#       define PERFCOUNTER_STOP                         (24 << 0)
+#       define PIPELINESTAT_START                       (25 << 0)
+#       define PIPELINESTAT_STOP                        (26 << 0)
+#       define PERFCOUNTER_SAMPLE                       (27 << 0)
+#       define SAMPLE_PIPELINESTAT                      (30 << 0)
+#       define SO_VGT_STREAMOUT_FLUSH                   (31 << 0)
+#       define SAMPLE_STREAMOUTSTATS                    (32 << 0)
+#       define RESET_VTX_CNT                            (33 << 0)
+#       define VGT_FLUSH                                (36 << 0)
+#       define BOTTOM_OF_PIPE_TS                        (40 << 0)
+#       define DB_CACHE_FLUSH_AND_INV                   (42 << 0)
+#       define FLUSH_AND_INV_DB_DATA_TS                 (43 << 0)
+#       define FLUSH_AND_INV_DB_META                    (44 << 0)
+#       define FLUSH_AND_INV_CB_DATA_TS                 (45 << 0)
+#       define FLUSH_AND_INV_CB_META                    (46 << 0)
+#       define CS_DONE                                  (47 << 0)
+#       define PS_DONE                                  (48 << 0)
+#       define FLUSH_AND_INV_CB_PIXEL_DATA              (49 << 0)
+#       define THREAD_TRACE_START                       (51 << 0)
+#       define THREAD_TRACE_STOP                        (52 << 0)
+#       define THREAD_TRACE_FLUSH                       (54 << 0)
+#       define THREAD_TRACE_FINISH                      (55 << 0)
+#       define PIXEL_PIPE_STAT_CONTROL                  (56 << 0)
+#       define PIXEL_PIPE_STAT_DUMP                     (57 << 0)
+#       define PIXEL_PIPE_STAT_RESET                    (58 << 0)
+
+#define        SCRATCH_REG0                                    0x30100
+#define        SCRATCH_REG1                                    0x30104
+#define        SCRATCH_REG2                                    0x30108
+#define        SCRATCH_REG3                                    0x3010C
+#define        SCRATCH_REG4                                    0x30110
+#define        SCRATCH_REG5                                    0x30114
+#define        SCRATCH_REG6                                    0x30118
+#define        SCRATCH_REG7                                    0x3011C
+
+#define        SCRATCH_UMSK                                    0x30140
+#define        SCRATCH_ADDR                                    0x30144
+
+#define        CP_SEM_WAIT_TIMER                               0x301BC
+
+#define        CP_SEM_INCOMPLETE_TIMER_CNTL                    0x301C8
+
+#define        CP_WAIT_REG_MEM_TIMEOUT                         0x301D0
+
+#define GRBM_GFX_INDEX                                 0x30800
+#define                INSTANCE_INDEX(x)                       ((x) << 0)
+#define                SH_INDEX(x)                             ((x) << 8)
+#define                SE_INDEX(x)                             ((x) << 16)
+#define                SH_BROADCAST_WRITES                     (1 << 29)
+#define                INSTANCE_BROADCAST_WRITES               (1 << 30)
+#define                SE_BROADCAST_WRITES                     (1 << 31)
+
+#define        VGT_ESGS_RING_SIZE                              0x30900
+#define        VGT_GSVS_RING_SIZE                              0x30904
+#define        VGT_PRIMITIVE_TYPE                              0x30908
+#define        VGT_INDEX_TYPE                                  0x3090C
+
+#define        VGT_NUM_INDICES                                 0x30930
+#define        VGT_NUM_INSTANCES                               0x30934
+#define        VGT_TF_RING_SIZE                                0x30938
+#define        VGT_HS_OFFCHIP_PARAM                            0x3093C
+#define        VGT_TF_MEMORY_BASE                              0x30940
+
+#define        PA_SU_LINE_STIPPLE_VALUE                        0x30a00
+#define        PA_SC_LINE_STIPPLE_STATE                        0x30a04
+
+#define        SQC_CACHES                                      0x30d20
+
+#define        CP_PERFMON_CNTL                                 0x36020
+
+#define        CGTS_TCC_DISABLE                                0x3c00c
+#define        CGTS_USER_TCC_DISABLE                           0x3c010
+#define                TCC_DISABLE_MASK                                0xFFFF0000
+#define                TCC_DISABLE_SHIFT                               16
+
+#define        CB_CGTT_SCLK_CTRL                               0x3c2a0
+
+/*
+ * PM4
+ */
+#define        PACKET_TYPE0    0
+#define        PACKET_TYPE1    1
+#define        PACKET_TYPE2    2
+#define        PACKET_TYPE3    3
+
+#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3)
+#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF)
+#define CP_PACKET0_GET_REG(h) (((h) & 0xFFFF) << 2)
+#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF)
+#define PACKET0(reg, n)        ((PACKET_TYPE0 << 30) |                         \
+                        (((reg) >> 2) & 0xFFFF) |                      \
+                        ((n) & 0x3FFF) << 16)
+#define CP_PACKET2                     0x80000000
+#define                PACKET2_PAD_SHIFT               0
+#define                PACKET2_PAD_MASK                (0x3fffffff << 0)
+
+#define PACKET2(v)     (CP_PACKET2 | REG_SET(PACKET2_PAD, (v)))
+
+#define PACKET3(op, n) ((PACKET_TYPE3 << 30) |                         \
+                        (((op) & 0xFF) << 8) |                         \
+                        ((n) & 0x3FFF) << 16)
+
+#define PACKET3_COMPUTE(op, n) (PACKET3(op, n) | 1 << 1)
+
+/* Packet 3 types */
+#define        PACKET3_NOP                                     0x10
+#define        PACKET3_SET_BASE                                0x11
+#define                PACKET3_BASE_INDEX(x)                  ((x) << 0)
+#define                        CE_PARTITION_BASE               3
+#define        PACKET3_CLEAR_STATE                             0x12
+#define        PACKET3_INDEX_BUFFER_SIZE                       0x13
+#define        PACKET3_DISPATCH_DIRECT                         0x15
+#define        PACKET3_DISPATCH_INDIRECT                       0x16
+#define        PACKET3_ATOMIC_GDS                              0x1D
+#define        PACKET3_ATOMIC_MEM                              0x1E
+#define        PACKET3_OCCLUSION_QUERY                         0x1F
+#define        PACKET3_SET_PREDICATION                         0x20
+#define        PACKET3_REG_RMW                                 0x21
+#define        PACKET3_COND_EXEC                               0x22
+#define        PACKET3_PRED_EXEC                               0x23
+#define        PACKET3_DRAW_INDIRECT                           0x24
+#define        PACKET3_DRAW_INDEX_INDIRECT                     0x25
+#define        PACKET3_INDEX_BASE                              0x26
+#define        PACKET3_DRAW_INDEX_2                            0x27
+#define        PACKET3_CONTEXT_CONTROL                         0x28
+#define        PACKET3_INDEX_TYPE                              0x2A
+#define        PACKET3_DRAW_INDIRECT_MULTI                     0x2C
+#define        PACKET3_DRAW_INDEX_AUTO                         0x2D
+#define        PACKET3_NUM_INSTANCES                           0x2F
+#define        PACKET3_DRAW_INDEX_MULTI_AUTO                   0x30
+#define        PACKET3_INDIRECT_BUFFER_CONST                   0x33
+#define        PACKET3_STRMOUT_BUFFER_UPDATE                   0x34
+#define        PACKET3_DRAW_INDEX_OFFSET_2                     0x35
+#define        PACKET3_DRAW_PREAMBLE                           0x36
+#define        PACKET3_WRITE_DATA                              0x37
+#define                WRITE_DATA_DST_SEL(x)                   ((x) << 8)
+                /* 0 - register
+                * 1 - memory (sync - via GRBM)
+                * 2 - gl2
+                * 3 - gds
+                * 4 - reserved
+                * 5 - memory (async - direct)
+                */
+#define                WR_ONE_ADDR                             (1 << 16)
+#define                WR_CONFIRM                              (1 << 20)
+#define                WRITE_DATA_CACHE_POLICY(x)              ((x) << 25)
+                /* 0 - LRU
+                * 1 - Stream
+                */
+#define                WRITE_DATA_ENGINE_SEL(x)                ((x) << 30)
+                /* 0 - me
+                * 1 - pfp
+                * 2 - ce
+                */
+#define        PACKET3_DRAW_INDEX_INDIRECT_MULTI               0x38
+#define        PACKET3_MEM_SEMAPHORE                           0x39
+#              define PACKET3_SEM_USE_MAILBOX       (0x1 << 16)
+#              define PACKET3_SEM_SEL_SIGNAL_TYPE   (0x1 << 20) /* 0 = increment, 1 = write 1 */
+#              define PACKET3_SEM_CLIENT_CODE      ((x) << 24) /* 0 = CP, 1 = CB, 2 = DB */
+#              define PACKET3_SEM_SEL_SIGNAL       (0x6 << 29)
+#              define PACKET3_SEM_SEL_WAIT         (0x7 << 29)
+#define        PACKET3_COPY_DW                                 0x3B
+#define        PACKET3_WAIT_REG_MEM                            0x3C
+#define                WAIT_REG_MEM_FUNCTION(x)                ((x) << 0)
+                /* 0 - always
+                * 1 - <
+                * 2 - <=
+                * 3 - ==
+                * 4 - !=
+                * 5 - >=
+                * 6 - >
+                */
+#define                WAIT_REG_MEM_MEM_SPACE(x)               ((x) << 4)
+                /* 0 - reg
+                * 1 - mem
+                */
+#define                WAIT_REG_MEM_OPERATION(x)               ((x) << 6)
+                /* 0 - wait_reg_mem
+                * 1 - wr_wait_wr_reg
+                */
+#define                WAIT_REG_MEM_ENGINE(x)                  ((x) << 8)
+                /* 0 - me
+                * 1 - pfp
+                */
+#define        PACKET3_INDIRECT_BUFFER                         0x3F
+#define                INDIRECT_BUFFER_TCL2_VOLATILE           (1 << 22)
+#define                INDIRECT_BUFFER_VALID                   (1 << 23)
+#define                INDIRECT_BUFFER_CACHE_POLICY(x)         ((x) << 28)
+                /* 0 - LRU
+                * 1 - Stream
+                * 2 - Bypass
+                */
+#define        PACKET3_COPY_DATA                               0x40
+#define        PACKET3_PFP_SYNC_ME                             0x42
+#define        PACKET3_SURFACE_SYNC                            0x43
+#              define PACKET3_DEST_BASE_0_ENA      (1 << 0)
+#              define PACKET3_DEST_BASE_1_ENA      (1 << 1)
+#              define PACKET3_CB0_DEST_BASE_ENA    (1 << 6)
+#              define PACKET3_CB1_DEST_BASE_ENA    (1 << 7)
+#              define PACKET3_CB2_DEST_BASE_ENA    (1 << 8)
+#              define PACKET3_CB3_DEST_BASE_ENA    (1 << 9)
+#              define PACKET3_CB4_DEST_BASE_ENA    (1 << 10)
+#              define PACKET3_CB5_DEST_BASE_ENA    (1 << 11)
+#              define PACKET3_CB6_DEST_BASE_ENA    (1 << 12)
+#              define PACKET3_CB7_DEST_BASE_ENA    (1 << 13)
+#              define PACKET3_DB_DEST_BASE_ENA     (1 << 14)
+#              define PACKET3_TCL1_VOL_ACTION_ENA  (1 << 15)
+#              define PACKET3_TC_VOL_ACTION_ENA    (1 << 16) /* L2 */
+#              define PACKET3_TC_WB_ACTION_ENA     (1 << 18) /* L2 */
+#              define PACKET3_DEST_BASE_2_ENA      (1 << 19)
+#              define PACKET3_DEST_BASE_3_ENA      (1 << 21)
+#              define PACKET3_TCL1_ACTION_ENA      (1 << 22)
+#              define PACKET3_TC_ACTION_ENA        (1 << 23) /* L2 */
+#              define PACKET3_CB_ACTION_ENA        (1 << 25)
+#              define PACKET3_DB_ACTION_ENA        (1 << 26)
+#              define PACKET3_SH_KCACHE_ACTION_ENA (1 << 27)
+#              define PACKET3_SH_KCACHE_VOL_ACTION_ENA (1 << 28)
+#              define PACKET3_SH_ICACHE_ACTION_ENA (1 << 29)
+#define        PACKET3_COND_WRITE                              0x45
+#define        PACKET3_EVENT_WRITE                             0x46
+#define                EVENT_TYPE(x)                           ((x) << 0)
+#define                EVENT_INDEX(x)                          ((x) << 8)
+                /* 0 - any non-TS event
+                * 1 - ZPASS_DONE, PIXEL_PIPE_STAT_*
+                * 2 - SAMPLE_PIPELINESTAT
+                * 3 - SAMPLE_STREAMOUTSTAT*
+                * 4 - *S_PARTIAL_FLUSH
+                * 5 - EOP events
+                * 6 - EOS events
+                */
+#define        PACKET3_EVENT_WRITE_EOP                         0x47
+#define                EOP_TCL1_VOL_ACTION_EN                  (1 << 12)
+#define                EOP_TC_VOL_ACTION_EN                    (1 << 13) /* L2 */
+#define                EOP_TC_WB_ACTION_EN                     (1 << 15) /* L2 */
+#define                EOP_TCL1_ACTION_EN                      (1 << 16)
+#define                EOP_TC_ACTION_EN                        (1 << 17) /* L2 */
+#define                EOP_CACHE_POLICY(x)                     ((x) << 25)
+                /* 0 - LRU
+                * 1 - Stream
+                * 2 - Bypass
+                */
+#define                EOP_TCL2_VOLATILE                       (1 << 27)
+#define                DATA_SEL(x)                             ((x) << 29)
+                /* 0 - discard
+                * 1 - send low 32bit data
+                * 2 - send 64bit data
+                * 3 - send 64bit GPU counter value
+                * 4 - send 64bit sys counter value
+                */
+#define                INT_SEL(x)                              ((x) << 24)
+                /* 0 - none
+                * 1 - interrupt only (DATA_SEL = 0)
+                * 2 - interrupt when data write is confirmed
+                */
+#define                DST_SEL(x)                              ((x) << 16)
+                /* 0 - MC
+                * 1 - TC/L2
+                */
+#define        PACKET3_EVENT_WRITE_EOS                         0x48
+#define        PACKET3_RELEASE_MEM                             0x49
+#define        PACKET3_PREAMBLE_CNTL                           0x4A
+#              define PACKET3_PREAMBLE_BEGIN_CLEAR_STATE     (2 << 28)
+#              define PACKET3_PREAMBLE_END_CLEAR_STATE       (3 << 28)
+#define        PACKET3_DMA_DATA                                0x50
+#define        PACKET3_AQUIRE_MEM                              0x58
+#define        PACKET3_REWIND                                  0x59
+#define        PACKET3_LOAD_UCONFIG_REG                        0x5E
+#define        PACKET3_LOAD_SH_REG                             0x5F
+#define        PACKET3_LOAD_CONFIG_REG                         0x60
+#define        PACKET3_LOAD_CONTEXT_REG                        0x61
+#define        PACKET3_SET_CONFIG_REG                          0x68
+#define                PACKET3_SET_CONFIG_REG_START                    0x00008000
+#define                PACKET3_SET_CONFIG_REG_END                      0x0000b000
+#define        PACKET3_SET_CONTEXT_REG                         0x69
+#define                PACKET3_SET_CONTEXT_REG_START                   0x00028000
+#define                PACKET3_SET_CONTEXT_REG_END                     0x00029000
+#define        PACKET3_SET_CONTEXT_REG_INDIRECT                0x73
+#define        PACKET3_SET_SH_REG                              0x76
+#define                PACKET3_SET_SH_REG_START                        0x0000b000
+#define                PACKET3_SET_SH_REG_END                          0x0000c000
+#define        PACKET3_SET_SH_REG_OFFSET                       0x77
+#define        PACKET3_SET_QUEUE_REG                           0x78
+#define        PACKET3_SET_UCONFIG_REG                         0x79
+#define                PACKET3_SET_UCONFIG_REG_START                   0x00030000
+#define                PACKET3_SET_UCONFIG_REG_END                     0x00031000
+#define        PACKET3_SCRATCH_RAM_WRITE                       0x7D
+#define        PACKET3_SCRATCH_RAM_READ                        0x7E
+#define        PACKET3_LOAD_CONST_RAM                          0x80
+#define        PACKET3_WRITE_CONST_RAM                         0x81
+#define        PACKET3_DUMP_CONST_RAM                          0x83
+#define        PACKET3_INCREMENT_CE_COUNTER                    0x84
+#define        PACKET3_INCREMENT_DE_COUNTER                    0x85
+#define        PACKET3_WAIT_ON_CE_COUNTER                      0x86
+#define        PACKET3_WAIT_ON_DE_COUNTER_DIFF                 0x88
+#define        PACKET3_SWITCH_BUFFER                           0x8B
+
+/* SDMA - first instance at 0xd000, second at 0xd800 */
+#define SDMA0_REGISTER_OFFSET                             0x0 /* not a register */
+#define SDMA1_REGISTER_OFFSET                             0x800 /* not a register */
+
+#define        SDMA0_UCODE_ADDR                                  0xD000
+#define        SDMA0_UCODE_DATA                                  0xD004
+
+#define SDMA0_CNTL                                        0xD010
+#       define TRAP_ENABLE                                (1 << 0)
+#       define SEM_INCOMPLETE_INT_ENABLE                  (1 << 1)
+#       define SEM_WAIT_INT_ENABLE                        (1 << 2)
+#       define DATA_SWAP_ENABLE                           (1 << 3)
+#       define FENCE_SWAP_ENABLE                          (1 << 4)
+#       define AUTO_CTXSW_ENABLE                          (1 << 18)
+#       define CTXEMPTY_INT_ENABLE                        (1 << 28)
+
+#define SDMA0_TILING_CONFIG                              0xD018
+
+#define SDMA0_SEM_INCOMPLETE_TIMER_CNTL                   0xD020
+#define SDMA0_SEM_WAIT_FAIL_TIMER_CNTL                    0xD024
+
+#define SDMA0_STATUS_REG                                  0xd034
+#       define SDMA_IDLE                                  (1 << 0)
+
+#define SDMA0_ME_CNTL                                     0xD048
+#       define SDMA_HALT                                  (1 << 0)
+
+#define SDMA0_GFX_RB_CNTL                                 0xD200
+#       define SDMA_RB_ENABLE                             (1 << 0)
+#       define SDMA_RB_SIZE(x)                            ((x) << 1) /* log2 */
+#       define SDMA_RB_SWAP_ENABLE                        (1 << 9) /* 8IN32 */
+#       define SDMA_RPTR_WRITEBACK_ENABLE                 (1 << 12)
+#       define SDMA_RPTR_WRITEBACK_SWAP_ENABLE            (1 << 13)  /* 8IN32 */
+#       define SDMA_RPTR_WRITEBACK_TIMER(x)               ((x) << 16) /* log2 */
+#define SDMA0_GFX_RB_BASE                                 0xD204
+#define SDMA0_GFX_RB_BASE_HI                              0xD208
+#define SDMA0_GFX_RB_RPTR                                 0xD20C
+#define SDMA0_GFX_RB_WPTR                                 0xD210
+
+#define SDMA0_GFX_RB_RPTR_ADDR_HI                         0xD220
+#define SDMA0_GFX_RB_RPTR_ADDR_LO                         0xD224
+#define SDMA0_GFX_IB_CNTL                                 0xD228
+#       define SDMA_IB_ENABLE                             (1 << 0)
+#       define SDMA_IB_SWAP_ENABLE                        (1 << 4)
+#       define SDMA_SWITCH_INSIDE_IB                      (1 << 8)
+#       define SDMA_CMD_VMID(x)                           ((x) << 16)
+
+#define SDMA0_GFX_VIRTUAL_ADDR                            0xD29C
+#define SDMA0_GFX_APE1_CNTL                               0xD2A0
+
+#define SDMA_PACKET(op, sub_op, e)     ((((e) & 0xFFFF) << 16) |       \
+                                        (((sub_op) & 0xFF) << 8) |     \
+                                        (((op) & 0xFF) << 0))
+/* sDMA opcodes */
+#define        SDMA_OPCODE_NOP                                   0
+#define        SDMA_OPCODE_COPY                                  1
+#       define SDMA_COPY_SUB_OPCODE_LINEAR                0
+#       define SDMA_COPY_SUB_OPCODE_TILED                 1
+#       define SDMA_COPY_SUB_OPCODE_SOA                   3
+#       define SDMA_COPY_SUB_OPCODE_LINEAR_SUB_WINDOW     4
+#       define SDMA_COPY_SUB_OPCODE_TILED_SUB_WINDOW      5
+#       define SDMA_COPY_SUB_OPCODE_T2T_SUB_WINDOW        6
+#define        SDMA_OPCODE_WRITE                                 2
+#       define SDMA_WRITE_SUB_OPCODE_LINEAR               0
+#       define SDMA_WRTIE_SUB_OPCODE_TILED                1
+#define        SDMA_OPCODE_INDIRECT_BUFFER                       4
+#define        SDMA_OPCODE_FENCE                                 5
+#define        SDMA_OPCODE_TRAP                                  6
+#define        SDMA_OPCODE_SEMAPHORE                             7
+#       define SDMA_SEMAPHORE_EXTRA_O                     (1 << 13)
+                /* 0 - increment
+                * 1 - write 1
+                */
+#       define SDMA_SEMAPHORE_EXTRA_S                     (1 << 14)
+                /* 0 - wait
+                * 1 - signal
+                */
+#       define SDMA_SEMAPHORE_EXTRA_M                     (1 << 15)
+                /* mailbox */
+#define        SDMA_OPCODE_POLL_REG_MEM                          8
+#       define SDMA_POLL_REG_MEM_EXTRA_OP(x)              ((x) << 10)
+                /* 0 - wait_reg_mem
+                * 1 - wr_wait_wr_reg
+                */
+#       define SDMA_POLL_REG_MEM_EXTRA_FUNC(x)            ((x) << 12)
+                /* 0 - always
+                * 1 - <
+                * 2 - <=
+                * 3 - ==
+                * 4 - !=
+                * 5 - >=
+                * 6 - >
+                */
+#       define SDMA_POLL_REG_MEM_EXTRA_M                  (1 << 15)
+                /* 0 = register
+                * 1 = memory
+                */
+#define        SDMA_OPCODE_COND_EXEC                             9
+#define        SDMA_OPCODE_CONSTANT_FILL                         11
+#       define SDMA_CONSTANT_FILL_EXTRA_SIZE(x)           ((x) << 14)
+                /* 0 = byte fill
+                * 2 = DW fill
+                */
+#define        SDMA_OPCODE_GENERATE_PTE_PDE                      12
+#define        SDMA_OPCODE_TIMESTAMP                             13
+#       define SDMA_TIMESTAMP_SUB_OPCODE_SET_LOCAL        0
+#       define SDMA_TIMESTAMP_SUB_OPCODE_GET_LOCAL        1
+#       define SDMA_TIMESTAMP_SUB_OPCODE_GET_GLOBAL       2
+#define        SDMA_OPCODE_SRBM_WRITE                            14
+#       define SDMA_SRBM_WRITE_EXTRA_BYTE_ENABLE(x)       ((x) << 12)
+                /* byte mask */
+
+/* UVD */
+
+#define UVD_UDEC_ADDR_CONFIG           0xef4c
+#define UVD_UDEC_DB_ADDR_CONFIG                0xef50
+#define UVD_UDEC_DBW_ADDR_CONFIG       0xef54
+
+#define UVD_LMI_EXT40_ADDR             0xf498
+#define UVD_LMI_ADDR_EXT               0xf594
+#define UVD_VCPU_CACHE_OFFSET0         0xf608
+#define UVD_VCPU_CACHE_SIZE0           0xf60c
+#define UVD_VCPU_CACHE_OFFSET1         0xf610
+#define UVD_VCPU_CACHE_SIZE1           0xf614
+#define UVD_VCPU_CACHE_OFFSET2         0xf618
+#define UVD_VCPU_CACHE_SIZE2           0xf61c
+
+#define UVD_RBC_RB_RPTR                        0xf690
+#define UVD_RBC_RB_WPTR                        0xf694
+
+/* UVD clocks */
+
+#define CG_DCLK_CNTL                   0xC050009C
+#      define DCLK_DIVIDER_MASK        0x7f
+#      define DCLK_DIR_CNTL_EN         (1 << 8)
+#define CG_DCLK_STATUS                 0xC05000A0
+#      define DCLK_STATUS              (1 << 0)
+#define CG_VCLK_CNTL                   0xC05000A4
+#define CG_VCLK_STATUS                 0xC05000A8
+
+#endif
diff --git a/drivers/gpu/drm/radeon/clearstate_cayman.h b/drivers/gpu/drm/radeon/clearstate_cayman.h
new file mode 100644 (file)
index 0000000..c003394
--- /dev/null
@@ -0,0 +1,1081 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+
+static const u32 SECT_CONTEXT_def_1[] =
+{
+    0x00000000, // DB_RENDER_CONTROL
+    0x00000000, // DB_COUNT_CONTROL
+    0x00000000, // DB_DEPTH_VIEW
+    0x00000000, // DB_RENDER_OVERRIDE
+    0x00000000, // DB_RENDER_OVERRIDE2
+    0x00000000, // DB_HTILE_DATA_BASE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // DB_STENCIL_CLEAR
+    0x00000000, // DB_DEPTH_CLEAR
+    0x00000000, // PA_SC_SCREEN_SCISSOR_TL
+    0x40004000, // PA_SC_SCREEN_SCISSOR_BR
+    0, // HOLE
+    0x00000000, // DB_DEPTH_INFO
+    0x00000000, // DB_Z_INFO
+    0x00000000, // DB_STENCIL_INFO
+    0x00000000, // DB_Z_READ_BASE
+    0x00000000, // DB_STENCIL_READ_BASE
+    0x00000000, // DB_Z_WRITE_BASE
+    0x00000000, // DB_STENCIL_WRITE_BASE
+    0x00000000, // DB_DEPTH_SIZE
+    0x00000000, // DB_DEPTH_SLICE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_0
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_1
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_2
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_3
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_4
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_5
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_6
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_7
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_8
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_9
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_10
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_11
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_12
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_13
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_14
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_15
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_0
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_1
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_2
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_3
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_4
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_5
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_6
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_7
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_8
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_9
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_10
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_11
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_12
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_13
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_14
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_15
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_0
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_1
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_2
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_3
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_4
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_5
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_6
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_7
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_8
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_9
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_10
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_11
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_12
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_13
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_14
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_15
+    0x00000000, // PA_SC_WINDOW_OFFSET
+    0x80000000, // PA_SC_WINDOW_SCISSOR_TL
+    0x40004000, // PA_SC_WINDOW_SCISSOR_BR
+    0x0000ffff, // PA_SC_CLIPRECT_RULE
+    0x00000000, // PA_SC_CLIPRECT_0_TL
+    0x40004000, // PA_SC_CLIPRECT_0_BR
+    0x00000000, // PA_SC_CLIPRECT_1_TL
+    0x40004000, // PA_SC_CLIPRECT_1_BR
+    0x00000000, // PA_SC_CLIPRECT_2_TL
+    0x40004000, // PA_SC_CLIPRECT_2_BR
+    0x00000000, // PA_SC_CLIPRECT_3_TL
+    0x40004000, // PA_SC_CLIPRECT_3_BR
+    0xaa99aaaa, // PA_SC_EDGERULE
+    0x00000000, // PA_SU_HARDWARE_SCREEN_OFFSET
+    0xffffffff, // CB_TARGET_MASK
+    0xffffffff, // CB_SHADER_MASK
+    0x80000000, // PA_SC_GENERIC_SCISSOR_TL
+    0x40004000, // PA_SC_GENERIC_SCISSOR_BR
+    0x00000000, // COHER_DEST_BASE_0
+    0x00000000, // COHER_DEST_BASE_1
+    0x80000000, // PA_SC_VPORT_SCISSOR_0_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_0_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_1_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_1_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_2_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_2_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_3_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_3_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_4_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_4_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_5_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_5_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_6_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_6_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_7_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_7_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_8_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_8_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_9_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_9_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_10_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_10_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_11_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_11_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_12_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_12_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_13_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_13_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_14_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_14_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_15_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_15_BR
+    0x00000000, // PA_SC_VPORT_ZMIN_0
+    0x3f800000, // PA_SC_VPORT_ZMAX_0
+    0x00000000, // PA_SC_VPORT_ZMIN_1
+    0x3f800000, // PA_SC_VPORT_ZMAX_1
+    0x00000000, // PA_SC_VPORT_ZMIN_2
+    0x3f800000, // PA_SC_VPORT_ZMAX_2
+    0x00000000, // PA_SC_VPORT_ZMIN_3
+    0x3f800000, // PA_SC_VPORT_ZMAX_3
+    0x00000000, // PA_SC_VPORT_ZMIN_4
+    0x3f800000, // PA_SC_VPORT_ZMAX_4
+    0x00000000, // PA_SC_VPORT_ZMIN_5
+    0x3f800000, // PA_SC_VPORT_ZMAX_5
+    0x00000000, // PA_SC_VPORT_ZMIN_6
+    0x3f800000, // PA_SC_VPORT_ZMAX_6
+    0x00000000, // PA_SC_VPORT_ZMIN_7
+    0x3f800000, // PA_SC_VPORT_ZMAX_7
+    0x00000000, // PA_SC_VPORT_ZMIN_8
+    0x3f800000, // PA_SC_VPORT_ZMAX_8
+    0x00000000, // PA_SC_VPORT_ZMIN_9
+    0x3f800000, // PA_SC_VPORT_ZMAX_9
+    0x00000000, // PA_SC_VPORT_ZMIN_10
+    0x3f800000, // PA_SC_VPORT_ZMAX_10
+    0x00000000, // PA_SC_VPORT_ZMIN_11
+    0x3f800000, // PA_SC_VPORT_ZMAX_11
+    0x00000000, // PA_SC_VPORT_ZMIN_12
+    0x3f800000, // PA_SC_VPORT_ZMAX_12
+    0x00000000, // PA_SC_VPORT_ZMIN_13
+    0x3f800000, // PA_SC_VPORT_ZMAX_13
+    0x00000000, // PA_SC_VPORT_ZMIN_14
+    0x3f800000, // PA_SC_VPORT_ZMAX_14
+    0x00000000, // PA_SC_VPORT_ZMIN_15
+    0x3f800000, // PA_SC_VPORT_ZMAX_15
+    0x00000000, // SX_MISC
+    0x00000000, // SX_SURFACE_SYNC
+    0x00000000, // SX_SCATTER_EXPORT_BASE
+    0x00000000, // SX_SCATTER_EXPORT_SIZE
+    0x00000000, // CP_PERFMON_CNTX_CNTL
+    0x00000000, // CP_RINGID
+    0x00000000, // CP_VMID
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_VTX_SEMANTIC_0
+    0x00000000, // SQ_VTX_SEMANTIC_1
+    0x00000000, // SQ_VTX_SEMANTIC_2
+    0x00000000, // SQ_VTX_SEMANTIC_3
+    0x00000000, // SQ_VTX_SEMANTIC_4
+    0x00000000, // SQ_VTX_SEMANTIC_5
+    0x00000000, // SQ_VTX_SEMANTIC_6
+    0x00000000, // SQ_VTX_SEMANTIC_7
+    0x00000000, // SQ_VTX_SEMANTIC_8
+    0x00000000, // SQ_VTX_SEMANTIC_9
+    0x00000000, // SQ_VTX_SEMANTIC_10
+    0x00000000, // SQ_VTX_SEMANTIC_11
+    0x00000000, // SQ_VTX_SEMANTIC_12
+    0x00000000, // SQ_VTX_SEMANTIC_13
+    0x00000000, // SQ_VTX_SEMANTIC_14
+    0x00000000, // SQ_VTX_SEMANTIC_15
+    0x00000000, // SQ_VTX_SEMANTIC_16
+    0x00000000, // SQ_VTX_SEMANTIC_17
+    0x00000000, // SQ_VTX_SEMANTIC_18
+    0x00000000, // SQ_VTX_SEMANTIC_19
+    0x00000000, // SQ_VTX_SEMANTIC_20
+    0x00000000, // SQ_VTX_SEMANTIC_21
+    0x00000000, // SQ_VTX_SEMANTIC_22
+    0x00000000, // SQ_VTX_SEMANTIC_23
+    0x00000000, // SQ_VTX_SEMANTIC_24
+    0x00000000, // SQ_VTX_SEMANTIC_25
+    0x00000000, // SQ_VTX_SEMANTIC_26
+    0x00000000, // SQ_VTX_SEMANTIC_27
+    0x00000000, // SQ_VTX_SEMANTIC_28
+    0x00000000, // SQ_VTX_SEMANTIC_29
+    0x00000000, // SQ_VTX_SEMANTIC_30
+    0x00000000, // SQ_VTX_SEMANTIC_31
+    0xffffffff, // VGT_MAX_VTX_INDX
+    0x00000000, // VGT_MIN_VTX_INDX
+    0x00000000, // VGT_INDX_OFFSET
+    0x00000000, // VGT_MULTI_PRIM_IB_RESET_INDX
+    0x00000000, // SX_ALPHA_TEST_CONTROL
+    0x00000000, // CB_BLEND_RED
+    0x00000000, // CB_BLEND_GREEN
+    0x00000000, // CB_BLEND_BLUE
+    0x00000000, // CB_BLEND_ALPHA
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // DB_STENCILREFMASK
+    0x00000000, // DB_STENCILREFMASK_BF
+    0x00000000, // SX_ALPHA_REF
+    0x00000000, // PA_CL_VPORT_XSCALE
+    0x00000000, // PA_CL_VPORT_XOFFSET
+    0x00000000, // PA_CL_VPORT_YSCALE
+    0x00000000, // PA_CL_VPORT_YOFFSET
+    0x00000000, // PA_CL_VPORT_ZSCALE
+    0x00000000, // PA_CL_VPORT_ZOFFSET
+    0x00000000, // PA_CL_VPORT_XSCALE_1
+    0x00000000, // PA_CL_VPORT_XOFFSET_1
+    0x00000000, // PA_CL_VPORT_YSCALE_1
+    0x00000000, // PA_CL_VPORT_YOFFSET_1
+    0x00000000, // PA_CL_VPORT_ZSCALE_1
+    0x00000000, // PA_CL_VPORT_ZOFFSET_1
+    0x00000000, // PA_CL_VPORT_XSCALE_2
+    0x00000000, // PA_CL_VPORT_XOFFSET_2
+    0x00000000, // PA_CL_VPORT_YSCALE_2
+    0x00000000, // PA_CL_VPORT_YOFFSET_2
+    0x00000000, // PA_CL_VPORT_ZSCALE_2
+    0x00000000, // PA_CL_VPORT_ZOFFSET_2
+    0x00000000, // PA_CL_VPORT_XSCALE_3
+    0x00000000, // PA_CL_VPORT_XOFFSET_3
+    0x00000000, // PA_CL_VPORT_YSCALE_3
+    0x00000000, // PA_CL_VPORT_YOFFSET_3
+    0x00000000, // PA_CL_VPORT_ZSCALE_3
+    0x00000000, // PA_CL_VPORT_ZOFFSET_3
+    0x00000000, // PA_CL_VPORT_XSCALE_4
+    0x00000000, // PA_CL_VPORT_XOFFSET_4
+    0x00000000, // PA_CL_VPORT_YSCALE_4
+    0x00000000, // PA_CL_VPORT_YOFFSET_4
+    0x00000000, // PA_CL_VPORT_ZSCALE_4
+    0x00000000, // PA_CL_VPORT_ZOFFSET_4
+    0x00000000, // PA_CL_VPORT_XSCALE_5
+    0x00000000, // PA_CL_VPORT_XOFFSET_5
+    0x00000000, // PA_CL_VPORT_YSCALE_5
+    0x00000000, // PA_CL_VPORT_YOFFSET_5
+    0x00000000, // PA_CL_VPORT_ZSCALE_5
+    0x00000000, // PA_CL_VPORT_ZOFFSET_5
+    0x00000000, // PA_CL_VPORT_XSCALE_6
+    0x00000000, // PA_CL_VPORT_XOFFSET_6
+    0x00000000, // PA_CL_VPORT_YSCALE_6
+    0x00000000, // PA_CL_VPORT_YOFFSET_6
+    0x00000000, // PA_CL_VPORT_ZSCALE_6
+    0x00000000, // PA_CL_VPORT_ZOFFSET_6
+    0x00000000, // PA_CL_VPORT_XSCALE_7
+    0x00000000, // PA_CL_VPORT_XOFFSET_7
+    0x00000000, // PA_CL_VPORT_YSCALE_7
+    0x00000000, // PA_CL_VPORT_YOFFSET_7
+    0x00000000, // PA_CL_VPORT_ZSCALE_7
+    0x00000000, // PA_CL_VPORT_ZOFFSET_7
+    0x00000000, // PA_CL_VPORT_XSCALE_8
+    0x00000000, // PA_CL_VPORT_XOFFSET_8
+    0x00000000, // PA_CL_VPORT_YSCALE_8
+    0x00000000, // PA_CL_VPORT_YOFFSET_8
+    0x00000000, // PA_CL_VPORT_ZSCALE_8
+    0x00000000, // PA_CL_VPORT_ZOFFSET_8
+    0x00000000, // PA_CL_VPORT_XSCALE_9
+    0x00000000, // PA_CL_VPORT_XOFFSET_9
+    0x00000000, // PA_CL_VPORT_YSCALE_9
+    0x00000000, // PA_CL_VPORT_YOFFSET_9
+    0x00000000, // PA_CL_VPORT_ZSCALE_9
+    0x00000000, // PA_CL_VPORT_ZOFFSET_9
+    0x00000000, // PA_CL_VPORT_XSCALE_10
+    0x00000000, // PA_CL_VPORT_XOFFSET_10
+    0x00000000, // PA_CL_VPORT_YSCALE_10
+    0x00000000, // PA_CL_VPORT_YOFFSET_10
+    0x00000000, // PA_CL_VPORT_ZSCALE_10
+    0x00000000, // PA_CL_VPORT_ZOFFSET_10
+    0x00000000, // PA_CL_VPORT_XSCALE_11
+    0x00000000, // PA_CL_VPORT_XOFFSET_11
+    0x00000000, // PA_CL_VPORT_YSCALE_11
+    0x00000000, // PA_CL_VPORT_YOFFSET_11
+    0x00000000, // PA_CL_VPORT_ZSCALE_11
+    0x00000000, // PA_CL_VPORT_ZOFFSET_11
+    0x00000000, // PA_CL_VPORT_XSCALE_12
+    0x00000000, // PA_CL_VPORT_XOFFSET_12
+    0x00000000, // PA_CL_VPORT_YSCALE_12
+    0x00000000, // PA_CL_VPORT_YOFFSET_12
+    0x00000000, // PA_CL_VPORT_ZSCALE_12
+    0x00000000, // PA_CL_VPORT_ZOFFSET_12
+    0x00000000, // PA_CL_VPORT_XSCALE_13
+    0x00000000, // PA_CL_VPORT_XOFFSET_13
+    0x00000000, // PA_CL_VPORT_YSCALE_13
+    0x00000000, // PA_CL_VPORT_YOFFSET_13
+    0x00000000, // PA_CL_VPORT_ZSCALE_13
+    0x00000000, // PA_CL_VPORT_ZOFFSET_13
+    0x00000000, // PA_CL_VPORT_XSCALE_14
+    0x00000000, // PA_CL_VPORT_XOFFSET_14
+    0x00000000, // PA_CL_VPORT_YSCALE_14
+    0x00000000, // PA_CL_VPORT_YOFFSET_14
+    0x00000000, // PA_CL_VPORT_ZSCALE_14
+    0x00000000, // PA_CL_VPORT_ZOFFSET_14
+    0x00000000, // PA_CL_VPORT_XSCALE_15
+    0x00000000, // PA_CL_VPORT_XOFFSET_15
+    0x00000000, // PA_CL_VPORT_YSCALE_15
+    0x00000000, // PA_CL_VPORT_YOFFSET_15
+    0x00000000, // PA_CL_VPORT_ZSCALE_15
+    0x00000000, // PA_CL_VPORT_ZOFFSET_15
+    0x00000000, // PA_CL_UCP_0_X
+    0x00000000, // PA_CL_UCP_0_Y
+    0x00000000, // PA_CL_UCP_0_Z
+    0x00000000, // PA_CL_UCP_0_W
+    0x00000000, // PA_CL_UCP_1_X
+    0x00000000, // PA_CL_UCP_1_Y
+    0x00000000, // PA_CL_UCP_1_Z
+    0x00000000, // PA_CL_UCP_1_W
+    0x00000000, // PA_CL_UCP_2_X
+    0x00000000, // PA_CL_UCP_2_Y
+    0x00000000, // PA_CL_UCP_2_Z
+    0x00000000, // PA_CL_UCP_2_W
+    0x00000000, // PA_CL_UCP_3_X
+    0x00000000, // PA_CL_UCP_3_Y
+    0x00000000, // PA_CL_UCP_3_Z
+    0x00000000, // PA_CL_UCP_3_W
+    0x00000000, // PA_CL_UCP_4_X
+    0x00000000, // PA_CL_UCP_4_Y
+    0x00000000, // PA_CL_UCP_4_Z
+    0x00000000, // PA_CL_UCP_4_W
+    0x00000000, // PA_CL_UCP_5_X
+    0x00000000, // PA_CL_UCP_5_Y
+    0x00000000, // PA_CL_UCP_5_Z
+    0x00000000, // PA_CL_UCP_5_W
+    0x00000000, // SPI_VS_OUT_ID_0
+    0x00000000, // SPI_VS_OUT_ID_1
+    0x00000000, // SPI_VS_OUT_ID_2
+    0x00000000, // SPI_VS_OUT_ID_3
+    0x00000000, // SPI_VS_OUT_ID_4
+    0x00000000, // SPI_VS_OUT_ID_5
+    0x00000000, // SPI_VS_OUT_ID_6
+    0x00000000, // SPI_VS_OUT_ID_7
+    0x00000000, // SPI_VS_OUT_ID_8
+    0x00000000, // SPI_VS_OUT_ID_9
+    0x00000000, // SPI_PS_INPUT_CNTL_0
+    0x00000000, // SPI_PS_INPUT_CNTL_1
+    0x00000000, // SPI_PS_INPUT_CNTL_2
+    0x00000000, // SPI_PS_INPUT_CNTL_3
+    0x00000000, // SPI_PS_INPUT_CNTL_4
+    0x00000000, // SPI_PS_INPUT_CNTL_5
+    0x00000000, // SPI_PS_INPUT_CNTL_6
+    0x00000000, // SPI_PS_INPUT_CNTL_7
+    0x00000000, // SPI_PS_INPUT_CNTL_8
+    0x00000000, // SPI_PS_INPUT_CNTL_9
+    0x00000000, // SPI_PS_INPUT_CNTL_10
+    0x00000000, // SPI_PS_INPUT_CNTL_11
+    0x00000000, // SPI_PS_INPUT_CNTL_12
+    0x00000000, // SPI_PS_INPUT_CNTL_13
+    0x00000000, // SPI_PS_INPUT_CNTL_14
+    0x00000000, // SPI_PS_INPUT_CNTL_15
+    0x00000000, // SPI_PS_INPUT_CNTL_16
+    0x00000000, // SPI_PS_INPUT_CNTL_17
+    0x00000000, // SPI_PS_INPUT_CNTL_18
+    0x00000000, // SPI_PS_INPUT_CNTL_19
+    0x00000000, // SPI_PS_INPUT_CNTL_20
+    0x00000000, // SPI_PS_INPUT_CNTL_21
+    0x00000000, // SPI_PS_INPUT_CNTL_22
+    0x00000000, // SPI_PS_INPUT_CNTL_23
+    0x00000000, // SPI_PS_INPUT_CNTL_24
+    0x00000000, // SPI_PS_INPUT_CNTL_25
+    0x00000000, // SPI_PS_INPUT_CNTL_26
+    0x00000000, // SPI_PS_INPUT_CNTL_27
+    0x00000000, // SPI_PS_INPUT_CNTL_28
+    0x00000000, // SPI_PS_INPUT_CNTL_29
+    0x00000000, // SPI_PS_INPUT_CNTL_30
+    0x00000000, // SPI_PS_INPUT_CNTL_31
+    0x00000000, // SPI_VS_OUT_CONFIG
+    0x00000001, // SPI_THREAD_GROUPING
+    0x00000002, // SPI_PS_IN_CONTROL_0
+    0x00000000, // SPI_PS_IN_CONTROL_1
+    0x00000000, // SPI_INTERP_CONTROL_0
+    0x00000000, // SPI_INPUT_Z
+    0x00000000, // SPI_FOG_CNTL
+    0x00000000, // SPI_BARYC_CNTL
+    0x00000000, // SPI_PS_IN_CONTROL_2
+    0x00000000, // SPI_COMPUTE_INPUT_CNTL
+    0x00000000, // SPI_COMPUTE_NUM_THREAD_X
+    0x00000000, // SPI_COMPUTE_NUM_THREAD_Y
+    0x00000000, // SPI_COMPUTE_NUM_THREAD_Z
+    0x00000000, // SPI_GPR_MGMT
+    0x00000000, // SPI_LDS_MGMT
+    0x00000000, // SPI_STACK_MGMT
+    0x00000000, // SPI_WAVE_MGMT_1
+    0x00000000, // SPI_WAVE_MGMT_2
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // GDS_ADDR_BASE
+    0x00003fff, // GDS_ADDR_SIZE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // GDS_ORDERED_COUNT
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // GDS_APPEND_CONSUME_UAV0
+    0x00000000, // GDS_APPEND_CONSUME_UAV1
+    0x00000000, // GDS_APPEND_CONSUME_UAV2
+    0x00000000, // GDS_APPEND_CONSUME_UAV3
+    0x00000000, // GDS_APPEND_CONSUME_UAV4
+    0x00000000, // GDS_APPEND_CONSUME_UAV5
+    0x00000000, // GDS_APPEND_CONSUME_UAV6
+    0x00000000, // GDS_APPEND_CONSUME_UAV7
+    0x00000000, // GDS_APPEND_CONSUME_UAV8
+    0x00000000, // GDS_APPEND_CONSUME_UAV9
+    0x00000000, // GDS_APPEND_CONSUME_UAV10
+    0x00000000, // GDS_APPEND_CONSUME_UAV11
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_BLEND0_CONTROL
+    0x00000000, // CB_BLEND1_CONTROL
+    0x00000000, // CB_BLEND2_CONTROL
+    0x00000000, // CB_BLEND3_CONTROL
+    0x00000000, // CB_BLEND4_CONTROL
+    0x00000000, // CB_BLEND5_CONTROL
+    0x00000000, // CB_BLEND6_CONTROL
+    0x00000000, // CB_BLEND7_CONTROL
+};
+static const u32 SECT_CONTEXT_def_2[] =
+{
+    0x00000000, // PA_CL_POINT_X_RAD
+    0x00000000, // PA_CL_POINT_Y_RAD
+    0x00000000, // PA_CL_POINT_SIZE
+    0x00000000, // PA_CL_POINT_CULL_RAD
+    0x00000000, // VGT_DMA_BASE_HI
+    0x00000000, // VGT_DMA_BASE
+};
+static const u32 SECT_CONTEXT_def_3[] =
+{
+    0x00000000, // DB_DEPTH_CONTROL
+    0x00000000, // DB_EQAA
+    0x00000000, // CB_COLOR_CONTROL
+    0x00000200, // DB_SHADER_CONTROL
+    0x00000000, // PA_CL_CLIP_CNTL
+    0x00000000, // PA_SU_SC_MODE_CNTL
+    0x00000000, // PA_CL_VTE_CNTL
+    0x00000000, // PA_CL_VS_OUT_CNTL
+    0x00000000, // PA_CL_NANINF_CNTL
+    0x00000000, // PA_SU_LINE_STIPPLE_CNTL
+    0x00000000, // PA_SU_LINE_STIPPLE_SCALE
+    0x00000000, // PA_SU_PRIM_FILTER_CNTL
+    0x00000000, // SQ_LSTMP_RING_ITEMSIZE
+    0x00000000, // SQ_HSTMP_RING_ITEMSIZE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_PS
+    0x00000000, // SQ_PGM_RESOURCES_PS
+    0x00000000, // SQ_PGM_RESOURCES_2_PS
+    0x00000000, // SQ_PGM_EXPORTS_PS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_VS
+    0x00000000, // SQ_PGM_RESOURCES_VS
+    0x00000000, // SQ_PGM_RESOURCES_2_VS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_GS
+    0x00000000, // SQ_PGM_RESOURCES_GS
+    0x00000000, // SQ_PGM_RESOURCES_2_GS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_ES
+    0x00000000, // SQ_PGM_RESOURCES_ES
+    0x00000000, // SQ_PGM_RESOURCES_2_ES
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_FS
+    0x00000000, // SQ_PGM_RESOURCES_FS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_HS
+    0x00000000, // SQ_PGM_RESOURCES_HS
+    0x00000000, // SQ_PGM_RESOURCES_2_HS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_LS
+    0x00000000, // SQ_PGM_RESOURCES_LS
+    0x00000000, // SQ_PGM_RESOURCES_2_LS
+};
+static const u32 SECT_CONTEXT_def_4[] =
+{
+    0x00000000, // SQ_LDS_ALLOC
+    0x00000000, // SQ_LDS_ALLOC_PS
+    0x00000000, // SQ_VTX_SEMANTIC_CLEAR
+    0, // HOLE
+    0x00000000, // SQ_THREAD_TRACE_CTRL
+    0, // HOLE
+    0x00000000, // SQ_ESGS_RING_ITEMSIZE
+    0x00000000, // SQ_GSVS_RING_ITEMSIZE
+    0x00000000, // SQ_ESTMP_RING_ITEMSIZE
+    0x00000000, // SQ_GSTMP_RING_ITEMSIZE
+    0x00000000, // SQ_VSTMP_RING_ITEMSIZE
+    0x00000000, // SQ_PSTMP_RING_ITEMSIZE
+    0, // HOLE
+    0x00000000, // SQ_GS_VERT_ITEMSIZE
+    0x00000000, // SQ_GS_VERT_ITEMSIZE_1
+    0x00000000, // SQ_GS_VERT_ITEMSIZE_2
+    0x00000000, // SQ_GS_VERT_ITEMSIZE_3
+    0x00000000, // SQ_GSVS_RING_OFFSET_1
+    0x00000000, // SQ_GSVS_RING_OFFSET_2
+    0x00000000, // SQ_GSVS_RING_OFFSET_3
+    0x00000000, // SQ_GWS_RING_OFFSET
+    0, // HOLE
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_0
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_1
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_2
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_3
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_4
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_5
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_6
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_7
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_8
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_9
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_10
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_11
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_12
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_13
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_14
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_15
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_0
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_1
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_2
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_3
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_4
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_5
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_6
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_7
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_8
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_9
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_10
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_11
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_12
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_13
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_14
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_15
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_0
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_1
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_2
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_3
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_4
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_5
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_6
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_7
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_8
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_9
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_10
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_11
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_12
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_13
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_14
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_15
+    0x00000000, // PA_SU_POINT_SIZE
+    0x00000000, // PA_SU_POINT_MINMAX
+    0x00000000, // PA_SU_LINE_CNTL
+    0x00000000, // PA_SC_LINE_STIPPLE
+    0x00000000, // VGT_OUTPUT_PATH_CNTL
+    0x00000000, // VGT_HOS_CNTL
+    0x00000000, // VGT_HOS_MAX_TESS_LEVEL
+    0x00000000, // VGT_HOS_MIN_TESS_LEVEL
+    0x00000000, // VGT_HOS_REUSE_DEPTH
+    0x00000000, // VGT_GROUP_PRIM_TYPE
+    0x00000000, // VGT_GROUP_FIRST_DECR
+    0x00000000, // VGT_GROUP_DECR
+    0x00000000, // VGT_GROUP_VECT_0_CNTL
+    0x00000000, // VGT_GROUP_VECT_1_CNTL
+    0x00000000, // VGT_GROUP_VECT_0_FMT_CNTL
+    0x00000000, // VGT_GROUP_VECT_1_FMT_CNTL
+    0x00000000, // VGT_GS_MODE
+    0, // HOLE
+    0x00000000, // PA_SC_MODE_CNTL_0
+    0x00000000, // PA_SC_MODE_CNTL_1
+    0x00000000, // VGT_ENHANCE
+    0x00000100, // VGT_GS_PER_ES
+    0x00000080, // VGT_ES_PER_GS
+    0x00000002, // VGT_GS_PER_VS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_GS_OUT_PRIM_TYPE
+    0x00000000, // IA_ENHANCE
+};
+static const u32 SECT_CONTEXT_def_5[] =
+{
+    0x00000000, // VGT_DMA_MAX_SIZE
+    0x00000000, // VGT_DMA_INDEX_TYPE
+    0, // HOLE
+    0x00000000, // VGT_PRIMITIVEID_EN
+    0x00000000, // VGT_DMA_NUM_INSTANCES
+};
+static const u32 SECT_CONTEXT_def_6[] =
+{
+    0x00000000, // VGT_MULTI_PRIM_IB_RESET_EN
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_INSTANCE_STEP_RATE_0
+    0x00000000, // VGT_INSTANCE_STEP_RATE_1
+    0x000000ff, // IA_MULTI_VGT_PARAM
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_REUSE_OFF
+    0x00000000, // VGT_VTX_CNT_EN
+    0x00000000, // DB_HTILE_SURFACE
+    0x00000000, // DB_SRESULTS_COMPARE_STATE0
+    0x00000000, // DB_SRESULTS_COMPARE_STATE1
+    0x00000000, // DB_PRELOAD_CONTROL
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_0
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_0
+    0x00000000, // VGT_STRMOUT_BUFFER_BASE_0
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_0
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_1
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_1
+    0x00000000, // VGT_STRMOUT_BUFFER_BASE_1
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_1
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_2
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_2
+    0x00000000, // VGT_STRMOUT_BUFFER_BASE_2
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_2
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_3
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_3
+    0x00000000, // VGT_STRMOUT_BUFFER_BASE_3
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_3
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_0
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_1
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_2
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_3
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_OFFSET
+    0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE
+    0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE
+    0, // HOLE
+    0x00000000, // VGT_GS_MAX_VERT_OUT
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_0
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_1
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_2
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_3
+    0x00000000, // VGT_SHADER_STAGES_EN
+    0x00000000, // VGT_LS_HS_CONFIG
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_TF_PARAM
+    0x00000000, // DB_ALPHA_TO_MASK
+};
+static const u32 SECT_CONTEXT_def_7[] =
+{
+    0x00000000, // PA_SU_POLY_OFFSET_DB_FMT_CNTL
+    0x00000000, // PA_SU_POLY_OFFSET_CLAMP
+    0x00000000, // PA_SU_POLY_OFFSET_FRONT_SCALE
+    0x00000000, // PA_SU_POLY_OFFSET_FRONT_OFFSET
+    0x00000000, // PA_SU_POLY_OFFSET_BACK_SCALE
+    0x00000000, // PA_SU_POLY_OFFSET_BACK_OFFSET
+    0x00000000, // VGT_GS_INSTANCE_CNT
+    0x00000000, // VGT_STRMOUT_CONFIG
+    0x00000000, // VGT_STRMOUT_BUFFER_CONFIG
+    0x00000000, // CB_IMMED0_BASE
+    0x00000000, // CB_IMMED1_BASE
+    0x00000000, // CB_IMMED2_BASE
+    0x00000000, // CB_IMMED3_BASE
+    0x00000000, // CB_IMMED4_BASE
+    0x00000000, // CB_IMMED5_BASE
+    0x00000000, // CB_IMMED6_BASE
+    0x00000000, // CB_IMMED7_BASE
+    0x00000000, // CB_IMMED8_BASE
+    0x00000000, // CB_IMMED9_BASE
+    0x00000000, // CB_IMMED10_BASE
+    0x00000000, // CB_IMMED11_BASE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // PA_SC_CENTROID_PRIORITY_0
+    0x00000000, // PA_SC_CENTROID_PRIORITY_1
+    0x00001000, // PA_SC_LINE_CNTL
+    0x00000000, // PA_SC_AA_CONFIG
+    0x00000005, // PA_SU_VTX_CNTL
+    0x3f800000, // PA_CL_GB_VERT_CLIP_ADJ
+    0x3f800000, // PA_CL_GB_VERT_DISC_ADJ
+    0x3f800000, // PA_CL_GB_HORZ_CLIP_ADJ
+    0x3f800000, // PA_CL_GB_HORZ_DISC_ADJ
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_1
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_2
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_3
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_0
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_1
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_2
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_3
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_0
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_1
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_2
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_3
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_0
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_1
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_2
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_3
+    0xffffffff, // PA_SC_AA_MASK_X0Y0_X1Y0
+    0xffffffff, // PA_SC_AA_MASK_X0Y1_X1Y1
+    0x00000000, // CB_CLRCMP_CONTROL
+    0x00000000, // CB_CLRCMP_SRC
+    0x00000000, // CB_CLRCMP_DST
+    0x00000000, // CB_CLRCMP_MSK
+    0, // HOLE
+    0, // HOLE
+    0x0000000e, // VGT_VERTEX_REUSE_BLOCK_CNTL
+    0x00000010, // VGT_OUT_DEALLOC_CNTL
+    0x00000000, // CB_COLOR0_BASE
+    0x00000000, // CB_COLOR0_PITCH
+    0x00000000, // CB_COLOR0_SLICE
+    0x00000000, // CB_COLOR0_VIEW
+    0x00000000, // CB_COLOR0_INFO
+    0x00000000, // CB_COLOR0_ATTRIB
+    0x00000000, // CB_COLOR0_DIM
+    0x00000000, // CB_COLOR0_CMASK
+    0x00000000, // CB_COLOR0_CMASK_SLICE
+    0x00000000, // CB_COLOR0_FMASK
+    0x00000000, // CB_COLOR0_FMASK_SLICE
+    0x00000000, // CB_COLOR0_CLEAR_WORD0
+    0x00000000, // CB_COLOR0_CLEAR_WORD1
+    0x00000000, // CB_COLOR0_CLEAR_WORD2
+    0x00000000, // CB_COLOR0_CLEAR_WORD3
+    0x00000000, // CB_COLOR1_BASE
+    0x00000000, // CB_COLOR1_PITCH
+    0x00000000, // CB_COLOR1_SLICE
+    0x00000000, // CB_COLOR1_VIEW
+    0x00000000, // CB_COLOR1_INFO
+    0x00000000, // CB_COLOR1_ATTRIB
+    0x00000000, // CB_COLOR1_DIM
+    0x00000000, // CB_COLOR1_CMASK
+    0x00000000, // CB_COLOR1_CMASK_SLICE
+    0x00000000, // CB_COLOR1_FMASK
+    0x00000000, // CB_COLOR1_FMASK_SLICE
+    0x00000000, // CB_COLOR1_CLEAR_WORD0
+    0x00000000, // CB_COLOR1_CLEAR_WORD1
+    0x00000000, // CB_COLOR1_CLEAR_WORD2
+    0x00000000, // CB_COLOR1_CLEAR_WORD3
+    0x00000000, // CB_COLOR2_BASE
+    0x00000000, // CB_COLOR2_PITCH
+    0x00000000, // CB_COLOR2_SLICE
+    0x00000000, // CB_COLOR2_VIEW
+    0x00000000, // CB_COLOR2_INFO
+    0x00000000, // CB_COLOR2_ATTRIB
+    0x00000000, // CB_COLOR2_DIM
+    0x00000000, // CB_COLOR2_CMASK
+    0x00000000, // CB_COLOR2_CMASK_SLICE
+    0x00000000, // CB_COLOR2_FMASK
+    0x00000000, // CB_COLOR2_FMASK_SLICE
+    0x00000000, // CB_COLOR2_CLEAR_WORD0
+    0x00000000, // CB_COLOR2_CLEAR_WORD1
+    0x00000000, // CB_COLOR2_CLEAR_WORD2
+    0x00000000, // CB_COLOR2_CLEAR_WORD3
+    0x00000000, // CB_COLOR3_BASE
+    0x00000000, // CB_COLOR3_PITCH
+    0x00000000, // CB_COLOR3_SLICE
+    0x00000000, // CB_COLOR3_VIEW
+    0x00000000, // CB_COLOR3_INFO
+    0x00000000, // CB_COLOR3_ATTRIB
+    0x00000000, // CB_COLOR3_DIM
+    0x00000000, // CB_COLOR3_CMASK
+    0x00000000, // CB_COLOR3_CMASK_SLICE
+    0x00000000, // CB_COLOR3_FMASK
+    0x00000000, // CB_COLOR3_FMASK_SLICE
+    0x00000000, // CB_COLOR3_CLEAR_WORD0
+    0x00000000, // CB_COLOR3_CLEAR_WORD1
+    0x00000000, // CB_COLOR3_CLEAR_WORD2
+    0x00000000, // CB_COLOR3_CLEAR_WORD3
+    0x00000000, // CB_COLOR4_BASE
+    0x00000000, // CB_COLOR4_PITCH
+    0x00000000, // CB_COLOR4_SLICE
+    0x00000000, // CB_COLOR4_VIEW
+    0x00000000, // CB_COLOR4_INFO
+    0x00000000, // CB_COLOR4_ATTRIB
+    0x00000000, // CB_COLOR4_DIM
+    0x00000000, // CB_COLOR4_CMASK
+    0x00000000, // CB_COLOR4_CMASK_SLICE
+    0x00000000, // CB_COLOR4_FMASK
+    0x00000000, // CB_COLOR4_FMASK_SLICE
+    0x00000000, // CB_COLOR4_CLEAR_WORD0
+    0x00000000, // CB_COLOR4_CLEAR_WORD1
+    0x00000000, // CB_COLOR4_CLEAR_WORD2
+    0x00000000, // CB_COLOR4_CLEAR_WORD3
+    0x00000000, // CB_COLOR5_BASE
+    0x00000000, // CB_COLOR5_PITCH
+    0x00000000, // CB_COLOR5_SLICE
+    0x00000000, // CB_COLOR5_VIEW
+    0x00000000, // CB_COLOR5_INFO
+    0x00000000, // CB_COLOR5_ATTRIB
+    0x00000000, // CB_COLOR5_DIM
+    0x00000000, // CB_COLOR5_CMASK
+    0x00000000, // CB_COLOR5_CMASK_SLICE
+    0x00000000, // CB_COLOR5_FMASK
+    0x00000000, // CB_COLOR5_FMASK_SLICE
+    0x00000000, // CB_COLOR5_CLEAR_WORD0
+    0x00000000, // CB_COLOR5_CLEAR_WORD1
+    0x00000000, // CB_COLOR5_CLEAR_WORD2
+    0x00000000, // CB_COLOR5_CLEAR_WORD3
+    0x00000000, // CB_COLOR6_BASE
+    0x00000000, // CB_COLOR6_PITCH
+    0x00000000, // CB_COLOR6_SLICE
+    0x00000000, // CB_COLOR6_VIEW
+    0x00000000, // CB_COLOR6_INFO
+    0x00000000, // CB_COLOR6_ATTRIB
+    0x00000000, // CB_COLOR6_DIM
+    0x00000000, // CB_COLOR6_CMASK
+    0x00000000, // CB_COLOR6_CMASK_SLICE
+    0x00000000, // CB_COLOR6_FMASK
+    0x00000000, // CB_COLOR6_FMASK_SLICE
+    0x00000000, // CB_COLOR6_CLEAR_WORD0
+    0x00000000, // CB_COLOR6_CLEAR_WORD1
+    0x00000000, // CB_COLOR6_CLEAR_WORD2
+    0x00000000, // CB_COLOR6_CLEAR_WORD3
+    0x00000000, // CB_COLOR7_BASE
+    0x00000000, // CB_COLOR7_PITCH
+    0x00000000, // CB_COLOR7_SLICE
+    0x00000000, // CB_COLOR7_VIEW
+    0x00000000, // CB_COLOR7_INFO
+    0x00000000, // CB_COLOR7_ATTRIB
+    0x00000000, // CB_COLOR7_DIM
+    0x00000000, // CB_COLOR7_CMASK
+    0x00000000, // CB_COLOR7_CMASK_SLICE
+    0x00000000, // CB_COLOR7_FMASK
+    0x00000000, // CB_COLOR7_FMASK_SLICE
+    0x00000000, // CB_COLOR7_CLEAR_WORD0
+    0x00000000, // CB_COLOR7_CLEAR_WORD1
+    0x00000000, // CB_COLOR7_CLEAR_WORD2
+    0x00000000, // CB_COLOR7_CLEAR_WORD3
+    0x00000000, // CB_COLOR8_BASE
+    0x00000000, // CB_COLOR8_PITCH
+    0x00000000, // CB_COLOR8_SLICE
+    0x00000000, // CB_COLOR8_VIEW
+    0x00000000, // CB_COLOR8_INFO
+    0x00000000, // CB_COLOR8_ATTRIB
+    0x00000000, // CB_COLOR8_DIM
+    0x00000000, // CB_COLOR9_BASE
+    0x00000000, // CB_COLOR9_PITCH
+    0x00000000, // CB_COLOR9_SLICE
+    0x00000000, // CB_COLOR9_VIEW
+    0x00000000, // CB_COLOR9_INFO
+    0x00000000, // CB_COLOR9_ATTRIB
+    0x00000000, // CB_COLOR9_DIM
+    0x00000000, // CB_COLOR10_BASE
+    0x00000000, // CB_COLOR10_PITCH
+    0x00000000, // CB_COLOR10_SLICE
+    0x00000000, // CB_COLOR10_VIEW
+    0x00000000, // CB_COLOR10_INFO
+    0x00000000, // CB_COLOR10_ATTRIB
+    0x00000000, // CB_COLOR10_DIM
+    0x00000000, // CB_COLOR11_BASE
+    0x00000000, // CB_COLOR11_PITCH
+    0x00000000, // CB_COLOR11_SLICE
+    0x00000000, // CB_COLOR11_VIEW
+    0x00000000, // CB_COLOR11_INFO
+    0x00000000, // CB_COLOR11_ATTRIB
+    0x00000000, // CB_COLOR11_DIM
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_0
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_1
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_2
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_3
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_4
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_5
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_6
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_7
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_8
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_9
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_10
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_11
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_12
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_13
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_14
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_15
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_0
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_1
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_2
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_3
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_4
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_5
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_6
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_7
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_8
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_9
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_10
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_11
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_12
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_13
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_14
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_15
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_0
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_1
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_2
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_3
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_4
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_5
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_6
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_7
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_8
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_9
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_10
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_11
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_12
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_13
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_14
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_15
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_0
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_1
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_2
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_3
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_4
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_5
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_6
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_7
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_8
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_9
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_10
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_11
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_12
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_13
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_14
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_15
+};
+static const struct cs_extent_def SECT_CONTEXT_defs[] =
+{
+    {SECT_CONTEXT_def_1, 0x0000a000, 488 },
+    {SECT_CONTEXT_def_2, 0x0000a1f5, 6 },
+    {SECT_CONTEXT_def_3, 0x0000a200, 55 },
+    {SECT_CONTEXT_def_4, 0x0000a23a, 99 },
+    {SECT_CONTEXT_def_5, 0x0000a29e, 5 },
+    {SECT_CONTEXT_def_6, 0x0000a2a5, 56 },
+    {SECT_CONTEXT_def_7, 0x0000a2de, 290 },
+    { 0, 0, 0 }
+};
+static const u32 SECT_CLEAR_def_1[] =
+{
+    0xffffffff, // SQ_TEX_SAMPLER_CLEAR
+    0xffffffff, // SQ_TEX_RESOURCE_CLEAR
+    0xffffffff, // SQ_LOOP_BOOL_CLEAR
+};
+static const struct cs_extent_def SECT_CLEAR_defs[] =
+{
+    {SECT_CLEAR_def_1, 0x0000ffc0, 3 },
+    { 0, 0, 0 }
+};
+static const u32 SECT_CTRLCONST_def_1[] =
+{
+    0x00000000, // SQ_VTX_BASE_VTX_LOC
+    0x00000000, // SQ_VTX_START_INST_LOC
+};
+static const struct cs_extent_def SECT_CTRLCONST_defs[] =
+{
+    {SECT_CTRLCONST_def_1, 0x0000f3fc, 2 },
+    { 0, 0, 0 }
+};
+struct cs_section_def cayman_cs_data[] = {
+    { SECT_CONTEXT_defs, SECT_CONTEXT },
+    { SECT_CLEAR_defs, SECT_CLEAR },
+    { SECT_CTRLCONST_defs, SECT_CTRLCONST },
+    { 0, SECT_NONE }
+};
diff --git a/drivers/gpu/drm/radeon/clearstate_defs.h b/drivers/gpu/drm/radeon/clearstate_defs.h
new file mode 100644 (file)
index 0000000..3eda707
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef CLEARSTATE_DEFS_H
+#define CLEARSTATE_DEFS_H
+
+enum section_id {
+    SECT_NONE,
+    SECT_CONTEXT,
+    SECT_CLEAR,
+    SECT_CTRLCONST
+};
+
+struct cs_extent_def {
+    const unsigned int *extent;
+    const unsigned int reg_index;
+    const unsigned int reg_count;
+};
+
+struct cs_section_def {
+    const struct cs_extent_def *section;
+    const enum section_id id;
+};
+
+#endif
diff --git a/drivers/gpu/drm/radeon/clearstate_evergreen.h b/drivers/gpu/drm/radeon/clearstate_evergreen.h
new file mode 100644 (file)
index 0000000..4791d85
--- /dev/null
@@ -0,0 +1,1080 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+
+static const u32 SECT_CONTEXT_def_1[] =
+{
+    0x00000000, // DB_RENDER_CONTROL
+    0x00000000, // DB_COUNT_CONTROL
+    0x00000000, // DB_DEPTH_VIEW
+    0x00000000, // DB_RENDER_OVERRIDE
+    0x00000000, // DB_RENDER_OVERRIDE2
+    0x00000000, // DB_HTILE_DATA_BASE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // DB_STENCIL_CLEAR
+    0x00000000, // DB_DEPTH_CLEAR
+    0x00000000, // PA_SC_SCREEN_SCISSOR_TL
+    0x40004000, // PA_SC_SCREEN_SCISSOR_BR
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // DB_Z_INFO
+    0x00000000, // DB_STENCIL_INFO
+    0x00000000, // DB_Z_READ_BASE
+    0x00000000, // DB_STENCIL_READ_BASE
+    0x00000000, // DB_Z_WRITE_BASE
+    0x00000000, // DB_STENCIL_WRITE_BASE
+    0x00000000, // DB_DEPTH_SIZE
+    0x00000000, // DB_DEPTH_SLICE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_0
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_1
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_2
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_3
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_4
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_5
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_6
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_7
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_8
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_9
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_10
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_11
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_12
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_13
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_14
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_15
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_0
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_1
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_2
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_3
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_4
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_5
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_6
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_7
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_8
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_9
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_10
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_11
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_12
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_13
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_14
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_15
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_0
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_1
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_2
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_3
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_4
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_5
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_6
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_7
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_8
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_9
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_10
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_11
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_12
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_13
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_14
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_15
+    0x00000000, // PA_SC_WINDOW_OFFSET
+    0x80000000, // PA_SC_WINDOW_SCISSOR_TL
+    0x40004000, // PA_SC_WINDOW_SCISSOR_BR
+    0x0000ffff, // PA_SC_CLIPRECT_RULE
+    0x00000000, // PA_SC_CLIPRECT_0_TL
+    0x40004000, // PA_SC_CLIPRECT_0_BR
+    0x00000000, // PA_SC_CLIPRECT_1_TL
+    0x40004000, // PA_SC_CLIPRECT_1_BR
+    0x00000000, // PA_SC_CLIPRECT_2_TL
+    0x40004000, // PA_SC_CLIPRECT_2_BR
+    0x00000000, // PA_SC_CLIPRECT_3_TL
+    0x40004000, // PA_SC_CLIPRECT_3_BR
+    0xaa99aaaa, // PA_SC_EDGERULE
+    0x00000000, // PA_SU_HARDWARE_SCREEN_OFFSET
+    0xffffffff, // CB_TARGET_MASK
+    0xffffffff, // CB_SHADER_MASK
+    0x80000000, // PA_SC_GENERIC_SCISSOR_TL
+    0x40004000, // PA_SC_GENERIC_SCISSOR_BR
+    0x00000000, // COHER_DEST_BASE_0
+    0x00000000, // COHER_DEST_BASE_1
+    0x80000000, // PA_SC_VPORT_SCISSOR_0_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_0_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_1_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_1_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_2_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_2_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_3_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_3_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_4_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_4_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_5_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_5_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_6_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_6_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_7_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_7_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_8_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_8_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_9_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_9_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_10_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_10_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_11_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_11_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_12_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_12_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_13_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_13_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_14_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_14_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_15_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_15_BR
+    0x00000000, // PA_SC_VPORT_ZMIN_0
+    0x3f800000, // PA_SC_VPORT_ZMAX_0
+    0x00000000, // PA_SC_VPORT_ZMIN_1
+    0x3f800000, // PA_SC_VPORT_ZMAX_1
+    0x00000000, // PA_SC_VPORT_ZMIN_2
+    0x3f800000, // PA_SC_VPORT_ZMAX_2
+    0x00000000, // PA_SC_VPORT_ZMIN_3
+    0x3f800000, // PA_SC_VPORT_ZMAX_3
+    0x00000000, // PA_SC_VPORT_ZMIN_4
+    0x3f800000, // PA_SC_VPORT_ZMAX_4
+    0x00000000, // PA_SC_VPORT_ZMIN_5
+    0x3f800000, // PA_SC_VPORT_ZMAX_5
+    0x00000000, // PA_SC_VPORT_ZMIN_6
+    0x3f800000, // PA_SC_VPORT_ZMAX_6
+    0x00000000, // PA_SC_VPORT_ZMIN_7
+    0x3f800000, // PA_SC_VPORT_ZMAX_7
+    0x00000000, // PA_SC_VPORT_ZMIN_8
+    0x3f800000, // PA_SC_VPORT_ZMAX_8
+    0x00000000, // PA_SC_VPORT_ZMIN_9
+    0x3f800000, // PA_SC_VPORT_ZMAX_9
+    0x00000000, // PA_SC_VPORT_ZMIN_10
+    0x3f800000, // PA_SC_VPORT_ZMAX_10
+    0x00000000, // PA_SC_VPORT_ZMIN_11
+    0x3f800000, // PA_SC_VPORT_ZMAX_11
+    0x00000000, // PA_SC_VPORT_ZMIN_12
+    0x3f800000, // PA_SC_VPORT_ZMAX_12
+    0x00000000, // PA_SC_VPORT_ZMIN_13
+    0x3f800000, // PA_SC_VPORT_ZMAX_13
+    0x00000000, // PA_SC_VPORT_ZMIN_14
+    0x3f800000, // PA_SC_VPORT_ZMAX_14
+    0x00000000, // PA_SC_VPORT_ZMIN_15
+    0x3f800000, // PA_SC_VPORT_ZMAX_15
+    0x00000000, // SX_MISC
+    0x00000000, // SX_SURFACE_SYNC
+    0x00000000, // CP_PERFMON_CNTX_CNTL
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_VTX_SEMANTIC_0
+    0x00000000, // SQ_VTX_SEMANTIC_1
+    0x00000000, // SQ_VTX_SEMANTIC_2
+    0x00000000, // SQ_VTX_SEMANTIC_3
+    0x00000000, // SQ_VTX_SEMANTIC_4
+    0x00000000, // SQ_VTX_SEMANTIC_5
+    0x00000000, // SQ_VTX_SEMANTIC_6
+    0x00000000, // SQ_VTX_SEMANTIC_7
+    0x00000000, // SQ_VTX_SEMANTIC_8
+    0x00000000, // SQ_VTX_SEMANTIC_9
+    0x00000000, // SQ_VTX_SEMANTIC_10
+    0x00000000, // SQ_VTX_SEMANTIC_11
+    0x00000000, // SQ_VTX_SEMANTIC_12
+    0x00000000, // SQ_VTX_SEMANTIC_13
+    0x00000000, // SQ_VTX_SEMANTIC_14
+    0x00000000, // SQ_VTX_SEMANTIC_15
+    0x00000000, // SQ_VTX_SEMANTIC_16
+    0x00000000, // SQ_VTX_SEMANTIC_17
+    0x00000000, // SQ_VTX_SEMANTIC_18
+    0x00000000, // SQ_VTX_SEMANTIC_19
+    0x00000000, // SQ_VTX_SEMANTIC_20
+    0x00000000, // SQ_VTX_SEMANTIC_21
+    0x00000000, // SQ_VTX_SEMANTIC_22
+    0x00000000, // SQ_VTX_SEMANTIC_23
+    0x00000000, // SQ_VTX_SEMANTIC_24
+    0x00000000, // SQ_VTX_SEMANTIC_25
+    0x00000000, // SQ_VTX_SEMANTIC_26
+    0x00000000, // SQ_VTX_SEMANTIC_27
+    0x00000000, // SQ_VTX_SEMANTIC_28
+    0x00000000, // SQ_VTX_SEMANTIC_29
+    0x00000000, // SQ_VTX_SEMANTIC_30
+    0x00000000, // SQ_VTX_SEMANTIC_31
+    0xffffffff, // VGT_MAX_VTX_INDX
+    0x00000000, // VGT_MIN_VTX_INDX
+    0x00000000, // VGT_INDX_OFFSET
+    0x00000000, // VGT_MULTI_PRIM_IB_RESET_INDX
+    0x00000000, // SX_ALPHA_TEST_CONTROL
+    0x00000000, // CB_BLEND_RED
+    0x00000000, // CB_BLEND_GREEN
+    0x00000000, // CB_BLEND_BLUE
+    0x00000000, // CB_BLEND_ALPHA
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // DB_STENCILREFMASK
+    0x00000000, // DB_STENCILREFMASK_BF
+    0x00000000, // SX_ALPHA_REF
+    0x00000000, // PA_CL_VPORT_XSCALE
+    0x00000000, // PA_CL_VPORT_XOFFSET
+    0x00000000, // PA_CL_VPORT_YSCALE
+    0x00000000, // PA_CL_VPORT_YOFFSET
+    0x00000000, // PA_CL_VPORT_ZSCALE
+    0x00000000, // PA_CL_VPORT_ZOFFSET
+    0x00000000, // PA_CL_VPORT_XSCALE_1
+    0x00000000, // PA_CL_VPORT_XOFFSET_1
+    0x00000000, // PA_CL_VPORT_YSCALE_1
+    0x00000000, // PA_CL_VPORT_YOFFSET_1
+    0x00000000, // PA_CL_VPORT_ZSCALE_1
+    0x00000000, // PA_CL_VPORT_ZOFFSET_1
+    0x00000000, // PA_CL_VPORT_XSCALE_2
+    0x00000000, // PA_CL_VPORT_XOFFSET_2
+    0x00000000, // PA_CL_VPORT_YSCALE_2
+    0x00000000, // PA_CL_VPORT_YOFFSET_2
+    0x00000000, // PA_CL_VPORT_ZSCALE_2
+    0x00000000, // PA_CL_VPORT_ZOFFSET_2
+    0x00000000, // PA_CL_VPORT_XSCALE_3
+    0x00000000, // PA_CL_VPORT_XOFFSET_3
+    0x00000000, // PA_CL_VPORT_YSCALE_3
+    0x00000000, // PA_CL_VPORT_YOFFSET_3
+    0x00000000, // PA_CL_VPORT_ZSCALE_3
+    0x00000000, // PA_CL_VPORT_ZOFFSET_3
+    0x00000000, // PA_CL_VPORT_XSCALE_4
+    0x00000000, // PA_CL_VPORT_XOFFSET_4
+    0x00000000, // PA_CL_VPORT_YSCALE_4
+    0x00000000, // PA_CL_VPORT_YOFFSET_4
+    0x00000000, // PA_CL_VPORT_ZSCALE_4
+    0x00000000, // PA_CL_VPORT_ZOFFSET_4
+    0x00000000, // PA_CL_VPORT_XSCALE_5
+    0x00000000, // PA_CL_VPORT_XOFFSET_5
+    0x00000000, // PA_CL_VPORT_YSCALE_5
+    0x00000000, // PA_CL_VPORT_YOFFSET_5
+    0x00000000, // PA_CL_VPORT_ZSCALE_5
+    0x00000000, // PA_CL_VPORT_ZOFFSET_5
+    0x00000000, // PA_CL_VPORT_XSCALE_6
+    0x00000000, // PA_CL_VPORT_XOFFSET_6
+    0x00000000, // PA_CL_VPORT_YSCALE_6
+    0x00000000, // PA_CL_VPORT_YOFFSET_6
+    0x00000000, // PA_CL_VPORT_ZSCALE_6
+    0x00000000, // PA_CL_VPORT_ZOFFSET_6
+    0x00000000, // PA_CL_VPORT_XSCALE_7
+    0x00000000, // PA_CL_VPORT_XOFFSET_7
+    0x00000000, // PA_CL_VPORT_YSCALE_7
+    0x00000000, // PA_CL_VPORT_YOFFSET_7
+    0x00000000, // PA_CL_VPORT_ZSCALE_7
+    0x00000000, // PA_CL_VPORT_ZOFFSET_7
+    0x00000000, // PA_CL_VPORT_XSCALE_8
+    0x00000000, // PA_CL_VPORT_XOFFSET_8
+    0x00000000, // PA_CL_VPORT_YSCALE_8
+    0x00000000, // PA_CL_VPORT_YOFFSET_8
+    0x00000000, // PA_CL_VPORT_ZSCALE_8
+    0x00000000, // PA_CL_VPORT_ZOFFSET_8
+    0x00000000, // PA_CL_VPORT_XSCALE_9
+    0x00000000, // PA_CL_VPORT_XOFFSET_9
+    0x00000000, // PA_CL_VPORT_YSCALE_9
+    0x00000000, // PA_CL_VPORT_YOFFSET_9
+    0x00000000, // PA_CL_VPORT_ZSCALE_9
+    0x00000000, // PA_CL_VPORT_ZOFFSET_9
+    0x00000000, // PA_CL_VPORT_XSCALE_10
+    0x00000000, // PA_CL_VPORT_XOFFSET_10
+    0x00000000, // PA_CL_VPORT_YSCALE_10
+    0x00000000, // PA_CL_VPORT_YOFFSET_10
+    0x00000000, // PA_CL_VPORT_ZSCALE_10
+    0x00000000, // PA_CL_VPORT_ZOFFSET_10
+    0x00000000, // PA_CL_VPORT_XSCALE_11
+    0x00000000, // PA_CL_VPORT_XOFFSET_11
+    0x00000000, // PA_CL_VPORT_YSCALE_11
+    0x00000000, // PA_CL_VPORT_YOFFSET_11
+    0x00000000, // PA_CL_VPORT_ZSCALE_11
+    0x00000000, // PA_CL_VPORT_ZOFFSET_11
+    0x00000000, // PA_CL_VPORT_XSCALE_12
+    0x00000000, // PA_CL_VPORT_XOFFSET_12
+    0x00000000, // PA_CL_VPORT_YSCALE_12
+    0x00000000, // PA_CL_VPORT_YOFFSET_12
+    0x00000000, // PA_CL_VPORT_ZSCALE_12
+    0x00000000, // PA_CL_VPORT_ZOFFSET_12
+    0x00000000, // PA_CL_VPORT_XSCALE_13
+    0x00000000, // PA_CL_VPORT_XOFFSET_13
+    0x00000000, // PA_CL_VPORT_YSCALE_13
+    0x00000000, // PA_CL_VPORT_YOFFSET_13
+    0x00000000, // PA_CL_VPORT_ZSCALE_13
+    0x00000000, // PA_CL_VPORT_ZOFFSET_13
+    0x00000000, // PA_CL_VPORT_XSCALE_14
+    0x00000000, // PA_CL_VPORT_XOFFSET_14
+    0x00000000, // PA_CL_VPORT_YSCALE_14
+    0x00000000, // PA_CL_VPORT_YOFFSET_14
+    0x00000000, // PA_CL_VPORT_ZSCALE_14
+    0x00000000, // PA_CL_VPORT_ZOFFSET_14
+    0x00000000, // PA_CL_VPORT_XSCALE_15
+    0x00000000, // PA_CL_VPORT_XOFFSET_15
+    0x00000000, // PA_CL_VPORT_YSCALE_15
+    0x00000000, // PA_CL_VPORT_YOFFSET_15
+    0x00000000, // PA_CL_VPORT_ZSCALE_15
+    0x00000000, // PA_CL_VPORT_ZOFFSET_15
+    0x00000000, // PA_CL_UCP_0_X
+    0x00000000, // PA_CL_UCP_0_Y
+    0x00000000, // PA_CL_UCP_0_Z
+    0x00000000, // PA_CL_UCP_0_W
+    0x00000000, // PA_CL_UCP_1_X
+    0x00000000, // PA_CL_UCP_1_Y
+    0x00000000, // PA_CL_UCP_1_Z
+    0x00000000, // PA_CL_UCP_1_W
+    0x00000000, // PA_CL_UCP_2_X
+    0x00000000, // PA_CL_UCP_2_Y
+    0x00000000, // PA_CL_UCP_2_Z
+    0x00000000, // PA_CL_UCP_2_W
+    0x00000000, // PA_CL_UCP_3_X
+    0x00000000, // PA_CL_UCP_3_Y
+    0x00000000, // PA_CL_UCP_3_Z
+    0x00000000, // PA_CL_UCP_3_W
+    0x00000000, // PA_CL_UCP_4_X
+    0x00000000, // PA_CL_UCP_4_Y
+    0x00000000, // PA_CL_UCP_4_Z
+    0x00000000, // PA_CL_UCP_4_W
+    0x00000000, // PA_CL_UCP_5_X
+    0x00000000, // PA_CL_UCP_5_Y
+    0x00000000, // PA_CL_UCP_5_Z
+    0x00000000, // PA_CL_UCP_5_W
+    0x00000000, // SPI_VS_OUT_ID_0
+    0x00000000, // SPI_VS_OUT_ID_1
+    0x00000000, // SPI_VS_OUT_ID_2
+    0x00000000, // SPI_VS_OUT_ID_3
+    0x00000000, // SPI_VS_OUT_ID_4
+    0x00000000, // SPI_VS_OUT_ID_5
+    0x00000000, // SPI_VS_OUT_ID_6
+    0x00000000, // SPI_VS_OUT_ID_7
+    0x00000000, // SPI_VS_OUT_ID_8
+    0x00000000, // SPI_VS_OUT_ID_9
+    0x00000000, // SPI_PS_INPUT_CNTL_0
+    0x00000000, // SPI_PS_INPUT_CNTL_1
+    0x00000000, // SPI_PS_INPUT_CNTL_2
+    0x00000000, // SPI_PS_INPUT_CNTL_3
+    0x00000000, // SPI_PS_INPUT_CNTL_4
+    0x00000000, // SPI_PS_INPUT_CNTL_5
+    0x00000000, // SPI_PS_INPUT_CNTL_6
+    0x00000000, // SPI_PS_INPUT_CNTL_7
+    0x00000000, // SPI_PS_INPUT_CNTL_8
+    0x00000000, // SPI_PS_INPUT_CNTL_9
+    0x00000000, // SPI_PS_INPUT_CNTL_10
+    0x00000000, // SPI_PS_INPUT_CNTL_11
+    0x00000000, // SPI_PS_INPUT_CNTL_12
+    0x00000000, // SPI_PS_INPUT_CNTL_13
+    0x00000000, // SPI_PS_INPUT_CNTL_14
+    0x00000000, // SPI_PS_INPUT_CNTL_15
+    0x00000000, // SPI_PS_INPUT_CNTL_16
+    0x00000000, // SPI_PS_INPUT_CNTL_17
+    0x00000000, // SPI_PS_INPUT_CNTL_18
+    0x00000000, // SPI_PS_INPUT_CNTL_19
+    0x00000000, // SPI_PS_INPUT_CNTL_20
+    0x00000000, // SPI_PS_INPUT_CNTL_21
+    0x00000000, // SPI_PS_INPUT_CNTL_22
+    0x00000000, // SPI_PS_INPUT_CNTL_23
+    0x00000000, // SPI_PS_INPUT_CNTL_24
+    0x00000000, // SPI_PS_INPUT_CNTL_25
+    0x00000000, // SPI_PS_INPUT_CNTL_26
+    0x00000000, // SPI_PS_INPUT_CNTL_27
+    0x00000000, // SPI_PS_INPUT_CNTL_28
+    0x00000000, // SPI_PS_INPUT_CNTL_29
+    0x00000000, // SPI_PS_INPUT_CNTL_30
+    0x00000000, // SPI_PS_INPUT_CNTL_31
+    0x00000000, // SPI_VS_OUT_CONFIG
+    0x00000001, // SPI_THREAD_GROUPING
+    0x00000000, // SPI_PS_IN_CONTROL_0
+    0x00000000, // SPI_PS_IN_CONTROL_1
+    0x00000000, // SPI_INTERP_CONTROL_0
+    0x00000000, // SPI_INPUT_Z
+    0x00000000, // SPI_FOG_CNTL
+    0x00000000, // SPI_BARYC_CNTL
+    0x00000000, // SPI_PS_IN_CONTROL_2
+    0x00000000, // SPI_COMPUTE_INPUT_CNTL
+    0x00000000, // SPI_COMPUTE_NUM_THREAD_X
+    0x00000000, // SPI_COMPUTE_NUM_THREAD_Y
+    0x00000000, // SPI_COMPUTE_NUM_THREAD_Z
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // GDS_ADDR_BASE
+    0x00003fff, // GDS_ADDR_SIZE
+    0x00000001, // GDS_ORDERED_WAVE_PER_SE
+    0x00000000, // GDS_APPEND_CONSUME_UAV0
+    0x00000000, // GDS_APPEND_CONSUME_UAV1
+    0x00000000, // GDS_APPEND_CONSUME_UAV2
+    0x00000000, // GDS_APPEND_CONSUME_UAV3
+    0x00000000, // GDS_APPEND_CONSUME_UAV4
+    0x00000000, // GDS_APPEND_CONSUME_UAV5
+    0x00000000, // GDS_APPEND_CONSUME_UAV6
+    0x00000000, // GDS_APPEND_CONSUME_UAV7
+    0x00000000, // GDS_APPEND_CONSUME_UAV8
+    0x00000000, // GDS_APPEND_CONSUME_UAV9
+    0x00000000, // GDS_APPEND_CONSUME_UAV10
+    0x00000000, // GDS_APPEND_CONSUME_UAV11
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_BLEND0_CONTROL
+    0x00000000, // CB_BLEND1_CONTROL
+    0x00000000, // CB_BLEND2_CONTROL
+    0x00000000, // CB_BLEND3_CONTROL
+    0x00000000, // CB_BLEND4_CONTROL
+    0x00000000, // CB_BLEND5_CONTROL
+    0x00000000, // CB_BLEND6_CONTROL
+    0x00000000, // CB_BLEND7_CONTROL
+};
+static const u32 SECT_CONTEXT_def_2[] =
+{
+    0x00000000, // PA_CL_POINT_X_RAD
+    0x00000000, // PA_CL_POINT_Y_RAD
+    0x00000000, // PA_CL_POINT_SIZE
+    0x00000000, // PA_CL_POINT_CULL_RAD
+    0x00000000, // VGT_DMA_BASE_HI
+    0x00000000, // VGT_DMA_BASE
+};
+static const u32 SECT_CONTEXT_def_3[] =
+{
+    0x00000000, // DB_DEPTH_CONTROL
+    0, // HOLE
+    0x00000000, // CB_COLOR_CONTROL
+    0x00000200, // DB_SHADER_CONTROL
+    0x00000000, // PA_CL_CLIP_CNTL
+    0x00000000, // PA_SU_SC_MODE_CNTL
+    0x00000000, // PA_CL_VTE_CNTL
+    0x00000000, // PA_CL_VS_OUT_CNTL
+    0x00000000, // PA_CL_NANINF_CNTL
+    0x00000000, // PA_SU_LINE_STIPPLE_CNTL
+    0x00000000, // PA_SU_LINE_STIPPLE_SCALE
+    0x00000000, // PA_SU_PRIM_FILTER_CNTL
+    0x00000000, // SQ_LSTMP_RING_ITEMSIZE
+    0x00000000, // SQ_HSTMP_RING_ITEMSIZE
+    0x00000000, // SQ_DYN_GPR_RESOURCE_LIMIT_1
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_PS
+    0x00000000, // SQ_PGM_RESOURCES_PS
+    0x00000000, // SQ_PGM_RESOURCES_2_PS
+    0x00000000, // SQ_PGM_EXPORTS_PS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_VS
+    0x00000000, // SQ_PGM_RESOURCES_VS
+    0x00000000, // SQ_PGM_RESOURCES_2_VS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_GS
+    0x00000000, // SQ_PGM_RESOURCES_GS
+    0x00000000, // SQ_PGM_RESOURCES_2_GS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_ES
+    0x00000000, // SQ_PGM_RESOURCES_ES
+    0x00000000, // SQ_PGM_RESOURCES_2_ES
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_FS
+    0x00000000, // SQ_PGM_RESOURCES_FS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_HS
+    0x00000000, // SQ_PGM_RESOURCES_HS
+    0x00000000, // SQ_PGM_RESOURCES_2_HS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_PGM_START_LS
+    0x00000000, // SQ_PGM_RESOURCES_LS
+    0x00000000, // SQ_PGM_RESOURCES_2_LS
+};
+static const u32 SECT_CONTEXT_def_4[] =
+{
+    0x00000000, // SQ_LDS_ALLOC
+    0x00000000, // SQ_LDS_ALLOC_PS
+    0x00000000, // SQ_VTX_SEMANTIC_CLEAR
+    0, // HOLE
+    0x00000000, // SQ_THREAD_TRACE_CTRL
+    0, // HOLE
+    0x00000000, // SQ_ESGS_RING_ITEMSIZE
+    0x00000000, // SQ_GSVS_RING_ITEMSIZE
+    0x00000000, // SQ_ESTMP_RING_ITEMSIZE
+    0x00000000, // SQ_GSTMP_RING_ITEMSIZE
+    0x00000000, // SQ_VSTMP_RING_ITEMSIZE
+    0x00000000, // SQ_PSTMP_RING_ITEMSIZE
+    0, // HOLE
+    0x00000000, // SQ_GS_VERT_ITEMSIZE
+    0x00000000, // SQ_GS_VERT_ITEMSIZE_1
+    0x00000000, // SQ_GS_VERT_ITEMSIZE_2
+    0x00000000, // SQ_GS_VERT_ITEMSIZE_3
+    0x00000000, // SQ_GSVS_RING_OFFSET_1
+    0x00000000, // SQ_GSVS_RING_OFFSET_2
+    0x00000000, // SQ_GSVS_RING_OFFSET_3
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_0
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_1
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_2
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_3
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_4
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_5
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_6
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_7
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_8
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_9
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_10
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_11
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_12
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_13
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_14
+    0x00000000, // SQ_ALU_CONST_CACHE_PS_15
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_0
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_1
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_2
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_3
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_4
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_5
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_6
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_7
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_8
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_9
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_10
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_11
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_12
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_13
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_14
+    0x00000000, // SQ_ALU_CONST_CACHE_VS_15
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_0
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_1
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_2
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_3
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_4
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_5
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_6
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_7
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_8
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_9
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_10
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_11
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_12
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_13
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_14
+    0x00000000, // SQ_ALU_CONST_CACHE_GS_15
+    0x00000000, // PA_SU_POINT_SIZE
+    0x00000000, // PA_SU_POINT_MINMAX
+    0x00000000, // PA_SU_LINE_CNTL
+    0x00000000, // PA_SC_LINE_STIPPLE
+    0x00000000, // VGT_OUTPUT_PATH_CNTL
+    0x00000000, // VGT_HOS_CNTL
+    0x00000000, // VGT_HOS_MAX_TESS_LEVEL
+    0x00000000, // VGT_HOS_MIN_TESS_LEVEL
+    0x00000000, // VGT_HOS_REUSE_DEPTH
+    0x00000000, // VGT_GROUP_PRIM_TYPE
+    0x00000000, // VGT_GROUP_FIRST_DECR
+    0x00000000, // VGT_GROUP_DECR
+    0x00000000, // VGT_GROUP_VECT_0_CNTL
+    0x00000000, // VGT_GROUP_VECT_1_CNTL
+    0x00000000, // VGT_GROUP_VECT_0_FMT_CNTL
+    0x00000000, // VGT_GROUP_VECT_1_FMT_CNTL
+    0x00000000, // VGT_GS_MODE
+    0, // HOLE
+    0x00000000, // PA_SC_MODE_CNTL_0
+    0x00000000, // PA_SC_MODE_CNTL_1
+    0x00000000, // VGT_ENHANCE
+    0x00000000, // VGT_GS_PER_ES
+    0x00000000, // VGT_ES_PER_GS
+    0x00000000, // VGT_GS_PER_VS
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_GS_OUT_PRIM_TYPE
+};
+static const u32 SECT_CONTEXT_def_5[] =
+{
+    0x00000000, // VGT_DMA_MAX_SIZE
+    0x00000000, // VGT_DMA_INDEX_TYPE
+    0, // HOLE
+    0x00000000, // VGT_PRIMITIVEID_EN
+    0x00000000, // VGT_DMA_NUM_INSTANCES
+};
+static const u32 SECT_CONTEXT_def_6[] =
+{
+    0x00000000, // VGT_MULTI_PRIM_IB_RESET_EN
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_INSTANCE_STEP_RATE_0
+    0x00000000, // VGT_INSTANCE_STEP_RATE_1
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_REUSE_OFF
+    0x00000000, // VGT_VTX_CNT_EN
+    0x00000000, // DB_HTILE_SURFACE
+    0x00000000, // DB_SRESULTS_COMPARE_STATE0
+    0x00000000, // DB_SRESULTS_COMPARE_STATE1
+    0x00000000, // DB_PRELOAD_CONTROL
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_0
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_0
+    0x00000000, // VGT_STRMOUT_BUFFER_BASE_0
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_0
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_1
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_1
+    0x00000000, // VGT_STRMOUT_BUFFER_BASE_1
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_1
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_2
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_2
+    0x00000000, // VGT_STRMOUT_BUFFER_BASE_2
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_2
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_3
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_3
+    0x00000000, // VGT_STRMOUT_BUFFER_BASE_3
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_3
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_0
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_1
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_2
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_3
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_OFFSET
+    0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE
+    0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE
+    0, // HOLE
+    0x00000000, // VGT_GS_MAX_VERT_OUT
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_0
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_1
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_2
+    0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_3
+    0x00000000, // VGT_SHADER_STAGES_EN
+    0x00000000, // VGT_LS_HS_CONFIG
+    0x00000000, // VGT_LS_SIZE
+    0x00000000, // VGT_HS_SIZE
+    0x00000000, // VGT_LS_HS_ALLOC
+    0x00000000, // VGT_HS_PATCH_CONST
+    0x00000000, // VGT_TF_PARAM
+    0x00000000, // DB_ALPHA_TO_MASK
+};
+static const u32 SECT_CONTEXT_def_7[] =
+{
+    0x00000000, // PA_SU_POLY_OFFSET_DB_FMT_CNTL
+    0x00000000, // PA_SU_POLY_OFFSET_CLAMP
+    0x00000000, // PA_SU_POLY_OFFSET_FRONT_SCALE
+    0x00000000, // PA_SU_POLY_OFFSET_FRONT_OFFSET
+    0x00000000, // PA_SU_POLY_OFFSET_BACK_SCALE
+    0x00000000, // PA_SU_POLY_OFFSET_BACK_OFFSET
+    0x00000000, // VGT_GS_INSTANCE_CNT
+    0x00000000, // VGT_STRMOUT_CONFIG
+    0x00000000, // VGT_STRMOUT_BUFFER_CONFIG
+    0x00000000, // CB_IMMED0_BASE
+    0x00000000, // CB_IMMED1_BASE
+    0x00000000, // CB_IMMED2_BASE
+    0x00000000, // CB_IMMED3_BASE
+    0x00000000, // CB_IMMED4_BASE
+    0x00000000, // CB_IMMED5_BASE
+    0x00000000, // CB_IMMED6_BASE
+    0x00000000, // CB_IMMED7_BASE
+    0x00000000, // CB_IMMED8_BASE
+    0x00000000, // CB_IMMED9_BASE
+    0x00000000, // CB_IMMED10_BASE
+    0x00000000, // CB_IMMED11_BASE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00001000, // PA_SC_LINE_CNTL
+    0x00000000, // PA_SC_AA_CONFIG
+    0x00000005, // PA_SU_VTX_CNTL
+    0x3f800000, // PA_CL_GB_VERT_CLIP_ADJ
+    0x3f800000, // PA_CL_GB_VERT_DISC_ADJ
+    0x3f800000, // PA_CL_GB_HORZ_CLIP_ADJ
+    0x3f800000, // PA_CL_GB_HORZ_DISC_ADJ
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_0
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_1
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_2
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_3
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_4
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_5
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_6
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_7
+    0xffffffff, // PA_SC_AA_MASK
+    0x00000000, // CB_CLRCMP_CONTROL
+    0x00000000, // CB_CLRCMP_SRC
+    0x00000000, // CB_CLRCMP_DST
+    0x00000000, // CB_CLRCMP_MSK
+    0, // HOLE
+    0, // HOLE
+    0x0000000e, // VGT_VERTEX_REUSE_BLOCK_CNTL
+    0x00000010, // VGT_OUT_DEALLOC_CNTL
+    0x00000000, // CB_COLOR0_BASE
+    0x00000000, // CB_COLOR0_PITCH
+    0x00000000, // CB_COLOR0_SLICE
+    0x00000000, // CB_COLOR0_VIEW
+    0x00000000, // CB_COLOR0_INFO
+    0x00000000, // CB_COLOR0_ATTRIB
+    0x00000000, // CB_COLOR0_DIM
+    0x00000000, // CB_COLOR0_CMASK
+    0x00000000, // CB_COLOR0_CMASK_SLICE
+    0x00000000, // CB_COLOR0_FMASK
+    0x00000000, // CB_COLOR0_FMASK_SLICE
+    0x00000000, // CB_COLOR0_CLEAR_WORD0
+    0x00000000, // CB_COLOR0_CLEAR_WORD1
+    0x00000000, // CB_COLOR0_CLEAR_WORD2
+    0x00000000, // CB_COLOR0_CLEAR_WORD3
+    0x00000000, // CB_COLOR1_BASE
+    0x00000000, // CB_COLOR1_PITCH
+    0x00000000, // CB_COLOR1_SLICE
+    0x00000000, // CB_COLOR1_VIEW
+    0x00000000, // CB_COLOR1_INFO
+    0x00000000, // CB_COLOR1_ATTRIB
+    0x00000000, // CB_COLOR1_DIM
+    0x00000000, // CB_COLOR1_CMASK
+    0x00000000, // CB_COLOR1_CMASK_SLICE
+    0x00000000, // CB_COLOR1_FMASK
+    0x00000000, // CB_COLOR1_FMASK_SLICE
+    0x00000000, // CB_COLOR1_CLEAR_WORD0
+    0x00000000, // CB_COLOR1_CLEAR_WORD1
+    0x00000000, // CB_COLOR1_CLEAR_WORD2
+    0x00000000, // CB_COLOR1_CLEAR_WORD3
+    0x00000000, // CB_COLOR2_BASE
+    0x00000000, // CB_COLOR2_PITCH
+    0x00000000, // CB_COLOR2_SLICE
+    0x00000000, // CB_COLOR2_VIEW
+    0x00000000, // CB_COLOR2_INFO
+    0x00000000, // CB_COLOR2_ATTRIB
+    0x00000000, // CB_COLOR2_DIM
+    0x00000000, // CB_COLOR2_CMASK
+    0x00000000, // CB_COLOR2_CMASK_SLICE
+    0x00000000, // CB_COLOR2_FMASK
+    0x00000000, // CB_COLOR2_FMASK_SLICE
+    0x00000000, // CB_COLOR2_CLEAR_WORD0
+    0x00000000, // CB_COLOR2_CLEAR_WORD1
+    0x00000000, // CB_COLOR2_CLEAR_WORD2
+    0x00000000, // CB_COLOR2_CLEAR_WORD3
+    0x00000000, // CB_COLOR3_BASE
+    0x00000000, // CB_COLOR3_PITCH
+    0x00000000, // CB_COLOR3_SLICE
+    0x00000000, // CB_COLOR3_VIEW
+    0x00000000, // CB_COLOR3_INFO
+    0x00000000, // CB_COLOR3_ATTRIB
+    0x00000000, // CB_COLOR3_DIM
+    0x00000000, // CB_COLOR3_CMASK
+    0x00000000, // CB_COLOR3_CMASK_SLICE
+    0x00000000, // CB_COLOR3_FMASK
+    0x00000000, // CB_COLOR3_FMASK_SLICE
+    0x00000000, // CB_COLOR3_CLEAR_WORD0
+    0x00000000, // CB_COLOR3_CLEAR_WORD1
+    0x00000000, // CB_COLOR3_CLEAR_WORD2
+    0x00000000, // CB_COLOR3_CLEAR_WORD3
+    0x00000000, // CB_COLOR4_BASE
+    0x00000000, // CB_COLOR4_PITCH
+    0x00000000, // CB_COLOR4_SLICE
+    0x00000000, // CB_COLOR4_VIEW
+    0x00000000, // CB_COLOR4_INFO
+    0x00000000, // CB_COLOR4_ATTRIB
+    0x00000000, // CB_COLOR4_DIM
+    0x00000000, // CB_COLOR4_CMASK
+    0x00000000, // CB_COLOR4_CMASK_SLICE
+    0x00000000, // CB_COLOR4_FMASK
+    0x00000000, // CB_COLOR4_FMASK_SLICE
+    0x00000000, // CB_COLOR4_CLEAR_WORD0
+    0x00000000, // CB_COLOR4_CLEAR_WORD1
+    0x00000000, // CB_COLOR4_CLEAR_WORD2
+    0x00000000, // CB_COLOR4_CLEAR_WORD3
+    0x00000000, // CB_COLOR5_BASE
+    0x00000000, // CB_COLOR5_PITCH
+    0x00000000, // CB_COLOR5_SLICE
+    0x00000000, // CB_COLOR5_VIEW
+    0x00000000, // CB_COLOR5_INFO
+    0x00000000, // CB_COLOR5_ATTRIB
+    0x00000000, // CB_COLOR5_DIM
+    0x00000000, // CB_COLOR5_CMASK
+    0x00000000, // CB_COLOR5_CMASK_SLICE
+    0x00000000, // CB_COLOR5_FMASK
+    0x00000000, // CB_COLOR5_FMASK_SLICE
+    0x00000000, // CB_COLOR5_CLEAR_WORD0
+    0x00000000, // CB_COLOR5_CLEAR_WORD1
+    0x00000000, // CB_COLOR5_CLEAR_WORD2
+    0x00000000, // CB_COLOR5_CLEAR_WORD3
+    0x00000000, // CB_COLOR6_BASE
+    0x00000000, // CB_COLOR6_PITCH
+    0x00000000, // CB_COLOR6_SLICE
+    0x00000000, // CB_COLOR6_VIEW
+    0x00000000, // CB_COLOR6_INFO
+    0x00000000, // CB_COLOR6_ATTRIB
+    0x00000000, // CB_COLOR6_DIM
+    0x00000000, // CB_COLOR6_CMASK
+    0x00000000, // CB_COLOR6_CMASK_SLICE
+    0x00000000, // CB_COLOR6_FMASK
+    0x00000000, // CB_COLOR6_FMASK_SLICE
+    0x00000000, // CB_COLOR6_CLEAR_WORD0
+    0x00000000, // CB_COLOR6_CLEAR_WORD1
+    0x00000000, // CB_COLOR6_CLEAR_WORD2
+    0x00000000, // CB_COLOR6_CLEAR_WORD3
+    0x00000000, // CB_COLOR7_BASE
+    0x00000000, // CB_COLOR7_PITCH
+    0x00000000, // CB_COLOR7_SLICE
+    0x00000000, // CB_COLOR7_VIEW
+    0x00000000, // CB_COLOR7_INFO
+    0x00000000, // CB_COLOR7_ATTRIB
+    0x00000000, // CB_COLOR7_DIM
+    0x00000000, // CB_COLOR7_CMASK
+    0x00000000, // CB_COLOR7_CMASK_SLICE
+    0x00000000, // CB_COLOR7_FMASK
+    0x00000000, // CB_COLOR7_FMASK_SLICE
+    0x00000000, // CB_COLOR7_CLEAR_WORD0
+    0x00000000, // CB_COLOR7_CLEAR_WORD1
+    0x00000000, // CB_COLOR7_CLEAR_WORD2
+    0x00000000, // CB_COLOR7_CLEAR_WORD3
+    0x00000000, // CB_COLOR8_BASE
+    0x00000000, // CB_COLOR8_PITCH
+    0x00000000, // CB_COLOR8_SLICE
+    0x00000000, // CB_COLOR8_VIEW
+    0x00000000, // CB_COLOR8_INFO
+    0x00000000, // CB_COLOR8_ATTRIB
+    0x00000000, // CB_COLOR8_DIM
+    0x00000000, // CB_COLOR9_BASE
+    0x00000000, // CB_COLOR9_PITCH
+    0x00000000, // CB_COLOR9_SLICE
+    0x00000000, // CB_COLOR9_VIEW
+    0x00000000, // CB_COLOR9_INFO
+    0x00000000, // CB_COLOR9_ATTRIB
+    0x00000000, // CB_COLOR9_DIM
+    0x00000000, // CB_COLOR10_BASE
+    0x00000000, // CB_COLOR10_PITCH
+    0x00000000, // CB_COLOR10_SLICE
+    0x00000000, // CB_COLOR10_VIEW
+    0x00000000, // CB_COLOR10_INFO
+    0x00000000, // CB_COLOR10_ATTRIB
+    0x00000000, // CB_COLOR10_DIM
+    0x00000000, // CB_COLOR11_BASE
+    0x00000000, // CB_COLOR11_PITCH
+    0x00000000, // CB_COLOR11_SLICE
+    0x00000000, // CB_COLOR11_VIEW
+    0x00000000, // CB_COLOR11_INFO
+    0x00000000, // CB_COLOR11_ATTRIB
+    0x00000000, // CB_COLOR11_DIM
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_0
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_1
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_2
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_3
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_4
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_5
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_6
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_7
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_8
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_9
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_10
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_11
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_12
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_13
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_14
+    0x00000000, // SQ_ALU_CONST_CACHE_HS_15
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_0
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_1
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_2
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_3
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_4
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_5
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_6
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_7
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_8
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_9
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_10
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_11
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_12
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_13
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_14
+    0x00000000, // SQ_ALU_CONST_CACHE_LS_15
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_0
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_1
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_2
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_3
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_4
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_5
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_6
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_7
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_8
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_9
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_10
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_11
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_12
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_13
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_14
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_15
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_0
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_1
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_2
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_3
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_4
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_5
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_6
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_7
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_8
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_9
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_10
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_11
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_12
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_13
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_14
+    0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_15
+};
+static const struct cs_extent_def SECT_CONTEXT_defs[] =
+{
+    {SECT_CONTEXT_def_1, 0x0000a000, 488 },
+    {SECT_CONTEXT_def_2, 0x0000a1f5, 6 },
+    {SECT_CONTEXT_def_3, 0x0000a200, 55 },
+    {SECT_CONTEXT_def_4, 0x0000a23a, 98 },
+    {SECT_CONTEXT_def_5, 0x0000a29e, 5 },
+    {SECT_CONTEXT_def_6, 0x0000a2a5, 56 },
+    {SECT_CONTEXT_def_7, 0x0000a2de, 290 },
+    { 0, 0, 0 }
+};
+static const u32 SECT_CLEAR_def_1[] =
+{
+    0xffffffff, // SQ_TEX_SAMPLER_CLEAR
+    0xffffffff, // SQ_TEX_RESOURCE_CLEAR
+    0xffffffff, // SQ_LOOP_BOOL_CLEAR
+};
+static const struct cs_extent_def SECT_CLEAR_defs[] =
+{
+    {SECT_CLEAR_def_1, 0x0000ffc0, 3 },
+    { 0, 0, 0 }
+};
+static const u32 SECT_CTRLCONST_def_1[] =
+{
+    0x00000000, // SQ_VTX_BASE_VTX_LOC
+    0x00000000, // SQ_VTX_START_INST_LOC
+};
+static const struct cs_extent_def SECT_CTRLCONST_defs[] =
+{
+    {SECT_CTRLCONST_def_1, 0x0000f3fc, 2 },
+    { 0, 0, 0 }
+};
+struct cs_section_def evergreen_cs_data[] = {
+    { SECT_CONTEXT_defs, SECT_CONTEXT },
+    { SECT_CLEAR_defs, SECT_CLEAR },
+    { SECT_CTRLCONST_defs, SECT_CTRLCONST },
+    { 0, SECT_NONE }
+};
diff --git a/drivers/gpu/drm/radeon/clearstate_si.h b/drivers/gpu/drm/radeon/clearstate_si.h
new file mode 100644 (file)
index 0000000..b994cb2
--- /dev/null
@@ -0,0 +1,941 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, 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.
+ *
+ */
+
+static const u32 si_SECT_CONTEXT_def_1[] =
+{
+    0x00000000, // DB_RENDER_CONTROL
+    0x00000000, // DB_COUNT_CONTROL
+    0x00000000, // DB_DEPTH_VIEW
+    0x00000000, // DB_RENDER_OVERRIDE
+    0x00000000, // DB_RENDER_OVERRIDE2
+    0x00000000, // DB_HTILE_DATA_BASE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // DB_DEPTH_BOUNDS_MIN
+    0x00000000, // DB_DEPTH_BOUNDS_MAX
+    0x00000000, // DB_STENCIL_CLEAR
+    0x00000000, // DB_DEPTH_CLEAR
+    0x00000000, // PA_SC_SCREEN_SCISSOR_TL
+    0x40004000, // PA_SC_SCREEN_SCISSOR_BR
+    0, // HOLE
+    0x00000000, // DB_DEPTH_INFO
+    0x00000000, // DB_Z_INFO
+    0x00000000, // DB_STENCIL_INFO
+    0x00000000, // DB_Z_READ_BASE
+    0x00000000, // DB_STENCIL_READ_BASE
+    0x00000000, // DB_Z_WRITE_BASE
+    0x00000000, // DB_STENCIL_WRITE_BASE
+    0x00000000, // DB_DEPTH_SIZE
+    0x00000000, // DB_DEPTH_SLICE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // TA_BC_BASE_ADDR
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // COHER_DEST_BASE_2
+    0x00000000, // COHER_DEST_BASE_3
+    0x00000000, // PA_SC_WINDOW_OFFSET
+    0x80000000, // PA_SC_WINDOW_SCISSOR_TL
+    0x40004000, // PA_SC_WINDOW_SCISSOR_BR
+    0x0000ffff, // PA_SC_CLIPRECT_RULE
+    0x00000000, // PA_SC_CLIPRECT_0_TL
+    0x40004000, // PA_SC_CLIPRECT_0_BR
+    0x00000000, // PA_SC_CLIPRECT_1_TL
+    0x40004000, // PA_SC_CLIPRECT_1_BR
+    0x00000000, // PA_SC_CLIPRECT_2_TL
+    0x40004000, // PA_SC_CLIPRECT_2_BR
+    0x00000000, // PA_SC_CLIPRECT_3_TL
+    0x40004000, // PA_SC_CLIPRECT_3_BR
+    0xaa99aaaa, // PA_SC_EDGERULE
+    0x00000000, // PA_SU_HARDWARE_SCREEN_OFFSET
+    0xffffffff, // CB_TARGET_MASK
+    0xffffffff, // CB_SHADER_MASK
+    0x80000000, // PA_SC_GENERIC_SCISSOR_TL
+    0x40004000, // PA_SC_GENERIC_SCISSOR_BR
+    0x00000000, // COHER_DEST_BASE_0
+    0x00000000, // COHER_DEST_BASE_1
+    0x80000000, // PA_SC_VPORT_SCISSOR_0_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_0_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_1_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_1_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_2_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_2_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_3_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_3_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_4_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_4_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_5_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_5_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_6_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_6_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_7_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_7_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_8_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_8_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_9_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_9_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_10_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_10_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_11_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_11_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_12_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_12_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_13_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_13_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_14_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_14_BR
+    0x80000000, // PA_SC_VPORT_SCISSOR_15_TL
+    0x40004000, // PA_SC_VPORT_SCISSOR_15_BR
+    0x00000000, // PA_SC_VPORT_ZMIN_0
+    0x3f800000, // PA_SC_VPORT_ZMAX_0
+    0x00000000, // PA_SC_VPORT_ZMIN_1
+    0x3f800000, // PA_SC_VPORT_ZMAX_1
+    0x00000000, // PA_SC_VPORT_ZMIN_2
+    0x3f800000, // PA_SC_VPORT_ZMAX_2
+    0x00000000, // PA_SC_VPORT_ZMIN_3
+    0x3f800000, // PA_SC_VPORT_ZMAX_3
+    0x00000000, // PA_SC_VPORT_ZMIN_4
+    0x3f800000, // PA_SC_VPORT_ZMAX_4
+    0x00000000, // PA_SC_VPORT_ZMIN_5
+    0x3f800000, // PA_SC_VPORT_ZMAX_5
+    0x00000000, // PA_SC_VPORT_ZMIN_6
+    0x3f800000, // PA_SC_VPORT_ZMAX_6
+    0x00000000, // PA_SC_VPORT_ZMIN_7
+    0x3f800000, // PA_SC_VPORT_ZMAX_7
+    0x00000000, // PA_SC_VPORT_ZMIN_8
+    0x3f800000, // PA_SC_VPORT_ZMAX_8
+    0x00000000, // PA_SC_VPORT_ZMIN_9
+    0x3f800000, // PA_SC_VPORT_ZMAX_9
+    0x00000000, // PA_SC_VPORT_ZMIN_10
+    0x3f800000, // PA_SC_VPORT_ZMAX_10
+    0x00000000, // PA_SC_VPORT_ZMIN_11
+    0x3f800000, // PA_SC_VPORT_ZMAX_11
+    0x00000000, // PA_SC_VPORT_ZMIN_12
+    0x3f800000, // PA_SC_VPORT_ZMAX_12
+    0x00000000, // PA_SC_VPORT_ZMIN_13
+    0x3f800000, // PA_SC_VPORT_ZMAX_13
+    0x00000000, // PA_SC_VPORT_ZMIN_14
+    0x3f800000, // PA_SC_VPORT_ZMAX_14
+    0x00000000, // PA_SC_VPORT_ZMIN_15
+    0x3f800000, // PA_SC_VPORT_ZMAX_15
+};
+static const u32 si_SECT_CONTEXT_def_2[] =
+{
+    0x00000000, // CP_PERFMON_CNTX_CNTL
+    0x00000000, // CP_RINGID
+    0x00000000, // CP_VMID
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0xffffffff, // VGT_MAX_VTX_INDX
+    0x00000000, // VGT_MIN_VTX_INDX
+    0x00000000, // VGT_INDX_OFFSET
+    0x00000000, // VGT_MULTI_PRIM_IB_RESET_INDX
+    0, // HOLE
+    0x00000000, // CB_BLEND_RED
+    0x00000000, // CB_BLEND_GREEN
+    0x00000000, // CB_BLEND_BLUE
+    0x00000000, // CB_BLEND_ALPHA
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // DB_STENCIL_CONTROL
+    0x00000000, // DB_STENCILREFMASK
+    0x00000000, // DB_STENCILREFMASK_BF
+    0, // HOLE
+    0x00000000, // PA_CL_VPORT_XSCALE
+    0x00000000, // PA_CL_VPORT_XOFFSET
+    0x00000000, // PA_CL_VPORT_YSCALE
+    0x00000000, // PA_CL_VPORT_YOFFSET
+    0x00000000, // PA_CL_VPORT_ZSCALE
+    0x00000000, // PA_CL_VPORT_ZOFFSET
+    0x00000000, // PA_CL_VPORT_XSCALE_1
+    0x00000000, // PA_CL_VPORT_XOFFSET_1
+    0x00000000, // PA_CL_VPORT_YSCALE_1
+    0x00000000, // PA_CL_VPORT_YOFFSET_1
+    0x00000000, // PA_CL_VPORT_ZSCALE_1
+    0x00000000, // PA_CL_VPORT_ZOFFSET_1
+    0x00000000, // PA_CL_VPORT_XSCALE_2
+    0x00000000, // PA_CL_VPORT_XOFFSET_2
+    0x00000000, // PA_CL_VPORT_YSCALE_2
+    0x00000000, // PA_CL_VPORT_YOFFSET_2
+    0x00000000, // PA_CL_VPORT_ZSCALE_2
+    0x00000000, // PA_CL_VPORT_ZOFFSET_2
+    0x00000000, // PA_CL_VPORT_XSCALE_3
+    0x00000000, // PA_CL_VPORT_XOFFSET_3
+    0x00000000, // PA_CL_VPORT_YSCALE_3
+    0x00000000, // PA_CL_VPORT_YOFFSET_3
+    0x00000000, // PA_CL_VPORT_ZSCALE_3
+    0x00000000, // PA_CL_VPORT_ZOFFSET_3
+    0x00000000, // PA_CL_VPORT_XSCALE_4
+    0x00000000, // PA_CL_VPORT_XOFFSET_4
+    0x00000000, // PA_CL_VPORT_YSCALE_4
+    0x00000000, // PA_CL_VPORT_YOFFSET_4
+    0x00000000, // PA_CL_VPORT_ZSCALE_4
+    0x00000000, // PA_CL_VPORT_ZOFFSET_4
+    0x00000000, // PA_CL_VPORT_XSCALE_5
+    0x00000000, // PA_CL_VPORT_XOFFSET_5
+    0x00000000, // PA_CL_VPORT_YSCALE_5
+    0x00000000, // PA_CL_VPORT_YOFFSET_5
+    0x00000000, // PA_CL_VPORT_ZSCALE_5
+    0x00000000, // PA_CL_VPORT_ZOFFSET_5
+    0x00000000, // PA_CL_VPORT_XSCALE_6
+    0x00000000, // PA_CL_VPORT_XOFFSET_6
+    0x00000000, // PA_CL_VPORT_YSCALE_6
+    0x00000000, // PA_CL_VPORT_YOFFSET_6
+    0x00000000, // PA_CL_VPORT_ZSCALE_6
+    0x00000000, // PA_CL_VPORT_ZOFFSET_6
+    0x00000000, // PA_CL_VPORT_XSCALE_7
+    0x00000000, // PA_CL_VPORT_XOFFSET_7
+    0x00000000, // PA_CL_VPORT_YSCALE_7
+    0x00000000, // PA_CL_VPORT_YOFFSET_7
+    0x00000000, // PA_CL_VPORT_ZSCALE_7
+    0x00000000, // PA_CL_VPORT_ZOFFSET_7
+    0x00000000, // PA_CL_VPORT_XSCALE_8
+    0x00000000, // PA_CL_VPORT_XOFFSET_8
+    0x00000000, // PA_CL_VPORT_YSCALE_8
+    0x00000000, // PA_CL_VPORT_YOFFSET_8
+    0x00000000, // PA_CL_VPORT_ZSCALE_8
+    0x00000000, // PA_CL_VPORT_ZOFFSET_8
+    0x00000000, // PA_CL_VPORT_XSCALE_9
+    0x00000000, // PA_CL_VPORT_XOFFSET_9
+    0x00000000, // PA_CL_VPORT_YSCALE_9
+    0x00000000, // PA_CL_VPORT_YOFFSET_9
+    0x00000000, // PA_CL_VPORT_ZSCALE_9
+    0x00000000, // PA_CL_VPORT_ZOFFSET_9
+    0x00000000, // PA_CL_VPORT_XSCALE_10
+    0x00000000, // PA_CL_VPORT_XOFFSET_10
+    0x00000000, // PA_CL_VPORT_YSCALE_10
+    0x00000000, // PA_CL_VPORT_YOFFSET_10
+    0x00000000, // PA_CL_VPORT_ZSCALE_10
+    0x00000000, // PA_CL_VPORT_ZOFFSET_10
+    0x00000000, // PA_CL_VPORT_XSCALE_11
+    0x00000000, // PA_CL_VPORT_XOFFSET_11
+    0x00000000, // PA_CL_VPORT_YSCALE_11
+    0x00000000, // PA_CL_VPORT_YOFFSET_11
+    0x00000000, // PA_CL_VPORT_ZSCALE_11
+    0x00000000, // PA_CL_VPORT_ZOFFSET_11
+    0x00000000, // PA_CL_VPORT_XSCALE_12
+    0x00000000, // PA_CL_VPORT_XOFFSET_12
+    0x00000000, // PA_CL_VPORT_YSCALE_12
+    0x00000000, // PA_CL_VPORT_YOFFSET_12
+    0x00000000, // PA_CL_VPORT_ZSCALE_12
+    0x00000000, // PA_CL_VPORT_ZOFFSET_12
+    0x00000000, // PA_CL_VPORT_XSCALE_13
+    0x00000000, // PA_CL_VPORT_XOFFSET_13
+    0x00000000, // PA_CL_VPORT_YSCALE_13
+    0x00000000, // PA_CL_VPORT_YOFFSET_13
+    0x00000000, // PA_CL_VPORT_ZSCALE_13
+    0x00000000, // PA_CL_VPORT_ZOFFSET_13
+    0x00000000, // PA_CL_VPORT_XSCALE_14
+    0x00000000, // PA_CL_VPORT_XOFFSET_14
+    0x00000000, // PA_CL_VPORT_YSCALE_14
+    0x00000000, // PA_CL_VPORT_YOFFSET_14
+    0x00000000, // PA_CL_VPORT_ZSCALE_14
+    0x00000000, // PA_CL_VPORT_ZOFFSET_14
+    0x00000000, // PA_CL_VPORT_XSCALE_15
+    0x00000000, // PA_CL_VPORT_XOFFSET_15
+    0x00000000, // PA_CL_VPORT_YSCALE_15
+    0x00000000, // PA_CL_VPORT_YOFFSET_15
+    0x00000000, // PA_CL_VPORT_ZSCALE_15
+    0x00000000, // PA_CL_VPORT_ZOFFSET_15
+    0x00000000, // PA_CL_UCP_0_X
+    0x00000000, // PA_CL_UCP_0_Y
+    0x00000000, // PA_CL_UCP_0_Z
+    0x00000000, // PA_CL_UCP_0_W
+    0x00000000, // PA_CL_UCP_1_X
+    0x00000000, // PA_CL_UCP_1_Y
+    0x00000000, // PA_CL_UCP_1_Z
+    0x00000000, // PA_CL_UCP_1_W
+    0x00000000, // PA_CL_UCP_2_X
+    0x00000000, // PA_CL_UCP_2_Y
+    0x00000000, // PA_CL_UCP_2_Z
+    0x00000000, // PA_CL_UCP_2_W
+    0x00000000, // PA_CL_UCP_3_X
+    0x00000000, // PA_CL_UCP_3_Y
+    0x00000000, // PA_CL_UCP_3_Z
+    0x00000000, // PA_CL_UCP_3_W
+    0x00000000, // PA_CL_UCP_4_X
+    0x00000000, // PA_CL_UCP_4_Y
+    0x00000000, // PA_CL_UCP_4_Z
+    0x00000000, // PA_CL_UCP_4_W
+    0x00000000, // PA_CL_UCP_5_X
+    0x00000000, // PA_CL_UCP_5_Y
+    0x00000000, // PA_CL_UCP_5_Z
+    0x00000000, // PA_CL_UCP_5_W
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SPI_PS_INPUT_CNTL_0
+    0x00000000, // SPI_PS_INPUT_CNTL_1
+    0x00000000, // SPI_PS_INPUT_CNTL_2
+    0x00000000, // SPI_PS_INPUT_CNTL_3
+    0x00000000, // SPI_PS_INPUT_CNTL_4
+    0x00000000, // SPI_PS_INPUT_CNTL_5
+    0x00000000, // SPI_PS_INPUT_CNTL_6
+    0x00000000, // SPI_PS_INPUT_CNTL_7
+    0x00000000, // SPI_PS_INPUT_CNTL_8
+    0x00000000, // SPI_PS_INPUT_CNTL_9
+    0x00000000, // SPI_PS_INPUT_CNTL_10
+    0x00000000, // SPI_PS_INPUT_CNTL_11
+    0x00000000, // SPI_PS_INPUT_CNTL_12
+    0x00000000, // SPI_PS_INPUT_CNTL_13
+    0x00000000, // SPI_PS_INPUT_CNTL_14
+    0x00000000, // SPI_PS_INPUT_CNTL_15
+    0x00000000, // SPI_PS_INPUT_CNTL_16
+    0x00000000, // SPI_PS_INPUT_CNTL_17
+    0x00000000, // SPI_PS_INPUT_CNTL_18
+    0x00000000, // SPI_PS_INPUT_CNTL_19
+    0x00000000, // SPI_PS_INPUT_CNTL_20
+    0x00000000, // SPI_PS_INPUT_CNTL_21
+    0x00000000, // SPI_PS_INPUT_CNTL_22
+    0x00000000, // SPI_PS_INPUT_CNTL_23
+    0x00000000, // SPI_PS_INPUT_CNTL_24
+    0x00000000, // SPI_PS_INPUT_CNTL_25
+    0x00000000, // SPI_PS_INPUT_CNTL_26
+    0x00000000, // SPI_PS_INPUT_CNTL_27
+    0x00000000, // SPI_PS_INPUT_CNTL_28
+    0x00000000, // SPI_PS_INPUT_CNTL_29
+    0x00000000, // SPI_PS_INPUT_CNTL_30
+    0x00000000, // SPI_PS_INPUT_CNTL_31
+    0x00000000, // SPI_VS_OUT_CONFIG
+    0, // HOLE
+    0x00000000, // SPI_PS_INPUT_ENA
+    0x00000000, // SPI_PS_INPUT_ADDR
+    0x00000000, // SPI_INTERP_CONTROL_0
+    0x00000002, // SPI_PS_IN_CONTROL
+    0, // HOLE
+    0x00000000, // SPI_BARYC_CNTL
+    0, // HOLE
+    0x00000000, // SPI_TMPRING_SIZE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SPI_WAVE_MGMT_1
+    0x00000000, // SPI_WAVE_MGMT_2
+    0x00000000, // SPI_SHADER_POS_FORMAT
+    0x00000000, // SPI_SHADER_Z_FORMAT
+    0x00000000, // SPI_SHADER_COL_FORMAT
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_BLEND0_CONTROL
+    0x00000000, // CB_BLEND1_CONTROL
+    0x00000000, // CB_BLEND2_CONTROL
+    0x00000000, // CB_BLEND3_CONTROL
+    0x00000000, // CB_BLEND4_CONTROL
+    0x00000000, // CB_BLEND5_CONTROL
+    0x00000000, // CB_BLEND6_CONTROL
+    0x00000000, // CB_BLEND7_CONTROL
+};
+static const u32 si_SECT_CONTEXT_def_3[] =
+{
+    0x00000000, // PA_CL_POINT_X_RAD
+    0x00000000, // PA_CL_POINT_Y_RAD
+    0x00000000, // PA_CL_POINT_SIZE
+    0x00000000, // PA_CL_POINT_CULL_RAD
+    0x00000000, // VGT_DMA_BASE_HI
+    0x00000000, // VGT_DMA_BASE
+};
+static const u32 si_SECT_CONTEXT_def_4[] =
+{
+    0x00000000, // DB_DEPTH_CONTROL
+    0x00000000, // DB_EQAA
+    0x00000000, // CB_COLOR_CONTROL
+    0x00000000, // DB_SHADER_CONTROL
+    0x00090000, // PA_CL_CLIP_CNTL
+    0x00000004, // PA_SU_SC_MODE_CNTL
+    0x00000000, // PA_CL_VTE_CNTL
+    0x00000000, // PA_CL_VS_OUT_CNTL
+    0x00000000, // PA_CL_NANINF_CNTL
+    0x00000000, // PA_SU_LINE_STIPPLE_CNTL
+    0x00000000, // PA_SU_LINE_STIPPLE_SCALE
+    0x00000000, // PA_SU_PRIM_FILTER_CNTL
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // PA_SU_POINT_SIZE
+    0x00000000, // PA_SU_POINT_MINMAX
+    0x00000000, // PA_SU_LINE_CNTL
+    0x00000000, // PA_SC_LINE_STIPPLE
+    0x00000000, // VGT_OUTPUT_PATH_CNTL
+    0x00000000, // VGT_HOS_CNTL
+    0x00000000, // VGT_HOS_MAX_TESS_LEVEL
+    0x00000000, // VGT_HOS_MIN_TESS_LEVEL
+    0x00000000, // VGT_HOS_REUSE_DEPTH
+    0x00000000, // VGT_GROUP_PRIM_TYPE
+    0x00000000, // VGT_GROUP_FIRST_DECR
+    0x00000000, // VGT_GROUP_DECR
+    0x00000000, // VGT_GROUP_VECT_0_CNTL
+    0x00000000, // VGT_GROUP_VECT_1_CNTL
+    0x00000000, // VGT_GROUP_VECT_0_FMT_CNTL
+    0x00000000, // VGT_GROUP_VECT_1_FMT_CNTL
+    0x00000000, // VGT_GS_MODE
+    0, // HOLE
+    0x00000000, // PA_SC_MODE_CNTL_0
+    0x00000000, // PA_SC_MODE_CNTL_1
+    0x00000000, // VGT_ENHANCE
+    0x00000100, // VGT_GS_PER_ES
+    0x00000080, // VGT_ES_PER_GS
+    0x00000002, // VGT_GS_PER_VS
+    0x00000000, // VGT_GSVS_RING_OFFSET_1
+    0x00000000, // VGT_GSVS_RING_OFFSET_2
+    0x00000000, // VGT_GSVS_RING_OFFSET_3
+    0x00000000, // VGT_GS_OUT_PRIM_TYPE
+    0x00000000, // IA_ENHANCE
+};
+static const u32 si_SECT_CONTEXT_def_5[] =
+{
+    0x00000000, // VGT_PRIMITIVEID_EN
+};
+static const u32 si_SECT_CONTEXT_def_6[] =
+{
+    0x00000000, // VGT_PRIMITIVEID_RESET
+};
+static const u32 si_SECT_CONTEXT_def_7[] =
+{
+    0x00000000, // VGT_MULTI_PRIM_IB_RESET_EN
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_INSTANCE_STEP_RATE_0
+    0x00000000, // VGT_INSTANCE_STEP_RATE_1
+    0x000000ff, // IA_MULTI_VGT_PARAM
+    0x00000000, // VGT_ESGS_RING_ITEMSIZE
+    0x00000000, // VGT_GSVS_RING_ITEMSIZE
+    0x00000000, // VGT_REUSE_OFF
+    0x00000000, // VGT_VTX_CNT_EN
+    0x00000000, // DB_HTILE_SURFACE
+    0x00000000, // DB_SRESULTS_COMPARE_STATE0
+    0x00000000, // DB_SRESULTS_COMPARE_STATE1
+    0x00000000, // DB_PRELOAD_CONTROL
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_0
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_0
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_0
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_1
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_1
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_1
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_2
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_2
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_2
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_3
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_3
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_3
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_OFFSET
+    0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE
+    0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE
+    0, // HOLE
+    0x00000000, // VGT_GS_MAX_VERT_OUT
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_SHADER_STAGES_EN
+    0x00000000, // VGT_LS_HS_CONFIG
+    0x00000000, // VGT_GS_VERT_ITEMSIZE
+    0x00000000, // VGT_GS_VERT_ITEMSIZE_1
+    0x00000000, // VGT_GS_VERT_ITEMSIZE_2
+    0x00000000, // VGT_GS_VERT_ITEMSIZE_3
+    0x00000000, // VGT_TF_PARAM
+    0x00000000, // DB_ALPHA_TO_MASK
+    0, // HOLE
+    0x00000000, // PA_SU_POLY_OFFSET_DB_FMT_CNTL
+    0x00000000, // PA_SU_POLY_OFFSET_CLAMP
+    0x00000000, // PA_SU_POLY_OFFSET_FRONT_SCALE
+    0x00000000, // PA_SU_POLY_OFFSET_FRONT_OFFSET
+    0x00000000, // PA_SU_POLY_OFFSET_BACK_SCALE
+    0x00000000, // PA_SU_POLY_OFFSET_BACK_OFFSET
+    0x00000000, // VGT_GS_INSTANCE_CNT
+    0x00000000, // VGT_STRMOUT_CONFIG
+    0x00000000, // VGT_STRMOUT_BUFFER_CONFIG
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // PA_SC_CENTROID_PRIORITY_0
+    0x00000000, // PA_SC_CENTROID_PRIORITY_1
+    0x00001000, // PA_SC_LINE_CNTL
+    0x00000000, // PA_SC_AA_CONFIG
+    0x00000005, // PA_SU_VTX_CNTL
+    0x3f800000, // PA_CL_GB_VERT_CLIP_ADJ
+    0x3f800000, // PA_CL_GB_VERT_DISC_ADJ
+    0x3f800000, // PA_CL_GB_HORZ_CLIP_ADJ
+    0x3f800000, // PA_CL_GB_HORZ_DISC_ADJ
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_1
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_2
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_3
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_0
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_1
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_2
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_3
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_0
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_1
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_2
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_3
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_0
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_1
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_2
+    0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_3
+    0xffffffff, // PA_SC_AA_MASK_X0Y0_X1Y0
+    0xffffffff, // PA_SC_AA_MASK_X0Y1_X1Y1
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x0000000e, // VGT_VERTEX_REUSE_BLOCK_CNTL
+    0x00000010, // VGT_OUT_DEALLOC_CNTL
+    0x00000000, // CB_COLOR0_BASE
+    0x00000000, // CB_COLOR0_PITCH
+    0x00000000, // CB_COLOR0_SLICE
+    0x00000000, // CB_COLOR0_VIEW
+    0x00000000, // CB_COLOR0_INFO
+    0x00000000, // CB_COLOR0_ATTRIB
+    0, // HOLE
+    0x00000000, // CB_COLOR0_CMASK
+    0x00000000, // CB_COLOR0_CMASK_SLICE
+    0x00000000, // CB_COLOR0_FMASK
+    0x00000000, // CB_COLOR0_FMASK_SLICE
+    0x00000000, // CB_COLOR0_CLEAR_WORD0
+    0x00000000, // CB_COLOR0_CLEAR_WORD1
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR1_BASE
+    0x00000000, // CB_COLOR1_PITCH
+    0x00000000, // CB_COLOR1_SLICE
+    0x00000000, // CB_COLOR1_VIEW
+    0x00000000, // CB_COLOR1_INFO
+    0x00000000, // CB_COLOR1_ATTRIB
+    0, // HOLE
+    0x00000000, // CB_COLOR1_CMASK
+    0x00000000, // CB_COLOR1_CMASK_SLICE
+    0x00000000, // CB_COLOR1_FMASK
+    0x00000000, // CB_COLOR1_FMASK_SLICE
+    0x00000000, // CB_COLOR1_CLEAR_WORD0
+    0x00000000, // CB_COLOR1_CLEAR_WORD1
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR2_BASE
+    0x00000000, // CB_COLOR2_PITCH
+    0x00000000, // CB_COLOR2_SLICE
+    0x00000000, // CB_COLOR2_VIEW
+    0x00000000, // CB_COLOR2_INFO
+    0x00000000, // CB_COLOR2_ATTRIB
+    0, // HOLE
+    0x00000000, // CB_COLOR2_CMASK
+    0x00000000, // CB_COLOR2_CMASK_SLICE
+    0x00000000, // CB_COLOR2_FMASK
+    0x00000000, // CB_COLOR2_FMASK_SLICE
+    0x00000000, // CB_COLOR2_CLEAR_WORD0
+    0x00000000, // CB_COLOR2_CLEAR_WORD1
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR3_BASE
+    0x00000000, // CB_COLOR3_PITCH
+    0x00000000, // CB_COLOR3_SLICE
+    0x00000000, // CB_COLOR3_VIEW
+    0x00000000, // CB_COLOR3_INFO
+    0x00000000, // CB_COLOR3_ATTRIB
+    0, // HOLE
+    0x00000000, // CB_COLOR3_CMASK
+    0x00000000, // CB_COLOR3_CMASK_SLICE
+    0x00000000, // CB_COLOR3_FMASK
+    0x00000000, // CB_COLOR3_FMASK_SLICE
+    0x00000000, // CB_COLOR3_CLEAR_WORD0
+    0x00000000, // CB_COLOR3_CLEAR_WORD1
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR4_BASE
+    0x00000000, // CB_COLOR4_PITCH
+    0x00000000, // CB_COLOR4_SLICE
+    0x00000000, // CB_COLOR4_VIEW
+    0x00000000, // CB_COLOR4_INFO
+    0x00000000, // CB_COLOR4_ATTRIB
+    0, // HOLE
+    0x00000000, // CB_COLOR4_CMASK
+    0x00000000, // CB_COLOR4_CMASK_SLICE
+    0x00000000, // CB_COLOR4_FMASK
+    0x00000000, // CB_COLOR4_FMASK_SLICE
+    0x00000000, // CB_COLOR4_CLEAR_WORD0
+    0x00000000, // CB_COLOR4_CLEAR_WORD1
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR5_BASE
+    0x00000000, // CB_COLOR5_PITCH
+    0x00000000, // CB_COLOR5_SLICE
+    0x00000000, // CB_COLOR5_VIEW
+    0x00000000, // CB_COLOR5_INFO
+    0x00000000, // CB_COLOR5_ATTRIB
+    0, // HOLE
+    0x00000000, // CB_COLOR5_CMASK
+    0x00000000, // CB_COLOR5_CMASK_SLICE
+    0x00000000, // CB_COLOR5_FMASK
+    0x00000000, // CB_COLOR5_FMASK_SLICE
+    0x00000000, // CB_COLOR5_CLEAR_WORD0
+    0x00000000, // CB_COLOR5_CLEAR_WORD1
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR6_BASE
+    0x00000000, // CB_COLOR6_PITCH
+    0x00000000, // CB_COLOR6_SLICE
+    0x00000000, // CB_COLOR6_VIEW
+    0x00000000, // CB_COLOR6_INFO
+    0x00000000, // CB_COLOR6_ATTRIB
+    0, // HOLE
+    0x00000000, // CB_COLOR6_CMASK
+    0x00000000, // CB_COLOR6_CMASK_SLICE
+    0x00000000, // CB_COLOR6_FMASK
+    0x00000000, // CB_COLOR6_FMASK_SLICE
+    0x00000000, // CB_COLOR6_CLEAR_WORD0
+    0x00000000, // CB_COLOR6_CLEAR_WORD1
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR7_BASE
+    0x00000000, // CB_COLOR7_PITCH
+    0x00000000, // CB_COLOR7_SLICE
+    0x00000000, // CB_COLOR7_VIEW
+    0x00000000, // CB_COLOR7_INFO
+    0x00000000, // CB_COLOR7_ATTRIB
+    0, // HOLE
+    0x00000000, // CB_COLOR7_CMASK
+    0x00000000, // CB_COLOR7_CMASK_SLICE
+    0x00000000, // CB_COLOR7_FMASK
+    0x00000000, // CB_COLOR7_FMASK_SLICE
+    0x00000000, // CB_COLOR7_CLEAR_WORD0
+    0x00000000, // CB_COLOR7_CLEAR_WORD1
+};
+static const struct cs_extent_def si_SECT_CONTEXT_defs[] =
+{
+    {si_SECT_CONTEXT_def_1, 0x0000a000, 212 },
+    {si_SECT_CONTEXT_def_2, 0x0000a0d8, 272 },
+    {si_SECT_CONTEXT_def_3, 0x0000a1f5, 6 },
+    {si_SECT_CONTEXT_def_4, 0x0000a200, 157 },
+    {si_SECT_CONTEXT_def_5, 0x0000a2a1, 1 },
+    {si_SECT_CONTEXT_def_6, 0x0000a2a3, 1 },
+    {si_SECT_CONTEXT_def_7, 0x0000a2a5, 233 },
+    { 0, 0, 0 }
+};
+static const struct cs_section_def si_cs_data[] = {
+    { si_SECT_CONTEXT_defs, SECT_CONTEXT },
+    { 0, SECT_NONE }
+};
diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c
new file mode 100644 (file)
index 0000000..9bcdd17
--- /dev/null
@@ -0,0 +1,2189 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "evergreend.h"
+#include "r600_dpm.h"
+#include "cypress_dpm.h"
+#include "atom.h"
+
+#define SMC_RAM_END 0x8000
+
+#define MC_CG_ARB_FREQ_F0           0x0a
+#define MC_CG_ARB_FREQ_F1           0x0b
+#define MC_CG_ARB_FREQ_F2           0x0c
+#define MC_CG_ARB_FREQ_F3           0x0d
+
+#define MC_CG_SEQ_DRAMCONF_S0       0x05
+#define MC_CG_SEQ_DRAMCONF_S1       0x06
+#define MC_CG_SEQ_YCLK_SUSPEND      0x04
+#define MC_CG_SEQ_YCLK_RESUME       0x0a
+
+struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps);
+struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev);
+struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev);
+
+static void cypress_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev,
+                                                bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 tmp, bif;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+       if (enable) {
+               if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) &&
+                   (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) {
+                       if (!pi->boot_in_gen2) {
+                               bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK;
+                               bif |= CG_CLIENT_REQ(0xd);
+                               WREG32(CG_BIF_REQ_AND_RSP, bif);
+
+                               tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
+                               tmp |= LC_HW_VOLTAGE_IF_CONTROL(1);
+                               tmp |= LC_GEN2_EN_STRAP;
+
+                               tmp |= LC_CLR_FAILED_SPD_CHANGE_CNT;
+                               WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+                               udelay(10);
+                               tmp &= ~LC_CLR_FAILED_SPD_CHANGE_CNT;
+                               WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+                       }
+               }
+       } else {
+               if (!pi->boot_in_gen2) {
+                       tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
+                       tmp &= ~LC_GEN2_EN_STRAP;
+               }
+               if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) ||
+                   (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2))
+                       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+       }
+}
+
+static void cypress_enable_dynamic_pcie_gen2(struct radeon_device *rdev,
+                                            bool enable)
+{
+       cypress_enable_bif_dynamic_pcie_gen2(rdev, enable);
+
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, ENABLE_GEN2PCIE, ~ENABLE_GEN2PCIE);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE);
+}
+
+#if 0
+static int cypress_enter_ulp_state(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (pi->gfx_clock_gating) {
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN);
+               WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON);
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON);
+
+               RREG32(GB_ADDR_CONFIG);
+       }
+
+       WREG32_P(SMC_MSG, HOST_SMC_MSG(PPSMC_MSG_SwitchToMinimumPower),
+                ~HOST_SMC_MSG_MASK);
+
+       udelay(7000);
+
+       return 0;
+}
+#endif
+
+static void cypress_gfx_clock_gating_enable(struct radeon_device *rdev,
+                                           bool enable)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       if (enable) {
+               if (eg_pi->light_sleep) {
+                       WREG32(GRBM_GFX_INDEX, 0xC0000000);
+
+                       WREG32_CG(CG_CGLS_TILE_0, 0xFFFFFFFF);
+                       WREG32_CG(CG_CGLS_TILE_1, 0xFFFFFFFF);
+                       WREG32_CG(CG_CGLS_TILE_2, 0xFFFFFFFF);
+                       WREG32_CG(CG_CGLS_TILE_3, 0xFFFFFFFF);
+                       WREG32_CG(CG_CGLS_TILE_4, 0xFFFFFFFF);
+                       WREG32_CG(CG_CGLS_TILE_5, 0xFFFFFFFF);
+                       WREG32_CG(CG_CGLS_TILE_6, 0xFFFFFFFF);
+                       WREG32_CG(CG_CGLS_TILE_7, 0xFFFFFFFF);
+                       WREG32_CG(CG_CGLS_TILE_8, 0xFFFFFFFF);
+                       WREG32_CG(CG_CGLS_TILE_9, 0xFFFFFFFF);
+                       WREG32_CG(CG_CGLS_TILE_10, 0xFFFFFFFF);
+                       WREG32_CG(CG_CGLS_TILE_11, 0xFFFFFFFF);
+
+                       WREG32_P(SCLK_PWRMGT_CNTL, DYN_LIGHT_SLEEP_EN, ~DYN_LIGHT_SLEEP_EN);
+               }
+               WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN);
+       } else {
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN);
+               WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON);
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON);
+               RREG32(GB_ADDR_CONFIG);
+
+               if (eg_pi->light_sleep) {
+                       WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_LIGHT_SLEEP_EN);
+
+                       WREG32(GRBM_GFX_INDEX, 0xC0000000);
+
+                       WREG32_CG(CG_CGLS_TILE_0, 0);
+                       WREG32_CG(CG_CGLS_TILE_1, 0);
+                       WREG32_CG(CG_CGLS_TILE_2, 0);
+                       WREG32_CG(CG_CGLS_TILE_3, 0);
+                       WREG32_CG(CG_CGLS_TILE_4, 0);
+                       WREG32_CG(CG_CGLS_TILE_5, 0);
+                       WREG32_CG(CG_CGLS_TILE_6, 0);
+                       WREG32_CG(CG_CGLS_TILE_7, 0);
+                       WREG32_CG(CG_CGLS_TILE_8, 0);
+                       WREG32_CG(CG_CGLS_TILE_9, 0);
+                       WREG32_CG(CG_CGLS_TILE_10, 0);
+                       WREG32_CG(CG_CGLS_TILE_11, 0);
+               }
+       }
+}
+
+static void cypress_mg_clock_gating_enable(struct radeon_device *rdev,
+                                          bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       if (enable) {
+               u32 cgts_sm_ctrl_reg;
+
+               if (rdev->family == CHIP_CEDAR)
+                       cgts_sm_ctrl_reg = CEDAR_MGCGCGTSSMCTRL_DFLT;
+               else if (rdev->family == CHIP_REDWOOD)
+                       cgts_sm_ctrl_reg = REDWOOD_MGCGCGTSSMCTRL_DFLT;
+               else
+                       cgts_sm_ctrl_reg = CYPRESS_MGCGCGTSSMCTRL_DFLT;
+
+               WREG32(GRBM_GFX_INDEX, 0xC0000000);
+
+               WREG32_CG(CG_CGTT_LOCAL_0, CYPRESS_MGCGTTLOCAL0_DFLT);
+               WREG32_CG(CG_CGTT_LOCAL_1, CYPRESS_MGCGTTLOCAL1_DFLT & 0xFFFFCFFF);
+               WREG32_CG(CG_CGTT_LOCAL_2, CYPRESS_MGCGTTLOCAL2_DFLT);
+               WREG32_CG(CG_CGTT_LOCAL_3, CYPRESS_MGCGTTLOCAL3_DFLT);
+
+               if (pi->mgcgtssm)
+                       WREG32(CGTS_SM_CTRL_REG, cgts_sm_ctrl_reg);
+
+               if (eg_pi->mcls) {
+                       WREG32_P(MC_CITF_MISC_RD_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE);
+                       WREG32_P(MC_CITF_MISC_WR_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE);
+                       WREG32_P(MC_CITF_MISC_VM_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE);
+                       WREG32_P(MC_HUB_MISC_HUB_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE);
+                       WREG32_P(MC_HUB_MISC_VM_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE);
+                       WREG32_P(MC_HUB_MISC_SIP_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE);
+                       WREG32_P(MC_XPB_CLK_GAT, MEM_LS_ENABLE, ~MEM_LS_ENABLE);
+                       WREG32_P(VM_L2_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE);
+               }
+       } else {
+               WREG32(GRBM_GFX_INDEX, 0xC0000000);
+
+               WREG32_CG(CG_CGTT_LOCAL_0, 0xFFFFFFFF);
+               WREG32_CG(CG_CGTT_LOCAL_1, 0xFFFFFFFF);
+               WREG32_CG(CG_CGTT_LOCAL_2, 0xFFFFFFFF);
+               WREG32_CG(CG_CGTT_LOCAL_3, 0xFFFFFFFF);
+
+               if (pi->mgcgtssm)
+                       WREG32(CGTS_SM_CTRL_REG, 0x81f44bc0);
+       }
+}
+
+void cypress_enable_spread_spectrum(struct radeon_device *rdev,
+                                   bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (enable) {
+               if (pi->sclk_ss)
+                       WREG32_P(GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, ~DYN_SPREAD_SPECTRUM_EN);
+
+               if (pi->mclk_ss)
+                       WREG32_P(MPLL_CNTL_MODE, SS_SSEN, ~SS_SSEN);
+       } else {
+               WREG32_P(CG_SPLL_SPREAD_SPECTRUM, 0, ~SSEN);
+               WREG32_P(GENERAL_PWRMGT, 0, ~DYN_SPREAD_SPECTRUM_EN);
+               WREG32_P(MPLL_CNTL_MODE, 0, ~SS_SSEN);
+               WREG32_P(MPLL_CNTL_MODE, 0, ~SS_DSMODE_EN);
+       }
+}
+
+void cypress_start_dpm(struct radeon_device *rdev)
+{
+       WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN);
+}
+
+void cypress_enable_sclk_control(struct radeon_device *rdev,
+                                bool enable)
+{
+       if (enable)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~SCLK_PWRMGT_OFF);
+       else
+               WREG32_P(SCLK_PWRMGT_CNTL, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF);
+}
+
+void cypress_enable_mclk_control(struct radeon_device *rdev,
+                                bool enable)
+{
+       if (enable)
+               WREG32_P(MCLK_PWRMGT_CNTL, 0, ~MPLL_PWRMGT_OFF);
+       else
+               WREG32_P(MCLK_PWRMGT_CNTL, MPLL_PWRMGT_OFF, ~MPLL_PWRMGT_OFF);
+}
+
+int cypress_notify_smc_display_change(struct radeon_device *rdev,
+                                     bool has_display)
+{
+       PPSMC_Msg msg = has_display ?
+               (PPSMC_Msg)PPSMC_MSG_HasDisplay : (PPSMC_Msg)PPSMC_MSG_NoDisplay;
+
+       if (rv770_send_msg_to_smc(rdev, msg) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return 0;
+}
+
+void cypress_program_response_times(struct radeon_device *rdev)
+{
+       u32 reference_clock;
+       u32 mclk_switch_limit;
+
+       reference_clock = radeon_get_xclk(rdev);
+       mclk_switch_limit = (460 * reference_clock) / 100;
+
+       rv770_write_smc_soft_register(rdev,
+                                     RV770_SMC_SOFT_REGISTER_mclk_switch_lim,
+                                     mclk_switch_limit);
+
+       rv770_write_smc_soft_register(rdev,
+                                     RV770_SMC_SOFT_REGISTER_mvdd_chg_time, 1);
+
+       rv770_write_smc_soft_register(rdev,
+                                     RV770_SMC_SOFT_REGISTER_mc_block_delay, 0xAA);
+
+       rv770_program_response_times(rdev);
+
+       if (ASIC_IS_LOMBOK(rdev))
+               rv770_write_smc_soft_register(rdev,
+                                             RV770_SMC_SOFT_REGISTER_is_asic_lombok, 1);
+
+}
+
+static int cypress_pcie_performance_request(struct radeon_device *rdev,
+                                           u8 perf_req, bool advertise)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 tmp;
+
+       udelay(10);
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+       if ((perf_req == PCIE_PERF_REQ_PECI_GEN1) && (tmp & LC_CURRENT_DATA_RATE))
+               return 0;
+
+#if defined(CONFIG_ACPI)
+       if ((perf_req == PCIE_PERF_REQ_PECI_GEN1) ||
+           (perf_req == PCIE_PERF_REQ_PECI_GEN2)) {
+               eg_pi->pcie_performance_request_registered = true;
+               return radeon_acpi_pcie_performance_request(rdev, perf_req, advertise);
+       } else if ((perf_req == PCIE_PERF_REQ_REMOVE_REGISTRY) &&
+                  eg_pi->pcie_performance_request_registered) {
+               eg_pi->pcie_performance_request_registered = false;
+               return radeon_acpi_pcie_performance_request(rdev, perf_req, advertise);
+       }
+#endif
+
+       return 0;
+}
+
+void cypress_advertise_gen2_capability(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 tmp;
+
+#if defined(CONFIG_ACPI)
+       radeon_acpi_pcie_notify_device_ready(rdev);
+#endif
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+
+       if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) &&
+           (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2))
+               pi->pcie_gen2 = true;
+       else
+               pi->pcie_gen2 = false;
+
+       if (!pi->pcie_gen2)
+               cypress_pcie_performance_request(rdev, PCIE_PERF_REQ_PECI_GEN2, true);
+
+}
+
+static enum radeon_pcie_gen cypress_get_maximum_link_speed(struct radeon_ps *radeon_state)
+{
+       struct rv7xx_ps *state = rv770_get_ps(radeon_state);
+
+       if (state->high.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2)
+               return 1;
+       return 0;
+}
+
+void cypress_notify_link_speed_change_after_state_change(struct radeon_device *rdev,
+                                                        struct radeon_ps *radeon_new_state,
+                                                        struct radeon_ps *radeon_current_state)
+{
+       enum radeon_pcie_gen pcie_link_speed_target =
+               cypress_get_maximum_link_speed(radeon_new_state);
+       enum radeon_pcie_gen pcie_link_speed_current =
+               cypress_get_maximum_link_speed(radeon_current_state);
+       u8 request;
+
+       if (pcie_link_speed_target < pcie_link_speed_current) {
+               if (pcie_link_speed_target == RADEON_PCIE_GEN1)
+                       request = PCIE_PERF_REQ_PECI_GEN1;
+               else if (pcie_link_speed_target == RADEON_PCIE_GEN2)
+                       request = PCIE_PERF_REQ_PECI_GEN2;
+               else
+                       request = PCIE_PERF_REQ_PECI_GEN3;
+
+               cypress_pcie_performance_request(rdev, request, false);
+       }
+}
+
+void cypress_notify_link_speed_change_before_state_change(struct radeon_device *rdev,
+                                                         struct radeon_ps *radeon_new_state,
+                                                         struct radeon_ps *radeon_current_state)
+{
+       enum radeon_pcie_gen pcie_link_speed_target =
+               cypress_get_maximum_link_speed(radeon_new_state);
+       enum radeon_pcie_gen pcie_link_speed_current =
+               cypress_get_maximum_link_speed(radeon_current_state);
+       u8 request;
+
+       if (pcie_link_speed_target > pcie_link_speed_current) {
+               if (pcie_link_speed_target == RADEON_PCIE_GEN1)
+                       request = PCIE_PERF_REQ_PECI_GEN1;
+               else if (pcie_link_speed_target == RADEON_PCIE_GEN2)
+                       request = PCIE_PERF_REQ_PECI_GEN2;
+               else
+                       request = PCIE_PERF_REQ_PECI_GEN3;
+
+               cypress_pcie_performance_request(rdev, request, false);
+       }
+}
+
+static int cypress_populate_voltage_value(struct radeon_device *rdev,
+                                         struct atom_voltage_table *table,
+                                         u16 value, RV770_SMC_VOLTAGE_VALUE *voltage)
+{
+       unsigned int i;
+
+       for (i = 0; i < table->count; i++) {
+               if (value <= table->entries[i].value) {
+                       voltage->index = (u8)i;
+                       voltage->value = cpu_to_be16(table->entries[i].value);
+                       break;
+               }
+       }
+
+       if (i == table->count)
+               return -EINVAL;
+
+       return 0;
+}
+
+u8 cypress_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u8 result = 0;
+       bool strobe_mode = false;
+
+       if (pi->mem_gddr5) {
+               if (mclk <= pi->mclk_strobe_mode_threshold)
+                       strobe_mode = true;
+               result = cypress_get_mclk_frequency_ratio(rdev, mclk, strobe_mode);
+
+               if (strobe_mode)
+                       result |= SMC_STROBE_ENABLE;
+       }
+
+       return result;
+}
+
+u32 cypress_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf)
+{
+       u32 ref_clk = rdev->clock.mpll.reference_freq;
+       u32 vco = clkf * ref_clk;
+
+       /* 100 Mhz ref clk */
+       if (ref_clk == 10000) {
+               if (vco > 500000)
+                       return 0xC6;
+               if (vco > 400000)
+                       return 0x9D;
+               if (vco > 330000)
+                       return 0x6C;
+               if (vco > 250000)
+                       return 0x2B;
+               if (vco >  160000)
+                       return 0x5B;
+               if (vco > 120000)
+                       return 0x0A;
+               return 0x4B;
+       }
+
+       /* 27 Mhz ref clk */
+       if (vco > 250000)
+               return 0x8B;
+       if (vco > 200000)
+               return 0xCC;
+       if (vco > 150000)
+               return 0x9B;
+       return 0x6B;
+}
+
+static int cypress_populate_mclk_value(struct radeon_device *rdev,
+                                      u32 engine_clock, u32 memory_clock,
+                                      RV7XX_SMC_MCLK_VALUE *mclk,
+                                      bool strobe_mode, bool dll_state_on)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       u32 mpll_ad_func_cntl =
+               pi->clk_regs.rv770.mpll_ad_func_cntl;
+       u32 mpll_ad_func_cntl_2 =
+               pi->clk_regs.rv770.mpll_ad_func_cntl_2;
+       u32 mpll_dq_func_cntl =
+               pi->clk_regs.rv770.mpll_dq_func_cntl;
+       u32 mpll_dq_func_cntl_2 =
+               pi->clk_regs.rv770.mpll_dq_func_cntl_2;
+       u32 mclk_pwrmgt_cntl =
+               pi->clk_regs.rv770.mclk_pwrmgt_cntl;
+       u32 dll_cntl =
+               pi->clk_regs.rv770.dll_cntl;
+       u32 mpll_ss1 = pi->clk_regs.rv770.mpll_ss1;
+       u32 mpll_ss2 = pi->clk_regs.rv770.mpll_ss2;
+       struct atom_clock_dividers dividers;
+       u32 ibias;
+       u32 dll_speed;
+       int ret;
+       u32 mc_seq_misc7;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM,
+                                            memory_clock, strobe_mode, &dividers);
+       if (ret)
+               return ret;
+
+       if (!strobe_mode) {
+               mc_seq_misc7 = RREG32(MC_SEQ_MISC7);
+
+               if(mc_seq_misc7 & 0x8000000)
+                       dividers.post_div = 1;
+       }
+
+       ibias = cypress_map_clkf_to_ibias(rdev, dividers.whole_fb_div);
+
+       mpll_ad_func_cntl &= ~(CLKR_MASK |
+                              YCLK_POST_DIV_MASK |
+                              CLKF_MASK |
+                              CLKFRAC_MASK |
+                              IBIAS_MASK);
+       mpll_ad_func_cntl |= CLKR(dividers.ref_div);
+       mpll_ad_func_cntl |= YCLK_POST_DIV(dividers.post_div);
+       mpll_ad_func_cntl |= CLKF(dividers.whole_fb_div);
+       mpll_ad_func_cntl |= CLKFRAC(dividers.frac_fb_div);
+       mpll_ad_func_cntl |= IBIAS(ibias);
+
+       if (dividers.vco_mode)
+               mpll_ad_func_cntl_2 |= VCO_MODE;
+       else
+               mpll_ad_func_cntl_2 &= ~VCO_MODE;
+
+       if (pi->mem_gddr5) {
+               mpll_dq_func_cntl &= ~(CLKR_MASK |
+                                      YCLK_POST_DIV_MASK |
+                                      CLKF_MASK |
+                                      CLKFRAC_MASK |
+                                      IBIAS_MASK);
+               mpll_dq_func_cntl |= CLKR(dividers.ref_div);
+               mpll_dq_func_cntl |= YCLK_POST_DIV(dividers.post_div);
+               mpll_dq_func_cntl |= CLKF(dividers.whole_fb_div);
+               mpll_dq_func_cntl |= CLKFRAC(dividers.frac_fb_div);
+               mpll_dq_func_cntl |= IBIAS(ibias);
+
+               if (strobe_mode)
+                       mpll_dq_func_cntl &= ~PDNB;
+               else
+                       mpll_dq_func_cntl |= PDNB;
+
+               if (dividers.vco_mode)
+                       mpll_dq_func_cntl_2 |= VCO_MODE;
+               else
+                       mpll_dq_func_cntl_2 &= ~VCO_MODE;
+       }
+
+       if (pi->mclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 vco_freq = memory_clock * dividers.post_div;
+
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_MEMORY_SS, vco_freq)) {
+                       u32 reference_clock = rdev->clock.mpll.reference_freq;
+                       u32 decoded_ref = rv740_get_decoded_reference_divider(dividers.ref_div);
+                       u32 clk_s = reference_clock * 5 / (decoded_ref * ss.rate);
+                       u32 clk_v = ss.percentage *
+                               (0x4000 * dividers.whole_fb_div + 0x800 * dividers.frac_fb_div) / (clk_s * 625);
+
+                       mpll_ss1 &= ~CLKV_MASK;
+                       mpll_ss1 |= CLKV(clk_v);
+
+                       mpll_ss2 &= ~CLKS_MASK;
+                       mpll_ss2 |= CLKS(clk_s);
+               }
+       }
+
+       dll_speed = rv740_get_dll_speed(pi->mem_gddr5,
+                                       memory_clock);
+
+       mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK;
+       mclk_pwrmgt_cntl |= DLL_SPEED(dll_speed);
+       if (dll_state_on)
+               mclk_pwrmgt_cntl |= (MRDCKA0_PDNB |
+                                    MRDCKA1_PDNB |
+                                    MRDCKB0_PDNB |
+                                    MRDCKB1_PDNB |
+                                    MRDCKC0_PDNB |
+                                    MRDCKC1_PDNB |
+                                    MRDCKD0_PDNB |
+                                    MRDCKD1_PDNB);
+       else
+               mclk_pwrmgt_cntl &= ~(MRDCKA0_PDNB |
+                                     MRDCKA1_PDNB |
+                                     MRDCKB0_PDNB |
+                                     MRDCKB1_PDNB |
+                                     MRDCKC0_PDNB |
+                                     MRDCKC1_PDNB |
+                                     MRDCKD0_PDNB |
+                                     MRDCKD1_PDNB);
+
+       mclk->mclk770.mclk_value = cpu_to_be32(memory_clock);
+       mclk->mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
+       mclk->mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2);
+       mclk->mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
+       mclk->mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2);
+       mclk->mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+       mclk->mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl);
+       mclk->mclk770.vMPLL_SS = cpu_to_be32(mpll_ss1);
+       mclk->mclk770.vMPLL_SS2 = cpu_to_be32(mpll_ss2);
+
+       return 0;
+}
+
+u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev,
+                                   u32 memory_clock, bool strobe_mode)
+{
+       u8 mc_para_index;
+
+       if (rdev->family >= CHIP_BARTS) {
+               if (strobe_mode) {
+                       if (memory_clock < 10000)
+                               mc_para_index = 0x00;
+                       else if (memory_clock > 47500)
+                               mc_para_index = 0x0f;
+                       else
+                               mc_para_index = (u8)((memory_clock - 10000) / 2500);
+               } else {
+                       if (memory_clock < 65000)
+                               mc_para_index = 0x00;
+                       else if (memory_clock > 135000)
+                               mc_para_index = 0x0f;
+                       else
+                               mc_para_index = (u8)((memory_clock - 60000) / 5000);
+               }
+       } else {
+               if (strobe_mode) {
+                       if (memory_clock < 10000)
+                               mc_para_index = 0x00;
+                       else if (memory_clock > 47500)
+                               mc_para_index = 0x0f;
+                       else
+                               mc_para_index = (u8)((memory_clock - 10000) / 2500);
+               } else {
+                       if (memory_clock < 40000)
+                               mc_para_index = 0x00;
+                       else if (memory_clock > 115000)
+                               mc_para_index = 0x0f;
+                       else
+                               mc_para_index = (u8)((memory_clock - 40000) / 5000);
+               }
+       }
+       return mc_para_index;
+}
+
+static int cypress_populate_mvdd_value(struct radeon_device *rdev,
+                                      u32 mclk,
+                                      RV770_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       if (!pi->mvdd_control) {
+               voltage->index = eg_pi->mvdd_high_index;
+               voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
+               return 0;
+       }
+
+       if (mclk <= pi->mvdd_split_frequency) {
+               voltage->index = eg_pi->mvdd_low_index;
+               voltage->value = cpu_to_be16(MVDD_LOW_VALUE);
+       } else {
+               voltage->index = eg_pi->mvdd_high_index;
+               voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
+       }
+
+       return 0;
+}
+
+int cypress_convert_power_level_to_smc(struct radeon_device *rdev,
+                                      struct rv7xx_pl *pl,
+                                      RV770_SMC_HW_PERFORMANCE_LEVEL *level,
+                                      u8 watermark_level)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       int ret;
+       bool dll_state_on;
+
+       level->gen2PCIE = pi->pcie_gen2 ?
+               ((pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? 1 : 0) : 0;
+       level->gen2XSP  = (pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? 1 : 0;
+       level->backbias = (pl->flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? 1 : 0;
+       level->displayWatermark = watermark_level;
+
+       ret = rv740_populate_sclk_value(rdev, pl->sclk, &level->sclk);
+       if (ret)
+               return ret;
+
+       level->mcFlags =  0;
+       if (pi->mclk_stutter_mode_threshold &&
+           (pl->mclk <= pi->mclk_stutter_mode_threshold) &&
+           !eg_pi->uvd_enabled) {
+               level->mcFlags |= SMC_MC_STUTTER_EN;
+               if (eg_pi->sclk_deep_sleep)
+                       level->stateFlags |= PPSMC_STATEFLAG_AUTO_PULSE_SKIP;
+               else
+                       level->stateFlags &= ~PPSMC_STATEFLAG_AUTO_PULSE_SKIP;
+       }
+
+       if (pi->mem_gddr5) {
+               if (pl->mclk > pi->mclk_edc_enable_threshold)
+                       level->mcFlags |= SMC_MC_EDC_RD_FLAG;
+
+               if (pl->mclk > eg_pi->mclk_edc_wr_enable_threshold)
+                       level->mcFlags |= SMC_MC_EDC_WR_FLAG;
+
+               level->strobeMode = cypress_get_strobe_mode_settings(rdev, pl->mclk);
+
+               if (level->strobeMode & SMC_STROBE_ENABLE) {
+                       if (cypress_get_mclk_frequency_ratio(rdev, pl->mclk, true) >=
+                           ((RREG32(MC_SEQ_MISC7) >> 16) & 0xf))
+                               dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
+                       else
+                               dll_state_on = ((RREG32(MC_SEQ_MISC6) >> 1) & 0x1) ? true : false;
+               } else
+                       dll_state_on = eg_pi->dll_default_on;
+
+               ret = cypress_populate_mclk_value(rdev,
+                                                 pl->sclk,
+                                                 pl->mclk,
+                                                 &level->mclk,
+                                                 (level->strobeMode & SMC_STROBE_ENABLE) != 0,
+                                                 dll_state_on);
+       } else {
+               ret = cypress_populate_mclk_value(rdev,
+                                                 pl->sclk,
+                                                 pl->mclk,
+                                                 &level->mclk,
+                                                 true,
+                                                 true);
+       }
+       if (ret)
+               return ret;
+
+       ret = cypress_populate_voltage_value(rdev,
+                                            &eg_pi->vddc_voltage_table,
+                                            pl->vddc,
+                                            &level->vddc);
+       if (ret)
+               return ret;
+
+       if (eg_pi->vddci_control) {
+               ret = cypress_populate_voltage_value(rdev,
+                                                    &eg_pi->vddci_voltage_table,
+                                                    pl->vddci,
+                                                    &level->vddci);
+               if (ret)
+                       return ret;
+       }
+
+       ret = cypress_populate_mvdd_value(rdev, pl->mclk, &level->mvdd);
+
+       return ret;
+}
+
+static int cypress_convert_power_state_to_smc(struct radeon_device *rdev,
+                                             struct radeon_ps *radeon_state,
+                                             RV770_SMC_SWSTATE *smc_state)
+{
+       struct rv7xx_ps *state = rv770_get_ps(radeon_state);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       int ret;
+
+       if (!(radeon_state->caps & ATOM_PPLIB_DISALLOW_ON_DC))
+               smc_state->flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       ret = cypress_convert_power_level_to_smc(rdev,
+                                                &state->low,
+                                                &smc_state->levels[0],
+                                                PPSMC_DISPLAY_WATERMARK_LOW);
+       if (ret)
+               return ret;
+
+       ret = cypress_convert_power_level_to_smc(rdev,
+                                                &state->medium,
+                                                &smc_state->levels[1],
+                                                PPSMC_DISPLAY_WATERMARK_LOW);
+       if (ret)
+               return ret;
+
+       ret = cypress_convert_power_level_to_smc(rdev,
+                                                &state->high,
+                                                &smc_state->levels[2],
+                                                PPSMC_DISPLAY_WATERMARK_HIGH);
+       if (ret)
+               return ret;
+
+       smc_state->levels[0].arbValue = MC_CG_ARB_FREQ_F1;
+       smc_state->levels[1].arbValue = MC_CG_ARB_FREQ_F2;
+       smc_state->levels[2].arbValue = MC_CG_ARB_FREQ_F3;
+
+       if (eg_pi->dynamic_ac_timing) {
+               smc_state->levels[0].ACIndex = 2;
+               smc_state->levels[1].ACIndex = 3;
+               smc_state->levels[2].ACIndex = 4;
+       } else {
+               smc_state->levels[0].ACIndex = 0;
+               smc_state->levels[1].ACIndex = 0;
+               smc_state->levels[2].ACIndex = 0;
+       }
+
+       rv770_populate_smc_sp(rdev, radeon_state, smc_state);
+
+       return rv770_populate_smc_t(rdev, radeon_state, smc_state);
+}
+
+static void cypress_convert_mc_registers(struct evergreen_mc_reg_entry *entry,
+                                        SMC_Evergreen_MCRegisterSet *data,
+                                        u32 num_entries, u32 valid_flag)
+{
+       u32 i, j;
+
+       for (i = 0, j = 0; j < num_entries; j++) {
+               if (valid_flag & (1 << j)) {
+                       data->value[i] = cpu_to_be32(entry->mc_data[j]);
+                       i++;
+               }
+       }
+}
+
+static void cypress_convert_mc_reg_table_entry_to_smc(struct radeon_device *rdev,
+                                                     struct rv7xx_pl *pl,
+                                                     SMC_Evergreen_MCRegisterSet *mc_reg_table_data)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 i = 0;
+
+       for (i = 0; i < eg_pi->mc_reg_table.num_entries; i++) {
+               if (pl->mclk <=
+                   eg_pi->mc_reg_table.mc_reg_table_entry[i].mclk_max)
+                       break;
+       }
+
+       if ((i == eg_pi->mc_reg_table.num_entries) && (i > 0))
+               --i;
+
+       cypress_convert_mc_registers(&eg_pi->mc_reg_table.mc_reg_table_entry[i],
+                                    mc_reg_table_data,
+                                    eg_pi->mc_reg_table.last,
+                                    eg_pi->mc_reg_table.valid_flag);
+}
+
+static void cypress_convert_mc_reg_table_to_smc(struct radeon_device *rdev,
+                                               struct radeon_ps *radeon_state,
+                                               SMC_Evergreen_MCRegisters *mc_reg_table)
+{
+       struct rv7xx_ps *state = rv770_get_ps(radeon_state);
+
+       cypress_convert_mc_reg_table_entry_to_smc(rdev,
+                                                 &state->low,
+                                                 &mc_reg_table->data[2]);
+       cypress_convert_mc_reg_table_entry_to_smc(rdev,
+                                                 &state->medium,
+                                                 &mc_reg_table->data[3]);
+       cypress_convert_mc_reg_table_entry_to_smc(rdev,
+                                                 &state->high,
+                                                 &mc_reg_table->data[4]);
+}
+
+int cypress_upload_sw_state(struct radeon_device *rdev,
+                           struct radeon_ps *radeon_new_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u16 address = pi->state_table_start +
+               offsetof(RV770_SMC_STATETABLE, driverState);
+       RV770_SMC_SWSTATE state = { 0 };
+       int ret;
+
+       ret = cypress_convert_power_state_to_smc(rdev, radeon_new_state, &state);
+       if (ret)
+               return ret;
+
+       return rv770_copy_bytes_to_smc(rdev, address, (u8 *)&state,
+                                   sizeof(RV770_SMC_SWSTATE),
+                                   pi->sram_end);
+}
+
+int cypress_upload_mc_reg_table(struct radeon_device *rdev,
+                               struct radeon_ps *radeon_new_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       SMC_Evergreen_MCRegisters mc_reg_table = { 0 };
+       u16 address;
+
+       cypress_convert_mc_reg_table_to_smc(rdev, radeon_new_state, &mc_reg_table);
+
+       address = eg_pi->mc_reg_table_start +
+               (u16)offsetof(SMC_Evergreen_MCRegisters, data[2]);
+
+       return rv770_copy_bytes_to_smc(rdev, address,
+                                      (u8 *)&mc_reg_table.data[2],
+                                      sizeof(SMC_Evergreen_MCRegisterSet) * 3,
+                                      pi->sram_end);
+}
+
+u32 cypress_calculate_burst_time(struct radeon_device *rdev,
+                                u32 engine_clock, u32 memory_clock)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 multiplier = pi->mem_gddr5 ? 1 : 2;
+       u32 result = (4 * multiplier * engine_clock) / (memory_clock / 2);
+       u32 burst_time;
+
+       if (result <= 4)
+               burst_time = 0;
+       else if (result < 8)
+               burst_time = result - 4;
+       else {
+               burst_time = result / 2 ;
+               if (burst_time > 18)
+                       burst_time = 18;
+       }
+
+       return burst_time;
+}
+
+void cypress_program_memory_timing_parameters(struct radeon_device *rdev,
+                                             struct radeon_ps *radeon_new_state)
+{
+       struct rv7xx_ps *new_state = rv770_get_ps(radeon_new_state);
+       u32 mc_arb_burst_time = RREG32(MC_ARB_BURST_TIME);
+
+       mc_arb_burst_time &= ~(STATE1_MASK | STATE2_MASK | STATE3_MASK);
+
+       mc_arb_burst_time |= STATE1(cypress_calculate_burst_time(rdev,
+                                                                new_state->low.sclk,
+                                                                new_state->low.mclk));
+       mc_arb_burst_time |= STATE2(cypress_calculate_burst_time(rdev,
+                                                                new_state->medium.sclk,
+                                                                new_state->medium.mclk));
+       mc_arb_burst_time |= STATE3(cypress_calculate_burst_time(rdev,
+                                                                new_state->high.sclk,
+                                                                new_state->high.mclk));
+
+       rv730_program_memory_timing_parameters(rdev, radeon_new_state);
+
+       WREG32(MC_ARB_BURST_TIME, mc_arb_burst_time);
+}
+
+static void cypress_populate_mc_reg_addresses(struct radeon_device *rdev,
+                                             SMC_Evergreen_MCRegisters *mc_reg_table)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 i, j;
+
+       for (i = 0, j = 0; j < eg_pi->mc_reg_table.last; j++) {
+               if (eg_pi->mc_reg_table.valid_flag & (1 << j)) {
+                       mc_reg_table->address[i].s0 =
+                               cpu_to_be16(eg_pi->mc_reg_table.mc_reg_address[j].s0);
+                       mc_reg_table->address[i].s1 =
+                               cpu_to_be16(eg_pi->mc_reg_table.mc_reg_address[j].s1);
+                       i++;
+               }
+       }
+
+       mc_reg_table->last = (u8)i;
+}
+
+static void cypress_set_mc_reg_address_table(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 i = 0;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_RAS_TIMING_LP >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_RAS_TIMING >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_CAS_TIMING_LP >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_CAS_TIMING >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_MISC_TIMING_LP >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_MISC_TIMING >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_MISC_TIMING2_LP >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_MISC_TIMING2 >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_RD_CTL_D0_LP >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_RD_CTL_D0 >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_RD_CTL_D1_LP >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_RD_CTL_D1 >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_WR_CTL_D0_LP >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_WR_CTL_D0 >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_WR_CTL_D1_LP >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_WR_CTL_D1 >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_PMG_CMD_EMRS >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_PMG_CMD_MRS_LP >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_PMG_CMD_MRS >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_PMG_CMD_MRS1 >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_MISC1 >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_MISC1 >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_RESERVE_M >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_RESERVE_M >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_MISC3 >> 2;
+       eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_MISC3 >> 2;
+       i++;
+
+       eg_pi->mc_reg_table.last = (u8)i;
+}
+
+static void cypress_retrieve_ac_timing_for_one_entry(struct radeon_device *rdev,
+                                                    struct evergreen_mc_reg_entry *entry)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 i;
+
+       for (i = 0; i < eg_pi->mc_reg_table.last; i++)
+               entry->mc_data[i] =
+                       RREG32(eg_pi->mc_reg_table.mc_reg_address[i].s1 << 2);
+
+}
+
+static void cypress_retrieve_ac_timing_for_all_ranges(struct radeon_device *rdev,
+                                                     struct atom_memory_clock_range_table *range_table)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 i, j;
+
+       for (i = 0; i < range_table->num_entries; i++) {
+               eg_pi->mc_reg_table.mc_reg_table_entry[i].mclk_max =
+                       range_table->mclk[i];
+               radeon_atom_set_ac_timing(rdev, range_table->mclk[i]);
+               cypress_retrieve_ac_timing_for_one_entry(rdev,
+                                                        &eg_pi->mc_reg_table.mc_reg_table_entry[i]);
+       }
+
+       eg_pi->mc_reg_table.num_entries = range_table->num_entries;
+       eg_pi->mc_reg_table.valid_flag = 0;
+
+       for (i = 0; i < eg_pi->mc_reg_table.last; i++) {
+               for (j = 1; j < range_table->num_entries; j++) {
+                       if (eg_pi->mc_reg_table.mc_reg_table_entry[j-1].mc_data[i] !=
+                           eg_pi->mc_reg_table.mc_reg_table_entry[j].mc_data[i]) {
+                               eg_pi->mc_reg_table.valid_flag |= (1 << i);
+                               break;
+                       }
+               }
+       }
+}
+
+static int cypress_initialize_mc_reg_table(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u8 module_index = rv770_get_memory_module_index(rdev);
+       struct atom_memory_clock_range_table range_table = { 0 };
+       int ret;
+
+       ret = radeon_atom_get_mclk_range_table(rdev,
+                                              pi->mem_gddr5,
+                                              module_index, &range_table);
+       if (ret)
+               return ret;
+
+       cypress_retrieve_ac_timing_for_all_ranges(rdev, &range_table);
+
+       return 0;
+}
+
+static void cypress_wait_for_mc_sequencer(struct radeon_device *rdev, u8 value)
+{
+       u32 i, j;
+       u32 channels = 2;
+
+       if ((rdev->family == CHIP_CYPRESS) ||
+           (rdev->family == CHIP_HEMLOCK))
+               channels = 4;
+       else if (rdev->family == CHIP_CEDAR)
+               channels = 1;
+
+       for (i = 0; i < channels; i++) {
+               if ((rdev->family == CHIP_CYPRESS) ||
+                   (rdev->family == CHIP_HEMLOCK)) {
+                       WREG32_P(MC_CONFIG_MCD, MC_RD_ENABLE_MCD(i), ~MC_RD_ENABLE_MCD_MASK);
+                       WREG32_P(MC_CG_CONFIG_MCD, MC_RD_ENABLE_MCD(i), ~MC_RD_ENABLE_MCD_MASK);
+               } else {
+                       WREG32_P(MC_CONFIG, MC_RD_ENABLE(i), ~MC_RD_ENABLE_MASK);
+                       WREG32_P(MC_CG_CONFIG, MC_RD_ENABLE(i), ~MC_RD_ENABLE_MASK);
+               }
+               for (j = 0; j < rdev->usec_timeout; j++) {
+                       if (((RREG32(MC_SEQ_CG) & CG_SEQ_RESP_MASK) >> CG_SEQ_RESP_SHIFT) == value)
+                               break;
+                       udelay(1);
+               }
+       }
+}
+
+static void cypress_force_mc_use_s1(struct radeon_device *rdev,
+                                   struct radeon_ps *radeon_boot_state)
+{
+       struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state);
+       u32 strobe_mode;
+       u32 mc_seq_cg;
+       int i;
+
+       if (RREG32(MC_SEQ_STATUS_M) & PMG_PWRSTATE)
+               return;
+
+       radeon_atom_set_ac_timing(rdev, boot_state->low.mclk);
+       radeon_mc_wait_for_idle(rdev);
+
+       if ((rdev->family == CHIP_CYPRESS) ||
+           (rdev->family == CHIP_HEMLOCK)) {
+               WREG32(MC_CONFIG_MCD, 0xf);
+               WREG32(MC_CG_CONFIG_MCD, 0xf);
+       } else {
+               WREG32(MC_CONFIG, 0xf);
+               WREG32(MC_CG_CONFIG, 0xf);
+       }
+
+       for (i = 0; i < rdev->num_crtc; i++)
+               radeon_wait_for_vblank(rdev, i);
+
+       WREG32(MC_SEQ_CG, MC_CG_SEQ_YCLK_SUSPEND);
+       cypress_wait_for_mc_sequencer(rdev, MC_CG_SEQ_YCLK_SUSPEND);
+
+       strobe_mode = cypress_get_strobe_mode_settings(rdev,
+                                                      boot_state->low.mclk);
+
+       mc_seq_cg = CG_SEQ_REQ(MC_CG_SEQ_DRAMCONF_S1);
+       mc_seq_cg |= SEQ_CG_RESP(strobe_mode);
+       WREG32(MC_SEQ_CG, mc_seq_cg);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(MC_SEQ_STATUS_M) & PMG_PWRSTATE)
+                       break;
+               udelay(1);
+       }
+
+       mc_seq_cg &= ~CG_SEQ_REQ_MASK;
+       mc_seq_cg |= CG_SEQ_REQ(MC_CG_SEQ_YCLK_RESUME);
+       WREG32(MC_SEQ_CG, mc_seq_cg);
+
+       cypress_wait_for_mc_sequencer(rdev, MC_CG_SEQ_YCLK_RESUME);
+}
+
+static void cypress_copy_ac_timing_from_s1_to_s0(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 value;
+       u32 i;
+
+       for (i = 0; i < eg_pi->mc_reg_table.last; i++) {
+               value = RREG32(eg_pi->mc_reg_table.mc_reg_address[i].s1 << 2);
+               WREG32(eg_pi->mc_reg_table.mc_reg_address[i].s0 << 2, value);
+       }
+}
+
+static void cypress_force_mc_use_s0(struct radeon_device *rdev,
+                                   struct radeon_ps *radeon_boot_state)
+{
+       struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state);
+       u32 strobe_mode;
+       u32 mc_seq_cg;
+       int i;
+
+       cypress_copy_ac_timing_from_s1_to_s0(rdev);
+       radeon_mc_wait_for_idle(rdev);
+
+       if ((rdev->family == CHIP_CYPRESS) ||
+           (rdev->family == CHIP_HEMLOCK)) {
+               WREG32(MC_CONFIG_MCD, 0xf);
+               WREG32(MC_CG_CONFIG_MCD, 0xf);
+       } else {
+               WREG32(MC_CONFIG, 0xf);
+               WREG32(MC_CG_CONFIG, 0xf);
+       }
+
+       for (i = 0; i < rdev->num_crtc; i++)
+               radeon_wait_for_vblank(rdev, i);
+
+       WREG32(MC_SEQ_CG, MC_CG_SEQ_YCLK_SUSPEND);
+       cypress_wait_for_mc_sequencer(rdev, MC_CG_SEQ_YCLK_SUSPEND);
+
+       strobe_mode = cypress_get_strobe_mode_settings(rdev,
+                                                      boot_state->low.mclk);
+
+       mc_seq_cg = CG_SEQ_REQ(MC_CG_SEQ_DRAMCONF_S0);
+       mc_seq_cg |= SEQ_CG_RESP(strobe_mode);
+       WREG32(MC_SEQ_CG, mc_seq_cg);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (!(RREG32(MC_SEQ_STATUS_M) & PMG_PWRSTATE))
+                       break;
+               udelay(1);
+       }
+
+       mc_seq_cg &= ~CG_SEQ_REQ_MASK;
+       mc_seq_cg |= CG_SEQ_REQ(MC_CG_SEQ_YCLK_RESUME);
+       WREG32(MC_SEQ_CG, mc_seq_cg);
+
+       cypress_wait_for_mc_sequencer(rdev, MC_CG_SEQ_YCLK_RESUME);
+}
+
+static int cypress_populate_initial_mvdd_value(struct radeon_device *rdev,
+                                              RV770_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       voltage->index = eg_pi->mvdd_high_index;
+       voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
+
+       return 0;
+}
+
+int cypress_populate_smc_initial_state(struct radeon_device *rdev,
+                                      struct radeon_ps *radeon_initial_state,
+                                      RV770_SMC_STATETABLE *table)
+{
+       struct rv7xx_ps *initial_state = rv770_get_ps(radeon_initial_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 a_t;
+
+       table->initialState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_ad_func_cntl);
+       table->initialState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL_2 =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_ad_func_cntl_2);
+       table->initialState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_dq_func_cntl);
+       table->initialState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL_2 =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_dq_func_cntl_2);
+       table->initialState.levels[0].mclk.mclk770.vMCLK_PWRMGT_CNTL =
+               cpu_to_be32(pi->clk_regs.rv770.mclk_pwrmgt_cntl);
+       table->initialState.levels[0].mclk.mclk770.vDLL_CNTL =
+               cpu_to_be32(pi->clk_regs.rv770.dll_cntl);
+
+       table->initialState.levels[0].mclk.mclk770.vMPLL_SS =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_ss1);
+       table->initialState.levels[0].mclk.mclk770.vMPLL_SS2 =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_ss2);
+
+       table->initialState.levels[0].mclk.mclk770.mclk_value =
+               cpu_to_be32(initial_state->low.mclk);
+
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
+               cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+               cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl_2);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+               cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl_3);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM =
+               cpu_to_be32(pi->clk_regs.rv770.cg_spll_spread_spectrum);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2 =
+               cpu_to_be32(pi->clk_regs.rv770.cg_spll_spread_spectrum_2);
+
+       table->initialState.levels[0].sclk.sclk_value =
+               cpu_to_be32(initial_state->low.sclk);
+
+       table->initialState.levels[0].arbValue = MC_CG_ARB_FREQ_F0;
+
+       table->initialState.levels[0].ACIndex = 0;
+
+       cypress_populate_voltage_value(rdev,
+                                      &eg_pi->vddc_voltage_table,
+                                      initial_state->low.vddc,
+                                      &table->initialState.levels[0].vddc);
+
+       if (eg_pi->vddci_control)
+               cypress_populate_voltage_value(rdev,
+                                              &eg_pi->vddci_voltage_table,
+                                              initial_state->low.vddci,
+                                              &table->initialState.levels[0].vddci);
+
+       cypress_populate_initial_mvdd_value(rdev,
+                                           &table->initialState.levels[0].mvdd);
+
+       a_t = CG_R(0xffff) | CG_L(0);
+       table->initialState.levels[0].aT = cpu_to_be32(a_t);
+
+       table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp);
+
+
+       if (pi->boot_in_gen2)
+               table->initialState.levels[0].gen2PCIE = 1;
+       else
+               table->initialState.levels[0].gen2PCIE = 0;
+       if (initial_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2)
+               table->initialState.levels[0].gen2XSP = 1;
+       else
+               table->initialState.levels[0].gen2XSP = 0;
+
+       if (pi->mem_gddr5) {
+               table->initialState.levels[0].strobeMode =
+                       cypress_get_strobe_mode_settings(rdev,
+                                                        initial_state->low.mclk);
+
+               if (initial_state->low.mclk > pi->mclk_edc_enable_threshold)
+                       table->initialState.levels[0].mcFlags = SMC_MC_EDC_RD_FLAG | SMC_MC_EDC_WR_FLAG;
+               else
+                       table->initialState.levels[0].mcFlags =  0;
+       }
+
+       table->initialState.levels[1] = table->initialState.levels[0];
+       table->initialState.levels[2] = table->initialState.levels[0];
+
+       table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       return 0;
+}
+
+int cypress_populate_smc_acpi_state(struct radeon_device *rdev,
+                                   RV770_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 mpll_ad_func_cntl =
+               pi->clk_regs.rv770.mpll_ad_func_cntl;
+       u32 mpll_ad_func_cntl_2 =
+               pi->clk_regs.rv770.mpll_ad_func_cntl_2;
+       u32 mpll_dq_func_cntl =
+               pi->clk_regs.rv770.mpll_dq_func_cntl;
+       u32 mpll_dq_func_cntl_2 =
+               pi->clk_regs.rv770.mpll_dq_func_cntl_2;
+       u32 spll_func_cntl =
+               pi->clk_regs.rv770.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 =
+               pi->clk_regs.rv770.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 =
+               pi->clk_regs.rv770.cg_spll_func_cntl_3;
+       u32 mclk_pwrmgt_cntl =
+               pi->clk_regs.rv770.mclk_pwrmgt_cntl;
+       u32 dll_cntl =
+               pi->clk_regs.rv770.dll_cntl;
+
+       table->ACPIState = table->initialState;
+
+       table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       if (pi->acpi_vddc) {
+               cypress_populate_voltage_value(rdev,
+                                              &eg_pi->vddc_voltage_table,
+                                              pi->acpi_vddc,
+                                              &table->ACPIState.levels[0].vddc);
+               if (pi->pcie_gen2) {
+                       if (pi->acpi_pcie_gen2)
+                               table->ACPIState.levels[0].gen2PCIE = 1;
+                       else
+                               table->ACPIState.levels[0].gen2PCIE = 0;
+               } else
+                       table->ACPIState.levels[0].gen2PCIE = 0;
+               if (pi->acpi_pcie_gen2)
+                       table->ACPIState.levels[0].gen2XSP = 1;
+               else
+                       table->ACPIState.levels[0].gen2XSP = 0;
+       } else {
+               cypress_populate_voltage_value(rdev,
+                                              &eg_pi->vddc_voltage_table,
+                                              pi->min_vddc_in_table,
+                                              &table->ACPIState.levels[0].vddc);
+               table->ACPIState.levels[0].gen2PCIE = 0;
+       }
+
+       if (eg_pi->acpi_vddci) {
+               if (eg_pi->vddci_control) {
+                       cypress_populate_voltage_value(rdev,
+                                                      &eg_pi->vddci_voltage_table,
+                                                      eg_pi->acpi_vddci,
+                                                      &table->ACPIState.levels[0].vddci);
+               }
+       }
+
+       mpll_ad_func_cntl &= ~PDNB;
+
+       mpll_ad_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN;
+
+       if (pi->mem_gddr5)
+               mpll_dq_func_cntl &= ~PDNB;
+       mpll_dq_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN | BYPASS;
+
+       mclk_pwrmgt_cntl |= (MRDCKA0_RESET |
+                            MRDCKA1_RESET |
+                            MRDCKB0_RESET |
+                            MRDCKB1_RESET |
+                            MRDCKC0_RESET |
+                            MRDCKC1_RESET |
+                            MRDCKD0_RESET |
+                            MRDCKD1_RESET);
+
+       mclk_pwrmgt_cntl &= ~(MRDCKA0_PDNB |
+                             MRDCKA1_PDNB |
+                             MRDCKB0_PDNB |
+                             MRDCKB1_PDNB |
+                             MRDCKC0_PDNB |
+                             MRDCKC1_PDNB |
+                             MRDCKD0_PDNB |
+                             MRDCKD1_PDNB);
+
+       dll_cntl |= (MRDCKA0_BYPASS |
+                    MRDCKA1_BYPASS |
+                    MRDCKB0_BYPASS |
+                    MRDCKB1_BYPASS |
+                    MRDCKC0_BYPASS |
+                    MRDCKC1_BYPASS |
+                    MRDCKD0_BYPASS |
+                    MRDCKD1_BYPASS);
+
+       /* evergreen only */
+       if (rdev->family <= CHIP_HEMLOCK)
+               spll_func_cntl |= SPLL_RESET | SPLL_SLEEP | SPLL_BYPASS_EN;
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(4);
+
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL =
+               cpu_to_be32(mpll_ad_func_cntl);
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL_2 =
+               cpu_to_be32(mpll_ad_func_cntl_2);
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL =
+               cpu_to_be32(mpll_dq_func_cntl);
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL_2 =
+               cpu_to_be32(mpll_dq_func_cntl_2);
+       table->ACPIState.levels[0].mclk.mclk770.vMCLK_PWRMGT_CNTL =
+               cpu_to_be32(mclk_pwrmgt_cntl);
+       table->ACPIState.levels[0].mclk.mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl);
+
+       table->ACPIState.levels[0].mclk.mclk770.mclk_value = 0;
+
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
+               cpu_to_be32(spll_func_cntl);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+               cpu_to_be32(spll_func_cntl_2);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+               cpu_to_be32(spll_func_cntl_3);
+
+       table->ACPIState.levels[0].sclk.sclk_value = 0;
+
+       cypress_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd);
+
+       if (eg_pi->dynamic_ac_timing)
+               table->ACPIState.levels[0].ACIndex = 1;
+
+       table->ACPIState.levels[1] = table->ACPIState.levels[0];
+       table->ACPIState.levels[2] = table->ACPIState.levels[0];
+
+       return 0;
+}
+
+static void cypress_trim_voltage_table_to_fit_state_table(struct radeon_device *rdev,
+                                                         struct atom_voltage_table *voltage_table)
+{
+       unsigned int i, diff;
+
+       if (voltage_table->count <= MAX_NO_VREG_STEPS)
+               return;
+
+       diff = voltage_table->count - MAX_NO_VREG_STEPS;
+
+       for (i= 0; i < MAX_NO_VREG_STEPS; i++)
+               voltage_table->entries[i] = voltage_table->entries[i + diff];
+
+       voltage_table->count = MAX_NO_VREG_STEPS;
+}
+
+int cypress_construct_voltage_tables(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       int ret;
+
+       ret = radeon_atom_get_voltage_table(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, 0,
+                                           &eg_pi->vddc_voltage_table);
+       if (ret)
+               return ret;
+
+       if (eg_pi->vddc_voltage_table.count > MAX_NO_VREG_STEPS)
+               cypress_trim_voltage_table_to_fit_state_table(rdev,
+                                                             &eg_pi->vddc_voltage_table);
+
+       if (eg_pi->vddci_control) {
+               ret = radeon_atom_get_voltage_table(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI, 0,
+                                                   &eg_pi->vddci_voltage_table);
+               if (ret)
+                       return ret;
+
+               if (eg_pi->vddci_voltage_table.count > MAX_NO_VREG_STEPS)
+                       cypress_trim_voltage_table_to_fit_state_table(rdev,
+                                                                     &eg_pi->vddci_voltage_table);
+       }
+
+       return 0;
+}
+
+static void cypress_populate_smc_voltage_table(struct radeon_device *rdev,
+                                              struct atom_voltage_table *voltage_table,
+                                              RV770_SMC_STATETABLE *table)
+{
+       unsigned int i;
+
+       for (i = 0; i < voltage_table->count; i++) {
+               table->highSMIO[i] = 0;
+               table->lowSMIO[i] |= cpu_to_be32(voltage_table->entries[i].smio_low);
+       }
+}
+
+int cypress_populate_smc_voltage_tables(struct radeon_device *rdev,
+                                       RV770_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       unsigned char i;
+
+       if (eg_pi->vddc_voltage_table.count) {
+               cypress_populate_smc_voltage_table(rdev,
+                                                  &eg_pi->vddc_voltage_table,
+                                                  table);
+
+               table->voltageMaskTable.highMask[RV770_SMC_VOLTAGEMASK_VDDC] = 0;
+               table->voltageMaskTable.lowMask[RV770_SMC_VOLTAGEMASK_VDDC] =
+                       cpu_to_be32(eg_pi->vddc_voltage_table.mask_low);
+
+               for (i = 0; i < eg_pi->vddc_voltage_table.count; i++) {
+                       if (pi->max_vddc_in_table <=
+                           eg_pi->vddc_voltage_table.entries[i].value) {
+                               table->maxVDDCIndexInPPTable = i;
+                               break;
+                       }
+               }
+       }
+
+       if (eg_pi->vddci_voltage_table.count) {
+               cypress_populate_smc_voltage_table(rdev,
+                                                  &eg_pi->vddci_voltage_table,
+                                                  table);
+
+               table->voltageMaskTable.highMask[RV770_SMC_VOLTAGEMASK_VDDCI] = 0;
+               table->voltageMaskTable.lowMask[RV770_SMC_VOLTAGEMASK_VDDCI] =
+                       cpu_to_be32(eg_pi->vddc_voltage_table.mask_low);
+       }
+
+       return 0;
+}
+
+static u32 cypress_get_mclk_split_point(struct atom_memory_info *memory_info)
+{
+       if ((memory_info->mem_type == MEM_TYPE_GDDR3) ||
+           (memory_info->mem_type == MEM_TYPE_DDR3))
+               return 30000;
+
+       return 0;
+}
+
+int cypress_get_mvdd_configuration(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u8 module_index;
+       struct atom_memory_info memory_info;
+       u32 tmp = RREG32(GENERAL_PWRMGT);
+
+       if (!(tmp & BACKBIAS_PAD_EN)) {
+               eg_pi->mvdd_high_index = 0;
+               eg_pi->mvdd_low_index = 1;
+               pi->mvdd_control = false;
+               return 0;
+       }
+
+       if (tmp & BACKBIAS_VALUE)
+               eg_pi->mvdd_high_index = 1;
+       else
+               eg_pi->mvdd_high_index = 0;
+
+       eg_pi->mvdd_low_index =
+               (eg_pi->mvdd_high_index == 0) ? 1 : 0;
+
+       module_index = rv770_get_memory_module_index(rdev);
+
+       if (radeon_atom_get_memory_info(rdev, module_index, &memory_info)) {
+               pi->mvdd_control = false;
+               return 0;
+       }
+
+       pi->mvdd_split_frequency =
+               cypress_get_mclk_split_point(&memory_info);
+
+       if (pi->mvdd_split_frequency == 0) {
+               pi->mvdd_control = false;
+               return 0;
+       }
+
+       return 0;
+}
+
+static int cypress_init_smc_table(struct radeon_device *rdev,
+                                 struct radeon_ps *radeon_boot_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       RV770_SMC_STATETABLE *table = &pi->smc_statetable;
+       int ret;
+
+       memset(table, 0, sizeof(RV770_SMC_STATETABLE));
+
+       cypress_populate_smc_voltage_tables(rdev, table);
+
+       switch (rdev->pm.int_thermal_type) {
+        case THERMAL_TYPE_EVERGREEN:
+        case THERMAL_TYPE_EMC2103_WITH_INTERNAL:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL;
+               break;
+        case THERMAL_TYPE_NONE:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE;
+               break;
+        default:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL;
+               break;
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (pi->mem_gddr5)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+       ret = cypress_populate_smc_initial_state(rdev, radeon_boot_state, table);
+       if (ret)
+               return ret;
+
+       ret = cypress_populate_smc_acpi_state(rdev, table);
+       if (ret)
+               return ret;
+
+       table->driverState = table->initialState;
+
+       return rv770_copy_bytes_to_smc(rdev,
+                                      pi->state_table_start,
+                                      (u8 *)table, sizeof(RV770_SMC_STATETABLE),
+                                      pi->sram_end);
+}
+
+int cypress_populate_mc_reg_table(struct radeon_device *rdev,
+                                 struct radeon_ps *radeon_boot_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state);
+       SMC_Evergreen_MCRegisters mc_reg_table = { 0 };
+
+       rv770_write_smc_soft_register(rdev,
+                                     RV770_SMC_SOFT_REGISTER_seq_index, 1);
+
+       cypress_populate_mc_reg_addresses(rdev, &mc_reg_table);
+
+       cypress_convert_mc_reg_table_entry_to_smc(rdev,
+                                                 &boot_state->low,
+                                                 &mc_reg_table.data[0]);
+
+       cypress_convert_mc_registers(&eg_pi->mc_reg_table.mc_reg_table_entry[0],
+                                    &mc_reg_table.data[1], eg_pi->mc_reg_table.last,
+                                    eg_pi->mc_reg_table.valid_flag);
+
+       cypress_convert_mc_reg_table_to_smc(rdev, radeon_boot_state, &mc_reg_table);
+
+       return rv770_copy_bytes_to_smc(rdev, eg_pi->mc_reg_table_start,
+                                      (u8 *)&mc_reg_table, sizeof(SMC_Evergreen_MCRegisters),
+                                      pi->sram_end);
+}
+
+int cypress_get_table_locations(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = rv770_read_smc_sram_dword(rdev,
+                                       EVERGREEN_SMC_FIRMWARE_HEADER_LOCATION +
+                                       EVERGREEN_SMC_FIRMWARE_HEADER_stateTable,
+                                       &tmp, pi->sram_end);
+       if (ret)
+               return ret;
+
+       pi->state_table_start = (u16)tmp;
+
+       ret = rv770_read_smc_sram_dword(rdev,
+                                       EVERGREEN_SMC_FIRMWARE_HEADER_LOCATION +
+                                       EVERGREEN_SMC_FIRMWARE_HEADER_softRegisters,
+                                       &tmp, pi->sram_end);
+       if (ret)
+               return ret;
+
+       pi->soft_regs_start = (u16)tmp;
+
+       ret = rv770_read_smc_sram_dword(rdev,
+                                       EVERGREEN_SMC_FIRMWARE_HEADER_LOCATION +
+                                       EVERGREEN_SMC_FIRMWARE_HEADER_mcRegisterTable,
+                                       &tmp, pi->sram_end);
+       if (ret)
+               return ret;
+
+       eg_pi->mc_reg_table_start = (u16)tmp;
+
+       return 0;
+}
+
+void cypress_enable_display_gap(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL);
+
+       tmp &= ~(DISP1_GAP_MASK | DISP2_GAP_MASK);
+       tmp |= (DISP1_GAP(R600_PM_DISPLAY_GAP_IGNORE) |
+               DISP2_GAP(R600_PM_DISPLAY_GAP_IGNORE));
+
+       tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK);
+       tmp |= (DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK) |
+               DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE));
+       WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+}
+
+static void cypress_program_display_gap(struct radeon_device *rdev)
+{
+       u32 tmp, pipe;
+       int i;
+
+       tmp = RREG32(CG_DISPLAY_GAP_CNTL) & ~(DISP1_GAP_MASK | DISP2_GAP_MASK);
+       if (rdev->pm.dpm.new_active_crtc_count > 0)
+               tmp |= DISP1_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM);
+       else
+               tmp |= DISP1_GAP(R600_PM_DISPLAY_GAP_IGNORE);
+
+       if (rdev->pm.dpm.new_active_crtc_count > 1)
+               tmp |= DISP2_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM);
+       else
+               tmp |= DISP2_GAP(R600_PM_DISPLAY_GAP_IGNORE);
+
+       WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+
+       tmp = RREG32(DCCG_DISP_SLOW_SELECT_REG);
+       pipe = (tmp & DCCG_DISP1_SLOW_SELECT_MASK) >> DCCG_DISP1_SLOW_SELECT_SHIFT;
+
+       if ((rdev->pm.dpm.new_active_crtc_count > 0) &&
+           (!(rdev->pm.dpm.new_active_crtcs & (1 << pipe)))) {
+               /* find the first active crtc */
+               for (i = 0; i < rdev->num_crtc; i++) {
+                       if (rdev->pm.dpm.new_active_crtcs & (1 << i))
+                               break;
+               }
+               if (i == rdev->num_crtc)
+                       pipe = 0;
+               else
+                       pipe = i;
+
+               tmp &= ~DCCG_DISP1_SLOW_SELECT_MASK;
+               tmp |= DCCG_DISP1_SLOW_SELECT(pipe);
+               WREG32(DCCG_DISP_SLOW_SELECT_REG, tmp);
+       }
+
+       cypress_notify_smc_display_change(rdev, rdev->pm.dpm.new_active_crtc_count > 0);
+}
+
+void cypress_dpm_setup_asic(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       rv740_read_clock_registers(rdev);
+       rv770_read_voltage_smio_registers(rdev);
+       rv770_get_max_vddc(rdev);
+       rv770_get_memory_type(rdev);
+
+       if (eg_pi->pcie_performance_request)
+               eg_pi->pcie_performance_request_registered = false;
+
+       if (eg_pi->pcie_performance_request)
+               cypress_advertise_gen2_capability(rdev);
+
+       rv770_get_pcie_gen2_status(rdev);
+
+       rv770_enable_acpi_pm(rdev);
+}
+
+int cypress_dpm_enable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+       int ret;
+
+       if (pi->gfx_clock_gating)
+               rv770_restore_cgcg(rdev);
+
+       if (rv770_dpm_enabled(rdev))
+               return -EINVAL;
+
+       if (pi->voltage_control) {
+               rv770_enable_voltage_control(rdev, true);
+               ret = cypress_construct_voltage_tables(rdev);
+               if (ret) {
+                       DRM_ERROR("cypress_construct_voltage_tables failed\n");
+                       return ret;
+               }
+       }
+
+       if (pi->mvdd_control) {
+               ret = cypress_get_mvdd_configuration(rdev);
+               if (ret) {
+                       DRM_ERROR("cypress_get_mvdd_configuration failed\n");
+                       return ret;
+               }
+       }
+
+       if (eg_pi->dynamic_ac_timing) {
+               cypress_set_mc_reg_address_table(rdev);
+               cypress_force_mc_use_s0(rdev, boot_ps);
+               ret = cypress_initialize_mc_reg_table(rdev);
+               if (ret)
+                       eg_pi->dynamic_ac_timing = false;
+               cypress_force_mc_use_s1(rdev, boot_ps);
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
+               rv770_enable_backbias(rdev, true);
+
+       if (pi->dynamic_ss)
+               cypress_enable_spread_spectrum(rdev, true);
+
+       if (pi->thermal_protection)
+               rv770_enable_thermal_protection(rdev, true);
+
+       rv770_setup_bsp(rdev);
+       rv770_program_git(rdev);
+       rv770_program_tp(rdev);
+       rv770_program_tpp(rdev);
+       rv770_program_sstp(rdev);
+       rv770_program_engine_speed_parameters(rdev);
+       cypress_enable_display_gap(rdev);
+       rv770_program_vc(rdev);
+
+       if (pi->dynamic_pcie_gen2)
+               cypress_enable_dynamic_pcie_gen2(rdev, true);
+
+       ret = rv770_upload_firmware(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_upload_firmware failed\n");
+               return ret;
+       }
+
+       ret = cypress_get_table_locations(rdev);
+       if (ret) {
+               DRM_ERROR("cypress_get_table_locations failed\n");
+               return ret;
+       }
+       ret = cypress_init_smc_table(rdev, boot_ps);
+       if (ret) {
+               DRM_ERROR("cypress_init_smc_table failed\n");
+               return ret;
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = cypress_populate_mc_reg_table(rdev, boot_ps);
+               if (ret) {
+                       DRM_ERROR("cypress_populate_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+
+       cypress_program_response_times(rdev);
+
+       r7xx_start_smc(rdev);
+
+       ret = cypress_notify_smc_display_change(rdev, false);
+       if (ret) {
+               DRM_ERROR("cypress_notify_smc_display_change failed\n");
+               return ret;
+       }
+       cypress_enable_sclk_control(rdev, true);
+
+       if (eg_pi->memory_transition)
+               cypress_enable_mclk_control(rdev, true);
+
+       cypress_start_dpm(rdev);
+
+       if (pi->gfx_clock_gating)
+               cypress_gfx_clock_gating_enable(rdev, true);
+
+       if (pi->mg_clock_gating)
+               cypress_mg_clock_gating_enable(rdev, true);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               PPSMC_Result result;
+
+               ret = rv770_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+               if (ret)
+                       return ret;
+               rdev->irq.dpm_thermal = true;
+               radeon_irq_set(rdev);
+               result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt);
+
+               if (result != PPSMC_Result_OK)
+                       DRM_DEBUG_KMS("Could not enable thermal interrupts.\n");
+       }
+
+       rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+
+       return 0;
+}
+
+void cypress_dpm_disable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+
+       if (!rv770_dpm_enabled(rdev))
+               return;
+
+       rv770_clear_vc(rdev);
+
+       if (pi->thermal_protection)
+               rv770_enable_thermal_protection(rdev, false);
+
+       if (pi->dynamic_pcie_gen2)
+               cypress_enable_dynamic_pcie_gen2(rdev, false);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               rdev->irq.dpm_thermal = false;
+               radeon_irq_set(rdev);
+       }
+
+       if (pi->gfx_clock_gating)
+               cypress_gfx_clock_gating_enable(rdev, false);
+
+       if (pi->mg_clock_gating)
+               cypress_mg_clock_gating_enable(rdev, false);
+
+       rv770_stop_dpm(rdev);
+       r7xx_stop_smc(rdev);
+
+       cypress_enable_spread_spectrum(rdev, false);
+
+       if (eg_pi->dynamic_ac_timing)
+               cypress_force_mc_use_s1(rdev, boot_ps);
+
+       rv770_reset_smio_status(rdev);
+}
+
+int cypress_dpm_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps;
+       struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
+       int ret;
+
+       ret = rv770_restrict_performance_levels_before_switch(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_restrict_performance_levels_before_switch failed\n");
+               return ret;
+       }
+       if (eg_pi->pcie_performance_request)
+               cypress_notify_link_speed_change_before_state_change(rdev, new_ps, old_ps);
+
+       rv770_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps);
+       ret = rv770_halt_smc(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_halt_smc failed\n");
+               return ret;
+       }
+       ret = cypress_upload_sw_state(rdev, new_ps);
+       if (ret) {
+               DRM_ERROR("cypress_upload_sw_state failed\n");
+               return ret;
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = cypress_upload_mc_reg_table(rdev, new_ps);
+               if (ret) {
+                       DRM_ERROR("cypress_upload_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+
+       cypress_program_memory_timing_parameters(rdev, new_ps);
+
+       ret = rv770_resume_smc(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_resume_smc failed\n");
+               return ret;
+       }
+       ret = rv770_set_sw_state(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_set_sw_state failed\n");
+               return ret;
+       }
+       rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+
+       if (eg_pi->pcie_performance_request)
+               cypress_notify_link_speed_change_after_state_change(rdev, new_ps, old_ps);
+
+       ret = rv770_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
+       if (ret) {
+               DRM_ERROR("rv770_dpm_force_performance_level failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+void cypress_dpm_reset_asic(struct radeon_device *rdev)
+{
+       rv770_restrict_performance_levels_before_switch(rdev);
+       rv770_set_boot_state(rdev);
+}
+
+void cypress_dpm_display_configuration_changed(struct radeon_device *rdev)
+{
+       cypress_program_display_gap(rdev);
+}
+
+int cypress_dpm_init(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi;
+       struct evergreen_power_info *eg_pi;
+       int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
+       uint16_t data_offset, size;
+       uint8_t frev, crev;
+       struct atom_clock_dividers dividers;
+       int ret;
+
+       eg_pi = kzalloc(sizeof(struct evergreen_power_info), GFP_KERNEL);
+       if (eg_pi == NULL)
+               return -ENOMEM;
+       rdev->pm.dpm.priv = eg_pi;
+       pi = &eg_pi->rv7xx;
+
+       rv770_get_max_vddc(rdev);
+
+       eg_pi->ulv.supported = false;
+       pi->acpi_vddc = 0;
+       eg_pi->acpi_vddci = 0;
+       pi->min_vddc_in_table = 0;
+       pi->max_vddc_in_table = 0;
+
+       ret = rv7xx_parse_power_table(rdev);
+       if (ret)
+               return ret;
+
+       if (rdev->pm.dpm.voltage_response_time == 0)
+               rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT;
+       if (rdev->pm.dpm.backbias_response_time == 0)
+               rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            0, false, &dividers);
+       if (ret)
+               pi->ref_div = dividers.ref_div + 1;
+       else
+               pi->ref_div = R600_REFERENCEDIVIDER_DFLT;
+
+       pi->mclk_strobe_mode_threshold = 40000;
+       pi->mclk_edc_enable_threshold = 40000;
+       eg_pi->mclk_edc_wr_enable_threshold = 40000;
+
+       pi->rlp = RV770_RLP_DFLT;
+       pi->rmp = RV770_RMP_DFLT;
+       pi->lhp = RV770_LHP_DFLT;
+       pi->lmp = RV770_LMP_DFLT;
+
+       pi->voltage_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, 0);
+
+       pi->mvdd_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC, 0);
+
+       eg_pi->vddci_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI, 0);
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                   &frev, &crev, &data_offset)) {
+               pi->sclk_ss = true;
+               pi->mclk_ss = true;
+               pi->dynamic_ss = true;
+       } else {
+               pi->sclk_ss = false;
+               pi->mclk_ss = false;
+               pi->dynamic_ss = true;
+       }
+
+       pi->asi = RV770_ASI_DFLT;
+       pi->pasi = CYPRESS_HASI_DFLT;
+       pi->vrc = CYPRESS_VRC_DFLT;
+
+       pi->power_gating = false;
+
+       if ((rdev->family == CHIP_CYPRESS) ||
+           (rdev->family == CHIP_HEMLOCK))
+               pi->gfx_clock_gating = false;
+       else
+               pi->gfx_clock_gating = true;
+
+       pi->mg_clock_gating = true;
+       pi->mgcgtssm = true;
+       eg_pi->ls_clock_gating = false;
+       eg_pi->sclk_deep_sleep = false;
+
+       pi->dynamic_pcie_gen2 = true;
+
+       if (pi->gfx_clock_gating &&
+           (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE))
+               pi->thermal_protection = true;
+       else
+               pi->thermal_protection = false;
+
+       pi->display_gap = true;
+
+       if (rdev->flags & RADEON_IS_MOBILITY)
+               pi->dcodt = true;
+       else
+               pi->dcodt = false;
+
+       pi->ulps = true;
+
+       eg_pi->dynamic_ac_timing = true;
+       eg_pi->abm = true;
+       eg_pi->mcls = true;
+       eg_pi->light_sleep = true;
+       eg_pi->memory_transition = true;
+#if defined(CONFIG_ACPI)
+       eg_pi->pcie_performance_request =
+               radeon_acpi_is_pcie_performance_request_supported(rdev);
+#else
+       eg_pi->pcie_performance_request = false;
+#endif
+
+       if ((rdev->family == CHIP_CYPRESS) ||
+           (rdev->family == CHIP_HEMLOCK) ||
+           (rdev->family == CHIP_JUNIPER))
+               eg_pi->dll_default_on = true;
+       else
+               eg_pi->dll_default_on = false;
+
+       eg_pi->sclk_deep_sleep = false;
+       pi->mclk_stutter_mode_threshold = 0;
+
+       pi->sram_end = SMC_RAM_END;
+
+       return 0;
+}
+
+void cypress_dpm_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               kfree(rdev->pm.dpm.ps[i].ps_priv);
+       }
+       kfree(rdev->pm.dpm.ps);
+       kfree(rdev->pm.dpm.priv);
+}
+
+bool cypress_dpm_vblank_too_short(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 vblank_time = r600_dpm_get_vblank_time(rdev);
+       u32 switch_limit = pi->mem_gddr5 ? 450 : 300;
+
+       if (vblank_time < switch_limit)
+               return true;
+       else
+               return false;
+
+}
diff --git a/drivers/gpu/drm/radeon/cypress_dpm.h b/drivers/gpu/drm/radeon/cypress_dpm.h
new file mode 100644 (file)
index 0000000..4c3f18c
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __CYPRESS_DPM_H__
+#define __CYPRESS_DPM_H__
+
+#include "rv770_dpm.h"
+#include "evergreen_smc.h"
+
+struct evergreen_mc_reg_entry {
+       u32 mclk_max;
+       u32 mc_data[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct evergreen_mc_reg_table {
+       u8 last;
+       u8 num_entries;
+       u16 valid_flag;
+       struct evergreen_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+       SMC_Evergreen_MCRegisterAddress mc_reg_address[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct evergreen_ulv_param {
+       bool supported;
+       struct rv7xx_pl *pl;
+};
+
+struct evergreen_arb_registers {
+       u32 mc_arb_dram_timing;
+       u32 mc_arb_dram_timing2;
+       u32 mc_arb_rfsh_rate;
+       u32 mc_arb_burst_time;
+};
+
+struct at {
+       u32 rlp;
+       u32 rmp;
+       u32 lhp;
+       u32 lmp;
+};
+
+struct evergreen_power_info {
+       /* must be first! */
+       struct rv7xx_power_info rv7xx;
+       /* flags */
+       bool vddci_control;
+       bool dynamic_ac_timing;
+       bool abm;
+       bool mcls;
+       bool light_sleep;
+       bool memory_transition;
+       bool pcie_performance_request;
+       bool pcie_performance_request_registered;
+       bool sclk_deep_sleep;
+       bool dll_default_on;
+       bool ls_clock_gating;
+       bool smu_uvd_hs;
+       bool uvd_enabled;
+       /* stored values */
+       u16 acpi_vddci;
+       u8 mvdd_high_index;
+       u8 mvdd_low_index;
+       u32 mclk_edc_wr_enable_threshold;
+       struct evergreen_mc_reg_table mc_reg_table;
+       struct atom_voltage_table vddc_voltage_table;
+       struct atom_voltage_table vddci_voltage_table;
+       struct evergreen_arb_registers bootup_arb_registers;
+       struct evergreen_ulv_param ulv;
+       struct at ats[2];
+       /* smc offsets */
+       u16 mc_reg_table_start;
+       struct radeon_ps current_rps;
+       struct rv7xx_ps current_ps;
+       struct radeon_ps requested_rps;
+       struct rv7xx_ps requested_ps;
+};
+
+#define CYPRESS_HASI_DFLT                               400000
+#define CYPRESS_MGCGTTLOCAL0_DFLT                       0x00000000
+#define CYPRESS_MGCGTTLOCAL1_DFLT                       0x00000000
+#define CYPRESS_MGCGTTLOCAL2_DFLT                       0x00000000
+#define CYPRESS_MGCGTTLOCAL3_DFLT                       0x00000000
+#define CYPRESS_MGCGCGTSSMCTRL_DFLT                     0x81944bc0
+#define REDWOOD_MGCGCGTSSMCTRL_DFLT                     0x6e944040
+#define CEDAR_MGCGCGTSSMCTRL_DFLT                       0x46944040
+#define CYPRESS_VRC_DFLT                                0xC00033
+
+#define PCIE_PERF_REQ_REMOVE_REGISTRY   0
+#define PCIE_PERF_REQ_FORCE_LOWPOWER    1
+#define PCIE_PERF_REQ_PECI_GEN1         2
+#define PCIE_PERF_REQ_PECI_GEN2         3
+#define PCIE_PERF_REQ_PECI_GEN3         4
+
+int cypress_convert_power_level_to_smc(struct radeon_device *rdev,
+                                      struct rv7xx_pl *pl,
+                                      RV770_SMC_HW_PERFORMANCE_LEVEL *level,
+                                      u8 watermark_level);
+int cypress_populate_smc_acpi_state(struct radeon_device *rdev,
+                                   RV770_SMC_STATETABLE *table);
+int cypress_populate_smc_voltage_tables(struct radeon_device *rdev,
+                                       RV770_SMC_STATETABLE *table);
+int cypress_populate_smc_initial_state(struct radeon_device *rdev,
+                                      struct radeon_ps *radeon_initial_state,
+                                      RV770_SMC_STATETABLE *table);
+u32 cypress_calculate_burst_time(struct radeon_device *rdev,
+                                u32 engine_clock, u32 memory_clock);
+void cypress_notify_link_speed_change_before_state_change(struct radeon_device *rdev,
+                                                         struct radeon_ps *radeon_new_state,
+                                                         struct radeon_ps *radeon_current_state);
+int cypress_upload_sw_state(struct radeon_device *rdev,
+                           struct radeon_ps *radeon_new_state);
+int cypress_upload_mc_reg_table(struct radeon_device *rdev,
+                               struct radeon_ps *radeon_new_state);
+void cypress_program_memory_timing_parameters(struct radeon_device *rdev,
+                                             struct radeon_ps *radeon_new_state);
+void cypress_notify_link_speed_change_after_state_change(struct radeon_device *rdev,
+                                                        struct radeon_ps *radeon_new_state,
+                                                        struct radeon_ps *radeon_current_state);
+int cypress_construct_voltage_tables(struct radeon_device *rdev);
+int cypress_get_mvdd_configuration(struct radeon_device *rdev);
+void cypress_enable_spread_spectrum(struct radeon_device *rdev,
+                                   bool enable);
+void cypress_enable_display_gap(struct radeon_device *rdev);
+int cypress_get_table_locations(struct radeon_device *rdev);
+int cypress_populate_mc_reg_table(struct radeon_device *rdev,
+                                 struct radeon_ps *radeon_boot_state);
+void cypress_program_response_times(struct radeon_device *rdev);
+int cypress_notify_smc_display_change(struct radeon_device *rdev,
+                                     bool has_display);
+void cypress_enable_sclk_control(struct radeon_device *rdev,
+                                bool enable);
+void cypress_enable_mclk_control(struct radeon_device *rdev,
+                                bool enable);
+void cypress_start_dpm(struct radeon_device *rdev);
+void cypress_advertise_gen2_capability(struct radeon_device *rdev);
+u32 cypress_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf);
+u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev,
+                                   u32 memory_clock, bool strobe_mode);
+u8 cypress_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk);
+
+#endif
index 0f89ce3..e49059d 100644 (file)
@@ -33,9 +33,7 @@
 #include "avivod.h"
 #include "evergreen_reg.h"
 #include "evergreen_blit_shaders.h"
-
-#define EVERGREEN_PFP_UCODE_SIZE 1120
-#define EVERGREEN_PM4_UCODE_SIZE 1376
+#include "radeon_ucode.h"
 
 static const u32 crtc_offsets[6] =
 {
@@ -47,9 +45,98 @@ static const u32 crtc_offsets[6] =
        EVERGREEN_CRTC5_REGISTER_OFFSET
 };
 
+#include "clearstate_evergreen.h"
+
+static u32 sumo_rlc_save_restore_register_list[] =
+{
+       0x98fc,
+       0x9830,
+       0x9834,
+       0x9838,
+       0x9870,
+       0x9874,
+       0x8a14,
+       0x8b24,
+       0x8bcc,
+       0x8b10,
+       0x8d00,
+       0x8d04,
+       0x8c00,
+       0x8c04,
+       0x8c08,
+       0x8c0c,
+       0x8d8c,
+       0x8c20,
+       0x8c24,
+       0x8c28,
+       0x8c18,
+       0x8c1c,
+       0x8cf0,
+       0x8e2c,
+       0x8e38,
+       0x8c30,
+       0x9508,
+       0x9688,
+       0x9608,
+       0x960c,
+       0x9610,
+       0x9614,
+       0x88c4,
+       0x88d4,
+       0xa008,
+       0x900c,
+       0x9100,
+       0x913c,
+       0x98f8,
+       0x98f4,
+       0x9b7c,
+       0x3f8c,
+       0x8950,
+       0x8954,
+       0x8a18,
+       0x8b28,
+       0x9144,
+       0x9148,
+       0x914c,
+       0x3f90,
+       0x3f94,
+       0x915c,
+       0x9160,
+       0x9178,
+       0x917c,
+       0x9180,
+       0x918c,
+       0x9190,
+       0x9194,
+       0x9198,
+       0x919c,
+       0x91a8,
+       0x91ac,
+       0x91b0,
+       0x91b4,
+       0x91b8,
+       0x91c4,
+       0x91c8,
+       0x91cc,
+       0x91d0,
+       0x91d4,
+       0x91e0,
+       0x91e4,
+       0x91ec,
+       0x91f0,
+       0x91f4,
+       0x9200,
+       0x9204,
+       0x929c,
+       0x9150,
+       0x802c,
+};
+static u32 sumo_rlc_save_restore_register_list_size = ARRAY_SIZE(sumo_rlc_save_restore_register_list);
+
 static void evergreen_gpu_init(struct radeon_device *rdev);
 void evergreen_fini(struct radeon_device *rdev);
 void evergreen_pcie_gen2_enable(struct radeon_device *rdev);
+void evergreen_program_aspm(struct radeon_device *rdev);
 extern void cayman_cp_int_cntl_setup(struct radeon_device *rdev,
                                     int ring, u32 cp_int_cntl);
 
@@ -1417,8 +1504,8 @@ void evergreen_pm_misc(struct radeon_device *rdev)
        struct radeon_voltage *voltage = &ps->clock_info[req_cm_idx].voltage;
 
        if (voltage->type == VOLTAGE_SW) {
-               /* 0xff01 is a flag rather then an actual voltage */
-               if (voltage->voltage == 0xff01)
+               /* 0xff0x are flags rather then an actual voltage */
+               if ((voltage->voltage & 0xff00) == 0xff00)
                        return;
                if (voltage->voltage && (voltage->voltage != rdev->pm.current_vddc)) {
                        radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC);
@@ -1438,8 +1525,8 @@ void evergreen_pm_misc(struct radeon_device *rdev)
                        voltage = &rdev->pm.power_state[req_ps_idx].
                                clock_info[rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx].voltage;
 
-               /* 0xff01 is a flag rather then an actual voltage */
-               if (voltage->vddci == 0xff01)
+               /* 0xff0x are flags rather then an actual voltage */
+               if ((voltage->vddci & 0xff00) == 0xff00)
                        return;
                if (voltage->vddci && (voltage->vddci != rdev->pm.current_vddci)) {
                        radeon_atom_set_voltage(rdev, voltage->vddci, SET_VOLTAGE_TYPE_ASIC_VDDCI);
@@ -2036,7 +2123,8 @@ static void evergreen_program_watermarks(struct radeon_device *rdev,
                                         u32 lb_size, u32 num_heads)
 {
        struct drm_display_mode *mode = &radeon_crtc->base.mode;
-       struct evergreen_wm_params wm;
+       struct evergreen_wm_params wm_low, wm_high;
+       u32 dram_channels;
        u32 pixel_period;
        u32 line_time = 0;
        u32 latency_watermark_a = 0, latency_watermark_b = 0;
@@ -2052,39 +2140,81 @@ static void evergreen_program_watermarks(struct radeon_device *rdev,
                line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535);
                priority_a_cnt = 0;
                priority_b_cnt = 0;
+               dram_channels = evergreen_get_number_of_dram_channels(rdev);
+
+               /* watermark for high clocks */
+               if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
+                       wm_high.yclk =
+                               radeon_dpm_get_mclk(rdev, false) * 10;
+                       wm_high.sclk =
+                               radeon_dpm_get_sclk(rdev, false) * 10;
+               } else {
+                       wm_high.yclk = rdev->pm.current_mclk * 10;
+                       wm_high.sclk = rdev->pm.current_sclk * 10;
+               }
 
-               wm.yclk = rdev->pm.current_mclk * 10;
-               wm.sclk = rdev->pm.current_sclk * 10;
-               wm.disp_clk = mode->clock;
-               wm.src_width = mode->crtc_hdisplay;
-               wm.active_time = mode->crtc_hdisplay * pixel_period;
-               wm.blank_time = line_time - wm.active_time;
-               wm.interlaced = false;
+               wm_high.disp_clk = mode->clock;
+               wm_high.src_width = mode->crtc_hdisplay;
+               wm_high.active_time = mode->crtc_hdisplay * pixel_period;
+               wm_high.blank_time = line_time - wm_high.active_time;
+               wm_high.interlaced = false;
                if (mode->flags & DRM_MODE_FLAG_INTERLACE)
-                       wm.interlaced = true;
-               wm.vsc = radeon_crtc->vsc;
-               wm.vtaps = 1;
+                       wm_high.interlaced = true;
+               wm_high.vsc = radeon_crtc->vsc;
+               wm_high.vtaps = 1;
                if (radeon_crtc->rmx_type != RMX_OFF)
-                       wm.vtaps = 2;
-               wm.bytes_per_pixel = 4; /* XXX: get this from fb config */
-               wm.lb_size = lb_size;
-               wm.dram_channels = evergreen_get_number_of_dram_channels(rdev);
-               wm.num_heads = num_heads;
+                       wm_high.vtaps = 2;
+               wm_high.bytes_per_pixel = 4; /* XXX: get this from fb config */
+               wm_high.lb_size = lb_size;
+               wm_high.dram_channels = dram_channels;
+               wm_high.num_heads = num_heads;
+
+               /* watermark for low clocks */
+               if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
+                       wm_low.yclk =
+                               radeon_dpm_get_mclk(rdev, true) * 10;
+                       wm_low.sclk =
+                               radeon_dpm_get_sclk(rdev, true) * 10;
+               } else {
+                       wm_low.yclk = rdev->pm.current_mclk * 10;
+                       wm_low.sclk = rdev->pm.current_sclk * 10;
+               }
+
+               wm_low.disp_clk = mode->clock;
+               wm_low.src_width = mode->crtc_hdisplay;
+               wm_low.active_time = mode->crtc_hdisplay * pixel_period;
+               wm_low.blank_time = line_time - wm_low.active_time;
+               wm_low.interlaced = false;
+               if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+                       wm_low.interlaced = true;
+               wm_low.vsc = radeon_crtc->vsc;
+               wm_low.vtaps = 1;
+               if (radeon_crtc->rmx_type != RMX_OFF)
+                       wm_low.vtaps = 2;
+               wm_low.bytes_per_pixel = 4; /* XXX: get this from fb config */
+               wm_low.lb_size = lb_size;
+               wm_low.dram_channels = dram_channels;
+               wm_low.num_heads = num_heads;
 
                /* set for high clocks */
-               latency_watermark_a = min(evergreen_latency_watermark(&wm), (u32)65535);
+               latency_watermark_a = min(evergreen_latency_watermark(&wm_high), (u32)65535);
                /* set for low clocks */
-               /* wm.yclk = low clk; wm.sclk = low clk */
-               latency_watermark_b = min(evergreen_latency_watermark(&wm), (u32)65535);
+               latency_watermark_b = min(evergreen_latency_watermark(&wm_low), (u32)65535);
 
                /* possibly force display priority to high */
                /* should really do this at mode validation time... */
-               if (!evergreen_average_bandwidth_vs_dram_bandwidth_for_display(&wm) ||
-                   !evergreen_average_bandwidth_vs_available_bandwidth(&wm) ||
-                   !evergreen_check_latency_hiding(&wm) ||
+               if (!evergreen_average_bandwidth_vs_dram_bandwidth_for_display(&wm_high) ||
+                   !evergreen_average_bandwidth_vs_available_bandwidth(&wm_high) ||
+                   !evergreen_check_latency_hiding(&wm_high) ||
                    (rdev->disp_priority == 2)) {
-                       DRM_DEBUG_KMS("force priority to high\n");
+                       DRM_DEBUG_KMS("force priority to high\n");
                        priority_a_cnt |= PRIORITY_ALWAYS_ON;
+               }
+               if (!evergreen_average_bandwidth_vs_dram_bandwidth_for_display(&wm_low) ||
+                   !evergreen_average_bandwidth_vs_available_bandwidth(&wm_low) ||
+                   !evergreen_check_latency_hiding(&wm_low) ||
+                   (rdev->disp_priority == 2)) {
+                       DRM_DEBUG_KMS("force priority b to high\n");
                        priority_b_cnt |= PRIORITY_ALWAYS_ON;
                }
 
@@ -2137,6 +2267,10 @@ static void evergreen_program_watermarks(struct radeon_device *rdev,
        WREG32(PRIORITY_A_CNT + radeon_crtc->crtc_offset, priority_a_cnt);
        WREG32(PRIORITY_B_CNT + radeon_crtc->crtc_offset, priority_b_cnt);
 
+       /* save values for DPM */
+       radeon_crtc->line_time = line_time;
+       radeon_crtc->wm_high = latency_watermark_a;
+       radeon_crtc->wm_low = latency_watermark_b;
 }
 
 /**
@@ -3120,10 +3254,8 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
                u32 efuse_straps_4;
                u32 efuse_straps_3;
 
-               WREG32(RCU_IND_INDEX, 0x204);
-               efuse_straps_4 = RREG32(RCU_IND_DATA);
-               WREG32(RCU_IND_INDEX, 0x203);
-               efuse_straps_3 = RREG32(RCU_IND_DATA);
+               efuse_straps_4 = RREG32_RCU(0x204);
+               efuse_straps_3 = RREG32_RCU(0x203);
                tmp = (((efuse_straps_4 & 0xf) << 4) |
                      ((efuse_straps_3 & 0xf0000000) >> 28));
        } else {
@@ -3727,6 +3859,262 @@ bool evergreen_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *rin
        return radeon_ring_test_lockup(rdev, ring);
 }
 
+/*
+ * RLC
+ */
+#define RLC_SAVE_RESTORE_LIST_END_MARKER    0x00000000
+#define RLC_CLEAR_STATE_END_MARKER          0x00000001
+
+void sumo_rlc_fini(struct radeon_device *rdev)
+{
+       int r;
+
+       /* save restore block */
+       if (rdev->rlc.save_restore_obj) {
+               r = radeon_bo_reserve(rdev->rlc.save_restore_obj, false);
+               if (unlikely(r != 0))
+                       dev_warn(rdev->dev, "(%d) reserve RLC sr bo failed\n", r);
+               radeon_bo_unpin(rdev->rlc.save_restore_obj);
+               radeon_bo_unreserve(rdev->rlc.save_restore_obj);
+
+               radeon_bo_unref(&rdev->rlc.save_restore_obj);
+               rdev->rlc.save_restore_obj = NULL;
+       }
+
+       /* clear state block */
+       if (rdev->rlc.clear_state_obj) {
+               r = radeon_bo_reserve(rdev->rlc.clear_state_obj, false);
+               if (unlikely(r != 0))
+                       dev_warn(rdev->dev, "(%d) reserve RLC c bo failed\n", r);
+               radeon_bo_unpin(rdev->rlc.clear_state_obj);
+               radeon_bo_unreserve(rdev->rlc.clear_state_obj);
+
+               radeon_bo_unref(&rdev->rlc.clear_state_obj);
+               rdev->rlc.clear_state_obj = NULL;
+       }
+}
+
+int sumo_rlc_init(struct radeon_device *rdev)
+{
+       u32 *src_ptr;
+       volatile u32 *dst_ptr;
+       u32 dws, data, i, j, k, reg_num;
+       u32 reg_list_num, reg_list_hdr_blk_index, reg_list_blk_index;
+       u64 reg_list_mc_addr;
+       struct cs_section_def *cs_data;
+       int r;
+
+       src_ptr = rdev->rlc.reg_list;
+       dws = rdev->rlc.reg_list_size;
+       cs_data = rdev->rlc.cs_data;
+
+       /* save restore block */
+       if (rdev->rlc.save_restore_obj == NULL) {
+               r = radeon_bo_create(rdev, dws * 4, PAGE_SIZE, true,
+                                    RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->rlc.save_restore_obj);
+               if (r) {
+                       dev_warn(rdev->dev, "(%d) create RLC sr bo failed\n", r);
+                       return r;
+               }
+       }
+
+       r = radeon_bo_reserve(rdev->rlc.save_restore_obj, false);
+       if (unlikely(r != 0)) {
+               sumo_rlc_fini(rdev);
+               return r;
+       }
+       r = radeon_bo_pin(rdev->rlc.save_restore_obj, RADEON_GEM_DOMAIN_VRAM,
+                         &rdev->rlc.save_restore_gpu_addr);
+       if (r) {
+               radeon_bo_unreserve(rdev->rlc.save_restore_obj);
+               dev_warn(rdev->dev, "(%d) pin RLC sr bo failed\n", r);
+               sumo_rlc_fini(rdev);
+               return r;
+       }
+       r = radeon_bo_kmap(rdev->rlc.save_restore_obj, (void **)&rdev->rlc.sr_ptr);
+       if (r) {
+               dev_warn(rdev->dev, "(%d) map RLC sr bo failed\n", r);
+               sumo_rlc_fini(rdev);
+               return r;
+       }
+       /* write the sr buffer */
+       dst_ptr = rdev->rlc.sr_ptr;
+       /* format:
+        * dw0: (reg2 << 16) | reg1
+        * dw1: reg1 save space
+        * dw2: reg2 save space
+        */
+       for (i = 0; i < dws; i++) {
+               data = src_ptr[i] >> 2;
+               i++;
+               if (i < dws)
+                       data |= (src_ptr[i] >> 2) << 16;
+               j = (((i - 1) * 3) / 2);
+               dst_ptr[j] = data;
+       }
+       j = ((i * 3) / 2);
+       dst_ptr[j] = RLC_SAVE_RESTORE_LIST_END_MARKER;
+
+       radeon_bo_kunmap(rdev->rlc.save_restore_obj);
+       radeon_bo_unreserve(rdev->rlc.save_restore_obj);
+
+       /* clear state block */
+       reg_list_num = 0;
+       dws = 0;
+       for (i = 0; cs_data[i].section != NULL; i++) {
+               for (j = 0; cs_data[i].section[j].extent != NULL; j++) {
+                       reg_list_num++;
+                       dws += cs_data[i].section[j].reg_count;
+               }
+       }
+       reg_list_blk_index = (3 * reg_list_num + 2);
+       dws += reg_list_blk_index;
+
+       if (rdev->rlc.clear_state_obj == NULL) {
+               r = radeon_bo_create(rdev, dws * 4, PAGE_SIZE, true,
+                                    RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->rlc.clear_state_obj);
+               if (r) {
+                       dev_warn(rdev->dev, "(%d) create RLC c bo failed\n", r);
+                       sumo_rlc_fini(rdev);
+                       return r;
+               }
+       }
+       r = radeon_bo_reserve(rdev->rlc.clear_state_obj, false);
+       if (unlikely(r != 0)) {
+               sumo_rlc_fini(rdev);
+               return r;
+       }
+       r = radeon_bo_pin(rdev->rlc.clear_state_obj, RADEON_GEM_DOMAIN_VRAM,
+                         &rdev->rlc.clear_state_gpu_addr);
+       if (r) {
+
+               radeon_bo_unreserve(rdev->rlc.clear_state_obj);
+               dev_warn(rdev->dev, "(%d) pin RLC c bo failed\n", r);
+               sumo_rlc_fini(rdev);
+               return r;
+       }
+       r = radeon_bo_kmap(rdev->rlc.clear_state_obj, (void **)&rdev->rlc.cs_ptr);
+       if (r) {
+               dev_warn(rdev->dev, "(%d) map RLC c bo failed\n", r);
+               sumo_rlc_fini(rdev);
+               return r;
+       }
+       /* set up the cs buffer */
+       dst_ptr = rdev->rlc.cs_ptr;
+       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;
+       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;
+                       reg_list_hdr_blk_index++;
+
+                       data = (cs_data[i].section[j].reg_index * 4) & 0xffffffff;
+                       dst_ptr[reg_list_hdr_blk_index] = data;
+                       reg_list_hdr_blk_index++;
+
+                       data = 0x08000000 | (reg_num * 4);
+                       dst_ptr[reg_list_hdr_blk_index] = 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;
+                       }
+                       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;
+
+       radeon_bo_kunmap(rdev->rlc.clear_state_obj);
+       radeon_bo_unreserve(rdev->rlc.clear_state_obj);
+
+       return 0;
+}
+
+static void evergreen_rlc_start(struct radeon_device *rdev)
+{
+       u32 mask = RLC_ENABLE;
+
+       if (rdev->flags & RADEON_IS_IGP) {
+               mask |= GFX_POWER_GATING_ENABLE | GFX_POWER_GATING_SRC;
+       }
+
+       WREG32(RLC_CNTL, mask);
+}
+
+int evergreen_rlc_resume(struct radeon_device *rdev)
+{
+       u32 i;
+       const __be32 *fw_data;
+
+       if (!rdev->rlc_fw)
+               return -EINVAL;
+
+       r600_rlc_stop(rdev);
+
+       WREG32(RLC_HB_CNTL, 0);
+
+       if (rdev->flags & RADEON_IS_IGP) {
+               if (rdev->family == CHIP_ARUBA) {
+                       u32 always_on_bitmap =
+                               3 | (3 << (16 * rdev->config.cayman.max_shader_engines));
+                       /* find out the number of active simds */
+                       u32 tmp = (RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffff0000) >> 16;
+                       tmp |= 0xffffffff << rdev->config.cayman.max_simds_per_se;
+                       tmp = hweight32(~tmp);
+                       if (tmp == rdev->config.cayman.max_simds_per_se) {
+                               WREG32(TN_RLC_LB_ALWAYS_ACTIVE_SIMD_MASK, always_on_bitmap);
+                               WREG32(TN_RLC_LB_PARAMS, 0x00601004);
+                               WREG32(TN_RLC_LB_INIT_SIMD_MASK, 0xffffffff);
+                               WREG32(TN_RLC_LB_CNTR_INIT, 0x00000000);
+                               WREG32(TN_RLC_LB_CNTR_MAX, 0x00002000);
+                       }
+               } else {
+                       WREG32(RLC_HB_WPTR_LSB_ADDR, 0);
+                       WREG32(RLC_HB_WPTR_MSB_ADDR, 0);
+               }
+               WREG32(TN_RLC_SAVE_AND_RESTORE_BASE, rdev->rlc.save_restore_gpu_addr >> 8);
+               WREG32(TN_RLC_CLEAR_STATE_RESTORE_BASE, rdev->rlc.clear_state_gpu_addr >> 8);
+       } else {
+               WREG32(RLC_HB_BASE, 0);
+               WREG32(RLC_HB_RPTR, 0);
+               WREG32(RLC_HB_WPTR, 0);
+               WREG32(RLC_HB_WPTR_LSB_ADDR, 0);
+               WREG32(RLC_HB_WPTR_MSB_ADDR, 0);
+       }
+       WREG32(RLC_MC_CNTL, 0);
+       WREG32(RLC_UCODE_CNTL, 0);
+
+       fw_data = (const __be32 *)rdev->rlc_fw->data;
+       if (rdev->family >= CHIP_ARUBA) {
+               for (i = 0; i < ARUBA_RLC_UCODE_SIZE; i++) {
+                       WREG32(RLC_UCODE_ADDR, i);
+                       WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
+               }
+       } else if (rdev->family >= CHIP_CAYMAN) {
+               for (i = 0; i < CAYMAN_RLC_UCODE_SIZE; i++) {
+                       WREG32(RLC_UCODE_ADDR, i);
+                       WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
+               }
+       } else {
+               for (i = 0; i < EVERGREEN_RLC_UCODE_SIZE; i++) {
+                       WREG32(RLC_UCODE_ADDR, i);
+                       WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
+               }
+       }
+       WREG32(RLC_UCODE_ADDR, 0);
+
+       evergreen_rlc_start(rdev);
+
+       return 0;
+}
+
 /* Interrupts */
 
 u32 evergreen_get_vblank_counter(struct radeon_device *rdev, int crtc)
@@ -3805,6 +4193,7 @@ int evergreen_irq_set(struct radeon_device *rdev)
        u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0;
        u32 afmt1 = 0, afmt2 = 0, afmt3 = 0, afmt4 = 0, afmt5 = 0, afmt6 = 0;
        u32 dma_cntl, dma_cntl1 = 0;
+       u32 thermal_int = 0;
 
        if (!rdev->irq.installed) {
                WARN(1, "Can't enable IRQ/MSI because no handler is installed\n");
@@ -3824,6 +4213,12 @@ int evergreen_irq_set(struct radeon_device *rdev)
        hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN;
        hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN;
        hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN;
+       if (rdev->family == CHIP_ARUBA)
+               thermal_int = RREG32(TN_CG_THERMAL_INT_CTRL) &
+                       ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
+       else
+               thermal_int = RREG32(CG_THERMAL_INT) &
+                       ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
 
        afmt1 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK;
        afmt2 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK;
@@ -3869,6 +4264,11 @@ int evergreen_irq_set(struct radeon_device *rdev)
                }
        }
 
+       if (rdev->irq.dpm_thermal) {
+               DRM_DEBUG("dpm thermal\n");
+               thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW;
+       }
+
        if (rdev->irq.crtc_vblank_int[0] ||
            atomic_read(&rdev->irq.pflip[0])) {
                DRM_DEBUG("evergreen_irq_set: vblank 0\n");
@@ -3990,6 +4390,10 @@ int evergreen_irq_set(struct radeon_device *rdev)
        WREG32(DC_HPD4_INT_CONTROL, hpd4);
        WREG32(DC_HPD5_INT_CONTROL, hpd5);
        WREG32(DC_HPD6_INT_CONTROL, hpd6);
+       if (rdev->family == CHIP_ARUBA)
+               WREG32(TN_CG_THERMAL_INT_CTRL, thermal_int);
+       else
+               WREG32(CG_THERMAL_INT, thermal_int);
 
        WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, afmt1);
        WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, afmt2);
@@ -4181,6 +4585,7 @@ int evergreen_irq_process(struct radeon_device *rdev)
        u32 ring_index;
        bool queue_hotplug = false;
        bool queue_hdmi = false;
+       bool queue_thermal = false;
 
        if (!rdev->ih.enabled || rdev->shutdown)
                return IRQ_NONE;
@@ -4502,6 +4907,16 @@ restart_ih:
                        DRM_DEBUG("IH: DMA trap\n");
                        radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX);
                        break;
+               case 230: /* thermal low to high */
+                       DRM_DEBUG("IH: thermal low to high\n");
+                       rdev->pm.dpm.thermal.high_to_low = false;
+                       queue_thermal = true;
+                       break;
+               case 231: /* thermal high to low */
+                       DRM_DEBUG("IH: thermal high to low\n");
+                       rdev->pm.dpm.thermal.high_to_low = true;
+                       queue_thermal = true;
+                       break;
                case 233: /* GUI IDLE */
                        DRM_DEBUG("IH: GUI idle\n");
                        break;
@@ -4524,6 +4939,8 @@ restart_ih:
                schedule_work(&rdev->hotplug_work);
        if (queue_hdmi)
                schedule_work(&rdev->audio_work);
+       if (queue_thermal && rdev->pm.dpm_enabled)
+               schedule_work(&rdev->pm.dpm.thermal.work);
        rdev->ih.rptr = rptr;
        WREG32(IH_RB_RPTR, rdev->ih.rptr);
        atomic_set(&rdev->ih.lock, 0);
@@ -4680,6 +5097,8 @@ static int evergreen_startup(struct radeon_device *rdev)
 
        /* enable pcie gen2 link */
        evergreen_pcie_gen2_enable(rdev);
+       /* enable aspm */
+       evergreen_program_aspm(rdev);
 
        if (ASIC_IS_DCE5(rdev)) {
                if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw || !rdev->mc_fw) {
@@ -4725,6 +5144,18 @@ static int evergreen_startup(struct radeon_device *rdev)
                dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
        }
 
+       /* allocate rlc buffers */
+       if (rdev->flags & RADEON_IS_IGP) {
+               rdev->rlc.reg_list = sumo_rlc_save_restore_register_list;
+               rdev->rlc.reg_list_size = sumo_rlc_save_restore_register_list_size;
+               rdev->rlc.cs_data = evergreen_cs_data;
+               r = sumo_rlc_init(rdev);
+               if (r) {
+                       DRM_ERROR("Failed to init rlc BOs!\n");
+                       return r;
+               }
+       }
+
        /* allocate wb buffer */
        r = radeon_wb_init(rdev);
        if (r)
@@ -4956,6 +5387,8 @@ int evergreen_init(struct radeon_device *rdev)
                r700_cp_fini(rdev);
                r600_dma_fini(rdev);
                r600_irq_fini(rdev);
+               if (rdev->flags & RADEON_IS_IGP)
+                       sumo_rlc_fini(rdev);
                radeon_wb_fini(rdev);
                radeon_ib_pool_fini(rdev);
                radeon_irq_kms_fini(rdev);
@@ -4984,6 +5417,8 @@ void evergreen_fini(struct radeon_device *rdev)
        r700_cp_fini(rdev);
        r600_dma_fini(rdev);
        r600_irq_fini(rdev);
+       if (rdev->flags & RADEON_IS_IGP)
+               sumo_rlc_fini(rdev);
        radeon_wb_fini(rdev);
        radeon_ib_pool_fini(rdev);
        radeon_irq_kms_fini(rdev);
@@ -5061,3 +5496,150 @@ void evergreen_pcie_gen2_enable(struct radeon_device *rdev)
                WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl);
        }
 }
+
+void evergreen_program_aspm(struct radeon_device *rdev)
+{
+       u32 data, orig;
+       u32 pcie_lc_cntl, pcie_lc_cntl_old;
+       bool disable_l0s, disable_l1 = false, disable_plloff_in_l1 = false;
+       /* fusion_platform = true
+        * if the system is a fusion system
+        * (APU or DGPU in a fusion system).
+        * todo: check if the system is a fusion platform.
+        */
+       bool fusion_platform = false;
+
+       if (!(rdev->flags & RADEON_IS_PCIE))
+               return;
+
+       switch (rdev->family) {
+       case CHIP_CYPRESS:
+       case CHIP_HEMLOCK:
+       case CHIP_JUNIPER:
+       case CHIP_REDWOOD:
+       case CHIP_CEDAR:
+       case CHIP_SUMO:
+       case CHIP_SUMO2:
+       case CHIP_PALM:
+       case CHIP_ARUBA:
+               disable_l0s = true;
+               break;
+       default:
+               disable_l0s = false;
+               break;
+       }
+
+       if (rdev->flags & RADEON_IS_IGP)
+               fusion_platform = true; /* XXX also dGPUs in a fusion system */
+
+       data = orig = RREG32_PIF_PHY0(PB0_PIF_PAIRING);
+       if (fusion_platform)
+               data &= ~MULTI_PIF;
+       else
+               data |= MULTI_PIF;
+       if (data != orig)
+               WREG32_PIF_PHY0(PB0_PIF_PAIRING, data);
+
+       data = orig = RREG32_PIF_PHY1(PB1_PIF_PAIRING);
+       if (fusion_platform)
+               data &= ~MULTI_PIF;
+       else
+               data |= MULTI_PIF;
+       if (data != orig)
+               WREG32_PIF_PHY1(PB1_PIF_PAIRING, data);
+
+       pcie_lc_cntl = pcie_lc_cntl_old = RREG32_PCIE_PORT(PCIE_LC_CNTL);
+       pcie_lc_cntl &= ~(LC_L0S_INACTIVITY_MASK | LC_L1_INACTIVITY_MASK);
+       if (!disable_l0s) {
+               if (rdev->family >= CHIP_BARTS)
+                       pcie_lc_cntl |= LC_L0S_INACTIVITY(7);
+               else
+                       pcie_lc_cntl |= LC_L0S_INACTIVITY(3);
+       }
+
+       if (!disable_l1) {
+               if (rdev->family >= CHIP_BARTS)
+                       pcie_lc_cntl |= LC_L1_INACTIVITY(7);
+               else
+                       pcie_lc_cntl |= LC_L1_INACTIVITY(8);
+
+               if (!disable_plloff_in_l1) {
+                       data = orig = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_0_MASK | PLL_POWER_STATE_IN_TXS2_0_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_0(7) | PLL_POWER_STATE_IN_TXS2_0(7);
+                       if (data != orig)
+                               WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0, data);
+
+                       data = orig = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_1_MASK | PLL_POWER_STATE_IN_TXS2_1_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_1(7) | PLL_POWER_STATE_IN_TXS2_1(7);
+                       if (data != orig)
+                               WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1, data);
+
+                       data = orig = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_0_MASK | PLL_POWER_STATE_IN_TXS2_0_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_0(7) | PLL_POWER_STATE_IN_TXS2_0(7);
+                       if (data != orig)
+                               WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0, data);
+
+                       data = orig = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_1_MASK | PLL_POWER_STATE_IN_TXS2_1_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_1(7) | PLL_POWER_STATE_IN_TXS2_1(7);
+                       if (data != orig)
+                               WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1, data);
+
+                       if (rdev->family >= CHIP_BARTS) {
+                               data = orig = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0);
+                               data &= ~PLL_RAMP_UP_TIME_0_MASK;
+                               data |= PLL_RAMP_UP_TIME_0(4);
+                               if (data != orig)
+                                       WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0, data);
+
+                               data = orig = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1);
+                               data &= ~PLL_RAMP_UP_TIME_1_MASK;
+                               data |= PLL_RAMP_UP_TIME_1(4);
+                               if (data != orig)
+                                       WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1, data);
+
+                               data = orig = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0);
+                               data &= ~PLL_RAMP_UP_TIME_0_MASK;
+                               data |= PLL_RAMP_UP_TIME_0(4);
+                               if (data != orig)
+                                       WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0, data);
+
+                               data = orig = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1);
+                               data &= ~PLL_RAMP_UP_TIME_1_MASK;
+                               data |= PLL_RAMP_UP_TIME_1(4);
+                               if (data != orig)
+                                       WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1, data);
+                       }
+
+                       data = orig = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL);
+                       data &= ~LC_DYN_LANES_PWR_STATE_MASK;
+                       data |= LC_DYN_LANES_PWR_STATE(3);
+                       if (data != orig)
+                               WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, data);
+
+                       if (rdev->family >= CHIP_BARTS) {
+                               data = orig = RREG32_PIF_PHY0(PB0_PIF_CNTL);
+                               data &= ~LS2_EXIT_TIME_MASK;
+                               data |= LS2_EXIT_TIME(1);
+                               if (data != orig)
+                                       WREG32_PIF_PHY0(PB0_PIF_CNTL, data);
+
+                               data = orig = RREG32_PIF_PHY1(PB1_PIF_CNTL);
+                               data &= ~LS2_EXIT_TIME_MASK;
+                               data |= LS2_EXIT_TIME(1);
+                               if (data != orig)
+                                       WREG32_PIF_PHY1(PB1_PIF_CNTL, data);
+                       }
+               }
+       }
+
+       /* evergreen parts only */
+       if (rdev->family < CHIP_BARTS)
+               pcie_lc_cntl |= LC_PMI_TO_L1_DIS;
+
+       if (pcie_lc_cntl != pcie_lc_cntl_old)
+               WREG32_PCIE_PORT(PCIE_LC_CNTL, pcie_lc_cntl);
+}
index ed7c8a7..b9c6f76 100644 (file)
@@ -128,14 +128,7 @@ static void evergreen_hdmi_update_avi_infoframe(struct drm_encoder *encoder,
        struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
        uint32_t offset = dig->afmt->offset;
        uint8_t *frame = buffer + 3;
-
-       /* Our header values (type, version, length) should be alright, Intel
-        * is using the same. Checksum function also seems to be OK, it works
-        * fine for audio infoframe. However calculated value is always lower
-        * by 2 in comparison to fglrx. It breaks displaying anything in case
-        * of TVs that strictly check the checksum. Hack it manually here to
-        * workaround this issue. */
-       frame[0x0] += 2;
+       uint8_t *header = buffer;
 
        WREG32(AFMT_AVI_INFO0 + offset,
                frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
@@ -144,7 +137,7 @@ static void evergreen_hdmi_update_avi_infoframe(struct drm_encoder *encoder,
        WREG32(AFMT_AVI_INFO2 + offset,
                frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24));
        WREG32(AFMT_AVI_INFO3 + offset,
-               frame[0xC] | (frame[0xD] << 8));
+               frame[0xC] | (frame[0xD] << 8) | (header[1] << 24));
 }
 
 static void evergreen_audio_set_dto(struct drm_encoder *encoder, u32 clock)
index 881aba2..8a4e641 100644 (file)
 #ifndef __EVERGREEN_REG_H__
 #define __EVERGREEN_REG_H__
 
+/* trinity */
+#define TN_SMC_IND_INDEX_0                              0x200
+#define TN_SMC_IND_DATA_0                               0x204
+
 /* evergreen */
+#define EVERGREEN_PIF_PHY0_INDEX                        0x8
+#define EVERGREEN_PIF_PHY0_DATA                         0xc
+#define EVERGREEN_PIF_PHY1_INDEX                        0x10
+#define EVERGREEN_PIF_PHY1_DATA                         0x14
+
 #define EVERGREEN_VGA_MEMORY_BASE_ADDRESS               0x310
 #define EVERGREEN_VGA_MEMORY_BASE_ADDRESS_HIGH          0x324
 #define EVERGREEN_D3VGA_CONTROL                         0x3e0
@@ -40,6 +49,9 @@
 #define EVERGREEN_AUDIO_PLL1_DIV                       0x5b4
 #define EVERGREEN_AUDIO_PLL1_UNK                       0x5bc
 
+#define EVERGREEN_CG_IND_ADDR                           0x8f8
+#define EVERGREEN_CG_IND_DATA                           0x8fc
+
 #define EVERGREEN_AUDIO_ENABLE                         0x5e78
 #define EVERGREEN_AUDIO_VENDOR_ID                      0x5ec0
 
diff --git a/drivers/gpu/drm/radeon/evergreen_smc.h b/drivers/gpu/drm/radeon/evergreen_smc.h
new file mode 100644 (file)
index 0000000..76ada8c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __EVERGREEN_SMC_H__
+#define __EVERGREEN_SMC_H__
+
+#include "rv770_smc.h"
+
+#pragma pack(push, 1)
+
+#define SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE 16
+
+struct SMC_Evergreen_MCRegisterAddress
+{
+    uint16_t s0;
+    uint16_t s1;
+};
+
+typedef struct SMC_Evergreen_MCRegisterAddress SMC_Evergreen_MCRegisterAddress;
+
+
+struct SMC_Evergreen_MCRegisterSet
+{
+    uint32_t value[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
+};
+
+typedef struct SMC_Evergreen_MCRegisterSet SMC_Evergreen_MCRegisterSet;
+
+struct SMC_Evergreen_MCRegisters
+{
+    uint8_t                             last;
+    uint8_t                             reserved[3];
+    SMC_Evergreen_MCRegisterAddress     address[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
+    SMC_Evergreen_MCRegisterSet         data[5];
+};
+
+typedef struct SMC_Evergreen_MCRegisters SMC_Evergreen_MCRegisters;
+
+#define EVERGREEN_SMC_FIRMWARE_HEADER_LOCATION 0x100
+
+#define EVERGREEN_SMC_FIRMWARE_HEADER_softRegisters   0x0
+#define EVERGREEN_SMC_FIRMWARE_HEADER_stateTable      0xC
+#define EVERGREEN_SMC_FIRMWARE_HEADER_mcRegisterTable 0x20
+
+
+#pragma pack(pop)
+
+#endif
index 75c0563..a7baf67 100644 (file)
 #define SUMO_GB_ADDR_CONFIG_GOLDEN           0x02010002
 #define SUMO2_GB_ADDR_CONFIG_GOLDEN          0x02010002
 
+/* pm registers */
+#define        SMC_MSG                                         0x20c
+#define                HOST_SMC_MSG(x)                         ((x) << 0)
+#define                HOST_SMC_MSG_MASK                       (0xff << 0)
+#define                HOST_SMC_MSG_SHIFT                      0
+#define                HOST_SMC_RESP(x)                        ((x) << 8)
+#define                HOST_SMC_RESP_MASK                      (0xff << 8)
+#define                HOST_SMC_RESP_SHIFT                     8
+#define                SMC_HOST_MSG(x)                         ((x) << 16)
+#define                SMC_HOST_MSG_MASK                       (0xff << 16)
+#define                SMC_HOST_MSG_SHIFT                      16
+#define                SMC_HOST_RESP(x)                        ((x) << 24)
+#define                SMC_HOST_RESP_MASK                      (0xff << 24)
+#define                SMC_HOST_RESP_SHIFT                     24
+
+#define DCCG_DISP_SLOW_SELECT_REG                       0x4fc
+#define                DCCG_DISP1_SLOW_SELECT(x)               ((x) << 0)
+#define                DCCG_DISP1_SLOW_SELECT_MASK             (7 << 0)
+#define                DCCG_DISP1_SLOW_SELECT_SHIFT            0
+#define                DCCG_DISP2_SLOW_SELECT(x)               ((x) << 4)
+#define                DCCG_DISP2_SLOW_SELECT_MASK             (7 << 4)
+#define                DCCG_DISP2_SLOW_SELECT_SHIFT            4
+
+#define        CG_SPLL_FUNC_CNTL                               0x600
+#define                SPLL_RESET                              (1 << 0)
+#define                SPLL_SLEEP                              (1 << 1)
+#define                SPLL_BYPASS_EN                          (1 << 3)
+#define                SPLL_REF_DIV(x)                         ((x) << 4)
+#define                SPLL_REF_DIV_MASK                       (0x3f << 4)
+#define                SPLL_PDIV_A(x)                          ((x) << 20)
+#define                SPLL_PDIV_A_MASK                        (0x7f << 20)
+#define        CG_SPLL_FUNC_CNTL_2                             0x604
+#define                SCLK_MUX_SEL(x)                         ((x) << 0)
+#define                SCLK_MUX_SEL_MASK                       (0x1ff << 0)
+#define        CG_SPLL_FUNC_CNTL_3                             0x608
+#define                SPLL_FB_DIV(x)                          ((x) << 0)
+#define                SPLL_FB_DIV_MASK                        (0x3ffffff << 0)
+#define                SPLL_DITHEN                             (1 << 28)
+
+#define MPLL_CNTL_MODE                                  0x61c
+#       define SS_SSEN                                  (1 << 24)
+#       define SS_DSMODE_EN                             (1 << 25)
+
+#define        MPLL_AD_FUNC_CNTL                               0x624
+#define                CLKF(x)                                 ((x) << 0)
+#define                CLKF_MASK                               (0x7f << 0)
+#define                CLKR(x)                                 ((x) << 7)
+#define                CLKR_MASK                               (0x1f << 7)
+#define                CLKFRAC(x)                              ((x) << 12)
+#define                CLKFRAC_MASK                            (0x1f << 12)
+#define                YCLK_POST_DIV(x)                        ((x) << 17)
+#define                YCLK_POST_DIV_MASK                      (3 << 17)
+#define                IBIAS(x)                                ((x) << 20)
+#define                IBIAS_MASK                              (0x3ff << 20)
+#define                RESET                                   (1 << 30)
+#define                PDNB                                    (1 << 31)
+#define        MPLL_AD_FUNC_CNTL_2                             0x628
+#define                BYPASS                                  (1 << 19)
+#define                BIAS_GEN_PDNB                           (1 << 24)
+#define                RESET_EN                                (1 << 25)
+#define                VCO_MODE                                (1 << 29)
+#define        MPLL_DQ_FUNC_CNTL                               0x62c
+#define        MPLL_DQ_FUNC_CNTL_2                             0x630
+
+#define GENERAL_PWRMGT                                  0x63c
+#       define GLOBAL_PWRMGT_EN                         (1 << 0)
+#       define STATIC_PM_EN                             (1 << 1)
+#       define THERMAL_PROTECTION_DIS                   (1 << 2)
+#       define THERMAL_PROTECTION_TYPE                  (1 << 3)
+#       define ENABLE_GEN2PCIE                          (1 << 4)
+#       define ENABLE_GEN2XSP                           (1 << 5)
+#       define SW_SMIO_INDEX(x)                         ((x) << 6)
+#       define SW_SMIO_INDEX_MASK                       (3 << 6)
+#       define SW_SMIO_INDEX_SHIFT                      6
+#       define LOW_VOLT_D2_ACPI                         (1 << 8)
+#       define LOW_VOLT_D3_ACPI                         (1 << 9)
+#       define VOLT_PWRMGT_EN                           (1 << 10)
+#       define BACKBIAS_PAD_EN                          (1 << 18)
+#       define BACKBIAS_VALUE                           (1 << 19)
+#       define DYN_SPREAD_SPECTRUM_EN                   (1 << 23)
+#       define AC_DC_SW                                 (1 << 24)
+
+#define SCLK_PWRMGT_CNTL                                  0x644
+#       define SCLK_PWRMGT_OFF                            (1 << 0)
+#       define SCLK_LOW_D1                                (1 << 1)
+#       define FIR_RESET                                  (1 << 4)
+#       define FIR_FORCE_TREND_SEL                        (1 << 5)
+#       define FIR_TREND_MODE                             (1 << 6)
+#       define DYN_GFX_CLK_OFF_EN                         (1 << 7)
+#       define GFX_CLK_FORCE_ON                           (1 << 8)
+#       define GFX_CLK_REQUEST_OFF                        (1 << 9)
+#       define GFX_CLK_FORCE_OFF                          (1 << 10)
+#       define GFX_CLK_OFF_ACPI_D1                        (1 << 11)
+#       define GFX_CLK_OFF_ACPI_D2                        (1 << 12)
+#       define GFX_CLK_OFF_ACPI_D3                        (1 << 13)
+#       define DYN_LIGHT_SLEEP_EN                         (1 << 14)
+#define        MCLK_PWRMGT_CNTL                                0x648
+#       define DLL_SPEED(x)                            ((x) << 0)
+#       define DLL_SPEED_MASK                          (0x1f << 0)
+#       define MPLL_PWRMGT_OFF                          (1 << 5)
+#       define DLL_READY                                (1 << 6)
+#       define MC_INT_CNTL                              (1 << 7)
+#       define MRDCKA0_PDNB                             (1 << 8)
+#       define MRDCKA1_PDNB                             (1 << 9)
+#       define MRDCKB0_PDNB                             (1 << 10)
+#       define MRDCKB1_PDNB                             (1 << 11)
+#       define MRDCKC0_PDNB                             (1 << 12)
+#       define MRDCKC1_PDNB                             (1 << 13)
+#       define MRDCKD0_PDNB                             (1 << 14)
+#       define MRDCKD1_PDNB                             (1 << 15)
+#       define MRDCKA0_RESET                            (1 << 16)
+#       define MRDCKA1_RESET                            (1 << 17)
+#       define MRDCKB0_RESET                            (1 << 18)
+#       define MRDCKB1_RESET                            (1 << 19)
+#       define MRDCKC0_RESET                            (1 << 20)
+#       define MRDCKC1_RESET                            (1 << 21)
+#       define MRDCKD0_RESET                            (1 << 22)
+#       define MRDCKD1_RESET                            (1 << 23)
+#       define DLL_READY_READ                           (1 << 24)
+#       define USE_DISPLAY_GAP                          (1 << 25)
+#       define USE_DISPLAY_URGENT_NORMAL                (1 << 26)
+#       define MPLL_TURNOFF_D2                          (1 << 28)
+#define        DLL_CNTL                                        0x64c
+#       define MRDCKA0_BYPASS                           (1 << 24)
+#       define MRDCKA1_BYPASS                           (1 << 25)
+#       define MRDCKB0_BYPASS                           (1 << 26)
+#       define MRDCKB1_BYPASS                           (1 << 27)
+#       define MRDCKC0_BYPASS                           (1 << 28)
+#       define MRDCKC1_BYPASS                           (1 << 29)
+#       define MRDCKD0_BYPASS                           (1 << 30)
+#       define MRDCKD1_BYPASS                           (1 << 31)
+
+#define CG_AT                                           0x6d4
+#       define CG_R(x)                                 ((x) << 0)
+#       define CG_R_MASK                               (0xffff << 0)
+#       define CG_L(x)                                 ((x) << 16)
+#       define CG_L_MASK                               (0xffff << 16)
+
+#define CG_DISPLAY_GAP_CNTL                               0x714
+#       define DISP1_GAP(x)                               ((x) << 0)
+#       define DISP1_GAP_MASK                             (3 << 0)
+#       define DISP2_GAP(x)                               ((x) << 2)
+#       define DISP2_GAP_MASK                             (3 << 2)
+#       define VBI_TIMER_COUNT(x)                         ((x) << 4)
+#       define VBI_TIMER_COUNT_MASK                       (0x3fff << 4)
+#       define VBI_TIMER_UNIT(x)                          ((x) << 20)
+#       define VBI_TIMER_UNIT_MASK                        (7 << 20)
+#       define DISP1_GAP_MCHG(x)                          ((x) << 24)
+#       define DISP1_GAP_MCHG_MASK                        (3 << 24)
+#       define DISP2_GAP_MCHG(x)                          ((x) << 26)
+#       define DISP2_GAP_MCHG_MASK                        (3 << 26)
+
+#define        CG_BIF_REQ_AND_RSP                              0x7f4
+#define                CG_CLIENT_REQ(x)                        ((x) << 0)
+#define                CG_CLIENT_REQ_MASK                      (0xff << 0)
+#define                CG_CLIENT_REQ_SHIFT                     0
+#define                CG_CLIENT_RESP(x)                       ((x) << 8)
+#define                CG_CLIENT_RESP_MASK                     (0xff << 8)
+#define                CG_CLIENT_RESP_SHIFT                    8
+#define                CLIENT_CG_REQ(x)                        ((x) << 16)
+#define                CLIENT_CG_REQ_MASK                      (0xff << 16)
+#define                CLIENT_CG_REQ_SHIFT                     16
+#define                CLIENT_CG_RESP(x)                       ((x) << 24)
+#define                CLIENT_CG_RESP_MASK                     (0xff << 24)
+#define                CLIENT_CG_RESP_SHIFT                    24
+
+#define        CG_SPLL_SPREAD_SPECTRUM                         0x790
+#define                SSEN                                    (1 << 0)
+#define        CG_SPLL_SPREAD_SPECTRUM_2                       0x794
+
+#define        MPLL_SS1                                        0x85c
+#define                CLKV(x)                                 ((x) << 0)
+#define                CLKV_MASK                               (0x3ffffff << 0)
+#define        MPLL_SS2                                        0x860
+#define                CLKS(x)                                 ((x) << 0)
+#define                CLKS_MASK                               (0xfff << 0)
+
+#define        CG_IND_ADDR                                     0x8f8
+#define        CG_IND_DATA                                     0x8fc
+/* CGIND regs */
+#define        CG_CGTT_LOCAL_0                                 0x00
+#define        CG_CGTT_LOCAL_1                                 0x01
+#define        CG_CGTT_LOCAL_2                                 0x02
+#define        CG_CGTT_LOCAL_3                                 0x03
+#define        CG_CGLS_TILE_0                                  0x20
+#define        CG_CGLS_TILE_1                                  0x21
+#define        CG_CGLS_TILE_2                                  0x22
+#define        CG_CGLS_TILE_3                                  0x23
+#define        CG_CGLS_TILE_4                                  0x24
+#define        CG_CGLS_TILE_5                                  0x25
+#define        CG_CGLS_TILE_6                                  0x26
+#define        CG_CGLS_TILE_7                                  0x27
+#define        CG_CGLS_TILE_8                                  0x28
+#define        CG_CGLS_TILE_9                                  0x29
+#define        CG_CGLS_TILE_10                                 0x2a
+#define        CG_CGLS_TILE_11                                 0x2b
+
+#define VM_L2_CG                                        0x15c0
+
+#define MC_CONFIG                                       0x2000
+
+#define MC_CONFIG_MCD                                   0x20a0
+#define MC_CG_CONFIG_MCD                                0x20a4
+#define                MC_RD_ENABLE_MCD(x)                     ((x) << 8)
+#define                MC_RD_ENABLE_MCD_MASK                   (7 << 8)
+
+#define MC_HUB_MISC_HUB_CG                              0x20b8
+#define MC_HUB_MISC_VM_CG                               0x20bc
+#define MC_HUB_MISC_SIP_CG                              0x20c0
+
+#define MC_XPB_CLK_GAT                                  0x2478
+
+#define MC_CG_CONFIG                                    0x25bc
+#define                MC_RD_ENABLE(x)                         ((x) << 4)
+#define                MC_RD_ENABLE_MASK                       (3 << 4)
+
+#define MC_CITF_MISC_RD_CG                              0x2648
+#define MC_CITF_MISC_WR_CG                              0x264c
+#define MC_CITF_MISC_VM_CG                              0x2650
+#       define MEM_LS_ENABLE                            (1 << 19)
+
+#define MC_ARB_BURST_TIME                               0x2808
+#define                STATE0(x)                               ((x) << 0)
+#define                STATE0_MASK                             (0x1f << 0)
+#define                STATE1(x)                               ((x) << 5)
+#define                STATE1_MASK                             (0x1f << 5)
+#define                STATE2(x)                               ((x) << 10)
+#define                STATE2_MASK                             (0x1f << 10)
+#define                STATE3(x)                               ((x) << 15)
+#define                STATE3_MASK                             (0x1f << 15)
+
+#define MC_SEQ_RAS_TIMING                               0x28a0
+#define MC_SEQ_CAS_TIMING                               0x28a4
+#define MC_SEQ_MISC_TIMING                              0x28a8
+#define MC_SEQ_MISC_TIMING2                             0x28ac
+
+#define MC_SEQ_RD_CTL_D0                                0x28b4
+#define MC_SEQ_RD_CTL_D1                                0x28b8
+#define MC_SEQ_WR_CTL_D0                                0x28bc
+#define MC_SEQ_WR_CTL_D1                                0x28c0
+
+#define MC_SEQ_STATUS_M                                 0x29f4
+#       define PMG_PWRSTATE                             (1 << 16)
+
+#define MC_SEQ_MISC1                                    0x2a04
+#define MC_SEQ_RESERVE_M                                0x2a08
+#define MC_PMG_CMD_EMRS                                 0x2a0c
+
+#define MC_SEQ_MISC3                                    0x2a2c
+
+#define MC_SEQ_MISC5                                    0x2a54
+#define MC_SEQ_MISC6                                    0x2a58
+
+#define MC_SEQ_MISC7                                    0x2a64
+
+#define MC_SEQ_CG                                       0x2a68
+#define                CG_SEQ_REQ(x)                           ((x) << 0)
+#define                CG_SEQ_REQ_MASK                         (0xff << 0)
+#define                CG_SEQ_REQ_SHIFT                        0
+#define                CG_SEQ_RESP(x)                          ((x) << 8)
+#define                CG_SEQ_RESP_MASK                        (0xff << 8)
+#define                CG_SEQ_RESP_SHIFT                       8
+#define                SEQ_CG_REQ(x)                           ((x) << 16)
+#define                SEQ_CG_REQ_MASK                         (0xff << 16)
+#define                SEQ_CG_REQ_SHIFT                        16
+#define                SEQ_CG_RESP(x)                          ((x) << 24)
+#define                SEQ_CG_RESP_MASK                        (0xff << 24)
+#define                SEQ_CG_RESP_SHIFT                       24
+#define MC_SEQ_RAS_TIMING_LP                            0x2a6c
+#define MC_SEQ_CAS_TIMING_LP                            0x2a70
+#define MC_SEQ_MISC_TIMING_LP                           0x2a74
+#define MC_SEQ_MISC_TIMING2_LP                          0x2a78
+#define MC_SEQ_WR_CTL_D0_LP                             0x2a7c
+#define MC_SEQ_WR_CTL_D1_LP                             0x2a80
+#define MC_SEQ_PMG_CMD_EMRS_LP                          0x2a84
+#define MC_SEQ_PMG_CMD_MRS_LP                           0x2a88
+
+#define MC_PMG_CMD_MRS                                  0x2aac
+
+#define MC_SEQ_RD_CTL_D0_LP                             0x2b1c
+#define MC_SEQ_RD_CTL_D1_LP                             0x2b20
+
+#define MC_PMG_CMD_MRS1                                 0x2b44
+#define MC_SEQ_PMG_CMD_MRS1_LP                          0x2b48
+
+#define CGTS_SM_CTRL_REG                                0x9150
+
 /* Registers */
 
 #define RCU_IND_INDEX                                  0x100
 #define CG_VCLK_STATUS                                  0x61c
 #define        CG_SCRATCH1                                     0x820
 
+#define RLC_CNTL                                        0x3f00
+#       define RLC_ENABLE                               (1 << 0)
+#       define GFX_POWER_GATING_ENABLE                  (1 << 7)
+#       define GFX_POWER_GATING_SRC                     (1 << 8)
+#       define DYN_PER_SIMD_PG_ENABLE                   (1 << 27)
+#       define LB_CNT_SPIM_ACTIVE                       (1 << 30)
+#       define LOAD_BALANCE_ENABLE                      (1 << 31)
+
+#define RLC_HB_BASE                                       0x3f10
+#define RLC_HB_CNTL                                       0x3f0c
+#define RLC_HB_RPTR                                       0x3f20
+#define RLC_HB_WPTR                                       0x3f1c
+#define RLC_HB_WPTR_LSB_ADDR                              0x3f14
+#define RLC_HB_WPTR_MSB_ADDR                              0x3f18
+#define RLC_MC_CNTL                                       0x3f44
+#define RLC_UCODE_CNTL                                    0x3f48
+#define RLC_UCODE_ADDR                                    0x3f2c
+#define RLC_UCODE_DATA                                    0x3f30
+
+/* new for TN */
+#define TN_RLC_SAVE_AND_RESTORE_BASE                      0x3f10
+#define TN_RLC_LB_CNTR_MAX                                0x3f14
+#define TN_RLC_LB_CNTR_INIT                               0x3f18
+#define TN_RLC_CLEAR_STATE_RESTORE_BASE                   0x3f20
+#define TN_RLC_LB_INIT_SIMD_MASK                          0x3fe4
+#define TN_RLC_LB_ALWAYS_ACTIVE_SIMD_MASK                 0x3fe8
+#define TN_RLC_LB_PARAMS                                  0x3fec
+
 #define GRBM_GFX_INDEX                                 0x802C
 #define                INSTANCE_INDEX(x)                       ((x) << 0)
 #define                SE_INDEX(x)                             ((x) << 16)
 #define        CG_THERMAL_CTRL                                 0x72c
 #define                TOFFSET_MASK                            0x00003FE0
 #define                TOFFSET_SHIFT                           5
+#define                DIG_THERM_DPM(x)                        ((x) << 14)
+#define                DIG_THERM_DPM_MASK                      0x003FC000
+#define                DIG_THERM_DPM_SHIFT                     14
+
+#define        CG_THERMAL_INT                                  0x734
+#define                DIG_THERM_INTH(x)                       ((x) << 8)
+#define                DIG_THERM_INTH_MASK                     0x0000FF00
+#define                DIG_THERM_INTH_SHIFT                    8
+#define                DIG_THERM_INTL(x)                       ((x) << 16)
+#define                DIG_THERM_INTL_MASK                     0x00FF0000
+#define                DIG_THERM_INTL_SHIFT                    16
+#define        THERM_INT_MASK_HIGH                     (1 << 24)
+#define        THERM_INT_MASK_LOW                      (1 << 25)
+
+#define        TN_CG_THERMAL_INT_CTRL                          0x738
+#define                TN_DIG_THERM_INTH(x)                    ((x) << 0)
+#define                TN_DIG_THERM_INTH_MASK                  0x000000FF
+#define                TN_DIG_THERM_INTH_SHIFT                 0
+#define                TN_DIG_THERM_INTL(x)                    ((x) << 8)
+#define                TN_DIG_THERM_INTL_MASK                  0x0000FF00
+#define                TN_DIG_THERM_INTL_SHIFT                 8
+#define        TN_THERM_INT_MASK_HIGH                  (1 << 24)
+#define        TN_THERM_INT_MASK_LOW                   (1 << 25)
+
 #define        CG_MULT_THERMAL_STATUS                          0x740
 #define                ASIC_T(x)                               ((x) << 16)
 #define                ASIC_T_MASK                             0x07FF0000
 #define        CG_TS0_STATUS                                   0x760
 #define                TS0_ADC_DOUT_MASK                       0x000003FF
 #define                TS0_ADC_DOUT_SHIFT                      0
+
 /* APU */
 #define        CG_THERMAL_STATUS                               0x678
 
 #define        DMA_PACKET_CONSTANT_FILL                0xd
 #define        DMA_PACKET_NOP                          0xf
 
-/* PCIE link stuff */
+/* PIF PHY0 indirect regs */
+#define PB0_PIF_CNTL                                      0x10
+#       define LS2_EXIT_TIME(x)                           ((x) << 17)
+#       define LS2_EXIT_TIME_MASK                         (0x7 << 17)
+#       define LS2_EXIT_TIME_SHIFT                        17
+#define PB0_PIF_PAIRING                                   0x11
+#       define MULTI_PIF                                  (1 << 25)
+#define PB0_PIF_PWRDOWN_0                                 0x12
+#       define PLL_POWER_STATE_IN_TXS2_0(x)               ((x) << 7)
+#       define PLL_POWER_STATE_IN_TXS2_0_MASK             (0x7 << 7)
+#       define PLL_POWER_STATE_IN_TXS2_0_SHIFT            7
+#       define PLL_POWER_STATE_IN_OFF_0(x)                ((x) << 10)
+#       define PLL_POWER_STATE_IN_OFF_0_MASK              (0x7 << 10)
+#       define PLL_POWER_STATE_IN_OFF_0_SHIFT             10
+#       define PLL_RAMP_UP_TIME_0(x)                      ((x) << 24)
+#       define PLL_RAMP_UP_TIME_0_MASK                    (0x7 << 24)
+#       define PLL_RAMP_UP_TIME_0_SHIFT                   24
+#define PB0_PIF_PWRDOWN_1                                 0x13
+#       define PLL_POWER_STATE_IN_TXS2_1(x)               ((x) << 7)
+#       define PLL_POWER_STATE_IN_TXS2_1_MASK             (0x7 << 7)
+#       define PLL_POWER_STATE_IN_TXS2_1_SHIFT            7
+#       define PLL_POWER_STATE_IN_OFF_1(x)                ((x) << 10)
+#       define PLL_POWER_STATE_IN_OFF_1_MASK              (0x7 << 10)
+#       define PLL_POWER_STATE_IN_OFF_1_SHIFT             10
+#       define PLL_RAMP_UP_TIME_1(x)                      ((x) << 24)
+#       define PLL_RAMP_UP_TIME_1_MASK                    (0x7 << 24)
+#       define PLL_RAMP_UP_TIME_1_SHIFT                   24
+/* PIF PHY1 indirect regs */
+#define PB1_PIF_CNTL                                      0x10
+#define PB1_PIF_PAIRING                                   0x11
+#define PB1_PIF_PWRDOWN_0                                 0x12
+#define PB1_PIF_PWRDOWN_1                                 0x13
+/* PCIE PORT indirect regs */
+#define PCIE_LC_CNTL                                      0xa0
+#       define LC_L0S_INACTIVITY(x)                       ((x) << 8)
+#       define LC_L0S_INACTIVITY_MASK                     (0xf << 8)
+#       define LC_L0S_INACTIVITY_SHIFT                    8
+#       define LC_L1_INACTIVITY(x)                        ((x) << 12)
+#       define LC_L1_INACTIVITY_MASK                      (0xf << 12)
+#       define LC_L1_INACTIVITY_SHIFT                     12
+#       define LC_PMI_TO_L1_DIS                           (1 << 16)
+#       define LC_ASPM_TO_L1_DIS                          (1 << 24)
 #define PCIE_LC_TRAINING_CNTL                             0xa1 /* PCIE_P */
 #define PCIE_LC_LINK_WIDTH_CNTL                           0xa2 /* PCIE_P */
 #       define LC_LINK_WIDTH_SHIFT                        0
 #       define LC_SHORT_RECONFIG_EN                       (1 << 11)
 #       define LC_UPCONFIGURE_SUPPORT                     (1 << 12)
 #       define LC_UPCONFIGURE_DIS                         (1 << 13)
+#       define LC_DYN_LANES_PWR_STATE(x)                  ((x) << 21)
+#       define LC_DYN_LANES_PWR_STATE_MASK                (0x3 << 21)
+#       define LC_DYN_LANES_PWR_STATE_SHIFT               21
 #define PCIE_LC_SPEED_CNTL                                0xa4 /* PCIE_P */
 #       define LC_GEN2_EN_STRAP                           (1 << 0)
 #       define LC_TARGET_LINK_SPEED_OVERRIDE_EN           (1 << 1)
 #       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK      (0x3 << 8)
 #       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT     3
 #       define LC_CURRENT_DATA_RATE                       (1 << 11)
+#       define LC_HW_VOLTAGE_IF_CONTROL(x)                ((x) << 12)
+#       define LC_HW_VOLTAGE_IF_CONTROL_MASK              (3 << 12)
+#       define LC_HW_VOLTAGE_IF_CONTROL_SHIFT             12
 #       define LC_VOLTAGE_TIMER_SEL_MASK                  (0xf << 14)
 #       define LC_CLR_FAILED_SPD_CHANGE_CNT               (1 << 21)
 #       define LC_OTHER_SIDE_EVER_SENT_GEN2               (1 << 23)
index 8458330..f30127c 100644 (file)
 #include "atom.h"
 #include "ni_reg.h"
 #include "cayman_blit_shaders.h"
+#include "radeon_ucode.h"
+#include "clearstate_cayman.h"
+
+static u32 tn_rlc_save_restore_register_list[] =
+{
+       0x98fc,
+       0x98f0,
+       0x9834,
+       0x9838,
+       0x9870,
+       0x9874,
+       0x8a14,
+       0x8b24,
+       0x8bcc,
+       0x8b10,
+       0x8c30,
+       0x8d00,
+       0x8d04,
+       0x8c00,
+       0x8c04,
+       0x8c10,
+       0x8c14,
+       0x8d8c,
+       0x8cf0,
+       0x8e38,
+       0x9508,
+       0x9688,
+       0x9608,
+       0x960c,
+       0x9610,
+       0x9614,
+       0x88c4,
+       0x8978,
+       0x88d4,
+       0x900c,
+       0x9100,
+       0x913c,
+       0x90e8,
+       0x9354,
+       0xa008,
+       0x98f8,
+       0x9148,
+       0x914c,
+       0x3f94,
+       0x98f4,
+       0x9b7c,
+       0x3f8c,
+       0x8950,
+       0x8954,
+       0x8a18,
+       0x8b28,
+       0x9144,
+       0x3f90,
+       0x915c,
+       0x9160,
+       0x9178,
+       0x917c,
+       0x9180,
+       0x918c,
+       0x9190,
+       0x9194,
+       0x9198,
+       0x919c,
+       0x91a8,
+       0x91ac,
+       0x91b0,
+       0x91b4,
+       0x91b8,
+       0x91c4,
+       0x91c8,
+       0x91cc,
+       0x91d0,
+       0x91d4,
+       0x91e0,
+       0x91e4,
+       0x91ec,
+       0x91f0,
+       0x91f4,
+       0x9200,
+       0x9204,
+       0x929c,
+       0x8030,
+       0x9150,
+       0x9a60,
+       0x920c,
+       0x9210,
+       0x9228,
+       0x922c,
+       0x9244,
+       0x9248,
+       0x91e8,
+       0x9294,
+       0x9208,
+       0x9224,
+       0x9240,
+       0x9220,
+       0x923c,
+       0x9258,
+       0x9744,
+       0xa200,
+       0xa204,
+       0xa208,
+       0xa20c,
+       0x8d58,
+       0x9030,
+       0x9034,
+       0x9038,
+       0x903c,
+       0x9040,
+       0x9654,
+       0x897c,
+       0xa210,
+       0xa214,
+       0x9868,
+       0xa02c,
+       0x9664,
+       0x9698,
+       0x949c,
+       0x8e10,
+       0x8e18,
+       0x8c50,
+       0x8c58,
+       0x8c60,
+       0x8c68,
+       0x89b4,
+       0x9830,
+       0x802c,
+};
+static u32 tn_rlc_save_restore_register_list_size = ARRAY_SIZE(tn_rlc_save_restore_register_list);
 
 extern bool evergreen_is_display_hung(struct radeon_device *rdev);
 extern void evergreen_print_gpu_status_regs(struct radeon_device *rdev);
@@ -44,36 +173,29 @@ extern void evergreen_irq_suspend(struct radeon_device *rdev);
 extern int evergreen_mc_init(struct radeon_device *rdev);
 extern void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev);
 extern void evergreen_pcie_gen2_enable(struct radeon_device *rdev);
-extern void si_rlc_fini(struct radeon_device *rdev);
-extern int si_rlc_init(struct radeon_device *rdev);
-
-#define EVERGREEN_PFP_UCODE_SIZE 1120
-#define EVERGREEN_PM4_UCODE_SIZE 1376
-#define EVERGREEN_RLC_UCODE_SIZE 768
-#define BTC_MC_UCODE_SIZE 6024
-
-#define CAYMAN_PFP_UCODE_SIZE 2176
-#define CAYMAN_PM4_UCODE_SIZE 2176
-#define CAYMAN_RLC_UCODE_SIZE 1024
-#define CAYMAN_MC_UCODE_SIZE 6037
-
-#define ARUBA_RLC_UCODE_SIZE 1536
+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);
 
 /* Firmware Names */
 MODULE_FIRMWARE("radeon/BARTS_pfp.bin");
 MODULE_FIRMWARE("radeon/BARTS_me.bin");
 MODULE_FIRMWARE("radeon/BARTS_mc.bin");
+MODULE_FIRMWARE("radeon/BARTS_smc.bin");
 MODULE_FIRMWARE("radeon/BTC_rlc.bin");
 MODULE_FIRMWARE("radeon/TURKS_pfp.bin");
 MODULE_FIRMWARE("radeon/TURKS_me.bin");
 MODULE_FIRMWARE("radeon/TURKS_mc.bin");
+MODULE_FIRMWARE("radeon/TURKS_smc.bin");
 MODULE_FIRMWARE("radeon/CAICOS_pfp.bin");
 MODULE_FIRMWARE("radeon/CAICOS_me.bin");
 MODULE_FIRMWARE("radeon/CAICOS_mc.bin");
+MODULE_FIRMWARE("radeon/CAICOS_smc.bin");
 MODULE_FIRMWARE("radeon/CAYMAN_pfp.bin");
 MODULE_FIRMWARE("radeon/CAYMAN_me.bin");
 MODULE_FIRMWARE("radeon/CAYMAN_mc.bin");
 MODULE_FIRMWARE("radeon/CAYMAN_rlc.bin");
+MODULE_FIRMWARE("radeon/CAYMAN_smc.bin");
 MODULE_FIRMWARE("radeon/ARUBA_pfp.bin");
 MODULE_FIRMWARE("radeon/ARUBA_me.bin");
 MODULE_FIRMWARE("radeon/ARUBA_rlc.bin");
@@ -566,6 +688,7 @@ int ni_init_microcode(struct radeon_device *rdev)
        const char *chip_name;
        const char *rlc_chip_name;
        size_t pfp_req_size, me_req_size, rlc_req_size, mc_req_size;
+       size_t smc_req_size = 0;
        char fw_name[30];
        int err;
 
@@ -586,6 +709,7 @@ int ni_init_microcode(struct radeon_device *rdev)
                me_req_size = EVERGREEN_PM4_UCODE_SIZE * 4;
                rlc_req_size = EVERGREEN_RLC_UCODE_SIZE * 4;
                mc_req_size = BTC_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(BARTS_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_TURKS:
                chip_name = "TURKS";
@@ -594,6 +718,7 @@ int ni_init_microcode(struct radeon_device *rdev)
                me_req_size = EVERGREEN_PM4_UCODE_SIZE * 4;
                rlc_req_size = EVERGREEN_RLC_UCODE_SIZE * 4;
                mc_req_size = BTC_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(TURKS_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_CAICOS:
                chip_name = "CAICOS";
@@ -602,6 +727,7 @@ int ni_init_microcode(struct radeon_device *rdev)
                me_req_size = EVERGREEN_PM4_UCODE_SIZE * 4;
                rlc_req_size = EVERGREEN_RLC_UCODE_SIZE * 4;
                mc_req_size = BTC_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(CAICOS_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_CAYMAN:
                chip_name = "CAYMAN";
@@ -610,6 +736,7 @@ int ni_init_microcode(struct radeon_device *rdev)
                me_req_size = CAYMAN_PM4_UCODE_SIZE * 4;
                rlc_req_size = CAYMAN_RLC_UCODE_SIZE * 4;
                mc_req_size = CAYMAN_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(CAYMAN_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_ARUBA:
                chip_name = "ARUBA";
@@ -672,6 +799,20 @@ int ni_init_microcode(struct radeon_device *rdev)
                        err = -EINVAL;
                }
        }
+
+       if ((rdev->family >= CHIP_BARTS) && (rdev->family <= CHIP_CAYMAN)) {
+               snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name);
+               err = request_firmware(&rdev->smc_fw, fw_name, &pdev->dev);
+               if (err)
+                       goto out;
+               if (rdev->smc_fw->size != smc_req_size) {
+                       printk(KERN_ERR
+                              "ni_mc: Bogus length %zu in firmware \"%s\"\n",
+                              rdev->mc_fw->size, fw_name);
+                       err = -EINVAL;
+               }
+       }
+
 out:
        platform_device_unregister(pdev);
 
@@ -692,6 +833,14 @@ out:
        return err;
 }
 
+int tn_get_temp(struct radeon_device *rdev)
+{
+       u32 temp = RREG32_SMC(TN_CURRENT_GNB_TEMP) & 0x7ff;
+       int actual_temp = (temp / 8) - 49;
+
+       return actual_temp * 1000;
+}
+
 /*
  * Core functions
  */
@@ -1027,6 +1176,16 @@ static void cayman_gpu_init(struct radeon_device *rdev)
        WREG32(PA_CL_ENHANCE, CLIP_VTX_REORDER_ENA | NUM_CLIP_SEQ(3));
 
        udelay(50);
+
+       /* set clockgating golden values on TN */
+       if (rdev->family == CHIP_ARUBA) {
+               tmp = RREG32_CG(CG_CGTT_LOCAL_0);
+               tmp &= ~0x00380000;
+               WREG32_CG(CG_CGTT_LOCAL_0, tmp);
+                tmp = RREG32_CG(CG_CGTT_LOCAL_1);
+               tmp &= ~0x0e000000;
+               WREG32_CG(CG_CGTT_LOCAL_1, tmp);
+       }
 }
 
 /*
@@ -1928,6 +2087,8 @@ static int cayman_startup(struct radeon_device *rdev)
 
        /* enable pcie gen2 link */
        evergreen_pcie_gen2_enable(rdev);
+       /* enable aspm */
+       evergreen_program_aspm(rdev);
 
        if (rdev->flags & RADEON_IS_IGP) {
                if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) {
@@ -1972,7 +2133,10 @@ static int cayman_startup(struct radeon_device *rdev)
 
        /* allocate rlc buffers */
        if (rdev->flags & RADEON_IS_IGP) {
-               r = si_rlc_init(rdev);
+               rdev->rlc.reg_list = tn_rlc_save_restore_register_list;
+               rdev->rlc.reg_list_size = tn_rlc_save_restore_register_list_size;
+               rdev->rlc.cs_data = cayman_cs_data;
+               r = sumo_rlc_init(rdev);
                if (r) {
                        DRM_ERROR("Failed to init rlc BOs!\n");
                        return r;
@@ -2229,7 +2393,7 @@ int cayman_init(struct radeon_device *rdev)
                cayman_dma_fini(rdev);
                r600_irq_fini(rdev);
                if (rdev->flags & RADEON_IS_IGP)
-                       si_rlc_fini(rdev);
+                       sumo_rlc_fini(rdev);
                radeon_wb_fini(rdev);
                radeon_ib_pool_fini(rdev);
                radeon_vm_manager_fini(rdev);
@@ -2260,7 +2424,7 @@ void cayman_fini(struct radeon_device *rdev)
        cayman_dma_fini(rdev);
        r600_irq_fini(rdev);
        if (rdev->flags & RADEON_IS_IGP)
-               si_rlc_fini(rdev);
+               sumo_rlc_fini(rdev);
        radeon_wb_fini(rdev);
        radeon_vm_manager_fini(rdev);
        radeon_ib_pool_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c
new file mode 100644 (file)
index 0000000..559cf24
--- /dev/null
@@ -0,0 +1,4370 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "nid.h"
+#include "r600_dpm.h"
+#include "ni_dpm.h"
+#include "atom.h"
+#include <linux/math64.h>
+#include <linux/seq_file.h>
+
+#define MC_CG_ARB_FREQ_F0           0x0a
+#define MC_CG_ARB_FREQ_F1           0x0b
+#define MC_CG_ARB_FREQ_F2           0x0c
+#define MC_CG_ARB_FREQ_F3           0x0d
+
+#define SMC_RAM_END 0xC000
+
+static const struct ni_cac_weights cac_weights_cayman_xt =
+{
+       0x15,
+       0x2,
+       0x19,
+       0x2,
+       0x8,
+       0x14,
+       0x2,
+       0x16,
+       0xE,
+       0x17,
+       0x13,
+       0x2B,
+       0x10,
+       0x7,
+       0x5,
+       0x5,
+       0x5,
+       0x2,
+       0x3,
+       0x9,
+       0x10,
+       0x10,
+       0x2B,
+       0xA,
+       0x9,
+       0x4,
+       0xD,
+       0xD,
+       0x3E,
+       0x18,
+       0x14,
+       0,
+       0x3,
+       0x3,
+       0x5,
+       0,
+       0x2,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0x1CC,
+       0,
+       0x164,
+       1,
+       1,
+       1,
+       1,
+       12,
+       12,
+       12,
+       0x12,
+       0x1F,
+       132,
+       5,
+       7,
+       0,
+       { 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0 },
+       true
+};
+
+static const struct ni_cac_weights cac_weights_cayman_pro =
+{
+       0x16,
+       0x4,
+       0x10,
+       0x2,
+       0xA,
+       0x16,
+       0x2,
+       0x18,
+       0x10,
+       0x1A,
+       0x16,
+       0x2D,
+       0x12,
+       0xA,
+       0x6,
+       0x6,
+       0x6,
+       0x2,
+       0x4,
+       0xB,
+       0x11,
+       0x11,
+       0x2D,
+       0xC,
+       0xC,
+       0x7,
+       0x10,
+       0x10,
+       0x3F,
+       0x1A,
+       0x16,
+       0,
+       0x7,
+       0x4,
+       0x6,
+       1,
+       0x2,
+       0x1,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0x30,
+       0,
+       0x1CF,
+       0,
+       0x166,
+       1,
+       1,
+       1,
+       1,
+       12,
+       12,
+       12,
+       0x15,
+       0x1F,
+       132,
+       6,
+       6,
+       0,
+       { 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0 },
+       true
+};
+
+static const struct ni_cac_weights cac_weights_cayman_le =
+{
+       0x7,
+       0xE,
+       0x1,
+       0xA,
+       0x1,
+       0x3F,
+       0x2,
+       0x18,
+       0x10,
+       0x1A,
+       0x1,
+       0x3F,
+       0x1,
+       0xE,
+       0x6,
+       0x6,
+       0x6,
+       0x2,
+       0x4,
+       0x9,
+       0x1A,
+       0x1A,
+       0x2C,
+       0xA,
+       0x11,
+       0x8,
+       0x19,
+       0x19,
+       0x1,
+       0x1,
+       0x1A,
+       0,
+       0x8,
+       0x5,
+       0x8,
+       0x1,
+       0x3,
+       0x1,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0x38,
+       0x38,
+       0x239,
+       0x3,
+       0x18A,
+       1,
+       1,
+       1,
+       1,
+       12,
+       12,
+       12,
+       0x15,
+       0x22,
+       132,
+       6,
+       6,
+       0,
+       { 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0 },
+       true
+};
+
+#define NISLANDS_MGCG_SEQUENCE  300
+
+static const u32 cayman_cgcg_cgls_default[] =
+{
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff
+};
+#define CAYMAN_CGCG_CGLS_DEFAULT_LENGTH sizeof(cayman_cgcg_cgls_default) / (3 * sizeof(u32))
+
+static const u32 cayman_cgcg_cgls_disable[] =
+{
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x00000644, 0x000f7902, 0x001f4180,
+       0x00000644, 0x000f3802, 0x001f4180
+};
+#define CAYMAN_CGCG_CGLS_DISABLE_LENGTH sizeof(cayman_cgcg_cgls_disable) / (3 * sizeof(u32))
+
+static const u32 cayman_cgcg_cgls_enable[] =
+{
+       0x00000644, 0x000f7882, 0x001f4080,
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000020, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000021, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000022, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000023, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000024, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000025, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000026, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000027, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000028, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000029, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000002a, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x0000002b, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff
+};
+#define CAYMAN_CGCG_CGLS_ENABLE_LENGTH  sizeof(cayman_cgcg_cgls_enable) / (3 * sizeof(u32))
+
+static const u32 cayman_mgcg_default[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x00003fc4, 0xc0000000, 0xffffffff,
+       0x00005448, 0x00000100, 0xffffffff,
+       0x000055e4, 0x00000100, 0xffffffff,
+       0x0000160c, 0x00000100, 0xffffffff,
+       0x00008984, 0x06000100, 0xffffffff,
+       0x0000c164, 0x00000100, 0xffffffff,
+       0x00008a18, 0x00000100, 0xffffffff,
+       0x0000897c, 0x06000100, 0xffffffff,
+       0x00008b28, 0x00000100, 0xffffffff,
+       0x00009144, 0x00800200, 0xffffffff,
+       0x00009a60, 0x00000100, 0xffffffff,
+       0x00009868, 0x00000100, 0xffffffff,
+       0x00008d58, 0x00000100, 0xffffffff,
+       0x00009510, 0x00000100, 0xffffffff,
+       0x0000949c, 0x00000100, 0xffffffff,
+       0x00009654, 0x00000100, 0xffffffff,
+       0x00009030, 0x00000100, 0xffffffff,
+       0x00009034, 0x00000100, 0xffffffff,
+       0x00009038, 0x00000100, 0xffffffff,
+       0x0000903c, 0x00000100, 0xffffffff,
+       0x00009040, 0x00000100, 0xffffffff,
+       0x0000a200, 0x00000100, 0xffffffff,
+       0x0000a204, 0x00000100, 0xffffffff,
+       0x0000a208, 0x00000100, 0xffffffff,
+       0x0000a20c, 0x00000100, 0xffffffff,
+       0x00009744, 0x00000100, 0xffffffff,
+       0x00003f80, 0x00000100, 0xffffffff,
+       0x0000a210, 0x00000100, 0xffffffff,
+       0x0000a214, 0x00000100, 0xffffffff,
+       0x000004d8, 0x00000100, 0xffffffff,
+       0x00009664, 0x00000100, 0xffffffff,
+       0x00009698, 0x00000100, 0xffffffff,
+       0x000004d4, 0x00000200, 0xffffffff,
+       0x000004d0, 0x00000000, 0xffffffff,
+       0x000030cc, 0x00000104, 0xffffffff,
+       0x0000d0c0, 0x00000100, 0xffffffff,
+       0x0000d8c0, 0x00000100, 0xffffffff,
+       0x0000802c, 0x40000000, 0xffffffff,
+       0x00003fc4, 0x40000000, 0xffffffff,
+       0x0000915c, 0x00010000, 0xffffffff,
+       0x00009160, 0x00030002, 0xffffffff,
+       0x00009164, 0x00050004, 0xffffffff,
+       0x00009168, 0x00070006, 0xffffffff,
+       0x00009178, 0x00070000, 0xffffffff,
+       0x0000917c, 0x00030002, 0xffffffff,
+       0x00009180, 0x00050004, 0xffffffff,
+       0x0000918c, 0x00010006, 0xffffffff,
+       0x00009190, 0x00090008, 0xffffffff,
+       0x00009194, 0x00070000, 0xffffffff,
+       0x00009198, 0x00030002, 0xffffffff,
+       0x0000919c, 0x00050004, 0xffffffff,
+       0x000091a8, 0x00010006, 0xffffffff,
+       0x000091ac, 0x00090008, 0xffffffff,
+       0x000091b0, 0x00070000, 0xffffffff,
+       0x000091b4, 0x00030002, 0xffffffff,
+       0x000091b8, 0x00050004, 0xffffffff,
+       0x000091c4, 0x00010006, 0xffffffff,
+       0x000091c8, 0x00090008, 0xffffffff,
+       0x000091cc, 0x00070000, 0xffffffff,
+       0x000091d0, 0x00030002, 0xffffffff,
+       0x000091d4, 0x00050004, 0xffffffff,
+       0x000091e0, 0x00010006, 0xffffffff,
+       0x000091e4, 0x00090008, 0xffffffff,
+       0x000091e8, 0x00000000, 0xffffffff,
+       0x000091ec, 0x00070000, 0xffffffff,
+       0x000091f0, 0x00030002, 0xffffffff,
+       0x000091f4, 0x00050004, 0xffffffff,
+       0x00009200, 0x00010006, 0xffffffff,
+       0x00009204, 0x00090008, 0xffffffff,
+       0x00009208, 0x00070000, 0xffffffff,
+       0x0000920c, 0x00030002, 0xffffffff,
+       0x00009210, 0x00050004, 0xffffffff,
+       0x0000921c, 0x00010006, 0xffffffff,
+       0x00009220, 0x00090008, 0xffffffff,
+       0x00009224, 0x00070000, 0xffffffff,
+       0x00009228, 0x00030002, 0xffffffff,
+       0x0000922c, 0x00050004, 0xffffffff,
+       0x00009238, 0x00010006, 0xffffffff,
+       0x0000923c, 0x00090008, 0xffffffff,
+       0x00009240, 0x00070000, 0xffffffff,
+       0x00009244, 0x00030002, 0xffffffff,
+       0x00009248, 0x00050004, 0xffffffff,
+       0x00009254, 0x00010006, 0xffffffff,
+       0x00009258, 0x00090008, 0xffffffff,
+       0x0000925c, 0x00070000, 0xffffffff,
+       0x00009260, 0x00030002, 0xffffffff,
+       0x00009264, 0x00050004, 0xffffffff,
+       0x00009270, 0x00010006, 0xffffffff,
+       0x00009274, 0x00090008, 0xffffffff,
+       0x00009278, 0x00070000, 0xffffffff,
+       0x0000927c, 0x00030002, 0xffffffff,
+       0x00009280, 0x00050004, 0xffffffff,
+       0x0000928c, 0x00010006, 0xffffffff,
+       0x00009290, 0x00090008, 0xffffffff,
+       0x000092a8, 0x00070000, 0xffffffff,
+       0x000092ac, 0x00030002, 0xffffffff,
+       0x000092b0, 0x00050004, 0xffffffff,
+       0x000092bc, 0x00010006, 0xffffffff,
+       0x000092c0, 0x00090008, 0xffffffff,
+       0x000092c4, 0x00070000, 0xffffffff,
+       0x000092c8, 0x00030002, 0xffffffff,
+       0x000092cc, 0x00050004, 0xffffffff,
+       0x000092d8, 0x00010006, 0xffffffff,
+       0x000092dc, 0x00090008, 0xffffffff,
+       0x00009294, 0x00000000, 0xffffffff,
+       0x0000802c, 0x40010000, 0xffffffff,
+       0x00003fc4, 0x40010000, 0xffffffff,
+       0x0000915c, 0x00010000, 0xffffffff,
+       0x00009160, 0x00030002, 0xffffffff,
+       0x00009164, 0x00050004, 0xffffffff,
+       0x00009168, 0x00070006, 0xffffffff,
+       0x00009178, 0x00070000, 0xffffffff,
+       0x0000917c, 0x00030002, 0xffffffff,
+       0x00009180, 0x00050004, 0xffffffff,
+       0x0000918c, 0x00010006, 0xffffffff,
+       0x00009190, 0x00090008, 0xffffffff,
+       0x00009194, 0x00070000, 0xffffffff,
+       0x00009198, 0x00030002, 0xffffffff,
+       0x0000919c, 0x00050004, 0xffffffff,
+       0x000091a8, 0x00010006, 0xffffffff,
+       0x000091ac, 0x00090008, 0xffffffff,
+       0x000091b0, 0x00070000, 0xffffffff,
+       0x000091b4, 0x00030002, 0xffffffff,
+       0x000091b8, 0x00050004, 0xffffffff,
+       0x000091c4, 0x00010006, 0xffffffff,
+       0x000091c8, 0x00090008, 0xffffffff,
+       0x000091cc, 0x00070000, 0xffffffff,
+       0x000091d0, 0x00030002, 0xffffffff,
+       0x000091d4, 0x00050004, 0xffffffff,
+       0x000091e0, 0x00010006, 0xffffffff,
+       0x000091e4, 0x00090008, 0xffffffff,
+       0x000091e8, 0x00000000, 0xffffffff,
+       0x000091ec, 0x00070000, 0xffffffff,
+       0x000091f0, 0x00030002, 0xffffffff,
+       0x000091f4, 0x00050004, 0xffffffff,
+       0x00009200, 0x00010006, 0xffffffff,
+       0x00009204, 0x00090008, 0xffffffff,
+       0x00009208, 0x00070000, 0xffffffff,
+       0x0000920c, 0x00030002, 0xffffffff,
+       0x00009210, 0x00050004, 0xffffffff,
+       0x0000921c, 0x00010006, 0xffffffff,
+       0x00009220, 0x00090008, 0xffffffff,
+       0x00009224, 0x00070000, 0xffffffff,
+       0x00009228, 0x00030002, 0xffffffff,
+       0x0000922c, 0x00050004, 0xffffffff,
+       0x00009238, 0x00010006, 0xffffffff,
+       0x0000923c, 0x00090008, 0xffffffff,
+       0x00009240, 0x00070000, 0xffffffff,
+       0x00009244, 0x00030002, 0xffffffff,
+       0x00009248, 0x00050004, 0xffffffff,
+       0x00009254, 0x00010006, 0xffffffff,
+       0x00009258, 0x00090008, 0xffffffff,
+       0x0000925c, 0x00070000, 0xffffffff,
+       0x00009260, 0x00030002, 0xffffffff,
+       0x00009264, 0x00050004, 0xffffffff,
+       0x00009270, 0x00010006, 0xffffffff,
+       0x00009274, 0x00090008, 0xffffffff,
+       0x00009278, 0x00070000, 0xffffffff,
+       0x0000927c, 0x00030002, 0xffffffff,
+       0x00009280, 0x00050004, 0xffffffff,
+       0x0000928c, 0x00010006, 0xffffffff,
+       0x00009290, 0x00090008, 0xffffffff,
+       0x000092a8, 0x00070000, 0xffffffff,
+       0x000092ac, 0x00030002, 0xffffffff,
+       0x000092b0, 0x00050004, 0xffffffff,
+       0x000092bc, 0x00010006, 0xffffffff,
+       0x000092c0, 0x00090008, 0xffffffff,
+       0x000092c4, 0x00070000, 0xffffffff,
+       0x000092c8, 0x00030002, 0xffffffff,
+       0x000092cc, 0x00050004, 0xffffffff,
+       0x000092d8, 0x00010006, 0xffffffff,
+       0x000092dc, 0x00090008, 0xffffffff,
+       0x00009294, 0x00000000, 0xffffffff,
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x00003fc4, 0xc0000000, 0xffffffff,
+       0x000008f8, 0x00000010, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000011, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000012, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000013, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000014, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000015, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000016, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000017, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000018, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000019, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001a, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x0000001b, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff
+};
+#define CAYMAN_MGCG_DEFAULT_LENGTH sizeof(cayman_mgcg_default) / (3 * sizeof(u32))
+
+static const u32 cayman_mgcg_disable[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x000008f8, 0x00000000, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000001, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000002, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x000008f8, 0x00000003, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xffffffff,
+       0x00009150, 0x00600000, 0xffffffff
+};
+#define CAYMAN_MGCG_DISABLE_LENGTH   sizeof(cayman_mgcg_disable) / (3 * sizeof(u32))
+
+static const u32 cayman_mgcg_enable[] =
+{
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x000008f8, 0x00000000, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000001, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x000008f8, 0x00000002, 0xffffffff,
+       0x000008fc, 0x00600000, 0xffffffff,
+       0x000008f8, 0x00000003, 0xffffffff,
+       0x000008fc, 0x00000000, 0xffffffff,
+       0x00009150, 0x96944200, 0xffffffff
+};
+
+#define CAYMAN_MGCG_ENABLE_LENGTH   sizeof(cayman_mgcg_enable) / (3 * sizeof(u32))
+
+#define NISLANDS_SYSLS_SEQUENCE  100
+
+static const u32 cayman_sysls_default[] =
+{
+       /* Register,   Value,     Mask bits */
+       0x000055e8, 0x00000000, 0xffffffff,
+       0x0000d0bc, 0x00000000, 0xffffffff,
+       0x0000d8bc, 0x00000000, 0xffffffff,
+       0x000015c0, 0x000c1401, 0xffffffff,
+       0x0000264c, 0x000c0400, 0xffffffff,
+       0x00002648, 0x000c0400, 0xffffffff,
+       0x00002650, 0x000c0400, 0xffffffff,
+       0x000020b8, 0x000c0400, 0xffffffff,
+       0x000020bc, 0x000c0400, 0xffffffff,
+       0x000020c0, 0x000c0c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680fff, 0xffffffff,
+       0x00002f50, 0x00000404, 0xffffffff,
+       0x000004c8, 0x00000001, 0xffffffff,
+       0x000064ec, 0x00000000, 0xffffffff,
+       0x00000c7c, 0x00000000, 0xffffffff,
+       0x00008dfc, 0x00000000, 0xffffffff
+};
+#define CAYMAN_SYSLS_DEFAULT_LENGTH sizeof(cayman_sysls_default) / (3 * sizeof(u32))
+
+static const u32 cayman_sysls_disable[] =
+{
+       /* Register,   Value,     Mask bits */
+       0x0000d0c0, 0x00000000, 0xffffffff,
+       0x0000d8c0, 0x00000000, 0xffffffff,
+       0x000055e8, 0x00000000, 0xffffffff,
+       0x0000d0bc, 0x00000000, 0xffffffff,
+       0x0000d8bc, 0x00000000, 0xffffffff,
+       0x000015c0, 0x00041401, 0xffffffff,
+       0x0000264c, 0x00040400, 0xffffffff,
+       0x00002648, 0x00040400, 0xffffffff,
+       0x00002650, 0x00040400, 0xffffffff,
+       0x000020b8, 0x00040400, 0xffffffff,
+       0x000020bc, 0x00040400, 0xffffffff,
+       0x000020c0, 0x00040c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680000, 0xffffffff,
+       0x00002f50, 0x00000404, 0xffffffff,
+       0x000004c8, 0x00000001, 0xffffffff,
+       0x000064ec, 0x00007ffd, 0xffffffff,
+       0x00000c7c, 0x0000ff00, 0xffffffff,
+       0x00008dfc, 0x0000007f, 0xffffffff
+};
+#define CAYMAN_SYSLS_DISABLE_LENGTH sizeof(cayman_sysls_disable) / (3 * sizeof(u32))
+
+static const u32 cayman_sysls_enable[] =
+{
+       /* Register,   Value,     Mask bits */
+       0x000055e8, 0x00000001, 0xffffffff,
+       0x0000d0bc, 0x00000100, 0xffffffff,
+       0x0000d8bc, 0x00000100, 0xffffffff,
+       0x000015c0, 0x000c1401, 0xffffffff,
+       0x0000264c, 0x000c0400, 0xffffffff,
+       0x00002648, 0x000c0400, 0xffffffff,
+       0x00002650, 0x000c0400, 0xffffffff,
+       0x000020b8, 0x000c0400, 0xffffffff,
+       0x000020bc, 0x000c0400, 0xffffffff,
+       0x000020c0, 0x000c0c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680fff, 0xffffffff,
+       0x00002f50, 0x00000903, 0xffffffff,
+       0x000004c8, 0x00000000, 0xffffffff,
+       0x000064ec, 0x00000000, 0xffffffff,
+       0x00000c7c, 0x00000000, 0xffffffff,
+       0x00008dfc, 0x00000000, 0xffffffff
+};
+#define CAYMAN_SYSLS_ENABLE_LENGTH sizeof(cayman_sysls_enable) / (3 * sizeof(u32))
+
+struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev);
+struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev);
+
+struct ni_power_info *ni_get_pi(struct radeon_device *rdev)
+{
+        struct ni_power_info *pi = rdev->pm.dpm.priv;
+
+        return pi;
+}
+
+struct ni_ps *ni_get_ps(struct radeon_ps *rps)
+{
+       struct ni_ps *ps = rps->ps_priv;
+
+       return ps;
+}
+
+static void ni_calculate_leakage_for_v_and_t_formula(const struct ni_leakage_coeffients *coeff,
+                                                    u16 v, s32 t,
+                                                    u32 ileakage,
+                                                    u32 *leakage)
+{
+       s64 kt, kv, leakage_w, i_leakage, vddc, temperature;
+
+       i_leakage = div64_s64(drm_int2fixp(ileakage), 1000);
+       vddc = div64_s64(drm_int2fixp(v), 1000);
+       temperature = div64_s64(drm_int2fixp(t), 1000);
+
+       kt = drm_fixp_mul(div64_s64(drm_int2fixp(coeff->at), 1000),
+                         drm_fixp_exp(drm_fixp_mul(div64_s64(drm_int2fixp(coeff->bt), 1000), temperature)));
+       kv = drm_fixp_mul(div64_s64(drm_int2fixp(coeff->av), 1000),
+                         drm_fixp_exp(drm_fixp_mul(div64_s64(drm_int2fixp(coeff->bv), 1000), vddc)));
+
+       leakage_w = drm_fixp_mul(drm_fixp_mul(drm_fixp_mul(i_leakage, kt), kv), vddc);
+
+       *leakage = drm_fixp2int(leakage_w * 1000);
+}
+
+static void ni_calculate_leakage_for_v_and_t(struct radeon_device *rdev,
+                                            const struct ni_leakage_coeffients *coeff,
+                                            u16 v,
+                                            s32 t,
+                                            u32 i_leakage,
+                                            u32 *leakage)
+{
+       ni_calculate_leakage_for_v_and_t_formula(coeff, v, t, i_leakage, leakage);
+}
+
+bool ni_dpm_vblank_too_short(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 vblank_time = r600_dpm_get_vblank_time(rdev);
+       u32 switch_limit = pi->mem_gddr5 ? 450 : 300;
+
+       if (vblank_time < switch_limit)
+               return true;
+       else
+               return false;
+
+}
+
+static void ni_apply_state_adjust_rules(struct radeon_device *rdev,
+                                       struct radeon_ps *rps)
+{
+       struct ni_ps *ps = ni_get_ps(rps);
+       struct radeon_clock_and_voltage_limits *max_limits;
+       bool disable_mclk_switching;
+       u32 mclk, sclk;
+       u16 vddc, vddci;
+       int i;
+
+       if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
+           ni_dpm_vblank_too_short(rdev))
+               disable_mclk_switching = true;
+       else
+               disable_mclk_switching = false;
+
+       if (rdev->pm.dpm.ac_power)
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+       else
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
+
+       if (rdev->pm.dpm.ac_power == false) {
+               for (i = 0; i < ps->performance_level_count; i++) {
+                       if (ps->performance_levels[i].mclk > max_limits->mclk)
+                               ps->performance_levels[i].mclk = max_limits->mclk;
+                       if (ps->performance_levels[i].sclk > max_limits->sclk)
+                               ps->performance_levels[i].sclk = max_limits->sclk;
+                       if (ps->performance_levels[i].vddc > max_limits->vddc)
+                               ps->performance_levels[i].vddc = max_limits->vddc;
+                       if (ps->performance_levels[i].vddci > max_limits->vddci)
+                               ps->performance_levels[i].vddci = max_limits->vddci;
+               }
+       }
+
+       /* XXX validate the min clocks required for display */
+
+       if (disable_mclk_switching) {
+               mclk  = ps->performance_levels[ps->performance_level_count - 1].mclk;
+               sclk = ps->performance_levels[0].sclk;
+               vddc = ps->performance_levels[0].vddc;
+               vddci = ps->performance_levels[ps->performance_level_count - 1].vddci;
+       } else {
+               sclk = ps->performance_levels[0].sclk;
+               mclk = ps->performance_levels[0].mclk;
+               vddc = ps->performance_levels[0].vddc;
+               vddci = ps->performance_levels[0].vddci;
+       }
+
+       /* adjusted low state */
+       ps->performance_levels[0].sclk = sclk;
+       ps->performance_levels[0].mclk = mclk;
+       ps->performance_levels[0].vddc = vddc;
+       ps->performance_levels[0].vddci = vddci;
+
+       btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk,
+                                 &ps->performance_levels[0].sclk,
+                                 &ps->performance_levels[0].mclk);
+
+       for (i = 1; i < ps->performance_level_count; i++) {
+               if (ps->performance_levels[i].sclk < ps->performance_levels[i - 1].sclk)
+                       ps->performance_levels[i].sclk = ps->performance_levels[i - 1].sclk;
+               if (ps->performance_levels[i].vddc < ps->performance_levels[i - 1].vddc)
+                       ps->performance_levels[i].vddc = ps->performance_levels[i - 1].vddc;
+       }
+
+       if (disable_mclk_switching) {
+               mclk = ps->performance_levels[0].mclk;
+               for (i = 1; i < ps->performance_level_count; i++) {
+                       if (mclk < ps->performance_levels[i].mclk)
+                               mclk = ps->performance_levels[i].mclk;
+               }
+               for (i = 0; i < ps->performance_level_count; i++) {
+                       ps->performance_levels[i].mclk = mclk;
+                       ps->performance_levels[i].vddci = vddci;
+               }
+       } else {
+               for (i = 1; i < ps->performance_level_count; i++) {
+                       if (ps->performance_levels[i].mclk < ps->performance_levels[i - 1].mclk)
+                               ps->performance_levels[i].mclk = ps->performance_levels[i - 1].mclk;
+                       if (ps->performance_levels[i].vddci < ps->performance_levels[i - 1].vddci)
+                               ps->performance_levels[i].vddci = ps->performance_levels[i - 1].vddci;
+               }
+       }
+
+       for (i = 1; i < ps->performance_level_count; i++)
+               btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk,
+                                         &ps->performance_levels[i].sclk,
+                                         &ps->performance_levels[i].mclk);
+
+       for (i = 0; i < ps->performance_level_count; i++)
+               btc_adjust_clock_combinations(rdev, max_limits,
+                                             &ps->performance_levels[i]);
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                                  ps->performance_levels[i].sclk,
+                                                  max_limits->vddc,  &ps->performance_levels[i].vddc);
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                  ps->performance_levels[i].mclk,
+                                                  max_limits->vddci, &ps->performance_levels[i].vddci);
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                  ps->performance_levels[i].mclk,
+                                                  max_limits->vddc,  &ps->performance_levels[i].vddc);
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk,
+                                                  rdev->clock.current_dispclk,
+                                                  max_limits->vddc,  &ps->performance_levels[i].vddc);
+       }
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               btc_apply_voltage_delta_rules(rdev,
+                                             max_limits->vddc, max_limits->vddci,
+                                             &ps->performance_levels[i].vddc,
+                                             &ps->performance_levels[i].vddci);
+       }
+
+       ps->dc_compatible = true;
+       for (i = 0; i < ps->performance_level_count; i++) {
+               if (ps->performance_levels[i].vddc > rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc)
+                       ps->dc_compatible = false;
+
+               if (ps->performance_levels[i].vddc < rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2)
+                       ps->performance_levels[i].flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2;
+       }
+}
+
+static void ni_cg_clockgating_default(struct radeon_device *rdev)
+{
+       u32 count;
+       const u32 *ps = NULL;
+
+       ps = (const u32 *)&cayman_cgcg_cgls_default;
+       count = CAYMAN_CGCG_CGLS_DEFAULT_LENGTH;
+
+       btc_program_mgcg_hw_sequence(rdev, ps, count);
+}
+
+static void ni_gfx_clockgating_enable(struct radeon_device *rdev,
+                                     bool enable)
+{
+       u32 count;
+       const u32 *ps = NULL;
+
+       if (enable) {
+               ps = (const u32 *)&cayman_cgcg_cgls_enable;
+               count = CAYMAN_CGCG_CGLS_ENABLE_LENGTH;
+       } else {
+               ps = (const u32 *)&cayman_cgcg_cgls_disable;
+               count = CAYMAN_CGCG_CGLS_DISABLE_LENGTH;
+       }
+
+       btc_program_mgcg_hw_sequence(rdev, ps, count);
+}
+
+static void ni_mg_clockgating_default(struct radeon_device *rdev)
+{
+       u32 count;
+       const u32 *ps = NULL;
+
+       ps = (const u32 *)&cayman_mgcg_default;
+       count = CAYMAN_MGCG_DEFAULT_LENGTH;
+
+       btc_program_mgcg_hw_sequence(rdev, ps, count);
+}
+
+static void ni_mg_clockgating_enable(struct radeon_device *rdev,
+                                    bool enable)
+{
+       u32 count;
+       const u32 *ps = NULL;
+
+       if (enable) {
+               ps = (const u32 *)&cayman_mgcg_enable;
+               count = CAYMAN_MGCG_ENABLE_LENGTH;
+       } else {
+               ps = (const u32 *)&cayman_mgcg_disable;
+               count = CAYMAN_MGCG_DISABLE_LENGTH;
+       }
+
+       btc_program_mgcg_hw_sequence(rdev, ps, count);
+}
+
+static void ni_ls_clockgating_default(struct radeon_device *rdev)
+{
+       u32 count;
+       const u32 *ps = NULL;
+
+       ps = (const u32 *)&cayman_sysls_default;
+       count = CAYMAN_SYSLS_DEFAULT_LENGTH;
+
+       btc_program_mgcg_hw_sequence(rdev, ps, count);
+}
+
+static void ni_ls_clockgating_enable(struct radeon_device *rdev,
+                                    bool enable)
+{
+       u32 count;
+       const u32 *ps = NULL;
+
+       if (enable) {
+               ps = (const u32 *)&cayman_sysls_enable;
+               count = CAYMAN_SYSLS_ENABLE_LENGTH;
+       } else {
+               ps = (const u32 *)&cayman_sysls_disable;
+               count = CAYMAN_SYSLS_DISABLE_LENGTH;
+       }
+
+       btc_program_mgcg_hw_sequence(rdev, ps, count);
+
+}
+
+static int ni_patch_single_dependency_table_based_on_leakage(struct radeon_device *rdev,
+                                                            struct radeon_clock_voltage_dependency_table *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 i;
+
+       if (table) {
+               for (i = 0; i < table->count; i++) {
+                       if (0xff01 == table->entries[i].v) {
+                               if (pi->max_vddc == 0)
+                                       return -EINVAL;
+                               table->entries[i].v = pi->max_vddc;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int ni_patch_dependency_tables_based_on_leakage(struct radeon_device *rdev)
+{
+       int ret = 0;
+
+       ret = ni_patch_single_dependency_table_based_on_leakage(rdev,
+                                                               &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk);
+
+       ret = ni_patch_single_dependency_table_based_on_leakage(rdev,
+                                                               &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk);
+       return ret;
+}
+
+static void ni_stop_dpm(struct radeon_device *rdev)
+{
+       WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN);
+}
+
+#if 0
+static int ni_notify_hw_of_power_source(struct radeon_device *rdev,
+                                       bool ac_power)
+{
+       if (ac_power)
+               return (rv770_send_msg_to_smc(rdev, PPSMC_MSG_RunningOnAC) == PPSMC_Result_OK) ?
+                       0 : -EINVAL;
+
+       return 0;
+}
+#endif
+
+static PPSMC_Result ni_send_msg_to_smc_with_parameter(struct radeon_device *rdev,
+                                                     PPSMC_Msg msg, u32 parameter)
+{
+       WREG32(SMC_SCRATCH0, parameter);
+       return rv770_send_msg_to_smc(rdev, msg);
+}
+
+static int ni_restrict_performance_levels_before_switch(struct radeon_device *rdev)
+{
+       if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 1) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+int ni_dpm_force_performance_level(struct radeon_device *rdev,
+                                  enum radeon_dpm_forced_level level)
+{
+       struct radeon_ps *rps = rdev->pm.dpm.current_ps;
+       struct ni_ps *ps = ni_get_ps(rps);
+       u32 levels;
+
+       if (level == RADEON_DPM_FORCED_LEVEL_HIGH) {
+               if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK)
+                       return -EINVAL;
+
+               if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 1) != PPSMC_Result_OK)
+                       return -EINVAL;
+       } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) {
+               if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+                       return -EINVAL;
+
+               levels = ps->performance_level_count - 1;
+               if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, levels) != PPSMC_Result_OK)
+                       return -EINVAL;
+       } else if (level == RADEON_DPM_FORCED_LEVEL_AUTO) {
+               if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+                       return -EINVAL;
+
+               if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       rdev->pm.dpm.forced_level = level;
+
+       return 0;
+}
+
+static void ni_stop_smc(struct radeon_device *rdev)
+{
+       u32 tmp;
+       int i;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               tmp = RREG32(LB_SYNC_RESET_SEL) & LB_SYNC_RESET_SEL_MASK;
+               if (tmp != 1)
+                       break;
+               udelay(1);
+       }
+
+       udelay(100);
+
+       r7xx_stop_smc(rdev);
+}
+
+static int ni_process_firmware_header(struct radeon_device *rdev)
+{
+        struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+        struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+        struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = rv770_read_smc_sram_dword(rdev,
+                                       NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                       NISLANDS_SMC_FIRMWARE_HEADER_stateTable,
+                                       &tmp, pi->sram_end);
+
+       if (ret)
+               return ret;
+
+       pi->state_table_start = (u16)tmp;
+
+       ret = rv770_read_smc_sram_dword(rdev,
+                                       NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                       NISLANDS_SMC_FIRMWARE_HEADER_softRegisters,
+                                       &tmp, pi->sram_end);
+
+       if (ret)
+               return ret;
+
+       pi->soft_regs_start = (u16)tmp;
+
+       ret = rv770_read_smc_sram_dword(rdev,
+                                       NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                       NISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable,
+                                       &tmp, pi->sram_end);
+
+       if (ret)
+               return ret;
+
+       eg_pi->mc_reg_table_start = (u16)tmp;
+
+       ret = rv770_read_smc_sram_dword(rdev,
+                                       NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                       NISLANDS_SMC_FIRMWARE_HEADER_fanTable,
+                                       &tmp, pi->sram_end);
+
+       if (ret)
+               return ret;
+
+       ni_pi->fan_table_start = (u16)tmp;
+
+       ret = rv770_read_smc_sram_dword(rdev,
+                                       NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                       NISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable,
+                                       &tmp, pi->sram_end);
+
+       if (ret)
+               return ret;
+
+       ni_pi->arb_table_start = (u16)tmp;
+
+       ret = rv770_read_smc_sram_dword(rdev,
+                                       NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                       NISLANDS_SMC_FIRMWARE_HEADER_cacTable,
+                                       &tmp, pi->sram_end);
+
+       if (ret)
+               return ret;
+
+       ni_pi->cac_table_start = (u16)tmp;
+
+       ret = rv770_read_smc_sram_dword(rdev,
+                                       NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                       NISLANDS_SMC_FIRMWARE_HEADER_spllTable,
+                                       &tmp, pi->sram_end);
+
+       if (ret)
+               return ret;
+
+       ni_pi->spll_table_start = (u16)tmp;
+
+
+       return ret;
+}
+
+static void ni_read_clock_registers(struct radeon_device *rdev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+
+       ni_pi->clock_registers.cg_spll_func_cntl = RREG32(CG_SPLL_FUNC_CNTL);
+       ni_pi->clock_registers.cg_spll_func_cntl_2 = RREG32(CG_SPLL_FUNC_CNTL_2);
+       ni_pi->clock_registers.cg_spll_func_cntl_3 = RREG32(CG_SPLL_FUNC_CNTL_3);
+       ni_pi->clock_registers.cg_spll_func_cntl_4 = RREG32(CG_SPLL_FUNC_CNTL_4);
+       ni_pi->clock_registers.cg_spll_spread_spectrum = RREG32(CG_SPLL_SPREAD_SPECTRUM);
+       ni_pi->clock_registers.cg_spll_spread_spectrum_2 = RREG32(CG_SPLL_SPREAD_SPECTRUM_2);
+       ni_pi->clock_registers.mpll_ad_func_cntl = RREG32(MPLL_AD_FUNC_CNTL);
+       ni_pi->clock_registers.mpll_ad_func_cntl_2 = RREG32(MPLL_AD_FUNC_CNTL_2);
+       ni_pi->clock_registers.mpll_dq_func_cntl = RREG32(MPLL_DQ_FUNC_CNTL);
+       ni_pi->clock_registers.mpll_dq_func_cntl_2 = RREG32(MPLL_DQ_FUNC_CNTL_2);
+       ni_pi->clock_registers.mclk_pwrmgt_cntl = RREG32(MCLK_PWRMGT_CNTL);
+       ni_pi->clock_registers.dll_cntl = RREG32(DLL_CNTL);
+       ni_pi->clock_registers.mpll_ss1 = RREG32(MPLL_SS1);
+       ni_pi->clock_registers.mpll_ss2 = RREG32(MPLL_SS2);
+}
+
+#if 0
+static int ni_enter_ulp_state(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (pi->gfx_clock_gating) {
+                WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN);
+               WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON);
+                WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON);
+               RREG32(GB_ADDR_CONFIG);
+        }
+
+       WREG32_P(SMC_MSG, HOST_SMC_MSG(PPSMC_MSG_SwitchToMinimumPower),
+                 ~HOST_SMC_MSG_MASK);
+
+       udelay(25000);
+
+       return 0;
+}
+#endif
+
+static void ni_program_response_times(struct radeon_device *rdev)
+{
+       u32 voltage_response_time, backbias_response_time, acpi_delay_time, vbi_time_out;
+       u32 vddc_dly, bb_dly, acpi_dly, vbi_dly, mclk_switch_limit;
+       u32 reference_clock;
+
+       rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mvdd_chg_time, 1);
+
+       voltage_response_time = (u32)rdev->pm.dpm.voltage_response_time;
+       backbias_response_time = (u32)rdev->pm.dpm.backbias_response_time;
+
+       if (voltage_response_time == 0)
+               voltage_response_time = 1000;
+
+       if (backbias_response_time == 0)
+               backbias_response_time = 1000;
+
+       acpi_delay_time = 15000;
+       vbi_time_out = 100000;
+
+       reference_clock = radeon_get_xclk(rdev);
+
+       vddc_dly = (voltage_response_time  * reference_clock) / 1600;
+       bb_dly   = (backbias_response_time * reference_clock) / 1600;
+       acpi_dly = (acpi_delay_time * reference_clock) / 1600;
+       vbi_dly  = (vbi_time_out * reference_clock) / 1600;
+
+       mclk_switch_limit = (460 * reference_clock) / 100;
+
+       rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_delay_vreg,  vddc_dly);
+       rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_delay_bbias, bb_dly);
+       rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_delay_acpi,  acpi_dly);
+       rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mclk_chg_timeout, vbi_dly);
+       rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mc_block_delay, 0xAA);
+       rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mclk_switch_lim, mclk_switch_limit);
+}
+
+static void ni_populate_smc_voltage_table(struct radeon_device *rdev,
+                                         struct atom_voltage_table *voltage_table,
+                                         NISLANDS_SMC_STATETABLE *table)
+{
+       unsigned int i;
+
+       for (i = 0; i < voltage_table->count; i++) {
+               table->highSMIO[i] = 0;
+               table->lowSMIO[i] |= cpu_to_be32(voltage_table->entries[i].smio_low);
+       }
+}
+
+static void ni_populate_smc_voltage_tables(struct radeon_device *rdev,
+                                          NISLANDS_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       unsigned char i;
+
+       if (eg_pi->vddc_voltage_table.count) {
+               ni_populate_smc_voltage_table(rdev, &eg_pi->vddc_voltage_table, table);
+               table->voltageMaskTable.highMask[NISLANDS_SMC_VOLTAGEMASK_VDDC] = 0;
+               table->voltageMaskTable.lowMask[NISLANDS_SMC_VOLTAGEMASK_VDDC] =
+                       cpu_to_be32(eg_pi->vddc_voltage_table.mask_low);
+
+               for (i = 0; i < eg_pi->vddc_voltage_table.count; i++) {
+                       if (pi->max_vddc_in_table <= eg_pi->vddc_voltage_table.entries[i].value) {
+                               table->maxVDDCIndexInPPTable = i;
+                               break;
+                       }
+               }
+       }
+
+       if (eg_pi->vddci_voltage_table.count) {
+               ni_populate_smc_voltage_table(rdev, &eg_pi->vddci_voltage_table, table);
+
+               table->voltageMaskTable.highMask[NISLANDS_SMC_VOLTAGEMASK_VDDCI] = 0;
+               table->voltageMaskTable.lowMask[NISLANDS_SMC_VOLTAGEMASK_VDDCI] =
+                       cpu_to_be32(eg_pi->vddc_voltage_table.mask_low);
+       }
+}
+
+static int ni_populate_voltage_value(struct radeon_device *rdev,
+                                    struct atom_voltage_table *table,
+                                    u16 value,
+                                    NISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       unsigned int i;
+
+       for (i = 0; i < table->count; i++) {
+               if (value <= table->entries[i].value) {
+                       voltage->index = (u8)i;
+                       voltage->value = cpu_to_be16(table->entries[i].value);
+                       break;
+               }
+       }
+
+       if (i >= table->count)
+               return -EINVAL;
+
+       return 0;
+}
+
+static void ni_populate_mvdd_value(struct radeon_device *rdev,
+                                  u32 mclk,
+                                  NISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+        struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       if (!pi->mvdd_control) {
+               voltage->index = eg_pi->mvdd_high_index;
+                voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
+               return;
+       }
+
+       if (mclk <= pi->mvdd_split_frequency) {
+               voltage->index = eg_pi->mvdd_low_index;
+               voltage->value = cpu_to_be16(MVDD_LOW_VALUE);
+       } else {
+               voltage->index = eg_pi->mvdd_high_index;
+               voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
+       }
+}
+
+static int ni_get_std_voltage_value(struct radeon_device *rdev,
+                                   NISLANDS_SMC_VOLTAGE_VALUE *voltage,
+                                   u16 *std_voltage)
+{
+       if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries &&
+           ((u32)voltage->index < rdev->pm.dpm.dyn_state.cac_leakage_table.count))
+               *std_voltage = rdev->pm.dpm.dyn_state.cac_leakage_table.entries[voltage->index].vddc;
+       else
+               *std_voltage = be16_to_cpu(voltage->value);
+
+       return 0;
+}
+
+static void ni_populate_std_voltage_value(struct radeon_device *rdev,
+                                         u16 value, u8 index,
+                                         NISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       voltage->index = index;
+       voltage->value = cpu_to_be16(value);
+}
+
+static u32 ni_get_smc_power_scaling_factor(struct radeon_device *rdev)
+{
+       u32 xclk_period;
+       u32 xclk = radeon_get_xclk(rdev);
+       u32 tmp = RREG32(CG_CAC_CTRL) & TID_CNT_MASK;
+
+       xclk_period = (1000000000UL / xclk);
+       xclk_period /= 10000UL;
+
+       return tmp * xclk_period;
+}
+
+static u32 ni_scale_power_for_smc(u32 power_in_watts, u32 scaling_factor)
+{
+       return (power_in_watts * scaling_factor) << 2;
+}
+
+static u32 ni_calculate_power_boost_limit(struct radeon_device *rdev,
+                                         struct radeon_ps *radeon_state,
+                                         u32 near_tdp_limit)
+{
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       u32 power_boost_limit = 0;
+       int ret;
+
+       if (ni_pi->enable_power_containment &&
+           ni_pi->use_power_boost_limit) {
+               NISLANDS_SMC_VOLTAGE_VALUE vddc;
+               u16 std_vddc_med;
+               u16 std_vddc_high;
+               u64 tmp, n, d;
+
+               if (state->performance_level_count < 3)
+                       return 0;
+
+               ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                               state->performance_levels[state->performance_level_count - 2].vddc,
+                                               &vddc);
+               if (ret)
+                       return 0;
+
+               ret = ni_get_std_voltage_value(rdev, &vddc, &std_vddc_med);
+               if (ret)
+                       return 0;
+
+               ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                               state->performance_levels[state->performance_level_count - 1].vddc,
+                                               &vddc);
+               if (ret)
+                       return 0;
+
+               ret = ni_get_std_voltage_value(rdev, &vddc, &std_vddc_high);
+               if (ret)
+                       return 0;
+
+               n = ((u64)near_tdp_limit * ((u64)std_vddc_med * (u64)std_vddc_med) * 90);
+               d = ((u64)std_vddc_high * (u64)std_vddc_high * 100);
+               tmp = div64_u64(n, d);
+
+               if (tmp >> 32)
+                       return 0;
+               power_boost_limit = (u32)tmp;
+       }
+
+       return power_boost_limit;
+}
+
+static int ni_calculate_adjusted_tdp_limits(struct radeon_device *rdev,
+                                           bool adjust_polarity,
+                                           u32 tdp_adjustment,
+                                           u32 *tdp_limit,
+                                           u32 *near_tdp_limit)
+{
+       if (tdp_adjustment > (u32)rdev->pm.dpm.tdp_od_limit)
+               return -EINVAL;
+
+       if (adjust_polarity) {
+               *tdp_limit = ((100 + tdp_adjustment) * rdev->pm.dpm.tdp_limit) / 100;
+               *near_tdp_limit = rdev->pm.dpm.near_tdp_limit + (*tdp_limit - rdev->pm.dpm.tdp_limit);
+       } else {
+               *tdp_limit = ((100 - tdp_adjustment) * rdev->pm.dpm.tdp_limit) / 100;
+               *near_tdp_limit = rdev->pm.dpm.near_tdp_limit - (rdev->pm.dpm.tdp_limit - *tdp_limit);
+       }
+
+       return 0;
+}
+
+static int ni_populate_smc_tdp_limits(struct radeon_device *rdev,
+                                     struct radeon_ps *radeon_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+
+       if (ni_pi->enable_power_containment) {
+               NISLANDS_SMC_STATETABLE *smc_table = &ni_pi->smc_statetable;
+               u32 scaling_factor = ni_get_smc_power_scaling_factor(rdev);
+               u32 tdp_limit;
+               u32 near_tdp_limit;
+               u32 power_boost_limit;
+               int ret;
+
+               if (scaling_factor == 0)
+                       return -EINVAL;
+
+               memset(smc_table, 0, sizeof(NISLANDS_SMC_STATETABLE));
+
+               ret = ni_calculate_adjusted_tdp_limits(rdev,
+                                                      false, /* ??? */
+                                                      rdev->pm.dpm.tdp_adjustment,
+                                                      &tdp_limit,
+                                                      &near_tdp_limit);
+               if (ret)
+                       return ret;
+
+               power_boost_limit = ni_calculate_power_boost_limit(rdev, radeon_state,
+                                                                  near_tdp_limit);
+
+               smc_table->dpm2Params.TDPLimit =
+                       cpu_to_be32(ni_scale_power_for_smc(tdp_limit, scaling_factor));
+               smc_table->dpm2Params.NearTDPLimit =
+                       cpu_to_be32(ni_scale_power_for_smc(near_tdp_limit, scaling_factor));
+               smc_table->dpm2Params.SafePowerLimit =
+                       cpu_to_be32(ni_scale_power_for_smc((near_tdp_limit * NISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100,
+                                                          scaling_factor));
+               smc_table->dpm2Params.PowerBoostLimit =
+                       cpu_to_be32(ni_scale_power_for_smc(power_boost_limit, scaling_factor));
+
+               ret = rv770_copy_bytes_to_smc(rdev,
+                                             (u16)(pi->state_table_start + offsetof(NISLANDS_SMC_STATETABLE, dpm2Params) +
+                                                   offsetof(PP_NIslands_DPM2Parameters, TDPLimit)),
+                                             (u8 *)(&smc_table->dpm2Params.TDPLimit),
+                                             sizeof(u32) * 4, pi->sram_end);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int ni_copy_and_switch_arb_sets(struct radeon_device *rdev,
+                               u32 arb_freq_src, u32 arb_freq_dest)
+{
+       u32 mc_arb_dram_timing;
+       u32 mc_arb_dram_timing2;
+       u32 burst_time;
+       u32 mc_cg_config;
+
+       switch (arb_freq_src) {
+        case MC_CG_ARB_FREQ_F0:
+               mc_arb_dram_timing  = RREG32(MC_ARB_DRAM_TIMING);
+               mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+               burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE0_MASK) >> STATE0_SHIFT;
+               break;
+        case MC_CG_ARB_FREQ_F1:
+               mc_arb_dram_timing  = RREG32(MC_ARB_DRAM_TIMING_1);
+               mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_1);
+               burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE1_MASK) >> STATE1_SHIFT;
+               break;
+        case MC_CG_ARB_FREQ_F2:
+               mc_arb_dram_timing  = RREG32(MC_ARB_DRAM_TIMING_2);
+               mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_2);
+               burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE2_MASK) >> STATE2_SHIFT;
+               break;
+        case MC_CG_ARB_FREQ_F3:
+               mc_arb_dram_timing  = RREG32(MC_ARB_DRAM_TIMING_3);
+               mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_3);
+               burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE3_MASK) >> STATE3_SHIFT;
+               break;
+        default:
+               return -EINVAL;
+       }
+
+       switch (arb_freq_dest) {
+        case MC_CG_ARB_FREQ_F0:
+               WREG32(MC_ARB_DRAM_TIMING, mc_arb_dram_timing);
+               WREG32(MC_ARB_DRAM_TIMING2, mc_arb_dram_timing2);
+               WREG32_P(MC_ARB_BURST_TIME, STATE0(burst_time), ~STATE0_MASK);
+               break;
+        case MC_CG_ARB_FREQ_F1:
+               WREG32(MC_ARB_DRAM_TIMING_1, mc_arb_dram_timing);
+               WREG32(MC_ARB_DRAM_TIMING2_1, mc_arb_dram_timing2);
+               WREG32_P(MC_ARB_BURST_TIME, STATE1(burst_time), ~STATE1_MASK);
+               break;
+        case MC_CG_ARB_FREQ_F2:
+               WREG32(MC_ARB_DRAM_TIMING_2, mc_arb_dram_timing);
+               WREG32(MC_ARB_DRAM_TIMING2_2, mc_arb_dram_timing2);
+               WREG32_P(MC_ARB_BURST_TIME, STATE2(burst_time), ~STATE2_MASK);
+               break;
+        case MC_CG_ARB_FREQ_F3:
+               WREG32(MC_ARB_DRAM_TIMING_3, mc_arb_dram_timing);
+               WREG32(MC_ARB_DRAM_TIMING2_3, mc_arb_dram_timing2);
+               WREG32_P(MC_ARB_BURST_TIME, STATE3(burst_time), ~STATE3_MASK);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       mc_cg_config = RREG32(MC_CG_CONFIG) | 0x0000000F;
+       WREG32(MC_CG_CONFIG, mc_cg_config);
+       WREG32_P(MC_ARB_CG, CG_ARB_REQ(arb_freq_dest), ~CG_ARB_REQ_MASK);
+
+       return 0;
+}
+
+static int ni_init_arb_table_index(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = rv770_read_smc_sram_dword(rdev, ni_pi->arb_table_start,
+                                       &tmp, pi->sram_end);
+       if (ret)
+               return ret;
+
+       tmp &= 0x00FFFFFF;
+       tmp |= ((u32)MC_CG_ARB_FREQ_F1) << 24;
+
+       return rv770_write_smc_sram_dword(rdev, ni_pi->arb_table_start,
+                                         tmp, pi->sram_end);
+}
+
+static int ni_initial_switch_from_arb_f0_to_f1(struct radeon_device *rdev)
+{
+       return ni_copy_and_switch_arb_sets(rdev, MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
+}
+
+static int ni_force_switch_to_arb_f0(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = rv770_read_smc_sram_dword(rdev, ni_pi->arb_table_start,
+                                       &tmp, pi->sram_end);
+       if (ret)
+               return ret;
+
+       tmp = (tmp >> 24) & 0xff;
+
+       if (tmp == MC_CG_ARB_FREQ_F0)
+               return 0;
+
+       return ni_copy_and_switch_arb_sets(rdev, tmp, MC_CG_ARB_FREQ_F0);
+}
+
+static int ni_populate_memory_timing_parameters(struct radeon_device *rdev,
+                                               struct rv7xx_pl *pl,
+                                               SMC_NIslands_MCArbDramTimingRegisterSet *arb_regs)
+{
+       u32 dram_timing;
+       u32 dram_timing2;
+
+       arb_regs->mc_arb_rfsh_rate =
+               (u8)rv770_calculate_memory_refresh_rate(rdev, pl->sclk);
+
+
+       radeon_atom_set_engine_dram_timings(rdev,
+                                            pl->sclk,
+                                            pl->mclk);
+
+       dram_timing = RREG32(MC_ARB_DRAM_TIMING);
+       dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+
+       arb_regs->mc_arb_dram_timing  = cpu_to_be32(dram_timing);
+       arb_regs->mc_arb_dram_timing2 = cpu_to_be32(dram_timing2);
+
+       return 0;
+}
+
+static int ni_do_program_memory_timing_parameters(struct radeon_device *rdev,
+                                                 struct radeon_ps *radeon_state,
+                                                 unsigned int first_arb_set)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       SMC_NIslands_MCArbDramTimingRegisterSet arb_regs = { 0 };
+       int i, ret = 0;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               ret = ni_populate_memory_timing_parameters(rdev, &state->performance_levels[i], &arb_regs);
+               if (ret)
+                       break;
+
+               ret = rv770_copy_bytes_to_smc(rdev,
+                                             (u16)(ni_pi->arb_table_start +
+                                                   offsetof(SMC_NIslands_MCArbDramTimingRegisters, data) +
+                                                   sizeof(SMC_NIslands_MCArbDramTimingRegisterSet) * (first_arb_set + i)),
+                                             (u8 *)&arb_regs,
+                                             (u16)sizeof(SMC_NIslands_MCArbDramTimingRegisterSet),
+                                             pi->sram_end);
+               if (ret)
+                       break;
+       }
+       return ret;
+}
+
+static int ni_program_memory_timing_parameters(struct radeon_device *rdev,
+                                              struct radeon_ps *radeon_new_state)
+{
+       return ni_do_program_memory_timing_parameters(rdev, radeon_new_state,
+                                                     NISLANDS_DRIVER_STATE_ARB_INDEX);
+}
+
+static void ni_populate_initial_mvdd_value(struct radeon_device *rdev,
+                                          struct NISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       voltage->index = eg_pi->mvdd_high_index;
+       voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
+}
+
+static int ni_populate_smc_initial_state(struct radeon_device *rdev,
+                                        struct radeon_ps *radeon_initial_state,
+                                        NISLANDS_SMC_STATETABLE *table)
+{
+       struct ni_ps *initial_state = ni_get_ps(radeon_initial_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       u32 reg;
+       int ret;
+
+       table->initialState.levels[0].mclk.vMPLL_AD_FUNC_CNTL =
+               cpu_to_be32(ni_pi->clock_registers.mpll_ad_func_cntl);
+       table->initialState.levels[0].mclk.vMPLL_AD_FUNC_CNTL_2 =
+               cpu_to_be32(ni_pi->clock_registers.mpll_ad_func_cntl_2);
+       table->initialState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL =
+               cpu_to_be32(ni_pi->clock_registers.mpll_dq_func_cntl);
+       table->initialState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL_2 =
+               cpu_to_be32(ni_pi->clock_registers.mpll_dq_func_cntl_2);
+       table->initialState.levels[0].mclk.vMCLK_PWRMGT_CNTL =
+               cpu_to_be32(ni_pi->clock_registers.mclk_pwrmgt_cntl);
+       table->initialState.levels[0].mclk.vDLL_CNTL =
+               cpu_to_be32(ni_pi->clock_registers.dll_cntl);
+       table->initialState.levels[0].mclk.vMPLL_SS =
+               cpu_to_be32(ni_pi->clock_registers.mpll_ss1);
+       table->initialState.levels[0].mclk.vMPLL_SS2 =
+               cpu_to_be32(ni_pi->clock_registers.mpll_ss2);
+       table->initialState.levels[0].mclk.mclk_value =
+               cpu_to_be32(initial_state->performance_levels[0].mclk);
+
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
+               cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+               cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl_2);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+               cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl_3);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 =
+               cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl_4);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM =
+               cpu_to_be32(ni_pi->clock_registers.cg_spll_spread_spectrum);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2 =
+               cpu_to_be32(ni_pi->clock_registers.cg_spll_spread_spectrum_2);
+       table->initialState.levels[0].sclk.sclk_value =
+               cpu_to_be32(initial_state->performance_levels[0].sclk);
+       table->initialState.levels[0].arbRefreshState =
+               NISLANDS_INITIAL_STATE_ARB_INDEX;
+
+       table->initialState.levels[0].ACIndex = 0;
+
+       ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                       initial_state->performance_levels[0].vddc,
+                                       &table->initialState.levels[0].vddc);
+       if (!ret) {
+               u16 std_vddc;
+
+               ret = ni_get_std_voltage_value(rdev,
+                                              &table->initialState.levels[0].vddc,
+                                              &std_vddc);
+               if (!ret)
+                       ni_populate_std_voltage_value(rdev, std_vddc,
+                                                     table->initialState.levels[0].vddc.index,
+                                                     &table->initialState.levels[0].std_vddc);
+       }
+
+       if (eg_pi->vddci_control)
+               ni_populate_voltage_value(rdev,
+                                         &eg_pi->vddci_voltage_table,
+                                         initial_state->performance_levels[0].vddci,
+                                         &table->initialState.levels[0].vddci);
+
+       ni_populate_initial_mvdd_value(rdev, &table->initialState.levels[0].mvdd);
+
+       reg = CG_R(0xffff) | CG_L(0);
+       table->initialState.levels[0].aT = cpu_to_be32(reg);
+
+       table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp);
+
+       if (pi->boot_in_gen2)
+               table->initialState.levels[0].gen2PCIE = 1;
+       else
+               table->initialState.levels[0].gen2PCIE = 0;
+
+       if (pi->mem_gddr5) {
+               table->initialState.levels[0].strobeMode =
+                       cypress_get_strobe_mode_settings(rdev,
+                                                        initial_state->performance_levels[0].mclk);
+
+               if (initial_state->performance_levels[0].mclk > pi->mclk_edc_enable_threshold)
+                       table->initialState.levels[0].mcFlags = NISLANDS_SMC_MC_EDC_RD_FLAG | NISLANDS_SMC_MC_EDC_WR_FLAG;
+               else
+                       table->initialState.levels[0].mcFlags =  0;
+       }
+
+       table->initialState.levelCount = 1;
+
+       table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       table->initialState.levels[0].dpm2.MaxPS = 0;
+       table->initialState.levels[0].dpm2.NearTDPDec = 0;
+       table->initialState.levels[0].dpm2.AboveSafeInc = 0;
+       table->initialState.levels[0].dpm2.BelowSafeInc = 0;
+
+       reg = MIN_POWER_MASK | MAX_POWER_MASK;
+       table->initialState.levels[0].SQPowerThrottle = cpu_to_be32(reg);
+
+       reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+       table->initialState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg);
+
+       return 0;
+}
+
+static int ni_populate_smc_acpi_state(struct radeon_device *rdev,
+                                     NISLANDS_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       u32 mpll_ad_func_cntl   = ni_pi->clock_registers.mpll_ad_func_cntl;
+       u32 mpll_ad_func_cntl_2 = ni_pi->clock_registers.mpll_ad_func_cntl_2;
+       u32 mpll_dq_func_cntl   = ni_pi->clock_registers.mpll_dq_func_cntl;
+       u32 mpll_dq_func_cntl_2 = ni_pi->clock_registers.mpll_dq_func_cntl_2;
+       u32 spll_func_cntl      = ni_pi->clock_registers.cg_spll_func_cntl;
+       u32 spll_func_cntl_2    = ni_pi->clock_registers.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3    = ni_pi->clock_registers.cg_spll_func_cntl_3;
+       u32 spll_func_cntl_4    = ni_pi->clock_registers.cg_spll_func_cntl_4;
+       u32 mclk_pwrmgt_cntl    = ni_pi->clock_registers.mclk_pwrmgt_cntl;
+       u32 dll_cntl            = ni_pi->clock_registers.dll_cntl;
+       u32 reg;
+       int ret;
+
+       table->ACPIState = table->initialState;
+
+       table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       if (pi->acpi_vddc) {
+               ret = ni_populate_voltage_value(rdev,
+                                               &eg_pi->vddc_voltage_table,
+                                               pi->acpi_vddc, &table->ACPIState.levels[0].vddc);
+               if (!ret) {
+                       u16 std_vddc;
+
+                       ret = ni_get_std_voltage_value(rdev,
+                                                      &table->ACPIState.levels[0].vddc, &std_vddc);
+                       if (!ret)
+                               ni_populate_std_voltage_value(rdev, std_vddc,
+                                                             table->ACPIState.levels[0].vddc.index,
+                                                             &table->ACPIState.levels[0].std_vddc);
+               }
+
+               if (pi->pcie_gen2) {
+                       if (pi->acpi_pcie_gen2)
+                               table->ACPIState.levels[0].gen2PCIE = 1;
+                       else
+                               table->ACPIState.levels[0].gen2PCIE = 0;
+               } else {
+                       table->ACPIState.levels[0].gen2PCIE = 0;
+               }
+       } else {
+               ret = ni_populate_voltage_value(rdev,
+                                               &eg_pi->vddc_voltage_table,
+                                               pi->min_vddc_in_table,
+                                               &table->ACPIState.levels[0].vddc);
+               if (!ret) {
+                       u16 std_vddc;
+
+                       ret = ni_get_std_voltage_value(rdev,
+                                                      &table->ACPIState.levels[0].vddc,
+                                                      &std_vddc);
+                       if (!ret)
+                               ni_populate_std_voltage_value(rdev, std_vddc,
+                                                             table->ACPIState.levels[0].vddc.index,
+                                                             &table->ACPIState.levels[0].std_vddc);
+               }
+               table->ACPIState.levels[0].gen2PCIE = 0;
+       }
+
+       if (eg_pi->acpi_vddci) {
+               if (eg_pi->vddci_control)
+                       ni_populate_voltage_value(rdev,
+                                                 &eg_pi->vddci_voltage_table,
+                                                 eg_pi->acpi_vddci,
+                                                 &table->ACPIState.levels[0].vddci);
+       }
+
+
+       mpll_ad_func_cntl &= ~PDNB;
+
+       mpll_ad_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN;
+
+        if (pi->mem_gddr5)
+                mpll_dq_func_cntl &= ~PDNB;
+        mpll_dq_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN | BYPASS;
+
+
+       mclk_pwrmgt_cntl |= (MRDCKA0_RESET |
+                            MRDCKA1_RESET |
+                            MRDCKB0_RESET |
+                            MRDCKB1_RESET |
+                            MRDCKC0_RESET |
+                            MRDCKC1_RESET |
+                            MRDCKD0_RESET |
+                            MRDCKD1_RESET);
+
+       mclk_pwrmgt_cntl &= ~(MRDCKA0_PDNB |
+                             MRDCKA1_PDNB |
+                             MRDCKB0_PDNB |
+                             MRDCKB1_PDNB |
+                             MRDCKC0_PDNB |
+                             MRDCKC1_PDNB |
+                             MRDCKD0_PDNB |
+                             MRDCKD1_PDNB);
+
+       dll_cntl |= (MRDCKA0_BYPASS |
+                     MRDCKA1_BYPASS |
+                     MRDCKB0_BYPASS |
+                     MRDCKB1_BYPASS |
+                     MRDCKC0_BYPASS |
+                     MRDCKC1_BYPASS |
+                     MRDCKD0_BYPASS |
+                     MRDCKD1_BYPASS);
+
+        spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(4);
+
+       table->ACPIState.levels[0].mclk.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2);
+       table->ACPIState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2);
+       table->ACPIState.levels[0].mclk.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+       table->ACPIState.levels[0].mclk.vDLL_CNTL = cpu_to_be32(dll_cntl);
+
+       table->ACPIState.levels[0].mclk.mclk_value = 0;
+
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 = cpu_to_be32(spll_func_cntl_4);
+
+       table->ACPIState.levels[0].sclk.sclk_value = 0;
+
+       ni_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd);
+
+       if (eg_pi->dynamic_ac_timing)
+               table->ACPIState.levels[0].ACIndex = 1;
+
+       table->ACPIState.levels[0].dpm2.MaxPS = 0;
+       table->ACPIState.levels[0].dpm2.NearTDPDec = 0;
+       table->ACPIState.levels[0].dpm2.AboveSafeInc = 0;
+       table->ACPIState.levels[0].dpm2.BelowSafeInc = 0;
+
+       reg = MIN_POWER_MASK | MAX_POWER_MASK;
+       table->ACPIState.levels[0].SQPowerThrottle = cpu_to_be32(reg);
+
+       reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+       table->ACPIState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg);
+
+       return 0;
+}
+
+static int ni_init_smc_table(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       int ret;
+       struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps;
+       NISLANDS_SMC_STATETABLE *table = &ni_pi->smc_statetable;
+
+       memset(table, 0, sizeof(NISLANDS_SMC_STATETABLE));
+
+       ni_populate_smc_voltage_tables(rdev, table);
+
+       switch (rdev->pm.int_thermal_type) {
+       case THERMAL_TYPE_NI:
+       case THERMAL_TYPE_EMC2103_WITH_INTERNAL:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL;
+               break;
+       case THERMAL_TYPE_NONE:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE;
+               break;
+       default:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL;
+               break;
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (pi->mem_gddr5)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+       ret = ni_populate_smc_initial_state(rdev, radeon_boot_state, table);
+       if (ret)
+               return ret;
+
+       ret = ni_populate_smc_acpi_state(rdev, table);
+       if (ret)
+               return ret;
+
+       table->driverState = table->initialState;
+
+       table->ULVState = table->initialState;
+
+       ret = ni_do_program_memory_timing_parameters(rdev, radeon_boot_state,
+                                                    NISLANDS_INITIAL_STATE_ARB_INDEX);
+       if (ret)
+               return ret;
+
+       return rv770_copy_bytes_to_smc(rdev, pi->state_table_start, (u8 *)table,
+                                      sizeof(NISLANDS_SMC_STATETABLE), pi->sram_end);
+}
+
+static int ni_calculate_sclk_params(struct radeon_device *rdev,
+                                   u32 engine_clock,
+                                   NISLANDS_SMC_SCLK_VALUE *sclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct atom_clock_dividers dividers;
+       u32 spll_func_cntl = ni_pi->clock_registers.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 = ni_pi->clock_registers.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 = ni_pi->clock_registers.cg_spll_func_cntl_3;
+       u32 spll_func_cntl_4 = ni_pi->clock_registers.cg_spll_func_cntl_4;
+       u32 cg_spll_spread_spectrum = ni_pi->clock_registers.cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2 = ni_pi->clock_registers.cg_spll_spread_spectrum_2;
+       u64 tmp;
+       u32 reference_clock = rdev->clock.spll.reference_freq;
+       u32 reference_divider;
+       u32 fbdiv;
+       int ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            engine_clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       reference_divider = 1 + dividers.ref_div;
+
+
+       tmp = (u64) engine_clock * reference_divider * dividers.post_div * 16834;
+       do_div(tmp, reference_clock);
+       fbdiv = (u32) tmp;
+
+       spll_func_cntl &= ~(SPLL_PDIV_A_MASK | SPLL_REF_DIV_MASK);
+       spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div);
+       spll_func_cntl |= SPLL_PDIV_A(dividers.post_div);
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(2);
+
+       spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK;
+       spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv);
+       spll_func_cntl_3 |= SPLL_DITHEN;
+
+       if (pi->sclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 vco_freq = engine_clock * dividers.post_div;
+
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
+                       u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
+                       u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000);
+
+                       cg_spll_spread_spectrum &= ~CLK_S_MASK;
+                       cg_spll_spread_spectrum |= CLK_S(clk_s);
+                       cg_spll_spread_spectrum |= SSEN;
+
+                       cg_spll_spread_spectrum_2 &= ~CLK_V_MASK;
+                       cg_spll_spread_spectrum_2 |= CLK_V(clk_v);
+               }
+       }
+
+       sclk->sclk_value = engine_clock;
+       sclk->vCG_SPLL_FUNC_CNTL = spll_func_cntl;
+       sclk->vCG_SPLL_FUNC_CNTL_2 = spll_func_cntl_2;
+       sclk->vCG_SPLL_FUNC_CNTL_3 = spll_func_cntl_3;
+       sclk->vCG_SPLL_FUNC_CNTL_4 = spll_func_cntl_4;
+       sclk->vCG_SPLL_SPREAD_SPECTRUM = cg_spll_spread_spectrum;
+       sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cg_spll_spread_spectrum_2;
+
+       return 0;
+}
+
+static int ni_populate_sclk_value(struct radeon_device *rdev,
+                                 u32 engine_clock,
+                                 NISLANDS_SMC_SCLK_VALUE *sclk)
+{
+       NISLANDS_SMC_SCLK_VALUE sclk_tmp;
+       int ret;
+
+       ret = ni_calculate_sclk_params(rdev, engine_clock, &sclk_tmp);
+       if (!ret) {
+               sclk->sclk_value = cpu_to_be32(sclk_tmp.sclk_value);
+               sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL);
+               sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_2);
+               sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_3);
+               sclk->vCG_SPLL_FUNC_CNTL_4 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_4);
+               sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM);
+               sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM_2);
+       }
+
+       return ret;
+}
+
+static int ni_init_smc_spll_table(struct radeon_device *rdev)
+{
+        struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       SMC_NISLANDS_SPLL_DIV_TABLE *spll_table;
+       NISLANDS_SMC_SCLK_VALUE sclk_params;
+       u32 fb_div;
+       u32 p_div;
+       u32 clk_s;
+       u32 clk_v;
+       u32 sclk = 0;
+       int i, ret;
+       u32 tmp;
+
+       if (ni_pi->spll_table_start == 0)
+               return -EINVAL;
+
+       spll_table = kzalloc(sizeof(SMC_NISLANDS_SPLL_DIV_TABLE), GFP_KERNEL);
+       if (spll_table == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < 256; i++) {
+               ret = ni_calculate_sclk_params(rdev, sclk, &sclk_params);
+               if (ret)
+                       break;
+
+               p_div = (sclk_params.vCG_SPLL_FUNC_CNTL & SPLL_PDIV_A_MASK) >> SPLL_PDIV_A_SHIFT;
+               fb_div = (sclk_params.vCG_SPLL_FUNC_CNTL_3 & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT;
+               clk_s = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM & CLK_S_MASK) >> CLK_S_SHIFT;
+               clk_v = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM_2 & CLK_V_MASK) >> CLK_V_SHIFT;
+
+               fb_div &= ~0x00001FFF;
+               fb_div >>= 1;
+               clk_v >>= 6;
+
+               if (p_div & ~(SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT))
+                       ret = -EINVAL;
+
+               if (clk_s & ~(SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT))
+                       ret = -EINVAL;
+
+               if (clk_s & ~(SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT))
+                       ret = -EINVAL;
+
+               if (clk_v & ~(SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT))
+                       ret = -EINVAL;
+
+               if (ret)
+                       break;
+
+               tmp = ((fb_div << SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_MASK) |
+                       ((p_div << SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_MASK);
+               spll_table->freq[i] = cpu_to_be32(tmp);
+
+               tmp = ((clk_v << SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_MASK) |
+                       ((clk_s << SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK);
+               spll_table->ss[i] = cpu_to_be32(tmp);
+
+               sclk += 512;
+       }
+
+       if (!ret)
+               ret = rv770_copy_bytes_to_smc(rdev, ni_pi->spll_table_start, (u8 *)spll_table,
+                                             sizeof(SMC_NISLANDS_SPLL_DIV_TABLE), pi->sram_end);
+
+       kfree(spll_table);
+
+       return ret;
+}
+
+static int ni_populate_mclk_value(struct radeon_device *rdev,
+                                 u32 engine_clock,
+                                 u32 memory_clock,
+                                 NISLANDS_SMC_MCLK_VALUE *mclk,
+                                 bool strobe_mode,
+                                 bool dll_state_on)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       u32 mpll_ad_func_cntl = ni_pi->clock_registers.mpll_ad_func_cntl;
+       u32 mpll_ad_func_cntl_2 = ni_pi->clock_registers.mpll_ad_func_cntl_2;
+       u32 mpll_dq_func_cntl = ni_pi->clock_registers.mpll_dq_func_cntl;
+       u32 mpll_dq_func_cntl_2 = ni_pi->clock_registers.mpll_dq_func_cntl_2;
+       u32 mclk_pwrmgt_cntl = ni_pi->clock_registers.mclk_pwrmgt_cntl;
+       u32 dll_cntl = ni_pi->clock_registers.dll_cntl;
+       u32 mpll_ss1 = ni_pi->clock_registers.mpll_ss1;
+       u32 mpll_ss2 = ni_pi->clock_registers.mpll_ss2;
+       struct atom_clock_dividers dividers;
+       u32 ibias;
+       u32 dll_speed;
+       int ret;
+       u32 mc_seq_misc7;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM,
+                                            memory_clock, strobe_mode, &dividers);
+       if (ret)
+               return ret;
+
+       if (!strobe_mode) {
+               mc_seq_misc7 = RREG32(MC_SEQ_MISC7);
+
+               if (mc_seq_misc7 & 0x8000000)
+                       dividers.post_div = 1;
+       }
+
+       ibias = cypress_map_clkf_to_ibias(rdev, dividers.whole_fb_div);
+
+       mpll_ad_func_cntl &= ~(CLKR_MASK |
+                              YCLK_POST_DIV_MASK |
+                              CLKF_MASK |
+                              CLKFRAC_MASK |
+                              IBIAS_MASK);
+       mpll_ad_func_cntl |= CLKR(dividers.ref_div);
+       mpll_ad_func_cntl |= YCLK_POST_DIV(dividers.post_div);
+       mpll_ad_func_cntl |= CLKF(dividers.whole_fb_div);
+       mpll_ad_func_cntl |= CLKFRAC(dividers.frac_fb_div);
+       mpll_ad_func_cntl |= IBIAS(ibias);
+
+       if (dividers.vco_mode)
+               mpll_ad_func_cntl_2 |= VCO_MODE;
+       else
+               mpll_ad_func_cntl_2 &= ~VCO_MODE;
+
+       if (pi->mem_gddr5) {
+               mpll_dq_func_cntl &= ~(CLKR_MASK |
+                                      YCLK_POST_DIV_MASK |
+                                      CLKF_MASK |
+                                      CLKFRAC_MASK |
+                                      IBIAS_MASK);
+               mpll_dq_func_cntl |= CLKR(dividers.ref_div);
+               mpll_dq_func_cntl |= YCLK_POST_DIV(dividers.post_div);
+               mpll_dq_func_cntl |= CLKF(dividers.whole_fb_div);
+               mpll_dq_func_cntl |= CLKFRAC(dividers.frac_fb_div);
+               mpll_dq_func_cntl |= IBIAS(ibias);
+
+               if (strobe_mode)
+                       mpll_dq_func_cntl &= ~PDNB;
+               else
+                       mpll_dq_func_cntl |= PDNB;
+
+               if (dividers.vco_mode)
+                       mpll_dq_func_cntl_2 |= VCO_MODE;
+               else
+                       mpll_dq_func_cntl_2 &= ~VCO_MODE;
+       }
+
+       if (pi->mclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 vco_freq = memory_clock * dividers.post_div;
+
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_MEMORY_SS, vco_freq)) {
+                       u32 reference_clock = rdev->clock.mpll.reference_freq;
+                       u32 decoded_ref = rv740_get_decoded_reference_divider(dividers.ref_div);
+                       u32 clk_s = reference_clock * 5 / (decoded_ref * ss.rate);
+                       u32 clk_v = ss.percentage *
+                               (0x4000 * dividers.whole_fb_div + 0x800 * dividers.frac_fb_div) / (clk_s * 625);
+
+                       mpll_ss1 &= ~CLKV_MASK;
+                       mpll_ss1 |= CLKV(clk_v);
+
+                       mpll_ss2 &= ~CLKS_MASK;
+                       mpll_ss2 |= CLKS(clk_s);
+               }
+       }
+
+       dll_speed = rv740_get_dll_speed(pi->mem_gddr5,
+                                       memory_clock);
+
+       mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK;
+       mclk_pwrmgt_cntl |= DLL_SPEED(dll_speed);
+       if (dll_state_on)
+               mclk_pwrmgt_cntl |= (MRDCKA0_PDNB |
+                                    MRDCKA1_PDNB |
+                                    MRDCKB0_PDNB |
+                                    MRDCKB1_PDNB |
+                                    MRDCKC0_PDNB |
+                                    MRDCKC1_PDNB |
+                                    MRDCKD0_PDNB |
+                                    MRDCKD1_PDNB);
+       else
+               mclk_pwrmgt_cntl &= ~(MRDCKA0_PDNB |
+                                     MRDCKA1_PDNB |
+                                     MRDCKB0_PDNB |
+                                     MRDCKB1_PDNB |
+                                     MRDCKC0_PDNB |
+                                     MRDCKC1_PDNB |
+                                     MRDCKD0_PDNB |
+                                     MRDCKD1_PDNB);
+
+
+       mclk->mclk_value = cpu_to_be32(memory_clock);
+       mclk->vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
+       mclk->vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2);
+       mclk->vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
+       mclk->vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2);
+       mclk->vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+       mclk->vDLL_CNTL = cpu_to_be32(dll_cntl);
+       mclk->vMPLL_SS = cpu_to_be32(mpll_ss1);
+       mclk->vMPLL_SS2 = cpu_to_be32(mpll_ss2);
+
+       return 0;
+}
+
+static void ni_populate_smc_sp(struct radeon_device *rdev,
+                              struct radeon_ps *radeon_state,
+                              NISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct ni_ps *ps = ni_get_ps(radeon_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       int i;
+
+       for (i = 0; i < ps->performance_level_count - 1; i++)
+               smc_state->levels[i].bSP = cpu_to_be32(pi->dsp);
+
+       smc_state->levels[ps->performance_level_count - 1].bSP =
+               cpu_to_be32(pi->psp);
+}
+
+static int ni_convert_power_level_to_smc(struct radeon_device *rdev,
+                                        struct rv7xx_pl *pl,
+                                        NISLANDS_SMC_HW_PERFORMANCE_LEVEL *level)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+        struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+        struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       int ret;
+       bool dll_state_on;
+       u16 std_vddc;
+       u32 tmp = RREG32(DC_STUTTER_CNTL);
+
+       level->gen2PCIE = pi->pcie_gen2 ?
+               ((pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? 1 : 0) : 0;
+
+       ret = ni_populate_sclk_value(rdev, pl->sclk, &level->sclk);
+       if (ret)
+               return ret;
+
+       level->mcFlags =  0;
+       if (pi->mclk_stutter_mode_threshold &&
+           (pl->mclk <= pi->mclk_stutter_mode_threshold) &&
+           !eg_pi->uvd_enabled &&
+           (tmp & DC_STUTTER_ENABLE_A) &&
+           (tmp & DC_STUTTER_ENABLE_B))
+               level->mcFlags |= NISLANDS_SMC_MC_STUTTER_EN;
+
+       if (pi->mem_gddr5) {
+               if (pl->mclk > pi->mclk_edc_enable_threshold)
+                       level->mcFlags |= NISLANDS_SMC_MC_EDC_RD_FLAG;
+               if (pl->mclk > eg_pi->mclk_edc_wr_enable_threshold)
+                       level->mcFlags |= NISLANDS_SMC_MC_EDC_WR_FLAG;
+
+               level->strobeMode = cypress_get_strobe_mode_settings(rdev, pl->mclk);
+
+               if (level->strobeMode & NISLANDS_SMC_STROBE_ENABLE) {
+                       if (cypress_get_mclk_frequency_ratio(rdev, pl->mclk, true) >=
+                           ((RREG32(MC_SEQ_MISC7) >> 16) & 0xf))
+                               dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
+                       else
+                               dll_state_on = ((RREG32(MC_SEQ_MISC6) >> 1) & 0x1) ? true : false;
+               } else {
+                       dll_state_on = false;
+                       if (pl->mclk > ni_pi->mclk_rtt_mode_threshold)
+                               level->mcFlags |= NISLANDS_SMC_MC_RTT_ENABLE;
+               }
+
+               ret = ni_populate_mclk_value(rdev, pl->sclk, pl->mclk,
+                                            &level->mclk,
+                                            (level->strobeMode & NISLANDS_SMC_STROBE_ENABLE) != 0,
+                                            dll_state_on);
+       } else
+               ret = ni_populate_mclk_value(rdev, pl->sclk, pl->mclk, &level->mclk, 1, 1);
+
+       if (ret)
+               return ret;
+
+       ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                       pl->vddc, &level->vddc);
+       if (ret)
+               return ret;
+
+       ret = ni_get_std_voltage_value(rdev, &level->vddc, &std_vddc);
+       if (ret)
+               return ret;
+
+       ni_populate_std_voltage_value(rdev, std_vddc,
+                                     level->vddc.index, &level->std_vddc);
+
+       if (eg_pi->vddci_control) {
+               ret = ni_populate_voltage_value(rdev, &eg_pi->vddci_voltage_table,
+                                               pl->vddci, &level->vddci);
+               if (ret)
+                       return ret;
+       }
+
+       ni_populate_mvdd_value(rdev, pl->mclk, &level->mvdd);
+
+       return ret;
+}
+
+static int ni_populate_smc_t(struct radeon_device *rdev,
+                            struct radeon_ps *radeon_state,
+                            NISLANDS_SMC_SWSTATE *smc_state)
+{
+        struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+        struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       u32 a_t;
+       u32 t_l, t_h;
+       u32 high_bsp;
+       int i, ret;
+
+       if (state->performance_level_count >= 9)
+               return -EINVAL;
+
+       if (state->performance_level_count < 2) {
+               a_t = CG_R(0xffff) | CG_L(0);
+               smc_state->levels[0].aT = cpu_to_be32(a_t);
+               return 0;
+       }
+
+       smc_state->levels[0].aT = cpu_to_be32(0);
+
+       for (i = 0; i <= state->performance_level_count - 2; i++) {
+               if (eg_pi->uvd_enabled)
+                       ret = r600_calculate_at(
+                               1000 * (i * (eg_pi->smu_uvd_hs ? 2 : 8) + 2),
+                               100 * R600_AH_DFLT,
+                               state->performance_levels[i + 1].sclk,
+                               state->performance_levels[i].sclk,
+                               &t_l,
+                               &t_h);
+               else
+                       ret = r600_calculate_at(
+                               1000 * (i + 1),
+                               100 * R600_AH_DFLT,
+                               state->performance_levels[i + 1].sclk,
+                               state->performance_levels[i].sclk,
+                               &t_l,
+                               &t_h);
+
+               if (ret) {
+                       t_h = (i + 1) * 1000 - 50 * R600_AH_DFLT;
+                       t_l = (i + 1) * 1000 + 50 * R600_AH_DFLT;
+               }
+
+               a_t = be32_to_cpu(smc_state->levels[i].aT) & ~CG_R_MASK;
+               a_t |= CG_R(t_l * pi->bsp / 20000);
+               smc_state->levels[i].aT = cpu_to_be32(a_t);
+
+               high_bsp = (i == state->performance_level_count - 2) ?
+                       pi->pbsp : pi->bsp;
+
+               a_t = CG_R(0xffff) | CG_L(t_h * high_bsp / 20000);
+               smc_state->levels[i + 1].aT = cpu_to_be32(a_t);
+       }
+
+       return 0;
+}
+
+static int ni_populate_power_containment_values(struct radeon_device *rdev,
+                                               struct radeon_ps *radeon_state,
+                                               NISLANDS_SMC_SWSTATE *smc_state)
+{
+        struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+        struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       u32 prev_sclk;
+       u32 max_sclk;
+       u32 min_sclk;
+       int i, ret;
+       u32 tdp_limit;
+       u32 near_tdp_limit;
+       u32 power_boost_limit;
+       u8 max_ps_percent;
+
+       if (ni_pi->enable_power_containment == false)
+               return 0;
+
+       if (state->performance_level_count == 0)
+               return -EINVAL;
+
+       if (smc_state->levelCount != state->performance_level_count)
+               return -EINVAL;
+
+       ret = ni_calculate_adjusted_tdp_limits(rdev,
+                                              false, /* ??? */
+                                              rdev->pm.dpm.tdp_adjustment,
+                                              &tdp_limit,
+                                              &near_tdp_limit);
+       if (ret)
+               return ret;
+
+       power_boost_limit = ni_calculate_power_boost_limit(rdev, radeon_state, near_tdp_limit);
+
+       ret = rv770_write_smc_sram_dword(rdev,
+                                        pi->state_table_start +
+                                        offsetof(NISLANDS_SMC_STATETABLE, dpm2Params) +
+                                        offsetof(PP_NIslands_DPM2Parameters, PowerBoostLimit),
+                                        ni_scale_power_for_smc(power_boost_limit, ni_get_smc_power_scaling_factor(rdev)),
+                                        pi->sram_end);
+       if (ret)
+               power_boost_limit = 0;
+
+       smc_state->levels[0].dpm2.MaxPS = 0;
+       smc_state->levels[0].dpm2.NearTDPDec = 0;
+       smc_state->levels[0].dpm2.AboveSafeInc = 0;
+       smc_state->levels[0].dpm2.BelowSafeInc = 0;
+       smc_state->levels[0].stateFlags |= power_boost_limit ? PPSMC_STATEFLAG_POWERBOOST : 0;
+
+       for (i = 1; i < state->performance_level_count; i++) {
+               prev_sclk = state->performance_levels[i-1].sclk;
+               max_sclk  = state->performance_levels[i].sclk;
+               max_ps_percent = (i != (state->performance_level_count - 1)) ?
+                       NISLANDS_DPM2_MAXPS_PERCENT_M : NISLANDS_DPM2_MAXPS_PERCENT_H;
+
+               if (max_sclk < prev_sclk)
+                       return -EINVAL;
+
+               if ((max_ps_percent == 0) || (prev_sclk == max_sclk) || eg_pi->uvd_enabled)
+                       min_sclk = max_sclk;
+               else if (1 == i)
+                       min_sclk = prev_sclk;
+               else
+                       min_sclk = (prev_sclk * (u32)max_ps_percent) / 100;
+
+               if (min_sclk < state->performance_levels[0].sclk)
+                       min_sclk = state->performance_levels[0].sclk;
+
+               if (min_sclk == 0)
+                       return -EINVAL;
+
+               smc_state->levels[i].dpm2.MaxPS =
+                       (u8)((NISLANDS_DPM2_MAX_PULSE_SKIP * (max_sclk - min_sclk)) / max_sclk);
+               smc_state->levels[i].dpm2.NearTDPDec = NISLANDS_DPM2_NEAR_TDP_DEC;
+               smc_state->levels[i].dpm2.AboveSafeInc = NISLANDS_DPM2_ABOVE_SAFE_INC;
+               smc_state->levels[i].dpm2.BelowSafeInc = NISLANDS_DPM2_BELOW_SAFE_INC;
+               smc_state->levels[i].stateFlags |=
+                       ((i != (state->performance_level_count - 1)) && power_boost_limit) ?
+                       PPSMC_STATEFLAG_POWERBOOST : 0;
+       }
+
+       return 0;
+}
+
+static int ni_populate_sq_ramping_values(struct radeon_device *rdev,
+                                        struct radeon_ps *radeon_state,
+                                        NISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       u32 sq_power_throttle;
+       u32 sq_power_throttle2;
+       bool enable_sq_ramping = ni_pi->enable_sq_ramping;
+       int i;
+
+       if (state->performance_level_count == 0)
+               return -EINVAL;
+
+       if (smc_state->levelCount != state->performance_level_count)
+               return -EINVAL;
+
+       if (rdev->pm.dpm.sq_ramping_threshold == 0)
+               return -EINVAL;
+
+       if (NISLANDS_DPM2_SQ_RAMP_MAX_POWER > (MAX_POWER_MASK >> MAX_POWER_SHIFT))
+               enable_sq_ramping = false;
+
+       if (NISLANDS_DPM2_SQ_RAMP_MIN_POWER > (MIN_POWER_MASK >> MIN_POWER_SHIFT))
+               enable_sq_ramping = false;
+
+       if (NISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA > (MAX_POWER_DELTA_MASK >> MAX_POWER_DELTA_SHIFT))
+               enable_sq_ramping = false;
+
+       if (NISLANDS_DPM2_SQ_RAMP_STI_SIZE > (STI_SIZE_MASK >> STI_SIZE_SHIFT))
+               enable_sq_ramping = false;
+
+       if (NISLANDS_DPM2_SQ_RAMP_LTI_RATIO <= (LTI_RATIO_MASK >> LTI_RATIO_SHIFT))
+               enable_sq_ramping = false;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               sq_power_throttle  = 0;
+               sq_power_throttle2 = 0;
+
+               if ((state->performance_levels[i].sclk >= rdev->pm.dpm.sq_ramping_threshold) &&
+                   enable_sq_ramping) {
+                       sq_power_throttle |= MAX_POWER(NISLANDS_DPM2_SQ_RAMP_MAX_POWER);
+                       sq_power_throttle |= MIN_POWER(NISLANDS_DPM2_SQ_RAMP_MIN_POWER);
+                       sq_power_throttle2 |= MAX_POWER_DELTA(NISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA);
+                       sq_power_throttle2 |= STI_SIZE(NISLANDS_DPM2_SQ_RAMP_STI_SIZE);
+                       sq_power_throttle2 |= LTI_RATIO(NISLANDS_DPM2_SQ_RAMP_LTI_RATIO);
+               } else {
+                       sq_power_throttle |= MAX_POWER_MASK | MIN_POWER_MASK;
+                       sq_power_throttle2 |= MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+               }
+
+               smc_state->levels[i].SQPowerThrottle   = cpu_to_be32(sq_power_throttle);
+               smc_state->levels[i].SQPowerThrottle_2 = cpu_to_be32(sq_power_throttle2);
+       }
+
+       return 0;
+}
+
+static int ni_enable_power_containment(struct radeon_device *rdev,
+                                      struct radeon_ps *radeon_new_state,
+                                      bool enable)
+{
+        struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       PPSMC_Result smc_result;
+       int ret = 0;
+
+       if (ni_pi->enable_power_containment) {
+               if (enable) {
+                       if (!r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) {
+                               smc_result = rv770_send_msg_to_smc(rdev, PPSMC_TDPClampingActive);
+                               if (smc_result != PPSMC_Result_OK) {
+                                       ret = -EINVAL;
+                                       ni_pi->pc_enabled = false;
+                               } else {
+                                       ni_pi->pc_enabled = true;
+                               }
+                       }
+               } else {
+                       smc_result = rv770_send_msg_to_smc(rdev, PPSMC_TDPClampingInactive);
+                       if (smc_result != PPSMC_Result_OK)
+                               ret = -EINVAL;
+                       ni_pi->pc_enabled = false;
+               }
+       }
+
+       return ret;
+}
+
+static int ni_convert_power_state_to_smc(struct radeon_device *rdev,
+                                        struct radeon_ps *radeon_state,
+                                        NISLANDS_SMC_SWSTATE *smc_state)
+{
+        struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       int i, ret;
+       u32 threshold = state->performance_levels[state->performance_level_count - 1].sclk * 100 / 100;
+
+       if (!(radeon_state->caps & ATOM_PPLIB_DISALLOW_ON_DC))
+               smc_state->flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       smc_state->levelCount = 0;
+
+       if (state->performance_level_count > NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE)
+               return -EINVAL;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               ret = ni_convert_power_level_to_smc(rdev, &state->performance_levels[i],
+                                                   &smc_state->levels[i]);
+               smc_state->levels[i].arbRefreshState =
+                       (u8)(NISLANDS_DRIVER_STATE_ARB_INDEX + i);
+
+               if (ret)
+                       return ret;
+
+               if (ni_pi->enable_power_containment)
+                       smc_state->levels[i].displayWatermark =
+                               (state->performance_levels[i].sclk < threshold) ?
+                               PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH;
+               else
+                       smc_state->levels[i].displayWatermark = (i < 2) ?
+                               PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH;
+
+               if (eg_pi->dynamic_ac_timing)
+                       smc_state->levels[i].ACIndex = NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i;
+               else
+                       smc_state->levels[i].ACIndex = 0;
+
+               smc_state->levelCount++;
+       }
+
+       rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_watermark_threshold,
+                                     cpu_to_be32(threshold / 512));
+
+       ni_populate_smc_sp(rdev, radeon_state, smc_state);
+
+       ret = ni_populate_power_containment_values(rdev, radeon_state, smc_state);
+       if (ret)
+               ni_pi->enable_power_containment = false;
+
+       ret = ni_populate_sq_ramping_values(rdev, radeon_state, smc_state);
+       if (ret)
+               ni_pi->enable_sq_ramping = false;
+
+       return ni_populate_smc_t(rdev, radeon_state, smc_state);
+}
+
+static int ni_upload_sw_state(struct radeon_device *rdev,
+                             struct radeon_ps *radeon_new_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u16 address = pi->state_table_start +
+               offsetof(NISLANDS_SMC_STATETABLE, driverState);
+       u16 state_size = sizeof(NISLANDS_SMC_SWSTATE) +
+               ((NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1) * sizeof(NISLANDS_SMC_HW_PERFORMANCE_LEVEL));
+       int ret;
+       NISLANDS_SMC_SWSTATE *smc_state = kzalloc(state_size, GFP_KERNEL);
+
+       if (smc_state == NULL)
+               return -ENOMEM;
+
+       ret = ni_convert_power_state_to_smc(rdev, radeon_new_state, smc_state);
+       if (ret)
+               goto done;
+
+       ret = rv770_copy_bytes_to_smc(rdev, address, (u8 *)smc_state, state_size, pi->sram_end);
+
+done:
+       kfree(smc_state);
+
+       return ret;
+}
+
+static int ni_set_mc_special_registers(struct radeon_device *rdev,
+                                      struct ni_mc_reg_table *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u8 i, j, k;
+       u32 temp_reg;
+
+       for (i = 0, j = table->last; i < table->last; i++) {
+               switch (table->mc_reg_address[i].s1) {
+               case MC_SEQ_MISC1 >> 2:
+                       if (j >= SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+                       temp_reg = RREG32(MC_PMG_CMD_EMRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_EMRS >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++)
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       ((temp_reg & 0xffff0000)) |
+                                       ((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16);
+                       j++;
+                       if (j >= SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+
+                       temp_reg = RREG32(MC_PMG_CMD_MRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS_LP >> 2;
+                       for(k = 0; k < table->num_entries; k++) {
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       (temp_reg & 0xffff0000) |
+                                       (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+                               if (!pi->mem_gddr5)
+                                       table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+                       }
+                       j++;
+                       if (j > SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+                       break;
+               case MC_SEQ_RESERVE_M >> 2:
+                       temp_reg = RREG32(MC_PMG_CMD_MRS1);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS1 >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++)
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       (temp_reg & 0xffff0000) |
+                                       (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+                       j++;
+                       if (j > SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       table->last = j;
+
+       return 0;
+}
+
+static bool ni_check_s0_mc_reg_index(u16 in_reg, u16 *out_reg)
+{
+       bool result = true;
+
+       switch (in_reg) {
+        case  MC_SEQ_RAS_TIMING >> 2:
+               *out_reg = MC_SEQ_RAS_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_CAS_TIMING >> 2:
+               *out_reg = MC_SEQ_CAS_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_MISC_TIMING >> 2:
+               *out_reg = MC_SEQ_MISC_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_MISC_TIMING2 >> 2:
+               *out_reg = MC_SEQ_MISC_TIMING2_LP >> 2;
+               break;
+        case MC_SEQ_RD_CTL_D0 >> 2:
+               *out_reg = MC_SEQ_RD_CTL_D0_LP >> 2;
+               break;
+        case MC_SEQ_RD_CTL_D1 >> 2:
+               *out_reg = MC_SEQ_RD_CTL_D1_LP >> 2;
+               break;
+        case MC_SEQ_WR_CTL_D0 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_D0_LP >> 2;
+               break;
+        case MC_SEQ_WR_CTL_D1 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_D1_LP >> 2;
+               break;
+        case MC_PMG_CMD_EMRS >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
+               break;
+        case MC_PMG_CMD_MRS >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS_LP >> 2;
+               break;
+        case MC_PMG_CMD_MRS1 >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
+               break;
+        case MC_SEQ_PMG_TIMING >> 2:
+               *out_reg = MC_SEQ_PMG_TIMING_LP >> 2;
+               break;
+        case MC_PMG_CMD_MRS2 >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS2_LP >> 2;
+               break;
+        default:
+               result = false;
+               break;
+       }
+
+       return result;
+}
+
+static void ni_set_valid_flag(struct ni_mc_reg_table *table)
+{
+       u8 i, j;
+
+       for (i = 0; i < table->last; i++) {
+               for (j = 1; j < table->num_entries; j++) {
+                       if (table->mc_reg_table_entry[j-1].mc_data[i] != table->mc_reg_table_entry[j].mc_data[i]) {
+                               table->valid_flag |= 1 << i;
+                               break;
+                       }
+               }
+       }
+}
+
+static void ni_set_s0_mc_reg_index(struct ni_mc_reg_table *table)
+{
+       u32 i;
+       u16 address;
+
+       for (i = 0; i < table->last; i++)
+               table->mc_reg_address[i].s0 =
+                       ni_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address) ?
+                       address : table->mc_reg_address[i].s1;
+}
+
+static int ni_copy_vbios_mc_reg_table(struct atom_mc_reg_table *table,
+                                     struct ni_mc_reg_table *ni_table)
+{
+       u8 i, j;
+
+       if (table->last > SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
+               return -EINVAL;
+       if (table->num_entries > MAX_AC_TIMING_ENTRIES)
+               return -EINVAL;
+
+       for (i = 0; i < table->last; i++)
+               ni_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1;
+       ni_table->last = table->last;
+
+       for (i = 0; i < table->num_entries; i++) {
+               ni_table->mc_reg_table_entry[i].mclk_max =
+                       table->mc_reg_table_entry[i].mclk_max;
+               for (j = 0; j < table->last; j++)
+                       ni_table->mc_reg_table_entry[i].mc_data[j] =
+                               table->mc_reg_table_entry[i].mc_data[j];
+       }
+       ni_table->num_entries = table->num_entries;
+
+       return 0;
+}
+
+static int ni_initialize_mc_reg_table(struct radeon_device *rdev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       int ret;
+       struct atom_mc_reg_table *table;
+       struct ni_mc_reg_table *ni_table = &ni_pi->mc_reg_table;
+       u8 module_index = rv770_get_memory_module_index(rdev);
+
+        table = kzalloc(sizeof(struct atom_mc_reg_table), GFP_KERNEL);
+        if (!table)
+                return -ENOMEM;
+
+       WREG32(MC_SEQ_RAS_TIMING_LP, RREG32(MC_SEQ_RAS_TIMING));
+       WREG32(MC_SEQ_CAS_TIMING_LP, RREG32(MC_SEQ_CAS_TIMING));
+       WREG32(MC_SEQ_MISC_TIMING_LP, RREG32(MC_SEQ_MISC_TIMING));
+       WREG32(MC_SEQ_MISC_TIMING2_LP, RREG32(MC_SEQ_MISC_TIMING2));
+       WREG32(MC_SEQ_PMG_CMD_EMRS_LP, RREG32(MC_PMG_CMD_EMRS));
+       WREG32(MC_SEQ_PMG_CMD_MRS_LP, RREG32(MC_PMG_CMD_MRS));
+       WREG32(MC_SEQ_PMG_CMD_MRS1_LP, RREG32(MC_PMG_CMD_MRS1));
+       WREG32(MC_SEQ_WR_CTL_D0_LP, RREG32(MC_SEQ_WR_CTL_D0));
+       WREG32(MC_SEQ_WR_CTL_D1_LP, RREG32(MC_SEQ_WR_CTL_D1));
+       WREG32(MC_SEQ_RD_CTL_D0_LP, RREG32(MC_SEQ_RD_CTL_D0));
+       WREG32(MC_SEQ_RD_CTL_D1_LP, RREG32(MC_SEQ_RD_CTL_D1));
+       WREG32(MC_SEQ_PMG_TIMING_LP, RREG32(MC_SEQ_PMG_TIMING));
+       WREG32(MC_SEQ_PMG_CMD_MRS2_LP, RREG32(MC_PMG_CMD_MRS2));
+
+       ret = radeon_atom_init_mc_reg_table(rdev, module_index, table);
+
+        if (ret)
+                goto init_mc_done;
+
+       ret = ni_copy_vbios_mc_reg_table(table, ni_table);
+
+        if (ret)
+                goto init_mc_done;
+
+       ni_set_s0_mc_reg_index(ni_table);
+
+       ret = ni_set_mc_special_registers(rdev, ni_table);
+
+        if (ret)
+                goto init_mc_done;
+
+       ni_set_valid_flag(ni_table);
+
+init_mc_done:
+        kfree(table);
+
+       return ret;
+}
+
+static void ni_populate_mc_reg_addresses(struct radeon_device *rdev,
+                                        SMC_NIslands_MCRegisters *mc_reg_table)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       u32 i, j;
+
+       for (i = 0, j = 0; j < ni_pi->mc_reg_table.last; j++) {
+               if (ni_pi->mc_reg_table.valid_flag & (1 << j)) {
+                       if (i >= SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               break;
+                       mc_reg_table->address[i].s0 =
+                               cpu_to_be16(ni_pi->mc_reg_table.mc_reg_address[j].s0);
+                       mc_reg_table->address[i].s1 =
+                               cpu_to_be16(ni_pi->mc_reg_table.mc_reg_address[j].s1);
+                       i++;
+               }
+       }
+       mc_reg_table->last = (u8)i;
+}
+
+
+static void ni_convert_mc_registers(struct ni_mc_reg_entry *entry,
+                                   SMC_NIslands_MCRegisterSet *data,
+                                   u32 num_entries, u32 valid_flag)
+{
+       u32 i, j;
+
+       for (i = 0, j = 0; j < num_entries; j++) {
+               if (valid_flag & (1 << j)) {
+                       data->value[i] = cpu_to_be32(entry->mc_data[j]);
+                       i++;
+               }
+       }
+}
+
+static void ni_convert_mc_reg_table_entry_to_smc(struct radeon_device *rdev,
+                                                struct rv7xx_pl *pl,
+                                                SMC_NIslands_MCRegisterSet *mc_reg_table_data)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       u32 i = 0;
+
+       for (i = 0; i < ni_pi->mc_reg_table.num_entries; i++) {
+               if (pl->mclk <= ni_pi->mc_reg_table.mc_reg_table_entry[i].mclk_max)
+                       break;
+       }
+
+       if ((i == ni_pi->mc_reg_table.num_entries) && (i > 0))
+               --i;
+
+       ni_convert_mc_registers(&ni_pi->mc_reg_table.mc_reg_table_entry[i],
+                               mc_reg_table_data,
+                               ni_pi->mc_reg_table.last,
+                               ni_pi->mc_reg_table.valid_flag);
+}
+
+static void ni_convert_mc_reg_table_to_smc(struct radeon_device *rdev,
+                                          struct radeon_ps *radeon_state,
+                                          SMC_NIslands_MCRegisters *mc_reg_table)
+{
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       int i;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               ni_convert_mc_reg_table_entry_to_smc(rdev,
+                                                    &state->performance_levels[i],
+                                                    &mc_reg_table->data[NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i]);
+       }
+}
+
+static int ni_populate_mc_reg_table(struct radeon_device *rdev,
+                                   struct radeon_ps *radeon_boot_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+        struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct ni_ps *boot_state = ni_get_ps(radeon_boot_state);
+       SMC_NIslands_MCRegisters *mc_reg_table = &ni_pi->smc_mc_reg_table;
+
+       memset(mc_reg_table, 0, sizeof(SMC_NIslands_MCRegisters));
+
+       rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_seq_index, 1);
+
+       ni_populate_mc_reg_addresses(rdev, mc_reg_table);
+
+       ni_convert_mc_reg_table_entry_to_smc(rdev, &boot_state->performance_levels[0],
+                                            &mc_reg_table->data[0]);
+
+       ni_convert_mc_registers(&ni_pi->mc_reg_table.mc_reg_table_entry[0],
+                               &mc_reg_table->data[1],
+                               ni_pi->mc_reg_table.last,
+                               ni_pi->mc_reg_table.valid_flag);
+
+       ni_convert_mc_reg_table_to_smc(rdev, radeon_boot_state, mc_reg_table);
+
+       return rv770_copy_bytes_to_smc(rdev, eg_pi->mc_reg_table_start,
+                                      (u8 *)mc_reg_table,
+                                      sizeof(SMC_NIslands_MCRegisters),
+                                      pi->sram_end);
+}
+
+static int ni_upload_mc_reg_table(struct radeon_device *rdev,
+                                 struct radeon_ps *radeon_new_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+        struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct ni_ps *ni_new_state = ni_get_ps(radeon_new_state);
+       SMC_NIslands_MCRegisters *mc_reg_table = &ni_pi->smc_mc_reg_table;
+       u16 address;
+
+       memset(mc_reg_table, 0, sizeof(SMC_NIslands_MCRegisters));
+
+       ni_convert_mc_reg_table_to_smc(rdev, radeon_new_state, mc_reg_table);
+
+       address = eg_pi->mc_reg_table_start +
+               (u16)offsetof(SMC_NIslands_MCRegisters, data[NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT]);
+
+       return rv770_copy_bytes_to_smc(rdev, address,
+                                      (u8 *)&mc_reg_table->data[NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT],
+                                      sizeof(SMC_NIslands_MCRegisterSet) * ni_new_state->performance_level_count,
+                                      pi->sram_end);
+}
+
+static int ni_init_driver_calculated_leakage_table(struct radeon_device *rdev,
+                                                  PP_NIslands_CACTABLES *cac_tables)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 leakage = 0;
+       unsigned int i, j, table_size;
+       s32 t;
+       u32 smc_leakage, max_leakage = 0;
+       u32 scaling_factor;
+
+       table_size = eg_pi->vddc_voltage_table.count;
+
+       if (SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES < table_size)
+               table_size = SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES;
+
+       scaling_factor = ni_get_smc_power_scaling_factor(rdev);
+
+       for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++) {
+               for (j = 0; j < table_size; j++) {
+                       t = (1000 * ((i + 1) * 8));
+
+                       if (t < ni_pi->cac_data.leakage_minimum_temperature)
+                               t = ni_pi->cac_data.leakage_minimum_temperature;
+
+                       ni_calculate_leakage_for_v_and_t(rdev,
+                                                        &ni_pi->cac_data.leakage_coefficients,
+                                                        eg_pi->vddc_voltage_table.entries[j].value,
+                                                        t,
+                                                        ni_pi->cac_data.i_leakage,
+                                                        &leakage);
+
+                       smc_leakage = ni_scale_power_for_smc(leakage, scaling_factor) / 1000;
+                       if (smc_leakage > max_leakage)
+                               max_leakage = smc_leakage;
+
+                       cac_tables->cac_lkge_lut[i][j] = cpu_to_be32(smc_leakage);
+               }
+       }
+
+       for (j = table_size; j < SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
+               for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++)
+                       cac_tables->cac_lkge_lut[i][j] = cpu_to_be32(max_leakage);
+       }
+       return 0;
+}
+
+static int ni_init_simplified_leakage_table(struct radeon_device *rdev,
+                                           PP_NIslands_CACTABLES *cac_tables)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_cac_leakage_table *leakage_table =
+               &rdev->pm.dpm.dyn_state.cac_leakage_table;
+       u32 i, j, table_size;
+       u32 smc_leakage, max_leakage = 0;
+       u32 scaling_factor;
+
+       if (!leakage_table)
+               return -EINVAL;
+
+       table_size = leakage_table->count;
+
+       if (eg_pi->vddc_voltage_table.count != table_size)
+               table_size = (eg_pi->vddc_voltage_table.count < leakage_table->count) ?
+                       eg_pi->vddc_voltage_table.count : leakage_table->count;
+
+       if (SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES < table_size)
+               table_size = SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES;
+
+       if (table_size == 0)
+               return -EINVAL;
+
+       scaling_factor = ni_get_smc_power_scaling_factor(rdev);
+
+       for (j = 0; j < table_size; j++) {
+               smc_leakage = leakage_table->entries[j].leakage;
+
+               if (smc_leakage > max_leakage)
+                       max_leakage = smc_leakage;
+
+               for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++)
+                       cac_tables->cac_lkge_lut[i][j] =
+                               cpu_to_be32(ni_scale_power_for_smc(smc_leakage, scaling_factor));
+       }
+
+       for (j = table_size; j < SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
+               for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++)
+                       cac_tables->cac_lkge_lut[i][j] =
+                               cpu_to_be32(ni_scale_power_for_smc(max_leakage, scaling_factor));
+       }
+       return 0;
+}
+
+static int ni_initialize_smc_cac_tables(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       PP_NIslands_CACTABLES *cac_tables = NULL;
+       int i, ret;
+        u32 reg;
+
+       if (ni_pi->enable_cac == false)
+               return 0;
+
+       cac_tables = kzalloc(sizeof(PP_NIslands_CACTABLES), GFP_KERNEL);
+       if (!cac_tables)
+               return -ENOMEM;
+
+       reg = RREG32(CG_CAC_CTRL) & ~(TID_CNT_MASK | TID_UNIT_MASK);
+       reg |= (TID_CNT(ni_pi->cac_weights->tid_cnt) |
+               TID_UNIT(ni_pi->cac_weights->tid_unit));
+       WREG32(CG_CAC_CTRL, reg);
+
+       for (i = 0; i < NISLANDS_DCCAC_MAX_LEVELS; i++)
+               ni_pi->dc_cac_table[i] = ni_pi->cac_weights->dc_cac[i];
+
+       for (i = 0; i < SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES; i++)
+               cac_tables->cac_bif_lut[i] = ni_pi->cac_weights->pcie_cac[i];
+
+       ni_pi->cac_data.i_leakage = rdev->pm.dpm.cac_leakage;
+       ni_pi->cac_data.pwr_const = 0;
+       ni_pi->cac_data.dc_cac_value = ni_pi->dc_cac_table[NISLANDS_DCCAC_LEVEL_0];
+       ni_pi->cac_data.bif_cac_value = 0;
+       ni_pi->cac_data.mc_wr_weight = ni_pi->cac_weights->mc_write_weight;
+       ni_pi->cac_data.mc_rd_weight = ni_pi->cac_weights->mc_read_weight;
+       ni_pi->cac_data.allow_ovrflw = 0;
+       ni_pi->cac_data.l2num_win_tdp = ni_pi->lta_window_size;
+       ni_pi->cac_data.num_win_tdp = 0;
+       ni_pi->cac_data.lts_truncate_n = ni_pi->lts_truncate;
+
+       if (ni_pi->driver_calculate_cac_leakage)
+               ret = ni_init_driver_calculated_leakage_table(rdev, cac_tables);
+       else
+               ret = ni_init_simplified_leakage_table(rdev, cac_tables);
+
+       if (ret)
+               goto done_free;
+
+       cac_tables->pwr_const      = cpu_to_be32(ni_pi->cac_data.pwr_const);
+       cac_tables->dc_cacValue    = cpu_to_be32(ni_pi->cac_data.dc_cac_value);
+       cac_tables->bif_cacValue   = cpu_to_be32(ni_pi->cac_data.bif_cac_value);
+       cac_tables->AllowOvrflw    = ni_pi->cac_data.allow_ovrflw;
+       cac_tables->MCWrWeight     = ni_pi->cac_data.mc_wr_weight;
+       cac_tables->MCRdWeight     = ni_pi->cac_data.mc_rd_weight;
+       cac_tables->numWin_TDP     = ni_pi->cac_data.num_win_tdp;
+       cac_tables->l2numWin_TDP   = ni_pi->cac_data.l2num_win_tdp;
+       cac_tables->lts_truncate_n = ni_pi->cac_data.lts_truncate_n;
+
+       ret = rv770_copy_bytes_to_smc(rdev, ni_pi->cac_table_start, (u8 *)cac_tables,
+                                     sizeof(PP_NIslands_CACTABLES), pi->sram_end);
+
+done_free:
+       if (ret) {
+               ni_pi->enable_cac = false;
+               ni_pi->enable_power_containment = false;
+       }
+
+       kfree(cac_tables);
+
+       return 0;
+}
+
+static int ni_initialize_hardware_cac_manager(struct radeon_device *rdev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       u32 reg;
+
+       if (!ni_pi->enable_cac ||
+           !ni_pi->cac_configuration_required)
+               return 0;
+
+       if (ni_pi->cac_weights == NULL)
+               return -EINVAL;
+
+       reg = RREG32_CG(CG_CAC_REGION_1_WEIGHT_0) & ~(WEIGHT_TCP_SIG0_MASK |
+                                                     WEIGHT_TCP_SIG1_MASK |
+                                                     WEIGHT_TA_SIG_MASK);
+       reg |= (WEIGHT_TCP_SIG0(ni_pi->cac_weights->weight_tcp_sig0) |
+               WEIGHT_TCP_SIG1(ni_pi->cac_weights->weight_tcp_sig1) |
+               WEIGHT_TA_SIG(ni_pi->cac_weights->weight_ta_sig));
+       WREG32_CG(CG_CAC_REGION_1_WEIGHT_0, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_1_WEIGHT_1) & ~(WEIGHT_TCC_EN0_MASK |
+                                                     WEIGHT_TCC_EN1_MASK |
+                                                     WEIGHT_TCC_EN2_MASK);
+       reg |= (WEIGHT_TCC_EN0(ni_pi->cac_weights->weight_tcc_en0) |
+               WEIGHT_TCC_EN1(ni_pi->cac_weights->weight_tcc_en1) |
+               WEIGHT_TCC_EN2(ni_pi->cac_weights->weight_tcc_en2));
+       WREG32_CG(CG_CAC_REGION_1_WEIGHT_1, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_2_WEIGHT_0) & ~(WEIGHT_CB_EN0_MASK |
+                                                     WEIGHT_CB_EN1_MASK |
+                                                     WEIGHT_CB_EN2_MASK |
+                                                     WEIGHT_CB_EN3_MASK);
+       reg |= (WEIGHT_CB_EN0(ni_pi->cac_weights->weight_cb_en0) |
+               WEIGHT_CB_EN1(ni_pi->cac_weights->weight_cb_en1) |
+               WEIGHT_CB_EN2(ni_pi->cac_weights->weight_cb_en2) |
+               WEIGHT_CB_EN3(ni_pi->cac_weights->weight_cb_en3));
+       WREG32_CG(CG_CAC_REGION_2_WEIGHT_0, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_2_WEIGHT_1) & ~(WEIGHT_DB_SIG0_MASK |
+                                                     WEIGHT_DB_SIG1_MASK |
+                                                     WEIGHT_DB_SIG2_MASK |
+                                                     WEIGHT_DB_SIG3_MASK);
+       reg |= (WEIGHT_DB_SIG0(ni_pi->cac_weights->weight_db_sig0) |
+               WEIGHT_DB_SIG1(ni_pi->cac_weights->weight_db_sig1) |
+               WEIGHT_DB_SIG2(ni_pi->cac_weights->weight_db_sig2) |
+               WEIGHT_DB_SIG3(ni_pi->cac_weights->weight_db_sig3));
+       WREG32_CG(CG_CAC_REGION_2_WEIGHT_1, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_2_WEIGHT_2) & ~(WEIGHT_SXM_SIG0_MASK |
+                                                     WEIGHT_SXM_SIG1_MASK |
+                                                     WEIGHT_SXM_SIG2_MASK |
+                                                     WEIGHT_SXS_SIG0_MASK |
+                                                     WEIGHT_SXS_SIG1_MASK);
+       reg |= (WEIGHT_SXM_SIG0(ni_pi->cac_weights->weight_sxm_sig0) |
+               WEIGHT_SXM_SIG1(ni_pi->cac_weights->weight_sxm_sig1) |
+               WEIGHT_SXM_SIG2(ni_pi->cac_weights->weight_sxm_sig2) |
+               WEIGHT_SXS_SIG0(ni_pi->cac_weights->weight_sxs_sig0) |
+               WEIGHT_SXS_SIG1(ni_pi->cac_weights->weight_sxs_sig1));
+       WREG32_CG(CG_CAC_REGION_2_WEIGHT_2, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_3_WEIGHT_0) & ~(WEIGHT_XBR_0_MASK |
+                                                     WEIGHT_XBR_1_MASK |
+                                                     WEIGHT_XBR_2_MASK |
+                                                     WEIGHT_SPI_SIG0_MASK);
+       reg |= (WEIGHT_XBR_0(ni_pi->cac_weights->weight_xbr_0) |
+               WEIGHT_XBR_1(ni_pi->cac_weights->weight_xbr_1) |
+               WEIGHT_XBR_2(ni_pi->cac_weights->weight_xbr_2) |
+               WEIGHT_SPI_SIG0(ni_pi->cac_weights->weight_spi_sig0));
+       WREG32_CG(CG_CAC_REGION_3_WEIGHT_0, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_3_WEIGHT_1) & ~(WEIGHT_SPI_SIG1_MASK |
+                                                     WEIGHT_SPI_SIG2_MASK |
+                                                     WEIGHT_SPI_SIG3_MASK |
+                                                     WEIGHT_SPI_SIG4_MASK |
+                                                     WEIGHT_SPI_SIG5_MASK);
+       reg |= (WEIGHT_SPI_SIG1(ni_pi->cac_weights->weight_spi_sig1) |
+               WEIGHT_SPI_SIG2(ni_pi->cac_weights->weight_spi_sig2) |
+               WEIGHT_SPI_SIG3(ni_pi->cac_weights->weight_spi_sig3) |
+               WEIGHT_SPI_SIG4(ni_pi->cac_weights->weight_spi_sig4) |
+               WEIGHT_SPI_SIG5(ni_pi->cac_weights->weight_spi_sig5));
+       WREG32_CG(CG_CAC_REGION_3_WEIGHT_1, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_0) & ~(WEIGHT_LDS_SIG0_MASK |
+                                                     WEIGHT_LDS_SIG1_MASK |
+                                                     WEIGHT_SC_MASK);
+       reg |= (WEIGHT_LDS_SIG0(ni_pi->cac_weights->weight_lds_sig0) |
+               WEIGHT_LDS_SIG1(ni_pi->cac_weights->weight_lds_sig1) |
+               WEIGHT_SC(ni_pi->cac_weights->weight_sc));
+       WREG32_CG(CG_CAC_REGION_4_WEIGHT_0, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_1) & ~(WEIGHT_BIF_MASK |
+                                                     WEIGHT_CP_MASK |
+                                                     WEIGHT_PA_SIG0_MASK |
+                                                     WEIGHT_PA_SIG1_MASK |
+                                                     WEIGHT_VGT_SIG0_MASK);
+       reg |= (WEIGHT_BIF(ni_pi->cac_weights->weight_bif) |
+               WEIGHT_CP(ni_pi->cac_weights->weight_cp) |
+               WEIGHT_PA_SIG0(ni_pi->cac_weights->weight_pa_sig0) |
+               WEIGHT_PA_SIG1(ni_pi->cac_weights->weight_pa_sig1) |
+               WEIGHT_VGT_SIG0(ni_pi->cac_weights->weight_vgt_sig0));
+       WREG32_CG(CG_CAC_REGION_4_WEIGHT_1, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_2) & ~(WEIGHT_VGT_SIG1_MASK |
+                                                     WEIGHT_VGT_SIG2_MASK |
+                                                     WEIGHT_DC_SIG0_MASK |
+                                                     WEIGHT_DC_SIG1_MASK |
+                                                     WEIGHT_DC_SIG2_MASK);
+       reg |= (WEIGHT_VGT_SIG1(ni_pi->cac_weights->weight_vgt_sig1) |
+               WEIGHT_VGT_SIG2(ni_pi->cac_weights->weight_vgt_sig2) |
+               WEIGHT_DC_SIG0(ni_pi->cac_weights->weight_dc_sig0) |
+               WEIGHT_DC_SIG1(ni_pi->cac_weights->weight_dc_sig1) |
+               WEIGHT_DC_SIG2(ni_pi->cac_weights->weight_dc_sig2));
+       WREG32_CG(CG_CAC_REGION_4_WEIGHT_2, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_3) & ~(WEIGHT_DC_SIG3_MASK |
+                                                     WEIGHT_UVD_SIG0_MASK |
+                                                     WEIGHT_UVD_SIG1_MASK |
+                                                     WEIGHT_SPARE0_MASK |
+                                                     WEIGHT_SPARE1_MASK);
+       reg |= (WEIGHT_DC_SIG3(ni_pi->cac_weights->weight_dc_sig3) |
+               WEIGHT_UVD_SIG0(ni_pi->cac_weights->weight_uvd_sig0) |
+               WEIGHT_UVD_SIG1(ni_pi->cac_weights->weight_uvd_sig1) |
+               WEIGHT_SPARE0(ni_pi->cac_weights->weight_spare0) |
+               WEIGHT_SPARE1(ni_pi->cac_weights->weight_spare1));
+       WREG32_CG(CG_CAC_REGION_4_WEIGHT_3, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_5_WEIGHT_0) & ~(WEIGHT_SQ_VSP_MASK |
+                                                     WEIGHT_SQ_VSP0_MASK);
+       reg |= (WEIGHT_SQ_VSP(ni_pi->cac_weights->weight_sq_vsp) |
+               WEIGHT_SQ_VSP0(ni_pi->cac_weights->weight_sq_vsp0));
+       WREG32_CG(CG_CAC_REGION_5_WEIGHT_0, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_5_WEIGHT_1) & ~(WEIGHT_SQ_GPR_MASK);
+       reg |= WEIGHT_SQ_GPR(ni_pi->cac_weights->weight_sq_gpr);
+       WREG32_CG(CG_CAC_REGION_5_WEIGHT_1, reg);
+
+       reg = RREG32_CG(CG_CAC_REGION_4_OVERRIDE_4) & ~(OVR_MODE_SPARE_0_MASK |
+                                                       OVR_VAL_SPARE_0_MASK |
+                                                       OVR_MODE_SPARE_1_MASK |
+                                                       OVR_VAL_SPARE_1_MASK);
+       reg |= (OVR_MODE_SPARE_0(ni_pi->cac_weights->ovr_mode_spare_0) |
+               OVR_VAL_SPARE_0(ni_pi->cac_weights->ovr_val_spare_0) |
+               OVR_MODE_SPARE_1(ni_pi->cac_weights->ovr_mode_spare_1) |
+               OVR_VAL_SPARE_1(ni_pi->cac_weights->ovr_val_spare_1));
+       WREG32_CG(CG_CAC_REGION_4_OVERRIDE_4, reg);
+
+       reg = RREG32(SQ_CAC_THRESHOLD) & ~(VSP_MASK |
+                                          VSP0_MASK |
+                                          GPR_MASK);
+       reg |= (VSP(ni_pi->cac_weights->vsp) |
+               VSP0(ni_pi->cac_weights->vsp0) |
+               GPR(ni_pi->cac_weights->gpr));
+       WREG32(SQ_CAC_THRESHOLD, reg);
+
+       reg = (MCDW_WR_ENABLE |
+              MCDX_WR_ENABLE |
+              MCDY_WR_ENABLE |
+              MCDZ_WR_ENABLE |
+              INDEX(0x09D4));
+       WREG32(MC_CG_CONFIG, reg);
+
+       reg = (READ_WEIGHT(ni_pi->cac_weights->mc_read_weight) |
+              WRITE_WEIGHT(ni_pi->cac_weights->mc_write_weight) |
+              ALLOW_OVERFLOW);
+       WREG32(MC_CG_DATAPORT, reg);
+
+       return 0;
+}
+
+static int ni_enable_smc_cac(struct radeon_device *rdev,
+                            struct radeon_ps *radeon_new_state,
+                            bool enable)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       int ret = 0;
+       PPSMC_Result smc_result;
+
+       if (ni_pi->enable_cac) {
+               if (enable) {
+                       if (!r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) {
+                               smc_result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_CollectCAC_PowerCorreln);
+
+                               if (ni_pi->support_cac_long_term_average) {
+                                       smc_result = rv770_send_msg_to_smc(rdev, PPSMC_CACLongTermAvgEnable);
+                                       if (PPSMC_Result_OK != smc_result)
+                                               ni_pi->support_cac_long_term_average = false;
+                               }
+
+                               smc_result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableCac);
+                               if (PPSMC_Result_OK != smc_result)
+                                       ret = -EINVAL;
+
+                               ni_pi->cac_enabled = (PPSMC_Result_OK == smc_result) ? true : false;
+                       }
+               } else if (ni_pi->cac_enabled) {
+                       smc_result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_DisableCac);
+
+                       ni_pi->cac_enabled = false;
+
+                       if (ni_pi->support_cac_long_term_average) {
+                               smc_result = rv770_send_msg_to_smc(rdev, PPSMC_CACLongTermAvgDisable);
+                               if (PPSMC_Result_OK != smc_result)
+                                       ni_pi->support_cac_long_term_average = false;
+                       }
+               }
+       }
+
+       return ret;
+}
+
+static int ni_pcie_performance_request(struct radeon_device *rdev,
+                                      u8 perf_req, bool advertise)
+{
+       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)
+                       radeon_acpi_pcie_notify_device_ready(rdev);
+               eg_pi->pcie_performance_request_registered = true;
+               return radeon_acpi_pcie_performance_request(rdev, perf_req, advertise);
+       } else if ((perf_req == PCIE_PERF_REQ_REMOVE_REGISTRY) &&
+                   eg_pi->pcie_performance_request_registered) {
+               eg_pi->pcie_performance_request_registered = false;
+               return radeon_acpi_pcie_performance_request(rdev, perf_req, advertise);
+       }
+#endif
+       return 0;
+}
+
+static int ni_advertise_gen2_capability(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 tmp;
+
+        tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+
+        if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) &&
+            (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2))
+                pi->pcie_gen2 = true;
+        else
+               pi->pcie_gen2 = false;
+
+       if (!pi->pcie_gen2)
+               ni_pcie_performance_request(rdev, PCIE_PERF_REQ_PECI_GEN2, true);
+
+       return 0;
+}
+
+static void ni_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev,
+                                           bool enable)
+{
+        struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+        u32 tmp, bif;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+
+       if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) &&
+           (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) {
+               if (enable) {
+                       if (!pi->boot_in_gen2) {
+                               bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK;
+                               bif |= CG_CLIENT_REQ(0xd);
+                               WREG32(CG_BIF_REQ_AND_RSP, bif);
+                       }
+                       tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
+                       tmp |= LC_HW_VOLTAGE_IF_CONTROL(1);
+                       tmp |= LC_GEN2_EN_STRAP;
+
+                       tmp |= LC_CLR_FAILED_SPD_CHANGE_CNT;
+                       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+                       udelay(10);
+                       tmp &= ~LC_CLR_FAILED_SPD_CHANGE_CNT;
+                       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+               } else {
+                       if (!pi->boot_in_gen2) {
+                               bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK;
+                               bif |= CG_CLIENT_REQ(0xd);
+                               WREG32(CG_BIF_REQ_AND_RSP, bif);
+
+                               tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
+                               tmp &= ~LC_GEN2_EN_STRAP;
+                       }
+                       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+               }
+       }
+}
+
+static void ni_enable_dynamic_pcie_gen2(struct radeon_device *rdev,
+                                       bool enable)
+{
+       ni_enable_bif_dynamic_pcie_gen2(rdev, enable);
+
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, ENABLE_GEN2PCIE, ~ENABLE_GEN2PCIE);
+       else
+                WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE);
+}
+
+void ni_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev,
+                                          struct radeon_ps *new_ps,
+                                          struct radeon_ps *old_ps)
+{
+       struct ni_ps *new_state = ni_get_ps(new_ps);
+       struct ni_ps *current_state = ni_get_ps(old_ps);
+
+       if ((new_ps->vclk == old_ps->vclk) &&
+           (new_ps->dclk == old_ps->dclk))
+               return;
+
+       if (new_state->performance_levels[new_state->performance_level_count - 1].sclk >=
+           current_state->performance_levels[current_state->performance_level_count - 1].sclk)
+               return;
+
+       radeon_set_uvd_clocks(rdev, new_ps->vclk, new_ps->dclk);
+}
+
+void ni_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev,
+                                         struct radeon_ps *new_ps,
+                                         struct radeon_ps *old_ps)
+{
+       struct ni_ps *new_state = ni_get_ps(new_ps);
+       struct ni_ps *current_state = ni_get_ps(old_ps);
+
+       if ((new_ps->vclk == old_ps->vclk) &&
+           (new_ps->dclk == old_ps->dclk))
+               return;
+
+       if (new_state->performance_levels[new_state->performance_level_count - 1].sclk <
+           current_state->performance_levels[current_state->performance_level_count - 1].sclk)
+               return;
+
+       radeon_set_uvd_clocks(rdev, new_ps->vclk, new_ps->dclk);
+}
+
+void ni_dpm_setup_asic(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       ni_read_clock_registers(rdev);
+       btc_read_arb_registers(rdev);
+       rv770_get_memory_type(rdev);
+       if (eg_pi->pcie_performance_request)
+               ni_advertise_gen2_capability(rdev);
+       rv770_get_pcie_gen2_status(rdev);
+       rv770_enable_acpi_pm(rdev);
+}
+
+void ni_update_current_ps(struct radeon_device *rdev,
+                         struct radeon_ps *rps)
+{
+       struct ni_ps *new_ps = ni_get_ps(rps);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+        struct ni_power_info *ni_pi = ni_get_pi(rdev);
+
+       eg_pi->current_rps = *rps;
+       ni_pi->current_ps = *new_ps;
+       eg_pi->current_rps.ps_priv = &ni_pi->current_ps;
+}
+
+void ni_update_requested_ps(struct radeon_device *rdev,
+                           struct radeon_ps *rps)
+{
+       struct ni_ps *new_ps = ni_get_ps(rps);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+        struct ni_power_info *ni_pi = ni_get_pi(rdev);
+
+       eg_pi->requested_rps = *rps;
+       ni_pi->requested_ps = *new_ps;
+       eg_pi->requested_rps.ps_priv = &ni_pi->requested_ps;
+}
+
+int ni_dpm_enable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+       int ret;
+
+       if (pi->gfx_clock_gating)
+               ni_cg_clockgating_default(rdev);
+        if (btc_dpm_enabled(rdev))
+                return -EINVAL;
+       if (pi->mg_clock_gating)
+               ni_mg_clockgating_default(rdev);
+       if (eg_pi->ls_clock_gating)
+               ni_ls_clockgating_default(rdev);
+       if (pi->voltage_control) {
+               rv770_enable_voltage_control(rdev, true);
+               ret = cypress_construct_voltage_tables(rdev);
+               if (ret) {
+                       DRM_ERROR("cypress_construct_voltage_tables failed\n");
+                       return ret;
+               }
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = ni_initialize_mc_reg_table(rdev);
+               if (ret)
+                       eg_pi->dynamic_ac_timing = false;
+       }
+       if (pi->dynamic_ss)
+               cypress_enable_spread_spectrum(rdev, true);
+       if (pi->thermal_protection)
+               rv770_enable_thermal_protection(rdev, true);
+       rv770_setup_bsp(rdev);
+       rv770_program_git(rdev);
+       rv770_program_tp(rdev);
+       rv770_program_tpp(rdev);
+       rv770_program_sstp(rdev);
+       cypress_enable_display_gap(rdev);
+       rv770_program_vc(rdev);
+       if (pi->dynamic_pcie_gen2)
+               ni_enable_dynamic_pcie_gen2(rdev, true);
+       ret = rv770_upload_firmware(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_upload_firmware failed\n");
+               return ret;
+       }
+       ret = ni_process_firmware_header(rdev);
+       if (ret) {
+               DRM_ERROR("ni_process_firmware_header failed\n");
+               return ret;
+       }
+       ret = ni_initial_switch_from_arb_f0_to_f1(rdev);
+       if (ret) {
+               DRM_ERROR("ni_initial_switch_from_arb_f0_to_f1 failed\n");
+               return ret;
+       }
+       ret = ni_init_smc_table(rdev);
+       if (ret) {
+               DRM_ERROR("ni_init_smc_table failed\n");
+               return ret;
+       }
+       ret = ni_init_smc_spll_table(rdev);
+       if (ret) {
+               DRM_ERROR("ni_init_smc_spll_table failed\n");
+               return ret;
+       }
+       ret = ni_init_arb_table_index(rdev);
+       if (ret) {
+               DRM_ERROR("ni_init_arb_table_index failed\n");
+               return ret;
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = ni_populate_mc_reg_table(rdev, boot_ps);
+               if (ret) {
+                       DRM_ERROR("ni_populate_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+       ret = ni_initialize_smc_cac_tables(rdev);
+       if (ret) {
+               DRM_ERROR("ni_initialize_smc_cac_tables failed\n");
+               return ret;
+       }
+       ret = ni_initialize_hardware_cac_manager(rdev);
+       if (ret) {
+               DRM_ERROR("ni_initialize_hardware_cac_manager failed\n");
+               return ret;
+       }
+       ret = ni_populate_smc_tdp_limits(rdev, boot_ps);
+       if (ret) {
+               DRM_ERROR("ni_populate_smc_tdp_limits failed\n");
+               return ret;
+       }
+       ni_program_response_times(rdev);
+       r7xx_start_smc(rdev);
+       ret = cypress_notify_smc_display_change(rdev, false);
+       if (ret) {
+               DRM_ERROR("cypress_notify_smc_display_change failed\n");
+               return ret;
+       }
+       cypress_enable_sclk_control(rdev, true);
+       if (eg_pi->memory_transition)
+               cypress_enable_mclk_control(rdev, true);
+       cypress_start_dpm(rdev);
+       if (pi->gfx_clock_gating)
+               ni_gfx_clockgating_enable(rdev, true);
+       if (pi->mg_clock_gating)
+               ni_mg_clockgating_enable(rdev, true);
+       if (eg_pi->ls_clock_gating)
+               ni_ls_clockgating_enable(rdev, true);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               PPSMC_Result result;
+
+               ret = rv770_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, 0xff * 1000);
+               if (ret)
+                       return ret;
+               rdev->irq.dpm_thermal = true;
+               radeon_irq_set(rdev);
+               result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt);
+
+               if (result != PPSMC_Result_OK)
+                       DRM_DEBUG_KMS("Could not enable thermal interrupts.\n");
+       }
+
+       rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+
+       ni_update_current_ps(rdev, boot_ps);
+
+       return 0;
+}
+
+void ni_dpm_disable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+
+       if (!btc_dpm_enabled(rdev))
+               return;
+       rv770_clear_vc(rdev);
+       if (pi->thermal_protection)
+               rv770_enable_thermal_protection(rdev, false);
+       ni_enable_power_containment(rdev, boot_ps, false);
+       ni_enable_smc_cac(rdev, boot_ps, false);
+       cypress_enable_spread_spectrum(rdev, false);
+       rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, false);
+       if (pi->dynamic_pcie_gen2)
+               ni_enable_dynamic_pcie_gen2(rdev, false);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               rdev->irq.dpm_thermal = false;
+               radeon_irq_set(rdev);
+       }
+
+       if (pi->gfx_clock_gating)
+               ni_gfx_clockgating_enable(rdev, false);
+       if (pi->mg_clock_gating)
+               ni_mg_clockgating_enable(rdev, false);
+       if (eg_pi->ls_clock_gating)
+               ni_ls_clockgating_enable(rdev, false);
+       ni_stop_dpm(rdev);
+       btc_reset_to_default(rdev);
+       ni_stop_smc(rdev);
+       ni_force_switch_to_arb_f0(rdev);
+
+       ni_update_current_ps(rdev, boot_ps);
+}
+
+static int ni_power_control_set_level(struct radeon_device *rdev)
+{
+       struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps;
+       int ret;
+
+       ret = ni_restrict_performance_levels_before_switch(rdev);
+       if (ret)
+               return ret;
+       ret = rv770_halt_smc(rdev);
+       if (ret)
+               return ret;
+       ret = ni_populate_smc_tdp_limits(rdev, new_ps);
+       if (ret)
+               return ret;
+       ret = rv770_resume_smc(rdev);
+       if (ret)
+               return ret;
+       ret = rv770_set_sw_state(rdev);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+int ni_dpm_pre_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps requested_ps = *rdev->pm.dpm.requested_ps;
+       struct radeon_ps *new_ps = &requested_ps;
+
+       ni_update_requested_ps(rdev, new_ps);
+
+       ni_apply_state_adjust_rules(rdev, &eg_pi->requested_rps);
+
+       return 0;
+}
+
+int ni_dpm_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *new_ps = &eg_pi->requested_rps;
+       struct radeon_ps *old_ps = &eg_pi->current_rps;
+       int ret;
+
+       ret = ni_restrict_performance_levels_before_switch(rdev);
+       if (ret) {
+               DRM_ERROR("ni_restrict_performance_levels_before_switch failed\n");
+               return ret;
+       }
+       ni_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps);
+       ret = ni_enable_power_containment(rdev, new_ps, false);
+       if (ret) {
+               DRM_ERROR("ni_enable_power_containment failed\n");
+               return ret;
+       }
+       ret = ni_enable_smc_cac(rdev, new_ps, false);
+       if (ret) {
+               DRM_ERROR("ni_enable_smc_cac failed\n");
+               return ret;
+       }
+       ret = rv770_halt_smc(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_halt_smc failed\n");
+               return ret;
+       }
+       if (eg_pi->smu_uvd_hs)
+               btc_notify_uvd_to_smc(rdev, new_ps);
+       ret = ni_upload_sw_state(rdev, new_ps);
+       if (ret) {
+               DRM_ERROR("ni_upload_sw_state failed\n");
+               return ret;
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = ni_upload_mc_reg_table(rdev, new_ps);
+               if (ret) {
+                       DRM_ERROR("ni_upload_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+       ret = ni_program_memory_timing_parameters(rdev, new_ps);
+       if (ret) {
+               DRM_ERROR("ni_program_memory_timing_parameters failed\n");
+               return ret;
+       }
+       ret = rv770_resume_smc(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_resume_smc failed\n");
+               return ret;
+       }
+       ret = rv770_set_sw_state(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_set_sw_state failed\n");
+               return ret;
+       }
+       ni_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+       ret = ni_enable_smc_cac(rdev, new_ps, true);
+       if (ret) {
+               DRM_ERROR("ni_enable_smc_cac failed\n");
+               return ret;
+       }
+       ret = ni_enable_power_containment(rdev, new_ps, true);
+       if (ret) {
+               DRM_ERROR("ni_enable_power_containment failed\n");
+               return ret;
+       }
+
+       /* update tdp */
+       ret = ni_power_control_set_level(rdev);
+       if (ret) {
+               DRM_ERROR("ni_power_control_set_level failed\n");
+               return ret;
+       }
+
+       ret = ni_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
+       if (ret) {
+               DRM_ERROR("ni_dpm_force_performance_level failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+void ni_dpm_post_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *new_ps = &eg_pi->requested_rps;
+
+       ni_update_current_ps(rdev, new_ps);
+}
+
+void ni_dpm_reset_asic(struct radeon_device *rdev)
+{
+       ni_restrict_performance_levels_before_switch(rdev);
+       rv770_set_boot_state(rdev);
+}
+
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+       struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+       struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+
+union pplib_clock_info {
+       struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
+       struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
+       struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
+       struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+
+union pplib_power_state {
+       struct _ATOM_PPLIB_STATE v1;
+       struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static void ni_parse_pplib_non_clock_info(struct radeon_device *rdev,
+                                         struct radeon_ps *rps,
+                                         struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
+                                         u8 table_rev)
+{
+       rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+       rps->class = le16_to_cpu(non_clock_info->usClassification);
+       rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
+
+       if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
+               rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
+               rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
+       } else if (r600_is_uvd_state(rps->class, rps->class2)) {
+               rps->vclk = RV770_DEFAULT_VCLK_FREQ;
+               rps->dclk = RV770_DEFAULT_DCLK_FREQ;
+       } else {
+               rps->vclk = 0;
+               rps->dclk = 0;
+       }
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
+               rdev->pm.dpm.boot_ps = rps;
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+               rdev->pm.dpm.uvd_ps = rps;
+}
+
+static void ni_parse_pplib_clock_info(struct radeon_device *rdev,
+                                     struct radeon_ps *rps, int index,
+                                     union pplib_clock_info *clock_info)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_ps *ps = ni_get_ps(rps);
+       u16 vddc;
+       struct rv7xx_pl *pl = &ps->performance_levels[index];
+
+       ps->performance_level_count = index + 1;
+
+       pl->sclk = le16_to_cpu(clock_info->evergreen.usEngineClockLow);
+       pl->sclk |= clock_info->evergreen.ucEngineClockHigh << 16;
+       pl->mclk = le16_to_cpu(clock_info->evergreen.usMemoryClockLow);
+       pl->mclk |= clock_info->evergreen.ucMemoryClockHigh << 16;
+
+       pl->vddc = le16_to_cpu(clock_info->evergreen.usVDDC);
+       pl->vddci = le16_to_cpu(clock_info->evergreen.usVDDCI);
+       pl->flags = le32_to_cpu(clock_info->evergreen.ulFlags);
+
+       /* patch up vddc if necessary */
+       if (pl->vddc == 0xff01) {
+               if (radeon_atom_get_max_vddc(rdev, 0, 0, &vddc) == 0)
+                       pl->vddc = vddc;
+       }
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) {
+               pi->acpi_vddc = pl->vddc;
+               eg_pi->acpi_vddci = pl->vddci;
+               if (ps->performance_levels[0].flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2)
+                       pi->acpi_pcie_gen2 = true;
+               else
+                       pi->acpi_pcie_gen2 = false;
+       }
+
+       if (rps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) {
+               eg_pi->ulv.supported = true;
+               eg_pi->ulv.pl = pl;
+       }
+
+       if (pi->min_vddc_in_table > pl->vddc)
+               pi->min_vddc_in_table = pl->vddc;
+
+       if (pi->max_vddc_in_table < pl->vddc)
+               pi->max_vddc_in_table = pl->vddc;
+
+       /* patch up boot state */
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+               u16 vddc, vddci, mvdd;
+               radeon_atombios_get_default_voltages(rdev, &vddc, &vddci, &mvdd);
+               pl->mclk = rdev->clock.default_mclk;
+               pl->sclk = rdev->clock.default_sclk;
+               pl->vddc = vddc;
+               pl->vddci = vddci;
+       }
+
+       if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
+           ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk = pl->sclk;
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk = pl->mclk;
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc = pl->vddc;
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci = pl->vddci;
+       }
+}
+
+static int ni_parse_power_table(struct radeon_device *rdev)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+       union pplib_power_state *power_state;
+       int i, j;
+       union pplib_clock_info *clock_info;
+       union power_info *power_info;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+        u16 data_offset;
+       u8 frev, crev;
+       struct ni_ps *ps;
+
+       if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset))
+               return -EINVAL;
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
+                                 power_info->pplib.ucNumStates, GFP_KERNEL);
+       if (!rdev->pm.dpm.ps)
+               return -ENOMEM;
+       rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
+       rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
+       rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
+
+       for (i = 0; i < power_info->pplib.ucNumStates; i++) {
+               power_state = (union pplib_power_state *)
+                       (mode_info->atom_context->bios + data_offset +
+                        le16_to_cpu(power_info->pplib.usStateArrayOffset) +
+                        i * power_info->pplib.ucStateEntrySize);
+               non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+                       (mode_info->atom_context->bios + data_offset +
+                        le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
+                        (power_state->v1.ucNonClockStateIndex *
+                         power_info->pplib.ucNonClockSize));
+               if (power_info->pplib.ucStateEntrySize - 1) {
+                       ps = kzalloc(sizeof(struct ni_ps), GFP_KERNEL);
+                       if (ps == NULL) {
+                               kfree(rdev->pm.dpm.ps);
+                               return -ENOMEM;
+                       }
+                       rdev->pm.dpm.ps[i].ps_priv = ps;
+                       ni_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
+                                                        non_clock_info,
+                                                        power_info->pplib.ucNonClockSize);
+                       for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) {
+                               clock_info = (union pplib_clock_info *)
+                                       (mode_info->atom_context->bios + data_offset +
+                                        le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
+                                        (power_state->v1.ucClockStateIndices[j] *
+                                         power_info->pplib.ucClockInfoSize));
+                               ni_parse_pplib_clock_info(rdev,
+                                                         &rdev->pm.dpm.ps[i], j,
+                                                         clock_info);
+                       }
+               }
+       }
+       rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates;
+       return 0;
+}
+
+int ni_dpm_init(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi;
+       struct evergreen_power_info *eg_pi;
+       struct ni_power_info *ni_pi;
+       int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
+       u16 data_offset, size;
+       u8 frev, crev;
+       struct atom_clock_dividers dividers;
+       int ret;
+
+       ni_pi = kzalloc(sizeof(struct ni_power_info), GFP_KERNEL);
+       if (ni_pi == NULL)
+               return -ENOMEM;
+       rdev->pm.dpm.priv = ni_pi;
+       eg_pi = &ni_pi->eg;
+       pi = &eg_pi->rv7xx;
+
+       rv770_get_max_vddc(rdev);
+
+       eg_pi->ulv.supported = false;
+       pi->acpi_vddc = 0;
+       eg_pi->acpi_vddci = 0;
+       pi->min_vddc_in_table = 0;
+       pi->max_vddc_in_table = 0;
+
+       ret = ni_parse_power_table(rdev);
+       if (ret)
+               return ret;
+       ret = r600_parse_extended_power_table(rdev);
+       if (ret)
+               return ret;
+
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries =
+               kzalloc(4 * sizeof(struct radeon_clock_voltage_dependency_entry), GFP_KERNEL);
+       if (!rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) {
+               r600_free_extended_power_table(rdev);
+               return -ENOMEM;
+       }
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.count = 4;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].clk = 0;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].v = 0;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].clk = 36000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].v = 720;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].clk = 54000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].v = 810;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].clk = 72000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].v = 900;
+
+       ni_patch_dependency_tables_based_on_leakage(rdev);
+
+       if (rdev->pm.dpm.voltage_response_time == 0)
+               rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT;
+       if (rdev->pm.dpm.backbias_response_time == 0)
+               rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            0, false, &dividers);
+       if (ret)
+               pi->ref_div = dividers.ref_div + 1;
+       else
+               pi->ref_div = R600_REFERENCEDIVIDER_DFLT;
+
+       pi->rlp = RV770_RLP_DFLT;
+       pi->rmp = RV770_RMP_DFLT;
+       pi->lhp = RV770_LHP_DFLT;
+       pi->lmp = RV770_LMP_DFLT;
+
+       eg_pi->ats[0].rlp = RV770_RLP_DFLT;
+       eg_pi->ats[0].rmp = RV770_RMP_DFLT;
+       eg_pi->ats[0].lhp = RV770_LHP_DFLT;
+       eg_pi->ats[0].lmp = RV770_LMP_DFLT;
+
+       eg_pi->ats[1].rlp = BTC_RLP_UVD_DFLT;
+       eg_pi->ats[1].rmp = BTC_RMP_UVD_DFLT;
+       eg_pi->ats[1].lhp = BTC_LHP_UVD_DFLT;
+       eg_pi->ats[1].lmp = BTC_LMP_UVD_DFLT;
+
+       eg_pi->smu_uvd_hs = true;
+
+       if (rdev->pdev->device == 0x6707) {
+               pi->mclk_strobe_mode_threshold = 55000;
+               pi->mclk_edc_enable_threshold = 55000;
+               eg_pi->mclk_edc_wr_enable_threshold = 55000;
+       } else {
+               pi->mclk_strobe_mode_threshold = 40000;
+               pi->mclk_edc_enable_threshold = 40000;
+               eg_pi->mclk_edc_wr_enable_threshold = 40000;
+       }
+       ni_pi->mclk_rtt_mode_threshold = eg_pi->mclk_edc_wr_enable_threshold;
+
+       pi->voltage_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, 0);
+
+       pi->mvdd_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC, 0);
+
+       eg_pi->vddci_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI, 0);
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                   &frev, &crev, &data_offset)) {
+               pi->sclk_ss = true;
+               pi->mclk_ss = true;
+               pi->dynamic_ss = true;
+       } else {
+               pi->sclk_ss = false;
+               pi->mclk_ss = false;
+               pi->dynamic_ss = true;
+       }
+
+       pi->asi = RV770_ASI_DFLT;
+       pi->pasi = CYPRESS_HASI_DFLT;
+       pi->vrc = CYPRESS_VRC_DFLT;
+
+       pi->power_gating = false;
+
+       pi->gfx_clock_gating = true;
+
+       pi->mg_clock_gating = true;
+       pi->mgcgtssm = true;
+       eg_pi->ls_clock_gating = false;
+       eg_pi->sclk_deep_sleep = false;
+
+       pi->dynamic_pcie_gen2 = true;
+
+       if (pi->gfx_clock_gating &&
+           (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE))
+               pi->thermal_protection = true;
+       else
+               pi->thermal_protection = false;
+
+       pi->display_gap = true;
+
+       pi->dcodt = true;
+
+       pi->ulps = true;
+
+       eg_pi->dynamic_ac_timing = true;
+       eg_pi->abm = true;
+       eg_pi->mcls = true;
+       eg_pi->light_sleep = true;
+       eg_pi->memory_transition = true;
+#if defined(CONFIG_ACPI)
+       eg_pi->pcie_performance_request =
+               radeon_acpi_is_pcie_performance_request_supported(rdev);
+#else
+       eg_pi->pcie_performance_request = false;
+#endif
+
+       eg_pi->dll_default_on = false;
+
+       eg_pi->sclk_deep_sleep = false;
+
+       pi->mclk_stutter_mode_threshold = 0;
+
+       pi->sram_end = SMC_RAM_END;
+
+       rdev->pm.dpm.dyn_state.mclk_sclk_ratio = 3;
+       rdev->pm.dpm.dyn_state.vddc_vddci_delta = 200;
+       rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2 = 900;
+       rdev->pm.dpm.dyn_state.valid_sclk_values.count = ARRAY_SIZE(btc_valid_sclk);
+       rdev->pm.dpm.dyn_state.valid_sclk_values.values = btc_valid_sclk;
+       rdev->pm.dpm.dyn_state.valid_mclk_values.count = 0;
+       rdev->pm.dpm.dyn_state.valid_mclk_values.values = NULL;
+       rdev->pm.dpm.dyn_state.sclk_mclk_delta = 12500;
+
+       ni_pi->cac_data.leakage_coefficients.at = 516;
+       ni_pi->cac_data.leakage_coefficients.bt = 18;
+       ni_pi->cac_data.leakage_coefficients.av = 51;
+       ni_pi->cac_data.leakage_coefficients.bv = 2957;
+
+       switch (rdev->pdev->device) {
+       case 0x6700:
+       case 0x6701:
+       case 0x6702:
+       case 0x6703:
+       case 0x6718:
+               ni_pi->cac_weights = &cac_weights_cayman_xt;
+               break;
+       case 0x6705:
+       case 0x6719:
+       case 0x671D:
+       case 0x671C:
+       default:
+               ni_pi->cac_weights = &cac_weights_cayman_pro;
+               break;
+       case 0x6704:
+       case 0x6706:
+       case 0x6707:
+       case 0x6708:
+       case 0x6709:
+               ni_pi->cac_weights = &cac_weights_cayman_le;
+               break;
+       }
+
+       if (ni_pi->cac_weights->enable_power_containment_by_default) {
+               ni_pi->enable_power_containment = true;
+               ni_pi->enable_cac = true;
+               ni_pi->enable_sq_ramping = true;
+       } else {
+               ni_pi->enable_power_containment = false;
+               ni_pi->enable_cac = false;
+               ni_pi->enable_sq_ramping = false;
+       }
+
+       ni_pi->driver_calculate_cac_leakage = false;
+       ni_pi->cac_configuration_required = true;
+
+       if (ni_pi->cac_configuration_required) {
+               ni_pi->support_cac_long_term_average = true;
+               ni_pi->lta_window_size = ni_pi->cac_weights->l2_lta_window_size;
+               ni_pi->lts_truncate = ni_pi->cac_weights->lts_truncate;
+       } else {
+               ni_pi->support_cac_long_term_average = false;
+               ni_pi->lta_window_size = 0;
+               ni_pi->lts_truncate = 0;
+       }
+
+       ni_pi->use_power_boost_limit = true;
+
+       return 0;
+}
+
+void ni_dpm_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               kfree(rdev->pm.dpm.ps[i].ps_priv);
+       }
+       kfree(rdev->pm.dpm.ps);
+       kfree(rdev->pm.dpm.priv);
+       kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries);
+       r600_free_extended_power_table(rdev);
+}
+
+void ni_dpm_print_power_state(struct radeon_device *rdev,
+                             struct radeon_ps *rps)
+{
+       struct ni_ps *ps = ni_get_ps(rps);
+       struct rv7xx_pl *pl;
+       int i;
+
+       r600_dpm_print_class_info(rps->class, rps->class2);
+       r600_dpm_print_cap_info(rps->caps);
+       printk("\tuvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+       for (i = 0; i < ps->performance_level_count; i++) {
+               pl = &ps->performance_levels[i];
+               if (rdev->family >= CHIP_TAHITI)
+                       printk("\t\tpower level %d    sclk: %u mclk: %u vddc: %u vddci: %u pcie gen: %u\n",
+                              i, pl->sclk, pl->mclk, pl->vddc, pl->vddci, pl->pcie_gen + 1);
+               else
+                       printk("\t\tpower level %d    sclk: %u mclk: %u vddc: %u vddci: %u\n",
+                              i, pl->sclk, pl->mclk, pl->vddc, pl->vddci);
+       }
+       r600_dpm_print_ps_status(rdev, rps);
+}
+
+void ni_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                   struct seq_file *m)
+{
+       struct radeon_ps *rps = rdev->pm.dpm.current_ps;
+       struct ni_ps *ps = ni_get_ps(rps);
+       struct rv7xx_pl *pl;
+       u32 current_index =
+               (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_INDEX_MASK) >>
+               CURRENT_STATE_INDEX_SHIFT;
+
+       if (current_index >= ps->performance_level_count) {
+               seq_printf(m, "invalid dpm profile %d\n", current_index);
+       } else {
+               pl = &ps->performance_levels[current_index];
+               seq_printf(m, "uvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+               seq_printf(m, "power level %d    sclk: %u mclk: %u vddc: %u vddci: %u\n",
+                          current_index, pl->sclk, pl->mclk, pl->vddc, pl->vddci);
+       }
+}
+
+u32 ni_dpm_get_sclk(struct radeon_device *rdev, bool low)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_ps *requested_state = ni_get_ps(&eg_pi->requested_rps);
+
+       if (low)
+               return requested_state->performance_levels[0].sclk;
+       else
+               return requested_state->performance_levels[requested_state->performance_level_count - 1].sclk;
+}
+
+u32 ni_dpm_get_mclk(struct radeon_device *rdev, bool low)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_ps *requested_state = ni_get_ps(&eg_pi->requested_rps);
+
+       if (low)
+               return requested_state->performance_levels[0].mclk;
+       else
+               return requested_state->performance_levels[requested_state->performance_level_count - 1].mclk;
+}
+
diff --git a/drivers/gpu/drm/radeon/ni_dpm.h b/drivers/gpu/drm/radeon/ni_dpm.h
new file mode 100644 (file)
index 0000000..6bbee91
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __NI_DPM_H__
+#define __NI_DPM_H__
+
+#include "cypress_dpm.h"
+#include "btc_dpm.h"
+#include "nislands_smc.h"
+
+struct ni_clock_registers {
+       u32 cg_spll_func_cntl;
+       u32 cg_spll_func_cntl_2;
+       u32 cg_spll_func_cntl_3;
+       u32 cg_spll_func_cntl_4;
+       u32 cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2;
+       u32 mclk_pwrmgt_cntl;
+       u32 dll_cntl;
+       u32 mpll_ad_func_cntl;
+       u32 mpll_ad_func_cntl_2;
+       u32 mpll_dq_func_cntl;
+       u32 mpll_dq_func_cntl_2;
+       u32 mpll_ss1;
+       u32 mpll_ss2;
+};
+
+struct ni_mc_reg_entry {
+       u32 mclk_max;
+       u32 mc_data[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct ni_mc_reg_table {
+       u8 last;
+       u8 num_entries;
+       u16 valid_flag;
+       struct ni_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+       SMC_NIslands_MCRegisterAddress mc_reg_address[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+#define NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT 2
+
+enum ni_dc_cac_level
+{
+       NISLANDS_DCCAC_LEVEL_0 = 0,
+       NISLANDS_DCCAC_LEVEL_1,
+       NISLANDS_DCCAC_LEVEL_2,
+       NISLANDS_DCCAC_LEVEL_3,
+       NISLANDS_DCCAC_LEVEL_4,
+       NISLANDS_DCCAC_LEVEL_5,
+       NISLANDS_DCCAC_LEVEL_6,
+       NISLANDS_DCCAC_LEVEL_7,
+       NISLANDS_DCCAC_MAX_LEVELS
+};
+
+struct ni_leakage_coeffients
+{
+       u32 at;
+       u32 bt;
+       u32 av;
+       u32 bv;
+       s32 t_slope;
+       s32 t_intercept;
+       u32 t_ref;
+};
+
+struct ni_cac_data
+{
+       struct ni_leakage_coeffients leakage_coefficients;
+       u32 i_leakage;
+       s32 leakage_minimum_temperature;
+       u32 pwr_const;
+       u32 dc_cac_value;
+       u32 bif_cac_value;
+       u32 lkge_pwr;
+       u8 mc_wr_weight;
+       u8 mc_rd_weight;
+       u8 allow_ovrflw;
+       u8 num_win_tdp;
+       u8 l2num_win_tdp;
+       u8 lts_truncate_n;
+};
+
+struct ni_cac_weights
+{
+       u32 weight_tcp_sig0;
+       u32 weight_tcp_sig1;
+       u32 weight_ta_sig;
+       u32 weight_tcc_en0;
+       u32 weight_tcc_en1;
+       u32 weight_tcc_en2;
+       u32 weight_cb_en0;
+       u32 weight_cb_en1;
+       u32 weight_cb_en2;
+       u32 weight_cb_en3;
+       u32 weight_db_sig0;
+       u32 weight_db_sig1;
+       u32 weight_db_sig2;
+       u32 weight_db_sig3;
+       u32 weight_sxm_sig0;
+       u32 weight_sxm_sig1;
+       u32 weight_sxm_sig2;
+       u32 weight_sxs_sig0;
+       u32 weight_sxs_sig1;
+       u32 weight_xbr_0;
+       u32 weight_xbr_1;
+       u32 weight_xbr_2;
+       u32 weight_spi_sig0;
+       u32 weight_spi_sig1;
+       u32 weight_spi_sig2;
+       u32 weight_spi_sig3;
+       u32 weight_spi_sig4;
+       u32 weight_spi_sig5;
+       u32 weight_lds_sig0;
+       u32 weight_lds_sig1;
+       u32 weight_sc;
+       u32 weight_bif;
+       u32 weight_cp;
+       u32 weight_pa_sig0;
+       u32 weight_pa_sig1;
+       u32 weight_vgt_sig0;
+       u32 weight_vgt_sig1;
+       u32 weight_vgt_sig2;
+       u32 weight_dc_sig0;
+       u32 weight_dc_sig1;
+       u32 weight_dc_sig2;
+       u32 weight_dc_sig3;
+       u32 weight_uvd_sig0;
+       u32 weight_uvd_sig1;
+       u32 weight_spare0;
+       u32 weight_spare1;
+       u32 weight_sq_vsp;
+       u32 weight_sq_vsp0;
+       u32 weight_sq_gpr;
+       u32 ovr_mode_spare_0;
+       u32 ovr_val_spare_0;
+       u32 ovr_mode_spare_1;
+       u32 ovr_val_spare_1;
+       u32 vsp;
+       u32 vsp0;
+       u32 gpr;
+       u8 mc_read_weight;
+       u8 mc_write_weight;
+       u32 tid_cnt;
+       u32 tid_unit;
+       u32 l2_lta_window_size;
+       u32 lts_truncate;
+       u32 dc_cac[NISLANDS_DCCAC_MAX_LEVELS];
+       u32 pcie_cac[SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES];
+       bool enable_power_containment_by_default;
+};
+
+struct ni_ps {
+       u16 performance_level_count;
+       bool dc_compatible;
+       struct rv7xx_pl performance_levels[NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE];
+};
+
+struct ni_power_info {
+       /* must be first! */
+       struct evergreen_power_info eg;
+       struct ni_clock_registers clock_registers;
+       struct ni_mc_reg_table mc_reg_table;
+       u32 mclk_rtt_mode_threshold;
+       /* flags */
+       bool use_power_boost_limit;
+       bool support_cac_long_term_average;
+       bool cac_enabled;
+       bool cac_configuration_required;
+       bool driver_calculate_cac_leakage;
+       bool pc_enabled;
+       bool enable_power_containment;
+       bool enable_cac;
+       bool enable_sq_ramping;
+       /* smc offsets */
+       u16 arb_table_start;
+       u16 fan_table_start;
+       u16 cac_table_start;
+       u16 spll_table_start;
+       /* CAC stuff */
+       struct ni_cac_data cac_data;
+       u32 dc_cac_table[NISLANDS_DCCAC_MAX_LEVELS];
+       const struct ni_cac_weights *cac_weights;
+       u8 lta_window_size;
+       u8 lts_truncate;
+       struct ni_ps current_ps;
+       struct ni_ps requested_ps;
+       /* scratch structs */
+       SMC_NIslands_MCRegisters smc_mc_reg_table;
+       NISLANDS_SMC_STATETABLE smc_statetable;
+};
+
+#define NISLANDS_INITIAL_STATE_ARB_INDEX    0
+#define NISLANDS_ACPI_STATE_ARB_INDEX       1
+#define NISLANDS_ULV_STATE_ARB_INDEX        2
+#define NISLANDS_DRIVER_STATE_ARB_INDEX     3
+
+#define NISLANDS_DPM2_MAX_PULSE_SKIP        256
+
+#define NISLANDS_DPM2_NEAR_TDP_DEC          10
+#define NISLANDS_DPM2_ABOVE_SAFE_INC        5
+#define NISLANDS_DPM2_BELOW_SAFE_INC        20
+
+#define NISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT            80
+
+#define NISLANDS_DPM2_MAXPS_PERCENT_H                   90
+#define NISLANDS_DPM2_MAXPS_PERCENT_M                   0
+
+#define NISLANDS_DPM2_SQ_RAMP_MAX_POWER                 0x3FFF
+#define NISLANDS_DPM2_SQ_RAMP_MIN_POWER                 0x12
+#define NISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA           0x15
+#define NISLANDS_DPM2_SQ_RAMP_STI_SIZE                  0x1E
+#define NISLANDS_DPM2_SQ_RAMP_LTI_RATIO                 0xF
+
+int ni_copy_and_switch_arb_sets(struct radeon_device *rdev,
+                               u32 arb_freq_src, u32 arb_freq_dest);
+void ni_update_current_ps(struct radeon_device *rdev,
+                         struct radeon_ps *rps);
+void ni_update_requested_ps(struct radeon_device *rdev,
+                           struct radeon_ps *rps);
+
+void ni_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev,
+                                          struct radeon_ps *new_ps,
+                                          struct radeon_ps *old_ps);
+void ni_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev,
+                                         struct radeon_ps *new_ps,
+                                         struct radeon_ps *old_ps);
+
+bool ni_dpm_vblank_too_short(struct radeon_device *rdev);
+
+#endif
index e226faf..fe24a93 100644 (file)
 #       define CACHE_FLUSH_AND_INV_EVENT_TS                     (0x14 << 0)
 #       define CACHE_FLUSH_AND_INV_EVENT                        (0x16 << 0)
 
+/* TN SMU registers */
+#define        TN_CURRENT_GNB_TEMP                             0x1F390
+
+/* pm registers */
+#define        SMC_MSG                                         0x20c
+#define                HOST_SMC_MSG(x)                         ((x) << 0)
+#define                HOST_SMC_MSG_MASK                       (0xff << 0)
+#define                HOST_SMC_MSG_SHIFT                      0
+#define                HOST_SMC_RESP(x)                        ((x) << 8)
+#define                HOST_SMC_RESP_MASK                      (0xff << 8)
+#define                HOST_SMC_RESP_SHIFT                     8
+#define                SMC_HOST_MSG(x)                         ((x) << 16)
+#define                SMC_HOST_MSG_MASK                       (0xff << 16)
+#define                SMC_HOST_MSG_SHIFT                      16
+#define                SMC_HOST_RESP(x)                        ((x) << 24)
+#define                SMC_HOST_RESP_MASK                      (0xff << 24)
+#define                SMC_HOST_RESP_SHIFT                     24
+
+#define        CG_SPLL_FUNC_CNTL                               0x600
+#define                SPLL_RESET                              (1 << 0)
+#define                SPLL_SLEEP                              (1 << 1)
+#define                SPLL_BYPASS_EN                          (1 << 3)
+#define                SPLL_REF_DIV(x)                         ((x) << 4)
+#define                SPLL_REF_DIV_MASK                       (0x3f << 4)
+#define                SPLL_PDIV_A(x)                          ((x) << 20)
+#define                SPLL_PDIV_A_MASK                        (0x7f << 20)
+#define                SPLL_PDIV_A_SHIFT                       20
+#define        CG_SPLL_FUNC_CNTL_2                             0x604
+#define                SCLK_MUX_SEL(x)                         ((x) << 0)
+#define                SCLK_MUX_SEL_MASK                       (0x1ff << 0)
+#define        CG_SPLL_FUNC_CNTL_3                             0x608
+#define                SPLL_FB_DIV(x)                          ((x) << 0)
+#define                SPLL_FB_DIV_MASK                        (0x3ffffff << 0)
+#define                SPLL_FB_DIV_SHIFT                       0
+#define                SPLL_DITHEN                             (1 << 28)
+
+#define MPLL_CNTL_MODE                                  0x61c
+#       define SS_SSEN                                  (1 << 24)
+#       define SS_DSMODE_EN                             (1 << 25)
+
+#define        MPLL_AD_FUNC_CNTL                               0x624
+#define                CLKF(x)                                 ((x) << 0)
+#define                CLKF_MASK                               (0x7f << 0)
+#define                CLKR(x)                                 ((x) << 7)
+#define                CLKR_MASK                               (0x1f << 7)
+#define                CLKFRAC(x)                              ((x) << 12)
+#define                CLKFRAC_MASK                            (0x1f << 12)
+#define                YCLK_POST_DIV(x)                        ((x) << 17)
+#define                YCLK_POST_DIV_MASK                      (3 << 17)
+#define                IBIAS(x)                                ((x) << 20)
+#define                IBIAS_MASK                              (0x3ff << 20)
+#define                RESET                                   (1 << 30)
+#define                PDNB                                    (1 << 31)
+#define        MPLL_AD_FUNC_CNTL_2                             0x628
+#define                BYPASS                                  (1 << 19)
+#define                BIAS_GEN_PDNB                           (1 << 24)
+#define                RESET_EN                                (1 << 25)
+#define                VCO_MODE                                (1 << 29)
+#define        MPLL_DQ_FUNC_CNTL                               0x62c
+#define        MPLL_DQ_FUNC_CNTL_2                             0x630
+
+#define GENERAL_PWRMGT                                  0x63c
+#       define GLOBAL_PWRMGT_EN                         (1 << 0)
+#       define STATIC_PM_EN                             (1 << 1)
+#       define THERMAL_PROTECTION_DIS                   (1 << 2)
+#       define THERMAL_PROTECTION_TYPE                  (1 << 3)
+#       define ENABLE_GEN2PCIE                          (1 << 4)
+#       define ENABLE_GEN2XSP                           (1 << 5)
+#       define SW_SMIO_INDEX(x)                         ((x) << 6)
+#       define SW_SMIO_INDEX_MASK                       (3 << 6)
+#       define SW_SMIO_INDEX_SHIFT                      6
+#       define LOW_VOLT_D2_ACPI                         (1 << 8)
+#       define LOW_VOLT_D3_ACPI                         (1 << 9)
+#       define VOLT_PWRMGT_EN                           (1 << 10)
+#       define BACKBIAS_PAD_EN                          (1 << 18)
+#       define BACKBIAS_VALUE                           (1 << 19)
+#       define DYN_SPREAD_SPECTRUM_EN                   (1 << 23)
+#       define AC_DC_SW                                 (1 << 24)
+
+#define SCLK_PWRMGT_CNTL                                  0x644
+#       define SCLK_PWRMGT_OFF                            (1 << 0)
+#       define SCLK_LOW_D1                                (1 << 1)
+#       define FIR_RESET                                  (1 << 4)
+#       define FIR_FORCE_TREND_SEL                        (1 << 5)
+#       define FIR_TREND_MODE                             (1 << 6)
+#       define DYN_GFX_CLK_OFF_EN                         (1 << 7)
+#       define GFX_CLK_FORCE_ON                           (1 << 8)
+#       define GFX_CLK_REQUEST_OFF                        (1 << 9)
+#       define GFX_CLK_FORCE_OFF                          (1 << 10)
+#       define GFX_CLK_OFF_ACPI_D1                        (1 << 11)
+#       define GFX_CLK_OFF_ACPI_D2                        (1 << 12)
+#       define GFX_CLK_OFF_ACPI_D3                        (1 << 13)
+#       define DYN_LIGHT_SLEEP_EN                         (1 << 14)
+#define        MCLK_PWRMGT_CNTL                                0x648
+#       define DLL_SPEED(x)                            ((x) << 0)
+#       define DLL_SPEED_MASK                          (0x1f << 0)
+#       define MPLL_PWRMGT_OFF                          (1 << 5)
+#       define DLL_READY                                (1 << 6)
+#       define MC_INT_CNTL                              (1 << 7)
+#       define MRDCKA0_PDNB                             (1 << 8)
+#       define MRDCKA1_PDNB                             (1 << 9)
+#       define MRDCKB0_PDNB                             (1 << 10)
+#       define MRDCKB1_PDNB                             (1 << 11)
+#       define MRDCKC0_PDNB                             (1 << 12)
+#       define MRDCKC1_PDNB                             (1 << 13)
+#       define MRDCKD0_PDNB                             (1 << 14)
+#       define MRDCKD1_PDNB                             (1 << 15)
+#       define MRDCKA0_RESET                            (1 << 16)
+#       define MRDCKA1_RESET                            (1 << 17)
+#       define MRDCKB0_RESET                            (1 << 18)
+#       define MRDCKB1_RESET                            (1 << 19)
+#       define MRDCKC0_RESET                            (1 << 20)
+#       define MRDCKC1_RESET                            (1 << 21)
+#       define MRDCKD0_RESET                            (1 << 22)
+#       define MRDCKD1_RESET                            (1 << 23)
+#       define DLL_READY_READ                           (1 << 24)
+#       define USE_DISPLAY_GAP                          (1 << 25)
+#       define USE_DISPLAY_URGENT_NORMAL                (1 << 26)
+#       define MPLL_TURNOFF_D2                          (1 << 28)
+#define        DLL_CNTL                                        0x64c
+#       define MRDCKA0_BYPASS                           (1 << 24)
+#       define MRDCKA1_BYPASS                           (1 << 25)
+#       define MRDCKB0_BYPASS                           (1 << 26)
+#       define MRDCKB1_BYPASS                           (1 << 27)
+#       define MRDCKC0_BYPASS                           (1 << 28)
+#       define MRDCKC1_BYPASS                           (1 << 29)
+#       define MRDCKD0_BYPASS                           (1 << 30)
+#       define MRDCKD1_BYPASS                           (1 << 31)
+
+#define TARGET_AND_CURRENT_PROFILE_INDEX                  0x66c
+#       define CURRENT_STATE_INDEX_MASK                   (0xf << 4)
+#       define CURRENT_STATE_INDEX_SHIFT                  4
+
+#define CG_AT                                           0x6d4
+#       define CG_R(x)                                 ((x) << 0)
+#       define CG_R_MASK                               (0xffff << 0)
+#       define CG_L(x)                                 ((x) << 16)
+#       define CG_L_MASK                               (0xffff << 16)
+
+#define        CG_BIF_REQ_AND_RSP                              0x7f4
+#define                CG_CLIENT_REQ(x)                        ((x) << 0)
+#define                CG_CLIENT_REQ_MASK                      (0xff << 0)
+#define                CG_CLIENT_REQ_SHIFT                     0
+#define                CG_CLIENT_RESP(x)                       ((x) << 8)
+#define                CG_CLIENT_RESP_MASK                     (0xff << 8)
+#define                CG_CLIENT_RESP_SHIFT                    8
+#define                CLIENT_CG_REQ(x)                        ((x) << 16)
+#define                CLIENT_CG_REQ_MASK                      (0xff << 16)
+#define                CLIENT_CG_REQ_SHIFT                     16
+#define                CLIENT_CG_RESP(x)                       ((x) << 24)
+#define                CLIENT_CG_RESP_MASK                     (0xff << 24)
+#define                CLIENT_CG_RESP_SHIFT                    24
+
+#define        CG_SPLL_SPREAD_SPECTRUM                         0x790
+#define                SSEN                                    (1 << 0)
+#define                CLK_S(x)                                ((x) << 4)
+#define                CLK_S_MASK                              (0xfff << 4)
+#define                CLK_S_SHIFT                             4
+#define        CG_SPLL_SPREAD_SPECTRUM_2                       0x794
+#define                CLK_V(x)                                ((x) << 0)
+#define                CLK_V_MASK                              (0x3ffffff << 0)
+#define                CLK_V_SHIFT                             0
+
+#define SMC_SCRATCH0                                    0x81c
+
+#define        CG_SPLL_FUNC_CNTL_4                             0x850
+
+#define        MPLL_SS1                                        0x85c
+#define                CLKV(x)                                 ((x) << 0)
+#define                CLKV_MASK                               (0x3ffffff << 0)
+#define        MPLL_SS2                                        0x860
+#define                CLKS(x)                                 ((x) << 0)
+#define                CLKS_MASK                               (0xfff << 0)
+
+#define        CG_CAC_CTRL                                     0x88c
+#define                TID_CNT(x)                              ((x) << 0)
+#define                TID_CNT_MASK                            (0x3fff << 0)
+#define                TID_UNIT(x)                             ((x) << 14)
+#define                TID_UNIT_MASK                           (0xf << 14)
+
+#define        CG_IND_ADDR                                     0x8f8
+#define        CG_IND_DATA                                     0x8fc
+/* CGIND regs */
+#define        CG_CGTT_LOCAL_0                                 0x00
+#define        CG_CGTT_LOCAL_1                                 0x01
+
+#define MC_CG_CONFIG                                    0x25bc
+#define         MCDW_WR_ENABLE                          (1 << 0)
+#define         MCDX_WR_ENABLE                          (1 << 1)
+#define         MCDY_WR_ENABLE                          (1 << 2)
+#define         MCDZ_WR_ENABLE                          (1 << 3)
+#define                MC_RD_ENABLE(x)                         ((x) << 4)
+#define                MC_RD_ENABLE_MASK                       (3 << 4)
+#define                INDEX(x)                                ((x) << 6)
+#define                INDEX_MASK                              (0xfff << 6)
+#define                INDEX_SHIFT                             6
+
+#define        MC_ARB_CAC_CNTL                                 0x2750
+#define         ENABLE                                  (1 << 0)
+#define                READ_WEIGHT(x)                          ((x) << 1)
+#define                READ_WEIGHT_MASK                        (0x3f << 1)
+#define                READ_WEIGHT_SHIFT                       1
+#define                WRITE_WEIGHT(x)                         ((x) << 7)
+#define                WRITE_WEIGHT_MASK                       (0x3f << 7)
+#define                WRITE_WEIGHT_SHIFT                      7
+#define         ALLOW_OVERFLOW                          (1 << 13)
+
+#define        MC_ARB_DRAM_TIMING                              0x2774
+#define        MC_ARB_DRAM_TIMING2                             0x2778
+
+#define        MC_ARB_RFSH_RATE                                0x27b0
+#define                POWERMODE0(x)                           ((x) << 0)
+#define                POWERMODE0_MASK                         (0xff << 0)
+#define                POWERMODE0_SHIFT                        0
+#define                POWERMODE1(x)                           ((x) << 8)
+#define                POWERMODE1_MASK                         (0xff << 8)
+#define                POWERMODE1_SHIFT                        8
+#define                POWERMODE2(x)                           ((x) << 16)
+#define                POWERMODE2_MASK                         (0xff << 16)
+#define                POWERMODE2_SHIFT                        16
+#define                POWERMODE3(x)                           ((x) << 24)
+#define                POWERMODE3_MASK                         (0xff << 24)
+#define                POWERMODE3_SHIFT                        24
+
+#define MC_ARB_CG                                       0x27e8
+#define                CG_ARB_REQ(x)                           ((x) << 0)
+#define                CG_ARB_REQ_MASK                         (0xff << 0)
+#define                CG_ARB_REQ_SHIFT                        0
+#define                CG_ARB_RESP(x)                          ((x) << 8)
+#define                CG_ARB_RESP_MASK                        (0xff << 8)
+#define                CG_ARB_RESP_SHIFT                       8
+#define                ARB_CG_REQ(x)                           ((x) << 16)
+#define                ARB_CG_REQ_MASK                         (0xff << 16)
+#define                ARB_CG_REQ_SHIFT                        16
+#define                ARB_CG_RESP(x)                          ((x) << 24)
+#define                ARB_CG_RESP_MASK                        (0xff << 24)
+#define                ARB_CG_RESP_SHIFT                       24
+
+#define        MC_ARB_DRAM_TIMING_1                            0x27f0
+#define        MC_ARB_DRAM_TIMING_2                            0x27f4
+#define        MC_ARB_DRAM_TIMING_3                            0x27f8
+#define        MC_ARB_DRAM_TIMING2_1                           0x27fc
+#define        MC_ARB_DRAM_TIMING2_2                           0x2800
+#define        MC_ARB_DRAM_TIMING2_3                           0x2804
+#define MC_ARB_BURST_TIME                               0x2808
+#define                STATE0(x)                               ((x) << 0)
+#define                STATE0_MASK                             (0x1f << 0)
+#define                STATE0_SHIFT                            0
+#define                STATE1(x)                               ((x) << 5)
+#define                STATE1_MASK                             (0x1f << 5)
+#define                STATE1_SHIFT                            5
+#define                STATE2(x)                               ((x) << 10)
+#define                STATE2_MASK                             (0x1f << 10)
+#define                STATE2_SHIFT                            10
+#define                STATE3(x)                               ((x) << 15)
+#define                STATE3_MASK                             (0x1f << 15)
+#define                STATE3_SHIFT                            15
+
+#define MC_CG_DATAPORT                                  0x2884
+
+#define MC_SEQ_RAS_TIMING                               0x28a0
+#define MC_SEQ_CAS_TIMING                               0x28a4
+#define MC_SEQ_MISC_TIMING                              0x28a8
+#define MC_SEQ_MISC_TIMING2                             0x28ac
+#define MC_SEQ_PMG_TIMING                               0x28b0
+#define MC_SEQ_RD_CTL_D0                                0x28b4
+#define MC_SEQ_RD_CTL_D1                                0x28b8
+#define MC_SEQ_WR_CTL_D0                                0x28bc
+#define MC_SEQ_WR_CTL_D1                                0x28c0
+
+#define MC_SEQ_MISC0                                    0x2a00
+#define         MC_SEQ_MISC0_GDDR5_SHIFT                28
+#define         MC_SEQ_MISC0_GDDR5_MASK                 0xf0000000
+#define         MC_SEQ_MISC0_GDDR5_VALUE                5
+#define MC_SEQ_MISC1                                    0x2a04
+#define MC_SEQ_RESERVE_M                                0x2a08
+#define MC_PMG_CMD_EMRS                                 0x2a0c
+
+#define MC_SEQ_MISC3                                    0x2a2c
+
+#define MC_SEQ_MISC5                                    0x2a54
+#define MC_SEQ_MISC6                                    0x2a58
+
+#define MC_SEQ_MISC7                                    0x2a64
+
+#define MC_SEQ_RAS_TIMING_LP                            0x2a6c
+#define MC_SEQ_CAS_TIMING_LP                            0x2a70
+#define MC_SEQ_MISC_TIMING_LP                           0x2a74
+#define MC_SEQ_MISC_TIMING2_LP                          0x2a78
+#define MC_SEQ_WR_CTL_D0_LP                             0x2a7c
+#define MC_SEQ_WR_CTL_D1_LP                             0x2a80
+#define MC_SEQ_PMG_CMD_EMRS_LP                          0x2a84
+#define MC_SEQ_PMG_CMD_MRS_LP                           0x2a88
+
+#define MC_PMG_CMD_MRS                                  0x2aac
+
+#define MC_SEQ_RD_CTL_D0_LP                             0x2b1c
+#define MC_SEQ_RD_CTL_D1_LP                             0x2b20
+
+#define MC_PMG_CMD_MRS1                                 0x2b44
+#define MC_SEQ_PMG_CMD_MRS1_LP                          0x2b48
+#define MC_SEQ_PMG_TIMING_LP                            0x2b4c
+
+#define MC_PMG_CMD_MRS2                                 0x2b5c
+#define MC_SEQ_PMG_CMD_MRS2_LP                          0x2b60
+
+#define        LB_SYNC_RESET_SEL                               0x6b28
+#define                LB_SYNC_RESET_SEL_MASK                  (3 << 0)
+#define                LB_SYNC_RESET_SEL_SHIFT                 0
+
+#define        DC_STUTTER_CNTL                                 0x6b30
+#define                DC_STUTTER_ENABLE_A                     (1 << 0)
+#define                DC_STUTTER_ENABLE_B                     (1 << 1)
+
+#define SQ_CAC_THRESHOLD                                0x8e4c
+#define                VSP(x)                                  ((x) << 0)
+#define                VSP_MASK                                (0xff << 0)
+#define                VSP_SHIFT                               0
+#define                VSP0(x)                                 ((x) << 8)
+#define                VSP0_MASK                               (0xff << 8)
+#define                VSP0_SHIFT                              8
+#define                GPR(x)                                  ((x) << 16)
+#define                GPR_MASK                                (0xff << 16)
+#define                GPR_SHIFT                               16
+
+#define SQ_POWER_THROTTLE                               0x8e58
+#define                MIN_POWER(x)                            ((x) << 0)
+#define                MIN_POWER_MASK                          (0x3fff << 0)
+#define                MIN_POWER_SHIFT                         0
+#define                MAX_POWER(x)                            ((x) << 16)
+#define                MAX_POWER_MASK                          (0x3fff << 16)
+#define                MAX_POWER_SHIFT                         0
+#define SQ_POWER_THROTTLE2                              0x8e5c
+#define                MAX_POWER_DELTA(x)                      ((x) << 0)
+#define                MAX_POWER_DELTA_MASK                    (0x3fff << 0)
+#define                MAX_POWER_DELTA_SHIFT                   0
+#define                STI_SIZE(x)                             ((x) << 16)
+#define                STI_SIZE_MASK                           (0x3ff << 16)
+#define                STI_SIZE_SHIFT                          16
+#define                LTI_RATIO(x)                            ((x) << 27)
+#define                LTI_RATIO_MASK                          (0xf << 27)
+#define                LTI_RATIO_SHIFT                         27
+
+/* CG indirect registers */
+#define CG_CAC_REGION_1_WEIGHT_0                        0x83
+#define                WEIGHT_TCP_SIG0(x)                      ((x) << 0)
+#define                WEIGHT_TCP_SIG0_MASK                    (0x3f << 0)
+#define                WEIGHT_TCP_SIG0_SHIFT                   0
+#define                WEIGHT_TCP_SIG1(x)                      ((x) << 6)
+#define                WEIGHT_TCP_SIG1_MASK                    (0x3f << 6)
+#define                WEIGHT_TCP_SIG1_SHIFT                   6
+#define                WEIGHT_TA_SIG(x)                        ((x) << 12)
+#define                WEIGHT_TA_SIG_MASK                      (0x3f << 12)
+#define                WEIGHT_TA_SIG_SHIFT                     12
+#define CG_CAC_REGION_1_WEIGHT_1                        0x84
+#define                WEIGHT_TCC_EN0(x)                       ((x) << 0)
+#define                WEIGHT_TCC_EN0_MASK                     (0x3f << 0)
+#define                WEIGHT_TCC_EN0_SHIFT                    0
+#define                WEIGHT_TCC_EN1(x)                       ((x) << 6)
+#define                WEIGHT_TCC_EN1_MASK                     (0x3f << 6)
+#define                WEIGHT_TCC_EN1_SHIFT                    6
+#define                WEIGHT_TCC_EN2(x)                       ((x) << 12)
+#define                WEIGHT_TCC_EN2_MASK                     (0x3f << 12)
+#define                WEIGHT_TCC_EN2_SHIFT                    12
+#define                WEIGHT_TCC_EN3(x)                       ((x) << 18)
+#define                WEIGHT_TCC_EN3_MASK                     (0x3f << 18)
+#define                WEIGHT_TCC_EN3_SHIFT                    18
+#define CG_CAC_REGION_2_WEIGHT_0                        0x85
+#define                WEIGHT_CB_EN0(x)                        ((x) << 0)
+#define                WEIGHT_CB_EN0_MASK                      (0x3f << 0)
+#define                WEIGHT_CB_EN0_SHIFT                     0
+#define                WEIGHT_CB_EN1(x)                        ((x) << 6)
+#define                WEIGHT_CB_EN1_MASK                      (0x3f << 6)
+#define                WEIGHT_CB_EN1_SHIFT                     6
+#define                WEIGHT_CB_EN2(x)                        ((x) << 12)
+#define                WEIGHT_CB_EN2_MASK                      (0x3f << 12)
+#define                WEIGHT_CB_EN2_SHIFT                     12
+#define                WEIGHT_CB_EN3(x)                        ((x) << 18)
+#define                WEIGHT_CB_EN3_MASK                      (0x3f << 18)
+#define                WEIGHT_CB_EN3_SHIFT                     18
+#define CG_CAC_REGION_2_WEIGHT_1                        0x86
+#define                WEIGHT_DB_SIG0(x)                       ((x) << 0)
+#define                WEIGHT_DB_SIG0_MASK                     (0x3f << 0)
+#define                WEIGHT_DB_SIG0_SHIFT                    0
+#define                WEIGHT_DB_SIG1(x)                       ((x) << 6)
+#define                WEIGHT_DB_SIG1_MASK                     (0x3f << 6)
+#define                WEIGHT_DB_SIG1_SHIFT                    6
+#define                WEIGHT_DB_SIG2(x)                       ((x) << 12)
+#define                WEIGHT_DB_SIG2_MASK                     (0x3f << 12)
+#define                WEIGHT_DB_SIG2_SHIFT                    12
+#define                WEIGHT_DB_SIG3(x)                       ((x) << 18)
+#define                WEIGHT_DB_SIG3_MASK                     (0x3f << 18)
+#define                WEIGHT_DB_SIG3_SHIFT                    18
+#define CG_CAC_REGION_2_WEIGHT_2                        0x87
+#define                WEIGHT_SXM_SIG0(x)                      ((x) << 0)
+#define                WEIGHT_SXM_SIG0_MASK                    (0x3f << 0)
+#define                WEIGHT_SXM_SIG0_SHIFT                   0
+#define                WEIGHT_SXM_SIG1(x)                      ((x) << 6)
+#define                WEIGHT_SXM_SIG1_MASK                    (0x3f << 6)
+#define                WEIGHT_SXM_SIG1_SHIFT                   6
+#define                WEIGHT_SXM_SIG2(x)                      ((x) << 12)
+#define                WEIGHT_SXM_SIG2_MASK                    (0x3f << 12)
+#define                WEIGHT_SXM_SIG2_SHIFT                   12
+#define                WEIGHT_SXS_SIG0(x)                      ((x) << 18)
+#define                WEIGHT_SXS_SIG0_MASK                    (0x3f << 18)
+#define                WEIGHT_SXS_SIG0_SHIFT                   18
+#define                WEIGHT_SXS_SIG1(x)                      ((x) << 24)
+#define                WEIGHT_SXS_SIG1_MASK                    (0x3f << 24)
+#define                WEIGHT_SXS_SIG1_SHIFT                   24
+#define CG_CAC_REGION_3_WEIGHT_0                        0x88
+#define                WEIGHT_XBR_0(x)                         ((x) << 0)
+#define                WEIGHT_XBR_0_MASK                       (0x3f << 0)
+#define                WEIGHT_XBR_0_SHIFT                      0
+#define                WEIGHT_XBR_1(x)                         ((x) << 6)
+#define                WEIGHT_XBR_1_MASK                       (0x3f << 6)
+#define                WEIGHT_XBR_1_SHIFT                      6
+#define                WEIGHT_XBR_2(x)                         ((x) << 12)
+#define                WEIGHT_XBR_2_MASK                       (0x3f << 12)
+#define                WEIGHT_XBR_2_SHIFT                      12
+#define                WEIGHT_SPI_SIG0(x)                      ((x) << 18)
+#define                WEIGHT_SPI_SIG0_MASK                    (0x3f << 18)
+#define                WEIGHT_SPI_SIG0_SHIFT                   18
+#define CG_CAC_REGION_3_WEIGHT_1                        0x89
+#define                WEIGHT_SPI_SIG1(x)                      ((x) << 0)
+#define                WEIGHT_SPI_SIG1_MASK                    (0x3f << 0)
+#define                WEIGHT_SPI_SIG1_SHIFT                   0
+#define                WEIGHT_SPI_SIG2(x)                      ((x) << 6)
+#define                WEIGHT_SPI_SIG2_MASK                    (0x3f << 6)
+#define                WEIGHT_SPI_SIG2_SHIFT                   6
+#define                WEIGHT_SPI_SIG3(x)                      ((x) << 12)
+#define                WEIGHT_SPI_SIG3_MASK                    (0x3f << 12)
+#define                WEIGHT_SPI_SIG3_SHIFT                   12
+#define                WEIGHT_SPI_SIG4(x)                      ((x) << 18)
+#define                WEIGHT_SPI_SIG4_MASK                    (0x3f << 18)
+#define                WEIGHT_SPI_SIG4_SHIFT                   18
+#define                WEIGHT_SPI_SIG5(x)                      ((x) << 24)
+#define                WEIGHT_SPI_SIG5_MASK                    (0x3f << 24)
+#define                WEIGHT_SPI_SIG5_SHIFT                   24
+#define CG_CAC_REGION_4_WEIGHT_0                        0x8a
+#define                WEIGHT_LDS_SIG0(x)                      ((x) << 0)
+#define                WEIGHT_LDS_SIG0_MASK                    (0x3f << 0)
+#define                WEIGHT_LDS_SIG0_SHIFT                   0
+#define                WEIGHT_LDS_SIG1(x)                      ((x) << 6)
+#define                WEIGHT_LDS_SIG1_MASK                    (0x3f << 6)
+#define                WEIGHT_LDS_SIG1_SHIFT                   6
+#define                WEIGHT_SC(x)                            ((x) << 24)
+#define                WEIGHT_SC_MASK                          (0x3f << 24)
+#define                WEIGHT_SC_SHIFT                         24
+#define CG_CAC_REGION_4_WEIGHT_1                        0x8b
+#define                WEIGHT_BIF(x)                           ((x) << 0)
+#define                WEIGHT_BIF_MASK                         (0x3f << 0)
+#define                WEIGHT_BIF_SHIFT                        0
+#define                WEIGHT_CP(x)                            ((x) << 6)
+#define                WEIGHT_CP_MASK                          (0x3f << 6)
+#define                WEIGHT_CP_SHIFT                         6
+#define                WEIGHT_PA_SIG0(x)                       ((x) << 12)
+#define                WEIGHT_PA_SIG0_MASK                     (0x3f << 12)
+#define                WEIGHT_PA_SIG0_SHIFT                    12
+#define                WEIGHT_PA_SIG1(x)                       ((x) << 18)
+#define                WEIGHT_PA_SIG1_MASK                     (0x3f << 18)
+#define                WEIGHT_PA_SIG1_SHIFT                    18
+#define                WEIGHT_VGT_SIG0(x)                      ((x) << 24)
+#define                WEIGHT_VGT_SIG0_MASK                    (0x3f << 24)
+#define                WEIGHT_VGT_SIG0_SHIFT                   24
+#define CG_CAC_REGION_4_WEIGHT_2                        0x8c
+#define                WEIGHT_VGT_SIG1(x)                      ((x) << 0)
+#define                WEIGHT_VGT_SIG1_MASK                    (0x3f << 0)
+#define                WEIGHT_VGT_SIG1_SHIFT                   0
+#define                WEIGHT_VGT_SIG2(x)                      ((x) << 6)
+#define                WEIGHT_VGT_SIG2_MASK                    (0x3f << 6)
+#define                WEIGHT_VGT_SIG2_SHIFT                   6
+#define                WEIGHT_DC_SIG0(x)                       ((x) << 12)
+#define                WEIGHT_DC_SIG0_MASK                     (0x3f << 12)
+#define                WEIGHT_DC_SIG0_SHIFT                    12
+#define                WEIGHT_DC_SIG1(x)                       ((x) << 18)
+#define                WEIGHT_DC_SIG1_MASK                     (0x3f << 18)
+#define                WEIGHT_DC_SIG1_SHIFT                    18
+#define                WEIGHT_DC_SIG2(x)                       ((x) << 24)
+#define                WEIGHT_DC_SIG2_MASK                     (0x3f << 24)
+#define                WEIGHT_DC_SIG2_SHIFT                    24
+#define CG_CAC_REGION_4_WEIGHT_3                        0x8d
+#define                WEIGHT_DC_SIG3(x)                       ((x) << 0)
+#define                WEIGHT_DC_SIG3_MASK                     (0x3f << 0)
+#define                WEIGHT_DC_SIG3_SHIFT                    0
+#define                WEIGHT_UVD_SIG0(x)                      ((x) << 6)
+#define                WEIGHT_UVD_SIG0_MASK                    (0x3f << 6)
+#define                WEIGHT_UVD_SIG0_SHIFT                   6
+#define                WEIGHT_UVD_SIG1(x)                      ((x) << 12)
+#define                WEIGHT_UVD_SIG1_MASK                    (0x3f << 12)
+#define                WEIGHT_UVD_SIG1_SHIFT                   12
+#define                WEIGHT_SPARE0(x)                        ((x) << 18)
+#define                WEIGHT_SPARE0_MASK                      (0x3f << 18)
+#define                WEIGHT_SPARE0_SHIFT                     18
+#define                WEIGHT_SPARE1(x)                        ((x) << 24)
+#define                WEIGHT_SPARE1_MASK                      (0x3f << 24)
+#define                WEIGHT_SPARE1_SHIFT                     24
+#define CG_CAC_REGION_5_WEIGHT_0                        0x8e
+#define                WEIGHT_SQ_VSP(x)                        ((x) << 0)
+#define                WEIGHT_SQ_VSP_MASK                      (0x3fff << 0)
+#define                WEIGHT_SQ_VSP_SHIFT                     0
+#define                WEIGHT_SQ_VSP0(x)                       ((x) << 14)
+#define                WEIGHT_SQ_VSP0_MASK                     (0x3fff << 14)
+#define                WEIGHT_SQ_VSP0_SHIFT                    14
+#define CG_CAC_REGION_4_OVERRIDE_4                      0xab
+#define                OVR_MODE_SPARE_0(x)                     ((x) << 16)
+#define                OVR_MODE_SPARE_0_MASK                   (0x1 << 16)
+#define                OVR_MODE_SPARE_0_SHIFT                  16
+#define                OVR_VAL_SPARE_0(x)                      ((x) << 17)
+#define                OVR_VAL_SPARE_0_MASK                    (0x1 << 17)
+#define                OVR_VAL_SPARE_0_SHIFT                   17
+#define                OVR_MODE_SPARE_1(x)                     ((x) << 18)
+#define                OVR_MODE_SPARE_1_MASK                   (0x3f << 18)
+#define                OVR_MODE_SPARE_1_SHIFT                  18
+#define                OVR_VAL_SPARE_1(x)                      ((x) << 19)
+#define                OVR_VAL_SPARE_1_MASK                    (0x3f << 19)
+#define                OVR_VAL_SPARE_1_SHIFT                   19
+#define CG_CAC_REGION_5_WEIGHT_1                        0xb7
+#define                WEIGHT_SQ_GPR(x)                        ((x) << 0)
+#define                WEIGHT_SQ_GPR_MASK                      (0x3fff << 0)
+#define                WEIGHT_SQ_GPR_SHIFT                     0
+#define                WEIGHT_SQ_LDS(x)                        ((x) << 14)
+#define                WEIGHT_SQ_LDS_MASK                      (0x3fff << 14)
+#define                WEIGHT_SQ_LDS_SHIFT                     14
+
+/* PCIE link stuff */
+#define PCIE_LC_TRAINING_CNTL                             0xa1 /* PCIE_P */
+#define PCIE_LC_LINK_WIDTH_CNTL                           0xa2 /* PCIE_P */
+#       define LC_LINK_WIDTH_SHIFT                        0
+#       define LC_LINK_WIDTH_MASK                         0x7
+#       define LC_LINK_WIDTH_X0                           0
+#       define LC_LINK_WIDTH_X1                           1
+#       define LC_LINK_WIDTH_X2                           2
+#       define LC_LINK_WIDTH_X4                           3
+#       define LC_LINK_WIDTH_X8                           4
+#       define LC_LINK_WIDTH_X16                          6
+#       define LC_LINK_WIDTH_RD_SHIFT                     4
+#       define LC_LINK_WIDTH_RD_MASK                      0x70
+#       define LC_RECONFIG_ARC_MISSING_ESCAPE             (1 << 7)
+#       define LC_RECONFIG_NOW                            (1 << 8)
+#       define LC_RENEGOTIATION_SUPPORT                   (1 << 9)
+#       define LC_RENEGOTIATE_EN                          (1 << 10)
+#       define LC_SHORT_RECONFIG_EN                       (1 << 11)
+#       define LC_UPCONFIGURE_SUPPORT                     (1 << 12)
+#       define LC_UPCONFIGURE_DIS                         (1 << 13)
+#define PCIE_LC_SPEED_CNTL                                0xa4 /* PCIE_P */
+#       define LC_GEN2_EN_STRAP                           (1 << 0)
+#       define LC_TARGET_LINK_SPEED_OVERRIDE_EN           (1 << 1)
+#       define LC_FORCE_EN_HW_SPEED_CHANGE                (1 << 5)
+#       define LC_FORCE_DIS_HW_SPEED_CHANGE               (1 << 6)
+#       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK      (0x3 << 8)
+#       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT     3
+#       define LC_CURRENT_DATA_RATE                       (1 << 11)
+#       define LC_HW_VOLTAGE_IF_CONTROL(x)                ((x) << 12)
+#       define LC_HW_VOLTAGE_IF_CONTROL_MASK              (3 << 12)
+#       define LC_HW_VOLTAGE_IF_CONTROL_SHIFT             12
+#       define LC_VOLTAGE_TIMER_SEL_MASK                  (0xf << 14)
+#       define LC_CLR_FAILED_SPD_CHANGE_CNT               (1 << 21)
+#       define LC_OTHER_SIDE_EVER_SENT_GEN2               (1 << 23)
+#       define LC_OTHER_SIDE_SUPPORTS_GEN2                (1 << 24)
+#define MM_CFGREGS_CNTL                                   0x544c
+#       define MM_WR_TO_CFG_EN                            (1 << 3)
+#define LINK_CNTL2                                        0x88 /* F0 */
+#       define TARGET_LINK_SPEED_MASK                     (0xf << 0)
+#       define SELECTABLE_DEEMPHASIS                      (1 << 6)
+
 /*
  * UVD
  */
diff --git a/drivers/gpu/drm/radeon/nislands_smc.h b/drivers/gpu/drm/radeon/nislands_smc.h
new file mode 100644 (file)
index 0000000..3cf8fc0
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __NISLANDS_SMC_H__
+#define __NISLANDS_SMC_H__
+
+#pragma pack(push, 1)
+
+#define NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
+
+struct PP_NIslands_Dpm2PerfLevel
+{
+    uint8_t     MaxPS;
+    uint8_t     TgtAct;
+    uint8_t     MaxPS_StepInc;
+    uint8_t     MaxPS_StepDec;
+    uint8_t     PSST;
+    uint8_t     NearTDPDec;
+    uint8_t     AboveSafeInc;
+    uint8_t     BelowSafeInc;
+    uint8_t     PSDeltaLimit;
+    uint8_t     PSDeltaWin;
+    uint8_t     Reserved[6];
+};
+
+typedef struct PP_NIslands_Dpm2PerfLevel PP_NIslands_Dpm2PerfLevel;
+
+struct PP_NIslands_DPM2Parameters
+{
+    uint32_t    TDPLimit;
+    uint32_t    NearTDPLimit;
+    uint32_t    SafePowerLimit;
+    uint32_t    PowerBoostLimit;
+};
+typedef struct PP_NIslands_DPM2Parameters PP_NIslands_DPM2Parameters;
+
+struct NISLANDS_SMC_SCLK_VALUE
+{
+    uint32_t        vCG_SPLL_FUNC_CNTL;
+    uint32_t        vCG_SPLL_FUNC_CNTL_2;
+    uint32_t        vCG_SPLL_FUNC_CNTL_3;
+    uint32_t        vCG_SPLL_FUNC_CNTL_4;
+    uint32_t        vCG_SPLL_SPREAD_SPECTRUM;
+    uint32_t        vCG_SPLL_SPREAD_SPECTRUM_2;
+    uint32_t        sclk_value;
+};
+
+typedef struct NISLANDS_SMC_SCLK_VALUE NISLANDS_SMC_SCLK_VALUE;
+
+struct NISLANDS_SMC_MCLK_VALUE
+{
+    uint32_t        vMPLL_FUNC_CNTL;
+    uint32_t        vMPLL_FUNC_CNTL_1;
+    uint32_t        vMPLL_FUNC_CNTL_2;
+    uint32_t        vMPLL_AD_FUNC_CNTL;
+    uint32_t        vMPLL_AD_FUNC_CNTL_2;
+    uint32_t        vMPLL_DQ_FUNC_CNTL;
+    uint32_t        vMPLL_DQ_FUNC_CNTL_2;
+    uint32_t        vMCLK_PWRMGT_CNTL;
+    uint32_t        vDLL_CNTL;
+    uint32_t        vMPLL_SS;
+    uint32_t        vMPLL_SS2;
+    uint32_t        mclk_value;
+};
+
+typedef struct NISLANDS_SMC_MCLK_VALUE NISLANDS_SMC_MCLK_VALUE;
+
+struct NISLANDS_SMC_VOLTAGE_VALUE
+{
+    uint16_t             value;
+    uint8_t              index;
+    uint8_t              padding;
+};
+
+typedef struct NISLANDS_SMC_VOLTAGE_VALUE NISLANDS_SMC_VOLTAGE_VALUE;
+
+struct NISLANDS_SMC_HW_PERFORMANCE_LEVEL
+{
+    uint8_t                     arbValue;
+    uint8_t                     ACIndex;
+    uint8_t                     displayWatermark;
+    uint8_t                     gen2PCIE;
+    uint8_t                     reserved1;
+    uint8_t                     reserved2;
+    uint8_t                     strobeMode;
+    uint8_t                     mcFlags;
+    uint32_t                    aT;
+    uint32_t                    bSP;
+    NISLANDS_SMC_SCLK_VALUE     sclk;
+    NISLANDS_SMC_MCLK_VALUE     mclk;
+    NISLANDS_SMC_VOLTAGE_VALUE  vddc;
+    NISLANDS_SMC_VOLTAGE_VALUE  mvdd;
+    NISLANDS_SMC_VOLTAGE_VALUE  vddci;
+    NISLANDS_SMC_VOLTAGE_VALUE  std_vddc;
+    uint32_t                    powergate_en;
+    uint8_t                     hUp;
+    uint8_t                     hDown;
+    uint8_t                     stateFlags;
+    uint8_t                     arbRefreshState;
+    uint32_t                    SQPowerThrottle;
+    uint32_t                    SQPowerThrottle_2;
+    uint32_t                    reserved[2];
+    PP_NIslands_Dpm2PerfLevel   dpm2;
+};
+
+#define NISLANDS_SMC_STROBE_RATIO    0x0F
+#define NISLANDS_SMC_STROBE_ENABLE   0x10
+
+#define NISLANDS_SMC_MC_EDC_RD_FLAG  0x01
+#define NISLANDS_SMC_MC_EDC_WR_FLAG  0x02
+#define NISLANDS_SMC_MC_RTT_ENABLE   0x04
+#define NISLANDS_SMC_MC_STUTTER_EN   0x08
+
+typedef struct NISLANDS_SMC_HW_PERFORMANCE_LEVEL NISLANDS_SMC_HW_PERFORMANCE_LEVEL;
+
+struct NISLANDS_SMC_SWSTATE
+{
+    uint8_t                             flags;
+    uint8_t                             levelCount;
+    uint8_t                             padding2;
+    uint8_t                             padding3;
+    NISLANDS_SMC_HW_PERFORMANCE_LEVEL   levels[1];
+};
+
+typedef struct NISLANDS_SMC_SWSTATE NISLANDS_SMC_SWSTATE;
+
+#define NISLANDS_SMC_VOLTAGEMASK_VDDC  0
+#define NISLANDS_SMC_VOLTAGEMASK_MVDD  1
+#define NISLANDS_SMC_VOLTAGEMASK_VDDCI 2
+#define NISLANDS_SMC_VOLTAGEMASK_MAX   4
+
+struct NISLANDS_SMC_VOLTAGEMASKTABLE
+{
+    uint8_t  highMask[NISLANDS_SMC_VOLTAGEMASK_MAX];
+    uint32_t lowMask[NISLANDS_SMC_VOLTAGEMASK_MAX];
+};
+
+typedef struct NISLANDS_SMC_VOLTAGEMASKTABLE NISLANDS_SMC_VOLTAGEMASKTABLE;
+
+#define NISLANDS_MAX_NO_VREG_STEPS 32
+
+struct NISLANDS_SMC_STATETABLE
+{
+    uint8_t                             thermalProtectType;
+    uint8_t                             systemFlags;
+    uint8_t                             maxVDDCIndexInPPTable;
+    uint8_t                             extraFlags;
+    uint8_t                             highSMIO[NISLANDS_MAX_NO_VREG_STEPS];
+    uint32_t                            lowSMIO[NISLANDS_MAX_NO_VREG_STEPS];
+    NISLANDS_SMC_VOLTAGEMASKTABLE       voltageMaskTable;
+    PP_NIslands_DPM2Parameters          dpm2Params;
+    NISLANDS_SMC_SWSTATE                initialState;
+    NISLANDS_SMC_SWSTATE                ACPIState;
+    NISLANDS_SMC_SWSTATE                ULVState;
+    NISLANDS_SMC_SWSTATE                driverState;
+    NISLANDS_SMC_HW_PERFORMANCE_LEVEL   dpmLevels[NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1];
+};
+
+typedef struct NISLANDS_SMC_STATETABLE NISLANDS_SMC_STATETABLE;
+
+#define NI_SMC_SOFT_REGISTERS_START        0x108
+
+#define NI_SMC_SOFT_REGISTER_mclk_chg_timeout        0x0
+#define NI_SMC_SOFT_REGISTER_delay_bbias             0xC
+#define NI_SMC_SOFT_REGISTER_delay_vreg              0x10
+#define NI_SMC_SOFT_REGISTER_delay_acpi              0x2C
+#define NI_SMC_SOFT_REGISTER_seq_index               0x64
+#define NI_SMC_SOFT_REGISTER_mvdd_chg_time           0x68
+#define NI_SMC_SOFT_REGISTER_mclk_switch_lim         0x78
+#define NI_SMC_SOFT_REGISTER_watermark_threshold     0x80
+#define NI_SMC_SOFT_REGISTER_mc_block_delay          0x84
+#define NI_SMC_SOFT_REGISTER_uvd_enabled             0x98
+
+#define SMC_NISLANDS_MC_TPP_CAC_NUM_OF_ENTRIES 16
+#define SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES 16
+#define SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES 16
+#define SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES 4
+
+struct SMC_NISLANDS_MC_TPP_CAC_TABLE
+{
+    uint32_t    tpp[SMC_NISLANDS_MC_TPP_CAC_NUM_OF_ENTRIES];
+    uint32_t    cacValue[SMC_NISLANDS_MC_TPP_CAC_NUM_OF_ENTRIES];
+};
+
+typedef struct SMC_NISLANDS_MC_TPP_CAC_TABLE SMC_NISLANDS_MC_TPP_CAC_TABLE;
+
+
+struct PP_NIslands_CACTABLES
+{
+    uint32_t                cac_bif_lut[SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES];
+    uint32_t                cac_lkge_lut[SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES][SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES];
+
+    uint32_t                pwr_const;
+
+    uint32_t                dc_cacValue;
+    uint32_t                bif_cacValue;
+    uint32_t                lkge_pwr;
+
+    uint8_t                 cac_width;
+    uint8_t                 window_size_p2;
+
+    uint8_t                 num_drop_lsb;
+    uint8_t                 padding_0;
+
+    uint32_t                last_power;
+
+    uint8_t                 AllowOvrflw;
+    uint8_t                 MCWrWeight;
+    uint8_t                 MCRdWeight;
+    uint8_t                 padding_1[9];
+
+    uint8_t                 enableWinAvg;
+    uint8_t                 numWin_TDP;
+    uint8_t                 l2numWin_TDP;
+    uint8_t                 WinIndex;
+
+    uint32_t                dynPwr_TDP[4];
+    uint32_t                lkgePwr_TDP[4];
+    uint32_t                power_TDP[4];
+    uint32_t                avg_dynPwr_TDP;
+    uint32_t                avg_lkgePwr_TDP;
+    uint32_t                avg_power_TDP;
+    uint32_t                lts_power_TDP;
+    uint8_t                 lts_truncate_n;
+    uint8_t                 padding_2[7];
+};
+
+typedef struct PP_NIslands_CACTABLES PP_NIslands_CACTABLES;
+
+#define SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE 32
+#define SMC_NISLANDS_MC_REGISTER_ARRAY_SET_COUNT 20
+
+struct SMC_NIslands_MCRegisterAddress
+{
+    uint16_t s0;
+    uint16_t s1;
+};
+
+typedef struct SMC_NIslands_MCRegisterAddress SMC_NIslands_MCRegisterAddress;
+
+
+struct SMC_NIslands_MCRegisterSet
+{
+    uint32_t value[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+typedef struct SMC_NIslands_MCRegisterSet SMC_NIslands_MCRegisterSet;
+
+struct SMC_NIslands_MCRegisters
+{
+    uint8_t                             last;
+    uint8_t                             reserved[3];
+    SMC_NIslands_MCRegisterAddress      address[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+    SMC_NIslands_MCRegisterSet          data[SMC_NISLANDS_MC_REGISTER_ARRAY_SET_COUNT];
+};
+
+typedef struct SMC_NIslands_MCRegisters SMC_NIslands_MCRegisters;
+
+struct SMC_NIslands_MCArbDramTimingRegisterSet
+{
+    uint32_t mc_arb_dram_timing;
+    uint32_t mc_arb_dram_timing2;
+    uint8_t  mc_arb_rfsh_rate;
+    uint8_t  padding[3];
+};
+
+typedef struct SMC_NIslands_MCArbDramTimingRegisterSet SMC_NIslands_MCArbDramTimingRegisterSet;
+
+struct SMC_NIslands_MCArbDramTimingRegisters
+{
+    uint8_t                                     arb_current;
+    uint8_t                                     reserved[3];
+    SMC_NIslands_MCArbDramTimingRegisterSet     data[20];
+};
+
+typedef struct SMC_NIslands_MCArbDramTimingRegisters SMC_NIslands_MCArbDramTimingRegisters;
+
+struct SMC_NISLANDS_SPLL_DIV_TABLE
+{
+    uint32_t    freq[256];
+    uint32_t    ss[256];
+};
+
+#define SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_MASK  0x01ffffff
+#define SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT 0
+#define SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_MASK   0xfe000000
+#define SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT  25
+#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_MASK   0x000fffff
+#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT  0
+#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK   0xfff00000
+#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT  20
+
+typedef struct SMC_NISLANDS_SPLL_DIV_TABLE SMC_NISLANDS_SPLL_DIV_TABLE;
+
+#define NISLANDS_SMC_FIRMWARE_HEADER_LOCATION 0x100
+
+#define NISLANDS_SMC_FIRMWARE_HEADER_version                   0x0
+#define NISLANDS_SMC_FIRMWARE_HEADER_flags                     0x4
+#define NISLANDS_SMC_FIRMWARE_HEADER_softRegisters             0x8
+#define NISLANDS_SMC_FIRMWARE_HEADER_stateTable                0xC
+#define NISLANDS_SMC_FIRMWARE_HEADER_fanTable                  0x10
+#define NISLANDS_SMC_FIRMWARE_HEADER_cacTable                  0x14
+#define NISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable           0x20
+#define NISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable 0x2C
+#define NISLANDS_SMC_FIRMWARE_HEADER_spllTable                 0x30
+
+#pragma pack(pop)
+
+#endif
+
diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h
new file mode 100644 (file)
index 0000000..b5564a3
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef PP_SMC_H
+#define PP_SMC_H
+
+#pragma pack(push, 1)
+
+#define PPSMC_SWSTATE_FLAG_DC                           0x01
+#define PPSMC_SWSTATE_FLAG_UVD                          0x02
+#define PPSMC_SWSTATE_FLAG_VCE                          0x04
+#define PPSMC_SWSTATE_FLAG_PCIE_X1                      0x08
+
+#define PPSMC_THERMAL_PROTECT_TYPE_INTERNAL             0x00
+#define PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL             0x01
+#define PPSMC_THERMAL_PROTECT_TYPE_NONE                 0xff
+
+#define PPSMC_SYSTEMFLAG_GPIO_DC                        0x01
+#define PPSMC_SYSTEMFLAG_STEPVDDC                       0x02
+#define PPSMC_SYSTEMFLAG_GDDR5                          0x04
+#define PPSMC_SYSTEMFLAG_DISABLE_BABYSTEP               0x08
+#define PPSMC_SYSTEMFLAG_REGULATOR_HOT                  0x10
+#define PPSMC_SYSTEMFLAG_REGULATOR_HOT_ANALOG           0x20
+#define PPSMC_SYSTEMFLAG_REGULATOR_HOT_PROG_GPIO        0x40
+
+#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_MASK              0x07
+#define PPSMC_EXTRAFLAGS_AC2DC_DONT_WAIT_FOR_VBLANK     0x08
+#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTODPMLOWSTATE   0x00
+#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTOINITIALSTATE  0x01
+#define PPSMC_EXTRAFLAGS_AC2DC_GPIO5_POLARITY_HIGH      0x02
+
+#define PPSMC_DISPLAY_WATERMARK_LOW                     0
+#define PPSMC_DISPLAY_WATERMARK_HIGH                    1
+
+#define PPSMC_STATEFLAG_AUTO_PULSE_SKIP    0x01
+#define PPSMC_STATEFLAG_POWERBOOST         0x02
+#define PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE 0x20
+#define PPSMC_STATEFLAG_DEEPSLEEP_BYPASS   0x40
+
+#define PPSMC_Result_OK             ((uint8_t)0x01)
+#define PPSMC_Result_Failed         ((uint8_t)0xFF)
+
+typedef uint8_t PPSMC_Result;
+
+#define PPSMC_MSG_Halt                      ((uint8_t)0x10)
+#define PPSMC_MSG_Resume                    ((uint8_t)0x11)
+#define PPSMC_MSG_ZeroLevelsDisabled        ((uint8_t)0x13)
+#define PPSMC_MSG_OneLevelsDisabled         ((uint8_t)0x14)
+#define PPSMC_MSG_TwoLevelsDisabled         ((uint8_t)0x15)
+#define PPSMC_MSG_EnableThermalInterrupt    ((uint8_t)0x16)
+#define PPSMC_MSG_RunningOnAC               ((uint8_t)0x17)
+#define PPSMC_MSG_SwitchToSwState           ((uint8_t)0x20)
+#define PPSMC_MSG_SwitchToInitialState      ((uint8_t)0x40)
+#define PPSMC_MSG_NoForcedLevel             ((uint8_t)0x41)
+#define PPSMC_MSG_ForceHigh                 ((uint8_t)0x42)
+#define PPSMC_MSG_ForceMediumOrHigh         ((uint8_t)0x43)
+#define PPSMC_MSG_SwitchToMinimumPower      ((uint8_t)0x51)
+#define PPSMC_MSG_ResumeFromMinimumPower    ((uint8_t)0x52)
+#define PPSMC_MSG_EnableCac                 ((uint8_t)0x53)
+#define PPSMC_MSG_DisableCac                ((uint8_t)0x54)
+#define PPSMC_TDPClampingActive             ((uint8_t)0x59)
+#define PPSMC_TDPClampingInactive           ((uint8_t)0x5A)
+#define PPSMC_MSG_NoDisplay                 ((uint8_t)0x5D)
+#define PPSMC_MSG_HasDisplay                ((uint8_t)0x5E)
+#define PPSMC_MSG_UVDPowerOFF               ((uint8_t)0x60)
+#define PPSMC_MSG_UVDPowerON                ((uint8_t)0x61)
+#define PPSMC_MSG_EnableULV                 ((uint8_t)0x62)
+#define PPSMC_MSG_DisableULV                ((uint8_t)0x63)
+#define PPSMC_MSG_EnterULV                  ((uint8_t)0x64)
+#define PPSMC_MSG_ExitULV                   ((uint8_t)0x65)
+#define PPSMC_CACLongTermAvgEnable          ((uint8_t)0x6E)
+#define PPSMC_CACLongTermAvgDisable         ((uint8_t)0x6F)
+#define PPSMC_MSG_CollectCAC_PowerCorreln   ((uint8_t)0x7A)
+#define PPSMC_FlushDataCache                ((uint8_t)0x80)
+#define PPSMC_MSG_SetEnabledLevels          ((uint8_t)0x82)
+#define PPSMC_MSG_SetForcedLevels           ((uint8_t)0x83)
+#define PPSMC_MSG_ResetToDefaults           ((uint8_t)0x84)
+#define PPSMC_MSG_EnableDTE                 ((uint8_t)0x87)
+#define PPSMC_MSG_DisableDTE                ((uint8_t)0x88)
+#define PPSMC_MSG_ThrottleOVRDSCLKDS        ((uint8_t)0x96)
+#define PPSMC_MSG_CancelThrottleOVRDSCLKDS  ((uint8_t)0x97)
+
+/* TN */
+#define PPSMC_MSG_DPM_Config                ((uint32_t) 0x102)
+#define PPSMC_MSG_DPM_ForceState            ((uint32_t) 0x104)
+#define PPSMC_MSG_PG_SIMD_Config            ((uint32_t) 0x108)
+#define PPSMC_MSG_DPM_N_LevelsDisabled      ((uint32_t) 0x112)
+#define PPSMC_MSG_DCE_RemoveVoltageAdjustment   ((uint32_t) 0x11d)
+#define PPSMC_MSG_DCE_AllowVoltageAdjustment    ((uint32_t) 0x11e)
+#define PPSMC_MSG_UVD_DPM_Config            ((uint32_t) 0x124)
+
+
+typedef uint16_t PPSMC_Msg;
+
+#pragma pack(pop)
+
+#endif
index d0314ec..c9affef 100644 (file)
@@ -3077,6 +3077,10 @@ int r100_set_surface_reg(struct radeon_device *rdev, int reg,
                        flags |= RADEON_SURF_TILE_COLOR_BOTH;
                if (tiling_flags & RADEON_TILING_MACRO)
                        flags |= RADEON_SURF_TILE_COLOR_MACRO;
+               /* setting pitch to 0 disables tiling */
+               if ((tiling_flags & (RADEON_TILING_MACRO|RADEON_TILING_MICRO))
+                               == 0)
+                       pitch = 0;
        } else if (rdev->family <= CHIP_RV280) {
                if (tiling_flags & (RADEON_TILING_MACRO))
                        flags |= R200_SURF_TILE_COLOR_MACRO;
@@ -3094,13 +3098,6 @@ int r100_set_surface_reg(struct radeon_device *rdev, int reg,
        if (tiling_flags & RADEON_TILING_SWAP_32BIT)
                flags |= RADEON_SURF_AP0_SWP_32BPP | RADEON_SURF_AP1_SWP_32BPP;
 
-       /* when we aren't tiling the pitch seems to needs to be furtherdivided down. - tested on power5 + rn50 server */
-       if (tiling_flags & (RADEON_TILING_SWAP_16BIT | RADEON_TILING_SWAP_32BIT)) {
-               if (!(tiling_flags & (RADEON_TILING_MACRO | RADEON_TILING_MICRO)))
-                       if (ASIC_IS_RN50(rdev))
-                               pitch /= 16;
-       }
-
        /* r100/r200 divide by 16 */
        if (rdev->family < CHIP_R300)
                flags |= pitch / 16;
index 6948eb8..2d3655f 100644 (file)
 #include "r600d.h"
 #include "atom.h"
 #include "avivod.h"
-
-#define PFP_UCODE_SIZE 576
-#define PM4_UCODE_SIZE 1792
-#define RLC_UCODE_SIZE 768
-#define R700_PFP_UCODE_SIZE 848
-#define R700_PM4_UCODE_SIZE 1360
-#define R700_RLC_UCODE_SIZE 1024
-#define EVERGREEN_PFP_UCODE_SIZE 1120
-#define EVERGREEN_PM4_UCODE_SIZE 1376
-#define EVERGREEN_RLC_UCODE_SIZE 768
-#define CAYMAN_RLC_UCODE_SIZE 1024
-#define ARUBA_RLC_UCODE_SIZE 1536
+#include "radeon_ucode.h"
 
 /* Firmware Names */
 MODULE_FIRMWARE("radeon/R600_pfp.bin");
@@ -68,24 +57,32 @@ MODULE_FIRMWARE("radeon/RS780_pfp.bin");
 MODULE_FIRMWARE("radeon/RS780_me.bin");
 MODULE_FIRMWARE("radeon/RV770_pfp.bin");
 MODULE_FIRMWARE("radeon/RV770_me.bin");
+MODULE_FIRMWARE("radeon/RV770_smc.bin");
 MODULE_FIRMWARE("radeon/RV730_pfp.bin");
 MODULE_FIRMWARE("radeon/RV730_me.bin");
+MODULE_FIRMWARE("radeon/RV730_smc.bin");
+MODULE_FIRMWARE("radeon/RV740_smc.bin");
 MODULE_FIRMWARE("radeon/RV710_pfp.bin");
 MODULE_FIRMWARE("radeon/RV710_me.bin");
+MODULE_FIRMWARE("radeon/RV710_smc.bin");
 MODULE_FIRMWARE("radeon/R600_rlc.bin");
 MODULE_FIRMWARE("radeon/R700_rlc.bin");
 MODULE_FIRMWARE("radeon/CEDAR_pfp.bin");
 MODULE_FIRMWARE("radeon/CEDAR_me.bin");
 MODULE_FIRMWARE("radeon/CEDAR_rlc.bin");
+MODULE_FIRMWARE("radeon/CEDAR_smc.bin");
 MODULE_FIRMWARE("radeon/REDWOOD_pfp.bin");
 MODULE_FIRMWARE("radeon/REDWOOD_me.bin");
 MODULE_FIRMWARE("radeon/REDWOOD_rlc.bin");
+MODULE_FIRMWARE("radeon/REDWOOD_smc.bin");
 MODULE_FIRMWARE("radeon/JUNIPER_pfp.bin");
 MODULE_FIRMWARE("radeon/JUNIPER_me.bin");
 MODULE_FIRMWARE("radeon/JUNIPER_rlc.bin");
+MODULE_FIRMWARE("radeon/JUNIPER_smc.bin");
 MODULE_FIRMWARE("radeon/CYPRESS_pfp.bin");
 MODULE_FIRMWARE("radeon/CYPRESS_me.bin");
 MODULE_FIRMWARE("radeon/CYPRESS_rlc.bin");
+MODULE_FIRMWARE("radeon/CYPRESS_smc.bin");
 MODULE_FIRMWARE("radeon/PALM_pfp.bin");
 MODULE_FIRMWARE("radeon/PALM_me.bin");
 MODULE_FIRMWARE("radeon/SUMO_rlc.bin");
@@ -108,6 +105,7 @@ static void r600_gpu_init(struct radeon_device *rdev);
 void r600_fini(struct radeon_device *rdev);
 void r600_irq_disable(struct radeon_device *rdev);
 static void r600_pcie_gen2_enable(struct radeon_device *rdev);
+extern int evergreen_rlc_resume(struct radeon_device *rdev);
 
 /**
  * r600_get_xclk - get the xclk
@@ -2149,7 +2147,8 @@ int r600_init_microcode(struct radeon_device *rdev)
        struct platform_device *pdev;
        const char *chip_name;
        const char *rlc_chip_name;
-       size_t pfp_req_size, me_req_size, rlc_req_size;
+       const char *smc_chip_name = "RV770";
+       size_t pfp_req_size, me_req_size, rlc_req_size, smc_req_size = 0;
        char fw_name[30];
        int err;
 
@@ -2195,32 +2194,51 @@ int r600_init_microcode(struct radeon_device *rdev)
        case CHIP_RV770:
                chip_name = "RV770";
                rlc_chip_name = "R700";
+               smc_chip_name = "RV770";
+               smc_req_size = ALIGN(RV770_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_RV730:
-       case CHIP_RV740:
                chip_name = "RV730";
                rlc_chip_name = "R700";
+               smc_chip_name = "RV730";
+               smc_req_size = ALIGN(RV730_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_RV710:
                chip_name = "RV710";
                rlc_chip_name = "R700";
+               smc_chip_name = "RV710";
+               smc_req_size = ALIGN(RV710_SMC_UCODE_SIZE, 4);
+               break;
+       case CHIP_RV740:
+               chip_name = "RV730";
+               rlc_chip_name = "R700";
+               smc_chip_name = "RV740";
+               smc_req_size = ALIGN(RV740_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_CEDAR:
                chip_name = "CEDAR";
                rlc_chip_name = "CEDAR";
+               smc_chip_name = "CEDAR";
+               smc_req_size = ALIGN(CEDAR_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_REDWOOD:
                chip_name = "REDWOOD";
                rlc_chip_name = "REDWOOD";
+               smc_chip_name = "REDWOOD";
+               smc_req_size = ALIGN(REDWOOD_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_JUNIPER:
                chip_name = "JUNIPER";
                rlc_chip_name = "JUNIPER";
+               smc_chip_name = "JUNIPER";
+               smc_req_size = ALIGN(JUNIPER_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_CYPRESS:
        case CHIP_HEMLOCK:
                chip_name = "CYPRESS";
                rlc_chip_name = "CYPRESS";
+               smc_chip_name = "CYPRESS";
+               smc_req_size = ALIGN(CYPRESS_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_PALM:
                chip_name = "PALM";
@@ -2246,9 +2264,9 @@ int r600_init_microcode(struct radeon_device *rdev)
                me_req_size = R700_PM4_UCODE_SIZE * 4;
                rlc_req_size = R700_RLC_UCODE_SIZE * 4;
        } else {
-               pfp_req_size = PFP_UCODE_SIZE * 4;
-               me_req_size = PM4_UCODE_SIZE * 12;
-               rlc_req_size = RLC_UCODE_SIZE * 4;
+               pfp_req_size = R600_PFP_UCODE_SIZE * 4;
+               me_req_size = R600_PM4_UCODE_SIZE * 12;
+               rlc_req_size = R600_RLC_UCODE_SIZE * 4;
        }
 
        DRM_INFO("Loading %s Microcode\n", chip_name);
@@ -2287,6 +2305,19 @@ int r600_init_microcode(struct radeon_device *rdev)
                err = -EINVAL;
        }
 
+       if ((rdev->family >= CHIP_RV770) && (rdev->family <= CHIP_HEMLOCK)) {
+               snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", smc_chip_name);
+               err = request_firmware(&rdev->smc_fw, fw_name, &pdev->dev);
+               if (err)
+                       goto out;
+               if (rdev->smc_fw->size != smc_req_size) {
+                       printk(KERN_ERR
+                              "smc: Bogus length %zu in firmware \"%s\"\n",
+                              rdev->smc_fw->size, fw_name);
+                       err = -EINVAL;
+               }
+       }
+
 out:
        platform_device_unregister(pdev);
 
@@ -2301,6 +2332,8 @@ out:
                rdev->me_fw = NULL;
                release_firmware(rdev->rlc_fw);
                rdev->rlc_fw = NULL;
+               release_firmware(rdev->smc_fw);
+               rdev->smc_fw = NULL;
        }
        return err;
 }
@@ -2331,13 +2364,13 @@ static int r600_cp_load_microcode(struct radeon_device *rdev)
 
        fw_data = (const __be32 *)rdev->me_fw->data;
        WREG32(CP_ME_RAM_WADDR, 0);
-       for (i = 0; i < PM4_UCODE_SIZE * 3; i++)
+       for (i = 0; i < R600_PM4_UCODE_SIZE * 3; i++)
                WREG32(CP_ME_RAM_DATA,
                       be32_to_cpup(fw_data++));
 
        fw_data = (const __be32 *)rdev->pfp_fw->data;
        WREG32(CP_PFP_UCODE_ADDR, 0);
-       for (i = 0; i < PFP_UCODE_SIZE; i++)
+       for (i = 0; i < R600_PFP_UCODE_SIZE; i++)
                WREG32(CP_PFP_UCODE_DATA,
                       be32_to_cpup(fw_data++));
 
@@ -3789,7 +3822,7 @@ static void r600_rlc_start(struct radeon_device *rdev)
        WREG32(RLC_CNTL, RLC_ENABLE);
 }
 
-static int r600_rlc_init(struct radeon_device *rdev)
+static int r600_rlc_resume(struct radeon_device *rdev)
 {
        u32 i;
        const __be32 *fw_data;
@@ -3801,45 +3834,22 @@ static int r600_rlc_init(struct radeon_device *rdev)
 
        WREG32(RLC_HB_CNTL, 0);
 
-       if (rdev->family == CHIP_ARUBA) {
-               WREG32(TN_RLC_SAVE_AND_RESTORE_BASE, rdev->rlc.save_restore_gpu_addr >> 8);
-               WREG32(TN_RLC_CLEAR_STATE_RESTORE_BASE, rdev->rlc.clear_state_gpu_addr >> 8);
-       }
-       if (rdev->family <= CHIP_CAYMAN) {
-               WREG32(RLC_HB_BASE, 0);
-               WREG32(RLC_HB_RPTR, 0);
-               WREG32(RLC_HB_WPTR, 0);
-       }
-       if (rdev->family <= CHIP_CAICOS) {
-               WREG32(RLC_HB_WPTR_LSB_ADDR, 0);
-               WREG32(RLC_HB_WPTR_MSB_ADDR, 0);
-       }
+       WREG32(RLC_HB_BASE, 0);
+       WREG32(RLC_HB_RPTR, 0);
+       WREG32(RLC_HB_WPTR, 0);
+       WREG32(RLC_HB_WPTR_LSB_ADDR, 0);
+       WREG32(RLC_HB_WPTR_MSB_ADDR, 0);
        WREG32(RLC_MC_CNTL, 0);
        WREG32(RLC_UCODE_CNTL, 0);
 
        fw_data = (const __be32 *)rdev->rlc_fw->data;
-       if (rdev->family >= CHIP_ARUBA) {
-               for (i = 0; i < ARUBA_RLC_UCODE_SIZE; i++) {
-                       WREG32(RLC_UCODE_ADDR, i);
-                       WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
-               }
-       } else if (rdev->family >= CHIP_CAYMAN) {
-               for (i = 0; i < CAYMAN_RLC_UCODE_SIZE; i++) {
-                       WREG32(RLC_UCODE_ADDR, i);
-                       WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
-               }
-       } else if (rdev->family >= CHIP_CEDAR) {
-               for (i = 0; i < EVERGREEN_RLC_UCODE_SIZE; i++) {
-                       WREG32(RLC_UCODE_ADDR, i);
-                       WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
-               }
-       } else if (rdev->family >= CHIP_RV770) {
+       if (rdev->family >= CHIP_RV770) {
                for (i = 0; i < R700_RLC_UCODE_SIZE; i++) {
                        WREG32(RLC_UCODE_ADDR, i);
                        WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
                }
        } else {
-               for (i = 0; i < RLC_UCODE_SIZE; i++) {
+               for (i = 0; i < R600_RLC_UCODE_SIZE; i++) {
                        WREG32(RLC_UCODE_ADDR, i);
                        WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
                }
@@ -3947,7 +3957,10 @@ int r600_irq_init(struct radeon_device *rdev)
        r600_disable_interrupts(rdev);
 
        /* init rlc */
-       ret = r600_rlc_init(rdev);
+       if (rdev->family >= CHIP_CEDAR)
+               ret = evergreen_rlc_resume(rdev);
+       else
+               ret = r600_rlc_resume(rdev);
        if (ret) {
                r600_ih_ring_fini(rdev);
                return ret;
@@ -4028,6 +4041,7 @@ int r600_irq_set(struct radeon_device *rdev)
        u32 hdmi0, hdmi1;
        u32 d1grph = 0, d2grph = 0;
        u32 dma_cntl;
+       u32 thermal_int = 0;
 
        if (!rdev->irq.installed) {
                WARN(1, "Can't enable IRQ/MSI because no handler is installed\n");
@@ -4062,8 +4076,21 @@ int r600_irq_set(struct radeon_device *rdev)
                hdmi0 = RREG32(HDMI0_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK;
                hdmi1 = RREG32(HDMI1_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK;
        }
+
        dma_cntl = RREG32(DMA_CNTL) & ~TRAP_ENABLE;
 
+       if ((rdev->family > CHIP_R600) && (rdev->family < CHIP_RV770)) {
+               thermal_int = RREG32(CG_THERMAL_INT) &
+                       ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
+       } else if (rdev->family >= CHIP_RV770) {
+               thermal_int = RREG32(RV770_CG_THERMAL_INT) &
+                       ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
+       }
+       if (rdev->irq.dpm_thermal) {
+               DRM_DEBUG("dpm thermal\n");
+               thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW;
+       }
+
        if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) {
                DRM_DEBUG("r600_irq_set: sw int\n");
                cp_int_cntl |= RB_INT_ENABLE;
@@ -4145,6 +4172,11 @@ int r600_irq_set(struct radeon_device *rdev)
                WREG32(HDMI0_AUDIO_PACKET_CONTROL, hdmi0);
                WREG32(HDMI1_AUDIO_PACKET_CONTROL, hdmi1);
        }
+       if ((rdev->family > CHIP_R600) && (rdev->family < CHIP_RV770)) {
+               WREG32(CG_THERMAL_INT, thermal_int);
+       } else if (rdev->family >= CHIP_RV770) {
+               WREG32(RV770_CG_THERMAL_INT, thermal_int);
+       }
 
        return 0;
 }
@@ -4336,6 +4368,7 @@ int r600_irq_process(struct radeon_device *rdev)
        u32 ring_index;
        bool queue_hotplug = false;
        bool queue_hdmi = false;
+       bool queue_thermal = false;
 
        if (!rdev->ih.enabled || rdev->shutdown)
                return IRQ_NONE;
@@ -4503,6 +4536,16 @@ restart_ih:
                        DRM_DEBUG("IH: DMA trap\n");
                        radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX);
                        break;
+               case 230: /* thermal low to high */
+                       DRM_DEBUG("IH: thermal low to high\n");
+                       rdev->pm.dpm.thermal.high_to_low = false;
+                       queue_thermal = true;
+                       break;
+               case 231: /* thermal high to low */
+                       DRM_DEBUG("IH: thermal high to low\n");
+                       rdev->pm.dpm.thermal.high_to_low = true;
+                       queue_thermal = true;
+                       break;
                case 233: /* GUI IDLE */
                        DRM_DEBUG("IH: GUI idle\n");
                        break;
@@ -4519,6 +4562,8 @@ restart_ih:
                schedule_work(&rdev->hotplug_work);
        if (queue_hdmi)
                schedule_work(&rdev->audio_work);
+       if (queue_thermal && rdev->pm.dpm_enabled)
+               schedule_work(&rdev->pm.dpm.thermal.work);
        rdev->ih.rptr = rptr;
        WREG32(IH_RB_RPTR, rdev->ih.rptr);
        atomic_set(&rdev->ih.lock, 0);
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c
new file mode 100644 (file)
index 0000000..b88f54b
--- /dev/null
@@ -0,0 +1,1048 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "r600d.h"
+#include "r600_dpm.h"
+#include "atom.h"
+
+const u32 r600_utc[R600_PM_NUMBER_OF_TC] =
+{
+       R600_UTC_DFLT_00,
+       R600_UTC_DFLT_01,
+       R600_UTC_DFLT_02,
+       R600_UTC_DFLT_03,
+       R600_UTC_DFLT_04,
+       R600_UTC_DFLT_05,
+       R600_UTC_DFLT_06,
+       R600_UTC_DFLT_07,
+       R600_UTC_DFLT_08,
+       R600_UTC_DFLT_09,
+       R600_UTC_DFLT_10,
+       R600_UTC_DFLT_11,
+       R600_UTC_DFLT_12,
+       R600_UTC_DFLT_13,
+       R600_UTC_DFLT_14,
+};
+
+const u32 r600_dtc[R600_PM_NUMBER_OF_TC] =
+{
+       R600_DTC_DFLT_00,
+       R600_DTC_DFLT_01,
+       R600_DTC_DFLT_02,
+       R600_DTC_DFLT_03,
+       R600_DTC_DFLT_04,
+       R600_DTC_DFLT_05,
+       R600_DTC_DFLT_06,
+       R600_DTC_DFLT_07,
+       R600_DTC_DFLT_08,
+       R600_DTC_DFLT_09,
+       R600_DTC_DFLT_10,
+       R600_DTC_DFLT_11,
+       R600_DTC_DFLT_12,
+       R600_DTC_DFLT_13,
+       R600_DTC_DFLT_14,
+};
+
+void r600_dpm_print_class_info(u32 class, u32 class2)
+{
+       printk("\tui class: ");
+       switch (class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) {
+       case ATOM_PPLIB_CLASSIFICATION_UI_NONE:
+       default:
+               printk("none\n");
+               break;
+       case ATOM_PPLIB_CLASSIFICATION_UI_BATTERY:
+               printk("battery\n");
+               break;
+       case ATOM_PPLIB_CLASSIFICATION_UI_BALANCED:
+               printk("balanced\n");
+               break;
+       case ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE:
+               printk("performance\n");
+               break;
+       }
+       printk("\tinternal class: ");
+       if (((class & ~ATOM_PPLIB_CLASSIFICATION_UI_MASK) == 0) &&
+           (class2 == 0))
+               printk("none");
+       else {
+               if (class & ATOM_PPLIB_CLASSIFICATION_BOOT)
+                       printk("boot ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
+                       printk("thermal ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE)
+                       printk("limited_pwr ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_REST)
+                       printk("rest ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_FORCED)
+                       printk("forced ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)
+                       printk("3d_perf ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE)
+                       printk("ovrdrv ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+                       printk("uvd ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_3DLOW)
+                       printk("3d_low ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_ACPI)
+                       printk("acpi ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)
+                       printk("uvd_hd2 ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)
+                       printk("uvd_hd ");
+               if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)
+                       printk("uvd_sd ");
+               if (class2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2)
+                       printk("limited_pwr2 ");
+               if (class2 & ATOM_PPLIB_CLASSIFICATION2_ULV)
+                       printk("ulv ");
+               if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)
+                       printk("uvd_mvc ");
+       }
+       printk("\n");
+}
+
+void r600_dpm_print_cap_info(u32 caps)
+{
+       printk("\tcaps: ");
+       if (caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY)
+               printk("single_disp ");
+       if (caps & ATOM_PPLIB_SUPPORTS_VIDEO_PLAYBACK)
+               printk("video ");
+       if (caps & ATOM_PPLIB_DISALLOW_ON_DC)
+               printk("no_dc ");
+       printk("\n");
+}
+
+void r600_dpm_print_ps_status(struct radeon_device *rdev,
+                             struct radeon_ps *rps)
+{
+       printk("\tstatus: ");
+       if (rps == rdev->pm.dpm.current_ps)
+               printk("c ");
+       if (rps == rdev->pm.dpm.requested_ps)
+               printk("r ");
+       if (rps == rdev->pm.dpm.boot_ps)
+               printk("b ");
+       printk("\n");
+}
+
+u32 r600_dpm_get_vblank_time(struct radeon_device *rdev)
+{
+       struct drm_device *dev = rdev->ddev;
+       struct drm_crtc *crtc;
+       struct radeon_crtc *radeon_crtc;
+       u32 line_time_us, vblank_lines;
+       u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               radeon_crtc = to_radeon_crtc(crtc);
+               if (crtc->enabled && radeon_crtc->enabled && radeon_crtc->hw_mode.clock) {
+                       line_time_us = (radeon_crtc->hw_mode.crtc_htotal * 1000) /
+                               radeon_crtc->hw_mode.clock;
+                       vblank_lines = radeon_crtc->hw_mode.crtc_vblank_end -
+                               radeon_crtc->hw_mode.crtc_vdisplay +
+                               (radeon_crtc->v_border * 2);
+                       vblank_time_us = vblank_lines * line_time_us;
+                       break;
+               }
+       }
+
+       return vblank_time_us;
+}
+
+void r600_calculate_u_and_p(u32 i, u32 r_c, u32 p_b,
+                           u32 *p, u32 *u)
+{
+       u32 b_c = 0;
+       u32 i_c;
+       u32 tmp;
+
+       i_c = (i * r_c) / 100;
+       tmp = i_c >> p_b;
+
+       while (tmp) {
+               b_c++;
+               tmp >>= 1;
+       }
+
+       *u = (b_c + 1) / 2;
+       *p = i_c / (1 << (2 * (*u)));
+}
+
+int r600_calculate_at(u32 t, u32 h, u32 fh, u32 fl, u32 *tl, u32 *th)
+{
+       u32 k, a, ah, al;
+       u32 t1;
+
+       if ((fl == 0) || (fh == 0) || (fl > fh))
+               return -EINVAL;
+
+       k = (100 * fh) / fl;
+       t1 = (t * (k - 100));
+       a = (1000 * (100 * h + t1)) / (10000 + (t1 / 100));
+       a = (a + 5) / 10;
+       ah = ((a * t) + 5000) / 10000;
+       al = a - ah;
+
+       *th = t - ah;
+       *tl = t + al;
+
+       return 0;
+}
+
+void r600_gfx_clockgating_enable(struct radeon_device *rdev, bool enable)
+{
+       int i;
+
+       if (enable) {
+               WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN);
+       } else {
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN);
+
+               WREG32(CG_RLC_REQ_AND_RSP, 0x2);
+
+               for (i = 0; i < rdev->usec_timeout; i++) {
+                       if (((RREG32(CG_RLC_REQ_AND_RSP) & CG_RLC_RSP_TYPE_MASK) >> CG_RLC_RSP_TYPE_SHIFT) == 1)
+                               break;
+                       udelay(1);
+               }
+
+               WREG32(CG_RLC_REQ_AND_RSP, 0x0);
+
+               WREG32(GRBM_PWR_CNTL, 0x1);
+               RREG32(GRBM_PWR_CNTL);
+       }
+}
+
+void r600_dynamicpm_enable(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN);
+}
+
+void r600_enable_thermal_protection(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
+       else
+               WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
+}
+
+void r600_enable_acpi_pm(struct radeon_device *rdev)
+{
+       WREG32_P(GENERAL_PWRMGT, STATIC_PM_EN, ~STATIC_PM_EN);
+}
+
+void r600_enable_dynamic_pcie_gen2(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, ENABLE_GEN2PCIE, ~ENABLE_GEN2PCIE);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE);
+}
+
+bool r600_dynamicpm_enabled(struct radeon_device *rdev)
+{
+       if (RREG32(GENERAL_PWRMGT) & GLOBAL_PWRMGT_EN)
+               return true;
+       else
+               return false;
+}
+
+void r600_enable_sclk_control(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, 0, ~SCLK_PWRMGT_OFF);
+       else
+               WREG32_P(GENERAL_PWRMGT, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF);
+}
+
+void r600_enable_mclk_control(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(MCLK_PWRMGT_CNTL, 0, ~MPLL_PWRMGT_OFF);
+       else
+               WREG32_P(MCLK_PWRMGT_CNTL, MPLL_PWRMGT_OFF, ~MPLL_PWRMGT_OFF);
+}
+
+void r600_enable_spll_bypass(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(CG_SPLL_FUNC_CNTL, SPLL_BYPASS_EN, ~SPLL_BYPASS_EN);
+       else
+               WREG32_P(CG_SPLL_FUNC_CNTL, 0, ~SPLL_BYPASS_EN);
+}
+
+void r600_wait_for_spll_change(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(CG_SPLL_FUNC_CNTL) & SPLL_CHG_STATUS)
+                       break;
+               udelay(1);
+       }
+}
+
+void r600_set_bsp(struct radeon_device *rdev, u32 u, u32 p)
+{
+       WREG32(CG_BSP, BSP(p) | BSU(u));
+}
+
+void r600_set_at(struct radeon_device *rdev,
+                u32 l_to_m, u32 m_to_h,
+                u32 h_to_m, u32 m_to_l)
+{
+       WREG32(CG_RT, FLS(l_to_m) | FMS(m_to_h));
+       WREG32(CG_LT, FHS(h_to_m) | FMS(m_to_l));
+}
+
+void r600_set_tc(struct radeon_device *rdev,
+                u32 index, u32 u_t, u32 d_t)
+{
+       WREG32(CG_FFCT_0 + (index * 4), UTC_0(u_t) | DTC_0(d_t));
+}
+
+void r600_select_td(struct radeon_device *rdev,
+                   enum r600_td td)
+{
+       if (td == R600_TD_AUTO)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_FORCE_TREND_SEL);
+       else
+               WREG32_P(SCLK_PWRMGT_CNTL, FIR_FORCE_TREND_SEL, ~FIR_FORCE_TREND_SEL);
+       if (td == R600_TD_UP)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_TREND_MODE);
+       if (td == R600_TD_DOWN)
+               WREG32_P(SCLK_PWRMGT_CNTL, FIR_TREND_MODE, ~FIR_TREND_MODE);
+}
+
+void r600_set_vrc(struct radeon_device *rdev, u32 vrv)
+{
+       WREG32(CG_FTV, vrv);
+}
+
+void r600_set_tpu(struct radeon_device *rdev, u32 u)
+{
+       WREG32_P(CG_TPC, TPU(u), ~TPU_MASK);
+}
+
+void r600_set_tpc(struct radeon_device *rdev, u32 c)
+{
+       WREG32_P(CG_TPC, TPCC(c), ~TPCC_MASK);
+}
+
+void r600_set_sstu(struct radeon_device *rdev, u32 u)
+{
+       WREG32_P(CG_SSP, CG_SSTU(u), ~CG_SSTU_MASK);
+}
+
+void r600_set_sst(struct radeon_device *rdev, u32 t)
+{
+       WREG32_P(CG_SSP, CG_SST(t), ~CG_SST_MASK);
+}
+
+void r600_set_git(struct radeon_device *rdev, u32 t)
+{
+       WREG32_P(CG_GIT, CG_GICST(t), ~CG_GICST_MASK);
+}
+
+void r600_set_fctu(struct radeon_device *rdev, u32 u)
+{
+       WREG32_P(CG_FC_T, FC_TU(u), ~FC_TU_MASK);
+}
+
+void r600_set_fct(struct radeon_device *rdev, u32 t)
+{
+       WREG32_P(CG_FC_T, FC_T(t), ~FC_T_MASK);
+}
+
+void r600_set_ctxcgtt3d_rphc(struct radeon_device *rdev, u32 p)
+{
+       WREG32_P(CG_CTX_CGTT3D_R, PHC(p), ~PHC_MASK);
+}
+
+void r600_set_ctxcgtt3d_rsdc(struct radeon_device *rdev, u32 s)
+{
+       WREG32_P(CG_CTX_CGTT3D_R, SDC(s), ~SDC_MASK);
+}
+
+void r600_set_vddc3d_oorsu(struct radeon_device *rdev, u32 u)
+{
+       WREG32_P(CG_VDDC3D_OOR, SU(u), ~SU_MASK);
+}
+
+void r600_set_vddc3d_oorphc(struct radeon_device *rdev, u32 p)
+{
+       WREG32_P(CG_VDDC3D_OOR, PHC(p), ~PHC_MASK);
+}
+
+void r600_set_vddc3d_oorsdc(struct radeon_device *rdev, u32 s)
+{
+       WREG32_P(CG_VDDC3D_OOR, SDC(s), ~SDC_MASK);
+}
+
+void r600_set_mpll_lock_time(struct radeon_device *rdev, u32 lock_time)
+{
+       WREG32_P(MPLL_TIME, MPLL_LOCK_TIME(lock_time), ~MPLL_LOCK_TIME_MASK);
+}
+
+void r600_set_mpll_reset_time(struct radeon_device *rdev, u32 reset_time)
+{
+       WREG32_P(MPLL_TIME, MPLL_RESET_TIME(reset_time), ~MPLL_RESET_TIME_MASK);
+}
+
+void r600_engine_clock_entry_enable(struct radeon_device *rdev,
+                                   u32 index, bool enable)
+{
+       if (enable)
+               WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2),
+                        STEP_0_SPLL_ENTRY_VALID, ~STEP_0_SPLL_ENTRY_VALID);
+       else
+               WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2),
+                        0, ~STEP_0_SPLL_ENTRY_VALID);
+}
+
+void r600_engine_clock_entry_enable_pulse_skipping(struct radeon_device *rdev,
+                                                  u32 index, bool enable)
+{
+       if (enable)
+               WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2),
+                        STEP_0_SPLL_STEP_ENABLE, ~STEP_0_SPLL_STEP_ENABLE);
+       else
+               WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2),
+                        0, ~STEP_0_SPLL_STEP_ENABLE);
+}
+
+void r600_engine_clock_entry_enable_post_divider(struct radeon_device *rdev,
+                                                u32 index, bool enable)
+{
+       if (enable)
+               WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2),
+                        STEP_0_POST_DIV_EN, ~STEP_0_POST_DIV_EN);
+       else
+               WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2),
+                        0, ~STEP_0_POST_DIV_EN);
+}
+
+void r600_engine_clock_entry_set_post_divider(struct radeon_device *rdev,
+                                             u32 index, u32 divider)
+{
+       WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART1 + (index * 4 * 2),
+                STEP_0_SPLL_POST_DIV(divider), ~STEP_0_SPLL_POST_DIV_MASK);
+}
+
+void r600_engine_clock_entry_set_reference_divider(struct radeon_device *rdev,
+                                                  u32 index, u32 divider)
+{
+       WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART1 + (index * 4 * 2),
+                STEP_0_SPLL_REF_DIV(divider), ~STEP_0_SPLL_REF_DIV_MASK);
+}
+
+void r600_engine_clock_entry_set_feedback_divider(struct radeon_device *rdev,
+                                                 u32 index, u32 divider)
+{
+       WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART1 + (index * 4 * 2),
+                STEP_0_SPLL_FB_DIV(divider), ~STEP_0_SPLL_FB_DIV_MASK);
+}
+
+void r600_engine_clock_entry_set_step_time(struct radeon_device *rdev,
+                                          u32 index, u32 step_time)
+{
+       WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART1 + (index * 4 * 2),
+                STEP_0_SPLL_STEP_TIME(step_time), ~STEP_0_SPLL_STEP_TIME_MASK);
+}
+
+void r600_vid_rt_set_ssu(struct radeon_device *rdev, u32 u)
+{
+       WREG32_P(VID_RT, SSTU(u), ~SSTU_MASK);
+}
+
+void r600_vid_rt_set_vru(struct radeon_device *rdev, u32 u)
+{
+       WREG32_P(VID_RT, VID_CRTU(u), ~VID_CRTU_MASK);
+}
+
+void r600_vid_rt_set_vrt(struct radeon_device *rdev, u32 rt)
+{
+       WREG32_P(VID_RT, VID_CRT(rt), ~VID_CRT_MASK);
+}
+
+void r600_voltage_control_enable_pins(struct radeon_device *rdev,
+                                     u64 mask)
+{
+       WREG32(LOWER_GPIO_ENABLE, mask & 0xffffffff);
+       WREG32(UPPER_GPIO_ENABLE, upper_32_bits(mask));
+}
+
+
+void r600_voltage_control_program_voltages(struct radeon_device *rdev,
+                                          enum r600_power_level index, u64 pins)
+{
+       u32 tmp, mask;
+       u32 ix = 3 - (3 & index);
+
+       WREG32(CTXSW_VID_LOWER_GPIO_CNTL + (ix * 4), pins & 0xffffffff);
+
+       mask = 7 << (3 * ix);
+       tmp = RREG32(VID_UPPER_GPIO_CNTL);
+       tmp = (tmp & ~mask) | ((pins >> (32 - (3 * ix))) & mask);
+       WREG32(VID_UPPER_GPIO_CNTL, tmp);
+}
+
+void r600_voltage_control_deactivate_static_control(struct radeon_device *rdev,
+                                                   u64 mask)
+{
+       u32 gpio;
+
+       gpio = RREG32(GPIOPAD_MASK);
+       gpio &= ~mask;
+       WREG32(GPIOPAD_MASK, gpio);
+
+       gpio = RREG32(GPIOPAD_EN);
+       gpio &= ~mask;
+       WREG32(GPIOPAD_EN, gpio);
+
+       gpio = RREG32(GPIOPAD_A);
+       gpio &= ~mask;
+       WREG32(GPIOPAD_A, gpio);
+}
+
+void r600_power_level_enable(struct radeon_device *rdev,
+                            enum r600_power_level index, bool enable)
+{
+       u32 ix = 3 - (3 & index);
+
+       if (enable)
+               WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4), CTXSW_FREQ_STATE_ENABLE,
+                        ~CTXSW_FREQ_STATE_ENABLE);
+       else
+               WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4), 0,
+                        ~CTXSW_FREQ_STATE_ENABLE);
+}
+
+void r600_power_level_set_voltage_index(struct radeon_device *rdev,
+                                       enum r600_power_level index, u32 voltage_index)
+{
+       u32 ix = 3 - (3 & index);
+
+       WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4),
+                CTXSW_FREQ_VIDS_CFG_INDEX(voltage_index), ~CTXSW_FREQ_VIDS_CFG_INDEX_MASK);
+}
+
+void r600_power_level_set_mem_clock_index(struct radeon_device *rdev,
+                                         enum r600_power_level index, u32 mem_clock_index)
+{
+       u32 ix = 3 - (3 & index);
+
+       WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4),
+                CTXSW_FREQ_MCLK_CFG_INDEX(mem_clock_index), ~CTXSW_FREQ_MCLK_CFG_INDEX_MASK);
+}
+
+void r600_power_level_set_eng_clock_index(struct radeon_device *rdev,
+                                         enum r600_power_level index, u32 eng_clock_index)
+{
+       u32 ix = 3 - (3 & index);
+
+       WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4),
+                CTXSW_FREQ_SCLK_CFG_INDEX(eng_clock_index), ~CTXSW_FREQ_SCLK_CFG_INDEX_MASK);
+}
+
+void r600_power_level_set_watermark_id(struct radeon_device *rdev,
+                                      enum r600_power_level index,
+                                      enum r600_display_watermark watermark_id)
+{
+       u32 ix = 3 - (3 & index);
+       u32 tmp = 0;
+
+       if (watermark_id == R600_DISPLAY_WATERMARK_HIGH)
+               tmp = CTXSW_FREQ_DISPLAY_WATERMARK;
+       WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4), tmp, ~CTXSW_FREQ_DISPLAY_WATERMARK);
+}
+
+void r600_power_level_set_pcie_gen2(struct radeon_device *rdev,
+                                   enum r600_power_level index, bool compatible)
+{
+       u32 ix = 3 - (3 & index);
+       u32 tmp = 0;
+
+       if (compatible)
+               tmp = CTXSW_FREQ_GEN2PCIE_VOLT;
+       WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4), tmp, ~CTXSW_FREQ_GEN2PCIE_VOLT);
+}
+
+enum r600_power_level r600_power_level_get_current_index(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_PROFILE_INDEX_MASK;
+       tmp >>= CURRENT_PROFILE_INDEX_SHIFT;
+       return tmp;
+}
+
+enum r600_power_level r600_power_level_get_target_index(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & TARGET_PROFILE_INDEX_MASK;
+       tmp >>= TARGET_PROFILE_INDEX_SHIFT;
+       return tmp;
+}
+
+void r600_power_level_set_enter_index(struct radeon_device *rdev,
+                                     enum r600_power_level index)
+{
+       WREG32_P(TARGET_AND_CURRENT_PROFILE_INDEX, DYN_PWR_ENTER_INDEX(index),
+                ~DYN_PWR_ENTER_INDEX_MASK);
+}
+
+void r600_wait_for_power_level_unequal(struct radeon_device *rdev,
+                                      enum r600_power_level index)
+{
+       int i;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (r600_power_level_get_target_index(rdev) != index)
+                       break;
+               udelay(1);
+       }
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (r600_power_level_get_current_index(rdev) != index)
+                       break;
+               udelay(1);
+       }
+}
+
+void r600_wait_for_power_level(struct radeon_device *rdev,
+                              enum r600_power_level index)
+{
+       int i;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (r600_power_level_get_target_index(rdev) == index)
+                       break;
+               udelay(1);
+       }
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (r600_power_level_get_current_index(rdev) == index)
+                       break;
+               udelay(1);
+       }
+}
+
+void r600_start_dpm(struct radeon_device *rdev)
+{
+       r600_enable_sclk_control(rdev, false);
+       r600_enable_mclk_control(rdev, false);
+
+       r600_dynamicpm_enable(rdev, true);
+
+       radeon_wait_for_vblank(rdev, 0);
+       radeon_wait_for_vblank(rdev, 1);
+
+       r600_enable_spll_bypass(rdev, true);
+       r600_wait_for_spll_change(rdev);
+       r600_enable_spll_bypass(rdev, false);
+       r600_wait_for_spll_change(rdev);
+
+       r600_enable_spll_bypass(rdev, true);
+       r600_wait_for_spll_change(rdev);
+       r600_enable_spll_bypass(rdev, false);
+       r600_wait_for_spll_change(rdev);
+
+       r600_enable_sclk_control(rdev, true);
+       r600_enable_mclk_control(rdev, true);
+}
+
+void r600_stop_dpm(struct radeon_device *rdev)
+{
+       r600_dynamicpm_enable(rdev, false);
+}
+
+int r600_dpm_pre_set_power_state(struct radeon_device *rdev)
+{
+       return 0;
+}
+
+void r600_dpm_post_set_power_state(struct radeon_device *rdev)
+{
+
+}
+
+bool r600_is_uvd_state(u32 class, u32 class2)
+{
+       if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+               return true;
+       if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)
+               return true;
+       if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)
+               return true;
+       if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)
+               return true;
+       if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)
+               return true;
+       return false;
+}
+
+int r600_set_thermal_temperature_range(struct radeon_device *rdev,
+                                      int min_temp, int max_temp)
+{
+       int low_temp = 0 * 1000;
+       int high_temp = 255 * 1000;
+
+       if (low_temp < min_temp)
+               low_temp = min_temp;
+       if (high_temp > max_temp)
+               high_temp = max_temp;
+       if (high_temp < low_temp) {
+               DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp);
+               return -EINVAL;
+       }
+
+       WREG32_P(CG_THERMAL_INT, DIG_THERM_INTH(high_temp / 1000), ~DIG_THERM_INTH_MASK);
+       WREG32_P(CG_THERMAL_INT, DIG_THERM_INTL(low_temp / 1000), ~DIG_THERM_INTL_MASK);
+       WREG32_P(CG_THERMAL_CTRL, DIG_THERM_DPM(high_temp / 1000), ~DIG_THERM_DPM_MASK);
+
+       rdev->pm.dpm.thermal.min_temp = low_temp;
+       rdev->pm.dpm.thermal.max_temp = high_temp;
+
+       return 0;
+}
+
+bool r600_is_internal_thermal_sensor(enum radeon_int_thermal_type sensor)
+{
+       switch (sensor) {
+       case THERMAL_TYPE_RV6XX:
+       case THERMAL_TYPE_RV770:
+       case THERMAL_TYPE_EVERGREEN:
+       case THERMAL_TYPE_SUMO:
+       case THERMAL_TYPE_NI:
+       case THERMAL_TYPE_SI:
+               return true;
+       case THERMAL_TYPE_ADT7473_WITH_INTERNAL:
+       case THERMAL_TYPE_EMC2103_WITH_INTERNAL:
+               return false; /* need special handling */
+       case THERMAL_TYPE_NONE:
+       case THERMAL_TYPE_EXTERNAL:
+       case THERMAL_TYPE_EXTERNAL_GPIO:
+       default:
+               return false;
+       }
+}
+
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+       struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+       struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE4 pplib4;
+       struct _ATOM_PPLIB_POWERPLAYTABLE5 pplib5;
+};
+
+union fan_info {
+       struct _ATOM_PPLIB_FANTABLE fan;
+       struct _ATOM_PPLIB_FANTABLE2 fan2;
+};
+
+static int r600_parse_clk_voltage_dep_table(struct radeon_clock_voltage_dependency_table *radeon_table,
+                                           ATOM_PPLIB_Clock_Voltage_Dependency_Table *atom_table)
+{
+       u32 size = atom_table->ucNumEntries *
+               sizeof(struct radeon_clock_voltage_dependency_entry);
+       int i;
+
+       radeon_table->entries = kzalloc(size, GFP_KERNEL);
+       if (!radeon_table->entries)
+               return -ENOMEM;
+
+       for (i = 0; i < atom_table->ucNumEntries; i++) {
+               radeon_table->entries[i].clk = le16_to_cpu(atom_table->entries[i].usClockLow) |
+                       (atom_table->entries[i].ucClockHigh << 16);
+               radeon_table->entries[i].v = le16_to_cpu(atom_table->entries[i].usVoltage);
+       }
+       radeon_table->count = atom_table->ucNumEntries;
+
+       return 0;
+}
+
+/* sizeof(ATOM_PPLIB_EXTENDEDHEADER) */
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2 12
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3 14
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4 16
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5 18
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6 20
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7 22
+
+int r600_parse_extended_power_table(struct radeon_device *rdev)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       union power_info *power_info;
+       union fan_info *fan_info;
+       ATOM_PPLIB_Clock_Voltage_Dependency_Table *dep_table;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+        u16 data_offset;
+       u8 frev, crev;
+       int ret, i;
+
+       if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset))
+               return -EINVAL;
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       /* fan table */
+       if (le16_to_cpu(power_info->pplib.usTableSize) >=
+           sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE3)) {
+               if (power_info->pplib3.usFanTableOffset) {
+                       fan_info = (union fan_info *)(mode_info->atom_context->bios + data_offset +
+                                                     le16_to_cpu(power_info->pplib3.usFanTableOffset));
+                       rdev->pm.dpm.fan.t_hyst = fan_info->fan.ucTHyst;
+                       rdev->pm.dpm.fan.t_min = le16_to_cpu(fan_info->fan.usTMin);
+                       rdev->pm.dpm.fan.t_med = le16_to_cpu(fan_info->fan.usTMed);
+                       rdev->pm.dpm.fan.t_high = le16_to_cpu(fan_info->fan.usTHigh);
+                       rdev->pm.dpm.fan.pwm_min = le16_to_cpu(fan_info->fan.usPWMMin);
+                       rdev->pm.dpm.fan.pwm_med = le16_to_cpu(fan_info->fan.usPWMMed);
+                       rdev->pm.dpm.fan.pwm_high = le16_to_cpu(fan_info->fan.usPWMHigh);
+                       if (fan_info->fan.ucFanTableFormat >= 2)
+                               rdev->pm.dpm.fan.t_max = le16_to_cpu(fan_info->fan2.usTMax);
+                       else
+                               rdev->pm.dpm.fan.t_max = 10900;
+                       rdev->pm.dpm.fan.cycle_delay = 100000;
+                       rdev->pm.dpm.fan.ucode_fan_control = true;
+               }
+       }
+
+       /* clock dependancy tables, shedding tables */
+       if (le16_to_cpu(power_info->pplib.usTableSize) >=
+           sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE4)) {
+               if (power_info->pplib4.usVddcDependencyOnSCLKOffset) {
+                       dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)
+                               (mode_info->atom_context->bios + data_offset +
+                                le16_to_cpu(power_info->pplib4.usVddcDependencyOnSCLKOffset));
+                       ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                                              dep_table);
+                       if (ret)
+                               return ret;
+               }
+               if (power_info->pplib4.usVddciDependencyOnMCLKOffset) {
+                       dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)
+                               (mode_info->atom_context->bios + data_offset +
+                                le16_to_cpu(power_info->pplib4.usVddciDependencyOnMCLKOffset));
+                       ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                              dep_table);
+                       if (ret) {
+                               kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries);
+                               return ret;
+                       }
+               }
+               if (power_info->pplib4.usVddcDependencyOnMCLKOffset) {
+                       dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *)
+                               (mode_info->atom_context->bios + data_offset +
+                                le16_to_cpu(power_info->pplib4.usVddcDependencyOnMCLKOffset));
+                       ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                              dep_table);
+                       if (ret) {
+                               kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries);
+                               kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries);
+                               return ret;
+                       }
+               }
+               if (power_info->pplib4.usMaxClockVoltageOnDCOffset) {
+                       ATOM_PPLIB_Clock_Voltage_Limit_Table *clk_v =
+                               (ATOM_PPLIB_Clock_Voltage_Limit_Table *)
+                               (mode_info->atom_context->bios + data_offset +
+                                le16_to_cpu(power_info->pplib4.usMaxClockVoltageOnDCOffset));
+                       if (clk_v->ucNumEntries) {
+                               rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk =
+                                       le16_to_cpu(clk_v->entries[0].usSclkLow) |
+                                       (clk_v->entries[0].ucSclkHigh << 16);
+                               rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.mclk =
+                                       le16_to_cpu(clk_v->entries[0].usMclkLow) |
+                                       (clk_v->entries[0].ucMclkHigh << 16);
+                               rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc =
+                                       le16_to_cpu(clk_v->entries[0].usVddc);
+                               rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddci =
+                                       le16_to_cpu(clk_v->entries[0].usVddci);
+                       }
+               }
+               if (power_info->pplib4.usVddcPhaseShedLimitsTableOffset) {
+                       ATOM_PPLIB_PhaseSheddingLimits_Table *psl =
+                               (ATOM_PPLIB_PhaseSheddingLimits_Table *)
+                               (mode_info->atom_context->bios + data_offset +
+                                le16_to_cpu(power_info->pplib4.usVddcPhaseShedLimitsTableOffset));
+
+                       rdev->pm.dpm.dyn_state.phase_shedding_limits_table.entries =
+                               kzalloc(psl->ucNumEntries *
+                                       sizeof(struct radeon_phase_shedding_limits_entry),
+                                       GFP_KERNEL);
+                       if (!rdev->pm.dpm.dyn_state.phase_shedding_limits_table.entries) {
+                               kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries);
+                               kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries);
+                               kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries);
+                               return -ENOMEM;
+                       }
+
+                       for (i = 0; i < psl->ucNumEntries; i++) {
+                               rdev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].sclk =
+                                       le16_to_cpu(psl->entries[i].usSclkLow) |
+                                       (psl->entries[i].ucSclkHigh << 16);
+                               rdev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].mclk =
+                                       le16_to_cpu(psl->entries[i].usMclkLow) |
+                                       (psl->entries[i].ucMclkHigh << 16);
+                               rdev->pm.dpm.dyn_state.phase_shedding_limits_table.entries[i].voltage =
+                                       le16_to_cpu(psl->entries[i].usVoltage);
+                       }
+                       rdev->pm.dpm.dyn_state.phase_shedding_limits_table.count =
+                               psl->ucNumEntries;
+               }
+       }
+
+       /* cac data */
+       if (le16_to_cpu(power_info->pplib.usTableSize) >=
+           sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE5)) {
+               rdev->pm.dpm.tdp_limit = le32_to_cpu(power_info->pplib5.ulTDPLimit);
+               rdev->pm.dpm.near_tdp_limit = le32_to_cpu(power_info->pplib5.ulNearTDPLimit);
+               rdev->pm.dpm.near_tdp_limit_adjusted = rdev->pm.dpm.near_tdp_limit;
+               rdev->pm.dpm.tdp_od_limit = le16_to_cpu(power_info->pplib5.usTDPODLimit);
+               if (rdev->pm.dpm.tdp_od_limit)
+                       rdev->pm.dpm.power_control = true;
+               else
+                       rdev->pm.dpm.power_control = false;
+               rdev->pm.dpm.tdp_adjustment = 0;
+               rdev->pm.dpm.sq_ramping_threshold = le32_to_cpu(power_info->pplib5.ulSQRampingThreshold);
+               rdev->pm.dpm.cac_leakage = le32_to_cpu(power_info->pplib5.ulCACLeakage);
+               rdev->pm.dpm.load_line_slope = le16_to_cpu(power_info->pplib5.usLoadLineSlope);
+               if (power_info->pplib5.usCACLeakageTableOffset) {
+                       ATOM_PPLIB_CAC_Leakage_Table *cac_table =
+                               (ATOM_PPLIB_CAC_Leakage_Table *)
+                               (mode_info->atom_context->bios + data_offset +
+                                le16_to_cpu(power_info->pplib5.usCACLeakageTableOffset));
+                       u32 size = cac_table->ucNumEntries * sizeof(struct radeon_cac_leakage_table);
+                       rdev->pm.dpm.dyn_state.cac_leakage_table.entries = kzalloc(size, GFP_KERNEL);
+                       if (!rdev->pm.dpm.dyn_state.cac_leakage_table.entries) {
+                               kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries);
+                               kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries);
+                               kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries);
+                               return -ENOMEM;
+                       }
+                       for (i = 0; i < cac_table->ucNumEntries; i++) {
+                               rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc =
+                                       le16_to_cpu(cac_table->entries[i].usVddc);
+                               rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].leakage =
+                                       le32_to_cpu(cac_table->entries[i].ulLeakageValue);
+                       }
+                       rdev->pm.dpm.dyn_state.cac_leakage_table.count = cac_table->ucNumEntries;
+               }
+       }
+
+       /* ppm table */
+       if (le16_to_cpu(power_info->pplib.usTableSize) >=
+           sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE3)) {
+               ATOM_PPLIB_EXTENDEDHEADER *ext_hdr = (ATOM_PPLIB_EXTENDEDHEADER *)
+                       (mode_info->atom_context->bios + data_offset +
+                        le16_to_cpu(power_info->pplib3.usExtendendedHeaderOffset));
+               if ((le16_to_cpu(ext_hdr->usSize) >= SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5) &&
+                   ext_hdr->usPPMTableOffset) {
+                       ATOM_PPLIB_PPM_Table *ppm = (ATOM_PPLIB_PPM_Table *)
+                               (mode_info->atom_context->bios + data_offset +
+                                le16_to_cpu(ext_hdr->usPPMTableOffset));
+                       rdev->pm.dpm.dyn_state.ppm_table =
+                               kzalloc(sizeof(struct radeon_ppm_table), GFP_KERNEL);
+                       if (!rdev->pm.dpm.dyn_state.ppm_table) {
+                               kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries);
+                               kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries);
+                               kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries);
+                               kfree(rdev->pm.dpm.dyn_state.cac_leakage_table.entries);
+                               return -ENOMEM;
+                       }
+                       rdev->pm.dpm.dyn_state.ppm_table->ppm_design = ppm->ucPpmDesign;
+                       rdev->pm.dpm.dyn_state.ppm_table->cpu_core_number =
+                               le16_to_cpu(ppm->usCpuCoreNumber);
+                       rdev->pm.dpm.dyn_state.ppm_table->platform_tdp =
+                               le32_to_cpu(ppm->ulPlatformTDP);
+                       rdev->pm.dpm.dyn_state.ppm_table->small_ac_platform_tdp =
+                               le32_to_cpu(ppm->ulSmallACPlatformTDP);
+                       rdev->pm.dpm.dyn_state.ppm_table->platform_tdc =
+                               le32_to_cpu(ppm->ulPlatformTDC);
+                       rdev->pm.dpm.dyn_state.ppm_table->small_ac_platform_tdc =
+                               le32_to_cpu(ppm->ulSmallACPlatformTDC);
+                       rdev->pm.dpm.dyn_state.ppm_table->apu_tdp =
+                               le32_to_cpu(ppm->ulApuTDP);
+                       rdev->pm.dpm.dyn_state.ppm_table->dgpu_tdp =
+                               le32_to_cpu(ppm->ulDGpuTDP);
+                       rdev->pm.dpm.dyn_state.ppm_table->dgpu_ulv_power =
+                               le32_to_cpu(ppm->ulDGpuUlvPower);
+                       rdev->pm.dpm.dyn_state.ppm_table->tj_max =
+                               le32_to_cpu(ppm->ulTjmax);
+               }
+       }
+
+       return 0;
+}
+
+void r600_free_extended_power_table(struct radeon_device *rdev)
+{
+       if (rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries)
+               kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries);
+       if (rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries)
+               kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries);
+       if (rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries)
+               kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries);
+       if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries)
+               kfree(rdev->pm.dpm.dyn_state.cac_leakage_table.entries);
+       if (rdev->pm.dpm.dyn_state.phase_shedding_limits_table.entries)
+               kfree(rdev->pm.dpm.dyn_state.phase_shedding_limits_table.entries);
+       if (rdev->pm.dpm.dyn_state.ppm_table)
+               kfree(rdev->pm.dpm.dyn_state.ppm_table);
+}
+
+enum radeon_pcie_gen r600_get_pcie_gen_support(struct radeon_device *rdev,
+                                              u32 sys_mask,
+                                              enum radeon_pcie_gen asic_gen,
+                                              enum radeon_pcie_gen default_gen)
+{
+       switch (asic_gen) {
+       case RADEON_PCIE_GEN1:
+               return RADEON_PCIE_GEN1;
+       case RADEON_PCIE_GEN2:
+               return RADEON_PCIE_GEN2;
+       case RADEON_PCIE_GEN3:
+               return RADEON_PCIE_GEN3;
+       default:
+               if ((sys_mask & DRM_PCIE_SPEED_80) && (default_gen == RADEON_PCIE_GEN3))
+                       return RADEON_PCIE_GEN3;
+               else if ((sys_mask & DRM_PCIE_SPEED_50) && (default_gen == RADEON_PCIE_GEN2))
+                       return RADEON_PCIE_GEN2;
+               else
+                       return RADEON_PCIE_GEN1;
+       }
+       return RADEON_PCIE_GEN1;
+}
diff --git a/drivers/gpu/drm/radeon/r600_dpm.h b/drivers/gpu/drm/radeon/r600_dpm.h
new file mode 100644 (file)
index 0000000..7c822d9
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __R600_DPM_H__
+#define __R600_DPM_H__
+
+#define R600_ASI_DFLT                                10000
+#define R600_BSP_DFLT                                0x41EB
+#define R600_BSU_DFLT                                0x2
+#define R600_AH_DFLT                                 5
+#define R600_RLP_DFLT                                25
+#define R600_RMP_DFLT                                65
+#define R600_LHP_DFLT                                40
+#define R600_LMP_DFLT                                15
+#define R600_TD_DFLT                                 0
+#define R600_UTC_DFLT_00                             0x24
+#define R600_UTC_DFLT_01                             0x22
+#define R600_UTC_DFLT_02                             0x22
+#define R600_UTC_DFLT_03                             0x22
+#define R600_UTC_DFLT_04                             0x22
+#define R600_UTC_DFLT_05                             0x22
+#define R600_UTC_DFLT_06                             0x22
+#define R600_UTC_DFLT_07                             0x22
+#define R600_UTC_DFLT_08                             0x22
+#define R600_UTC_DFLT_09                             0x22
+#define R600_UTC_DFLT_10                             0x22
+#define R600_UTC_DFLT_11                             0x22
+#define R600_UTC_DFLT_12                             0x22
+#define R600_UTC_DFLT_13                             0x22
+#define R600_UTC_DFLT_14                             0x22
+#define R600_DTC_DFLT_00                             0x24
+#define R600_DTC_DFLT_01                             0x22
+#define R600_DTC_DFLT_02                             0x22
+#define R600_DTC_DFLT_03                             0x22
+#define R600_DTC_DFLT_04                             0x22
+#define R600_DTC_DFLT_05                             0x22
+#define R600_DTC_DFLT_06                             0x22
+#define R600_DTC_DFLT_07                             0x22
+#define R600_DTC_DFLT_08                             0x22
+#define R600_DTC_DFLT_09                             0x22
+#define R600_DTC_DFLT_10                             0x22
+#define R600_DTC_DFLT_11                             0x22
+#define R600_DTC_DFLT_12                             0x22
+#define R600_DTC_DFLT_13                             0x22
+#define R600_DTC_DFLT_14                             0x22
+#define R600_VRC_DFLT                                0x0000C003
+#define R600_VOLTAGERESPONSETIME_DFLT                1000
+#define R600_BACKBIASRESPONSETIME_DFLT               1000
+#define R600_VRU_DFLT                                0x3
+#define R600_SPLLSTEPTIME_DFLT                       0x1000
+#define R600_SPLLSTEPUNIT_DFLT                       0x3
+#define R600_TPU_DFLT                                0
+#define R600_TPC_DFLT                                0x200
+#define R600_SSTU_DFLT                               0
+#define R600_SST_DFLT                                0x00C8
+#define R600_GICST_DFLT                              0x200
+#define R600_FCT_DFLT                                0x0400
+#define R600_FCTU_DFLT                               0
+#define R600_CTXCGTT3DRPHC_DFLT                      0x20
+#define R600_CTXCGTT3DRSDC_DFLT                      0x40
+#define R600_VDDC3DOORPHC_DFLT                       0x100
+#define R600_VDDC3DOORSDC_DFLT                       0x7
+#define R600_VDDC3DOORSU_DFLT                        0
+#define R600_MPLLLOCKTIME_DFLT                       100
+#define R600_MPLLRESETTIME_DFLT                      150
+#define R600_VCOSTEPPCT_DFLT                          20
+#define R600_ENDINGVCOSTEPPCT_DFLT                    5
+#define R600_REFERENCEDIVIDER_DFLT                    4
+
+#define R600_PM_NUMBER_OF_TC 15
+#define R600_PM_NUMBER_OF_SCLKS 20
+#define R600_PM_NUMBER_OF_MCLKS 4
+#define R600_PM_NUMBER_OF_VOLTAGE_LEVELS 4
+#define R600_PM_NUMBER_OF_ACTIVITY_LEVELS 3
+
+/* XXX are these ok? */
+#define R600_TEMP_RANGE_MIN (90 * 1000)
+#define R600_TEMP_RANGE_MAX (120 * 1000)
+
+enum r600_power_level {
+       R600_POWER_LEVEL_LOW = 0,
+       R600_POWER_LEVEL_MEDIUM = 1,
+       R600_POWER_LEVEL_HIGH = 2,
+       R600_POWER_LEVEL_CTXSW = 3,
+};
+
+enum r600_td {
+       R600_TD_AUTO,
+       R600_TD_UP,
+       R600_TD_DOWN,
+};
+
+enum r600_display_watermark {
+       R600_DISPLAY_WATERMARK_LOW = 0,
+       R600_DISPLAY_WATERMARK_HIGH = 1,
+};
+
+enum r600_display_gap
+{
+    R600_PM_DISPLAY_GAP_VBLANK_OR_WM = 0,
+    R600_PM_DISPLAY_GAP_VBLANK       = 1,
+    R600_PM_DISPLAY_GAP_WATERMARK    = 2,
+    R600_PM_DISPLAY_GAP_IGNORE       = 3,
+};
+
+extern const u32 r600_utc[R600_PM_NUMBER_OF_TC];
+extern const u32 r600_dtc[R600_PM_NUMBER_OF_TC];
+
+void r600_dpm_print_class_info(u32 class, u32 class2);
+void r600_dpm_print_cap_info(u32 caps);
+void r600_dpm_print_ps_status(struct radeon_device *rdev,
+                             struct radeon_ps *rps);
+u32 r600_dpm_get_vblank_time(struct radeon_device *rdev);
+bool r600_is_uvd_state(u32 class, u32 class2);
+void r600_calculate_u_and_p(u32 i, u32 r_c, u32 p_b,
+                           u32 *p, u32 *u);
+int r600_calculate_at(u32 t, u32 h, u32 fh, u32 fl, u32 *tl, u32 *th);
+void r600_gfx_clockgating_enable(struct radeon_device *rdev, bool enable);
+void r600_dynamicpm_enable(struct radeon_device *rdev, bool enable);
+void r600_enable_thermal_protection(struct radeon_device *rdev, bool enable);
+void r600_enable_acpi_pm(struct radeon_device *rdev);
+void r600_enable_dynamic_pcie_gen2(struct radeon_device *rdev, bool enable);
+bool r600_dynamicpm_enabled(struct radeon_device *rdev);
+void r600_enable_sclk_control(struct radeon_device *rdev, bool enable);
+void r600_enable_mclk_control(struct radeon_device *rdev, bool enable);
+void r600_enable_spll_bypass(struct radeon_device *rdev, bool enable);
+void r600_wait_for_spll_change(struct radeon_device *rdev);
+void r600_set_bsp(struct radeon_device *rdev, u32 u, u32 p);
+void r600_set_at(struct radeon_device *rdev,
+                u32 l_to_m, u32 m_to_h,
+                u32 h_to_m, u32 m_to_l);
+void r600_set_tc(struct radeon_device *rdev, u32 index, u32 u_t, u32 d_t);
+void r600_select_td(struct radeon_device *rdev, enum r600_td td);
+void r600_set_vrc(struct radeon_device *rdev, u32 vrv);
+void r600_set_tpu(struct radeon_device *rdev, u32 u);
+void r600_set_tpc(struct radeon_device *rdev, u32 c);
+void r600_set_sstu(struct radeon_device *rdev, u32 u);
+void r600_set_sst(struct radeon_device *rdev, u32 t);
+void r600_set_git(struct radeon_device *rdev, u32 t);
+void r600_set_fctu(struct radeon_device *rdev, u32 u);
+void r600_set_fct(struct radeon_device *rdev, u32 t);
+void r600_set_ctxcgtt3d_rphc(struct radeon_device *rdev, u32 p);
+void r600_set_ctxcgtt3d_rsdc(struct radeon_device *rdev, u32 s);
+void r600_set_vddc3d_oorsu(struct radeon_device *rdev, u32 u);
+void r600_set_vddc3d_oorphc(struct radeon_device *rdev, u32 p);
+void r600_set_vddc3d_oorsdc(struct radeon_device *rdev, u32 s);
+void r600_set_mpll_lock_time(struct radeon_device *rdev, u32 lock_time);
+void r600_set_mpll_reset_time(struct radeon_device *rdev, u32 reset_time);
+void r600_engine_clock_entry_enable(struct radeon_device *rdev,
+                                   u32 index, bool enable);
+void r600_engine_clock_entry_enable_pulse_skipping(struct radeon_device *rdev,
+                                                  u32 index, bool enable);
+void r600_engine_clock_entry_enable_post_divider(struct radeon_device *rdev,
+                                                u32 index, bool enable);
+void r600_engine_clock_entry_set_post_divider(struct radeon_device *rdev,
+                                             u32 index, u32 divider);
+void r600_engine_clock_entry_set_reference_divider(struct radeon_device *rdev,
+                                                  u32 index, u32 divider);
+void r600_engine_clock_entry_set_feedback_divider(struct radeon_device *rdev,
+                                                 u32 index, u32 divider);
+void r600_engine_clock_entry_set_step_time(struct radeon_device *rdev,
+                                          u32 index, u32 step_time);
+void r600_vid_rt_set_ssu(struct radeon_device *rdev, u32 u);
+void r600_vid_rt_set_vru(struct radeon_device *rdev, u32 u);
+void r600_vid_rt_set_vrt(struct radeon_device *rdev, u32 rt);
+void r600_voltage_control_enable_pins(struct radeon_device *rdev,
+                                     u64 mask);
+void r600_voltage_control_program_voltages(struct radeon_device *rdev,
+                                          enum r600_power_level index, u64 pins);
+void r600_voltage_control_deactivate_static_control(struct radeon_device *rdev,
+                                                   u64 mask);
+void r600_power_level_enable(struct radeon_device *rdev,
+                            enum r600_power_level index, bool enable);
+void r600_power_level_set_voltage_index(struct radeon_device *rdev,
+                                       enum r600_power_level index, u32 voltage_index);
+void r600_power_level_set_mem_clock_index(struct radeon_device *rdev,
+                                         enum r600_power_level index, u32 mem_clock_index);
+void r600_power_level_set_eng_clock_index(struct radeon_device *rdev,
+                                         enum r600_power_level index, u32 eng_clock_index);
+void r600_power_level_set_watermark_id(struct radeon_device *rdev,
+                                      enum r600_power_level index,
+                                      enum r600_display_watermark watermark_id);
+void r600_power_level_set_pcie_gen2(struct radeon_device *rdev,
+                                   enum r600_power_level index, bool compatible);
+enum r600_power_level r600_power_level_get_current_index(struct radeon_device *rdev);
+enum r600_power_level r600_power_level_get_target_index(struct radeon_device *rdev);
+void r600_power_level_set_enter_index(struct radeon_device *rdev,
+                                     enum r600_power_level index);
+void r600_wait_for_power_level_unequal(struct radeon_device *rdev,
+                                      enum r600_power_level index);
+void r600_wait_for_power_level(struct radeon_device *rdev,
+                              enum r600_power_level index);
+void r600_start_dpm(struct radeon_device *rdev);
+void r600_stop_dpm(struct radeon_device *rdev);
+
+int r600_set_thermal_temperature_range(struct radeon_device *rdev,
+                                      int min_temp, int max_temp);
+bool r600_is_internal_thermal_sensor(enum radeon_int_thermal_type sensor);
+
+int r600_parse_extended_power_table(struct radeon_device *rdev);
+void r600_free_extended_power_table(struct radeon_device *rdev);
+
+enum radeon_pcie_gen r600_get_pcie_gen_support(struct radeon_device *rdev,
+                                              u32 sys_mask,
+                                              enum radeon_pcie_gen asic_gen,
+                                              enum radeon_pcie_gen default_gen);
+
+#endif
index 456750a..e73b2a7 100644 (file)
@@ -133,14 +133,7 @@ static void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder,
        struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
        uint32_t offset = dig->afmt->offset;
        uint8_t *frame = buffer + 3;
-
-       /* Our header values (type, version, length) should be alright, Intel
-        * is using the same. Checksum function also seems to be OK, it works
-        * fine for audio infoframe. However calculated value is always lower
-        * by 2 in comparison to fglrx. It breaks displaying anything in case
-        * of TVs that strictly check the checksum. Hack it manually here to
-        * workaround this issue. */
-       frame[0x0] += 2;
+       uint8_t *header = buffer;
 
        WREG32(HDMI0_AVI_INFO0 + offset,
                frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
@@ -149,7 +142,7 @@ static void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder,
        WREG32(HDMI0_AVI_INFO2 + offset,
                frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24));
        WREG32(HDMI0_AVI_INFO3 + offset,
-               frame[0xC] | (frame[0xD] << 8));
+               frame[0xC] | (frame[0xD] << 8) | (header[1] << 24));
 }
 
 /*
index 909219b..3ef2026 100644 (file)
 #define R600_PCIE_PORT_INDEX                0x0038
 #define R600_PCIE_PORT_DATA                 0x003c
 
+#define R600_RCU_INDEX                      0x0100
+#define R600_RCU_DATA                       0x0104
+
+#define R600_UVD_CTX_INDEX                  0xf4a0
+#define R600_UVD_CTX_DATA                   0xf4a4
+
 #define R600_MC_VM_FB_LOCATION                 0x2180
 #define                R600_MC_FB_BASE_MASK                    0x0000FFFF
 #define                R600_MC_FB_BASE_SHIFT                   0
index 79df558..f1b3084 100644 (file)
 #define        GRBM_SOFT_RESET                                 0x8020
 #define                SOFT_RESET_CP                                   (1<<0)
 
+#define        CG_THERMAL_CTRL                                 0x7F0
+#define                DIG_THERM_DPM(x)                        ((x) << 12)
+#define                DIG_THERM_DPM_MASK                      0x000FF000
+#define                DIG_THERM_DPM_SHIFT                     12
 #define        CG_THERMAL_STATUS                               0x7F4
 #define                ASIC_T(x)                               ((x) << 0)
 #define                ASIC_T_MASK                             0x1FF
 #define                ASIC_T_SHIFT                            0
+#define        CG_THERMAL_INT                                  0x7F8
+#define                DIG_THERM_INTH(x)                       ((x) << 8)
+#define                DIG_THERM_INTH_MASK                     0x0000FF00
+#define                DIG_THERM_INTH_SHIFT                    8
+#define                DIG_THERM_INTL(x)                       ((x) << 16)
+#define                DIG_THERM_INTL_MASK                     0x00FF0000
+#define                DIG_THERM_INTL_SHIFT                    16
+#define        THERM_INT_MASK_HIGH                     (1 << 24)
+#define        THERM_INT_MASK_LOW                      (1 << 25)
+
+#define        RV770_CG_THERMAL_INT                            0x734
 
 #define        HDP_HOST_PATH_CNTL                              0x2C00
 #define        HDP_NONSURFACE_BASE                             0x2C04
 #define RLC_UCODE_ADDR                                    0x3f2c
 #define RLC_UCODE_DATA                                    0x3f30
 
-/* new for TN */
-#define TN_RLC_SAVE_AND_RESTORE_BASE                      0x3f10
-#define TN_RLC_CLEAR_STATE_RESTORE_BASE                   0x3f20
-
 #define SRBM_SOFT_RESET                                   0xe60
 #       define SOFT_RESET_DMA                             (1 << 12)
 #       define SOFT_RESET_RLC                             (1 << 13)
 #       define AFMT_AZ_FORMAT_WTRIG_ACK      (1 << 29)
 #       define AFMT_AZ_AUDIO_ENABLE_CHG_ACK  (1 << 30)
 
+/* Power management */
+#define CG_SPLL_FUNC_CNTL                                 0x600
+#       define SPLL_RESET                                (1 << 0)
+#       define SPLL_SLEEP                                (1 << 1)
+#       define SPLL_REF_DIV(x)                           ((x) << 2)
+#       define SPLL_REF_DIV_MASK                         (7 << 2)
+#       define SPLL_FB_DIV(x)                            ((x) << 5)
+#       define SPLL_FB_DIV_MASK                          (0xff << 5)
+#       define SPLL_PULSEEN                              (1 << 13)
+#       define SPLL_PULSENUM(x)                          ((x) << 14)
+#       define SPLL_PULSENUM_MASK                        (3 << 14)
+#       define SPLL_SW_HILEN(x)                          ((x) << 16)
+#       define SPLL_SW_HILEN_MASK                        (0xf << 16)
+#       define SPLL_SW_LOLEN(x)                          ((x) << 20)
+#       define SPLL_SW_LOLEN_MASK                        (0xf << 20)
+#       define SPLL_DIVEN                                (1 << 24)
+#       define SPLL_BYPASS_EN                            (1 << 25)
+#       define SPLL_CHG_STATUS                           (1 << 29)
+#       define SPLL_CTLREQ                               (1 << 30)
+#       define SPLL_CTLACK                               (1 << 31)
+
+#define GENERAL_PWRMGT                                    0x618
+#       define GLOBAL_PWRMGT_EN                           (1 << 0)
+#       define STATIC_PM_EN                               (1 << 1)
+#       define MOBILE_SU                                  (1 << 2)
+#       define THERMAL_PROTECTION_DIS                     (1 << 3)
+#       define THERMAL_PROTECTION_TYPE                    (1 << 4)
+#       define ENABLE_GEN2PCIE                            (1 << 5)
+#       define SW_GPIO_INDEX(x)                           ((x) << 6)
+#       define SW_GPIO_INDEX_MASK                         (3 << 6)
+#       define LOW_VOLT_D2_ACPI                           (1 << 8)
+#       define LOW_VOLT_D3_ACPI                           (1 << 9)
+#       define VOLT_PWRMGT_EN                             (1 << 10)
+#define CG_TPC                                            0x61c
+#       define TPCC(x)                                    ((x) << 0)
+#       define TPCC_MASK                                  (0x7fffff << 0)
+#       define TPU(x)                                     ((x) << 23)
+#       define TPU_MASK                                   (0x1f << 23)
+#define SCLK_PWRMGT_CNTL                                  0x620
+#       define SCLK_PWRMGT_OFF                            (1 << 0)
+#       define SCLK_TURNOFF                               (1 << 1)
+#       define SPLL_TURNOFF                               (1 << 2)
+#       define SU_SCLK_USE_BCLK                           (1 << 3)
+#       define DYNAMIC_GFX_ISLAND_PWR_DOWN                (1 << 4)
+#       define DYNAMIC_GFX_ISLAND_PWR_LP                  (1 << 5)
+#       define CLK_TURN_ON_STAGGER                        (1 << 6)
+#       define CLK_TURN_OFF_STAGGER                       (1 << 7)
+#       define FIR_FORCE_TREND_SEL                        (1 << 8)
+#       define FIR_TREND_MODE                             (1 << 9)
+#       define DYN_GFX_CLK_OFF_EN                         (1 << 10)
+#       define VDDC3D_TURNOFF_D1                          (1 << 11)
+#       define VDDC3D_TURNOFF_D2                          (1 << 12)
+#       define VDDC3D_TURNOFF_D3                          (1 << 13)
+#       define SPLL_TURNOFF_D2                            (1 << 14)
+#       define SCLK_LOW_D1                                (1 << 15)
+#       define DYN_GFX_CLK_OFF_MC_EN                      (1 << 16)
+#define MCLK_PWRMGT_CNTL                                  0x624
+#       define MPLL_PWRMGT_OFF                            (1 << 0)
+#       define YCLK_TURNOFF                               (1 << 1)
+#       define MPLL_TURNOFF                               (1 << 2)
+#       define SU_MCLK_USE_BCLK                           (1 << 3)
+#       define DLL_READY                                  (1 << 4)
+#       define MC_BUSY                                    (1 << 5)
+#       define MC_INT_CNTL                                (1 << 7)
+#       define MRDCKA_SLEEP                               (1 << 8)
+#       define MRDCKB_SLEEP                               (1 << 9)
+#       define MRDCKC_SLEEP                               (1 << 10)
+#       define MRDCKD_SLEEP                               (1 << 11)
+#       define MRDCKE_SLEEP                               (1 << 12)
+#       define MRDCKF_SLEEP                               (1 << 13)
+#       define MRDCKG_SLEEP                               (1 << 14)
+#       define MRDCKH_SLEEP                               (1 << 15)
+#       define MRDCKA_RESET                               (1 << 16)
+#       define MRDCKB_RESET                               (1 << 17)
+#       define MRDCKC_RESET                               (1 << 18)
+#       define MRDCKD_RESET                               (1 << 19)
+#       define MRDCKE_RESET                               (1 << 20)
+#       define MRDCKF_RESET                               (1 << 21)
+#       define MRDCKG_RESET                               (1 << 22)
+#       define MRDCKH_RESET                               (1 << 23)
+#       define DLL_READY_READ                             (1 << 24)
+#       define USE_DISPLAY_GAP                            (1 << 25)
+#       define USE_DISPLAY_URGENT_NORMAL                  (1 << 26)
+#       define USE_DISPLAY_GAP_CTXSW                      (1 << 27)
+#       define MPLL_TURNOFF_D2                            (1 << 28)
+#       define USE_DISPLAY_URGENT_CTXSW                   (1 << 29)
+
+#define MPLL_TIME                                         0x634
+#       define MPLL_LOCK_TIME(x)                          ((x) << 0)
+#       define MPLL_LOCK_TIME_MASK                        (0xffff << 0)
+#       define MPLL_RESET_TIME(x)                         ((x) << 16)
+#       define MPLL_RESET_TIME_MASK                       (0xffff << 16)
+
+#define SCLK_FREQ_SETTING_STEP_0_PART1                    0x648
+#       define STEP_0_SPLL_POST_DIV(x)                    ((x) << 0)
+#       define STEP_0_SPLL_POST_DIV_MASK                  (0xff << 0)
+#       define STEP_0_SPLL_FB_DIV(x)                      ((x) << 8)
+#       define STEP_0_SPLL_FB_DIV_MASK                    (0xff << 8)
+#       define STEP_0_SPLL_REF_DIV(x)                     ((x) << 16)
+#       define STEP_0_SPLL_REF_DIV_MASK                   (7 << 16)
+#       define STEP_0_SPLL_STEP_TIME(x)                   ((x) << 19)
+#       define STEP_0_SPLL_STEP_TIME_MASK                 (0x1fff << 19)
+#define SCLK_FREQ_SETTING_STEP_0_PART2                    0x64c
+#       define STEP_0_PULSE_HIGH_CNT(x)                   ((x) << 0)
+#       define STEP_0_PULSE_HIGH_CNT_MASK                 (0x1ff << 0)
+#       define STEP_0_POST_DIV_EN                         (1 << 9)
+#       define STEP_0_SPLL_STEP_ENABLE                    (1 << 30)
+#       define STEP_0_SPLL_ENTRY_VALID                    (1 << 31)
+
+#define VID_RT                                            0x6f8
+#       define VID_CRT(x)                                 ((x) << 0)
+#       define VID_CRT_MASK                               (0x1fff << 0)
+#       define VID_CRTU(x)                                ((x) << 13)
+#       define VID_CRTU_MASK                              (7 << 13)
+#       define SSTU(x)                                    ((x) << 16)
+#       define SSTU_MASK                                  (7 << 16)
+#define CTXSW_PROFILE_INDEX                               0x6fc
+#       define CTXSW_FREQ_VIDS_CFG_INDEX(x)               ((x) << 0)
+#       define CTXSW_FREQ_VIDS_CFG_INDEX_MASK             (3 << 0)
+#       define CTXSW_FREQ_VIDS_CFG_INDEX_SHIFT            0
+#       define CTXSW_FREQ_MCLK_CFG_INDEX(x)               ((x) << 2)
+#       define CTXSW_FREQ_MCLK_CFG_INDEX_MASK             (3 << 2)
+#       define CTXSW_FREQ_MCLK_CFG_INDEX_SHIFT            2
+#       define CTXSW_FREQ_SCLK_CFG_INDEX(x)               ((x) << 4)
+#       define CTXSW_FREQ_SCLK_CFG_INDEX_MASK             (0x1f << 4)
+#       define CTXSW_FREQ_SCLK_CFG_INDEX_SHIFT            4
+#       define CTXSW_FREQ_STATE_SPLL_RESET_EN             (1 << 9)
+#       define CTXSW_FREQ_STATE_ENABLE                    (1 << 10)
+#       define CTXSW_FREQ_DISPLAY_WATERMARK               (1 << 11)
+#       define CTXSW_FREQ_GEN2PCIE_VOLT                   (1 << 12)
+
+#define TARGET_AND_CURRENT_PROFILE_INDEX                  0x70c
+#       define TARGET_PROFILE_INDEX_MASK                  (3 << 0)
+#       define TARGET_PROFILE_INDEX_SHIFT                 0
+#       define CURRENT_PROFILE_INDEX_MASK                 (3 << 2)
+#       define CURRENT_PROFILE_INDEX_SHIFT                2
+#       define DYN_PWR_ENTER_INDEX(x)                     ((x) << 4)
+#       define DYN_PWR_ENTER_INDEX_MASK                   (3 << 4)
+#       define DYN_PWR_ENTER_INDEX_SHIFT                  4
+#       define CURR_MCLK_INDEX_MASK                       (3 << 6)
+#       define CURR_MCLK_INDEX_SHIFT                      6
+#       define CURR_SCLK_INDEX_MASK                       (0x1f << 8)
+#       define CURR_SCLK_INDEX_SHIFT                      8
+#       define CURR_VID_INDEX_MASK                        (3 << 13)
+#       define CURR_VID_INDEX_SHIFT                       13
+
+#define LOWER_GPIO_ENABLE                                 0x710
+#define UPPER_GPIO_ENABLE                                 0x714
+#define CTXSW_VID_LOWER_GPIO_CNTL                         0x718
+
+#define VID_UPPER_GPIO_CNTL                               0x740
+#define CG_CTX_CGTT3D_R                                   0x744
+#       define PHC(x)                                     ((x) << 0)
+#       define PHC_MASK                                   (0x1ff << 0)
+#       define SDC(x)                                     ((x) << 9)
+#       define SDC_MASK                                   (0x3fff << 9)
+#define CG_VDDC3D_OOR                                     0x748
+#       define SU(x)                                      ((x) << 23)
+#       define SU_MASK                                    (0xf << 23)
+#define CG_FTV                                            0x74c
+#define CG_FFCT_0                                         0x750
+#       define UTC_0(x)                                   ((x) << 0)
+#       define UTC_0_MASK                                 (0x3ff << 0)
+#       define DTC_0(x)                                   ((x) << 10)
+#       define DTC_0_MASK                                 (0x3ff << 10)
+
+#define CG_BSP                                            0x78c
+#       define BSP(x)                                     ((x) << 0)
+#       define BSP_MASK                                   (0xffff << 0)
+#       define BSU(x)                                     ((x) << 16)
+#       define BSU_MASK                                   (0xf << 16)
+#define CG_RT                                             0x790
+#       define FLS(x)                                     ((x) << 0)
+#       define FLS_MASK                                   (0xffff << 0)
+#       define FMS(x)                                     ((x) << 16)
+#       define FMS_MASK                                   (0xffff << 16)
+#define CG_LT                                             0x794
+#       define FHS(x)                                     ((x) << 0)
+#       define FHS_MASK                                   (0xffff << 0)
+#define CG_GIT                                            0x798
+#       define CG_GICST(x)                                ((x) << 0)
+#       define CG_GICST_MASK                              (0xffff << 0)
+#       define CG_GIPOT(x)                                ((x) << 16)
+#       define CG_GIPOT_MASK                              (0xffff << 16)
+
+#define CG_SSP                                            0x7a8
+#       define CG_SST(x)                                  ((x) << 0)
+#       define CG_SST_MASK                                (0xffff << 0)
+#       define CG_SSTU(x)                                 ((x) << 16)
+#       define CG_SSTU_MASK                               (0xf << 16)
+
+#define CG_RLC_REQ_AND_RSP                                0x7c4
+#       define RLC_CG_REQ_TYPE_MASK                       0xf
+#       define RLC_CG_REQ_TYPE_SHIFT                      0
+#       define CG_RLC_RSP_TYPE_MASK                       0xf0
+#       define CG_RLC_RSP_TYPE_SHIFT                      4
+
+#define CG_FC_T                                           0x7cc
+#       define FC_T(x)                                    ((x) << 0)
+#       define FC_T_MASK                                  (0xffff << 0)
+#       define FC_TU(x)                                   ((x) << 16)
+#       define FC_TU_MASK                                 (0x1f << 16)
+
+#define GPIOPAD_MASK                                      0x1798
+#define GPIOPAD_A                                         0x179c
+#define GPIOPAD_EN                                        0x17a0
+
+#define GRBM_PWR_CNTL                                     0x800c
+#       define REQ_TYPE_MASK                              0xf
+#       define REQ_TYPE_SHIFT                             0
+#       define RSP_TYPE_MASK                              0xf0
+#       define RSP_TYPE_SHIFT                             4
+
 /*
  * UVD
  */
index 142ce6c..9b7025d 100644 (file)
@@ -96,6 +96,7 @@ extern int radeon_pcie_gen2;
 extern int radeon_msi;
 extern int radeon_lockup_timeout;
 extern int radeon_fastfb;
+extern int radeon_dpm;
 
 /*
  * Copy from radeon_drv.h so we don't have to include both and have conflicting
@@ -150,6 +151,13 @@ extern int radeon_fastfb;
 #define RADEON_RESET_MC                                (1 << 10)
 #define RADEON_RESET_DISPLAY                   (1 << 11)
 
+/* max cursor sizes (in pixels) */
+#define CURSOR_WIDTH 64
+#define CURSOR_HEIGHT 64
+
+#define CIK_CURSOR_WIDTH 128
+#define CIK_CURSOR_HEIGHT 128
+
 /*
  * Errata workarounds.
  */
@@ -192,6 +200,7 @@ struct radeon_clock {
        uint32_t default_mclk;
        uint32_t default_sclk;
        uint32_t default_dispclk;
+       uint32_t current_dispclk;
        uint32_t dp_extclk;
        uint32_t max_pixel_clock;
 };
@@ -211,13 +220,51 @@ int radeon_atom_get_clock_dividers(struct radeon_device *rdev,
                                   u32 clock,
                                   bool strobe_mode,
                                   struct atom_clock_dividers *dividers);
+int radeon_atom_get_memory_pll_dividers(struct radeon_device *rdev,
+                                       u32 clock,
+                                       bool strobe_mode,
+                                       struct atom_mpll_param *mpll_param);
 void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type);
+int radeon_atom_get_voltage_gpio_settings(struct radeon_device *rdev,
+                                         u16 voltage_level, u8 voltage_type,
+                                         u32 *gpio_value, u32 *gpio_mask);
+void radeon_atom_set_engine_dram_timings(struct radeon_device *rdev,
+                                        u32 eng_clock, u32 mem_clock);
+int radeon_atom_get_voltage_step(struct radeon_device *rdev,
+                                u8 voltage_type, u16 *voltage_step);
+int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type,
+                            u16 voltage_id, u16 *voltage);
+int radeon_atom_get_leakage_vddc_based_on_leakage_idx(struct radeon_device *rdev,
+                                                     u16 *voltage,
+                                                     u16 leakage_idx);
+int radeon_atom_round_to_true_voltage(struct radeon_device *rdev,
+                                     u8 voltage_type,
+                                     u16 nominal_voltage,
+                                     u16 *true_voltage);
+int radeon_atom_get_min_voltage(struct radeon_device *rdev,
+                               u8 voltage_type, u16 *min_voltage);
+int radeon_atom_get_max_voltage(struct radeon_device *rdev,
+                               u8 voltage_type, u16 *max_voltage);
+int radeon_atom_get_voltage_table(struct radeon_device *rdev,
+                                 u8 voltage_type, u8 voltage_mode,
+                                 struct atom_voltage_table *voltage_table);
+bool radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
+                                u8 voltage_type, u8 voltage_mode);
+void radeon_atom_update_memory_dll(struct radeon_device *rdev,
+                                  u32 mem_clock);
+void radeon_atom_set_ac_timing(struct radeon_device *rdev,
+                              u32 mem_clock);
+int radeon_atom_init_mc_reg_table(struct radeon_device *rdev,
+                                 u8 module_index,
+                                 struct atom_mc_reg_table *reg_table);
+int radeon_atom_get_memory_info(struct radeon_device *rdev,
+                               u8 module_index, struct atom_memory_info *mem_info);
+int radeon_atom_get_mclk_range_table(struct radeon_device *rdev,
+                                    bool gddr5, u8 module_index,
+                                    struct atom_memory_clock_range_table *mclk_range_table);
+int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type,
+                            u16 voltage_id, u16 *voltage);
 void rs690_pm_info(struct radeon_device *rdev);
-extern int rv6xx_get_temp(struct radeon_device *rdev);
-extern int rv770_get_temp(struct radeon_device *rdev);
-extern int evergreen_get_temp(struct radeon_device *rdev);
-extern int sumo_get_temp(struct radeon_device *rdev);
-extern int si_get_temp(struct radeon_device *rdev);
 extern void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw,
                                    unsigned *bankh, unsigned *mtaspect,
                                    unsigned *tile_split);
@@ -549,6 +596,20 @@ struct radeon_scratch {
 int radeon_scratch_get(struct radeon_device *rdev, uint32_t *reg);
 void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg);
 
+/*
+ * GPU doorbell structures, functions & helpers
+ */
+struct radeon_doorbell {
+       u32                     num_pages;
+       bool                    free[1024];
+       /* doorbell mmio */
+       resource_size_t                 base;
+       resource_size_t                 size;
+       void __iomem                    *ptr;
+};
+
+int radeon_doorbell_get(struct radeon_device *rdev, u32 *page);
+void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell);
 
 /*
  * IRQS.
@@ -600,10 +661,21 @@ struct evergreen_irq_stat_regs {
        u32 afmt_status6;
 };
 
+struct cik_irq_stat_regs {
+       u32 disp_int;
+       u32 disp_int_cont;
+       u32 disp_int_cont2;
+       u32 disp_int_cont3;
+       u32 disp_int_cont4;
+       u32 disp_int_cont5;
+       u32 disp_int_cont6;
+};
+
 union radeon_irq_stat_regs {
        struct r500_irq_stat_regs r500;
        struct r600_irq_stat_regs r600;
        struct evergreen_irq_stat_regs evergreen;
+       struct cik_irq_stat_regs cik;
 };
 
 #define RADEON_MAX_HPD_PINS 6
@@ -620,6 +692,7 @@ struct radeon_irq {
        bool                            hpd[RADEON_MAX_HPD_PINS];
        bool                            afmt[RADEON_MAX_AFMT_BLOCKS];
        union radeon_irq_stat_regs      stat_regs;
+       bool                            dpm_thermal;
 };
 
 int radeon_irq_kms_init(struct radeon_device *rdev);
@@ -677,6 +750,22 @@ struct radeon_ring {
        u32                     idx;
        u64                     last_semaphore_signal_addr;
        u64                     last_semaphore_wait_addr;
+       /* for CIK queues */
+       u32 me;
+       u32 pipe;
+       u32 queue;
+       struct radeon_bo        *mqd_obj;
+       u32 doorbell_page_num;
+       u32 doorbell_offset;
+       unsigned                wptr_offs;
+};
+
+struct radeon_mec {
+       struct radeon_bo        *hpd_eop_obj;
+       u64                     hpd_eop_gpu_addr;
+       u32 num_pipe;
+       u32 num_mec;
+       u32 num_queue;
 };
 
 /*
@@ -778,15 +867,22 @@ struct r600_blit {
 };
 
 /*
- * SI RLC stuff
+ * RLC stuff
  */
-struct si_rlc {
+#include "clearstate_defs.h"
+
+struct radeon_rlc {
        /* for power gating */
        struct radeon_bo        *save_restore_obj;
        uint64_t                save_restore_gpu_addr;
+       volatile uint32_t       *sr_ptr;
+       u32                     *reg_list;
+       u32                     reg_list_size;
        /* for clear state */
        struct radeon_bo        *clear_state_obj;
        uint64_t                clear_state_gpu_addr;
+       volatile uint32_t       *cs_ptr;
+       struct cs_section_def   *cs_data;
 };
 
 int radeon_ib_get(struct radeon_device *rdev, int ring,
@@ -883,6 +979,7 @@ struct radeon_cs_parser {
        u32                     cs_flags;
        u32                     ring;
        s32                     priority;
+       struct ww_acquire_ctx   ticket;
 };
 
 extern int radeon_cs_finish_pages(struct radeon_cs_parser *p);
@@ -934,6 +1031,8 @@ struct radeon_wb {
 #define CAYMAN_WB_DMA1_RPTR_OFFSET   2304
 #define R600_WB_UVD_RPTR_OFFSET  2560
 #define R600_WB_EVENT_OFFSET     3072
+#define CIK_WB_CP1_WPTR_OFFSET     3328
+#define CIK_WB_CP2_WPTR_OFFSET     3584
 
 /**
  * struct radeon_pm - power management datas
@@ -958,6 +1057,7 @@ struct radeon_wb {
 enum radeon_pm_method {
        PM_METHOD_PROFILE,
        PM_METHOD_DYNPM,
+       PM_METHOD_DPM,
 };
 
 enum radeon_dynpm_state {
@@ -983,11 +1083,24 @@ enum radeon_voltage_type {
 };
 
 enum radeon_pm_state_type {
+       /* not used for dpm */
        POWER_STATE_TYPE_DEFAULT,
        POWER_STATE_TYPE_POWERSAVE,
+       /* user selectable states */
        POWER_STATE_TYPE_BATTERY,
        POWER_STATE_TYPE_BALANCED,
        POWER_STATE_TYPE_PERFORMANCE,
+       /* internal states */
+       POWER_STATE_TYPE_INTERNAL_UVD,
+       POWER_STATE_TYPE_INTERNAL_UVD_SD,
+       POWER_STATE_TYPE_INTERNAL_UVD_HD,
+       POWER_STATE_TYPE_INTERNAL_UVD_HD2,
+       POWER_STATE_TYPE_INTERNAL_UVD_MVC,
+       POWER_STATE_TYPE_INTERNAL_BOOT,
+       POWER_STATE_TYPE_INTERNAL_THERMAL,
+       POWER_STATE_TYPE_INTERNAL_ACPI,
+       POWER_STATE_TYPE_INTERNAL_ULV,
+       POWER_STATE_TYPE_INTERNAL_3DPERF,
 };
 
 enum radeon_pm_profile_type {
@@ -1016,12 +1129,17 @@ struct radeon_pm_profile {
 
 enum radeon_int_thermal_type {
        THERMAL_TYPE_NONE,
+       THERMAL_TYPE_EXTERNAL,
+       THERMAL_TYPE_EXTERNAL_GPIO,
        THERMAL_TYPE_RV6XX,
        THERMAL_TYPE_RV770,
+       THERMAL_TYPE_ADT7473_WITH_INTERNAL,
        THERMAL_TYPE_EVERGREEN,
        THERMAL_TYPE_SUMO,
        THERMAL_TYPE_NI,
        THERMAL_TYPE_SI,
+       THERMAL_TYPE_EMC2103_WITH_INTERNAL,
+       THERMAL_TYPE_CI,
 };
 
 struct radeon_voltage {
@@ -1075,6 +1193,201 @@ struct radeon_power_state {
  */
 #define RADEON_MODE_OVERCLOCK_MARGIN 500 /* 5 MHz */
 
+enum radeon_dpm_auto_throttle_src {
+       RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL,
+       RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL
+};
+
+enum radeon_dpm_event_src {
+       RADEON_DPM_EVENT_SRC_ANALOG = 0,
+       RADEON_DPM_EVENT_SRC_EXTERNAL = 1,
+       RADEON_DPM_EVENT_SRC_DIGITAL = 2,
+       RADEON_DPM_EVENT_SRC_ANALOG_OR_EXTERNAL = 3,
+       RADEON_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL = 4
+};
+
+struct radeon_ps {
+       u32 caps; /* vbios flags */
+       u32 class; /* vbios flags */
+       u32 class2; /* vbios flags */
+       /* UVD clocks */
+       u32 vclk;
+       u32 dclk;
+       /* asic priv */
+       void *ps_priv;
+};
+
+struct radeon_dpm_thermal {
+       /* thermal interrupt work */
+       struct work_struct work;
+       /* low temperature threshold */
+       int                min_temp;
+       /* high temperature threshold */
+       int                max_temp;
+       /* was interrupt low to high or high to low */
+       bool               high_to_low;
+};
+
+enum radeon_clk_action
+{
+       RADEON_SCLK_UP = 1,
+       RADEON_SCLK_DOWN
+};
+
+struct radeon_blacklist_clocks
+{
+       u32 sclk;
+       u32 mclk;
+       enum radeon_clk_action action;
+};
+
+struct radeon_clock_and_voltage_limits {
+       u32 sclk;
+       u32 mclk;
+       u32 vddc;
+       u32 vddci;
+};
+
+struct radeon_clock_array {
+       u32 count;
+       u32 *values;
+};
+
+struct radeon_clock_voltage_dependency_entry {
+       u32 clk;
+       u16 v;
+};
+
+struct radeon_clock_voltage_dependency_table {
+       u32 count;
+       struct radeon_clock_voltage_dependency_entry *entries;
+};
+
+struct radeon_cac_leakage_entry {
+       u16 vddc;
+       u32 leakage;
+};
+
+struct radeon_cac_leakage_table {
+       u32 count;
+       struct radeon_cac_leakage_entry *entries;
+};
+
+struct radeon_phase_shedding_limits_entry {
+       u16 voltage;
+       u32 sclk;
+       u32 mclk;
+};
+
+struct radeon_phase_shedding_limits_table {
+       u32 count;
+       struct radeon_phase_shedding_limits_entry *entries;
+};
+
+struct radeon_ppm_table {
+       u8 ppm_design;
+       u16 cpu_core_number;
+       u32 platform_tdp;
+       u32 small_ac_platform_tdp;
+       u32 platform_tdc;
+       u32 small_ac_platform_tdc;
+       u32 apu_tdp;
+       u32 dgpu_tdp;
+       u32 dgpu_ulv_power;
+       u32 tj_max;
+};
+
+struct radeon_dpm_dynamic_state {
+       struct radeon_clock_voltage_dependency_table vddc_dependency_on_sclk;
+       struct radeon_clock_voltage_dependency_table vddci_dependency_on_mclk;
+       struct radeon_clock_voltage_dependency_table vddc_dependency_on_mclk;
+       struct radeon_clock_voltage_dependency_table vddc_dependency_on_dispclk;
+       struct radeon_clock_array valid_sclk_values;
+       struct radeon_clock_array valid_mclk_values;
+       struct radeon_clock_and_voltage_limits max_clock_voltage_on_dc;
+       struct radeon_clock_and_voltage_limits max_clock_voltage_on_ac;
+       u32 mclk_sclk_ratio;
+       u32 sclk_mclk_delta;
+       u16 vddc_vddci_delta;
+       u16 min_vddc_for_pcie_gen2;
+       struct radeon_cac_leakage_table cac_leakage_table;
+       struct radeon_phase_shedding_limits_table phase_shedding_limits_table;
+       struct radeon_ppm_table *ppm_table;
+};
+
+struct radeon_dpm_fan {
+       u16 t_min;
+       u16 t_med;
+       u16 t_high;
+       u16 pwm_min;
+       u16 pwm_med;
+       u16 pwm_high;
+       u8 t_hyst;
+       u32 cycle_delay;
+       u16 t_max;
+       bool ucode_fan_control;
+};
+
+enum radeon_pcie_gen {
+       RADEON_PCIE_GEN1 = 0,
+       RADEON_PCIE_GEN2 = 1,
+       RADEON_PCIE_GEN3 = 2,
+       RADEON_PCIE_GEN_INVALID = 0xffff
+};
+
+enum radeon_dpm_forced_level {
+       RADEON_DPM_FORCED_LEVEL_AUTO = 0,
+       RADEON_DPM_FORCED_LEVEL_LOW = 1,
+       RADEON_DPM_FORCED_LEVEL_HIGH = 2,
+};
+
+struct radeon_dpm {
+       struct radeon_ps        *ps;
+       /* number of valid power states */
+       int                     num_ps;
+       /* current power state that is active */
+       struct radeon_ps        *current_ps;
+       /* requested power state */
+       struct radeon_ps        *requested_ps;
+       /* boot up power state */
+       struct radeon_ps        *boot_ps;
+       /* default uvd power state */
+       struct radeon_ps        *uvd_ps;
+       enum radeon_pm_state_type state;
+       enum radeon_pm_state_type user_state;
+       u32                     platform_caps;
+       u32                     voltage_response_time;
+       u32                     backbias_response_time;
+       void                    *priv;
+       u32                     new_active_crtcs;
+       int                     new_active_crtc_count;
+       u32                     current_active_crtcs;
+       int                     current_active_crtc_count;
+       struct radeon_dpm_dynamic_state dyn_state;
+       struct radeon_dpm_fan fan;
+       u32 tdp_limit;
+       u32 near_tdp_limit;
+       u32 near_tdp_limit_adjusted;
+       u32 sq_ramping_threshold;
+       u32 cac_leakage;
+       u16 tdp_od_limit;
+       u32 tdp_adjustment;
+       u16 load_line_slope;
+       bool power_control;
+       bool ac_power;
+       /* special states active */
+       bool                    thermal_active;
+       bool                    uvd_active;
+       /* thermal handling */
+       struct radeon_dpm_thermal thermal;
+       /* forced levels */
+       enum radeon_dpm_forced_level forced_level;
+};
+
+void radeon_dpm_enable_power_state(struct radeon_device *rdev,
+                                   enum radeon_pm_state_type dpm_state);
+
+
 struct radeon_pm {
        struct mutex            mutex;
        /* write locked while reprogramming mclk */
@@ -1128,6 +1441,9 @@ struct radeon_pm {
        /* internal thermal controller on rv6xx+ */
        enum radeon_int_thermal_type int_thermal_type;
        struct device           *int_hwmon_dev;
+       /* dpm */
+       bool                    dpm_enabled;
+       struct radeon_dpm       dpm;
 };
 
 int radeon_pm_get_type_index(struct radeon_device *rdev,
@@ -1266,6 +1582,10 @@ struct radeon_asic {
                int (*ib_test)(struct radeon_device *rdev, struct radeon_ring *cp);
                bool (*is_lockup)(struct radeon_device *rdev, struct radeon_ring *cp);
                void (*vm_flush)(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
+
+               u32 (*get_rptr)(struct radeon_device *rdev, struct radeon_ring *ring);
+               u32 (*get_wptr)(struct radeon_device *rdev, struct radeon_ring *ring);
+               void (*set_wptr)(struct radeon_device *rdev, struct radeon_ring *ring);
        } ring[RADEON_NUM_RINGS];
        /* irqs */
        struct {
@@ -1325,7 +1645,7 @@ struct radeon_asic {
                bool (*sense)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
                void (*set_polarity)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
        } hpd;
-       /* power management */
+       /* static power management */
        struct {
                void (*misc)(struct radeon_device *rdev);
                void (*prepare)(struct radeon_device *rdev);
@@ -1340,7 +1660,26 @@ struct radeon_asic {
                void (*set_pcie_lanes)(struct radeon_device *rdev, int lanes);
                void (*set_clock_gating)(struct radeon_device *rdev, int enable);
                int (*set_uvd_clocks)(struct radeon_device *rdev, u32 vclk, u32 dclk);
+               int (*get_temperature)(struct radeon_device *rdev);
        } pm;
+       /* dynamic power management */
+       struct {
+               int (*init)(struct radeon_device *rdev);
+               void (*setup_asic)(struct radeon_device *rdev);
+               int (*enable)(struct radeon_device *rdev);
+               void (*disable)(struct radeon_device *rdev);
+               int (*pre_set_power_state)(struct radeon_device *rdev);
+               int (*set_power_state)(struct radeon_device *rdev);
+               void (*post_set_power_state)(struct radeon_device *rdev);
+               void (*display_configuration_changed)(struct radeon_device *rdev);
+               void (*fini)(struct radeon_device *rdev);
+               u32 (*get_sclk)(struct radeon_device *rdev, bool low);
+               u32 (*get_mclk)(struct radeon_device *rdev, bool low);
+               void (*print_power_state)(struct radeon_device *rdev, struct radeon_ps *ps);
+               void (*debugfs_print_current_performance_level)(struct radeon_device *rdev, struct seq_file *m);
+               int (*force_performance_level)(struct radeon_device *rdev, enum radeon_dpm_forced_level level);
+               bool (*vblank_too_short)(struct radeon_device *rdev);
+       } dpm;
        /* pageflipping */
        struct {
                void (*pre_page_flip)(struct radeon_device *rdev, int crtc);
@@ -1505,6 +1844,36 @@ struct si_asic {
        uint32_t tile_mode_array[32];
 };
 
+struct cik_asic {
+       unsigned max_shader_engines;
+       unsigned max_tile_pipes;
+       unsigned max_cu_per_sh;
+       unsigned max_sh_per_se;
+       unsigned max_backends_per_se;
+       unsigned max_texture_channel_caches;
+       unsigned max_gprs;
+       unsigned max_gs_threads;
+       unsigned max_hw_contexts;
+       unsigned sc_prim_fifo_size_frontend;
+       unsigned sc_prim_fifo_size_backend;
+       unsigned sc_hiz_tile_fifo_size;
+       unsigned sc_earlyz_tile_fifo_size;
+
+       unsigned num_tile_pipes;
+       unsigned num_backends_per_se;
+       unsigned backend_disable_mask_per_asic;
+       unsigned backend_map;
+       unsigned num_texture_channel_caches;
+       unsigned mem_max_burst_length_bytes;
+       unsigned mem_row_size_in_kb;
+       unsigned shader_engine_tile_size;
+       unsigned num_gpus;
+       unsigned multi_gpu_tile_size;
+
+       unsigned tile_config;
+       uint32_t tile_mode_array[32];
+};
+
 union radeon_asic_config {
        struct r300_asic        r300;
        struct r100_asic        r100;
@@ -1513,6 +1882,7 @@ union radeon_asic_config {
        struct evergreen_asic   evergreen;
        struct cayman_asic      cayman;
        struct si_asic          si;
+       struct cik_asic         cik;
 };
 
 /*
@@ -1657,6 +2027,7 @@ struct radeon_device {
        struct radeon_gart              gart;
        struct radeon_mode_info         mode_info;
        struct radeon_scratch           scratch;
+       struct radeon_doorbell          doorbell;
        struct radeon_mman              mman;
        struct radeon_fence_driver      fence_drv[RADEON_NUM_RINGS];
        wait_queue_head_t               fence_queue;
@@ -1684,13 +2055,18 @@ struct radeon_device {
        const struct firmware *mc_fw;   /* NI MC firmware */
        const struct firmware *ce_fw;   /* SI CE firmware */
        const struct firmware *uvd_fw;  /* UVD firmware */
+       const struct firmware *mec_fw;  /* CIK MEC firmware */
+       const struct firmware *sdma_fw; /* CIK SDMA firmware */
+       const struct firmware *smc_fw;  /* SMC firmware */
        struct r600_blit r600_blit;
        struct r600_vram_scratch vram_scratch;
        int msi_enabled; /* msi enabled */
        struct r600_ih ih; /* r6/700 interrupt ring */
-       struct si_rlc rlc;
+       struct radeon_rlc rlc;
+       struct radeon_mec mec;
        struct work_struct hotplug_work;
        struct work_struct audio_work;
+       struct work_struct reset_work;
        int num_crtc; /* number of crtcs */
        struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */
        bool audio_enabled;
@@ -1727,6 +2103,9 @@ 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);
+
 /*
  * Cast helper
  */
@@ -1754,6 +2133,18 @@ void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v);
 #define WREG32_PCIE(reg, v) rv370_pcie_wreg(rdev, (reg), (v))
 #define RREG32_PCIE_PORT(reg) rdev->pciep_rreg(rdev, (reg))
 #define WREG32_PCIE_PORT(reg, v) rdev->pciep_wreg(rdev, (reg), (v))
+#define RREG32_SMC(reg) tn_smc_rreg(rdev, (reg))
+#define WREG32_SMC(reg, v) tn_smc_wreg(rdev, (reg), (v))
+#define RREG32_RCU(reg) r600_rcu_rreg(rdev, (reg))
+#define WREG32_RCU(reg, v) r600_rcu_wreg(rdev, (reg), (v))
+#define RREG32_CG(reg) eg_cg_rreg(rdev, (reg))
+#define WREG32_CG(reg, v) eg_cg_wreg(rdev, (reg), (v))
+#define RREG32_PIF_PHY0(reg) eg_pif_phy0_rreg(rdev, (reg))
+#define WREG32_PIF_PHY0(reg, v) eg_pif_phy0_wreg(rdev, (reg), (v))
+#define RREG32_PIF_PHY1(reg) eg_pif_phy1_rreg(rdev, (reg))
+#define WREG32_PIF_PHY1(reg, v) eg_pif_phy1_wreg(rdev, (reg), (v))
+#define RREG32_UVD_CTX(reg) r600_uvd_ctx_rreg(rdev, (reg))
+#define WREG32_UVD_CTX(reg, v) r600_uvd_ctx_wreg(rdev, (reg), (v))
 #define WREG32_P(reg, val, mask)                               \
        do {                                                    \
                uint32_t tmp_ = RREG32(reg);                    \
@@ -1774,6 +2165,9 @@ void r100_io_wreg(struct radeon_device *rdev, u32 reg, 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))
+
 /*
  * Indirect registers accessor
  */
@@ -1792,6 +2186,96 @@ static inline void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uin
        WREG32(RADEON_PCIE_DATA, (v));
 }
 
+static inline u32 tn_smc_rreg(struct radeon_device *rdev, u32 reg)
+{
+       u32 r;
+
+       WREG32(TN_SMC_IND_INDEX_0, (reg));
+       r = RREG32(TN_SMC_IND_DATA_0);
+       return r;
+}
+
+static inline void tn_smc_wreg(struct radeon_device *rdev, u32 reg, u32 v)
+{
+       WREG32(TN_SMC_IND_INDEX_0, (reg));
+       WREG32(TN_SMC_IND_DATA_0, (v));
+}
+
+static inline u32 r600_rcu_rreg(struct radeon_device *rdev, u32 reg)
+{
+       u32 r;
+
+       WREG32(R600_RCU_INDEX, ((reg) & 0x1fff));
+       r = RREG32(R600_RCU_DATA);
+       return r;
+}
+
+static inline void r600_rcu_wreg(struct radeon_device *rdev, u32 reg, u32 v)
+{
+       WREG32(R600_RCU_INDEX, ((reg) & 0x1fff));
+       WREG32(R600_RCU_DATA, (v));
+}
+
+static inline u32 eg_cg_rreg(struct radeon_device *rdev, u32 reg)
+{
+       u32 r;
+
+       WREG32(EVERGREEN_CG_IND_ADDR, ((reg) & 0xffff));
+       r = RREG32(EVERGREEN_CG_IND_DATA);
+       return r;
+}
+
+static inline void eg_cg_wreg(struct radeon_device *rdev, u32 reg, u32 v)
+{
+       WREG32(EVERGREEN_CG_IND_ADDR, ((reg) & 0xffff));
+       WREG32(EVERGREEN_CG_IND_DATA, (v));
+}
+
+static inline u32 eg_pif_phy0_rreg(struct radeon_device *rdev, u32 reg)
+{
+       u32 r;
+
+       WREG32(EVERGREEN_PIF_PHY0_INDEX, ((reg) & 0xffff));
+       r = RREG32(EVERGREEN_PIF_PHY0_DATA);
+       return r;
+}
+
+static inline void eg_pif_phy0_wreg(struct radeon_device *rdev, u32 reg, u32 v)
+{
+       WREG32(EVERGREEN_PIF_PHY0_INDEX, ((reg) & 0xffff));
+       WREG32(EVERGREEN_PIF_PHY0_DATA, (v));
+}
+
+static inline u32 eg_pif_phy1_rreg(struct radeon_device *rdev, u32 reg)
+{
+       u32 r;
+
+       WREG32(EVERGREEN_PIF_PHY1_INDEX, ((reg) & 0xffff));
+       r = RREG32(EVERGREEN_PIF_PHY1_DATA);
+       return r;
+}
+
+static inline void eg_pif_phy1_wreg(struct radeon_device *rdev, u32 reg, u32 v)
+{
+       WREG32(EVERGREEN_PIF_PHY1_INDEX, ((reg) & 0xffff));
+       WREG32(EVERGREEN_PIF_PHY1_DATA, (v));
+}
+
+static inline u32 r600_uvd_ctx_rreg(struct radeon_device *rdev, u32 reg)
+{
+       u32 r;
+
+       WREG32(R600_UVD_CTX_INDEX, ((reg) & 0x1ff));
+       r = RREG32(R600_UVD_CTX_DATA);
+       return r;
+}
+
+static inline void r600_uvd_ctx_wreg(struct radeon_device *rdev, u32 reg, u32 v)
+{
+       WREG32(R600_UVD_CTX_INDEX, ((reg) & 0x1ff));
+       WREG32(R600_UVD_CTX_DATA, (v));
+}
+
 void r100_pll_errata_after_index(struct radeon_device *rdev);
 
 
@@ -1840,6 +2324,16 @@ void r100_pll_errata_after_index(struct radeon_device *rdev);
                             (rdev->flags & RADEON_IS_IGP))
 #define ASIC_IS_DCE64(rdev) ((rdev->family == CHIP_OLAND))
 #define ASIC_IS_NODCE(rdev) ((rdev->family == CHIP_HAINAN))
+#define ASIC_IS_DCE8(rdev) ((rdev->family >= CHIP_BONAIRE))
+
+#define ASIC_IS_LOMBOK(rdev) ((rdev->ddev->pdev->device == 0x6849) || \
+                             (rdev->ddev->pdev->device == 0x6850) || \
+                             (rdev->ddev->pdev->device == 0x6858) || \
+                             (rdev->ddev->pdev->device == 0x6859) || \
+                             (rdev->ddev->pdev->device == 0x6840) || \
+                             (rdev->ddev->pdev->device == 0x6841) || \
+                             (rdev->ddev->pdev->device == 0x6842) || \
+                             (rdev->ddev->pdev->device == 0x6843))
 
 /*
  * BIOS helpers.
@@ -1892,6 +2386,9 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
 #define radeon_ring_ib_parse(rdev, r, ib) (rdev)->asic->ring[(r)].ib_parse((rdev), (ib))
 #define radeon_ring_is_lockup(rdev, r, cp) (rdev)->asic->ring[(r)].is_lockup((rdev), (cp))
 #define radeon_ring_vm_flush(rdev, r, vm) (rdev)->asic->ring[(r)].vm_flush((rdev), (r), (vm))
+#define radeon_ring_get_rptr(rdev, r) (rdev)->asic->ring[(r)->idx].get_rptr((rdev), (r))
+#define radeon_ring_get_wptr(rdev, r) (rdev)->asic->ring[(r)->idx].get_wptr((rdev), (r))
+#define radeon_ring_set_wptr(rdev, r) (rdev)->asic->ring[(r)->idx].set_wptr((rdev), (r))
 #define radeon_irq_set(rdev) (rdev)->asic->irq.set((rdev))
 #define radeon_irq_process(rdev) (rdev)->asic->irq.process((rdev))
 #define radeon_get_vblank_counter(rdev, crtc) (rdev)->asic->display.get_vblank_counter((rdev), (crtc))
@@ -1915,6 +2412,7 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
 #define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->pm.set_pcie_lanes((rdev), (l))
 #define radeon_set_clock_gating(rdev, e) (rdev)->asic->pm.set_clock_gating((rdev), (e))
 #define radeon_set_uvd_clocks(rdev, v, d) (rdev)->asic->pm.set_uvd_clocks((rdev), (v), (d))
+#define radeon_get_temperature(rdev) (rdev)->asic->pm.get_temperature((rdev))
 #define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->surface.set_reg((rdev), (r), (f), (p), (o), (s)))
 #define radeon_clear_surface_reg(rdev, r) ((rdev)->asic->surface.clear_reg((rdev), (r)))
 #define radeon_bandwidth_update(rdev) (rdev)->asic->display.bandwidth_update((rdev))
@@ -1935,6 +2433,21 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
 #define radeon_mc_wait_for_idle(rdev) (rdev)->asic->mc_wait_for_idle((rdev))
 #define radeon_get_xclk(rdev) (rdev)->asic->get_xclk((rdev))
 #define radeon_get_gpu_clock_counter(rdev) (rdev)->asic->get_gpu_clock_counter((rdev))
+#define radeon_dpm_init(rdev) rdev->asic->dpm.init((rdev))
+#define radeon_dpm_setup_asic(rdev) rdev->asic->dpm.setup_asic((rdev))
+#define radeon_dpm_enable(rdev) rdev->asic->dpm.enable((rdev))
+#define radeon_dpm_disable(rdev) rdev->asic->dpm.disable((rdev))
+#define radeon_dpm_pre_set_power_state(rdev) rdev->asic->dpm.pre_set_power_state((rdev))
+#define radeon_dpm_set_power_state(rdev) rdev->asic->dpm.set_power_state((rdev))
+#define radeon_dpm_post_set_power_state(rdev) rdev->asic->dpm.post_set_power_state((rdev))
+#define radeon_dpm_display_configuration_changed(rdev) rdev->asic->dpm.display_configuration_changed((rdev))
+#define radeon_dpm_fini(rdev) rdev->asic->dpm.fini((rdev))
+#define radeon_dpm_get_sclk(rdev, l) rdev->asic->dpm.get_sclk((rdev), (l))
+#define radeon_dpm_get_mclk(rdev, l) rdev->asic->dpm.get_mclk((rdev), (l))
+#define radeon_dpm_print_power_state(rdev, ps) rdev->asic->dpm.print_power_state((rdev), (ps))
+#define radeon_dpm_debugfs_print_current_performance_level(rdev, m) rdev->asic->dpm.debugfs_print_current_performance_level((rdev), (m))
+#define radeon_dpm_force_performance_level(rdev, l) rdev->asic->dpm.force_performance_level((rdev), (l))
+#define radeon_dpm_vblank_too_short(rdev) rdev->asic->dpm.vblank_too_short((rdev))
 
 /* Common functions */
 /* AGP */
@@ -2054,6 +2567,10 @@ extern int ni_mc_load_microcode(struct radeon_device *rdev);
 #if defined(CONFIG_ACPI)
 extern int radeon_acpi_init(struct radeon_device *rdev);
 extern void radeon_acpi_fini(struct radeon_device *rdev);
+extern bool radeon_acpi_is_pcie_performance_request_supported(struct radeon_device *rdev);
+extern int radeon_acpi_pcie_performance_request(struct radeon_device *rdev,
+                                               u8 perf_req, bool advertise);
+extern int radeon_acpi_pcie_notify_device_ready(struct radeon_device *rdev);
 #else
 static inline int radeon_acpi_init(struct radeon_device *rdev) { return 0; }
 static inline void radeon_acpi_fini(struct radeon_device *rdev) { }
index 196d28d..10f98c7 100644 (file)
@@ -78,6 +78,22 @@ struct atcs_verify_interface {
        u32 function_bits;      /* supported functions bit vector */
 } __packed;
 
+#define ATCS_VALID_FLAGS_MASK  0x3
+
+struct atcs_pref_req_input {
+       u16 size;               /* structure size in bytes (includes size field) */
+       u16 client_id;          /* client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) */
+       u16 valid_flags_mask;   /* valid flags mask */
+       u16 flags;              /* flags */
+       u8 req_type;            /* request type */
+       u8 perf_req;            /* performance request */
+} __packed;
+
+struct atcs_pref_req_output {
+       u16 size;               /* structure size in bytes (includes size field) */
+       u8 ret_val;             /* return value */
+} __packed;
+
 /* Call the ATIF method
  */
 /**
@@ -505,6 +521,135 @@ out:
        return err;
 }
 
+/**
+ * radeon_acpi_is_pcie_performance_request_supported
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Check if the ATCS pcie_perf_req and pcie_dev_rdy methods
+ * are supported (all asics).
+ * returns true if supported, false if not.
+ */
+bool radeon_acpi_is_pcie_performance_request_supported(struct radeon_device *rdev)
+{
+       struct radeon_atcs *atcs = &rdev->atcs;
+
+       if (atcs->functions.pcie_perf_req && atcs->functions.pcie_dev_rdy)
+               return true;
+
+       return false;
+}
+
+/**
+ * radeon_acpi_pcie_notify_device_ready
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Executes the PCIE_DEVICE_READY_NOTIFICATION method
+ * (all asics).
+ * returns 0 on success, error on failure.
+ */
+int radeon_acpi_pcie_notify_device_ready(struct radeon_device *rdev)
+{
+       acpi_handle handle;
+       union acpi_object *info;
+       struct radeon_atcs *atcs = &rdev->atcs;
+
+       /* Get the device handle */
+       handle = DEVICE_ACPI_HANDLE(&rdev->pdev->dev);
+       if (!handle)
+               return -EINVAL;
+
+       if (!atcs->functions.pcie_dev_rdy)
+               return -EINVAL;
+
+       info = radeon_atcs_call(handle, ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION, NULL);
+       if (!info)
+               return -EIO;
+
+       kfree(info);
+
+       return 0;
+}
+
+/**
+ * radeon_acpi_pcie_performance_request
+ *
+ * @rdev: radeon_device pointer
+ * @perf_req: requested perf level (pcie gen speed)
+ * @advertise: set advertise caps flag if set
+ *
+ * Executes the PCIE_PERFORMANCE_REQUEST method to
+ * change the pcie gen speed (all asics).
+ * returns 0 on success, error on failure.
+ */
+int radeon_acpi_pcie_performance_request(struct radeon_device *rdev,
+                                        u8 perf_req, bool advertise)
+{
+       acpi_handle handle;
+       union acpi_object *info;
+       struct radeon_atcs *atcs = &rdev->atcs;
+       struct atcs_pref_req_input atcs_input;
+       struct atcs_pref_req_output atcs_output;
+       struct acpi_buffer params;
+       size_t size;
+       u32 retry = 3;
+
+       /* Get the device handle */
+       handle = DEVICE_ACPI_HANDLE(&rdev->pdev->dev);
+       if (!handle)
+               return -EINVAL;
+
+       if (!atcs->functions.pcie_perf_req)
+               return -EINVAL;
+
+       atcs_input.size = sizeof(struct atcs_pref_req_input);
+       /* client id (bit 2-0: func num, 7-3: dev num, 15-8: bus num) */
+       atcs_input.client_id = rdev->pdev->devfn | (rdev->pdev->bus->number << 8);
+       atcs_input.valid_flags_mask = ATCS_VALID_FLAGS_MASK;
+       atcs_input.flags = ATCS_WAIT_FOR_COMPLETION;
+       if (advertise)
+               atcs_input.flags |= ATCS_ADVERTISE_CAPS;
+       atcs_input.req_type = ATCS_PCIE_LINK_SPEED;
+       atcs_input.perf_req = perf_req;
+
+       params.length = sizeof(struct atcs_pref_req_input);
+       params.pointer = &atcs_input;
+
+       while (retry--) {
+               info = radeon_atcs_call(handle, ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST, &params);
+               if (!info)
+                       return -EIO;
+
+               memset(&atcs_output, 0, sizeof(atcs_output));
+
+               size = *(u16 *) info->buffer.pointer;
+               if (size < 3) {
+                       DRM_INFO("ATCS buffer is too small: %zu\n", size);
+                       kfree(info);
+                       return -EINVAL;
+               }
+               size = min(sizeof(atcs_output), size);
+
+               memcpy(&atcs_output, info->buffer.pointer, size);
+
+               kfree(info);
+
+               switch (atcs_output.ret_val) {
+               case ATCS_REQUEST_REFUSED:
+               default:
+                       return -EINVAL;
+               case ATCS_REQUEST_COMPLETE:
+                       return 0;
+               case ATCS_REQUEST_IN_PROGRESS:
+                       udelay(10);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
 /**
  * radeon_acpi_event - handle notify events
  *
index a2802b4..0970774 100644 (file)
@@ -126,7 +126,11 @@ static void radeon_register_accessor_init(struct radeon_device *rdev)
                rdev->mc_rreg = &rs780_mc_rreg;
                rdev->mc_wreg = &rs780_mc_wreg;
        }
-       if (rdev->family >= CHIP_R600) {
+
+       if (rdev->family >= CHIP_BONAIRE) {
+               rdev->pciep_rreg = &cik_pciep_rreg;
+               rdev->pciep_wreg = &cik_pciep_wreg;
+       } else if (rdev->family >= CHIP_R600) {
                rdev->pciep_rreg = &r600_pciep_rreg;
                rdev->pciep_wreg = &r600_pciep_wreg;
        }
@@ -192,6 +196,9 @@ static struct radeon_asic r100_asic = {
                        .ring_test = &r100_ring_test,
                        .ib_test = &r100_ib_test,
                        .is_lockup = &r100_gpu_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -268,6 +275,9 @@ static struct radeon_asic r200_asic = {
                        .ring_test = &r100_ring_test,
                        .ib_test = &r100_ib_test,
                        .is_lockup = &r100_gpu_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -344,6 +354,9 @@ static struct radeon_asic r300_asic = {
                        .ring_test = &r100_ring_test,
                        .ib_test = &r100_ib_test,
                        .is_lockup = &r100_gpu_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -420,6 +433,9 @@ static struct radeon_asic r300_asic_pcie = {
                        .ring_test = &r100_ring_test,
                        .ib_test = &r100_ib_test,
                        .is_lockup = &r100_gpu_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -496,6 +512,9 @@ static struct radeon_asic r420_asic = {
                        .ring_test = &r100_ring_test,
                        .ib_test = &r100_ib_test,
                        .is_lockup = &r100_gpu_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -572,6 +591,9 @@ static struct radeon_asic rs400_asic = {
                        .ring_test = &r100_ring_test,
                        .ib_test = &r100_ib_test,
                        .is_lockup = &r100_gpu_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -648,6 +670,9 @@ static struct radeon_asic rs600_asic = {
                        .ring_test = &r100_ring_test,
                        .ib_test = &r100_ib_test,
                        .is_lockup = &r100_gpu_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -726,6 +751,9 @@ static struct radeon_asic rs690_asic = {
                        .ring_test = &r100_ring_test,
                        .ib_test = &r100_ib_test,
                        .is_lockup = &r100_gpu_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -804,6 +832,9 @@ static struct radeon_asic rv515_asic = {
                        .ring_test = &r100_ring_test,
                        .ib_test = &r100_ib_test,
                        .is_lockup = &r100_gpu_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -880,6 +911,9 @@ static struct radeon_asic r520_asic = {
                        .ring_test = &r100_ring_test,
                        .ib_test = &r100_ib_test,
                        .is_lockup = &r100_gpu_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -957,6 +991,9 @@ static struct radeon_asic r600_asic = {
                        .ring_test = &r600_ring_test,
                        .ib_test = &r600_ib_test,
                        .is_lockup = &r600_gfx_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_DMA_INDEX] = {
                        .ib_execute = &r600_dma_ring_ib_execute,
@@ -966,6 +1003,9 @@ static struct radeon_asic r600_asic = {
                        .ring_test = &r600_dma_ring_test,
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &r600_dma_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -1012,6 +1052,115 @@ static struct radeon_asic r600_asic = {
                .get_pcie_lanes = &r600_get_pcie_lanes,
                .set_pcie_lanes = &r600_set_pcie_lanes,
                .set_clock_gating = NULL,
+               .get_temperature = &rv6xx_get_temp,
+       },
+       .pflip = {
+               .pre_page_flip = &rs600_pre_page_flip,
+               .page_flip = &rs600_page_flip,
+               .post_page_flip = &rs600_post_page_flip,
+       },
+};
+
+static struct radeon_asic rv6xx_asic = {
+       .init = &r600_init,
+       .fini = &r600_fini,
+       .suspend = &r600_suspend,
+       .resume = &r600_resume,
+       .vga_set_state = &r600_vga_set_state,
+       .asic_reset = &r600_asic_reset,
+       .ioctl_wait_idle = r600_ioctl_wait_idle,
+       .gui_idle = &r600_gui_idle,
+       .mc_wait_for_idle = &r600_mc_wait_for_idle,
+       .get_xclk = &r600_get_xclk,
+       .get_gpu_clock_counter = &r600_get_gpu_clock_counter,
+       .gart = {
+               .tlb_flush = &r600_pcie_gart_tlb_flush,
+               .set_page = &rs600_gart_set_page,
+       },
+       .ring = {
+               [RADEON_RING_TYPE_GFX_INDEX] = {
+                       .ib_execute = &r600_ring_ib_execute,
+                       .emit_fence = &r600_fence_ring_emit,
+                       .emit_semaphore = &r600_semaphore_ring_emit,
+                       .cs_parse = &r600_cs_parse,
+                       .ring_test = &r600_ring_test,
+                       .ib_test = &r600_ib_test,
+                       .is_lockup = &r600_gfx_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
+               },
+               [R600_RING_TYPE_DMA_INDEX] = {
+                       .ib_execute = &r600_dma_ring_ib_execute,
+                       .emit_fence = &r600_dma_fence_ring_emit,
+                       .emit_semaphore = &r600_dma_semaphore_ring_emit,
+                       .cs_parse = &r600_dma_cs_parse,
+                       .ring_test = &r600_dma_ring_test,
+                       .ib_test = &r600_dma_ib_test,
+                       .is_lockup = &r600_dma_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
+               }
+       },
+       .irq = {
+               .set = &r600_irq_set,
+               .process = &r600_irq_process,
+       },
+       .display = {
+               .bandwidth_update = &rv515_bandwidth_update,
+               .get_vblank_counter = &rs600_get_vblank_counter,
+               .wait_for_vblank = &avivo_wait_for_vblank,
+               .set_backlight_level = &atombios_set_backlight_level,
+               .get_backlight_level = &atombios_get_backlight_level,
+       },
+       .copy = {
+               .blit = &r600_copy_blit,
+               .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+               .dma = &r600_copy_dma,
+               .dma_ring_index = R600_RING_TYPE_DMA_INDEX,
+               .copy = &r600_copy_dma,
+               .copy_ring_index = R600_RING_TYPE_DMA_INDEX,
+       },
+       .surface = {
+               .set_reg = r600_set_surface_reg,
+               .clear_reg = r600_clear_surface_reg,
+       },
+       .hpd = {
+               .init = &r600_hpd_init,
+               .fini = &r600_hpd_fini,
+               .sense = &r600_hpd_sense,
+               .set_polarity = &r600_hpd_set_polarity,
+       },
+       .pm = {
+               .misc = &r600_pm_misc,
+               .prepare = &rs600_pm_prepare,
+               .finish = &rs600_pm_finish,
+               .init_profile = &r600_pm_init_profile,
+               .get_dynpm_state = &r600_pm_get_dynpm_state,
+               .get_engine_clock = &radeon_atom_get_engine_clock,
+               .set_engine_clock = &radeon_atom_set_engine_clock,
+               .get_memory_clock = &radeon_atom_get_memory_clock,
+               .set_memory_clock = &radeon_atom_set_memory_clock,
+               .get_pcie_lanes = &r600_get_pcie_lanes,
+               .set_pcie_lanes = &r600_set_pcie_lanes,
+               .set_clock_gating = NULL,
+               .get_temperature = &rv6xx_get_temp,
+       },
+       .dpm = {
+               .init = &rv6xx_dpm_init,
+               .setup_asic = &rv6xx_setup_asic,
+               .enable = &rv6xx_dpm_enable,
+               .disable = &rv6xx_dpm_disable,
+               .pre_set_power_state = &r600_dpm_pre_set_power_state,
+               .set_power_state = &rv6xx_dpm_set_power_state,
+               .post_set_power_state = &r600_dpm_post_set_power_state,
+               .display_configuration_changed = &rv6xx_dpm_display_configuration_changed,
+               .fini = &rv6xx_dpm_fini,
+               .get_sclk = &rv6xx_dpm_get_sclk,
+               .get_mclk = &rv6xx_dpm_get_mclk,
+               .print_power_state = &rv6xx_dpm_print_power_state,
+               .debugfs_print_current_performance_level = &rv6xx_dpm_debugfs_print_current_performance_level,
        },
        .pflip = {
                .pre_page_flip = &rs600_pre_page_flip,
@@ -1045,6 +1194,9 @@ static struct radeon_asic rs780_asic = {
                        .ring_test = &r600_ring_test,
                        .ib_test = &r600_ib_test,
                        .is_lockup = &r600_gfx_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_DMA_INDEX] = {
                        .ib_execute = &r600_dma_ring_ib_execute,
@@ -1054,6 +1206,9 @@ static struct radeon_asic rs780_asic = {
                        .ring_test = &r600_dma_ring_test,
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &r600_dma_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -1100,6 +1255,21 @@ static struct radeon_asic rs780_asic = {
                .get_pcie_lanes = NULL,
                .set_pcie_lanes = NULL,
                .set_clock_gating = NULL,
+               .get_temperature = &rv6xx_get_temp,
+       },
+       .dpm = {
+               .init = &rs780_dpm_init,
+               .setup_asic = &rs780_dpm_setup_asic,
+               .enable = &rs780_dpm_enable,
+               .disable = &rs780_dpm_disable,
+               .pre_set_power_state = &r600_dpm_pre_set_power_state,
+               .set_power_state = &rs780_dpm_set_power_state,
+               .post_set_power_state = &r600_dpm_post_set_power_state,
+               .display_configuration_changed = &rs780_dpm_display_configuration_changed,
+               .fini = &rs780_dpm_fini,
+               .get_sclk = &rs780_dpm_get_sclk,
+               .get_mclk = &rs780_dpm_get_mclk,
+               .print_power_state = &rs780_dpm_print_power_state,
        },
        .pflip = {
                .pre_page_flip = &rs600_pre_page_flip,
@@ -1133,6 +1303,9 @@ static struct radeon_asic rv770_asic = {
                        .ring_test = &r600_ring_test,
                        .ib_test = &r600_ib_test,
                        .is_lockup = &r600_gfx_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_DMA_INDEX] = {
                        .ib_execute = &r600_dma_ring_ib_execute,
@@ -1142,6 +1315,9 @@ static struct radeon_asic rv770_asic = {
                        .ring_test = &r600_dma_ring_test,
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &r600_dma_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_UVD_INDEX] = {
                        .ib_execute = &r600_uvd_ib_execute,
@@ -1151,6 +1327,9 @@ static struct radeon_asic rv770_asic = {
                        .ring_test = &r600_uvd_ring_test,
                        .ib_test = &r600_uvd_ib_test,
                        .is_lockup = &radeon_ring_test_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -1198,6 +1377,24 @@ static struct radeon_asic rv770_asic = {
                .set_pcie_lanes = &r600_set_pcie_lanes,
                .set_clock_gating = &radeon_atom_set_clock_gating,
                .set_uvd_clocks = &rv770_set_uvd_clocks,
+               .get_temperature = &rv770_get_temp,
+       },
+       .dpm = {
+               .init = &rv770_dpm_init,
+               .setup_asic = &rv770_dpm_setup_asic,
+               .enable = &rv770_dpm_enable,
+               .disable = &rv770_dpm_disable,
+               .pre_set_power_state = &r600_dpm_pre_set_power_state,
+               .set_power_state = &rv770_dpm_set_power_state,
+               .post_set_power_state = &r600_dpm_post_set_power_state,
+               .display_configuration_changed = &rv770_dpm_display_configuration_changed,
+               .fini = &rv770_dpm_fini,
+               .get_sclk = &rv770_dpm_get_sclk,
+               .get_mclk = &rv770_dpm_get_mclk,
+               .print_power_state = &rv770_dpm_print_power_state,
+               .debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level,
+               .force_performance_level = &rv770_dpm_force_performance_level,
+               .vblank_too_short = &rv770_dpm_vblank_too_short,
        },
        .pflip = {
                .pre_page_flip = &rs600_pre_page_flip,
@@ -1231,6 +1428,9 @@ static struct radeon_asic evergreen_asic = {
                        .ring_test = &r600_ring_test,
                        .ib_test = &r600_ib_test,
                        .is_lockup = &evergreen_gfx_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_DMA_INDEX] = {
                        .ib_execute = &evergreen_dma_ring_ib_execute,
@@ -1240,6 +1440,9 @@ static struct radeon_asic evergreen_asic = {
                        .ring_test = &r600_dma_ring_test,
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &evergreen_dma_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_UVD_INDEX] = {
                        .ib_execute = &r600_uvd_ib_execute,
@@ -1249,6 +1452,9 @@ static struct radeon_asic evergreen_asic = {
                        .ring_test = &r600_uvd_ring_test,
                        .ib_test = &r600_uvd_ib_test,
                        .is_lockup = &radeon_ring_test_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -1296,6 +1502,24 @@ static struct radeon_asic evergreen_asic = {
                .set_pcie_lanes = &r600_set_pcie_lanes,
                .set_clock_gating = NULL,
                .set_uvd_clocks = &evergreen_set_uvd_clocks,
+               .get_temperature = &evergreen_get_temp,
+       },
+       .dpm = {
+               .init = &cypress_dpm_init,
+               .setup_asic = &cypress_dpm_setup_asic,
+               .enable = &cypress_dpm_enable,
+               .disable = &cypress_dpm_disable,
+               .pre_set_power_state = &r600_dpm_pre_set_power_state,
+               .set_power_state = &cypress_dpm_set_power_state,
+               .post_set_power_state = &r600_dpm_post_set_power_state,
+               .display_configuration_changed = &cypress_dpm_display_configuration_changed,
+               .fini = &cypress_dpm_fini,
+               .get_sclk = &rv770_dpm_get_sclk,
+               .get_mclk = &rv770_dpm_get_mclk,
+               .print_power_state = &rv770_dpm_print_power_state,
+               .debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level,
+               .force_performance_level = &rv770_dpm_force_performance_level,
+               .vblank_too_short = &cypress_dpm_vblank_too_short,
        },
        .pflip = {
                .pre_page_flip = &evergreen_pre_page_flip,
@@ -1329,6 +1553,9 @@ static struct radeon_asic sumo_asic = {
                        .ring_test = &r600_ring_test,
                        .ib_test = &r600_ib_test,
                        .is_lockup = &evergreen_gfx_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_DMA_INDEX] = {
                        .ib_execute = &evergreen_dma_ring_ib_execute,
@@ -1338,6 +1565,9 @@ static struct radeon_asic sumo_asic = {
                        .ring_test = &r600_dma_ring_test,
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &evergreen_dma_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_UVD_INDEX] = {
                        .ib_execute = &r600_uvd_ib_execute,
@@ -1347,6 +1577,9 @@ static struct radeon_asic sumo_asic = {
                        .ring_test = &r600_uvd_ring_test,
                        .ib_test = &r600_uvd_ib_test,
                        .is_lockup = &radeon_ring_test_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -1394,6 +1627,23 @@ static struct radeon_asic sumo_asic = {
                .set_pcie_lanes = NULL,
                .set_clock_gating = NULL,
                .set_uvd_clocks = &sumo_set_uvd_clocks,
+               .get_temperature = &sumo_get_temp,
+       },
+       .dpm = {
+               .init = &sumo_dpm_init,
+               .setup_asic = &sumo_dpm_setup_asic,
+               .enable = &sumo_dpm_enable,
+               .disable = &sumo_dpm_disable,
+               .pre_set_power_state = &sumo_dpm_pre_set_power_state,
+               .set_power_state = &sumo_dpm_set_power_state,
+               .post_set_power_state = &sumo_dpm_post_set_power_state,
+               .display_configuration_changed = &sumo_dpm_display_configuration_changed,
+               .fini = &sumo_dpm_fini,
+               .get_sclk = &sumo_dpm_get_sclk,
+               .get_mclk = &sumo_dpm_get_mclk,
+               .print_power_state = &sumo_dpm_print_power_state,
+               .debugfs_print_current_performance_level = &sumo_dpm_debugfs_print_current_performance_level,
+               .force_performance_level = &sumo_dpm_force_performance_level,
        },
        .pflip = {
                .pre_page_flip = &evergreen_pre_page_flip,
@@ -1427,6 +1677,9 @@ static struct radeon_asic btc_asic = {
                        .ring_test = &r600_ring_test,
                        .ib_test = &r600_ib_test,
                        .is_lockup = &evergreen_gfx_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_DMA_INDEX] = {
                        .ib_execute = &evergreen_dma_ring_ib_execute,
@@ -1436,6 +1689,9 @@ static struct radeon_asic btc_asic = {
                        .ring_test = &r600_dma_ring_test,
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &evergreen_dma_is_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_UVD_INDEX] = {
                        .ib_execute = &r600_uvd_ib_execute,
@@ -1445,6 +1701,9 @@ static struct radeon_asic btc_asic = {
                        .ring_test = &r600_uvd_ring_test,
                        .ib_test = &r600_uvd_ib_test,
                        .is_lockup = &radeon_ring_test_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -1492,6 +1751,24 @@ static struct radeon_asic btc_asic = {
                .set_pcie_lanes = &r600_set_pcie_lanes,
                .set_clock_gating = NULL,
                .set_uvd_clocks = &evergreen_set_uvd_clocks,
+               .get_temperature = &evergreen_get_temp,
+       },
+       .dpm = {
+               .init = &btc_dpm_init,
+               .setup_asic = &btc_dpm_setup_asic,
+               .enable = &btc_dpm_enable,
+               .disable = &btc_dpm_disable,
+               .pre_set_power_state = &btc_dpm_pre_set_power_state,
+               .set_power_state = &btc_dpm_set_power_state,
+               .post_set_power_state = &btc_dpm_post_set_power_state,
+               .display_configuration_changed = &cypress_dpm_display_configuration_changed,
+               .fini = &btc_dpm_fini,
+               .get_sclk = &btc_dpm_get_sclk,
+               .get_mclk = &btc_dpm_get_mclk,
+               .print_power_state = &rv770_dpm_print_power_state,
+               .debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level,
+               .force_performance_level = &rv770_dpm_force_performance_level,
+               .vblank_too_short = &btc_dpm_vblank_too_short,
        },
        .pflip = {
                .pre_page_flip = &evergreen_pre_page_flip,
@@ -1533,6 +1810,9 @@ static struct radeon_asic cayman_asic = {
                        .ib_test = &r600_ib_test,
                        .is_lockup = &cayman_gfx_is_lockup,
                        .vm_flush = &cayman_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [CAYMAN_RING_TYPE_CP1_INDEX] = {
                        .ib_execute = &cayman_ring_ib_execute,
@@ -1544,6 +1824,9 @@ static struct radeon_asic cayman_asic = {
                        .ib_test = &r600_ib_test,
                        .is_lockup = &cayman_gfx_is_lockup,
                        .vm_flush = &cayman_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [CAYMAN_RING_TYPE_CP2_INDEX] = {
                        .ib_execute = &cayman_ring_ib_execute,
@@ -1555,6 +1838,9 @@ static struct radeon_asic cayman_asic = {
                        .ib_test = &r600_ib_test,
                        .is_lockup = &cayman_gfx_is_lockup,
                        .vm_flush = &cayman_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_DMA_INDEX] = {
                        .ib_execute = &cayman_dma_ring_ib_execute,
@@ -1566,6 +1852,9 @@ static struct radeon_asic cayman_asic = {
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &cayman_dma_is_lockup,
                        .vm_flush = &cayman_dma_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [CAYMAN_RING_TYPE_DMA1_INDEX] = {
                        .ib_execute = &cayman_dma_ring_ib_execute,
@@ -1577,6 +1866,9 @@ static struct radeon_asic cayman_asic = {
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &cayman_dma_is_lockup,
                        .vm_flush = &cayman_dma_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_UVD_INDEX] = {
                        .ib_execute = &r600_uvd_ib_execute,
@@ -1586,6 +1878,9 @@ static struct radeon_asic cayman_asic = {
                        .ring_test = &r600_uvd_ring_test,
                        .ib_test = &r600_uvd_ib_test,
                        .is_lockup = &radeon_ring_test_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -1633,6 +1928,24 @@ static struct radeon_asic cayman_asic = {
                .set_pcie_lanes = &r600_set_pcie_lanes,
                .set_clock_gating = NULL,
                .set_uvd_clocks = &evergreen_set_uvd_clocks,
+               .get_temperature = &evergreen_get_temp,
+       },
+       .dpm = {
+               .init = &ni_dpm_init,
+               .setup_asic = &ni_dpm_setup_asic,
+               .enable = &ni_dpm_enable,
+               .disable = &ni_dpm_disable,
+               .pre_set_power_state = &ni_dpm_pre_set_power_state,
+               .set_power_state = &ni_dpm_set_power_state,
+               .post_set_power_state = &ni_dpm_post_set_power_state,
+               .display_configuration_changed = &cypress_dpm_display_configuration_changed,
+               .fini = &ni_dpm_fini,
+               .get_sclk = &ni_dpm_get_sclk,
+               .get_mclk = &ni_dpm_get_mclk,
+               .print_power_state = &ni_dpm_print_power_state,
+               .debugfs_print_current_performance_level = &ni_dpm_debugfs_print_current_performance_level,
+               .force_performance_level = &ni_dpm_force_performance_level,
+               .vblank_too_short = &ni_dpm_vblank_too_short,
        },
        .pflip = {
                .pre_page_flip = &evergreen_pre_page_flip,
@@ -1674,6 +1987,9 @@ static struct radeon_asic trinity_asic = {
                        .ib_test = &r600_ib_test,
                        .is_lockup = &cayman_gfx_is_lockup,
                        .vm_flush = &cayman_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [CAYMAN_RING_TYPE_CP1_INDEX] = {
                        .ib_execute = &cayman_ring_ib_execute,
@@ -1685,6 +2001,9 @@ static struct radeon_asic trinity_asic = {
                        .ib_test = &r600_ib_test,
                        .is_lockup = &cayman_gfx_is_lockup,
                        .vm_flush = &cayman_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [CAYMAN_RING_TYPE_CP2_INDEX] = {
                        .ib_execute = &cayman_ring_ib_execute,
@@ -1696,6 +2015,9 @@ static struct radeon_asic trinity_asic = {
                        .ib_test = &r600_ib_test,
                        .is_lockup = &cayman_gfx_is_lockup,
                        .vm_flush = &cayman_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_DMA_INDEX] = {
                        .ib_execute = &cayman_dma_ring_ib_execute,
@@ -1707,6 +2029,9 @@ static struct radeon_asic trinity_asic = {
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &cayman_dma_is_lockup,
                        .vm_flush = &cayman_dma_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [CAYMAN_RING_TYPE_DMA1_INDEX] = {
                        .ib_execute = &cayman_dma_ring_ib_execute,
@@ -1718,6 +2043,9 @@ static struct radeon_asic trinity_asic = {
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &cayman_dma_is_lockup,
                        .vm_flush = &cayman_dma_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_UVD_INDEX] = {
                        .ib_execute = &r600_uvd_ib_execute,
@@ -1727,6 +2055,9 @@ static struct radeon_asic trinity_asic = {
                        .ring_test = &r600_uvd_ring_test,
                        .ib_test = &r600_uvd_ib_test,
                        .is_lockup = &radeon_ring_test_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -1772,6 +2103,23 @@ static struct radeon_asic trinity_asic = {
                .set_pcie_lanes = NULL,
                .set_clock_gating = NULL,
                .set_uvd_clocks = &sumo_set_uvd_clocks,
+               .get_temperature = &tn_get_temp,
+       },
+       .dpm = {
+               .init = &trinity_dpm_init,
+               .setup_asic = &trinity_dpm_setup_asic,
+               .enable = &trinity_dpm_enable,
+               .disable = &trinity_dpm_disable,
+               .pre_set_power_state = &trinity_dpm_pre_set_power_state,
+               .set_power_state = &trinity_dpm_set_power_state,
+               .post_set_power_state = &trinity_dpm_post_set_power_state,
+               .display_configuration_changed = &trinity_dpm_display_configuration_changed,
+               .fini = &trinity_dpm_fini,
+               .get_sclk = &trinity_dpm_get_sclk,
+               .get_mclk = &trinity_dpm_get_mclk,
+               .print_power_state = &trinity_dpm_print_power_state,
+               .debugfs_print_current_performance_level = &trinity_dpm_debugfs_print_current_performance_level,
+               .force_performance_level = &trinity_dpm_force_performance_level,
        },
        .pflip = {
                .pre_page_flip = &evergreen_pre_page_flip,
@@ -1813,6 +2161,9 @@ static struct radeon_asic si_asic = {
                        .ib_test = &r600_ib_test,
                        .is_lockup = &si_gfx_is_lockup,
                        .vm_flush = &si_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [CAYMAN_RING_TYPE_CP1_INDEX] = {
                        .ib_execute = &si_ring_ib_execute,
@@ -1824,6 +2175,9 @@ static struct radeon_asic si_asic = {
                        .ib_test = &r600_ib_test,
                        .is_lockup = &si_gfx_is_lockup,
                        .vm_flush = &si_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [CAYMAN_RING_TYPE_CP2_INDEX] = {
                        .ib_execute = &si_ring_ib_execute,
@@ -1835,6 +2189,9 @@ static struct radeon_asic si_asic = {
                        .ib_test = &r600_ib_test,
                        .is_lockup = &si_gfx_is_lockup,
                        .vm_flush = &si_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_DMA_INDEX] = {
                        .ib_execute = &cayman_dma_ring_ib_execute,
@@ -1846,6 +2203,9 @@ static struct radeon_asic si_asic = {
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &si_dma_is_lockup,
                        .vm_flush = &si_dma_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [CAYMAN_RING_TYPE_DMA1_INDEX] = {
                        .ib_execute = &cayman_dma_ring_ib_execute,
@@ -1857,6 +2217,9 @@ static struct radeon_asic si_asic = {
                        .ib_test = &r600_dma_ib_test,
                        .is_lockup = &si_dma_is_lockup,
                        .vm_flush = &si_dma_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                },
                [R600_RING_TYPE_UVD_INDEX] = {
                        .ib_execute = &r600_uvd_ib_execute,
@@ -1866,6 +2229,9 @@ static struct radeon_asic si_asic = {
                        .ring_test = &r600_uvd_ring_test,
                        .ib_test = &r600_uvd_ib_test,
                        .is_lockup = &radeon_ring_test_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
                }
        },
        .irq = {
@@ -1911,6 +2277,334 @@ static struct radeon_asic si_asic = {
                .set_pcie_lanes = &r600_set_pcie_lanes,
                .set_clock_gating = NULL,
                .set_uvd_clocks = &si_set_uvd_clocks,
+               .get_temperature = &si_get_temp,
+       },
+       .dpm = {
+               .init = &si_dpm_init,
+               .setup_asic = &si_dpm_setup_asic,
+               .enable = &si_dpm_enable,
+               .disable = &si_dpm_disable,
+               .pre_set_power_state = &si_dpm_pre_set_power_state,
+               .set_power_state = &si_dpm_set_power_state,
+               .post_set_power_state = &si_dpm_post_set_power_state,
+               .display_configuration_changed = &si_dpm_display_configuration_changed,
+               .fini = &si_dpm_fini,
+               .get_sclk = &ni_dpm_get_sclk,
+               .get_mclk = &ni_dpm_get_mclk,
+               .print_power_state = &ni_dpm_print_power_state,
+               .debugfs_print_current_performance_level = &si_dpm_debugfs_print_current_performance_level,
+               .force_performance_level = &si_dpm_force_performance_level,
+               .vblank_too_short = &ni_dpm_vblank_too_short,
+       },
+       .pflip = {
+               .pre_page_flip = &evergreen_pre_page_flip,
+               .page_flip = &evergreen_page_flip,
+               .post_page_flip = &evergreen_post_page_flip,
+       },
+};
+
+static struct radeon_asic ci_asic = {
+       .init = &cik_init,
+       .fini = &cik_fini,
+       .suspend = &cik_suspend,
+       .resume = &cik_resume,
+       .asic_reset = &cik_asic_reset,
+       .vga_set_state = &r600_vga_set_state,
+       .ioctl_wait_idle = NULL,
+       .gui_idle = &r600_gui_idle,
+       .mc_wait_for_idle = &evergreen_mc_wait_for_idle,
+       .get_xclk = &cik_get_xclk,
+       .get_gpu_clock_counter = &cik_get_gpu_clock_counter,
+       .gart = {
+               .tlb_flush = &cik_pcie_gart_tlb_flush,
+               .set_page = &rs600_gart_set_page,
+       },
+       .vm = {
+               .init = &cik_vm_init,
+               .fini = &cik_vm_fini,
+               .pt_ring_index = R600_RING_TYPE_DMA_INDEX,
+               .set_page = &cik_vm_set_page,
+       },
+       .ring = {
+               [RADEON_RING_TYPE_GFX_INDEX] = {
+                       .ib_execute = &cik_ring_ib_execute,
+                       .ib_parse = &cik_ib_parse,
+                       .emit_fence = &cik_fence_gfx_ring_emit,
+                       .emit_semaphore = &cik_semaphore_ring_emit,
+                       .cs_parse = NULL,
+                       .ring_test = &cik_ring_test,
+                       .ib_test = &cik_ib_test,
+                       .is_lockup = &cik_gfx_is_lockup,
+                       .vm_flush = &cik_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
+               },
+               [CAYMAN_RING_TYPE_CP1_INDEX] = {
+                       .ib_execute = &cik_ring_ib_execute,
+                       .ib_parse = &cik_ib_parse,
+                       .emit_fence = &cik_fence_compute_ring_emit,
+                       .emit_semaphore = &cik_semaphore_ring_emit,
+                       .cs_parse = NULL,
+                       .ring_test = &cik_ring_test,
+                       .ib_test = &cik_ib_test,
+                       .is_lockup = &cik_gfx_is_lockup,
+                       .vm_flush = &cik_vm_flush,
+                       .get_rptr = &cik_compute_ring_get_rptr,
+                       .get_wptr = &cik_compute_ring_get_wptr,
+                       .set_wptr = &cik_compute_ring_set_wptr,
+               },
+               [CAYMAN_RING_TYPE_CP2_INDEX] = {
+                       .ib_execute = &cik_ring_ib_execute,
+                       .ib_parse = &cik_ib_parse,
+                       .emit_fence = &cik_fence_compute_ring_emit,
+                       .emit_semaphore = &cik_semaphore_ring_emit,
+                       .cs_parse = NULL,
+                       .ring_test = &cik_ring_test,
+                       .ib_test = &cik_ib_test,
+                       .is_lockup = &cik_gfx_is_lockup,
+                       .vm_flush = &cik_vm_flush,
+                       .get_rptr = &cik_compute_ring_get_rptr,
+                       .get_wptr = &cik_compute_ring_get_wptr,
+                       .set_wptr = &cik_compute_ring_set_wptr,
+               },
+               [R600_RING_TYPE_DMA_INDEX] = {
+                       .ib_execute = &cik_sdma_ring_ib_execute,
+                       .ib_parse = &cik_ib_parse,
+                       .emit_fence = &cik_sdma_fence_ring_emit,
+                       .emit_semaphore = &cik_sdma_semaphore_ring_emit,
+                       .cs_parse = NULL,
+                       .ring_test = &cik_sdma_ring_test,
+                       .ib_test = &cik_sdma_ib_test,
+                       .is_lockup = &cik_sdma_is_lockup,
+                       .vm_flush = &cik_dma_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
+               },
+               [CAYMAN_RING_TYPE_DMA1_INDEX] = {
+                       .ib_execute = &cik_sdma_ring_ib_execute,
+                       .ib_parse = &cik_ib_parse,
+                       .emit_fence = &cik_sdma_fence_ring_emit,
+                       .emit_semaphore = &cik_sdma_semaphore_ring_emit,
+                       .cs_parse = NULL,
+                       .ring_test = &cik_sdma_ring_test,
+                       .ib_test = &cik_sdma_ib_test,
+                       .is_lockup = &cik_sdma_is_lockup,
+                       .vm_flush = &cik_dma_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
+               },
+               [R600_RING_TYPE_UVD_INDEX] = {
+                       .ib_execute = &r600_uvd_ib_execute,
+                       .emit_fence = &r600_uvd_fence_emit,
+                       .emit_semaphore = &cayman_uvd_semaphore_emit,
+                       .cs_parse = &radeon_uvd_cs_parse,
+                       .ring_test = &r600_uvd_ring_test,
+                       .ib_test = &r600_uvd_ib_test,
+                       .is_lockup = &radeon_ring_test_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
+               }
+       },
+       .irq = {
+               .set = &cik_irq_set,
+               .process = &cik_irq_process,
+       },
+       .display = {
+               .bandwidth_update = &dce8_bandwidth_update,
+               .get_vblank_counter = &evergreen_get_vblank_counter,
+               .wait_for_vblank = &dce4_wait_for_vblank,
+       },
+       .copy = {
+               .blit = NULL,
+               .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+               .dma = &cik_copy_dma,
+               .dma_ring_index = R600_RING_TYPE_DMA_INDEX,
+               .copy = &cik_copy_dma,
+               .copy_ring_index = R600_RING_TYPE_DMA_INDEX,
+       },
+       .surface = {
+               .set_reg = r600_set_surface_reg,
+               .clear_reg = r600_clear_surface_reg,
+       },
+       .hpd = {
+               .init = &evergreen_hpd_init,
+               .fini = &evergreen_hpd_fini,
+               .sense = &evergreen_hpd_sense,
+               .set_polarity = &evergreen_hpd_set_polarity,
+       },
+       .pm = {
+               .misc = &evergreen_pm_misc,
+               .prepare = &evergreen_pm_prepare,
+               .finish = &evergreen_pm_finish,
+               .init_profile = &sumo_pm_init_profile,
+               .get_dynpm_state = &r600_pm_get_dynpm_state,
+               .get_engine_clock = &radeon_atom_get_engine_clock,
+               .set_engine_clock = &radeon_atom_set_engine_clock,
+               .get_memory_clock = &radeon_atom_get_memory_clock,
+               .set_memory_clock = &radeon_atom_set_memory_clock,
+               .get_pcie_lanes = NULL,
+               .set_pcie_lanes = NULL,
+               .set_clock_gating = NULL,
+               .set_uvd_clocks = &cik_set_uvd_clocks,
+       },
+       .pflip = {
+               .pre_page_flip = &evergreen_pre_page_flip,
+               .page_flip = &evergreen_page_flip,
+               .post_page_flip = &evergreen_post_page_flip,
+       },
+};
+
+static struct radeon_asic kv_asic = {
+       .init = &cik_init,
+       .fini = &cik_fini,
+       .suspend = &cik_suspend,
+       .resume = &cik_resume,
+       .asic_reset = &cik_asic_reset,
+       .vga_set_state = &r600_vga_set_state,
+       .ioctl_wait_idle = NULL,
+       .gui_idle = &r600_gui_idle,
+       .mc_wait_for_idle = &evergreen_mc_wait_for_idle,
+       .get_xclk = &cik_get_xclk,
+       .get_gpu_clock_counter = &cik_get_gpu_clock_counter,
+       .gart = {
+               .tlb_flush = &cik_pcie_gart_tlb_flush,
+               .set_page = &rs600_gart_set_page,
+       },
+       .vm = {
+               .init = &cik_vm_init,
+               .fini = &cik_vm_fini,
+               .pt_ring_index = R600_RING_TYPE_DMA_INDEX,
+               .set_page = &cik_vm_set_page,
+       },
+       .ring = {
+               [RADEON_RING_TYPE_GFX_INDEX] = {
+                       .ib_execute = &cik_ring_ib_execute,
+                       .ib_parse = &cik_ib_parse,
+                       .emit_fence = &cik_fence_gfx_ring_emit,
+                       .emit_semaphore = &cik_semaphore_ring_emit,
+                       .cs_parse = NULL,
+                       .ring_test = &cik_ring_test,
+                       .ib_test = &cik_ib_test,
+                       .is_lockup = &cik_gfx_is_lockup,
+                       .vm_flush = &cik_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
+               },
+               [CAYMAN_RING_TYPE_CP1_INDEX] = {
+                       .ib_execute = &cik_ring_ib_execute,
+                       .ib_parse = &cik_ib_parse,
+                       .emit_fence = &cik_fence_compute_ring_emit,
+                       .emit_semaphore = &cik_semaphore_ring_emit,
+                       .cs_parse = NULL,
+                       .ring_test = &cik_ring_test,
+                       .ib_test = &cik_ib_test,
+                       .is_lockup = &cik_gfx_is_lockup,
+                       .vm_flush = &cik_vm_flush,
+                       .get_rptr = &cik_compute_ring_get_rptr,
+                       .get_wptr = &cik_compute_ring_get_wptr,
+                       .set_wptr = &cik_compute_ring_set_wptr,
+               },
+               [CAYMAN_RING_TYPE_CP2_INDEX] = {
+                       .ib_execute = &cik_ring_ib_execute,
+                       .ib_parse = &cik_ib_parse,
+                       .emit_fence = &cik_fence_compute_ring_emit,
+                       .emit_semaphore = &cik_semaphore_ring_emit,
+                       .cs_parse = NULL,
+                       .ring_test = &cik_ring_test,
+                       .ib_test = &cik_ib_test,
+                       .is_lockup = &cik_gfx_is_lockup,
+                       .vm_flush = &cik_vm_flush,
+                       .get_rptr = &cik_compute_ring_get_rptr,
+                       .get_wptr = &cik_compute_ring_get_wptr,
+                       .set_wptr = &cik_compute_ring_set_wptr,
+               },
+               [R600_RING_TYPE_DMA_INDEX] = {
+                       .ib_execute = &cik_sdma_ring_ib_execute,
+                       .ib_parse = &cik_ib_parse,
+                       .emit_fence = &cik_sdma_fence_ring_emit,
+                       .emit_semaphore = &cik_sdma_semaphore_ring_emit,
+                       .cs_parse = NULL,
+                       .ring_test = &cik_sdma_ring_test,
+                       .ib_test = &cik_sdma_ib_test,
+                       .is_lockup = &cik_sdma_is_lockup,
+                       .vm_flush = &cik_dma_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
+               },
+               [CAYMAN_RING_TYPE_DMA1_INDEX] = {
+                       .ib_execute = &cik_sdma_ring_ib_execute,
+                       .ib_parse = &cik_ib_parse,
+                       .emit_fence = &cik_sdma_fence_ring_emit,
+                       .emit_semaphore = &cik_sdma_semaphore_ring_emit,
+                       .cs_parse = NULL,
+                       .ring_test = &cik_sdma_ring_test,
+                       .ib_test = &cik_sdma_ib_test,
+                       .is_lockup = &cik_sdma_is_lockup,
+                       .vm_flush = &cik_dma_vm_flush,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
+               },
+               [R600_RING_TYPE_UVD_INDEX] = {
+                       .ib_execute = &r600_uvd_ib_execute,
+                       .emit_fence = &r600_uvd_fence_emit,
+                       .emit_semaphore = &cayman_uvd_semaphore_emit,
+                       .cs_parse = &radeon_uvd_cs_parse,
+                       .ring_test = &r600_uvd_ring_test,
+                       .ib_test = &r600_uvd_ib_test,
+                       .is_lockup = &radeon_ring_test_lockup,
+                       .get_rptr = &radeon_ring_generic_get_rptr,
+                       .get_wptr = &radeon_ring_generic_get_wptr,
+                       .set_wptr = &radeon_ring_generic_set_wptr,
+               }
+       },
+       .irq = {
+               .set = &cik_irq_set,
+               .process = &cik_irq_process,
+       },
+       .display = {
+               .bandwidth_update = &dce8_bandwidth_update,
+               .get_vblank_counter = &evergreen_get_vblank_counter,
+               .wait_for_vblank = &dce4_wait_for_vblank,
+       },
+       .copy = {
+               .blit = NULL,
+               .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+               .dma = &cik_copy_dma,
+               .dma_ring_index = R600_RING_TYPE_DMA_INDEX,
+               .copy = &cik_copy_dma,
+               .copy_ring_index = R600_RING_TYPE_DMA_INDEX,
+       },
+       .surface = {
+               .set_reg = r600_set_surface_reg,
+               .clear_reg = r600_clear_surface_reg,
+       },
+       .hpd = {
+               .init = &evergreen_hpd_init,
+               .fini = &evergreen_hpd_fini,
+               .sense = &evergreen_hpd_sense,
+               .set_polarity = &evergreen_hpd_set_polarity,
+       },
+       .pm = {
+               .misc = &evergreen_pm_misc,
+               .prepare = &evergreen_pm_prepare,
+               .finish = &evergreen_pm_finish,
+               .init_profile = &sumo_pm_init_profile,
+               .get_dynpm_state = &r600_pm_get_dynpm_state,
+               .get_engine_clock = &radeon_atom_get_engine_clock,
+               .set_engine_clock = &radeon_atom_set_engine_clock,
+               .get_memory_clock = &radeon_atom_get_memory_clock,
+               .set_memory_clock = &radeon_atom_set_memory_clock,
+               .get_pcie_lanes = NULL,
+               .set_pcie_lanes = NULL,
+               .set_clock_gating = NULL,
+               .set_uvd_clocks = &cik_set_uvd_clocks,
        },
        .pflip = {
                .pre_page_flip = &evergreen_pre_page_flip,
@@ -1999,16 +2693,15 @@ int radeon_asic_init(struct radeon_device *rdev)
                rdev->asic = &r520_asic;
                break;
        case CHIP_R600:
+               rdev->asic = &r600_asic;
+               break;
        case CHIP_RV610:
        case CHIP_RV630:
        case CHIP_RV620:
        case CHIP_RV635:
        case CHIP_RV670:
-               rdev->asic = &r600_asic;
-               if (rdev->family == CHIP_R600)
-                       rdev->has_uvd = false;
-               else
-                       rdev->has_uvd = true;
+               rdev->asic = &rv6xx_asic;
+               rdev->has_uvd = true;
                break;
        case CHIP_RS780:
        case CHIP_RS880:
@@ -2082,6 +2775,19 @@ int radeon_asic_init(struct radeon_device *rdev)
                else
                        rdev->has_uvd = true;
                break;
+       case CHIP_BONAIRE:
+               rdev->asic = &ci_asic;
+               rdev->num_crtc = 6;
+               break;
+       case CHIP_KAVERI:
+       case CHIP_KABINI:
+               rdev->asic = &kv_asic;
+               /* set num crtcs */
+               if (rdev->family == CHIP_KAVERI)
+                       rdev->num_crtc = 4;
+               else
+                       rdev->num_crtc = 2;
+               break;
        default:
                /* FIXME: not supported yet */
                return -EINVAL;
index a72759e..45d0693 100644 (file)
@@ -47,6 +47,12 @@ u8 atombios_get_backlight_level(struct radeon_encoder *radeon_encoder);
 void radeon_legacy_set_backlight_level(struct radeon_encoder *radeon_encoder, u8 level);
 u8 radeon_legacy_get_backlight_level(struct radeon_encoder *radeon_encoder);
 
+u32 radeon_ring_generic_get_rptr(struct radeon_device *rdev,
+                                struct radeon_ring *ring);
+u32 radeon_ring_generic_get_wptr(struct radeon_device *rdev,
+                                struct radeon_ring *ring);
+void radeon_ring_generic_set_wptr(struct radeon_device *rdev,
+                                 struct radeon_ring *ring);
 
 /*
  * r100,rv100,rs100,rv200,rs200
@@ -395,6 +401,35 @@ void r600_kms_blit_copy(struct radeon_device *rdev,
 int r600_mc_wait_for_idle(struct radeon_device *rdev);
 u32 r600_get_xclk(struct radeon_device *rdev);
 uint64_t r600_get_gpu_clock_counter(struct radeon_device *rdev);
+int rv6xx_get_temp(struct radeon_device *rdev);
+int r600_dpm_pre_set_power_state(struct radeon_device *rdev);
+void r600_dpm_post_set_power_state(struct radeon_device *rdev);
+/* rv6xx dpm */
+int rv6xx_dpm_init(struct radeon_device *rdev);
+int rv6xx_dpm_enable(struct radeon_device *rdev);
+void rv6xx_dpm_disable(struct radeon_device *rdev);
+int rv6xx_dpm_set_power_state(struct radeon_device *rdev);
+void rv6xx_setup_asic(struct radeon_device *rdev);
+void rv6xx_dpm_display_configuration_changed(struct radeon_device *rdev);
+void rv6xx_dpm_fini(struct radeon_device *rdev);
+u32 rv6xx_dpm_get_sclk(struct radeon_device *rdev, bool low);
+u32 rv6xx_dpm_get_mclk(struct radeon_device *rdev, bool low);
+void rv6xx_dpm_print_power_state(struct radeon_device *rdev,
+                                struct radeon_ps *ps);
+void rv6xx_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                      struct seq_file *m);
+/* rs780 dpm */
+int rs780_dpm_init(struct radeon_device *rdev);
+int rs780_dpm_enable(struct radeon_device *rdev);
+void rs780_dpm_disable(struct radeon_device *rdev);
+int rs780_dpm_set_power_state(struct radeon_device *rdev);
+void rs780_dpm_setup_asic(struct radeon_device *rdev);
+void rs780_dpm_display_configuration_changed(struct radeon_device *rdev);
+void rs780_dpm_fini(struct radeon_device *rdev);
+u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low);
+u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low);
+void rs780_dpm_print_power_state(struct radeon_device *rdev,
+                                struct radeon_ps *ps);
 
 /* uvd */
 int r600_uvd_init(struct radeon_device *rdev);
@@ -428,6 +463,24 @@ int rv770_copy_dma(struct radeon_device *rdev,
 u32 rv770_get_xclk(struct radeon_device *rdev);
 int rv770_uvd_resume(struct radeon_device *rdev);
 int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
+int rv770_get_temp(struct radeon_device *rdev);
+/* rv7xx pm */
+int rv770_dpm_init(struct radeon_device *rdev);
+int rv770_dpm_enable(struct radeon_device *rdev);
+void rv770_dpm_disable(struct radeon_device *rdev);
+int rv770_dpm_set_power_state(struct radeon_device *rdev);
+void rv770_dpm_setup_asic(struct radeon_device *rdev);
+void rv770_dpm_display_configuration_changed(struct radeon_device *rdev);
+void rv770_dpm_fini(struct radeon_device *rdev);
+u32 rv770_dpm_get_sclk(struct radeon_device *rdev, bool low);
+u32 rv770_dpm_get_mclk(struct radeon_device *rdev, bool low);
+void rv770_dpm_print_power_state(struct radeon_device *rdev,
+                                struct radeon_ps *ps);
+void rv770_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                      struct seq_file *m);
+int rv770_dpm_force_performance_level(struct radeon_device *rdev,
+                                     enum radeon_dpm_forced_level level);
+bool rv770_dpm_vblank_too_short(struct radeon_device *rdev);
 
 /*
  * evergreen
@@ -482,6 +535,45 @@ int evergreen_copy_dma(struct radeon_device *rdev,
                       struct radeon_fence **fence);
 void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable);
 void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode);
+int evergreen_get_temp(struct radeon_device *rdev);
+int sumo_get_temp(struct radeon_device *rdev);
+int tn_get_temp(struct radeon_device *rdev);
+int cypress_dpm_init(struct radeon_device *rdev);
+void cypress_dpm_setup_asic(struct radeon_device *rdev);
+int cypress_dpm_enable(struct radeon_device *rdev);
+void cypress_dpm_disable(struct radeon_device *rdev);
+int cypress_dpm_set_power_state(struct radeon_device *rdev);
+void cypress_dpm_display_configuration_changed(struct radeon_device *rdev);
+void cypress_dpm_fini(struct radeon_device *rdev);
+bool cypress_dpm_vblank_too_short(struct radeon_device *rdev);
+int btc_dpm_init(struct radeon_device *rdev);
+void btc_dpm_setup_asic(struct radeon_device *rdev);
+int btc_dpm_enable(struct radeon_device *rdev);
+void btc_dpm_disable(struct radeon_device *rdev);
+int btc_dpm_pre_set_power_state(struct radeon_device *rdev);
+int btc_dpm_set_power_state(struct radeon_device *rdev);
+void btc_dpm_post_set_power_state(struct radeon_device *rdev);
+void btc_dpm_fini(struct radeon_device *rdev);
+u32 btc_dpm_get_sclk(struct radeon_device *rdev, bool low);
+u32 btc_dpm_get_mclk(struct radeon_device *rdev, bool low);
+bool btc_dpm_vblank_too_short(struct radeon_device *rdev);
+int sumo_dpm_init(struct radeon_device *rdev);
+int sumo_dpm_enable(struct radeon_device *rdev);
+void sumo_dpm_disable(struct radeon_device *rdev);
+int sumo_dpm_pre_set_power_state(struct radeon_device *rdev);
+int sumo_dpm_set_power_state(struct radeon_device *rdev);
+void sumo_dpm_post_set_power_state(struct radeon_device *rdev);
+void sumo_dpm_setup_asic(struct radeon_device *rdev);
+void sumo_dpm_display_configuration_changed(struct radeon_device *rdev);
+void sumo_dpm_fini(struct radeon_device *rdev);
+u32 sumo_dpm_get_sclk(struct radeon_device *rdev, bool low);
+u32 sumo_dpm_get_mclk(struct radeon_device *rdev, bool low);
+void sumo_dpm_print_power_state(struct radeon_device *rdev,
+                               struct radeon_ps *ps);
+void sumo_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                     struct seq_file *m);
+int sumo_dpm_force_performance_level(struct radeon_device *rdev,
+                                    enum radeon_dpm_forced_level level);
 
 /*
  * cayman
@@ -516,6 +608,41 @@ 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_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
 
+int ni_dpm_init(struct radeon_device *rdev);
+void ni_dpm_setup_asic(struct radeon_device *rdev);
+int ni_dpm_enable(struct radeon_device *rdev);
+void ni_dpm_disable(struct radeon_device *rdev);
+int ni_dpm_pre_set_power_state(struct radeon_device *rdev);
+int ni_dpm_set_power_state(struct radeon_device *rdev);
+void ni_dpm_post_set_power_state(struct radeon_device *rdev);
+void ni_dpm_fini(struct radeon_device *rdev);
+u32 ni_dpm_get_sclk(struct radeon_device *rdev, bool low);
+u32 ni_dpm_get_mclk(struct radeon_device *rdev, bool low);
+void ni_dpm_print_power_state(struct radeon_device *rdev,
+                             struct radeon_ps *ps);
+void ni_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                   struct seq_file *m);
+int ni_dpm_force_performance_level(struct radeon_device *rdev,
+                                  enum radeon_dpm_forced_level level);
+bool ni_dpm_vblank_too_short(struct radeon_device *rdev);
+int trinity_dpm_init(struct radeon_device *rdev);
+int trinity_dpm_enable(struct radeon_device *rdev);
+void trinity_dpm_disable(struct radeon_device *rdev);
+int trinity_dpm_pre_set_power_state(struct radeon_device *rdev);
+int trinity_dpm_set_power_state(struct radeon_device *rdev);
+void trinity_dpm_post_set_power_state(struct radeon_device *rdev);
+void trinity_dpm_setup_asic(struct radeon_device *rdev);
+void trinity_dpm_display_configuration_changed(struct radeon_device *rdev);
+void trinity_dpm_fini(struct radeon_device *rdev);
+u32 trinity_dpm_get_sclk(struct radeon_device *rdev, bool low);
+u32 trinity_dpm_get_mclk(struct radeon_device *rdev, bool low);
+void trinity_dpm_print_power_state(struct radeon_device *rdev,
+                                  struct radeon_ps *ps);
+void trinity_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                        struct seq_file *m);
+int trinity_dpm_force_performance_level(struct radeon_device *rdev,
+                                       enum radeon_dpm_forced_level level);
+
 /* DCE6 - SI */
 void dce6_bandwidth_update(struct radeon_device *rdev);
 
@@ -552,5 +679,82 @@ 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);
 int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
+int si_get_temp(struct radeon_device *rdev);
+int si_dpm_init(struct radeon_device *rdev);
+void si_dpm_setup_asic(struct radeon_device *rdev);
+int si_dpm_enable(struct radeon_device *rdev);
+void si_dpm_disable(struct radeon_device *rdev);
+int si_dpm_pre_set_power_state(struct radeon_device *rdev);
+int si_dpm_set_power_state(struct radeon_device *rdev);
+void si_dpm_post_set_power_state(struct radeon_device *rdev);
+void si_dpm_fini(struct radeon_device *rdev);
+void si_dpm_display_configuration_changed(struct radeon_device *rdev);
+void si_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                   struct seq_file *m);
+int si_dpm_force_performance_level(struct radeon_device *rdev,
+                                  enum radeon_dpm_forced_level level);
+
+/* DCE8 - CIK */
+void dce8_bandwidth_update(struct radeon_device *rdev);
+
+/*
+ * cik
+ */
+uint64_t cik_get_gpu_clock_counter(struct radeon_device *rdev);
+u32 cik_get_xclk(struct radeon_device *rdev);
+uint32_t cik_pciep_rreg(struct radeon_device *rdev, uint32_t reg);
+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);
+int cik_uvd_resume(struct radeon_device *rdev);
+void cik_sdma_fence_ring_emit(struct radeon_device *rdev,
+                             struct radeon_fence *fence);
+void cik_sdma_semaphore_ring_emit(struct radeon_device *rdev,
+                                 struct radeon_ring *ring,
+                                 struct radeon_semaphore *semaphore,
+                                 bool emit_wait);
+void cik_sdma_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
+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_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);
+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,
+                            struct radeon_ring *cp,
+                            struct radeon_semaphore *semaphore,
+                            bool emit_wait);
+void cik_pcie_gart_tlb_flush(struct radeon_device *rdev);
+int cik_init(struct radeon_device *rdev);
+void cik_fini(struct radeon_device *rdev);
+int cik_suspend(struct radeon_device *rdev);
+int cik_resume(struct radeon_device *rdev);
+bool cik_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp);
+int cik_asic_reset(struct radeon_device *rdev);
+void cik_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
+int cik_ring_test(struct radeon_device *rdev, struct radeon_ring *ring);
+int cik_ib_test(struct radeon_device *rdev, struct radeon_ring *ring);
+int cik_irq_set(struct radeon_device *rdev);
+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_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,
+                             struct radeon_ring *ring);
+u32 cik_compute_ring_get_wptr(struct radeon_device *rdev,
+                             struct radeon_ring *ring);
+void cik_compute_ring_set_wptr(struct radeon_device *rdev,
+                              struct radeon_ring *ring);
 
 #endif
index dea6f63..fbdaff5 100644 (file)
@@ -56,10 +56,6 @@ extern void
 radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_enum,
                          uint32_t supported_device);
 
-/* local */
-static int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type,
-                                   u16 voltage_id, u16 *voltage);
-
 union atom_supported_devices {
        struct _ATOM_SUPPORTED_DEVICES_INFO info;
        struct _ATOM_SUPPORTED_DEVICES_INFO_2 info_2;
@@ -1247,6 +1243,7 @@ bool radeon_atom_get_clock_info(struct drm_device *dev)
                        }
                        rdev->clock.dp_extclk =
                                le16_to_cpu(firmware_info->info_21.usUniphyDPModeExtClkFreq);
+                       rdev->clock.current_dispclk = rdev->clock.default_dispclk;
                }
                *dcpll = *p1pll;
 
@@ -1269,6 +1266,7 @@ union igp_info {
        struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
        struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6;
        struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7;
+       struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_8 info_8;
 };
 
 bool radeon_atombios_sideport_present(struct radeon_device *rdev)
@@ -1438,6 +1436,22 @@ static void radeon_atombios_get_igp_ss_overrides(struct radeon_device *rdev,
                                break;
                        }
                        break;
+               case 8:
+                       switch (id) {
+                       case ASIC_INTERNAL_SS_ON_TMDS:
+                               percentage = le16_to_cpu(igp_info->info_8.usDVISSPercentage);
+                               rate = le16_to_cpu(igp_info->info_8.usDVISSpreadRateIn10Hz);
+                               break;
+                       case ASIC_INTERNAL_SS_ON_HDMI:
+                               percentage = le16_to_cpu(igp_info->info_8.usHDMISSPercentage);
+                               rate = le16_to_cpu(igp_info->info_8.usHDMISSpreadRateIn10Hz);
+                               break;
+                       case ASIC_INTERNAL_SS_ON_LVDS:
+                               percentage = le16_to_cpu(igp_info->info_8.usLvdsSSPercentage);
+                               rate = le16_to_cpu(igp_info->info_8.usLvdsSSpreadRateIn10Hz);
+                               break;
+                       }
+                       break;
                default:
                        DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev);
                        break;
@@ -1499,6 +1513,10 @@ bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev,
                                                le16_to_cpu(ss_info->info_2.asSpreadSpectrum[i].usSpreadSpectrumPercentage);
                                        ss->type = ss_info->info_2.asSpreadSpectrum[i].ucSpreadSpectrumMode;
                                        ss->rate = le16_to_cpu(ss_info->info_2.asSpreadSpectrum[i].usSpreadRateIn10Hz);
+                                       if ((crev == 2) &&
+                                           ((id == ASIC_INTERNAL_ENGINE_SS) ||
+                                            (id == ASIC_INTERNAL_MEMORY_SS)))
+                                               ss->rate /= 100;
                                        return true;
                                }
                        }
@@ -1513,6 +1531,9 @@ bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev,
                                                le16_to_cpu(ss_info->info_3.asSpreadSpectrum[i].usSpreadSpectrumPercentage);
                                        ss->type = ss_info->info_3.asSpreadSpectrum[i].ucSpreadSpectrumMode;
                                        ss->rate = le16_to_cpu(ss_info->info_3.asSpreadSpectrum[i].usSpreadRateIn10Hz);
+                                       if ((id == ASIC_INTERNAL_ENGINE_SS) ||
+                                           (id == ASIC_INTERNAL_MEMORY_SS))
+                                               ss->rate /= 100;
                                        if (rdev->flags & RADEON_IS_IGP)
                                                radeon_atombios_get_igp_ss_overrides(rdev, ss, id);
                                        return true;
@@ -1927,6 +1948,7 @@ static const char *pp_lib_thermal_controller_names[] = {
        "Northern Islands",
        "Southern Islands",
        "lm96163",
+       "Sea Islands",
 };
 
 union power_info {
@@ -1944,6 +1966,7 @@ union pplib_clock_info {
        struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
        struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
        struct _ATOM_PPLIB_SI_CLOCK_INFO si;
+       struct _ATOM_PPLIB_CI_CLOCK_INFO ci;
 };
 
 union pplib_power_state {
@@ -2209,6 +2232,11 @@ static void radeon_atombios_add_pplib_thermal_controller(struct radeon_device *r
                                 (controller->ucFanParameters &
                                  ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
                        rdev->pm.int_thermal_type = THERMAL_TYPE_SI;
+               } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_CISLANDS) {
+                       DRM_INFO("Internal thermal controller %s fan control\n",
+                                (controller->ucFanParameters &
+                                 ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
+                       rdev->pm.int_thermal_type = THERMAL_TYPE_CI;
                } else if ((controller->ucType ==
                            ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO) ||
                           (controller->ucType ==
@@ -2241,8 +2269,8 @@ static void radeon_atombios_add_pplib_thermal_controller(struct radeon_device *r
        }
 }
 
-static void radeon_atombios_get_default_voltages(struct radeon_device *rdev,
-                                                u16 *vddc, u16 *vddci)
+void radeon_atombios_get_default_voltages(struct radeon_device *rdev,
+                                         u16 *vddc, u16 *vddci, u16 *mvdd)
 {
        struct radeon_mode_info *mode_info = &rdev->mode_info;
        int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
@@ -2252,6 +2280,7 @@ static void radeon_atombios_get_default_voltages(struct radeon_device *rdev,
 
        *vddc = 0;
        *vddci = 0;
+       *mvdd = 0;
 
        if (atom_parse_data_header(mode_info->atom_context, index, NULL,
                                   &frev, &crev, &data_offset)) {
@@ -2259,8 +2288,10 @@ static void radeon_atombios_get_default_voltages(struct radeon_device *rdev,
                        (union firmware_info *)(mode_info->atom_context->bios +
                                                data_offset);
                *vddc = le16_to_cpu(firmware_info->info_14.usBootUpVDDCVoltage);
-               if ((frev == 2) && (crev >= 2))
+               if ((frev == 2) && (crev >= 2)) {
                        *vddci = le16_to_cpu(firmware_info->info_22.usBootUpVDDCIVoltage);
+                       *mvdd = le16_to_cpu(firmware_info->info_22.usBootUpMVDDCVoltage);
+               }
        }
 }
 
@@ -2271,9 +2302,9 @@ static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rde
        int j;
        u32 misc = le32_to_cpu(non_clock_info->ulCapsAndSettings);
        u32 misc2 = le16_to_cpu(non_clock_info->usClassification);
-       u16 vddc, vddci;
+       u16 vddc, vddci, mvdd;
 
-       radeon_atombios_get_default_voltages(rdev, &vddc, &vddci);
+       radeon_atombios_get_default_voltages(rdev, &vddc, &vddci, &mvdd);
 
        rdev->pm.power_state[state_index].misc = misc;
        rdev->pm.power_state[state_index].misc2 = misc2;
@@ -2316,7 +2347,13 @@ static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rde
                        rdev->pm.default_vddc = rdev->pm.power_state[state_index].clock_info[0].voltage.voltage;
                        rdev->pm.default_vddci = rdev->pm.power_state[state_index].clock_info[0].voltage.vddci;
                } else {
-                       /* patch the table values with the default slck/mclk from firmware info */
+                       u16 max_vddci = 0;
+
+                       if (ASIC_IS_DCE4(rdev))
+                               radeon_atom_get_max_voltage(rdev,
+                                                           SET_VOLTAGE_TYPE_ASIC_VDDCI,
+                                                           &max_vddci);
+                       /* patch the table values with the default sclk/mclk from firmware info */
                        for (j = 0; j < mode_index; j++) {
                                rdev->pm.power_state[state_index].clock_info[j].mclk =
                                        rdev->clock.default_mclk;
@@ -2325,6 +2362,9 @@ static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rde
                                if (vddc)
                                        rdev->pm.power_state[state_index].clock_info[j].voltage.voltage =
                                                vddc;
+                               if (max_vddci)
+                                       rdev->pm.power_state[state_index].clock_info[j].voltage.vddci =
+                                               max_vddci;
                        }
                }
        }
@@ -2347,6 +2387,15 @@ static bool radeon_atombios_parse_pplib_clock_info(struct radeon_device *rdev,
                        sclk |= clock_info->rs780.ucLowEngineClockHigh << 16;
                        rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk;
                }
+       } else if (rdev->family >= CHIP_BONAIRE) {
+               sclk = le16_to_cpu(clock_info->ci.usEngineClockLow);
+               sclk |= clock_info->ci.ucEngineClockHigh << 16;
+               mclk = le16_to_cpu(clock_info->ci.usMemoryClockLow);
+               mclk |= clock_info->ci.ucMemoryClockHigh << 16;
+               rdev->pm.power_state[state_index].clock_info[mode_index].mclk = mclk;
+               rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk;
+               rdev->pm.power_state[state_index].clock_info[mode_index].voltage.type =
+                       VOLTAGE_NONE;
        } else if (rdev->family >= CHIP_TAHITI) {
                sclk = le16_to_cpu(clock_info->si.usEngineClockLow);
                sclk |= clock_info->si.ucEngineClockHigh << 16;
@@ -2392,6 +2441,10 @@ static bool radeon_atombios_parse_pplib_clock_info(struct radeon_device *rdev,
        case ATOM_VIRTUAL_VOLTAGE_ID1:
        case ATOM_VIRTUAL_VOLTAGE_ID2:
        case ATOM_VIRTUAL_VOLTAGE_ID3:
+       case ATOM_VIRTUAL_VOLTAGE_ID4:
+       case ATOM_VIRTUAL_VOLTAGE_ID5:
+       case ATOM_VIRTUAL_VOLTAGE_ID6:
+       case ATOM_VIRTUAL_VOLTAGE_ID7:
                if (radeon_atom_get_max_vddc(rdev, VOLTAGE_TYPE_VDDC,
                                             rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage,
                                             &vddc) == 0)
@@ -2667,6 +2720,8 @@ union get_clock_dividers {
        struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3 v3;
        struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4 v4;
        struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5 v5;
+       struct _COMPUTE_GPU_CLOCK_INPUT_PARAMETERS_V1_6 v6_in;
+       struct _COMPUTE_GPU_CLOCK_OUTPUT_PARAMETERS_V1_6 v6_out;
 };
 
 int radeon_atom_get_clock_dividers(struct radeon_device *rdev,
@@ -2699,7 +2754,8 @@ int radeon_atom_get_clock_dividers(struct radeon_device *rdev,
                break;
        case 2:
        case 3:
-               /* r6xx, r7xx, evergreen, ni */
+       case 5:
+               /* r6xx, r7xx, evergreen, ni, si */
                if (rdev->family <= CHIP_RV770) {
                        args.v2.ucAction = clock_type;
                        args.v2.ulClock = cpu_to_le32(clock);   /* 10 khz */
@@ -2732,6 +2788,9 @@ int radeon_atom_get_clock_dividers(struct radeon_device *rdev,
                                dividers->vco_mode = (args.v3.ucCntlFlag &
                                                      ATOM_PLL_CNTL_FLAG_MPLL_VCO_MODE) ? 1 : 0;
                        } else {
+                               /* for SI we use ComputeMemoryClockParam for memory plls */
+                               if (rdev->family >= CHIP_TAHITI)
+                                       return -EINVAL;
                                args.v5.ulClockParams = cpu_to_le32((clock_type << 24) | clock);
                                if (strobe_mode)
                                        args.v5.ucInputFlag = ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN;
@@ -2757,9 +2816,76 @@ int radeon_atom_get_clock_dividers(struct radeon_device *rdev,
 
                atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 
-               dividers->post_div = args.v4.ucPostDiv;
+               dividers->post_divider = dividers->post_div = args.v4.ucPostDiv;
                dividers->real_clock = le32_to_cpu(args.v4.ulClock);
                break;
+       case 6:
+               /* CI */
+               /* COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK, COMPUTE_GPUCLK_INPUT_FLAG_SCLK */
+               args.v6_in.ulClock.ulComputeClockFlag = clock_type;
+               args.v6_in.ulClock.ulClockFreq = cpu_to_le32(clock);    /* 10 khz */
+
+               atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+               dividers->whole_fb_div = le16_to_cpu(args.v6_out.ulFbDiv.usFbDiv);
+               dividers->frac_fb_div = le16_to_cpu(args.v6_out.ulFbDiv.usFbDivFrac);
+               dividers->ref_div = args.v6_out.ucPllRefDiv;
+               dividers->post_div = args.v6_out.ucPllPostDiv;
+               dividers->flags = args.v6_out.ucPllCntlFlag;
+               dividers->real_clock = le32_to_cpu(args.v6_out.ulClock.ulClock);
+               dividers->post_divider = args.v6_out.ulClock.ucPostDiv;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int radeon_atom_get_memory_pll_dividers(struct radeon_device *rdev,
+                                       u32 clock,
+                                       bool strobe_mode,
+                                       struct atom_mpll_param *mpll_param)
+{
+       COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_1 args;
+       int index = GetIndexIntoMasterTable(COMMAND, ComputeMemoryClockParam);
+       u8 frev, crev;
+
+       memset(&args, 0, sizeof(args));
+       memset(mpll_param, 0, sizeof(struct atom_mpll_param));
+
+       if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
+               return -EINVAL;
+
+       switch (frev) {
+       case 2:
+               switch (crev) {
+               case 1:
+                       /* SI */
+                       args.ulClock = cpu_to_le32(clock);      /* 10 khz */
+                       args.ucInputFlag = 0;
+                       if (strobe_mode)
+                               args.ucInputFlag |= MPLL_INPUT_FLAG_STROBE_MODE_EN;
+
+                       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+                       mpll_param->clkfrac = le16_to_cpu(args.ulFbDiv.usFbDivFrac);
+                       mpll_param->clkf = le16_to_cpu(args.ulFbDiv.usFbDiv);
+                       mpll_param->post_div = args.ucPostDiv;
+                       mpll_param->dll_speed = args.ucDllSpeed;
+                       mpll_param->bwcntl = args.ucBWCntl;
+                       mpll_param->vco_mode =
+                               (args.ucPllCntlFlag & MPLL_CNTL_FLAG_VCO_MODE_MASK) ? 1 : 0;
+                       mpll_param->yclk_sel =
+                               (args.ucPllCntlFlag & MPLL_CNTL_FLAG_BYPASS_DQ_PLL) ? 1 : 0;
+                       mpll_param->qdr =
+                               (args.ucPllCntlFlag & MPLL_CNTL_FLAG_QDR_ENABLE) ? 1 : 0;
+                       mpll_param->half_rate =
+                               (args.ucPllCntlFlag & MPLL_CNTL_FLAG_AD_HALF_RATE) ? 1 : 0;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
        default:
                return -EINVAL;
        }
@@ -2819,6 +2945,48 @@ void radeon_atom_set_memory_clock(struct radeon_device *rdev,
        atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
+void radeon_atom_set_engine_dram_timings(struct radeon_device *rdev,
+                                        u32 eng_clock, u32 mem_clock)
+{
+       SET_ENGINE_CLOCK_PS_ALLOCATION args;
+       int index = GetIndexIntoMasterTable(COMMAND, DynamicMemorySettings);
+       u32 tmp;
+
+       memset(&args, 0, sizeof(args));
+
+       tmp = eng_clock & SET_CLOCK_FREQ_MASK;
+       tmp |= (COMPUTE_ENGINE_PLL_PARAM << 24);
+
+       args.ulTargetEngineClock = cpu_to_le32(tmp);
+       if (mem_clock)
+               args.sReserved.ulClock = cpu_to_le32(mem_clock & SET_CLOCK_FREQ_MASK);
+
+       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+}
+
+void radeon_atom_update_memory_dll(struct radeon_device *rdev,
+                                  u32 mem_clock)
+{
+       u32 args;
+       int index = GetIndexIntoMasterTable(COMMAND, DynamicMemorySettings);
+
+       args = cpu_to_le32(mem_clock);  /* 10 khz */
+
+       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+}
+
+void radeon_atom_set_ac_timing(struct radeon_device *rdev,
+                              u32 mem_clock)
+{
+       SET_MEMORY_CLOCK_PS_ALLOCATION args;
+       int index = GetIndexIntoMasterTable(COMMAND, DynamicMemorySettings);
+       u32 tmp = mem_clock | (COMPUTE_MEMORY_PLL_PARAM << 24);
+
+       args.ulTargetMemoryClock = cpu_to_le32(tmp);    /* 10 khz */
+
+       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+}
+
 union set_voltage {
        struct _SET_VOLTAGE_PS_ALLOCATION alloc;
        struct _SET_VOLTAGE_PARAMETERS v1;
@@ -2863,8 +3031,8 @@ void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 v
        atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
-static int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type,
-                                   u16 voltage_id, u16 *voltage)
+int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type,
+                            u16 voltage_id, u16 *voltage)
 {
        union set_voltage args;
        int index = GetIndexIntoMasterTable(COMMAND, SetVoltage);
@@ -2902,6 +3070,695 @@ static int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type,
        return 0;
 }
 
+int radeon_atom_get_leakage_vddc_based_on_leakage_idx(struct radeon_device *rdev,
+                                                     u16 *voltage,
+                                                     u16 leakage_idx)
+{
+       return radeon_atom_get_max_vddc(rdev, VOLTAGE_TYPE_VDDC, leakage_idx, voltage);
+}
+
+int radeon_atom_get_voltage_gpio_settings(struct radeon_device *rdev,
+                                         u16 voltage_level, u8 voltage_type,
+                                         u32 *gpio_value, u32 *gpio_mask)
+{
+       union set_voltage args;
+       int index = GetIndexIntoMasterTable(COMMAND, SetVoltage);
+       u8 frev, crev;
+
+       if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
+               return -EINVAL;
+
+       switch (crev) {
+       case 1:
+               return -EINVAL;
+       case 2:
+               args.v2.ucVoltageType = voltage_type;
+               args.v2.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_GET_GPIOMASK;
+               args.v2.usVoltageLevel = cpu_to_le16(voltage_level);
+
+               atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+               *gpio_mask = le32_to_cpu(*(u32 *)&args.v2);
+
+               args.v2.ucVoltageType = voltage_type;
+               args.v2.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_GET_GPIOVAL;
+               args.v2.usVoltageLevel = cpu_to_le16(voltage_level);
+
+               atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+               *gpio_value = le32_to_cpu(*(u32 *)&args.v2);
+               break;
+       default:
+               DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+union voltage_object_info {
+       struct _ATOM_VOLTAGE_OBJECT_INFO v1;
+       struct _ATOM_VOLTAGE_OBJECT_INFO_V2 v2;
+       struct _ATOM_VOLTAGE_OBJECT_INFO_V3_1 v3;
+};
+
+union voltage_object {
+       struct _ATOM_VOLTAGE_OBJECT v1;
+       struct _ATOM_VOLTAGE_OBJECT_V2 v2;
+       union _ATOM_VOLTAGE_OBJECT_V3 v3;
+};
+
+static ATOM_VOLTAGE_OBJECT *atom_lookup_voltage_object_v1(ATOM_VOLTAGE_OBJECT_INFO *v1,
+                                                         u8 voltage_type)
+{
+       u32 size = le16_to_cpu(v1->sHeader.usStructureSize);
+       u32 offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO, asVoltageObj[0]);
+       u8 *start = (u8 *)v1;
+
+       while (offset < size) {
+               ATOM_VOLTAGE_OBJECT *vo = (ATOM_VOLTAGE_OBJECT *)(start + offset);
+               if (vo->ucVoltageType == voltage_type)
+                       return vo;
+               offset += offsetof(ATOM_VOLTAGE_OBJECT, asFormula.ucVIDAdjustEntries) +
+                       vo->asFormula.ucNumOfVoltageEntries;
+       }
+       return NULL;
+}
+
+static ATOM_VOLTAGE_OBJECT_V2 *atom_lookup_voltage_object_v2(ATOM_VOLTAGE_OBJECT_INFO_V2 *v2,
+                                                            u8 voltage_type)
+{
+       u32 size = le16_to_cpu(v2->sHeader.usStructureSize);
+       u32 offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO_V2, asVoltageObj[0]);
+       u8 *start = (u8*)v2;
+
+       while (offset < size) {
+               ATOM_VOLTAGE_OBJECT_V2 *vo = (ATOM_VOLTAGE_OBJECT_V2 *)(start + offset);
+               if (vo->ucVoltageType == voltage_type)
+                       return vo;
+               offset += offsetof(ATOM_VOLTAGE_OBJECT_V2, asFormula.asVIDAdjustEntries) +
+                       (vo->asFormula.ucNumOfVoltageEntries * sizeof(VOLTAGE_LUT_ENTRY));
+       }
+       return NULL;
+}
+
+static ATOM_VOLTAGE_OBJECT_V3 *atom_lookup_voltage_object_v3(ATOM_VOLTAGE_OBJECT_INFO_V3_1 *v3,
+                                                            u8 voltage_type, u8 voltage_mode)
+{
+       u32 size = le16_to_cpu(v3->sHeader.usStructureSize);
+       u32 offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO_V3_1, asVoltageObj[0]);
+       u8 *start = (u8*)v3;
+
+       while (offset < size) {
+               ATOM_VOLTAGE_OBJECT_V3 *vo = (ATOM_VOLTAGE_OBJECT_V3 *)(start + offset);
+               if ((vo->asGpioVoltageObj.sHeader.ucVoltageType == voltage_type) &&
+                   (vo->asGpioVoltageObj.sHeader.ucVoltageMode == voltage_mode))
+                       return vo;
+               offset += le16_to_cpu(vo->asGpioVoltageObj.sHeader.usSize);
+       }
+       return NULL;
+}
+
+bool
+radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
+                           u8 voltage_type, u8 voltage_mode)
+{
+       int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
+       u8 frev, crev;
+       u16 data_offset, size;
+       union voltage_object_info *voltage_info;
+       union voltage_object *voltage_object = NULL;
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                  &frev, &crev, &data_offset)) {
+               voltage_info = (union voltage_object_info *)
+                       (rdev->mode_info.atom_context->bios + data_offset);
+
+               switch (frev) {
+               case 1:
+               case 2:
+                       switch (crev) {
+                       case 1:
+                               voltage_object = (union voltage_object *)
+                                       atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
+                               if (voltage_object &&
+                                   (voltage_object->v1.asControl.ucVoltageControlId == VOLTAGE_CONTROLLED_BY_GPIO))
+                                       return true;
+                               break;
+                       case 2:
+                               voltage_object = (union voltage_object *)
+                                       atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
+                               if (voltage_object &&
+                                   (voltage_object->v2.asControl.ucVoltageControlId == VOLTAGE_CONTROLLED_BY_GPIO))
+                                       return true;
+                               break;
+                       default:
+                               DRM_ERROR("unknown voltage object table\n");
+                               return false;
+                       }
+                       break;
+               case 3:
+                       switch (crev) {
+                       case 1:
+                               if (atom_lookup_voltage_object_v3(&voltage_info->v3,
+                                                                 voltage_type, voltage_mode))
+                                       return true;
+                               break;
+                       default:
+                               DRM_ERROR("unknown voltage object table\n");
+                               return false;
+                       }
+                       break;
+               default:
+                       DRM_ERROR("unknown voltage object table\n");
+                       return false;
+               }
+
+       }
+       return false;
+}
+
+int radeon_atom_get_max_voltage(struct radeon_device *rdev,
+                               u8 voltage_type, u16 *max_voltage)
+{
+       int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
+       u8 frev, crev;
+       u16 data_offset, size;
+       union voltage_object_info *voltage_info;
+       union voltage_object *voltage_object = NULL;
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                  &frev, &crev, &data_offset)) {
+               voltage_info = (union voltage_object_info *)
+                       (rdev->mode_info.atom_context->bios + data_offset);
+
+               switch (crev) {
+               case 1:
+                       voltage_object = (union voltage_object *)
+                               atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
+                       if (voltage_object) {
+                               ATOM_VOLTAGE_FORMULA *formula =
+                                       &voltage_object->v1.asFormula;
+                               if (formula->ucFlag & 1)
+                                       *max_voltage =
+                                               le16_to_cpu(formula->usVoltageBaseLevel) +
+                                               formula->ucNumOfVoltageEntries / 2 *
+                                               le16_to_cpu(formula->usVoltageStep);
+                               else
+                                       *max_voltage =
+                                               le16_to_cpu(formula->usVoltageBaseLevel) +
+                                               (formula->ucNumOfVoltageEntries - 1) *
+                                               le16_to_cpu(formula->usVoltageStep);
+                               return 0;
+                       }
+                       break;
+               case 2:
+                       voltage_object = (union voltage_object *)
+                               atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
+                       if (voltage_object) {
+                               ATOM_VOLTAGE_FORMULA_V2 *formula =
+                                       &voltage_object->v2.asFormula;
+                               if (formula->ucNumOfVoltageEntries) {
+                                       *max_voltage =
+                                               le16_to_cpu(formula->asVIDAdjustEntries[
+                                                                   formula->ucNumOfVoltageEntries - 1
+                                                                   ].usVoltageValue);
+                                       return 0;
+                               }
+                       }
+                       break;
+               default:
+                       DRM_ERROR("unknown voltage object table\n");
+                       return -EINVAL;
+               }
+
+       }
+       return -EINVAL;
+}
+
+int radeon_atom_get_min_voltage(struct radeon_device *rdev,
+                               u8 voltage_type, u16 *min_voltage)
+{
+       int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
+       u8 frev, crev;
+       u16 data_offset, size;
+       union voltage_object_info *voltage_info;
+       union voltage_object *voltage_object = NULL;
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                  &frev, &crev, &data_offset)) {
+               voltage_info = (union voltage_object_info *)
+                       (rdev->mode_info.atom_context->bios + data_offset);
+
+               switch (crev) {
+               case 1:
+                       voltage_object = (union voltage_object *)
+                               atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
+                       if (voltage_object) {
+                               ATOM_VOLTAGE_FORMULA *formula =
+                                       &voltage_object->v1.asFormula;
+                               *min_voltage =
+                                       le16_to_cpu(formula->usVoltageBaseLevel);
+                               return 0;
+                       }
+                       break;
+               case 2:
+                       voltage_object = (union voltage_object *)
+                               atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
+                       if (voltage_object) {
+                               ATOM_VOLTAGE_FORMULA_V2 *formula =
+                                       &voltage_object->v2.asFormula;
+                               if (formula->ucNumOfVoltageEntries) {
+                                       *min_voltage =
+                                               le16_to_cpu(formula->asVIDAdjustEntries[
+                                                                   0
+                                                                   ].usVoltageValue);
+                                       return 0;
+                               }
+                       }
+                       break;
+               default:
+                       DRM_ERROR("unknown voltage object table\n");
+                       return -EINVAL;
+               }
+
+       }
+       return -EINVAL;
+}
+
+int radeon_atom_get_voltage_step(struct radeon_device *rdev,
+                                u8 voltage_type, u16 *voltage_step)
+{
+       int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
+       u8 frev, crev;
+       u16 data_offset, size;
+       union voltage_object_info *voltage_info;
+       union voltage_object *voltage_object = NULL;
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                  &frev, &crev, &data_offset)) {
+               voltage_info = (union voltage_object_info *)
+                       (rdev->mode_info.atom_context->bios + data_offset);
+
+               switch (crev) {
+               case 1:
+                       voltage_object = (union voltage_object *)
+                               atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
+                       if (voltage_object) {
+                               ATOM_VOLTAGE_FORMULA *formula =
+                                       &voltage_object->v1.asFormula;
+                               if (formula->ucFlag & 1)
+                                       *voltage_step =
+                                               (le16_to_cpu(formula->usVoltageStep) + 1) / 2;
+                               else
+                                       *voltage_step =
+                                               le16_to_cpu(formula->usVoltageStep);
+                               return 0;
+                       }
+                       break;
+               case 2:
+                       return -EINVAL;
+               default:
+                       DRM_ERROR("unknown voltage object table\n");
+                       return -EINVAL;
+               }
+
+       }
+       return -EINVAL;
+}
+
+int radeon_atom_round_to_true_voltage(struct radeon_device *rdev,
+                                     u8 voltage_type,
+                                     u16 nominal_voltage,
+                                     u16 *true_voltage)
+{
+       u16 min_voltage, max_voltage, voltage_step;
+
+       if (radeon_atom_get_max_voltage(rdev, voltage_type, &max_voltage))
+               return -EINVAL;
+       if (radeon_atom_get_min_voltage(rdev, voltage_type, &min_voltage))
+               return -EINVAL;
+       if (radeon_atom_get_voltage_step(rdev, voltage_type, &voltage_step))
+               return -EINVAL;
+
+       if (nominal_voltage <= min_voltage)
+               *true_voltage = min_voltage;
+       else if (nominal_voltage >= max_voltage)
+               *true_voltage = max_voltage;
+       else
+               *true_voltage = min_voltage +
+                       ((nominal_voltage - min_voltage) / voltage_step) *
+                       voltage_step;
+
+       return 0;
+}
+
+int radeon_atom_get_voltage_table(struct radeon_device *rdev,
+                                 u8 voltage_type, u8 voltage_mode,
+                                 struct atom_voltage_table *voltage_table)
+{
+       int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
+       u8 frev, crev;
+       u16 data_offset, size;
+       int i, ret;
+       union voltage_object_info *voltage_info;
+       union voltage_object *voltage_object = NULL;
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                  &frev, &crev, &data_offset)) {
+               voltage_info = (union voltage_object_info *)
+                       (rdev->mode_info.atom_context->bios + data_offset);
+
+               switch (frev) {
+               case 1:
+               case 2:
+                       switch (crev) {
+                       case 1:
+                               DRM_ERROR("old table version %d, %d\n", frev, crev);
+                               return -EINVAL;
+                       case 2:
+                               voltage_object = (union voltage_object *)
+                                       atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
+                               if (voltage_object) {
+                                       ATOM_VOLTAGE_FORMULA_V2 *formula =
+                                               &voltage_object->v2.asFormula;
+                                       if (formula->ucNumOfVoltageEntries > MAX_VOLTAGE_ENTRIES)
+                                               return -EINVAL;
+                                       for (i = 0; i < formula->ucNumOfVoltageEntries; i++) {
+                                               voltage_table->entries[i].value =
+                                                       le16_to_cpu(formula->asVIDAdjustEntries[i].usVoltageValue);
+                                               ret = radeon_atom_get_voltage_gpio_settings(rdev,
+                                                                                           voltage_table->entries[i].value,
+                                                                                           voltage_type,
+                                                                                           &voltage_table->entries[i].smio_low,
+                                                                                           &voltage_table->mask_low);
+                                               if (ret)
+                                                       return ret;
+                                       }
+                                       voltage_table->count = formula->ucNumOfVoltageEntries;
+                                       return 0;
+                               }
+                               break;
+                       default:
+                               DRM_ERROR("unknown voltage object table\n");
+                               return -EINVAL;
+                       }
+                       break;
+               case 3:
+                       switch (crev) {
+                       case 1:
+                               voltage_object = (union voltage_object *)
+                                       atom_lookup_voltage_object_v3(&voltage_info->v3,
+                                                                     voltage_type, voltage_mode);
+                               if (voltage_object) {
+                                       ATOM_GPIO_VOLTAGE_OBJECT_V3 *gpio =
+                                               &voltage_object->v3.asGpioVoltageObj;
+                                       if (gpio->ucGpioEntryNum > MAX_VOLTAGE_ENTRIES)
+                                               return -EINVAL;
+                                       for (i = 0; i < gpio->ucGpioEntryNum; i++) {
+                                               voltage_table->entries[i].value =
+                                                       le16_to_cpu(gpio->asVolGpioLut[i].usVoltageValue);
+                                               voltage_table->entries[i].smio_low =
+                                                       le32_to_cpu(gpio->asVolGpioLut[i].ulVoltageId);
+                                       }
+                                       voltage_table->mask_low = le32_to_cpu(gpio->ulGpioMaskVal);
+                                       voltage_table->count = gpio->ucGpioEntryNum;
+                                       voltage_table->phase_delay = gpio->ucPhaseDelay;
+                                       return 0;
+                               }
+                               break;
+                       default:
+                               DRM_ERROR("unknown voltage object table\n");
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       DRM_ERROR("unknown voltage object table\n");
+                       return -EINVAL;
+               }
+       }
+       return -EINVAL;
+}
+
+union vram_info {
+       struct _ATOM_VRAM_INFO_V3 v1_3;
+       struct _ATOM_VRAM_INFO_V4 v1_4;
+       struct _ATOM_VRAM_INFO_HEADER_V2_1 v2_1;
+};
+
+int radeon_atom_get_memory_info(struct radeon_device *rdev,
+                               u8 module_index, struct atom_memory_info *mem_info)
+{
+       int index = GetIndexIntoMasterTable(DATA, VRAM_Info);
+       u8 frev, crev, i;
+       u16 data_offset, size;
+       union vram_info *vram_info;
+       u8 *p;
+
+       memset(mem_info, 0, sizeof(struct atom_memory_info));
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                  &frev, &crev, &data_offset)) {
+               vram_info = (union vram_info *)
+                       (rdev->mode_info.atom_context->bios + data_offset);
+               switch (frev) {
+               case 1:
+                       switch (crev) {
+                       case 3:
+                               /* r6xx */
+                               if (module_index < vram_info->v1_3.ucNumOfVRAMModule) {
+                                       ATOM_VRAM_MODULE_V3 *vram_module =
+                                               (ATOM_VRAM_MODULE_V3 *)vram_info->v1_3.aVramInfo;
+                                       p = (u8 *)vram_info->v1_3.aVramInfo;
+
+                                       for (i = 0; i < module_index; i++) {
+                                               vram_module = (ATOM_VRAM_MODULE_V3 *)p;
+                                               if (le16_to_cpu(vram_module->usSize) == 0)
+                                                       return -EINVAL;
+                                               p += le16_to_cpu(vram_module->usSize);
+                                       }
+                                       mem_info->mem_vendor = vram_module->asMemory.ucMemoryVenderID & 0xf;
+                                       mem_info->mem_type = vram_module->asMemory.ucMemoryType & 0xf0;
+                               } else
+                                       return -EINVAL;
+                               break;
+                       case 4:
+                               /* r7xx, evergreen */
+                               if (module_index < vram_info->v1_4.ucNumOfVRAMModule) {
+                                       ATOM_VRAM_MODULE_V4 *vram_module =
+                                               (ATOM_VRAM_MODULE_V4 *)vram_info->v1_4.aVramInfo;
+                                       p = (u8 *)vram_info->v1_4.aVramInfo;
+
+                                       for (i = 0; i < module_index; i++) {
+                                               vram_module = (ATOM_VRAM_MODULE_V4 *)p;
+                                               if (le16_to_cpu(vram_module->usModuleSize) == 0)
+                                                       return -EINVAL;
+                                               p += le16_to_cpu(vram_module->usModuleSize);
+                                       }
+                                       mem_info->mem_vendor = vram_module->ucMemoryVenderID & 0xf;
+                                       mem_info->mem_type = vram_module->ucMemoryType & 0xf0;
+                               } else
+                                       return -EINVAL;
+                               break;
+                       default:
+                               DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+                               return -EINVAL;
+                       }
+                       break;
+               case 2:
+                       switch (crev) {
+                       case 1:
+                               /* ni */
+                               if (module_index < vram_info->v2_1.ucNumOfVRAMModule) {
+                                       ATOM_VRAM_MODULE_V7 *vram_module =
+                                               (ATOM_VRAM_MODULE_V7 *)vram_info->v2_1.aVramInfo;
+                                       p = (u8 *)vram_info->v2_1.aVramInfo;
+
+                                       for (i = 0; i < module_index; i++) {
+                                               vram_module = (ATOM_VRAM_MODULE_V7 *)p;
+                                               if (le16_to_cpu(vram_module->usModuleSize) == 0)
+                                                       return -EINVAL;
+                                               p += le16_to_cpu(vram_module->usModuleSize);
+                                       }
+                                       mem_info->mem_vendor = vram_module->ucMemoryVenderID & 0xf;
+                                       mem_info->mem_type = vram_module->ucMemoryType & 0xf0;
+                               } else
+                                       return -EINVAL;
+                               break;
+                       default:
+                               DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+                       return -EINVAL;
+               }
+               return 0;
+       }
+       return -EINVAL;
+}
+
+int radeon_atom_get_mclk_range_table(struct radeon_device *rdev,
+                                    bool gddr5, u8 module_index,
+                                    struct atom_memory_clock_range_table *mclk_range_table)
+{
+       int index = GetIndexIntoMasterTable(DATA, VRAM_Info);
+       u8 frev, crev, i;
+       u16 data_offset, size;
+       union vram_info *vram_info;
+       u32 mem_timing_size = gddr5 ?
+               sizeof(ATOM_MEMORY_TIMING_FORMAT_V2) : sizeof(ATOM_MEMORY_TIMING_FORMAT);
+       u8 *p;
+
+       memset(mclk_range_table, 0, sizeof(struct atom_memory_clock_range_table));
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                  &frev, &crev, &data_offset)) {
+               vram_info = (union vram_info *)
+                       (rdev->mode_info.atom_context->bios + data_offset);
+               switch (frev) {
+               case 1:
+                       switch (crev) {
+                       case 3:
+                               DRM_ERROR("old table version %d, %d\n", frev, crev);
+                               return -EINVAL;
+                       case 4:
+                               /* r7xx, evergreen */
+                               if (module_index < vram_info->v1_4.ucNumOfVRAMModule) {
+                                       ATOM_VRAM_MODULE_V4 *vram_module =
+                                               (ATOM_VRAM_MODULE_V4 *)vram_info->v1_4.aVramInfo;
+                                       ATOM_MEMORY_TIMING_FORMAT *format;
+                                       p = (u8 *)vram_info->v1_4.aVramInfo;
+
+                                       for (i = 0; i < module_index; i++) {
+                                               vram_module = (ATOM_VRAM_MODULE_V4 *)p;
+                                               if (le16_to_cpu(vram_module->usModuleSize) == 0)
+                                                       return -EINVAL;
+                                               p += le16_to_cpu(vram_module->usModuleSize);
+                                       }
+                                       mclk_range_table->num_entries = (u8)
+                                               ((vram_module->usModuleSize - offsetof(ATOM_VRAM_MODULE_V4, asMemTiming)) /
+                                                mem_timing_size);
+                                       p = (u8 *)vram_module->asMemTiming;
+                                       for (i = 0; i < mclk_range_table->num_entries; i++) {
+                                               format = (ATOM_MEMORY_TIMING_FORMAT *)p;
+                                               mclk_range_table->mclk[i] = le32_to_cpu(format->ulClkRange);
+                                               p += mem_timing_size;
+                                       }
+                               } else
+                                       return -EINVAL;
+                               break;
+                       default:
+                               DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+                               return -EINVAL;
+                       }
+                       break;
+               case 2:
+                       DRM_ERROR("new table version %d, %d\n", frev, crev);
+                       return -EINVAL;
+               default:
+                       DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+                       return -EINVAL;
+               }
+               return 0;
+       }
+       return -EINVAL;
+}
+
+#define MEM_ID_MASK           0xff000000
+#define MEM_ID_SHIFT          24
+#define CLOCK_RANGE_MASK      0x00ffffff
+#define CLOCK_RANGE_SHIFT     0
+#define LOW_NIBBLE_MASK       0xf
+#define DATA_EQU_PREV         0
+#define DATA_FROM_TABLE       4
+
+int radeon_atom_init_mc_reg_table(struct radeon_device *rdev,
+                                 u8 module_index,
+                                 struct atom_mc_reg_table *reg_table)
+{
+       int index = GetIndexIntoMasterTable(DATA, VRAM_Info);
+       u8 frev, crev, num_entries, t_mem_id, num_ranges = 0;
+       u32 i = 0, j;
+       u16 data_offset, size;
+       union vram_info *vram_info;
+
+       memset(reg_table, 0, sizeof(struct atom_mc_reg_table));
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                  &frev, &crev, &data_offset)) {
+               vram_info = (union vram_info *)
+                       (rdev->mode_info.atom_context->bios + data_offset);
+               switch (frev) {
+               case 1:
+                       DRM_ERROR("old table version %d, %d\n", frev, crev);
+                       return -EINVAL;
+               case 2:
+                       switch (crev) {
+                       case 1:
+                               if (module_index < vram_info->v2_1.ucNumOfVRAMModule) {
+                                       ATOM_INIT_REG_BLOCK *reg_block =
+                                               (ATOM_INIT_REG_BLOCK *)
+                                               ((u8 *)vram_info + le16_to_cpu(vram_info->v2_1.usMemClkPatchTblOffset));
+                                       ATOM_MEMORY_SETTING_DATA_BLOCK *reg_data =
+                                               (ATOM_MEMORY_SETTING_DATA_BLOCK *)
+                                               ((u8 *)reg_block + (2 * sizeof(u16)) +
+                                                le16_to_cpu(reg_block->usRegIndexTblSize));
+                                       num_entries = (u8)((le16_to_cpu(reg_block->usRegIndexTblSize)) /
+                                                          sizeof(ATOM_INIT_REG_INDEX_FORMAT)) - 1;
+                                       if (num_entries > VBIOS_MC_REGISTER_ARRAY_SIZE)
+                                               return -EINVAL;
+                                       while (!(reg_block->asRegIndexBuf[i].ucPreRegDataLength & ACCESS_PLACEHOLDER) &&
+                                             (i < num_entries)) {
+                                               reg_table->mc_reg_address[i].s1 =
+                                                       (u16)(le16_to_cpu(reg_block->asRegIndexBuf[i].usRegIndex));
+                                               reg_table->mc_reg_address[i].pre_reg_data =
+                                                       (u8)(reg_block->asRegIndexBuf[i].ucPreRegDataLength);
+                                               i++;
+                                       }
+                                       reg_table->last = i;
+                                       while ((*(u32 *)reg_data != END_OF_REG_DATA_BLOCK) &&
+                                              (num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES)) {
+                                               t_mem_id = (u8)((*(u32 *)reg_data & MEM_ID_MASK) >> MEM_ID_SHIFT);
+                                               if (module_index == t_mem_id) {
+                                                       reg_table->mc_reg_table_entry[num_ranges].mclk_max =
+                                                               (u32)((*(u32 *)reg_data & CLOCK_RANGE_MASK) >> CLOCK_RANGE_SHIFT);
+                                                       for (i = 0, j = 1; i < reg_table->last; i++) {
+                                                               if ((reg_table->mc_reg_address[i].pre_reg_data & LOW_NIBBLE_MASK) == DATA_FROM_TABLE) {
+                                                                       reg_table->mc_reg_table_entry[num_ranges].mc_data[i] =
+                                                                               (u32)*((u32 *)reg_data + j);
+                                                                       j++;
+                                                               } else if ((reg_table->mc_reg_address[i].pre_reg_data & LOW_NIBBLE_MASK) == DATA_EQU_PREV) {
+                                                                       reg_table->mc_reg_table_entry[num_ranges].mc_data[i] =
+                                                                               reg_table->mc_reg_table_entry[num_ranges].mc_data[i - 1];
+                                                               }
+                                                       }
+                                                       num_ranges++;
+                                               }
+                                               reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *)
+                                                       ((u8 *)reg_data + le16_to_cpu(reg_block->usRegDataBlkSize));
+                                       }
+                                       if (*(u32 *)reg_data != END_OF_REG_DATA_BLOCK)
+                                               return -EINVAL;
+                                       reg_table->num_entries = num_ranges;
+                               } else
+                                       return -EINVAL;
+                               break;
+                       default:
+                               DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+                       return -EINVAL;
+               }
+               return 0;
+       }
+       return -EINVAL;
+}
+
 void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev)
 {
        struct radeon_device *rdev = dev->dev_private;
index 7e265a5..13a130f 100644 (file)
@@ -106,7 +106,7 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
                radeon_bo_list_add_object(&p->relocs[i].lobj,
                                          &p->validated);
        }
-       return radeon_bo_list_validate(&p->validated, p->ring);
+       return radeon_bo_list_validate(&p->ticket, &p->validated, p->ring);
 }
 
 static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority)
@@ -314,15 +314,17 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
  * If error is set than unvalidate buffer, otherwise just free memory
  * used by parsing context.
  **/
-static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error)
+static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bool backoff)
 {
        unsigned i;
 
        if (!error) {
-               ttm_eu_fence_buffer_objects(&parser->validated,
+               ttm_eu_fence_buffer_objects(&parser->ticket,
+                                           &parser->validated,
                                            parser->ib.fence);
-       } else {
-               ttm_eu_backoff_reservation(&parser->validated);
+       } else if (backoff) {
+               ttm_eu_backoff_reservation(&parser->ticket,
+                                          &parser->validated);
        }
 
        if (parser->relocs != NULL) {
@@ -535,7 +537,7 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
        r = radeon_cs_parser_init(&parser, data);
        if (r) {
                DRM_ERROR("Failed to initialize parser !\n");
-               radeon_cs_parser_fini(&parser, r);
+               radeon_cs_parser_fini(&parser, r, false);
                up_read(&rdev->exclusive_lock);
                r = radeon_cs_handle_lockup(rdev, r);
                return r;
@@ -544,12 +546,13 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
        if (r) {
                if (r != -ERESTARTSYS)
                        DRM_ERROR("Failed to parse relocation %d!\n", r);
-               radeon_cs_parser_fini(&parser, r);
+               radeon_cs_parser_fini(&parser, r, false);
                up_read(&rdev->exclusive_lock);
                r = radeon_cs_handle_lockup(rdev, r);
                return r;
        }
 
+       /* XXX pick SD/HD/MVC */
        if (parser.ring == R600_RING_TYPE_UVD_INDEX)
                radeon_uvd_note_usage(rdev);
 
@@ -562,7 +565,7 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                goto out;
        }
 out:
-       radeon_cs_parser_fini(&parser, r);
+       radeon_cs_parser_fini(&parser, r, true);
        up_read(&rdev->exclusive_lock);
        r = radeon_cs_handle_lockup(rdev, r);
        return r;
index b097d5b..9630e8d 100644 (file)
@@ -27,9 +27,6 @@
 #include <drm/radeon_drm.h>
 #include "radeon.h"
 
-#define CURSOR_WIDTH 64
-#define CURSOR_HEIGHT 64
-
 static void radeon_lock_cursor(struct drm_crtc *crtc, bool lock)
 {
        struct radeon_device *rdev = crtc->dev->dev_private;
@@ -167,7 +164,8 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
                goto unpin;
        }
 
-       if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) {
+       if ((width > radeon_crtc->max_cursor_width) ||
+           (height > radeon_crtc->max_cursor_height)) {
                DRM_ERROR("bad cursor width or height %d x %d\n", width, height);
                return -EINVAL;
        }
@@ -233,11 +231,11 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc,
        DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
 
        if (x < 0) {
-               xorigin = min(-x, CURSOR_WIDTH - 1);
+               xorigin = min(-x, radeon_crtc->max_cursor_width - 1);
                x = 0;
        }
        if (y < 0) {
-               yorigin = min(-y, CURSOR_HEIGHT - 1);
+               yorigin = min(-y, radeon_crtc->max_cursor_height - 1);
                y = 0;
        }
 
index b0dc0b6..82335e3 100644 (file)
@@ -95,6 +95,9 @@ static const char radeon_family_name[][16] = {
        "VERDE",
        "OLAND",
        "HAINAN",
+       "BONAIRE",
+       "KAVERI",
+       "KABINI",
        "LAST",
 };
 
@@ -228,6 +231,94 @@ void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg)
        }
 }
 
+/*
+ * GPU doorbell aperture helpers function.
+ */
+/**
+ * radeon_doorbell_init - Init doorbell driver information.
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Init doorbell driver information (CIK)
+ * Returns 0 on success, error on failure.
+ */
+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.ptr = ioremap(rdev->doorbell.base, rdev->doorbell.size);
+       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;
+
+       for (i = 0; i < rdev->doorbell.num_pages; i++) {
+               rdev->doorbell.free[i] = true;
+       }
+       return 0;
+}
+
+/**
+ * radeon_doorbell_fini - Tear down doorbell driver information.
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Tear down doorbell driver information (CIK)
+ */
+void radeon_doorbell_fini(struct radeon_device *rdev)
+{
+       iounmap(rdev->doorbell.ptr);
+       rdev->doorbell.ptr = NULL;
+}
+
+/**
+ * radeon_doorbell_get - Allocate a doorbell page
+ *
+ * @rdev: radeon_device pointer
+ * @doorbell: doorbell page number
+ *
+ * Allocate a doorbell page 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;
+               }
+       }
+       return -EINVAL;
+}
+
+/**
+ * radeon_doorbell_free - Free a doorbell page
+ *
+ * @rdev: radeon_device pointer
+ * @doorbell: doorbell page number
+ *
+ * Free a doorbell page 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;
+}
+
 /*
  * radeon_wb_*()
  * Writeback is the the method by which the the GPU updates special pages
@@ -1145,8 +1236,13 @@ int radeon_device_init(struct radeon_device *rdev,
        /* Registers mapping */
        /* TODO: block userspace mapping of io register */
        spin_lock_init(&rdev->mmio_idx_lock);
-       rdev->rmmio_base = pci_resource_start(rdev->pdev, 2);
-       rdev->rmmio_size = pci_resource_len(rdev->pdev, 2);
+       if (rdev->family >= CHIP_BONAIRE) {
+               rdev->rmmio_base = pci_resource_start(rdev->pdev, 5);
+               rdev->rmmio_size = pci_resource_len(rdev->pdev, 5);
+       } else {
+               rdev->rmmio_base = pci_resource_start(rdev->pdev, 2);
+               rdev->rmmio_size = pci_resource_len(rdev->pdev, 2);
+       }
        rdev->rmmio = ioremap(rdev->rmmio_base, rdev->rmmio_size);
        if (rdev->rmmio == NULL) {
                return -ENOMEM;
@@ -1154,6 +1250,10 @@ int radeon_device_init(struct radeon_device *rdev,
        DRM_INFO("register mmio base: 0x%08X\n", (uint32_t)rdev->rmmio_base);
        DRM_INFO("register mmio size: %u\n", (unsigned)rdev->rmmio_size);
 
+       /* doorbell bar mapping */
+       if (rdev->family >= CHIP_BONAIRE)
+               radeon_doorbell_init(rdev);
+
        /* io port mapping */
        for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
                if (pci_resource_flags(rdev->pdev, i) & IORESOURCE_IO) {
@@ -1231,6 +1331,8 @@ void radeon_device_fini(struct radeon_device *rdev)
        rdev->rio_mem = NULL;
        iounmap(rdev->rmmio);
        rdev->rmmio = NULL;
+       if (rdev->family >= CHIP_BONAIRE)
+               radeon_doorbell_fini(rdev);
        radeon_debugfs_remove_files(rdev);
 }
 
index eb18bb7..c2b67b4 100644 (file)
@@ -153,7 +153,13 @@ static void dce5_crtc_load_lut(struct drm_crtc *crtc)
                NI_OUTPUT_CSC_OVL_MODE(NI_OUTPUT_CSC_BYPASS)));
        /* XXX match this to the depth of the crtc fmt block, move to modeset? */
        WREG32(0x6940 + radeon_crtc->crtc_offset, 0);
-
+       if (ASIC_IS_DCE8(rdev)) {
+               /* XXX this only needs to be programmed once per crtc at startup,
+                * not sure where the best place for it is
+                */
+               WREG32(CIK_ALPHA_CONTROL + radeon_crtc->crtc_offset,
+                      CIK_CURSOR_ALPHA_BLND_ENA);
+       }
 }
 
 static void legacy_crtc_load_lut(struct drm_crtc *crtc)
@@ -512,6 +518,14 @@ static void radeon_crtc_init(struct drm_device *dev, int index)
        radeon_crtc->crtc_id = index;
        rdev->mode_info.crtcs[index] = radeon_crtc;
 
+       if (rdev->family >= CHIP_BONAIRE) {
+               radeon_crtc->max_cursor_width = CIK_CURSOR_WIDTH;
+               radeon_crtc->max_cursor_height = CIK_CURSOR_HEIGHT;
+       } else {
+               radeon_crtc->max_cursor_width = CURSOR_WIDTH;
+               radeon_crtc->max_cursor_height = CURSOR_HEIGHT;
+       }
+
 #if 0
        radeon_crtc->mode_set.crtc = &radeon_crtc->base;
        radeon_crtc->mode_set.connectors = (struct drm_connector **)(radeon_crtc + 1);
@@ -530,7 +544,7 @@ static void radeon_crtc_init(struct drm_device *dev, int index)
                radeon_legacy_init_crtc(dev, radeon_crtc);
 }
 
-static const char *encoder_names[37] = {
+static const char *encoder_names[38] = {
        "NONE",
        "INTERNAL_LVDS",
        "INTERNAL_TMDS1",
@@ -567,7 +581,8 @@ static const char *encoder_names[37] = {
        "INTERNAL_UNIPHY2",
        "NUTMEG",
        "TRAVIS",
-       "INTERNAL_VCE"
+       "INTERNAL_VCE",
+       "INTERNAL_UNIPHY3",
 };
 
 static const char *hpd_names[6] = {
index 094e7e5..e5419b3 100644 (file)
  *   2.31.0 - Add fastfb support for rs690
  *   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
  */
 #define KMS_DRIVER_MAJOR       2
-#define KMS_DRIVER_MINOR       33
+#define KMS_DRIVER_MINOR       34
 #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);
@@ -127,6 +128,7 @@ struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev,
                                                        size_t size,
                                                        struct sg_table *sg);
 int radeon_gem_prime_pin(struct drm_gem_object *obj);
+void radeon_gem_prime_unpin(struct drm_gem_object *obj);
 void *radeon_gem_prime_vmap(struct drm_gem_object *obj);
 void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
 extern long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd,
@@ -164,6 +166,7 @@ int radeon_pcie_gen2 = -1;
 int radeon_msi = -1;
 int radeon_lockup_timeout = 10000;
 int radeon_fastfb = 0;
+int radeon_dpm = -1;
 
 MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
 module_param_named(no_wb, radeon_no_wb, int, 0444);
@@ -219,6 +222,9 @@ module_param_named(lockup_timeout, radeon_lockup_timeout, int, 0444);
 MODULE_PARM_DESC(fastfb, "Direct FB access for IGP chips (0 = disable, 1 = enable)");
 module_param_named(fastfb, radeon_fastfb, int, 0444);
 
+MODULE_PARM_DESC(dpm, "DPM support (1 = enable, 0 = disable, -1 = auto)");
+module_param_named(dpm, radeon_dpm, int, 0444);
+
 static struct pci_device_id pciidlist[] = {
        radeon_PCI_IDS
 };
@@ -422,6 +428,7 @@ static struct drm_driver kms_driver = {
        .gem_prime_export = drm_gem_prime_export,
        .gem_prime_import = drm_gem_prime_import,
        .gem_prime_pin = radeon_gem_prime_pin,
+       .gem_prime_unpin = radeon_gem_prime_unpin,
        .gem_prime_get_sg_table = radeon_gem_prime_get_sg_table,
        .gem_prime_import_sg_table = radeon_gem_prime_import_sg_table,
        .gem_prime_vmap = radeon_gem_prime_vmap,
index 36e9803..3c82890 100644 (file)
@@ -93,6 +93,9 @@ enum radeon_family {
        CHIP_VERDE,
        CHIP_OLAND,
        CHIP_HAINAN,
+       CHIP_BONAIRE,
+       CHIP_KAVERI,
+       CHIP_KABINI,
        CHIP_LAST,
 };
 
index 5a99d43..bcdefd1 100644 (file)
@@ -81,6 +81,23 @@ static void radeon_hotplug_work_func(struct work_struct *work)
        drm_helper_hpd_irq_event(dev);
 }
 
+/**
+ * radeon_irq_reset_work_func - execute gpu reset
+ *
+ * @work: work struct
+ *
+ * Execute scheduled gpu reset (cayman+).
+ * This function is called when the irq handler
+ * thinks we need a gpu reset.
+ */
+static void radeon_irq_reset_work_func(struct work_struct *work)
+{
+       struct radeon_device *rdev = container_of(work, struct radeon_device,
+                                                 reset_work);
+
+       radeon_gpu_reset(rdev);
+}
+
 /**
  * radeon_driver_irq_preinstall_kms - drm irq preinstall callback
  *
@@ -99,6 +116,7 @@ void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
        /* Disable *all* interrupts */
        for (i = 0; i < RADEON_NUM_RINGS; i++)
                atomic_set(&rdev->irq.ring_int[i], 0);
+       rdev->irq.dpm_thermal = false;
        for (i = 0; i < RADEON_MAX_HPD_PINS; i++)
                rdev->irq.hpd[i] = false;
        for (i = 0; i < RADEON_MAX_CRTCS; i++) {
@@ -146,6 +164,7 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev)
        /* Disable *all* interrupts */
        for (i = 0; i < RADEON_NUM_RINGS; i++)
                atomic_set(&rdev->irq.ring_int[i], 0);
+       rdev->irq.dpm_thermal = false;
        for (i = 0; i < RADEON_MAX_HPD_PINS; i++)
                rdev->irq.hpd[i] = false;
        for (i = 0; i < RADEON_MAX_CRTCS; i++) {
@@ -243,6 +262,7 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
 
        INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func);
        INIT_WORK(&rdev->audio_work, r600_audio_update_hdmi);
+       INIT_WORK(&rdev->reset_work, radeon_irq_reset_work_func);
 
        spin_lock_init(&rdev->irq.lock);
        r = drm_vblank_init(rdev->ddev, rdev->num_crtc);
index 4f2d4f4..49ff3d1 100644 (file)
@@ -229,7 +229,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                *value = rdev->accel_working;
                break;
        case RADEON_INFO_TILING_CONFIG:
-               if (rdev->family >= CHIP_TAHITI)
+               if (rdev->family >= CHIP_BONAIRE)
+                       *value = rdev->config.cik.tile_config;
+               else if (rdev->family >= CHIP_TAHITI)
                        *value = rdev->config.si.tile_config;
                else if (rdev->family >= CHIP_CAYMAN)
                        *value = rdev->config.cayman.tile_config;
@@ -281,7 +283,10 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                        *value = rdev->clock.spll.reference_freq * 10;
                break;
        case RADEON_INFO_NUM_BACKENDS:
-               if (rdev->family >= CHIP_TAHITI)
+               if (rdev->family >= CHIP_BONAIRE)
+                       *value = rdev->config.cik.max_backends_per_se *
+                               rdev->config.cik.max_shader_engines;
+               else if (rdev->family >= CHIP_TAHITI)
                        *value = rdev->config.si.max_backends_per_se *
                                rdev->config.si.max_shader_engines;
                else if (rdev->family >= CHIP_CAYMAN)
@@ -298,7 +303,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                }
                break;
        case RADEON_INFO_NUM_TILE_PIPES:
-               if (rdev->family >= CHIP_TAHITI)
+               if (rdev->family >= CHIP_BONAIRE)
+                       *value = rdev->config.cik.max_tile_pipes;
+               else if (rdev->family >= CHIP_TAHITI)
                        *value = rdev->config.si.max_tile_pipes;
                else if (rdev->family >= CHIP_CAYMAN)
                        *value = rdev->config.cayman.max_tile_pipes;
@@ -316,7 +323,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                *value = 1;
                break;
        case RADEON_INFO_BACKEND_MAP:
-               if (rdev->family >= CHIP_TAHITI)
+               if (rdev->family >= CHIP_BONAIRE)
+                       return -EINVAL;
+               else if (rdev->family >= CHIP_TAHITI)
                        *value = rdev->config.si.backend_map;
                else if (rdev->family >= CHIP_CAYMAN)
                        *value = rdev->config.cayman.backend_map;
@@ -343,7 +352,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                *value = RADEON_IB_VM_MAX_SIZE;
                break;
        case RADEON_INFO_MAX_PIPES:
-               if (rdev->family >= CHIP_TAHITI)
+               if (rdev->family >= CHIP_BONAIRE)
+                       *value = rdev->config.cik.max_cu_per_sh;
+               else if (rdev->family >= CHIP_TAHITI)
                        *value = rdev->config.si.max_cu_per_sh;
                else if (rdev->family >= CHIP_CAYMAN)
                        *value = rdev->config.cayman.max_pipes_per_simd;
@@ -367,7 +378,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                value64 = radeon_get_gpu_clock_counter(rdev);
                break;
        case RADEON_INFO_MAX_SE:
-               if (rdev->family >= CHIP_TAHITI)
+               if (rdev->family >= CHIP_BONAIRE)
+                       *value = rdev->config.cik.max_shader_engines;
+               else if (rdev->family >= CHIP_TAHITI)
                        *value = rdev->config.si.max_shader_engines;
                else if (rdev->family >= CHIP_CAYMAN)
                        *value = rdev->config.cayman.max_shader_engines;
@@ -377,7 +390,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                        *value = 1;
                break;
        case RADEON_INFO_MAX_SH_PER_SE:
-               if (rdev->family >= CHIP_TAHITI)
+               if (rdev->family >= CHIP_BONAIRE)
+                       *value = rdev->config.cik.max_sh_per_se;
+               else if (rdev->family >= CHIP_TAHITI)
                        *value = rdev->config.si.max_sh_per_se;
                else
                        return -EINVAL;
@@ -407,12 +422,16 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                }
                break;
        case RADEON_INFO_SI_TILE_MODE_ARRAY:
-               if (rdev->family < CHIP_TAHITI) {
-                       DRM_DEBUG_KMS("tile mode array is si only!\n");
+               if (rdev->family >= CHIP_BONAIRE) {
+                       value = rdev->config.cik.tile_mode_array;
+                       value_size = sizeof(uint32_t)*32;
+               } else if (rdev->family >= CHIP_TAHITI) {
+                       value = rdev->config.si.tile_mode_array;
+                       value_size = sizeof(uint32_t)*32;
+               } else {
+                       DRM_DEBUG_KMS("tile mode array is si+ only!\n");
                        return -EINVAL;
                }
-               value = rdev->config.si.tile_mode_array;
-               value_size = sizeof(uint32_t)*32;
                break;
        default:
                DRM_DEBUG_KMS("Invalid request %d\n", info->request);
index 69ad4fe..8296632 100644 (file)
@@ -307,6 +307,8 @@ struct radeon_crtc {
        uint64_t cursor_addr;
        int cursor_width;
        int cursor_height;
+       int max_cursor_width;
+       int max_cursor_height;
        uint32_t legacy_display_base_addr;
        uint32_t legacy_cursor_offset;
        enum radeon_rmx_type rmx_type;
@@ -329,6 +331,11 @@ struct radeon_crtc {
        u32 pll_flags;
        struct drm_encoder *encoder;
        struct drm_connector *connector;
+       /* for dpm */
+       u32 line_time;
+       u32 wm_low;
+       u32 wm_high;
+       struct drm_display_mode hw_mode;
 };
 
 struct radeon_encoder_primary_dac {
@@ -512,12 +519,99 @@ struct atom_clock_dividers {
        bool enable_dithen;
        u32 vco_mode;
        u32 real_clock;
+       /* added for CI */
+       u32 post_divider;
+       u32 flags;
+};
+
+struct atom_mpll_param {
+       union {
+               struct {
+#ifdef __BIG_ENDIAN
+                       u32 reserved : 8;
+                       u32 clkfrac : 12;
+                       u32 clkf : 12;
+#else
+                       u32 clkf : 12;
+                       u32 clkfrac : 12;
+                       u32 reserved : 8;
+#endif
+               };
+               u32 fb_div;
+       };
+       u32 post_div;
+       u32 bwcntl;
+       u32 dll_speed;
+       u32 vco_mode;
+       u32 yclk_sel;
+       u32 qdr;
+       u32 half_rate;
+};
+
+#define MEM_TYPE_GDDR5  0x50
+#define MEM_TYPE_GDDR4  0x40
+#define MEM_TYPE_GDDR3  0x30
+#define MEM_TYPE_DDR2   0x20
+#define MEM_TYPE_GDDR1  0x10
+#define MEM_TYPE_DDR3   0xb0
+#define MEM_TYPE_MASK   0xf0
+
+struct atom_memory_info {
+       u8 mem_vendor;
+       u8 mem_type;
+};
+
+#define MAX_AC_TIMING_ENTRIES 16
+
+struct atom_memory_clock_range_table
+{
+       u8 num_entries;
+       u8 rsv[3];
+       u32 mclk[MAX_AC_TIMING_ENTRIES];
+};
+
+#define VBIOS_MC_REGISTER_ARRAY_SIZE 32
+#define VBIOS_MAX_AC_TIMING_ENTRIES 20
+
+struct atom_mc_reg_entry {
+       u32 mclk_max;
+       u32 mc_data[VBIOS_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct atom_mc_register_address {
+       u16 s1;
+       u8 pre_reg_data;
+};
+
+struct atom_mc_reg_table {
+       u8 last;
+       u8 num_entries;
+       struct atom_mc_reg_entry mc_reg_table_entry[VBIOS_MAX_AC_TIMING_ENTRIES];
+       struct atom_mc_register_address mc_reg_address[VBIOS_MC_REGISTER_ARRAY_SIZE];
+};
+
+#define MAX_VOLTAGE_ENTRIES 32
+
+struct atom_voltage_table_entry
+{
+       u16 value;
+       u32 smio_low;
+};
+
+struct atom_voltage_table
+{
+       u32 count;
+       u32 mask_low;
+       u32 phase_delay;
+       struct atom_voltage_table_entry entries[MAX_VOLTAGE_ENTRIES];
 };
 
 extern enum radeon_tv_std
 radeon_combios_get_tv_info(struct radeon_device *rdev);
 extern enum radeon_tv_std
 radeon_atombios_get_tv_info(struct radeon_device *rdev);
+extern void radeon_atombios_get_default_voltages(struct radeon_device *rdev,
+                                                u16 *vddc, u16 *vddci, u16 *mvdd);
 
 extern struct drm_connector *
 radeon_get_connector_for_encoder(struct drm_encoder *encoder);
index 1424ccd..0219d26 100644 (file)
@@ -322,8 +322,8 @@ int radeon_bo_init(struct radeon_device *rdev)
 {
        /* Add an MTRR for the VRAM */
        if (!rdev->fastfb_working) {
-               rdev->mc.vram_mtrr = mtrr_add(rdev->mc.aper_base, rdev->mc.aper_size,
-                       MTRR_TYPE_WRCOMB, 1);
+               rdev->mc.vram_mtrr = arch_phys_wc_add(rdev->mc.aper_base,
+                                                     rdev->mc.aper_size);
        }
        DRM_INFO("Detected VRAM RAM=%lluM, BAR=%lluM\n",
                rdev->mc.mc_vram_size >> 20,
@@ -336,6 +336,7 @@ int radeon_bo_init(struct radeon_device *rdev)
 void radeon_bo_fini(struct radeon_device *rdev)
 {
        radeon_ttm_fini(rdev);
+       arch_phys_wc_del(rdev->mc.vram_mtrr);
 }
 
 void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
@@ -348,14 +349,15 @@ void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
        }
 }
 
-int radeon_bo_list_validate(struct list_head *head, int ring)
+int radeon_bo_list_validate(struct ww_acquire_ctx *ticket,
+                           struct list_head *head, int ring)
 {
        struct radeon_bo_list *lobj;
        struct radeon_bo *bo;
        u32 domain;
        int r;
 
-       r = ttm_eu_reserve_buffers(head);
+       r = ttm_eu_reserve_buffers(ticket, head);
        if (unlikely(r != 0)) {
                return r;
        }
@@ -398,7 +400,7 @@ int radeon_bo_get_surface_reg(struct radeon_bo *bo)
        int steal;
        int i;
 
-       BUG_ON(!radeon_bo_is_reserved(bo));
+       lockdep_assert_held(&bo->tbo.resv->lock.base);
 
        if (!bo->tiling_flags)
                return 0;
@@ -524,7 +526,8 @@ void radeon_bo_get_tiling_flags(struct radeon_bo *bo,
                                uint32_t *tiling_flags,
                                uint32_t *pitch)
 {
-       BUG_ON(!radeon_bo_is_reserved(bo));
+       lockdep_assert_held(&bo->tbo.resv->lock.base);
+
        if (tiling_flags)
                *tiling_flags = bo->tiling_flags;
        if (pitch)
@@ -534,7 +537,8 @@ void radeon_bo_get_tiling_flags(struct radeon_bo *bo,
 int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved,
                                bool force_drop)
 {
-       BUG_ON(!radeon_bo_is_reserved(bo) && !force_drop);
+       if (!force_drop)
+               lockdep_assert_held(&bo->tbo.resv->lock.base);
 
        if (!(bo->tiling_flags & RADEON_TILING_SURFACE))
                return 0;
@@ -617,26 +621,3 @@ int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, bool no_wait)
        ttm_bo_unreserve(&bo->tbo);
        return r;
 }
-
-
-/**
- * radeon_bo_reserve - reserve bo
- * @bo:                bo structure
- * @no_intr:   don't return -ERESTARTSYS on pending signal
- *
- * Returns:
- * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
- * a signal. Release all buffer reservations and return to user-space.
- */
-int radeon_bo_reserve(struct radeon_bo *bo, bool no_intr)
-{
-       int r;
-
-       r = ttm_bo_reserve(&bo->tbo, !no_intr, false, false, 0);
-       if (unlikely(r != 0)) {
-               if (r != -ERESTARTSYS)
-                       dev_err(bo->rdev->dev, "%p reserve failed\n", bo);
-               return r;
-       }
-       return 0;
-}
index e2cb80a..91519a5 100644 (file)
@@ -52,7 +52,27 @@ static inline unsigned radeon_mem_type_to_domain(u32 mem_type)
        return 0;
 }
 
-int radeon_bo_reserve(struct radeon_bo *bo, bool no_intr);
+/**
+ * radeon_bo_reserve - reserve bo
+ * @bo:                bo structure
+ * @no_intr:   don't return -ERESTARTSYS on pending signal
+ *
+ * Returns:
+ * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
+ * a signal. Release all buffer reservations and return to user-space.
+ */
+static inline int radeon_bo_reserve(struct radeon_bo *bo, bool no_intr)
+{
+       int r;
+
+       r = ttm_bo_reserve(&bo->tbo, !no_intr, false, false, 0);
+       if (unlikely(r != 0)) {
+               if (r != -ERESTARTSYS)
+                       dev_err(bo->rdev->dev, "%p reserve failed\n", bo);
+               return r;
+       }
+       return 0;
+}
 
 static inline void radeon_bo_unreserve(struct radeon_bo *bo)
 {
@@ -78,11 +98,6 @@ static inline unsigned long radeon_bo_size(struct radeon_bo *bo)
        return bo->tbo.num_pages << PAGE_SHIFT;
 }
 
-static inline bool radeon_bo_is_reserved(struct radeon_bo *bo)
-{
-       return ttm_bo_is_reserved(&bo->tbo);
-}
-
 static inline unsigned radeon_bo_ngpu_pages(struct radeon_bo *bo)
 {
        return (bo->tbo.num_pages << PAGE_SHIFT) / RADEON_GPU_PAGE_SIZE;
@@ -128,7 +143,8 @@ extern int radeon_bo_init(struct radeon_device *rdev);
 extern void radeon_bo_fini(struct radeon_device *rdev);
 extern void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
                                struct list_head *head);
-extern int radeon_bo_list_validate(struct list_head *head, int ring);
+extern int radeon_bo_list_validate(struct ww_acquire_ctx *ticket,
+                                  struct list_head *head, int ring);
 extern int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
                                struct vm_area_struct *vma);
 extern int radeon_bo_set_tiling_flags(struct radeon_bo *bo,
index 788c64c..f374c46 100644 (file)
@@ -388,7 +388,8 @@ static ssize_t radeon_get_pm_method(struct device *dev,
        int pm = rdev->pm.pm_method;
 
        return snprintf(buf, PAGE_SIZE, "%s\n",
-                       (pm == PM_METHOD_DYNPM) ? "dynpm" : "profile");
+                       (pm == PM_METHOD_DYNPM) ? "dynpm" :
+                       (pm == PM_METHOD_PROFILE) ? "profile" : "dpm");
 }
 
 static ssize_t radeon_set_pm_method(struct device *dev,
@@ -399,6 +400,11 @@ static ssize_t radeon_set_pm_method(struct device *dev,
        struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
        struct radeon_device *rdev = ddev->dev_private;
 
+       /* we don't support the legacy modes with dpm */
+       if (rdev->pm.pm_method == PM_METHOD_DPM) {
+               count = -EINVAL;
+               goto fail;
+       }
 
        if (strncmp("dynpm", buf, strlen("dynpm")) == 0) {
                mutex_lock(&rdev->pm.mutex);
@@ -423,8 +429,96 @@ fail:
        return count;
 }
 
+static ssize_t radeon_get_dpm_state(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+       struct radeon_device *rdev = ddev->dev_private;
+       enum radeon_pm_state_type pm = rdev->pm.dpm.user_state;
+
+       return snprintf(buf, PAGE_SIZE, "%s\n",
+                       (pm == POWER_STATE_TYPE_BATTERY) ? "battery" :
+                       (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance");
+}
+
+static ssize_t radeon_set_dpm_state(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf,
+                                   size_t count)
+{
+       struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+       struct radeon_device *rdev = ddev->dev_private;
+
+       mutex_lock(&rdev->pm.mutex);
+       if (strncmp("battery", buf, strlen("battery")) == 0)
+               rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY;
+       else if (strncmp("balanced", buf, strlen("balanced")) == 0)
+               rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
+       else if (strncmp("performance", buf, strlen("performance")) == 0)
+               rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE;
+       else {
+               mutex_unlock(&rdev->pm.mutex);
+               count = -EINVAL;
+               goto fail;
+       }
+       mutex_unlock(&rdev->pm.mutex);
+       radeon_pm_compute_clocks(rdev);
+fail:
+       return count;
+}
+
+static ssize_t radeon_get_dpm_forced_performance_level(struct device *dev,
+                                                      struct device_attribute *attr,
+                                                      char *buf)
+{
+       struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+       struct radeon_device *rdev = ddev->dev_private;
+       enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level;
+
+       return snprintf(buf, PAGE_SIZE, "%s\n",
+                       (level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" :
+                       (level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high");
+}
+
+static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev,
+                                                      struct device_attribute *attr,
+                                                      const char *buf,
+                                                      size_t count)
+{
+       struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
+       struct radeon_device *rdev = ddev->dev_private;
+       enum radeon_dpm_forced_level level;
+       int ret = 0;
+
+       mutex_lock(&rdev->pm.mutex);
+       if (strncmp("low", buf, strlen("low")) == 0) {
+               level = RADEON_DPM_FORCED_LEVEL_LOW;
+       } else if (strncmp("high", buf, strlen("high")) == 0) {
+               level = RADEON_DPM_FORCED_LEVEL_HIGH;
+       } 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) {
+               ret = radeon_dpm_force_performance_level(rdev, level);
+               if (ret)
+                       count = -EINVAL;
+       }
+       mutex_unlock(&rdev->pm.mutex);
+fail:
+       return count;
+}
+
 static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile);
 static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method);
+static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state);
+static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR,
+                  radeon_get_dpm_forced_performance_level,
+                  radeon_set_dpm_forced_performance_level);
 
 static ssize_t radeon_hwmon_show_temp(struct device *dev,
                                      struct device_attribute *attr,
@@ -434,27 +528,10 @@ static ssize_t radeon_hwmon_show_temp(struct device *dev,
        struct radeon_device *rdev = ddev->dev_private;
        int temp;
 
-       switch (rdev->pm.int_thermal_type) {
-       case THERMAL_TYPE_RV6XX:
-               temp = rv6xx_get_temp(rdev);
-               break;
-       case THERMAL_TYPE_RV770:
-               temp = rv770_get_temp(rdev);
-               break;
-       case THERMAL_TYPE_EVERGREEN:
-       case THERMAL_TYPE_NI:
-               temp = evergreen_get_temp(rdev);
-               break;
-       case THERMAL_TYPE_SUMO:
-               temp = sumo_get_temp(rdev);
-               break;
-       case THERMAL_TYPE_SI:
-               temp = si_get_temp(rdev);
-               break;
-       default:
+       if (rdev->asic->pm.get_temperature)
+               temp = radeon_get_temperature(rdev);
+       else
                temp = 0;
-               break;
-       }
 
        return snprintf(buf, PAGE_SIZE, "%d\n", temp);
 }
@@ -492,8 +569,7 @@ static int radeon_hwmon_init(struct radeon_device *rdev)
        case THERMAL_TYPE_NI:
        case THERMAL_TYPE_SUMO:
        case THERMAL_TYPE_SI:
-               /* No support for TN yet */
-               if (rdev->family == CHIP_ARUBA)
+               if (rdev->asic->pm.get_temperature == NULL)
                        return err;
                rdev->pm.int_hwmon_dev = hwmon_device_register(rdev->dev);
                if (IS_ERR(rdev->pm.int_hwmon_dev)) {
@@ -526,7 +602,289 @@ static void radeon_hwmon_fini(struct radeon_device *rdev)
        }
 }
 
-void radeon_pm_suspend(struct radeon_device *rdev)
+static void radeon_dpm_thermal_work_handler(struct work_struct *work)
+{
+       struct radeon_device *rdev =
+               container_of(work, struct radeon_device,
+                            pm.dpm.thermal.work);
+       /* switch to the thermal state */
+       enum radeon_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL;
+
+       if (!rdev->pm.dpm_enabled)
+               return;
+
+       if (rdev->asic->pm.get_temperature) {
+               int temp = radeon_get_temperature(rdev);
+
+               if (temp < rdev->pm.dpm.thermal.min_temp)
+                       /* switch back the user state */
+                       dpm_state = rdev->pm.dpm.user_state;
+       } else {
+               if (rdev->pm.dpm.thermal.high_to_low)
+                       /* switch back the user state */
+                       dpm_state = rdev->pm.dpm.user_state;
+       }
+       radeon_dpm_enable_power_state(rdev, dpm_state);
+}
+
+static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
+                                                    enum radeon_pm_state_type dpm_state)
+{
+       int i;
+       struct radeon_ps *ps;
+       u32 ui_class;
+       bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ?
+               true : false;
+
+       /* check if the vblank period is too short to adjust the mclk */
+       if (single_display && rdev->asic->dpm.vblank_too_short) {
+               if (radeon_dpm_vblank_too_short(rdev))
+                       single_display = false;
+       }
+
+       /* certain older asics have a separare 3D performance state,
+        * so try that first if the user selected performance
+        */
+       if (dpm_state == POWER_STATE_TYPE_PERFORMANCE)
+               dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF;
+       /* balanced states don't exist at the moment */
+       if (dpm_state == POWER_STATE_TYPE_BALANCED)
+               dpm_state = POWER_STATE_TYPE_PERFORMANCE;
+
+restart_search:
+       /* Pick the best power state based on current conditions */
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               ps = &rdev->pm.dpm.ps[i];
+               ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK;
+               switch (dpm_state) {
+               /* user states */
+               case POWER_STATE_TYPE_BATTERY:
+                       if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) {
+                               if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
+                                       if (single_display)
+                                               return ps;
+                               } else
+                                       return ps;
+                       }
+                       break;
+               case POWER_STATE_TYPE_BALANCED:
+                       if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) {
+                               if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
+                                       if (single_display)
+                                               return ps;
+                               } else
+                                       return ps;
+                       }
+                       break;
+               case POWER_STATE_TYPE_PERFORMANCE:
+                       if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
+                               if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
+                                       if (single_display)
+                                               return ps;
+                               } else
+                                       return ps;
+                       }
+                       break;
+               /* internal states */
+               case POWER_STATE_TYPE_INTERNAL_UVD:
+                       return rdev->pm.dpm.uvd_ps;
+               case POWER_STATE_TYPE_INTERNAL_UVD_SD:
+                       if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)
+                               return ps;
+                       break;
+               case POWER_STATE_TYPE_INTERNAL_UVD_HD:
+                       if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)
+                               return ps;
+                       break;
+               case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
+                       if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)
+                               return ps;
+                       break;
+               case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
+                       if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)
+                               return ps;
+                       break;
+               case POWER_STATE_TYPE_INTERNAL_BOOT:
+                       return rdev->pm.dpm.boot_ps;
+               case POWER_STATE_TYPE_INTERNAL_THERMAL:
+                       if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
+                               return ps;
+                       break;
+               case POWER_STATE_TYPE_INTERNAL_ACPI:
+                       if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI)
+                               return ps;
+                       break;
+               case POWER_STATE_TYPE_INTERNAL_ULV:
+                       if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV)
+                               return ps;
+                       break;
+               case POWER_STATE_TYPE_INTERNAL_3DPERF:
+                       if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)
+                               return ps;
+                       break;
+               default:
+                       break;
+               }
+       }
+       /* use a fallback state if we didn't match */
+       switch (dpm_state) {
+       case POWER_STATE_TYPE_INTERNAL_UVD_SD:
+       case POWER_STATE_TYPE_INTERNAL_UVD_HD:
+       case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
+       case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
+               return rdev->pm.dpm.uvd_ps;
+       case POWER_STATE_TYPE_INTERNAL_THERMAL:
+               dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI;
+               goto restart_search;
+       case POWER_STATE_TYPE_INTERNAL_ACPI:
+               dpm_state = POWER_STATE_TYPE_BATTERY;
+               goto restart_search;
+       case POWER_STATE_TYPE_BATTERY:
+       case POWER_STATE_TYPE_BALANCED:
+       case POWER_STATE_TYPE_INTERNAL_3DPERF:
+               dpm_state = POWER_STATE_TYPE_PERFORMANCE;
+               goto restart_search;
+       default:
+               break;
+       }
+
+       return NULL;
+}
+
+static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
+{
+       int i;
+       struct radeon_ps *ps;
+       enum radeon_pm_state_type dpm_state;
+       int ret;
+
+       /* if dpm init failed */
+       if (!rdev->pm.dpm_enabled)
+               return;
+
+       if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) {
+               /* add other state override checks here */
+               if ((!rdev->pm.dpm.thermal_active) &&
+                   (!rdev->pm.dpm.uvd_active))
+                       rdev->pm.dpm.state = rdev->pm.dpm.user_state;
+       }
+       dpm_state = rdev->pm.dpm.state;
+
+       ps = radeon_dpm_pick_power_state(rdev, dpm_state);
+       if (ps)
+               rdev->pm.dpm.requested_ps = ps;
+       else
+               return;
+
+       /* no need to reprogram if nothing changed unless we are on BTC+ */
+       if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) {
+               if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) {
+                       /* for pre-BTC and APUs if the num crtcs changed but state is the same,
+                        * all we need to do is update the display configuration.
+                        */
+                       if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) {
+                               /* update display watermarks based on new power state */
+                               radeon_bandwidth_update(rdev);
+                               /* update displays */
+                               radeon_dpm_display_configuration_changed(rdev);
+                               rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
+                               rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
+                       }
+                       return;
+               } else {
+                       /* for BTC+ if the num crtcs hasn't changed and state is the same,
+                        * nothing to do, if the num crtcs is > 1 and state is the same,
+                        * update display configuration.
+                        */
+                       if (rdev->pm.dpm.new_active_crtcs ==
+                           rdev->pm.dpm.current_active_crtcs) {
+                               return;
+                       } else {
+                               if ((rdev->pm.dpm.current_active_crtc_count > 1) &&
+                                   (rdev->pm.dpm.new_active_crtc_count > 1)) {
+                                       /* update display watermarks based on new power state */
+                                       radeon_bandwidth_update(rdev);
+                                       /* update displays */
+                                       radeon_dpm_display_configuration_changed(rdev);
+                                       rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
+                                       rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
+                                       return;
+                               }
+                       }
+               }
+       }
+
+       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);
+
+       ret = radeon_dpm_pre_set_power_state(rdev);
+       if (ret)
+               goto done;
+
+       /* update display watermarks based on new power state */
+       radeon_bandwidth_update(rdev);
+       /* update displays */
+       radeon_dpm_display_configuration_changed(rdev);
+
+       rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
+       rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
+
+       /* wait for the rings to drain */
+       for (i = 0; i < RADEON_NUM_RINGS; i++) {
+               struct radeon_ring *ring = &rdev->ring[i];
+               if (ring->ready)
+                       radeon_fence_wait_empty_locked(rdev, i);
+       }
+
+       /* program the new power state */
+       radeon_dpm_set_power_state(rdev);
+
+       /* update current power state */
+       rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps;
+
+       radeon_dpm_post_set_power_state(rdev);
+
+done:
+       mutex_unlock(&rdev->ring_lock);
+       up_write(&rdev->pm.mclk_lock);
+       mutex_unlock(&rdev->ddev->struct_mutex);
+}
+
+void radeon_dpm_enable_power_state(struct radeon_device *rdev,
+                                  enum radeon_pm_state_type dpm_state)
+{
+       if (!rdev->pm.dpm_enabled)
+               return;
+
+       mutex_lock(&rdev->pm.mutex);
+       switch (dpm_state) {
+       case POWER_STATE_TYPE_INTERNAL_THERMAL:
+               rdev->pm.dpm.thermal_active = true;
+               break;
+       case POWER_STATE_TYPE_INTERNAL_UVD:
+       case POWER_STATE_TYPE_INTERNAL_UVD_SD:
+       case POWER_STATE_TYPE_INTERNAL_UVD_HD:
+       case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
+       case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
+               rdev->pm.dpm.uvd_active = true;
+               break;
+       default:
+               rdev->pm.dpm.thermal_active = false;
+               rdev->pm.dpm.uvd_active = false;
+               break;
+       }
+       rdev->pm.dpm.state = dpm_state;
+       mutex_unlock(&rdev->pm.mutex);
+       radeon_pm_compute_clocks(rdev);
+}
+
+static void radeon_pm_suspend_old(struct radeon_device *rdev)
 {
        mutex_lock(&rdev->pm.mutex);
        if (rdev->pm.pm_method == PM_METHOD_DYNPM) {
@@ -538,11 +896,30 @@ void radeon_pm_suspend(struct radeon_device *rdev)
        cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work);
 }
 
-void radeon_pm_resume(struct radeon_device *rdev)
+static void radeon_pm_suspend_dpm(struct radeon_device *rdev)
+{
+       mutex_lock(&rdev->pm.mutex);
+       /* disable dpm */
+       radeon_dpm_disable(rdev);
+       /* reset the power state */
+       rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
+       rdev->pm.dpm_enabled = false;
+       mutex_unlock(&rdev->pm.mutex);
+}
+
+void radeon_pm_suspend(struct radeon_device *rdev)
+{
+       if (rdev->pm.pm_method == PM_METHOD_DPM)
+               radeon_pm_suspend_dpm(rdev);
+       else
+               radeon_pm_suspend_old(rdev);
+}
+
+static void radeon_pm_resume_old(struct radeon_device *rdev)
 {
        /* set up the default clocks if the MC ucode is loaded */
        if ((rdev->family >= CHIP_BARTS) &&
-           (rdev->family <= CHIP_CAYMAN) &&
+           (rdev->family <= CHIP_HAINAN) &&
            rdev->mc_fw) {
                if (rdev->pm.default_vddc)
                        radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
@@ -573,12 +950,50 @@ void radeon_pm_resume(struct radeon_device *rdev)
        radeon_pm_compute_clocks(rdev);
 }
 
-int radeon_pm_init(struct radeon_device *rdev)
+static void radeon_pm_resume_dpm(struct radeon_device *rdev)
+{
+       int ret;
+
+       /* asic init will reset to the boot state */
+       mutex_lock(&rdev->pm.mutex);
+       rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
+       radeon_dpm_setup_asic(rdev);
+       ret = radeon_dpm_enable(rdev);
+       mutex_unlock(&rdev->pm.mutex);
+       if (ret) {
+               DRM_ERROR("radeon: dpm resume failed\n");
+               if ((rdev->family >= CHIP_BARTS) &&
+                   (rdev->family <= CHIP_HAINAN) &&
+                   rdev->mc_fw) {
+                       if (rdev->pm.default_vddc)
+                               radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
+                                                       SET_VOLTAGE_TYPE_ASIC_VDDC);
+                       if (rdev->pm.default_vddci)
+                               radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
+                                                       SET_VOLTAGE_TYPE_ASIC_VDDCI);
+                       if (rdev->pm.default_sclk)
+                               radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
+                       if (rdev->pm.default_mclk)
+                               radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
+               }
+       } else {
+               rdev->pm.dpm_enabled = true;
+               radeon_pm_compute_clocks(rdev);
+       }
+}
+
+void radeon_pm_resume(struct radeon_device *rdev)
+{
+       if (rdev->pm.pm_method == PM_METHOD_DPM)
+               radeon_pm_resume_dpm(rdev);
+       else
+               radeon_pm_resume_old(rdev);
+}
+
+static int radeon_pm_init_old(struct radeon_device *rdev)
 {
        int ret;
 
-       /* default to profile method */
-       rdev->pm.pm_method = PM_METHOD_PROFILE;
        rdev->pm.profile = PM_PROFILE_DEFAULT;
        rdev->pm.dynpm_state = DYNPM_STATE_DISABLED;
        rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
@@ -599,7 +1014,7 @@ int radeon_pm_init(struct radeon_device *rdev)
                radeon_pm_init_profile(rdev);
                /* set up the default clocks if the MC ucode is loaded */
                if ((rdev->family >= CHIP_BARTS) &&
-                   (rdev->family <= CHIP_CAYMAN) &&
+                   (rdev->family <= CHIP_HAINAN) &&
                    rdev->mc_fw) {
                        if (rdev->pm.default_vddc)
                                radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
@@ -640,7 +1055,145 @@ int radeon_pm_init(struct radeon_device *rdev)
        return 0;
 }
 
-void radeon_pm_fini(struct radeon_device *rdev)
+static void radeon_dpm_print_power_states(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               printk("== power state %d ==\n", i);
+               radeon_dpm_print_power_state(rdev, &rdev->pm.dpm.ps[i]);
+       }
+}
+
+static int radeon_pm_init_dpm(struct radeon_device *rdev)
+{
+       int ret;
+
+       /* default to performance state */
+       rdev->pm.dpm.state = POWER_STATE_TYPE_BALANCED;
+       rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
+       rdev->pm.default_sclk = rdev->clock.default_sclk;
+       rdev->pm.default_mclk = rdev->clock.default_mclk;
+       rdev->pm.current_sclk = rdev->clock.default_sclk;
+       rdev->pm.current_mclk = rdev->clock.default_mclk;
+       rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
+
+       if (rdev->bios && rdev->is_atom_bios)
+               radeon_atombios_get_power_modes(rdev);
+       else
+               return -EINVAL;
+
+       /* set up the internal thermal sensor if applicable */
+       ret = radeon_hwmon_init(rdev);
+       if (ret)
+               return ret;
+
+       INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler);
+       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);
+       radeon_dpm_setup_asic(rdev);
+       ret = radeon_dpm_enable(rdev);
+       mutex_unlock(&rdev->pm.mutex);
+       if (ret) {
+               rdev->pm.dpm_enabled = false;
+               if ((rdev->family >= CHIP_BARTS) &&
+                   (rdev->family <= CHIP_HAINAN) &&
+                   rdev->mc_fw) {
+                       if (rdev->pm.default_vddc)
+                               radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
+                                                       SET_VOLTAGE_TYPE_ASIC_VDDC);
+                       if (rdev->pm.default_vddci)
+                               radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
+                                                       SET_VOLTAGE_TYPE_ASIC_VDDCI);
+                       if (rdev->pm.default_sclk)
+                               radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
+                       if (rdev->pm.default_mclk)
+                               radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
+               }
+               DRM_ERROR("radeon: dpm initialization failed\n");
+               return ret;
+       }
+       rdev->pm.dpm_enabled = true;
+       radeon_pm_compute_clocks(rdev);
+
+       if (rdev->pm.num_power_states > 1) {
+               ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state);
+               if (ret)
+                       DRM_ERROR("failed to create device file for dpm state\n");
+               ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
+               if (ret)
+                       DRM_ERROR("failed to create device file for dpm state\n");
+               /* XXX: these are noops for dpm but are here for backwards compat */
+               ret = device_create_file(rdev->dev, &dev_attr_power_profile);
+               if (ret)
+                       DRM_ERROR("failed to create device file for power profile\n");
+               ret = device_create_file(rdev->dev, &dev_attr_power_method);
+               if (ret)
+                       DRM_ERROR("failed to create device file for power method\n");
+
+               if (radeon_debugfs_pm_init(rdev)) {
+                       DRM_ERROR("Failed to register debugfs file for dpm!\n");
+               }
+
+               DRM_INFO("radeon: dpm initialized\n");
+       }
+
+       return 0;
+}
+
+int radeon_pm_init(struct radeon_device *rdev)
+{
+       /* enable dpm on rv6xx+ */
+       switch (rdev->family) {
+       case CHIP_RV610:
+       case CHIP_RV630:
+       case CHIP_RV620:
+       case CHIP_RV635:
+       case CHIP_RV670:
+       case CHIP_RS780:
+       case CHIP_RS880:
+       case CHIP_RV770:
+       case CHIP_RV730:
+       case CHIP_RV710:
+       case CHIP_RV740:
+       case CHIP_CEDAR:
+       case CHIP_REDWOOD:
+       case CHIP_JUNIPER:
+       case CHIP_CYPRESS:
+       case CHIP_HEMLOCK:
+       case CHIP_PALM:
+       case CHIP_SUMO:
+       case CHIP_SUMO2:
+       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:
+               if (radeon_dpm == 1)
+                       rdev->pm.pm_method = PM_METHOD_DPM;
+               else
+                       rdev->pm.pm_method = PM_METHOD_PROFILE;
+               break;
+       default:
+               /* default to profile method */
+               rdev->pm.pm_method = PM_METHOD_PROFILE;
+               break;
+       }
+
+       if (rdev->pm.pm_method == PM_METHOD_DPM)
+               return radeon_pm_init_dpm(rdev);
+       else
+               return radeon_pm_init_old(rdev);
+}
+
+static void radeon_pm_fini_old(struct radeon_device *rdev)
 {
        if (rdev->pm.num_power_states > 1) {
                mutex_lock(&rdev->pm.mutex);
@@ -668,7 +1221,36 @@ void radeon_pm_fini(struct radeon_device *rdev)
        radeon_hwmon_fini(rdev);
 }
 
-void radeon_pm_compute_clocks(struct radeon_device *rdev)
+static void radeon_pm_fini_dpm(struct radeon_device *rdev)
+{
+       if (rdev->pm.num_power_states > 1) {
+               mutex_lock(&rdev->pm.mutex);
+               radeon_dpm_disable(rdev);
+               mutex_unlock(&rdev->pm.mutex);
+
+               device_remove_file(rdev->dev, &dev_attr_power_dpm_state);
+               device_remove_file(rdev->dev, &dev_attr_power_dpm_force_performance_level);
+               /* XXX backwards compat */
+               device_remove_file(rdev->dev, &dev_attr_power_profile);
+               device_remove_file(rdev->dev, &dev_attr_power_method);
+       }
+       radeon_dpm_fini(rdev);
+
+       if (rdev->pm.power_state)
+               kfree(rdev->pm.power_state);
+
+       radeon_hwmon_fini(rdev);
+}
+
+void radeon_pm_fini(struct radeon_device *rdev)
+{
+       if (rdev->pm.pm_method == PM_METHOD_DPM)
+               radeon_pm_fini_dpm(rdev);
+       else
+               radeon_pm_fini_old(rdev);
+}
+
+static void radeon_pm_compute_clocks_old(struct radeon_device *rdev)
 {
        struct drm_device *ddev = rdev->ddev;
        struct drm_crtc *crtc;
@@ -739,6 +1321,46 @@ void radeon_pm_compute_clocks(struct radeon_device *rdev)
        mutex_unlock(&rdev->pm.mutex);
 }
 
+static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev)
+{
+       struct drm_device *ddev = rdev->ddev;
+       struct drm_crtc *crtc;
+       struct radeon_crtc *radeon_crtc;
+
+       mutex_lock(&rdev->pm.mutex);
+
+       /* update active crtc counts */
+       rdev->pm.dpm.new_active_crtcs = 0;
+       rdev->pm.dpm.new_active_crtc_count = 0;
+       list_for_each_entry(crtc,
+               &ddev->mode_config.crtc_list, head) {
+               radeon_crtc = to_radeon_crtc(crtc);
+               if (crtc->enabled) {
+                       rdev->pm.dpm.new_active_crtcs |= (1 << radeon_crtc->crtc_id);
+                       rdev->pm.dpm.new_active_crtc_count++;
+               }
+       }
+
+       /* update battery/ac status */
+       if (power_supply_is_system_supplied() > 0)
+               rdev->pm.dpm.ac_power = true;
+       else
+               rdev->pm.dpm.ac_power = false;
+
+       radeon_dpm_change_power_state_locked(rdev);
+
+       mutex_unlock(&rdev->pm.mutex);
+
+}
+
+void radeon_pm_compute_clocks(struct radeon_device *rdev)
+{
+       if (rdev->pm.pm_method == PM_METHOD_DPM)
+               radeon_pm_compute_clocks_dpm(rdev);
+       else
+               radeon_pm_compute_clocks_old(rdev);
+}
+
 static bool radeon_pm_in_vbl(struct radeon_device *rdev)
 {
        int  crtc, vpos, hpos, vbl_status;
@@ -842,19 +1464,28 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data)
        struct drm_device *dev = node->minor->dev;
        struct radeon_device *rdev = dev->dev_private;
 
-       seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk);
-       /* radeon_get_engine_clock is not reliable on APUs so just print the current clock */
-       if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP))
-               seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk);
-       else
-               seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
-       seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk);
-       if (rdev->asic->pm.get_memory_clock)
-               seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));
-       if (rdev->pm.current_vddc)
-               seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc);
-       if (rdev->asic->pm.get_pcie_lanes)
-               seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev));
+       if (rdev->pm.dpm_enabled) {
+               mutex_lock(&rdev->pm.mutex);
+               if (rdev->asic->dpm.debugfs_print_current_performance_level)
+                       radeon_dpm_debugfs_print_current_performance_level(rdev, m);
+               else
+                       seq_printf(m, "Debugfs support not implemented for this asic\n");
+               mutex_unlock(&rdev->pm.mutex);
+       } else {
+               seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk);
+               /* radeon_get_engine_clock is not reliable on APUs so just print the current clock */
+               if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP))
+                       seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk);
+               else
+                       seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
+               seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk);
+               if (rdev->asic->pm.get_memory_clock)
+                       seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));
+               if (rdev->pm.current_vddc)
+                       seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc);
+               if (rdev->asic->pm.get_pcie_lanes)
+                       seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev));
+       }
 
        return 0;
 }
index 4940af7..65b9eab 100644 (file)
@@ -88,11 +88,19 @@ int radeon_gem_prime_pin(struct drm_gem_object *obj)
 
        /* pin buffer into GTT */
        ret = radeon_bo_pin(bo, RADEON_GEM_DOMAIN_GTT, NULL);
-       if (ret) {
-               radeon_bo_unreserve(bo);
-               return ret;
-       }
        radeon_bo_unreserve(bo);
+       return ret;
+}
+
+void radeon_gem_prime_unpin(struct drm_gem_object *obj)
+{
+       struct radeon_bo *bo = gem_to_radeon_bo(obj);
+       int ret = 0;
 
-       return 0;
+       ret = radeon_bo_reserve(bo, false);
+       if (unlikely(ret != 0))
+               return;
+
+       radeon_bo_unpin(bo);
+       radeon_bo_unreserve(bo);
 }
index 7e2c2b7..62d5497 100644 (file)
@@ -57,6 +57,7 @@
 #include "evergreen_reg.h"
 #include "ni_reg.h"
 #include "si_reg.h"
+#include "cik_reg.h"
 
 #define RADEON_MC_AGP_LOCATION         0x014c
 #define                RADEON_MC_AGP_START_MASK        0x0000FFFF
index 8243401..5f1c51a 100644 (file)
@@ -357,6 +357,38 @@ bool radeon_ring_supports_scratch_reg(struct radeon_device *rdev,
        }
 }
 
+u32 radeon_ring_generic_get_rptr(struct radeon_device *rdev,
+                                struct radeon_ring *ring)
+{
+       u32 rptr;
+
+       if (rdev->wb.enabled && ring != &rdev->ring[R600_RING_TYPE_UVD_INDEX])
+               rptr = le32_to_cpu(rdev->wb.wb[ring->rptr_offs/4]);
+       else
+               rptr = RREG32(ring->rptr_reg);
+       rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
+
+       return rptr;
+}
+
+u32 radeon_ring_generic_get_wptr(struct radeon_device *rdev,
+                                struct radeon_ring *ring)
+{
+       u32 wptr;
+
+       wptr = RREG32(ring->wptr_reg);
+       wptr = (wptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
+
+       return wptr;
+}
+
+void radeon_ring_generic_set_wptr(struct radeon_device *rdev,
+                                 struct radeon_ring *ring)
+{
+       WREG32(ring->wptr_reg, (ring->wptr << ring->ptr_reg_shift) & ring->ptr_reg_mask);
+       (void)RREG32(ring->wptr_reg);
+}
+
 /**
  * radeon_ring_free_size - update the free size
  *
@@ -367,13 +399,7 @@ bool radeon_ring_supports_scratch_reg(struct radeon_device *rdev,
  */
 void radeon_ring_free_size(struct radeon_device *rdev, struct radeon_ring *ring)
 {
-       u32 rptr;
-
-       if (rdev->wb.enabled && ring != &rdev->ring[R600_RING_TYPE_UVD_INDEX])
-               rptr = le32_to_cpu(rdev->wb.wb[ring->rptr_offs/4]);
-       else
-               rptr = RREG32(ring->rptr_reg);
-       ring->rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
+       ring->rptr = radeon_ring_get_rptr(rdev, ring);
        /* This works because ring_size is a power of 2 */
        ring->ring_free_dw = (ring->rptr + (ring->ring_size / 4));
        ring->ring_free_dw -= ring->wptr;
@@ -465,8 +491,7 @@ void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring)
                radeon_ring_write(ring, ring->nop);
        }
        DRM_MEMORYBARRIER();
-       WREG32(ring->wptr_reg, (ring->wptr << ring->ptr_reg_shift) & ring->ptr_reg_mask);
-       (void)RREG32(ring->wptr_reg);
+       radeon_ring_set_wptr(rdev, ring);
 }
 
 /**
@@ -568,7 +593,6 @@ void radeon_ring_lockup_update(struct radeon_ring *ring)
 bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
 {
        unsigned long cjiffies, elapsed;
-       uint32_t rptr;
 
        cjiffies = jiffies;
        if (!time_after(cjiffies, ring->last_activity)) {
@@ -576,8 +600,7 @@ bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *rin
                radeon_ring_lockup_update(ring);
                return false;
        }
-       rptr = RREG32(ring->rptr_reg);
-       ring->rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
+       ring->rptr = radeon_ring_get_rptr(rdev, ring);
        if (ring->rptr != ring->last_rptr) {
                /* CP is still working no lockup */
                radeon_ring_lockup_update(ring);
@@ -804,9 +827,9 @@ static int radeon_debugfs_ring_info(struct seq_file *m, void *data)
 
        radeon_ring_free_size(rdev, ring);
        count = (ring->ring_size / 4) - ring->ring_free_dw;
-       tmp = RREG32(ring->wptr_reg) >> ring->ptr_reg_shift;
+       tmp = radeon_ring_get_wptr(rdev, ring);
        seq_printf(m, "wptr(0x%04x): 0x%08x [%5d]\n", ring->wptr_reg, tmp, tmp);
-       tmp = RREG32(ring->rptr_reg) >> ring->ptr_reg_shift;
+       tmp = radeon_ring_get_rptr(rdev, ring);
        seq_printf(m, "rptr(0x%04x): 0x%08x [%5d]\n", ring->rptr_reg, tmp, tmp);
        if (ring->rptr_save_reg) {
                seq_printf(m, "rptr next(0x%04x): 0x%08x\n", ring->rptr_save_reg,
index bbed4af..f4d6bce 100644 (file)
@@ -35,7 +35,6 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
 {
        struct radeon_bo *vram_obj = NULL;
        struct radeon_bo **gtt_obj = NULL;
-       struct radeon_fence *fence = NULL;
        uint64_t gtt_addr, vram_addr;
        unsigned i, n, size;
        int r, ring;
@@ -81,37 +80,38 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
        }
        r = radeon_bo_reserve(vram_obj, false);
        if (unlikely(r != 0))
-               goto out_cleanup;
+               goto out_unref;
        r = radeon_bo_pin(vram_obj, RADEON_GEM_DOMAIN_VRAM, &vram_addr);
        if (r) {
                DRM_ERROR("Failed to pin VRAM object\n");
-               goto out_cleanup;
+               goto out_unres;
        }
        for (i = 0; i < n; i++) {
                void *gtt_map, *vram_map;
                void **gtt_start, **gtt_end;
                void **vram_start, **vram_end;
+               struct radeon_fence *fence = NULL;
 
                r = radeon_bo_create(rdev, size, PAGE_SIZE, true,
                                     RADEON_GEM_DOMAIN_GTT, NULL, gtt_obj + i);
                if (r) {
                        DRM_ERROR("Failed to create GTT object %d\n", i);
-                       goto out_cleanup;
+                       goto out_lclean;
                }
 
                r = radeon_bo_reserve(gtt_obj[i], false);
                if (unlikely(r != 0))
-                       goto out_cleanup;
+                       goto out_lclean_unref;
                r = radeon_bo_pin(gtt_obj[i], RADEON_GEM_DOMAIN_GTT, &gtt_addr);
                if (r) {
                        DRM_ERROR("Failed to pin GTT object %d\n", i);
-                       goto out_cleanup;
+                       goto out_lclean_unres;
                }
 
                r = radeon_bo_kmap(gtt_obj[i], &gtt_map);
                if (r) {
                        DRM_ERROR("Failed to map GTT object %d\n", i);
-                       goto out_cleanup;
+                       goto out_lclean_unpin;
                }
 
                for (gtt_start = gtt_map, gtt_end = gtt_map + size;
@@ -127,13 +127,13 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
                        r = radeon_copy_blit(rdev, gtt_addr, vram_addr, size / RADEON_GPU_PAGE_SIZE, &fence);
                if (r) {
                        DRM_ERROR("Failed GTT->VRAM copy %d\n", i);
-                       goto out_cleanup;
+                       goto out_lclean_unpin;
                }
 
                r = radeon_fence_wait(fence, false);
                if (r) {
                        DRM_ERROR("Failed to wait for GTT->VRAM fence %d\n", i);
-                       goto out_cleanup;
+                       goto out_lclean_unpin;
                }
 
                radeon_fence_unref(&fence);
@@ -141,7 +141,7 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
                r = radeon_bo_kmap(vram_obj, &vram_map);
                if (r) {
                        DRM_ERROR("Failed to map VRAM object after copy %d\n", i);
-                       goto out_cleanup;
+                       goto out_lclean_unpin;
                }
 
                for (gtt_start = gtt_map, gtt_end = gtt_map + size,
@@ -160,7 +160,7 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
                                          (vram_addr - rdev->mc.vram_start +
                                           (void*)gtt_start - gtt_map));
                                radeon_bo_kunmap(vram_obj);
-                               goto out_cleanup;
+                               goto out_lclean_unpin;
                        }
                        *vram_start = vram_start;
                }
@@ -173,13 +173,13 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
                        r = radeon_copy_blit(rdev, vram_addr, gtt_addr, size / RADEON_GPU_PAGE_SIZE, &fence);
                if (r) {
                        DRM_ERROR("Failed VRAM->GTT copy %d\n", i);
-                       goto out_cleanup;
+                       goto out_lclean_unpin;
                }
 
                r = radeon_fence_wait(fence, false);
                if (r) {
                        DRM_ERROR("Failed to wait for VRAM->GTT fence %d\n", i);
-                       goto out_cleanup;
+                       goto out_lclean_unpin;
                }
 
                radeon_fence_unref(&fence);
@@ -187,7 +187,7 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
                r = radeon_bo_kmap(gtt_obj[i], &gtt_map);
                if (r) {
                        DRM_ERROR("Failed to map GTT object after copy %d\n", i);
-                       goto out_cleanup;
+                       goto out_lclean_unpin;
                }
 
                for (gtt_start = gtt_map, gtt_end = gtt_map + size,
@@ -206,7 +206,7 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
                                          (gtt_addr - rdev->mc.gtt_start +
                                           (void*)vram_start - vram_map));
                                radeon_bo_kunmap(gtt_obj[i]);
-                               goto out_cleanup;
+                               goto out_lclean_unpin;
                        }
                }
 
@@ -214,31 +214,32 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
 
                DRM_INFO("Tested GTT->VRAM and VRAM->GTT copy for GTT offset 0x%llx\n",
                         gtt_addr - rdev->mc.gtt_start);
+               continue;
+
+out_lclean_unpin:
+               radeon_bo_unpin(gtt_obj[i]);
+out_lclean_unres:
+               radeon_bo_unreserve(gtt_obj[i]);
+out_lclean_unref:
+               radeon_bo_unref(&gtt_obj[i]);
+out_lclean:
+               for (--i; i >= 0; --i) {
+                       radeon_bo_unpin(gtt_obj[i]);
+                       radeon_bo_unreserve(gtt_obj[i]);
+                       radeon_bo_unref(&gtt_obj[i]);
+               }
+               if (fence)
+                       radeon_fence_unref(&fence);
+               break;
        }
 
+       radeon_bo_unpin(vram_obj);
+out_unres:
+       radeon_bo_unreserve(vram_obj);
+out_unref:
+       radeon_bo_unref(&vram_obj);
 out_cleanup:
-       if (vram_obj) {
-               if (radeon_bo_is_reserved(vram_obj)) {
-                       radeon_bo_unpin(vram_obj);
-                       radeon_bo_unreserve(vram_obj);
-               }
-               radeon_bo_unref(&vram_obj);
-       }
-       if (gtt_obj) {
-               for (i = 0; i < n; i++) {
-                       if (gtt_obj[i]) {
-                               if (radeon_bo_is_reserved(gtt_obj[i])) {
-                                       radeon_bo_unpin(gtt_obj[i]);
-                                       radeon_bo_unreserve(gtt_obj[i]);
-                               }
-                               radeon_bo_unref(&gtt_obj[i]);
-                       }
-               }
-               kfree(gtt_obj);
-       }
-       if (fence) {
-               radeon_fence_unref(&fence);
-       }
+       kfree(gtt_obj);
        if (r) {
                printk(KERN_WARNING "Error while testing BO move.\n");
        }
diff --git a/drivers/gpu/drm/radeon/radeon_ucode.h b/drivers/gpu/drm/radeon/radeon_ucode.h
new file mode 100644 (file)
index 0000000..d8b05f7
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RADEON_UCODE_H__
+#define __RADEON_UCODE_H__
+
+/* CP */
+#define R600_PFP_UCODE_SIZE          576
+#define R600_PM4_UCODE_SIZE          1792
+#define R700_PFP_UCODE_SIZE          848
+#define R700_PM4_UCODE_SIZE          1360
+#define EVERGREEN_PFP_UCODE_SIZE     1120
+#define EVERGREEN_PM4_UCODE_SIZE     1376
+#define CAYMAN_PFP_UCODE_SIZE        2176
+#define CAYMAN_PM4_UCODE_SIZE        2176
+#define SI_PFP_UCODE_SIZE            2144
+#define SI_PM4_UCODE_SIZE            2144
+#define SI_CE_UCODE_SIZE             2144
+
+/* RLC */
+#define R600_RLC_UCODE_SIZE          768
+#define R700_RLC_UCODE_SIZE          1024
+#define EVERGREEN_RLC_UCODE_SIZE     768
+#define CAYMAN_RLC_UCODE_SIZE        1024
+#define ARUBA_RLC_UCODE_SIZE         1536
+#define SI_RLC_UCODE_SIZE            2048
+
+/* MC */
+#define BTC_MC_UCODE_SIZE            6024
+#define CAYMAN_MC_UCODE_SIZE         6037
+#define SI_MC_UCODE_SIZE             7769
+#define OLAND_MC_UCODE_SIZE          7863
+
+/* SMC */
+#define RV770_SMC_UCODE_START        0x0100
+#define RV770_SMC_UCODE_SIZE         0x410d
+#define RV770_SMC_INT_VECTOR_START   0xffc0
+#define RV770_SMC_INT_VECTOR_SIZE    0x0040
+
+#define RV730_SMC_UCODE_START        0x0100
+#define RV730_SMC_UCODE_SIZE         0x412c
+#define RV730_SMC_INT_VECTOR_START   0xffc0
+#define RV730_SMC_INT_VECTOR_SIZE    0x0040
+
+#define RV710_SMC_UCODE_START        0x0100
+#define RV710_SMC_UCODE_SIZE         0x3f1f
+#define RV710_SMC_INT_VECTOR_START   0xffc0
+#define RV710_SMC_INT_VECTOR_SIZE    0x0040
+
+#define RV740_SMC_UCODE_START        0x0100
+#define RV740_SMC_UCODE_SIZE         0x41c5
+#define RV740_SMC_INT_VECTOR_START   0xffc0
+#define RV740_SMC_INT_VECTOR_SIZE    0x0040
+
+#define CEDAR_SMC_UCODE_START        0x0100
+#define CEDAR_SMC_UCODE_SIZE         0x5d50
+#define CEDAR_SMC_INT_VECTOR_START   0xffc0
+#define CEDAR_SMC_INT_VECTOR_SIZE    0x0040
+
+#define REDWOOD_SMC_UCODE_START      0x0100
+#define REDWOOD_SMC_UCODE_SIZE       0x5f0a
+#define REDWOOD_SMC_INT_VECTOR_START 0xffc0
+#define REDWOOD_SMC_INT_VECTOR_SIZE  0x0040
+
+#define JUNIPER_SMC_UCODE_START      0x0100
+#define JUNIPER_SMC_UCODE_SIZE       0x5f1f
+#define JUNIPER_SMC_INT_VECTOR_START 0xffc0
+#define JUNIPER_SMC_INT_VECTOR_SIZE  0x0040
+
+#define CYPRESS_SMC_UCODE_START      0x0100
+#define CYPRESS_SMC_UCODE_SIZE       0x61f7
+#define CYPRESS_SMC_INT_VECTOR_START 0xffc0
+#define CYPRESS_SMC_INT_VECTOR_SIZE  0x0040
+
+#define BARTS_SMC_UCODE_START        0x0100
+#define BARTS_SMC_UCODE_SIZE         0x6107
+#define BARTS_SMC_INT_VECTOR_START   0xffc0
+#define BARTS_SMC_INT_VECTOR_SIZE    0x0040
+
+#define TURKS_SMC_UCODE_START        0x0100
+#define TURKS_SMC_UCODE_SIZE         0x605b
+#define TURKS_SMC_INT_VECTOR_START   0xffc0
+#define TURKS_SMC_INT_VECTOR_SIZE    0x0040
+
+#define CAICOS_SMC_UCODE_START       0x0100
+#define CAICOS_SMC_UCODE_SIZE        0x5fbd
+#define CAICOS_SMC_INT_VECTOR_START  0xffc0
+#define CAICOS_SMC_INT_VECTOR_SIZE   0x0040
+
+#define CAYMAN_SMC_UCODE_START       0x0100
+#define CAYMAN_SMC_UCODE_SIZE        0x79ec
+#define CAYMAN_SMC_INT_VECTOR_START  0xffc0
+#define CAYMAN_SMC_INT_VECTOR_SIZE   0x0040
+
+#define TAHITI_SMC_UCODE_START       0x10000
+#define TAHITI_SMC_UCODE_SIZE        0xf458
+
+#define PITCAIRN_SMC_UCODE_START     0x10000
+#define PITCAIRN_SMC_UCODE_SIZE      0xe9f4
+
+#define VERDE_SMC_UCODE_START        0x10000
+#define VERDE_SMC_UCODE_SIZE         0xebe4
+
+#define OLAND_SMC_UCODE_START        0x10000
+#define OLAND_SMC_UCODE_SIZE         0xe7b4
+
+#define HAINAN_SMC_UCODE_START       0x10000
+#define HAINAN_SMC_UCODE_SIZE        0xe67C
+
+#endif
index cad735d..41efcec 100644 (file)
 #define FIRMWARE_CYPRESS       "radeon/CYPRESS_uvd.bin"
 #define FIRMWARE_SUMO          "radeon/SUMO_uvd.bin"
 #define FIRMWARE_TAHITI                "radeon/TAHITI_uvd.bin"
+#define FIRMWARE_BONAIRE       "radeon/BONAIRE_uvd.bin"
 
 MODULE_FIRMWARE(FIRMWARE_RV710);
 MODULE_FIRMWARE(FIRMWARE_CYPRESS);
 MODULE_FIRMWARE(FIRMWARE_SUMO);
 MODULE_FIRMWARE(FIRMWARE_TAHITI);
+MODULE_FIRMWARE(FIRMWARE_BONAIRE);
 
 static void radeon_uvd_idle_work_handler(struct work_struct *work);
 
@@ -100,6 +102,12 @@ int radeon_uvd_init(struct radeon_device *rdev)
                fw_name = FIRMWARE_TAHITI;
                break;
 
+       case CHIP_BONAIRE:
+       case CHIP_KABINI:
+       case CHIP_KAVERI:
+               fw_name = FIRMWARE_BONAIRE;
+               break;
+
        default:
                return -EINVAL;
        }
@@ -542,6 +550,7 @@ static int radeon_uvd_send_msg(struct radeon_device *rdev,
                               struct radeon_fence **fence)
 {
        struct ttm_validate_buffer tv;
+       struct ww_acquire_ctx ticket;
        struct list_head head;
        struct radeon_ib ib;
        uint64_t addr;
@@ -553,7 +562,7 @@ static int radeon_uvd_send_msg(struct radeon_device *rdev,
        INIT_LIST_HEAD(&head);
        list_add(&tv.head, &head);
 
-       r = ttm_eu_reserve_buffers(&head);
+       r = ttm_eu_reserve_buffers(&ticket, &head);
        if (r)
                return r;
 
@@ -561,16 +570,12 @@ static int radeon_uvd_send_msg(struct radeon_device *rdev,
        radeon_uvd_force_into_uvd_segment(bo);
 
        r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
-       if (r) {
-               ttm_eu_backoff_reservation(&head);
-               return r;
-       }
+       if (r) 
+               goto err;
 
        r = radeon_ib_get(rdev, ring, &ib, NULL, 16);
-       if (r) {
-               ttm_eu_backoff_reservation(&head);
-               return r;
-       }
+       if (r)
+               goto err;
 
        addr = radeon_bo_gpu_offset(bo);
        ib.ptr[0] = PACKET0(UVD_GPCOM_VCPU_DATA0, 0);
@@ -584,11 +589,9 @@ static int radeon_uvd_send_msg(struct radeon_device *rdev,
        ib.length_dw = 16;
 
        r = radeon_ib_schedule(rdev, &ib, NULL);
-       if (r) {
-               ttm_eu_backoff_reservation(&head);
-               return r;
-       }
-       ttm_eu_fence_buffer_objects(&head, ib.fence);
+       if (r)
+               goto err;
+       ttm_eu_fence_buffer_objects(&ticket, &head, ib.fence);
 
        if (fence)
                *fence = radeon_fence_ref(ib.fence);
@@ -596,6 +599,10 @@ static int radeon_uvd_send_msg(struct radeon_device *rdev,
        radeon_ib_free(rdev, &ib);
        radeon_bo_unref(&bo);
        return 0;
+
+err:
+       ttm_eu_backoff_reservation(&ticket, &head);
+       return r;
 }
 
 /* multiple fence commands without any stream commands in between can
@@ -691,11 +698,19 @@ static void radeon_uvd_idle_work_handler(struct work_struct *work)
        struct radeon_device *rdev =
                container_of(work, struct radeon_device, uvd.idle_work.work);
 
-       if (radeon_fence_count_emitted(rdev, R600_RING_TYPE_UVD_INDEX) == 0)
-               radeon_set_uvd_clocks(rdev, 0, 0);
-       else
+       if (radeon_fence_count_emitted(rdev, R600_RING_TYPE_UVD_INDEX) == 0) {
+               if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
+                       mutex_lock(&rdev->pm.mutex);
+                       rdev->pm.dpm.uvd_active = false;
+                       mutex_unlock(&rdev->pm.mutex);
+                       radeon_pm_compute_clocks(rdev);
+               } else {
+                       radeon_set_uvd_clocks(rdev, 0, 0);
+               }
+       } else {
                schedule_delayed_work(&rdev->uvd.idle_work,
                                      msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS));
+       }
 }
 
 void radeon_uvd_note_usage(struct radeon_device *rdev)
@@ -703,8 +718,14 @@ void radeon_uvd_note_usage(struct radeon_device *rdev)
        bool set_clocks = !cancel_delayed_work_sync(&rdev->uvd.idle_work);
        set_clocks &= schedule_delayed_work(&rdev->uvd.idle_work,
                                            msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS));
-       if (set_clocks)
-               radeon_set_uvd_clocks(rdev, 53300, 40000);
+       if (set_clocks) {
+               if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
+                       /* XXX pick SD/HD/MVC */
+                       radeon_dpm_enable_power_state(rdev, POWER_STATE_TYPE_INTERNAL_UVD);
+               } else {
+                       radeon_set_uvd_clocks(rdev, 53300, 40000);
+               }
+       }
 }
 
 static unsigned radeon_uvd_calc_upll_post_div(unsigned vco_freq,
index 55880d5..d8ddfb3 100644 (file)
@@ -248,13 +248,16 @@ struct rs690_watermark {
 };
 
 static void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
-                                 struct radeon_crtc *crtc,
-                                 struct rs690_watermark *wm)
+                                        struct radeon_crtc *crtc,
+                                        struct rs690_watermark *wm,
+                                        bool low)
 {
        struct drm_display_mode *mode = &crtc->base.mode;
        fixed20_12 a, b, c;
        fixed20_12 pclk, request_fifo_depth, tolerable_latency, estimated_width;
        fixed20_12 consumption_time, line_time, chunk_time, read_delay_latency;
+       fixed20_12 sclk, core_bandwidth, max_bandwidth;
+       u32 selected_sclk;
 
        if (!crtc->base.enabled) {
                /* FIXME: wouldn't it better to set priority mark to maximum */
@@ -262,6 +265,21 @@ static void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
                return;
        }
 
+       if (((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880)) &&
+           (rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled)
+               selected_sclk = radeon_dpm_get_sclk(rdev, low);
+       else
+               selected_sclk = rdev->pm.current_sclk;
+
+       /* sclk in Mhz */
+       a.full = dfixed_const(100);
+       sclk.full = dfixed_const(selected_sclk);
+       sclk.full = dfixed_div(sclk, a);
+
+       /* core_bandwidth = sclk(Mhz) * 16 */
+       a.full = dfixed_const(16);
+       core_bandwidth.full = dfixed_div(rdev->pm.sclk, a);
+
        if (crtc->vsc.full > dfixed_const(2))
                wm->num_line_pair.full = dfixed_const(2);
        else
@@ -322,36 +340,36 @@ static void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
        wm->active_time.full = dfixed_div(wm->active_time, a);
 
        /* Maximun bandwidth is the minimun bandwidth of all component */
-       rdev->pm.max_bandwidth = rdev->pm.core_bandwidth;
+       max_bandwidth = core_bandwidth;
        if (rdev->mc.igp_sideport_enabled) {
-               if (rdev->pm.max_bandwidth.full > rdev->pm.sideport_bandwidth.full &&
+               if (max_bandwidth.full > rdev->pm.sideport_bandwidth.full &&
                        rdev->pm.sideport_bandwidth.full)
-                       rdev->pm.max_bandwidth = rdev->pm.sideport_bandwidth;
+                       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);
        } else {
-               if (rdev->pm.max_bandwidth.full > rdev->pm.k8_bandwidth.full &&
+               if (max_bandwidth.full > rdev->pm.k8_bandwidth.full &&
                        rdev->pm.k8_bandwidth.full)
-                       rdev->pm.max_bandwidth = rdev->pm.k8_bandwidth;
-               if (rdev->pm.max_bandwidth.full > rdev->pm.ht_bandwidth.full &&
+                       max_bandwidth = rdev->pm.k8_bandwidth;
+               if (max_bandwidth.full > rdev->pm.ht_bandwidth.full &&
                        rdev->pm.ht_bandwidth.full)
-                       rdev->pm.max_bandwidth = rdev->pm.ht_bandwidth;
+                       max_bandwidth = rdev->pm.ht_bandwidth;
                read_delay_latency.full = dfixed_const(5000);
        }
 
        /* sclk = system clocks(ns) = 1000 / max_bandwidth / 16 */
        a.full = dfixed_const(16);
-       rdev->pm.sclk.full = dfixed_mul(rdev->pm.max_bandwidth, a);
+       sclk.full = dfixed_mul(max_bandwidth, a);
        a.full = dfixed_const(1000);
-       rdev->pm.sclk.full = dfixed_div(a, rdev->pm.sclk);
+       sclk.full = dfixed_div(a, sclk);
        /* Determine chunk time
         * ChunkTime = the time it takes the DCP to send one chunk of data
         * to the LB which consists of pipeline delay and inter chunk gap
         * sclk = system clock(ns)
         */
        a.full = dfixed_const(256 * 13);
-       chunk_time.full = dfixed_mul(rdev->pm.sclk, a);
+       chunk_time.full = dfixed_mul(sclk, a);
        a.full = dfixed_const(10);
        chunk_time.full = dfixed_div(chunk_time, a);
 
@@ -415,175 +433,200 @@ static void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
        }
 }
 
-void rs690_bandwidth_update(struct radeon_device *rdev)
+static void rs690_compute_mode_priority(struct radeon_device *rdev,
+                                       struct rs690_watermark *wm0,
+                                       struct rs690_watermark *wm1,
+                                       struct drm_display_mode *mode0,
+                                       struct drm_display_mode *mode1,
+                                       u32 *d1mode_priority_a_cnt,
+                                       u32 *d2mode_priority_a_cnt)
 {
-       struct drm_display_mode *mode0 = NULL;
-       struct drm_display_mode *mode1 = NULL;
-       struct rs690_watermark wm0;
-       struct rs690_watermark wm1;
-       u32 tmp;
-       u32 d1mode_priority_a_cnt = S_006548_D1MODE_PRIORITY_A_OFF(1);
-       u32 d2mode_priority_a_cnt = S_006548_D1MODE_PRIORITY_A_OFF(1);
        fixed20_12 priority_mark02, priority_mark12, fill_rate;
        fixed20_12 a, b;
 
-       radeon_update_display_priority(rdev);
-
-       if (rdev->mode_info.crtcs[0]->base.enabled)
-               mode0 = &rdev->mode_info.crtcs[0]->base.mode;
-       if (rdev->mode_info.crtcs[1]->base.enabled)
-               mode1 = &rdev->mode_info.crtcs[1]->base.mode;
-       /*
-        * Set display0/1 priority up in the memory controller for
-        * modes if the user specifies HIGH for displaypriority
-        * option.
-        */
-       if ((rdev->disp_priority == 2) &&
-           ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740))) {
-               tmp = RREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER);
-               tmp &= C_000104_MC_DISP0R_INIT_LAT;
-               tmp &= C_000104_MC_DISP1R_INIT_LAT;
-               if (mode0)
-                       tmp |= S_000104_MC_DISP0R_INIT_LAT(1);
-               if (mode1)
-                       tmp |= S_000104_MC_DISP1R_INIT_LAT(1);
-               WREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER, tmp);
-       }
-       rs690_line_buffer_adjust(rdev, mode0, mode1);
-
-       if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740))
-               WREG32(R_006C9C_DCP_CONTROL, 0);
-       if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880))
-               WREG32(R_006C9C_DCP_CONTROL, 2);
-
-       rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0);
-       rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1);
-
-       tmp = (wm0.lb_request_fifo_depth - 1);
-       tmp |= (wm1.lb_request_fifo_depth - 1) << 16;
-       WREG32(R_006D58_LB_MAX_REQ_OUTSTANDING, tmp);
+       *d1mode_priority_a_cnt = S_006548_D1MODE_PRIORITY_A_OFF(1);
+       *d2mode_priority_a_cnt = S_006548_D1MODE_PRIORITY_A_OFF(1);
 
        if (mode0 && mode1) {
-               if (dfixed_trunc(wm0.dbpp) > 64)
-                       a.full = dfixed_mul(wm0.dbpp, wm0.num_line_pair);
+               if (dfixed_trunc(wm0->dbpp) > 64)
+                       a.full = dfixed_mul(wm0->dbpp, wm0->num_line_pair);
                else
-                       a.full = wm0.num_line_pair.full;
-               if (dfixed_trunc(wm1.dbpp) > 64)
-                       b.full = dfixed_mul(wm1.dbpp, wm1.num_line_pair);
+                       a.full = wm0->num_line_pair.full;
+               if (dfixed_trunc(wm1->dbpp) > 64)
+                       b.full = dfixed_mul(wm1->dbpp, wm1->num_line_pair);
                else
-                       b.full = wm1.num_line_pair.full;
+                       b.full = wm1->num_line_pair.full;
                a.full += b.full;
-               fill_rate.full = dfixed_div(wm0.sclk, a);
-               if (wm0.consumption_rate.full > fill_rate.full) {
-                       b.full = wm0.consumption_rate.full - fill_rate.full;
-                       b.full = dfixed_mul(b, wm0.active_time);
-                       a.full = dfixed_mul(wm0.worst_case_latency,
-                                               wm0.consumption_rate);
+               fill_rate.full = dfixed_div(wm0->sclk, a);
+               if (wm0->consumption_rate.full > fill_rate.full) {
+                       b.full = wm0->consumption_rate.full - fill_rate.full;
+                       b.full = dfixed_mul(b, wm0->active_time);
+                       a.full = dfixed_mul(wm0->worst_case_latency,
+                                               wm0->consumption_rate);
                        a.full = a.full + b.full;
                        b.full = dfixed_const(16 * 1000);
                        priority_mark02.full = dfixed_div(a, b);
                } else {
-                       a.full = dfixed_mul(wm0.worst_case_latency,
-                                               wm0.consumption_rate);
+                       a.full = dfixed_mul(wm0->worst_case_latency,
+                                               wm0->consumption_rate);
                        b.full = dfixed_const(16 * 1000);
                        priority_mark02.full = dfixed_div(a, b);
                }
-               if (wm1.consumption_rate.full > fill_rate.full) {
-                       b.full = wm1.consumption_rate.full - fill_rate.full;
-                       b.full = dfixed_mul(b, wm1.active_time);
-                       a.full = dfixed_mul(wm1.worst_case_latency,
-                                               wm1.consumption_rate);
+               if (wm1->consumption_rate.full > fill_rate.full) {
+                       b.full = wm1->consumption_rate.full - fill_rate.full;
+                       b.full = dfixed_mul(b, wm1->active_time);
+                       a.full = dfixed_mul(wm1->worst_case_latency,
+                                               wm1->consumption_rate);
                        a.full = a.full + b.full;
                        b.full = dfixed_const(16 * 1000);
                        priority_mark12.full = dfixed_div(a, b);
                } else {
-                       a.full = dfixed_mul(wm1.worst_case_latency,
-                                               wm1.consumption_rate);
+                       a.full = dfixed_mul(wm1->worst_case_latency,
+                                               wm1->consumption_rate);
                        b.full = dfixed_const(16 * 1000);
                        priority_mark12.full = dfixed_div(a, b);
                }
-               if (wm0.priority_mark.full > priority_mark02.full)
-                       priority_mark02.full = wm0.priority_mark.full;
+               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 (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);
-               d2mode_priority_a_cnt = dfixed_trunc(priority_mark12);
+               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);
+               *d2mode_priority_a_cnt = dfixed_trunc(priority_mark12);
                if (rdev->disp_priority == 2) {
-                       d1mode_priority_a_cnt |= S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(1);
-                       d2mode_priority_a_cnt |= S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(1);
+                       *d1mode_priority_a_cnt |= S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(1);
+                       *d2mode_priority_a_cnt |= S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(1);
                }
        } else if (mode0) {
-               if (dfixed_trunc(wm0.dbpp) > 64)
-                       a.full = dfixed_mul(wm0.dbpp, wm0.num_line_pair);
+               if (dfixed_trunc(wm0->dbpp) > 64)
+                       a.full = dfixed_mul(wm0->dbpp, wm0->num_line_pair);
                else
-                       a.full = wm0.num_line_pair.full;
-               fill_rate.full = dfixed_div(wm0.sclk, a);
-               if (wm0.consumption_rate.full > fill_rate.full) {
-                       b.full = wm0.consumption_rate.full - fill_rate.full;
-                       b.full = dfixed_mul(b, wm0.active_time);
-                       a.full = dfixed_mul(wm0.worst_case_latency,
-                                               wm0.consumption_rate);
+                       a.full = wm0->num_line_pair.full;
+               fill_rate.full = dfixed_div(wm0->sclk, a);
+               if (wm0->consumption_rate.full > fill_rate.full) {
+                       b.full = wm0->consumption_rate.full - fill_rate.full;
+                       b.full = dfixed_mul(b, wm0->active_time);
+                       a.full = dfixed_mul(wm0->worst_case_latency,
+                                               wm0->consumption_rate);
                        a.full = a.full + b.full;
                        b.full = dfixed_const(16 * 1000);
                        priority_mark02.full = dfixed_div(a, b);
                } else {
-                       a.full = dfixed_mul(wm0.worst_case_latency,
-                                               wm0.consumption_rate);
+                       a.full = dfixed_mul(wm0->worst_case_latency,
+                                               wm0->consumption_rate);
                        b.full = dfixed_const(16 * 1000);
                        priority_mark02.full = dfixed_div(a, b);
                }
-               if (wm0.priority_mark.full > priority_mark02.full)
-                       priority_mark02.full = wm0.priority_mark.full;
+               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);
+               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);
                if (rdev->disp_priority == 2)
-                       d1mode_priority_a_cnt |= S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(1);
+                       *d1mode_priority_a_cnt |= S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(1);
        } else if (mode1) {
-               if (dfixed_trunc(wm1.dbpp) > 64)
-                       a.full = dfixed_mul(wm1.dbpp, wm1.num_line_pair);
+               if (dfixed_trunc(wm1->dbpp) > 64)
+                       a.full = dfixed_mul(wm1->dbpp, wm1->num_line_pair);
                else
-                       a.full = wm1.num_line_pair.full;
-               fill_rate.full = dfixed_div(wm1.sclk, a);
-               if (wm1.consumption_rate.full > fill_rate.full) {
-                       b.full = wm1.consumption_rate.full - fill_rate.full;
-                       b.full = dfixed_mul(b, wm1.active_time);
-                       a.full = dfixed_mul(wm1.worst_case_latency,
-                                               wm1.consumption_rate);
+                       a.full = wm1->num_line_pair.full;
+               fill_rate.full = dfixed_div(wm1->sclk, a);
+               if (wm1->consumption_rate.full > fill_rate.full) {
+                       b.full = wm1->consumption_rate.full - fill_rate.full;
+                       b.full = dfixed_mul(b, wm1->active_time);
+                       a.full = dfixed_mul(wm1->worst_case_latency,
+                                               wm1->consumption_rate);
                        a.full = a.full + b.full;
                        b.full = dfixed_const(16 * 1000);
                        priority_mark12.full = dfixed_div(a, b);
                } else {
-                       a.full = dfixed_mul(wm1.worst_case_latency,
-                                               wm1.consumption_rate);
+                       a.full = dfixed_mul(wm1->worst_case_latency,
+                                               wm1->consumption_rate);
                        b.full = dfixed_const(16 * 1000);
                        priority_mark12.full = dfixed_div(a, b);
                }
-               if (wm1.priority_mark.full > priority_mark12.full)
-                       priority_mark12.full = wm1.priority_mark.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;
-               d2mode_priority_a_cnt = dfixed_trunc(priority_mark12);
+               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);
                if (rdev->disp_priority == 2)
-                       d2mode_priority_a_cnt |= S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(1);
+                       *d2mode_priority_a_cnt |= S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(1);
        }
+}
+
+void rs690_bandwidth_update(struct radeon_device *rdev)
+{
+       struct drm_display_mode *mode0 = NULL;
+       struct drm_display_mode *mode1 = NULL;
+       struct rs690_watermark wm0_high, wm0_low;
+       struct rs690_watermark wm1_high, wm1_low;
+       u32 tmp;
+       u32 d1mode_priority_a_cnt, d1mode_priority_b_cnt;
+       u32 d2mode_priority_a_cnt, d2mode_priority_b_cnt;
+
+       radeon_update_display_priority(rdev);
+
+       if (rdev->mode_info.crtcs[0]->base.enabled)
+               mode0 = &rdev->mode_info.crtcs[0]->base.mode;
+       if (rdev->mode_info.crtcs[1]->base.enabled)
+               mode1 = &rdev->mode_info.crtcs[1]->base.mode;
+       /*
+        * Set display0/1 priority up in the memory controller for
+        * modes if the user specifies HIGH for displaypriority
+        * option.
+        */
+       if ((rdev->disp_priority == 2) &&
+           ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740))) {
+               tmp = RREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER);
+               tmp &= C_000104_MC_DISP0R_INIT_LAT;
+               tmp &= C_000104_MC_DISP1R_INIT_LAT;
+               if (mode0)
+                       tmp |= S_000104_MC_DISP0R_INIT_LAT(1);
+               if (mode1)
+                       tmp |= S_000104_MC_DISP1R_INIT_LAT(1);
+               WREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER, tmp);
+       }
+       rs690_line_buffer_adjust(rdev, mode0, mode1);
+
+       if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740))
+               WREG32(R_006C9C_DCP_CONTROL, 0);
+       if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880))
+               WREG32(R_006C9C_DCP_CONTROL, 2);
+
+       rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0_high, false);
+       rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1_high, false);
+
+       rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0_low, true);
+       rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1_low, true);
+
+       tmp = (wm0_high.lb_request_fifo_depth - 1);
+       tmp |= (wm1_high.lb_request_fifo_depth - 1) << 16;
+       WREG32(R_006D58_LB_MAX_REQ_OUTSTANDING, tmp);
+
+       rs690_compute_mode_priority(rdev,
+                                   &wm0_high, &wm1_high,
+                                   mode0, mode1,
+                                   &d1mode_priority_a_cnt, &d2mode_priority_a_cnt);
+       rs690_compute_mode_priority(rdev,
+                                   &wm0_low, &wm1_low,
+                                   mode0, mode1,
+                                   &d1mode_priority_b_cnt, &d2mode_priority_b_cnt);
 
        WREG32(R_006548_D1MODE_PRIORITY_A_CNT, d1mode_priority_a_cnt);
-       WREG32(R_00654C_D1MODE_PRIORITY_B_CNT, d1mode_priority_a_cnt);
+       WREG32(R_00654C_D1MODE_PRIORITY_B_CNT, d1mode_priority_b_cnt);
        WREG32(R_006D48_D2MODE_PRIORITY_A_CNT, d2mode_priority_a_cnt);
-       WREG32(R_006D4C_D2MODE_PRIORITY_B_CNT, d2mode_priority_a_cnt);
+       WREG32(R_006D4C_D2MODE_PRIORITY_B_CNT, d2mode_priority_b_cnt);
 }
 
 uint32_t rs690_mc_rreg(struct radeon_device *rdev, uint32_t reg)
diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c
new file mode 100644 (file)
index 0000000..bef832a
--- /dev/null
@@ -0,0 +1,963 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "rs780d.h"
+#include "r600_dpm.h"
+#include "rs780_dpm.h"
+#include "atom.h"
+
+static struct igp_ps *rs780_get_ps(struct radeon_ps *rps)
+{
+       struct igp_ps *ps = rps->ps_priv;
+
+       return ps;
+}
+
+static struct igp_power_info *rs780_get_pi(struct radeon_device *rdev)
+{
+       struct igp_power_info *pi = rdev->pm.dpm.priv;
+
+       return pi;
+}
+
+static void rs780_get_pm_mode_parameters(struct radeon_device *rdev)
+{
+       struct igp_power_info *pi = rs780_get_pi(rdev);
+       struct radeon_mode_info *minfo = &rdev->mode_info;
+       struct drm_crtc *crtc;
+       struct radeon_crtc *radeon_crtc;
+       int i;
+
+       /* defaults */
+       pi->crtc_id = 0;
+       pi->refresh_rate = 60;
+
+       for (i = 0; i < rdev->num_crtc; i++) {
+               crtc = (struct drm_crtc *)minfo->crtcs[i];
+               if (crtc && crtc->enabled) {
+                       radeon_crtc = to_radeon_crtc(crtc);
+                       pi->crtc_id = radeon_crtc->crtc_id;
+                       if (crtc->mode.htotal && crtc->mode.vtotal)
+                               pi->refresh_rate =
+                                       (crtc->mode.clock * 1000) /
+                                       (crtc->mode.htotal * crtc->mode.vtotal);
+                       break;
+               }
+       }
+}
+
+static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable);
+
+static int rs780_initialize_dpm_power_state(struct radeon_device *rdev,
+                                           struct radeon_ps *boot_ps)
+{
+       struct atom_clock_dividers dividers;
+       struct igp_ps *default_state = rs780_get_ps(boot_ps);
+       int i, ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            default_state->sclk_low, false, &dividers);
+       if (ret)
+               return ret;
+
+       r600_engine_clock_entry_set_reference_divider(rdev, 0, dividers.ref_div);
+       r600_engine_clock_entry_set_feedback_divider(rdev, 0, dividers.fb_div);
+       r600_engine_clock_entry_set_post_divider(rdev, 0, dividers.post_div);
+
+       if (dividers.enable_post_div)
+               r600_engine_clock_entry_enable_post_divider(rdev, 0, true);
+       else
+               r600_engine_clock_entry_enable_post_divider(rdev, 0, false);
+
+       r600_engine_clock_entry_set_step_time(rdev, 0, R600_SST_DFLT);
+       r600_engine_clock_entry_enable_pulse_skipping(rdev, 0, false);
+
+       r600_engine_clock_entry_enable(rdev, 0, true);
+       for (i = 1; i < R600_PM_NUMBER_OF_SCLKS; i++)
+               r600_engine_clock_entry_enable(rdev, i, false);
+
+       r600_enable_mclk_control(rdev, false);
+       r600_voltage_control_enable_pins(rdev, 0);
+
+       return 0;
+}
+
+static int rs780_initialize_dpm_parameters(struct radeon_device *rdev,
+                                          struct radeon_ps *boot_ps)
+{
+       int ret = 0;
+       int i;
+
+       r600_set_bsp(rdev, R600_BSU_DFLT, R600_BSP_DFLT);
+
+       r600_set_at(rdev, 0, 0, 0, 0);
+
+       r600_set_git(rdev, R600_GICST_DFLT);
+
+       for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
+               r600_set_tc(rdev, i, 0, 0);
+
+       r600_select_td(rdev, R600_TD_DFLT);
+       r600_set_vrc(rdev, 0);
+
+       r600_set_tpu(rdev, R600_TPU_DFLT);
+       r600_set_tpc(rdev, R600_TPC_DFLT);
+
+       r600_set_sstu(rdev, R600_SSTU_DFLT);
+       r600_set_sst(rdev, R600_SST_DFLT);
+
+       r600_set_fctu(rdev, R600_FCTU_DFLT);
+       r600_set_fct(rdev, R600_FCT_DFLT);
+
+       r600_set_vddc3d_oorsu(rdev, R600_VDDC3DOORSU_DFLT);
+       r600_set_vddc3d_oorphc(rdev, R600_VDDC3DOORPHC_DFLT);
+       r600_set_vddc3d_oorsdc(rdev, R600_VDDC3DOORSDC_DFLT);
+       r600_set_ctxcgtt3d_rphc(rdev, R600_CTXCGTT3DRPHC_DFLT);
+       r600_set_ctxcgtt3d_rsdc(rdev, R600_CTXCGTT3DRSDC_DFLT);
+
+       r600_vid_rt_set_vru(rdev, R600_VRU_DFLT);
+       r600_vid_rt_set_vrt(rdev, R600_VOLTAGERESPONSETIME_DFLT);
+       r600_vid_rt_set_ssu(rdev, R600_SPLLSTEPUNIT_DFLT);
+
+       ret = rs780_initialize_dpm_power_state(rdev, boot_ps);
+
+       r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW,     0);
+       r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM,  0);
+       r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_HIGH,    0);
+
+       r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW,    0);
+       r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
+       r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_HIGH,   0);
+
+       r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW,    0);
+       r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
+       r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_HIGH,   0);
+
+       r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW,    R600_DISPLAY_WATERMARK_HIGH);
+       r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM, R600_DISPLAY_WATERMARK_HIGH);
+       r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_HIGH,   R600_DISPLAY_WATERMARK_HIGH);
+
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_CTXSW, false);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
+
+       r600_power_level_set_enter_index(rdev, R600_POWER_LEVEL_LOW);
+
+       r600_set_vrc(rdev, RS780_CGFTV_DFLT);
+
+       return ret;
+}
+
+static void rs780_start_dpm(struct radeon_device *rdev)
+{
+       r600_enable_sclk_control(rdev, false);
+       r600_enable_mclk_control(rdev, false);
+
+       r600_dynamicpm_enable(rdev, true);
+
+       radeon_wait_for_vblank(rdev, 0);
+       radeon_wait_for_vblank(rdev, 1);
+
+       r600_enable_spll_bypass(rdev, true);
+       r600_wait_for_spll_change(rdev);
+       r600_enable_spll_bypass(rdev, false);
+       r600_wait_for_spll_change(rdev);
+
+       r600_enable_spll_bypass(rdev, true);
+       r600_wait_for_spll_change(rdev);
+       r600_enable_spll_bypass(rdev, false);
+       r600_wait_for_spll_change(rdev);
+
+       r600_enable_sclk_control(rdev, true);
+}
+
+
+static void rs780_preset_ranges_slow_clk_fbdiv_en(struct radeon_device *rdev)
+{
+       WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1, RANGE_SLOW_CLK_FEEDBACK_DIV_EN,
+                ~RANGE_SLOW_CLK_FEEDBACK_DIV_EN);
+
+       WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1,
+                RANGE0_SLOW_CLK_FEEDBACK_DIV(RS780_SLOWCLKFEEDBACKDIV_DFLT),
+                ~RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK);
+}
+
+static void rs780_preset_starting_fbdiv(struct radeon_device *rdev)
+{
+       u32 fbdiv = (RREG32(CG_SPLL_FUNC_CNTL) & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT;
+
+       WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(fbdiv),
+                ~STARTING_FEEDBACK_DIV_MASK);
+
+       WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(fbdiv),
+                ~FORCED_FEEDBACK_DIV_MASK);
+
+       WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV);
+}
+
+static void rs780_voltage_scaling_init(struct radeon_device *rdev)
+{
+       struct igp_power_info *pi = rs780_get_pi(rdev);
+       struct drm_device *dev = rdev->ddev;
+       u32 fv_throt_pwm_fb_div_range[3];
+       u32 fv_throt_pwm_range[4];
+
+       if (dev->pdev->device == 0x9614) {
+               fv_throt_pwm_fb_div_range[0] = RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT;
+               fv_throt_pwm_fb_div_range[1] = RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT;
+               fv_throt_pwm_fb_div_range[2] = RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT;
+       } else if ((dev->pdev->device == 0x9714) ||
+                  (dev->pdev->device == 0x9715)) {
+               fv_throt_pwm_fb_div_range[0] = RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT;
+               fv_throt_pwm_fb_div_range[1] = RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT;
+               fv_throt_pwm_fb_div_range[2] = RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT;
+       } else {
+               fv_throt_pwm_fb_div_range[0] = RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT;
+               fv_throt_pwm_fb_div_range[1] = RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT;
+               fv_throt_pwm_fb_div_range[2] = RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT;
+       }
+
+       if (pi->pwm_voltage_control) {
+               fv_throt_pwm_range[0] = pi->min_voltage;
+               fv_throt_pwm_range[1] = pi->min_voltage;
+               fv_throt_pwm_range[2] = pi->max_voltage;
+               fv_throt_pwm_range[3] = pi->max_voltage;
+       } else {
+               fv_throt_pwm_range[0] = pi->invert_pwm_required ?
+                       RS780_FVTHROTPWMRANGE3_GPIO_DFLT : RS780_FVTHROTPWMRANGE0_GPIO_DFLT;
+               fv_throt_pwm_range[1] = pi->invert_pwm_required ?
+                       RS780_FVTHROTPWMRANGE2_GPIO_DFLT : RS780_FVTHROTPWMRANGE1_GPIO_DFLT;
+               fv_throt_pwm_range[2] = pi->invert_pwm_required ?
+                       RS780_FVTHROTPWMRANGE1_GPIO_DFLT : RS780_FVTHROTPWMRANGE2_GPIO_DFLT;
+               fv_throt_pwm_range[3] = pi->invert_pwm_required ?
+                       RS780_FVTHROTPWMRANGE0_GPIO_DFLT : RS780_FVTHROTPWMRANGE3_GPIO_DFLT;
+       }
+
+       WREG32_P(FVTHROT_PWM_CTRL_REG0,
+                STARTING_PWM_HIGHTIME(pi->max_voltage),
+                ~STARTING_PWM_HIGHTIME_MASK);
+
+       WREG32_P(FVTHROT_PWM_CTRL_REG0,
+                NUMBER_OF_CYCLES_IN_PERIOD(pi->num_of_cycles_in_period),
+                ~NUMBER_OF_CYCLES_IN_PERIOD_MASK);
+
+       WREG32_P(FVTHROT_PWM_CTRL_REG0, FORCE_STARTING_PWM_HIGHTIME,
+                ~FORCE_STARTING_PWM_HIGHTIME);
+
+       if (pi->invert_pwm_required)
+               WREG32_P(FVTHROT_PWM_CTRL_REG0, INVERT_PWM_WAVEFORM, ~INVERT_PWM_WAVEFORM);
+       else
+               WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~INVERT_PWM_WAVEFORM);
+
+       rs780_voltage_scaling_enable(rdev, true);
+
+       WREG32(FVTHROT_PWM_CTRL_REG1,
+              (MIN_PWM_HIGHTIME(pi->min_voltage) |
+               MAX_PWM_HIGHTIME(pi->max_voltage)));
+
+       WREG32(FVTHROT_PWM_US_REG0, RS780_FVTHROTPWMUSREG0_DFLT);
+       WREG32(FVTHROT_PWM_US_REG1, RS780_FVTHROTPWMUSREG1_DFLT);
+       WREG32(FVTHROT_PWM_DS_REG0, RS780_FVTHROTPWMDSREG0_DFLT);
+       WREG32(FVTHROT_PWM_DS_REG1, RS780_FVTHROTPWMDSREG1_DFLT);
+
+       WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1,
+                RANGE0_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[0]),
+                ~RANGE0_PWM_FEEDBACK_DIV_MASK);
+
+       WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG2,
+              (RANGE1_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[1]) |
+               RANGE2_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[2])));
+
+       WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG3,
+              (RANGE0_PWM(fv_throt_pwm_range[1]) |
+               RANGE1_PWM(fv_throt_pwm_range[2])));
+       WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG4,
+              (RANGE2_PWM(fv_throt_pwm_range[1]) |
+               RANGE3_PWM(fv_throt_pwm_range[2])));
+}
+
+static void rs780_clk_scaling_enable(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT | ENABLE_FV_UPDATE,
+                        ~(ENABLE_FV_THROT | ENABLE_FV_UPDATE));
+       else
+               WREG32_P(FVTHROT_CNTRL_REG, 0,
+                        ~(ENABLE_FV_THROT | ENABLE_FV_UPDATE));
+}
+
+static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT_IO, ~ENABLE_FV_THROT_IO);
+       else
+               WREG32_P(FVTHROT_CNTRL_REG, 0, ~ENABLE_FV_THROT_IO);
+}
+
+static void rs780_set_engine_clock_wfc(struct radeon_device *rdev)
+{
+       WREG32(FVTHROT_UTC0, RS780_FVTHROTUTC0_DFLT);
+       WREG32(FVTHROT_UTC1, RS780_FVTHROTUTC1_DFLT);
+       WREG32(FVTHROT_UTC2, RS780_FVTHROTUTC2_DFLT);
+       WREG32(FVTHROT_UTC3, RS780_FVTHROTUTC3_DFLT);
+       WREG32(FVTHROT_UTC4, RS780_FVTHROTUTC4_DFLT);
+
+       WREG32(FVTHROT_DTC0, RS780_FVTHROTDTC0_DFLT);
+       WREG32(FVTHROT_DTC1, RS780_FVTHROTDTC1_DFLT);
+       WREG32(FVTHROT_DTC2, RS780_FVTHROTDTC2_DFLT);
+       WREG32(FVTHROT_DTC3, RS780_FVTHROTDTC3_DFLT);
+       WREG32(FVTHROT_DTC4, RS780_FVTHROTDTC4_DFLT);
+}
+
+static void rs780_set_engine_clock_sc(struct radeon_device *rdev)
+{
+       WREG32_P(FVTHROT_FBDIV_REG2,
+                FB_DIV_TIMER_VAL(RS780_FBDIVTIMERVAL_DFLT),
+                ~FB_DIV_TIMER_VAL_MASK);
+
+       WREG32_P(FVTHROT_CNTRL_REG,
+                REFRESH_RATE_DIVISOR(0) | MINIMUM_CIP(0xf),
+                ~(REFRESH_RATE_DIVISOR_MASK | MINIMUM_CIP_MASK));
+}
+
+static void rs780_set_engine_clock_tdc(struct radeon_device *rdev)
+{
+       WREG32_P(FVTHROT_CNTRL_REG, 0, ~(FORCE_TREND_SEL | TREND_SEL_MODE));
+}
+
+static void rs780_set_engine_clock_ssc(struct radeon_device *rdev)
+{
+       WREG32(FVTHROT_FB_US_REG0, RS780_FVTHROTFBUSREG0_DFLT);
+       WREG32(FVTHROT_FB_US_REG1, RS780_FVTHROTFBUSREG1_DFLT);
+       WREG32(FVTHROT_FB_DS_REG0, RS780_FVTHROTFBDSREG0_DFLT);
+       WREG32(FVTHROT_FB_DS_REG1, RS780_FVTHROTFBDSREG1_DFLT);
+
+       WREG32_P(FVTHROT_FBDIV_REG1, MAX_FEEDBACK_STEP(1), ~MAX_FEEDBACK_STEP_MASK);
+}
+
+static void rs780_program_at(struct radeon_device *rdev)
+{
+       struct igp_power_info *pi = rs780_get_pi(rdev);
+
+       WREG32(FVTHROT_TARGET_REG, 30000000 / pi->refresh_rate);
+       WREG32(FVTHROT_CB1, 1000000 * 5 / pi->refresh_rate);
+       WREG32(FVTHROT_CB2, 1000000 * 10 / pi->refresh_rate);
+       WREG32(FVTHROT_CB3, 1000000 * 30 / pi->refresh_rate);
+       WREG32(FVTHROT_CB4, 1000000 * 50 / pi->refresh_rate);
+}
+
+static void rs780_disable_vbios_powersaving(struct radeon_device *rdev)
+{
+       WREG32_P(CG_INTGFX_MISC, 0, ~0xFFF00000);
+}
+
+static void rs780_force_voltage_to_high(struct radeon_device *rdev)
+{
+       struct igp_power_info *pi = rs780_get_pi(rdev);
+       struct igp_ps *current_state = rs780_get_ps(rdev->pm.dpm.current_ps);
+
+       if ((current_state->max_voltage == RS780_VDDC_LEVEL_HIGH) &&
+           (current_state->min_voltage == RS780_VDDC_LEVEL_HIGH))
+               return;
+
+       WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
+
+       udelay(1);
+
+       WREG32_P(FVTHROT_PWM_CTRL_REG0,
+                STARTING_PWM_HIGHTIME(pi->max_voltage),
+                ~STARTING_PWM_HIGHTIME_MASK);
+
+       WREG32_P(FVTHROT_PWM_CTRL_REG0,
+                FORCE_STARTING_PWM_HIGHTIME, ~FORCE_STARTING_PWM_HIGHTIME);
+
+       WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1, 0,
+               ~RANGE_PWM_FEEDBACK_DIV_EN);
+
+       udelay(1);
+
+       WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
+}
+
+static int rs780_set_engine_clock_scaling(struct radeon_device *rdev,
+                                         struct radeon_ps *new_ps,
+                                         struct radeon_ps *old_ps)
+{
+       struct atom_clock_dividers min_dividers, max_dividers, current_max_dividers;
+       struct igp_ps *new_state = rs780_get_ps(new_ps);
+       struct igp_ps *old_state = rs780_get_ps(old_ps);
+       int ret;
+
+       if ((new_state->sclk_high == old_state->sclk_high) &&
+           (new_state->sclk_low == old_state->sclk_low))
+               return 0;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            new_state->sclk_low, false, &min_dividers);
+       if (ret)
+               return ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            new_state->sclk_high, false, &max_dividers);
+       if (ret)
+               return ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            old_state->sclk_high, false, &current_max_dividers);
+       if (ret)
+               return ret;
+
+       WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
+
+       WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(max_dividers.fb_div),
+                ~FORCED_FEEDBACK_DIV_MASK);
+       WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(max_dividers.fb_div),
+                ~STARTING_FEEDBACK_DIV_MASK);
+       WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV);
+
+       udelay(100);
+
+       WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
+
+       if (max_dividers.fb_div > min_dividers.fb_div) {
+               WREG32_P(FVTHROT_FBDIV_REG0,
+                        MIN_FEEDBACK_DIV(min_dividers.fb_div) |
+                        MAX_FEEDBACK_DIV(max_dividers.fb_div),
+                        ~(MIN_FEEDBACK_DIV_MASK | MAX_FEEDBACK_DIV_MASK));
+
+               WREG32_P(FVTHROT_FBDIV_REG1, 0, ~FORCE_FEEDBACK_DIV);
+       }
+
+       return 0;
+}
+
+static void rs780_set_engine_clock_spc(struct radeon_device *rdev,
+                                      struct radeon_ps *new_ps,
+                                      struct radeon_ps *old_ps)
+{
+       struct igp_ps *new_state = rs780_get_ps(new_ps);
+       struct igp_ps *old_state = rs780_get_ps(old_ps);
+       struct igp_power_info *pi = rs780_get_pi(rdev);
+
+       if ((new_state->sclk_high == old_state->sclk_high) &&
+           (new_state->sclk_low == old_state->sclk_low))
+               return;
+
+       if (pi->crtc_id == 0)
+               WREG32_P(CG_INTGFX_MISC, 0, ~FVTHROT_VBLANK_SEL);
+       else
+               WREG32_P(CG_INTGFX_MISC, FVTHROT_VBLANK_SEL, ~FVTHROT_VBLANK_SEL);
+
+}
+
+static void rs780_activate_engine_clk_scaling(struct radeon_device *rdev,
+                                             struct radeon_ps *new_ps,
+                                             struct radeon_ps *old_ps)
+{
+       struct igp_ps *new_state = rs780_get_ps(new_ps);
+       struct igp_ps *old_state = rs780_get_ps(old_ps);
+
+       if ((new_state->sclk_high == old_state->sclk_high) &&
+           (new_state->sclk_low == old_state->sclk_low))
+               return;
+
+       rs780_clk_scaling_enable(rdev, true);
+}
+
+static u32 rs780_get_voltage_for_vddc_level(struct radeon_device *rdev,
+                                           enum rs780_vddc_level vddc)
+{
+       struct igp_power_info *pi = rs780_get_pi(rdev);
+
+       if (vddc == RS780_VDDC_LEVEL_HIGH)
+               return pi->max_voltage;
+       else if (vddc == RS780_VDDC_LEVEL_LOW)
+               return pi->min_voltage;
+       else
+               return pi->max_voltage;
+}
+
+static void rs780_enable_voltage_scaling(struct radeon_device *rdev,
+                                        struct radeon_ps *new_ps)
+{
+       struct igp_ps *new_state = rs780_get_ps(new_ps);
+       struct igp_power_info *pi = rs780_get_pi(rdev);
+       enum rs780_vddc_level vddc_high, vddc_low;
+
+       udelay(100);
+
+       if ((new_state->max_voltage == RS780_VDDC_LEVEL_HIGH) &&
+           (new_state->min_voltage == RS780_VDDC_LEVEL_HIGH))
+               return;
+
+       vddc_high = rs780_get_voltage_for_vddc_level(rdev,
+                                                    new_state->max_voltage);
+       vddc_low = rs780_get_voltage_for_vddc_level(rdev,
+                                                   new_state->min_voltage);
+
+       WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
+
+       udelay(1);
+       if (vddc_high > vddc_low) {
+               WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1,
+                        RANGE_PWM_FEEDBACK_DIV_EN, ~RANGE_PWM_FEEDBACK_DIV_EN);
+
+               WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~FORCE_STARTING_PWM_HIGHTIME);
+       } else if (vddc_high == vddc_low) {
+               if (pi->max_voltage != vddc_high) {
+                       WREG32_P(FVTHROT_PWM_CTRL_REG0,
+                                STARTING_PWM_HIGHTIME(vddc_high),
+                                ~STARTING_PWM_HIGHTIME_MASK);
+
+                       WREG32_P(FVTHROT_PWM_CTRL_REG0,
+                                FORCE_STARTING_PWM_HIGHTIME,
+                                ~FORCE_STARTING_PWM_HIGHTIME);
+               }
+       }
+
+       WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
+}
+
+static void rs780_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev,
+                                                    struct radeon_ps *new_ps,
+                                                    struct radeon_ps *old_ps)
+{
+       struct igp_ps *new_state = rs780_get_ps(new_ps);
+       struct igp_ps *current_state = rs780_get_ps(old_ps);
+
+       if ((new_ps->vclk == old_ps->vclk) &&
+           (new_ps->dclk == old_ps->dclk))
+               return;
+
+       if (new_state->sclk_high >= current_state->sclk_high)
+               return;
+
+       radeon_set_uvd_clocks(rdev, new_ps->vclk, new_ps->dclk);
+}
+
+static void rs780_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev,
+                                                   struct radeon_ps *new_ps,
+                                                   struct radeon_ps *old_ps)
+{
+       struct igp_ps *new_state = rs780_get_ps(new_ps);
+       struct igp_ps *current_state = rs780_get_ps(old_ps);
+
+       if ((new_ps->vclk == old_ps->vclk) &&
+           (new_ps->dclk == old_ps->dclk))
+               return;
+
+       if (new_state->sclk_high < current_state->sclk_high)
+               return;
+
+       radeon_set_uvd_clocks(rdev, new_ps->vclk, new_ps->dclk);
+}
+
+int rs780_dpm_enable(struct radeon_device *rdev)
+{
+       struct igp_power_info *pi = rs780_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+       int ret;
+
+       rs780_get_pm_mode_parameters(rdev);
+       rs780_disable_vbios_powersaving(rdev);
+
+       if (r600_dynamicpm_enabled(rdev))
+               return -EINVAL;
+       ret = rs780_initialize_dpm_parameters(rdev, boot_ps);
+       if (ret)
+               return ret;
+       rs780_start_dpm(rdev);
+
+       rs780_preset_ranges_slow_clk_fbdiv_en(rdev);
+       rs780_preset_starting_fbdiv(rdev);
+       if (pi->voltage_control)
+               rs780_voltage_scaling_init(rdev);
+       rs780_clk_scaling_enable(rdev, true);
+       rs780_set_engine_clock_sc(rdev);
+       rs780_set_engine_clock_wfc(rdev);
+       rs780_program_at(rdev);
+       rs780_set_engine_clock_tdc(rdev);
+       rs780_set_engine_clock_ssc(rdev);
+
+       if (pi->gfx_clock_gating)
+               r600_gfx_clockgating_enable(rdev, true);
+
+       if (rdev->irq.installed && (rdev->pm.int_thermal_type == THERMAL_TYPE_RV6XX)) {
+               ret = r600_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+               if (ret)
+                       return ret;
+               rdev->irq.dpm_thermal = true;
+               radeon_irq_set(rdev);
+       }
+
+       return 0;
+}
+
+void rs780_dpm_disable(struct radeon_device *rdev)
+{
+       struct igp_power_info *pi = rs780_get_pi(rdev);
+
+       r600_dynamicpm_enable(rdev, false);
+
+       rs780_clk_scaling_enable(rdev, false);
+       rs780_voltage_scaling_enable(rdev, false);
+
+       if (pi->gfx_clock_gating)
+               r600_gfx_clockgating_enable(rdev, false);
+
+       if (rdev->irq.installed &&
+           (rdev->pm.int_thermal_type == THERMAL_TYPE_RV6XX)) {
+               rdev->irq.dpm_thermal = false;
+               radeon_irq_set(rdev);
+       }
+}
+
+int rs780_dpm_set_power_state(struct radeon_device *rdev)
+{
+       struct igp_power_info *pi = rs780_get_pi(rdev);
+       struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps;
+       struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
+       int ret;
+
+       rs780_get_pm_mode_parameters(rdev);
+
+       rs780_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps);
+
+       if (pi->voltage_control) {
+               rs780_force_voltage_to_high(rdev);
+               mdelay(5);
+       }
+
+       ret = rs780_set_engine_clock_scaling(rdev, new_ps, old_ps);
+       if (ret)
+               return ret;
+       rs780_set_engine_clock_spc(rdev, new_ps, old_ps);
+
+       rs780_activate_engine_clk_scaling(rdev, new_ps, old_ps);
+
+       if (pi->voltage_control)
+               rs780_enable_voltage_scaling(rdev, new_ps);
+
+       rs780_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+
+       return 0;
+}
+
+void rs780_dpm_setup_asic(struct radeon_device *rdev)
+{
+
+}
+
+void rs780_dpm_display_configuration_changed(struct radeon_device *rdev)
+{
+       rs780_get_pm_mode_parameters(rdev);
+       rs780_program_at(rdev);
+}
+
+union igp_info {
+       struct _ATOM_INTEGRATED_SYSTEM_INFO info;
+       struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
+};
+
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+       struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+       struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+
+union pplib_clock_info {
+       struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
+       struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
+       struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
+       struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+
+union pplib_power_state {
+       struct _ATOM_PPLIB_STATE v1;
+       struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static void rs780_parse_pplib_non_clock_info(struct radeon_device *rdev,
+                                            struct radeon_ps *rps,
+                                            struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
+                                            u8 table_rev)
+{
+       rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+       rps->class = le16_to_cpu(non_clock_info->usClassification);
+       rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
+
+       if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
+               rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
+               rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
+       } else if (r600_is_uvd_state(rps->class, rps->class2)) {
+               rps->vclk = RS780_DEFAULT_VCLK_FREQ;
+               rps->dclk = RS780_DEFAULT_DCLK_FREQ;
+       } else {
+               rps->vclk = 0;
+               rps->dclk = 0;
+       }
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
+               rdev->pm.dpm.boot_ps = rps;
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+               rdev->pm.dpm.uvd_ps = rps;
+}
+
+static void rs780_parse_pplib_clock_info(struct radeon_device *rdev,
+                                        struct radeon_ps *rps,
+                                        union pplib_clock_info *clock_info)
+{
+       struct igp_ps *ps = rs780_get_ps(rps);
+       u32 sclk;
+
+       sclk = le16_to_cpu(clock_info->rs780.usLowEngineClockLow);
+       sclk |= clock_info->rs780.ucLowEngineClockHigh << 16;
+       ps->sclk_low = sclk;
+       sclk = le16_to_cpu(clock_info->rs780.usHighEngineClockLow);
+       sclk |= clock_info->rs780.ucHighEngineClockHigh << 16;
+       ps->sclk_high = sclk;
+       switch (le16_to_cpu(clock_info->rs780.usVDDC)) {
+       case ATOM_PPLIB_RS780_VOLTAGE_NONE:
+       default:
+               ps->min_voltage = RS780_VDDC_LEVEL_UNKNOWN;
+               ps->max_voltage = RS780_VDDC_LEVEL_UNKNOWN;
+               break;
+       case ATOM_PPLIB_RS780_VOLTAGE_LOW:
+               ps->min_voltage = RS780_VDDC_LEVEL_LOW;
+               ps->max_voltage = RS780_VDDC_LEVEL_LOW;
+               break;
+       case ATOM_PPLIB_RS780_VOLTAGE_HIGH:
+               ps->min_voltage = RS780_VDDC_LEVEL_HIGH;
+               ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
+               break;
+       case ATOM_PPLIB_RS780_VOLTAGE_VARIABLE:
+               ps->min_voltage = RS780_VDDC_LEVEL_LOW;
+               ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
+               break;
+       }
+       ps->flags = le32_to_cpu(clock_info->rs780.ulFlags);
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+               ps->sclk_low = rdev->clock.default_sclk;
+               ps->sclk_high = rdev->clock.default_sclk;
+               ps->min_voltage = RS780_VDDC_LEVEL_HIGH;
+               ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
+       }
+}
+
+static int rs780_parse_power_table(struct radeon_device *rdev)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+       union pplib_power_state *power_state;
+       int i;
+       union pplib_clock_info *clock_info;
+       union power_info *power_info;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+        u16 data_offset;
+       u8 frev, crev;
+       struct igp_ps *ps;
+
+       if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset))
+               return -EINVAL;
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
+                                 power_info->pplib.ucNumStates, GFP_KERNEL);
+       if (!rdev->pm.dpm.ps)
+               return -ENOMEM;
+       rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
+       rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
+       rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
+
+       for (i = 0; i < power_info->pplib.ucNumStates; i++) {
+               power_state = (union pplib_power_state *)
+                       (mode_info->atom_context->bios + data_offset +
+                        le16_to_cpu(power_info->pplib.usStateArrayOffset) +
+                        i * power_info->pplib.ucStateEntrySize);
+               non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+                       (mode_info->atom_context->bios + data_offset +
+                        le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
+                        (power_state->v1.ucNonClockStateIndex *
+                         power_info->pplib.ucNonClockSize));
+               if (power_info->pplib.ucStateEntrySize - 1) {
+                       clock_info = (union pplib_clock_info *)
+                               (mode_info->atom_context->bios + data_offset +
+                                le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
+                                (power_state->v1.ucClockStateIndices[0] *
+                                 power_info->pplib.ucClockInfoSize));
+                       ps = kzalloc(sizeof(struct igp_ps), GFP_KERNEL);
+                       if (ps == NULL) {
+                               kfree(rdev->pm.dpm.ps);
+                               return -ENOMEM;
+                       }
+                       rdev->pm.dpm.ps[i].ps_priv = ps;
+                       rs780_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
+                                                        non_clock_info,
+                                                        power_info->pplib.ucNonClockSize);
+                       rs780_parse_pplib_clock_info(rdev,
+                                                    &rdev->pm.dpm.ps[i],
+                                                    clock_info);
+               }
+       }
+       rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates;
+       return 0;
+}
+
+int rs780_dpm_init(struct radeon_device *rdev)
+{
+       struct igp_power_info *pi;
+       int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
+       union igp_info *info;
+       u16 data_offset;
+       u8 frev, crev;
+       int ret;
+
+       pi = kzalloc(sizeof(struct igp_power_info), GFP_KERNEL);
+       if (pi == NULL)
+               return -ENOMEM;
+       rdev->pm.dpm.priv = pi;
+
+       ret = rs780_parse_power_table(rdev);
+       if (ret)
+               return ret;
+
+       pi->voltage_control = false;
+       pi->gfx_clock_gating = true;
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, NULL,
+                                  &frev, &crev, &data_offset)) {
+               info = (union igp_info *)(rdev->mode_info.atom_context->bios + data_offset);
+
+               /* Get various system informations from bios */
+               switch (crev) {
+               case 1:
+                       pi->num_of_cycles_in_period =
+                               info->info.ucNumberOfCyclesInPeriod;
+                       pi->num_of_cycles_in_period |=
+                               info->info.ucNumberOfCyclesInPeriodHi << 8;
+                       pi->invert_pwm_required =
+                               (pi->num_of_cycles_in_period & 0x8000) ? true : false;
+                       pi->boot_voltage = info->info.ucStartingPWM_HighTime;
+                       pi->max_voltage = info->info.ucMaxNBVoltage;
+                       pi->max_voltage |= info->info.ucMaxNBVoltageHigh << 8;
+                       pi->min_voltage = info->info.ucMinNBVoltage;
+                       pi->min_voltage |= info->info.ucMinNBVoltageHigh << 8;
+                       pi->inter_voltage_low =
+                               le16_to_cpu(info->info.usInterNBVoltageLow);
+                       pi->inter_voltage_high =
+                               le16_to_cpu(info->info.usInterNBVoltageHigh);
+                       pi->voltage_control = true;
+                       pi->bootup_uma_clk = info->info.usK8MemoryClock * 100;
+                       break;
+               case 2:
+                       pi->num_of_cycles_in_period =
+                               le16_to_cpu(info->info_2.usNumberOfCyclesInPeriod);
+                       pi->invert_pwm_required =
+                               (pi->num_of_cycles_in_period & 0x8000) ? true : false;
+                       pi->boot_voltage =
+                               le16_to_cpu(info->info_2.usBootUpNBVoltage);
+                       pi->max_voltage =
+                               le16_to_cpu(info->info_2.usMaxNBVoltage);
+                       pi->min_voltage =
+                               le16_to_cpu(info->info_2.usMinNBVoltage);
+                       pi->system_config =
+                               le32_to_cpu(info->info_2.ulSystemConfig);
+                       pi->pwm_voltage_control =
+                               (pi->system_config & 0x4) ? true : false;
+                       pi->voltage_control = true;
+                       pi->bootup_uma_clk = le32_to_cpu(info->info_2.ulBootUpUMAClock);
+                       break;
+               default:
+                       DRM_ERROR("No integrated system info for your GPU\n");
+                       return -EINVAL;
+               }
+               if (pi->min_voltage > pi->max_voltage)
+                       pi->voltage_control = false;
+               if (pi->pwm_voltage_control) {
+                       if ((pi->num_of_cycles_in_period == 0) ||
+                           (pi->max_voltage == 0) ||
+                           (pi->min_voltage == 0))
+                               pi->voltage_control = false;
+               } else {
+                       if ((pi->num_of_cycles_in_period == 0) ||
+                           (pi->max_voltage == 0))
+                               pi->voltage_control = false;
+               }
+
+               return 0;
+       }
+       radeon_dpm_fini(rdev);
+       return -EINVAL;
+}
+
+void rs780_dpm_print_power_state(struct radeon_device *rdev,
+                                struct radeon_ps *rps)
+{
+       struct igp_ps *ps = rs780_get_ps(rps);
+
+       r600_dpm_print_class_info(rps->class, rps->class2);
+       r600_dpm_print_cap_info(rps->caps);
+       printk("\tuvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+       printk("\t\tpower level 0    sclk: %u vddc_index: %d\n",
+              ps->sclk_low, ps->min_voltage);
+       printk("\t\tpower level 1    sclk: %u vddc_index: %d\n",
+              ps->sclk_high, ps->max_voltage);
+       r600_dpm_print_ps_status(rdev, rps);
+}
+
+void rs780_dpm_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               kfree(rdev->pm.dpm.ps[i].ps_priv);
+       }
+       kfree(rdev->pm.dpm.ps);
+       kfree(rdev->pm.dpm.priv);
+}
+
+u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low)
+{
+       struct igp_ps *requested_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
+
+       if (low)
+               return requested_state->sclk_low;
+       else
+               return requested_state->sclk_high;
+}
+
+u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low)
+{
+       struct igp_power_info *pi = rs780_get_pi(rdev);
+
+       return pi->bootup_uma_clk;
+}
diff --git a/drivers/gpu/drm/radeon/rs780_dpm.h b/drivers/gpu/drm/radeon/rs780_dpm.h
new file mode 100644 (file)
index 0000000..47a40b1
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RS780_DPM_H__
+#define __RS780_DPM_H__
+
+enum rs780_vddc_level {
+       RS780_VDDC_LEVEL_UNKNOWN = 0,
+       RS780_VDDC_LEVEL_LOW = 1,
+       RS780_VDDC_LEVEL_HIGH = 2,
+};
+
+struct igp_power_info {
+       /* flags */
+       bool invert_pwm_required;
+       bool pwm_voltage_control;
+       bool voltage_control;
+       bool gfx_clock_gating;
+       /* stored values */
+       u32 system_config;
+       u32 bootup_uma_clk;
+       u16 max_voltage;
+       u16 min_voltage;
+       u16 boot_voltage;
+       u16 inter_voltage_low;
+       u16 inter_voltage_high;
+       u16 num_of_cycles_in_period;
+       /* variable */
+       int crtc_id;
+       int refresh_rate;
+};
+
+struct igp_ps {
+       enum rs780_vddc_level min_voltage;
+       enum rs780_vddc_level max_voltage;
+       u32 sclk_low;
+       u32 sclk_high;
+       u32 flags;
+};
+
+#define RS780_CGFTV_DFLT                 0x0303000f
+#define RS780_FBDIVTIMERVAL_DFLT         0x2710
+
+#define RS780_FVTHROTUTC0_DFLT   0x04010040
+#define RS780_FVTHROTUTC1_DFLT   0x04010040
+#define RS780_FVTHROTUTC2_DFLT   0x04010040
+#define RS780_FVTHROTUTC3_DFLT   0x04010040
+#define RS780_FVTHROTUTC4_DFLT   0x04010040
+
+#define RS780_FVTHROTDTC0_DFLT 0x04010040
+#define RS780_FVTHROTDTC1_DFLT 0x04010040
+#define RS780_FVTHROTDTC2_DFLT 0x04010040
+#define RS780_FVTHROTDTC3_DFLT 0x04010040
+#define RS780_FVTHROTDTC4_DFLT 0x04010040
+
+#define RS780_FVTHROTFBUSREG0_DFLT       0x00001001
+#define RS780_FVTHROTFBUSREG1_DFLT       0x00002002
+#define RS780_FVTHROTFBDSREG0_DFLT       0x00004001
+#define RS780_FVTHROTFBDSREG1_DFLT       0x00020010
+
+#define RS780_FVTHROTPWMUSREG0_DFLT      0x00002001
+#define RS780_FVTHROTPWMUSREG1_DFLT      0x00004003
+#define RS780_FVTHROTPWMDSREG0_DFLT      0x00002001
+#define RS780_FVTHROTPWMDSREG1_DFLT      0x00004003
+
+#define RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT  0x37
+#define RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT  0x4b
+#define RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT  0x8b
+
+#define RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT  0x8b
+#define RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT  0x8c
+#define RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT  0xb5
+
+#define RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT  0x8d
+#define RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT  0x8e
+#define RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT  0xBa
+
+#define RS780_FVTHROTPWMRANGE0_GPIO_DFLT  0x1a
+#define RS780_FVTHROTPWMRANGE1_GPIO_DFLT  0x1a
+#define RS780_FVTHROTPWMRANGE2_GPIO_DFLT  0x0
+#define RS780_FVTHROTPWMRANGE3_GPIO_DFLT  0x0
+
+#define RS780_SLOWCLKFEEDBACKDIV_DFLT 110
+
+#define RS780_CGCLKGATING_DFLT           0x0000E204
+
+#define RS780_DEFAULT_VCLK_FREQ  53300 /* 10 khz */
+#define RS780_DEFAULT_DCLK_FREQ  40000 /* 10 khz */
+
+#endif
diff --git a/drivers/gpu/drm/radeon/rs780d.h b/drivers/gpu/drm/radeon/rs780d.h
new file mode 100644 (file)
index 0000000..b1142ed
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RS780D_H__
+#define __RS780D_H__
+
+#define CG_SPLL_FUNC_CNTL                                 0x600
+#       define SPLL_RESET                                (1 << 0)
+#       define SPLL_SLEEP                                (1 << 1)
+#       define SPLL_REF_DIV(x)                           ((x) << 2)
+#       define SPLL_REF_DIV_MASK                         (7 << 2)
+#       define SPLL_FB_DIV(x)                            ((x) << 5)
+#       define SPLL_FB_DIV_MASK                          (0xff << 2)
+#       define SPLL_FB_DIV_SHIFT                         2
+#       define SPLL_PULSEEN                              (1 << 13)
+#       define SPLL_PULSENUM(x)                          ((x) << 14)
+#       define SPLL_PULSENUM_MASK                        (3 << 14)
+#       define SPLL_SW_HILEN(x)                          ((x) << 16)
+#       define SPLL_SW_HILEN_MASK                        (0xf << 16)
+#       define SPLL_SW_LOLEN(x)                          ((x) << 20)
+#       define SPLL_SW_LOLEN_MASK                        (0xf << 20)
+#       define SPLL_DIVEN                                (1 << 24)
+#       define SPLL_BYPASS_EN                            (1 << 25)
+#       define SPLL_CHG_STATUS                           (1 << 29)
+#       define SPLL_CTLREQ                               (1 << 30)
+#       define SPLL_CTLACK                               (1 << 31)
+
+/* RS780/RS880 PM */
+#define        FVTHROT_CNTRL_REG                               0x3000
+#define                DONT_WAIT_FOR_FBDIV_WRAP                (1 << 0)
+#define                MINIMUM_CIP(x)                          ((x) << 1)
+#define                MINIMUM_CIP_SHIFT                       1
+#define                MINIMUM_CIP_MASK                        0x1fffffe
+#define                REFRESH_RATE_DIVISOR(x)                 ((x) << 25)
+#define                REFRESH_RATE_DIVISOR_SHIFT              25
+#define                REFRESH_RATE_DIVISOR_MASK               (0x3 << 25)
+#define                ENABLE_FV_THROT                         (1 << 27)
+#define                ENABLE_FV_UPDATE                        (1 << 28)
+#define                TREND_SEL_MODE                          (1 << 29)
+#define                FORCE_TREND_SEL                         (1 << 30)
+#define                ENABLE_FV_THROT_IO                      (1 << 31)
+#define        FVTHROT_TARGET_REG                              0x3004
+#define                TARGET_IDLE_COUNT(x)                    ((x) << 0)
+#define                TARGET_IDLE_COUNT_MASK                  0xffffff
+#define                TARGET_IDLE_COUNT_SHIFT                 0
+#define        FVTHROT_CB1                                     0x3008
+#define        FVTHROT_CB2                                     0x300c
+#define        FVTHROT_CB3                                     0x3010
+#define        FVTHROT_CB4                                     0x3014
+#define        FVTHROT_UTC0                                    0x3018
+#define        FVTHROT_UTC1                                    0x301c
+#define        FVTHROT_UTC2                                    0x3020
+#define        FVTHROT_UTC3                                    0x3024
+#define        FVTHROT_UTC4                                    0x3028
+#define        FVTHROT_DTC0                                    0x302c
+#define        FVTHROT_DTC1                                    0x3030
+#define        FVTHROT_DTC2                                    0x3034
+#define        FVTHROT_DTC3                                    0x3038
+#define        FVTHROT_DTC4                                    0x303c
+#define        FVTHROT_FBDIV_REG0                              0x3040
+#define                MIN_FEEDBACK_DIV(x)                     ((x) << 0)
+#define                MIN_FEEDBACK_DIV_MASK                   0xfff
+#define                MIN_FEEDBACK_DIV_SHIFT                  0
+#define                MAX_FEEDBACK_DIV(x)                     ((x) << 12)
+#define                MAX_FEEDBACK_DIV_MASK                   (0xfff << 12)
+#define                MAX_FEEDBACK_DIV_SHIFT                  12
+#define        FVTHROT_FBDIV_REG1                              0x3044
+#define                MAX_FEEDBACK_STEP(x)                    ((x) << 0)
+#define                MAX_FEEDBACK_STEP_MASK                  0xfff
+#define                MAX_FEEDBACK_STEP_SHIFT                 0
+#define                STARTING_FEEDBACK_DIV(x)                ((x) << 12)
+#define                STARTING_FEEDBACK_DIV_MASK              (0xfff << 12)
+#define                STARTING_FEEDBACK_DIV_SHIFT             12
+#define                FORCE_FEEDBACK_DIV                      (1 << 24)
+#define        FVTHROT_FBDIV_REG2                              0x3048
+#define                FORCED_FEEDBACK_DIV(x)                  ((x) << 0)
+#define                FORCED_FEEDBACK_DIV_MASK                0xfff
+#define                FORCED_FEEDBACK_DIV_SHIFT               0
+#define                FB_DIV_TIMER_VAL(x)                     ((x) << 12)
+#define                FB_DIV_TIMER_VAL_MASK                   (0xffff << 12)
+#define                FB_DIV_TIMER_VAL_SHIFT                  12
+#define        FVTHROT_FB_US_REG0                              0x304c
+#define        FVTHROT_FB_US_REG1                              0x3050
+#define        FVTHROT_FB_DS_REG0                              0x3054
+#define        FVTHROT_FB_DS_REG1                              0x3058
+#define        FVTHROT_PWM_CTRL_REG0                           0x305c
+#define                STARTING_PWM_HIGHTIME(x)                ((x) << 0)
+#define                STARTING_PWM_HIGHTIME_MASK              0xfff
+#define                STARTING_PWM_HIGHTIME_SHIFT             0
+#define                NUMBER_OF_CYCLES_IN_PERIOD(x)           ((x) << 12)
+#define                NUMBER_OF_CYCLES_IN_PERIOD_MASK         (0xfff << 12)
+#define                NUMBER_OF_CYCLES_IN_PERIOD_SHIFT        12
+#define                FORCE_STARTING_PWM_HIGHTIME             (1 << 24)
+#define                INVERT_PWM_WAVEFORM                     (1 << 25)
+#define        FVTHROT_PWM_CTRL_REG1                           0x3060
+#define                MIN_PWM_HIGHTIME(x)                     ((x) << 0)
+#define                MIN_PWM_HIGHTIME_MASK                   0xfff
+#define                MIN_PWM_HIGHTIME_SHIFT                  0
+#define                MAX_PWM_HIGHTIME(x)                     ((x) << 12)
+#define                MAX_PWM_HIGHTIME_MASK                   (0xfff << 12)
+#define                MAX_PWM_HIGHTIME_SHIFT                  12
+#define        FVTHROT_PWM_US_REG0                             0x3064
+#define        FVTHROT_PWM_US_REG1                             0x3068
+#define        FVTHROT_PWM_DS_REG0                             0x306c
+#define        FVTHROT_PWM_DS_REG1                             0x3070
+#define        FVTHROT_STATUS_REG0                             0x3074
+#define                CURRENT_FEEDBACK_DIV_MASK               0xfff
+#define                CURRENT_FEEDBACK_DIV_SHIFT              0
+#define        FVTHROT_STATUS_REG1                             0x3078
+#define        FVTHROT_STATUS_REG2                             0x307c
+#define        CG_INTGFX_MISC                                  0x3080
+#define                FVTHROT_VBLANK_SEL                      (1 << 9)
+#define        FVTHROT_PWM_FEEDBACK_DIV_REG1                   0x308c
+#define                RANGE0_PWM_FEEDBACK_DIV(x)              ((x) << 0)
+#define                RANGE0_PWM_FEEDBACK_DIV_MASK            0xfff
+#define                RANGE0_PWM_FEEDBACK_DIV_SHIFT           0
+#define                RANGE_PWM_FEEDBACK_DIV_EN               (1 << 12)
+#define        FVTHROT_PWM_FEEDBACK_DIV_REG2                   0x3090
+#define                RANGE1_PWM_FEEDBACK_DIV(x)              ((x) << 0)
+#define                RANGE1_PWM_FEEDBACK_DIV_MASK            0xfff
+#define                RANGE1_PWM_FEEDBACK_DIV_SHIFT           0
+#define                RANGE2_PWM_FEEDBACK_DIV(x)              ((x) << 12)
+#define                RANGE2_PWM_FEEDBACK_DIV_MASK            (0xfff << 12)
+#define                RANGE2_PWM_FEEDBACK_DIV_SHIFT           12
+#define        FVTHROT_PWM_FEEDBACK_DIV_REG3                   0x3094
+#define                RANGE0_PWM(x)                           ((x) << 0)
+#define                RANGE0_PWM_MASK                         0xfff
+#define                RANGE0_PWM_SHIFT                        0
+#define                RANGE1_PWM(x)                           ((x) << 12)
+#define                RANGE1_PWM_MASK                         (0xfff << 12)
+#define                RANGE1_PWM_SHIFT                        12
+#define        FVTHROT_PWM_FEEDBACK_DIV_REG4                   0x3098
+#define                RANGE2_PWM(x)                           ((x) << 0)
+#define                RANGE2_PWM_MASK                         0xfff
+#define                RANGE2_PWM_SHIFT                        0
+#define                RANGE3_PWM(x)                           ((x) << 12)
+#define                RANGE3_PWM_MASK                         (0xfff << 12)
+#define                RANGE3_PWM_SHIFT                        12
+#define        FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1              0x30ac
+#define                RANGE0_SLOW_CLK_FEEDBACK_DIV(x)         ((x) << 0)
+#define                RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK       0xfff
+#define                RANGE0_SLOW_CLK_FEEDBACK_DIV_SHIFT      0
+#define                RANGE_SLOW_CLK_FEEDBACK_DIV_EN          (1 << 12)
+
+#define        GFX_MACRO_BYPASS_CNTL                           0x30c0
+#define                SPLL_BYPASS_CNTL                        (1 << 0)
+#define                UPLL_BYPASS_CNTL                        (1 << 1)
+
+#endif
index 21c7d7b..8ea1573 100644 (file)
@@ -937,13 +937,16 @@ struct rv515_watermark {
 };
 
 static void rv515_crtc_bandwidth_compute(struct radeon_device *rdev,
-                                 struct radeon_crtc *crtc,
-                                 struct rv515_watermark *wm)
+                                        struct radeon_crtc *crtc,
+                                        struct rv515_watermark *wm,
+                                        bool low)
 {
        struct drm_display_mode *mode = &crtc->base.mode;
        fixed20_12 a, b, c;
        fixed20_12 pclk, request_fifo_depth, tolerable_latency, estimated_width;
        fixed20_12 consumption_time, line_time, chunk_time, read_delay_latency;
+       fixed20_12 sclk;
+       u32 selected_sclk;
 
        if (!crtc->base.enabled) {
                /* FIXME: wouldn't it better to set priority mark to maximum */
@@ -951,6 +954,18 @@ static void rv515_crtc_bandwidth_compute(struct radeon_device *rdev,
                return;
        }
 
+       /* rv6xx, rv7xx */
+       if ((rdev->family >= CHIP_RV610) &&
+           (rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled)
+               selected_sclk = radeon_dpm_get_sclk(rdev, low);
+       else
+               selected_sclk = rdev->pm.current_sclk;
+
+       /* sclk in Mhz */
+       a.full = dfixed_const(100);
+       sclk.full = dfixed_const(selected_sclk);
+       sclk.full = dfixed_div(sclk, a);
+
        if (crtc->vsc.full > dfixed_const(2))
                wm->num_line_pair.full = dfixed_const(2);
        else
@@ -1016,7 +1031,7 @@ static void rv515_crtc_bandwidth_compute(struct radeon_device *rdev,
         * sclk = system clock(Mhz)
         */
        a.full = dfixed_const(600 * 1000);
-       chunk_time.full = dfixed_div(a, rdev->pm.sclk);
+       chunk_time.full = dfixed_div(a, sclk);
        read_delay_latency.full = dfixed_const(1000);
 
        /* Determine the worst case latency
@@ -1077,152 +1092,177 @@ static void rv515_crtc_bandwidth_compute(struct radeon_device *rdev,
        }
 }
 
-void rv515_bandwidth_avivo_update(struct radeon_device *rdev)
+static void rv515_compute_mode_priority(struct radeon_device *rdev,
+                                       struct rv515_watermark *wm0,
+                                       struct rv515_watermark *wm1,
+                                       struct drm_display_mode *mode0,
+                                       struct drm_display_mode *mode1,
+                                       u32 *d1mode_priority_a_cnt,
+                                       u32 *d2mode_priority_a_cnt)
 {
-       struct drm_display_mode *mode0 = NULL;
-       struct drm_display_mode *mode1 = NULL;
-       struct rv515_watermark wm0;
-       struct rv515_watermark wm1;
-       u32 tmp;
-       u32 d1mode_priority_a_cnt = MODE_PRIORITY_OFF;
-       u32 d2mode_priority_a_cnt = MODE_PRIORITY_OFF;
        fixed20_12 priority_mark02, priority_mark12, fill_rate;
        fixed20_12 a, b;
 
-       if (rdev->mode_info.crtcs[0]->base.enabled)
-               mode0 = &rdev->mode_info.crtcs[0]->base.mode;
-       if (rdev->mode_info.crtcs[1]->base.enabled)
-               mode1 = &rdev->mode_info.crtcs[1]->base.mode;
-       rs690_line_buffer_adjust(rdev, mode0, mode1);
-
-       rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0);
-       rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1);
-
-       tmp = wm0.lb_request_fifo_depth;
-       tmp |= wm1.lb_request_fifo_depth << 16;
-       WREG32(LB_MAX_REQ_OUTSTANDING, tmp);
+       *d1mode_priority_a_cnt = MODE_PRIORITY_OFF;
+       *d2mode_priority_a_cnt = MODE_PRIORITY_OFF;
 
        if (mode0 && mode1) {
-               if (dfixed_trunc(wm0.dbpp) > 64)
-                       a.full = dfixed_div(wm0.dbpp, wm0.num_line_pair);
+               if (dfixed_trunc(wm0->dbpp) > 64)
+                       a.full = dfixed_div(wm0->dbpp, wm0->num_line_pair);
                else
-                       a.full = wm0.num_line_pair.full;
-               if (dfixed_trunc(wm1.dbpp) > 64)
-                       b.full = dfixed_div(wm1.dbpp, wm1.num_line_pair);
+                       a.full = wm0->num_line_pair.full;
+               if (dfixed_trunc(wm1->dbpp) > 64)
+                       b.full = dfixed_div(wm1->dbpp, wm1->num_line_pair);
                else
-                       b.full = wm1.num_line_pair.full;
+                       b.full = wm1->num_line_pair.full;
                a.full += b.full;
-               fill_rate.full = dfixed_div(wm0.sclk, a);
-               if (wm0.consumption_rate.full > fill_rate.full) {
-                       b.full = wm0.consumption_rate.full - fill_rate.full;
-                       b.full = dfixed_mul(b, wm0.active_time);
+               fill_rate.full = dfixed_div(wm0->sclk, a);
+               if (wm0->consumption_rate.full > fill_rate.full) {
+                       b.full = wm0->consumption_rate.full - fill_rate.full;
+                       b.full = dfixed_mul(b, wm0->active_time);
                        a.full = dfixed_const(16);
                        b.full = dfixed_div(b, a);
-                       a.full = dfixed_mul(wm0.worst_case_latency,
-                                               wm0.consumption_rate);
+                       a.full = dfixed_mul(wm0->worst_case_latency,
+                                               wm0->consumption_rate);
                        priority_mark02.full = a.full + b.full;
                } else {
-                       a.full = dfixed_mul(wm0.worst_case_latency,
-                                               wm0.consumption_rate);
+                       a.full = dfixed_mul(wm0->worst_case_latency,
+                                               wm0->consumption_rate);
                        b.full = dfixed_const(16 * 1000);
                        priority_mark02.full = dfixed_div(a, b);
                }
-               if (wm1.consumption_rate.full > fill_rate.full) {
-                       b.full = wm1.consumption_rate.full - fill_rate.full;
-                       b.full = dfixed_mul(b, wm1.active_time);
+               if (wm1->consumption_rate.full > fill_rate.full) {
+                       b.full = wm1->consumption_rate.full - fill_rate.full;
+                       b.full = dfixed_mul(b, wm1->active_time);
                        a.full = dfixed_const(16);
                        b.full = dfixed_div(b, a);
-                       a.full = dfixed_mul(wm1.worst_case_latency,
-                                               wm1.consumption_rate);
+                       a.full = dfixed_mul(wm1->worst_case_latency,
+                                               wm1->consumption_rate);
                        priority_mark12.full = a.full + b.full;
                } else {
-                       a.full = dfixed_mul(wm1.worst_case_latency,
-                                               wm1.consumption_rate);
+                       a.full = dfixed_mul(wm1->worst_case_latency,
+                                               wm1->consumption_rate);
                        b.full = dfixed_const(16 * 1000);
                        priority_mark12.full = dfixed_div(a, b);
                }
-               if (wm0.priority_mark.full > priority_mark02.full)
-                       priority_mark02.full = wm0.priority_mark.full;
+               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 (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);
-               d2mode_priority_a_cnt = dfixed_trunc(priority_mark12);
+               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);
+               *d2mode_priority_a_cnt = dfixed_trunc(priority_mark12);
                if (rdev->disp_priority == 2) {
-                       d1mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON;
-                       d2mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON;
+                       *d1mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON;
+                       *d2mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON;
                }
        } else if (mode0) {
-               if (dfixed_trunc(wm0.dbpp) > 64)
-                       a.full = dfixed_div(wm0.dbpp, wm0.num_line_pair);
+               if (dfixed_trunc(wm0->dbpp) > 64)
+                       a.full = dfixed_div(wm0->dbpp, wm0->num_line_pair);
                else
-                       a.full = wm0.num_line_pair.full;
-               fill_rate.full = dfixed_div(wm0.sclk, a);
-               if (wm0.consumption_rate.full > fill_rate.full) {
-                       b.full = wm0.consumption_rate.full - fill_rate.full;
-                       b.full = dfixed_mul(b, wm0.active_time);
+                       a.full = wm0->num_line_pair.full;
+               fill_rate.full = dfixed_div(wm0->sclk, a);
+               if (wm0->consumption_rate.full > fill_rate.full) {
+                       b.full = wm0->consumption_rate.full - fill_rate.full;
+                       b.full = dfixed_mul(b, wm0->active_time);
                        a.full = dfixed_const(16);
                        b.full = dfixed_div(b, a);
-                       a.full = dfixed_mul(wm0.worst_case_latency,
-                                               wm0.consumption_rate);
+                       a.full = dfixed_mul(wm0->worst_case_latency,
+                                               wm0->consumption_rate);
                        priority_mark02.full = a.full + b.full;
                } else {
-                       a.full = dfixed_mul(wm0.worst_case_latency,
-                                               wm0.consumption_rate);
+                       a.full = dfixed_mul(wm0->worst_case_latency,
+                                               wm0->consumption_rate);
                        b.full = dfixed_const(16);
                        priority_mark02.full = dfixed_div(a, b);
                }
-               if (wm0.priority_mark.full > priority_mark02.full)
-                       priority_mark02.full = wm0.priority_mark.full;
+               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);
+               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);
                if (rdev->disp_priority == 2)
-                       d1mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON;
+                       *d1mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON;
        } else if (mode1) {
-               if (dfixed_trunc(wm1.dbpp) > 64)
-                       a.full = dfixed_div(wm1.dbpp, wm1.num_line_pair);
+               if (dfixed_trunc(wm1->dbpp) > 64)
+                       a.full = dfixed_div(wm1->dbpp, wm1->num_line_pair);
                else
-                       a.full = wm1.num_line_pair.full;
-               fill_rate.full = dfixed_div(wm1.sclk, a);
-               if (wm1.consumption_rate.full > fill_rate.full) {
-                       b.full = wm1.consumption_rate.full - fill_rate.full;
-                       b.full = dfixed_mul(b, wm1.active_time);
+                       a.full = wm1->num_line_pair.full;
+               fill_rate.full = dfixed_div(wm1->sclk, a);
+               if (wm1->consumption_rate.full > fill_rate.full) {
+                       b.full = wm1->consumption_rate.full - fill_rate.full;
+                       b.full = dfixed_mul(b, wm1->active_time);
                        a.full = dfixed_const(16);
                        b.full = dfixed_div(b, a);
-                       a.full = dfixed_mul(wm1.worst_case_latency,
-                                               wm1.consumption_rate);
+                       a.full = dfixed_mul(wm1->worst_case_latency,
+                                               wm1->consumption_rate);
                        priority_mark12.full = a.full + b.full;
                } else {
-                       a.full = dfixed_mul(wm1.worst_case_latency,
-                                               wm1.consumption_rate);
+                       a.full = dfixed_mul(wm1->worst_case_latency,
+                                               wm1->consumption_rate);
                        b.full = dfixed_const(16 * 1000);
                        priority_mark12.full = dfixed_div(a, b);
                }
-               if (wm1.priority_mark.full > priority_mark12.full)
-                       priority_mark12.full = wm1.priority_mark.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;
-               d2mode_priority_a_cnt = dfixed_trunc(priority_mark12);
+               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);
                if (rdev->disp_priority == 2)
-                       d2mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON;
+                       *d2mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON;
        }
+}
+
+void rv515_bandwidth_avivo_update(struct radeon_device *rdev)
+{
+       struct drm_display_mode *mode0 = NULL;
+       struct drm_display_mode *mode1 = NULL;
+       struct rv515_watermark wm0_high, wm0_low;
+       struct rv515_watermark wm1_high, wm1_low;
+       u32 tmp;
+       u32 d1mode_priority_a_cnt, d1mode_priority_b_cnt;
+       u32 d2mode_priority_a_cnt, d2mode_priority_b_cnt;
+
+       if (rdev->mode_info.crtcs[0]->base.enabled)
+               mode0 = &rdev->mode_info.crtcs[0]->base.mode;
+       if (rdev->mode_info.crtcs[1]->base.enabled)
+               mode1 = &rdev->mode_info.crtcs[1]->base.mode;
+       rs690_line_buffer_adjust(rdev, mode0, mode1);
+
+       rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0_high, false);
+       rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1_high, false);
+
+       rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0_low, false);
+       rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1_low, false);
+
+       tmp = wm0_high.lb_request_fifo_depth;
+       tmp |= wm1_high.lb_request_fifo_depth << 16;
+       WREG32(LB_MAX_REQ_OUTSTANDING, tmp);
+
+       rv515_compute_mode_priority(rdev,
+                                   &wm0_high, &wm1_high,
+                                   mode0, mode1,
+                                   &d1mode_priority_a_cnt, &d2mode_priority_a_cnt);
+       rv515_compute_mode_priority(rdev,
+                                   &wm0_low, &wm1_low,
+                                   mode0, mode1,
+                                   &d1mode_priority_b_cnt, &d2mode_priority_b_cnt);
 
        WREG32(D1MODE_PRIORITY_A_CNT, d1mode_priority_a_cnt);
-       WREG32(D1MODE_PRIORITY_B_CNT, d1mode_priority_a_cnt);
+       WREG32(D1MODE_PRIORITY_B_CNT, d1mode_priority_b_cnt);
        WREG32(D2MODE_PRIORITY_A_CNT, d2mode_priority_a_cnt);
-       WREG32(D2MODE_PRIORITY_B_CNT, d2mode_priority_a_cnt);
+       WREG32(D2MODE_PRIORITY_B_CNT, d2mode_priority_b_cnt);
 }
 
 void rv515_bandwidth_update(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c
new file mode 100644 (file)
index 0000000..8303de2
--- /dev/null
@@ -0,0 +1,2085 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "rv6xxd.h"
+#include "r600_dpm.h"
+#include "rv6xx_dpm.h"
+#include "atom.h"
+#include <linux/seq_file.h>
+
+static u32 rv6xx_scale_count_given_unit(struct radeon_device *rdev,
+                                       u32 unscaled_count, u32 unit);
+
+static struct rv6xx_ps *rv6xx_get_ps(struct radeon_ps *rps)
+{
+       struct rv6xx_ps *ps = rps->ps_priv;
+
+       return ps;
+}
+
+static struct rv6xx_power_info *rv6xx_get_pi(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rdev->pm.dpm.priv;
+
+       return pi;
+}
+
+static void rv6xx_force_pcie_gen1(struct radeon_device *rdev)
+{
+       u32 tmp;
+       int i;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+       tmp &= LC_GEN2_EN;
+       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+       tmp |= LC_INITIATE_LINK_SPEED_CHANGE;
+       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (!(RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL) & LC_CURRENT_DATA_RATE))
+                       break;
+               udelay(1);
+       }
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+       tmp &= ~LC_INITIATE_LINK_SPEED_CHANGE;
+       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+}
+
+static void rv6xx_enable_pcie_gen2_support(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+
+       if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) &&
+           (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) {
+               tmp |= LC_GEN2_EN;
+               WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+       }
+}
+
+static void rv6xx_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev,
+                                              bool enable)
+{
+       u32 tmp;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL) & ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
+       if (enable)
+               tmp |= LC_HW_VOLTAGE_IF_CONTROL(1);
+       else
+               tmp |= LC_HW_VOLTAGE_IF_CONTROL(0);
+       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+}
+
+static void rv6xx_enable_l0s(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL) & ~LC_L0S_INACTIVITY_MASK;
+       tmp |= LC_L0S_INACTIVITY(3);
+       WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp);
+}
+
+static void rv6xx_enable_l1(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL);
+       tmp &= ~LC_L1_INACTIVITY_MASK;
+       tmp |= LC_L1_INACTIVITY(4);
+       tmp &= ~LC_PMI_TO_L1_DIS;
+       tmp &= ~LC_ASPM_TO_L1_DIS;
+       WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp);
+}
+
+static void rv6xx_enable_pll_sleep_in_l1(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL) & ~LC_L1_INACTIVITY_MASK;
+       tmp |= LC_L1_INACTIVITY(8);
+       WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp);
+
+       /* NOTE, this is a PCIE indirect reg, not PCIE PORT */
+       tmp = RREG32_PCIE(PCIE_P_CNTL);
+       tmp |= P_PLL_PWRDN_IN_L1L23;
+       tmp &= ~P_PLL_BUF_PDNB;
+       tmp &= ~P_PLL_PDNB;
+       tmp |= P_ALLOW_PRX_FRONTEND_SHUTOFF;
+       WREG32_PCIE(PCIE_P_CNTL, tmp);
+}
+
+static int rv6xx_convert_clock_to_stepping(struct radeon_device *rdev,
+                                          u32 clock, struct rv6xx_sclk_stepping *step)
+{
+       int ret;
+       struct atom_clock_dividers dividers;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       if (dividers.enable_post_div)
+               step->post_divider = 2 + (dividers.post_div & 0xF) + (dividers.post_div >> 4);
+       else
+               step->post_divider = 1;
+
+       step->vco_frequency = clock * step->post_divider;
+
+       return 0;
+}
+
+static void rv6xx_output_stepping(struct radeon_device *rdev,
+                                 u32 step_index, struct rv6xx_sclk_stepping *step)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       u32 ref_clk = rdev->clock.spll.reference_freq;
+       u32 fb_divider;
+       u32 spll_step_count = rv6xx_scale_count_given_unit(rdev,
+                                                          R600_SPLLSTEPTIME_DFLT *
+                                                          pi->spll_ref_div,
+                                                          R600_SPLLSTEPUNIT_DFLT);
+
+       r600_engine_clock_entry_enable(rdev, step_index, true);
+       r600_engine_clock_entry_enable_pulse_skipping(rdev, step_index, false);
+
+       if (step->post_divider == 1)
+               r600_engine_clock_entry_enable_post_divider(rdev, step_index, false);
+       else {
+               u32 lo_len = (step->post_divider - 2) / 2;
+               u32 hi_len = step->post_divider - 2 - lo_len;
+
+               r600_engine_clock_entry_enable_post_divider(rdev, step_index, true);
+               r600_engine_clock_entry_set_post_divider(rdev, step_index, (hi_len << 4) | lo_len);
+       }
+
+       fb_divider = ((step->vco_frequency * pi->spll_ref_div) / ref_clk) >>
+               pi->fb_div_scale;
+
+       r600_engine_clock_entry_set_reference_divider(rdev, step_index,
+                                                     pi->spll_ref_div - 1);
+       r600_engine_clock_entry_set_feedback_divider(rdev, step_index, fb_divider);
+       r600_engine_clock_entry_set_step_time(rdev, step_index, spll_step_count);
+
+}
+
+static struct rv6xx_sclk_stepping rv6xx_next_vco_step(struct radeon_device *rdev,
+                                                     struct rv6xx_sclk_stepping *cur,
+                                                     bool increasing_vco, u32 step_size)
+{
+       struct rv6xx_sclk_stepping next;
+
+       next.post_divider = cur->post_divider;
+
+       if (increasing_vco)
+               next.vco_frequency = (cur->vco_frequency * (100 + step_size)) / 100;
+       else
+               next.vco_frequency = (cur->vco_frequency * 100 + 99 + step_size) / (100 + step_size);
+
+       return next;
+}
+
+static bool rv6xx_can_step_post_div(struct radeon_device *rdev,
+                                   struct rv6xx_sclk_stepping *cur,
+                                    struct rv6xx_sclk_stepping *target)
+{
+       return (cur->post_divider > target->post_divider) &&
+               ((cur->vco_frequency * target->post_divider) <=
+                (target->vco_frequency * (cur->post_divider - 1)));
+}
+
+static struct rv6xx_sclk_stepping rv6xx_next_post_div_step(struct radeon_device *rdev,
+                                                          struct rv6xx_sclk_stepping *cur,
+                                                          struct rv6xx_sclk_stepping *target)
+{
+       struct rv6xx_sclk_stepping next = *cur;
+
+       while (rv6xx_can_step_post_div(rdev, &next, target))
+               next.post_divider--;
+
+       return next;
+}
+
+static bool rv6xx_reached_stepping_target(struct radeon_device *rdev,
+                                         struct rv6xx_sclk_stepping *cur,
+                                         struct rv6xx_sclk_stepping *target,
+                                         bool increasing_vco)
+{
+       return (increasing_vco && (cur->vco_frequency >= target->vco_frequency)) ||
+               (!increasing_vco && (cur->vco_frequency <= target->vco_frequency));
+}
+
+static void rv6xx_generate_steps(struct radeon_device *rdev,
+                                u32 low, u32 high,
+                                 u32 start_index, u8 *end_index)
+{
+       struct rv6xx_sclk_stepping cur;
+       struct rv6xx_sclk_stepping target;
+       bool increasing_vco;
+       u32 step_index = start_index;
+
+       rv6xx_convert_clock_to_stepping(rdev, low, &cur);
+       rv6xx_convert_clock_to_stepping(rdev, high, &target);
+
+       rv6xx_output_stepping(rdev, step_index++, &cur);
+
+       increasing_vco = (target.vco_frequency >= cur.vco_frequency);
+
+       if (target.post_divider > cur.post_divider)
+               cur.post_divider = target.post_divider;
+
+       while (1) {
+               struct rv6xx_sclk_stepping next;
+
+               if (rv6xx_can_step_post_div(rdev, &cur, &target))
+                       next = rv6xx_next_post_div_step(rdev, &cur, &target);
+               else
+                       next = rv6xx_next_vco_step(rdev, &cur, increasing_vco, R600_VCOSTEPPCT_DFLT);
+
+               if (rv6xx_reached_stepping_target(rdev, &next, &target, increasing_vco)) {
+                       struct rv6xx_sclk_stepping tiny =
+                               rv6xx_next_vco_step(rdev, &target, !increasing_vco, R600_ENDINGVCOSTEPPCT_DFLT);
+                       tiny.post_divider = next.post_divider;
+
+                       if (!rv6xx_reached_stepping_target(rdev, &tiny, &cur, !increasing_vco))
+                               rv6xx_output_stepping(rdev, step_index++, &tiny);
+
+                       if ((next.post_divider != target.post_divider) &&
+                           (next.vco_frequency != target.vco_frequency)) {
+                               struct rv6xx_sclk_stepping final_vco;
+
+                               final_vco.vco_frequency = target.vco_frequency;
+                               final_vco.post_divider = next.post_divider;
+
+                               rv6xx_output_stepping(rdev, step_index++, &final_vco);
+                       }
+
+                       rv6xx_output_stepping(rdev, step_index++, &target);
+                       break;
+               } else
+                       rv6xx_output_stepping(rdev, step_index++, &next);
+
+               cur = next;
+       }
+
+       *end_index = (u8)step_index - 1;
+
+}
+
+static void rv6xx_generate_single_step(struct radeon_device *rdev,
+                                      u32 clock, u32 index)
+{
+       struct rv6xx_sclk_stepping step;
+
+       rv6xx_convert_clock_to_stepping(rdev, clock, &step);
+       rv6xx_output_stepping(rdev, index, &step);
+}
+
+static void rv6xx_invalidate_intermediate_steps_range(struct radeon_device *rdev,
+                                                     u32 start_index, u32 end_index)
+{
+       u32 step_index;
+
+       for (step_index = start_index + 1; step_index < end_index; step_index++)
+               r600_engine_clock_entry_enable(rdev, step_index, false);
+}
+
+static void rv6xx_set_engine_spread_spectrum_clk_s(struct radeon_device *rdev,
+                                                  u32 index, u32 clk_s)
+{
+       WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4),
+                CLKS(clk_s), ~CLKS_MASK);
+}
+
+static void rv6xx_set_engine_spread_spectrum_clk_v(struct radeon_device *rdev,
+                                                  u32 index, u32 clk_v)
+{
+       WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4),
+                CLKV(clk_v), ~CLKV_MASK);
+}
+
+static void rv6xx_enable_engine_spread_spectrum(struct radeon_device *rdev,
+                                               u32 index, bool enable)
+{
+       if (enable)
+               WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4),
+                        SSEN, ~SSEN);
+       else
+               WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4),
+                        0, ~SSEN);
+}
+
+static void rv6xx_set_memory_spread_spectrum_clk_s(struct radeon_device *rdev,
+                                                  u32 clk_s)
+{
+       WREG32_P(CG_MPLL_SPREAD_SPECTRUM, CLKS(clk_s), ~CLKS_MASK);
+}
+
+static void rv6xx_set_memory_spread_spectrum_clk_v(struct radeon_device *rdev,
+                                                  u32 clk_v)
+{
+       WREG32_P(CG_MPLL_SPREAD_SPECTRUM, CLKV(clk_v), ~CLKV_MASK);
+}
+
+static void rv6xx_enable_memory_spread_spectrum(struct radeon_device *rdev,
+                                               bool enable)
+{
+       if (enable)
+               WREG32_P(CG_MPLL_SPREAD_SPECTRUM, SSEN, ~SSEN);
+       else
+               WREG32_P(CG_MPLL_SPREAD_SPECTRUM, 0, ~SSEN);
+}
+
+static void rv6xx_enable_dynamic_spread_spectrum(struct radeon_device *rdev,
+                                                bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, ~DYN_SPREAD_SPECTRUM_EN);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~DYN_SPREAD_SPECTRUM_EN);
+}
+
+static void rv6xx_memory_clock_entry_enable_post_divider(struct radeon_device *rdev,
+                                                        u32 index, bool enable)
+{
+       if (enable)
+               WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4),
+                        LEVEL0_MPLL_DIV_EN, ~LEVEL0_MPLL_DIV_EN);
+       else
+               WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4), 0, ~LEVEL0_MPLL_DIV_EN);
+}
+
+static void rv6xx_memory_clock_entry_set_post_divider(struct radeon_device *rdev,
+                                                     u32 index, u32 divider)
+{
+       WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4),
+                LEVEL0_MPLL_POST_DIV(divider), ~LEVEL0_MPLL_POST_DIV_MASK);
+}
+
+static void rv6xx_memory_clock_entry_set_feedback_divider(struct radeon_device *rdev,
+                                                         u32 index, u32 divider)
+{
+       WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4), LEVEL0_MPLL_FB_DIV(divider),
+                ~LEVEL0_MPLL_FB_DIV_MASK);
+}
+
+static void rv6xx_memory_clock_entry_set_reference_divider(struct radeon_device *rdev,
+                                                          u32 index, u32 divider)
+{
+       WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4),
+                LEVEL0_MPLL_REF_DIV(divider), ~LEVEL0_MPLL_REF_DIV_MASK);
+}
+
+static void rv6xx_vid_response_set_brt(struct radeon_device *rdev, u32 rt)
+{
+       WREG32_P(VID_RT, BRT(rt), ~BRT_MASK);
+}
+
+static void rv6xx_enable_engine_feedback_and_reference_sync(struct radeon_device *rdev)
+{
+       WREG32_P(SPLL_CNTL_MODE, SPLL_DIV_SYNC, ~SPLL_DIV_SYNC);
+}
+
+static u64 rv6xx_clocks_per_unit(u32 unit)
+{
+       u64 tmp = 1 << (2 * unit);
+
+       return tmp;
+}
+
+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);
+
+       return (unscaled_count + count_per_unit - 1) / count_per_unit;
+}
+
+static u32 rv6xx_compute_count_for_delay(struct radeon_device *rdev,
+                                        u32 delay_us, u32 unit)
+{
+       u32 ref_clk = rdev->clock.spll.reference_freq;
+
+       return rv6xx_scale_count_given_unit(rdev, delay_us * (ref_clk / 100), unit);
+}
+
+static void rv6xx_calculate_engine_speed_stepping_parameters(struct radeon_device *rdev,
+                                                            struct rv6xx_ps *state)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       pi->hw.sclks[R600_POWER_LEVEL_LOW] =
+               state->low.sclk;
+       pi->hw.sclks[R600_POWER_LEVEL_MEDIUM] =
+               state->medium.sclk;
+       pi->hw.sclks[R600_POWER_LEVEL_HIGH] =
+               state->high.sclk;
+
+       pi->hw.low_sclk_index = R600_POWER_LEVEL_LOW;
+       pi->hw.medium_sclk_index = R600_POWER_LEVEL_MEDIUM;
+       pi->hw.high_sclk_index = R600_POWER_LEVEL_HIGH;
+}
+
+static void rv6xx_calculate_memory_clock_stepping_parameters(struct radeon_device *rdev,
+                                                            struct rv6xx_ps *state)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       pi->hw.mclks[R600_POWER_LEVEL_CTXSW] =
+               state->high.mclk;
+       pi->hw.mclks[R600_POWER_LEVEL_HIGH] =
+               state->high.mclk;
+       pi->hw.mclks[R600_POWER_LEVEL_MEDIUM] =
+               state->medium.mclk;
+       pi->hw.mclks[R600_POWER_LEVEL_LOW] =
+               state->low.mclk;
+
+       pi->hw.high_mclk_index = R600_POWER_LEVEL_HIGH;
+
+       if (state->high.mclk == state->medium.mclk)
+               pi->hw.medium_mclk_index =
+                       pi->hw.high_mclk_index;
+       else
+               pi->hw.medium_mclk_index = R600_POWER_LEVEL_MEDIUM;
+
+
+       if (state->medium.mclk == state->low.mclk)
+               pi->hw.low_mclk_index =
+                       pi->hw.medium_mclk_index;
+       else
+               pi->hw.low_mclk_index = R600_POWER_LEVEL_LOW;
+}
+
+static void rv6xx_calculate_voltage_stepping_parameters(struct radeon_device *rdev,
+                                                       struct rv6xx_ps *state)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       pi->hw.vddc[R600_POWER_LEVEL_CTXSW] = state->high.vddc;
+       pi->hw.vddc[R600_POWER_LEVEL_HIGH] = state->high.vddc;
+       pi->hw.vddc[R600_POWER_LEVEL_MEDIUM] = state->medium.vddc;
+       pi->hw.vddc[R600_POWER_LEVEL_LOW] = state->low.vddc;
+
+       pi->hw.backbias[R600_POWER_LEVEL_CTXSW] =
+               (state->high.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false;
+       pi->hw.backbias[R600_POWER_LEVEL_HIGH] =
+               (state->high.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false;
+       pi->hw.backbias[R600_POWER_LEVEL_MEDIUM] =
+               (state->medium.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false;
+       pi->hw.backbias[R600_POWER_LEVEL_LOW] =
+               (state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false;
+
+       pi->hw.pcie_gen2[R600_POWER_LEVEL_HIGH] =
+               (state->high.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? true : false;
+       pi->hw.pcie_gen2[R600_POWER_LEVEL_MEDIUM] =
+               (state->medium.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? true : false;
+       pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW] =
+               (state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? true : false;
+
+       pi->hw.high_vddc_index = R600_POWER_LEVEL_HIGH;
+
+       if ((state->high.vddc == state->medium.vddc) &&
+           ((state->high.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ==
+            (state->medium.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE)))
+               pi->hw.medium_vddc_index =
+                       pi->hw.high_vddc_index;
+       else
+               pi->hw.medium_vddc_index = R600_POWER_LEVEL_MEDIUM;
+
+       if ((state->medium.vddc == state->low.vddc) &&
+           ((state->medium.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ==
+            (state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE)))
+               pi->hw.low_vddc_index =
+                       pi->hw.medium_vddc_index;
+       else
+               pi->hw.medium_vddc_index = R600_POWER_LEVEL_LOW;
+}
+
+static inline u32 rv6xx_calculate_vco_frequency(u32 ref_clock,
+                                               struct atom_clock_dividers *dividers,
+                                               u32 fb_divider_scale)
+{
+       return ref_clock * ((dividers->fb_div & ~1) << fb_divider_scale) /
+               (dividers->ref_div + 1);
+}
+
+static inline u32 rv6xx_calculate_spread_spectrum_clk_v(u32 vco_freq, u32 ref_freq,
+                                                       u32 ss_rate, u32 ss_percent,
+                                                       u32 fb_divider_scale)
+{
+       u32 fb_divider = vco_freq / ref_freq;
+
+       return (ss_percent * ss_rate * 4 * (fb_divider * fb_divider) /
+               (5375 * ((vco_freq * 10) / (4096 >> fb_divider_scale))));
+}
+
+static inline u32 rv6xx_calculate_spread_spectrum_clk_s(u32 ss_rate, u32 ref_freq)
+{
+       return (((ref_freq * 10) / (ss_rate * 2)) - 1) / 4;
+}
+
+static void rv6xx_program_engine_spread_spectrum(struct radeon_device *rdev,
+                                                u32 clock, enum r600_power_level level)
+{
+       u32 ref_clk = rdev->clock.spll.reference_freq;
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       struct atom_clock_dividers dividers;
+       struct radeon_atom_ss ss;
+       u32 vco_freq, clk_v, clk_s;
+
+       rv6xx_enable_engine_spread_spectrum(rdev, level, false);
+
+       if (clock && pi->sclk_ss) {
+               if (radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, clock, false, &dividers) == 0) {
+                       vco_freq = rv6xx_calculate_vco_frequency(ref_clk, &dividers,
+                                                                pi->fb_div_scale);
+
+                       if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                            ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
+                               clk_v = rv6xx_calculate_spread_spectrum_clk_v(vco_freq,
+                                                                             (ref_clk / (dividers.ref_div + 1)),
+                                                                             ss.rate,
+                                                                             ss.percentage,
+                                                                             pi->fb_div_scale);
+
+                               clk_s = rv6xx_calculate_spread_spectrum_clk_s(ss.rate,
+                                                                             (ref_clk / (dividers.ref_div + 1)));
+
+                               rv6xx_set_engine_spread_spectrum_clk_v(rdev, level, clk_v);
+                               rv6xx_set_engine_spread_spectrum_clk_s(rdev, level, clk_s);
+                               rv6xx_enable_engine_spread_spectrum(rdev, level, true);
+                       }
+               }
+       }
+}
+
+static void rv6xx_program_sclk_spread_spectrum_parameters_except_lowest_entry(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       rv6xx_program_engine_spread_spectrum(rdev,
+                                            pi->hw.sclks[R600_POWER_LEVEL_HIGH],
+                                            R600_POWER_LEVEL_HIGH);
+
+       rv6xx_program_engine_spread_spectrum(rdev,
+                                            pi->hw.sclks[R600_POWER_LEVEL_MEDIUM],
+                                            R600_POWER_LEVEL_MEDIUM);
+
+}
+
+static int rv6xx_program_mclk_stepping_entry(struct radeon_device *rdev,
+                                            u32 entry, u32 clock)
+{
+       struct atom_clock_dividers dividers;
+
+       if (radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM, clock, false, &dividers))
+           return -EINVAL;
+
+
+       rv6xx_memory_clock_entry_set_reference_divider(rdev, entry, dividers.ref_div);
+       rv6xx_memory_clock_entry_set_feedback_divider(rdev, entry, dividers.fb_div);
+       rv6xx_memory_clock_entry_set_post_divider(rdev, entry, dividers.post_div);
+
+       if (dividers.enable_post_div)
+               rv6xx_memory_clock_entry_enable_post_divider(rdev, entry, true);
+       else
+               rv6xx_memory_clock_entry_enable_post_divider(rdev, entry, false);
+
+       return 0;
+}
+
+static void rv6xx_program_mclk_stepping_parameters_except_lowest_entry(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       int i;
+
+       for (i = 1; i < R600_PM_NUMBER_OF_MCLKS; i++) {
+               if (pi->hw.mclks[i])
+                       rv6xx_program_mclk_stepping_entry(rdev, i,
+                                                         pi->hw.mclks[i]);
+       }
+}
+
+static void rv6xx_find_memory_clock_with_highest_vco(struct radeon_device *rdev,
+                                                    u32 requested_memory_clock,
+                                                    u32 ref_clk,
+                                                    struct atom_clock_dividers *dividers,
+                                                    u32 *vco_freq)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       struct atom_clock_dividers req_dividers;
+       u32 vco_freq_temp;
+
+       if (radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM,
+                                          requested_memory_clock, false, &req_dividers) == 0) {
+               vco_freq_temp = rv6xx_calculate_vco_frequency(ref_clk, &req_dividers,
+                                                             pi->fb_div_scale);
+
+               if (vco_freq_temp > *vco_freq) {
+                       *dividers = req_dividers;
+                       *vco_freq = vco_freq_temp;
+               }
+       }
+}
+
+static void rv6xx_program_mclk_spread_spectrum_parameters(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       u32 ref_clk = rdev->clock.mpll.reference_freq;
+       struct atom_clock_dividers dividers;
+       struct radeon_atom_ss ss;
+       u32 vco_freq = 0, clk_v, clk_s;
+
+       rv6xx_enable_memory_spread_spectrum(rdev, false);
+
+       if (pi->mclk_ss) {
+               rv6xx_find_memory_clock_with_highest_vco(rdev,
+                                                        pi->hw.mclks[pi->hw.high_mclk_index],
+                                                        ref_clk,
+                                                        &dividers,
+                                                        &vco_freq);
+
+               rv6xx_find_memory_clock_with_highest_vco(rdev,
+                                                        pi->hw.mclks[pi->hw.medium_mclk_index],
+                                                        ref_clk,
+                                                        &dividers,
+                                                        &vco_freq);
+
+               rv6xx_find_memory_clock_with_highest_vco(rdev,
+                                                        pi->hw.mclks[pi->hw.low_mclk_index],
+                                                        ref_clk,
+                                                        &dividers,
+                                                        &vco_freq);
+
+               if (vco_freq) {
+                       if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                            ASIC_INTERNAL_MEMORY_SS, vco_freq)) {
+                               clk_v = rv6xx_calculate_spread_spectrum_clk_v(vco_freq,
+                                                                            (ref_clk / (dividers.ref_div + 1)),
+                                                                            ss.rate,
+                                                                            ss.percentage,
+                                                                            pi->fb_div_scale);
+
+                               clk_s = rv6xx_calculate_spread_spectrum_clk_s(ss.rate,
+                                                                            (ref_clk / (dividers.ref_div + 1)));
+
+                               rv6xx_set_memory_spread_spectrum_clk_v(rdev, clk_v);
+                               rv6xx_set_memory_spread_spectrum_clk_s(rdev, clk_s);
+                               rv6xx_enable_memory_spread_spectrum(rdev, true);
+                       }
+               }
+       }
+}
+
+static int rv6xx_program_voltage_stepping_entry(struct radeon_device *rdev,
+                                               u32 entry, u16 voltage)
+{
+       u32 mask, set_pins;
+       int ret;
+
+       ret = radeon_atom_get_voltage_gpio_settings(rdev, voltage,
+                                                   SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                                   &set_pins, &mask);
+       if (ret)
+               return ret;
+
+       r600_voltage_control_program_voltages(rdev, entry, set_pins);
+
+       return 0;
+}
+
+static void rv6xx_program_voltage_stepping_parameters_except_lowest_entry(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       int i;
+
+       for (i = 1; i < R600_PM_NUMBER_OF_VOLTAGE_LEVELS; i++)
+               rv6xx_program_voltage_stepping_entry(rdev, i,
+                                                    pi->hw.vddc[i]);
+
+}
+
+static void rv6xx_program_backbias_stepping_parameters_except_lowest_entry(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       if (pi->hw.backbias[1])
+               WREG32_P(VID_UPPER_GPIO_CNTL, MEDIUM_BACKBIAS_VALUE, ~MEDIUM_BACKBIAS_VALUE);
+       else
+               WREG32_P(VID_UPPER_GPIO_CNTL, 0, ~MEDIUM_BACKBIAS_VALUE);
+
+       if (pi->hw.backbias[2])
+               WREG32_P(VID_UPPER_GPIO_CNTL, HIGH_BACKBIAS_VALUE, ~HIGH_BACKBIAS_VALUE);
+       else
+               WREG32_P(VID_UPPER_GPIO_CNTL, 0, ~HIGH_BACKBIAS_VALUE);
+}
+
+static void rv6xx_program_sclk_spread_spectrum_parameters_lowest_entry(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       rv6xx_program_engine_spread_spectrum(rdev,
+                                            pi->hw.sclks[R600_POWER_LEVEL_LOW],
+                                            R600_POWER_LEVEL_LOW);
+}
+
+static void rv6xx_program_mclk_stepping_parameters_lowest_entry(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       if (pi->hw.mclks[0])
+               rv6xx_program_mclk_stepping_entry(rdev, 0,
+                                                 pi->hw.mclks[0]);
+}
+
+static void rv6xx_program_voltage_stepping_parameters_lowest_entry(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       rv6xx_program_voltage_stepping_entry(rdev, 0,
+                                            pi->hw.vddc[0]);
+
+}
+
+static void rv6xx_program_backbias_stepping_parameters_lowest_entry(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       if (pi->hw.backbias[0])
+               WREG32_P(VID_UPPER_GPIO_CNTL, LOW_BACKBIAS_VALUE, ~LOW_BACKBIAS_VALUE);
+       else
+               WREG32_P(VID_UPPER_GPIO_CNTL, 0, ~LOW_BACKBIAS_VALUE);
+}
+
+static u32 calculate_memory_refresh_rate(struct radeon_device *rdev,
+                                        u32 engine_clock)
+{
+       u32 dram_rows, dram_refresh_rate;
+       u32 tmp;
+
+       tmp = (RREG32(RAMCFG) & NOOFROWS_MASK) >> NOOFROWS_SHIFT;
+       dram_rows = 1 << (tmp + 10);
+       dram_refresh_rate = 1 << ((RREG32(MC_SEQ_RESERVE_M) & 0x3) + 3);
+
+       return ((engine_clock * 10) * dram_refresh_rate / dram_rows - 32) / 64;
+}
+
+static void rv6xx_program_memory_timing_parameters(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       u32 sqm_ratio;
+       u32 arb_refresh_rate;
+       u32 high_clock;
+
+       if (pi->hw.sclks[R600_POWER_LEVEL_HIGH] <
+           (pi->hw.sclks[R600_POWER_LEVEL_LOW] * 0xFF / 0x40))
+               high_clock = pi->hw.sclks[R600_POWER_LEVEL_HIGH];
+       else
+               high_clock =
+                       pi->hw.sclks[R600_POWER_LEVEL_LOW] * 0xFF / 0x40;
+
+       radeon_atom_set_engine_dram_timings(rdev, high_clock, 0);
+
+       sqm_ratio = (STATE0(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_LOW]) |
+                    STATE1(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_MEDIUM]) |
+                    STATE2(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_HIGH]) |
+                    STATE3(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_HIGH]));
+       WREG32(SQM_RATIO, sqm_ratio);
+
+       arb_refresh_rate =
+               (POWERMODE0(calculate_memory_refresh_rate(rdev,
+                                                         pi->hw.sclks[R600_POWER_LEVEL_LOW])) |
+                POWERMODE1(calculate_memory_refresh_rate(rdev,
+                                                         pi->hw.sclks[R600_POWER_LEVEL_MEDIUM])) |
+                POWERMODE2(calculate_memory_refresh_rate(rdev,
+                                                         pi->hw.sclks[R600_POWER_LEVEL_MEDIUM])) |
+                POWERMODE3(calculate_memory_refresh_rate(rdev,
+                                                         pi->hw.sclks[R600_POWER_LEVEL_HIGH])));
+       WREG32(ARB_RFSH_RATE, arb_refresh_rate);
+}
+
+static void rv6xx_program_mpll_timing_parameters(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       r600_set_mpll_lock_time(rdev, R600_MPLLLOCKTIME_DFLT *
+                               pi->mpll_ref_div);
+       r600_set_mpll_reset_time(rdev, R600_MPLLRESETTIME_DFLT);
+}
+
+static void rv6xx_program_bsp(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       u32 ref_clk = rdev->clock.spll.reference_freq;
+
+       r600_calculate_u_and_p(R600_ASI_DFLT,
+                              ref_clk, 16,
+                              &pi->bsp,
+                              &pi->bsu);
+
+       r600_set_bsp(rdev, pi->bsu, pi->bsp);
+}
+
+static void rv6xx_program_at(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       r600_set_at(rdev,
+                   (pi->hw.rp[0] * pi->bsp) / 200,
+                   (pi->hw.rp[1] * pi->bsp) / 200,
+                   (pi->hw.lp[2] * pi->bsp) / 200,
+                   (pi->hw.lp[1] * pi->bsp) / 200);
+}
+
+static void rv6xx_program_git(struct radeon_device *rdev)
+{
+       r600_set_git(rdev, R600_GICST_DFLT);
+}
+
+static void rv6xx_program_tp(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
+               r600_set_tc(rdev, i, r600_utc[i], r600_dtc[i]);
+
+       r600_select_td(rdev, R600_TD_DFLT);
+}
+
+static void rv6xx_program_vc(struct radeon_device *rdev)
+{
+       r600_set_vrc(rdev, R600_VRC_DFLT);
+}
+
+static void rv6xx_clear_vc(struct radeon_device *rdev)
+{
+       r600_set_vrc(rdev, 0);
+}
+
+static void rv6xx_program_tpp(struct radeon_device *rdev)
+{
+       r600_set_tpu(rdev, R600_TPU_DFLT);
+       r600_set_tpc(rdev, R600_TPC_DFLT);
+}
+
+static void rv6xx_program_sstp(struct radeon_device *rdev)
+{
+       r600_set_sstu(rdev, R600_SSTU_DFLT);
+       r600_set_sst(rdev, R600_SST_DFLT);
+}
+
+static void rv6xx_program_fcp(struct radeon_device *rdev)
+{
+       r600_set_fctu(rdev, R600_FCTU_DFLT);
+       r600_set_fct(rdev, R600_FCT_DFLT);
+}
+
+static void rv6xx_program_vddc3d_parameters(struct radeon_device *rdev)
+{
+       r600_set_vddc3d_oorsu(rdev, R600_VDDC3DOORSU_DFLT);
+       r600_set_vddc3d_oorphc(rdev, R600_VDDC3DOORPHC_DFLT);
+       r600_set_vddc3d_oorsdc(rdev, R600_VDDC3DOORSDC_DFLT);
+       r600_set_ctxcgtt3d_rphc(rdev, R600_CTXCGTT3DRPHC_DFLT);
+       r600_set_ctxcgtt3d_rsdc(rdev, R600_CTXCGTT3DRSDC_DFLT);
+}
+
+static void rv6xx_program_voltage_timing_parameters(struct radeon_device *rdev)
+{
+       u32 rt;
+
+       r600_vid_rt_set_vru(rdev, R600_VRU_DFLT);
+
+       r600_vid_rt_set_vrt(rdev,
+                           rv6xx_compute_count_for_delay(rdev,
+                                                         rdev->pm.dpm.voltage_response_time,
+                                                         R600_VRU_DFLT));
+
+       rt = rv6xx_compute_count_for_delay(rdev,
+                                          rdev->pm.dpm.backbias_response_time,
+                                          R600_VRU_DFLT);
+
+       rv6xx_vid_response_set_brt(rdev, (rt + 0x1F) >> 5);
+}
+
+static void rv6xx_program_engine_speed_parameters(struct radeon_device *rdev)
+{
+       r600_vid_rt_set_ssu(rdev, R600_SPLLSTEPUNIT_DFLT);
+       rv6xx_enable_engine_feedback_and_reference_sync(rdev);
+}
+
+static u64 rv6xx_get_master_voltage_mask(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       u64 master_mask = 0;
+       int i;
+
+       for (i = 0; i < R600_PM_NUMBER_OF_VOLTAGE_LEVELS; i++) {
+               u32 tmp_mask, tmp_set_pins;
+               int ret;
+
+               ret = radeon_atom_get_voltage_gpio_settings(rdev,
+                                                           pi->hw.vddc[i],
+                                                           SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                                           &tmp_set_pins, &tmp_mask);
+
+               if (ret == 0)
+                       master_mask |= tmp_mask;
+       }
+
+       return master_mask;
+}
+
+static void rv6xx_program_voltage_gpio_pins(struct radeon_device *rdev)
+{
+       r600_voltage_control_enable_pins(rdev,
+                                        rv6xx_get_master_voltage_mask(rdev));
+}
+
+static void rv6xx_enable_static_voltage_control(struct radeon_device *rdev,
+                                               struct radeon_ps *new_ps,
+                                               bool enable)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+
+       if (enable)
+               radeon_atom_set_voltage(rdev,
+                                       new_state->low.vddc,
+                                       SET_VOLTAGE_TYPE_ASIC_VDDC);
+       else
+               r600_voltage_control_deactivate_static_control(rdev,
+                                                              rv6xx_get_master_voltage_mask(rdev));
+}
+
+static void rv6xx_enable_display_gap(struct radeon_device *rdev, bool enable)
+{
+       if (enable) {
+               u32 tmp = (DISP1_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM) |
+                          DISP2_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM) |
+                          DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE) |
+                          DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE) |
+                          VBI_TIMER_COUNT(0x3FFF) |
+                          VBI_TIMER_UNIT(7));
+               WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+
+               WREG32_P(MCLK_PWRMGT_CNTL, USE_DISPLAY_GAP, ~USE_DISPLAY_GAP);
+       } else
+               WREG32_P(MCLK_PWRMGT_CNTL, 0, ~USE_DISPLAY_GAP);
+}
+
+static void rv6xx_program_power_level_enter_state(struct radeon_device *rdev)
+{
+       r600_power_level_set_enter_index(rdev, R600_POWER_LEVEL_MEDIUM);
+}
+
+static void rv6xx_calculate_t(u32 l_f, u32 h_f, int h,
+                             int d_l, int d_r, u8 *l, u8 *r)
+{
+       int a_n, a_d, h_r, l_r;
+
+       h_r = d_l;
+       l_r = 100 - d_r;
+
+       a_n = (int)h_f * d_l + (int)l_f * (h - d_r);
+       a_d = (int)l_f * l_r + (int)h_f * h_r;
+
+       if (a_d != 0) {
+               *l = d_l - h_r * a_n / a_d;
+               *r = d_r + l_r * a_n / a_d;
+       }
+}
+
+static void rv6xx_calculate_ap(struct radeon_device *rdev,
+                              struct rv6xx_ps *state)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       pi->hw.lp[0] = 0;
+       pi->hw.rp[R600_PM_NUMBER_OF_ACTIVITY_LEVELS - 1]
+               = 100;
+
+       rv6xx_calculate_t(state->low.sclk,
+                         state->medium.sclk,
+                         R600_AH_DFLT,
+                         R600_LMP_DFLT,
+                         R600_RLP_DFLT,
+                         &pi->hw.lp[1],
+                         &pi->hw.rp[0]);
+
+       rv6xx_calculate_t(state->medium.sclk,
+                         state->high.sclk,
+                         R600_AH_DFLT,
+                         R600_LHP_DFLT,
+                         R600_RMP_DFLT,
+                         &pi->hw.lp[2],
+                         &pi->hw.rp[1]);
+
+}
+
+static void rv6xx_calculate_stepping_parameters(struct radeon_device *rdev,
+                                               struct radeon_ps *new_ps)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+
+       rv6xx_calculate_engine_speed_stepping_parameters(rdev, new_state);
+       rv6xx_calculate_memory_clock_stepping_parameters(rdev, new_state);
+       rv6xx_calculate_voltage_stepping_parameters(rdev, new_state);
+       rv6xx_calculate_ap(rdev, new_state);
+}
+
+static void rv6xx_program_stepping_parameters_except_lowest_entry(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       rv6xx_program_mclk_stepping_parameters_except_lowest_entry(rdev);
+       if (pi->voltage_control)
+               rv6xx_program_voltage_stepping_parameters_except_lowest_entry(rdev);
+       rv6xx_program_backbias_stepping_parameters_except_lowest_entry(rdev);
+       rv6xx_program_sclk_spread_spectrum_parameters_except_lowest_entry(rdev);
+       rv6xx_program_mclk_spread_spectrum_parameters(rdev);
+       rv6xx_program_memory_timing_parameters(rdev);
+}
+
+static void rv6xx_program_stepping_parameters_lowest_entry(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       rv6xx_program_mclk_stepping_parameters_lowest_entry(rdev);
+       if (pi->voltage_control)
+               rv6xx_program_voltage_stepping_parameters_lowest_entry(rdev);
+       rv6xx_program_backbias_stepping_parameters_lowest_entry(rdev);
+       rv6xx_program_sclk_spread_spectrum_parameters_lowest_entry(rdev);
+}
+
+static void rv6xx_program_power_level_low(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW,
+                                          pi->hw.low_vddc_index);
+       r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW,
+                                            pi->hw.low_mclk_index);
+       r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW,
+                                            pi->hw.low_sclk_index);
+       r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW,
+                                         R600_DISPLAY_WATERMARK_LOW);
+       r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_LOW,
+                                      pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW]);
+}
+
+static void rv6xx_program_power_level_low_to_lowest_state(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW, 0);
+       r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
+       r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
+
+       r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW,
+                                         R600_DISPLAY_WATERMARK_LOW);
+
+       r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_LOW,
+                                      pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW]);
+
+}
+
+static void rv6xx_program_power_level_medium(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM,
+                                         pi->hw.medium_vddc_index);
+       r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM,
+                                           pi->hw.medium_mclk_index);
+       r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM,
+                                           pi->hw.medium_sclk_index);
+       r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM,
+                                        R600_DISPLAY_WATERMARK_LOW);
+       r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_MEDIUM,
+                                     pi->hw.pcie_gen2[R600_POWER_LEVEL_MEDIUM]);
+}
+
+static void rv6xx_program_power_level_medium_for_transition(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       rv6xx_program_mclk_stepping_entry(rdev,
+                                         R600_POWER_LEVEL_CTXSW,
+                                         pi->hw.mclks[pi->hw.low_mclk_index]);
+
+       r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM, 1);
+
+       r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM,
+                                            R600_POWER_LEVEL_CTXSW);
+       r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM,
+                                            pi->hw.medium_sclk_index);
+
+       r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM,
+                                         R600_DISPLAY_WATERMARK_LOW);
+
+       rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_MEDIUM, false);
+
+       r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_MEDIUM,
+                                      pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW]);
+}
+
+static void rv6xx_program_power_level_high(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_HIGH,
+                                          pi->hw.high_vddc_index);
+       r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_HIGH,
+                                            pi->hw.high_mclk_index);
+       r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_HIGH,
+                                            pi->hw.high_sclk_index);
+
+       r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_HIGH,
+                                         R600_DISPLAY_WATERMARK_HIGH);
+
+       r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_HIGH,
+                                      pi->hw.pcie_gen2[R600_POWER_LEVEL_HIGH]);
+}
+
+static void rv6xx_enable_backbias(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, BACKBIAS_PAD_EN | BACKBIAS_DPM_CNTL,
+                        ~(BACKBIAS_PAD_EN | BACKBIAS_DPM_CNTL));
+       else
+               WREG32_P(GENERAL_PWRMGT, 0,
+                        ~(BACKBIAS_VALUE | BACKBIAS_PAD_EN | BACKBIAS_DPM_CNTL));
+}
+
+static void rv6xx_program_display_gap(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL);
+
+       tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK);
+       if (RREG32(AVIVO_D1CRTC_CONTROL) & AVIVO_CRTC_EN) {
+               tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK);
+               tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
+       } else if (RREG32(AVIVO_D2CRTC_CONTROL) & AVIVO_CRTC_EN) {
+               tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
+               tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK);
+       } else {
+               tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
+               tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
+       }
+       WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+}
+
+static void rv6xx_set_sw_voltage_to_safe(struct radeon_device *rdev,
+                                        struct radeon_ps *new_ps,
+                                        struct radeon_ps *old_ps)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+       struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
+       u16 safe_voltage;
+
+       safe_voltage = (new_state->low.vddc >= old_state->low.vddc) ?
+               new_state->low.vddc : old_state->low.vddc;
+
+       rv6xx_program_voltage_stepping_entry(rdev, R600_POWER_LEVEL_CTXSW,
+                                            safe_voltage);
+
+       WREG32_P(GENERAL_PWRMGT, SW_GPIO_INDEX(R600_POWER_LEVEL_CTXSW),
+                ~SW_GPIO_INDEX_MASK);
+}
+
+static void rv6xx_set_sw_voltage_to_low(struct radeon_device *rdev,
+                                       struct radeon_ps *old_ps)
+{
+       struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
+
+       rv6xx_program_voltage_stepping_entry(rdev, R600_POWER_LEVEL_CTXSW,
+                                            old_state->low.vddc);
+
+       WREG32_P(GENERAL_PWRMGT, SW_GPIO_INDEX(R600_POWER_LEVEL_CTXSW),
+               ~SW_GPIO_INDEX_MASK);
+}
+
+static void rv6xx_set_safe_backbias(struct radeon_device *rdev,
+                                   struct radeon_ps *new_ps,
+                                   struct radeon_ps *old_ps)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+       struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
+
+       if ((new_state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) &&
+           (old_state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE))
+               WREG32_P(GENERAL_PWRMGT, BACKBIAS_VALUE, ~BACKBIAS_VALUE);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~BACKBIAS_VALUE);
+}
+
+static void rv6xx_set_safe_pcie_gen2(struct radeon_device *rdev,
+                                    struct radeon_ps *new_ps,
+                                    struct radeon_ps *old_ps)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+       struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
+
+       if ((new_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) !=
+           (old_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2))
+               rv6xx_force_pcie_gen1(rdev);
+}
+
+static void rv6xx_enable_dynamic_voltage_control(struct radeon_device *rdev,
+                                                bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, VOLT_PWRMGT_EN, ~VOLT_PWRMGT_EN);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~VOLT_PWRMGT_EN);
+}
+
+static void rv6xx_enable_dynamic_backbias_control(struct radeon_device *rdev,
+                                                 bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, BACKBIAS_DPM_CNTL, ~BACKBIAS_DPM_CNTL);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~BACKBIAS_DPM_CNTL);
+}
+
+static int rv6xx_step_sw_voltage(struct radeon_device *rdev,
+                                u16 initial_voltage,
+                                u16 target_voltage)
+{
+       u16 current_voltage;
+       u16 true_target_voltage;
+       u16 voltage_step;
+       int signed_voltage_step;
+
+       if ((radeon_atom_get_voltage_step(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                         &voltage_step)) ||
+           (radeon_atom_round_to_true_voltage(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                              initial_voltage, &current_voltage)) ||
+           (radeon_atom_round_to_true_voltage(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                              target_voltage, &true_target_voltage)))
+               return -EINVAL;
+
+       if (true_target_voltage < current_voltage)
+               signed_voltage_step = -(int)voltage_step;
+       else
+               signed_voltage_step = voltage_step;
+
+       while (current_voltage != true_target_voltage) {
+               current_voltage += signed_voltage_step;
+               rv6xx_program_voltage_stepping_entry(rdev, R600_POWER_LEVEL_CTXSW,
+                                                    current_voltage);
+               msleep((rdev->pm.dpm.voltage_response_time + 999) / 1000);
+       }
+
+       return 0;
+}
+
+static int rv6xx_step_voltage_if_increasing(struct radeon_device *rdev,
+                                           struct radeon_ps *new_ps,
+                                           struct radeon_ps *old_ps)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+       struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
+
+       if (new_state->low.vddc > old_state->low.vddc)
+               return rv6xx_step_sw_voltage(rdev,
+                                            old_state->low.vddc,
+                                            new_state->low.vddc);
+
+       return 0;
+}
+
+static int rv6xx_step_voltage_if_decreasing(struct radeon_device *rdev,
+                                           struct radeon_ps *new_ps,
+                                           struct radeon_ps *old_ps)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+       struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
+
+       if (new_state->low.vddc < old_state->low.vddc)
+               return rv6xx_step_sw_voltage(rdev,
+                                            old_state->low.vddc,
+                                            new_state->low.vddc);
+       else
+               return 0;
+}
+
+static void rv6xx_enable_high(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       if ((pi->restricted_levels < 1) ||
+           (pi->restricted_levels == 3))
+               r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, true);
+}
+
+static void rv6xx_enable_medium(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       if (pi->restricted_levels < 2)
+               r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true);
+}
+
+static void rv6xx_set_dpm_event_sources(struct radeon_device *rdev, u32 sources)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       bool want_thermal_protection;
+       enum radeon_dpm_event_src dpm_event_src;
+
+       switch (sources) {
+        case 0:
+        default:
+               want_thermal_protection = false;
+               break;
+        case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_DIGITAL;
+               break;
+
+        case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_EXTERNAL;
+               break;
+
+        case ((1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL) |
+             (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL)):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL;
+               break;
+       }
+
+       if (want_thermal_protection) {
+               WREG32_P(CG_THERMAL_CTRL, DPM_EVENT_SRC(dpm_event_src), ~DPM_EVENT_SRC_MASK);
+               if (pi->thermal_protection)
+                       WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
+       } else {
+               WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
+       }
+}
+
+static void rv6xx_enable_auto_throttle_source(struct radeon_device *rdev,
+                                             enum radeon_dpm_auto_throttle_src source,
+                                             bool enable)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       if (enable) {
+               if (!(pi->active_auto_throttle_sources & (1 << source))) {
+                       pi->active_auto_throttle_sources |= 1 << source;
+                       rv6xx_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
+               }
+       } else {
+               if (pi->active_auto_throttle_sources & (1 << source)) {
+                       pi->active_auto_throttle_sources &= ~(1 << source);
+                       rv6xx_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
+               }
+       }
+}
+
+
+static void rv6xx_enable_thermal_protection(struct radeon_device *rdev,
+                                           bool enable)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       if (pi->active_auto_throttle_sources)
+               r600_enable_thermal_protection(rdev, enable);
+}
+
+static void rv6xx_generate_transition_stepping(struct radeon_device *rdev,
+                                              struct radeon_ps *new_ps,
+                                              struct radeon_ps *old_ps)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+       struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       rv6xx_generate_steps(rdev,
+                            old_state->low.sclk,
+                            new_state->low.sclk,
+                            0, &pi->hw.medium_sclk_index);
+}
+
+static void rv6xx_generate_low_step(struct radeon_device *rdev,
+                                   struct radeon_ps *new_ps)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       pi->hw.low_sclk_index = 0;
+       rv6xx_generate_single_step(rdev,
+                                  new_state->low.sclk,
+                                  0);
+}
+
+static void rv6xx_invalidate_intermediate_steps(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       rv6xx_invalidate_intermediate_steps_range(rdev, 0,
+                                                 pi->hw.medium_sclk_index);
+}
+
+static void rv6xx_generate_stepping_table(struct radeon_device *rdev,
+                                         struct radeon_ps *new_ps)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+
+       pi->hw.low_sclk_index = 0;
+
+       rv6xx_generate_steps(rdev,
+                            new_state->low.sclk,
+                            new_state->medium.sclk,
+                            0,
+                            &pi->hw.medium_sclk_index);
+       rv6xx_generate_steps(rdev,
+                            new_state->medium.sclk,
+                            new_state->high.sclk,
+                            pi->hw.medium_sclk_index,
+                            &pi->hw.high_sclk_index);
+}
+
+static void rv6xx_enable_spread_spectrum(struct radeon_device *rdev,
+                                        bool enable)
+{
+       if (enable)
+               rv6xx_enable_dynamic_spread_spectrum(rdev, true);
+       else {
+               rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_LOW, false);
+               rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_MEDIUM, false);
+               rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_HIGH, false);
+               rv6xx_enable_dynamic_spread_spectrum(rdev, false);
+               rv6xx_enable_memory_spread_spectrum(rdev, false);
+       }
+}
+
+static void rv6xx_reset_lvtm_data_sync(struct radeon_device *rdev)
+{
+       if (ASIC_IS_DCE3(rdev))
+               WREG32_P(DCE3_LVTMA_DATA_SYNCHRONIZATION, LVTMA_PFREQCHG, ~LVTMA_PFREQCHG);
+       else
+               WREG32_P(LVTMA_DATA_SYNCHRONIZATION, LVTMA_PFREQCHG, ~LVTMA_PFREQCHG);
+}
+
+static void rv6xx_enable_dynamic_pcie_gen2(struct radeon_device *rdev,
+                                          struct radeon_ps *new_ps,
+                                          bool enable)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+
+       if (enable) {
+               rv6xx_enable_bif_dynamic_pcie_gen2(rdev, true);
+               rv6xx_enable_pcie_gen2_support(rdev);
+               r600_enable_dynamic_pcie_gen2(rdev, true);
+       } else {
+               if (!(new_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2))
+                       rv6xx_force_pcie_gen1(rdev);
+               rv6xx_enable_bif_dynamic_pcie_gen2(rdev, false);
+               r600_enable_dynamic_pcie_gen2(rdev, false);
+       }
+}
+
+static void rv6xx_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev,
+                                                    struct radeon_ps *new_ps,
+                                                    struct radeon_ps *old_ps)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+       struct rv6xx_ps *current_state = rv6xx_get_ps(old_ps);
+
+       if ((new_ps->vclk == old_ps->vclk) &&
+           (new_ps->dclk == old_ps->dclk))
+               return;
+
+       if (new_state->high.sclk >= current_state->high.sclk)
+               return;
+
+       radeon_set_uvd_clocks(rdev, new_ps->vclk, new_ps->dclk);
+}
+
+static void rv6xx_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev,
+                                                   struct radeon_ps *new_ps,
+                                                   struct radeon_ps *old_ps)
+{
+       struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
+       struct rv6xx_ps *current_state = rv6xx_get_ps(old_ps);
+
+       if ((new_ps->vclk == old_ps->vclk) &&
+           (new_ps->dclk == old_ps->dclk))
+               return;
+
+       if (new_state->high.sclk < current_state->high.sclk)
+               return;
+
+       radeon_set_uvd_clocks(rdev, new_ps->vclk, new_ps->dclk);
+}
+
+int rv6xx_dpm_enable(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+       int ret;
+
+       if (r600_dynamicpm_enabled(rdev))
+               return -EINVAL;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
+               rv6xx_enable_backbias(rdev, true);
+
+       if (pi->dynamic_ss)
+               rv6xx_enable_spread_spectrum(rdev, true);
+
+       rv6xx_program_mpll_timing_parameters(rdev);
+       rv6xx_program_bsp(rdev);
+       rv6xx_program_git(rdev);
+       rv6xx_program_tp(rdev);
+       rv6xx_program_tpp(rdev);
+       rv6xx_program_sstp(rdev);
+       rv6xx_program_fcp(rdev);
+       rv6xx_program_vddc3d_parameters(rdev);
+       rv6xx_program_voltage_timing_parameters(rdev);
+       rv6xx_program_engine_speed_parameters(rdev);
+
+       rv6xx_enable_display_gap(rdev, true);
+       if (pi->display_gap == false)
+               rv6xx_enable_display_gap(rdev, false);
+
+       rv6xx_program_power_level_enter_state(rdev);
+
+       rv6xx_calculate_stepping_parameters(rdev, boot_ps);
+
+       if (pi->voltage_control)
+               rv6xx_program_voltage_gpio_pins(rdev);
+
+       rv6xx_generate_stepping_table(rdev, boot_ps);
+
+       rv6xx_program_stepping_parameters_except_lowest_entry(rdev);
+       rv6xx_program_stepping_parameters_lowest_entry(rdev);
+
+       rv6xx_program_power_level_low(rdev);
+       rv6xx_program_power_level_medium(rdev);
+       rv6xx_program_power_level_high(rdev);
+       rv6xx_program_vc(rdev);
+       rv6xx_program_at(rdev);
+
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, true);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               ret = r600_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+               if (ret)
+                       return ret;
+               rdev->irq.dpm_thermal = true;
+               radeon_irq_set(rdev);
+       }
+
+       rv6xx_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+
+       r600_start_dpm(rdev);
+
+       if (pi->voltage_control)
+               rv6xx_enable_static_voltage_control(rdev, boot_ps, false);
+
+       if (pi->dynamic_pcie_gen2)
+               rv6xx_enable_dynamic_pcie_gen2(rdev, boot_ps, true);
+
+       if (pi->gfx_clock_gating)
+               r600_gfx_clockgating_enable(rdev, true);
+
+       return 0;
+}
+
+void rv6xx_dpm_disable(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+
+       if (!r600_dynamicpm_enabled(rdev))
+               return;
+
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true);
+       rv6xx_enable_display_gap(rdev, false);
+       rv6xx_clear_vc(rdev);
+       r600_set_at(rdev, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
+
+       if (pi->thermal_protection)
+               r600_enable_thermal_protection(rdev, false);
+
+       r600_wait_for_power_level(rdev, R600_POWER_LEVEL_LOW);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
+               rv6xx_enable_backbias(rdev, false);
+
+       rv6xx_enable_spread_spectrum(rdev, false);
+
+       if (pi->voltage_control)
+               rv6xx_enable_static_voltage_control(rdev, boot_ps, true);
+
+       if (pi->dynamic_pcie_gen2)
+               rv6xx_enable_dynamic_pcie_gen2(rdev, boot_ps, false);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               rdev->irq.dpm_thermal = false;
+               radeon_irq_set(rdev);
+       }
+
+       if (pi->gfx_clock_gating)
+               r600_gfx_clockgating_enable(rdev, false);
+
+       r600_stop_dpm(rdev);
+}
+
+int rv6xx_dpm_set_power_state(struct radeon_device *rdev)
+{
+       struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
+       struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps;
+       struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
+       int ret;
+
+       rv6xx_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps);
+
+       rv6xx_clear_vc(rdev);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
+       r600_set_at(rdev, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
+
+       if (pi->thermal_protection)
+               r600_enable_thermal_protection(rdev, false);
+
+       r600_wait_for_power_level(rdev, R600_POWER_LEVEL_LOW);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
+
+       rv6xx_generate_transition_stepping(rdev, new_ps, old_ps);
+       rv6xx_program_power_level_medium_for_transition(rdev);
+
+       if (pi->voltage_control) {
+               rv6xx_set_sw_voltage_to_safe(rdev, new_ps, old_ps);
+               if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
+                       rv6xx_set_sw_voltage_to_low(rdev, old_ps);
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
+               rv6xx_set_safe_backbias(rdev, new_ps, old_ps);
+
+       if (pi->dynamic_pcie_gen2)
+               rv6xx_set_safe_pcie_gen2(rdev, new_ps, old_ps);
+
+       if (pi->voltage_control)
+               rv6xx_enable_dynamic_voltage_control(rdev, false);
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
+               rv6xx_enable_dynamic_backbias_control(rdev, false);
+
+       if (pi->voltage_control) {
+               if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
+                       rv6xx_step_voltage_if_increasing(rdev, new_ps, old_ps);
+               msleep((rdev->pm.dpm.voltage_response_time + 999) / 1000);
+       }
+
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, false);
+       r600_wait_for_power_level_unequal(rdev, R600_POWER_LEVEL_LOW);
+
+       rv6xx_generate_low_step(rdev, new_ps);
+       rv6xx_invalidate_intermediate_steps(rdev);
+       rv6xx_calculate_stepping_parameters(rdev, new_ps);
+       rv6xx_program_stepping_parameters_lowest_entry(rdev);
+       rv6xx_program_power_level_low_to_lowest_state(rdev);
+
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
+       r600_wait_for_power_level(rdev, R600_POWER_LEVEL_LOW);
+       r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
+
+       if (pi->voltage_control) {
+               if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) {
+                       ret = rv6xx_step_voltage_if_decreasing(rdev, new_ps, old_ps);
+                       if (ret)
+                               return ret;
+               }
+               rv6xx_enable_dynamic_voltage_control(rdev, true);
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
+               rv6xx_enable_dynamic_backbias_control(rdev, true);
+
+       if (pi->dynamic_pcie_gen2)
+               rv6xx_enable_dynamic_pcie_gen2(rdev, new_ps, true);
+
+       rv6xx_reset_lvtm_data_sync(rdev);
+
+       rv6xx_generate_stepping_table(rdev, new_ps);
+       rv6xx_program_stepping_parameters_except_lowest_entry(rdev);
+       rv6xx_program_power_level_low(rdev);
+       rv6xx_program_power_level_medium(rdev);
+       rv6xx_program_power_level_high(rdev);
+       rv6xx_enable_medium(rdev);
+       rv6xx_enable_high(rdev);
+
+       if (pi->thermal_protection)
+               rv6xx_enable_thermal_protection(rdev, true);
+       rv6xx_program_vc(rdev);
+       rv6xx_program_at(rdev);
+
+       rv6xx_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+
+       return 0;
+}
+
+void rv6xx_setup_asic(struct radeon_device *rdev)
+{
+       r600_enable_acpi_pm(rdev);
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_ASPM_L0s)
+               rv6xx_enable_l0s(rdev);
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_ASPM_L1)
+               rv6xx_enable_l1(rdev);
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1)
+               rv6xx_enable_pll_sleep_in_l1(rdev);
+}
+
+void rv6xx_dpm_display_configuration_changed(struct radeon_device *rdev)
+{
+       rv6xx_program_display_gap(rdev);
+}
+
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+       struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+       struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+
+union pplib_clock_info {
+       struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
+       struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
+       struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
+       struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+
+union pplib_power_state {
+       struct _ATOM_PPLIB_STATE v1;
+       struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static void rv6xx_parse_pplib_non_clock_info(struct radeon_device *rdev,
+                                            struct radeon_ps *rps,
+                                            struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info)
+{
+       rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+       rps->class = le16_to_cpu(non_clock_info->usClassification);
+       rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
+
+       if (r600_is_uvd_state(rps->class, rps->class2)) {
+               rps->vclk = RV6XX_DEFAULT_VCLK_FREQ;
+               rps->dclk = RV6XX_DEFAULT_DCLK_FREQ;
+       } else {
+               rps->vclk = 0;
+               rps->dclk = 0;
+       }
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
+               rdev->pm.dpm.boot_ps = rps;
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+               rdev->pm.dpm.uvd_ps = rps;
+}
+
+static void rv6xx_parse_pplib_clock_info(struct radeon_device *rdev,
+                                        struct radeon_ps *rps, int index,
+                                        union pplib_clock_info *clock_info)
+{
+       struct rv6xx_ps *ps = rv6xx_get_ps(rps);
+       u32 sclk, mclk;
+       u16 vddc;
+       struct rv6xx_pl *pl;
+
+       switch (index) {
+       case 0:
+               pl = &ps->low;
+               break;
+       case 1:
+               pl = &ps->medium;
+               break;
+       case 2:
+       default:
+               pl = &ps->high;
+               break;
+       }
+
+       sclk = le16_to_cpu(clock_info->r600.usEngineClockLow);
+       sclk |= clock_info->r600.ucEngineClockHigh << 16;
+       mclk = le16_to_cpu(clock_info->r600.usMemoryClockLow);
+       mclk |= clock_info->r600.ucMemoryClockHigh << 16;
+
+       pl->mclk = mclk;
+       pl->sclk = sclk;
+       pl->vddc = le16_to_cpu(clock_info->r600.usVDDC);
+       pl->flags = le32_to_cpu(clock_info->r600.ulFlags);
+
+       /* patch up vddc if necessary */
+       if (pl->vddc == 0xff01) {
+               if (radeon_atom_get_max_vddc(rdev, 0, 0, &vddc) == 0)
+                       pl->vddc = vddc;
+       }
+
+       /* fix up pcie gen2 */
+       if (pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) {
+               if ((rdev->family == CHIP_RV610) || (rdev->family == CHIP_RV630)) {
+                       if (pl->vddc < 1100)
+                               pl->flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2;
+               }
+       }
+
+       /* patch up boot state */
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+               u16 vddc, vddci, mvdd;
+               radeon_atombios_get_default_voltages(rdev, &vddc, &vddci, &mvdd);
+               pl->mclk = rdev->clock.default_mclk;
+               pl->sclk = rdev->clock.default_sclk;
+               pl->vddc = vddc;
+       }
+}
+
+static int rv6xx_parse_power_table(struct radeon_device *rdev)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+       union pplib_power_state *power_state;
+       int i, j;
+       union pplib_clock_info *clock_info;
+       union power_info *power_info;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+        u16 data_offset;
+       u8 frev, crev;
+       struct rv6xx_ps *ps;
+
+       if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset))
+               return -EINVAL;
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
+                                 power_info->pplib.ucNumStates, GFP_KERNEL);
+       if (!rdev->pm.dpm.ps)
+               return -ENOMEM;
+       rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
+       rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
+       rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
+
+       for (i = 0; i < power_info->pplib.ucNumStates; i++) {
+               power_state = (union pplib_power_state *)
+                       (mode_info->atom_context->bios + data_offset +
+                        le16_to_cpu(power_info->pplib.usStateArrayOffset) +
+                        i * power_info->pplib.ucStateEntrySize);
+               non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+                       (mode_info->atom_context->bios + data_offset +
+                        le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
+                        (power_state->v1.ucNonClockStateIndex *
+                         power_info->pplib.ucNonClockSize));
+               if (power_info->pplib.ucStateEntrySize - 1) {
+                       ps = kzalloc(sizeof(struct rv6xx_ps), GFP_KERNEL);
+                       if (ps == NULL) {
+                               kfree(rdev->pm.dpm.ps);
+                               return -ENOMEM;
+                       }
+                       rdev->pm.dpm.ps[i].ps_priv = ps;
+                       rv6xx_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
+                                                        non_clock_info);
+                       for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) {
+                               clock_info = (union pplib_clock_info *)
+                                       (mode_info->atom_context->bios + data_offset +
+                                        le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
+                                        (power_state->v1.ucClockStateIndices[j] *
+                                         power_info->pplib.ucClockInfoSize));
+                               rv6xx_parse_pplib_clock_info(rdev,
+                                                            &rdev->pm.dpm.ps[i], j,
+                                                            clock_info);
+                       }
+               }
+       }
+       rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates;
+       return 0;
+}
+
+int rv6xx_dpm_init(struct radeon_device *rdev)
+{
+       int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
+       uint16_t data_offset, size;
+       uint8_t frev, crev;
+       struct atom_clock_dividers dividers;
+       struct rv6xx_power_info *pi;
+       int ret;
+
+       pi = kzalloc(sizeof(struct rv6xx_power_info), GFP_KERNEL);
+       if (pi == NULL)
+               return -ENOMEM;
+       rdev->pm.dpm.priv = pi;
+
+       ret = rv6xx_parse_power_table(rdev);
+       if (ret)
+               return ret;
+
+       if (rdev->pm.dpm.voltage_response_time == 0)
+               rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT;
+       if (rdev->pm.dpm.backbias_response_time == 0)
+               rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            0, false, &dividers);
+       if (ret)
+               pi->spll_ref_div = dividers.ref_div + 1;
+       else
+               pi->spll_ref_div = R600_REFERENCEDIVIDER_DFLT;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM,
+                                            0, false, &dividers);
+       if (ret)
+               pi->mpll_ref_div = dividers.ref_div + 1;
+       else
+               pi->mpll_ref_div = R600_REFERENCEDIVIDER_DFLT;
+
+       if (rdev->family >= CHIP_RV670)
+               pi->fb_div_scale = 1;
+       else
+               pi->fb_div_scale = 0;
+
+       pi->voltage_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, 0);
+
+       pi->gfx_clock_gating = true;
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                   &frev, &crev, &data_offset)) {
+               pi->sclk_ss = true;
+               pi->mclk_ss = true;
+               pi->dynamic_ss = true;
+       } else {
+               pi->sclk_ss = false;
+               pi->mclk_ss = false;
+               pi->dynamic_ss = false;
+       }
+
+       pi->dynamic_pcie_gen2 = true;
+
+       if (pi->gfx_clock_gating &&
+           (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE))
+               pi->thermal_protection = true;
+       else
+               pi->thermal_protection = false;
+
+       pi->display_gap = true;
+
+       return 0;
+}
+
+void rv6xx_dpm_print_power_state(struct radeon_device *rdev,
+                                struct radeon_ps *rps)
+{
+       struct rv6xx_ps *ps = rv6xx_get_ps(rps);
+       struct rv6xx_pl *pl;
+
+       r600_dpm_print_class_info(rps->class, rps->class2);
+       r600_dpm_print_cap_info(rps->caps);
+       printk("\tuvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+       pl = &ps->low;
+       printk("\t\tpower level 0    sclk: %u mclk: %u vddc: %u\n",
+              pl->sclk, pl->mclk, pl->vddc);
+       pl = &ps->medium;
+       printk("\t\tpower level 1    sclk: %u mclk: %u vddc: %u\n",
+              pl->sclk, pl->mclk, pl->vddc);
+       pl = &ps->high;
+       printk("\t\tpower level 2    sclk: %u mclk: %u vddc: %u\n",
+              pl->sclk, pl->mclk, pl->vddc);
+       r600_dpm_print_ps_status(rdev, rps);
+}
+
+void rv6xx_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                      struct seq_file *m)
+{
+       struct radeon_ps *rps = rdev->pm.dpm.current_ps;
+       struct rv6xx_ps *ps = rv6xx_get_ps(rps);
+       struct rv6xx_pl *pl;
+       u32 current_index =
+               (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_PROFILE_INDEX_MASK) >>
+               CURRENT_PROFILE_INDEX_SHIFT;
+
+       if (current_index > 2) {
+               seq_printf(m, "invalid dpm profile %d\n", current_index);
+       } else {
+               if (current_index == 0)
+                       pl = &ps->low;
+               else if (current_index == 1)
+                       pl = &ps->medium;
+               else /* current_index == 2 */
+                       pl = &ps->high;
+               seq_printf(m, "uvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+               seq_printf(m, "power level %d    sclk: %u mclk: %u vddc: %u\n",
+                          current_index, pl->sclk, pl->mclk, pl->vddc);
+       }
+}
+
+void rv6xx_dpm_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               kfree(rdev->pm.dpm.ps[i].ps_priv);
+       }
+       kfree(rdev->pm.dpm.ps);
+       kfree(rdev->pm.dpm.priv);
+}
+
+u32 rv6xx_dpm_get_sclk(struct radeon_device *rdev, bool low)
+{
+       struct rv6xx_ps *requested_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
+
+       if (low)
+               return requested_state->low.sclk;
+       else
+               return requested_state->high.sclk;
+}
+
+u32 rv6xx_dpm_get_mclk(struct radeon_device *rdev, bool low)
+{
+       struct rv6xx_ps *requested_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
+
+       if (low)
+               return requested_state->low.mclk;
+       else
+               return requested_state->high.mclk;
+}
diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.h b/drivers/gpu/drm/radeon/rv6xx_dpm.h
new file mode 100644 (file)
index 0000000..8035d53
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
+ */
+
+#ifndef __RV6XX_DPM_H__
+#define __RV6XX_DPM_H__
+
+#include "r600_dpm.h"
+
+/* Represents a single SCLK step. */
+struct rv6xx_sclk_stepping
+{
+    u32 vco_frequency;
+    u32 post_divider;
+};
+
+struct rv6xx_pm_hw_state {
+       u32 sclks[R600_PM_NUMBER_OF_ACTIVITY_LEVELS];
+       u32 mclks[R600_PM_NUMBER_OF_MCLKS];
+       u16 vddc[R600_PM_NUMBER_OF_VOLTAGE_LEVELS];
+       bool backbias[R600_PM_NUMBER_OF_VOLTAGE_LEVELS];
+       bool pcie_gen2[R600_PM_NUMBER_OF_ACTIVITY_LEVELS];
+       u8 high_sclk_index;
+       u8 medium_sclk_index;
+       u8 low_sclk_index;
+       u8 high_mclk_index;
+       u8 medium_mclk_index;
+       u8 low_mclk_index;
+       u8 high_vddc_index;
+       u8 medium_vddc_index;
+       u8 low_vddc_index;
+       u8 rp[R600_PM_NUMBER_OF_ACTIVITY_LEVELS];
+       u8 lp[R600_PM_NUMBER_OF_ACTIVITY_LEVELS];
+};
+
+struct rv6xx_power_info {
+       /* flags */
+       bool voltage_control;
+       bool sclk_ss;
+       bool mclk_ss;
+       bool dynamic_ss;
+       bool dynamic_pcie_gen2;
+       bool thermal_protection;
+       bool display_gap;
+       bool gfx_clock_gating;
+       /* clk values */
+       u32 fb_div_scale;
+       u32 spll_ref_div;
+       u32 mpll_ref_div;
+       u32 bsu;
+       u32 bsp;
+       /* */
+       u32 active_auto_throttle_sources;
+       /* current power state */
+       u32 restricted_levels;
+       struct rv6xx_pm_hw_state hw;
+};
+
+struct rv6xx_pl {
+       u32 sclk;
+       u32 mclk;
+       u16 vddc;
+       u32 flags;
+};
+
+struct rv6xx_ps {
+       struct rv6xx_pl high;
+       struct rv6xx_pl medium;
+       struct rv6xx_pl low;
+};
+
+#define RV6XX_DEFAULT_VCLK_FREQ  40000 /* 10 khz */
+#define RV6XX_DEFAULT_DCLK_FREQ  30000 /* 10 khz */
+
+#endif
diff --git a/drivers/gpu/drm/radeon/rv6xxd.h b/drivers/gpu/drm/radeon/rv6xxd.h
new file mode 100644 (file)
index 0000000..34e86f9
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef RV6XXD_H
+#define RV6XXD_H
+
+/* RV6xx power management */
+#define SPLL_CNTL_MODE                                    0x60c
+#       define SPLL_DIV_SYNC                              (1 << 5)
+
+#define GENERAL_PWRMGT                                    0x618
+#       define GLOBAL_PWRMGT_EN                           (1 << 0)
+#       define STATIC_PM_EN                               (1 << 1)
+#       define MOBILE_SU                                  (1 << 2)
+#       define THERMAL_PROTECTION_DIS                     (1 << 3)
+#       define THERMAL_PROTECTION_TYPE                    (1 << 4)
+#       define ENABLE_GEN2PCIE                            (1 << 5)
+#       define SW_GPIO_INDEX(x)                           ((x) << 6)
+#       define SW_GPIO_INDEX_MASK                         (3 << 6)
+#       define LOW_VOLT_D2_ACPI                           (1 << 8)
+#       define LOW_VOLT_D3_ACPI                           (1 << 9)
+#       define VOLT_PWRMGT_EN                             (1 << 10)
+#       define BACKBIAS_PAD_EN                            (1 << 16)
+#       define BACKBIAS_VALUE                             (1 << 17)
+#       define BACKBIAS_DPM_CNTL                          (1 << 18)
+#       define DYN_SPREAD_SPECTRUM_EN                     (1 << 21)
+
+#define MCLK_PWRMGT_CNTL                                  0x624
+#       define MPLL_PWRMGT_OFF                            (1 << 0)
+#       define YCLK_TURNOFF                               (1 << 1)
+#       define MPLL_TURNOFF                               (1 << 2)
+#       define SU_MCLK_USE_BCLK                           (1 << 3)
+#       define DLL_READY                                  (1 << 4)
+#       define MC_BUSY                                    (1 << 5)
+#       define MC_INT_CNTL                                (1 << 7)
+#       define MRDCKA_SLEEP                               (1 << 8)
+#       define MRDCKB_SLEEP                               (1 << 9)
+#       define MRDCKC_SLEEP                               (1 << 10)
+#       define MRDCKD_SLEEP                               (1 << 11)
+#       define MRDCKE_SLEEP                               (1 << 12)
+#       define MRDCKF_SLEEP                               (1 << 13)
+#       define MRDCKG_SLEEP                               (1 << 14)
+#       define MRDCKH_SLEEP                               (1 << 15)
+#       define MRDCKA_RESET                               (1 << 16)
+#       define MRDCKB_RESET                               (1 << 17)
+#       define MRDCKC_RESET                               (1 << 18)
+#       define MRDCKD_RESET                               (1 << 19)
+#       define MRDCKE_RESET                               (1 << 20)
+#       define MRDCKF_RESET                               (1 << 21)
+#       define MRDCKG_RESET                               (1 << 22)
+#       define MRDCKH_RESET                               (1 << 23)
+#       define DLL_READY_READ                             (1 << 24)
+#       define USE_DISPLAY_GAP                            (1 << 25)
+#       define USE_DISPLAY_URGENT_NORMAL                  (1 << 26)
+#       define USE_DISPLAY_GAP_CTXSW                      (1 << 27)
+#       define MPLL_TURNOFF_D2                            (1 << 28)
+#       define USE_DISPLAY_URGENT_CTXSW                   (1 << 29)
+
+#define MPLL_FREQ_LEVEL_0                                 0x6e8
+#       define LEVEL0_MPLL_POST_DIV(x)                    ((x) << 0)
+#       define LEVEL0_MPLL_POST_DIV_MASK                  (0xff << 0)
+#       define LEVEL0_MPLL_FB_DIV(x)                      ((x) << 8)
+#       define LEVEL0_MPLL_FB_DIV_MASK                    (0xfff << 8)
+#       define LEVEL0_MPLL_REF_DIV(x)                     ((x) << 20)
+#       define LEVEL0_MPLL_REF_DIV_MASK                   (0x3f << 20)
+#       define LEVEL0_MPLL_DIV_EN                         (1 << 28)
+#       define LEVEL0_DLL_BYPASS                          (1 << 29)
+#       define LEVEL0_DLL_RESET                           (1 << 30)
+
+#define VID_RT                                            0x6f8
+#       define VID_CRT(x)                                 ((x) << 0)
+#       define VID_CRT_MASK                               (0x1fff << 0)
+#       define VID_CRTU(x)                                ((x) << 13)
+#       define VID_CRTU_MASK                              (7 << 13)
+#       define SSTU(x)                                    ((x) << 16)
+#       define SSTU_MASK                                  (7 << 16)
+#       define VID_SWT(x)                                 ((x) << 19)
+#       define VID_SWT_MASK                               (0x1f << 19)
+#       define BRT(x)                                     ((x) << 24)
+#       define BRT_MASK                                   (0xff << 24)
+
+#define TARGET_AND_CURRENT_PROFILE_INDEX                  0x70c
+#       define TARGET_PROFILE_INDEX_MASK                  (3 << 0)
+#       define TARGET_PROFILE_INDEX_SHIFT                 0
+#       define CURRENT_PROFILE_INDEX_MASK                 (3 << 2)
+#       define CURRENT_PROFILE_INDEX_SHIFT                2
+#       define DYN_PWR_ENTER_INDEX(x)                     ((x) << 4)
+#       define DYN_PWR_ENTER_INDEX_MASK                   (3 << 4)
+#       define DYN_PWR_ENTER_INDEX_SHIFT                  4
+#       define CURR_MCLK_INDEX_MASK                       (3 << 6)
+#       define CURR_MCLK_INDEX_SHIFT                      6
+#       define CURR_SCLK_INDEX_MASK                       (0x1f << 8)
+#       define CURR_SCLK_INDEX_SHIFT                      8
+#       define CURR_VID_INDEX_MASK                        (3 << 13)
+#       define CURR_VID_INDEX_SHIFT                       13
+
+#define VID_UPPER_GPIO_CNTL                               0x740
+#       define CTXSW_UPPER_GPIO_VALUES(x)                 ((x) << 0)
+#       define CTXSW_UPPER_GPIO_VALUES_MASK               (7 << 0)
+#       define HIGH_UPPER_GPIO_VALUES(x)                  ((x) << 3)
+#       define HIGH_UPPER_GPIO_VALUES_MASK                (7 << 3)
+#       define MEDIUM_UPPER_GPIO_VALUES(x)                ((x) << 6)
+#       define MEDIUM_UPPER_GPIO_VALUES_MASK              (7 << 6)
+#       define LOW_UPPER_GPIO_VALUES(x)                   ((x) << 9)
+#       define LOW_UPPER_GPIO_VALUES_MASK                 (7 << 9)
+#       define CTXSW_BACKBIAS_VALUE                       (1 << 12)
+#       define HIGH_BACKBIAS_VALUE                        (1 << 13)
+#       define MEDIUM_BACKBIAS_VALUE                      (1 << 14)
+#       define LOW_BACKBIAS_VALUE                         (1 << 15)
+
+#define CG_DISPLAY_GAP_CNTL                               0x7dc
+#       define DISP1_GAP(x)                               ((x) << 0)
+#       define DISP1_GAP_MASK                             (3 << 0)
+#       define DISP2_GAP(x)                               ((x) << 2)
+#       define DISP2_GAP_MASK                             (3 << 2)
+#       define VBI_TIMER_COUNT(x)                         ((x) << 4)
+#       define VBI_TIMER_COUNT_MASK                       (0x3fff << 4)
+#       define VBI_TIMER_UNIT(x)                          ((x) << 20)
+#       define VBI_TIMER_UNIT_MASK                        (7 << 20)
+#       define DISP1_GAP_MCHG(x)                          ((x) << 24)
+#       define DISP1_GAP_MCHG_MASK                        (3 << 24)
+#       define DISP2_GAP_MCHG(x)                          ((x) << 26)
+#       define DISP2_GAP_MCHG_MASK                        (3 << 26)
+
+#define CG_THERMAL_CTRL                                   0x7f0
+#       define DPM_EVENT_SRC(x)                           ((x) << 0)
+#       define DPM_EVENT_SRC_MASK                         (7 << 0)
+#       define THERM_INC_CLK                              (1 << 3)
+#       define TOFFSET(x)                                 ((x) << 4)
+#       define TOFFSET_MASK                               (0xff << 4)
+#       define DIG_THERM_DPM(x)                           ((x) << 12)
+#       define DIG_THERM_DPM_MASK                         (0xff << 12)
+#       define CTF_SEL(x)                                 ((x) << 20)
+#       define CTF_SEL_MASK                               (7 << 20)
+#       define CTF_PAD_POLARITY                           (1 << 23)
+#       define CTF_PAD_EN                                 (1 << 24)
+
+#define CG_SPLL_SPREAD_SPECTRUM_LOW                       0x820
+#       define SSEN                                       (1 << 0)
+#       define CLKS(x)                                    ((x) << 3)
+#       define CLKS_MASK                                  (0xff << 3)
+#       define CLKS_SHIFT                                 3
+#       define CLKV(x)                                    ((x) << 11)
+#       define CLKV_MASK                                  (0x7ff << 11)
+#       define CLKV_SHIFT                                 11
+#define CG_MPLL_SPREAD_SPECTRUM                           0x830
+
+#define CITF_CNTL                                      0x200c
+#       define BLACKOUT_RD                              (1 << 0)
+#       define BLACKOUT_WR                              (1 << 1)
+
+#define RAMCFG                                         0x2408
+#define                NOOFBANK_SHIFT                                  0
+#define                NOOFBANK_MASK                                   0x00000001
+#define                NOOFRANK_SHIFT                                  1
+#define                NOOFRANK_MASK                                   0x00000002
+#define                NOOFROWS_SHIFT                                  2
+#define                NOOFROWS_MASK                                   0x0000001C
+#define                NOOFCOLS_SHIFT                                  5
+#define                NOOFCOLS_MASK                                   0x00000060
+#define                CHANSIZE_SHIFT                                  7
+#define                CHANSIZE_MASK                                   0x00000080
+#define                BURSTLENGTH_SHIFT                               8
+#define                BURSTLENGTH_MASK                                0x00000100
+#define                CHANSIZE_OVERRIDE                               (1 << 10)
+
+#define SQM_RATIO                                      0x2424
+#       define STATE0(x)                                ((x) << 0)
+#       define STATE0_MASK                              (0xff << 0)
+#       define STATE1(x)                                ((x) << 8)
+#       define STATE1_MASK                              (0xff << 8)
+#       define STATE2(x)                                ((x) << 16)
+#       define STATE2_MASK                              (0xff << 16)
+#       define STATE3(x)                                ((x) << 24)
+#       define STATE3_MASK                              (0xff << 24)
+
+#define ARB_RFSH_CNTL                                  0x2460
+#       define ENABLE                                   (1 << 0)
+#define ARB_RFSH_RATE                                  0x2464
+#       define POWERMODE0(x)                            ((x) << 0)
+#       define POWERMODE0_MASK                          (0xff << 0)
+#       define POWERMODE1(x)                            ((x) << 8)
+#       define POWERMODE1_MASK                          (0xff << 8)
+#       define POWERMODE2(x)                            ((x) << 16)
+#       define POWERMODE2_MASK                          (0xff << 16)
+#       define POWERMODE3(x)                            ((x) << 24)
+#       define POWERMODE3_MASK                          (0xff << 24)
+
+#define MC_SEQ_DRAM                                    0x2608
+#       define CKE_DYN                                  (1 << 12)
+
+#define MC_SEQ_CMD                                     0x26c4
+
+#define MC_SEQ_RESERVE_S                               0x2890
+#define MC_SEQ_RESERVE_M                               0x2894
+
+#define LVTMA_DATA_SYNCHRONIZATION                      0x7adc
+#       define LVTMA_PFREQCHG                           (1 << 8)
+#define DCE3_LVTMA_DATA_SYNCHRONIZATION                 0x7f98
+
+/* PCIE indirect regs */
+#define PCIE_P_CNTL                                       0x40
+#       define P_PLL_PWRDN_IN_L1L23                       (1 << 3)
+#       define P_PLL_BUF_PDNB                             (1 << 4)
+#       define P_PLL_PDNB                                 (1 << 9)
+#       define P_ALLOW_PRX_FRONTEND_SHUTOFF               (1 << 12)
+/* PCIE PORT indirect regs */
+#define PCIE_LC_CNTL                                      0xa0
+#       define LC_L0S_INACTIVITY(x)                       ((x) << 8)
+#       define LC_L0S_INACTIVITY_MASK                     (0xf << 8)
+#       define LC_L0S_INACTIVITY_SHIFT                    8
+#       define LC_L1_INACTIVITY(x)                        ((x) << 12)
+#       define LC_L1_INACTIVITY_MASK                      (0xf << 12)
+#       define LC_L1_INACTIVITY_SHIFT                     12
+#       define LC_PMI_TO_L1_DIS                           (1 << 16)
+#       define LC_ASPM_TO_L1_DIS                          (1 << 24)
+#define PCIE_LC_SPEED_CNTL                                0xa4
+#       define LC_GEN2_EN                                 (1 << 0)
+#       define LC_INITIATE_LINK_SPEED_CHANGE              (1 << 7)
+#       define LC_CURRENT_DATA_RATE                       (1 << 11)
+#       define LC_HW_VOLTAGE_IF_CONTROL(x)                ((x) << 12)
+#       define LC_HW_VOLTAGE_IF_CONTROL_MASK              (3 << 12)
+#       define LC_HW_VOLTAGE_IF_CONTROL_SHIFT             12
+#       define LC_OTHER_SIDE_EVER_SENT_GEN2               (1 << 23)
+#       define LC_OTHER_SIDE_SUPPORTS_GEN2                (1 << 24)
+
+#endif
diff --git a/drivers/gpu/drm/radeon/rv730_dpm.c b/drivers/gpu/drm/radeon/rv730_dpm.c
new file mode 100644 (file)
index 0000000..3f5e1cf
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "rv730d.h"
+#include "r600_dpm.h"
+#include "rv770_dpm.h"
+#include "atom.h"
+
+#define MC_CG_ARB_FREQ_F0           0x0a
+#define MC_CG_ARB_FREQ_F1           0x0b
+#define MC_CG_ARB_FREQ_F2           0x0c
+#define MC_CG_ARB_FREQ_F3           0x0d
+
+struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps);
+struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev);
+
+int rv730_populate_sclk_value(struct radeon_device *rdev,
+                             u32 engine_clock,
+                             RV770_SMC_SCLK_VALUE *sclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct atom_clock_dividers dividers;
+       u32 spll_func_cntl = pi->clk_regs.rv730.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 = pi->clk_regs.rv730.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 = pi->clk_regs.rv730.cg_spll_func_cntl_3;
+       u32 cg_spll_spread_spectrum = pi->clk_regs.rv730.cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2 = pi->clk_regs.rv730.cg_spll_spread_spectrum_2;
+       u64 tmp;
+       u32 reference_clock = rdev->clock.spll.reference_freq;
+       u32 reference_divider, post_divider;
+       u32 fbdiv;
+       int ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            engine_clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       reference_divider = 1 + dividers.ref_div;
+
+       if (dividers.enable_post_div)
+               post_divider = ((dividers.post_div >> 4) & 0xf) +
+                       (dividers.post_div & 0xf) + 2;
+       else
+               post_divider = 1;
+
+       tmp = (u64) engine_clock * reference_divider * post_divider * 16384;
+       do_div(tmp, reference_clock);
+       fbdiv = (u32) tmp;
+
+       /* set up registers */
+       if (dividers.enable_post_div)
+               spll_func_cntl |= SPLL_DIVEN;
+       else
+               spll_func_cntl &= ~SPLL_DIVEN;
+       spll_func_cntl &= ~(SPLL_HILEN_MASK | SPLL_LOLEN_MASK | SPLL_REF_DIV_MASK);
+       spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div);
+       spll_func_cntl |= SPLL_HILEN((dividers.post_div >> 4) & 0xf);
+       spll_func_cntl |= SPLL_LOLEN(dividers.post_div & 0xf);
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(2);
+
+       spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK;
+       spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv);
+       spll_func_cntl_3 |= SPLL_DITHEN;
+
+       if (pi->sclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 vco_freq = engine_clock * post_divider;
+
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
+                       u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
+                       u32 clk_v = ss.percentage * fbdiv / (clk_s * 10000);
+
+                       cg_spll_spread_spectrum &= ~CLK_S_MASK;
+                       cg_spll_spread_spectrum |= CLK_S(clk_s);
+                       cg_spll_spread_spectrum |= SSEN;
+
+                       cg_spll_spread_spectrum_2 &= ~CLK_V_MASK;
+                       cg_spll_spread_spectrum_2 |= CLK_V(clk_v);
+               }
+       }
+
+       sclk->sclk_value = cpu_to_be32(engine_clock);
+       sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl);
+       sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2);
+       sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3);
+       sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(cg_spll_spread_spectrum);
+       sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(cg_spll_spread_spectrum_2);
+
+       return 0;
+}
+
+int rv730_populate_mclk_value(struct radeon_device *rdev,
+                             u32 engine_clock, u32 memory_clock,
+                             LPRV7XX_SMC_MCLK_VALUE mclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 mclk_pwrmgt_cntl = pi->clk_regs.rv730.mclk_pwrmgt_cntl;
+       u32 dll_cntl = pi->clk_regs.rv730.dll_cntl;
+       u32 mpll_func_cntl = pi->clk_regs.rv730.mpll_func_cntl;
+       u32 mpll_func_cntl_2 = pi->clk_regs.rv730.mpll_func_cntl2;
+       u32 mpll_func_cntl_3 = pi->clk_regs.rv730.mpll_func_cntl3;
+       u32 mpll_ss = pi->clk_regs.rv730.mpll_ss;
+       u32 mpll_ss2 = pi->clk_regs.rv730.mpll_ss2;
+       struct atom_clock_dividers dividers;
+       u32 post_divider, reference_divider;
+       int ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM,
+                                            memory_clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       reference_divider = dividers.ref_div + 1;
+
+       if (dividers.enable_post_div)
+               post_divider = ((dividers.post_div >> 4) & 0xf) +
+                       (dividers.post_div & 0xf) + 2;
+       else
+               post_divider = 1;
+
+       /* setup the registers */
+       if (dividers.enable_post_div)
+               mpll_func_cntl |= MPLL_DIVEN;
+       else
+               mpll_func_cntl &= ~MPLL_DIVEN;
+
+       mpll_func_cntl &= ~(MPLL_REF_DIV_MASK | MPLL_HILEN_MASK | MPLL_LOLEN_MASK);
+       mpll_func_cntl |= MPLL_REF_DIV(dividers.ref_div);
+       mpll_func_cntl |= MPLL_HILEN((dividers.post_div >> 4) & 0xf);
+       mpll_func_cntl |= MPLL_LOLEN(dividers.post_div & 0xf);
+
+       mpll_func_cntl_3 &= ~MPLL_FB_DIV_MASK;
+       mpll_func_cntl_3 |= MPLL_FB_DIV(dividers.fb_div);
+       if (dividers.enable_dithen)
+               mpll_func_cntl_3 |= MPLL_DITHEN;
+       else
+               mpll_func_cntl_3 &= ~MPLL_DITHEN;
+
+       if (pi->mclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 vco_freq = memory_clock * post_divider;
+
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_MEMORY_SS, vco_freq)) {
+                       u32 reference_clock = rdev->clock.mpll.reference_freq;
+                       u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
+                       u32 clk_v = ss.percentage * dividers.fb_div / (clk_s * 10000);
+
+                       mpll_ss &= ~CLK_S_MASK;
+                       mpll_ss |= CLK_S(clk_s);
+                       mpll_ss |= SSEN;
+
+                       mpll_ss2 &= ~CLK_V_MASK;
+                       mpll_ss |= CLK_V(clk_v);
+               }
+       }
+
+
+       mclk->mclk730.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+       mclk->mclk730.vDLL_CNTL = cpu_to_be32(dll_cntl);
+       mclk->mclk730.mclk_value = cpu_to_be32(memory_clock);
+       mclk->mclk730.vMPLL_FUNC_CNTL = cpu_to_be32(mpll_func_cntl);
+       mclk->mclk730.vMPLL_FUNC_CNTL2 = cpu_to_be32(mpll_func_cntl_2);
+       mclk->mclk730.vMPLL_FUNC_CNTL3 = cpu_to_be32(mpll_func_cntl_3);
+       mclk->mclk730.vMPLL_SS = cpu_to_be32(mpll_ss);
+       mclk->mclk730.vMPLL_SS2 = cpu_to_be32(mpll_ss2);
+
+       return 0;
+}
+
+void rv730_read_clock_registers(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       pi->clk_regs.rv730.cg_spll_func_cntl =
+               RREG32(CG_SPLL_FUNC_CNTL);
+       pi->clk_regs.rv730.cg_spll_func_cntl_2 =
+               RREG32(CG_SPLL_FUNC_CNTL_2);
+       pi->clk_regs.rv730.cg_spll_func_cntl_3 =
+               RREG32(CG_SPLL_FUNC_CNTL_3);
+       pi->clk_regs.rv730.cg_spll_spread_spectrum =
+               RREG32(CG_SPLL_SPREAD_SPECTRUM);
+       pi->clk_regs.rv730.cg_spll_spread_spectrum_2 =
+               RREG32(CG_SPLL_SPREAD_SPECTRUM_2);
+
+       pi->clk_regs.rv730.mclk_pwrmgt_cntl =
+               RREG32(TCI_MCLK_PWRMGT_CNTL);
+       pi->clk_regs.rv730.dll_cntl =
+               RREG32(TCI_DLL_CNTL);
+       pi->clk_regs.rv730.mpll_func_cntl =
+               RREG32(CG_MPLL_FUNC_CNTL);
+       pi->clk_regs.rv730.mpll_func_cntl2 =
+               RREG32(CG_MPLL_FUNC_CNTL_2);
+       pi->clk_regs.rv730.mpll_func_cntl3 =
+               RREG32(CG_MPLL_FUNC_CNTL_3);
+       pi->clk_regs.rv730.mpll_ss =
+               RREG32(CG_TCI_MPLL_SPREAD_SPECTRUM);
+       pi->clk_regs.rv730.mpll_ss2 =
+               RREG32(CG_TCI_MPLL_SPREAD_SPECTRUM_2);
+}
+
+int rv730_populate_smc_acpi_state(struct radeon_device *rdev,
+                                 RV770_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 mpll_func_cntl = 0;
+       u32 mpll_func_cntl_2 = 0 ;
+       u32 mpll_func_cntl_3 = 0;
+       u32 mclk_pwrmgt_cntl;
+       u32 dll_cntl;
+       u32 spll_func_cntl;
+       u32 spll_func_cntl_2;
+       u32 spll_func_cntl_3;
+
+       table->ACPIState = table->initialState;
+       table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       if (pi->acpi_vddc) {
+               rv770_populate_vddc_value(rdev, pi->acpi_vddc,
+                                         &table->ACPIState.levels[0].vddc);
+               table->ACPIState.levels[0].gen2PCIE = pi->pcie_gen2 ?
+                       pi->acpi_pcie_gen2 : 0;
+               table->ACPIState.levels[0].gen2XSP =
+                       pi->acpi_pcie_gen2;
+       } else {
+               rv770_populate_vddc_value(rdev, pi->min_vddc_in_table,
+                                         &table->ACPIState.levels[0].vddc);
+               table->ACPIState.levels[0].gen2PCIE = 0;
+       }
+
+       mpll_func_cntl = pi->clk_regs.rv730.mpll_func_cntl;
+       mpll_func_cntl_2 = pi->clk_regs.rv730.mpll_func_cntl2;
+       mpll_func_cntl_3 = pi->clk_regs.rv730.mpll_func_cntl3;
+
+       mpll_func_cntl |= MPLL_RESET | MPLL_BYPASS_EN;
+       mpll_func_cntl &= ~MPLL_SLEEP;
+
+       mpll_func_cntl_2 &= ~MCLK_MUX_SEL_MASK;
+       mpll_func_cntl_2 |= MCLK_MUX_SEL(1);
+
+       mclk_pwrmgt_cntl = (MRDCKA_RESET |
+                           MRDCKB_RESET |
+                           MRDCKC_RESET |
+                           MRDCKD_RESET |
+                           MRDCKE_RESET |
+                           MRDCKF_RESET |
+                           MRDCKG_RESET |
+                           MRDCKH_RESET |
+                           MRDCKA_SLEEP |
+                           MRDCKB_SLEEP |
+                           MRDCKC_SLEEP |
+                           MRDCKD_SLEEP |
+                           MRDCKE_SLEEP |
+                           MRDCKF_SLEEP |
+                           MRDCKG_SLEEP |
+                           MRDCKH_SLEEP);
+
+       dll_cntl = 0xff000000;
+
+       spll_func_cntl = pi->clk_regs.rv730.cg_spll_func_cntl;
+       spll_func_cntl_2 = pi->clk_regs.rv730.cg_spll_func_cntl_2;
+       spll_func_cntl_3 = pi->clk_regs.rv730.cg_spll_func_cntl_3;
+
+       spll_func_cntl |= SPLL_RESET | SPLL_BYPASS_EN;
+       spll_func_cntl &= ~SPLL_SLEEP;
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(4);
+
+       table->ACPIState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL = cpu_to_be32(mpll_func_cntl);
+       table->ACPIState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL2 = cpu_to_be32(mpll_func_cntl_2);
+       table->ACPIState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL3 = cpu_to_be32(mpll_func_cntl_3);
+       table->ACPIState.levels[0].mclk.mclk730.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+       table->ACPIState.levels[0].mclk.mclk730.vDLL_CNTL = cpu_to_be32(dll_cntl);
+
+       table->ACPIState.levels[0].mclk.mclk730.mclk_value = 0;
+
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3);
+
+       table->ACPIState.levels[0].sclk.sclk_value = 0;
+
+       rv770_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd);
+
+       table->ACPIState.levels[1] = table->ACPIState.levels[0];
+       table->ACPIState.levels[2] = table->ACPIState.levels[0];
+
+       return 0;
+}
+
+int rv730_populate_smc_initial_state(struct radeon_device *rdev,
+                                    struct radeon_ps *radeon_state,
+                                    RV770_SMC_STATETABLE *table)
+{
+       struct rv7xx_ps *initial_state = rv770_get_ps(radeon_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 a_t;
+
+       table->initialState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL =
+               cpu_to_be32(pi->clk_regs.rv730.mpll_func_cntl);
+       table->initialState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL2 =
+               cpu_to_be32(pi->clk_regs.rv730.mpll_func_cntl2);
+       table->initialState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL3 =
+               cpu_to_be32(pi->clk_regs.rv730.mpll_func_cntl3);
+       table->initialState.levels[0].mclk.mclk730.vMCLK_PWRMGT_CNTL =
+               cpu_to_be32(pi->clk_regs.rv730.mclk_pwrmgt_cntl);
+       table->initialState.levels[0].mclk.mclk730.vDLL_CNTL =
+               cpu_to_be32(pi->clk_regs.rv730.dll_cntl);
+       table->initialState.levels[0].mclk.mclk730.vMPLL_SS =
+               cpu_to_be32(pi->clk_regs.rv730.mpll_ss);
+       table->initialState.levels[0].mclk.mclk730.vMPLL_SS2 =
+               cpu_to_be32(pi->clk_regs.rv730.mpll_ss2);
+
+       table->initialState.levels[0].mclk.mclk730.mclk_value =
+               cpu_to_be32(initial_state->low.mclk);
+
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
+               cpu_to_be32(pi->clk_regs.rv730.cg_spll_func_cntl);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+               cpu_to_be32(pi->clk_regs.rv730.cg_spll_func_cntl_2);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+               cpu_to_be32(pi->clk_regs.rv730.cg_spll_func_cntl_3);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM =
+               cpu_to_be32(pi->clk_regs.rv730.cg_spll_spread_spectrum);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2 =
+               cpu_to_be32(pi->clk_regs.rv730.cg_spll_spread_spectrum_2);
+
+       table->initialState.levels[0].sclk.sclk_value =
+               cpu_to_be32(initial_state->low.sclk);
+
+       table->initialState.levels[0].arbValue = MC_CG_ARB_FREQ_F0;
+
+       table->initialState.levels[0].seqValue =
+               rv770_get_seq_value(rdev, &initial_state->low);
+
+       rv770_populate_vddc_value(rdev,
+                                 initial_state->low.vddc,
+                                 &table->initialState.levels[0].vddc);
+       rv770_populate_initial_mvdd_value(rdev,
+                                         &table->initialState.levels[0].mvdd);
+
+       a_t = CG_R(0xffff) | CG_L(0);
+
+       table->initialState.levels[0].aT = cpu_to_be32(a_t);
+
+       table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp);
+
+       if (pi->boot_in_gen2)
+               table->initialState.levels[0].gen2PCIE = 1;
+       else
+               table->initialState.levels[0].gen2PCIE = 0;
+       if (initial_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2)
+               table->initialState.levels[0].gen2XSP = 1;
+       else
+               table->initialState.levels[0].gen2XSP = 0;
+
+       table->initialState.levels[1] = table->initialState.levels[0];
+       table->initialState.levels[2] = table->initialState.levels[0];
+
+       table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       return 0;
+}
+
+void rv730_program_memory_timing_parameters(struct radeon_device *rdev,
+                                           struct radeon_ps *radeon_state)
+{
+       struct rv7xx_ps *state = rv770_get_ps(radeon_state);
+       u32 arb_refresh_rate = 0;
+       u32 dram_timing = 0;
+       u32 dram_timing2 = 0;
+       u32 old_dram_timing = 0;
+       u32 old_dram_timing2 = 0;
+
+       arb_refresh_rate = RREG32(MC_ARB_RFSH_RATE) &
+               ~(POWERMODE1_MASK | POWERMODE2_MASK | POWERMODE3_MASK);
+       arb_refresh_rate |=
+               (POWERMODE1(rv770_calculate_memory_refresh_rate(rdev, state->low.sclk)) |
+                POWERMODE2(rv770_calculate_memory_refresh_rate(rdev, state->medium.sclk)) |
+                POWERMODE3(rv770_calculate_memory_refresh_rate(rdev, state->high.sclk)));
+       WREG32(MC_ARB_RFSH_RATE, arb_refresh_rate);
+
+       /* save the boot dram timings */
+       old_dram_timing = RREG32(MC_ARB_DRAM_TIMING);
+       old_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+
+       radeon_atom_set_engine_dram_timings(rdev,
+                                           state->high.sclk,
+                                           state->high.mclk);
+
+       dram_timing = RREG32(MC_ARB_DRAM_TIMING);
+       dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+
+       WREG32(MC_ARB_DRAM_TIMING_3, dram_timing);
+       WREG32(MC_ARB_DRAM_TIMING2_3, dram_timing2);
+
+       radeon_atom_set_engine_dram_timings(rdev,
+                                           state->medium.sclk,
+                                           state->medium.mclk);
+
+       dram_timing = RREG32(MC_ARB_DRAM_TIMING);
+       dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+
+       WREG32(MC_ARB_DRAM_TIMING_2, dram_timing);
+       WREG32(MC_ARB_DRAM_TIMING2_2, dram_timing2);
+
+       radeon_atom_set_engine_dram_timings(rdev,
+                                           state->low.sclk,
+                                           state->low.mclk);
+
+       dram_timing = RREG32(MC_ARB_DRAM_TIMING);
+       dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+
+       WREG32(MC_ARB_DRAM_TIMING_1, dram_timing);
+       WREG32(MC_ARB_DRAM_TIMING2_1, dram_timing2);
+
+       /* restore the boot dram timings */
+       WREG32(MC_ARB_DRAM_TIMING, old_dram_timing);
+       WREG32(MC_ARB_DRAM_TIMING2, old_dram_timing2);
+
+}
+
+void rv730_start_dpm(struct radeon_device *rdev)
+{
+       WREG32_P(SCLK_PWRMGT_CNTL, 0, ~SCLK_PWRMGT_OFF);
+
+       WREG32_P(TCI_MCLK_PWRMGT_CNTL, 0, ~MPLL_PWRMGT_OFF);
+
+       WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN);
+}
+
+void rv730_stop_dpm(struct radeon_device *rdev)
+{
+       PPSMC_Result result;
+
+       result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_TwoLevelsDisabled);
+
+       if (result != PPSMC_Result_OK)
+               DRM_ERROR("Could not force DPM to low\n");
+
+       WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN);
+
+       WREG32_P(SCLK_PWRMGT_CNTL, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF);
+
+       WREG32_P(TCI_MCLK_PWRMGT_CNTL, MPLL_PWRMGT_OFF, ~MPLL_PWRMGT_OFF);
+}
+
+void rv730_program_dcodt(struct radeon_device *rdev, bool use_dcodt)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 i = use_dcodt ? 0 : 1;
+       u32 mc4_io_pad_cntl;
+
+       mc4_io_pad_cntl = RREG32(MC4_IO_DQ_PAD_CNTL_D0_I0);
+       mc4_io_pad_cntl &= 0xFFFFFF00;
+       mc4_io_pad_cntl |= pi->odt_value_0[i];
+       WREG32(MC4_IO_DQ_PAD_CNTL_D0_I0, mc4_io_pad_cntl);
+       WREG32(MC4_IO_DQ_PAD_CNTL_D0_I1, mc4_io_pad_cntl);
+
+       mc4_io_pad_cntl = RREG32(MC4_IO_QS_PAD_CNTL_D0_I0);
+       mc4_io_pad_cntl &= 0xFFFFFF00;
+       mc4_io_pad_cntl |= pi->odt_value_1[i];
+       WREG32(MC4_IO_QS_PAD_CNTL_D0_I0, mc4_io_pad_cntl);
+       WREG32(MC4_IO_QS_PAD_CNTL_D0_I1, mc4_io_pad_cntl);
+}
+
+void rv730_get_odt_values(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 mc4_io_pad_cntl;
+
+       pi->odt_value_0[0] = (u8)0;
+       pi->odt_value_1[0] = (u8)0x80;
+
+       mc4_io_pad_cntl = RREG32(MC4_IO_DQ_PAD_CNTL_D0_I0);
+       pi->odt_value_0[1] = (u8)(mc4_io_pad_cntl & 0xff);
+
+       mc4_io_pad_cntl = RREG32(MC4_IO_QS_PAD_CNTL_D0_I0);
+       pi->odt_value_1[1] = (u8)(mc4_io_pad_cntl & 0xff);
+}
diff --git a/drivers/gpu/drm/radeon/rv730d.h b/drivers/gpu/drm/radeon/rv730d.h
new file mode 100644 (file)
index 0000000..f0a7954
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef RV730_H
+#define RV730_H
+
+#define        CG_SPLL_FUNC_CNTL                               0x600
+#define                SPLL_RESET                              (1 << 0)
+#define                SPLL_SLEEP                              (1 << 1)
+#define                SPLL_DIVEN                              (1 << 2)
+#define                SPLL_BYPASS_EN                          (1 << 3)
+#define                SPLL_REF_DIV(x)                         ((x) << 4)
+#define                SPLL_REF_DIV_MASK                       (0x3f << 4)
+#define                SPLL_HILEN(x)                           ((x) << 12)
+#define                SPLL_HILEN_MASK                         (0xf << 12)
+#define                SPLL_LOLEN(x)                           ((x) << 16)
+#define                SPLL_LOLEN_MASK                         (0xf << 16)
+#define        CG_SPLL_FUNC_CNTL_2                             0x604
+#define                SCLK_MUX_SEL(x)                         ((x) << 0)
+#define                SCLK_MUX_SEL_MASK                       (0x1ff << 0)
+#define        CG_SPLL_FUNC_CNTL_3                             0x608
+#define                SPLL_FB_DIV(x)                          ((x) << 0)
+#define                SPLL_FB_DIV_MASK                        (0x3ffffff << 0)
+#define                SPLL_DITHEN                             (1 << 28)
+
+#define        CG_MPLL_FUNC_CNTL                               0x624
+#define                MPLL_RESET                              (1 << 0)
+#define                MPLL_SLEEP                              (1 << 1)
+#define                MPLL_DIVEN                              (1 << 2)
+#define                MPLL_BYPASS_EN                          (1 << 3)
+#define                MPLL_REF_DIV(x)                         ((x) << 4)
+#define                MPLL_REF_DIV_MASK                       (0x3f << 4)
+#define                MPLL_HILEN(x)                           ((x) << 12)
+#define                MPLL_HILEN_MASK                         (0xf << 12)
+#define                MPLL_LOLEN(x)                           ((x) << 16)
+#define                MPLL_LOLEN_MASK                         (0xf << 16)
+#define        CG_MPLL_FUNC_CNTL_2                             0x628
+#define                MCLK_MUX_SEL(x)                         ((x) << 0)
+#define                MCLK_MUX_SEL_MASK                       (0x1ff << 0)
+#define        CG_MPLL_FUNC_CNTL_3                             0x62c
+#define                MPLL_FB_DIV(x)                          ((x) << 0)
+#define                MPLL_FB_DIV_MASK                        (0x3ffffff << 0)
+#define                MPLL_DITHEN                             (1 << 28)
+
+#define        CG_TCI_MPLL_SPREAD_SPECTRUM                     0x634
+#define        CG_TCI_MPLL_SPREAD_SPECTRUM_2                   0x638
+#define GENERAL_PWRMGT                                  0x63c
+#       define GLOBAL_PWRMGT_EN                         (1 << 0)
+#       define STATIC_PM_EN                             (1 << 1)
+#       define THERMAL_PROTECTION_DIS                   (1 << 2)
+#       define THERMAL_PROTECTION_TYPE                  (1 << 3)
+#       define ENABLE_GEN2PCIE                          (1 << 4)
+#       define ENABLE_GEN2XSP                           (1 << 5)
+#       define SW_SMIO_INDEX(x)                         ((x) << 6)
+#       define SW_SMIO_INDEX_MASK                       (3 << 6)
+#       define LOW_VOLT_D2_ACPI                         (1 << 8)
+#       define LOW_VOLT_D3_ACPI                         (1 << 9)
+#       define VOLT_PWRMGT_EN                           (1 << 10)
+#       define BACKBIAS_PAD_EN                          (1 << 18)
+#       define BACKBIAS_VALUE                           (1 << 19)
+#       define DYN_SPREAD_SPECTRUM_EN                   (1 << 23)
+#       define AC_DC_SW                                 (1 << 24)
+
+#define SCLK_PWRMGT_CNTL                                  0x644
+#       define SCLK_PWRMGT_OFF                            (1 << 0)
+#       define SCLK_LOW_D1                                (1 << 1)
+#       define FIR_RESET                                  (1 << 4)
+#       define FIR_FORCE_TREND_SEL                        (1 << 5)
+#       define FIR_TREND_MODE                             (1 << 6)
+#       define DYN_GFX_CLK_OFF_EN                         (1 << 7)
+#       define GFX_CLK_FORCE_ON                           (1 << 8)
+#       define GFX_CLK_REQUEST_OFF                        (1 << 9)
+#       define GFX_CLK_FORCE_OFF                          (1 << 10)
+#       define GFX_CLK_OFF_ACPI_D1                        (1 << 11)
+#       define GFX_CLK_OFF_ACPI_D2                        (1 << 12)
+#       define GFX_CLK_OFF_ACPI_D3                        (1 << 13)
+
+#define        TCI_MCLK_PWRMGT_CNTL                            0x648
+#       define MPLL_PWRMGT_OFF                          (1 << 5)
+#       define DLL_READY                                (1 << 6)
+#       define MC_INT_CNTL                              (1 << 7)
+#       define MRDCKA_SLEEP                             (1 << 8)
+#       define MRDCKB_SLEEP                             (1 << 9)
+#       define MRDCKC_SLEEP                             (1 << 10)
+#       define MRDCKD_SLEEP                             (1 << 11)
+#       define MRDCKE_SLEEP                             (1 << 12)
+#       define MRDCKF_SLEEP                             (1 << 13)
+#       define MRDCKG_SLEEP                             (1 << 14)
+#       define MRDCKH_SLEEP                             (1 << 15)
+#       define MRDCKA_RESET                             (1 << 16)
+#       define MRDCKB_RESET                             (1 << 17)
+#       define MRDCKC_RESET                             (1 << 18)
+#       define MRDCKD_RESET                             (1 << 19)
+#       define MRDCKE_RESET                             (1 << 20)
+#       define MRDCKF_RESET                             (1 << 21)
+#       define MRDCKG_RESET                             (1 << 22)
+#       define MRDCKH_RESET                             (1 << 23)
+#       define DLL_READY_READ                           (1 << 24)
+#       define USE_DISPLAY_GAP                          (1 << 25)
+#       define USE_DISPLAY_URGENT_NORMAL                (1 << 26)
+#       define MPLL_TURNOFF_D2                          (1 << 28)
+#define        TCI_DLL_CNTL                                    0x64c
+
+#define        CG_PG_CNTL                                      0x858
+#       define PWRGATE_ENABLE                           (1 << 0)
+
+#define        CG_AT                                           0x6d4
+#define                CG_R(x)                                 ((x) << 0)
+#define                CG_R_MASK                               (0xffff << 0)
+#define                CG_L(x)                                 ((x) << 16)
+#define                CG_L_MASK                               (0xffff << 16)
+
+#define        CG_SPLL_SPREAD_SPECTRUM                         0x790
+#define                SSEN                                    (1 << 0)
+#define                CLK_S(x)                                ((x) << 4)
+#define                CLK_S_MASK                              (0xfff << 4)
+#define        CG_SPLL_SPREAD_SPECTRUM_2                       0x794
+#define                CLK_V(x)                                ((x) << 0)
+#define                CLK_V_MASK                              (0x3ffffff << 0)
+
+#define        MC_ARB_DRAM_TIMING                              0x2774
+#define        MC_ARB_DRAM_TIMING2                             0x2778
+
+#define        MC_ARB_RFSH_RATE                                0x27b0
+#define                POWERMODE0(x)                           ((x) << 0)
+#define                POWERMODE0_MASK                         (0xff << 0)
+#define                POWERMODE1(x)                           ((x) << 8)
+#define                POWERMODE1_MASK                         (0xff << 8)
+#define                POWERMODE2(x)                           ((x) << 16)
+#define                POWERMODE2_MASK                         (0xff << 16)
+#define                POWERMODE3(x)                           ((x) << 24)
+#define                POWERMODE3_MASK                         (0xff << 24)
+
+#define        MC_ARB_DRAM_TIMING_1                            0x27f0
+#define        MC_ARB_DRAM_TIMING_2                            0x27f4
+#define        MC_ARB_DRAM_TIMING_3                            0x27f8
+#define        MC_ARB_DRAM_TIMING2_1                           0x27fc
+#define        MC_ARB_DRAM_TIMING2_2                           0x2800
+#define        MC_ARB_DRAM_TIMING2_3                           0x2804
+
+#define        MC4_IO_DQ_PAD_CNTL_D0_I0                        0x2978
+#define        MC4_IO_DQ_PAD_CNTL_D0_I1                        0x297c
+#define        MC4_IO_QS_PAD_CNTL_D0_I0                        0x2980
+#define        MC4_IO_QS_PAD_CNTL_D0_I1                        0x2984
+
+#endif
diff --git a/drivers/gpu/drm/radeon/rv740_dpm.c b/drivers/gpu/drm/radeon/rv740_dpm.c
new file mode 100644 (file)
index 0000000..c4c8da5
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "rv740d.h"
+#include "r600_dpm.h"
+#include "rv770_dpm.h"
+#include "atom.h"
+
+struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev);
+
+u32 rv740_get_decoded_reference_divider(u32 encoded_ref)
+{
+       u32 ref = 0;
+
+       switch (encoded_ref) {
+        case 0:
+               ref = 1;
+               break;
+        case 16:
+               ref = 2;
+               break;
+        case 17:
+               ref = 3;
+               break;
+        case 18:
+               ref = 2;
+               break;
+        case 19:
+               ref = 3;
+               break;
+        case 20:
+               ref = 4;
+               break;
+        case 21:
+               ref = 5;
+               break;
+        default:
+               DRM_ERROR("Invalid encoded Reference Divider\n");
+               ref = 0;
+               break;
+       }
+
+       return ref;
+}
+
+struct dll_speed_setting {
+       u16 min;
+       u16 max;
+       u32 dll_speed;
+};
+
+static struct dll_speed_setting dll_speed_table[16] =
+{
+       { 270, 320, 0x0f },
+       { 240, 270, 0x0e },
+       { 200, 240, 0x0d },
+       { 180, 200, 0x0c },
+       { 160, 180, 0x0b },
+       { 140, 160, 0x0a },
+       { 120, 140, 0x09 },
+       { 110, 120, 0x08 },
+       {  95, 110, 0x07 },
+       {  85,  95, 0x06 },
+       {  78,  85, 0x05 },
+       {  70,  78, 0x04 },
+       {  65,  70, 0x03 },
+       {  60,  65, 0x02 },
+       {  42,  60, 0x01 },
+       {  00,  42, 0x00 }
+};
+
+u32 rv740_get_dll_speed(bool is_gddr5, u32 memory_clock)
+{
+       int i;
+       u32 factor;
+       u16 data_rate;
+
+       if (is_gddr5)
+               factor = 4;
+       else
+               factor = 2;
+
+       data_rate = (u16)(memory_clock * factor / 1000);
+
+       if (data_rate < dll_speed_table[0].max) {
+               for (i = 0; i < 16; i++) {
+                       if (data_rate > dll_speed_table[i].min &&
+                           data_rate <= dll_speed_table[i].max)
+                               return dll_speed_table[i].dll_speed;
+               }
+       }
+
+       DRM_DEBUG_KMS("Target MCLK greater than largest MCLK in DLL speed table\n");
+
+       return 0x0f;
+}
+
+int rv740_populate_sclk_value(struct radeon_device *rdev, u32 engine_clock,
+                             RV770_SMC_SCLK_VALUE *sclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct atom_clock_dividers dividers;
+       u32 spll_func_cntl = pi->clk_regs.rv770.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 = pi->clk_regs.rv770.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 = pi->clk_regs.rv770.cg_spll_func_cntl_3;
+       u32 cg_spll_spread_spectrum = pi->clk_regs.rv770.cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2 = pi->clk_regs.rv770.cg_spll_spread_spectrum_2;
+       u64 tmp;
+       u32 reference_clock = rdev->clock.spll.reference_freq;
+       u32 reference_divider;
+       u32 fbdiv;
+       int ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            engine_clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       reference_divider = 1 + dividers.ref_div;
+
+       tmp = (u64) engine_clock * reference_divider * dividers.post_div * 16384;
+       do_div(tmp, reference_clock);
+       fbdiv = (u32) tmp;
+
+       spll_func_cntl &= ~(SPLL_PDIV_A_MASK | SPLL_REF_DIV_MASK);
+       spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div);
+       spll_func_cntl |= SPLL_PDIV_A(dividers.post_div);
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(2);
+
+       spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK;
+       spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv);
+       spll_func_cntl_3 |= SPLL_DITHEN;
+
+       if (pi->sclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 vco_freq = engine_clock * dividers.post_div;
+
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
+                       u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
+                       u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000);
+
+                       cg_spll_spread_spectrum &= ~CLK_S_MASK;
+                       cg_spll_spread_spectrum |= CLK_S(clk_s);
+                       cg_spll_spread_spectrum |= SSEN;
+
+                       cg_spll_spread_spectrum_2 &= ~CLK_V_MASK;
+                       cg_spll_spread_spectrum_2 |= CLK_V(clk_v);
+               }
+       }
+
+       sclk->sclk_value = cpu_to_be32(engine_clock);
+       sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl);
+       sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2);
+       sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3);
+       sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(cg_spll_spread_spectrum);
+       sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(cg_spll_spread_spectrum_2);
+
+       return 0;
+}
+
+int rv740_populate_mclk_value(struct radeon_device *rdev,
+                             u32 engine_clock, u32 memory_clock,
+                             RV7XX_SMC_MCLK_VALUE *mclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 mpll_ad_func_cntl = pi->clk_regs.rv770.mpll_ad_func_cntl;
+       u32 mpll_ad_func_cntl_2 = pi->clk_regs.rv770.mpll_ad_func_cntl_2;
+       u32 mpll_dq_func_cntl = pi->clk_regs.rv770.mpll_dq_func_cntl;
+       u32 mpll_dq_func_cntl_2 = pi->clk_regs.rv770.mpll_dq_func_cntl_2;
+       u32 mclk_pwrmgt_cntl = pi->clk_regs.rv770.mclk_pwrmgt_cntl;
+       u32 dll_cntl = pi->clk_regs.rv770.dll_cntl;
+       u32 mpll_ss1 = pi->clk_regs.rv770.mpll_ss1;
+       u32 mpll_ss2 = pi->clk_regs.rv770.mpll_ss2;
+       struct atom_clock_dividers dividers;
+       u32 ibias;
+       u32 dll_speed;
+       int ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM,
+                                            memory_clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       ibias = rv770_map_clkf_to_ibias(rdev, dividers.whole_fb_div);
+
+       mpll_ad_func_cntl &= ~(CLKR_MASK |
+                              YCLK_POST_DIV_MASK |
+                              CLKF_MASK |
+                              CLKFRAC_MASK |
+                              IBIAS_MASK);
+       mpll_ad_func_cntl |= CLKR(dividers.ref_div);
+       mpll_ad_func_cntl |= YCLK_POST_DIV(dividers.post_div);
+       mpll_ad_func_cntl |= CLKF(dividers.whole_fb_div);
+       mpll_ad_func_cntl |= CLKFRAC(dividers.frac_fb_div);
+       mpll_ad_func_cntl |= IBIAS(ibias);
+
+       if (dividers.vco_mode)
+               mpll_ad_func_cntl_2 |= VCO_MODE;
+       else
+               mpll_ad_func_cntl_2 &= ~VCO_MODE;
+
+       if (pi->mem_gddr5) {
+               mpll_dq_func_cntl &= ~(CLKR_MASK |
+                                      YCLK_POST_DIV_MASK |
+                                      CLKF_MASK |
+                                      CLKFRAC_MASK |
+                                      IBIAS_MASK);
+               mpll_dq_func_cntl |= CLKR(dividers.ref_div);
+               mpll_dq_func_cntl |= YCLK_POST_DIV(dividers.post_div);
+               mpll_dq_func_cntl |= CLKF(dividers.whole_fb_div);
+               mpll_dq_func_cntl |= CLKFRAC(dividers.frac_fb_div);
+               mpll_dq_func_cntl |= IBIAS(ibias);
+
+               if (dividers.vco_mode)
+                       mpll_dq_func_cntl_2 |= VCO_MODE;
+               else
+                       mpll_dq_func_cntl_2 &= ~VCO_MODE;
+       }
+
+       if (pi->mclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 vco_freq = memory_clock * dividers.post_div;
+
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_MEMORY_SS, vco_freq)) {
+                       u32 reference_clock = rdev->clock.mpll.reference_freq;
+                       u32 decoded_ref = rv740_get_decoded_reference_divider(dividers.ref_div);
+                       u32 clk_s = reference_clock * 5 / (decoded_ref * ss.rate);
+                       u32 clk_v = 0x40000 * ss.percentage *
+                               (dividers.whole_fb_div + (dividers.frac_fb_div / 8)) / (clk_s * 10000);
+
+                       mpll_ss1 &= ~CLKV_MASK;
+                       mpll_ss1 |= CLKV(clk_v);
+
+                       mpll_ss2 &= ~CLKS_MASK;
+                       mpll_ss2 |= CLKS(clk_s);
+               }
+       }
+
+       dll_speed = rv740_get_dll_speed(pi->mem_gddr5,
+                                       memory_clock);
+
+       mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK;
+       mclk_pwrmgt_cntl |= DLL_SPEED(dll_speed);
+
+       mclk->mclk770.mclk_value = cpu_to_be32(memory_clock);
+       mclk->mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
+       mclk->mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2);
+       mclk->mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
+       mclk->mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2);
+       mclk->mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+       mclk->mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl);
+       mclk->mclk770.vMPLL_SS = cpu_to_be32(mpll_ss1);
+       mclk->mclk770.vMPLL_SS2 = cpu_to_be32(mpll_ss2);
+
+       return 0;
+}
+
+void rv740_read_clock_registers(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       pi->clk_regs.rv770.cg_spll_func_cntl =
+               RREG32(CG_SPLL_FUNC_CNTL);
+       pi->clk_regs.rv770.cg_spll_func_cntl_2 =
+               RREG32(CG_SPLL_FUNC_CNTL_2);
+       pi->clk_regs.rv770.cg_spll_func_cntl_3 =
+               RREG32(CG_SPLL_FUNC_CNTL_3);
+       pi->clk_regs.rv770.cg_spll_spread_spectrum =
+               RREG32(CG_SPLL_SPREAD_SPECTRUM);
+       pi->clk_regs.rv770.cg_spll_spread_spectrum_2 =
+               RREG32(CG_SPLL_SPREAD_SPECTRUM_2);
+
+       pi->clk_regs.rv770.mpll_ad_func_cntl =
+               RREG32(MPLL_AD_FUNC_CNTL);
+       pi->clk_regs.rv770.mpll_ad_func_cntl_2 =
+               RREG32(MPLL_AD_FUNC_CNTL_2);
+       pi->clk_regs.rv770.mpll_dq_func_cntl =
+               RREG32(MPLL_DQ_FUNC_CNTL);
+       pi->clk_regs.rv770.mpll_dq_func_cntl_2 =
+               RREG32(MPLL_DQ_FUNC_CNTL_2);
+       pi->clk_regs.rv770.mclk_pwrmgt_cntl =
+               RREG32(MCLK_PWRMGT_CNTL);
+       pi->clk_regs.rv770.dll_cntl = RREG32(DLL_CNTL);
+       pi->clk_regs.rv770.mpll_ss1 = RREG32(MPLL_SS1);
+       pi->clk_regs.rv770.mpll_ss2 = RREG32(MPLL_SS2);
+}
+
+int rv740_populate_smc_acpi_state(struct radeon_device *rdev,
+                                 RV770_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 mpll_ad_func_cntl = pi->clk_regs.rv770.mpll_ad_func_cntl;
+       u32 mpll_ad_func_cntl_2 = pi->clk_regs.rv770.mpll_ad_func_cntl_2;
+       u32 mpll_dq_func_cntl = pi->clk_regs.rv770.mpll_dq_func_cntl;
+       u32 mpll_dq_func_cntl_2 = pi->clk_regs.rv770.mpll_dq_func_cntl_2;
+       u32 spll_func_cntl = pi->clk_regs.rv770.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 = pi->clk_regs.rv770.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 = pi->clk_regs.rv770.cg_spll_func_cntl_3;
+       u32 mclk_pwrmgt_cntl = pi->clk_regs.rv770.mclk_pwrmgt_cntl;
+       u32 dll_cntl = pi->clk_regs.rv770.dll_cntl;
+
+       table->ACPIState = table->initialState;
+
+       table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       if (pi->acpi_vddc) {
+               rv770_populate_vddc_value(rdev, pi->acpi_vddc,
+                                         &table->ACPIState.levels[0].vddc);
+               table->ACPIState.levels[0].gen2PCIE =
+                       pi->pcie_gen2 ?
+                       pi->acpi_pcie_gen2 : 0;
+               table->ACPIState.levels[0].gen2XSP =
+                       pi->acpi_pcie_gen2;
+       } else {
+               rv770_populate_vddc_value(rdev, pi->min_vddc_in_table,
+                                         &table->ACPIState.levels[0].vddc);
+               table->ACPIState.levels[0].gen2PCIE = 0;
+       }
+
+       mpll_ad_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN;
+
+       mpll_dq_func_cntl_2 |= BYPASS | BIAS_GEN_PDNB | RESET_EN;
+
+       mclk_pwrmgt_cntl |= (MRDCKA0_RESET |
+                            MRDCKA1_RESET |
+                            MRDCKB0_RESET |
+                            MRDCKB1_RESET |
+                            MRDCKC0_RESET |
+                            MRDCKC1_RESET |
+                            MRDCKD0_RESET |
+                            MRDCKD1_RESET);
+
+       dll_cntl |= (MRDCKA0_BYPASS |
+                    MRDCKA1_BYPASS |
+                    MRDCKB0_BYPASS |
+                    MRDCKB1_BYPASS |
+                    MRDCKC0_BYPASS |
+                    MRDCKC1_BYPASS |
+                    MRDCKD0_BYPASS |
+                    MRDCKD1_BYPASS);
+
+       spll_func_cntl |= SPLL_RESET | SPLL_SLEEP | SPLL_BYPASS_EN;
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(4);
+
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2);
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2);
+       table->ACPIState.levels[0].mclk.mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+       table->ACPIState.levels[0].mclk.mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl);
+
+       table->ACPIState.levels[0].mclk.mclk770.mclk_value = 0;
+
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3);
+
+       table->ACPIState.levels[0].sclk.sclk_value = 0;
+
+       table->ACPIState.levels[1] = table->ACPIState.levels[0];
+       table->ACPIState.levels[2] = table->ACPIState.levels[0];
+
+       rv770_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd);
+
+       return 0;
+}
+
+void rv740_enable_mclk_spread_spectrum(struct radeon_device *rdev,
+                                      bool enable)
+{
+       if (enable)
+               WREG32_P(MPLL_CNTL_MODE, SS_SSEN, ~SS_SSEN);
+       else
+               WREG32_P(MPLL_CNTL_MODE, 0, ~SS_SSEN);
+}
+
+u8 rv740_get_mclk_frequency_ratio(u32 memory_clock)
+{
+       u8 mc_para_index;
+
+       if ((memory_clock < 10000) || (memory_clock > 47500))
+               mc_para_index = 0x00;
+       else
+               mc_para_index = (u8)((memory_clock - 10000) / 2500);
+
+       return mc_para_index;
+}
diff --git a/drivers/gpu/drm/radeon/rv740d.h b/drivers/gpu/drm/radeon/rv740d.h
new file mode 100644 (file)
index 0000000..fe5ab07
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef RV740_H
+#define RV740_H
+
+#define        CG_SPLL_FUNC_CNTL                               0x600
+#define                SPLL_RESET                              (1 << 0)
+#define                SPLL_SLEEP                              (1 << 1)
+#define                SPLL_BYPASS_EN                          (1 << 3)
+#define                SPLL_REF_DIV(x)                         ((x) << 4)
+#define                SPLL_REF_DIV_MASK                       (0x3f << 4)
+#define                SPLL_PDIV_A(x)                          ((x) << 20)
+#define                SPLL_PDIV_A_MASK                        (0x7f << 20)
+#define        CG_SPLL_FUNC_CNTL_2                             0x604
+#define                SCLK_MUX_SEL(x)                         ((x) << 0)
+#define                SCLK_MUX_SEL_MASK                       (0x1ff << 0)
+#define        CG_SPLL_FUNC_CNTL_3                             0x608
+#define                SPLL_FB_DIV(x)                          ((x) << 0)
+#define                SPLL_FB_DIV_MASK                        (0x3ffffff << 0)
+#define                SPLL_DITHEN                             (1 << 28)
+
+#define        MPLL_CNTL_MODE                                  0x61c
+#define                SS_SSEN                                 (1 << 24)
+
+#define        MPLL_AD_FUNC_CNTL                               0x624
+#define                CLKF(x)                                 ((x) << 0)
+#define                CLKF_MASK                               (0x7f << 0)
+#define                CLKR(x)                                 ((x) << 7)
+#define                CLKR_MASK                               (0x1f << 7)
+#define                CLKFRAC(x)                              ((x) << 12)
+#define                CLKFRAC_MASK                            (0x1f << 12)
+#define                YCLK_POST_DIV(x)                        ((x) << 17)
+#define                YCLK_POST_DIV_MASK                      (3 << 17)
+#define                IBIAS(x)                                ((x) << 20)
+#define                IBIAS_MASK                              (0x3ff << 20)
+#define                RESET                                   (1 << 30)
+#define                PDNB                                    (1 << 31)
+#define        MPLL_AD_FUNC_CNTL_2                             0x628
+#define                BYPASS                                  (1 << 19)
+#define                BIAS_GEN_PDNB                           (1 << 24)
+#define                RESET_EN                                (1 << 25)
+#define                VCO_MODE                                (1 << 29)
+#define        MPLL_DQ_FUNC_CNTL                               0x62c
+#define        MPLL_DQ_FUNC_CNTL_2                             0x630
+
+#define        MCLK_PWRMGT_CNTL                                0x648
+#define                DLL_SPEED(x)                            ((x) << 0)
+#define                DLL_SPEED_MASK                          (0x1f << 0)
+#       define MPLL_PWRMGT_OFF                          (1 << 5)
+#       define DLL_READY                                (1 << 6)
+#       define MC_INT_CNTL                              (1 << 7)
+#       define MRDCKA0_SLEEP                            (1 << 8)
+#       define MRDCKA1_SLEEP                            (1 << 9)
+#       define MRDCKB0_SLEEP                            (1 << 10)
+#       define MRDCKB1_SLEEP                            (1 << 11)
+#       define MRDCKC0_SLEEP                            (1 << 12)
+#       define MRDCKC1_SLEEP                            (1 << 13)
+#       define MRDCKD0_SLEEP                            (1 << 14)
+#       define MRDCKD1_SLEEP                            (1 << 15)
+#       define MRDCKA0_RESET                            (1 << 16)
+#       define MRDCKA1_RESET                            (1 << 17)
+#       define MRDCKB0_RESET                            (1 << 18)
+#       define MRDCKB1_RESET                            (1 << 19)
+#       define MRDCKC0_RESET                            (1 << 20)
+#       define MRDCKC1_RESET                            (1 << 21)
+#       define MRDCKD0_RESET                            (1 << 22)
+#       define MRDCKD1_RESET                            (1 << 23)
+#       define DLL_READY_READ                           (1 << 24)
+#       define USE_DISPLAY_GAP                          (1 << 25)
+#       define USE_DISPLAY_URGENT_NORMAL                (1 << 26)
+#       define MPLL_TURNOFF_D2                          (1 << 28)
+#define        DLL_CNTL                                        0x64c
+#       define MRDCKA0_BYPASS                           (1 << 24)
+#       define MRDCKA1_BYPASS                           (1 << 25)
+#       define MRDCKB0_BYPASS                           (1 << 26)
+#       define MRDCKB1_BYPASS                           (1 << 27)
+#       define MRDCKC0_BYPASS                           (1 << 28)
+#       define MRDCKC1_BYPASS                           (1 << 29)
+#       define MRDCKD0_BYPASS                           (1 << 30)
+#       define MRDCKD1_BYPASS                           (1 << 31)
+
+#define        CG_SPLL_SPREAD_SPECTRUM                         0x790
+#define                SSEN                                    (1 << 0)
+#define                CLK_S(x)                                ((x) << 4)
+#define                CLK_S_MASK                              (0xfff << 4)
+#define        CG_SPLL_SPREAD_SPECTRUM_2                       0x794
+#define                CLK_V(x)                                ((x) << 0)
+#define                CLK_V_MASK                              (0x3ffffff << 0)
+
+#define        MPLL_SS1                                        0x85c
+#define                CLKV(x)                                 ((x) << 0)
+#define                CLKV_MASK                               (0x3ffffff << 0)
+#define        MPLL_SS2                                        0x860
+#define                CLKS(x)                                 ((x) << 0)
+#define                CLKS_MASK                               (0xfff << 0)
+
+#endif
diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c
new file mode 100644 (file)
index 0000000..d914e04
--- /dev/null
@@ -0,0 +1,2521 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "rv770d.h"
+#include "r600_dpm.h"
+#include "rv770_dpm.h"
+#include "cypress_dpm.h"
+#include "atom.h"
+#include <linux/seq_file.h>
+
+#define MC_CG_ARB_FREQ_F0           0x0a
+#define MC_CG_ARB_FREQ_F1           0x0b
+#define MC_CG_ARB_FREQ_F2           0x0c
+#define MC_CG_ARB_FREQ_F3           0x0d
+
+#define MC_CG_SEQ_DRAMCONF_S0       0x05
+#define MC_CG_SEQ_DRAMCONF_S1       0x06
+
+#define PCIE_BUS_CLK                10000
+#define TCLK                        (PCIE_BUS_CLK / 10)
+
+#define SMC_RAM_END 0xC000
+
+struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps)
+{
+       struct rv7xx_ps *ps = rps->ps_priv;
+
+       return ps;
+}
+
+struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rdev->pm.dpm.priv;
+
+       return pi;
+}
+
+struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *pi = rdev->pm.dpm.priv;
+
+       return pi;
+}
+
+static void rv770_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev,
+                                              bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 tmp;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+       if (enable) {
+               tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
+               tmp |= LC_HW_VOLTAGE_IF_CONTROL(1);
+               tmp |= LC_GEN2_EN_STRAP;
+       } else {
+               if (!pi->boot_in_gen2) {
+                       tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
+                       tmp &= ~LC_GEN2_EN_STRAP;
+               }
+       }
+       if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) ||
+           (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2))
+               WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+
+}
+
+static void rv770_enable_l0s(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL) & ~LC_L0S_INACTIVITY_MASK;
+       tmp |= LC_L0S_INACTIVITY(3);
+       WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp);
+}
+
+static void rv770_enable_l1(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL);
+       tmp &= ~LC_L1_INACTIVITY_MASK;
+       tmp |= LC_L1_INACTIVITY(4);
+       tmp &= ~LC_PMI_TO_L1_DIS;
+       tmp &= ~LC_ASPM_TO_L1_DIS;
+       WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp);
+}
+
+static void rv770_enable_pll_sleep_in_l1(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL) & ~LC_L1_INACTIVITY_MASK;
+       tmp |= LC_L1_INACTIVITY(8);
+       WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp);
+
+       /* NOTE, this is a PCIE indirect reg, not PCIE PORT */
+       tmp = RREG32_PCIE(PCIE_P_CNTL);
+       tmp |= P_PLL_PWRDN_IN_L1L23;
+       tmp &= ~P_PLL_BUF_PDNB;
+       tmp &= ~P_PLL_PDNB;
+       tmp |= P_ALLOW_PRX_FRONTEND_SHUTOFF;
+       WREG32_PCIE(PCIE_P_CNTL, tmp);
+}
+
+static void rv770_gfx_clock_gating_enable(struct radeon_device *rdev,
+                                         bool enable)
+{
+       if (enable)
+               WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN);
+       else {
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN);
+               WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON);
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON);
+               RREG32(GB_TILING_CONFIG);
+       }
+}
+
+static void rv770_mg_clock_gating_enable(struct radeon_device *rdev,
+                                        bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (enable) {
+               u32 mgcg_cgtt_local0;
+
+               if (rdev->family == CHIP_RV770)
+                       mgcg_cgtt_local0 = RV770_MGCGTTLOCAL0_DFLT;
+               else
+                       mgcg_cgtt_local0 = RV7XX_MGCGTTLOCAL0_DFLT;
+
+               WREG32(CG_CGTT_LOCAL_0, mgcg_cgtt_local0);
+               WREG32(CG_CGTT_LOCAL_1, (RV770_MGCGTTLOCAL1_DFLT & 0xFFFFCFFF));
+
+               if (pi->mgcgtssm)
+                       WREG32(CGTS_SM_CTRL_REG, RV770_MGCGCGTSSMCTRL_DFLT);
+       } else {
+               WREG32(CG_CGTT_LOCAL_0, 0xFFFFFFFF);
+               WREG32(CG_CGTT_LOCAL_1, 0xFFFFCFFF);
+       }
+}
+
+void rv770_restore_cgcg(struct radeon_device *rdev)
+{
+       bool dpm_en = false, cg_en = false;
+
+       if (RREG32(GENERAL_PWRMGT) & GLOBAL_PWRMGT_EN)
+               dpm_en = true;
+       if (RREG32(SCLK_PWRMGT_CNTL) & DYN_GFX_CLK_OFF_EN)
+               cg_en = true;
+
+       if (dpm_en && !cg_en)
+               WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN);
+}
+
+static void rv770_start_dpm(struct radeon_device *rdev)
+{
+       WREG32_P(SCLK_PWRMGT_CNTL, 0, ~SCLK_PWRMGT_OFF);
+
+       WREG32_P(MCLK_PWRMGT_CNTL, 0, ~MPLL_PWRMGT_OFF);
+
+       WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN);
+}
+
+void rv770_stop_dpm(struct radeon_device *rdev)
+{
+       PPSMC_Result result;
+
+       result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_TwoLevelsDisabled);
+
+       if (result != PPSMC_Result_OK)
+               DRM_ERROR("Could not force DPM to low.\n");
+
+       WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN);
+
+       WREG32_P(SCLK_PWRMGT_CNTL, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF);
+
+       WREG32_P(MCLK_PWRMGT_CNTL, MPLL_PWRMGT_OFF, ~MPLL_PWRMGT_OFF);
+}
+
+bool rv770_dpm_enabled(struct radeon_device *rdev)
+{
+       if (RREG32(GENERAL_PWRMGT) & GLOBAL_PWRMGT_EN)
+               return true;
+       else
+               return false;
+}
+
+void rv770_enable_thermal_protection(struct radeon_device *rdev,
+                                    bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
+       else
+               WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
+}
+
+void rv770_enable_acpi_pm(struct radeon_device *rdev)
+{
+       WREG32_P(GENERAL_PWRMGT, STATIC_PM_EN, ~STATIC_PM_EN);
+}
+
+u8 rv770_get_seq_value(struct radeon_device *rdev,
+                      struct rv7xx_pl *pl)
+{
+       return (pl->flags & ATOM_PPLIB_R600_FLAGS_LOWPOWER) ?
+               MC_CG_SEQ_DRAMCONF_S0 : MC_CG_SEQ_DRAMCONF_S1;
+}
+
+int rv770_read_smc_soft_register(struct radeon_device *rdev,
+                                u16 reg_offset, u32 *value)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       return rv770_read_smc_sram_dword(rdev,
+                                        pi->soft_regs_start + reg_offset,
+                                        value, pi->sram_end);
+}
+
+int rv770_write_smc_soft_register(struct radeon_device *rdev,
+                                 u16 reg_offset, u32 value)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       return rv770_write_smc_sram_dword(rdev,
+                                         pi->soft_regs_start + reg_offset,
+                                         value, pi->sram_end);
+}
+
+int rv770_populate_smc_t(struct radeon_device *rdev,
+                        struct radeon_ps *radeon_state,
+                        RV770_SMC_SWSTATE *smc_state)
+{
+       struct rv7xx_ps *state = rv770_get_ps(radeon_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       int i;
+       int a_n;
+       int a_d;
+       u8 l[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE];
+       u8 r[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE];
+       u32 a_t;
+
+       l[0] = 0;
+       r[2] = 100;
+
+       a_n = (int)state->medium.sclk * pi->lmp +
+               (int)state->low.sclk * (R600_AH_DFLT - pi->rlp);
+       a_d = (int)state->low.sclk * (100 - (int)pi->rlp) +
+               (int)state->medium.sclk * pi->lmp;
+
+       l[1] = (u8)(pi->lmp - (int)pi->lmp * a_n / a_d);
+       r[0] = (u8)(pi->rlp + (100 - (int)pi->rlp) * a_n / a_d);
+
+       a_n = (int)state->high.sclk * pi->lhp + (int)state->medium.sclk *
+               (R600_AH_DFLT - pi->rmp);
+       a_d = (int)state->medium.sclk * (100 - (int)pi->rmp) +
+               (int)state->high.sclk * pi->lhp;
+
+       l[2] = (u8)(pi->lhp - (int)pi->lhp * a_n / a_d);
+       r[1] = (u8)(pi->rmp + (100 - (int)pi->rmp) * a_n / a_d);
+
+       for (i = 0; i < (RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1); i++) {
+               a_t = CG_R(r[i] * pi->bsp / 200) | CG_L(l[i] * pi->bsp / 200);
+               smc_state->levels[i].aT = cpu_to_be32(a_t);
+       }
+
+       a_t = CG_R(r[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1] * pi->pbsp / 200) |
+               CG_L(l[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1] * pi->pbsp / 200);
+
+       smc_state->levels[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1].aT =
+               cpu_to_be32(a_t);
+
+       return 0;
+}
+
+int rv770_populate_smc_sp(struct radeon_device *rdev,
+                         struct radeon_ps *radeon_state,
+                         RV770_SMC_SWSTATE *smc_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       int i;
+
+       for (i = 0; i < (RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1); i++)
+               smc_state->levels[i].bSP = cpu_to_be32(pi->dsp);
+
+       smc_state->levels[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1].bSP =
+               cpu_to_be32(pi->psp);
+
+       return 0;
+}
+
+static void rv770_calculate_fractional_mpll_feedback_divider(u32 memory_clock,
+                                                            u32 reference_clock,
+                                                            bool gddr5,
+                                                            struct atom_clock_dividers *dividers,
+                                                            u32 *clkf,
+                                                            u32 *clkfrac)
+{
+       u32 post_divider, reference_divider, feedback_divider8;
+       u32 fyclk;
+
+       if (gddr5)
+               fyclk = (memory_clock * 8) / 2;
+       else
+               fyclk = (memory_clock * 4) / 2;
+
+       post_divider = dividers->post_div;
+       reference_divider = dividers->ref_div;
+
+       feedback_divider8 =
+               (8 * fyclk * reference_divider * post_divider) / reference_clock;
+
+       *clkf = feedback_divider8 / 8;
+       *clkfrac = feedback_divider8 % 8;
+}
+
+static int rv770_encode_yclk_post_div(u32 postdiv, u32 *encoded_postdiv)
+{
+       int ret = 0;
+
+       switch (postdiv) {
+        case 1:
+               *encoded_postdiv = 0;
+               break;
+        case 2:
+               *encoded_postdiv = 1;
+               break;
+        case 4:
+               *encoded_postdiv = 2;
+               break;
+        case 8:
+               *encoded_postdiv = 3;
+               break;
+        case 16:
+               *encoded_postdiv = 4;
+               break;
+        default:
+               ret = -EINVAL;
+               break;
+       }
+
+    return ret;
+}
+
+u32 rv770_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf)
+{
+       if (clkf <= 0x10)
+               return 0x4B;
+       if (clkf <= 0x19)
+               return 0x5B;
+       if (clkf <= 0x21)
+               return 0x2B;
+       if (clkf <= 0x27)
+               return 0x6C;
+       if (clkf <= 0x31)
+               return 0x9D;
+       return 0xC6;
+}
+
+static int rv770_populate_mclk_value(struct radeon_device *rdev,
+                                    u32 engine_clock, u32 memory_clock,
+                                    RV7XX_SMC_MCLK_VALUE *mclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u8 encoded_reference_dividers[] = { 0, 16, 17, 20, 21 };
+       u32 mpll_ad_func_cntl =
+               pi->clk_regs.rv770.mpll_ad_func_cntl;
+       u32 mpll_ad_func_cntl_2 =
+               pi->clk_regs.rv770.mpll_ad_func_cntl_2;
+       u32 mpll_dq_func_cntl =
+               pi->clk_regs.rv770.mpll_dq_func_cntl;
+       u32 mpll_dq_func_cntl_2 =
+               pi->clk_regs.rv770.mpll_dq_func_cntl_2;
+       u32 mclk_pwrmgt_cntl =
+               pi->clk_regs.rv770.mclk_pwrmgt_cntl;
+       u32 dll_cntl = pi->clk_regs.rv770.dll_cntl;
+       struct atom_clock_dividers dividers;
+       u32 reference_clock = rdev->clock.mpll.reference_freq;
+       u32 clkf, clkfrac;
+       u32 postdiv_yclk;
+       u32 ibias;
+       int ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM,
+                                            memory_clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       if ((dividers.ref_div < 1) || (dividers.ref_div > 5))
+               return -EINVAL;
+
+       rv770_calculate_fractional_mpll_feedback_divider(memory_clock, reference_clock,
+                                                        pi->mem_gddr5,
+                                                        &dividers, &clkf, &clkfrac);
+
+       ret = rv770_encode_yclk_post_div(dividers.post_div, &postdiv_yclk);
+       if (ret)
+               return ret;
+
+       ibias = rv770_map_clkf_to_ibias(rdev, clkf);
+
+       mpll_ad_func_cntl &= ~(CLKR_MASK |
+                              YCLK_POST_DIV_MASK |
+                              CLKF_MASK |
+                              CLKFRAC_MASK |
+                              IBIAS_MASK);
+       mpll_ad_func_cntl |= CLKR(encoded_reference_dividers[dividers.ref_div - 1]);
+       mpll_ad_func_cntl |= YCLK_POST_DIV(postdiv_yclk);
+       mpll_ad_func_cntl |= CLKF(clkf);
+       mpll_ad_func_cntl |= CLKFRAC(clkfrac);
+       mpll_ad_func_cntl |= IBIAS(ibias);
+
+       if (dividers.vco_mode)
+               mpll_ad_func_cntl_2 |= VCO_MODE;
+       else
+               mpll_ad_func_cntl_2 &= ~VCO_MODE;
+
+       if (pi->mem_gddr5) {
+               rv770_calculate_fractional_mpll_feedback_divider(memory_clock,
+                                                                reference_clock,
+                                                                pi->mem_gddr5,
+                                                                &dividers, &clkf, &clkfrac);
+
+               ibias = rv770_map_clkf_to_ibias(rdev, clkf);
+
+               ret = rv770_encode_yclk_post_div(dividers.post_div, &postdiv_yclk);
+               if (ret)
+                       return ret;
+
+               mpll_dq_func_cntl &= ~(CLKR_MASK |
+                                      YCLK_POST_DIV_MASK |
+                                      CLKF_MASK |
+                                      CLKFRAC_MASK |
+                                      IBIAS_MASK);
+               mpll_dq_func_cntl |= CLKR(encoded_reference_dividers[dividers.ref_div - 1]);
+               mpll_dq_func_cntl |= YCLK_POST_DIV(postdiv_yclk);
+               mpll_dq_func_cntl |= CLKF(clkf);
+               mpll_dq_func_cntl |= CLKFRAC(clkfrac);
+               mpll_dq_func_cntl |= IBIAS(ibias);
+
+               if (dividers.vco_mode)
+                       mpll_dq_func_cntl_2 |= VCO_MODE;
+               else
+                       mpll_dq_func_cntl_2 &= ~VCO_MODE;
+       }
+
+       mclk->mclk770.mclk_value = cpu_to_be32(memory_clock);
+       mclk->mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
+       mclk->mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2);
+       mclk->mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
+       mclk->mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2);
+       mclk->mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+       mclk->mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl);
+
+       return 0;
+}
+
+static int rv770_populate_sclk_value(struct radeon_device *rdev,
+                                    u32 engine_clock,
+                                    RV770_SMC_SCLK_VALUE *sclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct atom_clock_dividers dividers;
+       u32 spll_func_cntl =
+               pi->clk_regs.rv770.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 =
+               pi->clk_regs.rv770.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 =
+               pi->clk_regs.rv770.cg_spll_func_cntl_3;
+       u32 cg_spll_spread_spectrum =
+               pi->clk_regs.rv770.cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2 =
+               pi->clk_regs.rv770.cg_spll_spread_spectrum_2;
+       u64 tmp;
+       u32 reference_clock = rdev->clock.spll.reference_freq;
+       u32 reference_divider, post_divider;
+       u32 fbdiv;
+       int ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            engine_clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       reference_divider = 1 + dividers.ref_div;
+
+       if (dividers.enable_post_div)
+               post_divider = (0x0f & (dividers.post_div >> 4)) + (0x0f & dividers.post_div) + 2;
+       else
+               post_divider = 1;
+
+       tmp = (u64) engine_clock * reference_divider * post_divider * 16384;
+       do_div(tmp, reference_clock);
+       fbdiv = (u32) tmp;
+
+       if (dividers.enable_post_div)
+               spll_func_cntl |= SPLL_DIVEN;
+       else
+               spll_func_cntl &= ~SPLL_DIVEN;
+       spll_func_cntl &= ~(SPLL_HILEN_MASK | SPLL_LOLEN_MASK | SPLL_REF_DIV_MASK);
+       spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div);
+       spll_func_cntl |= SPLL_HILEN((dividers.post_div >> 4) & 0xf);
+       spll_func_cntl |= SPLL_LOLEN(dividers.post_div & 0xf);
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(2);
+
+       spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK;
+       spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv);
+       spll_func_cntl_3 |= SPLL_DITHEN;
+
+       if (pi->sclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 vco_freq = engine_clock * post_divider;
+
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
+                       u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
+                       u32 clk_v = ss.percentage * fbdiv / (clk_s * 10000);
+
+                       cg_spll_spread_spectrum &= ~CLKS_MASK;
+                       cg_spll_spread_spectrum |= CLKS(clk_s);
+                       cg_spll_spread_spectrum |= SSEN;
+
+                       cg_spll_spread_spectrum_2 &= ~CLKV_MASK;
+                       cg_spll_spread_spectrum_2 |= CLKV(clk_v);
+               }
+       }
+
+       sclk->sclk_value = cpu_to_be32(engine_clock);
+       sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl);
+       sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2);
+       sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3);
+       sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(cg_spll_spread_spectrum);
+       sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(cg_spll_spread_spectrum_2);
+
+       return 0;
+}
+
+int rv770_populate_vddc_value(struct radeon_device *rdev, u16 vddc,
+                             RV770_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       int i;
+
+       if (!pi->voltage_control) {
+               voltage->index = 0;
+               voltage->value = 0;
+               return 0;
+       }
+
+       for (i = 0; i < pi->valid_vddc_entries; i++) {
+               if (vddc <= pi->vddc_table[i].vddc) {
+                       voltage->index = pi->vddc_table[i].vddc_index;
+                       voltage->value = cpu_to_be16(vddc);
+                       break;
+               }
+       }
+
+       if (i == pi->valid_vddc_entries)
+               return -EINVAL;
+
+       return 0;
+}
+
+int rv770_populate_mvdd_value(struct radeon_device *rdev, u32 mclk,
+                             RV770_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (!pi->mvdd_control) {
+               voltage->index = MVDD_HIGH_INDEX;
+               voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
+               return 0;
+       }
+
+       if (mclk <= pi->mvdd_split_frequency) {
+               voltage->index = MVDD_LOW_INDEX;
+               voltage->value = cpu_to_be16(MVDD_LOW_VALUE);
+       } else {
+               voltage->index = MVDD_HIGH_INDEX;
+               voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
+       }
+
+       return 0;
+}
+
+static int rv770_convert_power_level_to_smc(struct radeon_device *rdev,
+                                           struct rv7xx_pl *pl,
+                                           RV770_SMC_HW_PERFORMANCE_LEVEL *level,
+                                           u8 watermark_level)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       int ret;
+
+       level->gen2PCIE = pi->pcie_gen2 ?
+               ((pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? 1 : 0) : 0;
+       level->gen2XSP  = (pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? 1 : 0;
+       level->backbias = (pl->flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? 1 : 0;
+       level->displayWatermark = watermark_level;
+
+       if (rdev->family == CHIP_RV740)
+               ret = rv740_populate_sclk_value(rdev, pl->sclk,
+                                               &level->sclk);
+       else if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710))
+               ret = rv730_populate_sclk_value(rdev, pl->sclk,
+                                               &level->sclk);
+       else
+               ret = rv770_populate_sclk_value(rdev, pl->sclk,
+                                               &level->sclk);
+       if (ret)
+               return ret;
+
+       if (rdev->family == CHIP_RV740) {
+               if (pi->mem_gddr5) {
+                       if (pl->mclk <= pi->mclk_strobe_mode_threshold)
+                               level->strobeMode =
+                                       rv740_get_mclk_frequency_ratio(pl->mclk) | 0x10;
+                       else
+                               level->strobeMode = 0;
+
+                       if (pl->mclk > pi->mclk_edc_enable_threshold)
+                               level->mcFlags = SMC_MC_EDC_RD_FLAG | SMC_MC_EDC_WR_FLAG;
+                       else
+                               level->mcFlags =  0;
+               }
+               ret = rv740_populate_mclk_value(rdev, pl->sclk,
+                                               pl->mclk, &level->mclk);
+       } else if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710))
+               ret = rv730_populate_mclk_value(rdev, pl->sclk,
+                                               pl->mclk, &level->mclk);
+       else
+               ret = rv770_populate_mclk_value(rdev, pl->sclk,
+                                               pl->mclk, &level->mclk);
+       if (ret)
+               return ret;
+
+       ret = rv770_populate_vddc_value(rdev, pl->vddc,
+                                       &level->vddc);
+       if (ret)
+               return ret;
+
+       ret = rv770_populate_mvdd_value(rdev, pl->mclk, &level->mvdd);
+
+       return ret;
+}
+
+static int rv770_convert_power_state_to_smc(struct radeon_device *rdev,
+                                           struct radeon_ps *radeon_state,
+                                           RV770_SMC_SWSTATE *smc_state)
+{
+       struct rv7xx_ps *state = rv770_get_ps(radeon_state);
+       int ret;
+
+       if (!(radeon_state->caps & ATOM_PPLIB_DISALLOW_ON_DC))
+               smc_state->flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       ret = rv770_convert_power_level_to_smc(rdev,
+                                              &state->low,
+                                              &smc_state->levels[0],
+                                              PPSMC_DISPLAY_WATERMARK_LOW);
+       if (ret)
+               return ret;
+
+       ret = rv770_convert_power_level_to_smc(rdev,
+                                              &state->medium,
+                                              &smc_state->levels[1],
+                                              PPSMC_DISPLAY_WATERMARK_LOW);
+       if (ret)
+               return ret;
+
+       ret = rv770_convert_power_level_to_smc(rdev,
+                                              &state->high,
+                                              &smc_state->levels[2],
+                                              PPSMC_DISPLAY_WATERMARK_HIGH);
+       if (ret)
+               return ret;
+
+       smc_state->levels[0].arbValue = MC_CG_ARB_FREQ_F1;
+       smc_state->levels[1].arbValue = MC_CG_ARB_FREQ_F2;
+       smc_state->levels[2].arbValue = MC_CG_ARB_FREQ_F3;
+
+       smc_state->levels[0].seqValue = rv770_get_seq_value(rdev,
+                                                           &state->low);
+       smc_state->levels[1].seqValue = rv770_get_seq_value(rdev,
+                                                           &state->medium);
+       smc_state->levels[2].seqValue = rv770_get_seq_value(rdev,
+                                                           &state->high);
+
+       rv770_populate_smc_sp(rdev, radeon_state, smc_state);
+
+       return rv770_populate_smc_t(rdev, radeon_state, smc_state);
+
+}
+
+u32 rv770_calculate_memory_refresh_rate(struct radeon_device *rdev,
+                                       u32 engine_clock)
+{
+       u32 dram_rows;
+       u32 dram_refresh_rate;
+       u32 mc_arb_rfsh_rate;
+       u32 tmp;
+
+       tmp = (RREG32(MC_ARB_RAMCFG) & NOOFROWS_MASK) >> NOOFROWS_SHIFT;
+       dram_rows = 1 << (tmp + 10);
+       tmp = RREG32(MC_SEQ_MISC0) & 3;
+       dram_refresh_rate = 1 << (tmp + 3);
+       mc_arb_rfsh_rate = ((engine_clock * 10) * dram_refresh_rate / dram_rows - 32) / 64;
+
+       return mc_arb_rfsh_rate;
+}
+
+static void rv770_program_memory_timing_parameters(struct radeon_device *rdev,
+                                                  struct radeon_ps *radeon_state)
+{
+       struct rv7xx_ps *state = rv770_get_ps(radeon_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 sqm_ratio;
+       u32 arb_refresh_rate;
+       u32 high_clock;
+
+       if (state->high.sclk < (state->low.sclk * 0xFF / 0x40))
+               high_clock = state->high.sclk;
+       else
+               high_clock = (state->low.sclk * 0xFF / 0x40);
+
+       radeon_atom_set_engine_dram_timings(rdev, high_clock,
+                                           state->high.mclk);
+
+       sqm_ratio =
+               STATE0(64 * high_clock / pi->boot_sclk) |
+               STATE1(64 * high_clock / state->low.sclk) |
+               STATE2(64 * high_clock / state->medium.sclk) |
+               STATE3(64 * high_clock / state->high.sclk);
+       WREG32(MC_ARB_SQM_RATIO, sqm_ratio);
+
+       arb_refresh_rate =
+               POWERMODE0(rv770_calculate_memory_refresh_rate(rdev, pi->boot_sclk)) |
+               POWERMODE1(rv770_calculate_memory_refresh_rate(rdev, state->low.sclk)) |
+               POWERMODE2(rv770_calculate_memory_refresh_rate(rdev, state->medium.sclk)) |
+               POWERMODE3(rv770_calculate_memory_refresh_rate(rdev, state->high.sclk));
+       WREG32(MC_ARB_RFSH_RATE, arb_refresh_rate);
+}
+
+void rv770_enable_backbias(struct radeon_device *rdev,
+                          bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, BACKBIAS_PAD_EN, ~BACKBIAS_PAD_EN);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~(BACKBIAS_VALUE | BACKBIAS_PAD_EN));
+}
+
+static void rv770_enable_spread_spectrum(struct radeon_device *rdev,
+                                        bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (enable) {
+               if (pi->sclk_ss)
+                       WREG32_P(GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, ~DYN_SPREAD_SPECTRUM_EN);
+
+               if (pi->mclk_ss) {
+                       if (rdev->family == CHIP_RV740)
+                               rv740_enable_mclk_spread_spectrum(rdev, true);
+               }
+       } else {
+               WREG32_P(CG_SPLL_SPREAD_SPECTRUM, 0, ~SSEN);
+
+               WREG32_P(GENERAL_PWRMGT, 0, ~DYN_SPREAD_SPECTRUM_EN);
+
+               WREG32_P(CG_MPLL_SPREAD_SPECTRUM, 0, ~SSEN);
+
+               if (rdev->family == CHIP_RV740)
+                       rv740_enable_mclk_spread_spectrum(rdev, false);
+       }
+}
+
+static void rv770_program_mpll_timing_parameters(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if ((rdev->family == CHIP_RV770) && !pi->mem_gddr5) {
+               WREG32(MPLL_TIME,
+                      (MPLL_LOCK_TIME(R600_MPLLLOCKTIME_DFLT * pi->ref_div) |
+                       MPLL_RESET_TIME(R600_MPLLRESETTIME_DFLT)));
+       }
+}
+
+void rv770_setup_bsp(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 xclk = radeon_get_xclk(rdev);
+
+       r600_calculate_u_and_p(pi->asi,
+                              xclk,
+                              16,
+                              &pi->bsp,
+                              &pi->bsu);
+
+       r600_calculate_u_and_p(pi->pasi,
+                              xclk,
+                              16,
+                              &pi->pbsp,
+                              &pi->pbsu);
+
+       pi->dsp = BSP(pi->bsp) | BSU(pi->bsu);
+       pi->psp = BSP(pi->pbsp) | BSU(pi->pbsu);
+
+       WREG32(CG_BSP, pi->dsp);
+
+}
+
+void rv770_program_git(struct radeon_device *rdev)
+{
+       WREG32_P(CG_GIT, CG_GICST(R600_GICST_DFLT), ~CG_GICST_MASK);
+}
+
+void rv770_program_tp(struct radeon_device *rdev)
+{
+       int i;
+       enum r600_td td = R600_TD_DFLT;
+
+       for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
+               WREG32(CG_FFCT_0 + (i * 4), (UTC_0(r600_utc[i]) | DTC_0(r600_dtc[i])));
+
+       if (td == R600_TD_AUTO)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_FORCE_TREND_SEL);
+       else
+               WREG32_P(SCLK_PWRMGT_CNTL, FIR_FORCE_TREND_SEL, ~FIR_FORCE_TREND_SEL);
+       if (td == R600_TD_UP)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_TREND_MODE);
+       if (td == R600_TD_DOWN)
+               WREG32_P(SCLK_PWRMGT_CNTL, FIR_TREND_MODE, ~FIR_TREND_MODE);
+}
+
+void rv770_program_tpp(struct radeon_device *rdev)
+{
+       WREG32(CG_TPC, R600_TPC_DFLT);
+}
+
+void rv770_program_sstp(struct radeon_device *rdev)
+{
+       WREG32(CG_SSP, (SSTU(R600_SSTU_DFLT) | SST(R600_SST_DFLT)));
+}
+
+void rv770_program_engine_speed_parameters(struct radeon_device *rdev)
+{
+       WREG32_P(SPLL_CNTL_MODE, SPLL_DIV_SYNC, ~SPLL_DIV_SYNC);
+}
+
+static void rv770_enable_display_gap(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL);
+
+       tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK);
+       tmp |= (DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE) |
+               DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE));
+       WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+}
+
+void rv770_program_vc(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       WREG32(CG_FTV, pi->vrc);
+}
+
+void rv770_clear_vc(struct radeon_device *rdev)
+{
+       WREG32(CG_FTV, 0);
+}
+
+int rv770_upload_firmware(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       int ret;
+
+       rv770_reset_smc(rdev);
+       rv770_stop_smc_clock(rdev);
+
+       ret = rv770_load_smc_ucode(rdev, pi->sram_end);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rv770_populate_smc_acpi_state(struct radeon_device *rdev,
+                                        RV770_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       u32 mpll_ad_func_cntl =
+               pi->clk_regs.rv770.mpll_ad_func_cntl;
+       u32 mpll_ad_func_cntl_2 =
+               pi->clk_regs.rv770.mpll_ad_func_cntl_2;
+       u32 mpll_dq_func_cntl =
+               pi->clk_regs.rv770.mpll_dq_func_cntl;
+       u32 mpll_dq_func_cntl_2 =
+               pi->clk_regs.rv770.mpll_dq_func_cntl_2;
+       u32 spll_func_cntl =
+               pi->clk_regs.rv770.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 =
+               pi->clk_regs.rv770.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 =
+               pi->clk_regs.rv770.cg_spll_func_cntl_3;
+       u32 mclk_pwrmgt_cntl;
+       u32 dll_cntl;
+
+       table->ACPIState = table->initialState;
+
+       table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       if (pi->acpi_vddc) {
+               rv770_populate_vddc_value(rdev, pi->acpi_vddc,
+                                         &table->ACPIState.levels[0].vddc);
+               if (pi->pcie_gen2) {
+                       if (pi->acpi_pcie_gen2)
+                               table->ACPIState.levels[0].gen2PCIE = 1;
+                       else
+                               table->ACPIState.levels[0].gen2PCIE = 0;
+               } else
+                       table->ACPIState.levels[0].gen2PCIE = 0;
+               if (pi->acpi_pcie_gen2)
+                       table->ACPIState.levels[0].gen2XSP = 1;
+               else
+                       table->ACPIState.levels[0].gen2XSP = 0;
+       } else {
+               rv770_populate_vddc_value(rdev, pi->min_vddc_in_table,
+                                         &table->ACPIState.levels[0].vddc);
+               table->ACPIState.levels[0].gen2PCIE = 0;
+       }
+
+
+       mpll_ad_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN;
+
+       mpll_dq_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN;
+
+       mclk_pwrmgt_cntl = (MRDCKA0_RESET |
+                           MRDCKA1_RESET |
+                           MRDCKB0_RESET |
+                           MRDCKB1_RESET |
+                           MRDCKC0_RESET |
+                           MRDCKC1_RESET |
+                           MRDCKD0_RESET |
+                           MRDCKD1_RESET);
+
+       dll_cntl = 0xff000000;
+
+       spll_func_cntl |= SPLL_RESET | SPLL_SLEEP | SPLL_BYPASS_EN;
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(4);
+
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2);
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
+       table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2);
+
+       table->ACPIState.levels[0].mclk.mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+       table->ACPIState.levels[0].mclk.mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl);
+
+       table->ACPIState.levels[0].mclk.mclk770.mclk_value = 0;
+
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3);
+
+       table->ACPIState.levels[0].sclk.sclk_value = 0;
+
+       rv770_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd);
+
+       table->ACPIState.levels[1] = table->ACPIState.levels[0];
+       table->ACPIState.levels[2] = table->ACPIState.levels[0];
+
+       return 0;
+}
+
+int rv770_populate_initial_mvdd_value(struct radeon_device *rdev,
+                                     RV770_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if ((pi->s0_vid_lower_smio_cntl & pi->mvdd_mask_low) ==
+            (pi->mvdd_low_smio[MVDD_LOW_INDEX] & pi->mvdd_mask_low) ) {
+               voltage->index = MVDD_LOW_INDEX;
+               voltage->value = cpu_to_be16(MVDD_LOW_VALUE);
+       } else {
+               voltage->index = MVDD_HIGH_INDEX;
+               voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
+       }
+
+       return 0;
+}
+
+static int rv770_populate_smc_initial_state(struct radeon_device *rdev,
+                                           struct radeon_ps *radeon_state,
+                                           RV770_SMC_STATETABLE *table)
+{
+       struct rv7xx_ps *initial_state = rv770_get_ps(radeon_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 a_t;
+
+       table->initialState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_ad_func_cntl);
+       table->initialState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL_2 =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_ad_func_cntl_2);
+       table->initialState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_dq_func_cntl);
+       table->initialState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL_2 =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_dq_func_cntl_2);
+       table->initialState.levels[0].mclk.mclk770.vMCLK_PWRMGT_CNTL =
+               cpu_to_be32(pi->clk_regs.rv770.mclk_pwrmgt_cntl);
+       table->initialState.levels[0].mclk.mclk770.vDLL_CNTL =
+               cpu_to_be32(pi->clk_regs.rv770.dll_cntl);
+
+       table->initialState.levels[0].mclk.mclk770.vMPLL_SS =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_ss1);
+       table->initialState.levels[0].mclk.mclk770.vMPLL_SS2 =
+               cpu_to_be32(pi->clk_regs.rv770.mpll_ss2);
+
+       table->initialState.levels[0].mclk.mclk770.mclk_value =
+               cpu_to_be32(initial_state->low.mclk);
+
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
+               cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+               cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl_2);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+               cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl_3);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM =
+               cpu_to_be32(pi->clk_regs.rv770.cg_spll_spread_spectrum);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2 =
+               cpu_to_be32(pi->clk_regs.rv770.cg_spll_spread_spectrum_2);
+
+       table->initialState.levels[0].sclk.sclk_value =
+               cpu_to_be32(initial_state->low.sclk);
+
+       table->initialState.levels[0].arbValue = MC_CG_ARB_FREQ_F0;
+
+       table->initialState.levels[0].seqValue =
+               rv770_get_seq_value(rdev, &initial_state->low);
+
+       rv770_populate_vddc_value(rdev,
+                                 initial_state->low.vddc,
+                                 &table->initialState.levels[0].vddc);
+       rv770_populate_initial_mvdd_value(rdev,
+                                         &table->initialState.levels[0].mvdd);
+
+       a_t = CG_R(0xffff) | CG_L(0);
+       table->initialState.levels[0].aT = cpu_to_be32(a_t);
+
+       table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp);
+
+       if (pi->boot_in_gen2)
+               table->initialState.levels[0].gen2PCIE = 1;
+       else
+               table->initialState.levels[0].gen2PCIE = 0;
+       if (initial_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2)
+               table->initialState.levels[0].gen2XSP = 1;
+       else
+               table->initialState.levels[0].gen2XSP = 0;
+
+       if (rdev->family == CHIP_RV740) {
+               if (pi->mem_gddr5) {
+                       if (initial_state->low.mclk <= pi->mclk_strobe_mode_threshold)
+                               table->initialState.levels[0].strobeMode =
+                                       rv740_get_mclk_frequency_ratio(initial_state->low.mclk) | 0x10;
+                       else
+                               table->initialState.levels[0].strobeMode = 0;
+
+                       if (initial_state->low.mclk >= pi->mclk_edc_enable_threshold)
+                               table->initialState.levels[0].mcFlags = SMC_MC_EDC_RD_FLAG | SMC_MC_EDC_WR_FLAG;
+                       else
+                               table->initialState.levels[0].mcFlags =  0;
+               }
+       }
+
+       table->initialState.levels[1] = table->initialState.levels[0];
+       table->initialState.levels[2] = table->initialState.levels[0];
+
+       table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       return 0;
+}
+
+static int rv770_populate_smc_vddc_table(struct radeon_device *rdev,
+                                        RV770_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       int i;
+
+       for (i = 0; i < pi->valid_vddc_entries; i++) {
+               table->highSMIO[pi->vddc_table[i].vddc_index] =
+                       pi->vddc_table[i].high_smio;
+               table->lowSMIO[pi->vddc_table[i].vddc_index] =
+                       cpu_to_be32(pi->vddc_table[i].low_smio);
+       }
+
+       table->voltageMaskTable.highMask[RV770_SMC_VOLTAGEMASK_VDDC] = 0;
+       table->voltageMaskTable.lowMask[RV770_SMC_VOLTAGEMASK_VDDC] =
+               cpu_to_be32(pi->vddc_mask_low);
+
+       for (i = 0;
+            ((i < pi->valid_vddc_entries) &&
+             (pi->max_vddc_in_table >
+              pi->vddc_table[i].vddc));
+            i++);
+
+       table->maxVDDCIndexInPPTable =
+               pi->vddc_table[i].vddc_index;
+
+       return 0;
+}
+
+static int rv770_populate_smc_mvdd_table(struct radeon_device *rdev,
+                                        RV770_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (pi->mvdd_control) {
+               table->lowSMIO[MVDD_HIGH_INDEX] |=
+                       cpu_to_be32(pi->mvdd_low_smio[MVDD_HIGH_INDEX]);
+               table->lowSMIO[MVDD_LOW_INDEX] |=
+                       cpu_to_be32(pi->mvdd_low_smio[MVDD_LOW_INDEX]);
+
+               table->voltageMaskTable.highMask[RV770_SMC_VOLTAGEMASK_MVDD] = 0;
+               table->voltageMaskTable.lowMask[RV770_SMC_VOLTAGEMASK_MVDD] =
+                       cpu_to_be32(pi->mvdd_mask_low);
+       }
+
+       return 0;
+}
+
+static int rv770_init_smc_table(struct radeon_device *rdev,
+                               struct radeon_ps *radeon_boot_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state);
+       RV770_SMC_STATETABLE *table = &pi->smc_statetable;
+       int ret;
+
+       memset(table, 0, sizeof(RV770_SMC_STATETABLE));
+
+       pi->boot_sclk = boot_state->low.sclk;
+
+       rv770_populate_smc_vddc_table(rdev, table);
+       rv770_populate_smc_mvdd_table(rdev, table);
+
+       switch (rdev->pm.int_thermal_type) {
+        case THERMAL_TYPE_RV770:
+        case THERMAL_TYPE_ADT7473_WITH_INTERNAL:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL;
+               break;
+        case THERMAL_TYPE_NONE:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE;
+               break;
+        case THERMAL_TYPE_EXTERNAL_GPIO:
+        default:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL;
+               break;
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC) {
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+               if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_DONT_WAIT_FOR_VBLANK_ON_ALERT)
+                       table->extraFlags |= PPSMC_EXTRAFLAGS_AC2DC_DONT_WAIT_FOR_VBLANK;
+
+               if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_GOTO_BOOT_ON_ALERT)
+                       table->extraFlags |= PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTOINITIALSTATE;
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (pi->mem_gddr5)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+       if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710))
+               ret = rv730_populate_smc_initial_state(rdev, radeon_boot_state, table);
+       else
+               ret = rv770_populate_smc_initial_state(rdev, radeon_boot_state, table);
+       if (ret)
+               return ret;
+
+       if (rdev->family == CHIP_RV740)
+               ret = rv740_populate_smc_acpi_state(rdev, table);
+       else if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710))
+               ret = rv730_populate_smc_acpi_state(rdev, table);
+       else
+               ret = rv770_populate_smc_acpi_state(rdev, table);
+       if (ret)
+               return ret;
+
+       table->driverState = table->initialState;
+
+       return rv770_copy_bytes_to_smc(rdev,
+                                      pi->state_table_start,
+                                      (const u8 *)table,
+                                      sizeof(RV770_SMC_STATETABLE),
+                                      pi->sram_end);
+}
+
+static int rv770_construct_vddc_table(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u16 min, max, step;
+       u32 steps = 0;
+       u8 vddc_index = 0;
+       u32 i;
+
+       radeon_atom_get_min_voltage(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, &min);
+       radeon_atom_get_max_voltage(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, &max);
+       radeon_atom_get_voltage_step(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, &step);
+
+       steps = (max - min) / step + 1;
+
+       if (steps > MAX_NO_VREG_STEPS)
+               return -EINVAL;
+
+       for (i = 0; i < steps; i++) {
+               u32 gpio_pins, gpio_mask;
+
+               pi->vddc_table[i].vddc = (u16)(min + i * step);
+               radeon_atom_get_voltage_gpio_settings(rdev,
+                                                     pi->vddc_table[i].vddc,
+                                                     SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                                     &gpio_pins, &gpio_mask);
+               pi->vddc_table[i].low_smio = gpio_pins & gpio_mask;
+               pi->vddc_table[i].high_smio = 0;
+               pi->vddc_mask_low = gpio_mask;
+               if (i > 0) {
+                       if ((pi->vddc_table[i].low_smio !=
+                            pi->vddc_table[i - 1].low_smio ) ||
+                            (pi->vddc_table[i].high_smio !=
+                             pi->vddc_table[i - 1].high_smio))
+                               vddc_index++;
+               }
+               pi->vddc_table[i].vddc_index = vddc_index;
+       }
+
+       pi->valid_vddc_entries = (u8)steps;
+
+       return 0;
+}
+
+static u32 rv770_get_mclk_split_point(struct atom_memory_info *memory_info)
+{
+       if (memory_info->mem_type == MEM_TYPE_GDDR3)
+               return 30000;
+
+       return 0;
+}
+
+static int rv770_get_mvdd_pin_configuration(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 gpio_pins, gpio_mask;
+
+       radeon_atom_get_voltage_gpio_settings(rdev,
+                                             MVDD_HIGH_VALUE, SET_VOLTAGE_TYPE_ASIC_MVDDC,
+                                             &gpio_pins, &gpio_mask);
+       pi->mvdd_mask_low = gpio_mask;
+       pi->mvdd_low_smio[MVDD_HIGH_INDEX] =
+               gpio_pins & gpio_mask;
+
+       radeon_atom_get_voltage_gpio_settings(rdev,
+                                             MVDD_LOW_VALUE, SET_VOLTAGE_TYPE_ASIC_MVDDC,
+                                             &gpio_pins, &gpio_mask);
+       pi->mvdd_low_smio[MVDD_LOW_INDEX] =
+               gpio_pins & gpio_mask;
+
+       return 0;
+}
+
+u8 rv770_get_memory_module_index(struct radeon_device *rdev)
+{
+       return (u8) ((RREG32(BIOS_SCRATCH_4) >> 16) & 0xff);
+}
+
+static int rv770_get_mvdd_configuration(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u8 memory_module_index;
+       struct atom_memory_info memory_info;
+
+       memory_module_index = rv770_get_memory_module_index(rdev);
+
+       if (radeon_atom_get_memory_info(rdev, memory_module_index, &memory_info)) {
+               pi->mvdd_control = false;
+               return 0;
+       }
+
+       pi->mvdd_split_frequency =
+               rv770_get_mclk_split_point(&memory_info);
+
+       if (pi->mvdd_split_frequency == 0) {
+               pi->mvdd_control = false;
+               return 0;
+       }
+
+       return rv770_get_mvdd_pin_configuration(rdev);
+}
+
+void rv770_enable_voltage_control(struct radeon_device *rdev,
+                                 bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, VOLT_PWRMGT_EN, ~VOLT_PWRMGT_EN);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~VOLT_PWRMGT_EN);
+}
+
+static void rv770_program_display_gap(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL);
+
+       tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK);
+       if (rdev->pm.dpm.new_active_crtcs & 1) {
+               tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK);
+               tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
+       } else if (rdev->pm.dpm.new_active_crtcs & 2) {
+               tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
+               tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK);
+       } else {
+               tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
+               tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
+       }
+       WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+}
+
+static void rv770_enable_dynamic_pcie_gen2(struct radeon_device *rdev,
+                                          bool enable)
+{
+       rv770_enable_bif_dynamic_pcie_gen2(rdev, enable);
+
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, ENABLE_GEN2PCIE, ~ENABLE_GEN2PCIE);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE);
+}
+
+static void r7xx_program_memory_timing_parameters(struct radeon_device *rdev,
+                                                 struct radeon_ps *radeon_new_state)
+{
+       if ((rdev->family == CHIP_RV730) ||
+           (rdev->family == CHIP_RV710) ||
+           (rdev->family == CHIP_RV740))
+               rv730_program_memory_timing_parameters(rdev, radeon_new_state);
+       else
+               rv770_program_memory_timing_parameters(rdev, radeon_new_state);
+}
+
+static int rv770_upload_sw_state(struct radeon_device *rdev,
+                                struct radeon_ps *radeon_new_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u16 address = pi->state_table_start +
+               offsetof(RV770_SMC_STATETABLE, driverState);
+       RV770_SMC_SWSTATE state = { 0 };
+       int ret;
+
+       ret = rv770_convert_power_state_to_smc(rdev, radeon_new_state, &state);
+       if (ret)
+               return ret;
+
+       return rv770_copy_bytes_to_smc(rdev, address, (const u8 *)&state,
+                                      sizeof(RV770_SMC_SWSTATE),
+                                      pi->sram_end);
+}
+
+int rv770_halt_smc(struct radeon_device *rdev)
+{
+       if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_Halt) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       if (rv770_wait_for_smc_inactive(rdev) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return 0;
+}
+
+int rv770_resume_smc(struct radeon_device *rdev)
+{
+       if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_Resume) != PPSMC_Result_OK)
+               return -EINVAL;
+       return 0;
+}
+
+int rv770_set_sw_state(struct radeon_device *rdev)
+{
+       if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_SwitchToSwState) != PPSMC_Result_OK)
+               return -EINVAL;
+       return 0;
+}
+
+int rv770_set_boot_state(struct radeon_device *rdev)
+{
+       if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_SwitchToInitialState) != PPSMC_Result_OK)
+               return -EINVAL;
+       return 0;
+}
+
+void rv770_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev,
+                                             struct radeon_ps *new_ps,
+                                             struct radeon_ps *old_ps)
+{
+       struct rv7xx_ps *new_state = rv770_get_ps(new_ps);
+       struct rv7xx_ps *current_state = rv770_get_ps(old_ps);
+
+       if ((new_ps->vclk == old_ps->vclk) &&
+           (new_ps->dclk == old_ps->dclk))
+               return;
+
+       if (new_state->high.sclk >= current_state->high.sclk)
+               return;
+
+       radeon_set_uvd_clocks(rdev, new_ps->vclk, new_ps->dclk);
+}
+
+void rv770_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev,
+                                            struct radeon_ps *new_ps,
+                                            struct radeon_ps *old_ps)
+{
+       struct rv7xx_ps *new_state = rv770_get_ps(new_ps);
+       struct rv7xx_ps *current_state = rv770_get_ps(old_ps);
+
+       if ((new_ps->vclk == old_ps->vclk) &&
+           (new_ps->dclk == old_ps->dclk))
+               return;
+
+       if (new_state->high.sclk < current_state->high.sclk)
+               return;
+
+       radeon_set_uvd_clocks(rdev, new_ps->vclk, new_ps->dclk);
+}
+
+int rv770_restrict_performance_levels_before_switch(struct radeon_device *rdev)
+{
+       if (rv770_send_msg_to_smc(rdev, (PPSMC_Msg)(PPSMC_MSG_NoForcedLevel)) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       if (rv770_send_msg_to_smc(rdev, (PPSMC_Msg)(PPSMC_MSG_TwoLevelsDisabled)) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return 0;
+}
+
+int rv770_dpm_force_performance_level(struct radeon_device *rdev,
+                                     enum radeon_dpm_forced_level level)
+{
+       PPSMC_Msg msg;
+
+       if (level == RADEON_DPM_FORCED_LEVEL_HIGH) {
+               if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_ZeroLevelsDisabled) != PPSMC_Result_OK)
+                       return -EINVAL;
+               msg = PPSMC_MSG_ForceHigh;
+       } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) {
+               if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK)
+                       return -EINVAL;
+               msg = (PPSMC_Msg)(PPSMC_MSG_TwoLevelsDisabled);
+       } else {
+               if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK)
+                       return -EINVAL;
+               msg = (PPSMC_Msg)(PPSMC_MSG_ZeroLevelsDisabled);
+       }
+
+       if (rv770_send_msg_to_smc(rdev, msg) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       rdev->pm.dpm.forced_level = level;
+
+       return 0;
+}
+
+void r7xx_start_smc(struct radeon_device *rdev)
+{
+       rv770_start_smc(rdev);
+       rv770_start_smc_clock(rdev);
+}
+
+
+void r7xx_stop_smc(struct radeon_device *rdev)
+{
+       rv770_reset_smc(rdev);
+       rv770_stop_smc_clock(rdev);
+}
+
+static void rv770_read_clock_registers(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       pi->clk_regs.rv770.cg_spll_func_cntl =
+               RREG32(CG_SPLL_FUNC_CNTL);
+       pi->clk_regs.rv770.cg_spll_func_cntl_2 =
+               RREG32(CG_SPLL_FUNC_CNTL_2);
+       pi->clk_regs.rv770.cg_spll_func_cntl_3 =
+               RREG32(CG_SPLL_FUNC_CNTL_3);
+       pi->clk_regs.rv770.cg_spll_spread_spectrum =
+               RREG32(CG_SPLL_SPREAD_SPECTRUM);
+       pi->clk_regs.rv770.cg_spll_spread_spectrum_2 =
+               RREG32(CG_SPLL_SPREAD_SPECTRUM_2);
+       pi->clk_regs.rv770.mpll_ad_func_cntl =
+               RREG32(MPLL_AD_FUNC_CNTL);
+       pi->clk_regs.rv770.mpll_ad_func_cntl_2 =
+               RREG32(MPLL_AD_FUNC_CNTL_2);
+       pi->clk_regs.rv770.mpll_dq_func_cntl =
+               RREG32(MPLL_DQ_FUNC_CNTL);
+       pi->clk_regs.rv770.mpll_dq_func_cntl_2 =
+               RREG32(MPLL_DQ_FUNC_CNTL_2);
+       pi->clk_regs.rv770.mclk_pwrmgt_cntl =
+               RREG32(MCLK_PWRMGT_CNTL);
+       pi->clk_regs.rv770.dll_cntl = RREG32(DLL_CNTL);
+}
+
+static void r7xx_read_clock_registers(struct radeon_device *rdev)
+{
+       if (rdev->family == CHIP_RV740)
+               rv740_read_clock_registers(rdev);
+       else if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710))
+               rv730_read_clock_registers(rdev);
+       else
+               rv770_read_clock_registers(rdev);
+}
+
+void rv770_read_voltage_smio_registers(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       pi->s0_vid_lower_smio_cntl =
+               RREG32(S0_VID_LOWER_SMIO_CNTL);
+}
+
+void rv770_reset_smio_status(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 sw_smio_index, vid_smio_cntl;
+
+       sw_smio_index =
+               (RREG32(GENERAL_PWRMGT) & SW_SMIO_INDEX_MASK) >> SW_SMIO_INDEX_SHIFT;
+       switch (sw_smio_index) {
+        case 3:
+               vid_smio_cntl = RREG32(S3_VID_LOWER_SMIO_CNTL);
+               break;
+        case 2:
+               vid_smio_cntl = RREG32(S2_VID_LOWER_SMIO_CNTL);
+               break;
+        case 1:
+               vid_smio_cntl = RREG32(S1_VID_LOWER_SMIO_CNTL);
+               break;
+        case 0:
+               return;
+        default:
+               vid_smio_cntl = pi->s0_vid_lower_smio_cntl;
+               break;
+       }
+
+       WREG32(S0_VID_LOWER_SMIO_CNTL, vid_smio_cntl);
+       WREG32_P(GENERAL_PWRMGT, SW_SMIO_INDEX(0), ~SW_SMIO_INDEX_MASK);
+}
+
+void rv770_get_memory_type(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 tmp;
+
+       tmp = RREG32(MC_SEQ_MISC0);
+
+       if (((tmp & MC_SEQ_MISC0_GDDR5_MASK) >> MC_SEQ_MISC0_GDDR5_SHIFT) ==
+           MC_SEQ_MISC0_GDDR5_VALUE)
+               pi->mem_gddr5 = true;
+       else
+               pi->mem_gddr5 = false;
+
+}
+
+void rv770_get_pcie_gen2_status(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 tmp;
+
+       tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+
+       if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) &&
+           (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2))
+               pi->pcie_gen2 = true;
+       else
+               pi->pcie_gen2 = false;
+
+       if (pi->pcie_gen2) {
+               if (tmp & LC_CURRENT_DATA_RATE)
+                       pi->boot_in_gen2 = true;
+               else
+                       pi->boot_in_gen2 = false;
+       } else
+               pi->boot_in_gen2 = false;
+}
+
+#if 0
+static int rv770_enter_ulp_state(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (pi->gfx_clock_gating) {
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN);
+               WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON);
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON);
+               RREG32(GB_TILING_CONFIG);
+       }
+
+       WREG32_P(SMC_MSG, HOST_SMC_MSG(PPSMC_MSG_SwitchToMinimumPower),
+                ~HOST_SMC_MSG_MASK);
+
+       udelay(7000);
+
+       return 0;
+}
+
+static int rv770_exit_ulp_state(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       int i;
+
+       WREG32_P(SMC_MSG, HOST_SMC_MSG(PPSMC_MSG_ResumeFromMinimumPower),
+                ~HOST_SMC_MSG_MASK);
+
+       udelay(7000);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (((RREG32(SMC_MSG) & HOST_SMC_RESP_MASK) >> HOST_SMC_RESP_SHIFT) == 1)
+                       break;
+               udelay(1000);
+       }
+
+       if (pi->gfx_clock_gating)
+               WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN);
+
+       return 0;
+}
+#endif
+
+static void rv770_get_mclk_odt_threshold(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u8 memory_module_index;
+       struct atom_memory_info memory_info;
+
+       pi->mclk_odt_threshold = 0;
+
+       if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) {
+               memory_module_index = rv770_get_memory_module_index(rdev);
+
+               if (radeon_atom_get_memory_info(rdev, memory_module_index, &memory_info))
+                       return;
+
+               if (memory_info.mem_type == MEM_TYPE_DDR2 ||
+                   memory_info.mem_type == MEM_TYPE_DDR3)
+                       pi->mclk_odt_threshold = 30000;
+       }
+}
+
+void rv770_get_max_vddc(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u16 vddc;
+
+       if (radeon_atom_get_max_vddc(rdev, 0, 0, &vddc))
+               pi->max_vddc = 0;
+       else
+               pi->max_vddc = vddc;
+}
+
+void rv770_program_response_times(struct radeon_device *rdev)
+{
+       u32 voltage_response_time, backbias_response_time;
+       u32 acpi_delay_time, vbi_time_out;
+       u32 vddc_dly, bb_dly, acpi_dly, vbi_dly;
+       u32 reference_clock;
+
+       voltage_response_time = (u32)rdev->pm.dpm.voltage_response_time;
+       backbias_response_time = (u32)rdev->pm.dpm.backbias_response_time;
+
+       if (voltage_response_time == 0)
+               voltage_response_time = 1000;
+
+       if (backbias_response_time == 0)
+               backbias_response_time = 1000;
+
+       acpi_delay_time = 15000;
+       vbi_time_out = 100000;
+
+       reference_clock = radeon_get_xclk(rdev);
+
+       vddc_dly = (voltage_response_time  * reference_clock) / 1600;
+       bb_dly = (backbias_response_time * reference_clock) / 1600;
+       acpi_dly = (acpi_delay_time * reference_clock) / 1600;
+       vbi_dly = (vbi_time_out * reference_clock) / 1600;
+
+       rv770_write_smc_soft_register(rdev,
+                                     RV770_SMC_SOFT_REGISTER_delay_vreg, vddc_dly);
+       rv770_write_smc_soft_register(rdev,
+                                     RV770_SMC_SOFT_REGISTER_delay_bbias, bb_dly);
+       rv770_write_smc_soft_register(rdev,
+                                     RV770_SMC_SOFT_REGISTER_delay_acpi, acpi_dly);
+       rv770_write_smc_soft_register(rdev,
+                                     RV770_SMC_SOFT_REGISTER_mclk_chg_timeout, vbi_dly);
+#if 0
+       /* XXX look up hw revision */
+       if (WEKIVA_A21)
+               rv770_write_smc_soft_register(rdev,
+                                             RV770_SMC_SOFT_REGISTER_baby_step_timer,
+                                             0x10);
+#endif
+}
+
+static void rv770_program_dcodt_before_state_switch(struct radeon_device *rdev,
+                                                   struct radeon_ps *radeon_new_state,
+                                                   struct radeon_ps *radeon_current_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct rv7xx_ps *new_state = rv770_get_ps(radeon_new_state);
+       struct rv7xx_ps *current_state = rv770_get_ps(radeon_current_state);
+       bool current_use_dc = false;
+       bool new_use_dc = false;
+
+       if (pi->mclk_odt_threshold == 0)
+               return;
+
+       if (current_state->high.mclk <= pi->mclk_odt_threshold)
+               current_use_dc = true;
+
+       if (new_state->high.mclk <= pi->mclk_odt_threshold)
+               new_use_dc = true;
+
+       if (current_use_dc == new_use_dc)
+               return;
+
+       if (!current_use_dc && new_use_dc)
+               return;
+
+       if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710))
+               rv730_program_dcodt(rdev, new_use_dc);
+}
+
+static void rv770_program_dcodt_after_state_switch(struct radeon_device *rdev,
+                                                  struct radeon_ps *radeon_new_state,
+                                                  struct radeon_ps *radeon_current_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct rv7xx_ps *new_state = rv770_get_ps(radeon_new_state);
+       struct rv7xx_ps *current_state = rv770_get_ps(radeon_current_state);
+       bool current_use_dc = false;
+       bool new_use_dc = false;
+
+       if (pi->mclk_odt_threshold == 0)
+               return;
+
+       if (current_state->high.mclk <= pi->mclk_odt_threshold)
+               current_use_dc = true;
+
+       if (new_state->high.mclk <= pi->mclk_odt_threshold)
+               new_use_dc = true;
+
+       if (current_use_dc == new_use_dc)
+               return;
+
+       if (current_use_dc && !new_use_dc)
+               return;
+
+       if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710))
+               rv730_program_dcodt(rdev, new_use_dc);
+}
+
+static void rv770_retrieve_odt_values(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (pi->mclk_odt_threshold == 0)
+               return;
+
+       if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710))
+               rv730_get_odt_values(rdev);
+}
+
+static void rv770_set_dpm_event_sources(struct radeon_device *rdev, u32 sources)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       bool want_thermal_protection;
+       enum radeon_dpm_event_src dpm_event_src;
+
+       switch (sources) {
+        case 0:
+        default:
+               want_thermal_protection = false;
+               break;
+        case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_DIGITAL;
+               break;
+
+        case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_EXTERNAL;
+               break;
+
+        case ((1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL) |
+             (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL)):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL;
+               break;
+       }
+
+       if (want_thermal_protection) {
+               WREG32_P(CG_THERMAL_CTRL, DPM_EVENT_SRC(dpm_event_src), ~DPM_EVENT_SRC_MASK);
+               if (pi->thermal_protection)
+                       WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
+       } else {
+               WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
+       }
+}
+
+void rv770_enable_auto_throttle_source(struct radeon_device *rdev,
+                                      enum radeon_dpm_auto_throttle_src source,
+                                      bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (enable) {
+               if (!(pi->active_auto_throttle_sources & (1 << source))) {
+                       pi->active_auto_throttle_sources |= 1 << source;
+                       rv770_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
+               }
+       } else {
+               if (pi->active_auto_throttle_sources & (1 << source)) {
+                       pi->active_auto_throttle_sources &= ~(1 << source);
+                       rv770_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
+               }
+       }
+}
+
+int rv770_set_thermal_temperature_range(struct radeon_device *rdev,
+                                       int min_temp, int max_temp)
+{
+       int low_temp = 0 * 1000;
+       int high_temp = 255 * 1000;
+
+       if (low_temp < min_temp)
+               low_temp = min_temp;
+       if (high_temp > max_temp)
+               high_temp = max_temp;
+       if (high_temp < low_temp) {
+               DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp);
+               return -EINVAL;
+       }
+
+       WREG32_P(CG_THERMAL_INT, DIG_THERM_INTH(high_temp / 1000), ~DIG_THERM_INTH_MASK);
+       WREG32_P(CG_THERMAL_INT, DIG_THERM_INTL(low_temp / 1000), ~DIG_THERM_INTL_MASK);
+       WREG32_P(CG_THERMAL_CTRL, DIG_THERM_DPM(high_temp / 1000), ~DIG_THERM_DPM_MASK);
+
+       rdev->pm.dpm.thermal.min_temp = low_temp;
+       rdev->pm.dpm.thermal.max_temp = high_temp;
+
+       return 0;
+}
+
+int rv770_dpm_enable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+       int ret;
+
+       if (pi->gfx_clock_gating)
+               rv770_restore_cgcg(rdev);
+
+       if (rv770_dpm_enabled(rdev))
+               return -EINVAL;
+
+       if (pi->voltage_control) {
+               rv770_enable_voltage_control(rdev, true);
+               ret = rv770_construct_vddc_table(rdev);
+               if (ret) {
+                       DRM_ERROR("rv770_construct_vddc_table failed\n");
+                       return ret;
+               }
+       }
+
+       if (pi->dcodt)
+               rv770_retrieve_odt_values(rdev);
+
+       if (pi->mvdd_control) {
+               ret = rv770_get_mvdd_configuration(rdev);
+               if (ret) {
+                       DRM_ERROR("rv770_get_mvdd_configuration failed\n");
+                       return ret;
+               }
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
+               rv770_enable_backbias(rdev, true);
+
+       rv770_enable_spread_spectrum(rdev, true);
+
+       if (pi->thermal_protection)
+               rv770_enable_thermal_protection(rdev, true);
+
+       rv770_program_mpll_timing_parameters(rdev);
+       rv770_setup_bsp(rdev);
+       rv770_program_git(rdev);
+       rv770_program_tp(rdev);
+       rv770_program_tpp(rdev);
+       rv770_program_sstp(rdev);
+       rv770_program_engine_speed_parameters(rdev);
+       rv770_enable_display_gap(rdev);
+       rv770_program_vc(rdev);
+
+       if (pi->dynamic_pcie_gen2)
+               rv770_enable_dynamic_pcie_gen2(rdev, true);
+
+       ret = rv770_upload_firmware(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_upload_firmware failed\n");
+               return ret;
+       }
+       ret = rv770_init_smc_table(rdev, boot_ps);
+       if (ret) {
+               DRM_ERROR("rv770_init_smc_table failed\n");
+               return ret;
+       }
+
+       rv770_program_response_times(rdev);
+       r7xx_start_smc(rdev);
+
+       if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710))
+               rv730_start_dpm(rdev);
+       else
+               rv770_start_dpm(rdev);
+
+       if (pi->gfx_clock_gating)
+               rv770_gfx_clock_gating_enable(rdev, true);
+
+       if (pi->mg_clock_gating)
+               rv770_mg_clock_gating_enable(rdev, true);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               PPSMC_Result result;
+
+               ret = rv770_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+               if (ret)
+                       return ret;
+               rdev->irq.dpm_thermal = true;
+               radeon_irq_set(rdev);
+               result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt);
+
+               if (result != PPSMC_Result_OK)
+                       DRM_DEBUG_KMS("Could not enable thermal interrupts.\n");
+       }
+
+       rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+
+       return 0;
+}
+
+void rv770_dpm_disable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (!rv770_dpm_enabled(rdev))
+               return;
+
+       rv770_clear_vc(rdev);
+
+       if (pi->thermal_protection)
+               rv770_enable_thermal_protection(rdev, false);
+
+       rv770_enable_spread_spectrum(rdev, false);
+
+       if (pi->dynamic_pcie_gen2)
+               rv770_enable_dynamic_pcie_gen2(rdev, false);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               rdev->irq.dpm_thermal = false;
+               radeon_irq_set(rdev);
+       }
+
+       if (pi->gfx_clock_gating)
+               rv770_gfx_clock_gating_enable(rdev, false);
+
+       if (pi->mg_clock_gating)
+               rv770_mg_clock_gating_enable(rdev, false);
+
+       if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710))
+               rv730_stop_dpm(rdev);
+       else
+               rv770_stop_dpm(rdev);
+
+       r7xx_stop_smc(rdev);
+       rv770_reset_smio_status(rdev);
+}
+
+int rv770_dpm_set_power_state(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps;
+       struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
+       int ret;
+
+       ret = rv770_restrict_performance_levels_before_switch(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_restrict_performance_levels_before_switch failed\n");
+               return ret;
+       }
+       rv770_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps);
+       ret = rv770_halt_smc(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_halt_smc failed\n");
+               return ret;
+       }
+       ret = rv770_upload_sw_state(rdev, new_ps);
+       if (ret) {
+               DRM_ERROR("rv770_upload_sw_state failed\n");
+               return ret;
+       }
+       r7xx_program_memory_timing_parameters(rdev, new_ps);
+       if (pi->dcodt)
+               rv770_program_dcodt_before_state_switch(rdev, new_ps, old_ps);
+       ret = rv770_resume_smc(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_resume_smc failed\n");
+               return ret;
+       }
+       ret = rv770_set_sw_state(rdev);
+       if (ret) {
+               DRM_ERROR("rv770_set_sw_state failed\n");
+               return ret;
+       }
+       if (pi->dcodt)
+               rv770_program_dcodt_after_state_switch(rdev, new_ps, old_ps);
+       rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+
+       ret = rv770_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
+       if (ret) {
+               DRM_ERROR("rv770_dpm_force_performance_level failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+void rv770_dpm_reset_asic(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+
+       rv770_restrict_performance_levels_before_switch(rdev);
+       if (pi->dcodt)
+               rv770_program_dcodt_before_state_switch(rdev, boot_ps, boot_ps);
+       rv770_set_boot_state(rdev);
+       if (pi->dcodt)
+               rv770_program_dcodt_after_state_switch(rdev, boot_ps, boot_ps);
+}
+
+void rv770_dpm_setup_asic(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       r7xx_read_clock_registers(rdev);
+       rv770_read_voltage_smio_registers(rdev);
+       rv770_get_memory_type(rdev);
+       if (pi->dcodt)
+               rv770_get_mclk_odt_threshold(rdev);
+       rv770_get_pcie_gen2_status(rdev);
+
+       rv770_enable_acpi_pm(rdev);
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_ASPM_L0s)
+               rv770_enable_l0s(rdev);
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_ASPM_L1)
+               rv770_enable_l1(rdev);
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1)
+               rv770_enable_pll_sleep_in_l1(rdev);
+}
+
+void rv770_dpm_display_configuration_changed(struct radeon_device *rdev)
+{
+       rv770_program_display_gap(rdev);
+}
+
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+       struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+       struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+
+union pplib_clock_info {
+       struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
+       struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
+       struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
+       struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+
+union pplib_power_state {
+       struct _ATOM_PPLIB_STATE v1;
+       struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static void rv7xx_parse_pplib_non_clock_info(struct radeon_device *rdev,
+                                            struct radeon_ps *rps,
+                                            struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
+                                            u8 table_rev)
+{
+       rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+       rps->class = le16_to_cpu(non_clock_info->usClassification);
+       rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
+
+       if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
+               rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
+               rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
+       } else if (r600_is_uvd_state(rps->class, rps->class2)) {
+               rps->vclk = RV770_DEFAULT_VCLK_FREQ;
+               rps->dclk = RV770_DEFAULT_DCLK_FREQ;
+       } else {
+               rps->vclk = 0;
+               rps->dclk = 0;
+       }
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
+               rdev->pm.dpm.boot_ps = rps;
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+               rdev->pm.dpm.uvd_ps = rps;
+}
+
+static void rv7xx_parse_pplib_clock_info(struct radeon_device *rdev,
+                                        struct radeon_ps *rps, int index,
+                                        union pplib_clock_info *clock_info)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct rv7xx_ps *ps = rv770_get_ps(rps);
+       u32 sclk, mclk;
+       u16 vddc;
+       struct rv7xx_pl *pl;
+
+       switch (index) {
+       case 0:
+               pl = &ps->low;
+               break;
+       case 1:
+               pl = &ps->medium;
+               break;
+       case 2:
+       default:
+               pl = &ps->high;
+               break;
+       }
+
+       if (rdev->family >= CHIP_CEDAR) {
+               sclk = le16_to_cpu(clock_info->evergreen.usEngineClockLow);
+               sclk |= clock_info->evergreen.ucEngineClockHigh << 16;
+               mclk = le16_to_cpu(clock_info->evergreen.usMemoryClockLow);
+               mclk |= clock_info->evergreen.ucMemoryClockHigh << 16;
+
+               pl->vddc = le16_to_cpu(clock_info->evergreen.usVDDC);
+               pl->vddci = le16_to_cpu(clock_info->evergreen.usVDDCI);
+               pl->flags = le32_to_cpu(clock_info->evergreen.ulFlags);
+       } else {
+               sclk = le16_to_cpu(clock_info->r600.usEngineClockLow);
+               sclk |= clock_info->r600.ucEngineClockHigh << 16;
+               mclk = le16_to_cpu(clock_info->r600.usMemoryClockLow);
+               mclk |= clock_info->r600.ucMemoryClockHigh << 16;
+
+               pl->vddc = le16_to_cpu(clock_info->r600.usVDDC);
+               pl->flags = le32_to_cpu(clock_info->r600.ulFlags);
+       }
+
+       pl->mclk = mclk;
+       pl->sclk = sclk;
+
+       /* patch up vddc if necessary */
+       if (pl->vddc == 0xff01) {
+               if (radeon_atom_get_max_vddc(rdev, 0, 0, &vddc) == 0)
+                       pl->vddc = vddc;
+       }
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) {
+               pi->acpi_vddc = pl->vddc;
+               if (rdev->family >= CHIP_CEDAR)
+                       eg_pi->acpi_vddci = pl->vddci;
+               if (ps->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2)
+                       pi->acpi_pcie_gen2 = true;
+               else
+                       pi->acpi_pcie_gen2 = false;
+       }
+
+       if (rps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) {
+               if (rdev->family >= CHIP_BARTS) {
+                       eg_pi->ulv.supported = true;
+                       eg_pi->ulv.pl = pl;
+               }
+       }
+
+       if (pi->min_vddc_in_table > pl->vddc)
+               pi->min_vddc_in_table = pl->vddc;
+
+       if (pi->max_vddc_in_table < pl->vddc)
+               pi->max_vddc_in_table = pl->vddc;
+
+       /* patch up boot state */
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+               u16 vddc, vddci, mvdd;
+               radeon_atombios_get_default_voltages(rdev, &vddc, &vddci, &mvdd);
+               pl->mclk = rdev->clock.default_mclk;
+               pl->sclk = rdev->clock.default_sclk;
+               pl->vddc = vddc;
+               pl->vddci = vddci;
+       }
+
+       if (rdev->family >= CHIP_BARTS) {
+               if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
+                   ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
+                       rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk = pl->sclk;
+                       rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk = pl->mclk;
+                       rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc = pl->vddc;
+                       rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci = pl->vddci;
+               }
+       }
+}
+
+int rv7xx_parse_power_table(struct radeon_device *rdev)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+       union pplib_power_state *power_state;
+       int i, j;
+       union pplib_clock_info *clock_info;
+       union power_info *power_info;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+        u16 data_offset;
+       u8 frev, crev;
+       struct rv7xx_ps *ps;
+
+       if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset))
+               return -EINVAL;
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
+                                 power_info->pplib.ucNumStates, GFP_KERNEL);
+       if (!rdev->pm.dpm.ps)
+               return -ENOMEM;
+       rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
+       rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
+       rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
+
+       for (i = 0; i < power_info->pplib.ucNumStates; i++) {
+               power_state = (union pplib_power_state *)
+                       (mode_info->atom_context->bios + data_offset +
+                        le16_to_cpu(power_info->pplib.usStateArrayOffset) +
+                        i * power_info->pplib.ucStateEntrySize);
+               non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+                       (mode_info->atom_context->bios + data_offset +
+                        le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
+                        (power_state->v1.ucNonClockStateIndex *
+                         power_info->pplib.ucNonClockSize));
+               if (power_info->pplib.ucStateEntrySize - 1) {
+                       ps = kzalloc(sizeof(struct rv7xx_ps), GFP_KERNEL);
+                       if (ps == NULL) {
+                               kfree(rdev->pm.dpm.ps);
+                               return -ENOMEM;
+                       }
+                       rdev->pm.dpm.ps[i].ps_priv = ps;
+                       rv7xx_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
+                                                        non_clock_info,
+                                                        power_info->pplib.ucNonClockSize);
+                       for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) {
+                               clock_info = (union pplib_clock_info *)
+                                       (mode_info->atom_context->bios + data_offset +
+                                        le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
+                                        (power_state->v1.ucClockStateIndices[j] *
+                                         power_info->pplib.ucClockInfoSize));
+                               rv7xx_parse_pplib_clock_info(rdev,
+                                                            &rdev->pm.dpm.ps[i], j,
+                                                            clock_info);
+                       }
+               }
+       }
+       rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates;
+       return 0;
+}
+
+int rv770_dpm_init(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi;
+       int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
+       uint16_t data_offset, size;
+       uint8_t frev, crev;
+       struct atom_clock_dividers dividers;
+       int ret;
+
+       pi = kzalloc(sizeof(struct rv7xx_power_info), GFP_KERNEL);
+       if (pi == NULL)
+               return -ENOMEM;
+       rdev->pm.dpm.priv = pi;
+
+       rv770_get_max_vddc(rdev);
+
+       pi->acpi_vddc = 0;
+       pi->min_vddc_in_table = 0;
+       pi->max_vddc_in_table = 0;
+
+       ret = rv7xx_parse_power_table(rdev);
+       if (ret)
+               return ret;
+
+       if (rdev->pm.dpm.voltage_response_time == 0)
+               rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT;
+       if (rdev->pm.dpm.backbias_response_time == 0)
+               rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            0, false, &dividers);
+       if (ret)
+               pi->ref_div = dividers.ref_div + 1;
+       else
+               pi->ref_div = R600_REFERENCEDIVIDER_DFLT;
+
+       pi->mclk_strobe_mode_threshold = 30000;
+       pi->mclk_edc_enable_threshold = 30000;
+
+       pi->rlp = RV770_RLP_DFLT;
+       pi->rmp = RV770_RMP_DFLT;
+       pi->lhp = RV770_LHP_DFLT;
+       pi->lmp = RV770_LMP_DFLT;
+
+       pi->voltage_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, 0);
+
+       pi->mvdd_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC, 0);
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                   &frev, &crev, &data_offset)) {
+               pi->sclk_ss = true;
+               pi->mclk_ss = true;
+               pi->dynamic_ss = true;
+       } else {
+               pi->sclk_ss = false;
+               pi->mclk_ss = false;
+               pi->dynamic_ss = false;
+       }
+
+       pi->asi = RV770_ASI_DFLT;
+       pi->pasi = RV770_HASI_DFLT;
+       pi->vrc = RV770_VRC_DFLT;
+
+       pi->power_gating = false;
+
+       pi->gfx_clock_gating = true;
+
+       pi->mg_clock_gating = true;
+       pi->mgcgtssm = true;
+
+       pi->dynamic_pcie_gen2 = true;
+
+       if (pi->gfx_clock_gating &&
+           (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE))
+               pi->thermal_protection = true;
+       else
+               pi->thermal_protection = false;
+
+       pi->display_gap = true;
+
+       if (rdev->flags & RADEON_IS_MOBILITY)
+               pi->dcodt = true;
+       else
+               pi->dcodt = false;
+
+       pi->ulps = true;
+
+       pi->mclk_stutter_mode_threshold = 0;
+
+       pi->sram_end = SMC_RAM_END;
+       pi->state_table_start = RV770_SMC_TABLE_ADDRESS;
+       pi->soft_regs_start = RV770_SMC_SOFT_REGISTERS_START;
+
+       return 0;
+}
+
+void rv770_dpm_print_power_state(struct radeon_device *rdev,
+                                struct radeon_ps *rps)
+{
+       struct rv7xx_ps *ps = rv770_get_ps(rps);
+       struct rv7xx_pl *pl;
+
+       r600_dpm_print_class_info(rps->class, rps->class2);
+       r600_dpm_print_cap_info(rps->caps);
+       printk("\tuvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+       if (rdev->family >= CHIP_CEDAR) {
+               pl = &ps->low;
+               printk("\t\tpower level 0    sclk: %u mclk: %u vddc: %u vddci: %u\n",
+                      pl->sclk, pl->mclk, pl->vddc, pl->vddci);
+               pl = &ps->medium;
+               printk("\t\tpower level 1    sclk: %u mclk: %u vddc: %u vddci: %u\n",
+                      pl->sclk, pl->mclk, pl->vddc, pl->vddci);
+               pl = &ps->high;
+               printk("\t\tpower level 2    sclk: %u mclk: %u vddc: %u vddci: %u\n",
+                      pl->sclk, pl->mclk, pl->vddc, pl->vddci);
+       } else {
+               pl = &ps->low;
+               printk("\t\tpower level 0    sclk: %u mclk: %u vddc: %u\n",
+                      pl->sclk, pl->mclk, pl->vddc);
+               pl = &ps->medium;
+               printk("\t\tpower level 1    sclk: %u mclk: %u vddc: %u\n",
+                      pl->sclk, pl->mclk, pl->vddc);
+               pl = &ps->high;
+               printk("\t\tpower level 2    sclk: %u mclk: %u vddc: %u\n",
+                      pl->sclk, pl->mclk, pl->vddc);
+       }
+       r600_dpm_print_ps_status(rdev, rps);
+}
+
+void rv770_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                      struct seq_file *m)
+{
+       struct radeon_ps *rps = rdev->pm.dpm.current_ps;
+       struct rv7xx_ps *ps = rv770_get_ps(rps);
+       struct rv7xx_pl *pl;
+       u32 current_index =
+               (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_PROFILE_INDEX_MASK) >>
+               CURRENT_PROFILE_INDEX_SHIFT;
+
+       if (current_index > 2) {
+               seq_printf(m, "invalid dpm profile %d\n", current_index);
+       } else {
+               if (current_index == 0)
+                       pl = &ps->low;
+               else if (current_index == 1)
+                       pl = &ps->medium;
+               else /* current_index == 2 */
+                       pl = &ps->high;
+               seq_printf(m, "uvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+               if (rdev->family >= CHIP_CEDAR) {
+                       seq_printf(m, "power level %d    sclk: %u mclk: %u vddc: %u vddci: %u\n",
+                                  current_index, pl->sclk, pl->mclk, pl->vddc, pl->vddci);
+               } else {
+                       seq_printf(m, "power level %d    sclk: %u mclk: %u vddc: %u\n",
+                                  current_index, pl->sclk, pl->mclk, pl->vddc);
+               }
+       }
+}
+
+void rv770_dpm_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               kfree(rdev->pm.dpm.ps[i].ps_priv);
+       }
+       kfree(rdev->pm.dpm.ps);
+       kfree(rdev->pm.dpm.priv);
+}
+
+u32 rv770_dpm_get_sclk(struct radeon_device *rdev, bool low)
+{
+       struct rv7xx_ps *requested_state = rv770_get_ps(rdev->pm.dpm.requested_ps);
+
+       if (low)
+               return requested_state->low.sclk;
+       else
+               return requested_state->high.sclk;
+}
+
+u32 rv770_dpm_get_mclk(struct radeon_device *rdev, bool low)
+{
+       struct rv7xx_ps *requested_state = rv770_get_ps(rdev->pm.dpm.requested_ps);
+
+       if (low)
+               return requested_state->low.mclk;
+       else
+               return requested_state->high.mclk;
+}
+
+bool rv770_dpm_vblank_too_short(struct radeon_device *rdev)
+{
+       u32 vblank_time = r600_dpm_get_vblank_time(rdev);
+
+       if (vblank_time < 300)
+               return true;
+       else
+               return false;
+
+}
diff --git a/drivers/gpu/drm/radeon/rv770_dpm.h b/drivers/gpu/drm/radeon/rv770_dpm.h
new file mode 100644 (file)
index 0000000..96b1b2a
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RV770_DPM_H__
+#define __RV770_DPM_H__
+
+#include "rv770_smc.h"
+
+struct rv770_clock_registers {
+       u32 cg_spll_func_cntl;
+       u32 cg_spll_func_cntl_2;
+       u32 cg_spll_func_cntl_3;
+       u32 cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2;
+       u32 mpll_ad_func_cntl;
+       u32 mpll_ad_func_cntl_2;
+       u32 mpll_dq_func_cntl;
+       u32 mpll_dq_func_cntl_2;
+       u32 mclk_pwrmgt_cntl;
+       u32 dll_cntl;
+       u32 mpll_ss1;
+       u32 mpll_ss2;
+};
+
+struct rv730_clock_registers {
+       u32 cg_spll_func_cntl;
+       u32 cg_spll_func_cntl_2;
+       u32 cg_spll_func_cntl_3;
+       u32 cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2;
+       u32 mclk_pwrmgt_cntl;
+       u32 dll_cntl;
+       u32 mpll_func_cntl;
+       u32 mpll_func_cntl2;
+       u32 mpll_func_cntl3;
+       u32 mpll_ss;
+       u32 mpll_ss2;
+};
+
+union r7xx_clock_registers {
+       struct rv770_clock_registers rv770;
+       struct rv730_clock_registers rv730;
+};
+
+struct vddc_table_entry {
+       u16 vddc;
+       u8 vddc_index;
+       u8 high_smio;
+       u32 low_smio;
+};
+
+#define MAX_NO_OF_MVDD_VALUES 2
+#define MAX_NO_VREG_STEPS 32
+
+struct rv7xx_power_info {
+       /* flags */
+       bool mem_gddr5;
+       bool pcie_gen2;
+       bool dynamic_pcie_gen2;
+       bool acpi_pcie_gen2;
+       bool boot_in_gen2;
+       bool voltage_control; /* vddc */
+       bool mvdd_control;
+       bool sclk_ss;
+       bool mclk_ss;
+       bool dynamic_ss;
+       bool gfx_clock_gating;
+       bool mg_clock_gating;
+       bool mgcgtssm;
+       bool power_gating;
+       bool thermal_protection;
+       bool display_gap;
+       bool dcodt;
+       bool ulps;
+       /* registers */
+       union r7xx_clock_registers clk_regs;
+       u32 s0_vid_lower_smio_cntl;
+       /* voltage */
+       u32 vddc_mask_low;
+       u32 mvdd_mask_low;
+       u32 mvdd_split_frequency;
+       u32 mvdd_low_smio[MAX_NO_OF_MVDD_VALUES];
+       u16 max_vddc;
+       u16 max_vddc_in_table;
+       u16 min_vddc_in_table;
+       struct vddc_table_entry vddc_table[MAX_NO_VREG_STEPS];
+       u8 valid_vddc_entries;
+       /* dc odt */
+       u32 mclk_odt_threshold;
+       u8 odt_value_0[2];
+       u8 odt_value_1[2];
+       /* stored values */
+       u32 boot_sclk;
+       u16 acpi_vddc;
+       u32 ref_div;
+       u32 active_auto_throttle_sources;
+       u32 mclk_stutter_mode_threshold;
+       u32 mclk_strobe_mode_threshold;
+       u32 mclk_edc_enable_threshold;
+       u32 bsp;
+       u32 bsu;
+       u32 pbsp;
+       u32 pbsu;
+       u32 dsp;
+       u32 psp;
+       u32 asi;
+       u32 pasi;
+       u32 vrc;
+       u32 restricted_levels;
+       u32 rlp;
+       u32 rmp;
+       u32 lhp;
+       u32 lmp;
+       /* smc offsets */
+       u16 state_table_start;
+       u16 soft_regs_start;
+       u16 sram_end;
+       /* scratch structs */
+       RV770_SMC_STATETABLE smc_statetable;
+};
+
+struct rv7xx_pl {
+       u32 sclk;
+       u32 mclk;
+       u16 vddc;
+       u16 vddci; /* eg+ only */
+       u32 flags;
+       enum radeon_pcie_gen pcie_gen; /* si+ only */
+};
+
+struct rv7xx_ps {
+       struct rv7xx_pl high;
+       struct rv7xx_pl medium;
+       struct rv7xx_pl low;
+       bool dc_compatible;
+};
+
+#define RV770_RLP_DFLT                                10
+#define RV770_RMP_DFLT                                25
+#define RV770_LHP_DFLT                                25
+#define RV770_LMP_DFLT                                10
+#define RV770_VRC_DFLT                                0x003f
+#define RV770_ASI_DFLT                                1000
+#define RV770_HASI_DFLT                               200000
+#define RV770_MGCGTTLOCAL0_DFLT                       0x00100000
+#define RV7XX_MGCGTTLOCAL0_DFLT                       0
+#define RV770_MGCGTTLOCAL1_DFLT                       0xFFFF0000
+#define RV770_MGCGCGTSSMCTRL_DFLT                     0x55940000
+
+#define MVDD_LOW_INDEX  0
+#define MVDD_HIGH_INDEX 1
+
+#define MVDD_LOW_VALUE  0
+#define MVDD_HIGH_VALUE 0xffff
+
+#define RV770_DEFAULT_VCLK_FREQ  53300 /* 10 khz */
+#define RV770_DEFAULT_DCLK_FREQ  40000 /* 10 khz */
+
+/* rv730/rv710 */
+int rv730_populate_sclk_value(struct radeon_device *rdev,
+                             u32 engine_clock,
+                             RV770_SMC_SCLK_VALUE *sclk);
+int rv730_populate_mclk_value(struct radeon_device *rdev,
+                             u32 engine_clock, u32 memory_clock,
+                             LPRV7XX_SMC_MCLK_VALUE mclk);
+void rv730_read_clock_registers(struct radeon_device *rdev);
+int rv730_populate_smc_acpi_state(struct radeon_device *rdev,
+                                 RV770_SMC_STATETABLE *table);
+int rv730_populate_smc_initial_state(struct radeon_device *rdev,
+                                    struct radeon_ps *radeon_initial_state,
+                                    RV770_SMC_STATETABLE *table);
+void rv730_program_memory_timing_parameters(struct radeon_device *rdev,
+                                           struct radeon_ps *radeon_state);
+void rv730_power_gating_enable(struct radeon_device *rdev,
+                              bool enable);
+void rv730_start_dpm(struct radeon_device *rdev);
+void rv730_stop_dpm(struct radeon_device *rdev);
+void rv730_program_dcodt(struct radeon_device *rdev, bool use_dcodt);
+void rv730_get_odt_values(struct radeon_device *rdev);
+
+/* rv740 */
+int rv740_populate_sclk_value(struct radeon_device *rdev, u32 engine_clock,
+                             RV770_SMC_SCLK_VALUE *sclk);
+int rv740_populate_mclk_value(struct radeon_device *rdev,
+                             u32 engine_clock, u32 memory_clock,
+                             RV7XX_SMC_MCLK_VALUE *mclk);
+void rv740_read_clock_registers(struct radeon_device *rdev);
+int rv740_populate_smc_acpi_state(struct radeon_device *rdev,
+                                 RV770_SMC_STATETABLE *table);
+void rv740_enable_mclk_spread_spectrum(struct radeon_device *rdev,
+                                      bool enable);
+u8 rv740_get_mclk_frequency_ratio(u32 memory_clock);
+u32 rv740_get_dll_speed(bool is_gddr5, u32 memory_clock);
+u32 rv740_get_decoded_reference_divider(u32 encoded_ref);
+
+/* rv770 */
+u32 rv770_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf);
+int rv770_populate_vddc_value(struct radeon_device *rdev, u16 vddc,
+                             RV770_SMC_VOLTAGE_VALUE *voltage);
+int rv770_populate_mvdd_value(struct radeon_device *rdev, u32 mclk,
+                             RV770_SMC_VOLTAGE_VALUE *voltage);
+u8 rv770_get_seq_value(struct radeon_device *rdev,
+                      struct rv7xx_pl *pl);
+int rv770_populate_initial_mvdd_value(struct radeon_device *rdev,
+                                     RV770_SMC_VOLTAGE_VALUE *voltage);
+u32 rv770_calculate_memory_refresh_rate(struct radeon_device *rdev,
+                                       u32 engine_clock);
+void rv770_program_response_times(struct radeon_device *rdev);
+int rv770_populate_smc_sp(struct radeon_device *rdev,
+                         struct radeon_ps *radeon_state,
+                         RV770_SMC_SWSTATE *smc_state);
+int rv770_populate_smc_t(struct radeon_device *rdev,
+                        struct radeon_ps *radeon_state,
+                        RV770_SMC_SWSTATE *smc_state);
+void rv770_read_voltage_smio_registers(struct radeon_device *rdev);
+void rv770_get_memory_type(struct radeon_device *rdev);
+void r7xx_start_smc(struct radeon_device *rdev);
+u8 rv770_get_memory_module_index(struct radeon_device *rdev);
+void rv770_get_max_vddc(struct radeon_device *rdev);
+void rv770_get_pcie_gen2_status(struct radeon_device *rdev);
+void rv770_enable_acpi_pm(struct radeon_device *rdev);
+void rv770_restore_cgcg(struct radeon_device *rdev);
+bool rv770_dpm_enabled(struct radeon_device *rdev);
+void rv770_enable_voltage_control(struct radeon_device *rdev,
+                                 bool enable);
+void rv770_enable_backbias(struct radeon_device *rdev,
+                          bool enable);
+void rv770_enable_thermal_protection(struct radeon_device *rdev,
+                                    bool enable);
+void rv770_enable_auto_throttle_source(struct radeon_device *rdev,
+                                      enum radeon_dpm_auto_throttle_src source,
+                                      bool enable);
+void rv770_setup_bsp(struct radeon_device *rdev);
+void rv770_program_git(struct radeon_device *rdev);
+void rv770_program_tp(struct radeon_device *rdev);
+void rv770_program_tpp(struct radeon_device *rdev);
+void rv770_program_sstp(struct radeon_device *rdev);
+void rv770_program_engine_speed_parameters(struct radeon_device *rdev);
+void rv770_program_vc(struct radeon_device *rdev);
+void rv770_clear_vc(struct radeon_device *rdev);
+int rv770_upload_firmware(struct radeon_device *rdev);
+void rv770_stop_dpm(struct radeon_device *rdev);
+void r7xx_stop_smc(struct radeon_device *rdev);
+void rv770_reset_smio_status(struct radeon_device *rdev);
+int rv770_restrict_performance_levels_before_switch(struct radeon_device *rdev);
+int rv770_dpm_force_performance_level(struct radeon_device *rdev,
+                                     enum radeon_dpm_forced_level level);
+int rv770_halt_smc(struct radeon_device *rdev);
+int rv770_resume_smc(struct radeon_device *rdev);
+int rv770_set_sw_state(struct radeon_device *rdev);
+int rv770_set_boot_state(struct radeon_device *rdev);
+int rv7xx_parse_power_table(struct radeon_device *rdev);
+void rv770_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev,
+                                             struct radeon_ps *new_ps,
+                                             struct radeon_ps *old_ps);
+void rv770_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev,
+                                            struct radeon_ps *new_ps,
+                                            struct radeon_ps *old_ps);
+
+/* smc */
+int rv770_read_smc_soft_register(struct radeon_device *rdev,
+                                u16 reg_offset, u32 *value);
+int rv770_write_smc_soft_register(struct radeon_device *rdev,
+                                 u16 reg_offset, u32 value);
+
+/* thermal */
+int rv770_set_thermal_temperature_range(struct radeon_device *rdev,
+                                       int min_temp, int max_temp);
+
+#endif
diff --git a/drivers/gpu/drm/radeon/rv770_smc.c b/drivers/gpu/drm/radeon/rv770_smc.c
new file mode 100644 (file)
index 0000000..ab95da5
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
+ */
+
+#include <linux/firmware.h>
+#include "drmP.h"
+#include "radeon.h"
+#include "rv770d.h"
+#include "rv770_dpm.h"
+#include "rv770_smc.h"
+#include "atom.h"
+#include "radeon_ucode.h"
+
+#define FIRST_SMC_INT_VECT_REG 0xFFD8
+#define FIRST_INT_VECT_S19     0xFFC0
+
+static const u8 rv770_smc_int_vectors[] =
+{
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x0C, 0xD7,
+       0x08, 0x2B, 0x08, 0x10,
+       0x03, 0x51, 0x03, 0x51,
+       0x03, 0x51, 0x03, 0x51
+};
+
+static const u8 rv730_smc_int_vectors[] =
+{
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x08, 0x15,
+       0x08, 0x15, 0x0C, 0xBB,
+       0x08, 0x30, 0x08, 0x15,
+       0x03, 0x56, 0x03, 0x56,
+       0x03, 0x56, 0x03, 0x56
+};
+
+static const u8 rv710_smc_int_vectors[] =
+{
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x08, 0x04,
+       0x08, 0x04, 0x0C, 0xCB,
+       0x08, 0x1F, 0x08, 0x04,
+       0x03, 0x51, 0x03, 0x51,
+       0x03, 0x51, 0x03, 0x51
+};
+
+static const u8 rv740_smc_int_vectors[] =
+{
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x08, 0x10,
+       0x08, 0x10, 0x0C, 0xD7,
+       0x08, 0x2B, 0x08, 0x10,
+       0x03, 0x51, 0x03, 0x51,
+       0x03, 0x51, 0x03, 0x51
+};
+
+static const u8 cedar_smc_int_vectors[] =
+{
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x11, 0x8B,
+       0x0B, 0x20, 0x0B, 0x05,
+       0x04, 0xF6, 0x04, 0xF6,
+       0x04, 0xF6, 0x04, 0xF6
+};
+
+static const u8 redwood_smc_int_vectors[] =
+{
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x11, 0x8B,
+       0x0B, 0x20, 0x0B, 0x05,
+       0x04, 0xF6, 0x04, 0xF6,
+       0x04, 0xF6, 0x04, 0xF6
+};
+
+static const u8 juniper_smc_int_vectors[] =
+{
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x11, 0x8B,
+       0x0B, 0x20, 0x0B, 0x05,
+       0x04, 0xF6, 0x04, 0xF6,
+       0x04, 0xF6, 0x04, 0xF6
+};
+
+static const u8 cypress_smc_int_vectors[] =
+{
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x0B, 0x05,
+       0x0B, 0x05, 0x11, 0x8B,
+       0x0B, 0x20, 0x0B, 0x05,
+       0x04, 0xF6, 0x04, 0xF6,
+       0x04, 0xF6, 0x04, 0xF6
+};
+
+static const u8 barts_smc_int_vectors[] =
+{
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x12, 0xAA,
+       0x0C, 0x2F, 0x15, 0xF6,
+       0x15, 0xF6, 0x05, 0x0A,
+       0x05, 0x0A, 0x05, 0x0A
+};
+
+static const u8 turks_smc_int_vectors[] =
+{
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x12, 0xAA,
+       0x0C, 0x2F, 0x15, 0xF6,
+       0x15, 0xF6, 0x05, 0x0A,
+       0x05, 0x0A, 0x05, 0x0A
+};
+
+static const u8 caicos_smc_int_vectors[] =
+{
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x0C, 0x14,
+       0x0C, 0x14, 0x12, 0xAA,
+       0x0C, 0x2F, 0x15, 0xF6,
+       0x15, 0xF6, 0x05, 0x0A,
+       0x05, 0x0A, 0x05, 0x0A
+};
+
+static const u8 cayman_smc_int_vectors[] =
+{
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x12, 0x05,
+       0x12, 0x05, 0x18, 0xEA,
+       0x12, 0x20, 0x1C, 0x34,
+       0x1C, 0x34, 0x08, 0x72,
+       0x08, 0x72, 0x08, 0x72
+};
+
+int rv770_set_smc_sram_address(struct radeon_device *rdev,
+                              u16 smc_address, u16 limit)
+{
+       u32 addr;
+
+       if (smc_address & 3)
+               return -EINVAL;
+       if ((smc_address + 3) > limit)
+               return -EINVAL;
+
+       addr = smc_address;
+       addr |= SMC_SRAM_AUTO_INC_DIS;
+
+       WREG32(SMC_SRAM_ADDR, addr);
+
+       return 0;
+}
+
+int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
+                           u16 smc_start_address, const u8 *src,
+                           u16 byte_count, u16 limit)
+{
+       u32 data, original_data, extra_shift;
+       u16 addr;
+       int ret;
+
+       if (smc_start_address & 3)
+               return -EINVAL;
+       if ((smc_start_address + byte_count) > limit)
+               return -EINVAL;
+
+       addr = smc_start_address;
+
+       while (byte_count >= 4) {
+               /* SMC address space is BE */
+               data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+               ret = rv770_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               WREG32(SMC_SRAM_DATA, data);
+
+               src += 4;
+               byte_count -= 4;
+               addr += 4;
+       }
+
+       /* RMW for final bytes */
+       if (byte_count > 0) {
+               data = 0;
+
+               ret = rv770_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               original_data = RREG32(SMC_SRAM_DATA);
+
+               extra_shift = 8 * (4 - byte_count);
+
+               while (byte_count > 0) {
+                       /* SMC address space is BE */
+                       data = (data << 8) + *src++;
+                       byte_count--;
+               }
+
+               data <<= extra_shift;
+
+               data |= (original_data & ~((~0UL) << extra_shift));
+
+               ret = rv770_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               WREG32(SMC_SRAM_DATA, data);
+       }
+
+       return 0;
+}
+
+static int rv770_program_interrupt_vectors(struct radeon_device *rdev,
+                                          u32 smc_first_vector, const u8 *src,
+                                          u32 byte_count)
+{
+       u32 tmp, i;
+
+       if (byte_count % 4)
+               return -EINVAL;
+
+       if (smc_first_vector < FIRST_SMC_INT_VECT_REG) {
+               tmp = FIRST_SMC_INT_VECT_REG - smc_first_vector;
+
+               if (tmp > byte_count)
+                       return 0;
+
+               byte_count -= tmp;
+               src += tmp;
+               smc_first_vector = FIRST_SMC_INT_VECT_REG;
+       }
+
+       for (i = 0; i < byte_count; i += 4) {
+               /* SMC address space is BE */
+               tmp = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3];
+
+               WREG32(SMC_ISR_FFD8_FFDB + i, tmp);
+       }
+
+       return 0;
+}
+
+void rv770_start_smc(struct radeon_device *rdev)
+{
+       WREG32_P(SMC_IO, SMC_RST_N, ~SMC_RST_N);
+}
+
+void rv770_reset_smc(struct radeon_device *rdev)
+{
+       WREG32_P(SMC_IO, 0, ~SMC_RST_N);
+}
+
+void rv770_stop_smc_clock(struct radeon_device *rdev)
+{
+       WREG32_P(SMC_IO, 0, ~SMC_CLK_EN);
+}
+
+void rv770_start_smc_clock(struct radeon_device *rdev)
+{
+       WREG32_P(SMC_IO, SMC_CLK_EN, ~SMC_CLK_EN);
+}
+
+bool rv770_is_smc_running(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32(SMC_IO);
+
+       if ((tmp & SMC_RST_N) && (tmp & SMC_CLK_EN))
+               return true;
+       else
+               return false;
+}
+
+PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
+{
+       u32 tmp;
+       int i;
+       PPSMC_Result result;
+
+       if (!rv770_is_smc_running(rdev))
+               return PPSMC_Result_Failed;
+
+       WREG32_P(SMC_MSG, HOST_SMC_MSG(msg), ~HOST_SMC_MSG_MASK);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
+               tmp >>= HOST_SMC_RESP_SHIFT;
+               if (tmp != 0)
+                       break;
+               udelay(1);
+       }
+
+       tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
+       tmp >>= HOST_SMC_RESP_SHIFT;
+
+       result = (PPSMC_Result)tmp;
+       return result;
+}
+
+PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev)
+{
+       int i;
+       PPSMC_Result result = PPSMC_Result_OK;
+
+       if (!rv770_is_smc_running(rdev))
+               return result;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(SMC_IO) & SMC_STOP_MODE)
+                       break;
+               udelay(1);
+       }
+
+       return result;
+}
+
+static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
+{
+       u16 i;
+
+       for (i = 0;  i < limit; i += 4) {
+               rv770_set_smc_sram_address(rdev, i, limit);
+               WREG32(SMC_SRAM_DATA, 0);
+       }
+}
+
+int rv770_load_smc_ucode(struct radeon_device *rdev,
+                        u16 limit)
+{
+       int ret;
+       const u8 *int_vect;
+       u16 int_vect_start_address;
+       u16 int_vect_size;
+       const u8 *ucode_data;
+       u16 ucode_start_address;
+       u16 ucode_size;
+
+       if (!rdev->smc_fw)
+               return -EINVAL;
+
+       rv770_clear_smc_sram(rdev, limit);
+
+       switch (rdev->family) {
+       case CHIP_RV770:
+               ucode_start_address = RV770_SMC_UCODE_START;
+               ucode_size = RV770_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&rv770_smc_int_vectors;
+               int_vect_start_address = RV770_SMC_INT_VECTOR_START;
+               int_vect_size = RV770_SMC_INT_VECTOR_SIZE;
+               break;
+       case CHIP_RV730:
+               ucode_start_address = RV730_SMC_UCODE_START;
+               ucode_size = RV730_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&rv730_smc_int_vectors;
+               int_vect_start_address = RV730_SMC_INT_VECTOR_START;
+               int_vect_size = RV730_SMC_INT_VECTOR_SIZE;
+               break;
+       case CHIP_RV710:
+               ucode_start_address = RV710_SMC_UCODE_START;
+               ucode_size = RV710_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&rv710_smc_int_vectors;
+               int_vect_start_address = RV710_SMC_INT_VECTOR_START;
+               int_vect_size = RV710_SMC_INT_VECTOR_SIZE;
+               break;
+       case CHIP_RV740:
+               ucode_start_address = RV740_SMC_UCODE_START;
+               ucode_size = RV740_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&rv740_smc_int_vectors;
+               int_vect_start_address = RV740_SMC_INT_VECTOR_START;
+               int_vect_size = RV740_SMC_INT_VECTOR_SIZE;
+               break;
+       case CHIP_CEDAR:
+               ucode_start_address = CEDAR_SMC_UCODE_START;
+               ucode_size = CEDAR_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&cedar_smc_int_vectors;
+               int_vect_start_address = CEDAR_SMC_INT_VECTOR_START;
+               int_vect_size = CEDAR_SMC_INT_VECTOR_SIZE;
+               break;
+       case CHIP_REDWOOD:
+               ucode_start_address = REDWOOD_SMC_UCODE_START;
+               ucode_size = REDWOOD_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&redwood_smc_int_vectors;
+               int_vect_start_address = REDWOOD_SMC_INT_VECTOR_START;
+               int_vect_size = REDWOOD_SMC_INT_VECTOR_SIZE;
+               break;
+       case CHIP_JUNIPER:
+               ucode_start_address = JUNIPER_SMC_UCODE_START;
+               ucode_size = JUNIPER_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&juniper_smc_int_vectors;
+               int_vect_start_address = JUNIPER_SMC_INT_VECTOR_START;
+               int_vect_size = JUNIPER_SMC_INT_VECTOR_SIZE;
+               break;
+       case CHIP_CYPRESS:
+       case CHIP_HEMLOCK:
+               ucode_start_address = CYPRESS_SMC_UCODE_START;
+               ucode_size = CYPRESS_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&cypress_smc_int_vectors;
+               int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START;
+               int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE;
+               break;
+       case CHIP_BARTS:
+               ucode_start_address = BARTS_SMC_UCODE_START;
+               ucode_size = BARTS_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&barts_smc_int_vectors;
+               int_vect_start_address = BARTS_SMC_INT_VECTOR_START;
+               int_vect_size = BARTS_SMC_INT_VECTOR_SIZE;
+               break;
+       case CHIP_TURKS:
+               ucode_start_address = TURKS_SMC_UCODE_START;
+               ucode_size = TURKS_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&turks_smc_int_vectors;
+               int_vect_start_address = TURKS_SMC_INT_VECTOR_START;
+               int_vect_size = TURKS_SMC_INT_VECTOR_SIZE;
+               break;
+       case CHIP_CAICOS:
+               ucode_start_address = CAICOS_SMC_UCODE_START;
+               ucode_size = CAICOS_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&caicos_smc_int_vectors;
+               int_vect_start_address = CAICOS_SMC_INT_VECTOR_START;
+               int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE;
+               break;
+       case CHIP_CAYMAN:
+               ucode_start_address = CAYMAN_SMC_UCODE_START;
+               ucode_size = CAYMAN_SMC_UCODE_SIZE;
+               int_vect = (const u8 *)&cayman_smc_int_vectors;
+               int_vect_start_address = CAYMAN_SMC_INT_VECTOR_START;
+               int_vect_size = CAYMAN_SMC_INT_VECTOR_SIZE;
+               break;
+       default:
+               DRM_ERROR("unknown asic in smc ucode loader\n");
+               BUG();
+       }
+
+       /* load the ucode */
+       ucode_data = (const u8 *)rdev->smc_fw->data;
+       ret = rv770_copy_bytes_to_smc(rdev, ucode_start_address,
+                                     ucode_data, ucode_size, limit);
+       if (ret)
+               return ret;
+
+       /* set up the int vectors */
+       ret = rv770_program_interrupt_vectors(rdev, int_vect_start_address,
+                                             int_vect, int_vect_size);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+int rv770_read_smc_sram_dword(struct radeon_device *rdev,
+                             u16 smc_address, u32 *value, u16 limit)
+{
+       int ret;
+
+       ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
+       if (ret)
+               return ret;
+
+       *value = RREG32(SMC_SRAM_DATA);
+
+       return 0;
+}
+
+int rv770_write_smc_sram_dword(struct radeon_device *rdev,
+                              u16 smc_address, u32 value, u16 limit)
+{
+       int ret;
+
+       ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
+       if (ret)
+               return ret;
+
+       WREG32(SMC_SRAM_DATA, value);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/radeon/rv770_smc.h b/drivers/gpu/drm/radeon/rv770_smc.h
new file mode 100644 (file)
index 0000000..f78d92a
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RV770_SMC_H__
+#define __RV770_SMC_H__
+
+#include "ppsmc.h"
+
+#pragma pack(push, 1)
+
+#define RV770_SMC_TABLE_ADDRESS 0xB000
+
+#define RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE    3
+
+struct RV770_SMC_SCLK_VALUE
+{
+    uint32_t        vCG_SPLL_FUNC_CNTL;
+    uint32_t        vCG_SPLL_FUNC_CNTL_2;
+    uint32_t        vCG_SPLL_FUNC_CNTL_3;
+    uint32_t        vCG_SPLL_SPREAD_SPECTRUM;
+    uint32_t        vCG_SPLL_SPREAD_SPECTRUM_2;
+    uint32_t        sclk_value;
+};
+
+typedef struct RV770_SMC_SCLK_VALUE RV770_SMC_SCLK_VALUE;
+
+struct RV770_SMC_MCLK_VALUE
+{
+    uint32_t        vMPLL_AD_FUNC_CNTL;
+    uint32_t        vMPLL_AD_FUNC_CNTL_2;
+    uint32_t        vMPLL_DQ_FUNC_CNTL;
+    uint32_t        vMPLL_DQ_FUNC_CNTL_2;
+    uint32_t        vMCLK_PWRMGT_CNTL;
+    uint32_t        vDLL_CNTL;
+    uint32_t        vMPLL_SS;
+    uint32_t        vMPLL_SS2;
+    uint32_t        mclk_value;
+};
+
+typedef struct RV770_SMC_MCLK_VALUE RV770_SMC_MCLK_VALUE;
+
+
+struct RV730_SMC_MCLK_VALUE
+{
+    uint32_t        vMCLK_PWRMGT_CNTL;
+    uint32_t        vDLL_CNTL;
+    uint32_t        vMPLL_FUNC_CNTL;
+    uint32_t        vMPLL_FUNC_CNTL2;
+    uint32_t        vMPLL_FUNC_CNTL3;
+    uint32_t        vMPLL_SS;
+    uint32_t        vMPLL_SS2;
+    uint32_t        mclk_value;
+};
+
+typedef struct RV730_SMC_MCLK_VALUE RV730_SMC_MCLK_VALUE;
+
+struct RV770_SMC_VOLTAGE_VALUE
+{
+    uint16_t             value;
+    uint8_t              index;
+    uint8_t              padding;
+};
+
+typedef struct RV770_SMC_VOLTAGE_VALUE RV770_SMC_VOLTAGE_VALUE;
+
+union RV7XX_SMC_MCLK_VALUE
+{
+    RV770_SMC_MCLK_VALUE    mclk770;
+    RV730_SMC_MCLK_VALUE    mclk730;
+};
+
+typedef union RV7XX_SMC_MCLK_VALUE RV7XX_SMC_MCLK_VALUE, *LPRV7XX_SMC_MCLK_VALUE;
+
+struct RV770_SMC_HW_PERFORMANCE_LEVEL
+{
+    uint8_t                 arbValue;
+    union{
+        uint8_t             seqValue;
+        uint8_t             ACIndex;
+    };
+    uint8_t                 displayWatermark;
+    uint8_t                 gen2PCIE;
+    uint8_t                 gen2XSP;
+    uint8_t                 backbias;
+    uint8_t                 strobeMode;
+    uint8_t                 mcFlags;
+    uint32_t                aT;
+    uint32_t                bSP;
+    RV770_SMC_SCLK_VALUE    sclk;
+    RV7XX_SMC_MCLK_VALUE    mclk;
+    RV770_SMC_VOLTAGE_VALUE vddc;
+    RV770_SMC_VOLTAGE_VALUE mvdd;
+    RV770_SMC_VOLTAGE_VALUE vddci;
+    uint8_t                 reserved1;
+    uint8_t                 reserved2;
+    uint8_t                 stateFlags;
+    uint8_t                 padding;
+};
+
+#define SMC_STROBE_RATIO    0x0F
+#define SMC_STROBE_ENABLE   0x10
+
+#define SMC_MC_EDC_RD_FLAG  0x01
+#define SMC_MC_EDC_WR_FLAG  0x02
+#define SMC_MC_RTT_ENABLE   0x04
+#define SMC_MC_STUTTER_EN   0x08
+
+typedef struct RV770_SMC_HW_PERFORMANCE_LEVEL RV770_SMC_HW_PERFORMANCE_LEVEL;
+
+struct RV770_SMC_SWSTATE
+{
+    uint8_t           flags;
+    uint8_t           padding1;
+    uint8_t           padding2;
+    uint8_t           padding3;
+    RV770_SMC_HW_PERFORMANCE_LEVEL levels[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE];
+};
+
+typedef struct RV770_SMC_SWSTATE RV770_SMC_SWSTATE;
+
+#define RV770_SMC_VOLTAGEMASK_VDDC 0
+#define RV770_SMC_VOLTAGEMASK_MVDD 1
+#define RV770_SMC_VOLTAGEMASK_VDDCI 2
+#define RV770_SMC_VOLTAGEMASK_MAX  4
+
+struct RV770_SMC_VOLTAGEMASKTABLE
+{
+    uint8_t  highMask[RV770_SMC_VOLTAGEMASK_MAX];
+    uint32_t lowMask[RV770_SMC_VOLTAGEMASK_MAX];
+};
+
+typedef struct RV770_SMC_VOLTAGEMASKTABLE RV770_SMC_VOLTAGEMASKTABLE;
+
+#define MAX_NO_VREG_STEPS 32
+
+struct RV770_SMC_STATETABLE
+{
+    uint8_t             thermalProtectType;
+    uint8_t             systemFlags;
+    uint8_t             maxVDDCIndexInPPTable;
+    uint8_t             extraFlags;
+    uint8_t             highSMIO[MAX_NO_VREG_STEPS];
+    uint32_t            lowSMIO[MAX_NO_VREG_STEPS];
+    RV770_SMC_VOLTAGEMASKTABLE voltageMaskTable;
+    RV770_SMC_SWSTATE   initialState;
+    RV770_SMC_SWSTATE   ACPIState;
+    RV770_SMC_SWSTATE   driverState;
+    RV770_SMC_SWSTATE   ULVState;
+};
+
+typedef struct RV770_SMC_STATETABLE RV770_SMC_STATETABLE;
+
+#define PPSMC_STATEFLAG_AUTO_PULSE_SKIP 0x01
+
+#pragma pack(pop)
+
+#define RV770_SMC_SOFT_REGISTERS_START        0x104
+
+#define RV770_SMC_SOFT_REGISTER_mclk_chg_timeout        0x0
+#define RV770_SMC_SOFT_REGISTER_baby_step_timer         0x8
+#define RV770_SMC_SOFT_REGISTER_delay_bbias             0xC
+#define RV770_SMC_SOFT_REGISTER_delay_vreg              0x10
+#define RV770_SMC_SOFT_REGISTER_delay_acpi              0x2C
+#define RV770_SMC_SOFT_REGISTER_seq_index               0x64
+#define RV770_SMC_SOFT_REGISTER_mvdd_chg_time           0x68
+#define RV770_SMC_SOFT_REGISTER_mclk_switch_lim         0x78
+#define RV770_SMC_SOFT_REGISTER_mc_block_delay          0x90
+#define RV770_SMC_SOFT_REGISTER_uvd_enabled             0x9C
+#define RV770_SMC_SOFT_REGISTER_is_asic_lombok          0xA0
+
+int rv770_set_smc_sram_address(struct radeon_device *rdev,
+                              u16 smc_address, u16 limit);
+int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
+                           u16 smc_start_address, const u8 *src,
+                           u16 byte_count, u16 limit);
+void rv770_start_smc(struct radeon_device *rdev);
+void rv770_reset_smc(struct radeon_device *rdev);
+void rv770_stop_smc_clock(struct radeon_device *rdev);
+void rv770_start_smc_clock(struct radeon_device *rdev);
+bool rv770_is_smc_running(struct radeon_device *rdev);
+PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg);
+PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev);
+int rv770_read_smc_sram_dword(struct radeon_device *rdev,
+                             u16 smc_address, u32 *value, u16 limit);
+int rv770_write_smc_sram_dword(struct radeon_device *rdev,
+                              u16 smc_address, u32 value, u16 limit);
+int rv770_load_smc_ucode(struct radeon_device *rdev,
+                        u16 limit);
+
+#endif
index 85b1626..6bef2b7 100644 (file)
 #      define UPLL_FB_DIV(x)                           ((x) << 0)
 #      define UPLL_FB_DIV_MASK                         0x01FFFFFF
 
+/* pm registers */
+#define        SMC_SRAM_ADDR                                   0x200
+#define                SMC_SRAM_AUTO_INC_DIS                           (1 << 16)
+#define        SMC_SRAM_DATA                                   0x204
+#define        SMC_IO                                          0x208
+#define                SMC_RST_N                                       (1 << 0)
+#define                SMC_STOP_MODE                                   (1 << 2)
+#define                SMC_CLK_EN                                      (1 << 11)
+#define        SMC_MSG                                         0x20c
+#define                HOST_SMC_MSG(x)                                 ((x) << 0)
+#define                HOST_SMC_MSG_MASK                               (0xff << 0)
+#define                HOST_SMC_MSG_SHIFT                              0
+#define                HOST_SMC_RESP(x)                                ((x) << 8)
+#define                HOST_SMC_RESP_MASK                              (0xff << 8)
+#define                HOST_SMC_RESP_SHIFT                             8
+#define                SMC_HOST_MSG(x)                                 ((x) << 16)
+#define                SMC_HOST_MSG_MASK                               (0xff << 16)
+#define                SMC_HOST_MSG_SHIFT                              16
+#define                SMC_HOST_RESP(x)                                ((x) << 24)
+#define                SMC_HOST_RESP_MASK                              (0xff << 24)
+#define                SMC_HOST_RESP_SHIFT                             24
+
+#define        SMC_ISR_FFD8_FFDB                               0x218
+
+#define        CG_SPLL_FUNC_CNTL                               0x600
+#define                SPLL_RESET                              (1 << 0)
+#define                SPLL_SLEEP                              (1 << 1)
+#define                SPLL_DIVEN                              (1 << 2)
+#define                SPLL_BYPASS_EN                          (1 << 3)
+#define                SPLL_REF_DIV(x)                         ((x) << 4)
+#define                SPLL_REF_DIV_MASK                       (0x3f << 4)
+#define                SPLL_HILEN(x)                           ((x) << 12)
+#define                SPLL_HILEN_MASK                         (0xf << 12)
+#define                SPLL_LOLEN(x)                           ((x) << 16)
+#define                SPLL_LOLEN_MASK                         (0xf << 16)
+#define        CG_SPLL_FUNC_CNTL_2                             0x604
+#define                SCLK_MUX_SEL(x)                         ((x) << 0)
+#define                SCLK_MUX_SEL_MASK                       (0x1ff << 0)
+#define        CG_SPLL_FUNC_CNTL_3                             0x608
+#define                SPLL_FB_DIV(x)                          ((x) << 0)
+#define                SPLL_FB_DIV_MASK                        (0x3ffffff << 0)
+#define                SPLL_DITHEN                             (1 << 28)
+
+#define        SPLL_CNTL_MODE                                  0x610
+#define                SPLL_DIV_SYNC                           (1 << 5)
+
+#define        MPLL_AD_FUNC_CNTL                               0x624
+#define                CLKF(x)                                 ((x) << 0)
+#define                CLKF_MASK                               (0x7f << 0)
+#define                CLKR(x)                                 ((x) << 7)
+#define                CLKR_MASK                               (0x1f << 7)
+#define                CLKFRAC(x)                              ((x) << 12)
+#define                CLKFRAC_MASK                            (0x1f << 12)
+#define                YCLK_POST_DIV(x)                        ((x) << 17)
+#define                YCLK_POST_DIV_MASK                      (3 << 17)
+#define                IBIAS(x)                                ((x) << 20)
+#define                IBIAS_MASK                              (0x3ff << 20)
+#define                RESET                                   (1 << 30)
+#define                PDNB                                    (1 << 31)
+#define        MPLL_AD_FUNC_CNTL_2                             0x628
+#define                BYPASS                                  (1 << 19)
+#define                BIAS_GEN_PDNB                           (1 << 24)
+#define                RESET_EN                                (1 << 25)
+#define                VCO_MODE                                (1 << 29)
+#define        MPLL_DQ_FUNC_CNTL                               0x62c
+#define        MPLL_DQ_FUNC_CNTL_2                             0x630
+
+#define GENERAL_PWRMGT                                  0x63c
+#       define GLOBAL_PWRMGT_EN                         (1 << 0)
+#       define STATIC_PM_EN                             (1 << 1)
+#       define THERMAL_PROTECTION_DIS                   (1 << 2)
+#       define THERMAL_PROTECTION_TYPE                  (1 << 3)
+#       define ENABLE_GEN2PCIE                          (1 << 4)
+#       define ENABLE_GEN2XSP                           (1 << 5)
+#       define SW_SMIO_INDEX(x)                         ((x) << 6)
+#       define SW_SMIO_INDEX_MASK                       (3 << 6)
+#       define SW_SMIO_INDEX_SHIFT                      6
+#       define LOW_VOLT_D2_ACPI                         (1 << 8)
+#       define LOW_VOLT_D3_ACPI                         (1 << 9)
+#       define VOLT_PWRMGT_EN                           (1 << 10)
+#       define BACKBIAS_PAD_EN                          (1 << 18)
+#       define BACKBIAS_VALUE                           (1 << 19)
+#       define DYN_SPREAD_SPECTRUM_EN                   (1 << 23)
+#       define AC_DC_SW                                 (1 << 24)
+
+#define CG_TPC                                            0x640
+#define SCLK_PWRMGT_CNTL                                  0x644
+#       define SCLK_PWRMGT_OFF                            (1 << 0)
+#       define SCLK_LOW_D1                                (1 << 1)
+#       define FIR_RESET                                  (1 << 4)
+#       define FIR_FORCE_TREND_SEL                        (1 << 5)
+#       define FIR_TREND_MODE                             (1 << 6)
+#       define DYN_GFX_CLK_OFF_EN                         (1 << 7)
+#       define GFX_CLK_FORCE_ON                           (1 << 8)
+#       define GFX_CLK_REQUEST_OFF                        (1 << 9)
+#       define GFX_CLK_FORCE_OFF                          (1 << 10)
+#       define GFX_CLK_OFF_ACPI_D1                        (1 << 11)
+#       define GFX_CLK_OFF_ACPI_D2                        (1 << 12)
+#       define GFX_CLK_OFF_ACPI_D3                        (1 << 13)
+#define        MCLK_PWRMGT_CNTL                                0x648
+#       define DLL_SPEED(x)                            ((x) << 0)
+#       define DLL_SPEED_MASK                          (0x1f << 0)
+#       define MPLL_PWRMGT_OFF                          (1 << 5)
+#       define DLL_READY                                (1 << 6)
+#       define MC_INT_CNTL                              (1 << 7)
+#       define MRDCKA0_SLEEP                            (1 << 8)
+#       define MRDCKA1_SLEEP                            (1 << 9)
+#       define MRDCKB0_SLEEP                            (1 << 10)
+#       define MRDCKB1_SLEEP                            (1 << 11)
+#       define MRDCKC0_SLEEP                            (1 << 12)
+#       define MRDCKC1_SLEEP                            (1 << 13)
+#       define MRDCKD0_SLEEP                            (1 << 14)
+#       define MRDCKD1_SLEEP                            (1 << 15)
+#       define MRDCKA0_RESET                            (1 << 16)
+#       define MRDCKA1_RESET                            (1 << 17)
+#       define MRDCKB0_RESET                            (1 << 18)
+#       define MRDCKB1_RESET                            (1 << 19)
+#       define MRDCKC0_RESET                            (1 << 20)
+#       define MRDCKC1_RESET                            (1 << 21)
+#       define MRDCKD0_RESET                            (1 << 22)
+#       define MRDCKD1_RESET                            (1 << 23)
+#       define DLL_READY_READ                           (1 << 24)
+#       define USE_DISPLAY_GAP                          (1 << 25)
+#       define USE_DISPLAY_URGENT_NORMAL                (1 << 26)
+#       define MPLL_TURNOFF_D2                          (1 << 28)
+#define        DLL_CNTL                                        0x64c
+#       define MRDCKA0_BYPASS                           (1 << 24)
+#       define MRDCKA1_BYPASS                           (1 << 25)
+#       define MRDCKB0_BYPASS                           (1 << 26)
+#       define MRDCKB1_BYPASS                           (1 << 27)
+#       define MRDCKC0_BYPASS                           (1 << 28)
+#       define MRDCKC1_BYPASS                           (1 << 29)
+#       define MRDCKD0_BYPASS                           (1 << 30)
+#       define MRDCKD1_BYPASS                           (1 << 31)
+
+#define MPLL_TIME                                         0x654
+#       define MPLL_LOCK_TIME(x)                       ((x) << 0)
+#       define MPLL_LOCK_TIME_MASK                     (0xffff << 0)
+#       define MPLL_RESET_TIME(x)                      ((x) << 16)
+#       define MPLL_RESET_TIME_MASK                    (0xffff << 16)
+
+#define CG_CLKPIN_CNTL                                    0x660
+#       define MUX_TCLK_TO_XCLK                           (1 << 8)
+#       define XTALIN_DIVIDE                              (1 << 9)
+
+#define TARGET_AND_CURRENT_PROFILE_INDEX                  0x66c
+#       define CURRENT_PROFILE_INDEX_MASK                 (0xf << 4)
+#       define CURRENT_PROFILE_INDEX_SHIFT                4
+
+#define S0_VID_LOWER_SMIO_CNTL                            0x678
+#define S1_VID_LOWER_SMIO_CNTL                            0x67c
+#define S2_VID_LOWER_SMIO_CNTL                            0x680
+#define S3_VID_LOWER_SMIO_CNTL                            0x684
+
+#define CG_FTV                                            0x690
+#define CG_FFCT_0                                         0x694
+#       define UTC_0(x)                                   ((x) << 0)
+#       define UTC_0_MASK                                 (0x3ff << 0)
+#       define DTC_0(x)                                   ((x) << 10)
+#       define DTC_0_MASK                                 (0x3ff << 10)
+
+#define CG_BSP                                          0x6d0
+#       define BSP(x)                                  ((x) << 0)
+#       define BSP_MASK                                        (0xffff << 0)
+#       define BSU(x)                                  ((x) << 16)
+#       define BSU_MASK                                        (0xf << 16)
+#define CG_AT                                           0x6d4
+#       define CG_R(x)                                 ((x) << 0)
+#       define CG_R_MASK                               (0xffff << 0)
+#       define CG_L(x)                                 ((x) << 16)
+#       define CG_L_MASK                               (0xffff << 16)
+#define CG_GIT                                          0x6d8
+#       define CG_GICST(x)                              ((x) << 0)
+#       define CG_GICST_MASK                            (0xffff << 0)
+#       define CG_GIPOT(x)                              ((x) << 16)
+#       define CG_GIPOT_MASK                            (0xffff << 16)
+
+#define CG_SSP                                            0x6e8
+#       define SST(x)                                     ((x) << 0)
+#       define SST_MASK                                   (0xffff << 0)
+#       define SSTU(x)                                    ((x) << 16)
+#       define SSTU_MASK                                  (0xf << 16)
+
+#define CG_DISPLAY_GAP_CNTL                               0x714
+#       define DISP1_GAP(x)                               ((x) << 0)
+#       define DISP1_GAP_MASK                             (3 << 0)
+#       define DISP2_GAP(x)                               ((x) << 2)
+#       define DISP2_GAP_MASK                             (3 << 2)
+#       define VBI_TIMER_COUNT(x)                         ((x) << 4)
+#       define VBI_TIMER_COUNT_MASK                       (0x3fff << 4)
+#       define VBI_TIMER_UNIT(x)                          ((x) << 20)
+#       define VBI_TIMER_UNIT_MASK                        (7 << 20)
+#       define DISP1_GAP_MCHG(x)                          ((x) << 24)
+#       define DISP1_GAP_MCHG_MASK                        (3 << 24)
+#       define DISP2_GAP_MCHG(x)                          ((x) << 26)
+#       define DISP2_GAP_MCHG_MASK                        (3 << 26)
+
+#define        CG_SPLL_SPREAD_SPECTRUM                         0x790
+#define                SSEN                                    (1 << 0)
+#define                CLKS(x)                                 ((x) << 4)
+#define                CLKS_MASK                               (0xfff << 4)
+#define        CG_SPLL_SPREAD_SPECTRUM_2                       0x794
+#define                CLKV(x)                                 ((x) << 0)
+#define                CLKV_MASK                               (0x3ffffff << 0)
+#define        CG_MPLL_SPREAD_SPECTRUM                         0x798
+#define CG_UPLL_SPREAD_SPECTRUM                                0x79c
+#      define SSEN_MASK                                0x00000001
+
+#define CG_CGTT_LOCAL_0                                   0x7d0
+#define CG_CGTT_LOCAL_1                                   0x7d4
+
+#define BIOS_SCRATCH_4                                    0x1734
+
+#define MC_SEQ_MISC0                                      0x2a00
+#define         MC_SEQ_MISC0_GDDR5_SHIFT                  28
+#define         MC_SEQ_MISC0_GDDR5_MASK                   0xf0000000
+#define         MC_SEQ_MISC0_GDDR5_VALUE                  5
+
+#define MC_ARB_SQM_RATIO                                  0x2770
+#define                STATE0(x)                               ((x) << 0)
+#define                STATE0_MASK                             (0xff << 0)
+#define                STATE1(x)                               ((x) << 8)
+#define                STATE1_MASK                             (0xff << 8)
+#define                STATE2(x)                               ((x) << 16)
+#define                STATE2_MASK                             (0xff << 16)
+#define                STATE3(x)                               ((x) << 24)
+#define                STATE3_MASK                             (0xff << 24)
+
+#define        MC_ARB_RFSH_RATE                                0x27b0
+#define                POWERMODE0(x)                           ((x) << 0)
+#define                POWERMODE0_MASK                         (0xff << 0)
+#define                POWERMODE1(x)                           ((x) << 8)
+#define                POWERMODE1_MASK                         (0xff << 8)
+#define                POWERMODE2(x)                           ((x) << 16)
+#define                POWERMODE2_MASK                         (0xff << 16)
+#define                POWERMODE3(x)                           ((x) << 24)
+#define                POWERMODE3_MASK                         (0xff << 24)
+
+#define CGTS_SM_CTRL_REG                                  0x9150
+
 /* Registers */
 #define        CB_COLOR0_BASE                                  0x28040
 #define        CB_COLOR1_BASE                                  0x28044
 #define        CONFIG_MEMSIZE                                  0x5428
 
 #define        CP_ME_CNTL                                      0x86D8
-#define                CP_ME_HALT                                      (1<<28)
-#define                CP_PFP_HALT                                     (1<<26)
+#define                CP_ME_HALT                                      (1 << 28)
+#define                CP_PFP_HALT                                     (1 << 26)
 #define        CP_ME_RAM_DATA                                  0xC160
 #define        CP_ME_RAM_RADDR                                 0xC158
 #define        CP_ME_RAM_WADDR                                 0xC15C
 #define                GUI_ACTIVE                                      (1<<31)
 #define        GRBM_STATUS2                                    0x8014
 
-#define CG_CLKPIN_CNTL                                    0x660
-#       define MUX_TCLK_TO_XCLK                           (1 << 8)
-#       define XTALIN_DIVIDE                              (1 << 9)
+#define        CG_THERMAL_CTRL                                 0x72C
+#define        DPM_EVENT_SRC(x)                        ((x) << 0)
+#define        DPM_EVENT_SRC_MASK                      (7 << 0)
+#define                DIG_THERM_DPM(x)                        ((x) << 14)
+#define                DIG_THERM_DPM_MASK                      0x003FC000
+#define                DIG_THERM_DPM_SHIFT                     14
+
+#define        CG_THERMAL_INT                                  0x734
+#define                DIG_THERM_INTH(x)                       ((x) << 8)
+#define                DIG_THERM_INTH_MASK                     0x0000FF00
+#define                DIG_THERM_INTH_SHIFT                    8
+#define                DIG_THERM_INTL(x)                       ((x) << 16)
+#define                DIG_THERM_INTL_MASK                     0x00FF0000
+#define                DIG_THERM_INTL_SHIFT                    16
+#define        THERM_INT_MASK_HIGH                     (1 << 24)
+#define        THERM_INT_MASK_LOW                      (1 << 25)
 
 #define        CG_MULT_THERMAL_STATUS                          0x740
 #define                ASIC_T(x)                               ((x) << 16)
 #define D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH             0x691c
 #define D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH             0x611c
 
-/* PCIE link stuff */
+/* PCIE indirect regs */
+#define PCIE_P_CNTL                                       0x40
+#       define P_PLL_PWRDN_IN_L1L23                       (1 << 3)
+#       define P_PLL_BUF_PDNB                             (1 << 4)
+#       define P_PLL_PDNB                                 (1 << 9)
+#       define P_ALLOW_PRX_FRONTEND_SHUTOFF               (1 << 12)
+/* PCIE PORT regs */
+#define PCIE_LC_CNTL                                      0xa0
+#       define LC_L0S_INACTIVITY(x)                       ((x) << 8)
+#       define LC_L0S_INACTIVITY_MASK                     (0xf << 8)
+#       define LC_L0S_INACTIVITY_SHIFT                    8
+#       define LC_L1_INACTIVITY(x)                        ((x) << 12)
+#       define LC_L1_INACTIVITY_MASK                      (0xf << 12)
+#       define LC_L1_INACTIVITY_SHIFT                     12
+#       define LC_PMI_TO_L1_DIS                           (1 << 16)
+#       define LC_ASPM_TO_L1_DIS                          (1 << 24)
 #define PCIE_LC_TRAINING_CNTL                             0xa1 /* PCIE_P */
 #define PCIE_LC_LINK_WIDTH_CNTL                           0xa2 /* PCIE_P */
 #       define LC_LINK_WIDTH_SHIFT                        0
 #       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK      (0x3 << 8)
 #       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT     3
 #       define LC_CURRENT_DATA_RATE                       (1 << 11)
+#       define LC_HW_VOLTAGE_IF_CONTROL(x)                ((x) << 12)
+#       define LC_HW_VOLTAGE_IF_CONTROL_MASK              (3 << 12)
+#       define LC_HW_VOLTAGE_IF_CONTROL_SHIFT             12
 #       define LC_VOLTAGE_TIMER_SEL_MASK                  (0xf << 14)
 #       define LC_CLR_FAILED_SPD_CHANGE_CNT               (1 << 21)
 #       define LC_OTHER_SIDE_EVER_SENT_GEN2               (1 << 23)
index a1b0da6..2349067 100644 (file)
 #include "sid.h"
 #include "atom.h"
 #include "si_blit_shaders.h"
+#include "clearstate_si.h"
+#include "radeon_ucode.h"
 
-#define SI_PFP_UCODE_SIZE 2144
-#define SI_PM4_UCODE_SIZE 2144
-#define SI_CE_UCODE_SIZE 2144
-#define SI_RLC_UCODE_SIZE 2048
-#define SI_MC_UCODE_SIZE 7769
-#define OLAND_MC_UCODE_SIZE 7863
 
 MODULE_FIRMWARE("radeon/TAHITI_pfp.bin");
 MODULE_FIRMWARE("radeon/TAHITI_me.bin");
 MODULE_FIRMWARE("radeon/TAHITI_ce.bin");
 MODULE_FIRMWARE("radeon/TAHITI_mc.bin");
 MODULE_FIRMWARE("radeon/TAHITI_rlc.bin");
+MODULE_FIRMWARE("radeon/TAHITI_smc.bin");
 MODULE_FIRMWARE("radeon/PITCAIRN_pfp.bin");
 MODULE_FIRMWARE("radeon/PITCAIRN_me.bin");
 MODULE_FIRMWARE("radeon/PITCAIRN_ce.bin");
 MODULE_FIRMWARE("radeon/PITCAIRN_mc.bin");
 MODULE_FIRMWARE("radeon/PITCAIRN_rlc.bin");
+MODULE_FIRMWARE("radeon/PITCAIRN_smc.bin");
 MODULE_FIRMWARE("radeon/VERDE_pfp.bin");
 MODULE_FIRMWARE("radeon/VERDE_me.bin");
 MODULE_FIRMWARE("radeon/VERDE_ce.bin");
 MODULE_FIRMWARE("radeon/VERDE_mc.bin");
 MODULE_FIRMWARE("radeon/VERDE_rlc.bin");
+MODULE_FIRMWARE("radeon/VERDE_smc.bin");
 MODULE_FIRMWARE("radeon/OLAND_pfp.bin");
 MODULE_FIRMWARE("radeon/OLAND_me.bin");
 MODULE_FIRMWARE("radeon/OLAND_ce.bin");
 MODULE_FIRMWARE("radeon/OLAND_mc.bin");
 MODULE_FIRMWARE("radeon/OLAND_rlc.bin");
+MODULE_FIRMWARE("radeon/OLAND_smc.bin");
 MODULE_FIRMWARE("radeon/HAINAN_pfp.bin");
 MODULE_FIRMWARE("radeon/HAINAN_me.bin");
 MODULE_FIRMWARE("radeon/HAINAN_ce.bin");
 MODULE_FIRMWARE("radeon/HAINAN_mc.bin");
 MODULE_FIRMWARE("radeon/HAINAN_rlc.bin");
+MODULE_FIRMWARE("radeon/HAINAN_smc.bin");
 
+static void si_pcie_gen3_enable(struct radeon_device *rdev);
+static void si_program_aspm(struct radeon_device *rdev);
 extern int r600_ih_ring_alloc(struct radeon_device *rdev);
 extern void r600_ih_ring_fini(struct radeon_device *rdev);
 extern void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev);
@@ -75,6 +78,228 @@ 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);
 
+static const u32 verde_rlc_save_restore_register_list[] =
+{
+       (0x8000 << 16) | (0x98f4 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x98f4 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0xe80 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0xe80 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x89bc >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x89bc >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x8c1c >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x8c1c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x98f0 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xe7c >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x9148 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x9148 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9150 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x897c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8d8c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xac54 >> 2),
+       0X00000000,
+       0x3,
+       (0x9c00 << 16) | (0x98f8 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9910 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9914 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9918 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x991c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9920 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9924 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9928 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x992c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9930 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9934 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9938 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x993c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9940 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9944 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9948 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x994c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9950 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9954 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9958 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x995c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9960 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9964 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9968 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x996c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9970 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9974 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9978 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x997c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9980 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9984 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9988 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x998c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8c00 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8c14 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8c04 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8c08 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x9b7c >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x9b7c >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0xe84 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0xe84 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x89c0 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x89c0 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x914c >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x914c >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x8c20 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x8c20 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x9354 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x9354 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9060 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9364 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9100 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x913c >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x90e0 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x90e4 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x90e8 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x90e0 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x90e4 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x90e8 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8bcc >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8b24 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x88c4 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8e50 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8c0c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8e58 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8e5c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9508 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x950c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9494 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xac0c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xac10 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xac14 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xae00 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xac08 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x88d4 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x88c8 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x88cc >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x89b0 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8b10 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8a14 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9830 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9834 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9838 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9a10 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x9870 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x9874 >> 2),
+       0x00000000,
+       (0x8001 << 16) | (0x9870 >> 2),
+       0x00000000,
+       (0x8001 << 16) | (0x9874 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x9870 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x9874 >> 2),
+       0x00000000,
+       (0x8041 << 16) | (0x9870 >> 2),
+       0x00000000,
+       (0x8041 << 16) | (0x9874 >> 2),
+       0x00000000,
+       0x00000000
+};
+
 static const u32 tahiti_golden_rlc_registers[] =
 {
        0xc424, 0xffffffff, 0x00601005,
@@ -1320,6 +1545,7 @@ static int si_init_microcode(struct radeon_device *rdev)
        const char *chip_name;
        const char *rlc_chip_name;
        size_t pfp_req_size, me_req_size, ce_req_size, rlc_req_size, mc_req_size;
+       size_t smc_req_size;
        char fw_name[30];
        int err;
 
@@ -1341,6 +1567,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                ce_req_size = SI_CE_UCODE_SIZE * 4;
                rlc_req_size = SI_RLC_UCODE_SIZE * 4;
                mc_req_size = SI_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(TAHITI_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_PITCAIRN:
                chip_name = "PITCAIRN";
@@ -1350,6 +1577,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                ce_req_size = SI_CE_UCODE_SIZE * 4;
                rlc_req_size = SI_RLC_UCODE_SIZE * 4;
                mc_req_size = SI_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(PITCAIRN_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_VERDE:
                chip_name = "VERDE";
@@ -1359,6 +1587,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                ce_req_size = SI_CE_UCODE_SIZE * 4;
                rlc_req_size = SI_RLC_UCODE_SIZE * 4;
                mc_req_size = SI_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(VERDE_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_OLAND:
                chip_name = "OLAND";
@@ -1368,6 +1597,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                ce_req_size = SI_CE_UCODE_SIZE * 4;
                rlc_req_size = SI_RLC_UCODE_SIZE * 4;
                mc_req_size = OLAND_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(OLAND_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_HAINAN:
                chip_name = "HAINAN";
@@ -1377,6 +1607,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                ce_req_size = SI_CE_UCODE_SIZE * 4;
                rlc_req_size = SI_RLC_UCODE_SIZE * 4;
                mc_req_size = OLAND_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(HAINAN_SMC_UCODE_SIZE, 4);
                break;
        default: BUG();
        }
@@ -1439,6 +1670,17 @@ static int si_init_microcode(struct radeon_device *rdev)
                err = -EINVAL;
        }
 
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name);
+       err = request_firmware(&rdev->smc_fw, fw_name, &pdev->dev);
+       if (err)
+               goto out;
+       if (rdev->smc_fw->size != smc_req_size) {
+               printk(KERN_ERR
+                      "si_smc: Bogus length %zu in firmware \"%s\"\n",
+                      rdev->smc_fw->size, fw_name);
+               err = -EINVAL;
+       }
+
 out:
        platform_device_unregister(pdev);
 
@@ -1457,6 +1699,8 @@ out:
                rdev->rlc_fw = NULL;
                release_firmware(rdev->mc_fw);
                rdev->mc_fw = NULL;
+               release_firmware(rdev->smc_fw);
+               rdev->smc_fw = NULL;
        }
        return err;
 }
@@ -1792,7 +2036,8 @@ static void dce6_program_watermarks(struct radeon_device *rdev,
                                         u32 lb_size, u32 num_heads)
 {
        struct drm_display_mode *mode = &radeon_crtc->base.mode;
-       struct dce6_wm_params wm;
+       struct dce6_wm_params wm_low, wm_high;
+       u32 dram_channels;
        u32 pixel_period;
        u32 line_time = 0;
        u32 latency_watermark_a = 0, latency_watermark_b = 0;
@@ -1808,38 +2053,83 @@ static void dce6_program_watermarks(struct radeon_device *rdev,
                priority_a_cnt = 0;
                priority_b_cnt = 0;
 
-               wm.yclk = rdev->pm.current_mclk * 10;
-               wm.sclk = rdev->pm.current_sclk * 10;
-               wm.disp_clk = mode->clock;
-               wm.src_width = mode->crtc_hdisplay;
-               wm.active_time = mode->crtc_hdisplay * pixel_period;
-               wm.blank_time = line_time - wm.active_time;
-               wm.interlaced = false;
-               if (mode->flags & DRM_MODE_FLAG_INTERLACE)
-                       wm.interlaced = true;
-               wm.vsc = radeon_crtc->vsc;
-               wm.vtaps = 1;
-               if (radeon_crtc->rmx_type != RMX_OFF)
-                       wm.vtaps = 2;
-               wm.bytes_per_pixel = 4; /* XXX: get this from fb config */
-               wm.lb_size = lb_size;
                if (rdev->family == CHIP_ARUBA)
-                       wm.dram_channels = evergreen_get_number_of_dram_channels(rdev);
+                       dram_channels = evergreen_get_number_of_dram_channels(rdev);
                else
-                       wm.dram_channels = si_get_number_of_dram_channels(rdev);
-               wm.num_heads = num_heads;
+                       dram_channels = si_get_number_of_dram_channels(rdev);
+
+               /* watermark for high clocks */
+               if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
+                       wm_high.yclk =
+                               radeon_dpm_get_mclk(rdev, false) * 10;
+                       wm_high.sclk =
+                               radeon_dpm_get_sclk(rdev, false) * 10;
+               } else {
+                       wm_high.yclk = rdev->pm.current_mclk * 10;
+                       wm_high.sclk = rdev->pm.current_sclk * 10;
+               }
+
+               wm_high.disp_clk = mode->clock;
+               wm_high.src_width = mode->crtc_hdisplay;
+               wm_high.active_time = mode->crtc_hdisplay * pixel_period;
+               wm_high.blank_time = line_time - wm_high.active_time;
+               wm_high.interlaced = false;
+               if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+                       wm_high.interlaced = true;
+               wm_high.vsc = radeon_crtc->vsc;
+               wm_high.vtaps = 1;
+               if (radeon_crtc->rmx_type != RMX_OFF)
+                       wm_high.vtaps = 2;
+               wm_high.bytes_per_pixel = 4; /* XXX: get this from fb config */
+               wm_high.lb_size = lb_size;
+               wm_high.dram_channels = dram_channels;
+               wm_high.num_heads = num_heads;
+
+               /* watermark for low clocks */
+               if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
+                       wm_low.yclk =
+                               radeon_dpm_get_mclk(rdev, true) * 10;
+                       wm_low.sclk =
+                               radeon_dpm_get_sclk(rdev, true) * 10;
+               } else {
+                       wm_low.yclk = rdev->pm.current_mclk * 10;
+                       wm_low.sclk = rdev->pm.current_sclk * 10;
+               }
+
+               wm_low.disp_clk = mode->clock;
+               wm_low.src_width = mode->crtc_hdisplay;
+               wm_low.active_time = mode->crtc_hdisplay * pixel_period;
+               wm_low.blank_time = line_time - wm_low.active_time;
+               wm_low.interlaced = false;
+               if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+                       wm_low.interlaced = true;
+               wm_low.vsc = radeon_crtc->vsc;
+               wm_low.vtaps = 1;
+               if (radeon_crtc->rmx_type != RMX_OFF)
+                       wm_low.vtaps = 2;
+               wm_low.bytes_per_pixel = 4; /* XXX: get this from fb config */
+               wm_low.lb_size = lb_size;
+               wm_low.dram_channels = dram_channels;
+               wm_low.num_heads = num_heads;
 
                /* set for high clocks */
-               latency_watermark_a = min(dce6_latency_watermark(&wm), (u32)65535);
+               latency_watermark_a = min(dce6_latency_watermark(&wm_high), (u32)65535);
                /* set for low clocks */
-               /* wm.yclk = low clk; wm.sclk = low clk */
-               latency_watermark_b = min(dce6_latency_watermark(&wm), (u32)65535);
+               latency_watermark_b = min(dce6_latency_watermark(&wm_low), (u32)65535);
 
                /* possibly force display priority to high */
                /* should really do this at mode validation time... */
-               if (!dce6_average_bandwidth_vs_dram_bandwidth_for_display(&wm) ||
-                   !dce6_average_bandwidth_vs_available_bandwidth(&wm) ||
-                   !dce6_check_latency_hiding(&wm) ||
+               if (!dce6_average_bandwidth_vs_dram_bandwidth_for_display(&wm_high) ||
+                   !dce6_average_bandwidth_vs_available_bandwidth(&wm_high) ||
+                   !dce6_check_latency_hiding(&wm_high) ||
+                   (rdev->disp_priority == 2)) {
+                       DRM_DEBUG_KMS("force priority to high\n");
+                       priority_a_cnt |= PRIORITY_ALWAYS_ON;
+                       priority_b_cnt |= PRIORITY_ALWAYS_ON;
+               }
+               if (!dce6_average_bandwidth_vs_dram_bandwidth_for_display(&wm_low) ||
+                   !dce6_average_bandwidth_vs_available_bandwidth(&wm_low) ||
+                   !dce6_check_latency_hiding(&wm_low) ||
                    (rdev->disp_priority == 2)) {
                        DRM_DEBUG_KMS("force priority to high\n");
                        priority_a_cnt |= PRIORITY_ALWAYS_ON;
@@ -1895,6 +2185,10 @@ static void dce6_program_watermarks(struct radeon_device *rdev,
        WREG32(PRIORITY_A_CNT + radeon_crtc->crtc_offset, priority_a_cnt);
        WREG32(PRIORITY_B_CNT + radeon_crtc->crtc_offset, priority_b_cnt);
 
+       /* save values for DPM */
+       radeon_crtc->line_time = line_time;
+       radeon_crtc->wm_high = latency_watermark_a;
+       radeon_crtc->wm_low = latency_watermark_b;
 }
 
 void dce6_bandwidth_update(struct radeon_device *rdev)
@@ -3535,8 +3829,8 @@ static void si_mc_program(struct radeon_device *rdev)
        }
 }
 
-static void si_vram_gtt_location(struct radeon_device *rdev,
-                                struct radeon_mc *mc)
+void si_vram_gtt_location(struct radeon_device *rdev,
+                         struct radeon_mc *mc)
 {
        if (mc->mc_vram_size > 0xFFC0000000ULL) {
                /* leave room for at least 1024M GTT */
@@ -4281,6 +4575,450 @@ void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
        radeon_ring_write(ring, 1 << vm->id);
 }
 
+/*
+ *  Power and clock gating
+ */
+static void si_wait_for_rlc_serdes(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(RLC_SERDES_MASTER_BUSY_0) == 0)
+                       break;
+               udelay(1);
+       }
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(RLC_SERDES_MASTER_BUSY_1) == 0)
+                       break;
+               udelay(1);
+       }
+}
+
+static void si_enable_gui_idle_interrupt(struct radeon_device *rdev,
+                                        bool enable)
+{
+       u32 tmp = RREG32(CP_INT_CNTL_RING0);
+       u32 mask;
+       int i;
+
+       if (enable)
+               tmp |= (CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE);
+       else
+               tmp &= ~(CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE);
+       WREG32(CP_INT_CNTL_RING0, tmp);
+
+       if (!enable) {
+               /* read a gfx register */
+               tmp = RREG32(DB_DEPTH_INFO);
+
+               mask = RLC_BUSY_STATUS | GFX_POWER_STATUS | GFX_CLOCK_STATUS | GFX_LS_STATUS;
+               for (i = 0; i < rdev->usec_timeout; i++) {
+                       if ((RREG32(RLC_STAT) & mask) == (GFX_CLOCK_STATUS | GFX_POWER_STATUS))
+                               break;
+                       udelay(1);
+               }
+       }
+}
+
+static void si_set_uvd_dcm(struct radeon_device *rdev,
+                          bool sw_mode)
+{
+       u32 tmp, tmp2;
+
+       tmp = RREG32(UVD_CGC_CTRL);
+       tmp &= ~(CLK_OD_MASK | CG_DT_MASK);
+       tmp |= DCM | CG_DT(1) | CLK_OD(4);
+
+       if (sw_mode) {
+               tmp &= ~0x7ffff800;
+               tmp2 = DYN_OR_EN | DYN_RR_EN | G_DIV_ID(7);
+       } else {
+               tmp |= 0x7ffff800;
+               tmp2 = 0;
+       }
+
+       WREG32(UVD_CGC_CTRL, tmp);
+       WREG32_UVD_CTX(UVD_CGC_CTRL2, tmp2);
+}
+
+static void si_init_uvd_internal_cg(struct radeon_device *rdev)
+{
+       bool hw_mode = true;
+
+       if (hw_mode) {
+               si_set_uvd_dcm(rdev, false);
+       } else {
+               u32 tmp = RREG32(UVD_CGC_CTRL);
+               tmp &= ~DCM;
+               WREG32(UVD_CGC_CTRL, tmp);
+       }
+}
+
+static u32 si_halt_rlc(struct radeon_device *rdev)
+{
+       u32 data, orig;
+
+       orig = data = RREG32(RLC_CNTL);
+
+       if (data & RLC_ENABLE) {
+               data &= ~RLC_ENABLE;
+               WREG32(RLC_CNTL, data);
+
+               si_wait_for_rlc_serdes(rdev);
+       }
+
+       return orig;
+}
+
+static void si_update_rlc(struct radeon_device *rdev, u32 rlc)
+{
+       u32 tmp;
+
+       tmp = RREG32(RLC_CNTL);
+       if (tmp != rlc)
+               WREG32(RLC_CNTL, rlc);
+}
+
+static void si_enable_dma_pg(struct radeon_device *rdev, bool enable)
+{
+       u32 data, orig;
+
+       orig = data = RREG32(DMA_PG);
+       if (enable)
+               data |= PG_CNTL_ENABLE;
+       else
+               data &= ~PG_CNTL_ENABLE;
+       if (orig != data)
+               WREG32(DMA_PG, data);
+}
+
+static void si_init_dma_pg(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       WREG32(DMA_PGFSM_WRITE,  0x00002000);
+       WREG32(DMA_PGFSM_CONFIG, 0x100010ff);
+
+       for (tmp = 0; tmp < 5; tmp++)
+               WREG32(DMA_PGFSM_WRITE, 0);
+}
+
+static void si_enable_gfx_cgpg(struct radeon_device *rdev,
+                              bool enable)
+{
+       u32 tmp;
+
+       if (enable) {
+               tmp = RLC_PUD(0x10) | RLC_PDD(0x10) | RLC_TTPD(0x10) | RLC_MSD(0x10);
+               WREG32(RLC_TTOP_D, tmp);
+
+               tmp = RREG32(RLC_PG_CNTL);
+               tmp |= GFX_PG_ENABLE;
+               WREG32(RLC_PG_CNTL, tmp);
+
+               tmp = RREG32(RLC_AUTO_PG_CTRL);
+               tmp |= AUTO_PG_EN;
+               WREG32(RLC_AUTO_PG_CTRL, tmp);
+       } else {
+               tmp = RREG32(RLC_AUTO_PG_CTRL);
+               tmp &= ~AUTO_PG_EN;
+               WREG32(RLC_AUTO_PG_CTRL, tmp);
+
+               tmp = RREG32(DB_RENDER_CONTROL);
+       }
+}
+
+static void si_init_gfx_cgpg(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       WREG32(RLC_SAVE_AND_RESTORE_BASE, rdev->rlc.save_restore_gpu_addr >> 8);
+
+       tmp = RREG32(RLC_PG_CNTL);
+       tmp |= GFX_PG_SRC;
+       WREG32(RLC_PG_CNTL, tmp);
+
+       WREG32(RLC_CLEAR_STATE_RESTORE_BASE, rdev->rlc.clear_state_gpu_addr >> 8);
+
+       tmp = RREG32(RLC_AUTO_PG_CTRL);
+
+       tmp &= ~GRBM_REG_SGIT_MASK;
+       tmp |= GRBM_REG_SGIT(0x700);
+       tmp &= ~PG_AFTER_GRBM_REG_ST_MASK;
+       WREG32(RLC_AUTO_PG_CTRL, tmp);
+}
+
+static u32 si_get_cu_active_bitmap(struct radeon_device *rdev, u32 se, u32 sh)
+{
+       u32 mask = 0, tmp, tmp1;
+       int i;
+
+       si_select_se_sh(rdev, se, sh);
+       tmp = RREG32(CC_GC_SHADER_ARRAY_CONFIG);
+       tmp1 = RREG32(GC_USER_SHADER_ARRAY_CONFIG);
+       si_select_se_sh(rdev, 0xffffffff, 0xffffffff);
+
+       tmp &= 0xffff0000;
+
+       tmp |= tmp1;
+       tmp >>= 16;
+
+       for (i = 0; i < rdev->config.si.max_cu_per_sh; i ++) {
+               mask <<= 1;
+               mask |= 1;
+       }
+
+       return (~tmp) & mask;
+}
+
+static void si_init_ao_cu_mask(struct radeon_device *rdev)
+{
+       u32 i, j, k, active_cu_number = 0;
+       u32 mask, counter, cu_bitmap;
+       u32 tmp = 0;
+
+       for (i = 0; i < rdev->config.si.max_shader_engines; i++) {
+               for (j = 0; j < rdev->config.si.max_sh_per_se; j++) {
+                       mask = 1;
+                       cu_bitmap = 0;
+                       counter  = 0;
+                       for (k = 0; k < rdev->config.si.max_cu_per_sh; k++) {
+                               if (si_get_cu_active_bitmap(rdev, i, j) & mask) {
+                                       if (counter < 2)
+                                               cu_bitmap |= mask;
+                                       counter++;
+                               }
+                               mask <<= 1;
+                       }
+
+                       active_cu_number += counter;
+                       tmp |= (cu_bitmap << (i * 16 + j * 8));
+               }
+       }
+
+       WREG32(RLC_PG_AO_CU_MASK, tmp);
+
+       tmp = RREG32(RLC_MAX_PG_CU);
+       tmp &= ~MAX_PU_CU_MASK;
+       tmp |= MAX_PU_CU(active_cu_number);
+       WREG32(RLC_MAX_PG_CU, tmp);
+}
+
+static void si_enable_cgcg(struct radeon_device *rdev,
+                          bool enable)
+{
+       u32 data, orig, tmp;
+
+       orig = data = RREG32(RLC_CGCG_CGLS_CTRL);
+
+       si_enable_gui_idle_interrupt(rdev, enable);
+
+       if (enable) {
+               WREG32(RLC_GCPM_GENERAL_3, 0x00000080);
+
+               tmp = si_halt_rlc(rdev);
+
+               WREG32(RLC_SERDES_WR_MASTER_MASK_0, 0xffffffff);
+               WREG32(RLC_SERDES_WR_MASTER_MASK_1, 0xffffffff);
+               WREG32(RLC_SERDES_WR_CTRL, 0x00b000ff);
+
+               si_wait_for_rlc_serdes(rdev);
+
+               si_update_rlc(rdev, tmp);
+
+               WREG32(RLC_SERDES_WR_CTRL, 0x007000ff);
+
+               data |= CGCG_EN | CGLS_EN;
+       } else {
+               RREG32(CB_CGTT_SCLK_CTRL);
+               RREG32(CB_CGTT_SCLK_CTRL);
+               RREG32(CB_CGTT_SCLK_CTRL);
+               RREG32(CB_CGTT_SCLK_CTRL);
+
+               data &= ~(CGCG_EN | CGLS_EN);
+       }
+
+       if (orig != data)
+               WREG32(RLC_CGCG_CGLS_CTRL, data);
+}
+
+static void si_enable_mgcg(struct radeon_device *rdev,
+                          bool enable)
+{
+       u32 data, orig, tmp = 0;
+
+       if (enable) {
+               orig = data = RREG32(CGTS_SM_CTRL_REG);
+               data = 0x96940200;
+               if (orig != data)
+                       WREG32(CGTS_SM_CTRL_REG, data);
+
+               orig = data = RREG32(CP_MEM_SLP_CNTL);
+               data |= CP_MEM_LS_EN;
+               if (orig != data)
+                       WREG32(CP_MEM_SLP_CNTL, data);
+
+               orig = data = RREG32(RLC_CGTT_MGCG_OVERRIDE);
+               data &= 0xffffffc0;
+               if (orig != data)
+                       WREG32(RLC_CGTT_MGCG_OVERRIDE, data);
+
+               tmp = si_halt_rlc(rdev);
+
+               WREG32(RLC_SERDES_WR_MASTER_MASK_0, 0xffffffff);
+               WREG32(RLC_SERDES_WR_MASTER_MASK_1, 0xffffffff);
+               WREG32(RLC_SERDES_WR_CTRL, 0x00d000ff);
+
+               si_update_rlc(rdev, tmp);
+       } else {
+               orig = data = RREG32(RLC_CGTT_MGCG_OVERRIDE);
+               data |= 0x00000003;
+               if (orig != data)
+                       WREG32(RLC_CGTT_MGCG_OVERRIDE, data);
+
+               data = RREG32(CP_MEM_SLP_CNTL);
+               if (data & CP_MEM_LS_EN) {
+                       data &= ~CP_MEM_LS_EN;
+                       WREG32(CP_MEM_SLP_CNTL, data);
+               }
+               orig = data = RREG32(CGTS_SM_CTRL_REG);
+               data |= LS_OVERRIDE | OVERRIDE;
+               if (orig != data)
+                       WREG32(CGTS_SM_CTRL_REG, data);
+
+               tmp = si_halt_rlc(rdev);
+
+               WREG32(RLC_SERDES_WR_MASTER_MASK_0, 0xffffffff);
+               WREG32(RLC_SERDES_WR_MASTER_MASK_1, 0xffffffff);
+               WREG32(RLC_SERDES_WR_CTRL, 0x00e000ff);
+
+               si_update_rlc(rdev, tmp);
+       }
+}
+
+static void si_enable_uvd_mgcg(struct radeon_device *rdev,
+                              bool enable)
+{
+       u32 orig, data, tmp;
+
+       if (enable) {
+               tmp = RREG32_UVD_CTX(UVD_CGC_MEM_CTRL);
+               tmp |= 0x3fff;
+               WREG32_UVD_CTX(UVD_CGC_MEM_CTRL, tmp);
+
+               orig = data = RREG32(UVD_CGC_CTRL);
+               data |= DCM;
+               if (orig != data)
+                       WREG32(UVD_CGC_CTRL, data);
+
+               WREG32_SMC(SMC_CG_IND_START + CG_CGTT_LOCAL_0, 0);
+               WREG32_SMC(SMC_CG_IND_START + CG_CGTT_LOCAL_1, 0);
+       } else {
+               tmp = RREG32_UVD_CTX(UVD_CGC_MEM_CTRL);
+               tmp &= ~0x3fff;
+               WREG32_UVD_CTX(UVD_CGC_MEM_CTRL, tmp);
+
+               orig = data = RREG32(UVD_CGC_CTRL);
+               data &= ~DCM;
+               if (orig != data)
+                       WREG32(UVD_CGC_CTRL, data);
+
+               WREG32_SMC(SMC_CG_IND_START + CG_CGTT_LOCAL_0, 0xffffffff);
+               WREG32_SMC(SMC_CG_IND_START + CG_CGTT_LOCAL_1, 0xffffffff);
+       }
+}
+
+static const u32 mc_cg_registers[] =
+{
+       MC_HUB_MISC_HUB_CG,
+       MC_HUB_MISC_SIP_CG,
+       MC_HUB_MISC_VM_CG,
+       MC_XPB_CLK_GAT,
+       ATC_MISC_CG,
+       MC_CITF_MISC_WR_CG,
+       MC_CITF_MISC_RD_CG,
+       MC_CITF_MISC_VM_CG,
+       VM_L2_CG,
+};
+
+static void si_enable_mc_ls(struct radeon_device *rdev,
+                           bool enable)
+{
+       int i;
+       u32 orig, data;
+
+       for (i = 0; i < ARRAY_SIZE(mc_cg_registers); i++) {
+               orig = data = RREG32(mc_cg_registers[i]);
+               if (enable)
+                       data |= MC_LS_ENABLE;
+               else
+                       data &= ~MC_LS_ENABLE;
+               if (data != orig)
+                       WREG32(mc_cg_registers[i], data);
+       }
+}
+
+
+static void si_init_cg(struct radeon_device *rdev)
+{
+       bool has_uvd = true;
+
+       si_enable_mgcg(rdev, true);
+       si_enable_cgcg(rdev, true);
+       /* disable MC LS on Tahiti */
+       if (rdev->family == CHIP_TAHITI)
+               si_enable_mc_ls(rdev, false);
+       if (has_uvd) {
+               si_enable_uvd_mgcg(rdev, true);
+               si_init_uvd_internal_cg(rdev);
+       }
+}
+
+static void si_fini_cg(struct radeon_device *rdev)
+{
+       bool has_uvd = true;
+
+       if (has_uvd)
+               si_enable_uvd_mgcg(rdev, false);
+       si_enable_cgcg(rdev, false);
+       si_enable_mgcg(rdev, false);
+}
+
+static void si_init_pg(struct radeon_device *rdev)
+{
+       bool has_pg = false;
+
+       /* only cape verde supports PG */
+       if (rdev->family == CHIP_VERDE)
+               has_pg = true;
+
+       if (has_pg) {
+               si_init_ao_cu_mask(rdev);
+               si_init_dma_pg(rdev);
+               si_enable_dma_pg(rdev, true);
+               si_init_gfx_cgpg(rdev);
+               si_enable_gfx_cgpg(rdev, true);
+       } else {
+               WREG32(RLC_SAVE_AND_RESTORE_BASE, rdev->rlc.save_restore_gpu_addr >> 8);
+               WREG32(RLC_CLEAR_STATE_RESTORE_BASE, rdev->rlc.clear_state_gpu_addr >> 8);
+       }
+}
+
+static void si_fini_pg(struct radeon_device *rdev)
+{
+       bool has_pg = false;
+
+       /* only cape verde supports PG */
+       if (rdev->family == CHIP_VERDE)
+               has_pg = true;
+
+       if (has_pg) {
+               si_enable_dma_pg(rdev, false);
+               si_enable_gfx_cgpg(rdev, false);
+       }
+}
+
 /*
  * RLC
  */
@@ -4313,8 +5051,15 @@ void si_rlc_fini(struct radeon_device *rdev)
        }
 }
 
+#define RLC_CLEAR_STATE_END_MARKER          0x00000001
+
 int si_rlc_init(struct radeon_device *rdev)
 {
+       volatile u32 *dst_ptr;
+       u32 dws, data, i, j, k, reg_num;
+       u32 reg_list_num, reg_list_hdr_blk_index, reg_list_blk_index;
+       u64 reg_list_mc_addr;
+       const struct cs_section_def *cs_data = si_cs_data;
        int r;
 
        /* save restore block */
@@ -4335,18 +5080,44 @@ int si_rlc_init(struct radeon_device *rdev)
        }
        r = radeon_bo_pin(rdev->rlc.save_restore_obj, RADEON_GEM_DOMAIN_VRAM,
                          &rdev->rlc.save_restore_gpu_addr);
-       radeon_bo_unreserve(rdev->rlc.save_restore_obj);
        if (r) {
+               radeon_bo_unreserve(rdev->rlc.save_restore_obj);
                dev_warn(rdev->dev, "(%d) pin RLC sr bo failed\n", r);
                si_rlc_fini(rdev);
                return r;
        }
 
+       if (rdev->family == CHIP_VERDE) {
+               r = radeon_bo_kmap(rdev->rlc.save_restore_obj, (void **)&rdev->rlc.sr_ptr);
+               if (r) {
+                       dev_warn(rdev->dev, "(%d) map RLC sr bo failed\n", r);
+                       si_rlc_fini(rdev);
+               return r;
+               }
+               /* write the sr buffer */
+               dst_ptr = rdev->rlc.sr_ptr;
+               for (i = 0; i < ARRAY_SIZE(verde_rlc_save_restore_register_list); i++) {
+                       dst_ptr[i] = verde_rlc_save_restore_register_list[i];
+               }
+               radeon_bo_kunmap(rdev->rlc.save_restore_obj);
+       }
+       radeon_bo_unreserve(rdev->rlc.save_restore_obj);
+
        /* clear state block */
+       reg_list_num = 0;
+       dws = 0;
+       for (i = 0; cs_data[i].section != NULL; i++) {
+               for (j = 0; cs_data[i].section[j].extent != NULL; j++) {
+                       reg_list_num++;
+                       dws += cs_data[i].section[j].reg_count;
+               }
+       }
+       reg_list_blk_index = (3 * reg_list_num + 2);
+       dws += reg_list_blk_index;
+
        if (rdev->rlc.clear_state_obj == NULL) {
-               r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true,
-                                    RADEON_GEM_DOMAIN_VRAM, NULL,
-                                    &rdev->rlc.clear_state_obj);
+               r = radeon_bo_create(rdev, dws * 4, PAGE_SIZE, true,
+                                    RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->rlc.clear_state_obj);
                if (r) {
                        dev_warn(rdev->dev, "(%d) create RLC c bo failed\n", r);
                        si_rlc_fini(rdev);
@@ -4360,24 +5131,113 @@ int si_rlc_init(struct radeon_device *rdev)
        }
        r = radeon_bo_pin(rdev->rlc.clear_state_obj, RADEON_GEM_DOMAIN_VRAM,
                          &rdev->rlc.clear_state_gpu_addr);
-       radeon_bo_unreserve(rdev->rlc.clear_state_obj);
        if (r) {
+
+               radeon_bo_unreserve(rdev->rlc.clear_state_obj);
                dev_warn(rdev->dev, "(%d) pin RLC c bo failed\n", r);
                si_rlc_fini(rdev);
                return r;
        }
+       r = radeon_bo_kmap(rdev->rlc.clear_state_obj, (void **)&rdev->rlc.cs_ptr);
+       if (r) {
+               dev_warn(rdev->dev, "(%d) map RLC c bo failed\n", r);
+               si_rlc_fini(rdev);
+               return r;
+       }
+       /* set up the cs buffer */
+       dst_ptr = rdev->rlc.cs_ptr;
+       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;
+       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;
+                       reg_list_hdr_blk_index++;
+
+                       data = (cs_data[i].section[j].reg_index * 4) & 0xffffffff;
+                       dst_ptr[reg_list_hdr_blk_index] = data;
+                       reg_list_hdr_blk_index++;
+
+                       data = 0x08000000 | (reg_num * 4);
+                       dst_ptr[reg_list_hdr_blk_index] = 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;
+                       }
+                       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;
+
+       radeon_bo_kunmap(rdev->rlc.clear_state_obj);
+       radeon_bo_unreserve(rdev->rlc.clear_state_obj);
 
        return 0;
 }
 
+static void si_rlc_reset(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32(GRBM_SOFT_RESET);
+
+       tmp |= SOFT_RESET_RLC;
+       WREG32(GRBM_SOFT_RESET, tmp);
+       udelay(50);
+       tmp &= ~SOFT_RESET_RLC;
+       WREG32(GRBM_SOFT_RESET, tmp);
+       udelay(50);
+}
+
 static void si_rlc_stop(struct radeon_device *rdev)
 {
        WREG32(RLC_CNTL, 0);
+
+       si_enable_gui_idle_interrupt(rdev, false);
+
+       si_wait_for_rlc_serdes(rdev);
 }
 
 static void si_rlc_start(struct radeon_device *rdev)
 {
        WREG32(RLC_CNTL, RLC_ENABLE);
+
+       si_enable_gui_idle_interrupt(rdev, true);
+
+       udelay(50);
+}
+
+static bool si_lbpw_supported(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       /* Enable LBPW only for DDR3 */
+       tmp = RREG32(MC_SEQ_MISC0);
+       if ((tmp & 0xF0000000) == 0xB0000000)
+               return true;
+       return false;
+}
+
+static void si_enable_lbpw(struct radeon_device *rdev, bool enable)
+{
+       u32 tmp;
+
+       tmp = RREG32(RLC_LB_CNTL);
+       if (enable)
+               tmp |= LOAD_BALANCE_ENABLE;
+       else
+               tmp &= ~LOAD_BALANCE_ENABLE;
+       WREG32(RLC_LB_CNTL, tmp);
+
+       if (!enable) {
+               si_select_se_sh(rdev, 0xffffffff, 0xffffffff);
+               WREG32(SPI_LB_CU_MASK, 0x00ff);
+       }
 }
 
 static int si_rlc_resume(struct radeon_device *rdev)
@@ -4390,14 +5250,18 @@ static int si_rlc_resume(struct radeon_device *rdev)
 
        si_rlc_stop(rdev);
 
+       si_rlc_reset(rdev);
+
+       si_init_pg(rdev);
+
+       si_init_cg(rdev);
+
        WREG32(RLC_RL_BASE, 0);
        WREG32(RLC_RL_SIZE, 0);
        WREG32(RLC_LB_CNTL, 0);
        WREG32(RLC_LB_CNTR_MAX, 0xffffffff);
        WREG32(RLC_LB_CNTR_INIT, 0);
-
-       WREG32(RLC_SAVE_AND_RESTORE_BASE, rdev->rlc.save_restore_gpu_addr >> 8);
-       WREG32(RLC_CLEAR_STATE_RESTORE_BASE, rdev->rlc.clear_state_gpu_addr >> 8);
+       WREG32(RLC_LB_INIT_CU_MASK, 0xffffffff);
 
        WREG32(RLC_MC_CNTL, 0);
        WREG32(RLC_UCODE_CNTL, 0);
@@ -4409,6 +5273,8 @@ static int si_rlc_resume(struct radeon_device *rdev)
        }
        WREG32(RLC_UCODE_ADDR, 0);
 
+       si_enable_lbpw(rdev, si_lbpw_supported(rdev));
+
        si_rlc_start(rdev);
 
        return 0;
@@ -4578,6 +5444,7 @@ int si_irq_set(struct radeon_device *rdev)
        u32 grbm_int_cntl = 0;
        u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0;
        u32 dma_cntl, dma_cntl1;
+       u32 thermal_int = 0;
 
        if (!rdev->irq.installed) {
                WARN(1, "Can't enable IRQ/MSI because no handler is installed\n");
@@ -4603,6 +5470,9 @@ int si_irq_set(struct radeon_device *rdev)
        dma_cntl = RREG32(DMA_CNTL + DMA0_REGISTER_OFFSET) & ~TRAP_ENABLE;
        dma_cntl1 = RREG32(DMA_CNTL + DMA1_REGISTER_OFFSET) & ~TRAP_ENABLE;
 
+       thermal_int = RREG32(CG_THERMAL_INT) &
+               ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
+
        /* enable CP interrupts on all rings */
        if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) {
                DRM_DEBUG("si_irq_set: sw int gfx\n");
@@ -4689,6 +5559,11 @@ int si_irq_set(struct radeon_device *rdev)
 
        WREG32(GRBM_INT_CNTL, grbm_int_cntl);
 
+       if (rdev->irq.dpm_thermal) {
+               DRM_DEBUG("dpm thermal\n");
+               thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW;
+       }
+
        if (rdev->num_crtc >= 2) {
                WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, crtc1);
                WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, crtc2);
@@ -4724,6 +5599,8 @@ int si_irq_set(struct radeon_device *rdev)
                WREG32(DC_HPD6_INT_CONTROL, hpd6);
        }
 
+       WREG32(CG_THERMAL_INT, thermal_int);
+
        return 0;
 }
 
@@ -4888,6 +5765,7 @@ int si_irq_process(struct radeon_device *rdev)
        u32 src_id, src_data, ring_id;
        u32 ring_index;
        bool queue_hotplug = false;
+       bool queue_thermal = false;
 
        if (!rdev->ih.enabled || rdev->shutdown)
                return IRQ_NONE;
@@ -5158,6 +6036,16 @@ restart_ih:
                        DRM_DEBUG("IH: DMA trap\n");
                        radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX);
                        break;
+               case 230: /* thermal low to high */
+                       DRM_DEBUG("IH: thermal low to high\n");
+                       rdev->pm.dpm.thermal.high_to_low = false;
+                       queue_thermal = true;
+                       break;
+               case 231: /* thermal high to low */
+                       DRM_DEBUG("IH: thermal high to low\n");
+                       rdev->pm.dpm.thermal.high_to_low = true;
+                       queue_thermal = true;
+                       break;
                case 233: /* GUI IDLE */
                        DRM_DEBUG("IH: GUI idle\n");
                        break;
@@ -5176,6 +6064,8 @@ restart_ih:
        }
        if (queue_hotplug)
                schedule_work(&rdev->hotplug_work);
+       if (queue_thermal && rdev->pm.dpm_enabled)
+               schedule_work(&rdev->pm.dpm.thermal.work);
        rdev->ih.rptr = rptr;
        WREG32(IH_RB_RPTR, rdev->ih.rptr);
        atomic_set(&rdev->ih.lock, 0);
@@ -5270,6 +6160,11 @@ static int si_startup(struct radeon_device *rdev)
        struct radeon_ring *ring;
        int r;
 
+       /* enable pcie gen2/3 link */
+       si_pcie_gen3_enable(rdev);
+       /* enable aspm */
+       si_program_aspm(rdev);
+
        if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw ||
            !rdev->rlc_fw || !rdev->mc_fw) {
                r = si_init_microcode(rdev);
@@ -5609,6 +6504,8 @@ void si_fini(struct radeon_device *rdev)
        cayman_dma_fini(rdev);
        si_irq_fini(rdev);
        si_rlc_fini(rdev);
+       si_fini_cg(rdev);
+       si_fini_pg(rdev);
        radeon_wb_fini(rdev);
        radeon_vm_manager_fini(rdev);
        radeon_ib_pool_fini(rdev);
@@ -5735,3 +6632,361 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
 
        return 0;
 }
+
+static void si_pcie_gen3_enable(struct radeon_device *rdev)
+{
+       struct pci_dev *root = rdev->pdev->bus->self;
+       int bridge_pos, gpu_pos;
+       u32 speed_cntl, mask, current_data_rate;
+       int ret, i;
+       u16 tmp16;
+
+       if (radeon_pcie_gen2 == 0)
+               return;
+
+       if (rdev->flags & RADEON_IS_IGP)
+               return;
+
+       if (!(rdev->flags & RADEON_IS_PCIE))
+               return;
+
+       ret = drm_pcie_get_speed_cap_mask(rdev->ddev, &mask);
+       if (ret != 0)
+               return;
+
+       if (!(mask & (DRM_PCIE_SPEED_50 | DRM_PCIE_SPEED_80)))
+               return;
+
+       speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+       current_data_rate = (speed_cntl & LC_CURRENT_DATA_RATE_MASK) >>
+               LC_CURRENT_DATA_RATE_SHIFT;
+       if (mask & DRM_PCIE_SPEED_80) {
+               if (current_data_rate == 2) {
+                       DRM_INFO("PCIE gen 3 link speeds already enabled\n");
+                       return;
+               }
+               DRM_INFO("enabling PCIE gen 3 link speeds, disable with radeon.pcie_gen2=0\n");
+       } else if (mask & DRM_PCIE_SPEED_50) {
+               if (current_data_rate == 1) {
+                       DRM_INFO("PCIE gen 2 link speeds already enabled\n");
+                       return;
+               }
+               DRM_INFO("enabling PCIE gen 2 link speeds, disable with radeon.pcie_gen2=0\n");
+       }
+
+       bridge_pos = pci_pcie_cap(root);
+       if (!bridge_pos)
+               return;
+
+       gpu_pos = pci_pcie_cap(rdev->pdev);
+       if (!gpu_pos)
+               return;
+
+       if (mask & DRM_PCIE_SPEED_80) {
+               /* re-try equalization if gen3 is not already enabled */
+               if (current_data_rate != 2) {
+                       u16 bridge_cfg, gpu_cfg;
+                       u16 bridge_cfg2, gpu_cfg2;
+                       u32 max_lw, current_lw, tmp;
+
+                       pci_read_config_word(root, bridge_pos + PCI_EXP_LNKCTL, &bridge_cfg);
+                       pci_read_config_word(rdev->pdev, gpu_pos + PCI_EXP_LNKCTL, &gpu_cfg);
+
+                       tmp16 = bridge_cfg | PCI_EXP_LNKCTL_HAWD;
+                       pci_write_config_word(root, bridge_pos + PCI_EXP_LNKCTL, tmp16);
+
+                       tmp16 = gpu_cfg | PCI_EXP_LNKCTL_HAWD;
+                       pci_write_config_word(rdev->pdev, gpu_pos + PCI_EXP_LNKCTL, tmp16);
+
+                       tmp = RREG32_PCIE(PCIE_LC_STATUS1);
+                       max_lw = (tmp & LC_DETECTED_LINK_WIDTH_MASK) >> LC_DETECTED_LINK_WIDTH_SHIFT;
+                       current_lw = (tmp & LC_OPERATING_LINK_WIDTH_MASK) >> LC_OPERATING_LINK_WIDTH_SHIFT;
+
+                       if (current_lw < max_lw) {
+                               tmp = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL);
+                               if (tmp & LC_RENEGOTIATION_SUPPORT) {
+                                       tmp &= ~(LC_LINK_WIDTH_MASK | LC_UPCONFIGURE_DIS);
+                                       tmp |= (max_lw << LC_LINK_WIDTH_SHIFT);
+                                       tmp |= LC_UPCONFIGURE_SUPPORT | LC_RENEGOTIATE_EN | LC_RECONFIG_NOW;
+                                       WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, tmp);
+                               }
+                       }
+
+                       for (i = 0; i < 10; i++) {
+                               /* check status */
+                               pci_read_config_word(rdev->pdev, gpu_pos + PCI_EXP_DEVSTA, &tmp16);
+                               if (tmp16 & PCI_EXP_DEVSTA_TRPND)
+                                       break;
+
+                               pci_read_config_word(root, bridge_pos + PCI_EXP_LNKCTL, &bridge_cfg);
+                               pci_read_config_word(rdev->pdev, gpu_pos + PCI_EXP_LNKCTL, &gpu_cfg);
+
+                               pci_read_config_word(root, bridge_pos + PCI_EXP_LNKCTL2, &bridge_cfg2);
+                               pci_read_config_word(rdev->pdev, gpu_pos + PCI_EXP_LNKCTL2, &gpu_cfg2);
+
+                               tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL4);
+                               tmp |= LC_SET_QUIESCE;
+                               WREG32_PCIE_PORT(PCIE_LC_CNTL4, tmp);
+
+                               tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL4);
+                               tmp |= LC_REDO_EQ;
+                               WREG32_PCIE_PORT(PCIE_LC_CNTL4, tmp);
+
+                               mdelay(100);
+
+                               /* linkctl */
+                               pci_read_config_word(root, bridge_pos + PCI_EXP_LNKCTL, &tmp16);
+                               tmp16 &= ~PCI_EXP_LNKCTL_HAWD;
+                               tmp16 |= (bridge_cfg & PCI_EXP_LNKCTL_HAWD);
+                               pci_write_config_word(root, bridge_pos + PCI_EXP_LNKCTL, tmp16);
+
+                               pci_read_config_word(rdev->pdev, gpu_pos + PCI_EXP_LNKCTL, &tmp16);
+                               tmp16 &= ~PCI_EXP_LNKCTL_HAWD;
+                               tmp16 |= (gpu_cfg & PCI_EXP_LNKCTL_HAWD);
+                               pci_write_config_word(rdev->pdev, gpu_pos + PCI_EXP_LNKCTL, tmp16);
+
+                               /* linkctl2 */
+                               pci_read_config_word(root, bridge_pos + PCI_EXP_LNKCTL2, &tmp16);
+                               tmp16 &= ~((1 << 4) | (7 << 9));
+                               tmp16 |= (bridge_cfg2 & ((1 << 4) | (7 << 9)));
+                               pci_write_config_word(root, bridge_pos + PCI_EXP_LNKCTL2, tmp16);
+
+                               pci_read_config_word(rdev->pdev, gpu_pos + PCI_EXP_LNKCTL2, &tmp16);
+                               tmp16 &= ~((1 << 4) | (7 << 9));
+                               tmp16 |= (gpu_cfg2 & ((1 << 4) | (7 << 9)));
+                               pci_write_config_word(rdev->pdev, gpu_pos + PCI_EXP_LNKCTL2, tmp16);
+
+                               tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL4);
+                               tmp &= ~LC_SET_QUIESCE;
+                               WREG32_PCIE_PORT(PCIE_LC_CNTL4, tmp);
+                       }
+               }
+       }
+
+       /* set the link speed */
+       speed_cntl |= LC_FORCE_EN_SW_SPEED_CHANGE | LC_FORCE_DIS_HW_SPEED_CHANGE;
+       speed_cntl &= ~LC_FORCE_DIS_SW_SPEED_CHANGE;
+       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl);
+
+       pci_read_config_word(rdev->pdev, gpu_pos + PCI_EXP_LNKCTL2, &tmp16);
+       tmp16 &= ~0xf;
+       if (mask & DRM_PCIE_SPEED_80)
+               tmp16 |= 3; /* gen3 */
+       else if (mask & DRM_PCIE_SPEED_50)
+               tmp16 |= 2; /* gen2 */
+       else
+               tmp16 |= 1; /* gen1 */
+       pci_write_config_word(rdev->pdev, gpu_pos + PCI_EXP_LNKCTL2, tmp16);
+
+       speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+       speed_cntl |= LC_INITIATE_LINK_SPEED_CHANGE;
+       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+               if ((speed_cntl & LC_INITIATE_LINK_SPEED_CHANGE) == 0)
+                       break;
+               udelay(1);
+       }
+}
+
+static void si_program_aspm(struct radeon_device *rdev)
+{
+       u32 data, orig;
+       bool disable_l0s = false, disable_l1 = false, disable_plloff_in_l1 = false;
+       bool disable_clkreq = false;
+
+       if (!(rdev->flags & RADEON_IS_PCIE))
+               return;
+
+       orig = data = RREG32_PCIE_PORT(PCIE_LC_N_FTS_CNTL);
+       data &= ~LC_XMIT_N_FTS_MASK;
+       data |= LC_XMIT_N_FTS(0x24) | LC_XMIT_N_FTS_OVERRIDE_EN;
+       if (orig != data)
+               WREG32_PCIE_PORT(PCIE_LC_N_FTS_CNTL, data);
+
+       orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL3);
+       data |= LC_GO_TO_RECOVERY;
+       if (orig != data)
+               WREG32_PCIE_PORT(PCIE_LC_CNTL3, data);
+
+       orig = data = RREG32_PCIE(PCIE_P_CNTL);
+       data |= P_IGNORE_EDB_ERR;
+       if (orig != data)
+               WREG32_PCIE(PCIE_P_CNTL, data);
+
+       orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL);
+       data &= ~(LC_L0S_INACTIVITY_MASK | LC_L1_INACTIVITY_MASK);
+       data |= LC_PMI_TO_L1_DIS;
+       if (!disable_l0s)
+               data |= LC_L0S_INACTIVITY(7);
+
+       if (!disable_l1) {
+               data |= LC_L1_INACTIVITY(7);
+               data &= ~LC_PMI_TO_L1_DIS;
+               if (orig != data)
+                       WREG32_PCIE_PORT(PCIE_LC_CNTL, data);
+
+               if (!disable_plloff_in_l1) {
+                       bool clk_req_support;
+
+                       orig = data = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_0_MASK | PLL_POWER_STATE_IN_TXS2_0_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_0(7) | PLL_POWER_STATE_IN_TXS2_0(7);
+                       if (orig != data)
+                               WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0, data);
+
+                       orig = data = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_1_MASK | PLL_POWER_STATE_IN_TXS2_1_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_1(7) | PLL_POWER_STATE_IN_TXS2_1(7);
+                       if (orig != data)
+                               WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1, data);
+
+                       orig = data = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_0_MASK | PLL_POWER_STATE_IN_TXS2_0_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_0(7) | PLL_POWER_STATE_IN_TXS2_0(7);
+                       if (orig != data)
+                               WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0, data);
+
+                       orig = data = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_1_MASK | PLL_POWER_STATE_IN_TXS2_1_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_1(7) | PLL_POWER_STATE_IN_TXS2_1(7);
+                       if (orig != data)
+                               WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1, data);
+
+                       if ((rdev->family != CHIP_OLAND) && (rdev->family != CHIP_HAINAN)) {
+                               orig = data = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0);
+                               data &= ~PLL_RAMP_UP_TIME_0_MASK;
+                               if (orig != data)
+                                       WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0, data);
+
+                               orig = data = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1);
+                               data &= ~PLL_RAMP_UP_TIME_1_MASK;
+                               if (orig != data)
+                                       WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1, data);
+
+                               orig = data = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_2);
+                               data &= ~PLL_RAMP_UP_TIME_2_MASK;
+                               if (orig != data)
+                                       WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_2, data);
+
+                               orig = data = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_3);
+                               data &= ~PLL_RAMP_UP_TIME_3_MASK;
+                               if (orig != data)
+                                       WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_3, data);
+
+                               orig = data = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0);
+                               data &= ~PLL_RAMP_UP_TIME_0_MASK;
+                               if (orig != data)
+                                       WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0, data);
+
+                               orig = data = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1);
+                               data &= ~PLL_RAMP_UP_TIME_1_MASK;
+                               if (orig != data)
+                                       WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1, data);
+
+                               orig = data = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_2);
+                               data &= ~PLL_RAMP_UP_TIME_2_MASK;
+                               if (orig != data)
+                                       WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_2, data);
+
+                               orig = data = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_3);
+                               data &= ~PLL_RAMP_UP_TIME_3_MASK;
+                               if (orig != data)
+                                       WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_3, data);
+                       }
+                       orig = data = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL);
+                       data &= ~LC_DYN_LANES_PWR_STATE_MASK;
+                       data |= LC_DYN_LANES_PWR_STATE(3);
+                       if (orig != data)
+                               WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, data);
+
+                       orig = data = RREG32_PIF_PHY0(PB0_PIF_CNTL);
+                       data &= ~LS2_EXIT_TIME_MASK;
+                       if ((rdev->family == CHIP_OLAND) || (rdev->family == CHIP_HAINAN))
+                               data |= LS2_EXIT_TIME(5);
+                       if (orig != data)
+                               WREG32_PIF_PHY0(PB0_PIF_CNTL, data);
+
+                       orig = data = RREG32_PIF_PHY1(PB1_PIF_CNTL);
+                       data &= ~LS2_EXIT_TIME_MASK;
+                       if ((rdev->family == CHIP_OLAND) || (rdev->family == CHIP_HAINAN))
+                               data |= LS2_EXIT_TIME(5);
+                       if (orig != data)
+                               WREG32_PIF_PHY1(PB1_PIF_CNTL, data);
+
+                       if (!disable_clkreq) {
+                               struct pci_dev *root = rdev->pdev->bus->self;
+                               u32 lnkcap;
+
+                               clk_req_support = false;
+                               pcie_capability_read_dword(root, PCI_EXP_LNKCAP, &lnkcap);
+                               if (lnkcap & PCI_EXP_LNKCAP_CLKPM)
+                                       clk_req_support = true;
+                       } else {
+                               clk_req_support = false;
+                       }
+
+                       if (clk_req_support) {
+                               orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL2);
+                               data |= LC_ALLOW_PDWN_IN_L1 | LC_ALLOW_PDWN_IN_L23;
+                               if (orig != data)
+                                       WREG32_PCIE_PORT(PCIE_LC_CNTL2, data);
+
+                               orig = data = RREG32(THM_CLK_CNTL);
+                               data &= ~(CMON_CLK_SEL_MASK | TMON_CLK_SEL_MASK);
+                               data |= CMON_CLK_SEL(1) | TMON_CLK_SEL(1);
+                               if (orig != data)
+                                       WREG32(THM_CLK_CNTL, data);
+
+                               orig = data = RREG32(MISC_CLK_CNTL);
+                               data &= ~(DEEP_SLEEP_CLK_SEL_MASK | ZCLK_SEL_MASK);
+                               data |= DEEP_SLEEP_CLK_SEL(1) | ZCLK_SEL(1);
+                               if (orig != data)
+                                       WREG32(MISC_CLK_CNTL, data);
+
+                               orig = data = RREG32(CG_CLKPIN_CNTL);
+                               data &= ~BCLK_AS_XCLK;
+                               if (orig != data)
+                                       WREG32(CG_CLKPIN_CNTL, data);
+
+                               orig = data = RREG32(CG_CLKPIN_CNTL_2);
+                               data &= ~FORCE_BIF_REFCLK_EN;
+                               if (orig != data)
+                                       WREG32(CG_CLKPIN_CNTL_2, data);
+
+                               orig = data = RREG32(MPLL_BYPASSCLK_SEL);
+                               data &= ~MPLL_CLKOUT_SEL_MASK;
+                               data |= MPLL_CLKOUT_SEL(4);
+                               if (orig != data)
+                                       WREG32(MPLL_BYPASSCLK_SEL, data);
+
+                               orig = data = RREG32(SPLL_CNTL_MODE);
+                               data &= ~SPLL_REFCLK_SEL_MASK;
+                               if (orig != data)
+                                       WREG32(SPLL_CNTL_MODE, data);
+                       }
+               }
+       } else {
+               if (orig != data)
+                       WREG32_PCIE_PORT(PCIE_LC_CNTL, data);
+       }
+
+       orig = data = RREG32_PCIE(PCIE_CNTL2);
+       data |= SLV_MEM_LS_EN | MST_MEM_LS_EN | REPLAY_MEM_LS_EN;
+       if (orig != data)
+               WREG32_PCIE(PCIE_CNTL2, data);
+
+       if (!disable_l0s) {
+               data = RREG32_PCIE_PORT(PCIE_LC_N_FTS_CNTL);
+               if((data & LC_N_FTS_MASK) == LC_N_FTS_MASK) {
+                       data = RREG32_PCIE(PCIE_LC_STATUS1);
+                       if ((data & LC_REVERSE_XMIT) && (data & LC_REVERSE_RCVR)) {
+                               orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL);
+                               data &= ~LC_L0S_INACTIVITY_MASK;
+                               if (orig != data)
+                                       WREG32_PCIE_PORT(PCIE_LC_CNTL, data);
+                       }
+               }
+       }
+}
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
new file mode 100644 (file)
index 0000000..73aaa2e
--- /dev/null
@@ -0,0 +1,6432 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, 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.
+ *
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "sid.h"
+#include "r600_dpm.h"
+#include "si_dpm.h"
+#include "atom.h"
+#include <linux/math64.h>
+#include <linux/seq_file.h>
+
+#define MC_CG_ARB_FREQ_F0           0x0a
+#define MC_CG_ARB_FREQ_F1           0x0b
+#define MC_CG_ARB_FREQ_F2           0x0c
+#define MC_CG_ARB_FREQ_F3           0x0d
+
+#define SMC_RAM_END                 0x20000
+
+#define DDR3_DRAM_ROWS              0x2000
+
+#define SCLK_MIN_DEEPSLEEP_FREQ     1350
+
+static const struct si_cac_config_reg cac_weights_tahiti[] =
+{
+       { 0x0, 0x0000ffff, 0, 0xc, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x101, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0xc, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x8fc, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x95, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x34e, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x1a1, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0xda, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x46, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x208, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0xe7, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x948, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x167, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x31, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x18e, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_tahiti[] =
+{
+       { 0x143, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+
+};
+
+static const struct si_cac_config_reg cac_override_tahiti[] =
+{
+       { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_tahiti =
+{
+       ((1 << 16) | 27027),
+       6,
+       0,
+       4,
+       95,
+       {
+               0UL,
+               0UL,
+               4521550UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               40
+       },
+       595000000UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_tahiti =
+{
+       { 1159409, 0, 0, 0, 0 },
+       { 777, 0, 0, 0, 0 },
+       2,
+       54000,
+       127000,
+       25,
+       2,
+       10,
+       13,
+       { 27, 31, 35, 39, 43, 47, 54, 61, 67, 74, 81, 88, 95, 0, 0, 0 },
+       { 240888759, 221057860, 235370597, 162287531, 158510299, 131423027, 116673180, 103067515, 87941937, 76209048, 68209175, 64090048, 58301890, 0, 0, 0 },
+       { 12024, 11189, 11451, 8411, 7939, 6666, 5681, 4905, 4241, 3720, 3354, 3122, 2890, 0, 0, 0 },
+       85,
+       false
+};
+
+static const struct si_dte_data dte_data_tahiti_le =
+{
+       { 0x1E8480, 0x7A1200, 0x2160EC0, 0x3938700, 0 },
+       { 0x7D, 0x7D, 0x4E4, 0xB00, 0 },
+       0x5,
+       0xAFC8,
+       0x64,
+       0x32,
+       1,
+       0,
+       0x10,
+       { 0x78, 0x7C, 0x82, 0x88, 0x8E, 0x94, 0x9A, 0xA0, 0xA6, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4 },
+       { 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700 },
+       { 0x2AF8, 0x2AF8, 0x29BB, 0x27F9, 0x2637, 0x2475, 0x22B3, 0x20F1, 0x1F2F, 0x1D6D, 0x1734, 0x1414, 0x10F4, 0xDD4, 0xAB4, 0x794 },
+       85,
+       true
+};
+
+static const struct si_dte_data dte_data_tahiti_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x7D0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_new_zealand =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0 },
+       { 0x29B, 0x3E9, 0x537, 0x7D2, 0 },
+       0x5,
+       0xAFC8,
+       0x69,
+       0x32,
+       1,
+       0,
+       0x10,
+       { 0x82, 0xA0, 0xB4, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0xDAC, 0x1388, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685 },
+       85,
+       true
+};
+
+static const struct si_dte_data dte_data_aruba_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_malta =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+struct si_cac_config_reg cac_weights_pitcairn[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x8a, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x24d, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x19, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0xc11, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x7f3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x403, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x367, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x4c9, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x45d, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x36d, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x534, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x5da, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x880, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0x201, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x9f, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x1f, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5de, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x7b, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x13, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0xf9, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x66, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x13, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x186, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_pitcairn[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_pitcairn[] =
+{
+    { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_pitcairn =
+{
+       ((1 << 16) | 27027),
+       5,
+       0,
+       6,
+       100,
+       {
+               51600000UL,
+               1800000UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_pitcairn =
+{
+       { 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,
+       false
+};
+
+static const struct si_dte_data dte_data_curacao_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x1D17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_curacao_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x1D17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_neptune_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x3A2F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_cac_config_reg cac_weights_chelsea_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x2BD, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_chelsea_xt[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x30A, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_heathrow[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x362, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_cape_verde_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x315, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_cape_verde[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x3BA, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_cape_verde[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_cape_verde[] =
+{
+    { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_cape_verde =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       7,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_cape_verde =
+{
+       { 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,
+       false
+};
+
+static const struct si_dte_data dte_data_venus_xtx =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x71C, 0xAAB, 0xE39, 0x11C7, 0x0 },
+       5,
+       55000,
+       0x69,
+       0xA,
+       1,
+       0,
+       0x3,
+       { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0xD6D8, 0x88B8, 0x1555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_venus_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0xBDA, 0x11C7, 0x17B4, 0x1DA1, 0x0 },
+       5,
+       55000,
+       0x69,
+       0xA,
+       1,
+       0,
+       0x3,
+       { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0xAFC8, 0x88B8, 0x238E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_venus_pro =
+{
+       {  0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x11C7, 0x1AAB, 0x238E, 0x2C72, 0x0 },
+       5,
+       55000,
+       0x69,
+       0xA,
+       1,
+       0,
+       0x3,
+       { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x88B8, 0x88B8, 0x3555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+struct si_cac_config_reg cac_weights_oland[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x3BA, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_mars_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_mars_xt[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x60, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_oland_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x90, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_oland_xt[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x120, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_oland[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_mars_pro[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_oland[] =
+{
+       { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_oland =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       7,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_powertune_data powertune_data_mars_pro =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       7,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_oland =
+{
+       { 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,
+       false
+};
+
+static const struct si_dte_data dte_data_mars_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       55000,
+       105,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0xF627, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_sun_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       55000,
+       105,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0xD555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+
+static const struct si_cac_config_reg cac_weights_hainan[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x2d9, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x22b, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x21c, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x1dc, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x24e, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x35e, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1143, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0xe17, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x441, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x28b, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0xabe, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xf11, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x907, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0xb45, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0xd1e, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xa2c, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x62, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x1f3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x42, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x709, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x3a, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x357, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x9f, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x314, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x6d, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x1b9, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_hainan =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       9,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev);
+struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev);
+struct ni_power_info *ni_get_pi(struct radeon_device *rdev);
+struct ni_ps *ni_get_ps(struct radeon_ps *rps);
+
+static int si_populate_voltage_value(struct radeon_device *rdev,
+                                    const struct atom_voltage_table *table,
+                                    u16 value, SISLANDS_SMC_VOLTAGE_VALUE *voltage);
+static int si_get_std_voltage_value(struct radeon_device *rdev,
+                                   SISLANDS_SMC_VOLTAGE_VALUE *voltage,
+                                   u16 *std_voltage);
+static int si_write_smc_soft_register(struct radeon_device *rdev,
+                                     u16 reg_offset, u32 value);
+static int si_convert_power_level_to_smc(struct radeon_device *rdev,
+                                        struct rv7xx_pl *pl,
+                                        SISLANDS_SMC_HW_PERFORMANCE_LEVEL *level);
+static int si_calculate_sclk_params(struct radeon_device *rdev,
+                                   u32 engine_clock,
+                                   SISLANDS_SMC_SCLK_VALUE *sclk);
+
+static struct si_power_info *si_get_pi(struct radeon_device *rdev)
+{
+        struct si_power_info *pi = rdev->pm.dpm.priv;
+
+        return pi;
+}
+
+static void si_calculate_leakage_for_v_and_t_formula(const struct ni_leakage_coeffients *coeff,
+                                                    u16 v, s32 t, u32 ileakage, u32 *leakage)
+{
+       s64 kt, kv, leakage_w, i_leakage, vddc;
+       s64 temperature, t_slope, t_intercept, av, bv, t_ref;
+
+       i_leakage = drm_int2fixp(ileakage / 100);
+       vddc = div64_s64(drm_int2fixp(v), 1000);
+       temperature = div64_s64(drm_int2fixp(t), 1000);
+
+       t_slope = div64_s64(drm_int2fixp(coeff->t_slope), 100000000);
+       t_intercept = div64_s64(drm_int2fixp(coeff->t_intercept), 100000000);
+       av = div64_s64(drm_int2fixp(coeff->av), 100000000);
+       bv = div64_s64(drm_int2fixp(coeff->bv), 100000000);
+       t_ref = drm_int2fixp(coeff->t_ref);
+
+       kt = drm_fixp_div(drm_fixp_exp(drm_fixp_mul(drm_fixp_mul(t_slope, vddc) + t_intercept, temperature)),
+                         drm_fixp_exp(drm_fixp_mul(drm_fixp_mul(t_slope, vddc) + t_intercept, t_ref)));
+       kv = drm_fixp_mul(av, drm_fixp_exp(drm_fixp_mul(bv, vddc)));
+
+       leakage_w = drm_fixp_mul(drm_fixp_mul(drm_fixp_mul(i_leakage, kt), kv), vddc);
+
+       *leakage = drm_fixp2int(leakage_w * 1000);
+}
+
+static void si_calculate_leakage_for_v_and_t(struct radeon_device *rdev,
+                                            const struct ni_leakage_coeffients *coeff,
+                                            u16 v,
+                                            s32 t,
+                                            u32 i_leakage,
+                                            u32 *leakage)
+{
+       si_calculate_leakage_for_v_and_t_formula(coeff, v, t, i_leakage, leakage);
+}
+
+static void si_calculate_leakage_for_v_formula(const struct ni_leakage_coeffients *coeff,
+                                              const u32 fixed_kt, u16 v,
+                                              u32 ileakage, u32 *leakage)
+{
+       s64 kt, kv, leakage_w, i_leakage, vddc;
+
+       i_leakage = div64_s64(drm_int2fixp(ileakage), 100);
+       vddc = div64_s64(drm_int2fixp(v), 1000);
+
+       kt = div64_s64(drm_int2fixp(fixed_kt), 100000000);
+       kv = drm_fixp_mul(div64_s64(drm_int2fixp(coeff->av), 100000000),
+                         drm_fixp_exp(drm_fixp_mul(div64_s64(drm_int2fixp(coeff->bv), 100000000), vddc)));
+
+       leakage_w = drm_fixp_mul(drm_fixp_mul(drm_fixp_mul(i_leakage, kt), kv), vddc);
+
+       *leakage = drm_fixp2int(leakage_w * 1000);
+}
+
+static void si_calculate_leakage_for_v(struct radeon_device *rdev,
+                                      const struct ni_leakage_coeffients *coeff,
+                                      const u32 fixed_kt,
+                                      u16 v,
+                                      u32 i_leakage,
+                                      u32 *leakage)
+{
+       si_calculate_leakage_for_v_formula(coeff, fixed_kt, v, i_leakage, leakage);
+}
+
+
+static void si_update_dte_from_pl2(struct radeon_device *rdev,
+                                  struct si_dte_data *dte_data)
+{
+       u32 p_limit1 = rdev->pm.dpm.tdp_limit;
+       u32 p_limit2 = rdev->pm.dpm.near_tdp_limit;
+       u32 k = dte_data->k;
+       u32 t_max = dte_data->max_t;
+       u32 t_split[5] = { 10, 15, 20, 25, 30 };
+       u32 t_0 = dte_data->t0;
+       u32 i;
+
+       if (p_limit2 != 0 && p_limit2 <= p_limit1) {
+               dte_data->tdep_count = 3;
+
+               for (i = 0; i < k; i++) {
+                       dte_data->r[i] =
+                               (t_split[i] * (t_max - t_0/(u32)1000) * (1 << 14)) /
+                               (p_limit2  * (u32)100);
+               }
+
+               dte_data->tdep_r[1] = dte_data->r[4] * 2;
+
+               for (i = 2; i < SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE; i++) {
+                       dte_data->tdep_r[i] = dte_data->r[4];
+               }
+       } else {
+               DRM_ERROR("Invalid PL2! DTE will not be updated.\n");
+       }
+}
+
+static void si_initialize_powertune_defaults(struct radeon_device *rdev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       bool update_dte_from_pl2 = false;
+
+       if (rdev->family == CHIP_TAHITI) {
+               si_pi->cac_weights = cac_weights_tahiti;
+               si_pi->lcac_config = lcac_tahiti;
+               si_pi->cac_override = cac_override_tahiti;
+               si_pi->powertune_data = &powertune_data_tahiti;
+               si_pi->dte_data = dte_data_tahiti;
+
+               switch (rdev->pdev->device) {
+               case 0x6798:
+                       si_pi->dte_data.enable_dte_by_default = true;
+                       break;
+               case 0x6799:
+                       si_pi->dte_data = dte_data_new_zealand;
+                       break;
+               case 0x6790:
+               case 0x6791:
+               case 0x6792:
+               case 0x679E:
+                       si_pi->dte_data = dte_data_aruba_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x679B:
+                       si_pi->dte_data = dte_data_malta;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x679A:
+                       si_pi->dte_data = dte_data_tahiti_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               default:
+                       if (si_pi->dte_data.enable_dte_by_default == true)
+                               DRM_ERROR("DTE is not enabled!\n");
+                       break;
+               }
+       } else if (rdev->family == CHIP_PITCAIRN) {
+               switch (rdev->pdev->device) {
+               case 0x6810:
+               case 0x6818:
+                       si_pi->cac_weights = cac_weights_pitcairn;
+                       si_pi->lcac_config = lcac_pitcairn;
+                       si_pi->cac_override = cac_override_pitcairn;
+                       si_pi->powertune_data = &powertune_data_pitcairn;
+                       si_pi->dte_data = dte_data_curacao_xt;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6819:
+               case 0x6811:
+                       si_pi->cac_weights = cac_weights_pitcairn;
+                       si_pi->lcac_config = lcac_pitcairn;
+                       si_pi->cac_override = cac_override_pitcairn;
+                       si_pi->powertune_data = &powertune_data_pitcairn;
+                       si_pi->dte_data = dte_data_curacao_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6800:
+               case 0x6806:
+                       si_pi->cac_weights = cac_weights_pitcairn;
+                       si_pi->lcac_config = lcac_pitcairn;
+                       si_pi->cac_override = cac_override_pitcairn;
+                       si_pi->powertune_data = &powertune_data_pitcairn;
+                       si_pi->dte_data = dte_data_neptune_xt;
+                       update_dte_from_pl2 = true;
+                       break;
+               default:
+                       si_pi->cac_weights = cac_weights_pitcairn;
+                       si_pi->lcac_config = lcac_pitcairn;
+                       si_pi->cac_override = cac_override_pitcairn;
+                       si_pi->powertune_data = &powertune_data_pitcairn;
+                       si_pi->dte_data = dte_data_pitcairn;
+               }
+       } else if (rdev->family == CHIP_VERDE) {
+               si_pi->lcac_config = lcac_cape_verde;
+               si_pi->cac_override = cac_override_cape_verde;
+               si_pi->powertune_data = &powertune_data_cape_verde;
+
+               switch (rdev->pdev->device) {
+               case 0x683B:
+               case 0x683F:
+               case 0x6829:
+                       si_pi->cac_weights = cac_weights_cape_verde_pro;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x6825:
+               case 0x6827:
+                       si_pi->cac_weights = cac_weights_heathrow;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x6824:
+               case 0x682D:
+                       si_pi->cac_weights = cac_weights_chelsea_xt;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x682F:
+                       si_pi->cac_weights = cac_weights_chelsea_pro;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x6820:
+                       si_pi->cac_weights = cac_weights_heathrow;
+                       si_pi->dte_data = dte_data_venus_xtx;
+                       break;
+               case 0x6821:
+                       si_pi->cac_weights = cac_weights_heathrow;
+                       si_pi->dte_data = dte_data_venus_xt;
+                       break;
+               case 0x6823:
+                       si_pi->cac_weights = cac_weights_chelsea_pro;
+                       si_pi->dte_data = dte_data_venus_pro;
+                       break;
+               case 0x682B:
+                       si_pi->cac_weights = cac_weights_chelsea_pro;
+                       si_pi->dte_data = dte_data_venus_pro;
+                       break;
+               default:
+                       si_pi->cac_weights = cac_weights_cape_verde;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               }
+       } else if (rdev->family == CHIP_OLAND) {
+               switch (rdev->pdev->device) {
+               case 0x6601:
+               case 0x6621:
+               case 0x6603:
+                       si_pi->cac_weights = cac_weights_mars_pro;
+                       si_pi->lcac_config = lcac_mars_pro;
+                       si_pi->cac_override = cac_override_oland;
+                       si_pi->powertune_data = &powertune_data_mars_pro;
+                       si_pi->dte_data = dte_data_mars_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6600:
+               case 0x6606:
+               case 0x6620:
+                       si_pi->cac_weights = cac_weights_mars_xt;
+                       si_pi->lcac_config = lcac_mars_pro;
+                       si_pi->cac_override = cac_override_oland;
+                       si_pi->powertune_data = &powertune_data_mars_pro;
+                       si_pi->dte_data = dte_data_mars_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6611:
+                       si_pi->cac_weights = cac_weights_oland_pro;
+                       si_pi->lcac_config = lcac_mars_pro;
+                       si_pi->cac_override = cac_override_oland;
+                       si_pi->powertune_data = &powertune_data_mars_pro;
+                       si_pi->dte_data = dte_data_mars_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6610:
+                       si_pi->cac_weights = cac_weights_oland_xt;
+                       si_pi->lcac_config = lcac_mars_pro;
+                       si_pi->cac_override = cac_override_oland;
+                       si_pi->powertune_data = &powertune_data_mars_pro;
+                       si_pi->dte_data = dte_data_mars_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               default:
+                       si_pi->cac_weights = cac_weights_oland;
+                       si_pi->lcac_config = lcac_oland;
+                       si_pi->cac_override = cac_override_oland;
+                       si_pi->powertune_data = &powertune_data_oland;
+                       si_pi->dte_data = dte_data_oland;
+                       break;
+               }
+       } else if (rdev->family == CHIP_HAINAN) {
+               si_pi->cac_weights = cac_weights_hainan;
+               si_pi->lcac_config = lcac_oland;
+               si_pi->cac_override = cac_override_oland;
+               si_pi->powertune_data = &powertune_data_hainan;
+               si_pi->dte_data = dte_data_sun_xt;
+               update_dte_from_pl2 = true;
+       } else {
+               DRM_ERROR("Unknown SI asic revision, failed to initialize PowerTune!\n");
+               return;
+       }
+
+       ni_pi->enable_power_containment = false;
+       ni_pi->enable_cac = false;
+       ni_pi->enable_sq_ramping = false;
+       si_pi->enable_dte = false;
+
+       if (si_pi->powertune_data->enable_powertune_by_default) {
+               ni_pi->enable_power_containment= true;
+               ni_pi->enable_cac = true;
+               if (si_pi->dte_data.enable_dte_by_default) {
+                       si_pi->enable_dte = true;
+                       if (update_dte_from_pl2)
+                               si_update_dte_from_pl2(rdev, &si_pi->dte_data);
+
+               }
+               ni_pi->enable_sq_ramping = true;
+       }
+
+       ni_pi->driver_calculate_cac_leakage = true;
+       ni_pi->cac_configuration_required = true;
+
+       if (ni_pi->cac_configuration_required) {
+               ni_pi->support_cac_long_term_average = true;
+               si_pi->dyn_powertune_data.l2_lta_window_size =
+                       si_pi->powertune_data->l2_lta_window_size_default;
+               si_pi->dyn_powertune_data.lts_truncate =
+                       si_pi->powertune_data->lts_truncate_default;
+       } else {
+               ni_pi->support_cac_long_term_average = false;
+               si_pi->dyn_powertune_data.l2_lta_window_size = 0;
+               si_pi->dyn_powertune_data.lts_truncate = 0;
+       }
+
+       si_pi->dyn_powertune_data.disable_uvd_powertune = false;
+}
+
+static u32 si_get_smc_power_scaling_factor(struct radeon_device *rdev)
+{
+       return 1;
+}
+
+static u32 si_calculate_cac_wintime(struct radeon_device *rdev)
+{
+       u32 xclk;
+       u32 wintime;
+       u32 cac_window;
+       u32 cac_window_size;
+
+       xclk = radeon_get_xclk(rdev);
+
+       if (xclk == 0)
+               return 0;
+
+       cac_window = RREG32(CG_CAC_CTRL) & CAC_WINDOW_MASK;
+       cac_window_size = ((cac_window & 0xFFFF0000) >> 16) * (cac_window & 0x0000FFFF);
+
+       wintime = (cac_window_size * 100) / xclk;
+
+       return wintime;
+}
+
+static u32 si_scale_power_for_smc(u32 power_in_watts, u32 scaling_factor)
+{
+       return power_in_watts;
+}
+
+static int si_calculate_adjusted_tdp_limits(struct radeon_device *rdev,
+                                           bool adjust_polarity,
+                                           u32 tdp_adjustment,
+                                           u32 *tdp_limit,
+                                           u32 *near_tdp_limit)
+{
+       u32 adjustment_delta, max_tdp_limit;
+
+       if (tdp_adjustment > (u32)rdev->pm.dpm.tdp_od_limit)
+               return -EINVAL;
+
+       max_tdp_limit = ((100 + 100) * rdev->pm.dpm.tdp_limit) / 100;
+
+       if (adjust_polarity) {
+               *tdp_limit = ((100 + tdp_adjustment) * rdev->pm.dpm.tdp_limit) / 100;
+               *near_tdp_limit = rdev->pm.dpm.near_tdp_limit_adjusted + (*tdp_limit - rdev->pm.dpm.tdp_limit);
+       } else {
+               *tdp_limit = ((100 - tdp_adjustment) * rdev->pm.dpm.tdp_limit) / 100;
+               adjustment_delta  = rdev->pm.dpm.tdp_limit - *tdp_limit;
+               if (adjustment_delta < rdev->pm.dpm.near_tdp_limit_adjusted)
+                       *near_tdp_limit = rdev->pm.dpm.near_tdp_limit_adjusted - adjustment_delta;
+               else
+                       *near_tdp_limit = 0;
+       }
+
+       if ((*tdp_limit <= 0) || (*tdp_limit > max_tdp_limit))
+               return -EINVAL;
+       if ((*near_tdp_limit <= 0) || (*near_tdp_limit > *tdp_limit))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int si_populate_smc_tdp_limits(struct radeon_device *rdev,
+                                     struct radeon_ps *radeon_state)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (ni_pi->enable_power_containment) {
+               SISLANDS_SMC_STATETABLE *smc_table = &si_pi->smc_statetable;
+               PP_SIslands_PAPMParameters *papm_parm;
+               struct radeon_ppm_table *ppm = rdev->pm.dpm.dyn_state.ppm_table;
+               u32 scaling_factor = si_get_smc_power_scaling_factor(rdev);
+               u32 tdp_limit;
+               u32 near_tdp_limit;
+               int ret;
+
+               if (scaling_factor == 0)
+                       return -EINVAL;
+
+               memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
+
+               ret = si_calculate_adjusted_tdp_limits(rdev,
+                                                      false, /* ??? */
+                                                      rdev->pm.dpm.tdp_adjustment,
+                                                      &tdp_limit,
+                                                      &near_tdp_limit);
+               if (ret)
+                       return ret;
+
+               smc_table->dpm2Params.TDPLimit =
+                       cpu_to_be32(si_scale_power_for_smc(tdp_limit, scaling_factor) * 1000);
+               smc_table->dpm2Params.NearTDPLimit =
+                       cpu_to_be32(si_scale_power_for_smc(near_tdp_limit, scaling_factor) * 1000);
+               smc_table->dpm2Params.SafePowerLimit =
+                       cpu_to_be32(si_scale_power_for_smc((near_tdp_limit * SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100, scaling_factor) * 1000);
+
+               ret = si_copy_bytes_to_smc(rdev,
+                                          (si_pi->state_table_start + offsetof(SISLANDS_SMC_STATETABLE, dpm2Params) +
+                                                offsetof(PP_SIslands_DPM2Parameters, TDPLimit)),
+                                          (u8 *)(&(smc_table->dpm2Params.TDPLimit)),
+                                          sizeof(u32) * 3,
+                                          si_pi->sram_end);
+               if (ret)
+                       return ret;
+
+               if (si_pi->enable_ppm) {
+                       papm_parm = &si_pi->papm_parm;
+                       memset(papm_parm, 0, sizeof(PP_SIslands_PAPMParameters));
+                       papm_parm->NearTDPLimitTherm = cpu_to_be32(ppm->dgpu_tdp);
+                       papm_parm->dGPU_T_Limit = cpu_to_be32(ppm->tj_max);
+                       papm_parm->dGPU_T_Warning = cpu_to_be32(95);
+                       papm_parm->dGPU_T_Hysteresis = cpu_to_be32(5);
+                       papm_parm->PlatformPowerLimit = 0xffffffff;
+                       papm_parm->NearTDPLimitPAPM = 0xffffffff;
+
+                       ret = si_copy_bytes_to_smc(rdev, si_pi->papm_cfg_table_start,
+                                                  (u8 *)papm_parm,
+                                                  sizeof(PP_SIslands_PAPMParameters),
+                                                  si_pi->sram_end);
+                       if (ret)
+                               return ret;
+               }
+       }
+       return 0;
+}
+
+static int si_populate_smc_tdp_limits_2(struct radeon_device *rdev,
+                                       struct radeon_ps *radeon_state)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (ni_pi->enable_power_containment) {
+               SISLANDS_SMC_STATETABLE *smc_table = &si_pi->smc_statetable;
+               u32 scaling_factor = si_get_smc_power_scaling_factor(rdev);
+               int ret;
+
+               memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
+
+               smc_table->dpm2Params.NearTDPLimit =
+                       cpu_to_be32(si_scale_power_for_smc(rdev->pm.dpm.near_tdp_limit_adjusted, scaling_factor) * 1000);
+               smc_table->dpm2Params.SafePowerLimit =
+                       cpu_to_be32(si_scale_power_for_smc((rdev->pm.dpm.near_tdp_limit_adjusted * SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100, scaling_factor) * 1000);
+
+               ret = si_copy_bytes_to_smc(rdev,
+                                          (si_pi->state_table_start +
+                                           offsetof(SISLANDS_SMC_STATETABLE, dpm2Params) +
+                                           offsetof(PP_SIslands_DPM2Parameters, NearTDPLimit)),
+                                          (u8 *)(&(smc_table->dpm2Params.NearTDPLimit)),
+                                          sizeof(u32) * 2,
+                                          si_pi->sram_end);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static u16 si_calculate_power_efficiency_ratio(struct radeon_device *rdev,
+                                              const u16 prev_std_vddc,
+                                              const u16 curr_std_vddc)
+{
+       u64 margin = (u64)SISLANDS_DPM2_PWREFFICIENCYRATIO_MARGIN;
+       u64 prev_vddc = (u64)prev_std_vddc;
+       u64 curr_vddc = (u64)curr_std_vddc;
+       u64 pwr_efficiency_ratio, n, d;
+
+       if ((prev_vddc == 0) || (curr_vddc == 0))
+               return 0;
+
+       n = div64_u64((u64)1024 * curr_vddc * curr_vddc * ((u64)1000 + margin), (u64)1000);
+       d = prev_vddc * prev_vddc;
+       pwr_efficiency_ratio = div64_u64(n, d);
+
+       if (pwr_efficiency_ratio > (u64)0xFFFF)
+               return 0;
+
+       return (u16)pwr_efficiency_ratio;
+}
+
+static bool si_should_disable_uvd_powertune(struct radeon_device *rdev,
+                                           struct radeon_ps *radeon_state)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (si_pi->dyn_powertune_data.disable_uvd_powertune &&
+           radeon_state->vclk && radeon_state->dclk)
+               return true;
+
+       return false;
+}
+
+static int si_populate_power_containment_values(struct radeon_device *rdev,
+                                               struct radeon_ps *radeon_state,
+                                               SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       SISLANDS_SMC_VOLTAGE_VALUE vddc;
+       u32 prev_sclk;
+       u32 max_sclk;
+       u32 min_sclk;
+       u16 prev_std_vddc;
+       u16 curr_std_vddc;
+       int i;
+       u16 pwr_efficiency_ratio;
+       u8 max_ps_percent;
+       bool disable_uvd_power_tune;
+       int ret;
+
+       if (ni_pi->enable_power_containment == false)
+               return 0;
+
+       if (state->performance_level_count == 0)
+               return -EINVAL;
+
+       if (smc_state->levelCount != state->performance_level_count)
+               return -EINVAL;
+
+       disable_uvd_power_tune = si_should_disable_uvd_powertune(rdev, radeon_state);
+
+       smc_state->levels[0].dpm2.MaxPS = 0;
+       smc_state->levels[0].dpm2.NearTDPDec = 0;
+       smc_state->levels[0].dpm2.AboveSafeInc = 0;
+       smc_state->levels[0].dpm2.BelowSafeInc = 0;
+       smc_state->levels[0].dpm2.PwrEfficiencyRatio = 0;
+
+       for (i = 1; i < state->performance_level_count; i++) {
+               prev_sclk = state->performance_levels[i-1].sclk;
+               max_sclk  = state->performance_levels[i].sclk;
+               if (i == 1)
+                       max_ps_percent = SISLANDS_DPM2_MAXPS_PERCENT_M;
+               else
+                       max_ps_percent = SISLANDS_DPM2_MAXPS_PERCENT_H;
+
+               if (prev_sclk > max_sclk)
+                       return -EINVAL;
+
+               if ((max_ps_percent == 0) ||
+                   (prev_sclk == max_sclk) ||
+                   disable_uvd_power_tune) {
+                       min_sclk = max_sclk;
+               } else if (i == 1) {
+                       min_sclk = prev_sclk;
+               } else {
+                       min_sclk = (prev_sclk * (u32)max_ps_percent) / 100;
+               }
+
+               if (min_sclk < state->performance_levels[0].sclk)
+                       min_sclk = state->performance_levels[0].sclk;
+
+               if (min_sclk == 0)
+                       return -EINVAL;
+
+               ret = si_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                               state->performance_levels[i-1].vddc, &vddc);
+               if (ret)
+                       return ret;
+
+               ret = si_get_std_voltage_value(rdev, &vddc, &prev_std_vddc);
+               if (ret)
+                       return ret;
+
+               ret = si_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                               state->performance_levels[i].vddc, &vddc);
+               if (ret)
+                       return ret;
+
+               ret = si_get_std_voltage_value(rdev, &vddc, &curr_std_vddc);
+               if (ret)
+                       return ret;
+
+               pwr_efficiency_ratio = si_calculate_power_efficiency_ratio(rdev,
+                                                                          prev_std_vddc, curr_std_vddc);
+
+               smc_state->levels[i].dpm2.MaxPS = (u8)((SISLANDS_DPM2_MAX_PULSE_SKIP * (max_sclk - min_sclk)) / max_sclk);
+               smc_state->levels[i].dpm2.NearTDPDec = SISLANDS_DPM2_NEAR_TDP_DEC;
+               smc_state->levels[i].dpm2.AboveSafeInc = SISLANDS_DPM2_ABOVE_SAFE_INC;
+               smc_state->levels[i].dpm2.BelowSafeInc = SISLANDS_DPM2_BELOW_SAFE_INC;
+               smc_state->levels[i].dpm2.PwrEfficiencyRatio = cpu_to_be16(pwr_efficiency_ratio);
+       }
+
+       return 0;
+}
+
+static int si_populate_sq_ramping_values(struct radeon_device *rdev,
+                                        struct radeon_ps *radeon_state,
+                                        SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       u32 sq_power_throttle, sq_power_throttle2;
+       bool enable_sq_ramping = ni_pi->enable_sq_ramping;
+       int i;
+
+       if (state->performance_level_count == 0)
+               return -EINVAL;
+
+       if (smc_state->levelCount != state->performance_level_count)
+               return -EINVAL;
+
+       if (rdev->pm.dpm.sq_ramping_threshold == 0)
+               return -EINVAL;
+
+       if (SISLANDS_DPM2_SQ_RAMP_MAX_POWER > (MAX_POWER_MASK >> MAX_POWER_SHIFT))
+               enable_sq_ramping = false;
+
+       if (SISLANDS_DPM2_SQ_RAMP_MIN_POWER > (MIN_POWER_MASK >> MIN_POWER_SHIFT))
+               enable_sq_ramping = false;
+
+       if (SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA > (MAX_POWER_DELTA_MASK >> MAX_POWER_DELTA_SHIFT))
+               enable_sq_ramping = false;
+
+       if (SISLANDS_DPM2_SQ_RAMP_STI_SIZE > (STI_SIZE_MASK >> STI_SIZE_SHIFT))
+               enable_sq_ramping = false;
+
+       if (NISLANDS_DPM2_SQ_RAMP_LTI_RATIO <= (LTI_RATIO_MASK >> LTI_RATIO_SHIFT))
+               enable_sq_ramping = false;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               sq_power_throttle = 0;
+               sq_power_throttle2 = 0;
+
+               if ((state->performance_levels[i].sclk >= rdev->pm.dpm.sq_ramping_threshold) &&
+                   enable_sq_ramping) {
+                       sq_power_throttle |= MAX_POWER(SISLANDS_DPM2_SQ_RAMP_MAX_POWER);
+                       sq_power_throttle |= MIN_POWER(SISLANDS_DPM2_SQ_RAMP_MIN_POWER);
+                       sq_power_throttle2 |= MAX_POWER_DELTA(SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA);
+                       sq_power_throttle2 |= STI_SIZE(SISLANDS_DPM2_SQ_RAMP_STI_SIZE);
+                       sq_power_throttle2 |= LTI_RATIO(SISLANDS_DPM2_SQ_RAMP_LTI_RATIO);
+               } else {
+                       sq_power_throttle |= MAX_POWER_MASK | MIN_POWER_MASK;
+                       sq_power_throttle2 |= MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+               }
+
+               smc_state->levels[i].SQPowerThrottle = cpu_to_be32(sq_power_throttle);
+               smc_state->levels[i].SQPowerThrottle_2 = cpu_to_be32(sq_power_throttle2);
+       }
+
+       return 0;
+}
+
+static int si_enable_power_containment(struct radeon_device *rdev,
+                                      struct radeon_ps *radeon_new_state,
+                                      bool enable)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       PPSMC_Result smc_result;
+       int ret = 0;
+
+       if (ni_pi->enable_power_containment) {
+               if (enable) {
+                       if (!si_should_disable_uvd_powertune(rdev, radeon_new_state)) {
+                               smc_result = si_send_msg_to_smc(rdev, PPSMC_TDPClampingActive);
+                               if (smc_result != PPSMC_Result_OK) {
+                                       ret = -EINVAL;
+                                       ni_pi->pc_enabled = false;
+                               } else {
+                                       ni_pi->pc_enabled = true;
+                               }
+                       }
+               } else {
+                       smc_result = si_send_msg_to_smc(rdev, PPSMC_TDPClampingInactive);
+                       if (smc_result != PPSMC_Result_OK)
+                               ret = -EINVAL;
+                       ni_pi->pc_enabled = false;
+               }
+       }
+
+       return ret;
+}
+
+static int si_initialize_smc_dte_tables(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int ret = 0;
+       struct si_dte_data *dte_data = &si_pi->dte_data;
+       Smc_SIslands_DTE_Configuration *dte_tables = NULL;
+       u32 table_size;
+       u8 tdep_count;
+       u32 i;
+
+       if (dte_data == NULL)
+               si_pi->enable_dte = false;
+
+       if (si_pi->enable_dte == false)
+               return 0;
+
+       if (dte_data->k <= 0)
+               return -EINVAL;
+
+       dte_tables = kzalloc(sizeof(Smc_SIslands_DTE_Configuration), GFP_KERNEL);
+       if (dte_tables == NULL) {
+               si_pi->enable_dte = false;
+               return -ENOMEM;
+       }
+
+       table_size = dte_data->k;
+
+       if (table_size > SMC_SISLANDS_DTE_MAX_FILTER_STAGES)
+               table_size = SMC_SISLANDS_DTE_MAX_FILTER_STAGES;
+
+       tdep_count = dte_data->tdep_count;
+       if (tdep_count > SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE)
+               tdep_count = SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE;
+
+       dte_tables->K = cpu_to_be32(table_size);
+       dte_tables->T0 = cpu_to_be32(dte_data->t0);
+       dte_tables->MaxT = cpu_to_be32(dte_data->max_t);
+       dte_tables->WindowSize = dte_data->window_size;
+       dte_tables->temp_select = dte_data->temp_select;
+       dte_tables->DTE_mode = dte_data->dte_mode;
+       dte_tables->Tthreshold = cpu_to_be32(dte_data->t_threshold);
+
+       if (tdep_count > 0)
+               table_size--;
+
+       for (i = 0; i < table_size; i++) {
+               dte_tables->tau[i] = cpu_to_be32(dte_data->tau[i]);
+               dte_tables->R[i]   = cpu_to_be32(dte_data->r[i]);
+       }
+
+       dte_tables->Tdep_count = tdep_count;
+
+       for (i = 0; i < (u32)tdep_count; i++) {
+               dte_tables->T_limits[i] = dte_data->t_limits[i];
+               dte_tables->Tdep_tau[i] = cpu_to_be32(dte_data->tdep_tau[i]);
+               dte_tables->Tdep_R[i] = cpu_to_be32(dte_data->tdep_r[i]);
+       }
+
+       ret = si_copy_bytes_to_smc(rdev, si_pi->dte_table_start, (u8 *)dte_tables,
+                                  sizeof(Smc_SIslands_DTE_Configuration), si_pi->sram_end);
+       kfree(dte_tables);
+
+       return ret;
+}
+
+static int si_get_cac_std_voltage_max_min(struct radeon_device *rdev,
+                                         u16 *max, u16 *min)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct radeon_cac_leakage_table *table =
+               &rdev->pm.dpm.dyn_state.cac_leakage_table;
+       u32 i;
+       u32 v0_loadline;
+
+
+       if (table == NULL)
+               return -EINVAL;
+
+       *max = 0;
+       *min = 0xFFFF;
+
+       for (i = 0; i < table->count; i++) {
+               if (table->entries[i].vddc > *max)
+                       *max = table->entries[i].vddc;
+               if (table->entries[i].vddc < *min)
+                       *min = table->entries[i].vddc;
+       }
+
+       if (si_pi->powertune_data->lkge_lut_v0_percent > 100)
+               return -EINVAL;
+
+       v0_loadline = (*min) * (100 - si_pi->powertune_data->lkge_lut_v0_percent) / 100;
+
+       if (v0_loadline > 0xFFFFUL)
+               return -EINVAL;
+
+       *min = (u16)v0_loadline;
+
+       if ((*min > *max) || (*max == 0) || (*min == 0))
+               return -EINVAL;
+
+       return 0;
+}
+
+static u16 si_get_cac_std_voltage_step(u16 max, u16 min)
+{
+       return ((max - min) + (SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES - 1)) /
+               SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES;
+}
+
+static int si_init_dte_leakage_table(struct radeon_device *rdev,
+                                    PP_SIslands_CacConfig *cac_tables,
+                                    u16 vddc_max, u16 vddc_min, u16 vddc_step,
+                                    u16 t0, u16 t_step)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 leakage;
+       unsigned int i, j;
+       s32 t;
+       u32 smc_leakage;
+       u32 scaling_factor;
+       u16 voltage;
+
+       scaling_factor = si_get_smc_power_scaling_factor(rdev);
+
+       for (i = 0; i < SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES ; i++) {
+               t = (1000 * (i * t_step + t0));
+
+               for (j = 0; j < SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
+                       voltage = vddc_max - (vddc_step * j);
+
+                       si_calculate_leakage_for_v_and_t(rdev,
+                                                        &si_pi->powertune_data->leakage_coefficients,
+                                                        voltage,
+                                                        t,
+                                                        si_pi->dyn_powertune_data.cac_leakage,
+                                                        &leakage);
+
+                       smc_leakage = si_scale_power_for_smc(leakage, scaling_factor) / 4;
+
+                       if (smc_leakage > 0xFFFF)
+                               smc_leakage = 0xFFFF;
+
+                       cac_tables->cac_lkge_lut[i][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES-1-j] =
+                               cpu_to_be16((u16)smc_leakage);
+               }
+       }
+       return 0;
+}
+
+static int si_init_simplified_leakage_table(struct radeon_device *rdev,
+                                           PP_SIslands_CacConfig *cac_tables,
+                                           u16 vddc_max, u16 vddc_min, u16 vddc_step)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 leakage;
+       unsigned int i, j;
+       u32 smc_leakage;
+       u32 scaling_factor;
+       u16 voltage;
+
+       scaling_factor = si_get_smc_power_scaling_factor(rdev);
+
+       for (j = 0; j < SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
+               voltage = vddc_max - (vddc_step * j);
+
+               si_calculate_leakage_for_v(rdev,
+                                          &si_pi->powertune_data->leakage_coefficients,
+                                          si_pi->powertune_data->fixed_kt,
+                                          voltage,
+                                          si_pi->dyn_powertune_data.cac_leakage,
+                                          &leakage);
+
+               smc_leakage = si_scale_power_for_smc(leakage, scaling_factor) / 4;
+
+               if (smc_leakage > 0xFFFF)
+                       smc_leakage = 0xFFFF;
+
+               for (i = 0; i < SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES ; i++)
+                       cac_tables->cac_lkge_lut[i][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES-1-j] =
+                               cpu_to_be16((u16)smc_leakage);
+       }
+       return 0;
+}
+
+static int si_initialize_smc_cac_tables(struct radeon_device *rdev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       PP_SIslands_CacConfig *cac_tables = NULL;
+       u16 vddc_max, vddc_min, vddc_step;
+       u16 t0, t_step;
+       u32 load_line_slope, reg;
+       int ret = 0;
+       u32 ticks_per_us = radeon_get_xclk(rdev) / 100;
+
+       if (ni_pi->enable_cac == false)
+               return 0;
+
+       cac_tables = kzalloc(sizeof(PP_SIslands_CacConfig), GFP_KERNEL);
+       if (!cac_tables)
+               return -ENOMEM;
+
+       reg = RREG32(CG_CAC_CTRL) & ~CAC_WINDOW_MASK;
+       reg |= CAC_WINDOW(si_pi->powertune_data->cac_window);
+       WREG32(CG_CAC_CTRL, reg);
+
+       si_pi->dyn_powertune_data.cac_leakage = rdev->pm.dpm.cac_leakage;
+       si_pi->dyn_powertune_data.dc_pwr_value =
+               si_pi->powertune_data->dc_cac[NISLANDS_DCCAC_LEVEL_0];
+       si_pi->dyn_powertune_data.wintime = si_calculate_cac_wintime(rdev);
+       si_pi->dyn_powertune_data.shift_n = si_pi->powertune_data->shift_n_default;
+
+       si_pi->dyn_powertune_data.leakage_minimum_temperature = 80 * 1000;
+
+       ret = si_get_cac_std_voltage_max_min(rdev, &vddc_max, &vddc_min);
+       if (ret)
+               goto done_free;
+
+       vddc_step = si_get_cac_std_voltage_step(vddc_max, vddc_min);
+       vddc_min = vddc_max - (vddc_step * (SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES - 1));
+       t_step = 4;
+       t0 = 60;
+
+       if (si_pi->enable_dte || ni_pi->driver_calculate_cac_leakage)
+               ret = si_init_dte_leakage_table(rdev, cac_tables,
+                                               vddc_max, vddc_min, vddc_step,
+                                               t0, t_step);
+       else
+               ret = si_init_simplified_leakage_table(rdev, cac_tables,
+                                                      vddc_max, vddc_min, vddc_step);
+       if (ret)
+               goto done_free;
+
+       load_line_slope = ((u32)rdev->pm.dpm.load_line_slope << SMC_SISLANDS_SCALE_R) / 100;
+
+       cac_tables->l2numWin_TDP = cpu_to_be32(si_pi->dyn_powertune_data.l2_lta_window_size);
+       cac_tables->lts_truncate_n = si_pi->dyn_powertune_data.lts_truncate;
+       cac_tables->SHIFT_N = si_pi->dyn_powertune_data.shift_n;
+       cac_tables->lkge_lut_V0 = cpu_to_be32((u32)vddc_min);
+       cac_tables->lkge_lut_Vstep = cpu_to_be32((u32)vddc_step);
+       cac_tables->R_LL = cpu_to_be32(load_line_slope);
+       cac_tables->WinTime = cpu_to_be32(si_pi->dyn_powertune_data.wintime);
+       cac_tables->calculation_repeats = cpu_to_be32(2);
+       cac_tables->dc_cac = cpu_to_be32(0);
+       cac_tables->log2_PG_LKG_SCALE = 12;
+       cac_tables->cac_temp = si_pi->powertune_data->operating_temp;
+       cac_tables->lkge_lut_T0 = cpu_to_be32((u32)t0);
+       cac_tables->lkge_lut_Tstep = cpu_to_be32((u32)t_step);
+
+       ret = si_copy_bytes_to_smc(rdev, si_pi->cac_table_start, (u8 *)cac_tables,
+                                  sizeof(PP_SIslands_CacConfig), si_pi->sram_end);
+
+       if (ret)
+               goto done_free;
+
+       ret = si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_ticks_per_us, ticks_per_us);
+
+done_free:
+       if (ret) {
+               ni_pi->enable_cac = false;
+               ni_pi->enable_power_containment = false;
+       }
+
+       kfree(cac_tables);
+
+       return 0;
+}
+
+static int si_program_cac_config_registers(struct radeon_device *rdev,
+                                          const struct si_cac_config_reg *cac_config_regs)
+{
+       const struct si_cac_config_reg *config_regs = cac_config_regs;
+       u32 data = 0, offset;
+
+       if (!config_regs)
+               return -EINVAL;
+
+       while (config_regs->offset != 0xFFFFFFFF) {
+               switch (config_regs->type) {
+               case SISLANDS_CACCONFIG_CGIND:
+                       offset = SMC_CG_IND_START + config_regs->offset;
+                       if (offset < SMC_CG_IND_END)
+                               data = RREG32_SMC(offset);
+                       break;
+               default:
+                       data = RREG32(config_regs->offset << 2);
+                       break;
+               }
+
+               data &= ~config_regs->mask;
+               data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+
+               switch (config_regs->type) {
+               case SISLANDS_CACCONFIG_CGIND:
+                       offset = SMC_CG_IND_START + config_regs->offset;
+                       if (offset < SMC_CG_IND_END)
+                               WREG32_SMC(offset, data);
+                       break;
+               default:
+                       WREG32(config_regs->offset << 2, data);
+                       break;
+               }
+               config_regs++;
+       }
+       return 0;
+}
+
+static int si_initialize_hardware_cac_manager(struct radeon_device *rdev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int ret;
+
+       if ((ni_pi->enable_cac == false) ||
+           (ni_pi->cac_configuration_required == false))
+               return 0;
+
+       ret = si_program_cac_config_registers(rdev, si_pi->lcac_config);
+       if (ret)
+               return ret;
+       ret = si_program_cac_config_registers(rdev, si_pi->cac_override);
+       if (ret)
+               return ret;
+       ret = si_program_cac_config_registers(rdev, si_pi->cac_weights);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int si_enable_smc_cac(struct radeon_device *rdev,
+                            struct radeon_ps *radeon_new_state,
+                            bool enable)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       PPSMC_Result smc_result;
+       int ret = 0;
+
+       if (ni_pi->enable_cac) {
+               if (enable) {
+                       if (!si_should_disable_uvd_powertune(rdev, radeon_new_state)) {
+                               if (ni_pi->support_cac_long_term_average) {
+                                       smc_result = si_send_msg_to_smc(rdev, PPSMC_CACLongTermAvgEnable);
+                                       if (smc_result != PPSMC_Result_OK)
+                                               ni_pi->support_cac_long_term_average = false;
+                               }
+
+                               smc_result = si_send_msg_to_smc(rdev, PPSMC_MSG_EnableCac);
+                               if (smc_result != PPSMC_Result_OK) {
+                                       ret = -EINVAL;
+                                       ni_pi->cac_enabled = false;
+                               } else {
+                                       ni_pi->cac_enabled = true;
+                               }
+
+                               if (si_pi->enable_dte) {
+                                       smc_result = si_send_msg_to_smc(rdev, PPSMC_MSG_EnableDTE);
+                                       if (smc_result != PPSMC_Result_OK)
+                                               ret = -EINVAL;
+                               }
+                       }
+               } else if (ni_pi->cac_enabled) {
+                       if (si_pi->enable_dte)
+                               smc_result = si_send_msg_to_smc(rdev, PPSMC_MSG_DisableDTE);
+
+                       smc_result = si_send_msg_to_smc(rdev, PPSMC_MSG_DisableCac);
+
+                       ni_pi->cac_enabled = false;
+
+                       if (ni_pi->support_cac_long_term_average)
+                               smc_result = si_send_msg_to_smc(rdev, PPSMC_CACLongTermAvgDisable);
+               }
+       }
+       return ret;
+}
+
+static int si_init_smc_spll_table(struct radeon_device *rdev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       SMC_SISLANDS_SPLL_DIV_TABLE *spll_table;
+       SISLANDS_SMC_SCLK_VALUE sclk_params;
+       u32 fb_div, p_div;
+       u32 clk_s, clk_v;
+       u32 sclk = 0;
+       int ret = 0;
+       u32 tmp;
+       int i;
+
+       if (si_pi->spll_table_start == 0)
+               return -EINVAL;
+
+       spll_table = kzalloc(sizeof(SMC_SISLANDS_SPLL_DIV_TABLE), GFP_KERNEL);
+       if (spll_table == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < 256; i++) {
+               ret = si_calculate_sclk_params(rdev, sclk, &sclk_params);
+               if (ret)
+                       break;
+
+               p_div = (sclk_params.vCG_SPLL_FUNC_CNTL & SPLL_PDIV_A_MASK) >> SPLL_PDIV_A_SHIFT;
+               fb_div = (sclk_params.vCG_SPLL_FUNC_CNTL_3 & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT;
+               clk_s = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM & CLK_S_MASK) >> CLK_S_SHIFT;
+               clk_v = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM_2 & CLK_V_MASK) >> CLK_V_SHIFT;
+
+               fb_div &= ~0x00001FFF;
+               fb_div >>= 1;
+               clk_v >>= 6;
+
+               if (p_div & ~(SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT))
+                       ret = -EINVAL;
+               if (fb_div & ~(SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT))
+                       ret = -EINVAL;
+               if (clk_s & ~(SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT))
+                       ret = -EINVAL;
+               if (clk_v & ~(SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT))
+                       ret = -EINVAL;
+
+               if (ret)
+                       break;
+
+               tmp = ((fb_div << SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK) |
+                       ((p_div << SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK);
+               spll_table->freq[i] = cpu_to_be32(tmp);
+
+               tmp = ((clk_v << SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK) |
+                       ((clk_s << SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK);
+               spll_table->ss[i] = cpu_to_be32(tmp);
+
+               sclk += 512;
+       }
+
+
+       if (!ret)
+               ret = si_copy_bytes_to_smc(rdev, si_pi->spll_table_start,
+                                          (u8 *)spll_table, sizeof(SMC_SISLANDS_SPLL_DIV_TABLE),
+                                          si_pi->sram_end);
+
+       if (ret)
+               ni_pi->enable_power_containment = false;
+
+       kfree(spll_table);
+
+       return ret;
+}
+
+static void si_apply_state_adjust_rules(struct radeon_device *rdev,
+                                       struct radeon_ps *rps)
+{
+       struct ni_ps *ps = ni_get_ps(rps);
+       struct radeon_clock_and_voltage_limits *max_limits;
+       bool disable_mclk_switching;
+       u32 mclk, sclk;
+       u16 vddc, vddci;
+       int i;
+
+       if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
+           ni_dpm_vblank_too_short(rdev))
+               disable_mclk_switching = true;
+       else
+               disable_mclk_switching = false;
+
+       if (rdev->pm.dpm.ac_power)
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+       else
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
+
+       for (i = ps->performance_level_count - 2; i >= 0; i--) {
+               if (ps->performance_levels[i].vddc > ps->performance_levels[i+1].vddc)
+                       ps->performance_levels[i].vddc = ps->performance_levels[i+1].vddc;
+       }
+       if (rdev->pm.dpm.ac_power == false) {
+               for (i = 0; i < ps->performance_level_count; i++) {
+                       if (ps->performance_levels[i].mclk > max_limits->mclk)
+                               ps->performance_levels[i].mclk = max_limits->mclk;
+                       if (ps->performance_levels[i].sclk > max_limits->sclk)
+                               ps->performance_levels[i].sclk = max_limits->sclk;
+                       if (ps->performance_levels[i].vddc > max_limits->vddc)
+                               ps->performance_levels[i].vddc = max_limits->vddc;
+                       if (ps->performance_levels[i].vddci > max_limits->vddci)
+                               ps->performance_levels[i].vddci = max_limits->vddci;
+               }
+       }
+
+       /* XXX validate the min clocks required for display */
+
+       if (disable_mclk_switching) {
+               mclk  = ps->performance_levels[ps->performance_level_count - 1].mclk;
+               sclk = ps->performance_levels[0].sclk;
+               vddc = ps->performance_levels[0].vddc;
+               vddci = ps->performance_levels[ps->performance_level_count - 1].vddci;
+       } else {
+               sclk = ps->performance_levels[0].sclk;
+               mclk = ps->performance_levels[0].mclk;
+               vddc = ps->performance_levels[0].vddc;
+               vddci = ps->performance_levels[0].vddci;
+       }
+
+       /* adjusted low state */
+       ps->performance_levels[0].sclk = sclk;
+       ps->performance_levels[0].mclk = mclk;
+       ps->performance_levels[0].vddc = vddc;
+       ps->performance_levels[0].vddci = vddci;
+
+       for (i = 1; i < ps->performance_level_count; i++) {
+               if (ps->performance_levels[i].sclk < ps->performance_levels[i - 1].sclk)
+                       ps->performance_levels[i].sclk = ps->performance_levels[i - 1].sclk;
+               if (ps->performance_levels[i].vddc < ps->performance_levels[i - 1].vddc)
+                       ps->performance_levels[i].vddc = ps->performance_levels[i - 1].vddc;
+       }
+
+       if (disable_mclk_switching) {
+               mclk = ps->performance_levels[0].mclk;
+               for (i = 1; i < ps->performance_level_count; i++) {
+                       if (mclk < ps->performance_levels[i].mclk)
+                               mclk = ps->performance_levels[i].mclk;
+               }
+               for (i = 0; i < ps->performance_level_count; i++) {
+                       ps->performance_levels[i].mclk = mclk;
+                       ps->performance_levels[i].vddci = vddci;
+               }
+       } else {
+               for (i = 1; i < ps->performance_level_count; i++) {
+                       if (ps->performance_levels[i].mclk < ps->performance_levels[i - 1].mclk)
+                               ps->performance_levels[i].mclk = ps->performance_levels[i - 1].mclk;
+                       if (ps->performance_levels[i].vddci < ps->performance_levels[i - 1].vddci)
+                               ps->performance_levels[i].vddci = ps->performance_levels[i - 1].vddci;
+               }
+       }
+
+        for (i = 0; i < ps->performance_level_count; i++)
+                btc_adjust_clock_combinations(rdev, max_limits,
+                                              &ps->performance_levels[i]);
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                                  ps->performance_levels[i].sclk,
+                                                  max_limits->vddc,  &ps->performance_levels[i].vddc);
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                  ps->performance_levels[i].mclk,
+                                                  max_limits->vddci, &ps->performance_levels[i].vddci);
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                  ps->performance_levels[i].mclk,
+                                                  max_limits->vddc,  &ps->performance_levels[i].vddc);
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk,
+                                                  rdev->clock.current_dispclk,
+                                                  max_limits->vddc,  &ps->performance_levels[i].vddc);
+       }
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               btc_apply_voltage_delta_rules(rdev,
+                                             max_limits->vddc, max_limits->vddci,
+                                             &ps->performance_levels[i].vddc,
+                                             &ps->performance_levels[i].vddci);
+       }
+
+       ps->dc_compatible = true;
+       for (i = 0; i < ps->performance_level_count; i++) {
+               if (ps->performance_levels[i].vddc > rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc)
+                       ps->dc_compatible = false;
+       }
+
+}
+
+#if 0
+static int si_read_smc_soft_register(struct radeon_device *rdev,
+                                    u16 reg_offset, u32 *value)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       return si_read_smc_sram_dword(rdev,
+                                     si_pi->soft_regs_start + reg_offset, value,
+                                     si_pi->sram_end);
+}
+#endif
+
+static int si_write_smc_soft_register(struct radeon_device *rdev,
+                                     u16 reg_offset, u32 value)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       return si_write_smc_sram_dword(rdev,
+                                      si_pi->soft_regs_start + reg_offset,
+                                      value, si_pi->sram_end);
+}
+
+static bool si_is_special_1gb_platform(struct radeon_device *rdev)
+{
+       bool ret = false;
+       u32 tmp, width, row, column, bank, density;
+       bool is_memory_gddr5, is_special;
+
+       tmp = RREG32(MC_SEQ_MISC0);
+       is_memory_gddr5 = (MC_SEQ_MISC0_GDDR5_VALUE == ((tmp & MC_SEQ_MISC0_GDDR5_MASK) >> MC_SEQ_MISC0_GDDR5_SHIFT));
+       is_special = (MC_SEQ_MISC0_REV_ID_VALUE == ((tmp & MC_SEQ_MISC0_REV_ID_MASK) >> MC_SEQ_MISC0_REV_ID_SHIFT))
+               & (MC_SEQ_MISC0_VEN_ID_VALUE == ((tmp & MC_SEQ_MISC0_VEN_ID_MASK) >> MC_SEQ_MISC0_VEN_ID_SHIFT));
+
+       WREG32(MC_SEQ_IO_DEBUG_INDEX, 0xb);
+       width = ((RREG32(MC_SEQ_IO_DEBUG_DATA) >> 1) & 1) ? 16 : 32;
+
+       tmp = RREG32(MC_ARB_RAMCFG);
+       row = ((tmp & NOOFROWS_MASK) >> NOOFROWS_SHIFT) + 10;
+       column = ((tmp & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT) + 8;
+       bank = ((tmp & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + 2;
+
+       density = (1 << (row + column - 20 + bank)) * width;
+
+       if ((rdev->pdev->device == 0x6819) &&
+           is_memory_gddr5 && is_special && (density == 0x400))
+               ret = true;
+
+       return ret;
+}
+
+static void si_get_leakage_vddc(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u16 vddc, count = 0;
+       int i, ret;
+
+       for (i = 0; i < SISLANDS_MAX_LEAKAGE_COUNT; i++) {
+               ret = radeon_atom_get_leakage_vddc_based_on_leakage_idx(rdev, &vddc, SISLANDS_LEAKAGE_INDEX0 + i);
+
+               if (!ret && (vddc > 0) && (vddc != (SISLANDS_LEAKAGE_INDEX0 + i))) {
+                       si_pi->leakage_voltage.entries[count].voltage = vddc;
+                       si_pi->leakage_voltage.entries[count].leakage_index =
+                               SISLANDS_LEAKAGE_INDEX0 + i;
+                       count++;
+               }
+       }
+       si_pi->leakage_voltage.count = count;
+}
+
+static int si_get_leakage_voltage_from_leakage_index(struct radeon_device *rdev,
+                                                    u32 index, u16 *leakage_voltage)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int i;
+
+       if (leakage_voltage == NULL)
+               return -EINVAL;
+
+       if ((index & 0xff00) != 0xff00)
+               return -EINVAL;
+
+       if ((index & 0xff) > SISLANDS_MAX_LEAKAGE_COUNT + 1)
+               return -EINVAL;
+
+       if (index < SISLANDS_LEAKAGE_INDEX0)
+               return -EINVAL;
+
+       for (i = 0; i < si_pi->leakage_voltage.count; i++) {
+               if (si_pi->leakage_voltage.entries[i].leakage_index == index) {
+                       *leakage_voltage = si_pi->leakage_voltage.entries[i].voltage;
+                       return 0;
+               }
+       }
+       return -EAGAIN;
+}
+
+static void si_set_dpm_event_sources(struct radeon_device *rdev, u32 sources)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       bool want_thermal_protection;
+       enum radeon_dpm_event_src dpm_event_src;
+
+       switch (sources) {
+       case 0:
+       default:
+               want_thermal_protection = false;
+                break;
+       case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_DIGITAL;
+               break;
+       case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_EXTERNAL;
+               break;
+       case ((1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL) |
+             (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL)):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL;
+               break;
+       }
+
+       if (want_thermal_protection) {
+               WREG32_P(CG_THERMAL_CTRL, DPM_EVENT_SRC(dpm_event_src), ~DPM_EVENT_SRC_MASK);
+               if (pi->thermal_protection)
+                       WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
+       } else {
+               WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
+       }
+}
+
+static void si_enable_auto_throttle_source(struct radeon_device *rdev,
+                                          enum radeon_dpm_auto_throttle_src source,
+                                          bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (enable) {
+               if (!(pi->active_auto_throttle_sources & (1 << source))) {
+                       pi->active_auto_throttle_sources |= 1 << source;
+                       si_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
+               }
+       } else {
+               if (pi->active_auto_throttle_sources & (1 << source)) {
+                       pi->active_auto_throttle_sources &= ~(1 << source);
+                       si_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
+               }
+       }
+}
+
+static void si_start_dpm(struct radeon_device *rdev)
+{
+       WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN);
+}
+
+static void si_stop_dpm(struct radeon_device *rdev)
+{
+       WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN);
+}
+
+static void si_enable_sclk_control(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~SCLK_PWRMGT_OFF);
+       else
+               WREG32_P(SCLK_PWRMGT_CNTL, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF);
+
+}
+
+#if 0
+static int si_notify_hardware_of_thermal_state(struct radeon_device *rdev,
+                                              u32 thermal_level)
+{
+       PPSMC_Result ret;
+
+       if (thermal_level == 0) {
+               ret = si_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt);
+               if (ret == PPSMC_Result_OK)
+                       return 0;
+               else
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+static void si_notify_hardware_vpu_recovery_event(struct radeon_device *rdev)
+{
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_tdr_is_about_to_happen, true);
+}
+#endif
+
+#if 0
+static int si_notify_hw_of_powersource(struct radeon_device *rdev, bool ac_power)
+{
+       if (ac_power)
+               return (si_send_msg_to_smc(rdev, PPSMC_MSG_RunningOnAC) == PPSMC_Result_OK) ?
+                       0 : -EINVAL;
+
+       return 0;
+}
+#endif
+
+static PPSMC_Result si_send_msg_to_smc_with_parameter(struct radeon_device *rdev,
+                                                     PPSMC_Msg msg, u32 parameter)
+{
+       WREG32(SMC_SCRATCH0, parameter);
+       return si_send_msg_to_smc(rdev, msg);
+}
+
+static int si_restrict_performance_levels_before_switch(struct radeon_device *rdev)
+{
+       if (si_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 1) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+int si_dpm_force_performance_level(struct radeon_device *rdev,
+                                  enum radeon_dpm_forced_level level)
+{
+       struct radeon_ps *rps = rdev->pm.dpm.current_ps;
+       struct ni_ps *ps = ni_get_ps(rps);
+       u32 levels;
+
+       if (level == RADEON_DPM_FORCED_LEVEL_HIGH) {
+               if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK)
+                       return -EINVAL;
+
+               if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 1) != PPSMC_Result_OK)
+                       return -EINVAL;
+       } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) {
+               if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+                       return -EINVAL;
+
+               levels = ps->performance_level_count - 1;
+               if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, levels) != PPSMC_Result_OK)
+                       return -EINVAL;
+       } else if (level == RADEON_DPM_FORCED_LEVEL_AUTO) {
+               if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+                       return -EINVAL;
+
+               if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       rdev->pm.dpm.forced_level = level;
+
+       return 0;
+}
+
+static int si_set_boot_state(struct radeon_device *rdev)
+{
+       return (si_send_msg_to_smc(rdev, PPSMC_MSG_SwitchToInitialState) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_set_sw_state(struct radeon_device *rdev)
+{
+       return (si_send_msg_to_smc(rdev, PPSMC_MSG_SwitchToSwState) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_halt_smc(struct radeon_device *rdev)
+{
+       if (si_send_msg_to_smc(rdev, PPSMC_MSG_Halt) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return (si_wait_for_smc_inactive(rdev) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_resume_smc(struct radeon_device *rdev)
+{
+       if (si_send_msg_to_smc(rdev, PPSMC_FlushDataCache) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return (si_send_msg_to_smc(rdev, PPSMC_MSG_Resume) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static void si_dpm_start_smc(struct radeon_device *rdev)
+{
+       si_program_jump_on_start(rdev);
+       si_start_smc(rdev);
+       si_start_smc_clock(rdev);
+}
+
+static void si_dpm_stop_smc(struct radeon_device *rdev)
+{
+       si_reset_smc(rdev);
+       si_stop_smc_clock(rdev);
+}
+
+static int si_process_firmware_header(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_stateTable,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+        si_pi->state_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_softRegisters,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->soft_regs_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->mc_reg_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->arb_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_CacConfigTable,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->cac_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_DteConfiguration,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->dte_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_spllTable,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->spll_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_PAPMParameters,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->papm_cfg_table_start = tmp;
+
+       return ret;
+}
+
+static void si_read_clock_registers(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       si_pi->clock_registers.cg_spll_func_cntl = RREG32(CG_SPLL_FUNC_CNTL);
+       si_pi->clock_registers.cg_spll_func_cntl_2 = RREG32(CG_SPLL_FUNC_CNTL_2);
+       si_pi->clock_registers.cg_spll_func_cntl_3 = RREG32(CG_SPLL_FUNC_CNTL_3);
+       si_pi->clock_registers.cg_spll_func_cntl_4 = RREG32(CG_SPLL_FUNC_CNTL_4);
+       si_pi->clock_registers.cg_spll_spread_spectrum = RREG32(CG_SPLL_SPREAD_SPECTRUM);
+       si_pi->clock_registers.cg_spll_spread_spectrum_2 = RREG32(CG_SPLL_SPREAD_SPECTRUM_2);
+       si_pi->clock_registers.dll_cntl = RREG32(DLL_CNTL);
+       si_pi->clock_registers.mclk_pwrmgt_cntl = RREG32(MCLK_PWRMGT_CNTL);
+       si_pi->clock_registers.mpll_ad_func_cntl = RREG32(MPLL_AD_FUNC_CNTL);
+       si_pi->clock_registers.mpll_dq_func_cntl = RREG32(MPLL_DQ_FUNC_CNTL);
+       si_pi->clock_registers.mpll_func_cntl = RREG32(MPLL_FUNC_CNTL);
+       si_pi->clock_registers.mpll_func_cntl_1 = RREG32(MPLL_FUNC_CNTL_1);
+       si_pi->clock_registers.mpll_func_cntl_2 = RREG32(MPLL_FUNC_CNTL_2);
+       si_pi->clock_registers.mpll_ss1 = RREG32(MPLL_SS1);
+       si_pi->clock_registers.mpll_ss2 = RREG32(MPLL_SS2);
+}
+
+static void si_enable_thermal_protection(struct radeon_device *rdev,
+                                         bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
+       else
+               WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
+}
+
+static void si_enable_acpi_power_management(struct radeon_device *rdev)
+{
+       WREG32_P(GENERAL_PWRMGT, STATIC_PM_EN, ~STATIC_PM_EN);
+}
+
+#if 0
+static int si_enter_ulp_state(struct radeon_device *rdev)
+{
+       WREG32(SMC_MESSAGE_0, PPSMC_MSG_SwitchToMinimumPower);
+
+       udelay(25000);
+
+       return 0;
+}
+
+static int si_exit_ulp_state(struct radeon_device *rdev)
+{
+       int i;
+
+       WREG32(SMC_MESSAGE_0, PPSMC_MSG_ResumeFromMinimumPower);
+
+       udelay(7000);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(SMC_RESP_0) == 1)
+                       break;
+               udelay(1000);
+       }
+
+       return 0;
+}
+#endif
+
+static int si_notify_smc_display_change(struct radeon_device *rdev,
+                                    bool has_display)
+{
+       PPSMC_Msg msg = has_display ?
+               PPSMC_MSG_HasDisplay : PPSMC_MSG_NoDisplay;
+
+       return (si_send_msg_to_smc(rdev, msg) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static void si_program_response_times(struct radeon_device *rdev)
+{
+       u32 voltage_response_time, backbias_response_time, acpi_delay_time, vbi_time_out;
+       u32 vddc_dly, acpi_dly, vbi_dly;
+       u32 reference_clock;
+
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_mvdd_chg_time, 1);
+
+       voltage_response_time = (u32)rdev->pm.dpm.voltage_response_time;
+        backbias_response_time = (u32)rdev->pm.dpm.backbias_response_time;
+
+       if (voltage_response_time == 0)
+               voltage_response_time = 1000;
+
+       acpi_delay_time = 15000;
+       vbi_time_out = 100000;
+
+       reference_clock = radeon_get_xclk(rdev);
+
+       vddc_dly = (voltage_response_time  * reference_clock) / 100;
+       acpi_dly = (acpi_delay_time * reference_clock) / 100;
+       vbi_dly  = (vbi_time_out * reference_clock) / 100;
+
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_delay_vreg,  vddc_dly);
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_delay_acpi,  acpi_dly);
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_mclk_chg_timeout, vbi_dly);
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_mc_block_delay, 0xAA);
+}
+
+static void si_program_ds_registers(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 tmp = 1; /* XXX: 0x10 on tahiti A0 */
+
+       if (eg_pi->sclk_deep_sleep) {
+               WREG32_P(MISC_CLK_CNTL, DEEP_SLEEP_CLK_SEL(tmp), ~DEEP_SLEEP_CLK_SEL_MASK);
+               WREG32_P(CG_SPLL_AUTOSCALE_CNTL, AUTOSCALE_ON_SS_CLEAR,
+                        ~AUTOSCALE_ON_SS_CLEAR);
+       }
+}
+
+static void si_program_display_gap(struct radeon_device *rdev)
+{
+       u32 tmp, pipe;
+       int i;
+
+       tmp = RREG32(CG_DISPLAY_GAP_CNTL) & ~(DISP1_GAP_MASK | DISP2_GAP_MASK);
+       if (rdev->pm.dpm.new_active_crtc_count > 0)
+               tmp |= DISP1_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM);
+       else
+               tmp |= DISP1_GAP(R600_PM_DISPLAY_GAP_IGNORE);
+
+       if (rdev->pm.dpm.new_active_crtc_count > 1)
+               tmp |= DISP2_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM);
+       else
+               tmp |= DISP2_GAP(R600_PM_DISPLAY_GAP_IGNORE);
+
+       WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+
+       tmp = RREG32(DCCG_DISP_SLOW_SELECT_REG);
+       pipe = (tmp & DCCG_DISP1_SLOW_SELECT_MASK) >> DCCG_DISP1_SLOW_SELECT_SHIFT;
+
+       if ((rdev->pm.dpm.new_active_crtc_count > 0) &&
+           (!(rdev->pm.dpm.new_active_crtcs & (1 << pipe)))) {
+               /* find the first active crtc */
+               for (i = 0; i < rdev->num_crtc; i++) {
+                       if (rdev->pm.dpm.new_active_crtcs & (1 << i))
+                               break;
+               }
+               if (i == rdev->num_crtc)
+                       pipe = 0;
+               else
+                       pipe = i;
+
+               tmp &= ~DCCG_DISP1_SLOW_SELECT_MASK;
+               tmp |= DCCG_DISP1_SLOW_SELECT(pipe);
+               WREG32(DCCG_DISP_SLOW_SELECT_REG, tmp);
+       }
+
+       si_notify_smc_display_change(rdev, rdev->pm.dpm.new_active_crtc_count > 0);
+}
+
+static void si_enable_spread_spectrum(struct radeon_device *rdev, bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (enable) {
+               if (pi->sclk_ss)
+                       WREG32_P(GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, ~DYN_SPREAD_SPECTRUM_EN);
+       } else {
+               WREG32_P(CG_SPLL_SPREAD_SPECTRUM, 0, ~SSEN);
+               WREG32_P(GENERAL_PWRMGT, 0, ~DYN_SPREAD_SPECTRUM_EN);
+       }
+}
+
+static void si_setup_bsp(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 xclk = radeon_get_xclk(rdev);
+
+       r600_calculate_u_and_p(pi->asi,
+                              xclk,
+                              16,
+                              &pi->bsp,
+                              &pi->bsu);
+
+       r600_calculate_u_and_p(pi->pasi,
+                              xclk,
+                              16,
+                              &pi->pbsp,
+                              &pi->pbsu);
+
+
+        pi->dsp = BSP(pi->bsp) | BSU(pi->bsu);
+       pi->psp = BSP(pi->pbsp) | BSU(pi->pbsu);
+
+       WREG32(CG_BSP, pi->dsp);
+}
+
+static void si_program_git(struct radeon_device *rdev)
+{
+       WREG32_P(CG_GIT, CG_GICST(R600_GICST_DFLT), ~CG_GICST_MASK);
+}
+
+static void si_program_tp(struct radeon_device *rdev)
+{
+       int i;
+       enum r600_td td = R600_TD_DFLT;
+
+       for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
+               WREG32(CG_FFCT_0 + (i * 4), (UTC_0(r600_utc[i]) | DTC_0(r600_dtc[i])));
+
+       if (td == R600_TD_AUTO)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_FORCE_TREND_SEL);
+       else
+               WREG32_P(SCLK_PWRMGT_CNTL, FIR_FORCE_TREND_SEL, ~FIR_FORCE_TREND_SEL);
+
+       if (td == R600_TD_UP)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_TREND_MODE);
+
+       if (td == R600_TD_DOWN)
+               WREG32_P(SCLK_PWRMGT_CNTL, FIR_TREND_MODE, ~FIR_TREND_MODE);
+}
+
+static void si_program_tpp(struct radeon_device *rdev)
+{
+       WREG32(CG_TPC, R600_TPC_DFLT);
+}
+
+static void si_program_sstp(struct radeon_device *rdev)
+{
+       WREG32(CG_SSP, (SSTU(R600_SSTU_DFLT) | SST(R600_SST_DFLT)));
+}
+
+static void si_enable_display_gap(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL);
+
+       tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK);
+       tmp |= (DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE) |
+               DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE));
+       WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+}
+
+static void si_program_vc(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       WREG32(CG_FTV, pi->vrc);
+}
+
+static void si_clear_vc(struct radeon_device *rdev)
+{
+       WREG32(CG_FTV, 0);
+}
+
+static u8 si_get_ddr3_mclk_frequency_ratio(u32 memory_clock)
+{
+       u8 mc_para_index;
+
+       if (memory_clock < 10000)
+               mc_para_index = 0;
+       else if (memory_clock >= 80000)
+               mc_para_index = 0x0f;
+       else
+               mc_para_index = (u8)((memory_clock - 10000) / 5000 + 1);
+       return mc_para_index;
+}
+
+static u8 si_get_mclk_frequency_ratio(u32 memory_clock, bool strobe_mode)
+{
+       u8 mc_para_index;
+
+       if (strobe_mode) {
+               if (memory_clock < 12500)
+                       mc_para_index = 0x00;
+               else if (memory_clock > 47500)
+                       mc_para_index = 0x0f;
+               else
+                       mc_para_index = (u8)((memory_clock - 10000) / 2500);
+       } else {
+               if (memory_clock < 65000)
+                       mc_para_index = 0x00;
+               else if (memory_clock > 135000)
+                       mc_para_index = 0x0f;
+               else
+                       mc_para_index = (u8)((memory_clock - 60000) / 5000);
+       }
+       return mc_para_index;
+}
+
+static u8 si_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       bool strobe_mode = false;
+       u8 result = 0;
+
+       if (mclk <= pi->mclk_strobe_mode_threshold)
+               strobe_mode = true;
+
+       if (pi->mem_gddr5)
+               result = si_get_mclk_frequency_ratio(mclk, strobe_mode);
+       else
+               result = si_get_ddr3_mclk_frequency_ratio(mclk);
+
+       if (strobe_mode)
+               result |= SISLANDS_SMC_STROBE_ENABLE;
+
+       return result;
+}
+
+static int si_upload_firmware(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int ret;
+
+       si_reset_smc(rdev);
+       si_stop_smc_clock(rdev);
+
+       ret = si_load_smc_ucode(rdev, si_pi->sram_end);
+
+       return ret;
+}
+
+static bool si_validate_phase_shedding_tables(struct radeon_device *rdev,
+                                             const struct atom_voltage_table *table,
+                                             const struct radeon_phase_shedding_limits_table *limits)
+{
+       u32 data, num_bits, num_levels;
+
+       if ((table == NULL) || (limits == NULL))
+               return false;
+
+       data = table->mask_low;
+
+       num_bits = hweight32(data);
+
+       if (num_bits == 0)
+               return false;
+
+       num_levels = (1 << num_bits);
+
+       if (table->count != num_levels)
+               return false;
+
+       if (limits->count != (num_levels - 1))
+               return false;
+
+       return true;
+}
+
+static void si_trim_voltage_table_to_fit_state_table(struct radeon_device *rdev,
+                                                    struct atom_voltage_table *voltage_table)
+{
+       unsigned int i, diff;
+
+       if (voltage_table->count <= SISLANDS_MAX_NO_VREG_STEPS)
+               return;
+
+       diff = voltage_table->count - SISLANDS_MAX_NO_VREG_STEPS;
+
+       for (i= 0; i < SISLANDS_MAX_NO_VREG_STEPS; i++)
+               voltage_table->entries[i] = voltage_table->entries[i + diff];
+
+       voltage_table->count = SISLANDS_MAX_NO_VREG_STEPS;
+}
+
+static int si_construct_voltage_tables(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int ret;
+
+       ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDC,
+                                           VOLTAGE_OBJ_GPIO_LUT, &eg_pi->vddc_voltage_table);
+       if (ret)
+               return ret;
+
+       if (eg_pi->vddc_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+               si_trim_voltage_table_to_fit_state_table(rdev, &eg_pi->vddc_voltage_table);
+
+       if (eg_pi->vddci_control) {
+               ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDCI,
+                                                   VOLTAGE_OBJ_GPIO_LUT, &eg_pi->vddci_voltage_table);
+               if (ret)
+                       return ret;
+
+               if (eg_pi->vddci_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+                       si_trim_voltage_table_to_fit_state_table(rdev, &eg_pi->vddci_voltage_table);
+       }
+
+       if (pi->mvdd_control) {
+               ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_MVDDC,
+                                                   VOLTAGE_OBJ_GPIO_LUT, &si_pi->mvdd_voltage_table);
+
+               if (ret) {
+                       pi->mvdd_control = false;
+                       return ret;
+               }
+
+               if (si_pi->mvdd_voltage_table.count == 0) {
+                       pi->mvdd_control = false;
+                       return -EINVAL;
+               }
+
+               if (si_pi->mvdd_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+                       si_trim_voltage_table_to_fit_state_table(rdev, &si_pi->mvdd_voltage_table);
+       }
+
+       if (si_pi->vddc_phase_shed_control) {
+               ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDC,
+                                                   VOLTAGE_OBJ_PHASE_LUT, &si_pi->vddc_phase_shed_table);
+               if (ret)
+                       si_pi->vddc_phase_shed_control = false;
+
+               if ((si_pi->vddc_phase_shed_table.count == 0) ||
+                   (si_pi->vddc_phase_shed_table.count > SISLANDS_MAX_NO_VREG_STEPS))
+                       si_pi->vddc_phase_shed_control = false;
+       }
+
+       return 0;
+}
+
+static void si_populate_smc_voltage_table(struct radeon_device *rdev,
+                                         const struct atom_voltage_table *voltage_table,
+                                         SISLANDS_SMC_STATETABLE *table)
+{
+       unsigned int i;
+
+       for (i = 0; i < voltage_table->count; i++)
+               table->lowSMIO[i] |= cpu_to_be32(voltage_table->entries[i].smio_low);
+}
+
+static int si_populate_smc_voltage_tables(struct radeon_device *rdev,
+                                         SISLANDS_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u8 i;
+
+       if (eg_pi->vddc_voltage_table.count) {
+               si_populate_smc_voltage_table(rdev, &eg_pi->vddc_voltage_table, table);
+               table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
+                       cpu_to_be32(eg_pi->vddc_voltage_table.mask_low);
+
+               for (i = 0; i < eg_pi->vddc_voltage_table.count; i++) {
+                       if (pi->max_vddc_in_table <= eg_pi->vddc_voltage_table.entries[i].value) {
+                               table->maxVDDCIndexInPPTable = i;
+                               break;
+                       }
+               }
+       }
+
+       if (eg_pi->vddci_voltage_table.count) {
+               si_populate_smc_voltage_table(rdev, &eg_pi->vddci_voltage_table, table);
+
+               table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDCI] =
+                       cpu_to_be32(eg_pi->vddci_voltage_table.mask_low);
+       }
+
+
+       if (si_pi->mvdd_voltage_table.count) {
+               si_populate_smc_voltage_table(rdev, &si_pi->mvdd_voltage_table, table);
+
+               table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_MVDD] =
+                       cpu_to_be32(si_pi->mvdd_voltage_table.mask_low);
+       }
+
+       if (si_pi->vddc_phase_shed_control) {
+               if (si_validate_phase_shedding_tables(rdev, &si_pi->vddc_phase_shed_table,
+                                                     &rdev->pm.dpm.dyn_state.phase_shedding_limits_table)) {
+                       si_populate_smc_voltage_table(rdev, &si_pi->vddc_phase_shed_table, table);
+
+                       table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
+                               cpu_to_be32(si_pi->vddc_phase_shed_table.mask_low);
+
+                       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_phase_shedding_delay,
+                                                  (u32)si_pi->vddc_phase_shed_table.phase_delay);
+               } else {
+                       si_pi->vddc_phase_shed_control = false;
+               }
+       }
+
+       return 0;
+}
+
+static int si_populate_voltage_value(struct radeon_device *rdev,
+                                    const struct atom_voltage_table *table,
+                                    u16 value, SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       unsigned int i;
+
+       for (i = 0; i < table->count; i++) {
+               if (value <= table->entries[i].value) {
+                       voltage->index = (u8)i;
+                       voltage->value = cpu_to_be16(table->entries[i].value);
+                       break;
+               }
+       }
+
+       if (i >= table->count)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int si_populate_mvdd_value(struct radeon_device *rdev, u32 mclk,
+                                 SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (pi->mvdd_control) {
+               if (mclk <= pi->mvdd_split_frequency)
+                       voltage->index = 0;
+               else
+                       voltage->index = (u8)(si_pi->mvdd_voltage_table.count) - 1;
+
+               voltage->value = cpu_to_be16(si_pi->mvdd_voltage_table.entries[voltage->index].value);
+       }
+       return 0;
+}
+
+static int si_get_std_voltage_value(struct radeon_device *rdev,
+                                   SISLANDS_SMC_VOLTAGE_VALUE *voltage,
+                                   u16 *std_voltage)
+{
+       u16 v_index;
+       bool voltage_found = false;
+       *std_voltage = be16_to_cpu(voltage->value);
+
+       if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries) {
+               if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_NEW_CAC_VOLTAGE) {
+                       if (rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries == NULL)
+                               return -EINVAL;
+
+                       for (v_index = 0; (u32)v_index < rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
+                               if (be16_to_cpu(voltage->value) ==
+                                   (u16)rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[v_index].v) {
+                                       voltage_found = true;
+                                       if ((u32)v_index < rdev->pm.dpm.dyn_state.cac_leakage_table.count)
+                                               *std_voltage =
+                                                       rdev->pm.dpm.dyn_state.cac_leakage_table.entries[v_index].vddc;
+                                       else
+                                               *std_voltage =
+                                                       rdev->pm.dpm.dyn_state.cac_leakage_table.entries[rdev->pm.dpm.dyn_state.cac_leakage_table.count-1].vddc;
+                                       break;
+                               }
+                       }
+
+                       if (!voltage_found) {
+                               for (v_index = 0; (u32)v_index < rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
+                                       if (be16_to_cpu(voltage->value) <=
+                                           (u16)rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[v_index].v) {
+                                               voltage_found = true;
+                                               if ((u32)v_index < rdev->pm.dpm.dyn_state.cac_leakage_table.count)
+                                                       *std_voltage =
+                                                               rdev->pm.dpm.dyn_state.cac_leakage_table.entries[v_index].vddc;
+                                               else
+                                                       *std_voltage =
+                                                               rdev->pm.dpm.dyn_state.cac_leakage_table.entries[rdev->pm.dpm.dyn_state.cac_leakage_table.count-1].vddc;
+                                               break;
+                                       }
+                               }
+                       }
+               } else {
+                       if ((u32)voltage->index < rdev->pm.dpm.dyn_state.cac_leakage_table.count)
+                               *std_voltage = rdev->pm.dpm.dyn_state.cac_leakage_table.entries[voltage->index].vddc;
+               }
+       }
+
+       return 0;
+}
+
+static int si_populate_std_voltage_value(struct radeon_device *rdev,
+                                        u16 value, u8 index,
+                                        SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       voltage->index = index;
+       voltage->value = cpu_to_be16(value);
+
+       return 0;
+}
+
+static int si_populate_phase_shedding_value(struct radeon_device *rdev,
+                                           const struct radeon_phase_shedding_limits_table *limits,
+                                           u16 voltage, u32 sclk, u32 mclk,
+                                           SISLANDS_SMC_VOLTAGE_VALUE *smc_voltage)
+{
+       unsigned int i;
+
+       for (i = 0; i < limits->count; i++) {
+               if ((voltage <= limits->entries[i].voltage) &&
+                   (sclk <= limits->entries[i].sclk) &&
+                   (mclk <= limits->entries[i].mclk))
+                       break;
+       }
+
+       smc_voltage->phase_settings = (u8)i;
+
+       return 0;
+}
+
+static int si_init_arb_table_index(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = si_read_smc_sram_dword(rdev, si_pi->arb_table_start, &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       tmp &= 0x00FFFFFF;
+       tmp |= MC_CG_ARB_FREQ_F1 << 24;
+
+       return si_write_smc_sram_dword(rdev, si_pi->arb_table_start,  tmp, si_pi->sram_end);
+}
+
+static int si_initial_switch_from_arb_f0_to_f1(struct radeon_device *rdev)
+{
+       return ni_copy_and_switch_arb_sets(rdev, MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
+}
+
+static int si_reset_to_default(struct radeon_device *rdev)
+{
+       return (si_send_msg_to_smc(rdev, PPSMC_MSG_ResetToDefaults) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_force_switch_to_arb_f0(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = si_read_smc_sram_dword(rdev, si_pi->arb_table_start,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       tmp = (tmp >> 24) & 0xff;
+
+       if (tmp == MC_CG_ARB_FREQ_F0)
+               return 0;
+
+       return ni_copy_and_switch_arb_sets(rdev, tmp, MC_CG_ARB_FREQ_F0);
+}
+
+static u32 si_calculate_memory_refresh_rate(struct radeon_device *rdev,
+                                           u32 engine_clock)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 dram_rows;
+       u32 dram_refresh_rate;
+       u32 mc_arb_rfsh_rate;
+       u32 tmp = (RREG32(MC_ARB_RAMCFG) & NOOFROWS_MASK) >> NOOFROWS_SHIFT;
+
+       if (pi->mem_gddr5)
+               dram_rows = 1 << (tmp + 10);
+       else
+               dram_rows = DDR3_DRAM_ROWS;
+
+       dram_refresh_rate = 1 << ((RREG32(MC_SEQ_MISC0) & 0x3) + 3);
+       mc_arb_rfsh_rate = ((engine_clock * 10) * dram_refresh_rate / dram_rows - 32) / 64;
+
+       return mc_arb_rfsh_rate;
+}
+
+static int si_populate_memory_timing_parameters(struct radeon_device *rdev,
+                                               struct rv7xx_pl *pl,
+                                               SMC_SIslands_MCArbDramTimingRegisterSet *arb_regs)
+{
+       u32 dram_timing;
+       u32 dram_timing2;
+       u32 burst_time;
+
+       arb_regs->mc_arb_rfsh_rate =
+               (u8)si_calculate_memory_refresh_rate(rdev, pl->sclk);
+
+       radeon_atom_set_engine_dram_timings(rdev,
+                                           pl->sclk,
+                                            pl->mclk);
+
+       dram_timing  = RREG32(MC_ARB_DRAM_TIMING);
+       dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+       burst_time = RREG32(MC_ARB_BURST_TIME) & STATE0_MASK;
+
+       arb_regs->mc_arb_dram_timing  = cpu_to_be32(dram_timing);
+       arb_regs->mc_arb_dram_timing2 = cpu_to_be32(dram_timing2);
+       arb_regs->mc_arb_burst_time = (u8)burst_time;
+
+       return 0;
+}
+
+static int si_do_program_memory_timing_parameters(struct radeon_device *rdev,
+                                                 struct radeon_ps *radeon_state,
+                                                 unsigned int first_arb_set)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       SMC_SIslands_MCArbDramTimingRegisterSet arb_regs = { 0 };
+       int i, ret = 0;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               ret = si_populate_memory_timing_parameters(rdev, &state->performance_levels[i], &arb_regs);
+               if (ret)
+                       break;
+               ret = si_copy_bytes_to_smc(rdev,
+                                          si_pi->arb_table_start +
+                                          offsetof(SMC_SIslands_MCArbDramTimingRegisters, data) +
+                                          sizeof(SMC_SIslands_MCArbDramTimingRegisterSet) * (first_arb_set + i),
+                                          (u8 *)&arb_regs,
+                                          sizeof(SMC_SIslands_MCArbDramTimingRegisterSet),
+                                          si_pi->sram_end);
+               if (ret)
+                       break;
+        }
+
+       return ret;
+}
+
+static int si_program_memory_timing_parameters(struct radeon_device *rdev,
+                                              struct radeon_ps *radeon_new_state)
+{
+       return si_do_program_memory_timing_parameters(rdev, radeon_new_state,
+                                                     SISLANDS_DRIVER_STATE_ARB_INDEX);
+}
+
+static int si_populate_initial_mvdd_value(struct radeon_device *rdev,
+                                         struct SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (pi->mvdd_control)
+               return si_populate_voltage_value(rdev, &si_pi->mvdd_voltage_table,
+                                                si_pi->mvdd_bootup_value, voltage);
+
+       return 0;
+}
+
+static int si_populate_smc_initial_state(struct radeon_device *rdev,
+                                        struct radeon_ps *radeon_initial_state,
+                                        SISLANDS_SMC_STATETABLE *table)
+{
+       struct ni_ps *initial_state = ni_get_ps(radeon_initial_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 reg;
+       int ret;
+
+       table->initialState.levels[0].mclk.vDLL_CNTL =
+               cpu_to_be32(si_pi->clock_registers.dll_cntl);
+       table->initialState.levels[0].mclk.vMCLK_PWRMGT_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mclk_pwrmgt_cntl);
+       table->initialState.levels[0].mclk.vMPLL_AD_FUNC_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mpll_ad_func_cntl);
+       table->initialState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mpll_dq_func_cntl);
+       table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mpll_func_cntl);
+       table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL_1 =
+               cpu_to_be32(si_pi->clock_registers.mpll_func_cntl_1);
+       table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL_2 =
+               cpu_to_be32(si_pi->clock_registers.mpll_func_cntl_2);
+       table->initialState.levels[0].mclk.vMPLL_SS =
+               cpu_to_be32(si_pi->clock_registers.mpll_ss1);
+       table->initialState.levels[0].mclk.vMPLL_SS2 =
+               cpu_to_be32(si_pi->clock_registers.mpll_ss2);
+
+       table->initialState.levels[0].mclk.mclk_value =
+               cpu_to_be32(initial_state->performance_levels[0].mclk);
+
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_2);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_3);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_4);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_spread_spectrum);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2  =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_spread_spectrum_2);
+
+       table->initialState.levels[0].sclk.sclk_value =
+               cpu_to_be32(initial_state->performance_levels[0].sclk);
+
+       table->initialState.levels[0].arbRefreshState =
+               SISLANDS_INITIAL_STATE_ARB_INDEX;
+
+       table->initialState.levels[0].ACIndex = 0;
+
+       ret = si_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                       initial_state->performance_levels[0].vddc,
+                                       &table->initialState.levels[0].vddc);
+
+       if (!ret) {
+               u16 std_vddc;
+
+               ret = si_get_std_voltage_value(rdev,
+                                              &table->initialState.levels[0].vddc,
+                                              &std_vddc);
+               if (!ret)
+                       si_populate_std_voltage_value(rdev, std_vddc,
+                                                     table->initialState.levels[0].vddc.index,
+                                                     &table->initialState.levels[0].std_vddc);
+       }
+
+       if (eg_pi->vddci_control)
+               si_populate_voltage_value(rdev,
+                                         &eg_pi->vddci_voltage_table,
+                                         initial_state->performance_levels[0].vddci,
+                                         &table->initialState.levels[0].vddci);
+
+       if (si_pi->vddc_phase_shed_control)
+               si_populate_phase_shedding_value(rdev,
+                                                &rdev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                initial_state->performance_levels[0].vddc,
+                                                initial_state->performance_levels[0].sclk,
+                                                initial_state->performance_levels[0].mclk,
+                                                &table->initialState.levels[0].vddc);
+
+       si_populate_initial_mvdd_value(rdev, &table->initialState.levels[0].mvdd);
+
+       reg = CG_R(0xffff) | CG_L(0);
+       table->initialState.levels[0].aT = cpu_to_be32(reg);
+
+       table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp);
+
+       table->initialState.levels[0].gen2PCIE = (u8)si_pi->boot_pcie_gen;
+
+       if (pi->mem_gddr5) {
+               table->initialState.levels[0].strobeMode =
+                       si_get_strobe_mode_settings(rdev,
+                                                   initial_state->performance_levels[0].mclk);
+
+               if (initial_state->performance_levels[0].mclk > pi->mclk_edc_enable_threshold)
+                       table->initialState.levels[0].mcFlags = SISLANDS_SMC_MC_EDC_RD_FLAG | SISLANDS_SMC_MC_EDC_WR_FLAG;
+               else
+                       table->initialState.levels[0].mcFlags =  0;
+       }
+
+       table->initialState.levelCount = 1;
+
+       table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       table->initialState.levels[0].dpm2.MaxPS = 0;
+       table->initialState.levels[0].dpm2.NearTDPDec = 0;
+       table->initialState.levels[0].dpm2.AboveSafeInc = 0;
+       table->initialState.levels[0].dpm2.BelowSafeInc = 0;
+       table->initialState.levels[0].dpm2.PwrEfficiencyRatio = 0;
+
+       reg = MIN_POWER_MASK | MAX_POWER_MASK;
+       table->initialState.levels[0].SQPowerThrottle = cpu_to_be32(reg);
+
+       reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+       table->initialState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg);
+
+       return 0;
+}
+
+static int si_populate_smc_acpi_state(struct radeon_device *rdev,
+                                     SISLANDS_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 spll_func_cntl = si_pi->clock_registers.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 = si_pi->clock_registers.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 = si_pi->clock_registers.cg_spll_func_cntl_3;
+       u32 spll_func_cntl_4 = si_pi->clock_registers.cg_spll_func_cntl_4;
+       u32 dll_cntl = si_pi->clock_registers.dll_cntl;
+       u32 mclk_pwrmgt_cntl = si_pi->clock_registers.mclk_pwrmgt_cntl;
+       u32 mpll_ad_func_cntl = si_pi->clock_registers.mpll_ad_func_cntl;
+       u32 mpll_dq_func_cntl = si_pi->clock_registers.mpll_dq_func_cntl;
+       u32 mpll_func_cntl = si_pi->clock_registers.mpll_func_cntl;
+       u32 mpll_func_cntl_1 = si_pi->clock_registers.mpll_func_cntl_1;
+       u32 mpll_func_cntl_2 = si_pi->clock_registers.mpll_func_cntl_2;
+       u32 reg;
+       int ret;
+
+       table->ACPIState = table->initialState;
+
+       table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       if (pi->acpi_vddc) {
+               ret = si_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                               pi->acpi_vddc, &table->ACPIState.levels[0].vddc);
+               if (!ret) {
+                       u16 std_vddc;
+
+                       ret = si_get_std_voltage_value(rdev,
+                                                      &table->ACPIState.levels[0].vddc, &std_vddc);
+                       if (!ret)
+                               si_populate_std_voltage_value(rdev, std_vddc,
+                                                             table->ACPIState.levels[0].vddc.index,
+                                                             &table->ACPIState.levels[0].std_vddc);
+               }
+               table->ACPIState.levels[0].gen2PCIE = si_pi->acpi_pcie_gen;
+
+               if (si_pi->vddc_phase_shed_control) {
+                       si_populate_phase_shedding_value(rdev,
+                                                        &rdev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                        pi->acpi_vddc,
+                                                        0,
+                                                        0,
+                                                        &table->ACPIState.levels[0].vddc);
+               }
+       } else {
+               ret = si_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                               pi->min_vddc_in_table, &table->ACPIState.levels[0].vddc);
+               if (!ret) {
+                       u16 std_vddc;
+
+                       ret = si_get_std_voltage_value(rdev,
+                                                      &table->ACPIState.levels[0].vddc, &std_vddc);
+
+                       if (!ret)
+                               si_populate_std_voltage_value(rdev, std_vddc,
+                                                             table->ACPIState.levels[0].vddc.index,
+                                                             &table->ACPIState.levels[0].std_vddc);
+               }
+               table->ACPIState.levels[0].gen2PCIE = (u8)r600_get_pcie_gen_support(rdev,
+                                                                                   si_pi->sys_pcie_mask,
+                                                                                   si_pi->boot_pcie_gen,
+                                                                                   RADEON_PCIE_GEN1);
+
+               if (si_pi->vddc_phase_shed_control)
+                       si_populate_phase_shedding_value(rdev,
+                                                        &rdev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                        pi->min_vddc_in_table,
+                                                        0,
+                                                        0,
+                                                        &table->ACPIState.levels[0].vddc);
+       }
+
+       if (pi->acpi_vddc) {
+               if (eg_pi->acpi_vddci)
+                       si_populate_voltage_value(rdev, &eg_pi->vddci_voltage_table,
+                                                 eg_pi->acpi_vddci,
+                                                 &table->ACPIState.levels[0].vddci);
+       }
+
+       mclk_pwrmgt_cntl |= MRDCK0_RESET | MRDCK1_RESET;
+       mclk_pwrmgt_cntl &= ~(MRDCK0_PDNB | MRDCK1_PDNB);
+
+       dll_cntl &= ~(MRDCK0_BYPASS | MRDCK1_BYPASS);
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(4);
+
+       table->ACPIState.levels[0].mclk.vDLL_CNTL =
+               cpu_to_be32(dll_cntl);
+       table->ACPIState.levels[0].mclk.vMCLK_PWRMGT_CNTL =
+               cpu_to_be32(mclk_pwrmgt_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_AD_FUNC_CNTL =
+               cpu_to_be32(mpll_ad_func_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL =
+               cpu_to_be32(mpll_dq_func_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL =
+               cpu_to_be32(mpll_func_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL_1 =
+               cpu_to_be32(mpll_func_cntl_1);
+       table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL_2 =
+               cpu_to_be32(mpll_func_cntl_2);
+       table->ACPIState.levels[0].mclk.vMPLL_SS =
+               cpu_to_be32(si_pi->clock_registers.mpll_ss1);
+       table->ACPIState.levels[0].mclk.vMPLL_SS2 =
+               cpu_to_be32(si_pi->clock_registers.mpll_ss2);
+
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
+               cpu_to_be32(spll_func_cntl);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+               cpu_to_be32(spll_func_cntl_2);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+               cpu_to_be32(spll_func_cntl_3);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 =
+               cpu_to_be32(spll_func_cntl_4);
+
+       table->ACPIState.levels[0].mclk.mclk_value = 0;
+       table->ACPIState.levels[0].sclk.sclk_value = 0;
+
+       si_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd);
+
+       if (eg_pi->dynamic_ac_timing)
+               table->ACPIState.levels[0].ACIndex = 0;
+
+       table->ACPIState.levels[0].dpm2.MaxPS = 0;
+       table->ACPIState.levels[0].dpm2.NearTDPDec = 0;
+       table->ACPIState.levels[0].dpm2.AboveSafeInc = 0;
+       table->ACPIState.levels[0].dpm2.BelowSafeInc = 0;
+       table->ACPIState.levels[0].dpm2.PwrEfficiencyRatio = 0;
+
+       reg = MIN_POWER_MASK | MAX_POWER_MASK;
+       table->ACPIState.levels[0].SQPowerThrottle = cpu_to_be32(reg);
+
+       reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+       table->ACPIState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg);
+
+       return 0;
+}
+
+static int si_populate_ulv_state(struct radeon_device *rdev,
+                                SISLANDS_SMC_SWSTATE *state)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       u32 sclk_in_sr = 1350; /* ??? */
+       int ret;
+
+       ret = si_convert_power_level_to_smc(rdev, &ulv->pl,
+                                           &state->levels[0]);
+       if (!ret) {
+               if (eg_pi->sclk_deep_sleep) {
+                       if (sclk_in_sr <= SCLK_MIN_DEEPSLEEP_FREQ)
+                               state->levels[0].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_BYPASS;
+                       else
+                               state->levels[0].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE;
+               }
+               if (ulv->one_pcie_lane_in_ulv)
+                       state->flags |= PPSMC_SWSTATE_FLAG_PCIE_X1;
+               state->levels[0].arbRefreshState = (u8)(SISLANDS_ULV_STATE_ARB_INDEX);
+               state->levels[0].ACIndex = 1;
+               state->levels[0].std_vddc = state->levels[0].vddc;
+               state->levelCount = 1;
+
+               state->flags |= PPSMC_SWSTATE_FLAG_DC;
+       }
+
+       return ret;
+}
+
+static int si_program_ulv_memory_timing_parameters(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       SMC_SIslands_MCArbDramTimingRegisterSet arb_regs = { 0 };
+       int ret;
+
+       ret = si_populate_memory_timing_parameters(rdev, &ulv->pl,
+                                                  &arb_regs);
+       if (ret)
+               return ret;
+
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_ulv_volt_change_delay,
+                                  ulv->volt_change_delay);
+
+       ret = si_copy_bytes_to_smc(rdev,
+                                  si_pi->arb_table_start +
+                                  offsetof(SMC_SIslands_MCArbDramTimingRegisters, data) +
+                                  sizeof(SMC_SIslands_MCArbDramTimingRegisterSet) * SISLANDS_ULV_STATE_ARB_INDEX,
+                                  (u8 *)&arb_regs,
+                                  sizeof(SMC_SIslands_MCArbDramTimingRegisterSet),
+                                  si_pi->sram_end);
+
+       return ret;
+}
+
+static void si_get_mvdd_configuration(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       pi->mvdd_split_frequency = 30000;
+}
+
+static int si_init_smc_table(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps;
+       const struct si_ulv_param *ulv = &si_pi->ulv;
+       SISLANDS_SMC_STATETABLE  *table = &si_pi->smc_statetable;
+       int ret;
+       u32 lane_width;
+       u32 vr_hot_gpio;
+
+       si_populate_smc_voltage_tables(rdev, table);
+
+       switch (rdev->pm.int_thermal_type) {
+       case THERMAL_TYPE_SI:
+       case THERMAL_TYPE_EMC2103_WITH_INTERNAL:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL;
+               break;
+       case THERMAL_TYPE_NONE:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE;
+               break;
+       default:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL;
+               break;
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT) {
+               if ((rdev->pdev->device != 0x6818) && (rdev->pdev->device != 0x6819))
+                       table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT;
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (pi->mem_gddr5)
+               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;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_VRHOT_GPIO_CONFIGURABLE) {
+               table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT_PROG_GPIO;
+               vr_hot_gpio = rdev->pm.dpm.backbias_response_time;
+               si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_vr_hot_gpio,
+                                          vr_hot_gpio);
+       }
+
+       ret = si_populate_smc_initial_state(rdev, radeon_boot_state, table);
+       if (ret)
+               return ret;
+
+       ret = si_populate_smc_acpi_state(rdev, table);
+       if (ret)
+               return ret;
+
+       table->driverState = table->initialState;
+
+       ret = si_do_program_memory_timing_parameters(rdev, radeon_boot_state,
+                                                    SISLANDS_INITIAL_STATE_ARB_INDEX);
+       if (ret)
+               return ret;
+
+       if (ulv->supported && ulv->pl.vddc) {
+               ret = si_populate_ulv_state(rdev, &table->ULVState);
+               if (ret)
+                       return ret;
+
+               ret = si_program_ulv_memory_timing_parameters(rdev);
+               if (ret)
+                       return ret;
+
+               WREG32(CG_ULV_CONTROL, ulv->cg_ulv_control);
+               WREG32(CG_ULV_PARAMETER, ulv->cg_ulv_parameter);
+
+               lane_width = radeon_get_pcie_lanes(rdev);
+               si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width, lane_width);
+       } else {
+               table->ULVState = table->initialState;
+       }
+
+       return si_copy_bytes_to_smc(rdev, si_pi->state_table_start,
+                                   (u8 *)table, sizeof(SISLANDS_SMC_STATETABLE),
+                                   si_pi->sram_end);
+}
+
+static int si_calculate_sclk_params(struct radeon_device *rdev,
+                                   u32 engine_clock,
+                                   SISLANDS_SMC_SCLK_VALUE *sclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct atom_clock_dividers dividers;
+       u32 spll_func_cntl = si_pi->clock_registers.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 = si_pi->clock_registers.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 = si_pi->clock_registers.cg_spll_func_cntl_3;
+       u32 spll_func_cntl_4 = si_pi->clock_registers.cg_spll_func_cntl_4;
+       u32 cg_spll_spread_spectrum = si_pi->clock_registers.cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2 = si_pi->clock_registers.cg_spll_spread_spectrum_2;
+       u64 tmp;
+       u32 reference_clock = rdev->clock.spll.reference_freq;
+       u32 reference_divider;
+       u32 fbdiv;
+       int ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            engine_clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       reference_divider = 1 + dividers.ref_div;
+
+       tmp = (u64) engine_clock * reference_divider * dividers.post_div * 16384;
+       do_div(tmp, reference_clock);
+       fbdiv = (u32) tmp;
+
+       spll_func_cntl &= ~(SPLL_PDIV_A_MASK | SPLL_REF_DIV_MASK);
+       spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div);
+       spll_func_cntl |= SPLL_PDIV_A(dividers.post_div);
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(2);
+
+        spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK;
+        spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv);
+        spll_func_cntl_3 |= SPLL_DITHEN;
+
+       if (pi->sclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 vco_freq = engine_clock * dividers.post_div;
+
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
+                       u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
+                       u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000);
+
+                       cg_spll_spread_spectrum &= ~CLK_S_MASK;
+                       cg_spll_spread_spectrum |= CLK_S(clk_s);
+                       cg_spll_spread_spectrum |= SSEN;
+
+                       cg_spll_spread_spectrum_2 &= ~CLK_V_MASK;
+                       cg_spll_spread_spectrum_2 |= CLK_V(clk_v);
+               }
+       }
+
+       sclk->sclk_value = engine_clock;
+       sclk->vCG_SPLL_FUNC_CNTL = spll_func_cntl;
+       sclk->vCG_SPLL_FUNC_CNTL_2 = spll_func_cntl_2;
+       sclk->vCG_SPLL_FUNC_CNTL_3 = spll_func_cntl_3;
+       sclk->vCG_SPLL_FUNC_CNTL_4 = spll_func_cntl_4;
+       sclk->vCG_SPLL_SPREAD_SPECTRUM = cg_spll_spread_spectrum;
+       sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cg_spll_spread_spectrum_2;
+
+       return 0;
+}
+
+static int si_populate_sclk_value(struct radeon_device *rdev,
+                                 u32 engine_clock,
+                                 SISLANDS_SMC_SCLK_VALUE *sclk)
+{
+       SISLANDS_SMC_SCLK_VALUE sclk_tmp;
+       int ret;
+
+       ret = si_calculate_sclk_params(rdev, engine_clock, &sclk_tmp);
+       if (!ret) {
+               sclk->sclk_value = cpu_to_be32(sclk_tmp.sclk_value);
+               sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL);
+               sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_2);
+               sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_3);
+               sclk->vCG_SPLL_FUNC_CNTL_4 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_4);
+               sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM);
+               sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM_2);
+       }
+
+       return ret;
+}
+
+static int si_populate_mclk_value(struct radeon_device *rdev,
+                                 u32 engine_clock,
+                                 u32 memory_clock,
+                                 SISLANDS_SMC_MCLK_VALUE *mclk,
+                                 bool strobe_mode,
+                                 bool dll_state_on)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32  dll_cntl = si_pi->clock_registers.dll_cntl;
+       u32  mclk_pwrmgt_cntl = si_pi->clock_registers.mclk_pwrmgt_cntl;
+       u32  mpll_ad_func_cntl = si_pi->clock_registers.mpll_ad_func_cntl;
+       u32  mpll_dq_func_cntl = si_pi->clock_registers.mpll_dq_func_cntl;
+       u32  mpll_func_cntl = si_pi->clock_registers.mpll_func_cntl;
+       u32  mpll_func_cntl_1 = si_pi->clock_registers.mpll_func_cntl_1;
+       u32  mpll_func_cntl_2 = si_pi->clock_registers.mpll_func_cntl_2;
+       u32  mpll_ss1 = si_pi->clock_registers.mpll_ss1;
+       u32  mpll_ss2 = si_pi->clock_registers.mpll_ss2;
+       struct atom_mpll_param mpll_param;
+       int ret;
+
+       ret = radeon_atom_get_memory_pll_dividers(rdev, memory_clock, strobe_mode, &mpll_param);
+       if (ret)
+               return ret;
+
+       mpll_func_cntl &= ~BWCTRL_MASK;
+       mpll_func_cntl |= BWCTRL(mpll_param.bwcntl);
+
+       mpll_func_cntl_1 &= ~(CLKF_MASK | CLKFRAC_MASK | VCO_MODE_MASK);
+       mpll_func_cntl_1 |= CLKF(mpll_param.clkf) |
+               CLKFRAC(mpll_param.clkfrac) | VCO_MODE(mpll_param.vco_mode);
+
+       mpll_ad_func_cntl &= ~YCLK_POST_DIV_MASK;
+       mpll_ad_func_cntl |= YCLK_POST_DIV(mpll_param.post_div);
+
+       if (pi->mem_gddr5) {
+               mpll_dq_func_cntl &= ~(YCLK_SEL_MASK | YCLK_POST_DIV_MASK);
+               mpll_dq_func_cntl |= YCLK_SEL(mpll_param.yclk_sel) |
+                       YCLK_POST_DIV(mpll_param.post_div);
+       }
+
+       if (pi->mclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 freq_nom;
+               u32 tmp;
+               u32 reference_clock = rdev->clock.mpll.reference_freq;
+
+               if (pi->mem_gddr5)
+                       freq_nom = memory_clock * 4;
+               else
+                       freq_nom = memory_clock * 2;
+
+               tmp = freq_nom / reference_clock;
+               tmp = tmp * tmp;
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                     ASIC_INTERNAL_MEMORY_SS, freq_nom)) {
+                       u32 clks = reference_clock * 5 / ss.rate;
+                       u32 clkv = (u32)((((131 * ss.percentage * ss.rate) / 100) * tmp) / freq_nom);
+
+                        mpll_ss1 &= ~CLKV_MASK;
+                        mpll_ss1 |= CLKV(clkv);
+
+                        mpll_ss2 &= ~CLKS_MASK;
+                        mpll_ss2 |= CLKS(clks);
+               }
+       }
+
+       mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK;
+       mclk_pwrmgt_cntl |= DLL_SPEED(mpll_param.dll_speed);
+
+       if (dll_state_on)
+               mclk_pwrmgt_cntl |= MRDCK0_PDNB | MRDCK1_PDNB;
+       else
+               mclk_pwrmgt_cntl &= ~(MRDCK0_PDNB | MRDCK1_PDNB);
+
+       mclk->mclk_value = cpu_to_be32(memory_clock);
+       mclk->vMPLL_FUNC_CNTL = cpu_to_be32(mpll_func_cntl);
+       mclk->vMPLL_FUNC_CNTL_1 = cpu_to_be32(mpll_func_cntl_1);
+       mclk->vMPLL_FUNC_CNTL_2 = cpu_to_be32(mpll_func_cntl_2);
+       mclk->vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
+       mclk->vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
+       mclk->vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+       mclk->vDLL_CNTL = cpu_to_be32(dll_cntl);
+       mclk->vMPLL_SS = cpu_to_be32(mpll_ss1);
+       mclk->vMPLL_SS2 = cpu_to_be32(mpll_ss2);
+
+       return 0;
+}
+
+static void si_populate_smc_sp(struct radeon_device *rdev,
+                              struct radeon_ps *radeon_state,
+                              SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct ni_ps *ps = ni_get_ps(radeon_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       int i;
+
+       for (i = 0; i < ps->performance_level_count - 1; i++)
+               smc_state->levels[i].bSP = cpu_to_be32(pi->dsp);
+
+       smc_state->levels[ps->performance_level_count - 1].bSP =
+               cpu_to_be32(pi->psp);
+}
+
+static int si_convert_power_level_to_smc(struct radeon_device *rdev,
+                                        struct rv7xx_pl *pl,
+                                        SISLANDS_SMC_HW_PERFORMANCE_LEVEL *level)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int ret;
+       bool dll_state_on;
+       u16 std_vddc;
+       bool gmc_pg = false;
+
+       if (eg_pi->pcie_performance_request &&
+           (si_pi->force_pcie_gen != RADEON_PCIE_GEN_INVALID))
+               level->gen2PCIE = (u8)si_pi->force_pcie_gen;
+       else
+               level->gen2PCIE = (u8)pl->pcie_gen;
+
+       ret = si_populate_sclk_value(rdev, pl->sclk, &level->sclk);
+       if (ret)
+               return ret;
+
+       level->mcFlags =  0;
+
+       if (pi->mclk_stutter_mode_threshold &&
+           (pl->mclk <= pi->mclk_stutter_mode_threshold) &&
+           !eg_pi->uvd_enabled &&
+           (RREG32(DPG_PIPE_STUTTER_CONTROL) & STUTTER_ENABLE) &&
+           (rdev->pm.dpm.new_active_crtc_count <= 2)) {
+               level->mcFlags |= SISLANDS_SMC_MC_STUTTER_EN;
+
+               if (gmc_pg)
+                       level->mcFlags |= SISLANDS_SMC_MC_PG_EN;
+       }
+
+       if (pi->mem_gddr5) {
+               if (pl->mclk > pi->mclk_edc_enable_threshold)
+                       level->mcFlags |= SISLANDS_SMC_MC_EDC_RD_FLAG;
+
+               if (pl->mclk > eg_pi->mclk_edc_wr_enable_threshold)
+                       level->mcFlags |= SISLANDS_SMC_MC_EDC_WR_FLAG;
+
+               level->strobeMode = si_get_strobe_mode_settings(rdev, pl->mclk);
+
+               if (level->strobeMode & SISLANDS_SMC_STROBE_ENABLE) {
+                       if (si_get_mclk_frequency_ratio(pl->mclk, true) >=
+                           ((RREG32(MC_SEQ_MISC7) >> 16) & 0xf))
+                               dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
+                       else
+                               dll_state_on = ((RREG32(MC_SEQ_MISC6) >> 1) & 0x1) ? true : false;
+               } else {
+                       dll_state_on = false;
+               }
+       } else {
+               level->strobeMode = si_get_strobe_mode_settings(rdev,
+                                                               pl->mclk);
+
+               dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
+       }
+
+       ret = si_populate_mclk_value(rdev,
+                                    pl->sclk,
+                                    pl->mclk,
+                                    &level->mclk,
+                                    (level->strobeMode & SISLANDS_SMC_STROBE_ENABLE) != 0, dll_state_on);
+       if (ret)
+               return ret;
+
+       ret = si_populate_voltage_value(rdev,
+                                       &eg_pi->vddc_voltage_table,
+                                       pl->vddc, &level->vddc);
+       if (ret)
+               return ret;
+
+
+       ret = si_get_std_voltage_value(rdev, &level->vddc, &std_vddc);
+       if (ret)
+               return ret;
+
+       ret = si_populate_std_voltage_value(rdev, std_vddc,
+                                           level->vddc.index, &level->std_vddc);
+       if (ret)
+               return ret;
+
+       if (eg_pi->vddci_control) {
+               ret = si_populate_voltage_value(rdev, &eg_pi->vddci_voltage_table,
+                                               pl->vddci, &level->vddci);
+               if (ret)
+                       return ret;
+       }
+
+       if (si_pi->vddc_phase_shed_control) {
+               ret = si_populate_phase_shedding_value(rdev,
+                                                      &rdev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                      pl->vddc,
+                                                      pl->sclk,
+                                                      pl->mclk,
+                                                      &level->vddc);
+               if (ret)
+                       return ret;
+       }
+
+       level->MaxPoweredUpCU = si_pi->max_cu;
+
+       ret = si_populate_mvdd_value(rdev, pl->mclk, &level->mvdd);
+
+       return ret;
+}
+
+static int si_populate_smc_t(struct radeon_device *rdev,
+                            struct radeon_ps *radeon_state,
+                            SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       u32 a_t;
+       u32 t_l, t_h;
+       u32 high_bsp;
+       int i, ret;
+
+       if (state->performance_level_count >= 9)
+               return -EINVAL;
+
+       if (state->performance_level_count < 2) {
+               a_t = CG_R(0xffff) | CG_L(0);
+               smc_state->levels[0].aT = cpu_to_be32(a_t);
+               return 0;
+       }
+
+       smc_state->levels[0].aT = cpu_to_be32(0);
+
+       for (i = 0; i <= state->performance_level_count - 2; i++) {
+               ret = r600_calculate_at(
+                       (50 / SISLANDS_MAX_HARDWARE_POWERLEVELS) * 100 * (i + 1),
+                       100 * R600_AH_DFLT,
+                       state->performance_levels[i + 1].sclk,
+                       state->performance_levels[i].sclk,
+                       &t_l,
+                       &t_h);
+
+               if (ret) {
+                       t_h = (i + 1) * 1000 - 50 * R600_AH_DFLT;
+                       t_l = (i + 1) * 1000 + 50 * R600_AH_DFLT;
+               }
+
+               a_t = be32_to_cpu(smc_state->levels[i].aT) & ~CG_R_MASK;
+               a_t |= CG_R(t_l * pi->bsp / 20000);
+               smc_state->levels[i].aT = cpu_to_be32(a_t);
+
+               high_bsp = (i == state->performance_level_count - 2) ?
+                       pi->pbsp : pi->bsp;
+               a_t = CG_R(0xffff) | CG_L(t_h * high_bsp / 20000);
+               smc_state->levels[i + 1].aT = cpu_to_be32(a_t);
+       }
+
+       return 0;
+}
+
+static int si_disable_ulv(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+
+       if (ulv->supported)
+               return (si_send_msg_to_smc(rdev, PPSMC_MSG_DisableULV) == PPSMC_Result_OK) ?
+                       0 : -EINVAL;
+
+       return 0;
+}
+
+static bool si_is_state_ulv_compatible(struct radeon_device *rdev,
+                                      struct radeon_ps *radeon_state)
+{
+       const struct si_power_info *si_pi = si_get_pi(rdev);
+       const struct si_ulv_param *ulv = &si_pi->ulv;
+       const struct ni_ps *state = ni_get_ps(radeon_state);
+       int i;
+
+       if (state->performance_levels[0].mclk != ulv->pl.mclk)
+               return false;
+
+       /* XXX validate against display requirements! */
+
+       for (i = 0; i < rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.count; i++) {
+               if (rdev->clock.current_dispclk <=
+                   rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[i].clk) {
+                       if (ulv->pl.vddc <
+                           rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[i].v)
+                               return false;
+               }
+       }
+
+       if ((radeon_state->vclk != 0) || (radeon_state->dclk != 0))
+               return false;
+
+       return true;
+}
+
+static int si_set_power_state_conditionally_enable_ulv(struct radeon_device *rdev,
+                                                      struct radeon_ps *radeon_new_state)
+{
+       const struct si_power_info *si_pi = si_get_pi(rdev);
+       const struct si_ulv_param *ulv = &si_pi->ulv;
+
+       if (ulv->supported) {
+               if (si_is_state_ulv_compatible(rdev, radeon_new_state))
+                       return (si_send_msg_to_smc(rdev, PPSMC_MSG_EnableULV) == PPSMC_Result_OK) ?
+                               0 : -EINVAL;
+       }
+       return 0;
+}
+
+static int si_convert_power_state_to_smc(struct radeon_device *rdev,
+                                        struct radeon_ps *radeon_state,
+                                        SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       int i, ret;
+       u32 threshold;
+       u32 sclk_in_sr = 1350; /* ??? */
+
+       if (state->performance_level_count > SISLANDS_MAX_HARDWARE_POWERLEVELS)
+               return -EINVAL;
+
+       threshold = state->performance_levels[state->performance_level_count-1].sclk * 100 / 100;
+
+       if (radeon_state->vclk && radeon_state->dclk) {
+               eg_pi->uvd_enabled = true;
+               if (eg_pi->smu_uvd_hs)
+                       smc_state->flags |= PPSMC_SWSTATE_FLAG_UVD;
+       } else {
+               eg_pi->uvd_enabled = false;
+       }
+
+       if (state->dc_compatible)
+               smc_state->flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       smc_state->levelCount = 0;
+       for (i = 0; i < state->performance_level_count; i++) {
+               if (eg_pi->sclk_deep_sleep) {
+                       if ((i == 0) || si_pi->sclk_deep_sleep_above_low) {
+                               if (sclk_in_sr <= SCLK_MIN_DEEPSLEEP_FREQ)
+                                       smc_state->levels[i].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_BYPASS;
+                               else
+                                       smc_state->levels[i].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE;
+                       }
+               }
+
+               ret = si_convert_power_level_to_smc(rdev, &state->performance_levels[i],
+                                                   &smc_state->levels[i]);
+               smc_state->levels[i].arbRefreshState =
+                       (u8)(SISLANDS_DRIVER_STATE_ARB_INDEX + i);
+
+               if (ret)
+                       return ret;
+
+               if (ni_pi->enable_power_containment)
+                       smc_state->levels[i].displayWatermark =
+                               (state->performance_levels[i].sclk < threshold) ?
+                               PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH;
+               else
+                       smc_state->levels[i].displayWatermark = (i < 2) ?
+                               PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH;
+
+               if (eg_pi->dynamic_ac_timing)
+                       smc_state->levels[i].ACIndex = SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i;
+               else
+                       smc_state->levels[i].ACIndex = 0;
+
+               smc_state->levelCount++;
+       }
+
+       si_write_smc_soft_register(rdev,
+                                  SI_SMC_SOFT_REGISTER_watermark_threshold,
+                                  threshold / 512);
+
+       si_populate_smc_sp(rdev, radeon_state, smc_state);
+
+       ret = si_populate_power_containment_values(rdev, radeon_state, smc_state);
+       if (ret)
+               ni_pi->enable_power_containment = false;
+
+       ret = si_populate_sq_ramping_values(rdev, radeon_state, smc_state);
+        if (ret)
+               ni_pi->enable_sq_ramping = false;
+
+       return si_populate_smc_t(rdev, radeon_state, smc_state);
+}
+
+static int si_upload_sw_state(struct radeon_device *rdev,
+                             struct radeon_ps *radeon_new_state)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct ni_ps *new_state = ni_get_ps(radeon_new_state);
+       int ret;
+       u32 address = si_pi->state_table_start +
+               offsetof(SISLANDS_SMC_STATETABLE, driverState);
+       u32 state_size = sizeof(SISLANDS_SMC_SWSTATE) +
+               ((new_state->performance_level_count - 1) *
+                sizeof(SISLANDS_SMC_HW_PERFORMANCE_LEVEL));
+       SISLANDS_SMC_SWSTATE *smc_state = &si_pi->smc_statetable.driverState;
+
+       memset(smc_state, 0, state_size);
+
+       ret = si_convert_power_state_to_smc(rdev, radeon_new_state, smc_state);
+       if (ret)
+               return ret;
+
+       ret = si_copy_bytes_to_smc(rdev, address, (u8 *)smc_state,
+                                  state_size, si_pi->sram_end);
+
+       return ret;
+}
+
+static int si_upload_ulv_state(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       int ret = 0;
+
+       if (ulv->supported && ulv->pl.vddc) {
+               u32 address = si_pi->state_table_start +
+                       offsetof(SISLANDS_SMC_STATETABLE, ULVState);
+               SISLANDS_SMC_SWSTATE *smc_state = &si_pi->smc_statetable.ULVState;
+               u32 state_size = sizeof(SISLANDS_SMC_SWSTATE);
+
+               memset(smc_state, 0, state_size);
+
+               ret = si_populate_ulv_state(rdev, smc_state);
+               if (!ret)
+                       ret = si_copy_bytes_to_smc(rdev, address, (u8 *)smc_state,
+                                                  state_size, si_pi->sram_end);
+       }
+
+       return ret;
+}
+
+static int si_upload_smc_data(struct radeon_device *rdev)
+{
+       struct radeon_crtc *radeon_crtc = NULL;
+       int i;
+
+       if (rdev->pm.dpm.new_active_crtc_count == 0)
+               return 0;
+
+       for (i = 0; i < rdev->num_crtc; i++) {
+               if (rdev->pm.dpm.new_active_crtcs & (1 << i)) {
+                       radeon_crtc = rdev->mode_info.crtcs[i];
+                       break;
+               }
+       }
+
+       if (radeon_crtc == NULL)
+               return 0;
+
+       if (radeon_crtc->line_time <= 0)
+               return 0;
+
+       if (si_write_smc_soft_register(rdev,
+                                      SI_SMC_SOFT_REGISTER_crtc_index,
+                                      radeon_crtc->crtc_id) != PPSMC_Result_OK)
+               return 0;
+
+       if (si_write_smc_soft_register(rdev,
+                                      SI_SMC_SOFT_REGISTER_mclk_change_block_cp_min,
+                                      radeon_crtc->wm_high / radeon_crtc->line_time) != PPSMC_Result_OK)
+               return 0;
+
+       if (si_write_smc_soft_register(rdev,
+                                      SI_SMC_SOFT_REGISTER_mclk_change_block_cp_max,
+                                      radeon_crtc->wm_low / radeon_crtc->line_time) != PPSMC_Result_OK)
+               return 0;
+
+       return 0;
+}
+
+static int si_set_mc_special_registers(struct radeon_device *rdev,
+                                      struct si_mc_reg_table *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u8 i, j, k;
+       u32 temp_reg;
+
+       for (i = 0, j = table->last; i < table->last; i++) {
+               if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                       return -EINVAL;
+               switch (table->mc_reg_address[i].s1 << 2) {
+               case MC_SEQ_MISC1:
+                       temp_reg = RREG32(MC_PMG_CMD_EMRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_EMRS >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++)
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       ((temp_reg & 0xffff0000)) |
+                                       ((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16);
+                       j++;
+                       if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+
+                       temp_reg = RREG32(MC_PMG_CMD_MRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++) {
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       (temp_reg & 0xffff0000) |
+                                       (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+                               if (!pi->mem_gddr5)
+                                       table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+                       }
+                       j++;
+                       if (j > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+
+                       if (!pi->mem_gddr5) {
+                               table->mc_reg_address[j].s1 = MC_PMG_AUTO_CMD >> 2;
+                               table->mc_reg_address[j].s0 = MC_PMG_AUTO_CMD >> 2;
+                               for (k = 0; k < table->num_entries; k++)
+                                       table->mc_reg_table_entry[k].mc_data[j] =
+                                               (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
+                               j++;
+                               if (j > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                                       return -EINVAL;
+                       }
+                       break;
+               case MC_SEQ_RESERVE_M:
+                       temp_reg = RREG32(MC_PMG_CMD_MRS1);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS1 >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
+                       for(k = 0; k < table->num_entries; k++)
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       (temp_reg & 0xffff0000) |
+                                       (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+                       j++;
+                       if (j > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       table->last = j;
+
+       return 0;
+}
+
+static bool si_check_s0_mc_reg_index(u16 in_reg, u16 *out_reg)
+{
+       bool result = true;
+
+       switch (in_reg) {
+       case  MC_SEQ_RAS_TIMING >> 2:
+               *out_reg = MC_SEQ_RAS_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_CAS_TIMING >> 2:
+               *out_reg = MC_SEQ_CAS_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_MISC_TIMING >> 2:
+               *out_reg = MC_SEQ_MISC_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_MISC_TIMING2 >> 2:
+               *out_reg = MC_SEQ_MISC_TIMING2_LP >> 2;
+               break;
+        case MC_SEQ_RD_CTL_D0 >> 2:
+               *out_reg = MC_SEQ_RD_CTL_D0_LP >> 2;
+               break;
+        case MC_SEQ_RD_CTL_D1 >> 2:
+               *out_reg = MC_SEQ_RD_CTL_D1_LP >> 2;
+               break;
+        case MC_SEQ_WR_CTL_D0 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_D0_LP >> 2;
+               break;
+        case MC_SEQ_WR_CTL_D1 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_D1_LP >> 2;
+               break;
+        case MC_PMG_CMD_EMRS >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
+               break;
+        case MC_PMG_CMD_MRS >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS_LP >> 2;
+               break;
+        case MC_PMG_CMD_MRS1 >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
+               break;
+        case MC_SEQ_PMG_TIMING >> 2:
+               *out_reg = MC_SEQ_PMG_TIMING_LP >> 2;
+               break;
+        case MC_PMG_CMD_MRS2 >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS2_LP >> 2;
+               break;
+        case MC_SEQ_WR_CTL_2 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_2_LP >> 2;
+               break;
+        default:
+               result = false;
+               break;
+       }
+
+       return result;
+}
+
+static void si_set_valid_flag(struct si_mc_reg_table *table)
+{
+       u8 i, j;
+
+       for (i = 0; i < table->last; i++) {
+               for (j = 1; j < table->num_entries; j++) {
+                       if (table->mc_reg_table_entry[j-1].mc_data[i] != table->mc_reg_table_entry[j].mc_data[i]) {
+                               table->valid_flag |= 1 << i;
+                               break;
+                       }
+               }
+       }
+}
+
+static void si_set_s0_mc_reg_index(struct si_mc_reg_table *table)
+{
+       u32 i;
+       u16 address;
+
+       for (i = 0; i < table->last; i++)
+               table->mc_reg_address[i].s0 = si_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address) ?
+                       address : table->mc_reg_address[i].s1;
+
+}
+
+static int si_copy_vbios_mc_reg_table(struct atom_mc_reg_table *table,
+                                     struct si_mc_reg_table *si_table)
+{
+       u8 i, j;
+
+       if (table->last > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+               return -EINVAL;
+       if (table->num_entries > MAX_AC_TIMING_ENTRIES)
+               return -EINVAL;
+
+       for (i = 0; i < table->last; i++)
+               si_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1;
+       si_table->last = table->last;
+
+       for (i = 0; i < table->num_entries; i++) {
+               si_table->mc_reg_table_entry[i].mclk_max =
+                       table->mc_reg_table_entry[i].mclk_max;
+               for (j = 0; j < table->last; j++) {
+                       si_table->mc_reg_table_entry[i].mc_data[j] =
+                               table->mc_reg_table_entry[i].mc_data[j];
+               }
+       }
+       si_table->num_entries = table->num_entries;
+
+       return 0;
+}
+
+static int si_initialize_mc_reg_table(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct atom_mc_reg_table *table;
+       struct si_mc_reg_table *si_table = &si_pi->mc_reg_table;
+       u8 module_index = rv770_get_memory_module_index(rdev);
+       int ret;
+
+       table = kzalloc(sizeof(struct atom_mc_reg_table), GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
+
+       WREG32(MC_SEQ_RAS_TIMING_LP, RREG32(MC_SEQ_RAS_TIMING));
+       WREG32(MC_SEQ_CAS_TIMING_LP, RREG32(MC_SEQ_CAS_TIMING));
+       WREG32(MC_SEQ_MISC_TIMING_LP, RREG32(MC_SEQ_MISC_TIMING));
+       WREG32(MC_SEQ_MISC_TIMING2_LP, RREG32(MC_SEQ_MISC_TIMING2));
+       WREG32(MC_SEQ_PMG_CMD_EMRS_LP, RREG32(MC_PMG_CMD_EMRS));
+       WREG32(MC_SEQ_PMG_CMD_MRS_LP, RREG32(MC_PMG_CMD_MRS));
+       WREG32(MC_SEQ_PMG_CMD_MRS1_LP, RREG32(MC_PMG_CMD_MRS1));
+       WREG32(MC_SEQ_WR_CTL_D0_LP, RREG32(MC_SEQ_WR_CTL_D0));
+       WREG32(MC_SEQ_WR_CTL_D1_LP, RREG32(MC_SEQ_WR_CTL_D1));
+       WREG32(MC_SEQ_RD_CTL_D0_LP, RREG32(MC_SEQ_RD_CTL_D0));
+       WREG32(MC_SEQ_RD_CTL_D1_LP, RREG32(MC_SEQ_RD_CTL_D1));
+       WREG32(MC_SEQ_PMG_TIMING_LP, RREG32(MC_SEQ_PMG_TIMING));
+       WREG32(MC_SEQ_PMG_CMD_MRS2_LP, RREG32(MC_PMG_CMD_MRS2));
+       WREG32(MC_SEQ_WR_CTL_2_LP, RREG32(MC_SEQ_WR_CTL_2));
+
+        ret = radeon_atom_init_mc_reg_table(rdev, module_index, table);
+        if (ret)
+                goto init_mc_done;
+
+        ret = si_copy_vbios_mc_reg_table(table, si_table);
+        if (ret)
+                goto init_mc_done;
+
+       si_set_s0_mc_reg_index(si_table);
+
+       ret = si_set_mc_special_registers(rdev, si_table);
+        if (ret)
+                goto init_mc_done;
+
+       si_set_valid_flag(si_table);
+
+init_mc_done:
+       kfree(table);
+
+       return ret;
+
+}
+
+static void si_populate_mc_reg_addresses(struct radeon_device *rdev,
+                                        SMC_SIslands_MCRegisters *mc_reg_table)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 i, j;
+
+       for (i = 0, j = 0; j < si_pi->mc_reg_table.last; j++) {
+               if (si_pi->mc_reg_table.valid_flag & (1 << j)) {
+                       if (i >= SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               break;
+                       mc_reg_table->address[i].s0 =
+                               cpu_to_be16(si_pi->mc_reg_table.mc_reg_address[j].s0);
+                       mc_reg_table->address[i].s1 =
+                               cpu_to_be16(si_pi->mc_reg_table.mc_reg_address[j].s1);
+                       i++;
+               }
+       }
+       mc_reg_table->last = (u8)i;
+}
+
+static void si_convert_mc_registers(const struct si_mc_reg_entry *entry,
+                                   SMC_SIslands_MCRegisterSet *data,
+                                   u32 num_entries, u32 valid_flag)
+{
+       u32 i, j;
+
+       for(i = 0, j = 0; j < num_entries; j++) {
+               if (valid_flag & (1 << j)) {
+                       data->value[i] = cpu_to_be32(entry->mc_data[j]);
+                       i++;
+               }
+       }
+}
+
+static void si_convert_mc_reg_table_entry_to_smc(struct radeon_device *rdev,
+                                                struct rv7xx_pl *pl,
+                                                SMC_SIslands_MCRegisterSet *mc_reg_table_data)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 i = 0;
+
+       for (i = 0; i < si_pi->mc_reg_table.num_entries; i++) {
+               if (pl->mclk <= si_pi->mc_reg_table.mc_reg_table_entry[i].mclk_max)
+                       break;
+       }
+
+       if ((i == si_pi->mc_reg_table.num_entries) && (i > 0))
+               --i;
+
+       si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[i],
+                               mc_reg_table_data, si_pi->mc_reg_table.last,
+                               si_pi->mc_reg_table.valid_flag);
+}
+
+static void si_convert_mc_reg_table_to_smc(struct radeon_device *rdev,
+                                          struct radeon_ps *radeon_state,
+                                          SMC_SIslands_MCRegisters *mc_reg_table)
+{
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       int i;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               si_convert_mc_reg_table_entry_to_smc(rdev,
+                                                    &state->performance_levels[i],
+                                                    &mc_reg_table->data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i]);
+       }
+}
+
+static int si_populate_mc_reg_table(struct radeon_device *rdev,
+                                   struct radeon_ps *radeon_boot_state)
+{
+       struct ni_ps *boot_state = ni_get_ps(radeon_boot_state);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       SMC_SIslands_MCRegisters *smc_mc_reg_table = &si_pi->smc_mc_reg_table;
+
+       memset(smc_mc_reg_table, 0, sizeof(SMC_SIslands_MCRegisters));
+
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_seq_index, 1);
+
+       si_populate_mc_reg_addresses(rdev, smc_mc_reg_table);
+
+       si_convert_mc_reg_table_entry_to_smc(rdev, &boot_state->performance_levels[0],
+                                            &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_INITIAL_SLOT]);
+
+       si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[0],
+                               &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ACPI_SLOT],
+                               si_pi->mc_reg_table.last,
+                               si_pi->mc_reg_table.valid_flag);
+
+       if (ulv->supported && ulv->pl.vddc != 0)
+               si_convert_mc_reg_table_entry_to_smc(rdev, &ulv->pl,
+                                                    &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ULV_SLOT]);
+       else
+               si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[0],
+                                       &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ULV_SLOT],
+                                       si_pi->mc_reg_table.last,
+                                       si_pi->mc_reg_table.valid_flag);
+
+       si_convert_mc_reg_table_to_smc(rdev, radeon_boot_state, smc_mc_reg_table);
+
+       return si_copy_bytes_to_smc(rdev, si_pi->mc_reg_table_start,
+                                   (u8 *)smc_mc_reg_table,
+                                   sizeof(SMC_SIslands_MCRegisters), si_pi->sram_end);
+}
+
+static int si_upload_mc_reg_table(struct radeon_device *rdev,
+                                 struct radeon_ps *radeon_new_state)
+{
+       struct ni_ps *new_state = ni_get_ps(radeon_new_state);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 address = si_pi->mc_reg_table_start +
+               offsetof(SMC_SIslands_MCRegisters,
+                        data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT]);
+       SMC_SIslands_MCRegisters *smc_mc_reg_table = &si_pi->smc_mc_reg_table;
+
+       memset(smc_mc_reg_table, 0, sizeof(SMC_SIslands_MCRegisters));
+
+       si_convert_mc_reg_table_to_smc(rdev, radeon_new_state, smc_mc_reg_table);
+
+
+       return si_copy_bytes_to_smc(rdev, address,
+                                   (u8 *)&smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT],
+                                   sizeof(SMC_SIslands_MCRegisterSet) * new_state->performance_level_count,
+                                   si_pi->sram_end);
+
+}
+
+static void si_enable_voltage_control(struct radeon_device *rdev, bool enable)
+{
+        if (enable)
+                WREG32_P(GENERAL_PWRMGT, VOLT_PWRMGT_EN, ~VOLT_PWRMGT_EN);
+        else
+                WREG32_P(GENERAL_PWRMGT, 0, ~VOLT_PWRMGT_EN);
+}
+
+static enum radeon_pcie_gen si_get_maximum_link_speed(struct radeon_device *rdev,
+                                                     struct radeon_ps *radeon_state)
+{
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       int i;
+       u16 pcie_speed, max_speed = 0;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               pcie_speed = state->performance_levels[i].pcie_gen;
+               if (max_speed < pcie_speed)
+                       max_speed = pcie_speed;
+       }
+       return max_speed;
+}
+
+static u16 si_get_current_pcie_speed(struct radeon_device *rdev)
+{
+       u32 speed_cntl;
+
+       speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL) & LC_CURRENT_DATA_RATE_MASK;
+       speed_cntl >>= LC_CURRENT_DATA_RATE_SHIFT;
+
+       return (u16)speed_cntl;
+}
+
+static void si_request_link_speed_change_before_state_change(struct radeon_device *rdev,
+                                                            struct radeon_ps *radeon_new_state,
+                                                            struct radeon_ps *radeon_current_state)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       enum radeon_pcie_gen target_link_speed = si_get_maximum_link_speed(rdev, radeon_new_state);
+       enum radeon_pcie_gen current_link_speed;
+
+       if (si_pi->force_pcie_gen == RADEON_PCIE_GEN_INVALID)
+               current_link_speed = si_get_maximum_link_speed(rdev, radeon_current_state);
+       else
+               current_link_speed = si_pi->force_pcie_gen;
+
+       si_pi->force_pcie_gen = RADEON_PCIE_GEN_INVALID;
+       si_pi->pspp_notify_required = false;
+       if (target_link_speed > current_link_speed) {
+               switch (target_link_speed) {
+#if defined(CONFIG_ACPI)
+               case RADEON_PCIE_GEN3:
+                       if (radeon_acpi_pcie_performance_request(rdev, PCIE_PERF_REQ_PECI_GEN3, false) == 0)
+                               break;
+                       si_pi->force_pcie_gen = RADEON_PCIE_GEN2;
+                       if (current_link_speed == RADEON_PCIE_GEN2)
+                               break;
+               case RADEON_PCIE_GEN2:
+                       if (radeon_acpi_pcie_performance_request(rdev, PCIE_PERF_REQ_PECI_GEN2, false) == 0)
+                               break;
+#endif
+               default:
+                       si_pi->force_pcie_gen = si_get_current_pcie_speed(rdev);
+                       break;
+               }
+       } else {
+               if (target_link_speed < current_link_speed)
+                       si_pi->pspp_notify_required = true;
+       }
+}
+
+static void si_notify_link_speed_change_after_state_change(struct radeon_device *rdev,
+                                                          struct radeon_ps *radeon_new_state,
+                                                          struct radeon_ps *radeon_current_state)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       enum radeon_pcie_gen target_link_speed = si_get_maximum_link_speed(rdev, radeon_new_state);
+       u8 request;
+
+       if (si_pi->pspp_notify_required) {
+               if (target_link_speed == RADEON_PCIE_GEN3)
+                       request = PCIE_PERF_REQ_PECI_GEN3;
+               else if (target_link_speed == RADEON_PCIE_GEN2)
+                       request = PCIE_PERF_REQ_PECI_GEN2;
+               else
+                       request = PCIE_PERF_REQ_PECI_GEN1;
+
+               if ((request == PCIE_PERF_REQ_PECI_GEN1) &&
+                   (si_get_current_pcie_speed(rdev) > 0))
+                       return;
+
+#if defined(CONFIG_ACPI)
+               radeon_acpi_pcie_performance_request(rdev, request, false);
+#endif
+       }
+}
+
+#if 0
+static int si_ds_request(struct radeon_device *rdev,
+                        bool ds_status_on, u32 count_write)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       if (eg_pi->sclk_deep_sleep) {
+               if (ds_status_on)
+                       return (si_send_msg_to_smc(rdev, PPSMC_MSG_CancelThrottleOVRDSCLKDS) ==
+                               PPSMC_Result_OK) ?
+                               0 : -EINVAL;
+               else
+                       return (si_send_msg_to_smc(rdev, PPSMC_MSG_ThrottleOVRDSCLKDS) ==
+                               PPSMC_Result_OK) ? 0 : -EINVAL;
+       }
+       return 0;
+}
+#endif
+
+static void si_set_max_cu_value(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (rdev->family == CHIP_VERDE) {
+               switch (rdev->pdev->device) {
+               case 0x6820:
+               case 0x6825:
+               case 0x6821:
+               case 0x6823:
+               case 0x6827:
+                       si_pi->max_cu = 10;
+                       break;
+               case 0x682D:
+               case 0x6824:
+               case 0x682F:
+               case 0x6826:
+                       si_pi->max_cu = 8;
+                       break;
+               case 0x6828:
+               case 0x6830:
+               case 0x6831:
+               case 0x6838:
+               case 0x6839:
+               case 0x683D:
+                       si_pi->max_cu = 10;
+                       break;
+               case 0x683B:
+               case 0x683F:
+               case 0x6829:
+                       si_pi->max_cu = 8;
+                       break;
+               default:
+                       si_pi->max_cu = 0;
+                       break;
+               }
+       } else {
+               si_pi->max_cu = 0;
+       }
+}
+
+static int si_patch_single_dependency_table_based_on_leakage(struct radeon_device *rdev,
+                                                            struct radeon_clock_voltage_dependency_table *table)
+{
+       u32 i;
+       int j;
+       u16 leakage_voltage;
+
+       if (table) {
+               for (i = 0; i < table->count; i++) {
+                       switch (si_get_leakage_voltage_from_leakage_index(rdev,
+                                                                         table->entries[i].v,
+                                                                         &leakage_voltage)) {
+                       case 0:
+                               table->entries[i].v = leakage_voltage;
+                               break;
+                       case -EAGAIN:
+                               return -EINVAL;
+                       case -EINVAL:
+                       default:
+                               break;
+                       }
+               }
+
+               for (j = (table->count - 2); j >= 0; j--) {
+                       table->entries[j].v = (table->entries[j].v <= table->entries[j + 1].v) ?
+                               table->entries[j].v : table->entries[j + 1].v;
+               }
+       }
+       return 0;
+}
+
+static int si_patch_dependency_tables_based_on_leakage(struct radeon_device *rdev)
+{
+       int ret = 0;
+
+       ret = si_patch_single_dependency_table_based_on_leakage(rdev,
+                                                               &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk);
+       ret = si_patch_single_dependency_table_based_on_leakage(rdev,
+                                                               &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk);
+       ret = si_patch_single_dependency_table_based_on_leakage(rdev,
+                                                               &rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk);
+       return ret;
+}
+
+static void si_set_pcie_lane_width_in_smc(struct radeon_device *rdev,
+                                         struct radeon_ps *radeon_new_state,
+                                         struct radeon_ps *radeon_current_state)
+{
+       u32 lane_width;
+       u32 new_lane_width =
+               (radeon_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+       u32 current_lane_width =
+               (radeon_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+
+       if (new_lane_width != current_lane_width) {
+               radeon_set_pcie_lanes(rdev, new_lane_width);
+               lane_width = radeon_get_pcie_lanes(rdev);
+               si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width, lane_width);
+       }
+}
+
+void si_dpm_setup_asic(struct radeon_device *rdev)
+{
+       rv770_get_memory_type(rdev);
+       si_read_clock_registers(rdev);
+       si_enable_acpi_power_management(rdev);
+}
+
+static int si_set_thermal_temperature_range(struct radeon_device *rdev,
+                                       int min_temp, int max_temp)
+{
+       int low_temp = 0 * 1000;
+       int high_temp = 255 * 1000;
+
+       if (low_temp < min_temp)
+               low_temp = min_temp;
+       if (high_temp > max_temp)
+               high_temp = max_temp;
+       if (high_temp < low_temp) {
+               DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp);
+               return -EINVAL;
+       }
+
+       WREG32_P(CG_THERMAL_INT, DIG_THERM_INTH(high_temp / 1000), ~DIG_THERM_INTH_MASK);
+       WREG32_P(CG_THERMAL_INT, DIG_THERM_INTL(low_temp / 1000), ~DIG_THERM_INTL_MASK);
+       WREG32_P(CG_THERMAL_CTRL, DIG_THERM_DPM(high_temp / 1000), ~DIG_THERM_DPM_MASK);
+
+       rdev->pm.dpm.thermal.min_temp = low_temp;
+       rdev->pm.dpm.thermal.max_temp = high_temp;
+
+       return 0;
+}
+
+int si_dpm_enable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+       int ret;
+
+       if (si_is_smc_running(rdev))
+               return -EINVAL;
+       if (pi->voltage_control)
+               si_enable_voltage_control(rdev, true);
+       if (pi->mvdd_control)
+               si_get_mvdd_configuration(rdev);
+       if (pi->voltage_control) {
+               ret = si_construct_voltage_tables(rdev);
+               if (ret) {
+                       DRM_ERROR("si_construct_voltage_tables failed\n");
+                       return ret;
+               }
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = si_initialize_mc_reg_table(rdev);
+               if (ret)
+                       eg_pi->dynamic_ac_timing = false;
+       }
+       if (pi->dynamic_ss)
+               si_enable_spread_spectrum(rdev, true);
+       if (pi->thermal_protection)
+               si_enable_thermal_protection(rdev, true);
+       si_setup_bsp(rdev);
+       si_program_git(rdev);
+       si_program_tp(rdev);
+       si_program_tpp(rdev);
+       si_program_sstp(rdev);
+       si_enable_display_gap(rdev);
+       si_program_vc(rdev);
+       ret = si_upload_firmware(rdev);
+       if (ret) {
+               DRM_ERROR("si_upload_firmware failed\n");
+               return ret;
+       }
+       ret = si_process_firmware_header(rdev);
+       if (ret) {
+               DRM_ERROR("si_process_firmware_header failed\n");
+               return ret;
+       }
+       ret = si_initial_switch_from_arb_f0_to_f1(rdev);
+       if (ret) {
+               DRM_ERROR("si_initial_switch_from_arb_f0_to_f1 failed\n");
+               return ret;
+       }
+       ret = si_init_smc_table(rdev);
+       if (ret) {
+               DRM_ERROR("si_init_smc_table failed\n");
+               return ret;
+       }
+       ret = si_init_smc_spll_table(rdev);
+       if (ret) {
+               DRM_ERROR("si_init_smc_spll_table failed\n");
+               return ret;
+       }
+       ret = si_init_arb_table_index(rdev);
+       if (ret) {
+               DRM_ERROR("si_init_arb_table_index failed\n");
+               return ret;
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = si_populate_mc_reg_table(rdev, boot_ps);
+               if (ret) {
+                       DRM_ERROR("si_populate_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+       ret = si_initialize_smc_cac_tables(rdev);
+       if (ret) {
+               DRM_ERROR("si_initialize_smc_cac_tables failed\n");
+               return ret;
+       }
+       ret = si_initialize_hardware_cac_manager(rdev);
+       if (ret) {
+               DRM_ERROR("si_initialize_hardware_cac_manager failed\n");
+               return ret;
+       }
+       ret = si_initialize_smc_dte_tables(rdev);
+       if (ret) {
+               DRM_ERROR("si_initialize_smc_dte_tables failed\n");
+               return ret;
+       }
+       ret = si_populate_smc_tdp_limits(rdev, boot_ps);
+       if (ret) {
+               DRM_ERROR("si_populate_smc_tdp_limits failed\n");
+               return ret;
+       }
+       ret = si_populate_smc_tdp_limits_2(rdev, boot_ps);
+       if (ret) {
+               DRM_ERROR("si_populate_smc_tdp_limits_2 failed\n");
+               return ret;
+       }
+       si_program_response_times(rdev);
+       si_program_ds_registers(rdev);
+       si_dpm_start_smc(rdev);
+       ret = si_notify_smc_display_change(rdev, false);
+       if (ret) {
+               DRM_ERROR("si_notify_smc_display_change failed\n");
+               return ret;
+       }
+       si_enable_sclk_control(rdev, true);
+       si_start_dpm(rdev);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               PPSMC_Result result;
+
+               ret = si_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+               if (ret)
+                       return ret;
+               rdev->irq.dpm_thermal = true;
+               radeon_irq_set(rdev);
+               result = si_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt);
+
+               if (result != PPSMC_Result_OK)
+                       DRM_DEBUG_KMS("Could not enable thermal interrupts.\n");
+       }
+
+       si_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+
+       ni_update_current_ps(rdev, boot_ps);
+
+       return 0;
+}
+
+void si_dpm_disable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+
+       if (!si_is_smc_running(rdev))
+               return;
+       si_disable_ulv(rdev);
+       si_clear_vc(rdev);
+       if (pi->thermal_protection)
+               si_enable_thermal_protection(rdev, false);
+       si_enable_power_containment(rdev, boot_ps, false);
+       si_enable_smc_cac(rdev, boot_ps, false);
+       si_enable_spread_spectrum(rdev, false);
+       si_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, false);
+       si_stop_dpm(rdev);
+       si_reset_to_default(rdev);
+       si_dpm_stop_smc(rdev);
+       si_force_switch_to_arb_f0(rdev);
+
+       ni_update_current_ps(rdev, boot_ps);
+}
+
+int si_dpm_pre_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps requested_ps = *rdev->pm.dpm.requested_ps;
+       struct radeon_ps *new_ps = &requested_ps;
+
+       ni_update_requested_ps(rdev, new_ps);
+
+       si_apply_state_adjust_rules(rdev, &eg_pi->requested_rps);
+
+       return 0;
+}
+
+static int si_power_control_set_level(struct radeon_device *rdev)
+{
+       struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps;
+       int ret;
+
+       ret = si_restrict_performance_levels_before_switch(rdev);
+       if (ret)
+               return ret;
+       ret = si_halt_smc(rdev);
+       if (ret)
+               return ret;
+       ret = si_populate_smc_tdp_limits(rdev, new_ps);
+       if (ret)
+               return ret;
+       ret = si_populate_smc_tdp_limits_2(rdev, new_ps);
+       if (ret)
+               return ret;
+       ret = si_resume_smc(rdev);
+       if (ret)
+               return ret;
+       ret = si_set_sw_state(rdev);
+       if (ret)
+               return ret;
+       return 0;
+}
+
+int si_dpm_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *new_ps = &eg_pi->requested_rps;
+       struct radeon_ps *old_ps = &eg_pi->current_rps;
+       int ret;
+
+       ret = si_disable_ulv(rdev);
+       if (ret) {
+               DRM_ERROR("si_disable_ulv failed\n");
+               return ret;
+       }
+       ret = si_restrict_performance_levels_before_switch(rdev);
+       if (ret) {
+               DRM_ERROR("si_restrict_performance_levels_before_switch failed\n");
+               return ret;
+       }
+       if (eg_pi->pcie_performance_request)
+               si_request_link_speed_change_before_state_change(rdev, new_ps, old_ps);
+       ni_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps);
+       ret = si_enable_power_containment(rdev, new_ps, false);
+       if (ret) {
+               DRM_ERROR("si_enable_power_containment failed\n");
+               return ret;
+       }
+       ret = si_enable_smc_cac(rdev, new_ps, false);
+       if (ret) {
+               DRM_ERROR("si_enable_smc_cac failed\n");
+               return ret;
+       }
+       ret = si_halt_smc(rdev);
+       if (ret) {
+               DRM_ERROR("si_halt_smc failed\n");
+               return ret;
+       }
+       ret = si_upload_sw_state(rdev, new_ps);
+       if (ret) {
+               DRM_ERROR("si_upload_sw_state failed\n");
+               return ret;
+       }
+       ret = si_upload_smc_data(rdev);
+       if (ret) {
+               DRM_ERROR("si_upload_smc_data failed\n");
+               return ret;
+       }
+       ret = si_upload_ulv_state(rdev);
+       if (ret) {
+               DRM_ERROR("si_upload_ulv_state failed\n");
+               return ret;
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = si_upload_mc_reg_table(rdev, new_ps);
+               if (ret) {
+                       DRM_ERROR("si_upload_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+       ret = si_program_memory_timing_parameters(rdev, new_ps);
+       if (ret) {
+               DRM_ERROR("si_program_memory_timing_parameters failed\n");
+               return ret;
+       }
+       si_set_pcie_lane_width_in_smc(rdev, new_ps, old_ps);
+
+       ret = si_resume_smc(rdev);
+       if (ret) {
+               DRM_ERROR("si_resume_smc failed\n");
+               return ret;
+       }
+       ret = si_set_sw_state(rdev);
+       if (ret) {
+               DRM_ERROR("si_set_sw_state failed\n");
+               return ret;
+       }
+       ni_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+       if (eg_pi->pcie_performance_request)
+               si_notify_link_speed_change_after_state_change(rdev, new_ps, old_ps);
+       ret = si_set_power_state_conditionally_enable_ulv(rdev, new_ps);
+       if (ret) {
+               DRM_ERROR("si_set_power_state_conditionally_enable_ulv failed\n");
+               return ret;
+       }
+       ret = si_enable_smc_cac(rdev, new_ps, true);
+       if (ret) {
+               DRM_ERROR("si_enable_smc_cac failed\n");
+               return ret;
+       }
+       ret = si_enable_power_containment(rdev, new_ps, true);
+       if (ret) {
+               DRM_ERROR("si_enable_power_containment failed\n");
+               return ret;
+       }
+
+       ret = si_power_control_set_level(rdev);
+       if (ret) {
+               DRM_ERROR("si_power_control_set_level failed\n");
+               return ret;
+       }
+
+#if 0
+       /* XXX */
+       ret = si_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
+       if (ret) {
+               DRM_ERROR("si_dpm_force_performance_level failed\n");
+               return ret;
+       }
+#else
+       rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO;
+#endif
+
+       return 0;
+}
+
+void si_dpm_post_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *new_ps = &eg_pi->requested_rps;
+
+       ni_update_current_ps(rdev, new_ps);
+}
+
+
+void si_dpm_reset_asic(struct radeon_device *rdev)
+{
+       si_restrict_performance_levels_before_switch(rdev);
+       si_disable_ulv(rdev);
+       si_set_boot_state(rdev);
+}
+
+void si_dpm_display_configuration_changed(struct radeon_device *rdev)
+{
+       si_program_display_gap(rdev);
+}
+
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+       struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+       struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+
+union pplib_clock_info {
+       struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
+       struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
+       struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
+       struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+       struct _ATOM_PPLIB_SI_CLOCK_INFO si;
+};
+
+union pplib_power_state {
+       struct _ATOM_PPLIB_STATE v1;
+       struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static void si_parse_pplib_non_clock_info(struct radeon_device *rdev,
+                                         struct radeon_ps *rps,
+                                         struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
+                                         u8 table_rev)
+{
+       rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+       rps->class = le16_to_cpu(non_clock_info->usClassification);
+       rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
+
+       if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
+               rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
+               rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
+       } else if (r600_is_uvd_state(rps->class, rps->class2)) {
+               rps->vclk = RV770_DEFAULT_VCLK_FREQ;
+               rps->dclk = RV770_DEFAULT_DCLK_FREQ;
+       } else {
+               rps->vclk = 0;
+               rps->dclk = 0;
+       }
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
+               rdev->pm.dpm.boot_ps = rps;
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+               rdev->pm.dpm.uvd_ps = rps;
+}
+
+static void si_parse_pplib_clock_info(struct radeon_device *rdev,
+                                     struct radeon_ps *rps, int index,
+                                     union pplib_clock_info *clock_info)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct ni_ps *ps = ni_get_ps(rps);
+       u16 leakage_voltage;
+       struct rv7xx_pl *pl = &ps->performance_levels[index];
+       int ret;
+
+       ps->performance_level_count = index + 1;
+
+       pl->sclk = le16_to_cpu(clock_info->si.usEngineClockLow);
+       pl->sclk |= clock_info->si.ucEngineClockHigh << 16;
+       pl->mclk = le16_to_cpu(clock_info->si.usMemoryClockLow);
+       pl->mclk |= clock_info->si.ucMemoryClockHigh << 16;
+
+       pl->vddc = le16_to_cpu(clock_info->si.usVDDC);
+       pl->vddci = le16_to_cpu(clock_info->si.usVDDCI);
+       pl->flags = le32_to_cpu(clock_info->si.ulFlags);
+       pl->pcie_gen = r600_get_pcie_gen_support(rdev,
+                                                si_pi->sys_pcie_mask,
+                                                si_pi->boot_pcie_gen,
+                                                clock_info->si.ucPCIEGen);
+
+       /* patch up vddc if necessary */
+       ret = si_get_leakage_voltage_from_leakage_index(rdev, pl->vddc,
+                                                       &leakage_voltage);
+       if (ret == 0)
+               pl->vddc = leakage_voltage;
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) {
+               pi->acpi_vddc = pl->vddc;
+               eg_pi->acpi_vddci = pl->vddci;
+               si_pi->acpi_pcie_gen = pl->pcie_gen;
+       }
+
+       if ((rps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) &&
+           index == 0) {
+               /* XXX disable for A0 tahiti */
+               si_pi->ulv.supported = true;
+               si_pi->ulv.pl = *pl;
+               si_pi->ulv.one_pcie_lane_in_ulv = false;
+               si_pi->ulv.volt_change_delay = SISLANDS_ULVVOLTAGECHANGEDELAY_DFLT;
+               si_pi->ulv.cg_ulv_parameter = SISLANDS_CGULVPARAMETER_DFLT;
+               si_pi->ulv.cg_ulv_control = SISLANDS_CGULVCONTROL_DFLT;
+       }
+
+       if (pi->min_vddc_in_table > pl->vddc)
+               pi->min_vddc_in_table = pl->vddc;
+
+       if (pi->max_vddc_in_table < pl->vddc)
+               pi->max_vddc_in_table = pl->vddc;
+
+       /* patch up boot state */
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+               u16 vddc, vddci, mvdd;
+               radeon_atombios_get_default_voltages(rdev, &vddc, &vddci, &mvdd);
+               pl->mclk = rdev->clock.default_mclk;
+               pl->sclk = rdev->clock.default_sclk;
+               pl->vddc = vddc;
+               pl->vddci = vddci;
+               si_pi->mvdd_bootup_value = mvdd;
+       }
+
+       if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
+           ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk = pl->sclk;
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk = pl->mclk;
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc = pl->vddc;
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci = pl->vddci;
+       }
+}
+
+static int si_parse_power_table(struct radeon_device *rdev)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+       union pplib_power_state *power_state;
+       int i, j, k, non_clock_array_index, clock_array_index;
+       union pplib_clock_info *clock_info;
+       struct _StateArray *state_array;
+       struct _ClockInfoArray *clock_info_array;
+       struct _NonClockInfoArray *non_clock_info_array;
+       union power_info *power_info;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+        u16 data_offset;
+       u8 frev, crev;
+       u8 *power_state_offset;
+       struct ni_ps *ps;
+
+       if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset))
+               return -EINVAL;
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       state_array = (struct _StateArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usStateArrayOffset));
+       clock_info_array = (struct _ClockInfoArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usClockInfoArrayOffset));
+       non_clock_info_array = (struct _NonClockInfoArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset));
+
+       rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
+                                 state_array->ucNumEntries, GFP_KERNEL);
+       if (!rdev->pm.dpm.ps)
+               return -ENOMEM;
+       power_state_offset = (u8 *)state_array->states;
+       rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
+       rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
+       rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
+       for (i = 0; i < state_array->ucNumEntries; i++) {
+               power_state = (union pplib_power_state *)power_state_offset;
+               non_clock_array_index = power_state->v2.nonClockInfoIndex;
+               non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+                       &non_clock_info_array->nonClockInfo[non_clock_array_index];
+               if (!rdev->pm.power_state[i].clock_info)
+                       return -EINVAL;
+               ps = kzalloc(sizeof(struct ni_ps), GFP_KERNEL);
+               if (ps == NULL) {
+                       kfree(rdev->pm.dpm.ps);
+                       return -ENOMEM;
+               }
+               rdev->pm.dpm.ps[i].ps_priv = ps;
+               si_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
+                                             non_clock_info,
+                                             non_clock_info_array->ucEntrySize);
+               k = 0;
+               for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
+                       clock_array_index = power_state->v2.clockInfoIndex[j];
+                       if (clock_array_index >= clock_info_array->ucNumEntries)
+                               continue;
+                       if (k >= SISLANDS_MAX_HARDWARE_POWERLEVELS)
+                               break;
+                       clock_info = (union pplib_clock_info *)
+                               &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
+                       si_parse_pplib_clock_info(rdev,
+                                                 &rdev->pm.dpm.ps[i], k,
+                                                 clock_info);
+                       k++;
+               }
+               power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
+       }
+       rdev->pm.dpm.num_ps = state_array->ucNumEntries;
+       return 0;
+}
+
+int si_dpm_init(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi;
+       struct evergreen_power_info *eg_pi;
+       struct ni_power_info *ni_pi;
+       struct si_power_info *si_pi;
+       int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
+       u16 data_offset, size;
+       u8 frev, crev;
+       struct atom_clock_dividers dividers;
+       int ret;
+       u32 mask;
+
+       si_pi = kzalloc(sizeof(struct si_power_info), GFP_KERNEL);
+       if (si_pi == NULL)
+               return -ENOMEM;
+       rdev->pm.dpm.priv = si_pi;
+       ni_pi = &si_pi->ni;
+       eg_pi = &ni_pi->eg;
+       pi = &eg_pi->rv7xx;
+
+       ret = drm_pcie_get_speed_cap_mask(rdev->ddev, &mask);
+       if (ret)
+               si_pi->sys_pcie_mask = 0;
+       else
+               si_pi->sys_pcie_mask = mask;
+       si_pi->force_pcie_gen = RADEON_PCIE_GEN_INVALID;
+       si_pi->boot_pcie_gen = si_get_current_pcie_speed(rdev);
+
+       si_set_max_cu_value(rdev);
+
+       rv770_get_max_vddc(rdev);
+       si_get_leakage_vddc(rdev);
+       si_patch_dependency_tables_based_on_leakage(rdev);
+
+       pi->acpi_vddc = 0;
+       eg_pi->acpi_vddci = 0;
+       pi->min_vddc_in_table = 0;
+       pi->max_vddc_in_table = 0;
+
+       ret = si_parse_power_table(rdev);
+       if (ret)
+               return ret;
+       ret = r600_parse_extended_power_table(rdev);
+       if (ret)
+               return ret;
+
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries =
+               kzalloc(4 * sizeof(struct radeon_clock_voltage_dependency_entry), GFP_KERNEL);
+       if (!rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) {
+               r600_free_extended_power_table(rdev);
+               return -ENOMEM;
+       }
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.count = 4;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].clk = 0;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].v = 0;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].clk = 36000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].v = 720;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].clk = 54000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].v = 810;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].clk = 72000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].v = 900;
+
+       if (rdev->pm.dpm.voltage_response_time == 0)
+               rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT;
+       if (rdev->pm.dpm.backbias_response_time == 0)
+               rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            0, false, &dividers);
+       if (ret)
+               pi->ref_div = dividers.ref_div + 1;
+       else
+               pi->ref_div = R600_REFERENCEDIVIDER_DFLT;
+
+       eg_pi->smu_uvd_hs = false;
+
+       pi->mclk_strobe_mode_threshold = 40000;
+       if (si_is_special_1gb_platform(rdev))
+               pi->mclk_stutter_mode_threshold = 0;
+       else
+               pi->mclk_stutter_mode_threshold = pi->mclk_strobe_mode_threshold;
+       pi->mclk_edc_enable_threshold = 40000;
+       eg_pi->mclk_edc_wr_enable_threshold = 40000;
+
+       ni_pi->mclk_rtt_mode_threshold = eg_pi->mclk_edc_wr_enable_threshold;
+
+       pi->voltage_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, VOLTAGE_OBJ_GPIO_LUT);
+
+       pi->mvdd_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC, VOLTAGE_OBJ_GPIO_LUT);
+
+       eg_pi->vddci_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI, VOLTAGE_OBJ_GPIO_LUT);
+
+       si_pi->vddc_phase_shed_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, VOLTAGE_OBJ_PHASE_LUT);
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                   &frev, &crev, &data_offset)) {
+               pi->sclk_ss = true;
+               pi->mclk_ss = true;
+               pi->dynamic_ss = true;
+       } else {
+               pi->sclk_ss = false;
+               pi->mclk_ss = false;
+               pi->dynamic_ss = true;
+       }
+
+       pi->asi = RV770_ASI_DFLT;
+       pi->pasi = CYPRESS_HASI_DFLT;
+       pi->vrc = SISLANDS_VRC_DFLT;
+
+       pi->gfx_clock_gating = true;
+
+       eg_pi->sclk_deep_sleep = true;
+       si_pi->sclk_deep_sleep_above_low = false;
+
+       if (pi->gfx_clock_gating &&
+           (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE))
+               pi->thermal_protection = true;
+       else
+               pi->thermal_protection = false;
+
+       eg_pi->dynamic_ac_timing = true;
+
+       eg_pi->light_sleep = true;
+#if defined(CONFIG_ACPI)
+       eg_pi->pcie_performance_request =
+               radeon_acpi_is_pcie_performance_request_supported(rdev);
+#else
+       eg_pi->pcie_performance_request = false;
+#endif
+
+       si_pi->sram_end = SMC_RAM_END;
+
+       rdev->pm.dpm.dyn_state.mclk_sclk_ratio = 4;
+       rdev->pm.dpm.dyn_state.sclk_mclk_delta = 15000;
+       rdev->pm.dpm.dyn_state.vddc_vddci_delta = 200;
+       rdev->pm.dpm.dyn_state.valid_sclk_values.count = 0;
+       rdev->pm.dpm.dyn_state.valid_sclk_values.values = NULL;
+       rdev->pm.dpm.dyn_state.valid_mclk_values.count = 0;
+       rdev->pm.dpm.dyn_state.valid_mclk_values.values = NULL;
+
+       si_initialize_powertune_defaults(rdev);
+
+       return 0;
+}
+
+void si_dpm_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               kfree(rdev->pm.dpm.ps[i].ps_priv);
+       }
+       kfree(rdev->pm.dpm.ps);
+       kfree(rdev->pm.dpm.priv);
+       kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries);
+       r600_free_extended_power_table(rdev);
+}
+
+void si_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                   struct seq_file *m)
+{
+       struct radeon_ps *rps = rdev->pm.dpm.current_ps;
+       struct ni_ps *ps = ni_get_ps(rps);
+       struct rv7xx_pl *pl;
+       u32 current_index =
+               (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_INDEX_MASK) >>
+               CURRENT_STATE_INDEX_SHIFT;
+
+       if (current_index >= ps->performance_level_count) {
+               seq_printf(m, "invalid dpm profile %d\n", current_index);
+       } else {
+               pl = &ps->performance_levels[current_index];
+               seq_printf(m, "uvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+               seq_printf(m, "power level %d    sclk: %u mclk: %u vddc: %u vddci: %u pcie gen: %u\n",
+                          current_index, pl->sclk, pl->mclk, pl->vddc, pl->vddci, pl->pcie_gen + 1);
+       }
+}
diff --git a/drivers/gpu/drm/radeon/si_dpm.h b/drivers/gpu/drm/radeon/si_dpm.h
new file mode 100644 (file)
index 0000000..4ce5032
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __SI_DPM_H__
+#define __SI_DPM_H__
+
+#include "ni_dpm.h"
+#include "sislands_smc.h"
+
+enum si_cac_config_reg_type
+{
+       SISLANDS_CACCONFIG_MMR = 0,
+       SISLANDS_CACCONFIG_CGIND,
+       SISLANDS_CACCONFIG_MAX
+};
+
+struct si_cac_config_reg
+{
+       u32 offset;
+       u32 mask;
+       u32 shift;
+       u32 value;
+       enum si_cac_config_reg_type type;
+};
+
+struct si_powertune_data
+{
+       u32 cac_window;
+       u32 l2_lta_window_size_default;
+       u8 lts_truncate_default;
+       u8 shift_n_default;
+       u8 operating_temp;
+       struct ni_leakage_coeffients leakage_coefficients;
+       u32 fixed_kt;
+       u32 lkge_lut_v0_percent;
+       u8 dc_cac[NISLANDS_DCCAC_MAX_LEVELS];
+       bool enable_powertune_by_default;
+};
+
+struct si_dyn_powertune_data
+{
+       u32 cac_leakage;
+       s32 leakage_minimum_temperature;
+       u32 wintime;
+       u32 l2_lta_window_size;
+       u8 lts_truncate;
+       u8 shift_n;
+       u8 dc_pwr_value;
+       bool disable_uvd_powertune;
+};
+
+struct si_dte_data
+{
+       u32 tau[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+       u32 r[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+       u32 k;
+       u32 t0;
+       u32 max_t;
+       u8 window_size;
+       u8 temp_select;
+       u8 dte_mode;
+       u8 tdep_count;
+       u8 t_limits[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+       u32 tdep_tau[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+       u32 tdep_r[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+       u32 t_threshold;
+       bool enable_dte_by_default;
+};
+
+struct si_clock_registers {
+       u32 cg_spll_func_cntl;
+       u32 cg_spll_func_cntl_2;
+       u32 cg_spll_func_cntl_3;
+       u32 cg_spll_func_cntl_4;
+       u32 cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2;
+       u32 dll_cntl;
+       u32 mclk_pwrmgt_cntl;
+       u32 mpll_ad_func_cntl;
+       u32 mpll_dq_func_cntl;
+       u32 mpll_func_cntl;
+       u32 mpll_func_cntl_1;
+       u32 mpll_func_cntl_2;
+       u32 mpll_ss1;
+       u32 mpll_ss2;
+};
+
+struct si_mc_reg_entry {
+       u32 mclk_max;
+       u32 mc_data[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct si_mc_reg_table {
+       u8 last;
+       u8 num_entries;
+       u16 valid_flag;
+       struct si_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+       SMC_NIslands_MCRegisterAddress mc_reg_address[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+#define SISLANDS_MCREGISTERTABLE_INITIAL_SLOT               0
+#define SISLANDS_MCREGISTERTABLE_ACPI_SLOT                  1
+#define SISLANDS_MCREGISTERTABLE_ULV_SLOT                   2
+#define SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT     3
+
+struct si_leakage_voltage_entry
+{
+       u16 voltage;
+       u16 leakage_index;
+};
+
+#define SISLANDS_LEAKAGE_INDEX0     0xff01
+#define SISLANDS_MAX_LEAKAGE_COUNT  4
+
+struct si_leakage_voltage
+{
+       u16 count;
+       struct si_leakage_voltage_entry entries[SISLANDS_MAX_LEAKAGE_COUNT];
+};
+
+#define SISLANDS_MAX_HARDWARE_POWERLEVELS 5
+
+struct si_ulv_param {
+       bool supported;
+       u32 cg_ulv_control;
+       u32 cg_ulv_parameter;
+       u32 volt_change_delay;
+       struct rv7xx_pl pl;
+       bool one_pcie_lane_in_ulv;
+};
+
+struct si_power_info {
+       /* must be first! */
+       struct ni_power_info ni;
+       struct si_clock_registers clock_registers;
+       struct si_mc_reg_table mc_reg_table;
+       struct atom_voltage_table mvdd_voltage_table;
+       struct atom_voltage_table vddc_phase_shed_table;
+       struct si_leakage_voltage leakage_voltage;
+       u16 mvdd_bootup_value;
+       struct si_ulv_param ulv;
+       u32 max_cu;
+       /* pcie gen */
+       enum radeon_pcie_gen force_pcie_gen;
+       enum radeon_pcie_gen boot_pcie_gen;
+       enum radeon_pcie_gen acpi_pcie_gen;
+       u32 sys_pcie_mask;
+       /* flags */
+       bool enable_dte;
+       bool enable_ppm;
+       bool vddc_phase_shed_control;
+       bool pspp_notify_required;
+       bool sclk_deep_sleep_above_low;
+       /* smc offsets */
+       u32 sram_end;
+       u32 state_table_start;
+       u32 soft_regs_start;
+       u32 mc_reg_table_start;
+       u32 arb_table_start;
+       u32 cac_table_start;
+       u32 dte_table_start;
+       u32 spll_table_start;
+       u32 papm_cfg_table_start;
+       /* CAC stuff */
+       const struct si_cac_config_reg *cac_weights;
+       const struct si_cac_config_reg *lcac_config;
+       const struct si_cac_config_reg *cac_override;
+       const struct si_powertune_data *powertune_data;
+       struct si_dyn_powertune_data dyn_powertune_data;
+       /* DTE stuff */
+       struct si_dte_data dte_data;
+       /* scratch structs */
+       SMC_SIslands_MCRegisters smc_mc_reg_table;
+       SISLANDS_SMC_STATETABLE smc_statetable;
+       PP_SIslands_PAPMParameters papm_parm;
+};
+
+#define SISLANDS_INITIAL_STATE_ARB_INDEX    0
+#define SISLANDS_ACPI_STATE_ARB_INDEX       1
+#define SISLANDS_ULV_STATE_ARB_INDEX        2
+#define SISLANDS_DRIVER_STATE_ARB_INDEX     3
+
+#define SISLANDS_DPM2_MAX_PULSE_SKIP        256
+
+#define SISLANDS_DPM2_NEAR_TDP_DEC          10
+#define SISLANDS_DPM2_ABOVE_SAFE_INC        5
+#define SISLANDS_DPM2_BELOW_SAFE_INC        20
+
+#define SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT            80
+
+#define SISLANDS_DPM2_MAXPS_PERCENT_H                   99
+#define SISLANDS_DPM2_MAXPS_PERCENT_M                   99
+
+#define SISLANDS_DPM2_SQ_RAMP_MAX_POWER                 0x3FFF
+#define SISLANDS_DPM2_SQ_RAMP_MIN_POWER                 0x12
+#define SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA           0x15
+#define SISLANDS_DPM2_SQ_RAMP_STI_SIZE                  0x1E
+#define SISLANDS_DPM2_SQ_RAMP_LTI_RATIO                 0xF
+
+#define SISLANDS_DPM2_PWREFFICIENCYRATIO_MARGIN         10
+
+#define SISLANDS_VRC_DFLT                               0xC000B3
+#define SISLANDS_ULVVOLTAGECHANGEDELAY_DFLT             1687
+#define SISLANDS_CGULVPARAMETER_DFLT                    0x00040035
+#define SISLANDS_CGULVCONTROL_DFLT                      0x1f007550
+
+
+#endif
diff --git a/drivers/gpu/drm/radeon/si_smc.c b/drivers/gpu/drm/radeon/si_smc.c
new file mode 100644 (file)
index 0000000..5f524c0
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
+ */
+
+#include <linux/firmware.h>
+#include "drmP.h"
+#include "radeon.h"
+#include "sid.h"
+#include "ppsmc.h"
+#include "radeon_ucode.h"
+
+int si_set_smc_sram_address(struct radeon_device *rdev,
+                           u32 smc_address, u32 limit)
+{
+       if (smc_address & 3)
+               return -EINVAL;
+       if ((smc_address + 3) > limit)
+               return -EINVAL;
+
+       WREG32(SMC_IND_INDEX_0, smc_address);
+       WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
+
+       return 0;
+}
+
+int si_copy_bytes_to_smc(struct radeon_device *rdev,
+                        u32 smc_start_address,
+                        const u8 *src, u32 byte_count, u32 limit)
+{
+       int ret;
+       u32 data, original_data, addr, extra_shift;
+
+       if (smc_start_address & 3)
+               return -EINVAL;
+       if ((smc_start_address + byte_count) > limit)
+               return -EINVAL;
+
+       addr = smc_start_address;
+
+       while (byte_count >= 4) {
+               /* SMC address space is BE */
+               data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+               ret = si_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               WREG32(SMC_IND_DATA_0, data);
+
+               src += 4;
+               byte_count -= 4;
+               addr += 4;
+       }
+
+       /* RMW for the final bytes */
+       if (byte_count > 0) {
+               data = 0;
+
+               ret = si_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               original_data = RREG32(SMC_IND_DATA_0);
+
+               extra_shift = 8 * (4 - byte_count);
+
+               while (byte_count > 0) {
+                       /* SMC address space is BE */
+                       data = (data << 8) + *src++;
+                       byte_count--;
+               }
+
+               data <<= extra_shift;
+
+               data |= (original_data & ~((~0UL) << extra_shift));
+
+               ret = si_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               WREG32(SMC_IND_DATA_0, data);
+       }
+       return 0;
+}
+
+void si_start_smc(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+
+       tmp &= ~RST_REG;
+
+       WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
+}
+
+void si_reset_smc(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+
+       tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+       tmp |= RST_REG;
+       WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
+}
+
+int si_program_jump_on_start(struct radeon_device *rdev)
+{
+       static u8 data[] = { 0x0E, 0x00, 0x40, 0x40 };
+
+       return si_copy_bytes_to_smc(rdev, 0x0, data, 4, sizeof(data)+1);
+}
+
+void si_stop_smc_clock(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+       tmp |= CK_DISABLE;
+
+       WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
+}
+
+void si_start_smc_clock(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+       tmp &= ~CK_DISABLE;
+
+       WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
+}
+
+bool si_is_smc_running(struct radeon_device *rdev)
+{
+       u32 rst = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+       u32 clk = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+       if (!(rst & RST_REG) && !(clk & CK_DISABLE))
+               return true;
+
+       return false;
+}
+
+PPSMC_Result si_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
+{
+       u32 tmp;
+       int i;
+
+       if (!si_is_smc_running(rdev))
+               return PPSMC_Result_Failed;
+
+       WREG32(SMC_MESSAGE_0, msg);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               tmp = RREG32(SMC_RESP_0);
+               if (tmp != 0)
+                       break;
+               udelay(1);
+       }
+       tmp = RREG32(SMC_RESP_0);
+
+       return (PPSMC_Result)tmp;
+}
+
+PPSMC_Result si_wait_for_smc_inactive(struct radeon_device *rdev)
+{
+       u32 tmp;
+       int i;
+
+       if (!si_is_smc_running(rdev))
+               return PPSMC_Result_OK;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+               if ((tmp & CKEN) == 0)
+                       break;
+               udelay(1);
+       }
+
+       return PPSMC_Result_OK;
+}
+
+int si_load_smc_ucode(struct radeon_device *rdev, u32 limit)
+{
+       u32 ucode_start_address;
+       u32 ucode_size;
+       const u8 *src;
+       u32 data;
+
+       if (!rdev->smc_fw)
+               return -EINVAL;
+
+       switch (rdev->family) {
+       case CHIP_TAHITI:
+               ucode_start_address = TAHITI_SMC_UCODE_START;
+               ucode_size = TAHITI_SMC_UCODE_SIZE;
+               break;
+       case CHIP_PITCAIRN:
+               ucode_start_address = PITCAIRN_SMC_UCODE_START;
+               ucode_size = PITCAIRN_SMC_UCODE_SIZE;
+               break;
+       case CHIP_VERDE:
+               ucode_start_address = VERDE_SMC_UCODE_START;
+               ucode_size = VERDE_SMC_UCODE_SIZE;
+               break;
+       case CHIP_OLAND:
+               ucode_start_address = OLAND_SMC_UCODE_START;
+               ucode_size = OLAND_SMC_UCODE_SIZE;
+               break;
+       case CHIP_HAINAN:
+               ucode_start_address = HAINAN_SMC_UCODE_START;
+               ucode_size = HAINAN_SMC_UCODE_SIZE;
+               break;
+       default:
+               DRM_ERROR("unknown asic in smc ucode loader\n");
+               BUG();
+       }
+
+       if (ucode_size & 3)
+               return -EINVAL;
+
+       src = (const u8 *)rdev->smc_fw->data;
+       WREG32(SMC_IND_INDEX_0, ucode_start_address);
+       WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
+       while (ucode_size >= 4) {
+               /* SMC address space is BE */
+               data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+               WREG32(SMC_IND_DATA_0, data);
+
+               src += 4;
+               ucode_size -= 4;
+       }
+       WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
+
+       return 0;
+}
+
+int si_read_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
+                          u32 *value, u32 limit)
+{
+       int ret;
+
+       ret = si_set_smc_sram_address(rdev, smc_address, limit);
+       if (ret)
+               return ret;
+
+       *value = RREG32(SMC_IND_DATA_0);
+       return 0;
+}
+
+int si_write_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
+                           u32 value, u32 limit)
+{
+       int ret;
+
+       ret = si_set_smc_sram_address(rdev, smc_address, limit);
+       if (ret)
+               return ret;
+
+       WREG32(SMC_IND_DATA_0, value);
+       return 0;
+}
index 8f2d7d4..12a20eb 100644 (file)
 #define VERDE_GB_ADDR_CONFIG_GOLDEN         0x12010002
 #define HAINAN_GB_ADDR_CONFIG_GOLDEN        0x02010001
 
+#define SI_MAX_SH_GPRS           256
+#define SI_MAX_TEMP_GPRS         16
+#define SI_MAX_SH_THREADS        256
+#define SI_MAX_SH_STACK_ENTRIES  4096
+#define SI_MAX_FRC_EOV_CNT       16384
+#define SI_MAX_BACKENDS          8
+#define SI_MAX_BACKENDS_MASK     0xFF
+#define SI_MAX_BACKENDS_PER_SE_MASK     0x0F
+#define SI_MAX_SIMDS             12
+#define SI_MAX_SIMDS_MASK        0x0FFF
+#define SI_MAX_SIMDS_PER_SE_MASK        0x00FF
+#define SI_MAX_PIPES             8
+#define SI_MAX_PIPES_MASK        0xFF
+#define SI_MAX_PIPES_PER_SIMD_MASK      0x3F
+#define SI_MAX_LDS_NUM           0xFFFF
+#define SI_MAX_TCC               16
+#define SI_MAX_TCC_MASK          0xFFFF
+
+/* SMC IND accessor regs */
+#define SMC_IND_INDEX_0                              0x200
+#define SMC_IND_DATA_0                               0x204
+
+#define SMC_IND_ACCESS_CNTL                          0x228
+#       define AUTO_INCREMENT_IND_0                  (1 << 0)
+#define SMC_MESSAGE_0                                0x22c
+#define SMC_RESP_0                                   0x230
+
+/* CG IND registers are accessed via SMC indirect space + SMC_CG_IND_START */
+#define SMC_CG_IND_START                    0xc0030000
+#define SMC_CG_IND_END                      0xc0040000
+
+#define        CG_CGTT_LOCAL_0                         0x400
+#define        CG_CGTT_LOCAL_1                         0x401
+
+/* SMC IND registers */
+#define        SMC_SYSCON_RESET_CNTL                           0x80000000
+#       define RST_REG                                  (1 << 0)
+#define        SMC_SYSCON_CLOCK_CNTL_0                         0x80000004
+#       define CK_DISABLE                               (1 << 0)
+#       define CKEN                                     (1 << 24)
+
+#define VGA_HDP_CONTROL                                0x328
+#define                VGA_MEMORY_DISABLE                              (1 << 4)
+
+#define DCCG_DISP_SLOW_SELECT_REG                       0x4fc
+#define                DCCG_DISP1_SLOW_SELECT(x)               ((x) << 0)
+#define                DCCG_DISP1_SLOW_SELECT_MASK             (7 << 0)
+#define                DCCG_DISP1_SLOW_SELECT_SHIFT            0
+#define                DCCG_DISP2_SLOW_SELECT(x)               ((x) << 4)
+#define                DCCG_DISP2_SLOW_SELECT_MASK             (7 << 4)
+#define                DCCG_DISP2_SLOW_SELECT_SHIFT            4
+
+#define        CG_SPLL_FUNC_CNTL                               0x600
+#define                SPLL_RESET                              (1 << 0)
+#define                SPLL_SLEEP                              (1 << 1)
+#define                SPLL_BYPASS_EN                          (1 << 3)
+#define                SPLL_REF_DIV(x)                         ((x) << 4)
+#define                SPLL_REF_DIV_MASK                       (0x3f << 4)
+#define                SPLL_PDIV_A(x)                          ((x) << 20)
+#define                SPLL_PDIV_A_MASK                        (0x7f << 20)
+#define                SPLL_PDIV_A_SHIFT                       20
+#define        CG_SPLL_FUNC_CNTL_2                             0x604
+#define                SCLK_MUX_SEL(x)                         ((x) << 0)
+#define                SCLK_MUX_SEL_MASK                       (0x1ff << 0)
+#define        CG_SPLL_FUNC_CNTL_3                             0x608
+#define                SPLL_FB_DIV(x)                          ((x) << 0)
+#define                SPLL_FB_DIV_MASK                        (0x3ffffff << 0)
+#define                SPLL_FB_DIV_SHIFT                       0
+#define                SPLL_DITHEN                             (1 << 28)
+#define        CG_SPLL_FUNC_CNTL_4                             0x60c
+
+#define        SPLL_CNTL_MODE                                  0x618
+#      define SPLL_REFCLK_SEL(x)                       ((x) << 8)
+#      define SPLL_REFCLK_SEL_MASK                     0xFF00
+
+#define        CG_SPLL_SPREAD_SPECTRUM                         0x620
+#define                SSEN                                    (1 << 0)
+#define                CLK_S(x)                                ((x) << 4)
+#define                CLK_S_MASK                              (0xfff << 4)
+#define                CLK_S_SHIFT                             4
+#define        CG_SPLL_SPREAD_SPECTRUM_2                       0x624
+#define                CLK_V(x)                                ((x) << 0)
+#define                CLK_V_MASK                              (0x3ffffff << 0)
+#define                CLK_V_SHIFT                             0
+
+#define        CG_SPLL_AUTOSCALE_CNTL                          0x62c
+#       define AUTOSCALE_ON_SS_CLEAR                    (1 << 9)
+
 /* discrete uvd clocks */
 #define        CG_UPLL_FUNC_CNTL                               0x634
 #      define UPLL_RESET_MASK                          0x00000001
 #define        CG_UPLL_SPREAD_SPECTRUM                         0x650
 #      define SSEN_MASK                                0x00000001
 
+#define        MPLL_BYPASSCLK_SEL                              0x65c
+#      define MPLL_CLKOUT_SEL(x)                       ((x) << 8)
+#      define MPLL_CLKOUT_SEL_MASK                     0xFF00
+
+#define CG_CLKPIN_CNTL                                    0x660
+#       define XTALIN_DIVIDE                              (1 << 1)
+#       define BCLK_AS_XCLK                               (1 << 2)
+#define CG_CLKPIN_CNTL_2                                  0x664
+#       define FORCE_BIF_REFCLK_EN                        (1 << 3)
+#       define MUX_TCLK_TO_XCLK                           (1 << 8)
+
+#define        THM_CLK_CNTL                                    0x66c
+#      define CMON_CLK_SEL(x)                          ((x) << 0)
+#      define CMON_CLK_SEL_MASK                        0xFF
+#      define TMON_CLK_SEL(x)                          ((x) << 8)
+#      define TMON_CLK_SEL_MASK                        0xFF00
+#define        MISC_CLK_CNTL                                   0x670
+#      define DEEP_SLEEP_CLK_SEL(x)                    ((x) << 0)
+#      define DEEP_SLEEP_CLK_SEL_MASK                  0xFF
+#      define ZCLK_SEL(x)                              ((x) << 8)
+#      define ZCLK_SEL_MASK                            0xFF00
+
+#define        CG_THERMAL_CTRL                                 0x700
+#define        DPM_EVENT_SRC(x)                        ((x) << 0)
+#define        DPM_EVENT_SRC_MASK                      (7 << 0)
+#define                DIG_THERM_DPM(x)                        ((x) << 14)
+#define                DIG_THERM_DPM_MASK                      0x003FC000
+#define                DIG_THERM_DPM_SHIFT                     14
+
+#define        CG_THERMAL_INT                                  0x708
+#define                DIG_THERM_INTH(x)                       ((x) << 8)
+#define                DIG_THERM_INTH_MASK                     0x0000FF00
+#define                DIG_THERM_INTH_SHIFT                    8
+#define                DIG_THERM_INTL(x)                       ((x) << 16)
+#define                DIG_THERM_INTL_MASK                     0x00FF0000
+#define                DIG_THERM_INTL_SHIFT                    16
+#define        THERM_INT_MASK_HIGH                     (1 << 24)
+#define        THERM_INT_MASK_LOW                      (1 << 25)
+
 #define        CG_MULT_THERMAL_STATUS                                  0x714
 #define                ASIC_MAX_TEMP(x)                                ((x) << 0)
 #define                ASIC_MAX_TEMP_MASK                              0x000001ff
 #define                CTF_TEMP_MASK                                   0x0003fe00
 #define                CTF_TEMP_SHIFT                                  9
 
-#define SI_MAX_SH_GPRS           256
-#define SI_MAX_TEMP_GPRS         16
-#define SI_MAX_SH_THREADS        256
-#define SI_MAX_SH_STACK_ENTRIES  4096
-#define SI_MAX_FRC_EOV_CNT       16384
-#define SI_MAX_BACKENDS          8
-#define SI_MAX_BACKENDS_MASK     0xFF
-#define SI_MAX_BACKENDS_PER_SE_MASK     0x0F
-#define SI_MAX_SIMDS             12
-#define SI_MAX_SIMDS_MASK        0x0FFF
-#define SI_MAX_SIMDS_PER_SE_MASK        0x00FF
-#define SI_MAX_PIPES             8
-#define SI_MAX_PIPES_MASK        0xFF
-#define SI_MAX_PIPES_PER_SIMD_MASK      0x3F
-#define SI_MAX_LDS_NUM           0xFFFF
-#define SI_MAX_TCC               16
-#define SI_MAX_TCC_MASK          0xFFFF
-
-#define VGA_HDP_CONTROL                                0x328
-#define                VGA_MEMORY_DISABLE                              (1 << 4)
-
-#define CG_CLKPIN_CNTL                                    0x660
-#       define XTALIN_DIVIDE                              (1 << 1)
-#define CG_CLKPIN_CNTL_2                                  0x664
-#       define MUX_TCLK_TO_XCLK                           (1 << 8)
+#define GENERAL_PWRMGT                                  0x780
+#       define GLOBAL_PWRMGT_EN                         (1 << 0)
+#       define STATIC_PM_EN                             (1 << 1)
+#       define THERMAL_PROTECTION_DIS                   (1 << 2)
+#       define THERMAL_PROTECTION_TYPE                  (1 << 3)
+#       define SW_SMIO_INDEX(x)                         ((x) << 6)
+#       define SW_SMIO_INDEX_MASK                       (1 << 6)
+#       define SW_SMIO_INDEX_SHIFT                      6
+#       define VOLT_PWRMGT_EN                           (1 << 10)
+#       define DYN_SPREAD_SPECTRUM_EN                   (1 << 23)
+#define CG_TPC                                            0x784
+#define SCLK_PWRMGT_CNTL                                  0x788
+#       define SCLK_PWRMGT_OFF                            (1 << 0)
+#       define SCLK_LOW_D1                                (1 << 1)
+#       define FIR_RESET                                  (1 << 4)
+#       define FIR_FORCE_TREND_SEL                        (1 << 5)
+#       define FIR_TREND_MODE                             (1 << 6)
+#       define DYN_GFX_CLK_OFF_EN                         (1 << 7)
+#       define GFX_CLK_FORCE_ON                           (1 << 8)
+#       define GFX_CLK_REQUEST_OFF                        (1 << 9)
+#       define GFX_CLK_FORCE_OFF                          (1 << 10)
+#       define GFX_CLK_OFF_ACPI_D1                        (1 << 11)
+#       define GFX_CLK_OFF_ACPI_D2                        (1 << 12)
+#       define GFX_CLK_OFF_ACPI_D3                        (1 << 13)
+#       define DYN_LIGHT_SLEEP_EN                         (1 << 14)
+
+#define TARGET_AND_CURRENT_PROFILE_INDEX                  0x798
+#       define CURRENT_STATE_INDEX_MASK                   (0xf << 4)
+#       define CURRENT_STATE_INDEX_SHIFT                  4
+
+#define CG_FTV                                            0x7bc
+
+#define CG_FFCT_0                                         0x7c0
+#       define UTC_0(x)                                   ((x) << 0)
+#       define UTC_0_MASK                                 (0x3ff << 0)
+#       define DTC_0(x)                                   ((x) << 10)
+#       define DTC_0_MASK                                 (0x3ff << 10)
+
+#define CG_BSP                                          0x7fc
+#       define BSP(x)                                  ((x) << 0)
+#       define BSP_MASK                                        (0xffff << 0)
+#       define BSU(x)                                  ((x) << 16)
+#       define BSU_MASK                                        (0xf << 16)
+#define CG_AT                                           0x800
+#       define CG_R(x)                                 ((x) << 0)
+#       define CG_R_MASK                               (0xffff << 0)
+#       define CG_L(x)                                 ((x) << 16)
+#       define CG_L_MASK                               (0xffff << 16)
+
+#define CG_GIT                                          0x804
+#       define CG_GICST(x)                              ((x) << 0)
+#       define CG_GICST_MASK                            (0xffff << 0)
+#       define CG_GIPOT(x)                              ((x) << 16)
+#       define CG_GIPOT_MASK                            (0xffff << 16)
+
+#define CG_SSP                                            0x80c
+#       define SST(x)                                     ((x) << 0)
+#       define SST_MASK                                   (0xffff << 0)
+#       define SSTU(x)                                    ((x) << 16)
+#       define SSTU_MASK                                  (0xf << 16)
+
+#define CG_DISPLAY_GAP_CNTL                               0x828
+#       define DISP1_GAP(x)                               ((x) << 0)
+#       define DISP1_GAP_MASK                             (3 << 0)
+#       define DISP2_GAP(x)                               ((x) << 2)
+#       define DISP2_GAP_MASK                             (3 << 2)
+#       define VBI_TIMER_COUNT(x)                         ((x) << 4)
+#       define VBI_TIMER_COUNT_MASK                       (0x3fff << 4)
+#       define VBI_TIMER_UNIT(x)                          ((x) << 20)
+#       define VBI_TIMER_UNIT_MASK                        (7 << 20)
+#       define DISP1_GAP_MCHG(x)                          ((x) << 24)
+#       define DISP1_GAP_MCHG_MASK                        (3 << 24)
+#       define DISP2_GAP_MCHG(x)                          ((x) << 26)
+#       define DISP2_GAP_MCHG_MASK                        (3 << 26)
+
+#define        CG_ULV_CONTROL                                  0x878
+#define        CG_ULV_PARAMETER                                0x87c
+
+#define        SMC_SCRATCH0                                    0x884
+
+#define        CG_CAC_CTRL                                     0x8b8
+#      define CAC_WINDOW(x)                            ((x) << 0)
+#      define CAC_WINDOW_MASK                          0x00ffffff
 
 #define DMIF_ADDR_CONFIG                               0xBD4
 
 #define        VM_CONTEXT0_PAGE_TABLE_END_ADDR                 0x157C
 #define        VM_CONTEXT1_PAGE_TABLE_END_ADDR                 0x1580
 
+#define VM_L2_CG                                       0x15c0
+#define                MC_CG_ENABLE                            (1 << 18)
+#define                MC_LS_ENABLE                            (1 << 19)
+
 #define MC_SHARED_CHMAP                                                0x2004
 #define                NOOFCHAN_SHIFT                                  12
 #define                NOOFCHAN_MASK                                   0x0000f000
 
 #define MC_SHARED_BLACKOUT_CNTL                        0x20ac
 
+#define MC_HUB_MISC_HUB_CG                             0x20b8
+#define MC_HUB_MISC_VM_CG                              0x20bc
+
+#define MC_HUB_MISC_SIP_CG                             0x20c0
+
+#define MC_XPB_CLK_GAT                                 0x2478
+
+#define MC_CITF_MISC_RD_CG                             0x2648
+#define MC_CITF_MISC_WR_CG                             0x264c
+#define MC_CITF_MISC_VM_CG                             0x2650
+
 #define        MC_ARB_RAMCFG                                   0x2760
 #define                NOOFBANK_SHIFT                                  0
 #define                NOOFBANK_MASK                                   0x00000003
 #define                NOOFGROUPS_SHIFT                                12
 #define                NOOFGROUPS_MASK                                 0x00001000
 
+#define        MC_ARB_DRAM_TIMING                              0x2774
+#define        MC_ARB_DRAM_TIMING2                             0x2778
+
+#define MC_ARB_BURST_TIME                               0x2808
+#define                STATE0(x)                               ((x) << 0)
+#define                STATE0_MASK                             (0x1f << 0)
+#define                STATE0_SHIFT                            0
+#define                STATE1(x)                               ((x) << 5)
+#define                STATE1_MASK                             (0x1f << 5)
+#define                STATE1_SHIFT                            5
+#define                STATE2(x)                               ((x) << 10)
+#define                STATE2_MASK                             (0x1f << 10)
+#define                STATE2_SHIFT                            10
+#define                STATE3(x)                               ((x) << 15)
+#define                STATE3_MASK                             (0x1f << 15)
+#define                STATE3_SHIFT                            15
+
 #define        MC_SEQ_TRAIN_WAKEUP_CNTL                        0x2808
 #define                TRAIN_DONE_D0                           (1 << 30)
 #define                TRAIN_DONE_D1                           (1 << 31)
 #define MC_SEQ_SUP_CNTL                                0x28c8
 #define                RUN_MASK                                (1 << 0)
 #define MC_SEQ_SUP_PGM                                 0x28cc
+#define MC_PMG_AUTO_CMD                                0x28d0
 
 #define MC_IO_PAD_CNTL_D0                              0x29d0
 #define                MEM_FALL_OUT_CMD                        (1 << 8)
 
+#define MC_SEQ_RAS_TIMING                               0x28a0
+#define MC_SEQ_CAS_TIMING                               0x28a4
+#define MC_SEQ_MISC_TIMING                              0x28a8
+#define MC_SEQ_MISC_TIMING2                             0x28ac
+#define MC_SEQ_PMG_TIMING                               0x28b0
+#define MC_SEQ_RD_CTL_D0                                0x28b4
+#define MC_SEQ_RD_CTL_D1                                0x28b8
+#define MC_SEQ_WR_CTL_D0                                0x28bc
+#define MC_SEQ_WR_CTL_D1                                0x28c0
+
+#define MC_SEQ_MISC0                                           0x2a00
+#define        MC_SEQ_MISC0_VEN_ID_SHIFT               8
+#define        MC_SEQ_MISC0_VEN_ID_MASK                0x00000f00
+#define        MC_SEQ_MISC0_VEN_ID_VALUE               3
+#define        MC_SEQ_MISC0_REV_ID_SHIFT               12
+#define        MC_SEQ_MISC0_REV_ID_MASK                0x0000f000
+#define        MC_SEQ_MISC0_REV_ID_VALUE               1
+#define        MC_SEQ_MISC0_GDDR5_SHIFT                28
+#define        MC_SEQ_MISC0_GDDR5_MASK                 0xf0000000
+#define        MC_SEQ_MISC0_GDDR5_VALUE                5
+#define MC_SEQ_MISC1                                    0x2a04
+#define MC_SEQ_RESERVE_M                                0x2a08
+#define MC_PMG_CMD_EMRS                                 0x2a0c
+
 #define MC_SEQ_IO_DEBUG_INDEX                          0x2a44
 #define MC_SEQ_IO_DEBUG_DATA                                   0x2a48
 
+#define MC_SEQ_MISC5                                    0x2a54
+#define MC_SEQ_MISC6                                    0x2a58
+
+#define MC_SEQ_MISC7                                    0x2a64
+
+#define MC_SEQ_RAS_TIMING_LP                            0x2a6c
+#define MC_SEQ_CAS_TIMING_LP                            0x2a70
+#define MC_SEQ_MISC_TIMING_LP                           0x2a74
+#define MC_SEQ_MISC_TIMING2_LP                          0x2a78
+#define MC_SEQ_WR_CTL_D0_LP                             0x2a7c
+#define MC_SEQ_WR_CTL_D1_LP                             0x2a80
+#define MC_SEQ_PMG_CMD_EMRS_LP                          0x2a84
+#define MC_SEQ_PMG_CMD_MRS_LP                           0x2a88
+
+#define MC_PMG_CMD_MRS                                  0x2aac
+
+#define MC_SEQ_RD_CTL_D0_LP                             0x2b1c
+#define MC_SEQ_RD_CTL_D1_LP                             0x2b20
+
+#define MC_PMG_CMD_MRS1                                 0x2b44
+#define MC_SEQ_PMG_CMD_MRS1_LP                          0x2b48
+#define MC_SEQ_PMG_TIMING_LP                            0x2b4c
+
+#define MC_SEQ_WR_CTL_2                                 0x2b54
+#define MC_SEQ_WR_CTL_2_LP                              0x2b58
+#define MC_PMG_CMD_MRS2                                 0x2b5c
+#define MC_SEQ_PMG_CMD_MRS2_LP                          0x2b60
+
+#define        MCLK_PWRMGT_CNTL                                0x2ba0
+#       define DLL_SPEED(x)                            ((x) << 0)
+#       define DLL_SPEED_MASK                          (0x1f << 0)
+#       define DLL_READY                                (1 << 6)
+#       define MC_INT_CNTL                              (1 << 7)
+#       define MRDCK0_PDNB                              (1 << 8)
+#       define MRDCK1_PDNB                              (1 << 9)
+#       define MRDCK0_RESET                             (1 << 16)
+#       define MRDCK1_RESET                             (1 << 17)
+#       define DLL_READY_READ                           (1 << 24)
+#define        DLL_CNTL                                        0x2ba4
+#       define MRDCK0_BYPASS                            (1 << 24)
+#       define MRDCK1_BYPASS                            (1 << 25)
+
+#define        MPLL_FUNC_CNTL                                  0x2bb4
+#define                BWCTRL(x)                               ((x) << 20)
+#define                BWCTRL_MASK                             (0xff << 20)
+#define        MPLL_FUNC_CNTL_1                                0x2bb8
+#define                VCO_MODE(x)                             ((x) << 0)
+#define                VCO_MODE_MASK                           (3 << 0)
+#define                CLKFRAC(x)                              ((x) << 4)
+#define                CLKFRAC_MASK                            (0xfff << 4)
+#define                CLKF(x)                                 ((x) << 16)
+#define                CLKF_MASK                               (0xfff << 16)
+#define        MPLL_FUNC_CNTL_2                                0x2bbc
+#define        MPLL_AD_FUNC_CNTL                               0x2bc0
+#define                YCLK_POST_DIV(x)                        ((x) << 0)
+#define                YCLK_POST_DIV_MASK                      (7 << 0)
+#define        MPLL_DQ_FUNC_CNTL                               0x2bc4
+#define                YCLK_SEL(x)                             ((x) << 4)
+#define                YCLK_SEL_MASK                           (1 << 4)
+
+#define        MPLL_SS1                                        0x2bcc
+#define                CLKV(x)                                 ((x) << 0)
+#define                CLKV_MASK                               (0x3ffffff << 0)
+#define        MPLL_SS2                                        0x2bd0
+#define                CLKS(x)                                 ((x) << 0)
+#define                CLKS_MASK                               (0xfff << 0)
+
 #define        HDP_HOST_PATH_CNTL                              0x2C00
 #define        HDP_NONSURFACE_BASE                             0x2C04
 #define        HDP_NONSURFACE_INFO                             0x2C08
 #define HDP_MISC_CNTL                                  0x2F4C
 #define        HDP_FLUSH_INVALIDATE_CACHE                      (1 << 0)
 
+#define ATC_MISC_CG                                    0x3350
+
 #define IH_RB_CNTL                                        0x3e00
 #       define IH_RB_ENABLE                               (1 << 0)
 #       define IH_IB_SIZE(x)                              ((x) << 1) /* log2 */
 #       define DC_HPDx_RX_INT_TIMER(x)                    ((x) << 16)
 #       define DC_HPDx_EN                                 (1 << 28)
 
+#define DPG_PIPE_STUTTER_CONTROL                          0x6cd4
+#       define STUTTER_ENABLE                             (1 << 0)
+
 /* 0x6e98, 0x7a98, 0x10698, 0x11298, 0x11e98, 0x12a98 */
 #define CRTC_STATUS_FRAME_COUNT                         0x6e98
 
 
 #define        SQC_CACHES                                      0x8C08
 
+#define SQ_POWER_THROTTLE                               0x8e58
+#define                MIN_POWER(x)                            ((x) << 0)
+#define                MIN_POWER_MASK                          (0x3fff << 0)
+#define                MIN_POWER_SHIFT                         0
+#define                MAX_POWER(x)                            ((x) << 16)
+#define                MAX_POWER_MASK                          (0x3fff << 16)
+#define                MAX_POWER_SHIFT                         0
+#define SQ_POWER_THROTTLE2                              0x8e5c
+#define                MAX_POWER_DELTA(x)                      ((x) << 0)
+#define                MAX_POWER_DELTA_MASK                    (0x3fff << 0)
+#define                MAX_POWER_DELTA_SHIFT                   0
+#define                STI_SIZE(x)                             ((x) << 16)
+#define                STI_SIZE_MASK                           (0x3ff << 16)
+#define                STI_SIZE_SHIFT                          16
+#define                LTI_RATIO(x)                            ((x) << 27)
+#define                LTI_RATIO_MASK                          (0xf << 27)
+#define                LTI_RATIO_SHIFT                         27
+
 #define        SX_DEBUG_1                                      0x9060
 
 #define        SPI_STATIC_THREAD_MGMT_1                        0x90E0
 #define        CGTS_USER_TCC_DISABLE                           0x914C
 #define                TCC_DISABLE_MASK                                0xFFFF0000
 #define                TCC_DISABLE_SHIFT                               16
+#define        CGTS_SM_CTRL_REG                                0x9150
+#define                OVERRIDE                                (1 << 21)
+#define                LS_OVERRIDE                             (1 << 22)
+
+#define        SPI_LB_CU_MASK                                  0x9354
 
 #define        TA_CNTL_AUX                                     0x9508
 
 #define        CB_PERFCOUNTER3_SELECT0                         0x9a38
 #define        CB_PERFCOUNTER3_SELECT1                         0x9a3c
 
+#define        CB_CGTT_SCLK_CTRL                               0x9a60
+
 #define        GC_USER_RB_BACKEND_DISABLE                      0x9B7C
 #define                BACKEND_DISABLE_MASK                    0x00FF0000
 #define                BACKEND_DISABLE_SHIFT                   16
 #       define CP_RINGID1_INT_STAT                      (1 << 30)
 #       define CP_RINGID0_INT_STAT                      (1 << 31)
 
+#define        CP_MEM_SLP_CNTL                                 0xC1E4
+#       define CP_MEM_LS_EN                             (1 << 0)
+
 #define        CP_DEBUG                                        0xC1FC
 
 #define RLC_CNTL                                          0xC300
 #define RLC_RL_BASE                                       0xC304
 #define RLC_RL_SIZE                                       0xC308
 #define RLC_LB_CNTL                                       0xC30C
+#       define LOAD_BALANCE_ENABLE                        (1 << 0)
 #define RLC_SAVE_AND_RESTORE_BASE                         0xC310
 #define RLC_LB_CNTR_MAX                                   0xC314
 #define RLC_LB_CNTR_INIT                                  0xC318
 #define RLC_CAPTURE_GPU_CLOCK_COUNT                       0xC340
 #define RLC_MC_CNTL                                       0xC344
 #define RLC_UCODE_CNTL                                    0xC348
+#define RLC_STAT                                          0xC34C
+#       define RLC_BUSY_STATUS                            (1 << 0)
+#       define GFX_POWER_STATUS                           (1 << 1)
+#       define GFX_CLOCK_STATUS                           (1 << 2)
+#       define GFX_LS_STATUS                              (1 << 3)
+
+#define        RLC_PG_CNTL                                     0xC35C
+#      define GFX_PG_ENABLE                            (1 << 0)
+#      define GFX_PG_SRC                               (1 << 1)
+
+#define        RLC_CGTT_MGCG_OVERRIDE                          0xC400
+#define        RLC_CGCG_CGLS_CTRL                              0xC404
+#      define CGCG_EN                                  (1 << 0)
+#      define CGLS_EN                                  (1 << 1)
+
+#define        RLC_TTOP_D                                      0xC414
+#      define RLC_PUD(x)                               ((x) << 0)
+#      define RLC_PUD_MASK                             (0xff << 0)
+#      define RLC_PDD(x)                               ((x) << 8)
+#      define RLC_PDD_MASK                             (0xff << 8)
+#      define RLC_TTPD(x)                              ((x) << 16)
+#      define RLC_TTPD_MASK                            (0xff << 16)
+#      define RLC_MSD(x)                               ((x) << 24)
+#      define RLC_MSD_MASK                             (0xff << 24)
+
+#define RLC_LB_INIT_CU_MASK                               0xC41C
+
+#define        RLC_PG_AO_CU_MASK                               0xC42C
+#define        RLC_MAX_PG_CU                                   0xC430
+#      define MAX_PU_CU(x)                             ((x) << 0)
+#      define MAX_PU_CU_MASK                           (0xff << 0)
+#define        RLC_AUTO_PG_CTRL                                0xC434
+#      define AUTO_PG_EN                               (1 << 0)
+#      define GRBM_REG_SGIT(x)                         ((x) << 3)
+#      define GRBM_REG_SGIT_MASK                       (0xffff << 3)
+#      define PG_AFTER_GRBM_REG_ST(x)                  ((x) << 19)
+#      define PG_AFTER_GRBM_REG_ST_MASK                (0x1fff << 19)
+
+#define RLC_SERDES_WR_MASTER_MASK_0                       0xC454
+#define RLC_SERDES_WR_MASTER_MASK_1                       0xC458
+#define RLC_SERDES_WR_CTRL                                0xC45C
+
+#define RLC_SERDES_MASTER_BUSY_0                          0xC464
+#define RLC_SERDES_MASTER_BUSY_1                          0xC468
+
+#define RLC_GCPM_GENERAL_3                                0xC478
+
+#define        DB_RENDER_CONTROL                               0x28000
+
+#define DB_DEPTH_INFO                                   0x2803c
 
 #define PA_SC_RASTER_CONFIG                             0x28350
 #       define RASTER_CONFIG_RB_MAP_0                   0
 #       define THREAD_TRACE_FLUSH                       (54 << 0)
 #       define THREAD_TRACE_FINISH                      (55 << 0)
 
+/* PIF PHY0 registers idx/data 0x8/0xc */
+#define PB0_PIF_CNTL                                      0x10
+#       define LS2_EXIT_TIME(x)                           ((x) << 17)
+#       define LS2_EXIT_TIME_MASK                         (0x7 << 17)
+#       define LS2_EXIT_TIME_SHIFT                        17
+#define PB0_PIF_PAIRING                                   0x11
+#       define MULTI_PIF                                  (1 << 25)
+#define PB0_PIF_PWRDOWN_0                                 0x12
+#       define PLL_POWER_STATE_IN_TXS2_0(x)               ((x) << 7)
+#       define PLL_POWER_STATE_IN_TXS2_0_MASK             (0x7 << 7)
+#       define PLL_POWER_STATE_IN_TXS2_0_SHIFT            7
+#       define PLL_POWER_STATE_IN_OFF_0(x)                ((x) << 10)
+#       define PLL_POWER_STATE_IN_OFF_0_MASK              (0x7 << 10)
+#       define PLL_POWER_STATE_IN_OFF_0_SHIFT             10
+#       define PLL_RAMP_UP_TIME_0(x)                      ((x) << 24)
+#       define PLL_RAMP_UP_TIME_0_MASK                    (0x7 << 24)
+#       define PLL_RAMP_UP_TIME_0_SHIFT                   24
+#define PB0_PIF_PWRDOWN_1                                 0x13
+#       define PLL_POWER_STATE_IN_TXS2_1(x)               ((x) << 7)
+#       define PLL_POWER_STATE_IN_TXS2_1_MASK             (0x7 << 7)
+#       define PLL_POWER_STATE_IN_TXS2_1_SHIFT            7
+#       define PLL_POWER_STATE_IN_OFF_1(x)                ((x) << 10)
+#       define PLL_POWER_STATE_IN_OFF_1_MASK              (0x7 << 10)
+#       define PLL_POWER_STATE_IN_OFF_1_SHIFT             10
+#       define PLL_RAMP_UP_TIME_1(x)                      ((x) << 24)
+#       define PLL_RAMP_UP_TIME_1_MASK                    (0x7 << 24)
+#       define PLL_RAMP_UP_TIME_1_SHIFT                   24
+
+#define PB0_PIF_PWRDOWN_2                                 0x17
+#       define PLL_POWER_STATE_IN_TXS2_2(x)               ((x) << 7)
+#       define PLL_POWER_STATE_IN_TXS2_2_MASK             (0x7 << 7)
+#       define PLL_POWER_STATE_IN_TXS2_2_SHIFT            7
+#       define PLL_POWER_STATE_IN_OFF_2(x)                ((x) << 10)
+#       define PLL_POWER_STATE_IN_OFF_2_MASK              (0x7 << 10)
+#       define PLL_POWER_STATE_IN_OFF_2_SHIFT             10
+#       define PLL_RAMP_UP_TIME_2(x)                      ((x) << 24)
+#       define PLL_RAMP_UP_TIME_2_MASK                    (0x7 << 24)
+#       define PLL_RAMP_UP_TIME_2_SHIFT                   24
+#define PB0_PIF_PWRDOWN_3                                 0x18
+#       define PLL_POWER_STATE_IN_TXS2_3(x)               ((x) << 7)
+#       define PLL_POWER_STATE_IN_TXS2_3_MASK             (0x7 << 7)
+#       define PLL_POWER_STATE_IN_TXS2_3_SHIFT            7
+#       define PLL_POWER_STATE_IN_OFF_3(x)                ((x) << 10)
+#       define PLL_POWER_STATE_IN_OFF_3_MASK              (0x7 << 10)
+#       define PLL_POWER_STATE_IN_OFF_3_SHIFT             10
+#       define PLL_RAMP_UP_TIME_3(x)                      ((x) << 24)
+#       define PLL_RAMP_UP_TIME_3_MASK                    (0x7 << 24)
+#       define PLL_RAMP_UP_TIME_3_SHIFT                   24
+/* PIF PHY1 registers idx/data 0x10/0x14 */
+#define PB1_PIF_CNTL                                      0x10
+#define PB1_PIF_PAIRING                                   0x11
+#define PB1_PIF_PWRDOWN_0                                 0x12
+#define PB1_PIF_PWRDOWN_1                                 0x13
+
+#define PB1_PIF_PWRDOWN_2                                 0x17
+#define PB1_PIF_PWRDOWN_3                                 0x18
+/* PCIE registers idx/data 0x30/0x34 */
+#define PCIE_CNTL2                                        0x1c /* PCIE */
+#       define SLV_MEM_LS_EN                              (1 << 16)
+#       define MST_MEM_LS_EN                              (1 << 18)
+#       define REPLAY_MEM_LS_EN                           (1 << 19)
+#define PCIE_LC_STATUS1                                   0x28 /* PCIE */
+#       define LC_REVERSE_RCVR                            (1 << 0)
+#       define LC_REVERSE_XMIT                            (1 << 1)
+#       define LC_OPERATING_LINK_WIDTH_MASK               (0x7 << 2)
+#       define LC_OPERATING_LINK_WIDTH_SHIFT              2
+#       define LC_DETECTED_LINK_WIDTH_MASK                (0x7 << 5)
+#       define LC_DETECTED_LINK_WIDTH_SHIFT               5
+
+#define PCIE_P_CNTL                                       0x40 /* PCIE */
+#       define P_IGNORE_EDB_ERR                           (1 << 6)
+
+/* PCIE PORT registers idx/data 0x38/0x3c */
+#define PCIE_LC_CNTL                                      0xa0
+#       define LC_L0S_INACTIVITY(x)                       ((x) << 8)
+#       define LC_L0S_INACTIVITY_MASK                     (0xf << 8)
+#       define LC_L0S_INACTIVITY_SHIFT                    8
+#       define LC_L1_INACTIVITY(x)                        ((x) << 12)
+#       define LC_L1_INACTIVITY_MASK                      (0xf << 12)
+#       define LC_L1_INACTIVITY_SHIFT                     12
+#       define LC_PMI_TO_L1_DIS                           (1 << 16)
+#       define LC_ASPM_TO_L1_DIS                          (1 << 24)
+#define PCIE_LC_LINK_WIDTH_CNTL                           0xa2 /* PCIE_P */
+#       define LC_LINK_WIDTH_SHIFT                        0
+#       define LC_LINK_WIDTH_MASK                         0x7
+#       define LC_LINK_WIDTH_X0                           0
+#       define LC_LINK_WIDTH_X1                           1
+#       define LC_LINK_WIDTH_X2                           2
+#       define LC_LINK_WIDTH_X4                           3
+#       define LC_LINK_WIDTH_X8                           4
+#       define LC_LINK_WIDTH_X16                          6
+#       define LC_LINK_WIDTH_RD_SHIFT                     4
+#       define LC_LINK_WIDTH_RD_MASK                      0x70
+#       define LC_RECONFIG_ARC_MISSING_ESCAPE             (1 << 7)
+#       define LC_RECONFIG_NOW                            (1 << 8)
+#       define LC_RENEGOTIATION_SUPPORT                   (1 << 9)
+#       define LC_RENEGOTIATE_EN                          (1 << 10)
+#       define LC_SHORT_RECONFIG_EN                       (1 << 11)
+#       define LC_UPCONFIGURE_SUPPORT                     (1 << 12)
+#       define LC_UPCONFIGURE_DIS                         (1 << 13)
+#       define LC_DYN_LANES_PWR_STATE(x)                  ((x) << 21)
+#       define LC_DYN_LANES_PWR_STATE_MASK                (0x3 << 21)
+#       define LC_DYN_LANES_PWR_STATE_SHIFT               21
+#define PCIE_LC_N_FTS_CNTL                                0xa3 /* PCIE_P */
+#       define LC_XMIT_N_FTS(x)                           ((x) << 0)
+#       define LC_XMIT_N_FTS_MASK                         (0xff << 0)
+#       define LC_XMIT_N_FTS_SHIFT                        0
+#       define LC_XMIT_N_FTS_OVERRIDE_EN                  (1 << 8)
+#       define LC_N_FTS_MASK                              (0xff << 24)
+#define PCIE_LC_SPEED_CNTL                                0xa4 /* PCIE_P */
+#       define LC_GEN2_EN_STRAP                           (1 << 0)
+#       define LC_GEN3_EN_STRAP                           (1 << 1)
+#       define LC_TARGET_LINK_SPEED_OVERRIDE_EN           (1 << 2)
+#       define LC_TARGET_LINK_SPEED_OVERRIDE_MASK         (0x3 << 3)
+#       define LC_TARGET_LINK_SPEED_OVERRIDE_SHIFT        3
+#       define LC_FORCE_EN_SW_SPEED_CHANGE                (1 << 5)
+#       define LC_FORCE_DIS_SW_SPEED_CHANGE               (1 << 6)
+#       define LC_FORCE_EN_HW_SPEED_CHANGE                (1 << 7)
+#       define LC_FORCE_DIS_HW_SPEED_CHANGE               (1 << 8)
+#       define LC_INITIATE_LINK_SPEED_CHANGE              (1 << 9)
+#       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK      (0x3 << 10)
+#       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT     10
+#       define LC_CURRENT_DATA_RATE_MASK                  (0x3 << 13) /* 0/1/2 = gen1/2/3 */
+#       define LC_CURRENT_DATA_RATE_SHIFT                 13
+#       define LC_CLR_FAILED_SPD_CHANGE_CNT               (1 << 16)
+#       define LC_OTHER_SIDE_EVER_SENT_GEN2               (1 << 18)
+#       define LC_OTHER_SIDE_SUPPORTS_GEN2                (1 << 19)
+#       define LC_OTHER_SIDE_EVER_SENT_GEN3               (1 << 20)
+#       define LC_OTHER_SIDE_SUPPORTS_GEN3                (1 << 21)
+
+#define PCIE_LC_CNTL2                                     0xb1
+#       define LC_ALLOW_PDWN_IN_L1                        (1 << 17)
+#       define LC_ALLOW_PDWN_IN_L23                       (1 << 18)
+
+#define PCIE_LC_CNTL3                                     0xb5 /* PCIE_P */
+#       define LC_GO_TO_RECOVERY                          (1 << 30)
+#define PCIE_LC_CNTL4                                     0xb6 /* PCIE_P */
+#       define LC_REDO_EQ                                 (1 << 5)
+#       define LC_SET_QUIESCE                             (1 << 13)
+
 /*
  * UVD
  */
 #define UVD_RBC_RB_RPTR                                        0xF690
 #define UVD_RBC_RB_WPTR                                        0xF694
 
+#define        UVD_CGC_CTRL                                    0xF4B0
+#      define DCM                                      (1 << 0)
+#      define CG_DT(x)                                 ((x) << 2)
+#      define CG_DT_MASK                               (0xf << 2)
+#      define CLK_OD(x)                                ((x) << 6)
+#      define CLK_OD_MASK                              (0x1f << 6)
+
+ /* UVD CTX indirect */
+#define        UVD_CGC_MEM_CTRL                                0xC0
+#define        UVD_CGC_CTRL2                                   0xC1
+#      define DYN_OR_EN                                (1 << 0)
+#      define DYN_RR_EN                                (1 << 1)
+#      define G_DIV_ID(x)                              ((x) << 2)
+#      define G_DIV_ID_MASK                            (0x7 << 2)
+
 /*
  * PM4
  */
 #       define DMA_IDLE                                   (1 << 0)
 #define DMA_TILING_CONFIG                                0xd0b8
 
+#define        DMA_PG                                          0xd0d4
+#      define PG_CNTL_ENABLE                           (1 << 0)
+#define        DMA_PGFSM_CONFIG                                0xd0d8
+#define        DMA_PGFSM_WRITE                                 0xd0dc
+
 #define DMA_PACKET(cmd, b, t, s, n)    ((((cmd) & 0xF) << 28) |        \
                                         (((b) & 0x1) << 26) |          \
                                         (((t) & 0x1) << 23) |          \
diff --git a/drivers/gpu/drm/radeon/sislands_smc.h b/drivers/gpu/drm/radeon/sislands_smc.h
new file mode 100644 (file)
index 0000000..5578e98
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef PP_SISLANDS_SMC_H
+#define PP_SISLANDS_SMC_H
+
+#include "ppsmc.h"
+
+#pragma pack(push, 1)
+
+#define SISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
+
+struct PP_SIslands_Dpm2PerfLevel
+{
+    uint8_t MaxPS;
+    uint8_t TgtAct;
+    uint8_t MaxPS_StepInc;
+    uint8_t MaxPS_StepDec;
+    uint8_t PSSamplingTime;
+    uint8_t NearTDPDec;
+    uint8_t AboveSafeInc;
+    uint8_t BelowSafeInc;
+    uint8_t PSDeltaLimit;
+    uint8_t PSDeltaWin;
+    uint16_t PwrEfficiencyRatio;
+    uint8_t Reserved[4];
+};
+
+typedef struct PP_SIslands_Dpm2PerfLevel PP_SIslands_Dpm2PerfLevel;
+
+struct PP_SIslands_DPM2Status
+{
+    uint32_t    dpm2Flags;
+    uint8_t     CurrPSkip;
+    uint8_t     CurrPSkipPowerShift;
+    uint8_t     CurrPSkipTDP;
+    uint8_t     CurrPSkipOCP;
+    uint8_t     MaxSPLLIndex;
+    uint8_t     MinSPLLIndex;
+    uint8_t     CurrSPLLIndex;
+    uint8_t     InfSweepMode;
+    uint8_t     InfSweepDir;
+    uint8_t     TDPexceeded;
+    uint8_t     reserved;
+    uint8_t     SwitchDownThreshold;
+    uint32_t    SwitchDownCounter;
+    uint32_t    SysScalingFactor;
+};
+
+typedef struct PP_SIslands_DPM2Status PP_SIslands_DPM2Status;
+
+struct PP_SIslands_DPM2Parameters
+{
+    uint32_t    TDPLimit;
+    uint32_t    NearTDPLimit;
+    uint32_t    SafePowerLimit;
+    uint32_t    PowerBoostLimit;
+    uint32_t    MinLimitDelta;
+};
+typedef struct PP_SIslands_DPM2Parameters PP_SIslands_DPM2Parameters;
+
+struct PP_SIslands_PAPMStatus
+{
+    uint32_t    EstimatedDGPU_T;
+    uint32_t    EstimatedDGPU_P;
+    uint32_t    EstimatedAPU_T;
+    uint32_t    EstimatedAPU_P;
+    uint8_t     dGPU_T_Limit_Exceeded;
+    uint8_t     reserved[3];
+};
+typedef struct PP_SIslands_PAPMStatus PP_SIslands_PAPMStatus;
+
+struct PP_SIslands_PAPMParameters
+{
+    uint32_t    NearTDPLimitTherm;
+    uint32_t    NearTDPLimitPAPM;
+    uint32_t    PlatformPowerLimit;
+    uint32_t    dGPU_T_Limit;
+    uint32_t    dGPU_T_Warning;
+    uint32_t    dGPU_T_Hysteresis;
+};
+typedef struct PP_SIslands_PAPMParameters PP_SIslands_PAPMParameters;
+
+struct SISLANDS_SMC_SCLK_VALUE
+{
+    uint32_t    vCG_SPLL_FUNC_CNTL;
+    uint32_t    vCG_SPLL_FUNC_CNTL_2;
+    uint32_t    vCG_SPLL_FUNC_CNTL_3;
+    uint32_t    vCG_SPLL_FUNC_CNTL_4;
+    uint32_t    vCG_SPLL_SPREAD_SPECTRUM;
+    uint32_t    vCG_SPLL_SPREAD_SPECTRUM_2;
+    uint32_t    sclk_value;
+};
+
+typedef struct SISLANDS_SMC_SCLK_VALUE SISLANDS_SMC_SCLK_VALUE;
+
+struct SISLANDS_SMC_MCLK_VALUE
+{
+    uint32_t    vMPLL_FUNC_CNTL;
+    uint32_t    vMPLL_FUNC_CNTL_1;
+    uint32_t    vMPLL_FUNC_CNTL_2;
+    uint32_t    vMPLL_AD_FUNC_CNTL;
+    uint32_t    vMPLL_DQ_FUNC_CNTL;
+    uint32_t    vMCLK_PWRMGT_CNTL;
+    uint32_t    vDLL_CNTL;
+    uint32_t    vMPLL_SS;
+    uint32_t    vMPLL_SS2;
+    uint32_t    mclk_value;
+};
+
+typedef struct SISLANDS_SMC_MCLK_VALUE SISLANDS_SMC_MCLK_VALUE;
+
+struct SISLANDS_SMC_VOLTAGE_VALUE
+{
+    uint16_t    value;
+    uint8_t     index;
+    uint8_t     phase_settings;
+};
+
+typedef struct SISLANDS_SMC_VOLTAGE_VALUE SISLANDS_SMC_VOLTAGE_VALUE;
+
+struct SISLANDS_SMC_HW_PERFORMANCE_LEVEL
+{
+    uint8_t                     ACIndex;
+    uint8_t                     displayWatermark;
+    uint8_t                     gen2PCIE;
+    uint8_t                     UVDWatermark;
+    uint8_t                     VCEWatermark;
+    uint8_t                     strobeMode;
+    uint8_t                     mcFlags;
+    uint8_t                     padding;
+    uint32_t                    aT;
+    uint32_t                    bSP;
+    SISLANDS_SMC_SCLK_VALUE     sclk;
+    SISLANDS_SMC_MCLK_VALUE     mclk;
+    SISLANDS_SMC_VOLTAGE_VALUE  vddc;
+    SISLANDS_SMC_VOLTAGE_VALUE  mvdd;
+    SISLANDS_SMC_VOLTAGE_VALUE  vddci;
+    SISLANDS_SMC_VOLTAGE_VALUE  std_vddc;
+    uint8_t                     hysteresisUp;
+    uint8_t                     hysteresisDown;
+    uint8_t                     stateFlags;
+    uint8_t                     arbRefreshState;
+    uint32_t                    SQPowerThrottle;
+    uint32_t                    SQPowerThrottle_2;
+    uint32_t                    MaxPoweredUpCU;
+    SISLANDS_SMC_VOLTAGE_VALUE  high_temp_vddc;
+    SISLANDS_SMC_VOLTAGE_VALUE  low_temp_vddc;
+    uint32_t                    reserved[2];
+    PP_SIslands_Dpm2PerfLevel   dpm2;
+};
+
+#define SISLANDS_SMC_STROBE_RATIO    0x0F
+#define SISLANDS_SMC_STROBE_ENABLE   0x10
+
+#define SISLANDS_SMC_MC_EDC_RD_FLAG  0x01
+#define SISLANDS_SMC_MC_EDC_WR_FLAG  0x02
+#define SISLANDS_SMC_MC_RTT_ENABLE   0x04
+#define SISLANDS_SMC_MC_STUTTER_EN   0x08
+#define SISLANDS_SMC_MC_PG_EN        0x10
+
+typedef struct SISLANDS_SMC_HW_PERFORMANCE_LEVEL SISLANDS_SMC_HW_PERFORMANCE_LEVEL;
+
+struct SISLANDS_SMC_SWSTATE
+{
+    uint8_t                             flags;
+    uint8_t                             levelCount;
+    uint8_t                             padding2;
+    uint8_t                             padding3;
+    SISLANDS_SMC_HW_PERFORMANCE_LEVEL   levels[1];
+};
+
+typedef struct SISLANDS_SMC_SWSTATE SISLANDS_SMC_SWSTATE;
+
+#define SISLANDS_SMC_VOLTAGEMASK_VDDC  0
+#define SISLANDS_SMC_VOLTAGEMASK_MVDD  1
+#define SISLANDS_SMC_VOLTAGEMASK_VDDCI 2
+#define SISLANDS_SMC_VOLTAGEMASK_MAX   4
+
+struct SISLANDS_SMC_VOLTAGEMASKTABLE
+{
+    uint32_t lowMask[SISLANDS_SMC_VOLTAGEMASK_MAX];
+};
+
+typedef struct SISLANDS_SMC_VOLTAGEMASKTABLE SISLANDS_SMC_VOLTAGEMASKTABLE;
+
+#define SISLANDS_MAX_NO_VREG_STEPS 32
+
+struct SISLANDS_SMC_STATETABLE
+{
+    uint8_t                             thermalProtectType;
+    uint8_t                             systemFlags;
+    uint8_t                             maxVDDCIndexInPPTable;
+    uint8_t                             extraFlags;
+    uint32_t                            lowSMIO[SISLANDS_MAX_NO_VREG_STEPS];
+    SISLANDS_SMC_VOLTAGEMASKTABLE       voltageMaskTable;
+    SISLANDS_SMC_VOLTAGEMASKTABLE       phaseMaskTable;
+    PP_SIslands_DPM2Parameters          dpm2Params;
+    SISLANDS_SMC_SWSTATE                initialState;
+    SISLANDS_SMC_SWSTATE                ACPIState;
+    SISLANDS_SMC_SWSTATE                ULVState;
+    SISLANDS_SMC_SWSTATE                driverState;
+    SISLANDS_SMC_HW_PERFORMANCE_LEVEL   dpmLevels[SISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1];
+};
+
+typedef struct SISLANDS_SMC_STATETABLE SISLANDS_SMC_STATETABLE;
+
+#define SI_SMC_SOFT_REGISTER_mclk_chg_timeout         0x0
+#define SI_SMC_SOFT_REGISTER_delay_vreg               0xC
+#define SI_SMC_SOFT_REGISTER_delay_acpi               0x28
+#define SI_SMC_SOFT_REGISTER_seq_index                0x5C
+#define SI_SMC_SOFT_REGISTER_mvdd_chg_time            0x60
+#define SI_SMC_SOFT_REGISTER_mclk_switch_lim          0x70
+#define SI_SMC_SOFT_REGISTER_watermark_threshold      0x78
+#define SI_SMC_SOFT_REGISTER_phase_shedding_delay     0x88
+#define SI_SMC_SOFT_REGISTER_ulv_volt_change_delay    0x8C
+#define SI_SMC_SOFT_REGISTER_mc_block_delay           0x98
+#define SI_SMC_SOFT_REGISTER_ticks_per_us             0xA8
+#define SI_SMC_SOFT_REGISTER_crtc_index               0xC4
+#define SI_SMC_SOFT_REGISTER_mclk_change_block_cp_min 0xC8
+#define SI_SMC_SOFT_REGISTER_mclk_change_block_cp_max 0xCC
+#define SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width  0xF4
+#define SI_SMC_SOFT_REGISTER_tdr_is_about_to_happen   0xFC
+#define SI_SMC_SOFT_REGISTER_vr_hot_gpio              0x100
+
+#define SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES 16
+#define SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES 32
+
+#define SMC_SISLANDS_SCALE_I  7
+#define SMC_SISLANDS_SCALE_R 12
+
+struct PP_SIslands_CacConfig
+{
+    uint16_t   cac_lkge_lut[SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES];
+    uint32_t   lkge_lut_V0;
+    uint32_t   lkge_lut_Vstep;
+    uint32_t   WinTime;
+    uint32_t   R_LL;
+    uint32_t   calculation_repeats;
+    uint32_t   l2numWin_TDP;
+    uint32_t   dc_cac;
+    uint8_t    lts_truncate_n;
+    uint8_t    SHIFT_N;
+    uint8_t    log2_PG_LKG_SCALE;
+    uint8_t    cac_temp;
+    uint32_t   lkge_lut_T0;
+    uint32_t   lkge_lut_Tstep;
+};
+
+typedef struct PP_SIslands_CacConfig PP_SIslands_CacConfig;
+
+#define SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE 16
+#define SMC_SISLANDS_MC_REGISTER_ARRAY_SET_COUNT 20
+
+struct SMC_SIslands_MCRegisterAddress
+{
+    uint16_t s0;
+    uint16_t s1;
+};
+
+typedef struct SMC_SIslands_MCRegisterAddress SMC_SIslands_MCRegisterAddress;
+
+struct SMC_SIslands_MCRegisterSet
+{
+    uint32_t value[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+typedef struct SMC_SIslands_MCRegisterSet SMC_SIslands_MCRegisterSet;
+
+struct SMC_SIslands_MCRegisters
+{
+    uint8_t                             last;
+    uint8_t                             reserved[3];
+    SMC_SIslands_MCRegisterAddress      address[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+    SMC_SIslands_MCRegisterSet          data[SMC_SISLANDS_MC_REGISTER_ARRAY_SET_COUNT];
+};
+
+typedef struct SMC_SIslands_MCRegisters SMC_SIslands_MCRegisters;
+
+struct SMC_SIslands_MCArbDramTimingRegisterSet
+{
+    uint32_t mc_arb_dram_timing;
+    uint32_t mc_arb_dram_timing2;
+    uint8_t  mc_arb_rfsh_rate;
+    uint8_t  mc_arb_burst_time;
+    uint8_t  padding[2];
+};
+
+typedef struct SMC_SIslands_MCArbDramTimingRegisterSet SMC_SIslands_MCArbDramTimingRegisterSet;
+
+struct SMC_SIslands_MCArbDramTimingRegisters
+{
+    uint8_t                                     arb_current;
+    uint8_t                                     reserved[3];
+    SMC_SIslands_MCArbDramTimingRegisterSet     data[16];
+};
+
+typedef struct SMC_SIslands_MCArbDramTimingRegisters SMC_SIslands_MCArbDramTimingRegisters;
+
+struct SMC_SISLANDS_SPLL_DIV_TABLE
+{
+    uint32_t    freq[256];
+    uint32_t    ss[256];
+};
+
+#define SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK  0x01ffffff
+#define SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT 0
+#define SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK   0xfe000000
+#define SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT  25
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK   0x000fffff
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT  0
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK   0xfff00000
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT  20
+
+typedef struct SMC_SISLANDS_SPLL_DIV_TABLE SMC_SISLANDS_SPLL_DIV_TABLE;
+
+#define SMC_SISLANDS_DTE_MAX_FILTER_STAGES 5
+
+#define SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE 16
+
+struct Smc_SIslands_DTE_Configuration
+{
+    uint32_t tau[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+    uint32_t R[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+    uint32_t K;
+    uint32_t T0;
+    uint32_t MaxT;
+    uint8_t  WindowSize;
+    uint8_t  Tdep_count;
+    uint8_t  temp_select;
+    uint8_t  DTE_mode;
+    uint8_t  T_limits[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+    uint32_t Tdep_tau[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+    uint32_t Tdep_R[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+    uint32_t Tthreshold;
+};
+
+typedef struct Smc_SIslands_DTE_Configuration Smc_SIslands_DTE_Configuration;
+
+#define SMC_SISLANDS_DTE_STATUS_FLAG_DTE_ON 1
+
+#define SISLANDS_SMC_FIRMWARE_HEADER_LOCATION 0x10000
+
+#define SISLANDS_SMC_FIRMWARE_HEADER_version                   0x0
+#define SISLANDS_SMC_FIRMWARE_HEADER_flags                     0x4
+#define SISLANDS_SMC_FIRMWARE_HEADER_softRegisters             0xC
+#define SISLANDS_SMC_FIRMWARE_HEADER_stateTable                0x10
+#define SISLANDS_SMC_FIRMWARE_HEADER_fanTable                  0x14
+#define SISLANDS_SMC_FIRMWARE_HEADER_CacConfigTable            0x18
+#define SISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable           0x24
+#define SISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable 0x30
+#define SISLANDS_SMC_FIRMWARE_HEADER_spllTable                 0x38
+#define SISLANDS_SMC_FIRMWARE_HEADER_DteConfiguration          0x40
+#define SISLANDS_SMC_FIRMWARE_HEADER_PAPMParameters            0x48
+
+#pragma pack(pop)
+
+int si_set_smc_sram_address(struct radeon_device *rdev,
+                           u32 smc_address, u32 limit);
+int si_copy_bytes_to_smc(struct radeon_device *rdev,
+                        u32 smc_start_address,
+                        const u8 *src, u32 byte_count, u32 limit);
+void si_start_smc(struct radeon_device *rdev);
+void si_reset_smc(struct radeon_device *rdev);
+int si_program_jump_on_start(struct radeon_device *rdev);
+void si_stop_smc_clock(struct radeon_device *rdev);
+void si_start_smc_clock(struct radeon_device *rdev);
+bool si_is_smc_running(struct radeon_device *rdev);
+PPSMC_Result si_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg);
+PPSMC_Result si_wait_for_smc_inactive(struct radeon_device *rdev);
+int si_load_smc_ucode(struct radeon_device *rdev, u32 limit);
+int si_read_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
+                          u32 *value, u32 limit);
+int si_write_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
+                           u32 value, u32 limit);
+
+#endif
+
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c
new file mode 100644 (file)
index 0000000..11b6b99
--- /dev/null
@@ -0,0 +1,1876 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "sumod.h"
+#include "r600_dpm.h"
+#include "cypress_dpm.h"
+#include "sumo_dpm.h"
+#include <linux/seq_file.h>
+
+#define SUMO_MAX_DEEPSLEEP_DIVIDER_ID 5
+#define SUMO_MINIMUM_ENGINE_CLOCK 800
+#define BOOST_DPM_LEVEL 7
+
+static const u32 sumo_utc[SUMO_PM_NUMBER_OF_TC] =
+{
+       SUMO_UTC_DFLT_00,
+       SUMO_UTC_DFLT_01,
+       SUMO_UTC_DFLT_02,
+       SUMO_UTC_DFLT_03,
+       SUMO_UTC_DFLT_04,
+       SUMO_UTC_DFLT_05,
+       SUMO_UTC_DFLT_06,
+       SUMO_UTC_DFLT_07,
+       SUMO_UTC_DFLT_08,
+       SUMO_UTC_DFLT_09,
+       SUMO_UTC_DFLT_10,
+       SUMO_UTC_DFLT_11,
+       SUMO_UTC_DFLT_12,
+       SUMO_UTC_DFLT_13,
+       SUMO_UTC_DFLT_14,
+};
+
+static const u32 sumo_dtc[SUMO_PM_NUMBER_OF_TC] =
+{
+       SUMO_DTC_DFLT_00,
+       SUMO_DTC_DFLT_01,
+       SUMO_DTC_DFLT_02,
+       SUMO_DTC_DFLT_03,
+       SUMO_DTC_DFLT_04,
+       SUMO_DTC_DFLT_05,
+       SUMO_DTC_DFLT_06,
+       SUMO_DTC_DFLT_07,
+       SUMO_DTC_DFLT_08,
+       SUMO_DTC_DFLT_09,
+       SUMO_DTC_DFLT_10,
+       SUMO_DTC_DFLT_11,
+       SUMO_DTC_DFLT_12,
+       SUMO_DTC_DFLT_13,
+       SUMO_DTC_DFLT_14,
+};
+
+struct sumo_ps *sumo_get_ps(struct radeon_ps *rps)
+{
+       struct sumo_ps *ps = rps->ps_priv;
+
+       return ps;
+}
+
+struct sumo_power_info *sumo_get_pi(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = rdev->pm.dpm.priv;
+
+       return pi;
+}
+
+static void sumo_gfx_clockgating_enable(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN);
+       else {
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN);
+               WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON);
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON);
+               RREG32(GB_ADDR_CONFIG);
+       }
+}
+
+#define CGCG_CGTT_LOCAL0_MASK 0xE5BFFFFF
+#define CGCG_CGTT_LOCAL1_MASK 0xEFFF07FF
+
+static void sumo_mg_clockgating_enable(struct radeon_device *rdev, bool enable)
+{
+       u32 local0;
+       u32 local1;
+
+       local0 = RREG32(CG_CGTT_LOCAL_0);
+       local1 = RREG32(CG_CGTT_LOCAL_1);
+
+       if (enable) {
+               WREG32(CG_CGTT_LOCAL_0, (0 & CGCG_CGTT_LOCAL0_MASK) | (local0 & ~CGCG_CGTT_LOCAL0_MASK) );
+               WREG32(CG_CGTT_LOCAL_1, (0 & CGCG_CGTT_LOCAL1_MASK) | (local1 & ~CGCG_CGTT_LOCAL1_MASK) );
+       } else {
+               WREG32(CG_CGTT_LOCAL_0, (0xFFFFFFFF & CGCG_CGTT_LOCAL0_MASK) | (local0 & ~CGCG_CGTT_LOCAL0_MASK) );
+               WREG32(CG_CGTT_LOCAL_1, (0xFFFFCFFF & CGCG_CGTT_LOCAL1_MASK) | (local1 & ~CGCG_CGTT_LOCAL1_MASK) );
+       }
+}
+
+static void sumo_program_git(struct radeon_device *rdev)
+{
+       u32 p, u;
+       u32 xclk = radeon_get_xclk(rdev);
+
+       r600_calculate_u_and_p(SUMO_GICST_DFLT,
+                              xclk, 16, &p, &u);
+
+       WREG32_P(CG_GIT, CG_GICST(p), ~CG_GICST_MASK);
+}
+
+static void sumo_program_grsd(struct radeon_device *rdev)
+{
+       u32 p, u;
+       u32 xclk = radeon_get_xclk(rdev);
+       u32 grs = 256 * 25 / 100;
+
+       r600_calculate_u_and_p(1, xclk, 14, &p, &u);
+
+       WREG32(CG_GCOOR, PHC(grs) | SDC(p) | SU(u));
+}
+
+void sumo_gfx_clockgating_initialize(struct radeon_device *rdev)
+{
+       sumo_program_git(rdev);
+       sumo_program_grsd(rdev);
+}
+
+static void sumo_gfx_powergating_initialize(struct radeon_device *rdev)
+{
+       u32 rcu_pwr_gating_cntl;
+       u32 p, u;
+       u32 p_c, p_p, d_p;
+       u32 r_t, i_t;
+       u32 xclk = radeon_get_xclk(rdev);
+
+       if (rdev->family == CHIP_PALM) {
+               p_c = 4;
+               d_p = 10;
+               r_t = 10;
+               i_t = 4;
+               p_p = 50 + 1000/200 + 6 * 32;
+       } else {
+               p_c = 16;
+               d_p = 50;
+               r_t = 50;
+               i_t  = 50;
+               p_p = 113;
+       }
+
+       WREG32(CG_SCRATCH2, 0x01B60A17);
+
+       r600_calculate_u_and_p(SUMO_GFXPOWERGATINGT_DFLT,
+                              xclk, 16, &p, &u);
+
+       WREG32_P(CG_PWR_GATING_CNTL, PGP(p) | PGU(u),
+                ~(PGP_MASK | PGU_MASK));
+
+       r600_calculate_u_and_p(SUMO_VOLTAGEDROPT_DFLT,
+                              xclk, 16, &p, &u);
+
+       WREG32_P(CG_CG_VOLTAGE_CNTL, PGP(p) | PGU(u),
+                ~(PGP_MASK | PGU_MASK));
+
+       if (rdev->family == CHIP_PALM) {
+               WREG32_RCU(RCU_PWR_GATING_SEQ0, 0x10103210);
+               WREG32_RCU(RCU_PWR_GATING_SEQ1, 0x10101010);
+       } else {
+               WREG32_RCU(RCU_PWR_GATING_SEQ0, 0x76543210);
+               WREG32_RCU(RCU_PWR_GATING_SEQ1, 0xFEDCBA98);
+       }
+
+       rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL);
+       rcu_pwr_gating_cntl &=
+               ~(RSVD_MASK | PCV_MASK | PGS_MASK);
+       rcu_pwr_gating_cntl |= PCV(p_c) | PGS(1) | PWR_GATING_EN;
+       if (rdev->family == CHIP_PALM) {
+               rcu_pwr_gating_cntl &= ~PCP_MASK;
+               rcu_pwr_gating_cntl |= PCP(0x77);
+       }
+       WREG32_RCU(RCU_PWR_GATING_CNTL, rcu_pwr_gating_cntl);
+
+       rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_2);
+       rcu_pwr_gating_cntl &= ~(MPPU_MASK | MPPD_MASK);
+       rcu_pwr_gating_cntl |= MPPU(p_p) | MPPD(50);
+       WREG32_RCU(RCU_PWR_GATING_CNTL_2, rcu_pwr_gating_cntl);
+
+       rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_3);
+       rcu_pwr_gating_cntl &= ~(DPPU_MASK | DPPD_MASK);
+       rcu_pwr_gating_cntl |= DPPU(d_p) | DPPD(50);
+       WREG32_RCU(RCU_PWR_GATING_CNTL_3, rcu_pwr_gating_cntl);
+
+       rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_4);
+       rcu_pwr_gating_cntl &= ~(RT_MASK | IT_MASK);
+       rcu_pwr_gating_cntl |= RT(r_t) | IT(i_t);
+       WREG32_RCU(RCU_PWR_GATING_CNTL_4, rcu_pwr_gating_cntl);
+
+       if (rdev->family == CHIP_PALM)
+               WREG32_RCU(RCU_PWR_GATING_CNTL_5, 0xA02);
+
+       sumo_smu_pg_init(rdev);
+
+       rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL);
+       rcu_pwr_gating_cntl &=
+               ~(RSVD_MASK | PCV_MASK | PGS_MASK);
+       rcu_pwr_gating_cntl |= PCV(p_c) | PGS(4) | PWR_GATING_EN;
+       if (rdev->family == CHIP_PALM) {
+               rcu_pwr_gating_cntl &= ~PCP_MASK;
+               rcu_pwr_gating_cntl |= PCP(0x77);
+       }
+       WREG32_RCU(RCU_PWR_GATING_CNTL, rcu_pwr_gating_cntl);
+
+       if (rdev->family == CHIP_PALM) {
+               rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_2);
+               rcu_pwr_gating_cntl &= ~(MPPU_MASK | MPPD_MASK);
+               rcu_pwr_gating_cntl |= MPPU(113) | MPPD(50);
+               WREG32_RCU(RCU_PWR_GATING_CNTL_2, rcu_pwr_gating_cntl);
+
+               rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_3);
+               rcu_pwr_gating_cntl &= ~(DPPU_MASK | DPPD_MASK);
+               rcu_pwr_gating_cntl |= DPPU(16) | DPPD(50);
+               WREG32_RCU(RCU_PWR_GATING_CNTL_3, rcu_pwr_gating_cntl);
+       }
+
+       sumo_smu_pg_init(rdev);
+
+       rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL);
+       rcu_pwr_gating_cntl &=
+               ~(RSVD_MASK | PCV_MASK | PGS_MASK);
+       rcu_pwr_gating_cntl |= PGS(5) | PWR_GATING_EN;
+
+       if (rdev->family == CHIP_PALM) {
+               rcu_pwr_gating_cntl |= PCV(4);
+               rcu_pwr_gating_cntl &= ~PCP_MASK;
+               rcu_pwr_gating_cntl |= PCP(0x77);
+       } else
+               rcu_pwr_gating_cntl |= PCV(11);
+       WREG32_RCU(RCU_PWR_GATING_CNTL, rcu_pwr_gating_cntl);
+
+       if (rdev->family == CHIP_PALM) {
+               rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_2);
+               rcu_pwr_gating_cntl &= ~(MPPU_MASK | MPPD_MASK);
+               rcu_pwr_gating_cntl |= MPPU(113) | MPPD(50);
+               WREG32_RCU(RCU_PWR_GATING_CNTL_2, rcu_pwr_gating_cntl);
+
+               rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_3);
+               rcu_pwr_gating_cntl &= ~(DPPU_MASK | DPPD_MASK);
+               rcu_pwr_gating_cntl |= DPPU(22) | DPPD(50);
+               WREG32_RCU(RCU_PWR_GATING_CNTL_3, rcu_pwr_gating_cntl);
+       }
+
+       sumo_smu_pg_init(rdev);
+}
+
+static void sumo_gfx_powergating_enable(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(CG_PWR_GATING_CNTL, DYN_PWR_DOWN_EN, ~DYN_PWR_DOWN_EN);
+       else {
+               WREG32_P(CG_PWR_GATING_CNTL, 0, ~DYN_PWR_DOWN_EN);
+               RREG32(GB_ADDR_CONFIG);
+       }
+}
+
+static int sumo_enable_clock_power_gating(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       if (pi->enable_gfx_clock_gating)
+               sumo_gfx_clockgating_initialize(rdev);
+       if (pi->enable_gfx_power_gating)
+               sumo_gfx_powergating_initialize(rdev);
+       if (pi->enable_mg_clock_gating)
+               sumo_mg_clockgating_enable(rdev, true);
+       if (pi->enable_gfx_clock_gating)
+               sumo_gfx_clockgating_enable(rdev, true);
+       if (pi->enable_gfx_power_gating)
+               sumo_gfx_powergating_enable(rdev, true);
+
+       return 0;
+}
+
+static void sumo_disable_clock_power_gating(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       if (pi->enable_gfx_clock_gating)
+               sumo_gfx_clockgating_enable(rdev, false);
+       if (pi->enable_gfx_power_gating)
+               sumo_gfx_powergating_enable(rdev, false);
+       if (pi->enable_mg_clock_gating)
+               sumo_mg_clockgating_enable(rdev, false);
+}
+
+static void sumo_calculate_bsp(struct radeon_device *rdev,
+                              u32 high_clk)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       u32 xclk = radeon_get_xclk(rdev);
+
+       pi->pasi = 65535 * 100 / high_clk;
+       pi->asi = 65535 * 100 / high_clk;
+
+       r600_calculate_u_and_p(pi->asi,
+                              xclk, 16, &pi->bsp, &pi->bsu);
+
+       r600_calculate_u_and_p(pi->pasi,
+                              xclk, 16, &pi->pbsp, &pi->pbsu);
+
+       pi->dsp = BSP(pi->bsp) | BSU(pi->bsu);
+       pi->psp = BSP(pi->pbsp) | BSU(pi->pbsu);
+}
+
+static void sumo_init_bsp(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       WREG32(CG_BSP_0, pi->psp);
+}
+
+
+static void sumo_program_bsp(struct radeon_device *rdev,
+                            struct radeon_ps *rps)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct sumo_ps *ps = sumo_get_ps(rps);
+       u32 i;
+       u32 highest_engine_clock = ps->levels[ps->num_levels - 1].sclk;
+
+       if (ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE)
+               highest_engine_clock = pi->boost_pl.sclk;
+
+       sumo_calculate_bsp(rdev, highest_engine_clock);
+
+       for (i = 0; i < ps->num_levels - 1; i++)
+               WREG32(CG_BSP_0 + (i * 4), pi->dsp);
+
+       WREG32(CG_BSP_0 + (i * 4), pi->psp);
+
+       if (ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE)
+               WREG32(CG_BSP_0 + (BOOST_DPM_LEVEL * 4), pi->psp);
+}
+
+static void sumo_write_at(struct radeon_device *rdev,
+                         u32 index, u32 value)
+{
+       if (index == 0)
+               WREG32(CG_AT_0, value);
+       else if (index == 1)
+               WREG32(CG_AT_1, value);
+       else if (index == 2)
+               WREG32(CG_AT_2, value);
+       else if (index == 3)
+               WREG32(CG_AT_3, value);
+       else if (index == 4)
+               WREG32(CG_AT_4, value);
+       else if (index == 5)
+               WREG32(CG_AT_5, value);
+       else if (index == 6)
+               WREG32(CG_AT_6, value);
+       else if (index == 7)
+               WREG32(CG_AT_7, value);
+}
+
+static void sumo_program_at(struct radeon_device *rdev,
+                           struct radeon_ps *rps)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct sumo_ps *ps = sumo_get_ps(rps);
+       u32 asi;
+       u32 i;
+       u32 m_a;
+       u32 a_t;
+       u32 r[SUMO_MAX_HARDWARE_POWERLEVELS];
+       u32 l[SUMO_MAX_HARDWARE_POWERLEVELS];
+
+       r[0] = SUMO_R_DFLT0;
+       r[1] = SUMO_R_DFLT1;
+       r[2] = SUMO_R_DFLT2;
+       r[3] = SUMO_R_DFLT3;
+       r[4] = SUMO_R_DFLT4;
+
+       l[0] = SUMO_L_DFLT0;
+       l[1] = SUMO_L_DFLT1;
+       l[2] = SUMO_L_DFLT2;
+       l[3] = SUMO_L_DFLT3;
+       l[4] = SUMO_L_DFLT4;
+
+       for (i = 0; i < ps->num_levels; i++) {
+               asi = (i == ps->num_levels - 1) ? pi->pasi : pi->asi;
+
+               m_a = asi * ps->levels[i].sclk / 100;
+
+               a_t = CG_R(m_a * r[i] / 100) | CG_L(m_a * l[i] / 100);
+
+               sumo_write_at(rdev, i, a_t);
+       }
+
+       if (ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) {
+               asi = pi->pasi;
+
+               m_a = asi * pi->boost_pl.sclk / 100;
+
+               a_t = CG_R(m_a * r[ps->num_levels - 1] / 100) |
+                       CG_L(m_a * l[ps->num_levels - 1] / 100);
+
+               sumo_write_at(rdev, BOOST_DPM_LEVEL, a_t);
+       }
+}
+
+static void sumo_program_tp(struct radeon_device *rdev)
+{
+       int i;
+       enum r600_td td = R600_TD_DFLT;
+
+       for (i = 0; i < SUMO_PM_NUMBER_OF_TC; i++) {
+               WREG32_P(CG_FFCT_0 + (i * 4), UTC_0(sumo_utc[i]), ~UTC_0_MASK);
+               WREG32_P(CG_FFCT_0 + (i * 4), DTC_0(sumo_dtc[i]), ~DTC_0_MASK);
+       }
+
+       if (td == R600_TD_AUTO)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_FORCE_TREND_SEL);
+       else
+               WREG32_P(SCLK_PWRMGT_CNTL, FIR_FORCE_TREND_SEL, ~FIR_FORCE_TREND_SEL);
+
+       if (td == R600_TD_UP)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_TREND_MODE);
+
+       if (td == R600_TD_DOWN)
+               WREG32_P(SCLK_PWRMGT_CNTL, FIR_TREND_MODE, ~FIR_TREND_MODE);
+}
+
+void sumo_program_vc(struct radeon_device *rdev, u32 vrc)
+{
+       WREG32(CG_FTV, vrc);
+}
+
+void sumo_clear_vc(struct radeon_device *rdev)
+{
+       WREG32(CG_FTV, 0);
+}
+
+void sumo_program_sstp(struct radeon_device *rdev)
+{
+       u32 p, u;
+       u32 xclk = radeon_get_xclk(rdev);
+
+       r600_calculate_u_and_p(SUMO_SST_DFLT,
+                              xclk, 16, &p, &u);
+
+       WREG32(CG_SSP, SSTU(u) | SST(p));
+}
+
+static void sumo_set_divider_value(struct radeon_device *rdev,
+                                  u32 index, u32 divider)
+{
+       u32 reg_index = index / 4;
+       u32 field_index = index % 4;
+
+       if (field_index == 0)
+               WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
+                        SCLK_FSTATE_0_DIV(divider), ~SCLK_FSTATE_0_DIV_MASK);
+       else if (field_index == 1)
+               WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
+                        SCLK_FSTATE_1_DIV(divider), ~SCLK_FSTATE_1_DIV_MASK);
+       else if (field_index == 2)
+               WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
+                        SCLK_FSTATE_2_DIV(divider), ~SCLK_FSTATE_2_DIV_MASK);
+       else if (field_index == 3)
+               WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
+                        SCLK_FSTATE_3_DIV(divider), ~SCLK_FSTATE_3_DIV_MASK);
+}
+
+static void sumo_set_ds_dividers(struct radeon_device *rdev,
+                                u32 index, u32 divider)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       if (pi->enable_sclk_ds) {
+               u32 dpm_ctrl = RREG32(CG_SCLK_DPM_CTRL_6);
+
+               dpm_ctrl &= ~(0x7 << (index * 3));
+               dpm_ctrl |= (divider << (index * 3));
+               WREG32(CG_SCLK_DPM_CTRL_6, dpm_ctrl);
+       }
+}
+
+static void sumo_set_ss_dividers(struct radeon_device *rdev,
+                                u32 index, u32 divider)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       if (pi->enable_sclk_ds) {
+               u32 dpm_ctrl = RREG32(CG_SCLK_DPM_CTRL_11);
+
+               dpm_ctrl &= ~(0x7 << (index * 3));
+               dpm_ctrl |= (divider << (index * 3));
+               WREG32(CG_SCLK_DPM_CTRL_11, dpm_ctrl);
+       }
+}
+
+static void sumo_set_vid(struct radeon_device *rdev, u32 index, u32 vid)
+{
+       u32 voltage_cntl = RREG32(CG_DPM_VOLTAGE_CNTL);
+
+       voltage_cntl &= ~(DPM_STATE0_LEVEL_MASK << (index * 2));
+       voltage_cntl |= (vid << (DPM_STATE0_LEVEL_SHIFT + index * 2));
+       WREG32(CG_DPM_VOLTAGE_CNTL, voltage_cntl);
+}
+
+static void sumo_set_allos_gnb_slow(struct radeon_device *rdev, u32 index, u32 gnb_slow)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       u32 temp = gnb_slow;
+       u32 cg_sclk_dpm_ctrl_3;
+
+       if (pi->driver_nbps_policy_disable)
+               temp = 1;
+
+       cg_sclk_dpm_ctrl_3 = RREG32(CG_SCLK_DPM_CTRL_3);
+       cg_sclk_dpm_ctrl_3 &= ~(GNB_SLOW_FSTATE_0_MASK << index);
+       cg_sclk_dpm_ctrl_3 |= (temp << (GNB_SLOW_FSTATE_0_SHIFT + index));
+
+       WREG32(CG_SCLK_DPM_CTRL_3, cg_sclk_dpm_ctrl_3);
+}
+
+static void sumo_program_power_level(struct radeon_device *rdev,
+                                    struct sumo_pl *pl, u32 index)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       int ret;
+       struct atom_clock_dividers dividers;
+       u32 ds_en = RREG32(DEEP_SLEEP_CNTL) & ENABLE_DS;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            pl->sclk, false, &dividers);
+       if (ret)
+               return;
+
+       sumo_set_divider_value(rdev, index, dividers.post_div);
+
+       sumo_set_vid(rdev, index, pl->vddc_index);
+
+       if (pl->ss_divider_index == 0 || pl->ds_divider_index == 0) {
+               if (ds_en)
+                       WREG32_P(DEEP_SLEEP_CNTL, 0, ~ENABLE_DS);
+       } else {
+               sumo_set_ss_dividers(rdev, index, pl->ss_divider_index);
+               sumo_set_ds_dividers(rdev, index, pl->ds_divider_index);
+
+               if (!ds_en)
+                       WREG32_P(DEEP_SLEEP_CNTL, ENABLE_DS, ~ENABLE_DS);
+       }
+
+       sumo_set_allos_gnb_slow(rdev, index, pl->allow_gnb_slow);
+
+       if (pi->enable_boost)
+               sumo_set_tdp_limit(rdev, index, pl->sclk_dpm_tdp_limit);
+}
+
+static void sumo_power_level_enable(struct radeon_device *rdev, u32 index, bool enable)
+{
+       u32 reg_index = index / 4;
+       u32 field_index = index % 4;
+
+       if (field_index == 0)
+               WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
+                        enable ? SCLK_FSTATE_0_VLD : 0, ~SCLK_FSTATE_0_VLD);
+       else if (field_index == 1)
+               WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
+                        enable ? SCLK_FSTATE_1_VLD : 0, ~SCLK_FSTATE_1_VLD);
+       else if (field_index == 2)
+               WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
+                        enable ? SCLK_FSTATE_2_VLD : 0, ~SCLK_FSTATE_2_VLD);
+       else if (field_index == 3)
+               WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
+                        enable ? SCLK_FSTATE_3_VLD : 0, ~SCLK_FSTATE_3_VLD);
+}
+
+static bool sumo_dpm_enabled(struct radeon_device *rdev)
+{
+       if (RREG32(CG_SCLK_DPM_CTRL_3) & DPM_SCLK_ENABLE)
+               return true;
+       else
+               return false;
+}
+
+static void sumo_start_dpm(struct radeon_device *rdev)
+{
+       WREG32_P(CG_SCLK_DPM_CTRL_3, DPM_SCLK_ENABLE, ~DPM_SCLK_ENABLE);
+}
+
+static void sumo_stop_dpm(struct radeon_device *rdev)
+{
+       WREG32_P(CG_SCLK_DPM_CTRL_3, 0, ~DPM_SCLK_ENABLE);
+}
+
+static void sumo_set_forced_mode(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(CG_SCLK_DPM_CTRL_3, FORCE_SCLK_STATE_EN, ~FORCE_SCLK_STATE_EN);
+       else
+               WREG32_P(CG_SCLK_DPM_CTRL_3, 0, ~FORCE_SCLK_STATE_EN);
+}
+
+static void sumo_set_forced_mode_enabled(struct radeon_device *rdev)
+{
+       int i;
+
+       sumo_set_forced_mode(rdev, true);
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(CG_SCLK_STATUS) & SCLK_OVERCLK_DETECT)
+                       break;
+               udelay(1);
+       }
+}
+
+static void sumo_wait_for_level_0(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURR_SCLK_INDEX_MASK) == 0)
+                       break;
+               udelay(1);
+       }
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURR_INDEX_MASK) == 0)
+                       break;
+               udelay(1);
+       }
+}
+
+static void sumo_set_forced_mode_disabled(struct radeon_device *rdev)
+{
+       sumo_set_forced_mode(rdev, false);
+}
+
+static void sumo_enable_power_level_0(struct radeon_device *rdev)
+{
+       sumo_power_level_enable(rdev, 0, true);
+}
+
+static void sumo_patch_boost_state(struct radeon_device *rdev,
+                                  struct radeon_ps *rps)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct sumo_ps *new_ps = sumo_get_ps(rps);
+
+       if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) {
+               pi->boost_pl = new_ps->levels[new_ps->num_levels - 1];
+               pi->boost_pl.sclk = pi->sys_info.boost_sclk;
+               pi->boost_pl.vddc_index = pi->sys_info.boost_vid_2bit;
+               pi->boost_pl.sclk_dpm_tdp_limit = pi->sys_info.sclk_dpm_tdp_limit_boost;
+       }
+}
+
+static void sumo_pre_notify_alt_vddnb_change(struct radeon_device *rdev,
+                                            struct radeon_ps *new_rps,
+                                            struct radeon_ps *old_rps)
+{
+       struct sumo_ps *new_ps = sumo_get_ps(new_rps);
+       struct sumo_ps *old_ps = sumo_get_ps(old_rps);
+       u32 nbps1_old = 0;
+       u32 nbps1_new = 0;
+
+       if (old_ps != NULL)
+               nbps1_old = (old_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE) ? 1 : 0;
+
+       nbps1_new = (new_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE) ? 1 : 0;
+
+       if (nbps1_old == 1 && nbps1_new == 0)
+               sumo_smu_notify_alt_vddnb_change(rdev, 0, 0);
+}
+
+static void sumo_post_notify_alt_vddnb_change(struct radeon_device *rdev,
+                                             struct radeon_ps *new_rps,
+                                             struct radeon_ps *old_rps)
+{
+       struct sumo_ps *new_ps = sumo_get_ps(new_rps);
+       struct sumo_ps *old_ps = sumo_get_ps(old_rps);
+       u32 nbps1_old = 0;
+       u32 nbps1_new = 0;
+
+       if (old_ps != NULL)
+               nbps1_old = (old_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE)? 1 : 0;
+
+       nbps1_new = (new_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE)? 1 : 0;
+
+       if (nbps1_old == 0 && nbps1_new == 1)
+               sumo_smu_notify_alt_vddnb_change(rdev, 1, 1);
+}
+
+static void sumo_enable_boost(struct radeon_device *rdev,
+                             struct radeon_ps *rps,
+                             bool enable)
+{
+       struct sumo_ps *new_ps = sumo_get_ps(rps);
+
+       if (enable) {
+               if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE)
+                       sumo_boost_state_enable(rdev, true);
+       } else
+               sumo_boost_state_enable(rdev, false);
+}
+
+static void sumo_set_forced_level(struct radeon_device *rdev, u32 index)
+{
+       WREG32_P(CG_SCLK_DPM_CTRL_3, FORCE_SCLK_STATE(index), ~FORCE_SCLK_STATE_MASK);
+}
+
+static void sumo_set_forced_level_0(struct radeon_device *rdev)
+{
+       sumo_set_forced_level(rdev, 0);
+}
+
+static void sumo_program_wl(struct radeon_device *rdev,
+                           struct radeon_ps *rps)
+{
+       struct sumo_ps *new_ps = sumo_get_ps(rps);
+       u32 dpm_ctrl4 = RREG32(CG_SCLK_DPM_CTRL_4);
+
+       dpm_ctrl4 &= 0xFFFFFF00;
+       dpm_ctrl4 |= (1 << (new_ps->num_levels - 1));
+
+       if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE)
+               dpm_ctrl4 |= (1 << BOOST_DPM_LEVEL);
+
+       WREG32(CG_SCLK_DPM_CTRL_4, dpm_ctrl4);
+}
+
+static void sumo_program_power_levels_0_to_n(struct radeon_device *rdev,
+                                            struct radeon_ps *new_rps,
+                                            struct radeon_ps *old_rps)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct sumo_ps *new_ps = sumo_get_ps(new_rps);
+       struct sumo_ps *old_ps = sumo_get_ps(old_rps);
+       u32 i;
+       u32 n_current_state_levels = (old_ps == NULL) ? 1 : old_ps->num_levels;
+
+       for (i = 0; i < new_ps->num_levels; i++) {
+               sumo_program_power_level(rdev, &new_ps->levels[i], i);
+               sumo_power_level_enable(rdev, i, true);
+       }
+
+       for (i = new_ps->num_levels; i < n_current_state_levels; i++)
+               sumo_power_level_enable(rdev, i, false);
+
+       if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE)
+               sumo_program_power_level(rdev, &pi->boost_pl, BOOST_DPM_LEVEL);
+}
+
+static void sumo_enable_acpi_pm(struct radeon_device *rdev)
+{
+       WREG32_P(GENERAL_PWRMGT, STATIC_PM_EN, ~STATIC_PM_EN);
+}
+
+static void sumo_program_power_level_enter_state(struct radeon_device *rdev)
+{
+       WREG32_P(CG_SCLK_DPM_CTRL_5, SCLK_FSTATE_BOOTUP(0), ~SCLK_FSTATE_BOOTUP_MASK);
+}
+
+static void sumo_program_acpi_power_level(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct atom_clock_dividers dividers;
+       int ret;
+
+        ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                             pi->acpi_pl.sclk,
+                                            false, &dividers);
+       if (ret)
+               return;
+
+       WREG32_P(CG_ACPI_CNTL, SCLK_ACPI_DIV(dividers.post_div), ~SCLK_ACPI_DIV_MASK);
+       WREG32_P(CG_ACPI_VOLTAGE_CNTL, 0, ~ACPI_VOLTAGE_EN);
+}
+
+static void sumo_program_bootup_state(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       u32 dpm_ctrl4 = RREG32(CG_SCLK_DPM_CTRL_4);
+       u32 i;
+
+       sumo_program_power_level(rdev, &pi->boot_pl, 0);
+
+       dpm_ctrl4 &= 0xFFFFFF00;
+       WREG32(CG_SCLK_DPM_CTRL_4, dpm_ctrl4);
+
+       for (i = 1; i < 8; i++)
+               sumo_power_level_enable(rdev, i, false);
+}
+
+static void sumo_setup_uvd_clocks(struct radeon_device *rdev,
+                                 struct radeon_ps *new_rps,
+                                 struct radeon_ps *old_rps)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       if (pi->enable_gfx_power_gating) {
+               sumo_gfx_powergating_enable(rdev, false);
+       }
+
+       radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk);
+
+       if (pi->enable_gfx_power_gating) {
+               if (!pi->disable_gfx_power_gating_in_uvd ||
+                   !r600_is_uvd_state(new_rps->class, new_rps->class2))
+                       sumo_gfx_powergating_enable(rdev, true);
+       }
+}
+
+static void sumo_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev,
+                                                   struct radeon_ps *new_rps,
+                                                   struct radeon_ps *old_rps)
+{
+       struct sumo_ps *new_ps = sumo_get_ps(new_rps);
+       struct sumo_ps *current_ps = sumo_get_ps(old_rps);
+
+       if ((new_rps->vclk == old_rps->vclk) &&
+           (new_rps->dclk == old_rps->dclk))
+               return;
+
+       if (new_ps->levels[new_ps->num_levels - 1].sclk >=
+           current_ps->levels[current_ps->num_levels - 1].sclk)
+               return;
+
+       sumo_setup_uvd_clocks(rdev, new_rps, old_rps);
+}
+
+static void sumo_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev,
+                                                  struct radeon_ps *new_rps,
+                                                  struct radeon_ps *old_rps)
+{
+       struct sumo_ps *new_ps = sumo_get_ps(new_rps);
+       struct sumo_ps *current_ps = sumo_get_ps(old_rps);
+
+       if ((new_rps->vclk == old_rps->vclk) &&
+           (new_rps->dclk == old_rps->dclk))
+               return;
+
+       if (new_ps->levels[new_ps->num_levels - 1].sclk <
+           current_ps->levels[current_ps->num_levels - 1].sclk)
+               return;
+
+       sumo_setup_uvd_clocks(rdev, new_rps, old_rps);
+}
+
+void sumo_take_smu_control(struct radeon_device *rdev, bool enable)
+{
+/* This bit selects who handles display phy powergating.
+ * Clear the bit to let atom handle it.
+ * Set it to let the driver handle it.
+ * For now we just let atom handle it.
+ */
+#if 0
+       u32 v = RREG32(DOUT_SCRATCH3);
+
+       if (enable)
+               v |= 0x4;
+       else
+               v &= 0xFFFFFFFB;
+
+       WREG32(DOUT_SCRATCH3, v);
+#endif
+}
+
+static void sumo_enable_sclk_ds(struct radeon_device *rdev, bool enable)
+{
+       if (enable) {
+               u32 deep_sleep_cntl = RREG32(DEEP_SLEEP_CNTL);
+               u32 deep_sleep_cntl2 = RREG32(DEEP_SLEEP_CNTL2);
+               u32 t = 1;
+
+               deep_sleep_cntl &= ~R_DIS;
+               deep_sleep_cntl &= ~HS_MASK;
+               deep_sleep_cntl |= HS(t > 4095 ? 4095 : t);
+
+               deep_sleep_cntl2 |= LB_UFP_EN;
+               deep_sleep_cntl2 &= INOUT_C_MASK;
+               deep_sleep_cntl2 |= INOUT_C(0xf);
+
+               WREG32(DEEP_SLEEP_CNTL2, deep_sleep_cntl2);
+               WREG32(DEEP_SLEEP_CNTL, deep_sleep_cntl);
+       } else
+               WREG32_P(DEEP_SLEEP_CNTL, 0, ~ENABLE_DS);
+}
+
+static void sumo_program_bootup_at(struct radeon_device *rdev)
+{
+       WREG32_P(CG_AT_0, CG_R(0xffff), ~CG_R_MASK);
+       WREG32_P(CG_AT_0, CG_L(0), ~CG_L_MASK);
+}
+
+static void sumo_reset_am(struct radeon_device *rdev)
+{
+       WREG32_P(SCLK_PWRMGT_CNTL, FIR_RESET, ~FIR_RESET);
+}
+
+static void sumo_start_am(struct radeon_device *rdev)
+{
+       WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_RESET);
+}
+
+static void sumo_program_ttp(struct radeon_device *rdev)
+{
+       u32 xclk = radeon_get_xclk(rdev);
+       u32 p, u;
+       u32 cg_sclk_dpm_ctrl_5 = RREG32(CG_SCLK_DPM_CTRL_5);
+
+       r600_calculate_u_and_p(1000,
+                              xclk, 16, &p, &u);
+
+       cg_sclk_dpm_ctrl_5 &= ~(TT_TP_MASK | TT_TU_MASK);
+       cg_sclk_dpm_ctrl_5 |= TT_TP(p) | TT_TU(u);
+
+       WREG32(CG_SCLK_DPM_CTRL_5, cg_sclk_dpm_ctrl_5);
+}
+
+static void sumo_program_ttt(struct radeon_device *rdev)
+{
+       u32 cg_sclk_dpm_ctrl_3 = RREG32(CG_SCLK_DPM_CTRL_3);
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       cg_sclk_dpm_ctrl_3 &= ~(GNB_TT_MASK | GNB_THERMTHRO_MASK);
+       cg_sclk_dpm_ctrl_3 |= GNB_TT(pi->thermal_auto_throttling + 49);
+
+       WREG32(CG_SCLK_DPM_CTRL_3, cg_sclk_dpm_ctrl_3);
+}
+
+
+static void sumo_enable_voltage_scaling(struct radeon_device *rdev, bool enable)
+{
+       if (enable) {
+               WREG32_P(CG_DPM_VOLTAGE_CNTL, DPM_VOLTAGE_EN, ~DPM_VOLTAGE_EN);
+               WREG32_P(CG_CG_VOLTAGE_CNTL, 0, ~CG_VOLTAGE_EN);
+       } else {
+               WREG32_P(CG_CG_VOLTAGE_CNTL, CG_VOLTAGE_EN, ~CG_VOLTAGE_EN);
+               WREG32_P(CG_DPM_VOLTAGE_CNTL, 0, ~DPM_VOLTAGE_EN);
+       }
+}
+
+static void sumo_override_cnb_thermal_events(struct radeon_device *rdev)
+{
+       WREG32_P(CG_SCLK_DPM_CTRL_3, CNB_THERMTHRO_MASK_SCLK,
+                ~CNB_THERMTHRO_MASK_SCLK);
+}
+
+static void sumo_program_dc_hto(struct radeon_device *rdev)
+{
+       u32 cg_sclk_dpm_ctrl_4 = RREG32(CG_SCLK_DPM_CTRL_4);
+       u32 p, u;
+       u32 xclk = radeon_get_xclk(rdev);
+
+       r600_calculate_u_and_p(100000,
+                              xclk, 14, &p, &u);
+
+       cg_sclk_dpm_ctrl_4 &= ~(DC_HDC_MASK | DC_HU_MASK);
+       cg_sclk_dpm_ctrl_4 |= DC_HDC(p) | DC_HU(u);
+
+       WREG32(CG_SCLK_DPM_CTRL_4, cg_sclk_dpm_ctrl_4);
+}
+
+static void sumo_force_nbp_state(struct radeon_device *rdev,
+                                struct radeon_ps *rps)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct sumo_ps *new_ps = sumo_get_ps(rps);
+
+       if (!pi->driver_nbps_policy_disable) {
+               if (new_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE)
+                       WREG32_P(CG_SCLK_DPM_CTRL_3, FORCE_NB_PSTATE_1, ~FORCE_NB_PSTATE_1);
+               else
+                       WREG32_P(CG_SCLK_DPM_CTRL_3, 0, ~FORCE_NB_PSTATE_1);
+       }
+}
+
+u32 sumo_get_sleep_divider_from_id(u32 id)
+{
+       return 1 << id;
+}
+
+u32 sumo_get_sleep_divider_id_from_clock(struct radeon_device *rdev,
+                                        u32 sclk,
+                                        u32 min_sclk_in_sr)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       u32 i;
+       u32 temp;
+       u32 min = (min_sclk_in_sr > SUMO_MINIMUM_ENGINE_CLOCK) ?
+               min_sclk_in_sr : SUMO_MINIMUM_ENGINE_CLOCK;
+
+       if (sclk < min)
+               return 0;
+
+       if (!pi->enable_sclk_ds)
+               return 0;
+
+       for (i = SUMO_MAX_DEEPSLEEP_DIVIDER_ID;  ; i--) {
+               temp = sclk / sumo_get_sleep_divider_from_id(i);
+
+               if (temp >= min || i == 0)
+                       break;
+       }
+       return i;
+}
+
+static u32 sumo_get_valid_engine_clock(struct radeon_device *rdev,
+                                      u32 lower_limit)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       u32 i;
+
+       for (i = 0; i < pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries; i++) {
+               if (pi->sys_info.sclk_voltage_mapping_table.entries[i].sclk_frequency >= lower_limit)
+                       return pi->sys_info.sclk_voltage_mapping_table.entries[i].sclk_frequency;
+       }
+
+       return pi->sys_info.sclk_voltage_mapping_table.entries[pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries - 1].sclk_frequency;
+}
+
+static void sumo_patch_thermal_state(struct radeon_device *rdev,
+                                    struct sumo_ps *ps,
+                                    struct sumo_ps *current_ps)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */
+       u32 current_vddc;
+       u32 current_sclk;
+       u32 current_index = 0;
+
+       if (current_ps) {
+               current_vddc = current_ps->levels[current_index].vddc_index;
+               current_sclk = current_ps->levels[current_index].sclk;
+       } else {
+               current_vddc = pi->boot_pl.vddc_index;
+               current_sclk = pi->boot_pl.sclk;
+       }
+
+       ps->levels[0].vddc_index = current_vddc;
+
+       if (ps->levels[0].sclk > current_sclk)
+               ps->levels[0].sclk = current_sclk;
+
+       ps->levels[0].ss_divider_index =
+               sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[0].sclk, sclk_in_sr);
+
+       ps->levels[0].ds_divider_index =
+               sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[0].sclk, SUMO_MINIMUM_ENGINE_CLOCK);
+
+       if (ps->levels[0].ds_divider_index > ps->levels[0].ss_divider_index + 1)
+               ps->levels[0].ds_divider_index = ps->levels[0].ss_divider_index + 1;
+
+       if (ps->levels[0].ss_divider_index == ps->levels[0].ds_divider_index) {
+               if (ps->levels[0].ss_divider_index > 1)
+                       ps->levels[0].ss_divider_index = ps->levels[0].ss_divider_index - 1;
+       }
+
+       if (ps->levels[0].ss_divider_index == 0)
+               ps->levels[0].ds_divider_index = 0;
+
+       if (ps->levels[0].ds_divider_index == 0)
+               ps->levels[0].ss_divider_index = 0;
+}
+
+static void sumo_apply_state_adjust_rules(struct radeon_device *rdev,
+                                         struct radeon_ps *new_rps,
+                                         struct radeon_ps *old_rps)
+{
+       struct sumo_ps *ps = sumo_get_ps(new_rps);
+       struct sumo_ps *current_ps = sumo_get_ps(old_rps);
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       u32 min_voltage = 0; /* ??? */
+       u32 min_sclk = pi->sys_info.min_sclk; /* XXX check against disp reqs */
+       u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */
+       u32 i;
+
+       if (new_rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
+               return sumo_patch_thermal_state(rdev, ps, current_ps);
+
+       if (pi->enable_boost) {
+               if (new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE)
+                       ps->flags |= SUMO_POWERSTATE_FLAGS_BOOST_STATE;
+       }
+
+       if ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) ||
+           (new_rps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) ||
+           (new_rps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE))
+               ps->flags |= SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE;
+
+       for (i = 0; i < ps->num_levels; i++) {
+               if (ps->levels[i].vddc_index < min_voltage)
+                       ps->levels[i].vddc_index = min_voltage;
+
+               if (ps->levels[i].sclk < min_sclk)
+                       ps->levels[i].sclk =
+                               sumo_get_valid_engine_clock(rdev, min_sclk);
+
+               ps->levels[i].ss_divider_index =
+                       sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[i].sclk, sclk_in_sr);
+
+               ps->levels[i].ds_divider_index =
+                       sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[i].sclk, SUMO_MINIMUM_ENGINE_CLOCK);
+
+               if (ps->levels[i].ds_divider_index > ps->levels[i].ss_divider_index + 1)
+                       ps->levels[i].ds_divider_index = ps->levels[i].ss_divider_index + 1;
+
+               if (ps->levels[i].ss_divider_index == ps->levels[i].ds_divider_index) {
+                       if (ps->levels[i].ss_divider_index > 1)
+                               ps->levels[i].ss_divider_index = ps->levels[i].ss_divider_index - 1;
+               }
+
+               if (ps->levels[i].ss_divider_index == 0)
+                       ps->levels[i].ds_divider_index = 0;
+
+               if (ps->levels[i].ds_divider_index == 0)
+                       ps->levels[i].ss_divider_index = 0;
+
+               if (ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE)
+                       ps->levels[i].allow_gnb_slow = 1;
+               else if ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) ||
+                        (new_rps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC))
+                       ps->levels[i].allow_gnb_slow = 0;
+               else if (i == ps->num_levels - 1)
+                       ps->levels[i].allow_gnb_slow = 0;
+               else
+                       ps->levels[i].allow_gnb_slow = 1;
+       }
+}
+
+static void sumo_cleanup_asic(struct radeon_device *rdev)
+{
+       sumo_take_smu_control(rdev, false);
+}
+
+static int sumo_set_thermal_temperature_range(struct radeon_device *rdev,
+                                             int min_temp, int max_temp)
+{
+       int low_temp = 0 * 1000;
+       int high_temp = 255 * 1000;
+
+       if (low_temp < min_temp)
+               low_temp = min_temp;
+       if (high_temp > max_temp)
+               high_temp = max_temp;
+       if (high_temp < low_temp) {
+               DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp);
+               return -EINVAL;
+       }
+
+       WREG32_P(CG_THERMAL_INT, DIG_THERM_INTH(49 + (high_temp / 1000)), ~DIG_THERM_INTH_MASK);
+       WREG32_P(CG_THERMAL_INT, DIG_THERM_INTL(49 + (low_temp / 1000)), ~DIG_THERM_INTL_MASK);
+
+       rdev->pm.dpm.thermal.min_temp = low_temp;
+       rdev->pm.dpm.thermal.max_temp = high_temp;
+
+       return 0;
+}
+
+static void sumo_update_current_ps(struct radeon_device *rdev,
+                                  struct radeon_ps *rps)
+{
+       struct sumo_ps *new_ps = sumo_get_ps(rps);
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       pi->current_rps = *rps;
+       pi->current_ps = *new_ps;
+       pi->current_rps.ps_priv = &pi->current_ps;
+}
+
+static void sumo_update_requested_ps(struct radeon_device *rdev,
+                                    struct radeon_ps *rps)
+{
+       struct sumo_ps *new_ps = sumo_get_ps(rps);
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       pi->requested_rps = *rps;
+       pi->requested_ps = *new_ps;
+       pi->requested_rps.ps_priv = &pi->requested_ps;
+}
+
+int sumo_dpm_enable(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       int ret;
+
+       if (sumo_dpm_enabled(rdev))
+               return -EINVAL;
+
+       ret = sumo_enable_clock_power_gating(rdev);
+       if (ret)
+               return ret;
+       sumo_program_bootup_state(rdev);
+       sumo_init_bsp(rdev);
+       sumo_reset_am(rdev);
+       sumo_program_tp(rdev);
+       sumo_program_bootup_at(rdev);
+       sumo_start_am(rdev);
+       if (pi->enable_auto_thermal_throttling) {
+               sumo_program_ttp(rdev);
+               sumo_program_ttt(rdev);
+       }
+       sumo_program_dc_hto(rdev);
+       sumo_program_power_level_enter_state(rdev);
+       sumo_enable_voltage_scaling(rdev, true);
+       sumo_program_sstp(rdev);
+       sumo_program_vc(rdev, SUMO_VRC_DFLT);
+       sumo_override_cnb_thermal_events(rdev);
+       sumo_start_dpm(rdev);
+       sumo_wait_for_level_0(rdev);
+       if (pi->enable_sclk_ds)
+               sumo_enable_sclk_ds(rdev, true);
+       if (pi->enable_boost)
+               sumo_enable_boost_timer(rdev);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               ret = sumo_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+               if (ret)
+                       return ret;
+               rdev->irq.dpm_thermal = true;
+               radeon_irq_set(rdev);
+       }
+
+       sumo_update_current_ps(rdev, rdev->pm.dpm.boot_ps);
+
+       return 0;
+}
+
+void sumo_dpm_disable(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       if (!sumo_dpm_enabled(rdev))
+               return;
+       sumo_disable_clock_power_gating(rdev);
+       if (pi->enable_sclk_ds)
+               sumo_enable_sclk_ds(rdev, false);
+       sumo_clear_vc(rdev);
+       sumo_wait_for_level_0(rdev);
+       sumo_stop_dpm(rdev);
+       sumo_enable_voltage_scaling(rdev, false);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               rdev->irq.dpm_thermal = false;
+               radeon_irq_set(rdev);
+       }
+
+       sumo_update_current_ps(rdev, rdev->pm.dpm.boot_ps);
+}
+
+int sumo_dpm_pre_set_power_state(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct radeon_ps requested_ps = *rdev->pm.dpm.requested_ps;
+       struct radeon_ps *new_ps = &requested_ps;
+
+       sumo_update_requested_ps(rdev, new_ps);
+
+       if (pi->enable_dynamic_patch_ps)
+               sumo_apply_state_adjust_rules(rdev,
+                                             &pi->requested_rps,
+                                             &pi->current_rps);
+
+       return 0;
+}
+
+int sumo_dpm_set_power_state(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct radeon_ps *new_ps = &pi->requested_rps;
+       struct radeon_ps *old_ps = &pi->current_rps;
+
+       if (pi->enable_dpm)
+               sumo_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps);
+       if (pi->enable_boost) {
+               sumo_enable_boost(rdev, new_ps, false);
+               sumo_patch_boost_state(rdev, new_ps);
+       }
+       if (pi->enable_dpm) {
+               sumo_pre_notify_alt_vddnb_change(rdev, new_ps, old_ps);
+               sumo_enable_power_level_0(rdev);
+               sumo_set_forced_level_0(rdev);
+               sumo_set_forced_mode_enabled(rdev);
+               sumo_wait_for_level_0(rdev);
+               sumo_program_power_levels_0_to_n(rdev, new_ps, old_ps);
+               sumo_program_wl(rdev, new_ps);
+               sumo_program_bsp(rdev, new_ps);
+               sumo_program_at(rdev, new_ps);
+               sumo_force_nbp_state(rdev, new_ps);
+               sumo_set_forced_mode_disabled(rdev);
+               sumo_set_forced_mode_enabled(rdev);
+               sumo_set_forced_mode_disabled(rdev);
+               sumo_post_notify_alt_vddnb_change(rdev, new_ps, old_ps);
+       }
+       if (pi->enable_boost)
+               sumo_enable_boost(rdev, new_ps, true);
+       if (pi->enable_dpm)
+               sumo_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+
+       rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO;
+
+       return 0;
+}
+
+void sumo_dpm_post_set_power_state(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct radeon_ps *new_ps = &pi->requested_rps;
+
+       sumo_update_current_ps(rdev, new_ps);
+}
+
+void sumo_dpm_reset_asic(struct radeon_device *rdev)
+{
+       sumo_program_bootup_state(rdev);
+       sumo_enable_power_level_0(rdev);
+       sumo_set_forced_level_0(rdev);
+       sumo_set_forced_mode_enabled(rdev);
+       sumo_wait_for_level_0(rdev);
+       sumo_set_forced_mode_disabled(rdev);
+       sumo_set_forced_mode_enabled(rdev);
+       sumo_set_forced_mode_disabled(rdev);
+}
+
+void sumo_dpm_setup_asic(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       sumo_initialize_m3_arb(rdev);
+       pi->fw_version = sumo_get_running_fw_version(rdev);
+       DRM_INFO("Found smc ucode version: 0x%08x\n", pi->fw_version);
+       sumo_program_acpi_power_level(rdev);
+       sumo_enable_acpi_pm(rdev);
+       sumo_take_smu_control(rdev, true);
+}
+
+void sumo_dpm_display_configuration_changed(struct radeon_device *rdev)
+{
+
+}
+
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+       struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+       struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+
+union pplib_clock_info {
+       struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
+       struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
+       struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
+       struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+
+union pplib_power_state {
+       struct _ATOM_PPLIB_STATE v1;
+       struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static void sumo_patch_boot_state(struct radeon_device *rdev,
+                                 struct sumo_ps *ps)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       ps->num_levels = 1;
+       ps->flags = 0;
+       ps->levels[0] = pi->boot_pl;
+}
+
+static void sumo_parse_pplib_non_clock_info(struct radeon_device *rdev,
+                                           struct radeon_ps *rps,
+                                           struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
+                                           u8 table_rev)
+{
+       struct sumo_ps *ps = sumo_get_ps(rps);
+
+       rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+       rps->class = le16_to_cpu(non_clock_info->usClassification);
+       rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
+
+       if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
+               rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
+               rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
+       } else {
+               rps->vclk = 0;
+               rps->dclk = 0;
+       }
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+               rdev->pm.dpm.boot_ps = rps;
+               sumo_patch_boot_state(rdev, ps);
+       }
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+               rdev->pm.dpm.uvd_ps = rps;
+}
+
+static void sumo_parse_pplib_clock_info(struct radeon_device *rdev,
+                                       struct radeon_ps *rps, int index,
+                                       union pplib_clock_info *clock_info)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct sumo_ps *ps = sumo_get_ps(rps);
+       struct sumo_pl *pl = &ps->levels[index];
+       u32 sclk;
+
+       sclk = le16_to_cpu(clock_info->sumo.usEngineClockLow);
+       sclk |= clock_info->sumo.ucEngineClockHigh << 16;
+       pl->sclk = sclk;
+       pl->vddc_index = clock_info->sumo.vddcIndex;
+       pl->sclk_dpm_tdp_limit = clock_info->sumo.tdpLimit;
+
+       ps->num_levels = index + 1;
+
+       if (pi->enable_sclk_ds) {
+               pl->ds_divider_index = 5;
+               pl->ss_divider_index = 4;
+       }
+}
+
+static int sumo_parse_power_table(struct radeon_device *rdev)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+       union pplib_power_state *power_state;
+       int i, j, k, non_clock_array_index, clock_array_index;
+       union pplib_clock_info *clock_info;
+       struct _StateArray *state_array;
+       struct _ClockInfoArray *clock_info_array;
+       struct _NonClockInfoArray *non_clock_info_array;
+       union power_info *power_info;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+        u16 data_offset;
+       u8 frev, crev;
+       u8 *power_state_offset;
+       struct sumo_ps *ps;
+
+       if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset))
+               return -EINVAL;
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       state_array = (struct _StateArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usStateArrayOffset));
+       clock_info_array = (struct _ClockInfoArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usClockInfoArrayOffset));
+       non_clock_info_array = (struct _NonClockInfoArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset));
+
+       rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
+                                 state_array->ucNumEntries, GFP_KERNEL);
+       if (!rdev->pm.dpm.ps)
+               return -ENOMEM;
+       power_state_offset = (u8 *)state_array->states;
+       rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
+       rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
+       rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
+       for (i = 0; i < state_array->ucNumEntries; i++) {
+               power_state = (union pplib_power_state *)power_state_offset;
+               non_clock_array_index = power_state->v2.nonClockInfoIndex;
+               non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+                       &non_clock_info_array->nonClockInfo[non_clock_array_index];
+               if (!rdev->pm.power_state[i].clock_info)
+                       return -EINVAL;
+               ps = kzalloc(sizeof(struct sumo_ps), GFP_KERNEL);
+               if (ps == NULL) {
+                       kfree(rdev->pm.dpm.ps);
+                       return -ENOMEM;
+               }
+               rdev->pm.dpm.ps[i].ps_priv = ps;
+               k = 0;
+               for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
+                       clock_array_index = power_state->v2.clockInfoIndex[j];
+                       if (k >= SUMO_MAX_HARDWARE_POWERLEVELS)
+                               break;
+                       clock_info = (union pplib_clock_info *)
+                               &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
+                       sumo_parse_pplib_clock_info(rdev,
+                                                   &rdev->pm.dpm.ps[i], k,
+                                                   clock_info);
+                       k++;
+               }
+               sumo_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
+                                               non_clock_info,
+                                               non_clock_info_array->ucEntrySize);
+               power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
+       }
+       rdev->pm.dpm.num_ps = state_array->ucNumEntries;
+       return 0;
+}
+
+u32 sumo_convert_vid2_to_vid7(struct radeon_device *rdev,
+                             struct sumo_vid_mapping_table *vid_mapping_table,
+                             u32 vid_2bit)
+{
+       u32 i;
+
+       for (i = 0; i < vid_mapping_table->num_entries; i++) {
+               if (vid_mapping_table->entries[i].vid_2bit == vid_2bit)
+                       return vid_mapping_table->entries[i].vid_7bit;
+       }
+
+       return vid_mapping_table->entries[vid_mapping_table->num_entries - 1].vid_7bit;
+}
+
+static u16 sumo_convert_voltage_index_to_value(struct radeon_device *rdev,
+                                              u32 vid_2bit)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       u32 vid_7bit = sumo_convert_vid2_to_vid7(rdev, &pi->sys_info.vid_mapping_table, vid_2bit);
+
+       if (vid_7bit > 0x7C)
+               return 0;
+
+       return (15500 - vid_7bit * 125 + 5) / 10;
+}
+
+static void sumo_construct_display_voltage_mapping_table(struct radeon_device *rdev,
+                                                        struct sumo_disp_clock_voltage_mapping_table *disp_clk_voltage_mapping_table,
+                                                        ATOM_CLK_VOLT_CAPABILITY *table)
+{
+       u32 i;
+
+       for (i = 0; i < SUMO_MAX_NUMBER_VOLTAGES; i++) {
+               if (table[i].ulMaximumSupportedCLK == 0)
+                       break;
+
+               disp_clk_voltage_mapping_table->display_clock_frequency[i] =
+                       table[i].ulMaximumSupportedCLK;
+       }
+
+       disp_clk_voltage_mapping_table->num_max_voltage_levels = i;
+
+       if (disp_clk_voltage_mapping_table->num_max_voltage_levels == 0) {
+               disp_clk_voltage_mapping_table->display_clock_frequency[0] = 80000;
+               disp_clk_voltage_mapping_table->num_max_voltage_levels = 1;
+       }
+}
+
+void sumo_construct_sclk_voltage_mapping_table(struct radeon_device *rdev,
+                                              struct sumo_sclk_voltage_mapping_table *sclk_voltage_mapping_table,
+                                              ATOM_AVAILABLE_SCLK_LIST *table)
+{
+       u32 i;
+       u32 n = 0;
+       u32 prev_sclk = 0;
+
+       for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) {
+               if (table[i].ulSupportedSCLK > prev_sclk) {
+                       sclk_voltage_mapping_table->entries[n].sclk_frequency =
+                               table[i].ulSupportedSCLK;
+                       sclk_voltage_mapping_table->entries[n].vid_2bit =
+                               table[i].usVoltageIndex;
+                       prev_sclk = table[i].ulSupportedSCLK;
+                       n++;
+               }
+       }
+
+       sclk_voltage_mapping_table->num_max_dpm_entries = n;
+}
+
+void sumo_construct_vid_mapping_table(struct radeon_device *rdev,
+                                     struct sumo_vid_mapping_table *vid_mapping_table,
+                                     ATOM_AVAILABLE_SCLK_LIST *table)
+{
+       u32 i, j;
+
+       for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) {
+               if (table[i].ulSupportedSCLK != 0) {
+                       vid_mapping_table->entries[table[i].usVoltageIndex].vid_7bit =
+                               table[i].usVoltageID;
+                       vid_mapping_table->entries[table[i].usVoltageIndex].vid_2bit =
+                               table[i].usVoltageIndex;
+               }
+       }
+
+       for (i = 0; i < SUMO_MAX_NUMBER_VOLTAGES; i++) {
+               if (vid_mapping_table->entries[i].vid_7bit == 0) {
+                       for (j = i + 1; j < SUMO_MAX_NUMBER_VOLTAGES; j++) {
+                               if (vid_mapping_table->entries[j].vid_7bit != 0) {
+                                       vid_mapping_table->entries[i] =
+                                               vid_mapping_table->entries[j];
+                                       vid_mapping_table->entries[j].vid_7bit = 0;
+                                       break;
+                               }
+                       }
+
+                       if (j == SUMO_MAX_NUMBER_VOLTAGES)
+                               break;
+               }
+       }
+
+       vid_mapping_table->num_entries = i;
+}
+
+union igp_info {
+       struct _ATOM_INTEGRATED_SYSTEM_INFO info;
+       struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
+       struct _ATOM_INTEGRATED_SYSTEM_INFO_V5 info_5;
+       struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6;
+};
+
+static int sumo_parse_sys_info_table(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
+       union igp_info *igp_info;
+       u8 frev, crev;
+       u16 data_offset;
+       int i;
+
+       if (atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset)) {
+               igp_info = (union igp_info *)(mode_info->atom_context->bios +
+                                             data_offset);
+
+               if (crev != 6) {
+                       DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev);
+                       return -EINVAL;
+               }
+               pi->sys_info.bootup_sclk = le32_to_cpu(igp_info->info_6.ulBootUpEngineClock);
+               pi->sys_info.min_sclk = le32_to_cpu(igp_info->info_6.ulMinEngineClock);
+               pi->sys_info.bootup_uma_clk = le32_to_cpu(igp_info->info_6.ulBootUpUMAClock);
+               pi->sys_info.bootup_nb_voltage_index =
+                       le16_to_cpu(igp_info->info_6.usBootUpNBVoltage);
+               if (igp_info->info_6.ucHtcTmpLmt == 0)
+                       pi->sys_info.htc_tmp_lmt = 203;
+               else
+                       pi->sys_info.htc_tmp_lmt = igp_info->info_6.ucHtcTmpLmt;
+               if (igp_info->info_6.ucHtcHystLmt == 0)
+                       pi->sys_info.htc_hyst_lmt = 5;
+               else
+                       pi->sys_info.htc_hyst_lmt = igp_info->info_6.ucHtcHystLmt;
+               if (pi->sys_info.htc_tmp_lmt <= pi->sys_info.htc_hyst_lmt) {
+                       DRM_ERROR("The htcTmpLmt should be larger than htcHystLmt.\n");
+               }
+               for (i = 0; i < NUMBER_OF_M3ARB_PARAM_SETS; i++) {
+                       pi->sys_info.csr_m3_arb_cntl_default[i] =
+                               le32_to_cpu(igp_info->info_6.ulCSR_M3_ARB_CNTL_DEFAULT[i]);
+                       pi->sys_info.csr_m3_arb_cntl_uvd[i] =
+                               le32_to_cpu(igp_info->info_6.ulCSR_M3_ARB_CNTL_UVD[i]);
+                       pi->sys_info.csr_m3_arb_cntl_fs3d[i] =
+                               le32_to_cpu(igp_info->info_6.ulCSR_M3_ARB_CNTL_FS3D[i]);
+               }
+               pi->sys_info.sclk_dpm_boost_margin =
+                       le32_to_cpu(igp_info->info_6.SclkDpmBoostMargin);
+               pi->sys_info.sclk_dpm_throttle_margin =
+                       le32_to_cpu(igp_info->info_6.SclkDpmThrottleMargin);
+               pi->sys_info.sclk_dpm_tdp_limit_pg =
+                       le16_to_cpu(igp_info->info_6.SclkDpmTdpLimitPG);
+               pi->sys_info.gnb_tdp_limit = le16_to_cpu(igp_info->info_6.GnbTdpLimit);
+               pi->sys_info.sclk_dpm_tdp_limit_boost =
+                       le16_to_cpu(igp_info->info_6.SclkDpmTdpLimitBoost);
+               pi->sys_info.boost_sclk = le32_to_cpu(igp_info->info_6.ulBoostEngineCLock);
+               pi->sys_info.boost_vid_2bit = igp_info->info_6.ulBoostVid_2bit;
+               if (igp_info->info_6.EnableBoost)
+                       pi->sys_info.enable_boost = true;
+               else
+                       pi->sys_info.enable_boost = false;
+               sumo_construct_display_voltage_mapping_table(rdev,
+                                                            &pi->sys_info.disp_clk_voltage_mapping_table,
+                                                            igp_info->info_6.sDISPCLK_Voltage);
+               sumo_construct_sclk_voltage_mapping_table(rdev,
+                                                         &pi->sys_info.sclk_voltage_mapping_table,
+                                                         igp_info->info_6.sAvail_SCLK);
+               sumo_construct_vid_mapping_table(rdev, &pi->sys_info.vid_mapping_table,
+                                                igp_info->info_6.sAvail_SCLK);
+
+       }
+       return 0;
+}
+
+static void sumo_construct_boot_and_acpi_state(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       pi->boot_pl.sclk = pi->sys_info.bootup_sclk;
+       pi->boot_pl.vddc_index = pi->sys_info.bootup_nb_voltage_index;
+       pi->boot_pl.ds_divider_index = 0;
+       pi->boot_pl.ss_divider_index = 0;
+       pi->boot_pl.allow_gnb_slow = 1;
+       pi->acpi_pl = pi->boot_pl;
+       pi->current_ps.num_levels = 1;
+       pi->current_ps.levels[0] = pi->boot_pl;
+}
+
+int sumo_dpm_init(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi;
+       u32 hw_rev = (RREG32(HW_REV) & ATI_REV_ID_MASK) >> ATI_REV_ID_SHIFT;
+       int ret;
+
+       pi = kzalloc(sizeof(struct sumo_power_info), GFP_KERNEL);
+       if (pi == NULL)
+               return -ENOMEM;
+       rdev->pm.dpm.priv = pi;
+
+       pi->driver_nbps_policy_disable = false;
+       if ((rdev->family == CHIP_PALM) && (hw_rev < 3))
+               pi->disable_gfx_power_gating_in_uvd = true;
+       else
+               pi->disable_gfx_power_gating_in_uvd = false;
+       pi->enable_alt_vddnb = true;
+       pi->enable_sclk_ds = true;
+       pi->enable_dynamic_m3_arbiter = false;
+       pi->enable_dynamic_patch_ps = true;
+       pi->enable_gfx_power_gating = true;
+       pi->enable_gfx_clock_gating = true;
+       pi->enable_mg_clock_gating = true;
+       pi->enable_auto_thermal_throttling = true;
+
+       ret = sumo_parse_sys_info_table(rdev);
+       if (ret)
+               return ret;
+
+       sumo_construct_boot_and_acpi_state(rdev);
+
+       ret = sumo_parse_power_table(rdev);
+       if (ret)
+               return ret;
+
+       pi->pasi = CYPRESS_HASI_DFLT;
+       pi->asi = RV770_ASI_DFLT;
+       pi->thermal_auto_throttling = pi->sys_info.htc_tmp_lmt;
+       pi->enable_boost = pi->sys_info.enable_boost;
+       pi->enable_dpm = true;
+
+       return 0;
+}
+
+void sumo_dpm_print_power_state(struct radeon_device *rdev,
+                               struct radeon_ps *rps)
+{
+       int i;
+       struct sumo_ps *ps = sumo_get_ps(rps);
+
+       r600_dpm_print_class_info(rps->class, rps->class2);
+       r600_dpm_print_cap_info(rps->caps);
+       printk("\tuvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+       for (i = 0; i < ps->num_levels; i++) {
+               struct sumo_pl *pl = &ps->levels[i];
+               printk("\t\tpower level %d    sclk: %u vddc: %u\n",
+                      i, pl->sclk,
+                      sumo_convert_voltage_index_to_value(rdev, pl->vddc_index));
+       }
+       r600_dpm_print_ps_status(rdev, rps);
+}
+
+void sumo_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                     struct seq_file *m)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct radeon_ps *rps = rdev->pm.dpm.current_ps;
+       struct sumo_ps *ps = sumo_get_ps(rps);
+       struct sumo_pl *pl;
+       u32 current_index =
+               (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURR_INDEX_MASK) >>
+               CURR_INDEX_SHIFT;
+
+       if (current_index == BOOST_DPM_LEVEL) {
+               pl = &pi->boost_pl;
+               seq_printf(m, "uvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+               seq_printf(m, "power level %d    sclk: %u vddc: %u\n",
+                          current_index, pl->sclk,
+                          sumo_convert_voltage_index_to_value(rdev, pl->vddc_index));
+       } else if (current_index >= ps->num_levels) {
+               seq_printf(m, "invalid dpm profile %d\n", current_index);
+       } else {
+               pl = &ps->levels[current_index];
+               seq_printf(m, "uvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+               seq_printf(m, "power level %d    sclk: %u vddc: %u\n",
+                          current_index, pl->sclk,
+                          sumo_convert_voltage_index_to_value(rdev, pl->vddc_index));
+       }
+}
+
+void sumo_dpm_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       sumo_cleanup_asic(rdev); /* ??? */
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               kfree(rdev->pm.dpm.ps[i].ps_priv);
+       }
+       kfree(rdev->pm.dpm.ps);
+       kfree(rdev->pm.dpm.priv);
+}
+
+u32 sumo_dpm_get_sclk(struct radeon_device *rdev, bool low)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct sumo_ps *requested_state = sumo_get_ps(&pi->requested_rps);
+
+       if (low)
+               return requested_state->levels[0].sclk;
+       else
+               return requested_state->levels[requested_state->num_levels - 1].sclk;
+}
+
+u32 sumo_dpm_get_mclk(struct radeon_device *rdev, bool low)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+
+       return pi->sys_info.bootup_uma_clk;
+}
+
+int sumo_dpm_force_performance_level(struct radeon_device *rdev,
+                                    enum radeon_dpm_forced_level level)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       struct radeon_ps *rps = &pi->current_rps;
+       struct sumo_ps *ps = sumo_get_ps(rps);
+       int i;
+
+       if (ps->num_levels <= 1)
+               return 0;
+
+       if (level == RADEON_DPM_FORCED_LEVEL_HIGH) {
+               sumo_power_level_enable(rdev, ps->num_levels - 1, true);
+               sumo_set_forced_level(rdev, ps->num_levels - 1);
+               sumo_set_forced_mode_enabled(rdev);
+               for (i = 0; i < ps->num_levels - 1; i++) {
+                       sumo_power_level_enable(rdev, i, false);
+               }
+               sumo_set_forced_mode(rdev, false);
+               sumo_set_forced_mode_enabled(rdev);
+               sumo_set_forced_mode(rdev, false);
+       } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) {
+               sumo_power_level_enable(rdev, 0, true);
+               sumo_set_forced_level(rdev, 0);
+               sumo_set_forced_mode_enabled(rdev);
+               for (i = 1; i < ps->num_levels; i++) {
+                       sumo_power_level_enable(rdev, i, false);
+               }
+               sumo_set_forced_mode(rdev, false);
+               sumo_set_forced_mode_enabled(rdev);
+               sumo_set_forced_mode(rdev, false);
+       } else {
+               for (i = 0; i < ps->num_levels; i++) {
+                       sumo_power_level_enable(rdev, i, true);
+               }
+       }
+
+       rdev->pm.dpm.forced_level = level;
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.h b/drivers/gpu/drm/radeon/sumo_dpm.h
new file mode 100644 (file)
index 0000000..07dda29
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __SUMO_DPM_H__
+#define __SUMO_DPM_H__
+
+#include "atom.h"
+
+#define SUMO_MAX_HARDWARE_POWERLEVELS 5
+#define SUMO_PM_NUMBER_OF_TC 15
+
+struct sumo_pl {
+       u32 sclk;
+       u32 vddc_index;
+       u32 ds_divider_index;
+       u32 ss_divider_index;
+       u32 allow_gnb_slow;
+       u32 sclk_dpm_tdp_limit;
+};
+
+/* used for the flags field */
+#define SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE (1 << 0)
+#define SUMO_POWERSTATE_FLAGS_BOOST_STATE       (1 << 1)
+
+struct sumo_ps {
+       struct sumo_pl levels[SUMO_MAX_HARDWARE_POWERLEVELS];
+       u32 num_levels;
+       /* flags */
+       u32 flags;
+};
+
+#define NUMBER_OF_M3ARB_PARAM_SETS 10
+#define SUMO_MAX_NUMBER_VOLTAGES    4
+
+struct sumo_disp_clock_voltage_mapping_table {
+       u32 num_max_voltage_levels;
+       u32 display_clock_frequency[SUMO_MAX_NUMBER_VOLTAGES];
+};
+
+struct sumo_vid_mapping_entry {
+       u16 vid_2bit;
+       u16 vid_7bit;
+};
+
+struct sumo_vid_mapping_table {
+       u32 num_entries;
+       struct sumo_vid_mapping_entry entries[SUMO_MAX_NUMBER_VOLTAGES];
+};
+
+struct sumo_sclk_voltage_mapping_entry {
+       u32 sclk_frequency;
+       u16 vid_2bit;
+       u16 rsv;
+};
+
+struct sumo_sclk_voltage_mapping_table {
+       u32 num_max_dpm_entries;
+       struct sumo_sclk_voltage_mapping_entry entries[SUMO_MAX_HARDWARE_POWERLEVELS];
+};
+
+struct sumo_sys_info {
+       u32 bootup_sclk;
+       u32 min_sclk;
+       u32 bootup_uma_clk;
+       u16 bootup_nb_voltage_index;
+       u8 htc_tmp_lmt;
+       u8 htc_hyst_lmt;
+       struct sumo_sclk_voltage_mapping_table sclk_voltage_mapping_table;
+       struct sumo_disp_clock_voltage_mapping_table disp_clk_voltage_mapping_table;
+       struct sumo_vid_mapping_table vid_mapping_table;
+       u32 csr_m3_arb_cntl_default[NUMBER_OF_M3ARB_PARAM_SETS];
+       u32 csr_m3_arb_cntl_uvd[NUMBER_OF_M3ARB_PARAM_SETS];
+       u32 csr_m3_arb_cntl_fs3d[NUMBER_OF_M3ARB_PARAM_SETS];
+       u32 sclk_dpm_boost_margin;
+       u32 sclk_dpm_throttle_margin;
+       u32 sclk_dpm_tdp_limit_pg;
+       u32 gnb_tdp_limit;
+       u32 sclk_dpm_tdp_limit_boost;
+       u32 boost_sclk;
+       u32 boost_vid_2bit;
+       bool enable_boost;
+};
+
+struct sumo_power_info {
+       u32 asi;
+       u32 pasi;
+       u32 bsp;
+       u32 bsu;
+       u32 pbsp;
+       u32 pbsu;
+       u32 dsp;
+       u32 psp;
+       u32 thermal_auto_throttling;
+       u32 uvd_m3_arbiter;
+       u32 fw_version;
+       struct sumo_sys_info sys_info;
+       struct sumo_pl acpi_pl;
+       struct sumo_pl boot_pl;
+       struct sumo_pl boost_pl;
+       bool disable_gfx_power_gating_in_uvd;
+       bool driver_nbps_policy_disable;
+       bool enable_alt_vddnb;
+       bool enable_dynamic_m3_arbiter;
+       bool enable_gfx_clock_gating;
+       bool enable_gfx_power_gating;
+       bool enable_mg_clock_gating;
+       bool enable_sclk_ds;
+       bool enable_auto_thermal_throttling;
+       bool enable_dynamic_patch_ps;
+       bool enable_dpm;
+       bool enable_boost;
+       struct radeon_ps current_rps;
+       struct sumo_ps current_ps;
+       struct radeon_ps requested_rps;
+       struct sumo_ps requested_ps;
+};
+
+#define SUMO_UTC_DFLT_00                     0x48
+#define SUMO_UTC_DFLT_01                     0x44
+#define SUMO_UTC_DFLT_02                     0x44
+#define SUMO_UTC_DFLT_03                     0x44
+#define SUMO_UTC_DFLT_04                     0x44
+#define SUMO_UTC_DFLT_05                     0x44
+#define SUMO_UTC_DFLT_06                     0x44
+#define SUMO_UTC_DFLT_07                     0x44
+#define SUMO_UTC_DFLT_08                     0x44
+#define SUMO_UTC_DFLT_09                     0x44
+#define SUMO_UTC_DFLT_10                     0x44
+#define SUMO_UTC_DFLT_11                     0x44
+#define SUMO_UTC_DFLT_12                     0x44
+#define SUMO_UTC_DFLT_13                     0x44
+#define SUMO_UTC_DFLT_14                     0x44
+
+#define SUMO_DTC_DFLT_00                     0x48
+#define SUMO_DTC_DFLT_01                     0x44
+#define SUMO_DTC_DFLT_02                     0x44
+#define SUMO_DTC_DFLT_03                     0x44
+#define SUMO_DTC_DFLT_04                     0x44
+#define SUMO_DTC_DFLT_05                     0x44
+#define SUMO_DTC_DFLT_06                     0x44
+#define SUMO_DTC_DFLT_07                     0x44
+#define SUMO_DTC_DFLT_08                     0x44
+#define SUMO_DTC_DFLT_09                     0x44
+#define SUMO_DTC_DFLT_10                     0x44
+#define SUMO_DTC_DFLT_11                     0x44
+#define SUMO_DTC_DFLT_12                     0x44
+#define SUMO_DTC_DFLT_13                     0x44
+#define SUMO_DTC_DFLT_14                     0x44
+
+#define SUMO_AH_DFLT               5
+
+#define SUMO_R_DFLT0               70
+#define SUMO_R_DFLT1               70
+#define SUMO_R_DFLT2               70
+#define SUMO_R_DFLT3               70
+#define SUMO_R_DFLT4               100
+
+#define SUMO_L_DFLT0               0
+#define SUMO_L_DFLT1               20
+#define SUMO_L_DFLT2               20
+#define SUMO_L_DFLT3               20
+#define SUMO_L_DFLT4               20
+#define SUMO_VRC_DFLT              0x30033
+#define SUMO_MGCGTTLOCAL0_DFLT     0
+#define SUMO_MGCGTTLOCAL1_DFLT     0
+#define SUMO_GICST_DFLT            19
+#define SUMO_SST_DFLT              8
+#define SUMO_VOLTAGEDROPT_DFLT     1
+#define SUMO_GFXPOWERGATINGT_DFLT  100
+
+/* sumo_dpm.c */
+void sumo_gfx_clockgating_initialize(struct radeon_device *rdev);
+void sumo_program_vc(struct radeon_device *rdev, u32 vrc);
+void sumo_clear_vc(struct radeon_device *rdev);
+void sumo_program_sstp(struct radeon_device *rdev);
+void sumo_take_smu_control(struct radeon_device *rdev, bool enable);
+void sumo_construct_sclk_voltage_mapping_table(struct radeon_device *rdev,
+                                              struct sumo_sclk_voltage_mapping_table *sclk_voltage_mapping_table,
+                                              ATOM_AVAILABLE_SCLK_LIST *table);
+void sumo_construct_vid_mapping_table(struct radeon_device *rdev,
+                                     struct sumo_vid_mapping_table *vid_mapping_table,
+                                     ATOM_AVAILABLE_SCLK_LIST *table);
+u32 sumo_convert_vid2_to_vid7(struct radeon_device *rdev,
+                             struct sumo_vid_mapping_table *vid_mapping_table,
+                             u32 vid_2bit);
+u32 sumo_get_sleep_divider_from_id(u32 id);
+u32 sumo_get_sleep_divider_id_from_clock(struct radeon_device *rdev,
+                                        u32 sclk,
+                                        u32 min_sclk_in_sr);
+
+/* sumo_smc.c */
+void sumo_initialize_m3_arb(struct radeon_device *rdev);
+void sumo_smu_pg_init(struct radeon_device *rdev);
+void sumo_set_tdp_limit(struct radeon_device *rdev, u32 index, u32 tdp_limit);
+void sumo_smu_notify_alt_vddnb_change(struct radeon_device *rdev,
+                                     bool powersaving, bool force_nbps1);
+void sumo_boost_state_enable(struct radeon_device *rdev, bool enable);
+void sumo_enable_boost_timer(struct radeon_device *rdev);
+u32 sumo_get_running_fw_version(struct radeon_device *rdev);
+
+#endif
diff --git a/drivers/gpu/drm/radeon/sumo_smc.c b/drivers/gpu/drm/radeon/sumo_smc.c
new file mode 100644 (file)
index 0000000..18abba5
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "sumod.h"
+#include "sumo_dpm.h"
+#include "ppsmc.h"
+
+#define SUMO_SMU_SERVICE_ROUTINE_PG_INIT        1
+#define SUMO_SMU_SERVICE_ROUTINE_ALTVDDNB_NOTIFY  27
+#define SUMO_SMU_SERVICE_ROUTINE_GFX_SRV_ID_20  20
+
+struct sumo_ps *sumo_get_ps(struct radeon_ps *rps);
+struct sumo_power_info *sumo_get_pi(struct radeon_device *rdev);
+
+static void sumo_send_msg_to_smu(struct radeon_device *rdev, u32 id)
+{
+       u32 gfx_int_req;
+       int i;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(GFX_INT_STATUS) & INT_DONE)
+                       break;
+               udelay(1);
+       }
+
+       gfx_int_req = SERV_INDEX(id) | INT_REQ;
+       WREG32(GFX_INT_REQ, gfx_int_req);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(GFX_INT_REQ) & INT_REQ)
+                       break;
+               udelay(1);
+       }
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(GFX_INT_STATUS) & INT_ACK)
+                       break;
+               udelay(1);
+       }
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(GFX_INT_STATUS) & INT_DONE)
+                       break;
+               udelay(1);
+       }
+
+       gfx_int_req &= ~INT_REQ;
+       WREG32(GFX_INT_REQ, gfx_int_req);
+}
+
+void sumo_initialize_m3_arb(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       u32 i;
+
+       if (!pi->enable_dynamic_m3_arbiter)
+               return;
+
+       for (i = 0; i < NUMBER_OF_M3ARB_PARAM_SETS; i++)
+               WREG32_RCU(MCU_M3ARB_PARAMS + (i * 4),
+                          pi->sys_info.csr_m3_arb_cntl_default[i]);
+
+       for (; i < NUMBER_OF_M3ARB_PARAM_SETS * 2; i++)
+               WREG32_RCU(MCU_M3ARB_PARAMS + (i * 4),
+                          pi->sys_info.csr_m3_arb_cntl_uvd[i % NUMBER_OF_M3ARB_PARAM_SETS]);
+
+       for (; i < NUMBER_OF_M3ARB_PARAM_SETS * 3; i++)
+               WREG32_RCU(MCU_M3ARB_PARAMS + (i * 4),
+                          pi->sys_info.csr_m3_arb_cntl_fs3d[i % NUMBER_OF_M3ARB_PARAM_SETS]);
+}
+
+static bool sumo_is_alt_vddnb_supported(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       bool return_code = false;
+
+       if (!pi->enable_alt_vddnb)
+               return return_code;
+
+       if ((rdev->family == CHIP_SUMO) || (rdev->family == CHIP_SUMO2)) {
+               if (pi->fw_version >= 0x00010C00)
+                       return_code = true;
+       }
+
+       return return_code;
+}
+
+void sumo_smu_notify_alt_vddnb_change(struct radeon_device *rdev,
+                                     bool powersaving, bool force_nbps1)
+{
+       u32 param = 0;
+
+       if (!sumo_is_alt_vddnb_supported(rdev))
+               return;
+
+       if (powersaving)
+               param |= 1;
+
+       if (force_nbps1)
+               param |= 2;
+
+       WREG32_RCU(RCU_ALTVDDNB_NOTIFY, param);
+
+       sumo_send_msg_to_smu(rdev, SUMO_SMU_SERVICE_ROUTINE_ALTVDDNB_NOTIFY);
+}
+
+void sumo_smu_pg_init(struct radeon_device *rdev)
+{
+       sumo_send_msg_to_smu(rdev, SUMO_SMU_SERVICE_ROUTINE_PG_INIT);
+}
+
+static u32 sumo_power_of_4(u32 unit)
+{
+       u32 ret = 1;
+       u32 i;
+
+       for (i = 0; i < unit; i++)
+               ret *= 4;
+
+       return ret;
+}
+
+void sumo_enable_boost_timer(struct radeon_device *rdev)
+{
+       struct sumo_power_info *pi = sumo_get_pi(rdev);
+       u32 period, unit, timer_value;
+       u32 xclk = radeon_get_xclk(rdev);
+
+       unit = (RREG32_RCU(RCU_LCLK_SCALING_CNTL) & LCLK_SCALING_TIMER_PRESCALER_MASK)
+               >> LCLK_SCALING_TIMER_PRESCALER_SHIFT;
+
+       period = 100 * (xclk / 100 / sumo_power_of_4(unit));
+
+       timer_value = (period << 16) | (unit << 4);
+
+       WREG32_RCU(RCU_GNB_PWR_REP_TIMER_CNTL, timer_value);
+       WREG32_RCU(RCU_BOOST_MARGIN, pi->sys_info.sclk_dpm_boost_margin);
+       WREG32_RCU(RCU_THROTTLE_MARGIN, pi->sys_info.sclk_dpm_throttle_margin);
+       WREG32_RCU(GNB_TDP_LIMIT, pi->sys_info.gnb_tdp_limit);
+       WREG32_RCU(RCU_SclkDpmTdpLimitPG, pi->sys_info.sclk_dpm_tdp_limit_pg);
+
+       sumo_send_msg_to_smu(rdev, SUMO_SMU_SERVICE_ROUTINE_GFX_SRV_ID_20);
+}
+
+void sumo_set_tdp_limit(struct radeon_device *rdev, u32 index, u32 tdp_limit)
+{
+       u32 regoffset = 0;
+       u32 shift = 0;
+       u32 mask = 0xFFF;
+       u32 sclk_dpm_tdp_limit;
+
+       switch (index) {
+       case 0:
+               regoffset = RCU_SclkDpmTdpLimit01;
+               shift = 16;
+               break;
+       case 1:
+               regoffset = RCU_SclkDpmTdpLimit01;
+               shift = 0;
+               break;
+       case 2:
+               regoffset = RCU_SclkDpmTdpLimit23;
+               shift = 16;
+               break;
+       case 3:
+               regoffset = RCU_SclkDpmTdpLimit23;
+               shift = 0;
+               break;
+       case 4:
+               regoffset = RCU_SclkDpmTdpLimit47;
+               shift = 16;
+               break;
+       case 7:
+               regoffset = RCU_SclkDpmTdpLimit47;
+               shift = 0;
+               break;
+       default:
+               break;
+       }
+
+       sclk_dpm_tdp_limit = RREG32_RCU(regoffset);
+       sclk_dpm_tdp_limit &= ~(mask << shift);
+       sclk_dpm_tdp_limit |= (tdp_limit << shift);
+       WREG32_RCU(regoffset, sclk_dpm_tdp_limit);
+}
+
+void sumo_boost_state_enable(struct radeon_device *rdev, bool enable)
+{
+       u32 boost_disable = RREG32_RCU(RCU_GPU_BOOST_DISABLE);
+
+       boost_disable &= 0xFFFFFFFE;
+       boost_disable |= (enable ? 0 : 1);
+       WREG32_RCU(RCU_GPU_BOOST_DISABLE, boost_disable);
+}
+
+u32 sumo_get_running_fw_version(struct radeon_device *rdev)
+{
+       return RREG32_RCU(RCU_FW_VERSION);
+}
+
diff --git a/drivers/gpu/drm/radeon/sumod.h b/drivers/gpu/drm/radeon/sumod.h
new file mode 100644 (file)
index 0000000..7c9c2d4
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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: Alex Deucher
+ */
+#ifndef _SUMOD_H_
+#define _SUMOD_H_
+
+/* pm registers */
+
+/* rcu */
+#define RCU_FW_VERSION                                  0x30c
+
+#define RCU_PWR_GATING_SEQ0                             0x408
+#define RCU_PWR_GATING_SEQ1                             0x40c
+#define RCU_PWR_GATING_CNTL                             0x410
+#       define PWR_GATING_EN                            (1 << 0)
+#       define RSVD_MASK                                (0x3 << 1)
+#       define PCV(x)                                   ((x) << 3)
+#       define PCV_MASK                                 (0x1f << 3)
+#       define PCV_SHIFT                                3
+#       define PCP(x)                                   ((x) << 8)
+#       define PCP_MASK                                 (0xf << 8)
+#       define PCP_SHIFT                                8
+#       define RPW(x)                                   ((x) << 16)
+#       define RPW_MASK                                 (0xf << 16)
+#       define RPW_SHIFT                                16
+#       define ID(x)                                    ((x) << 24)
+#       define ID_MASK                                  (0xf << 24)
+#       define ID_SHIFT                                 24
+#       define PGS(x)                                   ((x) << 28)
+#       define PGS_MASK                                 (0xf << 28)
+#       define PGS_SHIFT                                28
+
+#define RCU_ALTVDDNB_NOTIFY                             0x430
+#define RCU_LCLK_SCALING_CNTL                           0x434
+#       define LCLK_SCALING_EN                          (1 << 0)
+#       define LCLK_SCALING_TYPE                        (1 << 1)
+#       define LCLK_SCALING_TIMER_PRESCALER(x)          ((x) << 4)
+#       define LCLK_SCALING_TIMER_PRESCALER_MASK        (0xf << 4)
+#       define LCLK_SCALING_TIMER_PRESCALER_SHIFT       4
+#       define LCLK_SCALING_TIMER_PERIOD(x)             ((x) << 16)
+#       define LCLK_SCALING_TIMER_PERIOD_MASK           (0xf << 16)
+#       define LCLK_SCALING_TIMER_PERIOD_SHIFT          16
+
+#define RCU_PWR_GATING_CNTL_2                           0x4a0
+#       define MPPU(x)                                  ((x) << 0)
+#       define MPPU_MASK                                (0xffff << 0)
+#       define MPPU_SHIFT                               0
+#       define MPPD(x)                                  ((x) << 16)
+#       define MPPD_MASK                                (0xffff << 16)
+#       define MPPD_SHIFT                               16
+#define RCU_PWR_GATING_CNTL_3                           0x4a4
+#       define DPPU(x)                                  ((x) << 0)
+#       define DPPU_MASK                                (0xffff << 0)
+#       define DPPU_SHIFT                               0
+#       define DPPD(x)                                  ((x) << 16)
+#       define DPPD_MASK                                (0xffff << 16)
+#       define DPPD_SHIFT                               16
+#define RCU_PWR_GATING_CNTL_4                           0x4a8
+#       define RT(x)                                    ((x) << 0)
+#       define RT_MASK                                  (0xffff << 0)
+#       define RT_SHIFT                                 0
+#       define IT(x)                                    ((x) << 16)
+#       define IT_MASK                                  (0xffff << 16)
+#       define IT_SHIFT                                 16
+
+/* yes these two have the same address */
+#define RCU_PWR_GATING_CNTL_5                           0x504
+#define RCU_GPU_BOOST_DISABLE                           0x508
+
+#define MCU_M3ARB_INDEX                                 0x504
+#define MCU_M3ARB_PARAMS                                0x508
+
+#define RCU_GNB_PWR_REP_TIMER_CNTL                      0x50C
+
+#define RCU_SclkDpmTdpLimit01                           0x514
+#define RCU_SclkDpmTdpLimit23                           0x518
+#define RCU_SclkDpmTdpLimit47                           0x51C
+#define RCU_SclkDpmTdpLimitPG                           0x520
+
+#define GNB_TDP_LIMIT                                   0x540
+#define RCU_BOOST_MARGIN                                0x544
+#define RCU_THROTTLE_MARGIN                             0x548
+
+#define SMU_PCIE_PG_ARGS                                0x58C
+#define SMU_PCIE_PG_ARGS_2                              0x598
+#define SMU_PCIE_PG_ARGS_3                              0x59C
+
+/* mmio */
+#define RCU_STATUS                                      0x11c
+#       define GMC_PWR_GATER_BUSY                       (1 << 8)
+#       define GFX_PWR_GATER_BUSY                       (1 << 9)
+#       define UVD_PWR_GATER_BUSY                       (1 << 10)
+#       define PCIE_PWR_GATER_BUSY                      (1 << 11)
+#       define GMC_PWR_GATER_STATE                      (1 << 12)
+#       define GFX_PWR_GATER_STATE                      (1 << 13)
+#       define UVD_PWR_GATER_STATE                      (1 << 14)
+#       define PCIE_PWR_GATER_STATE                     (1 << 15)
+#       define GFX1_PWR_GATER_BUSY                      (1 << 16)
+#       define GFX2_PWR_GATER_BUSY                      (1 << 17)
+#       define GFX1_PWR_GATER_STATE                     (1 << 18)
+#       define GFX2_PWR_GATER_STATE                     (1 << 19)
+
+#define GFX_INT_REQ                                     0x120
+#       define INT_REQ                                  (1 << 0)
+#       define SERV_INDEX(x)                            ((x) << 1)
+#       define SERV_INDEX_MASK                          (0xff << 1)
+#       define SERV_INDEX_SHIFT                         1
+#define GFX_INT_STATUS                                  0x124
+#       define INT_ACK                                  (1 << 0)
+#       define INT_DONE                                 (1 << 1)
+
+#define CG_SCLK_CNTL                                    0x600
+#       define SCLK_DIVIDER(x)                          ((x) << 0)
+#       define SCLK_DIVIDER_MASK                        (0x7f << 0)
+#       define SCLK_DIVIDER_SHIFT                       0
+#define CG_SCLK_STATUS                                  0x604
+#       define SCLK_OVERCLK_DETECT                      (1 << 2)
+
+#define CG_DCLK_CNTL                                    0x610
+#       define DCLK_DIVIDER_MASK                        0x7f
+#       define DCLK_DIR_CNTL_EN                         (1 << 8)
+#define CG_DCLK_STATUS                                  0x614
+#       define DCLK_STATUS                              (1 << 0)
+#define CG_VCLK_CNTL                                    0x618
+#       define VCLK_DIVIDER_MASK                        0x7f
+#       define VCLK_DIR_CNTL_EN                         (1 << 8)
+#define CG_VCLK_STATUS                                  0x61c
+
+#define GENERAL_PWRMGT                                  0x63c
+#       define STATIC_PM_EN                             (1 << 1)
+
+#define SCLK_PWRMGT_CNTL                                0x644
+#       define SCLK_PWRMGT_OFF                          (1 << 0)
+#       define SCLK_LOW_D1                              (1 << 1)
+#       define FIR_RESET                                (1 << 4)
+#       define FIR_FORCE_TREND_SEL                      (1 << 5)
+#       define FIR_TREND_MODE                           (1 << 6)
+#       define DYN_GFX_CLK_OFF_EN                       (1 << 7)
+#       define GFX_CLK_FORCE_ON                         (1 << 8)
+#       define GFX_CLK_REQUEST_OFF                      (1 << 9)
+#       define GFX_CLK_FORCE_OFF                        (1 << 10)
+#       define GFX_CLK_OFF_ACPI_D1                      (1 << 11)
+#       define GFX_CLK_OFF_ACPI_D2                      (1 << 12)
+#       define GFX_CLK_OFF_ACPI_D3                      (1 << 13)
+#       define GFX_VOLTAGE_CHANGE_EN                    (1 << 16)
+#       define GFX_VOLTAGE_CHANGE_MODE                  (1 << 17)
+
+#define TARGET_AND_CURRENT_PROFILE_INDEX                0x66c
+#       define TARG_SCLK_INDEX(x)                       ((x) << 6)
+#       define TARG_SCLK_INDEX_MASK                     (0x7 << 6)
+#       define TARG_SCLK_INDEX_SHIFT                    6
+#       define CURR_SCLK_INDEX(x)                       ((x) << 9)
+#       define CURR_SCLK_INDEX_MASK                     (0x7 << 9)
+#       define CURR_SCLK_INDEX_SHIFT                    9
+#       define TARG_INDEX(x)                            ((x) << 12)
+#       define TARG_INDEX_MASK                          (0x7 << 12)
+#       define TARG_INDEX_SHIFT                         12
+#       define CURR_INDEX(x)                            ((x) << 15)
+#       define CURR_INDEX_MASK                          (0x7 << 15)
+#       define CURR_INDEX_SHIFT                         15
+
+#define CG_SCLK_DPM_CTRL                                0x684
+#       define SCLK_FSTATE_0_DIV(x)                     ((x) << 0)
+#       define SCLK_FSTATE_0_DIV_MASK                   (0x7f << 0)
+#       define SCLK_FSTATE_0_DIV_SHIFT                  0
+#       define SCLK_FSTATE_0_VLD                        (1 << 7)
+#       define SCLK_FSTATE_1_DIV(x)                     ((x) << 8)
+#       define SCLK_FSTATE_1_DIV_MASK                   (0x7f << 8)
+#       define SCLK_FSTATE_1_DIV_SHIFT                  8
+#       define SCLK_FSTATE_1_VLD                        (1 << 15)
+#       define SCLK_FSTATE_2_DIV(x)                     ((x) << 16)
+#       define SCLK_FSTATE_2_DIV_MASK                   (0x7f << 16)
+#       define SCLK_FSTATE_2_DIV_SHIFT                  16
+#       define SCLK_FSTATE_2_VLD                        (1 << 23)
+#       define SCLK_FSTATE_3_DIV(x)                     ((x) << 24)
+#       define SCLK_FSTATE_3_DIV_MASK                   (0x7f << 24)
+#       define SCLK_FSTATE_3_DIV_SHIFT                  24
+#       define SCLK_FSTATE_3_VLD                        (1 << 31)
+#define CG_SCLK_DPM_CTRL_2                              0x688
+#define CG_GCOOR                                        0x68c
+#       define PHC(x)                                   ((x) << 0)
+#       define PHC_MASK                                 (0x1f << 0)
+#       define PHC_SHIFT                                0
+#       define SDC(x)                                   ((x) << 9)
+#       define SDC_MASK                                 (0x3ff << 9)
+#       define SDC_SHIFT                                9
+#       define SU(x)                                    ((x) << 23)
+#       define SU_MASK                                  (0xf << 23)
+#       define SU_SHIFT                                 23
+#       define DIV_ID(x)                                ((x) << 28)
+#       define DIV_ID_MASK                              (0x7 << 28)
+#       define DIV_ID_SHIFT                             28
+
+#define CG_FTV                                          0x690
+#define CG_FFCT_0                                       0x694
+#       define UTC_0(x)                                 ((x) << 0)
+#       define UTC_0_MASK                               (0x3ff << 0)
+#       define UTC_0_SHIFT                              0
+#       define DTC_0(x)                                 ((x) << 10)
+#       define DTC_0_MASK                               (0x3ff << 10)
+#       define DTC_0_SHIFT                              10
+
+#define CG_GIT                                          0x6d8
+#       define CG_GICST(x)                              ((x) << 0)
+#       define CG_GICST_MASK                            (0xffff << 0)
+#       define CG_GICST_SHIFT                           0
+#       define CG_GIPOT(x)                              ((x) << 16)
+#       define CG_GIPOT_MASK                            (0xffff << 16)
+#       define CG_GIPOT_SHIFT                           16
+
+#define CG_SCLK_DPM_CTRL_3                              0x6e0
+#       define FORCE_SCLK_STATE(x)                      ((x) << 0)
+#       define FORCE_SCLK_STATE_MASK                    (0x7 << 0)
+#       define FORCE_SCLK_STATE_SHIFT                   0
+#       define FORCE_SCLK_STATE_EN                      (1 << 3)
+#       define GNB_TT(x)                                ((x) << 8)
+#       define GNB_TT_MASK                              (0xff << 8)
+#       define GNB_TT_SHIFT                             8
+#       define GNB_THERMTHRO_MASK                       (1 << 16)
+#       define CNB_THERMTHRO_MASK_SCLK                  (1 << 17)
+#       define DPM_SCLK_ENABLE                          (1 << 18)
+#       define GNB_SLOW_FSTATE_0_MASK                   (1 << 23)
+#       define GNB_SLOW_FSTATE_0_SHIFT                  23
+#       define FORCE_NB_PSTATE_1                        (1 << 31)
+
+#define CG_SSP                                          0x6e8
+#       define SST(x)                                   ((x) << 0)
+#       define SST_MASK                                 (0xffff << 0)
+#       define SST_SHIFT                                0
+#       define SSTU(x)                                  ((x) << 16)
+#       define SSTU_MASK                                (0xffff << 16)
+#       define SSTU_SHIFT                               16
+
+#define CG_ACPI_CNTL                                    0x70c
+#       define SCLK_ACPI_DIV(x)                         ((x) << 0)
+#       define SCLK_ACPI_DIV_MASK                       (0x7f << 0)
+#       define SCLK_ACPI_DIV_SHIFT                      0
+
+#define CG_SCLK_DPM_CTRL_4                              0x71c
+#       define DC_HDC(x)                                ((x) << 14)
+#       define DC_HDC_MASK                              (0x3fff << 14)
+#       define DC_HDC_SHIFT                             14
+#       define DC_HU(x)                                 ((x) << 28)
+#       define DC_HU_MASK                               (0xf << 28)
+#       define DC_HU_SHIFT                              28
+#define CG_SCLK_DPM_CTRL_5                              0x720
+#       define SCLK_FSTATE_BOOTUP(x)                    ((x) << 0)
+#       define SCLK_FSTATE_BOOTUP_MASK                  (0x7 << 0)
+#       define SCLK_FSTATE_BOOTUP_SHIFT                 0
+#       define TT_TP(x)                                 ((x) << 3)
+#       define TT_TP_MASK                               (0xffff << 3)
+#       define TT_TP_SHIFT                              3
+#       define TT_TU(x)                                 ((x) << 19)
+#       define TT_TU_MASK                               (0xff << 19)
+#       define TT_TU_SHIFT                              19
+#define CG_SCLK_DPM_CTRL_6                              0x724
+#define CG_AT_0                                         0x728
+#       define CG_R(x)                                  ((x) << 0)
+#       define CG_R_MASK                                (0xffff << 0)
+#       define CG_R_SHIFT                               0
+#       define CG_L(x)                                  ((x) << 16)
+#       define CG_L_MASK                                (0xffff << 16)
+#       define CG_L_SHIFT                               16
+#define CG_AT_1                                         0x72c
+#define CG_AT_2                                         0x730
+#define        CG_THERMAL_INT                                  0x734
+#define                DIG_THERM_INTH(x)                       ((x) << 8)
+#define                DIG_THERM_INTH_MASK                     0x0000FF00
+#define                DIG_THERM_INTH_SHIFT                    8
+#define                DIG_THERM_INTL(x)                       ((x) << 16)
+#define                DIG_THERM_INTL_MASK                     0x00FF0000
+#define                DIG_THERM_INTL_SHIFT                    16
+#define        THERM_INT_MASK_HIGH                     (1 << 24)
+#define        THERM_INT_MASK_LOW                      (1 << 25)
+#define CG_AT_3                                         0x738
+#define CG_AT_4                                         0x73c
+#define CG_AT_5                                         0x740
+#define CG_AT_6                                         0x744
+#define CG_AT_7                                         0x748
+
+#define CG_BSP_0                                        0x750
+#       define BSP(x)                                   ((x) << 0)
+#       define BSP_MASK                                 (0xffff << 0)
+#       define BSP_SHIFT                                0
+#       define BSU(x)                                   ((x) << 16)
+#       define BSU_MASK                                 (0xf << 16)
+#       define BSU_SHIFT                                16
+
+#define CG_CG_VOLTAGE_CNTL                              0x770
+#       define REQ                                      (1 << 0)
+#       define LEVEL(x)                                 ((x) << 1)
+#       define LEVEL_MASK                               (0x3 << 1)
+#       define LEVEL_SHIFT                              1
+#       define CG_VOLTAGE_EN                            (1 << 3)
+#       define FORCE                                    (1 << 4)
+#       define PERIOD(x)                                ((x) << 8)
+#       define PERIOD_MASK                              (0xffff << 8)
+#       define PERIOD_SHIFT                             8
+#       define UNIT(x)                                  ((x) << 24)
+#       define UNIT_MASK                                (0xf << 24)
+#       define UNIT_SHIFT                               24
+
+#define CG_ACPI_VOLTAGE_CNTL                            0x780
+#       define ACPI_VOLTAGE_EN                          (1 << 8)
+
+#define CG_DPM_VOLTAGE_CNTL                             0x788
+#       define DPM_STATE0_LEVEL_MASK                    (0x3 << 0)
+#       define DPM_STATE0_LEVEL_SHIFT                   0
+#       define DPM_VOLTAGE_EN                           (1 << 16)
+
+#define CG_PWR_GATING_CNTL                              0x7ac
+#       define DYN_PWR_DOWN_EN                          (1 << 0)
+#       define ACPI_PWR_DOWN_EN                         (1 << 1)
+#       define GFX_CLK_OFF_PWR_DOWN_EN                  (1 << 2)
+#       define IOC_DISGPU_PWR_DOWN_EN                   (1 << 3)
+#       define FORCE_POWR_ON                            (1 << 4)
+#       define PGP(x)                                   ((x) << 8)
+#       define PGP_MASK                                 (0xffff << 8)
+#       define PGP_SHIFT                                8
+#       define PGU(x)                                   ((x) << 24)
+#       define PGU_MASK                                 (0xf << 24)
+#       define PGU_SHIFT                                24
+
+#define CG_CGTT_LOCAL_0                                 0x7d0
+#define CG_CGTT_LOCAL_1                                 0x7d4
+
+#define DEEP_SLEEP_CNTL                                 0x818
+#       define R_DIS                                    (1 << 3)
+#       define HS(x)                                    ((x) << 4)
+#       define HS_MASK                                  (0xfff << 4)
+#       define HS_SHIFT                                 4
+#       define ENABLE_DS                                (1 << 31)
+#define DEEP_SLEEP_CNTL2                                0x81c
+#       define LB_UFP_EN                                (1 << 0)
+#       define INOUT_C(x)                               ((x) << 4)
+#       define INOUT_C_MASK                             (0xff << 4)
+#       define INOUT_C_SHIFT                            4
+
+#define CG_SCRATCH2                                     0x824
+
+#define CG_SCLK_DPM_CTRL_11                             0x830
+
+#define HW_REV                                         0x5564
+#       define ATI_REV_ID_MASK                          (0xf << 28)
+#       define ATI_REV_ID_SHIFT                         28
+/* 0 = A0, 1 = A1, 2 = B0, 3 = C0, etc. */
+
+#define DOUT_SCRATCH3                                  0x611c
+
+#define GB_ADDR_CONFIG                                 0x98f8
+
+#endif
diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c
new file mode 100644 (file)
index 0000000..a1eb5f5
--- /dev/null
@@ -0,0 +1,1949 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "trinityd.h"
+#include "r600_dpm.h"
+#include "trinity_dpm.h"
+#include <linux/seq_file.h>
+
+#define TRINITY_MAX_DEEPSLEEP_DIVIDER_ID 5
+#define TRINITY_MINIMUM_ENGINE_CLOCK 800
+#define SCLK_MIN_DIV_INTV_SHIFT     12
+#define TRINITY_DISPCLK_BYPASS_THRESHOLD 10000
+
+#ifndef TRINITY_MGCG_SEQUENCE
+#define TRINITY_MGCG_SEQUENCE  100
+
+static const u32 trinity_mgcg_shls_default[] =
+{
+       /* Register, Value, Mask */
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x00003fc4, 0xc0000000, 0xffffffff,
+       0x00005448, 0x00000100, 0xffffffff,
+       0x000055e4, 0x00000100, 0xffffffff,
+       0x0000160c, 0x00000100, 0xffffffff,
+       0x00008984, 0x06000100, 0xffffffff,
+       0x0000c164, 0x00000100, 0xffffffff,
+       0x00008a18, 0x00000100, 0xffffffff,
+       0x0000897c, 0x06000100, 0xffffffff,
+       0x00008b28, 0x00000100, 0xffffffff,
+       0x00009144, 0x00800200, 0xffffffff,
+       0x00009a60, 0x00000100, 0xffffffff,
+       0x00009868, 0x00000100, 0xffffffff,
+       0x00008d58, 0x00000100, 0xffffffff,
+       0x00009510, 0x00000100, 0xffffffff,
+       0x0000949c, 0x00000100, 0xffffffff,
+       0x00009654, 0x00000100, 0xffffffff,
+       0x00009030, 0x00000100, 0xffffffff,
+       0x00009034, 0x00000100, 0xffffffff,
+       0x00009038, 0x00000100, 0xffffffff,
+       0x0000903c, 0x00000100, 0xffffffff,
+       0x00009040, 0x00000100, 0xffffffff,
+       0x0000a200, 0x00000100, 0xffffffff,
+       0x0000a204, 0x00000100, 0xffffffff,
+       0x0000a208, 0x00000100, 0xffffffff,
+       0x0000a20c, 0x00000100, 0xffffffff,
+       0x00009744, 0x00000100, 0xffffffff,
+       0x00003f80, 0x00000100, 0xffffffff,
+       0x0000a210, 0x00000100, 0xffffffff,
+       0x0000a214, 0x00000100, 0xffffffff,
+       0x000004d8, 0x00000100, 0xffffffff,
+       0x00009664, 0x00000100, 0xffffffff,
+       0x00009698, 0x00000100, 0xffffffff,
+       0x000004d4, 0x00000200, 0xffffffff,
+       0x000004d0, 0x00000000, 0xffffffff,
+       0x000030cc, 0x00000104, 0xffffffff,
+       0x0000d0c0, 0x00000100, 0xffffffff,
+       0x0000d8c0, 0x00000100, 0xffffffff,
+       0x0000951c, 0x00010000, 0xffffffff,
+       0x00009160, 0x00030002, 0xffffffff,
+       0x00009164, 0x00050004, 0xffffffff,
+       0x00009168, 0x00070006, 0xffffffff,
+       0x00009178, 0x00070000, 0xffffffff,
+       0x0000917c, 0x00030002, 0xffffffff,
+       0x00009180, 0x00050004, 0xffffffff,
+       0x0000918c, 0x00010006, 0xffffffff,
+       0x00009190, 0x00090008, 0xffffffff,
+       0x00009194, 0x00070000, 0xffffffff,
+       0x00009198, 0x00030002, 0xffffffff,
+       0x0000919c, 0x00050004, 0xffffffff,
+       0x000091a8, 0x00010006, 0xffffffff,
+       0x000091ac, 0x00090008, 0xffffffff,
+       0x000091b0, 0x00070000, 0xffffffff,
+       0x000091b4, 0x00030002, 0xffffffff,
+       0x000091b8, 0x00050004, 0xffffffff,
+       0x000091c4, 0x00010006, 0xffffffff,
+       0x000091c8, 0x00090008, 0xffffffff,
+       0x000091cc, 0x00070000, 0xffffffff,
+       0x000091d0, 0x00030002, 0xffffffff,
+       0x000091d4, 0x00050004, 0xffffffff,
+       0x000091e0, 0x00010006, 0xffffffff,
+       0x000091e4, 0x00090008, 0xffffffff,
+       0x000091e8, 0x00000000, 0xffffffff,
+       0x000091ec, 0x00070000, 0xffffffff,
+       0x000091f0, 0x00030002, 0xffffffff,
+       0x000091f4, 0x00050004, 0xffffffff,
+       0x00009200, 0x00010006, 0xffffffff,
+       0x00009204, 0x00090008, 0xffffffff,
+       0x00009208, 0x00070000, 0xffffffff,
+       0x0000920c, 0x00030002, 0xffffffff,
+       0x00009210, 0x00050004, 0xffffffff,
+       0x0000921c, 0x00010006, 0xffffffff,
+       0x00009220, 0x00090008, 0xffffffff,
+       0x00009294, 0x00000000, 0xffffffff
+};
+
+static const u32 trinity_mgcg_shls_enable[] =
+{
+       /* Register, Value, Mask */
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x000008f8, 0x00000000, 0xffffffff,
+       0x000008fc, 0x00000000, 0x000133FF,
+       0x000008f8, 0x00000001, 0xffffffff,
+       0x000008fc, 0x00000000, 0xE00B03FC,
+       0x00009150, 0x96944200, 0xffffffff
+};
+
+static const u32 trinity_mgcg_shls_disable[] =
+{
+       /* Register, Value, Mask */
+       0x0000802c, 0xc0000000, 0xffffffff,
+       0x00009150, 0x00600000, 0xffffffff,
+       0x000008f8, 0x00000000, 0xffffffff,
+       0x000008fc, 0xffffffff, 0x000133FF,
+       0x000008f8, 0x00000001, 0xffffffff,
+       0x000008fc, 0xffffffff, 0xE00B03FC
+};
+#endif
+
+#ifndef TRINITY_SYSLS_SEQUENCE
+#define TRINITY_SYSLS_SEQUENCE  100
+
+static const u32 trinity_sysls_default[] =
+{
+       /* Register, Value, Mask */
+       0x000055e8, 0x00000000, 0xffffffff,
+       0x0000d0bc, 0x00000000, 0xffffffff,
+       0x0000d8bc, 0x00000000, 0xffffffff,
+       0x000015c0, 0x000c1401, 0xffffffff,
+       0x0000264c, 0x000c0400, 0xffffffff,
+       0x00002648, 0x000c0400, 0xffffffff,
+       0x00002650, 0x000c0400, 0xffffffff,
+       0x000020b8, 0x000c0400, 0xffffffff,
+       0x000020bc, 0x000c0400, 0xffffffff,
+       0x000020c0, 0x000c0c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680fff, 0xffffffff,
+       0x00002f50, 0x00000404, 0xffffffff,
+       0x000004c8, 0x00000001, 0xffffffff,
+       0x0000641c, 0x00000000, 0xffffffff,
+       0x00000c7c, 0x00000000, 0xffffffff,
+       0x00006dfc, 0x00000000, 0xffffffff
+};
+
+static const u32 trinity_sysls_disable[] =
+{
+       /* Register, Value, Mask */
+       0x0000d0c0, 0x00000000, 0xffffffff,
+       0x0000d8c0, 0x00000000, 0xffffffff,
+       0x000055e8, 0x00000000, 0xffffffff,
+       0x0000d0bc, 0x00000000, 0xffffffff,
+       0x0000d8bc, 0x00000000, 0xffffffff,
+       0x000015c0, 0x00041401, 0xffffffff,
+       0x0000264c, 0x00040400, 0xffffffff,
+       0x00002648, 0x00040400, 0xffffffff,
+       0x00002650, 0x00040400, 0xffffffff,
+       0x000020b8, 0x00040400, 0xffffffff,
+       0x000020bc, 0x00040400, 0xffffffff,
+       0x000020c0, 0x00040c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680000, 0xffffffff,
+       0x00002f50, 0x00000404, 0xffffffff,
+       0x000004c8, 0x00000001, 0xffffffff,
+       0x0000641c, 0x00007ffd, 0xffffffff,
+       0x00000c7c, 0x0000ff00, 0xffffffff,
+       0x00006dfc, 0x0000007f, 0xffffffff
+};
+
+static const u32 trinity_sysls_enable[] =
+{
+       /* Register, Value, Mask */
+       0x000055e8, 0x00000001, 0xffffffff,
+       0x0000d0bc, 0x00000100, 0xffffffff,
+       0x0000d8bc, 0x00000100, 0xffffffff,
+       0x000015c0, 0x000c1401, 0xffffffff,
+       0x0000264c, 0x000c0400, 0xffffffff,
+       0x00002648, 0x000c0400, 0xffffffff,
+       0x00002650, 0x000c0400, 0xffffffff,
+       0x000020b8, 0x000c0400, 0xffffffff,
+       0x000020bc, 0x000c0400, 0xffffffff,
+       0x000020c0, 0x000c0c80, 0xffffffff,
+       0x0000f4a0, 0x000000c0, 0xffffffff,
+       0x0000f4a4, 0x00680fff, 0xffffffff,
+       0x00002f50, 0x00000903, 0xffffffff,
+       0x000004c8, 0x00000000, 0xffffffff,
+       0x0000641c, 0x00000000, 0xffffffff,
+       0x00000c7c, 0x00000000, 0xffffffff,
+       0x00006dfc, 0x00000000, 0xffffffff
+};
+#endif
+
+static const u32 trinity_override_mgpg_sequences[] =
+{
+       /* Register, Value */
+       0x00000200, 0xE030032C,
+       0x00000204, 0x00000FFF,
+       0x00000200, 0xE0300058,
+       0x00000204, 0x00030301,
+       0x00000200, 0xE0300054,
+       0x00000204, 0x500010FF,
+       0x00000200, 0xE0300074,
+       0x00000204, 0x00030301,
+       0x00000200, 0xE0300070,
+       0x00000204, 0x500010FF,
+       0x00000200, 0xE0300090,
+       0x00000204, 0x00030301,
+       0x00000200, 0xE030008C,
+       0x00000204, 0x500010FF,
+       0x00000200, 0xE03000AC,
+       0x00000204, 0x00030301,
+       0x00000200, 0xE03000A8,
+       0x00000204, 0x500010FF,
+       0x00000200, 0xE03000C8,
+       0x00000204, 0x00030301,
+       0x00000200, 0xE03000C4,
+       0x00000204, 0x500010FF,
+       0x00000200, 0xE03000E4,
+       0x00000204, 0x00030301,
+       0x00000200, 0xE03000E0,
+       0x00000204, 0x500010FF,
+       0x00000200, 0xE0300100,
+       0x00000204, 0x00030301,
+       0x00000200, 0xE03000FC,
+       0x00000204, 0x500010FF,
+       0x00000200, 0xE0300058,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE0300054,
+       0x00000204, 0x600010FF,
+       0x00000200, 0xE0300074,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE0300070,
+       0x00000204, 0x600010FF,
+       0x00000200, 0xE0300090,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE030008C,
+       0x00000204, 0x600010FF,
+       0x00000200, 0xE03000AC,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE03000A8,
+       0x00000204, 0x600010FF,
+       0x00000200, 0xE03000C8,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE03000C4,
+       0x00000204, 0x600010FF,
+       0x00000200, 0xE03000E4,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE03000E0,
+       0x00000204, 0x600010FF,
+       0x00000200, 0xE0300100,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE03000FC,
+       0x00000204, 0x600010FF,
+       0x00000200, 0xE0300058,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE0300054,
+       0x00000204, 0x700010FF,
+       0x00000200, 0xE0300074,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE0300070,
+       0x00000204, 0x700010FF,
+       0x00000200, 0xE0300090,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE030008C,
+       0x00000204, 0x700010FF,
+       0x00000200, 0xE03000AC,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE03000A8,
+       0x00000204, 0x700010FF,
+       0x00000200, 0xE03000C8,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE03000C4,
+       0x00000204, 0x700010FF,
+       0x00000200, 0xE03000E4,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE03000E0,
+       0x00000204, 0x700010FF,
+       0x00000200, 0xE0300100,
+       0x00000204, 0x00030303,
+       0x00000200, 0xE03000FC,
+       0x00000204, 0x700010FF,
+       0x00000200, 0xE0300058,
+       0x00000204, 0x00010303,
+       0x00000200, 0xE0300054,
+       0x00000204, 0x800010FF,
+       0x00000200, 0xE0300074,
+       0x00000204, 0x00010303,
+       0x00000200, 0xE0300070,
+       0x00000204, 0x800010FF,
+       0x00000200, 0xE0300090,
+       0x00000204, 0x00010303,
+       0x00000200, 0xE030008C,
+       0x00000204, 0x800010FF,
+       0x00000200, 0xE03000AC,
+       0x00000204, 0x00010303,
+       0x00000200, 0xE03000A8,
+       0x00000204, 0x800010FF,
+       0x00000200, 0xE03000C4,
+       0x00000204, 0x800010FF,
+       0x00000200, 0xE03000C8,
+       0x00000204, 0x00010303,
+       0x00000200, 0xE03000E4,
+       0x00000204, 0x00010303,
+       0x00000200, 0xE03000E0,
+       0x00000204, 0x800010FF,
+       0x00000200, 0xE0300100,
+       0x00000204, 0x00010303,
+       0x00000200, 0xE03000FC,
+       0x00000204, 0x800010FF,
+       0x00000200, 0x0001f198,
+       0x00000204, 0x0003ffff,
+       0x00000200, 0x0001f19C,
+       0x00000204, 0x3fffffff,
+       0x00000200, 0xE030032C,
+       0x00000204, 0x00000000,
+};
+
+static void trinity_program_clk_gating_hw_sequence(struct radeon_device *rdev,
+                                                  const u32 *seq, u32 count);
+static void trinity_override_dynamic_mg_powergating(struct radeon_device *rdev);
+static void trinity_apply_state_adjust_rules(struct radeon_device *rdev,
+                                            struct radeon_ps *new_rps,
+                                            struct radeon_ps *old_rps);
+
+struct trinity_ps *trinity_get_ps(struct radeon_ps *rps)
+{
+       struct trinity_ps *ps = rps->ps_priv;
+
+       return ps;
+}
+
+struct trinity_power_info *trinity_get_pi(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = rdev->pm.dpm.priv;
+
+       return pi;
+}
+
+static void trinity_gfx_powergating_initialize(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 p, u;
+       u32 value;
+       struct atom_clock_dividers dividers;
+       u32 xclk = radeon_get_xclk(rdev);
+       u32 sssd = 1;
+       int ret;
+       u32 hw_rev = (RREG32(HW_REV) & ATI_REV_ID_MASK) >> ATI_REV_ID_SHIFT;
+
+        ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                             25000, false, &dividers);
+       if (ret)
+               return;
+
+       value = RREG32_SMC(GFX_POWER_GATING_CNTL);
+       value &= ~(SSSD_MASK | PDS_DIV_MASK);
+       if (sssd)
+               value |= SSSD(1);
+       value |= PDS_DIV(dividers.post_div);
+       WREG32_SMC(GFX_POWER_GATING_CNTL, value);
+
+       r600_calculate_u_and_p(500, xclk, 16, &p, &u);
+
+       WREG32(CG_PG_CTRL, SP(p) | SU(u));
+
+       WREG32_P(CG_GIPOTS, CG_GIPOT(p), ~CG_GIPOT_MASK);
+
+       /* XXX double check hw_rev */
+       if (pi->override_dynamic_mgpg && (hw_rev == 0))
+               trinity_override_dynamic_mg_powergating(rdev);
+
+}
+
+#define CGCG_CGTT_LOCAL0_MASK       0xFFFF33FF
+#define CGCG_CGTT_LOCAL1_MASK       0xFFFB0FFE
+#define CGTS_SM_CTRL_REG_DISABLE    0x00600000
+#define CGTS_SM_CTRL_REG_ENABLE     0x96944200
+
+static void trinity_mg_clockgating_enable(struct radeon_device *rdev,
+                                         bool enable)
+{
+       u32 local0;
+       u32 local1;
+
+       if (enable) {
+               local0 = RREG32_CG(CG_CGTT_LOCAL_0);
+               local1 = RREG32_CG(CG_CGTT_LOCAL_1);
+
+               WREG32_CG(CG_CGTT_LOCAL_0,
+                         (0x00380000 & CGCG_CGTT_LOCAL0_MASK) | (local0 & ~CGCG_CGTT_LOCAL0_MASK) );
+               WREG32_CG(CG_CGTT_LOCAL_1,
+                         (0x0E000000 & CGCG_CGTT_LOCAL1_MASK) | (local1 & ~CGCG_CGTT_LOCAL1_MASK) );
+
+               WREG32(CGTS_SM_CTRL_REG, CGTS_SM_CTRL_REG_ENABLE);
+       } else {
+               WREG32(CGTS_SM_CTRL_REG, CGTS_SM_CTRL_REG_DISABLE);
+
+               local0 = RREG32_CG(CG_CGTT_LOCAL_0);
+               local1 = RREG32_CG(CG_CGTT_LOCAL_1);
+
+               WREG32_CG(CG_CGTT_LOCAL_0,
+                         CGCG_CGTT_LOCAL0_MASK | (local0 & ~CGCG_CGTT_LOCAL0_MASK) );
+               WREG32_CG(CG_CGTT_LOCAL_1,
+                         CGCG_CGTT_LOCAL1_MASK | (local1 & ~CGCG_CGTT_LOCAL1_MASK) );
+       }
+}
+
+static void trinity_mg_clockgating_initialize(struct radeon_device *rdev)
+{
+       u32 count;
+       const u32 *seq = NULL;
+
+       seq = &trinity_mgcg_shls_default[0];
+       count = sizeof(trinity_mgcg_shls_default) / (3 * sizeof(u32));
+
+       trinity_program_clk_gating_hw_sequence(rdev, seq, count);
+}
+
+static void trinity_gfx_clockgating_enable(struct radeon_device *rdev,
+                                          bool enable)
+{
+       if (enable) {
+               WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN);
+       } else {
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN);
+               WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON);
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON);
+               RREG32(GB_ADDR_CONFIG);
+       }
+}
+
+static void trinity_program_clk_gating_hw_sequence(struct radeon_device *rdev,
+                                                  const u32 *seq, u32 count)
+{
+       u32 i, length = count * 3;
+
+       for (i = 0; i < length; i += 3)
+               WREG32_P(seq[i], seq[i+1], ~seq[i+2]);
+}
+
+static void trinity_program_override_mgpg_sequences(struct radeon_device *rdev,
+                                                   const u32 *seq, u32 count)
+{
+       u32  i, length = count * 2;
+
+       for (i = 0; i < length; i += 2)
+               WREG32(seq[i], seq[i+1]);
+
+}
+
+static void trinity_override_dynamic_mg_powergating(struct radeon_device *rdev)
+{
+       u32 count;
+       const u32 *seq = NULL;
+
+       seq = &trinity_override_mgpg_sequences[0];
+       count = sizeof(trinity_override_mgpg_sequences) / (2 * sizeof(u32));
+
+       trinity_program_override_mgpg_sequences(rdev, seq, count);
+}
+
+static void trinity_ls_clockgating_enable(struct radeon_device *rdev,
+                                         bool enable)
+{
+       u32 count;
+       const u32 *seq = NULL;
+
+       if (enable) {
+               seq = &trinity_sysls_enable[0];
+               count = sizeof(trinity_sysls_enable) / (3 * sizeof(u32));
+       } else {
+               seq = &trinity_sysls_disable[0];
+               count = sizeof(trinity_sysls_disable) / (3 * sizeof(u32));
+       }
+
+       trinity_program_clk_gating_hw_sequence(rdev, seq, count);
+}
+
+static void trinity_gfx_powergating_enable(struct radeon_device *rdev,
+                                          bool enable)
+{
+       if (enable) {
+               if (RREG32_SMC(CC_SMU_TST_EFUSE1_MISC) & RB_BACKEND_DISABLE_MASK)
+                       WREG32_SMC(SMU_SCRATCH_A, (RREG32_SMC(SMU_SCRATCH_A) | 0x01));
+
+               WREG32_P(SCLK_PWRMGT_CNTL, DYN_PWR_DOWN_EN, ~DYN_PWR_DOWN_EN);
+       } else {
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_PWR_DOWN_EN);
+               RREG32(GB_ADDR_CONFIG);
+       }
+}
+
+static void trinity_gfx_dynamic_mgpg_enable(struct radeon_device *rdev,
+                                           bool enable)
+{
+       u32 value;
+
+       if (enable) {
+               value = RREG32_SMC(PM_I_CNTL_1);
+               value &= ~DS_PG_CNTL_MASK;
+               value |= DS_PG_CNTL(1);
+               WREG32_SMC(PM_I_CNTL_1, value);
+
+               value = RREG32_SMC(SMU_S_PG_CNTL);
+               value &= ~DS_PG_EN_MASK;
+               value |= DS_PG_EN(1);
+               WREG32_SMC(SMU_S_PG_CNTL, value);
+       } else {
+               value = RREG32_SMC(SMU_S_PG_CNTL);
+               value &= ~DS_PG_EN_MASK;
+               WREG32_SMC(SMU_S_PG_CNTL, value);
+
+               value = RREG32_SMC(PM_I_CNTL_1);
+               value &= ~DS_PG_CNTL_MASK;
+               WREG32_SMC(PM_I_CNTL_1, value);
+       }
+
+       trinity_gfx_dynamic_mgpg_config(rdev);
+
+}
+
+static void trinity_enable_clock_power_gating(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       if (pi->enable_gfx_clock_gating)
+               sumo_gfx_clockgating_initialize(rdev);
+       if (pi->enable_mg_clock_gating)
+               trinity_mg_clockgating_initialize(rdev);
+       if (pi->enable_gfx_power_gating)
+               trinity_gfx_powergating_initialize(rdev);
+       if (pi->enable_mg_clock_gating) {
+               trinity_ls_clockgating_enable(rdev, true);
+               trinity_mg_clockgating_enable(rdev, true);
+       }
+       if (pi->enable_gfx_clock_gating)
+               trinity_gfx_clockgating_enable(rdev, true);
+       if (pi->enable_gfx_dynamic_mgpg)
+               trinity_gfx_dynamic_mgpg_enable(rdev, true);
+       if (pi->enable_gfx_power_gating)
+               trinity_gfx_powergating_enable(rdev, true);
+}
+
+static void trinity_disable_clock_power_gating(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       if (pi->enable_gfx_power_gating)
+               trinity_gfx_powergating_enable(rdev, false);
+       if (pi->enable_gfx_dynamic_mgpg)
+               trinity_gfx_dynamic_mgpg_enable(rdev, false);
+       if (pi->enable_gfx_clock_gating)
+               trinity_gfx_clockgating_enable(rdev, false);
+       if (pi->enable_mg_clock_gating) {
+               trinity_mg_clockgating_enable(rdev, false);
+               trinity_ls_clockgating_enable(rdev, false);
+       }
+}
+
+static void trinity_set_divider_value(struct radeon_device *rdev,
+                                     u32 index, u32 sclk)
+{
+       struct atom_clock_dividers  dividers;
+       int ret;
+       u32 value;
+       u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE;
+
+        ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                             sclk, false, &dividers);
+       if (ret)
+               return;
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix);
+       value &= ~CLK_DIVIDER_MASK;
+       value |= CLK_DIVIDER(dividers.post_div);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix, value);
+
+        ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                             sclk/2, false, &dividers);
+       if (ret)
+               return;
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_PG_CNTL + ix);
+       value &= ~PD_SCLK_DIVIDER_MASK;
+       value |= PD_SCLK_DIVIDER(dividers.post_div);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_PG_CNTL + ix, value);
+}
+
+static void trinity_set_ds_dividers(struct radeon_device *rdev,
+                                   u32 index, u32 divider)
+{
+       u32 value;
+       u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE;
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix);
+       value &= ~DS_DIV_MASK;
+       value |= DS_DIV(divider);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix, value);
+}
+
+static void trinity_set_ss_dividers(struct radeon_device *rdev,
+                                   u32 index, u32 divider)
+{
+       u32 value;
+       u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE;
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix);
+       value &= ~DS_SH_DIV_MASK;
+       value |= DS_SH_DIV(divider);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix, value);
+}
+
+static void trinity_set_vid(struct radeon_device *rdev, u32 index, u32 vid)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 vid_7bit = sumo_convert_vid2_to_vid7(rdev, &pi->sys_info.vid_mapping_table, vid);
+       u32 value;
+       u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE;
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix);
+       value &= ~VID_MASK;
+       value |= VID(vid_7bit);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix, value);
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix);
+       value &= ~LVRT_MASK;
+       value |= LVRT(0);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix, value);
+}
+
+static void trinity_set_allos_gnb_slow(struct radeon_device *rdev,
+                                      u32 index, u32 gnb_slow)
+{
+       u32 value;
+       u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE;
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_3 + ix);
+       value &= ~GNB_SLOW_MASK;
+       value |= GNB_SLOW(gnb_slow);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_3 + ix, value);
+}
+
+static void trinity_set_force_nbp_state(struct radeon_device *rdev,
+                                       u32 index, u32 force_nbp_state)
+{
+       u32 value;
+       u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE;
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_3 + ix);
+       value &= ~FORCE_NBPS1_MASK;
+       value |= FORCE_NBPS1(force_nbp_state);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_3 + ix, value);
+}
+
+static void trinity_set_display_wm(struct radeon_device *rdev,
+                                  u32 index, u32 wm)
+{
+       u32 value;
+       u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE;
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix);
+       value &= ~DISPLAY_WM_MASK;
+       value |= DISPLAY_WM(wm);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix, value);
+}
+
+static void trinity_set_vce_wm(struct radeon_device *rdev,
+                              u32 index, u32 wm)
+{
+       u32 value;
+       u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE;
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix);
+       value &= ~VCE_WM_MASK;
+       value |= VCE_WM(wm);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix, value);
+}
+
+static void trinity_set_at(struct radeon_device *rdev,
+                          u32 index, u32 at)
+{
+       u32 value;
+       u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE;
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_AT + ix);
+       value &= ~AT_MASK;
+       value |= AT(at);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_AT + ix, value);
+}
+
+static void trinity_program_power_level(struct radeon_device *rdev,
+                                       struct trinity_pl *pl, u32 index)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       if (index >= SUMO_MAX_HARDWARE_POWERLEVELS)
+               return;
+
+       trinity_set_divider_value(rdev, index, pl->sclk);
+       trinity_set_vid(rdev, index, pl->vddc_index);
+       trinity_set_ss_dividers(rdev, index, pl->ss_divider_index);
+       trinity_set_ds_dividers(rdev, index, pl->ds_divider_index);
+       trinity_set_allos_gnb_slow(rdev, index, pl->allow_gnb_slow);
+       trinity_set_force_nbp_state(rdev, index, pl->force_nbp_state);
+       trinity_set_display_wm(rdev, index, pl->display_wm);
+       trinity_set_vce_wm(rdev, index, pl->vce_wm);
+       trinity_set_at(rdev, index, pi->at[index]);
+}
+
+static void trinity_power_level_enable_disable(struct radeon_device *rdev,
+                                              u32 index, bool enable)
+{
+       u32 value;
+       u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE;
+
+       value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix);
+       value &= ~STATE_VALID_MASK;
+       if (enable)
+               value |= STATE_VALID(1);
+       WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix, value);
+}
+
+static bool trinity_dpm_enabled(struct radeon_device *rdev)
+{
+       if (RREG32_SMC(SMU_SCLK_DPM_CNTL) & SCLK_DPM_EN(1))
+               return true;
+       else
+               return false;
+}
+
+static void trinity_start_dpm(struct radeon_device *rdev)
+{
+       u32 value = RREG32_SMC(SMU_SCLK_DPM_CNTL);
+
+       value &= ~(SCLK_DPM_EN_MASK | SCLK_DPM_BOOT_STATE_MASK | VOLTAGE_CHG_EN_MASK);
+       value |= SCLK_DPM_EN(1) | SCLK_DPM_BOOT_STATE(0) | VOLTAGE_CHG_EN(1);
+       WREG32_SMC(SMU_SCLK_DPM_CNTL, value);
+
+       WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN);
+       WREG32_P(CG_CG_VOLTAGE_CNTL, 0, ~EN);
+
+       trinity_dpm_config(rdev, true);
+}
+
+static void trinity_wait_for_dpm_enabled(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(SCLK_PWRMGT_CNTL) & DYNAMIC_PM_EN)
+                       break;
+               udelay(1);
+       }
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & TARGET_STATE_MASK) == 0)
+                       break;
+               udelay(1);
+       }
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_MASK) == 0)
+                       break;
+               udelay(1);
+       }
+}
+
+static void trinity_stop_dpm(struct radeon_device *rdev)
+{
+       u32 sclk_dpm_cntl;
+
+       WREG32_P(CG_CG_VOLTAGE_CNTL, EN, ~EN);
+
+       sclk_dpm_cntl = RREG32_SMC(SMU_SCLK_DPM_CNTL);
+       sclk_dpm_cntl &= ~(SCLK_DPM_EN_MASK | VOLTAGE_CHG_EN_MASK);
+       WREG32_SMC(SMU_SCLK_DPM_CNTL, sclk_dpm_cntl);
+
+       trinity_dpm_config(rdev, false);
+}
+
+static void trinity_start_am(struct radeon_device *rdev)
+{
+       WREG32_P(SCLK_PWRMGT_CNTL, 0, ~(RESET_SCLK_CNT | RESET_BUSY_CNT));
+}
+
+static void trinity_reset_am(struct radeon_device *rdev)
+{
+       WREG32_P(SCLK_PWRMGT_CNTL, RESET_SCLK_CNT | RESET_BUSY_CNT,
+                ~(RESET_SCLK_CNT | RESET_BUSY_CNT));
+}
+
+static void trinity_wait_for_level_0(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_MASK) == 0)
+                       break;
+               udelay(1);
+       }
+}
+
+static void trinity_enable_power_level_0(struct radeon_device *rdev)
+{
+       trinity_power_level_enable_disable(rdev, 0, true);
+}
+
+static void trinity_force_level_0(struct radeon_device *rdev)
+{
+       trinity_dpm_force_state(rdev, 0);
+}
+
+static void trinity_unforce_levels(struct radeon_device *rdev)
+{
+       trinity_dpm_no_forced_level(rdev);
+}
+
+static void trinity_program_power_levels_0_to_n(struct radeon_device *rdev,
+                                               struct radeon_ps *new_rps,
+                                               struct radeon_ps *old_rps)
+{
+       struct trinity_ps *new_ps = trinity_get_ps(new_rps);
+       struct trinity_ps *old_ps = trinity_get_ps(old_rps);
+       u32 i;
+       u32 n_current_state_levels = (old_ps == NULL) ? 1 : old_ps->num_levels;
+
+       for (i = 0; i < new_ps->num_levels; i++) {
+               trinity_program_power_level(rdev, &new_ps->levels[i], i);
+               trinity_power_level_enable_disable(rdev, i, true);
+       }
+
+       for (i = new_ps->num_levels; i < n_current_state_levels; i++)
+               trinity_power_level_enable_disable(rdev, i, false);
+}
+
+static void trinity_program_bootup_state(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 i;
+
+       trinity_program_power_level(rdev, &pi->boot_pl, 0);
+       trinity_power_level_enable_disable(rdev, 0, true);
+
+       for (i = 1; i < 8; i++)
+               trinity_power_level_enable_disable(rdev, i, false);
+}
+
+static void trinity_setup_uvd_clock_table(struct radeon_device *rdev,
+                                         struct radeon_ps *rps)
+{
+       struct trinity_ps *ps = trinity_get_ps(rps);
+       u32 uvdstates = (ps->vclk_low_divider |
+                        ps->vclk_high_divider << 8 |
+                        ps->dclk_low_divider << 16 |
+                        ps->dclk_high_divider << 24);
+
+       WREG32_SMC(SMU_UVD_DPM_STATES, uvdstates);
+}
+
+static void trinity_setup_uvd_dpm_interval(struct radeon_device *rdev,
+                                          u32 interval)
+{
+       u32 p, u;
+       u32 tp = RREG32_SMC(PM_TP);
+       u32 val;
+       u32 xclk = radeon_get_xclk(rdev);
+
+       r600_calculate_u_and_p(interval, xclk, 16, &p, &u);
+
+       val = (p + tp - 1) / tp;
+
+       WREG32_SMC(SMU_UVD_DPM_CNTL, val);
+}
+
+static bool trinity_uvd_clocks_zero(struct radeon_ps *rps)
+{
+       if ((rps->vclk == 0) && (rps->dclk == 0))
+               return true;
+       else
+               return false;
+}
+
+static bool trinity_uvd_clocks_equal(struct radeon_ps *rps1,
+                                    struct radeon_ps *rps2)
+{
+       struct trinity_ps *ps1 = trinity_get_ps(rps1);
+       struct trinity_ps *ps2 = trinity_get_ps(rps2);
+
+       if ((rps1->vclk == rps2->vclk) &&
+           (rps1->dclk == rps2->dclk) &&
+           (ps1->vclk_low_divider == ps2->vclk_low_divider) &&
+           (ps1->vclk_high_divider == ps2->vclk_high_divider) &&
+           (ps1->dclk_low_divider == ps2->dclk_low_divider) &&
+           (ps1->dclk_high_divider == ps2->dclk_high_divider))
+               return true;
+       else
+               return false;
+}
+
+static void trinity_setup_uvd_clocks(struct radeon_device *rdev,
+                                    struct radeon_ps *new_rps,
+                                    struct radeon_ps *old_rps)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       if (pi->enable_gfx_power_gating) {
+               trinity_gfx_powergating_enable(rdev, false);
+       }
+
+       if (pi->uvd_dpm) {
+               if (trinity_uvd_clocks_zero(new_rps) &&
+                   !trinity_uvd_clocks_zero(old_rps)) {
+                       trinity_setup_uvd_dpm_interval(rdev, 0);
+               } else if (!trinity_uvd_clocks_zero(new_rps)) {
+                       trinity_setup_uvd_clock_table(rdev, new_rps);
+
+                       if (trinity_uvd_clocks_zero(old_rps)) {
+                               u32 tmp = RREG32(CG_MISC_REG);
+                               tmp &= 0xfffffffd;
+                               WREG32(CG_MISC_REG, tmp);
+
+                               radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk);
+
+                               trinity_setup_uvd_dpm_interval(rdev, 3000);
+                       }
+               }
+               trinity_uvd_dpm_config(rdev);
+       } else {
+               if (trinity_uvd_clocks_zero(new_rps) ||
+                   trinity_uvd_clocks_equal(new_rps, old_rps))
+                       return;
+
+               radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk);
+       }
+
+       if (pi->enable_gfx_power_gating) {
+               trinity_gfx_powergating_enable(rdev, true);
+       }
+}
+
+static void trinity_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev,
+                                                      struct radeon_ps *new_rps,
+                                                      struct radeon_ps *old_rps)
+{
+       struct trinity_ps *new_ps = trinity_get_ps(new_rps);
+       struct trinity_ps *current_ps = trinity_get_ps(new_rps);
+
+       if (new_ps->levels[new_ps->num_levels - 1].sclk >=
+           current_ps->levels[current_ps->num_levels - 1].sclk)
+               return;
+
+       trinity_setup_uvd_clocks(rdev, new_rps, old_rps);
+}
+
+static void trinity_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev,
+                                                     struct radeon_ps *new_rps,
+                                                     struct radeon_ps *old_rps)
+{
+       struct trinity_ps *new_ps = trinity_get_ps(new_rps);
+       struct trinity_ps *current_ps = trinity_get_ps(old_rps);
+
+       if (new_ps->levels[new_ps->num_levels - 1].sclk <
+           current_ps->levels[current_ps->num_levels - 1].sclk)
+               return;
+
+       trinity_setup_uvd_clocks(rdev, new_rps, old_rps);
+}
+
+static void trinity_program_ttt(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 value = RREG32_SMC(SMU_SCLK_DPM_TTT);
+
+       value &= ~(HT_MASK | LT_MASK);
+       value |= HT((pi->thermal_auto_throttling + 49) * 8);
+       value |= LT((pi->thermal_auto_throttling + 49 - pi->sys_info.htc_hyst_lmt) * 8);
+       WREG32_SMC(SMU_SCLK_DPM_TTT, value);
+}
+
+static void trinity_enable_att(struct radeon_device *rdev)
+{
+       u32 value = RREG32_SMC(SMU_SCLK_DPM_TT_CNTL);
+
+       value &= ~SCLK_TT_EN_MASK;
+       value |= SCLK_TT_EN(1);
+       WREG32_SMC(SMU_SCLK_DPM_TT_CNTL, value);
+}
+
+static void trinity_program_sclk_dpm(struct radeon_device *rdev)
+{
+       u32 p, u;
+       u32 tp = RREG32_SMC(PM_TP);
+       u32 ni;
+       u32 xclk = radeon_get_xclk(rdev);
+       u32 value;
+
+       r600_calculate_u_and_p(400, xclk, 16, &p, &u);
+
+       ni = (p + tp - 1) / tp;
+
+       value = RREG32_SMC(PM_I_CNTL_1);
+       value &= ~SCLK_DPM_MASK;
+       value |= SCLK_DPM(ni);
+       WREG32_SMC(PM_I_CNTL_1, value);
+}
+
+static int trinity_set_thermal_temperature_range(struct radeon_device *rdev,
+                                                int min_temp, int max_temp)
+{
+       int low_temp = 0 * 1000;
+       int high_temp = 255 * 1000;
+
+        if (low_temp < min_temp)
+               low_temp = min_temp;
+        if (high_temp > max_temp)
+               high_temp = max_temp;
+        if (high_temp < low_temp) {
+               DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp);
+                return -EINVAL;
+        }
+
+       WREG32_P(CG_THERMAL_INT_CTRL, DIG_THERM_INTH(49 + (high_temp / 1000)), ~DIG_THERM_INTH_MASK);
+       WREG32_P(CG_THERMAL_INT_CTRL, DIG_THERM_INTL(49 + (low_temp / 1000)), ~DIG_THERM_INTL_MASK);
+
+       rdev->pm.dpm.thermal.min_temp = low_temp;
+       rdev->pm.dpm.thermal.max_temp = high_temp;
+
+       return 0;
+}
+
+static void trinity_update_current_ps(struct radeon_device *rdev,
+                                     struct radeon_ps *rps)
+{
+       struct trinity_ps *new_ps = trinity_get_ps(rps);
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       pi->current_rps = *rps;
+       pi->current_ps = *new_ps;
+       pi->current_rps.ps_priv = &pi->current_ps;
+}
+
+static void trinity_update_requested_ps(struct radeon_device *rdev,
+                                       struct radeon_ps *rps)
+{
+       struct trinity_ps *new_ps = trinity_get_ps(rps);
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       pi->requested_rps = *rps;
+       pi->requested_ps = *new_ps;
+       pi->requested_rps.ps_priv = &pi->requested_ps;
+}
+
+int trinity_dpm_enable(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       int ret;
+
+       trinity_acquire_mutex(rdev);
+
+       if (trinity_dpm_enabled(rdev)) {
+               trinity_release_mutex(rdev);
+               return -EINVAL;
+       }
+
+       trinity_enable_clock_power_gating(rdev);
+       trinity_program_bootup_state(rdev);
+       sumo_program_vc(rdev, 0x00C00033);
+       trinity_start_am(rdev);
+       if (pi->enable_auto_thermal_throttling) {
+               trinity_program_ttt(rdev);
+               trinity_enable_att(rdev);
+       }
+       trinity_program_sclk_dpm(rdev);
+       trinity_start_dpm(rdev);
+       trinity_wait_for_dpm_enabled(rdev);
+       trinity_release_mutex(rdev);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               ret = trinity_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+               if (ret) {
+                       trinity_release_mutex(rdev);
+                       return ret;
+               }
+               rdev->irq.dpm_thermal = true;
+               radeon_irq_set(rdev);
+       }
+
+       trinity_update_current_ps(rdev, rdev->pm.dpm.boot_ps);
+
+       return 0;
+}
+
+void trinity_dpm_disable(struct radeon_device *rdev)
+{
+       trinity_acquire_mutex(rdev);
+       if (!trinity_dpm_enabled(rdev)) {
+               trinity_release_mutex(rdev);
+               return;
+       }
+       trinity_disable_clock_power_gating(rdev);
+       sumo_clear_vc(rdev);
+       trinity_wait_for_level_0(rdev);
+       trinity_stop_dpm(rdev);
+       trinity_reset_am(rdev);
+       trinity_release_mutex(rdev);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               rdev->irq.dpm_thermal = false;
+               radeon_irq_set(rdev);
+       }
+
+       trinity_update_current_ps(rdev, rdev->pm.dpm.boot_ps);
+}
+
+static void trinity_get_min_sclk_divider(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       pi->min_sclk_did =
+               (RREG32_SMC(CC_SMU_MISC_FUSES) & MinSClkDid_MASK) >> MinSClkDid_SHIFT;
+}
+
+static void trinity_setup_nbp_sim(struct radeon_device *rdev,
+                                 struct radeon_ps *rps)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       struct trinity_ps *new_ps = trinity_get_ps(rps);
+       u32 nbpsconfig;
+
+       if (pi->sys_info.nb_dpm_enable) {
+               nbpsconfig = RREG32_SMC(NB_PSTATE_CONFIG);
+               nbpsconfig &= ~(Dpm0PgNbPsLo_MASK | Dpm0PgNbPsHi_MASK | DpmXNbPsLo_MASK | DpmXNbPsHi_MASK);
+               nbpsconfig |= (Dpm0PgNbPsLo(new_ps->Dpm0PgNbPsLo) |
+                              Dpm0PgNbPsHi(new_ps->Dpm0PgNbPsHi) |
+                              DpmXNbPsLo(new_ps->DpmXNbPsLo) |
+                              DpmXNbPsHi(new_ps->DpmXNbPsHi));
+               WREG32_SMC(NB_PSTATE_CONFIG, nbpsconfig);
+       }
+}
+
+int trinity_dpm_force_performance_level(struct radeon_device *rdev,
+                                       enum radeon_dpm_forced_level level)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       struct radeon_ps *rps = &pi->current_rps;
+       struct trinity_ps *ps = trinity_get_ps(rps);
+       int i, ret;
+
+       if (ps->num_levels <= 1)
+               return 0;
+
+       if (level == RADEON_DPM_FORCED_LEVEL_HIGH) {
+               /* not supported by the hw */
+               return -EINVAL;
+       } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) {
+               ret = trinity_dpm_n_levels_disabled(rdev, ps->num_levels - 1);
+               if (ret)
+                       return ret;
+       } else {
+               for (i = 0; i < ps->num_levels; i++) {
+                       ret = trinity_dpm_n_levels_disabled(rdev, 0);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       rdev->pm.dpm.forced_level = level;
+
+       return 0;
+}
+
+int trinity_dpm_pre_set_power_state(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       struct radeon_ps requested_ps = *rdev->pm.dpm.requested_ps;
+       struct radeon_ps *new_ps = &requested_ps;
+
+       trinity_update_requested_ps(rdev, new_ps);
+
+       trinity_apply_state_adjust_rules(rdev,
+                                        &pi->requested_rps,
+                                        &pi->current_rps);
+
+       return 0;
+}
+
+int trinity_dpm_set_power_state(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       struct radeon_ps *new_ps = &pi->requested_rps;
+       struct radeon_ps *old_ps = &pi->current_rps;
+
+       trinity_acquire_mutex(rdev);
+       if (pi->enable_dpm) {
+               trinity_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps);
+               trinity_enable_power_level_0(rdev);
+               trinity_force_level_0(rdev);
+               trinity_wait_for_level_0(rdev);
+               trinity_setup_nbp_sim(rdev, new_ps);
+               trinity_program_power_levels_0_to_n(rdev, new_ps, old_ps);
+               trinity_force_level_0(rdev);
+               trinity_unforce_levels(rdev);
+               trinity_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+               rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO;
+       }
+       trinity_release_mutex(rdev);
+
+       return 0;
+}
+
+void trinity_dpm_post_set_power_state(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       struct radeon_ps *new_ps = &pi->requested_rps;
+
+       trinity_update_current_ps(rdev, new_ps);
+}
+
+void trinity_dpm_setup_asic(struct radeon_device *rdev)
+{
+       trinity_acquire_mutex(rdev);
+       sumo_program_sstp(rdev);
+       sumo_take_smu_control(rdev, true);
+       trinity_get_min_sclk_divider(rdev);
+       trinity_release_mutex(rdev);
+}
+
+void trinity_dpm_reset_asic(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       trinity_acquire_mutex(rdev);
+       if (pi->enable_dpm) {
+               trinity_enable_power_level_0(rdev);
+               trinity_force_level_0(rdev);
+               trinity_wait_for_level_0(rdev);
+               trinity_program_bootup_state(rdev);
+               trinity_force_level_0(rdev);
+               trinity_unforce_levels(rdev);
+       }
+       trinity_release_mutex(rdev);
+}
+
+static u16 trinity_convert_voltage_index_to_value(struct radeon_device *rdev,
+                                                 u32 vid_2bit)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 vid_7bit = sumo_convert_vid2_to_vid7(rdev, &pi->sys_info.vid_mapping_table, vid_2bit);
+       u32 svi_mode = (RREG32_SMC(PM_CONFIG) & SVI_Mode) ? 1 : 0;
+       u32 step = (svi_mode == 0) ? 1250 : 625;
+       u32 delta = vid_7bit * step + 50;
+
+       if (delta > 155000)
+               return 0;
+
+       return (155000 - delta) / 100;
+}
+
+static void trinity_patch_boot_state(struct radeon_device *rdev,
+                                    struct trinity_ps *ps)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       ps->num_levels = 1;
+       ps->nbps_flags = 0;
+       ps->bapm_flags = 0;
+       ps->levels[0] = pi->boot_pl;
+}
+
+static u8 trinity_calculate_vce_wm(struct radeon_device *rdev, u32 sclk)
+{
+       if (sclk < 20000)
+               return 1;
+       return 0;
+}
+
+static void trinity_construct_boot_state(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       pi->boot_pl.sclk = pi->sys_info.bootup_sclk;
+       pi->boot_pl.vddc_index = pi->sys_info.bootup_nb_voltage_index;
+       pi->boot_pl.ds_divider_index = 0;
+       pi->boot_pl.ss_divider_index = 0;
+       pi->boot_pl.allow_gnb_slow = 1;
+       pi->boot_pl.force_nbp_state = 0;
+       pi->boot_pl.display_wm = 0;
+       pi->boot_pl.vce_wm = 0;
+       pi->current_ps.num_levels = 1;
+       pi->current_ps.levels[0] = pi->boot_pl;
+}
+
+static u8 trinity_get_sleep_divider_id_from_clock(struct radeon_device *rdev,
+                                                 u32 sclk, u32 min_sclk_in_sr)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 i;
+       u32 temp;
+       u32 min = (min_sclk_in_sr > TRINITY_MINIMUM_ENGINE_CLOCK) ?
+               min_sclk_in_sr : TRINITY_MINIMUM_ENGINE_CLOCK;
+
+       if (sclk < min)
+               return 0;
+
+       if (!pi->enable_sclk_ds)
+               return 0;
+
+       for (i = TRINITY_MAX_DEEPSLEEP_DIVIDER_ID;  ; i--) {
+               temp = sclk / sumo_get_sleep_divider_from_id(i);
+               if (temp >= min || i == 0)
+                       break;
+       }
+
+       return (u8)i;
+}
+
+static u32 trinity_get_valid_engine_clock(struct radeon_device *rdev,
+                                         u32 lower_limit)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 i;
+
+       for (i = 0; i < pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries; i++) {
+               if (pi->sys_info.sclk_voltage_mapping_table.entries[i].sclk_frequency >= lower_limit)
+                       return pi->sys_info.sclk_voltage_mapping_table.entries[i].sclk_frequency;
+       }
+
+       if (i == pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries)
+               DRM_ERROR("engine clock out of range!");
+
+       return 0;
+}
+
+static void trinity_patch_thermal_state(struct radeon_device *rdev,
+                                       struct trinity_ps *ps,
+                                       struct trinity_ps *current_ps)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */
+       u32 current_vddc;
+       u32 current_sclk;
+       u32 current_index = 0;
+
+       if (current_ps) {
+               current_vddc = current_ps->levels[current_index].vddc_index;
+               current_sclk = current_ps->levels[current_index].sclk;
+       } else {
+               current_vddc = pi->boot_pl.vddc_index;
+               current_sclk = pi->boot_pl.sclk;
+       }
+
+       ps->levels[0].vddc_index = current_vddc;
+
+       if (ps->levels[0].sclk > current_sclk)
+               ps->levels[0].sclk = current_sclk;
+
+       ps->levels[0].ds_divider_index =
+               trinity_get_sleep_divider_id_from_clock(rdev, ps->levels[0].sclk, sclk_in_sr);
+       ps->levels[0].ss_divider_index = ps->levels[0].ds_divider_index;
+       ps->levels[0].allow_gnb_slow = 1;
+       ps->levels[0].force_nbp_state = 0;
+       ps->levels[0].display_wm = 0;
+       ps->levels[0].vce_wm =
+               trinity_calculate_vce_wm(rdev, ps->levels[0].sclk);
+}
+
+static u8 trinity_calculate_display_wm(struct radeon_device *rdev,
+                                      struct trinity_ps *ps, u32 index)
+{
+       if (ps == NULL || ps->num_levels <= 1)
+               return 0;
+       else if (ps->num_levels == 2) {
+               if (index == 0)
+                       return 0;
+               else
+                       return 1;
+       } else {
+               if (index == 0)
+                       return 0;
+               else if (ps->levels[index].sclk < 30000)
+                       return 0;
+               else
+                       return 1;
+       }
+}
+
+static u32 trinity_get_uvd_clock_index(struct radeon_device *rdev,
+                                      struct radeon_ps *rps)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 i = 0;
+
+       for (i = 0; i < 4; i++) {
+               if ((rps->vclk == pi->sys_info.uvd_clock_table_entries[i].vclk) &&
+                   (rps->dclk == pi->sys_info.uvd_clock_table_entries[i].dclk))
+                   break;
+       }
+
+       if (i >= 4) {
+               DRM_ERROR("UVD clock index not found!\n");
+               i = 3;
+       }
+       return i;
+}
+
+static void trinity_adjust_uvd_state(struct radeon_device *rdev,
+                                    struct radeon_ps *rps)
+{
+       struct trinity_ps *ps = trinity_get_ps(rps);
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 high_index = 0;
+       u32 low_index = 0;
+
+       if (pi->uvd_dpm && r600_is_uvd_state(rps->class, rps->class2)) {
+               high_index = trinity_get_uvd_clock_index(rdev, rps);
+
+               switch(high_index) {
+               case 3:
+               case 2:
+                       low_index = 1;
+                       break;
+               case 1:
+               case 0:
+               default:
+                       low_index = 0;
+                       break;
+               }
+
+               ps->vclk_low_divider =
+                       pi->sys_info.uvd_clock_table_entries[high_index].vclk_did;
+               ps->dclk_low_divider =
+                       pi->sys_info.uvd_clock_table_entries[high_index].dclk_did;
+               ps->vclk_high_divider =
+                       pi->sys_info.uvd_clock_table_entries[low_index].vclk_did;
+               ps->dclk_high_divider =
+                       pi->sys_info.uvd_clock_table_entries[low_index].dclk_did;
+       }
+}
+
+
+
+static void trinity_apply_state_adjust_rules(struct radeon_device *rdev,
+                                            struct radeon_ps *new_rps,
+                                            struct radeon_ps *old_rps)
+{
+       struct trinity_ps *ps = trinity_get_ps(new_rps);
+       struct trinity_ps *current_ps = trinity_get_ps(old_rps);
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 min_voltage = 0; /* ??? */
+       u32 min_sclk = pi->sys_info.min_sclk; /* XXX check against disp reqs */
+       u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */
+       u32 i;
+       bool force_high;
+       u32 num_active_displays = rdev->pm.dpm.new_active_crtc_count;
+
+       if (new_rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
+               return trinity_patch_thermal_state(rdev, ps, current_ps);
+
+       trinity_adjust_uvd_state(rdev, new_rps);
+
+       for (i = 0; i < ps->num_levels; i++) {
+               if (ps->levels[i].vddc_index < min_voltage)
+                       ps->levels[i].vddc_index = min_voltage;
+
+               if (ps->levels[i].sclk < min_sclk)
+                       ps->levels[i].sclk =
+                               trinity_get_valid_engine_clock(rdev, min_sclk);
+
+               ps->levels[i].ds_divider_index =
+                       sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[i].sclk, sclk_in_sr);
+
+               ps->levels[i].ss_divider_index = ps->levels[i].ds_divider_index;
+
+               ps->levels[i].allow_gnb_slow = 1;
+               ps->levels[i].force_nbp_state = 0;
+               ps->levels[i].display_wm =
+                       trinity_calculate_display_wm(rdev, ps, i);
+               ps->levels[i].vce_wm =
+                       trinity_calculate_vce_wm(rdev, ps->levels[0].sclk);
+       }
+
+       if ((new_rps->class & (ATOM_PPLIB_CLASSIFICATION_HDSTATE | ATOM_PPLIB_CLASSIFICATION_SDSTATE)) ||
+           ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY))
+               ps->bapm_flags |= TRINITY_POWERSTATE_FLAGS_BAPM_DISABLE;
+
+       if (pi->sys_info.nb_dpm_enable) {
+               ps->Dpm0PgNbPsLo = 0x1;
+               ps->Dpm0PgNbPsHi = 0x0;
+               ps->DpmXNbPsLo = 0x2;
+               ps->DpmXNbPsHi = 0x1;
+
+               if ((new_rps->class & (ATOM_PPLIB_CLASSIFICATION_HDSTATE | ATOM_PPLIB_CLASSIFICATION_SDSTATE)) ||
+                   ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)) {
+                       force_high = ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) ||
+                                     ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) &&
+                                      (pi->sys_info.uma_channel_number == 1)));
+                       force_high = (num_active_displays >= 3) || force_high;
+                       ps->Dpm0PgNbPsLo = force_high ? 0x2 : 0x3;
+                       ps->Dpm0PgNbPsHi = 0x1;
+                       ps->DpmXNbPsLo = force_high ? 0x2 : 0x3;
+                       ps->DpmXNbPsHi = 0x2;
+                       ps->levels[ps->num_levels - 1].allow_gnb_slow = 0;
+               }
+       }
+}
+
+static void trinity_cleanup_asic(struct radeon_device *rdev)
+{
+       sumo_take_smu_control(rdev, false);
+}
+
+#if 0
+static void trinity_pre_display_configuration_change(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       if (pi->voltage_drop_in_dce)
+               trinity_dce_enable_voltage_adjustment(rdev, false);
+}
+#endif
+
+static void trinity_add_dccac_value(struct radeon_device *rdev)
+{
+       u32 gpu_cac_avrg_cntl_window_size;
+       u32 num_active_displays = rdev->pm.dpm.new_active_crtc_count;
+       u64 disp_clk = rdev->clock.default_dispclk / 100;
+       u32 dc_cac_value;
+
+       gpu_cac_avrg_cntl_window_size =
+               (RREG32_SMC(GPU_CAC_AVRG_CNTL) & WINDOW_SIZE_MASK) >> WINDOW_SIZE_SHIFT;
+
+       dc_cac_value = (u32)((14213 * disp_clk * disp_clk * (u64)num_active_displays) >>
+                            (32 - gpu_cac_avrg_cntl_window_size));
+
+       WREG32_SMC(DC_CAC_VALUE, dc_cac_value);
+}
+
+void trinity_dpm_display_configuration_changed(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       if (pi->voltage_drop_in_dce)
+               trinity_dce_enable_voltage_adjustment(rdev, true);
+       trinity_add_dccac_value(rdev);
+}
+
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+       struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+       struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+
+union pplib_clock_info {
+       struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
+       struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
+       struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
+       struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+
+union pplib_power_state {
+       struct _ATOM_PPLIB_STATE v1;
+       struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static void trinity_parse_pplib_non_clock_info(struct radeon_device *rdev,
+                                              struct radeon_ps *rps,
+                                              struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
+                                              u8 table_rev)
+{
+       struct trinity_ps *ps = trinity_get_ps(rps);
+
+       rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+       rps->class = le16_to_cpu(non_clock_info->usClassification);
+       rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
+
+       if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
+               rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
+               rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
+       } else {
+               rps->vclk = 0;
+               rps->dclk = 0;
+       }
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+               rdev->pm.dpm.boot_ps = rps;
+               trinity_patch_boot_state(rdev, ps);
+       }
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+               rdev->pm.dpm.uvd_ps = rps;
+}
+
+static void trinity_parse_pplib_clock_info(struct radeon_device *rdev,
+                                          struct radeon_ps *rps, int index,
+                                          union pplib_clock_info *clock_info)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       struct trinity_ps *ps = trinity_get_ps(rps);
+       struct trinity_pl *pl = &ps->levels[index];
+       u32 sclk;
+
+       sclk = le16_to_cpu(clock_info->sumo.usEngineClockLow);
+       sclk |= clock_info->sumo.ucEngineClockHigh << 16;
+       pl->sclk = sclk;
+       pl->vddc_index = clock_info->sumo.vddcIndex;
+
+       ps->num_levels = index + 1;
+
+       if (pi->enable_sclk_ds) {
+               pl->ds_divider_index = 5;
+               pl->ss_divider_index = 5;
+       }
+}
+
+static int trinity_parse_power_table(struct radeon_device *rdev)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+       union pplib_power_state *power_state;
+       int i, j, k, non_clock_array_index, clock_array_index;
+       union pplib_clock_info *clock_info;
+       struct _StateArray *state_array;
+       struct _ClockInfoArray *clock_info_array;
+       struct _NonClockInfoArray *non_clock_info_array;
+       union power_info *power_info;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+        u16 data_offset;
+       u8 frev, crev;
+       u8 *power_state_offset;
+       struct sumo_ps *ps;
+
+       if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset))
+               return -EINVAL;
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       state_array = (struct _StateArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usStateArrayOffset));
+       clock_info_array = (struct _ClockInfoArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usClockInfoArrayOffset));
+       non_clock_info_array = (struct _NonClockInfoArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset));
+
+       rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
+                                 state_array->ucNumEntries, GFP_KERNEL);
+       if (!rdev->pm.dpm.ps)
+               return -ENOMEM;
+       power_state_offset = (u8 *)state_array->states;
+       rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
+       rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
+       rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
+       for (i = 0; i < state_array->ucNumEntries; i++) {
+               power_state = (union pplib_power_state *)power_state_offset;
+               non_clock_array_index = power_state->v2.nonClockInfoIndex;
+               non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+                       &non_clock_info_array->nonClockInfo[non_clock_array_index];
+               if (!rdev->pm.power_state[i].clock_info)
+                       return -EINVAL;
+               ps = kzalloc(sizeof(struct sumo_ps), GFP_KERNEL);
+               if (ps == NULL) {
+                       kfree(rdev->pm.dpm.ps);
+                       return -ENOMEM;
+               }
+               rdev->pm.dpm.ps[i].ps_priv = ps;
+               k = 0;
+               for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
+                       clock_array_index = power_state->v2.clockInfoIndex[j];
+                       if (clock_array_index >= clock_info_array->ucNumEntries)
+                               continue;
+                       if (k >= SUMO_MAX_HARDWARE_POWERLEVELS)
+                               break;
+                       clock_info = (union pplib_clock_info *)
+                               &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
+                       trinity_parse_pplib_clock_info(rdev,
+                                                      &rdev->pm.dpm.ps[i], k,
+                                                      clock_info);
+                       k++;
+               }
+               trinity_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
+                                                  non_clock_info,
+                                                  non_clock_info_array->ucEntrySize);
+               power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
+       }
+       rdev->pm.dpm.num_ps = state_array->ucNumEntries;
+       return 0;
+}
+
+union igp_info {
+       struct _ATOM_INTEGRATED_SYSTEM_INFO info;
+       struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
+       struct _ATOM_INTEGRATED_SYSTEM_INFO_V5 info_5;
+       struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6;
+       struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7;
+};
+
+static u32 trinity_convert_did_to_freq(struct radeon_device *rdev, u8 did)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       u32 divider;
+
+       if (did >= 8 && did <= 0x3f)
+               divider = did * 25;
+       else if (did > 0x3f && did <= 0x5f)
+               divider = (did - 64) * 50 + 1600;
+       else if (did > 0x5f && did <= 0x7e)
+               divider = (did - 96) * 100 + 3200;
+       else if (did == 0x7f)
+               divider = 128 * 100;
+       else
+               return 10000;
+
+       return ((pi->sys_info.dentist_vco_freq * 100) + (divider - 1)) / divider;
+}
+
+static int trinity_parse_sys_info_table(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
+       union igp_info *igp_info;
+       u8 frev, crev;
+       u16 data_offset;
+       int i;
+
+       if (atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset)) {
+               igp_info = (union igp_info *)(mode_info->atom_context->bios +
+                                             data_offset);
+
+               if (crev != 7) {
+                       DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev);
+                       return -EINVAL;
+               }
+               pi->sys_info.bootup_sclk = le32_to_cpu(igp_info->info_7.ulBootUpEngineClock);
+               pi->sys_info.min_sclk = le32_to_cpu(igp_info->info_7.ulMinEngineClock);
+               pi->sys_info.bootup_uma_clk = le32_to_cpu(igp_info->info_7.ulBootUpUMAClock);
+               pi->sys_info.dentist_vco_freq = le32_to_cpu(igp_info->info_7.ulDentistVCOFreq);
+               pi->sys_info.bootup_nb_voltage_index =
+                       le16_to_cpu(igp_info->info_7.usBootUpNBVoltage);
+               if (igp_info->info_7.ucHtcTmpLmt == 0)
+                       pi->sys_info.htc_tmp_lmt = 203;
+               else
+                       pi->sys_info.htc_tmp_lmt = igp_info->info_7.ucHtcTmpLmt;
+               if (igp_info->info_7.ucHtcHystLmt == 0)
+                       pi->sys_info.htc_hyst_lmt = 5;
+               else
+                       pi->sys_info.htc_hyst_lmt = igp_info->info_7.ucHtcHystLmt;
+               if (pi->sys_info.htc_tmp_lmt <= pi->sys_info.htc_hyst_lmt) {
+                       DRM_ERROR("The htcTmpLmt should be larger than htcHystLmt.\n");
+               }
+
+               if (pi->enable_nbps_policy)
+                       pi->sys_info.nb_dpm_enable = igp_info->info_7.ucNBDPMEnable;
+               else
+                       pi->sys_info.nb_dpm_enable = 0;
+
+               for (i = 0; i < TRINITY_NUM_NBPSTATES; i++) {
+                       pi->sys_info.nbp_mclk[i] = le32_to_cpu(igp_info->info_7.ulNbpStateMemclkFreq[i]);
+                       pi->sys_info.nbp_nclk[i] = le32_to_cpu(igp_info->info_7.ulNbpStateNClkFreq[i]);
+               }
+
+               pi->sys_info.nbp_voltage_index[0] = le16_to_cpu(igp_info->info_7.usNBP0Voltage);
+               pi->sys_info.nbp_voltage_index[1] = le16_to_cpu(igp_info->info_7.usNBP1Voltage);
+               pi->sys_info.nbp_voltage_index[2] = le16_to_cpu(igp_info->info_7.usNBP2Voltage);
+               pi->sys_info.nbp_voltage_index[3] = le16_to_cpu(igp_info->info_7.usNBP3Voltage);
+
+               if (!pi->sys_info.nb_dpm_enable) {
+                       for (i = 1; i < TRINITY_NUM_NBPSTATES; i++) {
+                               pi->sys_info.nbp_mclk[i] = pi->sys_info.nbp_mclk[0];
+                               pi->sys_info.nbp_nclk[i] = pi->sys_info.nbp_nclk[0];
+                               pi->sys_info.nbp_voltage_index[i] = pi->sys_info.nbp_voltage_index[0];
+                       }
+               }
+
+               pi->sys_info.uma_channel_number = igp_info->info_7.ucUMAChannelNumber;
+
+               sumo_construct_sclk_voltage_mapping_table(rdev,
+                                                         &pi->sys_info.sclk_voltage_mapping_table,
+                                                         igp_info->info_7.sAvail_SCLK);
+               sumo_construct_vid_mapping_table(rdev, &pi->sys_info.vid_mapping_table,
+                                                igp_info->info_7.sAvail_SCLK);
+
+               pi->sys_info.uvd_clock_table_entries[0].vclk_did =
+                       igp_info->info_7.ucDPMState0VclkFid;
+               pi->sys_info.uvd_clock_table_entries[1].vclk_did =
+                       igp_info->info_7.ucDPMState1VclkFid;
+               pi->sys_info.uvd_clock_table_entries[2].vclk_did =
+                       igp_info->info_7.ucDPMState2VclkFid;
+               pi->sys_info.uvd_clock_table_entries[3].vclk_did =
+                       igp_info->info_7.ucDPMState3VclkFid;
+
+               pi->sys_info.uvd_clock_table_entries[0].dclk_did =
+                       igp_info->info_7.ucDPMState0DclkFid;
+               pi->sys_info.uvd_clock_table_entries[1].dclk_did =
+                       igp_info->info_7.ucDPMState1DclkFid;
+               pi->sys_info.uvd_clock_table_entries[2].dclk_did =
+                       igp_info->info_7.ucDPMState2DclkFid;
+               pi->sys_info.uvd_clock_table_entries[3].dclk_did =
+                       igp_info->info_7.ucDPMState3DclkFid;
+
+               for (i = 0; i < 4; i++) {
+                       pi->sys_info.uvd_clock_table_entries[i].vclk =
+                               trinity_convert_did_to_freq(rdev,
+                                                           pi->sys_info.uvd_clock_table_entries[i].vclk_did);
+                       pi->sys_info.uvd_clock_table_entries[i].dclk =
+                               trinity_convert_did_to_freq(rdev,
+                                                           pi->sys_info.uvd_clock_table_entries[i].dclk_did);
+               }
+
+
+
+       }
+       return 0;
+}
+
+int trinity_dpm_init(struct radeon_device *rdev)
+{
+       struct trinity_power_info *pi;
+       int ret, i;
+
+       pi = kzalloc(sizeof(struct trinity_power_info), GFP_KERNEL);
+       if (pi == NULL)
+               return -ENOMEM;
+       rdev->pm.dpm.priv = pi;
+
+       for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++)
+               pi->at[i] = TRINITY_AT_DFLT;
+
+       pi->enable_nbps_policy = true;
+       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_auto_thermal_throttling = true;
+       pi->voltage_drop_in_dce = false; /* need to restructure dpm/modeset interaction */
+       pi->uvd_dpm = true; /* ??? */
+
+       ret = trinity_parse_sys_info_table(rdev);
+       if (ret)
+               return ret;
+
+       trinity_construct_boot_state(rdev);
+
+       ret = trinity_parse_power_table(rdev);
+       if (ret)
+               return ret;
+
+       pi->thermal_auto_throttling = pi->sys_info.htc_tmp_lmt;
+       pi->enable_dpm = true;
+
+       return 0;
+}
+
+void trinity_dpm_print_power_state(struct radeon_device *rdev,
+                                  struct radeon_ps *rps)
+{
+       int i;
+       struct trinity_ps *ps = trinity_get_ps(rps);
+
+       r600_dpm_print_class_info(rps->class, rps->class2);
+       r600_dpm_print_cap_info(rps->caps);
+       printk("\tuvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+       for (i = 0; i < ps->num_levels; i++) {
+               struct trinity_pl *pl = &ps->levels[i];
+               printk("\t\tpower level %d    sclk: %u vddc: %u\n",
+                      i, pl->sclk,
+                      trinity_convert_voltage_index_to_value(rdev, pl->vddc_index));
+       }
+       r600_dpm_print_ps_status(rdev, rps);
+}
+
+void trinity_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                        struct seq_file *m)
+{
+       struct radeon_ps *rps = rdev->pm.dpm.current_ps;
+       struct trinity_ps *ps = trinity_get_ps(rps);
+       struct trinity_pl *pl;
+       u32 current_index =
+               (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_MASK) >>
+               CURRENT_STATE_SHIFT;
+
+       if (current_index >= ps->num_levels) {
+               seq_printf(m, "invalid dpm profile %d\n", current_index);
+       } else {
+               pl = &ps->levels[current_index];
+               seq_printf(m, "uvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+               seq_printf(m, "power level %d    sclk: %u vddc: %u\n",
+                          current_index, pl->sclk,
+                          trinity_convert_voltage_index_to_value(rdev, pl->vddc_index));
+       }
+}
+
+void trinity_dpm_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       trinity_cleanup_asic(rdev); /* ??? */
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               kfree(rdev->pm.dpm.ps[i].ps_priv);
+       }
+       kfree(rdev->pm.dpm.ps);
+       kfree(rdev->pm.dpm.priv);
+}
+
+u32 trinity_dpm_get_sclk(struct radeon_device *rdev, bool low)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+       struct trinity_ps *requested_state = trinity_get_ps(&pi->requested_rps);
+
+       if (low)
+               return requested_state->levels[0].sclk;
+       else
+               return requested_state->levels[requested_state->num_levels - 1].sclk;
+}
+
+u32 trinity_dpm_get_mclk(struct radeon_device *rdev, bool low)
+{
+       struct trinity_power_info *pi = trinity_get_pi(rdev);
+
+       return pi->sys_info.bootup_uma_clk;
+}
diff --git a/drivers/gpu/drm/radeon/trinity_dpm.h b/drivers/gpu/drm/radeon/trinity_dpm.h
new file mode 100644 (file)
index 0000000..e82df07
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __TRINITY_DPM_H__
+#define __TRINITY_DPM_H__
+
+#include "sumo_dpm.h"
+
+#define TRINITY_SIZEOF_DPM_STATE_TABLE (SMU_SCLK_DPM_STATE_1_CNTL_0 - SMU_SCLK_DPM_STATE_0_CNTL_0)
+
+struct trinity_pl {
+       u32 sclk;
+       u8 vddc_index;
+       u8 ds_divider_index;
+       u8 ss_divider_index;
+       u8 allow_gnb_slow;
+       u8 force_nbp_state;
+       u8 display_wm;
+       u8 vce_wm;
+};
+
+#define TRINITY_POWERSTATE_FLAGS_NBPS_FORCEHIGH  (1 << 0)
+#define TRINITY_POWERSTATE_FLAGS_NBPS_LOCKTOHIGH (1 << 1)
+#define TRINITY_POWERSTATE_FLAGS_NBPS_LOCKTOLOW  (1 << 2)
+
+#define TRINITY_POWERSTATE_FLAGS_BAPM_DISABLE    (1 << 0)
+
+struct trinity_ps {
+       u32 num_levels;
+       struct trinity_pl levels[SUMO_MAX_HARDWARE_POWERLEVELS];
+
+       u32 nbps_flags;
+       u32 bapm_flags;
+
+       u8 Dpm0PgNbPsLo;
+       u8 Dpm0PgNbPsHi;
+       u8 DpmXNbPsLo;
+       u8 DpmXNbPsHi;
+
+       u32 vclk_low_divider;
+       u32 vclk_high_divider;
+       u32 dclk_low_divider;
+       u32 dclk_high_divider;
+};
+
+#define TRINITY_NUM_NBPSTATES   4
+
+struct trinity_uvd_clock_table_entry
+{
+       u32 vclk;
+       u32 dclk;
+       u8 vclk_did;
+       u8 dclk_did;
+       u8 rsv[2];
+};
+
+struct trinity_sys_info {
+       u32 bootup_uma_clk;
+       u32 bootup_sclk;
+       u32 min_sclk;
+       u32 dentist_vco_freq;
+       u32 nb_dpm_enable;
+       u32 nbp_mclk[TRINITY_NUM_NBPSTATES];
+       u32 nbp_nclk[TRINITY_NUM_NBPSTATES];
+       u16 nbp_voltage_index[TRINITY_NUM_NBPSTATES];
+       u16 bootup_nb_voltage_index;
+       u8 htc_tmp_lmt;
+       u8 htc_hyst_lmt;
+       struct sumo_sclk_voltage_mapping_table sclk_voltage_mapping_table;
+       struct sumo_vid_mapping_table vid_mapping_table;
+       u32 uma_channel_number;
+       struct trinity_uvd_clock_table_entry uvd_clock_table_entries[4];
+};
+
+struct trinity_power_info {
+       u32 at[SUMO_MAX_HARDWARE_POWERLEVELS];
+       u32 dpm_interval;
+       u32 thermal_auto_throttling;
+       struct trinity_sys_info sys_info;
+       struct trinity_pl boot_pl;
+       u32 min_sclk_did;
+       bool enable_nbps_policy;
+       bool voltage_drop_in_dce;
+       bool override_dynamic_mgpg;
+       bool enable_gfx_clock_gating;
+       bool enable_gfx_power_gating;
+       bool enable_mg_clock_gating;
+       bool enable_gfx_dynamic_mgpg;
+       bool enable_auto_thermal_throttling;
+       bool enable_dpm;
+       bool enable_sclk_ds;
+       bool uvd_dpm;
+       struct radeon_ps current_rps;
+       struct trinity_ps current_ps;
+       struct radeon_ps requested_rps;
+       struct trinity_ps requested_ps;
+};
+
+#define TRINITY_AT_DFLT            30
+
+/* trinity_smc.c */
+int trinity_dpm_config(struct radeon_device *rdev, bool enable);
+int trinity_uvd_dpm_config(struct radeon_device *rdev);
+int trinity_dpm_force_state(struct radeon_device *rdev, u32 n);
+int trinity_dpm_n_levels_disabled(struct radeon_device *rdev, u32 n);
+int trinity_dpm_no_forced_level(struct radeon_device *rdev);
+int trinity_dce_enable_voltage_adjustment(struct radeon_device *rdev,
+                                         bool enable);
+int trinity_gfx_dynamic_mgpg_config(struct radeon_device *rdev);
+void trinity_acquire_mutex(struct radeon_device *rdev);
+void trinity_release_mutex(struct radeon_device *rdev);
+
+#endif
diff --git a/drivers/gpu/drm/radeon/trinity_smc.c b/drivers/gpu/drm/radeon/trinity_smc.c
new file mode 100644 (file)
index 0000000..a42d89f
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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.
+ *
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "trinityd.h"
+#include "trinity_dpm.h"
+#include "ppsmc.h"
+
+struct trinity_ps *trinity_get_ps(struct radeon_ps *rps);
+struct trinity_power_info *trinity_get_pi(struct radeon_device *rdev);
+
+static int trinity_notify_message_to_smu(struct radeon_device *rdev, u32 id)
+{
+       int i;
+       u32 v = 0;
+
+       WREG32(SMC_MESSAGE_0, id);
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(SMC_RESP_0) != 0)
+                       break;
+               udelay(1);
+       }
+       v = RREG32(SMC_RESP_0);
+
+       if (v != 1) {
+               if (v == 0xFF) {
+                       DRM_ERROR("SMC failed to handle the message!\n");
+                       return -EINVAL;
+               } else if (v == 0xFE) {
+                       DRM_ERROR("Unknown SMC message!\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+int trinity_dpm_config(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_SMC(SMU_SCRATCH0, 1);
+       else
+               WREG32_SMC(SMU_SCRATCH0, 0);
+
+       return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DPM_Config);
+}
+
+int trinity_dpm_force_state(struct radeon_device *rdev, u32 n)
+{
+       WREG32_SMC(SMU_SCRATCH0, n);
+
+       return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DPM_ForceState);
+}
+
+int trinity_dpm_n_levels_disabled(struct radeon_device *rdev, u32 n)
+{
+       WREG32_SMC(SMU_SCRATCH0, n);
+
+       return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DPM_N_LevelsDisabled);
+}
+
+int trinity_uvd_dpm_config(struct radeon_device *rdev)
+{
+       return trinity_notify_message_to_smu(rdev, PPSMC_MSG_UVD_DPM_Config);
+}
+
+int trinity_dpm_no_forced_level(struct radeon_device *rdev)
+{
+       return trinity_notify_message_to_smu(rdev, PPSMC_MSG_NoForcedLevel);
+}
+
+int trinity_dce_enable_voltage_adjustment(struct radeon_device *rdev,
+                                         bool enable)
+{
+       if (enable)
+               return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DCE_AllowVoltageAdjustment);
+       else
+               return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DCE_RemoveVoltageAdjustment);
+}
+
+int trinity_gfx_dynamic_mgpg_config(struct radeon_device *rdev)
+{
+       return trinity_notify_message_to_smu(rdev, PPSMC_MSG_PG_SIMD_Config);
+}
+
+void trinity_acquire_mutex(struct radeon_device *rdev)
+{
+       int i;
+
+       WREG32(SMC_INT_REQ, 1);
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if ((RREG32(SMC_INT_REQ) & 0xffff) == 1)
+                       break;
+               udelay(1);
+       }
+}
+
+void trinity_release_mutex(struct radeon_device *rdev)
+{
+       WREG32(SMC_INT_REQ, 0);
+}
diff --git a/drivers/gpu/drm/radeon/trinityd.h b/drivers/gpu/drm/radeon/trinityd.h
new file mode 100644 (file)
index 0000000..fd32e27
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, 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: Alex Deucher
+ */
+#ifndef _TRINITYD_H_
+#define _TRINITYD_H_
+
+/* pm registers */
+
+/* cg */
+#define CG_CGTT_LOCAL_0                                 0x0
+#define CG_CGTT_LOCAL_1                                 0x1
+
+/* smc */
+#define SMU_SCLK_DPM_STATE_0_CNTL_0                     0x1f000
+#       define STATE_VALID(x)                           ((x) << 0)
+#       define STATE_VALID_MASK                         (0xff << 0)
+#       define STATE_VALID_SHIFT                        0
+#       define CLK_DIVIDER(x)                           ((x) << 8)
+#       define CLK_DIVIDER_MASK                         (0xff << 8)
+#       define CLK_DIVIDER_SHIFT                        8
+#       define VID(x)                                   ((x) << 16)
+#       define VID_MASK                                 (0xff << 16)
+#       define VID_SHIFT                                16
+#       define LVRT(x)                                  ((x) << 24)
+#       define LVRT_MASK                                (0xff << 24)
+#       define LVRT_SHIFT                               24
+#define SMU_SCLK_DPM_STATE_0_CNTL_1                     0x1f004
+#       define DS_DIV(x)                                ((x) << 0)
+#       define DS_DIV_MASK                              (0xff << 0)
+#       define DS_DIV_SHIFT                             0
+#       define DS_SH_DIV(x)                             ((x) << 8)
+#       define DS_SH_DIV_MASK                           (0xff << 8)
+#       define DS_SH_DIV_SHIFT                          8
+#       define DISPLAY_WM(x)                            ((x) << 16)
+#       define DISPLAY_WM_MASK                          (0xff << 16)
+#       define DISPLAY_WM_SHIFT                         16
+#       define VCE_WM(x)                                ((x) << 24)
+#       define VCE_WM_MASK                              (0xff << 24)
+#       define VCE_WM_SHIFT                             24
+
+#define SMU_SCLK_DPM_STATE_0_CNTL_3                     0x1f00c
+#       define GNB_SLOW(x)                              ((x) << 0)
+#       define GNB_SLOW_MASK                            (0xff << 0)
+#       define GNB_SLOW_SHIFT                           0
+#       define FORCE_NBPS1(x)                           ((x) << 8)
+#       define FORCE_NBPS1_MASK                         (0xff << 8)
+#       define FORCE_NBPS1_SHIFT                        8
+#define SMU_SCLK_DPM_STATE_0_AT                         0x1f010
+#       define AT(x)                                    ((x) << 0)
+#       define AT_MASK                                  (0xff << 0)
+#       define AT_SHIFT                                 0
+
+#define SMU_SCLK_DPM_STATE_0_PG_CNTL                    0x1f014
+#       define PD_SCLK_DIVIDER(x)                       ((x) << 16)
+#       define PD_SCLK_DIVIDER_MASK                     (0xff << 16)
+#       define PD_SCLK_DIVIDER_SHIFT                    16
+
+#define SMU_SCLK_DPM_STATE_1_CNTL_0                     0x1f020
+
+#define SMU_SCLK_DPM_CNTL                               0x1f100
+#       define SCLK_DPM_EN(x)                           ((x) << 0)
+#       define SCLK_DPM_EN_MASK                         (0xff << 0)
+#       define SCLK_DPM_EN_SHIFT                        0
+#       define SCLK_DPM_BOOT_STATE(x)                   ((x) << 16)
+#       define SCLK_DPM_BOOT_STATE_MASK                 (0xff << 16)
+#       define SCLK_DPM_BOOT_STATE_SHIFT                16
+#       define VOLTAGE_CHG_EN(x)                        ((x) << 24)
+#       define VOLTAGE_CHG_EN_MASK                      (0xff << 24)
+#       define VOLTAGE_CHG_EN_SHIFT                     24
+
+#define SMU_SCLK_DPM_TT_CNTL                            0x1f108
+#       define SCLK_TT_EN(x)                            ((x) << 0)
+#       define SCLK_TT_EN_MASK                          (0xff << 0)
+#       define SCLK_TT_EN_SHIFT                         0
+#define SMU_SCLK_DPM_TTT                                0x1f10c
+#       define LT(x)                                    ((x) << 0)
+#       define LT_MASK                                  (0xffff << 0)
+#       define LT_SHIFT                                 0
+#       define HT(x)                                    ((x) << 16)
+#       define HT_MASK                                  (0xffff << 16)
+#       define HT_SHIFT                                 16
+
+#define SMU_UVD_DPM_STATES                              0x1f1a0
+#define SMU_UVD_DPM_CNTL                                0x1f1a4
+
+#define SMU_S_PG_CNTL                                   0x1f118
+#       define DS_PG_EN(x)                              ((x) << 16)
+#       define DS_PG_EN_MASK                            (0xff << 16)
+#       define DS_PG_EN_SHIFT                           16
+
+#define GFX_POWER_GATING_CNTL                           0x1f38c
+#       define PDS_DIV(x)                               ((x) << 0)
+#       define PDS_DIV_MASK                             (0xff << 0)
+#       define PDS_DIV_SHIFT                            0
+#       define SSSD(x)                                  ((x) << 8)
+#       define SSSD_MASK                                (0xff << 8)
+#       define SSSD_SHIFT                               8
+
+#define PM_CONFIG                                       0x1f428
+#       define SVI_Mode                                 (1 << 29)
+
+#define PM_I_CNTL_1                                     0x1f464
+#       define SCLK_DPM(x)                              ((x) << 0)
+#       define SCLK_DPM_MASK                            (0xff << 0)
+#       define SCLK_DPM_SHIFT                           0
+#       define DS_PG_CNTL(x)                            ((x) << 16)
+#       define DS_PG_CNTL_MASK                          (0xff << 16)
+#       define DS_PG_CNTL_SHIFT                         16
+#define PM_TP                                           0x1f468
+
+#define NB_PSTATE_CONFIG                                0x1f5f8
+#       define Dpm0PgNbPsLo(x)                          ((x) << 0)
+#       define Dpm0PgNbPsLo_MASK                        (3 << 0)
+#       define Dpm0PgNbPsLo_SHIFT                       0
+#       define Dpm0PgNbPsHi(x)                          ((x) << 2)
+#       define Dpm0PgNbPsHi_MASK                        (3 << 2)
+#       define Dpm0PgNbPsHi_SHIFT                       2
+#       define DpmXNbPsLo(x)                            ((x) << 4)
+#       define DpmXNbPsLo_MASK                          (3 << 4)
+#       define DpmXNbPsLo_SHIFT                         4
+#       define DpmXNbPsHi(x)                            ((x) << 6)
+#       define DpmXNbPsHi_MASK                          (3 << 6)
+#       define DpmXNbPsHi_SHIFT                         6
+
+#define DC_CAC_VALUE                                    0x1f908
+
+#define GPU_CAC_AVRG_CNTL                               0x1f920
+#       define WINDOW_SIZE(x)                           ((x) << 0)
+#       define WINDOW_SIZE_MASK                         (0xff << 0)
+#       define WINDOW_SIZE_SHIFT                        0
+
+#define CC_SMU_MISC_FUSES                               0xe0001004
+#       define MinSClkDid(x)                   ((x) << 2)
+#       define MinSClkDid_MASK                 (0x7f << 2)
+#       define MinSClkDid_SHIFT                2
+
+#define CC_SMU_TST_EFUSE1_MISC                          0xe000101c
+#       define RB_BACKEND_DISABLE(x)                    ((x) << 16)
+#       define RB_BACKEND_DISABLE_MASK                  (3 << 16)
+#       define RB_BACKEND_DISABLE_SHIFT                 16
+
+#define SMU_SCRATCH_A                                   0xe0003024
+
+#define SMU_SCRATCH0                                    0xe0003040
+
+/* mmio */
+#define SMC_INT_REQ                                     0x220
+
+#define SMC_MESSAGE_0                                   0x22c
+#define SMC_RESP_0                                      0x230
+
+#define GENERAL_PWRMGT                                  0x670
+#       define GLOBAL_PWRMGT_EN                         (1 << 0)
+
+#define SCLK_PWRMGT_CNTL                                0x678
+#       define DYN_PWR_DOWN_EN                          (1 << 2)
+#       define RESET_BUSY_CNT                           (1 << 4)
+#       define RESET_SCLK_CNT                           (1 << 5)
+#       define DYN_GFX_CLK_OFF_EN                       (1 << 7)
+#       define GFX_CLK_FORCE_ON                         (1 << 8)
+#       define DYNAMIC_PM_EN                            (1 << 21)
+
+#define TARGET_AND_CURRENT_PROFILE_INDEX                0x684
+#       define TARGET_STATE(x)                          ((x) << 0)
+#       define TARGET_STATE_MASK                        (0xf << 0)
+#       define TARGET_STATE_SHIFT                       0
+#       define CURRENT_STATE(x)                         ((x) << 4)
+#       define CURRENT_STATE_MASK                       (0xf << 4)
+#       define CURRENT_STATE_SHIFT                      4
+
+#define CG_GIPOTS                                       0x6d8
+#       define CG_GIPOT(x)                              ((x) << 16)
+#       define CG_GIPOT_MASK                            (0xffff << 16)
+#       define CG_GIPOT_SHIFT                           16
+
+#define CG_PG_CTRL                                      0x6e0
+#       define SP(x)                                    ((x) << 0)
+#       define SP_MASK                                  (0xffff << 0)
+#       define SP_SHIFT                                 0
+#       define SU(x)                                    ((x) << 16)
+#       define SU_MASK                                  (0xffff << 16)
+#       define SU_SHIFT                                 16
+
+#define CG_MISC_REG                                     0x708
+
+#define CG_THERMAL_INT_CTRL                             0x738
+#       define DIG_THERM_INTH(x)                        ((x) << 0)
+#       define DIG_THERM_INTH_MASK                      (0xff << 0)
+#       define DIG_THERM_INTH_SHIFT                     0
+#       define DIG_THERM_INTL(x)                        ((x) << 8)
+#       define DIG_THERM_INTL_MASK                      (0xff << 8)
+#       define DIG_THERM_INTL_SHIFT                     8
+#       define THERM_INTH_MASK                          (1 << 24)
+#       define THERM_INTL_MASK                          (1 << 25)
+
+#define CG_CG_VOLTAGE_CNTL                              0x770
+#       define EN                                       (1 << 9)
+
+#define HW_REV                                         0x5564
+#       define ATI_REV_ID_MASK                          (0xf << 28)
+#       define ATI_REV_ID_SHIFT                         28
+/* 0 = A0, 1 = A1, 2 = B0, 3 = C0, etc. */
+
+#define CGTS_SM_CTRL_REG                                0x9150
+
+#define GB_ADDR_CONFIG                                  0x98f8
+
+#endif
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
new file mode 100644 (file)
index 0000000..72887df
--- /dev/null
@@ -0,0 +1,9 @@
+config DRM_RCAR_DU
+       tristate "DRM Support for R-Car Display Unit"
+       depends on DRM && ARM
+       select DRM_KMS_HELPER
+       select DRM_KMS_CMA_HELPER
+       select DRM_GEM_CMA_HELPER
+       help
+         Choose this option if you have an R-Car chipset.
+         If M is selected the module will be called rcar-du-drm.
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
new file mode 100644 (file)
index 0000000..7333c00
--- /dev/null
@@ -0,0 +1,8 @@
+rcar-du-drm-y := rcar_du_crtc.o \
+                rcar_du_drv.o \
+                rcar_du_kms.o \
+                rcar_du_lvds.o \
+                rcar_du_plane.o \
+                rcar_du_vga.o
+
+obj-$(CONFIG_DRM_RCAR_DU)      += rcar-du-drm.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
new file mode 100644 (file)
index 0000000..24183fb
--- /dev/null
@@ -0,0 +1,595 @@
+/*
+ * rcar_du_crtc.c  --  R-Car Display Unit CRTCs
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/mutex.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_lvds.h"
+#include "rcar_du_plane.h"
+#include "rcar_du_regs.h"
+#include "rcar_du_vga.h"
+
+#define to_rcar_crtc(c)        container_of(c, struct rcar_du_crtc, crtc)
+
+static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       return rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
+}
+
+static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data);
+}
+
+static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
+                     rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr);
+}
+
+static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
+                     rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set);
+}
+
+static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg,
+                                u32 clr, u32 set)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+       u32 value = rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
+
+       rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set);
+}
+
+static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_crtc *crtc = &rcrtc->crtc;
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       const struct drm_display_mode *mode = &crtc->mode;
+       unsigned long clk;
+       u32 value;
+       u32 div;
+
+       /* Dot clock */
+       clk = clk_get_rate(rcdu->clock);
+       div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000);
+       div = clamp(div, 1U, 64U) - 1;
+
+       rcar_du_write(rcdu, rcrtc->index ? ESCR2 : ESCR,
+                     ESCR_DCLKSEL_CLKS | div);
+       rcar_du_write(rcdu, rcrtc->index ? OTAR2 : OTAR, 0);
+
+       /* Signal polarities */
+       value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
+             | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
+             | DSMR_DIPM_DE;
+       rcar_du_crtc_write(rcrtc, DSMR, value);
+
+       /* Display timings */
+       rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19);
+       rcar_du_crtc_write(rcrtc, HDER, mode->htotal - mode->hsync_start +
+                                       mode->hdisplay - 19);
+       rcar_du_crtc_write(rcrtc, HSWR, mode->hsync_end -
+                                       mode->hsync_start - 1);
+       rcar_du_crtc_write(rcrtc, HCR,  mode->htotal - 1);
+
+       rcar_du_crtc_write(rcrtc, VDSR, mode->vtotal - mode->vsync_end - 2);
+       rcar_du_crtc_write(rcrtc, VDER, mode->vtotal - mode->vsync_end +
+                                       mode->vdisplay - 2);
+       rcar_du_crtc_write(rcrtc, VSPR, mode->vtotal - mode->vsync_end +
+                                       mode->vsync_start - 1);
+       rcar_du_crtc_write(rcrtc, VCR,  mode->vtotal - 1);
+
+       rcar_du_crtc_write(rcrtc, DESR,  mode->htotal - mode->hsync_start);
+       rcar_du_crtc_write(rcrtc, DEWR,  mode->hdisplay);
+}
+
+static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+       u32 dorcr = rcar_du_read(rcdu, DORCR);
+
+       dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK);
+
+       /* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and
+        * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by
+        * default.
+        */
+       if (rcrtc->outputs & (1 << 1) && rcrtc->index == 0)
+               dorcr |= DORCR_PG2D_DS1;
+       else
+               dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2;
+
+       rcar_du_write(rcdu, DORCR, dorcr);
+}
+
+static void __rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
+{
+       rcar_du_write(rcdu, DSYSR,
+                     (rcar_du_read(rcdu, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) |
+                     (start ? DSYSR_DEN : DSYSR_DRES));
+}
+
+static void rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
+{
+       /* Many of the configuration bits are only updated when the display
+        * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some
+        * of those bits could be pre-configured, but others (especially the
+        * bits related to plane assignment to display timing controllers) need
+        * to be modified at runtime.
+        *
+        * Restart the display controller if a start is requested. Sorry for the
+        * flicker. It should be possible to move most of the "DRES-update" bits
+        * setup to driver initialization time and minimize the number of cases
+        * when the display controller will have to be restarted.
+        */
+       if (start) {
+               if (rcdu->used_crtcs++ != 0)
+                       __rcar_du_start_stop(rcdu, false);
+               __rcar_du_start_stop(rcdu, true);
+       } else {
+               if (--rcdu->used_crtcs == 0)
+                       __rcar_du_start_stop(rcdu, false);
+       }
+}
+
+void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       /* Store the route from the CRTC output to the DU output. The DU will be
+        * configured when starting the CRTC.
+        */
+       rcrtc->outputs |= 1 << output;
+}
+
+void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
+{
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
+       unsigned int num_planes = 0;
+       unsigned int prio = 0;
+       unsigned int i;
+       u32 dptsr = 0;
+       u32 dspr = 0;
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+               struct rcar_du_plane *plane = &rcdu->planes.planes[i];
+               unsigned int j;
+
+               if (plane->crtc != &rcrtc->crtc || !plane->enabled)
+                       continue;
+
+               /* Insert the plane in the sorted planes array. */
+               for (j = num_planes++; j > 0; --j) {
+                       if (planes[j-1]->zpos <= plane->zpos)
+                               break;
+                       planes[j] = planes[j-1];
+               }
+
+               planes[j] = plane;
+               prio += plane->format->planes * 4;
+       }
+
+       for (i = 0; i < num_planes; ++i) {
+               struct rcar_du_plane *plane = planes[i];
+               unsigned int index = plane->hwindex;
+
+               prio -= 4;
+               dspr |= (index + 1) << prio;
+               dptsr |= DPTSR_PnDK(index) |  DPTSR_PnTS(index);
+
+               if (plane->format->planes == 2) {
+                       index = (index + 1) % 8;
+
+                       prio -= 4;
+                       dspr |= (index + 1) << prio;
+                       dptsr |= DPTSR_PnDK(index) |  DPTSR_PnTS(index);
+               }
+       }
+
+       /* Select display timing and dot clock generator 2 for planes associated
+        * with superposition controller 2.
+        */
+       if (rcrtc->index) {
+               u32 value = rcar_du_read(rcdu, DPTSR);
+
+               /* The DPTSR register is updated when the display controller is
+                * stopped. We thus need to restart the DU. Once again, sorry
+                * for the flicker. One way to mitigate the issue would be to
+                * pre-associate planes with CRTCs (either with a fixed 4/4
+                * split, or through a module parameter). Flicker would then
+                * occur only if we need to break the pre-association.
+                */
+               if (value != dptsr) {
+                       rcar_du_write(rcdu, DPTSR, dptsr);
+                       if (rcdu->used_crtcs) {
+                               __rcar_du_start_stop(rcdu, false);
+                               __rcar_du_start_stop(rcdu, true);
+                       }
+               }
+       }
+
+       rcar_du_write(rcdu, rcrtc->index ? DS2PR : DS1PR, dspr);
+}
+
+static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_crtc *crtc = &rcrtc->crtc;
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       unsigned int i;
+
+       if (rcrtc->started)
+               return;
+
+       if (WARN_ON(rcrtc->plane->format == NULL))
+               return;
+
+       /* Set display off and background to black */
+       rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
+       rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
+
+       /* Configure display timings and output routing */
+       rcar_du_crtc_set_display_timing(rcrtc);
+       rcar_du_crtc_set_routing(rcrtc);
+
+       mutex_lock(&rcdu->planes.lock);
+       rcrtc->plane->enabled = true;
+       rcar_du_crtc_update_planes(crtc);
+       mutex_unlock(&rcdu->planes.lock);
+
+       /* Setup planes. */
+       for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+               struct rcar_du_plane *plane = &rcdu->planes.planes[i];
+
+               if (plane->crtc != crtc || !plane->enabled)
+                       continue;
+
+               rcar_du_plane_setup(plane);
+       }
+
+       /* Select master sync mode. This enables display operation in master
+        * sync mode (with the HSYNC and VSYNC signals configured as outputs and
+        * actively driven).
+        */
+       rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_MASTER);
+
+       rcar_du_start_stop(rcdu, true);
+
+       rcrtc->started = true;
+}
+
+static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_crtc *crtc = &rcrtc->crtc;
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+
+       if (!rcrtc->started)
+               return;
+
+       mutex_lock(&rcdu->planes.lock);
+       rcrtc->plane->enabled = false;
+       rcar_du_crtc_update_planes(crtc);
+       mutex_unlock(&rcdu->planes.lock);
+
+       /* Select switch sync mode. This stops display operation and configures
+        * the HSYNC and VSYNC signals as inputs.
+        */
+       rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH);
+
+       rcar_du_start_stop(rcdu, false);
+
+       rcrtc->started = false;
+}
+
+void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       rcar_du_crtc_stop(rcrtc);
+       rcar_du_put(rcdu);
+}
+
+void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       if (rcrtc->dpms != DRM_MODE_DPMS_ON)
+               return;
+
+       rcar_du_get(rcdu);
+       rcar_du_crtc_start(rcrtc);
+}
+
+static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_crtc *crtc = &rcrtc->crtc;
+
+       rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
+       rcar_du_plane_update_base(rcrtc->plane);
+}
+
+static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       if (rcrtc->dpms == mode)
+               return;
+
+       if (mode == DRM_MODE_DPMS_ON) {
+               rcar_du_get(rcdu);
+               rcar_du_crtc_start(rcrtc);
+       } else {
+               rcar_du_crtc_stop(rcrtc);
+               rcar_du_put(rcdu);
+       }
+
+       rcrtc->dpms = mode;
+}
+
+static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc,
+                                   const struct drm_display_mode *mode,
+                                   struct drm_display_mode *adjusted_mode)
+{
+       /* TODO Fixup modes */
+       return true;
+}
+
+static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc)
+{
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       /* We need to access the hardware during mode set, acquire a reference
+        * to the DU.
+        */
+       rcar_du_get(rcdu);
+
+       /* Stop the CRTC and release the plane. Force the DPMS mode to off as a
+        * result.
+        */
+       rcar_du_crtc_stop(rcrtc);
+       rcar_du_plane_release(rcrtc->plane);
+
+       rcrtc->dpms = DRM_MODE_DPMS_OFF;
+}
+
+static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
+                                struct drm_display_mode *mode,
+                                struct drm_display_mode *adjusted_mode,
+                                int x, int y,
+                                struct drm_framebuffer *old_fb)
+{
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       const struct rcar_du_format_info *format;
+       int ret;
+
+       format = rcar_du_format_info(crtc->fb->pixel_format);
+       if (format == NULL) {
+               dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n",
+                       crtc->fb->pixel_format);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       ret = rcar_du_plane_reserve(rcrtc->plane, format);
+       if (ret < 0)
+               goto error;
+
+       rcrtc->plane->format = format;
+       rcrtc->plane->pitch = crtc->fb->pitches[0];
+
+       rcrtc->plane->src_x = x;
+       rcrtc->plane->src_y = y;
+       rcrtc->plane->width = mode->hdisplay;
+       rcrtc->plane->height = mode->vdisplay;
+
+       rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
+
+       rcrtc->outputs = 0;
+
+       return 0;
+
+error:
+       /* There's no rollback/abort operation to clean up in case of error. We
+        * thus need to release the reference to the DU acquired in prepare()
+        * here.
+        */
+       rcar_du_put(rcdu);
+       return ret;
+}
+
+static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       /* We're done, restart the CRTC and set the DPMS mode to on. The
+        * reference to the DU acquired at prepare() time will thus be released
+        * by the DPMS handler (possibly called by the disable() handler).
+        */
+       rcar_du_crtc_start(rcrtc);
+       rcrtc->dpms = DRM_MODE_DPMS_ON;
+}
+
+static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+                                     struct drm_framebuffer *old_fb)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       rcrtc->plane->src_x = x;
+       rcrtc->plane->src_y = y;
+
+       rcar_du_crtc_update_base(to_rcar_crtc(crtc));
+
+       return 0;
+}
+
+static void rcar_du_crtc_disable(struct drm_crtc *crtc)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+       rcar_du_plane_release(rcrtc->plane);
+}
+
+static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
+       .dpms = rcar_du_crtc_dpms,
+       .mode_fixup = rcar_du_crtc_mode_fixup,
+       .prepare = rcar_du_crtc_mode_prepare,
+       .commit = rcar_du_crtc_mode_commit,
+       .mode_set = rcar_du_crtc_mode_set,
+       .mode_set_base = rcar_du_crtc_mode_set_base,
+       .disable = rcar_du_crtc_disable,
+};
+
+void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
+                                  struct drm_file *file)
+{
+       struct drm_pending_vblank_event *event;
+       struct drm_device *dev = rcrtc->crtc.dev;
+       unsigned long flags;
+
+       /* Destroy the pending vertical blanking event associated with the
+        * pending page flip, if any, and disable vertical blanking interrupts.
+        */
+       spin_lock_irqsave(&dev->event_lock, flags);
+       event = rcrtc->event;
+       if (event && event->base.file_priv == file) {
+               rcrtc->event = NULL;
+               event->base.destroy(&event->base);
+               drm_vblank_put(dev, rcrtc->index);
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_pending_vblank_event *event;
+       struct drm_device *dev = rcrtc->crtc.dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       event = rcrtc->event;
+       rcrtc->event = NULL;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       if (event == NULL)
+               return;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       drm_send_vblank_event(dev, rcrtc->index, event);
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       drm_vblank_put(dev, rcrtc->index);
+}
+
+static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
+                                 struct drm_framebuffer *fb,
+                                 struct drm_pending_vblank_event *event)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct drm_device *dev = rcrtc->crtc.dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       if (rcrtc->event != NULL) {
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               return -EBUSY;
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       crtc->fb = fb;
+       rcar_du_crtc_update_base(rcrtc);
+
+       if (event) {
+               event->pipe = rcrtc->index;
+               drm_vblank_get(dev, rcrtc->index);
+               spin_lock_irqsave(&dev->event_lock, flags);
+               rcrtc->event = event;
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
+
+       return 0;
+}
+
+static const struct drm_crtc_funcs crtc_funcs = {
+       .destroy = drm_crtc_cleanup,
+       .set_config = drm_crtc_helper_set_config,
+       .page_flip = rcar_du_crtc_page_flip,
+};
+
+int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index)
+{
+       struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index];
+       struct drm_crtc *crtc = &rcrtc->crtc;
+       int ret;
+
+       rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0;
+       rcrtc->index = index;
+       rcrtc->dpms = DRM_MODE_DPMS_OFF;
+       rcrtc->plane = &rcdu->planes.planes[index];
+
+       rcrtc->plane->crtc = crtc;
+
+       ret = drm_crtc_init(rcdu->ddev, crtc, &crtc_funcs);
+       if (ret < 0)
+               return ret;
+
+       drm_crtc_helper_add(crtc, &crtc_helper_funcs);
+
+       return 0;
+}
+
+void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable)
+{
+       if (enable) {
+               rcar_du_crtc_write(rcrtc, DSRCR, DSRCR_VBCL);
+               rcar_du_crtc_set(rcrtc, DIER, DIER_VBE);
+       } else {
+               rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE);
+       }
+}
+
+void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc)
+{
+       u32 status;
+
+       status = rcar_du_crtc_read(rcrtc, DSSR);
+       rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK);
+
+       if (status & DSSR_VBK) {
+               drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index);
+               rcar_du_crtc_finish_page_flip(rcrtc);
+       }
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
new file mode 100644 (file)
index 0000000..2a0365b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * rcar_du_crtc.h  --  R-Car Display Unit CRTCs
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 __RCAR_DU_CRTC_H__
+#define __RCAR_DU_CRTC_H__
+
+#include <linux/mutex.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+
+struct rcar_du_device;
+struct rcar_du_plane;
+
+struct rcar_du_crtc {
+       struct drm_crtc crtc;
+
+       unsigned int mmio_offset;
+       unsigned int index;
+       bool started;
+
+       struct drm_pending_vblank_event *event;
+       unsigned int outputs;
+       int dpms;
+
+       struct rcar_du_plane *plane;
+};
+
+int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index);
+void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable);
+void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc);
+void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
+                                  struct drm_file *file);
+void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc);
+void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
+
+void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output);
+void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
+
+#endif /* __RCAR_DU_CRTC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
new file mode 100644 (file)
index 0000000..ff82877
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * rcar_du_drv.c  --  R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_regs.h"
+
+/* -----------------------------------------------------------------------------
+ * Core device operations
+ */
+
+/*
+ * rcar_du_get - Acquire a reference to the DU
+ *
+ * Acquiring a reference enables the device clock and setup core registers. A
+ * reference must be held before accessing any hardware registers.
+ *
+ * This function must be called with the DRM mode_config lock held.
+ *
+ * Return 0 in case of success or a negative error code otherwise.
+ */
+int rcar_du_get(struct rcar_du_device *rcdu)
+{
+       int ret;
+
+       if (rcdu->use_count)
+               goto done;
+
+       /* Enable clocks before accessing the hardware. */
+       ret = clk_prepare_enable(rcdu->clock);
+       if (ret < 0)
+               return ret;
+
+       /* Enable extended features */
+       rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE);
+       rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G);
+       rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3);
+       rcar_du_write(rcdu, DEFR4, DEFR4_CODE);
+       rcar_du_write(rcdu, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
+
+       /* Use DS1PR and DS2PR to configure planes priorities and connects the
+        * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
+        */
+       rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS);
+
+done:
+       rcdu->use_count++;
+       return 0;
+}
+
+/*
+ * rcar_du_put - Release a reference to the DU
+ *
+ * Releasing the last reference disables the device clock.
+ *
+ * This function must be called with the DRM mode_config lock held.
+ */
+void rcar_du_put(struct rcar_du_device *rcdu)
+{
+       if (--rcdu->use_count)
+               return;
+
+       clk_disable_unprepare(rcdu->clock);
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM operations
+ */
+
+static int rcar_du_unload(struct drm_device *dev)
+{
+       drm_kms_helper_poll_fini(dev);
+       drm_mode_config_cleanup(dev);
+       drm_vblank_cleanup(dev);
+       drm_irq_uninstall(dev);
+
+       dev->dev_private = NULL;
+
+       return 0;
+}
+
+static int rcar_du_load(struct drm_device *dev, unsigned long flags)
+{
+       struct platform_device *pdev = dev->platformdev;
+       struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
+       struct rcar_du_device *rcdu;
+       struct resource *ioarea;
+       struct resource *mem;
+       int ret;
+
+       if (pdata == NULL) {
+               dev_err(dev->dev, "no platform data\n");
+               return -ENODEV;
+       }
+
+       rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
+       if (rcdu == NULL) {
+               dev_err(dev->dev, "failed to allocate private data\n");
+               return -ENOMEM;
+       }
+
+       rcdu->dev = &pdev->dev;
+       rcdu->pdata = pdata;
+       rcdu->ddev = dev;
+       dev->dev_private = rcdu;
+
+       /* I/O resources and clocks */
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (mem == NULL) {
+               dev_err(&pdev->dev, "failed to get memory resource\n");
+               return -EINVAL;
+       }
+
+       ioarea = devm_request_mem_region(&pdev->dev, mem->start,
+                                        resource_size(mem), pdev->name);
+       if (ioarea == NULL) {
+               dev_err(&pdev->dev, "failed to request memory region\n");
+               return -EBUSY;
+       }
+
+       rcdu->mmio = devm_ioremap_nocache(&pdev->dev, ioarea->start,
+                                         resource_size(ioarea));
+       if (rcdu->mmio == NULL) {
+               dev_err(&pdev->dev, "failed to remap memory resource\n");
+               return -ENOMEM;
+       }
+
+       rcdu->clock = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(rcdu->clock)) {
+               dev_err(&pdev->dev, "failed to get clock\n");
+               return -ENOENT;
+       }
+
+       /* DRM/KMS objects */
+       ret = rcar_du_modeset_init(rcdu);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
+               goto done;
+       }
+
+       /* IRQ and vblank handling */
+       ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to initialize vblank\n");
+               goto done;
+       }
+
+       ret = drm_irq_install(dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to install IRQ handler\n");
+               goto done;
+       }
+
+       platform_set_drvdata(pdev, rcdu);
+
+done:
+       if (ret)
+               rcar_du_unload(dev);
+
+       return ret;
+}
+
+static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file)
+{
+       struct rcar_du_device *rcdu = dev->dev_private;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
+               rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file);
+}
+
+static irqreturn_t rcar_du_irq(int irq, void *arg)
+{
+       struct drm_device *dev = arg;
+       struct rcar_du_device *rcdu = dev->dev_private;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
+               rcar_du_crtc_irq(&rcdu->crtcs[i]);
+
+       return IRQ_HANDLED;
+}
+
+static int rcar_du_enable_vblank(struct drm_device *dev, int crtc)
+{
+       struct rcar_du_device *rcdu = dev->dev_private;
+
+       rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], true);
+
+       return 0;
+}
+
+static void rcar_du_disable_vblank(struct drm_device *dev, int crtc)
+{
+       struct rcar_du_device *rcdu = dev->dev_private;
+
+       rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], false);
+}
+
+static const struct file_operations rcar_du_fops = {
+       .owner          = THIS_MODULE,
+       .open           = drm_open,
+       .release        = drm_release,
+       .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = drm_compat_ioctl,
+#endif
+       .poll           = drm_poll,
+       .read           = drm_read,
+       .fasync         = drm_fasync,
+       .llseek         = no_llseek,
+       .mmap           = drm_gem_cma_mmap,
+};
+
+static struct drm_driver rcar_du_driver = {
+       .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
+                               | DRIVER_PRIME,
+       .load                   = rcar_du_load,
+       .unload                 = rcar_du_unload,
+       .preclose               = rcar_du_preclose,
+       .irq_handler            = rcar_du_irq,
+       .get_vblank_counter     = drm_vblank_count,
+       .enable_vblank          = rcar_du_enable_vblank,
+       .disable_vblank         = rcar_du_disable_vblank,
+       .gem_free_object        = drm_gem_cma_free_object,
+       .gem_vm_ops             = &drm_gem_cma_vm_ops,
+       .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
+       .gem_prime_import       = drm_gem_cma_dmabuf_import,
+       .gem_prime_export       = drm_gem_cma_dmabuf_export,
+       .dumb_create            = rcar_du_dumb_create,
+       .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
+       .dumb_destroy           = drm_gem_cma_dumb_destroy,
+       .fops                   = &rcar_du_fops,
+       .name                   = "rcar-du",
+       .desc                   = "Renesas R-Car Display Unit",
+       .date                   = "20130110",
+       .major                  = 1,
+       .minor                  = 0,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+#if CONFIG_PM_SLEEP
+static int rcar_du_pm_suspend(struct device *dev)
+{
+       struct rcar_du_device *rcdu = dev_get_drvdata(dev);
+
+       drm_kms_helper_poll_disable(rcdu->ddev);
+       /* TODO Suspend the CRTC */
+
+       return 0;
+}
+
+static int rcar_du_pm_resume(struct device *dev)
+{
+       struct rcar_du_device *rcdu = dev_get_drvdata(dev);
+
+       /* TODO Resume the CRTC */
+
+       drm_kms_helper_poll_enable(rcdu->ddev);
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops rcar_du_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
+};
+
+/* -----------------------------------------------------------------------------
+ * Platform driver
+ */
+
+static int rcar_du_probe(struct platform_device *pdev)
+{
+       return drm_platform_init(&rcar_du_driver, pdev);
+}
+
+static int rcar_du_remove(struct platform_device *pdev)
+{
+       drm_platform_exit(&rcar_du_driver, pdev);
+
+       return 0;
+}
+
+static struct platform_driver rcar_du_platform_driver = {
+       .probe          = rcar_du_probe,
+       .remove         = rcar_du_remove,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "rcar-du",
+               .pm     = &rcar_du_pm_ops,
+       },
+};
+
+module_platform_driver(rcar_du_platform_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
new file mode 100644 (file)
index 0000000..193cc59
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * rcar_du_drv.h  --  R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 __RCAR_DU_DRV_H__
+#define __RCAR_DU_DRV_H__
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/rcar-du.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_plane.h"
+
+struct clk;
+struct device;
+struct drm_device;
+
+struct rcar_du_device {
+       struct device *dev;
+       const struct rcar_du_platform_data *pdata;
+
+       void __iomem *mmio;
+       struct clk *clock;
+       unsigned int use_count;
+
+       struct drm_device *ddev;
+
+       struct rcar_du_crtc crtcs[2];
+       unsigned int used_crtcs;
+       unsigned int num_crtcs;
+
+       struct {
+               struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES];
+               unsigned int free;
+               struct mutex lock;
+
+               struct drm_property *alpha;
+               struct drm_property *colorkey;
+               struct drm_property *zpos;
+       } planes;
+};
+
+int rcar_du_get(struct rcar_du_device *rcdu);
+void rcar_du_put(struct rcar_du_device *rcdu);
+
+static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg)
+{
+       return ioread32(rcdu->mmio + reg);
+}
+
+static inline void rcar_du_write(struct rcar_du_device *rcdu, u32 reg, u32 data)
+{
+       iowrite32(data, rcdu->mmio + reg);
+}
+
+#endif /* __RCAR_DU_DRV_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
new file mode 100644 (file)
index 0000000..d30c2e2
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * rcar_du_kms.c  --  R-Car Display Unit Mode Setting
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_lvds.h"
+#include "rcar_du_regs.h"
+#include "rcar_du_vga.h"
+
+/* -----------------------------------------------------------------------------
+ * Format helpers
+ */
+
+static const struct rcar_du_format_info rcar_du_format_infos[] = {
+       {
+               .fourcc = DRM_FORMAT_RGB565,
+               .bpp = 16,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_ARGB1555,
+               .bpp = 16,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_XRGB1555,
+               .bpp = 16,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_XRGB8888,
+               .bpp = 32,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
+               .edf = PnDDCR4_EDF_RGB888,
+       }, {
+               .fourcc = DRM_FORMAT_ARGB8888,
+               .bpp = 32,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP,
+               .edf = PnDDCR4_EDF_ARGB8888,
+       }, {
+               .fourcc = DRM_FORMAT_UYVY,
+               .bpp = 16,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_YUYV,
+               .bpp = 16,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_NV12,
+               .bpp = 12,
+               .planes = 2,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_NV21,
+               .bpp = 12,
+               .planes = 2,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               /* In YUV 4:2:2, only NV16 is supported (NV61 isn't) */
+               .fourcc = DRM_FORMAT_NV16,
+               .bpp = 16,
+               .planes = 2,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       },
+};
+
+const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(rcar_du_format_infos); ++i) {
+               if (rcar_du_format_infos[i].fourcc == fourcc)
+                       return &rcar_du_format_infos[i];
+       }
+
+       return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Common connector and encoder functions
+ */
+
+struct drm_encoder *
+rcar_du_connector_best_encoder(struct drm_connector *connector)
+{
+       struct rcar_du_connector *rcon = to_rcar_connector(connector);
+
+       return &rcon->encoder->encoder;
+}
+
+void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder)
+{
+}
+
+void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
+                             struct drm_display_mode *mode,
+                             struct drm_display_mode *adjusted_mode)
+{
+       struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+
+       rcar_du_crtc_route_output(encoder->crtc, renc->output);
+}
+
+void rcar_du_encoder_mode_commit(struct drm_encoder *encoder)
+{
+}
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer
+ */
+
+int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
+                       struct drm_mode_create_dumb *args)
+{
+       unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+       unsigned int align;
+
+       /* The pitch must be aligned to a 16 pixels boundary. */
+       align = 16 * args->bpp / 8;
+       args->pitch = roundup(max(args->pitch, min_pitch), align);
+
+       return drm_gem_cma_dumb_create(file, dev, args);
+}
+
+static struct drm_framebuffer *
+rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+                 struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       const struct rcar_du_format_info *format;
+       unsigned int align;
+
+       format = rcar_du_format_info(mode_cmd->pixel_format);
+       if (format == NULL) {
+               dev_dbg(dev->dev, "unsupported pixel format %08x\n",
+                       mode_cmd->pixel_format);
+               return ERR_PTR(-EINVAL);
+       }
+
+       align = 16 * format->bpp / 8;
+
+       if (mode_cmd->pitches[0] & (align - 1) ||
+           mode_cmd->pitches[0] >= 8192) {
+               dev_dbg(dev->dev, "invalid pitch value %u\n",
+                       mode_cmd->pitches[0]);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (format->planes == 2) {
+               if (mode_cmd->pitches[1] != mode_cmd->pitches[0]) {
+                       dev_dbg(dev->dev,
+                               "luma and chroma pitches do not match\n");
+                       return ERR_PTR(-EINVAL);
+               }
+       }
+
+       return drm_fb_cma_create(dev, file_priv, mode_cmd);
+}
+
+static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
+       .fb_create = rcar_du_fb_create,
+};
+
+int rcar_du_modeset_init(struct rcar_du_device *rcdu)
+{
+       struct drm_device *dev = rcdu->ddev;
+       struct drm_encoder *encoder;
+       unsigned int i;
+       int ret;
+
+       drm_mode_config_init(rcdu->ddev);
+
+       rcdu->ddev->mode_config.min_width = 0;
+       rcdu->ddev->mode_config.min_height = 0;
+       rcdu->ddev->mode_config.max_width = 4095;
+       rcdu->ddev->mode_config.max_height = 2047;
+       rcdu->ddev->mode_config.funcs = &rcar_du_mode_config_funcs;
+
+       ret = rcar_du_plane_init(rcdu);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) {
+               ret = rcar_du_crtc_create(rcdu, i);
+               if (ret < 0)
+                       return ret;
+       }
+
+       rcdu->used_crtcs = 0;
+       rcdu->num_crtcs = i;
+
+       for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
+               const struct rcar_du_encoder_data *pdata =
+                       &rcdu->pdata->encoders[i];
+
+               if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) {
+                       dev_warn(rcdu->dev,
+                                "encoder %u references unexisting output %u, skipping\n",
+                                i, pdata->output);
+                       continue;
+               }
+
+               switch (pdata->encoder) {
+               case RCAR_DU_ENCODER_VGA:
+                       rcar_du_vga_init(rcdu, &pdata->u.vga, pdata->output);
+                       break;
+
+               case RCAR_DU_ENCODER_LVDS:
+                       rcar_du_lvds_init(rcdu, &pdata->u.lvds, pdata->output);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       /* Set the possible CRTCs and possible clones. All encoders can be
+        * driven by the CRTC associated with the output they're connected to,
+        * as well as by CRTC 0.
+        */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+
+               encoder->possible_crtcs = (1 << 0) | (1 << renc->output);
+               encoder->possible_clones = 1 << 0;
+       }
+
+       ret = rcar_du_plane_register(rcdu);
+       if (ret < 0)
+               return ret;
+
+       drm_kms_helper_poll_init(rcdu->ddev);
+
+       drm_helper_disable_unused_functions(rcdu->ddev);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
new file mode 100644 (file)
index 0000000..dba4722
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * rcar_du_kms.h  --  R-Car Display Unit Mode Setting
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 __RCAR_DU_KMS_H__
+#define __RCAR_DU_KMS_H__
+
+#include <linux/types.h>
+
+#include <drm/drm_crtc.h>
+
+struct rcar_du_device;
+
+struct rcar_du_format_info {
+       u32 fourcc;
+       unsigned int bpp;
+       unsigned int planes;
+       unsigned int pnmr;
+       unsigned int edf;
+};
+
+struct rcar_du_encoder {
+       struct drm_encoder encoder;
+       unsigned int output;
+};
+
+#define to_rcar_encoder(e) \
+       container_of(e, struct rcar_du_encoder, encoder)
+
+struct rcar_du_connector {
+       struct drm_connector connector;
+       struct rcar_du_encoder *encoder;
+};
+
+#define to_rcar_connector(c) \
+       container_of(c, struct rcar_du_connector, connector)
+
+const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc);
+
+struct drm_encoder *
+rcar_du_connector_best_encoder(struct drm_connector *connector);
+void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder);
+void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
+                             struct drm_display_mode *mode,
+                             struct drm_display_mode *adjusted_mode);
+void rcar_du_encoder_mode_commit(struct drm_encoder *encoder);
+
+int rcar_du_modeset_init(struct rcar_du_device *rcdu);
+
+int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
+                       struct drm_mode_create_dumb *args);
+
+#endif /* __RCAR_DU_KMS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c
new file mode 100644 (file)
index 0000000..7aefe72
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * rcar_du_lvds.c  --  R-Car Display Unit LVDS Encoder and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_lvds.h"
+
+struct rcar_du_lvds_connector {
+       struct rcar_du_connector connector;
+
+       const struct rcar_du_panel_data *panel;
+};
+
+#define to_rcar_lvds_connector(c) \
+       container_of(c, struct rcar_du_lvds_connector, connector.connector)
+
+/* -----------------------------------------------------------------------------
+ * Connector
+ */
+
+static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
+{
+       struct rcar_du_lvds_connector *lvdscon = to_rcar_lvds_connector(connector);
+       struct drm_display_mode *mode;
+
+       mode = drm_mode_create(connector->dev);
+       if (mode == NULL)
+               return 0;
+
+       mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
+       mode->clock = lvdscon->panel->mode.clock;
+       mode->hdisplay = lvdscon->panel->mode.hdisplay;
+       mode->hsync_start = lvdscon->panel->mode.hsync_start;
+       mode->hsync_end = lvdscon->panel->mode.hsync_end;
+       mode->htotal = lvdscon->panel->mode.htotal;
+       mode->vdisplay = lvdscon->panel->mode.vdisplay;
+       mode->vsync_start = lvdscon->panel->mode.vsync_start;
+       mode->vsync_end = lvdscon->panel->mode.vsync_end;
+       mode->vtotal = lvdscon->panel->mode.vtotal;
+       mode->flags = lvdscon->panel->mode.flags;
+
+       drm_mode_set_name(mode);
+       drm_mode_probed_add(connector, mode);
+
+       return 1;
+}
+
+static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector,
+                                           struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+       .get_modes = rcar_du_lvds_connector_get_modes,
+       .mode_valid = rcar_du_lvds_connector_mode_valid,
+       .best_encoder = rcar_du_connector_best_encoder,
+};
+
+static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
+{
+       drm_sysfs_connector_remove(connector);
+       drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force)
+{
+       return connector_status_connected;
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .detect = rcar_du_lvds_connector_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = rcar_du_lvds_connector_destroy,
+};
+
+static int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
+                                      struct rcar_du_encoder *renc,
+                                      const struct rcar_du_panel_data *panel)
+{
+       struct rcar_du_lvds_connector *lvdscon;
+       struct drm_connector *connector;
+       int ret;
+
+       lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL);
+       if (lvdscon == NULL)
+               return -ENOMEM;
+
+       lvdscon->panel = panel;
+
+       connector = &lvdscon->connector.connector;
+       connector->display_info.width_mm = panel->width_mm;
+       connector->display_info.height_mm = panel->height_mm;
+
+       ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
+                                DRM_MODE_CONNECTOR_LVDS);
+       if (ret < 0)
+               return ret;
+
+       drm_connector_helper_add(connector, &connector_helper_funcs);
+       ret = drm_sysfs_connector_add(connector);
+       if (ret < 0)
+               return ret;
+
+       drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+       drm_object_property_set_value(&connector->base,
+               rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+       ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
+       if (ret < 0)
+               return ret;
+
+       connector->encoder = &renc->encoder;
+       lvdscon->connector.encoder = renc;
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Encoder
+ */
+
+static void rcar_du_lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool rcar_du_lvds_encoder_mode_fixup(struct drm_encoder *encoder,
+                                          const struct drm_display_mode *mode,
+                                          struct drm_display_mode *adjusted_mode)
+{
+       const struct drm_display_mode *panel_mode;
+       struct drm_device *dev = encoder->dev;
+       struct drm_connector *connector;
+       bool found = false;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (connector->encoder == encoder) {
+                       found = true;
+                       break;
+               }
+       }
+
+       if (!found) {
+               dev_dbg(dev->dev, "mode_fixup: no connector found\n");
+               return false;
+       }
+
+       if (list_empty(&connector->modes)) {
+               dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
+               return false;
+       }
+
+       panel_mode = list_first_entry(&connector->modes,
+                                     struct drm_display_mode, head);
+
+       /* We're not allowed to modify the resolution. */
+       if (mode->hdisplay != panel_mode->hdisplay ||
+           mode->vdisplay != panel_mode->vdisplay)
+               return false;
+
+       /* The flat panel mode is fixed, just copy it to the adjusted mode. */
+       drm_mode_copy(adjusted_mode, panel_mode);
+
+       return true;
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+       .dpms = rcar_du_lvds_encoder_dpms,
+       .mode_fixup = rcar_du_lvds_encoder_mode_fixup,
+       .prepare = rcar_du_encoder_mode_prepare,
+       .commit = rcar_du_encoder_mode_commit,
+       .mode_set = rcar_du_encoder_mode_set,
+};
+
+static const struct drm_encoder_funcs encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+};
+
+int rcar_du_lvds_init(struct rcar_du_device *rcdu,
+                     const struct rcar_du_encoder_lvds_data *data,
+                     unsigned int output)
+{
+       struct rcar_du_encoder *renc;
+       int ret;
+
+       renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
+       if (renc == NULL)
+               return -ENOMEM;
+
+       renc->output = output;
+
+       ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
+                              DRM_MODE_ENCODER_LVDS);
+       if (ret < 0)
+               return ret;
+
+       drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
+
+       return rcar_du_lvds_connector_init(rcdu, renc, &data->panel);
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.h b/drivers/gpu/drm/rcar-du/rcar_du_lvds.h
new file mode 100644 (file)
index 0000000..b47f832
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * rcar_du_lvds.h  --  R-Car Display Unit LVDS Encoder and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 __RCAR_DU_LVDS_H__
+#define __RCAR_DU_LVDS_H__
+
+struct rcar_du_device;
+struct rcar_du_encoder_lvds_data;
+
+int rcar_du_lvds_init(struct rcar_du_device *rcdu,
+                     const struct rcar_du_encoder_lvds_data *data,
+                     unsigned int output);
+
+#endif /* __RCAR_DU_LVDS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
new file mode 100644 (file)
index 0000000..a65f81d
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * rcar_du_plane.c  --  R-Car Display Unit Planes
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_plane.h"
+#include "rcar_du_regs.h"
+
+#define RCAR_DU_COLORKEY_NONE          (0 << 24)
+#define RCAR_DU_COLORKEY_SOURCE                (1 << 24)
+#define RCAR_DU_COLORKEY_MASK          (1 << 24)
+
+struct rcar_du_kms_plane {
+       struct drm_plane plane;
+       struct rcar_du_plane *hwplane;
+};
+
+static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
+{
+       return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane;
+}
+
+static u32 rcar_du_plane_read(struct rcar_du_device *rcdu,
+                             unsigned int index, u32 reg)
+{
+       return rcar_du_read(rcdu, index * PLANE_OFF + reg);
+}
+
+static void rcar_du_plane_write(struct rcar_du_device *rcdu,
+                               unsigned int index, u32 reg, u32 data)
+{
+       rcar_du_write(rcdu, index * PLANE_OFF + reg, data);
+}
+
+int rcar_du_plane_reserve(struct rcar_du_plane *plane,
+                         const struct rcar_du_format_info *format)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+       unsigned int i;
+       int ret = -EBUSY;
+
+       mutex_lock(&rcdu->planes.lock);
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+               if (!(rcdu->planes.free & (1 << i)))
+                       continue;
+
+               if (format->planes == 1 ||
+                   rcdu->planes.free & (1 << ((i + 1) % 8)))
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(rcdu->planes.planes))
+               goto done;
+
+       rcdu->planes.free &= ~(1 << i);
+       if (format->planes == 2)
+               rcdu->planes.free &= ~(1 << ((i + 1) % 8));
+
+       plane->hwindex = i;
+
+       ret = 0;
+
+done:
+       mutex_unlock(&rcdu->planes.lock);
+       return ret;
+}
+
+void rcar_du_plane_release(struct rcar_du_plane *plane)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+
+       if (plane->hwindex == -1)
+               return;
+
+       mutex_lock(&rcdu->planes.lock);
+       rcdu->planes.free |= 1 << plane->hwindex;
+       if (plane->format->planes == 2)
+               rcdu->planes.free |= 1 << ((plane->hwindex + 1) % 8);
+       mutex_unlock(&rcdu->planes.lock);
+
+       plane->hwindex = -1;
+}
+
+void rcar_du_plane_update_base(struct rcar_du_plane *plane)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+       unsigned int index = plane->hwindex;
+
+       /* According to the datasheet the Y position is expressed in raster line
+        * units. However, 32bpp formats seem to require a doubled Y position
+        * value. Similarly, for the second plane, NV12 and NV21 formats seem to
+        * require a halved Y position value.
+        */
+       rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x);
+       rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y *
+                           (plane->format->bpp == 32 ? 2 : 1));
+       rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[0]);
+
+       if (plane->format->planes == 2) {
+               index = (index + 1) % 8;
+
+               rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x);
+               rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y *
+                                   (plane->format->bpp == 16 ? 2 : 1) / 2);
+               rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[1]);
+       }
+}
+
+void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
+                               struct drm_framebuffer *fb)
+{
+       struct drm_gem_cma_object *gem;
+
+       gem = drm_fb_cma_get_gem_obj(fb, 0);
+       plane->dma[0] = gem->paddr + fb->offsets[0];
+
+       if (plane->format->planes == 2) {
+               gem = drm_fb_cma_get_gem_obj(fb, 1);
+               plane->dma[1] = gem->paddr + fb->offsets[1];
+       }
+}
+
+static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
+                                    unsigned int index)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+       u32 colorkey;
+       u32 pnmr;
+
+       /* The PnALPHAR register controls alpha-blending in 16bpp formats
+        * (ARGB1555 and XRGB1555).
+        *
+        * For ARGB, set the alpha value to 0, and enable alpha-blending when
+        * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
+        *
+        * For XRGB, set the alpha value to the plane-wide alpha value and
+        * enable alpha-blending regardless of the X bit value.
+        */
+       if (plane->format->fourcc != DRM_FORMAT_XRGB1555)
+               rcar_du_plane_write(rcdu, index, PnALPHAR, PnALPHAR_ABIT_0);
+       else
+               rcar_du_plane_write(rcdu, index, PnALPHAR,
+                                   PnALPHAR_ABIT_X | plane->alpha);
+
+       pnmr = PnMR_BM_MD | plane->format->pnmr;
+
+       /* Disable color keying when requested. YUV formats have the
+        * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
+        * automatically.
+        */
+       if ((plane->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
+               pnmr |= PnMR_SPIM_TP_OFF;
+
+       /* For packed YUV formats we need to select the U/V order. */
+       if (plane->format->fourcc == DRM_FORMAT_YUYV)
+               pnmr |= PnMR_YCDF_YUYV;
+
+       rcar_du_plane_write(rcdu, index, PnMR, pnmr);
+
+       switch (plane->format->fourcc) {
+       case DRM_FORMAT_RGB565:
+               colorkey = ((plane->colorkey & 0xf80000) >> 8)
+                        | ((plane->colorkey & 0x00fc00) >> 5)
+                        | ((plane->colorkey & 0x0000f8) >> 3);
+               rcar_du_plane_write(rcdu, index, PnTC2R, colorkey);
+               break;
+
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_XRGB1555:
+               colorkey = ((plane->colorkey & 0xf80000) >> 9)
+                        | ((plane->colorkey & 0x00f800) >> 6)
+                        | ((plane->colorkey & 0x0000f8) >> 3);
+               rcar_du_plane_write(rcdu, index, PnTC2R, colorkey);
+               break;
+
+       case DRM_FORMAT_XRGB8888:
+       case DRM_FORMAT_ARGB8888:
+               rcar_du_plane_write(rcdu, index, PnTC3R,
+                                   PnTC3R_CODE | (plane->colorkey & 0xffffff));
+               break;
+       }
+}
+
+static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
+                                 unsigned int index)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+       u32 ddcr2 = PnDDCR2_CODE;
+       u32 ddcr4;
+       u32 mwr;
+
+       /* Data format
+        *
+        * The data format is selected by the DDDF field in PnMR and the EDF
+        * field in DDCR4.
+        */
+       ddcr4 = rcar_du_plane_read(rcdu, index, PnDDCR4);
+       ddcr4 &= ~PnDDCR4_EDF_MASK;
+       ddcr4 |= plane->format->edf | PnDDCR4_CODE;
+
+       rcar_du_plane_setup_mode(plane, index);
+
+       if (plane->format->planes == 2) {
+               if (plane->hwindex != index) {
+                       if (plane->format->fourcc == DRM_FORMAT_NV12 ||
+                           plane->format->fourcc == DRM_FORMAT_NV21)
+                               ddcr2 |= PnDDCR2_Y420;
+
+                       if (plane->format->fourcc == DRM_FORMAT_NV21)
+                               ddcr2 |= PnDDCR2_NV21;
+
+                       ddcr2 |= PnDDCR2_DIVU;
+               } else {
+                       ddcr2 |= PnDDCR2_DIVY;
+               }
+       }
+
+       rcar_du_plane_write(rcdu, index, PnDDCR2, ddcr2);
+       rcar_du_plane_write(rcdu, index, PnDDCR4, ddcr4);
+
+       /* Memory pitch (expressed in pixels) */
+       if (plane->format->planes == 2)
+               mwr = plane->pitch;
+       else
+               mwr = plane->pitch * 8 / plane->format->bpp;
+
+       rcar_du_plane_write(rcdu, index, PnMWR, mwr);
+
+       /* Destination position and size */
+       rcar_du_plane_write(rcdu, index, PnDSXR, plane->width);
+       rcar_du_plane_write(rcdu, index, PnDSYR, plane->height);
+       rcar_du_plane_write(rcdu, index, PnDPXR, plane->dst_x);
+       rcar_du_plane_write(rcdu, index, PnDPYR, plane->dst_y);
+
+       /* Wrap-around and blinking, disabled */
+       rcar_du_plane_write(rcdu, index, PnWASPR, 0);
+       rcar_du_plane_write(rcdu, index, PnWAMWR, 4095);
+       rcar_du_plane_write(rcdu, index, PnBTR, 0);
+       rcar_du_plane_write(rcdu, index, PnMLR, 0);
+}
+
+void rcar_du_plane_setup(struct rcar_du_plane *plane)
+{
+       __rcar_du_plane_setup(plane, plane->hwindex);
+       if (plane->format->planes == 2)
+               __rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8);
+
+       rcar_du_plane_update_base(plane);
+}
+
+static int
+rcar_du_plane_update(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 rcar_du_plane *rplane = to_rcar_plane(plane);
+       struct rcar_du_device *rcdu = plane->dev->dev_private;
+       const struct rcar_du_format_info *format;
+       unsigned int nplanes;
+       int ret;
+
+       format = rcar_du_format_info(fb->pixel_format);
+       if (format == NULL) {
+               dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
+                       fb->pixel_format);
+               return -EINVAL;
+       }
+
+       if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
+               dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
+               return -EINVAL;
+       }
+
+       nplanes = rplane->format ? rplane->format->planes : 0;
+
+       /* Reallocate hardware planes if the number of required planes has
+        * changed.
+        */
+       if (format->planes != nplanes) {
+               rcar_du_plane_release(rplane);
+               ret = rcar_du_plane_reserve(rplane, format);
+               if (ret < 0)
+                       return ret;
+       }
+
+       rplane->crtc = crtc;
+       rplane->format = format;
+       rplane->pitch = fb->pitches[0];
+
+       rplane->src_x = src_x >> 16;
+       rplane->src_y = src_y >> 16;
+       rplane->dst_x = crtc_x;
+       rplane->dst_y = crtc_y;
+       rplane->width = crtc_w;
+       rplane->height = crtc_h;
+
+       rcar_du_plane_compute_base(rplane, fb);
+       rcar_du_plane_setup(rplane);
+
+       mutex_lock(&rcdu->planes.lock);
+       rplane->enabled = true;
+       rcar_du_crtc_update_planes(rplane->crtc);
+       mutex_unlock(&rcdu->planes.lock);
+
+       return 0;
+}
+
+static int rcar_du_plane_disable(struct drm_plane *plane)
+{
+       struct rcar_du_device *rcdu = plane->dev->dev_private;
+       struct rcar_du_plane *rplane = to_rcar_plane(plane);
+
+       if (!rplane->enabled)
+               return 0;
+
+       mutex_lock(&rcdu->planes.lock);
+       rplane->enabled = false;
+       rcar_du_crtc_update_planes(rplane->crtc);
+       mutex_unlock(&rcdu->planes.lock);
+
+       rcar_du_plane_release(rplane);
+
+       rplane->crtc = NULL;
+       rplane->format = NULL;
+
+       return 0;
+}
+
+/* Both the .set_property and the .update_plane operations are called with the
+ * mode_config lock held. There is this no need to explicitly protect access to
+ * the alpha and colorkey fields and the mode register.
+ */
+static void rcar_du_plane_set_alpha(struct rcar_du_plane *plane, u32 alpha)
+{
+       if (plane->alpha == alpha)
+               return;
+
+       plane->alpha = alpha;
+       if (!plane->enabled || plane->format->fourcc != DRM_FORMAT_XRGB1555)
+               return;
+
+       rcar_du_plane_setup_mode(plane, plane->hwindex);
+}
+
+static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane,
+                                      u32 colorkey)
+{
+       if (plane->colorkey == colorkey)
+               return;
+
+       plane->colorkey = colorkey;
+       if (!plane->enabled)
+               return;
+
+       rcar_du_plane_setup_mode(plane, plane->hwindex);
+}
+
+static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane,
+                                  unsigned int zpos)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+
+       mutex_lock(&rcdu->planes.lock);
+       if (plane->zpos == zpos)
+               goto done;
+
+       plane->zpos = zpos;
+       if (!plane->enabled)
+               goto done;
+
+       rcar_du_crtc_update_planes(plane->crtc);
+
+done:
+       mutex_unlock(&rcdu->planes.lock);
+}
+
+static int rcar_du_plane_set_property(struct drm_plane *plane,
+                                     struct drm_property *property,
+                                     uint64_t value)
+{
+       struct rcar_du_device *rcdu = plane->dev->dev_private;
+       struct rcar_du_plane *rplane = to_rcar_plane(plane);
+
+       if (property == rcdu->planes.alpha)
+               rcar_du_plane_set_alpha(rplane, value);
+       else if (property == rcdu->planes.colorkey)
+               rcar_du_plane_set_colorkey(rplane, value);
+       else if (property == rcdu->planes.zpos)
+               rcar_du_plane_set_zpos(rplane, value);
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static const struct drm_plane_funcs rcar_du_plane_funcs = {
+       .update_plane = rcar_du_plane_update,
+       .disable_plane = rcar_du_plane_disable,
+       .set_property = rcar_du_plane_set_property,
+       .destroy = drm_plane_cleanup,
+};
+
+static const uint32_t formats[] = {
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV21,
+       DRM_FORMAT_NV16,
+};
+
+int rcar_du_plane_init(struct rcar_du_device *rcdu)
+{
+       unsigned int i;
+
+       mutex_init(&rcdu->planes.lock);
+       rcdu->planes.free = 0xff;
+
+       rcdu->planes.alpha =
+               drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
+       if (rcdu->planes.alpha == NULL)
+               return -ENOMEM;
+
+       /* The color key is expressed as an RGB888 triplet stored in a 32-bit
+        * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
+        * or enable source color keying (1).
+        */
+       rcdu->planes.colorkey =
+               drm_property_create_range(rcdu->ddev, 0, "colorkey",
+                                         0, 0x01ffffff);
+       if (rcdu->planes.colorkey == NULL)
+               return -ENOMEM;
+
+       rcdu->planes.zpos =
+               drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7);
+       if (rcdu->planes.zpos == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+               struct rcar_du_plane *plane = &rcdu->planes.planes[i];
+
+               plane->dev = rcdu;
+               plane->hwindex = -1;
+               plane->alpha = 255;
+               plane->colorkey = RCAR_DU_COLORKEY_NONE;
+               plane->zpos = 0;
+       }
+
+       return 0;
+}
+
+int rcar_du_plane_register(struct rcar_du_device *rcdu)
+{
+       unsigned int i;
+       int ret;
+
+       for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) {
+               struct rcar_du_kms_plane *plane;
+
+               plane = devm_kzalloc(rcdu->dev, sizeof(*plane), GFP_KERNEL);
+               if (plane == NULL)
+                       return -ENOMEM;
+
+               plane->hwplane = &rcdu->planes.planes[i + 2];
+               plane->hwplane->zpos = 1;
+
+               ret = drm_plane_init(rcdu->ddev, &plane->plane,
+                                    (1 << rcdu->num_crtcs) - 1,
+                                    &rcar_du_plane_funcs, formats,
+                                    ARRAY_SIZE(formats), false);
+               if (ret < 0)
+                       return ret;
+
+               drm_object_attach_property(&plane->plane.base,
+                                          rcdu->planes.alpha, 255);
+               drm_object_attach_property(&plane->plane.base,
+                                          rcdu->planes.colorkey,
+                                          RCAR_DU_COLORKEY_NONE);
+               drm_object_attach_property(&plane->plane.base,
+                                          rcdu->planes.zpos, 1);
+       }
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
new file mode 100644 (file)
index 0000000..5397dba
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * rcar_du_plane.h  --  R-Car Display Unit Planes
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 __RCAR_DU_PLANE_H__
+#define __RCAR_DU_PLANE_H__
+
+struct drm_crtc;
+struct drm_framebuffer;
+struct rcar_du_device;
+struct rcar_du_format_info;
+
+/* The RCAR DU has 8 hardware planes, shared between KMS planes and CRTCs. As
+ * using KMS planes requires at least one of the CRTCs being enabled, no more
+ * than 7 KMS planes can be available. We thus create 7 KMS planes and
+ * 9 software planes (one for each KMS planes and one for each CRTC).
+ */
+
+#define RCAR_DU_NUM_KMS_PLANES         7
+#define RCAR_DU_NUM_HW_PLANES          8
+#define RCAR_DU_NUM_SW_PLANES          9
+
+struct rcar_du_plane {
+       struct rcar_du_device *dev;
+       struct drm_crtc *crtc;
+
+       bool enabled;
+
+       int hwindex;            /* 0-based, -1 means unused */
+       unsigned int alpha;
+       unsigned int colorkey;
+       unsigned int zpos;
+
+       const struct rcar_du_format_info *format;
+
+       unsigned long dma[2];
+       unsigned int pitch;
+
+       unsigned int width;
+       unsigned int height;
+
+       unsigned int src_x;
+       unsigned int src_y;
+       unsigned int dst_x;
+       unsigned int dst_y;
+};
+
+int rcar_du_plane_init(struct rcar_du_device *rcdu);
+int rcar_du_plane_register(struct rcar_du_device *rcdu);
+void rcar_du_plane_setup(struct rcar_du_plane *plane);
+void rcar_du_plane_update_base(struct rcar_du_plane *plane);
+void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
+                               struct drm_framebuffer *fb);
+int rcar_du_plane_reserve(struct rcar_du_plane *plane,
+                         const struct rcar_du_format_info *format);
+void rcar_du_plane_release(struct rcar_du_plane *plane);
+
+#endif /* __RCAR_DU_PLANE_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
new file mode 100644 (file)
index 0000000..69f21f1
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ * rcar_du_regs.h  --  R-Car Display Unit Registers Definitions
+ *
+ * Copyright (C) 2013 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 __RCAR_DU_REGS_H__
+#define __RCAR_DU_REGS_H__
+
+#define DISP2_REG_OFFSET        0x30000
+
+/* -----------------------------------------------------------------------------
+ * Display Control Registers
+ */
+
+#define DSYSR                  0x00000 /* display 1 */
+#define D2SYSR                 0x30000 /* display 2 */
+#define DSYSR_ILTS             (1 << 29)
+#define DSYSR_DSEC             (1 << 20)
+#define DSYSR_IUPD             (1 << 16)
+#define DSYSR_DRES             (1 << 9)
+#define DSYSR_DEN              (1 << 8)
+#define DSYSR_TVM_MASTER       (0 << 6)
+#define DSYSR_TVM_SWITCH       (1 << 6)
+#define DSYSR_TVM_TVSYNC       (2 << 6)
+#define DSYSR_TVM_MASK         (3 << 6)
+#define DSYSR_SCM_INT_NONE     (0 << 4)
+#define DSYSR_SCM_INT_SYNC     (2 << 4)
+#define DSYSR_SCM_INT_VIDEO    (3 << 4)
+
+#define DSMR                   0x00004
+#define D2SMR                  0x30004
+#define DSMR_VSPM              (1 << 28)
+#define DSMR_ODPM              (1 << 27)
+#define DSMR_DIPM_DISP         (0 << 25)
+#define DSMR_DIPM_CSYNC                (1 << 25)
+#define DSMR_DIPM_DE           (3 << 25)
+#define DSMR_DIPM_MASK         (3 << 25)
+#define DSMR_CSPM              (1 << 24)
+#define DSMR_DIL               (1 << 19)
+#define DSMR_VSL               (1 << 18)
+#define DSMR_HSL               (1 << 17)
+#define DSMR_DDIS              (1 << 16)
+#define DSMR_CDEL              (1 << 15)
+#define DSMR_CDEM_CDE          (0 << 13)
+#define DSMR_CDEM_LOW          (2 << 13)
+#define DSMR_CDEM_HIGH         (3 << 13)
+#define DSMR_CDEM_MASK         (3 << 13)
+#define DSMR_CDED              (1 << 12)
+#define DSMR_ODEV              (1 << 8)
+#define DSMR_CSY_VH_OR         (0 << 6)
+#define DSMR_CSY_333           (2 << 6)
+#define DSMR_CSY_222           (3 << 6)
+#define DSMR_CSY_MASK          (3 << 6)
+
+#define DSSR                   0x00008
+#define D2SSR                  0x30008
+#define DSSR_VC1FB_DSA0                (0 << 30)
+#define DSSR_VC1FB_DSA1                (1 << 30)
+#define DSSR_VC1FB_DSA2                (2 << 30)
+#define DSSR_VC1FB_INIT                (3 << 30)
+#define DSSR_VC1FB_MASK                (3 << 30)
+#define DSSR_VC0FB_DSA0                (0 << 28)
+#define DSSR_VC0FB_DSA1                (1 << 28)
+#define DSSR_VC0FB_DSA2                (2 << 28)
+#define DSSR_VC0FB_INIT                (3 << 28)
+#define DSSR_VC0FB_MASK                (3 << 28)
+#define DSSR_DFB(n)            (1 << ((n)+15))
+#define DSSR_TVR               (1 << 15)
+#define DSSR_FRM               (1 << 14)
+#define DSSR_VBK               (1 << 11)
+#define DSSR_RINT              (1 << 9)
+#define DSSR_HBK               (1 << 8)
+#define DSSR_ADC(n)            (1 << ((n)-1))
+
+#define DSRCR                  0x0000c
+#define D2SRCR                 0x3000c
+#define DSRCR_TVCL             (1 << 15)
+#define DSRCR_FRCL             (1 << 14)
+#define DSRCR_VBCL             (1 << 11)
+#define DSRCR_RICL             (1 << 9)
+#define DSRCR_HBCL             (1 << 8)
+#define DSRCR_ADCL(n)          (1 << ((n)-1))
+#define DSRCR_MASK             0x0000cbff
+
+#define DIER                   0x00010
+#define D2IER                  0x30010
+#define DIER_TVE               (1 << 15)
+#define DIER_FRE               (1 << 14)
+#define DIER_VBE               (1 << 11)
+#define DIER_RIE               (1 << 9)
+#define DIER_HBE               (1 << 8)
+#define DIER_ADCE(n)           (1 << ((n)-1))
+
+#define CPCR                   0x00014
+#define CPCR_CP4CE             (1 << 19)
+#define CPCR_CP3CE             (1 << 18)
+#define CPCR_CP2CE             (1 << 17)
+#define CPCR_CP1CE             (1 << 16)
+
+#define DPPR                   0x00018
+#define DPPR_DPE(n)            (1 << ((n)*4-1))
+#define DPPR_DPS(n, p)         (((p)-1) << DPPR_DPS_SHIFT(n))
+#define DPPR_DPS_SHIFT(n)      (((n)-1)*4)
+#define DPPR_BPP16             (DPPR_DPE(8) | DPPR_DPS(8, 1))  /* plane1 */
+#define DPPR_BPP32_P1          (DPPR_DPE(7) | DPPR_DPS(7, 1))
+#define DPPR_BPP32_P2          (DPPR_DPE(8) | DPPR_DPS(8, 2))
+#define DPPR_BPP32             (DPPR_BPP32_P1 | DPPR_BPP32_P2) /* plane1 & 2 */
+
+#define DEFR                   0x00020
+#define D2EFR                  0x30020
+#define DEFR_CODE              (0x7773 << 16)
+#define DEFR_EXSL              (1 << 12)
+#define DEFR_EXVL              (1 << 11)
+#define DEFR_EXUP              (1 << 5)
+#define DEFR_VCUP              (1 << 4)
+#define DEFR_DEFE              (1 << 0)
+
+#define DAPCR                  0x00024
+#define DAPCR_CODE             (0x7773 << 16)
+#define DAPCR_AP2E             (1 << 4)
+#define DAPCR_AP1E             (1 << 0)
+
+#define DCPCR                  0x00028
+#define DCPCR_CODE             (0x7773 << 16)
+#define DCPCR_CA2B             (1 << 13)
+#define DCPCR_CD2F             (1 << 12)
+#define DCPCR_DC2E             (1 << 8)
+#define DCPCR_CAB              (1 << 5)
+#define DCPCR_CDF              (1 << 4)
+#define DCPCR_DCE              (1 << 0)
+
+#define DEFR2                  0x00034
+#define D2EFR2                 0x30034
+#define DEFR2_CODE             (0x7775 << 16)
+#define DEFR2_DEFE2G           (1 << 0)
+
+#define DEFR3                  0x00038
+#define D2EFR3                 0x30038
+#define DEFR3_CODE             (0x7776 << 16)
+#define DEFR3_EVDA             (1 << 14)
+#define DEFR3_EVDM_1           (1 << 12)
+#define DEFR3_EVDM_2           (2 << 12)
+#define DEFR3_EVDM_3           (3 << 12)
+#define DEFR3_VMSM2_EMA                (1 << 6)
+#define DEFR3_VMSM1_ENA                (1 << 4)
+#define DEFR3_DEFE3            (1 << 0)
+
+#define DEFR4                  0x0003c
+#define D2EFR4                 0x3003c
+#define DEFR4_CODE             (0x7777 << 16)
+#define DEFR4_LRUO             (1 << 5)
+#define DEFR4_SPCE             (1 << 4)
+
+#define DVCSR                  0x000d0
+#define DVCSR_VCnFB2_DSA0(n)   (0 << ((n)*2+16))
+#define DVCSR_VCnFB2_DSA1(n)   (1 << ((n)*2+16))
+#define DVCSR_VCnFB2_DSA2(n)   (2 << ((n)*2+16))
+#define DVCSR_VCnFB2_INIT(n)   (3 << ((n)*2+16))
+#define DVCSR_VCnFB2_MASK(n)   (3 << ((n)*2+16))
+#define DVCSR_VCnFB_DSA0(n)    (0 << ((n)*2))
+#define DVCSR_VCnFB_DSA1(n)    (1 << ((n)*2))
+#define DVCSR_VCnFB_DSA2(n)    (2 << ((n)*2))
+#define DVCSR_VCnFB_INIT(n)    (3 << ((n)*2))
+#define DVCSR_VCnFB_MASK(n)    (3 << ((n)*2))
+
+#define DEFR5                  0x000e0
+#define DEFR5_CODE             (0x66 << 24)
+#define DEFR5_YCRGB2_DIS       (0 << 14)
+#define DEFR5_YCRGB2_PRI1      (1 << 14)
+#define DEFR5_YCRGB2_PRI2      (2 << 14)
+#define DEFR5_YCRGB2_PRI3      (3 << 14)
+#define DEFR5_YCRGB2_MASK      (3 << 14)
+#define DEFR5_YCRGB1_DIS       (0 << 12)
+#define DEFR5_YCRGB1_PRI1      (1 << 12)
+#define DEFR5_YCRGB1_PRI2      (2 << 12)
+#define DEFR5_YCRGB1_PRI3      (3 << 12)
+#define DEFR5_YCRGB1_MASK      (3 << 12)
+#define DEFR5_DEFE5            (1 << 0)
+
+#define DDLTR                  0x000e4
+#define DDLTR_CODE             (0x7766 << 16)
+#define DDLTR_DLAR2            (1 << 6)
+#define DDLTR_DLAY2            (1 << 5)
+#define DDLTR_DLAY1            (1 << 1)
+
+#define DEFR6                  0x000e8
+#define DEFR6_CODE             (0x7778 << 16)
+#define DEFR6_ODPM22_D2SMR     (0 << 10)
+#define DEFR6_ODPM22_DISP      (2 << 10)
+#define DEFR6_ODPM22_CDE       (3 << 10)
+#define DEFR6_ODPM22_MASK      (3 << 10)
+#define DEFR6_ODPM12_DSMR      (0 << 8)
+#define DEFR6_ODPM12_DISP      (2 << 8)
+#define DEFR6_ODPM12_CDE       (3 << 8)
+#define DEFR6_ODPM12_MASK      (3 << 8)
+#define DEFR6_TCNE2            (1 << 6)
+#define DEFR6_MLOS1            (1 << 2)
+#define DEFR6_DEFAULT          (DEFR6_CODE | DEFR6_TCNE2)
+
+/* -----------------------------------------------------------------------------
+ * Display Timing Generation Registers
+ */
+
+#define HDSR                   0x00040
+#define HDER                   0x00044
+#define VDSR                   0x00048
+#define VDER                   0x0004c
+#define HCR                    0x00050
+#define HSWR                   0x00054
+#define VCR                    0x00058
+#define VSPR                   0x0005c
+#define EQWR                   0x00060
+#define SPWR                   0x00064
+#define CLAMPSR                        0x00070
+#define CLAMPWR                        0x00074
+#define DESR                   0x00078
+#define DEWR                   0x0007c
+
+/* -----------------------------------------------------------------------------
+ * Display Attribute Registers
+ */
+
+#define CP1TR                  0x00080
+#define CP2TR                  0x00084
+#define CP3TR                  0x00088
+#define CP4TR                  0x0008c
+
+#define DOOR                   0x00090
+#define DOOR_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
+#define CDER                   0x00094
+#define CDER_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
+#define BPOR                   0x00098
+#define BPOR_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
+
+#define RINTOFSR               0x0009c
+
+#define DSHPR                  0x000c8
+#define DSHPR_CODE             (0x7776 << 16)
+#define DSHPR_PRIH             (0xa << 4)
+#define DSHPR_PRIL_BPP16       (0x8 << 0)
+#define DSHPR_PRIL_BPP32       (0x9 << 0)
+
+/* -----------------------------------------------------------------------------
+ * Display Plane Registers
+ */
+
+#define PLANE_OFF              0x00100
+
+#define PnMR                   0x00100 /* plane 1 */
+#define PnMR_VISL_VIN0         (0 << 26)       /* use Video Input 0 */
+#define PnMR_VISL_VIN1         (1 << 26)       /* use Video Input 1 */
+#define PnMR_VISL_VIN2         (2 << 26)       /* use Video Input 2 */
+#define PnMR_VISL_VIN3         (3 << 26)       /* use Video Input 3 */
+#define PnMR_YCDF_YUYV         (1 << 20)       /* YUYV format */
+#define PnMR_TC_R              (0 << 17)       /* Tranparent color is PnTC1R */
+#define PnMR_TC_CP             (1 << 17)       /* Tranparent color is color palette */
+#define PnMR_WAE               (1 << 16)       /* Wrap around Enable */
+#define PnMR_SPIM_TP           (0 << 12)       /* Transparent Color */
+#define PnMR_SPIM_ALP          (1 << 12)       /* Alpha Blending */
+#define PnMR_SPIM_EOR          (2 << 12)       /* EOR */
+#define PnMR_SPIM_TP_OFF       (1 << 14)       /* No Transparent Color */
+#define PnMR_CPSL_CP1          (0 << 8)        /* Color Palette selected 1 */
+#define PnMR_CPSL_CP2          (1 << 8)        /* Color Palette selected 2 */
+#define PnMR_CPSL_CP3          (2 << 8)        /* Color Palette selected 3 */
+#define PnMR_CPSL_CP4          (3 << 8)        /* Color Palette selected 4 */
+#define PnMR_DC                        (1 << 7)        /* Display Area Change */
+#define PnMR_BM_MD             (0 << 4)        /* Manual Display Change Mode */
+#define PnMR_BM_AR             (1 << 4)        /* Auto Rendering Mode */
+#define PnMR_BM_AD             (2 << 4)        /* Auto Display Change Mode */
+#define PnMR_BM_VC             (3 << 4)        /* Video Capture Mode */
+#define PnMR_DDDF_8BPP         (0 << 0)        /* 8bit */
+#define PnMR_DDDF_16BPP                (1 << 0)        /* 16bit or 32bit */
+#define PnMR_DDDF_ARGB         (2 << 0)        /* ARGB */
+#define PnMR_DDDF_YC           (3 << 0)        /* YC */
+#define PnMR_DDDF_MASK         (3 << 0)
+
+#define PnMWR                  0x00104
+
+#define PnALPHAR               0x00108
+#define PnALPHAR_ABIT_1                (0 << 12)
+#define PnALPHAR_ABIT_0                (1 << 12)
+#define PnALPHAR_ABIT_X                (2 << 12)
+
+#define PnDSXR                 0x00110
+#define PnDSYR                 0x00114
+#define PnDPXR                 0x00118
+#define PnDPYR                 0x0011c
+
+#define PnDSA0R                        0x00120
+#define PnDSA1R                        0x00124
+#define PnDSA2R                        0x00128
+#define PnDSA_MASK             0xfffffff0
+
+#define PnSPXR                 0x00130
+#define PnSPYR                 0x00134
+#define PnWASPR                        0x00138
+#define PnWAMWR                        0x0013c
+
+#define PnBTR                  0x00140
+
+#define PnTC1R                 0x00144
+#define PnTC2R                 0x00148
+#define PnTC3R                 0x0014c
+#define PnTC3R_CODE            (0x66 << 24)
+
+#define PnMLR                  0x00150
+
+#define PnSWAPR                        0x00180
+#define PnSWAPR_DIGN           (1 << 4)
+#define PnSWAPR_SPQW           (1 << 3)
+#define PnSWAPR_SPLW           (1 << 2)
+#define PnSWAPR_SPWD           (1 << 1)
+#define PnSWAPR_SPBY           (1 << 0)
+
+#define PnDDCR                 0x00184
+#define PnDDCR_CODE            (0x7775 << 16)
+#define PnDDCR_LRGB1           (1 << 11)
+#define PnDDCR_LRGB0           (1 << 10)
+
+#define PnDDCR2                        0x00188
+#define PnDDCR2_CODE           (0x7776 << 16)
+#define PnDDCR2_NV21           (1 << 5)
+#define PnDDCR2_Y420           (1 << 4)
+#define PnDDCR2_DIVU           (1 << 1)
+#define PnDDCR2_DIVY           (1 << 0)
+
+#define PnDDCR4                        0x00190
+#define PnDDCR4_CODE           (0x7766 << 16)
+#define PnDDCR4_SDFS_RGB       (0 << 4)
+#define PnDDCR4_SDFS_YC                (5 << 4)
+#define PnDDCR4_SDFS_MASK      (7 << 4)
+#define PnDDCR4_EDF_NONE       (0 << 0)
+#define PnDDCR4_EDF_ARGB8888   (1 << 0)
+#define PnDDCR4_EDF_RGB888     (2 << 0)
+#define PnDDCR4_EDF_RGB666     (3 << 0)
+#define PnDDCR4_EDF_MASK       (7 << 0)
+
+#define APnMR                  0x0a100
+#define APnMR_WAE              (1 << 16)       /* Wrap around Enable */
+#define APnMR_DC               (1 << 7)        /* Display Area Change */
+#define APnMR_BM_MD            (0 << 4)        /* Manual Display Change Mode */
+#define APnMR_BM_AD            (2 << 4)        /* Auto Display Change Mode */
+
+#define APnMWR                 0x0a104
+#define APnDSA0R               0x0a120
+#define APnDSA1R               0x0a124
+#define APnDSA2R               0x0a128
+#define APnMLR                 0x0a150
+
+/* -----------------------------------------------------------------------------
+ * Display Capture Registers
+ */
+
+#define DCMWR                  0x0c104
+#define DC2MWR                 0x0c204
+#define DCSAR                  0x0c120
+#define DC2SAR                 0x0c220
+#define DCMLR                  0x0c150
+#define DC2MLR                 0x0c250
+
+/* -----------------------------------------------------------------------------
+ * Color Palette Registers
+ */
+
+#define CP1_000R               0x01000
+#define CP1_255R               0x013fc
+#define CP2_000R               0x02000
+#define CP2_255R               0x023fc
+#define CP3_000R               0x03000
+#define CP3_255R               0x033fc
+#define CP4_000R               0x04000
+#define CP4_255R               0x043fc
+
+/* -----------------------------------------------------------------------------
+ * External Synchronization Control Registers
+ */
+
+#define ESCR                   0x10000
+#define ESCR2                  0x31000
+#define ESCR_DCLKOINV          (1 << 25)
+#define ESCR_DCLKSEL_DCLKIN    (0 << 20)
+#define ESCR_DCLKSEL_CLKS      (1 << 20)
+#define ESCR_DCLKSEL_MASK      (1 << 20)
+#define ESCR_DCLKDIS           (1 << 16)
+#define ESCR_SYNCSEL_OFF       (0 << 8)
+#define ESCR_SYNCSEL_EXVSYNC   (2 << 8)
+#define ESCR_SYNCSEL_EXHSYNC   (3 << 8)
+#define ESCR_FRQSEL_MASK       (0x3f << 0)
+
+#define OTAR                   0x10004
+#define OTAR2                  0x31004
+
+/* -----------------------------------------------------------------------------
+ * Dual Display Output Control Registers
+ */
+
+#define DORCR                  0x11000
+#define DORCR_PG2T             (1 << 30)
+#define DORCR_DK2S             (1 << 28)
+#define DORCR_PG2D_DS1         (0 << 24)
+#define DORCR_PG2D_DS2         (1 << 24)
+#define DORCR_PG2D_FIX0                (2 << 24)
+#define DORCR_PG2D_DOOR                (3 << 24)
+#define DORCR_PG2D_MASK                (3 << 24)
+#define DORCR_DR1D             (1 << 21)
+#define DORCR_PG1D_DS1         (0 << 16)
+#define DORCR_PG1D_DS2         (1 << 16)
+#define DORCR_PG1D_FIX0                (2 << 16)
+#define DORCR_PG1D_DOOR                (3 << 16)
+#define DORCR_PG1D_MASK                (3 << 16)
+#define DORCR_RGPV             (1 << 4)
+#define DORCR_DPRS             (1 << 0)
+
+#define DPTSR                  0x11004
+#define DPTSR_PnDK(n)          (1 << ((n) + 16))
+#define DPTSR_PnTS(n)          (1 << (n))
+
+#define DAPTSR                 0x11008
+#define DAPTSR_APnDK(n)                (1 << ((n) + 16))
+#define DAPTSR_APnTS(n)                (1 << (n))
+
+#define DS1PR                  0x11020
+#define DS2PR                  0x11024
+
+/* -----------------------------------------------------------------------------
+ * YC-RGB Conversion Coefficient Registers
+ */
+
+#define YNCR                   0x11080
+#define YNOR                   0x11084
+#define CRNOR                  0x11088
+#define CBNOR                  0x1108c
+#define RCRCR                  0x11090
+#define GCRCR                  0x11094
+#define GCBCR                  0x11098
+#define BCBCR                  0x1109c
+
+#endif /* __RCAR_DU_REGS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.c b/drivers/gpu/drm/rcar-du/rcar_du_vga.c
new file mode 100644 (file)
index 0000000..327289e
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * rcar_du_vga.c  --  R-Car Display Unit VGA DAC and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_vga.h"
+
+/* -----------------------------------------------------------------------------
+ * Connector
+ */
+
+static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
+{
+       return 0;
+}
+
+static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector,
+                                           struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+       .get_modes = rcar_du_vga_connector_get_modes,
+       .mode_valid = rcar_du_vga_connector_mode_valid,
+       .best_encoder = rcar_du_connector_best_encoder,
+};
+
+static void rcar_du_vga_connector_destroy(struct drm_connector *connector)
+{
+       drm_sysfs_connector_remove(connector);
+       drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
+{
+       return connector_status_unknown;
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .detect = rcar_du_vga_connector_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = rcar_du_vga_connector_destroy,
+};
+
+static int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
+                                     struct rcar_du_encoder *renc)
+{
+       struct rcar_du_connector *rcon;
+       struct drm_connector *connector;
+       int ret;
+
+       rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
+       if (rcon == NULL)
+               return -ENOMEM;
+
+       connector = &rcon->connector;
+       connector->display_info.width_mm = 0;
+       connector->display_info.height_mm = 0;
+
+       ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
+                                DRM_MODE_CONNECTOR_VGA);
+       if (ret < 0)
+               return ret;
+
+       drm_connector_helper_add(connector, &connector_helper_funcs);
+       ret = drm_sysfs_connector_add(connector);
+       if (ret < 0)
+               return ret;
+
+       drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+       drm_object_property_set_value(&connector->base,
+               rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+       ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
+       if (ret < 0)
+               return ret;
+
+       connector->encoder = &renc->encoder;
+       rcon->encoder = renc;
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Encoder
+ */
+
+static void rcar_du_vga_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool rcar_du_vga_encoder_mode_fixup(struct drm_encoder *encoder,
+                                          const struct drm_display_mode *mode,
+                                          struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+       .dpms = rcar_du_vga_encoder_dpms,
+       .mode_fixup = rcar_du_vga_encoder_mode_fixup,
+       .prepare = rcar_du_encoder_mode_prepare,
+       .commit = rcar_du_encoder_mode_commit,
+       .mode_set = rcar_du_encoder_mode_set,
+};
+
+static const struct drm_encoder_funcs encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+};
+
+int rcar_du_vga_init(struct rcar_du_device *rcdu,
+                    const struct rcar_du_encoder_vga_data *data,
+                    unsigned int output)
+{
+       struct rcar_du_encoder *renc;
+       int ret;
+
+       renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
+       if (renc == NULL)
+               return -ENOMEM;
+
+       renc->output = output;
+
+       ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
+                              DRM_MODE_ENCODER_DAC);
+       if (ret < 0)
+               return ret;
+
+       drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
+
+       return rcar_du_vga_connector_init(rcdu, renc);
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.h b/drivers/gpu/drm/rcar-du/rcar_du_vga.h
new file mode 100644 (file)
index 0000000..66b4d2d
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * rcar_du_vga.h  --  R-Car Display Unit VGA DAC and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 __RCAR_DU_VGA_H__
+#define __RCAR_DU_VGA_H__
+
+struct rcar_du_device;
+struct rcar_du_encoder_vga_data;
+
+int rcar_du_vga_init(struct rcar_du_device *rcdu,
+                    const struct rcar_du_encoder_vga_data *data,
+                    unsigned int output);
+
+#endif /* __RCAR_DU_VGA_H__ */
index b55c1d6..bd6b2cf 100644 (file)
@@ -570,9 +570,6 @@ int savage_driver_firstopen(struct drm_device *dev)
        unsigned int fb_rsrc, aper_rsrc;
        int ret = 0;
 
-       dev_priv->mtrr[0].handle = -1;
-       dev_priv->mtrr[1].handle = -1;
-       dev_priv->mtrr[2].handle = -1;
        if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
                fb_rsrc = 0;
                fb_base = pci_resource_start(dev->pdev, 0);
@@ -584,21 +581,14 @@ int savage_driver_firstopen(struct drm_device *dev)
                if (pci_resource_len(dev->pdev, 0) == 0x08000000) {
                        /* Don't make MMIO write-cobining! We need 3
                         * MTRRs. */
-                       dev_priv->mtrr[0].base = fb_base;
-                       dev_priv->mtrr[0].size = 0x01000000;
-                       dev_priv->mtrr[0].handle =
-                           drm_mtrr_add(dev_priv->mtrr[0].base,
-                                        dev_priv->mtrr[0].size, DRM_MTRR_WC);
-                       dev_priv->mtrr[1].base = fb_base + 0x02000000;
-                       dev_priv->mtrr[1].size = 0x02000000;
-                       dev_priv->mtrr[1].handle =
-                           drm_mtrr_add(dev_priv->mtrr[1].base,
-                                        dev_priv->mtrr[1].size, DRM_MTRR_WC);
-                       dev_priv->mtrr[2].base = fb_base + 0x04000000;
-                       dev_priv->mtrr[2].size = 0x04000000;
-                       dev_priv->mtrr[2].handle =
-                           drm_mtrr_add(dev_priv->mtrr[2].base,
-                                        dev_priv->mtrr[2].size, DRM_MTRR_WC);
+                       dev_priv->mtrr_handles[0] =
+                               arch_phys_wc_add(fb_base, 0x01000000);
+                       dev_priv->mtrr_handles[1] =
+                               arch_phys_wc_add(fb_base + 0x02000000,
+                                                0x02000000);
+                       dev_priv->mtrr_handles[2] =
+                               arch_phys_wc_add(fb_base + 0x04000000,
+                                               0x04000000);
                } else {
                        DRM_ERROR("strange pci_resource_len %08llx\n",
                                  (unsigned long long)
@@ -616,11 +606,9 @@ int savage_driver_firstopen(struct drm_device *dev)
                if (pci_resource_len(dev->pdev, 1) == 0x08000000) {
                        /* Can use one MTRR to cover both fb and
                         * aperture. */
-                       dev_priv->mtrr[0].base = fb_base;
-                       dev_priv->mtrr[0].size = 0x08000000;
-                       dev_priv->mtrr[0].handle =
-                           drm_mtrr_add(dev_priv->mtrr[0].base,
-                                        dev_priv->mtrr[0].size, DRM_MTRR_WC);
+                       dev_priv->mtrr_handles[0] =
+                               arch_phys_wc_add(fb_base,
+                                                0x08000000);
                } else {
                        DRM_ERROR("strange pci_resource_len %08llx\n",
                                  (unsigned long long)
@@ -660,11 +648,10 @@ void savage_driver_lastclose(struct drm_device *dev)
        drm_savage_private_t *dev_priv = dev->dev_private;
        int i;
 
-       for (i = 0; i < 3; ++i)
-               if (dev_priv->mtrr[i].handle >= 0)
-                       drm_mtrr_del(dev_priv->mtrr[i].handle,
-                                dev_priv->mtrr[i].base,
-                                dev_priv->mtrr[i].size, DRM_MTRR_WC);
+       for (i = 0; i < 3; ++i) {
+               arch_phys_wc_del(dev_priv->mtrr_handles[i]);
+               dev_priv->mtrr_handles[i] = 0;
+       }
 }
 
 int savage_driver_unload(struct drm_device *dev)
index df2aac6..c05082a 100644 (file)
@@ -160,10 +160,7 @@ typedef struct drm_savage_private {
        drm_local_map_t *cmd_dma;
        drm_local_map_t fake_dma;
 
-       struct {
-               int handle;
-               unsigned long base, size;
-       } mtrr[3];
+       int mtrr_handles[3];
 
        /* BCI and status-related stuff */
        volatile uint32_t *status_ptr, *bci_ptr;
index 7e7d52b..ca498d1 100644 (file)
@@ -1,6 +1,6 @@
 config DRM_SHMOBILE
        tristate "DRM Support for SH Mobile"
-       depends on DRM && (SUPERH || ARCH_SHMOBILE)
+       depends on DRM && (ARM || SUPERH)
        select DRM_KMS_HELPER
        select DRM_KMS_CMA_HELPER
        select DRM_GEM_CMA_HELPER
index f6e0b53..edc1018 100644 (file)
@@ -90,7 +90,7 @@ static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev,
                return -EINVAL;
        }
 
-       clk = clk_get(sdev->dev, clkname);
+       clk = devm_clk_get(sdev->dev, clkname);
        if (IS_ERR(clk)) {
                dev_err(sdev->dev, "cannot get dot clock %s\n", clkname);
                return PTR_ERR(clk);
@@ -106,21 +106,12 @@ static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev,
 
 static int shmob_drm_unload(struct drm_device *dev)
 {
-       struct shmob_drm_device *sdev = dev->dev_private;
-
        drm_kms_helper_poll_fini(dev);
        drm_mode_config_cleanup(dev);
        drm_vblank_cleanup(dev);
        drm_irq_uninstall(dev);
 
-       if (sdev->clock)
-               clk_put(sdev->clock);
-
-       if (sdev->mmio)
-               iounmap(sdev->mmio);
-
        dev->dev_private = NULL;
-       kfree(sdev);
 
        return 0;
 }
@@ -139,7 +130,7 @@ static int shmob_drm_load(struct drm_device *dev, unsigned long flags)
                return -EINVAL;
        }
 
-       sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
+       sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
        if (sdev == NULL) {
                dev_err(dev->dev, "failed to allocate private data\n");
                return -ENOMEM;
@@ -156,29 +147,28 @@ static int shmob_drm_load(struct drm_device *dev, unsigned long flags)
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL) {
                dev_err(&pdev->dev, "failed to get memory resource\n");
-               ret = -EINVAL;
-               goto done;
+               return -EINVAL;
        }
 
-       sdev->mmio = ioremap_nocache(res->start, resource_size(res));
+       sdev->mmio = devm_ioremap_nocache(&pdev->dev, res->start,
+                                         resource_size(res));
        if (sdev->mmio == NULL) {
                dev_err(&pdev->dev, "failed to remap memory resource\n");
-               ret = -ENOMEM;
-               goto done;
+               return -ENOMEM;
        }
 
        ret = shmob_drm_setup_clocks(sdev, pdata->clk_source);
        if (ret < 0)
-               goto done;
+               return ret;
 
        ret = shmob_drm_init_interface(sdev);
        if (ret < 0)
-               goto done;
+               return ret;
 
        ret = shmob_drm_modeset_init(sdev);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to initialize mode setting\n");
-               goto done;
+               return ret;
        }
 
        for (i = 0; i < 4; ++i) {
@@ -273,7 +263,8 @@ static const struct file_operations shmob_drm_fops = {
 };
 
 static struct drm_driver shmob_drm_driver = {
-       .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
+       .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
+                               | DRIVER_PRIME,
        .load                   = shmob_drm_load,
        .unload                 = shmob_drm_unload,
        .preclose               = shmob_drm_preclose,
@@ -283,6 +274,10 @@ static struct drm_driver shmob_drm_driver = {
        .disable_vblank         = shmob_drm_disable_vblank,
        .gem_free_object        = drm_gem_cma_free_object,
        .gem_vm_ops             = &drm_gem_cma_vm_ops,
+       .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
+       .gem_prime_import       = drm_gem_cma_dmabuf_import,
+       .gem_prime_export       = drm_gem_cma_dmabuf_export,
        .dumb_create            = drm_gem_cma_dumb_create,
        .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
        .dumb_destroy           = drm_gem_cma_dumb_destroy,
index c291ee3..fc0ef0c 100644 (file)
@@ -116,7 +116,7 @@ shmob_drm_fb_create(struct drm_device *dev, struct drm_file *file_priv,
        }
 
        if (mode_cmd->pitches[0] & 7 || mode_cmd->pitches[0] >= 65536) {
-               dev_dbg(dev->dev, "valid pitch value %u\n",
+               dev_dbg(dev->dev, "invalid pitch value %u\n",
                        mode_cmd->pitches[0]);
                return ERR_PTR(-EINVAL);
        }
index e1eb899..060ae03 100644 (file)
@@ -166,7 +166,7 @@ void shmob_drm_plane_setup(struct drm_plane *plane)
 {
        struct shmob_drm_plane *splane = to_shmob_plane(plane);
 
-       if (plane->fb == NULL || !plane->enabled)
+       if (plane->fb == NULL)
                return;
 
        __shmob_drm_plane_setup(splane, plane->fb);
@@ -221,11 +221,8 @@ static int shmob_drm_plane_disable(struct drm_plane *plane)
 
 static void shmob_drm_plane_destroy(struct drm_plane *plane)
 {
-       struct shmob_drm_plane *splane = to_shmob_plane(plane);
-
        shmob_drm_plane_disable(plane);
        drm_plane_cleanup(plane);
-       kfree(splane);
 }
 
 static const struct drm_plane_funcs shmob_drm_plane_funcs = {
@@ -251,7 +248,7 @@ int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index)
        struct shmob_drm_plane *splane;
        int ret;
 
-       splane = kzalloc(sizeof(*splane), GFP_KERNEL);
+       splane = devm_kzalloc(sdev->dev, sizeof(*splane), GFP_KERNEL);
        if (splane == NULL)
                return -ENOMEM;
 
@@ -261,8 +258,6 @@ int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index)
        ret = drm_plane_init(sdev->ddev, &splane->plane, 1,
                             &shmob_drm_plane_funcs, formats,
                             ARRAY_SIZE(formats), false);
-       if (ret < 0)
-               kfree(splane);
 
        return ret;
 }
index 5dd3c7d..7418dcd 100644 (file)
@@ -42,7 +42,8 @@ struct tilcdc_crtc {
 
 static void unref_worker(struct work_struct *work)
 {
-       struct tilcdc_crtc *tilcdc_crtc = container_of(work, struct tilcdc_crtc, work);
+       struct tilcdc_crtc *tilcdc_crtc =
+               container_of(work, struct tilcdc_crtc, work);
        struct drm_device *dev = tilcdc_crtc->base.dev;
        struct drm_framebuffer *fb;
 
@@ -55,10 +56,12 @@ static void unref_worker(struct work_struct *work)
 static void set_scanout(struct drm_crtc *crtc, int n)
 {
        static const uint32_t base_reg[] = {
-                       LCDC_DMA_FB_BASE_ADDR_0_REG, LCDC_DMA_FB_BASE_ADDR_1_REG,
+                       LCDC_DMA_FB_BASE_ADDR_0_REG,
+                       LCDC_DMA_FB_BASE_ADDR_1_REG,
        };
        static const uint32_t ceil_reg[] = {
-                       LCDC_DMA_FB_CEILING_ADDR_0_REG, LCDC_DMA_FB_CEILING_ADDR_1_REG,
+                       LCDC_DMA_FB_CEILING_ADDR_0_REG,
+                       LCDC_DMA_FB_CEILING_ADDR_1_REG,
        };
        static const uint32_t stat[] = {
                        LCDC_END_OF_FRAME0, LCDC_END_OF_FRAME1,
@@ -194,7 +197,8 @@ static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode)
                tilcdc_crtc->frame_done = false;
                stop(crtc);
 
-               /* if necessary wait for framedone irq which will still come
+               /*
+                * if necessary wait for framedone irq which will still come
                 * before putting things to sleep..
                 */
                if (priv->rev == 2) {
@@ -289,17 +293,24 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
        reg = tilcdc_read(dev, LCDC_RASTER_TIMING_2_REG) & ~0x000fff00;
        reg |= LCDC_AC_BIAS_FREQUENCY(info->ac_bias) |
                LCDC_AC_BIAS_TRANSITIONS_PER_INT(info->ac_bias_intrpt);
+
+       /*
+        * subtract one from hfp, hbp, hsw because the hardware uses
+        * a value of 0 as 1
+        */
        if (priv->rev == 2) {
-               reg |= (hfp & 0x300) >> 8;
-               reg |= (hbp & 0x300) >> 4;
-               reg |= (hsw & 0x3c0) << 21;
+               /* clear bits we're going to set */
+               reg &= ~0x78000033;
+               reg |= ((hfp-1) & 0x300) >> 8;
+               reg |= ((hbp-1) & 0x300) >> 4;
+               reg |= ((hsw-1) & 0x3c0) << 21;
        }
        tilcdc_write(dev, LCDC_RASTER_TIMING_2_REG, reg);
 
        reg = (((mode->hdisplay >> 4) - 1) << 4) |
-               ((hbp & 0xff) << 24) |
-               ((hfp & 0xff) << 16) |
-               ((hsw & 0x3f) << 10);
+               (((hbp-1) & 0xff) << 24) |
+               (((hfp-1) & 0xff) << 16) |
+               (((hsw-1) & 0x3f) << 10);
        if (priv->rev == 2)
                reg |= (((mode->hdisplay >> 4) - 1) & 0x40) >> 3;
        tilcdc_write(dev, LCDC_RASTER_TIMING_0_REG, reg);
@@ -307,9 +318,24 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
        reg = ((mode->vdisplay - 1) & 0x3ff) |
                ((vbp & 0xff) << 24) |
                ((vfp & 0xff) << 16) |
-               ((vsw & 0x3f) << 10);
+               (((vsw-1) & 0x3f) << 10);
        tilcdc_write(dev, LCDC_RASTER_TIMING_1_REG, reg);
 
+       /*
+        * be sure to set Bit 10 for the V2 LCDC controller,
+        * otherwise limited to 1024 pixels width, stopping
+        * 1920x1080 being suppoted.
+        */
+       if (priv->rev == 2) {
+               if ((mode->vdisplay - 1) & 0x400) {
+                       tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG,
+                               LCDC_LPP_B10);
+               } else {
+                       tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG,
+                               LCDC_LPP_B10);
+               }
+       }
+
        /* Configure display type: */
        reg = tilcdc_read(dev, LCDC_RASTER_CTRL_REG) &
                ~(LCDC_TFT_MODE | LCDC_MONO_8BIT_MODE | LCDC_MONOCHROME_MODE |
@@ -384,10 +410,6 @@ static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
        return 0;
 }
 
-static void tilcdc_crtc_load_lut(struct drm_crtc *crtc)
-{
-}
-
 static const struct drm_crtc_funcs tilcdc_crtc_funcs = {
                .destroy        = tilcdc_crtc_destroy,
                .set_config     = drm_crtc_helper_set_config,
@@ -401,7 +423,6 @@ static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = {
                .commit         = tilcdc_crtc_commit,
                .mode_set       = tilcdc_crtc_mode_set,
                .mode_set_base  = tilcdc_crtc_mode_set_base,
-               .load_lut       = tilcdc_crtc_load_lut,
 };
 
 int tilcdc_crtc_max_width(struct drm_crtc *crtc)
@@ -422,7 +443,12 @@ int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode)
 {
        struct tilcdc_drm_private *priv = crtc->dev->dev_private;
        unsigned int bandwidth;
+       uint32_t hbp, hfp, hsw, vbp, vfp, vsw;
 
+       /*
+        * check to see if the width is within the range that
+        * the LCD Controller physically supports
+        */
        if (mode->hdisplay > tilcdc_crtc_max_width(crtc))
                return MODE_VIRTUAL_X;
 
@@ -433,10 +459,70 @@ int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode)
        if (mode->vdisplay > 2048)
                return MODE_VIRTUAL_Y;
 
+       DBG("Processing mode %dx%d@%d with pixel clock %d",
+               mode->hdisplay, mode->vdisplay,
+               drm_mode_vrefresh(mode), mode->clock);
+
+       hbp = mode->htotal - mode->hsync_end;
+       hfp = mode->hsync_start - mode->hdisplay;
+       hsw = mode->hsync_end - mode->hsync_start;
+       vbp = mode->vtotal - mode->vsync_end;
+       vfp = mode->vsync_start - mode->vdisplay;
+       vsw = mode->vsync_end - mode->vsync_start;
+
+       if ((hbp-1) & ~0x3ff) {
+               DBG("Pruning mode: Horizontal Back Porch out of range");
+               return MODE_HBLANK_WIDE;
+       }
+
+       if ((hfp-1) & ~0x3ff) {
+               DBG("Pruning mode: Horizontal Front Porch out of range");
+               return MODE_HBLANK_WIDE;
+       }
+
+       if ((hsw-1) & ~0x3ff) {
+               DBG("Pruning mode: Horizontal Sync Width out of range");
+               return MODE_HSYNC_WIDE;
+       }
+
+       if (vbp & ~0xff) {
+               DBG("Pruning mode: Vertical Back Porch out of range");
+               return MODE_VBLANK_WIDE;
+       }
+
+       if (vfp & ~0xff) {
+               DBG("Pruning mode: Vertical Front Porch out of range");
+               return MODE_VBLANK_WIDE;
+       }
+
+       if ((vsw-1) & ~0x3f) {
+               DBG("Pruning mode: Vertical Sync Width out of range");
+               return MODE_VSYNC_WIDE;
+       }
+
+       /*
+        * some devices have a maximum allowed pixel clock
+        * configured from the DT
+        */
+       if (mode->clock > priv->max_pixelclock) {
+               DBG("Pruning mode: pixel clock too high");
+               return MODE_CLOCK_HIGH;
+       }
+
+       /*
+        * some devices further limit the max horizontal resolution
+        * configured from the DT
+        */
+       if (mode->hdisplay > priv->max_width)
+               return MODE_BAD_WIDTH;
+
        /* filter out modes that would require too much memory bandwidth: */
-       bandwidth = mode->hdisplay * mode->vdisplay * drm_mode_vrefresh(mode);
-       if (bandwidth > priv->max_bandwidth)
+       bandwidth = mode->hdisplay * mode->vdisplay *
+               drm_mode_vrefresh(mode);
+       if (bandwidth > priv->max_bandwidth) {
+               DBG("Pruning mode: exceeds defined bandwidth limit");
                return MODE_BAD;
+       }
 
        return MODE_OK;
 }
index 2b5461b..40b71da 100644 (file)
@@ -26,6 +26,7 @@
 #include "drm_fb_helper.h"
 
 static LIST_HEAD(module_list);
+static bool slave_probing;
 
 void tilcdc_module_init(struct tilcdc_module *mod, const char *name,
                const struct tilcdc_module_ops *funcs)
@@ -41,6 +42,11 @@ void tilcdc_module_cleanup(struct tilcdc_module *mod)
        list_del(&mod->list);
 }
 
+void tilcdc_slave_probedefer(bool defered)
+{
+       slave_probing = defered;
+}
+
 static struct of_device_id tilcdc_of_match[];
 
 static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev,
@@ -157,7 +163,9 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
        struct platform_device *pdev = dev->platformdev;
        struct device_node *node = pdev->dev.of_node;
        struct tilcdc_drm_private *priv;
+       struct tilcdc_module *mod;
        struct resource *res;
+       u32 bpp = 0;
        int ret;
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -210,7 +218,20 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
 #endif
 
        if (of_property_read_u32(node, "max-bandwidth", &priv->max_bandwidth))
-               priv->max_bandwidth = 1280 * 1024 * 60;
+               priv->max_bandwidth = TILCDC_DEFAULT_MAX_BANDWIDTH;
+
+       DBG("Maximum Bandwidth Value %d", priv->max_bandwidth);
+
+       if (of_property_read_u32(node, "ti,max-width", &priv->max_width))
+               priv->max_width = TILCDC_DEFAULT_MAX_WIDTH;
+
+       DBG("Maximum Horizontal Pixel Width Value %dpixels", priv->max_width);
+
+       if (of_property_read_u32(node, "ti,max-pixelclock",
+                                       &priv->max_pixelclock))
+               priv->max_pixelclock = TILCDC_DEFAULT_MAX_PIXELCLOCK;
+
+       DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock);
 
        pm_runtime_enable(dev->dev);
 
@@ -256,7 +277,15 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
 
        platform_set_drvdata(pdev, dev);
 
-       priv->fbdev = drm_fbdev_cma_init(dev, 16,
+
+       list_for_each_entry(mod, &module_list, list) {
+               DBG("%s: preferred_bpp: %d", mod->name, mod->preferred_bpp);
+               bpp = mod->preferred_bpp;
+               if (bpp > 0)
+                       break;
+       }
+
+       priv->fbdev = drm_fbdev_cma_init(dev, bpp,
                        dev->mode_config.num_crtc,
                        dev->mode_config.num_connector);
 
@@ -557,6 +586,10 @@ static int tilcdc_pdev_probe(struct platform_device *pdev)
                return -ENXIO;
        }
 
+       /* defer probing if slave is in deferred probing */
+       if (slave_probing == true)
+               return -EPROBE_DEFER;
+
        return drm_platform_init(&tilcdc_driver, pdev);
 }
 
index 8242b5a..0938036 100644 (file)
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 
+/* Defaulting to pixel clock defined on AM335x */
+#define TILCDC_DEFAULT_MAX_PIXELCLOCK  126000
+/* Defaulting to max width as defined on AM335x */
+#define TILCDC_DEFAULT_MAX_WIDTH  2048
+/*
+ * This may need some tweaking, but want to allow at least 1280x1024@60
+ * with optimized DDR & EMIF settings tweaked 1920x1080@24 appears to
+ * be supportable
+ */
+#define TILCDC_DEFAULT_MAX_BANDWIDTH  (1280*1024*60)
+
+
 struct tilcdc_drm_private {
        void __iomem *mmio;
 
@@ -43,6 +55,16 @@ struct tilcdc_drm_private {
 
        /* don't attempt resolutions w/ higher W * H * Hz: */
        uint32_t max_bandwidth;
+       /*
+        * Pixel Clock will be restricted to some value as
+        * defined in the device datasheet measured in KHz
+        */
+       uint32_t max_pixelclock;
+       /*
+        * Max allowable width is limited on a per device basis
+        * measured in pixels
+        */
+       uint32_t max_width;
 
        /* register contents saved across suspend/resume: */
        u32 saved_register[12];
@@ -89,12 +111,13 @@ struct tilcdc_module {
        const char *name;
        struct list_head list;
        const struct tilcdc_module_ops *funcs;
+       unsigned int preferred_bpp;
 };
 
 void tilcdc_module_init(struct tilcdc_module *mod, const char *name,
                const struct tilcdc_module_ops *funcs);
 void tilcdc_module_cleanup(struct tilcdc_module *mod);
-
+void tilcdc_slave_probedefer(bool defered);
 
 /* Panel config that needs to be set in the crtc, but is not coming from
  * the mode timings.  The display module is expected to call
index 0917665..86c6732 100644 (file)
@@ -393,6 +393,8 @@ static int panel_probe(struct platform_device *pdev)
                goto fail;
        }
 
+       mod->preferred_bpp = panel_mod->info->bpp;
+
        panel_mod->backlight = of_find_backlight_by_node(node);
        if (panel_mod->backlight)
                dev_info(&pdev->dev, "found backlight\n");
index 17fd1b4..1bf5e25 100644 (file)
@@ -80,6 +80,7 @@
 #define LCDC_INVERT_PIXEL_CLOCK                  BIT(22)
 #define LCDC_INVERT_HSYNC                        BIT(21)
 #define LCDC_INVERT_VSYNC                        BIT(20)
+#define LCDC_LPP_B10                             BIT(26)
 
 /* LCDC Block */
 #define LCDC_PID_REG                             0x0
index db1d2fc..dfffaf0 100644 (file)
@@ -298,6 +298,7 @@ static int slave_probe(struct platform_device *pdev)
        struct tilcdc_module *mod;
        struct pinctrl *pinctrl;
        uint32_t i2c_phandle;
+       struct i2c_adapter *slavei2c;
        int ret = -EINVAL;
 
        /* bail out early if no DT data: */
@@ -306,42 +307,48 @@ static int slave_probe(struct platform_device *pdev)
                return -ENXIO;
        }
 
-       slave_mod = kzalloc(sizeof(*slave_mod), GFP_KERNEL);
-       if (!slave_mod)
-               return -ENOMEM;
-
-       mod = &slave_mod->base;
-
-       tilcdc_module_init(mod, "slave", &slave_module_ops);
-
-       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
-       if (IS_ERR(pinctrl))
-               dev_warn(&pdev->dev, "pins are not configured\n");
-
+       /* Bail out early if i2c not specified */
        if (of_property_read_u32(node, "i2c", &i2c_phandle)) {
                dev_err(&pdev->dev, "could not get i2c bus phandle\n");
-               goto fail;
+               return ret;
        }
 
        i2c_node = of_find_node_by_phandle(i2c_phandle);
        if (!i2c_node) {
                dev_err(&pdev->dev, "could not get i2c bus node\n");
-               goto fail;
+               return ret;
        }
 
-       slave_mod->i2c = of_find_i2c_adapter_by_node(i2c_node);
-       if (!slave_mod->i2c) {
+       /* but defer the probe if it can't be initialized it might come later */
+       slavei2c = of_find_i2c_adapter_by_node(i2c_node);
+       of_node_put(i2c_node);
+
+       if (!slavei2c) {
+               ret = -EPROBE_DEFER;
+               tilcdc_slave_probedefer(true);
                dev_err(&pdev->dev, "could not get i2c\n");
-               goto fail;
+               return ret;
        }
 
-       of_node_put(i2c_node);
+       slave_mod = kzalloc(sizeof(*slave_mod), GFP_KERNEL);
+       if (!slave_mod)
+               return -ENOMEM;
 
-       return 0;
+       mod = &slave_mod->base;
 
-fail:
-       slave_destroy(mod);
-       return ret;
+       mod->preferred_bpp = slave_info.bpp;
+
+       slave_mod->i2c = slavei2c;
+
+       tilcdc_module_init(mod, "slave", &slave_module_ops);
+
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl))
+               dev_warn(&pdev->dev, "pins are not configured\n");
+
+       tilcdc_slave_probedefer(false);
+
+       return 0;
 }
 
 static int slave_remove(struct platform_device *pdev)
index a36788f..925c7cd 100644 (file)
@@ -354,6 +354,8 @@ static int tfp410_probe(struct platform_device *pdev)
                goto fail;
        }
 
+       mod->preferred_bpp = dvi_info.bpp;
+
        i2c_node = of_find_node_by_phandle(i2c_phandle);
        if (!i2c_node) {
                dev_err(&pdev->dev, "could not get i2c bus node\n");
index 9b07b7d..cb9dd67 100644 (file)
@@ -150,6 +150,9 @@ static void ttm_bo_release_list(struct kref *list_kref)
        if (bo->ttm)
                ttm_tt_destroy(bo->ttm);
        atomic_dec(&bo->glob->bo_count);
+       if (bo->resv == &bo->ttm_resv)
+               reservation_object_fini(&bo->ttm_resv);
+
        if (bo->destroy)
                bo->destroy(bo);
        else {
@@ -158,24 +161,12 @@ static void ttm_bo_release_list(struct kref *list_kref)
        ttm_mem_global_free(bdev->glob->mem_glob, acc_size);
 }
 
-static int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo,
-                                 bool interruptible)
-{
-       if (interruptible) {
-               return wait_event_interruptible(bo->event_queue,
-                                              !ttm_bo_is_reserved(bo));
-       } else {
-               wait_event(bo->event_queue, !ttm_bo_is_reserved(bo));
-               return 0;
-       }
-}
-
 void ttm_bo_add_to_lru(struct ttm_buffer_object *bo)
 {
        struct ttm_bo_device *bdev = bo->bdev;
        struct ttm_mem_type_manager *man;
 
-       BUG_ON(!ttm_bo_is_reserved(bo));
+       lockdep_assert_held(&bo->resv->lock.base);
 
        if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) {
 
@@ -191,6 +182,7 @@ void ttm_bo_add_to_lru(struct ttm_buffer_object *bo)
                }
        }
 }
+EXPORT_SYMBOL(ttm_bo_add_to_lru);
 
 int ttm_bo_del_from_lru(struct ttm_buffer_object *bo)
 {
@@ -213,71 +205,6 @@ int ttm_bo_del_from_lru(struct ttm_buffer_object *bo)
        return put_count;
 }
 
-int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo,
-                         bool interruptible,
-                         bool no_wait, bool use_sequence, uint32_t sequence)
-{
-       int ret;
-
-       while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) {
-               /**
-                * Deadlock avoidance for multi-bo reserving.
-                */
-               if (use_sequence && bo->seq_valid) {
-                       /**
-                        * We've already reserved this one.
-                        */
-                       if (unlikely(sequence == bo->val_seq))
-                               return -EDEADLK;
-                       /**
-                        * Already reserved by a thread that will not back
-                        * off for us. We need to back off.
-                        */
-                       if (unlikely(sequence - bo->val_seq < (1 << 31)))
-                               return -EAGAIN;
-               }
-
-               if (no_wait)
-                       return -EBUSY;
-
-               ret = ttm_bo_wait_unreserved(bo, interruptible);
-
-               if (unlikely(ret))
-                       return ret;
-       }
-
-       if (use_sequence) {
-               bool wake_up = false;
-               /**
-                * Wake up waiters that may need to recheck for deadlock,
-                * if we decreased the sequence number.
-                */
-               if (unlikely((bo->val_seq - sequence < (1 << 31))
-                            || !bo->seq_valid))
-                       wake_up = true;
-
-               /*
-                * In the worst case with memory ordering these values can be
-                * seen in the wrong order. However since we call wake_up_all
-                * in that case, this will hopefully not pose a problem,
-                * and the worst case would only cause someone to accidentally
-                * hit -EAGAIN in ttm_bo_reserve when they see old value of
-                * val_seq. However this would only happen if seq_valid was
-                * written before val_seq was, and just means some slightly
-                * increased cpu usage
-                */
-               bo->val_seq = sequence;
-               bo->seq_valid = true;
-               if (wake_up)
-                       wake_up_all(&bo->event_queue);
-       } else {
-               bo->seq_valid = false;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(ttm_bo_reserve);
-
 static void ttm_bo_ref_bug(struct kref *list_kref)
 {
        BUG();
@@ -290,89 +217,16 @@ void ttm_bo_list_ref_sub(struct ttm_buffer_object *bo, int count,
                 (never_free) ? ttm_bo_ref_bug : ttm_bo_release_list);
 }
 
-int ttm_bo_reserve(struct ttm_buffer_object *bo,
-                  bool interruptible,
-                  bool no_wait, bool use_sequence, uint32_t sequence)
-{
-       struct ttm_bo_global *glob = bo->glob;
-       int put_count = 0;
-       int ret;
-
-       ret = ttm_bo_reserve_nolru(bo, interruptible, no_wait, use_sequence,
-                                  sequence);
-       if (likely(ret == 0)) {
-               spin_lock(&glob->lru_lock);
-               put_count = ttm_bo_del_from_lru(bo);
-               spin_unlock(&glob->lru_lock);
-               ttm_bo_list_ref_sub(bo, put_count, true);
-       }
-
-       return ret;
-}
-
-int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo,
-                                 bool interruptible, uint32_t sequence)
-{
-       bool wake_up = false;
-       int ret;
-
-       while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) {
-               WARN_ON(bo->seq_valid && sequence == bo->val_seq);
-
-               ret = ttm_bo_wait_unreserved(bo, interruptible);
-
-               if (unlikely(ret))
-                       return ret;
-       }
-
-       if ((bo->val_seq - sequence < (1 << 31)) || !bo->seq_valid)
-               wake_up = true;
-
-       /**
-        * Wake up waiters that may need to recheck for deadlock,
-        * if we decreased the sequence number.
-        */
-       bo->val_seq = sequence;
-       bo->seq_valid = true;
-       if (wake_up)
-               wake_up_all(&bo->event_queue);
-
-       return 0;
-}
-
-int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo,
-                           bool interruptible, uint32_t sequence)
-{
-       struct ttm_bo_global *glob = bo->glob;
-       int put_count, ret;
-
-       ret = ttm_bo_reserve_slowpath_nolru(bo, interruptible, sequence);
-       if (likely(!ret)) {
-               spin_lock(&glob->lru_lock);
-               put_count = ttm_bo_del_from_lru(bo);
-               spin_unlock(&glob->lru_lock);
-               ttm_bo_list_ref_sub(bo, put_count, true);
-       }
-       return ret;
-}
-EXPORT_SYMBOL(ttm_bo_reserve_slowpath);
-
-void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo)
-{
-       ttm_bo_add_to_lru(bo);
-       atomic_set(&bo->reserved, 0);
-       wake_up_all(&bo->event_queue);
-}
-
-void ttm_bo_unreserve(struct ttm_buffer_object *bo)
+void ttm_bo_del_sub_from_lru(struct ttm_buffer_object *bo)
 {
-       struct ttm_bo_global *glob = bo->glob;
+       int put_count;
 
-       spin_lock(&glob->lru_lock);
-       ttm_bo_unreserve_locked(bo);
-       spin_unlock(&glob->lru_lock);
+       spin_lock(&bo->glob->lru_lock);
+       put_count = ttm_bo_del_from_lru(bo);
+       spin_unlock(&bo->glob->lru_lock);
+       ttm_bo_list_ref_sub(bo, put_count, true);
 }
-EXPORT_SYMBOL(ttm_bo_unreserve);
+EXPORT_SYMBOL(ttm_bo_del_sub_from_lru);
 
 /*
  * Call bo->mutex locked.
@@ -544,17 +398,7 @@ static void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo)
        }
        ttm_bo_mem_put(bo, &bo->mem);
 
-       atomic_set(&bo->reserved, 0);
-       wake_up_all(&bo->event_queue);
-
-       /*
-        * Since the final reference to this bo may not be dropped by
-        * the current task we have to put a memory barrier here to make
-        * sure the changes done in this function are always visible.
-        *
-        * This function only needs protection against the final kref_put.
-        */
-       smp_mb__before_atomic_dec();
+       ww_mutex_unlock (&bo->resv->lock);
 }
 
 static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo)
@@ -586,10 +430,8 @@ 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) {
-               atomic_set(&bo->reserved, 0);
-               wake_up_all(&bo->event_queue);
-       }
+       if (!ret)
+               ww_mutex_unlock(&bo->resv->lock);
 
        kref_get(&bo->list_kref);
        list_add_tail(&bo->ddestroy, &bdev->ddestroy);
@@ -639,8 +481,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,
                sync_obj = driver->sync_obj_ref(bo->sync_obj);
                spin_unlock(&bdev->fence_lock);
 
-               atomic_set(&bo->reserved, 0);
-               wake_up_all(&bo->event_queue);
+               ww_mutex_unlock(&bo->resv->lock);
                spin_unlock(&glob->lru_lock);
 
                ret = driver->sync_obj_wait(sync_obj, false, interruptible);
@@ -678,8 +519,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,
                spin_unlock(&bdev->fence_lock);
 
        if (ret || unlikely(list_empty(&bo->ddestroy))) {
-               atomic_set(&bo->reserved, 0);
-               wake_up_all(&bo->event_queue);
+               ww_mutex_unlock(&bo->resv->lock);
                spin_unlock(&glob->lru_lock);
                return ret;
        }
@@ -831,7 +671,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
                goto out;
        }
 
-       BUG_ON(!ttm_bo_is_reserved(bo));
+       lockdep_assert_held(&bo->resv->lock.base);
 
        evict_mem = bo->mem;
        evict_mem.mm_node = NULL;
@@ -1121,7 +961,7 @@ int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
        struct ttm_mem_reg mem;
        struct ttm_bo_device *bdev = bo->bdev;
 
-       BUG_ON(!ttm_bo_is_reserved(bo));
+       lockdep_assert_held(&bo->resv->lock.base);
 
        /*
         * FIXME: It's possible to pipeline buffer moves.
@@ -1180,7 +1020,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,
 {
        int ret;
 
-       BUG_ON(!ttm_bo_is_reserved(bo));
+       lockdep_assert_held(&bo->resv->lock.base);
        /* Check that range is valid */
        if (placement->lpfn || placement->fpfn)
                if (placement->fpfn > placement->lpfn ||
@@ -1239,6 +1079,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
        int ret = 0;
        unsigned long num_pages;
        struct ttm_mem_global *mem_glob = bdev->glob->mem_glob;
+       bool locked;
 
        ret = ttm_mem_global_alloc(mem_glob, acc_size, false, false);
        if (ret) {
@@ -1265,8 +1106,6 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
        kref_init(&bo->kref);
        kref_init(&bo->list_kref);
        atomic_set(&bo->cpu_writers, 0);
-       atomic_set(&bo->reserved, 1);
-       init_waitqueue_head(&bo->event_queue);
        INIT_LIST_HEAD(&bo->lru);
        INIT_LIST_HEAD(&bo->ddestroy);
        INIT_LIST_HEAD(&bo->swap);
@@ -1284,37 +1123,34 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
        bo->mem.bus.io_reserved_count = 0;
        bo->priv_flags = 0;
        bo->mem.placement = (TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED);
-       bo->seq_valid = false;
        bo->persistent_swap_storage = persistent_swap_storage;
        bo->acc_size = acc_size;
        bo->sg = sg;
+       bo->resv = &bo->ttm_resv;
+       reservation_object_init(bo->resv);
        atomic_inc(&bo->glob->bo_count);
 
        ret = ttm_bo_check_placement(bo, placement);
-       if (unlikely(ret != 0))
-               goto out_err;
 
        /*
         * For ttm_bo_type_device buffers, allocate
         * address space from the device.
         */
-       if (bo->type == ttm_bo_type_device ||
-           bo->type == ttm_bo_type_sg) {
+       if (likely(!ret) &&
+           (bo->type == ttm_bo_type_device ||
+            bo->type == ttm_bo_type_sg))
                ret = ttm_bo_setup_vm(bo);
-               if (ret)
-                       goto out_err;
-       }
 
-       ret = ttm_bo_validate(bo, placement, interruptible, false);
-       if (ret)
-               goto out_err;
+       locked = ww_mutex_trylock(&bo->resv->lock);
+       WARN_ON(!locked);
 
-       ttm_bo_unreserve(bo);
-       return 0;
+       if (likely(!ret))
+               ret = ttm_bo_validate(bo, placement, interruptible, false);
 
-out_err:
        ttm_bo_unreserve(bo);
-       ttm_bo_unref(&bo);
+
+       if (unlikely(ret))
+               ttm_bo_unref(&bo);
 
        return ret;
 }
@@ -1619,9 +1455,7 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
                goto out_no_sys;
 
        bdev->addr_space_rb = RB_ROOT;
-       ret = drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000);
-       if (unlikely(ret != 0))
-               goto out_no_addr_mm;
+       drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000);
 
        INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue);
        INIT_LIST_HEAD(&bdev->ddestroy);
@@ -1635,8 +1469,6 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
        mutex_unlock(&glob->device_list_mutex);
 
        return 0;
-out_no_addr_mm:
-       ttm_bo_clean_mm(bdev, 0);
 out_no_sys:
        return ret;
 }
@@ -1927,8 +1759,7 @@ out:
         * already swapped buffer.
         */
 
-       atomic_set(&bo->reserved, 0);
-       wake_up_all(&bo->event_queue);
+       ww_mutex_unlock(&bo->resv->lock);
        kref_put(&bo->list_kref, ttm_bo_release_list);
        return ret;
 }
index 9212494..e4367f9 100644 (file)
@@ -103,18 +103,12 @@ static int ttm_bo_man_init(struct ttm_mem_type_manager *man,
                           unsigned long p_size)
 {
        struct ttm_range_manager *rman;
-       int ret;
 
        rman = kzalloc(sizeof(*rman), GFP_KERNEL);
        if (!rman)
                return -ENOMEM;
 
-       ret = drm_mm_init(&rman->mm, 0, p_size);
-       if (ret) {
-               kfree(rman);
-               return ret;
-       }
-
+       drm_mm_init(&rman->mm, 0, p_size);
        spin_lock_init(&rman->lock);
        man->priv = rman;
        return 0;
index af89458..319cf41 100644 (file)
@@ -433,6 +433,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
        struct ttm_buffer_object *fbo;
        struct ttm_bo_device *bdev = bo->bdev;
        struct ttm_bo_driver *driver = bdev->driver;
+       int ret;
 
        fbo = kmalloc(sizeof(*fbo), GFP_KERNEL);
        if (!fbo)
@@ -445,7 +446,6 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
         * TODO: Explicit member copy would probably be better here.
         */
 
-       init_waitqueue_head(&fbo->event_queue);
        INIT_LIST_HEAD(&fbo->ddestroy);
        INIT_LIST_HEAD(&fbo->lru);
        INIT_LIST_HEAD(&fbo->swap);
@@ -463,6 +463,10 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
        kref_init(&fbo->kref);
        fbo->destroy = &ttm_transfered_destroy;
        fbo->acc_size = 0;
+       fbo->resv = &fbo->ttm_resv;
+       reservation_object_init(fbo->resv);
+       ret = ww_mutex_trylock(&fbo->resv->lock);
+       WARN_ON(!ret);
 
        *new_obj = fbo;
        return 0;
index 7b90def..6c91178 100644 (file)
@@ -32,7 +32,8 @@
 #include <linux/sched.h>
 #include <linux/module.h>
 
-static void ttm_eu_backoff_reservation_locked(struct list_head *list)
+static void ttm_eu_backoff_reservation_locked(struct list_head *list,
+                                             struct ww_acquire_ctx *ticket)
 {
        struct ttm_validate_buffer *entry;
 
@@ -41,14 +42,12 @@ static void ttm_eu_backoff_reservation_locked(struct list_head *list)
                if (!entry->reserved)
                        continue;
 
+               entry->reserved = false;
                if (entry->removed) {
                        ttm_bo_add_to_lru(bo);
                        entry->removed = false;
-
                }
-               entry->reserved = false;
-               atomic_set(&bo->reserved, 0);
-               wake_up_all(&bo->event_queue);
+               ww_mutex_unlock(&bo->resv->lock);
        }
 }
 
@@ -82,7 +81,8 @@ static void ttm_eu_list_ref_sub(struct list_head *list)
        }
 }
 
-void ttm_eu_backoff_reservation(struct list_head *list)
+void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket,
+                               struct list_head *list)
 {
        struct ttm_validate_buffer *entry;
        struct ttm_bo_global *glob;
@@ -93,7 +93,8 @@ void ttm_eu_backoff_reservation(struct list_head *list)
        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);
+       ttm_eu_backoff_reservation_locked(list, ticket);
+       ww_acquire_fini(ticket);
        spin_unlock(&glob->lru_lock);
 }
 EXPORT_SYMBOL(ttm_eu_backoff_reservation);
@@ -110,12 +111,12 @@ EXPORT_SYMBOL(ttm_eu_backoff_reservation);
  * buffers in different orders.
  */
 
-int ttm_eu_reserve_buffers(struct list_head *list)
+int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
+                          struct list_head *list)
 {
        struct ttm_bo_global *glob;
        struct ttm_validate_buffer *entry;
        int ret;
-       uint32_t val_seq;
 
        if (list_empty(list))
                return 0;
@@ -129,9 +130,7 @@ int ttm_eu_reserve_buffers(struct list_head *list)
        entry = list_first_entry(list, struct ttm_validate_buffer, head);
        glob = entry->bo->glob;
 
-       spin_lock(&glob->lru_lock);
-       val_seq = entry->bo->bdev->val_seq++;
-
+       ww_acquire_init(ticket, &reservation_ww_class);
 retry:
        list_for_each_entry(entry, list, head) {
                struct ttm_buffer_object *bo = entry->bo;
@@ -140,49 +139,34 @@ retry:
                if (entry->reserved)
                        continue;
 
-               ret = ttm_bo_reserve_nolru(bo, true, true, true, val_seq);
-               switch (ret) {
-               case 0:
-                       break;
-               case -EBUSY:
-                       ttm_eu_del_from_lru_locked(list);
-                       spin_unlock(&glob->lru_lock);
-                       ret = ttm_bo_reserve_nolru(bo, true, false,
-                                                  true, val_seq);
-                       spin_lock(&glob->lru_lock);
-                       if (!ret)
-                               break;
-
-                       if (unlikely(ret != -EAGAIN))
-                               goto err;
 
-                       /* fallthrough */
-               case -EAGAIN:
-                       ttm_eu_backoff_reservation_locked(list);
+               ret = ttm_bo_reserve_nolru(bo, true, false, true, ticket);
 
-                       /*
-                        * temporarily increase sequence number every retry,
-                        * to prevent us from seeing our old reservation
-                        * sequence when someone else reserved the buffer,
-                        * but hasn't updated the seq_valid/seqno members yet.
+               if (ret == -EDEADLK) {
+                       /* uh oh, we lost out, drop every reservation and try
+                        * to only reserve this buffer, then start over if
+                        * this succeeds.
                         */
-                       val_seq = entry->bo->bdev->val_seq++;
-
+                       spin_lock(&glob->lru_lock);
+                       ttm_eu_backoff_reservation_locked(list, ticket);
                        spin_unlock(&glob->lru_lock);
                        ttm_eu_list_ref_sub(list);
-                       ret = ttm_bo_reserve_slowpath_nolru(bo, true, val_seq);
-                       if (unlikely(ret != 0))
-                               return ret;
-                       spin_lock(&glob->lru_lock);
+                       ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
+                                                              ticket);
+                       if (unlikely(ret != 0)) {
+                               if (ret == -EINTR)
+                                       ret = -ERESTARTSYS;
+                               goto err_fini;
+                       }
+
                        entry->reserved = true;
                        if (unlikely(atomic_read(&bo->cpu_writers) > 0)) {
                                ret = -EBUSY;
                                goto err;
                        }
                        goto retry;
-               default:
+               } else if (ret)
                        goto err;
-               }
 
                entry->reserved = true;
                if (unlikely(atomic_read(&bo->cpu_writers) > 0)) {
@@ -191,21 +175,27 @@ retry:
                }
        }
 
+       ww_acquire_done(ticket);
+       spin_lock(&glob->lru_lock);
        ttm_eu_del_from_lru_locked(list);
        spin_unlock(&glob->lru_lock);
        ttm_eu_list_ref_sub(list);
-
        return 0;
 
 err:
-       ttm_eu_backoff_reservation_locked(list);
+       spin_lock(&glob->lru_lock);
+       ttm_eu_backoff_reservation_locked(list, ticket);
        spin_unlock(&glob->lru_lock);
        ttm_eu_list_ref_sub(list);
+err_fini:
+       ww_acquire_done(ticket);
+       ww_acquire_fini(ticket);
        return ret;
 }
 EXPORT_SYMBOL(ttm_eu_reserve_buffers);
 
-void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj)
+void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,
+                                struct list_head *list, void *sync_obj)
 {
        struct ttm_validate_buffer *entry;
        struct ttm_buffer_object *bo;
@@ -228,11 +218,13 @@ void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj)
                bo = entry->bo;
                entry->old_sync_obj = bo->sync_obj;
                bo->sync_obj = driver->sync_obj_ref(sync_obj);
-               ttm_bo_unreserve_locked(bo);
+               ttm_bo_add_to_lru(bo);
+               ww_mutex_unlock(&bo->resv->lock);
                entry->reserved = false;
        }
        spin_unlock(&bdev->fence_lock);
        spin_unlock(&glob->lru_lock);
+       ww_acquire_fini(ticket);
 
        list_for_each_entry(entry, list, head) {
                if (entry->old_sync_obj)
index dc0c065..97e9d61 100644 (file)
@@ -393,19 +393,6 @@ static struct fb_ops udlfb_ops = {
        .fb_release = udl_fb_release,
 };
 
-static void udl_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
-                          u16 blue, int regno)
-{
-}
-
-static void udl_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
-                            u16 *blue, int regno)
-{
-       *red = 0;
-       *green = 0;
-       *blue = 0;
-}
-
 static int udl_user_framebuffer_dirty(struct drm_framebuffer *fb,
                                      struct drm_file *file,
                                      unsigned flags, unsigned color,
@@ -558,8 +545,6 @@ out:
 }
 
 static struct drm_fb_helper_funcs udl_fb_helper_funcs = {
-       .gamma_set = udl_crtc_fb_gamma_set,
-       .gamma_get = udl_crtc_fb_gamma_get,
        .fb_probe = udlfb_create,
 };
 
index e96d234..2ae1eb7 100644 (file)
@@ -363,10 +363,6 @@ static void udl_crtc_destroy(struct drm_crtc *crtc)
        kfree(crtc);
 }
 
-static void udl_load_lut(struct drm_crtc *crtc)
-{
-}
-
 static void udl_crtc_prepare(struct drm_crtc *crtc)
 {
 }
@@ -383,7 +379,6 @@ static struct drm_crtc_helper_funcs udl_helper_funcs = {
        .prepare = udl_crtc_prepare,
        .commit = udl_crtc_commit,
        .disable = udl_crtc_disable,
-       .load_lut = udl_load_lut,
 };
 
 static const struct drm_crtc_funcs udl_crtc_funcs = {
index 5fae06a..d4e54fc 100644 (file)
@@ -302,7 +302,7 @@ void vmw_bo_pin(struct ttm_buffer_object *bo, bool pin)
        uint32_t old_mem_type = bo->mem.mem_type;
        int ret;
 
-       BUG_ON(!ttm_bo_is_reserved(bo));
+       lockdep_assert_held(&bo->resv->lock.base);
        BUG_ON(old_mem_type != TTM_PL_VRAM &&
               old_mem_type != VMW_PL_GMR);
 
index 07dfd82..78e2164 100644 (file)
@@ -565,8 +565,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
                dev_priv->has_gmr = false;
        }
 
-       dev_priv->mmio_mtrr = drm_mtrr_add(dev_priv->mmio_start,
-                                          dev_priv->mmio_size, DRM_MTRR_WC);
+       dev_priv->mmio_mtrr = arch_phys_wc_add(dev_priv->mmio_start,
+                                              dev_priv->mmio_size);
 
        dev_priv->mmio_virt = ioremap_wc(dev_priv->mmio_start,
                                         dev_priv->mmio_size);
@@ -664,8 +664,7 @@ out_no_device:
 out_err4:
        iounmap(dev_priv->mmio_virt);
 out_err3:
-       drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start,
-                    dev_priv->mmio_size, DRM_MTRR_WC);
+       arch_phys_wc_del(dev_priv->mmio_mtrr);
        if (dev_priv->has_gmr)
                (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
        (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
@@ -709,8 +708,7 @@ static int vmw_driver_unload(struct drm_device *dev)
 
        ttm_object_device_release(&dev_priv->tdev);
        iounmap(dev_priv->mmio_virt);
-       drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start,
-                    dev_priv->mmio_size, DRM_MTRR_WC);
+       arch_phys_wc_del(dev_priv->mmio_mtrr);
        if (dev_priv->has_gmr)
                (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
        (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
index 394e647..599f646 100644 (file)
@@ -1432,6 +1432,7 @@ int vmw_execbuf_process(struct drm_file *file_priv,
        struct vmw_fence_obj *fence = NULL;
        struct vmw_resource *error_resource;
        struct list_head resource_list;
+       struct ww_acquire_ctx ticket;
        uint32_t handle;
        void *cmd;
        int ret;
@@ -1488,7 +1489,7 @@ int vmw_execbuf_process(struct drm_file *file_priv,
        if (unlikely(ret != 0))
                goto out_err;
 
-       ret = ttm_eu_reserve_buffers(&sw_context->validate_nodes);
+       ret = ttm_eu_reserve_buffers(&ticket, &sw_context->validate_nodes);
        if (unlikely(ret != 0))
                goto out_err;
 
@@ -1537,7 +1538,7 @@ int vmw_execbuf_process(struct drm_file *file_priv,
                DRM_ERROR("Fence submission error. Syncing.\n");
 
        vmw_resource_list_unreserve(&sw_context->resource_list, false);
-       ttm_eu_fence_buffer_objects(&sw_context->validate_nodes,
+       ttm_eu_fence_buffer_objects(&ticket, &sw_context->validate_nodes,
                                    (void *) fence);
 
        if (unlikely(dev_priv->pinned_bo != NULL &&
@@ -1570,7 +1571,7 @@ int vmw_execbuf_process(struct drm_file *file_priv,
 out_err:
        vmw_resource_relocations_free(&sw_context->res_relocations);
        vmw_free_relocations(sw_context);
-       ttm_eu_backoff_reservation(&sw_context->validate_nodes);
+       ttm_eu_backoff_reservation(&ticket, &sw_context->validate_nodes);
        vmw_resource_list_unreserve(&sw_context->resource_list, true);
        vmw_clear_validations(sw_context);
        if (unlikely(dev_priv->pinned_bo != NULL &&
@@ -1644,6 +1645,7 @@ void __vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv,
        struct list_head validate_list;
        struct ttm_validate_buffer pinned_val, query_val;
        struct vmw_fence_obj *lfence = NULL;
+       struct ww_acquire_ctx ticket;
 
        if (dev_priv->pinned_bo == NULL)
                goto out_unlock;
@@ -1657,7 +1659,7 @@ void __vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv,
        list_add_tail(&query_val.head, &validate_list);
 
        do {
-               ret = ttm_eu_reserve_buffers(&validate_list);
+               ret = ttm_eu_reserve_buffers(&ticket, &validate_list);
        } while (ret == -ERESTARTSYS);
 
        if (unlikely(ret != 0)) {
@@ -1684,7 +1686,7 @@ void __vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv,
                                                  NULL);
                fence = lfence;
        }
-       ttm_eu_fence_buffer_objects(&validate_list, (void *) fence);
+       ttm_eu_fence_buffer_objects(&ticket, &validate_list, (void *) fence);
        if (lfence != NULL)
                vmw_fence_obj_unreference(&lfence);
 
@@ -1696,7 +1698,7 @@ out_unlock:
        return;
 
 out_no_emit:
-       ttm_eu_backoff_reservation(&validate_list);
+       ttm_eu_backoff_reservation(&ticket, &validate_list);
 out_no_reserve:
        ttm_bo_unref(&query_val.bo);
        ttm_bo_unref(&pinned_val.bo);
index 3e3c7ab..d4607b2 100644 (file)
@@ -174,7 +174,6 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
                           uint32_t handle, uint32_t width, uint32_t height)
 {
        struct vmw_private *dev_priv = vmw_priv(crtc->dev);
-       struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
        struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
        struct vmw_surface *surface = NULL;
        struct vmw_dma_buffer *dmabuf = NULL;
@@ -197,6 +196,8 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
        }
 
        if (handle) {
+               struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+
                ret = vmw_user_lookup_handle(dev_priv, tfile,
                                             handle, &surface, &dmabuf);
                if (ret) {
index bc78425..7953d1f 100644 (file)
@@ -958,13 +958,13 @@ void vmw_resource_unreserve(struct vmw_resource *res,
        if (new_backup && new_backup != res->backup) {
 
                if (res->backup) {
-                       BUG_ON(!ttm_bo_is_reserved(&res->backup->base));
+                       lockdep_assert_held(&res->backup->base.resv->lock.base);
                        list_del_init(&res->mob_head);
                        vmw_dmabuf_unreference(&res->backup);
                }
 
                res->backup = vmw_dmabuf_reference(new_backup);
-               BUG_ON(!ttm_bo_is_reserved(&new_backup->base));
+               lockdep_assert_held(&new_backup->base.resv->lock.base);
                list_add_tail(&res->mob_head, &new_backup->res_list);
        }
        if (new_backup)
@@ -990,9 +990,11 @@ void vmw_resource_unreserve(struct vmw_resource *res,
  * @val_buf:        On successful return contains data about the
  *                  reserved and validated backup buffer.
  */
-int vmw_resource_check_buffer(struct vmw_resource *res,
-                             bool interruptible,
-                             struct ttm_validate_buffer *val_buf)
+static int
+vmw_resource_check_buffer(struct vmw_resource *res,
+                         struct ww_acquire_ctx *ticket,
+                         bool interruptible,
+                         struct ttm_validate_buffer *val_buf)
 {
        struct list_head val_list;
        bool backup_dirty = false;
@@ -1007,7 +1009,7 @@ int 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(&val_list);
+       ret = ttm_eu_reserve_buffers(ticket, &val_list);
        if (unlikely(ret != 0))
                goto out_no_reserve;
 
@@ -1025,7 +1027,7 @@ int vmw_resource_check_buffer(struct vmw_resource *res,
        return 0;
 
 out_no_validate:
-       ttm_eu_backoff_reservation(&val_list);
+       ttm_eu_backoff_reservation(ticket, &val_list);
 out_no_reserve:
        ttm_bo_unref(&val_buf->bo);
        if (backup_dirty)
@@ -1069,7 +1071,9 @@ int vmw_resource_reserve(struct vmw_resource *res, bool no_backup)
  *.
  * @val_buf:        Backup buffer information.
  */
-void vmw_resource_backoff_reservation(struct ttm_validate_buffer *val_buf)
+static void
+vmw_resource_backoff_reservation(struct ww_acquire_ctx *ticket,
+                                struct ttm_validate_buffer *val_buf)
 {
        struct list_head val_list;
 
@@ -1078,7 +1082,7 @@ void vmw_resource_backoff_reservation(struct ttm_validate_buffer *val_buf)
 
        INIT_LIST_HEAD(&val_list);
        list_add_tail(&val_buf->head, &val_list);
-       ttm_eu_backoff_reservation(&val_list);
+       ttm_eu_backoff_reservation(ticket, &val_list);
        ttm_bo_unref(&val_buf->bo);
 }
 
@@ -1092,12 +1096,13 @@ int vmw_resource_do_evict(struct vmw_resource *res)
 {
        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, true, &val_buf);
+       ret = vmw_resource_check_buffer(res, &ticket, true, &val_buf);
        if (unlikely(ret != 0))
                return ret;
 
@@ -1112,7 +1117,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(&val_buf);
+       vmw_resource_backoff_reservation(&ticket, &val_buf);
 
        return ret;
 }
index a1607d6..790ddf1 100644 (file)
@@ -73,7 +73,7 @@ struct host1x_syncpt_ops {
        void (*restore_wait_base)(struct host1x_syncpt *syncpt);
        void (*load_wait_base)(struct host1x_syncpt *syncpt);
        u32 (*load)(struct host1x_syncpt *syncpt);
-       void (*cpu_incr)(struct host1x_syncpt *syncpt);
+       int (*cpu_incr)(struct host1x_syncpt *syncpt);
        int (*patch_wait)(struct host1x_syncpt *syncpt, void *patch_addr);
 };
 
@@ -157,10 +157,10 @@ static inline u32 host1x_hw_syncpt_load(struct host1x *host,
        return host->syncpt_op->load(sp);
 }
 
-static inline void host1x_hw_syncpt_cpu_incr(struct host1x *host,
-                                            struct host1x_syncpt *sp)
+static inline int host1x_hw_syncpt_cpu_incr(struct host1x *host,
+                                           struct host1x_syncpt *sp)
 {
-       host->syncpt_op->cpu_incr(sp);
+       return host->syncpt_op->cpu_incr(sp);
 }
 
 static inline int host1x_hw_syncpt_patch_wait(struct host1x *host,
index 8c04943..5360e5a 100644 (file)
@@ -79,6 +79,9 @@ static int tegra_plane_disable(struct drm_plane *plane)
        struct tegra_plane *p = to_tegra_plane(plane);
        unsigned long value;
 
+       if (!plane->crtc)
+               return 0;
+
        value = WINDOW_A_SELECT << p->index;
        tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
 
@@ -140,6 +143,7 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
 static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
                             struct drm_framebuffer *fb)
 {
+       unsigned int format = tegra_dc_format(fb->pixel_format);
        struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
        unsigned long value;
 
@@ -150,6 +154,7 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
 
        tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR);
        tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE);
+       tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH);
 
        value = GENERAL_UPDATE | WIN_A_UPDATE;
        tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
index 2b561c9..e184b00 100644 (file)
@@ -148,6 +148,7 @@ int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm)
                                dev_err(host1x->dev,
                                        "DRM setup failed for %s: %d\n",
                                        dev_name(client->dev), err);
+                               mutex_unlock(&host1x->clients_lock);
                                return err;
                        }
                }
@@ -175,6 +176,7 @@ int host1x_drm_exit(struct host1x_drm *host1x)
                                dev_err(host1x->dev,
                                        "DRM cleanup failed for %s: %d\n",
                                        dev_name(client->dev), err);
+                               mutex_unlock(&host1x->clients_lock);
                                return err;
                        }
                }
@@ -257,6 +259,13 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
        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;
@@ -378,8 +387,7 @@ static int tegra_syncpt_incr(struct drm_device *drm, void *data,
        if (!sp)
                return -EINVAL;
 
-       host1x_syncpt_incr(sp);
-       return 0;
+       return host1x_syncpt_incr(sp);
 }
 
 static int tegra_syncpt_wait(struct drm_device *drm, void *data,
@@ -605,7 +613,7 @@ static void tegra_debugfs_cleanup(struct drm_minor *minor)
 #endif
 
 struct drm_driver tegra_drm_driver = {
-       .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM,
+       .driver_features = DRIVER_MODESET | DRIVER_GEM,
        .load = tegra_drm_load,
        .unload = tegra_drm_unload,
        .open = tegra_drm_open,
index 6a45ae0..27ffcf1 100644 (file)
@@ -84,7 +84,7 @@ static struct host1x_bo *host1x_bo_lookup(struct drm_device *drm,
 
        gem = drm_gem_object_lookup(drm, file, handle);
        if (!gem)
-               return 0;
+               return NULL;
 
        mutex_lock(&drm->struct_mutex);
        drm_gem_object_unreference(gem);
@@ -135,8 +135,10 @@ static int gr2d_submit(struct host1x_drm_context *context,
                        goto fail;
 
                bo = host1x_bo_lookup(drm, file, cmdbuf.handle);
-               if (!bo)
+               if (!bo) {
+                       err = -ENOENT;
                        goto fail;
+               }
 
                host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
                num_cmdbufs--;
@@ -158,8 +160,10 @@ static int gr2d_submit(struct host1x_drm_context *context,
                reloc->cmdbuf = cmdbuf;
                reloc->target = target;
 
-               if (!reloc->target || !reloc->cmdbuf)
+               if (!reloc->target || !reloc->cmdbuf) {
+                       err = -ENOENT;
                        goto fail;
+               }
        }
 
        err = copy_from_user(job->waitchk, waitchks,
@@ -281,7 +285,7 @@ static int gr2d_probe(struct platform_device *pdev)
        if (!gr2d->channel)
                return -ENOMEM;
 
-       *syncpts = host1x_syncpt_request(dev, 0);
+       *syncpts = host1x_syncpt_request(dev, false);
        if (!(*syncpts)) {
                host1x_channel_free(gr2d->channel);
                return -ENOMEM;
index 590b69d..2ee4ad5 100644 (file)
@@ -44,7 +44,7 @@ static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr,
        u32 i;
 
        for (i = 0; i < syncpt_incrs; i++)
-               host1x_syncpt_cpu_incr(cdma->timeout.syncpt);
+               host1x_syncpt_incr(cdma->timeout.syncpt);
 
        /* after CPU incr, ensure shadow is up to date */
        host1x_syncpt_load(cdma->timeout.syncpt);
index 6117499..0cf6095 100644 (file)
@@ -77,21 +77,19 @@ static u32 syncpt_load(struct host1x_syncpt *sp)
  * Write a cpu syncpoint increment to the hardware, without touching
  * the cache.
  */
-static void syncpt_cpu_incr(struct host1x_syncpt *sp)
+static int syncpt_cpu_incr(struct host1x_syncpt *sp)
 {
        struct host1x *host = sp->host;
        u32 reg_offset = sp->id / 32;
 
        if (!host1x_syncpt_client_managed(sp) &&
-           host1x_syncpt_idle(sp)) {
-               dev_err(host->dev, "Trying to increment syncpoint id %d beyond max\n",
-                       sp->id);
-               host1x_debug_dump(sp->host);
-               return;
-       }
+           host1x_syncpt_idle(sp))
+               return -EINVAL;
        host1x_sync_writel(host, BIT_MASK(sp->id),
                           HOST1X_SYNC_SYNCPT_CPU_INCR(reg_offset));
        wmb();
+
+       return 0;
 }
 
 /* remove a wait pointed to by patch_addr */
index f665d67..cc80766 100644 (file)
@@ -228,17 +228,15 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
        void *cmdbuf_page_addr = NULL;
 
        /* pin & patch the relocs for one gather */
-       while (i < job->num_relocs) {
+       for (i = 0; i < job->num_relocs; i++) {
                struct host1x_reloc *reloc = &job->relocarray[i];
                u32 reloc_addr = (job->reloc_addr_phys[i] +
                        reloc->target_offset) >> reloc->shift;
                u32 *target;
 
                /* skip all other gathers */
-               if (!(reloc->cmdbuf && cmdbuf == reloc->cmdbuf)) {
-                       i++;
+               if (cmdbuf != reloc->cmdbuf)
                        continue;
-               }
 
                if (last_page != reloc->cmdbuf_offset >> PAGE_SHIFT) {
                        if (cmdbuf_page_addr)
@@ -257,9 +255,6 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
 
                target = cmdbuf_page_addr + (reloc->cmdbuf_offset & ~PAGE_MASK);
                *target = reloc_addr;
-
-               /* mark this gather as handled */
-               reloc->cmdbuf = 0;
        }
 
        if (cmdbuf_page_addr)
@@ -268,15 +263,15 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
        return 0;
 }
 
-static int check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
+static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
                       unsigned int offset)
 {
        offset *= sizeof(u32);
 
        if (reloc->cmdbuf != cmdbuf || reloc->cmdbuf_offset != offset)
-               return -EINVAL;
+               return false;
 
-       return 0;
+       return true;
 }
 
 struct host1x_firewall {
@@ -307,10 +302,10 @@ static int check_mask(struct host1x_firewall *fw)
 
                if (mask & 1) {
                        if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) {
-                               bool bad_reloc = check_reloc(fw->reloc,
-                                                            fw->cmdbuf_id,
-                                                            fw->offset);
-                               if (!fw->num_relocs || bad_reloc)
+                               if (!fw->num_relocs)
+                                       return -EINVAL;
+                               if (!check_reloc(fw->reloc, fw->cmdbuf_id,
+                                                fw->offset))
                                        return -EINVAL;
                                fw->reloc++;
                                fw->num_relocs--;
@@ -330,14 +325,14 @@ static int check_incr(struct host1x_firewall *fw)
        u32 count = fw->count;
        u32 reg = fw->reg;
 
-       while (fw) {
+       while (count) {
                if (fw->words == 0)
                        return -EINVAL;
 
                if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) {
-                       bool bad_reloc = check_reloc(fw->reloc, fw->cmdbuf_id,
-                                                    fw->offset);
-                       if (!fw->num_relocs || bad_reloc)
+                       if (!fw->num_relocs)
+                               return -EINVAL;
+                       if (!check_reloc(fw->reloc, fw->cmdbuf_id, fw->offset))
                                return -EINVAL;
                        fw->reloc++;
                        fw->num_relocs--;
@@ -361,9 +356,9 @@ static int check_nonincr(struct host1x_firewall *fw)
                        return -EINVAL;
 
                if (is_addr_reg) {
-                       bool bad_reloc = check_reloc(fw->reloc, fw->cmdbuf_id,
-                                                    fw->offset);
-                       if (!fw->num_relocs || bad_reloc)
+                       if (!fw->num_relocs)
+                               return -EINVAL;
+                       if (!check_reloc(fw->reloc, fw->cmdbuf_id, fw->offset))
                                return -EINVAL;
                        fw->reloc++;
                        fw->num_relocs--;
@@ -376,69 +371,58 @@ static int check_nonincr(struct host1x_firewall *fw)
        return 0;
 }
 
-static int validate(struct host1x_job *job, struct device *dev,
-                   struct host1x_job_gather *g)
+static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
 {
-       u32 *cmdbuf_base;
+       u32 *cmdbuf_base = (u32 *)fw->job->gather_copy_mapped +
+               (g->offset / sizeof(u32));
        int err = 0;
-       struct host1x_firewall fw;
 
-       fw.job = job;
-       fw.dev = dev;
-       fw.reloc = job->relocarray;
-       fw.num_relocs = job->num_relocs;
-       fw.cmdbuf_id = g->bo;
-
-       fw.offset = 0;
-       fw.class = 0;
-
-       if (!job->is_addr_reg)
+       if (!fw->job->is_addr_reg)
                return 0;
 
-       cmdbuf_base = host1x_bo_mmap(g->bo);
-       if (!cmdbuf_base)
-               return -ENOMEM;
+       fw->words = g->words;
+       fw->cmdbuf_id = g->bo;
+       fw->offset = 0;
 
-       fw.words = g->words;
-       while (fw.words && !err) {
-               u32 word = cmdbuf_base[fw.offset];
+       while (fw->words && !err) {
+               u32 word = cmdbuf_base[fw->offset];
                u32 opcode = (word & 0xf0000000) >> 28;
 
-               fw.mask = 0;
-               fw.reg = 0;
-               fw.count = 0;
-               fw.words--;
-               fw.offset++;
+               fw->mask = 0;
+               fw->reg = 0;
+               fw->count = 0;
+               fw->words--;
+               fw->offset++;
 
                switch (opcode) {
                case 0:
-                       fw.class = word >> 6 & 0x3ff;
-                       fw.mask = word & 0x3f;
-                       fw.reg = word >> 16 & 0xfff;
-                       err = check_mask(&fw);
+                       fw->class = word >> 6 & 0x3ff;
+                       fw->mask = word & 0x3f;
+                       fw->reg = word >> 16 & 0xfff;
+                       err = check_mask(fw);
                        if (err)
                                goto out;
                        break;
                case 1:
-                       fw.reg = word >> 16 & 0xfff;
-                       fw.count = word & 0xffff;
-                       err = check_incr(&fw);
+                       fw->reg = word >> 16 & 0xfff;
+                       fw->count = word & 0xffff;
+                       err = check_incr(fw);
                        if (err)
                                goto out;
                        break;
 
                case 2:
-                       fw.reg = word >> 16 & 0xfff;
-                       fw.count = word & 0xffff;
-                       err = check_nonincr(&fw);
+                       fw->reg = word >> 16 & 0xfff;
+                       fw->count = word & 0xffff;
+                       err = check_nonincr(fw);
                        if (err)
                                goto out;
                        break;
 
                case 3:
-                       fw.mask = word & 0xffff;
-                       fw.reg = word >> 16 & 0xfff;
-                       err = check_mask(&fw);
+                       fw->mask = word & 0xffff;
+                       fw->reg = word >> 16 & 0xfff;
+                       err = check_mask(fw);
                        if (err)
                                goto out;
                        break;
@@ -453,21 +437,26 @@ static int validate(struct host1x_job *job, struct device *dev,
        }
 
        /* No relocs should remain at this point */
-       if (fw.num_relocs)
+       if (fw->num_relocs)
                err = -EINVAL;
 
 out:
-       host1x_bo_munmap(g->bo, cmdbuf_base);
-
        return err;
 }
 
 static inline int copy_gathers(struct host1x_job *job, struct device *dev)
 {
+       struct host1x_firewall fw;
        size_t size = 0;
        size_t offset = 0;
        int i;
 
+       fw.job = job;
+       fw.dev = dev;
+       fw.reloc = job->relocarray;
+       fw.num_relocs = job->num_relocs;
+       fw.class = 0;
+
        for (i = 0; i < job->num_gathers; i++) {
                struct host1x_job_gather *g = &job->gathers[i];
                size += g->words * sizeof(u32);
@@ -488,14 +477,19 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev)
                struct host1x_job_gather *g = &job->gathers[i];
                void *gather;
 
+               /* Copy the gather */
                gather = host1x_bo_mmap(g->bo);
                memcpy(job->gather_copy_mapped + offset, gather + g->offset,
                       g->words * sizeof(u32));
                host1x_bo_munmap(g->bo, gather);
 
+               /* Store the location in the buffer */
                g->base = job->gather_copy;
                g->offset = offset;
-               g->bo = NULL;
+
+               /* Validate the job */
+               if (validate(&fw, g))
+                       return -EINVAL;
 
                offset += g->words * sizeof(u32);
        }
@@ -540,20 +534,11 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev)
                        if (job->gathers[j].bo == g->bo)
                                job->gathers[j].handled = true;
 
-               err = 0;
-
-               if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL))
-                       err = validate(job, dev, g);
-
+               err = do_relocs(job, g->bo);
                if (err)
-                       dev_err(dev, "Job invalid (err=%d)\n", err);
-
-               if (!err)
-                       err = do_relocs(job, g->bo);
-
-               if (!err)
-                       err = do_waitchks(job, host, g->bo);
+                       break;
 
+               err = do_waitchks(job, host, g->bo);
                if (err)
                        break;
        }
index 4b49345..409745b 100644 (file)
@@ -32,7 +32,7 @@
 
 static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host,
                                                  struct device *dev,
-                                                 int client_managed)
+                                                 bool client_managed)
 {
        int i;
        struct host1x_syncpt *sp = host->syncpt;
@@ -40,7 +40,8 @@ static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host,
 
        for (i = 0; i < host->info->nb_pts && sp->name; i++, sp++)
                ;
-       if (sp->dev)
+
+       if (i >= host->info->nb_pts)
                return NULL;
 
        name = kasprintf(GFP_KERNEL, "%02d-%s", sp->id,
@@ -127,23 +128,12 @@ u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp)
        return val;
 }
 
-/*
- * Write a cpu syncpoint increment to the hardware, without touching
- * the cache. Caller is responsible for host being powered.
- */
-void host1x_syncpt_cpu_incr(struct host1x_syncpt *sp)
-{
-       host1x_hw_syncpt_cpu_incr(sp->host, sp);
-}
-
 /*
  * Increment syncpoint value from cpu, updating cache
  */
-void host1x_syncpt_incr(struct host1x_syncpt *sp)
+int host1x_syncpt_incr(struct host1x_syncpt *sp)
 {
-       if (host1x_syncpt_client_managed(sp))
-               host1x_syncpt_incr_max(sp, 1);
-       host1x_syncpt_cpu_incr(sp);
+       return host1x_hw_syncpt_cpu_incr(sp->host, sp);
 }
 
 /*
@@ -331,7 +321,7 @@ int host1x_syncpt_init(struct host1x *host)
        host1x_syncpt_restore(host);
 
        /* Allocate sync point to use for clearing waits for expired fences */
-       host->nop_sp = _host1x_syncpt_alloc(host, NULL, 0);
+       host->nop_sp = _host1x_syncpt_alloc(host, NULL, false);
        if (!host->nop_sp)
                return -ENOMEM;
 
@@ -339,7 +329,7 @@ int host1x_syncpt_init(struct host1x *host)
 }
 
 struct host1x_syncpt *host1x_syncpt_request(struct device *dev,
-                                           int client_managed)
+                                           bool client_managed)
 {
        struct host1x *host = dev_get_drvdata(dev->parent);
        return _host1x_syncpt_alloc(host, dev, client_managed);
@@ -353,7 +343,7 @@ void host1x_syncpt_free(struct host1x_syncpt *sp)
        kfree(sp->name);
        sp->dev = NULL;
        sp->name = NULL;
-       sp->client_managed = 0;
+       sp->client_managed = false;
 }
 
 void host1x_syncpt_deinit(struct host1x *host)
index c998061..267c0b9 100644 (file)
@@ -36,7 +36,7 @@ struct host1x_syncpt {
        atomic_t max_val;
        u32 base_val;
        const char *name;
-       int client_managed;
+       bool client_managed;
        struct host1x *host;
        struct device *dev;
 
@@ -94,7 +94,7 @@ static inline bool host1x_syncpt_check_max(struct host1x_syncpt *sp, u32 real)
 }
 
 /* Return true if sync point is client managed. */
-static inline int host1x_syncpt_client_managed(struct host1x_syncpt *sp)
+static inline bool host1x_syncpt_client_managed(struct host1x_syncpt *sp)
 {
        return sp->client_managed;
 }
@@ -115,9 +115,6 @@ static inline bool host1x_syncpt_idle(struct host1x_syncpt *sp)
 /* Return pointer to struct denoting sync point id. */
 struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id);
 
-/* Request incrementing a sync point. */
-void host1x_syncpt_cpu_incr(struct host1x_syncpt *sp);
-
 /* Load current value from hardware to the shadow register. */
 u32 host1x_syncpt_load(struct host1x_syncpt *sp);
 
@@ -133,8 +130,8 @@ 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);
 
-/* Increment sync point and its max. */
-void host1x_syncpt_incr(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);
@@ -157,7 +154,7 @@ u32 host1x_syncpt_id(struct host1x_syncpt *sp);
 
 /* Allocate a sync point for a device. */
 struct host1x_syncpt *host1x_syncpt_request(struct device *dev,
-               int client_managed);
+                                           bool client_managed);
 
 /* Free a sync point. */
 void host1x_syncpt_free(struct host1x_syncpt *sp);
index 71c2c71..34fbc2f 100644 (file)
@@ -3269,9 +3269,9 @@ static int cma_netdev_change(struct net_device *ndev, struct rdma_id_private *id
 }
 
 static int cma_netdev_callback(struct notifier_block *self, unsigned long event,
-                              void *ctx)
+                              void *ptr)
 {
-       struct net_device *ndev = (struct net_device *)ctx;
+       struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
        struct cma_device *cma_dev;
        struct rdma_id_private *id_priv;
        int ret = NOTIFY_DONE;
index 23d7343..a188d31 100644 (file)
@@ -1161,7 +1161,7 @@ static void netdev_removed(struct mlx4_ib_dev *dev, int port)
 static int mlx4_ib_netdev_event(struct notifier_block *this, unsigned long event,
                                void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct mlx4_ib_dev *ibdev;
        struct net_device *oldnd;
        struct mlx4_ib_iboe *iboe;
index 9144a6b..6ba3514 100644 (file)
@@ -291,25 +291,20 @@ static int msm_iommu_ctx_probe(struct platform_device *pdev)
 {
        struct msm_iommu_ctx_dev *c = pdev->dev.platform_data;
        struct msm_iommu_drvdata *drvdata;
-       struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
+       struct msm_iommu_ctx_drvdata *ctx_drvdata;
        int i, ret;
-       if (!c || !pdev->dev.parent) {
-               ret = -EINVAL;
-               goto fail;
-       }
 
-       drvdata = dev_get_drvdata(pdev->dev.parent);
+       if (!c || !pdev->dev.parent)
+               return -EINVAL;
 
-       if (!drvdata) {
-               ret = -ENODEV;
-               goto fail;
-       }
+       drvdata = dev_get_drvdata(pdev->dev.parent);
+       if (!drvdata)
+               return -ENODEV;
 
        ctx_drvdata = kzalloc(sizeof(*ctx_drvdata), GFP_KERNEL);
-       if (!ctx_drvdata) {
-               ret = -ENOMEM;
-               goto fail;
-       }
+       if (!ctx_drvdata)
+               return -ENOMEM;
+
        ctx_drvdata->num = c->num;
        ctx_drvdata->pdev = pdev;
 
@@ -403,6 +398,7 @@ static int __init msm_iommu_driver_init(void)
 
        ret = platform_driver_register(&msm_iommu_ctx_driver);
        if (ret != 0) {
+               platform_driver_unregister(&msm_iommu_driver);
                pr_err("Failed to register IOMMU context driver\n");
                goto error;
        }
index 88d657d..8b98d53 100644 (file)
@@ -885,7 +885,7 @@ isdn_net_log_skb(struct sk_buff *skb, isdn_net_local *lp)
 
        addinfo[0] = '\0';
        /* This check stolen from 2.1.72 dev_queue_xmit_nit() */
-       if (p < skb->data || skb->network_header >= skb->tail) {
+       if (p < skb->data || skb_network_header(skb) >= skb_tail_pointer(skb)) {
                /* fall back to old isdn_net_log_packet method() */
                char *buf = skb->data;
 
index 3835321..b45b240 100644 (file)
@@ -25,6 +25,9 @@ menuconfig NETDEVICES
 # that for each of the symbols.
 if NETDEVICES
 
+config MII
+       tristate
+
 config NET_CORE
        default y
        bool "Network core driver support"
@@ -100,13 +103,6 @@ config NET_FC
          adaptor below. You also should have said Y to "SCSI support" and
          "SCSI generic support".
 
-config MII
-       tristate "Generic Media Independent Interface device support"
-       help
-         Most ethernet controllers have MII transceiver either as an external
-         or internal device.  It is safe to say Y or M here even if your
-         ethernet card lacks MII.
-
 config IFB
        tristate "Intermediate Functional Block support"
        depends on NET_CLS_ACT
@@ -244,6 +240,16 @@ config VIRTIO_NET
          This is the virtual network driver for virtio.  It can be used with
          lguest or QEMU based VMMs (like KVM or Xen).  Say Y or M.
 
+config NLMON
+       tristate "Virtual netlink monitoring device"
+       ---help---
+         This option enables a monitoring net device for netlink skbs. The
+         purpose of this is to analyze netlink messages with packet sockets.
+         Thus applications like tcpdump will be able to see local netlink
+         messages if they tap into the netlink device, record pcaps for further
+         diagnostics, etc. This is mostly intended for developers or support
+         to debug netlink issues. If unsure, say N.
+
 endif # NET_CORE
 
 config SUNGEM_PHY
index ef3d090..3fef8a8 100644 (file)
@@ -22,6 +22,7 @@ obj-$(CONFIG_TUN) += tun.o
 obj-$(CONFIG_VETH) += veth.o
 obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
 obj-$(CONFIG_VXLAN) += vxlan.o
+obj-$(CONFIG_NLMON) += nlmon.o
 
 #
 # Networking Drivers
index e02cc26..4ea8ed1 100644 (file)
@@ -1056,7 +1056,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[])
  *
  */
 
-static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct slave *slave2)
+static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2)
 {
        u8 tmp_mac_addr[ETH_ALEN];
 
@@ -1129,6 +1129,7 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla
 {
        int perm_curr_diff;
        int perm_bond_diff;
+       struct slave *found_slave;
 
        perm_curr_diff = !ether_addr_equal_64bits(slave->perm_hwaddr,
                                                  slave->dev->dev_addr);
@@ -1136,21 +1137,12 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla
                                                  bond->dev->dev_addr);
 
        if (perm_curr_diff && perm_bond_diff) {
-               struct slave *tmp_slave;
-               int i, found = 0;
-
-               bond_for_each_slave(bond, tmp_slave, i) {
-                       if (ether_addr_equal_64bits(slave->perm_hwaddr,
-                                                   tmp_slave->dev->dev_addr)) {
-                               found = 1;
-                               break;
-                       }
-               }
+               found_slave = bond_slave_has_mac(bond, slave->perm_hwaddr);
 
-               if (found) {
+               if (found_slave) {
                        /* locking: needs RTNL and nothing else */
-                       alb_swap_mac_addr(bond, slave, tmp_slave);
-                       alb_fasten_mac_swap(bond, slave, tmp_slave);
+                       alb_swap_mac_addr(slave, found_slave);
+                       alb_fasten_mac_swap(bond, slave, found_slave);
                }
        }
 }
@@ -1175,16 +1167,13 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla
  * @slave.
  *
  * assumption: this function is called before @slave is attached to the
- *            bond slave list.
- *
- * caller must hold the bond lock for write since the mac addresses are compared
- * and may be swapped.
+ *            bond slave list.
  */
 static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slave *slave)
 {
-       struct slave *tmp_slave1, *tmp_slave2, *free_mac_slave;
+       struct slave *tmp_slave1, *free_mac_slave = NULL;
        struct slave *has_bond_addr = bond->curr_active_slave;
-       int i, j, found = 0;
+       int i;
 
        if (bond->slave_cnt == 0) {
                /* this is the first slave */
@@ -1196,15 +1185,7 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav
         * slaves in the bond.
         */
        if (!ether_addr_equal_64bits(slave->perm_hwaddr, bond->dev->dev_addr)) {
-               bond_for_each_slave(bond, tmp_slave1, i) {
-                       if (ether_addr_equal_64bits(tmp_slave1->dev->dev_addr,
-                                                   slave->dev->dev_addr)) {
-                               found = 1;
-                               break;
-                       }
-               }
-
-               if (!found)
+               if (!bond_slave_has_mac(bond, slave->dev->dev_addr))
                        return 0;
 
                /* Try setting slave mac to bond address and fall-through
@@ -1215,19 +1196,8 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav
        /* The slave's address is equal to the address of the bond.
         * Search for a spare address in the bond for this slave.
         */
-       free_mac_slave = NULL;
-
        bond_for_each_slave(bond, tmp_slave1, i) {
-               found = 0;
-               bond_for_each_slave(bond, tmp_slave2, j) {
-                       if (ether_addr_equal_64bits(tmp_slave1->perm_hwaddr,
-                                                   tmp_slave2->dev->dev_addr)) {
-                               found = 1;
-                               break;
-                       }
-               }
-
-               if (!found) {
+               if (!bond_slave_has_mac(bond, tmp_slave1->perm_hwaddr)) {
                        /* no slave has tmp_slave1's perm addr
                         * as its curr addr
                         */
@@ -1607,15 +1577,7 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave)
                return res;
        }
 
-       /* caller must hold the bond lock for write since the mac addresses
-        * are compared and may be swapped.
-        */
-       read_lock(&bond->lock);
-
        res = alb_handle_addr_collision_on_attach(bond, slave);
-
-       read_unlock(&bond->lock);
-
        if (res) {
                return res;
        }
@@ -1698,7 +1660,6 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
        __acquires(&bond->curr_slave_lock)
 {
        struct slave *swap_slave;
-       int i;
 
        if (bond->curr_active_slave == new_slave) {
                return;
@@ -1720,17 +1681,8 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
        /* set the new curr_active_slave to the bonds mac address
         * i.e. swap mac addresses of old curr_active_slave and new curr_active_slave
         */
-       if (!swap_slave) {
-               struct slave *tmp_slave;
-               /* find slave that is holding the bond's mac address */
-               bond_for_each_slave(bond, tmp_slave, i) {
-                       if (ether_addr_equal_64bits(tmp_slave->dev->dev_addr,
-                                                   bond->dev->dev_addr)) {
-                               swap_slave = tmp_slave;
-                               break;
-                       }
-               }
-       }
+       if (!swap_slave)
+               swap_slave = bond_slave_has_mac(bond, bond->dev->dev_addr);
 
        /*
         * Arrange for swap_slave and new_slave to temporarily be
@@ -1750,16 +1702,12 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
        /* curr_active_slave must be set before calling alb_swap_mac_addr */
        if (swap_slave) {
                /* swap mac address */
-               alb_swap_mac_addr(bond, swap_slave, new_slave);
-       } else {
-               /* set the new_slave to the bond mac address */
-               alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr);
-       }
-
-       if (swap_slave) {
+               alb_swap_mac_addr(swap_slave, new_slave);
                alb_fasten_mac_swap(bond, swap_slave, new_slave);
                read_lock(&bond->lock);
        } else {
+               /* set the new_slave to the bond mac address */
+               alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr);
                read_lock(&bond->lock);
                alb_send_learning_packets(new_slave, bond->dev->dev_addr);
        }
@@ -1776,9 +1724,8 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
 {
        struct bonding *bond = netdev_priv(bond_dev);
        struct sockaddr *sa = addr;
-       struct slave *slave, *swap_slave;
+       struct slave *swap_slave;
        int res;
-       int i;
 
        if (!is_valid_ether_addr(sa->sa_data)) {
                return -EADDRNOTAVAIL;
@@ -1799,18 +1746,10 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
                return 0;
        }
 
-       swap_slave = NULL;
-
-       bond_for_each_slave(bond, slave, i) {
-               if (ether_addr_equal_64bits(slave->dev->dev_addr,
-                                           bond_dev->dev_addr)) {
-                       swap_slave = slave;
-                       break;
-               }
-       }
+       swap_slave = bond_slave_has_mac(bond, bond_dev->dev_addr);
 
        if (swap_slave) {
-               alb_swap_mac_addr(bond, swap_slave, bond->curr_active_slave);
+               alb_swap_mac_addr(swap_slave, bond->curr_active_slave);
                alb_fasten_mac_swap(bond, swap_slave, bond->curr_active_slave);
        } else {
                alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr);
index f975696..07f257d 100644 (file)
@@ -104,6 +104,7 @@ static char *xmit_hash_policy;
 static int arp_interval = BOND_LINK_ARP_INTERV;
 static char *arp_ip_target[BOND_MAX_ARP_TARGETS];
 static char *arp_validate;
+static char *arp_all_targets;
 static char *fail_over_mac;
 static int all_slaves_active = 0;
 static struct bond_params bonding_defaults;
@@ -166,6 +167,8 @@ module_param(arp_validate, charp, 0);
 MODULE_PARM_DESC(arp_validate, "validate src/dst of ARP probes; "
                               "0 for none (default), 1 for active, "
                               "2 for backup, 3 for all");
+module_param(arp_all_targets, charp, 0);
+MODULE_PARM_DESC(arp_all_targets, "fail on any/all arp targets timeout; 0 for any (default), 1 for all");
 module_param(fail_over_mac, charp, 0);
 MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to "
                                "the same MAC; 0 for none (default), "
@@ -216,6 +219,12 @@ const struct bond_parm_tbl xmit_hashtype_tbl[] = {
 {      NULL,                   -1},
 };
 
+const struct bond_parm_tbl arp_all_targets_tbl[] = {
+{      "any",                  BOND_ARP_TARGETS_ANY},
+{      "all",                  BOND_ARP_TARGETS_ALL},
+{      NULL,                   -1},
+};
+
 const struct bond_parm_tbl arp_validate_tbl[] = {
 {      "none",                 BOND_ARP_VALIDATE_NONE},
 {      "active",               BOND_ARP_VALIDATE_ACTIVE},
@@ -706,45 +715,6 @@ static int bond_set_allmulti(struct bonding *bond, int inc)
        return err;
 }
 
-/*
- * Add a Multicast address to slaves
- * according to mode
- */
-static void bond_mc_add(struct bonding *bond, void *addr)
-{
-       if (USES_PRIMARY(bond->params.mode)) {
-               /* write lock already acquired */
-               if (bond->curr_active_slave)
-                       dev_mc_add(bond->curr_active_slave->dev, addr);
-       } else {
-               struct slave *slave;
-               int i;
-
-               bond_for_each_slave(bond, slave, i)
-                       dev_mc_add(slave->dev, addr);
-       }
-}
-
-/*
- * Remove a multicast address from slave
- * according to mode
- */
-static void bond_mc_del(struct bonding *bond, void *addr)
-{
-       if (USES_PRIMARY(bond->params.mode)) {
-               /* write lock already acquired */
-               if (bond->curr_active_slave)
-                       dev_mc_del(bond->curr_active_slave->dev, addr);
-       } else {
-               struct slave *slave;
-               int i;
-               bond_for_each_slave(bond, slave, i) {
-                       dev_mc_del(slave->dev, addr);
-               }
-       }
-}
-
-
 static void __bond_resend_igmp_join_requests(struct net_device *dev)
 {
        struct in_device *in_dev;
@@ -810,17 +780,15 @@ static void bond_resend_igmp_join_requests_delayed(struct work_struct *work)
        bond_resend_igmp_join_requests(bond);
 }
 
-/*
- * flush all members of flush->mc_list from device dev->mc_list
+/* Flush bond's hardware addresses from slave
  */
-static void bond_mc_list_flush(struct net_device *bond_dev,
+static void bond_hw_addr_flush(struct net_device *bond_dev,
                               struct net_device *slave_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
-       struct netdev_hw_addr *ha;
 
-       netdev_for_each_mc_addr(ha, bond_dev)
-               dev_mc_del(slave_dev, ha->addr);
+       dev_uc_unsync(slave_dev, bond_dev);
+       dev_mc_unsync(slave_dev, bond_dev);
 
        if (bond->params.mode == BOND_MODE_8023AD) {
                /* del lacpdu mc addr from mc list */
@@ -832,22 +800,14 @@ static void bond_mc_list_flush(struct net_device *bond_dev,
 
 /*--------------------------- Active slave change ---------------------------*/
 
-/*
- * Update the mc list and multicast-related flags for the new and
- * old active slaves (if any) according to the multicast mode, and
- * promiscuous flags unconditionally.
+/* Update the hardware address list and promisc/allmulti for the new and
+ * old active slaves (if any).  Modes that are !USES_PRIMARY keep all
+ * slaves up date at all times; only the USES_PRIMARY modes need to call
+ * this function to swap these settings during a failover.
  */
-static void bond_mc_swap(struct bonding *bond, struct slave *new_active,
-                        struct slave *old_active)
+static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active,
+                             struct slave *old_active)
 {
-       struct netdev_hw_addr *ha;
-
-       if (!USES_PRIMARY(bond->params.mode))
-               /* nothing to do -  mc list is already up-to-date on
-                * all slaves
-                */
-               return;
-
        if (old_active) {
                if (bond->dev->flags & IFF_PROMISC)
                        dev_set_promiscuity(old_active->dev, -1);
@@ -855,10 +815,7 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active,
                if (bond->dev->flags & IFF_ALLMULTI)
                        dev_set_allmulti(old_active->dev, -1);
 
-               netif_addr_lock_bh(bond->dev);
-               netdev_for_each_mc_addr(ha, bond->dev)
-                       dev_mc_del(old_active->dev, ha->addr);
-               netif_addr_unlock_bh(bond->dev);
+               bond_hw_addr_flush(bond->dev, old_active->dev);
        }
 
        if (new_active) {
@@ -870,12 +827,29 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active,
                        dev_set_allmulti(new_active->dev, 1);
 
                netif_addr_lock_bh(bond->dev);
-               netdev_for_each_mc_addr(ha, bond->dev)
-                       dev_mc_add(new_active->dev, ha->addr);
+               dev_uc_sync(new_active->dev, bond->dev);
+               dev_mc_sync(new_active->dev, bond->dev);
                netif_addr_unlock_bh(bond->dev);
        }
 }
 
+/**
+ * bond_set_dev_addr - clone slave's address to bond
+ * @bond_dev: bond net device
+ * @slave_dev: slave net device
+ *
+ * Should be called with RTNL held.
+ */
+static void bond_set_dev_addr(struct net_device *bond_dev,
+                             struct net_device *slave_dev)
+{
+       pr_debug("bond_dev=%p slave_dev=%p slave_dev->addr_len=%d\n",
+                bond_dev, slave_dev, slave_dev->addr_len);
+       memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
+       bond_dev->addr_assign_type = NET_ADDR_STOLEN;
+       call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev);
+}
+
 /*
  * bond_do_fail_over_mac
  *
@@ -898,11 +872,9 @@ static void bond_do_fail_over_mac(struct bonding *bond,
        switch (bond->params.fail_over_mac) {
        case BOND_FOM_ACTIVE:
                if (new_active) {
-                       memcpy(bond->dev->dev_addr,  new_active->dev->dev_addr,
-                              new_active->dev->addr_len);
                        write_unlock_bh(&bond->curr_slave_lock);
                        read_unlock(&bond->lock);
-                       call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev);
+                       bond_set_dev_addr(bond->dev, new_active->dev);
                        read_lock(&bond->lock);
                        write_lock_bh(&bond->curr_slave_lock);
                }
@@ -1090,7 +1062,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
        }
 
        if (USES_PRIMARY(bond->params.mode))
-               bond_mc_swap(bond, new_active, old_active);
+               bond_hw_addr_swap(bond, new_active, old_active);
 
        if (bond_is_lb(bond)) {
                bond_alb_handle_active_change(bond, new_active);
@@ -1333,17 +1305,6 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
 
 /*---------------------------------- IOCTL ----------------------------------*/
 
-static void bond_set_dev_addr(struct net_device *bond_dev,
-                             struct net_device *slave_dev)
-{
-       pr_debug("bond_dev=%p\n", bond_dev);
-       pr_debug("slave_dev=%p\n", slave_dev);
-       pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len);
-       memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
-       bond_dev->addr_assign_type = NET_ADDR_SET;
-       call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev);
-}
-
 static netdev_features_t bond_fix_features(struct net_device *dev,
        netdev_features_t features)
 {
@@ -1425,8 +1386,6 @@ done:
 static void bond_setup_by_slave(struct net_device *bond_dev,
                                struct net_device *slave_dev)
 {
-       struct bonding *bond = netdev_priv(bond_dev);
-
        bond_dev->header_ops        = slave_dev->header_ops;
 
        bond_dev->type              = slave_dev->type;
@@ -1435,7 +1394,6 @@ static void bond_setup_by_slave(struct net_device *bond_dev,
 
        memcpy(bond_dev->broadcast, slave_dev->broadcast,
                slave_dev->addr_len);
-       bond->setup_by_slave = 1;
 }
 
 /* On bonding slaves other than the currently active slave, suppress
@@ -1533,10 +1491,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        struct bonding *bond = netdev_priv(bond_dev);
        const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
        struct slave *new_slave = NULL;
-       struct netdev_hw_addr *ha;
        struct sockaddr addr;
        int link_reporting;
-       int res = 0;
+       int res = 0, i;
 
        if (!bond->params.use_carrier &&
            slave_dev->ethtool_ops->get_link == NULL &&
@@ -1643,7 +1600,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 
        /* If this is the first slave, then we need to set the master's hardware
         * address to be the same as the slave's. */
-       if (bond->slave_cnt == 0 && bond->dev_addr_from_first)
+       if (!bond->slave_cnt && bond->dev->addr_assign_type == NET_ADDR_RANDOM)
                bond_set_dev_addr(bond->dev, slave_dev);
 
        new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
@@ -1713,10 +1670,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                        goto err_close;
        }
 
-       /* If the mode USES_PRIMARY, then the new slave gets the
-        * master's promisc (and mc) settings only if it becomes the
-        * curr_active_slave, and that is taken care of later when calling
-        * bond_change_active()
+       /* If the mode USES_PRIMARY, then the following is handled by
+        * bond_change_active_slave().
         */
        if (!USES_PRIMARY(bond->params.mode)) {
                /* set promiscuity level to new slave */
@@ -1734,9 +1689,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                }
 
                netif_addr_lock_bh(bond_dev);
-               /* upload master's mc_list to new slave */
-               netdev_for_each_mc_addr(ha, bond_dev)
-                       dev_mc_add(slave_dev, ha->addr);
+
+               dev_mc_sync_multiple(slave_dev, bond_dev);
+               dev_uc_sync_multiple(slave_dev, bond_dev);
+
                netif_addr_unlock_bh(bond_dev);
        }
 
@@ -1766,6 +1722,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 
        new_slave->last_arp_rx = jiffies -
                (msecs_to_jiffies(bond->params.arp_interval) + 1);
+       for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
+               new_slave->target_last_arp_rx[i] = new_slave->last_arp_rx;
 
        if (bond->params.miimon && !bond->params.use_carrier) {
                link_reporting = bond_check_dev_link(bond, slave_dev, 1);
@@ -1915,11 +1873,9 @@ err_dest_symlinks:
        bond_destroy_slave_symlinks(bond_dev, slave_dev);
 
 err_detach:
-       if (!USES_PRIMARY(bond->params.mode)) {
-               netif_addr_lock_bh(bond_dev);
-               bond_mc_list_flush(bond_dev, slave_dev);
-               netif_addr_unlock_bh(bond_dev);
-       }
+       if (!USES_PRIMARY(bond->params.mode))
+               bond_hw_addr_flush(bond_dev, slave_dev);
+
        bond_del_vlans_from_slave(bond, slave_dev);
        write_lock_bh(&bond->lock);
        bond_detach_slave(bond, new_slave);
@@ -2089,7 +2045,6 @@ static int __bond_release_one(struct net_device *bond_dev,
        if (bond->slave_cnt == 0) {
                bond_set_carrier(bond);
                eth_hw_addr_random(bond_dev);
-               bond->dev_addr_from_first = true;
 
                if (bond_vlan_used(bond)) {
                        pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n",
@@ -2118,9 +2073,8 @@ static int __bond_release_one(struct net_device *bond_dev,
 
        bond_del_vlans_from_slave(bond, slave_dev);
 
-       /* If the mode USES_PRIMARY, then we should only remove its
-        * promisc and mc settings if it was the curr_active_slave, but that was
-        * already taken care of above when we detached the slave
+       /* If the mode USES_PRIMARY, then this cases was handled above by
+        * bond_change_active_slave(..., NULL)
         */
        if (!USES_PRIMARY(bond->params.mode)) {
                /* unset promiscuity level from slave */
@@ -2131,10 +2085,7 @@ static int __bond_release_one(struct net_device *bond_dev,
                if (bond_dev->flags & IFF_ALLMULTI)
                        dev_set_allmulti(slave_dev, -1);
 
-               /* flush master's mc_list from slave */
-               netif_addr_lock_bh(bond_dev);
-               bond_mc_list_flush(bond_dev, slave_dev);
-               netif_addr_unlock_bh(bond_dev);
+               bond_hw_addr_flush(bond_dev, slave_dev);
        }
 
        bond_upper_dev_unlink(bond_dev, slave_dev);
@@ -2672,18 +2623,19 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
 static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32 sip, __be32 tip)
 {
        int i;
-       __be32 *targets = bond->params.arp_targets;
 
-       for (i = 0; (i < BOND_MAX_ARP_TARGETS) && targets[i]; i++) {
-               pr_debug("bva: sip %pI4 tip %pI4 t[%d] %pI4 bhti(tip) %d\n",
-                        &sip, &tip, i, &targets[i],
-                        bond_has_this_ip(bond, tip));
-               if (sip == targets[i]) {
-                       if (bond_has_this_ip(bond, tip))
-                               slave->last_arp_rx = jiffies;
-                       return;
-               }
+       if (!sip || !bond_has_this_ip(bond, tip)) {
+               pr_debug("bva: sip %pI4 tip %pI4 not found\n", &sip, &tip);
+               return;
+       }
+
+       i = bond_get_targets_ip(bond->params.arp_targets, sip);
+       if (i == -1) {
+               pr_debug("bva: sip %pI4 not found in targets\n", &sip);
+               return;
        }
+       slave->last_arp_rx = jiffies;
+       slave->target_last_arp_rx[i] = jiffies;
 }
 
 static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -2698,6 +2650,10 @@ static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
                return RX_HANDLER_ANOTHER;
 
        read_lock(&bond->lock);
+
+       if (!slave_do_arp_validate(bond, slave))
+               goto out_unlock;
+
        alen = arp_hdr_len(bond->dev);
 
        pr_debug("bond_arp_rcv: bond %s skb->dev %s\n",
@@ -2737,10 +2693,17 @@ static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
         * configuration, the ARP probe will (hopefully) travel from
         * the active, through one switch, the router, then the other
         * switch before reaching the backup.
+        *
+        * We 'trust' the arp requests if there is an active slave and
+        * it received valid arp reply(s) after it became active. This
+        * is done to avoid endless looping when we can't reach the
+        * arp_ip_target and fool ourselves with our own arp requests.
         */
        if (bond_is_active_slave(slave))
                bond_validate_arp(bond, slave, sip, tip);
-       else
+       else if (bond->curr_active_slave &&
+                time_after(slave_last_rx(bond, bond->curr_active_slave),
+                           bond->curr_active_slave->jiffies))
                bond_validate_arp(bond, slave, tip, sip);
 
 out_unlock:
@@ -3225,7 +3188,7 @@ static int bond_slave_netdev_event(unsigned long event,
 
        switch (event) {
        case NETDEV_UNREGISTER:
-               if (bond->setup_by_slave)
+               if (bond_dev->type != ARPHRD_ETHER)
                        bond_release_and_destroy(bond_dev, slave_dev);
                else
                        bond_release(bond_dev, slave_dev);
@@ -3289,7 +3252,7 @@ static int bond_slave_netdev_event(unsigned long event,
 static int bond_netdev_event(struct notifier_block *this,
                             unsigned long event, void *ptr)
 {
-       struct net_device *event_dev = (struct net_device *)ptr;
+       struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
 
        pr_debug("event_dev: %s, event: %lx\n",
                 event_dev ? event_dev->name : "None",
@@ -3672,19 +3635,6 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
        return res;
 }
 
-static bool bond_addr_in_mc_list(unsigned char *addr,
-                                struct netdev_hw_addr_list *list,
-                                int addrlen)
-{
-       struct netdev_hw_addr *ha;
-
-       netdev_hw_addr_list_for_each(ha, list)
-               if (!memcmp(ha->addr, addr, addrlen))
-                       return true;
-
-       return false;
-}
-
 static void bond_change_rx_flags(struct net_device *bond_dev, int change)
 {
        struct bonding *bond = netdev_priv(bond_dev);
@@ -3698,35 +3648,29 @@ static void bond_change_rx_flags(struct net_device *bond_dev, int change)
                                  bond_dev->flags & IFF_ALLMULTI ? 1 : -1);
 }
 
-static void bond_set_multicast_list(struct net_device *bond_dev)
+static void bond_set_rx_mode(struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
-       struct netdev_hw_addr *ha;
-       bool found;
+       struct slave *slave;
+       int i;
 
        read_lock(&bond->lock);
 
-       /* looking for addresses to add to slaves' mc list */
-       netdev_for_each_mc_addr(ha, bond_dev) {
-               found = bond_addr_in_mc_list(ha->addr, &bond->mc_list,
-                                            bond_dev->addr_len);
-               if (!found)
-                       bond_mc_add(bond, ha->addr);
-       }
-
-       /* looking for addresses to delete from slaves' list */
-       netdev_hw_addr_list_for_each(ha, &bond->mc_list) {
-               found = bond_addr_in_mc_list(ha->addr, &bond_dev->mc,
-                                            bond_dev->addr_len);
-               if (!found)
-                       bond_mc_del(bond, ha->addr);
+       if (USES_PRIMARY(bond->params.mode)) {
+               read_lock(&bond->curr_slave_lock);
+               slave = bond->curr_active_slave;
+               if (slave) {
+                       dev_uc_sync(slave->dev, bond_dev);
+                       dev_mc_sync(slave->dev, bond_dev);
+               }
+               read_unlock(&bond->curr_slave_lock);
+       } else {
+               bond_for_each_slave(bond, slave, i) {
+                       dev_uc_sync_multiple(slave->dev, bond_dev);
+                       dev_mc_sync_multiple(slave->dev, bond_dev);
+               }
        }
 
-       /* save master's multicast list */
-       __hw_addr_flush(&bond->mc_list);
-       __hw_addr_add_multiple(&bond->mc_list, &bond_dev->mc,
-                              bond_dev->addr_len, NETDEV_HW_ADDR_T_MULTICAST);
-
        read_unlock(&bond->lock);
 }
 
@@ -3871,11 +3815,10 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
        pr_debug("bond=%p, name=%s\n",
                 bond, bond_dev ? bond_dev->name : "None");
 
-       /*
-        * If fail_over_mac is set to active, do nothing and return
-        * success.  Returning an error causes ifenslave to fail.
+       /* If fail_over_mac is enabled, do nothing and return success.
+        * Returning an error causes ifenslave to fail.
         */
-       if (bond->params.fail_over_mac == BOND_FOM_ACTIVE)
+       if (bond->params.fail_over_mac)
                return 0;
 
        if (!is_valid_ether_addr(sa->sa_data))
@@ -4333,7 +4276,7 @@ static const struct net_device_ops bond_netdev_ops = {
        .ndo_get_stats64        = bond_get_stats,
        .ndo_do_ioctl           = bond_do_ioctl,
        .ndo_change_rx_flags    = bond_change_rx_flags,
-       .ndo_set_rx_mode        = bond_set_multicast_list,
+       .ndo_set_rx_mode        = bond_set_rx_mode,
        .ndo_change_mtu         = bond_change_mtu,
        .ndo_set_mac_address    = bond_set_mac_address,
        .ndo_neigh_setup        = bond_neigh_setup,
@@ -4438,8 +4381,6 @@ static void bond_uninit(struct net_device *bond_dev)
 
        bond_debug_unregister(bond);
 
-       __hw_addr_flush(&bond->mc_list);
-
        list_for_each_entry_safe(vlan, tmp, &bond->vlan_list, vlan_list) {
                list_del(&vlan->vlan_list);
                kfree(vlan);
@@ -4484,6 +4425,7 @@ int bond_parse_parm(const char *buf, const struct bond_parm_tbl *tbl)
 static int bond_check_params(struct bond_params *params)
 {
        int arp_validate_value, fail_over_mac_value, primary_reselect_value, i;
+       int arp_all_targets_value;
 
        /*
         * Convert string parameters.
@@ -4674,7 +4616,11 @@ static int bond_check_params(struct bond_params *params)
                                   arp_ip_target[i]);
                        arp_interval = 0;
                } else {
-                       arp_target[arp_ip_count++] = ip;
+                       if (bond_get_targets_ip(arp_target, ip) == -1)
+                               arp_target[arp_ip_count++] = ip;
+                       else
+                               pr_warning("Warning: duplicate address %pI4 in arp_ip_target, skipping\n",
+                                          &ip);
                }
        }
 
@@ -4705,6 +4651,18 @@ static int bond_check_params(struct bond_params *params)
        } else
                arp_validate_value = 0;
 
+       arp_all_targets_value = 0;
+       if (arp_all_targets) {
+               arp_all_targets_value = bond_parse_parm(arp_all_targets,
+                                                       arp_all_targets_tbl);
+
+               if (arp_all_targets_value == -1) {
+                       pr_err("Error: invalid arp_all_targets_value \"%s\"\n",
+                              arp_all_targets);
+                       arp_all_targets_value = 0;
+               }
+       }
+
        if (miimon) {
                pr_info("MII link monitoring set to %d ms\n", miimon);
        } else if (arp_interval) {
@@ -4769,6 +4727,7 @@ static int bond_check_params(struct bond_params *params)
        params->num_peer_notif = num_peer_notif;
        params->arp_interval = arp_interval;
        params->arp_validate = arp_validate_value;
+       params->arp_all_targets = arp_all_targets_value;
        params->updelay = updelay;
        params->downdelay = downdelay;
        params->use_carrier = use_carrier;
@@ -4845,12 +4804,9 @@ static int bond_init(struct net_device *bond_dev)
 
        /* Ensure valid dev_addr */
        if (is_zero_ether_addr(bond_dev->dev_addr) &&
-           bond_dev->addr_assign_type == NET_ADDR_PERM) {
+           bond_dev->addr_assign_type == NET_ADDR_PERM)
                eth_hw_addr_random(bond_dev);
-               bond->dev_addr_from_first = true;
-       }
 
-       __hw_addr_init(&bond->mc_list);
        return 0;
 }
 
@@ -4923,7 +4879,7 @@ static int __net_init bond_net_init(struct net *net)
 
        bond_create_proc_dir(bn);
        bond_create_sysfs(bn);
-       
+
        return 0;
 }
 
index d7434e0..dc36a3d 100644 (file)
@@ -231,8 +231,7 @@ static ssize_t bonding_show_slaves(struct device *d,
 }
 
 /*
- * Set the slaves in the current bond.  The bond interface must be
- * up for this to succeed.
+ * Set the slaves in the current bond.
  * This is supposed to be only thin wrapper for bond_enslave and bond_release.
  * All hard work should be done there.
  */
@@ -363,7 +362,6 @@ static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
 
 /*
  * Show and set the bonding transmit hash method.
- * The bond interface must be down to change the xmit hash policy.
  */
 static ssize_t bonding_show_xmit_hash(struct device *d,
                                      struct device_attribute *attr,
@@ -383,20 +381,12 @@ static ssize_t bonding_store_xmit_hash(struct device *d,
        int new_value, ret = count;
        struct bonding *bond = to_bond(d);
 
-       if (bond->dev->flags & IFF_UP) {
-               pr_err("%s: Interface is up. Unable to update xmit policy.\n",
-                      bond->dev->name);
-               ret = -EPERM;
-               goto out;
-       }
-
        new_value = bond_parse_parm(buf, xmit_hashtype_tbl);
        if (new_value < 0)  {
                pr_err("%s: Ignoring invalid xmit hash policy value %.*s.\n",
                       bond->dev->name,
                       (int)strlen(buf) - 1, buf);
                ret = -EINVAL;
-               goto out;
        } else {
                bond->params.xmit_policy = new_value;
                bond_set_mode_ops(bond, bond->params.mode);
@@ -404,7 +394,7 @@ static ssize_t bonding_store_xmit_hash(struct device *d,
                        bond->dev->name,
                        xmit_hashtype_tbl[new_value].modename, new_value);
        }
-out:
+
        return ret;
 }
 static DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR,
@@ -453,6 +443,44 @@ static ssize_t bonding_store_arp_validate(struct device *d,
 
 static DEVICE_ATTR(arp_validate, S_IRUGO | S_IWUSR, bonding_show_arp_validate,
                   bonding_store_arp_validate);
+/*
+ * Show and set arp_all_targets.
+ */
+static ssize_t bonding_show_arp_all_targets(struct device *d,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct bonding *bond = to_bond(d);
+       int value = bond->params.arp_all_targets;
+
+       return sprintf(buf, "%s %d\n", arp_all_targets_tbl[value].modename,
+                      value);
+}
+
+static ssize_t bonding_store_arp_all_targets(struct device *d,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t count)
+{
+       struct bonding *bond = to_bond(d);
+       int new_value;
+
+       new_value = bond_parse_parm(buf, arp_all_targets_tbl);
+       if (new_value < 0) {
+               pr_err("%s: Ignoring invalid arp_all_targets value %s\n",
+                      bond->dev->name, buf);
+               return -EINVAL;
+       }
+       pr_info("%s: setting arp_all_targets to %s (%d).\n",
+               bond->dev->name, arp_all_targets_tbl[new_value].modename,
+               new_value);
+
+       bond->params.arp_all_targets = new_value;
+
+       return count;
+}
+
+static DEVICE_ATTR(arp_all_targets, S_IRUGO | S_IWUSR,
+                  bonding_show_arp_all_targets, bonding_store_arp_all_targets);
 
 /*
  * Show and store fail_over_mac.  User only allowed to change the
@@ -600,10 +628,11 @@ static ssize_t bonding_store_arp_targets(struct device *d,
                                         struct device_attribute *attr,
                                         const char *buf, size_t count)
 {
-       __be32 newtarget;
-       int i = 0, done = 0, ret = count;
        struct bonding *bond = to_bond(d);
-       __be32 *targets;
+       struct slave *slave;
+       __be32 newtarget, *targets;
+       unsigned long *targets_rx;
+       int ind, i, j, ret = -EINVAL;
 
        targets = bond->params.arp_targets;
        newtarget = in_aton(buf + 1);
@@ -612,57 +641,63 @@ static ssize_t bonding_store_arp_targets(struct device *d,
                if ((newtarget == 0) || (newtarget == htonl(INADDR_BROADCAST))) {
                        pr_err("%s: invalid ARP target %pI4 specified for addition\n",
                               bond->dev->name, &newtarget);
-                       ret = -EINVAL;
                        goto out;
                }
-               /* look for an empty slot to put the target in, and check for dupes */
-               for (i = 0; (i < BOND_MAX_ARP_TARGETS) && !done; i++) {
-                       if (targets[i] == newtarget) { /* duplicate */
-                               pr_err("%s: ARP target %pI4 is already present\n",
-                                      bond->dev->name, &newtarget);
-                               ret = -EINVAL;
-                               goto out;
-                       }
-                       if (targets[i] == 0) {
-                               pr_info("%s: adding ARP target %pI4.\n",
-                                       bond->dev->name, &newtarget);
-                               done = 1;
-                               targets[i] = newtarget;
-                       }
+
+               if (bond_get_targets_ip(targets, newtarget) != -1) { /* dup */
+                       pr_err("%s: ARP target %pI4 is already present\n",
+                              bond->dev->name, &newtarget);
+                       goto out;
                }
-               if (!done) {
+
+               ind = bond_get_targets_ip(targets, 0); /* first free slot */
+               if (ind == -1) {
                        pr_err("%s: ARP target table is full!\n",
                               bond->dev->name);
-                       ret = -EINVAL;
                        goto out;
                }
 
+               pr_info("%s: adding ARP target %pI4.\n", bond->dev->name,
+                        &newtarget);
+               /* not to race with bond_arp_rcv */
+               write_lock_bh(&bond->lock);
+               bond_for_each_slave(bond, slave, i)
+                       slave->target_last_arp_rx[ind] = jiffies;
+               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);
-                       ret = -EINVAL;
                        goto out;
                }
 
-               for (i = 0; (i < BOND_MAX_ARP_TARGETS) && !done; i++) {
-                       if (targets[i] == newtarget) {
-                               int j;
-                               pr_info("%s: removing ARP target %pI4.\n",
-                                       bond->dev->name, &newtarget);
-                               for (j = i; (j < (BOND_MAX_ARP_TARGETS-1)) && targets[j+1]; j++)
-                                       targets[j] = targets[j+1];
-
-                               targets[j] = 0;
-                               done = 1;
-                       }
-               }
-               if (!done) {
-                       pr_info("%s: unable to remove nonexistent ARP target %pI4.\n",
+               ind = bond_get_targets_ip(targets, newtarget);
+               if (ind == -1) {
+                       pr_err("%s: unable to remove nonexistent ARP target %pI4.\n",
                                bond->dev->name, &newtarget);
-                       ret = -EINVAL;
                        goto out;
                }
+
+               if (ind == 0 && !targets[1] && bond->params.arp_interval)
+                       pr_warn("%s: removing last arp target with arp_interval on\n",
+                               bond->dev->name);
+
+               pr_info("%s: removing ARP target %pI4.\n", bond->dev->name,
+                       &newtarget);
+
+               write_lock_bh(&bond->lock);
+               bond_for_each_slave(bond, slave, i) {
+                       targets_rx = slave->target_last_arp_rx;
+                       j = ind;
+                       for (; (j < BOND_MAX_ARP_TARGETS-1) && targets[j+1]; j++)
+                               targets_rx[j] = targets_rx[j+1];
+                       targets_rx[j] = 0;
+               }
+               for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
+                       targets[i] = targets[i+1];
+               targets[i] = 0;
+               write_unlock_bh(&bond->lock);
        } else {
                pr_err("no command found in arp_ip_targets file for bond %s. Use +<addr> or -<addr>.\n",
                       bond->dev->name);
@@ -670,6 +705,7 @@ static ssize_t bonding_store_arp_targets(struct device *d,
                goto out;
        }
 
+       ret = count;
 out:
        return ret;
 }
@@ -1645,6 +1681,7 @@ static struct attribute *per_bond_attrs[] = {
        &dev_attr_mode.attr,
        &dev_attr_fail_over_mac.attr,
        &dev_attr_arp_validate.attr,
+       &dev_attr_arp_all_targets.attr,
        &dev_attr_arp_interval.attr,
        &dev_attr_arp_ip_target.attr,
        &dev_attr_downdelay.attr,
index f989e15..42d1c65 100644 (file)
@@ -144,6 +144,7 @@ struct bond_params {
        u8 num_peer_notif;
        int arp_interval;
        int arp_validate;
+       int arp_all_targets;
        int use_carrier;
        int fail_over_mac;
        int updelay;
@@ -179,6 +180,7 @@ struct slave {
        int    delay;
        unsigned long jiffies;
        unsigned long last_arp_rx;
+       unsigned long target_last_arp_rx[BOND_MAX_ARP_TARGETS];
        s8     link;    /* one of BOND_LINK_XXXX */
        s8     new_link;
        u8     backup:1,   /* indicates backup slave. Value corresponds with
@@ -224,14 +226,12 @@ struct bonding {
        rwlock_t lock;
        rwlock_t curr_slave_lock;
        u8       send_peer_notif;
-       s8       setup_by_slave;
        u8       igmp_retrans;
 #ifdef CONFIG_PROC_FS
        struct   proc_dir_entry *proc_entry;
        char     proc_file_name[IFNAMSIZ];
 #endif /* CONFIG_PROC_FS */
        struct   list_head bond_list;
-       struct   netdev_hw_addr_list mc_list;
        int      (*xmit_hash_policy)(struct sk_buff *, int);
        u16      rr_tx_counter;
        struct   ad_bond_info ad_info;
@@ -248,7 +248,6 @@ struct bonding {
        /* debugging support via debugfs */
        struct   dentry *debug_dir;
 #endif /* CONFIG_DEBUG_FS */
-       bool    dev_addr_from_first;
 };
 
 static inline bool bond_vlan_used(struct bonding *bond)
@@ -323,6 +322,9 @@ static inline bool bond_is_active_slave(struct slave *slave)
 #define BOND_FOM_ACTIVE                        1
 #define BOND_FOM_FOLLOW                        2
 
+#define BOND_ARP_TARGETS_ANY           0
+#define BOND_ARP_TARGETS_ALL           1
+
 #define BOND_ARP_VALIDATE_NONE         0
 #define BOND_ARP_VALIDATE_ACTIVE       (1 << BOND_STATE_ACTIVE)
 #define BOND_ARP_VALIDATE_BACKUP       (1 << BOND_STATE_BACKUP)
@@ -335,11 +337,31 @@ static inline int slave_do_arp_validate(struct bonding *bond,
        return bond->params.arp_validate & (1 << bond_slave_state(slave));
 }
 
+/* Get the oldest arp which we've received on this slave for bond's
+ * arp_targets.
+ */
+static inline unsigned long slave_oldest_target_arp_rx(struct bonding *bond,
+                                                      struct slave *slave)
+{
+       int i = 1;
+       unsigned long ret = slave->target_last_arp_rx[0];
+
+       for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++)
+               if (time_before(slave->target_last_arp_rx[i], ret))
+                       ret = slave->target_last_arp_rx[i];
+
+       return ret;
+}
+
 static inline unsigned long slave_last_rx(struct bonding *bond,
                                        struct slave *slave)
 {
-       if (slave_do_arp_validate(bond, slave))
-               return slave->last_arp_rx;
+       if (slave_do_arp_validate(bond, slave)) {
+               if (bond->params.arp_all_targets == BOND_ARP_TARGETS_ALL)
+                       return slave_oldest_target_arp_rx(bond, slave);
+               else
+                       return slave->last_arp_rx;
+       }
 
        return slave->dev->last_rx;
 }
@@ -465,12 +487,29 @@ static inline struct slave *bond_slave_has_mac(struct bonding *bond,
        return NULL;
 }
 
+/* Check if the ip is present in arp ip list, or first free slot if ip == 0
+ * Returns -1 if not found, index if found
+ */
+static inline int bond_get_targets_ip(__be32 *targets, __be32 ip)
+{
+       int i;
+
+       for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
+               if (targets[i] == ip)
+                       return i;
+               else if (targets[i] == 0)
+                       break;
+
+       return -1;
+}
+
 /* exported from bond_main.c */
 extern int bond_net_id;
 extern const struct bond_parm_tbl bond_lacp_tbl[];
 extern const struct bond_parm_tbl bond_mode_tbl[];
 extern const struct bond_parm_tbl xmit_hashtype_tbl[];
 extern const struct bond_parm_tbl arp_validate_tbl[];
+extern const struct bond_parm_tbl arp_all_targets_tbl[];
 extern const struct bond_parm_tbl fail_over_mac_tbl[];
 extern const struct bond_parm_tbl pri_reselect_tbl[];
 extern struct bond_parm_tbl ad_select_tbl[];
index 77be3cb..34dea95 100644 (file)
@@ -35,8 +35,9 @@ MODULE_ALIAS_LDISC(N_CAIF);
 #define OFF 0
 #define CAIF_MAX_MTU 4096
 
-/*This list is protected by the rtnl lock. */
+static DEFINE_SPINLOCK(ser_lock);
 static LIST_HEAD(ser_list);
+static LIST_HEAD(ser_release_list);
 
 static bool ser_loop;
 module_param(ser_loop, bool, S_IRUGO);
@@ -308,6 +309,28 @@ static void ldisc_tx_wakeup(struct tty_struct *tty)
 }
 
 
+static void ser_release(struct work_struct *work)
+{
+       struct list_head list;
+       struct ser_device *ser, *tmp;
+
+       spin_lock(&ser_lock);
+       list_replace_init(&ser_release_list, &list);
+       spin_unlock(&ser_lock);
+
+       if (!list_empty(&list)) {
+               rtnl_lock();
+               list_for_each_entry_safe(ser, tmp, &list, node) {
+                       dev_close(ser->dev);
+                       unregister_netdevice(ser->dev);
+                       debugfs_deinit(ser);
+               }
+               rtnl_unlock();
+       }
+}
+
+static DECLARE_WORK(ser_release_work, ser_release);
+
 static int ldisc_open(struct tty_struct *tty)
 {
        struct ser_device *ser;
@@ -321,6 +344,9 @@ static int ldisc_open(struct tty_struct *tty)
        if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_TTY_CONFIG))
                return -EPERM;
 
+       /* release devices to avoid name collision */
+       ser_release(NULL);
+
        sprintf(name, "cf%s", tty->name);
        dev = alloc_netdev(sizeof(*ser), name, caifdev_setup);
        if (!dev)
@@ -341,7 +367,9 @@ static int ldisc_open(struct tty_struct *tty)
                return -ENODEV;
        }
 
+       spin_lock(&ser_lock);
        list_add(&ser->node, &ser_list);
+       spin_unlock(&ser_lock);
        rtnl_unlock();
        netif_stop_queue(dev);
        update_tty_status(ser);
@@ -351,19 +379,13 @@ static int ldisc_open(struct tty_struct *tty)
 static void ldisc_close(struct tty_struct *tty)
 {
        struct ser_device *ser = tty->disc_data;
-       /* Remove may be called inside or outside of rtnl_lock */
-       int islocked = rtnl_is_locked();
 
-       if (!islocked)
-               rtnl_lock();
-       /* device is freed automagically by net-sysfs */
-       dev_close(ser->dev);
-       unregister_netdevice(ser->dev);
-       list_del(&ser->node);
-       debugfs_deinit(ser);
        tty_kref_put(ser->tty);
-       if (!islocked)
-               rtnl_unlock();
+
+       spin_lock(&ser_lock);
+       list_move(&ser->node, &ser_release_list);
+       spin_unlock(&ser_lock);
+       schedule_work(&ser_release_work);
 }
 
 /* The line discipline structure. */
@@ -438,16 +460,11 @@ static int __init caif_ser_init(void)
 
 static void __exit caif_ser_exit(void)
 {
-       struct ser_device *ser = NULL;
-       struct list_head *node;
-       struct list_head *_tmp;
-
-       list_for_each_safe(node, _tmp, &ser_list) {
-               ser = list_entry(node, struct ser_device, node);
-               dev_close(ser->dev);
-               unregister_netdevice(ser->dev);
-               list_del(node);
-       }
+       spin_lock(&ser_lock);
+       list_splice(&ser_list, &ser_release_list);
+       spin_unlock(&ser_lock);
+       ser_release(NULL);
+       cancel_work_sync(&ser_release_work);
        tty_unregister_ldisc(N_CAIF);
        debugfs_remove_recursive(debugfsdir);
 }
index e456b70..3c06947 100644 (file)
@@ -102,12 +102,9 @@ config CAN_JANZ_ICAN3
          This driver can also be built as a module. If so, the module will be
          called janz-ican3.ko.
 
-config HAVE_CAN_FLEXCAN
-       bool
-
 config CAN_FLEXCAN
        tristate "Support for Freescale FLEXCAN based chips"
-       depends on HAVE_CAN_FLEXCAN
+       depends on ARM || PPC
        ---help---
          Say Y here if you want to support for Freescale FlexCAN.
 
index db52f44..dbbe97a 100644 (file)
@@ -1220,7 +1220,7 @@ static ssize_t at91_sysfs_set_mb0_id(struct device *dev,
                goto out;
        }
 
-       err = strict_strtoul(buf, 0, &can_id);
+       err = kstrtoul(buf, 0, &can_id);
        if (err) {
                ret = err;
                goto out;
@@ -1264,8 +1264,6 @@ static const struct of_device_id at91_can_dt_ids[] = {
        }
 };
 MODULE_DEVICE_TABLE(of, at91_can_dt_ids);
-#else
-#define at91_can_dt_ids NULL
 #endif
 
 static const struct at91_devtype_data *at91_can_get_driver_data(struct platform_device *pdev)
@@ -1393,8 +1391,6 @@ static int at91_can_remove(struct platform_device *pdev)
 
        unregister_netdev(dev);
 
-       platform_set_drvdata(pdev, NULL);
-
        iounmap(priv->reg_base);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1426,7 +1422,7 @@ static struct platform_driver at91_can_driver = {
        .driver = {
                .name = KBUILD_MODNAME,
                .owner = THIS_MODULE,
-               .of_match_table = at91_can_dt_ids,
+               .of_match_table = of_match_ptr(at91_can_dt_ids),
        },
        .id_table = at91_can_id_table,
 };
index d4a15e8..a2700d2 100644 (file)
@@ -580,7 +580,7 @@ static int bfin_can_probe(struct platform_device *pdev)
        priv->pin_list = pdata;
        priv->can.clock.freq = get_sclk();
 
-       dev_set_drvdata(&pdev->dev, dev);
+       platform_set_drvdata(pdev, dev);
        SET_NETDEV_DEV(dev, &pdev->dev);
 
        dev->flags |= IFF_ECHO; /* we support local echo */
@@ -613,7 +613,7 @@ exit:
 
 static int bfin_can_remove(struct platform_device *pdev)
 {
-       struct net_device *dev = dev_get_drvdata(&pdev->dev);
+       struct net_device *dev = platform_get_drvdata(pdev);
        struct bfin_can_priv *priv = netdev_priv(dev);
        struct resource *res;
 
@@ -621,8 +621,6 @@ static int bfin_can_remove(struct platform_device *pdev)
 
        unregister_candev(dev);
 
-       dev_set_drvdata(&pdev->dev, NULL);
-
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        release_mem_region(res->start, resource_size(res));
 
@@ -635,7 +633,7 @@ static int bfin_can_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM
 static int bfin_can_suspend(struct platform_device *pdev, pm_message_t mesg)
 {
-       struct net_device *dev = dev_get_drvdata(&pdev->dev);
+       struct net_device *dev = platform_get_drvdata(pdev);
        struct bfin_can_priv *priv = netdev_priv(dev);
        struct bfin_can_regs __iomem *reg = priv->membase;
        int timeout = BFIN_CAN_TIMEOUT;
@@ -658,7 +656,7 @@ static int bfin_can_suspend(struct platform_device *pdev, pm_message_t mesg)
 
 static int bfin_can_resume(struct platform_device *pdev)
 {
-       struct net_device *dev = dev_get_drvdata(&pdev->dev);
+       struct net_device *dev = platform_get_drvdata(pdev);
        struct bfin_can_priv *priv = netdev_priv(dev);
        struct bfin_can_regs __iomem *reg = priv->membase;
 
index d63b919..b918c73 100644 (file)
@@ -201,8 +201,8 @@ static int c_can_plat_probe(struct platform_device *pdev)
                        priv->instance = pdev->id;
 
                res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-               priv->raminit_ctrlreg = devm_request_and_ioremap(&pdev->dev, res);
-               if (!priv->raminit_ctrlreg || priv->instance < 0)
+               priv->raminit_ctrlreg = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(priv->raminit_ctrlreg) || priv->instance < 0)
                        dev_info(&pdev->dev, "control memory is not used for raminit\n");
                else
                        priv->raminit = c_can_hw_raminit;
@@ -234,7 +234,6 @@ static int c_can_plat_probe(struct platform_device *pdev)
        return 0;
 
 exit_free_device:
-       platform_set_drvdata(pdev, NULL);
        free_c_can_dev(dev);
 exit_iounmap:
        iounmap(addr);
@@ -255,7 +254,6 @@ static int c_can_plat_remove(struct platform_device *pdev)
        struct resource *mem;
 
        unregister_c_can_dev(dev);
-       platform_set_drvdata(pdev, NULL);
 
        free_c_can_dev(dev);
        iounmap(priv->base);
index 8eaaac8..87a47c0 100644 (file)
@@ -265,7 +265,7 @@ static int cc770_isa_probe(struct platform_device *pdev)
        else
                priv->clkout = COR_DEFAULT;
 
-       dev_set_drvdata(&pdev->dev, dev);
+       platform_set_drvdata(pdev, dev);
        SET_NETDEV_DEV(dev, &pdev->dev);
 
        err = register_cc770dev(dev);
@@ -293,12 +293,11 @@ static int cc770_isa_probe(struct platform_device *pdev)
 
 static int cc770_isa_remove(struct platform_device *pdev)
 {
-       struct net_device *dev = dev_get_drvdata(&pdev->dev);
+       struct net_device *dev = platform_get_drvdata(pdev);
        struct cc770_priv *priv = netdev_priv(dev);
        int idx = pdev->id;
 
        unregister_cc770dev(dev);
-       dev_set_drvdata(&pdev->dev, NULL);
 
        if (mem[idx]) {
                iounmap(priv->reg_base);
index d0f6bfc..034bdd8 100644 (file)
@@ -216,7 +216,7 @@ static int cc770_platform_probe(struct platform_device *pdev)
                 priv->reg_base, dev->irq, priv->can.clock.freq,
                 priv->cpu_interface, priv->bus_config, priv->clkout);
 
-       dev_set_drvdata(&pdev->dev, dev);
+       platform_set_drvdata(pdev, dev);
        SET_NETDEV_DEV(dev, &pdev->dev);
 
        err = register_cc770dev(dev);
@@ -240,7 +240,7 @@ exit_release_mem:
 
 static int cc770_platform_remove(struct platform_device *pdev)
 {
-       struct net_device *dev = dev_get_drvdata(&pdev->dev);
+       struct net_device *dev = platform_get_drvdata(pdev);
        struct cc770_priv *priv = netdev_priv(dev);
        struct resource *mem;
 
index 769d29e..7b0be09 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/can/dev.h>
 #include <linux/can/error.h>
 #include <linux/can/led.h>
-#include <linux/can/platform/flexcan.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/if_arp.h>
@@ -37,7 +36,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
-#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
 
 #define DRV_NAME                       "flexcan"
 
@@ -212,6 +211,7 @@ struct flexcan_priv {
        struct clk *clk_per;
        struct flexcan_platform_data *pdata;
        const struct flexcan_devtype_data *devtype_data;
+       struct regulator *reg_xceiver;
 };
 
 static struct flexcan_devtype_data fsl_p1010_devtype_data = {
@@ -259,15 +259,6 @@ static inline void flexcan_write(u32 val, void __iomem *addr)
 }
 #endif
 
-/*
- * Swtich transceiver on or off
- */
-static void flexcan_transceiver_switch(const struct flexcan_priv *priv, int on)
-{
-       if (priv->pdata && priv->pdata->transceiver_switch)
-               priv->pdata->transceiver_switch(on);
-}
-
 static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv,
                                              u32 reg_esr)
 {
@@ -800,7 +791,11 @@ static int flexcan_chip_start(struct net_device *dev)
        if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES)
                flexcan_write(0x0, &regs->rxfgmask);
 
-       flexcan_transceiver_switch(priv, 1);
+       if (priv->reg_xceiver)  {
+               err = regulator_enable(priv->reg_xceiver);
+               if (err)
+                       goto out;
+       }
 
        /* synchronize with the can bus */
        reg_mcr = flexcan_read(&regs->mcr);
@@ -843,7 +838,8 @@ static void flexcan_chip_stop(struct net_device *dev)
        reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT;
        flexcan_write(reg, &regs->mcr);
 
-       flexcan_transceiver_switch(priv, 0);
+       if (priv->reg_xceiver)
+               regulator_disable(priv->reg_xceiver);
        priv->can.state = CAN_STATE_STOPPED;
 
        return;
@@ -1004,16 +1000,11 @@ static int flexcan_probe(struct platform_device *pdev)
        struct flexcan_priv *priv;
        struct resource *mem;
        struct clk *clk_ipg = NULL, *clk_per = NULL;
-       struct pinctrl *pinctrl;
        void __iomem *base;
        resource_size_t mem_size;
        int err, irq;
        u32 clock_freq = 0;
 
-       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
-       if (IS_ERR(pinctrl))
-               return PTR_ERR(pinctrl);
-
        if (pdev->dev.of_node)
                of_property_read_u32(pdev->dev.of_node,
                                                "clock-frequency", &clock_freq);
@@ -1090,6 +1081,10 @@ static int flexcan_probe(struct platform_device *pdev)
        priv->pdata = pdev->dev.platform_data;
        priv->devtype_data = devtype_data;
 
+       priv->reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver");
+       if (IS_ERR(priv->reg_xceiver))
+               priv->reg_xceiver = NULL;
+
        netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT);
 
        dev_set_drvdata(&pdev->dev, dev);
@@ -1127,7 +1122,6 @@ static int flexcan_remove(struct platform_device *pdev)
        struct resource *mem;
 
        unregister_flexcandev(dev);
-       platform_set_drvdata(pdev, NULL);
        iounmap(priv->base);
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1138,10 +1132,10 @@ static int flexcan_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int flexcan_suspend(struct platform_device *pdev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int flexcan_suspend(struct device *device)
 {
-       struct net_device *dev = platform_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct flexcan_priv *priv = netdev_priv(dev);
 
        flexcan_chip_disable(priv);
@@ -1155,9 +1149,9 @@ static int flexcan_suspend(struct platform_device *pdev, pm_message_t state)
        return 0;
 }
 
-static int flexcan_resume(struct platform_device *pdev)
+static int flexcan_resume(struct device *device)
 {
-       struct net_device *dev = platform_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct flexcan_priv *priv = netdev_priv(dev);
 
        priv->can.state = CAN_STATE_ERROR_ACTIVE;
@@ -1169,21 +1163,19 @@ static int flexcan_resume(struct platform_device *pdev)
 
        return 0;
 }
-#else
-#define flexcan_suspend NULL
-#define flexcan_resume NULL
-#endif
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume);
 
 static struct platform_driver flexcan_driver = {
        .driver = {
                .name = DRV_NAME,
                .owner = THIS_MODULE,
+               .pm = &flexcan_pm_ops,
                .of_match_table = flexcan_of_match,
        },
        .probe = flexcan_probe,
        .remove = flexcan_remove,
-       .suspend = flexcan_suspend,
-       .resume = flexcan_resume,
        .id_table = flexcan_id_table,
 };
 
index 17fbc7a..6aa737a 100644 (file)
@@ -1646,7 +1646,7 @@ static int grcan_setup_netdev(struct platform_device *ofdev,
        if (err)
                goto exit_free_candev;
 
-       dev_set_drvdata(&ofdev->dev, dev);
+       platform_set_drvdata(ofdev, dev);
 
        /* Reset device to allow bit-timing to be set. No need to call
         * grcan_reset at this stage. That is done in grcan_open.
@@ -1683,10 +1683,9 @@ static int grcan_probe(struct platform_device *ofdev)
        }
 
        res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
-       base = devm_request_and_ioremap(&ofdev->dev, res);
-       if (!base) {
-               dev_err(&ofdev->dev, "couldn't map IO resource\n");
-               err = -EADDRNOTAVAIL;
+       base = devm_ioremap_resource(&ofdev->dev, res);
+       if (IS_ERR(base)) {
+               err = PTR_ERR(base);
                goto exit_error;
        }
 
@@ -1716,13 +1715,12 @@ exit_error:
 
 static int grcan_remove(struct platform_device *ofdev)
 {
-       struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+       struct net_device *dev = platform_get_drvdata(ofdev);
        struct grcan_priv *priv = netdev_priv(dev);
 
        unregister_candev(dev); /* Will in turn call grcan_close */
 
        irq_dispose_mapping(dev->irq);
-       dev_set_drvdata(&ofdev->dev, NULL);
        netif_napi_del(&priv->napi);
        free_candev(dev);
 
index c4bc1d2..36bd6fa 100644 (file)
@@ -1734,7 +1734,7 @@ static ssize_t ican3_sysfs_set_term(struct device *dev,
        unsigned long enable;
        int ret;
 
-       if (strict_strtoul(buf, 0, &enable))
+       if (kstrtoul(buf, 0, &enable))
                return -EINVAL;
 
        ret = ican3_set_termination(mod, enable);
index f27fca6..a3d99a8 100644 (file)
@@ -88,9 +88,9 @@ EXPORT_SYMBOL_GPL(devm_can_led_init);
 
 /* NETDEV rename notifier to rename the associated led triggers too */
 static int can_led_notifier(struct notifier_block *nb, unsigned long msg,
-                       void *data)
+                           void *ptr)
 {
-       struct net_device *netdev = data;
+       struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
        struct can_priv *priv = safe_candev_priv(netdev);
        char name[CAN_LED_NAME_SZ];
 
index 668850e..5b0ee8e 100644 (file)
@@ -302,7 +302,7 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev)
                goto exit_free_mscan;
        }
 
-       dev_set_drvdata(&ofdev->dev, dev);
+       platform_set_drvdata(ofdev, dev);
 
        dev_info(&ofdev->dev, "MSCAN at 0x%p, irq %d, clock %d Hz\n",
                 priv->reg_base, dev->irq, priv->can.clock.freq);
@@ -321,11 +321,9 @@ exit_unmap_mem:
 
 static int mpc5xxx_can_remove(struct platform_device *ofdev)
 {
-       struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+       struct net_device *dev = platform_get_drvdata(ofdev);
        struct mscan_priv *priv = netdev_priv(dev);
 
-       dev_set_drvdata(&ofdev->dev, NULL);
-
        unregister_mscandev(dev);
        iounmap(priv->reg_base);
        irq_dispose_mapping(dev->irq);
@@ -338,7 +336,7 @@ static int mpc5xxx_can_remove(struct platform_device *ofdev)
 static struct mscan_regs saved_regs;
 static int mpc5xxx_can_suspend(struct platform_device *ofdev, pm_message_t state)
 {
-       struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+       struct net_device *dev = platform_get_drvdata(ofdev);
        struct mscan_priv *priv = netdev_priv(dev);
        struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
 
@@ -349,7 +347,7 @@ static int mpc5xxx_can_suspend(struct platform_device *ofdev, pm_message_t state
 
 static int mpc5xxx_can_resume(struct platform_device *ofdev)
 {
-       struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+       struct net_device *dev = platform_get_drvdata(ofdev);
        struct mscan_priv *priv = netdev_priv(dev);
        struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
 
index 5c8da46..06a2823 100644 (file)
@@ -197,7 +197,7 @@ static int sja1000_isa_probe(struct platform_device *pdev)
        else
                priv->cdr = CDR_DEFAULT;
 
-       dev_set_drvdata(&pdev->dev, dev);
+       platform_set_drvdata(pdev, dev);
        SET_NETDEV_DEV(dev, &pdev->dev);
 
        err = register_sja1000dev(dev);
@@ -225,12 +225,11 @@ static int sja1000_isa_probe(struct platform_device *pdev)
 
 static int sja1000_isa_remove(struct platform_device *pdev)
 {
-       struct net_device *dev = dev_get_drvdata(&pdev->dev);
+       struct net_device *dev = platform_get_drvdata(pdev);
        struct sja1000_priv *priv = netdev_priv(dev);
        int idx = pdev->id;
 
        unregister_sja1000dev(dev);
-       dev_set_drvdata(&pdev->dev, NULL);
 
        if (mem[idx]) {
                iounmap(priv->reg_base);
index 8e0c4a0..31ad339 100644 (file)
@@ -72,13 +72,11 @@ static void sja1000_ofp_write_reg(const struct sja1000_priv *priv,
 
 static int sja1000_ofp_remove(struct platform_device *ofdev)
 {
-       struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+       struct net_device *dev = platform_get_drvdata(ofdev);
        struct sja1000_priv *priv = netdev_priv(dev);
        struct device_node *np = ofdev->dev.of_node;
        struct resource res;
 
-       dev_set_drvdata(&ofdev->dev, NULL);
-
        unregister_sja1000dev(dev);
        free_sja1000dev(dev);
        iounmap(priv->reg_base);
@@ -181,7 +179,7 @@ static int sja1000_ofp_probe(struct platform_device *ofdev)
                 priv->reg_base, dev->irq, priv->can.clock.freq,
                 priv->ocr, priv->cdr);
 
-       dev_set_drvdata(&ofdev->dev, dev);
+       platform_set_drvdata(ofdev, dev);
        SET_NETDEV_DEV(dev, &ofdev->dev);
 
        err = register_sja1000dev(dev);
index 21619bb..8e259c5 100644 (file)
@@ -135,7 +135,7 @@ static int sp_probe(struct platform_device *pdev)
                break;
        }
 
-       dev_set_drvdata(&pdev->dev, dev);
+       platform_set_drvdata(pdev, dev);
        SET_NETDEV_DEV(dev, &pdev->dev);
 
        err = register_sja1000dev(dev);
@@ -161,12 +161,11 @@ static int sp_probe(struct platform_device *pdev)
 
 static int sp_remove(struct platform_device *pdev)
 {
-       struct net_device *dev = dev_get_drvdata(&pdev->dev);
+       struct net_device *dev = platform_get_drvdata(pdev);
        struct sja1000_priv *priv = netdev_priv(dev);
        struct resource *res;
 
        unregister_sja1000dev(dev);
-       dev_set_drvdata(&pdev->dev, NULL);
 
        if (priv->reg_base)
                iounmap(priv->reg_base);
index 06b7e09..874188b 100644 (file)
@@ -161,7 +161,7 @@ static void slc_bump(struct slcan *sl)
 
        sl->rbuff[dlc_pos] = 0; /* terminate can_id string */
 
-       if (strict_strtoul(sl->rbuff+1, 16, &ultmp))
+       if (kstrtoul(sl->rbuff+1, 16, &ultmp))
                return;
 
        cf.can_id = ultmp;
index 3a2b456..65eef1e 100644 (file)
@@ -594,7 +594,7 @@ static ssize_t store_output(struct device *dev, struct device_attribute *attr,
        unsigned long val;
        int ret;
 
-       ret = strict_strtoul(buf, 0, &val);
+       ret = kstrtoul(buf, 0, &val);
        if (ret < 0)
                return ret;
        val &= 0xFF;
index f21fc37..3a349a2 100644 (file)
@@ -1001,7 +1001,6 @@ static int ti_hecc_remove(struct platform_device *pdev)
        iounmap(priv->base);
        release_mem_region(res->start, resource_size(res));
        free_candev(ndev);
-       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
index adb4bf5..ede8daa 100644 (file)
@@ -723,25 +723,6 @@ el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
                pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n",
                           dev->name, skb->len, inw(ioaddr + EL3_STATUS));
        }
-#if 0
-#ifndef final_version
-       {       /* Error-checking code, delete someday. */
-               ushort status = inw(ioaddr + EL3_STATUS);
-               if (status & 0x0001 &&          /* IRQ line active, missed one. */
-                   inw(ioaddr + EL3_STATUS) & 1) {                     /* Make sure. */
-                       pr_debug("%s: Missed interrupt, status then %04x now %04x"
-                                  "  Tx %2.2x Rx %4.4x.\n", dev->name, status,
-                                  inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
-                                  inw(ioaddr + RX_STATUS));
-                       /* Fake interrupt trigger by masking, acknowledge interrupts. */
-                       outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
-                       outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
-                                ioaddr + EL3_CMD);
-                       outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
-               }
-       }
-#endif
-#endif
        /*
         *      We lock the driver against other processors. Note
         *      we don't need to lock versus the IRQ as we suspended
index 072c6f1..ad5272b 100644 (file)
@@ -1012,10 +1012,8 @@ static int vortex_init_one(struct pci_dev *pdev,
                goto out;
 
        rc = pci_request_regions(pdev, DRV_NAME);
-       if (rc < 0) {
-               pci_disable_device(pdev);
-               goto out;
-       }
+       if (rc < 0)
+               goto out_disable;
 
        unit = vortex_cards_found;
 
@@ -1032,23 +1030,24 @@ static int vortex_init_one(struct pci_dev *pdev,
        if (!ioaddr) /* If mapping fails, fall-back to BAR 0... */
                ioaddr = pci_iomap(pdev, 0, 0);
        if (!ioaddr) {
-               pci_release_regions(pdev);
-               pci_disable_device(pdev);
                rc = -ENOMEM;
-               goto out;
+               goto out_release;
        }
 
        rc = vortex_probe1(&pdev->dev, ioaddr, pdev->irq,
                           ent->driver_data, unit);
-       if (rc < 0) {
-               pci_iounmap(pdev, ioaddr);
-               pci_release_regions(pdev);
-               pci_disable_device(pdev);
-               goto out;
-       }
+       if (rc < 0)
+               goto out_iounmap;
 
        vortex_cards_found++;
+       goto out;
 
+out_iounmap:
+       pci_iounmap(pdev, ioaddr);
+out_release:
+       pci_release_regions(pdev);
+out_disable:
+       pci_disable_device(pdev);
 out:
        return rc;
 }
@@ -1473,7 +1472,7 @@ static int vortex_probe1(struct device *gendev, void __iomem *ioaddr, int irq,
 
        if (pdev) {
                vp->pm_state_valid = 1;
-               pci_save_state(VORTEX_PCI(vp));
+               pci_save_state(pdev);
                acpi_set_WOL(dev);
        }
        retval = register_netdev(dev);
@@ -3233,21 +3232,20 @@ static void vortex_remove_one(struct pci_dev *pdev)
        vp = netdev_priv(dev);
 
        if (vp->cb_fn_base)
-               pci_iounmap(VORTEX_PCI(vp), vp->cb_fn_base);
+               pci_iounmap(pdev, vp->cb_fn_base);
 
        unregister_netdev(dev);
 
-       if (VORTEX_PCI(vp)) {
-               pci_set_power_state(VORTEX_PCI(vp), PCI_D0);    /* Go active */
-               if (vp->pm_state_valid)
-                       pci_restore_state(VORTEX_PCI(vp));
-               pci_disable_device(VORTEX_PCI(vp));
-       }
+       pci_set_power_state(pdev, PCI_D0);      /* Go active */
+       if (vp->pm_state_valid)
+               pci_restore_state(pdev);
+       pci_disable_device(pdev);
+
        /* Should really use issue_and_wait() here */
        iowrite16(TotalReset | ((vp->drv_flags & EEPROM_RESET) ? 0x04 : 0x14),
             vp->ioaddr + EL3_CMD);
 
-       pci_iounmap(VORTEX_PCI(vp), vp->ioaddr);
+       pci_iounmap(pdev, vp->ioaddr);
 
        pci_free_consistent(pdev,
                                                sizeof(struct boom_rx_desc) * RX_RING_SIZE
index 1c71c76..f00c763 100644 (file)
@@ -67,7 +67,6 @@ config PCMCIA_3C589
 config VORTEX
        tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support"
        depends on (PCI || EISA) && HAS_IOPORT
-       select NET_CORE
        select MII
        ---help---
          This option enables driver support for a large number of 10Mbps and
index 47618e5..b2e8405 100644 (file)
@@ -849,7 +849,6 @@ static int ne_drv_remove(struct platform_device *pdev)
                free_irq(dev->irq, dev);
                release_region(dev->base_addr, NE_IO_EXTENT);
                free_netdev(dev);
-               platform_set_drvdata(pdev, NULL);
        }
        return 0;
 }
index 587a885..9220108 100644 (file)
@@ -676,7 +676,7 @@ static int ne2k_pci_resume (struct pci_dev *pdev)
        struct net_device *dev = pci_get_drvdata (pdev);
        int rc;
 
-       pci_set_power_state(pdev, 0);
+       pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
 
        rc = pci_enable_device(pdev);
index ed956e0..2037080 100644 (file)
@@ -20,9 +20,11 @@ config SUNGEM_PHY
 source "drivers/net/ethernet/3com/Kconfig"
 source "drivers/net/ethernet/adaptec/Kconfig"
 source "drivers/net/ethernet/aeroflex/Kconfig"
+source "drivers/net/ethernet/allwinner/Kconfig"
 source "drivers/net/ethernet/alteon/Kconfig"
 source "drivers/net/ethernet/amd/Kconfig"
 source "drivers/net/ethernet/apple/Kconfig"
+source "drivers/net/ethernet/arc/Kconfig"
 source "drivers/net/ethernet/atheros/Kconfig"
 source "drivers/net/ethernet/cadence/Kconfig"
 source "drivers/net/ethernet/adi/Kconfig"
@@ -63,7 +65,6 @@ config JME
        tristate "JMicron(R) PCI-Express Gigabit Ethernet support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          This driver supports the PCI-Express gigabit ethernet adapters
@@ -95,7 +96,6 @@ config FEALNX
        tristate "Myson MTD-8xx PCI Ethernet support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          Say Y here to support the Myson MTD-800 family of PCI-based Ethernet
@@ -106,7 +106,6 @@ source "drivers/net/ethernet/8390/Kconfig"
 
 config NET_NETX
        tristate "NetX Ethernet support"
-       select NET_CORE
        select MII
        depends on ARCH_NETX
        ---help---
@@ -124,7 +123,6 @@ source "drivers/net/ethernet/oki-semi/Kconfig"
 config ETHOC
        tristate "OpenCores 10/100 Mbps Ethernet MAC support"
        depends on HAS_IOMEM && HAS_DMA
-       select NET_CORE
        select MII
        select PHYLIB
        select CRC32
index 8268d85..390bd0b 100644 (file)
@@ -6,9 +6,11 @@ obj-$(CONFIG_NET_VENDOR_3COM) += 3com/
 obj-$(CONFIG_NET_VENDOR_8390) += 8390/
 obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/
 obj-$(CONFIG_GRETH) += aeroflex/
+obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/
 obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/
 obj-$(CONFIG_NET_VENDOR_AMD) += amd/
 obj-$(CONFIG_NET_VENDOR_APPLE) += apple/
+obj-$(CONFIG_NET_VENDOR_ARC) += arc/
 obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/
 obj-$(CONFIG_NET_CADENCE) += cadence/
 obj-$(CONFIG_NET_BFIN) += adi/
index 0bff571..5c804bb 100644 (file)
@@ -22,7 +22,6 @@ config ADAPTEC_STARFIRE
        tristate "Adaptec Starfire/DuraLAN support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          Say Y here if you have an Adaptec Starfire (or DuraLAN) PCI network
index a948160..f952fff 100644 (file)
@@ -23,7 +23,6 @@ config BFIN_MAC
        tristate "Blackfin on-chip MAC support"
        depends on (BF516 || BF518 || BF526 || BF527 || BF536 || BF537)
        select CRC32
-       select NET_CORE
        select MII
        select PHYLIB
        select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE
index dada66b..e904b38 100644 (file)
@@ -1719,7 +1719,6 @@ out_err_mii_probe:
        mdiobus_unregister(lp->mii_bus);
        mdiobus_free(lp->mii_bus);
 out_err_probe_mac:
-       platform_set_drvdata(pdev, NULL);
        free_netdev(ndev);
 
        return rc;
@@ -1732,8 +1731,6 @@ static int bfin_mac_remove(struct platform_device *pdev)
 
        bfin_phc_release(lp);
 
-       platform_set_drvdata(pdev, NULL);
-
        lp->mii_bus->priv = NULL;
 
        unregister_netdev(ndev);
@@ -1868,7 +1865,6 @@ static int bfin_mii_bus_remove(struct platform_device *pdev)
        struct bfin_mii_bus_platform_data *mii_bus_pd =
                dev_get_platdata(&pdev->dev);
 
-       platform_set_drvdata(pdev, NULL);
        mdiobus_unregister(miibus);
        kfree(miibus->irq);
        mdiobus_free(miibus);
index 2692954..7ff4b30 100644 (file)
@@ -1565,7 +1565,7 @@ error1:
 
 static int greth_of_remove(struct platform_device *of_dev)
 {
-       struct net_device *ndev = dev_get_drvdata(&of_dev->dev);
+       struct net_device *ndev = platform_get_drvdata(of_dev);
        struct greth_private *greth = netdev_priv(ndev);
 
        /* Free descriptor areas */
@@ -1573,8 +1573,6 @@ static int greth_of_remove(struct platform_device *of_dev)
 
        dma_free_coherent(&of_dev->dev, 1024, greth->tx_bd_base, greth->tx_bd_base_phys);
 
-       dev_set_drvdata(&of_dev->dev, NULL);
-
        if (greth->phy)
                phy_stop(greth->phy);
        mdiobus_unregister(greth->mdio);
diff --git a/drivers/net/ethernet/allwinner/Kconfig b/drivers/net/ethernet/allwinner/Kconfig
new file mode 100644 (file)
index 0000000..53ad213
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Allwinner device configuration
+#
+
+config NET_VENDOR_ALLWINNER
+       bool "Allwinner devices"
+       default y
+       depends on ARCH_SUNXI
+       ---help---
+         If you have a network (Ethernet) card belonging to this
+        class, say Y and read the Ethernet-HOWTO, available from
+        <http://www.tldp.org/docs.html#howto>.
+
+        Note that the answer to this question doesn't directly
+        affect the kernel: saying N will just cause the configurator
+        to skip all the questions about Allwinner cards. If you say Y,
+        you will be asked for your specific card in the following
+        questions.
+
+if NET_VENDOR_ALLWINNER
+
+config SUN4I_EMAC
+        tristate "Allwinner A10 EMAC support"
+       depends on ARCH_SUNXI
+       depends on OF
+       select CRC32
+       select MII
+       select PHYLIB
+        ---help---
+          Support for Allwinner A10 EMAC ethernet driver.
+
+          To compile this driver as a module, choose M here.  The module
+          will be called sun4i-emac.
+
+endif # NET_VENDOR_ALLWINNER
diff --git a/drivers/net/ethernet/allwinner/Makefile b/drivers/net/ethernet/allwinner/Makefile
new file mode 100644 (file)
index 0000000..03129f7
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for the Allwinner device drivers.
+#
+
+obj-$(CONFIG_SUN4I_EMAC) += sun4i-emac.o
diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c
new file mode 100644 (file)
index 0000000..50b853a
--- /dev/null
@@ -0,0 +1,954 @@
+/*
+ * Allwinner EMAC Fast Ethernet driver for Linux.
+ *
+ * Copyright 2012-2013 Stefan Roese <sr@denx.de>
+ * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * Based on the Linux driver provided by Allwinner:
+ * Copyright (C) 1997  Sten Wang
+ *
+ * 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/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+
+#include "sun4i-emac.h"
+
+#define DRV_NAME               "sun4i-emac"
+#define DRV_VERSION            "1.02"
+
+#define EMAC_MAX_FRAME_LEN     0x0600
+
+/* Transmit timeout, default 5 seconds. */
+static int watchdog = 5000;
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
+
+/* EMAC register address locking.
+ *
+ * The EMAC uses an address register to control where data written
+ * to the data register goes. This means that the address register
+ * must be preserved over interrupts or similar calls.
+ *
+ * During interrupt and other critical calls, a spinlock is used to
+ * protect the system, but the calls themselves save the address
+ * in the address register in case they are interrupting another
+ * access to the device.
+ *
+ * For general accesses a lock is provided so that calls which are
+ * allowed to sleep are serialised so that the address register does
+ * not need to be saved. This lock also serves to serialise access
+ * to the EEPROM and PHY access registers which are shared between
+ * these two devices.
+ */
+
+/* The driver supports the original EMACE, and now the two newer
+ * devices, EMACA and EMACB.
+ */
+
+struct emac_board_info {
+       struct clk              *clk;
+       struct device           *dev;
+       struct platform_device  *pdev;
+       spinlock_t              lock;
+       void __iomem            *membase;
+       u32                     msg_enable;
+       struct net_device       *ndev;
+       struct sk_buff          *skb_last;
+       u16                     tx_fifo_stat;
+
+       int                     emacrx_completed_flag;
+
+       struct phy_device       *phy_dev;
+       struct device_node      *phy_node;
+       unsigned int            link;
+       unsigned int            speed;
+       unsigned int            duplex;
+
+       phy_interface_t         phy_interface;
+};
+
+static void emac_update_speed(struct net_device *dev)
+{
+       struct emac_board_info *db = netdev_priv(dev);
+       unsigned int reg_val;
+
+       /* set EMAC SPEED, depend on PHY  */
+       reg_val = readl(db->membase + EMAC_MAC_SUPP_REG);
+       reg_val &= ~(0x1 << 8);
+       if (db->speed == SPEED_100)
+               reg_val |= 1 << 8;
+       writel(reg_val, db->membase + EMAC_MAC_SUPP_REG);
+}
+
+static void emac_update_duplex(struct net_device *dev)
+{
+       struct emac_board_info *db = netdev_priv(dev);
+       unsigned int reg_val;
+
+       /* set duplex depend on phy */
+       reg_val = readl(db->membase + EMAC_MAC_CTL1_REG);
+       reg_val &= ~EMAC_MAC_CTL1_DUPLEX_EN;
+       if (db->duplex)
+               reg_val |= EMAC_MAC_CTL1_DUPLEX_EN;
+       writel(reg_val, db->membase + EMAC_MAC_CTL1_REG);
+}
+
+static void emac_handle_link_change(struct net_device *dev)
+{
+       struct emac_board_info *db = netdev_priv(dev);
+       struct phy_device *phydev = db->phy_dev;
+       unsigned long flags;
+       int status_change = 0;
+
+       if (phydev->link) {
+               if (db->speed != phydev->speed) {
+                       spin_lock_irqsave(&db->lock, flags);
+                       db->speed = phydev->speed;
+                       emac_update_speed(dev);
+                       spin_unlock_irqrestore(&db->lock, flags);
+                       status_change = 1;
+               }
+
+               if (db->duplex != phydev->duplex) {
+                       spin_lock_irqsave(&db->lock, flags);
+                       db->duplex = phydev->duplex;
+                       emac_update_duplex(dev);
+                       spin_unlock_irqrestore(&db->lock, flags);
+                       status_change = 1;
+               }
+       }
+
+       if (phydev->link != db->link) {
+               if (!phydev->link) {
+                       db->speed = 0;
+                       db->duplex = -1;
+               }
+               db->link = phydev->link;
+
+               status_change = 1;
+       }
+
+       if (status_change)
+               phy_print_status(phydev);
+}
+
+static int emac_mdio_probe(struct net_device *dev)
+{
+       struct emac_board_info *db = netdev_priv(dev);
+
+       /* to-do: PHY interrupts are currently not supported */
+
+       /* attach the mac to the phy */
+       db->phy_dev = of_phy_connect(db->ndev, db->phy_node,
+                                    &emac_handle_link_change, 0,
+                                    db->phy_interface);
+       if (!db->phy_dev) {
+               netdev_err(db->ndev, "could not find the PHY\n");
+               return -ENODEV;
+       }
+
+       /* mask with MAC supported features */
+       db->phy_dev->supported &= PHY_BASIC_FEATURES;
+       db->phy_dev->advertising = db->phy_dev->supported;
+
+       db->link = 0;
+       db->speed = 0;
+       db->duplex = -1;
+
+       return 0;
+}
+
+static void emac_mdio_remove(struct net_device *dev)
+{
+       struct emac_board_info *db = netdev_priv(dev);
+
+       phy_disconnect(db->phy_dev);
+       db->phy_dev = NULL;
+}
+
+static void emac_reset(struct emac_board_info *db)
+{
+       dev_dbg(db->dev, "resetting device\n");
+
+       /* RESET device */
+       writel(0, db->membase + EMAC_CTL_REG);
+       udelay(200);
+       writel(EMAC_CTL_RESET, db->membase + EMAC_CTL_REG);
+       udelay(200);
+}
+
+static void emac_outblk_32bit(void __iomem *reg, void *data, int count)
+{
+       writesl(reg, data, round_up(count, 4) / 4);
+}
+
+static void emac_inblk_32bit(void __iomem *reg, void *data, int count)
+{
+       readsl(reg, data, round_up(count, 4) / 4);
+}
+
+static int emac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       struct emac_board_info *dm = netdev_priv(dev);
+       struct phy_device *phydev = dm->phy_dev;
+
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       if (!phydev)
+               return -ENODEV;
+
+       return phy_mii_ioctl(phydev, rq, cmd);
+}
+
+/* ethtool ops */
+static void emac_get_drvinfo(struct net_device *dev,
+                             struct ethtool_drvinfo *info)
+{
+       strlcpy(info->driver, DRV_NAME, sizeof(DRV_NAME));
+       strlcpy(info->version, DRV_VERSION, sizeof(DRV_VERSION));
+       strlcpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info));
+}
+
+static int emac_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct emac_board_info *dm = netdev_priv(dev);
+       struct phy_device *phydev = dm->phy_dev;
+
+       if (!phydev)
+               return -ENODEV;
+
+       return phy_ethtool_gset(phydev, cmd);
+}
+
+static int emac_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct emac_board_info *dm = netdev_priv(dev);
+       struct phy_device *phydev = dm->phy_dev;
+
+       if (!phydev)
+               return -ENODEV;
+
+       return phy_ethtool_sset(phydev, cmd);
+}
+
+static const struct ethtool_ops emac_ethtool_ops = {
+       .get_drvinfo    = emac_get_drvinfo,
+       .get_settings   = emac_get_settings,
+       .set_settings   = emac_set_settings,
+       .get_link       = ethtool_op_get_link,
+};
+
+static unsigned int emac_setup(struct net_device *ndev)
+{
+       struct emac_board_info *db = netdev_priv(ndev);
+       unsigned int reg_val;
+
+       /* set up TX */
+       reg_val = readl(db->membase + EMAC_TX_MODE_REG);
+
+       writel(reg_val | EMAC_TX_MODE_ABORTED_FRAME_EN,
+               db->membase + EMAC_TX_MODE_REG);
+
+       /* set up RX */
+       reg_val = readl(db->membase + EMAC_RX_CTL_REG);
+
+       writel(reg_val | EMAC_RX_CTL_PASS_LEN_OOR_EN |
+               EMAC_RX_CTL_ACCEPT_UNICAST_EN | EMAC_RX_CTL_DA_FILTER_EN |
+               EMAC_RX_CTL_ACCEPT_MULTICAST_EN |
+               EMAC_RX_CTL_ACCEPT_BROADCAST_EN,
+               db->membase + EMAC_RX_CTL_REG);
+
+       /* set MAC */
+       /* set MAC CTL0 */
+       reg_val = readl(db->membase + EMAC_MAC_CTL0_REG);
+       writel(reg_val | EMAC_MAC_CTL0_RX_FLOW_CTL_EN |
+               EMAC_MAC_CTL0_TX_FLOW_CTL_EN,
+               db->membase + EMAC_MAC_CTL0_REG);
+
+       /* set MAC CTL1 */
+       reg_val = readl(db->membase + EMAC_MAC_CTL1_REG);
+       reg_val |= EMAC_MAC_CTL1_LEN_CHECK_EN;
+       reg_val |= EMAC_MAC_CTL1_CRC_EN;
+       reg_val |= EMAC_MAC_CTL1_PAD_EN;
+       writel(reg_val, db->membase + EMAC_MAC_CTL1_REG);
+
+       /* set up IPGT */
+       writel(EMAC_MAC_IPGT_FULL_DUPLEX, db->membase + EMAC_MAC_IPGT_REG);
+
+       /* set up IPGR */
+       writel((EMAC_MAC_IPGR_IPG1 << 8) | EMAC_MAC_IPGR_IPG2,
+               db->membase + EMAC_MAC_IPGR_REG);
+
+       /* set up Collison window */
+       writel((EMAC_MAC_CLRT_COLLISION_WINDOW << 8) | EMAC_MAC_CLRT_RM,
+               db->membase + EMAC_MAC_CLRT_REG);
+
+       /* set up Max Frame Length */
+       writel(EMAC_MAX_FRAME_LEN,
+               db->membase + EMAC_MAC_MAXF_REG);
+
+       return 0;
+}
+
+static unsigned int emac_powerup(struct net_device *ndev)
+{
+       struct emac_board_info *db = netdev_priv(ndev);
+       unsigned int reg_val;
+
+       /* initial EMAC */
+       /* flush RX FIFO */
+       reg_val = readl(db->membase + EMAC_RX_CTL_REG);
+       reg_val |= 0x8;
+       writel(reg_val, db->membase + EMAC_RX_CTL_REG);
+       udelay(1);
+
+       /* initial MAC */
+       /* soft reset MAC */
+       reg_val = readl(db->membase + EMAC_MAC_CTL0_REG);
+       reg_val &= ~EMAC_MAC_CTL0_SOFT_RESET;
+       writel(reg_val, db->membase + EMAC_MAC_CTL0_REG);
+
+       /* set MII clock */
+       reg_val = readl(db->membase + EMAC_MAC_MCFG_REG);
+       reg_val &= (~(0xf << 2));
+       reg_val |= (0xD << 2);
+       writel(reg_val, db->membase + EMAC_MAC_MCFG_REG);
+
+       /* clear RX counter */
+       writel(0x0, db->membase + EMAC_RX_FBC_REG);
+
+       /* disable all interrupt and clear interrupt status */
+       writel(0, db->membase + EMAC_INT_CTL_REG);
+       reg_val = readl(db->membase + EMAC_INT_STA_REG);
+       writel(reg_val, db->membase + EMAC_INT_STA_REG);
+
+       udelay(1);
+
+       /* set up EMAC */
+       emac_setup(ndev);
+
+       /* set mac_address to chip */
+       writel(ndev->dev_addr[0] << 16 | ndev->dev_addr[1] << 8 | ndev->
+              dev_addr[2], db->membase + EMAC_MAC_A1_REG);
+       writel(ndev->dev_addr[3] << 16 | ndev->dev_addr[4] << 8 | ndev->
+              dev_addr[5], db->membase + EMAC_MAC_A0_REG);
+
+       mdelay(1);
+
+       return 0;
+}
+
+static int emac_set_mac_address(struct net_device *dev, void *p)
+{
+       struct sockaddr *addr = p;
+       struct emac_board_info *db = netdev_priv(dev);
+
+       if (netif_running(dev))
+               return -EBUSY;
+
+       memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+
+       writel(dev->dev_addr[0] << 16 | dev->dev_addr[1] << 8 | dev->
+              dev_addr[2], db->membase + EMAC_MAC_A1_REG);
+       writel(dev->dev_addr[3] << 16 | dev->dev_addr[4] << 8 | dev->
+              dev_addr[5], db->membase + EMAC_MAC_A0_REG);
+
+       return 0;
+}
+
+/* Initialize emac board */
+static void emac_init_device(struct net_device *dev)
+{
+       struct emac_board_info *db = netdev_priv(dev);
+       unsigned long flags;
+       unsigned int reg_val;
+
+       spin_lock_irqsave(&db->lock, flags);
+
+       emac_update_speed(dev);
+       emac_update_duplex(dev);
+
+       /* enable RX/TX */
+       reg_val = readl(db->membase + EMAC_CTL_REG);
+       writel(reg_val | EMAC_CTL_RESET | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN,
+               db->membase + EMAC_CTL_REG);
+
+       /* enable RX/TX0/RX Hlevel interrup */
+       reg_val = readl(db->membase + EMAC_INT_CTL_REG);
+       reg_val |= (0xf << 0) | (0x01 << 8);
+       writel(reg_val, db->membase + EMAC_INT_CTL_REG);
+
+       spin_unlock_irqrestore(&db->lock, flags);
+}
+
+/* Our watchdog timed out. Called by the networking layer */
+static void emac_timeout(struct net_device *dev)
+{
+       struct emac_board_info *db = netdev_priv(dev);
+       unsigned long flags;
+
+       if (netif_msg_timer(db))
+               dev_err(db->dev, "tx time out.\n");
+
+       /* Save previous register address */
+       spin_lock_irqsave(&db->lock, flags);
+
+       netif_stop_queue(dev);
+       emac_reset(db);
+       emac_init_device(dev);
+       /* We can accept TX packets again */
+       dev->trans_start = jiffies;
+       netif_wake_queue(dev);
+
+       /* Restore previous register address */
+       spin_unlock_irqrestore(&db->lock, flags);
+}
+
+/* Hardware start transmission.
+ * Send a packet to media from the upper layer.
+ */
+static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct emac_board_info *db = netdev_priv(dev);
+       unsigned long channel;
+       unsigned long flags;
+
+       channel = db->tx_fifo_stat & 3;
+       if (channel == 3)
+               return 1;
+
+       channel = (channel == 1 ? 1 : 0);
+
+       spin_lock_irqsave(&db->lock, flags);
+
+       writel(channel, db->membase + EMAC_TX_INS_REG);
+
+       emac_outblk_32bit(db->membase + EMAC_TX_IO_DATA_REG,
+                       skb->data, skb->len);
+       dev->stats.tx_bytes += skb->len;
+
+       db->tx_fifo_stat |= 1 << channel;
+       /* TX control: First packet immediately send, second packet queue */
+       if (channel == 0) {
+               /* set TX len */
+               writel(skb->len, db->membase + EMAC_TX_PL0_REG);
+               /* start translate from fifo to phy */
+               writel(readl(db->membase + EMAC_TX_CTL0_REG) | 1,
+                      db->membase + EMAC_TX_CTL0_REG);
+
+               /* save the time stamp */
+               dev->trans_start = jiffies;
+       } else if (channel == 1) {
+               /* set TX len */
+               writel(skb->len, db->membase + EMAC_TX_PL1_REG);
+               /* start translate from fifo to phy */
+               writel(readl(db->membase + EMAC_TX_CTL1_REG) | 1,
+                      db->membase + EMAC_TX_CTL1_REG);
+
+               /* save the time stamp */
+               dev->trans_start = jiffies;
+       }
+
+       if ((db->tx_fifo_stat & 3) == 3) {
+               /* Second packet */
+               netif_stop_queue(dev);
+       }
+
+       spin_unlock_irqrestore(&db->lock, flags);
+
+       /* free this SKB */
+       dev_kfree_skb(skb);
+
+       return NETDEV_TX_OK;
+}
+
+/* EMAC interrupt handler
+ * receive the packet to upper layer, free the transmitted packet
+ */
+static void emac_tx_done(struct net_device *dev, struct emac_board_info *db,
+                         unsigned int tx_status)
+{
+       /* One packet sent complete */
+       db->tx_fifo_stat &= ~(tx_status & 3);
+       if (3 == (tx_status & 3))
+               dev->stats.tx_packets += 2;
+       else
+               dev->stats.tx_packets++;
+
+       if (netif_msg_tx_done(db))
+               dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);
+
+       netif_wake_queue(dev);
+}
+
+/* Received a packet and pass to upper layer
+ */
+static void emac_rx(struct net_device *dev)
+{
+       struct emac_board_info *db = netdev_priv(dev);
+       struct sk_buff *skb;
+       u8 *rdptr;
+       bool good_packet;
+       static int rxlen_last;
+       unsigned int reg_val;
+       u32 rxhdr, rxstatus, rxcount, rxlen;
+
+       /* Check packet ready or not */
+       while (1) {
+               /* race warning: the first packet might arrive with
+                * the interrupts disabled, but the second will fix
+                * it
+                */
+               rxcount = readl(db->membase + EMAC_RX_FBC_REG);
+
+               if (netif_msg_rx_status(db))
+                       dev_dbg(db->dev, "RXCount: %x\n", rxcount);
+
+               if ((db->skb_last != NULL) && (rxlen_last > 0)) {
+                       dev->stats.rx_bytes += rxlen_last;
+
+                       /* Pass to upper layer */
+                       db->skb_last->protocol = eth_type_trans(db->skb_last,
+                                                               dev);
+                       netif_rx(db->skb_last);
+                       dev->stats.rx_packets++;
+                       db->skb_last = NULL;
+                       rxlen_last = 0;
+
+                       reg_val = readl(db->membase + EMAC_RX_CTL_REG);
+                       reg_val &= ~EMAC_RX_CTL_DMA_EN;
+                       writel(reg_val, db->membase + EMAC_RX_CTL_REG);
+               }
+
+               if (!rxcount) {
+                       db->emacrx_completed_flag = 1;
+                       reg_val = readl(db->membase + EMAC_INT_CTL_REG);
+                       reg_val |= (0xf << 0) | (0x01 << 8);
+                       writel(reg_val, db->membase + EMAC_INT_CTL_REG);
+
+                       /* had one stuck? */
+                       rxcount = readl(db->membase + EMAC_RX_FBC_REG);
+                       if (!rxcount)
+                               return;
+               }
+
+               reg_val = readl(db->membase + EMAC_RX_IO_DATA_REG);
+               if (netif_msg_rx_status(db))
+                       dev_dbg(db->dev, "receive header: %x\n", reg_val);
+               if (reg_val != EMAC_UNDOCUMENTED_MAGIC) {
+                       /* disable RX */
+                       reg_val = readl(db->membase + EMAC_CTL_REG);
+                       writel(reg_val & ~EMAC_CTL_RX_EN,
+                              db->membase + EMAC_CTL_REG);
+
+                       /* Flush RX FIFO */
+                       reg_val = readl(db->membase + EMAC_RX_CTL_REG);
+                       writel(reg_val | (1 << 3),
+                              db->membase + EMAC_RX_CTL_REG);
+
+                       do {
+                               reg_val = readl(db->membase + EMAC_RX_CTL_REG);
+                       } while (reg_val & (1 << 3));
+
+                       /* enable RX */
+                       reg_val = readl(db->membase + EMAC_CTL_REG);
+                       writel(reg_val | EMAC_CTL_RX_EN,
+                              db->membase + EMAC_CTL_REG);
+                       reg_val = readl(db->membase + EMAC_INT_CTL_REG);
+                       reg_val |= (0xf << 0) | (0x01 << 8);
+                       writel(reg_val, db->membase + EMAC_INT_CTL_REG);
+
+                       db->emacrx_completed_flag = 1;
+
+                       return;
+               }
+
+               /* A packet ready now  & Get status/length */
+               good_packet = true;
+
+               emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG,
+                               &rxhdr, sizeof(rxhdr));
+
+               if (netif_msg_rx_status(db))
+                       dev_dbg(db->dev, "rxhdr: %x\n", *((int *)(&rxhdr)));
+
+               rxlen = EMAC_RX_IO_DATA_LEN(rxhdr);
+               rxstatus = EMAC_RX_IO_DATA_STATUS(rxhdr);
+
+               if (netif_msg_rx_status(db))
+                       dev_dbg(db->dev, "RX: status %02x, length %04x\n",
+                               rxstatus, rxlen);
+
+               /* Packet Status check */
+               if (rxlen < 0x40) {
+                       good_packet = false;
+                       if (netif_msg_rx_err(db))
+                               dev_dbg(db->dev, "RX: Bad Packet (runt)\n");
+               }
+
+               if (unlikely(!(rxstatus & EMAC_RX_IO_DATA_STATUS_OK))) {
+                       good_packet = false;
+
+                       if (rxstatus & EMAC_RX_IO_DATA_STATUS_CRC_ERR) {
+                               if (netif_msg_rx_err(db))
+                                       dev_dbg(db->dev, "crc error\n");
+                               dev->stats.rx_crc_errors++;
+                       }
+
+                       if (rxstatus & EMAC_RX_IO_DATA_STATUS_LEN_ERR) {
+                               if (netif_msg_rx_err(db))
+                                       dev_dbg(db->dev, "length error\n");
+                               dev->stats.rx_length_errors++;
+                       }
+               }
+
+               /* Move data from EMAC */
+               skb = dev_alloc_skb(rxlen + 4);
+               if (good_packet && skb) {
+                       skb_reserve(skb, 2);
+                       rdptr = (u8 *) skb_put(skb, rxlen - 4);
+
+                       /* Read received packet from RX SRAM */
+                       if (netif_msg_rx_status(db))
+                               dev_dbg(db->dev, "RxLen %x\n", rxlen);
+
+                       emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG,
+                                       rdptr, rxlen);
+                       dev->stats.rx_bytes += rxlen;
+
+                       /* Pass to upper layer */
+                       skb->protocol = eth_type_trans(skb, dev);
+                       netif_rx(skb);
+                       dev->stats.rx_packets++;
+               }
+       }
+}
+
+static irqreturn_t emac_interrupt(int irq, void *dev_id)
+{
+       struct net_device *dev = dev_id;
+       struct emac_board_info *db = netdev_priv(dev);
+       int int_status;
+       unsigned long flags;
+       unsigned int reg_val;
+
+       /* A real interrupt coming */
+
+       /* holders of db->lock must always block IRQs */
+       spin_lock_irqsave(&db->lock, flags);
+
+       /* Disable all interrupts */
+       writel(0, db->membase + EMAC_INT_CTL_REG);
+
+       /* Got EMAC interrupt status */
+       /* Got ISR */
+       int_status = readl(db->membase + EMAC_INT_STA_REG);
+       /* Clear ISR status */
+       writel(int_status, db->membase + EMAC_INT_STA_REG);
+
+       if (netif_msg_intr(db))
+               dev_dbg(db->dev, "emac interrupt %02x\n", int_status);
+
+       /* Received the coming packet */
+       if ((int_status & 0x100) && (db->emacrx_completed_flag == 1)) {
+               /* carrier lost */
+               db->emacrx_completed_flag = 0;
+               emac_rx(dev);
+       }
+
+       /* Transmit Interrupt check */
+       if (int_status & (0x01 | 0x02))
+               emac_tx_done(dev, db, int_status);
+
+       if (int_status & (0x04 | 0x08))
+               netdev_info(dev, " ab : %x\n", int_status);
+
+       /* Re-enable interrupt mask */
+       if (db->emacrx_completed_flag == 1) {
+               reg_val = readl(db->membase + EMAC_INT_CTL_REG);
+               reg_val |= (0xf << 0) | (0x01 << 8);
+               writel(reg_val, db->membase + EMAC_INT_CTL_REG);
+       }
+       spin_unlock_irqrestore(&db->lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Used by netconsole
+ */
+static void emac_poll_controller(struct net_device *dev)
+{
+       disable_irq(dev->irq);
+       emac_interrupt(dev->irq, dev);
+       enable_irq(dev->irq);
+}
+#endif
+
+/*  Open the interface.
+ *  The interface is opened whenever "ifconfig" actives it.
+ */
+static int emac_open(struct net_device *dev)
+{
+       struct emac_board_info *db = netdev_priv(dev);
+       int ret;
+
+       if (netif_msg_ifup(db))
+               dev_dbg(db->dev, "enabling %s\n", dev->name);
+
+       if (devm_request_irq(db->dev, dev->irq, &emac_interrupt,
+                            0, dev->name, dev))
+               return -EAGAIN;
+
+       /* Initialize EMAC board */
+       emac_reset(db);
+       emac_init_device(dev);
+
+       ret = emac_mdio_probe(dev);
+       if (ret < 0) {
+               netdev_err(dev, "cannot probe MDIO bus\n");
+               return ret;
+       }
+
+       phy_start(db->phy_dev);
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+static void emac_shutdown(struct net_device *dev)
+{
+       unsigned int reg_val;
+       struct emac_board_info *db = netdev_priv(dev);
+
+       /* Disable all interrupt */
+       writel(0, db->membase + EMAC_INT_CTL_REG);
+
+       /* clear interupt status */
+       reg_val = readl(db->membase + EMAC_INT_STA_REG);
+       writel(reg_val, db->membase + EMAC_INT_STA_REG);
+
+       /* Disable RX/TX */
+       reg_val = readl(db->membase + EMAC_CTL_REG);
+       reg_val &= ~(EMAC_CTL_TX_EN | EMAC_CTL_RX_EN | EMAC_CTL_RESET);
+       writel(reg_val, db->membase + EMAC_CTL_REG);
+}
+
+/* Stop the interface.
+ * The interface is stopped when it is brought.
+ */
+static int emac_stop(struct net_device *ndev)
+{
+       struct emac_board_info *db = netdev_priv(ndev);
+
+       if (netif_msg_ifdown(db))
+               dev_dbg(db->dev, "shutting down %s\n", ndev->name);
+
+       netif_stop_queue(ndev);
+       netif_carrier_off(ndev);
+
+       phy_stop(db->phy_dev);
+
+       emac_mdio_remove(ndev);
+
+       emac_shutdown(ndev);
+
+       return 0;
+}
+
+static const struct net_device_ops emac_netdev_ops = {
+       .ndo_open               = emac_open,
+       .ndo_stop               = emac_stop,
+       .ndo_start_xmit         = emac_start_xmit,
+       .ndo_tx_timeout         = emac_timeout,
+       .ndo_do_ioctl           = emac_ioctl,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = emac_set_mac_address,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = emac_poll_controller,
+#endif
+};
+
+/* Search EMAC board, allocate space and register it
+ */
+static int emac_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct emac_board_info *db;
+       struct net_device *ndev;
+       int ret = 0;
+       const char *mac_addr;
+
+       ndev = alloc_etherdev(sizeof(struct emac_board_info));
+       if (!ndev) {
+               dev_err(&pdev->dev, "could not allocate device.\n");
+               return -ENOMEM;
+       }
+
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       db = netdev_priv(ndev);
+       memset(db, 0, sizeof(*db));
+
+       db->dev = &pdev->dev;
+       db->ndev = ndev;
+       db->pdev = pdev;
+
+       spin_lock_init(&db->lock);
+
+       db->membase = of_iomap(np, 0);
+       if (!db->membase) {
+               dev_err(&pdev->dev, "failed to remap registers\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       /* fill in parameters for net-dev structure */
+       ndev->base_addr = (unsigned long)db->membase;
+       ndev->irq = irq_of_parse_and_map(np, 0);
+       if (ndev->irq == -ENXIO) {
+               netdev_err(ndev, "No irq resource\n");
+               ret = ndev->irq;
+               goto out;
+       }
+
+       db->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(db->clk))
+               goto out;
+
+       clk_prepare_enable(db->clk);
+
+       db->phy_node = of_parse_phandle(np, "phy", 0);
+       if (!db->phy_node) {
+               dev_err(&pdev->dev, "no associated PHY\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       /* Read MAC-address from DT */
+       mac_addr = of_get_mac_address(np);
+       if (mac_addr)
+               memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
+
+       /* Check if the MAC address is valid, if not get a random one */
+       if (!is_valid_ether_addr(ndev->dev_addr)) {
+               eth_hw_addr_random(ndev);
+               dev_warn(&pdev->dev, "using random MAC address %pM\n",
+                        ndev->dev_addr);
+       }
+
+       db->emacrx_completed_flag = 1;
+       emac_powerup(ndev);
+       emac_reset(db);
+
+       ether_setup(ndev);
+
+       ndev->netdev_ops = &emac_netdev_ops;
+       ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+       ndev->ethtool_ops = &emac_ethtool_ops;
+
+       platform_set_drvdata(pdev, ndev);
+
+       /* Carrier starts down, phylib will bring it up */
+       netif_carrier_off(ndev);
+
+       ret = register_netdev(ndev);
+       if (ret) {
+               dev_err(&pdev->dev, "Registering netdev failed!\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       dev_info(&pdev->dev, "%s: at %p, IRQ %d MAC: %pM\n",
+                ndev->name, db->membase, ndev->irq, ndev->dev_addr);
+
+       return 0;
+
+out:
+       dev_err(db->dev, "not found (%d).\n", ret);
+
+       free_netdev(ndev);
+
+       return ret;
+}
+
+static int emac_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+
+       unregister_netdev(ndev);
+       free_netdev(ndev);
+
+       dev_dbg(&pdev->dev, "released and freed device\n");
+       return 0;
+}
+
+static int emac_suspend(struct platform_device *dev, pm_message_t state)
+{
+       struct net_device *ndev = platform_get_drvdata(dev);
+
+       netif_carrier_off(ndev);
+       netif_device_detach(ndev);
+       emac_shutdown(ndev);
+
+       return 0;
+}
+
+static int emac_resume(struct platform_device *dev)
+{
+       struct net_device *ndev = platform_get_drvdata(dev);
+       struct emac_board_info *db = netdev_priv(ndev);
+
+       emac_reset(db);
+       emac_init_device(ndev);
+       netif_device_attach(ndev);
+
+       return 0;
+}
+
+static const struct of_device_id emac_of_match[] = {
+       {.compatible = "allwinner,sun4i-emac",},
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, emac_of_match);
+
+static struct platform_driver emac_driver = {
+       .driver = {
+               .name = "sun4i-emac",
+               .of_match_table = emac_of_match,
+       },
+       .probe = emac_probe,
+       .remove = emac_remove,
+       .suspend = emac_suspend,
+       .resume = emac_resume,
+};
+
+module_platform_driver(emac_driver);
+
+MODULE_AUTHOR("Stefan Roese <sr@denx.de>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A10 emac network driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.h b/drivers/net/ethernet/allwinner/sun4i-emac.h
new file mode 100644 (file)
index 0000000..38c72d9
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Allwinner EMAC Fast Ethernet driver for Linux.
+ *
+ * Copyright 2012 Stefan Roese <sr@denx.de>
+ * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * Based on the Linux driver provided by Allwinner:
+ * Copyright (C) 1997  Sten Wang
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _SUN4I_EMAC_H_
+#define _SUN4I_EMAC_H_
+
+#define EMAC_CTL_REG           (0x00)
+#define EMAC_CTL_RESET                 (1 << 0)
+#define EMAC_CTL_TX_EN                 (1 << 1)
+#define EMAC_CTL_RX_EN                 (1 << 2)
+#define EMAC_TX_MODE_REG       (0x04)
+#define EMAC_TX_MODE_ABORTED_FRAME_EN  (1 << 0)
+#define EMAC_TX_MODE_DMA_EN            (1 << 1)
+#define EMAC_TX_FLOW_REG       (0x08)
+#define EMAC_TX_CTL0_REG       (0x0c)
+#define EMAC_TX_CTL1_REG       (0x10)
+#define EMAC_TX_INS_REG                (0x14)
+#define EMAC_TX_PL0_REG                (0x18)
+#define EMAC_TX_PL1_REG                (0x1c)
+#define EMAC_TX_STA_REG                (0x20)
+#define EMAC_TX_IO_DATA_REG    (0x24)
+#define EMAC_TX_IO_DATA1_REG   (0x28)
+#define EMAC_TX_TSVL0_REG      (0x2c)
+#define EMAC_TX_TSVH0_REG      (0x30)
+#define EMAC_TX_TSVL1_REG      (0x34)
+#define EMAC_TX_TSVH1_REG      (0x38)
+#define EMAC_RX_CTL_REG                (0x3c)
+#define EMAC_RX_CTL_AUTO_DRQ_EN                (1 << 1)
+#define EMAC_RX_CTL_DMA_EN             (1 << 2)
+#define EMAC_RX_CTL_PASS_ALL_EN                (1 << 4)
+#define EMAC_RX_CTL_PASS_CTL_EN                (1 << 5)
+#define EMAC_RX_CTL_PASS_CRC_ERR_EN    (1 << 6)
+#define EMAC_RX_CTL_PASS_LEN_ERR_EN    (1 << 7)
+#define EMAC_RX_CTL_PASS_LEN_OOR_EN    (1 << 8)
+#define EMAC_RX_CTL_ACCEPT_UNICAST_EN  (1 << 16)
+#define EMAC_RX_CTL_DA_FILTER_EN       (1 << 17)
+#define EMAC_RX_CTL_ACCEPT_MULTICAST_EN        (1 << 20)
+#define EMAC_RX_CTL_HASH_FILTER_EN     (1 << 21)
+#define EMAC_RX_CTL_ACCEPT_BROADCAST_EN        (1 << 22)
+#define EMAC_RX_CTL_SA_FILTER_EN       (1 << 24)
+#define EMAC_RX_CTL_SA_FILTER_INVERT_EN        (1 << 25)
+#define EMAC_RX_HASH0_REG      (0x40)
+#define EMAC_RX_HASH1_REG      (0x44)
+#define EMAC_RX_STA_REG                (0x48)
+#define EMAC_RX_IO_DATA_REG    (0x4c)
+#define EMAC_RX_IO_DATA_LEN(x)         (x & 0xffff)
+#define EMAC_RX_IO_DATA_STATUS(x)      ((x >> 16) & 0xffff)
+#define EMAC_RX_IO_DATA_STATUS_CRC_ERR (1 << 4)
+#define EMAC_RX_IO_DATA_STATUS_LEN_ERR (3 << 5)
+#define EMAC_RX_IO_DATA_STATUS_OK      (1 << 7)
+#define EMAC_RX_FBC_REG                (0x50)
+#define EMAC_INT_CTL_REG       (0x54)
+#define EMAC_INT_STA_REG       (0x58)
+#define EMAC_MAC_CTL0_REG      (0x5c)
+#define EMAC_MAC_CTL0_RX_FLOW_CTL_EN   (1 << 2)
+#define EMAC_MAC_CTL0_TX_FLOW_CTL_EN   (1 << 3)
+#define EMAC_MAC_CTL0_SOFT_RESET       (1 << 15)
+#define EMAC_MAC_CTL1_REG      (0x60)
+#define EMAC_MAC_CTL1_DUPLEX_EN                (1 << 0)
+#define EMAC_MAC_CTL1_LEN_CHECK_EN     (1 << 1)
+#define EMAC_MAC_CTL1_HUGE_FRAME_EN    (1 << 2)
+#define EMAC_MAC_CTL1_DELAYED_CRC_EN   (1 << 3)
+#define EMAC_MAC_CTL1_CRC_EN           (1 << 4)
+#define EMAC_MAC_CTL1_PAD_EN           (1 << 5)
+#define EMAC_MAC_CTL1_PAD_CRC_EN       (1 << 6)
+#define EMAC_MAC_CTL1_AD_SHORT_FRAME_EN        (1 << 7)
+#define EMAC_MAC_CTL1_BACKOFF_DIS      (1 << 12)
+#define EMAC_MAC_IPGT_REG      (0x64)
+#define EMAC_MAC_IPGT_HALF_DUPLEX      (0x12)
+#define EMAC_MAC_IPGT_FULL_DUPLEX      (0x15)
+#define EMAC_MAC_IPGR_REG      (0x68)
+#define EMAC_MAC_IPGR_IPG1             (0x0c)
+#define EMAC_MAC_IPGR_IPG2             (0x12)
+#define EMAC_MAC_CLRT_REG      (0x6c)
+#define EMAC_MAC_CLRT_COLLISION_WINDOW (0x37)
+#define EMAC_MAC_CLRT_RM               (0x0f)
+#define EMAC_MAC_MAXF_REG      (0x70)
+#define EMAC_MAC_SUPP_REG      (0x74)
+#define EMAC_MAC_TEST_REG      (0x78)
+#define EMAC_MAC_MCFG_REG      (0x7c)
+#define EMAC_MAC_A0_REG                (0x98)
+#define EMAC_MAC_A1_REG                (0x9c)
+#define EMAC_MAC_A2_REG                (0xa0)
+#define EMAC_SAFX_L_REG0       (0xa4)
+#define EMAC_SAFX_H_REG0       (0xa8)
+#define EMAC_SAFX_L_REG1       (0xac)
+#define EMAC_SAFX_H_REG1       (0xb0)
+#define EMAC_SAFX_L_REG2       (0xb4)
+#define EMAC_SAFX_H_REG2       (0xb8)
+#define EMAC_SAFX_L_REG3       (0xbc)
+#define EMAC_SAFX_H_REG3       (0xc0)
+
+#define EMAC_PHY_DUPLEX                (1 << 8)
+
+#define EMAC_EEPROM_MAGIC      (0x444d394b)
+#define EMAC_UNDOCUMENTED_MAGIC        (0x0143414d)
+#endif /* _SUN4I_EMAC_H_ */
index b7894f8..219be1b 100644 (file)
@@ -702,19 +702,6 @@ static struct pci_driver acenic_pci_driver = {
        .remove         = acenic_remove_one,
 };
 
-static int __init acenic_init(void)
-{
-       return pci_register_driver(&acenic_pci_driver);
-}
-
-static void __exit acenic_exit(void)
-{
-       pci_unregister_driver(&acenic_pci_driver);
-}
-
-module_init(acenic_init);
-module_exit(acenic_exit);
-
 static void ace_free_descriptors(struct net_device *dev)
 {
        struct ace_private *ap = netdev_priv(dev);
@@ -3199,3 +3186,5 @@ static int read_eeprom_byte(struct net_device *dev, unsigned long offset)
               ap->name, offset);
        goto out;
 }
+
+module_pci_driver(acenic_pci_driver);
index 13d74aa..562df46 100644 (file)
@@ -34,7 +34,6 @@ config AMD8111_ETH
        tristate "AMD 8111 (new PCI LANCE) support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          If you have an AMD 8111-based PCI LANCE ethernet card,
@@ -60,7 +59,6 @@ config PCNET32
        tristate "AMD PCnet32 PCI support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          If you have a PCnet32 or PCnetPCI based network (Ethernet) card,
index 8e6b665..1b1429d 100644 (file)
@@ -1813,7 +1813,7 @@ static const struct net_device_ops amd8111e_netdev_ops = {
 static int amd8111e_probe_one(struct pci_dev *pdev,
                                  const struct pci_device_id *ent)
 {
-       int err,i,pm_cap;
+       int err, i;
        unsigned long reg_addr,reg_len;
        struct amd8111e_priv* lp;
        struct net_device* dev;
@@ -1842,7 +1842,7 @@ static int amd8111e_probe_one(struct pci_dev *pdev,
        pci_set_master(pdev);
 
        /* Find power-management capability. */
-       if((pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM))==0){
+       if (!pdev->pm_cap) {
                printk(KERN_ERR "amd8111e: No Power Management capability, "
                       "exiting.\n");
                err = -ENODEV;
@@ -1875,7 +1875,7 @@ static int amd8111e_probe_one(struct pci_dev *pdev,
        lp = netdev_priv(dev);
        lp->pci_dev = pdev;
        lp->amd8111e_net_dev = dev;
-       lp->pm_cap = pm_cap;
+       lp->pm_cap = pdev->pm_cap;
 
        spin_lock_init(&lp->lock);
 
@@ -1981,15 +1981,4 @@ static struct pci_driver amd8111e_driver = {
        .resume         = amd8111e_resume
 };
 
-static int __init amd8111e_init(void)
-{
-       return pci_register_driver(&amd8111e_driver);
-}
-
-static void __exit amd8111e_cleanup(void)
-{
-       pci_unregister_driver(&amd8111e_driver);
-}
-
-module_init(amd8111e_init);
-module_exit(amd8111e_cleanup);
+module_pci_driver(amd8111e_driver);
index 688aede..ceb45bc 100644 (file)
@@ -1301,8 +1301,6 @@ static int au1000_remove(struct platform_device *pdev)
        int i;
        struct resource *base, *macen;
 
-       platform_set_drvdata(pdev, NULL);
-
        unregister_netdev(dev);
        mdiobus_unregister(aup->mii_bus);
        mdiobus_free(aup->mii_bus);
index f47b780..ece5683 100644 (file)
@@ -1470,7 +1470,7 @@ no_link_test:
                goto fail;
        }
 
-       dev_set_drvdata(&op->dev, lp);
+       platform_set_drvdata(op, lp);
 
        printk(KERN_INFO "%s: LANCE %pM\n",
               dev->name, dev->dev_addr);
@@ -1501,7 +1501,7 @@ static int sunlance_sbus_probe(struct platform_device *op)
 
 static int sunlance_sbus_remove(struct platform_device *op)
 {
-       struct lance_private *lp = dev_get_drvdata(&op->dev);
+       struct lance_private *lp = platform_get_drvdata(op);
        struct net_device *net_dev = lp->dev;
 
        unregister_netdev(net_dev);
@@ -1510,8 +1510,6 @@ static int sunlance_sbus_remove(struct platform_device *op)
 
        free_netdev(net_dev);
 
-       dev_set_drvdata(&op->dev, NULL);
-
        return 0;
 }
 
index f36bbd6..a597b76 100644 (file)
@@ -1016,7 +1016,6 @@ static void bmac_set_multicast(struct net_device *dev)
 static void bmac_set_multicast(struct net_device *dev)
 {
        struct netdev_hw_addr *ha;
-       int i;
        unsigned short rx_cfg;
        u32 crc;
 
@@ -1030,14 +1029,12 @@ static void bmac_set_multicast(struct net_device *dev)
                rx_cfg |= RxPromiscEnable;
                bmwrite(dev, RXCFG, rx_cfg);
        } else {
-               u16 hash_table[4];
+               u16 hash_table[4] = { 0 };
 
                rx_cfg = bmread(dev, RXCFG);
                rx_cfg &= ~RxPromiscEnable;
                bmwrite(dev, RXCFG, rx_cfg);
 
-               for(i = 0; i < 4; i++) hash_table[i] = 0;
-
                netdev_for_each_mc_addr(ha, dev) {
                        crc = ether_crc_le(6, ha->addr);
                        crc >>= 26;
diff --git a/drivers/net/ethernet/arc/Kconfig b/drivers/net/ethernet/arc/Kconfig
new file mode 100644 (file)
index 0000000..514c57f
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# ARC EMAC network device configuration
+#
+
+config NET_VENDOR_ARC
+       bool "ARC devices"
+       default y
+       ---help---
+         If you have a network (Ethernet) card belonging to this class, say Y
+         and read the Ethernet-HOWTO, available from
+         <http://www.tldp.org/docs.html#howto>.
+
+         Note that the answer to this question doesn't directly affect the
+         kernel: saying N will just cause the configurator to skip all
+         the questions about ARC cards. If you say Y, you will be asked for
+         your specific card in the following questions.
+
+if NET_VENDOR_ARC
+
+config ARC_EMAC
+       tristate "ARC EMAC support"
+       select MII
+       select PHYLIB
+       depends on OF_IRQ
+       depends on OF_NET
+       ---help---
+         On some legacy ARC (Synopsys) FPGA boards such as ARCAngel4/ML50x
+         non-standard on-chip ethernet device ARC EMAC 10/100 is used.
+         Say Y here if you have such a board.  If unsure, say N.
+
+endif # NET_VENDOR_ARC
diff --git a/drivers/net/ethernet/arc/Makefile b/drivers/net/ethernet/arc/Makefile
new file mode 100644 (file)
index 0000000..00c8657
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for the ARC network device drivers.
+#
+
+arc_emac-objs := emac_main.o emac_mdio.o
+obj-$(CONFIG_ARC_EMAC) += arc_emac.o
diff --git a/drivers/net/ethernet/arc/emac.h b/drivers/net/ethernet/arc/emac.h
new file mode 100644 (file)
index 0000000..dc08678
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2004-2013 Synopsys, Inc. (www.synopsys.com)
+ *
+ * Registers and bits definitions of ARC EMAC
+ */
+
+#ifndef ARC_EMAC_H
+#define ARC_EMAC_H
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+
+/* STATUS and ENABLE Register bit masks */
+#define TXINT_MASK     (1<<0)  /* Transmit interrupt */
+#define RXINT_MASK     (1<<1)  /* Receive interrupt */
+#define ERR_MASK       (1<<2)  /* Error interrupt */
+#define TXCH_MASK      (1<<3)  /* Transmit chaining error interrupt */
+#define MSER_MASK      (1<<4)  /* Missed packet counter error */
+#define RXCR_MASK      (1<<8)  /* RXCRCERR counter rolled over  */
+#define RXFR_MASK      (1<<9)  /* RXFRAMEERR counter rolled over */
+#define RXFL_MASK      (1<<10) /* RXOFLOWERR counter rolled over */
+#define MDIO_MASK      (1<<12) /* MDIO complete interrupt */
+#define TXPL_MASK      (1<<31) /* Force polling of BD by EMAC */
+
+/* CONTROL Register bit masks */
+#define EN_MASK                (1<<0)  /* VMAC enable */
+#define TXRN_MASK      (1<<3)  /* TX enable */
+#define RXRN_MASK      (1<<4)  /* RX enable */
+#define DSBC_MASK      (1<<8)  /* Disable receive broadcast */
+#define ENFL_MASK      (1<<10) /* Enable Full-duplex */
+#define PROM_MASK      (1<<11) /* Promiscuous mode */
+
+/* Buffer descriptor INFO bit masks */
+#define OWN_MASK       (1<<31) /* 0-CPU owns buffer, 1-EMAC owns buffer */
+#define FIRST_MASK     (1<<16) /* First buffer in chain */
+#define LAST_MASK      (1<<17) /* Last buffer in chain */
+#define LEN_MASK       0x000007FF      /* last 11 bits */
+#define CRLS           (1<<21)
+#define DEFR           (1<<22)
+#define DROP           (1<<23)
+#define RTRY           (1<<24)
+#define LTCL           (1<<28)
+#define UFLO           (1<<29)
+
+#define FOR_EMAC       OWN_MASK
+#define FOR_CPU                0
+
+/* ARC EMAC register set combines entries for MAC and MDIO */
+enum {
+       R_ID = 0,
+       R_STATUS,
+       R_ENABLE,
+       R_CTRL,
+       R_POLLRATE,
+       R_RXERR,
+       R_MISS,
+       R_TX_RING,
+       R_RX_RING,
+       R_ADDRL,
+       R_ADDRH,
+       R_LAFL,
+       R_LAFH,
+       R_MDIO,
+};
+
+#define TX_TIMEOUT             (400*HZ/1000)   /* Transmission timeout */
+
+#define ARC_EMAC_NAPI_WEIGHT   40              /* Workload for NAPI */
+
+#define EMAC_BUFFER_SIZE       1536            /* EMAC buffer size */
+
+/**
+ * struct arc_emac_bd - EMAC buffer descriptor (BD).
+ *
+ * @info:      Contains status information on the buffer itself.
+ * @data:      32-bit byte addressable pointer to the packet data.
+ */
+struct arc_emac_bd {
+       __le32 info;
+       dma_addr_t data;
+};
+
+/* Number of Rx/Tx BD's */
+#define RX_BD_NUM      128
+#define TX_BD_NUM      128
+
+#define RX_RING_SZ     (RX_BD_NUM * sizeof(struct arc_emac_bd))
+#define TX_RING_SZ     (TX_BD_NUM * sizeof(struct arc_emac_bd))
+
+/**
+ * struct buffer_state - Stores Rx/Tx buffer state.
+ * @sk_buff:   Pointer to socket buffer.
+ * @addr:      Start address of DMA-mapped memory region.
+ * @len:       Length of DMA-mapped memory region.
+ */
+struct buffer_state {
+       struct sk_buff *skb;
+       DEFINE_DMA_UNMAP_ADDR(addr);
+       DEFINE_DMA_UNMAP_LEN(len);
+};
+
+/**
+ * struct arc_emac_priv - Storage of EMAC's private information.
+ * @dev:       Pointer to the current device.
+ * @ndev:      Pointer to the current network device.
+ * @phy_dev:   Pointer to attached PHY device.
+ * @bus:       Pointer to the current MII bus.
+ * @regs:      Base address of EMAC memory-mapped control registers.
+ * @napi:      Structure for NAPI.
+ * @stats:     Network device statistics.
+ * @rxbd:      Pointer to Rx BD ring.
+ * @txbd:      Pointer to Tx BD ring.
+ * @rxbd_dma:  DMA handle for Rx BD ring.
+ * @txbd_dma:  DMA handle for Tx BD ring.
+ * @rx_buff:   Storage for Rx buffers states.
+ * @tx_buff:   Storage for Tx buffers states.
+ * @txbd_curr: Index of Tx BD to use on the next "ndo_start_xmit".
+ * @txbd_dirty:        Index of Tx BD to free on the next Tx interrupt.
+ * @last_rx_bd:        Index of the last Rx BD we've got from EMAC.
+ * @link:      PHY's last seen link state.
+ * @duplex:    PHY's last set duplex mode.
+ * @speed:     PHY's last set speed.
+ * @max_speed: Maximum supported by current system network data-rate.
+ */
+struct arc_emac_priv {
+       /* Devices */
+       struct device *dev;
+       struct net_device *ndev;
+       struct phy_device *phy_dev;
+       struct mii_bus *bus;
+
+       void __iomem *regs;
+
+       struct napi_struct napi;
+       struct net_device_stats stats;
+
+       struct arc_emac_bd *rxbd;
+       struct arc_emac_bd *txbd;
+
+       dma_addr_t rxbd_dma;
+       dma_addr_t txbd_dma;
+
+       struct buffer_state rx_buff[RX_BD_NUM];
+       struct buffer_state tx_buff[TX_BD_NUM];
+       unsigned int txbd_curr;
+       unsigned int txbd_dirty;
+
+       unsigned int last_rx_bd;
+
+       unsigned int link;
+       unsigned int duplex;
+       unsigned int speed;
+       unsigned int max_speed;
+};
+
+/**
+ * arc_reg_set - Sets EMAC register with provided value.
+ * @priv:      Pointer to ARC EMAC private data structure.
+ * @reg:       Register offset from base address.
+ * @value:     Value to set in register.
+ */
+static inline void arc_reg_set(struct arc_emac_priv *priv, int reg, int value)
+{
+       iowrite32(value, priv->regs + reg * sizeof(int));
+}
+
+/**
+ * arc_reg_get - Gets value of specified EMAC register.
+ * @priv:      Pointer to ARC EMAC private data structure.
+ * @reg:       Register offset from base address.
+ *
+ * returns:    Value of requested register.
+ */
+static inline unsigned int arc_reg_get(struct arc_emac_priv *priv, int reg)
+{
+       return ioread32(priv->regs + reg * sizeof(int));
+}
+
+/**
+ * arc_reg_or - Applies mask to specified EMAC register - ("reg" | "mask").
+ * @priv:      Pointer to ARC EMAC private data structure.
+ * @reg:       Register offset from base address.
+ * @mask:      Mask to apply to specified register.
+ *
+ * This function reads initial register value, then applies provided mask
+ * to it and then writes register back.
+ */
+static inline void arc_reg_or(struct arc_emac_priv *priv, int reg, int mask)
+{
+       unsigned int value = arc_reg_get(priv, reg);
+       arc_reg_set(priv, reg, value | mask);
+}
+
+/**
+ * arc_reg_clr - Applies mask to specified EMAC register - ("reg" & ~"mask").
+ * @priv:      Pointer to ARC EMAC private data structure.
+ * @reg:       Register offset from base address.
+ * @mask:      Mask to apply to specified register.
+ *
+ * This function reads initial register value, then applies provided mask
+ * to it and then writes register back.
+ */
+static inline void arc_reg_clr(struct arc_emac_priv *priv, int reg, int mask)
+{
+       unsigned int value = arc_reg_get(priv, reg);
+       arc_reg_set(priv, reg, value & ~mask);
+}
+
+int arc_mdio_probe(struct platform_device *pdev, struct arc_emac_priv *priv);
+int arc_mdio_remove(struct arc_emac_priv *priv);
+
+#endif /* ARC_EMAC_H */
diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c
new file mode 100644 (file)
index 0000000..f1b121e
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+ * Copyright (C) 2004-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
+ * published by the Free Software Foundation.
+ *
+ * Driver for the ARC EMAC 10100 (hardware revision 5)
+ *
+ * Contributors:
+ *             Amit Bhor
+ *             Sameer Dhavale
+ *             Vineet Gupta
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+
+#include "emac.h"
+
+#define DRV_NAME       "arc_emac"
+#define DRV_VERSION    "1.0"
+
+/**
+ * arc_emac_adjust_link - Adjust the PHY link duplex.
+ * @ndev:      Pointer to the net_device structure.
+ *
+ * This function is called to change the duplex setting after auto negotiation
+ * is done by the PHY.
+ */
+static void arc_emac_adjust_link(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct phy_device *phy_dev = priv->phy_dev;
+       unsigned int reg, state_changed = 0;
+
+       if (priv->link != phy_dev->link) {
+               priv->link = phy_dev->link;
+               state_changed = 1;
+       }
+
+       if (priv->speed != phy_dev->speed) {
+               priv->speed = phy_dev->speed;
+               state_changed = 1;
+       }
+
+       if (priv->duplex != phy_dev->duplex) {
+               reg = arc_reg_get(priv, R_CTRL);
+
+               if (DUPLEX_FULL == phy_dev->duplex)
+                       reg |= ENFL_MASK;
+               else
+                       reg &= ~ENFL_MASK;
+
+               arc_reg_set(priv, R_CTRL, reg);
+               priv->duplex = phy_dev->duplex;
+               state_changed = 1;
+       }
+
+       if (state_changed)
+               phy_print_status(phy_dev);
+}
+
+/**
+ * arc_emac_get_settings - Get PHY settings.
+ * @ndev:      Pointer to net_device structure.
+ * @cmd:       Pointer to ethtool_cmd structure.
+ *
+ * This implements ethtool command for getting PHY settings. If PHY could
+ * not be found, the function returns -ENODEV. This function calls the
+ * relevant PHY ethtool API to get the PHY settings.
+ * Issue "ethtool ethX" under linux prompt to execute this function.
+ */
+static int arc_emac_get_settings(struct net_device *ndev,
+                                struct ethtool_cmd *cmd)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+
+       return phy_ethtool_gset(priv->phy_dev, cmd);
+}
+
+/**
+ * arc_emac_set_settings - Set PHY settings as passed in the argument.
+ * @ndev:      Pointer to net_device structure.
+ * @cmd:       Pointer to ethtool_cmd structure.
+ *
+ * This implements ethtool command for setting various PHY settings. If PHY
+ * could not be found, the function returns -ENODEV. This function calls the
+ * relevant PHY ethtool API to set the PHY.
+ * Issue e.g. "ethtool -s ethX speed 1000" under linux prompt to execute this
+ * function.
+ */
+static int arc_emac_set_settings(struct net_device *ndev,
+                                struct ethtool_cmd *cmd)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       return phy_ethtool_sset(priv->phy_dev, cmd);
+}
+
+/**
+ * arc_emac_get_drvinfo - Get EMAC driver information.
+ * @ndev:      Pointer to net_device structure.
+ * @info:      Pointer to ethtool_drvinfo structure.
+ *
+ * This implements ethtool command for getting the driver information.
+ * Issue "ethtool -i ethX" under linux prompt to execute this function.
+ */
+static void arc_emac_get_drvinfo(struct net_device *ndev,
+                                struct ethtool_drvinfo *info)
+{
+       strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+       strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+}
+
+static const struct ethtool_ops arc_emac_ethtool_ops = {
+       .get_settings   = arc_emac_get_settings,
+       .set_settings   = arc_emac_set_settings,
+       .get_drvinfo    = arc_emac_get_drvinfo,
+       .get_link       = ethtool_op_get_link,
+};
+
+#define FIRST_OR_LAST_MASK     (FIRST_MASK | LAST_MASK)
+
+/**
+ * arc_emac_tx_clean - clears processed by EMAC Tx BDs.
+ * @ndev:      Pointer to the network device.
+ */
+static void arc_emac_tx_clean(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &priv->stats;
+       unsigned int i;
+
+       for (i = 0; i < TX_BD_NUM; i++) {
+               unsigned int *txbd_dirty = &priv->txbd_dirty;
+               struct arc_emac_bd *txbd = &priv->txbd[*txbd_dirty];
+               struct buffer_state *tx_buff = &priv->tx_buff[*txbd_dirty];
+               struct sk_buff *skb = tx_buff->skb;
+               unsigned int info = le32_to_cpu(txbd->info);
+
+               *txbd_dirty = (*txbd_dirty + 1) % TX_BD_NUM;
+
+               if ((info & FOR_EMAC) || !txbd->data)
+                       break;
+
+               if (unlikely(info & (DROP | DEFR | LTCL | UFLO))) {
+                       stats->tx_errors++;
+                       stats->tx_dropped++;
+
+                       if (info & DEFR)
+                               stats->tx_carrier_errors++;
+
+                       if (info & LTCL)
+                               stats->collisions++;
+
+                       if (info & UFLO)
+                               stats->tx_fifo_errors++;
+               } else if (likely(info & FIRST_OR_LAST_MASK)) {
+                       stats->tx_packets++;
+                       stats->tx_bytes += skb->len;
+               }
+
+               dma_unmap_single(&ndev->dev, dma_unmap_addr(tx_buff, addr),
+                                dma_unmap_len(tx_buff, len), DMA_TO_DEVICE);
+
+               /* return the sk_buff to system */
+               dev_kfree_skb_irq(skb);
+
+               txbd->data = 0;
+               txbd->info = 0;
+
+               if (netif_queue_stopped(ndev))
+                       netif_wake_queue(ndev);
+       }
+}
+
+/**
+ * arc_emac_rx - processing of Rx packets.
+ * @ndev:      Pointer to the network device.
+ * @budget:    How many BDs to process on 1 call.
+ *
+ * returns:    Number of processed BDs
+ *
+ * Iterate through Rx BDs and deliver received packages to upper layer.
+ */
+static int arc_emac_rx(struct net_device *ndev, int budget)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       unsigned int work_done;
+
+       for (work_done = 0; work_done <= budget; work_done++) {
+               unsigned int *last_rx_bd = &priv->last_rx_bd;
+               struct net_device_stats *stats = &priv->stats;
+               struct buffer_state *rx_buff = &priv->rx_buff[*last_rx_bd];
+               struct arc_emac_bd *rxbd = &priv->rxbd[*last_rx_bd];
+               unsigned int pktlen, info = le32_to_cpu(rxbd->info);
+               struct sk_buff *skb;
+               dma_addr_t addr;
+
+               if (unlikely((info & OWN_MASK) == FOR_EMAC))
+                       break;
+
+               /* Make a note that we saw a packet at this BD.
+                * So next time, driver starts from this + 1
+                */
+               *last_rx_bd = (*last_rx_bd + 1) % RX_BD_NUM;
+
+               if (unlikely((info & FIRST_OR_LAST_MASK) !=
+                            FIRST_OR_LAST_MASK)) {
+                       /* We pre-allocate buffers of MTU size so incoming
+                        * packets won't be split/chained.
+                        */
+                       if (net_ratelimit())
+                               netdev_err(ndev, "incomplete packet received\n");
+
+                       /* Return ownership to EMAC */
+                       rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);
+                       stats->rx_errors++;
+                       stats->rx_length_errors++;
+                       continue;
+               }
+
+               pktlen = info & LEN_MASK;
+               stats->rx_packets++;
+               stats->rx_bytes += pktlen;
+               skb = rx_buff->skb;
+               skb_put(skb, pktlen);
+               skb->dev = ndev;
+               skb->protocol = eth_type_trans(skb, ndev);
+
+               dma_unmap_single(&ndev->dev, dma_unmap_addr(rx_buff, addr),
+                                dma_unmap_len(rx_buff, len), DMA_FROM_DEVICE);
+
+               /* Prepare the BD for next cycle */
+               rx_buff->skb = netdev_alloc_skb_ip_align(ndev,
+                                                        EMAC_BUFFER_SIZE);
+               if (unlikely(!rx_buff->skb)) {
+                       stats->rx_errors++;
+                       /* Because receive_skb is below, increment rx_dropped */
+                       stats->rx_dropped++;
+                       continue;
+               }
+
+               /* receive_skb only if new skb was allocated to avoid holes */
+               netif_receive_skb(skb);
+
+               addr = dma_map_single(&ndev->dev, (void *)rx_buff->skb->data,
+                                     EMAC_BUFFER_SIZE, DMA_FROM_DEVICE);
+               if (dma_mapping_error(&ndev->dev, addr)) {
+                       if (net_ratelimit())
+                               netdev_err(ndev, "cannot dma map\n");
+                       dev_kfree_skb(rx_buff->skb);
+                       stats->rx_errors++;
+                       continue;
+               }
+               dma_unmap_addr_set(rx_buff, addr, addr);
+               dma_unmap_len_set(rx_buff, len, EMAC_BUFFER_SIZE);
+
+               rxbd->data = cpu_to_le32(addr);
+
+               /* Make sure pointer to data buffer is set */
+               wmb();
+
+               /* Return ownership to EMAC */
+               rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);
+       }
+
+       return work_done;
+}
+
+/**
+ * arc_emac_poll - NAPI poll handler.
+ * @napi:      Pointer to napi_struct structure.
+ * @budget:    How many BDs to process on 1 call.
+ *
+ * returns:    Number of processed BDs
+ */
+static int arc_emac_poll(struct napi_struct *napi, int budget)
+{
+       struct net_device *ndev = napi->dev;
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       unsigned int work_done;
+
+       arc_emac_tx_clean(ndev);
+
+       work_done = arc_emac_rx(ndev, budget);
+       if (work_done < budget) {
+               napi_complete(napi);
+               arc_reg_or(priv, R_ENABLE, RXINT_MASK);
+       }
+
+       return work_done;
+}
+
+/**
+ * arc_emac_intr - Global interrupt handler for EMAC.
+ * @irq:               irq number.
+ * @dev_instance:      device instance.
+ *
+ * returns: IRQ_HANDLED for all cases.
+ *
+ * ARC EMAC has only 1 interrupt line, and depending on bits raised in
+ * STATUS register we may tell what is a reason for interrupt to fire.
+ */
+static irqreturn_t arc_emac_intr(int irq, void *dev_instance)
+{
+       struct net_device *ndev = dev_instance;
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &priv->stats;
+       unsigned int status;
+
+       status = arc_reg_get(priv, R_STATUS);
+       status &= ~MDIO_MASK;
+
+       /* Reset all flags except "MDIO complete" */
+       arc_reg_set(priv, R_STATUS, status);
+
+       if (status & RXINT_MASK) {
+               if (likely(napi_schedule_prep(&priv->napi))) {
+                       arc_reg_clr(priv, R_ENABLE, RXINT_MASK);
+                       __napi_schedule(&priv->napi);
+               }
+       }
+
+       if (status & ERR_MASK) {
+               /* MSER/RXCR/RXFR/RXFL interrupt fires on corresponding
+                * 8-bit error counter overrun.
+                */
+
+               if (status & MSER_MASK) {
+                       stats->rx_missed_errors += 0x100;
+                       stats->rx_errors += 0x100;
+               }
+
+               if (status & RXCR_MASK) {
+                       stats->rx_crc_errors += 0x100;
+                       stats->rx_errors += 0x100;
+               }
+
+               if (status & RXFR_MASK) {
+                       stats->rx_frame_errors += 0x100;
+                       stats->rx_errors += 0x100;
+               }
+
+               if (status & RXFL_MASK) {
+                       stats->rx_over_errors += 0x100;
+                       stats->rx_errors += 0x100;
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * arc_emac_open - Open the network device.
+ * @ndev:      Pointer to the network device.
+ *
+ * returns: 0, on success or non-zero error value on failure.
+ *
+ * This function sets the MAC address, requests and enables an IRQ
+ * for the EMAC device and starts the Tx queue.
+ * It also connects to the phy device.
+ */
+static int arc_emac_open(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct phy_device *phy_dev = priv->phy_dev;
+       int i;
+
+       phy_dev->autoneg = AUTONEG_ENABLE;
+       phy_dev->speed = 0;
+       phy_dev->duplex = 0;
+       phy_dev->advertising = phy_dev->supported;
+
+       if (priv->max_speed > 100) {
+               phy_dev->advertising &= PHY_GBIT_FEATURES;
+       } else if (priv->max_speed <= 100) {
+               phy_dev->advertising &= PHY_BASIC_FEATURES;
+               if (priv->max_speed <= 10) {
+                       phy_dev->advertising &= ~SUPPORTED_100baseT_Half;
+                       phy_dev->advertising &= ~SUPPORTED_100baseT_Full;
+               }
+       }
+
+       priv->last_rx_bd = 0;
+
+       /* Allocate and set buffers for Rx BD's */
+       for (i = 0; i < RX_BD_NUM; i++) {
+               dma_addr_t addr;
+               unsigned int *last_rx_bd = &priv->last_rx_bd;
+               struct arc_emac_bd *rxbd = &priv->rxbd[*last_rx_bd];
+               struct buffer_state *rx_buff = &priv->rx_buff[*last_rx_bd];
+
+               rx_buff->skb = netdev_alloc_skb_ip_align(ndev,
+                                                        EMAC_BUFFER_SIZE);
+               if (unlikely(!rx_buff->skb))
+                       return -ENOMEM;
+
+               addr = dma_map_single(&ndev->dev, (void *)rx_buff->skb->data,
+                                     EMAC_BUFFER_SIZE, DMA_FROM_DEVICE);
+               if (dma_mapping_error(&ndev->dev, addr)) {
+                       netdev_err(ndev, "cannot dma map\n");
+                       dev_kfree_skb(rx_buff->skb);
+                       return -ENOMEM;
+               }
+               dma_unmap_addr_set(rx_buff, addr, addr);
+               dma_unmap_len_set(rx_buff, len, EMAC_BUFFER_SIZE);
+
+               rxbd->data = cpu_to_le32(addr);
+
+               /* Make sure pointer to data buffer is set */
+               wmb();
+
+               /* Return ownership to EMAC */
+               rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);
+
+               *last_rx_bd = (*last_rx_bd + 1) % RX_BD_NUM;
+       }
+
+       /* Clean Tx BD's */
+       memset(priv->txbd, 0, TX_RING_SZ);
+
+       /* Initialize logical address filter */
+       arc_reg_set(priv, R_LAFL, 0);
+       arc_reg_set(priv, R_LAFH, 0);
+
+       /* Set BD ring pointers for device side */
+       arc_reg_set(priv, R_RX_RING, (unsigned int)priv->rxbd_dma);
+       arc_reg_set(priv, R_TX_RING, (unsigned int)priv->txbd_dma);
+
+       /* Enable interrupts */
+       arc_reg_set(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
+
+       /* Set CONTROL */
+       arc_reg_set(priv, R_CTRL,
+                    (RX_BD_NUM << 24) |        /* RX BD table length */
+                    (TX_BD_NUM << 16) |        /* TX BD table length */
+                    TXRN_MASK | RXRN_MASK);
+
+       napi_enable(&priv->napi);
+
+       /* Enable EMAC */
+       arc_reg_or(priv, R_CTRL, EN_MASK);
+
+       phy_start_aneg(priv->phy_dev);
+
+       netif_start_queue(ndev);
+
+       return 0;
+}
+
+/**
+ * arc_emac_stop - Close the network device.
+ * @ndev:      Pointer to the network device.
+ *
+ * This function stops the Tx queue, disables interrupts and frees the IRQ for
+ * the EMAC device.
+ * It also disconnects the PHY device associated with the EMAC device.
+ */
+static int arc_emac_stop(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+
+       napi_disable(&priv->napi);
+       netif_stop_queue(ndev);
+
+       /* Disable interrupts */
+       arc_reg_clr(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
+
+       /* Disable EMAC */
+       arc_reg_clr(priv, R_CTRL, EN_MASK);
+
+       return 0;
+}
+
+/**
+ * arc_emac_stats - Get system network statistics.
+ * @ndev:      Pointer to net_device structure.
+ *
+ * Returns the address of the device statistics structure.
+ * Statistics are updated in interrupt handler.
+ */
+static struct net_device_stats *arc_emac_stats(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &priv->stats;
+       unsigned long miss, rxerr;
+       u8 rxcrc, rxfram, rxoflow;
+
+       rxerr = arc_reg_get(priv, R_RXERR);
+       miss = arc_reg_get(priv, R_MISS);
+
+       rxcrc = rxerr;
+       rxfram = rxerr >> 8;
+       rxoflow = rxerr >> 16;
+
+       stats->rx_errors += miss;
+       stats->rx_errors += rxcrc + rxfram + rxoflow;
+
+       stats->rx_over_errors += rxoflow;
+       stats->rx_frame_errors += rxfram;
+       stats->rx_crc_errors += rxcrc;
+       stats->rx_missed_errors += miss;
+
+       return stats;
+}
+
+/**
+ * arc_emac_tx - Starts the data transmission.
+ * @skb:       sk_buff pointer that contains data to be Transmitted.
+ * @ndev:      Pointer to net_device structure.
+ *
+ * returns: NETDEV_TX_OK, on success
+ *             NETDEV_TX_BUSY, if any of the descriptors are not free.
+ *
+ * This function is invoked from upper layers to initiate transmission.
+ */
+static int arc_emac_tx(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       unsigned int len, *txbd_curr = &priv->txbd_curr;
+       struct net_device_stats *stats = &priv->stats;
+       __le32 *info = &priv->txbd[*txbd_curr].info;
+       dma_addr_t addr;
+
+       if (skb_padto(skb, ETH_ZLEN))
+               return NETDEV_TX_OK;
+
+       len = max_t(unsigned int, ETH_ZLEN, skb->len);
+
+       /* EMAC still holds this buffer in its possession.
+        * CPU must not modify this buffer descriptor
+        */
+       if (unlikely((le32_to_cpu(*info) & OWN_MASK) == FOR_EMAC)) {
+               netif_stop_queue(ndev);
+               return NETDEV_TX_BUSY;
+       }
+
+       addr = dma_map_single(&ndev->dev, (void *)skb->data, len,
+                             DMA_TO_DEVICE);
+
+       if (unlikely(dma_mapping_error(&ndev->dev, addr))) {
+               stats->tx_dropped++;
+               stats->tx_errors++;
+               dev_kfree_skb(skb);
+               return NETDEV_TX_OK;
+       }
+       dma_unmap_addr_set(&priv->tx_buff[*txbd_curr], addr, addr);
+       dma_unmap_len_set(&priv->tx_buff[*txbd_curr], len, len);
+
+       priv->tx_buff[*txbd_curr].skb = skb;
+       priv->txbd[*txbd_curr].data = cpu_to_le32(addr);
+
+       /* Make sure pointer to data buffer is set */
+       wmb();
+
+       *info = cpu_to_le32(FOR_EMAC | FIRST_OR_LAST_MASK | len);
+
+       /* Increment index to point to the next BD */
+       *txbd_curr = (*txbd_curr + 1) % TX_BD_NUM;
+
+       /* Get "info" of the next BD */
+       info = &priv->txbd[*txbd_curr].info;
+
+       /* Check if if Tx BD ring is full - next BD is still owned by EMAC */
+       if (unlikely((le32_to_cpu(*info) & OWN_MASK) == FOR_EMAC))
+               netif_stop_queue(ndev);
+
+       arc_reg_set(priv, R_STATUS, TXPL_MASK);
+
+       skb_tx_timestamp(skb);
+
+       return NETDEV_TX_OK;
+}
+
+/**
+ * arc_emac_set_address - Set the MAC address for this device.
+ * @ndev:      Pointer to net_device structure.
+ * @p:         6 byte Address to be written as MAC address.
+ *
+ * This function copies the HW address from the sockaddr structure to the
+ * net_device structure and updates the address in HW.
+ *
+ * returns:    -EBUSY if the net device is busy or 0 if the address is set
+ *             successfully.
+ */
+static int arc_emac_set_address(struct net_device *ndev, void *p)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct sockaddr *addr = p;
+       unsigned int addr_low, addr_hi;
+
+       if (netif_running(ndev))
+               return -EBUSY;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
+
+       addr_low = le32_to_cpu(*(__le32 *) &ndev->dev_addr[0]);
+       addr_hi = le16_to_cpu(*(__le16 *) &ndev->dev_addr[4]);
+
+       arc_reg_set(priv, R_ADDRL, addr_low);
+       arc_reg_set(priv, R_ADDRH, addr_hi);
+
+       return 0;
+}
+
+static const struct net_device_ops arc_emac_netdev_ops = {
+       .ndo_open               = arc_emac_open,
+       .ndo_stop               = arc_emac_stop,
+       .ndo_start_xmit         = arc_emac_tx,
+       .ndo_set_mac_address    = arc_emac_set_address,
+       .ndo_get_stats          = arc_emac_stats,
+};
+
+static int arc_emac_probe(struct platform_device *pdev)
+{
+       struct resource res_regs, res_irq;
+       struct device_node *phy_node;
+       struct arc_emac_priv *priv;
+       struct net_device *ndev;
+       const char *mac_addr;
+       unsigned int id, clock_frequency;
+       int err;
+
+       if (!pdev->dev.of_node)
+               return -ENODEV;
+
+       /* Get PHY from device tree */
+       phy_node = of_parse_phandle(pdev->dev.of_node, "phy", 0);
+       if (!phy_node) {
+               dev_err(&pdev->dev, "failed to retrieve phy description from device tree\n");
+               return -ENODEV;
+       }
+
+       /* Get EMAC registers base address from device tree */
+       err = of_address_to_resource(pdev->dev.of_node, 0, &res_regs);
+       if (err) {
+               dev_err(&pdev->dev, "failed to retrieve registers base from device tree\n");
+               return -ENODEV;
+       }
+
+       /* Get CPU clock frequency from device tree */
+       if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+                                &clock_frequency)) {
+               dev_err(&pdev->dev, "failed to retrieve <clock-frequency> from device tree\n");
+               return -EINVAL;
+       }
+
+       /* Get IRQ from device tree */
+       err = of_irq_to_resource(pdev->dev.of_node, 0, &res_irq);
+       if (!err) {
+               dev_err(&pdev->dev, "failed to retrieve <irq> value from device tree\n");
+               return -ENODEV;
+       }
+
+       ndev = alloc_etherdev(sizeof(struct arc_emac_priv));
+       if (!ndev)
+               return -ENOMEM;
+
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       ndev->netdev_ops = &arc_emac_netdev_ops;
+       ndev->ethtool_ops = &arc_emac_ethtool_ops;
+       ndev->watchdog_timeo = TX_TIMEOUT;
+       /* FIXME :: no multicast support yet */
+       ndev->flags &= ~IFF_MULTICAST;
+
+       priv = netdev_priv(ndev);
+       priv->dev = &pdev->dev;
+       priv->ndev = ndev;
+
+       priv->regs = devm_ioremap_resource(&pdev->dev, &res_regs);
+       if (IS_ERR(priv->regs)) {
+               err = PTR_ERR(priv->regs);
+               goto out;
+       }
+       dev_dbg(&pdev->dev, "Registers base address is 0x%p\n", priv->regs);
+
+       id = arc_reg_get(priv, R_ID);
+
+       /* Check for EMAC revision 5 or 7, magic number */
+       if (!(id == 0x0005fd02 || id == 0x0007fd02)) {
+               dev_err(&pdev->dev, "ARC EMAC not detected, id=0x%x\n", id);
+               err = -ENODEV;
+               goto out;
+       }
+       dev_info(&pdev->dev, "ARC EMAC detected with id: 0x%x\n", id);
+
+       /* Set poll rate so that it polls every 1 ms */
+       arc_reg_set(priv, R_POLLRATE, clock_frequency / 1000000);
+
+       /* Get max speed of operation from device tree */
+       if (of_property_read_u32(pdev->dev.of_node, "max-speed",
+                                &priv->max_speed)) {
+               dev_err(&pdev->dev, "failed to retrieve <max-speed> from device tree\n");
+               err = -EINVAL;
+               goto out;
+       }
+
+       ndev->irq = res_irq.start;
+       dev_info(&pdev->dev, "IRQ is %d\n", ndev->irq);
+
+       /* Register interrupt handler for device */
+       err = devm_request_irq(&pdev->dev, ndev->irq, arc_emac_intr, 0,
+                              ndev->name, ndev);
+       if (err) {
+               dev_err(&pdev->dev, "could not allocate IRQ\n");
+               goto out;
+       }
+
+       /* Get MAC address from device tree */
+       mac_addr = of_get_mac_address(pdev->dev.of_node);
+
+       if (!mac_addr || !is_valid_ether_addr(mac_addr))
+               eth_hw_addr_random(ndev);
+       else
+               memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
+
+       dev_info(&pdev->dev, "MAC address is now %pM\n", ndev->dev_addr);
+
+       /* Do 1 allocation instead of 2 separate ones for Rx and Tx BD rings */
+       priv->rxbd = dmam_alloc_coherent(&pdev->dev, RX_RING_SZ + TX_RING_SZ,
+                                        &priv->rxbd_dma, GFP_KERNEL);
+
+       if (!priv->rxbd) {
+               dev_err(&pdev->dev, "failed to allocate data buffers\n");
+               err = -ENOMEM;
+               goto out;
+       }
+
+       priv->txbd = priv->rxbd + RX_BD_NUM;
+
+       priv->txbd_dma = priv->rxbd_dma + RX_RING_SZ;
+       dev_dbg(&pdev->dev, "EMAC Device addr: Rx Ring [0x%x], Tx Ring[%x]\n",
+               (unsigned int)priv->rxbd_dma, (unsigned int)priv->txbd_dma);
+
+       err = arc_mdio_probe(pdev, priv);
+       if (err) {
+               dev_err(&pdev->dev, "failed to probe MII bus\n");
+               goto out;
+       }
+
+       priv->phy_dev = of_phy_connect(ndev, phy_node, arc_emac_adjust_link, 0,
+                                      PHY_INTERFACE_MODE_MII);
+       if (!priv->phy_dev) {
+               dev_err(&pdev->dev, "of_phy_connect() failed\n");
+               err = -ENODEV;
+               goto out;
+       }
+
+       dev_info(&pdev->dev, "connected to %s phy with id 0x%x\n",
+                priv->phy_dev->drv->name, priv->phy_dev->phy_id);
+
+       netif_napi_add(ndev, &priv->napi, arc_emac_poll, ARC_EMAC_NAPI_WEIGHT);
+
+       err = register_netdev(ndev);
+       if (err) {
+               netif_napi_del(&priv->napi);
+               dev_err(&pdev->dev, "failed to register network device\n");
+               goto out;
+       }
+
+       return 0;
+
+out:
+       free_netdev(ndev);
+       return err;
+}
+
+static int arc_emac_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+
+       phy_disconnect(priv->phy_dev);
+       priv->phy_dev = NULL;
+       arc_mdio_remove(priv);
+       unregister_netdev(ndev);
+       netif_napi_del(&priv->napi);
+       free_netdev(ndev);
+
+       return 0;
+}
+
+static const struct of_device_id arc_emac_dt_ids[] = {
+       { .compatible = "snps,arc-emac" },
+       { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, arc_emac_dt_ids);
+
+static struct platform_driver arc_emac_driver = {
+       .probe = arc_emac_probe,
+       .remove = arc_emac_remove,
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table  = arc_emac_dt_ids,
+               },
+};
+
+module_platform_driver(arc_emac_driver);
+
+MODULE_AUTHOR("Alexey Brodkin <abrodkin@synopsys.com>");
+MODULE_DESCRIPTION("ARC EMAC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/arc/emac_mdio.c b/drivers/net/ethernet/arc/emac_mdio.c
new file mode 100644 (file)
index 0000000..26ba242
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2004-2013 Synopsys, Inc. (www.synopsys.com)
+ *
+ * MDIO implementation for ARC EMAC
+ */
+
+#include <linux/delay.h>
+#include <linux/of_mdio.h>
+#include <linux/platform_device.h>
+
+#include "emac.h"
+
+/* Number of seconds we wait for "MDIO complete" flag to appear */
+#define ARC_MDIO_COMPLETE_POLL_COUNT   1
+
+/**
+ * arc_mdio_complete_wait - Waits until MDIO transaction is completed.
+ * @priv:      Pointer to ARC EMAC private data structure.
+ *
+ * returns:    0 on success, -ETIMEDOUT on a timeout.
+ */
+static int arc_mdio_complete_wait(struct arc_emac_priv *priv)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARC_MDIO_COMPLETE_POLL_COUNT * 40; i++) {
+               unsigned int status = arc_reg_get(priv, R_STATUS);
+
+               status &= MDIO_MASK;
+
+               if (status) {
+                       /* Reset "MDIO complete" flag */
+                       arc_reg_set(priv, R_STATUS, status);
+                       return 0;
+               }
+
+               msleep(25);
+       }
+
+       return -ETIMEDOUT;
+}
+
+/**
+ * arc_mdio_read - MDIO interface read function.
+ * @bus:       Pointer to MII bus structure.
+ * @phy_addr:  Address of the PHY device.
+ * @reg_num:   PHY register to read.
+ *
+ * returns:    The register contents on success, -ETIMEDOUT on a timeout.
+ *
+ * Reads the contents of the requested register from the requested PHY
+ * address.
+ */
+static int arc_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
+{
+       struct arc_emac_priv *priv = bus->priv;
+       unsigned int value;
+       int error;
+
+       arc_reg_set(priv, R_MDIO,
+                   0x60020000 | (phy_addr << 23) | (reg_num << 18));
+
+       error = arc_mdio_complete_wait(priv);
+       if (error < 0)
+               return error;
+
+       value = arc_reg_get(priv, R_MDIO) & 0xffff;
+
+       dev_dbg(priv->dev, "arc_mdio_read(phy_addr=%i, reg_num=%x) = %x\n",
+               phy_addr, reg_num, value);
+
+       return value;
+}
+
+/**
+ * arc_mdio_write - MDIO interface write function.
+ * @bus:       Pointer to MII bus structure.
+ * @phy_addr:  Address of the PHY device.
+ * @reg_num:   PHY register to write to.
+ * @value:     Value to be written into the register.
+ *
+ * returns:    0 on success, -ETIMEDOUT on a timeout.
+ *
+ * Writes the value to the requested register.
+ */
+static int arc_mdio_write(struct mii_bus *bus, int phy_addr,
+                         int reg_num, u16 value)
+{
+       struct arc_emac_priv *priv = bus->priv;
+
+       dev_dbg(priv->dev,
+               "arc_mdio_write(phy_addr=%i, reg_num=%x, value=%x)\n",
+               phy_addr, reg_num, value);
+
+       arc_reg_set(priv, R_MDIO,
+                    0x50020000 | (phy_addr << 23) | (reg_num << 18) | value);
+
+       return arc_mdio_complete_wait(priv);
+}
+
+/**
+ * arc_mdio_probe - MDIO probe function.
+ * @pdev:      Pointer to platform device.
+ * @priv:      Pointer to ARC EMAC private data structure.
+ *
+ * returns:    0 on success, -ENOMEM when mdiobus_alloc
+ * (to allocate memory for MII bus structure) fails.
+ *
+ * Sets up and registers the MDIO interface.
+ */
+int arc_mdio_probe(struct platform_device *pdev, struct arc_emac_priv *priv)
+{
+       struct mii_bus *bus;
+       int error;
+
+       bus = mdiobus_alloc();
+       if (!bus)
+               return -ENOMEM;
+
+       priv->bus = bus;
+       bus->priv = priv;
+       bus->parent = priv->dev;
+       bus->name = "Synopsys MII Bus",
+       bus->read = &arc_mdio_read;
+       bus->write = &arc_mdio_write;
+
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
+
+       error = of_mdiobus_register(bus, pdev->dev.of_node);
+       if (error) {
+               dev_err(priv->dev, "cannot register MDIO bus %s\n", bus->name);
+               mdiobus_free(bus);
+               return error;
+       }
+
+       return 0;
+}
+
+/**
+ * arc_mdio_remove - MDIO remove function.
+ * @priv:      Pointer to ARC EMAC private data structure.
+ *
+ * Unregisters the MDIO and frees any associate memory for MII bus.
+ */
+int arc_mdio_remove(struct arc_emac_priv *priv)
+{
+       mdiobus_unregister(priv->bus);
+       mdiobus_free(priv->bus);
+       priv->bus = NULL;
+
+       return 0;
+}
index ad6aa1e..58ad37c 100644 (file)
@@ -22,7 +22,6 @@ config ATL2
        tristate "Atheros L2 Fast Ethernet support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          This driver supports the Atheros L2 fast ethernet adapter.
@@ -34,7 +33,6 @@ config ATL1
        tristate "Atheros/Attansic L1 Gigabit Ethernet support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          This driver supports the Atheros/Attansic L1 gigabit ethernet
@@ -47,7 +45,6 @@ config ATL1E
        tristate "Atheros L1E Gigabit Ethernet support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          This driver supports the Atheros L1E gigabit ethernet adapter.
@@ -59,7 +56,6 @@ config ATL1C
        tristate "Atheros L1C Gigabit Ethernet support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          This driver supports the Atheros L1C gigabit ethernet adapter.
@@ -71,7 +67,6 @@ config ALX
        tristate "Qualcomm Atheros AR816x/AR817x support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MDIO
        help
          This driver supports the Qualcomm Atheros L1F ethernet adapter,
index 50b3ae2..d71103d 100644 (file)
@@ -85,16 +85,16 @@ struct alx_priv {
        struct {
                dma_addr_t dma;
                void *virt;
-               int size;
+               unsigned int size;
        } descmem;
 
        /* protect int_mask updates */
        spinlock_t irq_lock;
        u32 int_mask;
 
-       int tx_ringsz;
-       int rx_ringsz;
-       int rxbuf_size;
+       unsigned int tx_ringsz;
+       unsigned int rx_ringsz;
+       unsigned int rxbuf_size;
 
        struct napi_struct napi;
        struct alx_tx_queue txq;
index 6fa2aec..45b3650 100644 (file)
 #include "reg.h"
 #include "hw.h"
 
+static u32 alx_get_supported_speeds(struct alx_hw *hw)
+{
+       u32 supported = SUPPORTED_10baseT_Half |
+                       SUPPORTED_10baseT_Full |
+                       SUPPORTED_100baseT_Half |
+                       SUPPORTED_100baseT_Full;
+
+       if (alx_hw_giga(hw))
+               supported |= SUPPORTED_1000baseT_Full;
+
+       BUILD_BUG_ON(SUPPORTED_10baseT_Half != ADVERTISED_10baseT_Half);
+       BUILD_BUG_ON(SUPPORTED_10baseT_Full != ADVERTISED_10baseT_Full);
+       BUILD_BUG_ON(SUPPORTED_100baseT_Half != ADVERTISED_100baseT_Half);
+       BUILD_BUG_ON(SUPPORTED_100baseT_Full != ADVERTISED_100baseT_Full);
+       BUILD_BUG_ON(SUPPORTED_1000baseT_Full != ADVERTISED_1000baseT_Full);
+
+       return supported;
+}
 
 static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
 {
        struct alx_priv *alx = netdev_priv(netdev);
        struct alx_hw *hw = &alx->hw;
 
-       ecmd->supported = SUPPORTED_10baseT_Half |
-                         SUPPORTED_10baseT_Full |
-                         SUPPORTED_100baseT_Half |
-                         SUPPORTED_100baseT_Full |
-                         SUPPORTED_Autoneg |
+       ecmd->supported = SUPPORTED_Autoneg |
                          SUPPORTED_TP |
-                         SUPPORTED_Pause;
+                         SUPPORTED_Pause |
+                         SUPPORTED_Asym_Pause;
        if (alx_hw_giga(hw))
                ecmd->supported |= SUPPORTED_1000baseT_Full;
+       ecmd->supported |= alx_get_supported_speeds(hw);
 
        ecmd->advertising = ADVERTISED_TP;
        if (hw->adv_cfg & ADVERTISED_Autoneg)
@@ -68,6 +84,7 @@ static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
 
        ecmd->port = PORT_TP;
        ecmd->phy_address = 0;
+
        if (hw->adv_cfg & ADVERTISED_Autoneg)
                ecmd->autoneg = AUTONEG_ENABLE;
        else
@@ -85,14 +102,8 @@ static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                }
        }
 
-       if (hw->link_speed != SPEED_UNKNOWN) {
-               ethtool_cmd_speed_set(ecmd,
-                                     hw->link_speed - hw->link_speed % 10);
-               ecmd->duplex = hw->link_speed % 10;
-       } else {
-               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
-               ecmd->duplex = DUPLEX_UNKNOWN;
-       }
+       ethtool_cmd_speed_set(ecmd, hw->link_speed);
+       ecmd->duplex = hw->duplex;
 
        return 0;
 }
@@ -106,28 +117,15 @@ static int alx_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
        ASSERT_RTNL();
 
        if (ecmd->autoneg == AUTONEG_ENABLE) {
-               if (ecmd->advertising & ADVERTISED_1000baseT_Half)
+               if (ecmd->advertising & ~alx_get_supported_speeds(hw))
                        return -EINVAL;
                adv_cfg = ecmd->advertising | ADVERTISED_Autoneg;
        } else {
-               int speed = ethtool_cmd_speed(ecmd);
-
-               switch (speed + ecmd->duplex) {
-               case SPEED_10 + DUPLEX_HALF:
-                       adv_cfg = ADVERTISED_10baseT_Half;
-                       break;
-               case SPEED_10 + DUPLEX_FULL:
-                       adv_cfg = ADVERTISED_10baseT_Full;
-                       break;
-               case SPEED_100 + DUPLEX_HALF:
-                       adv_cfg = ADVERTISED_100baseT_Half;
-                       break;
-               case SPEED_100 + DUPLEX_FULL:
-                       adv_cfg = ADVERTISED_100baseT_Full;
-                       break;
-               default:
+               adv_cfg = alx_speed_to_ethadv(ethtool_cmd_speed(ecmd),
+                                             ecmd->duplex);
+
+               if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full)
                        return -EINVAL;
-               }
        }
 
        hw->adv_cfg = adv_cfg;
@@ -140,21 +138,10 @@ static void alx_get_pauseparam(struct net_device *netdev,
        struct alx_priv *alx = netdev_priv(netdev);
        struct alx_hw *hw = &alx->hw;
 
-       if (hw->flowctrl & ALX_FC_ANEG &&
-           hw->adv_cfg & ADVERTISED_Autoneg)
-               pause->autoneg = AUTONEG_ENABLE;
-       else
-               pause->autoneg = AUTONEG_DISABLE;
-
-       if (hw->flowctrl & ALX_FC_TX)
-               pause->tx_pause = 1;
-       else
-               pause->tx_pause = 0;
-
-       if (hw->flowctrl & ALX_FC_RX)
-               pause->rx_pause = 1;
-       else
-               pause->rx_pause = 0;
+       pause->autoneg = !!(hw->flowctrl & ALX_FC_ANEG &&
+                           hw->adv_cfg & ADVERTISED_Autoneg);
+       pause->tx_pause = !!(hw->flowctrl & ALX_FC_TX);
+       pause->rx_pause = !!(hw->flowctrl & ALX_FC_RX);
 }
 
 
@@ -187,7 +174,8 @@ static int alx_set_pauseparam(struct net_device *netdev,
 
        if (reconfig_phy) {
                err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc);
-               return err;
+               if (err)
+                       return err;
        }
 
        /* flow control on mac */
@@ -213,60 +201,12 @@ static void alx_set_msglevel(struct net_device *netdev, u32 data)
        alx->msg_enable = data;
 }
 
-static void alx_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
-{
-       struct alx_priv *alx = netdev_priv(netdev);
-       struct alx_hw *hw = &alx->hw;
-
-       wol->supported = WAKE_MAGIC | WAKE_PHY;
-       wol->wolopts = 0;
-
-       if (hw->sleep_ctrl & ALX_SLEEP_WOL_MAGIC)
-               wol->wolopts |= WAKE_MAGIC;
-       if (hw->sleep_ctrl & ALX_SLEEP_WOL_PHY)
-               wol->wolopts |= WAKE_PHY;
-}
-
-static int alx_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
-{
-       struct alx_priv *alx = netdev_priv(netdev);
-       struct alx_hw *hw = &alx->hw;
-
-       if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |
-                           WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
-               return -EOPNOTSUPP;
-
-       hw->sleep_ctrl = 0;
-
-       if (wol->wolopts & WAKE_MAGIC)
-               hw->sleep_ctrl |= ALX_SLEEP_WOL_MAGIC;
-       if (wol->wolopts & WAKE_PHY)
-               hw->sleep_ctrl |= ALX_SLEEP_WOL_PHY;
-
-       device_set_wakeup_enable(&alx->hw.pdev->dev, hw->sleep_ctrl);
-
-       return 0;
-}
-
-static void alx_get_drvinfo(struct net_device *netdev,
-                           struct ethtool_drvinfo *drvinfo)
-{
-       struct alx_priv *alx = netdev_priv(netdev);
-
-       strlcpy(drvinfo->driver, alx_drv_name, sizeof(drvinfo->driver));
-       strlcpy(drvinfo->bus_info, pci_name(alx->hw.pdev),
-               sizeof(drvinfo->bus_info));
-}
-
 const struct ethtool_ops alx_ethtool_ops = {
        .get_settings   = alx_get_settings,
        .set_settings   = alx_set_settings,
        .get_pauseparam = alx_get_pauseparam,
        .set_pauseparam = alx_set_pauseparam,
-       .get_drvinfo    = alx_get_drvinfo,
        .get_msglevel   = alx_get_msglevel,
        .set_msglevel   = alx_set_msglevel,
-       .get_wol        = alx_get_wol,
-       .set_wol        = alx_set_wol,
        .get_link       = ethtool_op_get_link,
 };
index 220a16a..1e8c24a 100644 (file)
@@ -282,8 +282,8 @@ static bool alx_read_macaddr(struct alx_hw *hw, u8 *addr)
        mac1 = alx_read_mem32(hw, ALX_STAD1);
 
        /* addr should be big-endian */
-       *(__be32 *)(addr + 2) = cpu_to_be32(mac0);
-       *(__be16 *)addr = cpu_to_be16(mac1);
+       put_unaligned(cpu_to_be32(mac0), (__be32 *)(addr + 2));
+       put_unaligned(cpu_to_be16(mac1), (__be16 *)addr);
 
        return is_valid_ether_addr(addr);
 }
@@ -326,22 +326,12 @@ void alx_set_macaddr(struct alx_hw *hw, const u8 *addr)
        u32 val;
 
        /* for example: 00-0B-6A-F6-00-DC * STAD0=6AF600DC, STAD1=000B */
-       val = be32_to_cpu(*(__be32 *)(addr + 2));
+       val = be32_to_cpu(get_unaligned((__be32 *)(addr + 2)));
        alx_write_mem32(hw, ALX_STAD0, val);
-       val = be16_to_cpu(*(__be16 *)addr);
+       val = be16_to_cpu(get_unaligned((__be16 *)addr));
        alx_write_mem32(hw, ALX_STAD1, val);
 }
 
-static void alx_enable_osc(struct alx_hw *hw)
-{
-       u32 val;
-
-       /* rising edge */
-       val = alx_read_mem32(hw, ALX_MISC);
-       alx_write_mem32(hw, ALX_MISC, val & ~ALX_MISC_INTNLOSC_OPEN);
-       alx_write_mem32(hw, ALX_MISC, val | ALX_MISC_INTNLOSC_OPEN);
-}
-
 static void alx_reset_osc(struct alx_hw *hw, u8 rev)
 {
        u32 val, val2;
@@ -624,12 +614,12 @@ void alx_start_mac(struct alx_hw *hw)
        alx_write_mem32(hw, ALX_TXQ0, txq | ALX_TXQ0_EN);
 
        mac = hw->rx_ctrl;
-       if (hw->link_speed % 10 == DUPLEX_FULL)
+       if (hw->duplex == DUPLEX_FULL)
                mac |= ALX_MAC_CTRL_FULLD;
        else
                mac &= ~ALX_MAC_CTRL_FULLD;
        ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED,
-                     hw->link_speed >= SPEED_1000 ? ALX_MAC_CTRL_SPEED_1000 :
+                     hw->link_speed == SPEED_1000 ? ALX_MAC_CTRL_SPEED_1000 :
                                                     ALX_MAC_CTRL_SPEED_10_100);
        mac |= ALX_MAC_CTRL_TX_EN | ALX_MAC_CTRL_RX_EN;
        hw->rx_ctrl = mac;
@@ -790,28 +780,22 @@ void alx_post_phy_link(struct alx_hw *hw)
        u16 phy_val, len, agc;
        u8 revid = alx_hw_revision(hw);
        bool adj_th = revid == ALX_REV_B0;
-       int speed;
-
-       if (hw->link_speed == SPEED_UNKNOWN)
-               speed = SPEED_UNKNOWN;
-       else
-               speed = hw->link_speed - hw->link_speed % 10;
 
        if (revid != ALX_REV_B0 && !alx_is_rev_a(revid))
                return;
 
        /* 1000BT/AZ, wrong cable length */
-       if (speed != SPEED_UNKNOWN) {
+       if (hw->link_speed != SPEED_UNKNOWN) {
                alx_read_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL6,
                                 &phy_val);
                len = ALX_GET_FIELD(phy_val, ALX_CLDCTRL6_CAB_LEN);
                alx_read_phy_dbg(hw, ALX_MIIDBG_AGC, &phy_val);
                agc = ALX_GET_FIELD(phy_val, ALX_AGC_2_VGA);
 
-               if ((speed == SPEED_1000 &&
+               if ((hw->link_speed == SPEED_1000 &&
                     (len > ALX_CLDCTRL6_CAB_LEN_SHORT1G ||
                      (len == 0 && agc > ALX_AGC_LONG1G_LIMT))) ||
-                   (speed == SPEED_100 &&
+                   (hw->link_speed == SPEED_100 &&
                     (len > ALX_CLDCTRL6_CAB_LEN_SHORT100M ||
                      (len == 0 && agc > ALX_AGC_LONG100M_LIMT)))) {
                        alx_write_phy_dbg(hw, ALX_MIIDBG_AZ_ANADECT,
@@ -831,10 +815,10 @@ void alx_post_phy_link(struct alx_hw *hw)
 
                /* threshold adjust */
                if (adj_th && hw->lnk_patch) {
-                       if (speed == SPEED_100) {
+                       if (hw->link_speed == SPEED_100) {
                                alx_write_phy_dbg(hw, ALX_MIIDBG_MSE16DB,
                                                  ALX_MSE16DB_UP);
-                       } else if (speed == SPEED_1000) {
+                       } else if (hw->link_speed == SPEED_1000) {
                                /*
                                 * Giga link threshold, raise the tolerance of
                                 * noise 50%
@@ -864,66 +848,6 @@ void alx_post_phy_link(struct alx_hw *hw)
        }
 }
 
-
-/* NOTE:
- *    1. phy link must be established before calling this function
- *    2. wol option (pattern,magic,link,etc.) is configed before call it.
- */
-int alx_pre_suspend(struct alx_hw *hw, int speed)
-{
-       u32 master, mac, phy, val;
-       int err = 0;
-
-       master = alx_read_mem32(hw, ALX_MASTER);
-       master &= ~ALX_MASTER_PCLKSEL_SRDS;
-       mac = hw->rx_ctrl;
-       /* 10/100 half */
-       ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED,  ALX_MAC_CTRL_SPEED_10_100);
-       mac &= ~(ALX_MAC_CTRL_FULLD | ALX_MAC_CTRL_RX_EN | ALX_MAC_CTRL_TX_EN);
-
-       phy = alx_read_mem32(hw, ALX_PHY_CTRL);
-       phy &= ~(ALX_PHY_CTRL_DSPRST_OUT | ALX_PHY_CTRL_CLS);
-       phy |= ALX_PHY_CTRL_RST_ANALOG | ALX_PHY_CTRL_HIB_PULSE |
-              ALX_PHY_CTRL_HIB_EN;
-
-       /* without any activity  */
-       if (!(hw->sleep_ctrl & ALX_SLEEP_ACTIVE)) {
-               err = alx_write_phy_reg(hw, ALX_MII_IER, 0);
-               if (err)
-                       return err;
-               phy |= ALX_PHY_CTRL_IDDQ | ALX_PHY_CTRL_POWER_DOWN;
-       } else {
-               if (hw->sleep_ctrl & (ALX_SLEEP_WOL_MAGIC | ALX_SLEEP_CIFS))
-                       mac |= ALX_MAC_CTRL_RX_EN | ALX_MAC_CTRL_BRD_EN;
-               if (hw->sleep_ctrl & ALX_SLEEP_CIFS)
-                       mac |= ALX_MAC_CTRL_TX_EN;
-               if (speed % 10 == DUPLEX_FULL)
-                       mac |= ALX_MAC_CTRL_FULLD;
-               if (speed >= SPEED_1000)
-                       ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED,
-                                     ALX_MAC_CTRL_SPEED_1000);
-               phy |= ALX_PHY_CTRL_DSPRST_OUT;
-               err = alx_write_phy_ext(hw, ALX_MIIEXT_ANEG,
-                                       ALX_MIIEXT_S3DIG10,
-                                       ALX_MIIEXT_S3DIG10_SL);
-               if (err)
-                       return err;
-       }
-
-       alx_enable_osc(hw);
-       hw->rx_ctrl = mac;
-       alx_write_mem32(hw, ALX_MASTER, master);
-       alx_write_mem32(hw, ALX_MAC_CTRL, mac);
-       alx_write_mem32(hw, ALX_PHY_CTRL, phy);
-
-       /* set val of PDLL D3PLLOFF */
-       val = alx_read_mem32(hw, ALX_PDLL_TRNS1);
-       val |= ALX_PDLL_TRNS1_D3PLLOFF_EN;
-       alx_write_mem32(hw, ALX_PDLL_TRNS1, val);
-
-       return 0;
-}
-
 bool alx_phy_configured(struct alx_hw *hw)
 {
        u32 cfg, hw_cfg;
@@ -938,7 +862,7 @@ bool alx_phy_configured(struct alx_hw *hw)
        return cfg == hw_cfg;
 }
 
-int alx_get_phy_link(struct alx_hw *hw, int *speed)
+int alx_read_phy_link(struct alx_hw *hw)
 {
        struct pci_dev *pdev = hw->pdev;
        u16 bmsr, giga;
@@ -953,7 +877,8 @@ int alx_get_phy_link(struct alx_hw *hw, int *speed)
                return err;
 
        if (!(bmsr & BMSR_LSTATUS)) {
-               *speed = SPEED_UNKNOWN;
+               hw->link_speed = SPEED_UNKNOWN;
+               hw->duplex = DUPLEX_UNKNOWN;
                return 0;
        }
 
@@ -967,20 +892,20 @@ int alx_get_phy_link(struct alx_hw *hw, int *speed)
 
        switch (giga & ALX_GIGA_PSSR_SPEED) {
        case ALX_GIGA_PSSR_1000MBS:
-               *speed = SPEED_1000;
+               hw->link_speed = SPEED_1000;
                break;
        case ALX_GIGA_PSSR_100MBS:
-               *speed = SPEED_100;
+               hw->link_speed = SPEED_100;
                break;
        case ALX_GIGA_PSSR_10MBS:
-               *speed = SPEED_10;
+               hw->link_speed = SPEED_10;
                break;
        default:
                goto wrong_speed;
        }
 
-       *speed += (giga & ALX_GIGA_PSSR_DPLX) ? DUPLEX_FULL : DUPLEX_HALF;
-       return 1;
+       hw->duplex = (giga & ALX_GIGA_PSSR_DPLX) ? DUPLEX_FULL : DUPLEX_HALF;
+       return 0;
 
 wrong_speed:
        dev_err(&pdev->dev, "invalid PHY speed/duplex: 0x%x\n", giga);
@@ -995,26 +920,6 @@ int alx_clear_phy_intr(struct alx_hw *hw)
        return alx_read_phy_reg(hw, ALX_MII_ISR, &isr);
 }
 
-int alx_config_wol(struct alx_hw *hw)
-{
-       u32 wol = 0;
-       int err = 0;
-
-       /* turn on magic packet event */
-       if (hw->sleep_ctrl & ALX_SLEEP_WOL_MAGIC)
-               wol |= ALX_WOL0_MAGIC_EN | ALX_WOL0_PME_MAGIC_EN;
-
-       /* turn on link up event */
-       if (hw->sleep_ctrl & ALX_SLEEP_WOL_PHY) {
-               wol |=  ALX_WOL0_LINK_EN | ALX_WOL0_PME_LINK;
-               /* only link up can wake up */
-               err = alx_write_phy_reg(hw, ALX_MII_IER, ALX_IER_LINK_UP);
-       }
-       alx_write_mem32(hw, ALX_WOL0, wol);
-
-       return err;
-}
-
 void alx_disable_rss(struct alx_hw *hw)
 {
        u32 ctrl = alx_read_mem32(hw, ALX_RXQ0);
@@ -1126,85 +1031,6 @@ void alx_configure_basic(struct alx_hw *hw)
        alx_write_mem32(hw, ALX_WRR, val);
 }
 
-static inline u32 alx_speed_to_ethadv(int speed)
-{
-       switch (speed) {
-       case SPEED_1000 + DUPLEX_FULL:
-               return ADVERTISED_1000baseT_Full;
-       case SPEED_100 + DUPLEX_FULL:
-               return ADVERTISED_100baseT_Full;
-       case SPEED_100 + DUPLEX_HALF:
-               return ADVERTISED_10baseT_Half;
-       case SPEED_10 + DUPLEX_FULL:
-               return ADVERTISED_10baseT_Full;
-       case SPEED_10 + DUPLEX_HALF:
-               return ADVERTISED_10baseT_Half;
-       default:
-               return 0;
-       }
-}
-
-int alx_select_powersaving_speed(struct alx_hw *hw, int *speed)
-{
-       int i, err, spd;
-       u16 lpa;
-
-       err = alx_get_phy_link(hw, &spd);
-       if (err < 0)
-               return err;
-
-       if (spd == SPEED_UNKNOWN)
-               return 0;
-
-       err = alx_read_phy_reg(hw, MII_LPA, &lpa);
-       if (err)
-               return err;
-
-       if (!(lpa & LPA_LPACK)) {
-               *speed = spd;
-               return 0;
-       }
-
-       if (lpa & LPA_10FULL)
-               *speed = SPEED_10 + DUPLEX_FULL;
-       else if (lpa & LPA_10HALF)
-               *speed = SPEED_10 + DUPLEX_HALF;
-       else if (lpa & LPA_100FULL)
-               *speed = SPEED_100 + DUPLEX_FULL;
-       else
-               *speed = SPEED_100 + DUPLEX_HALF;
-
-       if (*speed != spd) {
-               err = alx_write_phy_reg(hw, ALX_MII_IER, 0);
-               if (err)
-                       return err;
-               err = alx_setup_speed_duplex(hw,
-                                            alx_speed_to_ethadv(*speed) |
-                                            ADVERTISED_Autoneg,
-                                            ALX_FC_ANEG | ALX_FC_RX |
-                                            ALX_FC_TX);
-               if (err)
-                       return err;
-
-               /* wait for linkup */
-               for (i = 0; i < ALX_MAX_SETUP_LNK_CYCLE; i++) {
-                       int speed2;
-
-                       msleep(100);
-
-                       err = alx_get_phy_link(hw, &speed2);
-                       if (err < 0)
-                               return err;
-                       if (speed2 != SPEED_UNKNOWN)
-                               break;
-               }
-               if (i == ALX_MAX_SETUP_LNK_CYCLE)
-                       return -ETIMEDOUT;
-       }
-
-       return 0;
-}
-
 bool alx_get_phy_info(struct alx_hw *hw)
 {
        u16  devs1, devs2;
index 65e723d..96f3b43 100644 (file)
@@ -412,12 +412,11 @@ struct alx_hw {
        u32 smb_timer;
        /* SPEED_* + DUPLEX_*, SPEED_UNKNOWN if link is down */
        int link_speed;
+       u8 duplex;
 
        /* auto-neg advertisement or force mode config */
-       u32 adv_cfg;
        u8 flowctrl;
-
-       u32 sleep_ctrl;
+       u32 adv_cfg;
 
        spinlock_t mdio_lock;
        struct mdio_if_info mdio;
@@ -478,14 +477,12 @@ void alx_reset_pcie(struct alx_hw *hw);
 void alx_enable_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en);
 int alx_setup_speed_duplex(struct alx_hw *hw, u32 ethadv, u8 flowctrl);
 void alx_post_phy_link(struct alx_hw *hw);
-int alx_pre_suspend(struct alx_hw *hw, int speed);
 int alx_read_phy_reg(struct alx_hw *hw, u16 reg, u16 *phy_data);
 int alx_write_phy_reg(struct alx_hw *hw, u16 reg, u16 phy_data);
 int alx_read_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 *pdata);
 int alx_write_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 data);
-int alx_get_phy_link(struct alx_hw *hw, int *speed);
+int alx_read_phy_link(struct alx_hw *hw);
 int alx_clear_phy_intr(struct alx_hw *hw);
-int alx_config_wol(struct alx_hw *hw);
 void alx_cfg_mac_flowcontrol(struct alx_hw *hw, u8 fc);
 void alx_start_mac(struct alx_hw *hw);
 int alx_reset_mac(struct alx_hw *hw);
@@ -493,7 +490,21 @@ void alx_set_macaddr(struct alx_hw *hw, const u8 *addr);
 bool alx_phy_configured(struct alx_hw *hw);
 void alx_configure_basic(struct alx_hw *hw);
 void alx_disable_rss(struct alx_hw *hw);
-int alx_select_powersaving_speed(struct alx_hw *hw, int *speed);
 bool alx_get_phy_info(struct alx_hw *hw);
 
+static inline u32 alx_speed_to_ethadv(int speed, u8 duplex)
+{
+       if (speed == SPEED_1000 && duplex == DUPLEX_FULL)
+               return ADVERTISED_1000baseT_Full;
+       if (speed == SPEED_100 && duplex == DUPLEX_FULL)
+               return ADVERTISED_100baseT_Full;
+       if (speed == SPEED_100 && duplex== DUPLEX_HALF)
+               return ADVERTISED_100baseT_Half;
+       if (speed == SPEED_10 && duplex == DUPLEX_FULL)
+               return ADVERTISED_10baseT_Full;
+       if (speed == SPEED_10 && duplex == DUPLEX_HALF)
+               return ADVERTISED_10baseT_Half;
+       return 0;
+}
+
 #endif
index 418de8b..0e0b242 100644 (file)
@@ -706,12 +706,12 @@ static int alx_init_sw(struct alx_priv *alx)
        alx->rxbuf_size = ALIGN(ALX_RAW_MTU(hw->mtu), 8);
        alx->tx_ringsz = 256;
        alx->rx_ringsz = 512;
-       hw->sleep_ctrl = ALX_SLEEP_WOL_MAGIC | ALX_SLEEP_WOL_PHY;
        hw->imt = 200;
        alx->int_mask = ALX_ISR_MISC;
        hw->dma_chnl = hw->max_dma_chnl;
        hw->ith_tpd = alx->tx_ringsz / 3;
        hw->link_speed = SPEED_UNKNOWN;
+       hw->duplex = DUPLEX_UNKNOWN;
        hw->adv_cfg = ADVERTISED_Autoneg |
                      ADVERTISED_10baseT_Half |
                      ADVERTISED_10baseT_Full |
@@ -758,6 +758,7 @@ static void alx_halt(struct alx_priv *alx)
 
        alx_netif_stop(alx);
        hw->link_speed = SPEED_UNKNOWN;
+       hw->duplex = DUPLEX_UNKNOWN;
 
        alx_reset_mac(hw);
 
@@ -869,18 +870,18 @@ static void __alx_stop(struct alx_priv *alx)
        alx_free_rings(alx);
 }
 
-static const char *alx_speed_desc(u16 speed)
+static const char *alx_speed_desc(struct alx_hw *hw)
 {
-       switch (speed) {
-       case SPEED_1000 + DUPLEX_FULL:
+       switch (alx_speed_to_ethadv(hw->link_speed, hw->duplex)) {
+       case ADVERTISED_1000baseT_Full:
                return "1 Gbps Full";
-       case SPEED_100 + DUPLEX_FULL:
+       case ADVERTISED_100baseT_Full:
                return "100 Mbps Full";
-       case SPEED_100 + DUPLEX_HALF:
+       case ADVERTISED_100baseT_Half:
                return "100 Mbps Half";
-       case SPEED_10 + DUPLEX_FULL:
+       case ADVERTISED_10baseT_Full:
                return "10 Mbps Full";
-       case SPEED_10 + DUPLEX_HALF:
+       case ADVERTISED_10baseT_Half:
                return "10 Mbps Half";
        default:
                return "Unknown speed";
@@ -891,7 +892,8 @@ static void alx_check_link(struct alx_priv *alx)
 {
        struct alx_hw *hw = &alx->hw;
        unsigned long flags;
-       int speed, old_speed;
+       int old_speed;
+       u8 old_duplex;
        int err;
 
        /* clear PHY internal interrupt status, otherwise the main
@@ -899,7 +901,9 @@ static void alx_check_link(struct alx_priv *alx)
         */
        alx_clear_phy_intr(hw);
 
-       err = alx_get_phy_link(hw, &speed);
+       old_speed = hw->link_speed;
+       old_duplex = hw->duplex;
+       err = alx_read_phy_link(hw);
        if (err < 0)
                goto reset;
 
@@ -908,15 +912,12 @@ static void alx_check_link(struct alx_priv *alx)
        alx_write_mem32(hw, ALX_IMR, alx->int_mask);
        spin_unlock_irqrestore(&alx->irq_lock, flags);
 
-       old_speed = hw->link_speed;
-
-       if (old_speed == speed)
+       if (old_speed == hw->link_speed)
                return;
-       hw->link_speed = speed;
 
-       if (speed != SPEED_UNKNOWN) {
+       if (hw->link_speed != SPEED_UNKNOWN) {
                netif_info(alx, link, alx->dev,
-                          "NIC Up: %s\n", alx_speed_desc(speed));
+                          "NIC Up: %s\n", alx_speed_desc(hw));
                alx_post_phy_link(hw);
                alx_enable_aspm(hw, true, true);
                alx_start_mac(hw);
@@ -959,65 +960,6 @@ static int alx_stop(struct net_device *netdev)
        return 0;
 }
 
-static int __alx_shutdown(struct pci_dev *pdev, bool *wol_en)
-{
-       struct alx_priv *alx = pci_get_drvdata(pdev);
-       struct net_device *netdev = alx->dev;
-       struct alx_hw *hw = &alx->hw;
-       int err, speed;
-
-       netif_device_detach(netdev);
-
-       if (netif_running(netdev))
-               __alx_stop(alx);
-
-#ifdef CONFIG_PM_SLEEP
-       err = pci_save_state(pdev);
-       if (err)
-               return err;
-#endif
-
-       err = alx_select_powersaving_speed(hw, &speed);
-       if (err)
-               return err;
-       err = alx_clear_phy_intr(hw);
-       if (err)
-               return err;
-       err = alx_pre_suspend(hw, speed);
-       if (err)
-               return err;
-       err = alx_config_wol(hw);
-       if (err)
-               return err;
-
-       *wol_en = false;
-       if (hw->sleep_ctrl & ALX_SLEEP_ACTIVE) {
-               netif_info(alx, wol, netdev,
-                          "wol: ctrl=%X, speed=%X\n",
-                          hw->sleep_ctrl, speed);
-               device_set_wakeup_enable(&pdev->dev, true);
-               *wol_en = true;
-       }
-
-       pci_disable_device(pdev);
-
-       return 0;
-}
-
-static void alx_shutdown(struct pci_dev *pdev)
-{
-       int err;
-       bool wol_en;
-
-       err = __alx_shutdown(pdev, &wol_en);
-       if (!err) {
-               pci_wake_from_d3(pdev, wol_en);
-               pci_set_power_state(pdev, PCI_D3hot);
-       } else {
-               dev_err(&pdev->dev, "shutdown fail %d\n", err);
-       }
-}
-
 static void alx_link_check(struct work_struct *work)
 {
        struct alx_priv *alx;
@@ -1396,8 +1338,6 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto out_unmap;
        }
 
-       device_set_wakeup_enable(&pdev->dev, hw->sleep_ctrl);
-
        netdev_info(netdev,
                    "Qualcomm Atheros AR816x/AR817x Ethernet [%pM]\n",
                    netdev->dev_addr);
@@ -1442,22 +1382,12 @@ static void alx_remove(struct pci_dev *pdev)
 static int alx_suspend(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
-       int err;
-       bool wol_en;
-
-       err = __alx_shutdown(pdev, &wol_en);
-       if (err) {
-               dev_err(&pdev->dev, "shutdown fail in suspend %d\n", err);
-               return err;
-       }
-
-       if (wol_en) {
-               pci_prepare_to_sleep(pdev);
-       } else {
-               pci_wake_from_d3(pdev, false);
-               pci_set_power_state(pdev, PCI_D3hot);
-       }
+       struct alx_priv *alx = pci_get_drvdata(pdev);
 
+       if (!netif_running(alx->dev))
+               return 0;
+       netif_device_detach(alx->dev);
+       __alx_stop(alx);
        return 0;
 }
 
@@ -1465,49 +1395,20 @@ static int alx_resume(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        struct alx_priv *alx = pci_get_drvdata(pdev);
-       struct net_device *netdev = alx->dev;
-       struct alx_hw *hw = &alx->hw;
-       int err;
-
-       pci_set_power_state(pdev, PCI_D0);
-       pci_restore_state(pdev);
-       pci_save_state(pdev);
-
-       pci_enable_wake(pdev, PCI_D3hot, 0);
-       pci_enable_wake(pdev, PCI_D3cold, 0);
 
-       hw->link_speed = SPEED_UNKNOWN;
-       alx->int_mask = ALX_ISR_MISC;
-
-       alx_reset_pcie(hw);
-       alx_reset_phy(hw);
-
-       err = alx_reset_mac(hw);
-       if (err) {
-               netif_err(alx, hw, alx->dev,
-                         "resume:reset_mac fail %d\n", err);
-               return -EIO;
-       }
-
-       err = alx_setup_speed_duplex(hw, hw->adv_cfg, hw->flowctrl);
-       if (err) {
-               netif_err(alx, hw, alx->dev,
-                         "resume:setup_speed_duplex fail %d\n", err);
-               return -EIO;
-       }
-
-       if (netif_running(netdev)) {
-               err = __alx_open(alx, true);
-               if (err)
-                       return err;
-       }
-
-       netif_device_attach(netdev);
-
-       return err;
+       if (!netif_running(alx->dev))
+               return 0;
+       netif_device_attach(alx->dev);
+       return __alx_open(alx, true);
 }
+
+static SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume);
+#define ALX_PM_OPS      (&alx_pm_ops)
+#else
+#define ALX_PM_OPS      NULL
 #endif
 
+
 static pci_ers_result_t alx_pci_error_detected(struct pci_dev *pdev,
                                               pci_channel_state_t state)
 {
@@ -1550,8 +1451,6 @@ static pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev)
        }
 
        pci_set_master(pdev);
-       pci_enable_wake(pdev, PCI_D3hot, 0);
-       pci_enable_wake(pdev, PCI_D3cold, 0);
 
        alx_reset_pcie(hw);
        if (!alx_reset_mac(hw))
@@ -1587,13 +1486,6 @@ static const struct pci_error_handlers alx_err_handlers = {
        .resume         = alx_pci_error_resume,
 };
 
-#ifdef CONFIG_PM_SLEEP
-static SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume);
-#define ALX_PM_OPS      (&alx_pm_ops)
-#else
-#define ALX_PM_OPS      NULL
-#endif
-
 static DEFINE_PCI_DEVICE_TABLE(alx_pci_tbl) = {
        { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8161),
          .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
@@ -1611,7 +1503,6 @@ static struct pci_driver alx_driver = {
        .id_table    = alx_pci_tbl,
        .probe       = alx_probe,
        .remove      = alx_remove,
-       .shutdown    = alx_shutdown,
        .err_handler = &alx_err_handlers,
        .driver.pm   = ALX_PM_OPS,
 };
index 0ba9007..786a874 100644 (file)
@@ -2755,27 +2755,4 @@ static struct pci_driver atl1c_driver = {
        .driver.pm = &atl1c_pm_ops,
 };
 
-/**
- * atl1c_init_module - Driver Registration Routine
- *
- * atl1c_init_module is the first routine called when the driver is
- * loaded. All it does is register with the PCI subsystem.
- */
-static int __init atl1c_init_module(void)
-{
-       return pci_register_driver(&atl1c_driver);
-}
-
-/**
- * atl1c_exit_module - Driver Exit Cleanup Routine
- *
- * atl1c_exit_module is called just before the driver is removed
- * from memory.
- */
-static void __exit atl1c_exit_module(void)
-{
-       pci_unregister_driver(&atl1c_driver);
-}
-
-module_init(atl1c_init_module);
-module_exit(atl1c_exit_module);
+module_pci_driver(atl1c_driver);
index 0688bb8..895f537 100644 (file)
@@ -2489,27 +2489,4 @@ static struct pci_driver atl1e_driver = {
        .err_handler = &atl1e_err_handler
 };
 
-/**
- * atl1e_init_module - Driver Registration Routine
- *
- * atl1e_init_module is the first routine called when the driver is
- * loaded. All it does is register with the PCI subsystem.
- */
-static int __init atl1e_init_module(void)
-{
-       return pci_register_driver(&atl1e_driver);
-}
-
-/**
- * atl1e_exit_module - Driver Exit Cleanup Routine
- *
- * atl1e_exit_module is called just before the driver is removed
- * from memory.
- */
-static void __exit atl1e_exit_module(void)
-{
-       pci_unregister_driver(&atl1e_driver);
-}
-
-module_init(atl1e_init_module);
-module_exit(atl1e_exit_module);
+module_pci_driver(atl1e_driver);
index fa0915f..538211d 100644 (file)
@@ -3145,31 +3145,6 @@ static struct pci_driver atl1_driver = {
        .driver.pm = &atl1_pm_ops,
 };
 
-/**
- * atl1_exit_module - Driver Exit Cleanup Routine
- *
- * atl1_exit_module is called just before the driver is removed
- * from memory.
- */
-static void __exit atl1_exit_module(void)
-{
-       pci_unregister_driver(&atl1_driver);
-}
-
-/**
- * atl1_init_module - Driver Registration Routine
- *
- * atl1_init_module is the first routine called when the driver is
- * loaded. All it does is register with the PCI subsystem.
- */
-static int __init atl1_init_module(void)
-{
-       return pci_register_driver(&atl1_driver);
-}
-
-module_init(atl1_init_module);
-module_exit(atl1_exit_module);
-
 struct atl1_stats {
        char stat_string[ETH_GSTRING_LEN];
        int sizeof_stat;
@@ -3705,3 +3680,5 @@ static const struct ethtool_ops atl1_ethtool_ops = {
        .get_ethtool_stats      = atl1_get_ethtool_stats,
        .get_sset_count         = atl1_get_sset_count,
 };
+
+module_pci_driver(atl1_driver);
index 3e69b3f..1d680ba 100644 (file)
@@ -22,7 +22,6 @@ config B44
        tristate "Broadcom 440x/47xx ethernet support"
        depends on SSB_POSSIBLE && HAS_DMA
        select SSB
-       select NET_CORE
        select MII
        ---help---
          If you have a network (Ethernet) controller of this type, say Y
@@ -54,7 +53,6 @@ config B44_PCI
 config BCM63XX_ENET
        tristate "Broadcom 63xx internal mac support"
        depends on BCM63XX
-       select NET_CORE
        select MII
        select PHYLIB
        help
index 0b3e23e..b1bcd4b 100644 (file)
@@ -41,8 +41,8 @@ static int copybreak __read_mostly = 128;
 module_param(copybreak, int, 0);
 MODULE_PARM_DESC(copybreak, "Receive copy threshold");
 
-/* io memory shared between all devices */
-static void __iomem *bcm_enet_shared_base;
+/* io registers memory shared between all devices */
+static void __iomem *bcm_enet_shared_base[3];
 
 /*
  * io helpers to access mac registers
@@ -59,17 +59,76 @@ static inline void enet_writel(struct bcm_enet_priv *priv,
 }
 
 /*
- * io helpers to access shared registers
+ * io helpers to access switch registers
  */
+static inline u32 enetsw_readl(struct bcm_enet_priv *priv, u32 off)
+{
+       return bcm_readl(priv->base + off);
+}
+
+static inline void enetsw_writel(struct bcm_enet_priv *priv,
+                                u32 val, u32 off)
+{
+       bcm_writel(val, priv->base + off);
+}
+
+static inline u16 enetsw_readw(struct bcm_enet_priv *priv, u32 off)
+{
+       return bcm_readw(priv->base + off);
+}
+
+static inline void enetsw_writew(struct bcm_enet_priv *priv,
+                                u16 val, u32 off)
+{
+       bcm_writew(val, priv->base + off);
+}
+
+static inline u8 enetsw_readb(struct bcm_enet_priv *priv, u32 off)
+{
+       return bcm_readb(priv->base + off);
+}
+
+static inline void enetsw_writeb(struct bcm_enet_priv *priv,
+                                u8 val, u32 off)
+{
+       bcm_writeb(val, priv->base + off);
+}
+
+
+/* io helpers to access shared registers */
 static inline u32 enet_dma_readl(struct bcm_enet_priv *priv, u32 off)
 {
-       return bcm_readl(bcm_enet_shared_base + off);
+       return bcm_readl(bcm_enet_shared_base[0] + off);
 }
 
 static inline void enet_dma_writel(struct bcm_enet_priv *priv,
                                       u32 val, u32 off)
 {
-       bcm_writel(val, bcm_enet_shared_base + off);
+       bcm_writel(val, bcm_enet_shared_base[0] + off);
+}
+
+static inline u32 enet_dmac_readl(struct bcm_enet_priv *priv, u32 off, int chan)
+{
+       return bcm_readl(bcm_enet_shared_base[1] +
+               bcm63xx_enetdmacreg(off) + chan * priv->dma_chan_width);
+}
+
+static inline void enet_dmac_writel(struct bcm_enet_priv *priv,
+                                      u32 val, u32 off, int chan)
+{
+       bcm_writel(val, bcm_enet_shared_base[1] +
+               bcm63xx_enetdmacreg(off) + chan * priv->dma_chan_width);
+}
+
+static inline u32 enet_dmas_readl(struct bcm_enet_priv *priv, u32 off, int chan)
+{
+       return bcm_readl(bcm_enet_shared_base[2] + off + chan * priv->dma_chan_width);
+}
+
+static inline void enet_dmas_writel(struct bcm_enet_priv *priv,
+                                      u32 val, u32 off, int chan)
+{
+       bcm_writel(val, bcm_enet_shared_base[2] + off + chan * priv->dma_chan_width);
 }
 
 /*
@@ -196,7 +255,6 @@ static int bcm_enet_refill_rx(struct net_device *dev)
                        if (!skb)
                                break;
                        priv->rx_skb[desc_idx] = skb;
-
                        p = dma_map_single(&priv->pdev->dev, skb->data,
                                           priv->rx_skb_size,
                                           DMA_FROM_DEVICE);
@@ -206,7 +264,7 @@ static int bcm_enet_refill_rx(struct net_device *dev)
                len_stat = priv->rx_skb_size << DMADESC_LENGTH_SHIFT;
                len_stat |= DMADESC_OWNER_MASK;
                if (priv->rx_dirty_desc == priv->rx_ring_size - 1) {
-                       len_stat |= DMADESC_WRAP_MASK;
+                       len_stat |= (DMADESC_WRAP_MASK >> priv->dma_desc_shift);
                        priv->rx_dirty_desc = 0;
                } else {
                        priv->rx_dirty_desc++;
@@ -217,7 +275,10 @@ static int bcm_enet_refill_rx(struct net_device *dev)
                priv->rx_desc_count++;
 
                /* tell dma engine we allocated one buffer */
-               enet_dma_writel(priv, 1, ENETDMA_BUFALLOC_REG(priv->rx_chan));
+               if (priv->dma_has_sram)
+                       enet_dma_writel(priv, 1, ENETDMA_BUFALLOC_REG(priv->rx_chan));
+               else
+                       enet_dmac_writel(priv, 1, ENETDMAC_BUFALLOC, priv->rx_chan);
        }
 
        /* If rx ring is still empty, set a timer to try allocating
@@ -293,13 +354,15 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget)
 
                /* if the packet does not have start of packet _and_
                 * end of packet flag set, then just recycle it */
-               if ((len_stat & DMADESC_ESOP_MASK) != DMADESC_ESOP_MASK) {
+               if ((len_stat & (DMADESC_ESOP_MASK >> priv->dma_desc_shift)) !=
+                       (DMADESC_ESOP_MASK >> priv->dma_desc_shift)) {
                        dev->stats.rx_dropped++;
                        continue;
                }
 
                /* recycle packet if it's marked as bad */
-               if (unlikely(len_stat & DMADESC_ERR_MASK)) {
+               if (!priv->enet_is_sw &&
+                   unlikely(len_stat & DMADESC_ERR_MASK)) {
                        dev->stats.rx_errors++;
 
                        if (len_stat & DMADESC_OVSIZE_MASK)
@@ -353,8 +416,8 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget)
                bcm_enet_refill_rx(dev);
 
                /* kick rx dma */
-               enet_dma_writel(priv, ENETDMA_CHANCFG_EN_MASK,
-                               ENETDMA_CHANCFG_REG(priv->rx_chan));
+               enet_dmac_writel(priv, priv->dma_chan_en_mask,
+                                        ENETDMAC_CHANCFG, priv->rx_chan);
        }
 
        return processed;
@@ -429,10 +492,10 @@ static int bcm_enet_poll(struct napi_struct *napi, int budget)
        dev = priv->net_dev;
 
        /* ack interrupts */
-       enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK,
-                       ENETDMA_IR_REG(priv->rx_chan));
-       enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK,
-                       ENETDMA_IR_REG(priv->tx_chan));
+       enet_dmac_writel(priv, priv->dma_chan_int_mask,
+                        ENETDMAC_IR, priv->rx_chan);
+       enet_dmac_writel(priv, priv->dma_chan_int_mask,
+                        ENETDMAC_IR, priv->tx_chan);
 
        /* reclaim sent skb */
        tx_work_done = bcm_enet_tx_reclaim(dev, 0);
@@ -451,10 +514,10 @@ static int bcm_enet_poll(struct napi_struct *napi, int budget)
        napi_complete(napi);
 
        /* restore rx/tx interrupt */
-       enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK,
-                       ENETDMA_IRMASK_REG(priv->rx_chan));
-       enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK,
-                       ENETDMA_IRMASK_REG(priv->tx_chan));
+       enet_dmac_writel(priv, priv->dma_chan_int_mask,
+                        ENETDMAC_IRMASK, priv->rx_chan);
+       enet_dmac_writel(priv, priv->dma_chan_int_mask,
+                        ENETDMAC_IRMASK, priv->tx_chan);
 
        return rx_work_done;
 }
@@ -497,8 +560,8 @@ static irqreturn_t bcm_enet_isr_dma(int irq, void *dev_id)
        priv = netdev_priv(dev);
 
        /* mask rx/tx interrupts */
-       enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->rx_chan));
-       enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->tx_chan));
+       enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan);
+       enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan);
 
        napi_schedule(&priv->napi);
 
@@ -530,6 +593,26 @@ static int bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
                goto out_unlock;
        }
 
+       /* pad small packets sent on a switch device */
+       if (priv->enet_is_sw && skb->len < 64) {
+               int needed = 64 - skb->len;
+               char *data;
+
+               if (unlikely(skb_tailroom(skb) < needed)) {
+                       struct sk_buff *nskb;
+
+                       nskb = skb_copy_expand(skb, 0, needed, GFP_ATOMIC);
+                       if (!nskb) {
+                               ret = NETDEV_TX_BUSY;
+                               goto out_unlock;
+                       }
+                       dev_kfree_skb(skb);
+                       skb = nskb;
+               }
+               data = skb_put(skb, needed);
+               memset(data, 0, needed);
+       }
+
        /* point to the next available desc */
        desc = &priv->tx_desc_cpu[priv->tx_curr_desc];
        priv->tx_skb[priv->tx_curr_desc] = skb;
@@ -539,14 +622,14 @@ static int bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
                                       DMA_TO_DEVICE);
 
        len_stat = (skb->len << DMADESC_LENGTH_SHIFT) & DMADESC_LENGTH_MASK;
-       len_stat |= DMADESC_ESOP_MASK |
+       len_stat |= (DMADESC_ESOP_MASK >> priv->dma_desc_shift) |
                DMADESC_APPEND_CRC |
                DMADESC_OWNER_MASK;
 
        priv->tx_curr_desc++;
        if (priv->tx_curr_desc == priv->tx_ring_size) {
                priv->tx_curr_desc = 0;
-               len_stat |= DMADESC_WRAP_MASK;
+               len_stat |= (DMADESC_WRAP_MASK >> priv->dma_desc_shift);
        }
        priv->tx_desc_count--;
 
@@ -557,8 +640,8 @@ static int bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        wmb();
 
        /* kick tx dma */
-       enet_dma_writel(priv, ENETDMA_CHANCFG_EN_MASK,
-                       ENETDMA_CHANCFG_REG(priv->tx_chan));
+       enet_dmac_writel(priv, priv->dma_chan_en_mask,
+                                ENETDMAC_CHANCFG, priv->tx_chan);
 
        /* stop queue if no more desc available */
        if (!priv->tx_desc_count)
@@ -686,6 +769,9 @@ static void bcm_enet_set_flow(struct bcm_enet_priv *priv, int rx_en, int tx_en)
                val &= ~ENET_RXCFG_ENFLOW_MASK;
        enet_writel(priv, val, ENET_RXCFG_REG);
 
+       if (!priv->dma_has_sram)
+               return;
+
        /* tx flow control (pause frame generation) */
        val = enet_dma_readl(priv, ENETDMA_CFG_REG);
        if (tx_en)
@@ -833,8 +919,8 @@ static int bcm_enet_open(struct net_device *dev)
 
        /* mask all interrupts and request them */
        enet_writel(priv, 0, ENET_IRMASK_REG);
-       enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->rx_chan));
-       enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->tx_chan));
+       enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan);
+       enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan);
 
        ret = request_irq(dev->irq, bcm_enet_isr_mac, 0, dev->name, dev);
        if (ret)
@@ -909,8 +995,12 @@ static int bcm_enet_open(struct net_device *dev)
        priv->rx_curr_desc = 0;
 
        /* initialize flow control buffer allocation */
-       enet_dma_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0,
-                       ENETDMA_BUFALLOC_REG(priv->rx_chan));
+       if (priv->dma_has_sram)
+               enet_dma_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0,
+                               ENETDMA_BUFALLOC_REG(priv->rx_chan));
+       else
+               enet_dmac_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0,
+                               ENETDMAC_BUFALLOC, priv->rx_chan);
 
        if (bcm_enet_refill_rx(dev)) {
                dev_err(kdev, "cannot allocate rx skb queue\n");
@@ -919,37 +1009,55 @@ static int bcm_enet_open(struct net_device *dev)
        }
 
        /* write rx & tx ring addresses */
-       enet_dma_writel(priv, priv->rx_desc_dma,
-                       ENETDMA_RSTART_REG(priv->rx_chan));
-       enet_dma_writel(priv, priv->tx_desc_dma,
-                       ENETDMA_RSTART_REG(priv->tx_chan));
+       if (priv->dma_has_sram) {
+               enet_dmas_writel(priv, priv->rx_desc_dma,
+                                ENETDMAS_RSTART_REG, priv->rx_chan);
+               enet_dmas_writel(priv, priv->tx_desc_dma,
+                        ENETDMAS_RSTART_REG, priv->tx_chan);
+       } else {
+               enet_dmac_writel(priv, priv->rx_desc_dma,
+                               ENETDMAC_RSTART, priv->rx_chan);
+               enet_dmac_writel(priv, priv->tx_desc_dma,
+                               ENETDMAC_RSTART, priv->tx_chan);
+       }
 
        /* clear remaining state ram for rx & tx channel */
-       enet_dma_writel(priv, 0, ENETDMA_SRAM2_REG(priv->rx_chan));
-       enet_dma_writel(priv, 0, ENETDMA_SRAM2_REG(priv->tx_chan));
-       enet_dma_writel(priv, 0, ENETDMA_SRAM3_REG(priv->rx_chan));
-       enet_dma_writel(priv, 0, ENETDMA_SRAM3_REG(priv->tx_chan));
-       enet_dma_writel(priv, 0, ENETDMA_SRAM4_REG(priv->rx_chan));
-       enet_dma_writel(priv, 0, ENETDMA_SRAM4_REG(priv->tx_chan));
+       if (priv->dma_has_sram) {
+               enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->rx_chan);
+               enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->tx_chan);
+               enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->rx_chan);
+               enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->tx_chan);
+               enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->rx_chan);
+               enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->tx_chan);
+       } else {
+               enet_dmac_writel(priv, 0, ENETDMAC_FC, priv->rx_chan);
+               enet_dmac_writel(priv, 0, ENETDMAC_FC, priv->tx_chan);
+       }
 
        /* set max rx/tx length */
        enet_writel(priv, priv->hw_mtu, ENET_RXMAXLEN_REG);
        enet_writel(priv, priv->hw_mtu, ENET_TXMAXLEN_REG);
 
        /* set dma maximum burst len */
-       enet_dma_writel(priv, BCMENET_DMA_MAXBURST,
-                       ENETDMA_MAXBURST_REG(priv->rx_chan));
-       enet_dma_writel(priv, BCMENET_DMA_MAXBURST,
-                       ENETDMA_MAXBURST_REG(priv->tx_chan));
+       enet_dmac_writel(priv, priv->dma_maxburst,
+                        ENETDMAC_MAXBURST, priv->rx_chan);
+       enet_dmac_writel(priv, priv->dma_maxburst,
+                        ENETDMAC_MAXBURST, priv->tx_chan);
 
        /* set correct transmit fifo watermark */
        enet_writel(priv, BCMENET_TX_FIFO_TRESH, ENET_TXWMARK_REG);
 
        /* set flow control low/high threshold to 1/3 / 2/3 */
-       val = priv->rx_ring_size / 3;
-       enet_dma_writel(priv, val, ENETDMA_FLOWCL_REG(priv->rx_chan));
-       val = (priv->rx_ring_size * 2) / 3;
-       enet_dma_writel(priv, val, ENETDMA_FLOWCH_REG(priv->rx_chan));
+       if (priv->dma_has_sram) {
+               val = priv->rx_ring_size / 3;
+               enet_dma_writel(priv, val, ENETDMA_FLOWCL_REG(priv->rx_chan));
+               val = (priv->rx_ring_size * 2) / 3;
+               enet_dma_writel(priv, val, ENETDMA_FLOWCH_REG(priv->rx_chan));
+       } else {
+               enet_dmac_writel(priv, 5, ENETDMAC_FC, priv->rx_chan);
+               enet_dmac_writel(priv, priv->rx_ring_size, ENETDMAC_LEN, priv->rx_chan);
+               enet_dmac_writel(priv, priv->tx_ring_size, ENETDMAC_LEN, priv->tx_chan);
+       }
 
        /* all set, enable mac and interrupts, start dma engine and
         * kick rx dma channel */
@@ -958,26 +1066,26 @@ static int bcm_enet_open(struct net_device *dev)
        val |= ENET_CTL_ENABLE_MASK;
        enet_writel(priv, val, ENET_CTL_REG);
        enet_dma_writel(priv, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG);
-       enet_dma_writel(priv, ENETDMA_CHANCFG_EN_MASK,
-                       ENETDMA_CHANCFG_REG(priv->rx_chan));
+       enet_dmac_writel(priv, priv->dma_chan_en_mask,
+                        ENETDMAC_CHANCFG, priv->rx_chan);
 
        /* watch "mib counters about to overflow" interrupt */
        enet_writel(priv, ENET_IR_MIB, ENET_IR_REG);
        enet_writel(priv, ENET_IR_MIB, ENET_IRMASK_REG);
 
        /* watch "packet transferred" interrupt in rx and tx */
-       enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK,
-                       ENETDMA_IR_REG(priv->rx_chan));
-       enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK,
-                       ENETDMA_IR_REG(priv->tx_chan));
+       enet_dmac_writel(priv, priv->dma_chan_int_mask,
+                        ENETDMAC_IR, priv->rx_chan);
+       enet_dmac_writel(priv, priv->dma_chan_int_mask,
+                        ENETDMAC_IR, priv->tx_chan);
 
        /* make sure we enable napi before rx interrupt  */
        napi_enable(&priv->napi);
 
-       enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK,
-                       ENETDMA_IRMASK_REG(priv->rx_chan));
-       enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK,
-                       ENETDMA_IRMASK_REG(priv->tx_chan));
+       enet_dmac_writel(priv, priv->dma_chan_int_mask,
+                        ENETDMAC_IRMASK, priv->rx_chan);
+       enet_dmac_writel(priv, priv->dma_chan_int_mask,
+                        ENETDMAC_IRMASK, priv->tx_chan);
 
        if (priv->has_phy)
                phy_start(priv->phydev);
@@ -1057,14 +1165,14 @@ static void bcm_enet_disable_dma(struct bcm_enet_priv *priv, int chan)
 {
        int limit;
 
-       enet_dma_writel(priv, 0, ENETDMA_CHANCFG_REG(chan));
+       enet_dmac_writel(priv, 0, ENETDMAC_CHANCFG, chan);
 
        limit = 1000;
        do {
                u32 val;
 
-               val = enet_dma_readl(priv, ENETDMA_CHANCFG_REG(chan));
-               if (!(val & ENETDMA_CHANCFG_EN_MASK))
+               val = enet_dmac_readl(priv, ENETDMAC_CHANCFG, chan);
+               if (!(val & ENETDMAC_CHANCFG_EN_MASK))
                        break;
                udelay(1);
        } while (limit--);
@@ -1090,8 +1198,8 @@ static int bcm_enet_stop(struct net_device *dev)
 
        /* mask all interrupts */
        enet_writel(priv, 0, ENET_IRMASK_REG);
-       enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->rx_chan));
-       enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->tx_chan));
+       enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan);
+       enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan);
 
        /* make sure no mib update is scheduled */
        cancel_work_sync(&priv->mib_update_task);
@@ -1328,6 +1436,20 @@ static void bcm_enet_get_ethtool_stats(struct net_device *netdev,
        mutex_unlock(&priv->mib_update_lock);
 }
 
+static int bcm_enet_nway_reset(struct net_device *dev)
+{
+       struct bcm_enet_priv *priv;
+
+       priv = netdev_priv(dev);
+       if (priv->has_phy) {
+               if (!priv->phydev)
+                       return -ENODEV;
+               return genphy_restart_aneg(priv->phydev);
+       }
+
+       return -EOPNOTSUPP;
+}
+
 static int bcm_enet_get_settings(struct net_device *dev,
                                 struct ethtool_cmd *cmd)
 {
@@ -1470,6 +1592,7 @@ static const struct ethtool_ops bcm_enet_ethtool_ops = {
        .get_strings            = bcm_enet_get_strings,
        .get_sset_count         = bcm_enet_get_sset_count,
        .get_ethtool_stats      = bcm_enet_get_ethtool_stats,
+       .nway_reset             = bcm_enet_nway_reset,
        .get_settings           = bcm_enet_get_settings,
        .set_settings           = bcm_enet_set_settings,
        .get_drvinfo            = bcm_enet_get_drvinfo,
@@ -1530,7 +1653,7 @@ static int compute_hw_mtu(struct bcm_enet_priv *priv, int mtu)
         * it's appended
         */
        priv->rx_skb_size = ALIGN(actual_mtu + ETH_FCS_LEN,
-                                 BCMENET_DMA_MAXBURST * 4);
+                                 priv->dma_maxburst * 4);
        return 0;
 }
 
@@ -1621,7 +1744,7 @@ static int bcm_enet_probe(struct platform_device *pdev)
 
        /* stop if shared driver failed, assume driver->probe will be
         * called in the same order we register devices (correct ?) */
-       if (!bcm_enet_shared_base)
+       if (!bcm_enet_shared_base[0])
                return -ENODEV;
 
        res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1637,6 +1760,9 @@ static int bcm_enet_probe(struct platform_device *pdev)
                return -ENOMEM;
        priv = netdev_priv(dev);
 
+       priv->enet_is_sw = false;
+       priv->dma_maxburst = BCMENET_DMA_MAXBURST;
+
        ret = compute_hw_mtu(priv, dev->mtu);
        if (ret)
                goto out;
@@ -1687,6 +1813,11 @@ static int bcm_enet_probe(struct platform_device *pdev)
                priv->pause_tx = pd->pause_tx;
                priv->force_duplex_full = pd->force_duplex_full;
                priv->force_speed_100 = pd->force_speed_100;
+               priv->dma_chan_en_mask = pd->dma_chan_en_mask;
+               priv->dma_chan_int_mask = pd->dma_chan_int_mask;
+               priv->dma_chan_width = pd->dma_chan_width;
+               priv->dma_has_sram = pd->dma_has_sram;
+               priv->dma_desc_shift = pd->dma_desc_shift;
        }
 
        if (priv->mac_id == 0 && priv->has_phy && !priv->use_external_mii) {
@@ -1847,7 +1978,6 @@ static int bcm_enet_remove(struct platform_device *pdev)
        clk_disable_unprepare(priv->mac_clk);
        clk_put(priv->mac_clk);
 
-       platform_set_drvdata(pdev, NULL);
        free_netdev(dev);
        return 0;
 }
@@ -1862,55 +1992,920 @@ struct platform_driver bcm63xx_enet_driver = {
 };
 
 /*
- * reserve & remap memory space shared between all macs
+ * switch mii access callbacks
  */
-static int bcm_enet_shared_probe(struct platform_device *pdev)
+static int bcmenet_sw_mdio_read(struct bcm_enet_priv *priv,
+                               int ext, int phy_id, int location)
 {
-       struct resource *res;
+       u32 reg;
+       int ret;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -ENODEV;
+       spin_lock_bh(&priv->enetsw_mdio_lock);
+       enetsw_writel(priv, 0, ENETSW_MDIOC_REG);
 
-       bcm_enet_shared_base = devm_request_and_ioremap(&pdev->dev, res);
-       if (!bcm_enet_shared_base)
-               return -ENOMEM;
+       reg = ENETSW_MDIOC_RD_MASK |
+               (phy_id << ENETSW_MDIOC_PHYID_SHIFT) |
+               (location << ENETSW_MDIOC_REG_SHIFT);
 
-       return 0;
+       if (ext)
+               reg |= ENETSW_MDIOC_EXT_MASK;
+
+       enetsw_writel(priv, reg, ENETSW_MDIOC_REG);
+       udelay(50);
+       ret = enetsw_readw(priv, ENETSW_MDIOD_REG);
+       spin_unlock_bh(&priv->enetsw_mdio_lock);
+       return ret;
 }
 
-static int bcm_enet_shared_remove(struct platform_device *pdev)
+static void bcmenet_sw_mdio_write(struct bcm_enet_priv *priv,
+                                int ext, int phy_id, int location,
+                                uint16_t data)
 {
-       return 0;
+       u32 reg;
+
+       spin_lock_bh(&priv->enetsw_mdio_lock);
+       enetsw_writel(priv, 0, ENETSW_MDIOC_REG);
+
+       reg = ENETSW_MDIOC_WR_MASK |
+               (phy_id << ENETSW_MDIOC_PHYID_SHIFT) |
+               (location << ENETSW_MDIOC_REG_SHIFT);
+
+       if (ext)
+               reg |= ENETSW_MDIOC_EXT_MASK;
+
+       reg |= data;
+
+       enetsw_writel(priv, reg, ENETSW_MDIOC_REG);
+       udelay(50);
+       spin_unlock_bh(&priv->enetsw_mdio_lock);
+}
+
+static inline int bcm_enet_port_is_rgmii(int portid)
+{
+       return portid >= ENETSW_RGMII_PORT0;
 }
 
 /*
- * this "shared" driver is needed because both macs share a single
- * address space
+ * enet sw PHY polling
  */
-struct platform_driver bcm63xx_enet_shared_driver = {
-       .probe  = bcm_enet_shared_probe,
-       .remove = bcm_enet_shared_remove,
-       .driver = {
-               .name   = "bcm63xx_enet_shared",
-               .owner  = THIS_MODULE,
-       },
-};
+static void swphy_poll_timer(unsigned long data)
+{
+       struct bcm_enet_priv *priv = (struct bcm_enet_priv *)data;
+       unsigned int i;
+
+       for (i = 0; i < priv->num_ports; i++) {
+               struct bcm63xx_enetsw_port *port;
+               int val, j, up, advertise, lpa, lpa2, speed, duplex, media;
+               int external_phy = bcm_enet_port_is_rgmii(i);
+               u8 override;
+
+               port = &priv->used_ports[i];
+               if (!port->used)
+                       continue;
+
+               if (port->bypass_link)
+                       continue;
+
+               /* dummy read to clear */
+               for (j = 0; j < 2; j++)
+                       val = bcmenet_sw_mdio_read(priv, external_phy,
+                                                  port->phy_id, MII_BMSR);
+
+               if (val == 0xffff)
+                       continue;
+
+               up = (val & BMSR_LSTATUS) ? 1 : 0;
+               if (!(up ^ priv->sw_port_link[i]))
+                       continue;
+
+               priv->sw_port_link[i] = up;
+
+               /* link changed */
+               if (!up) {
+                       dev_info(&priv->pdev->dev, "link DOWN on %s\n",
+                                port->name);
+                       enetsw_writeb(priv, ENETSW_PORTOV_ENABLE_MASK,
+                                     ENETSW_PORTOV_REG(i));
+                       enetsw_writeb(priv, ENETSW_PTCTRL_RXDIS_MASK |
+                                     ENETSW_PTCTRL_TXDIS_MASK,
+                                     ENETSW_PTCTRL_REG(i));
+                       continue;
+               }
+
+               advertise = bcmenet_sw_mdio_read(priv, external_phy,
+                                                port->phy_id, MII_ADVERTISE);
+
+               lpa = bcmenet_sw_mdio_read(priv, external_phy, port->phy_id,
+                                          MII_LPA);
+
+               lpa2 = bcmenet_sw_mdio_read(priv, external_phy, port->phy_id,
+                                           MII_STAT1000);
+
+               /* figure out media and duplex from advertise and LPA values */
+               media = mii_nway_result(lpa & advertise);
+               duplex = (media & ADVERTISE_FULL) ? 1 : 0;
+               if (lpa2 & LPA_1000FULL)
+                       duplex = 1;
+
+               if (lpa2 & (LPA_1000FULL | LPA_1000HALF))
+                       speed = 1000;
+               else {
+                       if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF))
+                               speed = 100;
+                       else
+                               speed = 10;
+               }
+
+               dev_info(&priv->pdev->dev,
+                        "link UP on %s, %dMbps, %s-duplex\n",
+                        port->name, speed, duplex ? "full" : "half");
+
+               override = ENETSW_PORTOV_ENABLE_MASK |
+                       ENETSW_PORTOV_LINKUP_MASK;
+
+               if (speed == 1000)
+                       override |= ENETSW_IMPOV_1000_MASK;
+               else if (speed == 100)
+                       override |= ENETSW_IMPOV_100_MASK;
+               if (duplex)
+                       override |= ENETSW_IMPOV_FDX_MASK;
+
+               enetsw_writeb(priv, override, ENETSW_PORTOV_REG(i));
+               enetsw_writeb(priv, 0, ENETSW_PTCTRL_REG(i));
+       }
+
+       priv->swphy_poll.expires = jiffies + HZ;
+       add_timer(&priv->swphy_poll);
+}
 
 /*
- * entry point
+ * open callback, allocate dma rings & buffers and start rx operation
  */
-static int __init bcm_enet_init(void)
+static int bcm_enetsw_open(struct net_device *dev)
 {
-       int ret;
+       struct bcm_enet_priv *priv;
+       struct device *kdev;
+       int i, ret;
+       unsigned int size;
+       void *p;
+       u32 val;
 
-       ret = platform_driver_register(&bcm63xx_enet_shared_driver);
-       if (ret)
-               return ret;
+       priv = netdev_priv(dev);
+       kdev = &priv->pdev->dev;
 
-       ret = platform_driver_register(&bcm63xx_enet_driver);
+       /* mask all interrupts and request them */
+       enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan);
+       enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan);
+
+       ret = request_irq(priv->irq_rx, bcm_enet_isr_dma,
+                         IRQF_DISABLED, dev->name, dev);
        if (ret)
-               platform_driver_unregister(&bcm63xx_enet_shared_driver);
+               goto out_freeirq;
+
+       if (priv->irq_tx != -1) {
+               ret = request_irq(priv->irq_tx, bcm_enet_isr_dma,
+                                 IRQF_DISABLED, dev->name, dev);
+               if (ret)
+                       goto out_freeirq_rx;
+       }
+
+       /* allocate rx dma ring */
+       size = priv->rx_ring_size * sizeof(struct bcm_enet_desc);
+       p = dma_alloc_coherent(kdev, size, &priv->rx_desc_dma, GFP_KERNEL);
+       if (!p) {
+               dev_err(kdev, "cannot allocate rx ring %u\n", size);
+               ret = -ENOMEM;
+               goto out_freeirq_tx;
+       }
+
+       memset(p, 0, size);
+       priv->rx_desc_alloc_size = size;
+       priv->rx_desc_cpu = p;
+
+       /* allocate tx dma ring */
+       size = priv->tx_ring_size * sizeof(struct bcm_enet_desc);
+       p = dma_alloc_coherent(kdev, size, &priv->tx_desc_dma, GFP_KERNEL);
+       if (!p) {
+               dev_err(kdev, "cannot allocate tx ring\n");
+               ret = -ENOMEM;
+               goto out_free_rx_ring;
+       }
+
+       memset(p, 0, size);
+       priv->tx_desc_alloc_size = size;
+       priv->tx_desc_cpu = p;
+
+       priv->tx_skb = kzalloc(sizeof(struct sk_buff *) * priv->tx_ring_size,
+                              GFP_KERNEL);
+       if (!priv->tx_skb) {
+               dev_err(kdev, "cannot allocate rx skb queue\n");
+               ret = -ENOMEM;
+               goto out_free_tx_ring;
+       }
+
+       priv->tx_desc_count = priv->tx_ring_size;
+       priv->tx_dirty_desc = 0;
+       priv->tx_curr_desc = 0;
+       spin_lock_init(&priv->tx_lock);
+
+       /* init & fill rx ring with skbs */
+       priv->rx_skb = kzalloc(sizeof(struct sk_buff *) * priv->rx_ring_size,
+                              GFP_KERNEL);
+       if (!priv->rx_skb) {
+               dev_err(kdev, "cannot allocate rx skb queue\n");
+               ret = -ENOMEM;
+               goto out_free_tx_skb;
+       }
+
+       priv->rx_desc_count = 0;
+       priv->rx_dirty_desc = 0;
+       priv->rx_curr_desc = 0;
+
+       /* disable all ports */
+       for (i = 0; i < priv->num_ports; i++) {
+               enetsw_writeb(priv, ENETSW_PORTOV_ENABLE_MASK,
+                             ENETSW_PORTOV_REG(i));
+               enetsw_writeb(priv, ENETSW_PTCTRL_RXDIS_MASK |
+                             ENETSW_PTCTRL_TXDIS_MASK,
+                             ENETSW_PTCTRL_REG(i));
+
+               priv->sw_port_link[i] = 0;
+       }
+
+       /* reset mib */
+       val = enetsw_readb(priv, ENETSW_GMCR_REG);
+       val |= ENETSW_GMCR_RST_MIB_MASK;
+       enetsw_writeb(priv, val, ENETSW_GMCR_REG);
+       mdelay(1);
+       val &= ~ENETSW_GMCR_RST_MIB_MASK;
+       enetsw_writeb(priv, val, ENETSW_GMCR_REG);
+       mdelay(1);
+
+       /* force CPU port state */
+       val = enetsw_readb(priv, ENETSW_IMPOV_REG);
+       val |= ENETSW_IMPOV_FORCE_MASK | ENETSW_IMPOV_LINKUP_MASK;
+       enetsw_writeb(priv, val, ENETSW_IMPOV_REG);
+
+       /* enable switch forward engine */
+       val = enetsw_readb(priv, ENETSW_SWMODE_REG);
+       val |= ENETSW_SWMODE_FWD_EN_MASK;
+       enetsw_writeb(priv, val, ENETSW_SWMODE_REG);
+
+       /* enable jumbo on all ports */
+       enetsw_writel(priv, 0x1ff, ENETSW_JMBCTL_PORT_REG);
+       enetsw_writew(priv, 9728, ENETSW_JMBCTL_MAXSIZE_REG);
+
+       /* initialize flow control buffer allocation */
+       enet_dma_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0,
+                       ENETDMA_BUFALLOC_REG(priv->rx_chan));
+
+       if (bcm_enet_refill_rx(dev)) {
+               dev_err(kdev, "cannot allocate rx skb queue\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       /* write rx & tx ring addresses */
+       enet_dmas_writel(priv, priv->rx_desc_dma,
+                        ENETDMAS_RSTART_REG, priv->rx_chan);
+       enet_dmas_writel(priv, priv->tx_desc_dma,
+                        ENETDMAS_RSTART_REG, priv->tx_chan);
+
+       /* clear remaining state ram for rx & tx channel */
+       enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->rx_chan);
+       enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG, priv->tx_chan);
+       enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->rx_chan);
+       enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG, priv->tx_chan);
+       enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->rx_chan);
+       enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG, priv->tx_chan);
+
+       /* set dma maximum burst len */
+       enet_dmac_writel(priv, priv->dma_maxburst,
+                        ENETDMAC_MAXBURST, priv->rx_chan);
+       enet_dmac_writel(priv, priv->dma_maxburst,
+                        ENETDMAC_MAXBURST, priv->tx_chan);
+
+       /* set flow control low/high threshold to 1/3 / 2/3 */
+       val = priv->rx_ring_size / 3;
+       enet_dma_writel(priv, val, ENETDMA_FLOWCL_REG(priv->rx_chan));
+       val = (priv->rx_ring_size * 2) / 3;
+       enet_dma_writel(priv, val, ENETDMA_FLOWCH_REG(priv->rx_chan));
+
+       /* all set, enable mac and interrupts, start dma engine and
+        * kick rx dma channel
+        */
+       wmb();
+       enet_dma_writel(priv, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG);
+       enet_dmac_writel(priv, ENETDMAC_CHANCFG_EN_MASK,
+                        ENETDMAC_CHANCFG, priv->rx_chan);
+
+       /* watch "packet transferred" interrupt in rx and tx */
+       enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK,
+                        ENETDMAC_IR, priv->rx_chan);
+       enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK,
+                        ENETDMAC_IR, priv->tx_chan);
+
+       /* make sure we enable napi before rx interrupt  */
+       napi_enable(&priv->napi);
+
+       enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK,
+                        ENETDMAC_IRMASK, priv->rx_chan);
+       enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK,
+                        ENETDMAC_IRMASK, priv->tx_chan);
+
+       netif_carrier_on(dev);
+       netif_start_queue(dev);
+
+       /* apply override config for bypass_link ports here. */
+       for (i = 0; i < priv->num_ports; i++) {
+               struct bcm63xx_enetsw_port *port;
+               u8 override;
+               port = &priv->used_ports[i];
+               if (!port->used)
+                       continue;
+
+               if (!port->bypass_link)
+                       continue;
+
+               override = ENETSW_PORTOV_ENABLE_MASK |
+                       ENETSW_PORTOV_LINKUP_MASK;
+
+               switch (port->force_speed) {
+               case 1000:
+                       override |= ENETSW_IMPOV_1000_MASK;
+                       break;
+               case 100:
+                       override |= ENETSW_IMPOV_100_MASK;
+                       break;
+               case 10:
+                       break;
+               default:
+                       pr_warn("invalid forced speed on port %s: assume 10\n",
+                              port->name);
+                       break;
+               }
+
+               if (port->force_duplex_full)
+                       override |= ENETSW_IMPOV_FDX_MASK;
+
+
+               enetsw_writeb(priv, override, ENETSW_PORTOV_REG(i));
+               enetsw_writeb(priv, 0, ENETSW_PTCTRL_REG(i));
+       }
+
+       /* start phy polling timer */
+       init_timer(&priv->swphy_poll);
+       priv->swphy_poll.function = swphy_poll_timer;
+       priv->swphy_poll.data = (unsigned long)priv;
+       priv->swphy_poll.expires = jiffies;
+       add_timer(&priv->swphy_poll);
+       return 0;
+
+out:
+       for (i = 0; i < priv->rx_ring_size; i++) {
+               struct bcm_enet_desc *desc;
+
+               if (!priv->rx_skb[i])
+                       continue;
+
+               desc = &priv->rx_desc_cpu[i];
+               dma_unmap_single(kdev, desc->address, priv->rx_skb_size,
+                                DMA_FROM_DEVICE);
+               kfree_skb(priv->rx_skb[i]);
+       }
+       kfree(priv->rx_skb);
+
+out_free_tx_skb:
+       kfree(priv->tx_skb);
+
+out_free_tx_ring:
+       dma_free_coherent(kdev, priv->tx_desc_alloc_size,
+                         priv->tx_desc_cpu, priv->tx_desc_dma);
+
+out_free_rx_ring:
+       dma_free_coherent(kdev, priv->rx_desc_alloc_size,
+                         priv->rx_desc_cpu, priv->rx_desc_dma);
+
+out_freeirq_tx:
+       if (priv->irq_tx != -1)
+               free_irq(priv->irq_tx, dev);
+
+out_freeirq_rx:
+       free_irq(priv->irq_rx, dev);
+
+out_freeirq:
+       return ret;
+}
+
+/* stop callback */
+static int bcm_enetsw_stop(struct net_device *dev)
+{
+       struct bcm_enet_priv *priv;
+       struct device *kdev;
+       int i;
+
+       priv = netdev_priv(dev);
+       kdev = &priv->pdev->dev;
+
+       del_timer_sync(&priv->swphy_poll);
+       netif_stop_queue(dev);
+       napi_disable(&priv->napi);
+       del_timer_sync(&priv->rx_timeout);
+
+       /* mask all interrupts */
+       enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->rx_chan);
+       enet_dmac_writel(priv, 0, ENETDMAC_IRMASK, priv->tx_chan);
+
+       /* disable dma & mac */
+       bcm_enet_disable_dma(priv, priv->tx_chan);
+       bcm_enet_disable_dma(priv, priv->rx_chan);
+
+       /* force reclaim of all tx buffers */
+       bcm_enet_tx_reclaim(dev, 1);
+
+       /* free the rx skb ring */
+       for (i = 0; i < priv->rx_ring_size; i++) {
+               struct bcm_enet_desc *desc;
+
+               if (!priv->rx_skb[i])
+                       continue;
+
+               desc = &priv->rx_desc_cpu[i];
+               dma_unmap_single(kdev, desc->address, priv->rx_skb_size,
+                                DMA_FROM_DEVICE);
+               kfree_skb(priv->rx_skb[i]);
+       }
+
+       /* free remaining allocated memory */
+       kfree(priv->rx_skb);
+       kfree(priv->tx_skb);
+       dma_free_coherent(kdev, priv->rx_desc_alloc_size,
+                         priv->rx_desc_cpu, priv->rx_desc_dma);
+       dma_free_coherent(kdev, priv->tx_desc_alloc_size,
+                         priv->tx_desc_cpu, priv->tx_desc_dma);
+       if (priv->irq_tx != -1)
+               free_irq(priv->irq_tx, dev);
+       free_irq(priv->irq_rx, dev);
+
+       return 0;
+}
+
+/* try to sort out phy external status by walking the used_port field
+ * in the bcm_enet_priv structure. in case the phy address is not
+ * assigned to any physical port on the switch, assume it is external
+ * (and yell at the user).
+ */
+static int bcm_enetsw_phy_is_external(struct bcm_enet_priv *priv, int phy_id)
+{
+       int i;
+
+       for (i = 0; i < priv->num_ports; ++i) {
+               if (!priv->used_ports[i].used)
+                       continue;
+               if (priv->used_ports[i].phy_id == phy_id)
+                       return bcm_enet_port_is_rgmii(i);
+       }
+
+       printk_once(KERN_WARNING  "bcm63xx_enet: could not find a used port with phy_id %i, assuming phy is external\n",
+                   phy_id);
+       return 1;
+}
+
+/* can't use bcmenet_sw_mdio_read directly as we need to sort out
+ * external/internal status of the given phy_id first.
+ */
+static int bcm_enetsw_mii_mdio_read(struct net_device *dev, int phy_id,
+                                   int location)
+{
+       struct bcm_enet_priv *priv;
+
+       priv = netdev_priv(dev);
+       return bcmenet_sw_mdio_read(priv,
+                                   bcm_enetsw_phy_is_external(priv, phy_id),
+                                   phy_id, location);
+}
+
+/* can't use bcmenet_sw_mdio_write directly as we need to sort out
+ * external/internal status of the given phy_id first.
+ */
+static void bcm_enetsw_mii_mdio_write(struct net_device *dev, int phy_id,
+                                     int location,
+                                     int val)
+{
+       struct bcm_enet_priv *priv;
+
+       priv = netdev_priv(dev);
+       bcmenet_sw_mdio_write(priv, bcm_enetsw_phy_is_external(priv, phy_id),
+                             phy_id, location, val);
+}
+
+static int bcm_enetsw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       struct mii_if_info mii;
+
+       mii.dev = dev;
+       mii.mdio_read = bcm_enetsw_mii_mdio_read;
+       mii.mdio_write = bcm_enetsw_mii_mdio_write;
+       mii.phy_id = 0;
+       mii.phy_id_mask = 0x3f;
+       mii.reg_num_mask = 0x1f;
+       return generic_mii_ioctl(&mii, if_mii(rq), cmd, NULL);
+
+}
+
+static const struct net_device_ops bcm_enetsw_ops = {
+       .ndo_open               = bcm_enetsw_open,
+       .ndo_stop               = bcm_enetsw_stop,
+       .ndo_start_xmit         = bcm_enet_start_xmit,
+       .ndo_change_mtu         = bcm_enet_change_mtu,
+       .ndo_do_ioctl           = bcm_enetsw_ioctl,
+};
+
+
+static const struct bcm_enet_stats bcm_enetsw_gstrings_stats[] = {
+       { "rx_packets", DEV_STAT(rx_packets), -1 },
+       { "tx_packets", DEV_STAT(tx_packets), -1 },
+       { "rx_bytes", DEV_STAT(rx_bytes), -1 },
+       { "tx_bytes", DEV_STAT(tx_bytes), -1 },
+       { "rx_errors", DEV_STAT(rx_errors), -1 },
+       { "tx_errors", DEV_STAT(tx_errors), -1 },
+       { "rx_dropped", DEV_STAT(rx_dropped), -1 },
+       { "tx_dropped", DEV_STAT(tx_dropped), -1 },
+
+       { "tx_good_octets", GEN_STAT(mib.tx_gd_octets), ETHSW_MIB_RX_GD_OCT },
+       { "tx_unicast", GEN_STAT(mib.tx_unicast), ETHSW_MIB_RX_BRDCAST },
+       { "tx_broadcast", GEN_STAT(mib.tx_brdcast), ETHSW_MIB_RX_BRDCAST },
+       { "tx_multicast", GEN_STAT(mib.tx_mult), ETHSW_MIB_RX_MULT },
+       { "tx_64_octets", GEN_STAT(mib.tx_64), ETHSW_MIB_RX_64 },
+       { "tx_65_127_oct", GEN_STAT(mib.tx_65_127), ETHSW_MIB_RX_65_127 },
+       { "tx_128_255_oct", GEN_STAT(mib.tx_128_255), ETHSW_MIB_RX_128_255 },
+       { "tx_256_511_oct", GEN_STAT(mib.tx_256_511), ETHSW_MIB_RX_256_511 },
+       { "tx_512_1023_oct", GEN_STAT(mib.tx_512_1023), ETHSW_MIB_RX_512_1023},
+       { "tx_1024_1522_oct", GEN_STAT(mib.tx_1024_max),
+         ETHSW_MIB_RX_1024_1522 },
+       { "tx_1523_2047_oct", GEN_STAT(mib.tx_1523_2047),
+         ETHSW_MIB_RX_1523_2047 },
+       { "tx_2048_4095_oct", GEN_STAT(mib.tx_2048_4095),
+         ETHSW_MIB_RX_2048_4095 },
+       { "tx_4096_8191_oct", GEN_STAT(mib.tx_4096_8191),
+         ETHSW_MIB_RX_4096_8191 },
+       { "tx_8192_9728_oct", GEN_STAT(mib.tx_8192_9728),
+         ETHSW_MIB_RX_8192_9728 },
+       { "tx_oversize", GEN_STAT(mib.tx_ovr), ETHSW_MIB_RX_OVR },
+       { "tx_oversize_drop", GEN_STAT(mib.tx_ovr), ETHSW_MIB_RX_OVR_DISC },
+       { "tx_dropped", GEN_STAT(mib.tx_drop), ETHSW_MIB_RX_DROP },
+       { "tx_undersize", GEN_STAT(mib.tx_underrun), ETHSW_MIB_RX_UND },
+       { "tx_pause", GEN_STAT(mib.tx_pause), ETHSW_MIB_RX_PAUSE },
+
+       { "rx_good_octets", GEN_STAT(mib.rx_gd_octets), ETHSW_MIB_TX_ALL_OCT },
+       { "rx_broadcast", GEN_STAT(mib.rx_brdcast), ETHSW_MIB_TX_BRDCAST },
+       { "rx_multicast", GEN_STAT(mib.rx_mult), ETHSW_MIB_TX_MULT },
+       { "rx_unicast", GEN_STAT(mib.rx_unicast), ETHSW_MIB_TX_MULT },
+       { "rx_pause", GEN_STAT(mib.rx_pause), ETHSW_MIB_TX_PAUSE },
+       { "rx_dropped", GEN_STAT(mib.rx_drop), ETHSW_MIB_TX_DROP_PKTS },
+
+};
+
+#define BCM_ENETSW_STATS_LEN   \
+       (sizeof(bcm_enetsw_gstrings_stats) / sizeof(struct bcm_enet_stats))
+
+static void bcm_enetsw_get_strings(struct net_device *netdev,
+                                  u32 stringset, u8 *data)
+{
+       int i;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) {
+                       memcpy(data + i * ETH_GSTRING_LEN,
+                              bcm_enetsw_gstrings_stats[i].stat_string,
+                              ETH_GSTRING_LEN);
+               }
+               break;
+       }
+}
+
+static int bcm_enetsw_get_sset_count(struct net_device *netdev,
+                                    int string_set)
+{
+       switch (string_set) {
+       case ETH_SS_STATS:
+               return BCM_ENETSW_STATS_LEN;
+       default:
+               return -EINVAL;
+       }
+}
+
+static void bcm_enetsw_get_drvinfo(struct net_device *netdev,
+                                  struct ethtool_drvinfo *drvinfo)
+{
+       strncpy(drvinfo->driver, bcm_enet_driver_name, 32);
+       strncpy(drvinfo->version, bcm_enet_driver_version, 32);
+       strncpy(drvinfo->fw_version, "N/A", 32);
+       strncpy(drvinfo->bus_info, "bcm63xx", 32);
+       drvinfo->n_stats = BCM_ENETSW_STATS_LEN;
+}
+
+static void bcm_enetsw_get_ethtool_stats(struct net_device *netdev,
+                                        struct ethtool_stats *stats,
+                                        u64 *data)
+{
+       struct bcm_enet_priv *priv;
+       int i;
+
+       priv = netdev_priv(netdev);
+
+       for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) {
+               const struct bcm_enet_stats *s;
+               u32 lo, hi;
+               char *p;
+               int reg;
+
+               s = &bcm_enetsw_gstrings_stats[i];
+
+               reg = s->mib_reg;
+               if (reg == -1)
+                       continue;
+
+               lo = enetsw_readl(priv, ENETSW_MIB_REG(reg));
+               p = (char *)priv + s->stat_offset;
+
+               if (s->sizeof_stat == sizeof(u64)) {
+                       hi = enetsw_readl(priv, ENETSW_MIB_REG(reg + 1));
+                       *(u64 *)p = ((u64)hi << 32 | lo);
+               } else {
+                       *(u32 *)p = lo;
+               }
+       }
+
+       for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) {
+               const struct bcm_enet_stats *s;
+               char *p;
+
+               s = &bcm_enetsw_gstrings_stats[i];
+
+               if (s->mib_reg == -1)
+                       p = (char *)&netdev->stats + s->stat_offset;
+               else
+                       p = (char *)priv + s->stat_offset;
+
+               data[i] = (s->sizeof_stat == sizeof(u64)) ?
+                       *(u64 *)p : *(u32 *)p;
+       }
+}
+
+static void bcm_enetsw_get_ringparam(struct net_device *dev,
+                                    struct ethtool_ringparam *ering)
+{
+       struct bcm_enet_priv *priv;
+
+       priv = netdev_priv(dev);
+
+       /* rx/tx ring is actually only limited by memory */
+       ering->rx_max_pending = 8192;
+       ering->tx_max_pending = 8192;
+       ering->rx_mini_max_pending = 0;
+       ering->rx_jumbo_max_pending = 0;
+       ering->rx_pending = priv->rx_ring_size;
+       ering->tx_pending = priv->tx_ring_size;
+}
+
+static int bcm_enetsw_set_ringparam(struct net_device *dev,
+                                   struct ethtool_ringparam *ering)
+{
+       struct bcm_enet_priv *priv;
+       int was_running;
+
+       priv = netdev_priv(dev);
+
+       was_running = 0;
+       if (netif_running(dev)) {
+               bcm_enetsw_stop(dev);
+               was_running = 1;
+       }
+
+       priv->rx_ring_size = ering->rx_pending;
+       priv->tx_ring_size = ering->tx_pending;
+
+       if (was_running) {
+               int err;
+
+               err = bcm_enetsw_open(dev);
+               if (err)
+                       dev_close(dev);
+       }
+       return 0;
+}
+
+static struct ethtool_ops bcm_enetsw_ethtool_ops = {
+       .get_strings            = bcm_enetsw_get_strings,
+       .get_sset_count         = bcm_enetsw_get_sset_count,
+       .get_ethtool_stats      = bcm_enetsw_get_ethtool_stats,
+       .get_drvinfo            = bcm_enetsw_get_drvinfo,
+       .get_ringparam          = bcm_enetsw_get_ringparam,
+       .set_ringparam          = bcm_enetsw_set_ringparam,
+};
+
+/* allocate netdevice, request register memory and register device. */
+static int bcm_enetsw_probe(struct platform_device *pdev)
+{
+       struct bcm_enet_priv *priv;
+       struct net_device *dev;
+       struct bcm63xx_enetsw_platform_data *pd;
+       struct resource *res_mem;
+       int ret, irq_rx, irq_tx;
+
+       /* stop if shared driver failed, assume driver->probe will be
+        * called in the same order we register devices (correct ?)
+        */
+       if (!bcm_enet_shared_base[0])
+               return -ENODEV;
+
+       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       irq_rx = platform_get_irq(pdev, 0);
+       irq_tx = platform_get_irq(pdev, 1);
+       if (!res_mem || irq_rx < 0)
+               return -ENODEV;
+
+       ret = 0;
+       dev = alloc_etherdev(sizeof(*priv));
+       if (!dev)
+               return -ENOMEM;
+       priv = netdev_priv(dev);
+       memset(priv, 0, sizeof(*priv));
+
+       /* initialize default and fetch platform data */
+       priv->enet_is_sw = true;
+       priv->irq_rx = irq_rx;
+       priv->irq_tx = irq_tx;
+       priv->rx_ring_size = BCMENET_DEF_RX_DESC;
+       priv->tx_ring_size = BCMENET_DEF_TX_DESC;
+       priv->dma_maxburst = BCMENETSW_DMA_MAXBURST;
+
+       pd = pdev->dev.platform_data;
+       if (pd) {
+               memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN);
+               memcpy(priv->used_ports, pd->used_ports,
+                      sizeof(pd->used_ports));
+               priv->num_ports = pd->num_ports;
+               priv->dma_has_sram = pd->dma_has_sram;
+               priv->dma_chan_en_mask = pd->dma_chan_en_mask;
+               priv->dma_chan_int_mask = pd->dma_chan_int_mask;
+               priv->dma_chan_width = pd->dma_chan_width;
+       }
+
+       ret = compute_hw_mtu(priv, dev->mtu);
+       if (ret)
+               goto out;
+
+       if (!request_mem_region(res_mem->start, resource_size(res_mem),
+                               "bcm63xx_enetsw")) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       priv->base = ioremap(res_mem->start, resource_size(res_mem));
+       if (priv->base == NULL) {
+               ret = -ENOMEM;
+               goto out_release_mem;
+       }
+
+       priv->mac_clk = clk_get(&pdev->dev, "enetsw");
+       if (IS_ERR(priv->mac_clk)) {
+               ret = PTR_ERR(priv->mac_clk);
+               goto out_unmap;
+       }
+       clk_enable(priv->mac_clk);
+
+       priv->rx_chan = 0;
+       priv->tx_chan = 1;
+       spin_lock_init(&priv->rx_lock);
+
+       /* init rx timeout (used for oom) */
+       init_timer(&priv->rx_timeout);
+       priv->rx_timeout.function = bcm_enet_refill_rx_timer;
+       priv->rx_timeout.data = (unsigned long)dev;
+
+       /* register netdevice */
+       dev->netdev_ops = &bcm_enetsw_ops;
+       netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16);
+       SET_ETHTOOL_OPS(dev, &bcm_enetsw_ethtool_ops);
+       SET_NETDEV_DEV(dev, &pdev->dev);
+
+       spin_lock_init(&priv->enetsw_mdio_lock);
+
+       ret = register_netdev(dev);
+       if (ret)
+               goto out_put_clk;
+
+       netif_carrier_off(dev);
+       platform_set_drvdata(pdev, dev);
+       priv->pdev = pdev;
+       priv->net_dev = dev;
+
+       return 0;
+
+out_put_clk:
+       clk_put(priv->mac_clk);
+
+out_unmap:
+       iounmap(priv->base);
+
+out_release_mem:
+       release_mem_region(res_mem->start, resource_size(res_mem));
+out:
+       free_netdev(dev);
+       return ret;
+}
+
+
+/* exit func, stops hardware and unregisters netdevice */
+static int bcm_enetsw_remove(struct platform_device *pdev)
+{
+       struct bcm_enet_priv *priv;
+       struct net_device *dev;
+       struct resource *res;
+
+       /* stop netdevice */
+       dev = platform_get_drvdata(pdev);
+       priv = netdev_priv(dev);
+       unregister_netdev(dev);
+
+       /* release device resources */
+       iounmap(priv->base);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, resource_size(res));
+
+       platform_set_drvdata(pdev, NULL);
+       free_netdev(dev);
+       return 0;
+}
+
+struct platform_driver bcm63xx_enetsw_driver = {
+       .probe  = bcm_enetsw_probe,
+       .remove = bcm_enetsw_remove,
+       .driver = {
+               .name   = "bcm63xx_enetsw",
+               .owner  = THIS_MODULE,
+       },
+};
+
+/* reserve & remap memory space shared between all macs */
+static int bcm_enet_shared_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       void __iomem *p[3];
+       unsigned int i;
+
+       memset(bcm_enet_shared_base, 0, sizeof(bcm_enet_shared_base));
+
+       for (i = 0; i < 3; i++) {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+               p[i] = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(p[i]))
+                       return PTR_ERR(p[i]);
+       }
+
+       memcpy(bcm_enet_shared_base, p, sizeof(bcm_enet_shared_base));
+
+       return 0;
+}
+
+static int bcm_enet_shared_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+/* this "shared" driver is needed because both macs share a single
+ * address space
+ */
+struct platform_driver bcm63xx_enet_shared_driver = {
+       .probe  = bcm_enet_shared_probe,
+       .remove = bcm_enet_shared_remove,
+       .driver = {
+               .name   = "bcm63xx_enet_shared",
+               .owner  = THIS_MODULE,
+       },
+};
+
+/* entry point */
+static int __init bcm_enet_init(void)
+{
+       int ret;
+
+       ret = platform_driver_register(&bcm63xx_enet_shared_driver);
+       if (ret)
+               return ret;
+
+       ret = platform_driver_register(&bcm63xx_enet_driver);
+       if (ret)
+               platform_driver_unregister(&bcm63xx_enet_shared_driver);
+
+       ret = platform_driver_register(&bcm63xx_enetsw_driver);
+       if (ret) {
+               platform_driver_unregister(&bcm63xx_enet_driver);
+               platform_driver_unregister(&bcm63xx_enet_shared_driver);
+       }
 
        return ret;
 }
@@ -1918,6 +2913,7 @@ static int __init bcm_enet_init(void)
 static void __exit bcm_enet_exit(void)
 {
        platform_driver_unregister(&bcm63xx_enet_driver);
+       platform_driver_unregister(&bcm63xx_enetsw_driver);
        platform_driver_unregister(&bcm63xx_enet_shared_driver);
 }
 
index 133d585..f55af43 100644 (file)
@@ -18,6 +18,7 @@
 
 /* maximum burst len for dma (4 bytes unit) */
 #define BCMENET_DMA_MAXBURST   16
+#define BCMENETSW_DMA_MAXBURST 8
 
 /* tx transmit threshold (4 bytes unit), fifo is 256 bytes, the value
  * must be low enough so that a DMA transfer of above burst length can
 #define ETH_MIB_RX_CNTRL                       54
 
 
+/*
+ * SW MIB Counters register definitions
+*/
+#define ETHSW_MIB_TX_ALL_OCT                   0
+#define ETHSW_MIB_TX_DROP_PKTS                 2
+#define ETHSW_MIB_TX_QOS_PKTS                  3
+#define ETHSW_MIB_TX_BRDCAST                   4
+#define ETHSW_MIB_TX_MULT                      5
+#define ETHSW_MIB_TX_UNI                       6
+#define ETHSW_MIB_TX_COL                       7
+#define ETHSW_MIB_TX_1_COL                     8
+#define ETHSW_MIB_TX_M_COL                     9
+#define ETHSW_MIB_TX_DEF                       10
+#define ETHSW_MIB_TX_LATE                      11
+#define ETHSW_MIB_TX_EX_COL                    12
+#define ETHSW_MIB_TX_PAUSE                     14
+#define ETHSW_MIB_TX_QOS_OCT                   15
+
+#define ETHSW_MIB_RX_ALL_OCT                   17
+#define ETHSW_MIB_RX_UND                       19
+#define ETHSW_MIB_RX_PAUSE                     20
+#define ETHSW_MIB_RX_64                                21
+#define ETHSW_MIB_RX_65_127                    22
+#define ETHSW_MIB_RX_128_255                   23
+#define ETHSW_MIB_RX_256_511                   24
+#define ETHSW_MIB_RX_512_1023                  25
+#define ETHSW_MIB_RX_1024_1522                 26
+#define ETHSW_MIB_RX_OVR                       27
+#define ETHSW_MIB_RX_JAB                       28
+#define ETHSW_MIB_RX_ALIGN                     29
+#define ETHSW_MIB_RX_CRC                       30
+#define ETHSW_MIB_RX_GD_OCT                    31
+#define ETHSW_MIB_RX_DROP                      33
+#define ETHSW_MIB_RX_UNI                       34
+#define ETHSW_MIB_RX_MULT                      35
+#define ETHSW_MIB_RX_BRDCAST                   36
+#define ETHSW_MIB_RX_SA_CHANGE                 37
+#define ETHSW_MIB_RX_FRAG                      38
+#define ETHSW_MIB_RX_OVR_DISC                  39
+#define ETHSW_MIB_RX_SYM                       40
+#define ETHSW_MIB_RX_QOS_PKTS                  41
+#define ETHSW_MIB_RX_QOS_OCT                   42
+#define ETHSW_MIB_RX_1523_2047                 44
+#define ETHSW_MIB_RX_2048_4095                 45
+#define ETHSW_MIB_RX_4096_8191                 46
+#define ETHSW_MIB_RX_8192_9728                 47
+
+
 struct bcm_enet_mib_counters {
        u64 tx_gd_octets;
        u32 tx_gd_pkts;
        u32 tx_all_octets;
        u32 tx_all_pkts;
+       u32 tx_unicast;
        u32 tx_brdcast;
        u32 tx_mult;
        u32 tx_64;
@@ -97,7 +147,12 @@ struct bcm_enet_mib_counters {
        u32 tx_256_511;
        u32 tx_512_1023;
        u32 tx_1024_max;
+       u32 tx_1523_2047;
+       u32 tx_2048_4095;
+       u32 tx_4096_8191;
+       u32 tx_8192_9728;
        u32 tx_jab;
+       u32 tx_drop;
        u32 tx_ovr;
        u32 tx_frag;
        u32 tx_underrun;
@@ -114,6 +169,7 @@ struct bcm_enet_mib_counters {
        u32 rx_all_octets;
        u32 rx_all_pkts;
        u32 rx_brdcast;
+       u32 rx_unicast;
        u32 rx_mult;
        u32 rx_64;
        u32 rx_65_127;
@@ -197,6 +253,9 @@ struct bcm_enet_priv {
        /* number of dma desc in tx ring */
        int tx_ring_size;
 
+       /* maximum dma burst size */
+       int dma_maxburst;
+
        /* cpu view of rx dma ring */
        struct bcm_enet_desc *tx_desc_cpu;
 
@@ -269,6 +328,33 @@ struct bcm_enet_priv {
 
        /* maximum hardware transmit/receive size */
        unsigned int hw_mtu;
+
+       bool enet_is_sw;
+
+       /* port mapping for switch devices */
+       int num_ports;
+       struct bcm63xx_enetsw_port used_ports[ENETSW_MAX_PORT];
+       int sw_port_link[ENETSW_MAX_PORT];
+
+       /* used to poll switch port state */
+       struct timer_list swphy_poll;
+       spinlock_t enetsw_mdio_lock;
+
+       /* dma channel enable mask */
+       u32 dma_chan_en_mask;
+
+       /* dma channel interrupt mask */
+       u32 dma_chan_int_mask;
+
+       /* DMA engine has internal SRAM */
+       bool dma_has_sram;
+
+       /* dma channel width */
+       unsigned int dma_chan_width;
+
+       /* dma descriptor shift value */
+       unsigned int dma_desc_shift;
 };
 
+
 #endif /* ! BCM63XX_ENET_H_ */
index 5d20449..6a2de1d 100644 (file)
@@ -8104,7 +8104,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
 
        pci_set_master(pdev);
 
-       bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+       bp->pm_cap = pdev->pm_cap;
        if (bp->pm_cap == 0) {
                dev_err(&pdev->dev,
                        "Cannot find power management capability, aborting\n");
@@ -8764,18 +8764,4 @@ static struct pci_driver bnx2_pci_driver = {
        .err_handler    = &bnx2_err_handler,
 };
 
-static int __init bnx2_init(void)
-{
-       return pci_register_driver(&bnx2_pci_driver);
-}
-
-static void __exit bnx2_cleanup(void)
-{
-       pci_unregister_driver(&bnx2_pci_driver);
-}
-
-module_init(bnx2_init);
-module_exit(bnx2_cleanup);
-
-
-
+module_pci_driver(bnx2_pci_driver);
index 3dba2a7..dedbd76 100644 (file)
 #define BCM_DCBNL
 #endif
 
-
 #include "bnx2x_hsi.h"
 
 #include "../cnic_if.h"
 
-
 #define BNX2X_MIN_MSIX_VEC_CNT(bp)             ((bp)->min_msix_vec_cnt)
 
 #include <linux/mdio.h>
@@ -114,7 +112,6 @@ do {                                                                \
 #define BNX2X_ERROR(fmt, ...)                                  \
        pr_err("[%s:%d]" fmt, __func__, __LINE__, ##__VA_ARGS__)
 
-
 /* before we have a dev->name use dev_info() */
 #define BNX2X_DEV_INFO(fmt, ...)                                \
 do {                                                            \
@@ -147,7 +144,6 @@ do {                                                \
 #define U64_HI(x)                      ((u32)(((u64)(x)) >> 32))
 #define HILO_U64(hi, lo)               ((((u64)(hi)) << 32) + (lo))
 
-
 #define REG_ADDR(bp, offset)           ((bp->regview) + (offset))
 
 #define REG_RD(bp, offset)             readl(REG_ADDR(bp, offset))
@@ -366,7 +362,7 @@ union db_prod {
 /*
  * Number of required  SGEs is the sum of two:
  * 1. Number of possible opened aggregations (next packet for
- *    these aggregations will probably consume SGE immidiatelly)
+ *    these aggregations will probably consume SGE immediately)
  * 2. Rest of BRB blocks divided by 2 (block will consume new SGE only
  *    after placement on BD for new TPA aggregation)
  *
@@ -387,7 +383,6 @@ union db_prod {
 #define BIT_VEC64_ELEM_SHIFT           6
 #define BIT_VEC64_ELEM_MASK            ((u64)BIT_VEC64_ELEM_SZ - 1)
 
-
 #define __BIT_VEC64_SET_BIT(el, bit) \
        do { \
                el = ((el) | ((u64)0x1 << (bit))); \
@@ -398,7 +393,6 @@ union db_prod {
                el = ((el) & (~((u64)0x1 << (bit)))); \
        } while (0)
 
-
 #define BIT_VEC64_SET_BIT(vec64, idx) \
        __BIT_VEC64_SET_BIT((vec64)[(idx) >> BIT_VEC64_ELEM_SHIFT], \
                           (idx) & BIT_VEC64_ELEM_MASK)
@@ -419,8 +413,6 @@ union db_prod {
 
 /*******************************************************/
 
-
-
 /* Number of u64 elements in SGE mask array */
 #define RX_SGE_MASK_LEN                        (NUM_RX_SGE / BIT_VEC64_ELEM_SZ)
 #define RX_SGE_MASK_LEN_MASK           (RX_SGE_MASK_LEN - 1)
@@ -493,11 +485,26 @@ struct bnx2x_fastpath {
        struct bnx2x            *bp; /* parent */
 
        struct napi_struct      napi;
+
+#ifdef CONFIG_NET_LL_RX_POLL
+       unsigned int state;
+#define BNX2X_FP_STATE_IDLE                  0
+#define BNX2X_FP_STATE_NAPI            (1 << 0)    /* NAPI owns this FP */
+#define BNX2X_FP_STATE_POLL            (1 << 1)    /* poll owns this FP */
+#define BNX2X_FP_STATE_NAPI_YIELD      (1 << 2)    /* NAPI yielded this FP */
+#define BNX2X_FP_STATE_POLL_YIELD      (1 << 3)    /* poll yielded this FP */
+#define BNX2X_FP_YIELD (BNX2X_FP_STATE_NAPI_YIELD | BNX2X_FP_STATE_POLL_YIELD)
+#define BNX2X_FP_LOCKED        (BNX2X_FP_STATE_NAPI | BNX2X_FP_STATE_POLL)
+#define BNX2X_FP_USER_PEND (BNX2X_FP_STATE_POLL | BNX2X_FP_STATE_POLL_YIELD)
+       /* protect state */
+       spinlock_t lock;
+#endif /* CONFIG_NET_LL_RX_POLL */
+
        union host_hc_status_block      status_blk;
-       /* chip independed shortcuts into sb structure */
+       /* chip independent shortcuts into sb structure */
        __le16                  *sb_index_values;
        __le16                  *sb_running_index;
-       /* chip independed shortcut into rx_prods_offset memory */
+       /* chip independent shortcut into rx_prods_offset memory */
        u32                     ustorm_rx_prods_offset;
 
        u32                     rx_buf_size;
@@ -565,6 +572,116 @@ struct bnx2x_fastpath {
 #define bnx2x_fp_stats(bp, fp) (&((bp)->fp_stats[(fp)->index]))
 #define bnx2x_fp_qstats(bp, fp)        (&((bp)->fp_stats[(fp)->index].eth_q_stats))
 
+#ifdef CONFIG_NET_LL_RX_POLL
+static inline void bnx2x_fp_init_lock(struct bnx2x_fastpath *fp)
+{
+       spin_lock_init(&fp->lock);
+       fp->state = BNX2X_FP_STATE_IDLE;
+}
+
+/* called from the device poll routine to get ownership of a FP */
+static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp)
+{
+       bool rc = true;
+
+       spin_lock(&fp->lock);
+       if (fp->state & BNX2X_FP_LOCKED) {
+               WARN_ON(fp->state & BNX2X_FP_STATE_NAPI);
+               fp->state |= BNX2X_FP_STATE_NAPI_YIELD;
+               rc = false;
+       } else {
+               /* we don't care if someone yielded */
+               fp->state = BNX2X_FP_STATE_NAPI;
+       }
+       spin_unlock(&fp->lock);
+       return rc;
+}
+
+/* returns true is someone tried to get the FP while napi had it */
+static inline bool bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp)
+{
+       bool rc = false;
+
+       spin_lock(&fp->lock);
+       WARN_ON(fp->state &
+               (BNX2X_FP_STATE_POLL | BNX2X_FP_STATE_NAPI_YIELD));
+
+       if (fp->state & BNX2X_FP_STATE_POLL_YIELD)
+               rc = true;
+       fp->state = BNX2X_FP_STATE_IDLE;
+       spin_unlock(&fp->lock);
+       return rc;
+}
+
+/* called from bnx2x_low_latency_poll() */
+static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp)
+{
+       bool rc = true;
+
+       spin_lock_bh(&fp->lock);
+       if ((fp->state & BNX2X_FP_LOCKED)) {
+               fp->state |= BNX2X_FP_STATE_POLL_YIELD;
+               rc = false;
+       } else {
+               /* preserve yield marks */
+               fp->state |= BNX2X_FP_STATE_POLL;
+       }
+       spin_unlock_bh(&fp->lock);
+       return rc;
+}
+
+/* returns true if someone tried to get the FP while it was locked */
+static inline bool bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp)
+{
+       bool rc = false;
+
+       spin_lock_bh(&fp->lock);
+       WARN_ON(fp->state & BNX2X_FP_STATE_NAPI);
+
+       if (fp->state & BNX2X_FP_STATE_POLL_YIELD)
+               rc = true;
+       fp->state = BNX2X_FP_STATE_IDLE;
+       spin_unlock_bh(&fp->lock);
+       return rc;
+}
+
+/* true if a socket is polling, even if it did not get the lock */
+static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp)
+{
+       WARN_ON(!(fp->state & BNX2X_FP_LOCKED));
+       return fp->state & BNX2X_FP_USER_PEND;
+}
+#else
+static inline void bnx2x_fp_init_lock(struct bnx2x_fastpath *fp)
+{
+}
+
+static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp)
+{
+       return true;
+}
+
+static inline bool bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp)
+{
+       return false;
+}
+
+static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp)
+{
+       return false;
+}
+
+static inline bool bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp)
+{
+       return false;
+}
+
+static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp)
+{
+       return false;
+}
+#endif /* CONFIG_NET_LL_RX_POLL */
+
 /* Use 2500 as a mini-jumbo MTU for FCoE */
 #define BNX2X_FCOE_MINI_JUMBO_MTU      2500
 
@@ -580,12 +697,10 @@ struct bnx2x_fastpath {
                                                txdata_ptr[FIRST_TX_COS_INDEX] \
                                                ->var)
 
-
 #define IS_ETH_FP(fp)          ((fp)->index < BNX2X_NUM_ETH_QUEUES((fp)->bp))
 #define IS_FCOE_FP(fp)         ((fp)->index == FCOE_IDX((fp)->bp))
 #define IS_FCOE_IDX(idx)       ((idx) == FCOE_IDX(bp))
 
-
 /* MC hsi */
 #define MAX_FETCH_BD           13      /* HW max BDs per packet */
 #define RX_COPY_THRESH         92
@@ -613,7 +728,7 @@ struct bnx2x_fastpath {
  * START_BD(splitted)  - includes unpaged data segment for GSO
  * PARSING_BD          - for TSO and CSUM data
  * PARSING_BD2         - for encapsulation data
- * Frag BDs            - decribes pages for frags
+ * Frag BDs            - describes pages for frags
  */
 #define BDS_PER_TX_PKT         4
 #define MAX_BDS_PER_TX_PKT     (MAX_SKB_FRAGS + BDS_PER_TX_PKT)
@@ -693,12 +808,10 @@ struct bnx2x_fastpath {
                                 FW_DROP_LEVEL(bp))
 #define RCQ_TH_HI(bp)          (RCQ_TH_LO(bp) + DROPLESS_FC_HEADROOM)
 
-
 /* This is needed for determining of last_max */
 #define SUB_S16(a, b)          (s16)((s16)(a) - (s16)(b))
 #define SUB_S32(a, b)          (s32)((s32)(a) - (s32)(b))
 
-
 #define BNX2X_SWCID_SHIFT      17
 #define BNX2X_SWCID_MASK       ((0x1 << BNX2X_SWCID_SHIFT) - 1)
 
@@ -723,7 +836,6 @@ struct bnx2x_fastpath {
                       DPM_TRIGER_TYPE); \
        } while (0)
 
-
 /* TX CSUM helpers */
 #define SKB_CS_OFF(skb)                (offsetof(struct tcphdr, check) - \
                                 skb->csum_offset)
@@ -766,7 +878,6 @@ struct bnx2x_fastpath {
 #define BNX2X_RX_SUM_FIX(cqe) \
        BNX2X_PRS_FLAG_OVERETH_IPV4(cqe->fast_path_cqe.pars_flags.flags)
 
-
 #define FP_USB_FUNC_OFF        \
                        offsetof(struct cstorm_status_block_u, func)
 #define FP_CSB_FUNC_OFF        \
@@ -900,14 +1011,14 @@ struct bnx2x_common {
 #define CHIP_IS_E3A0(bp)               (CHIP_IS_E3(bp) && \
                                         (CHIP_REV(bp) == CHIP_REV_Ax))
 /* This define is used in two main places:
- * 1. In the early stages of nic_load, to know if to configrue Parser / Searcher
+ * 1. In the early stages of nic_load, to know if to configure Parser / Searcher
  * to nic-only mode or to offload mode. Offload mode is configured if either the
  * chip is E1x (where MIC_MODE register is not applicable), or if cnic already
  * registered for this port (which means that the user wants storage services).
  * 2. During cnic-related load, to know if offload mode is already configured in
- * the HW or needs to be configrued.
+ * the HW or needs to be configured.
  * Since the transition from nic-mode to offload-mode in HW causes traffic
- * coruption, nic-mode is configured only in ports on which storage services
+ * corruption, nic-mode is configured only in ports on which storage services
  * where never requested.
  */
 #define CONFIGURE_NIC_MODE(bp)         (!CHIP_IS_E1x(bp) && !CNIC_ENABLED(bp))
@@ -1008,14 +1119,14 @@ extern struct workqueue_struct *bnx2x_wq;
  * If the maximum number of FP-SB available is X then:
  * a. If CNIC is supported it consumes 1 FP-SB thus the max number of
  *    regular L2 queues is Y=X-1
- * b. in MF mode the actual number of L2 queues is Y= (X-1/MF_factor)
+ * b. In MF mode the actual number of L2 queues is Y= (X-1/MF_factor)
  * c. If the FCoE L2 queue is supported the actual number of L2 queues
  *    is Y+1
  * d. The number of irqs (MSIX vectors) is either Y+1 (one extra for
  *    slow-path interrupts) or Y+2 if CNIC is supported (one additional
  *    FP interrupt context for the CNIC).
  * e. The number of HW context (CID count) is always X or X+1 if FCoE
- *    L2 queue is supported. the cid for the FCoE L2 queue is always X.
+ *    L2 queue is supported. The cid for the FCoE L2 queue is always X.
  */
 
 /* fast-path interrupt contexts E1x */
@@ -1068,7 +1179,6 @@ struct bnx2x_slowpath {
                struct eth_classify_rules_ramrod_data   e2;
        } mac_rdata;
 
-
        union {
                struct tstorm_eth_mac_filter_config     e1x;
                struct eth_filter_rules_ramrod_data     e2;
@@ -1119,7 +1229,6 @@ struct bnx2x_slowpath {
 #define bnx2x_sp_mapping(bp, var) \
                (bp->slowpath_mapping + offsetof(struct bnx2x_slowpath, var))
 
-
 /* attn group wiring */
 #define MAX_DYNAMIC_ATTN_GRPS          8
 
@@ -1221,11 +1330,11 @@ enum {
        BNX2X_SP_RTNL_AFEX_F_UPDATE,
        BNX2X_SP_RTNL_ENABLE_SRIOV,
        BNX2X_SP_RTNL_VFPF_MCAST,
+       BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN,
        BNX2X_SP_RTNL_VFPF_STORM_RX_MODE,
        BNX2X_SP_RTNL_HYPERVISOR_VLAN,
 };
 
-
 struct bnx2x_prev_path_list {
        struct list_head list;
        u8 bus;
@@ -1392,6 +1501,7 @@ struct bnx2x {
 #define USING_SINGLE_MSIX_FLAG         (1 << 20)
 #define BC_SUPPORTS_DCBX_MSG_NON_PMF   (1 << 21)
 #define IS_VF_FLAG                     (1 << 22)
+#define INTERRUPTS_ENABLED_FLAG                (1 << 23)
 
 #define BP_NOMCP(bp)                   ((bp)->flags & NO_MCP_FLAG)
 
@@ -1585,7 +1695,7 @@ struct bnx2x {
        struct mutex            cnic_mutex;
        struct bnx2x_vlan_mac_obj iscsi_l2_mac_obj;
 
-       /* Start index of the "special" (CNIC related) L2 cleints */
+       /* Start index of the "special" (CNIC related) L2 clients */
        u8                              cnic_base_cl_id;
 
        int                     dmae_ready;
@@ -1699,7 +1809,7 @@ struct bnx2x {
        /* operation indication for the sp_rtnl task */
        unsigned long                           sp_rtnl_state;
 
-       /* DCBX Negotation results */
+       /* DCBX Negotiation results */
        struct dcbx_features                    dcbx_local_feat;
        u32                                     dcbx_error;
 
@@ -1755,7 +1865,6 @@ extern int num_queues;
 #define FUNC_FLG_SPQ           0x0010
 #define FUNC_FLG_LEADING       0x0020  /* PF only */
 
-
 struct bnx2x_func_init_params {
        /* dma */
        dma_addr_t      fw_stat_map;    /* valid iff FUNC_FLG_STATS */
@@ -1853,9 +1962,6 @@ struct bnx2x_func_init_params {
 
 #define skip_queue(bp, idx)    (NO_FCOE(bp) && IS_FCOE_IDX(idx))
 
-
-
-
 /**
  * bnx2x_set_mac_one - configure a single MAC address
  *
@@ -1921,7 +2027,6 @@ u32 bnx2x_dmae_opcode(struct bnx2x *bp, u8 src_type, u8 dst_type,
 void bnx2x_prep_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae,
                               u8 src_type, u8 dst_type);
 int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae);
-void bnx2x_dp_dmae(struct bnx2x *bp, struct dmae_command *dmae, int msglvl);
 
 /* FLR related routines */
 u32 bnx2x_flr_clnup_poll_count(struct bnx2x *bp);
@@ -1937,6 +2042,8 @@ int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
 void bnx2x_update_coalesce(struct bnx2x *bp);
 int bnx2x_get_cur_phy_idx(struct bnx2x *bp);
 
+bool bnx2x_port_after_undi(struct bnx2x *bp);
+
 static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
                           int wait)
 {
@@ -1998,7 +2105,6 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id,
 #define UNLOAD_CLOSE                   1
 #define UNLOAD_RECOVERY                        2
 
-
 /* DMAE command defines */
 #define DMAE_TIMEOUT                   -1
 #define DMAE_PCI_ERROR                 -2      /* E2 and onward */
@@ -2062,7 +2168,8 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id,
 #define DMAE_LEN32_WR_MAX(bp)          (CHIP_IS_E1(bp) ? 0x400 : 0x2000)
 
 #define DMAE_COMP_VAL                  0x60d0d0ae /* E2 and on - upper bit
-                                                       indicates eror */
+                                                   * indicates error
+                                                   */
 
 #define MAX_DMAE_C_PER_PORT            8
 #define INIT_DMAE_C(bp)                        (BP_PORT(bp) * MAX_DMAE_C_PER_PORT + \
@@ -2100,7 +2207,6 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id,
 #define SP_DESC_CNT            (BCM_PAGE_SIZE / sizeof(struct eth_spe))
 #define MAX_SP_DESC_CNT                        (SP_DESC_CNT - 1)
 
-
 #define BNX2X_BTR                      4
 #define MAX_SPQ_PENDING                        8
 
@@ -2137,6 +2243,8 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id,
 #define ATTN_HARD_WIRED_MASK           0xff00
 #define ATTENTION_ID                   4
 
+#define IS_MF_STORAGE_ONLY(bp) (IS_MF_STORAGE_SD(bp) || \
+                                IS_MF_FCOE_AFEX(bp))
 
 /* stuff added to make the code fit 80Col */
 
@@ -2338,4 +2446,9 @@ enum {
 
 #define NUM_MACS       8
 
+enum bnx2x_pci_bus_speed {
+       BNX2X_PCI_LINK_SPEED_2500 = 2500,
+       BNX2X_PCI_LINK_SPEED_5000 = 5000,
+       BNX2X_PCI_LINK_SPEED_8000 = 8000
+};
 #endif /* bnx2x.h */
index 638e554..ec3aa1d 100644 (file)
@@ -24,6 +24,7 @@
 #include <net/tcp.h>
 #include <net/ipv6.h>
 #include <net/ip6_checksum.h>
+#include <net/ll_poll.h>
 #include <linux/prefetch.h>
 #include "bnx2x_cmn.h"
 #include "bnx2x_init.h"
@@ -124,7 +125,7 @@ static void bnx2x_shrink_eth_fp(struct bnx2x *bp, int delta)
        int i, cos, old_eth_num = BNX2X_NUM_ETH_QUEUES(bp);
 
        /* Queue pointer cannot be re-set on an fp-basis, as moving pointer
-        * backward along the array could cause memory to be overriden
+        * backward along the array could cause memory to be overridden
         */
        for (cos = 1; cos < bp->max_cos; cos++) {
                for (i = 0; i < old_eth_num - delta; i++) {
@@ -165,7 +166,6 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fp_txdata *txdata,
        dma_unmap_single(&bp->pdev->dev, BD_UNMAP_ADDR(tx_start_bd),
                         BD_UNMAP_LEN(tx_start_bd), DMA_TO_DEVICE);
 
-
        nbd = le16_to_cpu(tx_start_bd->nbd) - 1;
 #ifdef BNX2X_STOP_ON_ERROR
        if ((nbd - 1) > (MAX_SKB_FRAGS + 2)) {
@@ -259,7 +259,7 @@ int bnx2x_tx_int(struct bnx2x *bp, struct bnx2x_fp_txdata *txdata)
        smp_mb();
 
        if (unlikely(netif_tx_queue_stopped(txq))) {
-               /* Taking tx_lock() is needed to prevent reenabling the queue
+               /* Taking tx_lock() is needed to prevent re-enabling the queue
                 * while it's empty. This could have happen if rx_action() gets
                 * suspended in bnx2x_tx_int() after the condition before
                 * netif_tx_wake_queue(), while tx_action (bnx2x_start_xmit()):
@@ -572,7 +572,7 @@ static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp,
                        return err;
                }
 
-               /* Unmap the page as we r going to pass it to the stack */
+               /* Unmap the page as we're going to pass it to the stack */
                dma_unmap_page(&bp->pdev->dev,
                               dma_unmap_addr(&old_rx_pg, mapping),
                               SGE_PAGES, DMA_FROM_DEVICE);
@@ -733,7 +733,6 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp,
                        dev_kfree_skb_any(skb);
                }
 
-
                /* put new data in bin */
                rx_buf->data = new_data;
 
@@ -805,40 +804,32 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
 {
        struct bnx2x *bp = fp->bp;
        u16 bd_cons, bd_prod, bd_prod_fw, comp_ring_cons;
-       u16 hw_comp_cons, sw_comp_cons, sw_comp_prod;
+       u16 sw_comp_cons, sw_comp_prod;
        int rx_pkt = 0;
+       union eth_rx_cqe *cqe;
+       struct eth_fast_path_rx_cqe *cqe_fp;
 
 #ifdef BNX2X_STOP_ON_ERROR
        if (unlikely(bp->panic))
                return 0;
 #endif
 
-       /* CQ "next element" is of the size of the regular element,
-          that's why it's ok here */
-       hw_comp_cons = le16_to_cpu(*fp->rx_cons_sb);
-       if ((hw_comp_cons & MAX_RCQ_DESC_CNT) == MAX_RCQ_DESC_CNT)
-               hw_comp_cons++;
-
        bd_cons = fp->rx_bd_cons;
        bd_prod = fp->rx_bd_prod;
        bd_prod_fw = bd_prod;
        sw_comp_cons = fp->rx_comp_cons;
        sw_comp_prod = fp->rx_comp_prod;
 
-       /* Memory barrier necessary as speculative reads of the rx
-        * buffer can be ahead of the index in the status block
-        */
-       rmb();
+       comp_ring_cons = RCQ_BD(sw_comp_cons);
+       cqe = &fp->rx_comp_ring[comp_ring_cons];
+       cqe_fp = &cqe->fast_path_cqe;
 
        DP(NETIF_MSG_RX_STATUS,
-          "queue[%d]:  hw_comp_cons %u  sw_comp_cons %u\n",
-          fp->index, hw_comp_cons, sw_comp_cons);
+          "queue[%d]: sw_comp_cons %u\n", fp->index, sw_comp_cons);
 
-       while (sw_comp_cons != hw_comp_cons) {
+       while (BNX2X_IS_CQE_COMPLETED(cqe_fp)) {
                struct sw_rx_bd *rx_buf = NULL;
                struct sk_buff *skb;
-               union eth_rx_cqe *cqe;
-               struct eth_fast_path_rx_cqe *cqe_fp;
                u8 cqe_fp_flags;
                enum eth_rx_cqe_type cqe_fp_type;
                u16 len, pad, queue;
@@ -850,12 +841,9 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
                        return 0;
 #endif
 
-               comp_ring_cons = RCQ_BD(sw_comp_cons);
                bd_prod = RX_BD(bd_prod);
                bd_cons = RX_BD(bd_cons);
 
-               cqe = &fp->rx_comp_ring[comp_ring_cons];
-               cqe_fp = &cqe->fast_path_cqe;
                cqe_fp_flags = cqe_fp->type_error_flags;
                cqe_fp_type = cqe_fp_flags & ETH_FAST_PATH_RX_CQE_TYPE;
 
@@ -899,7 +887,6 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
                                                cqe_fp);
 
                                goto next_rx;
-
                        }
                        queue = cqe->end_agg_cqe.queue_index;
                        tpa_info = &fp->tpa_info[queue];
@@ -1002,9 +989,13 @@ reuse_rx:
                    PARSING_FLAGS_VLAN)
                        __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
                                               le16_to_cpu(cqe_fp->vlan_tag));
-               napi_gro_receive(&fp->napi, skb);
 
+               skb_mark_ll(skb, &fp->napi);
 
+               if (bnx2x_fp_ll_polling(fp))
+                       netif_receive_skb(skb);
+               else
+                       napi_gro_receive(&fp->napi, skb);
 next_rx:
                rx_buf->data = NULL;
 
@@ -1016,8 +1007,15 @@ next_cqe:
                sw_comp_prod = NEXT_RCQ_IDX(sw_comp_prod);
                sw_comp_cons = NEXT_RCQ_IDX(sw_comp_cons);
 
+               /* mark CQE as free */
+               BNX2X_SEED_CQE(cqe_fp);
+
                if (rx_pkt == budget)
                        break;
+
+               comp_ring_cons = RCQ_BD(sw_comp_cons);
+               cqe = &fp->rx_comp_ring[comp_ring_cons];
+               cqe_fp = &cqe->fast_path_cqe;
        } /* while */
 
        fp->rx_bd_cons = bd_cons;
@@ -1053,8 +1051,6 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie)
 #endif
 
        /* Handle Rx and Tx according to MSI-X vector */
-       prefetch(fp->rx_cons_sb);
-
        for_each_cos_in_tx_queue(fp, cos)
                prefetch(fp->txdata_ptr[cos]->tx_cons_sb);
 
@@ -1118,7 +1114,7 @@ static void bnx2x_fill_report_data(struct bnx2x *bp,
 
        memset(data, 0, sizeof(*data));
 
-       /* Fill the report data: efective line speed */
+       /* Fill the report data: effective line speed */
        data->line_speed = line_speed;
 
        /* Link is down */
@@ -1161,7 +1157,7 @@ void bnx2x_link_report(struct bnx2x *bp)
  *
  * @bp:                driver handle
  *
- * None atomic inmlementation.
+ * None atomic implementation.
  * Should be called under the phy_lock.
  */
 void __bnx2x_link_report(struct bnx2x *bp)
@@ -1304,7 +1300,7 @@ void bnx2x_init_rx_rings(struct bnx2x *bp)
                   "mtu %d  rx_buf_size %d\n", bp->dev->mtu, fp->rx_buf_size);
 
                if (!fp->disable_tpa) {
-                       /* Fill the per-aggregtion pool */
+                       /* Fill the per-aggregation pool */
                        for (i = 0; i < MAX_AGG_QS(bp); i++) {
                                struct bnx2x_agg_info *tpa_info =
                                        &fp->tpa_info[i];
@@ -1726,7 +1722,7 @@ static int bnx2x_req_irq(struct bnx2x *bp)
        return request_irq(irq, bnx2x_interrupt, flags, bp->dev->name, bp->dev);
 }
 
-int bnx2x_setup_irqs(struct bnx2x *bp)
+static int bnx2x_setup_irqs(struct bnx2x *bp)
 {
        int rc = 0;
        if (bp->flags & USING_MSIX_FLAG &&
@@ -1759,32 +1755,46 @@ static void bnx2x_napi_enable_cnic(struct bnx2x *bp)
 {
        int i;
 
-       for_each_rx_queue_cnic(bp, i)
+       for_each_rx_queue_cnic(bp, i) {
+               bnx2x_fp_init_lock(&bp->fp[i]);
                napi_enable(&bnx2x_fp(bp, i, napi));
+       }
 }
 
 static void bnx2x_napi_enable(struct bnx2x *bp)
 {
        int i;
 
-       for_each_eth_queue(bp, i)
+       for_each_eth_queue(bp, i) {
+               bnx2x_fp_init_lock(&bp->fp[i]);
                napi_enable(&bnx2x_fp(bp, i, napi));
+       }
 }
 
 static void bnx2x_napi_disable_cnic(struct bnx2x *bp)
 {
        int i;
 
-       for_each_rx_queue_cnic(bp, i)
+       local_bh_disable();
+       for_each_rx_queue_cnic(bp, i) {
                napi_disable(&bnx2x_fp(bp, i, napi));
+               while (!bnx2x_fp_lock_napi(&bp->fp[i]))
+                       mdelay(1);
+       }
+       local_bh_enable();
 }
 
 static void bnx2x_napi_disable(struct bnx2x *bp)
 {
        int i;
 
-       for_each_eth_queue(bp, i)
+       local_bh_disable();
+       for_each_eth_queue(bp, i) {
                napi_disable(&bnx2x_fp(bp, i, napi));
+               while (!bnx2x_fp_lock_napi(&bp->fp[i]))
+                       mdelay(1);
+       }
+       local_bh_enable();
 }
 
 void bnx2x_netif_start(struct bnx2x *bp)
@@ -1829,7 +1839,7 @@ u16 bnx2x_select_queue(struct net_device *dev, struct sk_buff *skb)
        }
 
        /* select a non-FCoE queue */
-       return __skb_tx_hash(dev, skb, BNX2X_NUM_ETH_QUEUES(bp));
+       return __netdev_pick_tx(dev, skb) % BNX2X_NUM_ETH_QUEUES(bp);
 }
 
 void bnx2x_set_num_queues(struct bnx2x *bp)
@@ -1862,7 +1872,7 @@ void bnx2x_set_num_queues(struct bnx2x *bp)
  *
  * If the actual number of Tx queues (for each CoS) is less than 16 then there
  * will be the holes at the end of each group of 16 ETh L2 indices (0..15,
- * 16..31,...) with indicies that are not coupled with any real Tx queue.
+ * 16..31,...) with indices that are not coupled with any real Tx queue.
  *
  * The proper configuration of skb->queue_mapping is handled by
  * bnx2x_select_queue() and __skb_tx_hash().
@@ -1924,7 +1934,7 @@ static void bnx2x_set_rx_buf_size(struct bnx2x *bp)
                                  ETH_OVREHEAD +
                                  mtu +
                                  BNX2X_FW_RX_ALIGN_END;
-               /* Note : rx_buf_size doesnt take into account NET_SKB_PAD */
+               /* Note : rx_buf_size doesn't take into account NET_SKB_PAD */
                if (fp->rx_buf_size + NET_SKB_PAD <= PAGE_SIZE)
                        fp->rx_frag_size = fp->rx_buf_size + NET_SKB_PAD;
                else
@@ -1937,7 +1947,7 @@ static int bnx2x_init_rss_pf(struct bnx2x *bp)
        int i;
        u8 num_eth_queues = BNX2X_NUM_ETH_QUEUES(bp);
 
-       /* Prepare the initial contents fo the indirection table if RSS is
+       /* Prepare the initial contents for the indirection table if RSS is
         * enabled
         */
        for (i = 0; i < sizeof(bp->rss_conf_obj.ind_table); i++)
@@ -2015,7 +2025,7 @@ static int bnx2x_init_hw(struct bnx2x *bp, u32 load_code)
 
 /*
  * Cleans the object that have internal lists without sending
- * ramrods. Should be run when interrutps are disabled.
+ * ramrods. Should be run when interrupts are disabled.
  */
 void bnx2x_squeeze_objects(struct bnx2x *bp)
 {
@@ -2166,10 +2176,10 @@ static int bnx2x_alloc_fw_stats_mem(struct bnx2x *bp)
        bp->fw_stats_data_mapping = bp->fw_stats_mapping +
                bp->fw_stats_req_sz;
 
-       DP(BNX2X_MSG_SP, "statistics request base address set to %x %x",
+       DP(BNX2X_MSG_SP, "statistics request base address set to %x %x\n",
           U64_HI(bp->fw_stats_req_mapping),
           U64_LO(bp->fw_stats_req_mapping));
-       DP(BNX2X_MSG_SP, "statistics data base address set to %x %x",
+       DP(BNX2X_MSG_SP, "statistics data base address set to %x %x\n",
           U64_HI(bp->fw_stats_data_mapping),
           U64_LO(bp->fw_stats_data_mapping));
        return 0;
@@ -2183,6 +2193,8 @@ alloc_mem_err:
 /* send load request to mcp and analyze response */
 static int bnx2x_nic_load_request(struct bnx2x *bp, u32 *load_code)
 {
+       u32 param;
+
        /* init fw_seq */
        bp->fw_seq =
                (SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) &
@@ -2195,9 +2207,13 @@ static int bnx2x_nic_load_request(struct bnx2x *bp, u32 *load_code)
                 DRV_PULSE_SEQ_MASK);
        BNX2X_DEV_INFO("drv_pulse 0x%x\n", bp->fw_drv_pulse_wr_seq);
 
+       param = DRV_MSG_CODE_LOAD_REQ_WITH_LFA;
+
+       if (IS_MF_SD(bp) && bnx2x_port_after_undi(bp))
+               param |= DRV_MSG_CODE_LOAD_REQ_FORCE_LFA;
+
        /* load request */
-       (*load_code) = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ,
-                                       DRV_MSG_CODE_LOAD_REQ_WITH_LFA);
+       (*load_code) = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ, param);
 
        /* if mcp fails to respond we must abort */
        if (!(*load_code)) {
@@ -2238,7 +2254,7 @@ int bnx2x_nic_load_analyze_req(struct bnx2x *bp, u32 load_code)
 
                /* abort nic load if version mismatch */
                if (my_fw != loaded_fw) {
-                       BNX2X_ERR("bnx2x with FW %x was already loaded which mismatches my %x FW. aborting\n",
+                       BNX2X_ERR("bnx2x with FW %x was already loaded which mismatches my %x FW. Aborting\n",
                                  loaded_fw, my_fw);
                        return -EBUSY;
                }
@@ -2316,10 +2332,10 @@ static void bnx2x_nic_load_afex_dcc(struct bnx2x *bp, int load_code)
 static void bnx2x_bz_fp(struct bnx2x *bp, int index)
 {
        struct bnx2x_fastpath *fp = &bp->fp[index];
-
        int cos;
        struct napi_struct orig_napi = fp->napi;
        struct bnx2x_agg_info *orig_tpa_info = fp->tpa_info;
+
        /* bzero bnx2x_fastpath contents */
        if (fp->tpa_info)
                memset(fp->tpa_info, 0, ETH_MAX_AGGREGATION_QUEUES_E1H_E2 *
@@ -2345,8 +2361,7 @@ static void bnx2x_bz_fp(struct bnx2x *bp, int index)
                        fp->txdata_ptr[cos] = &bp->bnx2x_txq[cos *
                                BNX2X_NUM_ETH_QUEUES(bp) + index];
 
-       /*
-        * set the tpa flag for each queue. The tpa flag determines the queue
+       /* set the tpa flag for each queue. The tpa flag determines the queue
         * minimal size so it must be set prior to queue memory allocation
         */
        fp->disable_tpa = !(bp->flags & TPA_ENABLE_FLAG ||
@@ -2429,7 +2444,6 @@ int bnx2x_load_cnic(struct bnx2x *bp)
        if (bp->state == BNX2X_STATE_OPEN)
                bnx2x_cnic_notify(bp, CNIC_CTL_START_CMD);
 
-
        DP(NETIF_MSG_IFUP, "Ending successfully CNIC-related load\n");
 
        return 0;
@@ -2472,6 +2486,7 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
 
        bp->state = BNX2X_STATE_OPENING_WAIT4_LOAD;
 
+       /* zero the structure w/o any lock, before SP handler is initialized */
        memset(&bp->last_reported_link, 0, sizeof(bp->last_reported_link));
        __set_bit(BNX2X_LINK_REPORT_LINK_DOWN,
                &bp->last_reported_link.link_report_flags);
@@ -2536,8 +2551,8 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
        }
 
        /* configure multi cos mappings in kernel.
-        * this configuration may be overriden by a multi class queue discipline
-        * or by a dcbx negotiation result.
+        * this configuration may be overridden by a multi class queue
+        * discipline or by a dcbx negotiation result.
         */
        bnx2x_setup_tc(bp->dev, bp->max_cos);
 
@@ -2696,7 +2711,7 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
        /* Start the Tx */
        switch (load_mode) {
        case LOAD_NORMAL:
-               /* Tx queue should be only reenabled */
+               /* Tx queue should be only re-enabled */
                netif_tx_wake_all_queues(bp->dev);
                break;
 
@@ -2841,7 +2856,7 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link)
        }
 
        /* Nothing to do during unload if previous bnx2x_nic_load()
-        * have not completed succesfully - all resourses are released.
+        * have not completed successfully - all resources are released.
         *
         * we can get here only after unsuccessful ndo_* callback, during which
         * dev->IFF_UP flag is still on.
@@ -2856,6 +2871,9 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link)
        bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT;
        smp_mb();
 
+       /* indicate to VFs that the PF is going down */
+       bnx2x_iov_channel_down(bp);
+
        if (CNIC_LOADED(bp))
                bnx2x_cnic_notify(bp, CNIC_CTL_STOP_CMD);
 
@@ -2890,10 +2908,9 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link)
                /* Send the UNLOAD_REQUEST to the MCP */
                bnx2x_send_unload_req(bp, unload_mode);
 
-               /*
-                * Prevent transactions to host from the functions on the
+               /* Prevent transactions to host from the functions on the
                 * engine that doesn't reset global blocks in case of global
-                * attention once gloabl blocks are reset and gates are opened
+                * attention once global blocks are reset and gates are opened
                 * (the engine which leader will perform the recovery
                 * last).
                 */
@@ -2914,7 +2931,7 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link)
        }
 
        /*
-        * At this stage no more interrupts will arrive so we may safly clean
+        * At this stage no more interrupts will arrive so we may safely clean
         * the queueable objects here in case they failed to get cleaned so far.
         */
        if (IS_PF(bp))
@@ -2955,7 +2972,6 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link)
                        bnx2x_set_reset_global(bp);
        }
 
-
        /* The last driver must disable a "close the gate" if there is no
         * parity attention or "process kill" pending.
         */
@@ -3040,6 +3056,8 @@ int bnx2x_poll(struct napi_struct *napi, int budget)
                        return 0;
                }
 #endif
+               if (!bnx2x_fp_lock_napi(fp))
+                       return work_done;
 
                for_each_cos_in_tx_queue(fp, cos)
                        if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos]))
@@ -3049,12 +3067,15 @@ int bnx2x_poll(struct napi_struct *napi, int budget)
                        work_done += bnx2x_rx_int(fp, budget - work_done);
 
                        /* must not complete if we consumed full budget */
-                       if (work_done >= budget)
+                       if (work_done >= budget) {
+                               bnx2x_fp_unlock_napi(fp);
                                break;
+                       }
                }
 
                /* Fall out from the NAPI loop if needed */
-               if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) {
+               if (!bnx2x_fp_unlock_napi(fp) &&
+                   !(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) {
 
                        /* No need to update SB for FCoE L2 ring as long as
                         * it's connected to the default SB and the SB
@@ -3096,6 +3117,32 @@ int bnx2x_poll(struct napi_struct *napi, int budget)
        return work_done;
 }
 
+#ifdef CONFIG_NET_LL_RX_POLL
+/* must be called with local_bh_disable()d */
+int bnx2x_low_latency_recv(struct napi_struct *napi)
+{
+       struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath,
+                                                napi);
+       struct bnx2x *bp = fp->bp;
+       int found = 0;
+
+       if ((bp->state == BNX2X_STATE_CLOSED) ||
+           (bp->state == BNX2X_STATE_ERROR) ||
+           (bp->flags & (TPA_ENABLE_FLAG | GRO_ENABLE_FLAG)))
+               return LL_FLUSH_FAILED;
+
+       if (!bnx2x_fp_lock_poll(fp))
+               return LL_FLUSH_BUSY;
+
+       if (bnx2x_has_rx_work(fp))
+               found = bnx2x_rx_int(fp, 4);
+
+       bnx2x_fp_unlock_poll(fp);
+
+       return found;
+}
+#endif
+
 /* we split the first BD into headers and data BDs
  * to ease the pain of our fellow microcode engineers
  * we use one mapping for both BDs
@@ -3496,9 +3543,12 @@ static void bnx2x_update_pbds_gso_enc(struct sk_buff *skb,
        /* outer IP header info */
        if (xmit_type & XMIT_CSUM_V4) {
                struct iphdr *iph = ip_hdr(skb);
+               u16 csum = (__force u16)(~iph->check) -
+                          (__force u16)iph->tot_len -
+                          (__force u16)iph->frag_off;
+
                pbd2->fw_ip_csum_wo_len_flags_frag =
-                       bswab16(csum_fold((~iph->check) -
-                                         iph->tot_len - iph->frag_off));
+                       bswab16(csum_fold((__force __wsum)csum));
        } else {
                pbd2->fw_ip_hdr_to_payload_w =
                        hlen_w - ((sizeof(struct ipv6hdr)) >> 1);
@@ -3586,7 +3636,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
        DP(NETIF_MSG_TX_QUEUED, "indices: txq %d, fp %d, txdata %d\n",
           txq_index, fp_index, txdata_index); */
 
-       /* enable this debug print to view the tranmission details
+       /* enable this debug print to view the transmission details
        DP(NETIF_MSG_TX_QUEUED,
           "transmitting packet cid %d fp index %d txdata_index %d tx_data ptr %p fp pointer %p\n",
           txdata->cid, fp_index, txdata_index, txdata, fp); */
@@ -3968,7 +4018,7 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc)
        /* setup tc must be called under rtnl lock */
        ASSERT_RTNL();
 
-       /* no traffic classes requested. aborting */
+       /* no traffic classes requested. Aborting */
        if (!num_tc) {
                netdev_reset_tc(dev);
                return 0;
@@ -3976,7 +4026,7 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc)
 
        /* requested to support too many traffic classes */
        if (num_tc > bp->max_cos) {
-               BNX2X_ERR("support for too many traffic classes requested: %d. max supported is %d\n",
+               BNX2X_ERR("support for too many traffic classes requested: %d. Max supported is %d\n",
                          num_tc, bp->max_cos);
                return -EINVAL;
        }
@@ -3995,8 +4045,7 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc)
                   prio, bp->prio_to_cos[prio]);
        }
 
-
-       /* Use this configuration to diffrentiate tc0 from other COSes
+       /* Use this configuration to differentiate tc0 from other COSes
           This can be used for ets or pfc, and save the effort of setting
           up a multio class queue disc or negotiating DCBX with a switch
        netdev_set_prio_tc_map(dev, 0, 0);
@@ -4288,10 +4337,11 @@ static int bnx2x_alloc_fp_mem_at(struct bnx2x *bp, int index)
                                &bnx2x_fp(bp, index, rx_desc_mapping),
                                sizeof(struct eth_rx_bd) * NUM_RX_BD);
 
-               BNX2X_PCI_ALLOC(bnx2x_fp(bp, index, rx_comp_ring),
-                               &bnx2x_fp(bp, index, rx_comp_mapping),
-                               sizeof(struct eth_fast_path_rx_cqe) *
-                               NUM_RCQ_BD);
+               /* Seed all CQEs by 1s */
+               BNX2X_PCI_FALLOC(bnx2x_fp(bp, index, rx_comp_ring),
+                                &bnx2x_fp(bp, index, rx_comp_mapping),
+                                sizeof(struct eth_fast_path_rx_cqe) *
+                                NUM_RCQ_BD);
 
                /* SGE ring */
                BNX2X_ALLOC(bnx2x_fp(bp, index, rx_page_ring),
@@ -4472,7 +4522,6 @@ int bnx2x_alloc_mem_bp(struct bnx2x *bp)
 alloc_err:
        bnx2x_free_mem_bp(bp);
        return -ENOMEM;
-
 }
 
 int bnx2x_reload_if_running(struct net_device *dev)
@@ -4514,7 +4563,6 @@ int bnx2x_get_cur_phy_idx(struct bnx2x *bp)
        }
 
        return sel_phy_idx;
-
 }
 int bnx2x_get_link_cfg_idx(struct bnx2x *bp)
 {
@@ -4602,6 +4650,7 @@ int bnx2x_set_features(struct net_device *dev, netdev_features_t features)
 {
        struct bnx2x *bp = netdev_priv(dev);
        u32 flags = bp->flags;
+       u32 changes;
        bool bnx2x_reload = false;
 
        if (features & NETIF_F_LRO)
@@ -4626,10 +4675,16 @@ int bnx2x_set_features(struct net_device *dev, netdev_features_t features)
                }
        }
 
-       if (flags ^ bp->flags) {
-               bp->flags = flags;
+       changes = flags ^ bp->flags;
+
+       /* if GRO is changed while LRO is enabled, don't force a reload */
+       if ((changes & GRO_ENABLE_FLAG) && (flags & TPA_ENABLE_FLAG))
+               changes &= ~GRO_ENABLE_FLAG;
+
+       if (changes)
                bnx2x_reload = true;
-       }
+
+       bp->flags = flags;
 
        if (bnx2x_reload) {
                if (bp->recovery_state == BNX2X_RECOVERY_DONE)
@@ -4724,7 +4779,6 @@ int bnx2x_resume(struct pci_dev *pdev)
        return rc;
 }
 
-
 void bnx2x_set_ctx_validation(struct bnx2x *bp, struct eth_context *cxt,
                              u32 cid)
 {
@@ -4742,7 +4796,6 @@ static void storm_memset_hc_timeout(struct bnx2x *bp, u8 port,
                                    u8 fw_sb_id, u8 sb_index,
                                    u8 ticks)
 {
-
        u32 addr = BAR_CSTRORM_INTMEM +
                   CSTORM_STATUS_BLOCK_DATA_TIMEOUT_OFFSET(fw_sb_id, sb_index);
        REG_WR8(bp, addr, ticks);
index 151675d..c07a6d0 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 
-
 #include "bnx2x.h"
 #include "bnx2x_sriov.h"
 
@@ -50,13 +49,25 @@ extern int int_mode;
                } \
        } while (0)
 
-#define BNX2X_PCI_ALLOC(x, y, size)                            \
-do {                                                           \
-       x = dma_alloc_coherent(&bp->pdev->dev, size, y,         \
-                              GFP_KERNEL | __GFP_ZERO);        \
-       if (x == NULL)                                          \
-               goto alloc_mem_err;                             \
-} while (0)
+#define BNX2X_PCI_ALLOC(x, y, size) \
+       do { \
+               x = dma_alloc_coherent(&bp->pdev->dev, size, y, \
+                                      GFP_KERNEL | __GFP_ZERO); \
+               if (x == NULL) \
+                       goto alloc_mem_err; \
+               DP(NETIF_MSG_HW, "BNX2X_PCI_ALLOC: Physical %Lx Virtual %p\n", \
+                  (unsigned long long)(*y), x); \
+       } while (0)
+
+#define BNX2X_PCI_FALLOC(x, y, size) \
+       do { \
+               x = dma_alloc_coherent(&bp->pdev->dev, size, y, GFP_KERNEL); \
+               if (x == NULL) \
+                       goto alloc_mem_err; \
+               memset((void *)x, 0xFFFFFFFF, size); \
+               DP(NETIF_MSG_HW, "BNX2X_PCI_FALLOC: Physical %Lx Virtual %p\n",\
+                  (unsigned long long)(*y), x); \
+       } while (0)
 
 #define BNX2X_ALLOC(x, size) \
        do { \
@@ -494,9 +505,6 @@ void bnx2x_update_max_mf_config(struct bnx2x *bp, u32 value);
 /* Error handling */
 void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl);
 
-/* validate currect fw is loaded */
-bool bnx2x_test_firmware_version(struct bnx2x *bp, bool is_err);
-
 /* dev_close main block */
 int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link);
 
@@ -606,6 +614,13 @@ int bnx2x_enable_msi(struct bnx2x *bp);
  */
 int bnx2x_poll(struct napi_struct *napi, int budget);
 
+/**
+ * bnx2x_low_latency_recv - LL callback
+ *
+ * @napi:      napi structure
+ */
+int bnx2x_low_latency_recv(struct napi_struct *napi);
+
 /**
  * bnx2x_alloc_mem_bp - allocate memories outsize main driver structure
  *
@@ -800,16 +815,18 @@ static inline bool bnx2x_has_tx_work(struct bnx2x_fastpath *fp)
        return false;
 }
 
+#define BNX2X_IS_CQE_COMPLETED(cqe_fp) (cqe_fp->marker == 0x0)
+#define BNX2X_SEED_CQE(cqe_fp) (cqe_fp->marker = 0xFFFFFFFF)
 static inline int bnx2x_has_rx_work(struct bnx2x_fastpath *fp)
 {
-       u16 rx_cons_sb;
+       u16 cons;
+       union eth_rx_cqe *cqe;
+       struct eth_fast_path_rx_cqe *cqe_fp;
 
-       /* Tell compiler that status block fields can change */
-       barrier();
-       rx_cons_sb = le16_to_cpu(*fp->rx_cons_sb);
-       if ((rx_cons_sb & MAX_RCQ_DESC_CNT) == MAX_RCQ_DESC_CNT)
-               rx_cons_sb++;
-       return (fp->rx_comp_cons != rx_cons_sb);
+       cons = RCQ_BD(fp->rx_comp_cons);
+       cqe = &fp->rx_comp_ring[cons];
+       cqe_fp = &cqe->fast_path_cqe;
+       return BNX2X_IS_CQE_COMPLETED(cqe_fp);
 }
 
 /**
@@ -848,9 +865,11 @@ static inline void bnx2x_add_all_napi_cnic(struct bnx2x *bp)
        int i;
 
        /* Add NAPI objects */
-       for_each_rx_queue_cnic(bp, i)
+       for_each_rx_queue_cnic(bp, i) {
                netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi),
                               bnx2x_poll, NAPI_POLL_WEIGHT);
+               napi_hash_add(&bnx2x_fp(bp, i, napi));
+       }
 }
 
 static inline void bnx2x_add_all_napi(struct bnx2x *bp)
@@ -858,25 +877,31 @@ static inline void bnx2x_add_all_napi(struct bnx2x *bp)
        int i;
 
        /* Add NAPI objects */
-       for_each_eth_queue(bp, i)
+       for_each_eth_queue(bp, i) {
                netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi),
                               bnx2x_poll, NAPI_POLL_WEIGHT);
+               napi_hash_add(&bnx2x_fp(bp, i, napi));
+       }
 }
 
 static inline void bnx2x_del_all_napi_cnic(struct bnx2x *bp)
 {
        int i;
 
-       for_each_rx_queue_cnic(bp, i)
+       for_each_rx_queue_cnic(bp, i) {
+               napi_hash_del(&bnx2x_fp(bp, i, napi));
                netif_napi_del(&bnx2x_fp(bp, i, napi));
+       }
 }
 
 static inline void bnx2x_del_all_napi(struct bnx2x *bp)
 {
        int i;
 
-       for_each_eth_queue(bp, i)
+       for_each_eth_queue(bp, i) {
+               napi_hash_del(&bnx2x_fp(bp, i, napi));
                netif_napi_del(&bnx2x_fp(bp, i, napi));
+       }
 }
 
 int bnx2x_set_int_mode(struct bnx2x *bp);
@@ -1171,7 +1196,6 @@ static inline u8 bnx2x_cnic_eth_cl_id(struct bnx2x *bp, u8 cl_idx)
 
 static inline u8 bnx2x_cnic_fw_sb_id(struct bnx2x *bp)
 {
-
        /* the 'first' id is allocated for the cnic */
        return bp->base_fw_ndsb;
 }
@@ -1181,7 +1205,6 @@ static inline u8 bnx2x_cnic_igu_sb_id(struct bnx2x *bp)
        return bp->igu_base_sb;
 }
 
-
 static inline void bnx2x_init_fcoe_fp(struct bnx2x *bp)
 {
        struct bnx2x_fastpath *fp = bnx2x_fcoe_fp(bp);
@@ -1334,8 +1357,8 @@ static inline bool bnx2x_mtu_allows_gro(int mtu)
        int fpp = SGE_PAGE_SIZE / (mtu - ETH_MAX_TPA_HEADER_SIZE);
 
        /*
-        * 1. number of frags should not grow above MAX_SKB_FRAGS
-        * 2. frag must fit the page
+        * 1. Number of frags should not grow above MAX_SKB_FRAGS
+        * 2. Frag must fit the page
         */
        return mtu <= SGE_PAGE_SIZE && (U_ETH_SGL_SIZE * fpp) <= MAX_SKB_FRAGS;
 }
index 4b077a7..0c94df4 100644 (file)
@@ -253,7 +253,6 @@ static void bnx2x_dcbx_get_ets_feature(struct bnx2x *bp,
 
        memset(&pg_help_data, 0, sizeof(struct pg_help_data));
 
-
        if (GET_FLAGS(error, DCBX_LOCAL_ETS_ERROR))
                DP(BNX2X_MSG_DCB, "DCBX_LOCAL_ETS_ERROR\n");
 
@@ -298,7 +297,6 @@ static void bnx2x_dcbx_get_ets_feature(struct bnx2x *bp,
 static void  bnx2x_dcbx_get_pfc_feature(struct bnx2x *bp,
                                        struct dcbx_pfc_feature *pfc, u32 error)
 {
-
        if (GET_FLAGS(error, DCBX_LOCAL_PFC_ERROR))
                DP(BNX2X_MSG_DCB, "DCBX_LOCAL_PFC_ERROR\n");
 
@@ -367,7 +365,6 @@ static int bnx2x_dcbx_read_mib(struct bnx2x *bp,
        struct lldp_remote_mib *remote_mib ;
        struct lldp_local_mib  *local_mib;
 
-
        switch (read_mib_type) {
        case DCBX_READ_LOCAL_MIB:
                mib_size = sizeof(struct lldp_local_mib);
@@ -629,7 +626,6 @@ static int bnx2x_dcbx_read_shmem_neg_results(struct bnx2x *bp)
        return 0;
 }
 
-
 #ifdef BCM_DCBNL
 static inline
 u8 bnx2x_dcbx_dcbnl_app_up(struct dcbx_app_priority_entry *ent)
@@ -691,7 +687,7 @@ static inline void bnx2x_dcbx_update_tc_mapping(struct bnx2x *bp)
        }
 
        /* setup tc must be called under rtnl lock, but we can't take it here
-        * as we are handling an attetntion on a work queue which must be
+        * as we are handling an attention on a work queue which must be
         * flushed at some rtnl-locked contexts (e.g. if down)
         */
        if (!test_and_set_bit(BNX2X_SP_RTNL_SETUP_TC, &bp->sp_rtnl_state))
@@ -711,7 +707,7 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state)
                         */
                        bnx2x_dcbnl_update_applist(bp, true);
 
-                       /* Read rmeote mib if dcbx is in the FW */
+                       /* Read remote mib if dcbx is in the FW */
                        if (bnx2x_dcbx_read_shmem_remote_mib(bp))
                                return;
 #endif
@@ -742,7 +738,7 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state)
                        bnx2x_dcbx_update_tc_mapping(bp);
 
                        /*
-                        * allow other funtions to update their netdevices
+                        * allow other functions to update their netdevices
                         * accordingly
                         */
                        if (IS_MF(bp))
@@ -864,7 +860,7 @@ static void bnx2x_dcbx_admin_mib_updated_params(struct bnx2x *bp,
                           i, DCBX_PRI_PG_GET(af->ets.pri_pg_tbl, i));
                }
 
-               /*For IEEE admin_recommendation_bw_precentage
+               /*For IEEE admin_recommendation_bw_percentage
                 *For IEEE admin_recommendation_ets_pg */
                af->pfc.pri_en_bitmap = (u8)dp->admin_pfc_bitmap;
                for (i = 0; i < DCBX_CONFIG_MAX_APP_PROTOCOL; i++) {
@@ -896,13 +892,11 @@ static void bnx2x_dcbx_admin_mib_updated_params(struct bnx2x *bp,
                }
 
                af->app.default_pri = (u8)dp->admin_default_priority;
-
        }
 
        /* Write the data. */
        bnx2x_write_data(bp, (u32 *)&admin_mib, offset,
                         sizeof(struct lldp_admin_mib));
-
 }
 
 void bnx2x_dcbx_set_state(struct bnx2x *bp, bool dcb_on, u32 dcbx_enabled)
@@ -1076,7 +1070,7 @@ static void bnx2x_dcbx_get_num_pg_traf_type(struct bnx2x *bp,
        bool pg_found  = false;
        u32 i, traf_type, add_traf_type, add_pg;
        u32 *ttp = bp->dcbx_port_params.app.traffic_type_priority;
-       struct pg_entry_help_data *data = help_data->data; /*shotcut*/
+       struct pg_entry_help_data *data = help_data->data; /*shortcut*/
 
        /* Set to invalid */
        for (i = 0; i < LLFC_DRIVER_TRAFFIC_TYPE_MAX; i++)
@@ -1172,7 +1166,8 @@ static void bnx2x_dcbx_separate_pauseable_from_non(struct bnx2x *bp,
                                DCBX_PG_BW_GET(ets->pg_bw_tbl, pg_entry));
                else
                        /* If we join a group and one is strict
-                        * than the bw rulls */
+                        * than the bw rules
+                        */
                        cos_data->data[entry].strict =
                                                BNX2X_DCBX_STRICT_COS_HIGHEST;
        }
@@ -1181,7 +1176,6 @@ static void bnx2x_dcbx_separate_pauseable_from_non(struct bnx2x *bp,
                BNX2X_ERR("dcbx error: Both groups must have priorities\n");
 }
 
-
 #ifndef POWER_OF_2
 #define POWER_OF_2(x)  ((0 != x) && (0 == (x & (x-1))))
 #endif
@@ -1284,7 +1278,7 @@ static void bnx2x_dcbx_2cos_limit_cee_single_pg_to_cos_params(struct bnx2x *bp,
                } else {
                        /* If there are only pauseable priorities or
                         * only non-pauseable,* the lower priorities go
-                        * to the first queue and the higherpriorities go
+                        * to the first queue and the higher priorities go
                         * to the second queue.
                         */
                        cos_data->data[0].pausable =
@@ -1484,7 +1478,7 @@ static void bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params(
                 * queue and one priority goes to the second queue.
                 *
                 * We will join this two cases:
-                * if one is BW limited it will go to the secoend queue
+                * if one is BW limited it will go to the second queue
                 * otherwise the last priority will get it
                 */
 
@@ -1504,7 +1498,8 @@ static void bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params(
                                    false == b_found_strict)
                                        /* last entry will be handled separately
                                         * If no priority is strict than last
-                                        * enty goes to last queue.*/
+                                        * entry goes to last queue.
+                                        */
                                        entry = 1;
                                cos_data->data[entry].pri_join_mask |=
                                                                pri_tested;
@@ -1516,7 +1511,8 @@ static void bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params(
                                b_found_strict = true;
                                cos_data->data[1].pri_join_mask |= pri_tested;
                                /* If we join a group and one is strict
-                                * than the bw rulls */
+                                * than the bw rules
+                                */
                                cos_data->data[1].strict =
                                        BNX2X_DCBX_STRICT_COS_HIGHEST;
                        }
@@ -1524,7 +1520,6 @@ static void bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params(
        }
 }
 
-
 static void bnx2x_dcbx_2cos_limit_cee_fill_cos_params(struct bnx2x *bp,
                                       struct pg_help_data *help_data,
                                       struct dcbx_ets_feature *ets,
@@ -1533,7 +1528,6 @@ static void bnx2x_dcbx_2cos_limit_cee_fill_cos_params(struct bnx2x *bp,
                                       u32 pri_join_mask,
                                       u8 num_of_dif_pri)
 {
-
        /* default E2 settings */
        cos_data->num_of_cos = DCBX_COS_MAX_NUM_E2;
 
@@ -1629,7 +1623,6 @@ static u8 bnx2x_dcbx_cee_fill_strict_pri(struct bnx2x *bp,
                                         u8 num_spread_of_entries,
                                         u8 strict_app_pris)
 {
-
        if (bnx2x_dcbx_spread_strict_pri(bp, cos_data, entry,
                                         num_spread_of_entries,
                                         strict_app_pris)) {
@@ -1848,7 +1841,7 @@ static void bnx2x_dcbx_fw_struct(struct bnx2x *bp,
 
 void bnx2x_dcbx_pmf_update(struct bnx2x *bp)
 {
-       /* if we need to syncronize DCBX result from prev PMF
+       /* if we need to synchronize DCBX result from prev PMF
         * read it from shmem and update bp and netdev accordingly
         */
        if (SHMEM2_HAS(bp, drv_flags) &&
@@ -1876,7 +1869,6 @@ void bnx2x_dcbx_pmf_update(struct bnx2x *bp)
                 * dcbx negotiation.
                 */
                bnx2x_dcbx_update_tc_mapping(bp);
-
        }
 }
 
@@ -1943,14 +1935,14 @@ static void bnx2x_dcbnl_set_pg_tccfg_tx(struct net_device *netdev, int prio,
                return;
 
        /**
-        * bw_pct ingnored -    band-width percentage devision between user
+        * bw_pct ignored -     band-width percentage devision between user
         *                      priorities within the same group is not
         *                      standard and hence not supported
         *
-        * prio_type igonred -  priority levels within the same group are not
+        * prio_type ignored -  priority levels within the same group are not
         *                      standard and hence are not supported. According
         *                      to the standard pgid 15 is dedicated to strict
-        *                      prioirty traffic (on the port level).
+        *                      priority traffic (on the port level).
         *
         * up_map ignored
         */
@@ -1995,14 +1987,14 @@ static void bnx2x_dcbnl_get_pg_tccfg_tx(struct net_device *netdev, int prio,
        DP(BNX2X_MSG_DCB, "prio = %d\n", prio);
 
        /**
-        * bw_pct ingnored -    band-width percentage devision between user
+        * bw_pct ignored -     band-width percentage devision between user
         *                      priorities within the same group is not
         *                      standard and hence not supported
         *
-        * prio_type igonred -  priority levels within the same group are not
+        * prio_type ignored -  priority levels within the same group are not
         *                      standard and hence are not supported. According
         *                      to the standard pgid 15 is dedicated to strict
-        *                      prioirty traffic (on the port level).
+        *                      priority traffic (on the port level).
         *
         * up_map ignored
         */
@@ -2389,7 +2381,7 @@ static u8 bnx2x_dcbnl_get_featcfg(struct net_device *netdev, int featid,
                                *flags |= DCB_FEATCFG_ERROR;
                        break;
                default:
-                       BNX2X_ERR("Non valid featrue-ID\n");
+                       BNX2X_ERR("Non valid feature-ID\n");
                        rval = 1;
                        break;
                }
@@ -2430,7 +2422,7 @@ static u8 bnx2x_dcbnl_set_featcfg(struct net_device *netdev, int featid,
                                flags & DCB_FEATCFG_WILLING ? 1 : 0;
                        break;
                default:
-                       BNX2X_ERR("Non valid featrue-ID\n");
+                       BNX2X_ERR("Non valid feature-ID\n");
                        rval = 1;
                        break;
                }
index d153f44..125bd1b 100644 (file)
@@ -134,8 +134,6 @@ enum {
 #define PFC_BRB1_REG_HIGH_LLFC_LOW_THRESHOLD                   130
 #define PFC_BRB1_REG_HIGH_LLFC_HIGH_THRESHOLD                  170
 
-
-
 struct cos_entry_help_data {
        u32                     pri_join_mask;
        u32                     cos_bw;
@@ -170,7 +168,6 @@ struct cos_help_data {
                        (!(IS_DCBX_PFC_PRI_ONLY_NON_PAUSE((bp), (pg_pri)) || \
                         IS_DCBX_PFC_PRI_ONLY_PAUSE((bp), (pg_pri))))
 
-
 struct pg_entry_help_data {
        u8      num_of_dif_pri;
        u8      pg;
index bff5e33..12eb4ba 100644 (file)
  * consent.
  */
 
-
-/* This struct holds a signature to ensure the dump returned from the driver
- * match the meta data file inserted to grc_dump.tcl
- * The signature is time stamp, diag version and grc_dump version
- */
-
 #ifndef BNX2X_DUMP_H
 #define BNX2X_DUMP_H
 
@@ -28,7 +22,6 @@
 #define DRV_DUMP_USTORM_WAITP_ADDRESS    0x338a80
 #define DRV_DUMP_CSTORM_WAITP_ADDRESS    0x238a80
 
-
 /* Possible Chips */
 #define DUMP_CHIP_E1 1
 #define DUMP_CHIP_E1H 2
index ce1a916..c5f2251 100644 (file)
@@ -320,7 +320,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 
        speed = ethtool_cmd_speed(cmd);
 
-       /* If recieved a request for an unknown duplex, assume full*/
+       /* If received a request for an unknown duplex, assume full*/
        if (cmd->duplex == DUPLEX_UNKNOWN)
                cmd->duplex = DUPLEX_FULL;
 
@@ -733,7 +733,6 @@ static bool bnx2x_is_reg_in_chip(struct bnx2x *bp,
                return false;
 }
 
-
 static bool bnx2x_is_wreg_in_chip(struct bnx2x *bp,
        const struct wreg_addr *wreg_info)
 {
@@ -850,7 +849,7 @@ static int __bnx2x_get_preset_regs(struct bnx2x *bp, u32 *p, u32 preset)
 
        /* Paged registers are supported in E2 & E3 only */
        if (CHIP_IS_E2(bp) || CHIP_IS_E3(bp)) {
-               /* Read "paged" registes */
+               /* Read "paged" registers */
                bnx2x_read_pages_regs(bp, p, preset);
        }
 
@@ -960,6 +959,9 @@ static int bnx2x_set_dump(struct net_device *dev, struct ethtool_dump *val)
        struct bnx2x *bp = netdev_priv(dev);
 
        /* Use the ethtool_dump "flag" field as the dump preset index */
+       if (val->flag < 1 || val->flag > DUMP_MAX_PRESETS)
+               return -EINVAL;
+
        bp->dump_preset_idx = val->flag;
        return 0;
 }
@@ -969,12 +971,12 @@ static int bnx2x_get_dump_flag(struct net_device *dev,
 {
        struct bnx2x *bp = netdev_priv(dev);
 
+       dump->version = BNX2X_DUMP_VERSION;
+       dump->flag = bp->dump_preset_idx;
        /* Calculate the requested preset idx length */
        dump->len = bnx2x_get_preset_regs_len(dev, bp->dump_preset_idx);
        DP(BNX2X_MSG_ETHTOOL, "Get dump preset %d length=%d\n",
           bp->dump_preset_idx, dump->len);
-
-       dump->flag = ETHTOOL_GET_DUMP_DATA;
        return 0;
 }
 
@@ -986,8 +988,6 @@ static int bnx2x_get_dump_data(struct net_device *dev,
        struct bnx2x *bp = netdev_priv(dev);
        struct dump_header dump_hdr = {0};
 
-       memset(p, 0, dump->len);
-
        /* Disable parity attentions as long as following dump may
         * cause false alarms by reading never written registers. We
         * will re-enable parity attentions right after the dump.
@@ -1155,8 +1155,8 @@ static int bnx2x_get_eeprom_len(struct net_device *dev)
        return bp->common.flash_size;
 }
 
-/* Per pf misc lock must be aquired before the per port mcp lock. Otherwise, had
- * we done things the other way around, if two pfs from the same port would
+/* Per pf misc lock must be acquired before the per port mcp lock. Otherwise,
+ * had we done things the other way around, if two pfs from the same port would
  * attempt to access nvram at the same time, we could run into a scenario such
  * as:
  * pf A takes the port lock.
@@ -1381,12 +1381,29 @@ static int bnx2x_nvram_read32(struct bnx2x *bp, u32 offset, u32 *buf,
        return rc;
 }
 
+static bool bnx2x_is_nvm_accessible(struct bnx2x *bp)
+{
+       int rc = 1;
+       u16 pm = 0;
+       struct net_device *dev = pci_get_drvdata(bp->pdev);
+
+       if (bp->pm_cap)
+               rc = pci_read_config_word(bp->pdev,
+                                         bp->pm_cap + PCI_PM_CTRL, &pm);
+
+       if ((rc && !netif_running(dev)) ||
+           (!rc && ((pm & PCI_PM_CTRL_STATE_MASK) != (__force u16)PCI_D0)))
+               return false;
+
+       return true;
+}
+
 static int bnx2x_get_eeprom(struct net_device *dev,
                            struct ethtool_eeprom *eeprom, u8 *eebuf)
 {
        struct bnx2x *bp = netdev_priv(dev);
 
-       if (!netif_running(dev)) {
+       if (!bnx2x_is_nvm_accessible(bp)) {
                DP(BNX2X_MSG_ETHTOOL  | BNX2X_MSG_NVM,
                   "cannot access eeprom when the interface is down\n");
                return -EAGAIN;
@@ -1411,7 +1428,7 @@ static int bnx2x_get_module_eeprom(struct net_device *dev,
        u8 *user_data = data;
        unsigned int start_addr = ee->offset, xfer_size = 0;
 
-       if (!netif_running(dev)) {
+       if (!bnx2x_is_nvm_accessible(bp)) {
                DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
                   "cannot access eeprom when the interface is down\n");
                return -EAGAIN;
@@ -1474,7 +1491,7 @@ static int bnx2x_get_module_info(struct net_device *dev,
        int phy_idx, rc;
        u8 sff8472_comp, diag_type;
 
-       if (!netif_running(dev)) {
+       if (!bnx2x_is_nvm_accessible(bp)) {
                DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
                   "cannot access eeprom when the interface is down\n");
                return -EAGAIN;
@@ -1594,8 +1611,10 @@ static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf,
                 */
                val = be32_to_cpu(val_be);
 
-               val &= ~le32_to_cpu(0xff << BYTE_OFFSET(offset));
-               val |= le32_to_cpu(*data_buf << BYTE_OFFSET(offset));
+               val &= ~le32_to_cpu((__force __le32)
+                                   (0xff << BYTE_OFFSET(offset)));
+               val |= le32_to_cpu((__force __le32)
+                                  (*data_buf << BYTE_OFFSET(offset)));
 
                rc = bnx2x_nvram_write_dword(bp, align_offset, val,
                                             cmd_flags);
@@ -1676,7 +1695,8 @@ static int bnx2x_set_eeprom(struct net_device *dev,
        int port = BP_PORT(bp);
        int rc = 0;
        u32 ext_phy_config;
-       if (!netif_running(dev)) {
+
+       if (!bnx2x_is_nvm_accessible(bp)) {
                DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
                   "cannot access eeprom when the interface is down\n");
                return -EAGAIN;
@@ -1921,6 +1941,19 @@ static const char bnx2x_tests_str_arr[BNX2X_NUM_TESTS_SF][ETH_GSTRING_LEN] = {
        "link_test (online)         "
 };
 
+enum {
+       BNX2X_PRI_FLAG_ISCSI,
+       BNX2X_PRI_FLAG_FCOE,
+       BNX2X_PRI_FLAG_STORAGE,
+       BNX2X_PRI_FLAG_LEN,
+};
+
+static const char bnx2x_private_arr[BNX2X_PRI_FLAG_LEN][ETH_GSTRING_LEN] = {
+       "iSCSI offload support",
+       "FCoE offload support",
+       "Storage only interface"
+};
+
 static u32 bnx2x_eee_to_adv(u32 eee_adv)
 {
        u32 modes = 0;
@@ -2041,7 +2074,7 @@ static int bnx2x_set_eee(struct net_device *dev, struct ethtool_eee *edata)
                                    EEE_MODE_OVERRIDE_NVRAM |
                                    EEE_MODE_OUTPUT_TIME;
 
-       /* Restart link to propogate changes */
+       /* Restart link to propagate changes */
        if (netif_running(dev)) {
                bnx2x_stats_handle(bp, STATS_EVENT_STOP);
                bnx2x_force_link_reset(bp);
@@ -2160,7 +2193,7 @@ static int bnx2x_test_registers(struct bnx2x *bp)
                { BNX2X_CHIP_MASK_ALL, 0xffffffff, 0, 0x00000000 }
        };
 
-       if (!netif_running(bp->dev)) {
+       if (!bnx2x_is_nvm_accessible(bp)) {
                DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
                   "cannot access eeprom when the interface is down\n");
                return rc;
@@ -2264,7 +2297,7 @@ static int bnx2x_test_memory(struct bnx2x *bp)
                { NULL, 0xffffffff, {0, 0, 0, 0} }
        };
 
-       if (!netif_running(bp->dev)) {
+       if (!bnx2x_is_nvm_accessible(bp)) {
                DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
                   "cannot access eeprom when the interface is down\n");
                return rc;
@@ -2978,32 +3011,47 @@ static int bnx2x_num_stat_queues(struct bnx2x *bp)
 static int bnx2x_get_sset_count(struct net_device *dev, int stringset)
 {
        struct bnx2x *bp = netdev_priv(dev);
-       int i, num_stats;
+       int i, num_strings = 0;
 
        switch (stringset) {
        case ETH_SS_STATS:
                if (is_multi(bp)) {
-                       num_stats = bnx2x_num_stat_queues(bp) *
-                                               BNX2X_NUM_Q_STATS;
+                       num_strings = bnx2x_num_stat_queues(bp) *
+                                     BNX2X_NUM_Q_STATS;
                } else
-                       num_stats = 0;
+                       num_strings = 0;
                if (IS_MF_MODE_STAT(bp)) {
                        for (i = 0; i < BNX2X_NUM_STATS; i++)
                                if (IS_FUNC_STAT(i))
-                                       num_stats++;
+                                       num_strings++;
                } else
-                       num_stats += BNX2X_NUM_STATS;
+                       num_strings += BNX2X_NUM_STATS;
 
-               return num_stats;
+               return num_strings;
 
        case ETH_SS_TEST:
                return BNX2X_NUM_TESTS(bp);
 
+       case ETH_SS_PRIV_FLAGS:
+               return BNX2X_PRI_FLAG_LEN;
+
        default:
                return -EINVAL;
        }
 }
 
+static u32 bnx2x_get_private_flags(struct net_device *dev)
+{
+       struct bnx2x *bp = netdev_priv(dev);
+       u32 flags = 0;
+
+       flags |= (!(bp->flags & NO_ISCSI_FLAG) ? 1 : 0) << BNX2X_PRI_FLAG_ISCSI;
+       flags |= (!(bp->flags & NO_FCOE_FLAG)  ? 1 : 0) << BNX2X_PRI_FLAG_FCOE;
+       flags |= (!!IS_MF_STORAGE_ONLY(bp)) << BNX2X_PRI_FLAG_STORAGE;
+
+       return flags;
+}
+
 static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
 {
        struct bnx2x *bp = netdev_priv(dev);
@@ -3026,7 +3074,6 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
                        }
                }
 
-
                for (i = 0, j = 0; i < BNX2X_NUM_STATS; i++) {
                        if (IS_MF_MODE_STAT(bp) && IS_PORT_STAT(i))
                                continue;
@@ -3045,6 +3092,12 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
                        start = 4;
                memcpy(buf, bnx2x_tests_str_arr + start,
                       ETH_GSTRING_LEN * BNX2X_NUM_TESTS(bp));
+               break;
+
+       case ETH_SS_PRIV_FLAGS:
+               memcpy(buf, bnx2x_private_arr,
+                      ETH_GSTRING_LEN * BNX2X_PRI_FLAG_LEN);
+               break;
        }
 }
 
@@ -3106,17 +3159,12 @@ static int bnx2x_set_phys_id(struct net_device *dev,
 {
        struct bnx2x *bp = netdev_priv(dev);
 
-       if (!netif_running(dev)) {
+       if (!bnx2x_is_nvm_accessible(bp)) {
                DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,
                   "cannot access eeprom when the interface is down\n");
                return -EAGAIN;
        }
 
-       if (!bp->port.pmf) {
-               DP(BNX2X_MSG_ETHTOOL, "Interface is not pmf\n");
-               return -EOPNOTSUPP;
-       }
-
        switch (state) {
        case ETHTOOL_ID_ACTIVE:
                return 1;       /* cycle on/off once per second */
@@ -3148,7 +3196,6 @@ static int bnx2x_set_phys_id(struct net_device *dev,
 
 static int bnx2x_get_rss_flags(struct bnx2x *bp, struct ethtool_rxnfc *info)
 {
-
        switch (info->flow_type) {
        case TCP_V4_FLOW:
        case TCP_V6_FLOW:
@@ -3384,7 +3431,6 @@ static int bnx2x_set_channels(struct net_device *dev,
 {
        struct bnx2x *bp = netdev_priv(dev);
 
-
        DP(BNX2X_MSG_ETHTOOL,
           "set-channels command parameters: rx = %d, tx = %d, other = %d, combined = %d\n",
           channels->rx_count, channels->tx_count, channels->other_count,
@@ -3445,6 +3491,7 @@ static const struct ethtool_ops bnx2x_ethtool_ops = {
        .set_pauseparam         = bnx2x_set_pauseparam,
        .self_test              = bnx2x_self_test,
        .get_sset_count         = bnx2x_get_sset_count,
+       .get_priv_flags         = bnx2x_get_private_flags,
        .get_strings            = bnx2x_get_strings,
        .set_phys_id            = bnx2x_set_phys_id,
        .get_ethtool_stats      = bnx2x_get_ethtool_stats,
index 12f00a4..5018e52 100644 (file)
@@ -1323,6 +1323,8 @@ struct drv_func_mb {
        #define DRV_MSG_CODE_UNLOAD_SKIP_LINK_RESET     0x00000002
 
        #define DRV_MSG_CODE_LOAD_REQ_WITH_LFA          0x0000100a
+       #define DRV_MSG_CODE_LOAD_REQ_FORCE_LFA         0x00002000
+
        u32 fw_mb_header;
        #define FW_MSG_CODE_MASK                        0xffff0000
        #define FW_MSG_CODE_DRV_LOAD_COMMON             0x10100000
@@ -3816,7 +3818,8 @@ struct eth_fast_path_rx_cqe {
        __le16 len_on_bd;
        struct parsing_flags pars_flags;
        union eth_sgl_or_raw_data sgl_or_raw_data;
-       __le32 reserved1[8];
+       __le32 reserved1[7];
+       u32 marker;
 };
 
 
index b4c9dea..15a528b 100644 (file)
@@ -93,7 +93,6 @@ MODULE_FIRMWARE(FW_FILE_NAME_E1);
 MODULE_FIRMWARE(FW_FILE_NAME_E1H);
 MODULE_FIRMWARE(FW_FILE_NAME_E2);
 
-
 int num_queues;
 module_param(num_queues, int, 0);
 MODULE_PARM_DESC(num_queues,
@@ -103,8 +102,6 @@ static int disable_tpa;
 module_param(disable_tpa, int, 0);
 MODULE_PARM_DESC(disable_tpa, " Disable the TPA (LRO) feature");
 
-#define INT_MODE_INTx                  1
-#define INT_MODE_MSI                   2
 int int_mode;
 module_param(int_mode, int, 0);
 MODULE_PARM_DESC(int_mode, " Force interrupt mode other than MSI-X "
@@ -122,8 +119,6 @@ static int debug;
 module_param(debug, int, 0);
 MODULE_PARM_DESC(debug, " Default debug msglevel");
 
-
-
 struct workqueue_struct *bnx2x_wq;
 
 struct bnx2x_mac_vals {
@@ -376,9 +371,11 @@ static u32 bnx2x_reg_rd_ind(struct bnx2x *bp, u32 addr)
 #define DMAE_DP_DST_PCI                "pci dst_addr [%x:%08x]"
 #define DMAE_DP_DST_NONE       "dst_addr [none]"
 
-void bnx2x_dp_dmae(struct bnx2x *bp, struct dmae_command *dmae, int msglvl)
+static void bnx2x_dp_dmae(struct bnx2x *bp,
+                         struct dmae_command *dmae, int msglvl)
 {
        u32 src_type = dmae->opcode & DMAE_COMMAND_SRC;
+       int i;
 
        switch (dmae->opcode & DMAE_COMMAND_DST) {
        case DMAE_CMD_DST_PCI:
@@ -434,6 +431,10 @@ void bnx2x_dp_dmae(struct bnx2x *bp, struct dmae_command *dmae, int msglvl)
                           dmae->comp_val);
                break;
        }
+
+       for (i = 0; i < (sizeof(struct dmae_command)/4); i++)
+               DP(msglvl, "DMAE RAW [%02d]: 0x%08x\n",
+                  i, *(((u32 *)dmae) + i));
 }
 
 /* copy command into DMAE command memory and set DMAE command go */
@@ -508,8 +509,9 @@ int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae)
        int cnt = CHIP_REV_IS_SLOW(bp) ? (400000) : 4000;
        int rc = 0;
 
-       /*
-        * Lock the dmae channel. Disable BHs to prevent a dead-lock
+       bnx2x_dp_dmae(bp, dmae, BNX2X_MSG_DMAE);
+
+       /* Lock the dmae channel. Disable BHs to prevent a dead-lock
         * as long as this code is called both from syscall context and
         * from ndo_set_rx_mode() flow that may be called from BH.
         */
@@ -548,6 +550,7 @@ unlock:
 void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr,
                      u32 len32)
 {
+       int rc;
        struct dmae_command dmae;
 
        if (!bp->dmae_ready) {
@@ -571,11 +574,16 @@ void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr,
        dmae.len = len32;
 
        /* issue the command and wait for completion */
-       bnx2x_issue_dmae_with_comp(bp, &dmae);
+       rc = bnx2x_issue_dmae_with_comp(bp, &dmae);
+       if (rc) {
+               BNX2X_ERR("DMAE returned failure %d\n", rc);
+               bnx2x_panic();
+       }
 }
 
 void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32)
 {
+       int rc;
        struct dmae_command dmae;
 
        if (!bp->dmae_ready) {
@@ -603,7 +611,11 @@ void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32)
        dmae.len = len32;
 
        /* issue the command and wait for completion */
-       bnx2x_issue_dmae_with_comp(bp, &dmae);
+       rc = bnx2x_issue_dmae_with_comp(bp, &dmae);
+       if (rc) {
+               BNX2X_ERR("DMAE returned failure %d\n", rc);
+               bnx2x_panic();
+       }
 }
 
 static void bnx2x_write_dmae_phys_len(struct bnx2x *bp, dma_addr_t phys_addr,
@@ -811,8 +823,8 @@ static void bnx2x_hc_int_disable(struct bnx2x *bp)
        u32 val = REG_RD(bp, addr);
 
        /* in E1 we must use only PCI configuration space to disable
-        * MSI/MSIX capablility
-        * It's forbitten to disable IGU_PF_CONF_MSI_MSIX_EN in HC block
+        * MSI/MSIX capability
+        * It's forbidden to disable IGU_PF_CONF_MSI_MSIX_EN in HC block
         */
        if (CHIP_IS_E1(bp)) {
                /* Since IGU_PF_CONF_MSI_MSIX_EN still always on
@@ -839,7 +851,7 @@ static void bnx2x_hc_int_disable(struct bnx2x *bp)
 
        REG_WR(bp, addr, val);
        if (REG_RD(bp, addr) != val)
-               BNX2X_ERR("BUG! proper val not read from IGU!\n");
+               BNX2X_ERR("BUG! Proper val not read from IGU!\n");
 }
 
 static void bnx2x_igu_int_disable(struct bnx2x *bp)
@@ -857,7 +869,7 @@ static void bnx2x_igu_int_disable(struct bnx2x *bp)
 
        REG_WR(bp, IGU_REG_PF_CONFIGURATION, val);
        if (REG_RD(bp, IGU_REG_PF_CONFIGURATION) != val)
-               BNX2X_ERR("BUG! proper val not read from IGU!\n");
+               BNX2X_ERR("BUG! Proper val not read from IGU!\n");
 }
 
 static void bnx2x_int_disable(struct bnx2x *bp)
@@ -917,7 +929,6 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int)
               sp_sb_data.p_func.vf_valid,
               sp_sb_data.state);
 
-
        for_each_eth_queue(bp, i) {
                struct bnx2x_fastpath *fp = &bp->fp[i];
                int loop;
@@ -1016,7 +1027,7 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int)
                                hc_sm_p[j].timer_value);
                }
 
-               /* Indecies data */
+               /* Indices data */
                for (j = 0; j < loop; j++) {
                        pr_cont("INDEX[%d] flags (0x%x) timeout (0x%x)\n", j,
                               hc_index_p[j].flags,
@@ -1027,6 +1038,7 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int)
 #ifdef BNX2X_STOP_ON_ERROR
 
        /* event queue */
+       BNX2X_ERR("eq cons %x prod %x\n", bp->eq_cons, bp->eq_prod);
        for (i = 0; i < NUM_EQ_DESC; i++) {
                u32 *data = (u32 *)&bp->eq_ring[i].message.data;
 
@@ -1111,7 +1123,7 @@ void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int)
  * bnx2x_pf_flr_clnup() is called during nic_load in the per function HW
  * initialization.
  */
-#define FLR_WAIT_USEC          10000   /* 10 miliseconds */
+#define FLR_WAIT_USEC          10000   /* 10 milliseconds */
 #define FLR_WAIT_INTERVAL      50      /* usec */
 #define        FLR_POLL_CNT            (FLR_WAIT_USEC/FLR_WAIT_INTERVAL) /* 200 */
 
@@ -1290,7 +1302,6 @@ void bnx2x_tx_hw_flushed(struct bnx2x *bp, u32 poll_count)
        for (i = 0; i < ARRAY_SIZE(cmd_regs); i++)
                bnx2x_pbf_pN_cmd_flushed(bp, &cmd_regs[i], poll_count);
 
-
        /* Verify the transmission buffers are flushed P0, P1, P4 */
        for (i = 0; i < ARRAY_SIZE(buf_regs); i++)
                bnx2x_pbf_pN_buf_flushed(bp, &buf_regs[i], poll_count);
@@ -1305,11 +1316,9 @@ void bnx2x_tx_hw_flushed(struct bnx2x *bp, u32 poll_count)
 #define OP_GEN_AGG_VECT(index) \
        (((index) << SDM_OP_GEN_AGG_VECT_IDX_SHIFT) & SDM_OP_GEN_AGG_VECT_IDX)
 
-
 int bnx2x_send_final_clnup(struct bnx2x *bp, u8 clnup_func, u32 poll_cnt)
 {
        u32 op_gen_command = 0;
-
        u32 comp_addr = BAR_CSTRORM_INTMEM +
                        CSTORM_FINAL_CLEANUP_COMPLETE_OFFSET(clnup_func);
        int ret = 0;
@@ -1334,7 +1343,7 @@ int bnx2x_send_final_clnup(struct bnx2x *bp, u8 clnup_func, u32 poll_cnt)
                bnx2x_panic();
                return 1;
        }
-       /* Zero completion for nxt FLR */
+       /* Zero completion for next FLR */
        REG_WR(bp, comp_addr, 0);
 
        return ret;
@@ -1352,7 +1361,6 @@ u8 bnx2x_is_pcie_pending(struct pci_dev *dev)
 */
 static int bnx2x_poll_hw_usage_counters(struct bnx2x *bp, u32 poll_cnt)
 {
-
        /* wait for CFC PF usage-counter to zero (includes all the VFs) */
        if (bnx2x_flr_clnup_poll_hw_counter(bp,
                        CFC_REG_NUM_LCIDS_INSIDE_PF,
@@ -1360,7 +1368,6 @@ static int bnx2x_poll_hw_usage_counters(struct bnx2x *bp, u32 poll_cnt)
                        poll_cnt))
                return 1;
 
-
        /* Wait for DQ PF usage-counter to zero (until DQ cleanup) */
        if (bnx2x_flr_clnup_poll_hw_counter(bp,
                        DORQ_REG_PF_USAGE_CNT,
@@ -1390,7 +1397,7 @@ static int bnx2x_poll_hw_usage_counters(struct bnx2x *bp, u32 poll_cnt)
        /* Wait DMAE PF usage counter to zero */
        if (bnx2x_flr_clnup_poll_hw_counter(bp,
                        dmae_reg_go_c[INIT_DMAE_C(bp)],
-                       "DMAE dommand register timed out",
+                       "DMAE command register timed out",
                        poll_cnt))
                return 1;
 
@@ -1770,7 +1777,7 @@ void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe)
                break;
 
        case (RAMROD_CMD_ID_ETH_TERMINATE):
-               DP(BNX2X_MSG_SP, "got MULTI[%d] teminate ramrod\n", cid);
+               DP(BNX2X_MSG_SP, "got MULTI[%d] terminate ramrod\n", cid);
                drv_cmd = BNX2X_Q_CMD_TERMINATE;
                break;
 
@@ -1859,7 +1866,6 @@ irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
                mask = 0x2 << (fp->index + CNIC_SUPPORT(bp));
                if (status & mask) {
                        /* Handle Rx or Tx according to SB id */
-                       prefetch(fp->rx_cons_sb);
                        for_each_cos_in_tx_queue(fp, cos)
                                prefetch(fp->txdata_ptr[cos]->tx_cons_sb);
                        prefetch(&fp->sb_running_index[SM_RX_ID]);
@@ -1947,7 +1953,7 @@ int bnx2x_acquire_hw_lock(struct bnx2x *bp, u32 resource)
                if (lock_status & resource_bit)
                        return 0;
 
-               msleep(5);
+               usleep_range(5000, 10000);
        }
        BNX2X_ERR("Timeout\n");
        return -EAGAIN;
@@ -1982,8 +1988,8 @@ int bnx2x_release_hw_lock(struct bnx2x *bp, u32 resource)
        /* Validating that the resource is currently taken */
        lock_status = REG_RD(bp, hw_lock_control_reg);
        if (!(lock_status & resource_bit)) {
-               BNX2X_ERR("lock_status 0x%x resource_bit 0x%x. unlock was called but lock wasn't taken!\n",
-                  lock_status, resource_bit);
+               BNX2X_ERR("lock_status 0x%x resource_bit 0x%x. Unlock was called but lock wasn't taken!\n",
+                         lock_status, resource_bit);
                return -EFAULT;
        }
 
@@ -1991,7 +1997,6 @@ int bnx2x_release_hw_lock(struct bnx2x *bp, u32 resource)
        return 0;
 }
 
-
 int bnx2x_get_gpio(struct bnx2x *bp, int gpio_num, u8 port)
 {
        /* The GPIO should be swapped if swap register is set and active */
@@ -2347,14 +2352,13 @@ u8 bnx2x_link_test(struct bnx2x *bp, u8 is_serdes)
        return rc;
 }
 
-
 /* Calculates the sum of vn_min_rates.
    It's needed for further normalizing of the min_rates.
    Returns:
      sum of vn_min_rates.
        or
      0 - if all the min_rates are 0.
-     In the later case fainess algorithm should be deactivated.
+     In the later case fairness algorithm should be deactivated.
      If not all min_rates are zero then those that are zeroes will be set to 1.
  */
 static void bnx2x_calc_vn_min(struct bnx2x *bp,
@@ -2419,7 +2423,6 @@ static void bnx2x_calc_vn_max(struct bnx2x *bp, int vn,
        input->vnic_max_rate[vn] = vn_max_rate;
 }
 
-
 static int bnx2x_get_cmng_fns_mode(struct bnx2x *bp)
 {
        if (CHIP_REV_IS_SLOW(bp))
@@ -2435,7 +2438,7 @@ void bnx2x_read_mf_cfg(struct bnx2x *bp)
        int vn, n = (CHIP_MODE_IS_4_PORT(bp) ? 2 : 1);
 
        if (BP_NOMCP(bp))
-               return; /* what should be the default bvalue in this case */
+               return; /* what should be the default value in this case */
 
        /* For 2 port configuration the absolute function number formula
         * is:
@@ -2901,7 +2904,6 @@ u32 bnx2x_fw_command(struct bnx2x *bp, u32 command, u32 param)
        return rc;
 }
 
-
 static void storm_memset_func_cfg(struct bnx2x *bp,
                                 struct tstorm_eth_function_common_config *tcfg,
                                 u16 abs_fid)
@@ -2935,7 +2937,7 @@ void bnx2x_func_init(struct bnx2x *bp, struct bnx2x_func_init_params *p)
 }
 
 /**
- * bnx2x_get_tx_only_flags - Return common flags
+ * bnx2x_get_common_flags - Return common flags
  *
  * @bp         device handle
  * @fp         queue handle
@@ -3006,7 +3008,6 @@ static unsigned long bnx2x_get_q_flags(struct bnx2x *bp,
        if (IS_MF_AFEX(bp))
                __set_bit(BNX2X_Q_FLG_SILENT_VLAN_REM, &flags);
 
-
        return flags | bnx2x_get_common_flags(bp, fp, true);
 }
 
@@ -3082,7 +3083,7 @@ static void bnx2x_pf_rx_q_prep(struct bnx2x *bp,
         * placed on the BD (not including paddings).
         */
        rxq_init->buf_sz = fp->rx_buf_size - BNX2X_FW_RX_ALIGN_START -
-               BNX2X_FW_RX_ALIGN_END - IP_HEADER_ALIGNMENT_PADDING;
+                          BNX2X_FW_RX_ALIGN_END - IP_HEADER_ALIGNMENT_PADDING;
 
        rxq_init->cl_qzone_id = fp->cl_qzone_id;
        rxq_init->tpa_agg_sz = tpa_agg_size;
@@ -3124,7 +3125,7 @@ static void bnx2x_pf_tx_q_prep(struct bnx2x *bp,
        txq_init->fw_sb_id = fp->fw_sb_id;
 
        /*
-        * set the tss leading client id for TX classfication ==
+        * set the tss leading client id for TX classification ==
         * leading RSS client id
         */
        txq_init->tss_leading_cl_id = bnx2x_fp(bp, 0, cl_id);
@@ -3196,7 +3197,6 @@ static void bnx2x_pf_init(struct bnx2x *bp)
        storm_memset_eq_data(bp, &eq_data, BP_FUNC(bp));
 }
 
-
 static void bnx2x_e1h_disable(struct bnx2x *bp)
 {
        int port = BP_PORT(bp);
@@ -3212,7 +3212,7 @@ static void bnx2x_e1h_enable(struct bnx2x *bp)
 
        REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 1);
 
-       /* Tx queue should be only reenabled */
+       /* Tx queue should be only re-enabled */
        netif_tx_wake_all_queues(bp->dev);
 
        /*
@@ -3540,10 +3540,8 @@ static bool bnx2x_is_contextless_ramrod(int cmd, int cmd_type)
                return true;
        else
                return false;
-
 }
 
-
 /**
  * bnx2x_sp_post - place a single command on an SP ring
  *
@@ -3608,14 +3606,13 @@ int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
        /*
         * It's ok if the actual decrement is issued towards the memory
         * somewhere between the spin_lock and spin_unlock. Thus no
-        * more explict memory barrier is needed.
+        * more explicit memory barrier is needed.
         */
        if (common)
                atomic_dec(&bp->eq_spq_left);
        else
                atomic_dec(&bp->cq_spq_left);
 
-
        DP(BNX2X_MSG_SP,
           "SPQE[%x] (%x:%x)  (cmd, common?) (%d,%d)  hw_cid %x  data (%x:%x) type(0x%x) left (CQ, EQ) (%x,%x)\n",
           bp->spq_prod_idx, (u32)U64_HI(bp->spq_mapping),
@@ -3637,15 +3634,14 @@ static int bnx2x_acquire_alr(struct bnx2x *bp)
 
        might_sleep();
        for (j = 0; j < 1000; j++) {
-               val = (1UL << 31);
-               REG_WR(bp, GRCBASE_MCP + 0x9c, val);
-               val = REG_RD(bp, GRCBASE_MCP + 0x9c);
-               if (val & (1L << 31))
+               REG_WR(bp, MCP_REG_MCPR_ACCESS_LOCK, MCPR_ACCESS_LOCK_LOCK);
+               val = REG_RD(bp, MCP_REG_MCPR_ACCESS_LOCK);
+               if (val & MCPR_ACCESS_LOCK_LOCK)
                        break;
 
-               msleep(5);
+               usleep_range(5000, 10000);
        }
-       if (!(val & (1L << 31))) {
+       if (!(val & MCPR_ACCESS_LOCK_LOCK)) {
                BNX2X_ERR("Cannot acquire MCP access lock register\n");
                rc = -EBUSY;
        }
@@ -3656,7 +3652,7 @@ static int bnx2x_acquire_alr(struct bnx2x *bp)
 /* release split MCP access lock register */
 static void bnx2x_release_alr(struct bnx2x *bp)
 {
-       REG_WR(bp, GRCBASE_MCP + 0x9c, 0);
+       REG_WR(bp, MCP_REG_MCPR_ACCESS_LOCK, 0);
 }
 
 #define BNX2X_DEF_SB_ATT_IDX   0x0001
@@ -3678,7 +3674,7 @@ static u16 bnx2x_update_dsb_idx(struct bnx2x *bp)
                rc |= BNX2X_DEF_SB_IDX;
        }
 
-       /* Do not reorder: indecies reading should complete before handling */
+       /* Do not reorder: indices reading should complete before handling */
        barrier();
        return rc;
 }
@@ -3827,8 +3823,7 @@ static void bnx2x_fan_failure(struct bnx2x *bp)
        netdev_err(bp->dev, "Fan Failure on Network Controller has caused the driver to shutdown the card to prevent permanent damage.\n"
                            "Please contact OEM Support for assistance\n");
 
-       /*
-        * Schedule device reset (unload)
+       /* Schedule device reset (unload)
         * This is due to some boards consuming sufficient power when driver is
         * up to overheat if fan fails.
         */
@@ -3836,7 +3831,6 @@ static void bnx2x_fan_failure(struct bnx2x *bp)
        set_bit(BNX2X_SP_RTNL_FAN_FAILURE, &bp->sp_rtnl_state);
        smp_mb__after_clear_bit();
        schedule_delayed_work(&bp->sp_rtnl_task, 0);
-
 }
 
 static void bnx2x_attn_int_deasserted0(struct bnx2x *bp, u32 attn)
@@ -4106,7 +4100,7 @@ static void bnx2x_clear_reset_global(struct bnx2x *bp)
  */
 static bool bnx2x_reset_is_global(struct bnx2x *bp)
 {
-       u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+       u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
 
        DP(NETIF_MSG_HW, "GEN_REG_VAL=0x%08x\n", val);
        return (val & BNX2X_GLOBAL_RESET_BIT) ? true : false;
@@ -4157,7 +4151,7 @@ void bnx2x_set_reset_in_progress(struct bnx2x *bp)
  */
 bool bnx2x_reset_is_done(struct bnx2x *bp, int engine)
 {
-       u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
+       u32 val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
        u32 bit = engine ?
                BNX2X_PATH1_RST_IN_PROG_BIT : BNX2X_PATH0_RST_IN_PROG_BIT;
 
@@ -4260,13 +4254,18 @@ static bool bnx2x_get_load_status(struct bnx2x *bp, int engine)
        return val != 0;
 }
 
+static void _print_parity(struct bnx2x *bp, u32 reg)
+{
+       pr_cont(" [0x%08x] ", REG_RD(bp, reg));
+}
+
 static void _print_next_block(int idx, const char *blk)
 {
        pr_cont("%s%s", idx ? ", " : "", blk);
 }
 
-static int bnx2x_check_blocks_with_parity0(u32 sig, int par_num,
-                                          bool print)
+static int bnx2x_check_blocks_with_parity0(struct bnx2x *bp, u32 sig,
+                                           int par_num, bool print)
 {
        int i = 0;
        u32 cur_bit = 0;
@@ -4275,33 +4274,54 @@ static int bnx2x_check_blocks_with_parity0(u32 sig, int par_num,
                if (sig & cur_bit) {
                        switch (cur_bit) {
                        case AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "BRB");
+                                       _print_parity(bp,
+                                                     BRB1_REG_BRB1_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "PARSER");
+                                       _print_parity(bp, PRS_REG_PRS_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "TSDM");
+                                       _print_parity(bp,
+                                                     TSDM_REG_TSDM_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++,
                                                          "SEARCHER");
+                                       _print_parity(bp, SRC_REG_SRC_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_TCM_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "TCM");
+                                       _print_parity(bp,
+                                                     TCM_REG_TCM_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "TSEMI");
+                                       _print_parity(bp,
+                                                     TSEM_REG_TSEM_PRTY_STS_0);
+                                       _print_parity(bp,
+                                                     TSEM_REG_TSEM_PRTY_STS_1);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "XPB");
+                                       _print_parity(bp, GRCBASE_XPB +
+                                                         PB_REG_PB_PRTY_STS);
+                               }
                                break;
                        }
 
@@ -4313,8 +4333,9 @@ static int bnx2x_check_blocks_with_parity0(u32 sig, int par_num,
        return par_num;
 }
 
-static int bnx2x_check_blocks_with_parity1(u32 sig, int par_num,
-                                          bool *global, bool print)
+static int bnx2x_check_blocks_with_parity1(struct bnx2x *bp, u32 sig,
+                                           int par_num, bool *global,
+                                           bool print)
 {
        int i = 0;
        u32 cur_bit = 0;
@@ -4323,37 +4344,66 @@ static int bnx2x_check_blocks_with_parity1(u32 sig, int par_num,
                if (sig & cur_bit) {
                        switch (cur_bit) {
                        case AEU_INPUTS_ATTN_BITS_PBF_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "PBF");
+                                       _print_parity(bp, PBF_REG_PBF_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_QM_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "QM");
+                                       _print_parity(bp, QM_REG_QM_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_TIMERS_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "TM");
+                                       _print_parity(bp, TM_REG_TM_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_XSDM_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "XSDM");
+                                       _print_parity(bp,
+                                                     XSDM_REG_XSDM_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_XCM_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "XCM");
+                                       _print_parity(bp, XCM_REG_XCM_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_XSEMI_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "XSEMI");
+                                       _print_parity(bp,
+                                                     XSEM_REG_XSEM_PRTY_STS_0);
+                                       _print_parity(bp,
+                                                     XSEM_REG_XSEM_PRTY_STS_1);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_DOORBELLQ_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++,
                                                          "DOORBELLQ");
+                                       _print_parity(bp,
+                                                     DORQ_REG_DORQ_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_NIG_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "NIG");
+                                       if (CHIP_IS_E1x(bp)) {
+                                               _print_parity(bp,
+                                                       NIG_REG_NIG_PRTY_STS);
+                                       } else {
+                                               _print_parity(bp,
+                                                       NIG_REG_NIG_PRTY_STS_0);
+                                               _print_parity(bp,
+                                                       NIG_REG_NIG_PRTY_STS_1);
+                                       }
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_VAUX_PCI_CORE_PARITY_ERROR:
                                if (print)
@@ -4362,32 +4412,52 @@ static int bnx2x_check_blocks_with_parity1(u32 sig, int par_num,
                                *global = true;
                                break;
                        case AEU_INPUTS_ATTN_BITS_DEBUG_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "DEBUG");
+                                       _print_parity(bp, DBG_REG_DBG_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_USDM_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "USDM");
+                                       _print_parity(bp,
+                                                     USDM_REG_USDM_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_UCM_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "UCM");
+                                       _print_parity(bp, UCM_REG_UCM_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_USEMI_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "USEMI");
+                                       _print_parity(bp,
+                                                     USEM_REG_USEM_PRTY_STS_0);
+                                       _print_parity(bp,
+                                                     USEM_REG_USEM_PRTY_STS_1);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_UPB_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "UPB");
+                                       _print_parity(bp, GRCBASE_UPB +
+                                                         PB_REG_PB_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_CSDM_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "CSDM");
+                                       _print_parity(bp,
+                                                     CSDM_REG_CSDM_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_CCM_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "CCM");
+                                       _print_parity(bp, CCM_REG_CCM_PRTY_STS);
+                               }
                                break;
                        }
 
@@ -4399,8 +4469,8 @@ static int bnx2x_check_blocks_with_parity1(u32 sig, int par_num,
        return par_num;
 }
 
-static int bnx2x_check_blocks_with_parity2(u32 sig, int par_num,
-                                          bool print)
+static int bnx2x_check_blocks_with_parity2(struct bnx2x *bp, u32 sig,
+                                           int par_num, bool print)
 {
        int i = 0;
        u32 cur_bit = 0;
@@ -4409,12 +4479,23 @@ static int bnx2x_check_blocks_with_parity2(u32 sig, int par_num,
                if (sig & cur_bit) {
                        switch (cur_bit) {
                        case AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "CSEMI");
+                                       _print_parity(bp,
+                                                     CSEM_REG_CSEM_PRTY_STS_0);
+                                       _print_parity(bp,
+                                                     CSEM_REG_CSEM_PRTY_STS_1);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "PXP");
+                                       _print_parity(bp, PXP_REG_PXP_PRTY_STS);
+                                       _print_parity(bp,
+                                                     PXP2_REG_PXP2_PRTY_STS_0);
+                                       _print_parity(bp,
+                                                     PXP2_REG_PXP2_PRTY_STS_1);
+                               }
                                break;
                        case AEU_IN_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR:
                                if (print)
@@ -4422,24 +4503,42 @@ static int bnx2x_check_blocks_with_parity2(u32 sig, int par_num,
                                        "PXPPCICLOCKCLIENT");
                                break;
                        case AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "CFC");
+                                       _print_parity(bp,
+                                                     CFC_REG_CFC_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "CDU");
+                                       _print_parity(bp, CDU_REG_CDU_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_DMAE_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "DMAE");
+                                       _print_parity(bp,
+                                                     DMAE_REG_DMAE_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "IGU");
+                                       if (CHIP_IS_E1x(bp))
+                                               _print_parity(bp,
+                                                       HC_REG_HC_PRTY_STS);
+                                       else
+                                               _print_parity(bp,
+                                                       IGU_REG_IGU_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "MISC");
+                                       _print_parity(bp,
+                                                     MISC_REG_MISC_PRTY_STS);
+                               }
                                break;
                        }
 
@@ -4493,8 +4592,8 @@ static int bnx2x_check_blocks_with_parity3(u32 sig, int par_num,
        return par_num;
 }
 
-static int bnx2x_check_blocks_with_parity4(u32 sig, int par_num,
-                                          bool print)
+static int bnx2x_check_blocks_with_parity4(struct bnx2x *bp, u32 sig,
+                                           int par_num, bool print)
 {
        int i = 0;
        u32 cur_bit = 0;
@@ -4503,12 +4602,18 @@ static int bnx2x_check_blocks_with_parity4(u32 sig, int par_num,
                if (sig & cur_bit) {
                        switch (cur_bit) {
                        case AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "PGLUE_B");
+                                       _print_parity(bp,
+                                               PGLUE_B_REG_PGLUE_B_PRTY_STS);
+                               }
                                break;
                        case AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR:
-                               if (print)
+                               if (print) {
                                        _print_next_block(par_num++, "ATC");
+                                       _print_parity(bp,
+                                                     ATC_REG_ATC_PRTY_STS);
+                               }
                                break;
                        }
 
@@ -4539,15 +4644,15 @@ static bool bnx2x_parity_attn(struct bnx2x *bp, bool *global, bool print,
                if (print)
                        netdev_err(bp->dev,
                                   "Parity errors detected in blocks: ");
-               par_num = bnx2x_check_blocks_with_parity0(
+               par_num = bnx2x_check_blocks_with_parity0(bp,
                        sig[0] & HW_PRTY_ASSERT_SET_0, par_num, print);
-               par_num = bnx2x_check_blocks_with_parity1(
+               par_num = bnx2x_check_blocks_with_parity1(bp,
                        sig[1] & HW_PRTY_ASSERT_SET_1, par_num, global, print);
-               par_num = bnx2x_check_blocks_with_parity2(
+               par_num = bnx2x_check_blocks_with_parity2(bp,
                        sig[2] & HW_PRTY_ASSERT_SET_2, par_num, print);
                par_num = bnx2x_check_blocks_with_parity3(
                        sig[3] & HW_PRTY_ASSERT_SET_3, par_num, global, print);
-               par_num = bnx2x_check_blocks_with_parity4(
+               par_num = bnx2x_check_blocks_with_parity4(bp,
                        sig[4] & HW_PRTY_ASSERT_SET_4, par_num, print);
 
                if (print)
@@ -4591,7 +4696,6 @@ bool bnx2x_chk_parity_attn(struct bnx2x *bp, bool *global, bool print)
        return bnx2x_parity_attn(bp, global, print, attn.sig);
 }
 
-
 static void bnx2x_attn_int_deasserted4(struct bnx2x *bp, u32 attn)
 {
        u32 val;
@@ -4643,7 +4747,6 @@ static void bnx2x_attn_int_deasserted4(struct bnx2x *bp, u32 attn)
                (u32)(attn & (AEU_INPUTS_ATTN_BITS_PGLUE_PARITY_ERROR |
                    AEU_INPUTS_ATTN_BITS_ATC_PARITY_ERROR)));
        }
-
 }
 
 static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted)
@@ -4878,7 +4981,6 @@ static void bnx2x_handle_classification_eqe(struct bnx2x *bp,
                BNX2X_ERR("Failed to schedule new commands: %d\n", rc);
        else if (rc > 0)
                DP(BNX2X_MSG_SP, "Scheduled next pending commands...\n");
-
 }
 
 static void bnx2x_set_iscsi_eth_rx_mode(struct bnx2x *bp, bool start);
@@ -5009,7 +5111,7 @@ static void bnx2x_eq_int(struct bnx2x *bp)
        hw_cons = le16_to_cpu(*bp->eq_cons_sb);
 
        /* The hw_cos range is 1-255, 257 - the sw_cons range is 0-254, 256.
-        * when we get the the next-page we nned to adjust so the loop
+        * when we get the next-page we need to adjust so the loop
         * condition below will be met. The next element is the size of a
         * regular element and hence incrementing by 1
         */
@@ -5075,8 +5177,6 @@ static void bnx2x_eq_int(struct bnx2x *bp)
                        if (q_obj->complete_cmd(bp, q_obj, BNX2X_Q_CMD_CFC_DEL))
                                break;
 
-
-
                        goto next_spqe;
 
                case EVENT_RING_OPCODE_STOP_TRAFFIC:
@@ -5218,7 +5318,7 @@ static void bnx2x_sp_task(struct work_struct *work)
 
        DP(BNX2X_MSG_SP, "sp task invoked\n");
 
-       /* make sure the atomic interupt_occurred has been written */
+       /* make sure the atomic interrupt_occurred has been written */
        smp_rmb();
        if (atomic_read(&bp->interrupt_occurred)) {
 
@@ -5265,7 +5365,6 @@ static void bnx2x_sp_task(struct work_struct *work)
                /* ack status block only if something was actually handled */
                bnx2x_ack_sb(bp, bp->igu_dsb_id, ATTENTION_ID,
                             le16_to_cpu(bp->def_att_idx), IGU_INT_ENABLE, 1);
-
        }
 
        /* must be called after the EQ processing (since eq leads to sriov
@@ -5316,7 +5415,6 @@ irqreturn_t bnx2x_msix_sp_int(int irq, void *dev_instance)
 
 /* end of slow path */
 
-
 void bnx2x_drv_pulse(struct bnx2x *bp)
 {
        SHMEM_WR(bp, func_mb[BP_FW_MB_IDX(bp)].drv_pulse_mb,
@@ -5360,7 +5458,7 @@ static void bnx2x_timer(unsigned long data)
 
        /* sample pf vf bulletin board for new posts from pf */
        if (IS_VF(bp))
-               bnx2x_sample_bulletin(bp);
+               bnx2x_timer_sriov(bp);
 
        mod_timer(&bp->timer, jiffies + bp->current_interval);
 }
@@ -5382,7 +5480,6 @@ static void bnx2x_fill(struct bnx2x *bp, u32 addr, int fill, u32 len)
        else
                for (i = 0; i < len; i++)
                        REG_WR8(bp, addr + i, fill);
-
 }
 
 /* helper: writes FP SP data to FW - data_size in dwords */
@@ -5461,10 +5558,8 @@ static void bnx2x_zero_sp_sb(struct bnx2x *bp)
        bnx2x_fill(bp, BAR_CSTRORM_INTMEM +
                        CSTORM_SP_SYNC_BLOCK_OFFSET(func), 0,
                        CSTORM_SP_SYNC_BLOCK_SIZE);
-
 }
 
-
 static void bnx2x_setup_ndsb_state_machine(struct hc_status_block_sm *hc_sm,
                                           int igu_sb_id, int igu_seg_id)
 {
@@ -5474,7 +5569,6 @@ static void bnx2x_setup_ndsb_state_machine(struct hc_status_block_sm *hc_sm,
        hc_sm->time_to_expire = 0xFFFFFFFF;
 }
 
-
 /* allocates state machine ids. */
 static void bnx2x_map_sb_state_machines(struct hc_index_data *index_data)
 {
@@ -5700,7 +5794,7 @@ static void bnx2x_init_eq_ring(struct bnx2x *bp)
        bp->eq_cons = 0;
        bp->eq_prod = NUM_EQ_DESC;
        bp->eq_cons_sb = BNX2X_EQ_INDEX;
-       /* we want a warning message before it gets rought... */
+       /* we want a warning message before it gets wrought... */
        atomic_set(&bp->eq_spq_left,
                min_t(int, MAX_SP_DESC_CNT - MAX_SPQ_PENDING, NUM_EQ_DESC) - 1);
 }
@@ -5784,7 +5878,7 @@ static int bnx2x_fill_accept_flags(struct bnx2x *bp, u32 rx_mode,
 
                break;
        case BNX2X_RX_MODE_PROMISC:
-               /* According to deffinition of SI mode, iface in promisc mode
+               /* According to definition of SI mode, iface in promisc mode
                 * should receive matched and unmatched (in resolution of port)
                 * unicast packets.
                 */
@@ -5927,7 +6021,7 @@ static void bnx2x_init_eth_fp(struct bnx2x *bp, int fp_idx)
        /* init shortcut */
        fp->ustorm_rx_prods_offset = bnx2x_rx_ustorm_prods_offset(fp);
 
-       /* Setup SB indicies */
+       /* Setup SB indices */
        fp->rx_cons_sb = BNX2X_RX_SB_INDEX;
 
        /* Configure Queue State object */
@@ -5983,6 +6077,8 @@ static void bnx2x_init_tx_ring_one(struct bnx2x_fp_txdata *txdata)
                                    BCM_PAGE_SIZE*(i % NUM_TX_RINGS)));
        }
 
+       *txdata->tx_cons_sb = cpu_to_le16(0);
+
        SET_FLAG(txdata->tx_db.data.header.header, DOORBELL_HDR_DB_TYPE, 1);
        txdata->tx_db.data.zero_fill1 = 0;
        txdata->tx_db.data.prod = 0;
@@ -6001,6 +6097,7 @@ static void bnx2x_init_tx_rings_cnic(struct bnx2x *bp)
        for_each_tx_queue_cnic(bp, i)
                bnx2x_init_tx_ring_one(bp->fp[i].txdata_ptr[0]);
 }
+
 static void bnx2x_init_tx_rings(struct bnx2x *bp)
 {
        int i;
@@ -6043,11 +6140,6 @@ void bnx2x_pre_irq_nic_init(struct bnx2x *bp)
        bnx2x_init_rx_rings(bp);
        bnx2x_init_tx_rings(bp);
 
-       if (IS_VF(bp)) {
-               bnx2x_memset_stats(bp);
-               return;
-       }
-
        if (IS_PF(bp)) {
                /* Initialize MOD_ABS interrupts */
                bnx2x_init_mod_abs_int(bp, &bp->link_vars, bp->common.chip_id,
@@ -6058,6 +6150,8 @@ void bnx2x_pre_irq_nic_init(struct bnx2x *bp)
                bnx2x_init_def_sb(bp);
                bnx2x_update_dsb_idx(bp);
                bnx2x_init_sp_ring(bp);
+       } else {
+               bnx2x_memset_stats(bp);
        }
 }
 
@@ -6236,7 +6330,7 @@ static int bnx2x_int_mem_test(struct bnx2x *bp)
                if (val == 0x10)
                        break;
 
-               msleep(10);
+               usleep_range(10000, 20000);
                count--;
        }
        if (val != 0x10) {
@@ -6251,7 +6345,7 @@ static int bnx2x_int_mem_test(struct bnx2x *bp)
                if (val == 1)
                        break;
 
-               msleep(10);
+               usleep_range(10000, 20000);
                count--;
        }
        if (val != 0x1) {
@@ -6292,7 +6386,7 @@ static int bnx2x_int_mem_test(struct bnx2x *bp)
                if (val == 0xb0)
                        break;
 
-               msleep(10);
+               usleep_range(10000, 20000);
                count--;
        }
        if (val != 0xb0) {
@@ -6681,7 +6775,7 @@ static int bnx2x_init_hw_common(struct bnx2x *bp)
  *                 stay set)
  *             f.  If this is VNIC 3 of a port then also init
  *                 first_timers_ilt_entry to zero and last_timers_ilt_entry
- *                 to the last enrty in the ILT.
+ *                 to the last entry in the ILT.
  *
  *     Notes:
  *     Currently the PF error in the PGLC is non recoverable.
@@ -6772,7 +6866,6 @@ static int bnx2x_init_hw_common(struct bnx2x *bp)
 
        bnx2x_init_block(bp, BLOCK_QM, PHASE_COMMON);
 
-
        /* QM queues pointers table */
        bnx2x_qm_init_ptr_table(bp, bp->qm_cid_count, INITOP_SET);
 
@@ -7013,7 +7106,6 @@ static int bnx2x_init_hw_port(struct bnx2x *bp)
        u32 low, high;
        u32 val;
 
-
        DP(NETIF_MSG_HW, "starting port init  port %d\n", port);
 
        REG_WR(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, 0);
@@ -7078,7 +7170,6 @@ static int bnx2x_init_hw_port(struct bnx2x *bp)
                            BRB1_REG_MAC_GUARANTIED_1 :
                            BRB1_REG_MAC_GUARANTIED_0), 40);
 
-
        bnx2x_init_block(bp, BLOCK_PRS, init_phase);
        if (CHIP_IS_E3B0(bp)) {
                if (IS_MF_AFEX(bp)) {
@@ -7150,8 +7241,8 @@ static int bnx2x_init_hw_port(struct bnx2x *bp)
 
        bnx2x_init_block(bp, BLOCK_MISC_AEU, init_phase);
        /* init aeu_mask_attn_func_0/1:
-        *  - SF mode: bits 3-7 are masked. only bits 0-2 are in use
-        *  - MF mode: bit 3 is masked. bits 0-2 are in use as in SF
+        *  - SF mode: bits 3-7 are masked. Only bits 0-2 are in use
+        *  - MF mode: bit 3 is masked. Bits 0-2 are in use as in SF
         *             bits 4-7 are used for "per vn group attention" */
        val = IS_MF(bp) ? 0xF7 : 0x7;
        /* Enable DCBX attention for all but E1 */
@@ -7275,7 +7366,6 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id, bool is_pf)
        while (!(REG_RD(bp, igu_addr_ack) & sb_bit) && --cnt)
                msleep(20);
 
-
        if (!(REG_RD(bp, igu_addr_ack) & sb_bit)) {
                DP(NETIF_MSG_HW,
                   "Unable to finish IGU cleanup: idu_sb_id %d offset %d bit %d (cnt %d)\n",
@@ -7295,7 +7385,6 @@ static void bnx2x_clear_func_ilt(struct bnx2x *bp, u32 func)
                bnx2x_ilt_wr(bp, i, 0);
 }
 
-
 static void bnx2x_init_searcher(struct bnx2x *bp)
 {
        int port = BP_PORT(bp);
@@ -7331,7 +7420,6 @@ static int bnx2x_reset_nic_mode(struct bnx2x *bp)
        int rc, i, port = BP_PORT(bp);
        int vlan_en = 0, mac_en[NUM_MACS];
 
-
        /* Close input from network */
        if (bp->mf_mode == SINGLE_FUNCTION) {
                bnx2x_set_rx_filter(&bp->link_params, 0);
@@ -7406,7 +7494,7 @@ int bnx2x_init_hw_func_cnic(struct bnx2x *bp)
        bnx2x_ilt_init_op_cnic(bp, INITOP_SET);
 
        if (CONFIGURE_NIC_MODE(bp)) {
-               /* Configrue searcher as part of function hw init */
+               /* Configure searcher as part of function hw init */
                bnx2x_init_searcher(bp);
 
                /* Reset NIC mode */
@@ -7479,8 +7567,7 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
        } else {
                /* Set NIC mode */
                REG_WR(bp, PRS_REG_NIC_MODE, 1);
-               DP(NETIF_MSG_IFUP, "NIC MODE configrued\n");
-
+               DP(NETIF_MSG_IFUP, "NIC MODE configured\n");
        }
 
        if (!CHIP_IS_E1x(bp)) {
@@ -7677,7 +7764,7 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
                        }
                        bnx2x_igu_clear_sb(bp, bp->igu_dsb_id);
 
-                       /* !!! these should become driver const once
+                       /* !!! These should become driver const once
                           rf-tool supports split-68 const */
                        REG_WR(bp, IGU_REG_SB_INT_BEFORE_MASK_LSB, 0);
                        REG_WR(bp, IGU_REG_SB_INT_BEFORE_MASK_MSB, 0);
@@ -7734,7 +7821,6 @@ static int bnx2x_init_hw_func(struct bnx2x *bp)
        return 0;
 }
 
-
 void bnx2x_free_mem_cnic(struct bnx2x *bp)
 {
        bnx2x_ilt_mem_op_cnic(bp, ILT_MEMOP_FREE);
@@ -7779,7 +7865,6 @@ void bnx2x_free_mem(struct bnx2x *bp)
        bnx2x_iov_free_mem(bp);
 }
 
-
 int bnx2x_alloc_mem_cnic(struct bnx2x *bp)
 {
        if (!CHIP_IS_E1x(bp))
@@ -7793,7 +7878,7 @@ int bnx2x_alloc_mem_cnic(struct bnx2x *bp)
                                       host_hc_status_block_e1x));
 
        if (CONFIGURE_NIC_MODE(bp) && !bp->t2)
-               /* allocate searcher T2 table, as it wan't allocated before */
+               /* allocate searcher T2 table, as it wasn't allocated before */
                BNX2X_PCI_ALLOC(bp->t2, &bp->t2_mapping, SRC_T2_SZ);
 
        /* write address to which L5 should insert its values */
@@ -8068,7 +8153,6 @@ void bnx2x_ilt_set_info(struct bnx2x *bp)
                   ilt_client->page_size,
                   ilt_client->flags,
                   ilog2(ilt_client->page_size >> 12));
-
        }
 
        if (CNIC_SUPPORT(bp)) {
@@ -8124,7 +8208,6 @@ void bnx2x_ilt_set_info(struct bnx2x *bp)
 static void bnx2x_pf_q_prep_init(struct bnx2x *bp,
        struct bnx2x_fastpath *fp, struct bnx2x_queue_init_params *init_params)
 {
-
        u8 cos;
        int cxt_index, cxt_offset;
 
@@ -8133,7 +8216,7 @@ static void bnx2x_pf_q_prep_init(struct bnx2x *bp,
                __set_bit(BNX2X_Q_FLG_HC, &init_params->rx.flags);
                __set_bit(BNX2X_Q_FLG_HC, &init_params->tx.flags);
 
-               /* If HC is supporterd, enable host coalescing in the transition
+               /* If HC is supported, enable host coalescing in the transition
                 * to INIT state.
                 */
                __set_bit(BNX2X_Q_FLG_HC_EN, &init_params->rx.flags);
@@ -8205,7 +8288,6 @@ static int bnx2x_setup_tx_only(struct bnx2x *bp, struct bnx2x_fastpath *fp,
        return bnx2x_queue_state_change(bp, q_params);
 }
 
-
 /**
  * bnx2x_setup_queue - setup queue
  *
@@ -8254,7 +8336,6 @@ int bnx2x_setup_queue(struct bnx2x *bp, struct bnx2x_fastpath *fp,
 
        DP(NETIF_MSG_IFUP, "init complete\n");
 
-
        /* Now move the Queue to the SETUP state... */
        memset(setup_params, 0, sizeof(*setup_params));
 
@@ -8315,7 +8396,6 @@ static int bnx2x_stop_queue(struct bnx2x *bp, int index)
        /* We want to wait for completion in this context */
        __set_bit(RAMROD_COMP_WAIT, &q_params.ramrod_flags);
 
-
        /* close tx-only connections */
        for (tx_index = FIRST_TX_ONLY_COS_INDEX;
             tx_index < fp->max_cos;
@@ -8369,7 +8449,6 @@ static int bnx2x_stop_queue(struct bnx2x *bp, int index)
        return bnx2x_queue_state_change(bp, &q_params);
 }
 
-
 static void bnx2x_reset_func(struct bnx2x *bp)
 {
        int port = BP_PORT(bp);
@@ -8422,7 +8501,7 @@ static void bnx2x_reset_func(struct bnx2x *bp)
                 * scan to complete
                 */
                for (i = 0; i < 200; i++) {
-                       msleep(10);
+                       usleep_range(10000, 20000);
                        if (!REG_RD(bp, TM_REG_LIN0_SCAN_ON + port*4))
                                break;
                }
@@ -8623,14 +8702,14 @@ static int bnx2x_func_wait_started(struct bnx2x *bp)
 
        /*
         * (assumption: No Attention from MCP at this stage)
-        * PMF probably in the middle of TXdisable/enable transaction
+        * PMF probably in the middle of TX disable/enable transaction
         * 1. Sync IRS for default SB
-        * 2. Sync SP queue - this guarantes us that attention handling started
-        * 3. Wait, that TXdisable/enable transaction completes
+        * 2. Sync SP queue - this guarantees us that attention handling started
+        * 3. Wait, that TX disable/enable transaction completes
         *
-        * 1+2 guranty that if DCBx attention was scheduled it already changed
-        * pending bit of transaction from STARTED-->TX_STOPPED, if we alredy
-        * received complettion for the transaction the state is TX_STOPPED.
+        * 1+2 guarantee that if DCBx attention was scheduled it already changed
+        * pending bit of transaction from STARTED-->TX_STOPPED, if we already
+        * received completion for the transaction the state is TX_STOPPED.
         * State will return to STARTED after completion of TX_STOPPED-->STARTED
         * transaction.
         */
@@ -8660,7 +8739,7 @@ static int bnx2x_func_wait_started(struct bnx2x *bp)
                struct bnx2x_func_state_params func_params = {NULL};
 
                DP(NETIF_MSG_IFDOWN,
-                  "Hmmm... unexpected function state! Forcing STARTED-->TX_ST0PPED-->STARTED\n");
+                  "Hmmm... Unexpected function state! Forcing STARTED-->TX_ST0PPED-->STARTED\n");
 
                func_params.f_obj = &bp->func_obj;
                __set_bit(RAMROD_DRV_CLR_ONLY,
@@ -8740,7 +8819,6 @@ void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode, bool keep_link)
 
        bnx2x_iov_chip_cleanup(bp);
 
-
        /*
         * Send the UNLOAD_REQUEST to the MCP. This will return if
         * this function should perform FUNC, PORT or COMMON HW
@@ -8750,7 +8828,7 @@ void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode, bool keep_link)
 
        /*
         * (assumption: No Attention from MCP at this stage)
-        * PMF probably in the middle of TXdisable/enable transaction
+        * PMF probably in the middle of TX disable/enable transaction
         */
        rc = bnx2x_func_wait_started(bp);
        if (rc) {
@@ -8813,7 +8891,6 @@ unload_error:
        if (rc)
                BNX2X_ERR("HW_RESET failed\n");
 
-
        /* Report UNLOAD_DONE to MCP */
        bnx2x_send_unload_done(bp, keep_link);
 }
@@ -9179,7 +9256,6 @@ static int bnx2x_process_kill(struct bnx2x *bp, bool global)
        if (!CHIP_IS_E1x(bp) && bnx2x_er_poll_igu_vq(bp))
                return -EAGAIN;
 
-
        /* TBD: Indicate that "process kill" is in progress to MCP */
 
        /* Clear "unprepared" bit */
@@ -9367,7 +9443,7 @@ static void bnx2x_parity_recover(struct bnx2x *bp)
                                 * the first leader that performs a
                                 * leader_reset() reset the global blocks in
                                 * order to clear global attentions. Otherwise
-                                * the the gates will remain closed for that
+                                * the gates will remain closed for that
                                 * engine.
                                 */
                                if (load_status ||
@@ -9480,14 +9556,12 @@ static void bnx2x_sp_rtnl_task(struct work_struct *work)
                return;
        }
 
-       /* if stop on error is defined no recovery flows should be executed */
+       if (unlikely(bp->recovery_state != BNX2X_RECOVERY_DONE)) {
 #ifdef BNX2X_STOP_ON_ERROR
-       BNX2X_ERR("recovery flow called but STOP_ON_ERROR defined so reset not done to allow debug dump,\n"
-                 "you will need to reboot when done\n");
-       goto sp_rtnl_not_reset;
+               BNX2X_ERR("recovery flow called but STOP_ON_ERROR defined so reset not done to allow debug dump,\n"
+                         "you will need to reboot when done\n");
+               goto sp_rtnl_not_reset;
 #endif
-
-       if (unlikely(bp->recovery_state != BNX2X_RECOVERY_DONE)) {
                /*
                 * Clear all pending SP commands as we are going to reset the
                 * function anyway.
@@ -9502,6 +9576,12 @@ static void bnx2x_sp_rtnl_task(struct work_struct *work)
        }
 
        if (test_and_clear_bit(BNX2X_SP_RTNL_TX_TIMEOUT, &bp->sp_rtnl_state)) {
+#ifdef BNX2X_STOP_ON_ERROR
+               BNX2X_ERR("recovery flow called but STOP_ON_ERROR defined so reset not done to allow debug dump,\n"
+                         "you will need to reboot when done\n");
+               goto sp_rtnl_not_reset;
+#endif
+
                /*
                 * Clear all pending SP commands as we are going to reset the
                 * function anyway.
@@ -9540,6 +9620,13 @@ sp_rtnl_not_reset:
                   "sending set mcast vf pf channel message from rtnl sp-task\n");
                bnx2x_vfpf_set_mcast(bp->dev);
        }
+       if (test_and_clear_bit(BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN,
+                              &bp->sp_rtnl_state)){
+               if (!test_bit(__LINK_STATE_NOCARRIER, &bp->dev->state)) {
+                       bnx2x_tx_disable(bp);
+                       BNX2X_ERR("PF indicated channel is not servicable anymore. This means this VF device is no longer operational\n");
+               }
+       }
 
        if (test_and_clear_bit(BNX2X_SP_RTNL_VFPF_STORM_RX_MODE,
                               &bp->sp_rtnl_state)) {
@@ -9647,7 +9734,6 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp,
                        wb_data[0] &= ~BMAC_CONTROL_RX_ENABLE;
                        REG_WR(bp, vals->bmac_addr, wb_data[0]);
                        REG_WR(bp, vals->bmac_addr + 0x4, wb_data[1]);
-
                }
                BNX2X_DEV_INFO("Disable emac Rx\n");
                vals->emac_addr = NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4;
@@ -9681,7 +9767,6 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp,
 
        if (mac_stopped)
                msleep(20);
-
 }
 
 #define BNX2X_PREV_UNDI_PROD_ADDR(p) (BAR_TSTRORM_INTMEM + 0x1508 + ((p) << 4))
@@ -9780,6 +9865,21 @@ static bool bnx2x_prev_is_path_marked(struct bnx2x *bp)
        return rc;
 }
 
+bool bnx2x_port_after_undi(struct bnx2x *bp)
+{
+       struct bnx2x_prev_path_list *entry;
+       bool val;
+
+       down(&bnx2x_prev_sem);
+
+       entry = bnx2x_prev_path_get_entry(bp);
+       val = !!(entry && (entry->undi & (1 << BP_PORT(bp))));
+
+       up(&bnx2x_prev_sem);
+
+       return val;
+}
+
 static int bnx2x_prev_mark_path(struct bnx2x *bp, bool after_undi)
 {
        struct bnx2x_prev_path_list *tmp_list;
@@ -9839,7 +9939,6 @@ static int bnx2x_do_flr(struct bnx2x *bp)
        u16 status;
        struct pci_dev *dev = bp->pdev;
 
-
        if (CHIP_IS_E1x(bp)) {
                BNX2X_DEV_INFO("FLR not supported in E1/E1H\n");
                return -EINVAL;
@@ -9986,7 +10085,6 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp)
 
                if (!timer_count)
                        BNX2X_ERR("Failed to empty BRB, hope for the best\n");
-
        }
 
        /* No packets are in the pipeline, path is ready for reset */
@@ -10036,7 +10134,6 @@ static int bnx2x_prev_unload(struct bnx2x *bp)
 {
        int time_counter = 10;
        u32 rc, fw, hw_lock_reg, hw_lock_val;
-       struct bnx2x_prev_path_list *prev_list;
        BNX2X_DEV_INFO("Entering Previous Unload Flow\n");
 
        /* clear hw from errors which may have resulted from an interrupted
@@ -10049,7 +10146,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp)
                      (MISC_REG_DRIVER_CONTROL_1 + BP_FUNC(bp) * 8) :
                      (MISC_REG_DRIVER_CONTROL_7 + (BP_FUNC(bp) - 6) * 8);
 
-       hw_lock_val = (REG_RD(bp, hw_lock_reg));
+       hw_lock_val = REG_RD(bp, hw_lock_reg);
        if (hw_lock_val) {
                if (hw_lock_val & HW_LOCK_RESOURCE_NVRAM) {
                        BNX2X_DEV_INFO("Release Previously held NVRAM lock\n");
@@ -10064,7 +10161,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp)
 
        if (MCPR_ACCESS_LOCK_LOCK & REG_RD(bp, MCP_REG_MCPR_ACCESS_LOCK)) {
                BNX2X_DEV_INFO("Release previously held alr\n");
-               REG_WR(bp, MCP_REG_MCPR_ACCESS_LOCK, 0);
+               bnx2x_release_alr(bp);
        }
 
        do {
@@ -10093,7 +10190,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp)
                        break;
                }
 
-               /* non-common reply from MCP night require looping */
+               /* non-common reply from MCP might require looping */
                rc = bnx2x_prev_unload_uncommon(bp);
                if (rc != BNX2X_PREV_WAIT_NEEDED)
                        break;
@@ -10107,8 +10204,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp)
        }
 
        /* Mark function if its port was used to boot from SAN */
-       prev_list = bnx2x_prev_path_get_entry(bp);
-       if (prev_list && (prev_list->undi & (1 << BP_PORT(bp))))
+       if (bnx2x_port_after_undi(bp))
                bp->link_params.feature_config_flags |=
                        FEATURE_CONFIG_BOOT_FROM_SAN;
 
@@ -10192,8 +10288,6 @@ static void bnx2x_get_common_hwinfo(struct bnx2x *bp)
 
        bnx2x_init_shmem(bp);
 
-
-
        bp->common.shmem2_base = REG_RD(bp, (BP_PATH(bp) ?
                                        MISC_REG_GENERIC_CR_1 :
                                        MISC_REG_GENERIC_CR_0));
@@ -10455,6 +10549,9 @@ static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg)
                                        PORT_HW_CFG_SPEED_CAPABILITY_D0_10G))
                        bp->port.supported[idx] &= ~SUPPORTED_10000baseT_Full;
 
+               if (!(bp->link_params.speed_cap_mask[idx] &
+                                       PORT_HW_CFG_SPEED_CAPABILITY_D0_20G))
+                       bp->port.supported[idx] &= ~SUPPORTED_20000baseKR2_Full;
        }
 
        BNX2X_DEV_INFO("supported 0x%x 0x%x\n", bp->port.supported[0],
@@ -10765,7 +10862,6 @@ void bnx2x_get_iscsi_info(struct bnx2x *bp)
         */
        if (!bp->cnic_eth_dev.max_iscsi_conn)
                bp->flags |= no_flags;
-
 }
 
 static void bnx2x_get_ext_wwn_info(struct bnx2x *bp, int func)
@@ -10782,12 +10878,56 @@ static void bnx2x_get_ext_wwn_info(struct bnx2x *bp, int func)
        bp->cnic_eth_dev.fcoe_wwn_node_name_lo =
                MF_CFG_RD(bp, func_ext_config[func].fcoe_wwn_node_name_lower);
 }
+
+static int bnx2x_shared_fcoe_funcs(struct bnx2x *bp)
+{
+       u8 count = 0;
+
+       if (IS_MF(bp)) {
+               u8 fid;
+
+               /* iterate over absolute function ids for this path: */
+               for (fid = BP_PATH(bp); fid < E2_FUNC_MAX * 2; fid += 2) {
+                       if (IS_MF_SD(bp)) {
+                               u32 cfg = MF_CFG_RD(bp,
+                                                   func_mf_config[fid].config);
+
+                               if (!(cfg & FUNC_MF_CFG_FUNC_HIDE) &&
+                                   ((cfg & FUNC_MF_CFG_PROTOCOL_MASK) ==
+                                           FUNC_MF_CFG_PROTOCOL_FCOE))
+                                       count++;
+                       } else {
+                               u32 cfg = MF_CFG_RD(bp,
+                                                   func_ext_config[fid].
+                                                                     func_cfg);
+
+                               if ((cfg & MACP_FUNC_CFG_FLAGS_ENABLED) &&
+                                   (cfg & MACP_FUNC_CFG_FLAGS_FCOE_OFFLOAD))
+                                       count++;
+                       }
+               }
+       } else { /* SF */
+               int port, port_cnt = CHIP_MODE_IS_4_PORT(bp) ? 2 : 1;
+
+               for (port = 0; port < port_cnt; port++) {
+                       u32 lic = SHMEM_RD(bp,
+                                          drv_lic_key[port].max_fcoe_conn) ^
+                                 FW_ENCODE_32BIT_PATTERN;
+                       if (lic)
+                               count++;
+               }
+       }
+
+       return count;
+}
+
 static void bnx2x_get_fcoe_info(struct bnx2x *bp)
 {
        int port = BP_PORT(bp);
        int func = BP_ABS_FUNC(bp);
        u32 max_fcoe_conn = FW_ENCODE_32BIT_PATTERN ^ SHMEM_RD(bp,
                                drv_lic_key[port].max_fcoe_conn);
+       u8 num_fcoe_func = bnx2x_shared_fcoe_funcs(bp);
 
        if (!CNIC_SUPPORT(bp)) {
                bp->flags |= NO_FCOE_FLAG;
@@ -10801,9 +10941,10 @@ static void bnx2x_get_fcoe_info(struct bnx2x *bp)
 
        /* Calculate the number of maximum allowed FCoE tasks */
        bp->cnic_eth_dev.max_fcoe_exchanges = MAX_NUM_FCOE_TASKS_PER_ENGINE;
-       if (IS_MF(bp) || CHIP_MODE_IS_4_PORT(bp))
-               bp->cnic_eth_dev.max_fcoe_exchanges /=
-                                               MAX_FCOE_FUNCS_PER_ENGINE;
+
+       /* check if FCoE resources must be shared between different functions */
+       if (num_fcoe_func)
+               bp->cnic_eth_dev.max_fcoe_exchanges /= num_fcoe_func;
 
        /* Read the WWN: */
        if (!IS_MF(bp)) {
@@ -11031,7 +11172,7 @@ static int bnx2x_get_hwinfo(struct bnx2x *bp)
        } else {
                bp->common.int_block = INT_BLOCK_IGU;
 
-               /* do not allow device reset during IGU info preocessing */
+               /* do not allow device reset during IGU info processing */
                bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_RESET);
 
                val = REG_RD(bp, IGU_REG_BLOCK_CONFIGURATION);
@@ -11110,7 +11251,7 @@ static int bnx2x_get_hwinfo(struct bnx2x *bp)
                                E1H_FUNC_MAX * sizeof(struct drv_func_mb);
                /*
                 * get mf configuration:
-                * 1. existence of MF configuration
+                * 1. Existence of MF configuration
                 * 2. MAC address must be legal (check only upper bytes)
                 *    for  Switch-Independent mode;
                 *    OVLAN must be legal for Switch-Dependent mode
@@ -11384,7 +11525,6 @@ static int bnx2x_init_bp(struct bnx2x *bp)
        mutex_init(&bp->fw_mb_mutex);
        spin_lock_init(&bp->stats_lock);
 
-
        INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task);
        INIT_DELAYED_WORK(&bp->sp_rtnl_task, bnx2x_sp_rtnl_task);
        INIT_DELAYED_WORK(&bp->period_task, bnx2x_period_task);
@@ -11393,7 +11533,7 @@ static int bnx2x_init_bp(struct bnx2x *bp)
                if (rc)
                        return rc;
        } else {
-               random_ether_addr(bp->dev->dev_addr);
+               eth_zero_addr(bp->dev->dev_addr);
        }
 
        bnx2x_set_modes_bitmap(bp);
@@ -11417,7 +11557,6 @@ static int bnx2x_init_bp(struct bnx2x *bp)
                bnx2x_prev_unload(bp);
        }
 
-
        if (CHIP_REV_IS_FPGA(bp))
                dev_err(&bp->pdev->dev, "FPGA detected\n");
 
@@ -11489,7 +11628,7 @@ static int bnx2x_init_bp(struct bnx2x *bp)
 
        /* We need at least one default status block for slow-path events,
         * second status block for the L2 queue, and a third status block for
-        * CNIC if supproted.
+        * CNIC if supported.
         */
        if (CNIC_SUPPORT(bp))
                bp->min_msix_vec_cnt = 3;
@@ -11497,10 +11636,11 @@ static int bnx2x_init_bp(struct bnx2x *bp)
                bp->min_msix_vec_cnt = 2;
        BNX2X_DEV_INFO("bp->min_msix_vec_cnt %d", bp->min_msix_vec_cnt);
 
+       bp->dump_preset_idx = 1;
+
        return rc;
 }
 
-
 /****************************************************************************
 * General service functions
 ****************************************************************************/
@@ -11585,9 +11725,6 @@ static int bnx2x_close(struct net_device *dev)
        /* Unload the driver, release IRQs */
        bnx2x_nic_unload(bp, UNLOAD_CLOSE, false);
 
-       /* Power off */
-       bnx2x_set_power_state(bp, PCI_D3hot);
-
        return 0;
 }
 
@@ -11852,6 +11989,10 @@ static int bnx2x_validate_addr(struct net_device *dev)
 {
        struct bnx2x *bp = netdev_priv(dev);
 
+       /* query the bulletin board for mac address configured by the PF */
+       if (IS_VF(bp))
+               bnx2x_sample_bulletin(bp);
+
        if (!bnx2x_is_valid_ether_addr(bp, dev->dev_addr)) {
                BNX2X_ERR("Non-valid Ethernet address\n");
                return -EADDRNOTAVAIL;
@@ -11878,12 +12019,16 @@ static const struct net_device_ops bnx2x_netdev_ops = {
        .ndo_setup_tc           = bnx2x_setup_tc,
 #ifdef CONFIG_BNX2X_SRIOV
        .ndo_set_vf_mac         = bnx2x_set_vf_mac,
-       .ndo_set_vf_vlan        = bnx2x_set_vf_vlan,
+       .ndo_set_vf_vlan        = bnx2x_set_vf_vlan,
        .ndo_get_vf_config      = bnx2x_get_vf_config,
 #endif
 #ifdef NETDEV_FCOE_WWNN
        .ndo_fcoe_get_wwn       = bnx2x_fcoe_get_wwn,
 #endif
+
+#ifdef CONFIG_NET_LL_RX_POLL
+       .ndo_ll_poll            = bnx2x_low_latency_recv,
+#endif
 };
 
 static int bnx2x_set_coherency_mask(struct bnx2x *bp)
@@ -11959,7 +12104,7 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev,
        }
 
        if (IS_PF(bp)) {
-               bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+               bp->pm_cap = pdev->pm_cap;
                if (bp->pm_cap == 0) {
                        dev_err(&bp->pdev->dev,
                                "Cannot find power management capability, aborting\n");
@@ -12008,8 +12153,6 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev,
        }
        BNX2X_DEV_INFO("me reg PF num: %d\n", bp->pf_num);
 
-       bnx2x_set_power_state(bp, PCI_D0);
-
        /* clean indirect addresses */
        pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS,
                               PCICFG_VENDOR_ID_OFFSET);
@@ -12094,15 +12237,26 @@ err_out:
        return rc;
 }
 
-static void bnx2x_get_pcie_width_speed(struct bnx2x *bp, int *width, int *speed)
+static void bnx2x_get_pcie_width_speed(struct bnx2x *bp, int *width,
+                                      enum bnx2x_pci_bus_speed *speed)
 {
-       u32 val = 0;
+       u32 link_speed, val = 0;
 
        pci_read_config_dword(bp->pdev, PCICFG_LINK_CONTROL, &val);
        *width = (val & PCICFG_LINK_WIDTH) >> PCICFG_LINK_WIDTH_SHIFT;
 
-       /* return value of 1=2.5GHz 2=5GHz */
-       *speed = (val & PCICFG_LINK_SPEED) >> PCICFG_LINK_SPEED_SHIFT;
+       link_speed = (val & PCICFG_LINK_SPEED) >> PCICFG_LINK_SPEED_SHIFT;
+
+       switch (link_speed) {
+       case 3:
+               *speed = BNX2X_PCI_LINK_SPEED_8000;
+               break;
+       case 2:
+               *speed = BNX2X_PCI_LINK_SPEED_5000;
+               break;
+       default:
+               *speed = BNX2X_PCI_LINK_SPEED_2500;
+       }
 }
 
 static int bnx2x_check_firmware(struct bnx2x *bp)
@@ -12327,7 +12481,6 @@ static void bnx2x_release_firmware(struct bnx2x *bp)
        bp->firmware = NULL;
 }
 
-
 static struct bnx2x_func_sp_drv_ops bnx2x_func_sp_drv = {
        .init_hw_cmn_chip = bnx2x_init_hw_common_chip,
        .init_hw_cmn      = bnx2x_init_hw_common,
@@ -12465,7 +12618,8 @@ static int bnx2x_init_one(struct pci_dev *pdev,
 {
        struct net_device *dev = NULL;
        struct bnx2x *bp;
-       int pcie_width, pcie_speed;
+       int pcie_width;
+       enum bnx2x_pci_bus_speed pcie_speed;
        int rc, max_non_def_sbs;
        int rx_count, tx_count, rss_count, doorbell_size;
        int max_cos_est;
@@ -12605,7 +12759,6 @@ static int bnx2x_init_one(struct pci_dev *pdev,
        }
        BNX2X_DEV_INFO("device name after netdev register %s\n", dev->name);
 
-
        if (!NO_FCOE(bp)) {
                /* Add storage MAC address */
                rtnl_lock();
@@ -12617,15 +12770,15 @@ static int bnx2x_init_one(struct pci_dev *pdev,
        BNX2X_DEV_INFO("got pcie width %d and speed %d\n",
                       pcie_width, pcie_speed);
 
-       BNX2X_DEV_INFO(
-               "%s (%c%d) PCI-E x%d %s found at mem %lx, IRQ %d, node addr %pM\n",
-                   board_info[ent->driver_data].name,
-                   (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4),
-                   pcie_width,
-                   ((!CHIP_IS_E2(bp) && pcie_speed == 2) ||
-                    (CHIP_IS_E2(bp) && pcie_speed == 1)) ?
-                   "5GHz (Gen2)" : "2.5GHz",
-                   dev->base_addr, bp->pdev->irq, dev->dev_addr);
+       BNX2X_DEV_INFO("%s (%c%d) PCI-E x%d %s found at mem %lx, IRQ %d, node addr %pM\n",
+                      board_info[ent->driver_data].name,
+                      (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4),
+                      pcie_width,
+                      pcie_speed == BNX2X_PCI_LINK_SPEED_2500 ? "2.5GHz" :
+                      pcie_speed == BNX2X_PCI_LINK_SPEED_5000 ? "5.0GHz" :
+                      pcie_speed == BNX2X_PCI_LINK_SPEED_8000 ? "8.0GHz" :
+                      "Unknown",
+                      dev->base_addr, bp->pdev->irq, dev->dev_addr);
 
        return 0;
 
@@ -12647,17 +12800,11 @@ init_one_exit:
        return rc;
 }
 
-static void bnx2x_remove_one(struct pci_dev *pdev)
+static void __bnx2x_remove(struct pci_dev *pdev,
+                          struct net_device *dev,
+                          struct bnx2x *bp,
+                          bool remove_netdev)
 {
-       struct net_device *dev = pci_get_drvdata(pdev);
-       struct bnx2x *bp;
-
-       if (!dev) {
-               dev_err(&pdev->dev, "BAD net device from bnx2x_init_one\n");
-               return;
-       }
-       bp = netdev_priv(dev);
-
        /* Delete storage MAC address */
        if (!NO_FCOE(bp)) {
                rtnl_lock();
@@ -12670,7 +12817,17 @@ static void bnx2x_remove_one(struct pci_dev *pdev)
        bnx2x_dcbnl_update_applist(bp, true);
 #endif
 
-       unregister_netdev(dev);
+       /* Close the interface - either directly or implicitly */
+       if (remove_netdev) {
+               unregister_netdev(dev);
+       } else {
+               rtnl_lock();
+               if (netif_running(dev))
+                       bnx2x_close(dev);
+               rtnl_unlock();
+       }
+
+       bnx2x_iov_remove_one(bp);
 
        /* Power on: we can't let PCI layer write to us while we are in D3 */
        if (IS_PF(bp))
@@ -12686,12 +12843,16 @@ static void bnx2x_remove_one(struct pci_dev *pdev)
        /* Make sure RESET task is not scheduled before continuing */
        cancel_delayed_work_sync(&bp->sp_rtnl_task);
 
-       bnx2x_iov_remove_one(bp);
-
        /* send message via vfpf channel to release the resources of this vf */
        if (IS_VF(bp))
                bnx2x_vfpf_release(bp);
 
+       /* Assumes no further PCIe PM changes will occur */
+       if (system_state == SYSTEM_POWER_OFF) {
+               pci_wake_from_d3(pdev, bp->wol);
+               pci_set_power_state(pdev, PCI_D3hot);
+       }
+
        if (bp->regview)
                iounmap(bp->regview);
 
@@ -12706,7 +12867,8 @@ static void bnx2x_remove_one(struct pci_dev *pdev)
        }
        bnx2x_free_mem_bp(bp);
 
-       free_netdev(dev);
+       if (remove_netdev)
+               free_netdev(dev);
 
        if (atomic_read(&pdev->enable_cnt) == 1)
                pci_release_regions(pdev);
@@ -12715,6 +12877,20 @@ static void bnx2x_remove_one(struct pci_dev *pdev)
        pci_set_drvdata(pdev, NULL);
 }
 
+static void bnx2x_remove_one(struct pci_dev *pdev)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct bnx2x *bp;
+
+       if (!dev) {
+               dev_err(&pdev->dev, "BAD net device from bnx2x_init_one\n");
+               return;
+       }
+       bp = netdev_priv(dev);
+
+       __bnx2x_remove(pdev, dev, bp, true);
+}
+
 static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
 {
        bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT;
@@ -12747,19 +12923,6 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
        return 0;
 }
 
-static void bnx2x_eeh_recover(struct bnx2x *bp)
-{
-       u32 val;
-
-       mutex_init(&bp->port.phy_mutex);
-
-
-       val = SHMEM_RD(bp, validity_map[BP_PORT(bp)]);
-       if ((val & (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB))
-               != (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB))
-               BNX2X_ERR("BAD MCP validity signature\n");
-}
-
 /**
  * bnx2x_io_error_detected - called when PCI error is detected
  * @pdev: Pointer to PCI device
@@ -12828,6 +12991,10 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev)
 
        if (netif_running(dev)) {
                BNX2X_ERR("IO slot reset --> driver unload\n");
+
+               /* MCP should have been reset; Need to wait for validity */
+               bnx2x_init_shmem(bp);
+
                if (IS_PF(bp) && SHMEM2_HAS(bp, drv_capabilities_flag)) {
                        u32 v;
 
@@ -12849,7 +13016,7 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev)
 
                bnx2x_prev_unload(bp);
 
-               /* We should have resetted the engine, so It's fair to
+               /* We should have reseted the engine, so It's fair to
                 * assume the FW will no longer write to the bnx2x driver.
                 */
                bnx2x_squeeze_objects(bp);
@@ -12886,8 +13053,6 @@ static void bnx2x_io_resume(struct pci_dev *pdev)
 
        rtnl_lock();
 
-       bnx2x_eeh_recover(bp);
-
        bp->fw_seq = SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) &
                                                        DRV_MSG_SEQ_NUMBER_MASK;
 
@@ -12905,6 +13070,29 @@ static const struct pci_error_handlers bnx2x_err_handler = {
        .resume         = bnx2x_io_resume,
 };
 
+static void bnx2x_shutdown(struct pci_dev *pdev)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct bnx2x *bp;
+
+       if (!dev)
+               return;
+
+       bp = netdev_priv(dev);
+       if (!bp)
+               return;
+
+       rtnl_lock();
+       netif_device_detach(dev);
+       rtnl_unlock();
+
+       /* Don't remove the netdevice, as there are scenarios which will cause
+        * the kernel to hang, e.g., when trying to remove bnx2i while the
+        * rootfs is mounted from SAN.
+        */
+       __bnx2x_remove(pdev, dev, bp, false);
+}
+
 static struct pci_driver bnx2x_pci_driver = {
        .name        = DRV_MODULE_NAME,
        .id_table    = bnx2x_pci_tbl,
@@ -12916,6 +13104,7 @@ static struct pci_driver bnx2x_pci_driver = {
 #ifdef CONFIG_BNX2X_SRIOV
        .sriov_configure = bnx2x_sriov_configure,
 #endif
+       .shutdown    = bnx2x_shutdown,
 };
 
 static int __init bnx2x_init(void)
@@ -12941,11 +13130,12 @@ static int __init bnx2x_init(void)
 static void __exit bnx2x_cleanup(void)
 {
        struct list_head *pos, *q;
+
        pci_unregister_driver(&bnx2x_pci_driver);
 
        destroy_workqueue(bnx2x_wq);
 
-       /* Free globablly allocated resources */
+       /* Free globally allocated resources */
        list_for_each_safe(pos, q, &bnx2x_prev_list) {
                struct bnx2x_prev_path_list *tmp =
                        list_entry(pos, struct bnx2x_prev_path_list, list);
@@ -12968,7 +13158,7 @@ module_exit(bnx2x_cleanup);
  * @bp:                driver handle
  * @set:       set or clear the CAM entry
  *
- * This function will wait until the ramdord completion returns.
+ * This function will wait until the ramrod completion returns.
  * Return 0 if success, -ENODEV if ramrod doesn't return.
  */
 static int bnx2x_set_iscsi_eth_mac_addr(struct bnx2x *bp)
@@ -12996,7 +13186,6 @@ static void bnx2x_cnic_sp_post(struct bnx2x *bp, int count)
        BUG_ON(bp->cnic_spq_pending < count);
        bp->cnic_spq_pending -= count;
 
-
        for (; bp->cnic_kwq_pending; bp->cnic_kwq_pending--) {
                u16 type =  (le16_to_cpu(bp->cnic_kwq_cons->hdr.type)
                                & SPE_HDR_CONN_TYPE) >>
@@ -13169,7 +13358,6 @@ static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid, u8 err)
        bnx2x_cnic_sp_post(bp, 0);
 }
 
-
 /* Called with netif_addr_lock_bh() taken.
  * Sets an rx_mode config for an iSCSI ETH client.
  * Doesn't block.
@@ -13210,7 +13398,6 @@ static void bnx2x_set_iscsi_eth_rx_mode(struct bnx2x *bp, bool start)
        }
 }
 
-
 static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl)
 {
        struct bnx2x *bp = netdev_priv(dev);
@@ -13398,7 +13585,6 @@ void bnx2x_setup_cnic_info(struct bnx2x *bp)
 {
        struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
 
-
        cp->ctx_tbl_offset = FUNC_ILT_BASE(BP_FUNC(bp)) +
                             bnx2x_cid_ilt_lines(bp);
        cp->starting_cid = bnx2x_cid_ilt_lines(bp) * ILT_PAGE_CIDS;
@@ -13434,7 +13620,6 @@ static int bnx2x_register_cnic(struct net_device *dev, struct cnic_ops *ops,
                        BNX2X_ERR("CNIC-related load failed\n");
                        return rc;
                }
-
        }
 
        bp->cnic_enabled = true;
index d22bc40..8e627b8 100644 (file)
@@ -35,6 +35,8 @@
 #define ATC_REG_ATC_INT_STS_CLR                                         0x1101c0
 /* [RW 5] Parity mask register #0 read/write */
 #define ATC_REG_ATC_PRTY_MASK                                   0x1101d8
+/* [R 5] Parity register #0 read */
+#define ATC_REG_ATC_PRTY_STS                                    0x1101cc
 /* [RC 5] Parity register #0 read clear */
 #define ATC_REG_ATC_PRTY_STS_CLR                                0x1101d0
 /* [RW 19] Interrupt mask register #0 read/write */
 #define PBF_REG_PBF_INT_STS                                     0x1401c8
 /* [RW 20] Parity mask register #0 read/write */
 #define PBF_REG_PBF_PRTY_MASK                                   0x1401e4
+/* [R 28] Parity register #0 read */
+#define PBF_REG_PBF_PRTY_STS                                    0x1401d8
 /* [RC 20] Parity register #0 read clear */
 #define PBF_REG_PBF_PRTY_STS_CLR                                0x1401dc
 /* [RW 16] The Ethernet type value for L2 tag 0 */
 #define TM_REG_TM_INT_STS                                       0x1640f0
 /* [RW 7] Parity mask register #0 read/write */
 #define TM_REG_TM_PRTY_MASK                                     0x16410c
+/* [R 7] Parity register #0 read */
+#define TM_REG_TM_PRTY_STS                                      0x164100
 /* [RC 7] Parity register #0 read clear */
 #define TM_REG_TM_PRTY_STS_CLR                                  0x164104
 /* [RW 8] The event id for aggregated interrupt 0 */
index 32a9609..8f03c98 100644 (file)
@@ -35,9 +35,9 @@
 /**
  * bnx2x_exe_queue_init - init the Exe Queue object
  *
- * @o:         poiter to the object
+ * @o:         pointer to the object
  * @exe_len:   length
- * @owner:     poiter to the owner
+ * @owner:     pointer to the owner
  * @validate:  validate function pointer
  * @optimize:  optimize function pointer
  * @exec:      execute function pointer
@@ -142,7 +142,6 @@ free_and_exit:
        spin_unlock_bh(&o->lock);
 
        return rc;
-
 }
 
 static inline void __bnx2x_exe_queue_reset_pending(
@@ -163,13 +162,11 @@ static inline void __bnx2x_exe_queue_reset_pending(
 static inline void bnx2x_exe_queue_reset_pending(struct bnx2x *bp,
                                                 struct bnx2x_exe_queue_obj *o)
 {
-
        spin_lock_bh(&o->lock);
 
        __bnx2x_exe_queue_reset_pending(bp, o);
 
        spin_unlock_bh(&o->lock);
-
 }
 
 /**
@@ -179,7 +176,7 @@ static inline void bnx2x_exe_queue_reset_pending(struct bnx2x *bp,
  * @o:                 queue
  * @ramrod_flags:      flags
  *
- * (Atomicy is ensured using the exe_queue->lock).
+ * (Atomicity is ensured using the exe_queue->lock).
  */
 static inline int bnx2x_exe_queue_step(struct bnx2x *bp,
                                       struct bnx2x_exe_queue_obj *o,
@@ -192,8 +189,7 @@ static inline int bnx2x_exe_queue_step(struct bnx2x *bp,
 
        spin_lock_bh(&o->lock);
 
-       /*
-        * Next step should not be performed until the current is finished,
+       /* Next step should not be performed until the current is finished,
         * unless a DRV_CLEAR_ONLY bit is set. In this case we just want to
         * properly clear object internals without sending any command to the FW
         * which also implies there won't be any completion to clear the
@@ -209,8 +205,7 @@ static inline int bnx2x_exe_queue_step(struct bnx2x *bp,
                }
        }
 
-       /*
-        * Run through the pending commands list and create a next
+       /* Run through the pending commands list and create a next
         * execution chunk.
         */
        while (!list_empty(&o->exe_queue)) {
@@ -220,8 +215,7 @@ static inline int bnx2x_exe_queue_step(struct bnx2x *bp,
 
                if (cur_len + elem->cmd_len <= o->exe_chunk_len) {
                        cur_len += elem->cmd_len;
-                       /*
-                        * Prevent from both lists being empty when moving an
+                       /* Prevent from both lists being empty when moving an
                         * element. This will allow the call of
                         * bnx2x_exe_queue_empty() without locking.
                         */
@@ -241,14 +235,12 @@ static inline int bnx2x_exe_queue_step(struct bnx2x *bp,
 
        rc = o->execute(bp, o->owner, &o->pending_comp, ramrod_flags);
        if (rc < 0)
-               /*
-                *  In case of an error return the commands back to the queue
-                *  and reset the pending_comp.
+               /* In case of an error return the commands back to the queue
+                * and reset the pending_comp.
                 */
                list_splice_init(&o->pending_comp, &o->exe_queue);
        else if (!rc)
-               /*
-                * If zero is returned, means there are no outstanding pending
+               /* If zero is returned, means there are no outstanding pending
                 * completions and we may dismiss the pending list.
                 */
                __bnx2x_exe_queue_reset_pending(bp, o);
@@ -308,7 +300,6 @@ static inline int bnx2x_state_wait(struct bnx2x *bp, int state,
        /* can take a while if any port is running */
        int cnt = 5000;
 
-
        if (CHIP_REV_IS_EMUL(bp))
                cnt *= 20;
 
@@ -456,7 +447,6 @@ static int bnx2x_get_n_elements(struct bnx2x *bp, struct bnx2x_vlan_mac_obj *o,
                        DP(BNX2X_MSG_SP, "copied element number %d to address %p element was:\n",
                           counter, next);
                        next += stride + size;
-
                }
        }
        return counter * ETH_ALEN;
@@ -518,7 +508,6 @@ static int bnx2x_check_vlan_mac_add(struct bnx2x *bp,
        return 0;
 }
 
-
 /* check_del() callbacks */
 static struct bnx2x_vlan_mac_registry_elem *
        bnx2x_check_mac_del(struct bnx2x *bp,
@@ -609,7 +598,6 @@ static bool bnx2x_check_move_always_err(
        return false;
 }
 
-
 static inline u8 bnx2x_vlan_mac_get_rx_tx_flag(struct bnx2x_vlan_mac_obj *o)
 {
        struct bnx2x_raw_obj *raw = &o->raw;
@@ -626,7 +614,6 @@ static inline u8 bnx2x_vlan_mac_get_rx_tx_flag(struct bnx2x_vlan_mac_obj *o)
        return rx_tx_flag;
 }
 
-
 void bnx2x_set_mac_in_nig(struct bnx2x *bp,
                          bool add, unsigned char *dev_addr, int index)
 {
@@ -693,7 +680,7 @@ static inline void bnx2x_vlan_mac_set_cmd_hdr_e2(struct bnx2x *bp,
  *
  * @cid:       connection id
  * @type:      BNX2X_FILTER_XXX_PENDING
- * @hdr:       poiter to header to setup
+ * @hdr:       pointer to header to setup
  * @rule_cnt:
  *
  * currently we always configure one rule and echo field to contain a CID and an
@@ -707,7 +694,6 @@ static inline void bnx2x_vlan_mac_set_rdata_hdr_e2(u32 cid, int type,
        hdr->rule_cnt = (u8)rule_cnt;
 }
 
-
 /* hw_config() callbacks */
 static void bnx2x_set_one_mac_e2(struct bnx2x *bp,
                                 struct bnx2x_vlan_mac_obj *o,
@@ -723,8 +709,7 @@ static void bnx2x_set_one_mac_e2(struct bnx2x *bp,
        unsigned long *vlan_mac_flags = &elem->cmd_data.vlan_mac.vlan_mac_flags;
        u8 *mac = elem->cmd_data.vlan_mac.u.mac.mac;
 
-       /*
-        * Set LLH CAM entry: currently only iSCSI and ETH macs are
+       /* Set LLH CAM entry: currently only iSCSI and ETH macs are
         * relevant. In addition, current implementation is tuned for a
         * single ETH MAC.
         *
@@ -879,8 +864,7 @@ static void bnx2x_set_one_mac_e1x(struct bnx2x *bp,
        struct bnx2x_raw_obj *raw = &o->raw;
        struct mac_configuration_cmd *config =
                (struct mac_configuration_cmd *)(raw->rdata);
-       /*
-        * 57710 and 57711 do not support MOVE command,
+       /* 57710 and 57711 do not support MOVE command,
         * so it's either ADD or DEL
         */
        bool add = (elem->cmd_data.vlan_mac.cmd == BNX2X_VLAN_MAC_ADD) ?
@@ -960,7 +944,6 @@ static void bnx2x_set_one_vlan_mac_e2(struct bnx2x *bp,
        u16 vlan = elem->cmd_data.vlan_mac.u.vlan_mac.vlan;
        u8 *mac = elem->cmd_data.vlan_mac.u.vlan_mac.mac;
 
-
        /* Reset the ramrod data buffer for the first rule */
        if (rule_idx == 0)
                memset(data, 0, sizeof(*data));
@@ -969,7 +952,7 @@ static void bnx2x_set_one_vlan_mac_e2(struct bnx2x *bp,
        bnx2x_vlan_mac_set_cmd_hdr_e2(bp, o, add, CLASSIFY_RULE_OPCODE_PAIR,
                                      &rule_entry->pair.header);
 
-       /* Set VLAN and MAC themselvs */
+       /* Set VLAN and MAC themselves */
        rule_entry->pair.vlan = cpu_to_le16(vlan);
        bnx2x_set_fw_mac_addr(&rule_entry->pair.mac_msb,
                              &rule_entry->pair.mac_mid,
@@ -1021,8 +1004,7 @@ static void bnx2x_set_one_vlan_mac_e1h(struct bnx2x *bp,
        struct bnx2x_raw_obj *raw = &o->raw;
        struct mac_configuration_cmd *config =
                (struct mac_configuration_cmd *)(raw->rdata);
-       /*
-        * 57710 and 57711 do not support MOVE command,
+       /* 57710 and 57711 do not support MOVE command,
         * so it's either ADD or DEL
         */
        bool add = (elem->cmd_data.vlan_mac.cmd == BNX2X_VLAN_MAC_ADD) ?
@@ -1046,7 +1028,7 @@ static void bnx2x_set_one_vlan_mac_e1h(struct bnx2x *bp,
  *
  * @bp:                device handle
  * @p:         command parameters
- * @ppos:      pointer to the cooky
+ * @ppos:      pointer to the cookie
  *
  * reconfigure next MAC/VLAN/VLAN-MAC element from the
  * previously configured elements list.
@@ -1054,7 +1036,7 @@ static void bnx2x_set_one_vlan_mac_e1h(struct bnx2x *bp,
  * from command parameters only RAMROD_COMP_WAIT bit in ramrod_flags is        taken
  * into an account
  *
- * pointer to the cooky  - that should be given back in the next call to make
+ * pointer to the cookie  - that should be given back in the next call to make
  * function handle the next element. If *ppos is set to NULL it will restart the
  * iterator. If returned *ppos == NULL this means that the last element has been
  * handled.
@@ -1102,8 +1084,7 @@ static int bnx2x_vlan_mac_restore(struct bnx2x *bp,
        return bnx2x_config_vlan_mac(bp, p);
 }
 
-/*
- * bnx2x_exeq_get_mac/bnx2x_exeq_get_vlan/bnx2x_exeq_get_vlan_mac return a
+/* bnx2x_exeq_get_mac/bnx2x_exeq_get_vlan/bnx2x_exeq_get_vlan_mac return a
  * pointer to an element with a specific criteria and NULL if such an element
  * hasn't been found.
  */
@@ -1187,8 +1168,7 @@ static inline int bnx2x_validate_vlan_mac_add(struct bnx2x *bp,
                return rc;
        }
 
-       /*
-        * Check if there is a pending ADD command for this
+       /* Check if there is a pending ADD command for this
         * MAC/VLAN/VLAN-MAC. Return an error if there is.
         */
        if (exeq->get(exeq, elem)) {
@@ -1196,8 +1176,7 @@ static inline int bnx2x_validate_vlan_mac_add(struct bnx2x *bp,
                return -EEXIST;
        }
 
-       /*
-        * TODO: Check the pending MOVE from other objects where this
+       /* TODO: Check the pending MOVE from other objects where this
         * object is a destination object.
         */
 
@@ -1240,8 +1219,7 @@ static inline int bnx2x_validate_vlan_mac_del(struct bnx2x *bp,
                return -EEXIST;
        }
 
-       /*
-        * Check if there are pending DEL or MOVE commands for this
+       /* Check if there are pending DEL or MOVE commands for this
         * MAC/VLAN/VLAN-MAC. Return an error if so.
         */
        memcpy(&query_elem, elem, sizeof(query_elem));
@@ -1292,8 +1270,7 @@ static inline int bnx2x_validate_vlan_mac_move(struct bnx2x *bp,
        struct bnx2x_exe_queue_obj *src_exeq = &src_o->exe_queue;
        struct bnx2x_exe_queue_obj *dest_exeq = &dest_o->exe_queue;
 
-       /*
-        * Check if we can perform this operation based on the current registry
+       /* Check if we can perform this operation based on the current registry
         * state.
         */
        if (!src_o->check_move(bp, src_o, dest_o,
@@ -1302,8 +1279,7 @@ static inline int bnx2x_validate_vlan_mac_move(struct bnx2x *bp,
                return -EINVAL;
        }
 
-       /*
-        * Check if there is an already pending DEL or MOVE command for the
+       /* Check if there is an already pending DEL or MOVE command for the
         * source object or ADD command for a destination object. Return an
         * error if so.
         */
@@ -1392,7 +1368,7 @@ static int bnx2x_remove_vlan_mac(struct bnx2x *bp,
 }
 
 /**
- * bnx2x_wait_vlan_mac - passivly wait for 5 seconds until all work completes.
+ * bnx2x_wait_vlan_mac - passively wait for 5 seconds until all work completes.
  *
  * @bp:                device handle
  * @o:         bnx2x_vlan_mac_obj
@@ -1550,9 +1526,8 @@ static inline int bnx2x_vlan_mac_get_registry_elem(
 
                /* Get a new CAM offset */
                if (!o->get_cam_offset(o, &reg_elem->cam_offset)) {
-                       /*
-                        * This shell never happen, because we have checked the
-                        * CAM availiability in the 'validate'.
+                       /* This shall never happen, because we have checked the
+                        * CAM availability in the 'validate'.
                         */
                        WARN_ON(1);
                        kfree(reg_elem);
@@ -1599,8 +1574,7 @@ static int bnx2x_execute_vlan_mac(struct bnx2x *bp,
        struct bnx2x_vlan_mac_registry_elem *reg_elem;
        enum bnx2x_vlan_mac_cmd cmd;
 
-       /*
-        * If DRIVER_ONLY execution is requested, cleanup a registry
+       /* If DRIVER_ONLY execution is requested, cleanup a registry
         * and exit. Otherwise send a ramrod to FW.
         */
        if (!drv_only) {
@@ -1609,11 +1583,10 @@ static int bnx2x_execute_vlan_mac(struct bnx2x *bp,
                /* Set pending */
                r->set_pending(r);
 
-               /* Fill tha ramrod data */
+               /* Fill the ramrod data */
                list_for_each_entry(elem, exe_chunk, link) {
                        cmd = elem->cmd_data.vlan_mac.cmd;
-                       /*
-                        * We will add to the target object in MOVE command, so
+                       /* We will add to the target object in MOVE command, so
                         * change the object for a CAM search.
                         */
                        if (cmd == BNX2X_VLAN_MAC_MOVE)
@@ -1646,12 +1619,11 @@ static int bnx2x_execute_vlan_mac(struct bnx2x *bp,
                                idx++;
                }
 
-               /*
-                *  No need for an explicit memory barrier here as long we would
-                *  need to ensure the ordering of writing to the SPQ element
-                *  and updating of the SPQ producer which involves a memory
-                *  read and we will have to put a full memory barrier there
-                *  (inside bnx2x_sp_post()).
+               /* No need for an explicit memory barrier here as long we would
+                * need to ensure the ordering of writing to the SPQ element
+                * and updating of the SPQ producer which involves a memory
+                * read and we will have to put a full memory barrier there
+                * (inside bnx2x_sp_post()).
                 */
 
                rc = bnx2x_sp_post(bp, o->ramrod_cmd, r->cid,
@@ -1766,8 +1738,7 @@ int bnx2x_config_vlan_mac(
                        return rc;
        }
 
-       /*
-        * If nothing will be executed further in this iteration we want to
+       /* If nothing will be executed further in this iteration we want to
         * return PENDING if there are pending commands
         */
        if (!bnx2x_exe_queue_empty(&o->exe_queue))
@@ -1786,13 +1757,11 @@ int bnx2x_config_vlan_mac(
                        return rc;
        }
 
-       /*
-        * RAMROD_COMP_WAIT is a superset of RAMROD_EXEC. If it was set
+       /* RAMROD_COMP_WAIT is a superset of RAMROD_EXEC. If it was set
         * then user want to wait until the last command is done.
         */
        if (test_bit(RAMROD_COMP_WAIT, &p->ramrod_flags)) {
-               /*
-                * Wait maximum for the current exe_queue length iterations plus
+               /* Wait maximum for the current exe_queue length iterations plus
                 * one (for the current pending command).
                 */
                int max_iterations = bnx2x_exe_queue_length(&o->exe_queue) + 1;
@@ -1818,8 +1787,6 @@ int bnx2x_config_vlan_mac(
        return rc;
 }
 
-
-
 /**
  * bnx2x_vlan_mac_del_all - delete elements with given vlan_mac_flags spec
  *
@@ -1829,7 +1796,7 @@ int bnx2x_config_vlan_mac(
  * @ramrod_flags:      execution flags to be used for this deletion
  *
  * if the last operation has completed successfully and there are no
- * moreelements left, positive value if the last operation has completed
+ * more elements left, positive value if the last operation has completed
  * successfully and there are more previously configured elements, negative
  * value is current operation has failed.
  */
@@ -1870,8 +1837,7 @@ static int bnx2x_vlan_mac_del_all(struct bnx2x *bp,
        p.ramrod_flags = *ramrod_flags;
        p.user_req.cmd = BNX2X_VLAN_MAC_DEL;
 
-       /*
-        * Add all but the last VLAN-MAC to the execution queue without actually
+       /* Add all but the last VLAN-MAC to the execution queue without actually
         * execution anything.
         */
        __clear_bit(RAMROD_COMP_WAIT, &p.ramrod_flags);
@@ -1934,7 +1900,6 @@ static inline void bnx2x_init_vlan_mac_common(struct bnx2x_vlan_mac_obj *o,
                           state, pstate, type);
 }
 
-
 void bnx2x_init_mac_obj(struct bnx2x *bp,
                        struct bnx2x_vlan_mac_obj *mac_obj,
                        u8 cl_id, u32 cid, u8 func_id, void *rdata,
@@ -2048,8 +2013,7 @@ void bnx2x_init_vlan_mac_obj(struct bnx2x *bp,
        /* CAM pool handling */
        vlan_mac_obj->get_credit = bnx2x_get_credit_vlan_mac;
        vlan_mac_obj->put_credit = bnx2x_put_credit_vlan_mac;
-       /*
-        * CAM offset is relevant for 57710 and 57711 chips only which have a
+       /* CAM offset is relevant for 57710 and 57711 chips only which have a
         * single CAM for both MACs and VLAN-MAC pairs. So the offset
         * will be taken from MACs' pool object only.
         */
@@ -2092,7 +2056,6 @@ void bnx2x_init_vlan_mac_obj(struct bnx2x *bp,
                                     bnx2x_execute_vlan_mac,
                                     bnx2x_exeq_get_vlan_mac);
        }
-
 }
 
 /* RX_MODE verbs: DROP_ALL/ACCEPT_ALL/ACCEPT_ALL_MULTI/ACCEPT_ALL_VLAN/NORMAL */
@@ -2117,12 +2080,12 @@ static int bnx2x_set_rx_mode_e1x(struct bnx2x *bp,
        struct tstorm_eth_mac_filter_config *mac_filters =
                (struct tstorm_eth_mac_filter_config *)p->rdata;
 
-       /* initial seeting is drop-all */
+       /* initial setting is drop-all */
        u8 drop_all_ucast = 1, drop_all_mcast = 1;
        u8 accp_all_ucast = 0, accp_all_bcast = 0, accp_all_mcast = 0;
        u8 unmatched_unicast = 0;
 
-    /* In e1x there we only take into account rx acceot flag since tx switching
+    /* In e1x there we only take into account rx accept flag since tx switching
      * isn't enabled. */
        if (test_bit(BNX2X_ACCEPT_UNICAST, &p->rx_accept_flags))
                /* accept matched ucast */
@@ -2245,7 +2208,6 @@ static inline void bnx2x_rx_mode_set_cmd_state_e2(struct bnx2x *bp,
        }
 
        cmd->state = cpu_to_le16(state);
-
 }
 
 static int bnx2x_set_rx_mode_e2(struct bnx2x *bp,
@@ -2286,9 +2248,7 @@ static int bnx2x_set_rx_mode_e2(struct bnx2x *bp,
                                               false);
        }
 
-
-       /*
-        * If FCoE Queue configuration has been requested configure the Rx and
+       /* If FCoE Queue configuration has been requested configure the Rx and
         * internal switching modes for this queue in separate rules.
         *
         * FCoE queue shell never be set to ACCEPT_ALL packets of any sort:
@@ -2324,8 +2284,7 @@ static int bnx2x_set_rx_mode_e2(struct bnx2x *bp,
                }
        }
 
-       /*
-        * Set the ramrod header (most importantly - number of rules to
+       /* Set the ramrod header (most importantly - number of rules to
         * configure).
         */
        bnx2x_rx_mode_set_rdata_hdr_e2(p->cid, &data->header, rule_idx);
@@ -2334,12 +2293,11 @@ static int bnx2x_set_rx_mode_e2(struct bnx2x *bp,
                         data->header.rule_cnt, p->rx_accept_flags,
                         p->tx_accept_flags);
 
-       /*
-        *  No need for an explicit memory barrier here as long we would
-        *  need to ensure the ordering of writing to the SPQ element
-        *  and updating of the SPQ producer which involves a memory
-        *  read and we will have to put a full memory barrier there
-        *  (inside bnx2x_sp_post()).
+       /* No need for an explicit memory barrier here as long we would
+        * need to ensure the ordering of writing to the SPQ element
+        * and updating of the SPQ producer which involves a memory
+        * read and we will have to put a full memory barrier there
+        * (inside bnx2x_sp_post()).
         */
 
        /* Send a ramrod */
@@ -2476,7 +2434,7 @@ static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp,
                cur_mac = (struct bnx2x_mcast_mac_elem *)
                          ((u8 *)new_cmd + sizeof(*new_cmd));
 
-               /* Push the MACs of the current command into the pendig command
+               /* Push the MACs of the current command into the pending command
                 * MACs list: FIFO
                 */
                list_for_each_entry(pos, &p->mcast_list, link) {
@@ -2909,7 +2867,6 @@ static int bnx2x_mcast_validate_e2(struct bnx2x *bp,
        default:
                BNX2X_ERR("Unknown command: %d\n", cmd);
                return -EINVAL;
-
        }
 
        /* Increase the total number of MACs pending to be configured */
@@ -3034,20 +2991,18 @@ static int bnx2x_mcast_setup_e2(struct bnx2x *bp,
        if (!o->total_pending_num)
                bnx2x_mcast_refresh_registry_e2(bp, o);
 
-       /*
-        * If CLEAR_ONLY was requested - don't send a ramrod and clear
+       /* If CLEAR_ONLY was requested - don't send a ramrod and clear
         * RAMROD_PENDING status immediately.
         */
        if (test_bit(RAMROD_DRV_CLR_ONLY, &p->ramrod_flags)) {
                raw->clear_pending(raw);
                return 0;
        } else {
-               /*
-                *  No need for an explicit memory barrier here as long we would
-                *  need to ensure the ordering of writing to the SPQ element
-                *  and updating of the SPQ producer which involves a memory
-                *  read and we will have to put a full memory barrier there
-                *  (inside bnx2x_sp_post()).
+               /* No need for an explicit memory barrier here as long we would
+                * need to ensure the ordering of writing to the SPQ element
+                * and updating of the SPQ producer which involves a memory
+                * read and we will have to put a full memory barrier there
+                * (inside bnx2x_sp_post()).
                 */
 
                /* Send a ramrod */
@@ -3121,7 +3076,7 @@ static inline void bnx2x_mcast_hdl_restore_e1h(struct bnx2x *bp,
        }
 }
 
-/* On 57711 we write the multicast MACs' aproximate match
+/* On 57711 we write the multicast MACs' approximate match
  * table by directly into the TSTORM's internal RAM. So we don't
  * really need to handle any tricks to make it work.
  */
@@ -3223,7 +3178,6 @@ static int bnx2x_mcast_validate_e1(struct bnx2x *bp,
        default:
                BNX2X_ERR("Unknown command: %d\n", cmd);
                return -EINVAL;
-
        }
 
        /* We want to ensure that commands are executed one by one for 57710.
@@ -3245,7 +3199,7 @@ static void bnx2x_mcast_revert_e1(struct bnx2x *bp,
 
        /* If current command hasn't been handled yet and we are
         * here means that it's meant to be dropped and we have to
-        * update the number of outstandling MACs accordingly.
+        * update the number of outstanding MACs accordingly.
         */
        if (p->mcast_list_len)
                o->total_pending_num -= o->max_cmd_len;
@@ -3342,7 +3296,6 @@ static inline int bnx2x_mcast_handle_restore_cmd_e1(
        return -1;
 }
 
-
 static inline int bnx2x_mcast_handle_pending_cmds_e1(
        struct bnx2x *bp, struct bnx2x_mcast_ramrod_params *p)
 {
@@ -3352,7 +3305,6 @@ static inline int bnx2x_mcast_handle_pending_cmds_e1(
        union bnx2x_mcast_config_data cfg_data = {NULL};
        int cnt = 0;
 
-
        /* If nothing to be done - return */
        if (list_empty(&o->pending_cmds_head))
                return 0;
@@ -3523,20 +3475,18 @@ static int bnx2x_mcast_setup_e1(struct bnx2x *bp,
        if (rc)
                return rc;
 
-       /*
-        * If CLEAR_ONLY was requested - don't send a ramrod and clear
+       /* If CLEAR_ONLY was requested - don't send a ramrod and clear
         * RAMROD_PENDING status immediately.
         */
        if (test_bit(RAMROD_DRV_CLR_ONLY, &p->ramrod_flags)) {
                raw->clear_pending(raw);
                return 0;
        } else {
-               /*
-                *  No need for an explicit memory barrier here as long we would
-                *  need to ensure the ordering of writing to the SPQ element
-                *  and updating of the SPQ producer which involves a memory
-                *  read and we will have to put a full memory barrier there
-                *  (inside bnx2x_sp_post()).
+               /* No need for an explicit memory barrier here as long we would
+                * need to ensure the ordering of writing to the SPQ element
+                * and updating of the SPQ producer which involves a memory
+                * read and we will have to put a full memory barrier there
+                * (inside bnx2x_sp_post()).
                 */
 
                /* Send a ramrod */
@@ -3550,7 +3500,6 @@ static int bnx2x_mcast_setup_e1(struct bnx2x *bp,
                /* Ramrod completion is pending */
                return 1;
        }
-
 }
 
 static int bnx2x_mcast_get_registry_size_exact(struct bnx2x_mcast_obj *o)
@@ -3848,7 +3797,6 @@ static bool bnx2x_credit_pool_always_true(struct bnx2x_credit_pool_obj *o,
        return true;
 }
 
-
 static bool bnx2x_credit_pool_get_entry(
        struct bnx2x_credit_pool_obj *o,
        int *offset)
@@ -3999,8 +3947,7 @@ void bnx2x_init_mac_credit_pool(struct bnx2x *bp,
 
        } else {
 
-               /*
-                * CAM credit is equaly divided between all active functions
+               /* CAM credit is equaly divided between all active functions
                 * on the PATH.
                 */
                if ((func_num > 0)) {
@@ -4009,8 +3956,7 @@ void bnx2x_init_mac_credit_pool(struct bnx2x *bp,
                        else
                                cam_sz = BNX2X_CAM_SIZE_EMUL;
 
-                       /*
-                        * No need for CAM entries handling for 57712 and
+                       /* No need for CAM entries handling for 57712 and
                         * newer.
                         */
                        bnx2x_init_credit_pool(p, -1, cam_sz);
@@ -4018,7 +3964,6 @@ void bnx2x_init_mac_credit_pool(struct bnx2x *bp,
                        /* this should never happen! Block MAC operations. */
                        bnx2x_init_credit_pool(p, 0, 0);
                }
-
        }
 }
 
@@ -4028,14 +3973,12 @@ void bnx2x_init_vlan_credit_pool(struct bnx2x *bp,
                                 u8 func_num)
 {
        if (CHIP_IS_E1x(bp)) {
-               /*
-                * There is no VLAN credit in HW on 57710 and 57711 only
+               /* There is no VLAN credit in HW on 57710 and 57711 only
                 * MAC / MAC-VLAN can be set
                 */
                bnx2x_init_credit_pool(p, 0, -1);
        } else {
-               /*
-                * CAM credit is equaly divided between all active functions
+               /* CAM credit is equally divided between all active functions
                 * on the PATH.
                 */
                if (func_num > 0) {
@@ -4051,7 +3994,7 @@ void bnx2x_init_vlan_credit_pool(struct bnx2x *bp,
 /**
  * bnx2x_debug_print_ind_table - prints the indirection table configuration.
  *
- * @bp:                driver hanlde
+ * @bp:                driver handle
  * @p:         pointer to rss configuration
  *
  * Prints it when NETIF_MSG_IFUP debug level is configured.
@@ -4164,12 +4107,11 @@ static int bnx2x_setup_rss(struct bnx2x *bp,
                data->capabilities |= ETH_RSS_UPDATE_RAMROD_DATA_UPDATE_RSS_KEY;
        }
 
-       /*
-        *  No need for an explicit memory barrier here as long we would
-        *  need to ensure the ordering of writing to the SPQ element
-        *  and updating of the SPQ producer which involves a memory
-        *  read and we will have to put a full memory barrier there
-        *  (inside bnx2x_sp_post()).
+       /* No need for an explicit memory barrier here as long we would
+        * need to ensure the ordering of writing to the SPQ element
+        * and updating of the SPQ producer which involves a memory
+        * read and we will have to put a full memory barrier there
+        * (inside bnx2x_sp_post()).
         */
 
        /* Send a ramrod */
@@ -4215,7 +4157,6 @@ int bnx2x_config_rss(struct bnx2x *bp,
        return rc;
 }
 
-
 void bnx2x_init_rss_config_obj(struct bnx2x *bp,
                               struct bnx2x_rss_config_obj *rss_obj,
                               u8 cl_id, u32 cid, u8 func_id, u8 engine_id,
@@ -4288,7 +4229,6 @@ int bnx2x_queue_state_change(struct bnx2x *bp,
        return !!test_bit(pending_bit, pending);
 }
 
-
 static int bnx2x_queue_set_pending(struct bnx2x_queue_sp_obj *obj,
                                   struct bnx2x_queue_state_params *params)
 {
@@ -4337,7 +4277,7 @@ static int bnx2x_queue_comp_cmd(struct bnx2x *bp,
        }
 
        if (o->next_tx_only >= o->max_cos)
-               /* >= becuase tx only must always be smaller than cos since the
+               /* >= because tx only must always be smaller than cos since the
                 * primary connection supports COS 0
                 */
                BNX2X_ERR("illegal value for next tx_only: %d. max cos was %d",
@@ -4403,7 +4343,6 @@ static void bnx2x_q_fill_init_general_data(struct bnx2x *bp,
        gen_data->mtu = cpu_to_le16(params->mtu);
        gen_data->func_id = o->func_id;
 
-
        gen_data->cos = params->cos;
 
        gen_data->traffic_type =
@@ -4530,7 +4469,6 @@ static void bnx2x_q_fill_init_rx_data(struct bnx2x_queue_sp_obj *o,
                cpu_to_le16(params->silent_removal_value);
        rx_data->silent_vlan_mask =
                cpu_to_le16(params->silent_removal_mask);
-
 }
 
 /* initialize the general, tx and rx parts of a queue object */
@@ -4652,12 +4590,11 @@ static inline int bnx2x_q_send_setup_e1x(struct bnx2x *bp,
        /* Fill the ramrod data */
        bnx2x_q_fill_setup_data_cmn(bp, params, rdata);
 
-       /*
-        *  No need for an explicit memory barrier here as long we would
-        *  need to ensure the ordering of writing to the SPQ element
-        *  and updating of the SPQ producer which involves a memory
-        *  read and we will have to put a full memory barrier there
-        *  (inside bnx2x_sp_post()).
+       /* No need for an explicit memory barrier here as long we would
+        * need to ensure the ordering of writing to the SPQ element
+        * and updating of the SPQ producer which involves a memory
+        * read and we will have to put a full memory barrier there
+        * (inside bnx2x_sp_post()).
         */
 
        return bnx2x_sp_post(bp, ramrod, o->cids[BNX2X_PRIMARY_CID_INDEX],
@@ -4681,12 +4618,11 @@ static inline int bnx2x_q_send_setup_e2(struct bnx2x *bp,
        bnx2x_q_fill_setup_data_cmn(bp, params, rdata);
        bnx2x_q_fill_setup_data_e2(bp, params, rdata);
 
-       /*
-        *  No need for an explicit memory barrier here as long we would
-        *  need to ensure the ordering of writing to the SPQ element
-        *  and updating of the SPQ producer which involves a memory
-        *  read and we will have to put a full memory barrier there
-        *  (inside bnx2x_sp_post()).
+       /* No need for an explicit memory barrier here as long we would
+        * need to ensure the ordering of writing to the SPQ element
+        * and updating of the SPQ producer which involves a memory
+        * read and we will have to put a full memory barrier there
+        * (inside bnx2x_sp_post()).
         */
 
        return bnx2x_sp_post(bp, ramrod, o->cids[BNX2X_PRIMARY_CID_INDEX],
@@ -4706,7 +4642,6 @@ static inline int bnx2x_q_send_setup_tx_only(struct bnx2x *bp,
                &params->params.tx_only;
        u8 cid_index = tx_only_params->cid_index;
 
-
        if (cid_index >= o->max_cos) {
                BNX2X_ERR("queue[%d]: cid_index (%d) is out of range\n",
                          o->cl_id, cid_index);
@@ -4727,12 +4662,11 @@ static inline int bnx2x_q_send_setup_tx_only(struct bnx2x *bp,
                         o->cids[cid_index], rdata->general.client_id,
                         rdata->general.sp_client_id, rdata->general.cos);
 
-       /*
-        *  No need for an explicit memory barrier here as long we would
-        *  need to ensure the ordering of writing to the SPQ element
-        *  and updating of the SPQ producer which involves a memory
-        *  read and we will have to put a full memory barrier there
-        *  (inside bnx2x_sp_post()).
+       /* No need for an explicit memory barrier here as long we would
+        * need to ensure the ordering of writing to the SPQ element
+        * and updating of the SPQ producer which involves a memory
+        * read and we will have to put a full memory barrier there
+        * (inside bnx2x_sp_post()).
         */
 
        return bnx2x_sp_post(bp, ramrod, o->cids[cid_index],
@@ -4761,7 +4695,7 @@ static void bnx2x_q_fill_update_data(struct bnx2x *bp,
                test_bit(BNX2X_Q_UPDATE_IN_VLAN_REM_CHNG,
                         &params->update_flags);
 
-       /* Outer VLAN sripping */
+       /* Outer VLAN stripping */
        data->outer_vlan_removal_enable_flg =
                test_bit(BNX2X_Q_UPDATE_OUT_VLAN_REM, &params->update_flags);
        data->outer_vlan_removal_change_flg =
@@ -4816,19 +4750,17 @@ static inline int bnx2x_q_send_update(struct bnx2x *bp,
                return -EINVAL;
        }
 
-
        /* Clear the ramrod data */
        memset(rdata, 0, sizeof(*rdata));
 
        /* Fill the ramrod data */
        bnx2x_q_fill_update_data(bp, o, update_params, rdata);
 
-       /*
-        *  No need for an explicit memory barrier here as long we would
-        *  need to ensure the ordering of writing to the SPQ element
-        *  and updating of the SPQ producer which involves a memory
-        *  read and we will have to put a full memory barrier there
-        *  (inside bnx2x_sp_post()).
+       /* No need for an explicit memory barrier here as long we would
+        * need to ensure the ordering of writing to the SPQ element
+        * and updating of the SPQ producer which involves a memory
+        * read and we will have to put a full memory barrier there
+        * (inside bnx2x_sp_post()).
         */
 
        return bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_CLIENT_UPDATE,
@@ -5038,8 +4970,7 @@ static int bnx2x_queue_chk_transition(struct bnx2x *bp,
                 &params->params.update;
        u8 next_tx_only = o->num_tx_only;
 
-       /*
-        * Forget all pending for completion commands if a driver only state
+       /* Forget all pending for completion commands if a driver only state
         * transition has been requested.
         */
        if (test_bit(RAMROD_DRV_CLR_ONLY, &params->ramrod_flags)) {
@@ -5047,8 +4978,7 @@ static int bnx2x_queue_chk_transition(struct bnx2x *bp,
                o->next_state = BNX2X_Q_STATE_MAX;
        }
 
-       /*
-        * Don't allow a next state transition if we are in the middle of
+       /* Don't allow a next state transition if we are in the middle of
         * the previous one.
         */
        if (o->pending) {
@@ -5257,8 +5187,7 @@ enum bnx2x_func_state bnx2x_func_get_state(struct bnx2x *bp,
        if (o->pending)
                return BNX2X_F_STATE_MAX;
 
-       /*
-        * unsure the order of reading of o->pending and o->state
+       /* unsure the order of reading of o->pending and o->state
         * o->pending should be read first
         */
        rmb();
@@ -5356,8 +5285,7 @@ static int bnx2x_func_chk_transition(struct bnx2x *bp,
        enum bnx2x_func_state state = o->state, next_state = BNX2X_F_STATE_MAX;
        enum bnx2x_func_cmd cmd = params->cmd;
 
-       /*
-        * Forget all pending for completion commands if a driver only state
+       /* Forget all pending for completion commands if a driver only state
         * transition has been requested.
         */
        if (test_bit(RAMROD_DRV_CLR_ONLY, &params->ramrod_flags)) {
@@ -5365,8 +5293,7 @@ static int bnx2x_func_chk_transition(struct bnx2x *bp,
                o->next_state = BNX2X_F_STATE_MAX;
        }
 
-       /*
-        * Don't allow a next state transition if we are in the middle of
+       /* Don't allow a next state transition if we are in the middle of
         * the previous one.
         */
        if (o->pending)
@@ -5539,7 +5466,7 @@ static int bnx2x_func_hw_init(struct bnx2x *bp,
                goto init_err;
        }
 
-       /* Handle the beginning of COMMON_XXX pases separatelly... */
+       /* Handle the beginning of COMMON_XXX pases separately... */
        switch (load_code) {
        case FW_MSG_CODE_DRV_LOAD_COMMON_CHIP:
                rc = bnx2x_func_init_cmn_chip(bp, drv);
@@ -5573,7 +5500,7 @@ static int bnx2x_func_hw_init(struct bnx2x *bp,
 init_err:
        drv->gunzip_end(bp);
 
-       /* In case of success, complete the comand immediatelly: no ramrods
+       /* In case of success, complete the command immediately: no ramrods
         * have been sent.
         */
        if (!rc)
@@ -5598,7 +5525,7 @@ static inline void bnx2x_func_reset_func(struct bnx2x *bp,
 }
 
 /**
- * bnx2x_func_reset_port - reser HW at port stage
+ * bnx2x_func_reset_port - reset HW at port stage
  *
  * @bp:                device handle
  * @drv:
@@ -5620,7 +5547,7 @@ static inline void bnx2x_func_reset_port(struct bnx2x *bp,
 }
 
 /**
- * bnx2x_func_reset_cmn - reser HW at common stage
+ * bnx2x_func_reset_cmn - reset HW at common stage
  *
  * @bp:                device handle
  * @drv:
@@ -5636,7 +5563,6 @@ static inline void bnx2x_func_reset_cmn(struct bnx2x *bp,
        drv->reset_hw_cmn(bp);
 }
 
-
 static inline int bnx2x_func_hw_reset(struct bnx2x *bp,
                                      struct bnx2x_func_state_params *params)
 {
@@ -5663,7 +5589,7 @@ static inline int bnx2x_func_hw_reset(struct bnx2x *bp,
                break;
        }
 
-       /* Complete the comand immediatelly: no ramrods have been sent. */
+       /* Complete the command immediately: no ramrods have been sent. */
        o->complete_cmd(bp, o, BNX2X_F_CMD_HW_RESET);
 
        return 0;
index 43c00bc..798dfe9 100644 (file)
@@ -34,8 +34,7 @@ enum {
        RAMROD_RESTORE,
         /* Execute the next command now */
        RAMROD_EXEC,
-       /*
-        * Don't add a new command and continue execution of posponed
+       /* Don't add a new command and continue execution of postponed
         * commands. If not set a new command will be added to the
         * pending commands list.
         */
@@ -129,8 +128,7 @@ enum bnx2x_vlan_mac_cmd {
 struct bnx2x_vlan_mac_data {
        /* Requested command: BNX2X_VLAN_MAC_XX */
        enum bnx2x_vlan_mac_cmd cmd;
-       /*
-        * used to contain the data related vlan_mac_flags bits from
+       /* used to contain the data related vlan_mac_flags bits from
         * ramrod parameters.
         */
        unsigned long vlan_mac_flags;
@@ -190,14 +188,10 @@ typedef struct bnx2x_exeq_elem *
                                     struct bnx2x_exeq_elem *elem);
 
 struct bnx2x_exe_queue_obj {
-       /*
-        * Commands pending for an execution.
-        */
+       /* Commands pending for an execution. */
        struct list_head        exe_queue;
 
-       /*
-        * Commands pending for an completion.
-        */
+       /* Commands pending for an completion. */
        struct list_head        pending_comp;
 
        spinlock_t              lock;
@@ -245,14 +239,13 @@ struct bnx2x_exe_queue_obj {
 };
 /***************** Classification verbs: Set/Del MAC/VLAN/VLAN-MAC ************/
 /*
- * Element in the VLAN_MAC registry list having all currenty configured
+ * Element in the VLAN_MAC registry list having all currently configured
  * rules.
  */
 struct bnx2x_vlan_mac_registry_elem {
        struct list_head        link;
 
-       /*
-        * Used to store the cam offset used for the mac/vlan/vlan-mac.
+       /* Used to store the cam offset used for the mac/vlan/vlan-mac.
         * Relevant for 57710 and 57711 only. VLANs and MACs share the
         * same CAM for these chips.
         */
@@ -310,7 +303,7 @@ struct bnx2x_vlan_mac_obj {
         * @param n number of elements to get
         * @param buf buffer preallocated by caller into which elements
         *            will be copied. Note elements are 4-byte aligned
-        *            so buffer size must be able to accomodate the
+        *            so buffer size must be able to accommodate the
         *            aligned elements.
         *
         * @return number of copied bytes
@@ -395,7 +388,7 @@ struct bnx2x_vlan_mac_obj {
         * @param bp
         * @param p Command parameters (RAMROD_COMP_WAIT bit in
         *          ramrod_flags is only taken into an account)
-        * @param ppos a pointer to the cooky that should be given back in the
+        * @param ppos a pointer to the cookie that should be given back in the
         *        next call to make function handle the next element. If
         *        *ppos is set to NULL it will restart the iterator.
         *        If returned *ppos == NULL this means that the last
@@ -408,7 +401,7 @@ struct bnx2x_vlan_mac_obj {
                       struct bnx2x_vlan_mac_registry_elem **ppos);
 
        /**
-        * Should be called on a completion arival.
+        * Should be called on a completion arrival.
         *
         * @param bp
         * @param o
@@ -447,7 +440,7 @@ void bnx2x_set_mac_in_nig(struct bnx2x *bp,
 
 /** RX_MODE verbs:DROP_ALL/ACCEPT_ALL/ACCEPT_ALL_MULTI/ACCEPT_ALL_VLAN/NORMAL */
 
-/* RX_MODE ramrod spesial flags: set in rx_mode_flags field in
+/* RX_MODE ramrod special flags: set in rx_mode_flags field in
  * a bnx2x_rx_mode_ramrod_params.
  */
 enum {
@@ -475,8 +468,7 @@ struct bnx2x_rx_mode_ramrod_params {
        unsigned long ramrod_flags;
        unsigned long rx_mode_flags;
 
-       /*
-        * rdata is either a pointer to eth_filter_rules_ramrod_data(e2) or to
+       /* rdata is either a pointer to eth_filter_rules_ramrod_data(e2) or to
         * a tstorm_eth_mac_filter_config (e1x).
         */
        void *rdata;
@@ -646,12 +638,11 @@ struct bnx2x_credit_pool_obj {
        /* Maximum allowed credit. put() will check against it. */
        int             pool_sz;
 
-       /*
-        *  Allocate a pool table statically.
+       /* Allocate a pool table statically.
         *
-        *  Currently the mamimum allowed size is MAX_MAC_CREDIT_E2(272)
+        * Currently the maximum allowed size is MAX_MAC_CREDIT_E2(272)
         *
-        *  The set bit in the table will mean that the entry is available.
+        * The set bit in the table will mean that the entry is available.
         */
 #define BNX2X_POOL_VEC_SIZE    (MAX_MAC_CREDIT_E2 / 64)
        u64             pool_mirror[BNX2X_POOL_VEC_SIZE];
@@ -832,7 +823,7 @@ enum {
        BNX2X_Q_FLG_TUN_INC_INNER_IP_ID
 };
 
-/* Queue type options: queue type may be a compination of below. */
+/* Queue type options: queue type may be a combination of below. */
 enum bnx2x_q_type {
        /** TODO: Consider moving both these flags into the init()
         *        ramrod params.
@@ -1002,10 +993,9 @@ struct bnx2x_queue_sp_obj {
        u8              cl_id;
        u8              func_id;
 
-       /*
-        * number of traffic classes supported by queue.
-        * The primary connection of the queue suppotrs the first traffic
-        * class. Any further traffic class is suppoted by a tx-only
+       /* number of traffic classes supported by queue.
+        * The primary connection of the queue supports the first traffic
+        * class. Any further traffic class is supported by a tx-only
         * connection.
         *
         * Therefore max_cos is also a number of valid entries in the cids
@@ -1021,7 +1011,7 @@ struct bnx2x_queue_sp_obj {
 
        /* BNX2X_Q_CMD_XX bits. This object implements "one
         * pending" paradigm but for debug and tracing purposes it's
-        * more convinient to have different bits for different
+        * more convenient to have different bits for different
         * commands.
         */
        unsigned long   pending;
@@ -1210,7 +1200,7 @@ struct bnx2x_func_sp_obj {
 
        /* BNX2X_FUNC_CMD_XX bits. This object implements "one
         * pending" paradigm but for debug and tracing purposes it's
-        * more convinient to have different bits for different
+        * more convenient to have different bits for different
         * commands.
         */
        unsigned long           pending;
@@ -1329,7 +1319,7 @@ void bnx2x_init_rx_mode_obj(struct bnx2x *bp,
  *
  * @p: Command parameters
  *
- * Return: 0 - if operation was successfull and there is no pending completions,
+ * Return: 0 - if operation was successful and there is no pending completions,
  *         positive number - if there are pending completions,
  *         negative - if there were errors
  */
@@ -1361,7 +1351,7 @@ void bnx2x_init_mcast_obj(struct bnx2x *bp,
  * the current command will be enqueued to the tail of the
  * pending commands list.
  *
- * Return: 0 is operation was successfull and there are no pending completions,
+ * Return: 0 is operation was successful and there are no pending completions,
  *         negative if there were errors, positive if there are pending
  *         completions.
  */
@@ -1377,7 +1367,6 @@ void bnx2x_init_vlan_credit_pool(struct bnx2x *bp,
                                 struct bnx2x_credit_pool_obj *p, u8 func_id,
                                 u8 func_num);
 
-
 /****************** RSS CONFIGURATION ****************/
 void bnx2x_init_rss_config_obj(struct bnx2x *bp,
                               struct bnx2x_rss_config_obj *rss_obj,
index 2ce7c74..95861ef 100644 (file)
@@ -1341,7 +1341,7 @@ int bnx2x_vfop_qdown_cmd(struct bnx2x *bp,
  */
 
 /* internal vf enable - until vf is enabled internally all transactions
- * are blocked. this routine should always be called last with pretend.
+ * are blocked. This routine should always be called last with pretend.
  */
 static void bnx2x_vf_enable_internal(struct bnx2x *bp, u8 enable)
 {
@@ -1459,21 +1459,16 @@ static u8 bnx2x_vf_is_pcie_pending(struct bnx2x *bp, u8 abs_vfid)
        struct bnx2x_virtf *vf = bnx2x_vf_by_abs_fid(bp, abs_vfid);
 
        if (!vf)
-               goto unknown_dev;
+               return false;
 
        dev = pci_get_bus_and_slot(vf->bus, vf->devfn);
        if (dev)
                return bnx2x_is_pcie_pending(dev);
-
-unknown_dev:
        return false;
 }
 
 int bnx2x_vf_flr_clnup_epilog(struct bnx2x *bp, u8 abs_vfid)
 {
-       /* Wait 100ms */
-       msleep(100);
-
        /* Verify no pending pci transactions */
        if (bnx2x_vf_is_pcie_pending(bp, abs_vfid))
                BNX2X_ERR("PCIE Transactions still pending\n");
@@ -1620,7 +1615,7 @@ next_vf_to_clean:
             i++)
                ;
 
-       DP(BNX2X_MSG_IOV, "next vf to cleanup: %d. num of vfs: %d\n", i,
+       DP(BNX2X_MSG_IOV, "next vf to cleanup: %d. Num of vfs: %d\n", i,
           BNX2X_NR_VIRTFN(bp));
 
        if (i < BNX2X_NR_VIRTFN(bp)) {
@@ -1743,7 +1738,7 @@ void bnx2x_iov_init_dq(struct bnx2x *bp)
        REG_WR(bp, DORQ_REG_VF_TYPE_MIN_MCID_0, 0);
        REG_WR(bp, DORQ_REG_VF_TYPE_MAX_MCID_0, 0x1ffff);
 
-       /* set the number of VF alllowed doorbells to the full DQ range */
+       /* set the number of VF allowed doorbells to the full DQ range */
        REG_WR(bp, DORQ_REG_VF_NORM_MAX_CID_COUNT, 0x20000);
 
        /* set the VF doorbell threshold */
@@ -2176,6 +2171,9 @@ int bnx2x_iov_nic_init(struct bnx2x *bp)
 
        DP(BNX2X_MSG_IOV, "num of vfs: %d\n", (bp)->vfdb->sriov.nr_virtfn);
 
+       /* let FLR complete ... */
+       msleep(100);
+
        /* initialize vf database */
        for_each_vf(bp, vfid) {
                struct bnx2x_virtf *vf = BP_VF(bp, vfid);
@@ -2403,7 +2401,7 @@ int bnx2x_iov_eq_sp_event(struct bnx2x *bp, union event_ring_elem *elem)
 
        /* extract vf and rxq index from vf_cid - relies on the following:
         * 1. vfid on cid reflects the true abs_vfid
-        * 2. the max number of VFs (per path) is 64
+        * 2. The max number of VFs (per path) is 64
         */
        qidx = cid & ((1 << BNX2X_VF_CID_WND)-1);
        abs_vfid = (cid >> BNX2X_VF_CID_WND) & (BNX2X_MAX_NUM_OF_VFS-1);
@@ -2461,7 +2459,7 @@ static struct bnx2x_virtf *bnx2x_vf_by_cid(struct bnx2x *bp, int vf_cid)
 {
        /* extract the vf from vf_cid - relies on the following:
         * 1. vfid on cid reflects the true abs_vfid
-        * 2. the max number of VFs (per path) is 64
+        * 2. The max number of VFs (per path) is 64
         */
        int abs_vfid = (vf_cid >> BNX2X_VF_CID_WND) & (BNX2X_MAX_NUM_OF_VFS-1);
        return bnx2x_vf_by_abs_fid(bp, abs_vfid);
@@ -2480,7 +2478,7 @@ void bnx2x_iov_set_queue_sp_obj(struct bnx2x *bp, int vf_cid,
        if (vf) {
                /* extract queue index from vf_cid - relies on the following:
                 * 1. vfid on cid reflects the true abs_vfid
-                * 2. the max number of VFs (per path) is 64
+                * 2. The max number of VFs (per path) is 64
                 */
                int q_index = vf_cid & ((1 << BNX2X_VF_CID_WND)-1);
                *q_obj = &bnx2x_vfq(vf, q_index, sp_obj);
@@ -2705,7 +2703,7 @@ int bnx2x_vf_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf,
        }
 
        /* static allocation:
-        * the global maximum number are fixed per VF. fail the request if
+        * the global maximum number are fixed per VF. Fail the request if
         * requested number exceed these globals
         */
        if (!bnx2x_vf_chk_avail_resc(bp, vf, resc)) {
@@ -2777,6 +2775,10 @@ int bnx2x_vf_init(struct bnx2x *bp, struct bnx2x_virtf *vf, dma_addr_t *sb_map)
                   vf->abs_vfid, vf->state);
                return -EINVAL;
        }
+
+       /* let FLR complete ... */
+       msleep(100);
+
        /* FLR cleanup epilogue */
        if (bnx2x_vf_flr_clnup_epilog(bp, vf->abs_vfid))
                return -EBUSY;
@@ -2890,7 +2892,7 @@ int bnx2x_vfop_close_cmd(struct bnx2x *bp,
        return -ENOMEM;
 }
 
-/* VF release can be called either: 1. the VF was acquired but
+/* VF release can be called either: 1. The VF was acquired but
  * not enabled 2. the vf was enabled or in the process of being
  * enabled
  */
@@ -3024,7 +3026,6 @@ void bnx2x_unlock_vf_pf_channel(struct bnx2x *bp, struct bnx2x_virtf *vf,
 
 int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs_param)
 {
-
        struct bnx2x *bp = netdev_priv(pci_get_drvdata(dev));
 
        DP(BNX2X_MSG_IOV, "bnx2x_sriov_configure called with %d, BNX2X_NR_VIRTFN(bp) was %d\n",
@@ -3032,7 +3033,7 @@ int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs_param)
 
        /* HW channel is only operational when PF is up */
        if (bp->state != BNX2X_STATE_OPEN) {
-               BNX2X_ERR("VF num configurtion via sysfs not supported while PF is down");
+               BNX2X_ERR("VF num configuration via sysfs not supported while PF is down\n");
                return -EINVAL;
        }
 
@@ -3086,6 +3087,11 @@ void bnx2x_disable_sriov(struct bnx2x *bp)
 static int bnx2x_vf_ndo_sanity(struct bnx2x *bp, int vfidx,
                               struct bnx2x_virtf *vf)
 {
+       if (bp->state != BNX2X_STATE_OPEN) {
+               BNX2X_ERR("vf ndo called though PF is down\n");
+               return -EINVAL;
+       }
+
        if (!IS_SRIOV(bp)) {
                BNX2X_ERR("vf ndo called though sriov is disabled\n");
                return -EINVAL;
@@ -3141,7 +3147,7 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx,
                        /* mac configured by ndo so its in bulletin board */
                        memcpy(&ivi->mac, bulletin->mac, ETH_ALEN);
                else
-                       /* funtion has not been loaded yet. Show mac as 0s */
+                       /* function has not been loaded yet. Show mac as 0s */
                        memset(&ivi->mac, 0, ETH_ALEN);
 
                /* vlan */
@@ -3149,7 +3155,7 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx,
                        /* vlan configured by ndo so its in bulletin board */
                        memcpy(&ivi->vlan, &bulletin->vlan, VLAN_HLEN);
                else
-                       /* funtion has not been loaded yet. Show vlans as 0s */
+                       /* function has not been loaded yet. Show vlans as 0s */
                        memset(&ivi->vlan, 0, VLAN_HLEN);
        }
 
@@ -3189,7 +3195,7 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac)
                return -EINVAL;
        }
 
-       /* update PF's copy of the VF's bulletin. will no longer accept mac
+       /* update PF's copy of the VF's bulletin. Will no longer accept mac
         * configuration requests from vf unless match this mac
         */
        bulletin->valid_bitmap |= 1 << MAC_ADDR_VALID;
@@ -3358,8 +3364,11 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos)
        return 0;
 }
 
-/* crc is the first field in the bulletin board. compute the crc over the
- * entire bulletin board excluding the crc field itself
+/* crc is the first field in the bulletin board. Compute the crc over the
+ * entire bulletin board excluding the crc field itself. Use the length field
+ * as the Bulletin Board was posted by a PF with possibly a different version
+ * from the vf which will sample it. Therefore, the length is computed by the
+ * PF and the used blindly by the VF.
  */
 u32 bnx2x_crc_vf_bulletin(struct bnx2x *bp,
                          struct pf_vf_bulletin_content *bulletin)
@@ -3389,7 +3398,7 @@ enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp)
                        if (bulletin.crc == bnx2x_crc_vf_bulletin(bp,
                                                                  &bulletin))
                                break;
-                       BNX2X_ERR("bad crc on bulletin board. contained %x computed %x\n",
+                       BNX2X_ERR("bad crc on bulletin board. Contained %x computed %x\n",
                                  bulletin.crc,
                                  bnx2x_crc_vf_bulletin(bp, &bulletin));
                }
@@ -3417,6 +3426,20 @@ enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp)
        return PFVF_BULLETIN_UPDATED;
 }
 
+void bnx2x_timer_sriov(struct bnx2x *bp)
+{
+       bnx2x_sample_bulletin(bp);
+
+       /* if channel is down we need to self destruct */
+       if (bp->old_bulletin.valid_bitmap & 1 << CHANNEL_DOWN) {
+               smp_mb__before_clear_bit();
+               set_bit(BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN,
+                       &bp->sp_rtnl_state);
+               smp_mb__after_clear_bit();
+               schedule_delayed_work(&bp->sp_rtnl_task, 0);
+       }
+}
+
 void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
 {
        /* vf doorbells are embedded within the regview */
@@ -3452,7 +3475,7 @@ int bnx2x_open_epilog(struct bnx2x *bp)
         * register_netdevice which must have rtnl lock taken. As we are holding
         * the lock right now, that could only work if the probe would not take
         * the lock. However, as the probe of the vf may be called from other
-        * contexts as well (such as passthrough to vm failes) it can't assume
+        * contexts as well (such as passthrough to vm fails) it can't assume
         * the lock is being held for it. Using delayed work here allows the
         * probe code to simply take the lock (i.e. wait for it to be released
         * if it is being held). We only want to do this if the number of VFs
@@ -3467,3 +3490,23 @@ int bnx2x_open_epilog(struct bnx2x *bp)
 
        return 0;
 }
+
+void bnx2x_iov_channel_down(struct bnx2x *bp)
+{
+       int vf_idx;
+       struct pf_vf_bulletin_content *bulletin;
+
+       if (!IS_SRIOV(bp))
+               return;
+
+       for_each_vf(bp, vf_idx) {
+               /* locate this VFs bulletin board and update the channel down
+                * bit
+                */
+               bulletin = BP_VF_BULLETIN(bp, vf_idx);
+               bulletin->valid_bitmap |= 1 << CHANNEL_DOWN;
+
+               /* update vf bulletin board */
+               bnx2x_post_vf_bulletin(bp, vf_idx);
+       }
+}
index d67ddc5..d143a7c 100644 (file)
@@ -197,7 +197,7 @@ struct bnx2x_virtf {
 
        u8 state;
 #define VF_FREE                0       /* VF ready to be acquired holds no resc */
-#define VF_ACQUIRED    1       /* VF aquired, but not initalized */
+#define VF_ACQUIRED    1       /* VF acquired, but not initialized */
 #define VF_ENABLED     2       /* VF Enabled */
 #define VF_RESET       3       /* VF FLR'd, pending cleanup */
 
@@ -496,7 +496,7 @@ enum {
                else if ((next) == VFOP_VERIFY_PEND)                    \
                        BNX2X_ERR("expected pending\n");                \
                else {                                                  \
-                       DP(BNX2X_MSG_IOV, "no ramrod. scheduling\n");   \
+                       DP(BNX2X_MSG_IOV, "no ramrod. Scheduling\n");   \
                        atomic_set(&vf->op_in_progress, 1);             \
                        queue_delayed_work(bnx2x_wq, &bp->sp_task, 0);  \
                        return;                                         \
@@ -722,7 +722,6 @@ u32 bnx2x_crc_vf_bulletin(struct bnx2x *bp,
                          struct pf_vf_bulletin_content *bulletin);
 int bnx2x_post_vf_bulletin(struct bnx2x *bp, int vf);
 
-
 enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp);
 
 /* VF side vfpf channel functions */
@@ -752,6 +751,7 @@ static inline int bnx2x_vf_ustorm_prods_offset(struct bnx2x *bp,
 }
 
 enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp);
+void bnx2x_timer_sriov(struct bnx2x *bp);
 void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp);
 int bnx2x_vf_pci_alloc(struct bnx2x *bp);
 int bnx2x_enable_sriov(struct bnx2x *bp);
@@ -762,6 +762,7 @@ static inline int bnx2x_vf_headroom(struct bnx2x *bp)
 }
 void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp);
 int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs);
+void bnx2x_iov_channel_down(struct bnx2x *bp);
 int bnx2x_open_epilog(struct bnx2x *bp);
 
 #else /* CONFIG_BNX2X_SRIOV */
@@ -809,6 +810,7 @@ static inline enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp
 {
        return PFVF_BULLETIN_UNCHANGED;
 }
+static inline void bnx2x_timer_sriov(struct bnx2x *bp) {}
 
 static inline void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
 {
@@ -818,6 +820,7 @@ static inline void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
 static inline int bnx2x_vf_pci_alloc(struct bnx2x *bp) {return 0; }
 static inline void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp) {}
 static inline int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs) {return 0; }
+static inline void bnx2x_iov_channel_down(struct bnx2x *bp) {}
 static inline int bnx2x_open_epilog(struct bnx2x *bp) {return 0; }
 
 #endif /* CONFIG_BNX2X_SRIOV */
index 2ca3d94..98366ab 100644 (file)
@@ -1002,7 +1002,6 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp)
                qstats->valid_bytes_received_lo =
                                        qstats->total_bytes_received_lo;
 
-
                UPDATE_EXTEND_TSTAT(rcv_ucast_pkts,
                                        total_unicast_packets_received);
                UPDATE_EXTEND_TSTAT(rcv_mcast_pkts,
index d117f47..853824d 100644 (file)
@@ -40,7 +40,6 @@ struct nig_stats {
        u32 egress_mac_pkt1_hi;
 };
 
-
 enum bnx2x_stats_event {
        STATS_EVENT_PMF = 0,
        STATS_EVENT_LINK_UP,
@@ -208,7 +207,6 @@ struct bnx2x_eth_stats {
        u32 eee_tx_lpi;
 };
 
-
 struct bnx2x_eth_q_stats {
        u32 total_unicast_bytes_received_hi;
        u32 total_unicast_bytes_received_lo;
@@ -331,7 +329,6 @@ struct bnx2x_fw_port_stats_old {
         u32 mac_discard;
 };
 
-
 /****************************************************************************
 * Macros
 ****************************************************************************/
@@ -536,7 +533,6 @@ struct bnx2x_fw_port_stats_old {
                SUB_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \
        } while (0)
 
-
 /* forward */
 struct bnx2x;
 
index 928b074..2088063 100644 (file)
@@ -113,7 +113,7 @@ static int bnx2x_send_msg2pf(struct bnx2x *bp, u8 *done, dma_addr_t msg_mapping)
 {
        struct cstorm_vf_zone_data __iomem *zone_data =
                REG_ADDR(bp, PXP_VF_ADDR_CSDM_GLOBAL_START);
-       int tout = 600, interval = 100; /* wait for 60 seconds */
+       int tout = 100, interval = 100; /* wait for 10 seconds */
 
        if (*done) {
                BNX2X_ERR("done was non zero before message to pf was sent\n");
@@ -121,6 +121,16 @@ static int bnx2x_send_msg2pf(struct bnx2x *bp, u8 *done, dma_addr_t msg_mapping)
                return -EINVAL;
        }
 
+       /* if PF indicated channel is down avoid sending message. Return success
+        * so calling flow can continue
+        */
+       bnx2x_sample_bulletin(bp);
+       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;
+       }
+
        /* Write message address */
        writel(U64_LO(msg_mapping),
               &zone_data->non_trigger.vf_pf_channel.msg_addr_lo);
@@ -233,7 +243,7 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count)
 
                attempts++;
 
-               /* test whether the PF accepted our request. If not, humble the
+               /* test whether the PF accepted our request. If not, humble
                 * the request and try again.
                 */
                if (bp->acquire_resp.hdr.status == PFVF_STATUS_SUCCESS) {
@@ -333,7 +343,7 @@ int bnx2x_vfpf_release(struct bnx2x *bp)
                DP(BNX2X_MSG_SP, "vf released\n");
        } else {
                /* PF reports error */
-               BNX2X_ERR("PF failed our release request - are we out of sync? response status: %d\n",
+               BNX2X_ERR("PF failed our release request - are we out of sync? Response status: %d\n",
                          resp->hdr.status);
                rc = -EAGAIN;
                goto out;
@@ -787,7 +797,7 @@ static inline void bnx2x_set_vf_mbxs_valid(struct bnx2x *bp)
                storm_memset_vf_mbx_valid(bp, bnx2x_vf(bp, i, abs_vfid));
 }
 
-/* enable vf_pf mailbox (aka vf-pf-chanell) */
+/* enable vf_pf mailbox (aka vf-pf-channel) */
 void bnx2x_vf_enable_mbx(struct bnx2x *bp, u8 abs_vfid)
 {
        bnx2x_vf_flr_clnup_epilog(bp, abs_vfid);
@@ -844,7 +854,6 @@ static int bnx2x_copy32_vf_dmae(struct bnx2x *bp, u8 from_vf,
                dmae.dst_addr_hi = vf_addr_hi;
        }
        dmae.len = len32;
-       bnx2x_dp_dmae(bp, &dmae, BNX2X_MSG_DMAE);
 
        /* issue the command and wait for completion */
        return bnx2x_issue_dmae_with_comp(bp, &dmae);
@@ -1072,7 +1081,7 @@ static void bnx2x_vf_mbx_set_q_flags(struct bnx2x *bp, u32 mbx_q_flags,
        if (mbx_q_flags & VFPF_QUEUE_FLG_DHC)
                __set_bit(BNX2X_Q_FLG_DHC, sp_q_flags);
 
-       /* outer vlan removal is set according to the PF's multi fuction mode */
+       /* outer vlan removal is set according to PF's multi function mode */
        if (IS_MF_SD(bp))
                __set_bit(BNX2X_Q_FLG_OV, sp_q_flags);
 }
@@ -1104,7 +1113,7 @@ static void bnx2x_vf_mbx_setup_q(struct bnx2x *bp, struct bnx2x_virtf *vf,
                struct bnx2x_queue_init_params *init_p;
                struct bnx2x_queue_setup_params *setup_p;
 
-               /* reinit the VF operation context */
+               /* re-init the VF operation context */
                memset(&vf->op_params.qctor, 0 , sizeof(vf->op_params.qctor));
                setup_p = &vf->op_params.qctor.prep_qsetup;
                init_p =  &vf->op_params.qctor.qstate.params.init;
@@ -1588,8 +1597,9 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf,
                 * support them. Or this may be because someone wrote a crappy
                 * VF driver and is sending garbage over the channel.
                 */
-               BNX2X_ERR("unknown TLV. type %d length %d. first 20 bytes of mailbox buffer:\n",
-                         mbx->first_tlv.tl.type, mbx->first_tlv.tl.length);
+               BNX2X_ERR("unknown TLV. type %d length %d vf->state was %d. first 20 bytes of mailbox buffer:\n",
+                         mbx->first_tlv.tl.type, mbx->first_tlv.tl.length,
+                         vf->state);
                for (i = 0; i < 20; i++)
                        DP_CONT(BNX2X_MSG_IOV, "%x ",
                                mbx->msg->req.tlv_buf_size.tlv_buffer[i]);
@@ -1605,8 +1615,11 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf,
                        bnx2x_vf_mbx_resp(bp, vf);
                } else {
                        /* can't send a response since this VF is unknown to us
-                        * just unlock the channel and be done with.
+                        * just ack the FW to release the mailbox and unlock
+                        * the channel.
                         */
+                       storm_memset_vf_mbx_ack(bp, vf->abs_vfid);
+                       mmiowb();
                        bnx2x_unlock_vf_pf_channel(bp, vf,
                                                   mbx->first_tlv.tl.type);
                }
index 41708fa..f3ad174 100644 (file)
@@ -331,7 +331,10 @@ struct pf_vf_bulletin_content {
 #define VLAN_VALID             1       /* when set, the vf should not access
                                         * the vfpf channel
                                         */
-
+#define CHANNEL_DOWN           2       /* vfpf channel is disabled. VFs are not
+                                        * to attempt to send messages on the
+                                        * channel after this bit is set
+                                        */
        u8 mac[ETH_ALEN];
        u8 mac_padding[2];
 
index 6b0dc13..d78d4cf 100644 (file)
@@ -5622,7 +5622,7 @@ static void cnic_rcv_netevent(struct cnic_local *cp, unsigned long event,
 static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
                                                         void *ptr)
 {
-       struct net_device *netdev = ptr;
+       struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
        struct cnic_dev *dev;
        int new_dev = 0;
 
index e80bfb6..c277771 100644 (file)
@@ -2197,7 +2197,7 @@ static const struct net_device_ops sbmac_netdev_ops = {
 
 static int sbmac_init(struct platform_device *pldev, long long base)
 {
-       struct net_device *dev = dev_get_drvdata(&pldev->dev);
+       struct net_device *dev = platform_get_drvdata(pldev);
        int idx = pldev->id;
        struct sbmac_softc *sc = netdev_priv(dev);
        unsigned char *eaddr;
@@ -2275,7 +2275,7 @@ static int sbmac_init(struct platform_device *pldev, long long base)
                       dev->name);
                goto free_mdio;
        }
-       dev_set_drvdata(&pldev->dev, sc->mii_bus);
+       platform_set_drvdata(pldev, sc->mii_bus);
 
        err = register_netdev(dev);
        if (err) {
@@ -2300,7 +2300,6 @@ static int sbmac_init(struct platform_device *pldev, long long base)
        return 0;
 unreg_mdio:
        mdiobus_unregister(sc->mii_bus);
-       dev_set_drvdata(&pldev->dev, NULL);
 free_mdio:
        mdiobus_free(sc->mii_bus);
 uninit_ctx:
@@ -2624,7 +2623,7 @@ static int sbmac_probe(struct platform_device *pldev)
                goto out_unmap;
        }
 
-       dev_set_drvdata(&pldev->dev, dev);
+       platform_set_drvdata(pldev, dev);
        SET_NETDEV_DEV(dev, &pldev->dev);
 
        sc = netdev_priv(dev);
@@ -2649,7 +2648,7 @@ out_out:
 
 static int __exit sbmac_remove(struct platform_device *pldev)
 {
-       struct net_device *dev = dev_get_drvdata(&pldev->dev);
+       struct net_device *dev = platform_get_drvdata(pldev);
        struct sbmac_softc *sc = netdev_priv(dev);
 
        unregister_netdev(dev);
index a13463e..d964f30 100644 (file)
@@ -968,9 +968,6 @@ static void tg3_ape_driver_state_change(struct tg3 *tp, int kind)
 
                event = APE_EVENT_STATUS_STATE_UNLOAD;
                break;
-       case RESET_KIND_SUSPEND:
-               event = APE_EVENT_STATUS_STATE_SUSPEND;
-               break;
        default:
                return;
        }
@@ -1317,8 +1314,8 @@ static int tg3_phy_toggle_auxctl_smdsp(struct tg3 *tp, bool enable)
 
        if (err)
                return err;
-       if (enable)
 
+       if (enable)
                val |= MII_TG3_AUXCTL_ACTL_SMDSP_ENA;
        else
                val &= ~MII_TG3_AUXCTL_ACTL_SMDSP_ENA;
@@ -1745,10 +1742,6 @@ static void tg3_write_sig_pre_reset(struct tg3 *tp, int kind)
                        break;
                }
        }
-
-       if (kind == RESET_KIND_INIT ||
-           kind == RESET_KIND_SUSPEND)
-               tg3_ape_driver_state_change(tp, kind);
 }
 
 /* tp->lock is held. */
@@ -1770,9 +1763,6 @@ static void tg3_write_sig_post_reset(struct tg3 *tp, int kind)
                        break;
                }
        }
-
-       if (kind == RESET_KIND_SHUTDOWN)
-               tg3_ape_driver_state_change(tp, kind);
 }
 
 /* tp->lock is held. */
@@ -2341,6 +2331,46 @@ static void tg3_phy_apply_otp(struct tg3 *tp)
        tg3_phy_toggle_auxctl_smdsp(tp, false);
 }
 
+static void tg3_eee_pull_config(struct tg3 *tp, struct ethtool_eee *eee)
+{
+       u32 val;
+       struct ethtool_eee *dest = &tp->eee;
+
+       if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP))
+               return;
+
+       if (eee)
+               dest = eee;
+
+       if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, TG3_CL45_D7_EEERES_STAT, &val))
+               return;
+
+       /* Pull eee_active */
+       if (val == TG3_CL45_D7_EEERES_STAT_LP_1000T ||
+           val == TG3_CL45_D7_EEERES_STAT_LP_100TX) {
+               dest->eee_active = 1;
+       } else
+               dest->eee_active = 0;
+
+       /* Pull lp advertised settings */
+       if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE, &val))
+               return;
+       dest->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val);
+
+       /* Pull advertised and eee_enabled settings */
+       if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &val))
+               return;
+       dest->eee_enabled = !!val;
+       dest->advertised = mmd_eee_adv_to_ethtool_adv_t(val);
+
+       /* Pull tx_lpi_enabled */
+       val = tr32(TG3_CPMU_EEE_MODE);
+       dest->tx_lpi_enabled = !!(val & TG3_CPMU_EEEMD_LPI_IN_TX);
+
+       /* Pull lpi timer value */
+       dest->tx_lpi_timer = tr32(TG3_CPMU_EEE_DBTMR1) & 0xffff;
+}
+
 static void tg3_phy_eee_adjust(struct tg3 *tp, bool current_link_up)
 {
        u32 val;
@@ -2364,11 +2394,8 @@ static void tg3_phy_eee_adjust(struct tg3 *tp, bool current_link_up)
 
                tw32(TG3_CPMU_EEE_CTRL, eeectl);
 
-               tg3_phy_cl45_read(tp, MDIO_MMD_AN,
-                                 TG3_CL45_D7_EEERES_STAT, &val);
-
-               if (val == TG3_CL45_D7_EEERES_STAT_LP_1000T ||
-                   val == TG3_CL45_D7_EEERES_STAT_LP_100TX)
+               tg3_eee_pull_config(tp, NULL);
+               if (tp->eee.eee_active)
                        tp->setlpicnt = 2;
        }
 
@@ -4192,6 +4219,8 @@ static int tg3_power_down_prepare(struct tg3 *tp)
 
        tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN);
 
+       tg3_ape_driver_state_change(tp, RESET_KIND_SHUTDOWN);
+
        return 0;
 }
 
@@ -4292,6 +4321,16 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl)
                /* Advertise 1000-BaseT EEE ability */
                if (advertise & ADVERTISED_1000baseT_Full)
                        val |= MDIO_AN_EEE_ADV_1000T;
+
+               if (!tp->eee.eee_enabled) {
+                       val = 0;
+                       tp->eee.advertised = 0;
+               } else {
+                       tp->eee.advertised = advertise &
+                                            (ADVERTISED_100baseT_Full |
+                                             ADVERTISED_1000baseT_Full);
+               }
+
                err = tg3_phy_cl45_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val);
                if (err)
                        val = 0;
@@ -4536,26 +4575,23 @@ static int tg3_init_5401phy_dsp(struct tg3 *tp)
 
 static bool tg3_phy_eee_config_ok(struct tg3 *tp)
 {
-       u32 val;
-       u32 tgtadv = 0;
-       u32 advertising = tp->link_config.advertising;
+       struct ethtool_eee eee;
 
        if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP))
                return true;
 
-       if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &val))
-               return false;
-
-       val &= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
-
-
-       if (advertising & ADVERTISED_100baseT_Full)
-               tgtadv |= MDIO_AN_EEE_ADV_100TX;
-       if (advertising & ADVERTISED_1000baseT_Full)
-               tgtadv |= MDIO_AN_EEE_ADV_1000T;
+       tg3_eee_pull_config(tp, &eee);
 
-       if (val != tgtadv)
-               return false;
+       if (tp->eee.eee_enabled) {
+               if (tp->eee.advertised != eee.advertised ||
+                   tp->eee.tx_lpi_timer != eee.tx_lpi_timer ||
+                   tp->eee.tx_lpi_enabled != eee.tx_lpi_enabled)
+                       return false;
+       } else {
+               /* EEE is disabled but we're advertising */
+               if (eee.advertised)
+                       return false;
+       }
 
        return true;
 }
@@ -4656,6 +4692,42 @@ static void tg3_clear_mac_status(struct tg3 *tp)
        udelay(40);
 }
 
+static void tg3_setup_eee(struct tg3 *tp)
+{
+       u32 val;
+
+       val = TG3_CPMU_EEE_LNKIDL_PCIE_NL0 |
+             TG3_CPMU_EEE_LNKIDL_UART_IDL;
+       if (tg3_chip_rev_id(tp) == CHIPREV_ID_57765_A0)
+               val |= TG3_CPMU_EEE_LNKIDL_APE_TX_MT;
+
+       tw32_f(TG3_CPMU_EEE_LNKIDL_CTRL, val);
+
+       tw32_f(TG3_CPMU_EEE_CTRL,
+              TG3_CPMU_EEE_CTRL_EXIT_20_1_US);
+
+       val = TG3_CPMU_EEEMD_ERLY_L1_XIT_DET |
+             (tp->eee.tx_lpi_enabled ? TG3_CPMU_EEEMD_LPI_IN_TX : 0) |
+             TG3_CPMU_EEEMD_LPI_IN_RX |
+             TG3_CPMU_EEEMD_EEE_ENABLE;
+
+       if (tg3_asic_rev(tp) != ASIC_REV_5717)
+               val |= TG3_CPMU_EEEMD_SND_IDX_DET_EN;
+
+       if (tg3_flag(tp, ENABLE_APE))
+               val |= TG3_CPMU_EEEMD_APE_TX_DET_EN;
+
+       tw32_f(TG3_CPMU_EEE_MODE, tp->eee.eee_enabled ? val : 0);
+
+       tw32_f(TG3_CPMU_EEE_DBTMR1,
+              TG3_CPMU_DBTMR1_PCIEXIT_2047US |
+              (tp->eee.tx_lpi_timer & 0xffff));
+
+       tw32_f(TG3_CPMU_EEE_DBTMR2,
+              TG3_CPMU_DBTMR2_APE_TX_2047US |
+              TG3_CPMU_DBTMR2_TXIDXEQ_2047US);
+}
+
 static int tg3_setup_copper_phy(struct tg3 *tp, bool force_reset)
 {
        bool current_link_up;
@@ -4822,8 +4894,10 @@ static int tg3_setup_copper_phy(struct tg3 *tp, bool force_reset)
                         */
                        if (!eee_config_ok &&
                            (tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) &&
-                           !force_reset)
+                           !force_reset) {
+                               tg3_setup_eee(tp);
                                tg3_phy_reset(tp);
+                       }
                } else {
                        if (!(bmcr & BMCR_ANENABLE) &&
                            tp->link_config.speed == current_speed &&
@@ -6335,9 +6409,7 @@ static void tg3_tx_recover(struct tg3 *tp)
                    "Please report the problem to the driver maintainer "
                    "and include system chipset information.\n");
 
-       spin_lock(&tp->lock);
        tg3_flag_set(tp, TX_RECOVERY_PENDING);
-       spin_unlock(&tp->lock);
 }
 
 static inline u32 tg3_tx_avail(struct tg3_napi *tnapi)
@@ -9205,11 +9277,9 @@ static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec)
 }
 
 /* tp->lock is held. */
-static void tg3_rings_reset(struct tg3 *tp)
+static void tg3_tx_rcbs_disable(struct tg3 *tp)
 {
-       int i;
-       u32 stblk, txrcb, rxrcb, limit;
-       struct tg3_napi *tnapi = &tp->napi[0];
+       u32 txrcb, limit;
 
        /* Disable all transmit rings but the first. */
        if (!tg3_flag(tp, 5705_PLUS))
@@ -9226,7 +9296,33 @@ static void tg3_rings_reset(struct tg3 *tp)
             txrcb < limit; txrcb += TG3_BDINFO_SIZE)
                tg3_write_mem(tp, txrcb + TG3_BDINFO_MAXLEN_FLAGS,
                              BDINFO_FLAGS_DISABLED);
+}
+
+/* tp->lock is held. */
+static void tg3_tx_rcbs_init(struct tg3 *tp)
+{
+       int i = 0;
+       u32 txrcb = NIC_SRAM_SEND_RCB;
+
+       if (tg3_flag(tp, ENABLE_TSS))
+               i++;
+
+       for (; i < tp->irq_max; i++, txrcb += TG3_BDINFO_SIZE) {
+               struct tg3_napi *tnapi = &tp->napi[i];
+
+               if (!tnapi->tx_ring)
+                       continue;
+
+               tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping,
+                              (TG3_TX_RING_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT),
+                              NIC_SRAM_TX_BUFFER_DESC);
+       }
+}
 
+/* tp->lock is held. */
+static void tg3_rx_ret_rcbs_disable(struct tg3 *tp)
+{
+       u32 rxrcb, limit;
 
        /* Disable all receive return rings but the first. */
        if (tg3_flag(tp, 5717_PLUS))
@@ -9244,6 +9340,39 @@ static void tg3_rings_reset(struct tg3 *tp)
             rxrcb < limit; rxrcb += TG3_BDINFO_SIZE)
                tg3_write_mem(tp, rxrcb + TG3_BDINFO_MAXLEN_FLAGS,
                              BDINFO_FLAGS_DISABLED);
+}
+
+/* tp->lock is held. */
+static void tg3_rx_ret_rcbs_init(struct tg3 *tp)
+{
+       int i = 0;
+       u32 rxrcb = NIC_SRAM_RCV_RET_RCB;
+
+       if (tg3_flag(tp, ENABLE_RSS))
+               i++;
+
+       for (; i < tp->irq_max; i++, rxrcb += TG3_BDINFO_SIZE) {
+               struct tg3_napi *tnapi = &tp->napi[i];
+
+               if (!tnapi->rx_rcb)
+                       continue;
+
+               tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping,
+                              (tp->rx_ret_ring_mask + 1) <<
+                               BDINFO_FLAGS_MAXLEN_SHIFT, 0);
+       }
+}
+
+/* tp->lock is held. */
+static void tg3_rings_reset(struct tg3 *tp)
+{
+       int i;
+       u32 stblk;
+       struct tg3_napi *tnapi = &tp->napi[0];
+
+       tg3_tx_rcbs_disable(tp);
+
+       tg3_rx_ret_rcbs_disable(tp);
 
        /* Disable interrupts */
        tw32_mailbox_f(tp->napi[0].int_mbox, 1);
@@ -9280,9 +9409,6 @@ static void tg3_rings_reset(struct tg3 *tp)
                        tw32_tx_mbox(mbox + i * 8, 0);
        }
 
-       txrcb = NIC_SRAM_SEND_RCB;
-       rxrcb = NIC_SRAM_RCV_RET_RCB;
-
        /* Clear status block in ram. */
        memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE);
 
@@ -9292,46 +9418,20 @@ static void tg3_rings_reset(struct tg3 *tp)
        tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW,
             ((u64) tnapi->status_mapping & 0xffffffff));
 
-       if (tnapi->tx_ring) {
-               tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping,
-                              (TG3_TX_RING_SIZE <<
-                               BDINFO_FLAGS_MAXLEN_SHIFT),
-                              NIC_SRAM_TX_BUFFER_DESC);
-               txrcb += TG3_BDINFO_SIZE;
-       }
-
-       if (tnapi->rx_rcb) {
-               tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping,
-                              (tp->rx_ret_ring_mask + 1) <<
-                               BDINFO_FLAGS_MAXLEN_SHIFT, 0);
-               rxrcb += TG3_BDINFO_SIZE;
-       }
-
        stblk = HOSTCC_STATBLCK_RING1;
 
        for (i = 1, tnapi++; i < tp->irq_cnt; i++, tnapi++) {
                u64 mapping = (u64)tnapi->status_mapping;
                tw32(stblk + TG3_64BIT_REG_HIGH, mapping >> 32);
                tw32(stblk + TG3_64BIT_REG_LOW, mapping & 0xffffffff);
+               stblk += 8;
 
                /* Clear status block in ram. */
                memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE);
-
-               if (tnapi->tx_ring) {
-                       tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping,
-                                      (TG3_TX_RING_SIZE <<
-                                       BDINFO_FLAGS_MAXLEN_SHIFT),
-                                      NIC_SRAM_TX_BUFFER_DESC);
-                       txrcb += TG3_BDINFO_SIZE;
-               }
-
-               tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping,
-                              ((tp->rx_ret_ring_mask + 1) <<
-                               BDINFO_FLAGS_MAXLEN_SHIFT), 0);
-
-               stblk += 8;
-               rxrcb += TG3_BDINFO_SIZE;
        }
+
+       tg3_tx_rcbs_init(tp);
+       tg3_rx_ret_rcbs_init(tp);
 }
 
 static void tg3_setup_rxbd_thresholds(struct tg3 *tp)
@@ -9531,46 +9631,17 @@ static int tg3_reset_hw(struct tg3 *tp, bool reset_phy)
        if (tg3_flag(tp, INIT_COMPLETE))
                tg3_abort_hw(tp, 1);
 
-       /* Enable MAC control of LPI */
-       if (tp->phy_flags & TG3_PHYFLG_EEE_CAP) {
-               val = TG3_CPMU_EEE_LNKIDL_PCIE_NL0 |
-                     TG3_CPMU_EEE_LNKIDL_UART_IDL;
-               if (tg3_chip_rev_id(tp) == CHIPREV_ID_57765_A0)
-                       val |= TG3_CPMU_EEE_LNKIDL_APE_TX_MT;
-
-               tw32_f(TG3_CPMU_EEE_LNKIDL_CTRL, val);
-
-               tw32_f(TG3_CPMU_EEE_CTRL,
-                      TG3_CPMU_EEE_CTRL_EXIT_20_1_US);
-
-               val = TG3_CPMU_EEEMD_ERLY_L1_XIT_DET |
-                     TG3_CPMU_EEEMD_LPI_IN_TX |
-                     TG3_CPMU_EEEMD_LPI_IN_RX |
-                     TG3_CPMU_EEEMD_EEE_ENABLE;
-
-               if (tg3_asic_rev(tp) != ASIC_REV_5717)
-                       val |= TG3_CPMU_EEEMD_SND_IDX_DET_EN;
-
-               if (tg3_flag(tp, ENABLE_APE))
-                       val |= TG3_CPMU_EEEMD_APE_TX_DET_EN;
-
-               tw32_f(TG3_CPMU_EEE_MODE, val);
-
-               tw32_f(TG3_CPMU_EEE_DBTMR1,
-                      TG3_CPMU_DBTMR1_PCIEXIT_2047US |
-                      TG3_CPMU_DBTMR1_LNKIDLE_2047US);
-
-               tw32_f(TG3_CPMU_EEE_DBTMR2,
-                      TG3_CPMU_DBTMR2_APE_TX_2047US |
-                      TG3_CPMU_DBTMR2_TXIDXEQ_2047US);
-       }
-
        if ((tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) &&
            !(tp->phy_flags & TG3_PHYFLG_USER_CONFIGURED)) {
                tg3_phy_pull_config(tp);
+               tg3_eee_pull_config(tp, NULL);
                tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED;
        }
 
+       /* Enable MAC control of LPI */
+       if (tp->phy_flags & TG3_PHYFLG_EEE_CAP)
+               tg3_setup_eee(tp);
+
        if (reset_phy)
                tg3_phy_reset(tp);
 
@@ -11226,7 +11297,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq,
         */
        err = tg3_alloc_consistent(tp);
        if (err)
-               goto err_out1;
+               goto out_ints_fini;
 
        tg3_napi_init(tp);
 
@@ -11240,12 +11311,15 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq,
                                tnapi = &tp->napi[i];
                                free_irq(tnapi->irq_vec, tnapi);
                        }
-                       goto err_out2;
+                       goto out_napi_fini;
                }
        }
 
        tg3_full_lock(tp, 0);
 
+       if (init)
+               tg3_ape_driver_state_change(tp, RESET_KIND_INIT);
+
        err = tg3_init_hw(tp, reset_phy);
        if (err) {
                tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
@@ -11255,7 +11329,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq,
        tg3_full_unlock(tp);
 
        if (err)
-               goto err_out3;
+               goto out_free_irq;
 
        if (test_irq && tg3_flag(tp, USING_MSI)) {
                err = tg3_test_msi(tp);
@@ -11266,7 +11340,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq,
                        tg3_free_rings(tp);
                        tg3_full_unlock(tp);
 
-                       goto err_out2;
+                       goto out_napi_fini;
                }
 
                if (!tg3_flag(tp, 57765_PLUS) && tg3_flag(tp, USING_MSI)) {
@@ -11306,18 +11380,18 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq,
 
        return 0;
 
-err_out3:
+out_free_irq:
        for (i = tp->irq_cnt - 1; i >= 0; i--) {
                struct tg3_napi *tnapi = &tp->napi[i];
                free_irq(tnapi->irq_vec, tnapi);
        }
 
-err_out2:
+out_napi_fini:
        tg3_napi_disable(tp);
        tg3_napi_fini(tp);
        tg3_free_consistent(tp);
 
-err_out1:
+out_ints_fini:
        tg3_ints_fini(tp);
 
        return err;
@@ -13362,11 +13436,13 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest,
        struct tg3 *tp = netdev_priv(dev);
        bool doextlpbk = etest->flags & ETH_TEST_FL_EXTERNAL_LB;
 
-       if ((tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) &&
-           tg3_power_up(tp)) {
-               etest->flags |= ETH_TEST_FL_FAILED;
-               memset(data, 1, sizeof(u64) * TG3_NUM_TEST);
-               return;
+       if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) {
+               if (tg3_power_up(tp)) {
+                       etest->flags |= ETH_TEST_FL_FAILED;
+                       memset(data, 1, sizeof(u64) * TG3_NUM_TEST);
+                       return;
+               }
+               tg3_ape_driver_state_change(tp, RESET_KIND_INIT);
        }
 
        memset(data, 0, sizeof(u64) * TG3_NUM_TEST);
@@ -13657,6 +13733,57 @@ static int tg3_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec)
        return 0;
 }
 
+static int tg3_set_eee(struct net_device *dev, struct ethtool_eee *edata)
+{
+       struct tg3 *tp = netdev_priv(dev);
+
+       if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) {
+               netdev_warn(tp->dev, "Board does not support EEE!\n");
+               return -EOPNOTSUPP;
+       }
+
+       if (edata->advertised != tp->eee.advertised) {
+               netdev_warn(tp->dev,
+                           "Direct manipulation of EEE advertisement is not supported\n");
+               return -EINVAL;
+       }
+
+       if (edata->tx_lpi_timer > TG3_CPMU_DBTMR1_LNKIDLE_MAX) {
+               netdev_warn(tp->dev,
+                           "Maximal Tx Lpi timer supported is %#x(u)\n",
+                           TG3_CPMU_DBTMR1_LNKIDLE_MAX);
+               return -EINVAL;
+       }
+
+       tp->eee = *edata;
+
+       tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED;
+       tg3_warn_mgmt_link_flap(tp);
+
+       if (netif_running(tp->dev)) {
+               tg3_full_lock(tp, 0);
+               tg3_setup_eee(tp);
+               tg3_phy_reset(tp);
+               tg3_full_unlock(tp);
+       }
+
+       return 0;
+}
+
+static int tg3_get_eee(struct net_device *dev, struct ethtool_eee *edata)
+{
+       struct tg3 *tp = netdev_priv(dev);
+
+       if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) {
+               netdev_warn(tp->dev,
+                           "Board does not support EEE!\n");
+               return -EOPNOTSUPP;
+       }
+
+       *edata = tp->eee;
+       return 0;
+}
+
 static const struct ethtool_ops tg3_ethtool_ops = {
        .get_settings           = tg3_get_settings,
        .set_settings           = tg3_set_settings,
@@ -13690,6 +13817,8 @@ static const struct ethtool_ops tg3_ethtool_ops = {
        .get_channels           = tg3_get_channels,
        .set_channels           = tg3_set_channels,
        .get_ts_info            = tg3_get_ts_info,
+       .get_eee                = tg3_get_eee,
+       .set_eee                = tg3_set_eee,
 };
 
 static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev,
@@ -15038,9 +15167,18 @@ static int tg3_phy_probe(struct tg3 *tp)
             (tg3_asic_rev(tp) == ASIC_REV_5717 &&
              tg3_chip_rev_id(tp) != CHIPREV_ID_5717_A0) ||
             (tg3_asic_rev(tp) == ASIC_REV_57765 &&
-             tg3_chip_rev_id(tp) != CHIPREV_ID_57765_A0)))
+             tg3_chip_rev_id(tp) != CHIPREV_ID_57765_A0))) {
                tp->phy_flags |= TG3_PHYFLG_EEE_CAP;
 
+               tp->eee.supported = SUPPORTED_100baseT_Full |
+                                   SUPPORTED_1000baseT_Full;
+               tp->eee.advertised = ADVERTISED_100baseT_Full |
+                                    ADVERTISED_1000baseT_Full;
+               tp->eee.eee_enabled = 1;
+               tp->eee.tx_lpi_enabled = 1;
+               tp->eee.tx_lpi_timer = TG3_CPMU_DBTMR1_LNKIDLE_2047US;
+       }
+
        tg3_phy_init_link_config(tp);
 
        if (!(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) &&
@@ -17112,7 +17250,7 @@ static int tg3_init_one(struct pci_dev *pdev,
 {
        struct net_device *dev;
        struct tg3 *tp;
-       int i, err, pm_cap;
+       int i, err;
        u32 sndmbx, rcvmbx, intmbx;
        char str[40];
        u64 dma_mask, persist_dma_mask;
@@ -17134,25 +17272,10 @@ static int tg3_init_one(struct pci_dev *pdev,
 
        pci_set_master(pdev);
 
-       /* Find power-management capability. */
-       pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
-       if (pm_cap == 0) {
-               dev_err(&pdev->dev,
-                       "Cannot find Power Management capability, aborting\n");
-               err = -EIO;
-               goto err_out_free_res;
-       }
-
-       err = pci_set_power_state(pdev, PCI_D0);
-       if (err) {
-               dev_err(&pdev->dev, "Transition to D0 failed, aborting\n");
-               goto err_out_free_res;
-       }
-
        dev = alloc_etherdev_mq(sizeof(*tp), TG3_IRQ_MAX_VECS);
        if (!dev) {
                err = -ENOMEM;
-               goto err_out_power_down;
+               goto err_out_free_res;
        }
 
        SET_NETDEV_DEV(dev, &pdev->dev);
@@ -17160,7 +17283,7 @@ static int tg3_init_one(struct pci_dev *pdev,
        tp = netdev_priv(dev);
        tp->pdev = pdev;
        tp->dev = dev;
-       tp->pm_cap = pm_cap;
+       tp->pm_cap = pdev->pm_cap;
        tp->rx_mode = TG3_DEF_RX_MODE;
        tp->tx_mode = TG3_DEF_TX_MODE;
        tp->irq_sync = 1;
@@ -17498,9 +17621,6 @@ err_out_iounmap:
 err_out_free_dev:
        free_netdev(dev);
 
-err_out_power_down:
-       pci_set_power_state(pdev, PCI_D3hot);
-
 err_out_free_res:
        pci_release_regions(pdev);
 
@@ -17610,6 +17730,8 @@ static int tg3_resume(struct device *device)
 
        tg3_full_lock(tp, 0);
 
+       tg3_ape_driver_state_change(tp, RESET_KIND_INIT);
+
        tg3_flag_set(tp, INIT_COMPLETE);
        err = tg3_restart_hw(tp,
                             !(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN));
@@ -17671,10 +17793,13 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev,
        tg3_full_unlock(tp);
 
 done:
-       if (state == pci_channel_io_perm_failure)
+       if (state == pci_channel_io_perm_failure) {
+               tg3_napi_enable(tp);
+               dev_close(netdev);
                err = PCI_ERS_RESULT_DISCONNECT;
-       else
+       } else {
                pci_disable_device(pdev);
+       }
 
        rtnl_unlock();
 
@@ -17720,6 +17845,10 @@ static pci_ers_result_t tg3_io_slot_reset(struct pci_dev *pdev)
        rc = PCI_ERS_RESULT_RECOVERED;
 
 done:
+       if (rc != PCI_ERS_RESULT_RECOVERED && netif_running(netdev)) {
+               tg3_napi_enable(tp);
+               dev_close(netdev);
+       }
        rtnl_unlock();
 
        return rc;
@@ -17744,6 +17873,7 @@ static void tg3_io_resume(struct pci_dev *pdev)
                goto done;
 
        tg3_full_lock(tp, 0);
+       tg3_ape_driver_state_change(tp, RESET_KIND_INIT);
        tg3_flag_set(tp, INIT_COMPLETE);
        err = tg3_restart_hw(tp, true);
        if (err) {
@@ -17781,15 +17911,4 @@ static struct pci_driver tg3_driver = {
        .driver.pm      = &tg3_pm_ops,
 };
 
-static int __init tg3_init(void)
-{
-       return pci_register_driver(&tg3_driver);
-}
-
-static void __exit tg3_cleanup(void)
-{
-       pci_unregister_driver(&tg3_driver);
-}
-
-module_init(tg3_init);
-module_exit(tg3_cleanup);
+module_pci_driver(tg3_driver);
index ff6e30e..cd63d11 100644 (file)
 #define TG3_CPMU_EEE_DBTMR1            0x000036b4
 #define  TG3_CPMU_DBTMR1_PCIEXIT_2047US         0x07ff0000
 #define  TG3_CPMU_DBTMR1_LNKIDLE_2047US         0x000007ff
+#define  TG3_CPMU_DBTMR1_LNKIDLE_MAX    0x0000ffff
 #define TG3_CPMU_EEE_DBTMR2            0x000036b8
 #define  TG3_CPMU_DBTMR2_APE_TX_2047US  0x07ff0000
 #define  TG3_CPMU_DBTMR2_TXIDXEQ_2047US         0x000007ff
@@ -3372,6 +3373,7 @@ struct tg3 {
        unsigned int                    irq_cnt;
 
        struct ethtool_coalesce         coal;
+       struct ethtool_eee              eee;
 
        /* firmware info */
        const char                      *fw_needed;
index e423f82..b7d8127 100644 (file)
@@ -164,7 +164,8 @@ struct bfa_ioc_attr {
        u8                              port_mode;      /*!< enum bfa_mode */
        u8                              cap_bm;         /*!< capability */
        u8                              port_mode_cfg;  /*!< enum bfa_mode */
-       u8                              rsvd[4];        /*!< 64bit align */
+       u8                              def_fn;         /*!< 1 if default fn */
+       u8                              rsvd[3];        /*!< 64bit align */
 };
 
 /* Adapter capability mask definition */
index f2b73ff..6f3cac0 100644 (file)
@@ -2371,7 +2371,7 @@ bfa_nw_ioc_get_attr(struct bfa_ioc *ioc, struct bfa_ioc_attr *ioc_attr)
        memset((void *)ioc_attr, 0, sizeof(struct bfa_ioc_attr));
 
        ioc_attr->state = bfa_ioc_get_state(ioc);
-       ioc_attr->port_id = ioc->port_id;
+       ioc_attr->port_id = bfa_ioc_portid(ioc);
        ioc_attr->port_mode = ioc->port_mode;
 
        ioc_attr->port_mode_cfg = ioc->port_mode_cfg;
@@ -2381,8 +2381,9 @@ bfa_nw_ioc_get_attr(struct bfa_ioc *ioc, struct bfa_ioc_attr *ioc_attr)
 
        bfa_ioc_get_adapter_attr(ioc, &ioc_attr->adapter_attr);
 
-       ioc_attr->pci_attr.device_id = ioc->pcidev.device_id;
-       ioc_attr->pci_attr.pcifn = ioc->pcidev.pci_func;
+       ioc_attr->pci_attr.device_id = bfa_ioc_devid(ioc);
+       ioc_attr->pci_attr.pcifn = bfa_ioc_pcifn(ioc);
+       ioc_attr->def_fn = bfa_ioc_is_default(ioc);
        bfa_ioc_get_pci_chip_rev(ioc, ioc_attr->pci_attr.chip_rev);
 }
 
index 63a85e5..f04e0aa 100644 (file)
@@ -222,6 +222,8 @@ struct bfa_ioc_hwif {
 #define bfa_ioc_bar0(__ioc)            ((__ioc)->pcidev.pci_bar_kva)
 #define bfa_ioc_portid(__ioc)          ((__ioc)->port_id)
 #define bfa_ioc_asic_gen(__ioc)                ((__ioc)->asic_gen)
+#define bfa_ioc_is_default(__ioc)      \
+       (bfa_ioc_pcifn(__ioc) == bfa_ioc_portid(__ioc))
 #define bfa_ioc_fetch_stats(__ioc, __stats) \
                (((__stats)->drv_stats) = (__ioc)->stats)
 #define bfa_ioc_clr_stats(__ioc)       \
index 25dae75..f1eafc4 100644 (file)
@@ -455,6 +455,8 @@ void bna_bfi_rx_enet_stop_rsp(struct bna_rx *rx,
 void bna_bfi_rxf_cfg_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr);
 void bna_bfi_rxf_mcast_add_rsp(struct bna_rxf *rxf,
                               struct bfi_msgq_mhdr *msghdr);
+void bna_bfi_rxf_ucast_set_rsp(struct bna_rxf *rxf,
+                              struct bfi_msgq_mhdr *msghdr);
 
 /* APIs for BNA */
 void bna_rx_mod_init(struct bna_rx_mod *rx_mod, struct bna *bna,
index db14f69..3ca77fa 100644 (file)
@@ -298,7 +298,6 @@ bna_msgq_rsp_handler(void *arg, struct bfi_msgq_mhdr *msghdr)
        case BFI_ENET_I2H_RSS_ENABLE_RSP:
        case BFI_ENET_I2H_RX_PROMISCUOUS_RSP:
        case BFI_ENET_I2H_RX_DEFAULT_RSP:
-       case BFI_ENET_I2H_MAC_UCAST_SET_RSP:
        case BFI_ENET_I2H_MAC_UCAST_CLR_RSP:
        case BFI_ENET_I2H_MAC_UCAST_ADD_RSP:
        case BFI_ENET_I2H_MAC_UCAST_DEL_RSP:
@@ -311,6 +310,12 @@ bna_msgq_rsp_handler(void *arg, struct bfi_msgq_mhdr *msghdr)
                        bna_bfi_rxf_cfg_rsp(&rx->rxf, msghdr);
                break;
 
+       case BFI_ENET_I2H_MAC_UCAST_SET_RSP:
+               bna_rx_from_rid(bna, msghdr->enet_id, rx);
+               if (rx)
+                       bna_bfi_rxf_ucast_set_rsp(&rx->rxf, msghdr);
+               break;
+
        case BFI_ENET_I2H_MAC_MCAST_ADD_RSP:
                bna_rx_from_rid(bna, msghdr->enet_id, rx);
                if (rx)
index ea6f4a0..57cd1bf 100644 (file)
@@ -710,6 +710,21 @@ bna_bfi_rxf_cfg_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr)
        bfa_fsm_send_event(rxf, RXF_E_FW_RESP);
 }
 
+void
+bna_bfi_rxf_ucast_set_rsp(struct bna_rxf *rxf,
+                       struct bfi_msgq_mhdr *msghdr)
+{
+       struct bfi_enet_rsp *rsp =
+               (struct bfi_enet_rsp *)msghdr;
+
+       if (rsp->error) {
+               /* Clear ucast from cache */
+               rxf->ucast_active_set = 0;
+       }
+
+       bfa_fsm_send_event(rxf, RXF_E_FW_RESP);
+}
+
 void
 bna_bfi_rxf_mcast_add_rsp(struct bna_rxf *rxf,
                        struct bfi_msgq_mhdr *msghdr)
index 07f7ef0..b78e69e 100644 (file)
@@ -2624,6 +2624,9 @@ bnad_stop(struct net_device *netdev)
        bnad_destroy_tx(bnad, 0);
        bnad_destroy_rx(bnad, 0);
 
+       /* These config flags are cleared in the hardware */
+       bnad->cfg_flags &= ~(BNAD_CF_ALLMULTI | BNAD_CF_PROMISC);
+
        /* Synchronize mailbox IRQ */
        bnad_mbox_irq_sync(bnad);
 
index c1d0bc0..aefee77 100644 (file)
@@ -71,7 +71,7 @@ struct bnad_rx_ctrl {
 #define BNAD_NAME                      "bna"
 #define BNAD_NAME_LEN                  64
 
-#define BNAD_VERSION                   "3.1.2.1"
+#define BNAD_VERSION                   "3.2.21.1"
 
 #define BNAD_MAILBOX_MSIX_INDEX                0
 #define BNAD_MAILBOX_MSIX_VECTORS      1
index 14ca931..c37f706 100644 (file)
@@ -37,8 +37,8 @@
 
 extern char bfa_version[];
 
-#define CNA_FW_FILE_CT "ctfw-3.1.0.0.bin"
-#define CNA_FW_FILE_CT2        "ct2fw-3.1.0.0.bin"
+#define CNA_FW_FILE_CT "ctfw-3.2.1.0.bin"
+#define CNA_FW_FILE_CT2        "ct2fw-3.2.1.0.bin"
 #define FC_SYMNAME_MAX 256     /*!< max name server symbolic name size */
 
 #pragma pack(1)
index 768285e..8030cc0 100644 (file)
@@ -23,7 +23,6 @@ if NET_CADENCE
 config ARM_AT91_ETHER
        tristate "AT91RM9200 Ethernet support"
        depends on GENERIC_HARDIRQS && HAS_DMA
-       select NET_CORE
        select MACB
        ---help---
          If you wish to compile a kernel for the AT91RM9200 and enable
index cc9a185..3f19571 100644 (file)
@@ -435,7 +435,6 @@ static int at91ether_remove(struct platform_device *pdev)
        unregister_netdev(dev);
        clk_disable(lp->pclk);
        free_netdev(dev);
-       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
index c89aa41..e866608 100644 (file)
@@ -32,7 +32,8 @@
 
 #include "macb.h"
 
-#define RX_BUFFER_SIZE         128
+#define MACB_RX_BUFFER_SIZE    128
+#define RX_BUFFER_MULTIPLE     64  /* bytes */
 #define RX_RING_SIZE           512 /* must be power of 2 */
 #define RX_RING_BYTES          (sizeof(struct macb_dma_desc) * RX_RING_SIZE)
 
@@ -92,7 +93,7 @@ static struct macb_dma_desc *macb_rx_desc(struct macb *bp, unsigned int index)
 
 static void *macb_rx_buffer(struct macb *bp, unsigned int index)
 {
-       return bp->rx_buffers + RX_BUFFER_SIZE * macb_rx_ring_wrap(index);
+       return bp->rx_buffers + bp->rx_buffer_size * macb_rx_ring_wrap(index);
 }
 
 void macb_set_hwaddr(struct macb *bp)
@@ -528,6 +529,155 @@ static void macb_tx_interrupt(struct macb *bp)
                netif_wake_queue(bp->dev);
 }
 
+static void gem_rx_refill(struct macb *bp)
+{
+       unsigned int            entry;
+       struct sk_buff          *skb;
+       struct macb_dma_desc    *desc;
+       dma_addr_t              paddr;
+
+       while (CIRC_SPACE(bp->rx_prepared_head, bp->rx_tail, RX_RING_SIZE) > 0) {
+               u32 addr, ctrl;
+
+               entry = macb_rx_ring_wrap(bp->rx_prepared_head);
+               desc = &bp->rx_ring[entry];
+
+               /* Make hw descriptor updates visible to CPU */
+               rmb();
+
+               addr = desc->addr;
+               ctrl = desc->ctrl;
+               bp->rx_prepared_head++;
+
+               if ((addr & MACB_BIT(RX_USED)))
+                       continue;
+
+               if (bp->rx_skbuff[entry] == NULL) {
+                       /* allocate sk_buff for this free entry in ring */
+                       skb = netdev_alloc_skb(bp->dev, bp->rx_buffer_size);
+                       if (unlikely(skb == NULL)) {
+                               netdev_err(bp->dev,
+                                          "Unable to allocate sk_buff\n");
+                               break;
+                       }
+                       bp->rx_skbuff[entry] = skb;
+
+                       /* now fill corresponding descriptor entry */
+                       paddr = dma_map_single(&bp->pdev->dev, skb->data,
+                                              bp->rx_buffer_size, DMA_FROM_DEVICE);
+
+                       if (entry == RX_RING_SIZE - 1)
+                               paddr |= MACB_BIT(RX_WRAP);
+                       bp->rx_ring[entry].addr = paddr;
+                       bp->rx_ring[entry].ctrl = 0;
+
+                       /* properly align Ethernet header */
+                       skb_reserve(skb, NET_IP_ALIGN);
+               }
+       }
+
+       /* Make descriptor updates visible to hardware */
+       wmb();
+
+       netdev_vdbg(bp->dev, "rx ring: prepared head %d, tail %d\n",
+                  bp->rx_prepared_head, bp->rx_tail);
+}
+
+/* Mark DMA descriptors from begin up to and not including end as unused */
+static void discard_partial_frame(struct macb *bp, unsigned int begin,
+                                 unsigned int end)
+{
+       unsigned int frag;
+
+       for (frag = begin; frag != end; frag++) {
+               struct macb_dma_desc *desc = macb_rx_desc(bp, frag);
+               desc->addr &= ~MACB_BIT(RX_USED);
+       }
+
+       /* Make descriptor updates visible to hardware */
+       wmb();
+
+       /*
+        * When this happens, the hardware stats registers for
+        * whatever caused this is updated, so we don't have to record
+        * anything.
+        */
+}
+
+static int gem_rx(struct macb *bp, int budget)
+{
+       unsigned int            len;
+       unsigned int            entry;
+       struct sk_buff          *skb;
+       struct macb_dma_desc    *desc;
+       int                     count = 0;
+
+       while (count < budget) {
+               u32 addr, ctrl;
+
+               entry = macb_rx_ring_wrap(bp->rx_tail);
+               desc = &bp->rx_ring[entry];
+
+               /* Make hw descriptor updates visible to CPU */
+               rmb();
+
+               addr = desc->addr;
+               ctrl = desc->ctrl;
+
+               if (!(addr & MACB_BIT(RX_USED)))
+                       break;
+
+               desc->addr &= ~MACB_BIT(RX_USED);
+               bp->rx_tail++;
+               count++;
+
+               if (!(ctrl & MACB_BIT(RX_SOF) && ctrl & MACB_BIT(RX_EOF))) {
+                       netdev_err(bp->dev,
+                                  "not whole frame pointed by descriptor\n");
+                       bp->stats.rx_dropped++;
+                       break;
+               }
+               skb = bp->rx_skbuff[entry];
+               if (unlikely(!skb)) {
+                       netdev_err(bp->dev,
+                                  "inconsistent Rx descriptor chain\n");
+                       bp->stats.rx_dropped++;
+                       break;
+               }
+               /* now everything is ready for receiving packet */
+               bp->rx_skbuff[entry] = NULL;
+               len = MACB_BFEXT(RX_FRMLEN, ctrl);
+
+               netdev_vdbg(bp->dev, "gem_rx %u (len %u)\n", entry, len);
+
+               skb_put(skb, len);
+               addr = MACB_BF(RX_WADDR, MACB_BFEXT(RX_WADDR, addr));
+               dma_unmap_single(&bp->pdev->dev, addr,
+                                len, DMA_FROM_DEVICE);
+
+               skb->protocol = eth_type_trans(skb, bp->dev);
+               skb_checksum_none_assert(skb);
+
+               bp->stats.rx_packets++;
+               bp->stats.rx_bytes += skb->len;
+
+#if defined(DEBUG) && defined(VERBOSE_DEBUG)
+               netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n",
+                           skb->len, skb->csum);
+               print_hex_dump(KERN_DEBUG, " mac: ", DUMP_PREFIX_ADDRESS, 16, 1,
+                              skb->mac_header, 16, true);
+               print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_ADDRESS, 16, 1,
+                              skb->data, 32, true);
+#endif
+
+               netif_receive_skb(skb);
+       }
+
+       gem_rx_refill(bp);
+
+       return count;
+}
+
 static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
                         unsigned int last_frag)
 {
@@ -575,7 +725,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
        skb_put(skb, len);
 
        for (frag = first_frag; ; frag++) {
-               unsigned int frag_len = RX_BUFFER_SIZE;
+               unsigned int frag_len = bp->rx_buffer_size;
 
                if (offset + frag_len > len) {
                        BUG_ON(frag != last_frag);
@@ -583,7 +733,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
                }
                skb_copy_to_linear_data_offset(skb, offset,
                                macb_rx_buffer(bp, frag), frag_len);
-               offset += RX_BUFFER_SIZE;
+               offset += bp->rx_buffer_size;
                desc = macb_rx_desc(bp, frag);
                desc->addr &= ~MACB_BIT(RX_USED);
 
@@ -606,27 +756,6 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
        return 0;
 }
 
-/* Mark DMA descriptors from begin up to and not including end as unused */
-static void discard_partial_frame(struct macb *bp, unsigned int begin,
-                                 unsigned int end)
-{
-       unsigned int frag;
-
-       for (frag = begin; frag != end; frag++) {
-               struct macb_dma_desc *desc = macb_rx_desc(bp, frag);
-               desc->addr &= ~MACB_BIT(RX_USED);
-       }
-
-       /* Make descriptor updates visible to hardware */
-       wmb();
-
-       /*
-        * When this happens, the hardware stats registers for
-        * whatever caused this is updated, so we don't have to record
-        * anything.
-        */
-}
-
 static int macb_rx(struct macb *bp, int budget)
 {
        int received = 0;
@@ -687,7 +816,7 @@ static int macb_poll(struct napi_struct *napi, int budget)
        netdev_vdbg(bp->dev, "poll: status = %08lx, budget = %d\n",
                   (unsigned long)status, budget);
 
-       work_done = macb_rx(bp, budget);
+       work_done = bp->macbgem_ops.mog_rx(bp, budget);
        if (work_done < budget) {
                napi_complete(napi);
 
@@ -870,12 +999,71 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
        return NETDEV_TX_OK;
 }
 
+static void macb_init_rx_buffer_size(struct macb *bp, size_t size)
+{
+       if (!macb_is_gem(bp)) {
+               bp->rx_buffer_size = MACB_RX_BUFFER_SIZE;
+       } else {
+               bp->rx_buffer_size = size;
+
+               if (bp->rx_buffer_size % RX_BUFFER_MULTIPLE) {
+                       netdev_dbg(bp->dev,
+                                   "RX buffer must be multiple of %d bytes, expanding\n",
+                                   RX_BUFFER_MULTIPLE);
+                       bp->rx_buffer_size =
+                               roundup(bp->rx_buffer_size, RX_BUFFER_MULTIPLE);
+               }
+       }
+
+       netdev_dbg(bp->dev, "mtu [%u] rx_buffer_size [%Zu]\n",
+                  bp->dev->mtu, bp->rx_buffer_size);
+}
+
+static void gem_free_rx_buffers(struct macb *bp)
+{
+       struct sk_buff          *skb;
+       struct macb_dma_desc    *desc;
+       dma_addr_t              addr;
+       int i;
+
+       if (!bp->rx_skbuff)
+               return;
+
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               skb = bp->rx_skbuff[i];
+
+               if (skb == NULL)
+                       continue;
+
+               desc = &bp->rx_ring[i];
+               addr = MACB_BF(RX_WADDR, MACB_BFEXT(RX_WADDR, desc->addr));
+               dma_unmap_single(&bp->pdev->dev, addr, skb->len,
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(skb);
+               skb = NULL;
+       }
+
+       kfree(bp->rx_skbuff);
+       bp->rx_skbuff = NULL;
+}
+
+static void macb_free_rx_buffers(struct macb *bp)
+{
+       if (bp->rx_buffers) {
+               dma_free_coherent(&bp->pdev->dev,
+                                 RX_RING_SIZE * bp->rx_buffer_size,
+                                 bp->rx_buffers, bp->rx_buffers_dma);
+               bp->rx_buffers = NULL;
+       }
+}
+
 static void macb_free_consistent(struct macb *bp)
 {
        if (bp->tx_skb) {
                kfree(bp->tx_skb);
                bp->tx_skb = NULL;
        }
+       bp->macbgem_ops.mog_free_rx_buffers(bp);
        if (bp->rx_ring) {
                dma_free_coherent(&bp->pdev->dev, RX_RING_BYTES,
                                  bp->rx_ring, bp->rx_ring_dma);
@@ -886,12 +1074,37 @@ static void macb_free_consistent(struct macb *bp)
                                  bp->tx_ring, bp->tx_ring_dma);
                bp->tx_ring = NULL;
        }
-       if (bp->rx_buffers) {
-               dma_free_coherent(&bp->pdev->dev,
-                                 RX_RING_SIZE * RX_BUFFER_SIZE,
-                                 bp->rx_buffers, bp->rx_buffers_dma);
-               bp->rx_buffers = NULL;
-       }
+}
+
+static int gem_alloc_rx_buffers(struct macb *bp)
+{
+       int size;
+
+       size = RX_RING_SIZE * sizeof(struct sk_buff *);
+       bp->rx_skbuff = kzalloc(size, GFP_KERNEL);
+       if (!bp->rx_skbuff)
+               return -ENOMEM;
+       else
+               netdev_dbg(bp->dev,
+                          "Allocated %d RX struct sk_buff entries at %p\n",
+                          RX_RING_SIZE, bp->rx_skbuff);
+       return 0;
+}
+
+static int macb_alloc_rx_buffers(struct macb *bp)
+{
+       int size;
+
+       size = RX_RING_SIZE * bp->rx_buffer_size;
+       bp->rx_buffers = dma_alloc_coherent(&bp->pdev->dev, size,
+                                           &bp->rx_buffers_dma, GFP_KERNEL);
+       if (!bp->rx_buffers)
+               return -ENOMEM;
+       else
+               netdev_dbg(bp->dev,
+                          "Allocated RX buffers of %d bytes at %08lx (mapped %p)\n",
+                          size, (unsigned long)bp->rx_buffers_dma, bp->rx_buffers);
+       return 0;
 }
 
 static int macb_alloc_consistent(struct macb *bp)
@@ -921,14 +1134,8 @@ static int macb_alloc_consistent(struct macb *bp)
                   "Allocated TX ring of %d bytes at %08lx (mapped %p)\n",
                   size, (unsigned long)bp->tx_ring_dma, bp->tx_ring);
 
-       size = RX_RING_SIZE * RX_BUFFER_SIZE;
-       bp->rx_buffers = dma_alloc_coherent(&bp->pdev->dev, size,
-                                           &bp->rx_buffers_dma, GFP_KERNEL);
-       if (!bp->rx_buffers)
+       if (bp->macbgem_ops.mog_alloc_rx_buffers(bp))
                goto out_err;
-       netdev_dbg(bp->dev,
-                  "Allocated RX buffers of %d bytes at %08lx (mapped %p)\n",
-                  size, (unsigned long)bp->rx_buffers_dma, bp->rx_buffers);
 
        return 0;
 
@@ -937,6 +1144,21 @@ out_err:
        return -ENOMEM;
 }
 
+static void gem_init_rings(struct macb *bp)
+{
+       int i;
+
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               bp->tx_ring[i].addr = 0;
+               bp->tx_ring[i].ctrl = MACB_BIT(TX_USED);
+       }
+       bp->tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP);
+
+       bp->rx_tail = bp->rx_prepared_head = bp->tx_head = bp->tx_tail = 0;
+
+       gem_rx_refill(bp);
+}
+
 static void macb_init_rings(struct macb *bp)
 {
        int i;
@@ -946,7 +1168,7 @@ static void macb_init_rings(struct macb *bp)
        for (i = 0; i < RX_RING_SIZE; i++) {
                bp->rx_ring[i].addr = addr;
                bp->rx_ring[i].ctrl = 0;
-               addr += RX_BUFFER_SIZE;
+               addr += bp->rx_buffer_size;
        }
        bp->rx_ring[RX_RING_SIZE - 1].addr |= MACB_BIT(RX_WRAP);
 
@@ -1056,7 +1278,7 @@ static void macb_configure_dma(struct macb *bp)
 
        if (macb_is_gem(bp)) {
                dmacfg = gem_readl(bp, DMACFG) & ~GEM_BF(RXBS, -1L);
-               dmacfg |= GEM_BF(RXBS, RX_BUFFER_SIZE / 64);
+               dmacfg |= GEM_BF(RXBS, bp->rx_buffer_size / RX_BUFFER_MULTIPLE);
                dmacfg |= GEM_BF(FBLDO, 16);
                dmacfg |= GEM_BIT(TXPBMS) | GEM_BF(RXBMS, -1L);
                dmacfg &= ~GEM_BIT(ENDIA);
@@ -1070,7 +1292,7 @@ static void macb_configure_dma(struct macb *bp)
 static void macb_configure_caps(struct macb *bp)
 {
        if (macb_is_gem(bp)) {
-               if (GEM_BF(IRQCOR, gem_readl(bp, DCFG1)) == 0)
+               if (GEM_BFEXT(IRQCOR, gem_readl(bp, DCFG1)) == 0)
                        bp->caps |= MACB_CAPS_ISR_CLEAR_ON_WRITE;
        }
 }
@@ -1233,6 +1455,7 @@ EXPORT_SYMBOL_GPL(macb_set_rx_mode);
 static int macb_open(struct net_device *dev)
 {
        struct macb *bp = netdev_priv(dev);
+       size_t bufsz = dev->mtu + ETH_HLEN + ETH_FCS_LEN + NET_IP_ALIGN;
        int err;
 
        netdev_dbg(bp->dev, "open\n");
@@ -1244,6 +1467,9 @@ static int macb_open(struct net_device *dev)
        if (!bp->phy_dev)
                return -EAGAIN;
 
+       /* RX buffers initialization */
+       macb_init_rx_buffer_size(bp, bufsz);
+
        err = macb_alloc_consistent(bp);
        if (err) {
                netdev_err(dev, "Unable to allocate DMA memory (error %d)\n",
@@ -1253,7 +1479,7 @@ static int macb_open(struct net_device *dev)
 
        napi_enable(&bp->napi);
 
-       macb_init_rings(bp);
+       bp->macbgem_ops.mog_init_rings(bp);
        macb_init_hw(bp);
 
        /* schedule a link state check */
@@ -1572,6 +1798,19 @@ static int __init macb_probe(struct platform_device *pdev)
 
        dev->base_addr = regs->start;
 
+       /* setup appropriated routines according to adapter type */
+       if (macb_is_gem(bp)) {
+               bp->macbgem_ops.mog_alloc_rx_buffers = gem_alloc_rx_buffers;
+               bp->macbgem_ops.mog_free_rx_buffers = gem_free_rx_buffers;
+               bp->macbgem_ops.mog_init_rings = gem_init_rings;
+               bp->macbgem_ops.mog_rx = gem_rx;
+       } else {
+               bp->macbgem_ops.mog_alloc_rx_buffers = macb_alloc_rx_buffers;
+               bp->macbgem_ops.mog_free_rx_buffers = macb_free_rx_buffers;
+               bp->macbgem_ops.mog_init_rings = macb_init_rings;
+               bp->macbgem_ops.mog_rx = macb_rx;
+       }
+
        /* Set MII management clock divider */
        config = macb_mdc_clk_div(bp);
        config |= macb_dbw(bp);
@@ -1649,7 +1888,6 @@ err_out_put_pclk:
 err_out_free_dev:
        free_netdev(dev);
 err_out:
-       platform_set_drvdata(pdev, NULL);
        return err;
 }
 
@@ -1675,7 +1913,6 @@ static int __exit macb_remove(struct platform_device *pdev)
                clk_disable_unprepare(bp->pclk);
                clk_put(bp->pclk);
                free_netdev(dev);
-               platform_set_drvdata(pdev, NULL);
        }
 
        return 0;
index 548c0ec..f407615 100644 (file)
@@ -545,12 +545,24 @@ struct gem_stats {
        u32     rx_udp_checksum_errors;
 };
 
+struct macb;
+
+struct macb_or_gem_ops {
+       int     (*mog_alloc_rx_buffers)(struct macb *bp);
+       void    (*mog_free_rx_buffers)(struct macb *bp);
+       void    (*mog_init_rings)(struct macb *bp);
+       int     (*mog_rx)(struct macb *bp, int budget);
+};
+
 struct macb {
        void __iomem            *regs;
 
        unsigned int            rx_tail;
+       unsigned int            rx_prepared_head;
        struct macb_dma_desc    *rx_ring;
+       struct sk_buff          **rx_skbuff;
        void                    *rx_buffers;
+       size_t                  rx_buffer_size;
 
        unsigned int            tx_head, tx_tail;
        struct macb_dma_desc    *tx_ring;
@@ -573,6 +585,8 @@ struct macb {
        dma_addr_t              tx_ring_dma;
        dma_addr_t              rx_buffers_dma;
 
+       struct macb_or_gem_ops  macbgem_ops;
+
        struct mii_bus          *mii_bus;
        struct phy_device       *phy_dev;
        unsigned int            link;
index 4a1f2fa..7cb148c 100644 (file)
@@ -1790,7 +1790,6 @@ err_io:
        free_netdev(ndev);
 err_alloc:
        release_mem_region(res->start, resource_size(res));
-       platform_set_drvdata(pdev, NULL);
        return ret;
 }
 
@@ -1813,7 +1812,6 @@ static int xgmac_remove(struct platform_device *pdev)
        free_irq(ndev->irq, ndev);
        free_irq(priv->pmt_irq, ndev);
 
-       platform_set_drvdata(pdev, NULL);
        unregister_netdev(ndev);
        netif_napi_del(&priv->napi);
 
index 9624cfe..d7048db 100644 (file)
@@ -1351,22 +1351,11 @@ static void remove_one(struct pci_dev *pdev)
        t1_sw_reset(pdev);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver cxgb_pci_driver = {
        .name     = DRV_NAME,
        .id_table = t1_pci_tbl,
        .probe    = init_one,
        .remove   = remove_one,
 };
 
-static int __init t1_init_module(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit t1_cleanup_module(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(t1_init_module);
-module_exit(t1_cleanup_module);
+module_pci_driver(cxgb_pci_driver);
index 71497e8..b650951 100644 (file)
@@ -3037,7 +3037,9 @@ static void t3_io_resume(struct pci_dev *pdev)
        CH_ALERT(adapter, "adapter recovering, PEX ERR 0x%x\n",
                 t3_read_reg(adapter, A_PCIE_PEX_ERR));
 
+       rtnl_lock();
        t3_resume_ports(adapter);
+       rtnl_unlock();
 }
 
 static const struct pci_error_handlers t3_err_handler = {
index 0c96e5f..4058b85 100644 (file)
@@ -1246,6 +1246,7 @@ int cxgb3_offload_activate(struct adapter *adapter)
        struct tid_range stid_range, tid_range;
        struct mtutab mtutab;
        unsigned int l2t_capacity;
+       struct l2t_data *l2td;
 
        t = kzalloc(sizeof(*t), GFP_KERNEL);
        if (!t)
@@ -1261,8 +1262,8 @@ int cxgb3_offload_activate(struct adapter *adapter)
                goto out_free;
 
        err = -ENOMEM;
-       RCU_INIT_POINTER(dev->l2opt, t3_init_l2t(l2t_capacity));
-       if (!L2DATA(dev))
+       l2td = t3_init_l2t(l2t_capacity);
+       if (!l2td)
                goto out_free;
 
        natids = min(tid_range.num / 2, MAX_ATIDS);
@@ -1279,6 +1280,7 @@ int cxgb3_offload_activate(struct adapter *adapter)
        INIT_LIST_HEAD(&t->list_node);
        t->dev = dev;
 
+       RCU_INIT_POINTER(dev->l2opt, l2td);
        T3C_DATA(dev) = t;
        dev->recv = process_rx;
        dev->neigh_update = t3_l2t_update;
@@ -1294,8 +1296,7 @@ int cxgb3_offload_activate(struct adapter *adapter)
        return 0;
 
 out_free_l2t:
-       t3_free_l2t(L2DATA(dev));
-       RCU_INIT_POINTER(dev->l2opt, NULL);
+       t3_free_l2t(l2td);
 out_free:
        kfree(t);
        return err;
index f12e6b8..687ec4a 100644 (file)
@@ -455,6 +455,11 @@ static int alloc_pg_chunk(struct adapter *adapter, struct sge_fl *q,
                q->pg_chunk.offset = 0;
                mapping = pci_map_page(adapter->pdev, q->pg_chunk.page,
                                       0, q->alloc_size, PCI_DMA_FROMDEVICE);
+               if (unlikely(pci_dma_mapping_error(adapter->pdev, mapping))) {
+                       __free_pages(q->pg_chunk.page, order);
+                       q->pg_chunk.page = NULL;
+                       return -EIO;
+               }
                q->pg_chunk.mapping = mapping;
        }
        sd->pg_chunk = q->pg_chunk;
@@ -949,40 +954,75 @@ static inline unsigned int calc_tx_descs(const struct sk_buff *skb)
        return flits_to_desc(flits);
 }
 
+
+/*     map_skb - map a packet main body and its page fragments
+ *     @pdev: the PCI device
+ *     @skb: the packet
+ *     @addr: placeholder to save the mapped addresses
+ *
+ *     map the main body of an sk_buff and its page fragments, if any.
+ */
+static int map_skb(struct pci_dev *pdev, const struct sk_buff *skb,
+                  dma_addr_t *addr)
+{
+       const skb_frag_t *fp, *end;
+       const struct skb_shared_info *si;
+
+       *addr = pci_map_single(pdev, skb->data, skb_headlen(skb),
+                              PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(pdev, *addr))
+               goto out_err;
+
+       si = skb_shinfo(skb);
+       end = &si->frags[si->nr_frags];
+
+       for (fp = si->frags; fp < end; fp++) {
+               *++addr = skb_frag_dma_map(&pdev->dev, fp, 0, skb_frag_size(fp),
+                                          DMA_TO_DEVICE);
+               if (pci_dma_mapping_error(pdev, *addr))
+                       goto unwind;
+       }
+       return 0;
+
+unwind:
+       while (fp-- > si->frags)
+               dma_unmap_page(&pdev->dev, *--addr, skb_frag_size(fp),
+                              DMA_TO_DEVICE);
+
+       pci_unmap_single(pdev, addr[-1], skb_headlen(skb), PCI_DMA_TODEVICE);
+out_err:
+       return -ENOMEM;
+}
+
 /**
- *     make_sgl - populate a scatter/gather list for a packet
+ *     write_sgl - populate a scatter/gather list for a packet
  *     @skb: the packet
  *     @sgp: the SGL to populate
  *     @start: start address of skb main body data to include in the SGL
  *     @len: length of skb main body data to include in the SGL
- *     @pdev: the PCI device
+ *     @addr: the list of the mapped addresses
  *
- *     Generates a scatter/gather list for the buffers that make up a packet
+ *     Copies the scatter/gather list for the buffers that make up a packet
  *     and returns the SGL size in 8-byte words.  The caller must size the SGL
  *     appropriately.
  */
-static inline unsigned int make_sgl(const struct sk_buff *skb,
+static inline unsigned int write_sgl(const struct sk_buff *skb,
                                    struct sg_ent *sgp, unsigned char *start,
-                                   unsigned int len, struct pci_dev *pdev)
+                                   unsigned int len, const dma_addr_t *addr)
 {
-       dma_addr_t mapping;
-       unsigned int i, j = 0, nfrags;
+       unsigned int i, j = 0, k = 0, nfrags;
 
        if (len) {
-               mapping = pci_map_single(pdev, start, len, PCI_DMA_TODEVICE);
                sgp->len[0] = cpu_to_be32(len);
-               sgp->addr[0] = cpu_to_be64(mapping);
-               j = 1;
+               sgp->addr[j++] = cpu_to_be64(addr[k++]);
        }
 
        nfrags = skb_shinfo(skb)->nr_frags;
        for (i = 0; i < nfrags; i++) {
                const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
 
-               mapping = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag),
-                                          DMA_TO_DEVICE);
                sgp->len[j] = cpu_to_be32(skb_frag_size(frag));
-               sgp->addr[j] = cpu_to_be64(mapping);
+               sgp->addr[j] = cpu_to_be64(addr[k++]);
                j ^= 1;
                if (j == 0)
                        ++sgp;
@@ -1138,7 +1178,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb,
                            const struct port_info *pi,
                            unsigned int pidx, unsigned int gen,
                            struct sge_txq *q, unsigned int ndesc,
-                           unsigned int compl)
+                           unsigned int compl, const dma_addr_t *addr)
 {
        unsigned int flits, sgl_flits, cntrl, tso_info;
        struct sg_ent *sgp, sgl[MAX_SKB_FRAGS / 2 + 1];
@@ -1196,7 +1236,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb,
        }
 
        sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl;
-       sgl_flits = make_sgl(skb, sgp, skb->data, skb_headlen(skb), adap->pdev);
+       sgl_flits = write_sgl(skb, sgp, skb->data, skb_headlen(skb), addr);
 
        write_wr_hdr_sgl(ndesc, skb, d, pidx, q, sgl, flits, sgl_flits, gen,
                         htonl(V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | compl),
@@ -1227,6 +1267,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev)
        struct netdev_queue *txq;
        struct sge_qset *qs;
        struct sge_txq *q;
+       dma_addr_t addr[MAX_SKB_FRAGS + 1];
 
        /*
         * The chip min packet length is 9 octets but play safe and reject
@@ -1255,6 +1296,11 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev)
                return NETDEV_TX_BUSY;
        }
 
+       if (unlikely(map_skb(adap->pdev, skb, addr) < 0)) {
+               dev_kfree_skb(skb);
+               return NETDEV_TX_OK;
+       }
+
        q->in_use += ndesc;
        if (unlikely(credits - ndesc < q->stop_thres)) {
                t3_stop_tx_queue(txq, qs, q);
@@ -1312,7 +1358,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev)
        if (likely(!skb_shared(skb)))
                skb_orphan(skb);
 
-       write_tx_pkt_wr(adap, skb, pi, pidx, gen, q, ndesc, compl);
+       write_tx_pkt_wr(adap, skb, pi, pidx, gen, q, ndesc, compl, addr);
        check_ring_tx_db(adap, q);
        return NETDEV_TX_OK;
 }
@@ -1537,10 +1583,9 @@ static void deferred_unmap_destructor(struct sk_buff *skb)
        dui = (struct deferred_unmap_info *)skb->head;
        p = dui->addr;
 
-       if (skb->tail - skb->transport_header)
-               pci_unmap_single(dui->pdev, *p++,
-                                skb->tail - skb->transport_header,
-                                PCI_DMA_TODEVICE);
+       if (skb_tail_pointer(skb) - skb_transport_header(skb))
+               pci_unmap_single(dui->pdev, *p++, skb_tail_pointer(skb) -
+                                skb_transport_header(skb), PCI_DMA_TODEVICE);
 
        si = skb_shinfo(skb);
        for (i = 0; i < si->nr_frags; i++)
@@ -1578,7 +1623,8 @@ static void setup_deferred_unmapping(struct sk_buff *skb, struct pci_dev *pdev,
  */
 static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb,
                          struct sge_txq *q, unsigned int pidx,
-                         unsigned int gen, unsigned int ndesc)
+                         unsigned int gen, unsigned int ndesc,
+                         const dma_addr_t *addr)
 {
        unsigned int sgl_flits, flits;
        struct work_request_hdr *from;
@@ -1599,9 +1645,9 @@ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb,
 
        flits = skb_transport_offset(skb) / 8;
        sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl;
-       sgl_flits = make_sgl(skb, sgp, skb_transport_header(skb),
-                            skb->tail - skb->transport_header,
-                            adap->pdev);
+       sgl_flits = write_sgl(skb, sgp, skb_transport_header(skb),
+                            skb_tail_pointer(skb) -
+                            skb_transport_header(skb), addr);
        if (need_skb_unmap()) {
                setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits);
                skb->destructor = deferred_unmap_destructor;
@@ -1627,7 +1673,7 @@ static inline unsigned int calc_tx_descs_ofld(const struct sk_buff *skb)
 
        flits = skb_transport_offset(skb) / 8;  /* headers */
        cnt = skb_shinfo(skb)->nr_frags;
-       if (skb->tail != skb->transport_header)
+       if (skb_tail_pointer(skb) != skb_transport_header(skb))
                cnt++;
        return flits_to_desc(flits + sgl_len(cnt));
 }
@@ -1659,6 +1705,11 @@ again:   reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK);
                goto again;
        }
 
+       if (map_skb(adap->pdev, skb, (dma_addr_t *)skb->head)) {
+               spin_unlock(&q->lock);
+               return NET_XMIT_SUCCESS;
+       }
+
        gen = q->gen;
        q->in_use += ndesc;
        pidx = q->pidx;
@@ -1669,7 +1720,7 @@ again:    reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK);
        }
        spin_unlock(&q->lock);
 
-       write_ofld_wr(adap, skb, q, pidx, gen, ndesc);
+       write_ofld_wr(adap, skb, q, pidx, gen, ndesc, (dma_addr_t *)skb->head);
        check_ring_tx_db(adap, q);
        return NET_XMIT_SUCCESS;
 }
@@ -1687,6 +1738,7 @@ static void restart_offloadq(unsigned long data)
        struct sge_txq *q = &qs->txq[TXQ_OFLD];
        const struct port_info *pi = netdev_priv(qs->netdev);
        struct adapter *adap = pi->adapter;
+       unsigned int written = 0;
 
        spin_lock(&q->lock);
 again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK);
@@ -1706,10 +1758,14 @@ again:  reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK);
                        break;
                }
 
+               if (map_skb(adap->pdev, skb, (dma_addr_t *)skb->head))
+                       break;
+
                gen = q->gen;
                q->in_use += ndesc;
                pidx = q->pidx;
                q->pidx += ndesc;
+               written += ndesc;
                if (q->pidx >= q->size) {
                        q->pidx -= q->size;
                        q->gen ^= 1;
@@ -1717,7 +1773,8 @@ again:    reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK);
                __skb_unlink(skb, &q->sendq);
                spin_unlock(&q->lock);
 
-               write_ofld_wr(adap, skb, q, pidx, gen, ndesc);
+               write_ofld_wr(adap, skb, q, pidx, gen, ndesc,
+                            (dma_addr_t *)skb->head);
                spin_lock(&q->lock);
        }
        spin_unlock(&q->lock);
@@ -1727,8 +1784,9 @@ again:    reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK);
        set_bit(TXQ_LAST_PKT_DB, &q->flags);
 #endif
        wmb();
-       t3_write_reg(adap, A_SG_KDOORBELL,
-                    F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id));
+       if (likely(written))
+               t3_write_reg(adap, A_SG_KDOORBELL,
+                            F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id));
 }
 
 /**
index 681804b..2aafb80 100644 (file)
@@ -51,7 +51,7 @@
 #include "t4_hw.h"
 
 #define FW_VERSION_MAJOR 1
-#define FW_VERSION_MINOR 1
+#define FW_VERSION_MINOR 4
 #define FW_VERSION_MICRO 0
 
 #define FW_VERSION_MAJOR_T5 0
index 3cd397d..5a3256b 100644 (file)
@@ -4842,8 +4842,17 @@ static int adap_init0(struct adapter *adap)
         * is excessively mismatched relative to the driver.)
         */
        ret = t4_check_fw_version(adap);
+
+       /* The error code -EFAULT is returned by t4_check_fw_version() if
+        * firmware on adapter < supported firmware. If firmware on adapter
+        * is too old (not supported by driver) and we're the MASTER_PF set
+        * adapter state to DEV_STATE_UNINIT to force firmware upgrade
+        * and reinitialization.
+        */
+       if ((adap->flags & MASTER_PF) && ret == -EFAULT)
+               state = DEV_STATE_UNINIT;
        if ((adap->flags & MASTER_PF) && state != DEV_STATE_INIT) {
-               if (ret == -EINVAL || ret > 0) {
+               if (ret == -EINVAL || ret == -EFAULT || ret > 0) {
                        if (upgrade_fw(adap) >= 0) {
                                /*
                                 * Note that the chip was reset as part of the
@@ -4852,7 +4861,21 @@ static int adap_init0(struct adapter *adap)
                                 */
                                reset = 0;
                                ret = t4_check_fw_version(adap);
-                       }
+                       } else
+                               if (ret == -EFAULT) {
+                                       /*
+                                        * Firmware is old but still might
+                                        * work if we force reinitialization
+                                        * of the adapter. Ignoring FW upgrade
+                                        * failure.
+                                        */
+                                       dev_warn(adap->pdev_dev,
+                                                "Ignoring firmware upgrade "
+                                                "failure, and forcing driver "
+                                                "to reinitialize the "
+                                                "adapter.\n");
+                                       ret = 0;
+                               }
                }
                if (ret < 0)
                        return ret;
index 2bfbb20..ac311f5 100644 (file)
@@ -1294,7 +1294,7 @@ static inline unsigned int calc_tx_flits_ofld(const struct sk_buff *skb)
 
        flits = skb_transport_offset(skb) / 8U;   /* headers */
        cnt = skb_shinfo(skb)->nr_frags;
-       if (skb->tail != skb->transport_header)
+       if (skb_tail_pointer(skb) != skb_transport_header(skb))
                cnt++;
        return flits + sgl_len(cnt);
 }
index d02d4e8..4cbb2f9 100644 (file)
@@ -938,6 +938,15 @@ int t4_check_fw_version(struct adapter *adapter)
        memcpy(adapter->params.api_vers, api_vers,
               sizeof(adapter->params.api_vers));
 
+       if (major < exp_major || (major == exp_major && minor < exp_minor) ||
+           (major == exp_major && minor == exp_minor && micro < exp_micro)) {
+               dev_err(adapter->pdev_dev,
+                       "Card has firmware version %u.%u.%u, minimum "
+                       "supported firmware is %u.%u.%u.\n", major, minor,
+                       micro, exp_major, exp_minor, exp_micro);
+               return -EFAULT;
+       }
+
        if (major != exp_major) {            /* major mismatch - fail */
                dev_err(adapter->pdev_dev,
                        "card FW has major version %u, driver wants %u\n",
@@ -3773,7 +3782,6 @@ int t4_port_init(struct adapter *adap, int mbox, int pf, int vf)
                p->lport = j;
                p->rss_size = rss_size;
                memcpy(adap->port[i]->dev_addr, addr, ETH_ALEN);
-               adap->port[i]->dev_id = j;
 
                ret = ntohl(c.u.info.lstatus_to_modtype);
                p->mdio_addr = (ret & FW_PORT_CMD_MDIOCAP) ?
index 8388e36..7403dff 100644 (file)
@@ -44,7 +44,6 @@ config CS89x0_PLATFORM
 config EP93XX_ETH
        tristate "EP93xx Ethernet support"
        depends on ARM && ARCH_EP93XX
-       select NET_CORE
        select MII
        help
          This is a driver for the ethernet hardware included in EP93xx CPUs.
index 67b0388..e3d4ec8 100644 (file)
@@ -783,7 +783,6 @@ static int ep93xx_eth_remove(struct platform_device *pdev)
        dev = platform_get_drvdata(pdev);
        if (dev == NULL)
                return 0;
-       platform_set_drvdata(pdev, NULL);
 
        ep = netdev_priv(dev);
 
index 635f559..992ec2e 100644 (file)
@@ -1761,6 +1761,7 @@ static void enic_change_mtu_work(struct work_struct *work)
        enic_synchronize_irqs(enic);
        err = vnic_rq_disable(&enic->rq[0]);
        if (err) {
+               rtnl_unlock();
                netdev_err(netdev, "Unable to disable RQ.\n");
                return;
        }
@@ -1773,6 +1774,7 @@ static void enic_change_mtu_work(struct work_struct *work)
        vnic_rq_fill(&enic->rq[0], enic_rq_alloc_buf);
        /* Need at least one buffer on ring to get going */
        if (vnic_rq_desc_used(&enic->rq[0]) == 0) {
+               rtnl_unlock();
                netdev_err(netdev, "Unable to alloc receive buffers.\n");
                return;
        }
index 9745fe5..316c5e5 100644 (file)
@@ -6,7 +6,6 @@ config DM9000
        tristate "DM9000 support"
        depends on ARM || BLACKFIN || MIPS || COLDFIRE
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          Support for DM9000 chipset.
index 9105465..a13b312 100644 (file)
@@ -29,6 +29,8 @@
 #include <linux/spinlock.h>
 #include <linux/crc32.h>
 #include <linux/mii.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
 #include <linux/ethtool.h>
 #include <linux/dm9000.h>
 #include <linux/delay.h>
@@ -827,7 +829,7 @@ dm9000_hash_table_unlocked(struct net_device *dev)
        struct netdev_hw_addr *ha;
        int i, oft;
        u32 hash_val;
-       u16 hash_table[4];
+       u16 hash_table[4] = { 0, 0, 0, 0x8000 }; /* broadcast address */
        u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;
 
        dm9000_dbg(db, 1, "entering %s\n", __func__);
@@ -835,13 +837,6 @@ dm9000_hash_table_unlocked(struct net_device *dev)
        for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)
                iow(db, oft, dev->dev_addr[i]);
 
-       /* Clear Hash Table */
-       for (i = 0; i < 4; i++)
-               hash_table[i] = 0x0;
-
-       /* broadcast address */
-       hash_table[3] = 0x8000;
-
        if (dev->flags & IFF_PROMISC)
                rcr |= RCR_PRMSC;
 
@@ -1358,6 +1353,31 @@ static const struct net_device_ops dm9000_netdev_ops = {
 #endif
 };
 
+static struct dm9000_plat_data *dm9000_parse_dt(struct device *dev)
+{
+       struct dm9000_plat_data *pdata;
+       struct device_node *np = dev->of_node;
+       const void *mac_addr;
+
+       if (!IS_ENABLED(CONFIG_OF) || !np)
+               return NULL;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+
+       if (of_find_property(np, "davicom,ext-phy", NULL))
+               pdata->flags |= DM9000_PLATF_EXT_PHY;
+       if (of_find_property(np, "davicom,no-eeprom", NULL))
+               pdata->flags |= DM9000_PLATF_NO_EEPROM;
+
+       mac_addr = of_get_mac_address(np);
+       if (mac_addr)
+               memcpy(pdata->dev_addr, mac_addr, sizeof(pdata->dev_addr));
+
+       return pdata;
+}
+
 /*
  * Search DM9000 board, allocate space and register it
  */
@@ -1373,6 +1393,12 @@ dm9000_probe(struct platform_device *pdev)
        int i;
        u32 id_val;
 
+       if (!pdata) {
+               pdata = dm9000_parse_dt(&pdev->dev);
+               if (IS_ERR(pdata))
+                       return PTR_ERR(pdata);
+       }
+
        /* Init network device */
        ndev = alloc_etherdev(sizeof(struct board_info));
        if (!ndev)
@@ -1673,8 +1699,6 @@ dm9000_drv_remove(struct platform_device *pdev)
 {
        struct net_device *ndev = platform_get_drvdata(pdev);
 
-       platform_set_drvdata(pdev, NULL);
-
        unregister_netdev(ndev);
        dm9000_release_board(pdev, netdev_priv(ndev));
        free_netdev(ndev);              /* free device structure */
@@ -1683,11 +1707,20 @@ dm9000_drv_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id dm9000_of_matches[] = {
+       { .compatible = "davicom,dm9000", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dm9000_of_matches);
+#endif
+
 static struct platform_driver dm9000_driver = {
        .driver = {
                .name    = "dm9000",
                .owner   = THIS_MODULE,
                .pm      = &dm9000_drv_pm_ops,
+               .of_match_table = of_match_ptr(dm9000_of_matches),
        },
        .probe   = dm9000_probe,
        .remove  = dm9000_drv_remove,
index 1df33c7..eb9ba6e 100644 (file)
@@ -126,7 +126,6 @@ config WINBOND_840
        tristate "Winbond W89c840 Ethernet support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          This driver is for the Winbond W89c840 chip.  It also works with 
index 1e9443d..c94152f 100644 (file)
@@ -1410,12 +1410,6 @@ static int tulip_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                return i;
        }
 
-       /* The chip will fail to enter a low-power state later unless
-        * first explicitly commanded into D0 */
-       if (pci_set_power_state(pdev, PCI_D0)) {
-               pr_notice("Failed to set power state to D0\n");
-       }
-
        irq = pdev->irq;
 
        /* alloc_etherdev ensures aligned and zeroed private structures */
index cdbcd16..9b84cb0 100644 (file)
@@ -1171,16 +1171,4 @@ investigate_write_descriptor(struct net_device *dev,
        }
 }
 
-static int __init xircom_init(void)
-{
-       return pci_register_driver(&xircom_ops);
-}
-
-static void __exit xircom_exit(void)
-{
-       pci_unregister_driver(&xircom_ops);
-}
-
-module_init(xircom_init)
-module_exit(xircom_exit)
-
+module_pci_driver(xircom_ops);
index ee26ce7..c543ac1 100644 (file)
@@ -36,7 +36,6 @@ config SUNDANCE
        tristate "Sundance Alta support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          This driver is for the Sundance "Alta" chip.
index 0a51068..c827b1b 100644 (file)
@@ -333,6 +333,9 @@ enum vf_state {
 #define BE_VF_UC_PMAC_COUNT            2
 #define BE_FLAGS_QNQ_ASYNC_EVT_RCVD            (1 << 11)
 
+/* Ethtool set_dump flags */
+#define LANCER_INITIATE_FW_DUMP                        0x1
+
 struct phy_info {
        u8 transceiver;
        u8 autoneg;
@@ -398,6 +401,7 @@ struct be_adapter {
        u32 cmd_privileges;
        /* Ethtool knobs and info */
        char fw_ver[FW_VER_LEN];
+       char fw_on_flash[FW_VER_LEN];
        int if_handle;          /* Used to configure filtering */
        u32 *pmac_id;           /* MAC addr handle used by BE card */
        u32 beacon_state;       /* for set_phys_id */
index 1db2df6..6e6e0a1 100644 (file)
@@ -3255,6 +3255,72 @@ err:
        return status;
 }
 
+static int lancer_wait_idle(struct be_adapter *adapter)
+{
+#define SLIPORT_IDLE_TIMEOUT 30
+       u32 reg_val;
+       int status = 0, i;
+
+       for (i = 0; i < SLIPORT_IDLE_TIMEOUT; i++) {
+               reg_val = ioread32(adapter->db + PHYSDEV_CONTROL_OFFSET);
+               if ((reg_val & PHYSDEV_CONTROL_INP_MASK) == 0)
+                       break;
+
+               ssleep(1);
+       }
+
+       if (i == SLIPORT_IDLE_TIMEOUT)
+               status = -1;
+
+       return status;
+}
+
+int lancer_physdev_ctrl(struct be_adapter *adapter, u32 mask)
+{
+       int status = 0;
+
+       status = lancer_wait_idle(adapter);
+       if (status)
+               return status;
+
+       iowrite32(mask, adapter->db + PHYSDEV_CONTROL_OFFSET);
+
+       return status;
+}
+
+/* Routine to check whether dump image is present or not */
+bool dump_present(struct be_adapter *adapter)
+{
+       u32 sliport_status = 0;
+
+       sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET);
+       return !!(sliport_status & SLIPORT_STATUS_DIP_MASK);
+}
+
+int lancer_initiate_dump(struct be_adapter *adapter)
+{
+       int status;
+
+       /* give firmware reset and diagnostic dump */
+       status = lancer_physdev_ctrl(adapter, PHYSDEV_CONTROL_FW_RESET_MASK |
+                                    PHYSDEV_CONTROL_DD_MASK);
+       if (status < 0) {
+               dev_err(&adapter->pdev->dev, "Firmware reset failed\n");
+               return status;
+       }
+
+       status = lancer_wait_idle(adapter);
+       if (status)
+               return status;
+
+       if (!dump_present(adapter)) {
+               dev_err(&adapter->pdev->dev, "Dump image not present\n");
+               return -1;
+       }
+
+       return 0;
+}
+
 /* Uses sync mcc */
 int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain)
 {
index 025bdb0..5228d88 100644 (file)
@@ -1937,6 +1937,9 @@ extern int be_cmd_set_ext_fat_capabilites(struct be_adapter *adapter,
                                          struct be_dma_mem *cmd,
                                          struct be_fat_conf_params *cfgs);
 extern int lancer_wait_ready(struct be_adapter *adapter);
+extern int lancer_physdev_ctrl(struct be_adapter *adapter, u32 mask);
+extern int lancer_initiate_dump(struct be_adapter *adapter);
+extern bool dump_present(struct be_adapter *adapter);
 extern int lancer_test_and_set_rdy_state(struct be_adapter *adapter);
 extern int be_cmd_query_port_name(struct be_adapter *adapter, u8 *port_name);
 extern int be_cmd_get_func_config(struct be_adapter *adapter);
index 3d4461a..4f8c941 100644 (file)
@@ -177,19 +177,15 @@ static void be_get_drvinfo(struct net_device *netdev,
                                struct ethtool_drvinfo *drvinfo)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
-       char fw_on_flash[FW_VER_LEN];
-
-       memset(fw_on_flash, 0 , sizeof(fw_on_flash));
-       be_cmd_get_fw_ver(adapter, adapter->fw_ver, fw_on_flash);
 
        strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
        strlcpy(drvinfo->version, DRV_VER, sizeof(drvinfo->version));
-       if (!memcmp(adapter->fw_ver, fw_on_flash, FW_VER_LEN))
+       if (!memcmp(adapter->fw_ver, adapter->fw_on_flash, FW_VER_LEN))
                strlcpy(drvinfo->fw_version, adapter->fw_ver,
                        sizeof(drvinfo->fw_version));
        else
                snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
-                        "%s [%s]", adapter->fw_ver, fw_on_flash);
+                        "%s [%s]", adapter->fw_ver, adapter->fw_on_flash);
 
        strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
                sizeof(drvinfo->bus_info));
@@ -673,6 +669,34 @@ be_set_phys_id(struct net_device *netdev,
        return 0;
 }
 
+static int be_set_dump(struct net_device *netdev, struct ethtool_dump *dump)
+{
+       struct be_adapter *adapter = netdev_priv(netdev);
+       struct device *dev = &adapter->pdev->dev;
+       int status;
+
+       if (!lancer_chip(adapter)) {
+               dev_err(dev, "FW dump not supported\n");
+               return -EOPNOTSUPP;
+       }
+
+       if (dump_present(adapter)) {
+               dev_err(dev, "Previous dump not cleared, not forcing dump\n");
+               return 0;
+       }
+
+       switch (dump->flag) {
+       case LANCER_INITIATE_FW_DUMP:
+               status = lancer_initiate_dump(adapter);
+               if (!status)
+                       dev_info(dev, "F/w dump initiated successfully\n");
+               break;
+       default:
+               dev_err(dev, "Invalid dump level: 0x%x\n", dump->flag);
+               return -EINVAL;
+       }
+       return status;
+}
 
 static void
 be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
@@ -1110,6 +1134,7 @@ const struct ethtool_ops be_ethtool_ops = {
        .set_pauseparam = be_set_pauseparam,
        .get_strings = be_get_stat_strings,
        .set_phys_id = be_set_phys_id,
+       .set_dump = be_set_dump,
        .get_msglevel = be_get_msg_level,
        .set_msglevel = be_set_msg_level,
        .get_sset_count = be_get_sset_count,
index 8780183..3e21621 100644 (file)
 #define PHYSDEV_CONTROL_OFFSET         0x414
 
 #define SLIPORT_STATUS_ERR_MASK                0x80000000
+#define SLIPORT_STATUS_DIP_MASK                0x02000000
 #define SLIPORT_STATUS_RN_MASK         0x01000000
 #define SLIPORT_STATUS_RDY_MASK                0x00800000
 #define SLI_PORT_CONTROL_IP_MASK       0x08000000
 #define PHYSDEV_CONTROL_FW_RESET_MASK  0x00000002
+#define PHYSDEV_CONTROL_DD_MASK                0x00000004
 #define PHYSDEV_CONTROL_INP_MASK       0x40000000
 
 #define SLIPORT_ERROR_NO_RESOURCE1     0x2
index a0b4be5..2df48bb 100644 (file)
@@ -834,32 +834,39 @@ static int be_vlan_tag_tx_chk(struct be_adapter *adapter, struct sk_buff *skb)
        return vlan_tx_tag_present(skb) || adapter->pvid || adapter->qnq_vid;
 }
 
-static int be_ipv6_tx_stall_chk(struct be_adapter *adapter, struct sk_buff *skb)
+static int be_ipv6_tx_stall_chk(struct be_adapter *adapter,
+                               struct sk_buff *skb)
 {
-       return BE3_chip(adapter) &&
-               be_ipv6_exthdr_check(skb);
+       return BE3_chip(adapter) && be_ipv6_exthdr_check(skb);
 }
 
-static netdev_tx_t be_xmit(struct sk_buff *skb,
-                       struct net_device *netdev)
+static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter,
+                                          struct sk_buff *skb,
+                                          bool *skip_hw_vlan)
 {
-       struct be_adapter *adapter = netdev_priv(netdev);
-       struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)];
-       struct be_queue_info *txq = &txo->q;
-       struct iphdr *ip = NULL;
-       u32 wrb_cnt = 0, copied = 0;
-       u32 start = txq->head, eth_hdr_len;
-       bool dummy_wrb, stopped = false;
-       bool skip_hw_vlan = false;
        struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
+       unsigned int eth_hdr_len;
+       struct iphdr *ip;
 
-       eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ?
-               VLAN_ETH_HLEN : ETH_HLEN;
+       /* Lancer ASIC has a bug wherein packets that are 32 bytes or less
+        * may cause a transmit stall on that port. So the work-around is to
+        * pad such packets to a 36-byte length.
+        */
+       if (unlikely(lancer_chip(adapter) && skb->len <= 32)) {
+               if (skb_padto(skb, 36))
+                       goto tx_drop;
+               skb->len = 36;
+       }
 
        /* For padded packets, BE HW modifies tot_len field in IP header
         * incorrecly when VLAN tag is inserted by HW.
+        * For padded packets, Lancer computes incorrect checksum.
         */
-       if (skb->len <= 60 && vlan_tx_tag_present(skb) && is_ipv4_pkt(skb)) {
+       eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ?
+                                               VLAN_ETH_HLEN : ETH_HLEN;
+       if (skb->len <= 60 &&
+           (lancer_chip(adapter) || vlan_tx_tag_present(skb)) &&
+           is_ipv4_pkt(skb)) {
                ip = (struct iphdr *)ip_hdr(skb);
                pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len));
        }
@@ -869,15 +876,15 @@ static netdev_tx_t be_xmit(struct sk_buff *skb,
         */
        if ((adapter->function_mode & UMC_ENABLED) &&
            veh->h_vlan_proto == htons(ETH_P_8021Q))
-                       skip_hw_vlan = true;
+                       *skip_hw_vlan = true;
 
        /* HW has a bug wherein it will calculate CSUM for VLAN
         * pkts even though it is disabled.
         * Manually insert VLAN in pkt.
         */
        if (skb->ip_summed != CHECKSUM_PARTIAL &&
-                       vlan_tx_tag_present(skb)) {
-               skb = be_insert_vlan_in_pkt(adapter, skb, &skip_hw_vlan);
+           vlan_tx_tag_present(skb)) {
+               skb = be_insert_vlan_in_pkt(adapter, skb, skip_hw_vlan);
                if (unlikely(!skb))
                        goto tx_drop;
        }
@@ -887,8 +894,8 @@ static netdev_tx_t be_xmit(struct sk_buff *skb,
         * skip HW tagging is not enabled by FW.
         */
        if (unlikely(be_ipv6_tx_stall_chk(adapter, skb) &&
-                    (adapter->pvid || adapter->qnq_vid) &&
-                    !qnq_async_evt_rcvd(adapter)))
+           (adapter->pvid || adapter->qnq_vid) &&
+           !qnq_async_evt_rcvd(adapter)))
                goto tx_drop;
 
        /* Manual VLAN tag insertion to prevent:
@@ -899,11 +906,31 @@ static netdev_tx_t be_xmit(struct sk_buff *skb,
         */
        if (be_ipv6_tx_stall_chk(adapter, skb) &&
            be_vlan_tag_tx_chk(adapter, skb)) {
-               skb = be_insert_vlan_in_pkt(adapter, skb, &skip_hw_vlan);
+               skb = be_insert_vlan_in_pkt(adapter, skb, skip_hw_vlan);
                if (unlikely(!skb))
                        goto tx_drop;
        }
 
+       return skb;
+tx_drop:
+       dev_kfree_skb_any(skb);
+       return NULL;
+}
+
+static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+       struct be_adapter *adapter = netdev_priv(netdev);
+       struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)];
+       struct be_queue_info *txq = &txo->q;
+       bool dummy_wrb, stopped = false;
+       u32 wrb_cnt = 0, copied = 0;
+       bool skip_hw_vlan = false;
+       u32 start = txq->head;
+
+       skb = be_xmit_workarounds(adapter, skb, &skip_hw_vlan);
+       if (!skb)
+               return NETDEV_TX_OK;
+
        wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb);
 
        copied = make_tx_wrbs(adapter, txq, skb, wrb_cnt, dummy_wrb,
@@ -933,7 +960,6 @@ static netdev_tx_t be_xmit(struct sk_buff *skb,
                txq->head = start;
                dev_kfree_skb_any(skb);
        }
-tx_drop:
        return NETDEV_TX_OK;
 }
 
@@ -1236,30 +1262,6 @@ static int be_set_vf_tx_rate(struct net_device *netdev,
        return status;
 }
 
-static int be_find_vfs(struct be_adapter *adapter, int vf_state)
-{
-       struct pci_dev *dev, *pdev = adapter->pdev;
-       int vfs = 0, assigned_vfs = 0, pos;
-       u16 offset, stride;
-
-       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
-       if (!pos)
-               return 0;
-       pci_read_config_word(pdev, pos + PCI_SRIOV_VF_OFFSET, &offset);
-       pci_read_config_word(pdev, pos + PCI_SRIOV_VF_STRIDE, &stride);
-
-       dev = pci_get_device(pdev->vendor, PCI_ANY_ID, NULL);
-       while (dev) {
-               if (dev->is_virtfn && pci_physfn(dev) == pdev) {
-                       vfs++;
-                       if (dev->dev_flags & PCI_DEV_FLAGS_ASSIGNED)
-                               assigned_vfs++;
-               }
-               dev = pci_get_device(pdev->vendor, PCI_ANY_ID, dev);
-       }
-       return (vf_state == ASSIGNED) ? assigned_vfs : vfs;
-}
-
 static void be_eqd_update(struct be_adapter *adapter, struct be_eq_obj *eqo)
 {
        struct be_rx_stats *stats = rx_stats(&adapter->rx_obj[eqo->idx]);
@@ -2771,7 +2773,7 @@ static void be_vf_clear(struct be_adapter *adapter)
        struct be_vf_cfg *vf_cfg;
        u32 vf;
 
-       if (be_find_vfs(adapter, ASSIGNED)) {
+       if (pci_vfs_assigned(adapter->pdev)) {
                dev_warn(&adapter->pdev->dev,
                         "VFs are assigned to VMs: not disabling VFs\n");
                goto done;
@@ -2873,7 +2875,7 @@ static int be_vf_setup(struct be_adapter *adapter)
        int status, old_vfs, vf;
        struct device *dev = &adapter->pdev->dev;
 
-       old_vfs = be_find_vfs(adapter, ENABLED);
+       old_vfs = pci_num_vf(adapter->pdev);
        if (old_vfs) {
                dev_info(dev, "%d VFs are already enabled\n", old_vfs);
                if (old_vfs != num_vfs)
@@ -3184,7 +3186,7 @@ static int be_setup(struct be_adapter *adapter)
        if (status)
                goto err;
 
-       be_cmd_get_fw_ver(adapter, adapter->fw_ver, NULL);
+       be_cmd_get_fw_ver(adapter, adapter->fw_ver, adapter->fw_on_flash);
 
        if (adapter->vlans_added)
                be_vid_config(adapter);
@@ -3530,40 +3532,6 @@ static int be_flash_skyhawk(struct be_adapter *adapter,
        return 0;
 }
 
-static int lancer_wait_idle(struct be_adapter *adapter)
-{
-#define SLIPORT_IDLE_TIMEOUT 30
-       u32 reg_val;
-       int status = 0, i;
-
-       for (i = 0; i < SLIPORT_IDLE_TIMEOUT; i++) {
-               reg_val = ioread32(adapter->db + PHYSDEV_CONTROL_OFFSET);
-               if ((reg_val & PHYSDEV_CONTROL_INP_MASK) == 0)
-                       break;
-
-               ssleep(1);
-       }
-
-       if (i == SLIPORT_IDLE_TIMEOUT)
-               status = -1;
-
-       return status;
-}
-
-static int lancer_fw_reset(struct be_adapter *adapter)
-{
-       int status = 0;
-
-       status = lancer_wait_idle(adapter);
-       if (status)
-               return status;
-
-       iowrite32(PHYSDEV_CONTROL_FW_RESET_MASK, adapter->db +
-                 PHYSDEV_CONTROL_OFFSET);
-
-       return status;
-}
-
 static int lancer_fw_download(struct be_adapter *adapter,
                                const struct firmware *fw)
 {
@@ -3641,7 +3609,8 @@ static int lancer_fw_download(struct be_adapter *adapter,
        }
 
        if (change_status == LANCER_FW_RESET_NEEDED) {
-               status = lancer_fw_reset(adapter);
+               status = lancer_physdev_ctrl(adapter,
+                                            PHYSDEV_CONTROL_FW_RESET_MASK);
                if (status) {
                        dev_err(&adapter->pdev->dev,
                                "Adapter busy for FW reset.\n"
@@ -3776,6 +3745,10 @@ int be_load_fw(struct be_adapter *adapter, u8 *fw_file)
        else
                status = be_fw_download(adapter, fw);
 
+       if (!status)
+               be_cmd_get_fw_ver(adapter, adapter->fw_ver,
+                                 adapter->fw_on_flash);
+
 fw_exit:
        release_firmware(fw);
        return status;
@@ -4203,9 +4176,10 @@ reschedule:
        schedule_delayed_work(&adapter->work, msecs_to_jiffies(1000));
 }
 
+/* If any VFs are already enabled don't FLR the PF */
 static bool be_reset_required(struct be_adapter *adapter)
 {
-       return be_find_vfs(adapter, ENABLED) > 0 ? false : true;
+       return pci_num_vf(adapter->pdev) ? false : true;
 }
 
 static char *mc_name(struct be_adapter *adapter)
@@ -4390,7 +4364,7 @@ static int be_resume(struct pci_dev *pdev)
        if (status)
                return status;
 
-       pci_set_power_state(pdev, 0);
+       pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
 
        /* tell fw we're ready to fire cmds */
@@ -4486,7 +4460,7 @@ static pci_ers_result_t be_eeh_reset(struct pci_dev *pdev)
                return PCI_ERS_RESULT_DISCONNECT;
 
        pci_set_master(pdev);
-       pci_set_power_state(pdev, 0);
+       pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
 
        /* Check if card is ok and fw is ready */
index 5722bc6..cf579fb 100644 (file)
@@ -1147,8 +1147,6 @@ static int ethoc_remove(struct platform_device *pdev)
        struct net_device *netdev = platform_get_drvdata(pdev);
        struct ethoc *priv = netdev_priv(netdev);
 
-       platform_set_drvdata(pdev, NULL);
-
        if (netdev) {
                netif_napi_del(&priv->napi);
                phy_disconnect(priv->phy);
index b8974b9..5918c68 100644 (file)
@@ -21,7 +21,6 @@ if NET_VENDOR_FARADAY
 config FTMAC100
        tristate "Faraday FTMAC100 10/100 Ethernet support"
        depends on ARM
-       select NET_CORE
        select MII
        ---help---
          This driver supports the FTMAC100 10/100 Ethernet controller
index 21b85fb..934e1ae 100644 (file)
@@ -1311,7 +1311,6 @@ err_ioremap:
        release_resource(priv->res);
 err_req_mem:
        netif_napi_del(&priv->napi);
-       platform_set_drvdata(pdev, NULL);
        free_netdev(netdev);
 err_alloc_etherdev:
        return err;
@@ -1335,7 +1334,6 @@ static int __exit ftgmac100_remove(struct platform_device *pdev)
        release_resource(priv->res);
 
        netif_napi_del(&priv->napi);
-       platform_set_drvdata(pdev, NULL);
        free_netdev(netdev);
        return 0;
 }
index a6eda8d..4658f4c 100644 (file)
@@ -1149,7 +1149,6 @@ err_ioremap:
        release_resource(priv->res);
 err_req_mem:
        netif_napi_del(&priv->napi);
-       platform_set_drvdata(pdev, NULL);
        free_netdev(netdev);
 err_alloc_etherdev:
        return err;
@@ -1169,7 +1168,6 @@ static int __exit ftmac100_remove(struct platform_device *pdev)
        release_resource(priv->res);
 
        netif_napi_del(&priv->napi);
-       platform_set_drvdata(pdev, NULL);
        free_netdev(netdev);
        return 0;
 }
index 9ce5b71..2b0a0ea 100644 (file)
 #define BM_MIIGSK_CFGR_RMII            0x01
 #define BM_MIIGSK_CFGR_FRCONT_10M      0x40
 
+#define RMON_T_DROP            0x200 /* Count of frames not cntd correctly */
+#define RMON_T_PACKETS         0x204 /* RMON TX packet count */
+#define RMON_T_BC_PKT          0x208 /* RMON TX broadcast pkts */
+#define RMON_T_MC_PKT          0x20C /* RMON TX multicast pkts */
+#define RMON_T_CRC_ALIGN       0x210 /* RMON TX pkts with CRC align err */
+#define RMON_T_UNDERSIZE       0x214 /* RMON TX pkts < 64 bytes, good CRC */
+#define RMON_T_OVERSIZE                0x218 /* RMON TX pkts > MAX_FL bytes good CRC */
+#define RMON_T_FRAG            0x21C /* RMON TX pkts < 64 bytes, bad CRC */
+#define RMON_T_JAB             0x220 /* RMON TX pkts > MAX_FL bytes, bad CRC */
+#define RMON_T_COL             0x224 /* RMON TX collision count */
+#define RMON_T_P64             0x228 /* RMON TX 64 byte pkts */
+#define RMON_T_P65TO127                0x22C /* RMON TX 65 to 127 byte pkts */
+#define RMON_T_P128TO255       0x230 /* RMON TX 128 to 255 byte pkts */
+#define RMON_T_P256TO511       0x234 /* RMON TX 256 to 511 byte pkts */
+#define RMON_T_P512TO1023      0x238 /* RMON TX 512 to 1023 byte pkts */
+#define RMON_T_P1024TO2047     0x23C /* RMON TX 1024 to 2047 byte pkts */
+#define RMON_T_P_GTE2048       0x240 /* RMON TX pkts > 2048 bytes */
+#define RMON_T_OCTETS          0x244 /* RMON TX octets */
+#define IEEE_T_DROP            0x248 /* Count of frames not counted crtly */
+#define IEEE_T_FRAME_OK                0x24C /* Frames tx'd OK */
+#define IEEE_T_1COL            0x250 /* Frames tx'd with single collision */
+#define IEEE_T_MCOL            0x254 /* Frames tx'd with multiple collision */
+#define IEEE_T_DEF             0x258 /* Frames tx'd after deferral delay */
+#define IEEE_T_LCOL            0x25C /* Frames tx'd with late collision */
+#define IEEE_T_EXCOL           0x260 /* Frames tx'd with excesv collisions */
+#define IEEE_T_MACERR          0x264 /* Frames tx'd with TX FIFO underrun */
+#define IEEE_T_CSERR           0x268 /* Frames tx'd with carrier sense err */
+#define IEEE_T_SQE             0x26C /* Frames tx'd with SQE err */
+#define IEEE_T_FDXFC           0x270 /* Flow control pause frames tx'd */
+#define IEEE_T_OCTETS_OK       0x274 /* Octet count for frames tx'd w/o err */
+#define RMON_R_PACKETS         0x284 /* RMON RX packet count */
+#define RMON_R_BC_PKT          0x288 /* RMON RX broadcast pkts */
+#define RMON_R_MC_PKT          0x28C /* RMON RX multicast pkts */
+#define RMON_R_CRC_ALIGN       0x290 /* RMON RX pkts with CRC alignment err */
+#define RMON_R_UNDERSIZE       0x294 /* RMON RX pkts < 64 bytes, good CRC */
+#define RMON_R_OVERSIZE                0x298 /* RMON RX pkts > MAX_FL bytes good CRC */
+#define RMON_R_FRAG            0x29C /* RMON RX pkts < 64 bytes, bad CRC */
+#define RMON_R_JAB             0x2A0 /* RMON RX pkts > MAX_FL bytes, bad CRC */
+#define RMON_R_RESVD_O         0x2A4 /* Reserved */
+#define RMON_R_P64             0x2A8 /* RMON RX 64 byte pkts */
+#define RMON_R_P65TO127                0x2AC /* RMON RX 65 to 127 byte pkts */
+#define RMON_R_P128TO255       0x2B0 /* RMON RX 128 to 255 byte pkts */
+#define RMON_R_P256TO511       0x2B4 /* RMON RX 256 to 511 byte pkts */
+#define RMON_R_P512TO1023      0x2B8 /* RMON RX 512 to 1023 byte pkts */
+#define RMON_R_P1024TO2047     0x2BC /* RMON RX 1024 to 2047 byte pkts */
+#define RMON_R_P_GTE2048       0x2C0 /* RMON RX pkts > 2048 bytes */
+#define RMON_R_OCTETS          0x2C4 /* RMON RX octets */
+#define IEEE_R_DROP            0x2C8 /* Count frames not counted correctly */
+#define IEEE_R_FRAME_OK                0x2CC /* Frames rx'd OK */
+#define IEEE_R_CRC             0x2D0 /* Frames rx'd with CRC err */
+#define IEEE_R_ALIGN           0x2D4 /* Frames rx'd with alignment err */
+#define IEEE_R_MACERR          0x2D8 /* Receive FIFO overflow count */
+#define IEEE_R_FDXFC           0x2DC /* Flow control pause frames rx'd */
+#define IEEE_R_OCTETS_OK       0x2E0 /* Octet cnt for frames rx'd w/o err */
+
 #else
 
 #define FEC_ECNTRL             0x000 /* Ethernet control reg */
@@ -148,6 +203,9 @@ struct bufdesc_ex {
 #define BD_ENET_RX_CL           ((ushort)0x0001)
 #define BD_ENET_RX_STATS        ((ushort)0x013f)        /* All status bits */
 
+/* Enhanced buffer descriptor control/status used by Ethernet receive */
+#define BD_ENET_RX_VLAN         0x00000004
+
 /* Buffer descriptor control/status used by Ethernet transmit.
 */
 #define BD_ENET_TX_READY        ((ushort)0x8000)
@@ -272,9 +330,10 @@ struct fec_enet_private {
        int hwts_tx_en;
        struct timer_list time_keep;
        struct fec_enet_delayed_work delay_work;
+       struct regulator *reg_phy;
 };
 
-void fec_ptp_init(struct net_device *ndev, struct platform_device *pdev);
+void fec_ptp_init(struct platform_device *pdev);
 void fec_ptp_start_cyclecounter(struct net_device *ndev);
 int fec_ptp_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd);
 
index d48099f..d3ad5ea 100644 (file)
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
 #include <linux/of_net.h>
-#include <linux/pinctrl/consumer.h>
 #include <linux/regulator/consumer.h>
+#include <linux/if_vlan.h>
 
 #include <asm/cacheflush.h>
 
 #include "fec.h"
 
+static void set_multicast_list(struct net_device *ndev);
+
 #if defined(CONFIG_ARM)
 #define FEC_ALIGNMENT  0xf
 #else
@@ -89,6 +91,8 @@
 #define FEC_QUIRK_HAS_BUFDESC_EX       (1 << 4)
 /* Controller has hardware checksum support */
 #define FEC_QUIRK_HAS_CSUM             (1 << 5)
+/* Controller has hardware vlan support */
+#define FEC_QUIRK_HAS_VLAN             (1 << 6)
 
 static struct platform_device_id fec_devtype[] = {
        {
@@ -107,7 +111,8 @@ static struct platform_device_id fec_devtype[] = {
        }, {
                .name = "imx6q-fec",
                .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
-                               FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM,
+                               FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
+                               FEC_QUIRK_HAS_VLAN,
        }, {
                .name = "mvf600-fec",
                .driver_data = FEC_QUIRK_ENET_MAC,
@@ -178,11 +183,11 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
 #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
 
-/* The FEC stores dest/src/type, data, and checksum for receive packets.
+/* The FEC stores dest/src/type/vlan, data, and checksum for receive packets.
  */
-#define PKT_MAXBUF_SIZE                1518
+#define PKT_MAXBUF_SIZE                1522
 #define PKT_MINBUF_SIZE                64
-#define PKT_MAXBLR_SIZE                1520
+#define PKT_MAXBLR_SIZE                1536
 
 /* FEC receive acceleration */
 #define FEC_RACC_IPDIS         (1 << 1)
@@ -243,7 +248,7 @@ static void *swap_buffer(void *bufaddr, int len)
        int i;
        unsigned int *buf = bufaddr;
 
-       for (i = 0; i < (len + 3) / 4; i++, buf++)
+       for (i = 0; i < DIV_ROUND_UP(len, 4); i++, buf++)
                *buf = cpu_to_be32(*buf);
 
        return bufaddr;
@@ -471,9 +476,8 @@ fec_restart(struct net_device *ndev, int duplex)
        /* Clear any outstanding interrupt. */
        writel(0xffc00000, fep->hwp + FEC_IEVENT);
 
-       /* Reset all multicast. */
-       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
-       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+       /* Setup multicast filter. */
+       set_multicast_list(ndev);
 #ifndef CONFIG_M5272
        writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
        writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
@@ -609,6 +613,11 @@ fec_restart(struct net_device *ndev, int duplex)
        if (fep->bufdesc_ex)
                ecntl |= (1 << 4);
 
+#ifndef CONFIG_M5272
+       /* Enable the MIB statistic event counters */
+       writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT);
+#endif
+
        /* And last, enable the transmit and receive processing */
        writel(ecntl, fep->hwp + FEC_ECNTRL);
        writel(0, fep->hwp + FEC_R_DES_ACTIVE);
@@ -735,6 +744,7 @@ fec_enet_tx(struct net_device *ndev)
                                ndev->stats.tx_carrier_errors++;
                } else {
                        ndev->stats.tx_packets++;
+                       ndev->stats.tx_bytes += bdp->cbd_datlen;
                }
 
                if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
@@ -800,6 +810,9 @@ fec_enet_rx(struct net_device *ndev, int budget)
        ushort  pkt_len;
        __u8 *data;
        int     pkt_received = 0;
+       struct  bufdesc_ex *ebdp = NULL;
+       bool    vlan_packet_rcvd = false;
+       u16     vlan_tag;
 
 #ifdef CONFIG_M532x
        flush_cache_all();
@@ -863,6 +876,24 @@ fec_enet_rx(struct net_device *ndev, int budget)
                if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
                        swap_buffer(data, pkt_len);
 
+               /* Extract the enhanced buffer descriptor */
+               ebdp = NULL;
+               if (fep->bufdesc_ex)
+                       ebdp = (struct bufdesc_ex *)bdp;
+
+               /* If this is a VLAN packet remove the VLAN Tag */
+               vlan_packet_rcvd = false;
+               if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
+                   fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) {
+                       /* Push and remove the vlan tag */
+                       struct vlan_hdr *vlan_header =
+                                       (struct vlan_hdr *) (data + ETH_HLEN);
+                       vlan_tag = ntohs(vlan_header->h_vlan_TCI);
+                       pkt_len -= VLAN_HLEN;
+
+                       vlan_packet_rcvd = true;
+               }
+
                /* This does 16 byte alignment, exactly what we need.
                 * The packet length includes FCS, but we don't want to
                 * include that when passing upstream as it messes up
@@ -873,9 +904,18 @@ fec_enet_rx(struct net_device *ndev, int budget)
                if (unlikely(!skb)) {
                        ndev->stats.rx_dropped++;
                } else {
+                       int payload_offset = (2 * ETH_ALEN);
                        skb_reserve(skb, NET_IP_ALIGN);
                        skb_put(skb, pkt_len - 4);      /* Make room */
-                       skb_copy_to_linear_data(skb, data, pkt_len - 4);
+
+                       /* Extract the frame data without the VLAN header. */
+                       skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN));
+                       if (vlan_packet_rcvd)
+                               payload_offset = (2 * ETH_ALEN) + VLAN_HLEN;
+                       skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN),
+                                                      data + payload_offset,
+                                                      pkt_len - 4 - (2 * ETH_ALEN));
+
                        skb->protocol = eth_type_trans(skb, ndev);
 
                        /* Get receive timestamp from the skb */
@@ -883,8 +923,6 @@ fec_enet_rx(struct net_device *ndev, int budget)
                                struct skb_shared_hwtstamps *shhwtstamps =
                                                            skb_hwtstamps(skb);
                                unsigned long flags;
-                               struct bufdesc_ex *ebdp =
-                                       (struct bufdesc_ex *)bdp;
 
                                memset(shhwtstamps, 0, sizeof(*shhwtstamps));
 
@@ -895,9 +933,7 @@ fec_enet_rx(struct net_device *ndev, int budget)
                        }
 
                        if (fep->bufdesc_ex &&
-                               (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) {
-                               struct bufdesc_ex *ebdp =
-                                       (struct bufdesc_ex *)bdp;
+                           (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) {
                                if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) {
                                        /* don't check it */
                                        skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -906,6 +942,12 @@ fec_enet_rx(struct net_device *ndev, int budget)
                                }
                        }
 
+                       /* Handle received VLAN packets */
+                       if (vlan_packet_rcvd)
+                               __vlan_hwaccel_put_tag(skb,
+                                                      htons(ETH_P_8021Q),
+                                                      vlan_tag);
+
                        if (!skb_defer_rx_timestamp(skb))
                                napi_gro_receive(&fep->napi, skb);
                }
@@ -1444,8 +1486,117 @@ static int fec_enet_set_pauseparam(struct net_device *ndev,
        return 0;
 }
 
+static const struct fec_stat {
+       char name[ETH_GSTRING_LEN];
+       u16 offset;
+} fec_stats[] = {
+       /* RMON TX */
+       { "tx_dropped", RMON_T_DROP },
+       { "tx_packets", RMON_T_PACKETS },
+       { "tx_broadcast", RMON_T_BC_PKT },
+       { "tx_multicast", RMON_T_MC_PKT },
+       { "tx_crc_errors", RMON_T_CRC_ALIGN },
+       { "tx_undersize", RMON_T_UNDERSIZE },
+       { "tx_oversize", RMON_T_OVERSIZE },
+       { "tx_fragment", RMON_T_FRAG },
+       { "tx_jabber", RMON_T_JAB },
+       { "tx_collision", RMON_T_COL },
+       { "tx_64byte", RMON_T_P64 },
+       { "tx_65to127byte", RMON_T_P65TO127 },
+       { "tx_128to255byte", RMON_T_P128TO255 },
+       { "tx_256to511byte", RMON_T_P256TO511 },
+       { "tx_512to1023byte", RMON_T_P512TO1023 },
+       { "tx_1024to2047byte", RMON_T_P1024TO2047 },
+       { "tx_GTE2048byte", RMON_T_P_GTE2048 },
+       { "tx_octets", RMON_T_OCTETS },
+
+       /* IEEE TX */
+       { "IEEE_tx_drop", IEEE_T_DROP },
+       { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK },
+       { "IEEE_tx_1col", IEEE_T_1COL },
+       { "IEEE_tx_mcol", IEEE_T_MCOL },
+       { "IEEE_tx_def", IEEE_T_DEF },
+       { "IEEE_tx_lcol", IEEE_T_LCOL },
+       { "IEEE_tx_excol", IEEE_T_EXCOL },
+       { "IEEE_tx_macerr", IEEE_T_MACERR },
+       { "IEEE_tx_cserr", IEEE_T_CSERR },
+       { "IEEE_tx_sqe", IEEE_T_SQE },
+       { "IEEE_tx_fdxfc", IEEE_T_FDXFC },
+       { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK },
+
+       /* RMON RX */
+       { "rx_packets", RMON_R_PACKETS },
+       { "rx_broadcast", RMON_R_BC_PKT },
+       { "rx_multicast", RMON_R_MC_PKT },
+       { "rx_crc_errors", RMON_R_CRC_ALIGN },
+       { "rx_undersize", RMON_R_UNDERSIZE },
+       { "rx_oversize", RMON_R_OVERSIZE },
+       { "rx_fragment", RMON_R_FRAG },
+       { "rx_jabber", RMON_R_JAB },
+       { "rx_64byte", RMON_R_P64 },
+       { "rx_65to127byte", RMON_R_P65TO127 },
+       { "rx_128to255byte", RMON_R_P128TO255 },
+       { "rx_256to511byte", RMON_R_P256TO511 },
+       { "rx_512to1023byte", RMON_R_P512TO1023 },
+       { "rx_1024to2047byte", RMON_R_P1024TO2047 },
+       { "rx_GTE2048byte", RMON_R_P_GTE2048 },
+       { "rx_octets", RMON_R_OCTETS },
+
+       /* IEEE RX */
+       { "IEEE_rx_drop", IEEE_R_DROP },
+       { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK },
+       { "IEEE_rx_crc", IEEE_R_CRC },
+       { "IEEE_rx_align", IEEE_R_ALIGN },
+       { "IEEE_rx_macerr", IEEE_R_MACERR },
+       { "IEEE_rx_fdxfc", IEEE_R_FDXFC },
+       { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK },
+};
+
+static void fec_enet_get_ethtool_stats(struct net_device *dev,
+       struct ethtool_stats *stats, u64 *data)
+{
+       struct fec_enet_private *fep = netdev_priv(dev);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(fec_stats); i++)
+               data[i] = readl(fep->hwp + fec_stats[i].offset);
+}
+
+static void fec_enet_get_strings(struct net_device *netdev,
+       u32 stringset, u8 *data)
+{
+       int i;
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < ARRAY_SIZE(fec_stats); i++)
+                       memcpy(data + i * ETH_GSTRING_LEN,
+                               fec_stats[i].name, ETH_GSTRING_LEN);
+               break;
+       }
+}
+
+static int fec_enet_get_sset_count(struct net_device *dev, int sset)
+{
+       switch (sset) {
+       case ETH_SS_STATS:
+               return ARRAY_SIZE(fec_stats);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
 #endif /* !defined(CONFIG_M5272) */
 
+static int fec_enet_nway_reset(struct net_device *dev)
+{
+       struct fec_enet_private *fep = netdev_priv(dev);
+       struct phy_device *phydev = fep->phy_dev;
+
+       if (!phydev)
+               return -ENODEV;
+
+       return genphy_restart_aneg(phydev);
+}
+
 static const struct ethtool_ops fec_enet_ethtool_ops = {
 #if !defined(CONFIG_M5272)
        .get_pauseparam         = fec_enet_get_pauseparam,
@@ -1456,6 +1607,12 @@ static const struct ethtool_ops fec_enet_ethtool_ops = {
        .get_drvinfo            = fec_enet_get_drvinfo,
        .get_link               = ethtool_op_get_link,
        .get_ts_info            = fec_enet_get_ts_info,
+       .nway_reset             = fec_enet_nway_reset,
+#ifndef CONFIG_M5272
+       .get_ethtool_stats      = fec_enet_get_ethtool_stats,
+       .get_strings            = fec_enet_get_strings,
+       .get_sset_count         = fec_enet_get_sset_count,
+#endif
 };
 
 static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
@@ -1803,6 +1960,12 @@ static int fec_enet_init(struct net_device *ndev)
        writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
        netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT);
 
+       if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) {
+               /* enable hw VLAN support */
+               ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
+               ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
+       }
+
        if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) {
                /* enable hw accelerator */
                ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
@@ -1865,8 +2028,6 @@ fec_probe(struct platform_device *pdev)
        struct resource *r;
        const struct of_device_id *of_id;
        static int dev_id;
-       struct pinctrl *pinctrl;
-       struct regulator *reg_phy;
 
        of_id = of_match_device(fec_dt_ids, &pdev->dev);
        if (of_id)
@@ -1893,17 +2054,17 @@ fec_probe(struct platform_device *pdev)
                fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
 #endif
 
-       fep->hwp = devm_request_and_ioremap(&pdev->dev, r);
+       fep->hwp = devm_ioremap_resource(&pdev->dev, r);
+       if (IS_ERR(fep->hwp)) {
+               ret = PTR_ERR(fep->hwp);
+               goto failed_ioremap;
+       }
+
        fep->pdev = pdev;
        fep->dev_id = dev_id++;
 
        fep->bufdesc_ex = 0;
 
-       if (!fep->hwp) {
-               ret = -ENOMEM;
-               goto failed_ioremap;
-       }
-
        platform_set_drvdata(pdev, ndev);
 
        ret = of_get_phy_mode(pdev->dev.of_node);
@@ -1917,12 +2078,6 @@ fec_probe(struct platform_device *pdev)
                fep->phy_interface = ret;
        }
 
-       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
-       if (IS_ERR(pinctrl)) {
-               ret = PTR_ERR(pinctrl);
-               goto failed_pin;
-       }
-
        fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
        if (IS_ERR(fep->clk_ipg)) {
                ret = PTR_ERR(fep->clk_ipg);
@@ -1953,20 +2108,22 @@ fec_probe(struct platform_device *pdev)
        clk_prepare_enable(fep->clk_enet_out);
        clk_prepare_enable(fep->clk_ptp);
 
-       reg_phy = devm_regulator_get(&pdev->dev, "phy");
-       if (!IS_ERR(reg_phy)) {
-               ret = regulator_enable(reg_phy);
+       fep->reg_phy = devm_regulator_get(&pdev->dev, "phy");
+       if (!IS_ERR(fep->reg_phy)) {
+               ret = regulator_enable(fep->reg_phy);
                if (ret) {
                        dev_err(&pdev->dev,
                                "Failed to enable phy regulator: %d\n", ret);
                        goto failed_regulator;
                }
+       } else {
+               fep->reg_phy = NULL;
        }
 
        fec_reset_phy(pdev);
 
        if (fep->bufdesc_ex)
-               fec_ptp_init(ndev, pdev);
+               fec_ptp_init(pdev);
 
        ret = fec_enet_init(ndev);
        if (ret)
@@ -2010,19 +2167,20 @@ fec_probe(struct platform_device *pdev)
 failed_register:
        fec_enet_mii_remove(fep);
 failed_mii_init:
-failed_init:
+failed_irq:
        for (i = 0; i < FEC_IRQ_NUM; i++) {
                irq = platform_get_irq(pdev, i);
                if (irq > 0)
                        free_irq(irq, ndev);
        }
-failed_irq:
+failed_init:
+       if (fep->reg_phy)
+               regulator_disable(fep->reg_phy);
 failed_regulator:
        clk_disable_unprepare(fep->clk_ahb);
        clk_disable_unprepare(fep->clk_ipg);
        clk_disable_unprepare(fep->clk_enet_out);
        clk_disable_unprepare(fep->clk_ptp);
-failed_pin:
 failed_clk:
 failed_ioremap:
        free_netdev(ndev);
@@ -2041,21 +2199,21 @@ fec_drv_remove(struct platform_device *pdev)
        unregister_netdev(ndev);
        fec_enet_mii_remove(fep);
        del_timer_sync(&fep->time_keep);
+       for (i = 0; i < FEC_IRQ_NUM; i++) {
+               int irq = platform_get_irq(pdev, i);
+               if (irq > 0)
+                       free_irq(irq, ndev);
+       }
+       if (fep->reg_phy)
+               regulator_disable(fep->reg_phy);
        clk_disable_unprepare(fep->clk_ptp);
        if (fep->ptp_clock)
                ptp_clock_unregister(fep->ptp_clock);
        clk_disable_unprepare(fep->clk_enet_out);
        clk_disable_unprepare(fep->clk_ahb);
        clk_disable_unprepare(fep->clk_ipg);
-       for (i = 0; i < FEC_IRQ_NUM; i++) {
-               int irq = platform_get_irq(pdev, i);
-               if (irq > 0)
-                       free_irq(irq, ndev);
-       }
        free_netdev(ndev);
 
-       platform_set_drvdata(pdev, NULL);
-
        return 0;
 }
 
@@ -2074,6 +2232,9 @@ fec_suspend(struct device *dev)
        clk_disable_unprepare(fep->clk_ahb);
        clk_disable_unprepare(fep->clk_ipg);
 
+       if (fep->reg_phy)
+               regulator_disable(fep->reg_phy);
+
        return 0;
 }
 
@@ -2082,6 +2243,13 @@ fec_resume(struct device *dev)
 {
        struct net_device *ndev = dev_get_drvdata(dev);
        struct fec_enet_private *fep = netdev_priv(ndev);
+       int ret;
+
+       if (fep->reg_phy) {
+               ret = regulator_enable(fep->reg_phy);
+               if (ret)
+                       return ret;
+       }
 
        clk_prepare_enable(fep->clk_enet_out);
        clk_prepare_enable(fep->clk_ahb);
index 9bc15e2..9947765 100644 (file)
@@ -981,7 +981,7 @@ static int mpc52xx_fec_probe(struct platform_device *op)
                goto err_node;
 
        /* We're done ! */
-       dev_set_drvdata(&op->dev, ndev);
+       platform_set_drvdata(op, ndev);
        netdev_info(ndev, "%s MAC %pM\n",
                    op->dev.of_node->full_name, ndev->dev_addr);
 
@@ -1010,7 +1010,7 @@ mpc52xx_fec_remove(struct platform_device *op)
        struct net_device *ndev;
        struct mpc52xx_fec_priv *priv;
 
-       ndev = dev_get_drvdata(&op->dev);
+       ndev = platform_get_drvdata(op);
        priv = netdev_priv(ndev);
 
        unregister_netdev(ndev);
@@ -1030,14 +1030,13 @@ mpc52xx_fec_remove(struct platform_device *op)
 
        free_netdev(ndev);
 
-       dev_set_drvdata(&op->dev, NULL);
        return 0;
 }
 
 #ifdef CONFIG_PM
 static int mpc52xx_fec_of_suspend(struct platform_device *op, pm_message_t state)
 {
-       struct net_device *dev = dev_get_drvdata(&op->dev);
+       struct net_device *dev = platform_get_drvdata(op);
 
        if (netif_running(dev))
                mpc52xx_fec_close(dev);
@@ -1047,7 +1046,7 @@ static int mpc52xx_fec_of_suspend(struct platform_device *op, pm_message_t state
 
 static int mpc52xx_fec_of_resume(struct platform_device *op)
 {
-       struct net_device *dev = dev_get_drvdata(&op->dev);
+       struct net_device *dev = platform_get_drvdata(op);
 
        mpc52xx_fec_hw_init(dev);
        mpc52xx_fec_reset_stats(dev);
index 25fc960..5007e4f 100644 (file)
@@ -347,8 +347,9 @@ static void fec_time_keep(unsigned long _data)
  * cyclecounter init routine and exits.
  */
 
-void fec_ptp_init(struct net_device *ndev, struct platform_device *pdev)
+void fec_ptp_init(struct platform_device *pdev)
 {
+       struct net_device *ndev = platform_get_drvdata(pdev);
        struct fec_enet_private *fep = netdev_priv(ndev);
 
        fep->ptp_caps.owner = THIS_MODULE;
index 268414d..be92229 100644 (file)
@@ -1,7 +1,6 @@
 config FS_ENET
        tristate "Freescale Ethernet Driver"
        depends on NET_VENDOR_FREESCALE && (CPM1 || CPM2 || PPC_MPC512x)
-       select NET_CORE
        select MII
        select PHYLIB
 
index edc1200..8de53a1 100644 (file)
@@ -1048,7 +1048,7 @@ static int fs_enet_probe(struct platform_device *ofdev)
        }
 
        SET_NETDEV_DEV(ndev, &ofdev->dev);
-       dev_set_drvdata(&ofdev->dev, ndev);
+       platform_set_drvdata(ofdev, ndev);
 
        fep = netdev_priv(ndev);
        fep->dev = &ofdev->dev;
@@ -1106,7 +1106,6 @@ out_cleanup_data:
        fep->ops->cleanup_data(ndev);
 out_free_dev:
        free_netdev(ndev);
-       dev_set_drvdata(&ofdev->dev, NULL);
 out_put:
        of_node_put(fpi->phy_node);
 out_free_fpi:
@@ -1116,7 +1115,7 @@ out_free_fpi:
 
 static int fs_enet_remove(struct platform_device *ofdev)
 {
-       struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
+       struct net_device *ndev = platform_get_drvdata(ofdev);
        struct fs_enet_private *fep = netdev_priv(ndev);
 
        unregister_netdev(ndev);
index 2bafbd3..844ecfa 100644 (file)
@@ -179,7 +179,7 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev)
        }
 
        new_bus->parent = &ofdev->dev;
-       dev_set_drvdata(&ofdev->dev, new_bus);
+       platform_set_drvdata(ofdev, new_bus);
 
        ret = of_mdiobus_register(new_bus, ofdev->dev.of_node);
        if (ret)
@@ -188,7 +188,6 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev)
        return 0;
 
 out_free_irqs:
-       dev_set_drvdata(&ofdev->dev, NULL);
        kfree(new_bus->irq);
 out_unmap_regs:
        iounmap(bitbang->dir);
@@ -202,11 +201,10 @@ out:
 
 static int fs_enet_mdio_remove(struct platform_device *ofdev)
 {
-       struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
+       struct mii_bus *bus = platform_get_drvdata(ofdev);
        struct bb_info *bitbang = bus->priv;
 
        mdiobus_unregister(bus);
-       dev_set_drvdata(&ofdev->dev, NULL);
        kfree(bus->irq);
        free_mdio_bitbang(bus);
        iounmap(bitbang->dir);
index 18e8ef2..2f1c46a 100644 (file)
@@ -180,7 +180,7 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev)
        }
 
        new_bus->parent = &ofdev->dev;
-       dev_set_drvdata(&ofdev->dev, new_bus);
+       platform_set_drvdata(ofdev, new_bus);
 
        ret = of_mdiobus_register(new_bus, ofdev->dev.of_node);
        if (ret)
@@ -189,7 +189,6 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev)
        return 0;
 
 out_free_irqs:
-       dev_set_drvdata(&ofdev->dev, NULL);
        kfree(new_bus->irq);
 out_unmap_regs:
        iounmap(fec->fecp);
@@ -204,11 +203,10 @@ out:
 
 static int fs_enet_mdio_remove(struct platform_device *ofdev)
 {
-       struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
+       struct mii_bus *bus = platform_get_drvdata(ofdev);
        struct fec_info *fec = bus->priv;
 
        mdiobus_unregister(bus);
-       dev_set_drvdata(&ofdev->dev, NULL);
        kfree(bus->irq);
        iounmap(fec->fecp);
        kfree(fec);
index 2375a01..8d2db7b 100644 (file)
@@ -128,6 +128,7 @@ static void gfar_set_multi(struct net_device *dev);
 static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
 static void gfar_configure_serdes(struct net_device *dev);
 static int gfar_poll(struct napi_struct *napi, int budget);
+static int gfar_poll_sq(struct napi_struct *napi, int budget);
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void gfar_netpoll(struct net_device *dev);
 #endif
@@ -1000,7 +1001,7 @@ static int gfar_probe(struct platform_device *ofdev)
        spin_lock_init(&priv->bflock);
        INIT_WORK(&priv->reset_task, gfar_reset_task);
 
-       dev_set_drvdata(&ofdev->dev, priv);
+       platform_set_drvdata(ofdev, priv);
        regs = priv->gfargrp[0].regs;
 
        gfar_detect_errata(priv);
@@ -1038,9 +1039,13 @@ static int gfar_probe(struct platform_device *ofdev)
        dev->ethtool_ops = &gfar_ethtool_ops;
 
        /* Register for napi ...We are registering NAPI for each grp */
-       for (i = 0; i < priv->num_grps; i++)
-               netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll,
+       if (priv->mode == SQ_SG_MODE)
+               netif_napi_add(dev, &priv->gfargrp[0].napi, gfar_poll_sq,
                               GFAR_DEV_WEIGHT);
+       else
+               for (i = 0; i < priv->num_grps; i++)
+                       netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll,
+                                      GFAR_DEV_WEIGHT);
 
        if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
                dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
@@ -1240,15 +1245,13 @@ register_fail:
 
 static int gfar_remove(struct platform_device *ofdev)
 {
-       struct gfar_private *priv = dev_get_drvdata(&ofdev->dev);
+       struct gfar_private *priv = platform_get_drvdata(ofdev);
 
        if (priv->phy_node)
                of_node_put(priv->phy_node);
        if (priv->tbi_node)
                of_node_put(priv->tbi_node);
 
-       dev_set_drvdata(&ofdev->dev, NULL);
-
        unregister_netdev(priv->ndev);
        unmap_group_regs(priv);
        free_gfar_dev(priv);
@@ -2825,6 +2828,48 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
        return howmany;
 }
 
+static int gfar_poll_sq(struct napi_struct *napi, int budget)
+{
+       struct gfar_priv_grp *gfargrp =
+               container_of(napi, struct gfar_priv_grp, napi);
+       struct gfar __iomem *regs = gfargrp->regs;
+       struct gfar_priv_tx_q *tx_queue = gfargrp->priv->tx_queue[0];
+       struct gfar_priv_rx_q *rx_queue = gfargrp->priv->rx_queue[0];
+       int work_done = 0;
+
+       /* Clear IEVENT, so interrupts aren't called again
+        * because of the packets that have already arrived
+        */
+       gfar_write(&regs->ievent, IEVENT_RTX_MASK);
+
+       /* run Tx cleanup to completion */
+       if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx])
+               gfar_clean_tx_ring(tx_queue);
+
+       work_done = gfar_clean_rx_ring(rx_queue, budget);
+
+       if (work_done < budget) {
+               napi_complete(napi);
+               /* Clear the halt bit in RSTAT */
+               gfar_write(&regs->rstat, gfargrp->rstat);
+
+               gfar_write(&regs->imask, IMASK_DEFAULT);
+
+               /* If we are coalescing interrupts, update the timer
+                * Otherwise, clear it
+                */
+               gfar_write(&regs->txic, 0);
+               if (likely(tx_queue->txcoalescing))
+                       gfar_write(&regs->txic, tx_queue->txic);
+
+               gfar_write(&regs->rxic, 0);
+               if (unlikely(rx_queue->rxcoalescing))
+                       gfar_write(&regs->rxic, rx_queue->rxic);
+       }
+
+       return work_done;
+}
+
 static int gfar_poll(struct napi_struct *napi, int budget)
 {
        struct gfar_priv_grp *gfargrp =
index 083ea2b..098f133 100644 (file)
@@ -519,7 +519,7 @@ static int gianfar_ptp_probe(struct platform_device *dev)
        }
        gfar_phc_index = ptp_clock_index(etsects->clock);
 
-       dev_set_drvdata(&dev->dev, etsects);
+       platform_set_drvdata(dev, etsects);
 
        return 0;
 
@@ -537,7 +537,7 @@ no_memory:
 
 static int gianfar_ptp_remove(struct platform_device *dev)
 {
-       struct etsects *etsects = dev_get_drvdata(&dev->dev);
+       struct etsects *etsects = platform_get_drvdata(dev);
 
        gfar_write(&etsects->regs->tmr_temask, 0);
        gfar_write(&etsects->regs->tmr_ctrl,   0);
index e04c598..3c43dac 100644 (file)
@@ -3564,7 +3564,7 @@ static void ucc_geth_timeout(struct net_device *dev)
 
 static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state)
 {
-       struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
+       struct net_device *ndev = platform_get_drvdata(ofdev);
        struct ucc_geth_private *ugeth = netdev_priv(ndev);
 
        if (!netif_running(ndev))
@@ -3592,7 +3592,7 @@ static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state)
 
 static int ucc_geth_resume(struct platform_device *ofdev)
 {
-       struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
+       struct net_device *ndev = platform_get_drvdata(ofdev);
        struct ucc_geth_private *ugeth = netdev_priv(ndev);
        int err;
 
index 418068b..c1b6e7e 100644 (file)
@@ -227,7 +227,7 @@ static int xgmac_mdio_probe(struct platform_device *pdev)
                goto err_registration;
        }
 
-       dev_set_drvdata(&pdev->dev, bus);
+       platform_set_drvdata(pdev, bus);
 
        return 0;
 
@@ -242,7 +242,7 @@ err_ioremap:
 
 static int xgmac_mdio_remove(struct platform_device *pdev)
 {
-       struct mii_bus *bus = dev_get_drvdata(&pdev->dev);
+       struct mii_bus *bus = platform_get_drvdata(pdev);
 
        mdiobus_unregister(bus);
        iounmap(bus->priv);
index 6529d31..563a1ac 100644 (file)
@@ -5,8 +5,7 @@
 config NET_VENDOR_IBM
        bool "IBM devices"
        default y
-       depends on MCA || PPC_PSERIES || PPC_PSERIES || PPC_DCR || \
-                  (IBMEBUS && SPARSEMEM)
+       depends on PPC_PSERIES || PPC_DCR || (IBMEBUS && SPARSEMEM)
        ---help---
          If you have a network (Ethernet) card belonging to this class, say Y
          and read the Ethernet-HOWTO, available from
index de2969c..35853b4 100644 (file)
@@ -3287,7 +3287,7 @@ static int ehea_probe_adapter(struct platform_device *dev)
 
        adapter->pd = EHEA_PD_ID;
 
-       dev_set_drvdata(&dev->dev, adapter);
+       platform_set_drvdata(dev, adapter);
 
 
        /* initialize adapter and ports */
@@ -3358,7 +3358,7 @@ out:
 
 static int ehea_remove(struct platform_device *dev)
 {
-       struct ehea_adapter *adapter = dev_get_drvdata(&dev->dev);
+       struct ehea_adapter *adapter = platform_get_drvdata(dev);
        int i;
 
        for (i = 0; i < EHEA_MAX_PORTS; i++)
index 610ed22..856ea66 100644 (file)
@@ -696,7 +696,7 @@ static int mal_probe(struct platform_device *ofdev)
 
        /* Advertise this instance to the rest of the world */
        wmb();
-       dev_set_drvdata(&ofdev->dev, mal);
+       platform_set_drvdata(ofdev, mal);
 
        mal_dbg_register(mal);
 
@@ -722,7 +722,7 @@ static int mal_probe(struct platform_device *ofdev)
 
 static int mal_remove(struct platform_device *ofdev)
 {
-       struct mal_instance *mal = dev_get_drvdata(&ofdev->dev);
+       struct mal_instance *mal = platform_get_drvdata(ofdev);
 
        MAL_DBG(mal, "remove" NL);
 
@@ -735,8 +735,6 @@ static int mal_remove(struct platform_device *ofdev)
                       "mal%d: commac list is not empty on remove!\n",
                       mal->index);
 
-       dev_set_drvdata(&ofdev->dev, NULL);
-
        free_irq(mal->serr_irq, mal);
        free_irq(mal->txde_irq, mal);
        free_irq(mal->txeob_irq, mal);
index 3925176..c47e23d 100644 (file)
@@ -95,7 +95,7 @@ static inline u32 rgmii_mode_mask(int mode, int input)
 
 int rgmii_attach(struct platform_device *ofdev, int input, int mode)
 {
-       struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct rgmii_instance *dev = platform_get_drvdata(ofdev);
        struct rgmii_regs __iomem *p = dev->base;
 
        RGMII_DBG(dev, "attach(%d)" NL, input);
@@ -124,7 +124,7 @@ int rgmii_attach(struct platform_device *ofdev, int input, int mode)
 
 void rgmii_set_speed(struct platform_device *ofdev, int input, int speed)
 {
-       struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct rgmii_instance *dev = platform_get_drvdata(ofdev);
        struct rgmii_regs __iomem *p = dev->base;
        u32 ssr;
 
@@ -146,7 +146,7 @@ void rgmii_set_speed(struct platform_device *ofdev, int input, int speed)
 
 void rgmii_get_mdio(struct platform_device *ofdev, int input)
 {
-       struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct rgmii_instance *dev = platform_get_drvdata(ofdev);
        struct rgmii_regs __iomem *p = dev->base;
        u32 fer;
 
@@ -167,7 +167,7 @@ void rgmii_get_mdio(struct platform_device *ofdev, int input)
 
 void rgmii_put_mdio(struct platform_device *ofdev, int input)
 {
-       struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct rgmii_instance *dev = platform_get_drvdata(ofdev);
        struct rgmii_regs __iomem *p = dev->base;
        u32 fer;
 
@@ -188,7 +188,7 @@ void rgmii_put_mdio(struct platform_device *ofdev, int input)
 
 void rgmii_detach(struct platform_device *ofdev, int input)
 {
-       struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct rgmii_instance *dev = platform_get_drvdata(ofdev);
        struct rgmii_regs __iomem *p;
 
        BUG_ON(!dev || dev->users == 0);
@@ -214,7 +214,7 @@ int rgmii_get_regs_len(struct platform_device *ofdev)
 
 void *rgmii_dump_regs(struct platform_device *ofdev, void *buf)
 {
-       struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct rgmii_instance *dev = platform_get_drvdata(ofdev);
        struct emac_ethtool_regs_subhdr *hdr = buf;
        struct rgmii_regs *regs = (struct rgmii_regs *)(hdr + 1);
 
@@ -279,7 +279,7 @@ static int rgmii_probe(struct platform_device *ofdev)
               (dev->flags & EMAC_RGMII_FLAG_HAS_MDIO) ? "" : "out");
 
        wmb();
-       dev_set_drvdata(&ofdev->dev, dev);
+       platform_set_drvdata(ofdev, dev);
 
        return 0;
 
@@ -291,9 +291,7 @@ static int rgmii_probe(struct platform_device *ofdev)
 
 static int rgmii_remove(struct platform_device *ofdev)
 {
-       struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
-
-       dev_set_drvdata(&ofdev->dev, NULL);
+       struct rgmii_instance *dev = platform_get_drvdata(ofdev);
 
        WARN_ON(dev->users != 0);
 
index 795f139..c231a4a 100644 (file)
@@ -25,7 +25,7 @@
 
 int tah_attach(struct platform_device *ofdev, int channel)
 {
-       struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct tah_instance *dev = platform_get_drvdata(ofdev);
 
        mutex_lock(&dev->lock);
        /* Reset has been done at probe() time... nothing else to do for now */
@@ -37,7 +37,7 @@ int tah_attach(struct platform_device *ofdev, int channel)
 
 void tah_detach(struct platform_device *ofdev, int channel)
 {
-       struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct tah_instance *dev = platform_get_drvdata(ofdev);
 
        mutex_lock(&dev->lock);
        --dev->users;
@@ -46,7 +46,7 @@ void tah_detach(struct platform_device *ofdev, int channel)
 
 void tah_reset(struct platform_device *ofdev)
 {
-       struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct tah_instance *dev = platform_get_drvdata(ofdev);
        struct tah_regs __iomem *p = dev->base;
        int n;
 
@@ -74,7 +74,7 @@ int tah_get_regs_len(struct platform_device *ofdev)
 
 void *tah_dump_regs(struct platform_device *ofdev, void *buf)
 {
-       struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct tah_instance *dev = platform_get_drvdata(ofdev);
        struct emac_ethtool_regs_subhdr *hdr = buf;
        struct tah_regs *regs = (struct tah_regs *)(hdr + 1);
 
@@ -118,7 +118,7 @@ static int tah_probe(struct platform_device *ofdev)
                goto err_free;
        }
 
-       dev_set_drvdata(&ofdev->dev, dev);
+       platform_set_drvdata(ofdev, dev);
 
        /* Initialize TAH and enable IPv4 checksum verification, no TSO yet */
        tah_reset(ofdev);
@@ -137,9 +137,7 @@ static int tah_probe(struct platform_device *ofdev)
 
 static int tah_remove(struct platform_device *ofdev)
 {
-       struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
-
-       dev_set_drvdata(&ofdev->dev, NULL);
+       struct tah_instance *dev = platform_get_drvdata(ofdev);
 
        WARN_ON(dev->users != 0);
 
index f91202f..4cdf286 100644 (file)
@@ -84,7 +84,7 @@ static inline u32 zmii_mode_mask(int mode, int input)
 
 int zmii_attach(struct platform_device *ofdev, int input, int *mode)
 {
-       struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct zmii_instance *dev = platform_get_drvdata(ofdev);
        struct zmii_regs __iomem *p = dev->base;
 
        ZMII_DBG(dev, "init(%d, %d)" NL, input, *mode);
@@ -150,7 +150,7 @@ int zmii_attach(struct platform_device *ofdev, int input, int *mode)
 
 void zmii_get_mdio(struct platform_device *ofdev, int input)
 {
-       struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct zmii_instance *dev = platform_get_drvdata(ofdev);
        u32 fer;
 
        ZMII_DBG2(dev, "get_mdio(%d)" NL, input);
@@ -163,7 +163,7 @@ void zmii_get_mdio(struct platform_device *ofdev, int input)
 
 void zmii_put_mdio(struct platform_device *ofdev, int input)
 {
-       struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct zmii_instance *dev = platform_get_drvdata(ofdev);
 
        ZMII_DBG2(dev, "put_mdio(%d)" NL, input);
        mutex_unlock(&dev->lock);
@@ -172,7 +172,7 @@ void zmii_put_mdio(struct platform_device *ofdev, int input)
 
 void zmii_set_speed(struct platform_device *ofdev, int input, int speed)
 {
-       struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct zmii_instance *dev = platform_get_drvdata(ofdev);
        u32 ssr;
 
        mutex_lock(&dev->lock);
@@ -193,7 +193,7 @@ void zmii_set_speed(struct platform_device *ofdev, int input, int speed)
 
 void zmii_detach(struct platform_device *ofdev, int input)
 {
-       struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct zmii_instance *dev = platform_get_drvdata(ofdev);
 
        BUG_ON(!dev || dev->users == 0);
 
@@ -218,7 +218,7 @@ int zmii_get_regs_len(struct platform_device *ofdev)
 
 void *zmii_dump_regs(struct platform_device *ofdev, void *buf)
 {
-       struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
+       struct zmii_instance *dev = platform_get_drvdata(ofdev);
        struct emac_ethtool_regs_subhdr *hdr = buf;
        struct zmii_regs *regs = (struct zmii_regs *)(hdr + 1);
 
@@ -272,7 +272,7 @@ static int zmii_probe(struct platform_device *ofdev)
        printk(KERN_INFO
               "ZMII %s initialized\n", ofdev->dev.of_node->full_name);
        wmb();
-       dev_set_drvdata(&ofdev->dev, dev);
+       platform_set_drvdata(ofdev, dev);
 
        return 0;
 
@@ -284,9 +284,7 @@ static int zmii_probe(struct platform_device *ofdev)
 
 static int zmii_remove(struct platform_device *ofdev)
 {
-       struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
-
-       dev_set_drvdata(&ofdev->dev, NULL);
+       struct zmii_instance *dev = platform_get_drvdata(ofdev);
 
        WARN_ON(dev->users != 0);
 
index 5119ef1..14a66e9 100644 (file)
@@ -5,7 +5,6 @@
 config IP1000
        tristate "IP1000 Gigabit Ethernet support"
        depends on PCI
-       select NET_CORE
        select MII
        ---help---
          This driver supports IP1000 gigabit Ethernet cards.
index 068d781..1fde90b 100644 (file)
@@ -2298,15 +2298,4 @@ static struct pci_driver ipg_pci_driver = {
        .remove         = ipg_remove,
 };
 
-static int __init ipg_init_module(void)
-{
-       return pci_register_driver(&ipg_pci_driver);
-}
-
-static void __exit ipg_exit_module(void)
-{
-       pci_unregister_driver(&ipg_pci_driver);
-}
-
-module_init(ipg_init_module);
-module_exit(ipg_exit_module);
+module_pci_driver(ipg_pci_driver);
index 05f7264..f0e7ed2 100644 (file)
@@ -20,7 +20,6 @@ if NET_VENDOR_INTEL
 config E100
        tristate "Intel(R) PRO/100+ support"
        depends on PCI
-       select NET_CORE
        select MII
        ---help---
          This driver supports Intel(R) PRO/100 family of adapters.
index d2bea3f..5115ae7 100644 (file)
@@ -3069,7 +3069,7 @@ static int e100_resume(struct pci_dev *pdev)
        pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
        /* ack any pending wake events, disable PME */
-       pci_enable_wake(pdev, 0, 0);
+       pci_enable_wake(pdev, PCI_D0, 0);
 
        /* disable reverse auto-negotiation */
        if (nic->phy == phy_82552_v) {
@@ -3160,7 +3160,7 @@ static void e100_io_resume(struct pci_dev *pdev)
        struct nic *nic = netdev_priv(netdev);
 
        /* ack any pending wake events, disable PME */
-       pci_enable_wake(pdev, 0, 0);
+       pci_enable_wake(pdev, PCI_D0, 0);
 
        netif_device_attach(netdev);
        if (netif_running(netdev)) {
index b71c850..895450e 100644 (file)
@@ -66,17 +66,17 @@ static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw)
        s32 ret_val;
 
        if (hw->phy.media_type != e1000_media_type_copper) {
-               phy->type       = e1000_phy_none;
+               phy->type = e1000_phy_none;
                return 0;
        } else {
                phy->ops.power_up = e1000_power_up_phy_copper;
                phy->ops.power_down = e1000_power_down_phy_copper_80003es2lan;
        }
 
-       phy->addr               = 1;
-       phy->autoneg_mask       = AUTONEG_ADVERTISE_SPEED_DEFAULT;
-       phy->reset_delay_us      = 100;
-       phy->type               = e1000_phy_gg82563;
+       phy->addr = 1;
+       phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+       phy->reset_delay_us = 100;
+       phy->type = e1000_phy_gg82563;
 
        /* This can only be done after all function pointers are setup. */
        ret_val = e1000e_get_phy_id(hw);
@@ -98,19 +98,19 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw)
        u32 eecd = er32(EECD);
        u16 size;
 
-       nvm->opcode_bits        = 8;
-       nvm->delay_usec  = 1;
+       nvm->opcode_bits = 8;
+       nvm->delay_usec = 1;
        switch (nvm->override) {
        case e1000_nvm_override_spi_large:
-               nvm->page_size    = 32;
+               nvm->page_size = 32;
                nvm->address_bits = 16;
                break;
        case e1000_nvm_override_spi_small:
-               nvm->page_size    = 8;
+               nvm->page_size = 8;
                nvm->address_bits = 8;
                break;
        default:
-               nvm->page_size    = eecd & E1000_EECD_ADDR_BITS ? 32 : 8;
+               nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8;
                nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? 16 : 8;
                break;
        }
@@ -128,7 +128,7 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw)
        /* EEPROM access above 16k is unsupported */
        if (size > 14)
                size = 14;
-       nvm->word_size  = 1 << size;
+       nvm->word_size = 1 << size;
 
        return 0;
 }
@@ -859,7 +859,7 @@ static void e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw)
 
        /* Transmit Arbitration Control 0 */
        reg = er32(TARC(0));
-       reg &= ~(0xF << 27); /* 30:27 */
+       reg &= ~(0xF << 27);    /* 30:27 */
        if (hw->phy.media_type != e1000_media_type_copper)
                reg &= ~(1 << 20);
        ew32(TARC(0), reg);
index 7380442..4c303e2 100644 (file)
@@ -77,24 +77,24 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
                return 0;
        }
 
-       phy->addr                        = 1;
-       phy->autoneg_mask                = AUTONEG_ADVERTISE_SPEED_DEFAULT;
-       phy->reset_delay_us              = 100;
+       phy->addr = 1;
+       phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+       phy->reset_delay_us = 100;
 
-       phy->ops.power_up                = e1000_power_up_phy_copper;
-       phy->ops.power_down              = e1000_power_down_phy_copper_82571;
+       phy->ops.power_up = e1000_power_up_phy_copper;
+       phy->ops.power_down = e1000_power_down_phy_copper_82571;
 
        switch (hw->mac.type) {
        case e1000_82571:
        case e1000_82572:
-               phy->type                = e1000_phy_igp_2;
+               phy->type = e1000_phy_igp_2;
                break;
        case e1000_82573:
-               phy->type                = e1000_phy_m88;
+               phy->type = e1000_phy_m88;
                break;
        case e1000_82574:
        case e1000_82583:
-               phy->type                = e1000_phy_bm;
+               phy->type = e1000_phy_bm;
                phy->ops.acquire = e1000_get_hw_semaphore_82574;
                phy->ops.release = e1000_put_hw_semaphore_82574;
                phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82574;
@@ -193,7 +193,7 @@ static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw)
                /* EEPROM access above 16k is unsupported */
                if (size > 14)
                        size = 14;
-               nvm->word_size  = 1 << size;
+               nvm->word_size = 1 << size;
                break;
        }
 
@@ -339,7 +339,7 @@ static s32 e1000_init_mac_params_82571(struct e1000_hw *hw)
 static s32 e1000_get_variants_82571(struct e1000_adapter *adapter)
 {
        struct e1000_hw *hw = &adapter->hw;
-       static int global_quad_port_a; /* global port a indication */
+       static int global_quad_port_a;  /* global port a indication */
        struct pci_dev *pdev = adapter->pdev;
        int is_port_b = er32(STATUS) & E1000_STATUS_FUNC_1;
        s32 rc;
@@ -1003,8 +1003,6 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
        default:
                break;
        }
-       if (ret_val)
-               e_dbg("Cannot acquire MDIO ownership\n");
 
        ctrl = er32(CTRL);
 
@@ -1015,7 +1013,9 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
        switch (hw->mac.type) {
        case e1000_82574:
        case e1000_82583:
-               e1000_put_hw_semaphore_82574(hw);
+               /* Release mutex only if the hw semaphore is acquired */
+               if (!ret_val)
+                       e1000_put_hw_semaphore_82574(hw);
                break;
        default:
                break;
@@ -1178,7 +1178,7 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw)
 
        /* Transmit Arbitration Control 0 */
        reg = er32(TARC(0));
-       reg &= ~(0xF << 27); /* 30:27 */
+       reg &= ~(0xF << 27);    /* 30:27 */
        switch (hw->mac.type) {
        case e1000_82571:
        case e1000_82572:
@@ -1390,7 +1390,7 @@ bool e1000_check_phy_82574(struct e1000_hw *hw)
        ret_val = e1e_rphy(hw, E1000_RECEIVE_ERROR_COUNTER, &receive_errors);
        if (ret_val)
                return false;
-       if (receive_errors == E1000_RECEIVE_ERROR_MAX)  {
+       if (receive_errors == E1000_RECEIVE_ERROR_MAX) {
                ret_val = e1e_rphy(hw, E1000_BASE1000T_STATUS, &status_1kbt);
                if (ret_val)
                        return false;
index 7c8ca65..59c22bf 100644 (file)
@@ -244,7 +244,7 @@ static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx)
                mac->autoneg = 1;
                adapter->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL;
                break;
-       case SPEED_1000 + DUPLEX_HALF: /* not supported */
+       case SPEED_1000 + DUPLEX_HALF:  /* not supported */
        default:
                goto err_inval;
        }
@@ -416,7 +416,7 @@ static void e1000_set_msglevel(struct net_device *netdev, u32 data)
 
 static int e1000_get_regs_len(struct net_device __always_unused *netdev)
 {
-#define E1000_REGS_LEN 32 /* overestimate */
+#define E1000_REGS_LEN 32      /* overestimate */
        return E1000_REGS_LEN * sizeof(u32);
 }
 
@@ -433,22 +433,22 @@ static void e1000_get_regs(struct net_device *netdev,
        regs->version = (1 << 24) | (adapter->pdev->revision << 16) |
            adapter->pdev->device;
 
-       regs_buff[0]  = er32(CTRL);
-       regs_buff[1]  = er32(STATUS);
+       regs_buff[0] = er32(CTRL);
+       regs_buff[1] = er32(STATUS);
 
-       regs_buff[2]  = er32(RCTL);
-       regs_buff[3]  = er32(RDLEN(0));
-       regs_buff[4]  = er32(RDH(0));
-       regs_buff[5]  = er32(RDT(0));
-       regs_buff[6]  = er32(RDTR);
+       regs_buff[2] = er32(RCTL);
+       regs_buff[3] = er32(RDLEN(0));
+       regs_buff[4] = er32(RDH(0));
+       regs_buff[5] = er32(RDT(0));
+       regs_buff[6] = er32(RDTR);
 
-       regs_buff[7]  = er32(TCTL);
-       regs_buff[8]  = er32(TDLEN(0));
-       regs_buff[9]  = er32(TDH(0));
+       regs_buff[7] = er32(TCTL);
+       regs_buff[8] = er32(TDLEN(0));
+       regs_buff[9] = er32(TDH(0));
        regs_buff[10] = er32(TDT(0));
        regs_buff[11] = er32(TIDV);
 
-       regs_buff[12] = adapter->hw.phy.type;  /* PHY type (IGP=1, M88=0) */
+       regs_buff[12] = adapter->hw.phy.type;   /* PHY type (IGP=1, M88=0) */
 
        /* ethtool doesn't use anything past this point, so all this
         * code is likely legacy junk for apps that may or may not exist
@@ -1379,7 +1379,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter)
 
        if (hw->phy.media_type == e1000_media_type_copper &&
            hw->phy.type == e1000_phy_m88) {
-               ctrl_reg |= E1000_CTRL_ILOS; /* Invert Loss of Signal */
+               ctrl_reg |= E1000_CTRL_ILOS;    /* Invert Loss of Signal */
        } else {
                /* Set the ILOS bit on the fiber Nic if half duplex link is
                 * detected.
@@ -1613,7 +1613,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
                ew32(TDT(0), k);
                e1e_flush();
                msleep(200);
-               time = jiffies; /* set the start time for the receive */
+               time = jiffies; /* set the start time for the receive */
                good_cnt = 0;
                /* receive the sent packets */
                do {
@@ -1636,11 +1636,11 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
                         */
                } while ((good_cnt < 64) && !time_after(jiffies, time + 20));
                if (good_cnt != 64) {
-                       ret_val = 13; /* ret_val is the same as mis-compare */
+                       ret_val = 13;   /* ret_val is the same as mis-compare */
                        break;
                }
                if (jiffies >= (time + 20)) {
-                       ret_val = 14; /* error code for time out error */
+                       ret_val = 14;   /* error code for time out error */
                        break;
                }
        }
index 84850f7..a6f903a 100644 (file)
@@ -402,13 +402,13 @@ struct e1000_phy_stats {
 
 struct e1000_host_mng_dhcp_cookie {
        u32 signature;
-       u8  status;
-       u8  reserved0;
+       u8 status;
+       u8 reserved0;
        u16 vlan_id;
        u32 reserved1;
        u16 reserved2;
-       u8  reserved3;
-       u8  checksum;
+       u8 reserved3;
+       u8 checksum;
 };
 
 /* Host Interface "Rev 1" */
@@ -427,8 +427,8 @@ struct e1000_host_command_info {
 
 /* Host Interface "Rev 2" */
 struct e1000_host_mng_command_header {
-       u8  command_id;
-       u8  checksum;
+       u8 command_id;
+       u8 checksum;
        u16 reserved1;
        u16 reserved2;
        u16 command_length;
@@ -549,7 +549,7 @@ struct e1000_mac_info {
        u32 mta_shadow[MAX_MTA_REG];
        u16 rar_entry_count;
 
-       u8  forced_speed_duplex;
+       u8 forced_speed_duplex;
 
        bool adaptive_ifs;
        bool has_fwsm;
@@ -577,7 +577,7 @@ struct e1000_phy_info {
 
        u32 addr;
        u32 id;
-       u32 reset_delay_us; /* in usec */
+       u32 reset_delay_us;     /* in usec */
        u32 revision;
 
        enum e1000_media_type media_type;
@@ -636,11 +636,11 @@ struct e1000_dev_spec_82571 {
 };
 
 struct e1000_dev_spec_80003es2lan {
-       bool  mdic_wa_enable;
+       bool mdic_wa_enable;
 };
 
 struct e1000_shadow_ram {
-       u16  value;
+       u16 value;
        bool modified;
 };
 
@@ -660,17 +660,17 @@ struct e1000_hw {
        void __iomem *hw_addr;
        void __iomem *flash_address;
 
-       struct e1000_mac_info  mac;
-       struct e1000_fc_info   fc;
-       struct e1000_phy_info  phy;
-       struct e1000_nvm_info  nvm;
-       struct e1000_bus_info  bus;
+       struct e1000_mac_info mac;
+       struct e1000_fc_info fc;
+       struct e1000_phy_info phy;
+       struct e1000_nvm_info nvm;
+       struct e1000_bus_info bus;
        struct e1000_host_mng_dhcp_cookie mng_cookie;
 
        union {
-               struct e1000_dev_spec_82571     e82571;
+               struct e1000_dev_spec_82571 e82571;
                struct e1000_dev_spec_80003es2lan e80003es2lan;
-               struct e1000_dev_spec_ich8lan   ich8lan;
+               struct e1000_dev_spec_ich8lan ich8lan;
        } dev_spec;
 };
 
index ad9d8f2..9dde390 100644 (file)
@@ -101,12 +101,12 @@ union ich8_hws_flash_regacc {
 /* ICH Flash Protected Region */
 union ich8_flash_protected_range {
        struct ich8_pr {
-               u32 base:13;     /* 0:12 Protected Range Base */
-               u32 reserved1:2; /* 13:14 Reserved */
-               u32 rpe:1;       /* 15 Read Protection Enable */
-               u32 limit:13;    /* 16:28 Protected Range Limit */
-               u32 reserved2:2; /* 29:30 Reserved */
-               u32 wpe:1;       /* 31 Write Protection Enable */
+               u32 base:13;    /* 0:12 Protected Range Base */
+               u32 reserved1:2;        /* 13:14 Reserved */
+               u32 rpe:1;      /* 15 Read Protection Enable */
+               u32 limit:13;   /* 16:28 Protected Range Limit */
+               u32 reserved2:2;        /* 29:30 Reserved */
+               u32 wpe:1;      /* 31 Write Protection Enable */
        } range;
        u32 regval;
 };
@@ -362,21 +362,21 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw)
        struct e1000_phy_info *phy = &hw->phy;
        s32 ret_val;
 
-       phy->addr                     = 1;
-       phy->reset_delay_us           = 100;
-
-       phy->ops.set_page             = e1000_set_page_igp;
-       phy->ops.read_reg             = e1000_read_phy_reg_hv;
-       phy->ops.read_reg_locked      = e1000_read_phy_reg_hv_locked;
-       phy->ops.read_reg_page        = e1000_read_phy_reg_page_hv;
-       phy->ops.set_d0_lplu_state    = e1000_set_lplu_state_pchlan;
-       phy->ops.set_d3_lplu_state    = e1000_set_lplu_state_pchlan;
-       phy->ops.write_reg            = e1000_write_phy_reg_hv;
-       phy->ops.write_reg_locked     = e1000_write_phy_reg_hv_locked;
-       phy->ops.write_reg_page       = e1000_write_phy_reg_page_hv;
-       phy->ops.power_up             = e1000_power_up_phy_copper;
-       phy->ops.power_down           = e1000_power_down_phy_copper_ich8lan;
-       phy->autoneg_mask             = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+       phy->addr = 1;
+       phy->reset_delay_us = 100;
+
+       phy->ops.set_page = e1000_set_page_igp;
+       phy->ops.read_reg = e1000_read_phy_reg_hv;
+       phy->ops.read_reg_locked = e1000_read_phy_reg_hv_locked;
+       phy->ops.read_reg_page = e1000_read_phy_reg_page_hv;
+       phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan;
+       phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan;
+       phy->ops.write_reg = e1000_write_phy_reg_hv;
+       phy->ops.write_reg_locked = e1000_write_phy_reg_hv_locked;
+       phy->ops.write_reg_page = e1000_write_phy_reg_page_hv;
+       phy->ops.power_up = e1000_power_up_phy_copper;
+       phy->ops.power_down = e1000_power_down_phy_copper_ich8lan;
+       phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
 
        phy->id = e1000_phy_unknown;
 
@@ -445,11 +445,11 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
        s32 ret_val;
        u16 i = 0;
 
-       phy->addr                       = 1;
-       phy->reset_delay_us             = 100;
+       phy->addr = 1;
+       phy->reset_delay_us = 100;
 
-       phy->ops.power_up               = e1000_power_up_phy_copper;
-       phy->ops.power_down             = e1000_power_down_phy_copper_ich8lan;
+       phy->ops.power_up = e1000_power_up_phy_copper;
+       phy->ops.power_down = e1000_power_down_phy_copper_ich8lan;
 
        /* We may need to do this twice - once for IGP and if that fails,
         * we'll set BM func pointers and try again
@@ -457,7 +457,7 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
        ret_val = e1000e_determine_phy_address(hw);
        if (ret_val) {
                phy->ops.write_reg = e1000e_write_phy_reg_bm;
-               phy->ops.read_reg  = e1000e_read_phy_reg_bm;
+               phy->ops.read_reg = e1000e_read_phy_reg_bm;
                ret_val = e1000e_determine_phy_address(hw);
                if (ret_val) {
                        e_dbg("Cannot determine PHY addr. Erroring out\n");
@@ -560,7 +560,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw)
        /* Clear shadow ram */
        for (i = 0; i < nvm->word_size; i++) {
                dev_spec->shadow_ram[i].modified = false;
-               dev_spec->shadow_ram[i].value    = 0xFFFF;
+               dev_spec->shadow_ram[i].value = 0xFFFF;
        }
 
        return 0;
@@ -1012,7 +1012,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
        hw->dev_spec.ich8lan.eee_lp_ability = 0;
 
        if (!link)
-               return 0; /* No link detected */
+               return 0;       /* No link detected */
 
        mac->get_link_status = false;
 
@@ -2816,7 +2816,7 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
        s32 ret_val = -E1000_ERR_NVM;
        u8 count = 0;
 
-       if (size < 1  || size > 2 || offset > ICH_FLASH_LINEAR_ADDR_MASK)
+       if (size < 1 || size > 2 || offset > ICH_FLASH_LINEAR_ADDR_MASK)
                return -E1000_ERR_NVM;
 
        flash_linear_addr = ((ICH_FLASH_LINEAR_ADDR_MASK & offset) +
@@ -2939,7 +2939,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
         * write to bank 0 etc.  We also need to erase the segment that
         * is going to be written
         */
-       ret_val =  e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
+       ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
        if (ret_val) {
                e_dbg("Could not detect valid bank, assuming bank 0\n");
                bank = 0;
@@ -4073,7 +4073,7 @@ void e1000e_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw)
 {
        u32 reg;
        u16 data;
-       u8  retry = 0;
+       u8 retry = 0;
 
        if (hw->phy.type != e1000_phy_igp_3)
                return;
index a27e3bc..77f81cb 100644 (file)
@@ -1196,7 +1196,7 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring)
        while ((eop_desc->upper.data & cpu_to_le32(E1000_TXD_STAT_DD)) &&
               (count < tx_ring->count)) {
                bool cleaned = false;
-               rmb(); /* read buffer_info after eop_desc */
+               rmb();          /* read buffer_info after eop_desc */
                for (; !cleaned; count++) {
                        tx_desc = E1000_TX_DESC(*tx_ring, i);
                        buffer_info = &tx_ring->buffer_info[i];
@@ -1385,7 +1385,7 @@ static bool e1000_clean_rx_irq_ps(struct e1000_ring *rx_ring, int *work_done,
 
                                skb_put(skb, l1);
                                goto copydone;
-                       } /* if */
+                       }       /* if */
                }
 
                for (j = 0; j < PS_PAGE_BUFFERS; j++) {
@@ -1800,7 +1800,7 @@ static irqreturn_t e1000_intr(int __always_unused irq, void *data)
        u32 rctl, icr = er32(ICR);
 
        if (!icr || test_bit(__E1000_DOWN, &adapter->state))
-               return IRQ_NONE;  /* Not our interrupt */
+               return IRQ_NONE;        /* Not our interrupt */
 
        /* IMS will not auto-mask if INT_ASSERTED is not set, and if it is
         * not set, then the adapter didn't send an interrupt
@@ -2487,7 +2487,7 @@ static unsigned int e1000_update_itr(u16 itr_setting, int packets, int bytes)
                else if ((packets < 5) && (bytes > 512))
                        retval = low_latency;
                break;
-       case low_latency:  /* 50 usec aka 20000 ints/s */
+       case low_latency:       /* 50 usec aka 20000 ints/s */
                if (bytes > 10000) {
                        /* this if handles the TSO accounting */
                        if (bytes / packets > 8000)
@@ -2502,7 +2502,7 @@ static unsigned int e1000_update_itr(u16 itr_setting, int packets, int bytes)
                        retval = lowest_latency;
                }
                break;
-       case bulk_latency: /* 250 usec aka 4000 ints/s */
+       case bulk_latency:      /* 250 usec aka 4000 ints/s */
                if (bytes > 25000) {
                        if (packets > 35)
                                retval = low_latency;
@@ -2554,7 +2554,7 @@ static void e1000_set_itr(struct e1000_adapter *adapter)
                new_itr = 70000;
                break;
        case low_latency:
-               new_itr = 20000; /* aka hwitr = ~200 */
+               new_itr = 20000;        /* aka hwitr = ~200 */
                break;
        case bulk_latency:
                new_itr = 4000;
@@ -2673,7 +2673,7 @@ static int e1000e_poll(struct napi_struct *napi, int weight)
 }
 
 static int e1000_vlan_rx_add_vid(struct net_device *netdev,
-                                __be16 proto, u16 vid)
+                                __always_unused __be16 proto, u16 vid)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -2699,7 +2699,7 @@ static int e1000_vlan_rx_add_vid(struct net_device *netdev,
 }
 
 static int e1000_vlan_rx_kill_vid(struct net_device *netdev,
-                                 __be16 proto, u16 vid)
+                                 __always_unused __be16 proto, u16 vid)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -3104,13 +3104,13 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter)
                /* UPE and MPE will be handled by normal PROMISC logic
                 * in e1000e_set_rx_mode
                 */
-               rctl |= (E1000_RCTL_SBP | /* Receive bad packets */
-                        E1000_RCTL_BAM | /* RX All Bcast Pkts */
-                        E1000_RCTL_PMCF); /* RX All MAC Ctrl Pkts */
+               rctl |= (E1000_RCTL_SBP |       /* Receive bad packets */
+                        E1000_RCTL_BAM |       /* RX All Bcast Pkts */
+                        E1000_RCTL_PMCF);      /* RX All MAC Ctrl Pkts */
 
-               rctl &= ~(E1000_RCTL_VFE | /* Disable VLAN filter */
-                         E1000_RCTL_DPF | /* Allow filtered pause */
-                         E1000_RCTL_CFIEN); /* Dis VLAN CFIEN Filter */
+               rctl &= ~(E1000_RCTL_VFE |      /* Disable VLAN filter */
+                         E1000_RCTL_DPF |      /* Allow filtered pause */
+                         E1000_RCTL_CFIEN);    /* Dis VLAN CFIEN Filter */
                /* Do not mess with E1000_CTRL_VME, it affects transmit as well,
                 * and that breaks VLANs.
                 */
@@ -3799,7 +3799,7 @@ void e1000e_reset(struct e1000_adapter *adapter)
                hwm = min(((pba << 10) * 9 / 10),
                          ((pba << 10) - adapter->max_frame_size));
 
-               fc->high_water = hwm & E1000_FCRTH_RTH; /* 8-byte granularity */
+               fc->high_water = hwm & E1000_FCRTH_RTH; /* 8-byte granularity */
                fc->low_water = fc->high_water - 8;
                break;
        case e1000_pchlan:
@@ -3808,10 +3808,10 @@ void e1000e_reset(struct e1000_adapter *adapter)
                 */
                if (adapter->netdev->mtu > ETH_DATA_LEN) {
                        fc->high_water = 0x3500;
-                       fc->low_water  = 0x1500;
+                       fc->low_water = 0x1500;
                } else {
                        fc->high_water = 0x5000;
-                       fc->low_water  = 0x3000;
+                       fc->low_water = 0x3000;
                }
                fc->refresh_time = 0x1000;
                break;
@@ -4581,7 +4581,7 @@ static void e1000e_update_stats(struct e1000_adapter *adapter)
        adapter->stats.crcerrs += er32(CRCERRS);
        adapter->stats.gprc += er32(GPRC);
        adapter->stats.gorc += er32(GORCL);
-       er32(GORCH); /* Clear gorc */
+       er32(GORCH);            /* Clear gorc */
        adapter->stats.bprc += er32(BPRC);
        adapter->stats.mprc += er32(MPRC);
        adapter->stats.roc += er32(ROC);
@@ -4614,7 +4614,7 @@ static void e1000e_update_stats(struct e1000_adapter *adapter)
        adapter->stats.xofftxc += er32(XOFFTXC);
        adapter->stats.gptc += er32(GPTC);
        adapter->stats.gotc += er32(GOTCL);
-       er32(GOTCH); /* Clear gotc */
+       er32(GOTCH);            /* Clear gotc */
        adapter->stats.rnbc += er32(RNBC);
        adapter->stats.ruc += er32(RUC);
 
@@ -5106,13 +5106,13 @@ static int e1000_tso(struct e1000_ring *tx_ring, struct sk_buff *skb)
        context_desc = E1000_CONTEXT_DESC(*tx_ring, i);
        buffer_info = &tx_ring->buffer_info[i];
 
-       context_desc->lower_setup.ip_fields.ipcss  = ipcss;
-       context_desc->lower_setup.ip_fields.ipcso  = ipcso;
-       context_desc->lower_setup.ip_fields.ipcse  = cpu_to_le16(ipcse);
+       context_desc->lower_setup.ip_fields.ipcss = ipcss;
+       context_desc->lower_setup.ip_fields.ipcso = ipcso;
+       context_desc->lower_setup.ip_fields.ipcse = cpu_to_le16(ipcse);
        context_desc->upper_setup.tcp_fields.tucss = tucss;
        context_desc->upper_setup.tcp_fields.tucso = tucso;
        context_desc->upper_setup.tcp_fields.tucse = 0;
-       context_desc->tcp_seg_setup.fields.mss     = cpu_to_le16(mss);
+       context_desc->tcp_seg_setup.fields.mss = cpu_to_le16(mss);
        context_desc->tcp_seg_setup.fields.hdr_len = hdr_len;
        context_desc->cmd_and_length = cpu_to_le32(cmd_length);
 
@@ -5363,7 +5363,7 @@ static void e1000_tx_queue(struct e1000_ring *tx_ring, int tx_flags, int count)
 static int e1000_transfer_dhcp_info(struct e1000_adapter *adapter,
                                    struct sk_buff *skb)
 {
-       struct e1000_hw *hw =  &adapter->hw;
+       struct e1000_hw *hw = &adapter->hw;
        u16 length, offset;
 
        if (vlan_tx_tag_present(skb) &&
@@ -6259,7 +6259,7 @@ static void e1000_netpoll(struct net_device *netdev)
                e1000_intr_msi(adapter->pdev->irq, netdev);
                enable_irq(adapter->pdev->irq);
                break;
-       default: /* E1000E_INT_MODE_LEGACY */
+       default:                /* E1000E_INT_MODE_LEGACY */
                disable_irq(adapter->pdev->irq);
                e1000_intr(adapter->pdev->irq, netdev);
                enable_irq(adapter->pdev->irq);
@@ -6589,9 +6589,9 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                adapter->eee_advert = MDIO_EEE_100TX | MDIO_EEE_1000T;
 
        /* construct the net_device struct */
-       netdev->netdev_ops              = &e1000e_netdev_ops;
+       netdev->netdev_ops = &e1000e_netdev_ops;
        e1000e_set_ethtool_ops(netdev);
-       netdev->watchdog_timeo          = 5 * HZ;
+       netdev->watchdog_timeo = 5 * HZ;
        netif_napi_add(netdev, &adapter->napi, e1000e_poll, 64);
        strlcpy(netdev->name, pci_name(pdev), sizeof(netdev->name));
 
@@ -7034,7 +7034,6 @@ static void __exit e1000_exit_module(void)
 }
 module_exit(e1000_exit_module);
 
-
 MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>");
 MODULE_DESCRIPTION("Intel(R) PRO/1000 Network Driver");
 MODULE_LICENSE("GPL");
index 44ddc0a..d70a039 100644 (file)
@@ -117,7 +117,6 @@ static u16 e1000_shift_in_eec_bits(struct e1000_hw *hw, u16 count)
        u16 data;
 
        eecd = er32(EECD);
-
        eecd &= ~(E1000_EECD_DO | E1000_EECD_DI);
        data = 0;
 
index 59c76a6..da2be59 100644 (file)
@@ -1583,13 +1583,13 @@ s32 e1000e_check_downshift(struct e1000_hw *hw)
        case e1000_phy_gg82563:
        case e1000_phy_bm:
        case e1000_phy_82578:
-               offset  = M88E1000_PHY_SPEC_STATUS;
-               mask    = M88E1000_PSSR_DOWNSHIFT;
+               offset = M88E1000_PHY_SPEC_STATUS;
+               mask = M88E1000_PSSR_DOWNSHIFT;
                break;
        case e1000_phy_igp_2:
        case e1000_phy_igp_3:
-               offset  = IGP01E1000_PHY_LINK_HEALTH;
-               mask    = IGP01E1000_PLHR_SS_DOWNGRADE;
+               offset = IGP01E1000_PHY_LINK_HEALTH;
+               mask = IGP01E1000_PLHR_SS_DOWNGRADE;
                break;
        default:
                /* speed downshift not supported */
@@ -1653,14 +1653,14 @@ s32 e1000_check_polarity_igp(struct e1000_hw *hw)
 
        if ((data & IGP01E1000_PSSR_SPEED_MASK) ==
            IGP01E1000_PSSR_SPEED_1000MBPS) {
-               offset  = IGP01E1000_PHY_PCS_INIT_REG;
-               mask    = IGP01E1000_PHY_POLARITY_MASK;
+               offset = IGP01E1000_PHY_PCS_INIT_REG;
+               mask = IGP01E1000_PHY_POLARITY_MASK;
        } else {
                /* This really only applies to 10Mbps since
                 * there is no polarity for 100Mbps (always 0).
                 */
-               offset  = IGP01E1000_PHY_PORT_STATUS;
-               mask    = IGP01E1000_PSSR_POLARITY_REVERSED;
+               offset = IGP01E1000_PHY_PORT_STATUS;
+               mask = IGP01E1000_PSSR_POLARITY_REVERSED;
        }
 
        ret_val = e1e_rphy(hw, offset, &data);
@@ -1900,7 +1900,7 @@ s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw)
 s32 e1000e_get_phy_info_m88(struct e1000_hw *hw)
 {
        struct e1000_phy_info *phy = &hw->phy;
-       s32  ret_val;
+       s32 ret_val;
        u16 phy_data;
        bool link;
 
@@ -2253,7 +2253,7 @@ enum e1000_phy_type e1000e_get_phy_type_from_id(u32 phy_id)
        case M88E1011_I_PHY_ID:
                phy_type = e1000_phy_m88;
                break;
-       case IGP01E1000_I_PHY_ID: /* IGP 1 & 2 share this */
+       case IGP01E1000_I_PHY_ID:       /* IGP 1 & 2 share this */
                phy_type = e1000_phy_igp_2;
                break;
        case GG82563_E_PHY_ID:
@@ -2317,7 +2317,7 @@ s32 e1000e_determine_phy_address(struct e1000_hw *hw)
                        /* If phy_type is valid, break - we found our
                         * PHY address
                         */
-                       if (phy_type  != e1000_phy_unknown)
+                       if (phy_type != e1000_phy_unknown)
                                return 0;
 
                        usleep_range(1000, 2000);
index ff6a17c..f21a91a 100644 (file)
@@ -401,12 +401,82 @@ static s32 igb_init_mac_params_82575(struct e1000_hw *hw)
        return 0;
 }
 
+/**
+ *  igb_set_sfp_media_type_82575 - derives SFP module media type.
+ *  @hw: pointer to the HW structure
+ *
+ *  The media type is chosen based on SFP module.
+ *  compatibility flags retrieved from SFP ID EEPROM.
+ **/
+static s32 igb_set_sfp_media_type_82575(struct e1000_hw *hw)
+{
+       s32 ret_val = E1000_ERR_CONFIG;
+       u32 ctrl_ext = 0;
+       struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575;
+       struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags;
+       u8 tranceiver_type = 0;
+       s32 timeout = 3;
+
+       /* Turn I2C interface ON and power on sfp cage */
+       ctrl_ext = rd32(E1000_CTRL_EXT);
+       ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA;
+       wr32(E1000_CTRL_EXT, ctrl_ext | E1000_CTRL_I2C_ENA);
+
+       wrfl();
+
+       /* Read SFP module data */
+       while (timeout) {
+               ret_val = igb_read_sfp_data_byte(hw,
+                       E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_IDENTIFIER_OFFSET),
+                       &tranceiver_type);
+               if (ret_val == 0)
+                       break;
+               msleep(100);
+               timeout--;
+       }
+       if (ret_val != 0)
+               goto out;
+
+       ret_val = igb_read_sfp_data_byte(hw,
+                       E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_ETH_FLAGS_OFFSET),
+                       (u8 *)eth_flags);
+       if (ret_val != 0)
+               goto out;
+
+       /* Check if there is some SFP module plugged and powered */
+       if ((tranceiver_type == E1000_SFF_IDENTIFIER_SFP) ||
+           (tranceiver_type == E1000_SFF_IDENTIFIER_SFF)) {
+               dev_spec->module_plugged = true;
+               if (eth_flags->e1000_base_lx || eth_flags->e1000_base_sx) {
+                       hw->phy.media_type = e1000_media_type_internal_serdes;
+               } else if (eth_flags->e100_base_fx) {
+                       dev_spec->sgmii_active = true;
+                       hw->phy.media_type = e1000_media_type_internal_serdes;
+               } else if (eth_flags->e1000_base_t) {
+                       dev_spec->sgmii_active = true;
+                       hw->phy.media_type = e1000_media_type_copper;
+               } else {
+                       hw->phy.media_type = e1000_media_type_unknown;
+                       hw_dbg("PHY module has not been recognized\n");
+                       goto out;
+               }
+       } else {
+               hw->phy.media_type = e1000_media_type_unknown;
+       }
+       ret_val = 0;
+out:
+       /* Restore I2C interface setting */
+       wr32(E1000_CTRL_EXT, ctrl_ext);
+       return ret_val;
+}
+
 static s32 igb_get_invariants_82575(struct e1000_hw *hw)
 {
        struct e1000_mac_info *mac = &hw->mac;
        struct e1000_dev_spec_82575 * dev_spec = &hw->dev_spec._82575;
        s32 ret_val;
        u32 ctrl_ext = 0;
+       u32 link_mode = 0;
 
        switch (hw->device_id) {
        case E1000_DEV_ID_82575EB_COPPER:
@@ -470,15 +540,55 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
         */
        hw->phy.media_type = e1000_media_type_copper;
        dev_spec->sgmii_active = false;
+       dev_spec->module_plugged = false;
 
        ctrl_ext = rd32(E1000_CTRL_EXT);
-       switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) {
-       case E1000_CTRL_EXT_LINK_MODE_SGMII:
-               dev_spec->sgmii_active = true;
-               break;
+
+       link_mode = ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK;
+       switch (link_mode) {
        case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
-       case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES:
                hw->phy.media_type = e1000_media_type_internal_serdes;
+               break;
+       case E1000_CTRL_EXT_LINK_MODE_SGMII:
+               /* Get phy control interface type set (MDIO vs. I2C)*/
+               if (igb_sgmii_uses_mdio_82575(hw)) {
+                       hw->phy.media_type = e1000_media_type_copper;
+                       dev_spec->sgmii_active = true;
+                       break;
+               }
+               /* fall through for I2C based SGMII */
+       case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES:
+               /* read media type from SFP EEPROM */
+               ret_val = igb_set_sfp_media_type_82575(hw);
+               if ((ret_val != 0) ||
+                   (hw->phy.media_type == e1000_media_type_unknown)) {
+                       /* If media type was not identified then return media
+                        * type defined by the CTRL_EXT settings.
+                        */
+                       hw->phy.media_type = e1000_media_type_internal_serdes;
+
+                       if (link_mode == E1000_CTRL_EXT_LINK_MODE_SGMII) {
+                               hw->phy.media_type = e1000_media_type_copper;
+                               dev_spec->sgmii_active = true;
+                       }
+
+                       break;
+               }
+
+               /* do not change link mode for 100BaseFX */
+               if (dev_spec->eth_flags.e100_base_fx)
+                       break;
+
+               /* change current link mode setting */
+               ctrl_ext &= ~E1000_CTRL_EXT_LINK_MODE_MASK;
+
+               if (hw->phy.media_type == e1000_media_type_copper)
+                       ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_SGMII;
+               else
+                       ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES;
+
+               wr32(E1000_CTRL_EXT, ctrl_ext);
+
                break;
        default:
                break;
index 31a0f82..aa201ab 100644 (file)
 /* Clear Interrupt timers after IMS clear */
 /* packet buffer parity error detection enabled */
 /* descriptor FIFO parity error detection enable */
-#define E1000_CTRL_EXT_PBA_CLR        0x80000000 /* PBA Clear */
-#define E1000_I2CCMD_REG_ADDR_SHIFT   16
-#define E1000_I2CCMD_PHY_ADDR_SHIFT   24
-#define E1000_I2CCMD_OPCODE_READ      0x08000000
-#define E1000_I2CCMD_OPCODE_WRITE     0x00000000
-#define E1000_I2CCMD_READY            0x20000000
-#define E1000_I2CCMD_ERROR            0x80000000
-#define E1000_MAX_SGMII_PHY_REG_ADDR  255
-#define E1000_I2CCMD_PHY_TIMEOUT      200
-#define E1000_IVAR_VALID              0x80
-#define E1000_GPIE_NSICR              0x00000001
-#define E1000_GPIE_MSIX_MODE          0x00000010
-#define E1000_GPIE_EIAME              0x40000000
-#define E1000_GPIE_PBA                0x80000000
+#define E1000_CTRL_EXT_PBA_CLR         0x80000000 /* PBA Clear */
+#define E1000_I2CCMD_REG_ADDR_SHIFT    16
+#define E1000_I2CCMD_PHY_ADDR_SHIFT    24
+#define E1000_I2CCMD_OPCODE_READ       0x08000000
+#define E1000_I2CCMD_OPCODE_WRITE      0x00000000
+#define E1000_I2CCMD_READY             0x20000000
+#define E1000_I2CCMD_ERROR             0x80000000
+#define E1000_I2CCMD_SFP_DATA_ADDR(a)  (0x0000 + (a))
+#define E1000_I2CCMD_SFP_DIAG_ADDR(a)  (0x0100 + (a))
+#define E1000_MAX_SGMII_PHY_REG_ADDR   255
+#define E1000_I2CCMD_PHY_TIMEOUT       200
+#define E1000_IVAR_VALID               0x80
+#define E1000_GPIE_NSICR               0x00000001
+#define E1000_GPIE_MSIX_MODE           0x00000010
+#define E1000_GPIE_EIAME               0x40000000
+#define E1000_GPIE_PBA                 0x80000000
 
 /* Receive Descriptor bit definitions */
 #define E1000_RXD_STAT_DD       0x01    /* Descriptor Done */
 #define AUTONEG_ADVERTISE_SPEED_DEFAULT   E1000_ALL_SPEED_DUPLEX
 
 /* LED Control */
-#define E1000_LEDCTL_LED0_MODE_SHIFT      0
-#define E1000_LEDCTL_LED0_BLINK           0x00000080
+#define E1000_LEDCTL_LED0_MODE_SHIFT   0
+#define E1000_LEDCTL_LED0_BLINK                0x00000080
+#define E1000_LEDCTL_LED0_MODE_MASK    0x0000000F
+#define E1000_LEDCTL_LED0_IVRT         0x00000040
 
 #define E1000_LEDCTL_MODE_LED_ON        0xE
 #define E1000_LEDCTL_MODE_LED_OFF       0xF
index 488abb2..94d7866 100644 (file)
@@ -528,6 +528,8 @@ struct e1000_dev_spec_82575 {
        bool global_device_reset;
        bool eee_disable;
        bool clear_semaphore_once;
+       struct e1000_sfp_flags eth_flags;
+       bool module_plugged;
 };
 
 struct e1000_hw {
index bfc08e0..5caa332 100644 (file)
@@ -82,11 +82,11 @@ enum E1000_INVM_STRUCTURE_TYPE {
 #define E1000_INVM_MAJOR_SHIFT         4
 
 #define ID_LED_DEFAULT_I210            ((ID_LED_OFF1_ON2  << 8) | \
-                                        (ID_LED_OFF1_OFF2 <<  4) | \
-                                        (ID_LED_DEF1_DEF2))
+                                        (ID_LED_DEF1_DEF2 <<  4) | \
+                                        (ID_LED_OFF1_OFF2))
 #define ID_LED_DEFAULT_I210_SERDES     ((ID_LED_DEF1_DEF2 << 8) | \
                                         (ID_LED_DEF1_DEF2 <<  4) | \
-                                        (ID_LED_DEF1_DEF2))
+                                        (ID_LED_OFF1_ON2))
 
 /* NVM offset defaults for i211 device */
 #define NVM_INIT_CTRL_2_DEFAULT_I211   0X7243
index 2559d70..bab556a 100644 (file)
@@ -1332,7 +1332,13 @@ s32 igb_id_led_init(struct e1000_hw *hw)
        u16 data, i, temp;
        const u16 led_mask = 0x0F;
 
-       ret_val = igb_valid_led_default(hw, &data);
+       /* i210 and i211 devices have different LED mechanism */
+       if ((hw->mac.type == e1000_i210) ||
+           (hw->mac.type == e1000_i211))
+               ret_val = igb_valid_led_default_i210(hw, &data);
+       else
+               ret_val = igb_valid_led_default(hw, &data);
+
        if (ret_val)
                goto out;
 
@@ -1406,15 +1412,34 @@ s32 igb_blink_led(struct e1000_hw *hw)
        u32 ledctl_blink = 0;
        u32 i;
 
-       /* set the blink bit for each LED that's "on" (0x0E)
-        * in ledctl_mode2
-        */
-       ledctl_blink = hw->mac.ledctl_mode2;
-       for (i = 0; i < 4; i++)
-               if (((hw->mac.ledctl_mode2 >> (i * 8)) & 0xFF) ==
-                   E1000_LEDCTL_MODE_LED_ON)
-                       ledctl_blink |= (E1000_LEDCTL_LED0_BLINK <<
-                                        (i * 8));
+       if (hw->phy.media_type == e1000_media_type_fiber) {
+               /* always blink LED0 for PCI-E fiber */
+               ledctl_blink = E1000_LEDCTL_LED0_BLINK |
+                    (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT);
+       } else {
+               /* Set the blink bit for each LED that's "on" (0x0E)
+                * (or "off" if inverted) in ledctl_mode2.  The blink
+                * logic in hardware only works when mode is set to "on"
+                * so it must be changed accordingly when the mode is
+                * "off" and inverted.
+                */
+               ledctl_blink = hw->mac.ledctl_mode2;
+               for (i = 0; i < 32; i += 8) {
+                       u32 mode = (hw->mac.ledctl_mode2 >> i) &
+                           E1000_LEDCTL_LED0_MODE_MASK;
+                       u32 led_default = hw->mac.ledctl_default >> i;
+
+                       if ((!(led_default & E1000_LEDCTL_LED0_IVRT) &&
+                            (mode == E1000_LEDCTL_MODE_LED_ON)) ||
+                           ((led_default & E1000_LEDCTL_LED0_IVRT) &&
+                            (mode == E1000_LEDCTL_MODE_LED_OFF))) {
+                               ledctl_blink &=
+                                   ~(E1000_LEDCTL_LED0_MODE_MASK << i);
+                               ledctl_blink |= (E1000_LEDCTL_LED0_BLINK |
+                                                E1000_LEDCTL_MODE_LED_ON) << i;
+                       }
+               }
+       }
 
        wr32(E1000_LEDCTL, ledctl_blink);
 
index 9979ebc..6046194 100644 (file)
@@ -340,6 +340,130 @@ s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data)
        return 0;
 }
 
+/**
+ *  igb_read_sfp_data_byte - Reads SFP module data.
+ *  @hw: pointer to the HW structure
+ *  @offset: byte location offset to be read
+ *  @data: read data buffer pointer
+ *
+ *  Reads one byte from SFP module data stored
+ *  in SFP resided EEPROM memory or SFP diagnostic area.
+ *  Function should be called with
+ *  E1000_I2CCMD_SFP_DATA_ADDR(<byte offset>) for SFP module database access
+ *  E1000_I2CCMD_SFP_DIAG_ADDR(<byte offset>) for SFP diagnostics parameters
+ *  access
+ **/
+s32 igb_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data)
+{
+       u32 i = 0;
+       u32 i2ccmd = 0;
+       u32 data_local = 0;
+
+       if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) {
+               hw_dbg("I2CCMD command address exceeds upper limit\n");
+               return -E1000_ERR_PHY;
+       }
+
+       /* Set up Op-code, EEPROM Address,in the I2CCMD
+        * register. The MAC will take care of interfacing with the
+        * EEPROM to retrieve the desired data.
+        */
+       i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+                 E1000_I2CCMD_OPCODE_READ);
+
+       wr32(E1000_I2CCMD, i2ccmd);
+
+       /* Poll the ready bit to see if the I2C read completed */
+       for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+               udelay(50);
+               data_local = rd32(E1000_I2CCMD);
+               if (data_local & E1000_I2CCMD_READY)
+                       break;
+       }
+       if (!(data_local & E1000_I2CCMD_READY)) {
+               hw_dbg("I2CCMD Read did not complete\n");
+               return -E1000_ERR_PHY;
+       }
+       if (data_local & E1000_I2CCMD_ERROR) {
+               hw_dbg("I2CCMD Error bit set\n");
+               return -E1000_ERR_PHY;
+       }
+       *data = (u8) data_local & 0xFF;
+
+       return 0;
+}
+
+/**
+ *  e1000_write_sfp_data_byte - Writes SFP module data.
+ *  @hw: pointer to the HW structure
+ *  @offset: byte location offset to write to
+ *  @data: data to write
+ *
+ *  Writes one byte to SFP module data stored
+ *  in SFP resided EEPROM memory or SFP diagnostic area.
+ *  Function should be called with
+ *  E1000_I2CCMD_SFP_DATA_ADDR(<byte offset>) for SFP module database access
+ *  E1000_I2CCMD_SFP_DIAG_ADDR(<byte offset>) for SFP diagnostics parameters
+ *  access
+ **/
+s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data)
+{
+       u32 i = 0;
+       u32 i2ccmd = 0;
+       u32 data_local = 0;
+
+       if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) {
+               hw_dbg("I2CCMD command address exceeds upper limit\n");
+               return -E1000_ERR_PHY;
+       }
+       /* The programming interface is 16 bits wide
+        * so we need to read the whole word first
+        * then update appropriate byte lane and write
+        * the updated word back.
+        */
+       /* Set up Op-code, EEPROM Address,in the I2CCMD
+        * register. The MAC will take care of interfacing
+        * with an EEPROM to write the data given.
+        */
+       i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+                 E1000_I2CCMD_OPCODE_READ);
+       /* Set a command to read single word */
+       wr32(E1000_I2CCMD, i2ccmd);
+       for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+               udelay(50);
+               /* Poll the ready bit to see if lastly
+                * launched I2C operation completed
+                */
+               i2ccmd = rd32(E1000_I2CCMD);
+               if (i2ccmd & E1000_I2CCMD_READY) {
+                       /* Check if this is READ or WRITE phase */
+                       if ((i2ccmd & E1000_I2CCMD_OPCODE_READ) ==
+                           E1000_I2CCMD_OPCODE_READ) {
+                               /* Write the selected byte
+                                * lane and update whole word
+                                */
+                               data_local = i2ccmd & 0xFF00;
+                               data_local |= data;
+                               i2ccmd = ((offset <<
+                                       E1000_I2CCMD_REG_ADDR_SHIFT) |
+                                       E1000_I2CCMD_OPCODE_WRITE | data_local);
+                               wr32(E1000_I2CCMD, i2ccmd);
+                       } else {
+                               break;
+                       }
+               }
+       }
+       if (!(i2ccmd & E1000_I2CCMD_READY)) {
+               hw_dbg("I2CCMD Write did not complete\n");
+               return -E1000_ERR_PHY;
+       }
+       if (i2ccmd & E1000_I2CCMD_ERROR) {
+               hw_dbg("I2CCMD Error bit set\n");
+               return -E1000_ERR_PHY;
+       }
+       return 0;
+}
+
 /**
  *  igb_read_phy_reg_igp - Read igp PHY register
  *  @hw: pointer to the HW structure
index 784fd1c..6a0873f 100644 (file)
@@ -69,6 +69,8 @@ s32  igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
 s32  igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
 s32  igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data);
 s32  igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data);
+s32  igb_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data);
+s32  e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data);
 s32  igb_copper_link_setup_82580(struct e1000_hw *hw);
 s32  igb_get_phy_info_82580(struct e1000_hw *hw);
 s32  igb_phy_force_speed_duplex_82580(struct e1000_hw *hw);
@@ -157,4 +159,22 @@ s32  igb_check_polarity_m88(struct e1000_hw *hw);
 #define GS40G_CS_POWER_DOWN            0x0002
 #define GS40G_LINE_LB                  0x4000
 
+/* SFP modules ID memory locations */
+#define E1000_SFF_IDENTIFIER_OFFSET    0x00
+#define E1000_SFF_IDENTIFIER_SFF       0x02
+#define E1000_SFF_IDENTIFIER_SFP       0x03
+
+#define E1000_SFF_ETH_FLAGS_OFFSET     0x06
+/* Flags for SFP modules compatible with ETH up to 1Gb */
+struct e1000_sfp_flags {
+       u8 e1000_base_sx:1;
+       u8 e1000_base_lx:1;
+       u8 e1000_base_cx:1;
+       u8 e1000_base_t:1;
+       u8 e100_base_lx:1;
+       u8 e100_base_fx:1;
+       u8 e10_base_bx10:1;
+       u8 e10_base_px:1;
+};
+
 #endif
index 9d6c075..15ea8dc 100644 (file)
@@ -322,11 +322,6 @@ static inline int igb_desc_unused(struct igb_ring *ring)
        return ring->count + ring->next_to_clean - ring->next_to_use - 1;
 }
 
-struct igb_i2c_client_list {
-       struct i2c_client *client;
-       struct igb_i2c_client_list *next;
-};
-
 #ifdef CONFIG_IGB_HWMON
 
 #define IGB_HWMON_TYPE_LOC     0
@@ -514,13 +509,18 @@ extern void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector,
 extern void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector,
                                unsigned char *va,
                                struct sk_buff *skb);
-static inline void igb_ptp_rx_hwtstamp(struct igb_q_vector *q_vector,
+static inline void igb_ptp_rx_hwtstamp(struct igb_ring *rx_ring,
                                       union e1000_adv_rx_desc *rx_desc,
                                       struct sk_buff *skb)
 {
        if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TS) &&
            !igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP))
-               igb_ptp_rx_rgtstamp(q_vector, skb);
+               igb_ptp_rx_rgtstamp(rx_ring->q_vector, skb);
+
+       /* Update the last_rx_timestamp timer in order to enable watchdog check
+        * for error case of latched timestamp on a dropped packet.
+        */
+       rx_ring->last_rx_timestamp = jiffies;
 }
 
 extern int igb_ptp_hwtstamp_ioctl(struct net_device *netdev,
index 7876240..85fe7b5 100644 (file)
@@ -142,6 +142,8 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
+       struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575;
+       struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags;
        u32 status;
 
        if (hw->phy.media_type == e1000_media_type_copper) {
@@ -162,49 +164,26 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                        ecmd->advertising |= hw->phy.autoneg_advertised;
                }
 
-               if (hw->mac.autoneg != 1)
-                       ecmd->advertising &= ~(ADVERTISED_Pause |
-                                              ADVERTISED_Asym_Pause);
-
-               if (hw->fc.requested_mode == e1000_fc_full)
-                       ecmd->advertising |= ADVERTISED_Pause;
-               else if (hw->fc.requested_mode == e1000_fc_rx_pause)
-                       ecmd->advertising |= (ADVERTISED_Pause |
-                                             ADVERTISED_Asym_Pause);
-               else if (hw->fc.requested_mode == e1000_fc_tx_pause)
-                       ecmd->advertising |=  ADVERTISED_Asym_Pause;
-               else
-                       ecmd->advertising &= ~(ADVERTISED_Pause |
-                                              ADVERTISED_Asym_Pause);
-
                ecmd->port = PORT_TP;
                ecmd->phy_address = hw->phy.addr;
                ecmd->transceiver = XCVR_INTERNAL;
        } else {
-               ecmd->supported = (SUPPORTED_1000baseT_Full |
-                                  SUPPORTED_100baseT_Full |
-                                  SUPPORTED_FIBRE |
+               ecmd->supported = (SUPPORTED_FIBRE |
                                   SUPPORTED_Autoneg |
                                   SUPPORTED_Pause);
-               if (hw->mac.type == e1000_i354)
-                               ecmd->supported |= SUPPORTED_2500baseX_Full;
-
                ecmd->advertising = ADVERTISED_FIBRE;
-
-               switch (adapter->link_speed) {
-               case SPEED_2500:
-                       ecmd->advertising = ADVERTISED_2500baseX_Full;
-                       break;
-               case SPEED_1000:
-                       ecmd->advertising = ADVERTISED_1000baseT_Full;
-                       break;
-               case SPEED_100:
-                       ecmd->advertising = ADVERTISED_100baseT_Full;
-                       break;
-               default:
-                       break;
+               if (hw->mac.type == e1000_i354) {
+                       ecmd->supported |= SUPPORTED_2500baseX_Full;
+                       ecmd->advertising |= ADVERTISED_2500baseX_Full;
+               }
+               if ((eth_flags->e1000_base_lx) || (eth_flags->e1000_base_sx)) {
+                       ecmd->supported |= SUPPORTED_1000baseT_Full;
+                       ecmd->advertising |= ADVERTISED_1000baseT_Full;
+               }
+               if (eth_flags->e100_base_fx) {
+                       ecmd->supported |= SUPPORTED_100baseT_Full;
+                       ecmd->advertising |= ADVERTISED_100baseT_Full;
                }
-
                if (hw->mac.autoneg == 1)
                        ecmd->advertising |= ADVERTISED_Autoneg;
 
@@ -212,6 +191,21 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                ecmd->transceiver = XCVR_EXTERNAL;
        }
 
+       if (hw->mac.autoneg != 1)
+               ecmd->advertising &= ~(ADVERTISED_Pause |
+                                      ADVERTISED_Asym_Pause);
+
+       if (hw->fc.requested_mode == e1000_fc_full)
+               ecmd->advertising |= ADVERTISED_Pause;
+       else if (hw->fc.requested_mode == e1000_fc_rx_pause)
+               ecmd->advertising |= (ADVERTISED_Pause |
+                                     ADVERTISED_Asym_Pause);
+       else if (hw->fc.requested_mode == e1000_fc_tx_pause)
+               ecmd->advertising |=  ADVERTISED_Asym_Pause;
+       else
+               ecmd->advertising &= ~(ADVERTISED_Pause |
+                                      ADVERTISED_Asym_Pause);
+
        status = rd32(E1000_STATUS);
 
        if (status & E1000_STATUS_LU) {
@@ -392,6 +386,10 @@ static int igb_set_pauseparam(struct net_device *netdev,
        struct e1000_hw *hw = &adapter->hw;
        int retval = 0;
 
+       /* 100basefx does not support setting link flow control */
+       if (hw->dev_spec._82575.eth_flags.e100_base_fx)
+               return -EINVAL;
+
        adapter->fc_autoneg = pause->autoneg;
 
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
@@ -813,10 +811,8 @@ static int igb_set_eeprom(struct net_device *netdev,
        ret_val = hw->nvm.ops.write(hw, first_word,
                                    last_word - first_word + 1, eeprom_buff);
 
-       /* Update the checksum over the first part of the EEPROM if needed
-        * and flush shadow RAM for 82573 controllers
-        */
-       if ((ret_val == 0) && ((first_word <= NVM_CHECKSUM_REG)))
+       /* Update the checksum if nvm write succeeded */
+       if (ret_val == 0)
                hw->nvm.ops.update(hw);
 
        igb_set_fw_version(adapter);
index 64cbe0d..6a0c1b6 100644 (file)
@@ -1667,10 +1667,13 @@ void igb_down(struct igb_adapter *adapter)
        wrfl();
        msleep(10);
 
-       for (i = 0; i < adapter->num_q_vectors; i++)
+       igb_irq_disable(adapter);
+
+       for (i = 0; i < adapter->num_q_vectors; i++) {
+               napi_synchronize(&(adapter->q_vector[i]->napi));
                napi_disable(&(adapter->q_vector[i]->napi));
+       }
 
-       igb_irq_disable(adapter);
 
        del_timer_sync(&adapter->watchdog_timer);
        del_timer_sync(&adapter->phy_info_timer);
@@ -6622,7 +6625,7 @@ static void igb_process_skb_fields(struct igb_ring *rx_ring,
 
        igb_rx_checksum(rx_ring, rx_desc, skb);
 
-       igb_ptp_rx_hwtstamp(rx_ring->q_vector, rx_desc, skb);
+       igb_ptp_rx_hwtstamp(rx_ring, rx_desc, skb);
 
        if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
            igb_test_staterr(rx_desc, E1000_RXD_STAT_VP)) {
index ca93238..fb098b4 100644 (file)
 #include <linux/dca.h>
 #endif
 
+#include <net/ll_poll.h>
+
+#ifdef CONFIG_NET_LL_RX_POLL
+#define LL_EXTENDED_STATS
+#endif
 /* common prefix used by pr_<> macros */
 #undef pr_fmt
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -182,6 +187,11 @@ struct ixgbe_rx_buffer {
 struct ixgbe_queue_stats {
        u64 packets;
        u64 bytes;
+#ifdef LL_EXTENDED_STATS
+       u64 yields;
+       u64 misses;
+       u64 cleaned;
+#endif  /* LL_EXTENDED_STATS */
 };
 
 struct ixgbe_tx_queue_stats {
@@ -356,9 +366,133 @@ struct ixgbe_q_vector {
        struct rcu_head rcu;    /* to avoid race with update stats on free */
        char name[IFNAMSIZ + 9];
 
+#ifdef CONFIG_NET_LL_RX_POLL
+       unsigned int state;
+#define IXGBE_QV_STATE_IDLE        0
+#define IXGBE_QV_STATE_NAPI       1    /* NAPI owns this QV */
+#define IXGBE_QV_STATE_POLL       2    /* poll owns this QV */
+#define IXGBE_QV_LOCKED (IXGBE_QV_STATE_NAPI | IXGBE_QV_STATE_POLL)
+#define IXGBE_QV_STATE_NAPI_YIELD  4    /* NAPI yielded this QV */
+#define IXGBE_QV_STATE_POLL_YIELD  8    /* poll yielded this QV */
+#define IXGBE_QV_YIELD (IXGBE_QV_STATE_NAPI_YIELD | IXGBE_QV_STATE_POLL_YIELD)
+#define IXGBE_QV_USER_PEND (IXGBE_QV_STATE_POLL | IXGBE_QV_STATE_POLL_YIELD)
+       spinlock_t lock;
+#endif  /* CONFIG_NET_LL_RX_POLL */
+
        /* for dynamic allocation of rings associated with this q_vector */
        struct ixgbe_ring ring[0] ____cacheline_internodealigned_in_smp;
 };
+#ifdef CONFIG_NET_LL_RX_POLL
+static inline void ixgbe_qv_init_lock(struct ixgbe_q_vector *q_vector)
+{
+
+       spin_lock_init(&q_vector->lock);
+       q_vector->state = IXGBE_QV_STATE_IDLE;
+}
+
+/* called from the device poll routine to get ownership of a q_vector */
+static inline bool ixgbe_qv_lock_napi(struct ixgbe_q_vector *q_vector)
+{
+       int rc = true;
+       spin_lock(&q_vector->lock);
+       if (q_vector->state & IXGBE_QV_LOCKED) {
+               WARN_ON(q_vector->state & IXGBE_QV_STATE_NAPI);
+               q_vector->state |= IXGBE_QV_STATE_NAPI_YIELD;
+               rc = false;
+#ifdef LL_EXTENDED_STATS
+               q_vector->tx.ring->stats.yields++;
+#endif
+       } else
+               /* we don't care if someone yielded */
+               q_vector->state = IXGBE_QV_STATE_NAPI;
+       spin_unlock(&q_vector->lock);
+       return rc;
+}
+
+/* returns true is someone tried to get the qv while napi had it */
+static inline bool ixgbe_qv_unlock_napi(struct ixgbe_q_vector *q_vector)
+{
+       int rc = false;
+       spin_lock(&q_vector->lock);
+       WARN_ON(q_vector->state & (IXGBE_QV_STATE_POLL |
+                              IXGBE_QV_STATE_NAPI_YIELD));
+
+       if (q_vector->state & IXGBE_QV_STATE_POLL_YIELD)
+               rc = true;
+       q_vector->state = IXGBE_QV_STATE_IDLE;
+       spin_unlock(&q_vector->lock);
+       return rc;
+}
+
+/* called from ixgbe_low_latency_poll() */
+static inline bool ixgbe_qv_lock_poll(struct ixgbe_q_vector *q_vector)
+{
+       int rc = true;
+       spin_lock_bh(&q_vector->lock);
+       if ((q_vector->state & IXGBE_QV_LOCKED)) {
+               q_vector->state |= IXGBE_QV_STATE_POLL_YIELD;
+               rc = false;
+#ifdef LL_EXTENDED_STATS
+               q_vector->rx.ring->stats.yields++;
+#endif
+       } else
+               /* preserve yield marks */
+               q_vector->state |= IXGBE_QV_STATE_POLL;
+       spin_unlock_bh(&q_vector->lock);
+       return rc;
+}
+
+/* returns true if someone tried to get the qv while it was locked */
+static inline bool ixgbe_qv_unlock_poll(struct ixgbe_q_vector *q_vector)
+{
+       int rc = false;
+       spin_lock_bh(&q_vector->lock);
+       WARN_ON(q_vector->state & (IXGBE_QV_STATE_NAPI));
+
+       if (q_vector->state & IXGBE_QV_STATE_POLL_YIELD)
+               rc = true;
+       q_vector->state = IXGBE_QV_STATE_IDLE;
+       spin_unlock_bh(&q_vector->lock);
+       return rc;
+}
+
+/* true if a socket is polling, even if it did not get the lock */
+static inline bool ixgbe_qv_ll_polling(struct ixgbe_q_vector *q_vector)
+{
+       WARN_ON(!(q_vector->state & IXGBE_QV_LOCKED));
+       return q_vector->state & IXGBE_QV_USER_PEND;
+}
+#else /* CONFIG_NET_LL_RX_POLL */
+static inline void ixgbe_qv_init_lock(struct ixgbe_q_vector *q_vector)
+{
+}
+
+static inline bool ixgbe_qv_lock_napi(struct ixgbe_q_vector *q_vector)
+{
+       return true;
+}
+
+static inline bool ixgbe_qv_unlock_napi(struct ixgbe_q_vector *q_vector)
+{
+       return false;
+}
+
+static inline bool ixgbe_qv_lock_poll(struct ixgbe_q_vector *q_vector)
+{
+       return false;
+}
+
+static inline bool ixgbe_qv_unlock_poll(struct ixgbe_q_vector *q_vector)
+{
+       return false;
+}
+
+static inline bool ixgbe_qv_ll_polling(struct ixgbe_q_vector *q_vector)
+{
+       return false;
+}
+#endif /* CONFIG_NET_LL_RX_POLL */
+
 #ifdef CONFIG_IXGBE_HWMON
 
 #define IXGBE_HWMON_TYPE_LOC           0
index 1f2c805..e055e00 100644 (file)
@@ -380,3 +380,26 @@ s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw,
        }
        return 0;
 }
+
+static void ixgbe_dcb_read_rtrup2tc_82599(struct ixgbe_hw *hw, u8 *map)
+{
+       u32 reg, i;
+
+       reg = IXGBE_READ_REG(hw, IXGBE_RTRUP2TC);
+       for (i = 0; i < MAX_USER_PRIORITY; i++)
+               map[i] = IXGBE_RTRUP2TC_UP_MASK &
+                       (reg >> (i * IXGBE_RTRUP2TC_UP_SHIFT));
+       return;
+}
+
+void ixgbe_dcb_read_rtrup2tc(struct ixgbe_hw *hw, u8 *map)
+{
+       switch (hw->mac.type) {
+       case ixgbe_mac_82599EB:
+       case ixgbe_mac_X540:
+               ixgbe_dcb_read_rtrup2tc_82599(hw, map);
+               break;
+       default:
+               break;
+       }
+}
index 1634de8..fc0a2dd 100644 (file)
@@ -159,6 +159,8 @@ s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, u16 *refill, u16 *max,
 s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en, u8 *tc_prio);
 s32 ixgbe_dcb_hw_config(struct ixgbe_hw *, struct ixgbe_dcb_config *);
 
+void ixgbe_dcb_read_rtrup2tc(struct ixgbe_hw *hw, u8 *map);
+
 /* DCB definitions for credit calculation */
 #define DCB_CREDIT_QUANTUM     64   /* DCB Quantum */
 #define MAX_CREDIT_REFILL       511  /* 0x1FF * 64B = 32704B */
index a4ef076..d71d9ce 100644 (file)
@@ -45,6 +45,7 @@
 
 /* Receive UP2TC mapping */
 #define IXGBE_RTRUP2TC_UP_SHIFT 3
+#define IXGBE_RTRUP2TC_UP_MASK 7
 /* Transmit UP2TC mapping */
 #define IXGBE_RTTUP2TC_UP_SHIFT 3
 
index f3d68f9..edd89a1 100644 (file)
@@ -554,6 +554,9 @@ static int ixgbe_dcbnl_ieee_setets(struct net_device *dev,
                for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
                        adapter->ixgbe_ieee_ets->prio_tc[i] =
                                IEEE_8021QAZ_MAX_TCS;
+               /* if possible update UP2TC mappings from HW */
+               ixgbe_dcb_read_rtrup2tc(&adapter->hw,
+                                       adapter->ixgbe_ieee_ets->prio_tc);
        }
 
        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
index d375472..24e2e7a 100644 (file)
@@ -1054,6 +1054,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
                        data[i] = 0;
                        data[i+1] = 0;
                        i += 2;
+#ifdef LL_EXTENDED_STATS
+                       data[i] = 0;
+                       data[i+1] = 0;
+                       data[i+2] = 0;
+                       i += 3;
+#endif
                        continue;
                }
 
@@ -1063,6 +1069,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
                        data[i+1] = ring->stats.bytes;
                } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
                i += 2;
+#ifdef LL_EXTENDED_STATS
+               data[i] = ring->stats.yields;
+               data[i+1] = ring->stats.misses;
+               data[i+2] = ring->stats.cleaned;
+               i += 3;
+#endif
        }
        for (j = 0; j < IXGBE_NUM_RX_QUEUES; j++) {
                ring = adapter->rx_ring[j];
@@ -1070,6 +1082,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
                        data[i] = 0;
                        data[i+1] = 0;
                        i += 2;
+#ifdef LL_EXTENDED_STATS
+                       data[i] = 0;
+                       data[i+1] = 0;
+                       data[i+2] = 0;
+                       i += 3;
+#endif
                        continue;
                }
 
@@ -1079,6 +1097,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
                        data[i+1] = ring->stats.bytes;
                } while (u64_stats_fetch_retry_bh(&ring->syncp, start));
                i += 2;
+#ifdef LL_EXTENDED_STATS
+               data[i] = ring->stats.yields;
+               data[i+1] = ring->stats.misses;
+               data[i+2] = ring->stats.cleaned;
+               i += 3;
+#endif
        }
 
        for (j = 0; j < IXGBE_MAX_PACKET_BUFFERS; j++) {
@@ -1115,12 +1139,28 @@ static void ixgbe_get_strings(struct net_device *netdev, u32 stringset,
                        p += ETH_GSTRING_LEN;
                        sprintf(p, "tx_queue_%u_bytes", i);
                        p += ETH_GSTRING_LEN;
+#ifdef LL_EXTENDED_STATS
+                       sprintf(p, "tx_q_%u_napi_yield", i);
+                       p += ETH_GSTRING_LEN;
+                       sprintf(p, "tx_q_%u_misses", i);
+                       p += ETH_GSTRING_LEN;
+                       sprintf(p, "tx_q_%u_cleaned", i);
+                       p += ETH_GSTRING_LEN;
+#endif /* LL_EXTENDED_STATS */
                }
                for (i = 0; i < IXGBE_NUM_RX_QUEUES; i++) {
                        sprintf(p, "rx_queue_%u_packets", i);
                        p += ETH_GSTRING_LEN;
                        sprintf(p, "rx_queue_%u_bytes", i);
                        p += ETH_GSTRING_LEN;
+#ifdef LL_EXTENDED_STATS
+                       sprintf(p, "rx_q_%u_ll_poll_yield", i);
+                       p += ETH_GSTRING_LEN;
+                       sprintf(p, "rx_q_%u_misses", i);
+                       p += ETH_GSTRING_LEN;
+                       sprintf(p, "rx_q_%u_cleaned", i);
+                       p += ETH_GSTRING_LEN;
+#endif /* LL_EXTENDED_STATS */
                }
                for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) {
                        sprintf(p, "tx_pb_%u_pxon", i);
index ef5f7a6..90b4e10 100644 (file)
@@ -811,6 +811,7 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter,
        /* initialize NAPI */
        netif_napi_add(adapter->netdev, &q_vector->napi,
                       ixgbe_poll, 64);
+       napi_hash_add(&q_vector->napi);
 
        /* tie q_vector and adapter together */
        adapter->q_vector[v_idx] = q_vector;
@@ -931,6 +932,7 @@ static void ixgbe_free_q_vector(struct ixgbe_adapter *adapter, int v_idx)
                adapter->rx_ring[ring->queue_index] = NULL;
 
        adapter->q_vector[v_idx] = NULL;
+       napi_hash_del(&q_vector->napi);
        netif_napi_del(&q_vector->napi);
 
        /*
index d30fbdd..047ebaa 100644 (file)
@@ -1504,7 +1504,9 @@ static void ixgbe_rx_skb(struct ixgbe_q_vector *q_vector,
 {
        struct ixgbe_adapter *adapter = q_vector->adapter;
 
-       if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL))
+       if (ixgbe_qv_ll_polling(q_vector))
+               netif_receive_skb(skb);
+       else if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL))
                napi_gro_receive(&q_vector->napi, skb);
        else
                netif_rx(skb);
@@ -1892,9 +1894,9 @@ dma_sync:
  * expensive overhead for IOMMU access this provides a means of avoiding
  * it by maintaining the mapping of the page to the syste.
  *
- * Returns true if all work is completed without reaching budget
+ * Returns amount of work completed
  **/
-static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
+static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
                               struct ixgbe_ring *rx_ring,
                               const int budget)
 {
@@ -1976,6 +1978,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
                }
 
 #endif /* IXGBE_FCOE */
+               skb_mark_ll(skb, &q_vector->napi);
                ixgbe_rx_skb(q_vector, skb);
 
                /* update budget accounting */
@@ -1992,9 +1995,43 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
        if (cleaned_count)
                ixgbe_alloc_rx_buffers(rx_ring, cleaned_count);
 
-       return (total_rx_packets < budget);
+       return total_rx_packets;
 }
 
+#ifdef CONFIG_NET_LL_RX_POLL
+/* must be called with local_bh_disable()d */
+static int ixgbe_low_latency_recv(struct napi_struct *napi)
+{
+       struct ixgbe_q_vector *q_vector =
+                       container_of(napi, struct ixgbe_q_vector, napi);
+       struct ixgbe_adapter *adapter = q_vector->adapter;
+       struct ixgbe_ring  *ring;
+       int found = 0;
+
+       if (test_bit(__IXGBE_DOWN, &adapter->state))
+               return LL_FLUSH_FAILED;
+
+       if (!ixgbe_qv_lock_poll(q_vector))
+               return LL_FLUSH_BUSY;
+
+       ixgbe_for_each_ring(ring, q_vector->rx) {
+               found = ixgbe_clean_rx_irq(q_vector, ring, 4);
+#ifdef LL_EXTENDED_STATS
+               if (found)
+                       ring->stats.cleaned += found;
+               else
+                       ring->stats.misses++;
+#endif
+               if (found)
+                       break;
+       }
+
+       ixgbe_qv_unlock_poll(q_vector);
+
+       return found;
+}
+#endif /* CONFIG_NET_LL_RX_POLL */
+
 /**
  * ixgbe_configure_msix - Configure MSI-X hardware
  * @adapter: board private structure
@@ -2550,6 +2587,9 @@ int ixgbe_poll(struct napi_struct *napi, int budget)
        ixgbe_for_each_ring(ring, q_vector->tx)
                clean_complete &= !!ixgbe_clean_tx_irq(q_vector, ring);
 
+       if (!ixgbe_qv_lock_napi(q_vector))
+               return budget;
+
        /* attempt to distribute budget to each queue fairly, but don't allow
         * the budget to go below 1 because we'll exit polling */
        if (q_vector->rx.count > 1)
@@ -2558,9 +2598,10 @@ int ixgbe_poll(struct napi_struct *napi, int budget)
                per_ring_budget = budget;
 
        ixgbe_for_each_ring(ring, q_vector->rx)
-               clean_complete &= ixgbe_clean_rx_irq(q_vector, ring,
-                                                    per_ring_budget);
+               clean_complete &= (ixgbe_clean_rx_irq(q_vector, ring,
+                                  per_ring_budget) < per_ring_budget);
 
+       ixgbe_qv_unlock_napi(q_vector);
        /* If all work not completed, return budget and keep polling */
        if (!clean_complete)
                return budget;
@@ -3747,16 +3788,25 @@ static void ixgbe_napi_enable_all(struct ixgbe_adapter *adapter)
 {
        int q_idx;
 
-       for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++)
+       for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) {
+               ixgbe_qv_init_lock(adapter->q_vector[q_idx]);
                napi_enable(&adapter->q_vector[q_idx]->napi);
+       }
 }
 
 static void ixgbe_napi_disable_all(struct ixgbe_adapter *adapter)
 {
        int q_idx;
 
-       for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++)
+       local_bh_disable(); /* for ixgbe_qv_lock_napi() */
+       for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) {
                napi_disable(&adapter->q_vector[q_idx]->napi);
+               while (!ixgbe_qv_lock_napi(adapter->q_vector[q_idx])) {
+                       pr_info("QV %d locked\n", q_idx);
+                       mdelay(1);
+               }
+       }
+       local_bh_enable();
 }
 
 #ifdef CONFIG_IXGBE_DCB
@@ -7177,6 +7227,9 @@ static const struct net_device_ops ixgbe_netdev_ops = {
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = ixgbe_netpoll,
 #endif
+#ifdef CONFIG_NET_LL_RX_POLL
+       .ndo_ll_poll            = ixgbe_low_latency_recv,
+#endif
 #ifdef IXGBE_FCOE
        .ndo_fcoe_ddp_setup = ixgbe_fcoe_ddp_get,
        .ndo_fcoe_ddp_target = ixgbe_fcoe_ddp_target,
index 070a6f1..7fbe6ab 100644 (file)
@@ -3148,7 +3148,6 @@ jme_init_one(struct pci_dev *pdev,
        jme->mii_if.mdio_write = jme_mdio_write;
 
        jme_clear_pm(jme);
-       pci_set_power_state(jme->pdev, PCI_D0);
        device_set_wakeup_enable(&pdev->dev, true);
 
        jme_set_phyfifo_5level(jme);
index 5409fe8..270e65f 100644 (file)
@@ -483,7 +483,6 @@ static void korina_multicast_list(struct net_device *dev)
        unsigned long flags;
        struct netdev_hw_addr *ha;
        u32 recognise = ETH_ARC_AB;     /* always accept broadcasts */
-       int i;
 
        /* Set promiscuous mode */
        if (dev->flags & IFF_PROMISC)
@@ -495,12 +494,9 @@ static void korina_multicast_list(struct net_device *dev)
 
        /* Build the hash table */
        if (netdev_mc_count(dev) > 4) {
-               u16 hash_table[4];
+               u16 hash_table[4] = { 0 };
                u32 crc;
 
-               for (i = 0; i < 4; i++)
-                       hash_table[i] = 0;
-
                netdev_for_each_mc_addr(ha, dev) {
                        crc = ether_crc_le(6, ha->addr);
                        crc >>= 26;
@@ -1214,7 +1210,6 @@ static int korina_remove(struct platform_device *pdev)
        iounmap(lp->rx_dma_regs);
        iounmap(lp->tx_dma_regs);
 
-       platform_set_drvdata(pdev, NULL);
        unregister_netdev(bif->dev);
        free_netdev(bif->dev);
 
index d1cbfb1..c35db73 100644 (file)
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
 
 static char mv643xx_eth_driver_name[] = "mv643xx_eth";
 static char mv643xx_eth_driver_version[] = "1.4";
@@ -115,6 +119,8 @@ static char mv643xx_eth_driver_version[] = "1.4";
 #define  LINK_UP                       0x00000002
 #define TXQ_COMMAND                    0x0048
 #define TXQ_FIX_PRIO_CONF              0x004c
+#define PORT_SERIAL_CONTROL1           0x004c
+#define  CLK125_BYPASS_EN              0x00000010
 #define TX_BW_RATE                     0x0050
 #define TX_BW_MTU                      0x0058
 #define TX_BW_BURST                    0x005c
@@ -615,7 +621,7 @@ static int rxq_refill(struct rx_queue *rxq, int budget)
 
                rx_desc = rxq->rx_desc_area + rx;
 
-               size = skb->end - skb->data;
+               size = skb_end_pointer(skb) - skb->data;
                rx_desc->buf_ptr = dma_map_single(mp->dev->dev.parent,
                                                  skb->data, size,
                                                  DMA_FROM_DEVICE);
@@ -2450,13 +2456,159 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp)
        }
 }
 
+#if defined(CONFIG_OF)
+static const struct of_device_id mv643xx_eth_shared_ids[] = {
+       { .compatible = "marvell,orion-eth", },
+       { .compatible = "marvell,kirkwood-eth", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, mv643xx_eth_shared_ids);
+#endif
+
+#if defined(CONFIG_OF) && !defined(CONFIG_MV64X60)
+#define mv643xx_eth_property(_np, _name, _v)                           \
+       do {                                                            \
+               u32 tmp;                                                \
+               if (!of_property_read_u32(_np, "marvell," _name, &tmp)) \
+                       _v = tmp;                                       \
+       } while (0)
+
+static struct platform_device *port_platdev[3];
+
+static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
+                                         struct device_node *pnp)
+{
+       struct platform_device *ppdev;
+       struct mv643xx_eth_platform_data ppd;
+       struct resource res;
+       const char *mac_addr;
+       int ret;
+       int dev_num = 0;
+
+       memset(&ppd, 0, sizeof(ppd));
+       ppd.shared = pdev;
+
+       memset(&res, 0, sizeof(res));
+       if (!of_irq_to_resource(pnp, 0, &res)) {
+               dev_err(&pdev->dev, "missing interrupt on %s\n", pnp->name);
+               return -EINVAL;
+       }
+
+       if (of_property_read_u32(pnp, "reg", &ppd.port_number)) {
+               dev_err(&pdev->dev, "missing reg property on %s\n", pnp->name);
+               return -EINVAL;
+       }
+
+       if (ppd.port_number >= 3) {
+               dev_err(&pdev->dev, "invalid reg property on %s\n", pnp->name);
+               return -EINVAL;
+       }
+
+       while (dev_num < 3 && port_platdev[dev_num])
+               dev_num++;
+
+       if (dev_num == 3) {
+               dev_err(&pdev->dev, "too many ports registered\n");
+               return -EINVAL;
+       }
+
+       mac_addr = of_get_mac_address(pnp);
+       if (mac_addr)
+               memcpy(ppd.mac_addr, mac_addr, 6);
+
+       mv643xx_eth_property(pnp, "tx-queue-size", ppd.tx_queue_size);
+       mv643xx_eth_property(pnp, "tx-sram-addr", ppd.tx_sram_addr);
+       mv643xx_eth_property(pnp, "tx-sram-size", ppd.tx_sram_size);
+       mv643xx_eth_property(pnp, "rx-queue-size", ppd.rx_queue_size);
+       mv643xx_eth_property(pnp, "rx-sram-addr", ppd.rx_sram_addr);
+       mv643xx_eth_property(pnp, "rx-sram-size", ppd.rx_sram_size);
+
+       ppd.phy_node = of_parse_phandle(pnp, "phy-handle", 0);
+       if (!ppd.phy_node) {
+               ppd.phy_addr = MV643XX_ETH_PHY_NONE;
+               of_property_read_u32(pnp, "speed", &ppd.speed);
+               of_property_read_u32(pnp, "duplex", &ppd.duplex);
+       }
+
+       ppdev = platform_device_alloc(MV643XX_ETH_NAME, dev_num);
+       if (!ppdev)
+               return -ENOMEM;
+       ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+       ret = platform_device_add_resources(ppdev, &res, 1);
+       if (ret)
+               goto port_err;
+
+       ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd));
+       if (ret)
+               goto port_err;
+
+       ret = platform_device_add(ppdev);
+       if (ret)
+               goto port_err;
+
+       port_platdev[dev_num] = ppdev;
+
+       return 0;
+
+port_err:
+       platform_device_put(ppdev);
+       return ret;
+}
+
+static int mv643xx_eth_shared_of_probe(struct platform_device *pdev)
+{
+       struct mv643xx_eth_shared_platform_data *pd;
+       struct device_node *pnp, *np = pdev->dev.of_node;
+       int ret;
+
+       /* bail out if not registered from DT */
+       if (!np)
+               return 0;
+
+       pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
+       if (!pd)
+               return -ENOMEM;
+       pdev->dev.platform_data = pd;
+
+       mv643xx_eth_property(np, "tx-checksum-limit", pd->tx_csum_limit);
+
+       for_each_available_child_of_node(np, pnp) {
+               ret = mv643xx_eth_shared_of_add_port(pdev, pnp);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+static void mv643xx_eth_shared_of_remove(void)
+{
+       int n;
+
+       for (n = 0; n < 3; n++) {
+               platform_device_del(port_platdev[n]);
+               port_platdev[n] = NULL;
+       }
+}
+#else
+static inline int mv643xx_eth_shared_of_probe(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static inline void mv643xx_eth_shared_of_remove(void)
+{
+}
+#endif
+
 static int mv643xx_eth_shared_probe(struct platform_device *pdev)
 {
        static int mv643xx_eth_version_printed;
-       struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data;
+       struct mv643xx_eth_shared_platform_data *pd;
        struct mv643xx_eth_shared_private *msp;
        const struct mbus_dram_target_info *dram;
        struct resource *res;
+       int ret;
 
        if (!mv643xx_eth_version_printed++)
                pr_notice("MV-643xx 10/100/1000 ethernet driver version %s\n",
@@ -2469,8 +2621,9 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
        msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL);
        if (msp == NULL)
                return -ENOMEM;
+       platform_set_drvdata(pdev, msp);
 
-       msp->base = ioremap(res->start, resource_size(res));
+       msp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
        if (msp->base == NULL)
                return -ENOMEM;
 
@@ -2485,12 +2638,15 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
        if (dram)
                mv643xx_eth_conf_mbus_windows(msp, dram);
 
+       ret = mv643xx_eth_shared_of_probe(pdev);
+       if (ret)
+               return ret;
+       pd = pdev->dev.platform_data;
+
        msp->tx_csum_limit = (pd != NULL && pd->tx_csum_limit) ?
                                        pd->tx_csum_limit : 9 * 1024;
        infer_hw_params(msp);
 
-       platform_set_drvdata(pdev, msp);
-
        return 0;
 }
 
@@ -2498,10 +2654,9 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev)
 {
        struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev);
 
-       iounmap(msp->base);
+       mv643xx_eth_shared_of_remove();
        if (!IS_ERR(msp->clk))
                clk_disable_unprepare(msp->clk);
-
        return 0;
 }
 
@@ -2511,6 +2666,7 @@ static struct platform_driver mv643xx_eth_shared_driver = {
        .driver = {
                .name   = MV643XX_ETH_SHARED_NAME,
                .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(mv643xx_eth_shared_ids),
        },
 };
 
@@ -2701,6 +2857,15 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
 
        mp->dev = dev;
 
+       /* Kirkwood resets some registers on gated clocks. Especially
+        * CLK125_BYPASS_EN must be cleared but is not available on
+        * all other SoCs/System Controllers using this driver.
+        */
+       if (of_device_is_compatible(pdev->dev.of_node,
+                                   "marvell,kirkwood-eth-port"))
+               wrlp(mp, PORT_SERIAL_CONTROL1,
+                    rdlp(mp, PORT_SERIAL_CONTROL1) & ~CLK125_BYPASS_EN);
+
        /*
         * Start with a default rate, and if there is a clock, allow
         * it to override the default.
@@ -2710,23 +2875,35 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        if (!IS_ERR(mp->clk)) {
                clk_prepare_enable(mp->clk);
                mp->t_clk = clk_get_rate(mp->clk);
+       } else if (!IS_ERR(mp->shared->clk)) {
+               mp->t_clk = clk_get_rate(mp->shared->clk);
        }
 
        set_params(mp, pd);
        netif_set_real_num_tx_queues(dev, mp->txq_count);
        netif_set_real_num_rx_queues(dev, mp->rxq_count);
 
-       if (pd->phy_addr != MV643XX_ETH_PHY_NONE) {
+       err = 0;
+       if (pd->phy_node) {
+               mp->phy = of_phy_connect(mp->dev, pd->phy_node,
+                                        mv643xx_eth_adjust_link, 0,
+                                        PHY_INTERFACE_MODE_GMII);
+               if (!mp->phy)
+                       err = -ENODEV;
+       } else if (pd->phy_addr != MV643XX_ETH_PHY_NONE) {
                mp->phy = phy_scan(mp, pd->phy_addr);
 
-               if (IS_ERR(mp->phy)) {
+               if (IS_ERR(mp->phy))
                        err = PTR_ERR(mp->phy);
-                       if (err == -ENODEV)
-                               err = -EPROBE_DEFER;
-                       goto out;
-               }
-               phy_init(mp, pd->speed, pd->duplex);
+               else
+                       phy_init(mp, pd->speed, pd->duplex);
        }
+       if (err == -ENODEV) {
+               err = -EPROBE_DEFER;
+               goto out;
+       }
+       if (err)
+               goto out;
 
        SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops);
 
@@ -2805,7 +2982,7 @@ static int mv643xx_eth_remove(struct platform_device *pdev)
 
        unregister_netdev(mp->dev);
        if (mp->phy != NULL)
-               phy_detach(mp->phy);
+               phy_disconnect(mp->phy);
        cancel_work_sync(&mp->tx_timeout_task);
 
        if (!IS_ERR(mp->clk))
@@ -2813,8 +2990,6 @@ static int mv643xx_eth_remove(struct platform_device *pdev)
 
        free_netdev(mp->dev);
 
-       platform_set_drvdata(pdev, NULL);
-
        return 0;
 }
 
index c966785..712779f 100644 (file)
@@ -2251,6 +2251,21 @@ static int mvneta_change_mtu(struct net_device *dev, int mtu)
        return 0;
 }
 
+/* Get mac address */
+static void mvneta_get_mac_addr(struct mvneta_port *pp, unsigned char *addr)
+{
+       u32 mac_addr_l, mac_addr_h;
+
+       mac_addr_l = mvreg_read(pp, MVNETA_MAC_ADDR_LOW);
+       mac_addr_h = mvreg_read(pp, MVNETA_MAC_ADDR_HIGH);
+       addr[0] = (mac_addr_h >> 24) & 0xFF;
+       addr[1] = (mac_addr_h >> 16) & 0xFF;
+       addr[2] = (mac_addr_h >> 8) & 0xFF;
+       addr[3] = mac_addr_h & 0xFF;
+       addr[4] = (mac_addr_l >> 8) & 0xFF;
+       addr[5] = mac_addr_l & 0xFF;
+}
+
 /* Handle setting mac address */
 static int mvneta_set_mac_addr(struct net_device *dev, void *addr)
 {
@@ -2667,7 +2682,9 @@ static int mvneta_probe(struct platform_device *pdev)
        u32 phy_addr;
        struct mvneta_port *pp;
        struct net_device *dev;
-       const char *mac_addr;
+       const char *dt_mac_addr;
+       char hw_mac_addr[ETH_ALEN];
+       const char *mac_from;
        int phy_mode;
        int err;
 
@@ -2703,13 +2720,6 @@ static int mvneta_probe(struct platform_device *pdev)
                goto err_free_irq;
        }
 
-       mac_addr = of_get_mac_address(dn);
-
-       if (!mac_addr || !is_valid_ether_addr(mac_addr))
-               eth_hw_addr_random(dev);
-       else
-               memcpy(dev->dev_addr, mac_addr, ETH_ALEN);
-
        dev->tx_queue_len = MVNETA_MAX_TXD;
        dev->watchdog_timeo = 5 * HZ;
        dev->netdev_ops = &mvneta_netdev_ops;
@@ -2740,6 +2750,21 @@ static int mvneta_probe(struct platform_device *pdev)
 
        clk_prepare_enable(pp->clk);
 
+       dt_mac_addr = of_get_mac_address(dn);
+       if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) {
+               mac_from = "device tree";
+               memcpy(dev->dev_addr, dt_mac_addr, ETH_ALEN);
+       } else {
+               mvneta_get_mac_addr(pp, hw_mac_addr);
+               if (is_valid_ether_addr(hw_mac_addr)) {
+                       mac_from = "hardware";
+                       memcpy(dev->dev_addr, hw_mac_addr, ETH_ALEN);
+               } else {
+                       mac_from = "random";
+                       eth_hw_addr_random(dev);
+               }
+       }
+
        pp->tx_done_timer.data = (unsigned long)dev;
 
        pp->tx_ring_size = MVNETA_MAX_TXD;
@@ -2772,7 +2797,8 @@ static int mvneta_probe(struct platform_device *pdev)
                goto err_deinit;
        }
 
-       netdev_info(dev, "mac: %pM\n", dev->dev_addr);
+       netdev_info(dev, "Using %s mac address %pM\n", mac_from,
+                   dev->dev_addr);
 
        platform_set_drvdata(pdev, pp->dev);
 
@@ -2804,8 +2830,6 @@ static int mvneta_remove(struct platform_device *pdev)
        irq_dispose_mapping(dev->irq);
        free_netdev(dev);
 
-       platform_set_drvdata(pdev, NULL);
-
        return 0;
 }
 
index 1c8af8b..db48147 100644 (file)
@@ -357,7 +357,7 @@ static void rxq_refill(struct net_device *dev)
                /* Get 'used' Rx descriptor */
                used_rx_desc = pep->rx_used_desc_q;
                p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
-               size = skb->end - skb->data;
+               size = skb_end_pointer(skb) - skb->data;
                p_used_rx_desc->buf_ptr = dma_map_single(NULL,
                                                         skb->data,
                                                         size,
@@ -1602,7 +1602,6 @@ static int pxa168_eth_remove(struct platform_device *pdev)
        unregister_netdev(dev);
        cancel_work_sync(&pep->tx_timeout_task);
        free_netdev(dev);
-       platform_set_drvdata(pdev, NULL);
        return 0;
 }
 
index 171f4b3..c896079 100644 (file)
@@ -3706,7 +3706,7 @@ static const struct file_operations skge_debug_fops = {
 static int skge_device_event(struct notifier_block *unused,
                             unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct skge_port *skge;
        struct dentry *d;
 
index d175bbd..e09a8c6 100644 (file)
@@ -4642,7 +4642,7 @@ static const struct file_operations sky2_debug_fops = {
 static int sky2_device_event(struct notifier_block *unused,
                             unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct sky2_port *sky2 = netdev_priv(dev);
 
        if (dev->netdev_ops->ndo_open != sky2_open || !sky2_debug)
index 0e572a5..299d018 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/errno.h>
 
 #include <linux/mlx4/cmd.h>
+#include <linux/mlx4/device.h>
 #include <linux/semaphore.h>
 #include <rdma/ib_smi.h>
 
@@ -111,6 +112,14 @@ enum {
        GO_BIT_TIMEOUT_MSECS    = 10000
 };
 
+enum mlx4_vlan_transition {
+       MLX4_VLAN_TRANSITION_VST_VST = 0,
+       MLX4_VLAN_TRANSITION_VST_VGT = 1,
+       MLX4_VLAN_TRANSITION_VGT_VST = 2,
+       MLX4_VLAN_TRANSITION_VGT_VGT = 3,
+};
+
+
 struct mlx4_cmd_context {
        struct completion       done;
        int                     result;
@@ -256,6 +265,8 @@ static int mlx4_comm_cmd_wait(struct mlx4_dev *dev, u8 op,
 
        if (!wait_for_completion_timeout(&context->done,
                                         msecs_to_jiffies(timeout))) {
+               mlx4_warn(dev, "communication channel command 0x%x timed out\n",
+                         op);
                err = -EBUSY;
                goto out;
        }
@@ -485,6 +496,8 @@ static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
        }
 
        if (cmd_pending(dev)) {
+               mlx4_warn(dev, "command 0x%x timed out (go bit not cleared)\n",
+                         op);
                err = -ETIMEDOUT;
                goto out;
        }
@@ -548,6 +561,8 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
 
        if (!wait_for_completion_timeout(&context->done,
                                         msecs_to_jiffies(timeout))) {
+               mlx4_warn(dev, "command 0x%x timed out (go bit not cleared)\n",
+                         op);
                err = -EBUSY;
                goto out;
        }
@@ -785,6 +800,15 @@ static int mlx4_MAD_IFC_wrapper(struct mlx4_dev *dev, int slave,
                                    vhcr->op, MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE);
 }
 
+int MLX4_CMD_UPDATE_QP_wrapper(struct mlx4_dev *dev, int slave,
+                    struct mlx4_vhcr *vhcr,
+                    struct mlx4_cmd_mailbox *inbox,
+                    struct mlx4_cmd_mailbox *outbox,
+                    struct mlx4_cmd_info *cmd)
+{
+       return -EPERM;
+}
+
 int mlx4_DMA_wrapper(struct mlx4_dev *dev, int slave,
                     struct mlx4_vhcr *vhcr,
                     struct mlx4_cmd_mailbox *inbox,
@@ -1218,6 +1242,15 @@ static struct mlx4_cmd_info cmd_info[] = {
                .verify = NULL,
                .wrapper = mlx4_GEN_QP_wrapper
        },
+       {
+               .opcode = MLX4_CMD_UPDATE_QP,
+               .has_inbox = false,
+               .has_outbox = false,
+               .out_is_imm = false,
+               .encode_slave_id = false,
+               .verify = NULL,
+               .wrapper = MLX4_CMD_UPDATE_QP_wrapper
+       },
        {
                .opcode = MLX4_CMD_CONF_SPECIAL_QP,
                .has_inbox = false,
@@ -1488,6 +1521,102 @@ out:
        return ret;
 }
 
+static int calculate_transition(u16 oper_vlan, u16 admin_vlan)
+{
+       return (2 * (oper_vlan == MLX4_VGT) + (admin_vlan == MLX4_VGT));
+}
+
+int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
+                                           int slave, int port)
+{
+       struct mlx4_vport_oper_state *vp_oper;
+       struct mlx4_vport_state *vp_admin;
+       struct mlx4_vf_immed_vlan_work *work;
+       struct mlx4_dev *dev = &(priv->dev);
+       int err;
+       int admin_vlan_ix = NO_INDX;
+       enum mlx4_vlan_transition vlan_trans;
+
+       vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port];
+       vp_admin = &priv->mfunc.master.vf_admin[slave].vport[port];
+
+       if (vp_oper->state.default_vlan == vp_admin->default_vlan &&
+           vp_oper->state.default_qos == vp_admin->default_qos &&
+           vp_oper->state.link_state == vp_admin->link_state)
+               return 0;
+
+       vlan_trans = calculate_transition(vp_oper->state.default_vlan,
+                                         vp_admin->default_vlan);
+
+       if (!(priv->mfunc.master.slave_state[slave].active &&
+             dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP &&
+             vlan_trans == MLX4_VLAN_TRANSITION_VST_VST)) {
+               /* even if the UPDATE_QP command isn't supported, we still want
+                * to set this VF link according to the admin directive
+                */
+               vp_oper->state.link_state = vp_admin->link_state;
+               return -1;
+       }
+
+       mlx4_dbg(dev, "updating immediately admin params slave %d port %d\n",
+                slave, port);
+       mlx4_dbg(dev, "vlan %d QoS %d link down %d\n", vp_admin->default_vlan,
+                vp_admin->default_qos, vp_admin->link_state);
+
+       work = kzalloc(sizeof(*work), GFP_KERNEL);
+       if (!work)
+               return -ENOMEM;
+
+       if (vp_oper->state.default_vlan != vp_admin->default_vlan) {
+               err = __mlx4_register_vlan(&priv->dev, port,
+                                          vp_admin->default_vlan,
+                                          &admin_vlan_ix);
+               if (err) {
+                       kfree(work);
+                       mlx4_warn((&priv->dev),
+                                 "No vlan resources slave %d, port %d\n",
+                                 slave, port);
+                       return err;
+               }
+               work->flags |= MLX4_VF_IMMED_VLAN_FLAG_VLAN;
+               mlx4_dbg((&(priv->dev)),
+                        "alloc vlan %d idx  %d slave %d port %d\n",
+                        (int)(vp_admin->default_vlan),
+                        admin_vlan_ix, slave, port);
+       }
+
+       /* save original vlan ix and vlan id */
+       work->orig_vlan_id = vp_oper->state.default_vlan;
+       work->orig_vlan_ix = vp_oper->vlan_idx;
+
+       /* handle new qos */
+       if (vp_oper->state.default_qos != vp_admin->default_qos)
+               work->flags |= MLX4_VF_IMMED_VLAN_FLAG_QOS;
+
+       if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_VLAN)
+               vp_oper->vlan_idx = admin_vlan_ix;
+
+       vp_oper->state.default_vlan = vp_admin->default_vlan;
+       vp_oper->state.default_qos = vp_admin->default_qos;
+       vp_oper->state.link_state = vp_admin->link_state;
+
+       if (vp_admin->link_state == IFLA_VF_LINK_STATE_DISABLE)
+               work->flags |= MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE;
+
+       /* iterate over QPs owned by this slave, using UPDATE_QP */
+       work->port = port;
+       work->slave = slave;
+       work->qos = vp_oper->state.default_qos;
+       work->vlan_id = vp_oper->state.default_vlan;
+       work->vlan_ix = vp_oper->vlan_idx;
+       work->priv = priv;
+       INIT_WORK(&work->work, mlx4_vf_immed_vlan_work_handler);
+       queue_work(priv->mfunc.master.comm_wq, &work->work);
+
+       return 0;
+}
+
+
 static int mlx4_master_activate_admin_state(struct mlx4_priv *priv, int slave)
 {
        int port, err;
@@ -2102,10 +2231,12 @@ int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac)
 }
 EXPORT_SYMBOL_GPL(mlx4_set_vf_mac);
 
+
 int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos)
 {
        struct mlx4_priv *priv = mlx4_priv(dev);
-       struct mlx4_vport_state *s_info;
+       struct mlx4_vport_oper_state *vf_oper;
+       struct mlx4_vport_state *vf_admin;
        int slave;
 
        if ((!mlx4_is_master(dev)) ||
@@ -2119,12 +2250,19 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos)
        if (slave < 0)
                return -EINVAL;
 
-       s_info = &priv->mfunc.master.vf_admin[slave].vport[port];
+       vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port];
+       vf_oper = &priv->mfunc.master.vf_oper[slave].vport[port];
+
        if ((0 == vlan) && (0 == qos))
-               s_info->default_vlan = MLX4_VGT;
+               vf_admin->default_vlan = MLX4_VGT;
        else
-               s_info->default_vlan = vlan;
-       s_info->default_qos = qos;
+               vf_admin->default_vlan = vlan;
+       vf_admin->default_qos = qos;
+
+       if (mlx4_master_immediate_activate_vlan_qos(priv, slave, port))
+               mlx4_info(dev,
+                         "updating vf %d port %d config will take effect on next VF restart\n",
+                         vf, port);
        return 0;
 }
 EXPORT_SYMBOL_GPL(mlx4_set_vf_vlan);
@@ -2178,7 +2316,55 @@ int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_in
        ivf->qos        = s_info->default_qos;
        ivf->tx_rate    = s_info->tx_rate;
        ivf->spoofchk   = s_info->spoofchk;
+       ivf->linkstate  = s_info->link_state;
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(mlx4_get_vf_config);
+
+int mlx4_set_vf_link_state(struct mlx4_dev *dev, int port, int vf, int link_state)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       struct mlx4_vport_state *s_info;
+       int slave;
+       u8 link_stat_event;
+
+       slave = mlx4_get_slave_indx(dev, vf);
+       if (slave < 0)
+               return -EINVAL;
+
+       switch (link_state) {
+       case IFLA_VF_LINK_STATE_AUTO:
+               /* get current link state */
+               if (!priv->sense.do_sense_port[port])
+                       link_stat_event = MLX4_PORT_CHANGE_SUBTYPE_ACTIVE;
+               else
+                       link_stat_event = MLX4_PORT_CHANGE_SUBTYPE_DOWN;
+           break;
+
+       case IFLA_VF_LINK_STATE_ENABLE:
+               link_stat_event = MLX4_PORT_CHANGE_SUBTYPE_ACTIVE;
+           break;
+
+       case IFLA_VF_LINK_STATE_DISABLE:
+               link_stat_event = MLX4_PORT_CHANGE_SUBTYPE_DOWN;
+           break;
+
+       default:
+               mlx4_warn(dev, "unknown value for link_state %02x on slave %d port %d\n",
+                         link_state, slave, port);
+               return -EINVAL;
+       };
+       s_info = &priv->mfunc.master.vf_admin[slave].vport[port];
+       s_info->link_state = link_state;
+
+       /* send event */
+       mlx4_gen_port_state_change_eqe(dev, slave, port, link_stat_event);
+
+       if (mlx4_master_immediate_activate_vlan_qos(priv, slave, port))
+               mlx4_dbg(dev,
+                        "updating vf %d port %d no link state HW enforcment\n",
+                        vf, port);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mlx4_set_vf_link_state);
index 1e6c594..3e2d504 100644 (file)
@@ -139,6 +139,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
 
        if (!cq->is_tx) {
                netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq, 64);
+               napi_hash_add(&cq->napi);
                napi_enable(&cq->napi);
        }
 
@@ -162,6 +163,8 @@ void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
 {
        if (!cq->is_tx) {
                napi_disable(&cq->napi);
+               napi_hash_del(&cq->napi);
+               synchronize_rcu();
                netif_napi_del(&cq->napi);
        }
 
index 0f91222..9d4a1ea 100644 (file)
@@ -207,9 +207,6 @@ static int mlx4_en_dcbnl_ieee_getmaxrate(struct net_device *dev,
        struct mlx4_en_priv *priv = netdev_priv(dev);
        int i;
 
-       if (!priv->maxrate)
-               return -EINVAL;
-
        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
                maxrate->tc_maxrate[i] =
                        priv->maxrate[i] * MLX4_RATELIMIT_UNITS_IN_KB;
index c9e6b62..727874f 100644 (file)
@@ -222,7 +222,12 @@ static int mlx4_en_get_sset_count(struct net_device *dev, int sset)
        switch (sset) {
        case ETH_SS_STATS:
                return (priv->stats_bitmap ? bit_count : NUM_ALL_STATS) +
-                       (priv->tx_ring_num + priv->rx_ring_num) * 2;
+                       (priv->tx_ring_num * 2) +
+#ifdef CONFIG_NET_LL_RX_POLL
+                       (priv->rx_ring_num * 5);
+#else
+                       (priv->rx_ring_num * 2);
+#endif
        case ETH_SS_TEST:
                return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags
                                        & MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2;
@@ -271,6 +276,11 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev,
        for (i = 0; i < priv->rx_ring_num; i++) {
                data[index++] = priv->rx_ring[i].packets;
                data[index++] = priv->rx_ring[i].bytes;
+#ifdef CONFIG_NET_LL_RX_POLL
+               data[index++] = priv->rx_ring[i].yields;
+               data[index++] = priv->rx_ring[i].misses;
+               data[index++] = priv->rx_ring[i].cleaned;
+#endif
        }
        spin_unlock_bh(&priv->stats_lock);
 
@@ -334,6 +344,14 @@ static void mlx4_en_get_strings(struct net_device *dev,
                                "rx%d_packets", i);
                        sprintf(data + (index++) * ETH_GSTRING_LEN,
                                "rx%d_bytes", i);
+#ifdef CONFIG_NET_LL_RX_POLL
+                       sprintf(data + (index++) * ETH_GSTRING_LEN,
+                               "rx%d_napi_yield", i);
+                       sprintf(data + (index++) * ETH_GSTRING_LEN,
+                               "rx%d_misses", i);
+                       sprintf(data + (index++) * ETH_GSTRING_LEN,
+                               "rx%d_cleaned", i);
+#endif
                }
                break;
        }
index a5c9df0..a071cda 100644 (file)
@@ -310,7 +310,7 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
 err_mr:
        (void) mlx4_mr_free(dev, &mdev->mr);
 err_map:
-       if (!mdev->uar_map)
+       if (mdev->uar_map)
                iounmap(mdev->uar_map);
 err_uar:
        mlx4_uar_free(dev, &mdev->priv_uar);
index 89c47ea..caf2047 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/slab.h>
 #include <linux/hash.h>
 #include <net/ip.h>
+#include <net/ll_poll.h>
 
 #include <linux/mlx4/driver.h>
 #include <linux/mlx4/device.h>
@@ -67,6 +68,34 @@ int mlx4_en_setup_tc(struct net_device *dev, u8 up)
        return 0;
 }
 
+#ifdef CONFIG_NET_LL_RX_POLL
+/* must be called with local_bh_disable()d */
+static int mlx4_en_low_latency_recv(struct napi_struct *napi)
+{
+       struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi);
+       struct net_device *dev = cq->dev;
+       struct mlx4_en_priv *priv = netdev_priv(dev);
+       struct mlx4_en_rx_ring *rx_ring = &priv->rx_ring[cq->ring];
+       int done;
+
+       if (!priv->port_up)
+               return LL_FLUSH_FAILED;
+
+       if (!mlx4_en_cq_lock_poll(cq))
+               return LL_FLUSH_BUSY;
+
+       done = mlx4_en_process_rx_cq(dev, cq, 4);
+       if (likely(done))
+               rx_ring->cleaned += done;
+       else
+               rx_ring->misses++;
+
+       mlx4_en_cq_unlock_poll(cq);
+
+       return done;
+}
+#endif /* CONFIG_NET_LL_RX_POLL */
+
 #ifdef CONFIG_RFS_ACCEL
 
 struct mlx4_en_filter {
@@ -376,7 +405,7 @@ static int mlx4_en_vlan_rx_add_vid(struct net_device *dev,
                        en_err(priv, "Failed configuring VLAN filter\n");
        }
        if (mlx4_register_vlan(mdev->dev, priv->port, vid, &idx))
-               en_err(priv, "failed adding vlan %d\n", vid);
+               en_dbg(HW, priv, "failed adding vlan %d\n", vid);
        mutex_unlock(&mdev->state_lock);
 
        return 0;
@@ -399,7 +428,7 @@ static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev,
        if (!mlx4_find_cached_vlan(mdev->dev, priv->port, vid, &idx))
                mlx4_unregister_vlan(mdev->dev, priv->port, idx);
        else
-               en_err(priv, "could not find vid %d in cache\n", vid);
+               en_dbg(HW, priv, "could not find vid %d in cache\n", vid);
 
        if (mdev->device_up && priv->port_up) {
                err = mlx4_SET_VLAN_FLTR(mdev->dev, priv);
@@ -1207,10 +1236,19 @@ static void mlx4_en_tx_timeout(struct net_device *dev)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
+       int i;
 
        if (netif_msg_timer(priv))
                en_warn(priv, "Tx timeout called on port:%d\n", priv->port);
 
+       for (i = 0; i < priv->tx_ring_num; i++) {
+               if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, i)))
+                       continue;
+               en_warn(priv, "TX timeout on queue: %d, QP: 0x%x, CQ: 0x%x, Cons: 0x%x, Prod: 0x%x\n",
+                       i, priv->tx_ring[i].qpn, priv->tx_ring[i].cqn,
+                       priv->tx_ring[i].cons, priv->tx_ring[i].prod);
+       }
+
        priv->port_stats.tx_timeout++;
        en_dbg(DRV, priv, "Scheduling watchdog\n");
        queue_work(mdev->workqueue, &priv->watchdog_task);
@@ -1346,12 +1384,13 @@ static void mlx4_en_do_get_stats(struct work_struct *work)
 
        mutex_lock(&mdev->state_lock);
        if (mdev->device_up) {
-               err = mlx4_en_DUMP_ETH_STATS(mdev, priv->port, 0);
-               if (err)
-                       en_dbg(HW, priv, "Could not update stats\n");
+               if (priv->port_up) {
+                       err = mlx4_en_DUMP_ETH_STATS(mdev, priv->port, 0);
+                       if (err)
+                               en_dbg(HW, priv, "Could not update stats\n");
 
-               if (priv->port_up)
                        mlx4_en_auto_moderation(priv);
+               }
 
                queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY);
        }
@@ -1445,6 +1484,8 @@ int mlx4_en_start_port(struct net_device *dev)
        for (i = 0; i < priv->rx_ring_num; i++) {
                cq = &priv->rx_cq[i];
 
+               mlx4_en_cq_init_lock(cq);
+
                err = mlx4_en_activate_cq(priv, cq, i);
                if (err) {
                        en_err(priv, "Failed activating Rx CQ\n");
@@ -1603,6 +1644,9 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
                return;
        }
 
+       /* close port*/
+       mlx4_CLOSE_PORT(mdev->dev, priv->port);
+
        /* Synchronize with tx routine */
        netif_tx_lock_bh(dev);
        if (detach)
@@ -1694,14 +1738,20 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
 
        /* Free RX Rings */
        for (i = 0; i < priv->rx_ring_num; i++) {
-               mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]);
-               while (test_bit(NAPI_STATE_SCHED, &priv->rx_cq[i].napi.state))
+               struct mlx4_en_cq *cq = &priv->rx_cq[i];
+
+               local_bh_disable();
+               while (!mlx4_en_cq_lock_napi(cq)) {
+                       pr_info("CQ %d locked\n", i);
+                       mdelay(1);
+               }
+               local_bh_enable();
+
+               while (test_bit(NAPI_STATE_SCHED, &cq->napi.state))
                        msleep(1);
-               mlx4_en_deactivate_cq(priv, &priv->rx_cq[i]);
+               mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]);
+               mlx4_en_deactivate_cq(priv, cq);
        }
-
-       /* close port*/
-       mlx4_CLOSE_PORT(mdev->dev, priv->port);
 }
 
 static void mlx4_en_restart(struct work_struct *work)
@@ -2061,6 +2111,13 @@ static int mlx4_en_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_
        return mlx4_get_vf_config(mdev->dev, en_priv->port, vf, ivf);
 }
 
+static int mlx4_en_set_vf_link_state(struct net_device *dev, int vf, int link_state)
+{
+       struct mlx4_en_priv *en_priv = netdev_priv(dev);
+       struct mlx4_en_dev *mdev = en_priv->mdev;
+
+       return mlx4_set_vf_link_state(mdev->dev, en_priv->port, vf, link_state);
+}
 static const struct net_device_ops mlx4_netdev_ops = {
        .ndo_open               = mlx4_en_open,
        .ndo_stop               = mlx4_en_close,
@@ -2083,6 +2140,9 @@ static const struct net_device_ops mlx4_netdev_ops = {
 #ifdef CONFIG_RFS_ACCEL
        .ndo_rx_flow_steer      = mlx4_en_filter_rfs,
 #endif
+#ifdef CONFIG_NET_LL_RX_POLL
+       .ndo_ll_poll            = mlx4_en_low_latency_recv,
+#endif
 };
 
 static const struct net_device_ops mlx4_netdev_ops_master = {
@@ -2101,6 +2161,7 @@ static const struct net_device_ops mlx4_netdev_ops_master = {
        .ndo_set_vf_mac         = mlx4_en_set_vf_mac,
        .ndo_set_vf_vlan        = mlx4_en_set_vf_vlan,
        .ndo_set_vf_spoofchk    = mlx4_en_set_vf_spoofchk,
+       .ndo_set_vf_link_state  = mlx4_en_set_vf_link_state,
        .ndo_get_vf_config      = mlx4_en_get_vf_config,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = mlx4_en_netpoll,
@@ -2271,6 +2332,8 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
        mdev->pndev[port] = dev;
 
        netif_carrier_off(dev);
+       mlx4_en_set_default_moderation(priv);
+
        err = register_netdev(dev);
        if (err) {
                en_err(priv, "Netdev registration failed for port %d\n", port);
@@ -2302,7 +2365,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
                en_err(priv, "Failed Initializing port\n");
                goto out;
        }
-       mlx4_en_set_default_moderation(priv);
        queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY);
 
        if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)
index 02aee1e..76997b9 100644 (file)
@@ -31,6 +31,7 @@
  *
  */
 
+#include <net/ll_poll.h>
 #include <linux/mlx4/cq.h>
 #include <linux/slab.h>
 #include <linux/mlx4/qp.h>
 
 #include "mlx4_en.h"
 
+static int mlx4_alloc_pages(struct mlx4_en_priv *priv,
+                           struct mlx4_en_rx_alloc *page_alloc,
+                           const struct mlx4_en_frag_info *frag_info,
+                           gfp_t _gfp)
+{
+       int order;
+       struct page *page;
+       dma_addr_t dma;
+
+       for (order = MLX4_EN_ALLOC_PREFER_ORDER; ;) {
+               gfp_t gfp = _gfp;
+
+               if (order)
+                       gfp |= __GFP_COMP | __GFP_NOWARN;
+               page = alloc_pages(gfp, order);
+               if (likely(page))
+                       break;
+               if (--order < 0 ||
+                   ((PAGE_SIZE << order) < frag_info->frag_size))
+                       return -ENOMEM;
+       }
+       dma = dma_map_page(priv->ddev, page, 0, PAGE_SIZE << order,
+                          PCI_DMA_FROMDEVICE);
+       if (dma_mapping_error(priv->ddev, dma)) {
+               put_page(page);
+               return -ENOMEM;
+       }
+       page_alloc->size = PAGE_SIZE << order;
+       page_alloc->page = page;
+       page_alloc->dma = dma;
+       page_alloc->offset = frag_info->frag_align;
+       /* Not doing get_page() for each frag is a big win
+        * on asymetric workloads.
+        */
+       atomic_set(&page->_count, page_alloc->size / frag_info->frag_stride);
+       return 0;
+}
+
 static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv,
                               struct mlx4_en_rx_desc *rx_desc,
                               struct mlx4_en_rx_alloc *frags,
-                              struct mlx4_en_rx_alloc *ring_alloc)
+                              struct mlx4_en_rx_alloc *ring_alloc,
+                              gfp_t gfp)
 {
        struct mlx4_en_rx_alloc page_alloc[MLX4_EN_MAX_RX_FRAGS];
-       struct mlx4_en_frag_info *frag_info;
+       const struct mlx4_en_frag_info *frag_info;
        struct page *page;
        dma_addr_t dma;
        int i;
 
        for (i = 0; i < priv->num_frags; i++) {
                frag_info = &priv->frag_info[i];
-               if (ring_alloc[i].offset == frag_info->last_offset) {
-                       page = alloc_pages(GFP_ATOMIC | __GFP_COMP,
-                                       MLX4_EN_ALLOC_ORDER);
-                       if (!page)
-                               goto out;
-                       dma = dma_map_page(priv->ddev, page, 0,
-                               MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
-                       if (dma_mapping_error(priv->ddev, dma)) {
-                               put_page(page);
-                               goto out;
-                       }
-                       page_alloc[i].page = page;
-                       page_alloc[i].dma = dma;
-                       page_alloc[i].offset = frag_info->frag_align;
-               } else {
-                       page_alloc[i].page = ring_alloc[i].page;
-                       get_page(ring_alloc[i].page);
-                       page_alloc[i].dma = ring_alloc[i].dma;
-                       page_alloc[i].offset = ring_alloc[i].offset +
-                                               frag_info->frag_stride;
-               }
+               page_alloc[i] = ring_alloc[i];
+               page_alloc[i].offset += frag_info->frag_stride;
+               if (page_alloc[i].offset + frag_info->frag_stride <= ring_alloc[i].size)
+                       continue;
+               if (mlx4_alloc_pages(priv, &page_alloc[i], frag_info, gfp))
+                       goto out;
        }
 
        for (i = 0; i < priv->num_frags; i++) {
@@ -87,14 +112,16 @@ static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv,
 
        return 0;
 
-
 out:
        while (i--) {
                frag_info = &priv->frag_info[i];
-               if (ring_alloc[i].offset == frag_info->last_offset)
+               if (page_alloc[i].page != ring_alloc[i].page) {
                        dma_unmap_page(priv->ddev, page_alloc[i].dma,
-                               MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
-               put_page(page_alloc[i].page);
+                               page_alloc[i].size, PCI_DMA_FROMDEVICE);
+                       page = page_alloc[i].page;
+                       atomic_set(&page->_count, 1);
+                       put_page(page);
+               }
        }
        return -ENOMEM;
 }
@@ -103,12 +130,12 @@ static void mlx4_en_free_frag(struct mlx4_en_priv *priv,
                              struct mlx4_en_rx_alloc *frags,
                              int i)
 {
-       struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
+       const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
 
-       if (frags[i].offset == frag_info->last_offset) {
-               dma_unmap_page(priv->ddev, frags[i].dma, MLX4_EN_ALLOC_SIZE,
+       if (frags[i].offset + frag_info->frag_stride > frags[i].size)
+               dma_unmap_page(priv->ddev, frags[i].dma, frags[i].size,
                                         PCI_DMA_FROMDEVICE);
-       }
+
        if (frags[i].page)
                put_page(frags[i].page);
 }
@@ -116,35 +143,28 @@ static void mlx4_en_free_frag(struct mlx4_en_priv *priv,
 static int mlx4_en_init_allocator(struct mlx4_en_priv *priv,
                                  struct mlx4_en_rx_ring *ring)
 {
-       struct mlx4_en_rx_alloc *page_alloc;
        int i;
+       struct mlx4_en_rx_alloc *page_alloc;
 
        for (i = 0; i < priv->num_frags; i++) {
-               page_alloc = &ring->page_alloc[i];
-               page_alloc->page = alloc_pages(GFP_ATOMIC | __GFP_COMP,
-                                              MLX4_EN_ALLOC_ORDER);
-               if (!page_alloc->page)
-                       goto out;
+               const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
 
-               page_alloc->dma = dma_map_page(priv->ddev, page_alloc->page, 0,
-                                       MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
-               if (dma_mapping_error(priv->ddev, page_alloc->dma)) {
-                       put_page(page_alloc->page);
-                       page_alloc->page = NULL;
+               if (mlx4_alloc_pages(priv, &ring->page_alloc[i],
+                                    frag_info, GFP_KERNEL))
                        goto out;
-               }
-               page_alloc->offset = priv->frag_info[i].frag_align;
-               en_dbg(DRV, priv, "Initialized allocator:%d with page:%p\n",
-                      i, page_alloc->page);
        }
        return 0;
 
 out:
        while (i--) {
+               struct page *page;
+
                page_alloc = &ring->page_alloc[i];
                dma_unmap_page(priv->ddev, page_alloc->dma,
-                               MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
-               put_page(page_alloc->page);
+                              page_alloc->size, PCI_DMA_FROMDEVICE);
+               page = page_alloc->page;
+               atomic_set(&page->_count, 1);
+               put_page(page);
                page_alloc->page = NULL;
        }
        return -ENOMEM;
@@ -157,13 +177,18 @@ static void mlx4_en_destroy_allocator(struct mlx4_en_priv *priv,
        int i;
 
        for (i = 0; i < priv->num_frags; i++) {
+               const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
+
                page_alloc = &ring->page_alloc[i];
                en_dbg(DRV, priv, "Freeing allocator:%d count:%d\n",
                       i, page_count(page_alloc->page));
 
                dma_unmap_page(priv->ddev, page_alloc->dma,
-                               MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
-               put_page(page_alloc->page);
+                               page_alloc->size, PCI_DMA_FROMDEVICE);
+               while (page_alloc->offset + frag_info->frag_stride < page_alloc->size) {
+                       put_page(page_alloc->page);
+                       page_alloc->offset += frag_info->frag_stride;
+               }
                page_alloc->page = NULL;
        }
 }
@@ -194,13 +219,14 @@ static void mlx4_en_init_rx_desc(struct mlx4_en_priv *priv,
 }
 
 static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv,
-                                  struct mlx4_en_rx_ring *ring, int index)
+                                  struct mlx4_en_rx_ring *ring, int index,
+                                  gfp_t gfp)
 {
        struct mlx4_en_rx_desc *rx_desc = ring->buf + (index * ring->stride);
        struct mlx4_en_rx_alloc *frags = ring->rx_info +
                                        (index << priv->log_rx_info);
 
-       return mlx4_en_alloc_frags(priv, rx_desc, frags, ring->page_alloc);
+       return mlx4_en_alloc_frags(priv, rx_desc, frags, ring->page_alloc, gfp);
 }
 
 static inline void mlx4_en_update_rx_prod_db(struct mlx4_en_rx_ring *ring)
@@ -234,7 +260,8 @@ static int mlx4_en_fill_rx_buffers(struct mlx4_en_priv *priv)
                        ring = &priv->rx_ring[ring_ind];
 
                        if (mlx4_en_prepare_rx_desc(priv, ring,
-                                                   ring->actual_size)) {
+                                                   ring->actual_size,
+                                                   GFP_KERNEL)) {
                                if (ring->actual_size < MLX4_EN_MIN_RX_SIZE) {
                                        en_err(priv, "Failed to allocate "
                                                     "enough rx buffers\n");
@@ -449,11 +476,11 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv,
                                        DMA_FROM_DEVICE);
 
                /* Save page reference in skb */
-               get_page(frags[nr].page);
                __skb_frag_set_page(&skb_frags_rx[nr], frags[nr].page);
                skb_frag_size_set(&skb_frags_rx[nr], frag_info->frag_size);
                skb_frags_rx[nr].page_offset = frags[nr].offset;
                skb->truesize += frag_info->frag_stride;
+               frags[nr].page = NULL;
        }
        /* Adjust size of last fragment to match actual length */
        if (nr > 0)
@@ -546,7 +573,7 @@ static void mlx4_en_refill_rx_buffers(struct mlx4_en_priv *priv,
        int index = ring->prod & ring->size_mask;
 
        while ((u32) (ring->prod - ring->cons) < ring->actual_size) {
-               if (mlx4_en_prepare_rx_desc(priv, ring, index))
+               if (mlx4_en_prepare_rx_desc(priv, ring, index, GFP_ATOMIC))
                        break;
                ring->prod++;
                index = ring->prod & ring->size_mask;
@@ -656,8 +683,11 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
                                 * - DIX Ethernet (type interpretation)
                                 * - TCP/IP (v4)
                                 * - without IP options
-                                * - not an IP fragment */
-                               if (dev->features & NETIF_F_GRO) {
+                                * - not an IP fragment
+                                * - no LLS polling in progress
+                                */
+                               if (!mlx4_en_cq_ll_polling(cq) &&
+                                   (dev->features & NETIF_F_GRO)) {
                                        struct sk_buff *gro_skb = napi_get_frags(&cq->napi);
                                        if (!gro_skb)
                                                goto next;
@@ -737,6 +767,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
                                               timestamp);
                }
 
+               skb_mark_ll(skb, &cq->napi);
+
                /* Push it up the stack */
                netif_receive_skb(skb);
 
@@ -781,8 +813,13 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget)
        struct mlx4_en_priv *priv = netdev_priv(dev);
        int done;
 
+       if (!mlx4_en_cq_lock_napi(cq))
+               return budget;
+
        done = mlx4_en_process_rx_cq(dev, cq, budget);
 
+       mlx4_en_cq_unlock_napi(cq);
+
        /* If we used up all the quota - we're probably not done yet... */
        if (done == budget)
                INC_PERF_COUNTER(priv->pstats.napi_quota);
@@ -794,21 +831,7 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget)
        return done;
 }
 
-
-/* Calculate the last offset position that accommodates a full fragment
- * (assuming fagment size = stride-align) */
-static int mlx4_en_last_alloc_offset(struct mlx4_en_priv *priv, u16 stride, u16 align)
-{
-       u16 res = MLX4_EN_ALLOC_SIZE % stride;
-       u16 offset = MLX4_EN_ALLOC_SIZE - stride - res + align;
-
-       en_dbg(DRV, priv, "Calculated last offset for stride:%d align:%d "
-                           "res:%d offset:%d\n", stride, align, res, offset);
-       return offset;
-}
-
-
-static int frag_sizes[] = {
+static const int frag_sizes[] = {
        FRAG_SZ0,
        FRAG_SZ1,
        FRAG_SZ2,
@@ -836,9 +859,6 @@ void mlx4_en_calc_rx_buf(struct net_device *dev)
                        priv->frag_info[i].frag_stride =
                                ALIGN(frag_sizes[i], SMP_CACHE_BYTES);
                }
-               priv->frag_info[i].last_offset = mlx4_en_last_alloc_offset(
-                                               priv, priv->frag_info[i].frag_stride,
-                                               priv->frag_info[i].frag_align);
                buf_size += priv->frag_info[i].frag_size;
                i++;
        }
@@ -850,13 +870,13 @@ void mlx4_en_calc_rx_buf(struct net_device *dev)
        en_dbg(DRV, priv, "Rx buffer scatter-list (effective-mtu:%d "
                  "num_frags:%d):\n", eff_mtu, priv->num_frags);
        for (i = 0; i < priv->num_frags; i++) {
-               en_dbg(DRV, priv, "  frag:%d - size:%d prefix:%d align:%d "
-                               "stride:%d last_offset:%d\n", i,
-                               priv->frag_info[i].frag_size,
-                               priv->frag_info[i].frag_prefix_size,
-                               priv->frag_info[i].frag_align,
-                               priv->frag_info[i].frag_stride,
-                               priv->frag_info[i].last_offset);
+               en_err(priv,
+                      "  frag:%d - size:%d prefix:%d align:%d stride:%d\n",
+                      i,
+                      priv->frag_info[i].frag_size,
+                      priv->frag_info[i].frag_prefix_size,
+                      priv->frag_info[i].frag_align,
+                      priv->frag_info[i].frag_stride);
        }
 }
 
index 4e6877a..7c49238 100644 (file)
@@ -544,7 +544,7 @@ u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb)
        if (vlan_tx_tag_present(skb))
                up = vlan_tx_tag_get(skb) >> VLAN_PRIO_SHIFT;
 
-       return __skb_tx_hash(dev, skb, rings_p_up) + up * rings_p_up;
+       return __netdev_pick_tx(dev, skb) % rings_p_up + up * rings_p_up;
 }
 
 static void mlx4_bf_copy(void __iomem *dst, unsigned long *src, unsigned bytecnt)
index 6000342..7e04286 100644 (file)
@@ -448,6 +448,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
        int i;
        enum slave_port_gen_event gen_event;
        unsigned long flags;
+       struct mlx4_vport_state *s_info;
 
        while ((eqe = next_eqe_sw(eq, dev->caps.eqe_factor))) {
                /*
@@ -556,7 +557,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                                                mlx4_dbg(dev, "%s: Sending MLX4_PORT_CHANGE_SUBTYPE_DOWN"
                                                         " to slave: %d, port:%d\n",
                                                         __func__, i, port);
-                                               mlx4_slave_event(dev, i, eqe);
+                                               s_info = &priv->mfunc.master.vf_oper[slave].vport[port].state;
+                                               if (IFLA_VF_LINK_STATE_AUTO == s_info->link_state)
+                                                       mlx4_slave_event(dev, i, eqe);
                                        } else {  /* IB port */
                                                set_and_calc_slave_port_state(dev, i, port,
                                                                              MLX4_PORT_STATE_DEV_EVENT_PORT_DOWN,
@@ -580,7 +583,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                                        for (i = 0; i < dev->num_slaves; i++) {
                                                if (i == mlx4_master_func_num(dev))
                                                        continue;
-                                               mlx4_slave_event(dev, i, eqe);
+                                               s_info = &priv->mfunc.master.vf_oper[slave].vport[port].state;
+                                               if (IFLA_VF_LINK_STATE_AUTO == s_info->link_state)
+                                                       mlx4_slave_event(dev, i, eqe);
                                        }
                                else /* IB port */
                                        /* port-up event will be sent to a slave when the
index 2c97901..8873d68 100644 (file)
@@ -133,7 +133,8 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags)
                [4] = "Automatic MAC reassignment support",
                [5] = "Time stamping support",
                [6] = "VST (control vlan insertion/stripping) support",
-               [7] = "FSM (MAC anti-spoofing) support"
+               [7] = "FSM (MAC anti-spoofing) support",
+               [8] = "Dynamic QP updates support"
        };
        int i;
 
@@ -659,6 +660,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
                         QUERY_DEV_CAP_MAX_COUNTERS_OFFSET);
 
        MLX4_GET(field32, outbox, QUERY_DEV_CAP_EXT_2_FLAGS_OFFSET);
+       if (field32 & (1 << 16))
+               dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_UPDATE_QP;
        if (field32 & (1 << 26))
                dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_VLAN_CONTROL;
        if (field32 & (1 << 20))
@@ -830,8 +833,10 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave,
        u8 port_type;
        u16 short_field;
        int err;
+       int admin_link_state;
 
 #define MLX4_VF_PORT_NO_LINK_SENSE_MASK        0xE0
+#define MLX4_PORT_LINK_UP_MASK         0x80
 #define QUERY_PORT_CUR_MAX_PKEY_OFFSET 0x0c
 #define QUERY_PORT_CUR_MAX_GID_OFFSET  0x0e
 
@@ -861,6 +866,12 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave,
                /* set port type to currently operating port type */
                port_type |= (dev->caps.port_type[vhcr->in_modifier] & 0x3);
 
+               admin_link_state = priv->mfunc.master.vf_oper[slave].vport[vhcr->in_modifier].state.link_state;
+               if (IFLA_VF_LINK_STATE_ENABLE == admin_link_state)
+                       port_type |= MLX4_PORT_LINK_UP_MASK;
+               else if (IFLA_VF_LINK_STATE_DISABLE == admin_link_state)
+                       port_type &= ~MLX4_PORT_LINK_UP_MASK;
+
                MLX4_PUT(outbox->buf, port_type,
                         QUERY_PORT_SUPPORTED_TYPE_OFFSET);
 
index 264ddeb..e85af92 100644 (file)
@@ -842,11 +842,11 @@ static ssize_t set_port_ib_mtu(struct device *dev,
                return -EINVAL;
        }
 
-       err = sscanf(buf, "%d", &mtu);
-       if (err > 0)
+       err = kstrtoint(buf, 0, &mtu);
+       if (!err)
                ibta_mtu = int_to_ibta_mtu(mtu);
 
-       if (err <= 0 || ibta_mtu < 0) {
+       if (err || ibta_mtu < 0) {
                mlx4_err(mdev, "%s is invalid IBTA mtu\n", buf);
                return -EINVAL;
        }
@@ -2080,6 +2080,11 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                       num_vfs, MLX4_MAX_NUM_VF);
                return -EINVAL;
        }
+
+       if (num_vfs < 0) {
+               pr_err("num_vfs module parameter cannot be negative\n");
+               return -EINVAL;
+       }
        /*
         * Check for BARs.
         */
index df15bb6..17d9277 100644 (file)
@@ -482,6 +482,7 @@ struct mlx4_vport_state {
        u8  default_qos;
        u32 tx_rate;
        bool spoofchk;
+       u32 link_state;
 };
 
 struct mlx4_vf_admin_state {
@@ -570,6 +571,25 @@ struct mlx4_cmd {
        u8                      comm_toggle;
 };
 
+enum {
+       MLX4_VF_IMMED_VLAN_FLAG_VLAN = 1 << 0,
+       MLX4_VF_IMMED_VLAN_FLAG_QOS = 1 << 1,
+       MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE = 1 << 2,
+};
+struct mlx4_vf_immed_vlan_work {
+       struct work_struct      work;
+       struct mlx4_priv        *priv;
+       int                     flags;
+       int                     slave;
+       int                     vlan_ix;
+       int                     orig_vlan_ix;
+       u8                      port;
+       u8                      qos;
+       u16                     vlan_id;
+       u16                     orig_vlan_id;
+};
+
+
 struct mlx4_uar_table {
        struct mlx4_bitmap      bitmap;
 };
@@ -1217,4 +1237,6 @@ static inline spinlock_t *mlx4_tlock(struct mlx4_dev *dev)
 
 #define NOT_MASKED_PD_BITS 17
 
+void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work);
+
 #endif /* MLX4_H */
index b1d7657..35fb60e 100644 (file)
 
 /* Use the maximum between 16384 and a single page */
 #define MLX4_EN_ALLOC_SIZE     PAGE_ALIGN(16384)
-#define MLX4_EN_ALLOC_ORDER    get_order(MLX4_EN_ALLOC_SIZE)
 
-/* Receive fragment sizes; we use at most 4 fragments (for 9600 byte MTU
+#define MLX4_EN_ALLOC_PREFER_ORDER     PAGE_ALLOC_COSTLY_ORDER
+
+/* Receive fragment sizes; we use at most 3 fragments (for 9600 byte MTU
  * and 4K allocations) */
 enum {
-       FRAG_SZ0 = 512 - NET_IP_ALIGN,
-       FRAG_SZ1 = 1024,
+       FRAG_SZ0 = 1536 - NET_IP_ALIGN,
+       FRAG_SZ1 = 4096,
        FRAG_SZ2 = 4096,
        FRAG_SZ3 = MLX4_EN_ALLOC_SIZE
 };
@@ -234,9 +235,10 @@ struct mlx4_en_tx_desc {
 #define MLX4_EN_CX3_HIGH_ID    0x1005
 
 struct mlx4_en_rx_alloc {
-       struct page *page;
-       dma_addr_t dma;
-       u16 offset;
+       struct page     *page;
+       dma_addr_t      dma;
+       u32             offset;
+       u32             size;
 };
 
 struct mlx4_en_tx_ring {
@@ -290,6 +292,11 @@ struct mlx4_en_rx_ring {
        void *rx_info;
        unsigned long bytes;
        unsigned long packets;
+#ifdef CONFIG_NET_LL_RX_POLL
+       unsigned long yields;
+       unsigned long misses;
+       unsigned long cleaned;
+#endif
        unsigned long csum_ok;
        unsigned long csum_none;
        int hwtstamp_rx_filter;
@@ -310,6 +317,19 @@ struct mlx4_en_cq {
        u16 moder_cnt;
        struct mlx4_cqe *buf;
 #define MLX4_EN_OPCODE_ERROR   0x1e
+
+#ifdef CONFIG_NET_LL_RX_POLL
+       unsigned int state;
+#define MLX4_EN_CQ_STATE_IDLE        0
+#define MLX4_EN_CQ_STATE_NAPI     1    /* NAPI owns this CQ */
+#define MLX4_EN_CQ_STATE_POLL     2    /* poll owns this CQ */
+#define MLX4_CQ_LOCKED (MLX4_EN_CQ_STATE_NAPI | MLX4_EN_CQ_STATE_POLL)
+#define MLX4_EN_CQ_STATE_NAPI_YIELD  4    /* NAPI yielded this CQ */
+#define MLX4_EN_CQ_STATE_POLL_YIELD  8    /* poll yielded this CQ */
+#define CQ_YIELD (MLX4_EN_CQ_STATE_NAPI_YIELD | MLX4_EN_CQ_STATE_POLL_YIELD)
+#define CQ_USER_PEND (MLX4_EN_CQ_STATE_POLL | MLX4_EN_CQ_STATE_POLL_YIELD)
+       spinlock_t poll_lock; /* protects from LLS/napi conflicts */
+#endif  /* CONFIG_NET_LL_RX_POLL */
 };
 
 struct mlx4_en_port_profile {
@@ -421,8 +441,6 @@ struct mlx4_en_frag_info {
        u16 frag_prefix_size;
        u16 frag_stride;
        u16 frag_align;
-       u16 last_offset;
-
 };
 
 #ifdef CONFIG_MLX4_EN_DCB
@@ -562,6 +580,115 @@ struct mlx4_mac_entry {
        struct rcu_head rcu;
 };
 
+#ifdef CONFIG_NET_LL_RX_POLL
+static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq)
+{
+       spin_lock_init(&cq->poll_lock);
+       cq->state = MLX4_EN_CQ_STATE_IDLE;
+}
+
+/* called from the device poll rutine to get ownership of a cq */
+static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq)
+{
+       int rc = true;
+       spin_lock(&cq->poll_lock);
+       if (cq->state & MLX4_CQ_LOCKED) {
+               WARN_ON(cq->state & MLX4_EN_CQ_STATE_NAPI);
+               cq->state |= MLX4_EN_CQ_STATE_NAPI_YIELD;
+               rc = false;
+       } else
+               /* we don't care if someone yielded */
+               cq->state = MLX4_EN_CQ_STATE_NAPI;
+       spin_unlock(&cq->poll_lock);
+       return rc;
+}
+
+/* returns true is someone tried to get the cq while napi had it */
+static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq)
+{
+       int rc = false;
+       spin_lock(&cq->poll_lock);
+       WARN_ON(cq->state & (MLX4_EN_CQ_STATE_POLL |
+                              MLX4_EN_CQ_STATE_NAPI_YIELD));
+
+       if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD)
+               rc = true;
+       cq->state = MLX4_EN_CQ_STATE_IDLE;
+       spin_unlock(&cq->poll_lock);
+       return rc;
+}
+
+/* called from mlx4_en_low_latency_poll() */
+static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq)
+{
+       int rc = true;
+       spin_lock_bh(&cq->poll_lock);
+       if ((cq->state & MLX4_CQ_LOCKED)) {
+               struct net_device *dev = cq->dev;
+               struct mlx4_en_priv *priv = netdev_priv(dev);
+               struct mlx4_en_rx_ring *rx_ring = &priv->rx_ring[cq->ring];
+
+               cq->state |= MLX4_EN_CQ_STATE_POLL_YIELD;
+               rc = false;
+               rx_ring->yields++;
+       } else
+               /* preserve yield marks */
+               cq->state |= MLX4_EN_CQ_STATE_POLL;
+       spin_unlock_bh(&cq->poll_lock);
+       return rc;
+}
+
+/* returns true if someone tried to get the cq while it was locked */
+static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq)
+{
+       int rc = false;
+       spin_lock_bh(&cq->poll_lock);
+       WARN_ON(cq->state & (MLX4_EN_CQ_STATE_NAPI));
+
+       if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD)
+               rc = true;
+       cq->state = MLX4_EN_CQ_STATE_IDLE;
+       spin_unlock_bh(&cq->poll_lock);
+       return rc;
+}
+
+/* true if a socket is polling, even if it did not get the lock */
+static inline bool mlx4_en_cq_ll_polling(struct mlx4_en_cq *cq)
+{
+       WARN_ON(!(cq->state & MLX4_CQ_LOCKED));
+       return cq->state & CQ_USER_PEND;
+}
+#else
+static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq)
+{
+}
+
+static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq)
+{
+       return true;
+}
+
+static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq)
+{
+       return false;
+}
+
+static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq)
+{
+       return false;
+}
+
+static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq)
+{
+       return false;
+}
+
+static inline bool mlx4_en_cq_ll_polling(struct mlx4_en_cq *cq)
+{
+       return false;
+}
+#endif /* CONFIG_NET_LL_RX_POLL */
+
 #define MLX4_EN_WOL_DO_MODIFY (1ULL << 63)
 
 void mlx4_en_update_loopback_state(struct net_device *dev,
index 1157f02..f984a89 100644 (file)
@@ -101,6 +101,8 @@ struct res_qp {
        spinlock_t              mcg_spl;
        int                     local_qpn;
        atomic_t                ref_count;
+       u32                     qpc_flags;
+       u8                      sched_queue;
 };
 
 enum res_mtt_states {
@@ -355,7 +357,7 @@ static void update_gid(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *inbox,
 
 static int update_vport_qp_param(struct mlx4_dev *dev,
                                 struct mlx4_cmd_mailbox *inbox,
-                                u8 slave)
+                                u8 slave, u32 qpn)
 {
        struct mlx4_qp_context  *qpc = inbox->buf + 8;
        struct mlx4_vport_oper_state *vp_oper;
@@ -369,12 +371,30 @@ static int update_vport_qp_param(struct mlx4_dev *dev,
 
        if (MLX4_VGT != vp_oper->state.default_vlan) {
                qp_type = (be32_to_cpu(qpc->flags) >> 16) & 0xff;
-               if (MLX4_QP_ST_RC == qp_type)
+               if (MLX4_QP_ST_RC == qp_type ||
+                   (MLX4_QP_ST_UD == qp_type &&
+                    !mlx4_is_qp_reserved(dev, qpn)))
                        return -EINVAL;
 
+               /* the reserved QPs (special, proxy, tunnel)
+                * do not operate over vlans
+                */
+               if (mlx4_is_qp_reserved(dev, qpn))
+                       return 0;
+
                /* force strip vlan by clear vsd */
                qpc->param3 &= ~cpu_to_be32(MLX4_STRIP_VLAN);
-               if (0 != vp_oper->state.default_vlan) {
+
+               if (vp_oper->state.link_state == IFLA_VF_LINK_STATE_DISABLE &&
+                   dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP) {
+                       qpc->pri_path.vlan_control =
+                               MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED |
+                               MLX4_VLAN_CTRL_ETH_TX_BLOCK_PRIO_TAGGED |
+                               MLX4_VLAN_CTRL_ETH_TX_BLOCK_UNTAGGED |
+                               MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED |
+                               MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED |
+                               MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED;
+               } else if (0 != vp_oper->state.default_vlan) {
                        qpc->pri_path.vlan_control =
                                MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED |
                                MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED |
@@ -2114,6 +2134,8 @@ int mlx4_RST2INIT_QP_wrapper(struct mlx4_dev *dev, int slave,
        if (err)
                return err;
        qp->local_qpn = local_qpn;
+       qp->sched_queue = 0;
+       qp->qpc_flags = be32_to_cpu(qpc->flags);
 
        err = get_res(dev, slave, mtt_base, RES_MTT, &mtt);
        if (err)
@@ -2836,6 +2858,9 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave,
 {
        int err;
        struct mlx4_qp_context *qpc = inbox->buf + 8;
+       int qpn = vhcr->in_modifier & 0x7fffff;
+       struct res_qp *qp;
+       u8 orig_sched_queue;
 
        err = verify_qp_parameters(dev, inbox, QP_TRANS_INIT2RTR, slave);
        if (err)
@@ -2844,11 +2869,30 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave,
        update_pkey_index(dev, slave, inbox);
        update_gid(dev, inbox, (u8)slave);
        adjust_proxy_tun_qkey(dev, vhcr, qpc);
-       err = update_vport_qp_param(dev, inbox, slave);
+       orig_sched_queue = qpc->pri_path.sched_queue;
+       err = update_vport_qp_param(dev, inbox, slave, qpn);
        if (err)
                return err;
 
-       return mlx4_GEN_QP_wrapper(dev, slave, vhcr, inbox, outbox, cmd);
+       err = get_res(dev, slave, qpn, RES_QP, &qp);
+       if (err)
+               return err;
+       if (qp->com.from_state != RES_QP_HW) {
+               err = -EBUSY;
+               goto out;
+       }
+
+       err = mlx4_DMA_wrapper(dev, slave, vhcr, inbox, outbox, cmd);
+out:
+       /* if no error, save sched queue value passed in by VF. This is
+        * essentially the QOS value provided by the VF. This will be useful
+        * if we allow dynamic changes from VST back to VGT
+        */
+       if (!err)
+               qp->sched_queue = orig_sched_queue;
+
+       put_res(dev, slave, qpn, RES_QP);
+       return err;
 }
 
 int mlx4_RTR2RTS_QP_wrapper(struct mlx4_dev *dev, int slave,
@@ -3932,3 +3976,112 @@ void mlx4_delete_all_resources_for_slave(struct mlx4_dev *dev, int slave)
        rem_slave_xrcdns(dev, slave);
        mutex_unlock(&priv->mfunc.master.res_tracker.slave_list[slave].mutex);
 }
+
+void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work)
+{
+       struct mlx4_vf_immed_vlan_work *work =
+               container_of(_work, struct mlx4_vf_immed_vlan_work, work);
+       struct mlx4_cmd_mailbox *mailbox;
+       struct mlx4_update_qp_context *upd_context;
+       struct mlx4_dev *dev = &work->priv->dev;
+       struct mlx4_resource_tracker *tracker =
+               &work->priv->mfunc.master.res_tracker;
+       struct list_head *qp_list =
+               &tracker->slave_list[work->slave].res_list[RES_QP];
+       struct res_qp *qp;
+       struct res_qp *tmp;
+       u64 qp_mask = ((1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_UNTAGGED) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_1P) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_TAGGED) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_UNTAGGED) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_1P) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_TAGGED) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_VLAN_INDEX) |
+                      (1ULL << MLX4_UPD_QP_PATH_MASK_SCHED_QUEUE));
+
+       int err;
+       int port, errors = 0;
+       u8 vlan_control;
+
+       if (mlx4_is_slave(dev)) {
+               mlx4_warn(dev, "Trying to update-qp in slave %d\n",
+                         work->slave);
+               goto out;
+       }
+
+       mailbox = mlx4_alloc_cmd_mailbox(dev);
+       if (IS_ERR(mailbox))
+               goto out;
+       if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE) /* block all */
+               vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED |
+                       MLX4_VLAN_CTRL_ETH_TX_BLOCK_PRIO_TAGGED |
+                       MLX4_VLAN_CTRL_ETH_TX_BLOCK_UNTAGGED |
+                       MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED |
+                       MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED |
+                       MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED;
+       else if (!work->vlan_id)
+               vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED |
+                       MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED;
+       else
+               vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED |
+                       MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED |
+                       MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED;
+
+       upd_context = mailbox->buf;
+       upd_context->primary_addr_path_mask = cpu_to_be64(qp_mask);
+       upd_context->qp_context.pri_path.vlan_control = vlan_control;
+       upd_context->qp_context.pri_path.vlan_index = work->vlan_ix;
+
+       spin_lock_irq(mlx4_tlock(dev));
+       list_for_each_entry_safe(qp, tmp, qp_list, com.list) {
+               spin_unlock_irq(mlx4_tlock(dev));
+               if (qp->com.owner == work->slave) {
+                       if (qp->com.from_state != RES_QP_HW ||
+                           !qp->sched_queue ||  /* no INIT2RTR trans yet */
+                           mlx4_is_qp_reserved(dev, qp->local_qpn) ||
+                           qp->qpc_flags & (1 << MLX4_RSS_QPC_FLAG_OFFSET)) {
+                               spin_lock_irq(mlx4_tlock(dev));
+                               continue;
+                       }
+                       port = (qp->sched_queue >> 6 & 1) + 1;
+                       if (port != work->port) {
+                               spin_lock_irq(mlx4_tlock(dev));
+                               continue;
+                       }
+                       upd_context->qp_context.pri_path.sched_queue =
+                               qp->sched_queue & 0xC7;
+                       upd_context->qp_context.pri_path.sched_queue |=
+                               ((work->qos & 0x7) << 3);
+
+                       err = mlx4_cmd(dev, mailbox->dma,
+                                      qp->local_qpn & 0xffffff,
+                                      0, MLX4_CMD_UPDATE_QP,
+                                      MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE);
+                       if (err) {
+                               mlx4_info(dev, "UPDATE_QP failed for slave %d, "
+                                         "port %d, qpn %d (%d)\n",
+                                         work->slave, port, qp->local_qpn,
+                                         err);
+                               errors++;
+                       }
+               }
+               spin_lock_irq(mlx4_tlock(dev));
+       }
+       spin_unlock_irq(mlx4_tlock(dev));
+       mlx4_free_cmd_mailbox(dev, mailbox);
+
+       if (errors)
+               mlx4_err(dev, "%d UPDATE_QP failures for slave %d, port %d\n",
+                        errors, work->slave, work->port);
+
+       /* unregister previous vlan_id if needed and we had no errors
+        * while updating the QPs
+        */
+       if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_VLAN && !errors &&
+           NO_INDX != work->orig_vlan_ix)
+               __mlx4_unregister_vlan(&work->priv->dev, work->port,
+                                      work->orig_vlan_ix);
+out:
+       kfree(work);
+       return;
+}
index fe42fc0..d16b11e 100644 (file)
@@ -22,7 +22,6 @@ if NET_VENDOR_MICREL
 config ARM_KS8695_ETHER
        tristate "KS8695 Ethernet support"
        depends on ARM && ARCH_KS8695
-       select NET_CORE
        select MII
        ---help---
          If you wish to compile a kernel for the KS8695 and want to
@@ -39,7 +38,6 @@ config KS8842
 config KS8851
        tristate "Micrel KS8851 SPI"
        depends on SPI
-       select NET_CORE
        select MII
        select CRC32
        select EEPROM_93CX6
@@ -49,7 +47,6 @@ config KS8851
 config KS8851_MLL
        tristate "Micrel KS8851 MLL"
        depends on HAS_IOMEM
-       select NET_CORE
        select MII
        ---help---
          This platform driver is for Micrel KS8851 Address/data bus
@@ -58,7 +55,6 @@ config KS8851_MLL
 config KSZ884X_PCI
        tristate "Micrel KSZ8841/2 PCI"
        depends on PCI
-       select NET_CORE
        select MII
        select CRC32
        ---help---
index b6c60fd..106eb97 100644 (file)
@@ -1600,7 +1600,6 @@ ks8695_drv_remove(struct platform_device *pdev)
        struct net_device *ndev = platform_get_drvdata(pdev);
        struct ks8695_priv *ksp = netdev_priv(ndev);
 
-       platform_set_drvdata(pdev, NULL);
        netif_napi_del(&ksp->napi);
 
        unregister_netdev(ndev);
index fbcb9e7..e393d99 100644 (file)
@@ -1250,7 +1250,6 @@ static int ks8842_remove(struct platform_device *pdev)
        iounmap(adapter->hw_addr);
        free_netdev(netdev);
        release_mem_region(iomem->start, resource_size(iomem));
-       platform_set_drvdata(pdev, NULL);
        return 0;
 }
 
index ddaf138..ac20098 100644 (file)
@@ -35,6 +35,9 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/ks8851_mll.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_net.h>
 
 #define        DRV_NAME        "ks8851_mll"
 
@@ -1524,6 +1527,13 @@ static int ks_hw_init(struct ks_net *ks)
        return true;
 }
 
+#if defined(CONFIG_OF)
+static const struct of_device_id ks8851_ml_dt_ids[] = {
+       { .compatible = "micrel,ks8851-mll" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ks8851_ml_dt_ids);
+#endif
 
 static int ks8851_probe(struct platform_device *pdev)
 {
@@ -1532,7 +1542,7 @@ static int ks8851_probe(struct platform_device *pdev)
        struct net_device *netdev;
        struct ks_net *ks;
        u16 id, data;
-       struct ks8851_mll_platform_data *pdata;
+       const char *mac;
 
        io_d = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        io_c = platform_get_resource(pdev, IORESOURCE_MEM, 1);
@@ -1619,13 +1629,21 @@ static int ks8851_probe(struct platform_device *pdev)
        ks_wrreg16(ks, KS_OBCR, data | OBCR_ODS_16MA);
 
        /* overwriting the default MAC address */
-       pdata = pdev->dev.platform_data;
-       if (!pdata) {
-               netdev_err(netdev, "No platform data\n");
-               err = -ENODEV;
-               goto err_pdata;
+       if (pdev->dev.of_node) {
+               mac = of_get_mac_address(pdev->dev.of_node);
+               if (mac)
+                       memcpy(ks->mac_addr, mac, ETH_ALEN);
+       } else {
+               struct ks8851_mll_platform_data *pdata;
+
+               pdata = pdev->dev.platform_data;
+               if (!pdata) {
+                       netdev_err(netdev, "No platform data\n");
+                       err = -ENODEV;
+                       goto err_pdata;
+               }
+               memcpy(ks->mac_addr, pdata->mac_addr, ETH_ALEN);
        }
-       memcpy(ks->mac_addr, pdata->mac_addr, 6);
        if (!is_valid_ether_addr(ks->mac_addr)) {
                /* Use random MAC address if none passed */
                eth_random_addr(ks->mac_addr);
@@ -1671,7 +1689,6 @@ static int ks8851_remove(struct platform_device *pdev)
        iounmap(ks->hw_addr);
        free_netdev(netdev);
        release_mem_region(iomem->start, resource_size(iomem));
-       platform_set_drvdata(pdev, NULL);
        return 0;
 
 }
@@ -1680,6 +1697,7 @@ static struct platform_driver ks8851_platform_driver = {
        .driver = {
                .name = DRV_NAME,
                .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(ks8851_ml_dt_ids),
        },
        .probe = ks8851_probe,
        .remove = ks8851_remove,
index 7be9788..967bae8 100644 (file)
@@ -3299,7 +3299,7 @@ static int myri10ge_resume(struct pci_dev *pdev)
        if (mgp == NULL)
                return -EINVAL;
        netdev = mgp->dev;
-       pci_set_power_state(pdev, 0);   /* zeros conf space as a side effect */
+       pci_set_power_state(pdev, PCI_D0);      /* zeros conf space as a side effect */
        msleep(5);              /* give card time to respond */
        pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor);
        if (vendor == 0xffff) {
index cb9e638..dc2c6f5 100644 (file)
@@ -422,7 +422,6 @@ exit_free_pfifo:
 exit_free_xc:
        free_xc(priv->xc);
 exit_free_netdev:
-       platform_set_drvdata(pdev, NULL);
        free_netdev(ndev);
 exit:
        return ret;
@@ -430,11 +429,9 @@ exit:
 
 static int netx_eth_drv_remove(struct platform_device *pdev)
 {
-       struct net_device *ndev = dev_get_drvdata(&pdev->dev);
+       struct net_device *ndev = platform_get_drvdata(pdev);
        struct netx_eth_priv *priv = netdev_priv(ndev);
 
-       platform_set_drvdata(pdev, NULL);
-
        unregister_netdev(ndev);
        xc_stop(priv->xc);
        free_xc(priv->xc);
index 334c171..01182b5 100644 (file)
@@ -22,7 +22,6 @@ config W90P910_ETH
        tristate "Nuvoton w90p910 Ethernet support"
        depends on ARM && ARCH_W90X900
        select PHYLIB
-       select NET_CORE
        select MII
        ---help---
          Say Y here if you want to use built-in Ethernet ports
index 3df8287..e88bdb1 100644 (file)
@@ -1051,7 +1051,6 @@ failed_put_clk:
        clk_put(ether->clk);
 failed_free_rxirq:
        free_irq(ether->rxirq, pdev);
-       platform_set_drvdata(pdev, NULL);
 failed_free_txirq:
        free_irq(ether->txirq, pdev);
 failed_free_io:
@@ -1080,7 +1079,6 @@ static int w90p910_ether_remove(struct platform_device *pdev)
        free_irq(ether->rxirq, dev);
 
        del_timer_sync(&ether->check_timer);
-       platform_set_drvdata(pdev, NULL);
 
        free_netdev(dev);
        return 0;
index b003fe5..098b96d 100644 (file)
@@ -6340,7 +6340,7 @@ static DEFINE_PCI_DEVICE_TABLE(pci_tbl) = {
        {0,},
 };
 
-static struct pci_driver driver = {
+static struct pci_driver forcedeth_pci_driver = {
        .name           = DRV_NAME,
        .id_table       = pci_tbl,
        .probe          = nv_probe,
@@ -6349,16 +6349,6 @@ static struct pci_driver driver = {
        .driver.pm      = NV_PM_OPS,
 };
 
-static int __init init_nic(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit exit_nic(void)
-{
-       pci_unregister_driver(&driver);
-}
-
 module_param(max_interrupt_work, int, 0);
 MODULE_PARM_DESC(max_interrupt_work, "forcedeth maximum events handled per interrupt");
 module_param(optimization_mode, int, 0);
@@ -6379,11 +6369,8 @@ module_param(debug_tx_timeout, bool, 0);
 MODULE_PARM_DESC(debug_tx_timeout,
                 "Dump tx related registers and ring when tx_timeout happens");
 
+module_pci_driver(forcedeth_pci_driver);
 MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>");
 MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver");
 MODULE_LICENSE("GPL");
-
 MODULE_DEVICE_TABLE(pci, pci_tbl);
-
-module_init(init_nic);
-module_exit(exit_nic);
index 55a5548..a061b93 100644 (file)
@@ -1483,7 +1483,6 @@ static int lpc_eth_drv_probe(struct platform_device *pdev)
        return 0;
 
 err_out_unregister_netdev:
-       platform_set_drvdata(pdev, NULL);
        unregister_netdev(ndev);
 err_out_dma_unmap:
        if (!use_iram_for_net(&pldat->pdev->dev) ||
@@ -1511,7 +1510,6 @@ static int lpc_eth_drv_remove(struct platform_device *pdev)
        struct netdata_local *pldat = netdev_priv(ndev);
 
        unregister_netdev(ndev);
-       platform_set_drvdata(pdev, NULL);
 
        if (!use_iram_for_net(&pldat->pdev->dev) ||
            pldat->dma_buff_size > lpc32xx_return_iram_size())
index 91a8a5d..622aa75 100644 (file)
@@ -1448,7 +1448,7 @@ static int octeon_mgmt_probe(struct platform_device *pdev)
 
        SET_NETDEV_DEV(netdev, &pdev->dev);
 
-       dev_set_drvdata(&pdev->dev, netdev);
+       platform_set_drvdata(pdev, netdev);
        p = netdev_priv(netdev);
        netif_napi_add(netdev, &p->napi, octeon_mgmt_napi_poll,
                       OCTEON_MGMT_NAPI_WEIGHT);
@@ -1570,7 +1570,7 @@ err:
 
 static int octeon_mgmt_remove(struct platform_device *pdev)
 {
-       struct net_device *netdev = dev_get_drvdata(&pdev->dev);
+       struct net_device *netdev = platform_get_drvdata(pdev);
 
        unregister_netdev(netdev);
        free_netdev(netdev);
index 34d05bf..cb22341 100644 (file)
@@ -5,7 +5,6 @@
 config PCH_GBE
        tristate "OKI SEMICONDUCTOR IOH(ML7223/ML7831) GbE"
        depends on PCI
-       select NET_CORE
        select MII
        select PTP_1588_CLOCK_PCH
        ---help---
index 7fb7e17..7779036 100644 (file)
@@ -633,6 +633,8 @@ struct pch_gbe_adapter {
        struct pci_dev *ptp_pdev;
 };
 
+#define pch_gbe_hw_to_adapter(hw)      container_of(hw, struct pch_gbe_adapter, hw)
+
 extern const char pch_driver_version[];
 
 /* pch_gbe_main.c */
index 5ae03e8..ff3ad70 100644 (file)
@@ -19,6 +19,7 @@
  */
 #include "pch_gbe.h"
 #include "pch_gbe_phy.h"
+#include "pch_gbe_api.h"
 
 /* bus type values */
 #define pch_gbe_bus_type_unknown       0
@@ -70,7 +71,9 @@ static s32 pch_gbe_plat_init_hw(struct pch_gbe_hw *hw)
 
        ret_val = pch_gbe_phy_get_id(hw);
        if (ret_val) {
-               pr_err("pch_gbe_phy_get_id error\n");
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "pch_gbe_phy_get_id error\n");
                return ret_val;
        }
        pch_gbe_phy_init_setting(hw);
@@ -112,10 +115,12 @@ static void pch_gbe_plat_init_function_pointers(struct pch_gbe_hw *hw)
  *     0:      Successfully
  *     ENOSYS: Function is not registered
  */
-inline s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw)
+s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw)
 {
        if (!hw->reg) {
-               pr_err("ERROR: Registers not mapped\n");
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: Registers not mapped\n");
                return -ENOSYS;
        }
        pch_gbe_plat_init_function_pointers(hw);
@@ -126,12 +131,15 @@ inline s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw)
  * pch_gbe_hal_get_bus_info - Obtain bus information for adapter
  * @hw:        Pointer to the HW structure
  */
-inline void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw)
+void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw)
 {
-       if (!hw->func->get_bus_info)
-               pr_err("ERROR: configuration\n");
-       else
-               hw->func->get_bus_info(hw);
+       if (!hw->func->get_bus_info) {
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: configuration\n");
+               return;
+       }
+       hw->func->get_bus_info(hw);
 }
 
 /**
@@ -141,10 +149,12 @@ inline void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw)
  *     0:      Successfully
  *     ENOSYS: Function is not registered
  */
-inline s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw)
+s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw)
 {
        if (!hw->func->init_hw) {
-               pr_err("ERROR: configuration\n");
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: configuration\n");
                return -ENOSYS;
        }
        return hw->func->init_hw(hw);
@@ -159,7 +169,7 @@ inline s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw)
  *     0:      Successfully
  *     Negative value: Failed
  */
-inline s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset,
+s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset,
                                        u16 *data)
 {
        if (!hw->func->read_phy_reg)
@@ -176,7 +186,7 @@ inline s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset,
  *     0:      Successfully
  *     Negative value: Failed
  */
-inline s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset,
+s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset,
                                        u16 data)
 {
        if (!hw->func->write_phy_reg)
@@ -188,24 +198,30 @@ inline s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset,
  * pch_gbe_hal_phy_hw_reset - Hard PHY reset
  * @hw:            Pointer to the HW structure
  */
-inline void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw)
+void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw)
 {
-       if (!hw->func->reset_phy)
-               pr_err("ERROR: configuration\n");
-       else
-               hw->func->reset_phy(hw);
+       if (!hw->func->reset_phy) {
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: configuration\n");
+               return;
+       }
+       hw->func->reset_phy(hw);
 }
 
 /**
  * pch_gbe_hal_phy_sw_reset - Soft PHY reset
  * @hw:            Pointer to the HW structure
  */
-inline void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw)
+void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw)
 {
-       if (!hw->func->sw_reset_phy)
-               pr_err("ERROR: configuration\n");
-       else
-               hw->func->sw_reset_phy(hw);
+       if (!hw->func->sw_reset_phy) {
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: configuration\n");
+               return;
+       }
+       hw->func->sw_reset_phy(hw);
 }
 
 /**
@@ -215,10 +231,12 @@ inline void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw)
  *     0:      Successfully
  *     ENOSYS: Function is not registered
  */
-inline s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw)
+s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw)
 {
        if (!hw->func->read_mac_addr) {
-               pr_err("ERROR: configuration\n");
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: configuration\n");
                return -ENOSYS;
        }
        return hw->func->read_mac_addr(hw);
@@ -228,7 +246,7 @@ inline s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw)
  * pch_gbe_hal_power_up_phy - Power up PHY
  * @hw:        Pointer to the HW structure
  */
-inline void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw)
+void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw)
 {
        if (hw->func->power_up_phy)
                hw->func->power_up_phy(hw);
@@ -238,7 +256,7 @@ inline void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw)
  * pch_gbe_hal_power_down_phy - Power down PHY
  * @hw:        Pointer to the HW structure
  */
-inline void pch_gbe_hal_power_down_phy(struct pch_gbe_hw *hw)
+void pch_gbe_hal_power_down_phy(struct pch_gbe_hw *hw)
 {
        if (hw->func->power_down_phy)
                hw->func->power_down_phy(hw);
index 24b787b..1129db0 100644 (file)
@@ -122,7 +122,7 @@ static int pch_gbe_set_settings(struct net_device *netdev,
        }
        ret = mii_ethtool_sset(&adapter->mii, ecmd);
        if (ret) {
-               pr_err("Error: mii_ethtool_sset\n");
+               netdev_err(netdev, "Error: mii_ethtool_sset\n");
                return ret;
        }
        hw->mac.link_speed = speed;
index 0c1c65a..ab1039a 100644 (file)
@@ -287,7 +287,7 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
        return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
 }
 
-inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw)
+static inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw)
 {
        iowrite32(0x01, &hw->reg->MAC_ADDR_LOAD);
 }
@@ -300,6 +300,7 @@ inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw)
  */
 s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        u32  adr1a, adr1b;
 
        adr1a = ioread32(&hw->reg->mac_adr[0].high);
@@ -312,7 +313,7 @@ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw)
        hw->mac.addr[4] = (u8)(adr1b & 0xFF);
        hw->mac.addr[5] = (u8)((adr1b >> 8) & 0xFF);
 
-       pr_debug("hw->mac.addr : %pM\n", hw->mac.addr);
+       netdev_dbg(adapter->netdev, "hw->mac.addr : %pM\n", hw->mac.addr);
        return 0;
 }
 
@@ -324,6 +325,7 @@ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw)
 static void pch_gbe_wait_clr_bit(void *reg, u32 bit)
 {
        u32 tmp;
+
        /* wait busy */
        tmp = 1000;
        while ((ioread32(reg) & bit) && --tmp)
@@ -340,9 +342,10 @@ static void pch_gbe_wait_clr_bit(void *reg, u32 bit)
  */
 static void pch_gbe_mac_mar_set(struct pch_gbe_hw *hw, u8 * addr, u32 index)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        u32 mar_low, mar_high, adrmask;
 
-       pr_debug("index : 0x%x\n", index);
+       netdev_dbg(adapter->netdev, "index : 0x%x\n", index);
 
        /*
         * HW expects these in little endian so we reverse the byte order
@@ -468,10 +471,11 @@ static void pch_gbe_mac_mc_addr_list_update(struct pch_gbe_hw *hw,
  */
 s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        struct pch_gbe_mac_info *mac = &hw->mac;
        u32 rx_fctrl;
 
-       pr_debug("mac->fc = %u\n", mac->fc);
+       netdev_dbg(adapter->netdev, "mac->fc = %u\n", mac->fc);
 
        rx_fctrl = ioread32(&hw->reg->RX_FCTRL);
 
@@ -493,14 +497,16 @@ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw)
                mac->tx_fc_enable = true;
                break;
        default:
-               pr_err("Flow control param set incorrectly\n");
+               netdev_err(adapter->netdev,
+                          "Flow control param set incorrectly\n");
                return -EINVAL;
        }
        if (mac->link_duplex == DUPLEX_HALF)
                rx_fctrl &= ~PCH_GBE_FL_CTRL_EN;
        iowrite32(rx_fctrl, &hw->reg->RX_FCTRL);
-       pr_debug("RX_FCTRL reg : 0x%08x  mac->tx_fc_enable : %d\n",
-                ioread32(&hw->reg->RX_FCTRL), mac->tx_fc_enable);
+       netdev_dbg(adapter->netdev,
+                  "RX_FCTRL reg : 0x%08x  mac->tx_fc_enable : %d\n",
+                  ioread32(&hw->reg->RX_FCTRL), mac->tx_fc_enable);
        return 0;
 }
 
@@ -511,10 +517,11 @@ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw)
  */
 static void pch_gbe_mac_set_wol_event(struct pch_gbe_hw *hw, u32 wu_evt)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        u32 addr_mask;
 
-       pr_debug("wu_evt : 0x%08x  ADDR_MASK reg : 0x%08x\n",
-                wu_evt, ioread32(&hw->reg->ADDR_MASK));
+       netdev_dbg(adapter->netdev, "wu_evt : 0x%08x  ADDR_MASK reg : 0x%08x\n",
+                  wu_evt, ioread32(&hw->reg->ADDR_MASK));
 
        if (wu_evt) {
                /* Set Wake-On-Lan address mask */
@@ -546,6 +553,7 @@ static void pch_gbe_mac_set_wol_event(struct pch_gbe_hw *hw, u32 wu_evt)
 u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg,
                        u16 data)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        u32 data_out = 0;
        unsigned int i;
        unsigned long flags;
@@ -558,7 +566,7 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg,
                udelay(20);
        }
        if (i == 0) {
-               pr_err("pch-gbe.miim won't go Ready\n");
+               netdev_err(adapter->netdev, "pch-gbe.miim won't go Ready\n");
                spin_unlock_irqrestore(&hw->miim_lock, flags);
                return 0;       /* No way to indicate timeout error */
        }
@@ -573,9 +581,9 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg,
        }
        spin_unlock_irqrestore(&hw->miim_lock, flags);
 
-       pr_debug("PHY %s: reg=%d, data=0x%04X\n",
-                dir == PCH_GBE_MIIM_OPER_READ ? "READ" : "WRITE", reg,
-                dir == PCH_GBE_MIIM_OPER_READ ? data_out : data);
+       netdev_dbg(adapter->netdev, "PHY %s: reg=%d, data=0x%04X\n",
+                  dir == PCH_GBE_MIIM_OPER_READ ? "READ" : "WRITE", reg,
+                  dir == PCH_GBE_MIIM_OPER_READ ? data_out : data);
        return (u16) data_out;
 }
 
@@ -585,6 +593,7 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg,
  */
 static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        unsigned long tmp2, tmp3;
 
        /* Set Pause packet */
@@ -606,10 +615,13 @@ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw)
        /* Transmit Pause Packet */
        iowrite32(PCH_GBE_PS_PKT_RQ, &hw->reg->PAUSE_REQ);
 
-       pr_debug("PAUSE_PKT1-5 reg : 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
-                ioread32(&hw->reg->PAUSE_PKT1), ioread32(&hw->reg->PAUSE_PKT2),
-                ioread32(&hw->reg->PAUSE_PKT3), ioread32(&hw->reg->PAUSE_PKT4),
-                ioread32(&hw->reg->PAUSE_PKT5));
+       netdev_dbg(adapter->netdev,
+                  "PAUSE_PKT1-5 reg : 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                  ioread32(&hw->reg->PAUSE_PKT1),
+                  ioread32(&hw->reg->PAUSE_PKT2),
+                  ioread32(&hw->reg->PAUSE_PKT3),
+                  ioread32(&hw->reg->PAUSE_PKT4),
+                  ioread32(&hw->reg->PAUSE_PKT5));
 
        return;
 }
@@ -624,15 +636,15 @@ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw)
  */
 static int pch_gbe_alloc_queues(struct pch_gbe_adapter *adapter)
 {
-       adapter->tx_ring = kzalloc(sizeof(*adapter->tx_ring), GFP_KERNEL);
+       adapter->tx_ring = devm_kzalloc(&adapter->pdev->dev,
+                                       sizeof(*adapter->tx_ring), GFP_KERNEL);
        if (!adapter->tx_ring)
                return -ENOMEM;
 
-       adapter->rx_ring = kzalloc(sizeof(*adapter->rx_ring), GFP_KERNEL);
-       if (!adapter->rx_ring) {
-               kfree(adapter->tx_ring);
+       adapter->rx_ring = devm_kzalloc(&adapter->pdev->dev,
+                                       sizeof(*adapter->rx_ring), GFP_KERNEL);
+       if (!adapter->rx_ring)
                return -ENOMEM;
-       }
        return 0;
 }
 
@@ -669,7 +681,7 @@ static int pch_gbe_init_phy(struct pch_gbe_adapter *adapter)
                        break;
        }
        adapter->hw.phy.addr = adapter->mii.phy_id;
-       pr_debug("phy_addr = %d\n", adapter->mii.phy_id);
+       netdev_dbg(netdev, "phy_addr = %d\n", adapter->mii.phy_id);
        if (addr == 32)
                return -EAGAIN;
        /* Selected the phy and isolate the rest */
@@ -758,13 +770,15 @@ void pch_gbe_reinit_locked(struct pch_gbe_adapter *adapter)
  */
 void pch_gbe_reset(struct pch_gbe_adapter *adapter)
 {
+       struct net_device *netdev = adapter->netdev;
+
        pch_gbe_mac_reset_hw(&adapter->hw);
        /* reprogram multicast address register after reset */
-       pch_gbe_set_multi(adapter->netdev);
+       pch_gbe_set_multi(netdev);
        /* Setup the receive address. */
        pch_gbe_mac_init_rx_addrs(&adapter->hw, PCH_GBE_MAR_ENTRIES);
        if (pch_gbe_hal_init_hw(&adapter->hw))
-               pr_err("Hardware Error\n");
+               netdev_err(netdev, "Hardware Error\n");
 }
 
 /**
@@ -778,7 +792,7 @@ static void pch_gbe_free_irq(struct pch_gbe_adapter *adapter)
        free_irq(adapter->pdev->irq, netdev);
        if (adapter->have_msi) {
                pci_disable_msi(adapter->pdev);
-               pr_debug("call pci_disable_msi\n");
+               netdev_dbg(netdev, "call pci_disable_msi\n");
        }
 }
 
@@ -795,7 +809,8 @@ static void pch_gbe_irq_disable(struct pch_gbe_adapter *adapter)
        ioread32(&hw->reg->INT_ST);
        synchronize_irq(adapter->pdev->irq);
 
-       pr_debug("INT_EN reg : 0x%08x\n", ioread32(&hw->reg->INT_EN));
+       netdev_dbg(adapter->netdev, "INT_EN reg : 0x%08x\n",
+                  ioread32(&hw->reg->INT_EN));
 }
 
 /**
@@ -809,7 +824,8 @@ static void pch_gbe_irq_enable(struct pch_gbe_adapter *adapter)
        if (likely(atomic_dec_and_test(&adapter->irq_sem)))
                iowrite32(PCH_GBE_INT_ENABLE_MASK, &hw->reg->INT_EN);
        ioread32(&hw->reg->INT_ST);
-       pr_debug("INT_EN reg : 0x%08x\n", ioread32(&hw->reg->INT_EN));
+       netdev_dbg(adapter->netdev, "INT_EN reg : 0x%08x\n",
+                  ioread32(&hw->reg->INT_EN));
 }
 
 
@@ -846,9 +862,9 @@ static void pch_gbe_configure_tx(struct pch_gbe_adapter *adapter)
        struct pch_gbe_hw *hw = &adapter->hw;
        u32 tdba, tdlen, dctrl;
 
-       pr_debug("dma addr = 0x%08llx  size = 0x%08x\n",
-                (unsigned long long)adapter->tx_ring->dma,
-                adapter->tx_ring->size);
+       netdev_dbg(adapter->netdev, "dma addr = 0x%08llx  size = 0x%08x\n",
+                  (unsigned long long)adapter->tx_ring->dma,
+                  adapter->tx_ring->size);
 
        /* Setup the HW Tx Head and Tail descriptor pointers */
        tdba = adapter->tx_ring->dma;
@@ -894,9 +910,9 @@ static void pch_gbe_configure_rx(struct pch_gbe_adapter *adapter)
        struct pch_gbe_hw *hw = &adapter->hw;
        u32 rdba, rdlen, rxdma;
 
-       pr_debug("dma adr = 0x%08llx  size = 0x%08x\n",
-                (unsigned long long)adapter->rx_ring->dma,
-                adapter->rx_ring->size);
+       netdev_dbg(adapter->netdev, "dma adr = 0x%08llx  size = 0x%08x\n",
+                  (unsigned long long)adapter->rx_ring->dma,
+                  adapter->rx_ring->size);
 
        pch_gbe_mac_force_mac_fc(hw);
 
@@ -907,9 +923,10 @@ static void pch_gbe_configure_rx(struct pch_gbe_adapter *adapter)
        rxdma &= ~PCH_GBE_RX_DMA_EN;
        iowrite32(rxdma, &hw->reg->DMA_CTRL);
 
-       pr_debug("MAC_RX_EN reg = 0x%08x  DMA_CTRL reg = 0x%08x\n",
-                ioread32(&hw->reg->MAC_RX_EN),
-                ioread32(&hw->reg->DMA_CTRL));
+       netdev_dbg(adapter->netdev,
+                  "MAC_RX_EN reg = 0x%08x  DMA_CTRL reg = 0x%08x\n",
+                  ioread32(&hw->reg->MAC_RX_EN),
+                  ioread32(&hw->reg->DMA_CTRL));
 
        /* Setup the HW Rx Head and Tail Descriptor Pointers and
         * the Base and Length of the Rx Descriptor Ring */
@@ -977,7 +994,8 @@ static void pch_gbe_clean_tx_ring(struct pch_gbe_adapter *adapter,
                buffer_info = &tx_ring->buffer_info[i];
                pch_gbe_unmap_and_free_tx_resource(adapter, buffer_info);
        }
-       pr_debug("call pch_gbe_unmap_and_free_tx_resource() %d count\n", i);
+       netdev_dbg(adapter->netdev,
+                  "call pch_gbe_unmap_and_free_tx_resource() %d count\n", i);
 
        size = (unsigned long)sizeof(struct pch_gbe_buffer) * tx_ring->count;
        memset(tx_ring->buffer_info, 0, size);
@@ -1009,7 +1027,8 @@ pch_gbe_clean_rx_ring(struct pch_gbe_adapter *adapter,
                buffer_info = &rx_ring->buffer_info[i];
                pch_gbe_unmap_and_free_rx_resource(adapter, buffer_info);
        }
-       pr_debug("call pch_gbe_unmap_and_free_rx_resource() %d count\n", i);
+       netdev_dbg(adapter->netdev,
+                  "call pch_gbe_unmap_and_free_rx_resource() %d count\n", i);
        size = (unsigned long)sizeof(struct pch_gbe_buffer) * rx_ring->count;
        memset(rx_ring->buffer_info, 0, size);
 
@@ -1087,7 +1106,7 @@ static void pch_gbe_watchdog(unsigned long data)
        struct net_device *netdev = adapter->netdev;
        struct pch_gbe_hw *hw = &adapter->hw;
 
-       pr_debug("right now = %ld\n", jiffies);
+       netdev_dbg(netdev, "right now = %ld\n", jiffies);
 
        pch_gbe_update_stats(adapter);
        if ((mii_link_ok(&adapter->mii)) && (!netif_carrier_ok(netdev))) {
@@ -1095,7 +1114,7 @@ static void pch_gbe_watchdog(unsigned long data)
                netdev->tx_queue_len = adapter->tx_queue_len;
                /* mii library handles link maintenance tasks */
                if (mii_ethtool_gset(&adapter->mii, &cmd)) {
-                       pr_err("ethtool get setting Error\n");
+                       netdev_err(netdev, "ethtool get setting Error\n");
                        mod_timer(&adapter->watchdog_timer,
                                  round_jiffies(jiffies +
                                                PCH_GBE_WATCHDOG_PERIOD));
@@ -1213,7 +1232,7 @@ static void pch_gbe_tx_queue(struct pch_gbe_adapter *adapter,
                                          buffer_info->length,
                                          DMA_TO_DEVICE);
        if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) {
-               pr_err("TX DMA map failed\n");
+               netdev_err(adapter->netdev, "TX DMA map failed\n");
                buffer_info->dma = 0;
                buffer_info->time_stamp = 0;
                tx_ring->next_to_use = ring_num;
@@ -1333,13 +1352,13 @@ static irqreturn_t pch_gbe_intr(int irq, void *data)
        /* When request status is no interruption factor */
        if (unlikely(!int_st))
                return IRQ_NONE;        /* Not our interrupt. End processing. */
-       pr_debug("%s occur int_st = 0x%08x\n", __func__, int_st);
+       netdev_dbg(netdev, "%s occur int_st = 0x%08x\n", __func__, int_st);
        if (int_st & PCH_GBE_INT_RX_FRAME_ERR)
                adapter->stats.intr_rx_frame_err_count++;
        if (int_st & PCH_GBE_INT_RX_FIFO_ERR)
                if (!adapter->rx_stop_flag) {
                        adapter->stats.intr_rx_fifo_err_count++;
-                       pr_debug("Rx fifo over run\n");
+                       netdev_dbg(netdev, "Rx fifo over run\n");
                        adapter->rx_stop_flag = true;
                        int_en = ioread32(&hw->reg->INT_EN);
                        iowrite32((int_en & ~PCH_GBE_INT_RX_FIFO_ERR),
@@ -1359,7 +1378,7 @@ static irqreturn_t pch_gbe_intr(int irq, void *data)
        /* When Rx descriptor is empty  */
        if ((int_st & PCH_GBE_INT_RX_DSC_EMP)) {
                adapter->stats.intr_rx_dsc_empty_count++;
-               pr_debug("Rx descriptor is empty\n");
+               netdev_dbg(netdev, "Rx descriptor is empty\n");
                int_en = ioread32(&hw->reg->INT_EN);
                iowrite32((int_en & ~PCH_GBE_INT_RX_DSC_EMP), &hw->reg->INT_EN);
                if (hw->mac.tx_fc_enable) {
@@ -1382,8 +1401,8 @@ static irqreturn_t pch_gbe_intr(int irq, void *data)
                        __napi_schedule(&adapter->napi);
                }
        }
-       pr_debug("return = 0x%08x  INT_EN reg = 0x%08x\n",
-                IRQ_HANDLED, ioread32(&hw->reg->INT_EN));
+       netdev_dbg(netdev, "return = 0x%08x  INT_EN reg = 0x%08x\n",
+                  IRQ_HANDLED, ioread32(&hw->reg->INT_EN));
        return IRQ_HANDLED;
 }
 
@@ -1437,9 +1456,10 @@ pch_gbe_alloc_rx_buffers(struct pch_gbe_adapter *adapter,
                rx_desc->buffer_addr = (buffer_info->dma);
                rx_desc->gbec_status = DSC_INIT16;
 
-               pr_debug("i = %d  buffer_info->dma = 0x08%llx  buffer_info->length = 0x%x\n",
-                        i, (unsigned long long)buffer_info->dma,
-                        buffer_info->length);
+               netdev_dbg(netdev,
+                          "i = %d  buffer_info->dma = 0x08%llx  buffer_info->length = 0x%x\n",
+                          i, (unsigned long long)buffer_info->dma,
+                          buffer_info->length);
 
                if (unlikely(++i == rx_ring->count))
                        i = 0;
@@ -1531,12 +1551,13 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
        bool cleaned = false;
        int unused, thresh;
 
-       pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean);
+       netdev_dbg(adapter->netdev, "next_to_clean : %d\n",
+                  tx_ring->next_to_clean);
 
        i = tx_ring->next_to_clean;
        tx_desc = PCH_GBE_TX_DESC(*tx_ring, i);
-       pr_debug("gbec_status:0x%04x  dma_status:0x%04x\n",
-                tx_desc->gbec_status, tx_desc->dma_status);
+       netdev_dbg(adapter->netdev, "gbec_status:0x%04x  dma_status:0x%04x\n",
+                  tx_desc->gbec_status, tx_desc->dma_status);
 
        unused = PCH_GBE_DESC_UNUSED(tx_ring);
        thresh = tx_ring->count - PCH_GBE_TX_WEIGHT;
@@ -1544,8 +1565,10 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
        {  /* current marked clean, tx queue filling up, do extra clean */
                int j, k;
                if (unused < 8) {  /* tx queue nearly full */
-                       pr_debug("clean_tx: transmit queue warning (%x,%x) unused=%d\n",
-                               tx_ring->next_to_clean,tx_ring->next_to_use,unused);
+                       netdev_dbg(adapter->netdev,
+                                  "clean_tx: transmit queue warning (%x,%x) unused=%d\n",
+                                  tx_ring->next_to_clean, tx_ring->next_to_use,
+                                  unused);
                }
 
                /* current marked clean, scan for more that need cleaning. */
@@ -1557,49 +1580,56 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
                        if (++k >= tx_ring->count) k = 0;  /*increment, wrap*/
                }
                if (j < PCH_GBE_TX_WEIGHT) {
-                       pr_debug("clean_tx: unused=%d loops=%d found tx_desc[%x,%x:%x].gbec_status=%04x\n",
-                               unused,j, i,k, tx_ring->next_to_use, tx_desc->gbec_status);
+                       netdev_dbg(adapter->netdev,
+                                  "clean_tx: unused=%d loops=%d found tx_desc[%x,%x:%x].gbec_status=%04x\n",
+                                  unused, j, i, k, tx_ring->next_to_use,
+                                  tx_desc->gbec_status);
                        i = k;  /*found one to clean, usu gbec_status==2000.*/
                }
        }
 
        while ((tx_desc->gbec_status & DSC_INIT16) == 0x0000) {
-               pr_debug("gbec_status:0x%04x\n", tx_desc->gbec_status);
+               netdev_dbg(adapter->netdev, "gbec_status:0x%04x\n",
+                          tx_desc->gbec_status);
                buffer_info = &tx_ring->buffer_info[i];
                skb = buffer_info->skb;
                cleaned = true;
 
                if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_ABT)) {
                        adapter->stats.tx_aborted_errors++;
-                       pr_err("Transfer Abort Error\n");
+                       netdev_err(adapter->netdev, "Transfer Abort Error\n");
                } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_CRSER)
                          ) {
                        adapter->stats.tx_carrier_errors++;
-                       pr_err("Transfer Carrier Sense Error\n");
+                       netdev_err(adapter->netdev,
+                                  "Transfer Carrier Sense Error\n");
                } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_EXCOL)
                          ) {
                        adapter->stats.tx_aborted_errors++;
-                       pr_err("Transfer Collision Abort Error\n");
+                       netdev_err(adapter->netdev,
+                                  "Transfer Collision Abort Error\n");
                } else if ((tx_desc->gbec_status &
                            (PCH_GBE_TXD_GMAC_STAT_SNGCOL |
                             PCH_GBE_TXD_GMAC_STAT_MLTCOL))) {
                        adapter->stats.collisions++;
                        adapter->stats.tx_packets++;
                        adapter->stats.tx_bytes += skb->len;
-                       pr_debug("Transfer Collision\n");
+                       netdev_dbg(adapter->netdev, "Transfer Collision\n");
                } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_CMPLT)
                          ) {
                        adapter->stats.tx_packets++;
                        adapter->stats.tx_bytes += skb->len;
                }
                if (buffer_info->mapped) {
-                       pr_debug("unmap buffer_info->dma : %d\n", i);
+                       netdev_dbg(adapter->netdev,
+                                  "unmap buffer_info->dma : %d\n", i);
                        dma_unmap_single(&adapter->pdev->dev, buffer_info->dma,
                                         buffer_info->length, DMA_TO_DEVICE);
                        buffer_info->mapped = false;
                }
                if (buffer_info->skb) {
-                       pr_debug("trim buffer_info->skb : %d\n", i);
+                       netdev_dbg(adapter->netdev,
+                                  "trim buffer_info->skb : %d\n", i);
                        skb_trim(buffer_info->skb, 0);
                }
                tx_desc->gbec_status = DSC_INIT16;
@@ -1613,8 +1643,9 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
                        break;
                }
        }
-       pr_debug("called pch_gbe_unmap_and_free_tx_resource() %d count\n",
-                cleaned_count);
+       netdev_dbg(adapter->netdev,
+                  "called pch_gbe_unmap_and_free_tx_resource() %d count\n",
+                  cleaned_count);
        if (cleaned_count > 0)  { /*skip this if nothing cleaned*/
                /* Recover from running out of Tx resources in xmit_frame */
                spin_lock(&tx_ring->tx_lock);
@@ -1622,12 +1653,13 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
                {
                        netif_wake_queue(adapter->netdev);
                        adapter->stats.tx_restart_count++;
-                       pr_debug("Tx wake queue\n");
+                       netdev_dbg(adapter->netdev, "Tx wake queue\n");
                }
 
                tx_ring->next_to_clean = i;
 
-               pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean);
+               netdev_dbg(adapter->netdev, "next_to_clean : %d\n",
+                          tx_ring->next_to_clean);
                spin_unlock(&tx_ring->tx_lock);
        }
        return cleaned;
@@ -1684,22 +1716,22 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter,
                                   buffer_info->length, DMA_FROM_DEVICE);
                buffer_info->mapped = false;
 
-               pr_debug("RxDecNo = 0x%04x  Status[DMA:0x%02x GBE:0x%04x "
-                        "TCP:0x%08x]  BufInf = 0x%p\n",
-                        i, dma_status, gbec_status, tcp_ip_status,
-                        buffer_info);
+               netdev_dbg(netdev,
+                          "RxDecNo = 0x%04x  Status[DMA:0x%02x GBE:0x%04x TCP:0x%08x]  BufInf = 0x%p\n",
+                          i, dma_status, gbec_status, tcp_ip_status,
+                          buffer_info);
                /* Error check */
                if (unlikely(gbec_status & PCH_GBE_RXD_GMAC_STAT_NOTOCTAL)) {
                        adapter->stats.rx_frame_errors++;
-                       pr_err("Receive Not Octal Error\n");
+                       netdev_err(netdev, "Receive Not Octal Error\n");
                } else if (unlikely(gbec_status &
                                PCH_GBE_RXD_GMAC_STAT_NBLERR)) {
                        adapter->stats.rx_frame_errors++;
-                       pr_err("Receive Nibble Error\n");
+                       netdev_err(netdev, "Receive Nibble Error\n");
                } else if (unlikely(gbec_status &
                                PCH_GBE_RXD_GMAC_STAT_CRCERR)) {
                        adapter->stats.rx_crc_errors++;
-                       pr_err("Receive CRC Error\n");
+                       netdev_err(netdev, "Receive CRC Error\n");
                } else {
                        /* get receive length */
                        /* length convert[-3], length includes FCS length */
@@ -1730,8 +1762,9 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter,
 
                        napi_gro_receive(&adapter->napi, skb);
                        (*work_done)++;
-                       pr_debug("Receive skb->ip_summed: %d length: %d\n",
-                                skb->ip_summed, length);
+                       netdev_dbg(netdev,
+                                  "Receive skb->ip_summed: %d length: %d\n",
+                                  skb->ip_summed, length);
                }
                /* return some buffers to hardware, one at a time is too slow */
                if (unlikely(cleaned_count >= PCH_GBE_RX_BUFFER_WRITE)) {
@@ -1787,10 +1820,10 @@ int pch_gbe_setup_tx_resources(struct pch_gbe_adapter *adapter,
                tx_desc = PCH_GBE_TX_DESC(*tx_ring, desNo);
                tx_desc->gbec_status = DSC_INIT16;
        }
-       pr_debug("tx_ring->desc = 0x%p  tx_ring->dma = 0x%08llx\n"
-                "next_to_clean = 0x%08x  next_to_use = 0x%08x\n",
-                tx_ring->desc, (unsigned long long)tx_ring->dma,
-                tx_ring->next_to_clean, tx_ring->next_to_use);
+       netdev_dbg(adapter->netdev,
+                  "tx_ring->desc = 0x%p  tx_ring->dma = 0x%08llx next_to_clean = 0x%08x  next_to_use = 0x%08x\n",
+                  tx_ring->desc, (unsigned long long)tx_ring->dma,
+                  tx_ring->next_to_clean, tx_ring->next_to_use);
        return 0;
 }
 
@@ -1829,10 +1862,10 @@ int pch_gbe_setup_rx_resources(struct pch_gbe_adapter *adapter,
                rx_desc = PCH_GBE_RX_DESC(*rx_ring, desNo);
                rx_desc->gbec_status = DSC_INIT16;
        }
-       pr_debug("rx_ring->desc = 0x%p  rx_ring->dma = 0x%08llx "
-                "next_to_clean = 0x%08x  next_to_use = 0x%08x\n",
-                rx_ring->desc, (unsigned long long)rx_ring->dma,
-                rx_ring->next_to_clean, rx_ring->next_to_use);
+       netdev_dbg(adapter->netdev,
+                  "rx_ring->desc = 0x%p  rx_ring->dma = 0x%08llx next_to_clean = 0x%08x  next_to_use = 0x%08x\n",
+                  rx_ring->desc, (unsigned long long)rx_ring->dma,
+                  rx_ring->next_to_clean, rx_ring->next_to_use);
        return 0;
 }
 
@@ -1886,9 +1919,9 @@ static int pch_gbe_request_irq(struct pch_gbe_adapter *adapter)
        flags = IRQF_SHARED;
        adapter->have_msi = false;
        err = pci_enable_msi(adapter->pdev);
-       pr_debug("call pci_enable_msi\n");
+       netdev_dbg(netdev, "call pci_enable_msi\n");
        if (err) {
-               pr_debug("call pci_enable_msi - Error: %d\n", err);
+               netdev_dbg(netdev, "call pci_enable_msi - Error: %d\n", err);
        } else {
                flags = 0;
                adapter->have_msi = true;
@@ -1896,9 +1929,11 @@ static int pch_gbe_request_irq(struct pch_gbe_adapter *adapter)
        err = request_irq(adapter->pdev->irq, &pch_gbe_intr,
                          flags, netdev->name, netdev);
        if (err)
-               pr_err("Unable to allocate interrupt Error: %d\n", err);
-       pr_debug("adapter->have_msi : %d  flags : 0x%04x  return : 0x%04x\n",
-                adapter->have_msi, flags, err);
+               netdev_err(netdev, "Unable to allocate interrupt Error: %d\n",
+                          err);
+       netdev_dbg(netdev,
+                  "adapter->have_msi : %d  flags : 0x%04x  return : 0x%04x\n",
+                  adapter->have_msi, flags, err);
        return err;
 }
 
@@ -1919,7 +1954,7 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter)
 
        /* Ensure we have a valid MAC */
        if (!is_valid_ether_addr(adapter->hw.mac.addr)) {
-               pr_err("Error: Invalid MAC address\n");
+               netdev_err(netdev, "Error: Invalid MAC address\n");
                goto out;
        }
 
@@ -1933,12 +1968,14 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter)
 
        err = pch_gbe_request_irq(adapter);
        if (err) {
-               pr_err("Error: can't bring device up - irq request failed\n");
+               netdev_err(netdev,
+                          "Error: can't bring device up - irq request failed\n");
                goto out;
        }
        err = pch_gbe_alloc_rx_buffers_pool(adapter, rx_ring, rx_ring->count);
        if (err) {
-               pr_err("Error: can't bring device up - alloc rx buffers pool failed\n");
+               netdev_err(netdev,
+                          "Error: can't bring device up - alloc rx buffers pool failed\n");
                goto freeirq;
        }
        pch_gbe_alloc_tx_buffers(adapter, tx_ring);
@@ -2015,11 +2052,11 @@ static int pch_gbe_sw_init(struct pch_gbe_adapter *adapter)
 
        /* Initialize the hardware-specific values */
        if (pch_gbe_hal_setup_init_funcs(hw)) {
-               pr_err("Hardware Initialization Failure\n");
+               netdev_err(netdev, "Hardware Initialization Failure\n");
                return -EIO;
        }
        if (pch_gbe_alloc_queues(adapter)) {
-               pr_err("Unable to allocate memory for queues\n");
+               netdev_err(netdev, "Unable to allocate memory for queues\n");
                return -ENOMEM;
        }
        spin_lock_init(&adapter->hw.miim_lock);
@@ -2030,9 +2067,10 @@ static int pch_gbe_sw_init(struct pch_gbe_adapter *adapter)
 
        pch_gbe_init_stats(adapter);
 
-       pr_debug("rx_buffer_len : %d  mac.min_frame_size : %d  mac.max_frame_size : %d\n",
-                (u32) adapter->rx_buffer_len,
-                hw->mac.min_frame_size, hw->mac.max_frame_size);
+       netdev_dbg(netdev,
+                  "rx_buffer_len : %d  mac.min_frame_size : %d  mac.max_frame_size : %d\n",
+                  (u32) adapter->rx_buffer_len,
+                  hw->mac.min_frame_size, hw->mac.max_frame_size);
        return 0;
 }
 
@@ -2061,7 +2099,7 @@ static int pch_gbe_open(struct net_device *netdev)
        err = pch_gbe_up(adapter);
        if (err)
                goto err_up;
-       pr_debug("Success End\n");
+       netdev_dbg(netdev, "Success End\n");
        return 0;
 
 err_up:
@@ -2072,7 +2110,7 @@ err_setup_rx:
        pch_gbe_free_tx_resources(adapter, adapter->tx_ring);
 err_setup_tx:
        pch_gbe_reset(adapter);
-       pr_err("Error End\n");
+       netdev_err(netdev, "Error End\n");
        return err;
 }
 
@@ -2116,8 +2154,9 @@ static int pch_gbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
        if (unlikely(!PCH_GBE_DESC_UNUSED(tx_ring))) {
                netif_stop_queue(netdev);
                spin_unlock_irqrestore(&tx_ring->tx_lock, flags);
-               pr_debug("Return : BUSY  next_to use : 0x%08x  next_to clean : 0x%08x\n",
-                        tx_ring->next_to_use, tx_ring->next_to_clean);
+               netdev_dbg(netdev,
+                          "Return : BUSY  next_to use : 0x%08x  next_to clean : 0x%08x\n",
+                          tx_ring->next_to_use, tx_ring->next_to_clean);
                return NETDEV_TX_BUSY;
        }
 
@@ -2152,7 +2191,7 @@ static void pch_gbe_set_multi(struct net_device *netdev)
        int i;
        int mc_count;
 
-       pr_debug("netdev->flags : 0x%08x\n", netdev->flags);
+       netdev_dbg(netdev, "netdev->flags : 0x%08x\n", netdev->flags);
 
        /* Check for Promiscuous and All Multicast modes */
        rctl = ioread32(&hw->reg->RX_MODE);
@@ -2192,7 +2231,8 @@ static void pch_gbe_set_multi(struct net_device *netdev)
                                        PCH_GBE_MAR_ENTRIES);
        kfree(mta_list);
 
-       pr_debug("RX_MODE reg(check bit31,30 ADD,MLT) : 0x%08x  netdev->mc_count : 0x%08x\n",
+       netdev_dbg(netdev,
+                "RX_MODE reg(check bit31,30 ADD,MLT) : 0x%08x  netdev->mc_count : 0x%08x\n",
                 ioread32(&hw->reg->RX_MODE), mc_count);
 }
 
@@ -2218,12 +2258,12 @@ static int pch_gbe_set_mac(struct net_device *netdev, void *addr)
                pch_gbe_mac_mar_set(&adapter->hw, adapter->hw.mac.addr, 0);
                ret_val = 0;
        }
-       pr_debug("ret_val : 0x%08x\n", ret_val);
-       pr_debug("dev_addr : %pM\n", netdev->dev_addr);
-       pr_debug("mac_addr : %pM\n", adapter->hw.mac.addr);
-       pr_debug("MAC_ADR1AB reg : 0x%08x 0x%08x\n",
-                ioread32(&adapter->hw.reg->mac_adr[0].high),
-                ioread32(&adapter->hw.reg->mac_adr[0].low));
+       netdev_dbg(netdev, "ret_val : 0x%08x\n", ret_val);
+       netdev_dbg(netdev, "dev_addr : %pM\n", netdev->dev_addr);
+       netdev_dbg(netdev, "mac_addr : %pM\n", adapter->hw.mac.addr);
+       netdev_dbg(netdev, "MAC_ADR1AB reg : 0x%08x 0x%08x\n",
+                  ioread32(&adapter->hw.reg->mac_adr[0].high),
+                  ioread32(&adapter->hw.reg->mac_adr[0].low));
        return ret_val;
 }
 
@@ -2245,7 +2285,7 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu)
        max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
        if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) ||
                (max_frame > PCH_GBE_MAX_JUMBO_FRAME_SIZE)) {
-               pr_err("Invalid MTU setting\n");
+               netdev_err(netdev, "Invalid MTU setting\n");
                return -EINVAL;
        }
        if (max_frame <= PCH_GBE_FRAME_SIZE_2048)
@@ -2274,9 +2314,10 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu)
                adapter->hw.mac.max_frame_size = max_frame;
        }
 
-       pr_debug("max_frame : %d  rx_buffer_len : %d  mtu : %d  max_frame_size : %d\n",
-                max_frame, (u32) adapter->rx_buffer_len, netdev->mtu,
-                adapter->hw.mac.max_frame_size);
+       netdev_dbg(netdev,
+                  "max_frame : %d  rx_buffer_len : %d  mtu : %d  max_frame_size : %d\n",
+                  max_frame, (u32) adapter->rx_buffer_len, netdev->mtu,
+                  adapter->hw.mac.max_frame_size);
        return 0;
 }
 
@@ -2317,7 +2358,7 @@ static int pch_gbe_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
 {
        struct pch_gbe_adapter *adapter = netdev_priv(netdev);
 
-       pr_debug("cmd : 0x%04x\n", cmd);
+       netdev_dbg(netdev, "cmd : 0x%04x\n", cmd);
 
        if (cmd == SIOCSHWTSTAMP)
                return hwtstamp_ioctl(netdev, ifr, cmd);
@@ -2354,7 +2395,7 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget)
        bool poll_end_flag = false;
        bool cleaned = false;
 
-       pr_debug("budget : %d\n", budget);
+       netdev_dbg(adapter->netdev, "budget : %d\n", budget);
 
        pch_gbe_clean_rx(adapter, adapter->rx_ring, &work_done, budget);
        cleaned = pch_gbe_clean_tx(adapter, adapter->tx_ring);
@@ -2377,8 +2418,9 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget)
                pch_gbe_enable_dma_rx(&adapter->hw);
        }
 
-       pr_debug("poll_end_flag : %d  work_done : %d  budget : %d\n",
-                poll_end_flag, work_done, budget);
+       netdev_dbg(adapter->netdev,
+                  "poll_end_flag : %d  work_done : %d  budget : %d\n",
+                  poll_end_flag, work_done, budget);
 
        return work_done;
 }
@@ -2435,7 +2477,7 @@ static pci_ers_result_t pch_gbe_io_slot_reset(struct pci_dev *pdev)
        struct pch_gbe_hw *hw = &adapter->hw;
 
        if (pci_enable_device(pdev)) {
-               pr_err("Cannot re-enable PCI device after reset\n");
+               netdev_err(netdev, "Cannot re-enable PCI device after reset\n");
                return PCI_ERS_RESULT_DISCONNECT;
        }
        pci_set_master(pdev);
@@ -2455,7 +2497,8 @@ static void pch_gbe_io_resume(struct pci_dev *pdev)
 
        if (netif_running(netdev)) {
                if (pch_gbe_up(adapter)) {
-                       pr_debug("can't bring device back up after reset\n");
+                       netdev_dbg(netdev,
+                                  "can't bring device back up after reset\n");
                        return;
                }
        }
@@ -2509,7 +2552,7 @@ static int pch_gbe_resume(struct device *device)
 
        err = pci_enable_device(pdev);
        if (err) {
-               pr_err("Cannot enable PCI device from suspend\n");
+               netdev_err(netdev, "Cannot enable PCI device from suspend\n");
                return err;
        }
        pci_set_master(pdev);
@@ -2545,13 +2588,7 @@ static void pch_gbe_remove(struct pci_dev *pdev)
 
        pch_gbe_hal_phy_hw_reset(&adapter->hw);
 
-       kfree(adapter->tx_ring);
-       kfree(adapter->rx_ring);
-
-       iounmap(adapter->hw.reg);
-       pci_release_regions(pdev);
        free_netdev(netdev);
-       pci_disable_device(pdev);
 }
 
 static int pch_gbe_probe(struct pci_dev *pdev,
@@ -2561,7 +2598,7 @@ static int pch_gbe_probe(struct pci_dev *pdev,
        struct pch_gbe_adapter *adapter;
        int ret;
 
-       ret = pci_enable_device(pdev);
+       ret = pcim_enable_device(pdev);
        if (ret)
                return ret;
 
@@ -2574,24 +2611,22 @@ static int pch_gbe_probe(struct pci_dev *pdev,
                        if (ret) {
                                dev_err(&pdev->dev, "ERR: No usable DMA "
                                        "configuration, aborting\n");
-                               goto err_disable_device;
+                               return ret;
                        }
                }
        }
 
-       ret = pci_request_regions(pdev, KBUILD_MODNAME);
+       ret = pcim_iomap_regions(pdev, 1 << PCH_GBE_PCI_BAR, pci_name(pdev));
        if (ret) {
                dev_err(&pdev->dev,
                        "ERR: Can't reserve PCI I/O and memory resources\n");
-               goto err_disable_device;
+               return ret;
        }
        pci_set_master(pdev);
 
        netdev = alloc_etherdev((int)sizeof(struct pch_gbe_adapter));
-       if (!netdev) {
-               ret = -ENOMEM;
-               goto err_release_pci;
-       }
+       if (!netdev)
+               return -ENOMEM;
        SET_NETDEV_DEV(netdev, &pdev->dev);
 
        pci_set_drvdata(pdev, netdev);
@@ -2599,18 +2634,14 @@ static int pch_gbe_probe(struct pci_dev *pdev,
        adapter->netdev = netdev;
        adapter->pdev = pdev;
        adapter->hw.back = adapter;
-       adapter->hw.reg = pci_iomap(pdev, PCH_GBE_PCI_BAR, 0);
-       if (!adapter->hw.reg) {
-               ret = -EIO;
-               dev_err(&pdev->dev, "Can't ioremap\n");
-               goto err_free_netdev;
-       }
+       adapter->hw.reg = pcim_iomap_table(pdev)[PCH_GBE_PCI_BAR];
 
        adapter->ptp_pdev = pci_get_bus_and_slot(adapter->pdev->bus->number,
                                               PCI_DEVFN(12, 4));
        if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) {
-               pr_err("Bad ptp filter\n");
-               return -EINVAL;
+               dev_err(&pdev->dev, "Bad ptp filter\n");
+               ret = -EINVAL;
+               goto err_free_netdev;
        }
 
        netdev->netdev_ops = &pch_gbe_netdev_ops;
@@ -2628,7 +2659,7 @@ static int pch_gbe_probe(struct pci_dev *pdev,
        /* setup the private structure */
        ret = pch_gbe_sw_init(adapter);
        if (ret)
-               goto err_iounmap;
+               goto err_free_netdev;
 
        /* Initialize PHY */
        ret = pch_gbe_init_phy(adapter);
@@ -2684,16 +2715,8 @@ static int pch_gbe_probe(struct pci_dev *pdev,
 
 err_free_adapter:
        pch_gbe_hal_phy_hw_reset(&adapter->hw);
-       kfree(adapter->tx_ring);
-       kfree(adapter->rx_ring);
-err_iounmap:
-       iounmap(adapter->hw.reg);
 err_free_netdev:
        free_netdev(netdev);
-err_release_pci:
-       pci_release_regions(pdev);
-err_disable_device:
-       pci_disable_device(pdev);
        return ret;
 }
 
index 8653c3b..cf7c9b3 100644 (file)
@@ -237,16 +237,17 @@ static int pch_gbe_validate_option(int *value,
        case enable_option:
                switch (*value) {
                case OPTION_ENABLED:
-                       pr_debug("%s Enabled\n", opt->name);
+                       netdev_dbg(adapter->netdev, "%s Enabled\n", opt->name);
                        return 0;
                case OPTION_DISABLED:
-                       pr_debug("%s Disabled\n", opt->name);
+                       netdev_dbg(adapter->netdev, "%s Disabled\n", opt->name);
                        return 0;
                }
                break;
        case range_option:
                if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) {
-                       pr_debug("%s set to %i\n", opt->name, *value);
+                       netdev_dbg(adapter->netdev, "%s set to %i\n",
+                                  opt->name, *value);
                        return 0;
                }
                break;
@@ -258,7 +259,8 @@ static int pch_gbe_validate_option(int *value,
                        ent = &opt->arg.l.p[i];
                        if (*value == ent->i) {
                                if (ent->str[0] != '\0')
-                                       pr_debug("%s\n", ent->str);
+                                       netdev_dbg(adapter->netdev, "%s\n",
+                                                  ent->str);
                                return 0;
                        }
                }
@@ -268,8 +270,8 @@ static int pch_gbe_validate_option(int *value,
                BUG();
        }
 
-       pr_debug("Invalid %s value specified (%i) %s\n",
-                opt->name, *value, opt->err);
+       netdev_dbg(adapter->netdev, "Invalid %s value specified (%i) %s\n",
+                  opt->name, *value, opt->err);
        *value = opt->def;
        return -1;
 }
@@ -318,7 +320,8 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
                                         .p = an_list} }
                };
                if (speed || dplx) {
-                       pr_debug("AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored\n");
+                       netdev_dbg(adapter->netdev,
+                                  "AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored\n");
                        hw->phy.autoneg_advertised = opt.def;
                } else {
                        int tmp = AutoNeg;
@@ -332,13 +335,16 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
        case 0:
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                if ((speed || dplx))
-                       pr_debug("Speed and duplex autonegotiation enabled\n");
+                       netdev_dbg(adapter->netdev,
+                                  "Speed and duplex autonegotiation enabled\n");
                hw->mac.link_speed = SPEED_10;
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case HALF_DUPLEX:
-               pr_debug("Half Duplex specified without Speed\n");
-               pr_debug("Using Autonegotiation at Half Duplex only\n");
+               netdev_dbg(adapter->netdev,
+                          "Half Duplex specified without Speed\n");
+               netdev_dbg(adapter->netdev,
+                          "Using Autonegotiation at Half Duplex only\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF |
                                                PHY_ADVERTISE_100_HALF;
@@ -346,8 +352,10 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case FULL_DUPLEX:
-               pr_debug("Full Duplex specified without Speed\n");
-               pr_debug("Using Autonegotiation at Full Duplex only\n");
+               netdev_dbg(adapter->netdev,
+                          "Full Duplex specified without Speed\n");
+               netdev_dbg(adapter->netdev,
+                          "Using Autonegotiation at Full Duplex only\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                hw->phy.autoneg_advertised = PHY_ADVERTISE_10_FULL |
                                                PHY_ADVERTISE_100_FULL |
@@ -356,8 +364,10 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
                hw->mac.link_duplex = DUPLEX_FULL;
                break;
        case SPEED_10:
-               pr_debug("10 Mbps Speed specified without Duplex\n");
-               pr_debug("Using Autonegotiation at 10 Mbps only\n");
+               netdev_dbg(adapter->netdev,
+                          "10 Mbps Speed specified without Duplex\n");
+               netdev_dbg(adapter->netdev,
+                          "Using Autonegotiation at 10 Mbps only\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF |
                                                PHY_ADVERTISE_10_FULL;
@@ -365,22 +375,24 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case SPEED_10 + HALF_DUPLEX:
-               pr_debug("Forcing to 10 Mbps Half Duplex\n");
+               netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Half Duplex\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 0;
                hw->phy.autoneg_advertised = 0;
                hw->mac.link_speed = SPEED_10;
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case SPEED_10 + FULL_DUPLEX:
-               pr_debug("Forcing to 10 Mbps Full Duplex\n");
+               netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Full Duplex\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 0;
                hw->phy.autoneg_advertised = 0;
                hw->mac.link_speed = SPEED_10;
                hw->mac.link_duplex = DUPLEX_FULL;
                break;
        case SPEED_100:
-               pr_debug("100 Mbps Speed specified without Duplex\n");
-               pr_debug("Using Autonegotiation at 100 Mbps only\n");
+               netdev_dbg(adapter->netdev,
+                          "100 Mbps Speed specified without Duplex\n");
+               netdev_dbg(adapter->netdev,
+                          "Using Autonegotiation at 100 Mbps only\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                hw->phy.autoneg_advertised = PHY_ADVERTISE_100_HALF |
                                                PHY_ADVERTISE_100_FULL;
@@ -388,28 +400,33 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case SPEED_100 + HALF_DUPLEX:
-               pr_debug("Forcing to 100 Mbps Half Duplex\n");
+               netdev_dbg(adapter->netdev,
+                          "Forcing to 100 Mbps Half Duplex\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 0;
                hw->phy.autoneg_advertised = 0;
                hw->mac.link_speed = SPEED_100;
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case SPEED_100 + FULL_DUPLEX:
-               pr_debug("Forcing to 100 Mbps Full Duplex\n");
+               netdev_dbg(adapter->netdev,
+                          "Forcing to 100 Mbps Full Duplex\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 0;
                hw->phy.autoneg_advertised = 0;
                hw->mac.link_speed = SPEED_100;
                hw->mac.link_duplex = DUPLEX_FULL;
                break;
        case SPEED_1000:
-               pr_debug("1000 Mbps Speed specified without Duplex\n");
+               netdev_dbg(adapter->netdev,
+                          "1000 Mbps Speed specified without Duplex\n");
                goto full_duplex_only;
        case SPEED_1000 + HALF_DUPLEX:
-               pr_debug("Half Duplex is not supported at 1000 Mbps\n");
+               netdev_dbg(adapter->netdev,
+                          "Half Duplex is not supported at 1000 Mbps\n");
                /* fall through */
        case SPEED_1000 + FULL_DUPLEX:
 full_duplex_only:
-               pr_debug("Using Autonegotiation at 1000 Mbps Full Duplex only\n");
+               netdev_dbg(adapter->netdev,
+                          "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                hw->phy.autoneg_advertised = PHY_ADVERTISE_1000_FULL;
                hw->mac.link_speed = SPEED_1000;
index 28bb960..da07907 100644 (file)
@@ -97,6 +97,7 @@
  */
 s32 pch_gbe_phy_get_id(struct pch_gbe_hw *hw)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        struct pch_gbe_phy_info *phy = &hw->phy;
        s32 ret;
        u16 phy_id1;
@@ -115,8 +116,9 @@ s32 pch_gbe_phy_get_id(struct pch_gbe_hw *hw)
        phy->id = (u32)phy_id1;
        phy->id = ((phy->id << 6) | ((phy_id2 & 0xFC00) >> 10));
        phy->revision = (u32) (phy_id2 & 0x000F);
-       pr_debug("phy->id : 0x%08x  phy->revision : 0x%08x\n",
-                phy->id, phy->revision);
+       netdev_dbg(adapter->netdev,
+                  "phy->id : 0x%08x  phy->revision : 0x%08x\n",
+                  phy->id, phy->revision);
        return 0;
 }
 
@@ -134,7 +136,10 @@ s32 pch_gbe_phy_read_reg_miic(struct pch_gbe_hw *hw, u32 offset, u16 *data)
        struct pch_gbe_phy_info *phy = &hw->phy;
 
        if (offset > PHY_MAX_REG_ADDRESS) {
-               pr_err("PHY Address %d is out of range\n", offset);
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "PHY Address %d is out of range\n",
+                          offset);
                return -EINVAL;
        }
        *data = pch_gbe_mac_ctrl_miim(hw, phy->addr, PCH_GBE_HAL_MIIM_READ,
@@ -156,7 +161,10 @@ s32 pch_gbe_phy_write_reg_miic(struct pch_gbe_hw *hw, u32 offset, u16 data)
        struct pch_gbe_phy_info *phy = &hw->phy;
 
        if (offset > PHY_MAX_REG_ADDRESS) {
-               pr_err("PHY Address %d is out of range\n", offset);
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "PHY Address %d is out of range\n",
+                          offset);
                return -EINVAL;
        }
        pch_gbe_mac_ctrl_miim(hw, phy->addr, PCH_GBE_HAL_MIIM_WRITE,
@@ -235,7 +243,7 @@ void pch_gbe_phy_power_down(struct pch_gbe_hw *hw)
  * pch_gbe_phy_set_rgmii - RGMII interface setting
  * @hw:                    Pointer to the HW structure
  */
-inline void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw)
+void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw)
 {
        pch_gbe_phy_sw_reset(hw);
 }
@@ -246,15 +254,14 @@ inline void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw)
  */
 void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw)
 {
-       struct pch_gbe_adapter *adapter;
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        struct ethtool_cmd     cmd = { .cmd = ETHTOOL_GSET };
        int ret;
        u16 mii_reg;
 
-       adapter = container_of(hw, struct pch_gbe_adapter, hw);
        ret = mii_ethtool_gset(&adapter->mii, &cmd);
        if (ret)
-               pr_err("Error: mii_ethtool_gset\n");
+               netdev_err(adapter->netdev, "Error: mii_ethtool_gset\n");
 
        ethtool_cmd_speed_set(&cmd, hw->mac.link_speed);
        cmd.duplex = hw->mac.link_duplex;
@@ -263,12 +270,11 @@ void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw)
        pch_gbe_phy_write_reg_miic(hw, MII_BMCR, BMCR_RESET);
        ret = mii_ethtool_sset(&adapter->mii, &cmd);
        if (ret)
-               pr_err("Error: mii_ethtool_sset\n");
+               netdev_err(adapter->netdev, "Error: mii_ethtool_sset\n");
 
        pch_gbe_phy_sw_reset(hw);
 
        pch_gbe_phy_read_reg_miic(hw, PHY_PHYSP_CONTROL, &mii_reg);
        mii_reg |= PHYSP_CTRL_ASSERT_CRS_TX;
        pch_gbe_phy_write_reg_miic(hw, PHY_PHYSP_CONTROL, mii_reg);
-
 }
index cbbeca3..8d51800 100644 (file)
@@ -21,7 +21,6 @@ if NET_PACKET_ENGINE
 config HAMACHI
        tristate "Packet Engines Hamachi GNIC-II support"
        depends on PCI
-       select NET_CORE
        select MII
        ---help---
          If you have a Gigabit Ethernet card of this type, say Y and read
index 322a36b..3fe09ab 100644 (file)
@@ -53,8 +53,8 @@
 
 #define _NETXEN_NIC_LINUX_MAJOR 4
 #define _NETXEN_NIC_LINUX_MINOR 0
-#define _NETXEN_NIC_LINUX_SUBVERSION 80
-#define NETXEN_NIC_LINUX_VERSIONID  "4.0.80"
+#define _NETXEN_NIC_LINUX_SUBVERSION 81
+#define NETXEN_NIC_LINUX_VERSIONID  "4.0.81"
 
 #define NETXEN_VERSION_CODE(a, b, c)   (((a) << 24) + ((b) << 16) + (c))
 #define _major(v)      (((v) >> 24) & 0xff)
@@ -1855,7 +1855,7 @@ static const struct netxen_brdinfo netxen_boards[] = {
 
 #define NUM_SUPPORTED_BOARDS ARRAY_SIZE(netxen_boards)
 
-static inline void get_brd_name_by_type(u32 type, char *name)
+static inline int netxen_nic_get_brd_name_by_type(u32 type, char *name)
 {
        int i, found = 0;
        for (i = 0; i < NUM_SUPPORTED_BOARDS; ++i) {
@@ -1864,10 +1864,14 @@ static inline void get_brd_name_by_type(u32 type, char *name)
                        found = 1;
                        break;
                }
+       }
 
+       if (!found) {
+               strcpy(name, "Unknown");
+               return -EINVAL;
        }
-       if (!found)
-               name = "Unknown";
+
+       return 0;
 }
 
 static inline u32 netxen_tx_avail(struct nx_host_tx_ring *tx_ring)
index 28e0769..32c7906 100644 (file)
@@ -734,6 +734,9 @@ enum {
 #define NIC_CRB_BASE_2         (NETXEN_CAM_RAM(0x700))
 #define NETXEN_NIC_REG(X)      (NIC_CRB_BASE+(X))
 #define NETXEN_NIC_REG_2(X)    (NIC_CRB_BASE_2+(X))
+#define NETXEN_INTR_MODE_REG   NETXEN_NIC_REG(0x44)
+#define NETXEN_MSI_MODE                0x1
+#define NETXEN_INTX_MODE       0x2
 
 #define NX_CDRP_CRB_OFFSET             (NETXEN_NIC_REG(0x18))
 #define NX_ARG1_CRB_OFFSET             (NETXEN_NIC_REG(0x1c))
index af951f3..c401b0b 100644 (file)
@@ -592,48 +592,60 @@ static const struct net_device_ops netxen_netdev_ops = {
 #endif
 };
 
-static void
-netxen_setup_intr(struct netxen_adapter *adapter)
+static inline bool netxen_function_zero(struct pci_dev *pdev)
 {
-       struct netxen_legacy_intr_set *legacy_intrp;
-       struct pci_dev *pdev = adapter->pdev;
-       int err, num_msix;
+       return (PCI_FUNC(pdev->devfn) == 0) ? true : false;
+}
 
-       if (adapter->rss_supported) {
-               num_msix = (num_online_cpus() >= MSIX_ENTRIES_PER_ADAPTER) ?
-                       MSIX_ENTRIES_PER_ADAPTER : 2;
-       } else
-               num_msix = 1;
+static inline void netxen_set_interrupt_mode(struct netxen_adapter *adapter,
+                                            u32 mode)
+{
+       NXWR32(adapter, NETXEN_INTR_MODE_REG, mode);
+}
 
-       adapter->max_sds_rings = 1;
+static inline u32 netxen_get_interrupt_mode(struct netxen_adapter *adapter)
+{
+       return NXRD32(adapter, NETXEN_INTR_MODE_REG);
+}
 
-       adapter->flags &= ~(NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED);
+static void
+netxen_initialize_interrupt_registers(struct netxen_adapter *adapter)
+{
+       struct netxen_legacy_intr_set *legacy_intrp;
+       u32 tgt_status_reg, int_state_reg;
 
        if (adapter->ahw.revision_id >= NX_P3_B0)
                legacy_intrp = &legacy_intr[adapter->ahw.pci_func];
        else
                legacy_intrp = &legacy_intr[0];
 
+       tgt_status_reg = legacy_intrp->tgt_status_reg;
+       int_state_reg = ISR_INT_STATE_REG;
+
        adapter->int_vec_bit = legacy_intrp->int_vec_bit;
-       adapter->tgt_status_reg = netxen_get_ioaddr(adapter,
-                       legacy_intrp->tgt_status_reg);
+       adapter->tgt_status_reg = netxen_get_ioaddr(adapter, tgt_status_reg);
        adapter->tgt_mask_reg = netxen_get_ioaddr(adapter,
-                       legacy_intrp->tgt_mask_reg);
+                                                 legacy_intrp->tgt_mask_reg);
        adapter->pci_int_reg = netxen_get_ioaddr(adapter,
-                       legacy_intrp->pci_int_reg);
+                                                legacy_intrp->pci_int_reg);
        adapter->isr_int_vec = netxen_get_ioaddr(adapter, ISR_INT_VECTOR);
 
        if (adapter->ahw.revision_id >= NX_P3_B1)
                adapter->crb_int_state_reg = netxen_get_ioaddr(adapter,
-                       ISR_INT_STATE_REG);
+                                                              int_state_reg);
        else
                adapter->crb_int_state_reg = netxen_get_ioaddr(adapter,
-                       CRB_INT_VECTOR);
+                                                              CRB_INT_VECTOR);
+}
 
-       netxen_set_msix_bit(pdev, 0);
+static int netxen_setup_msi_interrupts(struct netxen_adapter *adapter,
+                                      int num_msix)
+{
+       struct pci_dev *pdev = adapter->pdev;
+       u32 value;
+       int err;
 
        if (adapter->msix_supported) {
-
                netxen_init_msix_entries(adapter, num_msix);
                err = pci_enable_msix(pdev, adapter->msix_entries, num_msix);
                if (err == 0) {
@@ -644,26 +656,59 @@ netxen_setup_intr(struct netxen_adapter *adapter)
                                adapter->max_sds_rings = num_msix;
 
                        dev_info(&pdev->dev, "using msi-x interrupts\n");
-                       return;
+                       return 0;
                }
-
-               if (err > 0)
-                       pci_disable_msix(pdev);
-
                /* fall through for msi */
        }
 
        if (use_msi && !pci_enable_msi(pdev)) {
+               value = msi_tgt_status[adapter->ahw.pci_func];
                adapter->flags |= NETXEN_NIC_MSI_ENABLED;
-               adapter->tgt_status_reg = netxen_get_ioaddr(adapter,
-                               msi_tgt_status[adapter->ahw.pci_func]);
-               dev_info(&pdev->dev, "using msi interrupts\n");
+               adapter->tgt_status_reg = netxen_get_ioaddr(adapter, value);
                adapter->msix_entries[0].vector = pdev->irq;
-               return;
+               dev_info(&pdev->dev, "using msi interrupts\n");
+               return 0;
        }
 
-       dev_info(&pdev->dev, "using legacy interrupts\n");
-       adapter->msix_entries[0].vector = pdev->irq;
+       dev_err(&pdev->dev, "Failed to acquire MSI-X/MSI interrupt vector\n");
+       return -EIO;
+}
+
+static int netxen_setup_intr(struct netxen_adapter *adapter)
+{
+       struct pci_dev *pdev = adapter->pdev;
+       int num_msix;
+
+       if (adapter->rss_supported)
+               num_msix = (num_online_cpus() >= MSIX_ENTRIES_PER_ADAPTER) ?
+                           MSIX_ENTRIES_PER_ADAPTER : 2;
+       else
+               num_msix = 1;
+
+       adapter->max_sds_rings = 1;
+       adapter->flags &= ~(NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED);
+
+       netxen_initialize_interrupt_registers(adapter);
+       netxen_set_msix_bit(pdev, 0);
+
+       if (netxen_function_zero(pdev)) {
+               if (!netxen_setup_msi_interrupts(adapter, num_msix))
+                       netxen_set_interrupt_mode(adapter, NETXEN_MSI_MODE);
+               else
+                       netxen_set_interrupt_mode(adapter, NETXEN_INTX_MODE);
+       } else {
+               if (netxen_get_interrupt_mode(adapter) == NETXEN_MSI_MODE &&
+                   netxen_setup_msi_interrupts(adapter, num_msix)) {
+                       dev_err(&pdev->dev, "Co-existence of MSI-X/MSI and INTx interrupts is not supported\n");
+                       return -EIO;
+               }
+       }
+
+       if (!NETXEN_IS_MSI_FAMILY(adapter)) {
+               adapter->msix_entries[0].vector = pdev->irq;
+               dev_info(&pdev->dev, "using legacy interrupts\n");
+       }
+       return 0;
 }
 
 static void
@@ -841,7 +886,9 @@ netxen_check_options(struct netxen_adapter *adapter)
        }
 
        if (adapter->portnum == 0) {
-               get_brd_name_by_type(adapter->ahw.board_type, brd_name);
+               if (netxen_nic_get_brd_name_by_type(adapter->ahw.board_type,
+                                                   brd_name))
+                       strcpy(serial_num, "Unknown");
 
                pr_info("%s: %s Board S/N %s  Chip rev 0x%x\n",
                                module_name(THIS_MODULE),
@@ -860,9 +907,9 @@ netxen_check_options(struct netxen_adapter *adapter)
                adapter->ahw.cut_through = (i & 0x8000) ? 1 : 0;
        }
 
-       dev_info(&pdev->dev, "firmware v%d.%d.%d [%s]\n",
-                       fw_major, fw_minor, fw_build,
-                       adapter->ahw.cut_through ? "cut-through" : "legacy");
+       dev_info(&pdev->dev, "Driver v%s, firmware v%d.%d.%d [%s]\n",
+                NETXEN_NIC_LINUX_VERSIONID, fw_major, fw_minor, fw_build,
+                adapter->ahw.cut_through ? "cut-through" : "legacy");
 
        if (adapter->fw_version >= NETXEN_VERSION_CODE(4, 0, 222))
                adapter->capabilities = NXRD32(adapter, CRB_FW_CAPABILITIES_1);
@@ -1508,7 +1555,13 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        netxen_nic_clear_stats(adapter);
 
-       netxen_setup_intr(adapter);
+       err = netxen_setup_intr(adapter);
+
+       if (err) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed to setup interrupts, error = %d\n", err);
+               goto err_out_disable_msi;
+       }
 
        err = netxen_setup_netdev(adapter, netdev);
        if (err)
@@ -1596,7 +1649,7 @@ static void netxen_nic_remove(struct pci_dev *pdev)
        clear_bit(__NX_RESETTING, &adapter->state);
 
        netxen_teardown_intr(adapter);
-
+       netxen_set_interrupt_mode(adapter, 0);
        netxen_remove_diag_entries(adapter);
 
        netxen_cleanup_pci_map(adapter);
@@ -2721,7 +2774,7 @@ netxen_store_bridged_mode(struct device *dev,
        if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC)
                goto err_out;
 
-       if (strict_strtoul(buf, 2, &new))
+       if (kstrtoul(buf, 2, &new))
                goto err_out;
 
        if (!netxen_config_bridged_mode(adapter, !!new))
@@ -2760,7 +2813,7 @@ netxen_store_diag_mode(struct device *dev,
        struct netxen_adapter *adapter = dev_get_drvdata(dev);
        unsigned long new;
 
-       if (strict_strtoul(buf, 2, &new))
+       if (kstrtoul(buf, 2, &new))
                return -EINVAL;
 
        if (!!new != !!(adapter->flags & NETXEN_NIC_DIAG_ENABLED))
@@ -3311,7 +3364,7 @@ static int netxen_netdev_event(struct notifier_block *this,
                                 unsigned long event, void *ptr)
 {
        struct netxen_adapter *adapter;
-       struct net_device *dev = (struct net_device *)ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net_device *orig_dev = dev;
        struct net_device *slave;
 
index c1b693c..b00cf56 100644 (file)
@@ -38,8 +38,8 @@
 
 #define _QLCNIC_LINUX_MAJOR 5
 #define _QLCNIC_LINUX_MINOR 2
-#define _QLCNIC_LINUX_SUBVERSION 42
-#define QLCNIC_LINUX_VERSIONID  "5.2.42"
+#define _QLCNIC_LINUX_SUBVERSION 44
+#define QLCNIC_LINUX_VERSIONID  "5.2.44"
 #define QLCNIC_DRV_IDC_VER  0x01
 #define QLCNIC_DRIVER_VERSION  ((_QLCNIC_LINUX_MAJOR << 16) |\
                 (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
@@ -303,7 +303,6 @@ extern int qlcnic_use_msi;
 extern int qlcnic_use_msi_x;
 extern int qlcnic_auto_fw_reset;
 extern int qlcnic_load_fw_file;
-extern int qlcnic_config_npars;
 
 /* Number of status descriptors to handle per interrupt */
 #define MAX_STATUS_HANDLE      (64)
@@ -394,6 +393,9 @@ struct qlcnic_fw_dump {
        u32     size;   /* total size of the dump */
        void    *data;  /* dump data area */
        struct  qlcnic_dump_template_hdr *tmpl_hdr;
+       dma_addr_t phys_addr;
+       void    *dma_buffer;
+       bool    use_pex_dma;
 };
 
 /*
@@ -427,6 +429,7 @@ struct qlcnic_hardware_context {
        u8 nic_mode;
        char diag_cnt;
 
+       u16 max_uc_count;
        u16 port_type;
        u16 board_type;
        u16 supported_type;
@@ -443,9 +446,10 @@ struct qlcnic_hardware_context {
        u16 max_mtu;
        u32 msg_enable;
        u16 act_pci_func;
+       u16 max_pci_func;
 
        u32 capabilities;
-       u32 capabilities2;
+       u32 extra_capability[3];
        u32 temp;
        u32 int_vec_bit;
        u32 fw_hal_version;
@@ -815,7 +819,8 @@ struct qlcnic_mac_list_s {
 
 #define QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG BIT_2
 #define QLCNIC_FW_CAP2_HW_LRO_IPV6             BIT_3
-#define QLCNIC_FW_CAPABILITY_2_OCBB            BIT_5
+#define QLCNIC_FW_CAPABILITY_SET_DRV_VER       BIT_5
+#define QLCNIC_FW_CAPABILITY_2_BEACON          BIT_7
 
 /* module types */
 #define LINKEVENT_MODULE_NOT_PRESENT                   1
@@ -913,6 +918,9 @@ struct qlcnic_ipaddr {
 #define QLCNIC_IS_TSO_CAPABLE(adapter)  \
        ((adapter)->ahw->capabilities & QLCNIC_FW_CAPABILITY_TSO)
 
+#define QLCNIC_BEACON_EANBLE           0xC
+#define QLCNIC_BEACON_DISABLE          0xD
+
 #define QLCNIC_DEF_NUM_STS_DESC_RINGS  4
 #define QLCNIC_MSIX_TBL_SPACE          8192
 #define QLCNIC_PCI_REG_MSIX_TBL        0x44
@@ -932,6 +940,7 @@ struct qlcnic_ipaddr {
 #define __QLCNIC_SRIOV_ENABLE          10
 #define __QLCNIC_SRIOV_CAPABLE         11
 #define __QLCNIC_MBX_POLL_ENABLE       12
+#define __QLCNIC_DIAG_MODE             13
 
 #define QLCNIC_INTERRUPT_TEST          1
 #define QLCNIC_LOOPBACK_TEST           2
@@ -1467,7 +1476,7 @@ int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *);
 void qlcnic_82xx_free_mac_list(struct qlcnic_adapter *adapter);
 
 int qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu);
-int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *);
+int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *, u32);
 int qlcnic_change_mtu(struct net_device *netdev, int new_mtu);
 netdev_features_t qlcnic_fix_features(struct net_device *netdev,
        netdev_features_t features);
@@ -1489,7 +1498,9 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
 int qlcnic_set_max_rss(struct qlcnic_adapter *, u8, size_t);
 int qlcnic_validate_max_rss(struct qlcnic_adapter *, __u32);
 void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter);
+void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *);
 int qlcnic_enable_msix(struct qlcnic_adapter *, u32);
+void qlcnic_set_drv_version(struct qlcnic_adapter *);
 
 /*  eSwitch management functions */
 int qlcnic_config_switch_port(struct qlcnic_adapter *,
@@ -1543,6 +1554,7 @@ int qlcnic_set_default_offload_settings(struct qlcnic_adapter *);
 int qlcnic_reset_npar_config(struct qlcnic_adapter *);
 int qlcnic_set_eswitch_port_config(struct qlcnic_adapter *);
 void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int, u16);
+int qlcnic_get_beacon_state(struct qlcnic_adapter *, u8 *);
 int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter);
 int qlcnic_read_mac_addr(struct qlcnic_adapter *);
 int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int);
@@ -1584,6 +1596,8 @@ struct qlcnic_nic_template {
        void (*napi_del)(struct qlcnic_adapter *);
        void (*config_ipaddr)(struct qlcnic_adapter *, __be32, int);
        irqreturn_t (*clear_legacy_intr)(struct qlcnic_adapter *);
+       int (*shutdown)(struct pci_dev *);
+       int (*resume)(struct qlcnic_adapter *);
 };
 
 /* Adapter hardware abstraction */
@@ -1625,6 +1639,7 @@ struct qlcnic_hardware_ops {
        int (*config_promisc_mode) (struct qlcnic_adapter *, u32);
        void (*change_l2_filter) (struct qlcnic_adapter *, u64 *, u16);
        int (*get_board_info) (struct qlcnic_adapter *);
+       void (*set_mac_filter_count) (struct qlcnic_adapter *);
        void (*free_mac_list) (struct qlcnic_adapter *);
 };
 
@@ -1787,6 +1802,18 @@ static inline void qlcnic_napi_enable(struct qlcnic_adapter *adapter)
        adapter->ahw->hw_ops->napi_enable(adapter);
 }
 
+static inline int __qlcnic_shutdown(struct pci_dev *pdev)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
+
+       return adapter->nic_ops->shutdown(pdev);
+}
+
+static inline int __qlcnic_resume(struct qlcnic_adapter *adapter)
+{
+       return adapter->nic_ops->resume(adapter);
+}
+
 static inline void qlcnic_napi_disable(struct qlcnic_adapter *adapter)
 {
        adapter->ahw->hw_ops->napi_disable(adapter);
@@ -1840,6 +1867,11 @@ static inline void qlcnic_free_mac_list(struct qlcnic_adapter *adapter)
        return adapter->ahw->hw_ops->free_mac_list(adapter);
 }
 
+static inline void qlcnic_set_mac_filter_count(struct qlcnic_adapter *adapter)
+{
+       adapter->ahw->hw_ops->set_mac_filter_count(adapter);
+}
+
 static inline void qlcnic_dev_request_reset(struct qlcnic_adapter *adapter,
                                            u32 key)
 {
@@ -1886,6 +1918,21 @@ static inline void qlcnic_enable_int(struct qlcnic_host_sds_ring *sds_ring)
                writel(0xfbff, adapter->tgt_mask_reg);
 }
 
+static inline int qlcnic_get_diag_lock(struct qlcnic_adapter *adapter)
+{
+       return test_and_set_bit(__QLCNIC_DIAG_MODE, &adapter->state);
+}
+
+static inline void qlcnic_release_diag_lock(struct qlcnic_adapter *adapter)
+{
+       clear_bit(__QLCNIC_DIAG_MODE, &adapter->state);
+}
+
+static inline int qlcnic_check_diag_status(struct qlcnic_adapter *adapter)
+{
+       return test_bit(__QLCNIC_DIAG_MODE, &adapter->state);
+}
+
 extern const struct ethtool_ops qlcnic_sriov_vf_ethtool_ops;
 extern const struct ethtool_ops qlcnic_ethtool_ops;
 extern const struct ethtool_ops qlcnic_ethtool_failed_ops;
index b4ff1e3..0913c62 100644 (file)
@@ -63,6 +63,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
        {QLCNIC_CMD_STOP_NIC_FUNC, 2, 1},
        {QLCNIC_CMD_SET_LED_CONFIG, 5, 1},
        {QLCNIC_CMD_GET_LED_CONFIG, 1, 5},
+       {QLCNIC_CMD_83XX_SET_DRV_VER, 4, 1},
        {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26},
        {QLCNIC_CMD_CONFIG_VPORT, 4, 4},
        {QLCNIC_CMD_BC_EVENT_SETUP, 2, 1},
@@ -172,6 +173,7 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = {
        .config_promisc_mode            = qlcnic_83xx_nic_set_promisc,
        .change_l2_filter               = qlcnic_83xx_change_l2_filter,
        .get_board_info                 = qlcnic_83xx_get_port_info,
+       .set_mac_filter_count           = qlcnic_83xx_set_mac_filter_count,
        .free_mac_list                  = qlcnic_82xx_free_mac_list,
 };
 
@@ -184,6 +186,8 @@ static struct qlcnic_nic_template qlcnic_83xx_ops = {
        .napi_del               = qlcnic_83xx_napi_del,
        .config_ipaddr          = qlcnic_83xx_config_ipaddr,
        .clear_legacy_intr      = qlcnic_83xx_clear_legacy_intr,
+       .shutdown               = qlcnic_83xx_shutdown,
+       .resume                 = qlcnic_83xx_resume,
 };
 
 void qlcnic_83xx_register_map(struct qlcnic_hardware_context *ahw)
@@ -312,6 +316,11 @@ inline void qlcnic_83xx_clear_legacy_intr_mask(struct qlcnic_adapter *adapter)
        writel(0, adapter->tgt_mask_reg);
 }
 
+inline void qlcnic_83xx_set_legacy_intr_mask(struct qlcnic_adapter *adapter)
+{
+       writel(1, adapter->tgt_mask_reg);
+}
+
 /* Enable MSI-x and INT-x interrupts */
 void qlcnic_83xx_enable_intr(struct qlcnic_adapter *adapter,
                             struct qlcnic_host_sds_ring *sds_ring)
@@ -458,6 +467,9 @@ void qlcnic_83xx_free_mbx_intr(struct qlcnic_adapter *adapter)
 {
        u32 num_msix;
 
+       if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
+               qlcnic_83xx_set_legacy_intr_mask(adapter);
+
        qlcnic_83xx_disable_mbx_intr(adapter);
 
        if (adapter->flags & QLCNIC_MSIX_ENABLED)
@@ -474,7 +486,6 @@ int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter)
 {
        irq_handler_t handler;
        u32 val;
-       char name[32];
        int err = 0;
        unsigned long flags = 0;
 
@@ -485,9 +496,7 @@ int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter)
        if (adapter->flags & QLCNIC_MSIX_ENABLED) {
                handler = qlcnic_83xx_handle_aen;
                val = adapter->msix_entries[adapter->ahw->num_msix - 1].vector;
-               snprintf(name, (IFNAMSIZ + 4),
-                        "%s[%s]", "qlcnic", "aen");
-               err = request_irq(val, handler, flags, name, adapter);
+               err = request_irq(val, handler, flags, "qlcnic-MB", adapter);
                if (err) {
                        dev_err(&adapter->pdev->dev,
                                "failed to register MBX interrupt\n");
@@ -604,6 +613,22 @@ int qlcnic_83xx_get_port_info(struct qlcnic_adapter *adapter)
        return status;
 }
 
+void qlcnic_83xx_set_mac_filter_count(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       u16 act_pci_fn = ahw->act_pci_func;
+       u16 count;
+
+       ahw->max_mc_count = QLC_83XX_MAX_MC_COUNT;
+       if (act_pci_fn <= 2)
+               count = (QLC_83XX_MAX_UC_COUNT - QLC_83XX_MAX_MC_COUNT) /
+                        act_pci_fn;
+       else
+               count = (QLC_83XX_LB_MAX_FILTERS - QLC_83XX_MAX_MC_COUNT) /
+                        act_pci_fn;
+       ahw->max_uc_count = count;
+}
+
 void qlcnic_83xx_enable_mbx_intrpt(struct qlcnic_adapter *adapter)
 {
        u32 val;
@@ -839,7 +864,9 @@ void qlcnic_83xx_idc_aen_work(struct work_struct *work)
        int i, err = 0;
 
        adapter = container_of(work, struct qlcnic_adapter, idc_aen_work.work);
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_IDC_ACK);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_IDC_ACK);
+       if (err)
+               return;
 
        for (i = 1; i < QLC_83XX_MBX_AEN_CNT; i++)
                cmd.req.arg[i] = adapter->ahw->mbox_aen[i];
@@ -1080,8 +1107,10 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)
                cap |= QLC_83XX_FW_CAP_LRO_MSS;
 
        /* set mailbox hdr and capabilities */
-       qlcnic_alloc_mbx_args(&cmd, adapter,
-                             QLCNIC_CMD_CREATE_RX_CTX);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_CREATE_RX_CTX);
+       if (err)
+               return err;
 
        if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
                cmd.req.arg[0] |= (0x3 << 29);
@@ -1239,7 +1268,9 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
                mbx.intr_id = 0xffff;
        mbx.src = 0;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX);
+       if (err)
+               return err;
 
        if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
                cmd.req.arg[0] |= (0x3 << 29);
@@ -1385,8 +1416,11 @@ int qlcnic_83xx_config_led(struct qlcnic_adapter *adapter, u32 state,
 
        if (state) {
                /* Get LED configuration */
-               qlcnic_alloc_mbx_args(&cmd, adapter,
-                                     QLCNIC_CMD_GET_LED_CONFIG);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_GET_LED_CONFIG);
+               if (status)
+                       return status;
+
                status = qlcnic_issue_cmd(adapter, &cmd);
                if (status) {
                        dev_err(&adapter->pdev->dev,
@@ -1400,8 +1434,11 @@ int qlcnic_83xx_config_led(struct qlcnic_adapter *adapter, u32 state,
                /* Set LED Configuration */
                mbx_in = (LSW(QLC_83XX_LED_CONFIG) << 16) |
                          LSW(QLC_83XX_LED_CONFIG);
-               qlcnic_alloc_mbx_args(&cmd, adapter,
-                                     QLCNIC_CMD_SET_LED_CONFIG);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_SET_LED_CONFIG);
+               if (status)
+                       return status;
+
                cmd.req.arg[1] = mbx_in;
                cmd.req.arg[2] = mbx_in;
                cmd.req.arg[3] = mbx_in;
@@ -1418,8 +1455,11 @@ mbx_err:
 
        } else {
                /* Restoring default LED configuration */
-               qlcnic_alloc_mbx_args(&cmd, adapter,
-                                     QLCNIC_CMD_SET_LED_CONFIG);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_SET_LED_CONFIG);
+               if (status)
+                       return status;
+
                cmd.req.arg[1] = adapter->ahw->mbox_reg[0];
                cmd.req.arg[2] = adapter->ahw->mbox_reg[1];
                cmd.req.arg[3] = adapter->ahw->mbox_reg[2];
@@ -1489,10 +1529,18 @@ void qlcnic_83xx_register_nic_idc_func(struct qlcnic_adapter *adapter,
                return;
 
        if (enable) {
-               qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INIT_NIC_FUNC);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_INIT_NIC_FUNC);
+               if (status)
+                       return;
+
                cmd.req.arg[1] = BIT_0 | BIT_31;
        } else {
-               qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_STOP_NIC_FUNC);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_STOP_NIC_FUNC);
+               if (status)
+                       return;
+
                cmd.req.arg[1] = BIT_0 | BIT_31;
        }
        status = qlcnic_issue_cmd(adapter, &cmd);
@@ -1509,7 +1557,10 @@ int qlcnic_83xx_set_port_config(struct qlcnic_adapter *adapter)
        struct qlcnic_cmd_args cmd;
        int err;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_PORT_CONFIG);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_PORT_CONFIG);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = adapter->ahw->port_config;
        err = qlcnic_issue_cmd(adapter, &cmd);
        if (err)
@@ -1523,7 +1574,10 @@ int qlcnic_83xx_get_port_config(struct qlcnic_adapter *adapter)
        struct qlcnic_cmd_args cmd;
        int err;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PORT_CONFIG);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PORT_CONFIG);
+       if (err)
+               return err;
+
        err = qlcnic_issue_cmd(adapter, &cmd);
        if (err)
                dev_info(&adapter->pdev->dev, "Get Port config failed\n");
@@ -1539,7 +1593,10 @@ int qlcnic_83xx_setup_link_event(struct qlcnic_adapter *adapter, int enable)
        u32 temp;
        struct qlcnic_cmd_args cmd;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_EVENT);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_EVENT);
+       if (err)
+               return err;
+
        temp = adapter->recv_ctx->context_id << 16;
        cmd.req.arg[1] = (enable ? 1 : 0) | BIT_8 | temp;
        err = qlcnic_issue_cmd(adapter, &cmd);
@@ -1570,7 +1627,11 @@ int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)
        if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)
                return -EIO;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_MAC_RX_MODE);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_CONFIGURE_MAC_RX_MODE);
+       if (err)
+               return err;
+
        qlcnic_83xx_set_interface_id_promisc(adapter, &temp);
        cmd.req.arg[1] = (mode ? 1 : 0) | temp;
        err = qlcnic_issue_cmd(adapter, &cmd);
@@ -1588,16 +1649,24 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
        struct qlcnic_hardware_context *ahw = adapter->ahw;
        int ret = 0, loop = 0, max_sds_rings = adapter->max_sds_rings;
 
-       QLCDB(adapter, DRV, "%s loopback test in progress\n",
-             mode == QLCNIC_ILB_MODE ? "internal" : "external");
        if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) {
-               dev_warn(&adapter->pdev->dev,
-                        "Loopback test not supported for non privilege function\n");
+               netdev_warn(netdev,
+                           "Loopback test not supported in non privileged mode\n");
                return ret;
        }
 
-       if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
+       if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
+               netdev_info(netdev, "Device is resetting\n");
                return -EBUSY;
+       }
+
+       if (qlcnic_get_diag_lock(adapter)) {
+               netdev_info(netdev, "Device is in diagnostics mode\n");
+               return -EBUSY;
+       }
+
+       netdev_info(netdev, "%s loopback test in progress\n",
+                   mode == QLCNIC_ILB_MODE ? "internal" : "external");
 
        ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST,
                                         max_sds_rings);
@@ -1610,13 +1679,19 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
 
        /* Poll for link up event before running traffic */
        do {
-               msleep(500);
+               msleep(QLC_83XX_LB_MSLEEP_COUNT);
                if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
                        qlcnic_83xx_process_aen(adapter);
 
-               if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
-                       dev_info(&adapter->pdev->dev,
-                                "Firmware didn't sent link up event to loopback request\n");
+               if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
+                       netdev_info(netdev,
+                                   "Device is resetting, free LB test resources\n");
+                       ret = -EIO;
+                       goto free_diag_res;
+               }
+               if (loop++ > QLC_83XX_LB_WAIT_COUNT) {
+                       netdev_info(netdev,
+                                   "Firmware didn't sent link up event to loopback request\n");
                        ret = -QLCNIC_FW_NOT_RESPOND;
                        qlcnic_83xx_clear_lb_mode(adapter, mode);
                        goto free_diag_res;
@@ -1638,13 +1713,14 @@ free_diag_res:
 
 fail_diag_alloc:
        adapter->max_sds_rings = max_sds_rings;
-       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+       qlcnic_release_diag_lock(adapter);
        return ret;
 }
 
 int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 {
        struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct net_device *netdev = adapter->netdev;
        int status = 0, loop = 0;
        u32 config;
 
@@ -1662,9 +1738,9 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 
        status = qlcnic_83xx_set_port_config(adapter);
        if (status) {
-               dev_err(&adapter->pdev->dev,
-                       "Failed to Set Loopback Mode = 0x%x.\n",
-                       ahw->port_config);
+               netdev_err(netdev,
+                          "Failed to Set Loopback Mode = 0x%x.\n",
+                          ahw->port_config);
                ahw->port_config = config;
                clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
                return status;
@@ -1672,13 +1748,19 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 
        /* Wait for Link and IDC Completion AEN */
        do {
-               msleep(300);
+               msleep(QLC_83XX_LB_MSLEEP_COUNT);
                if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
                        qlcnic_83xx_process_aen(adapter);
 
-               if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
-                       dev_err(&adapter->pdev->dev,
-                               "FW did not generate IDC completion AEN\n");
+               if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
+                       netdev_info(netdev,
+                                   "Device is resetting, free LB test resources\n");
+                       clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
+                       return -EIO;
+               }
+               if (loop++ > QLC_83XX_LB_WAIT_COUNT) {
+                       netdev_err(netdev,
+                                  "Did not receive IDC completion AEN\n");
                        clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
                        qlcnic_83xx_clear_lb_mode(adapter, mode);
                        return -EIO;
@@ -1693,6 +1775,7 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 {
        struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct net_device *netdev = adapter->netdev;
        int status = 0, loop = 0;
        u32 config = ahw->port_config;
 
@@ -1704,9 +1787,9 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 
        status = qlcnic_83xx_set_port_config(adapter);
        if (status) {
-               dev_err(&adapter->pdev->dev,
-                       "Failed to Clear Loopback Mode = 0x%x.\n",
-                       ahw->port_config);
+               netdev_err(netdev,
+                          "Failed to Clear Loopback Mode = 0x%x.\n",
+                          ahw->port_config);
                ahw->port_config = config;
                clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
                return status;
@@ -1714,13 +1797,20 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 
        /* Wait for Link and IDC Completion AEN */
        do {
-               msleep(300);
+               msleep(QLC_83XX_LB_MSLEEP_COUNT);
                if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
                        qlcnic_83xx_process_aen(adapter);
 
-               if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
-                       dev_err(&adapter->pdev->dev,
-                               "Firmware didn't sent IDC completion AEN\n");
+               if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
+                       netdev_info(netdev,
+                                   "Device is resetting, free LB test resources\n");
+                       clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
+                       return -EIO;
+               }
+
+               if (loop++ > QLC_83XX_LB_WAIT_COUNT) {
+                       netdev_err(netdev,
+                                  "Did not receive IDC completion AEN\n");
                        clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
                        return -EIO;
                }
@@ -1749,7 +1839,11 @@ void qlcnic_83xx_config_ipaddr(struct qlcnic_adapter *adapter, __be32 ip,
        u32 temp = 0, temp_ip;
        struct qlcnic_cmd_args cmd;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_IP_ADDR);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_CONFIGURE_IP_ADDR);
+       if (err)
+               return;
+
        qlcnic_83xx_set_interface_id_ipaddr(adapter, &temp);
 
        if (mode == QLCNIC_IP_UP)
@@ -1788,7 +1882,10 @@ int qlcnic_83xx_config_hw_lro(struct qlcnic_adapter *adapter, int mode)
        if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)
                return 0;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_HW_LRO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_HW_LRO);
+       if (err)
+               return err;
+
        temp = adapter->recv_ctx->context_id << 16;
        arg1 = lro_bit_mask | temp;
        cmd.req.arg[1] = arg1;
@@ -1810,8 +1907,9 @@ int qlcnic_83xx_config_rss(struct qlcnic_adapter *adapter, int enable)
                            0xae7b30b4d0ca2bcbULL, 0x43a38fb04167253dULL,
                            0x255b0ec26d5a56daULL };
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_RSS);
-
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_RSS);
+       if (err)
+               return err;
        /*
         * RSS request:
         * bits 3-0: Rsvd
@@ -1917,7 +2015,10 @@ int qlcnic_83xx_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac)
        struct qlcnic_cmd_args cmd;
        u32 mac_low, mac_high;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS);
+       if (err)
+               return err;
+
        qlcnic_83xx_configure_mac(adapter, mac, QLCNIC_GET_CURRENT_MAC, &cmd);
        err = qlcnic_issue_cmd(adapter, &cmd);
 
@@ -1948,7 +2049,10 @@ void qlcnic_83xx_config_intr_coal(struct qlcnic_adapter *adapter)
        if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)
                return;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTR_COAL);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTR_COAL);
+       if (err)
+               return;
+
        if (coal->type == QLCNIC_INTR_COAL_TYPE_RX) {
                temp = adapter->recv_ctx->context_id;
                cmd.req.arg[1] = QLCNIC_INTR_COAL_TYPE_RX | temp << 16;
@@ -2020,7 +2124,10 @@ int qlcnic_enable_eswitch(struct qlcnic_adapter *adapter, u8 port, u8 enable)
                return err;
        }
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_TOGGLE_ESWITCH);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_TOGGLE_ESWITCH);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = (port & 0xf) | BIT_4;
        err = qlcnic_issue_cmd(adapter, &cmd);
 
@@ -2048,7 +2155,10 @@ int qlcnic_83xx_set_nic_info(struct qlcnic_adapter *adapter,
                return err;
        }
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = (nic->pci_func << 16);
        cmd.req.arg[2] = 0x1 << 16;
        cmd.req.arg[3] = nic->phys_port | (nic->switch_mode << 16);
@@ -2079,13 +2189,17 @@ int qlcnic_83xx_get_nic_info(struct qlcnic_adapter *adapter,
        u32 temp;
        u8 op = 0;
        struct qlcnic_cmd_args cmd;
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO);
-       if (func_id != adapter->ahw->pci_func) {
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO);
+       if (err)
+               return err;
+
+       if (func_id != ahw->pci_func) {
                temp = func_id << 16;
                cmd.req.arg[1] = op | BIT_31 | temp;
        } else {
-               cmd.req.arg[1] = adapter->ahw->pci_func << 16;
+               cmd.req.arg[1] = ahw->pci_func << 16;
        }
        err = qlcnic_issue_cmd(adapter, &cmd);
        if (err) {
@@ -2112,6 +2226,9 @@ int qlcnic_83xx_get_nic_info(struct qlcnic_adapter *adapter,
                temp = (cmd.rsp.arg[8] & 0x7FFE0000) >> 17;
                npar_info->max_linkspeed_reg_offset = temp;
        }
+       if (npar_info->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS)
+               memcpy(ahw->extra_capability, &cmd.rsp.arg[16],
+                      sizeof(ahw->extra_capability));
 
 out:
        qlcnic_free_mbx_args(&cmd);
@@ -2121,26 +2238,28 @@ out:
 int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter,
                             struct qlcnic_pci_info *pci_info)
 {
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct device *dev = &adapter->pdev->dev;
+       struct qlcnic_cmd_args cmd;
        int i, err = 0, j = 0;
        u32 temp;
-       struct qlcnic_cmd_args cmd;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO);
+       if (err)
+               return err;
+
        err = qlcnic_issue_cmd(adapter, &cmd);
 
-       adapter->ahw->act_pci_func = 0;
+       ahw->act_pci_func = 0;
        if (err == QLCNIC_RCODE_SUCCESS) {
-               pci_info->func_count = cmd.rsp.arg[1] & 0xFF;
-               dev_info(&adapter->pdev->dev,
-                        "%s: total functions = %d\n",
-                        __func__, pci_info->func_count);
+               ahw->max_pci_func = cmd.rsp.arg[1] & 0xFF;
                for (i = 2, j = 0; j < QLCNIC_MAX_PCI_FUNC; j++, pci_info++) {
                        pci_info->id = cmd.rsp.arg[i] & 0xFFFF;
                        pci_info->active = (cmd.rsp.arg[i] & 0xFFFF0000) >> 16;
                        i++;
                        pci_info->type = cmd.rsp.arg[i] & 0xFFFF;
                        if (pci_info->type == QLCNIC_TYPE_NIC)
-                               adapter->ahw->act_pci_func++;
+                               ahw->act_pci_func++;
                        temp = (cmd.rsp.arg[i] & 0xFFFF0000) >> 16;
                        pci_info->default_port = temp;
                        i++;
@@ -2152,18 +2271,21 @@ int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter,
                        i++;
                        memcpy(pci_info->mac + sizeof(u32), &cmd.rsp.arg[i], 2);
                        i = i + 3;
-
-                       dev_info(&adapter->pdev->dev, "%s:\n"
-                                "\tid = %d active = %d type = %d\n"
-                                "\tport = %d min bw = %d max bw = %d\n"
-                                "\tmac_addr =  %pM\n", __func__,
-                                pci_info->id, pci_info->active, pci_info->type,
-                                pci_info->default_port, pci_info->tx_min_bw,
-                                pci_info->tx_max_bw, pci_info->mac);
+                       if (ahw->op_mode == QLCNIC_MGMT_FUNC)
+                               dev_info(dev, "id = %d active = %d type = %d\n"
+                                        "\tport = %d min bw = %d max bw = %d\n"
+                                        "\tmac_addr =  %pM\n", pci_info->id,
+                                        pci_info->active, pci_info->type,
+                                        pci_info->default_port,
+                                        pci_info->tx_min_bw,
+                                        pci_info->tx_max_bw, pci_info->mac);
                }
+               if (ahw->op_mode == QLCNIC_MGMT_FUNC)
+                       dev_info(dev, "Max vNIC functions = %d, active vNIC functions = %d\n",
+                                ahw->max_pci_func, ahw->act_pci_func);
+
        } else {
-               dev_err(&adapter->pdev->dev, "Failed to get PCI Info%d\n",
-                       err);
+               dev_err(dev, "Failed to get PCI Info, error = %d\n", err);
                err = -EIO;
        }
 
@@ -2180,7 +2302,10 @@ int qlcnic_83xx_config_intrpt(struct qlcnic_adapter *adapter, bool op_type)
        struct qlcnic_cmd_args cmd;
 
        max_ints = adapter->ahw->num_msix - 1;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTRPT);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTRPT);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = max_ints;
 
        if (qlcnic_sriov_vf_check(adapter))
@@ -2808,7 +2933,11 @@ int qlcnic_83xx_test_link(struct qlcnic_adapter *adapter)
                dev_info(&adapter->pdev->dev, "link state down\n");
                return config;
        }
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_STATUS);
+
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_STATUS);
+       if (err)
+               return err;
+
        err = qlcnic_issue_cmd(adapter, &cmd);
        if (err) {
                dev_info(&adapter->pdev->dev,
@@ -3034,7 +3163,9 @@ void qlcnic_83xx_get_stats(struct qlcnic_adapter *adapter, u64 *data)
        struct net_device *netdev = adapter->netdev;
        int ret = 0;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_STATISTICS);
+       ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_STATISTICS);
+       if (ret)
+               return;
        /* Get Tx stats */
        cmd.req.arg[1] = BIT_1 | (adapter->tx_ring->ctx_id << 16);
        cmd.rsp.num = QLC_83XX_TX_STAT_REGS;
@@ -3113,8 +3244,10 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
        u8 val;
        int ret, max_sds_rings = adapter->max_sds_rings;
 
-       if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
-               return -EIO;
+       if (qlcnic_get_diag_lock(adapter)) {
+               netdev_info(netdev, "Device in diagnostics mode\n");
+               return -EBUSY;
+       }
 
        ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_INTERRUPT_TEST,
                                         max_sds_rings);
@@ -3122,7 +3255,9 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
                goto fail_diag_irq;
 
        ahw->diag_cnt = 0;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST);
+       ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST);
+       if (ret)
+               goto fail_diag_irq;
 
        if (adapter->flags & QLCNIC_MSIX_ENABLED)
                intrpt_id = ahw->intr_tbl[0].id;
@@ -3156,7 +3291,7 @@ done:
 
 fail_diag_irq:
        adapter->max_sds_rings = max_sds_rings;
-       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+       qlcnic_release_diag_lock(adapter);
        return ret;
 }
 
@@ -3260,3 +3395,54 @@ int qlcnic_83xx_flash_test(struct qlcnic_adapter *adapter)
        }
        return 0;
 }
+
+int qlcnic_83xx_shutdown(struct pci_dev *pdev)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
+       struct net_device *netdev = adapter->netdev;
+       int retval;
+
+       netif_device_detach(netdev);
+       qlcnic_cancel_idc_work(adapter);
+
+       if (netif_running(netdev))
+               qlcnic_down(adapter, netdev);
+
+       qlcnic_83xx_disable_mbx_intr(adapter);
+       cancel_delayed_work_sync(&adapter->idc_aen_work);
+
+       retval = pci_save_state(pdev);
+       if (retval)
+               return retval;
+
+       return 0;
+}
+
+int qlcnic_83xx_resume(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct qlc_83xx_idc *idc = &ahw->idc;
+       int err = 0;
+
+       err = qlcnic_83xx_idc_init(adapter);
+       if (err)
+               return err;
+
+       if (ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE) {
+               if (ahw->op_mode == QLCNIC_MGMT_FUNC) {
+                       qlcnic_83xx_set_vnic_opmode(adapter);
+               } else {
+                       err = qlcnic_83xx_check_vnic_state(adapter);
+                       if (err)
+                               return err;
+               }
+       }
+
+       err = qlcnic_83xx_idc_reattach_driver(adapter);
+       if (err)
+               return err;
+
+       qlcnic_schedule_work(adapter, qlcnic_83xx_idc_poll_dev_state,
+                            idc->delay);
+       return err;
+}
index f5db67f..2548d14 100644 (file)
@@ -36,7 +36,8 @@
 #define QLC_83XX_MAX_DRV_LOCK_RECOVERY_ATTEMPT         3
 #define QLC_83XX_DRV_LOCK_RECOVERY_DELAY               200
 #define QLC_83XX_DRV_LOCK_RECOVERY_STATUS_MASK         0x3
-
+#define QLC_83XX_LB_WAIT_COUNT                         250
+#define QLC_83XX_LB_MSLEEP_COUNT                       20
 #define QLC_83XX_NO_NIC_RESOURCE       0x5
 #define QLC_83XX_MAC_PRESENT           0xC
 #define QLC_83XX_MAC_ABSENT            0xD
@@ -314,6 +315,7 @@ struct qlc_83xx_idc {
        u8              vnic_state;
        u8              vnic_wait_limit;
        u8              quiesce_req;
+       u8              delay_reset;
        char            **name;
 };
 
@@ -392,6 +394,8 @@ enum qlcnic_83xx_states {
 #define QLC_83XX_LB_MAX_FILTERS                        2048
 #define QLC_83XX_LB_BUCKET_SIZE                        256
 #define QLC_83XX_MINIMUM_VECTOR                        3
+#define QLC_83XX_MAX_MC_COUNT                  38
+#define QLC_83XX_MAX_UC_COUNT                  4096
 
 #define QLC_83XX_GET_FUNC_MODE_FROM_NPAR_INFO(val)     (val & 0x80000000)
 #define QLC_83XX_GET_LRO_CAPABILITY(val)               (val & 0x20)
@@ -623,4 +627,11 @@ u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *);
 u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *, u32 *);
 void qlcnic_83xx_enable_mbx_poll(struct qlcnic_adapter *);
 void qlcnic_83xx_disable_mbx_poll(struct qlcnic_adapter *);
+void qlcnic_83xx_set_mac_filter_count(struct qlcnic_adapter *);
+int qlcnic_83xx_shutdown(struct pci_dev *);
+int qlcnic_83xx_resume(struct qlcnic_adapter *);
+int qlcnic_83xx_idc_init(struct qlcnic_adapter *);
+int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *);
+int qlcnic_83xx_set_vnic_opmode(struct qlcnic_adapter *);
+int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *);
 #endif
index 5e7fb1d..f41dfab 100644 (file)
@@ -606,7 +606,7 @@ static int qlcnic_83xx_idc_check_fan_failure(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-static int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter)
 {
        int err;
 
@@ -629,6 +629,7 @@ static int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter)
                return -EIO;
        }
 
+       qlcnic_set_drv_version(adapter);
        qlcnic_83xx_idc_attach_driver(adapter);
 
        return 0;
@@ -649,6 +650,7 @@ static void qlcnic_83xx_idc_update_idc_params(struct qlcnic_adapter *adapter)
        ahw->idc.collect_dump = 0;
        ahw->reset_context = 0;
        adapter->tx_timeo_cnt = 0;
+       ahw->idc.delay_reset = 0;
 
        clear_bit(__QLCNIC_RESETTING, &adapter->state);
 }
@@ -883,21 +885,41 @@ static int qlcnic_83xx_idc_need_reset_state(struct qlcnic_adapter *adapter)
        int ret = 0;
 
        if (adapter->ahw->idc.prev_state != QLC_83XX_IDC_DEV_NEED_RESET) {
-               qlcnic_83xx_idc_update_drv_ack_reg(adapter, 1, 1);
                qlcnic_83xx_idc_update_audit_reg(adapter, 0, 1);
                set_bit(__QLCNIC_RESETTING, &adapter->state);
                clear_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status);
                if (adapter->ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE)
                        qlcnic_83xx_disable_vnic_mode(adapter, 1);
-               qlcnic_83xx_idc_detach_driver(adapter);
+
+               if (qlcnic_check_diag_status(adapter)) {
+                       dev_info(&adapter->pdev->dev,
+                                "%s: Wait for diag completion\n", __func__);
+                       adapter->ahw->idc.delay_reset = 1;
+                       return 0;
+               } else {
+                       qlcnic_83xx_idc_update_drv_ack_reg(adapter, 1, 1);
+                       qlcnic_83xx_idc_detach_driver(adapter);
+               }
        }
 
-       /* Check ACK from other functions */
-       ret = qlcnic_83xx_idc_check_reset_ack_reg(adapter);
-       if (ret) {
+       if (qlcnic_check_diag_status(adapter)) {
                dev_info(&adapter->pdev->dev,
-                        "%s: Waiting for reset ACK\n", __func__);
-               return 0;
+                        "%s: Wait for diag completion\n", __func__);
+               return  -1;
+       } else {
+               if (adapter->ahw->idc.delay_reset) {
+                       qlcnic_83xx_idc_update_drv_ack_reg(adapter, 1, 1);
+                       qlcnic_83xx_idc_detach_driver(adapter);
+                       adapter->ahw->idc.delay_reset = 0;
+               }
+
+               /* Check for ACK from other functions */
+               ret = qlcnic_83xx_idc_check_reset_ack_reg(adapter);
+               if (ret) {
+                       dev_info(&adapter->pdev->dev,
+                                "%s: Waiting for reset ACK\n", __func__);
+                       return -1;
+               }
        }
 
        /* Transit to INIT state and restart the HW */
@@ -1113,7 +1135,7 @@ qlcnic_83xx_idc_first_to_load_function_handler(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-static int qlcnic_83xx_idc_init(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_idc_init(struct qlcnic_adapter *adapter)
 {
        int ret = -EIO;
 
@@ -1532,9 +1554,18 @@ static int qlcnic_83xx_reset_template_checksum(struct qlcnic_adapter *p_dev)
 
 int qlcnic_83xx_get_reset_instruction_template(struct qlcnic_adapter *p_dev)
 {
-       u8 *p_buff;
-       u32 addr, count;
        struct qlcnic_hardware_context *ahw = p_dev->ahw;
+       u32 addr, count, prev_ver, curr_ver;
+       u8 *p_buff;
+
+       if (ahw->reset.buff != NULL) {
+               prev_ver = p_dev->fw_version;
+               curr_ver = qlcnic_83xx_get_fw_version(p_dev);
+               if (curr_ver > prev_ver)
+                       kfree(ahw->reset.buff);
+               else
+                       return 0;
+       }
 
        ahw->reset.seq_error = 0;
        ahw->reset.buff = kzalloc(QLC_83XX_RESTART_TEMPLATE_SIZE, GFP_KERNEL);
@@ -2062,7 +2093,11 @@ static void qlcnic_83xx_clear_function_resources(struct qlcnic_adapter *adapter)
        audit_mask = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_AUDIT);
 
        if (IS_QLC_83XX_USED(adapter, presence_mask, audit_mask)) {
-               qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_STOP_NIC_FUNC);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_STOP_NIC_FUNC);
+               if (status)
+                       return;
+
                cmd.req.arg[1] = BIT_31;
                status = qlcnic_issue_cmd(adapter, &cmd);
                if (status)
index b0c3de9..599d1fd 100644 (file)
@@ -39,30 +39,21 @@ int qlcnic_83xx_disable_vnic_mode(struct qlcnic_adapter *adapter, int lock)
        return 0;
 }
 
-static int qlcnic_83xx_set_vnic_opmode(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_set_vnic_opmode(struct qlcnic_adapter *adapter)
 {
        u8 id;
-       int i, ret = -EBUSY;
+       int ret = -EBUSY;
        u32 data = QLCNIC_MGMT_FUNC;
        struct qlcnic_hardware_context *ahw = adapter->ahw;
 
        if (qlcnic_83xx_lock_driver(adapter))
                return ret;
 
-       if (qlcnic_config_npars) {
-               for (i = 0; i < ahw->act_pci_func; i++) {
-                       id = adapter->npars[i].pci_func;
-                       if (id == ahw->pci_func)
-                               continue;
-                       data |= qlcnic_config_npars &
-                               QLC_83XX_SET_FUNC_OPMODE(0x3, id);
-               }
-       } else {
-               data = QLCRDX(adapter->ahw, QLC_83XX_DRV_OP_MODE);
-               data = (data & ~QLC_83XX_SET_FUNC_OPMODE(0x3, ahw->pci_func)) |
-                      QLC_83XX_SET_FUNC_OPMODE(QLCNIC_MGMT_FUNC,
-                                               ahw->pci_func);
-       }
+       id = ahw->pci_func;
+       data = QLCRDX(adapter->ahw, QLC_83XX_DRV_OP_MODE);
+       data = (data & ~QLC_83XX_SET_FUNC_OPMODE(0x3, id)) |
+              QLC_83XX_SET_FUNC_OPMODE(QLCNIC_MGMT_FUNC, id);
+
        QLCWRX(adapter->ahw, QLC_83XX_DRV_OP_MODE, data);
 
        qlcnic_83xx_unlock_driver(adapter);
@@ -196,20 +187,24 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *adapter)
        else
                priv_level = QLC_83XX_GET_FUNC_PRIVILEGE(op_mode,
                                                         ahw->pci_func);
-
-       if (priv_level == QLCNIC_NON_PRIV_FUNC) {
+       switch (priv_level) {
+       case QLCNIC_NON_PRIV_FUNC:
                ahw->op_mode = QLCNIC_NON_PRIV_FUNC;
                ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry;
                nic_ops->init_driver = qlcnic_83xx_init_non_privileged_vnic;
-       } else if (priv_level == QLCNIC_PRIV_FUNC) {
+               break;
+       case QLCNIC_PRIV_FUNC:
                ahw->op_mode = QLCNIC_PRIV_FUNC;
                ahw->idc.state_entry = qlcnic_83xx_idc_vnic_pf_entry;
                nic_ops->init_driver = qlcnic_83xx_init_privileged_vnic;
-       } else if (priv_level == QLCNIC_MGMT_FUNC) {
+               break;
+       case QLCNIC_MGMT_FUNC:
                ahw->op_mode = QLCNIC_MGMT_FUNC;
                ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry;
                nic_ops->init_driver = qlcnic_83xx_init_mgmt_vnic;
-       } else {
+               break;
+       default:
+               dev_err(&adapter->pdev->dev, "Invalid Virtual NIC opmode\n");
                return -EIO;
        }
 
@@ -218,8 +213,29 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *adapter)
        else
                adapter->flags &= ~QLCNIC_ESWITCH_ENABLED;
 
-       adapter->ahw->idc.vnic_state = QLCNIC_DEV_NPAR_NON_OPER;
-       adapter->ahw->idc.vnic_wait_limit = QLCNIC_DEV_NPAR_OPER_TIMEO;
+       ahw->idc.vnic_state = QLCNIC_DEV_NPAR_NON_OPER;
+       ahw->idc.vnic_wait_limit = QLCNIC_DEV_NPAR_OPER_TIMEO;
+
+       return 0;
+}
+
+int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct qlc_83xx_idc *idc = &ahw->idc;
+       u32 state;
+
+       state = QLCRDX(ahw, QLC_83XX_VNIC_STATE);
+       while (state != QLCNIC_DEV_NPAR_OPER && idc->vnic_wait_limit--) {
+               msleep(1000);
+               state = QLCRDX(ahw, QLC_83XX_VNIC_STATE);
+       }
+
+       if (!idc->vnic_wait_limit) {
+               dev_err(&adapter->pdev->dev,
+                       "vNIC mode not operational, state check timed out.\n");
+               return -EIO;
+       }
 
        return 0;
 }
index 6acf82b..0581a48 100644 (file)
@@ -36,7 +36,8 @@ static const struct qlcnic_mailbox_metadata qlcnic_mbx_tbl[] = {
        {QLCNIC_CMD_CONFIG_PORT, 4, 1},
        {QLCNIC_CMD_TEMP_SIZE, 4, 4},
        {QLCNIC_CMD_GET_TEMP_HDR, 4, 1},
-       {QLCNIC_CMD_SET_DRV_VER, 4, 1},
+       {QLCNIC_CMD_82XX_SET_DRV_VER, 4, 1},
+       {QLCNIC_CMD_GET_LED_STATUS, 4, 2},
 };
 
 static inline u32 qlcnic_get_cmd_signature(struct qlcnic_hardware_context *ahw)
@@ -181,7 +182,7 @@ int qlcnic_82xx_issue_cmd(struct qlcnic_adapter *adapter,
        return cmd->rsp.arg[0];
 }
 
-int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *adapter)
+int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *adapter, u32 fw_cmd)
 {
        struct qlcnic_cmd_args cmd;
        u32 arg1, arg2, arg3;
@@ -193,7 +194,10 @@ int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *adapter)
                 _QLCNIC_LINUX_MAJOR, _QLCNIC_LINUX_MINOR,
                 _QLCNIC_LINUX_SUBVERSION);
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_DRV_VER);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, fw_cmd);
+       if (err)
+               return err;
+
        memcpy(&arg1, drv_string, sizeof(u32));
        memcpy(&arg2, drv_string + 4, sizeof(u32));
        memcpy(&arg3, drv_string + 8, sizeof(u32));
@@ -221,7 +225,10 @@ qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu)
 
        if (recv_ctx->state != QLCNIC_HOST_CTX_STATE_ACTIVE)
                return err;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_MTU);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_MTU);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = recv_ctx->context_id;
        cmd.req.arg[2] = mtu;
 
@@ -335,7 +342,10 @@ int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
        }
 
        phys_addr = hostrq_phys_addr;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_RX_CTX);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_RX_CTX);
+       if (err)
+               goto out_free_rsp;
+
        cmd.req.arg[1] = MSD(phys_addr);
        cmd.req.arg[2] = LSD(phys_addr);
        cmd.req.arg[3] = rq_size;
@@ -373,10 +383,10 @@ int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
        recv_ctx->context_id = le16_to_cpu(prsp->context_id);
        recv_ctx->virt_port = prsp->virt_port;
 
+       qlcnic_free_mbx_args(&cmd);
 out_free_rsp:
        dma_free_coherent(&adapter->pdev->dev, rsp_size, prsp,
-               cardrsp_phys_addr);
-       qlcnic_free_mbx_args(&cmd);
+                         cardrsp_phys_addr);
 out_free_rq:
        dma_free_coherent(&adapter->pdev->dev, rq_size, prq, hostrq_phys_addr);
        return err;
@@ -388,7 +398,10 @@ void qlcnic_82xx_fw_cmd_del_rx_ctx(struct qlcnic_adapter *adapter)
        struct qlcnic_cmd_args cmd;
        struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX);
+       if (err)
+               return;
+
        cmd.req.arg[1] = recv_ctx->context_id;
        err = qlcnic_issue_cmd(adapter, &cmd);
        if (err)
@@ -457,7 +470,10 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,
 
        phys_addr = rq_phys_addr;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX);
+       if (err)
+               goto out_free_rsp;
+
        cmd.req.arg[1] = MSD(phys_addr);
        cmd.req.arg[2] = LSD(phys_addr);
        cmd.req.arg[3] = rq_size;
@@ -473,12 +489,13 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,
                err = -EIO;
        }
 
+       qlcnic_free_mbx_args(&cmd);
+
+out_free_rsp:
        dma_free_coherent(&adapter->pdev->dev, rsp_size, rsp_addr,
                          rsp_phys_addr);
-
 out_free_rq:
        dma_free_coherent(&adapter->pdev->dev, rq_size, rq_addr, rq_phys_addr);
-       qlcnic_free_mbx_args(&cmd);
 
        return err;
 }
@@ -487,8 +504,11 @@ void qlcnic_82xx_fw_cmd_del_tx_ctx(struct qlcnic_adapter *adapter,
                                   struct qlcnic_host_tx_ring *tx_ring)
 {
        struct qlcnic_cmd_args cmd;
+       int ret;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX);
+       ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX);
+       if (ret)
+               return;
 
        cmd.req.arg[1] = tx_ring->ctx_id;
        if (qlcnic_issue_cmd(adapter, &cmd))
@@ -503,7 +523,10 @@ qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config)
        int err;
        struct qlcnic_cmd_args cmd;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_PORT);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_PORT);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = config;
        err = qlcnic_issue_cmd(adapter, &cmd);
        qlcnic_free_mbx_args(&cmd);
@@ -707,7 +730,10 @@ int qlcnic_82xx_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac)
        struct qlcnic_cmd_args cmd;
        u32 mac_low, mac_high;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = adapter->ahw->pci_func | BIT_8;
        err = qlcnic_issue_cmd(adapter, &cmd);
 
@@ -746,7 +772,10 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter,
 
        nic_info = nic_info_addr;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO);
+       if (err)
+               goto out_free_dma;
+
        cmd.req.arg[1] = MSD(nic_dma_t);
        cmd.req.arg[2] = LSD(nic_dma_t);
        cmd.req.arg[3] = (func_id << 16 | nic_size);
@@ -768,9 +797,10 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter,
                npar_info->max_mtu = le16_to_cpu(nic_info->max_mtu);
        }
 
+       qlcnic_free_mbx_args(&cmd);
+out_free_dma:
        dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr,
                          nic_dma_t);
-       qlcnic_free_mbx_args(&cmd);
 
        return err;
 }
@@ -807,7 +837,10 @@ int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *adapter,
        nic_info->min_tx_bw = cpu_to_le16(nic->min_tx_bw);
        nic_info->max_tx_bw = cpu_to_le16(nic->max_tx_bw);
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO);
+       if (err)
+               goto out_free_dma;
+
        cmd.req.arg[1] = MSD(nic_dma_t);
        cmd.req.arg[2] = LSD(nic_dma_t);
        cmd.req.arg[3] = ((nic->pci_func << 16) | nic_size);
@@ -819,9 +852,10 @@ int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *adapter,
                err = -EIO;
        }
 
-       dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr,
-               nic_dma_t);
        qlcnic_free_mbx_args(&cmd);
+out_free_dma:
+       dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr,
+                         nic_dma_t);
 
        return err;
 }
@@ -845,7 +879,10 @@ int qlcnic_82xx_get_pci_info(struct qlcnic_adapter *adapter,
                return -ENOMEM;
 
        npar = pci_info_addr;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO);
+       if (err)
+               goto out_free_dma;
+
        cmd.req.arg[1] = MSD(pci_info_dma_t);
        cmd.req.arg[2] = LSD(pci_info_dma_t);
        cmd.req.arg[3] = pci_size;
@@ -873,20 +910,22 @@ int qlcnic_82xx_get_pci_info(struct qlcnic_adapter *adapter,
                err = -EIO;
        }
 
+       qlcnic_free_mbx_args(&cmd);
+out_free_dma:
        dma_free_coherent(&adapter->pdev->dev, pci_size, pci_info_addr,
                pci_info_dma_t);
-       qlcnic_free_mbx_args(&cmd);
 
        return err;
 }
 
 /* Configure eSwitch for port mirroring */
 int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id,
-                               u8 enable_mirroring, u8 pci_func)
+                                u8 enable_mirroring, u8 pci_func)
 {
+       struct device *dev = &adapter->pdev->dev;
+       struct qlcnic_cmd_args cmd;
        int err = -EIO;
        u32 arg1;
-       struct qlcnic_cmd_args cmd;
 
        if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC ||
            !(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE))
@@ -895,18 +934,20 @@ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id,
        arg1 = id | (enable_mirroring ? BIT_4 : 0);
        arg1 |= pci_func << 8;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_PORTMIRRORING);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_SET_PORTMIRRORING);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = arg1;
        err = qlcnic_issue_cmd(adapter, &cmd);
 
        if (err != QLCNIC_RCODE_SUCCESS)
-               dev_err(&adapter->pdev->dev,
-                       "Failed to configure port mirroring%d on eswitch:%d\n",
+               dev_err(dev, "Failed to configure port mirroring for vNIC function %d on eSwitch %d\n",
                        pci_func, id);
        else
-               dev_info(&adapter->pdev->dev,
-                       "Configured eSwitch %d for port mirroring:%d\n",
-                       id, pci_func);
+               dev_info(dev, "Configured port mirroring for vNIC function %d on eSwitch %d\n",
+                        pci_func, id);
        qlcnic_free_mbx_args(&cmd);
 
        return err;
@@ -941,7 +982,11 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
        arg1 = func | QLCNIC_STATS_VERSION << 8 | QLCNIC_STATS_PORT << 12;
        arg1 |= rx_tx << 15 | stats_size << 16;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_ESWITCH_STATS);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_GET_ESWITCH_STATS);
+       if (err)
+               goto out_free_dma;
+
        cmd.req.arg[1] = arg1;
        cmd.req.arg[2] = MSD(stats_dma_t);
        cmd.req.arg[3] = LSD(stats_dma_t);
@@ -963,9 +1008,10 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
                esw_stats->numbytes = le64_to_cpu(stats->numbytes);
        }
 
-       dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
-               stats_dma_t);
        qlcnic_free_mbx_args(&cmd);
+out_free_dma:
+       dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
+                         stats_dma_t);
 
        return err;
 }
@@ -989,7 +1035,10 @@ int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter,
        if (!stats_addr)
                return -ENOMEM;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_MAC_STATS);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_MAC_STATS);
+       if (err)
+               goto out_free_dma;
+
        cmd.req.arg[1] = stats_size << 16;
        cmd.req.arg[2] = MSD(stats_dma_t);
        cmd.req.arg[3] = LSD(stats_dma_t);
@@ -1020,11 +1069,12 @@ int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter,
                        "%s: Get mac stats failed, err=%d.\n", __func__, err);
        }
 
-       dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
-               stats_dma_t);
-
        qlcnic_free_mbx_args(&cmd);
 
+out_free_dma:
+       dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
+                         stats_dma_t);
+
        return err;
 }
 
@@ -1108,7 +1158,11 @@ int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw,
        arg1 = port | QLCNIC_STATS_VERSION << 8 | func_esw << 12;
        arg1 |= BIT_14 | rx_tx << 15;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_ESWITCH_STATS);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_GET_ESWITCH_STATS);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = arg1;
        err = qlcnic_issue_cmd(adapter, &cmd);
        qlcnic_free_mbx_args(&cmd);
@@ -1121,17 +1175,19 @@ err_ret:
        return -EIO;
 }
 
-static int
-__qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter,
-                                       u32 *arg1, u32 *arg2)
+static int __qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter,
+                                           u32 *arg1, u32 *arg2)
 {
-       int err = -EIO;
+       struct device *dev = &adapter->pdev->dev;
        struct qlcnic_cmd_args cmd;
-       u8 pci_func;
-       pci_func = (*arg1 >> 8);
+       u8 pci_func = *arg1 >> 8;
+       int err;
+
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_GET_ESWITCH_PORT_CONFIG);
+       if (err)
+               return err;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter,
-                             QLCNIC_CMD_GET_ESWITCH_PORT_CONFIG);
        cmd.req.arg[1] = *arg1;
        err = qlcnic_issue_cmd(adapter, &cmd);
        *arg1 = cmd.rsp.arg[1];
@@ -1139,12 +1195,11 @@ __qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter,
        qlcnic_free_mbx_args(&cmd);
 
        if (err == QLCNIC_RCODE_SUCCESS)
-               dev_info(&adapter->pdev->dev,
-                        "eSwitch port config for pci func %d\n", pci_func);
+               dev_info(dev, "Get eSwitch port config for vNIC function %d\n",
+                        pci_func);
        else
-               dev_err(&adapter->pdev->dev,
-                       "Failed to get eswitch port config for pci func %d\n",
-                                                               pci_func);
+               dev_err(dev, "Failed to get eswitch port config for vNIC function %d\n",
+                       pci_func);
        return err;
 }
 /* Configure eSwitch port
@@ -1157,9 +1212,10 @@ op_type = 1 for port vlan_id
 int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
                struct qlcnic_esw_func_cfg *esw_cfg)
 {
+       struct device *dev = &adapter->pdev->dev;
+       struct qlcnic_cmd_args cmd;
        int err = -EIO, index;
        u32 arg1, arg2 = 0;
-       struct qlcnic_cmd_args cmd;
        u8 pci_func;
 
        if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC)
@@ -1209,18 +1265,22 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
                return err;
        }
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_ESWITCH);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_CONFIGURE_ESWITCH);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = arg1;
        cmd.req.arg[2] = arg2;
        err = qlcnic_issue_cmd(adapter, &cmd);
        qlcnic_free_mbx_args(&cmd);
 
        if (err != QLCNIC_RCODE_SUCCESS)
-               dev_err(&adapter->pdev->dev,
-                       "Failed to configure eswitch pci func %d\n", pci_func);
+               dev_err(dev, "Failed to configure eswitch for vNIC function %d\n",
+                       pci_func);
        else
-               dev_info(&adapter->pdev->dev,
-                        "Configured eSwitch for pci func %d\n", pci_func);
+               dev_info(dev, "Configured eSwitch for vNIC function %d\n",
+                        pci_func);
 
        return err;
 }
index f67652d..700a463 100644 (file)
@@ -846,7 +846,9 @@ static int qlcnic_irq_test(struct net_device *netdev)
                goto clear_diag_irq;
 
        ahw->diag_cnt = 0;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST);
+       ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST);
+       if (ret)
+               goto free_diag_res;
 
        cmd.req.arg[1] = ahw->pci_func;
        ret = qlcnic_issue_cmd(adapter, &cmd);
@@ -858,6 +860,8 @@ static int qlcnic_irq_test(struct net_device *netdev)
 
 done:
        qlcnic_free_mbx_args(&cmd);
+
+free_diag_res:
        qlcnic_diag_free_res(netdev, max_sds_rings);
 
 clear_diag_irq:
index c0f0c0d..d262211 100644 (file)
@@ -672,6 +672,7 @@ enum {
 #define QLCNIC_HEARTBEAT_CHECK_RETRY_COUNT     10
 
 #define QLCNIC_MAX_MC_COUNT            38
+#define QLCNIC_MAX_UC_COUNT            512
 #define QLCNIC_WATCHDOG_TIMEOUTVALUE   5
 
 #define        ISR_MSI_INT_TRIGGER(FUNC) (QLCNIC_PCIX_PS_REG(PCIX_MSI_F(FUNC)))
index 106a12f..5b5d2ed 100644 (file)
@@ -499,6 +499,7 @@ int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr, u16 vlan)
 void __qlcnic_set_multi(struct net_device *netdev, u16 vlan)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
        struct netdev_hw_addr *ha;
        static const u8 bcast_addr[ETH_ALEN] = {
                0xff, 0xff, 0xff, 0xff, 0xff, 0xff
@@ -515,25 +516,30 @@ void __qlcnic_set_multi(struct net_device *netdev, u16 vlan)
        if (netdev->flags & IFF_PROMISC) {
                if (!(adapter->flags & QLCNIC_PROMISC_DISABLED))
                        mode = VPORT_MISS_MODE_ACCEPT_ALL;
-               goto send_fw_cmd;
-       }
-
-       if ((netdev->flags & IFF_ALLMULTI) ||
-           (netdev_mc_count(netdev) > adapter->ahw->max_mc_count)) {
-               mode = VPORT_MISS_MODE_ACCEPT_MULTI;
-               goto send_fw_cmd;
+       } else if (netdev->flags & IFF_ALLMULTI) {
+               if (netdev_mc_count(netdev) > ahw->max_mc_count) {
+                       mode = VPORT_MISS_MODE_ACCEPT_MULTI;
+               } else if (!netdev_mc_empty(netdev) &&
+                          !qlcnic_sriov_vf_check(adapter)) {
+                               netdev_for_each_mc_addr(ha, netdev)
+                                       qlcnic_nic_add_mac(adapter, ha->addr,
+                                                          vlan);
+               }
+               if (mode != VPORT_MISS_MODE_ACCEPT_MULTI &&
+                   qlcnic_sriov_vf_check(adapter))
+                       qlcnic_vf_add_mc_list(netdev, vlan);
        }
 
-       if (!netdev_mc_empty(netdev) && !qlcnic_sriov_vf_check(adapter)) {
-               netdev_for_each_mc_addr(ha, netdev) {
+       /* configure unicast MAC address, if there is not sufficient space
+        * to store all the unicast addresses then enable promiscuous mode
+        */
+       if (netdev_uc_count(netdev) > ahw->max_uc_count) {
+               mode = VPORT_MISS_MODE_ACCEPT_ALL;
+       } else if (!netdev_uc_empty(netdev)) {
+               netdev_for_each_uc_addr(ha, netdev)
                        qlcnic_nic_add_mac(adapter, ha->addr, vlan);
-               }
        }
 
-       if (qlcnic_sriov_vf_check(adapter))
-               qlcnic_vf_add_mc_list(netdev, vlan);
-
-send_fw_cmd:
        if (!qlcnic_sriov_vf_check(adapter)) {
                if (mode == VPORT_MISS_MODE_ACCEPT_ALL &&
                    !adapter->fdb_mac_learn) {
@@ -780,7 +786,8 @@ int qlcnic_82xx_config_hw_lro(struct qlcnic_adapter *adapter, int enable)
        word = 0;
        if (enable) {
                word = QLCNIC_ENABLE_IPV4_LRO | QLCNIC_NO_DEST_IPV4_CHECK;
-               if (adapter->ahw->capabilities2 & QLCNIC_FW_CAP2_HW_LRO_IPV6)
+               if (adapter->ahw->extra_capability[0] &
+                   QLCNIC_FW_CAP2_HW_LRO_IPV6)
                        word |= QLCNIC_ENABLE_IPV6_LRO |
                                QLCNIC_NO_DEST_IPV6_CHECK;
        }
@@ -1503,6 +1510,21 @@ int qlcnic_82xx_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate)
        return rv;
 }
 
+int qlcnic_get_beacon_state(struct qlcnic_adapter *adapter, u8 *h_state)
+{
+       struct qlcnic_cmd_args cmd;
+       int err;
+
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LED_STATUS);
+       if (!err) {
+               err = qlcnic_issue_cmd(adapter, &cmd);
+               if (!err)
+                       *h_state = cmd.rsp.arg[1];
+       }
+       qlcnic_free_mbx_args(&cmd);
+       return err;
+}
+
 void qlcnic_82xx_get_func_no(struct qlcnic_adapter *adapter)
 {
        void __iomem *msix_base_addr;
@@ -1555,3 +1577,54 @@ void qlcnic_82xx_api_unlock(struct qlcnic_adapter *adapter)
 {
        qlcnic_pcie_sem_unlock(adapter, 5);
 }
+
+int qlcnic_82xx_shutdown(struct pci_dev *pdev)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
+       struct net_device *netdev = adapter->netdev;
+       int retval;
+
+       netif_device_detach(netdev);
+
+       qlcnic_cancel_idc_work(adapter);
+
+       if (netif_running(netdev))
+               qlcnic_down(adapter, netdev);
+
+       qlcnic_clr_all_drv_state(adapter, 0);
+
+       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+
+       retval = pci_save_state(pdev);
+       if (retval)
+               return retval;
+
+       if (qlcnic_wol_supported(adapter)) {
+               pci_enable_wake(pdev, PCI_D3cold, 1);
+               pci_enable_wake(pdev, PCI_D3hot, 1);
+       }
+
+       return 0;
+}
+
+int qlcnic_82xx_resume(struct qlcnic_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+       int err;
+
+       err = qlcnic_start_firmware(adapter);
+       if (err) {
+               dev_err(&adapter->pdev->dev, "failed to start firmware\n");
+               return err;
+       }
+
+       if (netif_running(netdev)) {
+               err = qlcnic_up(adapter, netdev);
+               if (!err)
+                       qlcnic_restore_indev_addr(netdev, NETDEV_UP);
+       }
+
+       netif_device_attach(netdev);
+       qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY);
+       return err;
+}
index b6818f4..2c22504 100644 (file)
@@ -86,7 +86,8 @@ enum qlcnic_regs {
 #define QLCNIC_CMD_BC_EVENT_SETUP              0x31
 #define        QLCNIC_CMD_CONFIG_VPORT                 0x32
 #define QLCNIC_CMD_GET_MAC_STATS               0x37
-#define QLCNIC_CMD_SET_DRV_VER                 0x38
+#define QLCNIC_CMD_82XX_SET_DRV_VER            0x38
+#define QLCNIC_CMD_GET_LED_STATUS              0x3C
 #define QLCNIC_CMD_CONFIGURE_RSS               0x41
 #define QLCNIC_CMD_CONFIG_INTR_COAL            0x43
 #define QLCNIC_CMD_CONFIGURE_LED               0x44
@@ -102,6 +103,7 @@ enum qlcnic_regs {
 #define QLCNIC_CMD_GET_LINK_STATUS             0x68
 #define QLCNIC_CMD_SET_LED_CONFIG              0x69
 #define QLCNIC_CMD_GET_LED_CONFIG              0x6A
+#define QLCNIC_CMD_83XX_SET_DRV_VER            0x6F
 #define QLCNIC_CMD_ADD_RCV_RINGS               0x0B
 
 #define QLCNIC_INTRPT_INTX                     1
@@ -197,4 +199,8 @@ void qlcnic_82xx_api_unlock(struct qlcnic_adapter *);
 void qlcnic_82xx_napi_enable(struct qlcnic_adapter *);
 void qlcnic_82xx_napi_disable(struct qlcnic_adapter *);
 void qlcnic_82xx_napi_del(struct qlcnic_adapter *);
+int qlcnic_82xx_shutdown(struct pci_dev *);
+int qlcnic_82xx_resume(struct qlcnic_adapter *);
+void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed);
+void qlcnic_fw_poll_work(struct work_struct *work);
 #endif                         /* __QLCNIC_HW_H_ */
index aeb26a8..4528f8e 100644 (file)
@@ -52,10 +52,6 @@ int qlcnic_load_fw_file;
 MODULE_PARM_DESC(load_fw_file, "Load firmware from (0=flash, 1=file)");
 module_param_named(load_fw_file, qlcnic_load_fw_file, int, 0444);
 
-int qlcnic_config_npars;
-module_param(qlcnic_config_npars, int, 0444);
-MODULE_PARM_DESC(qlcnic_config_npars, "Configure NPARs (0=disabled, 1=enabled)");
-
 static int qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
 static void qlcnic_remove(struct pci_dev *pdev);
 static int qlcnic_open(struct net_device *netdev);
@@ -63,13 +59,11 @@ static int qlcnic_close(struct net_device *netdev);
 static void qlcnic_tx_timeout(struct net_device *netdev);
 static void qlcnic_attach_work(struct work_struct *work);
 static void qlcnic_fwinit_work(struct work_struct *work);
-static void qlcnic_fw_poll_work(struct work_struct *work);
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void qlcnic_poll_controller(struct net_device *netdev);
 #endif
 
 static void qlcnic_idc_debug_info(struct qlcnic_adapter *adapter, u8 encoding);
-static void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8);
 static int qlcnic_can_start_firmware(struct qlcnic_adapter *adapter);
 
 static irqreturn_t qlcnic_tmp_intr(int irq, void *data);
@@ -364,12 +358,15 @@ static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
                return ndo_dflt_fdb_del(ndm, tb, netdev, addr);
 
        if (adapter->flags & QLCNIC_ESWITCH_ENABLED) {
-               if (is_unicast_ether_addr(addr))
-                       err = qlcnic_nic_del_mac(adapter, addr);
-               else if (is_multicast_ether_addr(addr))
+               if (is_unicast_ether_addr(addr)) {
+                       err = dev_uc_del(netdev, addr);
+                       if (!err)
+                               err = qlcnic_nic_del_mac(adapter, addr);
+               } else if (is_multicast_ether_addr(addr)) {
                        err = dev_mc_del(netdev, addr);
-               else
+               } else {
                        err =  -EINVAL;
+               }
        }
        return err;
 }
@@ -392,12 +389,16 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        if (ether_addr_equal(addr, adapter->mac_addr))
                return err;
 
-       if (is_unicast_ether_addr(addr))
-               err = qlcnic_nic_add_mac(adapter, addr, 0);
-       else if (is_multicast_ether_addr(addr))
+       if (is_unicast_ether_addr(addr)) {
+               if (netdev_uc_count(netdev) < adapter->ahw->max_uc_count)
+                       err = dev_uc_add_excl(netdev, addr);
+               else
+                       err = -ENOMEM;
+       } else if (is_multicast_ether_addr(addr)) {
                err = dev_mc_add_excl(netdev, addr);
-       else
+       } else {
                err = -EINVAL;
+       }
 
        return err;
 }
@@ -449,6 +450,7 @@ static const struct net_device_ops qlcnic_netdev_ops = {
        .ndo_set_vf_tx_rate     = qlcnic_sriov_set_vf_tx_rate,
        .ndo_get_vf_config      = qlcnic_sriov_get_vf_config,
        .ndo_set_vf_vlan        = qlcnic_sriov_set_vf_vlan,
+       .ndo_set_vf_spoofchk    = qlcnic_sriov_set_vf_spoofchk,
 #endif
 };
 
@@ -465,6 +467,8 @@ static struct qlcnic_nic_template qlcnic_ops = {
        .napi_add               = qlcnic_82xx_napi_add,
        .napi_del               = qlcnic_82xx_napi_del,
        .config_ipaddr          = qlcnic_82xx_config_ipaddr,
+       .shutdown               = qlcnic_82xx_shutdown,
+       .resume                 = qlcnic_82xx_resume,
        .clear_legacy_intr      = qlcnic_82xx_clear_legacy_intr,
 };
 
@@ -508,6 +512,7 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = {
        .config_promisc_mode            = qlcnic_82xx_nic_set_promisc,
        .change_l2_filter               = qlcnic_82xx_change_filter,
        .get_board_info                 = qlcnic_82xx_get_board_info,
+       .set_mac_filter_count           = qlcnic_82xx_set_mac_filter_count,
        .free_mac_list                  = qlcnic_82xx_free_mac_list,
 };
 
@@ -768,7 +773,7 @@ static int
 qlcnic_set_function_modes(struct qlcnic_adapter *adapter)
 {
        u8 id;
-       int i, ret = 1;
+       int ret;
        u32 data = QLCNIC_MGMT_FUNC;
        struct qlcnic_hardware_context *ahw = adapter->ahw;
 
@@ -776,20 +781,10 @@ qlcnic_set_function_modes(struct qlcnic_adapter *adapter)
        if (ret)
                goto err_lock;
 
-       if (qlcnic_config_npars) {
-               for (i = 0; i < ahw->act_pci_func; i++) {
-                       id = adapter->npars[i].pci_func;
-                       if (id == ahw->pci_func)
-                               continue;
-                       data |= (qlcnic_config_npars &
-                                       QLC_DEV_SET_DRV(0xf, id));
-               }
-       } else {
-               data = QLC_SHARED_REG_RD32(adapter, QLCNIC_DRV_OP_MODE);
-               data = (data & ~QLC_DEV_SET_DRV(0xf, ahw->pci_func)) |
-                       (QLC_DEV_SET_DRV(QLCNIC_MGMT_FUNC,
-                                        ahw->pci_func));
-       }
+       id = ahw->pci_func;
+       data = QLC_SHARED_REG_RD32(adapter, QLCNIC_DRV_OP_MODE);
+       data = (data & ~QLC_DEV_SET_DRV(0xf, id)) |
+              QLC_DEV_SET_DRV(QLCNIC_MGMT_FUNC, id);
        QLC_SHARED_REG_WR32(adapter, QLCNIC_DRV_OP_MODE, data);
        qlcnic_api_unlock(adapter);
 err_lock:
@@ -875,6 +870,27 @@ static int qlcnic_setup_pci_map(struct pci_dev *pdev,
        return 0;
 }
 
+static inline bool qlcnic_validate_subsystem_id(struct qlcnic_adapter *adapter,
+                                               int index)
+{
+       struct pci_dev *pdev = adapter->pdev;
+       unsigned short subsystem_vendor;
+       bool ret = true;
+
+       subsystem_vendor = pdev->subsystem_vendor;
+
+       if (pdev->device == PCI_DEVICE_ID_QLOGIC_QLE824X ||
+           pdev->device == PCI_DEVICE_ID_QLOGIC_QLE834X) {
+               if (qlcnic_boards[index].sub_vendor == subsystem_vendor &&
+                   qlcnic_boards[index].sub_device == pdev->subsystem_device)
+                       ret = true;
+               else
+                       ret = false;
+       }
+
+       return ret;
+}
+
 static void qlcnic_get_board_name(struct qlcnic_adapter *adapter, char *name)
 {
        struct pci_dev *pdev = adapter->pdev;
@@ -882,20 +898,18 @@ static void qlcnic_get_board_name(struct qlcnic_adapter *adapter, char *name)
 
        for (i = 0; i < NUM_SUPPORTED_BOARDS; ++i) {
                if (qlcnic_boards[i].vendor == pdev->vendor &&
-                       qlcnic_boards[i].device == pdev->device &&
-                       qlcnic_boards[i].sub_vendor == pdev->subsystem_vendor &&
-                       qlcnic_boards[i].sub_device == pdev->subsystem_device) {
-                               sprintf(name, "%pM: %s" ,
-                                       adapter->mac_addr,
-                                       qlcnic_boards[i].short_name);
-                               found = 1;
-                               break;
+                   qlcnic_boards[i].device == pdev->device &&
+                   qlcnic_validate_subsystem_id(adapter, i)) {
+                       found = 1;
+                       break;
                }
-
        }
 
        if (!found)
                sprintf(name, "%pM Gigabit Ethernet", adapter->mac_addr);
+       else
+               sprintf(name, "%pM: %s" , adapter->mac_addr,
+                       qlcnic_boards[i].short_name);
 }
 
 static void
@@ -980,7 +994,7 @@ qlcnic_initialize_nic(struct qlcnic_adapter *adapter)
        if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) {
                u32 temp;
                temp = QLCRD32(adapter, CRB_FW_CAPABILITIES_2);
-               adapter->ahw->capabilities2 = temp;
+               adapter->ahw->extra_capability[0] = temp;
        }
        adapter->ahw->max_mac_filters = nic_info.max_mac_filters;
        adapter->ahw->max_mtu = nic_info.max_mtu;
@@ -1395,16 +1409,23 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter)
                        for (ring = 0; ring < num_sds_rings; ring++) {
                                sds_ring = &recv_ctx->sds_rings[ring];
                                if (qlcnic_82xx_check(adapter) &&
-                                   (ring == (num_sds_rings - 1)))
-                                       snprintf(sds_ring->name,
-                                                sizeof(sds_ring->name),
-                                                "qlcnic-%s[Tx0+Rx%d]",
-                                                netdev->name, ring);
-                               else
+                                   (ring == (num_sds_rings - 1))) {
+                                       if (!(adapter->flags &
+                                             QLCNIC_MSIX_ENABLED))
+                                               snprintf(sds_ring->name,
+                                                        sizeof(sds_ring->name),
+                                                        "qlcnic");
+                                       else
+                                               snprintf(sds_ring->name,
+                                                        sizeof(sds_ring->name),
+                                                        "%s-tx-0-rx-%d",
+                                                        netdev->name, ring);
+                               } else {
                                        snprintf(sds_ring->name,
                                                 sizeof(sds_ring->name),
-                                                "qlcnic-%s[Rx%d]",
+                                                "%s-rx-%d",
                                                 netdev->name, ring);
+                               }
                                err = request_irq(sds_ring->irq, handler, flags,
                                                  sds_ring->name, sds_ring);
                                if (err)
@@ -1419,7 +1440,7 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter)
                             ring++) {
                                tx_ring = &adapter->tx_ring[ring];
                                snprintf(tx_ring->name, sizeof(tx_ring->name),
-                                        "qlcnic-%s[Tx%d]", netdev->name, ring);
+                                        "%s-tx-%d", netdev->name, ring);
                                err = request_irq(tx_ring->irq, handler, flags,
                                                  tx_ring->name, tx_ring);
                                if (err)
@@ -1465,7 +1486,7 @@ static void qlcnic_get_lro_mss_capability(struct qlcnic_adapter *adapter)
        u32 capab = 0;
 
        if (qlcnic_82xx_check(adapter)) {
-               if (adapter->ahw->capabilities2 &
+               if (adapter->ahw->extra_capability[0] &
                    QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG)
                        adapter->flags |= QLCNIC_FW_LRO_MSS_CAP;
        } else {
@@ -1816,6 +1837,22 @@ qlcnic_reset_context(struct qlcnic_adapter *adapter)
        return err;
 }
 
+void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       u16 act_pci_fn = ahw->act_pci_func;
+       u16 count;
+
+       ahw->max_mc_count = QLCNIC_MAX_MC_COUNT;
+       if (act_pci_fn <= 2)
+               count = (QLCNIC_MAX_UC_COUNT - QLCNIC_MAX_MC_COUNT) /
+                        act_pci_fn;
+       else
+               count = (QLCNIC_LB_MAX_FILTERS - QLCNIC_MAX_MC_COUNT) /
+                        act_pci_fn;
+       ahw->max_uc_count = count;
+}
+
 int
 qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
                    int pci_using_dac)
@@ -1825,7 +1862,7 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
 
        adapter->rx_csum = 1;
        adapter->ahw->mc_enabled = 0;
-       adapter->ahw->max_mc_count = QLCNIC_MAX_MC_COUNT;
+       qlcnic_set_mac_filter_count(adapter);
 
        netdev->netdev_ops         = &qlcnic_netdev_ops;
        netdev->watchdog_timeo     = QLCNIC_WATCHDOG_TIMEOUTVALUE * HZ;
@@ -1863,6 +1900,7 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
                netdev->features |= NETIF_F_LRO;
 
        netdev->hw_features = netdev->features;
+       netdev->priv_flags |= IFF_UNICAST_FLT;
        netdev->irq = adapter->msix_entries[0].vector;
 
        err = register_netdev(netdev);
@@ -1947,6 +1985,21 @@ int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter,
        return 0;
 }
 
+void qlcnic_set_drv_version(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       u32 fw_cmd = 0;
+
+       if (qlcnic_82xx_check(adapter))
+               fw_cmd = QLCNIC_CMD_82XX_SET_DRV_VER;
+       else if (qlcnic_83xx_check(adapter))
+               fw_cmd = QLCNIC_CMD_83XX_SET_DRV_VER;
+
+       if ((ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) &&
+           (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_SET_DRV_VER))
+               qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd);
+}
+
 static int
 qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
@@ -1954,7 +2007,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        struct qlcnic_adapter *adapter = NULL;
        struct qlcnic_hardware_context *ahw;
        int err, pci_using_dac = -1;
-       u32 capab2;
        char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */
 
        if (pdev->is_virtfn)
@@ -2109,13 +2161,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (err)
                goto err_out_disable_mbx_intr;
 
-       if (qlcnic_82xx_check(adapter)) {
-               if (ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) {
-                       capab2 = QLCRD32(adapter, CRB_FW_CAPABILITIES_2);
-                       if (capab2 & QLCNIC_FW_CAPABILITY_2_OCBB)
-                               qlcnic_fw_cmd_set_drv_version(adapter);
-               }
-       }
+       qlcnic_set_drv_version(adapter);
 
        pci_set_drvdata(pdev, adapter);
 
@@ -2231,37 +2277,6 @@ static void qlcnic_remove(struct pci_dev *pdev)
        kfree(ahw);
        free_netdev(netdev);
 }
-static int __qlcnic_shutdown(struct pci_dev *pdev)
-{
-       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
-       struct net_device *netdev = adapter->netdev;
-       int retval;
-
-       netif_device_detach(netdev);
-
-       qlcnic_cancel_idc_work(adapter);
-
-       if (netif_running(netdev))
-               qlcnic_down(adapter, netdev);
-
-       qlcnic_sriov_cleanup(adapter);
-       if (qlcnic_82xx_check(adapter))
-               qlcnic_clr_all_drv_state(adapter, 0);
-
-       clear_bit(__QLCNIC_RESETTING, &adapter->state);
-
-       retval = pci_save_state(pdev);
-       if (retval)
-               return retval;
-       if (qlcnic_82xx_check(adapter)) {
-               if (qlcnic_wol_supported(adapter)) {
-                       pci_enable_wake(pdev, PCI_D3cold, 1);
-                       pci_enable_wake(pdev, PCI_D3hot, 1);
-               }
-       }
-
-       return 0;
-}
 
 static void qlcnic_shutdown(struct pci_dev *pdev)
 {
@@ -2272,8 +2287,7 @@ static void qlcnic_shutdown(struct pci_dev *pdev)
 }
 
 #ifdef CONFIG_PM
-static int
-qlcnic_suspend(struct pci_dev *pdev, pm_message_t state)
+static int qlcnic_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        int retval;
 
@@ -2285,11 +2299,9 @@ qlcnic_suspend(struct pci_dev *pdev, pm_message_t state)
        return 0;
 }
 
-static int
-qlcnic_resume(struct pci_dev *pdev)
+static int qlcnic_resume(struct pci_dev *pdev)
 {
        struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
-       struct net_device *netdev = adapter->netdev;
        int err;
 
        err = pci_enable_device(pdev);
@@ -2300,23 +2312,7 @@ qlcnic_resume(struct pci_dev *pdev)
        pci_set_master(pdev);
        pci_restore_state(pdev);
 
-       err = qlcnic_start_firmware(adapter);
-       if (err) {
-               dev_err(&pdev->dev, "failed to start firmware\n");
-               return err;
-       }
-
-       if (netif_running(netdev)) {
-               err = qlcnic_up(adapter, netdev);
-               if (err)
-                       goto done;
-
-               qlcnic_restore_indev_addr(netdev, NETDEV_UP);
-       }
-done:
-       netif_device_attach(netdev);
-       qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY);
-       return 0;
+       return  __qlcnic_resume(adapter);
 }
 #endif
 
@@ -2655,8 +2651,7 @@ qlcnic_clr_drv_state(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-static void
-qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed)
+void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed)
 {
        u32  val;
 
@@ -3086,6 +3081,7 @@ done:
        adapter->fw_fail_cnt = 0;
        adapter->flags &= ~QLCNIC_FW_HANG;
        clear_bit(__QLCNIC_RESETTING, &adapter->state);
+       qlcnic_set_drv_version(adapter);
 
        if (!qlcnic_clr_drv_state(adapter))
                qlcnic_schedule_work(adapter, qlcnic_fw_poll_work,
@@ -3166,8 +3162,7 @@ detach:
        return 1;
 }
 
-static void
-qlcnic_fw_poll_work(struct work_struct *work)
+void qlcnic_fw_poll_work(struct work_struct *work)
 {
        struct qlcnic_adapter *adapter = container_of(work,
                                struct qlcnic_adapter, fw_work.work);
@@ -3219,7 +3214,6 @@ static int qlcnic_attach_func(struct pci_dev *pdev)
        if (err)
                return err;
 
-       pci_set_power_state(pdev, PCI_D0);
        pci_set_master(pdev);
        pci_restore_state(pdev);
 
@@ -3517,7 +3511,7 @@ static int qlcnic_netdev_event(struct notifier_block *this,
                                 unsigned long event, void *ptr)
 {
        struct qlcnic_adapter *adapter;
-       struct net_device *dev = (struct net_device *)ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
 recheck:
        if (dev == NULL)
index 4b9bab1..ab8a674 100644 (file)
@@ -15,6 +15,7 @@
 #define QLC_83XX_MINIDUMP_FLASH                0x520000
 #define QLC_83XX_OCM_INDEX                     3
 #define QLC_83XX_PCI_INDEX                     0
+#define QLC_83XX_DMA_ENGINE_INDEX              8
 
 static const u32 qlcnic_ms_read_data[] = {
        0x410000A8, 0x410000AC, 0x410000B8, 0x410000BC
@@ -32,6 +33,16 @@ static const u32 qlcnic_ms_read_data[] = {
 
 #define QLCNIC_DUMP_MASK_MAX   0xff
 
+struct qlcnic_pex_dma_descriptor {
+       u32     read_data_size;
+       u32     dma_desc_cmd;
+       u32     src_addr_low;
+       u32     src_addr_high;
+       u32     dma_bus_addr_low;
+       u32     dma_bus_addr_high;
+       u32     rsvd[6];
+} __packed;
+
 struct qlcnic_common_entry_hdr {
        u32     type;
        u32     offset;
@@ -90,7 +101,10 @@ struct __ocm {
 } __packed;
 
 struct __mem {
-       u8      rsvd[24];
+       u32     desc_card_addr;
+       u32     dma_desc_cmd;
+       u32     start_dma_cmd;
+       u32     rsvd[3];
        u32     addr;
        u32     size;
 } __packed;
@@ -466,12 +480,12 @@ skip_poll:
        return l2->no_ops * l2->read_addr_num * sizeof(u32);
 }
 
-static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
-                             struct qlcnic_dump_entry *entry, __le32 *buffer)
+static u32 qlcnic_read_memory_test_agent(struct qlcnic_adapter *adapter,
+                                        struct __mem *mem, __le32 *buffer,
+                                        int *ret)
 {
-       u32 addr, data, test, ret = 0;
+       u32 addr, data, test;
        int i, reg_read;
-       struct __mem *mem = &entry->region.mem;
 
        reg_read = mem->size;
        addr = mem->addr;
@@ -480,7 +494,8 @@ static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
                dev_info(&adapter->pdev->dev,
                         "Unaligned memory addr:0x%x size:0x%x\n",
                         addr, reg_read);
-               return -EINVAL;
+               *ret = -EINVAL;
+               return 0;
        }
 
        mutex_lock(&adapter->ahw->mem_lock);
@@ -499,7 +514,7 @@ static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
                        if (printk_ratelimit()) {
                                dev_err(&adapter->pdev->dev,
                                        "failed to read through agent\n");
-                               ret = -EINVAL;
+                               *ret = -EIO;
                                goto out;
                        }
                }
@@ -516,6 +531,181 @@ out:
        return mem->size;
 }
 
+/* DMA register base address */
+#define QLC_DMA_REG_BASE_ADDR(dma_no)  (0x77320000 + (dma_no * 0x10000))
+
+/* DMA register offsets w.r.t base address */
+#define QLC_DMA_CMD_BUFF_ADDR_LOW      0
+#define QLC_DMA_CMD_BUFF_ADDR_HI       4
+#define QLC_DMA_CMD_STATUS_CTRL                8
+
+#define QLC_PEX_DMA_READ_SIZE          (PAGE_SIZE * 16)
+
+static int qlcnic_start_pex_dma(struct qlcnic_adapter *adapter,
+                               struct __mem *mem)
+{
+       struct qlcnic_dump_template_hdr *tmpl_hdr;
+       struct device *dev = &adapter->pdev->dev;
+       u32 dma_no, dma_base_addr, temp_addr;
+       int i, ret, dma_sts;
+
+       tmpl_hdr = adapter->ahw->fw_dump.tmpl_hdr;
+       dma_no = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX];
+       dma_base_addr = QLC_DMA_REG_BASE_ADDR(dma_no);
+
+       temp_addr = dma_base_addr + QLC_DMA_CMD_BUFF_ADDR_LOW;
+       ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr,
+                                          mem->desc_card_addr);
+       if (ret)
+               return ret;
+
+       temp_addr = dma_base_addr + QLC_DMA_CMD_BUFF_ADDR_HI;
+       ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr, 0);
+       if (ret)
+               return ret;
+
+       temp_addr = dma_base_addr + QLC_DMA_CMD_STATUS_CTRL;
+       ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr,
+                                          mem->start_dma_cmd);
+       if (ret)
+               return ret;
+
+       /* Wait for DMA to complete */
+       temp_addr = dma_base_addr + QLC_DMA_CMD_STATUS_CTRL;
+       for (i = 0; i < 400; i++) {
+               dma_sts = qlcnic_ind_rd(adapter, temp_addr);
+
+               if (dma_sts & BIT_1)
+                       usleep_range(250, 500);
+               else
+                       break;
+       }
+
+       if (i >= 400) {
+               dev_info(dev, "PEX DMA operation timed out");
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
+static u32 qlcnic_read_memory_pexdma(struct qlcnic_adapter *adapter,
+                                    struct __mem *mem,
+                                    __le32 *buffer, int *ret)
+{
+       struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
+       u32 temp, dma_base_addr, size = 0, read_size = 0;
+       struct qlcnic_pex_dma_descriptor *dma_descr;
+       struct qlcnic_dump_template_hdr *tmpl_hdr;
+       struct device *dev = &adapter->pdev->dev;
+       dma_addr_t dma_phys_addr;
+       void *dma_buffer;
+
+       tmpl_hdr = fw_dump->tmpl_hdr;
+
+       /* Check if DMA engine is available */
+       temp = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX];
+       dma_base_addr = QLC_DMA_REG_BASE_ADDR(temp);
+       temp = qlcnic_ind_rd(adapter,
+                            dma_base_addr + QLC_DMA_CMD_STATUS_CTRL);
+
+       if (!(temp & BIT_31)) {
+               dev_info(dev, "%s: DMA engine is not available\n", __func__);
+               *ret = -EIO;
+               return 0;
+       }
+
+       /* Create DMA descriptor */
+       dma_descr = kzalloc(sizeof(struct qlcnic_pex_dma_descriptor),
+                           GFP_KERNEL);
+       if (!dma_descr) {
+               *ret = -ENOMEM;
+               return 0;
+       }
+
+       /* dma_desc_cmd  0:15  = 0
+        * dma_desc_cmd 16:19  = mem->dma_desc_cmd 0:3
+        * dma_desc_cmd 20:23  = pci function number
+        * dma_desc_cmd 24:31  = mem->dma_desc_cmd 8:15
+        */
+       dma_phys_addr = fw_dump->phys_addr;
+       dma_buffer = fw_dump->dma_buffer;
+       temp = 0;
+       temp = mem->dma_desc_cmd & 0xff0f;
+       temp |= (adapter->ahw->pci_func & 0xf) << 4;
+       dma_descr->dma_desc_cmd = (temp << 16) & 0xffff0000;
+       dma_descr->dma_bus_addr_low = LSD(dma_phys_addr);
+       dma_descr->dma_bus_addr_high = MSD(dma_phys_addr);
+       dma_descr->src_addr_high = 0;
+
+       /* Collect memory dump using multiple DMA operations if required */
+       while (read_size < mem->size) {
+               if (mem->size - read_size >= QLC_PEX_DMA_READ_SIZE)
+                       size = QLC_PEX_DMA_READ_SIZE;
+               else
+                       size = mem->size - read_size;
+
+               dma_descr->src_addr_low = mem->addr + read_size;
+               dma_descr->read_data_size = size;
+
+               /* Write DMA descriptor to MS memory*/
+               temp = sizeof(struct qlcnic_pex_dma_descriptor) / 16;
+               *ret = qlcnic_83xx_ms_mem_write128(adapter, mem->desc_card_addr,
+                                                  (u32 *)dma_descr, temp);
+               if (*ret) {
+                       dev_info(dev, "Failed to write DMA descriptor to MS memory at address 0x%x\n",
+                                mem->desc_card_addr);
+                       goto free_dma_descr;
+               }
+
+               *ret = qlcnic_start_pex_dma(adapter, mem);
+               if (*ret) {
+                       dev_info(dev, "Failed to start PEX DMA operation\n");
+                       goto free_dma_descr;
+               }
+
+               memcpy(buffer, dma_buffer, size);
+               buffer += size / 4;
+               read_size += size;
+       }
+
+free_dma_descr:
+       kfree(dma_descr);
+
+       return read_size;
+}
+
+static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
+                             struct qlcnic_dump_entry *entry, __le32 *buffer)
+{
+       struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
+       struct device *dev = &adapter->pdev->dev;
+       struct __mem *mem = &entry->region.mem;
+       u32 data_size;
+       int ret = 0;
+
+       if (fw_dump->use_pex_dma) {
+               data_size = qlcnic_read_memory_pexdma(adapter, mem, buffer,
+                                                     &ret);
+               if (ret)
+                       dev_info(dev,
+                                "Failed to read memory dump using PEX DMA: mask[0x%x]\n",
+                                entry->hdr.mask);
+               else
+                       return data_size;
+       }
+
+       data_size = qlcnic_read_memory_test_agent(adapter, mem, buffer, &ret);
+       if (ret) {
+               dev_info(dev,
+                        "Failed to read memory dump using test agent method: mask[0x%x]\n",
+                        entry->hdr.mask);
+               return 0;
+       } else {
+               return data_size;
+       }
+}
+
 static u32 qlcnic_dump_nop(struct qlcnic_adapter *adapter,
                           struct qlcnic_dump_entry *entry, __le32 *buffer)
 {
@@ -893,6 +1083,12 @@ flash_temp:
 
        tmpl_hdr = ahw->fw_dump.tmpl_hdr;
        tmpl_hdr->drv_cap_mask = QLCNIC_DUMP_MASK_DEF;
+
+       if ((tmpl_hdr->version & 0xffffff) >= 0x20001)
+               ahw->fw_dump.use_pex_dma = true;
+       else
+               ahw->fw_dump.use_pex_dma = false;
+
        ahw->fw_dump.enable = 1;
 
        return 0;
@@ -910,7 +1106,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
        struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
        struct qlcnic_dump_template_hdr *tmpl_hdr = fw_dump->tmpl_hdr;
        static const struct qlcnic_dump_operations *fw_dump_ops;
+       struct device *dev = &adapter->pdev->dev;
        struct qlcnic_hardware_context *ahw;
+       void *temp_buffer;
 
        ahw = adapter->ahw;
 
@@ -944,6 +1142,16 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
        tmpl_hdr->sys_info[0] = QLCNIC_DRIVER_VERSION;
        tmpl_hdr->sys_info[1] = adapter->fw_version;
 
+       if (fw_dump->use_pex_dma) {
+               temp_buffer = dma_alloc_coherent(dev, QLC_PEX_DMA_READ_SIZE,
+                                                &fw_dump->phys_addr,
+                                                GFP_KERNEL);
+               if (!temp_buffer)
+                       fw_dump->use_pex_dma = false;
+               else
+                       fw_dump->dma_buffer = temp_buffer;
+       }
+
        if (qlcnic_82xx_check(adapter)) {
                ops_cnt = ARRAY_SIZE(qlcnic_fw_dump_ops);
                fw_dump_ops = qlcnic_fw_dump_ops;
@@ -1002,6 +1210,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
                return 0;
        }
 error:
+       if (fw_dump->use_pex_dma)
+               dma_free_coherent(dev, QLC_PEX_DMA_READ_SIZE,
+                                 fw_dump->dma_buffer, fw_dump->phys_addr);
        vfree(fw_dump->data);
        return -EINVAL;
 }
index d85fbb5..0daf660 100644 (file)
@@ -129,6 +129,7 @@ struct qlcnic_vport {
        u8                      vlan_mode;
        u16                     vlan;
        u8                      qos;
+       bool                    spoofchk;
        u8                      mac[6];
 };
 
@@ -194,6 +195,8 @@ int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *, struct qlcnic_vf_info *,
 int qlcnic_sriov_get_vf_vport_info(struct qlcnic_adapter *,
                                   struct qlcnic_info *, u16);
 int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *, u16, u8);
+int qlcnic_sriov_vf_shutdown(struct pci_dev *);
+int qlcnic_sriov_vf_resume(struct qlcnic_adapter *);
 
 static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter)
 {
@@ -225,6 +228,7 @@ int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int);
 int qlcnic_sriov_get_vf_config(struct net_device *, int ,
                               struct ifla_vf_info *);
 int qlcnic_sriov_set_vf_vlan(struct net_device *, int, u16, u8);
+int qlcnic_sriov_set_vf_spoofchk(struct net_device *, int, bool);
 #else
 static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {}
 static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {}
index 8b59a71..62380ce 100644 (file)
@@ -35,6 +35,7 @@ static void qlcnic_sriov_vf_cancel_fw_work(struct qlcnic_adapter *);
 static void qlcnic_sriov_cleanup_transaction(struct qlcnic_bc_trans *);
 static int qlcnic_sriov_vf_mbx_op(struct qlcnic_adapter *,
                                  struct qlcnic_cmd_args *);
+static void qlcnic_sriov_process_bc_cmd(struct work_struct *);
 
 static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = {
        .read_crb                       = qlcnic_83xx_read_crb,
@@ -75,6 +76,8 @@ static struct qlcnic_nic_template qlcnic_sriov_vf_ops = {
        .cancel_idc_work        = qlcnic_sriov_vf_cancel_fw_work,
        .napi_add               = qlcnic_83xx_napi_add,
        .napi_del               = qlcnic_83xx_napi_del,
+       .shutdown               = qlcnic_sriov_vf_shutdown,
+       .resume                 = qlcnic_sriov_vf_resume,
        .config_ipaddr          = qlcnic_83xx_config_ipaddr,
        .clear_legacy_intr      = qlcnic_83xx_clear_legacy_intr,
 };
@@ -179,6 +182,8 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs)
                spin_lock_init(&vf->rcv_pend.lock);
                init_completion(&vf->ch_free_cmpl);
 
+               INIT_WORK(&vf->trans_work, qlcnic_sriov_process_bc_cmd);
+
                if (qlcnic_sriov_pf_check(adapter)) {
                        vp = kzalloc(sizeof(struct qlcnic_vport), GFP_KERNEL);
                        if (!vp) {
@@ -187,6 +192,7 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs)
                        }
                        sriov->vf_info[i].vp = vp;
                        vp->max_tx_bw = MAX_BW;
+                       vp->spoofchk = true;
                        random_ether_addr(vp->mac);
                        dev_info(&adapter->pdev->dev,
                                 "MAC Address %pM is configured for VF %d\n",
@@ -652,6 +658,8 @@ int qlcnic_sriov_vf_init(struct qlcnic_adapter *adapter, int pci_using_dac)
        if (qlcnic_read_mac_addr(adapter))
                dev_warn(&adapter->pdev->dev, "failed to read mac addr\n");
 
+       INIT_DELAYED_WORK(&adapter->idc_aen_work, qlcnic_83xx_idc_aen_work);
+
        clear_bit(__QLCNIC_RESETTING, &adapter->state);
        return 0;
 }
@@ -864,7 +872,6 @@ static void qlcnic_sriov_schedule_bc_cmd(struct qlcnic_sriov *sriov,
            vf->adapter->need_fw_reset)
                return;
 
-       INIT_WORK(&vf->trans_work, func);
        queue_work(sriov->bc.bc_trans_wq, &vf->trans_work);
 }
 
@@ -1949,3 +1956,54 @@ static void qlcnic_sriov_vf_free_mac_list(struct qlcnic_adapter *adapter)
                kfree(cur);
        }
 }
+
+int qlcnic_sriov_vf_shutdown(struct pci_dev *pdev)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
+       struct net_device *netdev = adapter->netdev;
+       int retval;
+
+       netif_device_detach(netdev);
+       qlcnic_cancel_idc_work(adapter);
+
+       if (netif_running(netdev))
+               qlcnic_down(adapter, netdev);
+
+       qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_TERM);
+       qlcnic_sriov_cfg_bc_intr(adapter, 0);
+       qlcnic_83xx_disable_mbx_intr(adapter);
+       cancel_delayed_work_sync(&adapter->idc_aen_work);
+
+       retval = pci_save_state(pdev);
+       if (retval)
+               return retval;
+
+       return 0;
+}
+
+int qlcnic_sriov_vf_resume(struct qlcnic_adapter *adapter)
+{
+       struct qlc_83xx_idc *idc = &adapter->ahw->idc;
+       struct net_device *netdev = adapter->netdev;
+       int err;
+
+       set_bit(QLC_83XX_MODULE_LOADED, &idc->status);
+       qlcnic_83xx_enable_mbx_intrpt(adapter);
+       err = qlcnic_sriov_cfg_bc_intr(adapter, 1);
+       if (err)
+               return err;
+
+       err = qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_INIT);
+       if (!err) {
+               if (netif_running(netdev)) {
+                       err = qlcnic_up(adapter, netdev);
+                       if (!err)
+                               qlcnic_restore_indev_addr(netdev, NETDEV_UP);
+               }
+       }
+
+       netif_device_attach(netdev);
+       qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state,
+                            idc->delay);
+       return err;
+}
index 1a66ccd..ee0c1d3 100644 (file)
@@ -580,6 +580,7 @@ static int qlcnic_sriov_set_vf_acl(struct qlcnic_adapter *adapter, u8 func)
        struct qlcnic_cmd_args cmd;
        struct qlcnic_vport *vp;
        int err, id;
+       u8 *mac;
 
        id = qlcnic_sriov_func_to_index(adapter, func);
        if (id < 0)
@@ -591,6 +592,14 @@ static int qlcnic_sriov_set_vf_acl(struct qlcnic_adapter *adapter, u8 func)
                return err;
 
        cmd.req.arg[1] = 0x3 | func << 16;
+       if (vp->spoofchk == true) {
+               mac = vp->mac;
+               cmd.req.arg[2] |= BIT_1 | BIT_3 | BIT_8;
+               cmd.req.arg[4] = mac[5] | mac[4] << 8 | mac[3] << 16 |
+                                mac[2] << 24;
+               cmd.req.arg[5] = mac[1] | mac[0] << 8;
+       }
+
        if (vp->vlan_mode == QLC_PVID_MODE) {
                cmd.req.arg[2] |= BIT_6;
                cmd.req.arg[3] |= vp->vlan << 8;
@@ -1767,6 +1776,7 @@ int qlcnic_sriov_get_vf_config(struct net_device *netdev,
        memcpy(&ivi->mac, vp->mac, ETH_ALEN);
        ivi->vlan = vp->vlan;
        ivi->qos = vp->qos;
+       ivi->spoofchk = vp->spoofchk;
        if (vp->max_tx_bw == MAX_BW)
                ivi->tx_rate = 0;
        else
@@ -1775,3 +1785,29 @@ int qlcnic_sriov_get_vf_config(struct net_device *netdev,
        ivi->vf = vf;
        return 0;
 }
+
+int qlcnic_sriov_set_vf_spoofchk(struct net_device *netdev, int vf, bool chk)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct qlcnic_vf_info *vf_info;
+       struct qlcnic_vport *vp;
+
+       if (!qlcnic_sriov_pf_check(adapter))
+               return -EOPNOTSUPP;
+
+       if (vf >= sriov->num_vfs)
+               return -EINVAL;
+
+       vf_info = &sriov->vf_info[vf];
+       vp = vf_info->vp;
+       if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) {
+               netdev_err(netdev,
+                          "Spoof check change failed for VF %d, as VF driver is loaded. Please unload VF driver and retry the operation\n",
+                          vf);
+               return -EOPNOTSUPP;
+       }
+
+       vp->spoofchk = chk;
+       return 0;
+}
index e7a2fe2..10ed82b 100644 (file)
@@ -47,7 +47,7 @@ static ssize_t qlcnic_store_bridged_mode(struct device *dev,
        if (!test_bit(__QLCNIC_DEV_UP, &adapter->state))
                goto err_out;
 
-       if (strict_strtoul(buf, 2, &new))
+       if (kstrtoul(buf, 2, &new))
                goto err_out;
 
        if (!qlcnic_config_bridged_mode(adapter, !!new))
@@ -77,7 +77,7 @@ static ssize_t qlcnic_store_diag_mode(struct device *dev,
        struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
        unsigned long new;
 
-       if (strict_strtoul(buf, 2, &new))
+       if (kstrtoul(buf, 2, &new))
                return -EINVAL;
 
        if (!!new != !!(adapter->flags & QLCNIC_DIAG_ENABLED))
@@ -114,57 +114,51 @@ static int qlcnic_validate_beacon(struct qlcnic_adapter *adapter, u16 beacon,
        return 0;
 }
 
-static ssize_t qlcnic_store_beacon(struct device *dev,
-                                  struct device_attribute *attr,
-                                  const char *buf, size_t len)
+static int qlcnic_83xx_store_beacon(struct qlcnic_adapter *adapter,
+                                   const char *buf, size_t len)
 {
-       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
        struct qlcnic_hardware_context *ahw = adapter->ahw;
-       int err, max_sds_rings = adapter->max_sds_rings;
-       u16 beacon;
-       u8 b_state, b_rate;
        unsigned long h_beacon;
+       int err;
 
-       if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) {
-               dev_warn(dev,
-                        "LED test not supported in non privileged mode\n");
-               return -EOPNOTSUPP;
-       }
+       if (test_bit(__QLCNIC_RESETTING, &adapter->state))
+               return -EIO;
 
-       if (qlcnic_83xx_check(adapter) &&
-           !test_bit(__QLCNIC_RESETTING, &adapter->state)) {
-               if (kstrtoul(buf, 2, &h_beacon))
-                       return -EINVAL;
+       if (kstrtoul(buf, 2, &h_beacon))
+               return -EINVAL;
 
-               if (ahw->beacon_state == h_beacon)
-                       return len;
+       if (ahw->beacon_state == h_beacon)
+               return len;
 
-               rtnl_lock();
-               if (!ahw->beacon_state) {
-                       if (test_and_set_bit(__QLCNIC_LED_ENABLE,
-                                            &adapter->state)) {
-                               rtnl_unlock();
-                               return -EBUSY;
-                       }
-               }
-               if (h_beacon) {
-                       err = qlcnic_83xx_config_led(adapter, 1, h_beacon);
-                       if (err)
-                               goto beacon_err;
-               } else {
-                       err = qlcnic_83xx_config_led(adapter, 0, !h_beacon);
-                       if (err)
-                               goto beacon_err;
+       rtnl_lock();
+       if (!ahw->beacon_state) {
+               if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) {
+                       rtnl_unlock();
+                       return -EBUSY;
                }
-               /* set the current beacon state */
+       }
+
+       if (h_beacon)
+               err = qlcnic_83xx_config_led(adapter, 1, h_beacon);
+       else
+               err = qlcnic_83xx_config_led(adapter, 0, !h_beacon);
+       if (!err)
                ahw->beacon_state = h_beacon;
-beacon_err:
-               if (!ahw->beacon_state)
-                       clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
 
-               rtnl_unlock();
-               return len;
-       }
+       if (!ahw->beacon_state)
+               clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
+
+       rtnl_unlock();
+       return len;
+}
+
+static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter,
+                                   const char *buf, size_t len)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       int err, max_sds_rings = adapter->max_sds_rings;
+       u16 beacon;
+       u8 h_beacon_state, b_state, b_rate;
 
        if (len != sizeof(u16))
                return QL_STATUS_INVALID_PARAM;
@@ -174,16 +168,29 @@ beacon_err:
        if (err)
                return err;
 
-       if (adapter->ahw->beacon_state == b_state)
+       if (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_2_BEACON) {
+               err = qlcnic_get_beacon_state(adapter, &h_beacon_state);
+               if (!err) {
+                       dev_info(&adapter->pdev->dev,
+                                "Failed to get current beacon state\n");
+               } else {
+                       if (h_beacon_state == QLCNIC_BEACON_DISABLE)
+                               ahw->beacon_state = 0;
+                       else if (h_beacon_state == QLCNIC_BEACON_EANBLE)
+                               ahw->beacon_state = 2;
+               }
+       }
+
+       if (ahw->beacon_state == b_state)
                return len;
 
        rtnl_lock();
-
-       if (!adapter->ahw->beacon_state)
+       if (!ahw->beacon_state) {
                if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) {
                        rtnl_unlock();
                        return -EBUSY;
                }
+       }
 
        if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
                err = -EIO;
@@ -206,14 +213,37 @@ beacon_err:
        if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state))
                qlcnic_diag_free_res(adapter->netdev, max_sds_rings);
 
- out:
-       if (!adapter->ahw->beacon_state)
+out:
+       if (!ahw->beacon_state)
                clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
        rtnl_unlock();
 
        return err;
 }
 
+static ssize_t qlcnic_store_beacon(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t len)
+{
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       int err = 0;
+
+       if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) {
+               dev_warn(dev,
+                        "LED test not supported in non privileged mode\n");
+               return -EOPNOTSUPP;
+       }
+
+       if (qlcnic_82xx_check(adapter))
+               err = qlcnic_82xx_store_beacon(adapter, buf, len);
+       else if (qlcnic_83xx_check(adapter))
+               err = qlcnic_83xx_store_beacon(adapter, buf, len);
+       else
+               return -EIO;
+
+       return err;
+}
+
 static ssize_t qlcnic_show_beacon(struct device *dev,
                                  struct device_attribute *attr, char *buf)
 {
index f87cc21..2553cf4 100644 (file)
@@ -4946,15 +4946,4 @@ static struct pci_driver qlge_driver = {
        .err_handler = &qlge_err_handler
 };
 
-static int __init qlge_init_module(void)
-{
-       return pci_register_driver(&qlge_driver);
-}
-
-static void __exit qlge_exit(void)
-{
-       pci_unregister_driver(&qlge_driver);
-}
-
-module_init(qlge_init_module);
-module_exit(qlge_exit);
+module_pci_driver(qlge_driver);
index c8ba4b3..2055f7e 100644 (file)
@@ -22,7 +22,6 @@ config R6040
        tristate "RDC R6040 Fast Ethernet Adapter support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        select PHYLIB
        ---help---
index 0352345..e6acb9f 100644 (file)
@@ -1817,7 +1817,7 @@ static int cp_set_eeprom(struct net_device *dev,
 /* Put the board into D3cold state and wait for WakeUp signal */
 static void cp_set_d3_state (struct cp_private *cp)
 {
-       pci_enable_wake (cp->pdev, 0, 1); /* Enable PME# generation */
+       pci_enable_wake(cp->pdev, PCI_D0, 1); /* Enable PME# generation */
        pci_set_power_state (cp->pdev, PCI_D3hot);
 }
 
index 783fa8b..ae5d027 100644 (file)
@@ -37,7 +37,6 @@ config 8139CP
        tristate "RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          This is a driver for the Fast Ethernet PCI network cards based on
@@ -52,7 +51,6 @@ config 8139TOO
        tristate "RealTek RTL-8129/8130/8139 PCI Fast Ethernet Adapter support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          This is a driver for the Fast Ethernet PCI network cards based on
@@ -107,7 +105,6 @@ config R8169
        depends on PCI
        select FW_LOADER
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          Say Y here if you have a Realtek 8169 PCI Gigabit Ethernet adapter.
index bed9841..544514e 100644 (file)
@@ -4,14 +4,7 @@
 
 config SH_ETH
        tristate "Renesas SuperH Ethernet support"
-       depends on (SUPERH || ARCH_SHMOBILE) && \
-               (CPU_SUBTYPE_SH7710 || CPU_SUBTYPE_SH7712 || \
-                CPU_SUBTYPE_SH7763 || CPU_SUBTYPE_SH7619 || \
-                CPU_SUBTYPE_SH7724 || CPU_SUBTYPE_SH7734 || \
-                CPU_SUBTYPE_SH7757 || ARCH_R8A7740 || \
-                ARCH_R8A7778 || ARCH_R8A7779)
        select CRC32
-       select NET_CORE
        select MII
        select MDIO_BITBANG
        select PHYLIB
index e29fe8d..a753928 100644 (file)
@@ -313,9 +313,14 @@ static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = {
        [TSU_ADRL31]    = 0x01fc,
 };
 
-#if defined(CONFIG_CPU_SUBTYPE_SH7734) || \
-       defined(CONFIG_CPU_SUBTYPE_SH7763) || \
-       defined(CONFIG_ARCH_R8A7740)
+static int sh_eth_is_gether(struct sh_eth_private *mdp)
+{
+       if (mdp->reg_offset == sh_eth_offset_gigabit)
+               return 1;
+       else
+               return 0;
+}
+
 static void sh_eth_select_mii(struct net_device *ndev)
 {
        u32 value = 0x0;
@@ -339,11 +344,7 @@ static void sh_eth_select_mii(struct net_device *ndev)
 
        sh_eth_write(ndev, value, RMII_MII);
 }
-#endif
 
-/* There is CPU dependent code */
-#if defined(CONFIG_ARCH_R8A7778) || defined(CONFIG_ARCH_R8A7779)
-#define SH_ETH_RESET_DEFAULT   1
 static void sh_eth_set_duplex(struct net_device *ndev)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
@@ -354,7 +355,8 @@ static void sh_eth_set_duplex(struct net_device *ndev)
                sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR);
 }
 
-static void sh_eth_set_rate(struct net_device *ndev)
+/* There is CPU dependent code */
+static void sh_eth_set_rate_r8a777x(struct net_device *ndev)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
 
@@ -371,9 +373,9 @@ static void sh_eth_set_rate(struct net_device *ndev)
 }
 
 /* R8A7778/9 */
-static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
+static struct sh_eth_cpu_data r8a777x_data = {
        .set_duplex     = sh_eth_set_duplex,
-       .set_rate       = sh_eth_set_rate,
+       .set_rate       = sh_eth_set_rate_r8a777x,
 
        .ecsr_value     = ECSR_PSRTO | ECSR_LCHNG | ECSR_ICD,
        .ecsipr_value   = ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | ECSIPR_ICDIP,
@@ -383,26 +385,14 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
        .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE |
                          EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE |
                          EESR_ECI,
-       .tx_error_check = EESR_TWB | EESR_TABT | EESR_TDE | EESR_TFE,
 
        .apr            = 1,
        .mpr            = 1,
        .tpauser        = 1,
        .hw_swap        = 1,
 };
-#elif defined(CONFIG_CPU_SUBTYPE_SH7724)
-#define SH_ETH_RESET_DEFAULT   1
-static void sh_eth_set_duplex(struct net_device *ndev)
-{
-       struct sh_eth_private *mdp = netdev_priv(ndev);
 
-       if (mdp->duplex) /* Full */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR);
-       else            /* Half */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR);
-}
-
-static void sh_eth_set_rate(struct net_device *ndev)
+static void sh_eth_set_rate_sh7724(struct net_device *ndev)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
 
@@ -419,19 +409,18 @@ static void sh_eth_set_rate(struct net_device *ndev)
 }
 
 /* SH7724 */
-static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
+static struct sh_eth_cpu_data sh7724_data = {
        .set_duplex     = sh_eth_set_duplex,
-       .set_rate       = sh_eth_set_rate,
+       .set_rate       = sh_eth_set_rate_sh7724,
 
        .ecsr_value     = ECSR_PSRTO | ECSR_LCHNG | ECSR_ICD,
        .ecsipr_value   = ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | ECSIPR_ICDIP,
-       .eesipr_value   = DMAC_M_RFRMER | DMAC_M_ECI | 0x01ff009f,
+       .eesipr_value   = 0x01ff009f,
 
        .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO,
        .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE |
                          EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE |
                          EESR_ECI,
-       .tx_error_check = EESR_TWB | EESR_TABT | EESR_TDE | EESR_TFE,
 
        .apr            = 1,
        .mpr            = 1,
@@ -440,22 +429,8 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
        .rpadir         = 1,
        .rpadir_value   = 0x00020000, /* NET_IP_ALIGN assumed to be 2 */
 };
-#elif defined(CONFIG_CPU_SUBTYPE_SH7757)
-#define SH_ETH_HAS_BOTH_MODULES        1
-#define SH_ETH_HAS_TSU 1
-static int sh_eth_check_reset(struct net_device *ndev);
-
-static void sh_eth_set_duplex(struct net_device *ndev)
-{
-       struct sh_eth_private *mdp = netdev_priv(ndev);
 
-       if (mdp->duplex) /* Full */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR);
-       else            /* Half */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR);
-}
-
-static void sh_eth_set_rate(struct net_device *ndev)
+static void sh_eth_set_rate_sh7757(struct net_device *ndev)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
 
@@ -472,9 +447,9 @@ static void sh_eth_set_rate(struct net_device *ndev)
 }
 
 /* SH7757 */
-static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
-       .set_duplex             = sh_eth_set_duplex,
-       .set_rate               = sh_eth_set_rate,
+static struct sh_eth_cpu_data sh7757_data = {
+       .set_duplex     = sh_eth_set_duplex,
+       .set_rate       = sh_eth_set_rate_sh7757,
 
        .eesipr_value   = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
        .rmcr_value     = 0x00000001,
@@ -483,8 +458,8 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
        .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE |
                          EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE |
                          EESR_ECI,
-       .tx_error_check = EESR_TWB | EESR_TABT | EESR_TDE | EESR_TFE,
 
+       .irq_flags      = IRQF_SHARED,
        .apr            = 1,
        .mpr            = 1,
        .tpauser        = 1,
@@ -494,7 +469,7 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
        .rpadir_value   = 2 << 16,
 };
 
-#define SH_GIGA_ETH_BASE       0xfee00000
+#define SH_GIGA_ETH_BASE       0xfee00000UL
 #define GIGA_MALR(port)                (SH_GIGA_ETH_BASE + 0x800 * (port) + 0x05c8)
 #define GIGA_MAHR(port)                (SH_GIGA_ETH_BASE + 0x800 * (port) + 0x05c0)
 static void sh_eth_chip_reset_giga(struct net_device *ndev)
@@ -519,52 +494,6 @@ static void sh_eth_chip_reset_giga(struct net_device *ndev)
        }
 }
 
-static int sh_eth_is_gether(struct sh_eth_private *mdp);
-static int sh_eth_reset(struct net_device *ndev)
-{
-       struct sh_eth_private *mdp = netdev_priv(ndev);
-       int ret = 0;
-
-       if (sh_eth_is_gether(mdp)) {
-               sh_eth_write(ndev, 0x03, EDSR);
-               sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER,
-                               EDMR);
-
-               ret = sh_eth_check_reset(ndev);
-               if (ret)
-                       goto out;
-
-               /* Table Init */
-               sh_eth_write(ndev, 0x0, TDLAR);
-               sh_eth_write(ndev, 0x0, TDFAR);
-               sh_eth_write(ndev, 0x0, TDFXR);
-               sh_eth_write(ndev, 0x0, TDFFR);
-               sh_eth_write(ndev, 0x0, RDLAR);
-               sh_eth_write(ndev, 0x0, RDFAR);
-               sh_eth_write(ndev, 0x0, RDFXR);
-               sh_eth_write(ndev, 0x0, RDFFR);
-       } else {
-               sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_ETHER,
-                               EDMR);
-               mdelay(3);
-               sh_eth_write(ndev, sh_eth_read(ndev, EDMR) & ~EDMR_SRST_ETHER,
-                               EDMR);
-       }
-
-out:
-       return ret;
-}
-
-static void sh_eth_set_duplex_giga(struct net_device *ndev)
-{
-       struct sh_eth_private *mdp = netdev_priv(ndev);
-
-       if (mdp->duplex) /* Full */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR);
-       else            /* Half */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR);
-}
-
 static void sh_eth_set_rate_giga(struct net_device *ndev)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
@@ -585,9 +514,9 @@ static void sh_eth_set_rate_giga(struct net_device *ndev)
 }
 
 /* SH7757(GETHERC) */
-static struct sh_eth_cpu_data sh_eth_my_cpu_data_giga = {
+static struct sh_eth_cpu_data sh7757_data_giga = {
        .chip_reset     = sh_eth_chip_reset_giga,
-       .set_duplex     = sh_eth_set_duplex_giga,
+       .set_duplex     = sh_eth_set_duplex,
        .set_rate       = sh_eth_set_rate_giga,
 
        .ecsr_value     = ECSR_ICD | ECSR_MPD,
@@ -598,11 +527,10 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data_giga = {
        .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
                          EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
                          EESR_TDE | EESR_ECI,
-       .tx_error_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_TDE | \
-                         EESR_TFE,
        .fdr_value      = 0x0000072f,
        .rmcr_value     = 0x00000001,
 
+       .irq_flags      = IRQF_SHARED,
        .apr            = 1,
        .mpr            = 1,
        .tpauser        = 1,
@@ -615,19 +543,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data_giga = {
        .tsu            = 1,
 };
 
-static struct sh_eth_cpu_data *sh_eth_get_cpu_data(struct sh_eth_private *mdp)
-{
-       if (sh_eth_is_gether(mdp))
-               return &sh_eth_my_cpu_data_giga;
-       else
-               return &sh_eth_my_cpu_data;
-}
-
-#elif defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763)
-#define SH_ETH_HAS_TSU 1
-static int sh_eth_check_reset(struct net_device *ndev);
-static void sh_eth_reset_hw_crc(struct net_device *ndev);
-
 static void sh_eth_chip_reset(struct net_device *ndev)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
@@ -637,17 +552,7 @@ static void sh_eth_chip_reset(struct net_device *ndev)
        mdelay(1);
 }
 
-static void sh_eth_set_duplex(struct net_device *ndev)
-{
-       struct sh_eth_private *mdp = netdev_priv(ndev);
-
-       if (mdp->duplex) /* Full */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR);
-       else            /* Half */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR);
-}
-
-static void sh_eth_set_rate(struct net_device *ndev)
+static void sh_eth_set_rate_gether(struct net_device *ndev)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
 
@@ -666,11 +571,11 @@ static void sh_eth_set_rate(struct net_device *ndev)
        }
 }
 
-/* sh7763 */
-static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
+/* SH7734 */
+static struct sh_eth_cpu_data sh7734_data = {
        .chip_reset     = sh_eth_chip_reset,
        .set_duplex     = sh_eth_set_duplex,
-       .set_rate       = sh_eth_set_rate,
+       .set_rate       = sh_eth_set_rate_gether,
 
        .ecsr_value     = ECSR_ICD | ECSR_MPD,
        .ecsipr_value   = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
@@ -680,8 +585,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
        .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
                          EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
                          EESR_TDE | EESR_ECI,
-       .tx_error_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_TDE | \
-                         EESR_TFE,
 
        .apr            = 1,
        .mpr            = 1,
@@ -691,54 +594,37 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
        .no_trimd       = 1,
        .no_ade         = 1,
        .tsu            = 1,
-#if defined(CONFIG_CPU_SUBTYPE_SH7734)
-       .hw_crc     = 1,
-       .select_mii = 1,
-#endif
+       .hw_crc         = 1,
+       .select_mii     = 1,
 };
 
-static int sh_eth_reset(struct net_device *ndev)
-{
-       int ret = 0;
-
-       sh_eth_write(ndev, EDSR_ENALL, EDSR);
-       sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR);
-
-       ret = sh_eth_check_reset(ndev);
-       if (ret)
-               goto out;
+/* SH7763 */
+static struct sh_eth_cpu_data sh7763_data = {
+       .chip_reset     = sh_eth_chip_reset,
+       .set_duplex     = sh_eth_set_duplex,
+       .set_rate       = sh_eth_set_rate_gether,
 
-       /* Table Init */
-       sh_eth_write(ndev, 0x0, TDLAR);
-       sh_eth_write(ndev, 0x0, TDFAR);
-       sh_eth_write(ndev, 0x0, TDFXR);
-       sh_eth_write(ndev, 0x0, TDFFR);
-       sh_eth_write(ndev, 0x0, RDLAR);
-       sh_eth_write(ndev, 0x0, RDFAR);
-       sh_eth_write(ndev, 0x0, RDFXR);
-       sh_eth_write(ndev, 0x0, RDFFR);
-
-       /* Reset HW CRC register */
-       sh_eth_reset_hw_crc(ndev);
-
-       /* Select MII mode */
-       if (sh_eth_my_cpu_data.select_mii)
-               sh_eth_select_mii(ndev);
-out:
-       return ret;
-}
+       .ecsr_value     = ECSR_ICD | ECSR_MPD,
+       .ecsipr_value   = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
+       .eesipr_value   = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
 
-static void sh_eth_reset_hw_crc(struct net_device *ndev)
-{
-       if (sh_eth_my_cpu_data.hw_crc)
-               sh_eth_write(ndev, 0x0, CSMR);
-}
+       .tx_check       = EESR_TC1 | EESR_FTC,
+       .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT | \
+                         EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | \
+                         EESR_ECI,
 
-#elif defined(CONFIG_ARCH_R8A7740)
-#define SH_ETH_HAS_TSU 1
-static int sh_eth_check_reset(struct net_device *ndev);
+       .apr            = 1,
+       .mpr            = 1,
+       .tpauser        = 1,
+       .bculr          = 1,
+       .hw_swap        = 1,
+       .no_trimd       = 1,
+       .no_ade         = 1,
+       .tsu            = 1,
+       .irq_flags      = IRQF_SHARED,
+};
 
-static void sh_eth_chip_reset(struct net_device *ndev)
+static void sh_eth_chip_reset_r8a7740(struct net_device *ndev)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
 
@@ -749,65 +635,11 @@ static void sh_eth_chip_reset(struct net_device *ndev)
        sh_eth_select_mii(ndev);
 }
 
-static int sh_eth_reset(struct net_device *ndev)
-{
-       int ret = 0;
-
-       sh_eth_write(ndev, EDSR_ENALL, EDSR);
-       sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR);
-
-       ret = sh_eth_check_reset(ndev);
-       if (ret)
-               goto out;
-
-       /* Table Init */
-       sh_eth_write(ndev, 0x0, TDLAR);
-       sh_eth_write(ndev, 0x0, TDFAR);
-       sh_eth_write(ndev, 0x0, TDFXR);
-       sh_eth_write(ndev, 0x0, TDFFR);
-       sh_eth_write(ndev, 0x0, RDLAR);
-       sh_eth_write(ndev, 0x0, RDFAR);
-       sh_eth_write(ndev, 0x0, RDFXR);
-       sh_eth_write(ndev, 0x0, RDFFR);
-
-out:
-       return ret;
-}
-
-static void sh_eth_set_duplex(struct net_device *ndev)
-{
-       struct sh_eth_private *mdp = netdev_priv(ndev);
-
-       if (mdp->duplex) /* Full */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR);
-       else            /* Half */
-               sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR);
-}
-
-static void sh_eth_set_rate(struct net_device *ndev)
-{
-       struct sh_eth_private *mdp = netdev_priv(ndev);
-
-       switch (mdp->speed) {
-       case 10: /* 10BASE */
-               sh_eth_write(ndev, GECMR_10, GECMR);
-               break;
-       case 100:/* 100BASE */
-               sh_eth_write(ndev, GECMR_100, GECMR);
-               break;
-       case 1000: /* 1000BASE */
-               sh_eth_write(ndev, GECMR_1000, GECMR);
-               break;
-       default:
-               break;
-       }
-}
-
 /* R8A7740 */
-static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
-       .chip_reset     = sh_eth_chip_reset,
+static struct sh_eth_cpu_data r8a7740_data = {
+       .chip_reset     = sh_eth_chip_reset_r8a7740,
        .set_duplex     = sh_eth_set_duplex,
-       .set_rate       = sh_eth_set_rate,
+       .set_rate       = sh_eth_set_rate_gether,
 
        .ecsr_value     = ECSR_ICD | ECSR_MPD,
        .ecsipr_value   = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
@@ -817,8 +649,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
        .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
                          EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
                          EESR_TDE | EESR_ECI,
-       .tx_error_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_TDE | \
-                         EESR_TFE,
 
        .apr            = 1,
        .mpr            = 1,
@@ -829,11 +659,10 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
        .no_ade         = 1,
        .tsu            = 1,
        .select_mii     = 1,
+       .shift_rd0      = 1,
 };
 
-#elif defined(CONFIG_CPU_SUBTYPE_SH7619)
-#define SH_ETH_RESET_DEFAULT   1
-static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
+static struct sh_eth_cpu_data sh7619_data = {
        .eesipr_value   = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
 
        .apr            = 1,
@@ -841,14 +670,11 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
        .tpauser        = 1,
        .hw_swap        = 1,
 };
-#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
-#define SH_ETH_RESET_DEFAULT   1
-#define SH_ETH_HAS_TSU 1
-static struct sh_eth_cpu_data sh_eth_my_cpu_data = {
+
+static struct sh_eth_cpu_data sh771x_data = {
        .eesipr_value   = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
        .tsu            = 1,
 };
-#endif
 
 static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd)
 {
@@ -873,22 +699,8 @@ static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd)
 
        if (!cd->eesr_err_check)
                cd->eesr_err_check = DEFAULT_EESR_ERR_CHECK;
-
-       if (!cd->tx_error_check)
-               cd->tx_error_check = DEFAULT_TX_ERROR_CHECK;
 }
 
-#if defined(SH_ETH_RESET_DEFAULT)
-/* Chip Reset */
-static int  sh_eth_reset(struct net_device *ndev)
-{
-       sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_ETHER, EDMR);
-       mdelay(3);
-       sh_eth_write(ndev, sh_eth_read(ndev, EDMR) & ~EDMR_SRST_ETHER, EDMR);
-
-       return 0;
-}
-#else
 static int sh_eth_check_reset(struct net_device *ndev)
 {
        int ret = 0;
@@ -906,7 +718,49 @@ static int sh_eth_check_reset(struct net_device *ndev)
        }
        return ret;
 }
-#endif
+
+static int sh_eth_reset(struct net_device *ndev)
+{
+       struct sh_eth_private *mdp = netdev_priv(ndev);
+       int ret = 0;
+
+       if (sh_eth_is_gether(mdp)) {
+               sh_eth_write(ndev, EDSR_ENALL, EDSR);
+               sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER,
+                            EDMR);
+
+               ret = sh_eth_check_reset(ndev);
+               if (ret)
+                       goto out;
+
+               /* Table Init */
+               sh_eth_write(ndev, 0x0, TDLAR);
+               sh_eth_write(ndev, 0x0, TDFAR);
+               sh_eth_write(ndev, 0x0, TDFXR);
+               sh_eth_write(ndev, 0x0, TDFFR);
+               sh_eth_write(ndev, 0x0, RDLAR);
+               sh_eth_write(ndev, 0x0, RDFAR);
+               sh_eth_write(ndev, 0x0, RDFXR);
+               sh_eth_write(ndev, 0x0, RDFFR);
+
+               /* Reset HW CRC register */
+               if (mdp->cd->hw_crc)
+                       sh_eth_write(ndev, 0x0, CSMR);
+
+               /* Select MII mode */
+               if (mdp->cd->select_mii)
+                       sh_eth_select_mii(ndev);
+       } else {
+               sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_ETHER,
+                            EDMR);
+               mdelay(3);
+               sh_eth_write(ndev, sh_eth_read(ndev, EDMR) & ~EDMR_SRST_ETHER,
+                            EDMR);
+       }
+
+out:
+       return ret;
+}
 
 #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
 static void sh_eth_set_receive_align(struct sk_buff *skb)
@@ -982,14 +836,6 @@ static void read_mac_address(struct net_device *ndev, unsigned char *mac)
        }
 }
 
-static int sh_eth_is_gether(struct sh_eth_private *mdp)
-{
-       if (mdp->reg_offset == sh_eth_offset_gigabit)
-               return 1;
-       else
-               return 0;
-}
-
 static unsigned long sh_eth_get_edtrr_trns(struct sh_eth_private *mdp)
 {
        if (sh_eth_is_gether(mdp))
@@ -1388,7 +1234,7 @@ static int sh_eth_txfree(struct net_device *ndev)
 }
 
 /* Packet receive function */
-static int sh_eth_rx(struct net_device *ndev, u32 intr_status)
+static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
        struct sh_eth_rxdesc *rxdesc;
@@ -1396,6 +1242,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status)
        int entry = mdp->cur_rx % mdp->num_rx_ring;
        int boguscnt = (mdp->dirty_rx + mdp->num_rx_ring) - mdp->cur_rx;
        struct sk_buff *skb;
+       int exceeded = 0;
        u16 pkt_len = 0;
        u32 desc_status;
 
@@ -1407,10 +1254,15 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status)
                if (--boguscnt < 0)
                        break;
 
+               if (*quota <= 0) {
+                       exceeded = 1;
+                       break;
+               }
+               (*quota)--;
+
                if (!(desc_status & RDFEND))
                        ndev->stats.rx_length_errors++;
 
-#if defined(CONFIG_ARCH_R8A7740)
                /*
                 * In case of almost all GETHER/ETHERs, the Receive Frame State
                 * (RFS) bits in the Receive Descriptor 0 are from bit 9 to
@@ -1418,8 +1270,8 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status)
                 * bits are from bit 25 to bit 16. So, the driver needs right
                 * shifting by 16.
                 */
-               desc_status >>= 16;
-#endif
+               if (mdp->cd->shift_rd0)
+                       desc_status >>= 16;
 
                if (desc_status & (RD_RFS1 | RD_RFS2 | RD_RFS3 | RD_RFS4 |
                                   RD_RFS5 | RD_RFS6 | RD_RFS10)) {
@@ -1494,7 +1346,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status)
                sh_eth_write(ndev, EDRRR_R, EDRRR);
        }
 
-       return 0;
+       return exceeded;
 }
 
 static void sh_eth_rcv_snd_disable(struct net_device *ndev)
@@ -1636,7 +1488,7 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev)
        struct sh_eth_private *mdp = netdev_priv(ndev);
        struct sh_eth_cpu_data *cd = mdp->cd;
        irqreturn_t ret = IRQ_NONE;
-       unsigned long intr_status;
+       unsigned long intr_status, intr_enable;
 
        spin_lock(&mdp->lock);
 
@@ -1647,34 +1499,41 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev)
         * and we need to fully handle it in sh_eth_error() in order to quench
         * it as it doesn't get cleared by just writing 1 to the ECI bit...
         */
-       intr_status &= sh_eth_read(ndev, EESIPR) | DMAC_M_ECI;
-       /* Clear interrupt */
-       if (intr_status & (EESR_FRC | EESR_RMAF | EESR_RRF |
-                       EESR_RTLF | EESR_RTSF | EESR_PRE | EESR_CERF |
-                       cd->tx_check | cd->eesr_err_check)) {
-               sh_eth_write(ndev, intr_status, EESR);
+       intr_enable = sh_eth_read(ndev, EESIPR);
+       intr_status &= intr_enable | DMAC_M_ECI;
+       if (intr_status & (EESR_RX_CHECK | cd->tx_check | cd->eesr_err_check))
                ret = IRQ_HANDLED;
-       else
+       else
                goto other_irq;
 
-       if (intr_status & (EESR_FRC | /* Frame recv*/
-                       EESR_RMAF | /* Multi cast address recv*/
-                       EESR_RRF  | /* Bit frame recv */
-                       EESR_RTLF | /* Long frame recv*/
-                       EESR_RTSF | /* short frame recv */
-                       EESR_PRE  | /* PHY-LSI recv error */
-                       EESR_CERF)){ /* recv frame CRC error */
-               sh_eth_rx(ndev, intr_status);
+       if (intr_status & EESR_RX_CHECK) {
+               if (napi_schedule_prep(&mdp->napi)) {
+                       /* Mask Rx interrupts */
+                       sh_eth_write(ndev, intr_enable & ~EESR_RX_CHECK,
+                                    EESIPR);
+                       __napi_schedule(&mdp->napi);
+               } else {
+                       dev_warn(&ndev->dev,
+                                "ignoring interrupt, status 0x%08lx, mask 0x%08lx.\n",
+                                intr_status, intr_enable);
+               }
        }
 
        /* Tx Check */
        if (intr_status & cd->tx_check) {
+               /* Clear Tx interrupts */
+               sh_eth_write(ndev, intr_status & cd->tx_check, EESR);
+
                sh_eth_txfree(ndev);
                netif_wake_queue(ndev);
        }
 
-       if (intr_status & cd->eesr_err_check)
+       if (intr_status & cd->eesr_err_check) {
+               /* Clear error interrupts */
+               sh_eth_write(ndev, intr_status & cd->eesr_err_check, EESR);
+
                sh_eth_error(ndev, intr_status);
+       }
 
 other_irq:
        spin_unlock(&mdp->lock);
@@ -1682,6 +1541,33 @@ other_irq:
        return ret;
 }
 
+static int sh_eth_poll(struct napi_struct *napi, int budget)
+{
+       struct sh_eth_private *mdp = container_of(napi, struct sh_eth_private,
+                                                 napi);
+       struct net_device *ndev = napi->dev;
+       int quota = budget;
+       unsigned long intr_status;
+
+       for (;;) {
+               intr_status = sh_eth_read(ndev, EESR);
+               if (!(intr_status & EESR_RX_CHECK))
+                       break;
+               /* Clear Rx interrupts */
+               sh_eth_write(ndev, intr_status & EESR_RX_CHECK, EESR);
+
+               if (sh_eth_rx(ndev, intr_status, &quota))
+                       goto out;
+       }
+
+       napi_complete(napi);
+
+       /* Reenable Rx interrupts */
+       sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR);
+out:
+       return budget - quota;
+}
+
 /* PHY state control function */
 static void sh_eth_adjust_link(struct net_device *ndev)
 {
@@ -1972,14 +1858,7 @@ static int sh_eth_open(struct net_device *ndev)
        pm_runtime_get_sync(&mdp->pdev->dev);
 
        ret = request_irq(ndev->irq, sh_eth_interrupt,
-#if defined(CONFIG_CPU_SUBTYPE_SH7763) || \
-       defined(CONFIG_CPU_SUBTYPE_SH7764) || \
-       defined(CONFIG_CPU_SUBTYPE_SH7757)
-                               IRQF_SHARED,
-#else
-                               0,
-#endif
-                               ndev->name, ndev);
+                         mdp->cd->irq_flags, ndev->name, ndev);
        if (ret) {
                dev_err(&ndev->dev, "Can not assign IRQ number\n");
                return ret;
@@ -2000,6 +1879,8 @@ static int sh_eth_open(struct net_device *ndev)
        if (ret)
                goto out_free_irq;
 
+       napi_enable(&mdp->napi);
+
        return ret;
 
 out_free_irq:
@@ -2095,6 +1976,8 @@ static int sh_eth_close(struct net_device *ndev)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
 
+       napi_disable(&mdp->napi);
+
        netif_stop_queue(ndev);
 
        /* Disable interrupts by clearing the interrupt mask. */
@@ -2165,7 +2048,6 @@ static int sh_eth_do_ioctl(struct net_device *ndev, struct ifreq *rq,
        return phy_mii_ioctl(phydev, rq, cmd);
 }
 
-#if defined(SH_ETH_HAS_TSU)
 /* For TSU_POSTn. Please refer to the manual about this (strange) bitfields */
 static void *sh_eth_tsu_get_post_reg_offset(struct sh_eth_private *mdp,
                                            int entry)
@@ -2508,7 +2390,6 @@ static int sh_eth_vlan_rx_kill_vid(struct net_device *ndev,
 
        return 0;
 }
-#endif /* SH_ETH_HAS_TSU */
 
 /* SuperH's TSU register init function */
 static void sh_eth_tsu_init(struct sh_eth_private *mdp)
@@ -2652,11 +2533,21 @@ static const struct net_device_ops sh_eth_netdev_ops = {
        .ndo_stop               = sh_eth_close,
        .ndo_start_xmit         = sh_eth_start_xmit,
        .ndo_get_stats          = sh_eth_get_stats,
-#if defined(SH_ETH_HAS_TSU)
+       .ndo_tx_timeout         = sh_eth_tx_timeout,
+       .ndo_do_ioctl           = sh_eth_do_ioctl,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_change_mtu         = eth_change_mtu,
+};
+
+static const struct net_device_ops sh_eth_netdev_ops_tsu = {
+       .ndo_open               = sh_eth_open,
+       .ndo_stop               = sh_eth_close,
+       .ndo_start_xmit         = sh_eth_start_xmit,
+       .ndo_get_stats          = sh_eth_get_stats,
        .ndo_set_rx_mode        = sh_eth_set_multicast_list,
        .ndo_vlan_rx_add_vid    = sh_eth_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid   = sh_eth_vlan_rx_kill_vid,
-#endif
        .ndo_tx_timeout         = sh_eth_tx_timeout,
        .ndo_do_ioctl           = sh_eth_do_ioctl,
        .ndo_validate_addr      = eth_validate_addr,
@@ -2671,6 +2562,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
        struct net_device *ndev = NULL;
        struct sh_eth_private *mdp = NULL;
        struct sh_eth_plat_data *pd = pdev->dev.platform_data;
+       const struct platform_device_id *id = platform_get_device_id(pdev);
 
        /* get base addr */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2729,15 +2621,14 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
        mdp->reg_offset = sh_eth_get_register_offset(pd->register_type);
 
        /* set cpu data */
-#if defined(SH_ETH_HAS_BOTH_MODULES)
-       mdp->cd = sh_eth_get_cpu_data(mdp);
-#else
-       mdp->cd = &sh_eth_my_cpu_data;
-#endif
+       mdp->cd = (struct sh_eth_cpu_data *)id->driver_data;
        sh_eth_set_default_cpu_data(mdp->cd);
 
        /* set function */
-       ndev->netdev_ops = &sh_eth_netdev_ops;
+       if (mdp->cd->tsu)
+               ndev->netdev_ops = &sh_eth_netdev_ops_tsu;
+       else
+               ndev->netdev_ops = &sh_eth_netdev_ops;
        SET_ETHTOOL_OPS(ndev, &sh_eth_ethtool_ops);
        ndev->watchdog_timeo = TX_TIMEOUT;
 
@@ -2776,10 +2667,12 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
                }
        }
 
+       netif_napi_add(ndev, &mdp->napi, sh_eth_poll, 64);
+
        /* network device register */
        ret = register_netdev(ndev);
        if (ret)
-               goto out_release;
+               goto out_napi_del;
 
        /* mdio bus init */
        ret = sh_mdio_init(ndev, pdev->id, pd);
@@ -2797,6 +2690,9 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
 out_unregister:
        unregister_netdev(ndev);
 
+out_napi_del:
+       netif_napi_del(&mdp->napi);
+
 out_release:
        /* net_dev free */
        if (ndev)
@@ -2809,16 +2705,18 @@ out:
 static int sh_eth_drv_remove(struct platform_device *pdev)
 {
        struct net_device *ndev = platform_get_drvdata(pdev);
+       struct sh_eth_private *mdp = netdev_priv(ndev);
 
        sh_mdio_release(ndev);
        unregister_netdev(ndev);
+       netif_napi_del(&mdp->napi);
        pm_runtime_disable(&pdev->dev);
        free_netdev(ndev);
-       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
 
+#ifdef CONFIG_PM
 static int sh_eth_runtime_nop(struct device *dev)
 {
        /*
@@ -2832,17 +2730,36 @@ static int sh_eth_runtime_nop(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops sh_eth_dev_pm_ops = {
+static const struct dev_pm_ops sh_eth_dev_pm_ops = {
        .runtime_suspend = sh_eth_runtime_nop,
        .runtime_resume = sh_eth_runtime_nop,
 };
+#define SH_ETH_PM_OPS (&sh_eth_dev_pm_ops)
+#else
+#define SH_ETH_PM_OPS NULL
+#endif
+
+static struct platform_device_id sh_eth_id_table[] = {
+       { "sh7619-ether", (kernel_ulong_t)&sh7619_data },
+       { "sh771x-ether", (kernel_ulong_t)&sh771x_data },
+       { "sh7724-ether", (kernel_ulong_t)&sh7724_data },
+       { "sh7734-gether", (kernel_ulong_t)&sh7734_data },
+       { "sh7757-ether", (kernel_ulong_t)&sh7757_data },
+       { "sh7757-gether", (kernel_ulong_t)&sh7757_data_giga },
+       { "sh7763-gether", (kernel_ulong_t)&sh7763_data },
+       { "r8a7740-gether", (kernel_ulong_t)&r8a7740_data },
+       { "r8a777x-ether", (kernel_ulong_t)&r8a777x_data },
+       { }
+};
+MODULE_DEVICE_TABLE(platform, sh_eth_id_table);
 
 static struct platform_driver sh_eth_driver = {
        .probe = sh_eth_drv_probe,
        .remove = sh_eth_drv_remove,
+       .id_table = sh_eth_id_table,
        .driver = {
                   .name = CARDNAME,
-                  .pm = &sh_eth_dev_pm_ops,
+                  .pm = SH_ETH_PM_OPS,
        },
 };
 
index 62689a5..99995bf 100644 (file)
@@ -166,19 +166,16 @@ enum {
 /*
  * Register's bits
  */
-#if defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763) ||\
-    defined(CONFIG_ARCH_R8A7740)
-/* EDSR */
+/* EDSR : sh7734, sh7757, sh7763, and r8a7740 only */
 enum EDSR_BIT {
        EDSR_ENT = 0x01, EDSR_ENR = 0x02,
 };
 #define EDSR_ENALL (EDSR_ENT|EDSR_ENR)
 
-/* GECMR */
+/* GECMR : sh7734, sh7763 and r8a7740 only */
 enum GECMR_BIT {
        GECMR_10 = 0x0, GECMR_100 = 0x04, GECMR_1000 = 0x01,
 };
-#endif
 
 /* EDMR */
 enum DMAC_M_BIT {
@@ -251,13 +248,19 @@ enum EESR_BIT {
        EESR_CERF       = 0x00000001,
 };
 
+#define EESR_RX_CHECK          (EESR_FRC  | /* Frame recv */           \
+                                EESR_RMAF | /* Multicast address recv */ \
+                                EESR_RRF  | /* Bit frame recv */       \
+                                EESR_RTLF | /* Long frame recv */      \
+                                EESR_RTSF | /* Short frame recv */     \
+                                EESR_PRE  | /* PHY-LSI recv error */   \
+                                EESR_CERF)  /* Recv frame CRC error */
+
 #define DEFAULT_TX_CHECK       (EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | \
                                 EESR_RTO)
 #define DEFAULT_EESR_ERR_CHECK (EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | \
                                 EESR_RDE | EESR_RFRMER | EESR_ADE | \
                                 EESR_TFE | EESR_TDE | EESR_ECI)
-#define DEFAULT_TX_ERROR_CHECK (EESR_TWB | EESR_TABT | EESR_ADE | EESR_TDE | \
-                                EESR_TFE)
 
 /* EESIPR */
 enum DMAC_IM_BIT {
@@ -299,11 +302,11 @@ enum FCFTR_BIT {
 #define DEFAULT_FIFO_F_D_RFF   (FCFTR_RFF2 | FCFTR_RFF1 | FCFTR_RFF0)
 #define DEFAULT_FIFO_F_D_RFD   (FCFTR_RFD2 | FCFTR_RFD1 | FCFTR_RFD0)
 
-/* Transfer descriptor bit */
+/* Transmit descriptor bit */
 enum TD_STS_BIT {
-       TD_TACT = 0x80000000,
-       TD_TDLE = 0x40000000, TD_TFP1 = 0x20000000,
-       TD_TFP0 = 0x10000000,
+       TD_TACT = 0x80000000, TD_TDLE = 0x40000000,
+       TD_TFP1 = 0x20000000, TD_TFP0 = 0x10000000,
+       TD_TFE  = 0x08000000, TD_TWBI = 0x04000000,
 };
 #define TDF1ST TD_TFP1
 #define TDFEND TD_TFP0
@@ -463,9 +466,9 @@ struct sh_eth_cpu_data {
        /* interrupt checking mask */
        unsigned long tx_check;
        unsigned long eesr_err_check;
-       unsigned long tx_error_check;
 
        /* hardware features */
+       unsigned long irq_flags;        /* IRQ configuration flags */
        unsigned no_psr:1;              /* EtherC DO NOT have PSR */
        unsigned apr:1;                 /* EtherC have APR */
        unsigned mpr:1;                 /* EtherC have MPR */
@@ -478,6 +481,7 @@ struct sh_eth_cpu_data {
        unsigned no_ade:1;      /* E-DMAC DO NOT have ADE bit in EESR */
        unsigned hw_crc:1;      /* E-DMAC have CSMR */
        unsigned select_mii:1;  /* EtherC have RMII_MII (MII select register) */
+       unsigned shift_rd0:1;   /* shift Rx descriptor word 0 right by 16 */
 };
 
 struct sh_eth_private {
@@ -499,6 +503,7 @@ struct sh_eth_private {
        u32 cur_tx, dirty_tx;
        u32 rx_buf_sz;          /* Based on MTU+slack. */
        int edmac_endian;
+       struct napi_struct napi;
        /* MII transceiver section. */
        u32 phy_id;                                     /* PHY ID */
        struct mii_bus *mii_bus;        /* MDIO bus control */
index b6739af..a99739c 100644 (file)
@@ -1040,7 +1040,6 @@ static int s6gmac_remove(struct platform_device *pdev)
                unregister_netdev(dev);
                free_irq(dev->irq, dev);
                free_netdev(dev);
-               platform_set_drvdata(pdev, NULL);
        }
        return 0;
 }
index 0ad5694..856e523 100644 (file)
@@ -818,7 +818,6 @@ static int __exit sgiseeq_remove(struct platform_device *pdev)
        dma_free_noncoherent(&pdev->dev, sizeof(*sp->srings), sp->srings,
                             sp->srings_dma);
        free_netdev(dev);
-       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
index 4a14a94..c729688 100644 (file)
@@ -21,8 +21,8 @@
 #include <linux/ethtool.h>
 #include <linux/topology.h>
 #include <linux/gfp.h>
-#include <linux/cpu_rmap.h>
 #include <linux/aer.h>
+#include <linux/interrupt.h>
 #include "net_driver.h"
 #include "efx.h"
 #include "nic.h"
@@ -1283,29 +1283,6 @@ static unsigned int efx_wanted_parallelism(struct efx_nic *efx)
        return count;
 }
 
-static int
-efx_init_rx_cpu_rmap(struct efx_nic *efx, struct msix_entry *xentries)
-{
-#ifdef CONFIG_RFS_ACCEL
-       unsigned int i;
-       int rc;
-
-       efx->net_dev->rx_cpu_rmap = alloc_irq_cpu_rmap(efx->n_rx_channels);
-       if (!efx->net_dev->rx_cpu_rmap)
-               return -ENOMEM;
-       for (i = 0; i < efx->n_rx_channels; i++) {
-               rc = irq_cpu_rmap_add(efx->net_dev->rx_cpu_rmap,
-                                     xentries[i].vector);
-               if (rc) {
-                       free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap);
-                       efx->net_dev->rx_cpu_rmap = NULL;
-                       return rc;
-               }
-       }
-#endif
-       return 0;
-}
-
 /* Probe the number and type of interrupts we are able to obtain, and
  * the resulting numbers of channels and RX queues.
  */
@@ -1359,11 +1336,6 @@ static int efx_probe_interrupts(struct efx_nic *efx)
                                efx->n_tx_channels = n_channels;
                                efx->n_rx_channels = n_channels;
                        }
-                       rc = efx_init_rx_cpu_rmap(efx, xentries);
-                       if (rc) {
-                               pci_disable_msix(efx->pci_dev);
-                               return rc;
-                       }
                        for (i = 0; i < efx->n_channels; i++)
                                efx_get_channel(efx, i)->irq =
                                        xentries[i].vector;
@@ -1427,6 +1399,10 @@ static void efx_start_interrupts(struct efx_nic *efx, bool may_keep_eventq)
 
        BUG_ON(efx->state == STATE_DISABLED);
 
+       if (efx->eeh_disabled_legacy_irq) {
+               enable_irq(efx->legacy_irq);
+               efx->eeh_disabled_legacy_irq = false;
+       }
        if (efx->legacy_irq)
                efx->legacy_irq_enabled = true;
        efx_nic_enable_interrupts(efx);
@@ -2120,7 +2096,7 @@ static void efx_update_name(struct efx_nic *efx)
 static int efx_netdev_event(struct notifier_block *this,
                            unsigned long event, void *ptr)
 {
-       struct net_device *net_dev = ptr;
+       struct net_device *net_dev = netdev_notifier_info_to_dev(ptr);
 
        if (net_dev->netdev_ops == &efx_netdev_ops &&
            event == NETDEV_CHANGENAME)
@@ -2365,7 +2341,7 @@ out:
  * Returns 0 if the recovery mechanisms are unsuccessful.
  * Returns a non-zero value otherwise.
  */
-static int efx_try_recovery(struct efx_nic *efx)
+int efx_try_recovery(struct efx_nic *efx)
 {
 #ifdef CONFIG_EEH
        /* A PCI error can occur and not be seen by EEH because nothing
@@ -2603,10 +2579,6 @@ static void efx_pci_remove_main(struct efx_nic *efx)
        BUG_ON(efx->state == STATE_READY);
        cancel_work_sync(&efx->reset_work);
 
-#ifdef CONFIG_RFS_ACCEL
-       free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap);
-       efx->net_dev->rx_cpu_rmap = NULL;
-#endif
        efx_stop_interrupts(efx, false);
        efx_nic_fini_interrupt(efx);
        efx_fini_port(efx);
index 8372da2..bdb30bb 100644 (file)
@@ -124,6 +124,7 @@ extern const struct ethtool_ops efx_ethtool_ops;
 extern int efx_reset(struct efx_nic *efx, enum reset_type method);
 extern void efx_reset_down(struct efx_nic *efx, enum reset_type method);
 extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok);
+extern int efx_try_recovery(struct efx_nic *efx);
 
 /* Global */
 extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
index 6e76817..1fc2145 100644 (file)
@@ -1114,6 +1114,20 @@ static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev,
        return 0;
 }
 
+int efx_ethtool_get_ts_info(struct net_device *net_dev,
+                           struct ethtool_ts_info *ts_info)
+{
+       struct efx_nic *efx = netdev_priv(net_dev);
+
+       /* Software capabilities */
+       ts_info->so_timestamping = (SOF_TIMESTAMPING_RX_SOFTWARE |
+                                   SOF_TIMESTAMPING_SOFTWARE);
+       ts_info->phc_index = -1;
+
+       efx_ptp_get_ts_info(efx, ts_info);
+       return 0;
+}
+
 static int efx_ethtool_get_module_eeprom(struct net_device *net_dev,
                                         struct ethtool_eeprom *ee,
                                         u8 *data)
@@ -1176,7 +1190,7 @@ const struct ethtool_ops efx_ethtool_ops = {
        .get_rxfh_indir_size    = efx_ethtool_get_rxfh_indir_size,
        .get_rxfh_indir         = efx_ethtool_get_rxfh_indir,
        .set_rxfh_indir         = efx_ethtool_set_rxfh_indir,
-       .get_ts_info            = efx_ptp_get_ts_info,
+       .get_ts_info            = efx_ethtool_get_ts_info,
        .get_module_info        = efx_ethtool_get_module_info,
        .get_module_eeprom      = efx_ethtool_get_module_eeprom,
 };
index 2397f0e..b74a60a 100644 (file)
@@ -1185,8 +1185,21 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
 
        nhoff = skb_network_offset(skb);
 
-       if (skb->protocol != htons(ETH_P_IP))
+       if (skb->protocol == htons(ETH_P_8021Q)) {
+               EFX_BUG_ON_PARANOID(skb_headlen(skb) <
+                                   nhoff + sizeof(struct vlan_hdr));
+               if (((const struct vlan_hdr *)skb->data + nhoff)->
+                   h_vlan_encapsulated_proto != htons(ETH_P_IP))
+                       return -EPROTONOSUPPORT;
+
+               /* This is IP over 802.1q VLAN.  We can't filter on the
+                * IP 5-tuple and the vlan together, so just strip the
+                * vlan header and filter on the IP part.
+                */
+               nhoff += sizeof(struct vlan_hdr);
+       } else if (skb->protocol != htons(ETH_P_IP)) {
                return -EPROTONOSUPPORT;
+       }
 
        /* RFS must validate the IP header length before calling us */
        EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip));
index 39d6bd7..f4c7e6b 100644 (file)
@@ -243,6 +243,7 @@ struct efx_rx_buffer {
 #define EFX_RX_BUF_LAST_IN_PAGE        0x0001
 #define EFX_RX_PKT_CSUMMED     0x0002
 #define EFX_RX_PKT_DISCARD     0x0004
+#define EFX_RX_PKT_TCP         0x0040
 
 /**
  * struct efx_rx_page_state - Page-based rx buffer state
@@ -784,9 +785,11 @@ struct efx_nic {
 
        char name[IFNAMSIZ];
        struct pci_dev *pci_dev;
+       unsigned int port_num;
        const struct efx_nic_type *type;
        int legacy_irq;
        bool legacy_irq_enabled;
+       bool eeh_disabled_legacy_irq;
        struct workqueue_struct *workqueue;
        char workqueue_name[16];
        struct work_struct reset_work;
@@ -916,7 +919,7 @@ static inline int efx_dev_registered(struct efx_nic *efx)
 
 static inline unsigned int efx_port_num(struct efx_nic *efx)
 {
-       return efx->net_dev->dev_id;
+       return efx->port_num;
 }
 
 /**
index b0503cd..56ed3bc 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/seq_file.h>
+#include <linux/cpu_rmap.h>
 #include "net_driver.h"
 #include "bitfield.h"
 #include "efx.h"
@@ -1080,12 +1081,21 @@ efx_handle_rx_event(struct efx_channel *channel, const efx_qword_t *event)
        rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE);
 
        if (likely(rx_ev_pkt_ok)) {
-               /* If packet is marked as OK and packet type is TCP/IP or
-                * UDP/IP, then we can rely on the hardware checksum.
+               /* If packet is marked as OK then we can rely on the
+                * hardware checksum and classification.
                 */
-               flags = (rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP ||
-                        rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP) ?
-                       EFX_RX_PKT_CSUMMED : 0;
+               flags = 0;
+               switch (rx_ev_hdr_type) {
+               case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP:
+                       flags |= EFX_RX_PKT_TCP;
+                       /* fall through */
+               case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP:
+                       flags |= EFX_RX_PKT_CSUMMED;
+                       /* fall through */
+               case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_OTHER:
+               case FSE_AZ_RX_EV_HDR_TYPE_OTHER:
+                       break;
+               }
        } else {
                flags = efx_handle_rx_not_ok(rx_queue, event);
        }
@@ -1579,6 +1589,16 @@ static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id)
        efx_readd(efx, &reg, FR_BZ_INT_ISR0);
        queues = EFX_EXTRACT_DWORD(reg, 0, 31);
 
+       /* Legacy interrupts are disabled too late by the EEH kernel
+        * code. Disable them earlier.
+        * If an EEH error occurred, the read will have returned all ones.
+        */
+       if (EFX_DWORD_IS_ALL_ONES(reg) && efx_try_recovery(efx) &&
+           !efx->eeh_disabled_legacy_irq) {
+               disable_irq_nosync(efx->legacy_irq);
+               efx->eeh_disabled_legacy_irq = true;
+       }
+
        /* Handle non-event-queue sources */
        if (queues & (1U << efx->irq_level)) {
                syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT);
@@ -1687,6 +1707,7 @@ void efx_nic_push_rx_indir_table(struct efx_nic *efx)
 int efx_nic_init_interrupt(struct efx_nic *efx)
 {
        struct efx_channel *channel;
+       unsigned int n_irqs;
        int rc;
 
        if (!EFX_INT_MODE_USE_MSI(efx)) {
@@ -1707,7 +1728,19 @@ int efx_nic_init_interrupt(struct efx_nic *efx)
                return 0;
        }
 
+#ifdef CONFIG_RFS_ACCEL
+       if (efx->interrupt_mode == EFX_INT_MODE_MSIX) {
+               efx->net_dev->rx_cpu_rmap =
+                       alloc_irq_cpu_rmap(efx->n_rx_channels);
+               if (!efx->net_dev->rx_cpu_rmap) {
+                       rc = -ENOMEM;
+                       goto fail1;
+               }
+       }
+#endif
+
        /* Hook MSI or MSI-X interrupt */
+       n_irqs = 0;
        efx_for_each_channel(channel, efx) {
                rc = request_irq(channel->irq, efx_msi_interrupt,
                                 IRQF_PROBE_SHARED, /* Not shared */
@@ -1718,13 +1751,31 @@ int efx_nic_init_interrupt(struct efx_nic *efx)
                                  "failed to hook IRQ %d\n", channel->irq);
                        goto fail2;
                }
+               ++n_irqs;
+
+#ifdef CONFIG_RFS_ACCEL
+               if (efx->interrupt_mode == EFX_INT_MODE_MSIX &&
+                   channel->channel < efx->n_rx_channels) {
+                       rc = irq_cpu_rmap_add(efx->net_dev->rx_cpu_rmap,
+                                             channel->irq);
+                       if (rc)
+                               goto fail2;
+               }
+#endif
        }
 
        return 0;
 
  fail2:
-       efx_for_each_channel(channel, efx)
+#ifdef CONFIG_RFS_ACCEL
+       free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap);
+       efx->net_dev->rx_cpu_rmap = NULL;
+#endif
+       efx_for_each_channel(channel, efx) {
+               if (n_irqs-- == 0)
+                       break;
                free_irq(channel->irq, &efx->channel[channel->channel]);
+       }
  fail1:
        return rc;
 }
@@ -1734,11 +1785,14 @@ void efx_nic_fini_interrupt(struct efx_nic *efx)
        struct efx_channel *channel;
        efx_oword_t reg;
 
+#ifdef CONFIG_RFS_ACCEL
+       free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap);
+       efx->net_dev->rx_cpu_rmap = NULL;
+#endif
+
        /* Disable MSI/MSI-X interrupts */
-       efx_for_each_channel(channel, efx) {
-               if (channel->irq)
-                       free_irq(channel->irq, &efx->channel[channel->channel]);
-       }
+       efx_for_each_channel(channel, efx)
+               free_irq(channel->irq, &efx->channel[channel->channel]);
 
        /* ACK legacy interrupt */
        if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0)
index 1b00033..d63c299 100644 (file)
@@ -254,8 +254,8 @@ extern int efx_sriov_set_vf_spoofchk(struct net_device *net_dev, int vf,
 struct ethtool_ts_info;
 extern void efx_ptp_probe(struct efx_nic *efx);
 extern int efx_ptp_ioctl(struct efx_nic *efx, struct ifreq *ifr, int cmd);
-extern int efx_ptp_get_ts_info(struct net_device *net_dev,
-                              struct ethtool_ts_info *ts_info);
+extern void efx_ptp_get_ts_info(struct efx_nic *efx,
+                               struct ethtool_ts_info *ts_info);
 extern bool efx_ptp_is_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
 extern int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
 extern void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev);
index 9a95abf..b495394 100644 (file)
@@ -1203,18 +1203,16 @@ static int efx_ptp_ts_init(struct efx_nic *efx, struct hwtstamp_config *init)
        return 0;
 }
 
-int
-efx_ptp_get_ts_info(struct net_device *net_dev, struct ethtool_ts_info *ts_info)
+void efx_ptp_get_ts_info(struct efx_nic *efx, struct ethtool_ts_info *ts_info)
 {
-       struct efx_nic *efx = netdev_priv(net_dev);
        struct efx_ptp_data *ptp = efx->ptp_data;
 
        if (!ptp)
-               return -EOPNOTSUPP;
+               return;
 
-       ts_info->so_timestamping = (SOF_TIMESTAMPING_TX_HARDWARE |
-                                   SOF_TIMESTAMPING_RX_HARDWARE |
-                                   SOF_TIMESTAMPING_RAW_HARDWARE);
+       ts_info->so_timestamping |= (SOF_TIMESTAMPING_TX_HARDWARE |
+                                    SOF_TIMESTAMPING_RX_HARDWARE |
+                                    SOF_TIMESTAMPING_RAW_HARDWARE);
        ts_info->phc_index = ptp_clock_index(ptp->phc_clock);
        ts_info->tx_types = 1 << HWTSTAMP_TX_OFF | 1 << HWTSTAMP_TX_ON;
        ts_info->rx_filters = (1 << HWTSTAMP_FILTER_NONE |
@@ -1224,7 +1222,6 @@ efx_ptp_get_ts_info(struct net_device *net_dev, struct ethtool_ts_info *ts_info)
                               1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT |
                               1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC |
                               1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ);
-       return 0;
 }
 
 int efx_ptp_ioctl(struct efx_nic *efx, struct ifreq *ifr, int cmd)
index a7dfe36..6af9cfd 100644 (file)
@@ -36,7 +36,7 @@
 #define EFX_RECYCLE_RING_SIZE_NOIOMMU (2 * EFX_RX_PREFERRED_BATCH)
 
 /* Size of buffer allocated for skb header area. */
-#define EFX_SKB_HEADERS  64u
+#define EFX_SKB_HEADERS  128u
 
 /* This is the percentage fill level below which new RX descriptors
  * will be added to the RX descriptor ring.
@@ -282,9 +282,9 @@ static void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue,
 }
 
 /* Recycle the pages that are used by buffers that have just been received. */
-static void efx_recycle_rx_buffers(struct efx_channel *channel,
-                                  struct efx_rx_buffer *rx_buf,
-                                  unsigned int n_frags)
+static void efx_recycle_rx_pages(struct efx_channel *channel,
+                                struct efx_rx_buffer *rx_buf,
+                                unsigned int n_frags)
 {
        struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel);
 
@@ -294,6 +294,20 @@ static void efx_recycle_rx_buffers(struct efx_channel *channel,
        } while (--n_frags);
 }
 
+static void efx_discard_rx_packet(struct efx_channel *channel,
+                                 struct efx_rx_buffer *rx_buf,
+                                 unsigned int n_frags)
+{
+       struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel);
+
+       efx_recycle_rx_pages(channel, rx_buf, n_frags);
+
+       do {
+               efx_free_rx_buffer(rx_buf);
+               rx_buf = efx_rx_buf_next(rx_queue, rx_buf);
+       } while (--n_frags);
+}
+
 /**
  * efx_fast_push_rx_descriptors - push new RX descriptors quickly
  * @rx_queue:          RX descriptor queue
@@ -533,8 +547,7 @@ void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
         */
        if (unlikely(rx_buf->flags & EFX_RX_PKT_DISCARD)) {
                efx_rx_flush_packet(channel);
-               put_page(rx_buf->page);
-               efx_recycle_rx_buffers(channel, rx_buf, n_frags);
+               efx_discard_rx_packet(channel, rx_buf, n_frags);
                return;
        }
 
@@ -570,9 +583,9 @@ void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
                efx_sync_rx_buffer(efx, rx_buf, rx_buf->len);
        }
 
-       /* All fragments have been DMA-synced, so recycle buffers and pages. */
+       /* All fragments have been DMA-synced, so recycle pages. */
        rx_buf = efx_rx_buffer(rx_queue, index);
-       efx_recycle_rx_buffers(channel, rx_buf, n_frags);
+       efx_recycle_rx_pages(channel, rx_buf, n_frags);
 
        /* Pipeline receives so that we give time for packet headers to be
         * prefetched into cache.
@@ -598,6 +611,8 @@ static void efx_rx_deliver(struct efx_channel *channel, u8 *eh,
 
        /* Set the SKB flags */
        skb_checksum_none_assert(skb);
+       if (likely(rx_buf->flags & EFX_RX_PKT_CSUMMED))
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
 
        if (channel->type->receive_skb)
                if (channel->type->receive_skb(channel, skb))
@@ -627,7 +642,7 @@ void __efx_rx_packet(struct efx_channel *channel)
        if (unlikely(!(efx->net_dev->features & NETIF_F_RXCSUM)))
                rx_buf->flags &= ~EFX_RX_PKT_CSUMMED;
 
-       if (!channel->type->receive_skb)
+       if ((rx_buf->flags & EFX_RX_PKT_TCP) && !channel->type->receive_skb)
                efx_rx_packet_gro(channel, rx_buf, channel->rx_pkt_n_frags, eh);
        else
                efx_rx_deliver(channel, eh, rx_buf, channel->rx_pkt_n_frags);
@@ -675,7 +690,7 @@ static void efx_init_rx_recycle_ring(struct efx_nic *efx,
 #ifdef CONFIG_PPC64
        bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_IOMMU;
 #else
-       if (efx->pci_dev->dev.iommu_group)
+       if (iommu_present(&pci_bus_type))
                bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_IOMMU;
        else
                bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_NOIOMMU;
index 5166924..8c91775 100644 (file)
@@ -304,7 +304,7 @@ static int siena_probe_nic(struct efx_nic *efx)
        }
 
        efx_reado(efx, &reg, FR_AZ_CS_DEBUG);
-       efx->net_dev->dev_id = EFX_OWORD_FIELD(reg, FRF_CZ_CS_PORT_NUM) - 1;
+       efx->port_num = EFX_OWORD_FIELD(reg, FRF_CZ_CS_PORT_NUM) - 1;
 
        efx_mcdi_init(efx);
 
index c1c4bb8..e832f46 100644 (file)
@@ -22,7 +22,6 @@ config SGI_IOC3_ETH
        bool "SGI IOC3 Ethernet"
        depends on PCI && SGI_IP27
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          If you have a network (Ethernet) card of this type, say Y and read
index 7ed08c3..ffa7843 100644 (file)
@@ -1398,16 +1398,6 @@ static struct pci_driver ioc3_driver = {
        .remove         = ioc3_remove_one,
 };
 
-static int __init ioc3_init_module(void)
-{
-       return pci_register_driver(&ioc3_driver);
-}
-
-static void __exit ioc3_cleanup_module(void)
-{
-       pci_unregister_driver(&ioc3_driver);
-}
-
 static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        unsigned long data;
@@ -1677,9 +1667,7 @@ static void ioc3_set_multicast_list(struct net_device *dev)
        netif_wake_queue(dev);                  /* Let us get going again. */
 }
 
+module_pci_driver(ioc3_driver);
 MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
 MODULE_DESCRIPTION("SGI IOC3 Ethernet driver");
 MODULE_LICENSE("GPL");
-
-module_init(ioc3_init_module);
-module_exit(ioc3_cleanup_module);
index 4bdbaad..9f5f35e 100644 (file)
@@ -863,7 +863,6 @@ static int __exit meth_remove(struct platform_device *pdev)
 
        unregister_netdev(dev);
        free_netdev(dev);
-       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
index 28f7268..5eb933c 100644 (file)
@@ -1578,19 +1578,7 @@ static struct pci_driver sc92031_pci_driver = {
        .resume         = sc92031_resume,
 };
 
-static int __init sc92031_init(void)
-{
-       return pci_register_driver(&sc92031_pci_driver);
-}
-
-static void __exit sc92031_exit(void)
-{
-       pci_unregister_driver(&sc92031_pci_driver);
-}
-
-module_init(sc92031_init);
-module_exit(sc92031_exit);
-
+module_pci_driver(sc92031_pci_driver);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Cesar Eduardo Barros <cesarb@cesarb.net>");
 MODULE_DESCRIPTION("Silan SC92031 PCI Fast Ethernet Adapter driver");
index f1135cc..68d052b 100644 (file)
@@ -22,7 +22,6 @@ config SIS900
        tristate "SiS 900/7016 PCI Fast Ethernet Adapter support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          This is a driver for the Fast Ethernet PCI network cards based on
@@ -39,7 +38,6 @@ config SIS190
        tristate "SiS190/SiS191 gigabit ethernet support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          Say Y here if you have a SiS 190 PCI Fast Ethernet adapter or
index 9a9c379..02df089 100644 (file)
@@ -1934,15 +1934,4 @@ static struct pci_driver sis190_pci_driver = {
        .remove         = sis190_remove_one,
 };
 
-static int __init sis190_init_module(void)
-{
-       return pci_register_driver(&sis190_pci_driver);
-}
-
-static void __exit sis190_cleanup_module(void)
-{
-       pci_unregister_driver(&sis190_pci_driver);
-}
-
-module_init(sis190_init_module);
-module_exit(sis190_cleanup_module);
+module_pci_driver(sis190_pci_driver);
index bb4c167..068fc44 100644 (file)
@@ -37,7 +37,6 @@ config SMC9194
 config SMC91X
        tristate "SMC 91C9x/91C1xxx support"
        select CRC32
-       select NET_CORE
        select MII
        depends on (ARM || M32R || SUPERH || MIPS || BLACKFIN || \
                    MN10300 || COLDFIRE || ARM64)
@@ -57,7 +56,6 @@ config PCMCIA_SMC91C92
        tristate "SMC 91Cxx PCMCIA support"
        depends on PCMCIA
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
@@ -70,7 +68,6 @@ config EPIC100
        tristate "SMC EtherPower II"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC,
@@ -81,7 +78,6 @@ config EPIC100
 config SMC911X
        tristate "SMSC LAN911[5678] support"
        select CRC32
-       select NET_CORE
        select MII
        depends on (ARM || SUPERH || MN10300)
        ---help---
@@ -97,9 +93,8 @@ config SMC911X
 
 config SMSC911X
        tristate "SMSC LAN911x/LAN921x families embedded ethernet support"
-       depends on (ARM || SUPERH || BLACKFIN || MIPS || MN10300)
+       depends on HAS_IOMEM
        select CRC32
-       select NET_CORE
        select MII
        select PHYLIB
        ---help---
index 9dd842d..345558f 100644 (file)
@@ -2087,7 +2087,6 @@ static int smc911x_drv_probe(struct platform_device *pdev)
        ndev->base_addr = res->start;
        ret = smc911x_probe(ndev);
        if (ret != 0) {
-               platform_set_drvdata(pdev, NULL);
                iounmap(addr);
 release_both:
                free_netdev(ndev);
@@ -2113,7 +2112,6 @@ static int smc911x_drv_remove(struct platform_device *pdev)
        struct resource *res;
 
        DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
-       platform_set_drvdata(pdev, NULL);
 
        unregister_netdev(ndev);
 
index dfbf978..cde13be 100644 (file)
@@ -2299,7 +2299,6 @@ static int smc_drv_probe(struct platform_device *pdev)
        return 0;
 
  out_iounmap:
-       platform_set_drvdata(pdev, NULL);
        iounmap(addr);
  out_release_attrib:
        smc_release_attrib(pdev, ndev);
@@ -2319,8 +2318,6 @@ static int smc_drv_remove(struct platform_device *pdev)
        struct smc_local *lp = netdev_priv(ndev);
        struct resource *res;
 
-       platform_set_drvdata(pdev, NULL);
-
        unregister_netdev(ndev);
 
        free_irq(ndev->irq, ndev);
index 3663b9e..a141921 100644 (file)
@@ -2284,7 +2284,6 @@ static int smsc911x_drv_remove(struct platform_device *pdev)
        mdiobus_unregister(pdata->mii_bus);
        mdiobus_free(pdata->mii_bus);
 
-       platform_set_drvdata(pdev, NULL);
        unregister_netdev(dev);
        free_irq(dev->irq, dev);
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
@@ -2539,7 +2538,6 @@ out_disable_resources:
 out_enable_resources_fail:
        smsc911x_free_resources(pdev);
 out_request_resources_fail:
-       platform_set_drvdata(pdev, NULL);
        iounmap(pdata->ioaddr);
        free_netdev(dev);
 out_release_io_1:
index 43c1f32..6e52c0f 100644 (file)
@@ -1,7 +1,6 @@
 config STMMAC_ETH
        tristate "STMicroelectronics 10/100/1000 Ethernet driver"
        depends on HAS_IOMEM && HAS_DMA
-       select NET_CORE
        select MII
        select PHYLIB
        select CRC32
index 9517697..7eb8bab 100644 (file)
 #include "descs.h"
 #include "mmc.h"
 
-#undef CHIP_DEBUG_PRINT
-/* Turn-on extra printk debug for MAC core, dma and descriptors */
-/* #define CHIP_DEBUG_PRINT */
-
-#ifdef CHIP_DEBUG_PRINT
-#define CHIP_DBG(fmt, args...)  printk(fmt, ## args)
-#else
-#define CHIP_DBG(fmt, args...)  do { } while (0)
-#endif
-
 /* Synopsys Core versions */
 #define        DWMAC_CORE_3_40 0x34
 #define        DWMAC_CORE_3_50 0x35
index 7e05e8d..cdd9268 100644 (file)
@@ -91,8 +91,8 @@ static void dwmac1000_set_filter(struct net_device *dev, int id)
        unsigned int value = 0;
        unsigned int perfect_addr_number;
 
-       CHIP_DBG(KERN_INFO "%s: # mcasts %d, # unicast %d\n",
-                __func__, netdev_mc_count(dev), netdev_uc_count(dev));
+       pr_debug("%s: # mcasts %d, # unicast %d\n", __func__,
+                netdev_mc_count(dev), netdev_uc_count(dev));
 
        if (dev->flags & IFF_PROMISC)
                value = GMAC_FRAME_FILTER_PR;
@@ -152,7 +152,7 @@ static void dwmac1000_set_filter(struct net_device *dev, int id)
 #endif
        writel(value, ioaddr + GMAC_FRAME_FILTER);
 
-       CHIP_DBG(KERN_INFO "\tFilter: 0x%08x\n\tHash: HI 0x%08x, LO 0x%08x\n",
+       pr_debug("\tFilter: 0x%08x\n\tHash: HI 0x%08x, LO 0x%08x\n",
                 readl(ioaddr + GMAC_FRAME_FILTER),
                 readl(ioaddr + GMAC_HASH_HIGH), readl(ioaddr + GMAC_HASH_LOW));
 }
@@ -162,18 +162,18 @@ static void dwmac1000_flow_ctrl(void __iomem *ioaddr, unsigned int duplex,
 {
        unsigned int flow = 0;
 
-       CHIP_DBG(KERN_DEBUG "GMAC Flow-Control:\n");
+       pr_debug("GMAC Flow-Control:\n");
        if (fc & FLOW_RX) {
-               CHIP_DBG(KERN_DEBUG "\tReceive Flow-Control ON\n");
+               pr_debug("\tReceive Flow-Control ON\n");
                flow |= GMAC_FLOW_CTRL_RFE;
        }
        if (fc & FLOW_TX) {
-               CHIP_DBG(KERN_DEBUG "\tTransmit Flow-Control ON\n");
+               pr_debug("\tTransmit Flow-Control ON\n");
                flow |= GMAC_FLOW_CTRL_TFE;
        }
 
        if (duplex) {
-               CHIP_DBG(KERN_DEBUG "\tduplex mode: PAUSE %d\n", pause_time);
+               pr_debug("\tduplex mode: PAUSE %d\n", pause_time);
                flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT);
        }
 
@@ -185,11 +185,11 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode)
        unsigned int pmt = 0;
 
        if (mode & WAKE_MAGIC) {
-               CHIP_DBG(KERN_DEBUG "GMAC: WOL Magic frame\n");
+               pr_debug("GMAC: WOL Magic frame\n");
                pmt |= power_down | magic_pkt_en;
        }
        if (mode & WAKE_UCAST) {
-               CHIP_DBG(KERN_DEBUG "GMAC: WOL on global unicast\n");
+               pr_debug("GMAC: WOL on global unicast\n");
                pmt |= global_unicast;
        }
 
@@ -203,23 +203,13 @@ static int dwmac1000_irq_status(void __iomem *ioaddr,
        int ret = 0;
 
        /* Not used events (e.g. MMC interrupts) are not handled. */
-       if ((intr_status & mmc_tx_irq)) {
-               CHIP_DBG(KERN_INFO "GMAC: MMC tx interrupt: 0x%08x\n",
-                        readl(ioaddr + GMAC_MMC_TX_INTR));
+       if ((intr_status & mmc_tx_irq))
                x->mmc_tx_irq_n++;
-       }
-       if (unlikely(intr_status & mmc_rx_irq)) {
-               CHIP_DBG(KERN_INFO "GMAC: MMC rx interrupt: 0x%08x\n",
-                        readl(ioaddr + GMAC_MMC_RX_INTR));
+       if (unlikely(intr_status & mmc_rx_irq))
                x->mmc_rx_irq_n++;
-       }
-       if (unlikely(intr_status & mmc_rx_csum_offload_irq)) {
-               CHIP_DBG(KERN_INFO "GMAC: MMC rx csum offload: 0x%08x\n",
-                        readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD));
+       if (unlikely(intr_status & mmc_rx_csum_offload_irq))
                x->mmc_rx_csum_offload_irq_n++;
-       }
        if (unlikely(intr_status & pmt_irq)) {
-               CHIP_DBG(KERN_INFO "GMAC: received Magic frame\n");
                /* clear the PMT bits 5 and 6 by reading the PMT status reg */
                readl(ioaddr + GMAC_PMT);
                x->irq_receive_pmt_irq_n++;
@@ -229,32 +219,22 @@ static int dwmac1000_irq_status(void __iomem *ioaddr,
                /* Clean LPI interrupt by reading the Reg 12 */
                ret = readl(ioaddr + LPI_CTRL_STATUS);
 
-               if (ret & LPI_CTRL_STATUS_TLPIEN) {
-                       CHIP_DBG(KERN_INFO "GMAC TX entered in LPI\n");
+               if (ret & LPI_CTRL_STATUS_TLPIEN)
                        x->irq_tx_path_in_lpi_mode_n++;
-               }
-               if (ret & LPI_CTRL_STATUS_TLPIEX) {
-                       CHIP_DBG(KERN_INFO "GMAC TX exit from LPI\n");
+               if (ret & LPI_CTRL_STATUS_TLPIEX)
                        x->irq_tx_path_exit_lpi_mode_n++;
-               }
-               if (ret & LPI_CTRL_STATUS_RLPIEN) {
-                       CHIP_DBG(KERN_INFO "GMAC RX entered in LPI\n");
+               if (ret & LPI_CTRL_STATUS_RLPIEN)
                        x->irq_rx_path_in_lpi_mode_n++;
-               }
-               if (ret & LPI_CTRL_STATUS_RLPIEX) {
-                       CHIP_DBG(KERN_INFO "GMAC RX exit from LPI\n");
+               if (ret & LPI_CTRL_STATUS_RLPIEX)
                        x->irq_rx_path_exit_lpi_mode_n++;
-               }
        }
 
        if ((intr_status & pcs_ane_irq) || (intr_status & pcs_link_irq)) {
-               CHIP_DBG(KERN_INFO "GMAC PCS ANE IRQ\n");
                readl(ioaddr + GMAC_AN_STATUS);
                x->irq_pcs_ane_n++;
        }
        if (intr_status & rgmii_irq) {
                u32 status = readl(ioaddr + GMAC_S_R_GMII);
-               CHIP_DBG(KERN_INFO "GMAC RGMII/SGMII interrupt\n");
                x->irq_rgmii_n++;
 
                /* Save and dump the link status. */
@@ -271,11 +251,12 @@ static int dwmac1000_irq_status(void __iomem *ioaddr,
                                x->pcs_speed = SPEED_10;
 
                        x->pcs_link = 1;
-                       pr_debug("Link is Up - %d/%s\n", (int)x->pcs_speed,
+                       pr_debug("%s: Link is Up - %d/%s\n", __func__,
+                                (int)x->pcs_speed,
                                 x->pcs_duplex ? "Full" : "Half");
                } else {
                        x->pcs_link = 0;
-                       pr_debug("Link is Down\n");
+                       pr_debug("%s: Link is Down\n", __func__);
                }
        }
 
index 2c431b6..0c2058a 100644 (file)
@@ -116,7 +116,7 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
        u32 csr6 = readl(ioaddr + DMA_CONTROL);
 
        if (txmode == SF_DMA_MODE) {
-               CHIP_DBG(KERN_DEBUG "GMAC: enable TX store and forward mode\n");
+               pr_debug("GMAC: enable TX store and forward mode\n");
                /* Transmit COE type 2 cannot be done in cut-through mode. */
                csr6 |= DMA_CONTROL_TSF;
                /* Operating on second frame increase the performance
@@ -124,8 +124,7 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
                 */
                csr6 |= DMA_CONTROL_OSF;
        } else {
-               CHIP_DBG(KERN_DEBUG "GMAC: disabling TX SF (threshold %d)\n",
-                        txmode);
+               pr_debug("GMAC: disabling TX SF (threshold %d)\n", txmode);
                csr6 &= ~DMA_CONTROL_TSF;
                csr6 &= DMA_CONTROL_TC_TX_MASK;
                /* Set the transmit threshold */
@@ -142,11 +141,10 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
        }
 
        if (rxmode == SF_DMA_MODE) {
-               CHIP_DBG(KERN_DEBUG "GMAC: enable RX store and forward mode\n");
+               pr_debug("GMAC: enable RX store and forward mode\n");
                csr6 |= DMA_CONTROL_RSF;
        } else {
-               CHIP_DBG(KERN_DEBUG "GMAC: disable RX SF mode (threshold %d)\n",
-                        rxmode);
+               pr_debug("GMAC: disable RX SF mode (threshold %d)\n", rxmode);
                csr6 &= ~DMA_CONTROL_RSF;
                csr6 &= DMA_CONTROL_TC_RX_MASK;
                if (rxmode <= 32)
index 007bb2b..5857d67 100644 (file)
@@ -135,10 +135,6 @@ static void dwmac100_set_filter(struct net_device *dev, int id)
        }
 
        writel(value, ioaddr + MAC_CONTROL);
-
-       CHIP_DBG(KERN_INFO "%s: Filter: 0x%08x Hash: HI 0x%08x, LO 0x%08x\n",
-                __func__, readl(ioaddr + MAC_CONTROL),
-                readl(ioaddr + MAC_HASH_HIGH), readl(ioaddr + MAC_HASH_LOW));
 }
 
 static void dwmac100_flow_ctrl(void __iomem *ioaddr, unsigned int duplex,
index 67551c1..7d1dce9 100644 (file)
@@ -90,14 +90,14 @@ static void dwmac100_dump_dma_regs(void __iomem *ioaddr)
 {
        int i;
 
-       CHIP_DBG(KERN_DEBUG "DWMAC 100 DMA CSR\n");
+       pr_debug("DWMAC 100 DMA CSR\n");
        for (i = 0; i < 9; i++)
                pr_debug("\t CSR%d (offset 0x%x): 0x%08x\n", i,
                         (DMA_BUS_MODE + i * 4),
                         readl(ioaddr + DMA_BUS_MODE + i * 4));
-       CHIP_DBG(KERN_DEBUG "\t CSR20 (offset 0x%x): 0x%08x\n",
-                DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR));
-       CHIP_DBG(KERN_DEBUG "\t CSR21 (offset 0x%x): 0x%08x\n",
+
+       pr_debug("\tCSR20 (0x%x): 0x%08x, CSR21 (0x%x): 0x%08x\n",
+                DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR),
                 DMA_CUR_RX_BUF_ADDR, readl(ioaddr + DMA_CUR_RX_BUF_ADDR));
 }
 
index 491d7e9..484e3cf 100644 (file)
 #include "common.h"
 #include "dwmac_dma.h"
 
-#undef DWMAC_DMA_DEBUG
-#ifdef DWMAC_DMA_DEBUG
-#define DWMAC_LIB_DBG(fmt, args...)  printk(fmt, ## args)
-#else
-#define DWMAC_LIB_DBG(fmt, args...)  do { } while (0)
-#endif
-
 #define GMAC_HI_REG_AE         0x80000000
 
 /* CSR1 enables the transmit DMA to check for new descriptor */
@@ -85,24 +78,24 @@ static void show_tx_process_state(unsigned int status)
 
        switch (state) {
        case 0:
-               pr_info("- TX (Stopped): Reset or Stop command\n");
+               pr_debug("- TX (Stopped): Reset or Stop command\n");
                break;
        case 1:
-               pr_info("- TX (Running):Fetching the Tx desc\n");
+               pr_debug("- TX (Running):Fetching the Tx desc\n");
                break;
        case 2:
-               pr_info("- TX (Running): Waiting for end of tx\n");
+               pr_debug("- TX (Running): Waiting for end of tx\n");
                break;
        case 3:
-               pr_info("- TX (Running): Reading the data "
+               pr_debug("- TX (Running): Reading the data "
                       "and queuing the data into the Tx buf\n");
                break;
        case 6:
-               pr_info("- TX (Suspended): Tx Buff Underflow "
+               pr_debug("- TX (Suspended): Tx Buff Underflow "
                       "or an unavailable Transmit descriptor\n");
                break;
        case 7:
-               pr_info("- TX (Running): Closing Tx descriptor\n");
+               pr_debug("- TX (Running): Closing Tx descriptor\n");
                break;
        default:
                break;
@@ -116,29 +109,29 @@ static void show_rx_process_state(unsigned int status)
 
        switch (state) {
        case 0:
-               pr_info("- RX (Stopped): Reset or Stop command\n");
+               pr_debug("- RX (Stopped): Reset or Stop command\n");
                break;
        case 1:
-               pr_info("- RX (Running): Fetching the Rx desc\n");
+               pr_debug("- RX (Running): Fetching the Rx desc\n");
                break;
        case 2:
-               pr_info("- RX (Running):Checking for end of pkt\n");
+               pr_debug("- RX (Running):Checking for end of pkt\n");
                break;
        case 3:
-               pr_info("- RX (Running): Waiting for Rx pkt\n");
+               pr_debug("- RX (Running): Waiting for Rx pkt\n");
                break;
        case 4:
-               pr_info("- RX (Suspended): Unavailable Rx buf\n");
+               pr_debug("- RX (Suspended): Unavailable Rx buf\n");
                break;
        case 5:
-               pr_info("- RX (Running): Closing Rx descriptor\n");
+               pr_debug("- RX (Running): Closing Rx descriptor\n");
                break;
        case 6:
-               pr_info("- RX(Running): Flushing the current frame"
+               pr_debug("- RX(Running): Flushing the current frame"
                       " from the Rx buf\n");
                break;
        case 7:
-               pr_info("- RX (Running): Queuing the Rx frame"
+               pr_debug("- RX (Running): Queuing the Rx frame"
                       " from the Rx buf into memory\n");
                break;
        default:
@@ -154,51 +147,37 @@ int dwmac_dma_interrupt(void __iomem *ioaddr,
        /* read the status register (CSR5) */
        u32 intr_status = readl(ioaddr + DMA_STATUS);
 
-       DWMAC_LIB_DBG(KERN_INFO "%s: [CSR5: 0x%08x]\n", __func__, intr_status);
 #ifdef DWMAC_DMA_DEBUG
-       /* It displays the DMA process states (CSR5 register) */
+       /* Enable it to monitor DMA rx/tx status in case of critical problems */
+       pr_debug("%s: [CSR5: 0x%08x]\n", __func__, intr_status);
        show_tx_process_state(intr_status);
        show_rx_process_state(intr_status);
 #endif
        /* ABNORMAL interrupts */
        if (unlikely(intr_status & DMA_STATUS_AIS)) {
-               DWMAC_LIB_DBG(KERN_INFO "CSR5[15] DMA ABNORMAL IRQ: ");
                if (unlikely(intr_status & DMA_STATUS_UNF)) {
-                       DWMAC_LIB_DBG(KERN_INFO "transmit underflow\n");
                        ret = tx_hard_error_bump_tc;
                        x->tx_undeflow_irq++;
                }
-               if (unlikely(intr_status & DMA_STATUS_TJT)) {
-                       DWMAC_LIB_DBG(KERN_INFO "transmit jabber\n");
+               if (unlikely(intr_status & DMA_STATUS_TJT))
                        x->tx_jabber_irq++;
-               }
-               if (unlikely(intr_status & DMA_STATUS_OVF)) {
-                       DWMAC_LIB_DBG(KERN_INFO "recv overflow\n");
+
+               if (unlikely(intr_status & DMA_STATUS_OVF))
                        x->rx_overflow_irq++;
-               }
-               if (unlikely(intr_status & DMA_STATUS_RU)) {
-                       DWMAC_LIB_DBG(KERN_INFO "receive buffer unavailable\n");
+
+               if (unlikely(intr_status & DMA_STATUS_RU))
                        x->rx_buf_unav_irq++;
-               }
-               if (unlikely(intr_status & DMA_STATUS_RPS)) {
-                       DWMAC_LIB_DBG(KERN_INFO "receive process stopped\n");
+               if (unlikely(intr_status & DMA_STATUS_RPS))
                        x->rx_process_stopped_irq++;
-               }
-               if (unlikely(intr_status & DMA_STATUS_RWT)) {
-                       DWMAC_LIB_DBG(KERN_INFO "receive watchdog\n");
+               if (unlikely(intr_status & DMA_STATUS_RWT))
                        x->rx_watchdog_irq++;
-               }
-               if (unlikely(intr_status & DMA_STATUS_ETI)) {
-                       DWMAC_LIB_DBG(KERN_INFO "transmit early interrupt\n");
+               if (unlikely(intr_status & DMA_STATUS_ETI))
                        x->tx_early_irq++;
-               }
                if (unlikely(intr_status & DMA_STATUS_TPS)) {
-                       DWMAC_LIB_DBG(KERN_INFO "transmit process stopped\n");
                        x->tx_process_stopped_irq++;
                        ret = tx_hard_error;
                }
                if (unlikely(intr_status & DMA_STATUS_FBI)) {
-                       DWMAC_LIB_DBG(KERN_INFO "fatal bus error\n");
                        x->fatal_bus_error_irq++;
                        ret = tx_hard_error;
                }
@@ -224,12 +203,11 @@ int dwmac_dma_interrupt(void __iomem *ioaddr,
        /* Optional hardware blocks, interrupts should be disabled */
        if (unlikely(intr_status &
                     (DMA_STATUS_GPI | DMA_STATUS_GMI | DMA_STATUS_GLI)))
-               pr_info("%s: unexpected status %08x\n", __func__, intr_status);
+               pr_warn("%s: unexpected status %08x\n", __func__, intr_status);
 
        /* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
        writel((intr_status & 0x1ffff), ioaddr + DMA_STATUS);
 
-       DWMAC_LIB_DBG(KERN_INFO "\n\n");
        return ret;
 }
 
index 0fbc8fa..7e6628a 100644 (file)
@@ -33,54 +33,40 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x,
        struct net_device_stats *stats = (struct net_device_stats *)data;
 
        if (unlikely(p->des01.etx.error_summary)) {
-               CHIP_DBG(KERN_ERR "GMAC TX error... 0x%08x\n", p->des01.etx);
-               if (unlikely(p->des01.etx.jabber_timeout)) {
-                       CHIP_DBG(KERN_ERR "\tjabber_timeout error\n");
+               if (unlikely(p->des01.etx.jabber_timeout))
                        x->tx_jabber++;
-               }
 
                if (unlikely(p->des01.etx.frame_flushed)) {
-                       CHIP_DBG(KERN_ERR "\tframe_flushed error\n");
                        x->tx_frame_flushed++;
                        dwmac_dma_flush_tx_fifo(ioaddr);
                }
 
                if (unlikely(p->des01.etx.loss_carrier)) {
-                       CHIP_DBG(KERN_ERR "\tloss_carrier error\n");
                        x->tx_losscarrier++;
                        stats->tx_carrier_errors++;
                }
                if (unlikely(p->des01.etx.no_carrier)) {
-                       CHIP_DBG(KERN_ERR "\tno_carrier error\n");
                        x->tx_carrier++;
                        stats->tx_carrier_errors++;
                }
-               if (unlikely(p->des01.etx.late_collision)) {
-                       CHIP_DBG(KERN_ERR "\tlate_collision error\n");
+               if (unlikely(p->des01.etx.late_collision))
                        stats->collisions += p->des01.etx.collision_count;
-               }
-               if (unlikely(p->des01.etx.excessive_collisions)) {
-                       CHIP_DBG(KERN_ERR "\texcessive_collisions\n");
+
+               if (unlikely(p->des01.etx.excessive_collisions))
                        stats->collisions += p->des01.etx.collision_count;
-               }
-               if (unlikely(p->des01.etx.excessive_deferral)) {
-                       CHIP_DBG(KERN_INFO "\texcessive tx_deferral\n");
+
+               if (unlikely(p->des01.etx.excessive_deferral))
                        x->tx_deferred++;
-               }
 
                if (unlikely(p->des01.etx.underflow_error)) {
-                       CHIP_DBG(KERN_ERR "\tunderflow error\n");
                        dwmac_dma_flush_tx_fifo(ioaddr);
                        x->tx_underflow++;
                }
 
-               if (unlikely(p->des01.etx.ip_header_error)) {
-                       CHIP_DBG(KERN_ERR "\tTX IP header csum error\n");
+               if (unlikely(p->des01.etx.ip_header_error))
                        x->tx_ip_header_error++;
-               }
 
                if (unlikely(p->des01.etx.payload_error)) {
-                       CHIP_DBG(KERN_ERR "\tAddr/Payload csum error\n");
                        x->tx_payload_error++;
                        dwmac_dma_flush_tx_fifo(ioaddr);
                }
@@ -88,15 +74,12 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x,
                ret = -1;
        }
 
-       if (unlikely(p->des01.etx.deferred)) {
-               CHIP_DBG(KERN_INFO "GMAC TX status: tx deferred\n");
+       if (unlikely(p->des01.etx.deferred))
                x->tx_deferred++;
-       }
+
 #ifdef STMMAC_VLAN_TAG_USED
-       if (p->des01.etx.vlan_frame) {
-               CHIP_DBG(KERN_INFO "GMAC TX status: VLAN frame\n");
+       if (p->des01.etx.vlan_frame)
                x->tx_vlan++;
-       }
 #endif
 
        return ret;
@@ -123,30 +106,20 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err)
         *      0 1 1 | COE bypassed.. no IPv4/6 frame
         *      0 1 0 | Reserved.
         */
-       if (status == 0x0) {
-               CHIP_DBG(KERN_INFO "RX Des0 status: IEEE 802.3 Type frame.\n");
+       if (status == 0x0)
                ret = llc_snap;
-       } else if (status == 0x4) {
-               CHIP_DBG(KERN_INFO "RX Des0 status: IPv4/6 No CSUM errorS.\n");
+       else if (status == 0x4)
                ret = good_frame;
-       } else if (status == 0x5) {
-               CHIP_DBG(KERN_ERR "RX Des0 status: IPv4/6 Payload Error.\n");
+       else if (status == 0x5)
                ret = csum_none;
-       } else if (status == 0x6) {
-               CHIP_DBG(KERN_ERR "RX Des0 status: IPv4/6 Header Error.\n");
+       else if (status == 0x6)
                ret = csum_none;
-       } else if (status == 0x7) {
-               CHIP_DBG(KERN_ERR
-                   "RX Des0 status: IPv4/6 Header and Payload Error.\n");
+       else if (status == 0x7)
                ret = csum_none;
-       } else if (status == 0x1) {
-               CHIP_DBG(KERN_ERR
-                   "RX Des0 status: IPv4/6 unsupported IP PAYLOAD.\n");
+       else if (status == 0x1)
                ret = discard_frame;
-       } else if (status == 0x3) {
-               CHIP_DBG(KERN_ERR "RX Des0 status: No IPv4, IPv6 frame.\n");
+       else if (status == 0x3)
                ret = discard_frame;
-       }
        return ret;
 }
 
@@ -208,36 +181,26 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
        struct net_device_stats *stats = (struct net_device_stats *)data;
 
        if (unlikely(p->des01.erx.error_summary)) {
-               CHIP_DBG(KERN_ERR "GMAC RX Error Summary 0x%08x\n",
-                                 p->des01.erx);
                if (unlikely(p->des01.erx.descriptor_error)) {
-                       CHIP_DBG(KERN_ERR "\tdescriptor error\n");
                        x->rx_desc++;
                        stats->rx_length_errors++;
                }
-               if (unlikely(p->des01.erx.overflow_error)) {
-                       CHIP_DBG(KERN_ERR "\toverflow error\n");
+               if (unlikely(p->des01.erx.overflow_error))
                        x->rx_gmac_overflow++;
-               }
 
                if (unlikely(p->des01.erx.ipc_csum_error))
-                       CHIP_DBG(KERN_ERR "\tIPC Csum Error/Giant frame\n");
+                       pr_err("\tIPC Csum Error/Giant frame\n");
 
                if (unlikely(p->des01.erx.late_collision)) {
-                       CHIP_DBG(KERN_ERR "\tlate_collision error\n");
-                       stats->collisions++;
                        stats->collisions++;
                }
-               if (unlikely(p->des01.erx.receive_watchdog)) {
-                       CHIP_DBG(KERN_ERR "\treceive_watchdog error\n");
+               if (unlikely(p->des01.erx.receive_watchdog))
                        x->rx_watchdog++;
-               }
-               if (unlikely(p->des01.erx.error_gmii)) {
-                       CHIP_DBG(KERN_ERR "\tReceive Error\n");
+
+               if (unlikely(p->des01.erx.error_gmii))
                        x->rx_mii++;
-               }
+
                if (unlikely(p->des01.erx.crc_error)) {
-                       CHIP_DBG(KERN_ERR "\tCRC error\n");
                        x->rx_crc++;
                        stats->rx_crc_errors++;
                }
@@ -251,30 +214,24 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
        ret = enh_desc_coe_rdes0(p->des01.erx.ipc_csum_error,
                p->des01.erx.frame_type, p->des01.erx.rx_mac_addr);
 
-       if (unlikely(p->des01.erx.dribbling)) {
-               CHIP_DBG(KERN_ERR "GMAC RX: dribbling error\n");
+       if (unlikely(p->des01.erx.dribbling))
                x->dribbling_bit++;
-       }
+
        if (unlikely(p->des01.erx.sa_filter_fail)) {
-               CHIP_DBG(KERN_ERR "GMAC RX : Source Address filter fail\n");
                x->sa_rx_filter_fail++;
                ret = discard_frame;
        }
        if (unlikely(p->des01.erx.da_filter_fail)) {
-               CHIP_DBG(KERN_ERR "GMAC RX : Dest Address filter fail\n");
                x->da_rx_filter_fail++;
                ret = discard_frame;
        }
        if (unlikely(p->des01.erx.length_error)) {
-               CHIP_DBG(KERN_ERR "GMAC RX: length_error error\n");
                x->rx_length++;
                ret = discard_frame;
        }
 #ifdef STMMAC_VLAN_TAG_USED
-       if (p->des01.erx.vlan_tag) {
-               CHIP_DBG(KERN_INFO "GMAC RX: VLAN frame tagged\n");
+       if (p->des01.erx.vlan_tag)
                x->rx_vlan++;
-       }
 #endif
 
        return ret;
index 11775b9..35ad4f4 100644 (file)
@@ -52,10 +52,8 @@ static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x,
                ret = -1;
        }
 
-       if (p->des01.etx.vlan_frame) {
-               CHIP_DBG(KERN_INFO "GMAC TX status: VLAN frame\n");
+       if (p->des01.etx.vlan_frame)
                x->tx_vlan++;
-       }
 
        if (unlikely(p->des01.tx.deferred))
                x->tx_deferred++;
index e9eab29..f2ccb36 100644 (file)
 #include "stmmac_ptp.h"
 #include "stmmac.h"
 
-#undef STMMAC_DEBUG
-/*#define STMMAC_DEBUG*/
-#ifdef STMMAC_DEBUG
-#define DBG(nlevel, klevel, fmt, args...) \
-               ((void)(netif_msg_##nlevel(priv) && \
-               printk(KERN_##klevel fmt, ## args)))
-#else
-#define DBG(nlevel, klevel, fmt, args...) do { } while (0)
-#endif
-
-#undef STMMAC_RX_DEBUG
-/*#define STMMAC_RX_DEBUG*/
-#ifdef STMMAC_RX_DEBUG
-#define RX_DBG(fmt, args...)  printk(fmt, ## args)
-#else
-#define RX_DBG(fmt, args...)  do { } while (0)
-#endif
-
-#undef STMMAC_XMIT_DEBUG
-/*#define STMMAC_XMIT_DEBUG*/
-#ifdef STMMAC_XMIT_DEBUG
-#define TX_DBG(fmt, args...)  printk(fmt, ## args)
-#else
-#define TX_DBG(fmt, args...)  do { } while (0)
-#endif
-
 #define STMMAC_ALIGN(x)        L1_CACHE_ALIGN(x)
 #define JUMBO_LEN      9000
 
@@ -214,19 +188,17 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv)
        }
 }
 
-#if defined(STMMAC_XMIT_DEBUG) || defined(STMMAC_RX_DEBUG)
 static void print_pkt(unsigned char *buf, int len)
 {
        int j;
-       pr_info("len = %d byte, buf addr: 0x%p", len, buf);
+       pr_debug("len = %d byte, buf addr: 0x%p", len, buf);
        for (j = 0; j < len; j++) {
                if ((j % 16) == 0)
-                       pr_info("\n %03x:", j);
-               pr_info(" %02x", buf[j]);
+                       pr_debug("\n %03x:", j);
+               pr_debug(" %02x", buf[j]);
        }
-       pr_info("\n");
+       pr_debug("\n");
 }
-#endif
 
 /* minimum number of free TX descriptors required to wake up TX process */
 #define STMMAC_TX_THRESH(x)    (x->dma_tx_size/4)
@@ -696,9 +668,6 @@ static void stmmac_adjust_link(struct net_device *dev)
        if (phydev == NULL)
                return;
 
-       DBG(probe, DEBUG, "stmmac_adjust_link: called.  address %d link %d\n",
-           phydev->addr, phydev->link);
-
        spin_lock_irqsave(&priv->lock, flags);
 
        if (phydev->link) {
@@ -773,8 +742,6 @@ static void stmmac_adjust_link(struct net_device *dev)
        priv->eee_enabled = stmmac_eee_init(priv);
 
        spin_unlock_irqrestore(&priv->lock, flags);
-
-       DBG(probe, DEBUG, "stmmac_adjust_link: exiting\n");
 }
 
 /**
@@ -789,13 +756,13 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv)
        int interface = priv->plat->interface;
 
        if (priv->dma_cap.pcs) {
-               if ((interface & PHY_INTERFACE_MODE_RGMII) ||
-                   (interface & PHY_INTERFACE_MODE_RGMII_ID) ||
-                   (interface & PHY_INTERFACE_MODE_RGMII_RXID) ||
-                   (interface & PHY_INTERFACE_MODE_RGMII_TXID)) {
+               if ((interface == PHY_INTERFACE_MODE_RGMII) ||
+                   (interface == PHY_INTERFACE_MODE_RGMII_ID) ||
+                   (interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
+                   (interface == PHY_INTERFACE_MODE_RGMII_TXID)) {
                        pr_debug("STMMAC: PCS RGMII support enable\n");
                        priv->pcs = STMMAC_PCS_RGMII;
-               } else if (interface & PHY_INTERFACE_MODE_SGMII) {
+               } else if (interface == PHY_INTERFACE_MODE_SGMII) {
                        pr_debug("STMMAC: PCS SGMII support enable\n");
                        priv->pcs = STMMAC_PCS_SGMII;
                }
@@ -1015,8 +982,9 @@ static void init_dma_desc_rings(struct net_device *dev)
        if (bfsize < BUF_SIZE_16KiB)
                bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz);
 
-       DBG(probe, INFO, "stmmac: txsize %d, rxsize %d, bfsize %d\n",
-           txsize, rxsize, bfsize);
+       if (netif_msg_probe(priv))
+               pr_debug("%s: txsize %d, rxsize %d, bfsize %d\n", __func__,
+                        txsize, rxsize, bfsize);
 
        if (priv->extend_desc) {
                priv->dma_erx = dma_alloc_coherent(priv->device, rxsize *
@@ -1052,12 +1020,13 @@ static void init_dma_desc_rings(struct net_device *dev)
                                            GFP_KERNEL);
        priv->tx_skbuff = kmalloc_array(txsize, sizeof(struct sk_buff *),
                                        GFP_KERNEL);
-       if (netif_msg_drv(priv))
+       if (netif_msg_probe(priv)) {
                pr_debug("(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", __func__,
                         (u32) priv->dma_rx_phy, (u32) priv->dma_tx_phy);
 
-       /* RX INITIALIZATION */
-       DBG(probe, INFO, "stmmac: SKB addresses:\nskb\t\tskb data\tdma data\n");
+               /* RX INITIALIZATION */
+               pr_debug("\tSKB addresses:\nskb\t\tskb data\tdma data\n");
+       }
        for (i = 0; i < rxsize; i++) {
                struct dma_desc *p;
                if (priv->extend_desc)
@@ -1068,8 +1037,10 @@ static void init_dma_desc_rings(struct net_device *dev)
                if (stmmac_init_rx_buffers(priv, p, i))
                        break;
 
-               DBG(probe, INFO, "[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i],
-                   priv->rx_skbuff[i]->data, priv->rx_skbuff_dma[i]);
+               if (netif_msg_probe(priv))
+                       pr_debug("[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i],
+                                priv->rx_skbuff[i]->data,
+                                (unsigned int)priv->rx_skbuff_dma[i]);
        }
        priv->cur_rx = 0;
        priv->dirty_rx = (unsigned int)(i - rxsize);
@@ -1244,8 +1215,9 @@ static void stmmac_tx_clean(struct stmmac_priv *priv)
 
                        stmmac_get_tx_hwtstamp(priv, entry, skb);
                }
-               TX_DBG("%s: curr %d, dirty %d\n", __func__,
-                      priv->cur_tx, priv->dirty_tx);
+               if (netif_msg_tx_done(priv))
+                       pr_debug("%s: curr %d, dirty %d\n", __func__,
+                                priv->cur_tx, priv->dirty_tx);
 
                if (likely(priv->tx_skbuff_dma[entry])) {
                        dma_unmap_single(priv->device,
@@ -1270,7 +1242,8 @@ static void stmmac_tx_clean(struct stmmac_priv *priv)
                netif_tx_lock(priv->dev);
                if (netif_queue_stopped(priv->dev) &&
                    stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv)) {
-                       TX_DBG("%s: restart transmit\n", __func__);
+                       if (netif_msg_tx_done(priv))
+                               pr_debug("%s: restart transmit\n", __func__);
                        netif_wake_queue(priv->dev);
                }
                netif_tx_unlock(priv->dev);
@@ -1579,7 +1552,7 @@ static int stmmac_open(struct net_device *dev)
                if (ret) {
                        pr_err("%s: Cannot attach to PHY (error: %d)\n",
                               __func__, ret);
-                       goto open_error;
+                       goto phy_error;
                }
        }
 
@@ -1593,7 +1566,7 @@ static int stmmac_open(struct net_device *dev)
        ret = stmmac_init_dma_engine(priv);
        if (ret < 0) {
                pr_err("%s: DMA initialization failed\n", __func__);
-               goto open_error;
+               goto init_error;
        }
 
        /* Copy the MAC addr into the HW  */
@@ -1612,7 +1585,7 @@ static int stmmac_open(struct net_device *dev)
        if (unlikely(ret < 0)) {
                pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n",
                       __func__, dev->irq, ret);
-               goto open_error;
+               goto init_error;
        }
 
        /* Request the Wake IRQ in case of another line is used for WoL */
@@ -1622,7 +1595,7 @@ static int stmmac_open(struct net_device *dev)
                if (unlikely(ret < 0)) {
                        pr_err("%s: ERROR: allocating the WoL IRQ %d (%d)\n",
                               __func__, priv->wol_irq, ret);
-                       goto open_error_wolirq;
+                       goto wolirq_error;
                }
        }
 
@@ -1633,7 +1606,7 @@ static int stmmac_open(struct net_device *dev)
                if (unlikely(ret < 0)) {
                        pr_err("%s: ERROR: allocating the LPI IRQ %d (%d)\n",
                               __func__, priv->lpi_irq, ret);
-                       goto open_error_lpiirq;
+                       goto lpiirq_error;
                }
        }
 
@@ -1659,7 +1632,7 @@ static int stmmac_open(struct net_device *dev)
                pr_warn("%s: failed debugFS registration\n", __func__);
 #endif
        /* Start the ball rolling... */
-       DBG(probe, DEBUG, "%s: DMA RX/TX processes started...\n", dev->name);
+       pr_debug("%s: DMA RX/TX processes started...\n", dev->name);
        priv->hw->dma->start_tx(priv->ioaddr);
        priv->hw->dma->start_rx(priv->ioaddr);
 
@@ -1691,17 +1664,17 @@ static int stmmac_open(struct net_device *dev)
 
        return 0;
 
-open_error_lpiirq:
+lpiirq_error:
        if (priv->wol_irq != dev->irq)
                free_irq(priv->wol_irq, dev);
-
-open_error_wolirq:
+wolirq_error:
        free_irq(dev->irq, dev);
 
-open_error:
+init_error:
+       free_dma_desc_resources(priv);
        if (priv->phydev)
                phy_disconnect(priv->phydev);
-
+phy_error:
        clk_disable_unprepare(priv->stmmac_clk);
 
        return ret;
@@ -1796,16 +1769,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 
        entry = priv->cur_tx % txsize;
 
-#ifdef STMMAC_XMIT_DEBUG
-       if ((skb->len > ETH_FRAME_LEN) || nfrags)
-               pr_debug("%s: [entry %d]: skb addr %p len: %d nopagedlen: %d\n"
-                        "\tn_frags: %d - ip_summed: %d - %s gso\n"
-                        "\ttx_count_frames %d\n", __func__, entry,
-                        skb, skb->len, nopaged_len, nfrags, skb->ip_summed,
-                        !skb_is_gso(skb) ? "isn't" : "is",
-                        priv->tx_count_frames);
-#endif
-
        csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL);
 
        if (priv->extend_desc)
@@ -1815,12 +1778,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 
        first = desc;
 
-#ifdef STMMAC_XMIT_DEBUG
-       if ((nfrags > 0) || (skb->len > ETH_FRAME_LEN))
-               pr_debug("\tskb len: %d, nopaged_len: %d,\n"
-                        "\t\tn_frags: %d, ip_summed: %d\n",
-                        skb->len, nopaged_len, nfrags, skb->ip_summed);
-#endif
        priv->tx_skbuff[entry] = skb;
 
        /* To program the descriptors according to the size of the frame */
@@ -1856,7 +1813,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
                else
                        desc = priv->dma_tx + entry;
 
-               TX_DBG("\t[entry %d] segment len: %d\n", entry, len);
                desc->des2 = skb_frag_dma_map(priv->device, frag, 0, len,
                                              DMA_TO_DEVICE);
                priv->tx_skbuff_dma[entry] = desc->des2;
@@ -1880,8 +1836,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
        if (priv->tx_coal_frames > priv->tx_count_frames) {
                priv->hw->desc->clear_tx_ic(desc);
                priv->xstats.tx_reset_ic_bit++;
-               TX_DBG("\t[entry %d]: tx_count_frames %d\n", entry,
-                      priv->tx_count_frames);
                mod_timer(&priv->txtimer,
                          STMMAC_COAL_TIMER(priv->tx_coal_timer));
        } else
@@ -1893,22 +1847,22 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 
        priv->cur_tx++;
 
-#ifdef STMMAC_XMIT_DEBUG
        if (netif_msg_pktdata(priv)) {
-               pr_info("%s: curr %d dirty=%d entry=%d, first=%p, nfrags=%d",
+               pr_debug("%s: curr %d dirty=%d entry=%d, first=%p, nfrags=%d",
                        __func__, (priv->cur_tx % txsize),
                        (priv->dirty_tx % txsize), entry, first, nfrags);
+
                if (priv->extend_desc)
                        stmmac_display_ring((void *)priv->dma_etx, txsize, 1);
                else
                        stmmac_display_ring((void *)priv->dma_tx, txsize, 0);
 
-               pr_info(">>> frame to be transmitted: ");
+               pr_debug(">>> frame to be transmitted: ");
                print_pkt(skb->data, skb->len);
        }
-#endif
        if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) {
-               TX_DBG("%s: stop transmitted packets\n", __func__);
+               if (netif_msg_hw(priv))
+                       pr_debug("%s: stop transmitted packets\n", __func__);
                netif_stop_queue(dev);
        }
 
@@ -1968,7 +1922,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv)
 
                        priv->hw->ring->refill_desc3(priv, p);
 
-                       RX_DBG(KERN_INFO "\trefill entry #%d\n", entry);
+                       if (netif_msg_rx_status(priv))
+                               pr_debug("\trefill entry #%d\n", entry);
                }
                wmb();
                priv->hw->desc->set_rx_owner(p);
@@ -1991,15 +1946,13 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
        unsigned int count = 0;
        int coe = priv->plat->rx_coe;
 
-#ifdef STMMAC_RX_DEBUG
-       if (netif_msg_hw(priv)) {
-               pr_debug(">>> stmmac_rx: descriptor ring:\n");
+       if (netif_msg_rx_status(priv)) {
+               pr_debug("%s: descriptor ring:\n", __func__);
                if (priv->extend_desc)
                        stmmac_display_ring((void *)priv->dma_erx, rxsize, 1);
                else
                        stmmac_display_ring((void *)priv->dma_rx, rxsize, 0);
        }
-#endif
        while (count < limit) {
                int status;
                struct dma_desc *p;
@@ -2053,15 +2006,14 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
                         */
                        if (unlikely(status != llc_snap))
                                frame_len -= ETH_FCS_LEN;
-#ifdef STMMAC_RX_DEBUG
-                       if (frame_len > ETH_FRAME_LEN)
-                               pr_debug("\tRX frame size %d, COE status: %d\n",
-                                        frame_len, status);
 
-                       if (netif_msg_hw(priv))
+                       if (netif_msg_rx_status(priv)) {
                                pr_debug("\tdesc: %p [entry %d] buff=0x%x\n",
                                         p, entry, p->des2);
-#endif
+                               if (frame_len > ETH_FRAME_LEN)
+                                       pr_debug("\tframe size %d, COE: %d\n",
+                                                frame_len, status);
+                       }
                        skb = priv->rx_skbuff[entry];
                        if (unlikely(!skb)) {
                                pr_err("%s: Inconsistent Rx descriptor chain\n",
@@ -2078,12 +2030,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
                        dma_unmap_single(priv->device,
                                         priv->rx_skbuff_dma[entry],
                                         priv->dma_buf_sz, DMA_FROM_DEVICE);
-#ifdef STMMAC_RX_DEBUG
+
                        if (netif_msg_pktdata(priv)) {
-                               pr_info(" frame received (%dbytes)", frame_len);
+                               pr_debug("frame received (%dbytes)", frame_len);
                                print_pkt(skb->data, frame_len);
                        }
-#endif
+
                        skb->protocol = eth_type_trans(skb, priv->dev);
 
                        if (unlikely(!coe))
@@ -2562,9 +2514,6 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
        /* Get and dump the chip ID */
        priv->synopsys_id = stmmac_get_synopsys_id(priv);
 
-       /* To use alternate (extended) or normal descriptor structures */
-       stmmac_selec_desc_mode(priv);
-
        /* To use the chained or ring mode */
        if (chain_mode) {
                priv->hw->chain = &chain_mode_ops;
@@ -2599,6 +2548,9 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
        } else
                pr_info(" No HW DMA feature register supported");
 
+       /* To use alternate (extended) or normal descriptor structures */
+       stmmac_selec_desc_mode(priv);
+
        ret = priv->hw->mac->rx_ipc(priv->ioaddr);
        if (!ret) {
                pr_warn(" RX IPC Checksum Offload not configured.\n");
index cc15039..fe7bc99 100644 (file)
@@ -27,6 +27,9 @@
 #include <linux/mii.h>
 #include <linux/phy.h>
 #include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
 #include <asm/io.h>
 
 #include "stmmac.h"
@@ -131,10 +134,46 @@ static int stmmac_mdio_reset(struct mii_bus *bus)
        struct net_device *ndev = bus->priv;
        struct stmmac_priv *priv = netdev_priv(ndev);
        unsigned int mii_address = priv->hw->mii.addr;
+       struct stmmac_mdio_bus_data *data = priv->plat->mdio_bus_data;
+
+#ifdef CONFIG_OF
+       if (priv->device->of_node) {
+               int reset_gpio, active_low;
+
+               if (data->reset_gpio < 0) {
+                       struct device_node *np = priv->device->of_node;
+                       if (!np)
+                               return 0;
+
+                       data->reset_gpio = of_get_named_gpio(np,
+                                               "snps,reset-gpio", 0);
+                       if (data->reset_gpio < 0)
+                               return 0;
+
+                       data->active_low = of_property_read_bool(np,
+                                               "snps,reset-active-low");
+                       of_property_read_u32_array(np,
+                               "snps,reset-delays-us", data->delays, 3);
+               }
+
+               reset_gpio = data->reset_gpio;
+               active_low = data->active_low;
+
+               if (!gpio_request(reset_gpio, "mdio-reset")) {
+                       gpio_direction_output(reset_gpio, active_low ? 1 : 0);
+                       udelay(data->delays[0]);
+                       gpio_set_value(reset_gpio, active_low ? 0 : 1);
+                       udelay(data->delays[1]);
+                       gpio_set_value(reset_gpio, active_low ? 1 : 0);
+                       udelay(data->delays[2]);
+                       gpio_free(reset_gpio);
+               }
+       }
+#endif
 
-       if (priv->plat->mdio_bus_data->phy_reset) {
+       if (data->phy_reset) {
                pr_debug("stmmac_mdio_reset: calling phy_reset\n");
-               priv->plat->mdio_bus_data->phy_reset(priv->plat->bsp_priv);
+               data->phy_reset(priv->plat->bsp_priv);
        }
 
        /* This is a workaround for problems with the STE101P PHY.
@@ -172,6 +211,11 @@ int stmmac_mdio_register(struct net_device *ndev)
        else
                irqlist = priv->mii_irq;
 
+#ifdef CONFIG_OF
+       if (priv->device->of_node)
+               mdio_bus_data->reset_gpio = -1;
+#endif
+
        new_bus->name = "stmmac";
        new_bus->read = &stmmac_mdio_read;
        new_bus->write = &stmmac_mdio_write;
index 1d3780f..03de76c 100644 (file)
@@ -34,12 +34,20 @@ static int stmmac_probe_config_dt(struct platform_device *pdev,
                                  const char **mac)
 {
        struct device_node *np = pdev->dev.of_node;
+       struct stmmac_dma_cfg *dma_cfg;
 
        if (!np)
                return -ENODEV;
 
        *mac = of_get_mac_address(np);
        plat->interface = of_get_phy_mode(np);
+
+       plat->bus_id = of_alias_get_id(np, "ethernet");
+       if (plat->bus_id < 0)
+               plat->bus_id = 0;
+
+       of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr);
+
        plat->mdio_bus_data = devm_kzalloc(&pdev->dev,
                                           sizeof(struct stmmac_mdio_bus_data),
                                           GFP_KERNEL);
@@ -56,6 +64,22 @@ static int stmmac_probe_config_dt(struct platform_device *pdev,
                plat->pmt = 1;
        }
 
+       if (of_device_is_compatible(np, "snps,dwmac-3.610") ||
+               of_device_is_compatible(np, "snps,dwmac-3.710")) {
+               plat->enh_desc = 1;
+               plat->bugged_jumbo = 1;
+               plat->force_sf_dma_mode = 1;
+       }
+
+       dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), GFP_KERNEL);
+       if (!dma_cfg)
+               return -ENOMEM;
+
+       plat->dma_cfg = dma_cfg;
+       of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
+       dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst");
+       dma_cfg->mixed_burst = of_property_read_bool(np, "snps,mixed-burst");
+
        return 0;
 }
 #else
@@ -92,8 +116,10 @@ static int stmmac_pltfr_probe(struct platform_device *pdev)
        if (IS_ERR(addr))
                return PTR_ERR(addr);
 
+       plat_dat = pdev->dev.platform_data;
        if (pdev->dev.of_node) {
-               plat_dat = devm_kzalloc(&pdev->dev,
+               if (!plat_dat)
+                       plat_dat = devm_kzalloc(&pdev->dev,
                                        sizeof(struct plat_stmmacenet_data),
                                        GFP_KERNEL);
                if (!plat_dat) {
@@ -106,8 +132,6 @@ static int stmmac_pltfr_probe(struct platform_device *pdev)
                        pr_err("%s: main dt probe failed", __func__);
                        return ret;
                }
-       } else {
-               plat_dat = pdev->dev.platform_data;
        }
 
        /* Custom initialisation (if needed)*/
@@ -171,8 +195,6 @@ static int stmmac_pltfr_remove(struct platform_device *pdev)
        if (priv->plat->exit)
                priv->plat->exit(pdev);
 
-       platform_set_drvdata(pdev, NULL);
-
        return ret;
 }
 
@@ -230,7 +252,9 @@ static const struct dev_pm_ops stmmac_pltfr_pm_ops;
 
 static const struct of_device_id stmmac_dt_ids[] = {
        { .compatible = "st,spear600-gmac"},
+       { .compatible = "snps,dwmac-3.610"},
        { .compatible = "snps,dwmac-3.70a"},
+       { .compatible = "snps,dwmac-3.710"},
        { .compatible = "snps,dwmac"},
        { /* sentinel */ }
 };
index 4c682a3..759441b 100644 (file)
@@ -808,44 +808,43 @@ static int cas_reset_mii_phy(struct cas *cp)
        return limit <= 0;
 }
 
-static int cas_saturn_firmware_init(struct cas *cp)
+static void cas_saturn_firmware_init(struct cas *cp)
 {
        const struct firmware *fw;
        const char fw_name[] = "sun/cassini.bin";
        int err;
 
        if (PHY_NS_DP83065 != cp->phy_id)
-               return 0;
+               return;
 
        err = request_firmware(&fw, fw_name, &cp->pdev->dev);
        if (err) {
                pr_err("Failed to load firmware \"%s\"\n",
                       fw_name);
-               return err;
+               return;
        }
        if (fw->size < 2) {
                pr_err("bogus length %zu in \"%s\"\n",
                       fw->size, fw_name);
-               err = -EINVAL;
                goto out;
        }
        cp->fw_load_addr= fw->data[1] << 8 | fw->data[0];
        cp->fw_size = fw->size - 2;
        cp->fw_data = vmalloc(cp->fw_size);
-       if (!cp->fw_data) {
-               err = -ENOMEM;
+       if (!cp->fw_data)
                goto out;
-       }
        memcpy(cp->fw_data, &fw->data[2], cp->fw_size);
 out:
        release_firmware(fw);
-       return err;
 }
 
 static void cas_saturn_firmware_load(struct cas *cp)
 {
        int i;
 
+       if (!cp->fw_data)
+               return;
+
        cas_phy_powerdown(cp);
 
        /* expanded memory access mode */
@@ -5083,8 +5082,7 @@ static int cas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (cas_check_invariants(cp))
                goto err_out_iounmap;
        if (cp->cas_flags & CAS_FLAG_SATURN)
-               if (cas_saturn_firmware_init(cp))
-                       goto err_out_iounmap;
+               cas_saturn_firmware_init(cp);
 
        cp->init_block = (struct cas_init_block *)
                pci_alloc_consistent(pdev, sizeof(struct cas_init_block),
index 95cff98..fa32240 100644 (file)
@@ -10108,7 +10108,7 @@ static int niu_of_probe(struct platform_device *op)
                goto err_out_iounmap;
        }
 
-       dev_set_drvdata(&op->dev, dev);
+       platform_set_drvdata(op, dev);
 
        niu_device_announce(np);
 
@@ -10145,7 +10145,7 @@ err_out:
 
 static int niu_of_remove(struct platform_device *op)
 {
-       struct net_device *dev = dev_get_drvdata(&op->dev);
+       struct net_device *dev = platform_get_drvdata(op);
 
        if (dev) {
                struct niu *np = netdev_priv(dev);
@@ -10175,7 +10175,6 @@ static int niu_of_remove(struct platform_device *op)
                niu_put_parent(np);
 
                free_netdev(dev);
-               dev_set_drvdata(&op->dev, NULL);
        }
        return 0;
 }
index 0549759..0d43fa9 100644 (file)
@@ -995,7 +995,6 @@ static void bigmac_set_multicast(struct net_device *dev)
        struct bigmac *bp = netdev_priv(dev);
        void __iomem *bregs = bp->bregs;
        struct netdev_hw_addr *ha;
-       int i;
        u32 tmp, crc;
 
        /* Disable the receiver.  The bit self-clears when
@@ -1017,10 +1016,7 @@ static void bigmac_set_multicast(struct net_device *dev)
                tmp |= BIGMAC_RXCFG_PMISC;
                sbus_writel(tmp, bregs + BMAC_RXCFG);
        } else {
-               u16 hash_table[4];
-
-               for (i = 0; i < 4; i++)
-                       hash_table[i] = 0;
+               u16 hash_table[4] = { 0 };
 
                netdev_for_each_mc_addr(ha, dev) {
                        crc = ether_crc_le(6, ha->addr);
index 5f3f9d5..e62df2b 100644 (file)
@@ -3028,15 +3028,4 @@ static struct pci_driver gem_driver = {
 #endif /* CONFIG_PM */
 };
 
-static int __init gem_init(void)
-{
-       return pci_register_driver(&gem_driver);
-}
-
-static void __exit gem_cleanup(void)
-{
-       pci_unregister_driver(&gem_driver);
-}
-
-module_init(gem_init);
-module_exit(gem_cleanup);
+module_pci_driver(gem_driver);
index 436fa9d..171f5b0 100644 (file)
@@ -2506,7 +2506,7 @@ static struct quattro *quattro_sbus_find(struct platform_device *child)
        struct quattro *qp;
 
        op = to_platform_device(parent);
-       qp = dev_get_drvdata(&op->dev);
+       qp = platform_get_drvdata(op);
        if (qp)
                return qp;
 
@@ -2521,7 +2521,7 @@ static struct quattro *quattro_sbus_find(struct platform_device *child)
                qp->next = qfe_sbus_list;
                qfe_sbus_list = qp;
 
-               dev_set_drvdata(&op->dev, qp);
+               platform_set_drvdata(op, qp);
        }
        return qp;
 }
index 8182591..b072f4d 100644 (file)
@@ -767,7 +767,7 @@ static struct sunqec *get_qec(struct platform_device *child)
        struct platform_device *op = to_platform_device(child->dev.parent);
        struct sunqec *qecp;
 
-       qecp = dev_get_drvdata(&op->dev);
+       qecp = platform_get_drvdata(op);
        if (!qecp) {
                qecp = kzalloc(sizeof(struct sunqec), GFP_KERNEL);
                if (qecp) {
@@ -801,7 +801,7 @@ static struct sunqec *get_qec(struct platform_device *child)
                                goto fail;
                        }
 
-                       dev_set_drvdata(&op->dev, qecp);
+                       platform_set_drvdata(op, qecp);
 
                        qecp->next_module = root_qec_dev;
                        root_qec_dev = qecp;
@@ -902,7 +902,7 @@ static int qec_ether_init(struct platform_device *op)
        if (res)
                goto fail;
 
-       dev_set_drvdata(&op->dev, qe);
+       platform_set_drvdata(op, qe);
 
        printk(KERN_INFO "%s: qe channel[%d] %pM\n", dev->name, qe->channel,
               dev->dev_addr);
@@ -934,7 +934,7 @@ static int qec_sbus_probe(struct platform_device *op)
 
 static int qec_sbus_remove(struct platform_device *op)
 {
-       struct sunqe *qp = dev_get_drvdata(&op->dev);
+       struct sunqe *qp = platform_get_drvdata(op);
        struct net_device *net_dev = qp->dev;
 
        unregister_netdev(net_dev);
@@ -948,8 +948,6 @@ static int qec_sbus_remove(struct platform_device *op)
 
        free_netdev(net_dev);
 
-       dev_set_drvdata(&op->dev, NULL);
-
        return 0;
 }
 
index da4415d..05a1674 100644 (file)
@@ -1555,6 +1555,8 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                if (mac_addr)
                        memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN);
 
+               slave_data->phy_if = of_get_phy_mode(slave_node);
+
                if (data->dual_emac) {
                        if (of_property_read_u32(slave_node, "dual_emac_res_vlan",
                                                 &prop)) {
@@ -1702,10 +1704,10 @@ static int cpsw_probe(struct platform_device *pdev)
 
        if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
                memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN);
-               pr_info("Detected MACID = %pM", priv->mac_addr);
+               pr_info("Detected MACID = %pM\n", priv->mac_addr);
        } else {
                eth_random_addr(priv->mac_addr);
-               pr_info("Random MACID = %pM", priv->mac_addr);
+               pr_info("Random MACID = %pM\n", priv->mac_addr);
        }
 
        memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
@@ -1944,7 +1946,6 @@ static int cpsw_remove(struct platform_device *pdev)
        struct cpsw_priv *priv = netdev_priv(ndev);
        int i;
 
-       platform_set_drvdata(pdev, NULL);
        if (priv->data.dual_emac)
                unregister_netdev(cpsw_get_slave_ndev(priv, 1));
        unregister_netdev(ndev);
index 053c84f..031ebc8 100644 (file)
@@ -64,6 +64,7 @@
 #define CPDMA_DESC_TO_PORT_EN  BIT(20)
 #define CPDMA_TO_PORT_SHIFT    16
 #define CPDMA_DESC_PORT_MASK   (BIT(18) | BIT(17) | BIT(16))
+#define CPDMA_DESC_CRC_LEN     4
 
 #define CPDMA_TEARDOWN_VALUE   0xfffffffc
 
@@ -805,6 +806,10 @@ static int __cpdma_chan_process(struct cpdma_chan *chan)
                status = -EBUSY;
                goto unlock_ret;
        }
+
+       if (status & CPDMA_DESC_PASS_CRC)
+               outlen -= CPDMA_DESC_CRC_LEN;
+
        status  = status & (CPDMA_DESC_EOQ | CPDMA_DESC_TD_COMPLETE |
                            CPDMA_DESC_PORT_MASK);
 
index 860e15d..07b176b 100644 (file)
@@ -1532,7 +1532,7 @@ static int emac_dev_open(struct net_device *ndev)
        struct device *emac_dev = &ndev->dev;
        u32 cnt;
        struct resource *res;
-       int q, m, ret;
+       int ret;
        int i = 0;
        int k = 0;
        struct emac_priv *priv = netdev_priv(ndev);
@@ -1567,8 +1567,9 @@ static int emac_dev_open(struct net_device *ndev)
 
        while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) {
                for (i = res->start; i <= res->end; i++) {
-                       if (request_irq(i, emac_irq, IRQF_DISABLED,
-                                       ndev->name, ndev))
+                       if (devm_request_irq(&priv->pdev->dev, i, emac_irq,
+                                            IRQF_DISABLED,
+                                            ndev->name, ndev))
                                goto rollback;
                }
                k++;
@@ -1641,15 +1642,7 @@ static int emac_dev_open(struct net_device *ndev)
 
 rollback:
 
-       dev_err(emac_dev, "DaVinci EMAC: request_irq() failed");
-
-       for (q = k; k >= 0; k--) {
-               for (m = i; m >= res->start; m--)
-                       free_irq(m, ndev);
-               res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k-1);
-               m = res->end;
-       }
-
+       dev_err(emac_dev, "DaVinci EMAC: devm_request_irq() failed");
        ret = -EBUSY;
 err:
        pm_runtime_put(&priv->pdev->dev);
@@ -1667,9 +1660,6 @@ err:
  */
 static int emac_dev_stop(struct net_device *ndev)
 {
-       struct resource *res;
-       int i = 0;
-       int irq_num;
        struct emac_priv *priv = netdev_priv(ndev);
        struct device *emac_dev = &ndev->dev;
 
@@ -1685,13 +1675,6 @@ static int emac_dev_stop(struct net_device *ndev)
        if (priv->phydev)
                phy_disconnect(priv->phydev);
 
-       /* Free IRQ */
-       while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i))) {
-               for (irq_num = res->start; irq_num <= res->end; irq_num++)
-                       free_irq(irq_num, priv->ndev);
-               i++;
-       }
-
        if (netif_msg_drv(priv))
                dev_notice(emac_dev, "DaVinci EMAC: %s stopped\n", ndev->name);
 
@@ -1771,29 +1754,22 @@ static const struct net_device_ops emac_netdev_ops = {
 #endif
 };
 
-#ifdef CONFIG_OF
-static struct emac_platform_data
-       *davinci_emac_of_get_pdata(struct platform_device *pdev,
-       struct emac_priv *priv)
+static struct emac_platform_data *
+davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv)
 {
        struct device_node *np;
        struct emac_platform_data *pdata = NULL;
        const u8 *mac_addr;
-       u32 data;
-       int ret;
 
-       pdata = pdev->dev.platform_data;
-       if (!pdata) {
-               pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-               if (!pdata)
-                       goto nodata;
-       }
+       if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
+               return pdev->dev.platform_data;
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
 
        np = pdev->dev.of_node;
-       if (!np)
-               goto nodata;
-       else
-               pdata->version = EMAC_VERSION_2;
+       pdata->version = EMAC_VERSION_2;
 
        if (!is_valid_ether_addr(pdata->mac_addr)) {
                mac_addr = of_get_mac_address(np);
@@ -1801,47 +1777,31 @@ static struct emac_platform_data
                        memcpy(pdata->mac_addr, mac_addr, ETH_ALEN);
        }
 
-       ret = of_property_read_u32(np, "ti,davinci-ctrl-reg-offset", &data);
-       if (!ret)
-               pdata->ctrl_reg_offset = data;
+       of_property_read_u32(np, "ti,davinci-ctrl-reg-offset",
+                            &pdata->ctrl_reg_offset);
 
-       ret = of_property_read_u32(np, "ti,davinci-ctrl-mod-reg-offset",
-               &data);
-       if (!ret)
-               pdata->ctrl_mod_reg_offset = data;
+       of_property_read_u32(np, "ti,davinci-ctrl-mod-reg-offset",
+                            &pdata->ctrl_mod_reg_offset);
 
-       ret = of_property_read_u32(np, "ti,davinci-ctrl-ram-offset", &data);
-       if (!ret)
-               pdata->ctrl_ram_offset = data;
+       of_property_read_u32(np, "ti,davinci-ctrl-ram-offset",
+                            &pdata->ctrl_ram_offset);
 
-       ret = of_property_read_u32(np, "ti,davinci-ctrl-ram-size", &data);
-       if (!ret)
-               pdata->ctrl_ram_size = data;
+       of_property_read_u32(np, "ti,davinci-ctrl-ram-size",
+                            &pdata->ctrl_ram_size);
 
-       ret = of_property_read_u32(np, "ti,davinci-rmii-en", &data);
-       if (!ret)
-               pdata->rmii_en = data;
+       of_property_read_u8(np, "ti,davinci-rmii-en", &pdata->rmii_en);
 
-       ret = of_property_read_u32(np, "ti,davinci-no-bd-ram", &data);
-       if (!ret)
-               pdata->no_bd_ram = data;
+       pdata->no_bd_ram = of_property_read_bool(np, "ti,davinci-no-bd-ram");
 
        priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
        if (!priv->phy_node)
                pdata->phy_id = "";
 
        pdev->dev.platform_data = pdata;
-nodata:
+
        return  pdata;
 }
-#else
-static struct emac_platform_data
-       *davinci_emac_of_get_pdata(struct platform_device *pdev,
-       struct emac_priv *priv)
-{
-       return  pdev->dev.platform_data;
-}
-#endif
+
 /**
  * davinci_emac_probe - EMAC device probe
  * @pdev: The DaVinci EMAC device that we are removing
@@ -1856,7 +1816,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
        struct resource *res;
        struct net_device *ndev;
        struct emac_priv *priv;
-       unsigned long size, hw_ram_addr;
+       unsigned long hw_ram_addr;
        struct emac_platform_data *pdata;
        struct device *emac_dev;
        struct cpdma_params dma_params;
@@ -1907,25 +1867,10 @@ static int davinci_emac_probe(struct platform_device *pdev)
        emac_dev = &ndev->dev;
        /* Get EMAC platform data */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(&pdev->dev,"error getting res\n");
-               rc = -ENOENT;
-               goto no_pdata;
-       }
-
        priv->emac_base_phys = res->start + pdata->ctrl_reg_offset;
-       size = resource_size(res);
-       if (!devm_request_mem_region(&pdev->dev, res->start,
-                                    size, ndev->name)) {
-               dev_err(&pdev->dev, "failed request_mem_region() for regs\n");
-               rc = -ENXIO;
-               goto no_pdata;
-       }
-
-       priv->remap_addr = devm_ioremap(&pdev->dev, res->start, size);
-       if (!priv->remap_addr) {
-               dev_err(&pdev->dev, "unable to map IO\n");
-               rc = -ENOMEM;
+       priv->remap_addr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(priv->remap_addr)) {
+               rc = PTR_ERR(priv->remap_addr);
                goto no_pdata;
        }
        priv->emac_base = priv->remap_addr + pdata->ctrl_reg_offset;
@@ -2037,8 +1982,6 @@ static int davinci_emac_remove(struct platform_device *pdev)
 
        dev_notice(&ndev->dev, "DaVinci EMAC: davinci_emac_remove()\n");
 
-       platform_set_drvdata(pdev, NULL);
-
        if (priv->txchan)
                cpdma_chan_destroy(priv->txchan);
        if (priv->rxchan)
@@ -2078,11 +2021,13 @@ static const struct dev_pm_ops davinci_emac_pm_ops = {
        .resume         = davinci_emac_resume,
 };
 
+#if IS_ENABLED(CONFIG_OF)
 static const struct of_device_id davinci_emac_of_match[] = {
        {.compatible = "ti,davinci-dm6467-emac", },
        {},
 };
 MODULE_DEVICE_TABLE(of, davinci_emac_of_match);
+#endif
 
 /* davinci_emac_driver: EMAC platform driver structure */
 static struct platform_driver davinci_emac_driver = {
index ce7c499..16ddfc3 100644 (file)
@@ -292,6 +292,7 @@ static int davinci_mdio_write(struct mii_bus *bus, int phy_id,
        return 0;
 }
 
+#if IS_ENABLED(CONFIG_OF)
 static int davinci_mdio_probe_dt(struct mdio_platform_data *data,
                         struct platform_device *pdev)
 {
@@ -309,7 +310,7 @@ static int davinci_mdio_probe_dt(struct mdio_platform_data *data,
 
        return 0;
 }
-
+#endif
 
 static int davinci_mdio_probe(struct platform_device *pdev)
 {
@@ -487,11 +488,13 @@ static const struct dev_pm_ops davinci_mdio_pm_ops = {
        .resume_early   = davinci_mdio_resume,
 };
 
+#if IS_ENABLED(CONFIG_OF)
 static const struct of_device_id davinci_mdio_of_mtable[] = {
        { .compatible = "ti,davinci_mdio", },
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, davinci_mdio_of_mtable);
+#endif
 
 static struct platform_driver davinci_mdio_driver = {
        .driver = {
index 60c400f..591437e 100644 (file)
@@ -372,7 +372,7 @@ static int tlan_resume(struct pci_dev *pdev)
 
        pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
-       pci_enable_wake(pdev, 0, 0);
+       pci_enable_wake(pdev, PCI_D0, 0);
        netif_device_attach(dev);
 
        if (netif_running(dev))
@@ -533,7 +533,6 @@ static int tlan_probe1(struct pci_dev *pdev, long ioaddr, int irq, int rev,
                /* This is a hack. We need to know which board structure
                 * is suited for this adapter */
                device_id = inw(ioaddr + EISA_ID2);
-               priv->is_eisa = 1;
                if (device_id == 0x20F1) {
                        priv->adapter = &board_info[13]; /* NetFlex-3/E */
                        priv->adapter_rev = 23;         /* TLAN 2.3 */
index 5fc98a8..2eb33a2 100644 (file)
@@ -207,7 +207,6 @@ struct tlan_priv {
        u8                      tlan_full_duplex;
        spinlock_t              lock;
        u8                      link;
-       u8                      is_eisa;
        struct work_struct                      tlan_tqueue;
        u8                      neg_be_verbose;
 };
index fe25609..a971b9c 100644 (file)
@@ -2209,18 +2209,6 @@ MODULE_PARM_DESC(speed, "0:auto, 10:10Mbps, 100:100Mbps");
 module_param_named(duplex, options.duplex, int, 0);
 MODULE_PARM_DESC(duplex, "0:auto, 1:half, 2:full");
 
-static int __init tc35815_init_module(void)
-{
-       return pci_register_driver(&tc35815_pci_driver);
-}
-
-static void __exit tc35815_cleanup_module(void)
-{
-       pci_unregister_driver(&tc35815_pci_driver);
-}
-
-module_init(tc35815_init_module);
-module_exit(tc35815_cleanup_module);
-
+module_pci_driver(tc35815_pci_driver);
 MODULE_DESCRIPTION("TOSHIBA TC35815 PCI 10M/100M Ethernet driver");
 MODULE_LICENSE("GPL");
index 3c69a04..01bdc6c 100644 (file)
@@ -1682,7 +1682,6 @@ static int tsi108_ether_remove(struct platform_device *pdev)
 
        unregister_netdev(dev);
        tsi108_stop_ethernet(dev);
-       platform_set_drvdata(pdev, NULL);
        iounmap(priv->regs);
        iounmap(priv->phyregs);
        free_netdev(dev);
index 68a9ba6..8a049a2 100644 (file)
@@ -5,7 +5,6 @@
 config NET_VENDOR_VIA
        bool "VIA devices"
        default y
-       depends on PCI
        ---help---
          If you have a network (Ethernet) card belonging to this class, say Y
          and read the Ethernet-HOWTO, available from
@@ -22,7 +21,6 @@ config VIA_RHINE
        tristate "VIA Rhine support"
        depends on PCI
        select CRC32
-       select NET_CORE
        select MII
        ---help---
          If you have a VIA "Rhine" based network card (Rhine-I (VT86C100A),
@@ -45,10 +43,9 @@ config VIA_RHINE_MMIO
 
 config VIA_VELOCITY
        tristate "VIA Velocity support"
-       depends on PCI
+       depends on (PCI || USE_OF)
        select CRC32
        select CRC_CCITT
-       select NET_CORE
        select MII
        ---help---
          If you have a VIA "Velocity" based network card say Y here.
index fb62489..1d6dc41 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/types.h>
 #include <linux/bitops.h>
 #include <linux/init.h>
+#include <linux/dma-mapping.h>
 #include <linux/mm.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/if.h>
 #include <linux/uaccess.h>
 #include <linux/proc_fs.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
 #include <linux/inetdevice.h>
+#include <linux/platform_device.h>
 #include <linux/reboot.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
 
 #include "via-velocity.h"
 
+enum velocity_bus_type {
+       BUS_PCI,
+       BUS_PLATFORM,
+};
 
 static int velocity_nics;
 static int msglevel = MSG_LEVEL_INFO;
 
+static void velocity_set_power_state(struct velocity_info *vptr, char state)
+{
+       void *addr = vptr->mac_regs;
+
+       if (vptr->pdev)
+               pci_set_power_state(vptr->pdev, state);
+       else
+               writeb(state, addr + 0x154);
+}
+
 /**
  *     mac_get_cam_mask        -       Read a CAM mask
  *     @regs: register block for this velocity
@@ -361,12 +380,23 @@ static struct velocity_info_tbl chip_info_table[] = {
  *     Describe the PCI device identifiers that we support in this
  *     device driver. Used for hotplug autoloading.
  */
-static DEFINE_PCI_DEVICE_TABLE(velocity_id_table) = {
+
+static DEFINE_PCI_DEVICE_TABLE(velocity_pci_id_table) = {
        { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X) },
        { }
 };
 
-MODULE_DEVICE_TABLE(pci, velocity_id_table);
+MODULE_DEVICE_TABLE(pci, velocity_pci_id_table);
+
+/**
+ *     Describe the OF device identifiers that we support in this
+ *     device driver. Used for devicetree nodes.
+ */
+static struct of_device_id velocity_of_ids[] = {
+       { .compatible = "via,velocity-vt6110", .data = &chip_info_table[0] },
+       { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, velocity_of_ids);
 
 /**
  *     get_chip_name   -       identifier to name
@@ -384,29 +414,6 @@ static const char *get_chip_name(enum chip_type chip_id)
        return chip_info_table[i].name;
 }
 
-/**
- *     velocity_remove1        -       device unplug
- *     @pdev: PCI device being removed
- *
- *     Device unload callback. Called on an unplug or on module
- *     unload for each active device that is present. Disconnects
- *     the device from the network layer and frees all the resources
- */
-static void velocity_remove1(struct pci_dev *pdev)
-{
-       struct net_device *dev = pci_get_drvdata(pdev);
-       struct velocity_info *vptr = netdev_priv(dev);
-
-       unregister_netdev(dev);
-       iounmap(vptr->mac_regs);
-       pci_release_regions(pdev);
-       pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
-       free_netdev(dev);
-
-       velocity_nics--;
-}
-
 /**
  *     velocity_set_int_opt    -       parser for integer options
  *     @opt: pointer to option value
@@ -998,9 +1005,9 @@ static void velocity_print_link_status(struct velocity_info *vptr)
 {
 
        if (vptr->mii_status & VELOCITY_LINK_FAIL) {
-               VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: failed to detect cable link\n", vptr->dev->name);
+               VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: failed to detect cable link\n", vptr->netdev->name);
        } else if (vptr->options.spd_dpx == SPD_DPX_AUTO) {
-               VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link auto-negotiation", vptr->dev->name);
+               VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link auto-negotiation", vptr->netdev->name);
 
                if (vptr->mii_status & VELOCITY_SPEED_1000)
                        VELOCITY_PRT(MSG_LEVEL_INFO, " speed 1000M bps");
@@ -1014,7 +1021,7 @@ static void velocity_print_link_status(struct velocity_info *vptr)
                else
                        VELOCITY_PRT(MSG_LEVEL_INFO, " half duplex\n");
        } else {
-               VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link forced", vptr->dev->name);
+               VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link forced", vptr->netdev->name);
                switch (vptr->options.spd_dpx) {
                case SPD_DPX_1000_FULL:
                        VELOCITY_PRT(MSG_LEVEL_INFO, " speed 1000M bps full duplex\n");
@@ -1180,6 +1187,17 @@ static void mii_init(struct velocity_info *vptr, u32 mii_status)
        u16 BMCR;
 
        switch (PHYID_GET_PHY_ID(vptr->phy_id)) {
+       case PHYID_ICPLUS_IP101A:
+               MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP),
+                                               MII_ADVERTISE, vptr->mac_regs);
+               if (vptr->mii_status & VELOCITY_DUPLEX_FULL)
+                       MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION,
+                                                               vptr->mac_regs);
+               else
+                       MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION,
+                                                               vptr->mac_regs);
+               MII_REG_BITS_ON(PLED_LALBE, MII_TPISTATUS, vptr->mac_regs);
+               break;
        case PHYID_CICADA_CS8201:
                /*
                 *      Reset to hardware default
@@ -1311,6 +1329,7 @@ static void velocity_init_registers(struct velocity_info *vptr,
                                    enum velocity_init_type type)
 {
        struct mac_regs __iomem *regs = vptr->mac_regs;
+       struct net_device *netdev = vptr->netdev;
        int i, mii_status;
 
        mac_wol_reset(regs);
@@ -1319,7 +1338,7 @@ static void velocity_init_registers(struct velocity_info *vptr,
        case VELOCITY_INIT_RESET:
        case VELOCITY_INIT_WOL:
 
-               netif_stop_queue(vptr->dev);
+               netif_stop_queue(netdev);
 
                /*
                 *      Reset RX to prevent RX pointer not on the 4X location
@@ -1332,7 +1351,7 @@ static void velocity_init_registers(struct velocity_info *vptr,
                if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) {
                        velocity_print_link_status(vptr);
                        if (!(vptr->mii_status & VELOCITY_LINK_FAIL))
-                               netif_wake_queue(vptr->dev);
+                               netif_wake_queue(netdev);
                }
 
                enable_flow_control_ability(vptr);
@@ -1352,9 +1371,11 @@ static void velocity_init_registers(struct velocity_info *vptr,
                velocity_soft_reset(vptr);
                mdelay(5);
 
-               mac_eeprom_reload(regs);
-               for (i = 0; i < 6; i++)
-                       writeb(vptr->dev->dev_addr[i], &(regs->PAR[i]));
+               if (!vptr->no_eeprom) {
+                       mac_eeprom_reload(regs);
+                       for (i = 0; i < 6; i++)
+                               writeb(netdev->dev_addr[i], regs->PAR + i);
+               }
 
                /*
                 *      clear Pre_ACPI bit.
@@ -1377,7 +1398,7 @@ static void velocity_init_registers(struct velocity_info *vptr,
                /*
                 *      Set packet filter: Receive directed and broadcast address
                 */
-               velocity_set_multi(vptr->dev);
+               velocity_set_multi(netdev);
 
                /*
                 *      Enable MII auto-polling
@@ -1404,14 +1425,14 @@ static void velocity_init_registers(struct velocity_info *vptr,
                writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), &regs->CR0Set);
 
                mii_status = velocity_get_opt_media_mode(vptr);
-               netif_stop_queue(vptr->dev);
+               netif_stop_queue(netdev);
 
                mii_init(vptr, mii_status);
 
                if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) {
                        velocity_print_link_status(vptr);
                        if (!(vptr->mii_status & VELOCITY_LINK_FAIL))
-                               netif_wake_queue(vptr->dev);
+                               netif_wake_queue(netdev);
                }
 
                enable_flow_control_ability(vptr);
@@ -1459,7 +1480,6 @@ static int velocity_init_dma_rings(struct velocity_info *vptr)
        struct velocity_opt *opt = &vptr->options;
        const unsigned int rx_ring_size = opt->numrx * sizeof(struct rx_desc);
        const unsigned int tx_ring_size = opt->numtx * sizeof(struct tx_desc);
-       struct pci_dev *pdev = vptr->pdev;
        dma_addr_t pool_dma;
        void *pool;
        unsigned int i;
@@ -1467,14 +1487,14 @@ static int velocity_init_dma_rings(struct velocity_info *vptr)
        /*
         * Allocate all RD/TD rings a single pool.
         *
-        * pci_alloc_consistent() fulfills the requirement for 64 bytes
+        * dma_alloc_coherent() fulfills the requirement for 64 bytes
         * alignment
         */
-       pool = pci_alloc_consistent(pdev, tx_ring_size * vptr->tx.numq +
-                                   rx_ring_size, &pool_dma);
+       pool = dma_alloc_coherent(vptr->dev, tx_ring_size * vptr->tx.numq +
+                                   rx_ring_size, &pool_dma, GFP_ATOMIC);
        if (!pool) {
-               dev_err(&pdev->dev, "%s : DMA memory allocation failed.\n",
-                       vptr->dev->name);
+               dev_err(vptr->dev, "%s : DMA memory allocation failed.\n",
+                       vptr->netdev->name);
                return -ENOMEM;
        }
 
@@ -1514,7 +1534,7 @@ static int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx)
        struct rx_desc *rd = &(vptr->rx.ring[idx]);
        struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]);
 
-       rd_info->skb = netdev_alloc_skb(vptr->dev, vptr->rx.buf_sz + 64);
+       rd_info->skb = netdev_alloc_skb(vptr->netdev, vptr->rx.buf_sz + 64);
        if (rd_info->skb == NULL)
                return -ENOMEM;
 
@@ -1524,8 +1544,8 @@ static int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx)
         */
        skb_reserve(rd_info->skb,
                        64 - ((unsigned long) rd_info->skb->data & 63));
-       rd_info->skb_dma = pci_map_single(vptr->pdev, rd_info->skb->data,
-                                       vptr->rx.buf_sz, PCI_DMA_FROMDEVICE);
+       rd_info->skb_dma = dma_map_single(vptr->dev, rd_info->skb->data,
+                                       vptr->rx.buf_sz, DMA_FROM_DEVICE);
 
        /*
         *      Fill in the descriptor to match
@@ -1588,8 +1608,8 @@ static void velocity_free_rd_ring(struct velocity_info *vptr)
 
                if (!rd_info->skb)
                        continue;
-               pci_unmap_single(vptr->pdev, rd_info->skb_dma, vptr->rx.buf_sz,
-                                PCI_DMA_FROMDEVICE);
+               dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz,
+                                DMA_FROM_DEVICE);
                rd_info->skb_dma = 0;
 
                dev_kfree_skb(rd_info->skb);
@@ -1620,7 +1640,7 @@ static int velocity_init_rd_ring(struct velocity_info *vptr)
 
        if (velocity_rx_refill(vptr) != vptr->options.numrx) {
                VELOCITY_PRT(MSG_LEVEL_ERR, KERN_ERR
-                       "%s: failed to allocate RX buffer.\n", vptr->dev->name);
+                       "%s: failed to allocate RX buffer.\n", vptr->netdev->name);
                velocity_free_rd_ring(vptr);
                goto out;
        }
@@ -1670,7 +1690,7 @@ static void velocity_free_dma_rings(struct velocity_info *vptr)
        const int size = vptr->options.numrx * sizeof(struct rx_desc) +
                vptr->options.numtx * sizeof(struct tx_desc) * vptr->tx.numq;
 
-       pci_free_consistent(vptr->pdev, size, vptr->rx.ring, vptr->rx.pool_dma);
+       dma_free_coherent(vptr->dev, size, vptr->rx.ring, vptr->rx.pool_dma);
 }
 
 static int velocity_init_rings(struct velocity_info *vptr, int mtu)
@@ -1727,8 +1747,8 @@ static void velocity_free_tx_buf(struct velocity_info *vptr,
                                pktlen = max_t(size_t, pktlen,
                                                td->td_buf[i].size & ~TD_QUEUE);
 
-                       pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i],
-                                       le16_to_cpu(pktlen), PCI_DMA_TODEVICE);
+                       dma_unmap_single(vptr->dev, tdinfo->skb_dma[i],
+                                       le16_to_cpu(pktlen), DMA_TO_DEVICE);
                }
        }
        dev_kfree_skb_irq(skb);
@@ -1750,8 +1770,8 @@ static void velocity_free_td_ring_entry(struct velocity_info *vptr,
        if (td_info->skb) {
                for (i = 0; i < td_info->nskb_dma; i++) {
                        if (td_info->skb_dma[i]) {
-                               pci_unmap_single(vptr->pdev, td_info->skb_dma[i],
-                                       td_info->skb->len, PCI_DMA_TODEVICE);
+                               dma_unmap_single(vptr->dev, td_info->skb_dma[i],
+                                       td_info->skb->len, DMA_TO_DEVICE);
                                td_info->skb_dma[i] = 0;
                        }
                }
@@ -1809,7 +1829,7 @@ static void velocity_error(struct velocity_info *vptr, int status)
                printk(KERN_ERR "TD structure error TDindex=%hx\n", readw(&regs->TDIdx[0]));
                BYTE_REG_BITS_ON(TXESR_TDSTR, &regs->TXESR);
                writew(TRDCSR_RUN, &regs->TDCSRClr);
-               netif_stop_queue(vptr->dev);
+               netif_stop_queue(vptr->netdev);
 
                /* FIXME: port over the pci_device_failed code and use it
                   here */
@@ -1850,10 +1870,10 @@ static void velocity_error(struct velocity_info *vptr, int status)
 
                if (linked) {
                        vptr->mii_status &= ~VELOCITY_LINK_FAIL;
-                       netif_carrier_on(vptr->dev);
+                       netif_carrier_on(vptr->netdev);
                } else {
                        vptr->mii_status |= VELOCITY_LINK_FAIL;
-                       netif_carrier_off(vptr->dev);
+                       netif_carrier_off(vptr->netdev);
                }
 
                velocity_print_link_status(vptr);
@@ -1867,9 +1887,9 @@ static void velocity_error(struct velocity_info *vptr, int status)
                enable_mii_autopoll(regs);
 
                if (vptr->mii_status & VELOCITY_LINK_FAIL)
-                       netif_stop_queue(vptr->dev);
+                       netif_stop_queue(vptr->netdev);
                else
-                       netif_wake_queue(vptr->dev);
+                       netif_wake_queue(vptr->netdev);
 
        }
        if (status & ISR_MIBFI)
@@ -1894,7 +1914,7 @@ static int velocity_tx_srv(struct velocity_info *vptr)
        int idx;
        int works = 0;
        struct velocity_td_info *tdinfo;
-       struct net_device_stats *stats = &vptr->dev->stats;
+       struct net_device_stats *stats = &vptr->netdev->stats;
 
        for (qnum = 0; qnum < vptr->tx.numq; qnum++) {
                for (idx = vptr->tx.tail[qnum]; vptr->tx.used[qnum] > 0;
@@ -1939,9 +1959,9 @@ static int velocity_tx_srv(struct velocity_info *vptr)
         *      Look to see if we should kick the transmit network
         *      layer for more work.
         */
-       if (netif_queue_stopped(vptr->dev) && (full == 0) &&
+       if (netif_queue_stopped(vptr->netdev) && (full == 0) &&
            (!(vptr->mii_status & VELOCITY_LINK_FAIL))) {
-               netif_wake_queue(vptr->dev);
+               netif_wake_queue(vptr->netdev);
        }
        return works;
 }
@@ -1989,7 +2009,7 @@ static int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size,
        if (pkt_size < rx_copybreak) {
                struct sk_buff *new_skb;
 
-               new_skb = netdev_alloc_skb_ip_align(vptr->dev, pkt_size);
+               new_skb = netdev_alloc_skb_ip_align(vptr->netdev, pkt_size);
                if (new_skb) {
                        new_skb->ip_summed = rx_skb[0]->ip_summed;
                        skb_copy_from_linear_data(*rx_skb, new_skb->data, pkt_size);
@@ -2029,15 +2049,14 @@ static inline void velocity_iph_realign(struct velocity_info *vptr,
  */
 static int velocity_receive_frame(struct velocity_info *vptr, int idx)
 {
-       void (*pci_action)(struct pci_dev *, dma_addr_t, size_t, int);
-       struct net_device_stats *stats = &vptr->dev->stats;
+       struct net_device_stats *stats = &vptr->netdev->stats;
        struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]);
        struct rx_desc *rd = &(vptr->rx.ring[idx]);
        int pkt_len = le16_to_cpu(rd->rdesc0.len) & 0x3fff;
        struct sk_buff *skb;
 
        if (rd->rdesc0.RSR & (RSR_STP | RSR_EDP)) {
-               VELOCITY_PRT(MSG_LEVEL_VERBOSE, KERN_ERR " %s : the received frame span multple RDs.\n", vptr->dev->name);
+               VELOCITY_PRT(MSG_LEVEL_VERBOSE, KERN_ERR " %s : the received frame span multple RDs.\n", vptr->netdev->name);
                stats->rx_length_errors++;
                return -EINVAL;
        }
@@ -2047,8 +2066,8 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx)
 
        skb = rd_info->skb;
 
-       pci_dma_sync_single_for_cpu(vptr->pdev, rd_info->skb_dma,
-                                   vptr->rx.buf_sz, PCI_DMA_FROMDEVICE);
+       dma_sync_single_for_cpu(vptr->dev, rd_info->skb_dma,
+                                   vptr->rx.buf_sz, DMA_FROM_DEVICE);
 
        /*
         *      Drop frame not meeting IEEE 802.3
@@ -2061,21 +2080,20 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx)
                }
        }
 
-       pci_action = pci_dma_sync_single_for_device;
-
        velocity_rx_csum(rd, skb);
 
        if (velocity_rx_copy(&skb, pkt_len, vptr) < 0) {
                velocity_iph_realign(vptr, skb, pkt_len);
-               pci_action = pci_unmap_single;
                rd_info->skb = NULL;
+               dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz,
+                                DMA_FROM_DEVICE);
+       } else {
+               dma_sync_single_for_device(vptr->dev, rd_info->skb_dma,
+                                          vptr->rx.buf_sz, DMA_FROM_DEVICE);
        }
 
-       pci_action(vptr->pdev, rd_info->skb_dma, vptr->rx.buf_sz,
-                  PCI_DMA_FROMDEVICE);
-
        skb_put(skb, pkt_len - 4);
-       skb->protocol = eth_type_trans(skb, vptr->dev);
+       skb->protocol = eth_type_trans(skb, vptr->netdev);
 
        if (rd->rdesc0.RSR & RSR_DETAG) {
                u16 vid = swab16(le16_to_cpu(rd->rdesc1.PQTAG));
@@ -2100,7 +2118,7 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx)
  */
 static int velocity_rx_srv(struct velocity_info *vptr, int budget_left)
 {
-       struct net_device_stats *stats = &vptr->dev->stats;
+       struct net_device_stats *stats = &vptr->netdev->stats;
        int rd_curr = vptr->rx.curr;
        int works = 0;
 
@@ -2235,15 +2253,15 @@ static int velocity_open(struct net_device *dev)
                goto out;
 
        /* Ensure chip is running */
-       pci_set_power_state(vptr->pdev, PCI_D0);
+       velocity_set_power_state(vptr, PCI_D0);
 
        velocity_init_registers(vptr, VELOCITY_INIT_COLD);
 
-       ret = request_irq(vptr->pdev->irq, velocity_intr, IRQF_SHARED,
+       ret = request_irq(dev->irq, velocity_intr, IRQF_SHARED,
                          dev->name, dev);
        if (ret < 0) {
                /* Power down the chip */
-               pci_set_power_state(vptr->pdev, PCI_D3hot);
+               velocity_set_power_state(vptr, PCI_D3hot);
                velocity_free_rings(vptr);
                goto out;
        }
@@ -2292,7 +2310,7 @@ static int velocity_change_mtu(struct net_device *dev, int new_mtu)
 
        if ((new_mtu < VELOCITY_MIN_MTU) || new_mtu > (VELOCITY_MAX_MTU)) {
                VELOCITY_PRT(MSG_LEVEL_ERR, KERN_NOTICE "%s: Invalid MTU.\n",
-                               vptr->dev->name);
+                               vptr->netdev->name);
                ret = -EINVAL;
                goto out_0;
        }
@@ -2314,8 +2332,9 @@ static int velocity_change_mtu(struct net_device *dev, int new_mtu)
                        goto out_0;
                }
 
-               tmp_vptr->dev = dev;
+               tmp_vptr->netdev = dev;
                tmp_vptr->pdev = vptr->pdev;
+               tmp_vptr->dev = vptr->dev;
                tmp_vptr->options = vptr->options;
                tmp_vptr->tx.numq = vptr->tx.numq;
 
@@ -2415,7 +2434,7 @@ static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
           saving then we need to bring the device back up to talk to it */
 
        if (!netif_running(dev))
-               pci_set_power_state(vptr->pdev, PCI_D0);
+               velocity_set_power_state(vptr, PCI_D0);
 
        switch (cmd) {
        case SIOCGMIIPHY:       /* Get address of MII PHY in use. */
@@ -2428,7 +2447,7 @@ static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                ret = -EOPNOTSUPP;
        }
        if (!netif_running(dev))
-               pci_set_power_state(vptr->pdev, PCI_D3hot);
+               velocity_set_power_state(vptr, PCI_D3hot);
 
 
        return ret;
@@ -2494,7 +2513,7 @@ static int velocity_close(struct net_device *dev)
        if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED)
                velocity_get_ip(vptr);
 
-       free_irq(vptr->pdev->irq, dev);
+       free_irq(dev->irq, dev);
 
        velocity_free_rings(vptr);
 
@@ -2550,7 +2569,8 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb,
         *      add it to the transmit ring.
         */
        tdinfo->skb = skb;
-       tdinfo->skb_dma[0] = pci_map_single(vptr->pdev, skb->data, pktlen, PCI_DMA_TODEVICE);
+       tdinfo->skb_dma[0] = dma_map_single(vptr->dev, skb->data, pktlen,
+                                                               DMA_TO_DEVICE);
        td_ptr->tdesc0.len = cpu_to_le16(pktlen);
        td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]);
        td_ptr->td_buf[0].pa_high = 0;
@@ -2560,7 +2580,7 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
 
-               tdinfo->skb_dma[i + 1] = skb_frag_dma_map(&vptr->pdev->dev,
+               tdinfo->skb_dma[i + 1] = skb_frag_dma_map(vptr->dev,
                                                          frag, 0,
                                                          skb_frag_size(frag),
                                                          DMA_TO_DEVICE);
@@ -2632,12 +2652,9 @@ static const struct net_device_ops velocity_netdev_ops = {
  *     Set up the initial velocity_info struct for the device that has been
  *     discovered.
  */
-static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr,
-                              const struct velocity_info_tbl *info)
+static void velocity_init_info(struct velocity_info *vptr,
+                               const struct velocity_info_tbl *info)
 {
-       memset(vptr, 0, sizeof(struct velocity_info));
-
-       vptr->pdev = pdev;
        vptr->chip_id = info->chip_id;
        vptr->tx.numq = info->txqueue;
        vptr->multicast_limit = MCAM_SIZE;
@@ -2652,10 +2669,9 @@ static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr,
  *     Retrieve the PCI configuration space data that interests us from
  *     the kernel PCI layer
  */
-static int velocity_get_pci_info(struct velocity_info *vptr,
-                                struct pci_dev *pdev)
+static int velocity_get_pci_info(struct velocity_info *vptr)
 {
-       vptr->rev_id = pdev->revision;
+       struct pci_dev *pdev = vptr->pdev;
 
        pci_set_master(pdev);
 
@@ -2678,7 +2694,37 @@ static int velocity_get_pci_info(struct velocity_info *vptr,
                dev_err(&pdev->dev, "region #1 is too small.\n");
                return -EINVAL;
        }
-       vptr->pdev = pdev;
+
+       return 0;
+}
+
+/**
+ *     velocity_get_platform_info - retrieve platform info for device
+ *     @vptr: velocity device
+ *     @pdev: platform device it matches
+ *
+ *     Retrieve the Platform configuration data that interests us
+ */
+static int velocity_get_platform_info(struct velocity_info *vptr)
+{
+       struct resource res;
+       int ret;
+
+       if (of_get_property(vptr->dev->of_node, "no-eeprom", NULL))
+               vptr->no_eeprom = 1;
+
+       ret = of_address_to_resource(vptr->dev->of_node, 0, &res);
+       if (ret) {
+               dev_err(vptr->dev, "unable to find memory address\n");
+               return ret;
+       }
+
+       vptr->memaddr = res.start;
+
+       if (resource_size(&res) < VELOCITY_IO_SIZE) {
+               dev_err(vptr->dev, "memory region is too small.\n");
+               return -EINVAL;
+       }
 
        return 0;
 }
@@ -2692,7 +2738,7 @@ static int velocity_get_pci_info(struct velocity_info *vptr,
  */
 static void velocity_print_info(struct velocity_info *vptr)
 {
-       struct net_device *dev = vptr->dev;
+       struct net_device *dev = vptr->netdev;
 
        printk(KERN_INFO "%s: %s\n", dev->name, get_chip_name(vptr->chip_id));
        printk(KERN_INFO "%s: Ethernet Address: %pM\n",
@@ -2707,21 +2753,22 @@ static u32 velocity_get_link(struct net_device *dev)
 }
 
 /**
- *     velocity_found1         -       set up discovered velocity card
+ *     velocity_probe - set up discovered velocity device
  *     @pdev: PCI device
  *     @ent: PCI device table entry that matched
+ *     @bustype: bus that device is connected to
  *
  *     Configure a discovered adapter from scratch. Return a negative
  *     errno error code on failure paths.
  */
-static int velocity_found1(struct pci_dev *pdev,
-                          const struct pci_device_id *ent)
+static int velocity_probe(struct device *dev, int irq,
+                          const struct velocity_info_tbl *info,
+                          enum velocity_bus_type bustype)
 {
        static int first = 1;
-       struct net_device *dev;
+       struct net_device *netdev;
        int i;
        const char *drv_string;
-       const struct velocity_info_tbl *info = &chip_info_table[ent->driver_data];
        struct velocity_info *vptr;
        struct mac_regs __iomem *regs;
        int ret = -ENOMEM;
@@ -2730,20 +2777,18 @@ static int velocity_found1(struct pci_dev *pdev,
         * can support more than MAX_UNITS.
         */
        if (velocity_nics >= MAX_UNITS) {
-               dev_notice(&pdev->dev, "already found %d NICs.\n",
-                          velocity_nics);
+               dev_notice(dev, "already found %d NICs.\n", velocity_nics);
                return -ENODEV;
        }
 
-       dev = alloc_etherdev(sizeof(struct velocity_info));
-       if (!dev)
+       netdev = alloc_etherdev(sizeof(struct velocity_info));
+       if (!netdev)
                goto out;
 
        /* Chain it all together */
 
-       SET_NETDEV_DEV(dev, &pdev->dev);
-       vptr = netdev_priv(dev);
-
+       SET_NETDEV_DEV(netdev, dev);
+       vptr = netdev_priv(netdev);
 
        if (first) {
                printk(KERN_INFO "%s Ver. %s\n",
@@ -2753,41 +2798,41 @@ static int velocity_found1(struct pci_dev *pdev,
                first = 0;
        }
 
-       velocity_init_info(pdev, vptr, info);
-
+       netdev->irq = irq;
+       vptr->netdev = netdev;
        vptr->dev = dev;
 
-       ret = pci_enable_device(pdev);
-       if (ret < 0)
-               goto err_free_dev;
+       velocity_init_info(vptr, info);
 
-       ret = velocity_get_pci_info(vptr, pdev);
-       if (ret < 0) {
-               /* error message already printed */
-               goto err_disable;
-       }
+       if (bustype == BUS_PCI) {
+               vptr->pdev = to_pci_dev(dev);
 
-       ret = pci_request_regions(pdev, VELOCITY_NAME);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "No PCI resources.\n");
-               goto err_disable;
+               ret = velocity_get_pci_info(vptr);
+               if (ret < 0)
+                       goto err_free_dev;
+       } else {
+               vptr->pdev = NULL;
+               ret = velocity_get_platform_info(vptr);
+               if (ret < 0)
+                       goto err_free_dev;
        }
 
        regs = ioremap(vptr->memaddr, VELOCITY_IO_SIZE);
        if (regs == NULL) {
                ret = -EIO;
-               goto err_release_res;
+               goto err_free_dev;
        }
 
        vptr->mac_regs = regs;
+       vptr->rev_id = readb(&regs->rev_id);
 
        mac_wol_reset(regs);
 
        for (i = 0; i < 6; i++)
-               dev->dev_addr[i] = readb(&regs->PAR[i]);
+               netdev->dev_addr[i] = readb(&regs->PAR[i]);
 
 
-       drv_string = dev_driver_string(&pdev->dev);
+       drv_string = dev_driver_string(dev);
 
        velocity_get_options(&vptr->options, velocity_nics, drv_string);
 
@@ -2808,46 +2853,125 @@ static int velocity_found1(struct pci_dev *pdev,
 
        vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs);
 
-       dev->netdev_ops = &velocity_netdev_ops;
-       dev->ethtool_ops = &velocity_ethtool_ops;
-       netif_napi_add(dev, &vptr->napi, velocity_poll, VELOCITY_NAPI_WEIGHT);
+       netdev->netdev_ops = &velocity_netdev_ops;
+       netdev->ethtool_ops = &velocity_ethtool_ops;
+       netif_napi_add(netdev, &vptr->napi, velocity_poll,
+                                                       VELOCITY_NAPI_WEIGHT);
 
-       dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
+       netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
                           NETIF_F_HW_VLAN_CTAG_TX;
-       dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_FILTER |
-                        NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_IP_CSUM;
+       netdev->features |= NETIF_F_HW_VLAN_CTAG_TX |
+                       NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX |
+                       NETIF_F_IP_CSUM;
 
-       ret = register_netdev(dev);
+       ret = register_netdev(netdev);
        if (ret < 0)
                goto err_iounmap;
 
-       if (!velocity_get_link(dev)) {
-               netif_carrier_off(dev);
+       if (!velocity_get_link(netdev)) {
+               netif_carrier_off(netdev);
                vptr->mii_status |= VELOCITY_LINK_FAIL;
        }
 
        velocity_print_info(vptr);
-       pci_set_drvdata(pdev, dev);
+       dev_set_drvdata(vptr->dev, netdev);
 
        /* and leave the chip powered down */
 
-       pci_set_power_state(pdev, PCI_D3hot);
+       velocity_set_power_state(vptr, PCI_D3hot);
        velocity_nics++;
 out:
        return ret;
 
 err_iounmap:
        iounmap(regs);
-err_release_res:
-       pci_release_regions(pdev);
-err_disable:
-       pci_disable_device(pdev);
 err_free_dev:
-       free_netdev(dev);
+       free_netdev(netdev);
        goto out;
 }
 
-#ifdef CONFIG_PM
+/**
+ *     velocity_remove - device unplug
+ *     @dev: device being removed
+ *
+ *     Device unload callback. Called on an unplug or on module
+ *     unload for each active device that is present. Disconnects
+ *     the device from the network layer and frees all the resources
+ */
+static int velocity_remove(struct device *dev)
+{
+       struct net_device *netdev = dev_get_drvdata(dev);
+       struct velocity_info *vptr = netdev_priv(netdev);
+
+       unregister_netdev(netdev);
+       iounmap(vptr->mac_regs);
+       free_netdev(netdev);
+       velocity_nics--;
+
+       return 0;
+}
+
+static int velocity_pci_probe(struct pci_dev *pdev,
+                              const struct pci_device_id *ent)
+{
+       const struct velocity_info_tbl *info =
+                                       &chip_info_table[ent->driver_data];
+       int ret;
+
+       ret = pci_enable_device(pdev);
+       if (ret < 0)
+               return ret;
+
+       ret = pci_request_regions(pdev, VELOCITY_NAME);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "No PCI resources.\n");
+               goto fail1;
+       }
+
+       ret = velocity_probe(&pdev->dev, pdev->irq, info, BUS_PCI);
+       if (ret == 0)
+               return 0;
+
+       pci_release_regions(pdev);
+fail1:
+       pci_disable_device(pdev);
+       return ret;
+}
+
+static void velocity_pci_remove(struct pci_dev *pdev)
+{
+       velocity_remove(&pdev->dev);
+
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+}
+
+static int velocity_platform_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *of_id;
+       const struct velocity_info_tbl *info;
+       int irq;
+
+       of_id = of_match_device(velocity_of_ids, &pdev->dev);
+       if (!of_id)
+               return -EINVAL;
+       info = of_id->data;
+
+       irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+       if (!irq)
+               return -EINVAL;
+
+       return velocity_probe(&pdev->dev, irq, info, BUS_PLATFORM);
+}
+
+static int velocity_platform_remove(struct platform_device *pdev)
+{
+       velocity_remove(&pdev->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
 /**
  *     wol_calc_crc            -       WOL CRC
  *     @pattern: data pattern
@@ -3004,32 +3128,35 @@ static void velocity_save_context(struct velocity_info *vptr, struct velocity_co
 
 }
 
-static int velocity_suspend(struct pci_dev *pdev, pm_message_t state)
+static int velocity_suspend(struct device *dev)
 {
-       struct net_device *dev = pci_get_drvdata(pdev);
-       struct velocity_info *vptr = netdev_priv(dev);
+       struct net_device *netdev = dev_get_drvdata(dev);
+       struct velocity_info *vptr = netdev_priv(netdev);
        unsigned long flags;
 
-       if (!netif_running(vptr->dev))
+       if (!netif_running(vptr->netdev))
                return 0;
 
-       netif_device_detach(vptr->dev);
+       netif_device_detach(vptr->netdev);
 
        spin_lock_irqsave(&vptr->lock, flags);
-       pci_save_state(pdev);
+       if (vptr->pdev)
+               pci_save_state(vptr->pdev);
 
        if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) {
                velocity_get_ip(vptr);
                velocity_save_context(vptr, &vptr->context);
                velocity_shutdown(vptr);
                velocity_set_wol(vptr);
-               pci_enable_wake(pdev, PCI_D3hot, 1);
-               pci_set_power_state(pdev, PCI_D3hot);
+               if (vptr->pdev)
+                       pci_enable_wake(vptr->pdev, PCI_D3hot, 1);
+               velocity_set_power_state(vptr, PCI_D3hot);
        } else {
                velocity_save_context(vptr, &vptr->context);
                velocity_shutdown(vptr);
-               pci_disable_device(pdev);
-               pci_set_power_state(pdev, pci_choose_state(pdev, state));
+               if (vptr->pdev)
+                       pci_disable_device(vptr->pdev);
+               velocity_set_power_state(vptr, PCI_D3hot);
        }
 
        spin_unlock_irqrestore(&vptr->lock, flags);
@@ -3071,19 +3198,22 @@ static void velocity_restore_context(struct velocity_info *vptr, struct velocity
                writeb(*((u8 *) (context->mac_reg + i)), ptr + i);
 }
 
-static int velocity_resume(struct pci_dev *pdev)
+static int velocity_resume(struct device *dev)
 {
-       struct net_device *dev = pci_get_drvdata(pdev);
-       struct velocity_info *vptr = netdev_priv(dev);
+       struct net_device *netdev = dev_get_drvdata(dev);
+       struct velocity_info *vptr = netdev_priv(netdev);
        unsigned long flags;
        int i;
 
-       if (!netif_running(vptr->dev))
+       if (!netif_running(vptr->netdev))
                return 0;
 
-       pci_set_power_state(pdev, PCI_D0);
-       pci_enable_wake(pdev, 0, 0);
-       pci_restore_state(pdev);
+       velocity_set_power_state(vptr, PCI_D0);
+
+       if (vptr->pdev) {
+               pci_enable_wake(vptr->pdev, PCI_D0, 0);
+               pci_restore_state(vptr->pdev);
+       }
 
        mac_wol_reset(vptr->mac_regs);
 
@@ -3101,27 +3231,38 @@ static int velocity_resume(struct pci_dev *pdev)
 
        mac_enable_int(vptr->mac_regs);
        spin_unlock_irqrestore(&vptr->lock, flags);
-       netif_device_attach(vptr->dev);
+       netif_device_attach(vptr->netdev);
 
        return 0;
 }
-#endif
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(velocity_pm_ops, velocity_suspend, velocity_resume);
 
 /*
  *     Definition for our device driver. The PCI layer interface
  *     uses this to handle all our card discover and plugging
  */
-static struct pci_driver velocity_driver = {
+static struct pci_driver velocity_pci_driver = {
        .name           = VELOCITY_NAME,
-       .id_table       = velocity_id_table,
-       .probe          = velocity_found1,
-       .remove         = velocity_remove1,
-#ifdef CONFIG_PM
-       .suspend        = velocity_suspend,
-       .resume         = velocity_resume,
-#endif
+       .id_table       = velocity_pci_id_table,
+       .probe          = velocity_pci_probe,
+       .remove         = velocity_pci_remove,
+       .driver = {
+               .pm = &velocity_pm_ops,
+       },
 };
 
+static struct platform_driver velocity_platform_driver = {
+       .probe          = velocity_platform_probe,
+       .remove         = velocity_platform_remove,
+       .driver = {
+               .name = "via-velocity",
+               .owner = THIS_MODULE,
+               .of_match_table = velocity_of_ids,
+               .pm = &velocity_pm_ops,
+       },
+};
 
 /**
  *     velocity_ethtool_up     -       pre hook for ethtool
@@ -3134,7 +3275,7 @@ static int velocity_ethtool_up(struct net_device *dev)
 {
        struct velocity_info *vptr = netdev_priv(dev);
        if (!netif_running(dev))
-               pci_set_power_state(vptr->pdev, PCI_D0);
+               velocity_set_power_state(vptr, PCI_D0);
        return 0;
 }
 
@@ -3149,7 +3290,7 @@ static void velocity_ethtool_down(struct net_device *dev)
 {
        struct velocity_info *vptr = netdev_priv(dev);
        if (!netif_running(dev))
-               pci_set_power_state(vptr->pdev, PCI_D3hot);
+               velocity_set_power_state(vptr, PCI_D3hot);
 }
 
 static int velocity_get_settings(struct net_device *dev,
@@ -3269,9 +3410,14 @@ static int velocity_set_settings(struct net_device *dev,
 static void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
        struct velocity_info *vptr = netdev_priv(dev);
+
        strlcpy(info->driver, VELOCITY_NAME, sizeof(info->driver));
        strlcpy(info->version, VELOCITY_VERSION, sizeof(info->version));
-       strlcpy(info->bus_info, pci_name(vptr->pdev), sizeof(info->bus_info));
+       if (vptr->pdev)
+               strlcpy(info->bus_info, pci_name(vptr->pdev),
+                                               sizeof(info->bus_info));
+       else
+               strlcpy(info->bus_info, "platform", sizeof(info->bus_info));
 }
 
 static void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
@@ -3561,13 +3707,20 @@ static void velocity_unregister_notifier(void)
  */
 static int __init velocity_init_module(void)
 {
-       int ret;
+       int ret_pci, ret_platform;
 
        velocity_register_notifier();
-       ret = pci_register_driver(&velocity_driver);
-       if (ret < 0)
+
+       ret_pci = pci_register_driver(&velocity_pci_driver);
+       ret_platform = platform_driver_register(&velocity_platform_driver);
+
+       /* if both_registers failed, remove the notifier */
+       if ((ret_pci < 0) && (ret_platform < 0)) {
                velocity_unregister_notifier();
-       return ret;
+               return ret_pci;
+       }
+
+       return 0;
 }
 
 /**
@@ -3581,7 +3734,9 @@ static int __init velocity_init_module(void)
 static void __exit velocity_cleanup_module(void)
 {
        velocity_unregister_notifier();
-       pci_unregister_driver(&velocity_driver);
+
+       pci_unregister_driver(&velocity_pci_driver);
+       platform_driver_unregister(&velocity_platform_driver);
 }
 
 module_init(velocity_init_module);
index 4cb9f13..9453bfa 100644 (file)
@@ -1265,7 +1265,7 @@ struct velocity_context {
 #define PHYID_VT3216_64BIT  0x000FC600UL
 #define PHYID_MARVELL_1000  0x01410C50UL
 #define PHYID_MARVELL_1000S 0x01410C40UL
-
+#define PHYID_ICPLUS_IP101A 0x02430C54UL
 #define PHYID_REV_ID_MASK   0x0000000FUL
 
 #define PHYID_GET_PHY_ID(i)         ((i) & ~PHYID_REV_ID_MASK)
@@ -1434,8 +1434,10 @@ struct velocity_opt {
 #define GET_RD_BY_IDX(vptr, idx)   (vptr->rd_ring[idx])
 
 struct velocity_info {
+       struct device *dev;
        struct pci_dev *pdev;
-       struct net_device *dev;
+       struct net_device *netdev;
+       int no_eeprom;
 
        unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
        u8 ip_addr[4];
@@ -1514,7 +1516,7 @@ static inline int velocity_get_ip(struct velocity_info *vptr)
        int res = -ENOENT;
 
        rcu_read_lock();
-       in_dev = __in_dev_get_rcu(vptr->dev);
+       in_dev = __in_dev_get_rcu(vptr->netdev);
        if (in_dev != NULL) {
                ifa = (struct in_ifaddr *) in_dev->ifa_list;
                if (ifa != NULL) {
index a518dca..30fed08 100644 (file)
@@ -734,7 +734,6 @@ err_hw_probe:
        unregister_netdev(ndev);
 err_register:
        free_netdev(ndev);
-       platform_set_drvdata(pdev, NULL);
        return err;
 }
 
@@ -750,7 +749,6 @@ static int w5100_remove(struct platform_device *pdev)
 
        unregister_netdev(ndev);
        free_netdev(ndev);
-       platform_set_drvdata(pdev, NULL);
        return 0;
 }
 
index 6e00e3f..e928845 100644 (file)
@@ -646,7 +646,6 @@ err_hw_probe:
        unregister_netdev(ndev);
 err_register:
        free_netdev(ndev);
-       platform_set_drvdata(pdev, NULL);
        return err;
 }
 
@@ -662,7 +661,6 @@ static int w5300_remove(struct platform_device *pdev)
 
        unregister_netdev(ndev);
        free_netdev(ndev);
-       platform_set_drvdata(pdev, NULL);
        return 0;
 }
 
index 122d60c..7b90a5e 100644 (file)
@@ -5,7 +5,7 @@
 config NET_VENDOR_XILINX
        bool "Xilinx devices"
        default y
-       depends on PPC || PPC32 || MICROBLAZE
+       depends on PPC || PPC32 || MICROBLAZE || ARCH_ZYNQ
        ---help---
          If you have a network (Ethernet) card belonging to this class, say Y
          and read the Ethernet-HOWTO, available from
@@ -20,7 +20,7 @@ if NET_VENDOR_XILINX
 
 config XILINX_EMACLITE
        tristate "Xilinx 10/100 Ethernet Lite support"
-       depends on (PPC32 || MICROBLAZE)
+       depends on (PPC32 || MICROBLAZE || ARCH_ZYNQ)
        select PHYLIB
        ---help---
          This driver supports the 10/100 Ethernet Lite from Xilinx.
index 57c2e5e..58eb448 100644 (file)
@@ -1007,7 +1007,7 @@ static int temac_of_probe(struct platform_device *op)
                return -ENOMEM;
 
        ether_setup(ndev);
-       dev_set_drvdata(&op->dev, ndev);
+       platform_set_drvdata(op, ndev);
        SET_NETDEV_DEV(ndev, &op->dev);
        ndev->flags &= ~IFF_MULTICAST;  /* clear multicast */
        ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST;
@@ -1136,7 +1136,7 @@ static int temac_of_probe(struct platform_device *op)
 
 static int temac_of_remove(struct platform_device *op)
 {
-       struct net_device *ndev = dev_get_drvdata(&op->dev);
+       struct net_device *ndev = platform_get_drvdata(op);
        struct temac_local *lp = netdev_priv(ndev);
 
        temac_mdio_teardown(lp);
@@ -1145,7 +1145,6 @@ static int temac_of_remove(struct platform_device *op)
        if (lp->phy_node)
                of_node_put(lp->phy_node);
        lp->phy_node = NULL;
-       dev_set_drvdata(&op->dev, NULL);
        iounmap(lp->regs);
        if (lp->sdma_regs)
                iounmap(lp->sdma_regs);
index 24748e8..fb7d1c2 100644 (file)
@@ -1484,7 +1484,7 @@ static int axienet_of_probe(struct platform_device *op)
                return -ENOMEM;
 
        ether_setup(ndev);
-       dev_set_drvdata(&op->dev, ndev);
+       platform_set_drvdata(op, ndev);
 
        SET_NETDEV_DEV(ndev, &op->dev);
        ndev->flags &= ~IFF_MULTICAST;  /* clear multicast */
@@ -1622,7 +1622,7 @@ nodev:
 
 static int axienet_of_remove(struct platform_device *op)
 {
-       struct net_device *ndev = dev_get_drvdata(&op->dev);
+       struct net_device *ndev = platform_get_drvdata(op);
        struct axienet_local *lp = netdev_priv(ndev);
 
        axienet_mdio_teardown(lp);
@@ -1632,8 +1632,6 @@ static int axienet_of_remove(struct platform_device *op)
                of_node_put(lp->phy_node);
        lp->phy_node = NULL;
 
-       dev_set_drvdata(&op->dev, NULL);
-
        iounmap(lp->regs);
        if (lp->dma_regs)
                iounmap(lp->dma_regs);
index b7268b3..fd4dbda 100644 (file)
@@ -2,9 +2,9 @@
  * Xilinx EmacLite Linux driver for the Xilinx Ethernet MAC Lite device.
  *
  * This is a new flat driver which is based on the original emac_lite
- * driver from John Williams <john.williams@petalogix.com>.
+ * driver from John Williams <john.williams@xilinx.com>.
  *
- * 2007-2009 (c) Xilinx, Inc.
+ * 2007 - 2013 (c) Xilinx, 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
@@ -159,34 +159,32 @@ static void xemaclite_enable_interrupts(struct net_local *drvdata)
        u32 reg_data;
 
        /* Enable the Tx interrupts for the first Buffer */
-       reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET);
-       out_be32(drvdata->base_addr + XEL_TSR_OFFSET,
-                reg_data | XEL_TSR_XMIT_IE_MASK);
+       reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET);
+       __raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK,
+                    drvdata->base_addr + XEL_TSR_OFFSET);
 
        /* Enable the Tx interrupts for the second Buffer if
         * configured in HW */
        if (drvdata->tx_ping_pong != 0) {
-               reg_data = in_be32(drvdata->base_addr +
+               reg_data = __raw_readl(drvdata->base_addr +
                                   XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
-               out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
-                        XEL_TSR_OFFSET,
-                        reg_data | XEL_TSR_XMIT_IE_MASK);
+               __raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK,
+                            drvdata->base_addr + XEL_BUFFER_OFFSET +
+                            XEL_TSR_OFFSET);
        }
 
        /* Enable the Rx interrupts for the first buffer */
-       out_be32(drvdata->base_addr + XEL_RSR_OFFSET,
-                XEL_RSR_RECV_IE_MASK);
+       __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET);
 
        /* Enable the Rx interrupts for the second Buffer if
         * configured in HW */
        if (drvdata->rx_ping_pong != 0) {
-               out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
-                        XEL_RSR_OFFSET,
-                        XEL_RSR_RECV_IE_MASK);
+               __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr +
+                            XEL_BUFFER_OFFSET + XEL_RSR_OFFSET);
        }
 
        /* Enable the Global Interrupt Enable */
-       out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK);
+       __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
 }
 
 /**
@@ -201,37 +199,37 @@ static void xemaclite_disable_interrupts(struct net_local *drvdata)
        u32 reg_data;
 
        /* Disable the Global Interrupt Enable */
-       out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK);
+       __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
 
        /* Disable the Tx interrupts for the first buffer */
-       reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET);
-       out_be32(drvdata->base_addr + XEL_TSR_OFFSET,
-                reg_data & (~XEL_TSR_XMIT_IE_MASK));
+       reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET);
+       __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK),
+                    drvdata->base_addr + XEL_TSR_OFFSET);
 
        /* Disable the Tx interrupts for the second Buffer
         * if configured in HW */
        if (drvdata->tx_ping_pong != 0) {
-               reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+               reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET +
                                   XEL_TSR_OFFSET);
-               out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
-                        XEL_TSR_OFFSET,
-                        reg_data & (~XEL_TSR_XMIT_IE_MASK));
+               __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK),
+                            drvdata->base_addr + XEL_BUFFER_OFFSET +
+                            XEL_TSR_OFFSET);
        }
 
        /* Disable the Rx interrupts for the first buffer */
-       reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET);
-       out_be32(drvdata->base_addr + XEL_RSR_OFFSET,
-                reg_data & (~XEL_RSR_RECV_IE_MASK));
+       reg_data = __raw_readl(drvdata->base_addr + XEL_RSR_OFFSET);
+       __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK),
+                    drvdata->base_addr + XEL_RSR_OFFSET);
 
        /* Disable the Rx interrupts for the second buffer
         * if configured in HW */
        if (drvdata->rx_ping_pong != 0) {
 
-               reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
+               reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET +
                                   XEL_RSR_OFFSET);
-               out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET +
-                        XEL_RSR_OFFSET,
-                        reg_data & (~XEL_RSR_RECV_IE_MASK));
+               __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK),
+                            drvdata->base_addr + XEL_BUFFER_OFFSET +
+                            XEL_RSR_OFFSET);
        }
 }
 
@@ -351,7 +349,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
                byte_count = ETH_FRAME_LEN;
 
        /* Check if the expected buffer is available */
-       reg_data = in_be32(addr + XEL_TSR_OFFSET);
+       reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
        if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
             XEL_TSR_XMIT_ACTIVE_MASK)) == 0) {
 
@@ -364,7 +362,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
 
                addr = (void __iomem __force *)((u32 __force)addr ^
                                                 XEL_BUFFER_OFFSET);
-               reg_data = in_be32(addr + XEL_TSR_OFFSET);
+               reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
 
                if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
                     XEL_TSR_XMIT_ACTIVE_MASK)) != 0)
@@ -375,15 +373,16 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
        /* Write the frame to the buffer */
        xemaclite_aligned_write(data, (u32 __force *) addr, byte_count);
 
-       out_be32(addr + XEL_TPLR_OFFSET, (byte_count & XEL_TPLR_LENGTH_MASK));
+       __raw_writel((byte_count & XEL_TPLR_LENGTH_MASK),
+                    addr + XEL_TPLR_OFFSET);
 
        /* Update the Tx Status Register to indicate that there is a
         * frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which
         * is used by the interrupt handler to check whether a frame
         * has been transmitted */
-       reg_data = in_be32(addr + XEL_TSR_OFFSET);
+       reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
        reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK);
-       out_be32(addr + XEL_TSR_OFFSET, reg_data);
+       __raw_writel(reg_data, addr + XEL_TSR_OFFSET);
 
        return 0;
 }
@@ -408,7 +407,7 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
        addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use);
 
        /* Verify which buffer has valid data */
-       reg_data = in_be32(addr + XEL_RSR_OFFSET);
+       reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
 
        if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) {
                if (drvdata->rx_ping_pong != 0)
@@ -425,14 +424,14 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
                        return 0;       /* No data was available */
 
                /* Verify that buffer has valid data */
-               reg_data = in_be32(addr + XEL_RSR_OFFSET);
+               reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
                if ((reg_data & XEL_RSR_RECV_DONE_MASK) !=
                     XEL_RSR_RECV_DONE_MASK)
                        return 0;       /* No data was available */
        }
 
        /* Get the protocol type of the ethernet frame that arrived */
-       proto_type = ((ntohl(in_be32(addr + XEL_HEADER_OFFSET +
+       proto_type = ((ntohl(__raw_readl(addr + XEL_HEADER_OFFSET +
                        XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) &
                        XEL_RPLR_LENGTH_MASK);
 
@@ -441,7 +440,7 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
        if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) {
 
                if (proto_type == ETH_P_IP) {
-                       length = ((ntohl(in_be32(addr +
+                       length = ((ntohl(__raw_readl(addr +
                                        XEL_HEADER_IP_LENGTH_OFFSET +
                                        XEL_RXBUFF_OFFSET)) >>
                                        XEL_HEADER_SHIFT) &
@@ -463,9 +462,9 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
                                data, length);
 
        /* Acknowledge the frame */
-       reg_data = in_be32(addr + XEL_RSR_OFFSET);
+       reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
        reg_data &= ~XEL_RSR_RECV_DONE_MASK;
-       out_be32(addr + XEL_RSR_OFFSET, reg_data);
+       __raw_writel(reg_data, addr + XEL_RSR_OFFSET);
 
        return length;
 }
@@ -492,14 +491,14 @@ static void xemaclite_update_address(struct net_local *drvdata,
 
        xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN);
 
-       out_be32(addr + XEL_TPLR_OFFSET, ETH_ALEN);
+       __raw_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET);
 
        /* Update the MAC address in the EmacLite */
-       reg_data = in_be32(addr + XEL_TSR_OFFSET);
-       out_be32(addr + XEL_TSR_OFFSET, reg_data | XEL_TSR_PROG_MAC_ADDR);
+       reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
+       __raw_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET);
 
        /* Wait for EmacLite to finish with the MAC address update */
-       while ((in_be32(addr + XEL_TSR_OFFSET) &
+       while ((__raw_readl(addr + XEL_TSR_OFFSET) &
                XEL_TSR_PROG_MAC_ADDR) != 0)
                ;
 }
@@ -669,31 +668,32 @@ static irqreturn_t xemaclite_interrupt(int irq, void *dev_id)
        u32 tx_status;
 
        /* Check if there is Rx Data available */
-       if ((in_be32(base_addr + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK) ||
-                       (in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
+       if ((__raw_readl(base_addr + XEL_RSR_OFFSET) &
+                        XEL_RSR_RECV_DONE_MASK) ||
+           (__raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
                         & XEL_RSR_RECV_DONE_MASK))
 
                xemaclite_rx_handler(dev);
 
        /* Check if the Transmission for the first buffer is completed */
-       tx_status = in_be32(base_addr + XEL_TSR_OFFSET);
+       tx_status = __raw_readl(base_addr + XEL_TSR_OFFSET);
        if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
                (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
 
                tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
-               out_be32(base_addr + XEL_TSR_OFFSET, tx_status);
+               __raw_writel(tx_status, base_addr + XEL_TSR_OFFSET);
 
                tx_complete = true;
        }
 
        /* Check if the Transmission for the second buffer is completed */
-       tx_status = in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
+       tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
        if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
                (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
 
                tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
-               out_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET,
-                        tx_status);
+               __raw_writel(tx_status, base_addr + XEL_BUFFER_OFFSET +
+                            XEL_TSR_OFFSET);
 
                tx_complete = true;
        }
@@ -726,7 +726,7 @@ static int xemaclite_mdio_wait(struct net_local *lp)
        /* wait for the MDIO interface to not be busy or timeout
           after some time.
        */
-       while (in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
+       while (__raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
                        XEL_MDIOCTRL_MDIOSTS_MASK) {
                if (end - jiffies <= 0) {
                        WARN_ON(1);
@@ -762,17 +762,17 @@ static int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg)
         * MDIO Address register. Set the Status bit in the MDIO Control
         * register to start a MDIO read transaction.
         */
-       ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET);
-       out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET,
-                XEL_MDIOADDR_OP_MASK |
-                ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg));
-       out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET,
-                ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK);
+       ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
+       __raw_writel(XEL_MDIOADDR_OP_MASK |
+                    ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
+                    lp->base_addr + XEL_MDIOADDR_OFFSET);
+       __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
+                    lp->base_addr + XEL_MDIOCTRL_OFFSET);
 
        if (xemaclite_mdio_wait(lp))
                return -ETIMEDOUT;
 
-       rc = in_be32(lp->base_addr + XEL_MDIORD_OFFSET);
+       rc = __raw_readl(lp->base_addr + XEL_MDIORD_OFFSET);
 
        dev_dbg(&lp->ndev->dev,
                "xemaclite_mdio_read(phy_id=%i, reg=%x) == %x\n",
@@ -809,13 +809,13 @@ static int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg,
         * Data register. Finally, set the Status bit in the MDIO Control
         * register to start a MDIO write transaction.
         */
-       ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET);
-       out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET,
-                ~XEL_MDIOADDR_OP_MASK &
-                ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg));
-       out_be32(lp->base_addr + XEL_MDIOWR_OFFSET, val);
-       out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET,
-                ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK);
+       ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
+       __raw_writel(~XEL_MDIOADDR_OP_MASK &
+                    ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
+                    lp->base_addr + XEL_MDIOADDR_OFFSET);
+       __raw_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET);
+       __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
+                    lp->base_addr + XEL_MDIOCTRL_OFFSET);
 
        return 0;
 }
@@ -848,24 +848,39 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev)
        int rc;
        struct resource res;
        struct device_node *np = of_get_parent(lp->phy_node);
+       struct device_node *npp;
 
        /* Don't register the MDIO bus if the phy_node or its parent node
         * can't be found.
         */
-       if (!np)
+       if (!np) {
+               dev_err(dev, "Failed to register mdio bus.\n");
                return -ENODEV;
+       }
+       npp = of_get_parent(np);
+
+       of_address_to_resource(npp, 0, &res);
+       if (lp->ndev->mem_start != res.start) {
+               struct phy_device *phydev;
+               phydev = of_phy_find_device(lp->phy_node);
+               if (!phydev)
+                       dev_info(dev,
+                                "MDIO of the phy is not registered yet\n");
+               return 0;
+       }
 
        /* Enable the MDIO bus by asserting the enable bit in MDIO Control
         * register.
         */
-       out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET,
-                XEL_MDIOCTRL_MDIOEN_MASK);
+       __raw_writel(XEL_MDIOCTRL_MDIOEN_MASK,
+                    lp->base_addr + XEL_MDIOCTRL_OFFSET);
 
        bus = mdiobus_alloc();
-       if (!bus)
+       if (!bus) {
+               dev_err(dev, "Failed to allocate mdiobus\n");
                return -ENOMEM;
+       }
 
-       of_address_to_resource(np, 0, &res);
        snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
                 (unsigned long long)res.start);
        bus->priv = lp;
@@ -879,8 +894,10 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev)
        lp->mii_bus = bus;
 
        rc = of_mdiobus_register(bus, np);
-       if (rc)
+       if (rc) {
+               dev_err(dev, "Failed to register mdio bus.\n");
                goto err_register;
+       }
 
        return 0;
 
@@ -896,7 +913,7 @@ err_register:
  * There's nothing in the Emaclite device to be configured when the link
  * state changes. We just print the status.
  */
-void xemaclite_adjust_link(struct net_device *ndev)
+static void xemaclite_adjust_link(struct net_device *ndev)
 {
        struct net_local *lp = netdev_priv(ndev);
        struct phy_device *phy = lp->phy_dev;
@@ -1058,13 +1075,14 @@ static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev)
  * This function un maps the IO region of the Emaclite device and frees the net
  * device.
  */
-static void xemaclite_remove_ndev(struct net_device *ndev)
+static void xemaclite_remove_ndev(struct net_device *ndev,
+                                 struct platform_device *pdev)
 {
        if (ndev) {
                struct net_local *lp = netdev_priv(ndev);
 
                if (lp->base_addr)
-                       iounmap((void __iomem __force *) (lp->base_addr));
+                       devm_iounmap(&pdev->dev, lp->base_addr);
                free_netdev(ndev);
        }
 }
@@ -1110,8 +1128,7 @@ static struct net_device_ops xemaclite_netdev_ops;
  */
 static int xemaclite_of_probe(struct platform_device *ofdev)
 {
-       struct resource r_irq; /* Interrupt resources */
-       struct resource r_mem; /* IO mem resources */
+       struct resource *res;
        struct net_device *ndev = NULL;
        struct net_local *lp = NULL;
        struct device *dev = &ofdev->dev;
@@ -1121,20 +1138,6 @@ static int xemaclite_of_probe(struct platform_device *ofdev)
 
        dev_info(dev, "Device Tree Probing\n");
 
-       /* Get iospace for the device */
-       rc = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem);
-       if (rc) {
-               dev_err(dev, "invalid address\n");
-               return rc;
-       }
-
-       /* Get IRQ for the device */
-       rc = of_irq_to_resource(ofdev->dev.of_node, 0, &r_irq);
-       if (!rc) {
-               dev_err(dev, "no IRQ found\n");
-               return rc;
-       }
-
        /* Create an ethernet device instance */
        ndev = alloc_etherdev(sizeof(struct net_local));
        if (!ndev)
@@ -1143,30 +1146,28 @@ static int xemaclite_of_probe(struct platform_device *ofdev)
        dev_set_drvdata(dev, ndev);
        SET_NETDEV_DEV(ndev, &ofdev->dev);
 
-       ndev->irq = r_irq.start;
-       ndev->mem_start = r_mem.start;
-       ndev->mem_end = r_mem.end;
-
        lp = netdev_priv(ndev);
        lp->ndev = ndev;
 
-       if (!request_mem_region(ndev->mem_start,
-                               ndev->mem_end - ndev->mem_start + 1,
-                               DRIVER_NAME)) {
-               dev_err(dev, "Couldn't lock memory region at %p\n",
-                       (void *)ndev->mem_start);
-               rc = -EBUSY;
-               goto error2;
+       /* Get IRQ for the device */
+       res = platform_get_resource(ofdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               dev_err(dev, "no IRQ found\n");
+               goto error;
        }
 
-       /* Get the virtual base address for the device */
-       lp->base_addr = ioremap(r_mem.start, resource_size(&r_mem));
-       if (NULL == lp->base_addr) {
-               dev_err(dev, "EmacLite: Could not allocate iomem\n");
-               rc = -EIO;
-               goto error1;
+       ndev->irq = res->start;
+
+       res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
+       lp->base_addr = devm_ioremap_resource(&ofdev->dev, res);
+       if (IS_ERR(lp->base_addr)) {
+               rc = PTR_ERR(lp->base_addr);
+               goto error;
        }
 
+       ndev->mem_start = res->start;
+       ndev->mem_end = res->end;
+
        spin_lock_init(&lp->reset_lock);
        lp->next_tx_buf_to_use = 0x0;
        lp->next_rx_buf_to_use = 0x0;
@@ -1181,8 +1182,8 @@ static int xemaclite_of_probe(struct platform_device *ofdev)
                dev_warn(dev, "No MAC address found\n");
 
        /* Clear the Tx CSR's in case this is a restart */
-       out_be32(lp->base_addr + XEL_TSR_OFFSET, 0);
-       out_be32(lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, 0);
+       __raw_writel(0, lp->base_addr + XEL_TSR_OFFSET);
+       __raw_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
 
        /* Set the MAC address in the EmacLite device */
        xemaclite_update_address(lp, ndev->dev_addr);
@@ -1203,7 +1204,7 @@ static int xemaclite_of_probe(struct platform_device *ofdev)
        if (rc) {
                dev_err(dev,
                        "Cannot register network device, aborting\n");
-               goto error1;
+               goto error;
        }
 
        dev_info(dev,
@@ -1212,11 +1213,8 @@ static int xemaclite_of_probe(struct platform_device *ofdev)
                 (unsigned int __force)lp->base_addr, ndev->irq);
        return 0;
 
-error1:
-       release_mem_region(ndev->mem_start, resource_size(&r_mem));
-
-error2:
-       xemaclite_remove_ndev(ndev);
+error:
+       xemaclite_remove_ndev(ndev, ofdev);
        return rc;
 }
 
@@ -1251,9 +1249,7 @@ static int xemaclite_of_remove(struct platform_device *of_dev)
                of_node_put(lp->phy_node);
        lp->phy_node = NULL;
 
-       release_mem_region(ndev->mem_start, ndev->mem_end-ndev->mem_start + 1);
-
-       xemaclite_remove_ndev(ndev);
+       xemaclite_remove_ndev(ndev, of_dev);
        dev_set_drvdata(dev, NULL);
 
        return 0;
index 6958a5e..3d689fc 100644 (file)
@@ -1472,7 +1472,6 @@ err_phy_dis:
        phy_disconnect(port->phydev);
 err_free_mem:
        npe_port_tab[NPE_ID(port->id)] = NULL;
-       platform_set_drvdata(pdev, NULL);
        release_resource(port->mem_res);
 err_npe_rel:
        npe_release(port->npe);
@@ -1489,7 +1488,6 @@ static int eth_remove_one(struct platform_device *pdev)
        unregister_netdev(dev);
        phy_disconnect(port->phydev);
        npe_port_tab[NPE_ID(port->id)] = NULL;
-       platform_set_drvdata(pdev, NULL);
        npe_release(port->npe);
        release_resource(port->mem_res);
        free_netdev(dev);
index d5bd563..f5d7305 100644 (file)
@@ -2246,15 +2246,4 @@ static struct pci_driver skfddi_pci_driver = {
        .remove         = skfp_remove_one,
 };
 
-static int __init skfd_init(void)
-{
-       return pci_register_driver(&skfddi_pci_driver);
-}
-
-static void __exit skfd_exit(void)
-{
-       pci_unregister_driver(&skfddi_pci_driver);
-}
-
-module_init(skfd_init);
-module_exit(skfd_exit);
+module_pci_driver(skfddi_pci_driver);
index 02de6c8..f91bf0d 100644 (file)
@@ -103,7 +103,7 @@ static struct packet_type bpq_packet_type __read_mostly = {
 };
 
 static struct notifier_block bpq_dev_notifier = {
-       .notifier_call =bpq_device_event,
+       .notifier_call = bpq_device_event,
 };
 
 
@@ -544,9 +544,10 @@ static void bpq_free_device(struct net_device *ndev)
 /*
  *     Handle device status changes.
  */
-static int bpq_device_event(struct notifier_block *this,unsigned long event, void *ptr)
+static int bpq_device_event(struct notifier_block *this,
+                           unsigned long event, void *ptr)
 {
-       struct net_device *dev = (struct net_device *)ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
index 3c4d627..00ed751 100644 (file)
@@ -1686,15 +1686,4 @@ static struct pci_driver rr_driver = {
        .remove         = rr_remove_one,
 };
 
-static int __init rr_init_module(void)
-{
-       return pci_register_driver(&rr_driver);
-}
-
-static void __exit rr_cleanup_module(void)
-{
-       pci_unregister_driver(&rr_driver);
-}
-
-module_init(rr_init_module);
-module_exit(rr_cleanup_module);
+module_pci_driver(rr_driver);
index 22b4527..c74f384 100644 (file)
@@ -794,7 +794,6 @@ static int bfin_sir_remove(struct platform_device *pdev)
        kfree(self->rx_buff.head);
        free_netdev(dev);
        kfree(sir_port);
-       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
index 9448587..4455425 100644 (file)
@@ -838,7 +838,6 @@ static int sh_irda_remove(struct platform_device *pdev)
        sh_irda_remove_iobuf(self);
        iounmap(self->membase);
        free_netdev(ndev);
-       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
index 24aefcd..89682b4 100644 (file)
@@ -796,7 +796,6 @@ static int sh_sir_remove(struct platform_device *pdev)
        sh_sir_remove_iobuf(self);
        iounmap(self->membase);
        free_netdev(ndev);
-       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
index 6e91931..18373b6 100644 (file)
@@ -638,6 +638,14 @@ static int macvlan_ethtool_get_settings(struct net_device *dev,
        return __ethtool_get_settings(vlan->lowerdev, cmd);
 }
 
+static netdev_features_t macvlan_fix_features(struct net_device *dev,
+                                             netdev_features_t features)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+
+       return features & (vlan->set_features | ~MACVLAN_FEATURES);
+}
+
 static const struct ethtool_ops macvlan_ethtool_ops = {
        .get_link               = ethtool_op_get_link,
        .get_settings           = macvlan_ethtool_get_settings,
@@ -651,6 +659,7 @@ static const struct net_device_ops macvlan_netdev_ops = {
        .ndo_stop               = macvlan_stop,
        .ndo_start_xmit         = macvlan_start_xmit,
        .ndo_change_mtu         = macvlan_change_mtu,
+       .ndo_fix_features       = macvlan_fix_features,
        .ndo_change_rx_flags    = macvlan_change_rx_flags,
        .ndo_set_mac_address    = macvlan_set_mac_address,
        .ndo_set_rx_mode        = macvlan_set_mac_lists,
@@ -791,6 +800,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
        vlan->port     = port;
        vlan->receive  = receive;
        vlan->forward  = forward;
+       vlan->set_features = MACVLAN_FEATURES;
 
        vlan->mode     = MACVLAN_MODE_VEPA;
        if (data && data[IFLA_MACVLAN_MODE])
@@ -927,7 +937,7 @@ static struct rtnl_link_ops macvlan_link_ops = {
 static int macvlan_device_event(struct notifier_block *unused,
                                unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct macvlan_dev *vlan, *next;
        struct macvlan_port *port;
        LIST_HEAD(list_kill);
index b6dd6a7..f2c4a3b 100644 (file)
  * macvtap_proto is used to allocate queues through the sock allocation
  * mechanism.
  *
- * TODO: multiqueue support is currently not implemented, even though
- * macvtap is basically prepared for that. We will need to add this
- * here as well as in virtio-net and qemu to get line rate on 10gbit
- * adapters from a guest.
  */
 struct macvtap_queue {
        struct sock sk;
@@ -44,6 +40,9 @@ struct macvtap_queue {
        struct macvlan_dev __rcu *vlan;
        struct file *file;
        unsigned int flags;
+       u16 queue_index;
+       bool enabled;
+       struct list_head next;
 };
 
 static struct proto macvtap_proto = {
@@ -66,11 +65,14 @@ static struct cdev macvtap_cdev;
 
 static const struct proto_ops macvtap_socket_ops;
 
+#define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \
+                     NETIF_F_TSO6 | NETIF_F_UFO)
+#define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO)
 /*
  * RCU usage:
  * The macvtap_queue and the macvlan_dev are loosely coupled, the
  * pointers from one to the other can only be read while rcu_read_lock
- * or macvtap_lock is held.
+ * or rtnl is held.
  *
  * Both the file and the macvlan_dev hold a reference on the macvtap_queue
  * through sock_hold(&q->sk). When the macvlan_dev goes away first,
@@ -82,54 +84,84 @@ static const struct proto_ops macvtap_socket_ops;
  * file or the dev. The data structure is freed through __sk_free
  * when both our references and any pending SKBs are gone.
  */
-static DEFINE_SPINLOCK(macvtap_lock);
 
-/*
- * get_slot: return a [unused/occupied] slot in vlan->taps[]:
- *     - if 'q' is NULL, return the first empty slot;
- *     - otherwise, return the slot this pointer occupies.
- */
-static int get_slot(struct macvlan_dev *vlan, struct macvtap_queue *q)
+static int macvtap_enable_queue(struct net_device *dev, struct file *file,
+                               struct macvtap_queue *q)
 {
-       int i;
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       int err = -EINVAL;
 
-       for (i = 0; i < MAX_MACVTAP_QUEUES; i++) {
-               if (rcu_dereference_protected(vlan->taps[i],
-                                             lockdep_is_held(&macvtap_lock)) == q)
-                       return i;
-       }
+       ASSERT_RTNL();
+
+       if (q->enabled)
+               goto out;
 
-       /* Should never happen */
-       BUG_ON(1);
+       err = 0;
+       rcu_assign_pointer(vlan->taps[vlan->numvtaps], q);
+       q->queue_index = vlan->numvtaps;
+       q->enabled = true;
+
+       vlan->numvtaps++;
+out:
+       return err;
 }
 
 static int macvtap_set_queue(struct net_device *dev, struct file *file,
-                               struct macvtap_queue *q)
+                            struct macvtap_queue *q)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
-       int index;
        int err = -EBUSY;
 
-       spin_lock(&macvtap_lock);
-       if (vlan->numvtaps == MAX_MACVTAP_QUEUES)
+       rtnl_lock();
+       if (vlan->numqueues == MAX_MACVTAP_QUEUES)
                goto out;
 
        err = 0;
-       index = get_slot(vlan, NULL);
        rcu_assign_pointer(q->vlan, vlan);
-       rcu_assign_pointer(vlan->taps[index], q);
+       rcu_assign_pointer(vlan->taps[vlan->numvtaps], q);
        sock_hold(&q->sk);
 
        q->file = file;
+       q->queue_index = vlan->numvtaps;
+       q->enabled = true;
        file->private_data = q;
+       list_add_tail(&q->next, &vlan->queue_list);
 
        vlan->numvtaps++;
+       vlan->numqueues++;
 
 out:
-       spin_unlock(&macvtap_lock);
+       rtnl_unlock();
        return err;
 }
 
+static int macvtap_disable_queue(struct macvtap_queue *q)
+{
+       struct macvlan_dev *vlan;
+       struct macvtap_queue *nq;
+
+       ASSERT_RTNL();
+       if (!q->enabled)
+               return -EINVAL;
+
+       vlan = rtnl_dereference(q->vlan);
+
+       if (vlan) {
+               int index = q->queue_index;
+               BUG_ON(index >= vlan->numvtaps);
+               nq = rtnl_dereference(vlan->taps[vlan->numvtaps - 1]);
+               nq->queue_index = index;
+
+               rcu_assign_pointer(vlan->taps[index], nq);
+               RCU_INIT_POINTER(vlan->taps[vlan->numvtaps - 1], NULL);
+               q->enabled = false;
+
+               vlan->numvtaps--;
+       }
+
+       return 0;
+}
+
 /*
  * The file owning the queue got closed, give up both
  * the reference that the files holds as well as the
@@ -142,19 +174,20 @@ static void macvtap_put_queue(struct macvtap_queue *q)
 {
        struct macvlan_dev *vlan;
 
-       spin_lock(&macvtap_lock);
-       vlan = rcu_dereference_protected(q->vlan,
-                                        lockdep_is_held(&macvtap_lock));
+       rtnl_lock();
+       vlan = rtnl_dereference(q->vlan);
+
        if (vlan) {
-               int index = get_slot(vlan, q);
+               if (q->enabled)
+                       BUG_ON(macvtap_disable_queue(q));
 
-               RCU_INIT_POINTER(vlan->taps[index], NULL);
+               vlan->numqueues--;
                RCU_INIT_POINTER(q->vlan, NULL);
                sock_put(&q->sk);
-               --vlan->numvtaps;
+               list_del_init(&q->next);
        }
 
-       spin_unlock(&macvtap_lock);
+       rtnl_unlock();
 
        synchronize_rcu();
        sock_put(&q->sk);
@@ -172,7 +205,12 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev,
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
        struct macvtap_queue *tap = NULL;
-       int numvtaps = vlan->numvtaps;
+       /* Access to taps array is protected by rcu, but access to numvtaps
+        * isn't. Below we use it to lookup a queue, but treat it as a hint
+        * and validate that the result isn't NULL - in case we are
+        * racing against queue removal.
+        */
+       int numvtaps = ACCESS_ONCE(vlan->numvtaps);
        __u32 rxq;
 
        if (!numvtaps)
@@ -182,8 +220,7 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev,
        rxq = skb_get_rxhash(skb);
        if (rxq) {
                tap = rcu_dereference(vlan->taps[rxq % numvtaps]);
-               if (tap)
-                       goto out;
+               goto out;
        }
 
        if (likely(skb_rx_queue_recorded(skb))) {
@@ -193,17 +230,10 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev,
                        rxq -= numvtaps;
 
                tap = rcu_dereference(vlan->taps[rxq]);
-               if (tap)
-                       goto out;
-       }
-
-       /* Everything failed - find first available queue */
-       for (rxq = 0; rxq < MAX_MACVTAP_QUEUES; rxq++) {
-               tap = rcu_dereference(vlan->taps[rxq]);
-               if (tap)
-                       break;
+               goto out;
        }
 
+       tap = rcu_dereference(vlan->taps[0]);
 out:
        return tap;
 }
@@ -216,27 +246,24 @@ out:
 static void macvtap_del_queues(struct net_device *dev)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
-       struct macvtap_queue *q, *qlist[MAX_MACVTAP_QUEUES];
+       struct macvtap_queue *q, *tmp, *qlist[MAX_MACVTAP_QUEUES];
        int i, j = 0;
 
-       /* macvtap_put_queue can free some slots, so go through all slots */
-       spin_lock(&macvtap_lock);
-       for (i = 0; i < MAX_MACVTAP_QUEUES && vlan->numvtaps; i++) {
-               q = rcu_dereference_protected(vlan->taps[i],
-                                             lockdep_is_held(&macvtap_lock));
-               if (q) {
-                       qlist[j++] = q;
-                       RCU_INIT_POINTER(vlan->taps[i], NULL);
-                       RCU_INIT_POINTER(q->vlan, NULL);
+       ASSERT_RTNL();
+       list_for_each_entry_safe(q, tmp, &vlan->queue_list, next) {
+               list_del_init(&q->next);
+               qlist[j++] = q;
+               RCU_INIT_POINTER(q->vlan, NULL);
+               if (q->enabled)
                        vlan->numvtaps--;
-               }
+               vlan->numqueues--;
        }
-       BUG_ON(vlan->numvtaps != 0);
+       for (i = 0; i < vlan->numvtaps; i++)
+               RCU_INIT_POINTER(vlan->taps[i], NULL);
+       BUG_ON(vlan->numvtaps);
+       BUG_ON(vlan->numqueues);
        /* guarantee that any future macvtap_set_queue will fail */
        vlan->numvtaps = MAX_MACVTAP_QUEUES;
-       spin_unlock(&macvtap_lock);
-
-       synchronize_rcu();
 
        for (--j; j >= 0; j--)
                sock_put(&qlist[j]->sk);
@@ -249,14 +276,44 @@ static void macvtap_del_queues(struct net_device *dev)
  */
 static int macvtap_forward(struct net_device *dev, struct sk_buff *skb)
 {
+       struct macvlan_dev *vlan = netdev_priv(dev);
        struct macvtap_queue *q = macvtap_get_queue(dev, skb);
+       netdev_features_t features;
        if (!q)
                goto drop;
 
        if (skb_queue_len(&q->sk.sk_receive_queue) >= dev->tx_queue_len)
                goto drop;
 
-       skb_queue_tail(&q->sk.sk_receive_queue, skb);
+       skb->dev = dev;
+       /* Apply the forward feature mask so that we perform segmentation
+        * according to users wishes.
+        */
+       features = netif_skb_features(skb) & vlan->tap_features;
+       if (netif_needs_gso(skb, features)) {
+               struct sk_buff *segs = __skb_gso_segment(skb, features, false);
+
+               if (IS_ERR(segs))
+                       goto drop;
+
+               if (!segs) {
+                       skb_queue_tail(&q->sk.sk_receive_queue, skb);
+                       goto wake_up;
+               }
+
+               kfree_skb(skb);
+               while (segs) {
+                       struct sk_buff *nskb = segs->next;
+
+                       segs->next = NULL;
+                       skb_queue_tail(&q->sk.sk_receive_queue, segs);
+                       segs = nskb;
+               }
+       } else {
+               skb_queue_tail(&q->sk.sk_receive_queue, skb);
+       }
+
+wake_up:
        wake_up_interruptible_poll(sk_sleep(&q->sk), POLLIN | POLLRDNORM | POLLRDBAND);
        return NET_RX_SUCCESS;
 
@@ -322,6 +379,14 @@ static int macvtap_newlink(struct net *src_net,
                           struct nlattr *tb[],
                           struct nlattr *data[])
 {
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       INIT_LIST_HEAD(&vlan->queue_list);
+
+       /* Since macvlan supports all offloads by default, make
+        * tap support all offloads also.
+        */
+       vlan->tap_features = TUN_OFFLOADS;
+
        /* Don't put anything that may fail after macvlan_common_newlink
         * because we can't undo what it does.
         */
@@ -385,7 +450,7 @@ static int macvtap_open(struct inode *inode, struct file *file)
        if (!q)
                goto out;
 
-       q->sock.wq = &q->wq;
+       RCU_INIT_POINTER(q->sock.wq, &q->wq);
        init_waitqueue_head(&q->wq.wait);
        q->sock.type = SOCK_RAW;
        q->sock.state = SS_CONNECTED;
@@ -729,8 +794,8 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
 
        skb_probe_transport_header(skb, ETH_HLEN);
 
-       rcu_read_lock_bh();
-       vlan = rcu_dereference_bh(q->vlan);
+       rcu_read_lock();
+       vlan = rcu_dereference(q->vlan);
        /* copy skb_ubuf_info for callback when skb has no error */
        if (zerocopy) {
                skb_shinfo(skb)->destructor_arg = m->msg_control;
@@ -741,7 +806,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
                macvlan_start_xmit(skb, vlan->dev);
        else
                kfree_skb(skb);
-       rcu_read_unlock_bh();
+       rcu_read_unlock();
 
        return total_len;
 
@@ -749,11 +814,11 @@ err_kfree:
        kfree_skb(skb);
 
 err:
-       rcu_read_lock_bh();
-       vlan = rcu_dereference_bh(q->vlan);
+       rcu_read_lock();
+       vlan = rcu_dereference(q->vlan);
        if (vlan)
                vlan->dev->stats.tx_dropped++;
-       rcu_read_unlock_bh();
+       rcu_read_unlock();
 
        return err;
 }
@@ -829,11 +894,11 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q,
        copied += len;
 
 done:
-       rcu_read_lock_bh();
-       vlan = rcu_dereference_bh(q->vlan);
+       rcu_read_lock();
+       vlan = rcu_dereference(q->vlan);
        if (vlan)
                macvlan_count_rx(vlan, copied - vnet_hdr_len, ret == 0, 0);
-       rcu_read_unlock_bh();
+       rcu_read_unlock();
 
        return ret ? ret : copied;
 }
@@ -847,7 +912,9 @@ static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb,
        ssize_t ret = 0;
 
        while (len) {
-               prepare_to_wait(sk_sleep(&q->sk), &wait, TASK_INTERRUPTIBLE);
+               if (!noblock)
+                       prepare_to_wait(sk_sleep(&q->sk), &wait,
+                                       TASK_INTERRUPTIBLE);
 
                /* Read frames from the queue */
                skb = skb_dequeue(&q->sk.sk_receive_queue);
@@ -869,7 +936,8 @@ static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb,
                break;
        }
 
-       finish_wait(sk_sleep(&q->sk), &wait);
+       if (!noblock)
+               finish_wait(sk_sleep(&q->sk), &wait);
        return ret;
 }
 
@@ -892,6 +960,96 @@ out:
        return ret;
 }
 
+static struct macvlan_dev *macvtap_get_vlan(struct macvtap_queue *q)
+{
+       struct macvlan_dev *vlan;
+
+       ASSERT_RTNL();
+       vlan = rtnl_dereference(q->vlan);
+       if (vlan)
+               dev_hold(vlan->dev);
+
+       return vlan;
+}
+
+static void macvtap_put_vlan(struct macvlan_dev *vlan)
+{
+       dev_put(vlan->dev);
+}
+
+static int macvtap_ioctl_set_queue(struct file *file, unsigned int flags)
+{
+       struct macvtap_queue *q = file->private_data;
+       struct macvlan_dev *vlan;
+       int ret;
+
+       vlan = macvtap_get_vlan(q);
+       if (!vlan)
+               return -EINVAL;
+
+       if (flags & IFF_ATTACH_QUEUE)
+               ret = macvtap_enable_queue(vlan->dev, file, q);
+       else if (flags & IFF_DETACH_QUEUE)
+               ret = macvtap_disable_queue(q);
+       else
+               ret = -EINVAL;
+
+       macvtap_put_vlan(vlan);
+       return ret;
+}
+
+static int set_offload(struct macvtap_queue *q, unsigned long arg)
+{
+       struct macvlan_dev *vlan;
+       netdev_features_t features;
+       netdev_features_t feature_mask = 0;
+
+       vlan = rtnl_dereference(q->vlan);
+       if (!vlan)
+               return -ENOLINK;
+
+       features = vlan->dev->features;
+
+       if (arg & TUN_F_CSUM) {
+               feature_mask = NETIF_F_HW_CSUM;
+
+               if (arg & (TUN_F_TSO4 | TUN_F_TSO6)) {
+                       if (arg & TUN_F_TSO_ECN)
+                               feature_mask |= NETIF_F_TSO_ECN;
+                       if (arg & TUN_F_TSO4)
+                               feature_mask |= NETIF_F_TSO;
+                       if (arg & TUN_F_TSO6)
+                               feature_mask |= NETIF_F_TSO6;
+               }
+
+               if (arg & TUN_F_UFO)
+                       feature_mask |= NETIF_F_UFO;
+       }
+
+       /* tun/tap driver inverts the usage for TSO offloads, where
+        * setting the TSO bit means that the userspace wants to
+        * accept TSO frames and turning it off means that user space
+        * does not support TSO.
+        * For macvtap, we have to invert it to mean the same thing.
+        * When user space turns off TSO, we turn off GSO/LRO so that
+        * user-space will not receive TSO frames.
+        */
+       if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO))
+               features |= RX_OFFLOADS;
+       else
+               features &= ~RX_OFFLOADS;
+
+       /* tap_features are the same as features on tun/tap and
+        * reflect user expectations.
+        */
+       vlan->tap_features = vlan->dev->features &
+                           (feature_mask | ~TUN_OFFLOADS);
+       vlan->set_features = features;
+       netdev_update_features(vlan->dev);
+
+       return 0;
+}
+
 /*
  * provide compatibility with generic tun/tap interface
  */
@@ -915,7 +1073,8 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
                        return -EFAULT;
 
                ret = 0;
-               if ((u & ~IFF_VNET_HDR) != (IFF_NO_PI | IFF_TAP))
+               if ((u & ~(IFF_VNET_HDR | IFF_MULTI_QUEUE)) !=
+                   (IFF_NO_PI | IFF_TAP))
                        ret = -EINVAL;
                else
                        q->flags = u;
@@ -923,24 +1082,31 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
                return ret;
 
        case TUNGETIFF:
-               rcu_read_lock_bh();
-               vlan = rcu_dereference_bh(q->vlan);
-               if (vlan)
-                       dev_hold(vlan->dev);
-               rcu_read_unlock_bh();
-
-               if (!vlan)
+               rtnl_lock();
+               vlan = macvtap_get_vlan(q);
+               if (!vlan) {
+                       rtnl_unlock();
                        return -ENOLINK;
+               }
 
                ret = 0;
                if (copy_to_user(&ifr->ifr_name, vlan->dev->name, IFNAMSIZ) ||
                    put_user(q->flags, &ifr->ifr_flags))
                        ret = -EFAULT;
-               dev_put(vlan->dev);
+               macvtap_put_vlan(vlan);
+               rtnl_unlock();
                return ret;
 
+       case TUNSETQUEUE:
+               if (get_user(u, &ifr->ifr_flags))
+                       return -EFAULT;
+               rtnl_lock();
+               ret = macvtap_ioctl_set_queue(file, u);
+               rtnl_unlock();
+
        case TUNGETFEATURES:
-               if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR, up))
+               if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR |
+                            IFF_MULTI_QUEUE, up))
                        return -EFAULT;
                return 0;
 
@@ -976,7 +1142,10 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
                         got enabled for forwarded frames */
                if (!(q->flags & IFF_VNET_HDR))
                        return  -EINVAL;
-               return 0;
+               rtnl_lock();
+               ret = set_offload(q, arg);
+               rtnl_unlock();
+               return ret;
 
        default:
                return -EINVAL;
@@ -1055,7 +1224,7 @@ EXPORT_SYMBOL_GPL(macvtap_get_socket);
 static int macvtap_device_event(struct notifier_block *unused,
                                unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct macvlan_dev *vlan;
        struct device *classdev;
        dev_t devt;
index 4f777ed..4822aaf 100644 (file)
@@ -654,12 +654,11 @@ static struct configfs_subsystem netconsole_subsys = {
 
 /* Handle network interface device notifications */
 static int netconsole_netdev_event(struct notifier_block *this,
-                                  unsigned long event,
-                                  void *ptr)
+                                  unsigned long event, void *ptr)
 {
        unsigned long flags;
        struct netconsole_target *nt;
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        bool stopped = false;
 
        if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER ||
diff --git a/drivers/net/nlmon.c b/drivers/net/nlmon.c
new file mode 100644 (file)
index 0000000..b57ce5f
--- /dev/null
@@ -0,0 +1,181 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <net/net_namespace.h>
+#include <linux/if_arp.h>
+#include <net/rtnetlink.h>
+
+struct pcpu_lstats {
+       u64 packets;
+       u64 bytes;
+       struct u64_stats_sync syncp;
+};
+
+static netdev_tx_t nlmon_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       int len = skb->len;
+       struct pcpu_lstats *stats = this_cpu_ptr(dev->lstats);
+
+       u64_stats_update_begin(&stats->syncp);
+       stats->bytes += len;
+       stats->packets++;
+       u64_stats_update_end(&stats->syncp);
+
+       dev_kfree_skb(skb);
+
+       return NETDEV_TX_OK;
+}
+
+static int nlmon_is_valid_mtu(int new_mtu)
+{
+       /* Note that in netlink we do not really have an upper limit. On
+        * default, we use NLMSG_GOODSIZE. Here at least we should make
+        * sure that it's at least the header size.
+        */
+       return new_mtu >= (int) sizeof(struct nlmsghdr);
+}
+
+static int nlmon_change_mtu(struct net_device *dev, int new_mtu)
+{
+       if (!nlmon_is_valid_mtu(new_mtu))
+               return -EINVAL;
+
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+static int nlmon_dev_init(struct net_device *dev)
+{
+       dev->lstats = alloc_percpu(struct pcpu_lstats);
+
+       return dev->lstats == NULL ? -ENOMEM : 0;
+}
+
+static void nlmon_dev_uninit(struct net_device *dev)
+{
+       free_percpu(dev->lstats);
+}
+
+struct nlmon {
+       struct netlink_tap nt;
+};
+
+static int nlmon_open(struct net_device *dev)
+{
+       struct nlmon *nlmon = netdev_priv(dev);
+
+       nlmon->nt.dev = dev;
+       nlmon->nt.module = THIS_MODULE;
+       return netlink_add_tap(&nlmon->nt);
+}
+
+static int nlmon_close(struct net_device *dev)
+{
+       struct nlmon *nlmon = netdev_priv(dev);
+
+       return netlink_remove_tap(&nlmon->nt);
+}
+
+static struct rtnl_link_stats64 *
+nlmon_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+       int i;
+       u64 bytes = 0, packets = 0;
+
+       for_each_possible_cpu(i) {
+               const struct pcpu_lstats *nl_stats;
+               u64 tbytes, tpackets;
+               unsigned int start;
+
+               nl_stats = per_cpu_ptr(dev->lstats, i);
+
+               do {
+                       start = u64_stats_fetch_begin_bh(&nl_stats->syncp);
+                       tbytes = nl_stats->bytes;
+                       tpackets = nl_stats->packets;
+               } while (u64_stats_fetch_retry_bh(&nl_stats->syncp, start));
+
+               packets += tpackets;
+               bytes += tbytes;
+       }
+
+       stats->rx_packets = packets;
+       stats->tx_packets = 0;
+
+       stats->rx_bytes = bytes;
+       stats->tx_bytes = 0;
+
+       return stats;
+}
+
+static u32 always_on(struct net_device *dev)
+{
+       return 1;
+}
+
+static const struct ethtool_ops nlmon_ethtool_ops = {
+       .get_link = always_on,
+};
+
+static const struct net_device_ops nlmon_ops = {
+       .ndo_init = nlmon_dev_init,
+       .ndo_uninit = nlmon_dev_uninit,
+       .ndo_open = nlmon_open,
+       .ndo_stop = nlmon_close,
+       .ndo_start_xmit = nlmon_xmit,
+       .ndo_get_stats64 = nlmon_get_stats64,
+       .ndo_change_mtu = nlmon_change_mtu,
+};
+
+static void nlmon_setup(struct net_device *dev)
+{
+       dev->type = ARPHRD_NETLINK;
+       dev->tx_queue_len = 0;
+
+       dev->netdev_ops = &nlmon_ops;
+       dev->ethtool_ops = &nlmon_ethtool_ops;
+       dev->destructor = free_netdev;
+
+       dev->features = NETIF_F_FRAGLIST | NETIF_F_HIGHDMA;
+       dev->flags = IFF_NOARP;
+
+       /* That's rather a softlimit here, which, of course,
+        * can be altered. Not a real MTU, but what is to be
+        * expected in most cases.
+        */
+       dev->mtu = NLMSG_GOODSIZE;
+}
+
+static int nlmon_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+       if (tb[IFLA_ADDRESS])
+               return -EINVAL;
+       return 0;
+}
+
+static struct rtnl_link_ops nlmon_link_ops __read_mostly = {
+       .kind                   = "nlmon",
+       .priv_size              = sizeof(struct nlmon),
+       .setup                  = nlmon_setup,
+       .validate               = nlmon_validate,
+};
+
+static __init int nlmon_register(void)
+{
+       return rtnl_link_register(&nlmon_link_ops);
+}
+
+static __exit void nlmon_unregister(void)
+{
+       rtnl_link_unregister(&nlmon_link_ops);
+}
+
+module_init(nlmon_register);
+module_exit(nlmon_unregister);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>");
+MODULE_AUTHOR("Mathieu Geli <geli@enseirb.fr>");
+MODULE_DESCRIPTION("Netlink monitoring device");
+MODULE_ALIAS_RTNL_LINK("nlmon");
index 1e11f2b..3a316b3 100644 (file)
@@ -144,6 +144,16 @@ config MDIO_OCTEON
 
          If in doubt, say Y.
 
+config MDIO_SUN4I
+       tristate "Allwinner sun4i MDIO interface support"
+       depends on ARCH_SUNXI
+       select REGULATOR
+       select REGULATOR_FIXED_VOLTAGE
+       help
+         This driver supports the MDIO interface found in the network
+         interface units of the Allwinner SoC that have an EMAC (A10,
+         A12, A10s, etc.)
+
 config MDIO_BUS_MUX
        tristate
        depends on OF_MDIO
index 9645e38..23a2ab2 100644 (file)
@@ -30,3 +30,4 @@ obj-$(CONFIG_AMD_PHY)         += amd.o
 obj-$(CONFIG_MDIO_BUS_MUX)     += mdio-mux.o
 obj-$(CONFIG_MDIO_BUS_MUX_GPIO)        += mdio-mux-gpio.o
 obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
+obj-$(CONFIG_MDIO_SUN4I)       += mdio-sun4i.o
index 45cbc10..1f7091b 100644 (file)
 #define AT803X_MMD_ACCESS_CONTROL              0x0D
 #define AT803X_MMD_ACCESS_CONTROL_DATA         0x0E
 #define AT803X_FUNC_DATA                       0x4003
+#define AT803X_DEBUG_ADDR                      0x1D
+#define AT803X_DEBUG_DATA                      0x1E
+#define AT803X_DEBUG_SYSTEM_MODE_CTRL          0x05
+#define AT803X_DEBUG_RGMII_TX_CLK_DLY          BIT(8)
 
 MODULE_DESCRIPTION("Atheros 803x PHY driver");
 MODULE_AUTHOR("Matus Ujhelyi");
 MODULE_LICENSE("GPL");
 
-static void at803x_set_wol_mac_addr(struct phy_device *phydev)
+static int at803x_set_wol(struct phy_device *phydev,
+                         struct ethtool_wolinfo *wol)
 {
        struct net_device *ndev = phydev->attached_dev;
        const u8 *mac;
+       int ret;
+       u32 value;
        unsigned int i, offsets[] = {
                AT803X_LOC_MAC_ADDR_32_47_OFFSET,
                AT803X_LOC_MAC_ADDR_16_31_OFFSET,
@@ -43,30 +50,61 @@ static void at803x_set_wol_mac_addr(struct phy_device *phydev)
        };
 
        if (!ndev)
-               return;
+               return -ENODEV;
 
-       mac = (const u8 *) ndev->dev_addr;
+       if (wol->wolopts & WAKE_MAGIC) {
+               mac = (const u8 *) ndev->dev_addr;
 
-       if (!is_valid_ether_addr(mac))
-               return;
+               if (!is_valid_ether_addr(mac))
+                       return -EFAULT;
 
-       for (i = 0; i < 3; i++) {
-               phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
+               for (i = 0; i < 3; i++) {
+                       phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
                                  AT803X_DEVICE_ADDR);
-               phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
+                       phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
                                  offsets[i]);
-               phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
+                       phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
                                  AT803X_FUNC_DATA);
-               phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
+                       phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
                                  mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
+               }
+
+               value = phy_read(phydev, AT803X_INTR_ENABLE);
+               value |= AT803X_WOL_ENABLE;
+               ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
+               if (ret)
+                       return ret;
+               value = phy_read(phydev, AT803X_INTR_STATUS);
+       } else {
+               value = phy_read(phydev, AT803X_INTR_ENABLE);
+               value &= (~AT803X_WOL_ENABLE);
+               ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
+               if (ret)
+                       return ret;
+               value = phy_read(phydev, AT803X_INTR_STATUS);
        }
+
+       return ret;
+}
+
+static void at803x_get_wol(struct phy_device *phydev,
+                          struct ethtool_wolinfo *wol)
+{
+       u32 value;
+
+       wol->supported = WAKE_MAGIC;
+       wol->wolopts = 0;
+
+       value = phy_read(phydev, AT803X_INTR_ENABLE);
+       if (value & AT803X_WOL_ENABLE)
+               wol->wolopts |= WAKE_MAGIC;
 }
 
 static int at803x_config_init(struct phy_device *phydev)
 {
        int val;
+       int ret;
        u32 features;
-       int status;
 
        features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
                   SUPPORTED_FIBRE | SUPPORTED_BNC;
@@ -100,20 +138,29 @@ static int at803x_config_init(struct phy_device *phydev)
        phydev->supported = features;
        phydev->advertising = features;
 
-       /* enable WOL */
-       at803x_set_wol_mac_addr(phydev);
-       status = phy_write(phydev, AT803X_INTR_ENABLE, AT803X_WOL_ENABLE);
-       status = phy_read(phydev, AT803X_INTR_STATUS);
+       if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+               ret = phy_write(phydev, AT803X_DEBUG_ADDR,
+                               AT803X_DEBUG_SYSTEM_MODE_CTRL);
+               if (ret)
+                       return ret;
+               ret = phy_write(phydev, AT803X_DEBUG_DATA,
+                               AT803X_DEBUG_RGMII_TX_CLK_DLY);
+               if (ret)
+                       return ret;
+       }
 
        return 0;
 }
 
-/* ATHEROS 8035 */
-static struct phy_driver at8035_driver = {
+static struct phy_driver at803x_driver[] = {
+{
+       /* ATHEROS 8035 */
        .phy_id         = 0x004dd072,
        .name           = "Atheros 8035 ethernet",
        .phy_id_mask    = 0xffffffef,
        .config_init    = at803x_config_init,
+       .set_wol        = at803x_set_wol,
+       .get_wol        = at803x_get_wol,
        .features       = PHY_GBIT_FEATURES,
        .flags          = PHY_HAS_INTERRUPT,
        .config_aneg    = &genphy_config_aneg,
@@ -121,14 +168,14 @@ static struct phy_driver at8035_driver = {
        .driver         = {
                .owner = THIS_MODULE,
        },
-};
-
-/* ATHEROS 8030 */
-static struct phy_driver at8030_driver = {
+}, {
+       /* ATHEROS 8030 */
        .phy_id         = 0x004dd076,
        .name           = "Atheros 8030 ethernet",
        .phy_id_mask    = 0xffffffef,
        .config_init    = at803x_config_init,
+       .set_wol        = at803x_set_wol,
+       .get_wol        = at803x_get_wol,
        .features       = PHY_GBIT_FEATURES,
        .flags          = PHY_HAS_INTERRUPT,
        .config_aneg    = &genphy_config_aneg,
@@ -136,32 +183,33 @@ static struct phy_driver at8030_driver = {
        .driver         = {
                .owner = THIS_MODULE,
        },
-};
+}, {
+       /* ATHEROS 8031 */
+       .phy_id         = 0x004dd074,
+       .name           = "Atheros 8031 ethernet",
+       .phy_id_mask    = 0xffffffef,
+       .config_init    = at803x_config_init,
+       .set_wol        = at803x_set_wol,
+       .get_wol        = at803x_get_wol,
+       .features       = PHY_GBIT_FEATURES,
+       .flags          = PHY_HAS_INTERRUPT,
+       .config_aneg    = &genphy_config_aneg,
+       .read_status    = &genphy_read_status,
+       .driver         = {
+               .owner = THIS_MODULE,
+       },
+} };
 
 static int __init atheros_init(void)
 {
-       int ret;
-
-       ret = phy_driver_register(&at8035_driver);
-       if (ret)
-               goto err1;
-
-       ret = phy_driver_register(&at8030_driver);
-       if (ret)
-               goto err2;
-
-       return 0;
-
-err2:
-       phy_driver_unregister(&at8035_driver);
-err1:
-       return ret;
+       return phy_drivers_register(at803x_driver,
+                                   ARRAY_SIZE(at803x_driver));
 }
 
 static void __exit atheros_exit(void)
 {
-       phy_driver_unregister(&at8035_driver);
-       phy_driver_unregister(&at8030_driver);
+       return phy_drivers_unregister(at803x_driver,
+                                     ARRAY_SIZE(at803x_driver));
 }
 
 module_init(atheros_init);
index 84c7a39..ac55b08 100644 (file)
@@ -78,7 +78,7 @@ static struct phy_driver bcm63xx_driver[] = {
        .name           = "Broadcom BCM63XX (1)",
        /* ASYM_PAUSE bit is marked RO in datasheet, so don't cheat */
        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
-       .flags          = PHY_HAS_INTERRUPT,
+       .flags          = PHY_HAS_INTERRUPT | PHY_IS_INTERNAL,
        .config_init    = bcm63xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
@@ -91,7 +91,7 @@ static struct phy_driver bcm63xx_driver[] = {
        .phy_id_mask    = 0xfffffc00,
        .name           = "Broadcom BCM63XX (2)",
        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
-       .flags          = PHY_HAS_INTERRUPT,
+       .flags          = PHY_HAS_INTERRUPT | PHY_IS_INTERNAL,
        .config_init    = bcm63xx_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
index 202fe1f..2e91477 100644 (file)
 #define MII_M1011_PHY_STATUS_RESOLVED  0x0800
 #define MII_M1011_PHY_STATUS_LINK      0x0400
 
+#define MII_M1116R_CONTROL_REG_MAC     21
+
 
 MODULE_DESCRIPTION("Marvell PHY driver");
 MODULE_AUTHOR("Andy Fleming");
@@ -372,6 +374,66 @@ static int m88e1318_config_aneg(struct phy_device *phydev)
        return m88e1121_config_aneg(phydev);
 }
 
+static int m88e1510_config_aneg(struct phy_device *phydev)
+{
+       int err;
+
+       err = m88e1318_config_aneg(phydev);
+       if (err < 0)
+               return err;
+
+       return marvell_of_reg_init(phydev);
+}
+
+static int m88e1116r_config_init(struct phy_device *phydev)
+{
+       int temp;
+       int err;
+
+       temp = phy_read(phydev, MII_BMCR);
+       temp |= BMCR_RESET;
+       err = phy_write(phydev, MII_BMCR, temp);
+       if (err < 0)
+               return err;
+
+       mdelay(500);
+
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
+       if (err < 0)
+               return err;
+
+       temp = phy_read(phydev, MII_M1011_PHY_SCR);
+       temp |= (7 << 12);      /* max number of gigabit attempts */
+       temp |= (1 << 11);      /* enable downshift */
+       temp |= MII_M1011_PHY_SCR_AUTO_CROSS;
+       err = phy_write(phydev, MII_M1011_PHY_SCR, temp);
+       if (err < 0)
+               return err;
+
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 2);
+       if (err < 0)
+               return err;
+       temp = phy_read(phydev, MII_M1116R_CONTROL_REG_MAC);
+       temp |= (1 << 5);
+       temp |= (1 << 4);
+       err = phy_write(phydev, MII_M1116R_CONTROL_REG_MAC, temp);
+       if (err < 0)
+               return err;
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0);
+       if (err < 0)
+               return err;
+
+       temp = phy_read(phydev, MII_BMCR);
+       temp |= BMCR_RESET;
+       err = phy_write(phydev, MII_BMCR, temp);
+       if (err < 0)
+               return err;
+
+       mdelay(500);
+
+       return 0;
+}
+
 static int m88e1111_config_init(struct phy_device *phydev)
 {
        int err;
@@ -940,6 +1002,32 @@ static struct phy_driver marvell_drivers[] = {
                .config_intr = &marvell_config_intr,
                .driver = { .owner = THIS_MODULE },
        },
+       {
+               .phy_id = MARVELL_PHY_ID_88E1116R,
+               .phy_id_mask = MARVELL_PHY_ID_MASK,
+               .name = "Marvell 88E1116R",
+               .features = PHY_GBIT_FEATURES,
+               .flags = PHY_HAS_INTERRUPT,
+               .config_init = &m88e1116r_config_init,
+               .config_aneg = &genphy_config_aneg,
+               .read_status = &genphy_read_status,
+               .ack_interrupt = &marvell_ack_interrupt,
+               .config_intr = &marvell_config_intr,
+               .driver = { .owner = THIS_MODULE },
+       },
+       {
+               .phy_id = MARVELL_PHY_ID_88E1510,
+               .phy_id_mask = MARVELL_PHY_ID_MASK,
+               .name = "Marvell 88E1510",
+               .features = PHY_GBIT_FEATURES,
+               .flags = PHY_HAS_INTERRUPT,
+               .config_aneg = &m88e1510_config_aneg,
+               .read_status = &marvell_read_status,
+               .ack_interrupt = &marvell_ack_interrupt,
+               .config_intr = &marvell_config_intr,
+               .did_interrupt = &m88e1121_did_interrupt,
+               .driver = { .owner = THIS_MODULE },
+       },
 };
 
 static int __init marvell_init(void)
@@ -958,15 +1046,17 @@ module_init(marvell_init);
 module_exit(marvell_exit);
 
 static struct mdio_device_id __maybe_unused marvell_tbl[] = {
-       { 0x01410c60, 0xfffffff0 },
-       { 0x01410c90, 0xfffffff0 },
-       { 0x01410cc0, 0xfffffff0 },
-       { 0x01410e10, 0xfffffff0 },
-       { 0x01410cb0, 0xfffffff0 },
-       { 0x01410cd0, 0xfffffff0 },
-       { 0x01410e50, 0xfffffff0 },
-       { 0x01410e30, 0xfffffff0 },
-       { 0x01410e90, 0xfffffff0 },
+       { MARVELL_PHY_ID_88E1101, MARVELL_PHY_ID_MASK },
+       { MARVELL_PHY_ID_88E1112, MARVELL_PHY_ID_MASK },
+       { MARVELL_PHY_ID_88E1111, MARVELL_PHY_ID_MASK },
+       { MARVELL_PHY_ID_88E1118, MARVELL_PHY_ID_MASK },
+       { MARVELL_PHY_ID_88E1121R, MARVELL_PHY_ID_MASK },
+       { MARVELL_PHY_ID_88E1145, MARVELL_PHY_ID_MASK },
+       { MARVELL_PHY_ID_88E1149R, MARVELL_PHY_ID_MASK },
+       { MARVELL_PHY_ID_88E1240, MARVELL_PHY_ID_MASK },
+       { MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
+       { MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
+       { MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
        { }
 };
 
diff --git a/drivers/net/phy/mdio-sun4i.c b/drivers/net/phy/mdio-sun4i.c
new file mode 100644 (file)
index 0000000..61d3f4e
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Allwinner EMAC MDIO interface driver
+ *
+ * Copyright 2012-2013 Stefan Roese <sr@denx.de>
+ * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * Based on the Linux driver provided by Allwinner:
+ * Copyright (C) 1997  Sten Wang
+ *
+ * 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/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define EMAC_MAC_MCMD_REG      (0x00)
+#define EMAC_MAC_MADR_REG      (0x04)
+#define EMAC_MAC_MWTD_REG      (0x08)
+#define EMAC_MAC_MRDD_REG      (0x0c)
+#define EMAC_MAC_MIND_REG      (0x10)
+#define EMAC_MAC_SSRR_REG      (0x14)
+
+#define MDIO_TIMEOUT           (msecs_to_jiffies(100))
+
+struct sun4i_mdio_data {
+       void __iomem            *membase;
+       struct regulator        *regulator;
+};
+
+static int sun4i_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+       struct sun4i_mdio_data *data = bus->priv;
+       unsigned long start_jiffies;
+       int value;
+
+       /* issue the phy address and reg */
+       writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG);
+       /* pull up the phy io line */
+       writel(0x1, data->membase + EMAC_MAC_MCMD_REG);
+
+       /* Wait read complete */
+       start_jiffies = jiffies;
+       while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) {
+               if (time_after(start_jiffies,
+                              start_jiffies + MDIO_TIMEOUT))
+                       return -ETIMEDOUT;
+               msleep(1);
+       }
+
+       /* push down the phy io line */
+       writel(0x0, data->membase + EMAC_MAC_MCMD_REG);
+       /* and read data */
+       value = readl(data->membase + EMAC_MAC_MRDD_REG);
+
+       return value;
+}
+
+static int sun4i_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
+                           u16 value)
+{
+       struct sun4i_mdio_data *data = bus->priv;
+       unsigned long start_jiffies;
+
+       /* issue the phy address and reg */
+       writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG);
+       /* pull up the phy io line */
+       writel(0x1, data->membase + EMAC_MAC_MCMD_REG);
+
+       /* Wait read complete */
+       start_jiffies = jiffies;
+       while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) {
+               if (time_after(start_jiffies,
+                              start_jiffies + MDIO_TIMEOUT))
+                       return -ETIMEDOUT;
+               msleep(1);
+       }
+
+       /* push down the phy io line */
+       writel(0x0, data->membase + EMAC_MAC_MCMD_REG);
+       /* and write data */
+       writel(value, data->membase + EMAC_MAC_MWTD_REG);
+
+       return 0;
+}
+
+static int sun4i_mdio_reset(struct mii_bus *bus)
+{
+       return 0;
+}
+
+static int sun4i_mdio_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct mii_bus *bus;
+       struct sun4i_mdio_data *data;
+       int ret, i;
+
+       bus = mdiobus_alloc_size(sizeof(*data));
+       if (!bus)
+               return -ENOMEM;
+
+       bus->name = "sun4i_mii_bus";
+       bus->read = &sun4i_mdio_read;
+       bus->write = &sun4i_mdio_write;
+       bus->reset = &sun4i_mdio_reset;
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+       bus->parent = &pdev->dev;
+
+       bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+       if (!bus->irq) {
+               ret = -ENOMEM;
+               goto err_out_free_mdiobus;
+       }
+
+       for (i = 0; i < PHY_MAX_ADDR; i++)
+               bus->irq[i] = PHY_POLL;
+
+       data = bus->priv;
+       data->membase = of_iomap(np, 0);
+       if (!data->membase) {
+               ret = -ENOMEM;
+               goto err_out_free_mdio_irq;
+       }
+
+       data->regulator = devm_regulator_get(&pdev->dev, "phy");
+       if (IS_ERR(data->regulator)) {
+               if (PTR_ERR(data->regulator) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+
+               dev_info(&pdev->dev, "no regulator found\n");
+       } else {
+               ret = regulator_enable(data->regulator);
+               if (ret)
+                       goto err_out_free_mdio_irq;
+       }
+
+       ret = of_mdiobus_register(bus, np);
+       if (ret < 0)
+               goto err_out_disable_regulator;
+
+       platform_set_drvdata(pdev, bus);
+
+       return 0;
+
+err_out_disable_regulator:
+       regulator_disable(data->regulator);
+err_out_free_mdio_irq:
+       kfree(bus->irq);
+err_out_free_mdiobus:
+       mdiobus_free(bus);
+       return ret;
+}
+
+static int sun4i_mdio_remove(struct platform_device *pdev)
+{
+       struct mii_bus *bus = platform_get_drvdata(pdev);
+
+       mdiobus_unregister(bus);
+       kfree(bus->irq);
+       mdiobus_free(bus);
+
+       return 0;
+}
+
+static const struct of_device_id sun4i_mdio_dt_ids[] = {
+       { .compatible = "allwinner,sun4i-mdio" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, sun4i_mdio_dt_ids);
+
+static struct platform_driver sun4i_mdio_driver = {
+       .probe = sun4i_mdio_probe,
+       .remove = sun4i_mdio_remove,
+       .driver = {
+               .name = "sun4i-mdio",
+               .of_match_table = sun4i_mdio_dt_ids,
+       },
+};
+
+module_platform_driver(sun4i_mdio_driver);
+
+MODULE_DESCRIPTION("Allwinner EMAC MDIO interface driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_LICENSE("GPL");
index 663d2d0..36c6994 100644 (file)
@@ -294,7 +294,8 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd)
        cmd->duplex = phydev->duplex;
        cmd->port = PORT_MII;
        cmd->phy_address = phydev->addr;
-       cmd->transceiver = XCVR_EXTERNAL;
+       cmd->transceiver = phy_is_internal(phydev) ?
+               XCVR_INTERNAL : XCVR_EXTERNAL;
        cmd->autoneg = phydev->autoneg;
 
        return 0;
@@ -419,8 +420,6 @@ out_unlock:
 EXPORT_SYMBOL(phy_start_aneg);
 
 
-static void phy_change(struct work_struct *work);
-
 /**
  * phy_start_machine - start PHY state machine tracking
  * @phydev: the phy_device struct
@@ -565,8 +564,6 @@ int phy_start_interrupts(struct phy_device *phydev)
 {
        int err = 0;
 
-       INIT_WORK(&phydev->phy_queue, phy_change);
-
        atomic_set(&phydev->irq_disable, 0);
        if (request_irq(phydev->irq, phy_interrupt,
                                IRQF_SHARED,
@@ -623,7 +620,7 @@ EXPORT_SYMBOL(phy_stop_interrupts);
  * phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes
  * @work: work_struct that describes the work to be done
  */
-static void phy_change(struct work_struct *work)
+void phy_change(struct work_struct *work)
 {
        int err;
        struct phy_device *phydev =
@@ -682,7 +679,7 @@ void phy_stop(struct phy_device *phydev)
        if (PHY_HALTED == phydev->state)
                goto out_unlock;
 
-       if (phydev->irq != PHY_POLL) {
+       if (phy_interrupt_is_valid(phydev)) {
                /* Disable PHY Interrupts */
                phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
 
@@ -828,8 +825,9 @@ void phy_state_machine(struct work_struct *work)
                        break;
                case PHY_RUNNING:
                        /* Only register a CHANGE if we are
-                        * polling */
-                       if (PHY_POLL == phydev->irq)
+                        * polling or ignoring interrupts
+                        */
+                       if (!phy_interrupt_is_valid(phydev))
                                phydev->state = PHY_CHANGELINK;
                        break;
                case PHY_CHANGELINK:
@@ -848,7 +846,7 @@ void phy_state_machine(struct work_struct *work)
 
                        phydev->adjust_link(phydev->attached_dev);
 
-                       if (PHY_POLL != phydev->irq)
+                       if (phy_interrupt_is_valid(phydev))
                                err = phy_config_interrupt(phydev,
                                                PHY_INTERRUPT_ENABLED);
                        break;
@@ -922,6 +920,14 @@ void phy_state_machine(struct work_struct *work)
                        PHY_STATE_TIME * HZ);
 }
 
+void phy_mac_interrupt(struct phy_device *phydev, int new_link)
+{
+       cancel_work_sync(&phydev->phy_queue);
+       phydev->link = new_link;
+       schedule_work(&phydev->phy_queue);
+}
+EXPORT_SYMBOL(phy_mac_interrupt);
+
 static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
                                    int addr)
 {
index 3657b4a..74630e9 100644 (file)
@@ -189,6 +189,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
 
        mutex_init(&dev->lock);
        INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
+       INIT_WORK(&dev->phy_queue, phy_change);
 
        /* Request the appropriate module unconditionally; don't
           bother trying to do so only if it isn't already loaded,
@@ -1009,10 +1010,16 @@ static int phy_probe(struct device *dev)
        phydrv = to_phy_driver(drv);
        phydev->drv = phydrv;
 
-       /* Disable the interrupt if the PHY doesn't support it */
-       if (!(phydrv->flags & PHY_HAS_INTERRUPT))
+       /* Disable the interrupt if the PHY doesn't support it
+        * but the interrupt is still a valid one
+        */
+       if (!(phydrv->flags & PHY_HAS_INTERRUPT) &&
+                       phy_interrupt_is_valid(phydev))
                phydev->irq = PHY_POLL;
 
+       if (phydrv->flags & PHY_IS_INTERNAL)
+               phydev->is_internal = true;
+
        mutex_lock(&phydev->lock);
 
        /* Start out supporting everything. Eventually,
index d11c93e..f3bea13 100644 (file)
@@ -354,19 +354,7 @@ static struct spi_driver ks8995_driver = {
        .remove   = ks8995_remove,
 };
 
-static int __init ks8995_init(void)
-{
-       pr_info(DRV_DESC " version " DRV_VERSION "\n");
-
-       return spi_register_driver(&ks8995_driver);
-}
-module_init(ks8995_init);
-
-static void __exit ks8995_exit(void)
-{
-       spi_unregister_driver(&ks8995_driver);
-}
-module_exit(ks8995_exit);
+module_spi_driver(ks8995_driver);
 
 MODULE_DESCRIPTION(DRV_DESC);
 MODULE_VERSION(DRV_VERSION);
index 3492b53..69b482b 100644 (file)
 #define MII_VSC8244_ISTAT_DUPLEX       0x1000
 
 /* Vitesse Auxiliary Control/Status Register */
-#define MII_VSC8244_AUX_CONSTAT                0x1c
-#define MII_VSC8244_AUXCONSTAT_INIT            0x0000
-#define MII_VSC8244_AUXCONSTAT_DUPLEX          0x0020
-#define MII_VSC8244_AUXCONSTAT_SPEED           0x0018
-#define MII_VSC8244_AUXCONSTAT_GBIT            0x0010
-#define MII_VSC8244_AUXCONSTAT_100             0x0008
+#define MII_VSC8244_AUX_CONSTAT                0x1c
+#define MII_VSC8244_AUXCONSTAT_INIT    0x0000
+#define MII_VSC8244_AUXCONSTAT_DUPLEX  0x0020
+#define MII_VSC8244_AUXCONSTAT_SPEED   0x0018
+#define MII_VSC8244_AUXCONSTAT_GBIT    0x0010
+#define MII_VSC8244_AUXCONSTAT_100     0x0008
 
 #define MII_VSC8221_AUXCONSTAT_INIT    0x0004 /* need to set this bit? */
 #define MII_VSC8221_AUXCONSTAT_RESERVED        0x0004
 
 #define PHY_ID_VSC8244                 0x000fc6c0
 #define PHY_ID_VSC8221                 0x000fc550
+#define PHY_ID_VSC8211                 0x000fc4b0
 
 MODULE_DESCRIPTION("Vitesse PHY driver");
 MODULE_AUTHOR("Kriston Carson");
@@ -100,9 +101,8 @@ static int vsc824x_config_init(struct phy_device *phydev)
 static int vsc824x_ack_interrupt(struct phy_device *phydev)
 {
        int err = 0;
-       
-       /*
-        * Don't bother to ACK the interrupts if interrupts
+
+       /* Don't bother to ACK the interrupts if interrupts
         * are disabled.  The 824x cannot clear the interrupts
         * if they are disabled.
         */
@@ -122,8 +122,7 @@ static int vsc82xx_config_intr(struct phy_device *phydev)
                                MII_VSC8244_IMASK_MASK :
                                MII_VSC8221_IMASK_MASK);
        else {
-               /*
-                * The Vitesse PHY cannot clear the interrupt
+               /* The Vitesse PHY cannot clear the interrupt
                 * once it has disabled them, so we clear them first
                 */
                err = phy_read(phydev, MII_VSC8244_ISTAT);
@@ -146,7 +145,8 @@ static int vsc8221_config_init(struct phy_device *phydev)
        return err;
 
        /* Perhaps we should set EXT_CON1 based on the interface?
-          Options are 802.3Z SerDes or SGMII */
+        * Options are 802.3Z SerDes or SGMII
+        */
 }
 
 /* Vitesse 824x */
@@ -176,6 +176,19 @@ static struct phy_driver vsc82xx_driver[] = {
        .ack_interrupt  = &vsc824x_ack_interrupt,
        .config_intr    = &vsc82xx_config_intr,
        .driver         = { .owner = THIS_MODULE,},
+}, {
+       /* Vitesse 8211 */
+       .phy_id         = PHY_ID_VSC8211,
+       .phy_id_mask    = 0x000ffff0,
+       .name           = "Vitesse VSC8211",
+       .features       = PHY_GBIT_FEATURES,
+       .flags          = PHY_HAS_INTERRUPT,
+       .config_init    = &vsc8221_config_init,
+       .config_aneg    = &genphy_config_aneg,
+       .read_status    = &genphy_read_status,
+       .ack_interrupt  = &vsc824x_ack_interrupt,
+       .config_intr    = &vsc82xx_config_intr,
+       .driver         = { .owner = THIS_MODULE,},
 } };
 
 static int __init vsc82xx_init(void)
@@ -196,6 +209,7 @@ module_exit(vsc82xx_exit);
 static struct mdio_device_id __maybe_unused vitesse_tbl[] = {
        { PHY_ID_VSC8244, 0x000fffc0 },
        { PHY_ID_VSC8221, 0x000ffff0 },
+       { PHY_ID_VSC8211, 0x000ffff0 },
        { }
 };
 
index bb07ba9..5f66e30 100644 (file)
@@ -338,7 +338,7 @@ static void pppoe_flush_dev(struct net_device *dev)
 static int pppoe_device_event(struct notifier_block *this,
                              unsigned long event, void *ptr)
 {
-       struct net_device *dev = (struct net_device *)ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        /* Only look at sockets that are using this specific device. */
        switch (event) {
index b305105..bff7e0b 100644 (file)
@@ -525,31 +525,26 @@ static void team_set_no_mode(struct team *team)
        team->mode = &__team_no_mode;
 }
 
-static void __team_adjust_ops(struct team *team, int en_port_count)
+static void team_adjust_ops(struct team *team)
 {
        /*
         * To avoid checks in rx/tx skb paths, ensure here that non-null and
         * correct ops are always set.
         */
 
-       if (!en_port_count || !team_is_mode_set(team) ||
+       if (!team->en_port_count || !team_is_mode_set(team) ||
            !team->mode->ops->transmit)
                team->ops.transmit = team_dummy_transmit;
        else
                team->ops.transmit = team->mode->ops->transmit;
 
-       if (!en_port_count || !team_is_mode_set(team) ||
+       if (!team->en_port_count || !team_is_mode_set(team) ||
            !team->mode->ops->receive)
                team->ops.receive = team_dummy_receive;
        else
                team->ops.receive = team->mode->ops->receive;
 }
 
-static void team_adjust_ops(struct team *team)
-{
-       __team_adjust_ops(team, team->en_port_count);
-}
-
 /*
  * We can benefit from the fact that it's ensured no port is present
  * at the time of mode change. Therefore no packets are in fly so there's no
@@ -725,9 +720,9 @@ static bool team_queue_override_transmit(struct team *team, struct sk_buff *skb)
 static void __team_queue_override_port_del(struct team *team,
                                           struct team_port *port)
 {
+       if (!port->queue_id)
+               return;
        list_del_rcu(&port->qom_list);
-       synchronize_rcu();
-       INIT_LIST_HEAD(&port->qom_list);
 }
 
 static bool team_queue_override_port_has_gt_prio_than(struct team_port *port,
@@ -749,9 +744,8 @@ static void __team_queue_override_port_add(struct team *team,
        struct list_head *qom_list;
        struct list_head *node;
 
-       if (!port->queue_id || !team_port_enabled(port))
+       if (!port->queue_id)
                return;
-
        qom_list = __team_get_qom_list(team, port->queue_id);
        node = qom_list;
        list_for_each_entry(cur, qom_list, qom_list) {
@@ -768,7 +762,7 @@ static void __team_queue_override_enabled_check(struct team *team)
        bool enabled = false;
 
        list_for_each_entry(port, &team->port_list, list) {
-               if (!list_empty(&port->qom_list)) {
+               if (port->queue_id) {
                        enabled = true;
                        break;
                }
@@ -780,14 +774,44 @@ static void __team_queue_override_enabled_check(struct team *team)
        team->queue_override_enabled = enabled;
 }
 
-static void team_queue_override_port_refresh(struct team *team,
-                                            struct team_port *port)
+static void team_queue_override_port_prio_changed(struct team *team,
+                                                 struct team_port *port)
 {
+       if (!port->queue_id || team_port_enabled(port))
+               return;
        __team_queue_override_port_del(team, port);
        __team_queue_override_port_add(team, port);
        __team_queue_override_enabled_check(team);
 }
 
+static void team_queue_override_port_change_queue_id(struct team *team,
+                                                    struct team_port *port,
+                                                    u16 new_queue_id)
+{
+       if (team_port_enabled(port)) {
+               __team_queue_override_port_del(team, port);
+               port->queue_id = new_queue_id;
+               __team_queue_override_port_add(team, port);
+               __team_queue_override_enabled_check(team);
+       } else {
+               port->queue_id = new_queue_id;
+       }
+}
+
+static void team_queue_override_port_add(struct team *team,
+                                        struct team_port *port)
+{
+       __team_queue_override_port_add(team, port);
+       __team_queue_override_enabled_check(team);
+}
+
+static void team_queue_override_port_del(struct team *team,
+                                        struct team_port *port)
+{
+       __team_queue_override_port_del(team, port);
+       __team_queue_override_enabled_check(team);
+}
+
 
 /****************
  * Port handling
@@ -819,7 +843,7 @@ static void team_port_enable(struct team *team,
        hlist_add_head_rcu(&port->hlist,
                           team_port_index_hash(team, port->index));
        team_adjust_ops(team);
-       team_queue_override_port_refresh(team, port);
+       team_queue_override_port_add(team, port);
        if (team->ops.port_enabled)
                team->ops.port_enabled(team, port);
 }
@@ -848,14 +872,9 @@ static void team_port_disable(struct team *team,
        hlist_del_rcu(&port->hlist);
        __reconstruct_port_hlist(team, port->index);
        port->index = -1;
-       team_queue_override_port_refresh(team, port);
-       __team_adjust_ops(team, team->en_port_count - 1);
-       /*
-        * Wait until readers see adjusted ops. This ensures that
-        * readers never see team->en_port_count == 0
-        */
-       synchronize_rcu();
        team->en_port_count--;
+       team_queue_override_port_del(team, port);
+       team_adjust_ops(team);
 }
 
 #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \
@@ -1163,8 +1182,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
 
        team_port_set_orig_dev_addr(port);
        dev_set_mtu(port_dev, port->orig.mtu);
-       synchronize_rcu();
-       kfree(port);
+       kfree_rcu(port, rcu);
        netdev_info(dev, "Port device %s removed\n", portname);
        __team_compute_features(team);
 
@@ -1259,9 +1277,12 @@ static int team_priority_option_set(struct team *team,
                                    struct team_gsetter_ctx *ctx)
 {
        struct team_port *port = ctx->info->port;
+       s32 priority = ctx->data.s32_val;
 
-       port->priority = ctx->data.s32_val;
-       team_queue_override_port_refresh(team, port);
+       if (port->priority == priority)
+               return 0;
+       port->priority = priority;
+       team_queue_override_port_prio_changed(team, port);
        return 0;
 }
 
@@ -1278,17 +1299,16 @@ static int team_queue_id_option_set(struct team *team,
                                    struct team_gsetter_ctx *ctx)
 {
        struct team_port *port = ctx->info->port;
+       u16 new_queue_id = ctx->data.u32_val;
 
-       if (port->queue_id == ctx->data.u32_val)
+       if (port->queue_id == new_queue_id)
                return 0;
-       if (ctx->data.u32_val >= team->dev->real_num_tx_queues)
+       if (new_queue_id >= team->dev->real_num_tx_queues)
                return -EINVAL;
-       port->queue_id = ctx->data.u32_val;
-       team_queue_override_port_refresh(team, port);
+       team_queue_override_port_change_queue_id(team, port, new_queue_id);
        return 0;
 }
 
-
 static const struct team_option team_options[] = {
        {
                .name = "mode",
@@ -2648,7 +2668,7 @@ static void team_port_change_check(struct team_port *port, bool linkup)
 static int team_device_event(struct notifier_block *unused,
                             unsigned long event, void *ptr)
 {
-       struct net_device *dev = (struct net_device *) ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct team_port *port;
 
        port = team_port_get_rtnl(dev);
index cdc31b5..829a9cd 100644 (file)
@@ -112,9 +112,8 @@ static struct team_port *lb_hash_select_tx_port(struct team *team,
                                                struct sk_buff *skb,
                                                unsigned char hash)
 {
-       int port_index;
+       int port_index = team_num_to_port_index(team, hash);
 
-       port_index = hash % team->en_port_count;
        return team_get_port_by_index_rcu(team, port_index);
 }
 
index 472623f..5366585 100644 (file)
@@ -30,7 +30,8 @@ static bool rr_transmit(struct team *team, struct sk_buff *skb)
        struct team_port *port;
        int port_index;
 
-       port_index = rr_priv(team)->sent_packets++ % team->en_port_count;
+       port_index = team_num_to_port_index(team,
+                                           rr_priv(team)->sent_packets++);
        port = team_get_port_by_index_rcu(team, port_index);
        if (unlikely(!port))
                goto drop;
index 9c61f87..7eab5fc 100644 (file)
@@ -841,7 +841,7 @@ static const struct net_device_ops tap_netdev_ops = {
 #endif
 };
 
-static int tun_flow_init(struct tun_struct *tun)
+static void tun_flow_init(struct tun_struct *tun)
 {
        int i;
 
@@ -852,8 +852,6 @@ static int tun_flow_init(struct tun_struct *tun)
        setup_timer(&tun->flow_gc_timer, tun_flow_cleanup, (unsigned long)tun);
        mod_timer(&tun->flow_gc_timer,
                  round_jiffies_up(jiffies + tun->ageing_time));
-
-       return 0;
 }
 
 static void tun_flow_uninit(struct tun_struct *tun)
@@ -1532,6 +1530,9 @@ static int tun_flags(struct tun_struct *tun)
        if (tun->flags & TUN_TAP_MQ)
                flags |= IFF_MULTI_QUEUE;
 
+       if (tun->flags & TUN_PERSIST)
+               flags |= IFF_PERSIST;
+
        return flags;
 }
 
@@ -1661,10 +1662,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
                        goto err_free_dev;
 
                tun_net_init(dev);
-
-               err = tun_flow_init(tun);
-               if (err < 0)
-                       goto err_free_dev;
+               tun_flow_init(tun);
 
                dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST |
                        TUN_USER_FEATURES;
index 287cc62..d84bfd4 100644 (file)
@@ -67,7 +67,6 @@ config USB_KAWETH
 
 config USB_PEGASUS
        tristate "USB Pegasus/Pegasus-II based ethernet device support"
-       select NET_CORE
        select MII
        ---help---
          Say Y here if you know you have Pegasus or Pegasus-II based adapter.
@@ -83,7 +82,6 @@ config USB_PEGASUS
 
 config USB_RTL8150
        tristate "USB RTL8150 based ethernet device support"
-       select NET_CORE
        select MII
        help
          Say Y here if you have RTL8150 based usb-ethernet adapter.
@@ -95,7 +93,6 @@ config USB_RTL8150
 
 config USB_RTL8152
        tristate "Realtek RTL8152 Based USB 2.0 Ethernet Adapters"
-       select NET_CORE
        select MII
        help
          This option adds support for Realtek RTL8152 based USB 2.0
@@ -106,7 +103,6 @@ config USB_RTL8152
 
 config USB_USBNET
        tristate "Multi-purpose USB Networking Framework"
-       select NET_CORE
        select MII
        ---help---
          This driver supports several kinds of network links over USB,
index bd8758f..1e3c302 100644 (file)
@@ -1371,7 +1371,7 @@ static int ax88179_stop(struct usbnet *dev)
 }
 
 static const struct driver_info ax88179_info = {
-       .description = "ASIX AX88179 USB 3.0 Gigibit Ethernet",
+       .description = "ASIX AX88179 USB 3.0 Gigabit Ethernet",
        .bind = ax88179_bind,
        .unbind = ax88179_unbind,
        .status = ax88179_status,
@@ -1384,7 +1384,7 @@ static const struct driver_info ax88179_info = {
 };
 
 static const struct driver_info ax88178a_info = {
-       .description = "ASIX AX88178A USB 2.0 Gigibit Ethernet",
+       .description = "ASIX AX88178A USB 2.0 Gigabit Ethernet",
        .bind = ax88179_bind,
        .unbind = ax88179_unbind,
        .status = ax88179_status,
@@ -1433,6 +1433,7 @@ static struct usb_driver ax88179_178a_driver = {
        .probe =        usbnet_probe,
        .suspend =      ax88179_suspend,
        .resume =       ax88179_resume,
+       .reset_resume = ax88179_resume,
        .disconnect =   usbnet_disconnect,
        .supports_autosuspend = 1,
        .disable_hub_initiated_lpm = 1,
index 04ee044..4393f14 100644 (file)
@@ -215,6 +215,10 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
                                        goto bad_desc;
                        }
 
+                       /* some devices merge these - skip class check */
+                       if (info->control == info->data)
+                               goto next_desc;
+
                        /* a data interface altsetting does the real i/o */
                        d = &info->data->cur_altsetting->desc;
                        if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
@@ -304,19 +308,23 @@ next_desc:
        /* claim data interface and set it up ... with side effects.
         * network traffic can't flow until an altsetting is enabled.
         */
-       status = usb_driver_claim_interface(driver, info->data, dev);
-       if (status < 0)
-               return status;
+       if (info->data != info->control) {
+               status = usb_driver_claim_interface(driver, info->data, dev);
+               if (status < 0)
+                       return status;
+       }
        status = usbnet_get_endpoints(dev, info->data);
        if (status < 0) {
                /* ensure immediate exit from usbnet_disconnect */
                usb_set_intfdata(info->data, NULL);
-               usb_driver_release_interface(driver, info->data);
+               if (info->data != info->control)
+                       usb_driver_release_interface(driver, info->data);
                return status;
        }
 
        /* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */
-       dev->status = NULL;
+       if (info->data != info->control)
+               dev->status = NULL;
        if (info->control->cur_altsetting->desc.bNumEndpoints == 1) {
                struct usb_endpoint_descriptor  *desc;
 
@@ -349,6 +357,10 @@ void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
        struct cdc_state                *info = (void *) &dev->data;
        struct usb_driver               *driver = driver_of(intf);
 
+       /* combined interface - nothing  to do */
+       if (info->data == info->control)
+               return;
+
        /* disconnect master --> disconnect slave */
        if (intf == info->control && info->data) {
                /* ensure immediate exit from usbnet_disconnect */
index 534d8be..ff8594d 100644 (file)
@@ -60,6 +60,7 @@
 #define USB_PRODUCT_IPHONE_3GS  0x1294
 #define USB_PRODUCT_IPHONE_4   0x1297
 #define USB_PRODUCT_IPAD 0x129a
+#define USB_PRODUCT_IPAD_MINI    0x12ab
 #define USB_PRODUCT_IPHONE_4_VZW 0x129c
 #define USB_PRODUCT_IPHONE_4S  0x12a0
 #define USB_PRODUCT_IPHONE_5   0x12a8
@@ -106,6 +107,10 @@ static struct usb_device_id ipheth_table[] = {
                USB_VENDOR_APPLE, USB_PRODUCT_IPAD,
                IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
                IPHETH_USBINTF_PROTO) },
+       { USB_DEVICE_AND_INTERFACE_INFO(
+               USB_VENDOR_APPLE, USB_PRODUCT_IPAD_MINI,
+               IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+               IPHETH_USBINTF_PROTO) },
        { USB_DEVICE_AND_INTERFACE_INFO(
                USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4_VZW,
                IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
index 0192073..6866eae 100644 (file)
@@ -221,12 +221,9 @@ done:
                memset(skb_put(skb, padlen), 0, padlen);
        }
 
-       netdev_dbg(
-               dev->net,
-               "Sending package with length %i and padding %i. Header: %02x:%02x:%02x:%02x:%02x:%02x.",
-               content_len, padlen, header_start[0], header_start[1],
-               header_start[2], header_start[3], header_start[4],
-               header_start[5]);
+       netdev_dbg(dev->net,
+               "Sending package with length %i and padding %i. Header: %6phC.",
+               content_len, padlen, header_start);
 
        return skb;
 }
@@ -263,32 +260,23 @@ kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                                sizeof(EXPECTED_UNKNOWN_HEADER_1)) || !memcmp(
                                header_start, EXPECTED_UNKNOWN_HEADER_2,
                                sizeof(EXPECTED_UNKNOWN_HEADER_2))) {
-                               netdev_dbg(
-                                       dev->net,
-                                       "Received expected unknown frame header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n",
-                                       header_start[0], header_start[1],
-                                       header_start[2], header_start[3],
-                                       header_start[4], header_start[5],
+                               netdev_dbg(dev->net,
+                                       "Received expected unknown frame header: %6phC. Package length: %i\n",
+                                       header_start,
                                        skb->len - KALMIA_HEADER_LENGTH);
                        }
                        else {
-                               netdev_err(
-                                       dev->net,
-                                       "Received unknown frame header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n",
-                                       header_start[0], header_start[1],
-                                       header_start[2], header_start[3],
-                                       header_start[4], header_start[5],
+                               netdev_err(dev->net,
+                                       "Received unknown frame header: %6phC. Package length: %i\n",
+                                       header_start,
                                        skb->len - KALMIA_HEADER_LENGTH);
                                return 0;
                        }
                }
                else
-                       netdev_dbg(
-                               dev->net,
-                               "Received header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n",
-                               header_start[0], header_start[1], header_start[2],
-                               header_start[3], header_start[4], header_start[5],
-                               skb->len - KALMIA_HEADER_LENGTH);
+                       netdev_dbg(dev->net,
+                               "Received header: %6phC. Package length: %i\n",
+                               header_start, skb->len - KALMIA_HEADER_LENGTH);
 
                /* subtract start header and end header */
                usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH);
@@ -310,12 +298,9 @@ kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                                sizeof(HEADER_END_OF_USB_PACKET)) == 0);
                        if (!is_last) {
                                header_start = skb->data + ether_packet_length;
-                               netdev_dbg(
-                                       dev->net,
-                                       "End header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n",
-                                       header_start[0], header_start[1],
-                                       header_start[2], header_start[3],
-                                       header_start[4], header_start[5],
+                               netdev_dbg(dev->net,
+                                       "End header: %6phC. Package length: %i\n",
+                                       header_start,
                                        skb->len - KALMIA_HEADER_LENGTH);
                        }
                }
index 5645921..606eba2 100644 (file)
@@ -523,6 +523,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x19d2, 0x0002, 1)},
        {QMI_FIXED_INTF(0x19d2, 0x0012, 1)},
        {QMI_FIXED_INTF(0x19d2, 0x0017, 3)},
+       {QMI_FIXED_INTF(0x19d2, 0x0019, 3)},    /* ONDA MT689DC */
        {QMI_FIXED_INTF(0x19d2, 0x0021, 4)},
        {QMI_FIXED_INTF(0x19d2, 0x0025, 1)},
        {QMI_FIXED_INTF(0x19d2, 0x0031, 4)},
@@ -582,6 +583,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x1199, 0x901c, 8)},    /* Sierra Wireless EM7700 */
        {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)},    /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */
        {QMI_FIXED_INTF(0x2357, 0x0201, 4)},    /* TP-LINK HSUPA Modem MA180 */
+       {QMI_FIXED_INTF(0x2357, 0x9000, 4)},    /* TP-LINK MA260 */
        {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)},    /* Telit LE920 */
        {QMI_FIXED_INTF(0x1e2d, 0x12d1, 4)},    /* Cinterion PLxx */
 
@@ -618,6 +620,7 @@ static const struct usb_device_id products[] = {
        {QMI_GOBI_DEVICE(0x05c6, 0x9265)},      /* Asus Gobi 2000 Modem device (VR305) */
        {QMI_GOBI_DEVICE(0x05c6, 0x9235)},      /* Top Global Gobi 2000 Modem device (VR306) */
        {QMI_GOBI_DEVICE(0x05c6, 0x9275)},      /* iRex Technologies Gobi 2000 Modem device (VR307) */
+       {QMI_GOBI_DEVICE(0x0af0, 0x8120)},      /* Option GTM681W */
        {QMI_GOBI_DEVICE(0x1199, 0x68a5)},      /* Sierra Wireless Modem */
        {QMI_GOBI_DEVICE(0x1199, 0x68a9)},      /* Sierra Wireless Modem */
        {QMI_GOBI_DEVICE(0x1199, 0x9001)},      /* Sierra Wireless Gobi 2000 Modem device (VT773) */
@@ -631,7 +634,6 @@ static const struct usb_device_id products[] = {
        {QMI_GOBI_DEVICE(0x1199, 0x9009)},      /* Sierra Wireless Gobi 2000 Modem device (VT773) */
        {QMI_GOBI_DEVICE(0x1199, 0x900a)},      /* Sierra Wireless Gobi 2000 Modem device (VT773) */
        {QMI_GOBI_DEVICE(0x1199, 0x9011)},      /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
-       {QMI_FIXED_INTF(0x1199, 0x9011, 5)},    /* alternate interface number!? */
        {QMI_GOBI_DEVICE(0x16d8, 0x8002)},      /* CMDTech Gobi 2000 Modem device (VU922) */
        {QMI_GOBI_DEVICE(0x05c6, 0x9205)},      /* Gobi 2000 Modem device */
        {QMI_GOBI_DEVICE(0x1199, 0x9013)},      /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
index 14e5198..d02bac8 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/signal.h>
 #include <linux/slab.h>
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/mii.h>
@@ -1749,18 +1748,7 @@ static struct usb_driver rtl8152_driver = {
        .resume =       rtl8152_resume
 };
 
-static int __init usb_rtl8152_init(void)
-{
-       return usb_register(&rtl8152_driver);
-}
-
-static void __exit usb_rtl8152_exit(void)
-{
-       usb_deregister(&rtl8152_driver);
-}
-
-module_init(usb_rtl8152_init);
-module_exit(usb_rtl8152_exit);
+module_usb_driver(rtl8152_driver);
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
index 177f911..da86652 100644 (file)
@@ -379,12 +379,6 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
        else
                snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d");
 
-       if (strchr(dev->name, '%')) {
-               err = dev_alloc_name(dev, dev->name);
-               if (err < 0)
-                       goto err_alloc_name;
-       }
-
        err = register_netdevice(dev);
        if (err < 0)
                goto err_register_dev;
@@ -404,7 +398,6 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
 
 err_register_dev:
        /* nothing to do */
-err_alloc_name:
 err_configure_peer:
        unregister_netdevice(peer);
        return err;
index c9e0038..42d670a 100644 (file)
@@ -602,7 +602,7 @@ static int virtnet_poll(struct napi_struct *napi, int budget)
                container_of(napi, struct receive_queue, napi);
        struct virtnet_info *vi = rq->vq->vdev->priv;
        void *buf;
-       unsigned int len, received = 0;
+       unsigned int r, len, received = 0;
 
 again:
        while (received < budget &&
@@ -619,8 +619,9 @@ again:
 
        /* Out of packets? */
        if (received < budget) {
+               r = virtqueue_enable_cb_prepare(rq->vq);
                napi_complete(napi);
-               if (unlikely(!virtqueue_enable_cb(rq->vq)) &&
+               if (unlikely(virtqueue_poll(rq->vq, r)) &&
                    napi_schedule_prep(napi)) {
                        virtqueue_disable_cb(rq->vq);
                        __napi_schedule(napi);
index 57325f3..227b54a 100644 (file)
@@ -44,6 +44,8 @@
 
 #define VXLAN_VERSION  "0.1"
 
+#define PORT_HASH_BITS 8
+#define PORT_HASH_SIZE  (1<<PORT_HASH_BITS)
 #define VNI_HASH_BITS  10
 #define VNI_HASH_SIZE  (1<<VNI_HASH_BITS)
 #define FDB_HASH_BITS  8
@@ -66,30 +68,44 @@ struct vxlanhdr {
 
 /* UDP port for VXLAN traffic.
  * The IANA assigned port is 4789, but the Linux default is 8472
- * for compatability with early adopters.
+ * for compatibility with early adopters.
  */
-static unsigned int vxlan_port __read_mostly = 8472;
-module_param_named(udp_port, vxlan_port, uint, 0444);
+static unsigned short vxlan_port __read_mostly = 8472;
+module_param_named(udp_port, vxlan_port, ushort, 0444);
 MODULE_PARM_DESC(udp_port, "Destination UDP port");
 
 static bool log_ecn_error = true;
 module_param(log_ecn_error, bool, 0644);
 MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
 
-/* per-net private data for this module */
-static unsigned int vxlan_net_id;
-struct vxlan_net {
-       struct socket     *sock;        /* UDP encap socket */
+static int vxlan_net_id;
+
+static const u8 all_zeros_mac[ETH_ALEN];
+
+/* per UDP socket information */
+struct vxlan_sock {
+       struct hlist_node hlist;
+       struct rcu_head   rcu;
+       struct work_struct del_work;
+       atomic_t          refcnt;
+       struct socket     *sock;
        struct hlist_head vni_list[VNI_HASH_SIZE];
 };
 
+/* per-network namespace private data for this module */
+struct vxlan_net {
+       struct list_head  vxlan_list;
+       struct hlist_head sock_list[PORT_HASH_SIZE];
+       spinlock_t        sock_lock;
+};
+
 struct vxlan_rdst {
-       struct rcu_head          rcu;
        __be32                   remote_ip;
        __be16                   remote_port;
        u32                      remote_vni;
        u32                      remote_ifindex;
-       struct vxlan_rdst       *remote_next;
+       struct list_head         list;
+       struct rcu_head          rcu;
 };
 
 /* Forwarding table entry */
@@ -98,7 +114,7 @@ struct vxlan_fdb {
        struct rcu_head   rcu;
        unsigned long     updated;      /* jiffies */
        unsigned long     used;
-       struct vxlan_rdst remote;
+       struct list_head  remotes;
        u16               state;        /* see ndm_state */
        u8                flags;        /* see ndm_flags */
        u8                eth_addr[ETH_ALEN];
@@ -106,7 +122,9 @@ struct vxlan_fdb {
 
 /* Pseudo network device */
 struct vxlan_dev {
-       struct hlist_node hlist;
+       struct hlist_node hlist;        /* vni hash table */
+       struct list_head  next;         /* vxlan's per namespace list */
+       struct vxlan_sock *vn_sock;     /* listening socket */
        struct net_device *dev;
        struct vxlan_rdst default_dst;  /* default destination */
        __be32            saddr;        /* source address */
@@ -117,6 +135,9 @@ struct vxlan_dev {
        __u8              ttl;
        u32               flags;        /* VXLAN_F_* below */
 
+       struct work_struct sock_work;
+       struct work_struct igmp_work;
+
        unsigned long     age_interval;
        struct timer_list age_timer;
        spinlock_t        hash_lock;
@@ -134,20 +155,55 @@ struct vxlan_dev {
 
 /* salt for hash table */
 static u32 vxlan_salt __read_mostly;
+static struct workqueue_struct *vxlan_wq;
+
+static void vxlan_sock_work(struct work_struct *work);
+
+/* Virtual Network hash table head */
+static inline struct hlist_head *vni_head(struct vxlan_sock *vs, u32 id)
+{
+       return &vs->vni_list[hash_32(id, VNI_HASH_BITS)];
+}
 
-static inline struct hlist_head *vni_head(struct net *net, u32 id)
+/* Socket hash table head */
+static inline struct hlist_head *vs_head(struct net *net, __be16 port)
 {
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
 
-       return &vn->vni_list[hash_32(id, VNI_HASH_BITS)];
+       return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)];
+}
+
+/* First remote destination for a forwarding entry.
+ * Guaranteed to be non-NULL because remotes are never deleted.
+ */
+static inline struct vxlan_rdst *first_remote(struct vxlan_fdb *fdb)
+{
+       return list_first_or_null_rcu(&fdb->remotes, struct vxlan_rdst, list);
+}
+
+/* Find VXLAN socket based on network namespace and UDP port */
+static struct vxlan_sock *vxlan_find_port(struct net *net, __be16 port)
+{
+       struct vxlan_sock *vs;
+
+       hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {
+               if (inet_sk(vs->sock->sk)->inet_sport == port)
+                       return vs;
+       }
+       return NULL;
 }
 
 /* Look up VNI in a per net namespace table */
-static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id)
+static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, __be16 port)
 {
+       struct vxlan_sock *vs;
        struct vxlan_dev *vxlan;
 
-       hlist_for_each_entry_rcu(vxlan, vni_head(net, id), hlist) {
+       vs = vxlan_find_port(net, port);
+       if (!vs)
+               return NULL;
+
+       hlist_for_each_entry_rcu(vxlan, vni_head(vs, id), hlist) {
                if (vxlan->default_dst.remote_vni == id)
                        return vxlan;
        }
@@ -157,9 +213,9 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id)
 
 /* Fill in neighbour message in skbuff. */
 static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
-                          const struct vxlan_fdb *fdb,
-                          u32 portid, u32 seq, int type, unsigned int flags,
-                          const struct vxlan_rdst *rdst)
+                         const struct vxlan_fdb *fdb,
+                         u32 portid, u32 seq, int type, unsigned int flags,
+                         const struct vxlan_rdst *rdst)
 {
        unsigned long now = jiffies;
        struct nda_cacheinfo ci;
@@ -197,7 +253,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
            nla_put_be16(skb, NDA_PORT, rdst->remote_port))
                goto nla_put_failure;
        if (rdst->remote_vni != vxlan->default_dst.remote_vni &&
-           nla_put_be32(skb, NDA_VNI, rdst->remote_vni))
+           nla_put_u32(skb, NDA_VNI, rdst->remote_vni))
                goto nla_put_failure;
        if (rdst->remote_ifindex &&
            nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex))
@@ -230,7 +286,7 @@ static inline size_t vxlan_nlmsg_size(void)
 }
 
 static void vxlan_fdb_notify(struct vxlan_dev *vxlan,
-                            const struct vxlan_fdb *fdb, int type)
+                            struct vxlan_fdb *fdb, int type)
 {
        struct net *net = dev_net(vxlan->dev);
        struct sk_buff *skb;
@@ -240,7 +296,7 @@ static void vxlan_fdb_notify(struct vxlan_dev *vxlan,
        if (skb == NULL)
                goto errout;
 
-       err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, &fdb->remote);
+       err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, first_remote(fdb));
        if (err < 0) {
                /* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */
                WARN_ON(err == -EMSGSIZE);
@@ -258,22 +314,27 @@ errout:
 static void vxlan_ip_miss(struct net_device *dev, __be32 ipa)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
-       struct vxlan_fdb f;
+       struct vxlan_fdb f = {
+               .state = NUD_STALE,
+       };
+       struct vxlan_rdst remote = {
+               .remote_ip = ipa, /* goes to NDA_DST */
+               .remote_vni = VXLAN_N_VID,
+       };
 
-       memset(&f, 0, sizeof f);
-       f.state = NUD_STALE;
-       f.remote.remote_ip = ipa; /* goes to NDA_DST */
-       f.remote.remote_vni = VXLAN_N_VID;
+       INIT_LIST_HEAD(&f.remotes);
+       list_add_rcu(&remote.list, &f.remotes);
 
        vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH);
 }
 
 static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
 {
-       struct vxlan_fdb        f;
+       struct vxlan_fdb f = {
+               .state = NUD_STALE,
+       };
 
-       memset(&f, 0, sizeof f);
-       f.state = NUD_STALE;
+       INIT_LIST_HEAD(&f.remotes);
        memcpy(f.eth_addr, eth_addr, ETH_ALEN);
 
        vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH);
@@ -328,21 +389,34 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
        return f;
 }
 
-/* Add/update destinations for multicast */
-static int vxlan_fdb_append(struct vxlan_fdb *f,
-                           __be32 ip, __be16 port, __u32 vni, __u32 ifindex)
+/* caller should hold vxlan->hash_lock */
+static struct vxlan_rdst *vxlan_fdb_find_rdst(struct vxlan_fdb *f,
+                                             __be32 ip, __be16 port,
+                                             __u32 vni, __u32 ifindex)
 {
-       struct vxlan_rdst *rd_prev, *rd;
+       struct vxlan_rdst *rd;
 
-       rd_prev = NULL;
-       for (rd = &f->remote; rd; rd = rd->remote_next) {
+       list_for_each_entry(rd, &f->remotes, list) {
                if (rd->remote_ip == ip &&
                    rd->remote_port == port &&
                    rd->remote_vni == vni &&
                    rd->remote_ifindex == ifindex)
-                       return 0;
-               rd_prev = rd;
+                       return rd;
        }
+
+       return NULL;
+}
+
+/* Add/update destinations for multicast */
+static int vxlan_fdb_append(struct vxlan_fdb *f,
+                           __be32 ip, __be16 port, __u32 vni, __u32 ifindex)
+{
+       struct vxlan_rdst *rd;
+
+       rd = vxlan_fdb_find_rdst(f, ip, port, vni, ifindex);
+       if (rd)
+               return 0;
+
        rd = kmalloc(sizeof(*rd), GFP_ATOMIC);
        if (rd == NULL)
                return -ENOBUFS;
@@ -350,8 +424,9 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
        rd->remote_port = port;
        rd->remote_vni = vni;
        rd->remote_ifindex = ifindex;
-       rd->remote_next = NULL;
-       rd_prev->remote_next = rd;
+
+       list_add_tail_rcu(&rd->list, &f->remotes);
+
        return 1;
 }
 
@@ -383,7 +458,8 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
                        notify = 1;
                }
                if ((flags & NLM_F_APPEND) &&
-                   is_multicast_ether_addr(f->eth_addr)) {
+                   (is_multicast_ether_addr(f->eth_addr) ||
+                    is_zero_ether_addr(f->eth_addr))) {
                        int rc = vxlan_fdb_append(f, ip, port, vni, ifindex);
 
                        if (rc < 0)
@@ -403,16 +479,14 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
                        return -ENOMEM;
 
                notify = 1;
-               f->remote.remote_ip = ip;
-               f->remote.remote_port = port;
-               f->remote.remote_vni = vni;
-               f->remote.remote_ifindex = ifindex;
-               f->remote.remote_next = NULL;
                f->state = state;
                f->flags = ndm_flags;
                f->updated = f->used = jiffies;
+               INIT_LIST_HEAD(&f->remotes);
                memcpy(f->eth_addr, mac, ETH_ALEN);
 
+               vxlan_fdb_append(f, ip, port, vni, ifindex);
+
                ++vxlan->addrcnt;
                hlist_add_head_rcu(&f->hlist,
                                   vxlan_fdb_head(vxlan, mac));
@@ -424,16 +498,19 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
        return 0;
 }
 
+static void vxlan_fdb_free_rdst(struct rcu_head *head)
+{
+       struct vxlan_rdst *rd = container_of(head, struct vxlan_rdst, rcu);
+       kfree(rd);
+}
+
 static void vxlan_fdb_free(struct rcu_head *head)
 {
        struct vxlan_fdb *f = container_of(head, struct vxlan_fdb, rcu);
+       struct vxlan_rdst *rd, *nd;
 
-       while (f->remote.remote_next) {
-               struct vxlan_rdst *rd = f->remote.remote_next;
-
-               f->remote.remote_next = rd->remote_next;
+       list_for_each_entry_safe(rd, nd, &f->remotes, list)
                kfree(rd);
-       }
        kfree(f);
 }
 
@@ -449,58 +526,77 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f)
        call_rcu(&f->rcu, vxlan_fdb_free);
 }
 
-/* Add static entry (via netlink) */
-static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
-                        struct net_device *dev,
-                        const unsigned char *addr, u16 flags)
+static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
+                          __be32 *ip, __be16 *port, u32 *vni, u32 *ifindex)
 {
-       struct vxlan_dev *vxlan = netdev_priv(dev);
        struct net *net = dev_net(vxlan->dev);
-       __be32 ip;
-       __be16 port;
-       u32 vni, ifindex;
-       int err;
 
-       if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_REACHABLE))) {
-               pr_info("RTM_NEWNEIGH with invalid state %#x\n",
-                       ndm->ndm_state);
-               return -EINVAL;
-       }
-
-       if (tb[NDA_DST] == NULL)
-               return -EINVAL;
-
-       if (nla_len(tb[NDA_DST]) != sizeof(__be32))
-               return -EAFNOSUPPORT;
+       if (tb[NDA_DST]) {
+               if (nla_len(tb[NDA_DST]) != sizeof(__be32))
+                       return -EAFNOSUPPORT;
 
-       ip = nla_get_be32(tb[NDA_DST]);
+               *ip = nla_get_be32(tb[NDA_DST]);
+       } else {
+               *ip = htonl(INADDR_ANY);
+       }
 
        if (tb[NDA_PORT]) {
                if (nla_len(tb[NDA_PORT]) != sizeof(__be16))
                        return -EINVAL;
-               port = nla_get_be16(tb[NDA_PORT]);
-       } else
-               port = vxlan->dst_port;
+               *port = nla_get_be16(tb[NDA_PORT]);
+       } else {
+               *port = vxlan->dst_port;
+       }
 
        if (tb[NDA_VNI]) {
                if (nla_len(tb[NDA_VNI]) != sizeof(u32))
                        return -EINVAL;
-               vni = nla_get_u32(tb[NDA_VNI]);
-       } else
-               vni = vxlan->default_dst.remote_vni;
+               *vni = nla_get_u32(tb[NDA_VNI]);
+       } else {
+               *vni = vxlan->default_dst.remote_vni;
+       }
 
        if (tb[NDA_IFINDEX]) {
                struct net_device *tdev;
 
                if (nla_len(tb[NDA_IFINDEX]) != sizeof(u32))
                        return -EINVAL;
-               ifindex = nla_get_u32(tb[NDA_IFINDEX]);
-               tdev = dev_get_by_index(net, ifindex);
+               *ifindex = nla_get_u32(tb[NDA_IFINDEX]);
+               tdev = dev_get_by_index(net, *ifindex);
                if (!tdev)
                        return -EADDRNOTAVAIL;
                dev_put(tdev);
-       } else
-               ifindex = 0;
+       } else {
+               *ifindex = 0;
+       }
+
+       return 0;
+}
+
+/* Add static entry (via netlink) */
+static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
+                        struct net_device *dev,
+                        const unsigned char *addr, u16 flags)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       /* struct net *net = dev_net(vxlan->dev); */
+       __be32 ip;
+       __be16 port;
+       u32 vni, ifindex;
+       int err;
+
+       if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_REACHABLE))) {
+               pr_info("RTM_NEWNEIGH with invalid state %#x\n",
+                       ndm->ndm_state);
+               return -EINVAL;
+       }
+
+       if (tb[NDA_DST] == NULL)
+               return -EINVAL;
+
+       err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &vni, &ifindex);
+       if (err)
+               return err;
 
        spin_lock_bh(&vxlan->hash_lock);
        err = vxlan_fdb_create(vxlan, addr, ip, ndm->ndm_state, flags,
@@ -517,14 +613,43 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_fdb *f;
-       int err = -ENOENT;
+       struct vxlan_rdst *rd = NULL;
+       __be32 ip;
+       __be16 port;
+       u32 vni, ifindex;
+       int err;
+
+       err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &vni, &ifindex);
+       if (err)
+               return err;
+
+       err = -ENOENT;
 
        spin_lock_bh(&vxlan->hash_lock);
        f = vxlan_find_mac(vxlan, addr);
-       if (f) {
-               vxlan_fdb_destroy(vxlan, f);
-               err = 0;
+       if (!f)
+               goto out;
+
+       if (ip != htonl(INADDR_ANY)) {
+               rd = vxlan_fdb_find_rdst(f, ip, port, vni, ifindex);
+               if (!rd)
+                       goto out;
+       }
+
+       err = 0;
+
+       /* remove a destination if it's not the only one on the list,
+        * otherwise destroy the fdb entry
+        */
+       if (rd && !list_is_singular(&f->remotes)) {
+               list_del_rcu(&rd->list);
+               call_rcu(&rd->rcu, vxlan_fdb_free_rdst);
+               goto out;
        }
+
+       vxlan_fdb_destroy(vxlan, f);
+
+out:
        spin_unlock_bh(&vxlan->hash_lock);
 
        return err;
@@ -543,23 +668,24 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
 
                hlist_for_each_entry_rcu(f, &vxlan->fdb_head[h], hlist) {
                        struct vxlan_rdst *rd;
-                       for (rd = &f->remote; rd; rd = rd->remote_next) {
-                               if (idx < cb->args[0])
-                                       goto skip;
 
+                       if (idx < cb->args[0])
+                               goto skip;
+
+                       list_for_each_entry_rcu(rd, &f->remotes, list) {
                                err = vxlan_fdb_info(skb, vxlan, f,
                                                     NETLINK_CB(cb->skb).portid,
                                                     cb->nlh->nlmsg_seq,
                                                     RTM_NEWNEIGH,
                                                     NLM_F_MULTI, rd);
                                if (err < 0)
-                                       break;
-skip:
-                               ++idx;
+                                       goto out;
                        }
+skip:
+                       ++idx;
                }
        }
-
+out:
        return idx;
 }
 
@@ -575,7 +701,9 @@ static bool vxlan_snoop(struct net_device *dev,
 
        f = vxlan_find_mac(vxlan, src_mac);
        if (likely(f)) {
-               if (likely(f->remote.remote_ip == src_ip))
+               struct vxlan_rdst *rdst = first_remote(f);
+
+               if (likely(rdst->remote_ip == src_ip))
                        return false;
 
                /* Don't migrate static entries, drop packets */
@@ -585,10 +713,11 @@ static bool vxlan_snoop(struct net_device *dev,
                if (net_ratelimit())
                        netdev_info(dev,
                                    "%pM migrated from %pI4 to %pI4\n",
-                                   src_mac, &f->remote.remote_ip, &src_ip);
+                                   src_mac, &rdst->remote_ip, &src_ip);
 
-               f->remote.remote_ip = src_ip;
+               rdst->remote_ip = src_ip;
                f->updated = jiffies;
+               vxlan_fdb_notify(vxlan, f, RTM_NEWNEIGH);
        } else {
                /* learned new entry */
                spin_lock(&vxlan->hash_lock);
@@ -609,78 +738,61 @@ static bool vxlan_snoop(struct net_device *dev,
 
 
 /* See if multicast group is already in use by other ID */
-static bool vxlan_group_used(struct vxlan_net *vn,
-                            const struct vxlan_dev *this)
+static bool vxlan_group_used(struct vxlan_net *vn, __be32 remote_ip)
 {
-       const struct vxlan_dev *vxlan;
-       unsigned h;
-
-       for (h = 0; h < VNI_HASH_SIZE; ++h)
-               hlist_for_each_entry(vxlan, &vn->vni_list[h], hlist) {
-                       if (vxlan == this)
-                               continue;
+       struct vxlan_dev *vxlan;
 
-                       if (!netif_running(vxlan->dev))
-                               continue;
+       list_for_each_entry(vxlan, &vn->vxlan_list, next) {
+               if (!netif_running(vxlan->dev))
+                       continue;
 
-                       if (vxlan->default_dst.remote_ip == this->default_dst.remote_ip)
-                               return true;
-               }
+               if (vxlan->default_dst.remote_ip == remote_ip)
+                       return true;
+       }
 
        return false;
 }
 
-/* kernel equivalent to IP_ADD_MEMBERSHIP */
-static int vxlan_join_group(struct net_device *dev)
+static void vxlan_sock_hold(struct vxlan_sock *vs)
 {
-       struct vxlan_dev *vxlan = netdev_priv(dev);
-       struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
-       struct sock *sk = vn->sock->sk;
-       struct ip_mreqn mreq = {
-               .imr_multiaddr.s_addr   = vxlan->default_dst.remote_ip,
-               .imr_ifindex            = vxlan->default_dst.remote_ifindex,
-       };
-       int err;
+       atomic_inc(&vs->refcnt);
+}
 
-       /* Already a member of group */
-       if (vxlan_group_used(vn, vxlan))
-               return 0;
+static void vxlan_sock_release(struct vxlan_net *vn, struct vxlan_sock *vs)
+{
+       if (!atomic_dec_and_test(&vs->refcnt))
+               return;
 
-       /* Need to drop RTNL to call multicast join */
-       rtnl_unlock();
-       lock_sock(sk);
-       err = ip_mc_join_group(sk, &mreq);
-       release_sock(sk);
-       rtnl_lock();
+       spin_lock(&vn->sock_lock);
+       hlist_del_rcu(&vs->hlist);
+       spin_unlock(&vn->sock_lock);
 
-       return err;
+       queue_work(vxlan_wq, &vs->del_work);
 }
 
-
-/* kernel equivalent to IP_DROP_MEMBERSHIP */
-static int vxlan_leave_group(struct net_device *dev)
+/* Callback to update multicast group membership.
+ * Scheduled when vxlan goes up/down.
+ */
+static void vxlan_igmp_work(struct work_struct *work)
 {
-       struct vxlan_dev *vxlan = netdev_priv(dev);
-       struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
-       int err = 0;
-       struct sock *sk = vn->sock->sk;
+       struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, igmp_work);
+       struct vxlan_net *vn = net_generic(dev_net(vxlan->dev), vxlan_net_id);
+       struct vxlan_sock *vs = vxlan->vn_sock;
+       struct sock *sk = vs->sock->sk;
        struct ip_mreqn mreq = {
                .imr_multiaddr.s_addr   = vxlan->default_dst.remote_ip,
                .imr_ifindex            = vxlan->default_dst.remote_ifindex,
        };
 
-       /* Only leave group when last vxlan is done. */
-       if (vxlan_group_used(vn, vxlan))
-               return 0;
-
-       /* Need to drop RTNL to call multicast leave */
-       rtnl_unlock();
        lock_sock(sk);
-       err = ip_mc_leave_group(sk, &mreq);
+       if (vxlan_group_used(vn, vxlan->default_dst.remote_ip))
+               ip_mc_join_group(sk, &mreq);
+       else
+               ip_mc_leave_group(sk, &mreq);
        release_sock(sk);
-       rtnl_lock();
 
-       return err;
+       vxlan_sock_release(vn, vs);
+       dev_put(vxlan->dev);
 }
 
 /* Callback from net/ipv4/udp.c to receive packets */
@@ -690,6 +802,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
        struct vxlanhdr *vxh;
        struct vxlan_dev *vxlan;
        struct pcpu_tstats *stats;
+       __be16 port;
        __u32 vni;
        int err;
 
@@ -713,9 +826,11 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 
        /* Is this VNI defined? */
        vni = ntohl(vxh->vx_vni) >> 8;
-       vxlan = vxlan_find_vni(sock_net(sk), vni);
+       port = inet_sk(sk)->inet_sport;
+       vxlan = vxlan_find_vni(sock_net(sk), vni, port);
        if (!vxlan) {
-               netdev_dbg(skb->dev, "unknown vni %d\n", vni);
+               netdev_dbg(skb->dev, "unknown vni %d port %u\n",
+                          vni, ntohs(port));
                goto drop;
        }
 
@@ -834,7 +949,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
                }
 
                f = vxlan_find_mac(vxlan, n->ha);
-               if (f && f->remote.remote_ip == htonl(INADDR_ANY)) {
+               if (f && first_remote(f)->remote_ip == htonl(INADDR_ANY)) {
                        /* bridge-local neighbor */
                        neigh_release(n);
                        goto out;
@@ -896,7 +1011,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
        return false;
 }
 
-static void vxlan_sock_free(struct sk_buff *skb)
+static void vxlan_sock_put(struct sk_buff *skb)
 {
        sock_put(skb->sk);
 }
@@ -904,13 +1019,13 @@ static void vxlan_sock_free(struct sk_buff *skb)
 /* On transmit, associate with the tunnel socket */
 static void vxlan_set_owner(struct net_device *dev, struct sk_buff *skb)
 {
-       struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
-       struct sock *sk = vn->sock->sk;
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct sock *sk = vxlan->vn_sock->sock->sk;
 
        skb_orphan(skb);
        sock_hold(sk);
        skb->sk = sk;
-       skb->destructor = vxlan_sock_free;
+       skb->destructor = vxlan_sock_put;
 }
 
 /* Compute source port for outgoing packet
@@ -976,21 +1091,21 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
        }
 }
 
-static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
-                                 struct vxlan_rdst *rdst, bool did_rsc)
+static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+                          struct vxlan_rdst *rdst, bool did_rsc)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct rtable *rt;
        const struct iphdr *old_iph;
-       struct iphdr *iph;
        struct vxlanhdr *vxh;
        struct udphdr *uh;
        struct flowi4 fl4;
        __be32 dst;
        __be16 src_port, dst_port;
-        u32 vni;
+       u32 vni;
        __be16 df = 0;
        __u8 tos, ttl;
+       int err;
 
        dst_port = rdst->remote_port ? rdst->remote_port : vxlan->dst_port;
        vni = rdst->remote_vni;
@@ -1000,7 +1115,7 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                if (did_rsc) {
                        /* short-circuited back to local bridge */
                        vxlan_encap_bypass(skb, vxlan, vxlan);
-                       return NETDEV_TX_OK;
+                       return;
                }
                goto drop;
        }
@@ -1052,19 +1167,12 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                struct vxlan_dev *dst_vxlan;
 
                ip_rt_put(rt);
-               dst_vxlan = vxlan_find_vni(dev_net(dev), vni);
+               dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port);
                if (!dst_vxlan)
                        goto tx_error;
                vxlan_encap_bypass(skb, vxlan, dst_vxlan);
-               return NETDEV_TX_OK;
+               return;
        }
-
-       memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
-       IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
-                             IPSKB_REROUTED);
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
-
        vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh));
        vxh->vx_flags = htonl(VXLAN_FLAGS);
        vxh->vx_vni = htonl(vni << 8);
@@ -1079,28 +1187,19 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
        uh->len = htons(skb->len);
        uh->check = 0;
 
-       __skb_push(skb, sizeof(*iph));
-       skb_reset_network_header(skb);
-       iph             = ip_hdr(skb);
-       iph->version    = 4;
-       iph->ihl        = sizeof(struct iphdr) >> 2;
-       iph->frag_off   = df;
-       iph->protocol   = IPPROTO_UDP;
-       iph->tos        = ip_tunnel_ecn_encap(tos, old_iph, skb);
-       iph->daddr      = dst;
-       iph->saddr      = fl4.saddr;
-       iph->ttl        = ttl ? : ip4_dst_hoplimit(&rt->dst);
-       tunnel_ip_select_ident(skb, old_iph, &rt->dst);
-
-       nf_reset(skb);
-
        vxlan_set_owner(dev, skb);
 
        if (handle_offloads(skb))
                goto drop;
 
-       iptunnel_xmit(skb, dev);
-       return NETDEV_TX_OK;
+       tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
+       ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
+
+       err = iptunnel_xmit(dev_net(dev), rt, skb, fl4.saddr, dst,
+                           IPPROTO_UDP, tos, ttl, df);
+       iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+
+       return;
 
 drop:
        dev->stats.tx_dropped++;
@@ -1110,7 +1209,6 @@ tx_error:
        dev->stats.tx_errors++;
 tx_free:
        dev_kfree_skb(skb);
-       return NETDEV_TX_OK;
 }
 
 /* Transmit local packets over Vxlan
@@ -1124,9 +1222,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct ethhdr *eth;
        bool did_rsc = false;
-       struct vxlan_rdst *rdst0, *rdst;
+       struct vxlan_rdst *rdst;
        struct vxlan_fdb *f;
-       int rc1, rc;
 
        skb_reset_mac_header(skb);
        eth = eth_hdr(skb);
@@ -1145,33 +1242,28 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 
        if (f == NULL) {
-               rdst0 = &vxlan->default_dst;
-
-               if (rdst0->remote_ip == htonl(INADDR_ANY) &&
-                   (vxlan->flags & VXLAN_F_L2MISS) &&
-                   !is_multicast_ether_addr(eth->h_dest))
-                       vxlan_fdb_miss(vxlan, eth->h_dest);
-       } else
-               rdst0 = &f->remote;
-
-       rc = NETDEV_TX_OK;
+               f = vxlan_find_mac(vxlan, all_zeros_mac);
+               if (f == NULL) {
+                       if ((vxlan->flags & VXLAN_F_L2MISS) &&
+                           !is_multicast_ether_addr(eth->h_dest))
+                               vxlan_fdb_miss(vxlan, eth->h_dest);
+
+                       dev->stats.tx_dropped++;
+                       dev_kfree_skb(skb);
+                       return NETDEV_TX_OK;
+               }
+       }
 
-       /* if there are multiple destinations, send copies */
-       for (rdst = rdst0->remote_next; rdst; rdst = rdst->remote_next) {
+       list_for_each_entry_rcu(rdst, &f->remotes, list) {
                struct sk_buff *skb1;
 
                skb1 = skb_clone(skb, GFP_ATOMIC);
-               if (skb1) {
-                       rc1 = vxlan_xmit_one(skb1, dev, rdst, did_rsc);
-                       if (rc == NETDEV_TX_OK)
-                               rc = rc1;
-               }
+               if (skb1)
+                       vxlan_xmit_one(skb1, dev, rdst, did_rsc);
        }
 
-       rc1 = vxlan_xmit_one(skb, dev, rdst0, did_rsc);
-       if (rc == NETDEV_TX_OK)
-               rc = rc1;
-       return rc;
+       dev_kfree_skb(skb);
+       return NETDEV_TX_OK;
 }
 
 /* Walk the forwarding table and purge stale entries */
@@ -1214,23 +1306,70 @@ static void vxlan_cleanup(unsigned long arg)
 /* Setup stats when device is created */
 static int vxlan_init(struct net_device *dev)
 {
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
+       struct vxlan_sock *vs;
+       __u32 vni = vxlan->default_dst.remote_vni;
+
        dev->tstats = alloc_percpu(struct pcpu_tstats);
        if (!dev->tstats)
                return -ENOMEM;
 
+       spin_lock(&vn->sock_lock);
+       vs = vxlan_find_port(dev_net(dev), vxlan->dst_port);
+       if (vs) {
+               /* If we have a socket with same port already, reuse it */
+               atomic_inc(&vs->refcnt);
+               vxlan->vn_sock = vs;
+               hlist_add_head_rcu(&vxlan->hlist, vni_head(vs, vni));
+       } else {
+               /* otherwise make new socket outside of RTNL */
+               dev_hold(dev);
+               queue_work(vxlan_wq, &vxlan->sock_work);
+       }
+       spin_unlock(&vn->sock_lock);
+
        return 0;
 }
 
+static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan)
+{
+       struct vxlan_fdb *f;
+
+       spin_lock_bh(&vxlan->hash_lock);
+       f = __vxlan_find_mac(vxlan, all_zeros_mac);
+       if (f)
+               vxlan_fdb_destroy(vxlan, f);
+       spin_unlock_bh(&vxlan->hash_lock);
+}
+
+static void vxlan_uninit(struct net_device *dev)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
+       struct vxlan_sock *vs = vxlan->vn_sock;
+
+       vxlan_fdb_delete_default(vxlan);
+
+       if (vs)
+               vxlan_sock_release(vn, vs);
+       free_percpu(dev->tstats);
+}
+
 /* Start ageing timer and join group when device is brought up */
 static int vxlan_open(struct net_device *dev)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
-       int err;
+       struct vxlan_sock *vs = vxlan->vn_sock;
+
+       /* socket hasn't been created */
+       if (!vs)
+               return -ENOTCONN;
 
        if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip))) {
-               err = vxlan_join_group(dev);
-               if (err)
-                       return err;
+               vxlan_sock_hold(vs);
+               dev_hold(dev);
+               queue_work(vxlan_wq, &vxlan->igmp_work);
        }
 
        if (vxlan->age_interval)
@@ -1242,7 +1381,7 @@ static int vxlan_open(struct net_device *dev)
 /* Purge the forwarding table */
 static void vxlan_flush(struct vxlan_dev *vxlan)
 {
-       unsigned h;
+       unsigned int h;
 
        spin_lock_bh(&vxlan->hash_lock);
        for (h = 0; h < FDB_HASH_SIZE; ++h) {
@@ -1250,7 +1389,9 @@ static void vxlan_flush(struct vxlan_dev *vxlan)
                hlist_for_each_safe(p, n, &vxlan->fdb_head[h]) {
                        struct vxlan_fdb *f
                                = container_of(p, struct vxlan_fdb, hlist);
-                       vxlan_fdb_destroy(vxlan, f);
+                       /* the all_zeros_mac entry is deleted at vxlan_uninit */
+                       if (!is_zero_ether_addr(f->eth_addr))
+                               vxlan_fdb_destroy(vxlan, f);
                }
        }
        spin_unlock_bh(&vxlan->hash_lock);
@@ -1260,9 +1401,13 @@ static void vxlan_flush(struct vxlan_dev *vxlan)
 static int vxlan_stop(struct net_device *dev)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_sock *vs = vxlan->vn_sock;
 
-       if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip)))
-               vxlan_leave_group(dev);
+       if (vs && IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip))) {
+               vxlan_sock_hold(vs);
+               dev_hold(dev);
+               queue_work(vxlan_wq, &vxlan->igmp_work);
+       }
 
        del_timer_sync(&vxlan->age_timer);
 
@@ -1278,6 +1423,7 @@ static void vxlan_set_multicast_list(struct net_device *dev)
 
 static const struct net_device_ops vxlan_netdev_ops = {
        .ndo_init               = vxlan_init,
+       .ndo_uninit             = vxlan_uninit,
        .ndo_open               = vxlan_open,
        .ndo_stop               = vxlan_stop,
        .ndo_start_xmit         = vxlan_xmit,
@@ -1296,17 +1442,11 @@ static struct device_type vxlan_type = {
        .name = "vxlan",
 };
 
-static void vxlan_free(struct net_device *dev)
-{
-       free_percpu(dev->tstats);
-       free_netdev(dev);
-}
-
 /* Initialize the device structure. */
 static void vxlan_setup(struct net_device *dev)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
-       unsigned h;
+       unsigned int h;
        int low, high;
 
        eth_hw_addr_random(dev);
@@ -1314,7 +1454,7 @@ static void vxlan_setup(struct net_device *dev)
        dev->hard_header_len = ETH_HLEN + VXLAN_HEADROOM;
 
        dev->netdev_ops = &vxlan_netdev_ops;
-       dev->destructor = vxlan_free;
+       dev->destructor = free_netdev;
        SET_NETDEV_DEVTYPE(dev, &vxlan_type);
 
        dev->tx_queue_len = 0;
@@ -1329,7 +1469,10 @@ static void vxlan_setup(struct net_device *dev)
        dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
        dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
 
+       INIT_LIST_HEAD(&vxlan->next);
        spin_lock_init(&vxlan->hash_lock);
+       INIT_WORK(&vxlan->igmp_work, vxlan_igmp_work);
+       INIT_WORK(&vxlan->sock_work, vxlan_sock_work);
 
        init_timer_deferrable(&vxlan->age_timer);
        vxlan->age_timer.function = vxlan_cleanup;
@@ -1413,9 +1556,113 @@ static const struct ethtool_ops vxlan_ethtool_ops = {
        .get_link       = ethtool_op_get_link,
 };
 
+static void vxlan_del_work(struct work_struct *work)
+{
+       struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work);
+
+       sk_release_kernel(vs->sock->sk);
+       kfree_rcu(vs, rcu);
+}
+
+static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port)
+{
+       struct vxlan_sock *vs;
+       struct sock *sk;
+       struct sockaddr_in vxlan_addr = {
+               .sin_family = AF_INET,
+               .sin_addr.s_addr = htonl(INADDR_ANY),
+               .sin_port = port,
+       };
+       int rc;
+       unsigned int h;
+
+       vs = kmalloc(sizeof(*vs), GFP_KERNEL);
+       if (!vs)
+               return ERR_PTR(-ENOMEM);
+
+       for (h = 0; h < VNI_HASH_SIZE; ++h)
+               INIT_HLIST_HEAD(&vs->vni_list[h]);
+
+       INIT_WORK(&vs->del_work, vxlan_del_work);
+
+       /* Create UDP socket for encapsulation receive. */
+       rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &vs->sock);
+       if (rc < 0) {
+               pr_debug("UDP socket create failed\n");
+               kfree(vs);
+               return ERR_PTR(rc);
+       }
+
+       /* Put in proper namespace */
+       sk = vs->sock->sk;
+       sk_change_net(sk, net);
+
+       rc = kernel_bind(vs->sock, (struct sockaddr *) &vxlan_addr,
+                        sizeof(vxlan_addr));
+       if (rc < 0) {
+               pr_debug("bind for UDP socket %pI4:%u (%d)\n",
+                        &vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc);
+               sk_release_kernel(sk);
+               kfree(vs);
+               return ERR_PTR(rc);
+       }
+
+       /* Disable multicast loopback */
+       inet_sk(sk)->mc_loop = 0;
+
+       /* Mark socket as an encapsulation socket. */
+       udp_sk(sk)->encap_type = 1;
+       udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv;
+       udp_encap_enable();
+       atomic_set(&vs->refcnt, 1);
+
+       return vs;
+}
+
+/* Scheduled at device creation to bind to a socket */
+static void vxlan_sock_work(struct work_struct *work)
+{
+       struct vxlan_dev *vxlan
+               = container_of(work, struct vxlan_dev, sock_work);
+       struct net_device *dev = vxlan->dev;
+       struct net *net = dev_net(dev);
+       __u32 vni = vxlan->default_dst.remote_vni;
+       __be16 port = vxlan->dst_port;
+       struct vxlan_net *vn = net_generic(net, vxlan_net_id);
+       struct vxlan_sock *nvs, *ovs;
+
+       nvs = vxlan_socket_create(net, port);
+       if (IS_ERR(nvs)) {
+               netdev_err(vxlan->dev, "Can not create UDP socket, %ld\n",
+                          PTR_ERR(nvs));
+               goto out;
+       }
+
+       spin_lock(&vn->sock_lock);
+       /* Look again to see if can reuse socket */
+       ovs = vxlan_find_port(net, port);
+       if (ovs) {
+               atomic_inc(&ovs->refcnt);
+               vxlan->vn_sock = ovs;
+               hlist_add_head_rcu(&vxlan->hlist, vni_head(ovs, vni));
+               spin_unlock(&vn->sock_lock);
+
+               sk_release_kernel(nvs->sock->sk);
+               kfree(nvs);
+       } else {
+               vxlan->vn_sock = nvs;
+               hlist_add_head_rcu(&nvs->hlist, vs_head(net, port));
+               hlist_add_head_rcu(&vxlan->hlist, vni_head(nvs, vni));
+               spin_unlock(&vn->sock_lock);
+       }
+out:
+       dev_put(dev);
+}
+
 static int vxlan_newlink(struct net *net, struct net_device *dev,
                         struct nlattr *tb[], struct nlattr *data[])
 {
+       struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_rdst *dst = &vxlan->default_dst;
        __u32 vni;
@@ -1425,10 +1672,6 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
                return -EINVAL;
 
        vni = nla_get_u32(data[IFLA_VXLAN_ID]);
-       if (vxlan_find_vni(net, vni)) {
-               pr_info("duplicate VNI %u\n", vni);
-               return -EEXIST;
-       }
        dst->remote_vni = vni;
 
        if (data[IFLA_VXLAN_GROUP])
@@ -1494,13 +1737,32 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
        if (data[IFLA_VXLAN_PORT])
                vxlan->dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]);
 
+       if (vxlan_find_vni(net, vni, vxlan->dst_port)) {
+               pr_info("duplicate VNI %u\n", vni);
+               return -EEXIST;
+       }
+
        SET_ETHTOOL_OPS(dev, &vxlan_ethtool_ops);
 
+       /* create an fdb entry for default destination */
+       err = vxlan_fdb_create(vxlan, all_zeros_mac,
+                              vxlan->default_dst.remote_ip,
+                              NUD_REACHABLE|NUD_PERMANENT,
+                              NLM_F_EXCL|NLM_F_CREATE,
+                              vxlan->dst_port, vxlan->default_dst.remote_vni,
+                              vxlan->default_dst.remote_ifindex, NTF_SELF);
+       if (err)
+               return err;
+
        err = register_netdevice(dev);
-       if (!err)
-               hlist_add_head_rcu(&vxlan->hlist, vni_head(net, dst->remote_vni));
+       if (err) {
+               vxlan_fdb_delete_default(vxlan);
+               return err;
+       }
 
-       return err;
+       list_add(&vxlan->next, &vn->vxlan_list);
+
+       return 0;
 }
 
 static void vxlan_dellink(struct net_device *dev, struct list_head *head)
@@ -1508,7 +1770,7 @@ static void vxlan_dellink(struct net_device *dev, struct list_head *head)
        struct vxlan_dev *vxlan = netdev_priv(dev);
 
        hlist_del_rcu(&vxlan->hlist);
-
+       list_del(&vxlan->next);
        unregister_netdevice_queue(dev, head);
 }
 
@@ -1595,46 +1857,13 @@ static struct rtnl_link_ops vxlan_link_ops __read_mostly = {
 static __net_init int vxlan_init_net(struct net *net)
 {
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
-       struct sock *sk;
-       struct sockaddr_in vxlan_addr = {
-               .sin_family = AF_INET,
-               .sin_addr.s_addr = htonl(INADDR_ANY),
-       };
-       int rc;
-       unsigned h;
-
-       /* Create UDP socket for encapsulation receive. */
-       rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &vn->sock);
-       if (rc < 0) {
-               pr_debug("UDP socket create failed\n");
-               return rc;
-       }
-       /* Put in proper namespace */
-       sk = vn->sock->sk;
-       sk_change_net(sk, net);
-
-       vxlan_addr.sin_port = htons(vxlan_port);
-
-       rc = kernel_bind(vn->sock, (struct sockaddr *) &vxlan_addr,
-                        sizeof(vxlan_addr));
-       if (rc < 0) {
-               pr_debug("bind for UDP socket %pI4:%u (%d)\n",
-                        &vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc);
-               sk_release_kernel(sk);
-               vn->sock = NULL;
-               return rc;
-       }
-
-       /* Disable multicast loopback */
-       inet_sk(sk)->mc_loop = 0;
+       unsigned int h;
 
-       /* Mark socket as an encapsulation socket. */
-       udp_sk(sk)->encap_type = 1;
-       udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv;
-       udp_encap_enable();
+       INIT_LIST_HEAD(&vn->vxlan_list);
+       spin_lock_init(&vn->sock_lock);
 
-       for (h = 0; h < VNI_HASH_SIZE; ++h)
-               INIT_HLIST_HEAD(&vn->vni_list[h]);
+       for (h = 0; h < PORT_HASH_SIZE; ++h)
+               INIT_HLIST_HEAD(&vn->sock_list[h]);
 
        return 0;
 }
@@ -1643,18 +1872,11 @@ static __net_exit void vxlan_exit_net(struct net *net)
 {
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        struct vxlan_dev *vxlan;
-       unsigned h;
 
        rtnl_lock();
-       for (h = 0; h < VNI_HASH_SIZE; ++h)
-               hlist_for_each_entry(vxlan, &vn->vni_list[h], hlist)
-                       dev_close(vxlan->dev);
+       list_for_each_entry(vxlan, &vn->vxlan_list, next)
+               dev_close(vxlan->dev);
        rtnl_unlock();
-
-       if (vn->sock) {
-               sk_release_kernel(vn->sock->sk);
-               vn->sock = NULL;
-       }
 }
 
 static struct pernet_operations vxlan_net_ops = {
@@ -1668,6 +1890,10 @@ static int __init vxlan_init_module(void)
 {
        int rc;
 
+       vxlan_wq = alloc_workqueue("vxlan", 0, 0);
+       if (!vxlan_wq)
+               return -ENOMEM;
+
        get_random_bytes(&vxlan_salt, sizeof(vxlan_salt));
 
        rc = register_pernet_device(&vxlan_net_ops);
@@ -1683,14 +1909,16 @@ static int __init vxlan_init_module(void)
 out2:
        unregister_pernet_device(&vxlan_net_ops);
 out1:
+       destroy_workqueue(vxlan_wq);
        return rc;
 }
-module_init(vxlan_init_module);
+late_initcall(vxlan_init_module);
 
 static void __exit vxlan_cleanup_module(void)
 {
-       rtnl_link_unregister(&vxlan_link_ops);
        unregister_pernet_device(&vxlan_net_ops);
+       rtnl_link_unregister(&vxlan_link_ops);
+       destroy_workqueue(vxlan_wq);
        rcu_barrier();
 }
 module_exit(vxlan_cleanup_module);
index 6a8a382..0d1c759 100644 (file)
@@ -493,7 +493,7 @@ static void dlci_setup(struct net_device *dev)
 static int dlci_dev_event(struct notifier_block *unused,
                          unsigned long event, void *ptr)
 {
-       struct net_device *dev = (struct net_device *) ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (dev_net(dev) != &init_net)
                return NOTIFY_DONE;
index a0a932c..9c33ca9 100644 (file)
@@ -99,7 +99,7 @@ static inline void hdlc_proto_stop(struct net_device *dev)
 static int hdlc_device_event(struct notifier_block *this, unsigned long event,
                             void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        hdlc_device *hdlc;
        unsigned long flags;
        int on;
index fc9d11d..e7bbdb7 100644 (file)
@@ -1384,7 +1384,6 @@ static int hss_remove_one(struct platform_device *pdev)
        unregister_hdlc_device(port->netdev);
        free_netdev(port->netdev);
        npe_release(port->npe);
-       platform_set_drvdata(pdev, NULL);
        kfree(port);
        return 0;
 }
index a73b49e..a33a46f 100644 (file)
@@ -370,7 +370,7 @@ static int lapbeth_device_event(struct notifier_block *this,
                                unsigned long event, void *ptr)
 {
        struct lapbethdev *lapbeth;
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (dev_net(dev) != &init_net)
                return NOTIFY_DONE;
index f8f0156..200020e 100644 (file)
@@ -280,5 +280,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zd1211rw/Kconfig"
 source "drivers/net/wireless/mwifiex/Kconfig"
+source "drivers/net/wireless/cw1200/Kconfig"
 
 endif # WLAN
index 67156ef..0fab227 100644 (file)
@@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/
 
 obj-$(CONFIG_BRCMFMAC) += brcm80211/
 obj-$(CONFIG_BRCMSMAC) += brcm80211/
+
+obj-$(CONFIG_CW1200)   += cw1200/
index 2c02b4e..1abf1d4 100644 (file)
@@ -31,5 +31,6 @@ source "drivers/net/wireless/ath/carl9170/Kconfig"
 source "drivers/net/wireless/ath/ath6kl/Kconfig"
 source "drivers/net/wireless/ath/ar5523/Kconfig"
 source "drivers/net/wireless/ath/wil6210/Kconfig"
+source "drivers/net/wireless/ath/ath10k/Kconfig"
 
 endif
index 97b964d..fb05cfd 100644 (file)
@@ -4,6 +4,7 @@ obj-$(CONFIG_CARL9170)          += carl9170/
 obj-$(CONFIG_ATH6KL)           += ath6kl/
 obj-$(CONFIG_AR5523)           += ar5523/
 obj-$(CONFIG_WIL6210)          += wil6210/
+obj-$(CONFIG_ATH10K)           += ath10k/
 
 obj-$(CONFIG_ATH_COMMON)       += ath.o
 
index 4521342..daeafef 100644 (file)
@@ -239,13 +239,12 @@ enum ATH_DEBUG {
        ATH_DBG_CONFIG          = 0x00000200,
        ATH_DBG_FATAL           = 0x00000400,
        ATH_DBG_PS              = 0x00000800,
-       ATH_DBG_HWTIMER         = 0x00001000,
-       ATH_DBG_BTCOEX          = 0x00002000,
-       ATH_DBG_WMI             = 0x00004000,
-       ATH_DBG_BSTUCK          = 0x00008000,
-       ATH_DBG_MCI             = 0x00010000,
-       ATH_DBG_DFS             = 0x00020000,
-       ATH_DBG_WOW             = 0x00040000,
+       ATH_DBG_BTCOEX          = 0x00001000,
+       ATH_DBG_WMI             = 0x00002000,
+       ATH_DBG_BSTUCK          = 0x00004000,
+       ATH_DBG_MCI             = 0x00008000,
+       ATH_DBG_DFS             = 0x00010000,
+       ATH_DBG_WOW             = 0x00020000,
        ATH_DBG_ANY             = 0xffffffff
 };
 
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
new file mode 100644 (file)
index 0000000..cde58fe
--- /dev/null
@@ -0,0 +1,39 @@
+config ATH10K
+        tristate "Atheros 802.11ac wireless cards support"
+        depends on MAC80211
+       select ATH_COMMON
+        ---help---
+          This module adds support for wireless adapters based on
+          Atheros IEEE 802.11ac family of chipsets.
+
+          If you choose to build a module, it'll be called ath10k.
+
+config ATH10K_PCI
+       tristate "Atheros ath10k PCI support"
+       depends on ATH10K && PCI
+       ---help---
+         This module adds support for PCIE bus
+
+config ATH10K_DEBUG
+       bool "Atheros ath10k debugging"
+       depends on ATH10K
+       ---help---
+         Enables debug support
+
+         If unsure, say Y to make it easier to debug problems.
+
+config ATH10K_DEBUGFS
+       bool "Atheros ath10k debugfs support"
+       depends on ATH10K
+       ---help---
+         Enabled debugfs support
+
+         If unsure, say Y to make it easier to debug problems.
+
+config ATH10K_TRACING
+       bool "Atheros ath10k tracing support"
+       depends on ATH10K
+       depends on EVENT_TRACING
+       ---help---
+         Select this to ath10k use tracing infrastructure.
+
diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
new file mode 100644 (file)
index 0000000..a4179f4
--- /dev/null
@@ -0,0 +1,20 @@
+obj-$(CONFIG_ATH10K) += ath10k_core.o
+ath10k_core-y += mac.o \
+                debug.o \
+                core.o \
+                htc.o \
+                htt.o \
+                htt_rx.o \
+                htt_tx.o \
+                txrx.o \
+                wmi.o \
+                bmi.o
+
+ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o
+
+obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o
+ath10k_pci-y += pci.o \
+               ce.o
+
+# for tracing framework to find trace.h
+CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c
new file mode 100644 (file)
index 0000000..1a2ef51
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "bmi.h"
+#include "hif.h"
+#include "debug.h"
+#include "htc.h"
+
+int ath10k_bmi_done(struct ath10k *ar)
+{
+       struct bmi_cmd cmd;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__);
+               return 0;
+       }
+
+       ar->bmi.done_sent = true;
+       cmd.id = __cpu_to_le32(BMI_DONE);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
+       if (ret) {
+               ath10k_warn("unable to write to the device: %d\n", ret);
+               return ret;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n");
+       return 0;
+}
+
+int ath10k_bmi_get_target_info(struct ath10k *ar,
+                              struct bmi_target_info *target_info)
+{
+       struct bmi_cmd cmd;
+       union bmi_resp resp;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
+       u32 resplen = sizeof(resp.get_target_info);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("BMI Get Target Info Command disallowed\n");
+               return -EBUSY;
+       }
+
+       cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
+       if (ret) {
+               ath10k_warn("unable to get target info from device\n");
+               return ret;
+       }
+
+       if (resplen < sizeof(resp.get_target_info)) {
+               ath10k_warn("invalid get_target_info response length (%d)\n",
+                           resplen);
+               return -EIO;
+       }
+
+       target_info->version = __le32_to_cpu(resp.get_target_info.version);
+       target_info->type    = __le32_to_cpu(resp.get_target_info.type);
+       return 0;
+}
+
+int ath10k_bmi_read_memory(struct ath10k *ar,
+                          u32 address, void *buffer, u32 length)
+{
+       struct bmi_cmd cmd;
+       union bmi_resp resp;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);
+       u32 rxlen;
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
+                  __func__, ar, address, length);
+
+       while (length) {
+               rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
+
+               cmd.id            = __cpu_to_le32(BMI_READ_MEMORY);
+               cmd.read_mem.addr = __cpu_to_le32(address);
+               cmd.read_mem.len  = __cpu_to_le32(rxlen);
+
+               ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
+                                                 &resp, &rxlen);
+               if (ret) {
+                       ath10k_warn("unable to read from the device\n");
+                       return ret;
+               }
+
+               memcpy(buffer, resp.read_mem.payload, rxlen);
+               address += rxlen;
+               buffer  += rxlen;
+               length  -= rxlen;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_write_memory(struct ath10k *ar,
+                           u32 address, const void *buffer, u32 length)
+{
+       struct bmi_cmd cmd;
+       u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);
+       u32 txlen;
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
+                  __func__, ar, address, length);
+
+       while (length) {
+               txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
+
+               /* copy before roundup to avoid reading beyond buffer*/
+               memcpy(cmd.write_mem.payload, buffer, txlen);
+               txlen = roundup(txlen, 4);
+
+               cmd.id             = __cpu_to_le32(BMI_WRITE_MEMORY);
+               cmd.write_mem.addr = __cpu_to_le32(address);
+               cmd.write_mem.len  = __cpu_to_le32(txlen);
+
+               ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
+                                                 NULL, NULL);
+               if (ret) {
+                       ath10k_warn("unable to write to the device\n");
+                       return ret;
+               }
+
+               /* fixup roundup() so `length` zeroes out for last chunk */
+               txlen = min(txlen, length);
+
+               address += txlen;
+               buffer  += txlen;
+               length  -= txlen;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
+{
+       struct bmi_cmd cmd;
+       union bmi_resp resp;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);
+       u32 resplen = sizeof(resp.execute);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "%s: (device: 0x%p, address: 0x%x, param: %d)\n",
+                  __func__, ar, address, *param);
+
+       cmd.id            = __cpu_to_le32(BMI_EXECUTE);
+       cmd.execute.addr  = __cpu_to_le32(address);
+       cmd.execute.param = __cpu_to_le32(*param);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
+       if (ret) {
+               ath10k_warn("unable to read from the device\n");
+               return ret;
+       }
+
+       if (resplen < sizeof(resp.execute)) {
+               ath10k_warn("invalid execute response length (%d)\n",
+                           resplen);
+               return ret;
+       }
+
+       *param = __le32_to_cpu(resp.execute.result);
+       return 0;
+}
+
+int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
+{
+       struct bmi_cmd cmd;
+       u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);
+       u32 txlen;
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       while (length) {
+               txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
+
+               WARN_ON_ONCE(txlen & 3);
+
+               cmd.id          = __cpu_to_le32(BMI_LZ_DATA);
+               cmd.lz_data.len = __cpu_to_le32(txlen);
+               memcpy(cmd.lz_data.payload, buffer, txlen);
+
+               ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
+                                                 NULL, NULL);
+               if (ret) {
+                       ath10k_warn("unable to write to the device\n");
+                       return ret;
+               }
+
+               buffer += txlen;
+               length -= txlen;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
+{
+       struct bmi_cmd cmd;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       cmd.id            = __cpu_to_le32(BMI_LZ_STREAM_START);
+       cmd.lz_start.addr = __cpu_to_le32(address);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
+       if (ret) {
+               ath10k_warn("unable to Start LZ Stream to the device\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_fast_download(struct ath10k *ar,
+                            u32 address, const void *buffer, u32 length)
+{
+       u8 trailer[4] = {};
+       u32 head_len = rounddown(length, 4);
+       u32 trailer_len = length - head_len;
+       int ret;
+
+       ret = ath10k_bmi_lz_stream_start(ar, address);
+       if (ret)
+               return ret;
+
+       /* copy the last word into a zero padded buffer */
+       if (trailer_len > 0)
+               memcpy(trailer, buffer + head_len, trailer_len);
+
+       ret = ath10k_bmi_lz_data(ar, buffer, head_len);
+       if (ret)
+               return ret;
+
+       if (trailer_len > 0)
+               ret = ath10k_bmi_lz_data(ar, trailer, 4);
+
+       if (ret != 0)
+               return ret;
+
+       /*
+        * Close compressed stream and open a new (fake) one.
+        * This serves mainly to flush Target caches.
+        */
+       ret = ath10k_bmi_lz_stream_start(ar, 0x00);
+
+       return ret;
+}
diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h
new file mode 100644 (file)
index 0000000..32c56aa
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BMI_H_
+#define _BMI_H_
+
+#include "core.h"
+
+/*
+ * Bootloader Messaging Interface (BMI)
+ *
+ * BMI is a very simple messaging interface used during initialization
+ * to read memory, write memory, execute code, and to define an
+ * application entry PC.
+ *
+ * It is used to download an application to QCA988x, to provide
+ * patches to code that is already resident on QCA988x, and generally
+ * to examine and modify state.  The Host has an opportunity to use
+ * BMI only once during bootup.  Once the Host issues a BMI_DONE
+ * command, this opportunity ends.
+ *
+ * The Host writes BMI requests to mailbox0, and reads BMI responses
+ * from mailbox0.   BMI requests all begin with a command
+ * (see below for specific commands), and are followed by
+ * command-specific data.
+ *
+ * Flow control:
+ * The Host can only issue a command once the Target gives it a
+ * "BMI Command Credit", using AR8K Counter #4.  As soon as the
+ * Target has completed a command, it issues another BMI Command
+ * Credit (so the Host can issue the next command).
+ *
+ * BMI handles all required Target-side cache flushing.
+ */
+
+/* Maximum data size used for BMI transfers */
+#define BMI_MAX_DATA_SIZE      256
+
+/* len = cmd + addr + length */
+#define BMI_MAX_CMDBUF_SIZE (BMI_MAX_DATA_SIZE + \
+                       sizeof(u32) + \
+                       sizeof(u32) + \
+                       sizeof(u32))
+
+/* BMI Commands */
+
+enum bmi_cmd_id {
+       BMI_NO_COMMAND          = 0,
+       BMI_DONE                = 1,
+       BMI_READ_MEMORY         = 2,
+       BMI_WRITE_MEMORY        = 3,
+       BMI_EXECUTE             = 4,
+       BMI_SET_APP_START       = 5,
+       BMI_READ_SOC_REGISTER   = 6,
+       BMI_READ_SOC_WORD       = 6,
+       BMI_WRITE_SOC_REGISTER  = 7,
+       BMI_WRITE_SOC_WORD      = 7,
+       BMI_GET_TARGET_ID       = 8,
+       BMI_GET_TARGET_INFO     = 8,
+       BMI_ROMPATCH_INSTALL    = 9,
+       BMI_ROMPATCH_UNINSTALL  = 10,
+       BMI_ROMPATCH_ACTIVATE   = 11,
+       BMI_ROMPATCH_DEACTIVATE = 12,
+       BMI_LZ_STREAM_START     = 13, /* should be followed by LZ_DATA */
+       BMI_LZ_DATA             = 14,
+       BMI_NVRAM_PROCESS       = 15,
+};
+
+#define BMI_NVRAM_SEG_NAME_SZ 16
+
+struct bmi_cmd {
+       __le32 id; /* enum bmi_cmd_id */
+       union {
+               struct {
+               } done;
+               struct {
+                       __le32 addr;
+                       __le32 len;
+               } read_mem;
+               struct {
+                       __le32 addr;
+                       __le32 len;
+                       u8 payload[0];
+               } write_mem;
+               struct {
+                       __le32 addr;
+                       __le32 param;
+               } execute;
+               struct {
+                       __le32 addr;
+               } set_app_start;
+               struct {
+                       __le32 addr;
+               } read_soc_reg;
+               struct {
+                       __le32 addr;
+                       __le32 value;
+               } write_soc_reg;
+               struct {
+               } get_target_info;
+               struct {
+                       __le32 rom_addr;
+                       __le32 ram_addr; /* or value */
+                       __le32 size;
+                       __le32 activate; /* 0=install, but dont activate */
+               } rompatch_install;
+               struct {
+                       __le32 patch_id;
+               } rompatch_uninstall;
+               struct {
+                       __le32 count;
+                       __le32 patch_ids[0]; /* length of @count */
+               } rompatch_activate;
+               struct {
+                       __le32 count;
+                       __le32 patch_ids[0]; /* length of @count */
+               } rompatch_deactivate;
+               struct {
+                       __le32 addr;
+               } lz_start;
+               struct {
+                       __le32 len; /* max BMI_MAX_DATA_SIZE */
+                       u8 payload[0]; /* length of @len */
+               } lz_data;
+               struct {
+                       u8 name[BMI_NVRAM_SEG_NAME_SZ];
+               } nvram_process;
+               u8 payload[BMI_MAX_CMDBUF_SIZE];
+       };
+} __packed;
+
+union bmi_resp {
+       struct {
+               u8 payload[0];
+       } read_mem;
+       struct {
+               __le32 result;
+       } execute;
+       struct {
+               __le32 value;
+       } read_soc_reg;
+       struct {
+               __le32 len;
+               __le32 version;
+               __le32 type;
+       } get_target_info;
+       struct {
+               __le32 patch_id;
+       } rompatch_install;
+       struct {
+               __le32 patch_id;
+       } rompatch_uninstall;
+       struct {
+               /* 0 = nothing executed
+                * otherwise = NVRAM segment return value */
+               __le32 result;
+       } nvram_process;
+       u8 payload[BMI_MAX_CMDBUF_SIZE];
+} __packed;
+
+struct bmi_target_info {
+       u32 version;
+       u32 type;
+};
+
+
+/* in msec */
+#define BMI_COMMUNICATION_TIMEOUT_HZ (1*HZ)
+
+#define BMI_CE_NUM_TO_TARG 0
+#define BMI_CE_NUM_TO_HOST 1
+
+int ath10k_bmi_done(struct ath10k *ar);
+int ath10k_bmi_get_target_info(struct ath10k *ar,
+                              struct bmi_target_info *target_info);
+int ath10k_bmi_read_memory(struct ath10k *ar, u32 address,
+                          void *buffer, u32 length);
+int ath10k_bmi_write_memory(struct ath10k *ar, u32 address,
+                           const void *buffer, u32 length);
+
+#define ath10k_bmi_read32(ar, item, val)                               \
+       ({                                                              \
+               int ret;                                                \
+               u32 addr;                                               \
+               __le32 tmp;                                             \
+                                                                       \
+               addr = host_interest_item_address(HI_ITEM(item));       \
+               ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \
+               *val = __le32_to_cpu(tmp);                              \
+               ret;                                                    \
+        })
+
+#define ath10k_bmi_write32(ar, item, val)                              \
+       ({                                                              \
+               int ret;                                                \
+               u32 address;                                            \
+               __le32 v = __cpu_to_le32(val);                          \
+                                                                       \
+               address = host_interest_item_address(HI_ITEM(item));    \
+               ret = ath10k_bmi_write_memory(ar, address,              \
+                                             (u8 *)&v, sizeof(v));     \
+               ret;                                                    \
+       })
+
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param);
+int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address);
+int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length);
+int ath10k_bmi_fast_download(struct ath10k *ar, u32 address,
+                            const void *buffer, u32 length);
+#endif /* _BMI_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
new file mode 100644 (file)
index 0000000..61a8ac7
--- /dev/null
@@ -0,0 +1,1189 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "hif.h"
+#include "pci.h"
+#include "ce.h"
+#include "debug.h"
+
+/*
+ * Support for Copy Engine hardware, which is mainly used for
+ * communication between Host and Target over a PCIe interconnect.
+ */
+
+/*
+ * A single CopyEngine (CE) comprises two "rings":
+ *   a source ring
+ *   a destination ring
+ *
+ * Each ring consists of a number of descriptors which specify
+ * an address, length, and meta-data.
+ *
+ * Typically, one side of the PCIe interconnect (Host or Target)
+ * controls one ring and the other side controls the other ring.
+ * The source side chooses when to initiate a transfer and it
+ * chooses what to send (buffer address, length). The destination
+ * side keeps a supply of "anonymous receive buffers" available and
+ * it handles incoming data as it arrives (when the destination
+ * recieves an interrupt).
+ *
+ * The sender may send a simple buffer (address/length) or it may
+ * send a small list of buffers.  When a small list is sent, hardware
+ * "gathers" these and they end up in a single destination buffer
+ * with a single interrupt.
+ *
+ * There are several "contexts" managed by this layer -- more, it
+ * may seem -- than should be needed. These are provided mainly for
+ * maximum flexibility and especially to facilitate a simpler HIF
+ * implementation. There are per-CopyEngine recv, send, and watermark
+ * contexts. These are supplied by the caller when a recv, send,
+ * or watermark handler is established and they are echoed back to
+ * the caller when the respective callbacks are invoked. There is
+ * also a per-transfer context supplied by the caller when a buffer
+ * (or sendlist) is sent and when a buffer is enqueued for recv.
+ * These per-transfer contexts are echoed back to the caller when
+ * the buffer is sent/received.
+ */
+
+static inline void ath10k_ce_dest_ring_write_index_set(struct ath10k *ar,
+                                                      u32 ce_ctrl_addr,
+                                                      unsigned int n)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS, n);
+}
+
+static inline u32 ath10k_ce_dest_ring_write_index_get(struct ath10k *ar,
+                                                     u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS);
+}
+
+static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar,
+                                                     u32 ce_ctrl_addr,
+                                                     unsigned int n)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       void __iomem *indicator_addr;
+
+       if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+               ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
+               return;
+       }
+
+       /* workaround for QCA988x_1.0 HW CE */
+       indicator_addr = ar_pci->mem + ce_ctrl_addr + DST_WATERMARK_ADDRESS;
+
+       if (ce_ctrl_addr == ath10k_ce_base_address(CDC_WAR_DATA_CE)) {
+               iowrite32((CDC_WAR_MAGIC_STR | n), indicator_addr);
+       } else {
+               unsigned long irq_flags;
+               local_irq_save(irq_flags);
+               iowrite32(1, indicator_addr);
+
+               /*
+                * PCIE write waits for ACK in IPQ8K, there is no
+                * need to read back value.
+                */
+               (void)ioread32(indicator_addr);
+               (void)ioread32(indicator_addr); /* conservative */
+
+               ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
+
+               iowrite32(0, indicator_addr);
+               local_irq_restore(irq_flags);
+       }
+}
+
+static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS);
+}
+
+static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_SRRI_ADDRESS);
+}
+
+static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr,
+                                                   unsigned int addr)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + SR_BA_ADDRESS, addr);
+}
+
+static inline void ath10k_ce_src_ring_size_set(struct ath10k *ar,
+                                              u32 ce_ctrl_addr,
+                                              unsigned int n)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + SR_SIZE_ADDRESS, n);
+}
+
+static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar,
+                                              u32 ce_ctrl_addr,
+                                              unsigned int n)
+{
+       u32 ctrl1_addr = ath10k_pci_read32((ar),
+                                          (ce_ctrl_addr) + CE_CTRL1_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+                          (ctrl1_addr &  ~CE_CTRL1_DMAX_LENGTH_MASK) |
+                          CE_CTRL1_DMAX_LENGTH_SET(n));
+}
+
+static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr,
+                                                   unsigned int n)
+{
+       u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+                          (ctrl1_addr & ~CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) |
+                          CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr,
+                                                    unsigned int n)
+{
+       u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+                          (ctrl1_addr & ~CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) |
+                          CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(n));
+}
+
+static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_DRRI_ADDRESS);
+}
+
+static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr,
+                                                    u32 addr)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + DR_BA_ADDRESS, addr);
+}
+
+static inline void ath10k_ce_dest_ring_size_set(struct ath10k *ar,
+                                               u32 ce_ctrl_addr,
+                                               unsigned int n)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + DR_SIZE_ADDRESS, n);
+}
+
+static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar,
+                                                  u32 ce_ctrl_addr,
+                                                  unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
+                          (addr & ~SRC_WATERMARK_HIGH_MASK) |
+                          SRC_WATERMARK_HIGH_SET(n));
+}
+
+static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar,
+                                                 u32 ce_ctrl_addr,
+                                                 unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
+                          (addr & ~SRC_WATERMARK_LOW_MASK) |
+                          SRC_WATERMARK_LOW_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr,
+                                                   unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
+                          (addr & ~DST_WATERMARK_HIGH_MASK) |
+                          DST_WATERMARK_HIGH_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar,
+                                                  u32 ce_ctrl_addr,
+                                                  unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
+                          (addr & ~DST_WATERMARK_LOW_MASK) |
+                          DST_WATERMARK_LOW_SET(n));
+}
+
+static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar,
+                                                       u32 ce_ctrl_addr)
+{
+       u32 host_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + HOST_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+                          host_ie_addr | HOST_IE_COPY_COMPLETE_MASK);
+}
+
+static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar,
+                                                       u32 ce_ctrl_addr)
+{
+       u32 host_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + HOST_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+                          host_ie_addr & ~HOST_IE_COPY_COMPLETE_MASK);
+}
+
+static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr)
+{
+       u32 host_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + HOST_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+                          host_ie_addr & ~CE_WATERMARK_MASK);
+}
+
+static inline void ath10k_ce_error_intr_enable(struct ath10k *ar,
+                                              u32 ce_ctrl_addr)
+{
+       u32 misc_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + MISC_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS,
+                          misc_ie_addr | CE_ERROR_MASK);
+}
+
+static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr,
+                                                    unsigned int mask)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IS_ADDRESS, mask);
+}
+
+
+/*
+ * Guts of ath10k_ce_send, used by both ath10k_ce_send and
+ * ath10k_ce_sendlist_send.
+ * The caller takes responsibility for any needed locking.
+ */
+static int ath10k_ce_send_nolock(struct ce_state *ce_state,
+                                void *per_transfer_context,
+                                u32 buffer,
+                                unsigned int nbytes,
+                                unsigned int transfer_id,
+                                unsigned int flags)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ce_ring_state *src_ring = ce_state->src_ring;
+       struct ce_desc *desc, *sdesc;
+       unsigned int nentries_mask = src_ring->nentries_mask;
+       unsigned int sw_index = src_ring->sw_index;
+       unsigned int write_index = src_ring->write_index;
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       u32 desc_flags = 0;
+       int ret = 0;
+
+       if (nbytes > ce_state->src_sz_max)
+               ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n",
+                           __func__, nbytes, ce_state->src_sz_max);
+
+       ath10k_pci_wake(ar);
+
+       if (unlikely(CE_RING_DELTA(nentries_mask,
+                                  write_index, sw_index - 1) <= 0)) {
+               ret = -EIO;
+               goto exit;
+       }
+
+       desc = CE_SRC_RING_TO_DESC(src_ring->base_addr_owner_space,
+                                  write_index);
+       sdesc = CE_SRC_RING_TO_DESC(src_ring->shadow_base, write_index);
+
+       desc_flags |= SM(transfer_id, CE_DESC_FLAGS_META_DATA);
+
+       if (flags & CE_SEND_FLAG_GATHER)
+               desc_flags |= CE_DESC_FLAGS_GATHER;
+       if (flags & CE_SEND_FLAG_BYTE_SWAP)
+               desc_flags |= CE_DESC_FLAGS_BYTE_SWAP;
+
+       sdesc->addr   = __cpu_to_le32(buffer);
+       sdesc->nbytes = __cpu_to_le16(nbytes);
+       sdesc->flags  = __cpu_to_le16(desc_flags);
+
+       *desc = *sdesc;
+
+       src_ring->per_transfer_context[write_index] = per_transfer_context;
+
+       /* Update Source Ring Write Index */
+       write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+
+       /* WORKAROUND */
+       if (!(flags & CE_SEND_FLAG_GATHER))
+               ath10k_ce_src_ring_write_index_set(ar, ctrl_addr, write_index);
+
+       src_ring->write_index = write_index;
+exit:
+       ath10k_pci_sleep(ar);
+       return ret;
+}
+
+int ath10k_ce_send(struct ce_state *ce_state,
+                  void *per_transfer_context,
+                  u32 buffer,
+                  unsigned int nbytes,
+                  unsigned int transfer_id,
+                  unsigned int flags)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
+                                   buffer, nbytes, transfer_id, flags);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, u32 buffer,
+                               unsigned int nbytes, u32 flags)
+{
+       unsigned int num_items = sendlist->num_items;
+       struct ce_sendlist_item *item;
+
+       item = &sendlist->item[num_items];
+       item->data = buffer;
+       item->u.nbytes = nbytes;
+       item->flags = flags;
+       sendlist->num_items++;
+}
+
+int ath10k_ce_sendlist_send(struct ce_state *ce_state,
+                           void *per_transfer_context,
+                           struct ce_sendlist *sendlist,
+                           unsigned int transfer_id)
+{
+       struct ce_ring_state *src_ring = ce_state->src_ring;
+       struct ce_sendlist_item *item;
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       unsigned int nentries_mask = src_ring->nentries_mask;
+       unsigned int num_items = sendlist->num_items;
+       unsigned int sw_index;
+       unsigned int write_index;
+       int i, delta, ret = -ENOMEM;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       sw_index = src_ring->sw_index;
+       write_index = src_ring->write_index;
+
+       delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
+
+       if (delta >= num_items) {
+               /*
+                * Handle all but the last item uniformly.
+                */
+               for (i = 0; i < num_items - 1; i++) {
+                       item = &sendlist->item[i];
+                       ret = ath10k_ce_send_nolock(ce_state,
+                                                   CE_SENDLIST_ITEM_CTXT,
+                                                   (u32) item->data,
+                                                   item->u.nbytes, transfer_id,
+                                                   item->flags |
+                                                   CE_SEND_FLAG_GATHER);
+                       if (ret)
+                               ath10k_warn("CE send failed for item: %d\n", i);
+               }
+               /*
+                * Provide valid context pointer for final item.
+                */
+               item = &sendlist->item[i];
+               ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
+                                           (u32) item->data, item->u.nbytes,
+                                           transfer_id, item->flags);
+               if (ret)
+                       ath10k_warn("CE send failed for last item: %d\n", i);
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+                              void *per_recv_context,
+                              u32 buffer)
+{
+       struct ce_ring_state *dest_ring = ce_state->dest_ring;
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       unsigned int nentries_mask = dest_ring->nentries_mask;
+       unsigned int write_index;
+       unsigned int sw_index;
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       write_index = dest_ring->write_index;
+       sw_index = dest_ring->sw_index;
+
+       ath10k_pci_wake(ar);
+
+       if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) {
+               struct ce_desc *base = dest_ring->base_addr_owner_space;
+               struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index);
+
+               /* Update destination descriptor */
+               desc->addr    = __cpu_to_le32(buffer);
+               desc->nbytes = 0;
+
+               dest_ring->per_transfer_context[write_index] =
+                                                       per_recv_context;
+
+               /* Update Destination Ring Write Index */
+               write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+               ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index);
+               dest_ring->write_index = write_index;
+               ret = 0;
+       } else {
+               ret = -EIO;
+       }
+       ath10k_pci_sleep(ar);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+/*
+ * Guts of ath10k_ce_completed_recv_next.
+ * The caller takes responsibility for any necessary locking.
+ */
+static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state,
+                                               void **per_transfer_contextp,
+                                               u32 *bufferp,
+                                               unsigned int *nbytesp,
+                                               unsigned int *transfer_idp,
+                                               unsigned int *flagsp)
+{
+       struct ce_ring_state *dest_ring = ce_state->dest_ring;
+       unsigned int nentries_mask = dest_ring->nentries_mask;
+       unsigned int sw_index = dest_ring->sw_index;
+
+       struct ce_desc *base = dest_ring->base_addr_owner_space;
+       struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index);
+       struct ce_desc sdesc;
+       u16 nbytes;
+
+       /* Copy in one go for performance reasons */
+       sdesc = *desc;
+
+       nbytes = __le16_to_cpu(sdesc.nbytes);
+       if (nbytes == 0) {
+               /*
+                * This closes a relatively unusual race where the Host
+                * sees the updated DRRI before the update to the
+                * corresponding descriptor has completed. We treat this
+                * as a descriptor that is not yet done.
+                */
+               return -EIO;
+       }
+
+       desc->nbytes = 0;
+
+       /* Return data from completed destination descriptor */
+       *bufferp = __le32_to_cpu(sdesc.addr);
+       *nbytesp = nbytes;
+       *transfer_idp = MS(__le16_to_cpu(sdesc.flags), CE_DESC_FLAGS_META_DATA);
+
+       if (__le16_to_cpu(sdesc.flags) & CE_DESC_FLAGS_BYTE_SWAP)
+               *flagsp = CE_RECV_FLAG_SWAPPED;
+       else
+               *flagsp = 0;
+
+       if (per_transfer_contextp)
+               *per_transfer_contextp =
+                       dest_ring->per_transfer_context[sw_index];
+
+       /* sanity */
+       dest_ring->per_transfer_context[sw_index] = NULL;
+
+       /* Update sw_index */
+       sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+       dest_ring->sw_index = sw_index;
+
+       return 0;
+}
+
+int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+                                 void **per_transfer_contextp,
+                                 u32 *bufferp,
+                                 unsigned int *nbytesp,
+                                 unsigned int *transfer_idp,
+                                 unsigned int *flagsp)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ret = ath10k_ce_completed_recv_next_nolock(ce_state,
+                                                  per_transfer_contextp,
+                                                  bufferp, nbytesp,
+                                                  transfer_idp, flagsp);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp)
+{
+       struct ce_ring_state *dest_ring;
+       unsigned int nentries_mask;
+       unsigned int sw_index;
+       unsigned int write_index;
+       int ret;
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+
+       dest_ring = ce_state->dest_ring;
+
+       if (!dest_ring)
+               return -EIO;
+
+       ar = ce_state->ar;
+       ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       nentries_mask = dest_ring->nentries_mask;
+       sw_index = dest_ring->sw_index;
+       write_index = dest_ring->write_index;
+       if (write_index != sw_index) {
+               struct ce_desc *base = dest_ring->base_addr_owner_space;
+               struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index);
+
+               /* Return data from completed destination descriptor */
+               *bufferp = __le32_to_cpu(desc->addr);
+
+               if (per_transfer_contextp)
+                       *per_transfer_contextp =
+                               dest_ring->per_transfer_context[sw_index];
+
+               /* sanity */
+               dest_ring->per_transfer_context[sw_index] = NULL;
+
+               /* Update sw_index */
+               sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+               dest_ring->sw_index = sw_index;
+               ret = 0;
+       } else {
+               ret = -EIO;
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+/*
+ * Guts of ath10k_ce_completed_send_next.
+ * The caller takes responsibility for any necessary locking.
+ */
+static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state,
+                                               void **per_transfer_contextp,
+                                               u32 *bufferp,
+                                               unsigned int *nbytesp,
+                                               unsigned int *transfer_idp)
+{
+       struct ce_ring_state *src_ring = ce_state->src_ring;
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       struct ath10k *ar = ce_state->ar;
+       unsigned int nentries_mask = src_ring->nentries_mask;
+       unsigned int sw_index = src_ring->sw_index;
+       unsigned int read_index;
+       int ret = -EIO;
+
+       if (src_ring->hw_index == sw_index) {
+               /*
+                * The SW completion index has caught up with the cached
+                * version of the HW completion index.
+                * Update the cached HW completion index to see whether
+                * the SW has really caught up to the HW, or if the cached
+                * value of the HW index has become stale.
+                */
+               ath10k_pci_wake(ar);
+               src_ring->hw_index =
+                       ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+               ath10k_pci_sleep(ar);
+       }
+       read_index = src_ring->hw_index;
+
+       if ((read_index != sw_index) && (read_index != 0xffffffff)) {
+               struct ce_desc *sbase = src_ring->shadow_base;
+               struct ce_desc *sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index);
+
+               /* Return data from completed source descriptor */
+               *bufferp = __le32_to_cpu(sdesc->addr);
+               *nbytesp = __le16_to_cpu(sdesc->nbytes);
+               *transfer_idp = MS(__le16_to_cpu(sdesc->flags),
+                                               CE_DESC_FLAGS_META_DATA);
+
+               if (per_transfer_contextp)
+                       *per_transfer_contextp =
+                               src_ring->per_transfer_context[sw_index];
+
+               /* sanity */
+               src_ring->per_transfer_context[sw_index] = NULL;
+
+               /* Update sw_index */
+               sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+               src_ring->sw_index = sw_index;
+               ret = 0;
+       }
+
+       return ret;
+}
+
+/* NB: Modeled after ath10k_ce_completed_send_next */
+int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp,
+                              unsigned int *nbytesp,
+                              unsigned int *transfer_idp)
+{
+       struct ce_ring_state *src_ring;
+       unsigned int nentries_mask;
+       unsigned int sw_index;
+       unsigned int write_index;
+       int ret;
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+
+       src_ring = ce_state->src_ring;
+
+       if (!src_ring)
+               return -EIO;
+
+       ar = ce_state->ar;
+       ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       nentries_mask = src_ring->nentries_mask;
+       sw_index = src_ring->sw_index;
+       write_index = src_ring->write_index;
+
+       if (write_index != sw_index) {
+               struct ce_desc *base = src_ring->base_addr_owner_space;
+               struct ce_desc *desc = CE_SRC_RING_TO_DESC(base, sw_index);
+
+               /* Return data from completed source descriptor */
+               *bufferp = __le32_to_cpu(desc->addr);
+               *nbytesp = __le16_to_cpu(desc->nbytes);
+               *transfer_idp = MS(__le16_to_cpu(desc->flags),
+                                               CE_DESC_FLAGS_META_DATA);
+
+               if (per_transfer_contextp)
+                       *per_transfer_contextp =
+                               src_ring->per_transfer_context[sw_index];
+
+               /* sanity */
+               src_ring->per_transfer_context[sw_index] = NULL;
+
+               /* Update sw_index */
+               sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+               src_ring->sw_index = sw_index;
+               ret = 0;
+       } else {
+               ret = -EIO;
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+                                 void **per_transfer_contextp,
+                                 u32 *bufferp,
+                                 unsigned int *nbytesp,
+                                 unsigned int *transfer_idp)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ret = ath10k_ce_completed_send_next_nolock(ce_state,
+                                                  per_transfer_contextp,
+                                                  bufferp, nbytesp,
+                                                  transfer_idp);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+/*
+ * Guts of interrupt handler for per-engine interrupts on a particular CE.
+ *
+ * Invokes registered callbacks for recv_complete,
+ * send_complete, and watermarks.
+ */
+void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       void *transfer_context;
+       u32 buf;
+       unsigned int nbytes;
+       unsigned int id;
+       unsigned int flags;
+
+       ath10k_pci_wake(ar);
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       /* Clear the copy-complete interrupts that will be handled here. */
+       ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
+                                         HOST_IS_COPY_COMPLETE_MASK);
+
+       if (ce_state->recv_cb) {
+               /*
+                * Pop completed recv buffers and call the registered
+                * recv callback for each
+                */
+               while (ath10k_ce_completed_recv_next_nolock(ce_state,
+                                                           &transfer_context,
+                                                           &buf, &nbytes,
+                                                           &id, &flags) == 0) {
+                       spin_unlock_bh(&ar_pci->ce_lock);
+                       ce_state->recv_cb(ce_state, transfer_context, buf,
+                                         nbytes, id, flags);
+                       spin_lock_bh(&ar_pci->ce_lock);
+               }
+       }
+
+       if (ce_state->send_cb) {
+               /*
+                * Pop completed send buffers and call the registered
+                * send callback for each
+                */
+               while (ath10k_ce_completed_send_next_nolock(ce_state,
+                                                           &transfer_context,
+                                                           &buf,
+                                                           &nbytes,
+                                                           &id) == 0) {
+                       spin_unlock_bh(&ar_pci->ce_lock);
+                       ce_state->send_cb(ce_state, transfer_context,
+                                         buf, nbytes, id);
+                       spin_lock_bh(&ar_pci->ce_lock);
+               }
+       }
+
+       /*
+        * Misc CE interrupts are not being handled, but still need
+        * to be cleared.
+        */
+       ath10k_ce_engine_int_status_clear(ar, ctrl_addr, CE_WATERMARK_MASK);
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+       ath10k_pci_sleep(ar);
+}
+
+/*
+ * Handler for per-engine interrupts on ALL active CEs.
+ * This is used in cases where the system is sharing a
+ * single interrput for all CEs
+ */
+
+void ath10k_ce_per_engine_service_any(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ce_id;
+       u32 intr_summary;
+
+       ath10k_pci_wake(ar);
+       intr_summary = CE_INTERRUPT_SUMMARY(ar);
+
+       for (ce_id = 0; intr_summary && (ce_id < ar_pci->ce_count); ce_id++) {
+               if (intr_summary & (1 << ce_id))
+                       intr_summary &= ~(1 << ce_id);
+               else
+                       /* no intr pending on this CE */
+                       continue;
+
+               ath10k_ce_per_engine_service(ar, ce_id);
+       }
+
+       ath10k_pci_sleep(ar);
+}
+
+/*
+ * Adjust interrupts for the copy complete handler.
+ * If it's needed for either send or recv, then unmask
+ * this interrupt; otherwise, mask it.
+ *
+ * Called with ce_lock held.
+ */
+static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state,
+                                               int disable_copy_compl_intr)
+{
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       struct ath10k *ar = ce_state->ar;
+
+       ath10k_pci_wake(ar);
+
+       if ((!disable_copy_compl_intr) &&
+           (ce_state->send_cb || ce_state->recv_cb))
+               ath10k_ce_copy_complete_inter_enable(ar, ctrl_addr);
+       else
+               ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+
+       ath10k_ce_watermark_intr_disable(ar, ctrl_addr);
+
+       ath10k_pci_sleep(ar);
+}
+
+void ath10k_ce_disable_interrupts(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ce_id;
+
+       ath10k_pci_wake(ar);
+       for (ce_id = 0; ce_id < ar_pci->ce_count; ce_id++) {
+               struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
+               u32 ctrl_addr = ce_state->ctrl_addr;
+
+               ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+       }
+       ath10k_pci_sleep(ar);
+}
+
+void ath10k_ce_send_cb_register(struct ce_state *ce_state,
+                               void (*send_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id),
+                               int disable_interrupts)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ce_state->send_cb = send_cb;
+       ath10k_ce_per_engine_handler_adjust(ce_state, disable_interrupts);
+       spin_unlock_bh(&ar_pci->ce_lock);
+}
+
+void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
+                               void (*recv_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id,
+                                                unsigned int flags))
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ce_state->recv_cb = recv_cb;
+       ath10k_ce_per_engine_handler_adjust(ce_state, 0);
+       spin_unlock_bh(&ar_pci->ce_lock);
+}
+
+static int ath10k_ce_init_src_ring(struct ath10k *ar,
+                                  unsigned int ce_id,
+                                  struct ce_state *ce_state,
+                                  const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_ring_state *src_ring;
+       unsigned int nentries = attr->src_nentries;
+       unsigned int ce_nbytes;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+       dma_addr_t base_addr;
+       char *ptr;
+
+       nentries = roundup_pow_of_two(nentries);
+
+       if (ce_state->src_ring) {
+               WARN_ON(ce_state->src_ring->nentries != nentries);
+               return 0;
+       }
+
+       ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+       ptr = kzalloc(ce_nbytes, GFP_KERNEL);
+       if (ptr == NULL)
+               return -ENOMEM;
+
+       ce_state->src_ring = (struct ce_ring_state *)ptr;
+       src_ring = ce_state->src_ring;
+
+       ptr += sizeof(struct ce_ring_state);
+       src_ring->nentries = nentries;
+       src_ring->nentries_mask = nentries - 1;
+
+       ath10k_pci_wake(ar);
+       src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+       src_ring->hw_index = src_ring->sw_index;
+
+       src_ring->write_index =
+               ath10k_ce_src_ring_write_index_get(ar, ctrl_addr);
+       ath10k_pci_sleep(ar);
+
+       src_ring->per_transfer_context = (void **)ptr;
+
+       /*
+        * Legacy platforms that do not support cache
+        * coherent DMA are unsupported
+        */
+       src_ring->base_addr_owner_space_unaligned =
+               pci_alloc_consistent(ar_pci->pdev,
+                                    (nentries * sizeof(struct ce_desc) +
+                                     CE_DESC_RING_ALIGN),
+                                    &base_addr);
+       src_ring->base_addr_ce_space_unaligned = base_addr;
+
+       src_ring->base_addr_owner_space = PTR_ALIGN(
+                       src_ring->base_addr_owner_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+       src_ring->base_addr_ce_space = ALIGN(
+                       src_ring->base_addr_ce_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+
+       /*
+        * Also allocate a shadow src ring in regular
+        * mem to use for faster access.
+        */
+       src_ring->shadow_base_unaligned =
+               kmalloc((nentries * sizeof(struct ce_desc) +
+                        CE_DESC_RING_ALIGN), GFP_KERNEL);
+
+       src_ring->shadow_base = PTR_ALIGN(
+                       src_ring->shadow_base_unaligned,
+                       CE_DESC_RING_ALIGN);
+
+       ath10k_pci_wake(ar);
+       ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr,
+                                        src_ring->base_addr_ce_space);
+       ath10k_ce_src_ring_size_set(ar, ctrl_addr, nentries);
+       ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, attr->src_sz_max);
+       ath10k_ce_src_ring_byte_swap_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries);
+       ath10k_pci_sleep(ar);
+
+       return 0;
+}
+
+static int ath10k_ce_init_dest_ring(struct ath10k *ar,
+                                   unsigned int ce_id,
+                                   struct ce_state *ce_state,
+                                   const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_ring_state *dest_ring;
+       unsigned int nentries = attr->dest_nentries;
+       unsigned int ce_nbytes;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+       dma_addr_t base_addr;
+       char *ptr;
+
+       nentries = roundup_pow_of_two(nentries);
+
+       if (ce_state->dest_ring) {
+               WARN_ON(ce_state->dest_ring->nentries != nentries);
+               return 0;
+       }
+
+       ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+       ptr = kzalloc(ce_nbytes, GFP_KERNEL);
+       if (ptr == NULL)
+               return -ENOMEM;
+
+       ce_state->dest_ring = (struct ce_ring_state *)ptr;
+       dest_ring = ce_state->dest_ring;
+
+       ptr += sizeof(struct ce_ring_state);
+       dest_ring->nentries = nentries;
+       dest_ring->nentries_mask = nentries - 1;
+
+       ath10k_pci_wake(ar);
+       dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
+       dest_ring->write_index =
+               ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr);
+       ath10k_pci_sleep(ar);
+
+       dest_ring->per_transfer_context = (void **)ptr;
+
+       /*
+        * Legacy platforms that do not support cache
+        * coherent DMA are unsupported
+        */
+       dest_ring->base_addr_owner_space_unaligned =
+               pci_alloc_consistent(ar_pci->pdev,
+                                    (nentries * sizeof(struct ce_desc) +
+                                     CE_DESC_RING_ALIGN),
+                                    &base_addr);
+       dest_ring->base_addr_ce_space_unaligned = base_addr;
+
+       /*
+        * Correctly initialize memory to 0 to prevent garbage
+        * data crashing system when download firmware
+        */
+       memset(dest_ring->base_addr_owner_space_unaligned, 0,
+              nentries * sizeof(struct ce_desc) + CE_DESC_RING_ALIGN);
+
+       dest_ring->base_addr_owner_space = PTR_ALIGN(
+                       dest_ring->base_addr_owner_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+       dest_ring->base_addr_ce_space = ALIGN(
+                       dest_ring->base_addr_ce_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+
+       ath10k_pci_wake(ar);
+       ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr,
+                                         dest_ring->base_addr_ce_space);
+       ath10k_ce_dest_ring_size_set(ar, ctrl_addr, nentries);
+       ath10k_ce_dest_ring_byte_swap_set(ar, ctrl_addr, 0);
+       ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0);
+       ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries);
+       ath10k_pci_sleep(ar);
+
+       return 0;
+}
+
+static struct ce_state *ath10k_ce_init_state(struct ath10k *ar,
+                                            unsigned int ce_id,
+                                            const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_state = NULL;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       if (!ar_pci->ce_id_to_state[ce_id]) {
+               ce_state = kzalloc(sizeof(*ce_state), GFP_ATOMIC);
+               if (ce_state == NULL) {
+                       spin_unlock_bh(&ar_pci->ce_lock);
+                       return NULL;
+               }
+
+               ar_pci->ce_id_to_state[ce_id] = ce_state;
+               ce_state->ar = ar;
+               ce_state->id = ce_id;
+               ce_state->ctrl_addr = ctrl_addr;
+               ce_state->state = CE_RUNNING;
+               /* Save attribute flags */
+               ce_state->attr_flags = attr->flags;
+               ce_state->src_sz_max = attr->src_sz_max;
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ce_state;
+}
+
+/*
+ * Initialize a Copy Engine based on caller-supplied attributes.
+ * This may be called once to initialize both source and destination
+ * rings or it may be called twice for separate source and destination
+ * initialization. It may be that only one side or the other is
+ * initialized by software/firmware.
+ */
+struct ce_state *ath10k_ce_init(struct ath10k *ar,
+                               unsigned int ce_id,
+                               const struct ce_attr *attr)
+{
+       struct ce_state *ce_state;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       ce_state = ath10k_ce_init_state(ar, ce_id, attr);
+       if (!ce_state) {
+               ath10k_err("Failed to initialize CE state for ID: %d\n", ce_id);
+               return NULL;
+       }
+
+       if (attr->src_nentries) {
+               if (ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr)) {
+                       ath10k_err("Failed to initialize CE src ring for ID: %d\n",
+                                  ce_id);
+                       ath10k_ce_deinit(ce_state);
+                       return NULL;
+               }
+       }
+
+       if (attr->dest_nentries) {
+               if (ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr)) {
+                       ath10k_err("Failed to initialize CE dest ring for ID: %d\n",
+                                  ce_id);
+                       ath10k_ce_deinit(ce_state);
+                       return NULL;
+               }
+       }
+
+       /* Enable CE error interrupts */
+       ath10k_pci_wake(ar);
+       ath10k_ce_error_intr_enable(ar, ctrl_addr);
+       ath10k_pci_sleep(ar);
+
+       return ce_state;
+}
+
+void ath10k_ce_deinit(struct ce_state *ce_state)
+{
+       unsigned int ce_id = ce_state->id;
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       ce_state->state = CE_UNUSED;
+       ar_pci->ce_id_to_state[ce_id] = NULL;
+
+       if (ce_state->src_ring) {
+               kfree(ce_state->src_ring->shadow_base_unaligned);
+               pci_free_consistent(ar_pci->pdev,
+                                   (ce_state->src_ring->nentries *
+                                    sizeof(struct ce_desc) +
+                                    CE_DESC_RING_ALIGN),
+                                   ce_state->src_ring->base_addr_owner_space,
+                                   ce_state->src_ring->base_addr_ce_space);
+               kfree(ce_state->src_ring);
+       }
+
+       if (ce_state->dest_ring) {
+               pci_free_consistent(ar_pci->pdev,
+                                   (ce_state->dest_ring->nentries *
+                                    sizeof(struct ce_desc) +
+                                    CE_DESC_RING_ALIGN),
+                                   ce_state->dest_ring->base_addr_owner_space,
+                                   ce_state->dest_ring->base_addr_ce_space);
+               kfree(ce_state->dest_ring);
+       }
+       kfree(ce_state);
+}
diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
new file mode 100644 (file)
index 0000000..c17f07c
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _CE_H_
+#define _CE_H_
+
+#include "hif.h"
+
+
+/* Maximum number of Copy Engine's supported */
+#define CE_COUNT_MAX 8
+#define CE_HTT_H2T_MSG_SRC_NENTRIES 2048
+
+/* Descriptor rings must be aligned to this boundary */
+#define CE_DESC_RING_ALIGN     8
+#define CE_SENDLIST_ITEMS_MAX  12
+#define CE_SEND_FLAG_GATHER    0x00010000
+
+/*
+ * Copy Engine support: low-level Target-side Copy Engine API.
+ * This is a hardware access layer used by code that understands
+ * how to use copy engines.
+ */
+
+struct ce_state;
+
+
+/* Copy Engine operational state */
+enum ce_op_state {
+       CE_UNUSED,
+       CE_PAUSED,
+       CE_RUNNING,
+};
+
+#define CE_DESC_FLAGS_GATHER         (1 << 0)
+#define CE_DESC_FLAGS_BYTE_SWAP      (1 << 1)
+#define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC
+#define CE_DESC_FLAGS_META_DATA_LSB  3
+
+struct ce_desc {
+       __le32 addr;
+       __le16 nbytes;
+       __le16 flags; /* %CE_DESC_FLAGS_ */
+};
+
+/* Copy Engine Ring internal state */
+struct ce_ring_state {
+       /* Number of entries in this ring; must be power of 2 */
+       unsigned int nentries;
+       unsigned int nentries_mask;
+
+       /*
+        * For dest ring, this is the next index to be processed
+        * by software after it was/is received into.
+        *
+        * For src ring, this is the last descriptor that was sent
+        * and completion processed by software.
+        *
+        * Regardless of src or dest ring, this is an invariant
+        * (modulo ring size):
+        *     write index >= read index >= sw_index
+        */
+       unsigned int sw_index;
+       /* cached copy */
+       unsigned int write_index;
+       /*
+        * For src ring, this is the next index not yet processed by HW.
+        * This is a cached copy of the real HW index (read index), used
+        * for avoiding reading the HW index register more often than
+        * necessary.
+        * This extends the invariant:
+        *     write index >= read index >= hw_index >= sw_index
+        *
+        * For dest ring, this is currently unused.
+        */
+       /* cached copy */
+       unsigned int hw_index;
+
+       /* Start of DMA-coherent area reserved for descriptors */
+       /* Host address space */
+       void *base_addr_owner_space_unaligned;
+       /* CE address space */
+       u32 base_addr_ce_space_unaligned;
+
+       /*
+        * Actual start of descriptors.
+        * Aligned to descriptor-size boundary.
+        * Points into reserved DMA-coherent area, above.
+        */
+       /* Host address space */
+       void *base_addr_owner_space;
+
+       /* CE address space */
+       u32 base_addr_ce_space;
+       /*
+        * Start of shadow copy of descriptors, within regular memory.
+        * Aligned to descriptor-size boundary.
+        */
+       void *shadow_base_unaligned;
+       struct ce_desc *shadow_base;
+
+       void **per_transfer_context;
+};
+
+/* Copy Engine internal state */
+struct ce_state {
+       struct ath10k *ar;
+       unsigned int id;
+
+       unsigned int attr_flags;
+
+       u32 ctrl_addr;
+       enum ce_op_state state;
+
+       void (*send_cb) (struct ce_state *ce_state,
+                        void *per_transfer_send_context,
+                        u32 buffer,
+                        unsigned int nbytes,
+                        unsigned int transfer_id);
+       void (*recv_cb) (struct ce_state *ce_state,
+                        void *per_transfer_recv_context,
+                        u32 buffer,
+                        unsigned int nbytes,
+                        unsigned int transfer_id,
+                        unsigned int flags);
+
+       unsigned int src_sz_max;
+       struct ce_ring_state *src_ring;
+       struct ce_ring_state *dest_ring;
+};
+
+struct ce_sendlist_item {
+       /* e.g. buffer or desc list */
+       dma_addr_t data;
+       union {
+               /* simple buffer */
+               unsigned int nbytes;
+               /* Rx descriptor list */
+               unsigned int ndesc;
+       } u;
+       /* externally-specified flags; OR-ed with internal flags */
+       u32 flags;
+};
+
+struct ce_sendlist {
+       unsigned int num_items;
+       struct ce_sendlist_item item[CE_SENDLIST_ITEMS_MAX];
+};
+
+/* Copy Engine settable attributes */
+struct ce_attr;
+
+/*==================Send====================*/
+
+/* ath10k_ce_send flags */
+#define CE_SEND_FLAG_BYTE_SWAP 1
+
+/*
+ * Queue a source buffer to be sent to an anonymous destination buffer.
+ *   ce         - which copy engine to use
+ *   buffer          - address of buffer
+ *   nbytes          - number of bytes to send
+ *   transfer_id     - arbitrary ID; reflected to destination
+ *   flags           - CE_SEND_FLAG_* values
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Note: If no flags are specified, use CE's default data swap mode.
+ *
+ * Implementation note: pushes 1 buffer to Source ring
+ */
+int ath10k_ce_send(struct ce_state *ce_state,
+                  void *per_transfer_send_context,
+                  u32 buffer,
+                  unsigned int nbytes,
+                  /* 14 bits */
+                  unsigned int transfer_id,
+                  unsigned int flags);
+
+void ath10k_ce_send_cb_register(struct ce_state *ce_state,
+                               void (*send_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id),
+                               int disable_interrupts);
+
+/* Append a simple buffer (address/length) to a sendlist. */
+void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist,
+                               u32 buffer,
+                               unsigned int nbytes,
+                               /* OR-ed with internal flags */
+                               u32 flags);
+
+/*
+ * Queue a "sendlist" of buffers to be sent using gather to a single
+ * anonymous destination buffer
+ *   ce         - which copy engine to use
+ *   sendlist        - list of simple buffers to send using gather
+ *   transfer_id     - arbitrary ID; reflected to destination
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Implemenation note: Pushes multiple buffers with Gather to Source ring.
+ */
+int ath10k_ce_sendlist_send(struct ce_state *ce_state,
+                           void *per_transfer_send_context,
+                           struct ce_sendlist *sendlist,
+                           /* 14 bits */
+                           unsigned int transfer_id);
+
+/*==================Recv=======================*/
+
+/*
+ * Make a buffer available to receive. The buffer must be at least of a
+ * minimal size appropriate for this copy engine (src_sz_max attribute).
+ *   ce                    - which copy engine to use
+ *   per_transfer_recv_context  - context passed back to caller's recv_cb
+ *   buffer                     - address of buffer in CE space
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Implemenation note: Pushes a buffer to Dest ring.
+ */
+int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+                              void *per_transfer_recv_context,
+                              u32 buffer);
+
+void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
+                               void (*recv_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id,
+                                                unsigned int flags));
+
+/* recv flags */
+/* Data is byte-swapped */
+#define CE_RECV_FLAG_SWAPPED   1
+
+/*
+ * Supply data for the next completed unprocessed receive descriptor.
+ * Pops buffer from Dest ring.
+ */
+int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+                                 void **per_transfer_contextp,
+                                 u32 *bufferp,
+                                 unsigned int *nbytesp,
+                                 unsigned int *transfer_idp,
+                                 unsigned int *flagsp);
+/*
+ * Supply data for the next completed unprocessed send descriptor.
+ * Pops 1 completed send buffer from Source ring.
+ */
+int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+                          void **per_transfer_contextp,
+                          u32 *bufferp,
+                          unsigned int *nbytesp,
+                          unsigned int *transfer_idp);
+
+/*==================CE Engine Initialization=======================*/
+
+/* Initialize an instance of a CE */
+struct ce_state *ath10k_ce_init(struct ath10k *ar,
+                               unsigned int ce_id,
+                               const struct ce_attr *attr);
+
+/*==================CE Engine Shutdown=======================*/
+/*
+ * Support clean shutdown by allowing the caller to revoke
+ * receive buffers.  Target DMA must be stopped before using
+ * this API.
+ */
+int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp);
+
+/*
+ * Support clean shutdown by allowing the caller to cancel
+ * pending sends.  Target DMA must be stopped before using
+ * this API.
+ */
+int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp,
+                              unsigned int *nbytesp,
+                              unsigned int *transfer_idp);
+
+void ath10k_ce_deinit(struct ce_state *ce_state);
+
+/*==================CE Interrupt Handlers====================*/
+void ath10k_ce_per_engine_service_any(struct ath10k *ar);
+void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id);
+void ath10k_ce_disable_interrupts(struct ath10k *ar);
+
+/* ce_attr.flags values */
+/* Use NonSnooping PCIe accesses? */
+#define CE_ATTR_NO_SNOOP               1
+
+/* Byte swap data words */
+#define CE_ATTR_BYTE_SWAP_DATA         2
+
+/* Swizzle descriptors? */
+#define CE_ATTR_SWIZZLE_DESCRIPTORS    4
+
+/* no interrupt on copy completion */
+#define CE_ATTR_DIS_INTR               8
+
+/* Attributes of an instance of a Copy Engine */
+struct ce_attr {
+       /* CE_ATTR_* values */
+       unsigned int flags;
+
+       /* currently not in use */
+       unsigned int priority;
+
+       /* #entries in source ring - Must be a power of 2 */
+       unsigned int src_nentries;
+
+       /*
+        * Max source send size for this CE.
+        * This is also the minimum size of a destination buffer.
+        */
+       unsigned int src_sz_max;
+
+       /* #entries in destination ring - Must be a power of 2 */
+       unsigned int dest_nentries;
+
+       /* Future use */
+       void *reserved;
+};
+
+/*
+ * When using sendlist_send to transfer multiple buffer fragments, the
+ * transfer context of each fragment, except last one, will be filled
+ * with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for
+ * each fragment done with send and the transfer context would be
+ * CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the
+ * status of a send completion.
+ */
+#define CE_SENDLIST_ITEM_CTXT  ((void *)0xcecebeef)
+
+#define SR_BA_ADDRESS          0x0000
+#define SR_SIZE_ADDRESS                0x0004
+#define DR_BA_ADDRESS          0x0008
+#define DR_SIZE_ADDRESS                0x000c
+#define CE_CMD_ADDRESS         0x0018
+
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB     17
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB     17
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK    0x00020000
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \
+       (((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \
+       CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK)
+
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB     16
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB     16
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK    0x00010000
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \
+       (((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \
+        CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB)
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \
+       (((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \
+        CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK)
+
+#define CE_CTRL1_DMAX_LENGTH_MSB               15
+#define CE_CTRL1_DMAX_LENGTH_LSB               0
+#define CE_CTRL1_DMAX_LENGTH_MASK              0x0000ffff
+#define CE_CTRL1_DMAX_LENGTH_GET(x) \
+       (((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB)
+#define CE_CTRL1_DMAX_LENGTH_SET(x) \
+       (((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK)
+
+#define CE_CTRL1_ADDRESS                       0x0010
+#define CE_CTRL1_HW_MASK                       0x0007ffff
+#define CE_CTRL1_SW_MASK                       0x0007ffff
+#define CE_CTRL1_HW_WRITE_MASK                 0x00000000
+#define CE_CTRL1_SW_WRITE_MASK                 0x0007ffff
+#define CE_CTRL1_RSTMASK                       0xffffffff
+#define CE_CTRL1_RESET                         0x00000080
+
+#define CE_CMD_HALT_STATUS_MSB                 3
+#define CE_CMD_HALT_STATUS_LSB                 3
+#define CE_CMD_HALT_STATUS_MASK                        0x00000008
+#define CE_CMD_HALT_STATUS_GET(x) \
+       (((x) & CE_CMD_HALT_STATUS_MASK) >> CE_CMD_HALT_STATUS_LSB)
+#define CE_CMD_HALT_STATUS_SET(x) \
+       (((0 | (x)) << CE_CMD_HALT_STATUS_LSB) & CE_CMD_HALT_STATUS_MASK)
+#define CE_CMD_HALT_STATUS_RESET               0
+#define CE_CMD_HALT_MSB                                0
+#define CE_CMD_HALT_MASK                       0x00000001
+
+#define HOST_IE_COPY_COMPLETE_MSB              0
+#define HOST_IE_COPY_COMPLETE_LSB              0
+#define HOST_IE_COPY_COMPLETE_MASK             0x00000001
+#define HOST_IE_COPY_COMPLETE_GET(x) \
+       (((x) & HOST_IE_COPY_COMPLETE_MASK) >> HOST_IE_COPY_COMPLETE_LSB)
+#define HOST_IE_COPY_COMPLETE_SET(x) \
+       (((0 | (x)) << HOST_IE_COPY_COMPLETE_LSB) & HOST_IE_COPY_COMPLETE_MASK)
+#define HOST_IE_COPY_COMPLETE_RESET            0
+#define HOST_IE_ADDRESS                                0x002c
+
+#define HOST_IS_DST_RING_LOW_WATERMARK_MASK    0x00000010
+#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK   0x00000008
+#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK    0x00000004
+#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK   0x00000002
+#define HOST_IS_COPY_COMPLETE_MASK             0x00000001
+#define HOST_IS_ADDRESS                                0x0030
+
+#define MISC_IE_ADDRESS                                0x0034
+
+#define MISC_IS_AXI_ERR_MASK                   0x00000400
+
+#define MISC_IS_DST_ADDR_ERR_MASK              0x00000200
+#define MISC_IS_SRC_LEN_ERR_MASK               0x00000100
+#define MISC_IS_DST_MAX_LEN_VIO_MASK           0x00000080
+#define MISC_IS_DST_RING_OVERFLOW_MASK         0x00000040
+#define MISC_IS_SRC_RING_OVERFLOW_MASK         0x00000020
+
+#define MISC_IS_ADDRESS                                0x0038
+
+#define SR_WR_INDEX_ADDRESS                    0x003c
+
+#define DST_WR_INDEX_ADDRESS                   0x0040
+
+#define CURRENT_SRRI_ADDRESS                   0x0044
+
+#define CURRENT_DRRI_ADDRESS                   0x0048
+
+#define SRC_WATERMARK_LOW_MSB                  31
+#define SRC_WATERMARK_LOW_LSB                  16
+#define SRC_WATERMARK_LOW_MASK                 0xffff0000
+#define SRC_WATERMARK_LOW_GET(x) \
+       (((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB)
+#define SRC_WATERMARK_LOW_SET(x) \
+       (((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK)
+#define SRC_WATERMARK_LOW_RESET                        0
+#define SRC_WATERMARK_HIGH_MSB                 15
+#define SRC_WATERMARK_HIGH_LSB                 0
+#define SRC_WATERMARK_HIGH_MASK                        0x0000ffff
+#define SRC_WATERMARK_HIGH_GET(x) \
+       (((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB)
+#define SRC_WATERMARK_HIGH_SET(x) \
+       (((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK)
+#define SRC_WATERMARK_HIGH_RESET               0
+#define SRC_WATERMARK_ADDRESS                  0x004c
+
+#define DST_WATERMARK_LOW_LSB                  16
+#define DST_WATERMARK_LOW_MASK                 0xffff0000
+#define DST_WATERMARK_LOW_SET(x) \
+       (((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK)
+#define DST_WATERMARK_LOW_RESET                        0
+#define DST_WATERMARK_HIGH_MSB                 15
+#define DST_WATERMARK_HIGH_LSB                 0
+#define DST_WATERMARK_HIGH_MASK                        0x0000ffff
+#define DST_WATERMARK_HIGH_GET(x) \
+       (((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB)
+#define DST_WATERMARK_HIGH_SET(x) \
+       (((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK)
+#define DST_WATERMARK_HIGH_RESET               0
+#define DST_WATERMARK_ADDRESS                  0x0050
+
+
+static inline u32 ath10k_ce_base_address(unsigned int ce_id)
+{
+       return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
+}
+
+#define CE_WATERMARK_MASK (HOST_IS_SRC_RING_LOW_WATERMARK_MASK  | \
+                          HOST_IS_SRC_RING_HIGH_WATERMARK_MASK | \
+                          HOST_IS_DST_RING_LOW_WATERMARK_MASK  | \
+                          HOST_IS_DST_RING_HIGH_WATERMARK_MASK)
+
+#define CE_ERROR_MASK  (MISC_IS_AXI_ERR_MASK           | \
+                        MISC_IS_DST_ADDR_ERR_MASK      | \
+                        MISC_IS_SRC_LEN_ERR_MASK       | \
+                        MISC_IS_DST_MAX_LEN_VIO_MASK   | \
+                        MISC_IS_DST_RING_OVERFLOW_MASK | \
+                        MISC_IS_SRC_RING_OVERFLOW_MASK)
+
+#define CE_SRC_RING_TO_DESC(baddr, idx) \
+       (&(((struct ce_desc *)baddr)[idx]))
+
+#define CE_DEST_RING_TO_DESC(baddr, idx) \
+       (&(((struct ce_desc *)baddr)[idx]))
+
+/* Ring arithmetic (modulus number of entries in ring, which is a pwr of 2). */
+#define CE_RING_DELTA(nentries_mask, fromidx, toidx) \
+       (((int)(toidx)-(int)(fromidx)) & (nentries_mask))
+
+#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask))
+
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB              8
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK             0x0000ff00
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(x) \
+       (((x) & CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK) >> \
+               CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB)
+#define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS                   0x0000
+
+#define CE_INTERRUPT_SUMMARY(ar) \
+       CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \
+               ath10k_pci_read32((ar), CE_WRAPPER_BASE_ADDRESS + \
+               CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS))
+
+#endif /* _CE_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
new file mode 100644 (file)
index 0000000..2b3426b
--- /dev/null
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+
+#include "core.h"
+#include "mac.h"
+#include "htc.h"
+#include "hif.h"
+#include "wmi.h"
+#include "bmi.h"
+#include "debug.h"
+#include "htt.h"
+
+unsigned int ath10k_debug_mask;
+static bool uart_print;
+static unsigned int ath10k_p2p;
+module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
+module_param(uart_print, bool, 0644);
+module_param_named(p2p, ath10k_p2p, uint, 0644);
+MODULE_PARM_DESC(debug_mask, "Debugging mask");
+MODULE_PARM_DESC(uart_print, "Uart target debugging");
+MODULE_PARM_DESC(p2p, "Enable ath10k P2P support");
+
+static const struct ath10k_hw_params ath10k_hw_params_list[] = {
+       {
+               .id = QCA988X_HW_1_0_VERSION,
+               .name = "qca988x hw1.0",
+               .patch_load_addr = QCA988X_HW_1_0_PATCH_LOAD_ADDR,
+               .fw = {
+                       .dir = QCA988X_HW_1_0_FW_DIR,
+                       .fw = QCA988X_HW_1_0_FW_FILE,
+                       .otp = QCA988X_HW_1_0_OTP_FILE,
+                       .board = QCA988X_HW_1_0_BOARD_DATA_FILE,
+               },
+       },
+       {
+               .id = QCA988X_HW_2_0_VERSION,
+               .name = "qca988x hw2.0",
+               .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
+               .fw = {
+                       .dir = QCA988X_HW_2_0_FW_DIR,
+                       .fw = QCA988X_HW_2_0_FW_FILE,
+                       .otp = QCA988X_HW_2_0_OTP_FILE,
+                       .board = QCA988X_HW_2_0_BOARD_DATA_FILE,
+               },
+       },
+};
+
+static void ath10k_send_suspend_complete(struct ath10k *ar)
+{
+       ath10k_dbg(ATH10K_DBG_CORE, "%s\n", __func__);
+
+       ar->is_target_paused = true;
+       wake_up(&ar->event_queue);
+}
+
+static int ath10k_check_fw_version(struct ath10k *ar)
+{
+       char version[32];
+
+       if (ar->fw_version_major >= SUPPORTED_FW_MAJOR &&
+           ar->fw_version_minor >= SUPPORTED_FW_MINOR &&
+           ar->fw_version_release >= SUPPORTED_FW_RELEASE &&
+           ar->fw_version_build >= SUPPORTED_FW_BUILD)
+               return 0;
+
+       snprintf(version, sizeof(version), "%u.%u.%u.%u",
+                SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR,
+                SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD);
+
+       ath10k_warn("WARNING: Firmware version %s is not officially supported.\n",
+                   ar->hw->wiphy->fw_version);
+       ath10k_warn("Please upgrade to version %s (or newer)\n", version);
+
+       return 0;
+}
+
+static int ath10k_init_connect_htc(struct ath10k *ar)
+{
+       int status;
+
+       status = ath10k_wmi_connect_htc_service(ar);
+       if (status)
+               goto conn_fail;
+
+       /* Start HTC */
+       status = ath10k_htc_start(ar->htc);
+       if (status)
+               goto conn_fail;
+
+       /* Wait for WMI event to be ready */
+       status = ath10k_wmi_wait_for_service_ready(ar);
+       if (status <= 0) {
+               ath10k_warn("wmi service ready event not received");
+               status = -ETIMEDOUT;
+               goto timeout;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE, "core wmi ready\n");
+       return 0;
+
+timeout:
+       ath10k_htc_stop(ar->htc);
+conn_fail:
+       return status;
+}
+
+static int ath10k_init_configure_target(struct ath10k *ar)
+{
+       u32 param_host;
+       int ret;
+
+       /* tell target which HTC version it is used*/
+       ret = ath10k_bmi_write32(ar, hi_app_host_interest,
+                                HTC_PROTOCOL_VERSION);
+       if (ret) {
+               ath10k_err("settings HTC version failed\n");
+               return ret;
+       }
+
+       /* set the firmware mode to STA/IBSS/AP */
+       ret = ath10k_bmi_read32(ar, hi_option_flag, &param_host);
+       if (ret) {
+               ath10k_err("setting firmware mode (1/2) failed\n");
+               return ret;
+       }
+
+       /* TODO following parameters need to be re-visited. */
+       /* num_device */
+       param_host |= (1 << HI_OPTION_NUM_DEV_SHIFT);
+       /* Firmware mode */
+       /* FIXME: Why FW_MODE_AP ??.*/
+       param_host |= (HI_OPTION_FW_MODE_AP << HI_OPTION_FW_MODE_SHIFT);
+       /* mac_addr_method */
+       param_host |= (1 << HI_OPTION_MAC_ADDR_METHOD_SHIFT);
+       /* firmware_bridge */
+       param_host |= (0 << HI_OPTION_FW_BRIDGE_SHIFT);
+       /* fwsubmode */
+       param_host |= (0 << HI_OPTION_FW_SUBMODE_SHIFT);
+
+       ret = ath10k_bmi_write32(ar, hi_option_flag, param_host);
+       if (ret) {
+               ath10k_err("setting firmware mode (2/2) failed\n");
+               return ret;
+       }
+
+       /* We do all byte-swapping on the host */
+       ret = ath10k_bmi_write32(ar, hi_be, 0);
+       if (ret) {
+               ath10k_err("setting host CPU BE mode failed\n");
+               return ret;
+       }
+
+       /* FW descriptor/Data swap flags */
+       ret = ath10k_bmi_write32(ar, hi_fw_swap, 0);
+
+       if (ret) {
+               ath10k_err("setting FW data/desc swap flags failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
+                                                  const char *dir,
+                                                  const char *file)
+{
+       char filename[100];
+       const struct firmware *fw;
+       int ret;
+
+       if (file == NULL)
+               return ERR_PTR(-ENOENT);
+
+       if (dir == NULL)
+               dir = ".";
+
+       snprintf(filename, sizeof(filename), "%s/%s", dir, file);
+       ret = request_firmware(&fw, filename, ar->dev);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return fw;
+}
+
+static int ath10k_push_board_ext_data(struct ath10k *ar,
+                                     const struct firmware *fw)
+{
+       u32 board_data_size = QCA988X_BOARD_DATA_SZ;
+       u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ;
+       u32 board_ext_data_addr;
+       int ret;
+
+       ret = ath10k_bmi_read32(ar, hi_board_ext_data, &board_ext_data_addr);
+       if (ret) {
+               ath10k_err("could not read board ext data addr (%d)\n", ret);
+               return ret;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "ath10k: Board extended Data download addr: 0x%x\n",
+                  board_ext_data_addr);
+
+       if (board_ext_data_addr == 0)
+               return 0;
+
+       if (fw->size != (board_data_size + board_ext_data_size)) {
+               ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n",
+                          fw->size, board_data_size, board_ext_data_size);
+               return -EINVAL;
+       }
+
+       ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
+                                     fw->data + board_data_size,
+                                     board_ext_data_size);
+       if (ret) {
+               ath10k_err("could not write board ext data (%d)\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_board_ext_data_config,
+                                (board_ext_data_size << 16) | 1);
+       if (ret) {
+               ath10k_err("could not write board ext data bit (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ath10k_download_board_data(struct ath10k *ar)
+{
+       u32 board_data_size = QCA988X_BOARD_DATA_SZ;
+       u32 address;
+       const struct firmware *fw;
+       int ret;
+
+       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
+                                 ar->hw_params.fw.board);
+       if (IS_ERR(fw)) {
+               ath10k_err("could not fetch board data fw file (%ld)\n",
+                          PTR_ERR(fw));
+               return PTR_ERR(fw);
+       }
+
+       ret = ath10k_push_board_ext_data(ar, fw);
+       if (ret) {
+               ath10k_err("could not push board ext data (%d)\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_bmi_read32(ar, hi_board_data, &address);
+       if (ret) {
+               ath10k_err("could not read board data addr (%d)\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_bmi_write_memory(ar, address, fw->data,
+                                     min_t(u32, board_data_size, fw->size));
+       if (ret) {
+               ath10k_err("could not write board data (%d)\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_board_data_initialized, 1);
+       if (ret) {
+               ath10k_err("could not write board data bit (%d)\n", ret);
+               goto exit;
+       }
+
+exit:
+       release_firmware(fw);
+       return ret;
+}
+
+static int ath10k_download_and_run_otp(struct ath10k *ar)
+{
+       const struct firmware *fw;
+       u32 address;
+       u32 exec_param;
+       int ret;
+
+       /* OTP is optional */
+
+       if (ar->hw_params.fw.otp == NULL) {
+               ath10k_info("otp file not defined\n");
+               return 0;
+       }
+
+       address = ar->hw_params.patch_load_addr;
+
+       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
+                                 ar->hw_params.fw.otp);
+       if (IS_ERR(fw)) {
+               ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+               return 0;
+       }
+
+       ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+       if (ret) {
+               ath10k_err("could not write otp (%d)\n", ret);
+               goto exit;
+       }
+
+       exec_param = 0;
+       ret = ath10k_bmi_execute(ar, address, &exec_param);
+       if (ret) {
+               ath10k_err("could not execute otp (%d)\n", ret);
+               goto exit;
+       }
+
+exit:
+       release_firmware(fw);
+       return ret;
+}
+
+static int ath10k_download_fw(struct ath10k *ar)
+{
+       const struct firmware *fw;
+       u32 address;
+       int ret;
+
+       if (ar->hw_params.fw.fw == NULL)
+               return -EINVAL;
+
+       address = ar->hw_params.patch_load_addr;
+
+       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
+                                 ar->hw_params.fw.fw);
+       if (IS_ERR(fw)) {
+               ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
+               return PTR_ERR(fw);
+       }
+
+       ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+       if (ret) {
+               ath10k_err("could not write fw (%d)\n", ret);
+               goto exit;
+       }
+
+exit:
+       release_firmware(fw);
+       return ret;
+}
+
+static int ath10k_init_download_firmware(struct ath10k *ar)
+{
+       int ret;
+
+       ret = ath10k_download_board_data(ar);
+       if (ret)
+               return ret;
+
+       ret = ath10k_download_and_run_otp(ar);
+       if (ret)
+               return ret;
+
+       ret = ath10k_download_fw(ar);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static int ath10k_init_uart(struct ath10k *ar)
+{
+       int ret;
+
+       /*
+        * Explicitly setting UART prints to zero as target turns it on
+        * based on scratch registers.
+        */
+       ret = ath10k_bmi_write32(ar, hi_serial_enable, 0);
+       if (ret) {
+               ath10k_warn("could not disable UART prints (%d)\n", ret);
+               return ret;
+       }
+
+       if (!uart_print) {
+               ath10k_info("UART prints disabled\n");
+               return 0;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7);
+       if (ret) {
+               ath10k_warn("could not enable UART prints (%d)\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_serial_enable, 1);
+       if (ret) {
+               ath10k_warn("could not enable UART prints (%d)\n", ret);
+               return ret;
+       }
+
+       ath10k_info("UART prints enabled\n");
+       return 0;
+}
+
+static int ath10k_init_hw_params(struct ath10k *ar)
+{
+       const struct ath10k_hw_params *uninitialized_var(hw_params);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ath10k_hw_params_list); i++) {
+               hw_params = &ath10k_hw_params_list[i];
+
+               if (hw_params->id == ar->target_version)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(ath10k_hw_params_list)) {
+               ath10k_err("Unsupported hardware version: 0x%x\n",
+                          ar->target_version);
+               return -EINVAL;
+       }
+
+       ar->hw_params = *hw_params;
+
+       ath10k_info("Hardware name %s version 0x%x\n",
+                   ar->hw_params.name, ar->target_version);
+
+       return 0;
+}
+
+struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
+                                 enum ath10k_bus bus,
+                                 const struct ath10k_hif_ops *hif_ops)
+{
+       struct ath10k *ar;
+
+       ar = ath10k_mac_create();
+       if (!ar)
+               return NULL;
+
+       ar->ath_common.priv = ar;
+       ar->ath_common.hw = ar->hw;
+
+       ar->p2p = !!ath10k_p2p;
+       ar->dev = dev;
+
+       ar->hif.priv = hif_priv;
+       ar->hif.ops = hif_ops;
+       ar->hif.bus = bus;
+
+       ar->free_vdev_map = 0xFF; /* 8 vdevs */
+
+       init_completion(&ar->scan.started);
+       init_completion(&ar->scan.completed);
+       init_completion(&ar->scan.on_channel);
+
+       init_completion(&ar->install_key_done);
+       init_completion(&ar->vdev_setup_done);
+
+       setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar);
+
+       ar->workqueue = create_singlethread_workqueue("ath10k_wq");
+       if (!ar->workqueue)
+               goto err_wq;
+
+       mutex_init(&ar->conf_mutex);
+       spin_lock_init(&ar->data_lock);
+
+       INIT_LIST_HEAD(&ar->peers);
+       init_waitqueue_head(&ar->peer_mapping_wq);
+
+       init_completion(&ar->offchan_tx_completed);
+       INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
+       skb_queue_head_init(&ar->offchan_tx_queue);
+
+       init_waitqueue_head(&ar->event_queue);
+
+       return ar;
+
+err_wq:
+       ath10k_mac_destroy(ar);
+       return NULL;
+}
+EXPORT_SYMBOL(ath10k_core_create);
+
+void ath10k_core_destroy(struct ath10k *ar)
+{
+       flush_workqueue(ar->workqueue);
+       destroy_workqueue(ar->workqueue);
+
+       ath10k_mac_destroy(ar);
+}
+EXPORT_SYMBOL(ath10k_core_destroy);
+
+
+int ath10k_core_register(struct ath10k *ar)
+{
+       struct ath10k_htc_ops htc_ops;
+       struct bmi_target_info target_info;
+       int status;
+
+       memset(&target_info, 0, sizeof(target_info));
+       status = ath10k_bmi_get_target_info(ar, &target_info);
+       if (status)
+               goto err;
+
+       ar->target_version = target_info.version;
+       ar->hw->wiphy->hw_version = target_info.version;
+
+       status = ath10k_init_hw_params(ar);
+       if (status)
+               goto err;
+
+       if (ath10k_init_configure_target(ar)) {
+               status = -EINVAL;
+               goto err;
+       }
+
+       status = ath10k_init_download_firmware(ar);
+       if (status)
+               goto err;
+
+       status = ath10k_init_uart(ar);
+       if (status)
+               goto err;
+
+       htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete;
+
+       ar->htc = ath10k_htc_create(ar, &htc_ops);
+       if (IS_ERR(ar->htc)) {
+               status = PTR_ERR(ar->htc);
+               ath10k_err("could not create HTC (%d)\n", status);
+               goto err;
+       }
+
+       status = ath10k_bmi_done(ar);
+       if (status)
+               goto err_htc_destroy;
+
+       status = ath10k_wmi_attach(ar);
+       if (status) {
+               ath10k_err("WMI attach failed: %d\n", status);
+               goto err_htc_destroy;
+       }
+
+       status = ath10k_htc_wait_target(ar->htc);
+       if (status)
+               goto err_wmi_detach;
+
+       ar->htt = ath10k_htt_attach(ar);
+       if (!ar->htt) {
+               status = -ENOMEM;
+               goto err_wmi_detach;
+       }
+
+       status = ath10k_init_connect_htc(ar);
+       if (status)
+               goto err_htt_detach;
+
+       ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version);
+
+       status = ath10k_check_fw_version(ar);
+       if (status)
+               goto err_disconnect_htc;
+
+       status = ath10k_wmi_cmd_init(ar);
+       if (status) {
+               ath10k_err("could not send WMI init command (%d)\n", status);
+               goto err_disconnect_htc;
+       }
+
+       status = ath10k_wmi_wait_for_unified_ready(ar);
+       if (status <= 0) {
+               ath10k_err("wmi unified ready event not received\n");
+               status = -ETIMEDOUT;
+               goto err_disconnect_htc;
+       }
+
+       status = ath10k_htt_attach_target(ar->htt);
+       if (status)
+               goto err_disconnect_htc;
+
+       status = ath10k_mac_register(ar);
+       if (status)
+               goto err_disconnect_htc;
+
+       status = ath10k_debug_create(ar);
+       if (status) {
+               ath10k_err("unable to initialize debugfs\n");
+               goto err_unregister_mac;
+       }
+
+       return 0;
+
+err_unregister_mac:
+       ath10k_mac_unregister(ar);
+err_disconnect_htc:
+       ath10k_htc_stop(ar->htc);
+err_htt_detach:
+       ath10k_htt_detach(ar->htt);
+err_wmi_detach:
+       ath10k_wmi_detach(ar);
+err_htc_destroy:
+       ath10k_htc_destroy(ar->htc);
+err:
+       return status;
+}
+EXPORT_SYMBOL(ath10k_core_register);
+
+void ath10k_core_unregister(struct ath10k *ar)
+{
+       /* We must unregister from mac80211 before we stop HTC and HIF.
+        * Otherwise we will fail to submit commands to FW and mac80211 will be
+        * unhappy about callback failures. */
+       ath10k_mac_unregister(ar);
+       ath10k_htc_stop(ar->htc);
+       ath10k_htt_detach(ar->htt);
+       ath10k_wmi_detach(ar);
+       ath10k_htc_destroy(ar->htc);
+}
+EXPORT_SYMBOL(ath10k_core_unregister);
+
+int ath10k_core_target_suspend(struct ath10k *ar)
+{
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+
+       ret = ath10k_wmi_pdev_suspend_target(ar);
+       if (ret)
+               ath10k_warn("could not suspend target (%d)\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_core_target_suspend);
+
+int ath10k_core_target_resume(struct ath10k *ar)
+{
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+
+       ret = ath10k_wmi_pdev_resume_target(ar);
+       if (ret)
+               ath10k_warn("could not resume target (%d)\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_core_target_resume);
+
+MODULE_AUTHOR("Qualcomm Atheros");
+MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
new file mode 100644 (file)
index 0000000..539336d
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _CORE_H_
+#define _CORE_H_
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+
+#include "htc.h"
+#include "hw.h"
+#include "targaddrs.h"
+#include "wmi.h"
+#include "../ath.h"
+#include "../regd.h"
+
+#define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
+#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
+#define WO(_f)      ((_f##_OFFSET) >> 2)
+
+#define ATH10K_SCAN_ID 0
+#define WMI_READY_TIMEOUT (5 * HZ)
+#define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ)
+
+/* Antenna noise floor */
+#define ATH10K_DEFAULT_NOISE_FLOOR -95
+
+struct ath10k;
+
+enum ath10k_bus {
+       ATH10K_BUS_PCI,
+};
+
+struct ath10k_skb_cb {
+       dma_addr_t paddr;
+       bool is_mapped;
+       bool is_aborted;
+
+       struct {
+               u8 vdev_id;
+               u16 msdu_id;
+               u8 tid;
+               bool is_offchan;
+               bool is_conf;
+               bool discard;
+               bool no_ack;
+               u8 refcount;
+               struct sk_buff *txfrag;
+               struct sk_buff *msdu;
+       } __packed htt;
+
+       /* 4 bytes left on 64bit arch */
+} __packed;
+
+static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
+{
+       BUILD_BUG_ON(sizeof(struct ath10k_skb_cb) >
+                    IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
+       return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data;
+}
+
+static inline int ath10k_skb_map(struct device *dev, struct sk_buff *skb)
+{
+       if (ATH10K_SKB_CB(skb)->is_mapped)
+               return -EINVAL;
+
+       ATH10K_SKB_CB(skb)->paddr = dma_map_single(dev, skb->data, skb->len,
+                                                  DMA_TO_DEVICE);
+
+       if (unlikely(dma_mapping_error(dev, ATH10K_SKB_CB(skb)->paddr)))
+               return -EIO;
+
+       ATH10K_SKB_CB(skb)->is_mapped = true;
+       return 0;
+}
+
+static inline int ath10k_skb_unmap(struct device *dev, struct sk_buff *skb)
+{
+       if (!ATH10K_SKB_CB(skb)->is_mapped)
+               return -EINVAL;
+
+       dma_unmap_single(dev, ATH10K_SKB_CB(skb)->paddr, skb->len,
+                        DMA_TO_DEVICE);
+       ATH10K_SKB_CB(skb)->is_mapped = false;
+       return 0;
+}
+
+static inline u32 host_interest_item_address(u32 item_offset)
+{
+       return QCA988X_HOST_INTEREST_ADDRESS + item_offset;
+}
+
+struct ath10k_bmi {
+       bool done_sent;
+};
+
+struct ath10k_wmi {
+       enum ath10k_htc_ep_id eid;
+       struct completion service_ready;
+       struct completion unified_ready;
+       atomic_t pending_tx_count;
+       wait_queue_head_t wq;
+
+       struct sk_buff_head wmi_event_list;
+       struct work_struct wmi_event_work;
+};
+
+struct ath10k_peer_stat {
+       u8 peer_macaddr[ETH_ALEN];
+       u32 peer_rssi;
+       u32 peer_tx_rate;
+};
+
+struct ath10k_target_stats {
+       /* PDEV stats */
+       s32 ch_noise_floor;
+       u32 tx_frame_count;
+       u32 rx_frame_count;
+       u32 rx_clear_count;
+       u32 cycle_count;
+       u32 phy_err_count;
+       u32 chan_tx_power;
+
+       /* PDEV TX stats */
+       s32 comp_queued;
+       s32 comp_delivered;
+       s32 msdu_enqued;
+       s32 mpdu_enqued;
+       s32 wmm_drop;
+       s32 local_enqued;
+       s32 local_freed;
+       s32 hw_queued;
+       s32 hw_reaped;
+       s32 underrun;
+       s32 tx_abort;
+       s32 mpdus_requed;
+       u32 tx_ko;
+       u32 data_rc;
+       u32 self_triggers;
+       u32 sw_retry_failure;
+       u32 illgl_rate_phy_err;
+       u32 pdev_cont_xretry;
+       u32 pdev_tx_timeout;
+       u32 pdev_resets;
+       u32 phy_underrun;
+       u32 txop_ovf;
+
+       /* PDEV RX stats */
+       s32 mid_ppdu_route_change;
+       s32 status_rcvd;
+       s32 r0_frags;
+       s32 r1_frags;
+       s32 r2_frags;
+       s32 r3_frags;
+       s32 htt_msdus;
+       s32 htt_mpdus;
+       s32 loc_msdus;
+       s32 loc_mpdus;
+       s32 oversize_amsdu;
+       s32 phy_errs;
+       s32 phy_err_drop;
+       s32 mpdu_errs;
+
+       /* VDEV STATS */
+
+       /* PEER STATS */
+       u8 peers;
+       struct ath10k_peer_stat peer_stat[TARGET_NUM_PEERS];
+
+       /* TODO: Beacon filter stats */
+
+};
+
+#define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */
+
+struct ath10k_peer {
+       struct list_head list;
+       int vdev_id;
+       u8 addr[ETH_ALEN];
+       DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
+       struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
+};
+
+#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
+
+struct ath10k_vif {
+       u32 vdev_id;
+       enum wmi_vdev_type vdev_type;
+       enum wmi_vdev_subtype vdev_subtype;
+       u32 beacon_interval;
+       u32 dtim_period;
+
+       struct ath10k *ar;
+       struct ieee80211_vif *vif;
+
+       struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
+       u8 def_wep_key_index;
+
+       u16 tx_seq_no;
+
+       union {
+               struct {
+                       u8 bssid[ETH_ALEN];
+                       u32 uapsd;
+               } sta;
+               struct {
+                       /* 127 stations; wmi limit */
+                       u8 tim_bitmap[16];
+                       u8 tim_len;
+                       u32 ssid_len;
+                       u8 ssid[IEEE80211_MAX_SSID_LEN];
+                       bool hidden_ssid;
+                       /* P2P_IE with NoA attribute for P2P_GO case */
+                       u32 noa_len;
+                       u8 *noa_data;
+               } ap;
+               struct {
+                       u8 bssid[ETH_ALEN];
+               } ibss;
+       } u;
+};
+
+struct ath10k_vif_iter {
+       u32 vdev_id;
+       struct ath10k_vif *arvif;
+};
+
+struct ath10k_debug {
+       struct dentry *debugfs_phy;
+
+       struct ath10k_target_stats target_stats;
+       u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
+
+       struct completion event_stats_compl;
+};
+
+struct ath10k {
+       struct ath_common ath_common;
+       struct ieee80211_hw *hw;
+       struct device *dev;
+       u8 mac_addr[ETH_ALEN];
+
+       u32 target_version;
+       u8 fw_version_major;
+       u32 fw_version_minor;
+       u16 fw_version_release;
+       u16 fw_version_build;
+       u32 phy_capability;
+       u32 hw_min_tx_power;
+       u32 hw_max_tx_power;
+       u32 ht_cap_info;
+       u32 vht_cap_info;
+
+       struct targetdef *targetdef;
+       struct hostdef *hostdef;
+
+       bool p2p;
+
+       struct {
+               void *priv;
+               enum ath10k_bus bus;
+               const struct ath10k_hif_ops *ops;
+       } hif;
+
+       struct ath10k_wmi wmi;
+
+       wait_queue_head_t event_queue;
+       bool is_target_paused;
+
+       struct ath10k_bmi bmi;
+
+       struct ath10k_htc *htc;
+       struct ath10k_htt *htt;
+
+       struct ath10k_hw_params {
+               u32 id;
+               const char *name;
+               u32 patch_load_addr;
+
+               struct ath10k_hw_params_fw {
+                       const char *dir;
+                       const char *fw;
+                       const char *otp;
+                       const char *board;
+               } fw;
+       } hw_params;
+
+       struct {
+               struct completion started;
+               struct completion completed;
+               struct completion on_channel;
+               struct timer_list timeout;
+               bool is_roc;
+               bool in_progress;
+               bool aborting;
+               int vdev_id;
+               int roc_freq;
+       } scan;
+
+       struct {
+               struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+       } mac;
+
+       /* should never be NULL; needed for regular htt rx */
+       struct ieee80211_channel *rx_channel;
+
+       /* valid during scan; needed for mgmt rx during scan */
+       struct ieee80211_channel *scan_channel;
+
+       int free_vdev_map;
+       int monitor_vdev_id;
+       bool monitor_enabled;
+       bool monitor_present;
+       unsigned int filter_flags;
+
+       struct wmi_pdev_set_wmm_params_arg wmm_params;
+       struct completion install_key_done;
+
+       struct completion vdev_setup_done;
+
+       struct workqueue_struct *workqueue;
+
+       /* prevents concurrent FW reconfiguration */
+       struct mutex conf_mutex;
+
+       /* protects shared structure data */
+       spinlock_t data_lock;
+
+       struct list_head peers;
+       wait_queue_head_t peer_mapping_wq;
+
+       struct work_struct offchan_tx_work;
+       struct sk_buff_head offchan_tx_queue;
+       struct completion offchan_tx_completed;
+       struct sk_buff *offchan_tx_skb;
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+       struct ath10k_debug debug;
+#endif
+};
+
+struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
+                                 enum ath10k_bus bus,
+                                 const struct ath10k_hif_ops *hif_ops);
+void ath10k_core_destroy(struct ath10k *ar);
+
+int ath10k_core_register(struct ath10k *ar);
+void ath10k_core_unregister(struct ath10k *ar);
+
+int ath10k_core_target_suspend(struct ath10k *ar);
+int ath10k_core_target_resume(struct ath10k *ar);
+
+#endif /* _CORE_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
new file mode 100644 (file)
index 0000000..499034b
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+
+#include "core.h"
+#include "debug.h"
+
+static int ath10k_printk(const char *level, const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+       int rtn;
+
+       va_start(args, fmt);
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       rtn = printk("%sath10k: %pV", level, &vaf);
+
+       va_end(args);
+
+       return rtn;
+}
+
+int ath10k_info(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       ret = ath10k_printk(KERN_INFO, "%pV", &vaf);
+       trace_ath10k_log_info(&vaf);
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_info);
+
+int ath10k_err(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       ret = ath10k_printk(KERN_ERR, "%pV", &vaf);
+       trace_ath10k_log_err(&vaf);
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_err);
+
+int ath10k_warn(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret = 0;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+
+       if (net_ratelimit())
+               ret = ath10k_printk(KERN_WARNING, "%pV", &vaf);
+
+       trace_ath10k_log_warn(&vaf);
+
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_warn);
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+
+void ath10k_debug_read_service_map(struct ath10k *ar,
+                                  void *service_map,
+                                  size_t map_size)
+{
+       memcpy(ar->debug.wmi_service_bitmap, service_map, map_size);
+}
+
+static ssize_t ath10k_read_wmi_services(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       char *buf;
+       unsigned int len = 0, buf_len = 1500;
+       const char *status;
+       ssize_t ret_cnt;
+       int i;
+
+       buf = kzalloc(buf_len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (len > buf_len)
+               len = buf_len;
+
+       for (i = 0; i < WMI_SERVICE_LAST; i++) {
+               if (WMI_SERVICE_IS_ENABLED(ar->debug.wmi_service_bitmap, i))
+                       status = "enabled";
+               else
+                       status = "disabled";
+
+               len += scnprintf(buf + len, buf_len - len,
+                                "0x%02x - %20s - %s\n",
+                                i, wmi_service_name(i), status);
+       }
+
+       ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+       mutex_unlock(&ar->conf_mutex);
+
+       kfree(buf);
+       return ret_cnt;
+}
+
+static const struct file_operations fops_wmi_services = {
+       .read = ath10k_read_wmi_services,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+void ath10k_debug_read_target_stats(struct ath10k *ar,
+                                   struct wmi_stats_event *ev)
+{
+       u8 *tmp = ev->data;
+       struct ath10k_target_stats *stats;
+       int num_pdev_stats, num_vdev_stats, num_peer_stats;
+       struct wmi_pdev_stats *ps;
+       int i;
+
+       mutex_lock(&ar->conf_mutex);
+
+       stats = &ar->debug.target_stats;
+
+       num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); /* 0 or 1 */
+       num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); /* 0 or max vdevs */
+       num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */
+
+       if (num_pdev_stats) {
+               ps = (struct wmi_pdev_stats *)tmp;
+
+               stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf);
+               stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count);
+               stats->rx_frame_count = __le32_to_cpu(ps->rx_frame_count);
+               stats->rx_clear_count = __le32_to_cpu(ps->rx_clear_count);
+               stats->cycle_count = __le32_to_cpu(ps->cycle_count);
+               stats->phy_err_count = __le32_to_cpu(ps->phy_err_count);
+               stats->chan_tx_power = __le32_to_cpu(ps->chan_tx_pwr);
+
+               stats->comp_queued = __le32_to_cpu(ps->wal.tx.comp_queued);
+               stats->comp_delivered =
+                       __le32_to_cpu(ps->wal.tx.comp_delivered);
+               stats->msdu_enqued = __le32_to_cpu(ps->wal.tx.msdu_enqued);
+               stats->mpdu_enqued = __le32_to_cpu(ps->wal.tx.mpdu_enqued);
+               stats->wmm_drop = __le32_to_cpu(ps->wal.tx.wmm_drop);
+               stats->local_enqued = __le32_to_cpu(ps->wal.tx.local_enqued);
+               stats->local_freed = __le32_to_cpu(ps->wal.tx.local_freed);
+               stats->hw_queued = __le32_to_cpu(ps->wal.tx.hw_queued);
+               stats->hw_reaped = __le32_to_cpu(ps->wal.tx.hw_reaped);
+               stats->underrun = __le32_to_cpu(ps->wal.tx.underrun);
+               stats->tx_abort = __le32_to_cpu(ps->wal.tx.tx_abort);
+               stats->mpdus_requed = __le32_to_cpu(ps->wal.tx.mpdus_requed);
+               stats->tx_ko = __le32_to_cpu(ps->wal.tx.tx_ko);
+               stats->data_rc = __le32_to_cpu(ps->wal.tx.data_rc);
+               stats->self_triggers = __le32_to_cpu(ps->wal.tx.self_triggers);
+               stats->sw_retry_failure =
+                       __le32_to_cpu(ps->wal.tx.sw_retry_failure);
+               stats->illgl_rate_phy_err =
+                       __le32_to_cpu(ps->wal.tx.illgl_rate_phy_err);
+               stats->pdev_cont_xretry =
+                       __le32_to_cpu(ps->wal.tx.pdev_cont_xretry);
+               stats->pdev_tx_timeout =
+                       __le32_to_cpu(ps->wal.tx.pdev_tx_timeout);
+               stats->pdev_resets = __le32_to_cpu(ps->wal.tx.pdev_resets);
+               stats->phy_underrun = __le32_to_cpu(ps->wal.tx.phy_underrun);
+               stats->txop_ovf = __le32_to_cpu(ps->wal.tx.txop_ovf);
+
+               stats->mid_ppdu_route_change =
+                       __le32_to_cpu(ps->wal.rx.mid_ppdu_route_change);
+               stats->status_rcvd = __le32_to_cpu(ps->wal.rx.status_rcvd);
+               stats->r0_frags = __le32_to_cpu(ps->wal.rx.r0_frags);
+               stats->r1_frags = __le32_to_cpu(ps->wal.rx.r1_frags);
+               stats->r2_frags = __le32_to_cpu(ps->wal.rx.r2_frags);
+               stats->r3_frags = __le32_to_cpu(ps->wal.rx.r3_frags);
+               stats->htt_msdus = __le32_to_cpu(ps->wal.rx.htt_msdus);
+               stats->htt_mpdus = __le32_to_cpu(ps->wal.rx.htt_mpdus);
+               stats->loc_msdus = __le32_to_cpu(ps->wal.rx.loc_msdus);
+               stats->loc_mpdus = __le32_to_cpu(ps->wal.rx.loc_mpdus);
+               stats->oversize_amsdu =
+                       __le32_to_cpu(ps->wal.rx.oversize_amsdu);
+               stats->phy_errs = __le32_to_cpu(ps->wal.rx.phy_errs);
+               stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop);
+               stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs);
+
+               tmp += sizeof(struct wmi_pdev_stats);
+       }
+
+       /* 0 or max vdevs */
+       /* Currently firmware does not support VDEV stats */
+       if (num_vdev_stats) {
+               struct wmi_vdev_stats *vdev_stats;
+
+               for (i = 0; i < num_vdev_stats; i++) {
+                       vdev_stats = (struct wmi_vdev_stats *)tmp;
+                       tmp += sizeof(struct wmi_vdev_stats);
+               }
+       }
+
+       if (num_peer_stats) {
+               struct wmi_peer_stats *peer_stats;
+               struct ath10k_peer_stat *s;
+
+               stats->peers = num_peer_stats;
+
+               for (i = 0; i < num_peer_stats; i++) {
+                       peer_stats = (struct wmi_peer_stats *)tmp;
+                       s = &stats->peer_stat[i];
+
+                       WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats->peer_macaddr,
+                                                  s->peer_macaddr);
+                       s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi);
+                       s->peer_tx_rate =
+                               __le32_to_cpu(peer_stats->peer_tx_rate);
+
+                       tmp += sizeof(struct wmi_peer_stats);
+               }
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+       complete(&ar->debug.event_stats_compl);
+}
+
+static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       struct ath10k_target_stats *fw_stats;
+       char *buf;
+       unsigned int len = 0, buf_len = 2500;
+       ssize_t ret_cnt;
+       long left;
+       int i;
+       int ret;
+
+       fw_stats = &ar->debug.target_stats;
+
+       buf = kzalloc(buf_len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
+       if (ret) {
+               ath10k_warn("could not request stats (%d)\n", ret);
+               kfree(buf);
+               return -EIO;
+       }
+
+       left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+
+       if (left <= 0) {
+               kfree(buf);
+               return -ETIMEDOUT;
+       }
+
+       mutex_lock(&ar->conf_mutex);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PDEV stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Channel noise floor", fw_stats->ch_noise_floor);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "Channel TX power", fw_stats->chan_tx_power);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "TX frame count", fw_stats->tx_frame_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "RX frame count", fw_stats->rx_frame_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "RX clear count", fw_stats->rx_clear_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "Cycle count", fw_stats->cycle_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "PHY error count", fw_stats->phy_err_count);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PDEV TX stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HTT cookies queued", fw_stats->comp_queued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HTT cookies disp.", fw_stats->comp_delivered);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDU queued", fw_stats->msdu_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU queued", fw_stats->mpdu_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs dropped", fw_stats->wmm_drop);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Local enqued", fw_stats->local_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Local freed", fw_stats->local_freed);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HW queued", fw_stats->hw_queued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PPDUs reaped", fw_stats->hw_reaped);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Num underruns", fw_stats->underrun);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PPDUs cleaned", fw_stats->tx_abort);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs requed", fw_stats->mpdus_requed);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Excessive retries", fw_stats->tx_ko);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HW rate", fw_stats->data_rc);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Sched self tiggers", fw_stats->self_triggers);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Dropped due to SW retries",
+                        fw_stats->sw_retry_failure);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Illegal rate phy errors",
+                        fw_stats->illgl_rate_phy_err);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Pdev continous xretry", fw_stats->pdev_cont_xretry);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "TX timeout", fw_stats->pdev_tx_timeout);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PDEV resets", fw_stats->pdev_resets);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY underrun", fw_stats->phy_underrun);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU is more than txop limit", fw_stats->txop_ovf);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PDEV RX stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Mid PPDU route change",
+                        fw_stats->mid_ppdu_route_change);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Tot. number of statuses", fw_stats->status_rcvd);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 0", fw_stats->r0_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 1", fw_stats->r1_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 2", fw_stats->r2_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 3", fw_stats->r3_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs delivered to HTT", fw_stats->htt_msdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs delivered to HTT", fw_stats->htt_mpdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs delivered to stack", fw_stats->loc_msdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs delivered to stack", fw_stats->loc_mpdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Oversized AMSUs", fw_stats->oversize_amsdu);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY errors", fw_stats->phy_errs);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY errors drops", fw_stats->phy_err_drop);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PEER stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       for (i = 0; i < fw_stats->peers; i++) {
+               len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
+                                "Peer MAC address",
+                                fw_stats->peer_stat[i].peer_macaddr);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "Peer RSSI", fw_stats->peer_stat[i].peer_rssi);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "Peer TX rate",
+                                fw_stats->peer_stat[i].peer_tx_rate);
+               len += scnprintf(buf + len, buf_len - len, "\n");
+       }
+
+       if (len > buf_len)
+               len = buf_len;
+
+       ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+       mutex_unlock(&ar->conf_mutex);
+
+       kfree(buf);
+       return ret_cnt;
+}
+
+static const struct file_operations fops_fw_stats = {
+       .read = ath10k_read_fw_stats,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+int ath10k_debug_create(struct ath10k *ar)
+{
+       ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
+                                                  ar->hw->wiphy->debugfsdir);
+
+       if (!ar->debug.debugfs_phy)
+               return -ENOMEM;
+
+       init_completion(&ar->debug.event_stats_compl);
+
+       debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
+                           &fops_fw_stats);
+
+       debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
+                           &fops_wmi_services);
+
+       return 0;
+}
+#endif /* CONFIG_ATH10K_DEBUGFS */
+
+#ifdef CONFIG_ATH10K_DEBUG
+void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+
+       va_start(args, fmt);
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       if (ath10k_debug_mask & mask)
+               ath10k_printk(KERN_DEBUG, "%pV", &vaf);
+
+       trace_ath10k_log_dbg(mask, &vaf);
+
+       va_end(args);
+}
+EXPORT_SYMBOL(ath10k_dbg);
+
+void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+                    const char *msg, const char *prefix,
+                    const void *buf, size_t len)
+{
+       if (ath10k_debug_mask & mask) {
+               if (msg)
+                       ath10k_dbg(mask, "%s\n", msg);
+
+               print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
+       }
+
+       /* tracing code doesn't like null strings :/ */
+       trace_ath10k_log_dbg_dump(msg ? msg : "", prefix ? prefix : "",
+                                 buf, len);
+}
+EXPORT_SYMBOL(ath10k_dbg_dump);
+
+#endif /* CONFIG_ATH10K_DEBUG */
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
new file mode 100644 (file)
index 0000000..168140c
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEBUG_H_
+#define _DEBUG_H_
+
+#include <linux/types.h>
+#include "trace.h"
+
+enum ath10k_debug_mask {
+       ATH10K_DBG_PCI          = 0x00000001,
+       ATH10K_DBG_WMI          = 0x00000002,
+       ATH10K_DBG_HTC          = 0x00000004,
+       ATH10K_DBG_HTT          = 0x00000008,
+       ATH10K_DBG_MAC          = 0x00000010,
+       ATH10K_DBG_CORE         = 0x00000020,
+       ATH10K_DBG_PCI_DUMP     = 0x00000040,
+       ATH10K_DBG_HTT_DUMP     = 0x00000080,
+       ATH10K_DBG_MGMT         = 0x00000100,
+       ATH10K_DBG_DATA         = 0x00000200,
+       ATH10K_DBG_ANY          = 0xffffffff,
+};
+
+extern unsigned int ath10k_debug_mask;
+
+extern __printf(1, 2) int ath10k_info(const char *fmt, ...);
+extern __printf(1, 2) int ath10k_err(const char *fmt, ...);
+extern __printf(1, 2) int ath10k_warn(const char *fmt, ...);
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+int ath10k_debug_create(struct ath10k *ar);
+void ath10k_debug_read_service_map(struct ath10k *ar,
+                                  void *service_map,
+                                  size_t map_size);
+void ath10k_debug_read_target_stats(struct ath10k *ar,
+                                   struct wmi_stats_event *ev);
+
+#else
+static inline int ath10k_debug_create(struct ath10k *ar)
+{
+       return 0;
+}
+
+static inline void ath10k_debug_read_service_map(struct ath10k *ar,
+                                                void *service_map,
+                                                size_t map_size)
+{
+}
+
+static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
+                                                 struct wmi_stats_event *ev)
+{
+}
+#endif /* CONFIG_ATH10K_DEBUGFS */
+
+#ifdef CONFIG_ATH10K_DEBUG
+extern __printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask,
+                                     const char *fmt, ...);
+void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+                    const char *msg, const char *prefix,
+                    const void *buf, size_t len);
+#else /* CONFIG_ATH10K_DEBUG */
+
+static inline int ath10k_dbg(enum ath10k_debug_mask dbg_mask,
+                            const char *fmt, ...)
+{
+       return 0;
+}
+
+static inline void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+                                  const char *msg, const char *prefix,
+                                  const void *buf, size_t len)
+{
+}
+#endif /* CONFIG_ATH10K_DEBUG */
+#endif /* _DEBUG_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
new file mode 100644 (file)
index 0000000..73a24d4
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _HIF_H_
+#define _HIF_H_
+
+#include <linux/kernel.h>
+#include "core.h"
+
+struct ath10k_hif_cb {
+       int (*tx_completion)(struct ath10k *ar,
+                            struct sk_buff *wbuf,
+                            unsigned transfer_id);
+       int (*rx_completion)(struct ath10k *ar,
+                            struct sk_buff *wbuf,
+                            u8 pipe_id);
+};
+
+struct ath10k_hif_ops {
+       /* Send the head of a buffer to HIF for transmission to the target. */
+       int (*send_head)(struct ath10k *ar, u8 pipe_id,
+                        unsigned int transfer_id,
+                        unsigned int nbytes,
+                        struct sk_buff *buf);
+
+       /*
+        * API to handle HIF-specific BMI message exchanges, this API is
+        * synchronous and only allowed to be called from a context that
+        * can block (sleep)
+        */
+       int (*exchange_bmi_msg)(struct ath10k *ar,
+                               void *request, u32 request_len,
+                               void *response, u32 *response_len);
+
+       int (*start)(struct ath10k *ar);
+
+       void (*stop)(struct ath10k *ar);
+
+       int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
+                                  u8 *ul_pipe, u8 *dl_pipe,
+                                  int *ul_is_polled, int *dl_is_polled);
+
+       void (*get_default_pipe)(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe);
+
+       /*
+        * Check if prior sends have completed.
+        *
+        * Check whether the pipe in question has any completed
+        * sends that have not yet been processed.
+        * This function is only relevant for HIF pipes that are configured
+        * to be polled rather than interrupt-driven.
+        */
+       void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
+
+       void (*init)(struct ath10k *ar,
+                    struct ath10k_hif_cb *callbacks);
+
+       u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+};
+
+
+static inline int ath10k_hif_send_head(struct ath10k *ar, u8 pipe_id,
+                                      unsigned int transfer_id,
+                                      unsigned int nbytes,
+                                      struct sk_buff *buf)
+{
+       return ar->hif.ops->send_head(ar, pipe_id, transfer_id, nbytes, buf);
+}
+
+static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
+                                             void *request, u32 request_len,
+                                             void *response, u32 *response_len)
+{
+       return ar->hif.ops->exchange_bmi_msg(ar, request, request_len,
+                                            response, response_len);
+}
+
+static inline int ath10k_hif_start(struct ath10k *ar)
+{
+       return ar->hif.ops->start(ar);
+}
+
+static inline void ath10k_hif_stop(struct ath10k *ar)
+{
+       return ar->hif.ops->stop(ar);
+}
+
+static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar,
+                                                u16 service_id,
+                                                u8 *ul_pipe, u8 *dl_pipe,
+                                                int *ul_is_polled,
+                                                int *dl_is_polled)
+{
+       return ar->hif.ops->map_service_to_pipe(ar, service_id,
+                                               ul_pipe, dl_pipe,
+                                               ul_is_polled, dl_is_polled);
+}
+
+static inline void ath10k_hif_get_default_pipe(struct ath10k *ar,
+                                              u8 *ul_pipe, u8 *dl_pipe)
+{
+       ar->hif.ops->get_default_pipe(ar, ul_pipe, dl_pipe);
+}
+
+static inline void ath10k_hif_send_complete_check(struct ath10k *ar,
+                                                 u8 pipe_id, int force)
+{
+       ar->hif.ops->send_complete_check(ar, pipe_id, force);
+}
+
+static inline void ath10k_hif_init(struct ath10k *ar,
+                                  struct ath10k_hif_cb *callbacks)
+{
+       ar->hif.ops->init(ar, callbacks);
+}
+
+static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
+                                                  u8 pipe_id)
+{
+       return ar->hif.ops->get_free_queue_number(ar, pipe_id);
+}
+
+#endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
new file mode 100644 (file)
index 0000000..74363c9
--- /dev/null
@@ -0,0 +1,1000 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "hif.h"
+#include "debug.h"
+
+/********/
+/* Send */
+/********/
+
+static inline void ath10k_htc_send_complete_check(struct ath10k_htc_ep *ep,
+                                                 int force)
+{
+       /*
+        * Check whether HIF has any prior sends that have finished,
+        * have not had the post-processing done.
+        */
+       ath10k_hif_send_complete_check(ep->htc->ar, ep->ul_pipe_id, force);
+}
+
+static void ath10k_htc_control_tx_complete(struct ath10k *ar,
+                                          struct sk_buff *skb)
+{
+       kfree_skb(skb);
+}
+
+static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *skb_cb;
+
+       skb = dev_alloc_skb(ATH10K_HTC_CONTROL_BUFFER_SIZE);
+       if (!skb) {
+               ath10k_warn("Unable to allocate ctrl skb\n");
+               return NULL;
+       }
+
+       skb_reserve(skb, 20); /* FIXME: why 20 bytes? */
+       WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
+
+       skb_cb = ATH10K_SKB_CB(skb);
+       memset(skb_cb, 0, sizeof(*skb_cb));
+
+       ath10k_dbg(ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb);
+       return skb;
+}
+
+static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc,
+                                            struct sk_buff *skb)
+{
+       ath10k_skb_unmap(htc->ar->dev, skb);
+       skb_pull(skb, sizeof(struct ath10k_htc_hdr));
+}
+
+static void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
+                  ep->eid, skb);
+
+       ath10k_htc_restore_tx_skb(ep->htc, skb);
+
+       if (!ep->ep_ops.ep_tx_complete) {
+               ath10k_warn("no tx handler for eid %d\n", ep->eid);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+
+       ep->ep_ops.ep_tx_complete(ep->htc->ar, skb);
+}
+
+/* assumes tx_lock is held */
+static bool ath10k_htc_ep_need_credit_update(struct ath10k_htc_ep *ep)
+{
+       if (!ep->tx_credit_flow_enabled)
+               return false;
+       if (ep->tx_credits >= ep->tx_credits_per_max_message)
+               return false;
+
+       ath10k_dbg(ATH10K_DBG_HTC, "HTC: endpoint %d needs credit update\n",
+                  ep->eid);
+       return true;
+}
+
+static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
+                                     struct sk_buff *skb)
+{
+       struct ath10k_htc_hdr *hdr;
+
+       hdr = (struct ath10k_htc_hdr *)skb->data;
+       memset(hdr, 0, sizeof(*hdr));
+
+       hdr->eid = ep->eid;
+       hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr));
+
+       spin_lock_bh(&ep->htc->tx_lock);
+       hdr->seq_no = ep->seq_no++;
+
+       if (ath10k_htc_ep_need_credit_update(ep))
+               hdr->flags |= ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE;
+
+       spin_unlock_bh(&ep->htc->tx_lock);
+}
+
+static int ath10k_htc_issue_skb(struct ath10k_htc *htc,
+                               struct ath10k_htc_ep *ep,
+                               struct sk_buff *skb,
+                               u8 credits)
+{
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
+                  ep->eid, skb);
+
+       ath10k_htc_prepare_tx_skb(ep, skb);
+
+       ret = ath10k_skb_map(htc->ar->dev, skb);
+       if (ret)
+               goto err;
+
+       ret = ath10k_hif_send_head(htc->ar,
+                                  ep->ul_pipe_id,
+                                  ep->eid,
+                                  skb->len,
+                                  skb);
+       if (unlikely(ret))
+               goto err;
+
+       return 0;
+err:
+       ath10k_warn("HTC issue failed: %d\n", ret);
+
+       spin_lock_bh(&htc->tx_lock);
+       ep->tx_credits += credits;
+       spin_unlock_bh(&htc->tx_lock);
+
+       /* this is the simplest way to handle out-of-resources for non-credit
+        * based endpoints. credit based endpoints can still get -ENOSR, but
+        * this is highly unlikely as credit reservation should prevent that */
+       if (ret == -ENOSR) {
+               spin_lock_bh(&htc->tx_lock);
+               __skb_queue_head(&ep->tx_queue, skb);
+               spin_unlock_bh(&htc->tx_lock);
+
+               return ret;
+       }
+
+       skb_cb->is_aborted = true;
+       ath10k_htc_notify_tx_completion(ep, skb);
+
+       return ret;
+}
+
+static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc,
+                                                      struct ath10k_htc_ep *ep,
+                                                      u8 *credits)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *skb_cb;
+       int credits_required;
+       int remainder;
+       unsigned int transfer_len;
+
+       lockdep_assert_held(&htc->tx_lock);
+
+       skb = __skb_dequeue(&ep->tx_queue);
+       if (!skb)
+               return NULL;
+
+       skb_cb = ATH10K_SKB_CB(skb);
+       transfer_len = skb->len;
+
+       if (likely(transfer_len <= htc->target_credit_size)) {
+               credits_required = 1;
+       } else {
+               /* figure out how many credits this message requires */
+               credits_required = transfer_len / htc->target_credit_size;
+               remainder = transfer_len % htc->target_credit_size;
+
+               if (remainder)
+                       credits_required++;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n",
+                  credits_required, ep->tx_credits);
+
+       if (ep->tx_credits < credits_required) {
+               __skb_queue_head(&ep->tx_queue, skb);
+               return NULL;
+       }
+
+       ep->tx_credits -= credits_required;
+       *credits = credits_required;
+       return skb;
+}
+
+static void ath10k_htc_send_work(struct work_struct *work)
+{
+       struct ath10k_htc_ep *ep = container_of(work,
+                                       struct ath10k_htc_ep, send_work);
+       struct ath10k_htc *htc = ep->htc;
+       struct sk_buff *skb;
+       u8 credits = 0;
+       int ret;
+
+       while (true) {
+               if (ep->ul_is_polled)
+                       ath10k_htc_send_complete_check(ep, 0);
+
+               spin_lock_bh(&htc->tx_lock);
+               if (ep->tx_credit_flow_enabled)
+                       skb = ath10k_htc_get_skb_credit_based(htc, ep,
+                                                             &credits);
+               else
+                       skb = __skb_dequeue(&ep->tx_queue);
+               spin_unlock_bh(&htc->tx_lock);
+
+               if (!skb)
+                       break;
+
+               ret = ath10k_htc_issue_skb(htc, ep, skb, credits);
+               if (ret == -ENOSR)
+                       break;
+       }
+}
+
+int ath10k_htc_send(struct ath10k_htc *htc,
+                   enum ath10k_htc_ep_id eid,
+                   struct sk_buff *skb)
+{
+       struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+
+       if (eid >= ATH10K_HTC_EP_COUNT) {
+               ath10k_warn("Invalid endpoint id: %d\n", eid);
+               return -ENOENT;
+       }
+
+       skb_push(skb, sizeof(struct ath10k_htc_hdr));
+
+       spin_lock_bh(&htc->tx_lock);
+       __skb_queue_tail(&ep->tx_queue, skb);
+       spin_unlock_bh(&htc->tx_lock);
+
+       queue_work(htc->ar->workqueue, &ep->send_work);
+       return 0;
+}
+
+static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
+                                           struct sk_buff *skb,
+                                           unsigned int eid)
+{
+       struct ath10k_htc *htc = ar->htc;
+       struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+       bool stopping;
+
+       ath10k_htc_notify_tx_completion(ep, skb);
+       /* the skb now belongs to the completion handler */
+
+       spin_lock_bh(&htc->tx_lock);
+       stopping = htc->stopping;
+       spin_unlock_bh(&htc->tx_lock);
+
+       if (!ep->tx_credit_flow_enabled && !stopping)
+               /*
+                * note: when using TX credit flow, the re-checking of
+                * queues happens when credits flow back from the target.
+                * in the non-TX credit case, we recheck after the packet
+                * completes
+                */
+               queue_work(ar->workqueue, &ep->send_work);
+
+       return 0;
+}
+
+/* flush endpoint TX queue */
+static void ath10k_htc_flush_endpoint_tx(struct ath10k_htc *htc,
+                                        struct ath10k_htc_ep *ep)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *skb_cb;
+
+       spin_lock_bh(&htc->tx_lock);
+       for (;;) {
+               skb = __skb_dequeue(&ep->tx_queue);
+               if (!skb)
+                       break;
+
+               skb_cb = ATH10K_SKB_CB(skb);
+               skb_cb->is_aborted = true;
+               ath10k_htc_notify_tx_completion(ep, skb);
+       }
+       spin_unlock_bh(&htc->tx_lock);
+
+       cancel_work_sync(&ep->send_work);
+}
+
+/***********/
+/* Receive */
+/***********/
+
+static void
+ath10k_htc_process_credit_report(struct ath10k_htc *htc,
+                                const struct ath10k_htc_credit_report *report,
+                                int len,
+                                enum ath10k_htc_ep_id eid)
+{
+       struct ath10k_htc_ep *ep;
+       int i, n_reports;
+
+       if (len % sizeof(*report))
+               ath10k_warn("Uneven credit report len %d", len);
+
+       n_reports = len / sizeof(*report);
+
+       spin_lock_bh(&htc->tx_lock);
+       for (i = 0; i < n_reports; i++, report++) {
+               if (report->eid >= ATH10K_HTC_EP_COUNT)
+                       break;
+
+               ath10k_dbg(ATH10K_DBG_HTC, "ep %d got %d credits\n",
+                          report->eid, report->credits);
+
+               ep = &htc->endpoint[report->eid];
+               ep->tx_credits += report->credits;
+
+               if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue))
+                       queue_work(htc->ar->workqueue, &ep->send_work);
+       }
+       spin_unlock_bh(&htc->tx_lock);
+}
+
+static int ath10k_htc_process_trailer(struct ath10k_htc *htc,
+                                     u8 *buffer,
+                                     int length,
+                                     enum ath10k_htc_ep_id src_eid)
+{
+       int status = 0;
+       struct ath10k_htc_record *record;
+       u8 *orig_buffer;
+       int orig_length;
+       size_t len;
+
+       orig_buffer = buffer;
+       orig_length = length;
+
+       while (length > 0) {
+               record = (struct ath10k_htc_record *)buffer;
+
+               if (length < sizeof(record->hdr)) {
+                       status = -EINVAL;
+                       break;
+               }
+
+               if (record->hdr.len > length) {
+                       /* no room left in buffer for record */
+                       ath10k_warn("Invalid record length: %d\n",
+                                   record->hdr.len);
+                       status = -EINVAL;
+                       break;
+               }
+
+               switch (record->hdr.id) {
+               case ATH10K_HTC_RECORD_CREDITS:
+                       len = sizeof(struct ath10k_htc_credit_report);
+                       if (record->hdr.len < len) {
+                               ath10k_warn("Credit report too long\n");
+                               status = -EINVAL;
+                               break;
+                       }
+                       ath10k_htc_process_credit_report(htc,
+                                                        record->credit_report,
+                                                        record->hdr.len,
+                                                        src_eid);
+                       break;
+               default:
+                       ath10k_warn("Unhandled record: id:%d length:%d\n",
+                                   record->hdr.id, record->hdr.len);
+                       break;
+               }
+
+               if (status)
+                       break;
+
+               /* multiple records may be present in a trailer */
+               buffer += sizeof(record->hdr) + record->hdr.len;
+               length -= sizeof(record->hdr) + record->hdr.len;
+       }
+
+       if (status)
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc rx bad trailer", "",
+                               orig_buffer, orig_length);
+
+       return status;
+}
+
+static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
+                                           struct sk_buff *skb,
+                                           u8 pipe_id)
+{
+       int status = 0;
+       struct ath10k_htc *htc = ar->htc;
+       struct ath10k_htc_hdr *hdr;
+       struct ath10k_htc_ep *ep;
+       u16 payload_len;
+       u32 trailer_len = 0;
+       size_t min_len;
+       u8 eid;
+       bool trailer_present;
+
+       hdr = (struct ath10k_htc_hdr *)skb->data;
+       skb_pull(skb, sizeof(*hdr));
+
+       eid = hdr->eid;
+
+       if (eid >= ATH10K_HTC_EP_COUNT) {
+               ath10k_warn("HTC Rx: invalid eid %d\n", eid);
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad header", "",
+                               hdr, sizeof(*hdr));
+               status = -EINVAL;
+               goto out;
+       }
+
+       ep = &htc->endpoint[eid];
+
+       /*
+        * If this endpoint that received a message from the target has
+        * a to-target HIF pipe whose send completions are polled rather
+        * than interrupt-driven, this is a good point to ask HIF to check
+        * whether it has any completed sends to handle.
+        */
+       if (ep->ul_is_polled)
+               ath10k_htc_send_complete_check(ep, 1);
+
+       payload_len = __le16_to_cpu(hdr->len);
+
+       if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) {
+               ath10k_warn("HTC rx frame too long, len: %zu\n",
+                           payload_len + sizeof(*hdr));
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len", "",
+                               hdr, sizeof(*hdr));
+               status = -EINVAL;
+               goto out;
+       }
+
+       if (skb->len < payload_len) {
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "HTC Rx: insufficient length, got %d, expected %d\n",
+                          skb->len, payload_len);
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len",
+                               "", hdr, sizeof(*hdr));
+               status = -EINVAL;
+               goto out;
+       }
+
+       /* get flags to check for trailer */
+       trailer_present = hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
+       if (trailer_present) {
+               u8 *trailer;
+
+               trailer_len = hdr->trailer_len;
+               min_len = sizeof(struct ath10k_ath10k_htc_record_hdr);
+
+               if ((trailer_len < min_len) ||
+                   (trailer_len > payload_len)) {
+                       ath10k_warn("Invalid trailer length: %d\n",
+                                   trailer_len);
+                       status = -EPROTO;
+                       goto out;
+               }
+
+               trailer = (u8 *)hdr;
+               trailer += sizeof(*hdr);
+               trailer += payload_len;
+               trailer -= trailer_len;
+               status = ath10k_htc_process_trailer(htc, trailer,
+                                                   trailer_len, hdr->eid);
+               if (status)
+                       goto out;
+
+               skb_trim(skb, skb->len - trailer_len);
+       }
+
+       if (((int)payload_len - (int)trailer_len) <= 0)
+               /* zero length packet with trailer data, just drop these */
+               goto out;
+
+       if (eid == ATH10K_HTC_EP_0) {
+               struct ath10k_htc_msg *msg = (struct ath10k_htc_msg *)skb->data;
+
+               switch (__le16_to_cpu(msg->hdr.message_id)) {
+               default:
+                       /* handle HTC control message */
+                       if (completion_done(&htc->ctl_resp)) {
+                               /*
+                                * this is a fatal error, target should not be
+                                * sending unsolicited messages on the ep 0
+                                */
+                               ath10k_warn("HTC rx ctrl still processing\n");
+                               status = -EINVAL;
+                               complete(&htc->ctl_resp);
+                               goto out;
+                       }
+
+                       htc->control_resp_len =
+                               min_t(int, skb->len,
+                                     ATH10K_HTC_MAX_CTRL_MSG_LEN);
+
+                       memcpy(htc->control_resp_buffer, skb->data,
+                              htc->control_resp_len);
+
+                       complete(&htc->ctl_resp);
+                       break;
+               case ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE:
+                       htc->htc_ops.target_send_suspend_complete(ar);
+               }
+               goto out;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n",
+                  eid, skb);
+       ep->ep_ops.ep_rx_complete(ar, skb);
+
+       /* skb is now owned by the rx completion handler */
+       skb = NULL;
+out:
+       kfree_skb(skb);
+
+       return status;
+}
+
+static void ath10k_htc_control_rx_complete(struct ath10k *ar,
+                                          struct sk_buff *skb)
+{
+       /* This is unexpected. FW is not supposed to send regular rx on this
+        * endpoint. */
+       ath10k_warn("unexpected htc rx\n");
+       kfree_skb(skb);
+}
+
+/***************/
+/* Init/Deinit */
+/***************/
+
+static const char *htc_service_name(enum ath10k_htc_svc_id id)
+{
+       switch (id) {
+       case ATH10K_HTC_SVC_ID_RESERVED:
+               return "Reserved";
+       case ATH10K_HTC_SVC_ID_RSVD_CTRL:
+               return "Control";
+       case ATH10K_HTC_SVC_ID_WMI_CONTROL:
+               return "WMI";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BE:
+               return "DATA BE";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BK:
+               return "DATA BK";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VI:
+               return "DATA VI";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VO:
+               return "DATA VO";
+       case ATH10K_HTC_SVC_ID_NMI_CONTROL:
+               return "NMI Control";
+       case ATH10K_HTC_SVC_ID_NMI_DATA:
+               return "NMI Data";
+       case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
+               return "HTT Data";
+       case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS:
+               return "RAW";
+       }
+
+       return "Unknown";
+}
+
+static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc)
+{
+       struct ath10k_htc_ep *ep;
+       int i;
+
+       for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
+               ep = &htc->endpoint[i];
+               ep->service_id = ATH10K_HTC_SVC_ID_UNUSED;
+               ep->max_ep_message_len = 0;
+               ep->max_tx_queue_depth = 0;
+               ep->eid = i;
+               skb_queue_head_init(&ep->tx_queue);
+               ep->htc = htc;
+               ep->tx_credit_flow_enabled = true;
+               INIT_WORK(&ep->send_work, ath10k_htc_send_work);
+       }
+}
+
+static void ath10k_htc_setup_target_buffer_assignments(struct ath10k_htc *htc)
+{
+       struct ath10k_htc_svc_tx_credits *entry;
+
+       entry = &htc->service_tx_alloc[0];
+
+       /*
+        * for PCIE allocate all credists/HTC buffers to WMI.
+        * no buffers are used/required for data. data always
+        * remains on host.
+        */
+       entry++;
+       entry->service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
+       entry->credit_allocation = htc->total_transmit_credits;
+}
+
+static u8 ath10k_htc_get_credit_allocation(struct ath10k_htc *htc,
+                                          u16 service_id)
+{
+       u8 allocation = 0;
+       int i;
+
+       for (i = 0; i < ATH10K_HTC_EP_COUNT; i++) {
+               if (htc->service_tx_alloc[i].service_id == service_id)
+                       allocation =
+                           htc->service_tx_alloc[i].credit_allocation;
+       }
+
+       return allocation;
+}
+
+int ath10k_htc_wait_target(struct ath10k_htc *htc)
+{
+       int status = 0;
+       struct ath10k_htc_svc_conn_req conn_req;
+       struct ath10k_htc_svc_conn_resp conn_resp;
+       struct ath10k_htc_msg *msg;
+       u16 message_id;
+       u16 credit_count;
+       u16 credit_size;
+
+       INIT_COMPLETION(htc->ctl_resp);
+
+       status = ath10k_hif_start(htc->ar);
+       if (status) {
+               ath10k_err("could not start HIF (%d)\n", status);
+               goto err_start;
+       }
+
+       status = wait_for_completion_timeout(&htc->ctl_resp,
+                                            ATH10K_HTC_WAIT_TIMEOUT_HZ);
+       if (status <= 0) {
+               if (status == 0)
+                       status = -ETIMEDOUT;
+
+               ath10k_err("ctl_resp never came in (%d)\n", status);
+               goto err_target;
+       }
+
+       if (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->ready)) {
+               ath10k_err("Invalid HTC ready msg len:%d\n",
+                          htc->control_resp_len);
+
+               status = -ECOMM;
+               goto err_target;
+       }
+
+       msg = (struct ath10k_htc_msg *)htc->control_resp_buffer;
+       message_id   = __le16_to_cpu(msg->hdr.message_id);
+       credit_count = __le16_to_cpu(msg->ready.credit_count);
+       credit_size  = __le16_to_cpu(msg->ready.credit_size);
+
+       if (message_id != ATH10K_HTC_MSG_READY_ID) {
+               ath10k_err("Invalid HTC ready msg: 0x%x\n", message_id);
+               status = -ECOMM;
+               goto err_target;
+       }
+
+       htc->total_transmit_credits = credit_count;
+       htc->target_credit_size = credit_size;
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "Target ready! transmit resources: %d size:%d\n",
+                  htc->total_transmit_credits,
+                  htc->target_credit_size);
+
+       if ((htc->total_transmit_credits == 0) ||
+           (htc->target_credit_size == 0)) {
+               status = -ECOMM;
+               ath10k_err("Invalid credit size received\n");
+               goto err_target;
+       }
+
+       ath10k_htc_setup_target_buffer_assignments(htc);
+
+       /* setup our pseudo HTC control endpoint connection */
+       memset(&conn_req, 0, sizeof(conn_req));
+       memset(&conn_resp, 0, sizeof(conn_resp));
+       conn_req.ep_ops.ep_tx_complete = ath10k_htc_control_tx_complete;
+       conn_req.ep_ops.ep_rx_complete = ath10k_htc_control_rx_complete;
+       conn_req.max_send_queue_depth = ATH10K_NUM_CONTROL_TX_BUFFERS;
+       conn_req.service_id = ATH10K_HTC_SVC_ID_RSVD_CTRL;
+
+       /* connect fake service */
+       status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp);
+       if (status) {
+               ath10k_err("could not connect to htc service (%d)\n", status);
+               goto err_target;
+       }
+
+       return 0;
+err_target:
+       ath10k_hif_stop(htc->ar);
+err_start:
+       return status;
+}
+
+int ath10k_htc_connect_service(struct ath10k_htc *htc,
+                              struct ath10k_htc_svc_conn_req *conn_req,
+                              struct ath10k_htc_svc_conn_resp *conn_resp)
+{
+       struct ath10k_htc_msg *msg;
+       struct ath10k_htc_conn_svc *req_msg;
+       struct ath10k_htc_conn_svc_response resp_msg_dummy;
+       struct ath10k_htc_conn_svc_response *resp_msg = &resp_msg_dummy;
+       enum ath10k_htc_ep_id assigned_eid = ATH10K_HTC_EP_COUNT;
+       struct ath10k_htc_ep *ep;
+       struct sk_buff *skb;
+       unsigned int max_msg_size = 0;
+       int length, status;
+       bool disable_credit_flow_ctrl = false;
+       u16 message_id, service_id, flags = 0;
+       u8 tx_alloc = 0;
+
+       /* special case for HTC pseudo control service */
+       if (conn_req->service_id == ATH10K_HTC_SVC_ID_RSVD_CTRL) {
+               disable_credit_flow_ctrl = true;
+               assigned_eid = ATH10K_HTC_EP_0;
+               max_msg_size = ATH10K_HTC_MAX_CTRL_MSG_LEN;
+               memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy));
+               goto setup;
+       }
+
+       tx_alloc = ath10k_htc_get_credit_allocation(htc,
+                                                   conn_req->service_id);
+       if (!tx_alloc)
+               ath10k_warn("HTC Service %s does not allocate target credits\n",
+                           htc_service_name(conn_req->service_id));
+
+       skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
+       if (!skb) {
+               ath10k_err("Failed to allocate HTC packet\n");
+               return -ENOMEM;
+       }
+
+       length = sizeof(msg->hdr) + sizeof(msg->connect_service);
+       skb_put(skb, length);
+       memset(skb->data, 0, length);
+
+       msg = (struct ath10k_htc_msg *)skb->data;
+       msg->hdr.message_id =
+               __cpu_to_le16(ATH10K_HTC_MSG_CONNECT_SERVICE_ID);
+
+       flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC);
+
+       req_msg = &msg->connect_service;
+       req_msg->flags = __cpu_to_le16(flags);
+       req_msg->service_id = __cpu_to_le16(conn_req->service_id);
+
+       /* Only enable credit flow control for WMI ctrl service */
+       if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) {
+               flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
+               disable_credit_flow_ctrl = true;
+       }
+
+       INIT_COMPLETION(htc->ctl_resp);
+
+       status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
+       if (status) {
+               kfree_skb(skb);
+               return status;
+       }
+
+       /* wait for response */
+       status = wait_for_completion_timeout(&htc->ctl_resp,
+                                            ATH10K_HTC_CONN_SVC_TIMEOUT_HZ);
+       if (status <= 0) {
+               if (status == 0)
+                       status = -ETIMEDOUT;
+               ath10k_err("Service connect timeout: %d\n", status);
+               return status;
+       }
+
+       /* we controlled the buffer creation, it's aligned */
+       msg = (struct ath10k_htc_msg *)htc->control_resp_buffer;
+       resp_msg = &msg->connect_service_response;
+       message_id = __le16_to_cpu(msg->hdr.message_id);
+       service_id = __le16_to_cpu(resp_msg->service_id);
+
+       if ((message_id != ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||
+           (htc->control_resp_len < sizeof(msg->hdr) +
+            sizeof(msg->connect_service_response))) {
+               ath10k_err("Invalid resp message ID 0x%x", message_id);
+               return -EPROTO;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "HTC Service %s connect response: status: 0x%x, assigned ep: 0x%x\n",
+                  htc_service_name(service_id),
+                  resp_msg->status, resp_msg->eid);
+
+       conn_resp->connect_resp_code = resp_msg->status;
+
+       /* check response status */
+       if (resp_msg->status != ATH10K_HTC_CONN_SVC_STATUS_SUCCESS) {
+               ath10k_err("HTC Service %s connect request failed: 0x%x)\n",
+                          htc_service_name(service_id),
+                          resp_msg->status);
+               return -EPROTO;
+       }
+
+       assigned_eid = (enum ath10k_htc_ep_id)resp_msg->eid;
+       max_msg_size = __le16_to_cpu(resp_msg->max_msg_size);
+
+setup:
+
+       if (assigned_eid >= ATH10K_HTC_EP_COUNT)
+               return -EPROTO;
+
+       if (max_msg_size == 0)
+               return -EPROTO;
+
+       ep = &htc->endpoint[assigned_eid];
+       ep->eid = assigned_eid;
+
+       if (ep->service_id != ATH10K_HTC_SVC_ID_UNUSED)
+               return -EPROTO;
+
+       /* return assigned endpoint to caller */
+       conn_resp->eid = assigned_eid;
+       conn_resp->max_msg_len = __le16_to_cpu(resp_msg->max_msg_size);
+
+       /* setup the endpoint */
+       ep->service_id = conn_req->service_id;
+       ep->max_tx_queue_depth = conn_req->max_send_queue_depth;
+       ep->max_ep_message_len = __le16_to_cpu(resp_msg->max_msg_size);
+       ep->tx_credits = tx_alloc;
+       ep->tx_credit_size = htc->target_credit_size;
+       ep->tx_credits_per_max_message = ep->max_ep_message_len /
+                                        htc->target_credit_size;
+
+       if (ep->max_ep_message_len % htc->target_credit_size)
+               ep->tx_credits_per_max_message++;
+
+       /* copy all the callbacks */
+       ep->ep_ops = conn_req->ep_ops;
+
+       status = ath10k_hif_map_service_to_pipe(htc->ar,
+                                               ep->service_id,
+                                               &ep->ul_pipe_id,
+                                               &ep->dl_pipe_id,
+                                               &ep->ul_is_polled,
+                                               &ep->dl_is_polled);
+       if (status)
+               return status;
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n",
+                  htc_service_name(ep->service_id), ep->ul_pipe_id,
+                  ep->dl_pipe_id, ep->eid);
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "EP %d UL polled: %d, DL polled: %d\n",
+                  ep->eid, ep->ul_is_polled, ep->dl_is_polled);
+
+       if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
+               ep->tx_credit_flow_enabled = false;
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "HTC service: %s eid: %d TX flow control disabled\n",
+                          htc_service_name(ep->service_id), assigned_eid);
+       }
+
+       return status;
+}
+
+struct sk_buff *ath10k_htc_alloc_skb(int size)
+{
+       struct sk_buff *skb;
+
+       skb = dev_alloc_skb(size + sizeof(struct ath10k_htc_hdr));
+       if (!skb) {
+               ath10k_warn("could not allocate HTC tx skb\n");
+               return NULL;
+       }
+
+       skb_reserve(skb, sizeof(struct ath10k_htc_hdr));
+
+       /* FW/HTC requires 4-byte aligned streams */
+       if (!IS_ALIGNED((unsigned long)skb->data, 4))
+               ath10k_warn("Unaligned HTC tx skb\n");
+
+       return skb;
+}
+
+int ath10k_htc_start(struct ath10k_htc *htc)
+{
+       struct sk_buff *skb;
+       int status = 0;
+       struct ath10k_htc_msg *msg;
+
+       skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(msg->hdr) + sizeof(msg->setup_complete_ext));
+       memset(skb->data, 0, skb->len);
+
+       msg = (struct ath10k_htc_msg *)skb->data;
+       msg->hdr.message_id =
+               __cpu_to_le16(ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID);
+
+       ath10k_dbg(ATH10K_DBG_HTC, "HTC is using TX credit flow control\n");
+
+       status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
+       if (status) {
+               kfree_skb(skb);
+               return status;
+       }
+
+       return 0;
+}
+
+/*
+ * stop HTC communications, i.e. stop interrupt reception, and flush all
+ * queued buffers
+ */
+void ath10k_htc_stop(struct ath10k_htc *htc)
+{
+       int i;
+       struct ath10k_htc_ep *ep;
+
+       spin_lock_bh(&htc->tx_lock);
+       htc->stopping = true;
+       spin_unlock_bh(&htc->tx_lock);
+
+       for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
+               ep = &htc->endpoint[i];
+               ath10k_htc_flush_endpoint_tx(htc, ep);
+       }
+
+       ath10k_hif_stop(htc->ar);
+       ath10k_htc_reset_endpoint_states(htc);
+}
+
+/* registered target arrival callback from the HIF layer */
+struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
+                                    struct ath10k_htc_ops *htc_ops)
+{
+       struct ath10k_hif_cb htc_callbacks;
+       struct ath10k_htc_ep *ep = NULL;
+       struct ath10k_htc *htc = NULL;
+
+       /* FIXME: use struct ath10k instead */
+       htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL);
+       if (!htc)
+               return ERR_PTR(-ENOMEM);
+
+       spin_lock_init(&htc->tx_lock);
+
+       memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops));
+
+       ath10k_htc_reset_endpoint_states(htc);
+
+       /* setup HIF layer callbacks */
+       htc_callbacks.rx_completion = ath10k_htc_rx_completion_handler;
+       htc_callbacks.tx_completion = ath10k_htc_tx_completion_handler;
+       htc->ar = ar;
+
+       /* Get HIF default pipe for HTC message exchange */
+       ep = &htc->endpoint[ATH10K_HTC_EP_0];
+
+       ath10k_hif_init(ar, &htc_callbacks);
+       ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id);
+
+       init_completion(&htc->ctl_resp);
+
+       return htc;
+}
+
+void ath10k_htc_destroy(struct ath10k_htc *htc)
+{
+       kfree(htc);
+}
diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h
new file mode 100644 (file)
index 0000000..fa45844
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _HTC_H_
+#define _HTC_H_
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/bug.h>
+#include <linux/skbuff.h>
+#include <linux/semaphore.h>
+#include <linux/timer.h>
+
+struct ath10k;
+
+/****************/
+/* HTC protocol */
+/****************/
+
+/*
+ * HTC - host-target control protocol
+ *
+ * tx packets are generally <htc_hdr><payload>
+ * rx packets are more complex: <htc_hdr><payload><trailer>
+ *
+ * The payload + trailer length is stored in len.
+ * To get payload-only length one needs to payload - trailer_len.
+ *
+ * Trailer contains (possibly) multiple <htc_record>.
+ * Each record is a id-len-value.
+ *
+ * HTC header flags, control_byte0, control_byte1
+ * have different meaning depending whether its tx
+ * or rx.
+ *
+ * Alignment: htc_hdr, payload and trailer are
+ * 4-byte aligned.
+ */
+
+enum ath10k_htc_tx_flags {
+       ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01,
+       ATH10K_HTC_FLAG_SEND_BUNDLE        = 0x02
+};
+
+enum ath10k_htc_rx_flags {
+       ATH10K_HTC_FLAG_TRAILER_PRESENT = 0x02,
+       ATH10K_HTC_FLAG_BUNDLE_MASK     = 0xF0
+};
+
+struct ath10k_htc_hdr {
+       u8 eid; /* @enum ath10k_htc_ep_id */
+       u8 flags; /* @enum ath10k_htc_tx_flags, ath10k_htc_rx_flags */
+       __le16 len;
+       union {
+               u8 trailer_len; /* for rx */
+               u8 control_byte0;
+       } __packed;
+       union {
+               u8 seq_no; /* for tx */
+               u8 control_byte1;
+       } __packed;
+       u8 pad0;
+       u8 pad1;
+} __packed __aligned(4);
+
+enum ath10k_ath10k_htc_msg_id {
+       ATH10K_HTC_MSG_READY_ID                = 1,
+       ATH10K_HTC_MSG_CONNECT_SERVICE_ID      = 2,
+       ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3,
+       ATH10K_HTC_MSG_SETUP_COMPLETE_ID       = 4,
+       ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID    = 5,
+       ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE   = 6
+};
+
+enum ath10k_htc_version {
+       ATH10K_HTC_VERSION_2P0 = 0x00, /* 2.0 */
+       ATH10K_HTC_VERSION_2P1 = 0x01, /* 2.1 */
+};
+
+enum ath10k_htc_conn_flags {
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH    = 0x0,
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF      = 0x1,
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2,
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY         = 0x3,
+#define ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK 0x3
+       ATH10K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE    = 1 << 2,
+       ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 1 << 3
+#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_MASK 0xFF00
+#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_LSB  8
+};
+
+enum ath10k_htc_conn_svc_status {
+       ATH10K_HTC_CONN_SVC_STATUS_SUCCESS      = 0,
+       ATH10K_HTC_CONN_SVC_STATUS_NOT_FOUND    = 1,
+       ATH10K_HTC_CONN_SVC_STATUS_FAILED       = 2,
+       ATH10K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3,
+       ATH10K_HTC_CONN_SVC_STATUS_NO_MORE_EP   = 4
+};
+
+struct ath10k_ath10k_htc_msg_hdr {
+       __le16 message_id; /* @enum htc_message_id */
+} __packed;
+
+struct ath10k_htc_unknown {
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_ready {
+       __le16 credit_count;
+       __le16 credit_size;
+       u8 max_endpoints;
+       u8 pad0;
+} __packed;
+
+struct ath10k_htc_ready_extended {
+       struct ath10k_htc_ready base;
+       u8 htc_version; /* @enum ath10k_htc_version */
+       u8 max_msgs_per_htc_bundle;
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_conn_svc {
+       __le16 service_id;
+       __le16 flags; /* @enum ath10k_htc_conn_flags */
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_conn_svc_response {
+       __le16 service_id;
+       u8 status; /* @enum ath10k_htc_conn_svc_status */
+       u8 eid;
+       __le16 max_msg_size;
+} __packed;
+
+struct ath10k_htc_setup_complete_extended {
+       u8 pad0;
+       u8 pad1;
+       __le32 flags; /* @enum htc_setup_complete_flags */
+       u8 max_msgs_per_bundled_recv;
+       u8 pad2;
+       u8 pad3;
+       u8 pad4;
+} __packed;
+
+struct ath10k_htc_msg {
+       struct ath10k_ath10k_htc_msg_hdr hdr;
+       union {
+               /* host-to-target */
+               struct ath10k_htc_conn_svc connect_service;
+               struct ath10k_htc_ready ready;
+               struct ath10k_htc_ready_extended ready_ext;
+               struct ath10k_htc_unknown unknown;
+               struct ath10k_htc_setup_complete_extended setup_complete_ext;
+
+               /* target-to-host */
+               struct ath10k_htc_conn_svc_response connect_service_response;
+       };
+} __packed __aligned(4);
+
+enum ath10k_ath10k_htc_record_id {
+       ATH10K_HTC_RECORD_NULL    = 0,
+       ATH10K_HTC_RECORD_CREDITS = 1
+};
+
+struct ath10k_ath10k_htc_record_hdr {
+       u8 id; /* @enum ath10k_ath10k_htc_record_id */
+       u8 len;
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_credit_report {
+       u8 eid; /* @enum ath10k_htc_ep_id */
+       u8 credits;
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_record {
+       struct ath10k_ath10k_htc_record_hdr hdr;
+       union {
+               struct ath10k_htc_credit_report credit_report[0];
+               u8 pauload[0];
+       };
+} __packed __aligned(4);
+
+/*
+ * note: the trailer offset is dynamic depending
+ * on payload length. this is only a struct layout draft
+ */
+struct ath10k_htc_frame {
+       struct ath10k_htc_hdr hdr;
+       union {
+               struct ath10k_htc_msg msg;
+               u8 payload[0];
+       };
+       struct ath10k_htc_record trailer[0];
+} __packed __aligned(4);
+
+
+/*******************/
+/* Host-side stuff */
+/*******************/
+
+enum ath10k_htc_svc_gid {
+       ATH10K_HTC_SVC_GRP_RSVD = 0,
+       ATH10K_HTC_SVC_GRP_WMI = 1,
+       ATH10K_HTC_SVC_GRP_NMI = 2,
+       ATH10K_HTC_SVC_GRP_HTT = 3,
+
+       ATH10K_HTC_SVC_GRP_TEST = 254,
+       ATH10K_HTC_SVC_GRP_LAST = 255,
+};
+
+#define SVC(group, idx) \
+       (int)(((int)(group) << 8) | (int)(idx))
+
+enum ath10k_htc_svc_id {
+       /* NOTE: service ID of 0x0000 is reserved and should never be used */
+       ATH10K_HTC_SVC_ID_RESERVED      = 0x0000,
+       ATH10K_HTC_SVC_ID_UNUSED        = ATH10K_HTC_SVC_ID_RESERVED,
+
+       ATH10K_HTC_SVC_ID_RSVD_CTRL     = SVC(ATH10K_HTC_SVC_GRP_RSVD, 1),
+       ATH10K_HTC_SVC_ID_WMI_CONTROL   = SVC(ATH10K_HTC_SVC_GRP_WMI, 0),
+       ATH10K_HTC_SVC_ID_WMI_DATA_BE   = SVC(ATH10K_HTC_SVC_GRP_WMI, 1),
+       ATH10K_HTC_SVC_ID_WMI_DATA_BK   = SVC(ATH10K_HTC_SVC_GRP_WMI, 2),
+       ATH10K_HTC_SVC_ID_WMI_DATA_VI   = SVC(ATH10K_HTC_SVC_GRP_WMI, 3),
+       ATH10K_HTC_SVC_ID_WMI_DATA_VO   = SVC(ATH10K_HTC_SVC_GRP_WMI, 4),
+
+       ATH10K_HTC_SVC_ID_NMI_CONTROL   = SVC(ATH10K_HTC_SVC_GRP_NMI, 0),
+       ATH10K_HTC_SVC_ID_NMI_DATA      = SVC(ATH10K_HTC_SVC_GRP_NMI, 1),
+
+       ATH10K_HTC_SVC_ID_HTT_DATA_MSG  = SVC(ATH10K_HTC_SVC_GRP_HTT, 0),
+
+       /* raw stream service (i.e. flash, tcmd, calibration apps) */
+       ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH10K_HTC_SVC_GRP_TEST, 0),
+};
+
+#undef SVC
+
+enum ath10k_htc_ep_id {
+       ATH10K_HTC_EP_UNUSED = -1,
+       ATH10K_HTC_EP_0 = 0,
+       ATH10K_HTC_EP_1 = 1,
+       ATH10K_HTC_EP_2,
+       ATH10K_HTC_EP_3,
+       ATH10K_HTC_EP_4,
+       ATH10K_HTC_EP_5,
+       ATH10K_HTC_EP_6,
+       ATH10K_HTC_EP_7,
+       ATH10K_HTC_EP_8,
+       ATH10K_HTC_EP_COUNT,
+};
+
+struct ath10k_htc_ops {
+       void (*target_send_suspend_complete)(struct ath10k *ar);
+};
+
+struct ath10k_htc_ep_ops {
+       void (*ep_tx_complete)(struct ath10k *, struct sk_buff *);
+       void (*ep_rx_complete)(struct ath10k *, struct sk_buff *);
+};
+
+/* service connection information */
+struct ath10k_htc_svc_conn_req {
+       u16 service_id;
+       struct ath10k_htc_ep_ops ep_ops;
+       int max_send_queue_depth;
+};
+
+/* service connection response information */
+struct ath10k_htc_svc_conn_resp {
+       u8 buffer_len;
+       u8 actual_len;
+       enum ath10k_htc_ep_id eid;
+       unsigned int max_msg_len;
+       u8 connect_resp_code;
+};
+
+#define ATH10K_NUM_CONTROL_TX_BUFFERS 2
+#define ATH10K_HTC_MAX_LEN 4096
+#define ATH10K_HTC_MAX_CTRL_MSG_LEN 256
+#define ATH10K_HTC_WAIT_TIMEOUT_HZ (1*HZ)
+#define ATH10K_HTC_CONTROL_BUFFER_SIZE (ATH10K_HTC_MAX_CTRL_MSG_LEN + \
+                                       sizeof(struct ath10k_htc_hdr))
+#define ATH10K_HTC_CONN_SVC_TIMEOUT_HZ (1*HZ)
+
+struct ath10k_htc_ep {
+       struct ath10k_htc *htc;
+       enum ath10k_htc_ep_id eid;
+       enum ath10k_htc_svc_id service_id;
+       struct ath10k_htc_ep_ops ep_ops;
+
+       int max_tx_queue_depth;
+       int max_ep_message_len;
+       u8 ul_pipe_id;
+       u8 dl_pipe_id;
+       int ul_is_polled; /* call HIF to get tx completions */
+       int dl_is_polled; /* call HIF to fetch rx (not implemented) */
+
+       struct sk_buff_head tx_queue;
+
+       u8 seq_no; /* for debugging */
+       int tx_credits;
+       int tx_credit_size;
+       int tx_credits_per_max_message;
+       bool tx_credit_flow_enabled;
+
+       struct work_struct send_work;
+};
+
+struct ath10k_htc_svc_tx_credits {
+       u16 service_id;
+       u8  credit_allocation;
+};
+
+struct ath10k_htc {
+       struct ath10k *ar;
+       struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
+
+       /* protects endpoint and stopping fields */
+       spinlock_t tx_lock;
+
+       struct ath10k_htc_ops htc_ops;
+
+       u8 control_resp_buffer[ATH10K_HTC_MAX_CTRL_MSG_LEN];
+       int control_resp_len;
+
+       struct completion ctl_resp;
+
+       int total_transmit_credits;
+       struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT];
+       int target_credit_size;
+
+       bool stopping;
+};
+
+struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
+                                    struct ath10k_htc_ops *htc_ops);
+int ath10k_htc_wait_target(struct ath10k_htc *htc);
+int ath10k_htc_start(struct ath10k_htc *htc);
+int ath10k_htc_connect_service(struct ath10k_htc *htc,
+                              struct ath10k_htc_svc_conn_req  *conn_req,
+                              struct ath10k_htc_svc_conn_resp *conn_resp);
+int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
+                   struct sk_buff *packet);
+void ath10k_htc_stop(struct ath10k_htc *htc);
+void ath10k_htc_destroy(struct ath10k_htc *htc);
+struct sk_buff *ath10k_htc_alloc_skb(int size);
+
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c
new file mode 100644 (file)
index 0000000..185a546
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/slab.h>
+
+#include "htt.h"
+#include "core.h"
+#include "debug.h"
+
+static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
+{
+       struct ath10k_htc_svc_conn_req conn_req;
+       struct ath10k_htc_svc_conn_resp conn_resp;
+       int status;
+
+       memset(&conn_req, 0, sizeof(conn_req));
+       memset(&conn_resp, 0, sizeof(conn_resp));
+
+       conn_req.ep_ops.ep_tx_complete = ath10k_htt_htc_tx_complete;
+       conn_req.ep_ops.ep_rx_complete = ath10k_htt_t2h_msg_handler;
+
+       /* connect to control service */
+       conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG;
+
+       status = ath10k_htc_connect_service(htt->ar->htc, &conn_req,
+                                           &conn_resp);
+
+       if (status)
+               return status;
+
+       htt->eid = conn_resp.eid;
+
+       return 0;
+}
+
+struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
+{
+       struct ath10k_htt *htt;
+       int ret;
+
+       htt = kzalloc(sizeof(*htt), GFP_KERNEL);
+       if (!htt)
+               return NULL;
+
+       htt->ar = ar;
+       htt->max_throughput_mbps = 800;
+
+       /*
+        * Connect to HTC service.
+        * This has to be done before calling ath10k_htt_rx_attach,
+        * since ath10k_htt_rx_attach involves sending a rx ring configure
+        * message to the target.
+        */
+       if (ath10k_htt_htc_attach(htt))
+               goto err_htc_attach;
+
+       ret = ath10k_htt_tx_attach(htt);
+       if (ret) {
+               ath10k_err("could not attach htt tx (%d)\n", ret);
+               goto err_htc_attach;
+       }
+
+       if (ath10k_htt_rx_attach(htt))
+               goto err_rx_attach;
+
+       /*
+        * Prefetch enough data to satisfy target
+        * classification engine.
+        * This is for LL chips. HL chips will probably
+        * transfer all frame in the tx fragment.
+        */
+       htt->prefetch_len =
+               36 + /* 802.11 + qos + ht */
+               4 + /* 802.1q */
+               8 + /* llc snap */
+               2; /* ip4 dscp or ip6 priority */
+
+       return htt;
+
+err_rx_attach:
+       ath10k_htt_tx_detach(htt);
+err_htc_attach:
+       kfree(htt);
+       return NULL;
+}
+
+#define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
+
+static int ath10k_htt_verify_version(struct ath10k_htt *htt)
+{
+       ath10k_dbg(ATH10K_DBG_HTT,
+                  "htt target version %d.%d; host version %d.%d\n",
+                   htt->target_version_major,
+                   htt->target_version_minor,
+                   HTT_CURRENT_VERSION_MAJOR,
+                   HTT_CURRENT_VERSION_MINOR);
+
+       if (htt->target_version_major != HTT_CURRENT_VERSION_MAJOR) {
+               ath10k_err("htt major versions are incompatible!\n");
+               return -ENOTSUPP;
+       }
+
+       if (htt->target_version_minor != HTT_CURRENT_VERSION_MINOR)
+               ath10k_warn("htt minor version differ but still compatible\n");
+
+       return 0;
+}
+
+int ath10k_htt_attach_target(struct ath10k_htt *htt)
+{
+       int status;
+
+       init_completion(&htt->target_version_received);
+
+       status = ath10k_htt_h2t_ver_req_msg(htt);
+       if (status)
+               return status;
+
+       status = wait_for_completion_timeout(&htt->target_version_received,
+                                               HTT_TARGET_VERSION_TIMEOUT_HZ);
+       if (status <= 0) {
+               ath10k_warn("htt version request timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       status = ath10k_htt_verify_version(htt);
+       if (status)
+               return status;
+
+       return ath10k_htt_send_rx_ring_cfg_ll(htt);
+}
+
+void ath10k_htt_detach(struct ath10k_htt *htt)
+{
+       ath10k_htt_rx_detach(htt);
+       ath10k_htt_tx_detach(htt);
+       kfree(htt);
+}
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
new file mode 100644 (file)
index 0000000..a7a7aa0
--- /dev/null
@@ -0,0 +1,1338 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _HTT_H_
+#define _HTT_H_
+
+#include <linux/bug.h>
+
+#include "core.h"
+#include "htc.h"
+#include "rx_desc.h"
+
+#define HTT_CURRENT_VERSION_MAJOR      2
+#define HTT_CURRENT_VERSION_MINOR      1
+
+enum htt_dbg_stats_type {
+       HTT_DBG_STATS_WAL_PDEV_TXRX = 1 << 0,
+       HTT_DBG_STATS_RX_REORDER    = 1 << 1,
+       HTT_DBG_STATS_RX_RATE_INFO  = 1 << 2,
+       HTT_DBG_STATS_TX_PPDU_LOG   = 1 << 3,
+       HTT_DBG_STATS_TX_RATE_INFO  = 1 << 4,
+       /* bits 5-23 currently reserved */
+
+       HTT_DBG_NUM_STATS /* keep this last */
+};
+
+enum htt_h2t_msg_type { /* host-to-target */
+       HTT_H2T_MSG_TYPE_VERSION_REQ        = 0,
+       HTT_H2T_MSG_TYPE_TX_FRM             = 1,
+       HTT_H2T_MSG_TYPE_RX_RING_CFG        = 2,
+       HTT_H2T_MSG_TYPE_STATS_REQ          = 3,
+       HTT_H2T_MSG_TYPE_SYNC               = 4,
+       HTT_H2T_MSG_TYPE_AGGR_CFG           = 5,
+       HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 6,
+       HTT_H2T_MSG_TYPE_MGMT_TX            = 7,
+
+       HTT_H2T_NUM_MSGS /* keep this last */
+};
+
+struct htt_cmd_hdr {
+       u8 msg_type;
+} __packed;
+
+struct htt_ver_req {
+       u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)];
+} __packed;
+
+/*
+ * HTT tx MSDU descriptor
+ *
+ * The HTT tx MSDU descriptor is created by the host HTT SW for each
+ * tx MSDU.  The HTT tx MSDU descriptor contains the information that
+ * the target firmware needs for the FW's tx processing, particularly
+ * for creating the HW msdu descriptor.
+ * The same HTT tx descriptor is used for HL and LL systems, though
+ * a few fields within the tx descriptor are used only by LL or
+ * only by HL.
+ * The HTT tx descriptor is defined in two manners: by a struct with
+ * bitfields, and by a series of [dword offset, bit mask, bit shift]
+ * definitions.
+ * The target should use the struct def, for simplicitly and clarity,
+ * but the host shall use the bit-mast + bit-shift defs, to be endian-
+ * neutral.  Specifically, the host shall use the get/set macros built
+ * around the mask + shift defs.
+ */
+struct htt_data_tx_desc_frag {
+       __le32 paddr;
+       __le32 len;
+} __packed;
+
+enum htt_data_tx_desc_flags0 {
+       HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT = 1 << 0,
+       HTT_DATA_TX_DESC_FLAGS0_NO_AGGR         = 1 << 1,
+       HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT      = 1 << 2,
+       HTT_DATA_TX_DESC_FLAGS0_NO_CLASSIFY     = 1 << 3,
+       HTT_DATA_TX_DESC_FLAGS0_RSVD0           = 1 << 4
+#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_MASK 0xE0
+#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_LSB 5
+};
+
+enum htt_data_tx_desc_flags1 {
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_BITS 6
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_MASK 0x003F
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_LSB  0
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_BITS 5
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_MASK 0x07C0
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_LSB  6
+       HTT_DATA_TX_DESC_FLAGS1_POSTPONED        = 1 << 11,
+       HTT_DATA_TX_DESC_FLAGS1_MORE_IN_BATCH    = 1 << 12,
+       HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD = 1 << 13,
+       HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD = 1 << 14,
+       HTT_DATA_TX_DESC_FLAGS1_RSVD1            = 1 << 15
+};
+
+enum htt_data_tx_ext_tid {
+       HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST = 16,
+       HTT_DATA_TX_EXT_TID_MGMT                = 17,
+       HTT_DATA_TX_EXT_TID_INVALID             = 31
+};
+
+#define HTT_INVALID_PEERID 0xFFFF
+
+/*
+ * htt_data_tx_desc - used for data tx path
+ *
+ * Note: vdev_id irrelevant for pkt_type == raw and no_classify == 1.
+ *       ext_tid: for qos-data frames (0-15), see %HTT_DATA_TX_EXT_TID_
+ *                for special kinds of tids
+ *       postponed: only for HL hosts. indicates if this is a resend
+ *                  (HL hosts manage queues on the host )
+ *       more_in_batch: only for HL hosts. indicates if more packets are
+ *                      pending. this allows target to wait and aggregate
+ */
+struct htt_data_tx_desc {
+       u8 flags0; /* %HTT_DATA_TX_DESC_FLAGS0_ */
+       __le16 flags1; /* %HTT_DATA_TX_DESC_FLAGS1_ */
+       __le16 len;
+       __le16 id;
+       __le32 frags_paddr;
+       __le32 peerid;
+       u8 prefetch[0]; /* start of frame, for FW classification engine */
+} __packed;
+
+enum htt_rx_ring_flags {
+       HTT_RX_RING_FLAGS_MAC80211_HDR = 1 << 0,
+       HTT_RX_RING_FLAGS_MSDU_PAYLOAD = 1 << 1,
+       HTT_RX_RING_FLAGS_PPDU_START   = 1 << 2,
+       HTT_RX_RING_FLAGS_PPDU_END     = 1 << 3,
+       HTT_RX_RING_FLAGS_MPDU_START   = 1 << 4,
+       HTT_RX_RING_FLAGS_MPDU_END     = 1 << 5,
+       HTT_RX_RING_FLAGS_MSDU_START   = 1 << 6,
+       HTT_RX_RING_FLAGS_MSDU_END     = 1 << 7,
+       HTT_RX_RING_FLAGS_RX_ATTENTION = 1 << 8,
+       HTT_RX_RING_FLAGS_FRAG_INFO    = 1 << 9,
+       HTT_RX_RING_FLAGS_UNICAST_RX   = 1 << 10,
+       HTT_RX_RING_FLAGS_MULTICAST_RX = 1 << 11,
+       HTT_RX_RING_FLAGS_CTRL_RX      = 1 << 12,
+       HTT_RX_RING_FLAGS_MGMT_RX      = 1 << 13,
+       HTT_RX_RING_FLAGS_NULL_RX      = 1 << 14,
+       HTT_RX_RING_FLAGS_PHY_DATA_RX  = 1 << 15
+};
+
+struct htt_rx_ring_setup_ring {
+       __le32 fw_idx_shadow_reg_paddr;
+       __le32 rx_ring_base_paddr;
+       __le16 rx_ring_len; /* in 4-byte words */
+       __le16 rx_ring_bufsize; /* rx skb size - in bytes */
+       __le16 flags; /* %HTT_RX_RING_FLAGS_ */
+       __le16 fw_idx_init_val;
+
+       /* the following offsets are in 4-byte units */
+       __le16 mac80211_hdr_offset;
+       __le16 msdu_payload_offset;
+       __le16 ppdu_start_offset;
+       __le16 ppdu_end_offset;
+       __le16 mpdu_start_offset;
+       __le16 mpdu_end_offset;
+       __le16 msdu_start_offset;
+       __le16 msdu_end_offset;
+       __le16 rx_attention_offset;
+       __le16 frag_info_offset;
+} __packed;
+
+struct htt_rx_ring_setup_hdr {
+       u8 num_rings; /* supported values: 1, 2 */
+       __le16 rsvd0;
+} __packed;
+
+struct htt_rx_ring_setup {
+       struct htt_rx_ring_setup_hdr hdr;
+       struct htt_rx_ring_setup_ring rings[0];
+} __packed;
+
+/*
+ * htt_stats_req - request target to send specified statistics
+ *
+ * @msg_type: hardcoded %HTT_H2T_MSG_TYPE_STATS_REQ
+ * @upload_types: see %htt_dbg_stats_type. this is 24bit field actually
+ *     so make sure its little-endian.
+ * @reset_types: see %htt_dbg_stats_type. this is 24bit field actually
+ *     so make sure its little-endian.
+ * @cfg_val: stat_type specific configuration
+ * @stat_type: see %htt_dbg_stats_type
+ * @cookie_lsb: used for confirmation message from target->host
+ * @cookie_msb: ditto as %cookie
+ */
+struct htt_stats_req {
+       u8 upload_types[3];
+       u8 rsvd0;
+       u8 reset_types[3];
+       struct {
+               u8 mpdu_bytes;
+               u8 mpdu_num_msdus;
+               u8 msdu_bytes;
+       } __packed;
+       u8 stat_type;
+       __le32 cookie_lsb;
+       __le32 cookie_msb;
+} __packed;
+
+#define HTT_STATS_REQ_CFG_STAT_TYPE_INVALID 0xff
+
+/*
+ * htt_oob_sync_req - request out-of-band sync
+ *
+ * The HTT SYNC tells the target to suspend processing of subsequent
+ * HTT host-to-target messages until some other target agent locally
+ * informs the target HTT FW that the current sync counter is equal to
+ * or greater than (in a modulo sense) the sync counter specified in
+ * the SYNC message.
+ *
+ * This allows other host-target components to synchronize their operation
+ * with HTT, e.g. to ensure that tx frames don't get transmitted until a
+ * security key has been downloaded to and activated by the target.
+ * In the absence of any explicit synchronization counter value
+ * specification, the target HTT FW will use zero as the default current
+ * sync value.
+ *
+ * The HTT target FW will suspend its host->target message processing as long
+ * as 0 < (in-band sync counter - out-of-band sync counter) & 0xff < 128.
+ */
+struct htt_oob_sync_req {
+       u8 sync_count;
+       __le16 rsvd0;
+} __packed;
+
+#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_MASK 0x1F
+#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_LSB  0
+
+struct htt_aggr_conf {
+       u8 max_num_ampdu_subframes;
+       union {
+               /* dont use bitfields; undefined behaviour */
+               u8 flags; /* see %HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_ */
+               u8 max_num_amsdu_subframes:5;
+       } __packed;
+} __packed;
+
+#define HTT_MGMT_FRM_HDR_DOWNLOAD_LEN 32
+
+struct htt_mgmt_tx_desc {
+       u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)];
+       __le32 msdu_paddr;
+       __le32 desc_id;
+       __le32 len;
+       __le32 vdev_id;
+       u8 hdr[HTT_MGMT_FRM_HDR_DOWNLOAD_LEN];
+} __packed;
+
+enum htt_mgmt_tx_status {
+       HTT_MGMT_TX_STATUS_OK    = 0,
+       HTT_MGMT_TX_STATUS_RETRY = 1,
+       HTT_MGMT_TX_STATUS_DROP  = 2
+};
+
+/*=== target -> host messages ===============================================*/
+
+
+enum htt_t2h_msg_type {
+       HTT_T2H_MSG_TYPE_VERSION_CONF           = 0x0,
+       HTT_T2H_MSG_TYPE_RX_IND                 = 0x1,
+       HTT_T2H_MSG_TYPE_RX_FLUSH               = 0x2,
+       HTT_T2H_MSG_TYPE_PEER_MAP               = 0x3,
+       HTT_T2H_MSG_TYPE_PEER_UNMAP             = 0x4,
+       HTT_T2H_MSG_TYPE_RX_ADDBA               = 0x5,
+       HTT_T2H_MSG_TYPE_RX_DELBA               = 0x6,
+       HTT_T2H_MSG_TYPE_TX_COMPL_IND           = 0x7,
+       HTT_T2H_MSG_TYPE_PKTLOG                 = 0x8,
+       HTT_T2H_MSG_TYPE_STATS_CONF             = 0x9,
+       HTT_T2H_MSG_TYPE_RX_FRAG_IND            = 0xa,
+       HTT_T2H_MSG_TYPE_SEC_IND                = 0xb,
+       HTT_T2H_MSG_TYPE_RC_UPDATE_IND          = 0xc,
+       HTT_T2H_MSG_TYPE_TX_INSPECT_IND         = 0xd,
+       HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION     = 0xe,
+       HTT_T2H_MSG_TYPE_TEST,
+       /* keep this last */
+       HTT_T2H_NUM_MSGS
+};
+
+/*
+ * htt_resp_hdr - header for target-to-host messages
+ *
+ * msg_type: see htt_t2h_msg_type
+ */
+struct htt_resp_hdr {
+       u8 msg_type;
+} __packed;
+
+#define HTT_RESP_HDR_MSG_TYPE_OFFSET 0
+#define HTT_RESP_HDR_MSG_TYPE_MASK   0xff
+#define HTT_RESP_HDR_MSG_TYPE_LSB    0
+
+/* htt_ver_resp - response sent for htt_ver_req */
+struct htt_ver_resp {
+       u8 minor;
+       u8 major;
+       u8 rsvd0;
+} __packed;
+
+struct htt_mgmt_tx_completion {
+       u8 rsvd0;
+       u8 rsvd1;
+       u8 rsvd2;
+       __le32 desc_id;
+       __le32 status;
+} __packed;
+
+#define HTT_RX_INDICATION_INFO0_EXT_TID_MASK  (0x3F)
+#define HTT_RX_INDICATION_INFO0_EXT_TID_LSB   (0)
+#define HTT_RX_INDICATION_INFO0_FLUSH_VALID   (1 << 6)
+#define HTT_RX_INDICATION_INFO0_RELEASE_VALID (1 << 7)
+
+#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_MASK   0x0000003F
+#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_LSB    0
+#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_MASK     0x00000FC0
+#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_LSB      6
+#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_MASK 0x0003F000
+#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_LSB  12
+#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_MASK   0x00FC0000
+#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_LSB    18
+#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_MASK     0xFF000000
+#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_LSB      24
+
+struct htt_rx_indication_hdr {
+       u8 info0; /* %HTT_RX_INDICATION_INFO0_ */
+       __le16 peer_id;
+       __le32 info1; /* %HTT_RX_INDICATION_INFO1_ */
+} __packed;
+
+#define HTT_RX_INDICATION_INFO0_PHY_ERR_VALID    (1 << 0)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_MASK (0x1E)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_LSB  (1)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK  (1 << 5)
+#define HTT_RX_INDICATION_INFO0_END_VALID        (1 << 6)
+#define HTT_RX_INDICATION_INFO0_START_VALID      (1 << 7)
+
+#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_MASK    0x00FFFFFF
+#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_LSB     0
+#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_MASK 0xFF000000
+#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_LSB  24
+
+#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_MASK 0x00FFFFFF
+#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_LSB  0
+#define HTT_RX_INDICATION_INFO2_SERVICE_MASK    0xFF000000
+#define HTT_RX_INDICATION_INFO2_SERVICE_LSB     24
+
+enum htt_rx_legacy_rate {
+       HTT_RX_OFDM_48 = 0,
+       HTT_RX_OFDM_24 = 1,
+       HTT_RX_OFDM_12,
+       HTT_RX_OFDM_6,
+       HTT_RX_OFDM_54,
+       HTT_RX_OFDM_36,
+       HTT_RX_OFDM_18,
+       HTT_RX_OFDM_9,
+
+       /* long preamble */
+       HTT_RX_CCK_11_LP = 0,
+       HTT_RX_CCK_5_5_LP = 1,
+       HTT_RX_CCK_2_LP,
+       HTT_RX_CCK_1_LP,
+       /* short preamble */
+       HTT_RX_CCK_11_SP,
+       HTT_RX_CCK_5_5_SP,
+       HTT_RX_CCK_2_SP
+};
+
+enum htt_rx_legacy_rate_type {
+       HTT_RX_LEGACY_RATE_OFDM = 0,
+       HTT_RX_LEGACY_RATE_CCK
+};
+
+enum htt_rx_preamble_type {
+       HTT_RX_LEGACY        = 0x4,
+       HTT_RX_HT            = 0x8,
+       HTT_RX_HT_WITH_TXBF  = 0x9,
+       HTT_RX_VHT           = 0xC,
+       HTT_RX_VHT_WITH_TXBF = 0xD,
+};
+
+/*
+ * Fields: phy_err_valid, phy_err_code, tsf,
+ * usec_timestamp, sub_usec_timestamp
+ * ..are valid only if end_valid == 1.
+ *
+ * Fields: rssi_chains, legacy_rate_type,
+ * legacy_rate_cck, preamble_type, service,
+ * vht_sig_*
+ * ..are valid only if start_valid == 1;
+ */
+struct htt_rx_indication_ppdu {
+       u8 combined_rssi;
+       u8 sub_usec_timestamp;
+       u8 phy_err_code;
+       u8 info0; /* HTT_RX_INDICATION_INFO0_ */
+       struct {
+               u8 pri20_db;
+               u8 ext20_db;
+               u8 ext40_db;
+               u8 ext80_db;
+       } __packed rssi_chains[4];
+       __le32 tsf;
+       __le32 usec_timestamp;
+       __le32 info1; /* HTT_RX_INDICATION_INFO1_ */
+       __le32 info2; /* HTT_RX_INDICATION_INFO2_ */
+} __packed;
+
+enum htt_rx_mpdu_status {
+       HTT_RX_IND_MPDU_STATUS_UNKNOWN = 0x0,
+       HTT_RX_IND_MPDU_STATUS_OK,
+       HTT_RX_IND_MPDU_STATUS_ERR_FCS,
+       HTT_RX_IND_MPDU_STATUS_ERR_DUP,
+       HTT_RX_IND_MPDU_STATUS_ERR_REPLAY,
+       HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER,
+       /* only accept EAPOL frames */
+       HTT_RX_IND_MPDU_STATUS_UNAUTH_PEER,
+       HTT_RX_IND_MPDU_STATUS_OUT_OF_SYNC,
+       /* Non-data in promiscous mode */
+       HTT_RX_IND_MPDU_STATUS_MGMT_CTRL,
+       HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR,
+       HTT_RX_IND_MPDU_STATUS_DECRYPT_ERR,
+       HTT_RX_IND_MPDU_STATUS_MPDU_LENGTH_ERR,
+       HTT_RX_IND_MPDU_STATUS_ENCRYPT_REQUIRED_ERR,
+       HTT_RX_IND_MPDU_STATUS_PRIVACY_ERR,
+
+       /*
+        * MISC: discard for unspecified reasons.
+        * Leave this enum value last.
+        */
+       HTT_RX_IND_MPDU_STATUS_ERR_MISC = 0xFF
+};
+
+struct htt_rx_indication_mpdu_range {
+       u8 mpdu_count;
+       u8 mpdu_range_status; /* %htt_rx_mpdu_status */
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct htt_rx_indication_prefix {
+       __le16 fw_rx_desc_bytes;
+       u8 pad0;
+       u8 pad1;
+};
+
+struct htt_rx_indication {
+       struct htt_rx_indication_hdr hdr;
+       struct htt_rx_indication_ppdu ppdu;
+       struct htt_rx_indication_prefix prefix;
+
+       /*
+        * the following fields are both dynamically sized, so
+        * take care addressing them
+        */
+
+       /* the size of this is %fw_rx_desc_bytes */
+       struct fw_rx_desc_base fw_desc;
+
+       /*
+        * %mpdu_ranges starts after &%prefix + roundup(%fw_rx_desc_bytes, 4)
+        * and has %num_mpdu_ranges elements.
+        */
+       struct htt_rx_indication_mpdu_range mpdu_ranges[0];
+} __packed;
+
+static inline struct htt_rx_indication_mpdu_range *
+               htt_rx_ind_get_mpdu_ranges(struct htt_rx_indication *rx_ind)
+{
+       void *ptr = rx_ind;
+
+       ptr += sizeof(rx_ind->hdr)
+            + sizeof(rx_ind->ppdu)
+            + sizeof(rx_ind->prefix)
+            + roundup(__le16_to_cpu(rx_ind->prefix.fw_rx_desc_bytes), 4);
+       return ptr;
+}
+
+enum htt_rx_flush_mpdu_status {
+       HTT_RX_FLUSH_MPDU_DISCARD = 0,
+       HTT_RX_FLUSH_MPDU_REORDER = 1,
+};
+
+/*
+ * htt_rx_flush - discard or reorder given range of mpdus
+ *
+ * Note: host must check if all sequence numbers between
+ *     [seq_num_start, seq_num_end-1] are valid.
+ */
+struct htt_rx_flush {
+       __le16 peer_id;
+       u8 tid;
+       u8 rsvd0;
+       u8 mpdu_status; /* %htt_rx_flush_mpdu_status */
+       u8 seq_num_start; /* it is 6 LSBs of 802.11 seq no */
+       u8 seq_num_end; /* it is 6 LSBs of 802.11 seq no */
+};
+
+struct htt_rx_peer_map {
+       u8 vdev_id;
+       __le16 peer_id;
+       u8 addr[6];
+       u8 rsvd0;
+       u8 rsvd1;
+} __packed;
+
+struct htt_rx_peer_unmap {
+       u8 rsvd0;
+       __le16 peer_id;
+} __packed;
+
+enum htt_security_types {
+       HTT_SECURITY_NONE,
+       HTT_SECURITY_WEP128,
+       HTT_SECURITY_WEP104,
+       HTT_SECURITY_WEP40,
+       HTT_SECURITY_TKIP,
+       HTT_SECURITY_TKIP_NOMIC,
+       HTT_SECURITY_AES_CCMP,
+       HTT_SECURITY_WAPI,
+
+       HTT_NUM_SECURITY_TYPES /* keep this last! */
+};
+
+enum htt_security_flags {
+#define HTT_SECURITY_TYPE_MASK 0x7F
+#define HTT_SECURITY_TYPE_LSB  0
+       HTT_SECURITY_IS_UNICAST = 1 << 7
+};
+
+struct htt_security_indication {
+       union {
+               /* dont use bitfields; undefined behaviour */
+               u8 flags; /* %htt_security_flags */
+               struct {
+                       u8 security_type:7, /* %htt_security_types */
+                          is_unicast:1;
+               } __packed;
+       } __packed;
+       __le16 peer_id;
+       u8 michael_key[8];
+       u8 wapi_rsc[16];
+} __packed;
+
+#define HTT_RX_BA_INFO0_TID_MASK     0x000F
+#define HTT_RX_BA_INFO0_TID_LSB      0
+#define HTT_RX_BA_INFO0_PEER_ID_MASK 0xFFF0
+#define HTT_RX_BA_INFO0_PEER_ID_LSB  4
+
+struct htt_rx_addba {
+       u8 window_size;
+       __le16 info0; /* %HTT_RX_BA_INFO0_ */
+} __packed;
+
+struct htt_rx_delba {
+       u8 rsvd0;
+       __le16 info0; /* %HTT_RX_BA_INFO0_ */
+} __packed;
+
+enum htt_data_tx_status {
+       HTT_DATA_TX_STATUS_OK            = 0,
+       HTT_DATA_TX_STATUS_DISCARD       = 1,
+       HTT_DATA_TX_STATUS_NO_ACK        = 2,
+       HTT_DATA_TX_STATUS_POSTPONE      = 3, /* HL only */
+       HTT_DATA_TX_STATUS_DOWNLOAD_FAIL = 128
+};
+
+enum htt_data_tx_flags {
+#define HTT_DATA_TX_STATUS_MASK 0x07
+#define HTT_DATA_TX_STATUS_LSB  0
+#define HTT_DATA_TX_TID_MASK    0x78
+#define HTT_DATA_TX_TID_LSB     3
+       HTT_DATA_TX_TID_INVALID = 1 << 7
+};
+
+#define HTT_TX_COMPL_INV_MSDU_ID 0xFFFF
+
+struct htt_data_tx_completion {
+       union {
+               u8 flags;
+               struct {
+                       u8 status:3,
+                          tid:4,
+                          tid_invalid:1;
+               } __packed;
+       } __packed;
+       u8 num_msdus;
+       u8 rsvd0;
+       __le16 msdus[0]; /* variable length based on %num_msdus */
+} __packed;
+
+struct htt_tx_compl_ind_base {
+       u32 hdr;
+       u16 payload[1/*or more*/];
+} __packed;
+
+struct htt_rc_tx_done_params {
+       u32 rate_code;
+       u32 rate_code_flags;
+       u32 flags;
+       u32 num_enqued; /* 1 for non-AMPDU */
+       u32 num_retries;
+       u32 num_failed; /* for AMPDU */
+       u32 ack_rssi;
+       u32 time_stamp;
+       u32 is_probe;
+};
+
+struct htt_rc_update {
+       u8 vdev_id;
+       __le16 peer_id;
+       u8 addr[6];
+       u8 num_elems;
+       u8 rsvd0;
+       struct htt_rc_tx_done_params params[0]; /* variable length %num_elems */
+} __packed;
+
+/* see htt_rx_indication for similar fields and descriptions */
+struct htt_rx_fragment_indication {
+       union {
+               u8 info0; /* %HTT_RX_FRAG_IND_INFO0_ */
+               struct {
+                       u8 ext_tid:5,
+                          flush_valid:1;
+               } __packed;
+       } __packed;
+       __le16 peer_id;
+       __le32 info1; /* %HTT_RX_FRAG_IND_INFO1_ */
+       __le16 fw_rx_desc_bytes;
+       __le16 rsvd0;
+
+       u8 fw_msdu_rx_desc[0];
+} __packed;
+
+#define HTT_RX_FRAG_IND_INFO0_EXT_TID_MASK     0x1F
+#define HTT_RX_FRAG_IND_INFO0_EXT_TID_LSB      0
+#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_MASK 0x20
+#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_LSB  5
+
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_MASK 0x0000003F
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_LSB  0
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_MASK   0x00000FC0
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_LSB    6
+
+/*
+ * target -> host test message definition
+ *
+ * The following field definitions describe the format of the test
+ * message sent from the target to the host.
+ * The message consists of a 4-octet header, followed by a variable
+ * number of 32-bit integer values, followed by a variable number
+ * of 8-bit character values.
+ *
+ * |31                         16|15           8|7            0|
+ * |-----------------------------------------------------------|
+ * |          num chars          |   num ints   |   msg type   |
+ * |-----------------------------------------------------------|
+ * |                           int 0                           |
+ * |-----------------------------------------------------------|
+ * |                           int 1                           |
+ * |-----------------------------------------------------------|
+ * |                            ...                            |
+ * |-----------------------------------------------------------|
+ * |    char 3    |    char 2    |    char 1    |    char 0    |
+ * |-----------------------------------------------------------|
+ * |              |              |      ...     |    char 4    |
+ * |-----------------------------------------------------------|
+ *   - MSG_TYPE
+ *     Bits 7:0
+ *     Purpose: identifies this as a test message
+ *     Value: HTT_MSG_TYPE_TEST
+ *   - NUM_INTS
+ *     Bits 15:8
+ *     Purpose: indicate how many 32-bit integers follow the message header
+ *   - NUM_CHARS
+ *     Bits 31:16
+ *     Purpose: indicate how many 8-bit charaters follow the series of integers
+ */
+struct htt_rx_test {
+       u8 num_ints;
+       __le16 num_chars;
+
+       /* payload consists of 2 lists:
+        *  a) num_ints * sizeof(__le32)
+        *  b) num_chars * sizeof(u8) aligned to 4bytes */
+       u8 payload[0];
+} __packed;
+
+static inline __le32 *htt_rx_test_get_ints(struct htt_rx_test *rx_test)
+{
+       return (__le32 *)rx_test->payload;
+}
+
+static inline u8 *htt_rx_test_get_chars(struct htt_rx_test *rx_test)
+{
+       return rx_test->payload + (rx_test->num_ints * sizeof(__le32));
+}
+
+/*
+ * target -> host packet log message
+ *
+ * The following field definitions describe the format of the packet log
+ * message sent from the target to the host.
+ * The message consists of a 4-octet header,followed by a variable number
+ * of 32-bit character values.
+ *
+ * |31          24|23          16|15           8|7            0|
+ * |-----------------------------------------------------------|
+ * |              |              |              |   msg type   |
+ * |-----------------------------------------------------------|
+ * |                        payload                            |
+ * |-----------------------------------------------------------|
+ *   - MSG_TYPE
+ *     Bits 7:0
+ *     Purpose: identifies this as a test message
+ *     Value: HTT_MSG_TYPE_PACKETLOG
+ */
+struct htt_pktlog_msg {
+       u8 pad[3];
+       __le32 payload[1 /* or more */];
+} __packed;
+
+struct htt_dbg_stats_rx_reorder_stats {
+       /* Non QoS MPDUs received */
+       __le32 deliver_non_qos;
+
+       /* MPDUs received in-order */
+       __le32 deliver_in_order;
+
+       /* Flush due to reorder timer expired */
+       __le32 deliver_flush_timeout;
+
+       /* Flush due to move out of window */
+       __le32 deliver_flush_oow;
+
+       /* Flush due to DELBA */
+       __le32 deliver_flush_delba;
+
+       /* MPDUs dropped due to FCS error */
+       __le32 fcs_error;
+
+       /* MPDUs dropped due to monitor mode non-data packet */
+       __le32 mgmt_ctrl;
+
+       /* MPDUs dropped due to invalid peer */
+       __le32 invalid_peer;
+
+       /* MPDUs dropped due to duplication (non aggregation) */
+       __le32 dup_non_aggr;
+
+       /* MPDUs dropped due to processed before */
+       __le32 dup_past;
+
+       /* MPDUs dropped due to duplicate in reorder queue */
+       __le32 dup_in_reorder;
+
+       /* Reorder timeout happened */
+       __le32 reorder_timeout;
+
+       /* invalid bar ssn */
+       __le32 invalid_bar_ssn;
+
+       /* reorder reset due to bar ssn */
+       __le32 ssn_reset;
+};
+
+struct htt_dbg_stats_wal_tx_stats {
+       /* Num HTT cookies queued to dispatch list */
+       __le32 comp_queued;
+
+       /* Num HTT cookies dispatched */
+       __le32 comp_delivered;
+
+       /* Num MSDU queued to WAL */
+       __le32 msdu_enqued;
+
+       /* Num MPDU queue to WAL */
+       __le32 mpdu_enqued;
+
+       /* Num MSDUs dropped by WMM limit */
+       __le32 wmm_drop;
+
+       /* Num Local frames queued */
+       __le32 local_enqued;
+
+       /* Num Local frames done */
+       __le32 local_freed;
+
+       /* Num queued to HW */
+       __le32 hw_queued;
+
+       /* Num PPDU reaped from HW */
+       __le32 hw_reaped;
+
+       /* Num underruns */
+       __le32 underrun;
+
+       /* Num PPDUs cleaned up in TX abort */
+       __le32 tx_abort;
+
+       /* Num MPDUs requed by SW */
+       __le32 mpdus_requed;
+
+       /* excessive retries */
+       __le32 tx_ko;
+
+       /* data hw rate code */
+       __le32 data_rc;
+
+       /* Scheduler self triggers */
+       __le32 self_triggers;
+
+       /* frames dropped due to excessive sw retries */
+       __le32 sw_retry_failure;
+
+       /* illegal rate phy errors  */
+       __le32 illgl_rate_phy_err;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_cont_xretry;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_tx_timeout;
+
+       /* wal pdev resets  */
+       __le32 pdev_resets;
+
+       __le32 phy_underrun;
+
+       /* MPDU is more than txop limit */
+       __le32 txop_ovf;
+} __packed;
+
+struct htt_dbg_stats_wal_rx_stats {
+       /* Cnts any change in ring routing mid-ppdu */
+       __le32 mid_ppdu_route_change;
+
+       /* Total number of statuses processed */
+       __le32 status_rcvd;
+
+       /* Extra frags on rings 0-3 */
+       __le32 r0_frags;
+       __le32 r1_frags;
+       __le32 r2_frags;
+       __le32 r3_frags;
+
+       /* MSDUs / MPDUs delivered to HTT */
+       __le32 htt_msdus;
+       __le32 htt_mpdus;
+
+       /* MSDUs / MPDUs delivered to local stack */
+       __le32 loc_msdus;
+       __le32 loc_mpdus;
+
+       /* AMSDUs that have more MSDUs than the status ring size */
+       __le32 oversize_amsdu;
+
+       /* Number of PHY errors */
+       __le32 phy_errs;
+
+       /* Number of PHY errors drops */
+       __le32 phy_err_drop;
+
+       /* Number of mpdu errors - FCS, MIC, ENC etc. */
+       __le32 mpdu_errs;
+} __packed;
+
+struct htt_dbg_stats_wal_peer_stats {
+       __le32 dummy; /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */
+} __packed;
+
+struct htt_dbg_stats_wal_pdev_txrx {
+       struct htt_dbg_stats_wal_tx_stats tx_stats;
+       struct htt_dbg_stats_wal_rx_stats rx_stats;
+       struct htt_dbg_stats_wal_peer_stats peer_stats;
+} __packed;
+
+struct htt_dbg_stats_rx_rate_info {
+       __le32 mcs[10];
+       __le32 sgi[10];
+       __le32 nss[4];
+       __le32 stbc[10];
+       __le32 bw[3];
+       __le32 pream[6];
+       __le32 ldpc;
+       __le32 txbf;
+};
+
+/*
+ * htt_dbg_stats_status -
+ * present -     The requested stats have been delivered in full.
+ *               This indicates that either the stats information was contained
+ *               in its entirety within this message, or else this message
+ *               completes the delivery of the requested stats info that was
+ *               partially delivered through earlier STATS_CONF messages.
+ * partial -     The requested stats have been delivered in part.
+ *               One or more subsequent STATS_CONF messages with the same
+ *               cookie value will be sent to deliver the remainder of the
+ *               information.
+ * error -       The requested stats could not be delivered, for example due
+ *               to a shortage of memory to construct a message holding the
+ *               requested stats.
+ * invalid -     The requested stat type is either not recognized, or the
+ *               target is configured to not gather the stats type in question.
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * series_done - This special value indicates that no further stats info
+ *               elements are present within a series of stats info elems
+ *               (within a stats upload confirmation message).
+ */
+enum htt_dbg_stats_status {
+       HTT_DBG_STATS_STATUS_PRESENT     = 0,
+       HTT_DBG_STATS_STATUS_PARTIAL     = 1,
+       HTT_DBG_STATS_STATUS_ERROR       = 2,
+       HTT_DBG_STATS_STATUS_INVALID     = 3,
+       HTT_DBG_STATS_STATUS_SERIES_DONE = 7
+};
+
+/*
+ * target -> host statistics upload
+ *
+ * The following field definitions describe the format of the HTT target
+ * to host stats upload confirmation message.
+ * The message contains a cookie echoed from the HTT host->target stats
+ * upload request, which identifies which request the confirmation is
+ * for, and a series of tag-length-value stats information elements.
+ * The tag-length header for each stats info element also includes a
+ * status field, to indicate whether the request for the stat type in
+ * question was fully met, partially met, unable to be met, or invalid
+ * (if the stat type in question is disabled in the target).
+ * A special value of all 1's in this status field is used to indicate
+ * the end of the series of stats info elements.
+ *
+ *
+ * |31                         16|15           8|7   5|4       0|
+ * |------------------------------------------------------------|
+ * |                  reserved                  |    msg type   |
+ * |------------------------------------------------------------|
+ * |                        cookie LSBs                         |
+ * |------------------------------------------------------------|
+ * |                        cookie MSBs                         |
+ * |------------------------------------------------------------|
+ * |      stats entry length     |   reserved   |  S  |stat type|
+ * |------------------------------------------------------------|
+ * |                                                            |
+ * |                  type-specific stats info                  |
+ * |                                                            |
+ * |------------------------------------------------------------|
+ * |      stats entry length     |   reserved   |  S  |stat type|
+ * |------------------------------------------------------------|
+ * |                                                            |
+ * |                  type-specific stats info                  |
+ * |                                                            |
+ * |------------------------------------------------------------|
+ * |              n/a            |   reserved   | 111 |   n/a   |
+ * |------------------------------------------------------------|
+ * Header fields:
+ *  - MSG_TYPE
+ *    Bits 7:0
+ *    Purpose: identifies this is a statistics upload confirmation message
+ *    Value: 0x9
+ *  - COOKIE_LSBS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to match a target->host stats confirmation
+ *        message with its preceding host->target stats request message.
+ *    Value: LSBs of the opaque cookie specified by the host-side requestor
+ *  - COOKIE_MSBS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to match a target->host stats confirmation
+ *        message with its preceding host->target stats request message.
+ *    Value: MSBs of the opaque cookie specified by the host-side requestor
+ *
+ * Stats Information Element tag-length header fields:
+ *  - STAT_TYPE
+ *    Bits 4:0
+ *    Purpose: identifies the type of statistics info held in the
+ *        following information element
+ *    Value: htt_dbg_stats_type
+ *  - STATUS
+ *    Bits 7:5
+ *    Purpose: indicate whether the requested stats are present
+ *    Value: htt_dbg_stats_status, including a special value (0x7) to mark
+ *        the completion of the stats entry series
+ *  - LENGTH
+ *    Bits 31:16
+ *    Purpose: indicate the stats information size
+ *    Value: This field specifies the number of bytes of stats information
+ *       that follows the element tag-length header.
+ *       It is expected but not required that this length is a multiple of
+ *       4 bytes.  Even if the length is not an integer multiple of 4, the
+ *       subsequent stats entry header will begin on a 4-byte aligned
+ *       boundary.
+ */
+
+#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_MASK 0x1F
+#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_LSB  0
+#define HTT_STATS_CONF_ITEM_INFO_STATUS_MASK    0xE0
+#define HTT_STATS_CONF_ITEM_INFO_STATUS_LSB     5
+
+struct htt_stats_conf_item {
+       union {
+               u8 info;
+               struct {
+                       u8 stat_type:5; /* %HTT_DBG_STATS_ */
+                       u8 status:3; /* %HTT_DBG_STATS_STATUS_ */
+               } __packed;
+       } __packed;
+       u8 pad;
+       __le16 length;
+       u8 payload[0]; /* roundup(length, 4) long */
+} __packed;
+
+struct htt_stats_conf {
+       u8 pad[3];
+       __le32 cookie_lsb;
+       __le32 cookie_msb;
+
+       /* each item has variable length! */
+       struct htt_stats_conf_item items[0];
+} __packed;
+
+static inline struct htt_stats_conf_item *htt_stats_conf_next_item(
+                                       const struct htt_stats_conf_item *item)
+{
+       return (void *)item + sizeof(*item) + roundup(item->length, 4);
+}
+/*
+ * host -> target FRAG DESCRIPTOR/MSDU_EXT DESC bank
+ *
+ * The following field definitions describe the format of the HTT host
+ * to target frag_desc/msdu_ext bank configuration message.
+ * The message contains the based address and the min and max id of the
+ * MSDU_EXT/FRAG_DESC that will be used by the HTT to map MSDU DESC and
+ * MSDU_EXT/FRAG_DESC.
+ * HTT will use id in HTT descriptor instead sending the frag_desc_ptr.
+ * For QCA988X HW the firmware will use fragment_desc_ptr but in WIFI2.0
+ * the hardware does the mapping/translation.
+ *
+ * Total banks that can be configured is configured to 16.
+ *
+ * This should be called before any TX has be initiated by the HTT
+ *
+ * |31                         16|15           8|7   5|4       0|
+ * |------------------------------------------------------------|
+ * | DESC_SIZE    |  NUM_BANKS   | RES |SWP|pdev|    msg type   |
+ * |------------------------------------------------------------|
+ * |                     BANK0_BASE_ADDRESS                     |
+ * |------------------------------------------------------------|
+ * |                            ...                             |
+ * |------------------------------------------------------------|
+ * |                    BANK15_BASE_ADDRESS                     |
+ * |------------------------------------------------------------|
+ * |       BANK0_MAX_ID          |       BANK0_MIN_ID           |
+ * |------------------------------------------------------------|
+ * |                            ...                             |
+ * |------------------------------------------------------------|
+ * |       BANK15_MAX_ID         |       BANK15_MIN_ID          |
+ * |------------------------------------------------------------|
+ * Header fields:
+ *  - MSG_TYPE
+ *    Bits 7:0
+ *    Value: 0x6
+ *  - BANKx_BASE_ADDRESS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to specify the base address of the MSDU_EXT
+ *         bank physical/bus address.
+ *  - BANKx_MIN_ID
+ *    Bits 15:0
+ *    Purpose: Provide a mechanism to specify the min index that needs to
+ *          mapped.
+ *  - BANKx_MAX_ID
+ *    Bits 31:16
+ *    Purpose: Provide a mechanism to specify the max index that needs to
+ *
+ */
+struct htt_frag_desc_bank_id {
+       __le16 bank_min_id;
+       __le16 bank_max_id;
+} __packed;
+
+/* real is 16 but it wouldn't fit in the max htt message size
+ * so we use a conservatively safe value for now */
+#define HTT_FRAG_DESC_BANK_MAX 4
+
+#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_MASK 0x03
+#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_LSB  0
+#define HTT_FRAG_DESC_BANK_CFG_INFO_SWAP         (1 << 2)
+
+struct htt_frag_desc_bank_cfg {
+       u8 info; /* HTT_FRAG_DESC_BANK_CFG_INFO_ */
+       u8 num_banks;
+       u8 desc_size;
+       __le32 bank_base_addrs[HTT_FRAG_DESC_BANK_MAX];
+       struct htt_frag_desc_bank_id bank_id[HTT_FRAG_DESC_BANK_MAX];
+} __packed;
+
+union htt_rx_pn_t {
+       /* WEP: 24-bit PN */
+       u32 pn24;
+
+       /* TKIP or CCMP: 48-bit PN */
+       u_int64_t pn48;
+
+       /* WAPI: 128-bit PN */
+       u_int64_t pn128[2];
+};
+
+struct htt_cmd {
+       struct htt_cmd_hdr hdr;
+       union {
+               struct htt_ver_req ver_req;
+               struct htt_mgmt_tx_desc mgmt_tx;
+               struct htt_data_tx_desc data_tx;
+               struct htt_rx_ring_setup rx_setup;
+               struct htt_stats_req stats_req;
+               struct htt_oob_sync_req oob_sync_req;
+               struct htt_aggr_conf aggr_conf;
+               struct htt_frag_desc_bank_cfg frag_desc_bank_cfg;
+       };
+} __packed;
+
+struct htt_resp {
+       struct htt_resp_hdr hdr;
+       union {
+               struct htt_ver_resp ver_resp;
+               struct htt_mgmt_tx_completion mgmt_tx_completion;
+               struct htt_data_tx_completion data_tx_completion;
+               struct htt_rx_indication rx_ind;
+               struct htt_rx_fragment_indication rx_frag_ind;
+               struct htt_rx_peer_map peer_map;
+               struct htt_rx_peer_unmap peer_unmap;
+               struct htt_rx_flush rx_flush;
+               struct htt_rx_addba rx_addba;
+               struct htt_rx_delba rx_delba;
+               struct htt_security_indication security_indication;
+               struct htt_rc_update rc_update;
+               struct htt_rx_test rx_test;
+               struct htt_pktlog_msg pktlog_msg;
+               struct htt_stats_conf stats_conf;
+       };
+} __packed;
+
+
+/*** host side structures follow ***/
+
+struct htt_tx_done {
+       u32 msdu_id;
+       bool discard;
+       bool no_ack;
+};
+
+struct htt_peer_map_event {
+       u8 vdev_id;
+       u16 peer_id;
+       u8 addr[ETH_ALEN];
+};
+
+struct htt_peer_unmap_event {
+       u16 peer_id;
+};
+
+struct htt_rx_info {
+       struct sk_buff *skb;
+       enum htt_rx_mpdu_status status;
+       enum htt_rx_mpdu_encrypt_type encrypt_type;
+       s8 signal;
+       struct {
+               u8 info0;
+               u32 info1;
+               u32 info2;
+       } rate;
+       bool fcs_err;
+};
+
+struct ath10k_htt {
+       struct ath10k *ar;
+       enum ath10k_htc_ep_id eid;
+
+       int max_throughput_mbps;
+       u8 target_version_major;
+       u8 target_version_minor;
+       struct completion target_version_received;
+
+       struct {
+               /*
+                * Ring of network buffer objects - This ring is
+                * used exclusively by the host SW. This ring
+                * mirrors the dev_addrs_ring that is shared
+                * between the host SW and the MAC HW. The host SW
+                * uses this netbufs ring to locate the network
+                * buffer objects whose data buffers the HW has
+                * filled.
+                */
+               struct sk_buff **netbufs_ring;
+               /*
+                * Ring of buffer addresses -
+                * This ring holds the "physical" device address of the
+                * rx buffers the host SW provides for the MAC HW to
+                * fill.
+                */
+               __le32 *paddrs_ring;
+
+               /*
+                * Base address of ring, as a "physical" device address
+                * rather than a CPU address.
+                */
+               dma_addr_t base_paddr;
+
+               /* how many elems in the ring (power of 2) */
+               int size;
+
+               /* size - 1 */
+               unsigned size_mask;
+
+               /* how many rx buffers to keep in the ring */
+               int fill_level;
+
+               /* how many rx buffers (full+empty) are in the ring */
+               int fill_cnt;
+
+               /*
+                * alloc_idx - where HTT SW has deposited empty buffers
+                * This is allocated in consistent mem, so that the FW can
+                * read this variable, and program the HW's FW_IDX reg with
+                * the value of this shadow register.
+                */
+               struct {
+                       __le32 *vaddr;
+                       dma_addr_t paddr;
+               } alloc_idx;
+
+               /* where HTT SW has processed bufs filled by rx MAC DMA */
+               struct {
+                       unsigned msdu_payld;
+               } sw_rd_idx;
+
+               /*
+                * refill_retry_timer - timer triggered when the ring is
+                * not refilled to the level expected
+                */
+               struct timer_list refill_retry_timer;
+
+               /* Protects access to all rx ring buffer state variables */
+               spinlock_t lock;
+       } rx_ring;
+
+       unsigned int prefetch_len;
+
+       /* Protects access to %pending_tx, %used_msdu_ids */
+       spinlock_t tx_lock;
+       int max_num_pending_tx;
+       int num_pending_tx;
+       struct sk_buff **pending_tx;
+       unsigned long *used_msdu_ids; /* bitmap */
+       wait_queue_head_t empty_tx_wq;
+
+       /* set if host-fw communication goes haywire
+        * used to avoid further failures */
+       bool rx_confused;
+};
+
+#define RX_HTT_HDR_STATUS_LEN 64
+
+/* This structure layout is programmed via rx ring setup
+ * so that FW knows how to transfer the rx descriptor to the host.
+ * Buffers like this are placed on the rx ring. */
+struct htt_rx_desc {
+       union {
+               /* This field is filled on the host using the msdu buffer
+                * from htt_rx_indication */
+               struct fw_rx_desc_base fw_desc;
+               u32 pad;
+       } __packed;
+       struct {
+               struct rx_attention attention;
+               struct rx_frag_info frag_info;
+               struct rx_mpdu_start mpdu_start;
+               struct rx_msdu_start msdu_start;
+               struct rx_msdu_end msdu_end;
+               struct rx_mpdu_end mpdu_end;
+               struct rx_ppdu_start ppdu_start;
+               struct rx_ppdu_end ppdu_end;
+       } __packed;
+       u8 rx_hdr_status[RX_HTT_HDR_STATUS_LEN];
+       u8 msdu_payload[0];
+};
+
+#define HTT_RX_DESC_ALIGN 8
+
+#define HTT_MAC_ADDR_LEN 6
+
+/*
+ * FIX THIS
+ * Should be: sizeof(struct htt_host_rx_desc) + max rx MSDU size,
+ * rounded up to a cache line size.
+ */
+#define HTT_RX_BUF_SIZE 1920
+#define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc))
+
+/*
+ * DMA_MAP expects the buffer to be an integral number of cache lines.
+ * Rather than checking the actual cache line size, this code makes a
+ * conservative estimate of what the cache line size could be.
+ */
+#define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */
+#define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1)
+
+struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar);
+int ath10k_htt_attach_target(struct ath10k_htt *htt);
+void ath10k_htt_detach(struct ath10k_htt *htt);
+
+int ath10k_htt_tx_attach(struct ath10k_htt *htt);
+void ath10k_htt_tx_detach(struct ath10k_htt *htt);
+int ath10k_htt_rx_attach(struct ath10k_htt *htt);
+void ath10k_htt_rx_detach(struct ath10k_htt *htt);
+void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
+int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
+
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
+int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt);
+void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
+int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
+int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *);
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
new file mode 100644 (file)
index 0000000..de058d7
--- /dev/null
@@ -0,0 +1,1167 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "htc.h"
+#include "htt.h"
+#include "txrx.h"
+#include "debug.h"
+
+#include <linux/log2.h>
+
+/* slightly larger than one large A-MPDU */
+#define HTT_RX_RING_SIZE_MIN 128
+
+/* roughly 20 ms @ 1 Gbps of 1500B MSDUs */
+#define HTT_RX_RING_SIZE_MAX 2048
+
+#define HTT_RX_AVG_FRM_BYTES 1000
+
+/* ms, very conservative */
+#define HTT_RX_HOST_LATENCY_MAX_MS 20
+
+/* ms, conservative */
+#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 10
+
+/* when under memory pressure rx ring refill may fail and needs a retry */
+#define HTT_RX_RING_REFILL_RETRY_MS 50
+
+static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
+{
+       int size;
+
+       /*
+        * It is expected that the host CPU will typically be able to
+        * service the rx indication from one A-MPDU before the rx
+        * indication from the subsequent A-MPDU happens, roughly 1-2 ms
+        * later. However, the rx ring should be sized very conservatively,
+        * to accomodate the worst reasonable delay before the host CPU
+        * services a rx indication interrupt.
+        *
+        * The rx ring need not be kept full of empty buffers. In theory,
+        * the htt host SW can dynamically track the low-water mark in the
+        * rx ring, and dynamically adjust the level to which the rx ring
+        * is filled with empty buffers, to dynamically meet the desired
+        * low-water mark.
+        *
+        * In contrast, it's difficult to resize the rx ring itself, once
+        * it's in use. Thus, the ring itself should be sized very
+        * conservatively, while the degree to which the ring is filled
+        * with empty buffers should be sized moderately conservatively.
+        */
+
+       /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
+       size =
+           htt->max_throughput_mbps +
+           1000  /
+           (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_MAX_MS;
+
+       if (size < HTT_RX_RING_SIZE_MIN)
+               size = HTT_RX_RING_SIZE_MIN;
+
+       if (size > HTT_RX_RING_SIZE_MAX)
+               size = HTT_RX_RING_SIZE_MAX;
+
+       size = roundup_pow_of_two(size);
+
+       return size;
+}
+
+static int ath10k_htt_rx_ring_fill_level(struct ath10k_htt *htt)
+{
+       int size;
+
+       /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
+       size =
+           htt->max_throughput_mbps *
+           1000  /
+           (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_WORST_LIKELY_MS;
+
+       /*
+        * Make sure the fill level is at least 1 less than the ring size.
+        * Leaving 1 element empty allows the SW to easily distinguish
+        * between a full ring vs. an empty ring.
+        */
+       if (size >= htt->rx_ring.size)
+               size = htt->rx_ring.size - 1;
+
+       return size;
+}
+
+static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *cb;
+       int i;
+
+       for (i = 0; i < htt->rx_ring.fill_cnt; i++) {
+               skb = htt->rx_ring.netbufs_ring[i];
+               cb = ATH10K_SKB_CB(skb);
+               dma_unmap_single(htt->ar->dev, cb->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(skb);
+       }
+
+       htt->rx_ring.fill_cnt = 0;
+}
+
+static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
+{
+       struct htt_rx_desc *rx_desc;
+       struct sk_buff *skb;
+       dma_addr_t paddr;
+       int ret = 0, idx;
+
+       idx = __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr));
+       while (num > 0) {
+               skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN);
+               if (!skb) {
+                       ret = -ENOMEM;
+                       goto fail;
+               }
+
+               if (!IS_ALIGNED((unsigned long)skb->data, HTT_RX_DESC_ALIGN))
+                       skb_pull(skb,
+                                PTR_ALIGN(skb->data, HTT_RX_DESC_ALIGN) -
+                                skb->data);
+
+               /* Clear rx_desc attention word before posting to Rx ring */
+               rx_desc = (struct htt_rx_desc *)skb->data;
+               rx_desc->attention.flags = __cpu_to_le32(0);
+
+               paddr = dma_map_single(htt->ar->dev, skb->data,
+                                      skb->len + skb_tailroom(skb),
+                                      DMA_FROM_DEVICE);
+
+               if (unlikely(dma_mapping_error(htt->ar->dev, paddr))) {
+                       dev_kfree_skb_any(skb);
+                       ret = -ENOMEM;
+                       goto fail;
+               }
+
+               ATH10K_SKB_CB(skb)->paddr = paddr;
+               htt->rx_ring.netbufs_ring[idx] = skb;
+               htt->rx_ring.paddrs_ring[idx] = __cpu_to_le32(paddr);
+               htt->rx_ring.fill_cnt++;
+
+               num--;
+               idx++;
+               idx &= htt->rx_ring.size_mask;
+       }
+
+fail:
+       *(htt->rx_ring.alloc_idx.vaddr) = __cpu_to_le32(idx);
+       return ret;
+}
+
+static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
+{
+       lockdep_assert_held(&htt->rx_ring.lock);
+       return __ath10k_htt_rx_ring_fill_n(htt, num);
+}
+
+static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
+{
+       int ret, num_to_fill;
+
+       spin_lock_bh(&htt->rx_ring.lock);
+       num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
+       ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill);
+       if (ret == -ENOMEM) {
+               /*
+                * Failed to fill it to the desired level -
+                * we'll start a timer and try again next time.
+                * As long as enough buffers are left in the ring for
+                * another A-MPDU rx, no special recovery is needed.
+                */
+               mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
+                         msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS));
+       }
+       spin_unlock_bh(&htt->rx_ring.lock);
+}
+
+static void ath10k_htt_rx_ring_refill_retry(unsigned long arg)
+{
+       struct ath10k_htt *htt = (struct ath10k_htt *)arg;
+       ath10k_htt_rx_msdu_buff_replenish(htt);
+}
+
+static unsigned ath10k_htt_rx_ring_elems(struct ath10k_htt *htt)
+{
+       return (__le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr) -
+               htt->rx_ring.sw_rd_idx.msdu_payld) & htt->rx_ring.size_mask;
+}
+
+void ath10k_htt_rx_detach(struct ath10k_htt *htt)
+{
+       int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
+
+       del_timer_sync(&htt->rx_ring.refill_retry_timer);
+
+       while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
+               struct sk_buff *skb =
+                               htt->rx_ring.netbufs_ring[sw_rd_idx];
+               struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+
+               dma_unmap_single(htt->ar->dev, cb->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(htt->rx_ring.netbufs_ring[sw_rd_idx]);
+               sw_rd_idx++;
+               sw_rd_idx &= htt->rx_ring.size_mask;
+       }
+
+       dma_free_coherent(htt->ar->dev,
+                         (htt->rx_ring.size *
+                          sizeof(htt->rx_ring.paddrs_ring)),
+                         htt->rx_ring.paddrs_ring,
+                         htt->rx_ring.base_paddr);
+
+       dma_free_coherent(htt->ar->dev,
+                         sizeof(*htt->rx_ring.alloc_idx.vaddr),
+                         htt->rx_ring.alloc_idx.vaddr,
+                         htt->rx_ring.alloc_idx.paddr);
+
+       kfree(htt->rx_ring.netbufs_ring);
+}
+
+static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
+{
+       int idx;
+       struct sk_buff *msdu;
+
+       spin_lock_bh(&htt->rx_ring.lock);
+
+       if (ath10k_htt_rx_ring_elems(htt) == 0)
+               ath10k_warn("htt rx ring is empty!\n");
+
+       idx = htt->rx_ring.sw_rd_idx.msdu_payld;
+       msdu = htt->rx_ring.netbufs_ring[idx];
+
+       idx++;
+       idx &= htt->rx_ring.size_mask;
+       htt->rx_ring.sw_rd_idx.msdu_payld = idx;
+       htt->rx_ring.fill_cnt--;
+
+       spin_unlock_bh(&htt->rx_ring.lock);
+       return msdu;
+}
+
+static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb)
+{
+       struct sk_buff *next;
+
+       while (skb) {
+               next = skb->next;
+               dev_kfree_skb_any(skb);
+               skb = next;
+       }
+}
+
+static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
+                                  u8 **fw_desc, int *fw_desc_len,
+                                  struct sk_buff **head_msdu,
+                                  struct sk_buff **tail_msdu)
+{
+       int msdu_len, msdu_chaining = 0;
+       struct sk_buff *msdu;
+       struct htt_rx_desc *rx_desc;
+
+       if (ath10k_htt_rx_ring_elems(htt) == 0)
+               ath10k_warn("htt rx ring is empty!\n");
+
+       if (htt->rx_confused) {
+               ath10k_warn("htt is confused. refusing rx\n");
+               return 0;
+       }
+
+       msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt);
+       while (msdu) {
+               int last_msdu, msdu_len_invalid, msdu_chained;
+
+               dma_unmap_single(htt->ar->dev,
+                                ATH10K_SKB_CB(msdu)->paddr,
+                                msdu->len + skb_tailroom(msdu),
+                                DMA_FROM_DEVICE);
+
+               ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
+                               msdu->data, msdu->len + skb_tailroom(msdu));
+
+               rx_desc = (struct htt_rx_desc *)msdu->data;
+
+               /* FIXME: we must report msdu payload since this is what caller
+                *        expects now */
+               skb_put(msdu, offsetof(struct htt_rx_desc, msdu_payload));
+               skb_pull(msdu, offsetof(struct htt_rx_desc, msdu_payload));
+
+               /*
+                * Sanity check - confirm the HW is finished filling in the
+                * rx data.
+                * If the HW and SW are working correctly, then it's guaranteed
+                * that the HW's MAC DMA is done before this point in the SW.
+                * To prevent the case that we handle a stale Rx descriptor,
+                * just assert for now until we have a way to recover.
+                */
+               if (!(__le32_to_cpu(rx_desc->attention.flags)
+                               & RX_ATTENTION_FLAGS_MSDU_DONE)) {
+                       ath10k_htt_rx_free_msdu_chain(*head_msdu);
+                       *head_msdu = NULL;
+                       msdu = NULL;
+                       ath10k_err("htt rx stopped. cannot recover\n");
+                       htt->rx_confused = true;
+                       break;
+               }
+
+               /*
+                * Copy the FW rx descriptor for this MSDU from the rx
+                * indication message into the MSDU's netbuf. HL uses the
+                * same rx indication message definition as LL, and simply
+                * appends new info (fields from the HW rx desc, and the
+                * MSDU payload itself). So, the offset into the rx
+                * indication message only has to account for the standard
+                * offset of the per-MSDU FW rx desc info within the
+                * message, and how many bytes of the per-MSDU FW rx desc
+                * info have already been consumed. (And the endianness of
+                * the host, since for a big-endian host, the rx ind
+                * message contents, including the per-MSDU rx desc bytes,
+                * were byteswapped during upload.)
+                */
+               if (*fw_desc_len > 0) {
+                       rx_desc->fw_desc.info0 = **fw_desc;
+                       /*
+                        * The target is expected to only provide the basic
+                        * per-MSDU rx descriptors. Just to be sure, verify
+                        * that the target has not attached extension data
+                        * (e.g. LRO flow ID).
+                        */
+
+                       /* or more, if there's extension data */
+                       (*fw_desc)++;
+                       (*fw_desc_len)--;
+               } else {
+                       /*
+                        * When an oversized AMSDU happened, FW will lost
+                        * some of MSDU status - in this case, the FW
+                        * descriptors provided will be less than the
+                        * actual MSDUs inside this MPDU. Mark the FW
+                        * descriptors so that it will still deliver to
+                        * upper stack, if no CRC error for this MPDU.
+                        *
+                        * FIX THIS - the FW descriptors are actually for
+                        * MSDUs in the end of this A-MSDU instead of the
+                        * beginning.
+                        */
+                       rx_desc->fw_desc.info0 = 0;
+               }
+
+               msdu_len_invalid = !!(__le32_to_cpu(rx_desc->attention.flags)
+                                       & (RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR |
+                                          RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR));
+               msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0),
+                             RX_MSDU_START_INFO0_MSDU_LENGTH);
+               msdu_chained = rx_desc->frag_info.ring2_more_count;
+
+               if (msdu_len_invalid)
+                       msdu_len = 0;
+
+               skb_trim(msdu, 0);
+               skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE));
+               msdu_len -= msdu->len;
+
+               /* FIXME: Do chained buffers include htt_rx_desc or not? */
+               while (msdu_chained--) {
+                       struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
+
+                       dma_unmap_single(htt->ar->dev,
+                                        ATH10K_SKB_CB(next)->paddr,
+                                        next->len + skb_tailroom(next),
+                                        DMA_FROM_DEVICE);
+
+                       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
+                                       next->data,
+                                       next->len + skb_tailroom(next));
+
+                       skb_trim(next, 0);
+                       skb_put(next, min(msdu_len, HTT_RX_BUF_SIZE));
+                       msdu_len -= next->len;
+
+                       msdu->next = next;
+                       msdu = next;
+                       msdu_chaining = 1;
+               }
+
+               if (msdu_len > 0) {
+                       /* This may suggest FW bug? */
+                       ath10k_warn("htt rx msdu len not consumed (%d)\n",
+                                   msdu_len);
+               }
+
+               last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
+                               RX_MSDU_END_INFO0_LAST_MSDU;
+
+               if (last_msdu) {
+                       msdu->next = NULL;
+                       break;
+               } else {
+                       struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
+                       msdu->next = next;
+                       msdu = next;
+               }
+       }
+       *tail_msdu = msdu;
+
+       /*
+        * Don't refill the ring yet.
+        *
+        * First, the elements popped here are still in use - it is not
+        * safe to overwrite them until the matching call to
+        * mpdu_desc_list_next. Second, for efficiency it is preferable to
+        * refill the rx ring with 1 PPDU's worth of rx buffers (something
+        * like 32 x 3 buffers), rather than one MPDU's worth of rx buffers
+        * (something like 3 buffers). Consequently, we'll rely on the txrx
+        * SW to tell us when it is done pulling all the PPDU's rx buffers
+        * out of the rx ring, and then refill it just once.
+        */
+
+       return msdu_chaining;
+}
+
+int ath10k_htt_rx_attach(struct ath10k_htt *htt)
+{
+       dma_addr_t paddr;
+       void *vaddr;
+       struct timer_list *timer = &htt->rx_ring.refill_retry_timer;
+
+       htt->rx_ring.size = ath10k_htt_rx_ring_size(htt);
+       if (!is_power_of_2(htt->rx_ring.size)) {
+               ath10k_warn("htt rx ring size is not power of 2\n");
+               return -EINVAL;
+       }
+
+       htt->rx_ring.size_mask = htt->rx_ring.size - 1;
+
+       /*
+        * Set the initial value for the level to which the rx ring
+        * should be filled, based on the max throughput and the
+        * worst likely latency for the host to fill the rx ring
+        * with new buffers. In theory, this fill level can be
+        * dynamically adjusted from the initial value set here, to
+        * reflect the actual host latency rather than a
+        * conservative assumption about the host latency.
+        */
+       htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt);
+
+       htt->rx_ring.netbufs_ring =
+               kmalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
+                       GFP_KERNEL);
+       if (!htt->rx_ring.netbufs_ring)
+               goto err_netbuf;
+
+       vaddr = dma_alloc_coherent(htt->ar->dev,
+                  (htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring)),
+                  &paddr, GFP_DMA);
+       if (!vaddr)
+               goto err_dma_ring;
+
+       htt->rx_ring.paddrs_ring = vaddr;
+       htt->rx_ring.base_paddr = paddr;
+
+       vaddr = dma_alloc_coherent(htt->ar->dev,
+                                  sizeof(*htt->rx_ring.alloc_idx.vaddr),
+                                  &paddr, GFP_DMA);
+       if (!vaddr)
+               goto err_dma_idx;
+
+       htt->rx_ring.alloc_idx.vaddr = vaddr;
+       htt->rx_ring.alloc_idx.paddr = paddr;
+       htt->rx_ring.sw_rd_idx.msdu_payld = 0;
+       *htt->rx_ring.alloc_idx.vaddr = 0;
+
+       /* Initialize the Rx refill retry timer */
+       setup_timer(timer, ath10k_htt_rx_ring_refill_retry, (unsigned long)htt);
+
+       spin_lock_init(&htt->rx_ring.lock);
+
+       htt->rx_ring.fill_cnt = 0;
+       if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level))
+               goto err_fill_ring;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n",
+                  htt->rx_ring.size, htt->rx_ring.fill_level);
+       return 0;
+
+err_fill_ring:
+       ath10k_htt_rx_ring_free(htt);
+       dma_free_coherent(htt->ar->dev,
+                         sizeof(*htt->rx_ring.alloc_idx.vaddr),
+                         htt->rx_ring.alloc_idx.vaddr,
+                         htt->rx_ring.alloc_idx.paddr);
+err_dma_idx:
+       dma_free_coherent(htt->ar->dev,
+                         (htt->rx_ring.size *
+                          sizeof(htt->rx_ring.paddrs_ring)),
+                         htt->rx_ring.paddrs_ring,
+                         htt->rx_ring.base_paddr);
+err_dma_ring:
+       kfree(htt->rx_ring.netbufs_ring);
+err_netbuf:
+       return -ENOMEM;
+}
+
+static int ath10k_htt_rx_crypto_param_len(enum htt_rx_mpdu_encrypt_type type)
+{
+       switch (type) {
+       case HTT_RX_MPDU_ENCRYPT_WEP40:
+       case HTT_RX_MPDU_ENCRYPT_WEP104:
+               return 4;
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
+       case HTT_RX_MPDU_ENCRYPT_WEP128: /* not tested */
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
+       case HTT_RX_MPDU_ENCRYPT_WAPI: /* not tested */
+       case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
+               return 8;
+       case HTT_RX_MPDU_ENCRYPT_NONE:
+               return 0;
+       }
+
+       ath10k_warn("unknown encryption type %d\n", type);
+       return 0;
+}
+
+static int ath10k_htt_rx_crypto_tail_len(enum htt_rx_mpdu_encrypt_type type)
+{
+       switch (type) {
+       case HTT_RX_MPDU_ENCRYPT_NONE:
+       case HTT_RX_MPDU_ENCRYPT_WEP40:
+       case HTT_RX_MPDU_ENCRYPT_WEP104:
+       case HTT_RX_MPDU_ENCRYPT_WEP128:
+       case HTT_RX_MPDU_ENCRYPT_WAPI:
+               return 0;
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
+               return 4;
+       case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
+               return 8;
+       }
+
+       ath10k_warn("unknown encryption type %d\n", type);
+       return 0;
+}
+
+/* Applies for first msdu in chain, before altering it. */
+static struct ieee80211_hdr *ath10k_htt_rx_skb_get_hdr(struct sk_buff *skb)
+{
+       struct htt_rx_desc *rxd;
+       enum rx_msdu_decap_format fmt;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+       if (fmt == RX_MSDU_DECAP_RAW)
+               return (void *)skb->data;
+       else
+               return (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+}
+
+/* This function only applies for first msdu in an msdu chain */
+static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
+{
+       if (ieee80211_is_data_qos(hdr->frame_control)) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               if (qc[0] & 0x80)
+                       return true;
+       }
+       return false;
+}
+
+static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
+                       struct htt_rx_info *info)
+{
+       struct htt_rx_desc *rxd;
+       struct sk_buff *amsdu;
+       struct sk_buff *first;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb = info->skb;
+       enum rx_msdu_decap_format fmt;
+       enum htt_rx_mpdu_encrypt_type enctype;
+       unsigned int hdr_len;
+       int crypto_len;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+       enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+                       RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+
+       /* FIXME: No idea what assumptions are safe here. Need logs */
+       if ((fmt == RX_MSDU_DECAP_RAW && skb->next) ||
+           (fmt == RX_MSDU_DECAP_8023_SNAP_LLC)) {
+               ath10k_htt_rx_free_msdu_chain(skb->next);
+               skb->next = NULL;
+               return -ENOTSUPP;
+       }
+
+       /* A-MSDU max is a little less than 8K */
+       amsdu = dev_alloc_skb(8*1024);
+       if (!amsdu) {
+               ath10k_warn("A-MSDU allocation failed\n");
+               ath10k_htt_rx_free_msdu_chain(skb->next);
+               skb->next = NULL;
+               return -ENOMEM;
+       }
+
+       if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) {
+               int hdrlen;
+
+               hdr = (void *)rxd->rx_hdr_status;
+               hdrlen = ieee80211_hdrlen(hdr->frame_control);
+               memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen);
+       }
+
+       first = skb;
+       while (skb) {
+               void *decap_hdr;
+               int decap_len = 0;
+
+               rxd = (void *)skb->data - sizeof(*rxd);
+               fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                               RX_MSDU_START_INFO1_DECAP_FORMAT);
+               decap_hdr = (void *)rxd->rx_hdr_status;
+
+               if (skb == first) {
+                       /* We receive linked A-MSDU subframe skbuffs. The
+                        * first one contains the original 802.11 header (and
+                        * possible crypto param) in the RX descriptor. The
+                        * A-MSDU subframe header follows that. Each part is
+                        * aligned to 4 byte boundary. */
+
+                       hdr = (void *)amsdu->data;
+                       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+                       crypto_len = ath10k_htt_rx_crypto_param_len(enctype);
+
+                       decap_hdr += roundup(hdr_len, 4);
+                       decap_hdr += roundup(crypto_len, 4);
+               }
+
+               if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
+                       /* Ethernet2 decap inserts ethernet header in place of
+                        * A-MSDU subframe header. */
+                       skb_pull(skb, 6 + 6 + 2);
+
+                       /* A-MSDU subframe header length */
+                       decap_len += 6 + 6 + 2;
+
+                       /* Ethernet2 decap also strips the LLC/SNAP so we need
+                        * to re-insert it. The LLC/SNAP follows A-MSDU
+                        * subframe header. */
+                       /* FIXME: Not all LLCs are 8 bytes long */
+                       decap_len += 8;
+
+                       memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
+               }
+
+               if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) {
+                       /* Native Wifi decap inserts regular 802.11 header
+                        * in place of A-MSDU subframe header. */
+                       hdr = (struct ieee80211_hdr *)skb->data;
+                       skb_pull(skb, ieee80211_hdrlen(hdr->frame_control));
+
+                       /* A-MSDU subframe header length */
+                       decap_len += 6 + 6 + 2;
+
+                       memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
+               }
+
+               if (fmt == RX_MSDU_DECAP_RAW)
+                       skb_trim(skb, skb->len - 4); /* remove FCS */
+
+               memcpy(skb_put(amsdu, skb->len), skb->data, skb->len);
+
+               /* A-MSDU subframes are padded to 4bytes
+                * but relative to first subframe, not the whole MPDU */
+               if (skb->next && ((decap_len + skb->len) & 3)) {
+                       int padlen = 4 - ((decap_len + skb->len) & 3);
+                       memset(skb_put(amsdu, padlen), 0, padlen);
+               }
+
+               skb = skb->next;
+       }
+
+       info->skb = amsdu;
+       info->encrypt_type = enctype;
+
+       ath10k_htt_rx_free_msdu_chain(first);
+
+       return 0;
+}
+
+static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
+{
+       struct sk_buff *skb = info->skb;
+       struct htt_rx_desc *rxd;
+       struct ieee80211_hdr *hdr;
+       enum rx_msdu_decap_format fmt;
+       enum htt_rx_mpdu_encrypt_type enctype;
+
+       /* This shouldn't happen. If it does than it may be a FW bug. */
+       if (skb->next) {
+               ath10k_warn("received chained non A-MSDU frame\n");
+               ath10k_htt_rx_free_msdu_chain(skb->next);
+               skb->next = NULL;
+       }
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+       enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+                       RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+       hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+
+       switch (fmt) {
+       case RX_MSDU_DECAP_RAW:
+               /* remove trailing FCS */
+               skb_trim(skb, skb->len - 4);
+               break;
+       case RX_MSDU_DECAP_NATIVE_WIFI:
+               /* nothing to do here */
+               break;
+       case RX_MSDU_DECAP_ETHERNET2_DIX:
+               /* macaddr[6] + macaddr[6] + ethertype[2] */
+               skb_pull(skb, 6 + 6 + 2);
+               break;
+       case RX_MSDU_DECAP_8023_SNAP_LLC:
+               /* macaddr[6] + macaddr[6] + len[2] */
+               /* we don't need this for non-A-MSDU */
+               skb_pull(skb, 6 + 6 + 2);
+               break;
+       }
+
+       if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
+               void *llc;
+               int llclen;
+
+               llclen = 8;
+               llc  = hdr;
+               llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4);
+               llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
+
+               skb_push(skb, llclen);
+               memcpy(skb->data, llc, llclen);
+       }
+
+       if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) {
+               int len = ieee80211_hdrlen(hdr->frame_control);
+               skb_push(skb, len);
+               memcpy(skb->data, hdr, len);
+       }
+
+       info->skb = skb;
+       info->encrypt_type = enctype;
+       return 0;
+}
+
+static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb)
+{
+       struct htt_rx_desc *rxd;
+       u32 flags;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       flags = __le32_to_cpu(rxd->attention.flags);
+
+       if (flags & RX_ATTENTION_FLAGS_DECRYPT_ERR)
+               return true;
+
+       return false;
+}
+
+static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb)
+{
+       struct htt_rx_desc *rxd;
+       u32 flags;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       flags = __le32_to_cpu(rxd->attention.flags);
+
+       if (flags & RX_ATTENTION_FLAGS_FCS_ERR)
+               return true;
+
+       return false;
+}
+
+static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
+                                 struct htt_rx_indication *rx)
+{
+       struct htt_rx_info info;
+       struct htt_rx_indication_mpdu_range *mpdu_ranges;
+       struct ieee80211_hdr *hdr;
+       int num_mpdu_ranges;
+       int fw_desc_len;
+       u8 *fw_desc;
+       int i, j;
+       int ret;
+
+       memset(&info, 0, sizeof(info));
+
+       fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
+       fw_desc = (u8 *)&rx->fw_desc;
+
+       num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
+                            HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
+       mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
+
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
+                       rx, sizeof(*rx) +
+                       (sizeof(struct htt_rx_indication_mpdu_range) *
+                               num_mpdu_ranges));
+
+       for (i = 0; i < num_mpdu_ranges; i++) {
+               info.status = mpdu_ranges[i].mpdu_range_status;
+
+               for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) {
+                       struct sk_buff *msdu_head, *msdu_tail;
+                       enum htt_rx_mpdu_status status;
+                       int msdu_chaining;
+
+                       msdu_head = NULL;
+                       msdu_tail = NULL;
+                       msdu_chaining = ath10k_htt_rx_amsdu_pop(htt,
+                                                        &fw_desc,
+                                                        &fw_desc_len,
+                                                        &msdu_head,
+                                                        &msdu_tail);
+
+                       if (!msdu_head) {
+                               ath10k_warn("htt rx no data!\n");
+                               continue;
+                       }
+
+                       if (msdu_head->len == 0) {
+                               ath10k_dbg(ATH10K_DBG_HTT,
+                                          "htt rx dropping due to zero-len\n");
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       if (ath10k_htt_rx_has_decrypt_err(msdu_head)) {
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       status = info.status;
+
+                       /* Skip mgmt frames while we handle this in WMI */
+                       if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) {
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       if (status != HTT_RX_IND_MPDU_STATUS_OK &&
+                           status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
+                           !htt->ar->monitor_enabled) {
+                               ath10k_dbg(ATH10K_DBG_HTT,
+                                          "htt rx ignoring frame w/ status %d\n",
+                                          status);
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       /* FIXME: we do not support chaining yet.
+                        * this needs investigation */
+                       if (msdu_chaining) {
+                               ath10k_warn("msdu_chaining is true\n");
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       info.skb     = msdu_head;
+                       info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
+                       info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
+                       info.signal += rx->ppdu.combined_rssi;
+
+                       info.rate.info0 = rx->ppdu.info0;
+                       info.rate.info1 = __le32_to_cpu(rx->ppdu.info1);
+                       info.rate.info2 = __le32_to_cpu(rx->ppdu.info2);
+
+                       hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
+
+                       if (ath10k_htt_rx_hdr_is_amsdu(hdr))
+                               ret = ath10k_htt_rx_amsdu(htt, &info);
+                       else
+                               ret = ath10k_htt_rx_msdu(htt, &info);
+
+                       if (ret && !info.fcs_err) {
+                               ath10k_warn("error processing msdus %d\n", ret);
+                               dev_kfree_skb_any(info.skb);
+                               continue;
+                       }
+
+                       if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data))
+                               ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n");
+
+                       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ",
+                                       info.skb->data, info.skb->len);
+                       ath10k_process_rx(htt->ar, &info);
+               }
+       }
+
+       ath10k_htt_rx_msdu_buff_replenish(htt);
+}
+
+static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
+                               struct htt_rx_fragment_indication *frag)
+{
+       struct sk_buff *msdu_head, *msdu_tail;
+       struct htt_rx_desc *rxd;
+       enum rx_msdu_decap_format fmt;
+       struct htt_rx_info info = {};
+       struct ieee80211_hdr *hdr;
+       int msdu_chaining;
+       bool tkip_mic_err;
+       bool decrypt_err;
+       u8 *fw_desc;
+       int fw_desc_len, hdrlen, paramlen;
+       int trim;
+
+       fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes);
+       fw_desc = (u8 *)frag->fw_msdu_rx_desc;
+
+       msdu_head = NULL;
+       msdu_tail = NULL;
+       msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
+                                               &msdu_head, &msdu_tail);
+
+       ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
+
+       if (!msdu_head) {
+               ath10k_warn("htt rx frag no data\n");
+               return;
+       }
+
+       if (msdu_chaining || msdu_head != msdu_tail) {
+               ath10k_warn("aggregation with fragmentation?!\n");
+               ath10k_htt_rx_free_msdu_chain(msdu_head);
+               return;
+       }
+
+       /* FIXME: implement signal strength */
+
+       hdr = (struct ieee80211_hdr *)msdu_head->data;
+       rxd = (void *)msdu_head->data - sizeof(*rxd);
+       tkip_mic_err = !!(__le32_to_cpu(rxd->attention.flags) &
+                               RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
+       decrypt_err = !!(__le32_to_cpu(rxd->attention.flags) &
+                               RX_ATTENTION_FLAGS_DECRYPT_ERR);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+       if (fmt != RX_MSDU_DECAP_RAW) {
+               ath10k_warn("we dont support non-raw fragmented rx yet\n");
+               dev_kfree_skb_any(msdu_head);
+               goto end;
+       }
+
+       info.skb = msdu_head;
+       info.status = HTT_RX_IND_MPDU_STATUS_OK;
+       info.encrypt_type = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+                               RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+
+       if (tkip_mic_err) {
+               ath10k_warn("tkip mic error\n");
+               info.status = HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR;
+       }
+
+       if (decrypt_err) {
+               ath10k_warn("decryption err in fragmented rx\n");
+               dev_kfree_skb_any(info.skb);
+               goto end;
+       }
+
+       if (info.encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) {
+               hdrlen = ieee80211_hdrlen(hdr->frame_control);
+               paramlen = ath10k_htt_rx_crypto_param_len(info.encrypt_type);
+
+               /* It is more efficient to move the header than the payload */
+               memmove((void *)info.skb->data + paramlen,
+                       (void *)info.skb->data,
+                       hdrlen);
+               skb_pull(info.skb, paramlen);
+               hdr = (struct ieee80211_hdr *)info.skb->data;
+       }
+
+       /* remove trailing FCS */
+       trim  = 4;
+
+       /* remove crypto trailer */
+       trim += ath10k_htt_rx_crypto_tail_len(info.encrypt_type);
+
+       /* last fragment of TKIP frags has MIC */
+       if (!ieee80211_has_morefrags(hdr->frame_control) &&
+           info.encrypt_type == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
+               trim += 8;
+
+       if (trim > info.skb->len) {
+               ath10k_warn("htt rx fragment: trailer longer than the frame itself? drop\n");
+               dev_kfree_skb_any(info.skb);
+               goto end;
+       }
+
+       skb_trim(info.skb, info.skb->len - trim);
+
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt frag mpdu: ",
+                       info.skb->data, info.skb->len);
+       ath10k_process_rx(htt->ar, &info);
+
+end:
+       if (fw_desc_len > 0) {
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "expecting more fragmented rx in one indication %d\n",
+                          fw_desc_len);
+       }
+}
+
+void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct ath10k_htt *htt = ar->htt;
+       struct htt_resp *resp = (struct htt_resp *)skb->data;
+
+       /* confirm alignment */
+       if (!IS_ALIGNED((unsigned long)skb->data, 4))
+               ath10k_warn("unaligned htt message, expect trouble\n");
+
+       ath10k_dbg(ATH10K_DBG_HTT, "HTT RX, msg_type: 0x%0X\n",
+                  resp->hdr.msg_type);
+       switch (resp->hdr.msg_type) {
+       case HTT_T2H_MSG_TYPE_VERSION_CONF: {
+               htt->target_version_major = resp->ver_resp.major;
+               htt->target_version_minor = resp->ver_resp.minor;
+               complete(&htt->target_version_received);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_RX_IND: {
+               ath10k_htt_rx_handler(htt, &resp->rx_ind);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_PEER_MAP: {
+               struct htt_peer_map_event ev = {
+                       .vdev_id = resp->peer_map.vdev_id,
+                       .peer_id = __le16_to_cpu(resp->peer_map.peer_id),
+               };
+               memcpy(ev.addr, resp->peer_map.addr, sizeof(ev.addr));
+               ath10k_peer_map_event(htt, &ev);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_PEER_UNMAP: {
+               struct htt_peer_unmap_event ev = {
+                       .peer_id = __le16_to_cpu(resp->peer_unmap.peer_id),
+               };
+               ath10k_peer_unmap_event(htt, &ev);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION: {
+               struct htt_tx_done tx_done = {};
+               int status = __le32_to_cpu(resp->mgmt_tx_completion.status);
+
+               tx_done.msdu_id =
+                       __le32_to_cpu(resp->mgmt_tx_completion.desc_id);
+
+               switch (status) {
+               case HTT_MGMT_TX_STATUS_OK:
+                       break;
+               case HTT_MGMT_TX_STATUS_RETRY:
+                       tx_done.no_ack = true;
+                       break;
+               case HTT_MGMT_TX_STATUS_DROP:
+                       tx_done.discard = true;
+                       break;
+               }
+
+               ath10k_txrx_tx_completed(htt, &tx_done);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_TX_COMPL_IND: {
+               struct htt_tx_done tx_done = {};
+               int status = MS(resp->data_tx_completion.flags,
+                               HTT_DATA_TX_STATUS);
+               __le16 msdu_id;
+               int i;
+
+               switch (status) {
+               case HTT_DATA_TX_STATUS_NO_ACK:
+                       tx_done.no_ack = true;
+                       break;
+               case HTT_DATA_TX_STATUS_OK:
+                       break;
+               case HTT_DATA_TX_STATUS_DISCARD:
+               case HTT_DATA_TX_STATUS_POSTPONE:
+               case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
+                       tx_done.discard = true;
+                       break;
+               default:
+                       ath10k_warn("unhandled tx completion status %d\n",
+                                   status);
+                       tx_done.discard = true;
+                       break;
+               }
+
+               ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
+                          resp->data_tx_completion.num_msdus);
+
+               for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
+                       msdu_id = resp->data_tx_completion.msdus[i];
+                       tx_done.msdu_id = __le16_to_cpu(msdu_id);
+                       ath10k_txrx_tx_completed(htt, &tx_done);
+               }
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_SEC_IND: {
+               struct ath10k *ar = htt->ar;
+               struct htt_security_indication *ev = &resp->security_indication;
+
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "sec ind peer_id %d unicast %d type %d\n",
+                         __le16_to_cpu(ev->peer_id),
+                         !!(ev->flags & HTT_SECURITY_IS_UNICAST),
+                         MS(ev->flags, HTT_SECURITY_TYPE));
+               complete(&ar->install_key_done);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_RX_FRAG_IND: {
+               ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
+                               skb->data, skb->len);
+               ath10k_htt_rx_frag_handler(htt, &resp->rx_frag_ind);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_TEST:
+               /* FIX THIS */
+               break;
+       case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
+       case HTT_T2H_MSG_TYPE_STATS_CONF:
+       case HTT_T2H_MSG_TYPE_RX_ADDBA:
+       case HTT_T2H_MSG_TYPE_RX_DELBA:
+       case HTT_T2H_MSG_TYPE_RX_FLUSH:
+       default:
+               ath10k_dbg(ATH10K_DBG_HTT, "htt event (%d) not handled\n",
+                          resp->hdr.msg_type);
+               ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
+                               skb->data, skb->len);
+               break;
+       };
+
+       /* Free the indication buffer */
+       dev_kfree_skb_any(skb);
+}
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
new file mode 100644 (file)
index 0000000..ef79106
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/etherdevice.h>
+#include "htt.h"
+#include "mac.h"
+#include "hif.h"
+#include "txrx.h"
+#include "debug.h"
+
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+{
+       htt->num_pending_tx--;
+       if (htt->num_pending_tx == htt->max_num_pending_tx - 1)
+               ieee80211_wake_queues(htt->ar->hw);
+}
+
+static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+{
+       spin_lock_bh(&htt->tx_lock);
+       __ath10k_htt_tx_dec_pending(htt);
+       spin_unlock_bh(&htt->tx_lock);
+}
+
+static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt)
+{
+       int ret = 0;
+
+       spin_lock_bh(&htt->tx_lock);
+
+       if (htt->num_pending_tx >= htt->max_num_pending_tx) {
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       htt->num_pending_tx++;
+       if (htt->num_pending_tx == htt->max_num_pending_tx)
+               ieee80211_stop_queues(htt->ar->hw);
+
+exit:
+       spin_unlock_bh(&htt->tx_lock);
+       return ret;
+}
+
+int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt)
+{
+       int msdu_id;
+
+       lockdep_assert_held(&htt->tx_lock);
+
+       msdu_id = find_first_zero_bit(htt->used_msdu_ids,
+                                     htt->max_num_pending_tx);
+       if (msdu_id == htt->max_num_pending_tx)
+               return -ENOBUFS;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id);
+       __set_bit(msdu_id, htt->used_msdu_ids);
+       return msdu_id;
+}
+
+void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id)
+{
+       lockdep_assert_held(&htt->tx_lock);
+
+       if (!test_bit(msdu_id, htt->used_msdu_ids))
+               ath10k_warn("trying to free unallocated msdu_id %d\n", msdu_id);
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id);
+       __clear_bit(msdu_id, htt->used_msdu_ids);
+}
+
+int ath10k_htt_tx_attach(struct ath10k_htt *htt)
+{
+       u8 pipe;
+
+       spin_lock_init(&htt->tx_lock);
+       init_waitqueue_head(&htt->empty_tx_wq);
+
+       /* At the beginning free queue number should hint us the maximum
+        * queue length */
+       pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id;
+       htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
+                                                                  pipe);
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx max num pending tx %d\n",
+                  htt->max_num_pending_tx);
+
+       htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) *
+                                 htt->max_num_pending_tx, GFP_KERNEL);
+       if (!htt->pending_tx)
+               return -ENOMEM;
+
+       htt->used_msdu_ids = kzalloc(sizeof(unsigned long) *
+                                    BITS_TO_LONGS(htt->max_num_pending_tx),
+                                    GFP_KERNEL);
+       if (!htt->used_msdu_ids) {
+               kfree(htt->pending_tx);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
+{
+       struct sk_buff *txdesc;
+       int msdu_id;
+
+       /* No locks needed. Called after communication with the device has
+        * been stopped. */
+
+       for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) {
+               if (!test_bit(msdu_id, htt->used_msdu_ids))
+                       continue;
+
+               txdesc = htt->pending_tx[msdu_id];
+               if (!txdesc)
+                       continue;
+
+               ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n",
+                          msdu_id);
+
+               if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
+                       ATH10K_SKB_CB(txdesc)->htt.refcount = 1;
+
+               ATH10K_SKB_CB(txdesc)->htt.discard = true;
+               ath10k_txrx_tx_unref(htt, txdesc);
+       }
+}
+
+void ath10k_htt_tx_detach(struct ath10k_htt *htt)
+{
+       ath10k_htt_tx_cleanup_pending(htt);
+       kfree(htt->pending_tx);
+       kfree(htt->used_msdu_ids);
+       return;
+}
+
+void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+       struct ath10k_htt *htt = ar->htt;
+
+       if (skb_cb->htt.is_conf) {
+               dev_kfree_skb_any(skb);
+               return;
+       }
+
+       if (skb_cb->is_aborted) {
+               skb_cb->htt.discard = true;
+
+               /* if the skbuff is aborted we need to make sure we'll free up
+                * the tx resources, we can't simply run tx_unref() 2 times
+                * because if htt tx completion came in earlier we'd access
+                * unallocated memory */
+               if (skb_cb->htt.refcount > 1)
+                       skb_cb->htt.refcount = 1;
+       }
+
+       ath10k_txrx_tx_unref(htt, skb);
+}
+
+int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
+{
+       struct sk_buff *skb;
+       struct htt_cmd *cmd;
+       int len = 0;
+       int ret;
+
+       len += sizeof(cmd->hdr);
+       len += sizeof(cmd->ver_req);
+
+       skb = ath10k_htc_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, len);
+       cmd = (struct htt_cmd *)skb->data;
+       cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_VERSION_REQ;
+
+       ATH10K_SKB_CB(skb)->htt.is_conf = true;
+
+       ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+       if (ret) {
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
+{
+       struct sk_buff *skb;
+       struct htt_cmd *cmd;
+       struct htt_rx_ring_setup_ring *ring;
+       const int num_rx_ring = 1;
+       u16 flags;
+       u32 fw_idx;
+       int len;
+       int ret;
+
+       /*
+        * the HW expects the buffer to be an integral number of 4-byte
+        * "words"
+        */
+       BUILD_BUG_ON(!IS_ALIGNED(HTT_RX_BUF_SIZE, 4));
+       BUILD_BUG_ON((HTT_RX_BUF_SIZE & HTT_MAX_CACHE_LINE_SIZE_MASK) != 0);
+
+       len = sizeof(cmd->hdr) + sizeof(cmd->rx_setup.hdr)
+           + (sizeof(*ring) * num_rx_ring);
+       skb = ath10k_htc_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, len);
+
+       cmd = (struct htt_cmd *)skb->data;
+       ring = &cmd->rx_setup.rings[0];
+
+       cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_RX_RING_CFG;
+       cmd->rx_setup.hdr.num_rings = 1;
+
+       /* FIXME: do we need all of this? */
+       flags = 0;
+       flags |= HTT_RX_RING_FLAGS_MAC80211_HDR;
+       flags |= HTT_RX_RING_FLAGS_MSDU_PAYLOAD;
+       flags |= HTT_RX_RING_FLAGS_PPDU_START;
+       flags |= HTT_RX_RING_FLAGS_PPDU_END;
+       flags |= HTT_RX_RING_FLAGS_MPDU_START;
+       flags |= HTT_RX_RING_FLAGS_MPDU_END;
+       flags |= HTT_RX_RING_FLAGS_MSDU_START;
+       flags |= HTT_RX_RING_FLAGS_MSDU_END;
+       flags |= HTT_RX_RING_FLAGS_RX_ATTENTION;
+       flags |= HTT_RX_RING_FLAGS_FRAG_INFO;
+       flags |= HTT_RX_RING_FLAGS_UNICAST_RX;
+       flags |= HTT_RX_RING_FLAGS_MULTICAST_RX;
+       flags |= HTT_RX_RING_FLAGS_CTRL_RX;
+       flags |= HTT_RX_RING_FLAGS_MGMT_RX;
+       flags |= HTT_RX_RING_FLAGS_NULL_RX;
+       flags |= HTT_RX_RING_FLAGS_PHY_DATA_RX;
+
+       fw_idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr);
+
+       ring->fw_idx_shadow_reg_paddr =
+               __cpu_to_le32(htt->rx_ring.alloc_idx.paddr);
+       ring->rx_ring_base_paddr = __cpu_to_le32(htt->rx_ring.base_paddr);
+       ring->rx_ring_len = __cpu_to_le16(htt->rx_ring.size);
+       ring->rx_ring_bufsize = __cpu_to_le16(HTT_RX_BUF_SIZE);
+       ring->flags = __cpu_to_le16(flags);
+       ring->fw_idx_init_val = __cpu_to_le16(fw_idx);
+
+#define desc_offset(x) (offsetof(struct htt_rx_desc, x) / 4)
+
+       ring->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status));
+       ring->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload));
+       ring->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start));
+       ring->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end));
+       ring->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start));
+       ring->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end));
+       ring->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start));
+       ring->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end));
+       ring->rx_attention_offset = __cpu_to_le16(desc_offset(attention));
+       ring->frag_info_offset = __cpu_to_le16(desc_offset(frag_info));
+
+#undef desc_offset
+
+       ATH10K_SKB_CB(skb)->htt.is_conf = true;
+
+       ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+       if (ret) {
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+{
+       struct device *dev = htt->ar->dev;
+       struct ath10k_skb_cb *skb_cb;
+       struct sk_buff *txdesc = NULL;
+       struct htt_cmd *cmd;
+       u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+       int len = 0;
+       int msdu_id = -1;
+       int res;
+
+
+       res = ath10k_htt_tx_inc_pending(htt);
+       if (res)
+               return res;
+
+       len += sizeof(cmd->hdr);
+       len += sizeof(cmd->mgmt_tx);
+
+       txdesc = ath10k_htc_alloc_skb(len);
+       if (!txdesc) {
+               res = -ENOMEM;
+               goto err;
+       }
+
+       spin_lock_bh(&htt->tx_lock);
+       msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
+       if (msdu_id < 0) {
+               spin_unlock_bh(&htt->tx_lock);
+               res = msdu_id;
+               goto err;
+       }
+       htt->pending_tx[msdu_id] = txdesc;
+       spin_unlock_bh(&htt->tx_lock);
+
+       res = ath10k_skb_map(dev, msdu);
+       if (res)
+               goto err;
+
+       skb_put(txdesc, len);
+       cmd = (struct htt_cmd *)txdesc->data;
+       cmd->hdr.msg_type         = HTT_H2T_MSG_TYPE_MGMT_TX;
+       cmd->mgmt_tx.msdu_paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr);
+       cmd->mgmt_tx.len        = __cpu_to_le32(msdu->len);
+       cmd->mgmt_tx.desc_id    = __cpu_to_le32(msdu_id);
+       cmd->mgmt_tx.vdev_id    = __cpu_to_le32(vdev_id);
+       memcpy(cmd->mgmt_tx.hdr, msdu->data,
+              min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN));
+
+       /* refcount is decremented by HTC and HTT completions until it reaches
+        * zero and is freed */
+       skb_cb = ATH10K_SKB_CB(txdesc);
+       skb_cb->htt.msdu_id = msdu_id;
+       skb_cb->htt.refcount = 2;
+       skb_cb->htt.msdu = msdu;
+
+       res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+       if (res)
+               goto err;
+
+       return 0;
+
+err:
+       ath10k_skb_unmap(dev, msdu);
+
+       if (txdesc)
+               dev_kfree_skb_any(txdesc);
+       if (msdu_id >= 0) {
+               spin_lock_bh(&htt->tx_lock);
+               htt->pending_tx[msdu_id] = NULL;
+               ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+               spin_unlock_bh(&htt->tx_lock);
+       }
+       ath10k_htt_tx_dec_pending(htt);
+       return res;
+}
+
+int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+{
+       struct device *dev = htt->ar->dev;
+       struct htt_cmd *cmd;
+       struct htt_data_tx_desc_frag *tx_frags;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
+       struct ath10k_skb_cb *skb_cb;
+       struct sk_buff *txdesc = NULL;
+       struct sk_buff *txfrag = NULL;
+       u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+       u8 tid;
+       int prefetch_len, desc_len, frag_len;
+       dma_addr_t frags_paddr;
+       int msdu_id = -1;
+       int res;
+       u8 flags0;
+       u16 flags1;
+
+       res = ath10k_htt_tx_inc_pending(htt);
+       if (res)
+               return res;
+
+       prefetch_len = min(htt->prefetch_len, msdu->len);
+       prefetch_len = roundup(prefetch_len, 4);
+
+       desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len;
+       frag_len = sizeof(*tx_frags) * 2;
+
+       txdesc = ath10k_htc_alloc_skb(desc_len);
+       if (!txdesc) {
+               res = -ENOMEM;
+               goto err;
+       }
+
+       txfrag = dev_alloc_skb(frag_len);
+       if (!txfrag) {
+               res = -ENOMEM;
+               goto err;
+       }
+
+       if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) {
+               ath10k_warn("htt alignment check failed. dropping packet.\n");
+               res = -EIO;
+               goto err;
+       }
+
+       spin_lock_bh(&htt->tx_lock);
+       msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
+       if (msdu_id < 0) {
+               spin_unlock_bh(&htt->tx_lock);
+               res = msdu_id;
+               goto err;
+       }
+       htt->pending_tx[msdu_id] = txdesc;
+       spin_unlock_bh(&htt->tx_lock);
+
+       res = ath10k_skb_map(dev, msdu);
+       if (res)
+               goto err;
+
+       /* tx fragment list must be terminated with zero-entry */
+       skb_put(txfrag, frag_len);
+       tx_frags = (struct htt_data_tx_desc_frag *)txfrag->data;
+       tx_frags[0].paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr);
+       tx_frags[0].len   = __cpu_to_le32(msdu->len);
+       tx_frags[1].paddr = __cpu_to_le32(0);
+       tx_frags[1].len   = __cpu_to_le32(0);
+
+       res = ath10k_skb_map(dev, txfrag);
+       if (res)
+               goto err;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "txfrag 0x%llx msdu 0x%llx\n",
+                  (unsigned long long) ATH10K_SKB_CB(txfrag)->paddr,
+                  (unsigned long long) ATH10K_SKB_CB(msdu)->paddr);
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "txfrag: ",
+                       txfrag->data, frag_len);
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ",
+                       msdu->data, msdu->len);
+
+       skb_put(txdesc, desc_len);
+       cmd = (struct htt_cmd *)txdesc->data;
+       memset(cmd, 0, desc_len);
+
+       tid = ATH10K_SKB_CB(msdu)->htt.tid;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt data tx using tid %hhu\n", tid);
+
+       flags0  = 0;
+       if (!ieee80211_has_protected(hdr->frame_control))
+               flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
+       flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
+       flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
+                    HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
+
+       flags1  = 0;
+       flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
+       flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
+
+       frags_paddr = ATH10K_SKB_CB(txfrag)->paddr;
+
+       cmd->hdr.msg_type        = HTT_H2T_MSG_TYPE_TX_FRM;
+       cmd->data_tx.flags0      = flags0;
+       cmd->data_tx.flags1      = __cpu_to_le16(flags1);
+       cmd->data_tx.len         = __cpu_to_le16(msdu->len);
+       cmd->data_tx.id          = __cpu_to_le16(msdu_id);
+       cmd->data_tx.frags_paddr = __cpu_to_le32(frags_paddr);
+       cmd->data_tx.peerid      = __cpu_to_le32(HTT_INVALID_PEERID);
+
+       memcpy(cmd->data_tx.prefetch, msdu->data, prefetch_len);
+
+       /* refcount is decremented by HTC and HTT completions until it reaches
+        * zero and is freed */
+       skb_cb = ATH10K_SKB_CB(txdesc);
+       skb_cb->htt.msdu_id = msdu_id;
+       skb_cb->htt.refcount = 2;
+       skb_cb->htt.txfrag = txfrag;
+       skb_cb->htt.msdu = msdu;
+
+       res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+       if (res)
+               goto err;
+
+       return 0;
+err:
+       if (txfrag)
+               ath10k_skb_unmap(dev, txfrag);
+       if (txdesc)
+               dev_kfree_skb_any(txdesc);
+       if (txfrag)
+               dev_kfree_skb_any(txfrag);
+       if (msdu_id >= 0) {
+               spin_lock_bh(&htt->tx_lock);
+               htt->pending_tx[msdu_id] = NULL;
+               ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+               spin_unlock_bh(&htt->tx_lock);
+       }
+       ath10k_htt_tx_dec_pending(htt);
+       ath10k_skb_unmap(dev, msdu);
+       return res;
+}
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
new file mode 100644 (file)
index 0000000..44ed5af
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _HW_H_
+#define _HW_H_
+
+#include "targaddrs.h"
+
+/* Supported FW version */
+#define SUPPORTED_FW_MAJOR     1
+#define SUPPORTED_FW_MINOR     0
+#define SUPPORTED_FW_RELEASE   0
+#define SUPPORTED_FW_BUILD     629
+
+/* QCA988X 1.0 definitions */
+#define QCA988X_HW_1_0_VERSION         0x4000002c
+#define QCA988X_HW_1_0_FW_DIR          "ath10k/QCA988X/hw1.0"
+#define QCA988X_HW_1_0_FW_FILE         "firmware.bin"
+#define QCA988X_HW_1_0_OTP_FILE                "otp.bin"
+#define QCA988X_HW_1_0_BOARD_DATA_FILE "board.bin"
+#define QCA988X_HW_1_0_PATCH_LOAD_ADDR 0x1234
+
+/* QCA988X 2.0 definitions */
+#define QCA988X_HW_2_0_VERSION         0x4100016c
+#define QCA988X_HW_2_0_FW_DIR          "ath10k/QCA988X/hw2.0"
+#define QCA988X_HW_2_0_FW_FILE         "firmware.bin"
+#define QCA988X_HW_2_0_OTP_FILE                "otp.bin"
+#define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin"
+#define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234
+
+/* Known pecularities:
+ *  - current FW doesn't support raw rx mode (last tested v599)
+ *  - current FW dumps upon raw tx mode (last tested v599)
+ *  - raw appears in nwifi decap, raw and nwifi appear in ethernet decap
+ *  - raw have FCS, nwifi doesn't
+ *  - ethernet frames have 802.11 header decapped and parts (base hdr, cipher
+ *    param, llc/snap) are aligned to 4byte boundaries each */
+enum ath10k_hw_txrx_mode {
+       ATH10K_HW_TXRX_RAW = 0,
+       ATH10K_HW_TXRX_NATIVE_WIFI = 1,
+       ATH10K_HW_TXRX_ETHERNET = 2,
+};
+
+enum ath10k_mcast2ucast_mode {
+       ATH10K_MCAST2UCAST_DISABLED = 0,
+       ATH10K_MCAST2UCAST_ENABLED = 1,
+};
+
+#define TARGET_NUM_VDEVS                       8
+#define TARGET_NUM_PEER_AST                    2
+#define TARGET_NUM_WDS_ENTRIES                 32
+#define TARGET_DMA_BURST_SIZE                  0
+#define TARGET_MAC_AGGR_DELIM                  0
+#define TARGET_AST_SKID_LIMIT                  16
+#define TARGET_NUM_PEERS                       16
+#define TARGET_NUM_OFFLOAD_PEERS               0
+#define TARGET_NUM_OFFLOAD_REORDER_BUFS         0
+#define TARGET_NUM_PEER_KEYS                   2
+#define TARGET_NUM_TIDS                (2 * ((TARGET_NUM_PEERS) + (TARGET_NUM_VDEVS)))
+#define TARGET_TX_CHAIN_MASK                   (BIT(0) | BIT(1) | BIT(2))
+#define TARGET_RX_CHAIN_MASK                   (BIT(0) | BIT(1) | BIT(2))
+#define TARGET_RX_TIMEOUT_LO_PRI               100
+#define TARGET_RX_TIMEOUT_HI_PRI               40
+#define TARGET_RX_DECAP_MODE                   ATH10K_HW_TXRX_ETHERNET
+#define TARGET_SCAN_MAX_PENDING_REQS           4
+#define TARGET_BMISS_OFFLOAD_MAX_VDEV          3
+#define TARGET_ROAM_OFFLOAD_MAX_VDEV           3
+#define TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES    8
+#define TARGET_GTK_OFFLOAD_MAX_VDEV            3
+#define TARGET_NUM_MCAST_GROUPS                        0
+#define TARGET_NUM_MCAST_TABLE_ELEMS           0
+#define TARGET_MCAST2UCAST_MODE                        ATH10K_MCAST2UCAST_DISABLED
+#define TARGET_TX_DBG_LOG_SIZE                 1024
+#define TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 0
+#define TARGET_VOW_CONFIG                      0
+#define TARGET_NUM_MSDU_DESC                   (1024 + 400)
+#define TARGET_MAX_FRAG_ENTRIES                        0
+
+
+/* Number of Copy Engines supported */
+#define CE_COUNT 8
+
+/*
+ * Total number of PCIe MSI interrupts requested for all interrupt sources.
+ * PCIe standard forces this to be a power of 2.
+ * Some Host OS's limit MSI requests that can be granted to 8
+ * so for now we abide by this limit and avoid requesting more
+ * than that.
+ */
+#define MSI_NUM_REQUEST_LOG2   3
+#define MSI_NUM_REQUEST                (1<<MSI_NUM_REQUEST_LOG2)
+
+/*
+ * Granted MSIs are assigned as follows:
+ * Firmware uses the first
+ * Remaining MSIs, if any, are used by Copy Engines
+ * This mapping is known to both Target firmware and Host software.
+ * It may be changed as long as Host and Target are kept in sync.
+ */
+/* MSI for firmware (errors, etc.) */
+#define MSI_ASSIGN_FW          0
+
+/* MSIs for Copy Engines */
+#define MSI_ASSIGN_CE_INITIAL  1
+#define MSI_ASSIGN_CE_MAX      7
+
+/* as of IP3.7.1 */
+#define RTC_STATE_V_ON                         3
+
+#define RTC_STATE_COLD_RESET_MASK              0x00000400
+#define RTC_STATE_V_LSB                                0
+#define RTC_STATE_V_MASK                       0x00000007
+#define RTC_STATE_ADDRESS                      0x0000
+#define PCIE_SOC_WAKE_V_MASK                   0x00000001
+#define PCIE_SOC_WAKE_ADDRESS                  0x0004
+#define PCIE_SOC_WAKE_RESET                    0x00000000
+#define SOC_GLOBAL_RESET_ADDRESS               0x0008
+
+#define RTC_SOC_BASE_ADDRESS                   0x00004000
+#define RTC_WMAC_BASE_ADDRESS                  0x00005000
+#define MAC_COEX_BASE_ADDRESS                  0x00006000
+#define BT_COEX_BASE_ADDRESS                   0x00007000
+#define SOC_PCIE_BASE_ADDRESS                  0x00008000
+#define SOC_CORE_BASE_ADDRESS                  0x00009000
+#define WLAN_UART_BASE_ADDRESS                 0x0000c000
+#define WLAN_SI_BASE_ADDRESS                   0x00010000
+#define WLAN_GPIO_BASE_ADDRESS                 0x00014000
+#define WLAN_ANALOG_INTF_BASE_ADDRESS          0x0001c000
+#define WLAN_MAC_BASE_ADDRESS                  0x00020000
+#define EFUSE_BASE_ADDRESS                     0x00030000
+#define FPGA_REG_BASE_ADDRESS                  0x00039000
+#define WLAN_UART2_BASE_ADDRESS                        0x00054c00
+#define CE_WRAPPER_BASE_ADDRESS                        0x00057000
+#define CE0_BASE_ADDRESS                       0x00057400
+#define CE1_BASE_ADDRESS                       0x00057800
+#define CE2_BASE_ADDRESS                       0x00057c00
+#define CE3_BASE_ADDRESS                       0x00058000
+#define CE4_BASE_ADDRESS                       0x00058400
+#define CE5_BASE_ADDRESS                       0x00058800
+#define CE6_BASE_ADDRESS                       0x00058c00
+#define CE7_BASE_ADDRESS                       0x00059000
+#define DBI_BASE_ADDRESS                       0x00060000
+#define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS     0x0006c000
+#define PCIE_LOCAL_BASE_ADDRESS                        0x00080000
+
+#define SOC_RESET_CONTROL_OFFSET               0x00000000
+#define SOC_RESET_CONTROL_SI0_RST_MASK         0x00000001
+#define SOC_CPU_CLOCK_OFFSET                   0x00000020
+#define SOC_CPU_CLOCK_STANDARD_LSB             0
+#define SOC_CPU_CLOCK_STANDARD_MASK            0x00000003
+#define SOC_CLOCK_CONTROL_OFFSET               0x00000028
+#define SOC_CLOCK_CONTROL_SI0_CLK_MASK         0x00000001
+#define SOC_SYSTEM_SLEEP_OFFSET                        0x000000c4
+#define SOC_LPO_CAL_OFFSET                     0x000000e0
+#define SOC_LPO_CAL_ENABLE_LSB                 20
+#define SOC_LPO_CAL_ENABLE_MASK                        0x00100000
+
+#define WLAN_RESET_CONTROL_COLD_RST_MASK       0x00000008
+#define WLAN_RESET_CONTROL_WARM_RST_MASK       0x00000004
+#define WLAN_SYSTEM_SLEEP_DISABLE_LSB          0
+#define WLAN_SYSTEM_SLEEP_DISABLE_MASK         0x00000001
+
+#define WLAN_GPIO_PIN0_ADDRESS                 0x00000028
+#define WLAN_GPIO_PIN0_CONFIG_MASK             0x00007800
+#define WLAN_GPIO_PIN1_ADDRESS                 0x0000002c
+#define WLAN_GPIO_PIN1_CONFIG_MASK             0x00007800
+#define WLAN_GPIO_PIN10_ADDRESS                        0x00000050
+#define WLAN_GPIO_PIN11_ADDRESS                        0x00000054
+#define WLAN_GPIO_PIN12_ADDRESS                        0x00000058
+#define WLAN_GPIO_PIN13_ADDRESS                        0x0000005c
+
+#define CLOCK_GPIO_OFFSET                      0xffffffff
+#define CLOCK_GPIO_BT_CLK_OUT_EN_LSB           0
+#define CLOCK_GPIO_BT_CLK_OUT_EN_MASK          0
+
+#define SI_CONFIG_OFFSET                       0x00000000
+#define SI_CONFIG_BIDIR_OD_DATA_LSB            18
+#define SI_CONFIG_BIDIR_OD_DATA_MASK           0x00040000
+#define SI_CONFIG_I2C_LSB                      16
+#define SI_CONFIG_I2C_MASK                     0x00010000
+#define SI_CONFIG_POS_SAMPLE_LSB               7
+#define SI_CONFIG_POS_SAMPLE_MASK              0x00000080
+#define SI_CONFIG_INACTIVE_DATA_LSB            5
+#define SI_CONFIG_INACTIVE_DATA_MASK           0x00000020
+#define SI_CONFIG_INACTIVE_CLK_LSB             4
+#define SI_CONFIG_INACTIVE_CLK_MASK            0x00000010
+#define SI_CONFIG_DIVIDER_LSB                  0
+#define SI_CONFIG_DIVIDER_MASK                 0x0000000f
+#define SI_CS_OFFSET                           0x00000004
+#define SI_CS_DONE_ERR_MASK                    0x00000400
+#define SI_CS_DONE_INT_MASK                    0x00000200
+#define SI_CS_START_LSB                                8
+#define SI_CS_START_MASK                       0x00000100
+#define SI_CS_RX_CNT_LSB                       4
+#define SI_CS_RX_CNT_MASK                      0x000000f0
+#define SI_CS_TX_CNT_LSB                       0
+#define SI_CS_TX_CNT_MASK                      0x0000000f
+
+#define SI_TX_DATA0_OFFSET                     0x00000008
+#define SI_TX_DATA1_OFFSET                     0x0000000c
+#define SI_RX_DATA0_OFFSET                     0x00000010
+#define SI_RX_DATA1_OFFSET                     0x00000014
+
+#define CORE_CTRL_CPU_INTR_MASK                        0x00002000
+#define CORE_CTRL_ADDRESS                      0x0000
+#define PCIE_INTR_ENABLE_ADDRESS               0x0008
+#define PCIE_INTR_CLR_ADDRESS                  0x0014
+#define SCRATCH_3_ADDRESS                      0x0030
+
+/* Firmware indications to the Host via SCRATCH_3 register. */
+#define FW_INDICATOR_ADDRESS   (SOC_CORE_BASE_ADDRESS + SCRATCH_3_ADDRESS)
+#define FW_IND_EVENT_PENDING                   1
+#define FW_IND_INITIALIZED                     2
+
+/* HOST_REG interrupt from firmware */
+#define PCIE_INTR_FIRMWARE_MASK                        0x00000400
+#define PCIE_INTR_CE_MASK_ALL                  0x0007f800
+
+#define DRAM_BASE_ADDRESS                      0x00400000
+
+#define MISSING 0
+
+#define SYSTEM_SLEEP_OFFSET                    SOC_SYSTEM_SLEEP_OFFSET
+#define WLAN_SYSTEM_SLEEP_OFFSET               SOC_SYSTEM_SLEEP_OFFSET
+#define WLAN_RESET_CONTROL_OFFSET              SOC_RESET_CONTROL_OFFSET
+#define CLOCK_CONTROL_OFFSET                   SOC_CLOCK_CONTROL_OFFSET
+#define CLOCK_CONTROL_SI0_CLK_MASK             SOC_CLOCK_CONTROL_SI0_CLK_MASK
+#define RESET_CONTROL_MBOX_RST_MASK            MISSING
+#define RESET_CONTROL_SI0_RST_MASK             SOC_RESET_CONTROL_SI0_RST_MASK
+#define GPIO_BASE_ADDRESS                      WLAN_GPIO_BASE_ADDRESS
+#define GPIO_PIN0_OFFSET                       WLAN_GPIO_PIN0_ADDRESS
+#define GPIO_PIN1_OFFSET                       WLAN_GPIO_PIN1_ADDRESS
+#define GPIO_PIN0_CONFIG_MASK                  WLAN_GPIO_PIN0_CONFIG_MASK
+#define GPIO_PIN1_CONFIG_MASK                  WLAN_GPIO_PIN1_CONFIG_MASK
+#define SI_BASE_ADDRESS                                WLAN_SI_BASE_ADDRESS
+#define SCRATCH_BASE_ADDRESS                   SOC_CORE_BASE_ADDRESS
+#define LOCAL_SCRATCH_OFFSET                   0x18
+#define CPU_CLOCK_OFFSET                       SOC_CPU_CLOCK_OFFSET
+#define LPO_CAL_OFFSET                         SOC_LPO_CAL_OFFSET
+#define GPIO_PIN10_OFFSET                      WLAN_GPIO_PIN10_ADDRESS
+#define GPIO_PIN11_OFFSET                      WLAN_GPIO_PIN11_ADDRESS
+#define GPIO_PIN12_OFFSET                      WLAN_GPIO_PIN12_ADDRESS
+#define GPIO_PIN13_OFFSET                      WLAN_GPIO_PIN13_ADDRESS
+#define CPU_CLOCK_STANDARD_LSB                 SOC_CPU_CLOCK_STANDARD_LSB
+#define CPU_CLOCK_STANDARD_MASK                        SOC_CPU_CLOCK_STANDARD_MASK
+#define LPO_CAL_ENABLE_LSB                     SOC_LPO_CAL_ENABLE_LSB
+#define LPO_CAL_ENABLE_MASK                    SOC_LPO_CAL_ENABLE_MASK
+#define ANALOG_INTF_BASE_ADDRESS               WLAN_ANALOG_INTF_BASE_ADDRESS
+#define MBOX_BASE_ADDRESS                      MISSING
+#define INT_STATUS_ENABLE_ERROR_LSB            MISSING
+#define INT_STATUS_ENABLE_ERROR_MASK           MISSING
+#define INT_STATUS_ENABLE_CPU_LSB              MISSING
+#define INT_STATUS_ENABLE_CPU_MASK             MISSING
+#define INT_STATUS_ENABLE_COUNTER_LSB          MISSING
+#define INT_STATUS_ENABLE_COUNTER_MASK         MISSING
+#define INT_STATUS_ENABLE_MBOX_DATA_LSB                MISSING
+#define INT_STATUS_ENABLE_MBOX_DATA_MASK       MISSING
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_LSB   MISSING
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK  MISSING
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_LSB    MISSING
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK   MISSING
+#define COUNTER_INT_STATUS_ENABLE_BIT_LSB      MISSING
+#define COUNTER_INT_STATUS_ENABLE_BIT_MASK     MISSING
+#define INT_STATUS_ENABLE_ADDRESS              MISSING
+#define CPU_INT_STATUS_ENABLE_BIT_LSB          MISSING
+#define CPU_INT_STATUS_ENABLE_BIT_MASK         MISSING
+#define HOST_INT_STATUS_ADDRESS                        MISSING
+#define CPU_INT_STATUS_ADDRESS                 MISSING
+#define ERROR_INT_STATUS_ADDRESS               MISSING
+#define ERROR_INT_STATUS_WAKEUP_MASK           MISSING
+#define ERROR_INT_STATUS_WAKEUP_LSB            MISSING
+#define ERROR_INT_STATUS_RX_UNDERFLOW_MASK     MISSING
+#define ERROR_INT_STATUS_RX_UNDERFLOW_LSB      MISSING
+#define ERROR_INT_STATUS_TX_OVERFLOW_MASK      MISSING
+#define ERROR_INT_STATUS_TX_OVERFLOW_LSB       MISSING
+#define COUNT_DEC_ADDRESS                      MISSING
+#define HOST_INT_STATUS_CPU_MASK               MISSING
+#define HOST_INT_STATUS_CPU_LSB                        MISSING
+#define HOST_INT_STATUS_ERROR_MASK             MISSING
+#define HOST_INT_STATUS_ERROR_LSB              MISSING
+#define HOST_INT_STATUS_COUNTER_MASK           MISSING
+#define HOST_INT_STATUS_COUNTER_LSB            MISSING
+#define RX_LOOKAHEAD_VALID_ADDRESS             MISSING
+#define WINDOW_DATA_ADDRESS                    MISSING
+#define WINDOW_READ_ADDR_ADDRESS               MISSING
+#define WINDOW_WRITE_ADDR_ADDRESS              MISSING
+
+#define RTC_STATE_V_GET(x) (((x) & RTC_STATE_V_MASK) >> RTC_STATE_V_LSB)
+
+#endif /* _HW_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
new file mode 100644 (file)
index 0000000..da5c333
--- /dev/null
@@ -0,0 +1,3069 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "mac.h"
+
+#include <net/mac80211.h>
+#include <linux/etherdevice.h>
+
+#include "core.h"
+#include "debug.h"
+#include "wmi.h"
+#include "htt.h"
+#include "txrx.h"
+
+/**********/
+/* Crypto */
+/**********/
+
+static int ath10k_send_key(struct ath10k_vif *arvif,
+                          struct ieee80211_key_conf *key,
+                          enum set_key_cmd cmd,
+                          const u8 *macaddr)
+{
+       struct wmi_vdev_install_key_arg arg = {
+               .vdev_id = arvif->vdev_id,
+               .key_idx = key->keyidx,
+               .key_len = key->keylen,
+               .key_data = key->key,
+               .macaddr = macaddr,
+       };
+
+       if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+               arg.key_flags = WMI_KEY_PAIRWISE;
+       else
+               arg.key_flags = WMI_KEY_GROUP;
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_CCMP:
+               arg.key_cipher = WMI_CIPHER_AES_CCM;
+               key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               arg.key_cipher = WMI_CIPHER_TKIP;
+               arg.key_txmic_len = 8;
+               arg.key_rxmic_len = 8;
+               break;
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               arg.key_cipher = WMI_CIPHER_WEP;
+               /* AP/IBSS mode requires self-key to be groupwise
+                * Otherwise pairwise key must be set */
+               if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN))
+                       arg.key_flags = WMI_KEY_PAIRWISE;
+               break;
+       default:
+               ath10k_warn("cipher %d is not supported\n", key->cipher);
+               return -EOPNOTSUPP;
+       }
+
+       if (cmd == DISABLE_KEY) {
+               arg.key_cipher = WMI_CIPHER_NONE;
+               arg.key_data = NULL;
+       }
+
+       return ath10k_wmi_vdev_install_key(arvif->ar, &arg);
+}
+
+static int ath10k_install_key(struct ath10k_vif *arvif,
+                             struct ieee80211_key_conf *key,
+                             enum set_key_cmd cmd,
+                             const u8 *macaddr)
+{
+       struct ath10k *ar = arvif->ar;
+       int ret;
+
+       INIT_COMPLETION(ar->install_key_done);
+
+       ret = ath10k_send_key(arvif, key, cmd, macaddr);
+       if (ret)
+               return ret;
+
+       ret = wait_for_completion_timeout(&ar->install_key_done, 3*HZ);
+       if (ret == 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
+                                       const u8 *addr)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ath10k_peer *peer;
+       int ret;
+       int i;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!peer)
+               return -ENOENT;
+
+       for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
+               if (arvif->wep_keys[i] == NULL)
+                       continue;
+
+               ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY,
+                                        addr);
+               if (ret)
+                       return ret;
+
+               peer->keys[i] = arvif->wep_keys[i];
+       }
+
+       return 0;
+}
+
+static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
+                                 const u8 *addr)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ath10k_peer *peer;
+       int first_errno = 0;
+       int ret;
+       int i;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!peer)
+               return -ENOENT;
+
+       for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+               if (peer->keys[i] == NULL)
+                       continue;
+
+               ret = ath10k_install_key(arvif, peer->keys[i],
+                                        DISABLE_KEY, addr);
+               if (ret && first_errno == 0)
+                       first_errno = ret;
+
+               if (ret)
+                       ath10k_warn("could not remove peer wep key %d (%d)\n",
+                                   i, ret);
+
+               peer->keys[i] = NULL;
+       }
+
+       return first_errno;
+}
+
+static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
+                                struct ieee80211_key_conf *key)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ath10k_peer *peer;
+       u8 addr[ETH_ALEN];
+       int first_errno = 0;
+       int ret;
+       int i;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       for (;;) {
+               /* since ath10k_install_key we can't hold data_lock all the
+                * time, so we try to remove the keys incrementally */
+               spin_lock_bh(&ar->data_lock);
+               i = 0;
+               list_for_each_entry(peer, &ar->peers, list) {
+                       for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+                               if (peer->keys[i] == key) {
+                                       memcpy(addr, peer->addr, ETH_ALEN);
+                                       peer->keys[i] = NULL;
+                                       break;
+                               }
+                       }
+
+                       if (i < ARRAY_SIZE(peer->keys))
+                               break;
+               }
+               spin_unlock_bh(&ar->data_lock);
+
+               if (i == ARRAY_SIZE(peer->keys))
+                       break;
+
+               ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr);
+               if (ret && first_errno == 0)
+                       first_errno = ret;
+
+               if (ret)
+                       ath10k_warn("could not remove key for %pM\n", addr);
+       }
+
+       return first_errno;
+}
+
+
+/*********************/
+/* General utilities */
+/*********************/
+
+static inline enum wmi_phy_mode
+chan_to_phymode(const struct cfg80211_chan_def *chandef)
+{
+       enum wmi_phy_mode phymode = MODE_UNKNOWN;
+
+       switch (chandef->chan->band) {
+       case IEEE80211_BAND_2GHZ:
+               switch (chandef->width) {
+               case NL80211_CHAN_WIDTH_20_NOHT:
+                       phymode = MODE_11G;
+                       break;
+               case NL80211_CHAN_WIDTH_20:
+                       phymode = MODE_11NG_HT20;
+                       break;
+               case NL80211_CHAN_WIDTH_40:
+                       phymode = MODE_11NG_HT40;
+                       break;
+               case NL80211_CHAN_WIDTH_5:
+               case NL80211_CHAN_WIDTH_10:
+               case NL80211_CHAN_WIDTH_80:
+               case NL80211_CHAN_WIDTH_80P80:
+               case NL80211_CHAN_WIDTH_160:
+                       phymode = MODE_UNKNOWN;
+                       break;
+               }
+               break;
+       case IEEE80211_BAND_5GHZ:
+               switch (chandef->width) {
+               case NL80211_CHAN_WIDTH_20_NOHT:
+                       phymode = MODE_11A;
+                       break;
+               case NL80211_CHAN_WIDTH_20:
+                       phymode = MODE_11NA_HT20;
+                       break;
+               case NL80211_CHAN_WIDTH_40:
+                       phymode = MODE_11NA_HT40;
+                       break;
+               case NL80211_CHAN_WIDTH_80:
+                       phymode = MODE_11AC_VHT80;
+                       break;
+               case NL80211_CHAN_WIDTH_5:
+               case NL80211_CHAN_WIDTH_10:
+               case NL80211_CHAN_WIDTH_80P80:
+               case NL80211_CHAN_WIDTH_160:
+                       phymode = MODE_UNKNOWN;
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+
+       WARN_ON(phymode == MODE_UNKNOWN);
+       return phymode;
+}
+
+static u8 ath10k_parse_mpdudensity(u8 mpdudensity)
+{
+/*
+ * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
+ *   0 for no restriction
+ *   1 for 1/4 us
+ *   2 for 1/2 us
+ *   3 for 1 us
+ *   4 for 2 us
+ *   5 for 4 us
+ *   6 for 8 us
+ *   7 for 16 us
+ */
+       switch (mpdudensity) {
+       case 0:
+               return 0;
+       case 1:
+       case 2:
+       case 3:
+       /* Our lower layer calculations limit our precision to
+          1 microsecond */
+               return 1;
+       case 4:
+               return 2;
+       case 5:
+               return 4;
+       case 6:
+               return 8;
+       case 7:
+               return 16;
+       default:
+               return 0;
+       }
+}
+
+static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ret = ath10k_wmi_peer_delete(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
+{
+       struct ath10k_peer *peer, *tmp;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+               if (peer->vdev_id != vdev_id)
+                       continue;
+
+               ath10k_warn("removing stale peer %pM from vdev_id %d\n",
+                           peer->addr, vdev_id);
+
+               list_del(&peer->list);
+               kfree(peer);
+       }
+       spin_unlock_bh(&ar->data_lock);
+}
+
+/************************/
+/* Interface management */
+/************************/
+
+static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
+{
+       int ret;
+
+       ret = wait_for_completion_timeout(&ar->vdev_setup_done,
+                                         ATH10K_VDEV_SETUP_TIMEOUT_HZ);
+       if (ret == 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ath10k_vdev_start(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_conf *conf = &ar->hw->conf;
+       struct ieee80211_channel *channel = conf->chandef.chan;
+       struct wmi_vdev_start_request_arg arg = {};
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       INIT_COMPLETION(ar->vdev_setup_done);
+
+       arg.vdev_id = arvif->vdev_id;
+       arg.dtim_period = arvif->dtim_period;
+       arg.bcn_intval = arvif->beacon_interval;
+
+       arg.channel.freq = channel->center_freq;
+
+       arg.channel.band_center_freq1 = conf->chandef.center_freq1;
+
+       arg.channel.mode = chan_to_phymode(&conf->chandef);
+
+       arg.channel.min_power = channel->max_power * 3;
+       arg.channel.max_power = channel->max_power * 4;
+       arg.channel.max_reg_power = channel->max_reg_power * 4;
+       arg.channel.max_antenna_gain = channel->max_antenna_gain;
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               arg.ssid = arvif->u.ap.ssid;
+               arg.ssid_len = arvif->u.ap.ssid_len;
+               arg.hidden_ssid = arvif->u.ap.hidden_ssid;
+       } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
+               arg.ssid = arvif->vif->bss_conf.ssid;
+               arg.ssid_len = arvif->vif->bss_conf.ssid_len;
+       }
+
+       ret = ath10k_wmi_vdev_start(ar, &arg);
+       if (ret) {
+               ath10k_warn("WMI vdev start failed: ret %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("vdev setup failed %d\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int ath10k_vdev_stop(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       INIT_COMPLETION(ar->vdev_setup_done);
+
+       ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
+       if (ret) {
+               ath10k_warn("WMI vdev stop failed: ret %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("vdev setup failed %d\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
+{
+       struct ieee80211_channel *channel = ar->hw->conf.chandef.chan;
+       struct wmi_vdev_start_request_arg arg = {};
+       enum nl80211_channel_type type;
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       type = cfg80211_get_chandef_type(&ar->hw->conf.chandef);
+
+       arg.vdev_id = vdev_id;
+       arg.channel.freq = channel->center_freq;
+       arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1;
+
+       /* TODO setup this dynamically, what in case we
+          don't have any vifs? */
+       arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef);
+
+       arg.channel.min_power = channel->max_power * 3;
+       arg.channel.max_power = channel->max_power * 4;
+       arg.channel.max_reg_power = channel->max_reg_power * 4;
+       arg.channel.max_antenna_gain = channel->max_antenna_gain;
+
+       ret = ath10k_wmi_vdev_start(ar, &arg);
+       if (ret) {
+               ath10k_warn("Monitor vdev start failed: ret %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("Monitor vdev setup failed %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr);
+       if (ret) {
+               ath10k_warn("Monitor vdev up failed: %d\n", ret);
+               goto vdev_stop;
+       }
+
+       ar->monitor_vdev_id = vdev_id;
+       ar->monitor_enabled = true;
+
+       return 0;
+
+vdev_stop:
+       ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
+       if (ret)
+               ath10k_warn("Monitor vdev stop failed: %d\n", ret);
+
+       return ret;
+}
+
+static int ath10k_monitor_stop(struct ath10k *ar)
+{
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       /* For some reasons, ath10k_wmi_vdev_down() here couse
+        * often ath10k_wmi_vdev_stop() to fail. Next we could
+        * not run monitor vdev and driver reload
+        * required. Don't see such problems we skip
+        * ath10k_wmi_vdev_down() here.
+        */
+
+       ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
+       if (ret)
+               ath10k_warn("Monitor vdev stop failed: %d\n", ret);
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret)
+               ath10k_warn("Monitor_down sync failed: %d\n", ret);
+
+       ar->monitor_enabled = false;
+       return ret;
+}
+
+static int ath10k_monitor_create(struct ath10k *ar)
+{
+       int bit, ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (ar->monitor_present) {
+               ath10k_warn("Monitor mode already enabled\n");
+               return 0;
+       }
+
+       bit = ffs(ar->free_vdev_map);
+       if (bit == 0) {
+               ath10k_warn("No free VDEV slots\n");
+               return -ENOMEM;
+       }
+
+       ar->monitor_vdev_id = bit - 1;
+       ar->free_vdev_map &= ~(1 << ar->monitor_vdev_id);
+
+       ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id,
+                                    WMI_VDEV_TYPE_MONITOR,
+                                    0, ar->mac_addr);
+       if (ret) {
+               ath10k_warn("WMI vdev monitor create failed: ret %d\n", ret);
+               goto vdev_fail;
+       }
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n",
+                  ar->monitor_vdev_id);
+
+       ar->monitor_present = true;
+       return 0;
+
+vdev_fail:
+       /*
+        * Restore the ID to the global map.
+        */
+       ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
+       return ret;
+}
+
+static int ath10k_monitor_destroy(struct ath10k *ar)
+{
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (!ar->monitor_present)
+               return 0;
+
+       ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
+       if (ret) {
+               ath10k_warn("WMI vdev monitor delete failed: %d\n", ret);
+               return ret;
+       }
+
+       ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
+       ar->monitor_present = false;
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n",
+                  ar->monitor_vdev_id);
+       return ret;
+}
+
+static void ath10k_control_beaconing(struct ath10k_vif *arvif,
+                               struct ieee80211_bss_conf *info)
+{
+       int ret = 0;
+
+       if (!info->enable_beacon) {
+               ath10k_vdev_stop(arvif);
+               return;
+       }
+
+       arvif->tx_seq_no = 0x1000;
+
+       ret = ath10k_vdev_start(arvif);
+       if (ret)
+               return;
+
+       ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, 0, info->bssid);
+       if (ret) {
+               ath10k_warn("Failed to bring up VDEV: %d\n",
+                           arvif->vdev_id);
+               return;
+       }
+       ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id);
+}
+
+static void ath10k_control_ibss(struct ath10k_vif *arvif,
+                               struct ieee80211_bss_conf *info,
+                               const u8 self_peer[ETH_ALEN])
+{
+       int ret = 0;
+
+       if (!info->ibss_joined) {
+               ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
+               if (ret)
+                       ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n",
+                                   self_peer, arvif->vdev_id, ret);
+
+               if (is_zero_ether_addr(arvif->u.ibss.bssid))
+                       return;
+
+               ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
+                                        arvif->u.ibss.bssid);
+               if (ret) {
+                       ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n",
+                                   arvif->u.ibss.bssid, arvif->vdev_id, ret);
+                       return;
+               }
+
+               memset(arvif->u.ibss.bssid, 0, ETH_ALEN);
+
+               return;
+       }
+
+       ret = ath10k_peer_create(arvif->ar, arvif->vdev_id, self_peer);
+       if (ret) {
+               ath10k_warn("Failed to create IBSS self peer:%pM for VDEV:%d ret:%d\n",
+                           self_peer, arvif->vdev_id, ret);
+               return;
+       }
+
+       ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_ATIM_WINDOW,
+                                       ATH10K_DEFAULT_ATIM);
+       if (ret)
+               ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n",
+                           arvif->vdev_id, ret);
+}
+
+/*
+ * Review this when mac80211 gains per-interface powersave support.
+ */
+static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct ath10k_generic_iter *ar_iter = data;
+       struct ieee80211_conf *conf = &ar_iter->ar->hw->conf;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       enum wmi_sta_powersave_param param;
+       enum wmi_sta_ps_mode psmode;
+       int ret;
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       if (conf->flags & IEEE80211_CONF_PS) {
+               psmode = WMI_STA_PS_MODE_ENABLED;
+               param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
+
+               ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar,
+                                                 arvif->vdev_id,
+                                                 param,
+                                                 conf->dynamic_ps_timeout);
+               if (ret) {
+                       ath10k_warn("Failed to set inactivity time for VDEV: %d\n",
+                                   arvif->vdev_id);
+                       return;
+               }
+
+               ar_iter->ret = ret;
+       } else {
+               psmode = WMI_STA_PS_MODE_DISABLED;
+       }
+
+       ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id,
+                                            psmode);
+       if (ar_iter->ret)
+               ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n",
+                           psmode, arvif->vdev_id);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n",
+                          psmode, arvif->vdev_id);
+}
+
+/**********************/
+/* Station management */
+/**********************/
+
+static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
+                                     struct ath10k_vif *arvif,
+                                     struct ieee80211_sta *sta,
+                                     struct ieee80211_bss_conf *bss_conf,
+                                     struct wmi_peer_assoc_complete_arg *arg)
+{
+       memcpy(arg->addr, sta->addr, ETH_ALEN);
+       arg->vdev_id = arvif->vdev_id;
+       arg->peer_aid = sta->aid;
+       arg->peer_flags |= WMI_PEER_AUTH;
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
+               /*
+                * Seems FW have problems with Power Save in STA
+                * mode when we setup this parameter to high (eg. 5).
+                * Often we see that FW don't send NULL (with clean P flags)
+                * frame even there is info about buffered frames in beacons.
+                * Sometimes we have to wait more than 10 seconds before FW
+                * will wakeup. Often sending one ping from AP to our device
+                * just fail (more than 50%).
+                *
+                * Seems setting this FW parameter to 1 couse FW
+                * will check every beacon and will wakup immediately
+                * after detection buffered data.
+                */
+               arg->peer_listen_intval = 1;
+       else
+               arg->peer_listen_intval = ar->hw->conf.listen_interval;
+
+       arg->peer_num_spatial_streams = 1;
+
+       /*
+        * The assoc capabilities are available only in managed mode.
+        */
+       if (arvif->vdev_type == WMI_VDEV_TYPE_STA && bss_conf)
+               arg->peer_caps = bss_conf->assoc_capability;
+}
+
+static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
+                                      struct ath10k_vif *arvif,
+                                      struct wmi_peer_assoc_complete_arg *arg)
+{
+       struct ieee80211_vif *vif = arvif->vif;
+       struct ieee80211_bss_conf *info = &vif->bss_conf;
+       struct cfg80211_bss *bss;
+       const u8 *rsnie = NULL;
+       const u8 *wpaie = NULL;
+
+       bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
+                              info->bssid, NULL, 0, 0, 0);
+       if (bss) {
+               const struct cfg80211_bss_ies *ies;
+
+               rcu_read_lock();
+               rsnie = ieee80211_bss_get_ie(bss, WLAN_EID_RSN);
+
+               ies = rcu_dereference(bss->ies);
+
+               wpaie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+                               WLAN_OUI_TYPE_MICROSOFT_WPA,
+                               ies->data,
+                               ies->len);
+               rcu_read_unlock();
+               cfg80211_put_bss(ar->hw->wiphy, bss);
+       }
+
+       /* FIXME: base on RSN IE/WPA IE is a correct idea? */
+       if (rsnie || wpaie) {
+               ath10k_dbg(ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__);
+               arg->peer_flags |= WMI_PEER_NEED_PTK_4_WAY;
+       }
+
+       if (wpaie) {
+               ath10k_dbg(ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__);
+               arg->peer_flags |= WMI_PEER_NEED_GTK_2_WAY;
+       }
+}
+
+static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
+                                     struct ieee80211_sta *sta,
+                                     struct wmi_peer_assoc_complete_arg *arg)
+{
+       struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates;
+       const struct ieee80211_supported_band *sband;
+       const struct ieee80211_rate *rates;
+       u32 ratemask;
+       int i;
+
+       sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
+       ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
+       rates = sband->bitrates;
+
+       rateset->num_rates = 0;
+
+       for (i = 0; i < 32; i++, ratemask >>= 1, rates++) {
+               if (!(ratemask & 1))
+                       continue;
+
+               rateset->rates[rateset->num_rates] = rates->hw_value;
+               rateset->num_rates++;
+       }
+}
+
+static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
+                                  struct ieee80211_sta *sta,
+                                  struct wmi_peer_assoc_complete_arg *arg)
+{
+       const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       int smps;
+       int i, n;
+
+       if (!ht_cap->ht_supported)
+               return;
+
+       arg->peer_flags |= WMI_PEER_HT;
+       arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+                                   ht_cap->ampdu_factor)) - 1;
+
+       arg->peer_mpdu_density =
+               ath10k_parse_mpdudensity(ht_cap->ampdu_density);
+
+       arg->peer_ht_caps = ht_cap->cap;
+       arg->peer_rate_caps |= WMI_RC_HT_FLAG;
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)
+               arg->peer_flags |= WMI_PEER_LDPC;
+
+       if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
+               arg->peer_flags |= WMI_PEER_40MHZ;
+               arg->peer_rate_caps |= WMI_RC_CW40_FLAG;
+       }
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
+               arg->peer_rate_caps |= WMI_RC_SGI_FLAG;
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
+               arg->peer_rate_caps |= WMI_RC_SGI_FLAG;
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) {
+               arg->peer_rate_caps |= WMI_RC_TX_STBC_FLAG;
+               arg->peer_flags |= WMI_PEER_STBC;
+       }
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) {
+               u32 stbc;
+               stbc = ht_cap->cap & IEEE80211_HT_CAP_RX_STBC;
+               stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT;
+               stbc = stbc << WMI_RC_RX_STBC_FLAG_S;
+               arg->peer_rate_caps |= stbc;
+               arg->peer_flags |= WMI_PEER_STBC;
+       }
+
+       smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS;
+       smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+       if (smps == WLAN_HT_CAP_SM_PS_STATIC) {
+               arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
+               arg->peer_flags |= WMI_PEER_STATIC_MIMOPS;
+       } else if (smps == WLAN_HT_CAP_SM_PS_DYNAMIC) {
+               arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
+               arg->peer_flags |= WMI_PEER_DYN_MIMOPS;
+       }
+
+       if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
+               arg->peer_rate_caps |= WMI_RC_TS_FLAG;
+       else if (ht_cap->mcs.rx_mask[1])
+               arg->peer_rate_caps |= WMI_RC_DS_FLAG;
+
+       for (i = 0, n = 0; i < IEEE80211_HT_MCS_MASK_LEN*8; i++)
+               if (ht_cap->mcs.rx_mask[i/8] & (1 << i%8))
+                       arg->peer_ht_rates.rates[n++] = i;
+
+       arg->peer_ht_rates.num_rates = n;
+       arg->peer_num_spatial_streams = max((n+7) / 8, 1);
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n",
+                  arg->peer_ht_rates.num_rates,
+                  arg->peer_num_spatial_streams);
+}
+
+static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
+                                      struct ath10k_vif *arvif,
+                                      struct ieee80211_sta *sta,
+                                      struct ieee80211_bss_conf *bss_conf,
+                                      struct wmi_peer_assoc_complete_arg *arg)
+{
+       u32 uapsd = 0;
+       u32 max_sp = 0;
+
+       if (sta->wme)
+               arg->peer_flags |= WMI_PEER_QOS;
+
+       if (sta->wme && sta->uapsd_queues) {
+               ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n",
+                          sta->uapsd_queues, sta->max_sp);
+
+               arg->peer_flags |= WMI_PEER_APSD;
+               arg->peer_flags |= WMI_RC_UAPSD_FLAG;
+
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+                       uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC3_TRIGGER_EN;
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
+                       uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC2_TRIGGER_EN;
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
+                       uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC1_TRIGGER_EN;
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
+                       uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC0_TRIGGER_EN;
+
+
+               if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP)
+                       max_sp = sta->max_sp;
+
+               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+                                          sta->addr,
+                                          WMI_AP_PS_PEER_PARAM_UAPSD,
+                                          uapsd);
+
+               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+                                          sta->addr,
+                                          WMI_AP_PS_PEER_PARAM_MAX_SP,
+                                          max_sp);
+
+               /* TODO setup this based on STA listen interval and
+                  beacon interval. Currently we don't know
+                  sta->listen_interval - mac80211 patch required.
+                  Currently use 10 seconds */
+               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+                                          sta->addr,
+                                          WMI_AP_PS_PEER_PARAM_AGEOUT_TIME,
+                                          10);
+       }
+}
+
+static void ath10k_peer_assoc_h_qos_sta(struct ath10k *ar,
+                                       struct ath10k_vif *arvif,
+                                       struct ieee80211_sta *sta,
+                                       struct ieee80211_bss_conf *bss_conf,
+                                       struct wmi_peer_assoc_complete_arg *arg)
+{
+       if (bss_conf->qos)
+               arg->peer_flags |= WMI_PEER_QOS;
+}
+
+static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
+                                   struct ieee80211_sta *sta,
+                                   struct wmi_peer_assoc_complete_arg *arg)
+{
+       const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+
+       if (!vht_cap->vht_supported)
+               return;
+
+       arg->peer_flags |= WMI_PEER_VHT;
+
+       arg->peer_vht_caps = vht_cap->cap;
+
+       if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
+               arg->peer_flags |= WMI_PEER_80MHZ;
+
+       arg->peer_vht_rates.rx_max_rate =
+               __le16_to_cpu(vht_cap->vht_mcs.rx_highest);
+       arg->peer_vht_rates.rx_mcs_set =
+               __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
+       arg->peer_vht_rates.tx_max_rate =
+               __le16_to_cpu(vht_cap->vht_mcs.tx_highest);
+       arg->peer_vht_rates.tx_mcs_set =
+               __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n");
+}
+
+static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
+                                   struct ath10k_vif *arvif,
+                                   struct ieee80211_sta *sta,
+                                   struct ieee80211_bss_conf *bss_conf,
+                                   struct wmi_peer_assoc_complete_arg *arg)
+{
+       switch (arvif->vdev_type) {
+       case WMI_VDEV_TYPE_AP:
+               ath10k_peer_assoc_h_qos_ap(ar, arvif, sta, bss_conf, arg);
+               break;
+       case WMI_VDEV_TYPE_STA:
+               ath10k_peer_assoc_h_qos_sta(ar, arvif, sta, bss_conf, arg);
+               break;
+       default:
+               break;
+       }
+}
+
+static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
+                                       struct ath10k_vif *arvif,
+                                       struct ieee80211_sta *sta,
+                                       struct wmi_peer_assoc_complete_arg *arg)
+{
+       enum wmi_phy_mode phymode = MODE_UNKNOWN;
+
+       /* FIXME: add VHT */
+
+       switch (ar->hw->conf.chandef.chan->band) {
+       case IEEE80211_BAND_2GHZ:
+               if (sta->ht_cap.ht_supported) {
+                       if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+                               phymode = MODE_11NG_HT40;
+                       else
+                               phymode = MODE_11NG_HT20;
+               } else {
+                       phymode = MODE_11G;
+               }
+
+               break;
+       case IEEE80211_BAND_5GHZ:
+               if (sta->ht_cap.ht_supported) {
+                       if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+                               phymode = MODE_11NA_HT40;
+                       else
+                               phymode = MODE_11NA_HT20;
+               } else {
+                       phymode = MODE_11A;
+               }
+
+               break;
+       default:
+               break;
+       }
+
+       arg->peer_phymode = phymode;
+       WARN_ON(phymode == MODE_UNKNOWN);
+}
+
+static int ath10k_peer_assoc(struct ath10k *ar,
+                            struct ath10k_vif *arvif,
+                            struct ieee80211_sta *sta,
+                            struct ieee80211_bss_conf *bss_conf)
+{
+       struct wmi_peer_assoc_complete_arg arg;
+
+       memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
+
+       ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
+       ath10k_peer_assoc_h_crypto(ar, arvif, &arg);
+       ath10k_peer_assoc_h_rates(ar, sta, &arg);
+       ath10k_peer_assoc_h_ht(ar, sta, &arg);
+       ath10k_peer_assoc_h_vht(ar, sta, &arg);
+       ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg);
+       ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg);
+
+       return ath10k_wmi_peer_assoc(ar, &arg);
+}
+
+/* can be called only in mac80211 callbacks due to `key_count` usage */
+static void ath10k_bss_assoc(struct ieee80211_hw *hw,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_bss_conf *bss_conf)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ieee80211_sta *ap_sta;
+       int ret;
+
+       rcu_read_lock();
+
+       ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
+       if (!ap_sta) {
+               ath10k_warn("Failed to find station entry for %pM\n",
+                           bss_conf->bssid);
+               rcu_read_unlock();
+               return;
+       }
+
+       ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf);
+       if (ret) {
+               ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid);
+               rcu_read_unlock();
+               return;
+       }
+
+       rcu_read_unlock();
+
+       ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid,
+                                bss_conf->bssid);
+       if (ret)
+               ath10k_warn("VDEV: %d up failed: ret %d\n",
+                           arvif->vdev_id, ret);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "VDEV: %d associated, BSSID: %pM, AID: %d\n",
+                          arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
+}
+
+/*
+ * FIXME: flush TIDs
+ */
+static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret;
+
+       /*
+        * For some reason, calling VDEV-DOWN before VDEV-STOP
+        * makes the FW to send frames via HTT after disassociation.
+        * No idea why this happens, even though VDEV-DOWN is supposed
+        * to be analogous to link down, so just stop the VDEV.
+        */
+       ret = ath10k_vdev_stop(arvif);
+       if (!ret)
+               ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n",
+                          arvif->vdev_id);
+
+       /*
+        * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
+        * report beacons from previously associated network through HTT.
+        * This in turn would spam mac80211 WARN_ON if we bring down all
+        * interfaces as it expects there is no rx when no interface is
+        * running.
+        */
+       ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
+       if (ret)
+               ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n",
+                          arvif->vdev_id, ret);
+
+       ath10k_wmi_flush_tx(ar);
+
+       arvif->def_wep_key_index = 0;
+}
+
+static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
+                               struct ieee80211_sta *sta)
+{
+       int ret = 0;
+
+       ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
+       if (ret) {
+               ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
+               return ret;
+       }
+
+       ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
+       if (ret) {
+               ath10k_warn("could not install peer wep keys (%d)\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
+                                  struct ieee80211_sta *sta)
+{
+       int ret = 0;
+
+       ret = ath10k_clear_peer_keys(arvif, sta->addr);
+       if (ret) {
+               ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+/**************/
+/* Regulatory */
+/**************/
+
+static int ath10k_update_channel_list(struct ath10k *ar)
+{
+       struct ieee80211_hw *hw = ar->hw;
+       struct ieee80211_supported_band **bands;
+       enum ieee80211_band band;
+       struct ieee80211_channel *channel;
+       struct wmi_scan_chan_list_arg arg = {0};
+       struct wmi_channel_arg *ch;
+       bool passive;
+       int len;
+       int ret;
+       int i;
+
+       bands = hw->wiphy->bands;
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               if (!bands[band])
+                       continue;
+
+               for (i = 0; i < bands[band]->n_channels; i++) {
+                       if (bands[band]->channels[i].flags &
+                           IEEE80211_CHAN_DISABLED)
+                               continue;
+
+                       arg.n_channels++;
+               }
+       }
+
+       len = sizeof(struct wmi_channel_arg) * arg.n_channels;
+       arg.channels = kzalloc(len, GFP_KERNEL);
+       if (!arg.channels)
+               return -ENOMEM;
+
+       ch = arg.channels;
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               if (!bands[band])
+                       continue;
+
+               for (i = 0; i < bands[band]->n_channels; i++) {
+                       channel = &bands[band]->channels[i];
+
+                       if (channel->flags & IEEE80211_CHAN_DISABLED)
+                               continue;
+
+                       ch->allow_ht   = true;
+
+                       /* FIXME: when should we really allow VHT? */
+                       ch->allow_vht = true;
+
+                       ch->allow_ibss =
+                               !(channel->flags & IEEE80211_CHAN_NO_IBSS);
+
+                       ch->ht40plus =
+                               !(channel->flags & IEEE80211_CHAN_NO_HT40PLUS);
+
+                       passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN;
+                       ch->passive = passive;
+
+                       ch->freq = channel->center_freq;
+                       ch->min_power = channel->max_power * 3;
+                       ch->max_power = channel->max_power * 4;
+                       ch->max_reg_power = channel->max_reg_power * 4;
+                       ch->max_antenna_gain = channel->max_antenna_gain;
+                       ch->reg_class_id = 0; /* FIXME */
+
+                       /* FIXME: why use only legacy modes, why not any
+                        * HT/VHT modes? Would that even make any
+                        * difference? */
+                       if (channel->band == IEEE80211_BAND_2GHZ)
+                               ch->mode = MODE_11G;
+                       else
+                               ch->mode = MODE_11A;
+
+                       if (WARN_ON_ONCE(ch->mode == MODE_UNKNOWN))
+                               continue;
+
+                       ath10k_dbg(ATH10K_DBG_WMI,
+                                  "%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
+                                  __func__, ch - arg.channels, arg.n_channels,
+                                  ch->freq, ch->max_power, ch->max_reg_power,
+                                  ch->max_antenna_gain, ch->mode);
+
+                       ch++;
+               }
+       }
+
+       ret = ath10k_wmi_scan_chan_list(ar, &arg);
+       kfree(arg.channels);
+
+       return ret;
+}
+
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+                               struct regulatory_request *request)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct reg_dmn_pair_mapping *regpair;
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+       ret = ath10k_update_channel_list(ar);
+       if (ret)
+               ath10k_warn("could not update channel list (%d)\n", ret);
+
+       regpair = ar->ath_common.regulatory.regpair;
+       /* Target allows setting up per-band regdomain but ath_common provides
+        * a combined one only */
+       ret = ath10k_wmi_pdev_set_regdomain(ar,
+                                           regpair->regDmnEnum,
+                                           regpair->regDmnEnum, /* 2ghz */
+                                           regpair->regDmnEnum, /* 5ghz */
+                                           regpair->reg_2ghz_ctl,
+                                           regpair->reg_5ghz_ctl);
+       if (ret)
+               ath10k_warn("could not set pdev regdomain (%d)\n", ret);
+}
+
+/***************/
+/* TX handlers */
+/***************/
+
+/*
+ * Frames sent to the FW have to be in "Native Wifi" format.
+ * Strip the QoS field from the 802.11 header.
+ */
+static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw,
+                                      struct ieee80211_tx_control *control,
+                                      struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (void *)skb->data;
+       u8 *qos_ctl;
+
+       if (!ieee80211_is_data_qos(hdr->frame_control))
+               return;
+
+       qos_ctl = ieee80211_get_qos_ctl(hdr);
+       memmove(qos_ctl, qos_ctl + IEEE80211_QOS_CTL_LEN,
+               skb->len - ieee80211_hdrlen(hdr->frame_control));
+       skb_trim(skb, skb->len - IEEE80211_QOS_CTL_LEN);
+}
+
+static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_key_conf *key = info->control.hw_key;
+       int ret;
+
+       /* TODO AP mode should be implemented */
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       if (!ieee80211_has_protected(hdr->frame_control))
+               return;
+
+       if (!key)
+               return;
+
+       if (key->cipher != WLAN_CIPHER_SUITE_WEP40 &&
+           key->cipher != WLAN_CIPHER_SUITE_WEP104)
+               return;
+
+       if (key->keyidx == arvif->def_wep_key_index)
+               return;
+
+       ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx);
+
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_DEF_KEYID,
+                                       key->keyidx);
+       if (ret) {
+               ath10k_warn("could not update wep keyidx (%d)\n", ret);
+               return;
+       }
+
+       arvif->def_wep_key_index = key->keyidx;
+}
+
+static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+       /* This is case only for P2P_GO */
+       if (arvif->vdev_type != WMI_VDEV_TYPE_AP ||
+           arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+               return;
+
+       if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) {
+               spin_lock_bh(&ar->data_lock);
+               if (arvif->u.ap.noa_data)
+                       if (!pskb_expand_head(skb, 0, arvif->u.ap.noa_len,
+                                             GFP_ATOMIC))
+                               memcpy(skb_put(skb, arvif->u.ap.noa_len),
+                                      arvif->u.ap.noa_data,
+                                      arvif->u.ap.noa_len);
+               spin_unlock_bh(&ar->data_lock);
+       }
+}
+
+static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       int ret;
+
+       if (ieee80211_is_mgmt(hdr->frame_control))
+               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+       else if (ieee80211_is_nullfunc(hdr->frame_control))
+               /* FW does not report tx status properly for NullFunc frames
+                * unless they are sent through mgmt tx path. mac80211 sends
+                * those frames when it detects link/beacon loss and depends on
+                * the tx status to be correct. */
+               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+       else
+               ret = ath10k_htt_tx(ar->htt, skb);
+
+       if (ret) {
+               ath10k_warn("tx failed (%d). dropping packet.\n", ret);
+               ieee80211_free_txskb(ar->hw, skb);
+       }
+}
+
+void ath10k_offchan_tx_purge(struct ath10k *ar)
+{
+       struct sk_buff *skb;
+
+       for (;;) {
+               skb = skb_dequeue(&ar->offchan_tx_queue);
+               if (!skb)
+                       break;
+
+               ieee80211_free_txskb(ar->hw, skb);
+       }
+}
+
+void ath10k_offchan_tx_work(struct work_struct *work)
+{
+       struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work);
+       struct ath10k_peer *peer;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb;
+       const u8 *peer_addr;
+       int vdev_id;
+       int ret;
+
+       /* FW requirement: We must create a peer before FW will send out
+        * an offchannel frame. Otherwise the frame will be stuck and
+        * never transmitted. We delete the peer upon tx completion.
+        * It is unlikely that a peer for offchannel tx will already be
+        * present. However it may be in some rare cases so account for that.
+        * Otherwise we might remove a legitimate peer and break stuff. */
+
+       for (;;) {
+               skb = skb_dequeue(&ar->offchan_tx_queue);
+               if (!skb)
+                       break;
+
+               mutex_lock(&ar->conf_mutex);
+
+               ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n",
+                          skb);
+
+               hdr = (struct ieee80211_hdr *)skb->data;
+               peer_addr = ieee80211_get_DA(hdr);
+               vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id;
+
+               spin_lock_bh(&ar->data_lock);
+               peer = ath10k_peer_find(ar, vdev_id, peer_addr);
+               spin_unlock_bh(&ar->data_lock);
+
+               if (peer)
+                       ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n",
+                                  peer_addr, vdev_id);
+
+               if (!peer) {
+                       ret = ath10k_peer_create(ar, vdev_id, peer_addr);
+                       if (ret)
+                               ath10k_warn("peer %pM on vdev %d not created (%d)\n",
+                                           peer_addr, vdev_id, ret);
+               }
+
+               spin_lock_bh(&ar->data_lock);
+               INIT_COMPLETION(ar->offchan_tx_completed);
+               ar->offchan_tx_skb = skb;
+               spin_unlock_bh(&ar->data_lock);
+
+               ath10k_tx_htt(ar, skb);
+
+               ret = wait_for_completion_timeout(&ar->offchan_tx_completed,
+                                                 3 * HZ);
+               if (ret <= 0)
+                       ath10k_warn("timed out waiting for offchannel skb %p\n",
+                                   skb);
+
+               if (!peer) {
+                       ret = ath10k_peer_delete(ar, vdev_id, peer_addr);
+                       if (ret)
+                               ath10k_warn("peer %pM on vdev %d not deleted (%d)\n",
+                                           peer_addr, vdev_id, ret);
+               }
+
+               mutex_unlock(&ar->conf_mutex);
+       }
+}
+
+/************/
+/* Scanning */
+/************/
+
+/*
+ * This gets called if we dont get a heart-beat during scan.
+ * This may indicate the FW has hung and we need to abort the
+ * scan manually to prevent cancel_hw_scan() from deadlocking
+ */
+void ath10k_reset_scan(unsigned long ptr)
+{
+       struct ath10k *ar = (struct ath10k *)ptr;
+
+       spin_lock_bh(&ar->data_lock);
+       if (!ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               return;
+       }
+
+       ath10k_warn("scan timeout. resetting. fw issue?\n");
+
+       if (ar->scan.is_roc)
+               ieee80211_remain_on_channel_expired(ar->hw);
+       else
+               ieee80211_scan_completed(ar->hw, 1 /* aborted */);
+
+       ar->scan.in_progress = false;
+       complete_all(&ar->scan.completed);
+       spin_unlock_bh(&ar->data_lock);
+}
+
+static int ath10k_abort_scan(struct ath10k *ar)
+{
+       struct wmi_stop_scan_arg arg = {
+               .req_id = 1, /* FIXME */
+               .req_type = WMI_SCAN_STOP_ONE,
+               .u.scan_id = ATH10K_SCAN_ID,
+       };
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       del_timer_sync(&ar->scan.timeout);
+
+       spin_lock_bh(&ar->data_lock);
+       if (!ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               return 0;
+       }
+
+       ar->scan.aborting = true;
+       spin_unlock_bh(&ar->data_lock);
+
+       ret = ath10k_wmi_stop_scan(ar, &arg);
+       if (ret) {
+               ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+               return -EIO;
+       }
+
+       ath10k_wmi_flush_tx(ar);
+
+       ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ);
+       if (ret == 0)
+               ath10k_warn("timed out while waiting for scan to stop\n");
+
+       /* scan completion may be done right after we timeout here, so let's
+        * check the in_progress and tell mac80211 scan is completed. if we
+        * don't do that and FW fails to send us scan completion indication
+        * then userspace won't be able to scan anymore */
+       ret = 0;
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               ath10k_warn("could not stop scan. its still in progress\n");
+               ar->scan.in_progress = false;
+               ath10k_offchan_tx_purge(ar);
+               ret = -ETIMEDOUT;
+       }
+       spin_unlock_bh(&ar->data_lock);
+
+       return ret;
+}
+
+static int ath10k_start_scan(struct ath10k *ar,
+                            const struct wmi_start_scan_arg *arg)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ret = ath10k_wmi_start_scan(ar, arg);
+       if (ret)
+               return ret;
+
+       /* make sure we submit the command so the completion
+       * timeout makes sense */
+       ath10k_wmi_flush_tx(ar);
+
+       ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ);
+       if (ret == 0) {
+               ath10k_abort_scan(ar);
+               return ret;
+       }
+
+       /* the scan can complete earlier, before we even
+        * start the timer. in that case the timer handler
+        * checks ar->scan.in_progress and bails out if its
+        * false. Add a 200ms margin to account event/command
+        * processing. */
+       mod_timer(&ar->scan.timeout, jiffies +
+                 msecs_to_jiffies(arg->max_scan_time+200));
+       return 0;
+}
+
+/**********************/
+/* mac80211 callbacks */
+/**********************/
+
+static void ath10k_tx(struct ieee80211_hw *hw,
+                     struct ieee80211_tx_control *control,
+                     struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = NULL;
+       u32 vdev_id = 0;
+       u8 tid;
+
+       if (info->control.vif) {
+               arvif = ath10k_vif_to_arvif(info->control.vif);
+               vdev_id = arvif->vdev_id;
+       } else if (ar->monitor_enabled) {
+               vdev_id = ar->monitor_vdev_id;
+       }
+
+       /* We should disable CCK RATE due to P2P */
+       if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
+               ath10k_dbg(ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
+
+       /* we must calculate tid before we apply qos workaround
+        * as we'd lose the qos control field */
+       tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+       if (ieee80211_is_data_qos(hdr->frame_control) &&
+           is_unicast_ether_addr(ieee80211_get_DA(hdr))) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+       }
+
+       ath10k_tx_h_qos_workaround(hw, control, skb);
+       ath10k_tx_h_update_wep_key(skb);
+       ath10k_tx_h_add_p2p_noa_ie(ar, skb);
+       ath10k_tx_h_seq_no(skb);
+
+       memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
+       ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
+       ATH10K_SKB_CB(skb)->htt.tid = tid;
+
+       if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+               spin_lock_bh(&ar->data_lock);
+               ATH10K_SKB_CB(skb)->htt.is_offchan = true;
+               ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id;
+               spin_unlock_bh(&ar->data_lock);
+
+               ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb);
+
+               skb_queue_tail(&ar->offchan_tx_queue, skb);
+               ieee80211_queue_work(hw, &ar->offchan_tx_work);
+               return;
+       }
+
+       ath10k_tx_htt(ar, skb);
+}
+
+/*
+ * Initialize various parameters with default vaules.
+ */
+static int ath10k_start(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
+       if (ret)
+               ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
+                           ret);
+
+       ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0);
+       if (ret)
+               ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
+                           ret);
+
+       return 0;
+}
+
+static void ath10k_stop(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+
+       /* avoid leaks in case FW never confirms scan for offchannel */
+       cancel_work_sync(&ar->offchan_tx_work);
+       ath10k_offchan_tx_purge(ar);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
+       struct ath10k_generic_iter ar_iter;
+       struct ath10k *ar = hw->priv;
+       struct ieee80211_conf *conf = &hw->conf;
+       int ret = 0;
+       u32 flags;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n",
+                          conf->chandef.chan->center_freq);
+               spin_lock_bh(&ar->data_lock);
+               ar->rx_channel = conf->chandef.chan;
+               spin_unlock_bh(&ar->data_lock);
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_PS) {
+               memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+               ar_iter.ar = ar;
+               flags = IEEE80211_IFACE_ITER_RESUME_ALL;
+
+               ieee80211_iterate_active_interfaces_atomic(hw,
+                                                          flags,
+                                                          ath10k_ps_iter,
+                                                          &ar_iter);
+
+               ret = ar_iter.ret;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+               if (conf->flags & IEEE80211_CONF_MONITOR)
+                       ret = ath10k_monitor_create(ar);
+               else
+                       ret = ath10k_monitor_destroy(ar);
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+/*
+ * TODO:
+ * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE,
+ * because we will send mgmt frames without CCK. This requirement
+ * for P2P_FIND/GO_NEG should be handled by checking CCK flag
+ * in the TX packet.
+ */
+static int ath10k_add_interface(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       enum wmi_sta_powersave_param param;
+       int ret = 0;
+       u32 value;
+       int bit;
+
+       mutex_lock(&ar->conf_mutex);
+
+       arvif->ar = ar;
+       arvif->vif = vif;
+
+       if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
+               ath10k_warn("Only one monitor interface allowed\n");
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       bit = ffs(ar->free_vdev_map);
+       if (bit == 0) {
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       arvif->vdev_id = bit - 1;
+       arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
+       ar->free_vdev_map &= ~(1 << arvif->vdev_id);
+
+       if (ar->p2p)
+               arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NL80211_IFTYPE_STATION:
+               arvif->vdev_type = WMI_VDEV_TYPE_STA;
+               if (vif->p2p)
+                       arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_CLIENT;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               arvif->vdev_type = WMI_VDEV_TYPE_IBSS;
+               break;
+       case NL80211_IFTYPE_AP:
+               arvif->vdev_type = WMI_VDEV_TYPE_AP;
+
+               if (vif->p2p)
+                       arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO;
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               arvif->vdev_type = WMI_VDEV_TYPE_MONITOR;
+               break;
+       default:
+               WARN_ON(1);
+               break;
+       }
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n",
+                  arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype);
+
+       ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
+                                    arvif->vdev_subtype, vif->addr);
+       if (ret) {
+               ath10k_warn("WMI vdev create failed: ret %d\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID,
+                                       arvif->def_wep_key_index);
+       if (ret)
+               ath10k_warn("Failed to set default keyid: %d\n", ret);
+
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+                                       ATH10K_HW_TXRX_NATIVE_WIFI);
+       if (ret)
+               ath10k_warn("Failed to set TX encap: %d\n", ret);
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
+               if (ret) {
+                       ath10k_warn("Failed to create peer for AP: %d\n", ret);
+                       goto exit;
+               }
+       }
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_STA) {
+               param = WMI_STA_PS_PARAM_RX_WAKE_POLICY;
+               value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
+               ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                                 param, value);
+               if (ret)
+                       ath10k_warn("Failed to set RX wake policy: %d\n", ret);
+
+               param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
+               value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
+               ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                                 param, value);
+               if (ret)
+                       ath10k_warn("Failed to set TX wake thresh: %d\n", ret);
+
+               param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
+               value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
+               ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                                 param, value);
+               if (ret)
+                       ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
+       }
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+               ar->monitor_present = true;
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static void ath10k_remove_interface(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id);
+
+       ar->free_vdev_map |= 1 << (arvif->vdev_id);
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
+               if (ret)
+                       ath10k_warn("Failed to remove peer for AP: %d\n", ret);
+
+               kfree(arvif->u.ap.noa_data);
+       }
+
+       ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
+       if (ret)
+               ath10k_warn("WMI vdev delete failed: %d\n", ret);
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+               ar->monitor_present = false;
+
+       ath10k_peer_cleanup(ar, arvif->vdev_id);
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+/*
+ * FIXME: Has to be verified.
+ */
+#define SUPPORTED_FILTERS                      \
+       (FIF_PROMISC_IN_BSS |                   \
+       FIF_ALLMULTI |                          \
+       FIF_CONTROL |                           \
+       FIF_PSPOLL |                            \
+       FIF_OTHER_BSS |                         \
+       FIF_BCN_PRBRESP_PROMISC |               \
+       FIF_PROBE_REQ |                         \
+       FIF_FCSFAIL)
+
+static void ath10k_configure_filter(struct ieee80211_hw *hw,
+                                   unsigned int changed_flags,
+                                   unsigned int *total_flags,
+                                   u64 multicast)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       changed_flags &= SUPPORTED_FILTERS;
+       *total_flags &= SUPPORTED_FILTERS;
+       ar->filter_flags = *total_flags;
+
+       if ((ar->filter_flags & FIF_PROMISC_IN_BSS) &&
+           !ar->monitor_enabled) {
+               ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
+               if (ret)
+                       ath10k_warn("Unable to start monitor mode\n");
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n");
+       } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) &&
+                  ar->monitor_enabled) {
+               ret = ath10k_monitor_stop(ar);
+               if (ret)
+                       ath10k_warn("Unable to stop monitor mode\n");
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n");
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_bss_conf *info,
+                                   u32 changed)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (changed & BSS_CHANGED_IBSS)
+               ath10k_control_ibss(arvif, info, vif->addr);
+
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               arvif->beacon_interval = info->beacon_int;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_BEACON_INTERVAL,
+                                               arvif->beacon_interval);
+               if (ret)
+                       ath10k_warn("Failed to set beacon interval for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Beacon interval: %d set for VDEV: %d\n",
+                                  arvif->beacon_interval, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_BEACON) {
+               ret = ath10k_wmi_pdev_set_param(ar,
+                                               WMI_PDEV_PARAM_BEACON_TX_MODE,
+                                               WMI_BEACON_STAGGERED_MODE);
+               if (ret)
+                       ath10k_warn("Failed to set beacon mode for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set staggered beacon mode for VDEV: %d\n",
+                                  arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_BEACON_INFO) {
+               arvif->dtim_period = info->dtim_period;
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_DTIM_PERIOD,
+                                               arvif->dtim_period);
+               if (ret)
+                       ath10k_warn("Failed to set dtim period for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set dtim period: %d for VDEV: %d\n",
+                                  arvif->dtim_period, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_SSID &&
+           vif->type == NL80211_IFTYPE_AP) {
+               arvif->u.ap.ssid_len = info->ssid_len;
+               if (info->ssid_len)
+                       memcpy(arvif->u.ap.ssid, info->ssid, info->ssid_len);
+               arvif->u.ap.hidden_ssid = info->hidden_ssid;
+       }
+
+       if (changed & BSS_CHANGED_BSSID) {
+               if (!is_zero_ether_addr(info->bssid)) {
+                       ret = ath10k_peer_create(ar, arvif->vdev_id,
+                                                info->bssid);
+                       if (ret)
+                               ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
+                                           info->bssid, arvif->vdev_id);
+                       else
+                               ath10k_dbg(ATH10K_DBG_MAC,
+                                          "Added peer: %pM for VDEV: %d\n",
+                                          info->bssid, arvif->vdev_id);
+
+
+                       if (vif->type == NL80211_IFTYPE_STATION) {
+                               /*
+                                * this is never erased as we it for crypto key
+                                * clearing; this is FW requirement
+                                */
+                               memcpy(arvif->u.sta.bssid, info->bssid,
+                                      ETH_ALEN);
+
+                               ret = ath10k_vdev_start(arvif);
+                               if (!ret)
+                                       ath10k_dbg(ATH10K_DBG_MAC,
+                                                  "VDEV: %d started with BSSID: %pM\n",
+                                                  arvif->vdev_id, info->bssid);
+                       }
+
+                       /*
+                        * Mac80211 does not keep IBSS bssid when leaving IBSS,
+                        * so driver need to store it. It is needed when leaving
+                        * IBSS in order to remove BSSID peer.
+                        */
+                       if (vif->type == NL80211_IFTYPE_ADHOC)
+                               memcpy(arvif->u.ibss.bssid, info->bssid,
+                                      ETH_ALEN);
+               }
+       }
+
+       if (changed & BSS_CHANGED_BEACON_ENABLED)
+               ath10k_control_beaconing(arvif, info);
+
+       if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+               u32 cts_prot;
+               if (info->use_cts_prot)
+                       cts_prot = 1;
+               else
+                       cts_prot = 0;
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_ENABLE_RTSCTS,
+                                               cts_prot);
+               if (ret)
+                       ath10k_warn("Failed to set CTS prot for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set CTS prot: %d for VDEV: %d\n",
+                                  cts_prot, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               u32 slottime;
+               if (info->use_short_slot)
+                       slottime = WMI_VDEV_SLOT_TIME_SHORT; /* 9us */
+
+               else
+                       slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_SLOT_TIME,
+                                               slottime);
+               if (ret)
+                       ath10k_warn("Failed to set erp slot for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set slottime: %d for VDEV: %d\n",
+                                  slottime, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               u32 preamble;
+               if (info->use_short_preamble)
+                       preamble = WMI_VDEV_PREAMBLE_SHORT;
+               else
+                       preamble = WMI_VDEV_PREAMBLE_LONG;
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_PREAMBLE,
+                                               preamble);
+               if (ret)
+                       ath10k_warn("Failed to set preamble for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set preamble: %d for VDEV: %d\n",
+                                  preamble, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_ASSOC) {
+               if (info->assoc)
+                       ath10k_bss_assoc(hw, vif, info);
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_hw_scan(struct ieee80211_hw *hw,
+                         struct ieee80211_vif *vif,
+                         struct cfg80211_scan_request *req)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct wmi_start_scan_arg arg;
+       int ret = 0;
+       int i;
+
+       mutex_lock(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       INIT_COMPLETION(ar->scan.started);
+       INIT_COMPLETION(ar->scan.completed);
+       ar->scan.in_progress = true;
+       ar->scan.aborting = false;
+       ar->scan.is_roc = false;
+       ar->scan.vdev_id = arvif->vdev_id;
+       spin_unlock_bh(&ar->data_lock);
+
+       memset(&arg, 0, sizeof(arg));
+       ath10k_wmi_start_scan_init(ar, &arg);
+       arg.vdev_id = arvif->vdev_id;
+       arg.scan_id = ATH10K_SCAN_ID;
+
+       if (!req->no_cck)
+               arg.scan_ctrl_flags |= WMI_SCAN_ADD_CCK_RATES;
+
+       if (req->ie_len) {
+               arg.ie_len = req->ie_len;
+               memcpy(arg.ie, req->ie, arg.ie_len);
+       }
+
+       if (req->n_ssids) {
+               arg.n_ssids = req->n_ssids;
+               for (i = 0; i < arg.n_ssids; i++) {
+                       arg.ssids[i].len  = req->ssids[i].ssid_len;
+                       arg.ssids[i].ssid = req->ssids[i].ssid;
+               }
+       }
+
+       if (req->n_channels) {
+               arg.n_channels = req->n_channels;
+               for (i = 0; i < arg.n_channels; i++)
+                       arg.channels[i] = req->channels[i]->center_freq;
+       }
+
+       ret = ath10k_start_scan(ar, &arg);
+       if (ret) {
+               ath10k_warn("could not start hw scan (%d)\n", ret);
+               spin_lock_bh(&ar->data_lock);
+               ar->scan.in_progress = false;
+               spin_unlock_bh(&ar->data_lock);
+       }
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+       ret = ath10k_abort_scan(ar);
+       if (ret) {
+               ath10k_warn("couldn't abort scan (%d). forcefully sending scan completion to mac80211\n",
+                           ret);
+               ieee80211_scan_completed(hw, 1 /* aborted */);
+       }
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+                         struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+                         struct ieee80211_key_conf *key)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k_peer *peer;
+       const u8 *peer_addr;
+       bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+                     key->cipher == WLAN_CIPHER_SUITE_WEP104;
+       int ret = 0;
+
+       if (key->keyidx > WMI_MAX_KEY_INDEX)
+               return -ENOSPC;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (sta)
+               peer_addr = sta->addr;
+       else if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
+               peer_addr = vif->bss_conf.bssid;
+       else
+               peer_addr = vif->addr;
+
+       key->hw_key_idx = key->keyidx;
+
+       /* the peer should not disappear in mid-way (unless FW goes awry) since
+        * we already hold conf_mutex. we just make sure its there now. */
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!peer) {
+               if (cmd == SET_KEY) {
+                       ath10k_warn("cannot install key for non-existent peer %pM\n",
+                                   peer_addr);
+                       ret = -EOPNOTSUPP;
+                       goto exit;
+               } else {
+                       /* if the peer doesn't exist there is no key to disable
+                        * anymore */
+                       goto exit;
+               }
+       }
+
+       if (is_wep) {
+               if (cmd == SET_KEY)
+                       arvif->wep_keys[key->keyidx] = key;
+               else
+                       arvif->wep_keys[key->keyidx] = NULL;
+
+               if (cmd == DISABLE_KEY)
+                       ath10k_clear_vdev_key(arvif, key);
+       }
+
+       ret = ath10k_install_key(arvif, key, cmd, peer_addr);
+       if (ret) {
+               ath10k_warn("ath10k_install_key failed (%d)\n", ret);
+               goto exit;
+       }
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
+       if (peer && cmd == SET_KEY)
+               peer->keys[key->keyidx] = key;
+       else if (peer && cmd == DISABLE_KEY)
+               peer->keys[key->keyidx] = NULL;
+       else if (peer == NULL)
+               /* impossible unless FW goes crazy */
+               ath10k_warn("peer %pM disappeared!\n", peer_addr);
+       spin_unlock_bh(&ar->data_lock);
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_sta_state(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta,
+                           enum ieee80211_sta_state old_state,
+                           enum ieee80211_sta_state new_state)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (old_state == IEEE80211_STA_NOTEXIST &&
+           new_state == IEEE80211_STA_NONE &&
+           vif->type != NL80211_IFTYPE_STATION) {
+               /*
+                * New station addition.
+                */
+               ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
+               if (ret)
+                       ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
+                                   sta->addr, arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Added peer: %pM for VDEV: %d\n",
+                                  sta->addr, arvif->vdev_id);
+       } else if ((old_state == IEEE80211_STA_NONE &&
+                   new_state == IEEE80211_STA_NOTEXIST)) {
+               /*
+                * Existing station deletion.
+                */
+               ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
+               if (ret)
+                       ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n",
+                                   sta->addr, arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Removed peer: %pM for VDEV: %d\n",
+                                  sta->addr, arvif->vdev_id);
+
+               if (vif->type == NL80211_IFTYPE_STATION)
+                       ath10k_bss_disassoc(hw, vif);
+       } else if (old_state == IEEE80211_STA_AUTH &&
+                  new_state == IEEE80211_STA_ASSOC &&
+                  (vif->type == NL80211_IFTYPE_AP ||
+                   vif->type == NL80211_IFTYPE_ADHOC)) {
+               /*
+                * New association.
+                */
+               ret = ath10k_station_assoc(ar, arvif, sta);
+               if (ret)
+                       ath10k_warn("Failed to associate station: %pM\n",
+                                   sta->addr);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Station %pM moved to assoc state\n",
+                                  sta->addr);
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTH &&
+                  (vif->type == NL80211_IFTYPE_AP ||
+                   vif->type == NL80211_IFTYPE_ADHOC)) {
+               /*
+                * Disassociation.
+                */
+               ret = ath10k_station_disassoc(ar, arvif, sta);
+               if (ret)
+                       ath10k_warn("Failed to disassociate station: %pM\n",
+                                   sta->addr);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Station %pM moved to disassociated state\n",
+                                  sta->addr);
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
+                                u16 ac, bool enable)
+{
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       u32 value = 0;
+       int ret = 0;
+
+       if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
+               return 0;
+
+       switch (ac) {
+       case IEEE80211_AC_VO:
+               value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC3_TRIGGER_EN;
+               break;
+       case IEEE80211_AC_VI:
+               value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC2_TRIGGER_EN;
+               break;
+       case IEEE80211_AC_BE:
+               value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC1_TRIGGER_EN;
+               break;
+       case IEEE80211_AC_BK:
+               value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC0_TRIGGER_EN;
+               break;
+       }
+
+       if (enable)
+               arvif->u.sta.uapsd |= value;
+       else
+               arvif->u.sta.uapsd &= ~value;
+
+       ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                         WMI_STA_PS_PARAM_UAPSD,
+                                         arvif->u.sta.uapsd);
+       if (ret) {
+               ath10k_warn("could not set uapsd params %d\n", ret);
+               goto exit;
+       }
+
+       if (arvif->u.sta.uapsd)
+               value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD;
+       else
+               value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
+
+       ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                         WMI_STA_PS_PARAM_RX_WAKE_POLICY,
+                                         value);
+       if (ret)
+               ath10k_warn("could not set rx wake param %d\n", ret);
+
+exit:
+       return ret;
+}
+
+static int ath10k_conf_tx(struct ieee80211_hw *hw,
+                         struct ieee80211_vif *vif, u16 ac,
+                         const struct ieee80211_tx_queue_params *params)
+{
+       struct ath10k *ar = hw->priv;
+       struct wmi_wmm_params_arg *p = NULL;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       switch (ac) {
+       case IEEE80211_AC_VO:
+               p = &ar->wmm_params.ac_vo;
+               break;
+       case IEEE80211_AC_VI:
+               p = &ar->wmm_params.ac_vi;
+               break;
+       case IEEE80211_AC_BE:
+               p = &ar->wmm_params.ac_be;
+               break;
+       case IEEE80211_AC_BK:
+               p = &ar->wmm_params.ac_bk;
+               break;
+       }
+
+       if (WARN_ON(!p)) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       p->cwmin = params->cw_min;
+       p->cwmax = params->cw_max;
+       p->aifs = params->aifs;
+
+       /*
+        * The channel time duration programmed in the HW is in absolute
+        * microseconds, while mac80211 gives the txop in units of
+        * 32 microseconds.
+        */
+       p->txop = params->txop * 32;
+
+       /* FIXME: FW accepts wmm params per hw, not per vif */
+       ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params);
+       if (ret) {
+               ath10k_warn("could not set wmm params %d\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
+       if (ret)
+               ath10k_warn("could not set sta uapsd %d\n", ret);
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+#define ATH10K_ROC_TIMEOUT_HZ (2*HZ)
+
+static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_channel *chan,
+                                   int duration,
+                                   enum ieee80211_roc_type type)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct wmi_start_scan_arg arg;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       INIT_COMPLETION(ar->scan.started);
+       INIT_COMPLETION(ar->scan.completed);
+       INIT_COMPLETION(ar->scan.on_channel);
+       ar->scan.in_progress = true;
+       ar->scan.aborting = false;
+       ar->scan.is_roc = true;
+       ar->scan.vdev_id = arvif->vdev_id;
+       ar->scan.roc_freq = chan->center_freq;
+       spin_unlock_bh(&ar->data_lock);
+
+       memset(&arg, 0, sizeof(arg));
+       ath10k_wmi_start_scan_init(ar, &arg);
+       arg.vdev_id = arvif->vdev_id;
+       arg.scan_id = ATH10K_SCAN_ID;
+       arg.n_channels = 1;
+       arg.channels[0] = chan->center_freq;
+       arg.dwell_time_active = duration;
+       arg.dwell_time_passive = duration;
+       arg.max_scan_time = 2 * duration;
+       arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE;
+       arg.scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ;
+
+       ret = ath10k_start_scan(ar, &arg);
+       if (ret) {
+               ath10k_warn("could not start roc scan (%d)\n", ret);
+               spin_lock_bh(&ar->data_lock);
+               ar->scan.in_progress = false;
+               spin_unlock_bh(&ar->data_lock);
+               goto exit;
+       }
+
+       ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ);
+       if (ret == 0) {
+               ath10k_warn("could not switch to channel for roc scan\n");
+               ath10k_abort_scan(ar);
+               ret = -ETIMEDOUT;
+               goto exit;
+       }
+
+       ret = 0;
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+
+       mutex_lock(&ar->conf_mutex);
+       ath10k_abort_scan(ar);
+       mutex_unlock(&ar->conf_mutex);
+
+       return 0;
+}
+
+/*
+ * Both RTS and Fragmentation threshold are interface-specific
+ * in ath10k, but device-specific in mac80211.
+ */
+static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct ath10k_generic_iter *ar_iter = data;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
+
+       rts = min_t(u32, rts, ATH10K_RTS_MAX);
+
+       ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
+                                                WMI_VDEV_PARAM_RTS_THRESHOLD,
+                                                rts);
+       if (ar_iter->ret)
+               ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
+                           arvif->vdev_id);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "Set RTS threshold: %d for VDEV: %d\n",
+                          rts, arvif->vdev_id);
+}
+
+static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       struct ath10k_generic_iter ar_iter;
+       struct ath10k *ar = hw->priv;
+
+       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+       ar_iter.ar = ar;
+
+       mutex_lock(&ar->conf_mutex);
+       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+                                           ath10k_set_rts_iter, &ar_iter);
+       mutex_unlock(&ar->conf_mutex);
+
+       return ar_iter.ret;
+}
+
+static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct ath10k_generic_iter *ar_iter = data;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
+       int ret;
+
+       frag = clamp_t(u32, frag,
+                      ATH10K_FRAGMT_THRESHOLD_MIN,
+                      ATH10K_FRAGMT_THRESHOLD_MAX);
+
+       ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+                                       frag);
+
+       ar_iter->ret = ret;
+       if (ar_iter->ret)
+               ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
+                           arvif->vdev_id);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "Set frag threshold: %d for VDEV: %d\n",
+                          frag, arvif->vdev_id);
+}
+
+static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       struct ath10k_generic_iter ar_iter;
+       struct ath10k *ar = hw->priv;
+
+       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+       ar_iter.ar = ar;
+
+       mutex_lock(&ar->conf_mutex);
+       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+                                           ath10k_set_frag_iter, &ar_iter);
+       mutex_unlock(&ar->conf_mutex);
+
+       return ar_iter.ret;
+}
+
+static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       /* mac80211 doesn't care if we really xmit queued frames or not
+        * we'll collect those frames either way if we stop/delete vdevs */
+       if (drop)
+               return;
+
+       ret = wait_event_timeout(ar->htt->empty_tx_wq, ({
+                       bool empty;
+                       spin_lock_bh(&ar->htt->tx_lock);
+                       empty = bitmap_empty(ar->htt->used_msdu_ids,
+                                            ar->htt->max_num_pending_tx);
+                       spin_unlock_bh(&ar->htt->tx_lock);
+                       (empty);
+               }), ATH10K_FLUSH_TIMEOUT_HZ);
+       if (ret <= 0)
+               ath10k_warn("tx not flushed\n");
+}
+
+/* TODO: Implement this function properly
+ * For now it is needed to reply to Probe Requests in IBSS mode.
+ * Propably we need this information from FW.
+ */
+static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
+{
+       return 1;
+}
+
+static const struct ieee80211_ops ath10k_ops = {
+       .tx                             = ath10k_tx,
+       .start                          = ath10k_start,
+       .stop                           = ath10k_stop,
+       .config                         = ath10k_config,
+       .add_interface                  = ath10k_add_interface,
+       .remove_interface               = ath10k_remove_interface,
+       .configure_filter               = ath10k_configure_filter,
+       .bss_info_changed               = ath10k_bss_info_changed,
+       .hw_scan                        = ath10k_hw_scan,
+       .cancel_hw_scan                 = ath10k_cancel_hw_scan,
+       .set_key                        = ath10k_set_key,
+       .sta_state                      = ath10k_sta_state,
+       .conf_tx                        = ath10k_conf_tx,
+       .remain_on_channel              = ath10k_remain_on_channel,
+       .cancel_remain_on_channel       = ath10k_cancel_remain_on_channel,
+       .set_rts_threshold              = ath10k_set_rts_threshold,
+       .set_frag_threshold             = ath10k_set_frag_threshold,
+       .flush                          = ath10k_flush,
+       .tx_last_beacon                 = ath10k_tx_last_beacon,
+};
+
+#define RATETAB_ENT(_rate, _rateid, _flags) { \
+       .bitrate                = (_rate), \
+       .flags                  = (_flags), \
+       .hw_value               = (_rateid), \
+}
+
+#define CHAN2G(_channel, _freq, _flags) { \
+       .band                   = IEEE80211_BAND_2GHZ, \
+       .hw_value               = (_channel), \
+       .center_freq            = (_freq), \
+       .flags                  = (_flags), \
+       .max_antenna_gain       = 0, \
+       .max_power              = 30, \
+}
+
+#define CHAN5G(_channel, _freq, _flags) { \
+       .band                   = IEEE80211_BAND_5GHZ, \
+       .hw_value               = (_channel), \
+       .center_freq            = (_freq), \
+       .flags                  = (_flags), \
+       .max_antenna_gain       = 0, \
+       .max_power              = 30, \
+}
+
+static const struct ieee80211_channel ath10k_2ghz_channels[] = {
+       CHAN2G(1, 2412, 0),
+       CHAN2G(2, 2417, 0),
+       CHAN2G(3, 2422, 0),
+       CHAN2G(4, 2427, 0),
+       CHAN2G(5, 2432, 0),
+       CHAN2G(6, 2437, 0),
+       CHAN2G(7, 2442, 0),
+       CHAN2G(8, 2447, 0),
+       CHAN2G(9, 2452, 0),
+       CHAN2G(10, 2457, 0),
+       CHAN2G(11, 2462, 0),
+       CHAN2G(12, 2467, 0),
+       CHAN2G(13, 2472, 0),
+       CHAN2G(14, 2484, 0),
+};
+
+static const struct ieee80211_channel ath10k_5ghz_channels[] = {
+       CHAN5G(36, 5180, 0),
+       CHAN5G(40, 5200, 0),
+       CHAN5G(44, 5220, 0),
+       CHAN5G(48, 5240, 0),
+       CHAN5G(52, 5260, 0),
+       CHAN5G(56, 5280, 0),
+       CHAN5G(60, 5300, 0),
+       CHAN5G(64, 5320, 0),
+       CHAN5G(100, 5500, 0),
+       CHAN5G(104, 5520, 0),
+       CHAN5G(108, 5540, 0),
+       CHAN5G(112, 5560, 0),
+       CHAN5G(116, 5580, 0),
+       CHAN5G(120, 5600, 0),
+       CHAN5G(124, 5620, 0),
+       CHAN5G(128, 5640, 0),
+       CHAN5G(132, 5660, 0),
+       CHAN5G(136, 5680, 0),
+       CHAN5G(140, 5700, 0),
+       CHAN5G(149, 5745, 0),
+       CHAN5G(153, 5765, 0),
+       CHAN5G(157, 5785, 0),
+       CHAN5G(161, 5805, 0),
+       CHAN5G(165, 5825, 0),
+};
+
+static struct ieee80211_rate ath10k_rates[] = {
+       /* CCK */
+       RATETAB_ENT(10,  0x82, 0),
+       RATETAB_ENT(20,  0x84, 0),
+       RATETAB_ENT(55,  0x8b, 0),
+       RATETAB_ENT(110, 0x96, 0),
+       /* OFDM */
+       RATETAB_ENT(60,  0x0c, 0),
+       RATETAB_ENT(90,  0x12, 0),
+       RATETAB_ENT(120, 0x18, 0),
+       RATETAB_ENT(180, 0x24, 0),
+       RATETAB_ENT(240, 0x30, 0),
+       RATETAB_ENT(360, 0x48, 0),
+       RATETAB_ENT(480, 0x60, 0),
+       RATETAB_ENT(540, 0x6c, 0),
+};
+
+#define ath10k_a_rates (ath10k_rates + 4)
+#define ath10k_a_rates_size (ARRAY_SIZE(ath10k_rates) - 4)
+#define ath10k_g_rates (ath10k_rates + 0)
+#define ath10k_g_rates_size (ARRAY_SIZE(ath10k_rates))
+
+struct ath10k *ath10k_mac_create(void)
+{
+       struct ieee80211_hw *hw;
+       struct ath10k *ar;
+
+       hw = ieee80211_alloc_hw(sizeof(struct ath10k), &ath10k_ops);
+       if (!hw)
+               return NULL;
+
+       ar = hw->priv;
+       ar->hw = hw;
+
+       return ar;
+}
+
+void ath10k_mac_destroy(struct ath10k *ar)
+{
+       ieee80211_free_hw(ar->hw);
+}
+
+static const struct ieee80211_iface_limit ath10k_if_limits[] = {
+       {
+       .max    = 8,
+       .types  = BIT(NL80211_IFTYPE_STATION)
+               | BIT(NL80211_IFTYPE_P2P_CLIENT)
+               | BIT(NL80211_IFTYPE_P2P_GO)
+               | BIT(NL80211_IFTYPE_AP)
+       }
+};
+
+static const struct ieee80211_iface_combination ath10k_if_comb = {
+       .limits = ath10k_if_limits,
+       .n_limits = ARRAY_SIZE(ath10k_if_limits),
+       .max_interfaces = 8,
+       .num_different_channels = 1,
+       .beacon_int_infra_match = true,
+};
+
+static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
+{
+       struct ieee80211_sta_vht_cap vht_cap = {0};
+       u16 mcs_map;
+
+       vht_cap.vht_supported = 1;
+       vht_cap.cap = ar->vht_cap_info;
+
+       /* FIXME: check dynamically how many streams board supports */
+       mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
+               IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
+               IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
+               IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
+               IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
+               IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
+               IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
+               IEEE80211_VHT_MCS_NOT_SUPPORTED << 14;
+
+       vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
+       vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
+
+       return vht_cap;
+}
+
+static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar)
+{
+       int i;
+       struct ieee80211_sta_ht_cap ht_cap = {0};
+
+       if (!(ar->ht_cap_info & WMI_HT_CAP_ENABLED))
+               return ht_cap;
+
+       ht_cap.ht_supported = 1;
+       ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+       ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
+       ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
+       ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI)
+               ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_HT40_SGI)
+               ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) {
+               u32 smps;
+
+               smps   = WLAN_HT_CAP_SM_PS_DYNAMIC;
+               smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+               ht_cap.cap |= smps;
+       }
+
+       if (ar->ht_cap_info & WMI_HT_CAP_TX_STBC)
+               ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_RX_STBC) {
+               u32 stbc;
+
+               stbc   = ar->ht_cap_info;
+               stbc  &= WMI_HT_CAP_RX_STBC;
+               stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT;
+               stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT;
+               stbc  &= IEEE80211_HT_CAP_RX_STBC;
+
+               ht_cap.cap |= stbc;
+       }
+
+       if (ar->ht_cap_info & WMI_HT_CAP_LDPC)
+               ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_L_SIG_TXOP_PROT)
+               ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT;
+
+       /* max AMSDU is implicitly taken from vht_cap_info */
+       if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK)
+               ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+       for (i = 0; i < WMI_MAX_SPATIAL_STREAM; i++)
+               ht_cap.mcs.rx_mask[i] = 0xFF;
+
+       ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
+
+       return ht_cap;
+}
+
+
+static void ath10k_get_arvif_iter(void *data, u8 *mac,
+                                 struct ieee80211_vif *vif)
+{
+       struct ath10k_vif_iter *arvif_iter = data;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+       if (arvif->vdev_id == arvif_iter->vdev_id)
+               arvif_iter->arvif = arvif;
+}
+
+struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id)
+{
+       struct ath10k_vif_iter arvif_iter;
+       u32 flags;
+
+       memset(&arvif_iter, 0, sizeof(struct ath10k_vif_iter));
+       arvif_iter.vdev_id = vdev_id;
+
+       flags = IEEE80211_IFACE_ITER_RESUME_ALL;
+       ieee80211_iterate_active_interfaces_atomic(ar->hw,
+                                                  flags,
+                                                  ath10k_get_arvif_iter,
+                                                  &arvif_iter);
+       if (!arvif_iter.arvif) {
+               ath10k_warn("No VIF found for VDEV: %d\n", vdev_id);
+               return NULL;
+       }
+
+       return arvif_iter.arvif;
+}
+
+int ath10k_mac_register(struct ath10k *ar)
+{
+       struct ieee80211_supported_band *band;
+       struct ieee80211_sta_vht_cap vht_cap;
+       struct ieee80211_sta_ht_cap ht_cap;
+       void *channels;
+       int ret;
+
+       SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr);
+
+       SET_IEEE80211_DEV(ar->hw, ar->dev);
+
+       ht_cap = ath10k_get_ht_cap(ar);
+       vht_cap = ath10k_create_vht_cap(ar);
+
+       if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
+               channels = kmemdup(ath10k_2ghz_channels,
+                                  sizeof(ath10k_2ghz_channels),
+                                  GFP_KERNEL);
+               if (!channels)
+                       return -ENOMEM;
+
+               band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
+               band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
+               band->channels = channels;
+               band->n_bitrates = ath10k_g_rates_size;
+               band->bitrates = ath10k_g_rates;
+               band->ht_cap = ht_cap;
+
+               /* vht is not supported in 2.4 GHz */
+
+               ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+       }
+
+       if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) {
+               channels = kmemdup(ath10k_5ghz_channels,
+                                  sizeof(ath10k_5ghz_channels),
+                                  GFP_KERNEL);
+               if (!channels) {
+                       if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
+                               band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
+                               kfree(band->channels);
+                       }
+                       return -ENOMEM;
+               }
+
+               band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
+               band->n_channels = ARRAY_SIZE(ath10k_5ghz_channels);
+               band->channels = channels;
+               band->n_bitrates = ath10k_a_rates_size;
+               band->bitrates = ath10k_a_rates;
+               band->ht_cap = ht_cap;
+               band->vht_cap = vht_cap;
+               ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band;
+       }
+
+       ar->hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_ADHOC) |
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_P2P_CLIENT) |
+               BIT(NL80211_IFTYPE_P2P_GO);
+
+       ar->hw->flags = IEEE80211_HW_SIGNAL_DBM |
+                       IEEE80211_HW_SUPPORTS_PS |
+                       IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+                       IEEE80211_HW_SUPPORTS_UAPSD |
+                       IEEE80211_HW_MFP_CAPABLE |
+                       IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+                       IEEE80211_HW_HAS_RATE_CONTROL |
+                       IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+                       IEEE80211_HW_WANT_MONITOR_VIF |
+                       IEEE80211_HW_AP_LINK_PS;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
+               ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_ENABLED) {
+               ar->hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+               ar->hw->flags |= IEEE80211_HW_TX_AMPDU_SETUP_IN_HW;
+       }
+
+       ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID;
+       ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN;
+
+       ar->hw->vif_data_size = sizeof(struct ath10k_vif);
+
+       ar->hw->channel_change_time = 5000;
+       ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
+
+       ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+       ar->hw->wiphy->max_remain_on_channel_duration = 5000;
+
+       ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+       /*
+        * on LL hardware queues are managed entirely by the FW
+        * so we only advertise to mac we can do the queues thing
+        */
+       ar->hw->queues = 4;
+
+       ar->hw->wiphy->iface_combinations = &ath10k_if_comb;
+       ar->hw->wiphy->n_iface_combinations = 1;
+
+       ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
+                           ath10k_reg_notifier);
+       if (ret) {
+               ath10k_err("Regulatory initialization failed\n");
+               return ret;
+       }
+
+       ret = ieee80211_register_hw(ar->hw);
+       if (ret) {
+               ath10k_err("ieee80211 registration failed: %d\n", ret);
+               return ret;
+       }
+
+       if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
+               ret = regulatory_hint(ar->hw->wiphy,
+                                     ar->ath_common.regulatory.alpha2);
+               if (ret)
+                       goto exit;
+       }
+
+       return 0;
+exit:
+       ieee80211_unregister_hw(ar->hw);
+       return ret;
+}
+
+void ath10k_mac_unregister(struct ath10k *ar)
+{
+       ieee80211_unregister_hw(ar->hw);
+
+       kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
+       kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
+
+       SET_IEEE80211_DEV(ar->hw, NULL);
+}
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
new file mode 100644 (file)
index 0000000..27fc92e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _MAC_H_
+#define _MAC_H_
+
+#include <net/mac80211.h>
+#include "core.h"
+
+struct ath10k_generic_iter {
+       struct ath10k *ar;
+       int ret;
+};
+
+struct ath10k *ath10k_mac_create(void);
+void ath10k_mac_destroy(struct ath10k *ar);
+int ath10k_mac_register(struct ath10k *ar);
+void ath10k_mac_unregister(struct ath10k *ar);
+struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
+void ath10k_reset_scan(unsigned long ptr);
+void ath10k_offchan_tx_purge(struct ath10k *ar);
+void ath10k_offchan_tx_work(struct work_struct *work);
+
+static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
+{
+       return (struct ath10k_vif *)vif->drv_priv;
+}
+
+static inline void ath10k_tx_h_seq_no(struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+       if (info->flags  & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+               if (arvif->tx_seq_no == 0)
+                       arvif->tx_seq_no = 0x1000;
+
+               if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
+                       arvif->tx_seq_no += 0x10;
+               hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+               hdr->seq_ctrl |= cpu_to_le16(arvif->tx_seq_no);
+       }
+}
+
+#endif /* _MAC_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
new file mode 100644 (file)
index 0000000..33af467
--- /dev/null
@@ -0,0 +1,2507 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include "core.h"
+#include "debug.h"
+
+#include "targaddrs.h"
+#include "bmi.h"
+
+#include "hif.h"
+#include "htc.h"
+
+#include "ce.h"
+#include "pci.h"
+
+unsigned int ath10k_target_ps;
+module_param(ath10k_target_ps, uint, 0644);
+MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option");
+
+#define QCA988X_1_0_DEVICE_ID  (0xabcd)
+#define QCA988X_2_0_DEVICE_ID  (0x003c)
+
+static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = {
+       { PCI_VDEVICE(ATHEROS, QCA988X_1_0_DEVICE_ID) }, /* PCI-E QCA988X V1 */
+       { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */
+       {0}
+};
+
+static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
+                                      u32 *data);
+
+static void ath10k_pci_process_ce(struct ath10k *ar);
+static int ath10k_pci_post_rx(struct ath10k *ar);
+static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
+                                            int num);
+static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
+static void ath10k_pci_stop_ce(struct ath10k *ar);
+
+static const struct ce_attr host_ce_config_wlan[] = {
+       /* host->target HTC control and raw streams */
+       { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,},
+       /* could be moved to share CE3 */
+       /* target->host HTT + HTC control */
+       { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 512, 512, NULL,},
+       /* target->host WMI */
+       { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,},
+       /* host->target WMI */
+       { /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,},
+       /* host->target HTT */
+       { /* CE4 */ CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, 0,
+                   CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,},
+       /* unused */
+       { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+       /* Target autonomous hif_memcpy */
+       { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+       /* ce_diag, the Diagnostic Window */
+       { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+};
+
+/* Target firmware's Copy Engine configuration. */
+static const struct ce_pipe_config target_ce_config_wlan[] = {
+       /* host->target HTC control and raw streams */
+       { /* CE0 */ 0, PIPEDIR_OUT, 32, 256, CE_ATTR_FLAGS, 0,},
+       /* target->host HTT + HTC control */
+       { /* CE1 */ 1, PIPEDIR_IN, 32, 512, CE_ATTR_FLAGS, 0,},
+       /* target->host WMI */
+       { /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,},
+       /* host->target WMI */
+       { /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
+       /* host->target HTT */
+       { /* CE4 */ 4, PIPEDIR_OUT, 256, 256, CE_ATTR_FLAGS, 0,},
+       /* NB: 50% of src nentries, since tx has 2 frags */
+       /* unused */
+       { /* CE5 */ 5, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
+       /* Reserved for target autonomous hif_memcpy */
+       { /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,},
+       /* CE7 used only by Host */
+};
+
+/*
+ * Diagnostic read/write access is provided for startup/config/debug usage.
+ * Caller must guarantee proper alignment, when applicable, and single user
+ * at any moment.
+ */
+static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
+                                   int nbytes)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret = 0;
+       u32 buf;
+       unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+       unsigned int id;
+       unsigned int flags;
+       struct ce_state *ce_diag;
+       /* Host buffer address in CE space */
+       u32 ce_data;
+       dma_addr_t ce_data_base = 0;
+       void *data_buf = NULL;
+       int i;
+
+       /*
+        * This code cannot handle reads to non-memory space. Redirect to the
+        * register read fn but preserve the multi word read capability of
+        * this fn
+        */
+       if (address < DRAM_BASE_ADDRESS) {
+               if (!IS_ALIGNED(address, 4) ||
+                   !IS_ALIGNED((unsigned long)data, 4))
+                       return -EIO;
+
+               while ((nbytes >= 4) &&  ((ret = ath10k_pci_diag_read_access(
+                                          ar, address, (u32 *)data)) == 0)) {
+                       nbytes -= sizeof(u32);
+                       address += sizeof(u32);
+                       data += sizeof(u32);
+               }
+               return ret;
+       }
+
+       ce_diag = ar_pci->ce_diag;
+
+       /*
+        * Allocate a temporary bounce buffer to hold caller's data
+        * to be DMA'ed from Target. This guarantees
+        *   1) 4-byte alignment
+        *   2) Buffer in DMA-able space
+        */
+       orig_nbytes = nbytes;
+       data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
+                                                        orig_nbytes,
+                                                        &ce_data_base);
+
+       if (!data_buf) {
+               ret = -ENOMEM;
+               goto done;
+       }
+       memset(data_buf, 0, orig_nbytes);
+
+       remaining_bytes = orig_nbytes;
+       ce_data = ce_data_base;
+       while (remaining_bytes) {
+               nbytes = min_t(unsigned int, remaining_bytes,
+                              DIAG_TRANSFER_LIMIT);
+
+               ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, ce_data);
+               if (ret != 0)
+                       goto done;
+
+               /* Request CE to send from Target(!) address to Host buffer */
+               /*
+                * The address supplied by the caller is in the
+                * Target CPU virtual address space.
+                *
+                * In order to use this address with the diagnostic CE,
+                * convert it from Target CPU virtual address space
+                * to CE address space
+                */
+               ath10k_pci_wake(ar);
+               address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem,
+                                                    address);
+               ath10k_pci_sleep(ar);
+
+               ret = ath10k_ce_send(ce_diag, NULL, (u32)address, nbytes, 0,
+                                0);
+               if (ret)
+                       goto done;
+
+               i = 0;
+               while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id) != 0) {
+                       mdelay(1);
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != (u32) address) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               i = 0;
+               while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id, &flags) != 0) {
+                       mdelay(1);
+
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != ce_data) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               remaining_bytes -= nbytes;
+               address += nbytes;
+               ce_data += nbytes;
+       }
+
+done:
+       if (ret == 0) {
+               /* Copy data from allocated DMA buf to caller's buf */
+               WARN_ON_ONCE(orig_nbytes & 3);
+               for (i = 0; i < orig_nbytes / sizeof(__le32); i++) {
+                       ((u32 *)data)[i] =
+                               __le32_to_cpu(((__le32 *)data_buf)[i]);
+               }
+       } else
+               ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n",
+                          __func__, address);
+
+       if (data_buf)
+               pci_free_consistent(ar_pci->pdev, orig_nbytes,
+                                   data_buf, ce_data_base);
+
+       return ret;
+}
+
+/* Read 4-byte aligned data from Target memory or register */
+static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
+                                      u32 *data)
+{
+       /* Assume range doesn't cross this boundary */
+       if (address >= DRAM_BASE_ADDRESS)
+               return ath10k_pci_diag_read_mem(ar, address, data, sizeof(u32));
+
+       ath10k_pci_wake(ar);
+       *data = ath10k_pci_read32(ar, address);
+       ath10k_pci_sleep(ar);
+       return 0;
+}
+
+static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
+                                    const void *data, int nbytes)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret = 0;
+       u32 buf;
+       unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+       unsigned int id;
+       unsigned int flags;
+       struct ce_state *ce_diag;
+       void *data_buf = NULL;
+       u32 ce_data;    /* Host buffer address in CE space */
+       dma_addr_t ce_data_base = 0;
+       int i;
+
+       ce_diag = ar_pci->ce_diag;
+
+       /*
+        * Allocate a temporary bounce buffer to hold caller's data
+        * to be DMA'ed to Target. This guarantees
+        *   1) 4-byte alignment
+        *   2) Buffer in DMA-able space
+        */
+       orig_nbytes = nbytes;
+       data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
+                                                        orig_nbytes,
+                                                        &ce_data_base);
+       if (!data_buf) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       /* Copy caller's data to allocated DMA buf */
+       WARN_ON_ONCE(orig_nbytes & 3);
+       for (i = 0; i < orig_nbytes / sizeof(__le32); i++)
+               ((__le32 *)data_buf)[i] = __cpu_to_le32(((u32 *)data)[i]);
+
+       /*
+        * The address supplied by the caller is in the
+        * Target CPU virtual address space.
+        *
+        * In order to use this address with the diagnostic CE,
+        * convert it from
+        *    Target CPU virtual address space
+        * to
+        *    CE address space
+        */
+       ath10k_pci_wake(ar);
+       address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem, address);
+       ath10k_pci_sleep(ar);
+
+       remaining_bytes = orig_nbytes;
+       ce_data = ce_data_base;
+       while (remaining_bytes) {
+               /* FIXME: check cast */
+               nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT);
+
+               /* Set up to receive directly into Target(!) address */
+               ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, address);
+               if (ret != 0)
+                       goto done;
+
+               /*
+                * Request CE to send caller-supplied data that
+                * was copied to bounce buffer to Target(!) address.
+                */
+               ret = ath10k_ce_send(ce_diag, NULL, (u32) ce_data,
+                                    nbytes, 0, 0);
+               if (ret != 0)
+                       goto done;
+
+               i = 0;
+               while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id) != 0) {
+                       mdelay(1);
+
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != ce_data) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               i = 0;
+               while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id, &flags) != 0) {
+                       mdelay(1);
+
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != address) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               remaining_bytes -= nbytes;
+               address += nbytes;
+               ce_data += nbytes;
+       }
+
+done:
+       if (data_buf) {
+               pci_free_consistent(ar_pci->pdev, orig_nbytes, data_buf,
+                                   ce_data_base);
+       }
+
+       if (ret != 0)
+               ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", __func__,
+                          address);
+
+       return ret;
+}
+
+/* Write 4B data to Target memory or register */
+static int ath10k_pci_diag_write_access(struct ath10k *ar, u32 address,
+                                       u32 data)
+{
+       /* Assume range doesn't cross this boundary */
+       if (address >= DRAM_BASE_ADDRESS)
+               return ath10k_pci_diag_write_mem(ar, address, &data,
+                                                sizeof(u32));
+
+       ath10k_pci_wake(ar);
+       ath10k_pci_write32(ar, address, data);
+       ath10k_pci_sleep(ar);
+       return 0;
+}
+
+static bool ath10k_pci_target_is_awake(struct ath10k *ar)
+{
+       void __iomem *mem = ath10k_pci_priv(ar)->mem;
+       u32 val;
+       val = ioread32(mem + PCIE_LOCAL_BASE_ADDRESS +
+                      RTC_STATE_ADDRESS);
+       return (RTC_STATE_V_GET(val) == RTC_STATE_V_ON);
+}
+
+static void ath10k_pci_wait(struct ath10k *ar)
+{
+       int n = 100;
+
+       while (n-- && !ath10k_pci_target_is_awake(ar))
+               msleep(10);
+
+       if (n < 0)
+               ath10k_warn("Unable to wakeup target\n");
+}
+
+void ath10k_do_pci_wake(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       void __iomem *pci_addr = ar_pci->mem;
+       int tot_delay = 0;
+       int curr_delay = 5;
+
+       if (atomic_read(&ar_pci->keep_awake_count) == 0) {
+               /* Force AWAKE */
+               iowrite32(PCIE_SOC_WAKE_V_MASK,
+                         pci_addr + PCIE_LOCAL_BASE_ADDRESS +
+                         PCIE_SOC_WAKE_ADDRESS);
+       }
+       atomic_inc(&ar_pci->keep_awake_count);
+
+       if (ar_pci->verified_awake)
+               return;
+
+       for (;;) {
+               if (ath10k_pci_target_is_awake(ar)) {
+                       ar_pci->verified_awake = true;
+                       break;
+               }
+
+               if (tot_delay > PCIE_WAKE_TIMEOUT) {
+                       ath10k_warn("target takes too long to wake up (awake count %d)\n",
+                                   atomic_read(&ar_pci->keep_awake_count));
+                       break;
+               }
+
+               udelay(curr_delay);
+               tot_delay += curr_delay;
+
+               if (curr_delay < 50)
+                       curr_delay += 5;
+       }
+}
+
+void ath10k_do_pci_sleep(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       void __iomem *pci_addr = ar_pci->mem;
+
+       if (atomic_dec_and_test(&ar_pci->keep_awake_count)) {
+               /* Allow sleep */
+               ar_pci->verified_awake = false;
+               iowrite32(PCIE_SOC_WAKE_RESET,
+                         pci_addr + PCIE_LOCAL_BASE_ADDRESS +
+                         PCIE_SOC_WAKE_ADDRESS);
+       }
+}
+
+/*
+ * FIXME: Handle OOM properly.
+ */
+static inline
+struct ath10k_pci_compl *get_free_compl(struct hif_ce_pipe_info *pipe_info)
+{
+       struct ath10k_pci_compl *compl = NULL;
+
+       spin_lock_bh(&pipe_info->pipe_lock);
+       if (list_empty(&pipe_info->compl_free)) {
+               ath10k_warn("Completion buffers are full\n");
+               goto exit;
+       }
+       compl = list_first_entry(&pipe_info->compl_free,
+                                struct ath10k_pci_compl, list);
+       list_del(&compl->list);
+exit:
+       spin_unlock_bh(&pipe_info->pipe_lock);
+       return compl;
+}
+
+/* Called by lower (CE) layer when a send to Target completes. */
+static void ath10k_pci_ce_send_done(struct ce_state *ce_state,
+                                   void *transfer_context,
+                                   u32 ce_data,
+                                   unsigned int nbytes,
+                                   unsigned int transfer_id)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
+       struct ath10k_pci_compl *compl;
+       bool process = false;
+
+       do {
+               /*
+                * For the send completion of an item in sendlist, just
+                * increment num_sends_allowed. The upper layer callback will
+                * be triggered when last fragment is done with send.
+                */
+               if (transfer_context == CE_SENDLIST_ITEM_CTXT) {
+                       spin_lock_bh(&pipe_info->pipe_lock);
+                       pipe_info->num_sends_allowed++;
+                       spin_unlock_bh(&pipe_info->pipe_lock);
+                       continue;
+               }
+
+               compl = get_free_compl(pipe_info);
+               if (!compl)
+                       break;
+
+               compl->send_or_recv = HIF_CE_COMPLETE_SEND;
+               compl->ce_state = ce_state;
+               compl->pipe_info = pipe_info;
+               compl->transfer_context = transfer_context;
+               compl->nbytes = nbytes;
+               compl->transfer_id = transfer_id;
+               compl->flags = 0;
+
+               /*
+                * Add the completion to the processing queue.
+                */
+               spin_lock_bh(&ar_pci->compl_lock);
+               list_add_tail(&compl->list, &ar_pci->compl_process);
+               spin_unlock_bh(&ar_pci->compl_lock);
+
+               process = true;
+       } while (ath10k_ce_completed_send_next(ce_state,
+                                                          &transfer_context,
+                                                          &ce_data, &nbytes,
+                                                          &transfer_id) == 0);
+
+       /*
+        * If only some of the items within a sendlist have completed,
+        * don't invoke completion processing until the entire sendlist
+        * has been sent.
+        */
+       if (!process)
+               return;
+
+       ath10k_pci_process_ce(ar);
+}
+
+/* Called by lower (CE) layer when data is received from the Target. */
+static void ath10k_pci_ce_recv_data(struct ce_state *ce_state,
+                                   void *transfer_context, u32 ce_data,
+                                   unsigned int nbytes,
+                                   unsigned int transfer_id,
+                                   unsigned int flags)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
+       struct ath10k_pci_compl *compl;
+       struct sk_buff *skb;
+
+       do {
+               compl = get_free_compl(pipe_info);
+               if (!compl)
+                       break;
+
+               compl->send_or_recv = HIF_CE_COMPLETE_RECV;
+               compl->ce_state = ce_state;
+               compl->pipe_info = pipe_info;
+               compl->transfer_context = transfer_context;
+               compl->nbytes = nbytes;
+               compl->transfer_id = transfer_id;
+               compl->flags = flags;
+
+               skb = transfer_context;
+               dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               /*
+                * Add the completion to the processing queue.
+                */
+               spin_lock_bh(&ar_pci->compl_lock);
+               list_add_tail(&compl->list, &ar_pci->compl_process);
+               spin_unlock_bh(&ar_pci->compl_lock);
+
+       } while (ath10k_ce_completed_recv_next(ce_state,
+                                                          &transfer_context,
+                                                          &ce_data, &nbytes,
+                                                          &transfer_id,
+                                                          &flags) == 0);
+
+       ath10k_pci_process_ce(ar);
+}
+
+/* Send the first nbytes bytes of the buffer */
+static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
+                                   unsigned int transfer_id,
+                                   unsigned int bytes, struct sk_buff *nbuf)
+{
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(nbuf);
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe_id]);
+       struct ce_state *ce_hdl = pipe_info->ce_hdl;
+       struct ce_sendlist sendlist;
+       unsigned int len;
+       u32 flags = 0;
+       int ret;
+
+       memset(&sendlist, 0, sizeof(struct ce_sendlist));
+
+       len = min(bytes, nbuf->len);
+       bytes -= len;
+
+       if (len & 3)
+               ath10k_warn("skb not aligned to 4-byte boundary (%d)\n", len);
+
+       ath10k_dbg(ATH10K_DBG_PCI,
+                  "pci send data vaddr %p paddr 0x%llx len %d as %d bytes\n",
+                  nbuf->data, (unsigned long long) skb_cb->paddr,
+                  nbuf->len, len);
+       ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
+                       "ath10k tx: data: ",
+                       nbuf->data, nbuf->len);
+
+       ath10k_ce_sendlist_buf_add(&sendlist, skb_cb->paddr, len, flags);
+
+       /* Make sure we have resources to handle this request */
+       spin_lock_bh(&pipe_info->pipe_lock);
+       if (!pipe_info->num_sends_allowed) {
+               ath10k_warn("Pipe: %d is full\n", pipe_id);
+               spin_unlock_bh(&pipe_info->pipe_lock);
+               return -ENOSR;
+       }
+       pipe_info->num_sends_allowed--;
+       spin_unlock_bh(&pipe_info->pipe_lock);
+
+       ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id);
+       if (ret)
+               ath10k_warn("CE send failed: %p\n", nbuf);
+
+       return ret;
+}
+
+static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe]);
+       int ret;
+
+       spin_lock_bh(&pipe_info->pipe_lock);
+       ret = pipe_info->num_sends_allowed;
+       spin_unlock_bh(&pipe_info->pipe_lock);
+
+       return ret;
+}
+
+static void ath10k_pci_hif_dump_area(struct ath10k *ar)
+{
+       u32 reg_dump_area = 0;
+       u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {};
+       u32 host_addr;
+       int ret;
+       u32 i;
+
+       ath10k_err("firmware crashed!\n");
+       ath10k_err("hardware name %s version 0x%x\n",
+                  ar->hw_params.name, ar->target_version);
+       ath10k_err("firmware version: %u.%u.%u.%u\n", ar->fw_version_major,
+                  ar->fw_version_minor, ar->fw_version_release,
+                  ar->fw_version_build);
+
+       host_addr = host_interest_item_address(HI_ITEM(hi_failure_state));
+       if (ath10k_pci_diag_read_mem(ar, host_addr,
+                                    &reg_dump_area, sizeof(u32)) != 0) {
+               ath10k_warn("could not read hi_failure_state\n");
+               return;
+       }
+
+       ath10k_err("target register Dump Location: 0x%08X\n", reg_dump_area);
+
+       ret = ath10k_pci_diag_read_mem(ar, reg_dump_area,
+                                      &reg_dump_values[0],
+                                      REG_DUMP_COUNT_QCA988X * sizeof(u32));
+       if (ret != 0) {
+               ath10k_err("could not dump FW Dump Area\n");
+               return;
+       }
+
+       BUILD_BUG_ON(REG_DUMP_COUNT_QCA988X % 4);
+
+       ath10k_err("target Register Dump\n");
+       for (i = 0; i < REG_DUMP_COUNT_QCA988X; i += 4)
+               ath10k_err("[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n",
+                          i,
+                          reg_dump_values[i],
+                          reg_dump_values[i + 1],
+                          reg_dump_values[i + 2],
+                          reg_dump_values[i + 3]);
+}
+
+static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
+                                              int force)
+{
+       if (!force) {
+               int resources;
+               /*
+                * Decide whether to actually poll for completions, or just
+                * wait for a later chance.
+                * If there seem to be plenty of resources left, then just wait
+                * since checking involves reading a CE register, which is a
+                * relatively expensive operation.
+                */
+               resources = ath10k_pci_hif_get_free_queue_number(ar, pipe);
+
+               /*
+                * If at least 50% of the total resources are still available,
+                * don't bother checking again yet.
+                */
+               if (resources > (host_ce_config_wlan[pipe].src_nentries >> 1))
+                       return;
+       }
+       ath10k_ce_per_engine_service(ar, pipe);
+}
+
+static void ath10k_pci_hif_post_init(struct ath10k *ar,
+                                    struct ath10k_hif_cb *callbacks)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       memcpy(&ar_pci->msg_callbacks_current, callbacks,
+              sizeof(ar_pci->msg_callbacks_current));
+}
+
+static int ath10k_pci_start_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_diag = ar_pci->ce_diag;
+       const struct ce_attr *attr;
+       struct hif_ce_pipe_info *pipe_info;
+       struct ath10k_pci_compl *compl;
+       int i, pipe_num, completions, disable_interrupts;
+
+       spin_lock_init(&ar_pci->compl_lock);
+       INIT_LIST_HEAD(&ar_pci->compl_process);
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+
+               spin_lock_init(&pipe_info->pipe_lock);
+               INIT_LIST_HEAD(&pipe_info->compl_free);
+
+               /* Handle Diagnostic CE specially */
+               if (pipe_info->ce_hdl == ce_diag)
+                       continue;
+
+               attr = &host_ce_config_wlan[pipe_num];
+               completions = 0;
+
+               if (attr->src_nentries) {
+                       disable_interrupts = attr->flags & CE_ATTR_DIS_INTR;
+                       ath10k_ce_send_cb_register(pipe_info->ce_hdl,
+                                                  ath10k_pci_ce_send_done,
+                                                  disable_interrupts);
+                       completions += attr->src_nentries;
+                       pipe_info->num_sends_allowed = attr->src_nentries - 1;
+               }
+
+               if (attr->dest_nentries) {
+                       ath10k_ce_recv_cb_register(pipe_info->ce_hdl,
+                                                  ath10k_pci_ce_recv_data);
+                       completions += attr->dest_nentries;
+               }
+
+               if (completions == 0)
+                       continue;
+
+               for (i = 0; i < completions; i++) {
+                       compl = kmalloc(sizeof(struct ath10k_pci_compl),
+                                       GFP_KERNEL);
+                       if (!compl) {
+                               ath10k_warn("No memory for completion state\n");
+                               ath10k_pci_stop_ce(ar);
+                               return -ENOMEM;
+                       }
+
+                       compl->send_or_recv = HIF_CE_COMPLETE_FREE;
+                       list_add_tail(&compl->list, &pipe_info->compl_free);
+               }
+       }
+
+       return 0;
+}
+
+static void ath10k_pci_stop_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_pci_compl *compl;
+       struct sk_buff *skb;
+       int i;
+
+       ath10k_ce_disable_interrupts(ar);
+
+       /* Cancel the pending tasklet */
+       tasklet_kill(&ar_pci->intr_tq);
+
+       for (i = 0; i < CE_COUNT; i++)
+               tasklet_kill(&ar_pci->pipe_info[i].intr);
+
+       /* Mark pending completions as aborted, so that upper layers free up
+        * their associated resources */
+       spin_lock_bh(&ar_pci->compl_lock);
+       list_for_each_entry(compl, &ar_pci->compl_process, list) {
+               skb = (struct sk_buff *)compl->transfer_context;
+               ATH10K_SKB_CB(skb)->is_aborted = true;
+       }
+       spin_unlock_bh(&ar_pci->compl_lock);
+}
+
+static void ath10k_pci_cleanup_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_pci_compl *compl, *tmp;
+       struct hif_ce_pipe_info *pipe_info;
+       struct sk_buff *netbuf;
+       int pipe_num;
+
+       /* Free pending completions. */
+       spin_lock_bh(&ar_pci->compl_lock);
+       if (!list_empty(&ar_pci->compl_process))
+               ath10k_warn("pending completions still present! possible memory leaks.\n");
+
+       list_for_each_entry_safe(compl, tmp, &ar_pci->compl_process, list) {
+               list_del(&compl->list);
+               netbuf = (struct sk_buff *)compl->transfer_context;
+               dev_kfree_skb_any(netbuf);
+               kfree(compl);
+       }
+       spin_unlock_bh(&ar_pci->compl_lock);
+
+       /* Free unused completions for each pipe. */
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+
+               spin_lock_bh(&pipe_info->pipe_lock);
+               list_for_each_entry_safe(compl, tmp,
+                                        &pipe_info->compl_free, list) {
+                       list_del(&compl->list);
+                       kfree(compl);
+               }
+               spin_unlock_bh(&pipe_info->pipe_lock);
+       }
+}
+
+static void ath10k_pci_process_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ar->hif.priv;
+       struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
+       struct ath10k_pci_compl *compl;
+       struct sk_buff *skb;
+       unsigned int nbytes;
+       int ret, send_done = 0;
+
+       /* Upper layers aren't ready to handle tx/rx completions in parallel so
+        * we must serialize all completion processing. */
+
+       spin_lock_bh(&ar_pci->compl_lock);
+       if (ar_pci->compl_processing) {
+               spin_unlock_bh(&ar_pci->compl_lock);
+               return;
+       }
+       ar_pci->compl_processing = true;
+       spin_unlock_bh(&ar_pci->compl_lock);
+
+       for (;;) {
+               spin_lock_bh(&ar_pci->compl_lock);
+               if (list_empty(&ar_pci->compl_process)) {
+                       spin_unlock_bh(&ar_pci->compl_lock);
+                       break;
+               }
+               compl = list_first_entry(&ar_pci->compl_process,
+                                        struct ath10k_pci_compl, list);
+               list_del(&compl->list);
+               spin_unlock_bh(&ar_pci->compl_lock);
+
+               if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) {
+                       cb->tx_completion(ar,
+                                         compl->transfer_context,
+                                         compl->transfer_id);
+                       send_done = 1;
+               } else {
+                       ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1);
+                       if (ret) {
+                               ath10k_warn("Unable to post recv buffer for pipe: %d\n",
+                                           compl->pipe_info->pipe_num);
+                               break;
+                       }
+
+                       skb = (struct sk_buff *)compl->transfer_context;
+                       nbytes = compl->nbytes;
+
+                       ath10k_dbg(ATH10K_DBG_PCI,
+                                  "ath10k_pci_ce_recv_data netbuf=%p  nbytes=%d\n",
+                                  skb, nbytes);
+                       ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
+                                       "ath10k rx: ", skb->data, nbytes);
+
+                       if (skb->len + skb_tailroom(skb) >= nbytes) {
+                               skb_trim(skb, 0);
+                               skb_put(skb, nbytes);
+                               cb->rx_completion(ar, skb,
+                                                 compl->pipe_info->pipe_num);
+                       } else {
+                               ath10k_warn("rxed more than expected (nbytes %d, max %d)",
+                                           nbytes,
+                                           skb->len + skb_tailroom(skb));
+                       }
+               }
+
+               compl->send_or_recv = HIF_CE_COMPLETE_FREE;
+
+               /*
+                * Add completion back to the pipe's free list.
+                */
+               spin_lock_bh(&compl->pipe_info->pipe_lock);
+               list_add_tail(&compl->list, &compl->pipe_info->compl_free);
+               compl->pipe_info->num_sends_allowed += send_done;
+               spin_unlock_bh(&compl->pipe_info->pipe_lock);
+       }
+
+       spin_lock_bh(&ar_pci->compl_lock);
+       ar_pci->compl_processing = false;
+       spin_unlock_bh(&ar_pci->compl_lock);
+}
+
+/* TODO - temporary mapping while we have too few CE's */
+static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar,
+                                             u16 service_id, u8 *ul_pipe,
+                                             u8 *dl_pipe, int *ul_is_polled,
+                                             int *dl_is_polled)
+{
+       int ret = 0;
+
+       /* polling for received messages not supported */
+       *dl_is_polled = 0;
+
+       switch (service_id) {
+       case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
+               /*
+                * Host->target HTT gets its own pipe, so it can be polled
+                * while other pipes are interrupt driven.
+                */
+               *ul_pipe = 4;
+               /*
+                * Use the same target->host pipe for HTC ctrl, HTC raw
+                * streams, and HTT.
+                */
+               *dl_pipe = 1;
+               break;
+
+       case ATH10K_HTC_SVC_ID_RSVD_CTRL:
+       case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS:
+               /*
+                * Note: HTC_RAW_STREAMS_SVC is currently unused, and
+                * HTC_CTRL_RSVD_SVC could share the same pipe as the
+                * WMI services.  So, if another CE is needed, change
+                * this to *ul_pipe = 3, which frees up CE 0.
+                */
+               /* *ul_pipe = 3; */
+               *ul_pipe = 0;
+               *dl_pipe = 1;
+               break;
+
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BK:
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BE:
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VI:
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VO:
+
+       case ATH10K_HTC_SVC_ID_WMI_CONTROL:
+               *ul_pipe = 3;
+               *dl_pipe = 2;
+               break;
+
+               /* pipe 5 unused   */
+               /* pipe 6 reserved */
+               /* pipe 7 reserved */
+
+       default:
+               ret = -1;
+               break;
+       }
+       *ul_is_polled =
+               (host_ce_config_wlan[*ul_pipe].flags & CE_ATTR_DIS_INTR) != 0;
+
+       return ret;
+}
+
+static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
+                                               u8 *ul_pipe, u8 *dl_pipe)
+{
+       int ul_is_polled, dl_is_polled;
+
+       (void)ath10k_pci_hif_map_service_to_pipe(ar,
+                                                ATH10K_HTC_SVC_ID_RSVD_CTRL,
+                                                ul_pipe,
+                                                dl_pipe,
+                                                &ul_is_polled,
+                                                &dl_is_polled);
+}
+
+static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
+                                  int num)
+{
+       struct ath10k *ar = pipe_info->hif_ce_state;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_state = pipe_info->ce_hdl;
+       struct sk_buff *skb;
+       dma_addr_t ce_data;
+       int i, ret = 0;
+
+       if (pipe_info->buf_sz == 0)
+               return 0;
+
+       for (i = 0; i < num; i++) {
+               skb = dev_alloc_skb(pipe_info->buf_sz);
+               if (!skb) {
+                       ath10k_warn("could not allocate skbuff for pipe %d\n",
+                                   num);
+                       ret = -ENOMEM;
+                       goto err;
+               }
+
+               WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
+
+               ce_data = dma_map_single(ar->dev, skb->data,
+                                        skb->len + skb_tailroom(skb),
+                                        DMA_FROM_DEVICE);
+
+               if (unlikely(dma_mapping_error(ar->dev, ce_data))) {
+                       ath10k_warn("could not dma map skbuff\n");
+                       dev_kfree_skb_any(skb);
+                       ret = -EIO;
+                       goto err;
+               }
+
+               ATH10K_SKB_CB(skb)->paddr = ce_data;
+
+               pci_dma_sync_single_for_device(ar_pci->pdev, ce_data,
+                                              pipe_info->buf_sz,
+                                              PCI_DMA_FROMDEVICE);
+
+               ret = ath10k_ce_recv_buf_enqueue(ce_state, (void *)skb,
+                                                ce_data);
+               if (ret) {
+                       ath10k_warn("could not enqueue to pipe %d (%d)\n",
+                                   num, ret);
+                       goto err;
+               }
+       }
+
+       return ret;
+
+err:
+       ath10k_pci_rx_pipe_cleanup(pipe_info);
+       return ret;
+}
+
+static int ath10k_pci_post_rx(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info;
+       const struct ce_attr *attr;
+       int pipe_num, ret = 0;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               attr = &host_ce_config_wlan[pipe_num];
+
+               if (attr->dest_nentries == 0)
+                       continue;
+
+               ret = ath10k_pci_post_rx_pipe(pipe_info,
+                                             attr->dest_nentries - 1);
+               if (ret) {
+                       ath10k_warn("Unable to replenish recv buffers for pipe: %d\n",
+                                   pipe_num);
+
+                       for (; pipe_num >= 0; pipe_num--) {
+                               pipe_info = &ar_pci->pipe_info[pipe_num];
+                               ath10k_pci_rx_pipe_cleanup(pipe_info);
+                       }
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int ath10k_pci_hif_start(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       ret = ath10k_pci_start_ce(ar);
+       if (ret) {
+               ath10k_warn("could not start CE (%d)\n", ret);
+               return ret;
+       }
+
+       /* Post buffers once to start things off. */
+       ret = ath10k_pci_post_rx(ar);
+       if (ret) {
+               ath10k_warn("could not post rx pipes (%d)\n", ret);
+               return ret;
+       }
+
+       ar_pci->started = 1;
+       return 0;
+}
+
+static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+{
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+       struct ce_state *ce_hdl;
+       u32 buf_sz;
+       struct sk_buff *netbuf;
+       u32 ce_data;
+
+       buf_sz = pipe_info->buf_sz;
+
+       /* Unused Copy Engine */
+       if (buf_sz == 0)
+               return;
+
+       ar = pipe_info->hif_ce_state;
+       ar_pci = ath10k_pci_priv(ar);
+
+       if (!ar_pci->started)
+               return;
+
+       ce_hdl = pipe_info->ce_hdl;
+
+       while (ath10k_ce_revoke_recv_next(ce_hdl, (void **)&netbuf,
+                                         &ce_data) == 0) {
+               dma_unmap_single(ar->dev, ATH10K_SKB_CB(netbuf)->paddr,
+                                netbuf->len + skb_tailroom(netbuf),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(netbuf);
+       }
+}
+
+static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+{
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+       struct ce_state *ce_hdl;
+       struct sk_buff *netbuf;
+       u32 ce_data;
+       unsigned int nbytes;
+       unsigned int id;
+       u32 buf_sz;
+
+       buf_sz = pipe_info->buf_sz;
+
+       /* Unused Copy Engine */
+       if (buf_sz == 0)
+               return;
+
+       ar = pipe_info->hif_ce_state;
+       ar_pci = ath10k_pci_priv(ar);
+
+       if (!ar_pci->started)
+               return;
+
+       ce_hdl = pipe_info->ce_hdl;
+
+       while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf,
+                                         &ce_data, &nbytes, &id) == 0) {
+               if (netbuf != CE_SENDLIST_ITEM_CTXT)
+                       /*
+                        * Indicate the completion to higer layer to free
+                        * the buffer
+                        */
+                       ATH10K_SKB_CB(netbuf)->is_aborted = true;
+                       ar_pci->msg_callbacks_current.tx_completion(ar,
+                                                                   netbuf,
+                                                                   id);
+       }
+}
+
+/*
+ * Cleanup residual buffers for device shutdown:
+ *    buffers that were enqueued for receive
+ *    buffers that were to be sent
+ * Note: Buffers that had completed but which were
+ * not yet processed are on a completion queue. They
+ * are handled when the completion thread shuts down.
+ */
+static void ath10k_pci_buffer_cleanup(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int pipe_num;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               struct hif_ce_pipe_info *pipe_info;
+
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               ath10k_pci_rx_pipe_cleanup(pipe_info);
+               ath10k_pci_tx_pipe_cleanup(pipe_info);
+       }
+}
+
+static void ath10k_pci_ce_deinit(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info;
+       int pipe_num;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               if (pipe_info->ce_hdl) {
+                       ath10k_ce_deinit(pipe_info->ce_hdl);
+                       pipe_info->ce_hdl = NULL;
+                       pipe_info->buf_sz = 0;
+               }
+       }
+}
+
+static void ath10k_pci_hif_stop(struct ath10k *ar)
+{
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       ath10k_pci_stop_ce(ar);
+
+       /* At this point, asynchronous threads are stopped, the target should
+        * not DMA nor interrupt. We process the leftovers and then free
+        * everything else up. */
+
+       ath10k_pci_process_ce(ar);
+       ath10k_pci_cleanup_ce(ar);
+       ath10k_pci_buffer_cleanup(ar);
+       ath10k_pci_ce_deinit(ar);
+}
+
+static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
+                                          void *req, u32 req_len,
+                                          void *resp, u32 *resp_len)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_tx = ar_pci->pipe_info[BMI_CE_NUM_TO_TARG].ce_hdl;
+       struct ce_state *ce_rx = ar_pci->pipe_info[BMI_CE_NUM_TO_HOST].ce_hdl;
+       dma_addr_t req_paddr = 0;
+       dma_addr_t resp_paddr = 0;
+       struct bmi_xfer xfer = {};
+       void *treq, *tresp = NULL;
+       int ret = 0;
+
+       if (resp && !resp_len)
+               return -EINVAL;
+
+       if (resp && resp_len && *resp_len == 0)
+               return -EINVAL;
+
+       treq = kmemdup(req, req_len, GFP_KERNEL);
+       if (!treq)
+               return -ENOMEM;
+
+       req_paddr = dma_map_single(ar->dev, treq, req_len, DMA_TO_DEVICE);
+       ret = dma_mapping_error(ar->dev, req_paddr);
+       if (ret)
+               goto err_dma;
+
+       if (resp && resp_len) {
+               tresp = kzalloc(*resp_len, GFP_KERNEL);
+               if (!tresp) {
+                       ret = -ENOMEM;
+                       goto err_req;
+               }
+
+               resp_paddr = dma_map_single(ar->dev, tresp, *resp_len,
+                                           DMA_FROM_DEVICE);
+               ret = dma_mapping_error(ar->dev, resp_paddr);
+               if (ret)
+                       goto err_req;
+
+               xfer.wait_for_resp = true;
+               xfer.resp_len = 0;
+
+               ath10k_ce_recv_buf_enqueue(ce_rx, &xfer, resp_paddr);
+       }
+
+       init_completion(&xfer.done);
+
+       ret = ath10k_ce_send(ce_tx, &xfer, req_paddr, req_len, -1, 0);
+       if (ret)
+               goto err_resp;
+
+       ret = wait_for_completion_timeout(&xfer.done,
+                                         BMI_COMMUNICATION_TIMEOUT_HZ);
+       if (ret <= 0) {
+               u32 unused_buffer;
+               unsigned int unused_nbytes;
+               unsigned int unused_id;
+
+               ret = -ETIMEDOUT;
+               ath10k_ce_cancel_send_next(ce_tx, NULL, &unused_buffer,
+                                          &unused_nbytes, &unused_id);
+       } else {
+               /* non-zero means we did not time out */
+               ret = 0;
+       }
+
+err_resp:
+       if (resp) {
+               u32 unused_buffer;
+
+               ath10k_ce_revoke_recv_next(ce_rx, NULL, &unused_buffer);
+               dma_unmap_single(ar->dev, resp_paddr,
+                                *resp_len, DMA_FROM_DEVICE);
+       }
+err_req:
+       dma_unmap_single(ar->dev, req_paddr, req_len, DMA_TO_DEVICE);
+
+       if (ret == 0 && resp_len) {
+               *resp_len = min(*resp_len, xfer.resp_len);
+               memcpy(resp, tresp, xfer.resp_len);
+       }
+err_dma:
+       kfree(treq);
+       kfree(tresp);
+
+       return ret;
+}
+
+static void ath10k_pci_bmi_send_done(struct ce_state *ce_state,
+                                    void *transfer_context,
+                                    u32 data,
+                                    unsigned int nbytes,
+                                    unsigned int transfer_id)
+{
+       struct bmi_xfer *xfer = transfer_context;
+
+       if (xfer->wait_for_resp)
+               return;
+
+       complete(&xfer->done);
+}
+
+static void ath10k_pci_bmi_recv_data(struct ce_state *ce_state,
+                                    void *transfer_context,
+                                    u32 data,
+                                    unsigned int nbytes,
+                                    unsigned int transfer_id,
+                                    unsigned int flags)
+{
+       struct bmi_xfer *xfer = transfer_context;
+
+       if (!xfer->wait_for_resp) {
+               ath10k_warn("unexpected: BMI data received; ignoring\n");
+               return;
+       }
+
+       xfer->resp_len = nbytes;
+       complete(&xfer->done);
+}
+
+/*
+ * Map from service/endpoint to Copy Engine.
+ * This table is derived from the CE_PCI TABLE, above.
+ * It is passed to the Target at startup for use by firmware.
+ */
+static const struct service_to_pipe target_service_to_ce_map_wlan[] = {
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VO,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VO,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BK,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BK,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BE,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BE,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VI,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VI,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_CONTROL,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_CONTROL,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_RSVD_CTRL,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                0,             /* could be moved to 3 (share with WMI) */
+       },
+       {
+                ATH10K_HTC_SVC_ID_RSVD_CTRL,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                1,
+       },
+       {
+                ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS,    /* not currently used */
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                0,
+       },
+       {
+                ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS,    /* not currently used */
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                1,
+       },
+       {
+                ATH10K_HTC_SVC_ID_HTT_DATA_MSG,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                4,
+       },
+       {
+                ATH10K_HTC_SVC_ID_HTT_DATA_MSG,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                1,
+       },
+
+       /* (Additions here) */
+
+       {                               /* Must be last */
+                0,
+                0,
+                0,
+       },
+};
+
+/*
+ * Send an interrupt to the device to wake up the Target CPU
+ * so it has an opportunity to notice any changed state.
+ */
+static int ath10k_pci_wake_target_cpu(struct ath10k *ar)
+{
+       int ret;
+       u32 core_ctrl;
+
+       ret = ath10k_pci_diag_read_access(ar, SOC_CORE_BASE_ADDRESS |
+                                             CORE_CTRL_ADDRESS,
+                                         &core_ctrl);
+       if (ret) {
+               ath10k_warn("Unable to read core ctrl\n");
+               return ret;
+       }
+
+       /* A_INUM_FIRMWARE interrupt to Target CPU */
+       core_ctrl |= CORE_CTRL_CPU_INTR_MASK;
+
+       ret = ath10k_pci_diag_write_access(ar, SOC_CORE_BASE_ADDRESS |
+                                              CORE_CTRL_ADDRESS,
+                                          core_ctrl);
+       if (ret)
+               ath10k_warn("Unable to set interrupt mask\n");
+
+       return ret;
+}
+
+static int ath10k_pci_init_config(struct ath10k *ar)
+{
+       u32 interconnect_targ_addr;
+       u32 pcie_state_targ_addr = 0;
+       u32 pipe_cfg_targ_addr = 0;
+       u32 svc_to_pipe_map = 0;
+       u32 pcie_config_flags = 0;
+       u32 ealloc_value;
+       u32 ealloc_targ_addr;
+       u32 flag2_value;
+       u32 flag2_targ_addr;
+       int ret = 0;
+
+       /* Download to Target the CE Config and the service-to-CE map */
+       interconnect_targ_addr =
+               host_interest_item_address(HI_ITEM(hi_interconnect_state));
+
+       /* Supply Target-side CE configuration */
+       ret = ath10k_pci_diag_read_access(ar, interconnect_targ_addr,
+                                         &pcie_state_targ_addr);
+       if (ret != 0) {
+               ath10k_err("Failed to get pcie state addr: %d\n", ret);
+               return ret;
+       }
+
+       if (pcie_state_targ_addr == 0) {
+               ret = -EIO;
+               ath10k_err("Invalid pcie state addr\n");
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+                                         offsetof(struct pcie_state,
+                                                  pipe_cfg_addr),
+                                         &pipe_cfg_targ_addr);
+       if (ret != 0) {
+               ath10k_err("Failed to get pipe cfg addr: %d\n", ret);
+               return ret;
+       }
+
+       if (pipe_cfg_targ_addr == 0) {
+               ret = -EIO;
+               ath10k_err("Invalid pipe cfg addr\n");
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_write_mem(ar, pipe_cfg_targ_addr,
+                                target_ce_config_wlan,
+                                sizeof(target_ce_config_wlan));
+
+       if (ret != 0) {
+               ath10k_err("Failed to write pipe cfg: %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+                                         offsetof(struct pcie_state,
+                                                  svc_to_pipe_map),
+                                         &svc_to_pipe_map);
+       if (ret != 0) {
+               ath10k_err("Failed to get svc/pipe map: %d\n", ret);
+               return ret;
+       }
+
+       if (svc_to_pipe_map == 0) {
+               ret = -EIO;
+               ath10k_err("Invalid svc_to_pipe map\n");
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_write_mem(ar, svc_to_pipe_map,
+                                target_service_to_ce_map_wlan,
+                                sizeof(target_service_to_ce_map_wlan));
+       if (ret != 0) {
+               ath10k_err("Failed to write svc/pipe map: %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+                                         offsetof(struct pcie_state,
+                                                  config_flags),
+                                         &pcie_config_flags);
+       if (ret != 0) {
+               ath10k_err("Failed to get pcie config_flags: %d\n", ret);
+               return ret;
+       }
+
+       pcie_config_flags &= ~PCIE_CONFIG_FLAG_ENABLE_L1;
+
+       ret = ath10k_pci_diag_write_mem(ar, pcie_state_targ_addr +
+                                offsetof(struct pcie_state, config_flags),
+                                &pcie_config_flags,
+                                sizeof(pcie_config_flags));
+       if (ret != 0) {
+               ath10k_err("Failed to write pcie config_flags: %d\n", ret);
+               return ret;
+       }
+
+       /* configure early allocation */
+       ealloc_targ_addr = host_interest_item_address(HI_ITEM(hi_early_alloc));
+
+       ret = ath10k_pci_diag_read_access(ar, ealloc_targ_addr, &ealloc_value);
+       if (ret != 0) {
+               ath10k_err("Faile to get early alloc val: %d\n", ret);
+               return ret;
+       }
+
+       /* first bank is switched to IRAM */
+       ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) &
+                        HI_EARLY_ALLOC_MAGIC_MASK);
+       ealloc_value |= ((1 << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
+                        HI_EARLY_ALLOC_IRAM_BANKS_MASK);
+
+       ret = ath10k_pci_diag_write_access(ar, ealloc_targ_addr, ealloc_value);
+       if (ret != 0) {
+               ath10k_err("Failed to set early alloc val: %d\n", ret);
+               return ret;
+       }
+
+       /* Tell Target to proceed with initialization */
+       flag2_targ_addr = host_interest_item_address(HI_ITEM(hi_option_flag2));
+
+       ret = ath10k_pci_diag_read_access(ar, flag2_targ_addr, &flag2_value);
+       if (ret != 0) {
+               ath10k_err("Failed to get option val: %d\n", ret);
+               return ret;
+       }
+
+       flag2_value |= HI_OPTION_EARLY_CFG_DONE;
+
+       ret = ath10k_pci_diag_write_access(ar, flag2_targ_addr, flag2_value);
+       if (ret != 0) {
+               ath10k_err("Failed to set option val: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+
+
+static int ath10k_pci_ce_init(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info;
+       const struct ce_attr *attr;
+       int pipe_num;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               pipe_info->pipe_num = pipe_num;
+               pipe_info->hif_ce_state = ar;
+               attr = &host_ce_config_wlan[pipe_num];
+
+               pipe_info->ce_hdl = ath10k_ce_init(ar, pipe_num, attr);
+               if (pipe_info->ce_hdl == NULL) {
+                       ath10k_err("Unable to initialize CE for pipe: %d\n",
+                                  pipe_num);
+
+                       /* It is safe to call it here. It checks if ce_hdl is
+                        * valid for each pipe */
+                       ath10k_pci_ce_deinit(ar);
+                       return -1;
+               }
+
+               if (pipe_num == ar_pci->ce_count - 1) {
+                       /*
+                        * Reserve the ultimate CE for
+                        * diagnostic Window support
+                        */
+                       ar_pci->ce_diag =
+                       ar_pci->pipe_info[ar_pci->ce_count - 1].ce_hdl;
+                       continue;
+               }
+
+               pipe_info->buf_sz = (size_t) (attr->src_sz_max);
+       }
+
+       /*
+        * Initially, establish CE completion handlers for use with BMI.
+        * These are overwritten with generic handlers after we exit BMI phase.
+        */
+       pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG];
+       ath10k_ce_send_cb_register(pipe_info->ce_hdl,
+                                  ath10k_pci_bmi_send_done, 0);
+
+       pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST];
+       ath10k_ce_recv_cb_register(pipe_info->ce_hdl,
+                                  ath10k_pci_bmi_recv_data);
+
+       return 0;
+}
+
+static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       u32 fw_indicator_address, fw_indicator;
+
+       ath10k_pci_wake(ar);
+
+       fw_indicator_address = ar_pci->fw_indicator_address;
+       fw_indicator = ath10k_pci_read32(ar, fw_indicator_address);
+
+       if (fw_indicator & FW_IND_EVENT_PENDING) {
+               /* ACK: clear Target-side pending event */
+               ath10k_pci_write32(ar, fw_indicator_address,
+                                  fw_indicator & ~FW_IND_EVENT_PENDING);
+
+               if (ar_pci->started) {
+                       ath10k_pci_hif_dump_area(ar);
+               } else {
+                       /*
+                        * Probable Target failure before we're prepared
+                        * to handle it.  Generally unexpected.
+                        */
+                       ath10k_warn("early firmware event indicated\n");
+               }
+       }
+
+       ath10k_pci_sleep(ar);
+}
+
+static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
+       .send_head              = ath10k_pci_hif_send_head,
+       .exchange_bmi_msg       = ath10k_pci_hif_exchange_bmi_msg,
+       .start                  = ath10k_pci_hif_start,
+       .stop                   = ath10k_pci_hif_stop,
+       .map_service_to_pipe    = ath10k_pci_hif_map_service_to_pipe,
+       .get_default_pipe       = ath10k_pci_hif_get_default_pipe,
+       .send_complete_check    = ath10k_pci_hif_send_complete_check,
+       .init                   = ath10k_pci_hif_post_init,
+       .get_free_queue_number  = ath10k_pci_hif_get_free_queue_number,
+};
+
+static void ath10k_pci_ce_tasklet(unsigned long ptr)
+{
+       struct hif_ce_pipe_info *pipe = (struct hif_ce_pipe_info *)ptr;
+       struct ath10k_pci *ar_pci = pipe->ar_pci;
+
+       ath10k_ce_per_engine_service(ar_pci->ar, pipe->pipe_num);
+}
+
+static void ath10k_msi_err_tasklet(unsigned long data)
+{
+       struct ath10k *ar = (struct ath10k *)data;
+
+       ath10k_pci_fw_interrupt_handler(ar);
+}
+
+/*
+ * Handler for a per-engine interrupt on a PARTICULAR CE.
+ * This is used in cases where each CE has a private MSI interrupt.
+ */
+static irqreturn_t ath10k_pci_per_engine_handler(int irq, void *arg)
+{
+       struct ath10k *ar = arg;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ce_id = irq - ar_pci->pdev->irq - MSI_ASSIGN_CE_INITIAL;
+
+       if (ce_id < 0 || ce_id >= ARRAY_SIZE(ar_pci->pipe_info)) {
+               ath10k_warn("unexpected/invalid irq %d ce_id %d\n", irq, ce_id);
+               return IRQ_HANDLED;
+       }
+
+       /*
+        * NOTE: We are able to derive ce_id from irq because we
+        * use a one-to-one mapping for CE's 0..5.
+        * CE's 6 & 7 do not use interrupts at all.
+        *
+        * This mapping must be kept in sync with the mapping
+        * used by firmware.
+        */
+       tasklet_schedule(&ar_pci->pipe_info[ce_id].intr);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t ath10k_pci_msi_fw_handler(int irq, void *arg)
+{
+       struct ath10k *ar = arg;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       tasklet_schedule(&ar_pci->msi_fw_err);
+       return IRQ_HANDLED;
+}
+
+/*
+ * Top-level interrupt handler for all PCI interrupts from a Target.
+ * When a block of MSI interrupts is allocated, this top-level handler
+ * is not used; instead, we directly call the correct sub-handler.
+ */
+static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg)
+{
+       struct ath10k *ar = arg;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       if (ar_pci->num_msi_intrs == 0) {
+               /*
+                * IMPORTANT: INTR_CLR regiser has to be set after
+                * INTR_ENABLE is set to 0, otherwise interrupt can not be
+                * really cleared.
+                */
+               iowrite32(0, ar_pci->mem +
+                         (SOC_CORE_BASE_ADDRESS |
+                          PCIE_INTR_ENABLE_ADDRESS));
+               iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                         PCIE_INTR_CE_MASK_ALL,
+                         ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                        PCIE_INTR_CLR_ADDRESS));
+               /*
+                * IMPORTANT: this extra read transaction is required to
+                * flush the posted write buffer.
+                */
+               (void) ioread32(ar_pci->mem +
+                               (SOC_CORE_BASE_ADDRESS |
+                                PCIE_INTR_ENABLE_ADDRESS));
+       }
+
+       tasklet_schedule(&ar_pci->intr_tq);
+
+       return IRQ_HANDLED;
+}
+
+static void ath10k_pci_tasklet(unsigned long data)
+{
+       struct ath10k *ar = (struct ath10k *)data;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */
+       ath10k_ce_per_engine_service_any(ar);
+
+       if (ar_pci->num_msi_intrs == 0) {
+               /* Enable Legacy PCI line interrupts */
+               iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                         PCIE_INTR_CE_MASK_ALL,
+                         ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                        PCIE_INTR_ENABLE_ADDRESS));
+               /*
+                * IMPORTANT: this extra read transaction is required to
+                * flush the posted write buffer
+                */
+               (void) ioread32(ar_pci->mem +
+                               (SOC_CORE_BASE_ADDRESS |
+                                PCIE_INTR_ENABLE_ADDRESS));
+       }
+}
+
+static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+       int i;
+
+       ret = pci_enable_msi_block(ar_pci->pdev, num);
+       if (ret)
+               return ret;
+
+       ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW,
+                         ath10k_pci_msi_fw_handler,
+                         IRQF_SHARED, "ath10k_pci", ar);
+       if (ret)
+               return ret;
+
+       for (i = MSI_ASSIGN_CE_INITIAL; i <= MSI_ASSIGN_CE_MAX; i++) {
+               ret = request_irq(ar_pci->pdev->irq + i,
+                                 ath10k_pci_per_engine_handler,
+                                 IRQF_SHARED, "ath10k_pci", ar);
+               if (ret) {
+                       ath10k_warn("request_irq(%d) failed %d\n",
+                                   ar_pci->pdev->irq + i, ret);
+
+                       for (i--; i >= MSI_ASSIGN_CE_INITIAL; i--)
+                               free_irq(ar_pci->pdev->irq + i, ar);
+
+                       free_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, ar);
+                       pci_disable_msi(ar_pci->pdev);
+                       return ret;
+               }
+       }
+
+       ath10k_info("MSI-X interrupt handling (%d intrs)\n", num);
+       return 0;
+}
+
+static int ath10k_pci_start_intr_msi(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       ret = pci_enable_msi(ar_pci->pdev);
+       if (ret < 0)
+               return ret;
+
+       ret = request_irq(ar_pci->pdev->irq,
+                         ath10k_pci_interrupt_handler,
+                         IRQF_SHARED, "ath10k_pci", ar);
+       if (ret < 0) {
+               pci_disable_msi(ar_pci->pdev);
+               return ret;
+       }
+
+       ath10k_info("MSI interrupt handling\n");
+       return 0;
+}
+
+static int ath10k_pci_start_intr_legacy(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       ret = request_irq(ar_pci->pdev->irq,
+                         ath10k_pci_interrupt_handler,
+                         IRQF_SHARED, "ath10k_pci", ar);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Make sure to wake the Target before enabling Legacy
+        * Interrupt.
+        */
+       iowrite32(PCIE_SOC_WAKE_V_MASK,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       ath10k_pci_wait(ar);
+
+       /*
+        * A potential race occurs here: The CORE_BASE write
+        * depends on target correctly decoding AXI address but
+        * host won't know when target writes BAR to CORE_CTRL.
+        * This write might get lost if target has NOT written BAR.
+        * For now, fix the race by repeating the write in below
+        * synchronization checking.
+        */
+       iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                 PCIE_INTR_CE_MASK_ALL,
+                 ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                PCIE_INTR_ENABLE_ADDRESS));
+       iowrite32(PCIE_SOC_WAKE_RESET,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       ath10k_info("legacy interrupt handling\n");
+       return 0;
+}
+
+static int ath10k_pci_start_intr(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int num = MSI_NUM_REQUEST;
+       int ret;
+       int i;
+
+       tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long) ar);
+       tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet,
+                    (unsigned long) ar);
+
+       for (i = 0; i < CE_COUNT; i++) {
+               ar_pci->pipe_info[i].ar_pci = ar_pci;
+               tasklet_init(&ar_pci->pipe_info[i].intr,
+                            ath10k_pci_ce_tasklet,
+                            (unsigned long)&ar_pci->pipe_info[i]);
+       }
+
+       if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features))
+               num = 1;
+
+       if (num > 1) {
+               ret = ath10k_pci_start_intr_msix(ar, num);
+               if (ret == 0)
+                       goto exit;
+
+               ath10k_warn("MSI-X didn't succeed (%d), trying MSI\n", ret);
+               num = 1;
+       }
+
+       if (num == 1) {
+               ret = ath10k_pci_start_intr_msi(ar);
+               if (ret == 0)
+                       goto exit;
+
+               ath10k_warn("MSI didn't succeed (%d), trying legacy INTR\n",
+                           ret);
+               num = 0;
+       }
+
+       ret = ath10k_pci_start_intr_legacy(ar);
+
+exit:
+       ar_pci->num_msi_intrs = num;
+       ar_pci->ce_count = CE_COUNT;
+       return ret;
+}
+
+static void ath10k_pci_stop_intr(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int i;
+
+       /* There's at least one interrupt irregardless whether its legacy INTR
+        * or MSI or MSI-X */
+       for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
+               free_irq(ar_pci->pdev->irq + i, ar);
+
+       if (ar_pci->num_msi_intrs > 0)
+               pci_disable_msi(ar_pci->pdev);
+}
+
+static int ath10k_pci_reset_target(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int wait_limit = 300; /* 3 sec */
+
+       /* Wait for Target to finish initialization before we proceed. */
+       iowrite32(PCIE_SOC_WAKE_V_MASK,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       ath10k_pci_wait(ar);
+
+       while (wait_limit-- &&
+              !(ioread32(ar_pci->mem + FW_INDICATOR_ADDRESS) &
+                FW_IND_INITIALIZED)) {
+               if (ar_pci->num_msi_intrs == 0)
+                       /* Fix potential race by repeating CORE_BASE writes */
+                       iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                                 PCIE_INTR_CE_MASK_ALL,
+                                 ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                                PCIE_INTR_ENABLE_ADDRESS));
+               mdelay(10);
+       }
+
+       if (wait_limit < 0) {
+               ath10k_err("Target stalled\n");
+               iowrite32(PCIE_SOC_WAKE_RESET,
+                         ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                         PCIE_SOC_WAKE_ADDRESS);
+               return -EIO;
+       }
+
+       iowrite32(PCIE_SOC_WAKE_RESET,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       return 0;
+}
+
+static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci)
+{
+       struct ath10k *ar = ar_pci->ar;
+       void __iomem *mem = ar_pci->mem;
+       int i;
+       u32 val;
+
+       if (!SOC_GLOBAL_RESET_ADDRESS)
+               return;
+
+       if (!mem)
+               return;
+
+       ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS,
+                              PCIE_SOC_WAKE_V_MASK);
+       for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+               if (ath10k_pci_target_is_awake(ar))
+                       break;
+               msleep(1);
+       }
+
+       /* Put Target, including PCIe, into RESET. */
+       val = ath10k_pci_reg_read32(mem, SOC_GLOBAL_RESET_ADDRESS);
+       val |= 1;
+       ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+
+       for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+               if (ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+                                         RTC_STATE_COLD_RESET_MASK)
+                       break;
+               msleep(1);
+       }
+
+       /* Pull Target, including PCIe, out of RESET. */
+       val &= ~1;
+       ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+
+       for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+               if (!(ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+                                           RTC_STATE_COLD_RESET_MASK))
+                       break;
+               msleep(1);
+       }
+
+       ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET);
+}
+
+static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
+{
+       int i;
+
+       for (i = 0; i < ATH10K_PCI_FEATURE_COUNT; i++) {
+               if (!test_bit(i, ar_pci->features))
+                       continue;
+
+               switch (i) {
+               case ATH10K_PCI_FEATURE_MSI_X:
+                       ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n");
+                       break;
+               case ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND:
+                       ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n");
+                       break;
+               }
+       }
+}
+
+static int ath10k_pci_probe(struct pci_dev *pdev,
+                           const struct pci_device_id *pci_dev)
+{
+       void __iomem *mem;
+       int ret = 0;
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+       u32 lcr_val;
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       ar_pci = kzalloc(sizeof(*ar_pci), GFP_KERNEL);
+       if (ar_pci == NULL)
+               return -ENOMEM;
+
+       ar_pci->pdev = pdev;
+       ar_pci->dev = &pdev->dev;
+
+       switch (pci_dev->device) {
+       case QCA988X_1_0_DEVICE_ID:
+               set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features);
+               break;
+       case QCA988X_2_0_DEVICE_ID:
+               set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features);
+               break;
+       default:
+               ret = -ENODEV;
+               ath10k_err("Unkown device ID: %d\n", pci_dev->device);
+               goto err_ar_pci;
+       }
+
+       ath10k_pci_dump_features(ar_pci);
+
+       ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI,
+                               &ath10k_pci_hif_ops);
+       if (!ar) {
+               ath10k_err("ath10k_core_create failed!\n");
+               ret = -EINVAL;
+               goto err_ar_pci;
+       }
+
+       /* Enable QCA988X_1.0 HW workarounds */
+       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features))
+               spin_lock_init(&ar_pci->hw_v1_workaround_lock);
+
+       ar_pci->ar = ar;
+       ar_pci->fw_indicator_address = FW_INDICATOR_ADDRESS;
+       atomic_set(&ar_pci->keep_awake_count, 0);
+
+       pci_set_drvdata(pdev, ar);
+
+       /*
+        * Without any knowledge of the Host, the Target may have been reset or
+        * power cycled and its Config Space may no longer reflect the PCI
+        * address space that was assigned earlier by the PCI infrastructure.
+        * Refresh it now.
+        */
+       ret = pci_assign_resource(pdev, BAR_NUM);
+       if (ret) {
+               ath10k_err("cannot assign PCI space: %d\n", ret);
+               goto err_ar;
+       }
+
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               ath10k_err("cannot enable PCI device: %d\n", ret);
+               goto err_ar;
+       }
+
+       /* Request MMIO resources */
+       ret = pci_request_region(pdev, BAR_NUM, "ath");
+       if (ret) {
+               ath10k_err("PCI MMIO reservation error: %d\n", ret);
+               goto err_device;
+       }
+
+       /*
+        * Target structures have a limit of 32 bit DMA pointers.
+        * DMA pointers can be wider than 32 bits by default on some systems.
+        */
+       ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (ret) {
+               ath10k_err("32-bit DMA not available: %d\n", ret);
+               goto err_region;
+       }
+
+       ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (ret) {
+               ath10k_err("cannot enable 32-bit consistent DMA\n");
+               goto err_region;
+       }
+
+       /* Set bus master bit in PCI_COMMAND to enable DMA */
+       pci_set_master(pdev);
+
+       /*
+        * Temporary FIX: disable ASPM
+        * Will be removed after the OTP is programmed
+        */
+       pci_read_config_dword(pdev, 0x80, &lcr_val);
+       pci_write_config_dword(pdev, 0x80, (lcr_val & 0xffffff00));
+
+       /* Arrange for access to Target SoC registers. */
+       mem = pci_iomap(pdev, BAR_NUM, 0);
+       if (!mem) {
+               ath10k_err("PCI iomap error\n");
+               ret = -EIO;
+               goto err_master;
+       }
+
+       ar_pci->mem = mem;
+
+       spin_lock_init(&ar_pci->ce_lock);
+
+       ar_pci->cacheline_sz = dma_get_cache_alignment();
+
+       ret = ath10k_pci_start_intr(ar);
+       if (ret) {
+               ath10k_err("could not start interrupt handling (%d)\n", ret);
+               goto err_iomap;
+       }
+
+       /*
+        * Bring the target up cleanly.
+        *
+        * The target may be in an undefined state with an AUX-powered Target
+        * and a Host in WoW mode. If the Host crashes, loses power, or is
+        * restarted (without unloading the driver) then the Target is left
+        * (aux) powered and running. On a subsequent driver load, the Target
+        * is in an unexpected state. We try to catch that here in order to
+        * reset the Target and retry the probe.
+        */
+       ath10k_pci_device_reset(ar_pci);
+
+       ret = ath10k_pci_reset_target(ar);
+       if (ret)
+               goto err_intr;
+
+       if (ath10k_target_ps) {
+               ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
+       } else {
+               /* Force AWAKE forever */
+               ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
+               ath10k_do_pci_wake(ar);
+       }
+
+       ret = ath10k_pci_ce_init(ar);
+       if (ret)
+               goto err_intr;
+
+       ret = ath10k_pci_init_config(ar);
+       if (ret)
+               goto err_ce;
+
+       ret = ath10k_pci_wake_target_cpu(ar);
+       if (ret) {
+               ath10k_err("could not wake up target CPU (%d)\n", ret);
+               goto err_ce;
+       }
+
+       ret = ath10k_core_register(ar);
+       if (ret) {
+               ath10k_err("could not register driver core (%d)\n", ret);
+               goto err_ce;
+       }
+
+       return 0;
+
+err_ce:
+       ath10k_pci_ce_deinit(ar);
+err_intr:
+       ath10k_pci_stop_intr(ar);
+err_iomap:
+       pci_iounmap(pdev, mem);
+err_master:
+       pci_clear_master(pdev);
+err_region:
+       pci_release_region(pdev, BAR_NUM);
+err_device:
+       pci_disable_device(pdev);
+err_ar:
+       pci_set_drvdata(pdev, NULL);
+       ath10k_core_destroy(ar);
+err_ar_pci:
+       /* call HIF PCI free here */
+       kfree(ar_pci);
+
+       return ret;
+}
+
+static void ath10k_pci_remove(struct pci_dev *pdev)
+{
+       struct ath10k *ar = pci_get_drvdata(pdev);
+       struct ath10k_pci *ar_pci;
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       if (!ar)
+               return;
+
+       ar_pci = ath10k_pci_priv(ar);
+
+       if (!ar_pci)
+               return;
+
+       tasklet_kill(&ar_pci->msi_fw_err);
+
+       ath10k_core_unregister(ar);
+       ath10k_pci_stop_intr(ar);
+
+       pci_set_drvdata(pdev, NULL);
+       pci_iounmap(pdev, ar_pci->mem);
+       pci_release_region(pdev, BAR_NUM);
+       pci_clear_master(pdev);
+       pci_disable_device(pdev);
+
+       ath10k_core_destroy(ar);
+       kfree(ar_pci);
+}
+
+#if defined(CONFIG_PM_SLEEP)
+
+#define ATH10K_PCI_PM_CONTROL 0x44
+
+static int ath10k_pci_suspend(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct ath10k *ar = pci_get_drvdata(pdev);
+       struct ath10k_pci *ar_pci;
+       u32 val;
+       int ret, retval;
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       if (!ar)
+               return -ENODEV;
+
+       ar_pci = ath10k_pci_priv(ar);
+       if (!ar_pci)
+               return -ENODEV;
+
+       if (ath10k_core_target_suspend(ar))
+               return -EBUSY;
+
+       ret = wait_event_interruptible_timeout(ar->event_queue,
+                                               ar->is_target_paused == true,
+                                               1 * HZ);
+       if (ret < 0) {
+               ath10k_warn("suspend interrupted (%d)\n", ret);
+               retval = ret;
+               goto resume;
+       } else if (ret == 0) {
+               ath10k_warn("suspend timed out - target pause event never came\n");
+               retval = EIO;
+               goto resume;
+       }
+
+       /*
+        * reset is_target_paused and host can check that in next time,
+        * or it will always be TRUE and host just skip the waiting
+        * condition, it causes target assert due to host already
+        * suspend
+        */
+       ar->is_target_paused = false;
+
+       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+       if ((val & 0x000000ff) != 0x3) {
+               pci_save_state(pdev);
+               pci_disable_device(pdev);
+               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+                                      (val & 0xffffff00) | 0x03);
+       }
+
+       return 0;
+resume:
+       ret = ath10k_core_target_resume(ar);
+       if (ret)
+               ath10k_warn("could not resume (%d)\n", ret);
+
+       return retval;
+}
+
+static int ath10k_pci_resume(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct ath10k *ar = pci_get_drvdata(pdev);
+       struct ath10k_pci *ar_pci;
+       int ret;
+       u32 val;
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       if (!ar)
+               return -ENODEV;
+       ar_pci = ath10k_pci_priv(ar);
+
+       if (!ar_pci)
+               return -ENODEV;
+
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               ath10k_warn("cannot enable PCI device: %d\n", ret);
+               return ret;
+       }
+
+       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+       if ((val & 0x000000ff) != 0) {
+               pci_restore_state(pdev);
+               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+                                      val & 0xffffff00);
+               /*
+                * Suspend/Resume resets the PCI configuration space,
+                * so we have to re-disable the RETRY_TIMEOUT register (0x41)
+                * to keep PCI Tx retries from interfering with C3 CPU state
+                */
+               pci_read_config_dword(pdev, 0x40, &val);
+
+               if ((val & 0x0000ff00) != 0)
+                       pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+       }
+
+       ret = ath10k_core_target_resume(ar);
+       if (ret)
+               ath10k_warn("target resume failed: %d\n", ret);
+
+       return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops,
+                        ath10k_pci_suspend,
+                        ath10k_pci_resume);
+
+#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops)
+
+#else
+
+#define ATH10K_PCI_PM_OPS NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
+MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table);
+
+static struct pci_driver ath10k_pci_driver = {
+       .name = "ath10k_pci",
+       .id_table = ath10k_pci_id_table,
+       .probe = ath10k_pci_probe,
+       .remove = ath10k_pci_remove,
+       .driver.pm = ATH10K_PCI_PM_OPS,
+};
+
+static int __init ath10k_pci_init(void)
+{
+       int ret;
+
+       ret = pci_register_driver(&ath10k_pci_driver);
+       if (ret)
+               ath10k_err("pci_register_driver failed [%d]\n", ret);
+
+       return ret;
+}
+module_init(ath10k_pci_init);
+
+static void __exit ath10k_pci_exit(void)
+{
+       pci_unregister_driver(&ath10k_pci_driver);
+}
+
+module_exit(ath10k_pci_exit);
+
+MODULE_AUTHOR("Qualcomm Atheros");
+MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_FW_FILE);
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_OTP_FILE);
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_OTP_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);
diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h
new file mode 100644 (file)
index 0000000..d2a055a
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _PCI_H_
+#define _PCI_H_
+
+#include <linux/interrupt.h>
+
+#include "hw.h"
+#include "ce.h"
+
+/* FW dump area */
+#define REG_DUMP_COUNT_QCA988X 60
+
+/*
+ * maximum number of bytes that can be handled atomically by DiagRead/DiagWrite
+ */
+#define DIAG_TRANSFER_LIMIT 2048
+
+/*
+ * maximum number of bytes that can be
+ * handled atomically by DiagRead/DiagWrite
+ */
+#define DIAG_TRANSFER_LIMIT 2048
+
+struct bmi_xfer {
+       struct completion done;
+       bool wait_for_resp;
+       u32 resp_len;
+};
+
+struct ath10k_pci_compl {
+       struct list_head list;
+       int send_or_recv;
+       struct ce_state *ce_state;
+       struct hif_ce_pipe_info *pipe_info;
+       void *transfer_context;
+       unsigned int nbytes;
+       unsigned int transfer_id;
+       unsigned int flags;
+};
+
+/* compl_state.send_or_recv */
+#define HIF_CE_COMPLETE_FREE 0
+#define HIF_CE_COMPLETE_SEND 1
+#define HIF_CE_COMPLETE_RECV 2
+
+/*
+ * PCI-specific Target state
+ *
+ * NOTE: Structure is shared between Host software and Target firmware!
+ *
+ * Much of this may be of interest to the Host so
+ * HOST_INTEREST->hi_interconnect_state points here
+ * (and all members are 32-bit quantities in order to
+ * facilitate Host access). In particular, Host software is
+ * required to initialize pipe_cfg_addr and svc_to_pipe_map.
+ */
+struct pcie_state {
+       /* Pipe configuration Target address */
+       /* NB: ce_pipe_config[CE_COUNT] */
+       u32 pipe_cfg_addr;
+
+       /* Service to pipe map Target address */
+       /* NB: service_to_pipe[PIPE_TO_CE_MAP_CN] */
+       u32 svc_to_pipe_map;
+
+       /* number of MSI interrupts requested */
+       u32 msi_requested;
+
+       /* number of MSI interrupts granted */
+       u32 msi_granted;
+
+       /* Message Signalled Interrupt address */
+       u32 msi_addr;
+
+       /* Base data */
+       u32 msi_data;
+
+       /*
+        * Data for firmware interrupt;
+        * MSI data for other interrupts are
+        * in various SoC registers
+        */
+       u32 msi_fw_intr_data;
+
+       /* PCIE_PWR_METHOD_* */
+       u32 power_mgmt_method;
+
+       /* PCIE_CONFIG_FLAG_* */
+       u32 config_flags;
+};
+
+/* PCIE_CONFIG_FLAG definitions */
+#define PCIE_CONFIG_FLAG_ENABLE_L1  0x0000001
+
+/* Host software's Copy Engine configuration. */
+#define CE_ATTR_FLAGS 0
+
+/*
+ * Configuration information for a Copy Engine pipe.
+ * Passed from Host to Target during startup (one per CE).
+ *
+ * NOTE: Structure is shared between Host software and Target firmware!
+ */
+struct ce_pipe_config {
+       u32 pipenum;
+       u32 pipedir;
+       u32 nentries;
+       u32 nbytes_max;
+       u32 flags;
+       u32 reserved;
+};
+
+/*
+ * Directions for interconnect pipe configuration.
+ * These definitions may be used during configuration and are shared
+ * between Host and Target.
+ *
+ * Pipe Directions are relative to the Host, so PIPEDIR_IN means
+ * "coming IN over air through Target to Host" as with a WiFi Rx operation.
+ * Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air"
+ * as with a WiFi Tx operation. This is somewhat awkward for the "middle-man"
+ * Target since things that are "PIPEDIR_OUT" are coming IN to the Target
+ * over the interconnect.
+ */
+#define PIPEDIR_NONE    0
+#define PIPEDIR_IN      1  /* Target-->Host, WiFi Rx direction */
+#define PIPEDIR_OUT     2  /* Host->Target, WiFi Tx direction */
+#define PIPEDIR_INOUT   3  /* bidirectional */
+
+/* Establish a mapping between a service/direction and a pipe. */
+struct service_to_pipe {
+       u32 service_id;
+       u32 pipedir;
+       u32 pipenum;
+};
+
+enum ath10k_pci_features {
+       ATH10K_PCI_FEATURE_MSI_X                = 0,
+       ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND    = 1,
+
+       /* keep last */
+       ATH10K_PCI_FEATURE_COUNT
+};
+
+/* Per-pipe state. */
+struct hif_ce_pipe_info {
+       /* Handle of underlying Copy Engine */
+       struct ce_state *ce_hdl;
+
+       /* Our pipe number; facilitiates use of pipe_info ptrs. */
+       u8 pipe_num;
+
+       /* Convenience back pointer to hif_ce_state. */
+       struct ath10k *hif_ce_state;
+
+       size_t buf_sz;
+
+       /* protects compl_free and num_send_allowed */
+       spinlock_t pipe_lock;
+
+       /* List of free CE completion slots */
+       struct list_head compl_free;
+
+       /* Limit the number of outstanding send requests. */
+       int num_sends_allowed;
+
+       struct ath10k_pci *ar_pci;
+       struct tasklet_struct intr;
+};
+
+struct ath10k_pci {
+       struct pci_dev *pdev;
+       struct device *dev;
+       struct ath10k *ar;
+       void __iomem *mem;
+       int cacheline_sz;
+
+       DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT);
+
+       /*
+        * Number of MSI interrupts granted, 0 --> using legacy PCI line
+        * interrupts.
+        */
+       int num_msi_intrs;
+
+       struct tasklet_struct intr_tq;
+       struct tasklet_struct msi_fw_err;
+
+       /* Number of Copy Engines supported */
+       unsigned int ce_count;
+
+       int started;
+
+       atomic_t keep_awake_count;
+       bool verified_awake;
+
+       /* List of CE completions to be processed */
+       struct list_head compl_process;
+
+       /* protects compl_processing and compl_process */
+       spinlock_t compl_lock;
+
+       bool compl_processing;
+
+       struct hif_ce_pipe_info pipe_info[CE_COUNT_MAX];
+
+       struct ath10k_hif_cb msg_callbacks_current;
+
+       /* Target address used to signal a pending firmware event */
+       u32 fw_indicator_address;
+
+       /* Copy Engine used for Diagnostic Accesses */
+       struct ce_state *ce_diag;
+
+       /* FIXME: document what this really protects */
+       spinlock_t ce_lock;
+
+       /* Map CE id to ce_state */
+       struct ce_state *ce_id_to_state[CE_COUNT_MAX];
+
+       /* makes sure that dummy reads are atomic */
+       spinlock_t hw_v1_workaround_lock;
+};
+
+static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
+{
+       return ar->hif.priv;
+}
+
+static inline u32 ath10k_pci_reg_read32(void __iomem *mem, u32 addr)
+{
+       return ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+}
+
+static inline void ath10k_pci_reg_write32(void __iomem *mem, u32 addr, u32 val)
+{
+       iowrite32(val, mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+}
+
+#define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
+#define PCIE_WAKE_TIMEOUT 5000 /* 5ms */
+
+#define BAR_NUM 0
+
+#define CDC_WAR_MAGIC_STR   0xceef0000
+#define CDC_WAR_DATA_CE     4
+
+/*
+ * TODO: Should be a function call specific to each Target-type.
+ * This convoluted macro converts from Target CPU Virtual Address Space to CE
+ * Address Space. As part of this process, we conservatively fetch the current
+ * PCIE_BAR. MOST of the time, this should match the upper bits of PCI space
+ * for this device; but that's not guaranteed.
+ */
+#define TARG_CPU_SPACE_TO_CE_SPACE(ar, pci_addr, addr)                 \
+       (((ioread32((pci_addr)+(SOC_CORE_BASE_ADDRESS|                  \
+         CORE_CTRL_ADDRESS)) & 0x7ff) << 21) |                         \
+        0x100000 | ((addr) & 0xfffff))
+
+/* Wait up to this many Ms for a Diagnostic Access CE operation to complete */
+#define DIAG_ACCESS_CE_TIMEOUT_MS 10
+
+/*
+ * This API allows the Host to access Target registers directly
+ * and relatively efficiently over PCIe.
+ * This allows the Host to avoid extra overhead associated with
+ * sending a message to firmware and waiting for a response message
+ * from firmware, as is done on other interconnects.
+ *
+ * Yet there is some complexity with direct accesses because the
+ * Target's power state is not known a priori. The Host must issue
+ * special PCIe reads/writes in order to explicitly wake the Target
+ * and to verify that it is awake and will remain awake.
+ *
+ * Usage:
+ *
+ *   Use ath10k_pci_read32 and ath10k_pci_write32 to access Target space.
+ *   These calls must be bracketed by ath10k_pci_wake and
+ *   ath10k_pci_sleep.  A single BEGIN/END pair is adequate for
+ *   multiple READ/WRITE operations.
+ *
+ *   Use ath10k_pci_wake to put the Target in a state in
+ *   which it is legal for the Host to directly access it. This
+ *   may involve waking the Target from a low power state, which
+ *   may take up to 2Ms!
+ *
+ *   Use ath10k_pci_sleep to tell the Target that as far as
+ *   this code path is concerned, it no longer needs to remain
+ *   directly accessible.  BEGIN/END is under a reference counter;
+ *   multiple code paths may issue BEGIN/END on a single targid.
+ */
+static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset,
+                                     u32 value)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       void __iomem *addr = ar_pci->mem;
+
+       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+               unsigned long irq_flags;
+
+               spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags);
+
+               ioread32(addr+offset+4); /* 3rd read prior to write */
+               ioread32(addr+offset+4); /* 2nd read prior to write */
+               ioread32(addr+offset+4); /* 1st read prior to write */
+               iowrite32(value, addr+offset);
+
+               spin_unlock_irqrestore(&ar_pci->hw_v1_workaround_lock,
+                                      irq_flags);
+       } else {
+               iowrite32(value, addr+offset);
+       }
+}
+
+static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       return ioread32(ar_pci->mem + offset);
+}
+
+extern unsigned int ath10k_target_ps;
+
+void ath10k_do_pci_wake(struct ath10k *ar);
+void ath10k_do_pci_sleep(struct ath10k *ar);
+
+static inline void ath10k_pci_wake(struct ath10k *ar)
+{
+       if (ath10k_target_ps)
+               ath10k_do_pci_wake(ar);
+}
+
+static inline void ath10k_pci_sleep(struct ath10k *ar)
+{
+       if (ath10k_target_ps)
+               ath10k_do_pci_sleep(ar);
+}
+
+#endif /* _PCI_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h
new file mode 100644 (file)
index 0000000..bfec6c8
--- /dev/null
@@ -0,0 +1,990 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _RX_DESC_H_
+#define _RX_DESC_H_
+
+enum rx_attention_flags {
+       RX_ATTENTION_FLAGS_FIRST_MPDU          = 1 << 0,
+       RX_ATTENTION_FLAGS_LAST_MPDU           = 1 << 1,
+       RX_ATTENTION_FLAGS_MCAST_BCAST         = 1 << 2,
+       RX_ATTENTION_FLAGS_PEER_IDX_INVALID    = 1 << 3,
+       RX_ATTENTION_FLAGS_PEER_IDX_TIMEOUT    = 1 << 4,
+       RX_ATTENTION_FLAGS_POWER_MGMT          = 1 << 5,
+       RX_ATTENTION_FLAGS_NON_QOS             = 1 << 6,
+       RX_ATTENTION_FLAGS_NULL_DATA           = 1 << 7,
+       RX_ATTENTION_FLAGS_MGMT_TYPE           = 1 << 8,
+       RX_ATTENTION_FLAGS_CTRL_TYPE           = 1 << 9,
+       RX_ATTENTION_FLAGS_MORE_DATA           = 1 << 10,
+       RX_ATTENTION_FLAGS_EOSP                = 1 << 11,
+       RX_ATTENTION_FLAGS_U_APSD_TRIGGER      = 1 << 12,
+       RX_ATTENTION_FLAGS_FRAGMENT            = 1 << 13,
+       RX_ATTENTION_FLAGS_ORDER               = 1 << 14,
+       RX_ATTENTION_FLAGS_CLASSIFICATION      = 1 << 15,
+       RX_ATTENTION_FLAGS_OVERFLOW_ERR        = 1 << 16,
+       RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR     = 1 << 17,
+       RX_ATTENTION_FLAGS_TCP_UDP_CHKSUM_FAIL = 1 << 18,
+       RX_ATTENTION_FLAGS_IP_CHKSUM_FAIL      = 1 << 19,
+       RX_ATTENTION_FLAGS_SA_IDX_INVALID      = 1 << 20,
+       RX_ATTENTION_FLAGS_DA_IDX_INVALID      = 1 << 21,
+       RX_ATTENTION_FLAGS_SA_IDX_TIMEOUT      = 1 << 22,
+       RX_ATTENTION_FLAGS_DA_IDX_TIMEOUT      = 1 << 23,
+       RX_ATTENTION_FLAGS_ENCRYPT_REQUIRED    = 1 << 24,
+       RX_ATTENTION_FLAGS_DIRECTED            = 1 << 25,
+       RX_ATTENTION_FLAGS_BUFFER_FRAGMENT     = 1 << 26,
+       RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR     = 1 << 27,
+       RX_ATTENTION_FLAGS_TKIP_MIC_ERR        = 1 << 28,
+       RX_ATTENTION_FLAGS_DECRYPT_ERR         = 1 << 29,
+       RX_ATTENTION_FLAGS_FCS_ERR             = 1 << 30,
+       RX_ATTENTION_FLAGS_MSDU_DONE           = 1 << 31,
+};
+
+struct rx_attention {
+       __le32 flags; /* %RX_ATTENTION_FLAGS_ */
+} __packed;
+
+/*
+ * first_mpdu
+ *             Indicates the first MSDU of the PPDU.  If both first_mpdu
+ *             and last_mpdu are set in the MSDU then this is a not an
+ *             A-MPDU frame but a stand alone MPDU.  Interior MPDU in an
+ *             A-MPDU shall have both first_mpdu and last_mpdu bits set to
+ *             0.  The PPDU start status will only be valid when this bit
+ *             is set.
+ *
+ * last_mpdu
+ *             Indicates the last MSDU of the last MPDU of the PPDU.  The
+ *             PPDU end status will only be valid when this bit is set.
+ *
+ * mcast_bcast
+ *             Multicast / broadcast indicator.  Only set when the MAC
+ *             address 1 bit 0 is set indicating mcast/bcast and the BSSID
+ *             matches one of the 4 BSSID registers. Only set when
+ *             first_msdu is set.
+ *
+ * peer_idx_invalid
+ *             Indicates no matching entries within the the max search
+ *             count.  Only set when first_msdu is set.
+ *
+ * peer_idx_timeout
+ *             Indicates an unsuccessful search for the peer index due to
+ *             timeout.  Only set when first_msdu is set.
+ *
+ * power_mgmt
+ *             Power management bit set in the 802.11 header.  Only set
+ *             when first_msdu is set.
+ *
+ * non_qos
+ *             Set if packet is not a non-QoS data frame.  Only set when
+ *             first_msdu is set.
+ *
+ * null_data
+ *             Set if frame type indicates either null data or QoS null
+ *             data format.  Only set when first_msdu is set.
+ *
+ * mgmt_type
+ *             Set if packet is a management packet.  Only set when
+ *             first_msdu is set.
+ *
+ * ctrl_type
+ *             Set if packet is a control packet.  Only set when first_msdu
+ *             is set.
+ *
+ * more_data
+ *             Set if more bit in frame control is set.  Only set when
+ *             first_msdu is set.
+ *
+ * eosp
+ *             Set if the EOSP (end of service period) bit in the QoS
+ *             control field is set.  Only set when first_msdu is set.
+ *
+ * u_apsd_trigger
+ *             Set if packet is U-APSD trigger.  Key table will have bits
+ *             per TID to indicate U-APSD trigger.
+ *
+ * fragment
+ *             Indicates that this is an 802.11 fragment frame.  This is
+ *             set when either the more_frag bit is set in the frame
+ *             control or the fragment number is not zero.  Only set when
+ *             first_msdu is set.
+ *
+ * order
+ *             Set if the order bit in the frame control is set.  Only set
+ *             when first_msdu is set.
+ *
+ * classification
+ *             Indicates that this status has a corresponding MSDU that
+ *             requires FW processing.  The OLE will have classification
+ *             ring mask registers which will indicate the ring(s) for
+ *             packets and descriptors which need FW attention.
+ *
+ * overflow_err
+ *             PCU Receive FIFO does not have enough space to store the
+ *             full receive packet.  Enough space is reserved in the
+ *             receive FIFO for the status is written.  This MPDU remaining
+ *             packets in the PPDU will be filtered and no Ack response
+ *             will be transmitted.
+ *
+ * msdu_length_err
+ *             Indicates that the MSDU length from the 802.3 encapsulated
+ *             length field extends beyond the MPDU boundary.
+ *
+ * tcp_udp_chksum_fail
+ *             Indicates that the computed checksum (tcp_udp_chksum) did
+ *             not match the checksum in the TCP/UDP header.
+ *
+ * ip_chksum_fail
+ *             Indicates that the computed checksum did not match the
+ *             checksum in the IP header.
+ *
+ * sa_idx_invalid
+ *             Indicates no matching entry was found in the address search
+ *             table for the source MAC address.
+ *
+ * da_idx_invalid
+ *             Indicates no matching entry was found in the address search
+ *             table for the destination MAC address.
+ *
+ * sa_idx_timeout
+ *             Indicates an unsuccessful search for the source MAC address
+ *             due to the expiring of the search timer.
+ *
+ * da_idx_timeout
+ *             Indicates an unsuccessful search for the destination MAC
+ *             address due to the expiring of the search timer.
+ *
+ * encrypt_required
+ *             Indicates that this data type frame is not encrypted even if
+ *             the policy for this MPDU requires encryption as indicated in
+ *             the peer table key type.
+ *
+ * directed
+ *             MPDU is a directed packet which means that the RA matched
+ *             our STA addresses.  In proxySTA it means that the TA matched
+ *             an entry in our address search table with the corresponding
+ *             'no_ack' bit is the address search entry cleared.
+ *
+ * buffer_fragment
+ *             Indicates that at least one of the rx buffers has been
+ *             fragmented.  If set the FW should look at the rx_frag_info
+ *             descriptor described below.
+ *
+ * mpdu_length_err
+ *             Indicates that the MPDU was pre-maturely terminated
+ *             resulting in a truncated MPDU.  Don't trust the MPDU length
+ *             field.
+ *
+ * tkip_mic_err
+ *             Indicates that the MPDU Michael integrity check failed
+ *
+ * decrypt_err
+ *             Indicates that the MPDU decrypt integrity check failed
+ *
+ * fcs_err
+ *             Indicates that the MPDU FCS check failed
+ *
+ * msdu_done
+ *             If set indicates that the RX packet data, RX header data, RX
+ *             PPDU start descriptor, RX MPDU start/end descriptor, RX MSDU
+ *             start/end descriptors and RX Attention descriptor are all
+ *             valid.  This bit must be in the last octet of the
+ *             descriptor.
+ */
+
+struct rx_frag_info {
+       u8 ring0_more_count;
+       u8 ring1_more_count;
+       u8 ring2_more_count;
+       u8 ring3_more_count;
+} __packed;
+
+/*
+ * ring0_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 0.  Field is filled in by the RX_DMA.
+ *
+ * ring1_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 1. Field is filled in by the RX_DMA.
+ *
+ * ring2_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 2. Field is filled in by the RX_DMA.
+ *
+ * ring3_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 3. Field is filled in by the RX_DMA.
+ */
+
+enum htt_rx_mpdu_encrypt_type {
+       HTT_RX_MPDU_ENCRYPT_WEP40            = 0,
+       HTT_RX_MPDU_ENCRYPT_WEP104           = 1,
+       HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC = 2,
+       HTT_RX_MPDU_ENCRYPT_WEP128           = 3,
+       HTT_RX_MPDU_ENCRYPT_TKIP_WPA         = 4,
+       HTT_RX_MPDU_ENCRYPT_WAPI             = 5,
+       HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2     = 6,
+       HTT_RX_MPDU_ENCRYPT_NONE             = 7,
+};
+
+#define RX_MPDU_START_INFO0_PEER_IDX_MASK     0x000007ff
+#define RX_MPDU_START_INFO0_PEER_IDX_LSB      0
+#define RX_MPDU_START_INFO0_SEQ_NUM_MASK      0x0fff0000
+#define RX_MPDU_START_INFO0_SEQ_NUM_LSB       16
+#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_MASK 0xf0000000
+#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_LSB  28
+#define RX_MPDU_START_INFO0_FROM_DS           (1 << 11)
+#define RX_MPDU_START_INFO0_TO_DS             (1 << 12)
+#define RX_MPDU_START_INFO0_ENCRYPTED         (1 << 13)
+#define RX_MPDU_START_INFO0_RETRY             (1 << 14)
+#define RX_MPDU_START_INFO0_TXBF_H_INFO       (1 << 15)
+
+#define RX_MPDU_START_INFO1_TID_MASK 0xf0000000
+#define RX_MPDU_START_INFO1_TID_LSB  28
+#define RX_MPDU_START_INFO1_DIRECTED (1 << 16)
+
+struct rx_mpdu_start {
+       __le32 info0;
+       union {
+               struct {
+                       __le32 pn31_0;
+                       __le32 info1; /* %RX_MPDU_START_INFO1_ */
+               } __packed;
+               struct {
+                       u8 pn[6];
+               } __packed;
+       } __packed;
+} __packed;
+
+/*
+ * peer_idx
+ *             The index of the address search table which associated with
+ *             the peer table entry corresponding to this MPDU.  Only valid
+ *             when first_msdu is set.
+ *
+ * fr_ds
+ *             Set if the from DS bit is set in the frame control.  Only
+ *             valid when first_msdu is set.
+ *
+ * to_ds
+ *             Set if the to DS bit is set in the frame control.  Only
+ *             valid when first_msdu is set.
+ *
+ * encrypted
+ *             Protected bit from the frame control.  Only valid when
+ *             first_msdu is set.
+ *
+ * retry
+ *             Retry bit from the frame control.  Only valid when
+ *             first_msdu is set.
+ *
+ * txbf_h_info
+ *             The MPDU data will contain H information.  Primarily used
+ *             for debug.
+ *
+ * seq_num
+ *             The sequence number from the 802.11 header.  Only valid when
+ *             first_msdu is set.
+ *
+ * encrypt_type
+ *             Indicates type of decrypt cipher used (as defined in the
+ *             peer table)
+ *             0: WEP40
+ *             1: WEP104
+ *             2: TKIP without MIC
+ *             3: WEP128
+ *             4: TKIP (WPA)
+ *             5: WAPI
+ *             6: AES-CCM (WPA2)
+ *             7: No cipher
+ *             Only valid when first_msdu_is set
+ *
+ * pn_31_0
+ *             Bits [31:0] of the PN number extracted from the IV field
+ *             WEP: IV = {key_id_octet, pn2, pn1, pn0}.  Only pn[23:0] is
+ *             valid.
+ *             TKIP: IV = {pn5, pn4, pn3, pn2, key_id_octet, pn0,
+ *             WEPSeed[1], pn1}.  Only pn[47:0] is valid.
+ *             AES-CCM: IV = {pn5, pn4, pn3, pn2, key_id_octet, 0x0, pn1,
+ *             pn0}.  Only pn[47:0] is valid.
+ *             WAPI: IV = {key_id_octet, 0x0, pn15, pn14, pn13, pn12, pn11,
+ *             pn10, pn9, pn8, pn7, pn6, pn5, pn4, pn3, pn2, pn1, pn0}.
+ *             The ext_wapi_pn[127:48] in the rx_msdu_misc descriptor and
+ *             pn[47:0] are valid.
+ *             Only valid when first_msdu is set.
+ *
+ * pn_47_32
+ *             Bits [47:32] of the PN number.   See description for
+ *             pn_31_0.  The remaining PN fields are in the rx_msdu_end
+ *             descriptor
+ *
+ * pn
+ *             Use this field to access the pn without worrying about
+ *             byte-order and bitmasking/bitshifting.
+ *
+ * directed
+ *             See definition in RX attention descriptor
+ *
+ * reserved_2
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ *
+ * tid
+ *             The TID field in the QoS control field
+ */
+
+#define RX_MPDU_END_INFO0_RESERVED_0_MASK     0x00001fff
+#define RX_MPDU_END_INFO0_RESERVED_0_LSB      0
+#define RX_MPDU_END_INFO0_POST_DELIM_CNT_MASK 0x0fff0000
+#define RX_MPDU_END_INFO0_POST_DELIM_CNT_LSB  16
+#define RX_MPDU_END_INFO0_OVERFLOW_ERR        (1 << 13)
+#define RX_MPDU_END_INFO0_LAST_MPDU           (1 << 14)
+#define RX_MPDU_END_INFO0_POST_DELIM_ERR      (1 << 15)
+#define RX_MPDU_END_INFO0_MPDU_LENGTH_ERR     (1 << 28)
+#define RX_MPDU_END_INFO0_TKIP_MIC_ERR        (1 << 29)
+#define RX_MPDU_END_INFO0_DECRYPT_ERR         (1 << 30)
+#define RX_MPDU_END_INFO0_FCS_ERR             (1 << 31)
+
+struct rx_mpdu_end {
+       __le32 info0;
+} __packed;
+
+/*
+ * reserved_0
+ *             Reserved
+ *
+ * overflow_err
+ *             PCU Receive FIFO does not have enough space to store the
+ *             full receive packet.  Enough space is reserved in the
+ *             receive FIFO for the status is written.  This MPDU remaining
+ *             packets in the PPDU will be filtered and no Ack response
+ *             will be transmitted.
+ *
+ * last_mpdu
+ *             Indicates that this is the last MPDU of a PPDU.
+ *
+ * post_delim_err
+ *             Indicates that a delimiter FCS error occurred after this
+ *             MPDU before the next MPDU.  Only valid when last_msdu is
+ *             set.
+ *
+ * post_delim_cnt
+ *             Count of the delimiters after this MPDU.  This requires the
+ *             last MPDU to be held until all the EOF descriptors have been
+ *             received.  This may be inefficient in the future when
+ *             ML-MIMO is used.  Only valid when last_mpdu is set.
+ *
+ * mpdu_length_err
+ *             See definition in RX attention descriptor
+ *
+ * tkip_mic_err
+ *             See definition in RX attention descriptor
+ *
+ * decrypt_err
+ *             See definition in RX attention descriptor
+ *
+ * fcs_err
+ *             See definition in RX attention descriptor
+ */
+
+#define RX_MSDU_START_INFO0_MSDU_LENGTH_MASK    0x00003fff
+#define RX_MSDU_START_INFO0_MSDU_LENGTH_LSB     0
+#define RX_MSDU_START_INFO0_IP_OFFSET_MASK      0x000fc000
+#define RX_MSDU_START_INFO0_IP_OFFSET_LSB       14
+#define RX_MSDU_START_INFO0_RING_MASK_MASK      0x00f00000
+#define RX_MSDU_START_INFO0_RING_MASK_LSB       20
+#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_MASK 0x7f000000
+#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_LSB  24
+
+#define RX_MSDU_START_INFO1_MSDU_NUMBER_MASK    0x000000ff
+#define RX_MSDU_START_INFO1_MSDU_NUMBER_LSB     0
+#define RX_MSDU_START_INFO1_DECAP_FORMAT_MASK   0x00000300
+#define RX_MSDU_START_INFO1_DECAP_FORMAT_LSB    8
+#define RX_MSDU_START_INFO1_SA_IDX_MASK         0x07ff0000
+#define RX_MSDU_START_INFO1_SA_IDX_LSB          16
+#define RX_MSDU_START_INFO1_IPV4_PROTO          (1 << 10)
+#define RX_MSDU_START_INFO1_IPV6_PROTO          (1 << 11)
+#define RX_MSDU_START_INFO1_TCP_PROTO           (1 << 12)
+#define RX_MSDU_START_INFO1_UDP_PROTO           (1 << 13)
+#define RX_MSDU_START_INFO1_IP_FRAG             (1 << 14)
+#define RX_MSDU_START_INFO1_TCP_ONLY_ACK        (1 << 15)
+
+enum rx_msdu_decap_format {
+       RX_MSDU_DECAP_RAW           = 0,
+       RX_MSDU_DECAP_NATIVE_WIFI   = 1,
+       RX_MSDU_DECAP_ETHERNET2_DIX = 2,
+       RX_MSDU_DECAP_8023_SNAP_LLC = 3
+};
+
+struct rx_msdu_start {
+       __le32 info0; /* %RX_MSDU_START_INFO0_ */
+       __le32 flow_id_crc;
+       __le32 info1; /* %RX_MSDU_START_INFO1_ */
+} __packed;
+
+/*
+ * msdu_length
+ *             MSDU length in bytes after decapsulation.  This field is
+ *             still valid for MPDU frames without A-MSDU.  It still
+ *             represents MSDU length after decapsulation
+ *
+ * ip_offset
+ *             Indicates the IP offset in bytes from the start of the
+ *             packet after decapsulation.  Only valid if ipv4_proto or
+ *             ipv6_proto is set.
+ *
+ * ring_mask
+ *             Indicates the destination RX rings for this MSDU.
+ *
+ * tcp_udp_offset
+ *             Indicates the offset in bytes to the start of TCP or UDP
+ *             header from the start of the IP header after decapsulation.
+ *             Only valid if tcp_prot or udp_prot is set.  The value 0
+ *             indicates that the offset is longer than 127 bytes.
+ *
+ * reserved_0c
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ *
+ * flow_id_crc
+ *             The flow_id_crc runs CRC32 on the following information:
+ *             IPv4 option: dest_addr[31:0], src_addr [31:0], {24'b0,
+ *             protocol[7:0]}.
+ *             IPv6 option: dest_addr[127:0], src_addr [127:0], {24'b0,
+ *             next_header[7:0]}
+ *             UDP case: sort_port[15:0], dest_port[15:0]
+ *             TCP case: sort_port[15:0], dest_port[15:0],
+ *             {header_length[3:0], 6'b0, flags[5:0], window_size[15:0]},
+ *             {16'b0, urgent_ptr[15:0]}, all options except 32-bit
+ *             timestamp.
+ *
+ * msdu_number
+ *             Indicates the MSDU number within a MPDU.  This value is
+ *             reset to zero at the start of each MPDU.  If the number of
+ *             MSDU exceeds 255 this number will wrap using modulo 256.
+ *
+ * decap_format
+ *             Indicates the format after decapsulation:
+ *             0: RAW: No decapsulation
+ *             1: Native WiFi
+ *             2: Ethernet 2 (DIX)
+ *             3: 802.3 (SNAP/LLC)
+ *
+ * ipv4_proto
+ *             Set if L2 layer indicates IPv4 protocol.
+ *
+ * ipv6_proto
+ *             Set if L2 layer indicates IPv6 protocol.
+ *
+ * tcp_proto
+ *             Set if the ipv4_proto or ipv6_proto are set and the IP
+ *             protocol indicates TCP.
+ *
+ * udp_proto
+ *             Set if the ipv4_proto or ipv6_proto are set and the IP
+ *                     protocol indicates UDP.
+ *
+ * ip_frag
+ *             Indicates that either the IP More frag bit is set or IP frag
+ *             number is non-zero.  If set indicates that this is a
+ *             fragmented IP packet.
+ *
+ * tcp_only_ack
+ *             Set if only the TCP Ack bit is set in the TCP flags and if
+ *             the TCP payload is 0.
+ *
+ * sa_idx
+ *             The offset in the address table which matches the MAC source
+ *             address.
+ *
+ * reserved_2b
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ */
+
+#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_MASK 0x00003fff
+#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_LSB  0
+#define RX_MSDU_END_INFO0_FIRST_MSDU                (1 << 14)
+#define RX_MSDU_END_INFO0_LAST_MSDU                 (1 << 15)
+#define RX_MSDU_END_INFO0_PRE_DELIM_ERR             (1 << 30)
+#define RX_MSDU_END_INFO0_RESERVED_3B               (1 << 31)
+
+struct rx_msdu_end {
+       __le16 ip_hdr_cksum;
+       __le16 tcp_hdr_cksum;
+       u8 key_id_octet;
+       u8 classification_filter;
+       u8 wapi_pn[10];
+       __le32 info0;
+} __packed;
+
+/*
+ *ip_hdr_chksum
+ *             This can include the IP header checksum or the pseudo header
+ *             checksum used by TCP/UDP checksum.
+ *
+ *tcp_udp_chksum
+ *             The value of the computed TCP/UDP checksum.  A mode bit
+ *             selects whether this checksum is the full checksum or the
+ *             partial checksum which does not include the pseudo header.
+ *
+ *key_id_octet
+ *             The key ID octet from the IV.  Only valid when first_msdu is
+ *             set.
+ *
+ *classification_filter
+ *             Indicates the number classification filter rule
+ *
+ *ext_wapi_pn_63_48
+ *             Extension PN (packet number) which is only used by WAPI.
+ *             This corresponds to WAPI PN bits [63:48] (pn6 and pn7).  The
+ *             WAPI PN bits [63:0] are in the pn field of the rx_mpdu_start
+ *             descriptor.
+ *
+ *ext_wapi_pn_95_64
+ *             Extension PN (packet number) which is only used by WAPI.
+ *             This corresponds to WAPI PN bits [95:64] (pn8, pn9, pn10 and
+ *             pn11).
+ *
+ *ext_wapi_pn_127_96
+ *             Extension PN (packet number) which is only used by WAPI.
+ *             This corresponds to WAPI PN bits [127:96] (pn12, pn13, pn14,
+ *             pn15).
+ *
+ *reported_mpdu_length
+ *             MPDU length before decapsulation.  Only valid when
+ *             first_msdu is set.  This field is taken directly from the
+ *             length field of the A-MPDU delimiter or the preamble length
+ *             field for non-A-MPDU frames.
+ *
+ *first_msdu
+ *             Indicates the first MSDU of A-MSDU.  If both first_msdu and
+ *             last_msdu are set in the MSDU then this is a non-aggregated
+ *             MSDU frame: normal MPDU.  Interior MSDU in an A-MSDU shall
+ *             have both first_mpdu and last_mpdu bits set to 0.
+ *
+ *last_msdu
+ *             Indicates the last MSDU of the A-MSDU.  MPDU end status is
+ *             only valid when last_msdu is set.
+ *
+ *reserved_3a
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ *
+ *pre_delim_err
+ *             Indicates that the first delimiter had a FCS failure.  Only
+ *             valid when first_mpdu and first_msdu are set.
+ *
+ *reserved_3b
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ */
+
+#define RX_PPDU_START_SIG_RATE_SELECT_OFDM 0
+#define RX_PPDU_START_SIG_RATE_SELECT_CCK  1
+
+#define RX_PPDU_START_SIG_RATE_OFDM_48 0
+#define RX_PPDU_START_SIG_RATE_OFDM_24 1
+#define RX_PPDU_START_SIG_RATE_OFDM_12 2
+#define RX_PPDU_START_SIG_RATE_OFDM_6  3
+#define RX_PPDU_START_SIG_RATE_OFDM_54 4
+#define RX_PPDU_START_SIG_RATE_OFDM_36 5
+#define RX_PPDU_START_SIG_RATE_OFDM_18 6
+#define RX_PPDU_START_SIG_RATE_OFDM_9  7
+
+#define RX_PPDU_START_SIG_RATE_CCK_LP_11  0
+#define RX_PPDU_START_SIG_RATE_CCK_LP_5_5 1
+#define RX_PPDU_START_SIG_RATE_CCK_LP_2   2
+#define RX_PPDU_START_SIG_RATE_CCK_LP_1   3
+#define RX_PPDU_START_SIG_RATE_CCK_SP_11  4
+#define RX_PPDU_START_SIG_RATE_CCK_SP_5_5 5
+#define RX_PPDU_START_SIG_RATE_CCK_SP_2   6
+
+#define HTT_RX_PPDU_START_PREAMBLE_LEGACY        0x04
+#define HTT_RX_PPDU_START_PREAMBLE_HT            0x08
+#define HTT_RX_PPDU_START_PREAMBLE_HT_WITH_TXBF  0x09
+#define HTT_RX_PPDU_START_PREAMBLE_VHT           0x0C
+#define HTT_RX_PPDU_START_PREAMBLE_VHT_WITH_TXBF 0x0D
+
+#define RX_PPDU_START_INFO0_IS_GREENFIELD (1 << 0)
+
+#define RX_PPDU_START_INFO1_L_SIG_RATE_MASK    0x0000000f
+#define RX_PPDU_START_INFO1_L_SIG_RATE_LSB     0
+#define RX_PPDU_START_INFO1_L_SIG_LENGTH_MASK  0x0001ffe0
+#define RX_PPDU_START_INFO1_L_SIG_LENGTH_LSB   5
+#define RX_PPDU_START_INFO1_L_SIG_TAIL_MASK    0x00fc0000
+#define RX_PPDU_START_INFO1_L_SIG_TAIL_LSB     18
+#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_MASK 0xff000000
+#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_LSB  24
+#define RX_PPDU_START_INFO1_L_SIG_RATE_SELECT  (1 << 4)
+#define RX_PPDU_START_INFO1_L_SIG_PARITY       (1 << 17)
+
+#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_MASK 0x00ffffff
+#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_LSB  0
+
+#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_MASK 0x00ffffff
+#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_LSB  0
+#define RX_PPDU_START_INFO3_TXBF_H_INFO             (1 << 24)
+
+#define RX_PPDU_START_INFO4_VHT_SIG_B_MASK 0x1fffffff
+#define RX_PPDU_START_INFO4_VHT_SIG_B_LSB  0
+
+#define RX_PPDU_START_INFO5_SERVICE_MASK 0x0000ffff
+#define RX_PPDU_START_INFO5_SERVICE_LSB  0
+
+struct rx_ppdu_start {
+       struct {
+               u8 pri20_mhz;
+               u8 ext20_mhz;
+               u8 ext40_mhz;
+               u8 ext80_mhz;
+       } rssi_chains[4];
+       u8 rssi_comb;
+       __le16 rsvd0;
+       u8 info0; /* %RX_PPDU_START_INFO0_ */
+       __le32 info1; /* %RX_PPDU_START_INFO1_ */
+       __le32 info2; /* %RX_PPDU_START_INFO2_ */
+       __le32 info3; /* %RX_PPDU_START_INFO3_ */
+       __le32 info4; /* %RX_PPDU_START_INFO4_ */
+       __le32 info5; /* %RX_PPDU_START_INFO5_ */
+} __packed;
+
+/*
+ * rssi_chain0_pri20
+ *             RSSI of RX PPDU on chain 0 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec20
+ *             RSSI of RX PPDU on chain 0 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec40
+ *             RSSI of RX PPDU on chain 0 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec80
+ *             RSSI of RX PPDU on chain 0 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_pri20
+ *             RSSI of RX PPDU on chain 1 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec20
+ *             RSSI of RX PPDU on chain 1 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec40
+ *             RSSI of RX PPDU on chain 1 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec80
+ *             RSSI of RX PPDU on chain 1 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_pri20
+ *             RSSI of RX PPDU on chain 2 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec20
+ *             RSSI of RX PPDU on chain 2 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec40
+ *             RSSI of RX PPDU on chain 2 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec80
+ *             RSSI of RX PPDU on chain 2 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_pri20
+ *             RSSI of RX PPDU on chain 3 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec20
+ *             RSSI of RX PPDU on chain 3 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec40
+ *             RSSI of RX PPDU on chain 3 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec80
+ *             RSSI of RX PPDU on chain 3 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_comb
+ *             The combined RSSI of RX PPDU of all active chains and
+ *             bandwidths.  Value of 0x80 indicates invalid.
+ *
+ * reserved_4a
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * is_greenfield
+ *             Do we really support this?
+ *
+ * reserved_4b
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * l_sig_rate
+ *             If l_sig_rate_select is 0:
+ *             0x8: OFDM 48 Mbps
+ *             0x9: OFDM 24 Mbps
+ *             0xA: OFDM 12 Mbps
+ *             0xB: OFDM 6 Mbps
+ *             0xC: OFDM 54 Mbps
+ *             0xD: OFDM 36 Mbps
+ *             0xE: OFDM 18 Mbps
+ *             0xF: OFDM 9 Mbps
+ *             If l_sig_rate_select is 1:
+ *             0x8: CCK 11 Mbps long preamble
+ *             0x9: CCK 5.5 Mbps long preamble
+ *             0xA: CCK 2 Mbps long preamble
+ *             0xB: CCK 1 Mbps long preamble
+ *             0xC: CCK 11 Mbps short preamble
+ *             0xD: CCK 5.5 Mbps short preamble
+ *             0xE: CCK 2 Mbps short preamble
+ *
+ * l_sig_rate_select
+ *             Legacy signal rate select.  If set then l_sig_rate indicates
+ *             CCK rates.  If clear then l_sig_rate indicates OFDM rates.
+ *
+ * l_sig_length
+ *             Length of legacy frame in octets.
+ *
+ * l_sig_parity
+ *             Odd parity over l_sig_rate and l_sig_length
+ *
+ * l_sig_tail
+ *             Tail bits for Viterbi decoder
+ *
+ * preamble_type
+ *             Indicates the type of preamble ahead:
+ *             0x4: Legacy (OFDM/CCK)
+ *             0x8: HT
+ *             0x9: HT with TxBF
+ *             0xC: VHT
+ *             0xD: VHT with TxBF
+ *             0x80 - 0xFF: Reserved for special baseband data types such
+ *             as radar and spectral scan.
+ *
+ * ht_sig_vht_sig_a_1
+ *             If preamble_type == 0x8 or 0x9
+ *             HT-SIG (first 24 bits)
+ *             If preamble_type == 0xC or 0xD
+ *             VHT-SIG A (first 24 bits)
+ *             Else
+ *             Reserved
+ *
+ * reserved_6
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * ht_sig_vht_sig_a_2
+ *             If preamble_type == 0x8 or 0x9
+ *             HT-SIG (last 24 bits)
+ *             If preamble_type == 0xC or 0xD
+ *             VHT-SIG A (last 24 bits)
+ *             Else
+ *             Reserved
+ *
+ * txbf_h_info
+ *             Indicates that the packet data carries H information which
+ *             is used for TxBF debug.
+ *
+ * reserved_7
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * vht_sig_b
+ *             WiFi 1.0 and WiFi 2.0 will likely have this field to be all
+ *             0s since the BB does not plan on decoding VHT SIG-B.
+ *
+ * reserved_8
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * service
+ *             Service field from BB for OFDM, HT and VHT packets.  CCK
+ *             packets will have service field of 0.
+ *
+ * reserved_9
+ *             Reserved: HW should fill with 0, FW should ignore.
+*/
+
+
+#define RX_PPDU_END_FLAGS_PHY_ERR             (1 << 0)
+#define RX_PPDU_END_FLAGS_RX_LOCATION         (1 << 1)
+#define RX_PPDU_END_FLAGS_TXBF_H_INFO         (1 << 2)
+
+#define RX_PPDU_END_INFO0_RX_ANTENNA_MASK     0x00ffffff
+#define RX_PPDU_END_INFO0_RX_ANTENNA_LSB      0
+#define RX_PPDU_END_INFO0_FLAGS_TX_HT_VHT_ACK (1 << 24)
+#define RX_PPDU_END_INFO0_BB_CAPTURED_CHANNEL (1 << 25)
+
+#define RX_PPDU_END_INFO1_PPDU_DONE (1 << 15)
+
+struct rx_ppdu_end {
+       __le32 evm_p0;
+       __le32 evm_p1;
+       __le32 evm_p2;
+       __le32 evm_p3;
+       __le32 evm_p4;
+       __le32 evm_p5;
+       __le32 evm_p6;
+       __le32 evm_p7;
+       __le32 evm_p8;
+       __le32 evm_p9;
+       __le32 evm_p10;
+       __le32 evm_p11;
+       __le32 evm_p12;
+       __le32 evm_p13;
+       __le32 evm_p14;
+       __le32 evm_p15;
+       __le32 tsf_timestamp;
+       __le32 wb_timestamp;
+       u8 locationing_timestamp;
+       u8 phy_err_code;
+       __le16 flags; /* %RX_PPDU_END_FLAGS_ */
+       __le32 info0; /* %RX_PPDU_END_INFO0_ */
+       __le16 bb_length;
+       __le16 info1; /* %RX_PPDU_END_INFO1_ */
+} __packed;
+
+/*
+ * evm_p0
+ *             EVM for pilot 0.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p1
+ *             EVM for pilot 1.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p2
+ *             EVM for pilot 2.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p3
+ *             EVM for pilot 3.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p4
+ *             EVM for pilot 4.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p5
+ *             EVM for pilot 5.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p6
+ *             EVM for pilot 6.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p7
+ *             EVM for pilot 7.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p8
+ *             EVM for pilot 8.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p9
+ *             EVM for pilot 9.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p10
+ *             EVM for pilot 10.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p11
+ *             EVM for pilot 11.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p12
+ *             EVM for pilot 12.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p13
+ *             EVM for pilot 13.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p14
+ *             EVM for pilot 14.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p15
+ *             EVM for pilot 15.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * tsf_timestamp
+ *             Receive TSF timestamp sampled on the rising edge of
+ *             rx_clear.  For PHY errors this may be the current TSF when
+ *             phy_error is asserted if the rx_clear does not assert before
+ *             the end of the PHY error.
+ *
+ * wb_timestamp
+ *             WLAN/BT timestamp is a 1 usec resolution timestamp which
+ *             does not get updated based on receive beacon like TSF.  The
+ *             same rules for capturing tsf_timestamp are used to capture
+ *             the wb_timestamp.
+ *
+ * locationing_timestamp
+ *             Timestamp used for locationing.  This timestamp is used to
+ *             indicate fractions of usec.  For example if the MAC clock is
+ *             running at 80 MHz, the timestamp will increment every 12.5
+ *             nsec.  The value starts at 0 and increments to 79 and
+ *             returns to 0 and repeats.  This information is valid for
+ *             every PPDU.  This information can be used in conjunction
+ *             with wb_timestamp to capture large delta times.
+ *
+ * phy_err_code
+ *             See the 1.10.8.1.2 for the list of the PHY error codes.
+ *
+ * phy_err
+ *             Indicates a PHY error was detected for this PPDU.
+ *
+ * rx_location
+ *             Indicates that location information was requested.
+ *
+ * txbf_h_info
+ *             Indicates that the packet data carries H information which
+ *             is used for TxBF debug.
+ *
+ * reserved_18
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * rx_antenna
+ *             Receive antenna value
+ *
+ * tx_ht_vht_ack
+ *             Indicates that a HT or VHT Ack/BA frame was transmitted in
+ *             response to this receive packet.
+ *
+ * bb_captured_channel
+ *             Indicates that the BB has captured a channel dump.  FW can
+ *             then read the channel dump memory.  This may indicate that
+ *             the channel was captured either based on PCU setting the
+ *             capture_channel bit  BB descriptor or FW setting the
+ *             capture_channel mode bit.
+ *
+ * reserved_19
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * bb_length
+ *             Indicates the number of bytes of baseband information for
+ *             PPDUs where the BB descriptor preamble type is 0x80 to 0xFF
+ *             which indicates that this is not a normal PPDU but rather
+ *             contains baseband debug information.
+ *
+ * reserved_20
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * ppdu_done
+ *             PPDU end status is only valid when ppdu_done bit is set.
+ *             Every time HW sets this bit in memory FW/SW must clear this
+ *             bit in memory.  FW will initialize all the ppdu_done dword
+ *             to 0.
+*/
+
+#define FW_RX_DESC_INFO0_DISCARD  (1 << 0)
+#define FW_RX_DESC_INFO0_FORWARD  (1 << 1)
+#define FW_RX_DESC_INFO0_INSPECT  (1 << 5)
+#define FW_RX_DESC_INFO0_EXT_MASK 0xC0
+#define FW_RX_DESC_INFO0_EXT_LSB  6
+
+struct fw_rx_desc_base {
+       u8 info0;
+} __packed;
+
+#endif /* _RX_DESC_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h
new file mode 100644 (file)
index 0000000..be7ba1e
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __TARGADDRS_H__
+#define __TARGADDRS_H__
+
+/*
+ * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the
+ * host_interest structure.  It must match the address of the _host_interest
+ * symbol (see linker script).
+ *
+ * Host Interest is shared between Host and Target in order to coordinate
+ * between the two, and is intended to remain constant (with additions only
+ * at the end) across software releases.
+ *
+ * All addresses are available here so that it's possible to
+ * write a single binary that works with all Target Types.
+ * May be used in assembler code as well as C.
+ */
+#define QCA988X_HOST_INTEREST_ADDRESS    0x00400800
+#define HOST_INTEREST_MAX_SIZE          0x200
+
+/*
+ * These are items that the Host may need to access via BMI or via the
+ * Diagnostic Window. The position of items in this structure must remain
+ * constant across firmware revisions! Types for each item must be fixed
+ * size across target and host platforms. More items may be added at the end.
+ */
+struct host_interest {
+       /*
+        * Pointer to application-defined area, if any.
+        * Set by Target application during startup.
+        */
+       u32 hi_app_host_interest;                       /* 0x00 */
+
+       /* Pointer to register dump area, valid after Target crash. */
+       u32 hi_failure_state;                           /* 0x04 */
+
+       /* Pointer to debug logging header */
+       u32 hi_dbglog_hdr;                              /* 0x08 */
+
+       u32 hi_unused0c;                                /* 0x0c */
+
+       /*
+        * General-purpose flag bits, similar to SOC_OPTION_* flags.
+        * Can be used by application rather than by OS.
+        */
+       u32 hi_option_flag;                             /* 0x10 */
+
+       /*
+        * Boolean that determines whether or not to
+        * display messages on the serial port.
+        */
+       u32 hi_serial_enable;                           /* 0x14 */
+
+       /* Start address of DataSet index, if any */
+       u32 hi_dset_list_head;                          /* 0x18 */
+
+       /* Override Target application start address */
+       u32 hi_app_start;                               /* 0x1c */
+
+       /* Clock and voltage tuning */
+       u32 hi_skip_clock_init;                         /* 0x20 */
+       u32 hi_core_clock_setting;                      /* 0x24 */
+       u32 hi_cpu_clock_setting;                       /* 0x28 */
+       u32 hi_system_sleep_setting;                    /* 0x2c */
+       u32 hi_xtal_control_setting;                    /* 0x30 */
+       u32 hi_pll_ctrl_setting_24ghz;                  /* 0x34 */
+       u32 hi_pll_ctrl_setting_5ghz;                   /* 0x38 */
+       u32 hi_ref_voltage_trim_setting;                /* 0x3c */
+       u32 hi_clock_info;                              /* 0x40 */
+
+       /* Host uses BE CPU or not */
+       u32 hi_be;                                      /* 0x44 */
+
+       u32 hi_stack;   /* normal stack */                      /* 0x48 */
+       u32 hi_err_stack; /* error stack */             /* 0x4c */
+       u32 hi_desired_cpu_speed_hz;                    /* 0x50 */
+
+       /* Pointer to Board Data  */
+       u32 hi_board_data;                              /* 0x54 */
+
+       /*
+        * Indication of Board Data state:
+        *    0: board data is not yet initialized.
+        *    1: board data is initialized; unknown size
+        *   >1: number of bytes of initialized board data
+        */
+       u32 hi_board_data_initialized;                  /* 0x58 */
+
+       u32 hi_dset_ram_index_table;                    /* 0x5c */
+
+       u32 hi_desired_baud_rate;                       /* 0x60 */
+       u32 hi_dbglog_config;                           /* 0x64 */
+       u32 hi_end_ram_reserve_sz;                      /* 0x68 */
+       u32 hi_mbox_io_block_sz;                        /* 0x6c */
+
+       u32 hi_num_bpatch_streams;                      /* 0x70 -- unused */
+       u32 hi_mbox_isr_yield_limit;                    /* 0x74 */
+
+       u32 hi_refclk_hz;                               /* 0x78 */
+       u32 hi_ext_clk_detected;                        /* 0x7c */
+       u32 hi_dbg_uart_txpin;                          /* 0x80 */
+       u32 hi_dbg_uart_rxpin;                          /* 0x84 */
+       u32 hi_hci_uart_baud;                           /* 0x88 */
+       u32 hi_hci_uart_pin_assignments;                /* 0x8C */
+
+       u32 hi_hci_uart_baud_scale_val;                 /* 0x90 */
+       u32 hi_hci_uart_baud_step_val;                  /* 0x94 */
+
+       u32 hi_allocram_start;                          /* 0x98 */
+       u32 hi_allocram_sz;                             /* 0x9c */
+       u32 hi_hci_bridge_flags;                        /* 0xa0 */
+       u32 hi_hci_uart_support_pins;                   /* 0xa4 */
+
+       u32 hi_hci_uart_pwr_mgmt_params;                /* 0xa8 */
+
+       /*
+        * 0xa8 - [1]: 0 = UART FC active low, 1 = UART FC active high
+        *        [31:16]: wakeup timeout in ms
+        */
+       /* Pointer to extended board Data  */
+       u32 hi_board_ext_data;                          /* 0xac */
+       u32 hi_board_ext_data_config;                   /* 0xb0 */
+       /*
+        * Bit [0]  :   valid
+        * Bit[31:16:   size
+        */
+       /*
+        * hi_reset_flag is used to do some stuff when target reset.
+        * such as restore app_start after warm reset or
+        * preserve host Interest area, or preserve ROM data, literals etc.
+        */
+       u32  hi_reset_flag;                             /* 0xb4 */
+       /* indicate hi_reset_flag is valid */
+       u32  hi_reset_flag_valid;                       /* 0xb8 */
+       u32 hi_hci_uart_pwr_mgmt_params_ext;            /* 0xbc */
+       /* 0xbc - [31:0]: idle timeout in ms */
+       /* ACS flags */
+       u32 hi_acs_flags;                               /* 0xc0 */
+       u32 hi_console_flags;                           /* 0xc4 */
+       u32 hi_nvram_state;                             /* 0xc8 */
+       u32 hi_option_flag2;                            /* 0xcc */
+
+       /* If non-zero, override values sent to Host in WMI_READY event. */
+       u32 hi_sw_version_override;                     /* 0xd0 */
+       u32 hi_abi_version_override;                    /* 0xd4 */
+
+       /*
+        * Percentage of high priority RX traffic to total expected RX traffic
+        * applicable only to ar6004
+        */
+       u32 hi_hp_rx_traffic_ratio;                     /* 0xd8 */
+
+       /* test applications flags */
+       u32 hi_test_apps_related;                       /* 0xdc */
+       /* location of test script */
+       u32 hi_ota_testscript;                          /* 0xe0 */
+       /* location of CAL data */
+       u32 hi_cal_data;                                /* 0xe4 */
+
+       /* Number of packet log buffers */
+       u32 hi_pktlog_num_buffers;                      /* 0xe8 */
+
+       /* wow extension configuration */
+       u32 hi_wow_ext_config;                          /* 0xec */
+       u32 hi_pwr_save_flags;                          /* 0xf0 */
+
+       /* Spatial Multiplexing Power Save (SMPS) options */
+       u32 hi_smps_options;                            /* 0xf4 */
+
+       /* Interconnect-specific state */
+       u32 hi_interconnect_state;                      /* 0xf8 */
+
+       /* Coex configuration flags */
+       u32 hi_coex_config;                             /* 0xfc */
+
+       /* Early allocation support */
+       u32 hi_early_alloc;                             /* 0x100 */
+       /* FW swap field */
+       /*
+        * Bits of this 32bit word will be used to pass specific swap
+        * instruction to FW
+        */
+       /*
+        * Bit 0 -- AP Nart descriptor no swap. When this bit is set
+        * FW will not swap TX descriptor. Meaning packets are formed
+        * on the target processor.
+        */
+       /* Bit 1 - unused */
+       u32 hi_fw_swap;                                 /* 0x104 */
+} __packed;
+
+#define HI_ITEM(item)  offsetof(struct host_interest, item)
+
+/* Bits defined in hi_option_flag */
+
+/* Enable timer workaround */
+#define HI_OPTION_TIMER_WAR         0x01
+/* Limit BMI command credits */
+#define HI_OPTION_BMI_CRED_LIMIT    0x02
+/* Relay Dot11 hdr to/from host */
+#define HI_OPTION_RELAY_DOT11_HDR   0x04
+/* MAC addr method 0-locally administred 1-globally unique addrs */
+#define HI_OPTION_MAC_ADDR_METHOD   0x08
+/* Firmware Bridging */
+#define HI_OPTION_FW_BRIDGE         0x10
+/* Enable CPU profiling */
+#define HI_OPTION_ENABLE_PROFILE    0x20
+/* Disable debug logging */
+#define HI_OPTION_DISABLE_DBGLOG    0x40
+/* Skip Era Tracking */
+#define HI_OPTION_SKIP_ERA_TRACKING 0x80
+/* Disable PAPRD (debug) */
+#define HI_OPTION_PAPRD_DISABLE     0x100
+#define HI_OPTION_NUM_DEV_LSB       0x200
+#define HI_OPTION_NUM_DEV_MSB       0x800
+#define HI_OPTION_DEV_MODE_LSB      0x1000
+#define HI_OPTION_DEV_MODE_MSB      0x8000000
+/* Disable LowFreq Timer Stabilization */
+#define HI_OPTION_NO_LFT_STBL       0x10000000
+/* Skip regulatory scan */
+#define HI_OPTION_SKIP_REG_SCAN     0x20000000
+/*
+ * Do regulatory scan during init before
+ * sending WMI ready event to host
+ */
+#define HI_OPTION_INIT_REG_SCAN     0x40000000
+
+/* REV6: Do not adjust memory map */
+#define HI_OPTION_SKIP_MEMMAP       0x80000000
+
+#define HI_OPTION_MAC_ADDR_METHOD_SHIFT 3
+
+/* 2 bits of hi_option_flag are used to represent 3 modes */
+#define HI_OPTION_FW_MODE_IBSS    0x0 /* IBSS Mode */
+#define HI_OPTION_FW_MODE_BSS_STA 0x1 /* STA Mode */
+#define HI_OPTION_FW_MODE_AP      0x2 /* AP Mode */
+#define HI_OPTION_FW_MODE_BT30AMP 0x3 /* BT30 AMP Mode */
+
+/* 2 bits of hi_option flag are usedto represent 4 submodes */
+#define HI_OPTION_FW_SUBMODE_NONE    0x0  /* Normal mode */
+#define HI_OPTION_FW_SUBMODE_P2PDEV  0x1  /* p2p device mode */
+#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2 /* p2p client mode */
+#define HI_OPTION_FW_SUBMODE_P2PGO   0x3 /* p2p go mode */
+
+/* Num dev Mask */
+#define HI_OPTION_NUM_DEV_MASK    0x7
+#define HI_OPTION_NUM_DEV_SHIFT   0x9
+
+/* firmware bridging */
+#define HI_OPTION_FW_BRIDGE_SHIFT 0x04
+
+/*
+Fw Mode/SubMode Mask
+|-----------------------------------------------------------------------------|
+|  SUB   |   SUB   |   SUB   |  SUB    |         |         |         |        |
+|MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0]|
+|  (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)  |
+|-----------------------------------------------------------------------------|
+*/
+#define HI_OPTION_FW_MODE_BITS         0x2
+#define HI_OPTION_FW_MODE_MASK         0x3
+#define HI_OPTION_FW_MODE_SHIFT        0xC
+#define HI_OPTION_ALL_FW_MODE_MASK     0xFF
+
+#define HI_OPTION_FW_SUBMODE_BITS      0x2
+#define HI_OPTION_FW_SUBMODE_MASK      0x3
+#define HI_OPTION_FW_SUBMODE_SHIFT     0x14
+#define HI_OPTION_ALL_FW_SUBMODE_MASK  0xFF00
+#define HI_OPTION_ALL_FW_SUBMODE_SHIFT 0x8
+
+
+/* hi_option_flag2 options */
+#define HI_OPTION_OFFLOAD_AMSDU     0x01
+#define HI_OPTION_DFS_SUPPORT       0x02 /* Enable DFS support */
+#define HI_OPTION_ENABLE_RFKILL     0x04 /* RFKill Enable Feature*/
+#define HI_OPTION_RADIO_RETENTION_DISABLE 0x08 /* Disable radio retention */
+#define HI_OPTION_EARLY_CFG_DONE    0x10 /* Early configuration is complete */
+
+#define HI_OPTION_RF_KILL_SHIFT     0x2
+#define HI_OPTION_RF_KILL_MASK      0x1
+
+/* hi_reset_flag */
+/* preserve App Start address */
+#define HI_RESET_FLAG_PRESERVE_APP_START         0x01
+/* preserve host interest */
+#define HI_RESET_FLAG_PRESERVE_HOST_INTEREST     0x02
+/* preserve ROM data */
+#define HI_RESET_FLAG_PRESERVE_ROMDATA           0x04
+#define HI_RESET_FLAG_PRESERVE_NVRAM_STATE       0x08
+#define HI_RESET_FLAG_PRESERVE_BOOT_INFO         0x10
+#define HI_RESET_FLAG_WARM_RESET       0x20
+
+/* define hi_fw_swap bits */
+#define HI_DESC_IN_FW_BIT      0x01
+
+/* indicate the reset flag is valid */
+#define HI_RESET_FLAG_IS_VALID  0x12345678
+
+/* ACS is enabled */
+#define HI_ACS_FLAGS_ENABLED        (1 << 0)
+/* Use physical WWAN device */
+#define HI_ACS_FLAGS_USE_WWAN       (1 << 1)
+/* Use test VAP */
+#define HI_ACS_FLAGS_TEST_VAP       (1 << 2)
+
+/*
+ * CONSOLE FLAGS
+ *
+ * Bit Range  Meaning
+ * ---------  --------------------------------
+ *   2..0     UART ID (0 = Default)
+ *    3       Baud Select (0 = 9600, 1 = 115200)
+ *   30..4    Reserved
+ *    31      Enable Console
+ *
+ */
+
+#define HI_CONSOLE_FLAGS_ENABLE       (1 << 31)
+#define HI_CONSOLE_FLAGS_UART_MASK    (0x7)
+#define HI_CONSOLE_FLAGS_UART_SHIFT   0
+#define HI_CONSOLE_FLAGS_BAUD_SELECT  (1 << 3)
+
+/* SM power save options */
+#define HI_SMPS_ALLOW_MASK            (0x00000001)
+#define HI_SMPS_MODE_MASK             (0x00000002)
+#define HI_SMPS_MODE_STATIC           (0x00000000)
+#define HI_SMPS_MODE_DYNAMIC          (0x00000002)
+#define HI_SMPS_DISABLE_AUTO_MODE     (0x00000004)
+#define HI_SMPS_DATA_THRESH_MASK      (0x000007f8)
+#define HI_SMPS_DATA_THRESH_SHIFT     (3)
+#define HI_SMPS_RSSI_THRESH_MASK      (0x0007f800)
+#define HI_SMPS_RSSI_THRESH_SHIFT     (11)
+#define HI_SMPS_LOWPWR_CM_MASK        (0x00380000)
+#define HI_SMPS_LOWPWR_CM_SHIFT       (15)
+#define HI_SMPS_HIPWR_CM_MASK         (0x03c00000)
+#define HI_SMPS_HIPWR_CM_SHIFT        (19)
+
+/*
+ * WOW Extension configuration
+ *
+ * Bit Range  Meaning
+ * ---------  --------------------------------
+ *   8..0     Size of each WOW pattern (max 511)
+ *   15..9    Number of patterns per list (max 127)
+ *   17..16   Number of lists (max 4)
+ *   30..18   Reserved
+ *   31       Enabled
+ *
+ *  set values (except enable) to zeros for default settings
+ */
+
+#define HI_WOW_EXT_ENABLED_MASK        (1 << 31)
+#define HI_WOW_EXT_NUM_LIST_SHIFT      16
+#define HI_WOW_EXT_NUM_LIST_MASK       (0x3 << HI_WOW_EXT_NUM_LIST_SHIFT)
+#define HI_WOW_EXT_NUM_PATTERNS_SHIFT  9
+#define HI_WOW_EXT_NUM_PATTERNS_MASK   (0x7F << HI_WOW_EXT_NUM_PATTERNS_SHIFT)
+#define HI_WOW_EXT_PATTERN_SIZE_SHIFT  0
+#define HI_WOW_EXT_PATTERN_SIZE_MASK   (0x1FF << HI_WOW_EXT_PATTERN_SIZE_SHIFT)
+
+#define HI_WOW_EXT_MAKE_CONFIG(num_lists, count, size) \
+       ((((num_lists) << HI_WOW_EXT_NUM_LIST_SHIFT) & \
+               HI_WOW_EXT_NUM_LIST_MASK) | \
+       (((count) << HI_WOW_EXT_NUM_PATTERNS_SHIFT) & \
+               HI_WOW_EXT_NUM_PATTERNS_MASK) | \
+       (((size) << HI_WOW_EXT_PATTERN_SIZE_SHIFT) & \
+               HI_WOW_EXT_PATTERN_SIZE_MASK))
+
+#define HI_WOW_EXT_GET_NUM_LISTS(config) \
+       (((config) & HI_WOW_EXT_NUM_LIST_MASK) >> HI_WOW_EXT_NUM_LIST_SHIFT)
+#define HI_WOW_EXT_GET_NUM_PATTERNS(config) \
+       (((config) & HI_WOW_EXT_NUM_PATTERNS_MASK) >> \
+               HI_WOW_EXT_NUM_PATTERNS_SHIFT)
+#define HI_WOW_EXT_GET_PATTERN_SIZE(config) \
+       (((config) & HI_WOW_EXT_PATTERN_SIZE_MASK) >> \
+               HI_WOW_EXT_PATTERN_SIZE_SHIFT)
+
+/*
+ * Early allocation configuration
+ * Support RAM bank configuration before BMI done and this eases the memory
+ * allocation at very early stage
+ * Bit Range  Meaning
+ * ---------  ----------------------------------
+ * [0:3]      number of bank assigned to be IRAM
+ * [4:15]     reserved
+ * [16:31]    magic number
+ *
+ * Note:
+ * 1. target firmware would check magic number and if it's a match, firmware
+ *    would consider the bits[0:15] are valid and base on that to calculate
+ *    the end of DRAM. Early allocation would be located at that area and
+ *    may be reclaimed when necesary
+ * 2. if no magic number is found, early allocation would happen at "_end"
+ *    symbol of ROM which is located before the app-data and might NOT be
+ *    re-claimable. If this is adopted, link script should keep this in
+ *    mind to avoid data corruption.
+ */
+#define HI_EARLY_ALLOC_MAGIC           0x6d8a
+#define HI_EARLY_ALLOC_MAGIC_MASK      0xffff0000
+#define HI_EARLY_ALLOC_MAGIC_SHIFT     16
+#define HI_EARLY_ALLOC_IRAM_BANKS_MASK 0x0000000f
+#define HI_EARLY_ALLOC_IRAM_BANKS_SHIFT        0
+
+#define HI_EARLY_ALLOC_VALID() \
+       ((((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_MAGIC_MASK) >> \
+       HI_EARLY_ALLOC_MAGIC_SHIFT) == (HI_EARLY_ALLOC_MAGIC))
+#define HI_EARLY_ALLOC_GET_IRAM_BANKS() \
+       (((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_IRAM_BANKS_MASK) \
+       >> HI_EARLY_ALLOC_IRAM_BANKS_SHIFT)
+
+/*power save flag bit definitions*/
+#define HI_PWR_SAVE_LPL_ENABLED   0x1
+/*b1-b3 reserved*/
+/*b4-b5 : dev0 LPL type : 0 - none
+                         1- Reduce Pwr Search
+                         2- Reduce Pwr Listen*/
+/*b6-b7 : dev1 LPL type and so on for Max 8 devices*/
+#define HI_PWR_SAVE_LPL_DEV0_LSB   4
+#define HI_PWR_SAVE_LPL_DEV_MASK   0x3
+/*power save related utility macros*/
+#define HI_LPL_ENABLED() \
+       ((HOST_INTEREST->hi_pwr_save_flags & HI_PWR_SAVE_LPL_ENABLED))
+#define HI_DEV_LPL_TYPE_GET(_devix) \
+       (HOST_INTEREST->hi_pwr_save_flags & ((HI_PWR_SAVE_LPL_DEV_MASK) << \
+        (HI_PWR_SAVE_LPL_DEV0_LSB + (_devix)*2)))
+
+#define HOST_INTEREST_SMPS_IS_ALLOWED() \
+       ((HOST_INTEREST->hi_smps_options & HI_SMPS_ALLOW_MASK))
+
+/* Reserve 1024 bytes for extended board data */
+#define QCA988X_BOARD_DATA_SZ     7168
+#define QCA988X_BOARD_EXT_DATA_SZ 0
+
+#endif /* __TARGADDRS_H__ */
diff --git a/drivers/net/wireless/ath/ath10k/trace.c b/drivers/net/wireless/ath/ath10k/trace.c
new file mode 100644 (file)
index 0000000..4a31e2c
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h
new file mode 100644 (file)
index 0000000..85e806b
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+
+#include <linux/tracepoint.h>
+
+#define _TRACE_H_
+
+/* create empty functions when tracing is disabled */
+#if !defined(CONFIG_ATH10K_TRACING)
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif /* !CONFIG_ATH10K_TRACING || __CHECKER__ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ath10k
+
+#define ATH10K_MSG_MAX 200
+
+DECLARE_EVENT_CLASS(ath10k_log_event,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf),
+       TP_STRUCT__entry(
+               __dynamic_array(char, msg, ATH10K_MSG_MAX)
+       ),
+       TP_fast_assign(
+               WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+                                      ATH10K_MSG_MAX,
+                                      vaf->fmt,
+                                      *vaf->va) >= ATH10K_MSG_MAX);
+       ),
+       TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_err,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_warn,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_info,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+TRACE_EVENT(ath10k_log_dbg,
+       TP_PROTO(unsigned int level, struct va_format *vaf),
+       TP_ARGS(level, vaf),
+       TP_STRUCT__entry(
+               __field(unsigned int, level)
+               __dynamic_array(char, msg, ATH10K_MSG_MAX)
+       ),
+       TP_fast_assign(
+               __entry->level = level;
+               WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+                                      ATH10K_MSG_MAX,
+                                      vaf->fmt,
+                                      *vaf->va) >= ATH10K_MSG_MAX);
+       ),
+       TP_printk("%s", __get_str(msg))
+);
+
+TRACE_EVENT(ath10k_log_dbg_dump,
+       TP_PROTO(const char *msg, const char *prefix,
+                const void *buf, size_t buf_len),
+
+       TP_ARGS(msg, prefix, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __string(msg, msg)
+               __string(prefix, prefix)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __assign_str(msg, msg);
+               __assign_str(prefix, prefix);
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "%s/%s\n", __get_str(prefix), __get_str(msg)
+       )
+);
+
+TRACE_EVENT(ath10k_wmi_cmd,
+       TP_PROTO(int id, void *buf, size_t buf_len),
+
+       TP_ARGS(id, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(unsigned int, id)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->id = id;
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "id %d len %zu",
+               __entry->id,
+               __entry->buf_len
+       )
+);
+
+TRACE_EVENT(ath10k_wmi_event,
+       TP_PROTO(int id, void *buf, size_t buf_len),
+
+       TP_ARGS(id, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(unsigned int, id)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->id = id;
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "id %d len %zu",
+               __entry->id,
+               __entry->buf_len
+       )
+);
+
+#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
+
+/* we don't want to use include/trace/events */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
new file mode 100644 (file)
index 0000000..68b6fae
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "txrx.h"
+#include "htt.h"
+#include "mac.h"
+#include "debug.h"
+
+static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb)
+{
+       if (!ATH10K_SKB_CB(skb)->htt.is_offchan)
+               return;
+
+       /* If the original wait_for_completion() timed out before
+        * {data,mgmt}_tx_completed() was called then we could complete
+        * offchan_tx_completed for a different skb. Prevent this by using
+        * offchan_tx_skb. */
+       spin_lock_bh(&ar->data_lock);
+       if (ar->offchan_tx_skb != skb) {
+               ath10k_warn("completed old offchannel frame\n");
+               goto out;
+       }
+
+       complete(&ar->offchan_tx_completed);
+       ar->offchan_tx_skb = NULL; /* just for sanity */
+
+       ath10k_dbg(ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb);
+out:
+       spin_unlock_bh(&ar->data_lock);
+}
+
+void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc)
+{
+       struct device *dev = htt->ar->dev;
+       struct ieee80211_tx_info *info;
+       struct sk_buff *txfrag = ATH10K_SKB_CB(txdesc)->htt.txfrag;
+       struct sk_buff *msdu = ATH10K_SKB_CB(txdesc)->htt.msdu;
+       int ret;
+
+       if (ATH10K_SKB_CB(txdesc)->htt.refcount == 0)
+               return;
+
+       ATH10K_SKB_CB(txdesc)->htt.refcount--;
+
+       if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
+               return;
+
+       if (txfrag) {
+               ret = ath10k_skb_unmap(dev, txfrag);
+               if (ret)
+                       ath10k_warn("txfrag unmap failed (%d)\n", ret);
+
+               dev_kfree_skb_any(txfrag);
+       }
+
+       ret = ath10k_skb_unmap(dev, msdu);
+       if (ret)
+               ath10k_warn("data skb unmap failed (%d)\n", ret);
+
+       ath10k_report_offchan_tx(htt->ar, msdu);
+
+       info = IEEE80211_SKB_CB(msdu);
+       memset(&info->status, 0, sizeof(info->status));
+
+       if (ATH10K_SKB_CB(txdesc)->htt.discard) {
+               ieee80211_free_txskb(htt->ar->hw, msdu);
+               goto exit;
+       }
+
+       if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
+               info->flags |= IEEE80211_TX_STAT_ACK;
+
+       if (ATH10K_SKB_CB(txdesc)->htt.no_ack)
+               info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+       ieee80211_tx_status(htt->ar->hw, msdu);
+       /* we do not own the msdu anymore */
+
+exit:
+       spin_lock_bh(&htt->tx_lock);
+       htt->pending_tx[ATH10K_SKB_CB(txdesc)->htt.msdu_id] = NULL;
+       ath10k_htt_tx_free_msdu_id(htt, ATH10K_SKB_CB(txdesc)->htt.msdu_id);
+       __ath10k_htt_tx_dec_pending(htt);
+       if (bitmap_empty(htt->used_msdu_ids, htt->max_num_pending_tx))
+               wake_up(&htt->empty_tx_wq);
+       spin_unlock_bh(&htt->tx_lock);
+
+       dev_kfree_skb_any(txdesc);
+}
+
+void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
+                             const struct htt_tx_done *tx_done)
+{
+       struct sk_buff *txdesc;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n",
+                  tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack);
+
+       if (tx_done->msdu_id >= htt->max_num_pending_tx) {
+               ath10k_warn("warning: msdu_id %d too big, ignoring\n",
+                           tx_done->msdu_id);
+               return;
+       }
+
+       txdesc = htt->pending_tx[tx_done->msdu_id];
+
+       ATH10K_SKB_CB(txdesc)->htt.discard = tx_done->discard;
+       ATH10K_SKB_CB(txdesc)->htt.no_ack = tx_done->no_ack;
+
+       ath10k_txrx_tx_unref(htt, txdesc);
+}
+
+static const u8 rx_legacy_rate_idx[] = {
+       3,      /* 0x00  - 11Mbps  */
+       2,      /* 0x01  - 5.5Mbps */
+       1,      /* 0x02  - 2Mbps   */
+       0,      /* 0x03  - 1Mbps   */
+       3,      /* 0x04  - 11Mbps  */
+       2,      /* 0x05  - 5.5Mbps */
+       1,      /* 0x06  - 2Mbps   */
+       0,      /* 0x07  - 1Mbps   */
+       10,     /* 0x08  - 48Mbps  */
+       8,      /* 0x09  - 24Mbps  */
+       6,      /* 0x0A  - 12Mbps  */
+       4,      /* 0x0B  - 6Mbps   */
+       11,     /* 0x0C  - 54Mbps  */
+       9,      /* 0x0D  - 36Mbps  */
+       7,      /* 0x0E  - 18Mbps  */
+       5,      /* 0x0F  - 9Mbps   */
+};
+
+static void process_rx_rates(struct ath10k *ar, struct htt_rx_info *info,
+                            enum ieee80211_band band,
+                            struct ieee80211_rx_status *status)
+{
+       u8 cck, rate, rate_idx, bw, sgi, mcs, nss;
+       u8 info0 = info->rate.info0;
+       u32 info1 = info->rate.info1;
+       u32 info2 = info->rate.info2;
+       u8 preamble = 0;
+
+       /* Check if valid fields */
+       if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID))
+               return;
+
+       preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE);
+
+       switch (preamble) {
+       case HTT_RX_LEGACY:
+               cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK;
+               rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE);
+               rate_idx = 0;
+
+               if (rate < 0x08 || rate > 0x0F)
+                       break;
+
+               switch (band) {
+               case IEEE80211_BAND_2GHZ:
+                       if (cck)
+                               rate &= ~BIT(3);
+                       rate_idx = rx_legacy_rate_idx[rate];
+                       break;
+               case IEEE80211_BAND_5GHZ:
+                       rate_idx = rx_legacy_rate_idx[rate];
+                       /* We are using same rate table registering
+                          HW - ath10k_rates[]. In case of 5GHz skip
+                          CCK rates, so -4 here */
+                       rate_idx -= 4;
+                       break;
+               default:
+                       break;
+               }
+
+               status->rate_idx = rate_idx;
+               break;
+       case HTT_RX_HT:
+       case HTT_RX_HT_WITH_TXBF:
+               /* HT-SIG - Table 20-11 in info1 and info2 */
+               mcs = info1 & 0x1F;
+               nss = mcs >> 3;
+               bw = (info1 >> 7) & 1;
+               sgi = (info2 >> 7) & 1;
+
+               status->rate_idx = mcs;
+               status->flag |= RX_FLAG_HT;
+               if (sgi)
+                       status->flag |= RX_FLAG_SHORT_GI;
+               if (bw)
+                       status->flag |= RX_FLAG_40MHZ;
+               break;
+       case HTT_RX_VHT:
+       case HTT_RX_VHT_WITH_TXBF:
+               /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2
+                  TODO check this */
+               mcs = (info2 >> 4) & 0x0F;
+               nss = (info1 >> 10) & 0x07;
+               bw = info1 & 3;
+               sgi = info2 & 1;
+
+               status->rate_idx = mcs;
+               status->vht_nss = nss;
+
+               if (sgi)
+                       status->flag |= RX_FLAG_SHORT_GI;
+
+               switch (bw) {
+               /* 20MHZ */
+               case 0:
+                       break;
+               /* 40MHZ */
+               case 1:
+                       status->flag |= RX_FLAG_40MHZ;
+                       break;
+               /* 80MHZ */
+               case 2:
+                       status->flag |= RX_FLAG_80MHZ;
+               }
+
+               status->flag |= RX_FLAG_VHT;
+               break;
+       default:
+               break;
+       }
+}
+
+void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
+{
+       struct ieee80211_rx_status *status;
+       struct ieee80211_channel *ch;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)info->skb->data;
+
+       status = IEEE80211_SKB_RXCB(info->skb);
+       memset(status, 0, sizeof(*status));
+
+       if (info->encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) {
+               status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
+                               RX_FLAG_MMIC_STRIPPED;
+               hdr->frame_control = __cpu_to_le16(
+                               __le16_to_cpu(hdr->frame_control) &
+                               ~IEEE80211_FCTL_PROTECTED);
+       }
+
+       if (info->status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR)
+               status->flag |= RX_FLAG_MMIC_ERROR;
+
+       if (info->fcs_err)
+               status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+       status->signal = info->signal;
+
+       spin_lock_bh(&ar->data_lock);
+       ch = ar->scan_channel;
+       if (!ch)
+               ch = ar->rx_channel;
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!ch) {
+               ath10k_warn("no channel configured; ignoring frame!\n");
+               dev_kfree_skb_any(info->skb);
+               return;
+       }
+
+       process_rx_rates(ar, info, ch->band, status);
+       status->band = ch->band;
+       status->freq = ch->center_freq;
+
+       ath10k_dbg(ATH10K_DBG_DATA,
+                  "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u\n",
+                  info->skb,
+                  info->skb->len,
+                  status->flag == 0 ? "legacy" : "",
+                  status->flag & RX_FLAG_HT ? "ht" : "",
+                  status->flag & RX_FLAG_VHT ? "vht" : "",
+                  status->flag & RX_FLAG_40MHZ ? "40" : "",
+                  status->flag & RX_FLAG_80MHZ ? "80" : "",
+                  status->flag & RX_FLAG_SHORT_GI ? "sgi " : "",
+                  status->rate_idx,
+                  status->vht_nss,
+                  status->freq,
+                  status->band);
+
+       ieee80211_rx(ar->hw, info->skb);
+}
+
+struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
+                                    const u8 *addr)
+{
+       struct ath10k_peer *peer;
+
+       lockdep_assert_held(&ar->data_lock);
+
+       list_for_each_entry(peer, &ar->peers, list) {
+               if (peer->vdev_id != vdev_id)
+                       continue;
+               if (memcmp(peer->addr, addr, ETH_ALEN))
+                       continue;
+
+               return peer;
+       }
+
+       return NULL;
+}
+
+static struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar,
+                                                 int peer_id)
+{
+       struct ath10k_peer *peer;
+
+       lockdep_assert_held(&ar->data_lock);
+
+       list_for_each_entry(peer, &ar->peers, list)
+               if (test_bit(peer_id, peer->peer_ids))
+                       return peer;
+
+       return NULL;
+}
+
+static int ath10k_wait_for_peer_common(struct ath10k *ar, int vdev_id,
+                                      const u8 *addr, bool expect_mapped)
+{
+       int ret;
+
+       ret = wait_event_timeout(ar->peer_mapping_wq, ({
+                       bool mapped;
+
+                       spin_lock_bh(&ar->data_lock);
+                       mapped = !!ath10k_peer_find(ar, vdev_id, addr);
+                       spin_unlock_bh(&ar->data_lock);
+
+                       mapped == expect_mapped;
+               }), 3*HZ);
+
+       if (ret <= 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id, const u8 *addr)
+{
+       return ath10k_wait_for_peer_common(ar, vdev_id, addr, true);
+}
+
+int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id, const u8 *addr)
+{
+       return ath10k_wait_for_peer_common(ar, vdev_id, addr, false);
+}
+
+void ath10k_peer_map_event(struct ath10k_htt *htt,
+                          struct htt_peer_map_event *ev)
+{
+       struct ath10k *ar = htt->ar;
+       struct ath10k_peer *peer;
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, ev->vdev_id, ev->addr);
+       if (!peer) {
+               peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
+               if (!peer)
+                       goto exit;
+
+               peer->vdev_id = ev->vdev_id;
+               memcpy(peer->addr, ev->addr, ETH_ALEN);
+               list_add(&peer->list, &ar->peers);
+               wake_up(&ar->peer_mapping_wq);
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n",
+                  ev->vdev_id, ev->addr, ev->peer_id);
+
+       set_bit(ev->peer_id, peer->peer_ids);
+exit:
+       spin_unlock_bh(&ar->data_lock);
+}
+
+void ath10k_peer_unmap_event(struct ath10k_htt *htt,
+                            struct htt_peer_unmap_event *ev)
+{
+       struct ath10k *ar = htt->ar;
+       struct ath10k_peer *peer;
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find_by_id(ar, ev->peer_id);
+       if (!peer) {
+               ath10k_warn("unknown peer id %d\n", ev->peer_id);
+               goto exit;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n",
+                  peer->vdev_id, peer->addr, ev->peer_id);
+
+       clear_bit(ev->peer_id, peer->peer_ids);
+
+       if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS)) {
+               list_del(&peer->list);
+               kfree(peer);
+               wake_up(&ar->peer_mapping_wq);
+       }
+
+exit:
+       spin_unlock_bh(&ar->data_lock);
+}
diff --git a/drivers/net/wireless/ath/ath10k/txrx.h b/drivers/net/wireless/ath/ath10k/txrx.h
new file mode 100644 (file)
index 0000000..e78632a
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _TXRX_H_
+#define _TXRX_H_
+
+#include "htt.h"
+
+void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc);
+void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
+                             const struct htt_tx_done *tx_done);
+void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info);
+
+struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
+                                    const u8 *addr);
+int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id,
+                                const u8 *addr);
+int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id,
+                                const u8 *addr);
+
+void ath10k_peer_map_event(struct ath10k_htt *htt,
+                          struct htt_peer_map_event *ev);
+void ath10k_peer_unmap_event(struct ath10k_htt *htt,
+                            struct htt_peer_unmap_event *ev);
+
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
new file mode 100644 (file)
index 0000000..7d4b798
--- /dev/null
@@ -0,0 +1,2081 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/skbuff.h>
+
+#include "core.h"
+#include "htc.h"
+#include "debug.h"
+#include "wmi.h"
+#include "mac.h"
+
+void ath10k_wmi_flush_tx(struct ath10k *ar)
+{
+       int ret;
+
+       ret = wait_event_timeout(ar->wmi.wq,
+                                atomic_read(&ar->wmi.pending_tx_count) == 0,
+                                5*HZ);
+       if (atomic_read(&ar->wmi.pending_tx_count) == 0)
+               return;
+
+       if (ret == 0)
+               ret = -ETIMEDOUT;
+
+       if (ret < 0)
+               ath10k_warn("wmi flush failed (%d)\n", ret);
+}
+
+int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
+{
+       int ret;
+       ret = wait_for_completion_timeout(&ar->wmi.service_ready,
+                                         WMI_SERVICE_READY_TIMEOUT_HZ);
+       return ret;
+}
+
+int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar)
+{
+       int ret;
+       ret = wait_for_completion_timeout(&ar->wmi.unified_ready,
+                                         WMI_UNIFIED_READY_TIMEOUT_HZ);
+       return ret;
+}
+
+static struct sk_buff *ath10k_wmi_alloc_skb(u32 len)
+{
+       struct sk_buff *skb;
+       u32 round_len = roundup(len, 4);
+
+       skb = ath10k_htc_alloc_skb(WMI_SKB_HEADROOM + round_len);
+       if (!skb)
+               return NULL;
+
+       skb_reserve(skb, WMI_SKB_HEADROOM);
+       if (!IS_ALIGNED((unsigned long)skb->data, 4))
+               ath10k_warn("Unaligned WMI skb\n");
+
+       skb_put(skb, round_len);
+       memset(skb->data, 0, round_len);
+
+       return skb;
+}
+
+static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
+{
+       dev_kfree_skb(skb);
+
+       if (atomic_sub_return(1, &ar->wmi.pending_tx_count) == 0)
+               wake_up(&ar->wmi.wq);
+}
+
+/* WMI command API */
+static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
+                              enum wmi_cmd_id cmd_id)
+{
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+       struct wmi_cmd_hdr *cmd_hdr;
+       int status;
+       u32 cmd = 0;
+
+       if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
+               return -ENOMEM;
+
+       cmd |= SM(cmd_id, WMI_CMD_HDR_CMD_ID);
+
+       cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+       cmd_hdr->cmd_id = __cpu_to_le32(cmd);
+
+       if (atomic_add_return(1, &ar->wmi.pending_tx_count) >
+           WMI_MAX_PENDING_TX_COUNT) {
+               /* avoid using up memory when FW hangs */
+               atomic_dec(&ar->wmi.pending_tx_count);
+               return -EBUSY;
+       }
+
+       memset(skb_cb, 0, sizeof(*skb_cb));
+
+       trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len);
+
+       status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb);
+       if (status) {
+               dev_kfree_skb_any(skb);
+               atomic_dec(&ar->wmi.pending_tx_count);
+               return status;
+       }
+
+       return 0;
+}
+
+static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data;
+       enum wmi_scan_event_type event_type;
+       enum wmi_scan_completion_reason reason;
+       u32 freq;
+       u32 req_id;
+       u32 scan_id;
+       u32 vdev_id;
+
+       event_type = __le32_to_cpu(event->event_type);
+       reason     = __le32_to_cpu(event->reason);
+       freq       = __le32_to_cpu(event->channel_freq);
+       req_id     = __le32_to_cpu(event->scan_req_id);
+       scan_id    = __le32_to_cpu(event->scan_id);
+       vdev_id    = __le32_to_cpu(event->vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENTID\n");
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "scan event type %d reason %d freq %d req_id %d "
+                  "scan_id %d vdev_id %d\n",
+                  event_type, reason, freq, req_id, scan_id, vdev_id);
+
+       spin_lock_bh(&ar->data_lock);
+
+       switch (event_type) {
+       case WMI_SCAN_EVENT_STARTED:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_STARTED\n");
+               if (ar->scan.in_progress && ar->scan.is_roc)
+                       ieee80211_ready_on_channel(ar->hw);
+
+               complete(&ar->scan.started);
+               break;
+       case WMI_SCAN_EVENT_COMPLETED:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_COMPLETED\n");
+               switch (reason) {
+               case WMI_SCAN_REASON_COMPLETED:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_COMPLETED\n");
+                       break;
+               case WMI_SCAN_REASON_CANCELLED:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_CANCELED\n");
+                       break;
+               case WMI_SCAN_REASON_PREEMPTED:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_PREEMPTED\n");
+                       break;
+               case WMI_SCAN_REASON_TIMEDOUT:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_TIMEDOUT\n");
+                       break;
+               default:
+                       break;
+               }
+
+               ar->scan_channel = NULL;
+               if (!ar->scan.in_progress) {
+                       ath10k_warn("no scan requested, ignoring\n");
+                       break;
+               }
+
+               if (ar->scan.is_roc) {
+                       ath10k_offchan_tx_purge(ar);
+
+                       if (!ar->scan.aborting)
+                               ieee80211_remain_on_channel_expired(ar->hw);
+               } else {
+                       ieee80211_scan_completed(ar->hw, ar->scan.aborting);
+               }
+
+               del_timer(&ar->scan.timeout);
+               complete_all(&ar->scan.completed);
+               ar->scan.in_progress = false;
+               break;
+       case WMI_SCAN_EVENT_BSS_CHANNEL:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_BSS_CHANNEL\n");
+               ar->scan_channel = NULL;
+               break;
+       case WMI_SCAN_EVENT_FOREIGN_CHANNEL:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_FOREIGN_CHANNEL\n");
+               ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq);
+               if (ar->scan.in_progress && ar->scan.is_roc &&
+                   ar->scan.roc_freq == freq) {
+                       complete(&ar->scan.on_channel);
+               }
+               break;
+       case WMI_SCAN_EVENT_DEQUEUED:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_DEQUEUED\n");
+               break;
+       case WMI_SCAN_EVENT_PREEMPTED:
+               ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_PREEMPTED\n");
+               break;
+       case WMI_SCAN_EVENT_START_FAILED:
+               ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_START_FAILED\n");
+               break;
+       default:
+               break;
+       }
+
+       spin_unlock_bh(&ar->data_lock);
+       return 0;
+}
+
+static inline enum ieee80211_band phy_mode_to_band(u32 phy_mode)
+{
+       enum ieee80211_band band;
+
+       switch (phy_mode) {
+       case MODE_11A:
+       case MODE_11NA_HT20:
+       case MODE_11NA_HT40:
+       case MODE_11AC_VHT20:
+       case MODE_11AC_VHT40:
+       case MODE_11AC_VHT80:
+               band = IEEE80211_BAND_5GHZ;
+               break;
+       case MODE_11G:
+       case MODE_11B:
+       case MODE_11GONLY:
+       case MODE_11NG_HT20:
+       case MODE_11NG_HT40:
+       case MODE_11AC_VHT20_2G:
+       case MODE_11AC_VHT40_2G:
+       case MODE_11AC_VHT80_2G:
+       default:
+               band = IEEE80211_BAND_2GHZ;
+       }
+
+       return band;
+}
+
+static inline u8 get_rate_idx(u32 rate, enum ieee80211_band band)
+{
+       u8 rate_idx = 0;
+
+       /* rate in Kbps */
+       switch (rate) {
+       case 1000:
+               rate_idx = 0;
+               break;
+       case 2000:
+               rate_idx = 1;
+               break;
+       case 5500:
+               rate_idx = 2;
+               break;
+       case 11000:
+               rate_idx = 3;
+               break;
+       case 6000:
+               rate_idx = 4;
+               break;
+       case 9000:
+               rate_idx = 5;
+               break;
+       case 12000:
+               rate_idx = 6;
+               break;
+       case 18000:
+               rate_idx = 7;
+               break;
+       case 24000:
+               rate_idx = 8;
+               break;
+       case 36000:
+               rate_idx = 9;
+               break;
+       case 48000:
+               rate_idx = 10;
+               break;
+       case 54000:
+               rate_idx = 11;
+               break;
+       default:
+               break;
+       }
+
+       if (band == IEEE80211_BAND_5GHZ) {
+               if (rate_idx > 3)
+                       /* Omit CCK rates */
+                       rate_idx -= 4;
+               else
+                       rate_idx = 0;
+       }
+
+       return rate_idx;
+}
+
+static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_mgmt_rx_event *event = (struct wmi_mgmt_rx_event *)skb->data;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_hdr *hdr;
+       u32 rx_status;
+       u32 channel;
+       u32 phy_mode;
+       u32 snr;
+       u32 rate;
+       u32 buf_len;
+       u16 fc;
+
+       channel   = __le32_to_cpu(event->hdr.channel);
+       buf_len   = __le32_to_cpu(event->hdr.buf_len);
+       rx_status = __le32_to_cpu(event->hdr.status);
+       snr       = __le32_to_cpu(event->hdr.snr);
+       phy_mode  = __le32_to_cpu(event->hdr.phy_mode);
+       rate      = __le32_to_cpu(event->hdr.rate);
+
+       memset(status, 0, sizeof(*status));
+
+       ath10k_dbg(ATH10K_DBG_MGMT,
+                  "event mgmt rx status %08x\n", rx_status);
+
+       if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) {
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       if (rx_status & WMI_RX_STATUS_ERR_KEY_CACHE_MISS) {
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       if (rx_status & WMI_RX_STATUS_ERR_CRC)
+               status->flag |= RX_FLAG_FAILED_FCS_CRC;
+       if (rx_status & WMI_RX_STATUS_ERR_MIC)
+               status->flag |= RX_FLAG_MMIC_ERROR;
+
+       status->band = phy_mode_to_band(phy_mode);
+       status->freq = ieee80211_channel_to_frequency(channel, status->band);
+       status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
+       status->rate_idx = get_rate_idx(rate, status->band);
+
+       skb_pull(skb, sizeof(event->hdr));
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       fc = le16_to_cpu(hdr->frame_control);
+
+       if (fc & IEEE80211_FCTL_PROTECTED) {
+               status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
+                               RX_FLAG_MMIC_STRIPPED;
+               hdr->frame_control = __cpu_to_le16(fc &
+                                       ~IEEE80211_FCTL_PROTECTED);
+       }
+
+       ath10k_dbg(ATH10K_DBG_MGMT,
+                  "event mgmt rx skb %p len %d ftype %02x stype %02x\n",
+                  skb, skb->len,
+                  fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE);
+
+       ath10k_dbg(ATH10K_DBG_MGMT,
+                  "event mgmt rx freq %d band %d snr %d, rate_idx %d\n",
+                  status->freq, status->band, status->signal,
+                  status->rate_idx);
+
+       /*
+        * packets from HTC come aligned to 4byte boundaries
+        * because they can originally come in along with a trailer
+        */
+       skb_trim(skb, buf_len);
+
+       ieee80211_rx(ar->hw, skb);
+       return 0;
+}
+
+static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_CHAN_INFO_EVENTID\n");
+}
+
+static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n");
+}
+
+static void ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_MESG_EVENTID\n");
+}
+
+static void ath10k_wmi_event_update_stats(struct ath10k *ar,
+                                         struct sk_buff *skb)
+{
+       struct wmi_stats_event *ev = (struct wmi_stats_event *)skb->data;
+
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
+
+       ath10k_debug_read_target_stats(ar, ev);
+}
+
+static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       struct wmi_vdev_start_response_event *ev;
+
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n");
+
+       ev = (struct wmi_vdev_start_response_event *)skb->data;
+
+       if (WARN_ON(__le32_to_cpu(ev->status)))
+               return;
+
+       complete(&ar->vdev_setup_done);
+}
+
+static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar,
+                                         struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n");
+       complete(&ar->vdev_setup_done);
+}
+
+static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PEER_STA_KICKOUT_EVENTID\n");
+}
+
+/*
+ * FIXME
+ *
+ * We don't report to mac80211 sleep state of connected
+ * stations. Due to this mac80211 can't fill in TIM IE
+ * correctly.
+ *
+ * I know of no way of getting nullfunc frames that contain
+ * sleep transition from connected stations - these do not
+ * seem to be sent from the target to the host. There also
+ * doesn't seem to be a dedicated event for that. So the
+ * only way left to do this would be to read tim_bitmap
+ * during SWBA.
+ *
+ * We could probably try using tim_bitmap from SWBA to tell
+ * mac80211 which stations are asleep and which are not. The
+ * problem here is calling mac80211 functions so many times
+ * could take too long and make us miss the time to submit
+ * the beacon to the target.
+ *
+ * So as a workaround we try to extend the TIM IE if there
+ * is unicast buffered for stations with aid > 7 and fill it
+ * in ourselves.
+ */
+static void ath10k_wmi_update_tim(struct ath10k *ar,
+                                 struct ath10k_vif *arvif,
+                                 struct sk_buff *bcn,
+                                 struct wmi_bcn_info *bcn_info)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)bcn->data;
+       struct ieee80211_tim_ie *tim;
+       u8 *ies, *ie;
+       u8 ie_len, pvm_len;
+
+       /* if next SWBA has no tim_changed the tim_bitmap is garbage.
+        * we must copy the bitmap upon change and reuse it later */
+       if (__le32_to_cpu(bcn_info->tim_info.tim_changed)) {
+               int i;
+
+               BUILD_BUG_ON(sizeof(arvif->u.ap.tim_bitmap) !=
+                            sizeof(bcn_info->tim_info.tim_bitmap));
+
+               for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) {
+                       __le32 t = bcn_info->tim_info.tim_bitmap[i / 4];
+                       u32 v = __le32_to_cpu(t);
+                       arvif->u.ap.tim_bitmap[i] = (v >> ((i % 4) * 8)) & 0xFF;
+               }
+
+               /* FW reports either length 0 or 16
+                * so we calculate this on our own */
+               arvif->u.ap.tim_len = 0;
+               for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++)
+                       if (arvif->u.ap.tim_bitmap[i])
+                               arvif->u.ap.tim_len = i;
+
+               arvif->u.ap.tim_len++;
+       }
+
+       ies = bcn->data;
+       ies += ieee80211_hdrlen(hdr->frame_control);
+       ies += 12; /* fixed parameters */
+
+       ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies,
+                                   (u8 *)skb_tail_pointer(bcn) - ies);
+       if (!ie) {
+               /* highly unlikely for mac80211 */
+               ath10k_warn("no tim ie found;\n");
+               return;
+       }
+
+       tim = (void *)ie + 2;
+       ie_len = ie[1];
+       pvm_len = ie_len - 3; /* exclude dtim count, dtim period, bmap ctl */
+
+       if (pvm_len < arvif->u.ap.tim_len) {
+               int expand_size = sizeof(arvif->u.ap.tim_bitmap) - pvm_len;
+               int move_size = skb_tail_pointer(bcn) - (ie + 2 + ie_len);
+               void *next_ie = ie + 2 + ie_len;
+
+               if (skb_put(bcn, expand_size)) {
+                       memmove(next_ie + expand_size, next_ie, move_size);
+
+                       ie[1] += expand_size;
+                       ie_len += expand_size;
+                       pvm_len += expand_size;
+               } else {
+                       ath10k_warn("tim expansion failed\n");
+               }
+       }
+
+       if (pvm_len > sizeof(arvif->u.ap.tim_bitmap)) {
+               ath10k_warn("tim pvm length is too great (%d)\n", pvm_len);
+               return;
+       }
+
+       tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast);
+       memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n",
+                  tim->dtim_count, tim->dtim_period,
+                  tim->bitmap_ctrl, pvm_len);
+}
+
+static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len,
+                                  struct wmi_p2p_noa_info *noa)
+{
+       struct ieee80211_p2p_noa_attr *noa_attr;
+       u8  ctwindow_oppps = noa->ctwindow_oppps;
+       u8 ctwindow = ctwindow_oppps >> WMI_P2P_OPPPS_CTWINDOW_OFFSET;
+       bool oppps = !!(ctwindow_oppps & WMI_P2P_OPPPS_ENABLE_BIT);
+       __le16 *noa_attr_len;
+       u16 attr_len;
+       u8 noa_descriptors = noa->num_descriptors;
+       int i;
+
+       /* P2P IE */
+       data[0] = WLAN_EID_VENDOR_SPECIFIC;
+       data[1] = len - 2;
+       data[2] = (WLAN_OUI_WFA >> 16) & 0xff;
+       data[3] = (WLAN_OUI_WFA >> 8) & 0xff;
+       data[4] = (WLAN_OUI_WFA >> 0) & 0xff;
+       data[5] = WLAN_OUI_TYPE_WFA_P2P;
+
+       /* NOA ATTR */
+       data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE;
+       noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */
+       noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9];
+
+       noa_attr->index = noa->index;
+       noa_attr->oppps_ctwindow = ctwindow;
+       if (oppps)
+               noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT;
+
+       for (i = 0; i < noa_descriptors; i++) {
+               noa_attr->desc[i].count =
+                       __le32_to_cpu(noa->descriptors[i].type_count);
+               noa_attr->desc[i].duration = noa->descriptors[i].duration;
+               noa_attr->desc[i].interval = noa->descriptors[i].interval;
+               noa_attr->desc[i].start_time = noa->descriptors[i].start_time;
+       }
+
+       attr_len = 2; /* index + oppps_ctwindow */
+       attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc);
+       *noa_attr_len = __cpu_to_le16(attr_len);
+}
+
+static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa)
+{
+       u32 len = 0;
+       u8 noa_descriptors = noa->num_descriptors;
+       u8 opp_ps_info = noa->ctwindow_oppps;
+       bool opps_enabled = !!(opp_ps_info & WMI_P2P_OPPPS_ENABLE_BIT);
+
+
+       if (!noa_descriptors && !opps_enabled)
+               return len;
+
+       len += 1 + 1 + 4; /* EID + len + OUI */
+       len += 1 + 2; /* noa attr  + attr len */
+       len += 1 + 1; /* index + oppps_ctwindow */
+       len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc);
+
+       return len;
+}
+
+static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif,
+                                 struct sk_buff *bcn,
+                                 struct wmi_bcn_info *bcn_info)
+{
+       struct wmi_p2p_noa_info *noa = &bcn_info->p2p_noa_info;
+       u8 *new_data, *old_data = arvif->u.ap.noa_data;
+       u32 new_len;
+
+       if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+               return;
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "noa changed: %d\n", noa->changed);
+       if (noa->changed & WMI_P2P_NOA_CHANGED_BIT) {
+               new_len = ath10k_p2p_calc_noa_ie_len(noa);
+               if (!new_len)
+                       goto cleanup;
+
+               new_data = kmalloc(new_len, GFP_ATOMIC);
+               if (!new_data)
+                       goto cleanup;
+
+               ath10k_p2p_fill_noa_ie(new_data, new_len, noa);
+
+               spin_lock_bh(&ar->data_lock);
+               arvif->u.ap.noa_data = new_data;
+               arvif->u.ap.noa_len = new_len;
+               spin_unlock_bh(&ar->data_lock);
+               kfree(old_data);
+       }
+
+       if (arvif->u.ap.noa_data)
+               if (!pskb_expand_head(bcn, 0, arvif->u.ap.noa_len, GFP_ATOMIC))
+                       memcpy(skb_put(bcn, arvif->u.ap.noa_len),
+                              arvif->u.ap.noa_data,
+                              arvif->u.ap.noa_len);
+       return;
+
+cleanup:
+       spin_lock_bh(&ar->data_lock);
+       arvif->u.ap.noa_data = NULL;
+       arvif->u.ap.noa_len = 0;
+       spin_unlock_bh(&ar->data_lock);
+       kfree(old_data);
+}
+
+
+static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_host_swba_event *ev;
+       u32 map;
+       int i = -1;
+       struct wmi_bcn_info *bcn_info;
+       struct ath10k_vif *arvif;
+       struct wmi_bcn_tx_arg arg;
+       struct sk_buff *bcn;
+       int vdev_id = 0;
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n");
+
+       ev = (struct wmi_host_swba_event *)skb->data;
+       map = __le32_to_cpu(ev->vdev_map);
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "host swba:\n"
+                  "-vdev map 0x%x\n",
+                  ev->vdev_map);
+
+       for (; map; map >>= 1, vdev_id++) {
+               if (!(map & 0x1))
+                       continue;
+
+               i++;
+
+               if (i >= WMI_MAX_AP_VDEV) {
+                       ath10k_warn("swba has corrupted vdev map\n");
+                       break;
+               }
+
+               bcn_info = &ev->bcn_info[i];
+
+               ath10k_dbg(ATH10K_DBG_MGMT,
+                          "-bcn_info[%d]:\n"
+                          "--tim_len %d\n"
+                          "--tim_mcast %d\n"
+                          "--tim_changed %d\n"
+                          "--tim_num_ps_pending %d\n"
+                          "--tim_bitmap 0x%08x%08x%08x%08x\n",
+                          i,
+                          __le32_to_cpu(bcn_info->tim_info.tim_len),
+                          __le32_to_cpu(bcn_info->tim_info.tim_mcast),
+                          __le32_to_cpu(bcn_info->tim_info.tim_changed),
+                          __le32_to_cpu(bcn_info->tim_info.tim_num_ps_pending),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[3]),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[2]),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[1]),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[0]));
+
+               arvif = ath10k_get_arvif(ar, vdev_id);
+               if (arvif == NULL) {
+                       ath10k_warn("no vif for vdev_id %d found\n", vdev_id);
+                       continue;
+               }
+
+               bcn = ieee80211_beacon_get(ar->hw, arvif->vif);
+               if (!bcn) {
+                       ath10k_warn("could not get mac80211 beacon\n");
+                       continue;
+               }
+
+               ath10k_tx_h_seq_no(bcn);
+               ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info);
+               ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info);
+
+               arg.vdev_id = arvif->vdev_id;
+               arg.tx_rate = 0;
+               arg.tx_power = 0;
+               arg.bcn = bcn->data;
+               arg.bcn_len = bcn->len;
+
+               ret = ath10k_wmi_beacon_send(ar, &arg);
+               if (ret)
+                       ath10k_warn("could not send beacon (%d)\n", ret);
+
+               dev_kfree_skb_any(bcn);
+       }
+}
+
+static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar,
+                                              struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PHYERR_EVENTID\n");
+}
+
+static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n");
+}
+
+static void ath10k_wmi_event_profile_match(struct ath10k *ar,
+                                   struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n");
+}
+
+static void ath10k_wmi_event_debug_print(struct ath10k *ar,
+                                 struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_PRINT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar,
+                                              struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n");
+}
+
+static void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_rtt_error_report(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n");
+}
+
+static void ath10k_wmi_event_dcs_interference(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n");
+}
+
+static void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar,
+                                          struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n");
+}
+
+static void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar,
+                                        struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n");
+}
+
+static void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n");
+}
+
+static void ath10k_wmi_event_delba_complete(struct ath10k *ar,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_addba_complete(struct ath10k *ar,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
+                                               struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n");
+}
+
+static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       struct wmi_service_ready_event *ev = (void *)skb->data;
+
+       if (skb->len < sizeof(*ev)) {
+               ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
+                           skb->len, sizeof(*ev));
+               return;
+       }
+
+       ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
+       ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
+       ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
+       ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
+       ar->fw_version_major =
+               (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
+       ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
+       ar->fw_version_release =
+               (__le32_to_cpu(ev->sw_version_1) & 0xffff0000) >> 16;
+       ar->fw_version_build = (__le32_to_cpu(ev->sw_version_1) & 0x0000ffff);
+       ar->phy_capability = __le32_to_cpu(ev->phy_capability);
+
+       ar->ath_common.regulatory.current_rd =
+               __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
+
+       ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap,
+                                     sizeof(ev->wmi_service_bitmap));
+
+       if (strlen(ar->hw->wiphy->fw_version) == 0) {
+               snprintf(ar->hw->wiphy->fw_version,
+                        sizeof(ar->hw->wiphy->fw_version),
+                        "%u.%u.%u.%u",
+                        ar->fw_version_major,
+                        ar->fw_version_minor,
+                        ar->fw_version_release,
+                        ar->fw_version_build);
+       }
+
+       /* FIXME: it probably should be better to support this */
+       if (__le32_to_cpu(ev->num_mem_reqs) > 0) {
+               ath10k_warn("target requested %d memory chunks; ignoring\n",
+                           __le32_to_cpu(ev->num_mem_reqs));
+       }
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u\n",
+                  __le32_to_cpu(ev->sw_version),
+                  __le32_to_cpu(ev->sw_version_1),
+                  __le32_to_cpu(ev->abi_version),
+                  __le32_to_cpu(ev->phy_capability),
+                  __le32_to_cpu(ev->ht_cap_info),
+                  __le32_to_cpu(ev->vht_cap_info),
+                  __le32_to_cpu(ev->vht_supp_mcs),
+                  __le32_to_cpu(ev->sys_cap_info),
+                  __le32_to_cpu(ev->num_mem_reqs));
+
+       complete(&ar->wmi.service_ready);
+}
+
+static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data;
+
+       if (WARN_ON(skb->len < sizeof(*ev)))
+               return -EINVAL;
+
+       memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n",
+                  __le32_to_cpu(ev->sw_version),
+                  __le32_to_cpu(ev->abi_version),
+                  ev->mac_addr.addr,
+                  __le32_to_cpu(ev->status));
+
+       complete(&ar->wmi.unified_ready);
+       return 0;
+}
+
+static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_cmd_hdr *cmd_hdr;
+       enum wmi_event_id id;
+       u16 len;
+
+       cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+       id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+
+       if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
+               return;
+
+       len = skb->len;
+
+       trace_ath10k_wmi_event(id, skb->data, skb->len);
+
+       switch (id) {
+       case WMI_MGMT_RX_EVENTID:
+               ath10k_wmi_event_mgmt_rx(ar, skb);
+               /* mgmt_rx() owns the skb now! */
+               return;
+       case WMI_SCAN_EVENTID:
+               ath10k_wmi_event_scan(ar, skb);
+               break;
+       case WMI_CHAN_INFO_EVENTID:
+               ath10k_wmi_event_chan_info(ar, skb);
+               break;
+       case WMI_ECHO_EVENTID:
+               ath10k_wmi_event_echo(ar, skb);
+               break;
+       case WMI_DEBUG_MESG_EVENTID:
+               ath10k_wmi_event_debug_mesg(ar, skb);
+               break;
+       case WMI_UPDATE_STATS_EVENTID:
+               ath10k_wmi_event_update_stats(ar, skb);
+               break;
+       case WMI_VDEV_START_RESP_EVENTID:
+               ath10k_wmi_event_vdev_start_resp(ar, skb);
+               break;
+       case WMI_VDEV_STOPPED_EVENTID:
+               ath10k_wmi_event_vdev_stopped(ar, skb);
+               break;
+       case WMI_PEER_STA_KICKOUT_EVENTID:
+               ath10k_wmi_event_peer_sta_kickout(ar, skb);
+               break;
+       case WMI_HOST_SWBA_EVENTID:
+               ath10k_wmi_event_host_swba(ar, skb);
+               break;
+       case WMI_TBTTOFFSET_UPDATE_EVENTID:
+               ath10k_wmi_event_tbttoffset_update(ar, skb);
+               break;
+       case WMI_PHYERR_EVENTID:
+               ath10k_wmi_event_phyerr(ar, skb);
+               break;
+       case WMI_ROAM_EVENTID:
+               ath10k_wmi_event_roam(ar, skb);
+               break;
+       case WMI_PROFILE_MATCH:
+               ath10k_wmi_event_profile_match(ar, skb);
+               break;
+       case WMI_DEBUG_PRINT_EVENTID:
+               ath10k_wmi_event_debug_print(ar, skb);
+               break;
+       case WMI_PDEV_QVIT_EVENTID:
+               ath10k_wmi_event_pdev_qvit(ar, skb);
+               break;
+       case WMI_WLAN_PROFILE_DATA_EVENTID:
+               ath10k_wmi_event_wlan_profile_data(ar, skb);
+               break;
+       case WMI_RTT_MEASUREMENT_REPORT_EVENTID:
+               ath10k_wmi_event_rtt_measurement_report(ar, skb);
+               break;
+       case WMI_TSF_MEASUREMENT_REPORT_EVENTID:
+               ath10k_wmi_event_tsf_measurement_report(ar, skb);
+               break;
+       case WMI_RTT_ERROR_REPORT_EVENTID:
+               ath10k_wmi_event_rtt_error_report(ar, skb);
+               break;
+       case WMI_WOW_WAKEUP_HOST_EVENTID:
+               ath10k_wmi_event_wow_wakeup_host(ar, skb);
+               break;
+       case WMI_DCS_INTERFERENCE_EVENTID:
+               ath10k_wmi_event_dcs_interference(ar, skb);
+               break;
+       case WMI_PDEV_TPC_CONFIG_EVENTID:
+               ath10k_wmi_event_pdev_tpc_config(ar, skb);
+               break;
+       case WMI_PDEV_FTM_INTG_EVENTID:
+               ath10k_wmi_event_pdev_ftm_intg(ar, skb);
+               break;
+       case WMI_GTK_OFFLOAD_STATUS_EVENTID:
+               ath10k_wmi_event_gtk_offload_status(ar, skb);
+               break;
+       case WMI_GTK_REKEY_FAIL_EVENTID:
+               ath10k_wmi_event_gtk_rekey_fail(ar, skb);
+               break;
+       case WMI_TX_DELBA_COMPLETE_EVENTID:
+               ath10k_wmi_event_delba_complete(ar, skb);
+               break;
+       case WMI_TX_ADDBA_COMPLETE_EVENTID:
+               ath10k_wmi_event_addba_complete(ar, skb);
+               break;
+       case WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID:
+               ath10k_wmi_event_vdev_install_key_complete(ar, skb);
+               break;
+       case WMI_SERVICE_READY_EVENTID:
+               ath10k_wmi_service_ready_event_rx(ar, skb);
+               break;
+       case WMI_READY_EVENTID:
+               ath10k_wmi_ready_event_rx(ar, skb);
+               break;
+       default:
+               ath10k_warn("Unknown eventid: %d\n", id);
+               break;
+       }
+
+       dev_kfree_skb(skb);
+}
+
+static void ath10k_wmi_event_work(struct work_struct *work)
+{
+       struct ath10k *ar = container_of(work, struct ath10k,
+                                        wmi.wmi_event_work);
+       struct sk_buff *skb;
+
+       for (;;) {
+               skb = skb_dequeue(&ar->wmi.wmi_event_list);
+               if (!skb)
+                       break;
+
+               ath10k_wmi_event_process(ar, skb);
+       }
+}
+
+static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_cmd_hdr *cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+       enum wmi_event_id event_id;
+
+       event_id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+
+       /* some events require to be handled ASAP
+        * thus can't be defered to a worker thread */
+       switch (event_id) {
+       case WMI_HOST_SWBA_EVENTID:
+       case WMI_MGMT_RX_EVENTID:
+               ath10k_wmi_event_process(ar, skb);
+               return;
+       default:
+               break;
+       }
+
+       skb_queue_tail(&ar->wmi.wmi_event_list, skb);
+       queue_work(ar->workqueue, &ar->wmi.wmi_event_work);
+}
+
+/* WMI Initialization functions */
+int ath10k_wmi_attach(struct ath10k *ar)
+{
+       init_completion(&ar->wmi.service_ready);
+       init_completion(&ar->wmi.unified_ready);
+       init_waitqueue_head(&ar->wmi.wq);
+
+       skb_queue_head_init(&ar->wmi.wmi_event_list);
+       INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work);
+
+       return 0;
+}
+
+void ath10k_wmi_detach(struct ath10k *ar)
+{
+       /* HTC should've drained the packets already */
+       if (WARN_ON(atomic_read(&ar->wmi.pending_tx_count) > 0))
+               ath10k_warn("there are still pending packets\n");
+
+       cancel_work_sync(&ar->wmi.wmi_event_work);
+       skb_queue_purge(&ar->wmi.wmi_event_list);
+}
+
+int ath10k_wmi_connect_htc_service(struct ath10k *ar)
+{
+       int status;
+       struct ath10k_htc_svc_conn_req conn_req;
+       struct ath10k_htc_svc_conn_resp conn_resp;
+
+       memset(&conn_req, 0, sizeof(conn_req));
+       memset(&conn_resp, 0, sizeof(conn_resp));
+
+       /* these fields are the same for all service endpoints */
+       conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete;
+       conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx;
+
+       /* connect to control service */
+       conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
+
+       status = ath10k_htc_connect_service(ar->htc, &conn_req, &conn_resp);
+       if (status) {
+               ath10k_warn("failed to connect to WMI CONTROL service status: %d\n",
+                           status);
+               return status;
+       }
+
+       ar->wmi.eid = conn_resp.eid;
+       return 0;
+}
+
+int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
+                                 u16 rd5g, u16 ctl2g, u16 ctl5g)
+{
+       struct wmi_pdev_set_regdomain_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_set_regdomain_cmd *)skb->data;
+       cmd->reg_domain = __cpu_to_le32(rd);
+       cmd->reg_domain_2G = __cpu_to_le32(rd2g);
+       cmd->reg_domain_5G = __cpu_to_le32(rd5g);
+       cmd->conformance_test_limit_2G = __cpu_to_le32(ctl2g);
+       cmd->conformance_test_limit_5G = __cpu_to_le32(ctl5g);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n",
+                  rd, rd2g, rd5g, ctl2g, ctl5g);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_REGDOMAIN_CMDID);
+}
+
+int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
+                               const struct wmi_channel_arg *arg)
+{
+       struct wmi_set_channel_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (arg->passive)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_set_channel_cmd *)skb->data;
+       cmd->chan.mhz               = __cpu_to_le32(arg->freq);
+       cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq);
+       cmd->chan.mode              = arg->mode;
+       cmd->chan.min_power         = arg->min_power;
+       cmd->chan.max_power         = arg->max_power;
+       cmd->chan.reg_power         = arg->max_reg_power;
+       cmd->chan.reg_classid       = arg->reg_class_id;
+       cmd->chan.antenna_max       = arg->max_antenna_gain;
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi set channel mode %d freq %d\n",
+                  arg->mode, arg->freq);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_CHANNEL_CMDID);
+}
+
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
+{
+       struct wmi_pdev_suspend_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_suspend_cmd *)skb->data;
+       cmd->suspend_opt = WMI_PDEV_SUSPEND;
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SUSPEND_CMDID);
+}
+
+int ath10k_wmi_pdev_resume_target(struct ath10k *ar)
+{
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(0);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_RESUME_CMDID);
+}
+
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
+                             u32 value)
+{
+       struct wmi_pdev_set_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_set_param_cmd *)skb->data;
+       cmd->param_id    = __cpu_to_le32(id);
+       cmd->param_value = __cpu_to_le32(value);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n",
+                  id, value);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_PARAM_CMDID);
+}
+
+int ath10k_wmi_cmd_init(struct ath10k *ar)
+{
+       struct wmi_init_cmd *cmd;
+       struct sk_buff *buf;
+       struct wmi_resource_config config = {};
+       u32 val;
+
+       config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
+       config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS);
+       config.num_offload_peers = __cpu_to_le32(TARGET_NUM_OFFLOAD_PEERS);
+
+       config.num_offload_reorder_bufs =
+               __cpu_to_le32(TARGET_NUM_OFFLOAD_REORDER_BUFS);
+
+       config.num_peer_keys = __cpu_to_le32(TARGET_NUM_PEER_KEYS);
+       config.num_tids = __cpu_to_le32(TARGET_NUM_TIDS);
+       config.ast_skid_limit = __cpu_to_le32(TARGET_AST_SKID_LIMIT);
+       config.tx_chain_mask = __cpu_to_le32(TARGET_TX_CHAIN_MASK);
+       config.rx_chain_mask = __cpu_to_le32(TARGET_RX_CHAIN_MASK);
+       config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_be = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_RX_TIMEOUT_HI_PRI);
+       config.rx_decap_mode = __cpu_to_le32(TARGET_RX_DECAP_MODE);
+
+       config.scan_max_pending_reqs =
+               __cpu_to_le32(TARGET_SCAN_MAX_PENDING_REQS);
+
+       config.bmiss_offload_max_vdev =
+               __cpu_to_le32(TARGET_BMISS_OFFLOAD_MAX_VDEV);
+
+       config.roam_offload_max_vdev =
+               __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_VDEV);
+
+       config.roam_offload_max_ap_profiles =
+               __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES);
+
+       config.num_mcast_groups = __cpu_to_le32(TARGET_NUM_MCAST_GROUPS);
+       config.num_mcast_table_elems =
+               __cpu_to_le32(TARGET_NUM_MCAST_TABLE_ELEMS);
+
+       config.mcast2ucast_mode = __cpu_to_le32(TARGET_MCAST2UCAST_MODE);
+       config.tx_dbg_log_size = __cpu_to_le32(TARGET_TX_DBG_LOG_SIZE);
+       config.num_wds_entries = __cpu_to_le32(TARGET_NUM_WDS_ENTRIES);
+       config.dma_burst_size = __cpu_to_le32(TARGET_DMA_BURST_SIZE);
+       config.mac_aggr_delim = __cpu_to_le32(TARGET_MAC_AGGR_DELIM);
+
+       val = TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK;
+       config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val);
+
+       config.vow_config = __cpu_to_le32(TARGET_VOW_CONFIG);
+
+       config.gtk_offload_max_vdev =
+               __cpu_to_le32(TARGET_GTK_OFFLOAD_MAX_VDEV);
+
+       config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC);
+       config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES);
+
+       buf = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!buf)
+               return -ENOMEM;
+
+       cmd = (struct wmi_init_cmd *)buf->data;
+       cmd->num_host_mem_chunks = 0;
+       memcpy(&cmd->resource_config, &config, sizeof(config));
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n");
+       return ath10k_wmi_cmd_send(ar, buf, WMI_INIT_CMDID);
+}
+
+static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg)
+{
+       int len;
+
+       len = sizeof(struct wmi_start_scan_cmd);
+
+       if (arg->ie_len) {
+               if (!arg->ie)
+                       return -EINVAL;
+               if (arg->ie_len > WLAN_SCAN_PARAMS_MAX_IE_LEN)
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_ie_data);
+               len += roundup(arg->ie_len, 4);
+       }
+
+       if (arg->n_channels) {
+               if (!arg->channels)
+                       return -EINVAL;
+               if (arg->n_channels > ARRAY_SIZE(arg->channels))
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_chan_list);
+               len += sizeof(__le32) * arg->n_channels;
+       }
+
+       if (arg->n_ssids) {
+               if (!arg->ssids)
+                       return -EINVAL;
+               if (arg->n_ssids > WLAN_SCAN_PARAMS_MAX_SSID)
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_ssid_list);
+               len += sizeof(struct wmi_ssid) * arg->n_ssids;
+       }
+
+       if (arg->n_bssids) {
+               if (!arg->bssids)
+                       return -EINVAL;
+               if (arg->n_bssids > WLAN_SCAN_PARAMS_MAX_BSSID)
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_bssid_list);
+               len += sizeof(struct wmi_mac_addr) * arg->n_bssids;
+       }
+
+       return len;
+}
+
+int ath10k_wmi_start_scan(struct ath10k *ar,
+                         const struct wmi_start_scan_arg *arg)
+{
+       struct wmi_start_scan_cmd *cmd;
+       struct sk_buff *skb;
+       struct wmi_ie_data *ie;
+       struct wmi_chan_list *channels;
+       struct wmi_ssid_list *ssids;
+       struct wmi_bssid_list *bssids;
+       u32 scan_id;
+       u32 scan_req_id;
+       int off;
+       int len = 0;
+       int i;
+
+       len = ath10k_wmi_start_scan_calc_len(arg);
+       if (len < 0)
+               return len; /* len contains error code here */
+
+       skb = ath10k_wmi_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       scan_id  = WMI_HOST_SCAN_REQ_ID_PREFIX;
+       scan_id |= arg->scan_id;
+
+       scan_req_id  = WMI_HOST_SCAN_REQUESTOR_ID_PREFIX;
+       scan_req_id |= arg->scan_req_id;
+
+       cmd = (struct wmi_start_scan_cmd *)skb->data;
+       cmd->scan_id            = __cpu_to_le32(scan_id);
+       cmd->scan_req_id        = __cpu_to_le32(scan_req_id);
+       cmd->vdev_id            = __cpu_to_le32(arg->vdev_id);
+       cmd->scan_priority      = __cpu_to_le32(arg->scan_priority);
+       cmd->notify_scan_events = __cpu_to_le32(arg->notify_scan_events);
+       cmd->dwell_time_active  = __cpu_to_le32(arg->dwell_time_active);
+       cmd->dwell_time_passive = __cpu_to_le32(arg->dwell_time_passive);
+       cmd->min_rest_time      = __cpu_to_le32(arg->min_rest_time);
+       cmd->max_rest_time      = __cpu_to_le32(arg->max_rest_time);
+       cmd->repeat_probe_time  = __cpu_to_le32(arg->repeat_probe_time);
+       cmd->probe_spacing_time = __cpu_to_le32(arg->probe_spacing_time);
+       cmd->idle_time          = __cpu_to_le32(arg->idle_time);
+       cmd->max_scan_time      = __cpu_to_le32(arg->max_scan_time);
+       cmd->probe_delay        = __cpu_to_le32(arg->probe_delay);
+       cmd->scan_ctrl_flags    = __cpu_to_le32(arg->scan_ctrl_flags);
+
+       /* TLV list starts after fields included in the struct */
+       off = sizeof(*cmd);
+
+       if (arg->n_channels) {
+               channels = (void *)skb->data + off;
+               channels->tag = __cpu_to_le32(WMI_CHAN_LIST_TAG);
+               channels->num_chan = __cpu_to_le32(arg->n_channels);
+
+               for (i = 0; i < arg->n_channels; i++)
+                       channels->channel_list[i] =
+                               __cpu_to_le32(arg->channels[i]);
+
+               off += sizeof(*channels);
+               off += sizeof(__le32) * arg->n_channels;
+       }
+
+       if (arg->n_ssids) {
+               ssids = (void *)skb->data + off;
+               ssids->tag = __cpu_to_le32(WMI_SSID_LIST_TAG);
+               ssids->num_ssids = __cpu_to_le32(arg->n_ssids);
+
+               for (i = 0; i < arg->n_ssids; i++) {
+                       ssids->ssids[i].ssid_len =
+                               __cpu_to_le32(arg->ssids[i].len);
+                       memcpy(&ssids->ssids[i].ssid,
+                              arg->ssids[i].ssid,
+                              arg->ssids[i].len);
+               }
+
+               off += sizeof(*ssids);
+               off += sizeof(struct wmi_ssid) * arg->n_ssids;
+       }
+
+       if (arg->n_bssids) {
+               bssids = (void *)skb->data + off;
+               bssids->tag = __cpu_to_le32(WMI_BSSID_LIST_TAG);
+               bssids->num_bssid = __cpu_to_le32(arg->n_bssids);
+
+               for (i = 0; i < arg->n_bssids; i++)
+                       memcpy(&bssids->bssid_list[i],
+                              arg->bssids[i].bssid,
+                              ETH_ALEN);
+
+               off += sizeof(*bssids);
+               off += sizeof(struct wmi_mac_addr) * arg->n_bssids;
+       }
+
+       if (arg->ie_len) {
+               ie = (void *)skb->data + off;
+               ie->tag = __cpu_to_le32(WMI_IE_TAG);
+               ie->ie_len = __cpu_to_le32(arg->ie_len);
+               memcpy(ie->ie_data, arg->ie, arg->ie_len);
+
+               off += sizeof(*ie);
+               off += roundup(arg->ie_len, 4);
+       }
+
+       if (off != skb->len) {
+               dev_kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n");
+       return ath10k_wmi_cmd_send(ar, skb, WMI_START_SCAN_CMDID);
+}
+
+void ath10k_wmi_start_scan_init(struct ath10k *ar,
+                               struct wmi_start_scan_arg *arg)
+{
+       /* setup commonly used values */
+       arg->scan_req_id = 1;
+       arg->scan_priority = WMI_SCAN_PRIORITY_LOW;
+       arg->dwell_time_active = 50;
+       arg->dwell_time_passive = 150;
+       arg->min_rest_time = 50;
+       arg->max_rest_time = 500;
+       arg->repeat_probe_time = 0;
+       arg->probe_spacing_time = 0;
+       arg->idle_time = 0;
+       arg->max_scan_time = 5000;
+       arg->probe_delay = 5;
+       arg->notify_scan_events = WMI_SCAN_EVENT_STARTED
+               | WMI_SCAN_EVENT_COMPLETED
+               | WMI_SCAN_EVENT_BSS_CHANNEL
+               | WMI_SCAN_EVENT_FOREIGN_CHANNEL
+               | WMI_SCAN_EVENT_DEQUEUED;
+       arg->scan_ctrl_flags |= WMI_SCAN_ADD_OFDM_RATES;
+       arg->scan_ctrl_flags |= WMI_SCAN_CHAN_STAT_EVENT;
+       arg->n_bssids = 1;
+       arg->bssids[0].bssid = "\xFF\xFF\xFF\xFF\xFF\xFF";
+}
+
+int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg)
+{
+       struct wmi_stop_scan_cmd *cmd;
+       struct sk_buff *skb;
+       u32 scan_id;
+       u32 req_id;
+
+       if (arg->req_id > 0xFFF)
+               return -EINVAL;
+       if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       scan_id = arg->u.scan_id;
+       scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX;
+
+       req_id = arg->req_id;
+       req_id |= WMI_HOST_SCAN_REQUESTOR_ID_PREFIX;
+
+       cmd = (struct wmi_stop_scan_cmd *)skb->data;
+       cmd->req_type    = __cpu_to_le32(arg->req_type);
+       cmd->vdev_id     = __cpu_to_le32(arg->u.vdev_id);
+       cmd->scan_id     = __cpu_to_le32(scan_id);
+       cmd->scan_req_id = __cpu_to_le32(req_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n",
+                  arg->req_id, arg->req_type, arg->u.scan_id);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_STOP_SCAN_CMDID);
+}
+
+int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
+                          enum wmi_vdev_type type,
+                          enum wmi_vdev_subtype subtype,
+                          const u8 macaddr[ETH_ALEN])
+{
+       struct wmi_vdev_create_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_create_cmd *)skb->data;
+       cmd->vdev_id      = __cpu_to_le32(vdev_id);
+       cmd->vdev_type    = __cpu_to_le32(type);
+       cmd->vdev_subtype = __cpu_to_le32(subtype);
+       memcpy(cmd->vdev_macaddr.addr, macaddr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "WMI vdev create: id %d type %d subtype %d macaddr %pM\n",
+                  vdev_id, type, subtype, macaddr);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_CREATE_CMDID);
+}
+
+int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
+{
+       struct wmi_vdev_delete_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_delete_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "WMI vdev delete id %d\n", vdev_id);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DELETE_CMDID);
+}
+
+static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
+                               const struct wmi_vdev_start_request_arg *arg,
+                               enum wmi_cmd_id cmd_id)
+{
+       struct wmi_vdev_start_request_cmd *cmd;
+       struct sk_buff *skb;
+       const char *cmdname;
+       u32 flags = 0;
+
+       if (cmd_id != WMI_VDEV_START_REQUEST_CMDID &&
+           cmd_id != WMI_VDEV_RESTART_REQUEST_CMDID)
+               return -EINVAL;
+       if (WARN_ON(arg->ssid && arg->ssid_len == 0))
+               return -EINVAL;
+       if (WARN_ON(arg->hidden_ssid && !arg->ssid))
+               return -EINVAL;
+       if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid)))
+               return -EINVAL;
+
+       if (cmd_id == WMI_VDEV_START_REQUEST_CMDID)
+               cmdname = "start";
+       else if (cmd_id == WMI_VDEV_RESTART_REQUEST_CMDID)
+               cmdname = "restart";
+       else
+               return -EINVAL; /* should not happen, we already check cmd_id */
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       if (arg->hidden_ssid)
+               flags |= WMI_VDEV_START_HIDDEN_SSID;
+       if (arg->pmf_enabled)
+               flags |= WMI_VDEV_START_PMF_ENABLED;
+
+       cmd = (struct wmi_vdev_start_request_cmd *)skb->data;
+       cmd->vdev_id         = __cpu_to_le32(arg->vdev_id);
+       cmd->disable_hw_ack  = __cpu_to_le32(arg->disable_hw_ack);
+       cmd->beacon_interval = __cpu_to_le32(arg->bcn_intval);
+       cmd->dtim_period     = __cpu_to_le32(arg->dtim_period);
+       cmd->flags           = __cpu_to_le32(flags);
+       cmd->bcn_tx_rate     = __cpu_to_le32(arg->bcn_tx_rate);
+       cmd->bcn_tx_power    = __cpu_to_le32(arg->bcn_tx_power);
+
+       if (arg->ssid) {
+               cmd->ssid.ssid_len = __cpu_to_le32(arg->ssid_len);
+               memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len);
+       }
+
+       cmd->chan.mhz = __cpu_to_le32(arg->channel.freq);
+
+       cmd->chan.band_center_freq1 =
+               __cpu_to_le32(arg->channel.band_center_freq1);
+
+       cmd->chan.mode = arg->channel.mode;
+       cmd->chan.min_power = arg->channel.min_power;
+       cmd->chan.max_power = arg->channel.max_power;
+       cmd->chan.reg_power = arg->channel.max_reg_power;
+       cmd->chan.reg_classid = arg->channel.reg_class_id;
+       cmd->chan.antenna_max = arg->channel.max_antenna_gain;
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi vdev %s id 0x%x freq %d, mode %d, ch_flags: 0x%0X,"
+                  "max_power: %d\n", cmdname, arg->vdev_id, arg->channel.freq,
+                  arg->channel.mode, flags, arg->channel.max_power);
+
+       return ath10k_wmi_cmd_send(ar, skb, cmd_id);
+}
+
+int ath10k_wmi_vdev_start(struct ath10k *ar,
+                         const struct wmi_vdev_start_request_arg *arg)
+{
+       return ath10k_wmi_vdev_start_restart(ar, arg,
+                                            WMI_VDEV_START_REQUEST_CMDID);
+}
+
+int ath10k_wmi_vdev_restart(struct ath10k *ar,
+                    const struct wmi_vdev_start_request_arg *arg)
+{
+       return ath10k_wmi_vdev_start_restart(ar, arg,
+                                            WMI_VDEV_RESTART_REQUEST_CMDID);
+}
+
+int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
+{
+       struct wmi_vdev_stop_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_stop_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_STOP_CMDID);
+}
+
+int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
+{
+       struct wmi_vdev_up_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_up_cmd *)skb->data;
+       cmd->vdev_id       = __cpu_to_le32(vdev_id);
+       cmd->vdev_assoc_id = __cpu_to_le32(aid);
+       memcpy(&cmd->vdev_bssid.addr, bssid, 6);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n",
+                  vdev_id, aid, bssid);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_UP_CMDID);
+}
+
+int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
+{
+       struct wmi_vdev_down_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_down_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi mgmt vdev down id 0x%x\n", vdev_id);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DOWN_CMDID);
+}
+
+int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
+                             enum wmi_vdev_param param_id, u32 param_value)
+{
+       struct wmi_vdev_set_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_set_param_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->param_id    = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(param_value);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi vdev id 0x%x set param %d value %d\n",
+                  vdev_id, param_id, param_value);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_SET_PARAM_CMDID);
+}
+
+int ath10k_wmi_vdev_install_key(struct ath10k *ar,
+                               const struct wmi_vdev_install_key_arg *arg)
+{
+       struct wmi_vdev_install_key_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL)
+               return -EINVAL;
+       if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->key_len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_install_key_cmd *)skb->data;
+       cmd->vdev_id       = __cpu_to_le32(arg->vdev_id);
+       cmd->key_idx       = __cpu_to_le32(arg->key_idx);
+       cmd->key_flags     = __cpu_to_le32(arg->key_flags);
+       cmd->key_cipher    = __cpu_to_le32(arg->key_cipher);
+       cmd->key_len       = __cpu_to_le32(arg->key_len);
+       cmd->key_txmic_len = __cpu_to_le32(arg->key_txmic_len);
+       cmd->key_rxmic_len = __cpu_to_le32(arg->key_rxmic_len);
+
+       if (arg->macaddr)
+               memcpy(cmd->peer_macaddr.addr, arg->macaddr, ETH_ALEN);
+       if (arg->key_data)
+               memcpy(cmd->key_data, arg->key_data, arg->key_len);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID);
+}
+
+int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
+                          const u8 peer_addr[ETH_ALEN])
+{
+       struct wmi_peer_create_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_create_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+       memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi peer create vdev_id %d peer_addr %pM\n",
+                  vdev_id, peer_addr);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_CREATE_CMDID);
+}
+
+int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
+                          const u8 peer_addr[ETH_ALEN])
+{
+       struct wmi_peer_delete_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_delete_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+       memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi peer delete vdev_id %d peer_addr %pM\n",
+                  vdev_id, peer_addr);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_DELETE_CMDID);
+}
+
+int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
+                         const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
+{
+       struct wmi_peer_flush_tids_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_flush_tids_cmd *)skb->data;
+       cmd->vdev_id         = __cpu_to_le32(vdev_id);
+       cmd->peer_tid_bitmap = __cpu_to_le32(tid_bitmap);
+       memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n",
+                  vdev_id, peer_addr, tid_bitmap);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_FLUSH_TIDS_CMDID);
+}
+
+int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
+                             const u8 *peer_addr, enum wmi_peer_param param_id,
+                             u32 param_value)
+{
+       struct wmi_peer_set_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_set_param_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->param_id    = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(param_value);
+       memcpy(&cmd->peer_macaddr.addr, peer_addr, 6);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi vdev %d peer 0x%pM set param %d value %d\n",
+                  vdev_id, peer_addr, param_id, param_value);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_SET_PARAM_CMDID);
+}
+
+int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
+                         enum wmi_sta_ps_mode psmode)
+{
+       struct wmi_sta_powersave_mode_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_sta_powersave_mode_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->sta_ps_mode = __cpu_to_le32(psmode);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi set powersave id 0x%x mode %d\n",
+                  vdev_id, psmode);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_MODE_CMDID);
+}
+
+int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
+                               enum wmi_sta_powersave_param param_id,
+                               u32 value)
+{
+       struct wmi_sta_powersave_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_sta_powersave_param_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->param_id    = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(value);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi sta ps param vdev_id 0x%x param %d value %d\n",
+                  vdev_id, param_id, value);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_PARAM_CMDID);
+}
+
+int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+                              enum wmi_ap_ps_peer_param param_id, u32 value)
+{
+       struct wmi_ap_ps_peer_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (!mac)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_ap_ps_peer_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+       cmd->param_id = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(value);
+       memcpy(&cmd->peer_macaddr, mac, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n",
+                  vdev_id, param_id, value, mac);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_AP_PS_PEER_PARAM_CMDID);
+}
+
+int ath10k_wmi_scan_chan_list(struct ath10k *ar,
+                             const struct wmi_scan_chan_list_arg *arg)
+{
+       struct wmi_scan_chan_list_cmd *cmd;
+       struct sk_buff *skb;
+       struct wmi_channel_arg *ch;
+       struct wmi_channel *ci;
+       int len;
+       int i;
+
+       len = sizeof(*cmd) + arg->n_channels * sizeof(struct wmi_channel);
+
+       skb = ath10k_wmi_alloc_skb(len);
+       if (!skb)
+               return -EINVAL;
+
+       cmd = (struct wmi_scan_chan_list_cmd *)skb->data;
+       cmd->num_scan_chans = __cpu_to_le32(arg->n_channels);
+
+       for (i = 0; i < arg->n_channels; i++) {
+               u32 flags = 0;
+
+               ch = &arg->channels[i];
+               ci = &cmd->chan_info[i];
+
+               if (ch->passive)
+                       flags |= WMI_CHAN_FLAG_PASSIVE;
+               if (ch->allow_ibss)
+                       flags |= WMI_CHAN_FLAG_ADHOC_ALLOWED;
+               if (ch->allow_ht)
+                       flags |= WMI_CHAN_FLAG_ALLOW_HT;
+               if (ch->allow_vht)
+                       flags |= WMI_CHAN_FLAG_ALLOW_VHT;
+               if (ch->ht40plus)
+                       flags |= WMI_CHAN_FLAG_HT40_PLUS;
+
+               ci->mhz               = __cpu_to_le32(ch->freq);
+               ci->band_center_freq1 = __cpu_to_le32(ch->freq);
+               ci->band_center_freq2 = 0;
+               ci->min_power         = ch->min_power;
+               ci->max_power         = ch->max_power;
+               ci->reg_power         = ch->max_reg_power;
+               ci->antenna_max       = ch->max_antenna_gain;
+               ci->antenna_max       = 0;
+
+               /* mode & flags share storage */
+               ci->mode              = ch->mode;
+               ci->flags            |= __cpu_to_le32(flags);
+       }
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_SCAN_CHAN_LIST_CMDID);
+}
+
+int ath10k_wmi_peer_assoc(struct ath10k *ar,
+                         const struct wmi_peer_assoc_complete_arg *arg)
+{
+       struct wmi_peer_assoc_complete_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (arg->peer_mpdu_density > 16)
+               return -EINVAL;
+       if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES)
+               return -EINVAL;
+       if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_assoc_complete_cmd *)skb->data;
+       cmd->vdev_id            = __cpu_to_le32(arg->vdev_id);
+       cmd->peer_new_assoc     = __cpu_to_le32(arg->peer_reassoc ? 0 : 1);
+       cmd->peer_associd       = __cpu_to_le32(arg->peer_aid);
+       cmd->peer_flags         = __cpu_to_le32(arg->peer_flags);
+       cmd->peer_caps          = __cpu_to_le32(arg->peer_caps);
+       cmd->peer_listen_intval = __cpu_to_le32(arg->peer_listen_intval);
+       cmd->peer_ht_caps       = __cpu_to_le32(arg->peer_ht_caps);
+       cmd->peer_max_mpdu      = __cpu_to_le32(arg->peer_max_mpdu);
+       cmd->peer_mpdu_density  = __cpu_to_le32(arg->peer_mpdu_density);
+       cmd->peer_rate_caps     = __cpu_to_le32(arg->peer_rate_caps);
+       cmd->peer_nss           = __cpu_to_le32(arg->peer_num_spatial_streams);
+       cmd->peer_vht_caps      = __cpu_to_le32(arg->peer_vht_caps);
+       cmd->peer_phymode       = __cpu_to_le32(arg->peer_phymode);
+
+       memcpy(cmd->peer_macaddr.addr, arg->addr, ETH_ALEN);
+
+       cmd->peer_legacy_rates.num_rates =
+               __cpu_to_le32(arg->peer_legacy_rates.num_rates);
+       memcpy(cmd->peer_legacy_rates.rates, arg->peer_legacy_rates.rates,
+              arg->peer_legacy_rates.num_rates);
+
+       cmd->peer_ht_rates.num_rates =
+               __cpu_to_le32(arg->peer_ht_rates.num_rates);
+       memcpy(cmd->peer_ht_rates.rates, arg->peer_ht_rates.rates,
+              arg->peer_ht_rates.num_rates);
+
+       cmd->peer_vht_rates.rx_max_rate =
+               __cpu_to_le32(arg->peer_vht_rates.rx_max_rate);
+       cmd->peer_vht_rates.rx_mcs_set =
+               __cpu_to_le32(arg->peer_vht_rates.rx_mcs_set);
+       cmd->peer_vht_rates.tx_max_rate =
+               __cpu_to_le32(arg->peer_vht_rates.tx_max_rate);
+       cmd->peer_vht_rates.tx_mcs_set =
+               __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID);
+}
+
+int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg)
+{
+       struct wmi_bcn_tx_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_bcn_tx_cmd *)skb->data;
+       cmd->hdr.vdev_id  = __cpu_to_le32(arg->vdev_id);
+       cmd->hdr.tx_rate  = __cpu_to_le32(arg->tx_rate);
+       cmd->hdr.tx_power = __cpu_to_le32(arg->tx_power);
+       cmd->hdr.bcn_len  = __cpu_to_le32(arg->bcn_len);
+       memcpy(cmd->bcn, arg->bcn, arg->bcn_len);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_BCN_TX_CMDID);
+}
+
+static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
+                                         const struct wmi_wmm_params_arg *arg)
+{
+       params->cwmin  = __cpu_to_le32(arg->cwmin);
+       params->cwmax  = __cpu_to_le32(arg->cwmax);
+       params->aifs   = __cpu_to_le32(arg->aifs);
+       params->txop   = __cpu_to_le32(arg->txop);
+       params->acm    = __cpu_to_le32(arg->acm);
+       params->no_ack = __cpu_to_le32(arg->no_ack);
+}
+
+int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
+                       const struct wmi_pdev_set_wmm_params_arg *arg)
+{
+       struct wmi_pdev_set_wmm_params *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_set_wmm_params *)skb->data;
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_be, &arg->ac_be);
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_bk, &arg->ac_bk);
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vi, &arg->ac_vi);
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n");
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_WMM_PARAMS_CMDID);
+}
+
+int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
+{
+       struct wmi_request_stats_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_request_stats_cmd *)skb->data;
+       cmd->stats_id = __cpu_to_le32(stats_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
+}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
new file mode 100644 (file)
index 0000000..9555f5a
--- /dev/null
@@ -0,0 +1,3052 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WMI_H_
+#define _WMI_H_
+
+#include <linux/types.h>
+#include <net/mac80211.h>
+
+/*
+ * This file specifies the WMI interface for the Unified Software
+ * Architecture.
+ *
+ * It includes definitions of all the commands and events. Commands are
+ * messages from the host to the target. Events and Replies are messages
+ * from the target to the host.
+ *
+ * Ownership of correctness in regards to WMI commands belongs to the host
+ * driver and the target is not required to validate parameters for value,
+ * proper range, or any other checking.
+ *
+ * Guidelines for extending this interface are below.
+ *
+ * 1. Add new WMI commands ONLY within the specified range - 0x9000 - 0x9fff
+ *
+ * 2. Use ONLY u32 type for defining member variables within WMI
+ *    command/event structures. Do not use u8, u16, bool or
+ *    enum types within these structures.
+ *
+ * 3. DO NOT define bit fields within structures. Implement bit fields
+ *    using masks if necessary. Do not use the programming language's bit
+ *    field definition.
+ *
+ * 4. Define macros for encode/decode of u8, u16 fields within
+ *    the u32 variables. Use these macros for set/get of these fields.
+ *    Try to use this to optimize the structure without bloating it with
+ *    u32 variables for every lower sized field.
+ *
+ * 5. Do not use PACK/UNPACK attributes for the structures as each member
+ *    variable is already 4-byte aligned by virtue of being a u32
+ *    type.
+ *
+ * 6. Comment each parameter part of the WMI command/event structure by
+ *    using the 2 stars at the begining of C comment instead of one star to
+ *    enable HTML document generation using Doxygen.
+ *
+ */
+
+/* Control Path */
+struct wmi_cmd_hdr {
+       __le32 cmd_id;
+} __packed;
+
+#define WMI_CMD_HDR_CMD_ID_MASK   0x00FFFFFF
+#define WMI_CMD_HDR_CMD_ID_LSB    0
+#define WMI_CMD_HDR_PLT_PRIV_MASK 0xFF000000
+#define WMI_CMD_HDR_PLT_PRIV_LSB  24
+
+#define HTC_PROTOCOL_VERSION    0x0002
+#define WMI_PROTOCOL_VERSION    0x0002
+
+enum wmi_service_id {
+       WMI_SERVICE_BEACON_OFFLOAD = 0,   /* beacon offload */
+       WMI_SERVICE_SCAN_OFFLOAD,         /* scan offload */
+       WMI_SERVICE_ROAM_OFFLOAD,         /* roam offload */
+       WMI_SERVICE_BCN_MISS_OFFLOAD,     /* beacon miss offload */
+       WMI_SERVICE_STA_PWRSAVE,          /* fake sleep + basic power save */
+       WMI_SERVICE_STA_ADVANCED_PWRSAVE, /* uapsd, pspoll, force sleep */
+       WMI_SERVICE_AP_UAPSD,             /* uapsd on AP */
+       WMI_SERVICE_AP_DFS,               /* DFS on AP */
+       WMI_SERVICE_11AC,                 /* supports 11ac */
+       WMI_SERVICE_BLOCKACK,   /* Supports triggering ADDBA/DELBA from host*/
+       WMI_SERVICE_PHYERR,               /* PHY error */
+       WMI_SERVICE_BCN_FILTER,           /* Beacon filter support */
+       WMI_SERVICE_RTT,                  /* RTT (round trip time) support */
+       WMI_SERVICE_RATECTRL,             /* Rate-control */
+       WMI_SERVICE_WOW,                  /* WOW Support */
+       WMI_SERVICE_RATECTRL_CACHE,       /* Rate-control caching */
+       WMI_SERVICE_IRAM_TIDS,            /* TIDs in IRAM */
+       WMI_SERVICE_ARPNS_OFFLOAD,        /* ARP NS Offload support */
+       WMI_SERVICE_NLO,                  /* Network list offload service */
+       WMI_SERVICE_GTK_OFFLOAD,          /* GTK offload */
+       WMI_SERVICE_SCAN_SCH,             /* Scan Scheduler Service */
+       WMI_SERVICE_CSA_OFFLOAD,          /* CSA offload service */
+       WMI_SERVICE_CHATTER,              /* Chatter service */
+       WMI_SERVICE_COEX_FREQAVOID,       /* FW report freq range to avoid */
+       WMI_SERVICE_PACKET_POWER_SAVE,    /* packet power save service */
+       WMI_SERVICE_FORCE_FW_HANG,        /* To test fw recovery mechanism */
+       WMI_SERVICE_GPIO,                 /* GPIO service */
+       WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, /* Modulated DTIM support */
+       WMI_STA_UAPSD_BASIC_AUTO_TRIG,    /* UAPSD AC Trigger Generation  */
+       WMI_STA_UAPSD_VAR_AUTO_TRIG,      /* -do- */
+       WMI_SERVICE_STA_KEEP_ALIVE,       /* STA keep alive mechanism support */
+       WMI_SERVICE_TX_ENCAP,             /* Packet type for TX encapsulation */
+
+       WMI_SERVICE_LAST,
+       WMI_MAX_SERVICE = 64              /* max service */
+};
+
+static inline char *wmi_service_name(int service_id)
+{
+       switch (service_id) {
+       case WMI_SERVICE_BEACON_OFFLOAD:
+               return "BEACON_OFFLOAD";
+       case WMI_SERVICE_SCAN_OFFLOAD:
+               return "SCAN_OFFLOAD";
+       case WMI_SERVICE_ROAM_OFFLOAD:
+               return "ROAM_OFFLOAD";
+       case WMI_SERVICE_BCN_MISS_OFFLOAD:
+               return "BCN_MISS_OFFLOAD";
+       case WMI_SERVICE_STA_PWRSAVE:
+               return "STA_PWRSAVE";
+       case WMI_SERVICE_STA_ADVANCED_PWRSAVE:
+               return "STA_ADVANCED_PWRSAVE";
+       case WMI_SERVICE_AP_UAPSD:
+               return "AP_UAPSD";
+       case WMI_SERVICE_AP_DFS:
+               return "AP_DFS";
+       case WMI_SERVICE_11AC:
+               return "11AC";
+       case WMI_SERVICE_BLOCKACK:
+               return "BLOCKACK";
+       case WMI_SERVICE_PHYERR:
+               return "PHYERR";
+       case WMI_SERVICE_BCN_FILTER:
+               return "BCN_FILTER";
+       case WMI_SERVICE_RTT:
+               return "RTT";
+       case WMI_SERVICE_RATECTRL:
+               return "RATECTRL";
+       case WMI_SERVICE_WOW:
+               return "WOW";
+       case WMI_SERVICE_RATECTRL_CACHE:
+               return "RATECTRL CACHE";
+       case WMI_SERVICE_IRAM_TIDS:
+               return "IRAM TIDS";
+       case WMI_SERVICE_ARPNS_OFFLOAD:
+               return "ARPNS_OFFLOAD";
+       case WMI_SERVICE_NLO:
+               return "NLO";
+       case WMI_SERVICE_GTK_OFFLOAD:
+               return "GTK_OFFLOAD";
+       case WMI_SERVICE_SCAN_SCH:
+               return "SCAN_SCH";
+       case WMI_SERVICE_CSA_OFFLOAD:
+               return "CSA_OFFLOAD";
+       case WMI_SERVICE_CHATTER:
+               return "CHATTER";
+       case WMI_SERVICE_COEX_FREQAVOID:
+               return "COEX_FREQAVOID";
+       case WMI_SERVICE_PACKET_POWER_SAVE:
+               return "PACKET_POWER_SAVE";
+       case WMI_SERVICE_FORCE_FW_HANG:
+               return "FORCE FW HANG";
+       case WMI_SERVICE_GPIO:
+               return "GPIO";
+       case WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM:
+               return "MODULATED DTIM";
+       case WMI_STA_UAPSD_BASIC_AUTO_TRIG:
+               return "BASIC UAPSD";
+       case WMI_STA_UAPSD_VAR_AUTO_TRIG:
+               return "VAR UAPSD";
+       case WMI_SERVICE_STA_KEEP_ALIVE:
+               return "STA KEEP ALIVE";
+       case WMI_SERVICE_TX_ENCAP:
+               return "TX ENCAP";
+       default:
+               return "UNKNOWN SERVICE\n";
+       }
+}
+
+
+#define WMI_SERVICE_BM_SIZE \
+       ((WMI_MAX_SERVICE + sizeof(u32) - 1)/sizeof(u32))
+
+/* 2 word representation of MAC addr */
+struct wmi_mac_addr {
+       union {
+               u8 addr[6];
+               struct {
+                       u32 word0;
+                       u32 word1;
+               } __packed;
+       } __packed;
+} __packed;
+
+/* macro to convert MAC address from WMI word format to char array */
+#define WMI_MAC_ADDR_TO_CHAR_ARRAY(pwmi_mac_addr, c_macaddr) do { \
+       (c_macaddr)[0] =  ((pwmi_mac_addr)->word0) & 0xff; \
+       (c_macaddr)[1] = (((pwmi_mac_addr)->word0) >> 8) & 0xff; \
+       (c_macaddr)[2] = (((pwmi_mac_addr)->word0) >> 16) & 0xff; \
+       (c_macaddr)[3] = (((pwmi_mac_addr)->word0) >> 24) & 0xff; \
+       (c_macaddr)[4] =  ((pwmi_mac_addr)->word1) & 0xff; \
+       (c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \
+       } while (0)
+
+/*
+ * wmi command groups.
+ */
+enum wmi_cmd_group {
+       /* 0 to 2 are reserved */
+       WMI_GRP_START = 0x3,
+       WMI_GRP_SCAN = WMI_GRP_START,
+       WMI_GRP_PDEV,
+       WMI_GRP_VDEV,
+       WMI_GRP_PEER,
+       WMI_GRP_MGMT,
+       WMI_GRP_BA_NEG,
+       WMI_GRP_STA_PS,
+       WMI_GRP_DFS,
+       WMI_GRP_ROAM,
+       WMI_GRP_OFL_SCAN,
+       WMI_GRP_P2P,
+       WMI_GRP_AP_PS,
+       WMI_GRP_RATE_CTRL,
+       WMI_GRP_PROFILE,
+       WMI_GRP_SUSPEND,
+       WMI_GRP_BCN_FILTER,
+       WMI_GRP_WOW,
+       WMI_GRP_RTT,
+       WMI_GRP_SPECTRAL,
+       WMI_GRP_STATS,
+       WMI_GRP_ARP_NS_OFL,
+       WMI_GRP_NLO_OFL,
+       WMI_GRP_GTK_OFL,
+       WMI_GRP_CSA_OFL,
+       WMI_GRP_CHATTER,
+       WMI_GRP_TID_ADDBA,
+       WMI_GRP_MISC,
+       WMI_GRP_GPIO,
+};
+
+#define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1)
+#define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1)
+
+/* Command IDs and commande events. */
+enum wmi_cmd_id {
+       WMI_INIT_CMDID = 0x1,
+
+       /* Scan specific commands */
+       WMI_START_SCAN_CMDID = WMI_CMD_GRP(WMI_GRP_SCAN),
+       WMI_STOP_SCAN_CMDID,
+       WMI_SCAN_CHAN_LIST_CMDID,
+       WMI_SCAN_SCH_PRIO_TBL_CMDID,
+
+       /* PDEV (physical device) specific commands */
+       WMI_PDEV_SET_REGDOMAIN_CMDID = WMI_CMD_GRP(WMI_GRP_PDEV),
+       WMI_PDEV_SET_CHANNEL_CMDID,
+       WMI_PDEV_SET_PARAM_CMDID,
+       WMI_PDEV_PKTLOG_ENABLE_CMDID,
+       WMI_PDEV_PKTLOG_DISABLE_CMDID,
+       WMI_PDEV_SET_WMM_PARAMS_CMDID,
+       WMI_PDEV_SET_HT_CAP_IE_CMDID,
+       WMI_PDEV_SET_VHT_CAP_IE_CMDID,
+       WMI_PDEV_SET_DSCP_TID_MAP_CMDID,
+       WMI_PDEV_SET_QUIET_MODE_CMDID,
+       WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+       WMI_PDEV_GET_TPC_CONFIG_CMDID,
+       WMI_PDEV_SET_BASE_MACADDR_CMDID,
+
+       /* VDEV (virtual device) specific commands */
+       WMI_VDEV_CREATE_CMDID = WMI_CMD_GRP(WMI_GRP_VDEV),
+       WMI_VDEV_DELETE_CMDID,
+       WMI_VDEV_START_REQUEST_CMDID,
+       WMI_VDEV_RESTART_REQUEST_CMDID,
+       WMI_VDEV_UP_CMDID,
+       WMI_VDEV_STOP_CMDID,
+       WMI_VDEV_DOWN_CMDID,
+       WMI_VDEV_SET_PARAM_CMDID,
+       WMI_VDEV_INSTALL_KEY_CMDID,
+
+       /* peer specific commands */
+       WMI_PEER_CREATE_CMDID = WMI_CMD_GRP(WMI_GRP_PEER),
+       WMI_PEER_DELETE_CMDID,
+       WMI_PEER_FLUSH_TIDS_CMDID,
+       WMI_PEER_SET_PARAM_CMDID,
+       WMI_PEER_ASSOC_CMDID,
+       WMI_PEER_ADD_WDS_ENTRY_CMDID,
+       WMI_PEER_REMOVE_WDS_ENTRY_CMDID,
+       WMI_PEER_MCAST_GROUP_CMDID,
+
+       /* beacon/management specific commands */
+       WMI_BCN_TX_CMDID = WMI_CMD_GRP(WMI_GRP_MGMT),
+       WMI_PDEV_SEND_BCN_CMDID,
+       WMI_BCN_TMPL_CMDID,
+       WMI_BCN_FILTER_RX_CMDID,
+       WMI_PRB_REQ_FILTER_RX_CMDID,
+       WMI_MGMT_TX_CMDID,
+       WMI_PRB_TMPL_CMDID,
+
+       /* commands to directly control BA negotiation directly from host. */
+       WMI_ADDBA_CLEAR_RESP_CMDID = WMI_CMD_GRP(WMI_GRP_BA_NEG),
+       WMI_ADDBA_SEND_CMDID,
+       WMI_ADDBA_STATUS_CMDID,
+       WMI_DELBA_SEND_CMDID,
+       WMI_ADDBA_SET_RESP_CMDID,
+       WMI_SEND_SINGLEAMSDU_CMDID,
+
+       /* Station power save specific config */
+       WMI_STA_POWERSAVE_MODE_CMDID = WMI_CMD_GRP(WMI_GRP_STA_PS),
+       WMI_STA_POWERSAVE_PARAM_CMDID,
+       WMI_STA_MIMO_PS_MODE_CMDID,
+
+       /** DFS-specific commands */
+       WMI_PDEV_DFS_ENABLE_CMDID = WMI_CMD_GRP(WMI_GRP_DFS),
+       WMI_PDEV_DFS_DISABLE_CMDID,
+
+       /* Roaming specific  commands */
+       WMI_ROAM_SCAN_MODE = WMI_CMD_GRP(WMI_GRP_ROAM),
+       WMI_ROAM_SCAN_RSSI_THRESHOLD,
+       WMI_ROAM_SCAN_PERIOD,
+       WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+       WMI_ROAM_AP_PROFILE,
+
+       /* offload scan specific commands */
+       WMI_OFL_SCAN_ADD_AP_PROFILE = WMI_CMD_GRP(WMI_GRP_OFL_SCAN),
+       WMI_OFL_SCAN_REMOVE_AP_PROFILE,
+       WMI_OFL_SCAN_PERIOD,
+
+       /* P2P specific commands */
+       WMI_P2P_DEV_SET_DEVICE_INFO = WMI_CMD_GRP(WMI_GRP_P2P),
+       WMI_P2P_DEV_SET_DISCOVERABILITY,
+       WMI_P2P_GO_SET_BEACON_IE,
+       WMI_P2P_GO_SET_PROBE_RESP_IE,
+       WMI_P2P_SET_VENDOR_IE_DATA_CMDID,
+
+       /* AP power save specific config */
+       WMI_AP_PS_PEER_PARAM_CMDID = WMI_CMD_GRP(WMI_GRP_AP_PS),
+       WMI_AP_PS_PEER_UAPSD_COEX_CMDID,
+
+       /* Rate-control specific commands */
+       WMI_PEER_RATE_RETRY_SCHED_CMDID =
+       WMI_CMD_GRP(WMI_GRP_RATE_CTRL),
+
+       /* WLAN Profiling commands. */
+       WMI_WLAN_PROFILE_TRIGGER_CMDID = WMI_CMD_GRP(WMI_GRP_PROFILE),
+       WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+       WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+       WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+       WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+
+       /* Suspend resume command Ids */
+       WMI_PDEV_SUSPEND_CMDID = WMI_CMD_GRP(WMI_GRP_SUSPEND),
+       WMI_PDEV_RESUME_CMDID,
+
+       /* Beacon filter commands */
+       WMI_ADD_BCN_FILTER_CMDID = WMI_CMD_GRP(WMI_GRP_BCN_FILTER),
+       WMI_RMV_BCN_FILTER_CMDID,
+
+       /* WOW Specific WMI commands*/
+       WMI_WOW_ADD_WAKE_PATTERN_CMDID = WMI_CMD_GRP(WMI_GRP_WOW),
+       WMI_WOW_DEL_WAKE_PATTERN_CMDID,
+       WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+       WMI_WOW_ENABLE_CMDID,
+       WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+
+       /* RTT measurement related cmd */
+       WMI_RTT_MEASREQ_CMDID = WMI_CMD_GRP(WMI_GRP_RTT),
+       WMI_RTT_TSF_CMDID,
+
+       /* spectral scan commands */
+       WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID = WMI_CMD_GRP(WMI_GRP_SPECTRAL),
+       WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+
+       /* F/W stats */
+       WMI_REQUEST_STATS_CMDID = WMI_CMD_GRP(WMI_GRP_STATS),
+
+       /* ARP OFFLOAD REQUEST*/
+       WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_CMD_GRP(WMI_GRP_ARP_NS_OFL),
+
+       /* NS offload confid*/
+       WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_NLO_OFL),
+
+       /* GTK offload Specific WMI commands*/
+       WMI_GTK_OFFLOAD_CMDID = WMI_CMD_GRP(WMI_GRP_GTK_OFL),
+
+       /* CSA offload Specific WMI commands*/
+       WMI_CSA_OFFLOAD_ENABLE_CMDID = WMI_CMD_GRP(WMI_GRP_CSA_OFL),
+       WMI_CSA_OFFLOAD_CHANSWITCH_CMDID,
+
+       /* Chatter commands*/
+       WMI_CHATTER_SET_MODE_CMDID = WMI_CMD_GRP(WMI_GRP_CHATTER),
+
+       /* addba specific commands */
+       WMI_PEER_TID_ADDBA_CMDID = WMI_CMD_GRP(WMI_GRP_TID_ADDBA),
+       WMI_PEER_TID_DELBA_CMDID,
+
+       /* set station mimo powersave method */
+       WMI_STA_DTIM_PS_METHOD_CMDID,
+       /* Configure the Station UAPSD AC Auto Trigger Parameters */
+       WMI_STA_UAPSD_AUTO_TRIG_CMDID,
+
+       /* STA Keep alive parameter configuration,
+          Requires WMI_SERVICE_STA_KEEP_ALIVE */
+       WMI_STA_KEEPALIVE_CMD,
+
+       /* misc command group */
+       WMI_ECHO_CMDID = WMI_CMD_GRP(WMI_GRP_MISC),
+       WMI_PDEV_UTF_CMDID,
+       WMI_DBGLOG_CFG_CMDID,
+       WMI_PDEV_QVIT_CMDID,
+       WMI_PDEV_FTM_INTG_CMDID,
+       WMI_VDEV_SET_KEEPALIVE_CMDID,
+       WMI_VDEV_GET_KEEPALIVE_CMDID,
+
+       /* GPIO Configuration */
+       WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
+       WMI_GPIO_OUTPUT_CMDID,
+};
+
+enum wmi_event_id {
+       WMI_SERVICE_READY_EVENTID = 0x1,
+       WMI_READY_EVENTID,
+
+       /* Scan specific events */
+       WMI_SCAN_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_SCAN),
+
+       /* PDEV specific events */
+       WMI_PDEV_TPC_CONFIG_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PDEV),
+       WMI_CHAN_INFO_EVENTID,
+       WMI_PHYERR_EVENTID,
+
+       /* VDEV specific events */
+       WMI_VDEV_START_RESP_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_VDEV),
+       WMI_VDEV_STOPPED_EVENTID,
+       WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID,
+
+       /* peer specific events */
+       WMI_PEER_STA_KICKOUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PEER),
+
+       /* beacon/mgmt specific events */
+       WMI_MGMT_RX_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MGMT),
+       WMI_HOST_SWBA_EVENTID,
+       WMI_TBTTOFFSET_UPDATE_EVENTID,
+
+       /* ADDBA Related WMI Events*/
+       WMI_TX_DELBA_COMPLETE_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_BA_NEG),
+       WMI_TX_ADDBA_COMPLETE_EVENTID,
+
+       /* Roam event to trigger roaming on host */
+       WMI_ROAM_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_ROAM),
+       WMI_PROFILE_MATCH,
+
+       /* WoW */
+       WMI_WOW_WAKEUP_HOST_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_WOW),
+
+       /* RTT */
+       WMI_RTT_MEASUREMENT_REPORT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_RTT),
+       WMI_TSF_MEASUREMENT_REPORT_EVENTID,
+       WMI_RTT_ERROR_REPORT_EVENTID,
+
+       /* GTK offload */
+       WMI_GTK_OFFLOAD_STATUS_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GTK_OFL),
+       WMI_GTK_REKEY_FAIL_EVENTID,
+
+       /* CSA IE received event */
+       WMI_CSA_HANDLING_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_CSA_OFL),
+
+       /* Misc events */
+       WMI_ECHO_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MISC),
+       WMI_PDEV_UTF_EVENTID,
+       WMI_DEBUG_MESG_EVENTID,
+       WMI_UPDATE_STATS_EVENTID,
+       WMI_DEBUG_PRINT_EVENTID,
+       WMI_DCS_INTERFERENCE_EVENTID,
+       WMI_PDEV_QVIT_EVENTID,
+       WMI_WLAN_PROFILE_DATA_EVENTID,
+       WMI_PDEV_FTM_INTG_EVENTID,
+       WMI_WLAN_FREQ_AVOID_EVENTID,
+       WMI_VDEV_GET_KEEPALIVE_EVENTID,
+
+       /* GPIO Event */
+       WMI_GPIO_INPUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GPIO),
+};
+
+enum wmi_phy_mode {
+       MODE_11A        = 0,   /* 11a Mode */
+       MODE_11G        = 1,   /* 11b/g Mode */
+       MODE_11B        = 2,   /* 11b Mode */
+       MODE_11GONLY    = 3,   /* 11g only Mode */
+       MODE_11NA_HT20   = 4,  /* 11a HT20 mode */
+       MODE_11NG_HT20   = 5,  /* 11g HT20 mode */
+       MODE_11NA_HT40   = 6,  /* 11a HT40 mode */
+       MODE_11NG_HT40   = 7,  /* 11g HT40 mode */
+       MODE_11AC_VHT20 = 8,
+       MODE_11AC_VHT40 = 9,
+       MODE_11AC_VHT80 = 10,
+       /*    MODE_11AC_VHT160 = 11, */
+       MODE_11AC_VHT20_2G = 11,
+       MODE_11AC_VHT40_2G = 12,
+       MODE_11AC_VHT80_2G = 13,
+       MODE_UNKNOWN    = 14,
+       MODE_MAX        = 14
+};
+
+#define WMI_CHAN_LIST_TAG      0x1
+#define WMI_SSID_LIST_TAG      0x2
+#define WMI_BSSID_LIST_TAG     0x3
+#define WMI_IE_TAG             0x4
+
+struct wmi_channel {
+       __le32 mhz;
+       __le32 band_center_freq1;
+       __le32 band_center_freq2; /* valid for 11ac, 80plus80 */
+       union {
+               __le32 flags; /* WMI_CHAN_FLAG_ */
+               struct {
+                       u8 mode; /* only 6 LSBs */
+               } __packed;
+       } __packed;
+       union {
+               __le32 reginfo0;
+               struct {
+                       u8 min_power;
+                       u8 max_power;
+                       u8 reg_power;
+                       u8 reg_classid;
+               } __packed;
+       } __packed;
+       union {
+               __le32 reginfo1;
+               struct {
+                       u8 antenna_max;
+               } __packed;
+       } __packed;
+} __packed;
+
+struct wmi_channel_arg {
+       u32 freq;
+       u32 band_center_freq1;
+       bool passive;
+       bool allow_ibss;
+       bool allow_ht;
+       bool allow_vht;
+       bool ht40plus;
+       /* note: power unit is 1/4th of dBm */
+       u32 min_power;
+       u32 max_power;
+       u32 max_reg_power;
+       u32 max_antenna_gain;
+       u32 reg_class_id;
+       enum wmi_phy_mode mode;
+};
+
+enum wmi_channel_change_cause {
+       WMI_CHANNEL_CHANGE_CAUSE_NONE = 0,
+       WMI_CHANNEL_CHANGE_CAUSE_CSA,
+};
+
+#define WMI_CHAN_FLAG_HT40_PLUS      (1 << 6)
+#define WMI_CHAN_FLAG_PASSIVE        (1 << 7)
+#define WMI_CHAN_FLAG_ADHOC_ALLOWED  (1 << 8)
+#define WMI_CHAN_FLAG_AP_DISABLED    (1 << 9)
+#define WMI_CHAN_FLAG_DFS            (1 << 10)
+#define WMI_CHAN_FLAG_ALLOW_HT       (1 << 11)
+#define WMI_CHAN_FLAG_ALLOW_VHT      (1 << 12)
+
+/* Indicate reason for channel switch */
+#define WMI_CHANNEL_CHANGE_CAUSE_CSA (1 << 13)
+
+#define WMI_MAX_SPATIAL_STREAM   3
+
+/* HT Capabilities*/
+#define WMI_HT_CAP_ENABLED                0x0001   /* HT Enabled/ disabled */
+#define WMI_HT_CAP_HT20_SGI       0x0002   /* Short Guard Interval with HT20 */
+#define WMI_HT_CAP_DYNAMIC_SMPS           0x0004   /* Dynamic MIMO powersave */
+#define WMI_HT_CAP_TX_STBC                0x0008   /* B3 TX STBC */
+#define WMI_HT_CAP_TX_STBC_MASK_SHIFT     3
+#define WMI_HT_CAP_RX_STBC                0x0030   /* B4-B5 RX STBC */
+#define WMI_HT_CAP_RX_STBC_MASK_SHIFT     4
+#define WMI_HT_CAP_LDPC                   0x0040   /* LDPC supported */
+#define WMI_HT_CAP_L_SIG_TXOP_PROT        0x0080   /* L-SIG TXOP Protection */
+#define WMI_HT_CAP_MPDU_DENSITY           0x0700   /* MPDU Density */
+#define WMI_HT_CAP_MPDU_DENSITY_MASK_SHIFT 8
+#define WMI_HT_CAP_HT40_SGI               0x0800
+
+#define WMI_HT_CAP_DEFAULT_ALL (WMI_HT_CAP_ENABLED       | \
+                               WMI_HT_CAP_HT20_SGI      | \
+                               WMI_HT_CAP_HT40_SGI      | \
+                               WMI_HT_CAP_TX_STBC       | \
+                               WMI_HT_CAP_RX_STBC       | \
+                               WMI_HT_CAP_LDPC)
+
+
+/*
+ * WMI_VHT_CAP_* these maps to ieee 802.11ac vht capability information
+ * field. The fields not defined here are not supported, or reserved.
+ * Do not change these masks and if you have to add new one follow the
+ * bitmask as specified by 802.11ac draft.
+ */
+
+#define WMI_VHT_CAP_MAX_MPDU_LEN_MASK            0x00000003
+#define WMI_VHT_CAP_RX_LDPC                      0x00000010
+#define WMI_VHT_CAP_SGI_80MHZ                    0x00000020
+#define WMI_VHT_CAP_TX_STBC                      0x00000080
+#define WMI_VHT_CAP_RX_STBC_MASK                 0x00000300
+#define WMI_VHT_CAP_RX_STBC_MASK_SHIFT           8
+#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP            0x03800000
+#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIFT      23
+#define WMI_VHT_CAP_RX_FIXED_ANT                 0x10000000
+#define WMI_VHT_CAP_TX_FIXED_ANT                 0x20000000
+
+/* The following also refer for max HT AMSDU */
+#define WMI_VHT_CAP_MAX_MPDU_LEN_3839            0x00000000
+#define WMI_VHT_CAP_MAX_MPDU_LEN_7935            0x00000001
+#define WMI_VHT_CAP_MAX_MPDU_LEN_11454           0x00000002
+
+#define WMI_VHT_CAP_DEFAULT_ALL (WMI_VHT_CAP_MAX_MPDU_LEN_11454  | \
+                                WMI_VHT_CAP_RX_LDPC             | \
+                                WMI_VHT_CAP_SGI_80MHZ           | \
+                                WMI_VHT_CAP_TX_STBC             | \
+                                WMI_VHT_CAP_RX_STBC_MASK        | \
+                                WMI_VHT_CAP_MAX_AMPDU_LEN_EXP   | \
+                                WMI_VHT_CAP_RX_FIXED_ANT        | \
+                                WMI_VHT_CAP_TX_FIXED_ANT)
+
+/*
+ * Interested readers refer to Rx/Tx MCS Map definition as defined in
+ * 802.11ac
+ */
+#define WMI_VHT_MAX_MCS_4_SS_MASK(r, ss)      ((3 & (r)) << (((ss) - 1) << 1))
+#define WMI_VHT_MAX_SUPP_RATE_MASK           0x1fff0000
+#define WMI_VHT_MAX_SUPP_RATE_MASK_SHIFT     16
+
+enum {
+       REGDMN_MODE_11A              = 0x00001, /* 11a channels */
+       REGDMN_MODE_TURBO            = 0x00002, /* 11a turbo-only channels */
+       REGDMN_MODE_11B              = 0x00004, /* 11b channels */
+       REGDMN_MODE_PUREG            = 0x00008, /* 11g channels (OFDM only) */
+       REGDMN_MODE_11G              = 0x00008, /* XXX historical */
+       REGDMN_MODE_108G             = 0x00020, /* 11a+Turbo channels */
+       REGDMN_MODE_108A             = 0x00040, /* 11g+Turbo channels */
+       REGDMN_MODE_XR               = 0x00100, /* XR channels */
+       REGDMN_MODE_11A_HALF_RATE    = 0x00200, /* 11A half rate channels */
+       REGDMN_MODE_11A_QUARTER_RATE = 0x00400, /* 11A quarter rate channels */
+       REGDMN_MODE_11NG_HT20        = 0x00800, /* 11N-G HT20 channels */
+       REGDMN_MODE_11NA_HT20        = 0x01000, /* 11N-A HT20 channels */
+       REGDMN_MODE_11NG_HT40PLUS    = 0x02000, /* 11N-G HT40 + channels */
+       REGDMN_MODE_11NG_HT40MINUS   = 0x04000, /* 11N-G HT40 - channels */
+       REGDMN_MODE_11NA_HT40PLUS    = 0x08000, /* 11N-A HT40 + channels */
+       REGDMN_MODE_11NA_HT40MINUS   = 0x10000, /* 11N-A HT40 - channels */
+       REGDMN_MODE_11AC_VHT20       = 0x20000, /* 5Ghz, VHT20 */
+       REGDMN_MODE_11AC_VHT40PLUS   = 0x40000, /* 5Ghz, VHT40 + channels */
+       REGDMN_MODE_11AC_VHT40MINUS  = 0x80000, /* 5Ghz  VHT40 - channels */
+       REGDMN_MODE_11AC_VHT80       = 0x100000, /* 5Ghz, VHT80 channels */
+       REGDMN_MODE_ALL              = 0xffffffff
+};
+
+#define REGDMN_CAP1_CHAN_HALF_RATE        0x00000001
+#define REGDMN_CAP1_CHAN_QUARTER_RATE     0x00000002
+#define REGDMN_CAP1_CHAN_HAL49GHZ         0x00000004
+
+/* regulatory capabilities */
+#define REGDMN_EEPROM_EEREGCAP_EN_FCC_MIDBAND   0x0040
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_U1_EVEN    0x0080
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_U2         0x0100
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_MIDBAND    0x0200
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_U1_ODD     0x0400
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_NEW_11A    0x0800
+
+struct hal_reg_capabilities {
+       /* regdomain value specified in EEPROM */
+       __le32 eeprom_rd;
+       /*regdomain */
+       __le32 eeprom_rd_ext;
+       /* CAP1 capabilities bit map. */
+       __le32 regcap1;
+       /* REGDMN EEPROM CAP. */
+       __le32 regcap2;
+       /* REGDMN MODE */
+       __le32 wireless_modes;
+       __le32 low_2ghz_chan;
+       __le32 high_2ghz_chan;
+       __le32 low_5ghz_chan;
+       __le32 high_5ghz_chan;
+} __packed;
+
+enum wlan_mode_capability {
+       WHAL_WLAN_11A_CAPABILITY   = 0x1,
+       WHAL_WLAN_11G_CAPABILITY   = 0x2,
+       WHAL_WLAN_11AG_CAPABILITY  = 0x3,
+};
+
+/* structure used by FW for requesting host memory */
+struct wlan_host_mem_req {
+       /* ID of the request */
+       __le32 req_id;
+       /* size of the  of each unit */
+       __le32 unit_size;
+       /* flags to  indicate that
+        * the number units is dependent
+        * on number of resources(num vdevs num peers .. etc)
+        */
+       __le32 num_unit_info;
+       /*
+        * actual number of units to allocate . if flags in the num_unit_info
+        * indicate that number of units is tied to number of a particular
+        * resource to allocate then  num_units filed is set to 0 and host
+        * will derive the number units from number of the resources it is
+        * requesting.
+        */
+       __le32 num_units;
+} __packed;
+
+#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \
+       ((((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
+       (1 << ((svc_id)%(sizeof(u32))))) != 0)
+
+/*
+ * The following struct holds optional payload for
+ * wmi_service_ready_event,e.g., 11ac pass some of the
+ * device capability to the host.
+ */
+struct wmi_service_ready_event {
+       __le32 sw_version;
+       __le32 sw_version_1;
+       __le32 abi_version;
+       /* WMI_PHY_CAPABILITY */
+       __le32 phy_capability;
+       /* Maximum number of frag table entries that SW will populate less 1 */
+       __le32 max_frag_entry;
+       __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
+       __le32 num_rf_chains;
+       /*
+        * The following field is only valid for service type
+        * WMI_SERVICE_11AC
+        */
+       __le32 ht_cap_info; /* WMI HT Capability */
+       __le32 vht_cap_info; /* VHT capability info field of 802.11ac */
+       __le32 vht_supp_mcs; /* VHT Supported MCS Set field Rx/Tx same */
+       __le32 hw_min_tx_power;
+       __le32 hw_max_tx_power;
+       struct hal_reg_capabilities hal_reg_capabilities;
+       __le32 sys_cap_info;
+       __le32 min_pkt_size_enable; /* Enterprise mode short pkt enable */
+       /*
+        * Max beacon and Probe Response IE offload size
+        * (includes optional P2P IEs)
+        */
+       __le32 max_bcn_ie_size;
+       /*
+        * request to host to allocate a chuck of memory and pss it down to FW
+        * via WM_INIT. FW uses this as FW extesnsion memory for saving its
+        * data structures. Only valid for low latency interfaces like PCIE
+        * where FW can access this memory directly (or) by DMA.
+        */
+       __le32 num_mem_reqs;
+       struct wlan_host_mem_req mem_reqs[1];
+} __packed;
+
+/*
+ * status consists of  upper 16 bits fo int status and lower 16 bits of
+ * module ID that retuned status
+ */
+#define WLAN_INIT_STATUS_SUCCESS   0x0
+#define WLAN_GET_INIT_STATUS_REASON(status)    ((status) & 0xffff)
+#define WLAN_GET_INIT_STATUS_MODULE_ID(status) (((status) >> 16) & 0xffff)
+
+#define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ)
+#define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ)
+
+struct wmi_ready_event {
+       __le32 sw_version;
+       __le32 abi_version;
+       struct wmi_mac_addr mac_addr;
+       __le32 status;
+} __packed;
+
+struct wmi_resource_config {
+       /* number of virtual devices (VAPs) to support */
+       __le32 num_vdevs;
+
+       /* number of peer nodes to support */
+       __le32 num_peers;
+
+       /*
+        * In offload mode target supports features like WOW, chatter and
+        * other protocol offloads. In order to support them some
+        * functionalities like reorder buffering, PN checking need to be
+        * done in target. This determines maximum number of peers suported
+        * by target in offload mode
+        */
+       __le32 num_offload_peers;
+
+       /* For target-based RX reordering */
+       __le32 num_offload_reorder_bufs;
+
+       /* number of keys per peer */
+       __le32 num_peer_keys;
+
+       /* total number of TX/RX data TIDs */
+       __le32 num_tids;
+
+       /*
+        * max skid for resolving hash collisions
+        *
+        *   The address search table is sparse, so that if two MAC addresses
+        *   result in the same hash value, the second of these conflicting
+        *   entries can slide to the next index in the address search table,
+        *   and use it, if it is unoccupied.  This ast_skid_limit parameter
+        *   specifies the upper bound on how many subsequent indices to search
+        *   over to find an unoccupied space.
+        */
+       __le32 ast_skid_limit;
+
+       /*
+        * the nominal chain mask for transmit
+        *
+        *   The chain mask may be modified dynamically, e.g. to operate AP
+        *   tx with a reduced number of chains if no clients are associated.
+        *   This configuration parameter specifies the nominal chain-mask that
+        *   should be used when not operating with a reduced set of tx chains.
+        */
+       __le32 tx_chain_mask;
+
+       /*
+        * the nominal chain mask for receive
+        *
+        *   The chain mask may be modified dynamically, e.g. for a client
+        *   to use a reduced number of chains for receive if the traffic to
+        *   the client is low enough that it doesn't require downlink MIMO
+        *   or antenna diversity.
+        *   This configuration parameter specifies the nominal chain-mask that
+        *   should be used when not operating with a reduced set of rx chains.
+        */
+       __le32 rx_chain_mask;
+
+       /*
+        * what rx reorder timeout (ms) to use for the AC
+        *
+        *   Each WMM access class (voice, video, best-effort, background) will
+        *   have its own timeout value to dictate how long to wait for missing
+        *   rx MPDUs to arrive before flushing subsequent MPDUs that have
+        *   already been received.
+        *   This parameter specifies the timeout in milliseconds for each
+        *   class.
+        */
+       __le32 rx_timeout_pri_vi;
+       __le32 rx_timeout_pri_vo;
+       __le32 rx_timeout_pri_be;
+       __le32 rx_timeout_pri_bk;
+
+       /*
+        * what mode the rx should decap packets to
+        *
+        *   MAC can decap to RAW (no decap), native wifi or Ethernet types
+        *   THis setting also determines the default TX behavior, however TX
+        *   behavior can be modified on a per VAP basis during VAP init
+        */
+       __le32 rx_decap_mode;
+
+       /* what is the maximum scan requests than can be queued */
+       __le32 scan_max_pending_reqs;
+
+       /* maximum VDEV that could use BMISS offload */
+       __le32 bmiss_offload_max_vdev;
+
+       /* maximum VDEV that could use offload roaming */
+       __le32 roam_offload_max_vdev;
+
+       /* maximum AP profiles that would push to offload roaming */
+       __le32 roam_offload_max_ap_profiles;
+
+       /*
+        * how many groups to use for mcast->ucast conversion
+        *
+        *   The target's WAL maintains a table to hold information regarding
+        *   which peers belong to a given multicast group, so that if
+        *   multicast->unicast conversion is enabled, the target can convert
+        *   multicast tx frames to a series of unicast tx frames, to each
+        *   peer within the multicast group.
+            This num_mcast_groups configuration parameter tells the target how
+        *   many multicast groups to provide storage for within its multicast
+        *   group membership table.
+        */
+       __le32 num_mcast_groups;
+
+       /*
+        * size to alloc for the mcast membership table
+        *
+        *   This num_mcast_table_elems configuration parameter tells the
+        *   target how many peer elements it needs to provide storage for in
+        *   its multicast group membership table.
+        *   These multicast group membership table elements are shared by the
+        *   multicast groups stored within the table.
+        */
+       __le32 num_mcast_table_elems;
+
+       /*
+        * whether/how to do multicast->unicast conversion
+        *
+        *   This configuration parameter specifies whether the target should
+        *   perform multicast --> unicast conversion on transmit, and if so,
+        *   what to do if it finds no entries in its multicast group
+        *   membership table for the multicast IP address in the tx frame.
+        *   Configuration value:
+        *   0 -> Do not perform multicast to unicast conversion.
+        *   1 -> Convert multicast frames to unicast, if the IP multicast
+        *        address from the tx frame is found in the multicast group
+        *        membership table.  If the IP multicast address is not found,
+        *        drop the frame.
+        *   2 -> Convert multicast frames to unicast, if the IP multicast
+        *        address from the tx frame is found in the multicast group
+        *        membership table.  If the IP multicast address is not found,
+        *        transmit the frame as multicast.
+        */
+       __le32 mcast2ucast_mode;
+
+       /*
+        * how much memory to allocate for a tx PPDU dbg log
+        *
+        *   This parameter controls how much memory the target will allocate
+        *   to store a log of tx PPDU meta-information (how large the PPDU
+        *   was, when it was sent, whether it was successful, etc.)
+        */
+       __le32 tx_dbg_log_size;
+
+       /* how many AST entries to be allocated for WDS */
+       __le32 num_wds_entries;
+
+       /*
+        * MAC DMA burst size, e.g., For target PCI limit can be
+        * 0 -default, 1 256B
+        */
+       __le32 dma_burst_size;
+
+       /*
+        * Fixed delimiters to be inserted after every MPDU to
+        * account for interface latency to avoid underrun.
+        */
+       __le32 mac_aggr_delim;
+
+       /*
+        *   determine whether target is responsible for detecting duplicate
+        *   non-aggregate MPDU and timing out stale fragments.
+        *
+        *   A-MPDU reordering is always performed on the target.
+        *
+        *   0: target responsible for frag timeout and dup checking
+        *   1: host responsible for frag timeout and dup checking
+        */
+       __le32 rx_skip_defrag_timeout_dup_detection_check;
+
+       /*
+        * Configuration for VoW :
+        * No of Video Nodes to be supported
+        * and Max no of descriptors for each Video link (node).
+        */
+       __le32 vow_config;
+
+       /* maximum VDEV that could use GTK offload */
+       __le32 gtk_offload_max_vdev;
+
+       /* Number of msdu descriptors target should use */
+       __le32 num_msdu_desc;
+
+       /*
+        * Max. number of Tx fragments per MSDU
+        *  This parameter controls the max number of Tx fragments per MSDU.
+        *  This is sent by the target as part of the WMI_SERVICE_READY event
+        *  and is overriden by the OS shim as required.
+        */
+       __le32 max_frag_entries;
+} __packed;
+
+/* strucutre describing host memory chunk. */
+struct host_memory_chunk {
+       /* id of the request that is passed up in service ready */
+       __le32 req_id;
+       /* the physical address the memory chunk */
+       __le32 ptr;
+       /* size of the chunk */
+       __le32 size;
+} __packed;
+
+struct wmi_init_cmd {
+       struct wmi_resource_config resource_config;
+       __le32 num_host_mem_chunks;
+
+       /*
+        * variable number of host memory chunks.
+        * This should be the last element in the structure
+        */
+       struct host_memory_chunk host_mem_chunks[1];
+} __packed;
+
+/* TLV for channel list */
+struct wmi_chan_list {
+       __le32 tag; /* WMI_CHAN_LIST_TAG */
+       __le32 num_chan;
+       __le32 channel_list[0];
+} __packed;
+
+struct wmi_bssid_list {
+       __le32 tag; /* WMI_BSSID_LIST_TAG */
+       __le32 num_bssid;
+       struct wmi_mac_addr bssid_list[0];
+} __packed;
+
+struct wmi_ie_data {
+       __le32 tag; /* WMI_IE_TAG */
+       __le32 ie_len;
+       u8 ie_data[0];
+} __packed;
+
+struct wmi_ssid {
+       __le32 ssid_len;
+       u8 ssid[32];
+} __packed;
+
+struct wmi_ssid_list {
+       __le32 tag; /* WMI_SSID_LIST_TAG */
+       __le32 num_ssids;
+       struct wmi_ssid ssids[0];
+} __packed;
+
+/* prefix used by scan requestor ids on the host */
+#define WMI_HOST_SCAN_REQUESTOR_ID_PREFIX 0xA000
+
+/* prefix used by scan request ids generated on the host */
+/* host cycles through the lower 12 bits to generate ids */
+#define WMI_HOST_SCAN_REQ_ID_PREFIX 0xA000
+
+#define WLAN_SCAN_PARAMS_MAX_SSID    16
+#define WLAN_SCAN_PARAMS_MAX_BSSID   4
+#define WLAN_SCAN_PARAMS_MAX_IE_LEN  256
+
+/* Scan priority numbers must be sequential, starting with 0 */
+enum wmi_scan_priority {
+       WMI_SCAN_PRIORITY_VERY_LOW = 0,
+       WMI_SCAN_PRIORITY_LOW,
+       WMI_SCAN_PRIORITY_MEDIUM,
+       WMI_SCAN_PRIORITY_HIGH,
+       WMI_SCAN_PRIORITY_VERY_HIGH,
+       WMI_SCAN_PRIORITY_COUNT   /* number of priorities supported */
+};
+
+struct wmi_start_scan_cmd {
+       /* Scan ID */
+       __le32 scan_id;
+       /* Scan requestor ID */
+       __le32 scan_req_id;
+       /* VDEV id(interface) that is requesting scan */
+       __le32 vdev_id;
+       /* Scan Priority, input to scan scheduler */
+       __le32 scan_priority;
+       /* Scan events subscription */
+       __le32 notify_scan_events;
+       /* dwell time in msec on active channels */
+       __le32 dwell_time_active;
+       /* dwell time in msec on passive channels */
+       __le32 dwell_time_passive;
+       /*
+        * min time in msec on the BSS channel,only valid if atleast one
+        * VDEV is active
+        */
+       __le32 min_rest_time;
+       /*
+        * max rest time in msec on the BSS channel,only valid if at least
+        * one VDEV is active
+        */
+       /*
+        * the scanner will rest on the bss channel at least min_rest_time
+        * after min_rest_time the scanner will start checking for tx/rx
+        * activity on all VDEVs. if there is no activity the scanner will
+        * switch to off channel. if there is activity the scanner will let
+        * the radio on the bss channel until max_rest_time expires.at
+        * max_rest_time scanner will switch to off channel irrespective of
+        * activity. activity is determined by the idle_time parameter.
+        */
+       __le32 max_rest_time;
+       /*
+        * time before sending next set of probe requests.
+        * The scanner keeps repeating probe requests transmission with
+        * period specified by repeat_probe_time.
+        * The number of probe requests specified depends on the ssid_list
+        * and bssid_list
+        */
+       __le32 repeat_probe_time;
+       /* time in msec between 2 consequetive probe requests with in a set. */
+       __le32 probe_spacing_time;
+       /*
+        * data inactivity time in msec on bss channel that will be used by
+        * scanner for measuring the inactivity.
+        */
+       __le32 idle_time;
+       /* maximum time in msec allowed for scan  */
+       __le32 max_scan_time;
+       /*
+        * delay in msec before sending first probe request after switching
+        * to a channel
+        */
+       __le32 probe_delay;
+       /* Scan control flags */
+       __le32 scan_ctrl_flags;
+
+       /* Burst duration time in msecs */
+       __le32 burst_duration;
+       /*
+        * TLV (tag length value )  paramerters follow the scan_cmd structure.
+        * TLV can contain channel list, bssid list, ssid list and
+        * ie. the TLV tags are defined above;
+        */
+} __packed;
+
+struct wmi_ssid_arg {
+       int len;
+       const u8 *ssid;
+};
+
+struct wmi_bssid_arg {
+       const u8 *bssid;
+};
+
+struct wmi_start_scan_arg {
+       u32 scan_id;
+       u32 scan_req_id;
+       u32 vdev_id;
+       u32 scan_priority;
+       u32 notify_scan_events;
+       u32 dwell_time_active;
+       u32 dwell_time_passive;
+       u32 min_rest_time;
+       u32 max_rest_time;
+       u32 repeat_probe_time;
+       u32 probe_spacing_time;
+       u32 idle_time;
+       u32 max_scan_time;
+       u32 probe_delay;
+       u32 scan_ctrl_flags;
+
+       u32 ie_len;
+       u32 n_channels;
+       u32 n_ssids;
+       u32 n_bssids;
+
+       u8 ie[WLAN_SCAN_PARAMS_MAX_IE_LEN];
+       u32 channels[64];
+       struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID];
+       struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID];
+};
+
+/* scan control flags */
+
+/* passively scan all channels including active channels */
+#define WMI_SCAN_FLAG_PASSIVE        0x1
+/* add wild card ssid probe request even though ssid_list is specified. */
+#define WMI_SCAN_ADD_BCAST_PROBE_REQ 0x2
+/* add cck rates to rates/xrate ie for the generated probe request */
+#define WMI_SCAN_ADD_CCK_RATES 0x4
+/* add ofdm rates to rates/xrate ie for the generated probe request */
+#define WMI_SCAN_ADD_OFDM_RATES 0x8
+/* To enable indication of Chan load and Noise floor to host */
+#define WMI_SCAN_CHAN_STAT_EVENT 0x10
+/* Filter Probe request frames  */
+#define WMI_SCAN_FILTER_PROBE_REQ 0x20
+/* When set, DFS channels will not be scanned */
+#define WMI_SCAN_BYPASS_DFS_CHN 0x40
+/* Different FW scan engine may choose to bail out on errors.
+ * Allow the driver to have influence over that. */
+#define WMI_SCAN_CONTINUE_ON_ERROR 0x80
+
+/* WMI_SCAN_CLASS_MASK must be the same value as IEEE80211_SCAN_CLASS_MASK */
+#define WMI_SCAN_CLASS_MASK 0xFF000000
+
+
+enum wmi_stop_scan_type {
+       WMI_SCAN_STOP_ONE       = 0x00000000, /* stop by scan_id */
+       WMI_SCAN_STOP_VDEV_ALL  = 0x01000000, /* stop by vdev_id */
+       WMI_SCAN_STOP_ALL       = 0x04000000, /* stop all scans */
+};
+
+struct wmi_stop_scan_cmd {
+       __le32 scan_req_id;
+       __le32 scan_id;
+       __le32 req_type;
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_stop_scan_arg {
+       u32 req_id;
+       enum wmi_stop_scan_type req_type;
+       union {
+               u32 scan_id;
+               u32 vdev_id;
+       } u;
+};
+
+struct wmi_scan_chan_list_cmd {
+       __le32 num_scan_chans;
+       struct wmi_channel chan_info[0];
+} __packed;
+
+struct wmi_scan_chan_list_arg {
+       u32 n_channels;
+       struct wmi_channel_arg *channels;
+};
+
+enum wmi_bss_filter {
+       WMI_BSS_FILTER_NONE = 0,        /* no beacons forwarded */
+       WMI_BSS_FILTER_ALL,             /* all beacons forwarded */
+       WMI_BSS_FILTER_PROFILE,         /* only beacons matching profile */
+       WMI_BSS_FILTER_ALL_BUT_PROFILE, /* all but beacons matching profile */
+       WMI_BSS_FILTER_CURRENT_BSS,     /* only beacons matching current BSS */
+       WMI_BSS_FILTER_ALL_BUT_BSS,     /* all but beacons matching BSS */
+       WMI_BSS_FILTER_PROBED_SSID,     /* beacons matching probed ssid */
+       WMI_BSS_FILTER_LAST_BSS,        /* marker only */
+};
+
+enum wmi_scan_event_type {
+       WMI_SCAN_EVENT_STARTED         = 0x1,
+       WMI_SCAN_EVENT_COMPLETED       = 0x2,
+       WMI_SCAN_EVENT_BSS_CHANNEL     = 0x4,
+       WMI_SCAN_EVENT_FOREIGN_CHANNEL = 0x8,
+       WMI_SCAN_EVENT_DEQUEUED        = 0x10,
+       WMI_SCAN_EVENT_PREEMPTED       = 0x20, /* possibly by high-prio scan */
+       WMI_SCAN_EVENT_START_FAILED    = 0x40,
+       WMI_SCAN_EVENT_RESTARTED       = 0x80,
+       WMI_SCAN_EVENT_MAX             = 0x8000
+};
+
+enum wmi_scan_completion_reason {
+       WMI_SCAN_REASON_COMPLETED,
+       WMI_SCAN_REASON_CANCELLED,
+       WMI_SCAN_REASON_PREEMPTED,
+       WMI_SCAN_REASON_TIMEDOUT,
+       WMI_SCAN_REASON_MAX,
+};
+
+struct wmi_scan_event {
+       __le32 event_type; /* %WMI_SCAN_EVENT_ */
+       __le32 reason; /* %WMI_SCAN_REASON_ */
+       __le32 channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */
+       __le32 scan_req_id;
+       __le32 scan_id;
+       __le32 vdev_id;
+} __packed;
+
+/*
+ * This defines how much headroom is kept in the
+ * receive frame between the descriptor and the
+ * payload, in order for the WMI PHY error and
+ * management handler to insert header contents.
+ *
+ * This is in bytes.
+ */
+#define WMI_MGMT_RX_HDR_HEADROOM    52
+
+/*
+ * This event will be used for sending scan results
+ * as well as rx mgmt frames to the host. The rx buffer
+ * will be sent as part of this WMI event. It would be a
+ * good idea to pass all the fields in the RX status
+ * descriptor up to the host.
+ */
+struct wmi_mgmt_rx_hdr {
+       __le32 channel;
+       __le32 snr;
+       __le32 rate;
+       __le32 phy_mode;
+       __le32 buf_len;
+       __le32 status; /* %WMI_RX_STATUS_ */
+} __packed;
+
+struct wmi_mgmt_rx_event {
+       struct wmi_mgmt_rx_hdr hdr;
+       u8 buf[0];
+} __packed;
+
+#define WMI_RX_STATUS_OK                       0x00
+#define WMI_RX_STATUS_ERR_CRC                  0x01
+#define WMI_RX_STATUS_ERR_DECRYPT              0x08
+#define WMI_RX_STATUS_ERR_MIC                  0x10
+#define WMI_RX_STATUS_ERR_KEY_CACHE_MISS       0x20
+
+struct wmi_single_phyerr_rx_hdr {
+       /* TSF timestamp */
+       __le32 tsf_timestamp;
+
+       /*
+        * Current freq1, freq2
+        *
+        * [7:0]:    freq1[lo]
+        * [15:8] :   freq1[hi]
+        * [23:16]:   freq2[lo]
+        * [31:24]:   freq2[hi]
+        */
+       __le16 freq1;
+       __le16 freq2;
+
+       /*
+        * Combined RSSI over all chains and channel width for this PHY error
+        *
+        * [7:0]: RSSI combined
+        * [15:8]: Channel width (MHz)
+        * [23:16]: PHY error code
+        * [24:16]: reserved (future use)
+        */
+       u8 rssi_combined;
+       u8 chan_width_mhz;
+       u8 phy_err_code;
+       u8 rsvd0;
+
+       /*
+        * RSSI on chain 0 through 3
+        *
+        * This is formatted the same as the PPDU_START RX descriptor
+        * field:
+        *
+        * [7:0]:   pri20
+        * [15:8]:  sec20
+        * [23:16]: sec40
+        * [31:24]: sec80
+        */
+
+       __le32 rssi_chain0;
+       __le32 rssi_chain1;
+       __le32 rssi_chain2;
+       __le32 rssi_chain3;
+
+       /*
+        * Last calibrated NF value for chain 0 through 3
+        *
+        * nf_list_1:
+        *
+        * + [15:0] - chain 0
+        * + [31:16] - chain 1
+        *
+        * nf_list_2:
+        *
+        * + [15:0] - chain 2
+        * + [31:16] - chain 3
+        */
+       __le32 nf_list_1;
+       __le32 nf_list_2;
+
+
+       /* Length of the frame */
+       __le32 buf_len;
+} __packed;
+
+struct wmi_single_phyerr_rx_event {
+       /* Phy error event header */
+       struct wmi_single_phyerr_rx_hdr hdr;
+       /* frame buffer */
+       u8 bufp[0];
+} __packed;
+
+struct wmi_comb_phyerr_rx_hdr {
+       /* Phy error phy error count */
+       __le32 num_phyerr_events;
+       __le32 tsf_l32;
+       __le32 tsf_u32;
+} __packed;
+
+struct wmi_comb_phyerr_rx_event {
+       /* Phy error phy error count */
+       struct wmi_comb_phyerr_rx_hdr hdr;
+       /*
+        * frame buffer - contains multiple payloads in the order:
+        *                    header - payload, header - payload...
+        *  (The header is of type: wmi_single_phyerr_rx_hdr)
+        */
+       u8 bufp[0];
+} __packed;
+
+struct wmi_mgmt_tx_hdr {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 tx_rate;
+       __le32 tx_power;
+       __le32 buf_len;
+} __packed;
+
+struct wmi_mgmt_tx_cmd {
+       struct wmi_mgmt_tx_hdr hdr;
+       u8 buf[0];
+} __packed;
+
+struct wmi_echo_event {
+       __le32 value;
+} __packed;
+
+struct wmi_echo_cmd {
+       __le32 value;
+} __packed;
+
+
+struct wmi_pdev_set_regdomain_cmd {
+       __le32 reg_domain;
+       __le32 reg_domain_2G;
+       __le32 reg_domain_5G;
+       __le32 conformance_test_limit_2G;
+       __le32 conformance_test_limit_5G;
+} __packed;
+
+/* Command to set/unset chip in quiet mode */
+struct wmi_pdev_set_quiet_cmd {
+       /* period in TUs */
+       __le32 period;
+
+       /* duration in TUs */
+       __le32 duration;
+
+       /* offset in TUs */
+       __le32 next_start;
+
+       /* enable/disable */
+       __le32 enabled;
+} __packed;
+
+
+/*
+ * 802.11g protection mode.
+ */
+enum ath10k_protmode {
+       ATH10K_PROT_NONE     = 0,    /* no protection */
+       ATH10K_PROT_CTSONLY  = 1,    /* CTS to self */
+       ATH10K_PROT_RTSCTS   = 2,    /* RTS-CTS */
+};
+
+enum wmi_beacon_gen_mode {
+       WMI_BEACON_STAGGERED_MODE = 0,
+       WMI_BEACON_BURST_MODE = 1
+};
+
+enum wmi_csa_event_ies_present_flag {
+       WMI_CSA_IE_PRESENT = 0x00000001,
+       WMI_XCSA_IE_PRESENT = 0x00000002,
+       WMI_WBW_IE_PRESENT = 0x00000004,
+       WMI_CSWARP_IE_PRESENT = 0x00000008,
+};
+
+/* wmi CSA receive event from beacon frame */
+struct wmi_csa_event {
+       __le32 i_fc_dur;
+       /* Bit 0-15: FC */
+       /* Bit 16-31: DUR */
+       struct wmi_mac_addr i_addr1;
+       struct wmi_mac_addr i_addr2;
+       __le32 csa_ie[2];
+       __le32 xcsa_ie[2];
+       __le32 wb_ie[2];
+       __le32 cswarp_ie;
+       __le32 ies_present_flag; /* wmi_csa_event_ies_present_flag */
+} __packed;
+
+/* the definition of different PDEV parameters */
+#define PDEV_DEFAULT_STATS_UPDATE_PERIOD    500
+#define VDEV_DEFAULT_STATS_UPDATE_PERIOD    500
+#define PEER_DEFAULT_STATS_UPDATE_PERIOD    500
+
+enum wmi_pdev_param {
+       /* TX chian mask */
+       WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
+       /* RX chian mask */
+       WMI_PDEV_PARAM_RX_CHAIN_MASK,
+       /* TX power limit for 2G Radio */
+       WMI_PDEV_PARAM_TXPOWER_LIMIT2G,
+       /* TX power limit for 5G Radio */
+       WMI_PDEV_PARAM_TXPOWER_LIMIT5G,
+       /* TX power scale */
+       WMI_PDEV_PARAM_TXPOWER_SCALE,
+       /* Beacon generation mode . 0: host, 1: target   */
+       WMI_PDEV_PARAM_BEACON_GEN_MODE,
+       /* Beacon generation mode . 0: staggered 1: bursted   */
+       WMI_PDEV_PARAM_BEACON_TX_MODE,
+       /*
+        * Resource manager off chan mode .
+        * 0: turn off off chan mode. 1: turn on offchan mode
+        */
+       WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+       /*
+        * Protection mode:
+        * 0: no protection 1:use CTS-to-self 2: use RTS/CTS
+        */
+       WMI_PDEV_PARAM_PROTECTION_MODE,
+       /* Dynamic bandwidth 0: disable 1: enable */
+       WMI_PDEV_PARAM_DYNAMIC_BW,
+       /* Non aggregrate/ 11g sw retry threshold.0-disable */
+       WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+       /* aggregrate sw retry threshold. 0-disable*/
+       WMI_PDEV_PARAM_AGG_SW_RETRY_TH,
+       /* Station kickout threshold (non of consecutive failures).0-disable */
+       WMI_PDEV_PARAM_STA_KICKOUT_TH,
+       /* Aggerate size scaling configuration per AC */
+       WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+       /* LTR enable */
+       WMI_PDEV_PARAM_LTR_ENABLE,
+       /* LTR latency for BE, in us */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_BE,
+       /* LTR latency for BK, in us */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_BK,
+       /* LTR latency for VI, in us */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_VI,
+       /* LTR latency for VO, in us  */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_VO,
+       /* LTR AC latency timeout, in ms */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+       /* LTR platform latency override, in us */
+       WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+       /* LTR-RX override, in us */
+       WMI_PDEV_PARAM_LTR_RX_OVERRIDE,
+       /* Tx activity timeout for LTR, in us */
+       WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+       /* L1SS state machine enable */
+       WMI_PDEV_PARAM_L1SS_ENABLE,
+       /* Deep sleep state machine enable */
+       WMI_PDEV_PARAM_DSLEEP_ENABLE,
+       /* RX buffering flush enable */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH,
+       /* RX buffering matermark */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_WATERMARK,
+       /* RX buffering timeout enable */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+       /* RX buffering timeout value */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE,
+       /* pdev level stats update period in ms */
+       WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+       /* vdev level stats update period in ms */
+       WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+       /* peer level stats update period in ms */
+       WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+       /* beacon filter status update period */
+       WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+       /* QOS Mgmt frame protection MFP/PMF 0: disable, 1: enable */
+       WMI_PDEV_PARAM_PMF_QOS,
+       /* Access category on which ARP frames are sent */
+       WMI_PDEV_PARAM_ARP_AC_OVERRIDE,
+       /* DCS configuration */
+       WMI_PDEV_PARAM_DCS,
+       /* Enable/Disable ANI on target */
+       WMI_PDEV_PARAM_ANI_ENABLE,
+       /* configure the ANI polling period */
+       WMI_PDEV_PARAM_ANI_POLL_PERIOD,
+       /* configure the ANI listening period */
+       WMI_PDEV_PARAM_ANI_LISTEN_PERIOD,
+       /* configure OFDM immunity level */
+       WMI_PDEV_PARAM_ANI_OFDM_LEVEL,
+       /* configure CCK immunity level */
+       WMI_PDEV_PARAM_ANI_CCK_LEVEL,
+       /* Enable/Disable CDD for 1x1 STAs in rate control module */
+       WMI_PDEV_PARAM_DYNTXCHAIN,
+       /* Enable/Disable proxy STA */
+       WMI_PDEV_PARAM_PROXY_STA,
+       /* Enable/Disable low power state when all VDEVs are inactive/idle. */
+       WMI_PDEV_PARAM_IDLE_PS_CONFIG,
+       /* Enable/Disable power gating sleep */
+       WMI_PDEV_PARAM_POWER_GATING_SLEEP,
+};
+
+struct wmi_pdev_set_param_cmd {
+       __le32 param_id;
+       __le32 param_value;
+} __packed;
+
+struct wmi_pdev_get_tpc_config_cmd {
+       /* parameter   */
+       __le32 param;
+} __packed;
+
+#define WMI_TPC_RATE_MAX               160
+#define WMI_TPC_TX_N_CHAIN             4
+
+enum wmi_tpc_config_event_flag {
+       WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD     = 0x1,
+       WMI_TPC_CONFIG_EVENT_FLAG_TABLE_STBC    = 0x2,
+       WMI_TPC_CONFIG_EVENT_FLAG_TABLE_TXBF    = 0x4,
+};
+
+struct wmi_pdev_tpc_config_event {
+       __le32 reg_domain;
+       __le32 chan_freq;
+       __le32 phy_mode;
+       __le32 twice_antenna_reduction;
+       __le32 twice_max_rd_power;
+       s32 twice_antenna_gain;
+       __le32 power_limit;
+       __le32 rate_max;
+       __le32 num_tx_chain;
+       __le32 ctl;
+       __le32 flags;
+       s8 max_reg_allow_pow[WMI_TPC_TX_N_CHAIN];
+       s8 max_reg_allow_pow_agcdd[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+       s8 max_reg_allow_pow_agstbc[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+       s8 max_reg_allow_pow_agtxbf[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+       u8 rates_array[WMI_TPC_RATE_MAX];
+} __packed;
+
+/* Transmit power scale factor. */
+enum wmi_tp_scale {
+       WMI_TP_SCALE_MAX    = 0,        /* no scaling (default) */
+       WMI_TP_SCALE_50     = 1,        /* 50% of max (-3 dBm) */
+       WMI_TP_SCALE_25     = 2,        /* 25% of max (-6 dBm) */
+       WMI_TP_SCALE_12     = 3,        /* 12% of max (-9 dBm) */
+       WMI_TP_SCALE_MIN    = 4,        /* min, but still on   */
+       WMI_TP_SCALE_SIZE   = 5,        /* max num of enum     */
+};
+
+struct wmi_set_channel_cmd {
+       /* channel (only frequency and mode info are used) */
+       struct wmi_channel chan;
+} __packed;
+
+struct wmi_pdev_chanlist_update_event {
+       /* number of channels */
+       __le32 num_chan;
+       /* array of channels */
+       struct wmi_channel channel_list[1];
+} __packed;
+
+#define WMI_MAX_DEBUG_MESG (sizeof(u32) * 32)
+
+struct wmi_debug_mesg_event {
+       /* message buffer, NULL terminated */
+       char bufp[WMI_MAX_DEBUG_MESG];
+} __packed;
+
+enum {
+       /* P2P device */
+       VDEV_SUBTYPE_P2PDEV = 0,
+       /* P2P client */
+       VDEV_SUBTYPE_P2PCLI,
+       /* P2P GO */
+       VDEV_SUBTYPE_P2PGO,
+       /* BT3.0 HS */
+       VDEV_SUBTYPE_BT,
+};
+
+struct wmi_pdev_set_channel_cmd {
+       /* idnore power , only use flags , mode and freq */
+       struct wmi_channel chan;
+} __packed;
+
+/* Customize the DSCP (bit) to TID (0-7) mapping for QOS */
+#define WMI_DSCP_MAP_MAX    (64)
+struct wmi_pdev_set_dscp_tid_map_cmd {
+       /* map indicating DSCP to TID conversion */
+       __le32 dscp_to_tid_map[WMI_DSCP_MAP_MAX];
+} __packed;
+
+enum mcast_bcast_rate_id {
+       WMI_SET_MCAST_RATE,
+       WMI_SET_BCAST_RATE
+};
+
+struct mcast_bcast_rate {
+       enum mcast_bcast_rate_id rate_id;
+       __le32 rate;
+} __packed;
+
+struct wmi_wmm_params {
+       __le32 cwmin;
+       __le32 cwmax;
+       __le32 aifs;
+       __le32 txop;
+       __le32 acm;
+       __le32 no_ack;
+} __packed;
+
+struct wmi_pdev_set_wmm_params {
+       struct wmi_wmm_params ac_be;
+       struct wmi_wmm_params ac_bk;
+       struct wmi_wmm_params ac_vi;
+       struct wmi_wmm_params ac_vo;
+} __packed;
+
+struct wmi_wmm_params_arg {
+       u32 cwmin;
+       u32 cwmax;
+       u32 aifs;
+       u32 txop;
+       u32 acm;
+       u32 no_ack;
+};
+
+struct wmi_pdev_set_wmm_params_arg {
+       struct wmi_wmm_params_arg ac_be;
+       struct wmi_wmm_params_arg ac_bk;
+       struct wmi_wmm_params_arg ac_vi;
+       struct wmi_wmm_params_arg ac_vo;
+};
+
+struct wal_dbg_tx_stats {
+       /* Num HTT cookies queued to dispatch list */
+       __le32 comp_queued;
+
+       /* Num HTT cookies dispatched */
+       __le32 comp_delivered;
+
+       /* Num MSDU queued to WAL */
+       __le32 msdu_enqued;
+
+       /* Num MPDU queue to WAL */
+       __le32 mpdu_enqued;
+
+       /* Num MSDUs dropped by WMM limit */
+       __le32 wmm_drop;
+
+       /* Num Local frames queued */
+       __le32 local_enqued;
+
+       /* Num Local frames done */
+       __le32 local_freed;
+
+       /* Num queued to HW */
+       __le32 hw_queued;
+
+       /* Num PPDU reaped from HW */
+       __le32 hw_reaped;
+
+       /* Num underruns */
+       __le32 underrun;
+
+       /* Num PPDUs cleaned up in TX abort */
+       __le32 tx_abort;
+
+       /* Num MPDUs requed by SW */
+       __le32 mpdus_requed;
+
+       /* excessive retries */
+       __le32 tx_ko;
+
+       /* data hw rate code */
+       __le32 data_rc;
+
+       /* Scheduler self triggers */
+       __le32 self_triggers;
+
+       /* frames dropped due to excessive sw retries */
+       __le32 sw_retry_failure;
+
+       /* illegal rate phy errors  */
+       __le32 illgl_rate_phy_err;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_cont_xretry;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_tx_timeout;
+
+       /* wal pdev resets  */
+       __le32 pdev_resets;
+
+       __le32 phy_underrun;
+
+       /* MPDU is more than txop limit */
+       __le32 txop_ovf;
+} __packed;
+
+struct wal_dbg_rx_stats {
+       /* Cnts any change in ring routing mid-ppdu */
+       __le32 mid_ppdu_route_change;
+
+       /* Total number of statuses processed */
+       __le32 status_rcvd;
+
+       /* Extra frags on rings 0-3 */
+       __le32 r0_frags;
+       __le32 r1_frags;
+       __le32 r2_frags;
+       __le32 r3_frags;
+
+       /* MSDUs / MPDUs delivered to HTT */
+       __le32 htt_msdus;
+       __le32 htt_mpdus;
+
+       /* MSDUs / MPDUs delivered to local stack */
+       __le32 loc_msdus;
+       __le32 loc_mpdus;
+
+       /* AMSDUs that have more MSDUs than the status ring size */
+       __le32 oversize_amsdu;
+
+       /* Number of PHY errors */
+       __le32 phy_errs;
+
+       /* Number of PHY errors drops */
+       __le32 phy_err_drop;
+
+       /* Number of mpdu errors - FCS, MIC, ENC etc. */
+       __le32 mpdu_errs;
+} __packed;
+
+struct wal_dbg_peer_stats {
+       /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */
+       __le32 dummy;
+} __packed;
+
+struct wal_dbg_stats {
+       struct wal_dbg_tx_stats tx;
+       struct wal_dbg_rx_stats rx;
+       struct wal_dbg_peer_stats peer;
+} __packed;
+
+enum wmi_stats_id {
+       WMI_REQUEST_PEER_STAT   = 0x01,
+       WMI_REQUEST_AP_STAT     = 0x02
+};
+
+struct wmi_request_stats_cmd {
+       __le32 stats_id;
+
+       /*
+        * Space to add parameters like
+        * peer mac addr
+        */
+} __packed;
+
+/* Suspend option */
+enum {
+       /* suspend */
+       WMI_PDEV_SUSPEND,
+
+       /* suspend and disable all interrupts */
+       WMI_PDEV_SUSPEND_AND_DISABLE_INTR,
+};
+
+struct wmi_pdev_suspend_cmd {
+       /* suspend option sent to target */
+       __le32 suspend_opt;
+} __packed;
+
+struct wmi_stats_event {
+       __le32 stats_id; /* %WMI_REQUEST_ */
+       /*
+        * number of pdev stats event structures
+        * (wmi_pdev_stats) 0 or 1
+        */
+       __le32 num_pdev_stats;
+       /*
+        * number of vdev stats event structures
+        * (wmi_vdev_stats) 0 or max vdevs
+        */
+       __le32 num_vdev_stats;
+       /*
+        * number of peer stats event structures
+        * (wmi_peer_stats) 0 or max peers
+        */
+       __le32 num_peer_stats;
+       __le32 num_bcnflt_stats;
+       /*
+        * followed by
+        *   num_pdev_stats * size of(struct wmi_pdev_stats)
+        *   num_vdev_stats * size of(struct wmi_vdev_stats)
+        *   num_peer_stats * size of(struct wmi_peer_stats)
+        *
+        *  By having a zero sized array, the pointer to data area
+        *  becomes available without increasing the struct size
+        */
+       u8 data[0];
+} __packed;
+
+/*
+ * PDEV statistics
+ * TODO: add all PDEV stats here
+ */
+struct wmi_pdev_stats {
+       __le32 chan_nf;        /* Channel noise floor */
+       __le32 tx_frame_count; /* TX frame count */
+       __le32 rx_frame_count; /* RX frame count */
+       __le32 rx_clear_count; /* rx clear count */
+       __le32 cycle_count;    /* cycle count */
+       __le32 phy_err_count;  /* Phy error count */
+       __le32 chan_tx_pwr;    /* channel tx power */
+       struct wal_dbg_stats wal; /* WAL dbg stats */
+} __packed;
+
+/*
+ * VDEV statistics
+ * TODO: add all VDEV stats here
+ */
+struct wmi_vdev_stats {
+       __le32 vdev_id;
+} __packed;
+
+/*
+ * peer statistics.
+ * TODO: add more stats
+ */
+struct wmi_peer_stats {
+       struct wmi_mac_addr peer_macaddr;
+       __le32 peer_rssi;
+       __le32 peer_tx_rate;
+} __packed;
+
+struct wmi_vdev_create_cmd {
+       __le32 vdev_id;
+       __le32 vdev_type;
+       __le32 vdev_subtype;
+       struct wmi_mac_addr vdev_macaddr;
+} __packed;
+
+enum wmi_vdev_type {
+       WMI_VDEV_TYPE_AP      = 1,
+       WMI_VDEV_TYPE_STA     = 2,
+       WMI_VDEV_TYPE_IBSS    = 3,
+       WMI_VDEV_TYPE_MONITOR = 4,
+};
+
+enum wmi_vdev_subtype {
+       WMI_VDEV_SUBTYPE_NONE       = 0,
+       WMI_VDEV_SUBTYPE_P2P_DEVICE = 1,
+       WMI_VDEV_SUBTYPE_P2P_CLIENT = 2,
+       WMI_VDEV_SUBTYPE_P2P_GO     = 3,
+};
+
+/* values for vdev_subtype */
+
+/* values for vdev_start_request flags */
+/*
+ * Indicates that AP VDEV uses hidden ssid. only valid for
+ *  AP/GO */
+#define WMI_VDEV_START_HIDDEN_SSID  (1<<0)
+/*
+ * Indicates if robust management frame/management frame
+ *  protection is enabled. For GO/AP vdevs, it indicates that
+ *  it may support station/client associations with RMF enabled.
+ *  For STA/client vdevs, it indicates that sta will
+ *  associate with AP with RMF enabled. */
+#define WMI_VDEV_START_PMF_ENABLED  (1<<1)
+
+struct wmi_p2p_noa_descriptor {
+       __le32 type_count; /* 255: continuous schedule, 0: reserved */
+       __le32 duration;  /* Absent period duration in micro seconds */
+       __le32 interval;   /* Absent period interval in micro seconds */
+       __le32 start_time; /* 32 bit tsf time when in starts */
+} __packed;
+
+struct wmi_vdev_start_request_cmd {
+       /* WMI channel */
+       struct wmi_channel chan;
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* requestor id identifying the caller module */
+       __le32 requestor_id;
+       /* beacon interval from received beacon */
+       __le32 beacon_interval;
+       /* DTIM Period from the received beacon */
+       __le32 dtim_period;
+       /* Flags */
+       __le32 flags;
+       /* ssid field. Only valid for AP/GO/IBSS/BTAmp VDEV type. */
+       struct wmi_ssid ssid;
+       /* beacon/probe reponse xmit rate. Applicable for SoftAP. */
+       __le32 bcn_tx_rate;
+       /* beacon/probe reponse xmit power. Applicable for SoftAP. */
+       __le32 bcn_tx_power;
+       /* number of p2p NOA descriptor(s) from scan entry */
+       __le32 num_noa_descriptors;
+       /*
+        * Disable H/W ack. This used by WMI_VDEV_RESTART_REQUEST_CMDID.
+        * During CAC, Our HW shouldn't ack ditected frames
+        */
+       __le32 disable_hw_ack;
+       /* actual p2p NOA descriptor from scan entry */
+       struct wmi_p2p_noa_descriptor noa_descriptors[2];
+} __packed;
+
+struct wmi_vdev_restart_request_cmd {
+       struct wmi_vdev_start_request_cmd vdev_start_request_cmd;
+} __packed;
+
+struct wmi_vdev_start_request_arg {
+       u32 vdev_id;
+       struct wmi_channel_arg channel;
+       u32 bcn_intval;
+       u32 dtim_period;
+       u8 *ssid;
+       u32 ssid_len;
+       u32 bcn_tx_rate;
+       u32 bcn_tx_power;
+       bool disable_hw_ack;
+       bool hidden_ssid;
+       bool pmf_enabled;
+};
+
+struct wmi_vdev_delete_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_up_cmd {
+       __le32 vdev_id;
+       __le32 vdev_assoc_id;
+       struct wmi_mac_addr vdev_bssid;
+} __packed;
+
+struct wmi_vdev_stop_cmd {
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_down_cmd {
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_standby_response_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_resume_response_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_set_param_cmd {
+       __le32 vdev_id;
+       __le32 param_id;
+       __le32 param_value;
+} __packed;
+
+#define WMI_MAX_KEY_INDEX   3
+#define WMI_MAX_KEY_LEN     32
+
+#define WMI_KEY_PAIRWISE 0x00
+#define WMI_KEY_GROUP    0x01
+#define WMI_KEY_TX_USAGE 0x02 /* default tx key - static wep */
+
+struct wmi_key_seq_counter {
+       __le32 key_seq_counter_l;
+       __le32 key_seq_counter_h;
+} __packed;
+
+#define WMI_CIPHER_NONE     0x0 /* clear key */
+#define WMI_CIPHER_WEP      0x1
+#define WMI_CIPHER_TKIP     0x2
+#define WMI_CIPHER_AES_OCB  0x3
+#define WMI_CIPHER_AES_CCM  0x4
+#define WMI_CIPHER_WAPI     0x5
+#define WMI_CIPHER_CKIP     0x6
+#define WMI_CIPHER_AES_CMAC 0x7
+
+struct wmi_vdev_install_key_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 key_idx;
+       __le32 key_flags;
+       __le32 key_cipher; /* %WMI_CIPHER_ */
+       struct wmi_key_seq_counter key_rsc_counter;
+       struct wmi_key_seq_counter key_global_rsc_counter;
+       struct wmi_key_seq_counter key_tsc_counter;
+       u8 wpi_key_rsc_counter[16];
+       u8 wpi_key_tsc_counter[16];
+       __le32 key_len;
+       __le32 key_txmic_len;
+       __le32 key_rxmic_len;
+
+       /* contains key followed by tx mic followed by rx mic */
+       u8 key_data[0];
+} __packed;
+
+struct wmi_vdev_install_key_arg {
+       u32 vdev_id;
+       const u8 *macaddr;
+       u32 key_idx;
+       u32 key_flags;
+       u32 key_cipher;
+       u32 key_len;
+       u32 key_txmic_len;
+       u32 key_rxmic_len;
+       const void *key_data;
+};
+
+/* Preamble types to be used with VDEV fixed rate configuration */
+enum wmi_rate_preamble {
+       WMI_RATE_PREAMBLE_OFDM,
+       WMI_RATE_PREAMBLE_CCK,
+       WMI_RATE_PREAMBLE_HT,
+       WMI_RATE_PREAMBLE_VHT,
+};
+
+/* Value to disable fixed rate setting */
+#define WMI_FIXED_RATE_NONE    (0xff)
+
+/* the definition of different VDEV parameters */
+enum wmi_vdev_param {
+       /* RTS Threshold */
+       WMI_VDEV_PARAM_RTS_THRESHOLD = 0x1,
+       /* Fragmentation threshold */
+       WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+       /* beacon interval in TUs */
+       WMI_VDEV_PARAM_BEACON_INTERVAL,
+       /* Listen interval in TUs */
+       WMI_VDEV_PARAM_LISTEN_INTERVAL,
+       /* muticast rate in Mbps */
+       WMI_VDEV_PARAM_MULTICAST_RATE,
+       /* management frame rate in Mbps */
+       WMI_VDEV_PARAM_MGMT_TX_RATE,
+       /* slot time (long vs short) */
+       WMI_VDEV_PARAM_SLOT_TIME,
+       /* preamble (long vs short) */
+       WMI_VDEV_PARAM_PREAMBLE,
+       /* SWBA time (time before tbtt in msec) */
+       WMI_VDEV_PARAM_SWBA_TIME,
+       /* time period for updating VDEV stats */
+       WMI_VDEV_STATS_UPDATE_PERIOD,
+       /* age out time in msec for frames queued for station in power save */
+       WMI_VDEV_PWRSAVE_AGEOUT_TIME,
+       /*
+        * Host SWBA interval (time in msec before tbtt for SWBA event
+        * generation).
+        */
+       WMI_VDEV_HOST_SWBA_INTERVAL,
+       /* DTIM period (specified in units of num beacon intervals) */
+       WMI_VDEV_PARAM_DTIM_PERIOD,
+       /*
+        * scheduler air time limit for this VDEV. used by off chan
+        * scheduler.
+        */
+       WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+       /* enable/dsiable WDS for this VDEV  */
+       WMI_VDEV_PARAM_WDS,
+       /* ATIM Window */
+       WMI_VDEV_PARAM_ATIM_WINDOW,
+       /* BMISS max */
+       WMI_VDEV_PARAM_BMISS_COUNT_MAX,
+       /* BMISS first time */
+       WMI_VDEV_PARAM_BMISS_FIRST_BCNT,
+       /* BMISS final time */
+       WMI_VDEV_PARAM_BMISS_FINAL_BCNT,
+       /* WMM enables/disabled */
+       WMI_VDEV_PARAM_FEATURE_WMM,
+       /* Channel width */
+       WMI_VDEV_PARAM_CHWIDTH,
+       /* Channel Offset */
+       WMI_VDEV_PARAM_CHEXTOFFSET,
+       /* Disable HT Protection */
+       WMI_VDEV_PARAM_DISABLE_HTPROTECTION,
+       /* Quick STA Kickout */
+       WMI_VDEV_PARAM_STA_QUICKKICKOUT,
+       /* Rate to be used with Management frames */
+       WMI_VDEV_PARAM_MGMT_RATE,
+       /* Protection Mode */
+       WMI_VDEV_PARAM_PROTECTION_MODE,
+       /* Fixed rate setting */
+       WMI_VDEV_PARAM_FIXED_RATE,
+       /* Short GI Enable/Disable */
+       WMI_VDEV_PARAM_SGI,
+       /* Enable LDPC */
+       WMI_VDEV_PARAM_LDPC,
+       /* Enable Tx STBC */
+       WMI_VDEV_PARAM_TX_STBC,
+       /* Enable Rx STBC */
+       WMI_VDEV_PARAM_RX_STBC,
+       /* Intra BSS forwarding  */
+       WMI_VDEV_PARAM_INTRA_BSS_FWD,
+       /* Setting Default xmit key for Vdev */
+       WMI_VDEV_PARAM_DEF_KEYID,
+       /* NSS width */
+       WMI_VDEV_PARAM_NSS,
+       /* Set the custom rate for the broadcast data frames */
+       WMI_VDEV_PARAM_BCAST_DATA_RATE,
+       /* Set the custom rate (rate-code) for multicast data frames */
+       WMI_VDEV_PARAM_MCAST_DATA_RATE,
+       /* Tx multicast packet indicate Enable/Disable */
+       WMI_VDEV_PARAM_MCAST_INDICATE,
+       /* Tx DHCP packet indicate Enable/Disable */
+       WMI_VDEV_PARAM_DHCP_INDICATE,
+       /* Enable host inspection of Tx unicast packet to unknown destination */
+       WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+
+       /* The minimum amount of time AP begins to consider STA inactive */
+       WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+
+       /*
+        * An associated STA is considered inactive when there is no recent
+        * TX/RX activity and no downlink frames are buffered for it. Once a
+        * STA exceeds the maximum idle inactive time, the AP will send an
+        * 802.11 data-null as a keep alive to verify the STA is still
+        * associated. If the STA does ACK the data-null, or if the data-null
+        * is buffered and the STA does not retrieve it, the STA will be
+        * considered unresponsive
+        * (see WMI_VDEV_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS).
+        */
+       WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+
+       /*
+        * An associated STA is considered unresponsive if there is no recent
+        * TX/RX activity and downlink frames are buffered for it. Once a STA
+        * exceeds the maximum unresponsive time, the AP will send a
+        * WMI_STA_KICKOUT event to the host so the STA can be deleted. */
+       WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+
+       /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */
+       WMI_VDEV_PARAM_AP_ENABLE_NAWDS,
+       /* Enable/Disable RTS-CTS */
+       WMI_VDEV_PARAM_ENABLE_RTSCTS,
+       /* Enable TXBFee/er */
+       WMI_VDEV_PARAM_TXBF,
+
+       /* Set packet power save */
+       WMI_VDEV_PARAM_PACKET_POWERSAVE,
+
+       /*
+        * Drops un-encrypted packets if eceived in an encrypted connection
+        * otherwise forwards to host.
+        */
+       WMI_VDEV_PARAM_DROP_UNENCRY,
+
+       /*
+        * Set the encapsulation type for frames.
+        */
+       WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+};
+
+/* slot time long */
+#define WMI_VDEV_SLOT_TIME_LONG                0x1
+/* slot time short */
+#define WMI_VDEV_SLOT_TIME_SHORT       0x2
+/* preablbe long */
+#define WMI_VDEV_PREAMBLE_LONG         0x1
+/* preablbe short */
+#define WMI_VDEV_PREAMBLE_SHORT                0x2
+
+enum wmi_start_event_param {
+       WMI_VDEV_RESP_START_EVENT = 0,
+       WMI_VDEV_RESP_RESTART_EVENT,
+};
+
+struct wmi_vdev_start_response_event {
+       __le32 vdev_id;
+       __le32 req_id;
+       __le32 resp_type; /* %WMI_VDEV_RESP_ */
+       __le32 status;
+} __packed;
+
+struct wmi_vdev_standby_req_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_resume_req_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_stopped_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+/*
+ * common structure used for simple events
+ * (stopped, resume_req, standby response)
+ */
+struct wmi_vdev_simple_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+/* VDEV start response status codes */
+/* VDEV succesfully started */
+#define WMI_INIFIED_VDEV_START_RESPONSE_STATUS_SUCCESS 0x0
+
+/* requested VDEV not found */
+#define WMI_INIFIED_VDEV_START_RESPONSE_INVALID_VDEVID 0x1
+
+/* unsupported VDEV combination */
+#define WMI_INIFIED_VDEV_START_RESPONSE_NOT_SUPPORTED  0x2
+
+/* Beacon processing related command and event structures */
+struct wmi_bcn_tx_hdr {
+       __le32 vdev_id;
+       __le32 tx_rate;
+       __le32 tx_power;
+       __le32 bcn_len;
+} __packed;
+
+struct wmi_bcn_tx_cmd {
+       struct wmi_bcn_tx_hdr hdr;
+       u8 *bcn[0];
+} __packed;
+
+struct wmi_bcn_tx_arg {
+       u32 vdev_id;
+       u32 tx_rate;
+       u32 tx_power;
+       u32 bcn_len;
+       const void *bcn;
+};
+
+/* Beacon filter */
+#define WMI_BCN_FILTER_ALL   0 /* Filter all beacons */
+#define WMI_BCN_FILTER_NONE  1 /* Pass all beacons */
+#define WMI_BCN_FILTER_RSSI  2 /* Pass Beacons RSSI >= RSSI threshold */
+#define WMI_BCN_FILTER_BSSID 3 /* Pass Beacons with matching BSSID */
+#define WMI_BCN_FILTER_SSID  4 /* Pass Beacons with matching SSID */
+
+struct wmi_bcn_filter_rx_cmd {
+       /* Filter ID */
+       __le32 bcn_filter_id;
+       /* Filter type - wmi_bcn_filter */
+       __le32 bcn_filter;
+       /* Buffer len */
+       __le32 bcn_filter_len;
+       /* Filter info (threshold, BSSID, RSSI) */
+       u8 *bcn_filter_buf;
+} __packed;
+
+/* Capabilities and IEs to be passed to firmware */
+struct wmi_bcn_prb_info {
+       /* Capabilities */
+       __le32 caps;
+       /* ERP info */
+       __le32 erp;
+       /* Advanced capabilities */
+       /* HT capabilities */
+       /* HT Info */
+       /* ibss_dfs */
+       /* wpa Info */
+       /* rsn Info */
+       /* rrm info */
+       /* ath_ext */
+       /* app IE */
+} __packed;
+
+struct wmi_bcn_tmpl_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* TIM IE offset from the beginning of the template. */
+       __le32 tim_ie_offset;
+       /* beacon probe capabilities and IEs */
+       struct wmi_bcn_prb_info bcn_prb_info;
+       /* beacon buffer length */
+       __le32 buf_len;
+       /* variable length data */
+       u8 data[1];
+} __packed;
+
+struct wmi_prb_tmpl_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* beacon probe capabilities and IEs */
+       struct wmi_bcn_prb_info bcn_prb_info;
+       /* beacon buffer length */
+       __le32 buf_len;
+       /* Variable length data */
+       u8 data[1];
+} __packed;
+
+enum wmi_sta_ps_mode {
+       /* enable power save for the given STA VDEV */
+       WMI_STA_PS_MODE_DISABLED = 0,
+       /* disable power save  for a given STA VDEV */
+       WMI_STA_PS_MODE_ENABLED = 1,
+};
+
+struct wmi_sta_powersave_mode_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+
+       /*
+        * Power save mode
+        * (see enum wmi_sta_ps_mode)
+        */
+       __le32 sta_ps_mode;
+} __packed;
+
+enum wmi_csa_offload_en {
+       WMI_CSA_OFFLOAD_DISABLE = 0,
+       WMI_CSA_OFFLOAD_ENABLE = 1,
+};
+
+struct wmi_csa_offload_enable_cmd {
+       __le32 vdev_id;
+       __le32 csa_offload_enable;
+} __packed;
+
+struct wmi_csa_offload_chanswitch_cmd {
+       __le32 vdev_id;
+       struct wmi_channel chan;
+} __packed;
+
+/*
+ * This parameter controls the policy for retrieving frames from AP while the
+ * STA is in sleep state.
+ *
+ * Only takes affect if the sta_ps_mode is enabled
+ */
+enum wmi_sta_ps_param_rx_wake_policy {
+       /*
+        * Wake up when ever there is an  RX activity on the VDEV. In this mode
+        * the Power save SM(state machine) will come out of sleep by either
+        * sending null frame (or) a data frame (with PS==0) in response to TIM
+        * bit set in the received beacon frame from AP.
+        */
+       WMI_STA_PS_RX_WAKE_POLICY_WAKE = 0,
+
+       /*
+        * Here the power save state machine will not wakeup in response to TIM
+        * bit, instead it will send a PSPOLL (or) UASPD trigger based on UAPSD
+        * configuration setup by WMISET_PS_SET_UAPSD  WMI command.  When all
+        * access categories are delivery-enabled, the station will send a
+        * UAPSD trigger frame, otherwise it will send a PS-Poll.
+        */
+       WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD = 1,
+};
+
+/*
+ * Number of tx frames/beacon  that cause the power save SM to wake up.
+ *
+ * Value 1 causes the SM to wake up for every TX. Value 0 has a special
+ * meaning, It will cause the SM to never wake up. This is useful if you want
+ * to keep the system to sleep all the time for some kind of test mode . host
+ * can change this parameter any time.  It will affect at the next tx frame.
+ */
+enum wmi_sta_ps_param_tx_wake_threshold {
+       WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER = 0,
+       WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS = 1,
+
+       /*
+        * Values greater than one indicate that many TX attempts per beacon
+        * interval before the STA will wake up
+        */
+};
+
+/*
+ * The maximum number of PS-Poll frames the FW will send in response to
+ * traffic advertised in TIM before waking up (by sending a null frame with PS
+ * = 0). Value 0 has a special meaning: there is no maximum count and the FW
+ * will send as many PS-Poll as are necessary to retrieve buffered BU. This
+ * parameter is used when the RX wake policy is
+ * WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD and ignored when the RX wake
+ * policy is WMI_STA_PS_RX_WAKE_POLICY_WAKE.
+ */
+enum wmi_sta_ps_param_pspoll_count {
+       WMI_STA_PS_PSPOLL_COUNT_NO_MAX = 0,
+       /*
+        * Values greater than 0 indicate the maximum numer of PS-Poll frames
+        * FW will send before waking up.
+        */
+};
+
+/*
+ * This will include the delivery and trigger enabled state for every AC.
+ * This is the negotiated state with AP. The host MLME needs to set this based
+ * on AP capability and the state Set in the association request by the
+ * station MLME.Lower 8 bits of the value specify the UAPSD configuration.
+ */
+#define WMI_UAPSD_AC_TYPE_DELI 0
+#define WMI_UAPSD_AC_TYPE_TRIG 1
+
+#define WMI_UAPSD_AC_BIT_MASK(ac, type) \
+       ((type ==  WMI_UAPSD_AC_TYPE_DELI) ? (1<<(ac<<1)) : (1<<((ac<<1)+1)))
+
+enum wmi_sta_ps_param_uapsd {
+       WMI_STA_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0),
+       WMI_STA_PS_UAPSD_AC0_TRIGGER_EN  = (1 << 1),
+       WMI_STA_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2),
+       WMI_STA_PS_UAPSD_AC1_TRIGGER_EN  = (1 << 3),
+       WMI_STA_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4),
+       WMI_STA_PS_UAPSD_AC2_TRIGGER_EN  = (1 << 5),
+       WMI_STA_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6),
+       WMI_STA_PS_UAPSD_AC3_TRIGGER_EN  = (1 << 7),
+};
+
+enum wmi_sta_powersave_param {
+       /*
+        * Controls how frames are retrievd from AP while STA is sleeping
+        *
+        * (see enum wmi_sta_ps_param_rx_wake_policy)
+        */
+       WMI_STA_PS_PARAM_RX_WAKE_POLICY = 0,
+
+       /*
+        * The STA will go active after this many TX
+        *
+        * (see enum wmi_sta_ps_param_tx_wake_threshold)
+        */
+       WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD = 1,
+
+       /*
+        * Number of PS-Poll to send before STA wakes up
+        *
+        * (see enum wmi_sta_ps_param_pspoll_count)
+        *
+        */
+       WMI_STA_PS_PARAM_PSPOLL_COUNT = 2,
+
+       /*
+        * TX/RX inactivity time in msec before going to sleep.
+        *
+        * The power save SM will monitor tx/rx activity on the VDEV, if no
+        * activity for the specified msec of the parameter the Power save
+        * SM will go to sleep.
+        */
+       WMI_STA_PS_PARAM_INACTIVITY_TIME = 3,
+
+       /*
+        * Set uapsd configuration.
+        *
+        * (see enum wmi_sta_ps_param_uapsd)
+        */
+       WMI_STA_PS_PARAM_UAPSD = 4,
+};
+
+struct wmi_sta_powersave_param_cmd {
+       __le32 vdev_id;
+       __le32 param_id; /* %WMI_STA_PS_PARAM_ */
+       __le32 param_value;
+} __packed;
+
+/* No MIMO power save */
+#define WMI_STA_MIMO_PS_MODE_DISABLE
+/* mimo powersave mode static*/
+#define WMI_STA_MIMO_PS_MODE_STATIC
+/* mimo powersave mode dynamic */
+#define WMI_STA_MIMO_PS_MODE_DYNAMIC
+
+struct wmi_sta_mimo_ps_mode_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* mimo powersave mode as defined above */
+       __le32 mimo_pwrsave_mode;
+} __packed;
+
+/* U-APSD configuration of peer station from (re)assoc request and TSPECs */
+enum wmi_ap_ps_param_uapsd {
+       WMI_AP_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0),
+       WMI_AP_PS_UAPSD_AC0_TRIGGER_EN  = (1 << 1),
+       WMI_AP_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2),
+       WMI_AP_PS_UAPSD_AC1_TRIGGER_EN  = (1 << 3),
+       WMI_AP_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4),
+       WMI_AP_PS_UAPSD_AC2_TRIGGER_EN  = (1 << 5),
+       WMI_AP_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6),
+       WMI_AP_PS_UAPSD_AC3_TRIGGER_EN  = (1 << 7),
+};
+
+/* U-APSD maximum service period of peer station */
+enum wmi_ap_ps_peer_param_max_sp {
+       WMI_AP_PS_PEER_PARAM_MAX_SP_UNLIMITED = 0,
+       WMI_AP_PS_PEER_PARAM_MAX_SP_2 = 1,
+       WMI_AP_PS_PEER_PARAM_MAX_SP_4 = 2,
+       WMI_AP_PS_PEER_PARAM_MAX_SP_6 = 3,
+       MAX_WMI_AP_PS_PEER_PARAM_MAX_SP,
+};
+
+/*
+ * AP power save parameter
+ * Set a power save specific parameter for a peer station
+ */
+enum wmi_ap_ps_peer_param {
+       /* Set uapsd configuration for a given peer.
+        *
+        * Include the delivery and trigger enabled state for every AC.
+        * The host  MLME needs to set this based on AP capability and stations
+        * request Set in the association request  received from the station.
+        *
+        * Lower 8 bits of the value specify the UAPSD configuration.
+        *
+        * (see enum wmi_ap_ps_param_uapsd)
+        * The default value is 0.
+        */
+       WMI_AP_PS_PEER_PARAM_UAPSD = 0,
+
+       /*
+        * Set the service period for a UAPSD capable station
+        *
+        * The service period from wme ie in the (re)assoc request frame.
+        *
+        * (see enum wmi_ap_ps_peer_param_max_sp)
+        */
+       WMI_AP_PS_PEER_PARAM_MAX_SP = 1,
+
+       /* Time in seconds for aging out buffered frames for STA in PS */
+       WMI_AP_PS_PEER_PARAM_AGEOUT_TIME = 2,
+};
+
+struct wmi_ap_ps_peer_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+
+       /* AP powersave param (see enum wmi_ap_ps_peer_param) */
+       __le32 param_id;
+
+       /* AP powersave param value */
+       __le32 param_value;
+} __packed;
+
+/* 128 clients = 4 words */
+#define WMI_TIM_BITMAP_ARRAY_SIZE 4
+
+struct wmi_tim_info {
+       __le32 tim_len;
+       __le32 tim_mcast;
+       __le32 tim_bitmap[WMI_TIM_BITMAP_ARRAY_SIZE];
+       __le32 tim_changed;
+       __le32 tim_num_ps_pending;
+} __packed;
+
+/* Maximum number of NOA Descriptors supported */
+#define WMI_P2P_MAX_NOA_DESCRIPTORS 4
+#define WMI_P2P_OPPPS_ENABLE_BIT       BIT(0)
+#define WMI_P2P_OPPPS_CTWINDOW_OFFSET  1
+#define WMI_P2P_NOA_CHANGED_BIT        BIT(0)
+
+struct wmi_p2p_noa_info {
+       /* Bit 0 - Flag to indicate an update in NOA schedule
+          Bits 7-1 - Reserved */
+       u8 changed;
+       /* NOA index */
+       u8 index;
+       /* Bit 0 - Opp PS state of the AP
+          Bits 1-7 - Ctwindow in TUs */
+       u8 ctwindow_oppps;
+       /* Number of NOA descriptors */
+       u8 num_descriptors;
+
+       struct wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS];
+} __packed;
+
+struct wmi_bcn_info {
+       struct wmi_tim_info tim_info;
+       struct wmi_p2p_noa_info p2p_noa_info;
+} __packed;
+
+struct wmi_host_swba_event {
+       __le32 vdev_map;
+       struct wmi_bcn_info bcn_info[1];
+} __packed;
+
+#define WMI_MAX_AP_VDEV 16
+
+struct wmi_tbtt_offset_event {
+       __le32 vdev_map;
+       __le32 tbttoffset_list[WMI_MAX_AP_VDEV];
+} __packed;
+
+
+struct wmi_peer_create_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+struct wmi_peer_delete_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+struct wmi_peer_flush_tids_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 peer_tid_bitmap;
+} __packed;
+
+struct wmi_fixed_rate {
+       /*
+        * rate mode . 0: disable fixed rate (auto rate)
+        *   1: legacy (non 11n) rate  specified as ieee rate 2*Mbps
+        *   2: ht20 11n rate  specified as mcs index
+        *   3: ht40 11n rate  specified as mcs index
+        */
+       __le32  rate_mode;
+       /*
+        * 4 rate values for 4 rate series. series 0 is stored in byte 0 (LSB)
+        * and series 3 is stored at byte 3 (MSB)
+        */
+       __le32  rate_series;
+       /*
+        * 4 retry counts for 4 rate series. retry count for rate 0 is stored
+        * in byte 0 (LSB) and retry count for rate 3 is stored at byte 3
+        * (MSB)
+        */
+       __le32  rate_retries;
+} __packed;
+
+struct wmi_peer_fixed_rate_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* fixed rate */
+       struct wmi_fixed_rate peer_fixed_rate;
+} __packed;
+
+#define WMI_MGMT_TID    17
+
+struct wmi_addba_clear_resp_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+struct wmi_addba_send_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+       /* Buffer/Window size*/
+       __le32 buffersize;
+} __packed;
+
+struct wmi_delba_send_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+       /* Is Initiator */
+       __le32 initiator;
+       /* Reason code */
+       __le32 reasoncode;
+} __packed;
+
+struct wmi_addba_setresponse_cmd {
+       /* unique id identifying the vdev, generated by the caller */
+       __le32 vdev_id;
+       /* peer mac address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+       /* status code */
+       __le32 statuscode;
+} __packed;
+
+struct wmi_send_singleamsdu_cmd {
+       /* unique id identifying the vdev, generated by the caller */
+       __le32 vdev_id;
+       /* peer mac address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+} __packed;
+
+enum wmi_peer_smps_state {
+       WMI_PEER_SMPS_PS_NONE = 0x0,
+       WMI_PEER_SMPS_STATIC  = 0x1,
+       WMI_PEER_SMPS_DYNAMIC = 0x2
+};
+
+enum wmi_peer_param {
+       WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */
+       WMI_PEER_AMPDU      = 0x2,
+       WMI_PEER_AUTHORIZE  = 0x3,
+       WMI_PEER_CHAN_WIDTH = 0x4,
+       WMI_PEER_NSS        = 0x5,
+       WMI_PEER_USE_4ADDR  = 0x6
+};
+
+struct wmi_peer_set_param_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 param_id;
+       __le32 param_value;
+} __packed;
+
+#define MAX_SUPPORTED_RATES 128
+
+struct wmi_rate_set {
+       /* total number of rates */
+       __le32 num_rates;
+       /*
+        * rates (each 8bit value) packed into a 32 bit word.
+        * the rates are filled from least significant byte to most
+        * significant byte.
+        */
+       __le32 rates[(MAX_SUPPORTED_RATES/4)+1];
+} __packed;
+
+struct wmi_rate_set_arg {
+       unsigned int num_rates;
+       u8 rates[MAX_SUPPORTED_RATES];
+};
+
+/*
+ * NOTE: It would bea good idea to represent the Tx MCS
+ * info in one word and Rx in another word. This is split
+ * into multiple words for convenience
+ */
+struct wmi_vht_rate_set {
+       __le32 rx_max_rate; /* Max Rx data rate */
+       __le32 rx_mcs_set;  /* Negotiated RX VHT rates */
+       __le32 tx_max_rate; /* Max Tx data rate */
+       __le32 tx_mcs_set;  /* Negotiated TX VHT rates */
+} __packed;
+
+struct wmi_vht_rate_set_arg {
+       u32 rx_max_rate;
+       u32 rx_mcs_set;
+       u32 tx_max_rate;
+       u32 tx_mcs_set;
+};
+
+struct wmi_peer_set_rates_cmd {
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* legacy rate set */
+       struct wmi_rate_set peer_legacy_rates;
+       /* ht rate set */
+       struct wmi_rate_set peer_ht_rates;
+} __packed;
+
+struct wmi_peer_set_q_empty_callback_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       __le32 callback_enable;
+} __packed;
+
+#define WMI_PEER_AUTH           0x00000001
+#define WMI_PEER_QOS            0x00000002
+#define WMI_PEER_NEED_PTK_4_WAY 0x00000004
+#define WMI_PEER_NEED_GTK_2_WAY 0x00000010
+#define WMI_PEER_APSD           0x00000800
+#define WMI_PEER_HT             0x00001000
+#define WMI_PEER_40MHZ          0x00002000
+#define WMI_PEER_STBC           0x00008000
+#define WMI_PEER_LDPC           0x00010000
+#define WMI_PEER_DYN_MIMOPS     0x00020000
+#define WMI_PEER_STATIC_MIMOPS  0x00040000
+#define WMI_PEER_SPATIAL_MUX    0x00200000
+#define WMI_PEER_VHT            0x02000000
+#define WMI_PEER_80MHZ          0x04000000
+#define WMI_PEER_PMF            0x08000000
+
+/*
+ * Peer rate capabilities.
+ *
+ * This is of interest to the ratecontrol
+ * module which resides in the firmware. The bit definitions are
+ * consistent with that defined in if_athrate.c.
+ */
+#define WMI_RC_DS_FLAG          0x01
+#define WMI_RC_CW40_FLAG        0x02
+#define WMI_RC_SGI_FLAG         0x04
+#define WMI_RC_HT_FLAG          0x08
+#define WMI_RC_RTSCTS_FLAG      0x10
+#define WMI_RC_TX_STBC_FLAG     0x20
+#define WMI_RC_RX_STBC_FLAG     0xC0
+#define WMI_RC_RX_STBC_FLAG_S   6
+#define WMI_RC_WEP_TKIP_FLAG    0x100
+#define WMI_RC_TS_FLAG          0x200
+#define WMI_RC_UAPSD_FLAG       0x400
+
+/* Maximum listen interval supported by hw in units of beacon interval */
+#define ATH10K_MAX_HW_LISTEN_INTERVAL 5
+
+struct wmi_peer_assoc_complete_cmd {
+       struct wmi_mac_addr peer_macaddr;
+       __le32 vdev_id;
+       __le32 peer_new_assoc; /* 1=assoc, 0=reassoc */
+       __le32 peer_associd; /* 16 LSBs */
+       __le32 peer_flags;
+       __le32 peer_caps; /* 16 LSBs */
+       __le32 peer_listen_intval;
+       __le32 peer_ht_caps;
+       __le32 peer_max_mpdu;
+       __le32 peer_mpdu_density; /* 0..16 */
+       __le32 peer_rate_caps;
+       struct wmi_rate_set peer_legacy_rates;
+       struct wmi_rate_set peer_ht_rates;
+       __le32 peer_nss; /* num of spatial streams */
+       __le32 peer_vht_caps;
+       __le32 peer_phymode;
+       struct wmi_vht_rate_set peer_vht_rates;
+       /* HT Operation Element of the peer. Five bytes packed in 2
+        *  INT32 array and filled from lsb to msb. */
+       __le32 peer_ht_info[2];
+} __packed;
+
+struct wmi_peer_assoc_complete_arg {
+       u8 addr[ETH_ALEN];
+       u32 vdev_id;
+       bool peer_reassoc;
+       u16 peer_aid;
+       u32 peer_flags; /* see %WMI_PEER_ */
+       u16 peer_caps;
+       u32 peer_listen_intval;
+       u32 peer_ht_caps;
+       u32 peer_max_mpdu;
+       u32 peer_mpdu_density; /* 0..16 */
+       u32 peer_rate_caps; /* see %WMI_RC_ */
+       struct wmi_rate_set_arg peer_legacy_rates;
+       struct wmi_rate_set_arg peer_ht_rates;
+       u32 peer_num_spatial_streams;
+       u32 peer_vht_caps;
+       enum wmi_phy_mode peer_phymode;
+       struct wmi_vht_rate_set_arg peer_vht_rates;
+};
+
+struct wmi_peer_add_wds_entry_cmd {
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* wds MAC addr */
+       struct wmi_mac_addr wds_macaddr;
+} __packed;
+
+struct wmi_peer_remove_wds_entry_cmd {
+       /* wds MAC addr */
+       struct wmi_mac_addr wds_macaddr;
+} __packed;
+
+struct wmi_peer_q_empty_callback_event {
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+/*
+ * Channel info WMI event
+ */
+struct wmi_chan_info_event {
+       __le32 err_code;
+       __le32 freq;
+       __le32 cmd_flags;
+       __le32 noise_floor;
+       __le32 rx_clear_count;
+       __le32 cycle_count;
+} __packed;
+
+/* Beacon filter wmi command info */
+#define BCN_FLT_MAX_SUPPORTED_IES      256
+#define BCN_FLT_MAX_ELEMS_IE_LIST      (BCN_FLT_MAX_SUPPORTED_IES / 32)
+
+struct bss_bcn_stats {
+       __le32 vdev_id;
+       __le32 bss_bcnsdropped;
+       __le32 bss_bcnsdelivered;
+} __packed;
+
+struct bcn_filter_stats {
+       __le32 bcns_dropped;
+       __le32 bcns_delivered;
+       __le32 activefilters;
+       struct bss_bcn_stats bss_stats;
+} __packed;
+
+struct wmi_add_bcn_filter_cmd {
+       u32 vdev_id;
+       u32 ie_map[BCN_FLT_MAX_ELEMS_IE_LIST];
+} __packed;
+
+enum wmi_sta_keepalive_method {
+       WMI_STA_KEEPALIVE_METHOD_NULL_FRAME = 1,
+       WMI_STA_KEEPALIVE_METHOD_UNSOLICITATED_ARP_RESPONSE = 2,
+};
+
+/* note: ip4 addresses are in network byte order, i.e. big endian */
+struct wmi_sta_keepalive_arp_resp {
+       __be32 src_ip4_addr;
+       __be32 dest_ip4_addr;
+       struct wmi_mac_addr dest_mac_addr;
+} __packed;
+
+struct wmi_sta_keepalive_cmd {
+       __le32 vdev_id;
+       __le32 enabled;
+       __le32 method; /* WMI_STA_KEEPALIVE_METHOD_ */
+       __le32 interval; /* in seconds */
+       struct wmi_sta_keepalive_arp_resp arp_resp;
+} __packed;
+
+#define ATH10K_RTS_MAX         2347
+#define ATH10K_FRAGMT_THRESHOLD_MIN    540
+#define ATH10K_FRAGMT_THRESHOLD_MAX    2346
+
+#define WMI_MAX_EVENT 0x1000
+/* Maximum number of pending TXed WMI packets */
+#define WMI_MAX_PENDING_TX_COUNT 128
+#define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr)
+
+/* By default disable power save for IBSS */
+#define ATH10K_DEFAULT_ATIM 0
+
+struct ath10k;
+struct ath10k_vif;
+
+int ath10k_wmi_attach(struct ath10k *ar);
+void ath10k_wmi_detach(struct ath10k *ar);
+int ath10k_wmi_wait_for_service_ready(struct ath10k *ar);
+int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar);
+void ath10k_wmi_flush_tx(struct ath10k *ar);
+
+int ath10k_wmi_connect_htc_service(struct ath10k *ar);
+int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
+                               const struct wmi_channel_arg *);
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar);
+int ath10k_wmi_pdev_resume_target(struct ath10k *ar);
+int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
+                                 u16 rd5g, u16 ctl2g, u16 ctl5g);
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
+                             u32 value);
+int ath10k_wmi_cmd_init(struct ath10k *ar);
+int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *);
+void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *);
+int ath10k_wmi_stop_scan(struct ath10k *ar,
+                        const struct wmi_stop_scan_arg *arg);
+int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
+                          enum wmi_vdev_type type,
+                          enum wmi_vdev_subtype subtype,
+                          const u8 macaddr[ETH_ALEN]);
+int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id);
+int ath10k_wmi_vdev_start(struct ath10k *ar,
+                         const struct wmi_vdev_start_request_arg *);
+int ath10k_wmi_vdev_restart(struct ath10k *ar,
+                           const struct wmi_vdev_start_request_arg *);
+int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id);
+int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
+                      const u8 *bssid);
+int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id);
+int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
+                             enum wmi_vdev_param param_id, u32 param_value);
+int ath10k_wmi_vdev_install_key(struct ath10k *ar,
+                               const struct wmi_vdev_install_key_arg *arg);
+int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
+                   const u8 peer_addr[ETH_ALEN]);
+int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
+                   const u8 peer_addr[ETH_ALEN]);
+int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
+                  const u8 peer_addr[ETH_ALEN], u32 tid_bitmap);
+int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
+                             const u8 *peer_addr,
+                             enum wmi_peer_param param_id, u32 param_value);
+int ath10k_wmi_peer_assoc(struct ath10k *ar,
+                         const struct wmi_peer_assoc_complete_arg *arg);
+int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
+                         enum wmi_sta_ps_mode psmode);
+int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
+                               enum wmi_sta_powersave_param param_id,
+                               u32 value);
+int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+                              enum wmi_ap_ps_peer_param param_id, u32 value);
+int ath10k_wmi_scan_chan_list(struct ath10k *ar,
+                             const struct wmi_scan_chan_list_arg *arg);
+int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg);
+int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
+                       const struct wmi_pdev_set_wmm_params_arg *arg);
+int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
+
+#endif /* _WMI_H_ */
index 8e8bcc7..e9bc9e6 100644 (file)
@@ -185,7 +185,6 @@ static int ath_ahb_probe(struct platform_device *pdev)
 
  err_free_hw:
        ieee80211_free_hw(hw);
-       platform_set_drvdata(pdev, NULL);
  err_iounmap:
         iounmap(mem);
  err_out:
@@ -221,7 +220,6 @@ static int ath_ahb_remove(struct platform_device *pdev)
 
        ath5k_deinit_ah(ah);
        iounmap(ah->iobase);
-       platform_set_drvdata(pdev, NULL);
        ieee80211_free_hw(hw);
 
        return 0;
index 7f702fe..ce67ab7 100644 (file)
@@ -60,6 +60,7 @@
 
 #include <asm/unaligned.h>
 
+#include <net/mac80211.h>
 #include "base.h"
 #include "reg.h"
 #include "debug.h"
@@ -666,9 +667,46 @@ static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
        return htype;
 }
 
+static struct ieee80211_rate *
+ath5k_get_rate(const struct ieee80211_hw *hw,
+              const struct ieee80211_tx_info *info,
+              struct ath5k_buf *bf, int idx)
+{
+       /*
+       * convert a ieee80211_tx_rate RC-table entry to
+       * the respective ieee80211_rate struct
+       */
+       if (bf->rates[idx].idx < 0) {
+               return NULL;
+       }
+
+       return &hw->wiphy->bands[info->band]->bitrates[ bf->rates[idx].idx ];
+}
+
+static u16
+ath5k_get_rate_hw_value(const struct ieee80211_hw *hw,
+                       const struct ieee80211_tx_info *info,
+                       struct ath5k_buf *bf, int idx)
+{
+       struct ieee80211_rate *rate;
+       u16 hw_rate;
+       u8 rc_flags;
+
+       rate = ath5k_get_rate(hw, info, bf, idx);
+       if (!rate)
+               return 0;
+
+       rc_flags = bf->rates[idx].flags;
+       hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
+                  rate->hw_value_short : rate->hw_value;
+
+       return hw_rate;
+}
+
 static int
 ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
-                 struct ath5k_txq *txq, int padsize)
+                 struct ath5k_txq *txq, int padsize,
+                 struct ieee80211_tx_control *control)
 {
        struct ath5k_desc *ds = bf->desc;
        struct sk_buff *skb = bf->skb;
@@ -688,7 +726,11 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
        bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len,
                        DMA_TO_DEVICE);
 
-       rate = ieee80211_get_tx_rate(ah->hw, info);
+       ieee80211_get_tx_rates(info->control.vif, (control) ? control->sta : NULL, skb, bf->rates,
+                              ARRAY_SIZE(bf->rates));
+
+       rate = ath5k_get_rate(ah->hw, info, bf, 0);
+
        if (!rate) {
                ret = -EINVAL;
                goto err_unmap;
@@ -698,8 +740,8 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
                flags |= AR5K_TXDESC_NOACK;
 
        rc_flags = info->control.rates[0].flags;
-       hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
-               rate->hw_value_short : rate->hw_value;
+
+       hw_rate = ath5k_get_rate_hw_value(ah->hw, info, bf, 0);
 
        pktlen = skb->len;
 
@@ -722,12 +764,13 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
                duration = le16_to_cpu(ieee80211_ctstoself_duration(ah->hw,
                        info->control.vif, pktlen, info));
        }
+
        ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
                ieee80211_get_hdrlen_from_skb(skb), padsize,
                get_hw_packet_type(skb),
                (ah->ah_txpower.txp_requested * 2),
                hw_rate,
-               info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
+               bf->rates[0].count, keyidx, ah->ah_tx_ant, flags,
                cts_rate, duration);
        if (ret)
                goto err_unmap;
@@ -736,13 +779,15 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
        if (ah->ah_capabilities.cap_has_mrr_support) {
                memset(mrr_rate, 0, sizeof(mrr_rate));
                memset(mrr_tries, 0, sizeof(mrr_tries));
+
                for (i = 0; i < 3; i++) {
-                       rate = ieee80211_get_alt_retry_rate(ah->hw, info, i);
+
+                       rate = ath5k_get_rate(ah->hw, info, bf, i);
                        if (!rate)
                                break;
 
-                       mrr_rate[i] = rate->hw_value;
-                       mrr_tries[i] = info->control.rates[i + 1].count;
+                       mrr_rate[i] = ath5k_get_rate_hw_value(ah->hw, info, bf, i);
+                       mrr_tries[i] = bf->rates[i].count;
                }
 
                ath5k_hw_setup_mrr_tx_desc(ah, ds,
@@ -1515,7 +1560,7 @@ unlock:
 
 void
 ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-              struct ath5k_txq *txq)
+              struct ath5k_txq *txq, struct ieee80211_tx_control *control)
 {
        struct ath5k_hw *ah = hw->priv;
        struct ath5k_buf *bf;
@@ -1555,7 +1600,7 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
 
        bf->skb = skb;
 
-       if (ath5k_txbuf_setup(ah, bf, txq, padsize)) {
+       if (ath5k_txbuf_setup(ah, bf, txq, padsize, control)) {
                bf->skb = NULL;
                spin_lock_irqsave(&ah->txbuflock, flags);
                list_add_tail(&bf->list, &ah->txbuf);
@@ -1571,11 +1616,13 @@ drop_packet:
 
 static void
 ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb,
-                        struct ath5k_txq *txq, struct ath5k_tx_status *ts)
+                        struct ath5k_txq *txq, struct ath5k_tx_status *ts,
+                        struct ath5k_buf *bf)
 {
        struct ieee80211_tx_info *info;
        u8 tries[3];
        int i;
+       int size = 0;
 
        ah->stats.tx_all_count++;
        ah->stats.tx_bytes_count += skb->len;
@@ -1587,6 +1634,9 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb,
 
        ieee80211_tx_info_clear_status(info);
 
+       size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates));
+       memcpy(info->status.rates, bf->rates, size);
+
        for (i = 0; i < ts->ts_final_idx; i++) {
                struct ieee80211_tx_rate *r =
                        &info->status.rates[i];
@@ -1663,7 +1713,7 @@ ath5k_tx_processq(struct ath5k_hw *ah, struct ath5k_txq *txq)
 
                        dma_unmap_single(ah->dev, bf->skbaddr, skb->len,
                                        DMA_TO_DEVICE);
-                       ath5k_tx_frame_completed(ah, skb, txq, &ts);
+                       ath5k_tx_frame_completed(ah, skb, txq, &ts, bf);
                }
 
                /*
@@ -1917,7 +1967,7 @@ ath5k_beacon_send(struct ath5k_hw *ah)
 
        skb = ieee80211_get_buffered_bc(ah->hw, vif);
        while (skb) {
-               ath5k_tx_queue(ah->hw, skb, ah->cabq);
+               ath5k_tx_queue(ah->hw, skb, ah->cabq, NULL);
 
                if (ah->cabq->txq_len >= ah->cabq->txq_max)
                        break;
@@ -2442,7 +2492,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
                        IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
                        IEEE80211_HW_SIGNAL_DBM |
                        IEEE80211_HW_MFP_CAPABLE |
-                       IEEE80211_HW_REPORTS_TX_ACK_STATUS;
+                       IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+                       IEEE80211_HW_SUPPORTS_RC_TABLE;
 
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_AP) |
index 6c94c7f..ca9a83c 100644 (file)
@@ -47,6 +47,7 @@ struct ath5k_hw;
 struct ath5k_txq;
 struct ieee80211_channel;
 struct ath_bus_ops;
+struct ieee80211_tx_control;
 enum nl80211_iftype;
 
 enum ath5k_srev_type {
@@ -61,11 +62,12 @@ struct ath5k_srev_name {
 };
 
 struct ath5k_buf {
-       struct list_head        list;
-       struct ath5k_desc       *desc;  /* virtual addr of desc */
-       dma_addr_t              daddr;  /* physical addr of desc */
-       struct sk_buff          *skb;   /* skbuff for buf */
-       dma_addr_t              skbaddr;/* physical addr of skb data */
+       struct list_head                list;
+       struct ath5k_desc               *desc;          /* virtual addr of desc */
+       dma_addr_t                      daddr;          /* physical addr of desc */
+       struct sk_buff                  *skb;           /* skbuff for buf */
+       dma_addr_t                      skbaddr;        /* physical addr of skb data */
+       struct ieee80211_tx_rate        rates[4];       /* number of multi-rate stages */
 };
 
 struct ath5k_vif {
@@ -103,7 +105,7 @@ int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan);
 void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
 void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
 void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-                   struct ath5k_txq *txq);
+                   struct ath5k_txq *txq, struct ieee80211_tx_control *control);
 
 const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val);
 
index 06f86f4..81b686c 100644 (file)
@@ -66,7 +66,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
                return;
        }
 
-       ath5k_tx_queue(hw, skb, &ah->txqs[qnum]);
+       ath5k_tx_queue(hw, skb, &ah->txqs[qnum], control);
 }
 
 
index 5c9736a..2437ad2 100644 (file)
@@ -3175,10 +3175,21 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 {
        struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
        struct ath6kl *ar = ath6kl_priv(vif->ndev);
-       u32 id;
+       u32 id, freq;
        const struct ieee80211_mgmt *mgmt;
        bool more_data, queued;
 
+       /* default to the current channel, but use the one specified as argument
+        * if any
+        */
+       freq = vif->ch_hint;
+       if (chan)
+               freq = chan->center_freq;
+
+       /* never send freq zero to the firmware */
+       if (WARN_ON(freq == 0))
+               return -EINVAL;
+
        mgmt = (const struct ieee80211_mgmt *) buf;
        if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
            ieee80211_is_probe_resp(mgmt->frame_control) &&
@@ -3188,8 +3199,7 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                 * command to allow the target to fill in the generic IEs.
                 */
                *cookie = 0; /* TX status not supported */
-               return ath6kl_send_go_probe_resp(vif, buf, len,
-                                                chan->center_freq);
+               return ath6kl_send_go_probe_resp(vif, buf, len, freq);
        }
 
        id = vif->send_action_id++;
@@ -3205,17 +3215,14 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 
        /* AP mode Power saving processing */
        if (vif->nw_type == AP_NETWORK) {
-               queued = ath6kl_mgmt_powersave_ap(vif,
-                                       id, chan->center_freq,
-                                       wait, buf,
-                                       len, &more_data, no_cck);
+               queued = ath6kl_mgmt_powersave_ap(vif, id, freq, wait, buf, len,
+                                                 &more_data, no_cck);
                if (queued)
                        return 0;
        }
 
-       return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
-                                       chan->center_freq, wait,
-                                       buf, len, no_cck);
+       return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, freq,
+                                       wait, buf, len, no_cck);
 }
 
 static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
@@ -3679,6 +3686,20 @@ err:
        return NULL;
 }
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support ath6kl_wowlan_support = {
+       .flags = WIPHY_WOWLAN_MAGIC_PKT |
+                WIPHY_WOWLAN_DISCONNECT |
+                WIPHY_WOWLAN_GTK_REKEY_FAILURE  |
+                WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+                WIPHY_WOWLAN_EAP_IDENTITY_REQ   |
+                WIPHY_WOWLAN_4WAY_HANDSHAKE,
+       .n_patterns = WOW_MAX_FILTERS_PER_LIST,
+       .pattern_min_len = 1,
+       .pattern_max_len = WOW_PATTERN_SIZE,
+};
+#endif
+
 int ath6kl_cfg80211_init(struct ath6kl *ar)
 {
        struct wiphy *wiphy = ar->wiphy;
@@ -3772,15 +3793,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
        wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
 
 #ifdef CONFIG_PM
-       wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                             WIPHY_WOWLAN_DISCONNECT |
-                             WIPHY_WOWLAN_GTK_REKEY_FAILURE  |
-                             WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
-                             WIPHY_WOWLAN_EAP_IDENTITY_REQ   |
-                             WIPHY_WOWLAN_4WAY_HANDSHAKE;
-       wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
-       wiphy->wowlan.pattern_min_len = 1;
-       wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
+       wiphy->wowlan = &ath6kl_wowlan_support;
 #endif
 
        wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS;
index fe38b83..dbfd17d 100644 (file)
@@ -1240,20 +1240,14 @@ static ssize_t ath6kl_force_roam_write(struct file *file,
        char buf[20];
        size_t len;
        u8 bssid[ETH_ALEN];
-       int i;
-       int addr[ETH_ALEN];
 
        len = min(count, sizeof(buf) - 1);
        if (copy_from_user(buf, user_buf, len))
                return -EFAULT;
        buf[len] = '\0';
 
-       if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
-                  &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5])
-           != ETH_ALEN)
+       if (!mac_pton(buf, bssid))
                return -EINVAL;
-       for (i = 0; i < ETH_ALEN; i++)
-               bssid[i] = addr[i];
 
        ret = ath6kl_wmi_force_roam_cmd(ar->wmi, bssid);
        if (ret)
index 40ffee6..6a67881 100644 (file)
@@ -1696,10 +1696,16 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar)
                                                    test_bit(WMI_READY,
                                                             &ar->flag),
                                                    WMI_TIMEOUT);
+       if (timeleft <= 0) {
+               clear_bit(WMI_READY, &ar->flag);
+               ath6kl_err("wmi is not ready or wait was interrupted: %ld\n",
+                          timeleft);
+               ret = -EIO;
+               goto err_htc_stop;
+       }
 
        ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n");
 
-
        if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) {
                ath6kl_info("%s %s fw %s api %d%s\n",
                            ar->hw.name,
@@ -1718,12 +1724,6 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar)
                goto err_htc_stop;
        }
 
-       if (!timeleft || signal_pending(current)) {
-               ath6kl_err("wmi is not ready or wait was interrupted\n");
-               ret = -EIO;
-               goto err_htc_stop;
-       }
-
        ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__);
 
        /* communicate the wmi protocol verision to the target */
index fb14145..7126bdd 100644 (file)
@@ -345,17 +345,17 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
 {
        struct hif_scatter_req *s_req;
        struct bus_request *bus_req;
-       int i, scat_req_sz, scat_list_sz, sg_sz, buf_sz;
+       int i, scat_req_sz, scat_list_sz, size;
        u8 *virt_buf;
 
        scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item);
        scat_req_sz = sizeof(*s_req) + scat_list_sz;
 
        if (!virt_scat)
-               sg_sz = sizeof(struct scatterlist) * n_scat_entry;
+               size = sizeof(struct scatterlist) * n_scat_entry;
        else
-               buf_sz =  2 * L1_CACHE_BYTES +
-                         ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
+               size =  2 * L1_CACHE_BYTES +
+                       ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
 
        for (i = 0; i < n_scat_req; i++) {
                /* allocate the scatter request */
@@ -364,7 +364,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
                        return -ENOMEM;
 
                if (virt_scat) {
-                       virt_buf = kzalloc(buf_sz, GFP_KERNEL);
+                       virt_buf = kzalloc(size, GFP_KERNEL);
                        if (!virt_buf) {
                                kfree(s_req);
                                return -ENOMEM;
@@ -374,7 +374,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
                                (u8 *)L1_CACHE_ALIGN((unsigned long)virt_buf);
                } else {
                        /* allocate sglist */
-                       s_req->sgentries = kzalloc(sg_sz, GFP_KERNEL);
+                       s_req->sgentries = kzalloc(size, GFP_KERNEL);
 
                        if (!s_req->sgentries) {
                                kfree(s_req);
index bed0d33..f38ff6a 100644 (file)
@@ -1061,6 +1061,22 @@ static void ath6kl_usb_cleanup_scatter(struct ath6kl *ar)
        return;
 }
 
+static int ath6kl_usb_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+{
+       /*
+        * cfg80211 suspend/WOW currently not supported for USB.
+        */
+       return 0;
+}
+
+static int ath6kl_usb_resume(struct ath6kl *ar)
+{
+       /*
+        * cfg80211 resume currently not supported for USB.
+        */
+       return 0;
+}
+
 static const struct ath6kl_hif_ops ath6kl_usb_ops = {
        .diag_read32 = ath6kl_usb_diag_read32,
        .diag_write32 = ath6kl_usb_diag_write32,
@@ -1074,6 +1090,8 @@ static const struct ath6kl_hif_ops ath6kl_usb_ops = {
        .pipe_map_service = ath6kl_usb_map_service_pipe,
        .pipe_get_free_queue_number = ath6kl_usb_get_free_queue_number,
        .cleanup_scatter = ath6kl_usb_cleanup_scatter,
+       .suspend = ath6kl_usb_suspend,
+       .resume = ath6kl_usb_resume,
 };
 
 /* ath6kl usb driver registered functions */
@@ -1152,7 +1170,7 @@ static void ath6kl_usb_remove(struct usb_interface *interface)
 
 #ifdef CONFIG_PM
 
-static int ath6kl_usb_suspend(struct usb_interface *interface,
+static int ath6kl_usb_pm_suspend(struct usb_interface *interface,
                              pm_message_t message)
 {
        struct ath6kl_usb *device;
@@ -1162,7 +1180,7 @@ static int ath6kl_usb_suspend(struct usb_interface *interface,
        return 0;
 }
 
-static int ath6kl_usb_resume(struct usb_interface *interface)
+static int ath6kl_usb_pm_resume(struct usb_interface *interface)
 {
        struct ath6kl_usb *device;
        device = usb_get_intfdata(interface);
@@ -1175,7 +1193,7 @@ static int ath6kl_usb_resume(struct usb_interface *interface)
        return 0;
 }
 
-static int ath6kl_usb_reset_resume(struct usb_interface *intf)
+static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf)
 {
        if (usb_get_intfdata(intf))
                ath6kl_usb_remove(intf);
@@ -1184,9 +1202,9 @@ static int ath6kl_usb_reset_resume(struct usb_interface *intf)
 
 #else
 
-#define ath6kl_usb_suspend NULL
-#define ath6kl_usb_resume NULL
-#define ath6kl_usb_reset_resume NULL
+#define ath6kl_usb_pm_suspend NULL
+#define ath6kl_usb_pm_resume NULL
+#define ath6kl_usb_pm_reset_resume NULL
 
 #endif
 
@@ -1201,9 +1219,9 @@ MODULE_DEVICE_TABLE(usb, ath6kl_usb_ids);
 static struct usb_driver ath6kl_usb_driver = {
        .name = "ath6kl_usb",
        .probe = ath6kl_usb_probe,
-       .suspend = ath6kl_usb_suspend,
-       .resume = ath6kl_usb_resume,
-       .reset_resume = ath6kl_usb_reset_resume,
+       .suspend = ath6kl_usb_pm_suspend,
+       .resume = ath6kl_usb_pm_resume,
+       .reset_resume = ath6kl_usb_pm_reset_resume,
        .disconnect = ath6kl_usb_remove,
        .id_table = ath6kl_usb_ids,
        .supports_autosuspend = true,
index f985cf3..d491a31 100644 (file)
@@ -84,14 +84,6 @@ config ATH9K_DFS_CERTIFIED
          developed. At this point enabling this option won't do anything
          except increase code size.
 
-config ATH9K_MAC_DEBUG
-       bool "Atheros MAC statistics"
-       depends on ATH9K_DEBUGFS
-       default y
-       ---help---
-         This option enables collection of statistics for Rx/Tx status
-         data and some other MAC related statistics
-
 config ATH9K_LEGACY_RATE_CONTROL
        bool "Atheros ath9k rate control"
        depends on ATH9K
index d1ff3c2..072e4b5 100644 (file)
@@ -150,7 +150,6 @@ static int ath_ahb_probe(struct platform_device *pdev)
        free_irq(irq, sc);
  err_free_hw:
        ieee80211_free_hw(hw);
-       platform_set_drvdata(pdev, NULL);
        return ret;
 }
 
@@ -164,7 +163,6 @@ static int ath_ahb_remove(struct platform_device *pdev)
                ath9k_deinit_device(sc);
                free_irq(sc->irq, sc);
                ieee80211_free_hw(sc->hw);
-               platform_set_drvdata(pdev, NULL);
        }
 
        return 0;
index 7ecd40f..4994bea 100644 (file)
@@ -46,8 +46,8 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = {
        {  5,  4,  1  }, /* lvl 5 */
        {  6,  5,  1  }, /* lvl 6 */
        {  7,  6,  1  }, /* lvl 7 */
-       {  7,  6,  0  }, /* lvl 8 */
-       {  7,  7,  0  }  /* lvl 9 */
+       {  7,  7,  1  }, /* lvl 8 */
+       {  7,  8,  0  }  /* lvl 9 */
 };
 #define ATH9K_ANI_OFDM_NUM_LEVEL \
        ARRAY_SIZE(ofdm_level_table)
@@ -91,8 +91,8 @@ static const struct ani_cck_level_entry cck_level_table[] = {
        {  4,  0  }, /* lvl 4 */
        {  5,  0  }, /* lvl 5 */
        {  6,  0  }, /* lvl 6 */
-       {  6,  0  }, /* lvl 7 (only for high rssi) */
-       {  7,  0  }  /* lvl 8 (only for high rssi) */
+       {  7,  0  }, /* lvl 7 (only for high rssi) */
+       {  8,  0  }  /* lvl 8 (only for high rssi) */
 };
 
 #define ATH9K_ANI_CCK_NUM_LEVEL \
@@ -118,10 +118,10 @@ static void ath9k_ani_restart(struct ath_hw *ah)
 {
        struct ar5416AniState *aniState;
 
-       if (!DO_ANI(ah))
+       if (!ah->curchan)
                return;
 
-       aniState = &ah->curchan->ani;
+       aniState = &ah->ani;
        aniState->listenTime = 0;
 
        ENABLE_REGWRITE_BUFFER(ah);
@@ -143,7 +143,7 @@ static void ath9k_ani_restart(struct ath_hw *ah)
 static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,
                                  bool scan)
 {
-       struct ar5416AniState *aniState = &ah->curchan->ani;
+       struct ar5416AniState *aniState = &ah->ani;
        struct ath_common *common = ath9k_hw_common(ah);
        const struct ani_ofdm_level_entry *entry_ofdm;
        const struct ani_cck_level_entry *entry_cck;
@@ -177,10 +177,15 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,
            BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH)
                weak_sig = true;
 
-       if (aniState->ofdmWeakSigDetect != weak_sig)
-                       ath9k_hw_ani_control(ah,
-                               ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
-                               entry_ofdm->ofdm_weak_signal_on);
+       /*
+        * OFDM Weak signal detection is always enabled for AP mode.
+        */
+       if (ah->opmode != NL80211_IFTYPE_AP &&
+           aniState->ofdmWeakSigDetect != weak_sig) {
+               ath9k_hw_ani_control(ah,
+                                    ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+                                    entry_ofdm->ofdm_weak_signal_on);
+       }
 
        if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) {
                ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
@@ -195,10 +200,10 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
 {
        struct ar5416AniState *aniState;
 
-       if (!DO_ANI(ah))
+       if (!ah->curchan)
                return;
 
-       aniState = &ah->curchan->ani;
+       aniState = &ah->ani;
 
        if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL)
                ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false);
@@ -210,7 +215,7 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
 static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel,
                                 bool scan)
 {
-       struct ar5416AniState *aniState = &ah->curchan->ani;
+       struct ar5416AniState *aniState = &ah->ani;
        struct ath_common *common = ath9k_hw_common(ah);
        const struct ani_ofdm_level_entry *entry_ofdm;
        const struct ani_cck_level_entry *entry_cck;
@@ -251,10 +256,10 @@ static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
 {
        struct ar5416AniState *aniState;
 
-       if (!DO_ANI(ah))
+       if (!ah->curchan)
                return;
 
-       aniState = &ah->curchan->ani;
+       aniState = &ah->ani;
 
        if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL)
                ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1,
@@ -269,7 +274,7 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
 {
        struct ar5416AniState *aniState;
 
-       aniState = &ah->curchan->ani;
+       aniState = &ah->ani;
 
        /* lower OFDM noise immunity */
        if (aniState->ofdmNoiseImmunityLevel > 0 &&
@@ -292,12 +297,12 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
  */
 void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
 {
-       struct ar5416AniState *aniState = &ah->curchan->ani;
+       struct ar5416AniState *aniState = &ah->ani;
        struct ath9k_channel *chan = ah->curchan;
        struct ath_common *common = ath9k_hw_common(ah);
        int ofdm_nil, cck_nil;
 
-       if (!DO_ANI(ah))
+       if (!ah->curchan)
                return;
 
        BUG_ON(aniState == NULL);
@@ -363,24 +368,13 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
        ath9k_hw_set_ofdm_nil(ah, ofdm_nil, is_scanning);
        ath9k_hw_set_cck_nil(ah, cck_nil, is_scanning);
 
-       /*
-        * enable phy counters if hw supports or if not, enable phy
-        * interrupts (so we can count each one)
-        */
        ath9k_ani_restart(ah);
-
-       ENABLE_REGWRITE_BUFFER(ah);
-
-       REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
-       REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
-
-       REGWRITE_BUFFER_FLUSH(ah);
 }
 
 static bool ath9k_hw_ani_read_counters(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ar5416AniState *aniState = &ah->curchan->ani;
+       struct ar5416AniState *aniState = &ah->ani;
        u32 phyCnt1, phyCnt2;
        int32_t listenTime;
 
@@ -415,10 +409,10 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)
        struct ath_common *common = ath9k_hw_common(ah);
        u32 ofdmPhyErrRate, cckPhyErrRate;
 
-       if (!DO_ANI(ah))
+       if (!ah->curchan)
                return;
 
-       aniState = &ah->curchan->ani;
+       aniState = &ah->ani;
        if (!ath9k_hw_ani_read_counters(ah))
                return;
 
@@ -490,32 +484,22 @@ EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
 void ath9k_hw_ani_init(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
-       int i;
+       struct ar5416AniState *ani = &ah->ani;
 
        ath_dbg(common, ANI, "Initialize ANI\n");
 
        ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
        ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;
-
        ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH;
        ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW;
 
-       for (i = 0; i < ARRAY_SIZE(ah->channels); i++) {
-               struct ath9k_channel *chan = &ah->channels[i];
-               struct ar5416AniState *ani = &chan->ani;
-
-               ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
-
-               ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
-
-               ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false;
-
-               ani->ofdmsTurn = true;
-
-               ani->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
-               ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
-               ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
-       }
+       ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
+       ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
+       ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false;
+       ani->ofdmsTurn = true;
+       ani->ofdmWeakSigDetect = true;
+       ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
+       ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
 
        /*
         * since we expect some ongoing maintenance on the tables, let's sanity
@@ -524,9 +508,6 @@ void ath9k_hw_ani_init(struct ath_hw *ah)
        ah->aniperiod = ATH9K_ANI_PERIOD;
        ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL;
 
-       if (ah->config.enable_ani)
-               ah->proc_phyerr |= HAL_PROCESS_ANI;
-
        ath9k_ani_restart(ah);
        ath9k_enable_mib_counters(ah);
 }
index dddb136..b54a3fb 100644 (file)
 #ifndef ANI_H
 #define ANI_H
 
-#define HAL_PROCESS_ANI           0x00000001
-
-#define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI) && ah->curchan)
-
 #define BEACON_RSSI(ahp) (ahp->stats.avgbrssi)
 
 /* units are errors per second */
-#define ATH9K_ANI_OFDM_TRIG_HIGH          3500
+#define ATH9K_ANI_OFDM_TRIG_HIGH           3500
 #define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000
 
-/* units are errors per second */
 #define ATH9K_ANI_OFDM_TRIG_LOW           400
 #define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900
 
-/* units are errors per second */
 #define ATH9K_ANI_CCK_TRIG_HIGH           600
-
-/* units are errors per second */
 #define ATH9K_ANI_CCK_TRIG_LOW            300
 
-#define ATH9K_ANI_NOISE_IMMUNE_LVL        4
-#define ATH9K_ANI_USE_OFDM_WEAK_SIG       true
-#define ATH9K_ANI_CCK_WEAK_SIG_THR        false
-
 #define ATH9K_ANI_SPUR_IMMUNE_LVL         3
-
 #define ATH9K_ANI_FIRSTEP_LVL             2
 
 #define ATH9K_ANI_RSSI_THR_HIGH           40
 /* in ms */
 #define ATH9K_ANI_POLLINTERVAL            1000
 
-#define HAL_NOISE_IMMUNE_MAX              4
-#define HAL_SPUR_IMMUNE_MAX               7
-#define HAL_FIRST_STEP_MAX                2
-
 #define ATH9K_SIG_FIRSTEP_SETTING_MIN     0
 #define ATH9K_SIG_FIRSTEP_SETTING_MAX     20
 #define ATH9K_SIG_SPUR_IMM_SETTING_MIN    0
@@ -111,7 +94,7 @@ struct ar5416AniState {
        u8 mrcCCK;
        u8 spurImmunityLevel;
        u8 firstepLevel;
-       u8 ofdmWeakSigDetect;
+       bool ofdmWeakSigDetect;
        u32 listenTime;
        u32 ofdmPhyErrCount;
        u32 cckPhyErrCount;
@@ -119,8 +102,6 @@ struct ar5416AniState {
 };
 
 struct ar5416Stats {
-       u32 ast_ani_niup;
-       u32 ast_ani_nidown;
        u32 ast_ani_spurup;
        u32 ast_ani_spurdown;
        u32 ast_ani_ofdmon;
index 391da5a..d1acfe9 100644 (file)
@@ -931,7 +931,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
 {
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_channel *chan = ah->curchan;
-       struct ar5416AniState *aniState = &chan->ani;
+       struct ar5416AniState *aniState = &ah->ani;
        s32 value, value2;
 
        switch (cmd & ah->ani_function) {
@@ -1207,7 +1207,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_channel *chan = ah->curchan;
-       struct ar5416AniState *aniState = &chan->ani;
+       struct ar5416AniState *aniState = &ah->ani;
        struct ath9k_ani_default *iniDef;
        u32 val;
 
@@ -1251,7 +1251,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
        /* these levels just got reset to defaults by the INI */
        aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
        aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
-       aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
+       aniState->ofdmWeakSigDetect = true;
        aniState->mrcCCK = false; /* not available on pre AR9003 */
 }
 
index 830daa1..8dc2d08 100644 (file)
@@ -38,10 +38,6 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah)
        else
                INIT_INI_ARRAY(&ah->iniPcieSerdes,
                           ar9280PciePhy_clkreq_always_on_L1_9280);
-#ifdef CONFIG_PM_SLEEP
-               INIT_INI_ARRAY(&ah->iniPcieSerdesWow,
-                              ar9280PciePhy_awow);
-#endif
 
        if (AR_SREV_9287_11_OR_LATER(ah)) {
                INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1);
index beb6162..4d18c66 100644 (file)
@@ -925,20 +925,6 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = {
        {0x00004044, 0x00000000},
 };
 
-static const u32 ar9280PciePhy_awow[][2] = {
-       /* Addr      allmodes  */
-       {0x00004040, 0x9248fd00},
-       {0x00004040, 0x24924924},
-       {0x00004040, 0xa8000019},
-       {0x00004040, 0x13160820},
-       {0x00004040, 0xe5980560},
-       {0x00004040, 0xc01dcffd},
-       {0x00004040, 0x1aaabe41},
-       {0x00004040, 0xbe105554},
-       {0x00004040, 0x00043007},
-       {0x00004044, 0x00000000},
-};
-
 static const u32 ar9285Modes_9285_1_2[][5] = {
        /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
index e6b92ff..d105e43 100644 (file)
@@ -3563,14 +3563,24 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
 {
        struct ath9k_hw_capabilities *pCap = &ah->caps;
        int chain;
-       u32 regval;
+       u32 regval, value, gpio;
        static const u32 switch_chain_reg[AR9300_MAX_CHAINS] = {
                        AR_PHY_SWITCH_CHAIN_0,
                        AR_PHY_SWITCH_CHAIN_1,
                        AR_PHY_SWITCH_CHAIN_2,
        };
 
-       u32 value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz);
+       if (AR_SREV_9485(ah) && (ar9003_hw_get_rx_gain_idx(ah) == 0)) {
+               if (ah->config.xlna_gpio)
+                       gpio = ah->config.xlna_gpio;
+               else
+                       gpio = AR9300_EXT_LNA_CTL_GPIO_AR9485;
+
+               ath9k_hw_cfg_output(ah, gpio,
+                                   AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED);
+       }
+
+       value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz);
 
        if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
                REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM,
@@ -3596,7 +3606,7 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
         *   7:4 R/W  SWITCH_TABLE_COM_SPDT_WLAN_IDLE
         * SWITCH_TABLE_COM_SPDT_WLAN_IDLE
         */
-       if (AR_SREV_9462_20(ah) || AR_SREV_9565(ah)) {
+       if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565(ah)) {
                value = ar9003_switch_com_spdt_get(ah, is2ghz);
                REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL,
                                AR_SWITCH_TABLE_COM_SPDT_ALL, value);
@@ -3796,7 +3806,13 @@ static void ar9003_hw_atten_apply(struct ath_hw *ah, struct ath9k_channel *chan)
                        REG_RMW_FIELD(ah, ext_atten_reg[i],
                                      AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value);
 
-                       value = ar9003_hw_atten_chain_get_margin(ah, i, chan);
+                       if (AR_SREV_9485(ah) &&
+                           (ar9003_hw_get_rx_gain_idx(ah) == 0) &&
+                           ah->config.xatten_margin_cfg)
+                               value = 5;
+                       else
+                               value = ar9003_hw_atten_chain_get_margin(ah, i, chan);
+
                        REG_RMW_FIELD(ah, ext_atten_reg[i],
                                      AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN,
                                      value);
@@ -4043,8 +4059,9 @@ static void ar9003_hw_thermo_cal_apply(struct ath_hw *ah)
 {
        u32 data, ko, kg;
 
-       if (!AR_SREV_9462_20(ah))
+       if (!AR_SREV_9462_20_OR_LATER(ah))
                return;
+
        ar9300_otp_read_word(ah, 1, &data);
        ko = data & 0xff;
        kg = (data >> 8) & 0xff;
@@ -4546,7 +4563,7 @@ static void ar9003_hw_get_target_power_eeprom(struct ath_hw *ah,
                                                 is2GHz);
 
        for (i = 0; i < ar9300RateSize; i++) {
-               ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n",
+               ath_dbg(common, REGULATORY, "TPC[%02d] 0x%08x\n",
                        i, targetPowerValT2[i]);
        }
 }
@@ -4736,7 +4753,7 @@ tempslope:
                              AR_PHY_TPC_19_ALPHA_THERM, temp_slope);
        }
 
-       if (AR_SREV_9462_20(ah))
+       if (AR_SREV_9462_20_OR_LATER(ah))
                REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1,
                              AR_PHY_TPC_19_B1_ALPHA_THERM, temp_slope);
 
@@ -5272,7 +5289,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
                return;
 
        for (i = 0; i < ar9300RateSize; i++) {
-               ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n",
+               ath_dbg(common, REGULATORY, "TPC[%02d] 0x%08x\n",
                        i, targetPowerValT2[i]);
        }
 
index a3523c9..d402cb3 100644 (file)
@@ -24,6 +24,7 @@
 #include "ar955x_1p0_initvals.h"
 #include "ar9580_1p0_initvals.h"
 #include "ar9462_2p0_initvals.h"
+#include "ar9462_2p1_initvals.h"
 #include "ar9565_1p0_initvals.h"
 
 /* General hardware code for the AR9003 hadware family */
@@ -197,6 +198,31 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
 
                INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
                                ar9485_1_1_pcie_phy_clkreq_disable_L1);
+       } else if (AR_SREV_9462_21(ah)) {
+               INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
+                              ar9462_2p1_mac_core);
+               INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST],
+                              ar9462_2p1_mac_postamble);
+               INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+                              ar9462_2p1_baseband_core);
+               INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+                              ar9462_2p1_baseband_postamble);
+               INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE],
+                              ar9462_2p1_radio_core);
+               INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST],
+                              ar9462_2p1_radio_postamble);
+               INIT_INI_ARRAY(&ah->ini_radio_post_sys2ant,
+                              ar9462_2p1_radio_postamble_sys2ant);
+               INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE],
+                              ar9462_2p1_soc_preamble);
+               INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST],
+                              ar9462_2p1_soc_postamble);
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                              ar9462_2p1_common_rx_gain);
+               INIT_INI_ARRAY(&ah->iniModesFastClock,
+                              ar9462_2p1_modes_fast_clock);
+               INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
+                              ar9462_2p1_baseband_core_txfir_coeff_japan_2484);
        } else if (AR_SREV_9462_20(ah)) {
 
                INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9462_2p0_mac_core);
@@ -407,6 +433,9 @@ static void ar9003_tx_gain_table_mode0(struct ath_hw *ah)
        else if (AR_SREV_9580(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9580_1p0_lowest_ob_db_tx_gain_table);
+       else if (AR_SREV_9462_21(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                       ar9462_2p1_modes_low_ob_db_tx_gain);
        else if (AR_SREV_9462_20(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9462_modes_low_ob_db_tx_gain_table_2p0);
@@ -438,6 +467,9 @@ static void ar9003_tx_gain_table_mode1(struct ath_hw *ah)
        else if (AR_SREV_9550(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar955x_1p0_modes_no_xpa_tx_gain_table);
+       else if (AR_SREV_9462_21(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                       ar9462_2p1_modes_high_ob_db_tx_gain);
        else if (AR_SREV_9462_20(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9462_modes_high_ob_db_tx_gain_table_2p0);
@@ -507,6 +539,12 @@ static void ar9003_tx_gain_table_mode4(struct ath_hw *ah)
        else if (AR_SREV_9580(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9580_1p0_mixed_ob_db_tx_gain_table);
+       else if (AR_SREV_9462_21(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                      ar9462_2p1_modes_mix_ob_db_tx_gain);
+       else if (AR_SREV_9462_20(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                      ar9462_modes_mix_ob_db_tx_gain_table_2p0);
        else
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9300Modes_mixed_ob_db_tx_gain_table_2p2);
@@ -584,6 +622,9 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah)
        } else if (AR_SREV_9580(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                                ar9580_1p0_rx_gain_table);
+       else if (AR_SREV_9462_21(ah))
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                               ar9462_2p1_common_rx_gain);
        else if (AR_SREV_9462_20(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                                ar9462_common_rx_gain_table_2p0);
@@ -606,6 +647,9 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah)
        else if (AR_SREV_9485_11(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                        ar9485Common_wo_xlna_rx_gain_1_1);
+       else if (AR_SREV_9462_21(ah))
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                       ar9462_2p1_common_wo_xlna_rx_gain);
        else if (AR_SREV_9462_20(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                        ar9462_common_wo_xlna_rx_gain_table_2p0);
@@ -627,9 +671,40 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah)
 
 static void ar9003_rx_gain_table_mode2(struct ath_hw *ah)
 {
-       if (AR_SREV_9462_20(ah))
+       if (AR_SREV_9462_21(ah)) {
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                              ar9462_2p1_common_mixed_rx_gain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_core,
+                              ar9462_2p1_baseband_core_mix_rxgain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_postamble,
+                              ar9462_2p1_baseband_postamble_mix_rxgain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                              ar9462_2p1_baseband_postamble_5g_xlna);
+       } else if (AR_SREV_9462_20(ah)) {
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                               ar9462_common_mixed_rx_gain_table_2p0);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_core,
+                              ar9462_2p0_baseband_core_mix_rxgain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_postamble,
+                              ar9462_2p0_baseband_postamble_mix_rxgain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                              ar9462_2p0_baseband_postamble_5g_xlna);
+       }
+}
+
+static void ar9003_rx_gain_table_mode3(struct ath_hw *ah)
+{
+       if (AR_SREV_9462_21(ah)) {
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                              ar9462_2p1_common_5g_xlna_only_rx_gain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                              ar9462_2p1_baseband_postamble_5g_xlna);
+       } else if (AR_SREV_9462_20(ah)) {
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                              ar9462_2p0_5g_xlna_only_rxgain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                              ar9462_2p0_baseband_postamble_5g_xlna);
+       }
 }
 
 static void ar9003_rx_gain_table_apply(struct ath_hw *ah)
@@ -645,6 +720,9 @@ static void ar9003_rx_gain_table_apply(struct ath_hw *ah)
        case 2:
                ar9003_rx_gain_table_mode2(ah);
                break;
+       case 3:
+               ar9003_rx_gain_table_mode3(ah);
+               break;
        }
 }
 
index 301bf72..5163abd 100644 (file)
@@ -469,6 +469,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
 
        rxs->rs_status = 0;
        rxs->rs_flags =  0;
+       rxs->flag =  0;
 
        rxs->rs_datalen = rxsp->status2 & AR_DataLen;
        rxs->rs_tstamp =  rxsp->status3;
@@ -493,8 +494,8 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
        rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0;
        rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0;
        rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7);
-       rxs->rs_flags  = (rxsp->status4 & AR_GI) ? ATH9K_RX_GI : 0;
-       rxs->rs_flags  |= (rxsp->status4 & AR_2040) ? ATH9K_RX_2040 : 0;
+       rxs->flag  |= (rxsp->status4 & AR_GI) ? RX_FLAG_SHORT_GI : 0;
+       rxs->flag  |= (rxsp->status4 & AR_2040) ? RX_FLAG_40MHZ : 0;
 
        rxs->evm0 = rxsp->status6;
        rxs->evm1 = rxsp->status7;
index 09c1f9d..6343cc9 100644 (file)
@@ -454,6 +454,8 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
                if (accum_cnt <= thresh_accum_cnt)
                        continue;
 
+               max_index++;
+
                /* sum(tx amplitude) */
                accum_tx = ((data_L[i] >> 16) & 0xffff) |
                    ((data_U[i] & 0x7ff) << 16);
@@ -468,20 +470,21 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
 
                accum_tx <<= scale_factor;
                accum_rx <<= scale_factor;
-               x_est[i + 1] = (((accum_tx + accum_cnt) / accum_cnt) + 32) >>
-                   scale_factor;
+               x_est[max_index] =
+                       (((accum_tx + accum_cnt) / accum_cnt) + 32) >>
+                       scale_factor;
 
-               Y[i + 1] = ((((accum_rx + accum_cnt) / accum_cnt) + 32) >>
+               Y[max_index] =
+                       ((((accum_rx + accum_cnt) / accum_cnt) + 32) >>
                            scale_factor) +
-                           (1 << scale_factor) * max_index + 16;
+                       (1 << scale_factor) * i + 16;
 
                if (accum_ang >= (1 << 26))
                        accum_ang -= 1 << 27;
 
-               theta[i + 1] = ((accum_ang * (1 << scale_factor)) + accum_cnt) /
-                   accum_cnt;
-
-               max_index++;
+               theta[max_index] =
+                       ((accum_ang * (1 << scale_factor)) + accum_cnt) /
+                       accum_cnt;
        }
 
        /*
index e1714d7..1f694ab 100644 (file)
@@ -735,22 +735,53 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
                return -EINVAL;
        }
 
+       /*
+        * SOC, MAC, BB, RADIO initvals.
+        */
        for (i = 0; i < ATH_INI_NUM_SPLIT; i++) {
                ar9003_hw_prog_ini(ah, &ah->iniSOC[i], modesIndex);
                ar9003_hw_prog_ini(ah, &ah->iniMac[i], modesIndex);
                ar9003_hw_prog_ini(ah, &ah->iniBB[i], modesIndex);
                ar9003_hw_prog_ini(ah, &ah->iniRadio[i], modesIndex);
-               if (i == ATH_INI_POST && AR_SREV_9462_20(ah))
+               if (i == ATH_INI_POST && AR_SREV_9462_20_OR_LATER(ah))
                        ar9003_hw_prog_ini(ah,
                                           &ah->ini_radio_post_sys2ant,
                                           modesIndex);
        }
 
+       /*
+        * RXGAIN initvals.
+        */
        REG_WRITE_ARRAY(&ah->iniModesRxGain, 1, regWrites);
+
+       if (AR_SREV_9462_20_OR_LATER(ah)) {
+               /*
+                * CUS217 mix LNA mode.
+                */
+               if (ar9003_hw_get_rx_gain_idx(ah) == 2) {
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_core,
+                                       1, regWrites);
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_postamble,
+                                       modesIndex, regWrites);
+               }
+
+               /*
+                * 5G-XLNA
+                */
+               if ((ar9003_hw_get_rx_gain_idx(ah) == 2) ||
+                   (ar9003_hw_get_rx_gain_idx(ah) == 3)) {
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                                       modesIndex, regWrites);
+               }
+       }
+
        if (AR_SREV_9550(ah))
                REG_WRITE_ARRAY(&ah->ini_modes_rx_gain_bounds, modesIndex,
                                regWrites);
 
+       /*
+        * TXGAIN initvals.
+        */
        if (AR_SREV_9550(ah)) {
                int modes_txgain_index;
 
@@ -772,8 +803,14 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
                REG_WRITE_ARRAY(&ah->iniModesFastClock,
                                modesIndex, regWrites);
 
+       /*
+        * Clock frequency initvals.
+        */
        REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites);
 
+       /*
+        * JAPAN regulatory.
+        */
        if (chan->channel == 2484)
                ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1);
 
@@ -905,7 +942,12 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
 {
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_channel *chan = ah->curchan;
-       struct ar5416AniState *aniState = &chan->ani;
+       struct ar5416AniState *aniState = &ah->ani;
+       int m1ThreshLow, m2ThreshLow;
+       int m1Thresh, m2Thresh;
+       int m2CountThr, m2CountThrLow;
+       int m1ThreshLowExt, m2ThreshLowExt;
+       int m1ThreshExt, m2ThreshExt;
        s32 value, value2;
 
        switch (cmd & ah->ani_function) {
@@ -919,6 +961,61 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
                 */
                u32 on = param ? 1 : 0;
 
+               if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
+                       goto skip_ws_det;
+
+               m1ThreshLow = on ?
+                       aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
+               m2ThreshLow = on ?
+                       aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
+               m1Thresh = on ?
+                       aniState->iniDef.m1Thresh : m1Thresh_off;
+               m2Thresh = on ?
+                       aniState->iniDef.m2Thresh : m2Thresh_off;
+               m2CountThr = on ?
+                       aniState->iniDef.m2CountThr : m2CountThr_off;
+               m2CountThrLow = on ?
+                       aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
+               m1ThreshLowExt = on ?
+                       aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
+               m2ThreshLowExt = on ?
+                       aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
+               m1ThreshExt = on ?
+                       aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
+               m2ThreshExt = on ?
+                       aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
+
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
+                             m1ThreshLow);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
+                             m2ThreshLow);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M1_THRESH,
+                             m1Thresh);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M2_THRESH,
+                             m2Thresh);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M2COUNT_THR,
+                             m2CountThr);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
+                             m2CountThrLow);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
+                             m1ThreshLowExt);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
+                             m2ThreshLowExt);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M1_THRESH,
+                             m1ThreshExt);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M2_THRESH,
+                             m2ThreshExt);
+skip_ws_det:
                if (on)
                        REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
                                    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
@@ -1173,7 +1270,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
        struct ath9k_ani_default *iniDef;
        u32 val;
 
-       aniState = &ah->curchan->ani;
+       aniState = &ah->ani;
        iniDef = &aniState->iniDef;
 
        ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n",
@@ -1214,7 +1311,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
        /* these levels just got reset to defaults by the INI */
        aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
        aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
-       aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
+       aniState->ofdmWeakSigDetect = true;
        aniState->mrcCCK = true;
 }
 
@@ -1415,7 +1512,7 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
        ar9003_hw_prog_ini(ah, &ah->iniBB[ATH_INI_POST], modesIndex);
        ar9003_hw_prog_ini(ah, &ah->iniRadio[ATH_INI_POST], modesIndex);
 
-       if (AR_SREV_9462_20(ah))
+       if (AR_SREV_9462_20_OR_LATER(ah))
                ar9003_hw_prog_ini(ah, &ah->ini_radio_post_sys2ant,
                                   modesIndex);
 
index e717741..d4d39f3 100644 (file)
 
 #define AR_PHY_CCA_NOM_VAL_9330_2GHZ          -118
 
+#define AR9300_EXT_LNA_CTL_GPIO_AR9485 9
+
 /*
  * AGC Field Definitions
  */
 #define AR_PHY_TPC_5_B1         (AR_SM1_BASE + 0x208)
 #define AR_PHY_TPC_6_B1         (AR_SM1_BASE + 0x20c)
 #define AR_PHY_TPC_11_B1        (AR_SM1_BASE + 0x220)
-#define AR_PHY_PDADC_TAB_1     (AR_SM1_BASE + (AR_SREV_AR9462(ah) ? \
+#define AR_PHY_PDADC_TAB_1     (AR_SM1_BASE + (AR_SREV_9462_20_OR_LATER(ah) ? \
                                        0x280 : 0x240))
 #define AR_PHY_TPC_19_B1       (AR_SM1_BASE + 0x240)
 #define AR_PHY_TPC_19_B1_ALPHA_THERM           0xff
 #define AR_GLB_GPIO_CONTROL    (AR_GLB_BASE)
 #define AR_PHY_GLB_CONTROL     (AR_GLB_BASE + 0x44)
 #define AR_GLB_SCRATCH(_ah)    (AR_GLB_BASE + \
-                                       (AR_SREV_9462_20(_ah) ? 0x4c : 0x50))
+                                       (AR_SREV_9462_20_OR_LATER(_ah) ? 0x4c : 0x50))
 #define AR_GLB_STATUS          (AR_GLB_BASE + 0x48)
 
 /*
index 999ab08..092b9d4 100644 (file)
@@ -78,7 +78,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = {
        {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
        {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
        {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
-       {0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
+       {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
        {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
        {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
        {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
@@ -879,6 +879,69 @@ static const u32 ar9462_2p0_radio_postamble[][5] = {
        {0x0001650c, 0x48000000, 0x40000000, 0x40000000, 0x40000000},
 };
 
+static const u32 ar9462_modes_mix_ob_db_tx_gain_table_2p0[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002},
+       {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+       {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+       {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+       {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x0000a410, 0x0000d0da, 0x0000d0da, 0x0000d0de, 0x0000d0de},
+       {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
+       {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002},
+       {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004},
+       {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200},
+       {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202},
+       {0x0000a514, 0x18022622, 0x18022622, 0x12000400, 0x12000400},
+       {0x0000a518, 0x1b022822, 0x1b022822, 0x16000402, 0x16000402},
+       {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404},
+       {0x0000a520, 0x22022c41, 0x22022c41, 0x1c000603, 0x1c000603},
+       {0x0000a524, 0x28023042, 0x28023042, 0x21000a02, 0x21000a02},
+       {0x0000a528, 0x2c023044, 0x2c023044, 0x25000a04, 0x25000a04},
+       {0x0000a52c, 0x2f023644, 0x2f023644, 0x28000a20, 0x28000a20},
+       {0x0000a530, 0x34025643, 0x34025643, 0x2c000e20, 0x2c000e20},
+       {0x0000a534, 0x38025a44, 0x38025a44, 0x30000e22, 0x30000e22},
+       {0x0000a538, 0x3b025e45, 0x3b025e45, 0x34000e24, 0x34000e24},
+       {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x38001640, 0x38001640},
+       {0x0000a540, 0x48025e6c, 0x48025e6c, 0x3c001660, 0x3c001660},
+       {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3f001861, 0x3f001861},
+       {0x0000a548, 0x55025eb3, 0x55025eb3, 0x43001a81, 0x43001a81},
+       {0x0000a54c, 0x58025ef3, 0x58025ef3, 0x47001a83, 0x47001a83},
+       {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x4a001c84, 0x4a001c84},
+       {0x0000a554, 0x62025f56, 0x62025f56, 0x4e001ce3, 0x4e001ce3},
+       {0x0000a558, 0x66027f56, 0x66027f56, 0x52001ce5, 0x52001ce5},
+       {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x56001ce9, 0x56001ce9},
+       {0x0000a560, 0x70049f56, 0x70049f56, 0x5a001ceb, 0x5a001ceb},
+       {0x0000a564, 0x751ffff6, 0x751ffff6, 0x5c001eec, 0x5c001eec},
+       {0x0000a568, 0x751ffff6, 0x751ffff6, 0x5e001ef0, 0x5e001ef0},
+       {0x0000a56c, 0x751ffff6, 0x751ffff6, 0x60001ef4, 0x60001ef4},
+       {0x0000a570, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6},
+       {0x0000a574, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6},
+       {0x0000a578, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6},
+       {0x0000a57c, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6},
+       {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000},
+       {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000},
+       {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501},
+       {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501},
+       {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03},
+       {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04},
+       {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04},
+       {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+       {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+       {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+       {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+};
+
 static const u32 ar9462_modes_high_ob_db_tx_gain_table_2p0[][5] = {
        /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
        {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002},
@@ -1449,4 +1512,284 @@ static const u32 ar9462_common_mixed_rx_gain_table_2p0[][2] = {
        {0x0000b1fc, 0x00000196},
 };
 
+static const u32 ar9462_2p0_baseband_postamble_5g_xlna[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282},
+};
+
+static const u32 ar9462_2p0_5g_xlna_only_rxgain[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a000, 0x00010000},
+       {0x0000a004, 0x00030002},
+       {0x0000a008, 0x00050004},
+       {0x0000a00c, 0x00810080},
+       {0x0000a010, 0x00830082},
+       {0x0000a014, 0x01810180},
+       {0x0000a018, 0x01830182},
+       {0x0000a01c, 0x01850184},
+       {0x0000a020, 0x01890188},
+       {0x0000a024, 0x018b018a},
+       {0x0000a028, 0x018d018c},
+       {0x0000a02c, 0x03820190},
+       {0x0000a030, 0x03840383},
+       {0x0000a034, 0x03880385},
+       {0x0000a038, 0x038a0389},
+       {0x0000a03c, 0x038c038b},
+       {0x0000a040, 0x0390038d},
+       {0x0000a044, 0x03920391},
+       {0x0000a048, 0x03940393},
+       {0x0000a04c, 0x03960395},
+       {0x0000a050, 0x00000000},
+       {0x0000a054, 0x00000000},
+       {0x0000a058, 0x00000000},
+       {0x0000a05c, 0x00000000},
+       {0x0000a060, 0x00000000},
+       {0x0000a064, 0x00000000},
+       {0x0000a068, 0x00000000},
+       {0x0000a06c, 0x00000000},
+       {0x0000a070, 0x00000000},
+       {0x0000a074, 0x00000000},
+       {0x0000a078, 0x00000000},
+       {0x0000a07c, 0x00000000},
+       {0x0000a080, 0x29292929},
+       {0x0000a084, 0x29292929},
+       {0x0000a088, 0x29292929},
+       {0x0000a08c, 0x29292929},
+       {0x0000a090, 0x22292929},
+       {0x0000a094, 0x1d1d2222},
+       {0x0000a098, 0x0c111117},
+       {0x0000a09c, 0x00030303},
+       {0x0000a0a0, 0x00000000},
+       {0x0000a0a4, 0x00000000},
+       {0x0000a0a8, 0x00000000},
+       {0x0000a0ac, 0x00000000},
+       {0x0000a0b0, 0x00000000},
+       {0x0000a0b4, 0x00000000},
+       {0x0000a0b8, 0x00000000},
+       {0x0000a0bc, 0x00000000},
+       {0x0000a0c0, 0x001f0000},
+       {0x0000a0c4, 0x01000101},
+       {0x0000a0c8, 0x011e011f},
+       {0x0000a0cc, 0x011c011d},
+       {0x0000a0d0, 0x02030204},
+       {0x0000a0d4, 0x02010202},
+       {0x0000a0d8, 0x021f0200},
+       {0x0000a0dc, 0x0302021e},
+       {0x0000a0e0, 0x03000301},
+       {0x0000a0e4, 0x031e031f},
+       {0x0000a0e8, 0x0402031d},
+       {0x0000a0ec, 0x04000401},
+       {0x0000a0f0, 0x041e041f},
+       {0x0000a0f4, 0x0502041d},
+       {0x0000a0f8, 0x05000501},
+       {0x0000a0fc, 0x051e051f},
+       {0x0000a100, 0x06010602},
+       {0x0000a104, 0x061f0600},
+       {0x0000a108, 0x061d061e},
+       {0x0000a10c, 0x07020703},
+       {0x0000a110, 0x07000701},
+       {0x0000a114, 0x00000000},
+       {0x0000a118, 0x00000000},
+       {0x0000a11c, 0x00000000},
+       {0x0000a120, 0x00000000},
+       {0x0000a124, 0x00000000},
+       {0x0000a128, 0x00000000},
+       {0x0000a12c, 0x00000000},
+       {0x0000a130, 0x00000000},
+       {0x0000a134, 0x00000000},
+       {0x0000a138, 0x00000000},
+       {0x0000a13c, 0x00000000},
+       {0x0000a140, 0x001f0000},
+       {0x0000a144, 0x01000101},
+       {0x0000a148, 0x011e011f},
+       {0x0000a14c, 0x011c011d},
+       {0x0000a150, 0x02030204},
+       {0x0000a154, 0x02010202},
+       {0x0000a158, 0x021f0200},
+       {0x0000a15c, 0x0302021e},
+       {0x0000a160, 0x03000301},
+       {0x0000a164, 0x031e031f},
+       {0x0000a168, 0x0402031d},
+       {0x0000a16c, 0x04000401},
+       {0x0000a170, 0x041e041f},
+       {0x0000a174, 0x0502041d},
+       {0x0000a178, 0x05000501},
+       {0x0000a17c, 0x051e051f},
+       {0x0000a180, 0x06010602},
+       {0x0000a184, 0x061f0600},
+       {0x0000a188, 0x061d061e},
+       {0x0000a18c, 0x07020703},
+       {0x0000a190, 0x07000701},
+       {0x0000a194, 0x00000000},
+       {0x0000a198, 0x00000000},
+       {0x0000a19c, 0x00000000},
+       {0x0000a1a0, 0x00000000},
+       {0x0000a1a4, 0x00000000},
+       {0x0000a1a8, 0x00000000},
+       {0x0000a1ac, 0x00000000},
+       {0x0000a1b0, 0x00000000},
+       {0x0000a1b4, 0x00000000},
+       {0x0000a1b8, 0x00000000},
+       {0x0000a1bc, 0x00000000},
+       {0x0000a1c0, 0x00000000},
+       {0x0000a1c4, 0x00000000},
+       {0x0000a1c8, 0x00000000},
+       {0x0000a1cc, 0x00000000},
+       {0x0000a1d0, 0x00000000},
+       {0x0000a1d4, 0x00000000},
+       {0x0000a1d8, 0x00000000},
+       {0x0000a1dc, 0x00000000},
+       {0x0000a1e0, 0x00000000},
+       {0x0000a1e4, 0x00000000},
+       {0x0000a1e8, 0x00000000},
+       {0x0000a1ec, 0x00000000},
+       {0x0000a1f0, 0x00000396},
+       {0x0000a1f4, 0x00000396},
+       {0x0000a1f8, 0x00000396},
+       {0x0000a1fc, 0x00000196},
+       {0x0000b000, 0x00010000},
+       {0x0000b004, 0x00030002},
+       {0x0000b008, 0x00050004},
+       {0x0000b00c, 0x00810080},
+       {0x0000b010, 0x00830082},
+       {0x0000b014, 0x01810180},
+       {0x0000b018, 0x01830182},
+       {0x0000b01c, 0x01850184},
+       {0x0000b020, 0x02810280},
+       {0x0000b024, 0x02830282},
+       {0x0000b028, 0x02850284},
+       {0x0000b02c, 0x02890288},
+       {0x0000b030, 0x028b028a},
+       {0x0000b034, 0x0388028c},
+       {0x0000b038, 0x038a0389},
+       {0x0000b03c, 0x038c038b},
+       {0x0000b040, 0x0390038d},
+       {0x0000b044, 0x03920391},
+       {0x0000b048, 0x03940393},
+       {0x0000b04c, 0x03960395},
+       {0x0000b050, 0x00000000},
+       {0x0000b054, 0x00000000},
+       {0x0000b058, 0x00000000},
+       {0x0000b05c, 0x00000000},
+       {0x0000b060, 0x00000000},
+       {0x0000b064, 0x00000000},
+       {0x0000b068, 0x00000000},
+       {0x0000b06c, 0x00000000},
+       {0x0000b070, 0x00000000},
+       {0x0000b074, 0x00000000},
+       {0x0000b078, 0x00000000},
+       {0x0000b07c, 0x00000000},
+       {0x0000b080, 0x2a2d2f32},
+       {0x0000b084, 0x21232328},
+       {0x0000b088, 0x19191c1e},
+       {0x0000b08c, 0x12141417},
+       {0x0000b090, 0x07070e0e},
+       {0x0000b094, 0x03030305},
+       {0x0000b098, 0x00000003},
+       {0x0000b09c, 0x00000000},
+       {0x0000b0a0, 0x00000000},
+       {0x0000b0a4, 0x00000000},
+       {0x0000b0a8, 0x00000000},
+       {0x0000b0ac, 0x00000000},
+       {0x0000b0b0, 0x00000000},
+       {0x0000b0b4, 0x00000000},
+       {0x0000b0b8, 0x00000000},
+       {0x0000b0bc, 0x00000000},
+       {0x0000b0c0, 0x003f0020},
+       {0x0000b0c4, 0x00400041},
+       {0x0000b0c8, 0x0140005f},
+       {0x0000b0cc, 0x0160015f},
+       {0x0000b0d0, 0x017e017f},
+       {0x0000b0d4, 0x02410242},
+       {0x0000b0d8, 0x025f0240},
+       {0x0000b0dc, 0x027f0260},
+       {0x0000b0e0, 0x0341027e},
+       {0x0000b0e4, 0x035f0340},
+       {0x0000b0e8, 0x037f0360},
+       {0x0000b0ec, 0x04400441},
+       {0x0000b0f0, 0x0460045f},
+       {0x0000b0f4, 0x0541047f},
+       {0x0000b0f8, 0x055f0540},
+       {0x0000b0fc, 0x057f0560},
+       {0x0000b100, 0x06400641},
+       {0x0000b104, 0x0660065f},
+       {0x0000b108, 0x067e067f},
+       {0x0000b10c, 0x07410742},
+       {0x0000b110, 0x075f0740},
+       {0x0000b114, 0x077f0760},
+       {0x0000b118, 0x07800781},
+       {0x0000b11c, 0x07a0079f},
+       {0x0000b120, 0x07c107bf},
+       {0x0000b124, 0x000007c0},
+       {0x0000b128, 0x00000000},
+       {0x0000b12c, 0x00000000},
+       {0x0000b130, 0x00000000},
+       {0x0000b134, 0x00000000},
+       {0x0000b138, 0x00000000},
+       {0x0000b13c, 0x00000000},
+       {0x0000b140, 0x003f0020},
+       {0x0000b144, 0x00400041},
+       {0x0000b148, 0x0140005f},
+       {0x0000b14c, 0x0160015f},
+       {0x0000b150, 0x017e017f},
+       {0x0000b154, 0x02410242},
+       {0x0000b158, 0x025f0240},
+       {0x0000b15c, 0x027f0260},
+       {0x0000b160, 0x0341027e},
+       {0x0000b164, 0x035f0340},
+       {0x0000b168, 0x037f0360},
+       {0x0000b16c, 0x04400441},
+       {0x0000b170, 0x0460045f},
+       {0x0000b174, 0x0541047f},
+       {0x0000b178, 0x055f0540},
+       {0x0000b17c, 0x057f0560},
+       {0x0000b180, 0x06400641},
+       {0x0000b184, 0x0660065f},
+       {0x0000b188, 0x067e067f},
+       {0x0000b18c, 0x07410742},
+       {0x0000b190, 0x075f0740},
+       {0x0000b194, 0x077f0760},
+       {0x0000b198, 0x07800781},
+       {0x0000b19c, 0x07a0079f},
+       {0x0000b1a0, 0x07c107bf},
+       {0x0000b1a4, 0x000007c0},
+       {0x0000b1a8, 0x00000000},
+       {0x0000b1ac, 0x00000000},
+       {0x0000b1b0, 0x00000000},
+       {0x0000b1b4, 0x00000000},
+       {0x0000b1b8, 0x00000000},
+       {0x0000b1bc, 0x00000000},
+       {0x0000b1c0, 0x00000000},
+       {0x0000b1c4, 0x00000000},
+       {0x0000b1c8, 0x00000000},
+       {0x0000b1cc, 0x00000000},
+       {0x0000b1d0, 0x00000000},
+       {0x0000b1d4, 0x00000000},
+       {0x0000b1d8, 0x00000000},
+       {0x0000b1dc, 0x00000000},
+       {0x0000b1e0, 0x00000000},
+       {0x0000b1e4, 0x00000000},
+       {0x0000b1e8, 0x00000000},
+       {0x0000b1ec, 0x00000000},
+       {0x0000b1f0, 0x00000396},
+       {0x0000b1f4, 0x00000396},
+       {0x0000b1f8, 0x00000396},
+       {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_2p0_baseband_core_mix_rxgain[][2] = {
+       /* Addr      allmodes  */
+       {0x00009fd0, 0x0a2d6b93},
+};
+
+static const u32 ar9462_2p0_baseband_postamble_mix_rxgain[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009820, 0x206a022e, 0x206a022e, 0x206a01ae, 0x206a01ae},
+       {0x00009824, 0x63c640de, 0x5ac640d0, 0x63c640da, 0x63c640da},
+       {0x00009828, 0x0796be89, 0x0696b081, 0x0916be81, 0x0916be81},
+       {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000d8, 0x6c4000d8},
+       {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec86d2e, 0x7ec86d2e},
+       {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32395c5e},
+};
+
 #endif /* INITVALS_9462_2P0_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p1_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p1_initvals.h
new file mode 100644 (file)
index 0000000..4dbc294
--- /dev/null
@@ -0,0 +1,1774 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INITVALS_9462_2P1_H
+#define INITVALS_9462_2P1_H
+
+/* AR9462 2.1 */
+
+static const u32 ar9462_2p1_mac_core[][2] = {
+       /* Addr      allmodes  */
+       {0x00000008, 0x00000000},
+       {0x00000030, 0x000e0085},
+       {0x00000034, 0x00000005},
+       {0x00000040, 0x00000000},
+       {0x00000044, 0x00000000},
+       {0x00000048, 0x00000008},
+       {0x0000004c, 0x00000010},
+       {0x00000050, 0x00000000},
+       {0x00001040, 0x002ffc0f},
+       {0x00001044, 0x002ffc0f},
+       {0x00001048, 0x002ffc0f},
+       {0x0000104c, 0x002ffc0f},
+       {0x00001050, 0x002ffc0f},
+       {0x00001054, 0x002ffc0f},
+       {0x00001058, 0x002ffc0f},
+       {0x0000105c, 0x002ffc0f},
+       {0x00001060, 0x002ffc0f},
+       {0x00001064, 0x002ffc0f},
+       {0x000010f0, 0x00000100},
+       {0x00001270, 0x00000000},
+       {0x000012b0, 0x00000000},
+       {0x000012f0, 0x00000000},
+       {0x0000143c, 0x00000000},
+       {0x0000147c, 0x00000000},
+       {0x00001810, 0x0f000003},
+       {0x00008000, 0x00000000},
+       {0x00008004, 0x00000000},
+       {0x00008008, 0x00000000},
+       {0x0000800c, 0x00000000},
+       {0x00008018, 0x00000000},
+       {0x00008020, 0x00000000},
+       {0x00008038, 0x00000000},
+       {0x0000803c, 0x00080000},
+       {0x00008040, 0x00000000},
+       {0x00008044, 0x00000000},
+       {0x00008048, 0x00000000},
+       {0x0000804c, 0xffffffff},
+       {0x00008054, 0x00000000},
+       {0x00008058, 0x00000000},
+       {0x0000805c, 0x000fc78f},
+       {0x00008060, 0x0000000f},
+       {0x00008064, 0x00000000},
+       {0x00008070, 0x00000310},
+       {0x00008074, 0x00000020},
+       {0x00008078, 0x00000000},
+       {0x0000809c, 0x0000000f},
+       {0x000080a0, 0x00000000},
+       {0x000080a4, 0x02ff0000},
+       {0x000080a8, 0x0e070605},
+       {0x000080ac, 0x0000000d},
+       {0x000080b0, 0x00000000},
+       {0x000080b4, 0x00000000},
+       {0x000080b8, 0x00000000},
+       {0x000080bc, 0x00000000},
+       {0x000080c0, 0x2a800000},
+       {0x000080c4, 0x06900168},
+       {0x000080c8, 0x13881c20},
+       {0x000080cc, 0x01f40000},
+       {0x000080d0, 0x00252500},
+       {0x000080d4, 0x00b00005},
+       {0x000080d8, 0x00400002},
+       {0x000080dc, 0x00000000},
+       {0x000080e0, 0xffffffff},
+       {0x000080e4, 0x0000ffff},
+       {0x000080e8, 0x3f3f3f3f},
+       {0x000080ec, 0x00000000},
+       {0x000080f0, 0x00000000},
+       {0x000080f4, 0x00000000},
+       {0x000080fc, 0x00020000},
+       {0x00008100, 0x00000000},
+       {0x00008108, 0x00000052},
+       {0x0000810c, 0x00000000},
+       {0x00008110, 0x00000000},
+       {0x00008114, 0x000007ff},
+       {0x00008118, 0x000000aa},
+       {0x0000811c, 0x00003210},
+       {0x00008124, 0x00000000},
+       {0x00008128, 0x00000000},
+       {0x0000812c, 0x00000000},
+       {0x00008130, 0x00000000},
+       {0x00008134, 0x00000000},
+       {0x00008138, 0x00000000},
+       {0x0000813c, 0x0000ffff},
+       {0x00008144, 0xffffffff},
+       {0x00008168, 0x00000000},
+       {0x0000816c, 0x00000000},
+       {0x00008170, 0x18486e00},
+       {0x00008174, 0x33332210},
+       {0x00008178, 0x00000000},
+       {0x0000817c, 0x00020000},
+       {0x000081c4, 0x33332210},
+       {0x000081c8, 0x00000000},
+       {0x000081cc, 0x00000000},
+       {0x000081d4, 0x00000000},
+       {0x000081ec, 0x00000000},
+       {0x000081f0, 0x00000000},
+       {0x000081f4, 0x00000000},
+       {0x000081f8, 0x00000000},
+       {0x000081fc, 0x00000000},
+       {0x00008240, 0x00100000},
+       {0x00008244, 0x0010f400},
+       {0x00008248, 0x00000800},
+       {0x0000824c, 0x0001e800},
+       {0x00008250, 0x00000000},
+       {0x00008254, 0x00000000},
+       {0x00008258, 0x00000000},
+       {0x0000825c, 0x40000000},
+       {0x00008260, 0x00080922},
+       {0x00008264, 0x99c00010},
+       {0x00008268, 0xffffffff},
+       {0x0000826c, 0x0000ffff},
+       {0x00008270, 0x00000000},
+       {0x00008274, 0x40000000},
+       {0x00008278, 0x003e4180},
+       {0x0000827c, 0x00000004},
+       {0x00008284, 0x0000002c},
+       {0x00008288, 0x0000002c},
+       {0x0000828c, 0x000000ff},
+       {0x00008294, 0x00000000},
+       {0x00008298, 0x00000000},
+       {0x0000829c, 0x00000000},
+       {0x00008300, 0x00000140},
+       {0x00008314, 0x00000000},
+       {0x0000831c, 0x0000010d},
+       {0x00008328, 0x00000000},
+       {0x0000832c, 0x0000001f},
+       {0x00008330, 0x00000302},
+       {0x00008334, 0x00000700},
+       {0x00008338, 0xffff0000},
+       {0x0000833c, 0x02400000},
+       {0x00008340, 0x000107ff},
+       {0x00008344, 0xaa48107b},
+       {0x00008348, 0x008f0000},
+       {0x0000835c, 0x00000000},
+       {0x00008360, 0xffffffff},
+       {0x00008364, 0xffffffff},
+       {0x00008368, 0x00000000},
+       {0x00008370, 0x00000000},
+       {0x00008374, 0x000000ff},
+       {0x00008378, 0x00000000},
+       {0x0000837c, 0x00000000},
+       {0x00008380, 0xffffffff},
+       {0x00008384, 0xffffffff},
+       {0x00008390, 0xffffffff},
+       {0x00008394, 0xffffffff},
+       {0x00008398, 0x00000000},
+       {0x0000839c, 0x00000000},
+       {0x000083a4, 0x0000fa14},
+       {0x000083a8, 0x000f0c00},
+       {0x000083ac, 0x33332210},
+       {0x000083b0, 0x33332210},
+       {0x000083b4, 0x33332210},
+       {0x000083b8, 0x33332210},
+       {0x000083bc, 0x00000000},
+       {0x000083c0, 0x00000000},
+       {0x000083c4, 0x00000000},
+       {0x000083c8, 0x00000000},
+       {0x000083cc, 0x00000200},
+       {0x000083d0, 0x000301ff},
+};
+
+static const u32 ar9462_2p1_mac_postamble[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
+       {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c},
+       {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38},
+       {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00},
+       {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b},
+       {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810},
+       {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a},
+       {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440},
+};
+
+static const u32 ar9462_2p1_baseband_core[][2] = {
+       /* Addr      allmodes  */
+       {0x00009800, 0xafe68e30},
+       {0x00009804, 0xfd14e000},
+       {0x00009808, 0x9c0a9f6b},
+       {0x0000980c, 0x04900000},
+       {0x00009814, 0x9280c00a},
+       {0x00009818, 0x00000000},
+       {0x0000981c, 0x00020028},
+       {0x00009834, 0x6400a290},
+       {0x00009838, 0x0108ecff},
+       {0x0000983c, 0x0d000600},
+       {0x00009880, 0x201fff00},
+       {0x00009884, 0x00001042},
+       {0x000098a4, 0x00200400},
+       {0x000098b0, 0x32440bbe},
+       {0x000098d0, 0x004b6a8e},
+       {0x000098d4, 0x00000820},
+       {0x000098dc, 0x00000000},
+       {0x000098e4, 0x01ffffff},
+       {0x000098e8, 0x01ffffff},
+       {0x000098ec, 0x01ffffff},
+       {0x000098f0, 0x00000000},
+       {0x000098f4, 0x00000000},
+       {0x00009bf0, 0x80000000},
+       {0x00009c04, 0xff55ff55},
+       {0x00009c08, 0x0320ff55},
+       {0x00009c0c, 0x00000000},
+       {0x00009c10, 0x00000000},
+       {0x00009c14, 0x00046384},
+       {0x00009c18, 0x05b6b440},
+       {0x00009c1c, 0x00b6b440},
+       {0x00009d00, 0xc080a333},
+       {0x00009d04, 0x40206c10},
+       {0x00009d08, 0x009c4060},
+       {0x00009d0c, 0x9883800a},
+       {0x00009d10, 0x01834061},
+       {0x00009d14, 0x00c0040b},
+       {0x00009d18, 0x00000000},
+       {0x00009e08, 0x0038230c},
+       {0x00009e24, 0x990bb515},
+       {0x00009e28, 0x0c6f0000},
+       {0x00009e30, 0x06336f77},
+       {0x00009e34, 0x6af6532f},
+       {0x00009e38, 0x0cc80c00},
+       {0x00009e40, 0x15262820},
+       {0x00009e4c, 0x00001004},
+       {0x00009e50, 0x00ff03f1},
+       {0x00009e54, 0xe4c555c2},
+       {0x00009e58, 0xfd857722},
+       {0x00009e5c, 0xe9198724},
+       {0x00009fc0, 0x803e4788},
+       {0x00009fc4, 0x0001efb5},
+       {0x00009fcc, 0x40000014},
+       {0x00009fd0, 0x0a193b93},
+       {0x0000a20c, 0x00000000},
+       {0x0000a220, 0x00000000},
+       {0x0000a224, 0x00000000},
+       {0x0000a228, 0x10002310},
+       {0x0000a23c, 0x00000000},
+       {0x0000a244, 0x0c000000},
+       {0x0000a2a0, 0x00000001},
+       {0x0000a2c0, 0x00000001},
+       {0x0000a2c8, 0x00000000},
+       {0x0000a2cc, 0x18c43433},
+       {0x0000a2d4, 0x00000000},
+       {0x0000a2ec, 0x00000000},
+       {0x0000a2f0, 0x00000000},
+       {0x0000a2f4, 0x00000000},
+       {0x0000a2f8, 0x00000000},
+       {0x0000a344, 0x00000000},
+       {0x0000a34c, 0x00000000},
+       {0x0000a350, 0x0000a000},
+       {0x0000a364, 0x00000000},
+       {0x0000a370, 0x00000000},
+       {0x0000a390, 0x00000001},
+       {0x0000a394, 0x00000444},
+       {0x0000a398, 0x001f0e0f},
+       {0x0000a39c, 0x0075393f},
+       {0x0000a3a0, 0xb79f6427},
+       {0x0000a3c0, 0x20202020},
+       {0x0000a3c4, 0x22222220},
+       {0x0000a3c8, 0x20200020},
+       {0x0000a3cc, 0x20202020},
+       {0x0000a3d0, 0x20202020},
+       {0x0000a3d4, 0x20202020},
+       {0x0000a3d8, 0x20202020},
+       {0x0000a3dc, 0x20202020},
+       {0x0000a3e0, 0x20202020},
+       {0x0000a3e4, 0x20202020},
+       {0x0000a3e8, 0x20202020},
+       {0x0000a3ec, 0x20202020},
+       {0x0000a3f0, 0x00000000},
+       {0x0000a3f4, 0x00000006},
+       {0x0000a3f8, 0x0c9bd380},
+       {0x0000a3fc, 0x000f0f01},
+       {0x0000a400, 0x8fa91f01},
+       {0x0000a404, 0x00000000},
+       {0x0000a408, 0x0e79e5c6},
+       {0x0000a40c, 0x00820820},
+       {0x0000a414, 0x1ce739ce},
+       {0x0000a418, 0x2d001dce},
+       {0x0000a434, 0x00000000},
+       {0x0000a438, 0x00001801},
+       {0x0000a43c, 0x00100000},
+       {0x0000a444, 0x00000000},
+       {0x0000a448, 0x05000080},
+       {0x0000a44c, 0x00000001},
+       {0x0000a450, 0x00010000},
+       {0x0000a454, 0x07000000},
+       {0x0000a644, 0xbfad9d74},
+       {0x0000a648, 0x0048060a},
+       {0x0000a64c, 0x00002037},
+       {0x0000a670, 0x03020100},
+       {0x0000a674, 0x09080504},
+       {0x0000a678, 0x0d0c0b0a},
+       {0x0000a67c, 0x13121110},
+       {0x0000a680, 0x31301514},
+       {0x0000a684, 0x35343332},
+       {0x0000a688, 0x00000036},
+       {0x0000a690, 0x00000838},
+       {0x0000a6b0, 0x0000000a},
+       {0x0000a6b4, 0x00512c01},
+       {0x0000a7c0, 0x00000000},
+       {0x0000a7c4, 0xfffffffc},
+       {0x0000a7c8, 0x00000000},
+       {0x0000a7cc, 0x00000000},
+       {0x0000a7d0, 0x00000000},
+       {0x0000a7d4, 0x00000004},
+       {0x0000a7dc, 0x00000000},
+       {0x0000a7f0, 0x80000000},
+       {0x0000a8d0, 0x004b6a8e},
+       {0x0000a8d4, 0x00000820},
+       {0x0000a8dc, 0x00000000},
+       {0x0000a8f0, 0x00000000},
+       {0x0000a8f4, 0x00000000},
+       {0x0000abf0, 0x80000000},
+       {0x0000b2d0, 0x00000080},
+       {0x0000b2d4, 0x00000000},
+       {0x0000b2ec, 0x00000000},
+       {0x0000b2f0, 0x00000000},
+       {0x0000b2f4, 0x00000000},
+       {0x0000b2f8, 0x00000000},
+       {0x0000b408, 0x0e79e5c0},
+       {0x0000b40c, 0x00820820},
+       {0x0000b420, 0x00000000},
+       {0x0000b6b0, 0x0000000a},
+       {0x0000b6b4, 0x00000001},
+};
+
+static const u32 ar9462_2p1_baseband_postamble[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a800d},
+       {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a01ae},
+       {0x00009824, 0x63c640de, 0x5ac640d0, 0x5ac640d0, 0x63c640da},
+       {0x00009828, 0x0796be89, 0x0696b081, 0x0696b881, 0x09143e81},
+       {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+       {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c},
+       {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4},
+       {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a2},
+       {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020},
+       {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000d8},
+       {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec86d2e},
+       {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32365a5e},
+       {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
+       {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+       {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
+       {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282},
+       {0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27},
+       {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
+       {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
+       {0x0000a204, 0x01318fc0, 0x01318fc4, 0x01318fc4, 0x01318fc0},
+       {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
+       {0x0000a22c, 0x01026a2f, 0x01026a27, 0x01026a2f, 0x01026a2f},
+       {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b},
+       {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff},
+       {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018},
+       {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+       {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+       {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
+       {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e},
+       {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501},
+       {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e},
+       {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
+       {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
+       {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
+       {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
+       {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+       {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
+       {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
+       {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a3a4, 0x00000050, 0x00000050, 0x00000000, 0x00000000},
+       {0x0000a3a8, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa},
+       {0x0000a3ac, 0xaaaaaa00, 0xaa30aa30, 0xaaaaaa00, 0xaaaaaa00},
+       {0x0000a41c, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce},
+       {0x0000a420, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce},
+       {0x0000a424, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce},
+       {0x0000a428, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce},
+       {0x0000a42c, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce},
+       {0x0000a430, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce},
+       {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+       {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000},
+       {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+       {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce},
+       {0x0000b284, 0x00000000, 0x00000000, 0x00000550, 0x00000550},
+};
+
+static const u32 ar9462_2p1_radio_core[][2] = {
+       /* Addr      allmodes  */
+       {0x00016000, 0x36db6db6},
+       {0x00016004, 0x6db6db40},
+       {0x00016008, 0x73f00000},
+       {0x0001600c, 0x00000000},
+       {0x00016010, 0x6d820001},
+       {0x00016040, 0x7f80fff8},
+       {0x0001604c, 0x2699e04f},
+       {0x00016050, 0x6db6db6c},
+       {0x00016058, 0x6c200000},
+       {0x00016080, 0x000c0000},
+       {0x00016084, 0x9a68048c},
+       {0x00016088, 0x54214514},
+       {0x0001608c, 0x1203040b},
+       {0x00016090, 0x24926490},
+       {0x00016098, 0xd2888888},
+       {0x000160a0, 0x0a108ffe},
+       {0x000160a4, 0x812fc491},
+       {0x000160a8, 0x423c8000},
+       {0x000160b4, 0x92000000},
+       {0x000160b8, 0x0285dddc},
+       {0x000160bc, 0x02908888},
+       {0x000160c0, 0x00adb6d0},
+       {0x000160c4, 0x6db6db60},
+       {0x000160c8, 0x6db6db6c},
+       {0x000160cc, 0x0de6c1b0},
+       {0x00016100, 0x3fffbe04},
+       {0x00016104, 0xfff80000},
+       {0x00016108, 0x00200400},
+       {0x00016110, 0x00000000},
+       {0x00016144, 0x02084080},
+       {0x00016148, 0x000080c0},
+       {0x00016280, 0x050a0001},
+       {0x00016284, 0x3d841418},
+       {0x00016288, 0x00000000},
+       {0x0001628c, 0xe3000000},
+       {0x00016290, 0xa1005080},
+       {0x00016294, 0x00000020},
+       {0x00016298, 0x54a82900},
+       {0x00016340, 0x121e4276},
+       {0x00016344, 0x00300000},
+       {0x00016400, 0x36db6db6},
+       {0x00016404, 0x6db6db40},
+       {0x00016408, 0x73f00000},
+       {0x0001640c, 0x00000000},
+       {0x00016410, 0x6c800001},
+       {0x00016440, 0x7f80fff8},
+       {0x0001644c, 0x4699e04f},
+       {0x00016450, 0x6db6db6c},
+       {0x00016500, 0x3fffbe04},
+       {0x00016504, 0xfff80000},
+       {0x00016508, 0x00200400},
+       {0x00016510, 0x00000000},
+       {0x00016544, 0x02084080},
+       {0x00016548, 0x000080c0},
+};
+
+static const u32 ar9462_2p1_radio_postamble[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x0001609c, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524},
+       {0x000160b0, 0x01d67f70, 0x01d67f70, 0x01d67f70, 0x01d67f70},
+       {0x0001610c, 0x48000000, 0x40000000, 0x40000000, 0x40000000},
+       {0x0001650c, 0x48000000, 0x40000000, 0x40000000, 0x40000000},
+};
+
+static const u32 ar9462_2p1_soc_preamble[][2] = {
+       /* Addr      allmodes  */
+       {0x000040a4, 0x00a0c1c9},
+       {0x00007020, 0x00000000},
+       {0x00007034, 0x00000002},
+       {0x00007038, 0x000004c2},
+};
+
+static const u32 ar9462_2p1_soc_postamble[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00007010, 0x00000033, 0x00000033, 0x00000033, 0x00000033},
+};
+
+static const u32 ar9462_2p1_radio_postamble_sys2ant[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808},
+       {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008},
+       {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008},
+};
+
+static const u32 ar9462_2p1_common_rx_gain[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a000, 0x00010000},
+       {0x0000a004, 0x00030002},
+       {0x0000a008, 0x00050004},
+       {0x0000a00c, 0x00810080},
+       {0x0000a010, 0x00830082},
+       {0x0000a014, 0x01810180},
+       {0x0000a018, 0x01830182},
+       {0x0000a01c, 0x01850184},
+       {0x0000a020, 0x01890188},
+       {0x0000a024, 0x018b018a},
+       {0x0000a028, 0x018d018c},
+       {0x0000a02c, 0x01910190},
+       {0x0000a030, 0x01930192},
+       {0x0000a034, 0x01950194},
+       {0x0000a038, 0x038a0196},
+       {0x0000a03c, 0x038c038b},
+       {0x0000a040, 0x0390038d},
+       {0x0000a044, 0x03920391},
+       {0x0000a048, 0x03940393},
+       {0x0000a04c, 0x03960395},
+       {0x0000a050, 0x00000000},
+       {0x0000a054, 0x00000000},
+       {0x0000a058, 0x00000000},
+       {0x0000a05c, 0x00000000},
+       {0x0000a060, 0x00000000},
+       {0x0000a064, 0x00000000},
+       {0x0000a068, 0x00000000},
+       {0x0000a06c, 0x00000000},
+       {0x0000a070, 0x00000000},
+       {0x0000a074, 0x00000000},
+       {0x0000a078, 0x00000000},
+       {0x0000a07c, 0x00000000},
+       {0x0000a080, 0x22222229},
+       {0x0000a084, 0x1d1d1d1d},
+       {0x0000a088, 0x1d1d1d1d},
+       {0x0000a08c, 0x1d1d1d1d},
+       {0x0000a090, 0x171d1d1d},
+       {0x0000a094, 0x11111717},
+       {0x0000a098, 0x00030311},
+       {0x0000a09c, 0x00000000},
+       {0x0000a0a0, 0x00000000},
+       {0x0000a0a4, 0x00000000},
+       {0x0000a0a8, 0x00000000},
+       {0x0000a0ac, 0x00000000},
+       {0x0000a0b0, 0x00000000},
+       {0x0000a0b4, 0x00000000},
+       {0x0000a0b8, 0x00000000},
+       {0x0000a0bc, 0x00000000},
+       {0x0000a0c0, 0x001f0000},
+       {0x0000a0c4, 0x01000101},
+       {0x0000a0c8, 0x011e011f},
+       {0x0000a0cc, 0x011c011d},
+       {0x0000a0d0, 0x02030204},
+       {0x0000a0d4, 0x02010202},
+       {0x0000a0d8, 0x021f0200},
+       {0x0000a0dc, 0x0302021e},
+       {0x0000a0e0, 0x03000301},
+       {0x0000a0e4, 0x031e031f},
+       {0x0000a0e8, 0x0402031d},
+       {0x0000a0ec, 0x04000401},
+       {0x0000a0f0, 0x041e041f},
+       {0x0000a0f4, 0x0502041d},
+       {0x0000a0f8, 0x05000501},
+       {0x0000a0fc, 0x051e051f},
+       {0x0000a100, 0x06010602},
+       {0x0000a104, 0x061f0600},
+       {0x0000a108, 0x061d061e},
+       {0x0000a10c, 0x07020703},
+       {0x0000a110, 0x07000701},
+       {0x0000a114, 0x00000000},
+       {0x0000a118, 0x00000000},
+       {0x0000a11c, 0x00000000},
+       {0x0000a120, 0x00000000},
+       {0x0000a124, 0x00000000},
+       {0x0000a128, 0x00000000},
+       {0x0000a12c, 0x00000000},
+       {0x0000a130, 0x00000000},
+       {0x0000a134, 0x00000000},
+       {0x0000a138, 0x00000000},
+       {0x0000a13c, 0x00000000},
+       {0x0000a140, 0x001f0000},
+       {0x0000a144, 0x01000101},
+       {0x0000a148, 0x011e011f},
+       {0x0000a14c, 0x011c011d},
+       {0x0000a150, 0x02030204},
+       {0x0000a154, 0x02010202},
+       {0x0000a158, 0x021f0200},
+       {0x0000a15c, 0x0302021e},
+       {0x0000a160, 0x03000301},
+       {0x0000a164, 0x031e031f},
+       {0x0000a168, 0x0402031d},
+       {0x0000a16c, 0x04000401},
+       {0x0000a170, 0x041e041f},
+       {0x0000a174, 0x0502041d},
+       {0x0000a178, 0x05000501},
+       {0x0000a17c, 0x051e051f},
+       {0x0000a180, 0x06010602},
+       {0x0000a184, 0x061f0600},
+       {0x0000a188, 0x061d061e},
+       {0x0000a18c, 0x07020703},
+       {0x0000a190, 0x07000701},
+       {0x0000a194, 0x00000000},
+       {0x0000a198, 0x00000000},
+       {0x0000a19c, 0x00000000},
+       {0x0000a1a0, 0x00000000},
+       {0x0000a1a4, 0x00000000},
+       {0x0000a1a8, 0x00000000},
+       {0x0000a1ac, 0x00000000},
+       {0x0000a1b0, 0x00000000},
+       {0x0000a1b4, 0x00000000},
+       {0x0000a1b8, 0x00000000},
+       {0x0000a1bc, 0x00000000},
+       {0x0000a1c0, 0x00000000},
+       {0x0000a1c4, 0x00000000},
+       {0x0000a1c8, 0x00000000},
+       {0x0000a1cc, 0x00000000},
+       {0x0000a1d0, 0x00000000},
+       {0x0000a1d4, 0x00000000},
+       {0x0000a1d8, 0x00000000},
+       {0x0000a1dc, 0x00000000},
+       {0x0000a1e0, 0x00000000},
+       {0x0000a1e4, 0x00000000},
+       {0x0000a1e8, 0x00000000},
+       {0x0000a1ec, 0x00000000},
+       {0x0000a1f0, 0x00000396},
+       {0x0000a1f4, 0x00000396},
+       {0x0000a1f8, 0x00000396},
+       {0x0000a1fc, 0x00000196},
+       {0x0000b000, 0x00010000},
+       {0x0000b004, 0x00030002},
+       {0x0000b008, 0x00050004},
+       {0x0000b00c, 0x00810080},
+       {0x0000b010, 0x00830082},
+       {0x0000b014, 0x01810180},
+       {0x0000b018, 0x01830182},
+       {0x0000b01c, 0x01850184},
+       {0x0000b020, 0x02810280},
+       {0x0000b024, 0x02830282},
+       {0x0000b028, 0x02850284},
+       {0x0000b02c, 0x02890288},
+       {0x0000b030, 0x028b028a},
+       {0x0000b034, 0x0388028c},
+       {0x0000b038, 0x038a0389},
+       {0x0000b03c, 0x038c038b},
+       {0x0000b040, 0x0390038d},
+       {0x0000b044, 0x03920391},
+       {0x0000b048, 0x03940393},
+       {0x0000b04c, 0x03960395},
+       {0x0000b050, 0x00000000},
+       {0x0000b054, 0x00000000},
+       {0x0000b058, 0x00000000},
+       {0x0000b05c, 0x00000000},
+       {0x0000b060, 0x00000000},
+       {0x0000b064, 0x00000000},
+       {0x0000b068, 0x00000000},
+       {0x0000b06c, 0x00000000},
+       {0x0000b070, 0x00000000},
+       {0x0000b074, 0x00000000},
+       {0x0000b078, 0x00000000},
+       {0x0000b07c, 0x00000000},
+       {0x0000b080, 0x2a2d2f32},
+       {0x0000b084, 0x21232328},
+       {0x0000b088, 0x19191c1e},
+       {0x0000b08c, 0x12141417},
+       {0x0000b090, 0x07070e0e},
+       {0x0000b094, 0x03030305},
+       {0x0000b098, 0x00000003},
+       {0x0000b09c, 0x00000000},
+       {0x0000b0a0, 0x00000000},
+       {0x0000b0a4, 0x00000000},
+       {0x0000b0a8, 0x00000000},
+       {0x0000b0ac, 0x00000000},
+       {0x0000b0b0, 0x00000000},
+       {0x0000b0b4, 0x00000000},
+       {0x0000b0b8, 0x00000000},
+       {0x0000b0bc, 0x00000000},
+       {0x0000b0c0, 0x003f0020},
+       {0x0000b0c4, 0x00400041},
+       {0x0000b0c8, 0x0140005f},
+       {0x0000b0cc, 0x0160015f},
+       {0x0000b0d0, 0x017e017f},
+       {0x0000b0d4, 0x02410242},
+       {0x0000b0d8, 0x025f0240},
+       {0x0000b0dc, 0x027f0260},
+       {0x0000b0e0, 0x0341027e},
+       {0x0000b0e4, 0x035f0340},
+       {0x0000b0e8, 0x037f0360},
+       {0x0000b0ec, 0x04400441},
+       {0x0000b0f0, 0x0460045f},
+       {0x0000b0f4, 0x0541047f},
+       {0x0000b0f8, 0x055f0540},
+       {0x0000b0fc, 0x057f0560},
+       {0x0000b100, 0x06400641},
+       {0x0000b104, 0x0660065f},
+       {0x0000b108, 0x067e067f},
+       {0x0000b10c, 0x07410742},
+       {0x0000b110, 0x075f0740},
+       {0x0000b114, 0x077f0760},
+       {0x0000b118, 0x07800781},
+       {0x0000b11c, 0x07a0079f},
+       {0x0000b120, 0x07c107bf},
+       {0x0000b124, 0x000007c0},
+       {0x0000b128, 0x00000000},
+       {0x0000b12c, 0x00000000},
+       {0x0000b130, 0x00000000},
+       {0x0000b134, 0x00000000},
+       {0x0000b138, 0x00000000},
+       {0x0000b13c, 0x00000000},
+       {0x0000b140, 0x003f0020},
+       {0x0000b144, 0x00400041},
+       {0x0000b148, 0x0140005f},
+       {0x0000b14c, 0x0160015f},
+       {0x0000b150, 0x017e017f},
+       {0x0000b154, 0x02410242},
+       {0x0000b158, 0x025f0240},
+       {0x0000b15c, 0x027f0260},
+       {0x0000b160, 0x0341027e},
+       {0x0000b164, 0x035f0340},
+       {0x0000b168, 0x037f0360},
+       {0x0000b16c, 0x04400441},
+       {0x0000b170, 0x0460045f},
+       {0x0000b174, 0x0541047f},
+       {0x0000b178, 0x055f0540},
+       {0x0000b17c, 0x057f0560},
+       {0x0000b180, 0x06400641},
+       {0x0000b184, 0x0660065f},
+       {0x0000b188, 0x067e067f},
+       {0x0000b18c, 0x07410742},
+       {0x0000b190, 0x075f0740},
+       {0x0000b194, 0x077f0760},
+       {0x0000b198, 0x07800781},
+       {0x0000b19c, 0x07a0079f},
+       {0x0000b1a0, 0x07c107bf},
+       {0x0000b1a4, 0x000007c0},
+       {0x0000b1a8, 0x00000000},
+       {0x0000b1ac, 0x00000000},
+       {0x0000b1b0, 0x00000000},
+       {0x0000b1b4, 0x00000000},
+       {0x0000b1b8, 0x00000000},
+       {0x0000b1bc, 0x00000000},
+       {0x0000b1c0, 0x00000000},
+       {0x0000b1c4, 0x00000000},
+       {0x0000b1c8, 0x00000000},
+       {0x0000b1cc, 0x00000000},
+       {0x0000b1d0, 0x00000000},
+       {0x0000b1d4, 0x00000000},
+       {0x0000b1d8, 0x00000000},
+       {0x0000b1dc, 0x00000000},
+       {0x0000b1e0, 0x00000000},
+       {0x0000b1e4, 0x00000000},
+       {0x0000b1e8, 0x00000000},
+       {0x0000b1ec, 0x00000000},
+       {0x0000b1f0, 0x00000396},
+       {0x0000b1f4, 0x00000396},
+       {0x0000b1f8, 0x00000396},
+       {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_2p1_common_mixed_rx_gain[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a000, 0x00010000},
+       {0x0000a004, 0x00030002},
+       {0x0000a008, 0x00050004},
+       {0x0000a00c, 0x00810080},
+       {0x0000a010, 0x00830082},
+       {0x0000a014, 0x01810180},
+       {0x0000a018, 0x01830182},
+       {0x0000a01c, 0x01850184},
+       {0x0000a020, 0x01890188},
+       {0x0000a024, 0x018b018a},
+       {0x0000a028, 0x018d018c},
+       {0x0000a02c, 0x03820190},
+       {0x0000a030, 0x03840383},
+       {0x0000a034, 0x03880385},
+       {0x0000a038, 0x038a0389},
+       {0x0000a03c, 0x038c038b},
+       {0x0000a040, 0x0390038d},
+       {0x0000a044, 0x03920391},
+       {0x0000a048, 0x03940393},
+       {0x0000a04c, 0x03960395},
+       {0x0000a050, 0x00000000},
+       {0x0000a054, 0x00000000},
+       {0x0000a058, 0x00000000},
+       {0x0000a05c, 0x00000000},
+       {0x0000a060, 0x00000000},
+       {0x0000a064, 0x00000000},
+       {0x0000a068, 0x00000000},
+       {0x0000a06c, 0x00000000},
+       {0x0000a070, 0x00000000},
+       {0x0000a074, 0x00000000},
+       {0x0000a078, 0x00000000},
+       {0x0000a07c, 0x00000000},
+       {0x0000a080, 0x29292929},
+       {0x0000a084, 0x29292929},
+       {0x0000a088, 0x29292929},
+       {0x0000a08c, 0x29292929},
+       {0x0000a090, 0x22292929},
+       {0x0000a094, 0x1d1d2222},
+       {0x0000a098, 0x0c111117},
+       {0x0000a09c, 0x00030303},
+       {0x0000a0a0, 0x00000000},
+       {0x0000a0a4, 0x00000000},
+       {0x0000a0a8, 0x00000000},
+       {0x0000a0ac, 0x00000000},
+       {0x0000a0b0, 0x00000000},
+       {0x0000a0b4, 0x00000000},
+       {0x0000a0b8, 0x00000000},
+       {0x0000a0bc, 0x00000000},
+       {0x0000a0c0, 0x001f0000},
+       {0x0000a0c4, 0x01000101},
+       {0x0000a0c8, 0x011e011f},
+       {0x0000a0cc, 0x011c011d},
+       {0x0000a0d0, 0x02030204},
+       {0x0000a0d4, 0x02010202},
+       {0x0000a0d8, 0x021f0200},
+       {0x0000a0dc, 0x0302021e},
+       {0x0000a0e0, 0x03000301},
+       {0x0000a0e4, 0x031e031f},
+       {0x0000a0e8, 0x0402031d},
+       {0x0000a0ec, 0x04000401},
+       {0x0000a0f0, 0x041e041f},
+       {0x0000a0f4, 0x0502041d},
+       {0x0000a0f8, 0x05000501},
+       {0x0000a0fc, 0x051e051f},
+       {0x0000a100, 0x06010602},
+       {0x0000a104, 0x061f0600},
+       {0x0000a108, 0x061d061e},
+       {0x0000a10c, 0x07020703},
+       {0x0000a110, 0x07000701},
+       {0x0000a114, 0x00000000},
+       {0x0000a118, 0x00000000},
+       {0x0000a11c, 0x00000000},
+       {0x0000a120, 0x00000000},
+       {0x0000a124, 0x00000000},
+       {0x0000a128, 0x00000000},
+       {0x0000a12c, 0x00000000},
+       {0x0000a130, 0x00000000},
+       {0x0000a134, 0x00000000},
+       {0x0000a138, 0x00000000},
+       {0x0000a13c, 0x00000000},
+       {0x0000a140, 0x001f0000},
+       {0x0000a144, 0x01000101},
+       {0x0000a148, 0x011e011f},
+       {0x0000a14c, 0x011c011d},
+       {0x0000a150, 0x02030204},
+       {0x0000a154, 0x02010202},
+       {0x0000a158, 0x021f0200},
+       {0x0000a15c, 0x0302021e},
+       {0x0000a160, 0x03000301},
+       {0x0000a164, 0x031e031f},
+       {0x0000a168, 0x0402031d},
+       {0x0000a16c, 0x04000401},
+       {0x0000a170, 0x041e041f},
+       {0x0000a174, 0x0502041d},
+       {0x0000a178, 0x05000501},
+       {0x0000a17c, 0x051e051f},
+       {0x0000a180, 0x06010602},
+       {0x0000a184, 0x061f0600},
+       {0x0000a188, 0x061d061e},
+       {0x0000a18c, 0x07020703},
+       {0x0000a190, 0x07000701},
+       {0x0000a194, 0x00000000},
+       {0x0000a198, 0x00000000},
+       {0x0000a19c, 0x00000000},
+       {0x0000a1a0, 0x00000000},
+       {0x0000a1a4, 0x00000000},
+       {0x0000a1a8, 0x00000000},
+       {0x0000a1ac, 0x00000000},
+       {0x0000a1b0, 0x00000000},
+       {0x0000a1b4, 0x00000000},
+       {0x0000a1b8, 0x00000000},
+       {0x0000a1bc, 0x00000000},
+       {0x0000a1c0, 0x00000000},
+       {0x0000a1c4, 0x00000000},
+       {0x0000a1c8, 0x00000000},
+       {0x0000a1cc, 0x00000000},
+       {0x0000a1d0, 0x00000000},
+       {0x0000a1d4, 0x00000000},
+       {0x0000a1d8, 0x00000000},
+       {0x0000a1dc, 0x00000000},
+       {0x0000a1e0, 0x00000000},
+       {0x0000a1e4, 0x00000000},
+       {0x0000a1e8, 0x00000000},
+       {0x0000a1ec, 0x00000000},
+       {0x0000a1f0, 0x00000396},
+       {0x0000a1f4, 0x00000396},
+       {0x0000a1f8, 0x00000396},
+       {0x0000a1fc, 0x00000196},
+       {0x0000b000, 0x00010000},
+       {0x0000b004, 0x00030002},
+       {0x0000b008, 0x00050004},
+       {0x0000b00c, 0x00810080},
+       {0x0000b010, 0x00830082},
+       {0x0000b014, 0x01810180},
+       {0x0000b018, 0x01830182},
+       {0x0000b01c, 0x01850184},
+       {0x0000b020, 0x02810280},
+       {0x0000b024, 0x02830282},
+       {0x0000b028, 0x02850284},
+       {0x0000b02c, 0x02890288},
+       {0x0000b030, 0x028b028a},
+       {0x0000b034, 0x0388028c},
+       {0x0000b038, 0x038a0389},
+       {0x0000b03c, 0x038c038b},
+       {0x0000b040, 0x0390038d},
+       {0x0000b044, 0x03920391},
+       {0x0000b048, 0x03940393},
+       {0x0000b04c, 0x03960395},
+       {0x0000b050, 0x00000000},
+       {0x0000b054, 0x00000000},
+       {0x0000b058, 0x00000000},
+       {0x0000b05c, 0x00000000},
+       {0x0000b060, 0x00000000},
+       {0x0000b064, 0x00000000},
+       {0x0000b068, 0x00000000},
+       {0x0000b06c, 0x00000000},
+       {0x0000b070, 0x00000000},
+       {0x0000b074, 0x00000000},
+       {0x0000b078, 0x00000000},
+       {0x0000b07c, 0x00000000},
+       {0x0000b080, 0x2a2d2f32},
+       {0x0000b084, 0x21232328},
+       {0x0000b088, 0x19191c1e},
+       {0x0000b08c, 0x12141417},
+       {0x0000b090, 0x07070e0e},
+       {0x0000b094, 0x03030305},
+       {0x0000b098, 0x00000003},
+       {0x0000b09c, 0x00000000},
+       {0x0000b0a0, 0x00000000},
+       {0x0000b0a4, 0x00000000},
+       {0x0000b0a8, 0x00000000},
+       {0x0000b0ac, 0x00000000},
+       {0x0000b0b0, 0x00000000},
+       {0x0000b0b4, 0x00000000},
+       {0x0000b0b8, 0x00000000},
+       {0x0000b0bc, 0x00000000},
+       {0x0000b0c0, 0x003f0020},
+       {0x0000b0c4, 0x00400041},
+       {0x0000b0c8, 0x0140005f},
+       {0x0000b0cc, 0x0160015f},
+       {0x0000b0d0, 0x017e017f},
+       {0x0000b0d4, 0x02410242},
+       {0x0000b0d8, 0x025f0240},
+       {0x0000b0dc, 0x027f0260},
+       {0x0000b0e0, 0x0341027e},
+       {0x0000b0e4, 0x035f0340},
+       {0x0000b0e8, 0x037f0360},
+       {0x0000b0ec, 0x04400441},
+       {0x0000b0f0, 0x0460045f},
+       {0x0000b0f4, 0x0541047f},
+       {0x0000b0f8, 0x055f0540},
+       {0x0000b0fc, 0x057f0560},
+       {0x0000b100, 0x06400641},
+       {0x0000b104, 0x0660065f},
+       {0x0000b108, 0x067e067f},
+       {0x0000b10c, 0x07410742},
+       {0x0000b110, 0x075f0740},
+       {0x0000b114, 0x077f0760},
+       {0x0000b118, 0x07800781},
+       {0x0000b11c, 0x07a0079f},
+       {0x0000b120, 0x07c107bf},
+       {0x0000b124, 0x000007c0},
+       {0x0000b128, 0x00000000},
+       {0x0000b12c, 0x00000000},
+       {0x0000b130, 0x00000000},
+       {0x0000b134, 0x00000000},
+       {0x0000b138, 0x00000000},
+       {0x0000b13c, 0x00000000},
+       {0x0000b140, 0x003f0020},
+       {0x0000b144, 0x00400041},
+       {0x0000b148, 0x0140005f},
+       {0x0000b14c, 0x0160015f},
+       {0x0000b150, 0x017e017f},
+       {0x0000b154, 0x02410242},
+       {0x0000b158, 0x025f0240},
+       {0x0000b15c, 0x027f0260},
+       {0x0000b160, 0x0341027e},
+       {0x0000b164, 0x035f0340},
+       {0x0000b168, 0x037f0360},
+       {0x0000b16c, 0x04400441},
+       {0x0000b170, 0x0460045f},
+       {0x0000b174, 0x0541047f},
+       {0x0000b178, 0x055f0540},
+       {0x0000b17c, 0x057f0560},
+       {0x0000b180, 0x06400641},
+       {0x0000b184, 0x0660065f},
+       {0x0000b188, 0x067e067f},
+       {0x0000b18c, 0x07410742},
+       {0x0000b190, 0x075f0740},
+       {0x0000b194, 0x077f0760},
+       {0x0000b198, 0x07800781},
+       {0x0000b19c, 0x07a0079f},
+       {0x0000b1a0, 0x07c107bf},
+       {0x0000b1a4, 0x000007c0},
+       {0x0000b1a8, 0x00000000},
+       {0x0000b1ac, 0x00000000},
+       {0x0000b1b0, 0x00000000},
+       {0x0000b1b4, 0x00000000},
+       {0x0000b1b8, 0x00000000},
+       {0x0000b1bc, 0x00000000},
+       {0x0000b1c0, 0x00000000},
+       {0x0000b1c4, 0x00000000},
+       {0x0000b1c8, 0x00000000},
+       {0x0000b1cc, 0x00000000},
+       {0x0000b1d0, 0x00000000},
+       {0x0000b1d4, 0x00000000},
+       {0x0000b1d8, 0x00000000},
+       {0x0000b1dc, 0x00000000},
+       {0x0000b1e0, 0x00000000},
+       {0x0000b1e4, 0x00000000},
+       {0x0000b1e8, 0x00000000},
+       {0x0000b1ec, 0x00000000},
+       {0x0000b1f0, 0x00000396},
+       {0x0000b1f4, 0x00000396},
+       {0x0000b1f8, 0x00000396},
+       {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_2p1_baseband_core_mix_rxgain[][2] = {
+       /* Addr      allmodes  */
+       {0x00009fd0, 0x0a2d6b93},
+};
+
+static const u32 ar9462_2p1_baseband_postamble_mix_rxgain[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009820, 0x206a022e, 0x206a022e, 0x206a01ae, 0x206a01ae},
+       {0x00009824, 0x63c640de, 0x5ac640d0, 0x63c640da, 0x63c640da},
+       {0x00009828, 0x0796be89, 0x0696b081, 0x0916be81, 0x0916be81},
+       {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000d8, 0x6c4000d8},
+       {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec86d2e, 0x7ec86d2e},
+       {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32395c5e},
+};
+
+static const u32 ar9462_2p1_baseband_postamble_5g_xlna[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282},
+};
+
+static const u32 ar9462_2p1_common_wo_xlna_rx_gain[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a000, 0x00010000},
+       {0x0000a004, 0x00030002},
+       {0x0000a008, 0x00050004},
+       {0x0000a00c, 0x00810080},
+       {0x0000a010, 0x00830082},
+       {0x0000a014, 0x01810180},
+       {0x0000a018, 0x01830182},
+       {0x0000a01c, 0x01850184},
+       {0x0000a020, 0x01890188},
+       {0x0000a024, 0x018b018a},
+       {0x0000a028, 0x018d018c},
+       {0x0000a02c, 0x03820190},
+       {0x0000a030, 0x03840383},
+       {0x0000a034, 0x03880385},
+       {0x0000a038, 0x038a0389},
+       {0x0000a03c, 0x038c038b},
+       {0x0000a040, 0x0390038d},
+       {0x0000a044, 0x03920391},
+       {0x0000a048, 0x03940393},
+       {0x0000a04c, 0x03960395},
+       {0x0000a050, 0x00000000},
+       {0x0000a054, 0x00000000},
+       {0x0000a058, 0x00000000},
+       {0x0000a05c, 0x00000000},
+       {0x0000a060, 0x00000000},
+       {0x0000a064, 0x00000000},
+       {0x0000a068, 0x00000000},
+       {0x0000a06c, 0x00000000},
+       {0x0000a070, 0x00000000},
+       {0x0000a074, 0x00000000},
+       {0x0000a078, 0x00000000},
+       {0x0000a07c, 0x00000000},
+       {0x0000a080, 0x29292929},
+       {0x0000a084, 0x29292929},
+       {0x0000a088, 0x29292929},
+       {0x0000a08c, 0x29292929},
+       {0x0000a090, 0x22292929},
+       {0x0000a094, 0x1d1d2222},
+       {0x0000a098, 0x0c111117},
+       {0x0000a09c, 0x00030303},
+       {0x0000a0a0, 0x00000000},
+       {0x0000a0a4, 0x00000000},
+       {0x0000a0a8, 0x00000000},
+       {0x0000a0ac, 0x00000000},
+       {0x0000a0b0, 0x00000000},
+       {0x0000a0b4, 0x00000000},
+       {0x0000a0b8, 0x00000000},
+       {0x0000a0bc, 0x00000000},
+       {0x0000a0c0, 0x001f0000},
+       {0x0000a0c4, 0x01000101},
+       {0x0000a0c8, 0x011e011f},
+       {0x0000a0cc, 0x011c011d},
+       {0x0000a0d0, 0x02030204},
+       {0x0000a0d4, 0x02010202},
+       {0x0000a0d8, 0x021f0200},
+       {0x0000a0dc, 0x0302021e},
+       {0x0000a0e0, 0x03000301},
+       {0x0000a0e4, 0x031e031f},
+       {0x0000a0e8, 0x0402031d},
+       {0x0000a0ec, 0x04000401},
+       {0x0000a0f0, 0x041e041f},
+       {0x0000a0f4, 0x0502041d},
+       {0x0000a0f8, 0x05000501},
+       {0x0000a0fc, 0x051e051f},
+       {0x0000a100, 0x06010602},
+       {0x0000a104, 0x061f0600},
+       {0x0000a108, 0x061d061e},
+       {0x0000a10c, 0x07020703},
+       {0x0000a110, 0x07000701},
+       {0x0000a114, 0x00000000},
+       {0x0000a118, 0x00000000},
+       {0x0000a11c, 0x00000000},
+       {0x0000a120, 0x00000000},
+       {0x0000a124, 0x00000000},
+       {0x0000a128, 0x00000000},
+       {0x0000a12c, 0x00000000},
+       {0x0000a130, 0x00000000},
+       {0x0000a134, 0x00000000},
+       {0x0000a138, 0x00000000},
+       {0x0000a13c, 0x00000000},
+       {0x0000a140, 0x001f0000},
+       {0x0000a144, 0x01000101},
+       {0x0000a148, 0x011e011f},
+       {0x0000a14c, 0x011c011d},
+       {0x0000a150, 0x02030204},
+       {0x0000a154, 0x02010202},
+       {0x0000a158, 0x021f0200},
+       {0x0000a15c, 0x0302021e},
+       {0x0000a160, 0x03000301},
+       {0x0000a164, 0x031e031f},
+       {0x0000a168, 0x0402031d},
+       {0x0000a16c, 0x04000401},
+       {0x0000a170, 0x041e041f},
+       {0x0000a174, 0x0502041d},
+       {0x0000a178, 0x05000501},
+       {0x0000a17c, 0x051e051f},
+       {0x0000a180, 0x06010602},
+       {0x0000a184, 0x061f0600},
+       {0x0000a188, 0x061d061e},
+       {0x0000a18c, 0x07020703},
+       {0x0000a190, 0x07000701},
+       {0x0000a194, 0x00000000},
+       {0x0000a198, 0x00000000},
+       {0x0000a19c, 0x00000000},
+       {0x0000a1a0, 0x00000000},
+       {0x0000a1a4, 0x00000000},
+       {0x0000a1a8, 0x00000000},
+       {0x0000a1ac, 0x00000000},
+       {0x0000a1b0, 0x00000000},
+       {0x0000a1b4, 0x00000000},
+       {0x0000a1b8, 0x00000000},
+       {0x0000a1bc, 0x00000000},
+       {0x0000a1c0, 0x00000000},
+       {0x0000a1c4, 0x00000000},
+       {0x0000a1c8, 0x00000000},
+       {0x0000a1cc, 0x00000000},
+       {0x0000a1d0, 0x00000000},
+       {0x0000a1d4, 0x00000000},
+       {0x0000a1d8, 0x00000000},
+       {0x0000a1dc, 0x00000000},
+       {0x0000a1e0, 0x00000000},
+       {0x0000a1e4, 0x00000000},
+       {0x0000a1e8, 0x00000000},
+       {0x0000a1ec, 0x00000000},
+       {0x0000a1f0, 0x00000396},
+       {0x0000a1f4, 0x00000396},
+       {0x0000a1f8, 0x00000396},
+       {0x0000a1fc, 0x00000196},
+       {0x0000b000, 0x00010000},
+       {0x0000b004, 0x00030002},
+       {0x0000b008, 0x00050004},
+       {0x0000b00c, 0x00810080},
+       {0x0000b010, 0x00830082},
+       {0x0000b014, 0x01810180},
+       {0x0000b018, 0x01830182},
+       {0x0000b01c, 0x01850184},
+       {0x0000b020, 0x02810280},
+       {0x0000b024, 0x02830282},
+       {0x0000b028, 0x02850284},
+       {0x0000b02c, 0x02890288},
+       {0x0000b030, 0x028b028a},
+       {0x0000b034, 0x0388028c},
+       {0x0000b038, 0x038a0389},
+       {0x0000b03c, 0x038c038b},
+       {0x0000b040, 0x0390038d},
+       {0x0000b044, 0x03920391},
+       {0x0000b048, 0x03940393},
+       {0x0000b04c, 0x03960395},
+       {0x0000b050, 0x00000000},
+       {0x0000b054, 0x00000000},
+       {0x0000b058, 0x00000000},
+       {0x0000b05c, 0x00000000},
+       {0x0000b060, 0x00000000},
+       {0x0000b064, 0x00000000},
+       {0x0000b068, 0x00000000},
+       {0x0000b06c, 0x00000000},
+       {0x0000b070, 0x00000000},
+       {0x0000b074, 0x00000000},
+       {0x0000b078, 0x00000000},
+       {0x0000b07c, 0x00000000},
+       {0x0000b080, 0x32323232},
+       {0x0000b084, 0x2f2f3232},
+       {0x0000b088, 0x23282a2d},
+       {0x0000b08c, 0x1c1e2123},
+       {0x0000b090, 0x14171919},
+       {0x0000b094, 0x0e0e1214},
+       {0x0000b098, 0x03050707},
+       {0x0000b09c, 0x00030303},
+       {0x0000b0a0, 0x00000000},
+       {0x0000b0a4, 0x00000000},
+       {0x0000b0a8, 0x00000000},
+       {0x0000b0ac, 0x00000000},
+       {0x0000b0b0, 0x00000000},
+       {0x0000b0b4, 0x00000000},
+       {0x0000b0b8, 0x00000000},
+       {0x0000b0bc, 0x00000000},
+       {0x0000b0c0, 0x003f0020},
+       {0x0000b0c4, 0x00400041},
+       {0x0000b0c8, 0x0140005f},
+       {0x0000b0cc, 0x0160015f},
+       {0x0000b0d0, 0x017e017f},
+       {0x0000b0d4, 0x02410242},
+       {0x0000b0d8, 0x025f0240},
+       {0x0000b0dc, 0x027f0260},
+       {0x0000b0e0, 0x0341027e},
+       {0x0000b0e4, 0x035f0340},
+       {0x0000b0e8, 0x037f0360},
+       {0x0000b0ec, 0x04400441},
+       {0x0000b0f0, 0x0460045f},
+       {0x0000b0f4, 0x0541047f},
+       {0x0000b0f8, 0x055f0540},
+       {0x0000b0fc, 0x057f0560},
+       {0x0000b100, 0x06400641},
+       {0x0000b104, 0x0660065f},
+       {0x0000b108, 0x067e067f},
+       {0x0000b10c, 0x07410742},
+       {0x0000b110, 0x075f0740},
+       {0x0000b114, 0x077f0760},
+       {0x0000b118, 0x07800781},
+       {0x0000b11c, 0x07a0079f},
+       {0x0000b120, 0x07c107bf},
+       {0x0000b124, 0x000007c0},
+       {0x0000b128, 0x00000000},
+       {0x0000b12c, 0x00000000},
+       {0x0000b130, 0x00000000},
+       {0x0000b134, 0x00000000},
+       {0x0000b138, 0x00000000},
+       {0x0000b13c, 0x00000000},
+       {0x0000b140, 0x003f0020},
+       {0x0000b144, 0x00400041},
+       {0x0000b148, 0x0140005f},
+       {0x0000b14c, 0x0160015f},
+       {0x0000b150, 0x017e017f},
+       {0x0000b154, 0x02410242},
+       {0x0000b158, 0x025f0240},
+       {0x0000b15c, 0x027f0260},
+       {0x0000b160, 0x0341027e},
+       {0x0000b164, 0x035f0340},
+       {0x0000b168, 0x037f0360},
+       {0x0000b16c, 0x04400441},
+       {0x0000b170, 0x0460045f},
+       {0x0000b174, 0x0541047f},
+       {0x0000b178, 0x055f0540},
+       {0x0000b17c, 0x057f0560},
+       {0x0000b180, 0x06400641},
+       {0x0000b184, 0x0660065f},
+       {0x0000b188, 0x067e067f},
+       {0x0000b18c, 0x07410742},
+       {0x0000b190, 0x075f0740},
+       {0x0000b194, 0x077f0760},
+       {0x0000b198, 0x07800781},
+       {0x0000b19c, 0x07a0079f},
+       {0x0000b1a0, 0x07c107bf},
+       {0x0000b1a4, 0x000007c0},
+       {0x0000b1a8, 0x00000000},
+       {0x0000b1ac, 0x00000000},
+       {0x0000b1b0, 0x00000000},
+       {0x0000b1b4, 0x00000000},
+       {0x0000b1b8, 0x00000000},
+       {0x0000b1bc, 0x00000000},
+       {0x0000b1c0, 0x00000000},
+       {0x0000b1c4, 0x00000000},
+       {0x0000b1c8, 0x00000000},
+       {0x0000b1cc, 0x00000000},
+       {0x0000b1d0, 0x00000000},
+       {0x0000b1d4, 0x00000000},
+       {0x0000b1d8, 0x00000000},
+       {0x0000b1dc, 0x00000000},
+       {0x0000b1e0, 0x00000000},
+       {0x0000b1e4, 0x00000000},
+       {0x0000b1e8, 0x00000000},
+       {0x0000b1ec, 0x00000000},
+       {0x0000b1f0, 0x00000396},
+       {0x0000b1f4, 0x00000396},
+       {0x0000b1f8, 0x00000396},
+       {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_2p1_common_5g_xlna_only_rx_gain[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a000, 0x00010000},
+       {0x0000a004, 0x00030002},
+       {0x0000a008, 0x00050004},
+       {0x0000a00c, 0x00810080},
+       {0x0000a010, 0x00830082},
+       {0x0000a014, 0x01810180},
+       {0x0000a018, 0x01830182},
+       {0x0000a01c, 0x01850184},
+       {0x0000a020, 0x01890188},
+       {0x0000a024, 0x018b018a},
+       {0x0000a028, 0x018d018c},
+       {0x0000a02c, 0x03820190},
+       {0x0000a030, 0x03840383},
+       {0x0000a034, 0x03880385},
+       {0x0000a038, 0x038a0389},
+       {0x0000a03c, 0x038c038b},
+       {0x0000a040, 0x0390038d},
+       {0x0000a044, 0x03920391},
+       {0x0000a048, 0x03940393},
+       {0x0000a04c, 0x03960395},
+       {0x0000a050, 0x00000000},
+       {0x0000a054, 0x00000000},
+       {0x0000a058, 0x00000000},
+       {0x0000a05c, 0x00000000},
+       {0x0000a060, 0x00000000},
+       {0x0000a064, 0x00000000},
+       {0x0000a068, 0x00000000},
+       {0x0000a06c, 0x00000000},
+       {0x0000a070, 0x00000000},
+       {0x0000a074, 0x00000000},
+       {0x0000a078, 0x00000000},
+       {0x0000a07c, 0x00000000},
+       {0x0000a080, 0x29292929},
+       {0x0000a084, 0x29292929},
+       {0x0000a088, 0x29292929},
+       {0x0000a08c, 0x29292929},
+       {0x0000a090, 0x22292929},
+       {0x0000a094, 0x1d1d2222},
+       {0x0000a098, 0x0c111117},
+       {0x0000a09c, 0x00030303},
+       {0x0000a0a0, 0x00000000},
+       {0x0000a0a4, 0x00000000},
+       {0x0000a0a8, 0x00000000},
+       {0x0000a0ac, 0x00000000},
+       {0x0000a0b0, 0x00000000},
+       {0x0000a0b4, 0x00000000},
+       {0x0000a0b8, 0x00000000},
+       {0x0000a0bc, 0x00000000},
+       {0x0000a0c0, 0x001f0000},
+       {0x0000a0c4, 0x01000101},
+       {0x0000a0c8, 0x011e011f},
+       {0x0000a0cc, 0x011c011d},
+       {0x0000a0d0, 0x02030204},
+       {0x0000a0d4, 0x02010202},
+       {0x0000a0d8, 0x021f0200},
+       {0x0000a0dc, 0x0302021e},
+       {0x0000a0e0, 0x03000301},
+       {0x0000a0e4, 0x031e031f},
+       {0x0000a0e8, 0x0402031d},
+       {0x0000a0ec, 0x04000401},
+       {0x0000a0f0, 0x041e041f},
+       {0x0000a0f4, 0x0502041d},
+       {0x0000a0f8, 0x05000501},
+       {0x0000a0fc, 0x051e051f},
+       {0x0000a100, 0x06010602},
+       {0x0000a104, 0x061f0600},
+       {0x0000a108, 0x061d061e},
+       {0x0000a10c, 0x07020703},
+       {0x0000a110, 0x07000701},
+       {0x0000a114, 0x00000000},
+       {0x0000a118, 0x00000000},
+       {0x0000a11c, 0x00000000},
+       {0x0000a120, 0x00000000},
+       {0x0000a124, 0x00000000},
+       {0x0000a128, 0x00000000},
+       {0x0000a12c, 0x00000000},
+       {0x0000a130, 0x00000000},
+       {0x0000a134, 0x00000000},
+       {0x0000a138, 0x00000000},
+       {0x0000a13c, 0x00000000},
+       {0x0000a140, 0x001f0000},
+       {0x0000a144, 0x01000101},
+       {0x0000a148, 0x011e011f},
+       {0x0000a14c, 0x011c011d},
+       {0x0000a150, 0x02030204},
+       {0x0000a154, 0x02010202},
+       {0x0000a158, 0x021f0200},
+       {0x0000a15c, 0x0302021e},
+       {0x0000a160, 0x03000301},
+       {0x0000a164, 0x031e031f},
+       {0x0000a168, 0x0402031d},
+       {0x0000a16c, 0x04000401},
+       {0x0000a170, 0x041e041f},
+       {0x0000a174, 0x0502041d},
+       {0x0000a178, 0x05000501},
+       {0x0000a17c, 0x051e051f},
+       {0x0000a180, 0x06010602},
+       {0x0000a184, 0x061f0600},
+       {0x0000a188, 0x061d061e},
+       {0x0000a18c, 0x07020703},
+       {0x0000a190, 0x07000701},
+       {0x0000a194, 0x00000000},
+       {0x0000a198, 0x00000000},
+       {0x0000a19c, 0x00000000},
+       {0x0000a1a0, 0x00000000},
+       {0x0000a1a4, 0x00000000},
+       {0x0000a1a8, 0x00000000},
+       {0x0000a1ac, 0x00000000},
+       {0x0000a1b0, 0x00000000},
+       {0x0000a1b4, 0x00000000},
+       {0x0000a1b8, 0x00000000},
+       {0x0000a1bc, 0x00000000},
+       {0x0000a1c0, 0x00000000},
+       {0x0000a1c4, 0x00000000},
+       {0x0000a1c8, 0x00000000},
+       {0x0000a1cc, 0x00000000},
+       {0x0000a1d0, 0x00000000},
+       {0x0000a1d4, 0x00000000},
+       {0x0000a1d8, 0x00000000},
+       {0x0000a1dc, 0x00000000},
+       {0x0000a1e0, 0x00000000},
+       {0x0000a1e4, 0x00000000},
+       {0x0000a1e8, 0x00000000},
+       {0x0000a1ec, 0x00000000},
+       {0x0000a1f0, 0x00000396},
+       {0x0000a1f4, 0x00000396},
+       {0x0000a1f8, 0x00000396},
+       {0x0000a1fc, 0x00000196},
+       {0x0000b000, 0x00010000},
+       {0x0000b004, 0x00030002},
+       {0x0000b008, 0x00050004},
+       {0x0000b00c, 0x00810080},
+       {0x0000b010, 0x00830082},
+       {0x0000b014, 0x01810180},
+       {0x0000b018, 0x01830182},
+       {0x0000b01c, 0x01850184},
+       {0x0000b020, 0x02810280},
+       {0x0000b024, 0x02830282},
+       {0x0000b028, 0x02850284},
+       {0x0000b02c, 0x02890288},
+       {0x0000b030, 0x028b028a},
+       {0x0000b034, 0x0388028c},
+       {0x0000b038, 0x038a0389},
+       {0x0000b03c, 0x038c038b},
+       {0x0000b040, 0x0390038d},
+       {0x0000b044, 0x03920391},
+       {0x0000b048, 0x03940393},
+       {0x0000b04c, 0x03960395},
+       {0x0000b050, 0x00000000},
+       {0x0000b054, 0x00000000},
+       {0x0000b058, 0x00000000},
+       {0x0000b05c, 0x00000000},
+       {0x0000b060, 0x00000000},
+       {0x0000b064, 0x00000000},
+       {0x0000b068, 0x00000000},
+       {0x0000b06c, 0x00000000},
+       {0x0000b070, 0x00000000},
+       {0x0000b074, 0x00000000},
+       {0x0000b078, 0x00000000},
+       {0x0000b07c, 0x00000000},
+       {0x0000b080, 0x2a2d2f32},
+       {0x0000b084, 0x21232328},
+       {0x0000b088, 0x19191c1e},
+       {0x0000b08c, 0x12141417},
+       {0x0000b090, 0x07070e0e},
+       {0x0000b094, 0x03030305},
+       {0x0000b098, 0x00000003},
+       {0x0000b09c, 0x00000000},
+       {0x0000b0a0, 0x00000000},
+       {0x0000b0a4, 0x00000000},
+       {0x0000b0a8, 0x00000000},
+       {0x0000b0ac, 0x00000000},
+       {0x0000b0b0, 0x00000000},
+       {0x0000b0b4, 0x00000000},
+       {0x0000b0b8, 0x00000000},
+       {0x0000b0bc, 0x00000000},
+       {0x0000b0c0, 0x003f0020},
+       {0x0000b0c4, 0x00400041},
+       {0x0000b0c8, 0x0140005f},
+       {0x0000b0cc, 0x0160015f},
+       {0x0000b0d0, 0x017e017f},
+       {0x0000b0d4, 0x02410242},
+       {0x0000b0d8, 0x025f0240},
+       {0x0000b0dc, 0x027f0260},
+       {0x0000b0e0, 0x0341027e},
+       {0x0000b0e4, 0x035f0340},
+       {0x0000b0e8, 0x037f0360},
+       {0x0000b0ec, 0x04400441},
+       {0x0000b0f0, 0x0460045f},
+       {0x0000b0f4, 0x0541047f},
+       {0x0000b0f8, 0x055f0540},
+       {0x0000b0fc, 0x057f0560},
+       {0x0000b100, 0x06400641},
+       {0x0000b104, 0x0660065f},
+       {0x0000b108, 0x067e067f},
+       {0x0000b10c, 0x07410742},
+       {0x0000b110, 0x075f0740},
+       {0x0000b114, 0x077f0760},
+       {0x0000b118, 0x07800781},
+       {0x0000b11c, 0x07a0079f},
+       {0x0000b120, 0x07c107bf},
+       {0x0000b124, 0x000007c0},
+       {0x0000b128, 0x00000000},
+       {0x0000b12c, 0x00000000},
+       {0x0000b130, 0x00000000},
+       {0x0000b134, 0x00000000},
+       {0x0000b138, 0x00000000},
+       {0x0000b13c, 0x00000000},
+       {0x0000b140, 0x003f0020},
+       {0x0000b144, 0x00400041},
+       {0x0000b148, 0x0140005f},
+       {0x0000b14c, 0x0160015f},
+       {0x0000b150, 0x017e017f},
+       {0x0000b154, 0x02410242},
+       {0x0000b158, 0x025f0240},
+       {0x0000b15c, 0x027f0260},
+       {0x0000b160, 0x0341027e},
+       {0x0000b164, 0x035f0340},
+       {0x0000b168, 0x037f0360},
+       {0x0000b16c, 0x04400441},
+       {0x0000b170, 0x0460045f},
+       {0x0000b174, 0x0541047f},
+       {0x0000b178, 0x055f0540},
+       {0x0000b17c, 0x057f0560},
+       {0x0000b180, 0x06400641},
+       {0x0000b184, 0x0660065f},
+       {0x0000b188, 0x067e067f},
+       {0x0000b18c, 0x07410742},
+       {0x0000b190, 0x075f0740},
+       {0x0000b194, 0x077f0760},
+       {0x0000b198, 0x07800781},
+       {0x0000b19c, 0x07a0079f},
+       {0x0000b1a0, 0x07c107bf},
+       {0x0000b1a4, 0x000007c0},
+       {0x0000b1a8, 0x00000000},
+       {0x0000b1ac, 0x00000000},
+       {0x0000b1b0, 0x00000000},
+       {0x0000b1b4, 0x00000000},
+       {0x0000b1b8, 0x00000000},
+       {0x0000b1bc, 0x00000000},
+       {0x0000b1c0, 0x00000000},
+       {0x0000b1c4, 0x00000000},
+       {0x0000b1c8, 0x00000000},
+       {0x0000b1cc, 0x00000000},
+       {0x0000b1d0, 0x00000000},
+       {0x0000b1d4, 0x00000000},
+       {0x0000b1d8, 0x00000000},
+       {0x0000b1dc, 0x00000000},
+       {0x0000b1e0, 0x00000000},
+       {0x0000b1e4, 0x00000000},
+       {0x0000b1e8, 0x00000000},
+       {0x0000b1ec, 0x00000000},
+       {0x0000b1f0, 0x00000396},
+       {0x0000b1f4, 0x00000396},
+       {0x0000b1f8, 0x00000396},
+       {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_2p1_modes_low_ob_db_tx_gain[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002},
+       {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+       {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+       {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+       {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+       {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+       {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+       {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+       {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202},
+       {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400},
+       {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402},
+       {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404},
+       {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603},
+       {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02},
+       {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04},
+       {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20},
+       {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20},
+       {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22},
+       {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24},
+       {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640},
+       {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660},
+       {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861},
+       {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81},
+       {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83},
+       {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84},
+       {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3},
+       {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5},
+       {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9},
+       {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb},
+       {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+       {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+       {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+       {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+       {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+       {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+       {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec},
+       {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000},
+       {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501},
+       {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501},
+       {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03},
+       {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
+       {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04},
+       {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005},
+       {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+       {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+       {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+       {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005},
+       {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
+       {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
+       {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+       {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x00016044, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4},
+       {0x00016048, 0x64992060, 0x64992060, 0x64992060, 0x64992060},
+       {0x00016054, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000},
+       {0x00016444, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4},
+       {0x00016448, 0x64992000, 0x64992000, 0x64992000, 0x64992000},
+       {0x00016454, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000},
+};
+
+static const u32 ar9462_2p1_modes_high_ob_db_tx_gain[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002},
+       {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+       {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+       {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+       {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x0000a410, 0x000050da, 0x000050da, 0x000050de, 0x000050de},
+       {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
+       {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002},
+       {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004},
+       {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200},
+       {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202},
+       {0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400},
+       {0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402},
+       {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404},
+       {0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603},
+       {0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02},
+       {0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04},
+       {0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20},
+       {0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20},
+       {0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22},
+       {0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24},
+       {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640},
+       {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660},
+       {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861},
+       {0x0000a548, 0x55025eb3, 0x55025eb3, 0x3e001a81, 0x3e001a81},
+       {0x0000a54c, 0x58025ef3, 0x58025ef3, 0x42001a83, 0x42001a83},
+       {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x44001a84, 0x44001a84},
+       {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3},
+       {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5},
+       {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9},
+       {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb},
+       {0x0000a564, 0x751ffff6, 0x751ffff6, 0x56001eec, 0x56001eec},
+       {0x0000a568, 0x751ffff6, 0x751ffff6, 0x58001ef0, 0x58001ef0},
+       {0x0000a56c, 0x751ffff6, 0x751ffff6, 0x5a001ef4, 0x5a001ef4},
+       {0x0000a570, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6},
+       {0x0000a574, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6},
+       {0x0000a578, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6},
+       {0x0000a57c, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6},
+       {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000},
+       {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000},
+       {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501},
+       {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501},
+       {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03},
+       {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04},
+       {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04},
+       {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+       {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+       {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+       {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x00016044, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4},
+       {0x00016048, 0x8db49060, 0x8db49060, 0x8db49060, 0x8db49060},
+       {0x00016054, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000},
+       {0x00016444, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4},
+       {0x00016448, 0x8db49000, 0x8db49000, 0x8db49000, 0x8db49000},
+       {0x00016454, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000},
+};
+
+static const u32 ar9462_2p1_modes_mix_ob_db_tx_gain[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002},
+       {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+       {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+       {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+       {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+       {0x0000a410, 0x0000d0da, 0x0000d0da, 0x0000d0de, 0x0000d0de},
+       {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
+       {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002},
+       {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004},
+       {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200},
+       {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202},
+       {0x0000a514, 0x18022622, 0x18022622, 0x12000400, 0x12000400},
+       {0x0000a518, 0x1b022822, 0x1b022822, 0x16000402, 0x16000402},
+       {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404},
+       {0x0000a520, 0x22022c41, 0x22022c41, 0x1c000603, 0x1c000603},
+       {0x0000a524, 0x28023042, 0x28023042, 0x21000a02, 0x21000a02},
+       {0x0000a528, 0x2c023044, 0x2c023044, 0x25000a04, 0x25000a04},
+       {0x0000a52c, 0x2f023644, 0x2f023644, 0x28000a20, 0x28000a20},
+       {0x0000a530, 0x34025643, 0x34025643, 0x2c000e20, 0x2c000e20},
+       {0x0000a534, 0x38025a44, 0x38025a44, 0x30000e22, 0x30000e22},
+       {0x0000a538, 0x3b025e45, 0x3b025e45, 0x34000e24, 0x34000e24},
+       {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x38001640, 0x38001640},
+       {0x0000a540, 0x48025e6c, 0x48025e6c, 0x3c001660, 0x3c001660},
+       {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3f001861, 0x3f001861},
+       {0x0000a548, 0x55025eb3, 0x55025eb3, 0x43001a81, 0x43001a81},
+       {0x0000a54c, 0x58025ef3, 0x58025ef3, 0x47001a83, 0x47001a83},
+       {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x4a001c84, 0x4a001c84},
+       {0x0000a554, 0x62025f56, 0x62025f56, 0x4e001ce3, 0x4e001ce3},
+       {0x0000a558, 0x66027f56, 0x66027f56, 0x52001ce5, 0x52001ce5},
+       {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x56001ce9, 0x56001ce9},
+       {0x0000a560, 0x70049f56, 0x70049f56, 0x5a001ceb, 0x5a001ceb},
+       {0x0000a564, 0x751ffff6, 0x751ffff6, 0x5c001eec, 0x5c001eec},
+       {0x0000a568, 0x751ffff6, 0x751ffff6, 0x5e001ef0, 0x5e001ef0},
+       {0x0000a56c, 0x751ffff6, 0x751ffff6, 0x60001ef4, 0x60001ef4},
+       {0x0000a570, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6},
+       {0x0000a574, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6},
+       {0x0000a578, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6},
+       {0x0000a57c, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6},
+       {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000},
+       {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000},
+       {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501},
+       {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501},
+       {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03},
+       {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04},
+       {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04},
+       {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
+       {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352},
+       {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584},
+       {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800},
+       {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+};
+
+static const u32 ar9462_2p1_modes_fast_clock[][3] = {
+       /* Addr      5G_HT20     5G_HT40   */
+       {0x00001030, 0x00000268, 0x000004d0},
+       {0x00001070, 0x0000018c, 0x00000318},
+       {0x000010b0, 0x00000fd0, 0x00001fa0},
+       {0x00008014, 0x044c044c, 0x08980898},
+       {0x0000801c, 0x148ec02b, 0x148ec057},
+       {0x00008318, 0x000044c0, 0x00008980},
+       {0x00009e00, 0x0372131c, 0x0372131c},
+       {0x0000a230, 0x0000400b, 0x00004016},
+       {0x0000a254, 0x00000898, 0x00001130},
+};
+
+static const u32 ar9462_2p1_baseband_core_txfir_coeff_japan_2484[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a398, 0x00000000},
+       {0x0000a39c, 0x6f7f0301},
+       {0x0000a3a0, 0xca9228ee},
+};
+
+#endif /* INITVALS_9462_2P1_H */
index 42b03dc..c1224b5 100644 (file)
@@ -296,6 +296,7 @@ struct ath_tx {
        struct ath_txq txq[ATH9K_NUM_TX_QUEUES];
        struct ath_descdma txdma;
        struct ath_txq *txq_map[IEEE80211_NUM_ACS];
+       struct ath_txq *uapsdq;
        u32 txq_max_pending[IEEE80211_NUM_ACS];
        u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32];
 };
@@ -343,6 +344,8 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
 void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop);
 int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                 struct ath_tx_control *txctl);
+void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                struct sk_buff *skb);
 void ath_tx_tasklet(struct ath_softc *sc);
 void ath_tx_edma_tasklet(struct ath_softc *sc);
 int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
@@ -353,6 +356,11 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid
 void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an);
 void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
                       struct ath_node *an);
+void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
+                                  struct ieee80211_sta *sta,
+                                  u16 tids, int nframes,
+                                  enum ieee80211_frame_release_type reason,
+                                  bool more_data);
 
 /********/
 /* VIFs */
@@ -623,6 +631,11 @@ void ath_ant_comb_update(struct ath_softc *sc);
 /* Main driver core */
 /********************/
 
+#define ATH9K_PCI_CUS198 0x0001
+#define ATH9K_PCI_CUS230 0x0002
+#define ATH9K_PCI_CUS217 0x0004
+#define ATH9K_PCI_WOW    0x0008
+
 /*
  * Default cache line size, in bytes.
  * Used when PCI device not fully initialized by bootrom/BIOS
@@ -642,6 +655,7 @@ enum sc_op_flags {
        SC_OP_ANI_RUN,
        SC_OP_PRIM_STA_VIF,
        SC_OP_HW_RESET,
+       SC_OP_SCANNING,
 };
 
 /* Powersave flags */
@@ -706,6 +720,7 @@ struct ath_softc {
 
        unsigned int hw_busy_count;
        unsigned long sc_flags;
+       unsigned long driver_data;
 
        u32 intrstatus;
        u16 ps_flags; /* PS_* */
@@ -755,7 +770,6 @@ struct ath_softc {
        struct rchan *rfs_chan_spec_scan;
        enum spectral_mode spectral_mode;
        struct ath_spec_scan spec_config;
-       int scanning;
 
 #ifdef CONFIG_PM_SLEEP
        atomic_t wow_got_bmiss_intr;
index 2ff570f..1a17732 100644 (file)
@@ -39,7 +39,8 @@ static void ath9k_beaconq_config(struct ath_softc *sc)
 
        ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
 
-       if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
+       if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
+           sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) {
                /* Always burst out beacon and CAB traffic. */
                qi.tqi_aifs = 1;
                qi.tqi_cwmin = 0;
@@ -107,23 +108,6 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
        ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
 }
 
-static void ath9k_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
-{
-       struct ath_softc *sc = hw->priv;
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_tx_control txctl;
-
-       memset(&txctl, 0, sizeof(struct ath_tx_control));
-       txctl.txq = sc->beacon.cabq;
-
-       ath_dbg(common, XMIT, "transmitting CABQ packet, skb: %p\n", skb);
-
-       if (ath_tx_start(hw, skb, &txctl) != 0) {
-               ath_dbg(common, XMIT, "CABQ TX failed\n");
-               ieee80211_free_txskb(hw, skb);
-       }
-}
-
 static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
                                             struct ieee80211_vif *vif)
 {
@@ -205,10 +189,8 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
 
        ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx);
 
-       while (skb) {
-               ath9k_tx_cabq(hw, skb);
-               skb = ieee80211_get_buffered_bc(hw, vif);
-       }
+       if (skb)
+               ath_tx_cabq(hw, vif, skb);
 
        return bf;
 }
@@ -273,7 +255,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
        u64 tsf;
        int slot;
 
-       if (sc->sc_ah->opmode != NL80211_IFTYPE_AP) {
+       if (sc->sc_ah->opmode != NL80211_IFTYPE_AP &&
+           sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) {
                ath_dbg(common, BEACON, "slot 0, tsf: %llu\n",
                        ath9k_hw_gettsf64(sc->sc_ah));
                return 0;
@@ -765,10 +748,10 @@ void ath9k_set_beacon(struct ath_softc *sc)
 
        switch (sc->sc_ah->opmode) {
        case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_MESH_POINT:
                ath9k_beacon_config_ap(sc, cur_conf);
                break;
        case NL80211_IFTYPE_ADHOC:
-       case NL80211_IFTYPE_MESH_POINT:
                ath9k_beacon_config_adhoc(sc, cur_conf);
                break;
        case NL80211_IFTYPE_STATION:
index 7304e75..5e8219a 100644 (file)
@@ -387,7 +387,6 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
 
        if (!caldata) {
                chan->noisefloor = nf;
-               ah->noise = ath9k_hw_getchan_noise(ah, chan);
                return false;
        }
 
index b37eb8d..87454f6 100644 (file)
@@ -69,7 +69,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf,
                return -EFAULT;
 
        buf[len] = '\0';
-       if (strict_strtoul(buf, 0, &mask))
+       if (kstrtoul(buf, 0, &mask))
                return -EINVAL;
 
        common->debug_mask = mask;
@@ -114,7 +114,7 @@ static ssize_t write_file_tx_chainmask(struct file *file, const char __user *use
                return -EFAULT;
 
        buf[len] = '\0';
-       if (strict_strtoul(buf, 0, &mask))
+       if (kstrtoul(buf, 0, &mask))
                return -EINVAL;
 
        ah->txchainmask = mask;
@@ -157,7 +157,7 @@ static ssize_t write_file_rx_chainmask(struct file *file, const char __user *use
                return -EFAULT;
 
        buf[len] = '\0';
-       if (strict_strtoul(buf, 0, &mask))
+       if (kstrtoul(buf, 0, &mask))
                return -EINVAL;
 
        ah->rxchainmask = mask;
@@ -173,25 +173,69 @@ static const struct file_operations fops_rx_chainmask = {
        .llseek = default_llseek,
 };
 
-static ssize_t read_file_disable_ani(struct file *file, char __user *user_buf,
+static ssize_t read_file_ani(struct file *file, char __user *user_buf,
                             size_t count, loff_t *ppos)
 {
        struct ath_softc *sc = file->private_data;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       char buf[32];
-       unsigned int len;
+       struct ath_hw *ah = sc->sc_ah;
+       unsigned int len = 0, size = 1024;
+       ssize_t retval = 0;
+       char *buf;
 
-       len = sprintf(buf, "%d\n", common->disable_ani);
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       if (common->disable_ani) {
+               len += snprintf(buf + len, size - len, "%s: %s\n",
+                               "ANI", "DISABLED");
+               goto exit;
+       }
+
+       len += snprintf(buf + len, size - len, "%15s: %s\n",
+                       "ANI", "ENABLED");
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "ANI RESET", ah->stats.ast_ani_reset);
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "SPUR UP", ah->stats.ast_ani_spurup);
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "SPUR DOWN", ah->stats.ast_ani_spurup);
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon);
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff);
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "MRC-CCK ON", ah->stats.ast_ani_ccklow);
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "MRC-CCK OFF", ah->stats.ast_ani_cckhigh);
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "FIR-STEP UP", ah->stats.ast_ani_stepup);
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "FIR-STEP DOWN", ah->stats.ast_ani_stepdown);
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero);
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs);
+       len += snprintf(buf + len, size - len, "%15s: %u\n",
+                       "CCK ERRORS", ah->stats.ast_ani_cckerrs);
+exit:
+       if (len > size)
+               len = size;
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
 }
 
-static ssize_t write_file_disable_ani(struct file *file,
-                                     const char __user *user_buf,
-                                     size_t count, loff_t *ppos)
+static ssize_t write_file_ani(struct file *file,
+                             const char __user *user_buf,
+                             size_t count, loff_t *ppos)
 {
        struct ath_softc *sc = file->private_data;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       unsigned long disable_ani;
+       unsigned long ani;
        char buf[32];
        ssize_t len;
 
@@ -200,12 +244,15 @@ static ssize_t write_file_disable_ani(struct file *file,
                return -EFAULT;
 
        buf[len] = '\0';
-       if (strict_strtoul(buf, 0, &disable_ani))
+       if (kstrtoul(buf, 0, &ani))
                return -EINVAL;
 
-       common->disable_ani = !!disable_ani;
+       if (ani < 0 || ani > 1)
+               return -EINVAL;
+
+       common->disable_ani = !ani;
 
-       if (disable_ani) {
+       if (common->disable_ani) {
                clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
                ath_stop_ani(sc);
        } else {
@@ -215,9 +262,9 @@ static ssize_t write_file_disable_ani(struct file *file,
        return count;
 }
 
-static const struct file_operations fops_disable_ani = {
-       .read = read_file_disable_ani,
-       .write = write_file_disable_ani,
+static const struct file_operations fops_ani = {
+       .read = read_file_ani,
+       .write = write_file_ani,
        .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
@@ -253,7 +300,7 @@ static ssize_t write_file_ant_diversity(struct file *file,
                goto exit;
 
        buf[len] = '\0';
-       if (strict_strtoul(buf, 0, &antenna_diversity))
+       if (kstrtoul(buf, 0, &antenna_diversity))
                return -EINVAL;
 
        common->antenna_diversity = !!antenna_diversity;
@@ -738,8 +785,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
                       struct ath_tx_status *ts, struct ath_txq *txq,
                       unsigned int flags)
 {
-#define TX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].ts\
-                       [sc->debug.tsidx].c)
        int qnum = txq->axq_qnum;
 
        TX_STAT_INC(qnum, tx_pkts_all);
@@ -771,37 +816,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
                TX_STAT_INC(qnum, data_underrun);
        if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN)
                TX_STAT_INC(qnum, delim_underrun);
-
-#ifdef CONFIG_ATH9K_MAC_DEBUG
-       spin_lock(&sc->debug.samp_lock);
-       TX_SAMP_DBG(jiffies) = jiffies;
-       TX_SAMP_DBG(rssi_ctl0) = ts->ts_rssi_ctl0;
-       TX_SAMP_DBG(rssi_ctl1) = ts->ts_rssi_ctl1;
-       TX_SAMP_DBG(rssi_ctl2) = ts->ts_rssi_ctl2;
-       TX_SAMP_DBG(rssi_ext0) = ts->ts_rssi_ext0;
-       TX_SAMP_DBG(rssi_ext1) = ts->ts_rssi_ext1;
-       TX_SAMP_DBG(rssi_ext2) = ts->ts_rssi_ext2;
-       TX_SAMP_DBG(rateindex) = ts->ts_rateindex;
-       TX_SAMP_DBG(isok) = !!(ts->ts_status & ATH9K_TXERR_MASK);
-       TX_SAMP_DBG(rts_fail_cnt) = ts->ts_shortretry;
-       TX_SAMP_DBG(data_fail_cnt) = ts->ts_longretry;
-       TX_SAMP_DBG(rssi) = ts->ts_rssi;
-       TX_SAMP_DBG(tid) = ts->tid;
-       TX_SAMP_DBG(qid) = ts->qid;
-
-       if (ts->ts_flags & ATH9K_TX_BA) {
-               TX_SAMP_DBG(ba_low) = ts->ba_low;
-               TX_SAMP_DBG(ba_high) = ts->ba_high;
-       } else {
-               TX_SAMP_DBG(ba_low) = 0;
-               TX_SAMP_DBG(ba_high) = 0;
-       }
-
-       sc->debug.tsidx = (sc->debug.tsidx + 1) % ATH_DBG_MAX_SAMPLES;
-       spin_unlock(&sc->debug.samp_lock);
-#endif
-
-#undef TX_SAMP_DBG
 }
 
 static const struct file_operations fops_xmit = {
@@ -915,8 +929,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
 void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
 {
 #define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++
-#define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\
-                       [sc->debug.rsidx].c)
 
        RX_STAT_INC(rx_pkts_all);
        sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen;
@@ -940,27 +952,7 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
                        RX_PHY_ERR_INC(rs->rs_phyerr);
        }
 
-#ifdef CONFIG_ATH9K_MAC_DEBUG
-       spin_lock(&sc->debug.samp_lock);
-       RX_SAMP_DBG(jiffies) = jiffies;
-       RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0;
-       RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1;
-       RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2;
-       RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0;
-       RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1;
-       RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2;
-       RX_SAMP_DBG(antenna) = rs->rs_antenna;
-       RX_SAMP_DBG(rssi) = rs->rs_rssi;
-       RX_SAMP_DBG(rate) = rs->rs_rate;
-       RX_SAMP_DBG(is_mybeacon) = rs->is_mybeacon;
-
-       sc->debug.rsidx = (sc->debug.rsidx + 1) % ATH_DBG_MAX_SAMPLES;
-       spin_unlock(&sc->debug.samp_lock);
-
-#endif
-
 #undef RX_PHY_ERR_INC
-#undef RX_SAMP_DBG
 }
 
 static const struct file_operations fops_recv = {
@@ -1278,7 +1270,7 @@ static ssize_t write_file_regidx(struct file *file, const char __user *user_buf,
                return -EFAULT;
 
        buf[len] = '\0';
-       if (strict_strtoul(buf, 0, &regidx))
+       if (kstrtoul(buf, 0, &regidx))
                return -EINVAL;
 
        sc->debug.regidx = regidx;
@@ -1323,7 +1315,7 @@ static ssize_t write_file_regval(struct file *file, const char __user *user_buf,
                return -EFAULT;
 
        buf[len] = '\0';
-       if (strict_strtoul(buf, 0, &regval))
+       if (kstrtoul(buf, 0, &regval))
                return -EINVAL;
 
        ath9k_ps_wakeup(sc);
@@ -1485,283 +1477,6 @@ static const struct file_operations fops_modal_eeprom = {
        .llseek = default_llseek,
 };
 
-#ifdef CONFIG_ATH9K_MAC_DEBUG
-
-void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
-{
-#define ATH_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].c)
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       unsigned long flags;
-       int i;
-
-       ath9k_ps_wakeup(sc);
-
-       spin_lock_bh(&sc->debug.samp_lock);
-
-       spin_lock_irqsave(&common->cc_lock, flags);
-       ath_hw_cycle_counters_update(common);
-
-       ATH_SAMP_DBG(cc.cycles) = common->cc_ani.cycles;
-       ATH_SAMP_DBG(cc.rx_busy) = common->cc_ani.rx_busy;
-       ATH_SAMP_DBG(cc.rx_frame) = common->cc_ani.rx_frame;
-       ATH_SAMP_DBG(cc.tx_frame) = common->cc_ani.tx_frame;
-       spin_unlock_irqrestore(&common->cc_lock, flags);
-
-       ATH_SAMP_DBG(noise) = ah->noise;
-
-       REG_WRITE_D(ah, AR_MACMISC,
-                 ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
-                  (AR_MACMISC_MISC_OBS_BUS_1 <<
-                   AR_MACMISC_MISC_OBS_BUS_MSB_S)));
-
-       for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
-               ATH_SAMP_DBG(dma_dbg_reg_vals[i]) = REG_READ_D(ah,
-                               AR_DMADBG_0 + (i * sizeof(u32)));
-
-       ATH_SAMP_DBG(pcu_obs) = REG_READ_D(ah, AR_OBS_BUS_1);
-       ATH_SAMP_DBG(pcu_cr) = REG_READ_D(ah, AR_CR);
-
-       memcpy(ATH_SAMP_DBG(nfCalHist), sc->caldata.nfCalHist,
-                       sizeof(ATH_SAMP_DBG(nfCalHist)));
-
-       sc->debug.sampidx = (sc->debug.sampidx + 1) % ATH_DBG_MAX_SAMPLES;
-       spin_unlock_bh(&sc->debug.samp_lock);
-       ath9k_ps_restore(sc);
-
-#undef ATH_SAMP_DBG
-}
-
-static int open_file_bb_mac_samps(struct inode *inode, struct file *file)
-{
-#define ATH_SAMP_DBG(c) bb_mac_samp[sampidx].c
-       struct ath_softc *sc = inode->i_private;
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       struct ieee80211_conf *conf = &common->hw->conf;
-       struct ath_dbg_bb_mac_samp *bb_mac_samp;
-       struct ath9k_nfcal_hist *h;
-       int i, j, qcuOffset = 0, dcuOffset = 0;
-       u32 *qcuBase, *dcuBase, size = 30000, len = 0;
-       u32 sampidx = 0;
-       u8 *buf;
-       u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
-       u8 nread;
-
-       if (test_bit(SC_OP_INVALID, &sc->sc_flags))
-               return -EAGAIN;
-
-       buf = vmalloc(size);
-       if (!buf)
-               return -ENOMEM;
-       bb_mac_samp = vmalloc(sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
-       if (!bb_mac_samp) {
-               vfree(buf);
-               return -ENOMEM;
-       }
-       /* Account the current state too */
-       ath9k_debug_samp_bb_mac(sc);
-
-       spin_lock_bh(&sc->debug.samp_lock);
-       memcpy(bb_mac_samp, sc->debug.bb_mac_samp,
-                       sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
-       len += snprintf(buf + len, size - len,
-                       "Current Sample Index: %d\n", sc->debug.sampidx);
-       spin_unlock_bh(&sc->debug.samp_lock);
-
-       len += snprintf(buf + len, size - len,
-                       "Raw DMA Debug Dump:\n");
-       len += snprintf(buf + len, size - len, "Sample |\t");
-       for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
-               len += snprintf(buf + len, size - len, " DMA Reg%d |\t", i);
-       len += snprintf(buf + len, size - len, "\n");
-
-       for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
-               len += snprintf(buf + len, size - len, "%d\t", sampidx);
-
-               for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
-                       len += snprintf(buf + len, size - len, " %08x\t",
-                                       ATH_SAMP_DBG(dma_dbg_reg_vals[i]));
-               len += snprintf(buf + len, size - len, "\n");
-       }
-       len += snprintf(buf + len, size - len, "\n");
-
-       len += snprintf(buf + len, size - len,
-                       "Sample Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
-       for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
-               qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
-               dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
-
-               for (i = 0; i < ATH9K_NUM_QUEUES; i++,
-                               qcuOffset += 4, dcuOffset += 5) {
-                       if (i == 8) {
-                               qcuOffset = 0;
-                               qcuBase++;
-                       }
-
-                       if (i == 6) {
-                               dcuOffset = 0;
-                               dcuBase++;
-                       }
-                       if (!sc->debug.stats.txstats[i].queued)
-                               continue;
-
-                       len += snprintf(buf + len, size - len,
-                               "%4d %7d    %2x      %1x     %2x         %2x\n",
-                               sampidx, i,
-                               (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
-                               (*qcuBase & (0x8 << qcuOffset)) >>
-                               (qcuOffset + 3),
-                               ATH_SAMP_DBG(dma_dbg_reg_vals[2]) &
-                               (0x7 << (i * 3)) >> (i * 3),
-                               (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
-               }
-               len += snprintf(buf + len, size - len, "\n");
-       }
-       len += snprintf(buf + len, size - len,
-                       "samp qcu_sh qcu_fh qcu_comp dcu_comp dcu_arb dcu_fp "
-                       "ch_idle_dur ch_idle_dur_val txfifo_val0 txfifo_val1 "
-                       "txfifo_dcu0 txfifo_dcu1 pcu_obs AR_CR\n");
-
-       for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
-               qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
-               dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
-
-               len += snprintf(buf + len, size - len, "%4d %5x %5x ", sampidx,
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x003c0000) >> 18,
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x03c00000) >> 22);
-               len += snprintf(buf + len, size - len, "%7x %8x ",
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x1c000000) >> 26,
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x3));
-               len += snprintf(buf + len, size - len, "%7x %7x ",
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x06000000) >> 25,
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x38000000) >> 27);
-               len += snprintf(buf + len, size - len, "%7d %12d ",
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x000003fc) >> 2,
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000400) >> 10);
-               len += snprintf(buf + len, size - len, "%12d %12d ",
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000800) >> 11,
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00001000) >> 12);
-               len += snprintf(buf + len, size - len, "%12d %12d ",
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x0001e000) >> 13,
-                       (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x001e0000) >> 17);
-               len += snprintf(buf + len, size - len, "0x%07x 0x%07x\n",
-                               ATH_SAMP_DBG(pcu_obs), ATH_SAMP_DBG(pcu_cr));
-       }
-
-       len += snprintf(buf + len, size - len,
-                       "Sample ChNoise Chain privNF #Reading Readings\n");
-       for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
-               h = ATH_SAMP_DBG(nfCalHist);
-               if (!ATH_SAMP_DBG(noise))
-                       continue;
-
-               for (i = 0; i < NUM_NF_READINGS; i++) {
-                       if (!(chainmask & (1 << i)) ||
-                           ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)))
-                               continue;
-
-                       nread = AR_PHY_CCA_FILTERWINDOW_LENGTH -
-                               h[i].invalidNFcount;
-                       len += snprintf(buf + len, size - len,
-                                       "%4d %5d %4d\t   %d\t %d\t",
-                                       sampidx, ATH_SAMP_DBG(noise),
-                                       i, h[i].privNF, nread);
-                       for (j = 0; j < nread; j++)
-                               len += snprintf(buf + len, size - len,
-                                       " %d", h[i].nfCalBuffer[j]);
-                       len += snprintf(buf + len, size - len, "\n");
-               }
-       }
-       len += snprintf(buf + len, size - len, "\nCycle counters:\n"
-                       "Sample Total    Rxbusy   Rxframes Txframes\n");
-       for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
-               if (!ATH_SAMP_DBG(cc.cycles))
-                       continue;
-               len += snprintf(buf + len, size - len,
-                               "%4d %08x %08x %08x %08x\n",
-                               sampidx, ATH_SAMP_DBG(cc.cycles),
-                               ATH_SAMP_DBG(cc.rx_busy),
-                               ATH_SAMP_DBG(cc.rx_frame),
-                               ATH_SAMP_DBG(cc.tx_frame));
-       }
-
-       len += snprintf(buf + len, size - len, "Tx status Dump :\n");
-       len += snprintf(buf + len, size - len,
-                       "Sample rssi:- ctl0 ctl1 ctl2 ext0 ext1 ext2 comb "
-                       "isok rts_fail data_fail rate tid qid "
-                                       "ba_low  ba_high tx_before(ms)\n");
-       for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
-               for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
-                       if (!ATH_SAMP_DBG(ts[i].jiffies))
-                               continue;
-                       len += snprintf(buf + len, size - len, "%-14d"
-                               "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-8d "
-                               "%-9d %-4d %-3d %-3d %08x %08x %-11d\n",
-                               sampidx,
-                               ATH_SAMP_DBG(ts[i].rssi_ctl0),
-                               ATH_SAMP_DBG(ts[i].rssi_ctl1),
-                               ATH_SAMP_DBG(ts[i].rssi_ctl2),
-                               ATH_SAMP_DBG(ts[i].rssi_ext0),
-                               ATH_SAMP_DBG(ts[i].rssi_ext1),
-                               ATH_SAMP_DBG(ts[i].rssi_ext2),
-                               ATH_SAMP_DBG(ts[i].rssi),
-                               ATH_SAMP_DBG(ts[i].isok),
-                               ATH_SAMP_DBG(ts[i].rts_fail_cnt),
-                               ATH_SAMP_DBG(ts[i].data_fail_cnt),
-                               ATH_SAMP_DBG(ts[i].rateindex),
-                               ATH_SAMP_DBG(ts[i].tid),
-                               ATH_SAMP_DBG(ts[i].qid),
-                               ATH_SAMP_DBG(ts[i].ba_low),
-                               ATH_SAMP_DBG(ts[i].ba_high),
-                               jiffies_to_msecs(jiffies -
-                                       ATH_SAMP_DBG(ts[i].jiffies)));
-               }
-       }
-
-       len += snprintf(buf + len, size - len, "Rx status Dump :\n");
-       len += snprintf(buf + len, size - len, "Sample rssi:- ctl0 ctl1 ctl2 "
-                       "ext0 ext1 ext2 comb beacon ant rate rx_before(ms)\n");
-       for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
-               for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
-                       if (!ATH_SAMP_DBG(rs[i].jiffies))
-                               continue;
-                       len += snprintf(buf + len, size - len, "%-14d"
-                               "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-9s %-2d %02x %-13d\n",
-                               sampidx,
-                               ATH_SAMP_DBG(rs[i].rssi_ctl0),
-                               ATH_SAMP_DBG(rs[i].rssi_ctl1),
-                               ATH_SAMP_DBG(rs[i].rssi_ctl2),
-                               ATH_SAMP_DBG(rs[i].rssi_ext0),
-                               ATH_SAMP_DBG(rs[i].rssi_ext1),
-                               ATH_SAMP_DBG(rs[i].rssi_ext2),
-                               ATH_SAMP_DBG(rs[i].rssi),
-                               ATH_SAMP_DBG(rs[i].is_mybeacon) ?
-                               "True" : "False",
-                               ATH_SAMP_DBG(rs[i].antenna),
-                               ATH_SAMP_DBG(rs[i].rate),
-                               jiffies_to_msecs(jiffies -
-                                       ATH_SAMP_DBG(rs[i].jiffies)));
-               }
-       }
-
-       vfree(bb_mac_samp);
-       file->private_data = buf;
-
-       return 0;
-#undef ATH_SAMP_DBG
-}
-
-static const struct file_operations fops_samps = {
-       .open = open_file_bb_mac_samps,
-       .read = ath9k_debugfs_read_buf,
-       .release = ath9k_debugfs_release_buf,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-#endif
-
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
 static ssize_t read_file_btcoex(struct file *file, char __user *user_buf,
                                size_t count, loff_t *ppos)
@@ -2059,8 +1774,8 @@ int ath9k_init_debug(struct ath_hw *ah)
                            sc->debug.debugfs_phy, sc, &fops_rx_chainmask);
        debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR,
                            sc->debug.debugfs_phy, sc, &fops_tx_chainmask);
-       debugfs_create_file("disable_ani", S_IRUSR | S_IWUSR,
-                           sc->debug.debugfs_phy, sc, &fops_disable_ani);
+       debugfs_create_file("ani", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc, &fops_ani);
        debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
                            &sc->sc_ah->config.enable_paprd);
        debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
@@ -2095,11 +1810,6 @@ int ath9k_init_debug(struct ath_hw *ah)
        debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR,
                            sc->debug.debugfs_phy, sc,
                            &fops_spectral_fft_period);
-
-#ifdef CONFIG_ATH9K_MAC_DEBUG
-       debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc,
-                           &fops_samps);
-#endif
        debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
                           sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
        debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
index 9d49aab..fc67919 100644 (file)
@@ -251,56 +251,10 @@ struct ath_stats {
        u32 reset[__RESET_TYPE_MAX];
 };
 
-#define ATH_DBG_MAX_SAMPLES    10
-struct ath_dbg_bb_mac_samp {
-       u32 dma_dbg_reg_vals[ATH9K_NUM_DMA_DEBUG_REGS];
-       u32 pcu_obs, pcu_cr, noise;
-       struct {
-               u64 jiffies;
-               int8_t rssi_ctl0;
-               int8_t rssi_ctl1;
-               int8_t rssi_ctl2;
-               int8_t rssi_ext0;
-               int8_t rssi_ext1;
-               int8_t rssi_ext2;
-               int8_t rssi;
-               bool isok;
-               u8 rts_fail_cnt;
-               u8 data_fail_cnt;
-               u8 rateindex;
-               u8 qid;
-               u8 tid;
-               u32 ba_low;
-               u32 ba_high;
-       } ts[ATH_DBG_MAX_SAMPLES];
-       struct {
-               u64 jiffies;
-               int8_t rssi_ctl0;
-               int8_t rssi_ctl1;
-               int8_t rssi_ctl2;
-               int8_t rssi_ext0;
-               int8_t rssi_ext1;
-               int8_t rssi_ext2;
-               int8_t rssi;
-               bool is_mybeacon;
-               u8 antenna;
-               u8 rate;
-       } rs[ATH_DBG_MAX_SAMPLES];
-       struct ath_cycle_counters cc;
-       struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
-};
-
 struct ath9k_debug {
        struct dentry *debugfs_phy;
        u32 regidx;
        struct ath_stats stats;
-#ifdef CONFIG_ATH9K_MAC_DEBUG
-       spinlock_t samp_lock;
-       struct ath_dbg_bb_mac_samp bb_mac_samp[ATH_DBG_MAX_SAMPLES];
-       u8 sampidx;
-       u8 tsidx;
-       u8 rsidx;
-#endif
 };
 
 int ath9k_init_debug(struct ath_hw *ah);
@@ -364,17 +318,4 @@ static inline void ath_debug_stat_rx(struct ath_softc *sc,
 
 #endif /* CONFIG_ATH9K_DEBUGFS */
 
-#ifdef CONFIG_ATH9K_MAC_DEBUG
-
-void ath9k_debug_samp_bb_mac(struct ath_softc *sc);
-
-#else
-
-static inline void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
-{
-}
-
-#endif
-
-
 #endif /* DEBUG_H */
index b7611b7..3c6e413 100644 (file)
@@ -96,7 +96,7 @@ static ssize_t write_file_dfs(struct file *file, const char __user *user_buf,
                return -EFAULT;
 
        buf[len] = '\0';
-       if (strict_strtoul(buf, 0, &val))
+       if (kstrtoul(buf, 0, &val))
                return -EINVAL;
 
        if (val == DFS_STATS_RESET_MAGIC)
index f5dda84..9e582e1 100644 (file)
@@ -234,10 +234,15 @@ static inline void ath9k_skb_queue_complete(struct hif_device_usb *hif_dev,
        struct sk_buff *skb;
 
        while ((skb = __skb_dequeue(queue)) != NULL) {
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+               int ln = skb->len;
+#endif
                ath9k_htc_txcompletion_cb(hif_dev->htc_handle,
                                          skb, txok);
-               if (txok)
+               if (txok) {
                        TX_STAT_INC(skb_success);
+                       TX_STAT_ADD(skb_success_bytes, ln);
+               }
                else
                        TX_STAT_INC(skb_failed);
        }
@@ -620,6 +625,7 @@ static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev,
 
 err:
        for (i = 0; i < pool_index; i++) {
+               RX_STAT_ADD(skb_completed_bytes, skb_pool[i]->len);
                ath9k_htc_rx_msg(hif_dev->htc_handle, skb_pool[i],
                                 skb_pool[i]->len, USB_WLAN_RX_PIPE);
                RX_STAT_INC(skb_completed);
index d3b099d..055d7c2 100644 (file)
@@ -142,6 +142,7 @@ struct ath9k_htc_target_aggr {
 #define WLAN_RC_40_FLAG  0x02
 #define WLAN_RC_SGI_FLAG 0x04
 #define WLAN_RC_HT_FLAG  0x08
+#define ATH_RC_TX_STBC_FLAG 0x20
 
 struct ath9k_htc_rateset {
        u8 rs_nrates;
@@ -208,6 +209,9 @@ struct ath9k_htc_target_rx_stats {
                case NL80211_IFTYPE_AP:         \
                        _priv->num_ap_vif++;    \
                        break;                  \
+               case NL80211_IFTYPE_MESH_POINT: \
+                       _priv->num_mbss_vif++;  \
+                       break;                  \
                default:                        \
                        break;                  \
                }                               \
@@ -224,6 +228,9 @@ struct ath9k_htc_target_rx_stats {
                case NL80211_IFTYPE_AP:         \
                        _priv->num_ap_vif--;    \
                        break;                  \
+               case NL80211_IFTYPE_MESH_POINT: \
+                       _priv->num_mbss_vif--;  \
+                       break;                  \
                default:                        \
                        break;                  \
                }                               \
@@ -317,7 +324,9 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
 #ifdef CONFIG_ATH9K_HTC_DEBUGFS
 
 #define TX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c++)
+#define TX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c += a)
 #define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c++)
+#define RX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c += a)
 #define CAB_STAT_INC   priv->debug.tx_stats.cab_queued++
 
 #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++)
@@ -330,6 +339,7 @@ struct ath_tx_stats {
        u32 buf_completed;
        u32 skb_queued;
        u32 skb_success;
+       u32 skb_success_bytes;
        u32 skb_failed;
        u32 cab_queued;
        u32 queue_stats[IEEE80211_NUM_ACS];
@@ -338,6 +348,7 @@ struct ath_tx_stats {
 struct ath_rx_stats {
        u32 skb_allocated;
        u32 skb_completed;
+       u32 skb_completed_bytes;
        u32 skb_dropped;
        u32 err_crc;
        u32 err_decrypt_crc;
@@ -355,10 +366,20 @@ struct ath9k_debug {
        struct ath_rx_stats rx_stats;
 };
 
+void ath9k_htc_get_et_strings(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             u32 sset, u8 *data);
+int ath9k_htc_get_et_sset_count(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif, int sset);
+void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct ethtool_stats *stats, u64 *data);
 #else
 
 #define TX_STAT_INC(c) do { } while (0)
+#define TX_STAT_ADD(c, a) do { } while (0)
 #define RX_STAT_INC(c) do { } while (0)
+#define RX_STAT_ADD(c, a) do { } while (0)
 #define CAB_STAT_INC   do { } while (0)
 
 #define TX_QSTAT_INC(c) do { } while (0)
@@ -450,6 +471,7 @@ struct ath9k_htc_priv {
        u8 sta_slot;
        u8 vif_sta_pos[ATH9K_HTC_MAX_VIF];
        u8 num_ibss_vif;
+       u8 num_mbss_vif;
        u8 num_sta_vif;
        u8 num_sta_assoc_vif;
        u8 num_ap_vif;
@@ -575,6 +597,8 @@ bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
 void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv);
 void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw);
 
+struct base_eep_header *ath9k_htc_get_eeprom_base(struct ath9k_htc_priv *priv);
+
 #ifdef CONFIG_MAC80211_LEDS
 void ath9k_init_leds(struct ath9k_htc_priv *priv);
 void ath9k_deinit_leds(struct ath9k_htc_priv *priv);
index f13f458..e0c03bd 100644 (file)
@@ -28,7 +28,8 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv)
 
        ath9k_hw_get_txq_props(ah, priv->beaconq, &qi);
 
-       if (priv->ah->opmode == NL80211_IFTYPE_AP) {
+       if (priv->ah->opmode == NL80211_IFTYPE_AP ||
+           priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) {
                qi.tqi_aifs = 1;
                qi.tqi_cwmin = 0;
                qi.tqi_cwmax = 0;
@@ -628,6 +629,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
        case NL80211_IFTYPE_ADHOC:
                ath9k_htc_beacon_config_adhoc(priv, cur_conf);
                break;
+       case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_AP:
                ath9k_htc_beacon_config_ap(priv, cur_conf);
                break;
@@ -649,6 +651,7 @@ void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv)
        case NL80211_IFTYPE_ADHOC:
                ath9k_htc_beacon_config_adhoc(priv, cur_conf);
                break;
+       case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_AP:
                ath9k_htc_beacon_config_ap(priv, cur_conf);
                break;
index 87110de..c1b45e2 100644 (file)
@@ -471,7 +471,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf,
                return -EFAULT;
 
        buf[len] = '\0';
-       if (strict_strtoul(buf, 0, &mask))
+       if (kstrtoul(buf, 0, &mask))
                return -EINVAL;
 
        common->debug_mask = mask;
@@ -496,21 +496,7 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
        ssize_t retval = 0;
        char *buf;
 
-       /*
-        * This can be done since all the 3 EEPROM families have the
-        * same base header upto a certain point, and we are interested in
-        * the data only upto that point.
-        */
-
-       if (AR_SREV_9271(priv->ah))
-               pBase = (struct base_eep_header *)
-                       &priv->ah->eeprom.map4k.baseEepHeader;
-       else if (priv->ah->hw_version.usbdev == AR9280_USB)
-               pBase = (struct base_eep_header *)
-                       &priv->ah->eeprom.def.baseEepHeader;
-       else if (priv->ah->hw_version.usbdev == AR9287_USB)
-               pBase = (struct base_eep_header *)
-                       &priv->ah->eeprom.map9287.baseEepHeader;
+       pBase = ath9k_htc_get_eeprom_base(priv);
 
        if (pBase == NULL) {
                ath_err(common, "Unknown EEPROM type\n");
@@ -916,6 +902,87 @@ static const struct file_operations fops_modal_eeprom = {
        .llseek = default_llseek,
 };
 
+
+/* Ethtool support for get-stats */
+#define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO"
+static const char ath9k_htc_gstrings_stats[][ETH_GSTRING_LEN] = {
+       "tx_pkts_nic",
+       "tx_bytes_nic",
+       "rx_pkts_nic",
+       "rx_bytes_nic",
+
+       AMKSTR(d_tx_pkts),
+
+       "d_rx_crc_err",
+       "d_rx_decrypt_crc_err",
+       "d_rx_phy_err",
+       "d_rx_mic_err",
+       "d_rx_pre_delim_crc_err",
+       "d_rx_post_delim_crc_err",
+       "d_rx_decrypt_busy_err",
+
+       "d_rx_phyerr_radar",
+       "d_rx_phyerr_ofdm_timing",
+       "d_rx_phyerr_cck_timing",
+
+};
+#define ATH9K_HTC_SSTATS_LEN ARRAY_SIZE(ath9k_htc_gstrings_stats)
+
+void ath9k_htc_get_et_strings(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             u32 sset, u8 *data)
+{
+       if (sset == ETH_SS_STATS)
+               memcpy(data, *ath9k_htc_gstrings_stats,
+                      sizeof(ath9k_htc_gstrings_stats));
+}
+
+int ath9k_htc_get_et_sset_count(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif, int sset)
+{
+       if (sset == ETH_SS_STATS)
+               return ATH9K_HTC_SSTATS_LEN;
+       return 0;
+}
+
+#define STXBASE priv->debug.tx_stats
+#define SRXBASE priv->debug.rx_stats
+#define ASTXQ(a)                                       \
+       data[i++] = STXBASE.a[IEEE80211_AC_BE];         \
+       data[i++] = STXBASE.a[IEEE80211_AC_BK];         \
+       data[i++] = STXBASE.a[IEEE80211_AC_VI];         \
+       data[i++] = STXBASE.a[IEEE80211_AC_VO]
+
+void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct ethtool_stats *stats, u64 *data)
+{
+       struct ath9k_htc_priv *priv = hw->priv;
+       int i = 0;
+
+       data[i++] = STXBASE.skb_success;
+       data[i++] = STXBASE.skb_success_bytes;
+       data[i++] = SRXBASE.skb_completed;
+       data[i++] = SRXBASE.skb_completed_bytes;
+
+       ASTXQ(queue_stats);
+
+       data[i++] = SRXBASE.err_crc;
+       data[i++] = SRXBASE.err_decrypt_crc;
+       data[i++] = SRXBASE.err_phy;
+       data[i++] = SRXBASE.err_mic;
+       data[i++] = SRXBASE.err_pre_delim;
+       data[i++] = SRXBASE.err_post_delim;
+       data[i++] = SRXBASE.err_decrypt_busy;
+
+       data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_RADAR];
+       data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_OFDM_TIMING];
+       data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_CCK_TIMING];
+
+       WARN_ON(i != ATH9K_HTC_SSTATS_LEN);
+}
+
+
 int ath9k_htc_init_debug(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
index a47f5e0..71a183f 100644 (file)
@@ -517,6 +517,9 @@ static void setup_ht_cap(struct ath9k_htc_priv *priv,
        ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n",
                tx_streams, rx_streams);
 
+       if (tx_streams >= 2)
+               ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
+
        if (tx_streams != rx_streams) {
                ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
                ht_info->mcs.tx_params |= ((tx_streams - 1) <<
@@ -698,6 +701,9 @@ static const struct ieee80211_iface_limit if_limits[] = {
        { .max = 2,     .types = BIT(NL80211_IFTYPE_STATION) |
                                 BIT(NL80211_IFTYPE_P2P_CLIENT) },
        { .max = 2,     .types = BIT(NL80211_IFTYPE_AP) |
+#ifdef CONFIG_MAC80211_MESH
+                                BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
                                 BIT(NL80211_IFTYPE_P2P_GO) },
 };
 
@@ -712,6 +718,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
                               struct ieee80211_hw *hw)
 {
        struct ath_common *common = ath9k_hw_common(priv->ah);
+       struct base_eep_header *pBase;
 
        hw->flags = IEEE80211_HW_SIGNAL_DBM |
                IEEE80211_HW_AMPDU_AGGREGATION |
@@ -721,6 +728,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
                IEEE80211_HW_SUPPORTS_PS |
                IEEE80211_HW_PS_NULLFUNC_STACK |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+               IEEE80211_HW_MFP_CAPABLE |
                IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
 
        hw->wiphy->interface_modes =
@@ -728,7 +736,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
                BIT(NL80211_IFTYPE_ADHOC) |
                BIT(NL80211_IFTYPE_AP) |
                BIT(NL80211_IFTYPE_P2P_GO) |
-               BIT(NL80211_IFTYPE_P2P_CLIENT);
+               BIT(NL80211_IFTYPE_P2P_CLIENT) |
+               BIT(NL80211_IFTYPE_MESH_POINT);
 
        hw->wiphy->iface_combinations = &if_comb;
        hw->wiphy->n_iface_combinations = 1;
@@ -765,6 +774,12 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
                                     &priv->sbands[IEEE80211_BAND_5GHZ].ht_cap);
        }
 
+       pBase = ath9k_htc_get_eeprom_base(priv);
+       if (pBase) {
+               hw->wiphy->available_antennas_rx = pBase->rxMask;
+               hw->wiphy->available_antennas_tx = pBase->txMask;
+       }
+
        SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
 }
 
index 62f1b76..5c1bec1 100644 (file)
@@ -113,7 +113,9 @@ static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        struct ath9k_htc_priv *priv = data;
        struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
 
-       if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon)
+       if ((vif->type == NL80211_IFTYPE_AP ||
+            vif->type == NL80211_IFTYPE_MESH_POINT) &&
+           bss_conf->enable_beacon)
                priv->reconfig_beacon = true;
 
        if (bss_conf->assoc) {
@@ -180,6 +182,8 @@ static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
                priv->ah->opmode = NL80211_IFTYPE_ADHOC;
        else if (priv->num_ap_vif)
                priv->ah->opmode = NL80211_IFTYPE_AP;
+       else if (priv->num_mbss_vif)
+               priv->ah->opmode = NL80211_IFTYPE_MESH_POINT;
        else
                priv->ah->opmode = NL80211_IFTYPE_STATION;
 
@@ -623,6 +627,8 @@ static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
                trate->rates.ht_rates.rs_nrates = j;
 
                caps = WLAN_RC_HT_FLAG;
+               if (sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
+                       caps |= ATH_RC_TX_STBC_FLAG;
                if (sta->ht_cap.mcs.rx_mask[1])
                        caps |= WLAN_RC_DS_FLAG;
                if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
@@ -810,8 +816,7 @@ void ath9k_htc_ani_work(struct work_struct *work)
        }
 
        /* Verify whether we must check ANI */
-       if (ah->config.enable_ani &&
-           (timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
+       if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
                aniflag = true;
                common->ani.checkani_timer = timestamp;
        }
@@ -841,8 +846,7 @@ set_timer:
        * short calibration and long calibration.
        */
        cal_interval = ATH_LONG_CALINTERVAL;
-       if (ah->config.enable_ani)
-               cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
+       cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
        if (!common->ani.caldone)
                cal_interval = min(cal_interval, (u32)short_cal_interval);
 
@@ -1052,6 +1056,9 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
        case NL80211_IFTYPE_AP:
                hvif.opmode = HTC_M_HOSTAP;
                break;
+       case NL80211_IFTYPE_MESH_POINT:
+               hvif.opmode = HTC_M_WDS;        /* close enough */
+               break;
        default:
                ath_err(common,
                        "Interface type %d not yet supported\n", vif->type);
@@ -1084,6 +1091,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
        INC_VIF(priv, vif->type);
 
        if ((vif->type == NL80211_IFTYPE_AP) ||
+           (vif->type == NL80211_IFTYPE_MESH_POINT) ||
            (vif->type == NL80211_IFTYPE_ADHOC))
                ath9k_htc_assign_bslot(priv, vif);
 
@@ -1134,6 +1142,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
        DEC_VIF(priv, vif->type);
 
        if ((vif->type == NL80211_IFTYPE_AP) ||
+            vif->type == NL80211_IFTYPE_MESH_POINT ||
            (vif->type == NL80211_IFTYPE_ADHOC))
                ath9k_htc_remove_bslot(priv, vif);
 
@@ -1525,9 +1534,10 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
        if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
                /*
                 * Disable SWBA interrupt only if there are no
-                * AP/IBSS interfaces.
+                * concurrent AP/mesh or IBSS interfaces.
                 */
-               if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
+               if ((priv->num_ap_vif + priv->num_mbss_vif <= 1) ||
+                    priv->num_ibss_vif) {
                        ath_dbg(common, CONFIG,
                                "Beacon disabled for BSS: %pM\n",
                                bss_conf->bssid);
@@ -1538,12 +1548,15 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_BEACON_INT) {
                /*
-                * Reset the HW TSF for the first AP interface.
+                * Reset the HW TSF for the first AP or mesh interface.
                 */
-               if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
-                   (priv->nvifs == 1) &&
-                   (priv->num_ap_vif == 1) &&
-                   (vif->type == NL80211_IFTYPE_AP)) {
+               if (priv->nvifs == 1 &&
+                   ((priv->ah->opmode == NL80211_IFTYPE_AP &&
+                     vif->type == NL80211_IFTYPE_AP &&
+                     priv->num_ap_vif == 1) ||
+                   (priv->ah->opmode == NL80211_IFTYPE_MESH_POINT &&
+                     vif->type == NL80211_IFTYPE_MESH_POINT &&
+                     priv->num_mbss_vif == 1))) {
                        set_bit(OP_TSF_RESET, &priv->op_flags);
                }
                ath_dbg(common, CONFIG,
@@ -1761,6 +1774,43 @@ static int ath9k_htc_get_stats(struct ieee80211_hw *hw,
        return 0;
 }
 
+struct base_eep_header *ath9k_htc_get_eeprom_base(struct ath9k_htc_priv *priv)
+{
+       struct base_eep_header *pBase = NULL;
+       /*
+        * This can be done since all the 3 EEPROM families have the
+        * same base header upto a certain point, and we are interested in
+        * the data only upto that point.
+        */
+
+       if (AR_SREV_9271(priv->ah))
+               pBase = (struct base_eep_header *)
+                       &priv->ah->eeprom.map4k.baseEepHeader;
+       else if (priv->ah->hw_version.usbdev == AR9280_USB)
+               pBase = (struct base_eep_header *)
+                       &priv->ah->eeprom.def.baseEepHeader;
+       else if (priv->ah->hw_version.usbdev == AR9287_USB)
+               pBase = (struct base_eep_header *)
+                       &priv->ah->eeprom.map9287.baseEepHeader;
+       return pBase;
+}
+
+
+static int ath9k_htc_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant,
+                                u32 *rx_ant)
+{
+       struct ath9k_htc_priv *priv = hw->priv;
+       struct base_eep_header *pBase = ath9k_htc_get_eeprom_base(priv);
+       if (pBase) {
+               *tx_ant = pBase->txMask;
+               *rx_ant = pBase->rxMask;
+       } else {
+               *tx_ant = 0;
+               *rx_ant = 0;
+       }
+       return 0;
+}
+
 struct ieee80211_ops ath9k_htc_ops = {
        .tx                 = ath9k_htc_tx,
        .start              = ath9k_htc_start,
@@ -1786,4 +1836,11 @@ struct ieee80211_ops ath9k_htc_ops = {
        .set_coverage_class = ath9k_htc_set_coverage_class,
        .set_bitrate_mask   = ath9k_htc_set_bitrate_mask,
        .get_stats          = ath9k_htc_get_stats,
+       .get_antenna        = ath9k_htc_get_antenna,
+
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+       .get_et_sset_count  = ath9k_htc_get_et_sset_count,
+       .get_et_stats       = ath9k_htc_get_et_stats,
+       .get_et_strings     = ath9k_htc_get_et_strings,
+#endif
 };
index 6bd0e92..e602c95 100644 (file)
@@ -887,7 +887,7 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv)
        if (priv->rxfilter & FIF_PSPOLL)
                rfilt |= ATH9K_RX_FILTER_PSPOLL;
 
-       if (priv->nvifs > 1)
+       if (priv->nvifs > 1 || priv->rxfilter & FIF_OTHER_BSS)
                rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
 
        return rfilt;
index 15dfefc..4ca0cb0 100644 (file)
@@ -452,7 +452,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
        ah->config.pcie_clock_req = 0;
        ah->config.pcie_waen = 0;
        ah->config.analog_shiftreg = 1;
-       ah->config.enable_ani = true;
 
        for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
                ah->config.spurchans[i][0] = AR_NO_SPUR;
@@ -549,8 +548,7 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
                ah->eep_ops->get_eeprom_ver(ah),
                ah->eep_ops->get_eeprom_rev(ah));
 
-       if (ah->config.enable_ani)
-               ath9k_hw_ani_init(ah);
+       ath9k_hw_ani_init(ah);
 
        return 0;
 }
@@ -1250,10 +1248,10 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode)
 
        switch (opmode) {
        case NL80211_IFTYPE_ADHOC:
-       case NL80211_IFTYPE_MESH_POINT:
                set |= AR_STA_ID1_ADHOC;
                REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
                break;
+       case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_AP:
                set |= AR_STA_ID1_STA_AP;
                /* fall through */
@@ -1872,7 +1870,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        ah->caldata = caldata;
        if (caldata && (chan->channel != caldata->channel ||
-                       chan->channelFlags != caldata->channelFlags)) {
+                       chan->channelFlags != caldata->channelFlags ||
+                       chan->chanmode != caldata->chanmode)) {
                /* Operating channel changed, reset channel calibration data */
                memset(caldata, 0, sizeof(*caldata));
                ath9k_init_nfcal_hist_buffer(ah, chan);
@@ -2255,12 +2254,12 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
 
        switch (ah->opmode) {
        case NL80211_IFTYPE_ADHOC:
-       case NL80211_IFTYPE_MESH_POINT:
                REG_SET_BIT(ah, AR_TXCFG,
                            AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
                REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon +
                          TU_TO_USEC(ah->atim_window ? ah->atim_window : 1));
                flags |= AR_NDP_TIMER_EN;
+       case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_AP:
                REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
                REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon -
@@ -2600,17 +2599,12 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
                if (!(ah->ent_mode & AR_ENT_OTP_49GHZ_DISABLE))
                        pCap->hw_caps |= ATH9K_HW_CAP_MCI;
 
-               if (AR_SREV_9462_20(ah))
+               if (AR_SREV_9462_20_OR_LATER(ah))
                        pCap->hw_caps |= ATH9K_HW_CAP_RTT;
        }
 
-       if (AR_SREV_9280_20_OR_LATER(ah)) {
-               pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE |
-                                ATH9K_HW_WOW_PATTERN_MATCH_EXACT;
-
-               if (AR_SREV_9280(ah))
-                       pCap->hw_caps |= ATH9K_HW_WOW_PATTERN_MATCH_DWORD;
-       }
+       if (AR_SREV_9462(ah))
+               pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE;
 
        if (AR_SREV_9300_20_OR_LATER(ah) &&
            ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
@@ -3048,7 +3042,7 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah,
 
        timer_next = tsf + trig_timeout;
 
-       ath_dbg(ath9k_hw_common(ah), HWTIMER,
+       ath_dbg(ath9k_hw_common(ah), BTCOEX,
                "current tsf %x period %x timer_next %x\n",
                tsf, timer_period, timer_next);
 
@@ -3147,7 +3141,7 @@ void ath_gen_timer_isr(struct ath_hw *ah)
                index = rightmost_index(timer_table, &thresh_mask);
                timer = timer_table->timers[index];
                BUG_ON(!timer);
-               ath_dbg(common, HWTIMER, "TSF overflow for Gen timer %d\n",
+               ath_dbg(common, BTCOEX, "TSF overflow for Gen timer %d\n",
                        index);
                timer->overflow(timer->arg);
        }
@@ -3156,7 +3150,7 @@ void ath_gen_timer_isr(struct ath_hw *ah)
                index = rightmost_index(timer_table, &trigger_mask);
                timer = timer_table->timers[index];
                BUG_ON(!timer);
-               ath_dbg(common, HWTIMER,
+               ath_dbg(common, BTCOEX,
                        "Gen timer[%d] trigger\n", index);
                timer->trigger(timer->arg);
        }
index ae30343..cd74b3a 100644 (file)
@@ -246,9 +246,7 @@ enum ath9k_hw_caps {
        ATH9K_HW_CAP_MCI                        = BIT(15),
        ATH9K_HW_CAP_DFS                        = BIT(16),
        ATH9K_HW_WOW_DEVICE_CAPABLE             = BIT(17),
-       ATH9K_HW_WOW_PATTERN_MATCH_EXACT        = BIT(18),
-       ATH9K_HW_WOW_PATTERN_MATCH_DWORD        = BIT(19),
-       ATH9K_HW_CAP_PAPRD                      = BIT(20),
+       ATH9K_HW_CAP_PAPRD                      = BIT(18),
 };
 
 /*
@@ -291,7 +289,6 @@ struct ath9k_ops_config {
        u32 ofdm_trig_high;
        u32 cck_trig_high;
        u32 cck_trig_low;
-       u32 enable_ani;
        u32 enable_paprd;
        int serialize_regmode;
        bool rx_intr_mitigation;
@@ -310,6 +307,10 @@ struct ath9k_ops_config {
        u16 spurchans[AR_EEPROM_MODAL_SPURS][2];
        u8 max_txtrig_level;
        u16 ani_poll_interval; /* ANI poll interval in ms */
+
+       /* Platform specific config */
+       u32 xlna_gpio;
+       bool xatten_margin_cfg;
 };
 
 enum ath9k_int {
@@ -423,7 +424,6 @@ struct ath9k_hw_cal_data {
 
 struct ath9k_channel {
        struct ieee80211_channel *chan;
-       struct ar5416AniState ani;
        u16 channel;
        u32 channelFlags;
        u32 chanmode;
@@ -854,10 +854,10 @@ struct ath_hw {
        u32 globaltxtimeout;
 
        /* ANI */
-       u32 proc_phyerr;
        u32 aniperiod;
        enum ath9k_ani_cmd ani_function;
        u32 ani_skip_count;
+       struct ar5416AniState ani;
 
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
        struct ath_btcoex_hw btcoex_hw;
@@ -882,9 +882,6 @@ struct ath_hw {
        struct ar5416IniArray iniBank6;
        struct ar5416IniArray iniAddac;
        struct ar5416IniArray iniPcieSerdes;
-#ifdef CONFIG_PM_SLEEP
-       struct ar5416IniArray iniPcieSerdesWow;
-#endif
        struct ar5416IniArray iniPcieSerdesLowPower;
        struct ar5416IniArray iniModesFastClock;
        struct ar5416IniArray iniAdditional;
@@ -895,6 +892,9 @@ struct ath_hw {
        struct ar5416IniArray iniCckfirJapan2484;
        struct ar5416IniArray iniModes_9271_ANI_reg;
        struct ar5416IniArray ini_radio_post_sys2ant;
+       struct ar5416IniArray ini_modes_rxgain_5g_xlna;
+       struct ar5416IniArray ini_modes_rxgain_bb_core;
+       struct ar5416IniArray ini_modes_rxgain_bb_postamble;
 
        struct ar5416IniArray iniMac[ATH_INI_NUM_SPLIT];
        struct ar5416IniArray iniBB[ATH_INI_NUM_SPLIT];
@@ -1165,8 +1165,6 @@ static inline void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
 }
 #endif
 
-
-
 #define ATH9K_CLOCK_RATE_CCK           22
 #define ATH9K_CLOCK_RATE_5GHZ_OFDM     40
 #define ATH9K_CLOCK_RATE_2GHZ_OFDM     44
index 2ba4945..16f8b20 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/ath9k_platform.h>
 #include <linux/module.h>
 #include <linux/relay.h>
+#include <net/ieee80211_radiotap.h>
 
 #include "ath9k.h"
 
@@ -431,6 +432,8 @@ static int ath9k_init_queues(struct ath_softc *sc)
        sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
        ath_cabq_update(sc);
 
+       sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0);
+
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i);
                sc->tx.txq_map[i]->mac80211_qnum = i;
@@ -510,6 +513,27 @@ static void ath9k_init_misc(struct ath_softc *sc)
        sc->spec_config.fft_period = 0xF;
 }
 
+static void ath9k_init_platform(struct ath_softc *sc)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       if (common->bus_ops->ath_bus_type != ATH_PCI)
+               return;
+
+       if (sc->driver_data & (ATH9K_PCI_CUS198 |
+                              ATH9K_PCI_CUS230)) {
+               ah->config.xlna_gpio = 9;
+               ah->config.xatten_margin_cfg = true;
+
+               ath_info(common, "Set parameters for %s\n",
+                        (sc->driver_data & ATH9K_PCI_CUS198) ?
+                        "CUS198" : "CUS230");
+       } else if (sc->driver_data & ATH9K_PCI_CUS217) {
+               ath_info(common, "CUS217 card detected\n");
+       }
+}
+
 static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob,
                                    void *ctx)
 {
@@ -601,6 +625,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        common->btcoex_enabled = ath9k_btcoex_enable == 1;
        common->disable_ani = false;
 
+       /*
+        * Platform quirks.
+        */
+       ath9k_init_platform(sc);
+
        /*
         * Enable Antenna diversity only when BTCOEX is disabled
         * and the user manually requests the feature.
@@ -613,9 +642,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        spin_lock_init(&sc->sc_serial_rw);
        spin_lock_init(&sc->sc_pm_lock);
        mutex_init(&sc->mutex);
-#ifdef CONFIG_ATH9K_MAC_DEBUG
-       spin_lock_init(&sc->debug.samp_lock);
-#endif
        tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
        tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
                     (unsigned long)sc);
@@ -755,6 +781,15 @@ static const struct ieee80211_iface_combination if_comb[] = {
        }
 };
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support ath9k_wowlan_support = {
+       .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
+       .n_patterns = MAX_NUM_USER_PATTERN,
+       .pattern_min_len = 1,
+       .pattern_max_len = MAX_PATTERN_SIZE,
+};
+#endif
+
 void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 {
        struct ath_hw *ah = sc->sc_ah;
@@ -769,12 +804,19 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                IEEE80211_HW_SUPPORTS_RC_TABLE;
 
-       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)
-                hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
+               hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+
+               if (AR_SREV_9280_20_OR_LATER(ah))
+                       hw->radiotap_mcs_details |=
+                               IEEE80211_RADIOTAP_MCS_HAVE_STBC;
+       }
 
        if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt)
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
+       hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
+
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_P2P_GO) |
                BIT(NL80211_IFTYPE_P2P_CLIENT) |
@@ -794,21 +836,13 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
        hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 
 #ifdef CONFIG_PM_SLEEP
-
        if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
-           device_can_wakeup(sc->dev)) {
-
-               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                                         WIPHY_WOWLAN_DISCONNECT;
-               hw->wiphy->wowlan.n_patterns = MAX_NUM_USER_PATTERN;
-               hw->wiphy->wowlan.pattern_min_len = 1;
-               hw->wiphy->wowlan.pattern_max_len = MAX_PATTERN_SIZE;
-
-       }
+           (sc->driver_data & ATH9K_PCI_WOW) &&
+           device_can_wakeup(sc->dev))
+               hw->wiphy->wowlan = &ath9k_wowlan_support;
 
        atomic_set(&sc->wow_sleep_proc_intr, -1);
        atomic_set(&sc->wow_got_bmiss_intr, -1);
-
 #endif
 
        hw->queues = 4;
index 849259b..fff5d3c 100644 (file)
@@ -390,9 +390,7 @@ void ath_ani_calibrate(unsigned long data)
        }
 
        /* Verify whether we must check ANI */
-       if (sc->sc_ah->config.enable_ani
-           && (timestamp - common->ani.checkani_timer) >=
-           ah->config.ani_poll_interval) {
+       if ((timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) {
                aniflag = true;
                common->ani.checkani_timer = timestamp;
        }
@@ -418,7 +416,6 @@ void ath_ani_calibrate(unsigned long data)
                longcal ? "long" : "", shortcal ? "short" : "",
                aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
 
-       ath9k_debug_samp_bb_mac(sc);
        ath9k_ps_restore(sc);
 
 set_timer:
@@ -428,9 +425,7 @@ set_timer:
        * short calibration and long calibration.
        */
        cal_interval = ATH_LONG_CALINTERVAL;
-       if (sc->sc_ah->config.enable_ani)
-               cal_interval = min(cal_interval,
-                                  (u32)ah->config.ani_poll_interval);
+       cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval);
        if (!common->ani.caldone)
                cal_interval = min(cal_interval, (u32)short_cal_interval);
 
index 566109a..2ef05eb 100644 (file)
@@ -547,6 +547,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
 
        rs->rs_status = 0;
        rs->rs_flags = 0;
+       rs->flag = 0;
 
        rs->rs_datalen = ads.ds_rxstatus1 & AR_DataLen;
        rs->rs_tstamp = ads.AR_RcvTimestamp;
@@ -586,10 +587,17 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
        rs->rs_moreaggr =
                (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
        rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna);
-       rs->rs_flags =
-               (ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0;
-       rs->rs_flags |=
-               (ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0;
+
+       /* directly mapped flags for ieee80211_rx_status */
+       rs->flag |=
+               (ads.ds_rxstatus3 & AR_GI) ? RX_FLAG_SHORT_GI : 0;
+       rs->flag |=
+               (ads.ds_rxstatus3 & AR_2040) ? RX_FLAG_40MHZ : 0;
+       if (AR_SREV_9280_20_OR_LATER(ah))
+               rs->flag |=
+                       (ads.ds_rxstatus3 & AR_STBC) ?
+                               /* we can only Nss=1 STBC */
+                               (1 << RX_FLAG_STBC_SHIFT) : 0;
 
        if (ads.ds_rxstatus8 & AR_PreDelimCRCErr)
                rs->rs_flags |= ATH9K_RX_DELIM_CRC_PRE;
index 5865f92..b02dfce 100644 (file)
@@ -149,6 +149,7 @@ struct ath_rx_status {
        u32 evm2;
        u32 evm3;
        u32 evm4;
+       u32 flag; /* see enum mac80211_rx_flags */
 };
 
 struct ath_htc_rx_status {
@@ -533,7 +534,8 @@ struct ar5416_desc {
 #define AR_2040             0x00000002
 #define AR_Parallel40       0x00000004
 #define AR_Parallel40_S     2
-#define AR_RxStatusRsvd30   0x000000f8
+#define AR_STBC             0x00000008 /* on ar9280 and later */
+#define AR_RxStatusRsvd30   0x000000f0
 #define AR_RxAntenna       0xffffff00
 #define AR_RxAntenna_S     8
 
index 5092eca..1737a3e 100644 (file)
@@ -193,7 +193,6 @@ static bool ath_prepare_reset(struct ath_softc *sc)
        ath_stop_ani(sc);
        del_timer_sync(&sc->rx_poll_timer);
 
-       ath9k_debug_samp_bb_mac(sc);
        ath9k_hw_disable_interrupts(ah);
 
        if (!ath_drain_all_txq(sc))
@@ -1211,13 +1210,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                ath_update_survey_stats(sc);
                spin_unlock_irqrestore(&common->cc_lock, flags);
 
-               /*
-                * Preserve the current channel values, before updating
-                * the same channel
-                */
-               if (ah->curchan && (old_pos == pos))
-                       ath9k_hw_getnf(ah, ah->curchan);
-
                ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
                                          curchan, channel_type);
 
@@ -1273,7 +1265,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                                curchan->center_freq);
                } else {
                        /* perform spectral scan if requested. */
-                       if (sc->scanning &&
+                       if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
                            sc->spectral_mode == SPECTRAL_CHANSCAN)
                                ath9k_spectral_scan_trigger(hw);
                }
@@ -1690,7 +1682,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
        bool flush = false;
        int ret = 0;
 
-       local_bh_disable();
+       mutex_lock(&sc->mutex);
 
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
@@ -1723,7 +1715,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
                ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n");
        }
 
-       local_bh_enable();
+       mutex_unlock(&sc->mutex);
 
        return ret;
 }
@@ -2007,7 +1999,6 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ath9k_hw_capabilities *pcaps = &ah->caps;
        int pattern_count = 0;
        int i, byte_cnt;
        u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
@@ -2077,36 +2068,9 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
 
        /* Create Disassociate pattern mask */
 
-       if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_EXACT) {
-
-               if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_DWORD) {
-                       /*
-                        * for AR9280, because of hardware limitation, the
-                        * first 4 bytes have to be matched for all patterns.
-                        * the mask for disassociation and de-auth pattern
-                        * matching need to enable the first 4 bytes.
-                        * also the duration field needs to be filled.
-                        */
-                       dis_deauth_mask[0] = 0xf0;
-
-                       /*
-                        * fill in duration field
-                        FIXME: what is the exact value ?
-                        */
-                       dis_deauth_pattern[2] = 0xff;
-                       dis_deauth_pattern[3] = 0xff;
-               } else {
-                       dis_deauth_mask[0] = 0xfe;
-               }
-
-               dis_deauth_mask[1] = 0x03;
-               dis_deauth_mask[2] = 0xc0;
-       } else {
-               dis_deauth_mask[0] = 0xef;
-               dis_deauth_mask[1] = 0x3f;
-               dis_deauth_mask[2] = 0x00;
-               dis_deauth_mask[3] = 0xfc;
-       }
+       dis_deauth_mask[0] = 0xfe;
+       dis_deauth_mask[1] = 0x03;
+       dis_deauth_mask[2] = 0xc0;
 
        ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
 
@@ -2342,15 +2306,13 @@ static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
 static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
 {
        struct ath_softc *sc = hw->priv;
-
-       sc->scanning = 1;
+       set_bit(SC_OP_SCANNING, &sc->sc_flags);
 }
 
 static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
 {
        struct ath_softc *sc = hw->priv;
-
-       sc->scanning = 0;
+       clear_bit(SC_OP_SCANNING, &sc->sc_flags);
 }
 
 struct ieee80211_ops ath9k_ops = {
@@ -2378,6 +2340,7 @@ struct ieee80211_ops ath9k_ops = {
        .flush              = ath9k_flush,
        .tx_frames_pending  = ath9k_tx_frames_pending,
        .tx_last_beacon     = ath9k_tx_last_beacon,
+       .release_buffered_frames = ath9k_release_buffered_frames,
        .get_stats          = ath9k_get_stats,
        .set_antenna        = ath9k_set_antenna,
        .get_antenna        = ath9k_get_antenna,
index 0e0d395..c585c9b 100644 (file)
@@ -34,8 +34,108 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
        { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI   */
        { PCI_VDEVICE(ATHEROS, 0x002E) }, /* PCI-E */
        { PCI_VDEVICE(ATHEROS, 0x0030) }, /* PCI-E  AR9300 */
+
+       /* PCI-E CUS198 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2086),
+         .driver_data = ATH9K_PCI_CUS198 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1237),
+         .driver_data = ATH9K_PCI_CUS198 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2126),
+         .driver_data = ATH9K_PCI_CUS198 },
+
+       /* PCI-E CUS230 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2152),
+         .driver_data = ATH9K_PCI_CUS230 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_FOXCONN,
+                        0xE075),
+         .driver_data = ATH9K_PCI_CUS230 },
+
        { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E  AR9485 */
        { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E  AR9580 */
+
+       /* PCI-E CUS217 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2116),
+         .driver_data = ATH9K_PCI_CUS217 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        0x11AD, /* LITEON */
+                        0x6661),
+         .driver_data = ATH9K_PCI_CUS217 },
+
+       /* AR9462 with WoW support */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        PCI_VENDOR_ID_ATHEROS,
+                        0x3117),
+         .driver_data = ATH9K_PCI_WOW },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        PCI_VENDOR_ID_LENOVO,
+                        0x3214),
+         .driver_data = ATH9K_PCI_WOW },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        PCI_VENDOR_ID_ATTANSIC,
+                        0x0091),
+         .driver_data = ATH9K_PCI_WOW },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2110),
+         .driver_data = ATH9K_PCI_WOW },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        PCI_VENDOR_ID_ASUSTEK,
+                        0x850E),
+         .driver_data = ATH9K_PCI_WOW },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        0x11AD, /* LITEON */
+                        0x6631),
+         .driver_data = ATH9K_PCI_WOW },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        0x11AD, /* LITEON */
+                        0x6641),
+         .driver_data = ATH9K_PCI_WOW },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        PCI_VENDOR_ID_HP,
+                        0x1864),
+         .driver_data = ATH9K_PCI_WOW },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        0x14CD, /* USI */
+                        0x0063),
+         .driver_data = ATH9K_PCI_WOW },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        0x14CD, /* USI */
+                        0x0064),
+         .driver_data = ATH9K_PCI_WOW },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        0x10CF, /* Fujitsu */
+                        0x1783),
+         .driver_data = ATH9K_PCI_WOW },
+
        { PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E  AR9462 */
        { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E  AR1111/AR9485 */
        { PCI_VDEVICE(ATHEROS, 0x0036) }, /* PCI-E  AR9565 */
@@ -221,6 +321,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        sc->hw = hw;
        sc->dev = &pdev->dev;
        sc->mem = pcim_iomap_table(pdev)[0];
+       sc->driver_data = id->driver_data;
 
        /* Will be cleared in ath9k_start() */
        set_bit(SC_OP_INVALID, &sc->sc_flags);
index 8be2b5d..865e043 100644 (file)
@@ -868,10 +868,7 @@ static int ath9k_process_rate(struct ath_common *common,
        if (rx_stats->rs_rate & 0x80) {
                /* HT rate */
                rxs->flag |= RX_FLAG_HT;
-               if (rx_stats->rs_flags & ATH9K_RX_2040)
-                       rxs->flag |= RX_FLAG_40MHZ;
-               if (rx_stats->rs_flags & ATH9K_RX_GI)
-                       rxs->flag |= RX_FLAG_SHORT_GI;
+               rxs->flag |= rx_stats->flag;
                rxs->rate_idx = rx_stats->rs_rate & 0x7f;
                return 0;
        }
@@ -958,11 +955,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
        if (rx_stats->rs_more)
                return 0;
 
-       ath9k_process_rssi(common, hw, hdr, rx_stats);
-
        if (ath9k_process_rate(common, hw, rx_stats, rx_status))
                return -EINVAL;
 
+       ath9k_process_rssi(common, hw, hdr, rx_stats);
+
        rx_status->band = hw->conf.chandef.chan->band;
        rx_status->freq = hw->conf.chandef.chan->center_freq;
        rx_status->signal = ah->noise + rx_stats->rs_rssi;
index f7c90cc..5af9744 100644 (file)
 #define AR_SREV_REVISION_9580_10       4 /* AR9580 1.0 */
 #define AR_SREV_VERSION_9462           0x280
 #define AR_SREV_REVISION_9462_20       2
+#define AR_SREV_REVISION_9462_21       3
 #define AR_SREV_VERSION_9565            0x2C0
 #define AR_SREV_REVISION_9565_10        0
 #define AR_SREV_VERSION_9550           0x400
 
 #define AR_SREV_9462(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462))
-
 #define AR_SREV_9462_20(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
-       ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_20))
+        ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_20))
+#define AR_SREV_9462_21(_ah) \
+       (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+        ((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_21))
+#define AR_SREV_9462_20_OR_LATER(_ah) \
+       (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+        ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9462_20))
+#define AR_SREV_9462_21_OR_LATER(_ah) \
+       (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+        ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9462_21))
 
 #define AR_SREV_9565(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565))
index 9f85630..81c88dd 100644 (file)
@@ -34,17 +34,6 @@ const char *ath9k_hw_wow_event_to_string(u32 wow_event)
 }
 EXPORT_SYMBOL(ath9k_hw_wow_event_to_string);
 
-static void ath9k_hw_config_serdes_wow_sleep(struct ath_hw *ah)
-{
-       int i;
-
-       for (i = 0; i < ah->iniPcieSerdesWow.ia_rows; i++)
-               REG_WRITE(ah, INI_RA(&ah->iniPcieSerdesWow, i, 0),
-                         INI_RA(&ah->iniPcieSerdesWow, i, 1));
-
-       usleep_range(1000, 1500);
-}
-
 static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
@@ -58,15 +47,8 @@ static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
                ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n",
                        REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW));
                return;
-       } else {
-               if (!AR_SREV_9300_20_OR_LATER(ah))
-                       REG_WRITE(ah, AR_RXDP, 0x0);
        }
 
-       /* AR9280 WoW has sleep issue, do not set it to sleep */
-       if (AR_SREV_9280_20(ah))
-               return;
-
        REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
 }
 
@@ -84,27 +66,16 @@ static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
 
        /* set the transmit buffer */
        ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16));
-
-       if (!(AR_SREV_9300_20_OR_LATER(ah)))
-               ctl[0] += (KAL_ANTENNA_MODE << 25);
-
        ctl[1] = 0;
        ctl[3] = 0xb;   /* OFDM_6M hardware value for this rate */
        ctl[4] = 0;
        ctl[7] = (ah->txchainmask) << 2;
-
-       if (AR_SREV_9300_20_OR_LATER(ah))
-               ctl[2] = 0xf << 16; /* tx_tries 0 */
-       else
-               ctl[2] = 0x7 << 16; /* tx_tries 0 */
-
+       ctl[2] = 0xf << 16; /* tx_tries 0 */
 
        for (i = 0; i < KAL_NUM_DESC_WORDS; i++)
                REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
 
-       /* for AR9300 family 13 descriptor words */
-       if (AR_SREV_9300_20_OR_LATER(ah))
-               REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
+       REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
 
        data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) |
                       (KAL_TO_DS << 8) | (KAL_DURATION_ID << 16);
@@ -183,9 +154,6 @@ void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
 
        ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT);
 
-       if (!AR_SREV_9285_12_OR_LATER(ah))
-               return;
-
        if (pattern_count < 4) {
                /* Pattern 0-3 uses AR_WOW_LENGTH1 register */
                set = (pattern_len & AR_WOW_LENGTH_MAX) <<
@@ -207,6 +175,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
 {
        u32 wow_status = 0;
        u32 val = 0, rval;
+
        /*
         * read the WoW status register to know
         * the wakeup reason
@@ -223,19 +192,14 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
        val &= ah->wow_event_mask;
 
        if (val) {
-
                if (val & AR_WOW_MAGIC_PAT_FOUND)
                        wow_status |= AH_WOW_MAGIC_PATTERN_EN;
-
                if (AR_WOW_PATTERN_FOUND(val))
                        wow_status |= AH_WOW_USER_PATTERN_EN;
-
                if (val & AR_WOW_KEEP_ALIVE_FAIL)
                        wow_status |= AH_WOW_LINK_CHANGE;
-
                if (val & AR_WOW_BEACON_FAIL)
                        wow_status |= AH_WOW_BEACON_MISS;
-
        }
 
        /*
@@ -254,17 +218,6 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
        REG_WRITE(ah, AR_WOW_PATTERN,
                  AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN)));
 
-       /*
-        * tie reset register for AR9002 family of chipsets
-        * NB: not tieing it back might have some repurcussions.
-        */
-
-       if (!AR_SREV_9300_20_OR_LATER(ah)) {
-               REG_SET_BIT(ah, AR_WA, AR_WA_UNTIE_RESET_EN |
-                           AR_WA_POR_SHORT | AR_WA_RESET_EN);
-       }
-
-
        /*
         * restore the beacon threshold to init value
         */
@@ -277,8 +230,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
         * reset to our Chip's Power On Reset so that any PCI-E
         * reset from the bus will not reset our chip
         */
-
-       if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress)
+       if (ah->is_pciexpress)
                ath9k_hw_configpcipowersave(ah, false);
 
        ah->wow_event_mask = 0;
@@ -298,7 +250,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
         * are from the 'pattern_enable' in this function and
         * 'pattern_count' of ath9k_hw_wow_apply_pattern()
         */
-
        wow_event_mask = ah->wow_event_mask;
 
        /*
@@ -306,50 +257,15 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
         * WOW sleep, we do want the Reset from the PCI-E to disturb
         * our hw state
         */
-
        if (ah->is_pciexpress) {
-
                /*
                 * we need to untie the internal POR (power-on-reset)
                 * to the external PCI-E reset. We also need to tie
                 * the PCI-E Phy reset to the PCI-E reset.
                 */
-
-               if (AR_SREV_9300_20_OR_LATER(ah)) {
-                       set = AR_WA_RESET_EN | AR_WA_POR_SHORT;
-                       clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
-                       REG_RMW(ah, AR_WA, set, clr);
-               } else {
-                       if (AR_SREV_9285(ah) || AR_SREV_9287(ah))
-                               set = AR9285_WA_DEFAULT;
-                       else
-                               set = AR9280_WA_DEFAULT;
-
-                       /*
-                        * In AR9280 and AR9285, bit 14 in WA register
-                        * (disable L1) should only be set when device
-                        * enters D3 state and be cleared when device
-                        * comes back to D0
-                        */
-
-                       if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE)
-                               set |= AR_WA_D3_L1_DISABLE;
-
-                       clr = AR_WA_UNTIE_RESET_EN;
-                       set |= AR_WA_RESET_EN | AR_WA_POR_SHORT;
-                       REG_RMW(ah, AR_WA, set, clr);
-
-                       /*
-                        * for WoW sleep, we reprogram the SerDes so that the
-                        * PLL and CLK REQ are both enabled. This uses more
-                        * power but otherwise WoW sleep is unstable and the
-                        * chip may disappear.
-                        */
-
-                       if (AR_SREV_9285_12_OR_LATER(ah))
-                               ath9k_hw_config_serdes_wow_sleep(ah);
-
-               }
+               set = AR_WA_RESET_EN | AR_WA_POR_SHORT;
+               clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
+               REG_RMW(ah, AR_WA, set, clr);
        }
 
        /*
@@ -378,7 +294,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
         * Program default values for pattern backoff, aifs/slot/KAL count,
         * beacon miss timeout, KAL timeout, etc.
         */
-
        set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF);
        REG_SET_BIT(ah, AR_WOW_PATTERN, set);
 
@@ -398,7 +313,7 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
        /*
         * Keep alive timo in ms except AR9280
         */
-       if (!pattern_enable || AR_SREV_9280(ah))
+       if (!pattern_enable)
                set = AR_WOW_KEEP_ALIVE_NEVER;
        else
                set = KAL_TIMEOUT * 32;
@@ -420,7 +335,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
        /*
         * Configure MAC WoW Registers
         */
-
        set = 0;
        /* Send keep alive timeouts anyway */
        clr = AR_WOW_KEEP_ALIVE_AUTO_DIS;
@@ -430,16 +344,9 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
        else
                set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
 
-       /*
-        * FIXME: For now disable keep alive frame
-        * failure. This seems to sometimes trigger
-        * unnecessary wake up with AR9485 chipsets.
-        */
        set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
-
        REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr);
 
-
        /*
         * we are relying on a bmiss failure. ensure we have
         * enough threshold to prevent false positives
@@ -473,14 +380,8 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
        set |= AR_WOW_MAC_INTR_EN;
        REG_RMW(ah, AR_WOW_PATTERN, set, clr);
 
-       /*
-        * For AR9285 and later version of chipsets
-        * enable WoW pattern match for packets less
-        * than 256 bytes for all patterns
-        */
-       if (AR_SREV_9285_12_OR_LATER(ah))
-               REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
-                         AR_WOW_PATTERN_SUPPORTED);
+       REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
+                 AR_WOW_PATTERN_SUPPORTED);
 
        /*
         * Set the power states appropriately and enable PME
@@ -488,43 +389,32 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
        clr = 0;
        set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN |
              AR_PMCTRL_PWR_PM_CTRL_ENA;
-       /*
-        * This is needed for AR9300 chipsets to wake-up
-        * the host.
-        */
-       if (AR_SREV_9300_20_OR_LATER(ah))
-               clr = AR_PCIE_PM_CTRL_ENA;
 
+       clr = AR_PCIE_PM_CTRL_ENA;
        REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr);
 
-       if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
-               /*
-                * this is needed to prevent the chip waking up
-                * the host within 3-4 seconds with certain
-                * platform/BIOS. The fix is to enable
-                * D1 & D3 to match original definition and
-                * also match the OTP value. Anyway this
-                * is more related to SW WOW.
-                */
-               clr = AR_PMCTRL_PWR_STATE_D1D3;
-               REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
-
-               set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
-               REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
-       }
-
+       /*
+        * this is needed to prevent the chip waking up
+        * the host within 3-4 seconds with certain
+        * platform/BIOS. The fix is to enable
+        * D1 & D3 to match original definition and
+        * also match the OTP value. Anyway this
+        * is more related to SW WOW.
+        */
+       clr = AR_PMCTRL_PWR_STATE_D1D3;
+       REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
 
+       set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
+       REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
 
        REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM);
 
-       if (AR_SREV_9300_20_OR_LATER(ah)) {
-               /* to bring down WOW power low margin */
-               set = BIT(13);
-               REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set);
-               /* HW WoW */
-               clr = BIT(5);
-               REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
-       }
+       /* to bring down WOW power low margin */
+       set = BIT(13);
+       REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set);
+       /* HW WoW */
+       clr = BIT(5);
+       REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
 
        ath9k_hw_set_powermode_wow_sleep(ah);
        ah->wow_event_mask = wow_event_mask;
index 83ab6be..c59ae43 100644 (file)
@@ -518,6 +518,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                        ath_tx_complete_buf(sc, bf, txq, &bf_head, ts,
                                !txfail);
                } else {
+                       if (tx_info->flags & IEEE80211_TX_STATUS_EOSP) {
+                               tx_info->flags &= ~IEEE80211_TX_STATUS_EOSP;
+                               ieee80211_sta_eosp(sta);
+                       }
                        /* retry the un-acked ones */
                        if (bf->bf_next == NULL && bf_last->bf_stale) {
                                struct ath_buf *tbf;
@@ -786,25 +790,20 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
        return ndelim;
 }
 
-static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
-                                            struct ath_txq *txq,
-                                            struct ath_atx_tid *tid,
-                                            struct list_head *bf_q,
-                                            int *aggr_len)
+static struct ath_buf *
+ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
+                       struct ath_atx_tid *tid)
 {
-#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
-       struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
-       int rl = 0, nframes = 0, ndelim, prev_al = 0;
-       u16 aggr_limit = 0, al = 0, bpad = 0,
-               al_delta, h_baw = tid->baw_size / 2;
-       enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
-       struct ieee80211_tx_info *tx_info;
        struct ath_frame_info *fi;
        struct sk_buff *skb;
+       struct ath_buf *bf;
        u16 seqno;
 
-       do {
+       while (1) {
                skb = skb_peek(&tid->buf_q);
+               if (!skb)
+                       break;
+
                fi = get_frame_info(skb);
                bf = fi->bf;
                if (!fi->bf)
@@ -820,10 +819,8 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                seqno = bf->bf_state.seqno;
 
                /* do not step over block-ack window */
-               if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) {
-                       status = ATH_AGGR_BAW_CLOSED;
+               if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno))
                        break;
-               }
 
                if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) {
                        struct ath_tx_status ts = {};
@@ -837,6 +834,40 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                        continue;
                }
 
+               bf->bf_next = NULL;
+               bf->bf_lastbf = bf;
+               return bf;
+       }
+
+       return NULL;
+}
+
+static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
+                                            struct ath_txq *txq,
+                                            struct ath_atx_tid *tid,
+                                            struct list_head *bf_q,
+                                            int *aggr_len)
+{
+#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
+       struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
+       int rl = 0, nframes = 0, ndelim, prev_al = 0;
+       u16 aggr_limit = 0, al = 0, bpad = 0,
+               al_delta, h_baw = tid->baw_size / 2;
+       enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
+       struct ieee80211_tx_info *tx_info;
+       struct ath_frame_info *fi;
+       struct sk_buff *skb;
+
+       do {
+               bf = ath_tx_get_tid_subframe(sc, txq, tid);
+               if (!bf) {
+                       status = ATH_AGGR_BAW_CLOSED;
+                       break;
+               }
+
+               skb = bf->bf_mpdu;
+               fi = get_frame_info(skb);
+
                if (!bf_first)
                        bf_first = bf;
 
@@ -882,7 +913,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
 
                /* link buffers of this frame to the aggregate */
                if (!fi->retries)
-                       ath_tx_addto_baw(sc, tid, seqno);
+                       ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
                bf->bf_state.ndelim = ndelim;
 
                __skb_unlink(skb, &tid->buf_q);
@@ -1090,10 +1121,8 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                             struct ath_txq *txq, int len)
 {
        struct ath_hw *ah = sc->sc_ah;
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
-       struct ath_buf *bf_first = bf;
+       struct ath_buf *bf_first = NULL;
        struct ath_tx_info info;
-       bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR);
 
        memset(&info, 0, sizeof(info));
        info.is_first = true;
@@ -1101,24 +1130,11 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
        info.txpower = MAX_RATE_POWER;
        info.qcu = txq->axq_qnum;
 
-       info.flags = ATH9K_TXDESC_INTREQ;
-       if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
-               info.flags |= ATH9K_TXDESC_NOACK;
-       if (tx_info->flags & IEEE80211_TX_CTL_LDPC)
-               info.flags |= ATH9K_TXDESC_LDPC;
-
-       ath_buf_set_rate(sc, bf, &info, len);
-
-       if (tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
-               info.flags |= ATH9K_TXDESC_CLRDMASK;
-
-       if (bf->bf_state.bfs_paprd)
-               info.flags |= (u32) bf->bf_state.bfs_paprd << ATH9K_TXDESC_PAPRD_S;
-
-
        while (bf) {
                struct sk_buff *skb = bf->bf_mpdu;
+               struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
                struct ath_frame_info *fi = get_frame_info(skb);
+               bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR);
 
                info.type = get_hw_packet_type(skb);
                if (bf->bf_next)
@@ -1126,6 +1142,26 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                else
                        info.link = 0;
 
+               if (!bf_first) {
+                       bf_first = bf;
+
+                       info.flags = ATH9K_TXDESC_INTREQ;
+                       if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) ||
+                           txq == sc->tx.uapsdq)
+                               info.flags |= ATH9K_TXDESC_CLRDMASK;
+
+                       if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
+                               info.flags |= ATH9K_TXDESC_NOACK;
+                       if (tx_info->flags & IEEE80211_TX_CTL_LDPC)
+                               info.flags |= ATH9K_TXDESC_LDPC;
+
+                       if (bf->bf_state.bfs_paprd)
+                               info.flags |= (u32) bf->bf_state.bfs_paprd <<
+                                             ATH9K_TXDESC_PAPRD_S;
+
+                       ath_buf_set_rate(sc, bf, &info, len);
+               }
+
                info.buf_addr[0] = bf->bf_buf_addr;
                info.buf_len[0] = skb->len;
                info.pkt_len = fi->framelen;
@@ -1135,7 +1171,7 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                if (aggr) {
                        if (bf == bf_first)
                                info.aggr = AGGR_BUF_FIRST;
-                       else if (!bf->bf_next)
+                       else if (bf == bf_first->bf_lastbf)
                                info.aggr = AGGR_BUF_LAST;
                        else
                                info.aggr = AGGR_BUF_MIDDLE;
@@ -1144,6 +1180,9 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                        info.aggr_len = len;
                }
 
+               if (bf == bf_first->bf_lastbf)
+                       bf_first = NULL;
+
                ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
                bf = bf->bf_next;
        }
@@ -1328,6 +1367,70 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
        ath_txq_unlock_complete(sc, txq);
 }
 
+void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
+                                  struct ieee80211_sta *sta,
+                                  u16 tids, int nframes,
+                                  enum ieee80211_frame_release_type reason,
+                                  bool more_data)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_node *an = (struct ath_node *)sta->drv_priv;
+       struct ath_txq *txq = sc->tx.uapsdq;
+       struct ieee80211_tx_info *info;
+       struct list_head bf_q;
+       struct ath_buf *bf_tail = NULL, *bf;
+       int sent = 0;
+       int i;
+
+       INIT_LIST_HEAD(&bf_q);
+       for (i = 0; tids && nframes; i++, tids >>= 1) {
+               struct ath_atx_tid *tid;
+
+               if (!(tids & 1))
+                       continue;
+
+               tid = ATH_AN_2_TID(an, i);
+               if (tid->paused)
+                       continue;
+
+               ath_txq_lock(sc, tid->ac->txq);
+               while (!skb_queue_empty(&tid->buf_q) && nframes > 0) {
+                       bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid);
+                       if (!bf)
+                               break;
+
+                       __skb_unlink(bf->bf_mpdu, &tid->buf_q);
+                       list_add_tail(&bf->list, &bf_q);
+                       ath_set_rates(tid->an->vif, tid->an->sta, bf);
+                       ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
+                       bf->bf_state.bf_type &= ~BUF_AGGR;
+                       if (bf_tail)
+                               bf_tail->bf_next = bf;
+
+                       bf_tail = bf;
+                       nframes--;
+                       sent++;
+                       TX_STAT_INC(txq->axq_qnum, a_queued_hw);
+
+                       if (skb_queue_empty(&tid->buf_q))
+                               ieee80211_sta_set_buffered(an->sta, i, false);
+               }
+               ath_txq_unlock_complete(sc, tid->ac->txq);
+       }
+
+       if (list_empty(&bf_q))
+               return;
+
+       info = IEEE80211_SKB_CB(bf_tail->bf_mpdu);
+       info->flags |= IEEE80211_TX_STATUS_EOSP;
+
+       bf = list_first_entry(&bf_q, struct ath_buf, list);
+       ath_txq_lock(sc, txq);
+       ath_tx_fill_desc(sc, bf, txq, 0);
+       ath_tx_txqaddbuf(sc, txq, &bf_q, false);
+       ath_txq_unlock(sc, txq);
+}
+
 /********************/
 /* Queue Management */
 /********************/
@@ -1679,14 +1782,19 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
        }
 
        if (!internal) {
-               txq->axq_depth++;
-               if (bf_is_ampdu_not_probing(bf))
-                       txq->axq_ampdu_depth++;
+               while (bf) {
+                       txq->axq_depth++;
+                       if (bf_is_ampdu_not_probing(bf))
+                               txq->axq_ampdu_depth++;
+
+                       bf = bf->bf_lastbf->bf_next;
+               }
        }
 }
 
-static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
-                             struct sk_buff *skb, struct ath_tx_control *txctl)
+static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_txq *txq,
+                             struct ath_atx_tid *tid, struct sk_buff *skb,
+                             struct ath_tx_control *txctl)
 {
        struct ath_frame_info *fi = get_frame_info(skb);
        struct list_head bf_head;
@@ -1699,21 +1807,22 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
         * - seqno is not within block-ack window
         * - h/w queue depth exceeds low water mark
         */
-       if (!skb_queue_empty(&tid->buf_q) || tid->paused ||
-           !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) ||
-           txctl->txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) {
+       if ((!skb_queue_empty(&tid->buf_q) || tid->paused ||
+            !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) ||
+            txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) &&
+           txq != sc->tx.uapsdq) {
                /*
                 * Add this frame to software queue for scheduling later
                 * for aggregation.
                 */
-               TX_STAT_INC(txctl->txq->axq_qnum, a_queued_sw);
+               TX_STAT_INC(txq->axq_qnum, a_queued_sw);
                __skb_queue_tail(&tid->buf_q, skb);
                if (!txctl->an || !txctl->an->sleeping)
-                       ath_tx_queue_tid(txctl->txq, tid);
+                       ath_tx_queue_tid(txq, tid);
                return;
        }
 
-       bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+       bf = ath_tx_setup_buffer(sc, txq, tid, skb);
        if (!bf) {
                ieee80211_free_txskb(sc->hw, skb);
                return;
@@ -1728,10 +1837,10 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
        ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
 
        /* Queue to h/w without aggregation */
-       TX_STAT_INC(txctl->txq->axq_qnum, a_queued_hw);
+       TX_STAT_INC(txq->axq_qnum, a_queued_hw);
        bf->bf_lastbf = bf;
-       ath_tx_fill_desc(sc, bf, txctl->txq, fi->framelen);
-       ath_tx_txqaddbuf(sc, txctl->txq, &bf_head, false);
+       ath_tx_fill_desc(sc, bf, txq, fi->framelen);
+       ath_tx_txqaddbuf(sc, txq, &bf_head, false);
 }
 
 static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
@@ -1869,22 +1978,16 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
        return bf;
 }
 
-/* Upon failure caller should free skb */
-int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
-                struct ath_tx_control *txctl)
+static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb,
+                         struct ath_tx_control *txctl)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_sta *sta = txctl->sta;
        struct ieee80211_vif *vif = info->control.vif;
        struct ath_softc *sc = hw->priv;
-       struct ath_txq *txq = txctl->txq;
-       struct ath_atx_tid *tid = NULL;
-       struct ath_buf *bf;
-       int padpos, padsize;
        int frmlen = skb->len + FCS_LEN;
-       u8 tidno;
-       int q;
+       int padpos, padsize;
 
        /* NOTE:  sta can be NULL according to net/mac80211.h */
        if (sta)
@@ -1905,6 +2008,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
        }
 
+       if ((vif && vif->type != NL80211_IFTYPE_AP &&
+                   vif->type != NL80211_IFTYPE_AP_VLAN) ||
+           !ieee80211_is_data(hdr->frame_control))
+               info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
+
        /* Add the padding after the header if this is not already done */
        padpos = ieee80211_hdrlen(hdr->frame_control);
        padsize = padpos & 3;
@@ -1914,16 +2022,34 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 
                skb_push(skb, padsize);
                memmove(skb->data, skb->data + padsize, padpos);
-               hdr = (struct ieee80211_hdr *) skb->data;
        }
 
-       if ((vif && vif->type != NL80211_IFTYPE_AP &&
-                   vif->type != NL80211_IFTYPE_AP_VLAN) ||
-           !ieee80211_is_data(hdr->frame_control))
-               info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
-
        setup_frame_info(hw, sta, skb, frmlen);
+       return 0;
+}
+
+
+/* Upon failure caller should free skb */
+int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
+                struct ath_tx_control *txctl)
+{
+       struct ieee80211_hdr *hdr;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_sta *sta = txctl->sta;
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath_softc *sc = hw->priv;
+       struct ath_txq *txq = txctl->txq;
+       struct ath_atx_tid *tid = NULL;
+       struct ath_buf *bf;
+       u8 tidno;
+       int q;
+       int ret;
+
+       ret = ath_tx_prepare(hw, skb, txctl);
+       if (ret)
+           return ret;
 
+       hdr = (struct ieee80211_hdr *) skb->data;
        /*
         * At this point, the vif, hw_key and sta pointers in the tx control
         * info are no longer valid (overwritten by the ath_frame_info data.
@@ -1939,6 +2065,12 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                txq->stopped = true;
        }
 
+       if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) {
+               ath_txq_unlock(sc, txq);
+               txq = sc->tx.uapsdq;
+               ath_txq_lock(sc, txq);
+       }
+
        if (txctl->an && ieee80211_is_data_qos(hdr->frame_control)) {
                tidno = ieee80211_get_qos_ctl(hdr)[0] &
                        IEEE80211_QOS_CTL_TID_MASK;
@@ -1952,11 +2084,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                 * Try aggregation if it's a unicast data frame
                 * and the destination is HT capable.
                 */
-               ath_tx_send_ampdu(sc, tid, skb, txctl);
+               ath_tx_send_ampdu(sc, txq, tid, skb, txctl);
                goto out;
        }
 
-       bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+       bf = ath_tx_setup_buffer(sc, txq, tid, skb);
        if (!bf) {
                if (txctl->paprd)
                        dev_kfree_skb_any(skb);
@@ -1971,7 +2103,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                bf->bf_state.bfs_paprd_timestamp = jiffies;
 
        ath_set_rates(vif, sta, bf);
-       ath_tx_send_normal(sc, txctl->txq, tid, skb);
+       ath_tx_send_normal(sc, txq, tid, skb);
 
 out:
        ath_txq_unlock(sc, txq);
@@ -1979,6 +2111,74 @@ out:
        return 0;
 }
 
+void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                struct sk_buff *skb)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_tx_control txctl = {
+               .txq = sc->beacon.cabq
+       };
+       struct ath_tx_info info = {};
+       struct ieee80211_hdr *hdr;
+       struct ath_buf *bf_tail = NULL;
+       struct ath_buf *bf;
+       LIST_HEAD(bf_q);
+       int duration = 0;
+       int max_duration;
+
+       max_duration =
+               sc->cur_beacon_conf.beacon_interval * 1000 *
+               sc->cur_beacon_conf.dtim_period / ATH_BCBUF;
+
+       do {
+               struct ath_frame_info *fi = get_frame_info(skb);
+
+               if (ath_tx_prepare(hw, skb, &txctl))
+                       break;
+
+               bf = ath_tx_setup_buffer(sc, txctl.txq, NULL, skb);
+               if (!bf)
+                       break;
+
+               bf->bf_lastbf = bf;
+               ath_set_rates(vif, NULL, bf);
+               ath_buf_set_rate(sc, bf, &info, fi->framelen);
+               duration += info.rates[0].PktDuration;
+               if (bf_tail)
+                       bf_tail->bf_next = bf;
+
+               list_add_tail(&bf->list, &bf_q);
+               bf_tail = bf;
+               skb = NULL;
+
+               if (duration > max_duration)
+                       break;
+
+               skb = ieee80211_get_buffered_bc(hw, vif);
+       } while(skb);
+
+       if (skb)
+               ieee80211_free_txskb(hw, skb);
+
+       if (list_empty(&bf_q))
+               return;
+
+       bf = list_first_entry(&bf_q, struct ath_buf, list);
+       hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data;
+
+       if (hdr->frame_control & IEEE80211_FCTL_MOREDATA) {
+               hdr->frame_control &= ~IEEE80211_FCTL_MOREDATA;
+               dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
+                       sizeof(*hdr), DMA_TO_DEVICE);
+       }
+
+       ath_txq_lock(sc, txctl.txq);
+       ath_tx_fill_desc(sc, bf, txctl.txq, 0);
+       ath_tx_txqaddbuf(sc, txctl.txq, &bf_q, false);
+       TX_STAT_INC(txctl.txq->axq_qnum, queued);
+       ath_txq_unlock(sc, txctl.txq);
+}
+
 /*****************/
 /* TX Completion */
 /*****************/
@@ -2024,7 +2224,12 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
        }
        spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
+       __skb_queue_tail(&txq->complete_q, skb);
+
        q = skb_get_queue_mapping(skb);
+       if (txq == sc->tx.uapsdq)
+               txq = sc->tx.txq_map[q];
+
        if (txq == sc->tx.txq_map[q]) {
                if (WARN_ON(--txq->pending_frames < 0))
                        txq->pending_frames = 0;
@@ -2035,8 +2240,6 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
                        txq->stopped = false;
                }
        }
-
-       __skb_queue_tail(&txq->complete_q, skb);
 }
 
 static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
index 9dce106..8596aba 100644 (file)
@@ -133,6 +133,9 @@ struct carl9170_sta_tid {
 
        /* Preaggregation reorder queue */
        struct sk_buff_head queue;
+
+       struct ieee80211_sta *sta;
+       struct ieee80211_vif *vif;
 };
 
 #define CARL9170_QUEUE_TIMEOUT         256
index e9010a4..4a33c6e 100644 (file)
@@ -1448,6 +1448,8 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
                tid_info->state = CARL9170_TID_STATE_PROGRESS;
                tid_info->tid = tid;
                tid_info->max = sta_info->ampdu_max_len;
+               tid_info->sta = sta;
+               tid_info->vif = vif;
 
                INIT_LIST_HEAD(&tid_info->list);
                INIT_LIST_HEAD(&tid_info->tmp_list);
@@ -1857,6 +1859,7 @@ void *carl9170_alloc(size_t priv_size)
                     IEEE80211_HW_SUPPORTS_PS |
                     IEEE80211_HW_PS_NULLFUNC_STACK |
                     IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC |
+                    IEEE80211_HW_SUPPORTS_RC_TABLE |
                     IEEE80211_HW_SIGNAL_DBM;
 
        if (!modparam_noht) {
index c61cafa..e3f696e 100644 (file)
@@ -625,7 +625,7 @@ static void carl9170_tx_ampdu_timeout(struct ar9170 *ar)
                    msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT)))
                        goto unlock;
 
-               sta = __carl9170_get_tx_sta(ar, skb);
+               sta = iter->sta;
                if (WARN_ON(!sta))
                        goto unlock;
 
@@ -866,6 +866,93 @@ static bool carl9170_tx_cts_check(struct ar9170 *ar,
        return false;
 }
 
+static void carl9170_tx_get_rates(struct ar9170 *ar,
+                                 struct ieee80211_vif *vif,
+                                 struct ieee80211_sta *sta,
+                                 struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info;
+
+       BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES);
+       BUILD_BUG_ON(IEEE80211_TX_MAX_RATES > IEEE80211_TX_RATE_TABLE_SIZE);
+
+       info = IEEE80211_SKB_CB(skb);
+
+       ieee80211_get_tx_rates(vif, sta, skb,
+                              info->control.rates,
+                              IEEE80211_TX_MAX_RATES);
+}
+
+static void carl9170_tx_apply_rateset(struct ar9170 *ar,
+                                     struct ieee80211_tx_info *sinfo,
+                                     struct sk_buff *skb)
+{
+       struct ieee80211_tx_rate *txrate;
+       struct ieee80211_tx_info *info;
+       struct _carl9170_tx_superframe *txc = (void *) skb->data;
+       int i;
+       bool ampdu;
+       bool no_ack;
+
+       info = IEEE80211_SKB_CB(skb);
+       ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU);
+       no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK);
+
+       /* Set the rate control probe flag for all (sub-) frames.
+        * This is because the TX_STATS_AMPDU flag is only set on
+        * the last frame, so it has to be inherited.
+        */
+       info->flags |= (sinfo->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE);
+
+       /* NOTE: For the first rate, the ERP & AMPDU flags are directly
+        * taken from mac_control. For all fallback rate, the firmware
+        * updates the mac_control flags from the rate info field.
+        */
+       for (i = 0; i < CARL9170_TX_MAX_RATES; i++) {
+               __le32 phy_set;
+
+               txrate = &sinfo->control.rates[i];
+               if (txrate->idx < 0)
+                       break;
+
+               phy_set = carl9170_tx_physet(ar, info, txrate);
+               if (i == 0) {
+                       __le16 mac_tmp = cpu_to_le16(0);
+
+                       /* first rate - part of the hw's frame header */
+                       txc->f.phy_control = phy_set;
+
+                       if (ampdu && txrate->flags & IEEE80211_TX_RC_MCS)
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR);
+
+                       if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
+                       else if (carl9170_tx_cts_check(ar, txrate))
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
+
+                       txc->f.mac_control |= mac_tmp;
+               } else {
+                       /* fallback rates are stored in the firmware's
+                        * retry rate set array.
+                        */
+                       txc->s.rr[i - 1] = phy_set;
+               }
+
+               SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i],
+                       txrate->count);
+
+               if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
+                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS <<
+                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
+               else if (carl9170_tx_cts_check(ar, txrate))
+                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS <<
+                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
+
+               if (ampdu && (txrate->flags & IEEE80211_TX_RC_MCS))
+                       txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU;
+       }
+}
+
 static int carl9170_tx_prepare(struct ar9170 *ar,
                               struct ieee80211_sta *sta,
                               struct sk_buff *skb)
@@ -874,13 +961,10 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
        struct _carl9170_tx_superframe *txc;
        struct carl9170_vif_info *cvif;
        struct ieee80211_tx_info *info;
-       struct ieee80211_tx_rate *txrate;
        struct carl9170_tx_info *arinfo;
        unsigned int hw_queue;
-       int i;
        __le16 mac_tmp;
        u16 len;
-       bool ampdu, no_ack;
 
        BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data));
        BUILD_BUG_ON(sizeof(struct _carl9170_tx_superdesc) !=
@@ -889,8 +973,6 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
        BUILD_BUG_ON(sizeof(struct _ar9170_tx_hwdesc) !=
                     AR9170_TX_HWDESC_LEN);
 
-       BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES);
-
        BUILD_BUG_ON(AR9170_MAX_VIRTUAL_MAC >
                ((CARL9170_TX_SUPER_MISC_VIF_ID >>
                 CARL9170_TX_SUPER_MISC_VIF_ID_S) + 1));
@@ -932,8 +1014,7 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
        mac_tmp |= cpu_to_le16((hw_queue << AR9170_TX_MAC_QOS_S) &
                               AR9170_TX_MAC_QOS);
 
-       no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK);
-       if (unlikely(no_ack))
+       if (unlikely(info->flags & IEEE80211_TX_CTL_NO_ACK))
                mac_tmp |= cpu_to_le16(AR9170_TX_MAC_NO_ACK);
 
        if (info->control.hw_key) {
@@ -954,8 +1035,7 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
                }
        }
 
-       ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU);
-       if (ampdu) {
+       if (info->flags & IEEE80211_TX_CTL_AMPDU) {
                unsigned int density, factor;
 
                if (unlikely(!sta || !cvif))
@@ -982,50 +1062,6 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
                        txc->s.ampdu_settings, factor);
        }
 
-       /*
-        * NOTE: For the first rate, the ERP & AMPDU flags are directly
-        * taken from mac_control. For all fallback rate, the firmware
-        * updates the mac_control flags from the rate info field.
-        */
-       for (i = 0; i < CARL9170_TX_MAX_RATES; i++) {
-               __le32 phy_set;
-               txrate = &info->control.rates[i];
-               if (txrate->idx < 0)
-                       break;
-
-               phy_set = carl9170_tx_physet(ar, info, txrate);
-               if (i == 0) {
-                       /* first rate - part of the hw's frame header */
-                       txc->f.phy_control = phy_set;
-
-                       if (ampdu && txrate->flags & IEEE80211_TX_RC_MCS)
-                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR);
-                       if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
-                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
-                       else if (carl9170_tx_cts_check(ar, txrate))
-                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
-
-               } else {
-                       /* fallback rates are stored in the firmware's
-                        * retry rate set array.
-                        */
-                       txc->s.rr[i - 1] = phy_set;
-               }
-
-               SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i],
-                       txrate->count);
-
-               if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
-                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS <<
-                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
-               else if (carl9170_tx_cts_check(ar, txrate))
-                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS <<
-                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
-
-               if (ampdu && (txrate->flags & IEEE80211_TX_RC_MCS))
-                       txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU;
-       }
-
        txc->s.len = cpu_to_le16(skb->len);
        txc->f.length = cpu_to_le16(len + FCS_LEN);
        txc->f.mac_control = mac_tmp;
@@ -1086,31 +1122,12 @@ static void carl9170_set_ampdu_params(struct ar9170 *ar, struct sk_buff *skb)
        }
 }
 
-static bool carl9170_tx_rate_check(struct ar9170 *ar, struct sk_buff *_dest,
-                                  struct sk_buff *_src)
-{
-       struct _carl9170_tx_superframe *dest, *src;
-
-       dest = (void *) _dest->data;
-       src = (void *) _src->data;
-
-       /*
-        * The mac80211 rate control algorithm expects that all MPDUs in
-        * an AMPDU share the same tx vectors.
-        * This is not really obvious right now, because the hardware
-        * does the AMPDU setup according to its own rulebook.
-        * Our nicely assembled, strictly monotonic increasing mpdu
-        * chains will be broken up, mashed back together...
-        */
-
-       return (dest->f.phy_control == src->f.phy_control);
-}
-
 static void carl9170_tx_ampdu(struct ar9170 *ar)
 {
        struct sk_buff_head agg;
        struct carl9170_sta_tid *tid_info;
        struct sk_buff *skb, *first;
+       struct ieee80211_tx_info *tx_info_first;
        unsigned int i = 0, done_ampdus = 0;
        u16 seq, queue, tmpssn;
 
@@ -1156,6 +1173,7 @@ retry:
                        goto processed;
                }
 
+               tx_info_first = NULL;
                while ((skb = skb_peek(&tid_info->queue))) {
                        /* strict 0, 1, ..., n - 1, n frame sequence order */
                        if (unlikely(carl9170_get_seq(skb) != seq))
@@ -1166,8 +1184,13 @@ retry:
                            (tid_info->max - 1)))
                                break;
 
-                       if (!carl9170_tx_rate_check(ar, skb, first))
-                               break;
+                       if (!tx_info_first) {
+                               carl9170_tx_get_rates(ar, tid_info->vif,
+                                                     tid_info->sta, first);
+                               tx_info_first = IEEE80211_SKB_CB(first);
+                       }
+
+                       carl9170_tx_apply_rateset(ar, tx_info_first, skb);
 
                        atomic_inc(&ar->tx_ampdu_upload);
                        tid_info->snx = seq = SEQ_NEXT(seq);
@@ -1182,8 +1205,7 @@ retry:
                if (skb_queue_empty(&tid_info->queue) ||
                    carl9170_get_seq(skb_peek(&tid_info->queue)) !=
                    tid_info->snx) {
-                       /*
-                        * stop TID, if A-MPDU frames are still missing,
+                       /* stop TID, if A-MPDU frames are still missing,
                         * or whenever the queue is empty.
                         */
 
@@ -1450,12 +1472,14 @@ void carl9170_op_tx(struct ieee80211_hw *hw,
        struct ar9170 *ar = hw->priv;
        struct ieee80211_tx_info *info;
        struct ieee80211_sta *sta = control->sta;
+       struct ieee80211_vif *vif;
        bool run;
 
        if (unlikely(!IS_STARTED(ar)))
                goto err_free;
 
        info = IEEE80211_SKB_CB(skb);
+       vif = info->control.vif;
 
        if (unlikely(carl9170_tx_prepare(ar, sta, skb)))
                goto err_free;
@@ -1486,6 +1510,8 @@ void carl9170_op_tx(struct ieee80211_hw *hw,
        } else {
                unsigned int queue = skb_get_queue_mapping(skb);
 
+               carl9170_tx_get_rates(ar, vif, sta, skb);
+               carl9170_tx_apply_rateset(ar, info, skb);
                skb_queue_tail(&ar->tx_pending[queue], skb);
        }
 
index ccc4c71..7d077c7 100644 (file)
@@ -42,11 +42,11 @@ static int __ath_regd_init(struct ath_regulatory *reg);
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM)
 
 /* We allow IBSS on these on a case by case basis by regulatory domain */
-#define ATH9K_5GHZ_5150_5350   REG_RULE(5150-10, 5350+10, 40, 0, 30,\
+#define ATH9K_5GHZ_5150_5350   REG_RULE(5150-10, 5350+10, 80, 0, 30,\
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
-#define ATH9K_5GHZ_5470_5850   REG_RULE(5470-10, 5850+10, 40, 0, 30,\
+#define ATH9K_5GHZ_5470_5850   REG_RULE(5470-10, 5850+10, 80, 0, 30,\
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
-#define ATH9K_5GHZ_5725_5850   REG_RULE(5725-10, 5850+10, 40, 0, 30,\
+#define ATH9K_5GHZ_5725_5850   REG_RULE(5725-10, 5850+10, 80, 0, 30,\
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
 
 #define ATH9K_2GHZ_ALL         ATH9K_2GHZ_CH01_11, \
index bac3d98..ce8c038 100644 (file)
@@ -27,3 +27,15 @@ config WIL6210_ISR_COR
          self-clear when accessed for debug purposes, it makes
          such monitoring impossible.
          Say y unless you debug interrupts
+
+config WIL6210_TRACING
+       bool "wil6210 tracing support"
+       depends on WIL6210
+       depends on EVENT_TRACING
+       default y
+       ---help---
+         Say Y here to enable tracepoints for the wil6210 driver
+         using the kernel tracing infrastructure.  Select this
+         option if you are interested in debugging the driver.
+
+         If unsure, say Y to make it easier to debug problems.
index d288eea..f891d51 100644 (file)
@@ -1,15 +1,20 @@
 obj-$(CONFIG_WIL6210) += wil6210.o
 
-wil6210-objs := main.o
-wil6210-objs += netdev.o
-wil6210-objs += cfg80211.o
-wil6210-objs += pcie_bus.o
-wil6210-objs += debugfs.o
-wil6210-objs += wmi.o
-wil6210-objs += interrupt.o
-wil6210-objs += txrx.o
+wil6210-y := main.o
+wil6210-y += netdev.o
+wil6210-y += cfg80211.o
+wil6210-y += pcie_bus.o
+wil6210-y += debugfs.o
+wil6210-y += wmi.o
+wil6210-y += interrupt.o
+wil6210-y += txrx.o
+wil6210-y += debug.o
+wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
 
 ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
        subdir-ccflags-y += -Werror
 endif
+# for tracing framework to find trace.h
+CFLAGS_trace.o := -I$(src)
+
 subdir-ccflags-y += -D__CHECK_ENDIAN__
index c5d4a87..61c302a 100644 (file)
@@ -322,12 +322,16 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
         * FW don't support scan after connection attempt
         */
        set_bit(wil_status_dontscan, &wil->status);
+       set_bit(wil_status_fwconnecting, &wil->status);
 
        rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
        if (rc == 0) {
                /* Connect can take lots of time */
                mod_timer(&wil->connect_timer,
                          jiffies + msecs_to_jiffies(2000));
+       } else {
+               clear_bit(wil_status_dontscan, &wil->status);
+               clear_bit(wil_status_fwconnecting, &wil->status);
        }
 
  out:
@@ -398,6 +402,30 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
        return 0;
 }
 
+static int wil_fix_bcon(struct wil6210_priv *wil,
+                       struct cfg80211_beacon_data *bcon)
+{
+       struct ieee80211_mgmt *f = (struct ieee80211_mgmt *)bcon->probe_resp;
+       size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+       int rc = 0;
+
+       if (bcon->probe_resp_len <= hlen)
+               return 0;
+
+       if (!bcon->proberesp_ies) {
+               bcon->proberesp_ies = f->u.probe_resp.variable;
+               bcon->proberesp_ies_len = bcon->probe_resp_len - hlen;
+               rc = 1;
+       }
+       if (!bcon->assocresp_ies) {
+               bcon->assocresp_ies = f->u.probe_resp.variable;
+               bcon->assocresp_ies_len = bcon->probe_resp_len - hlen;
+               rc = 1;
+       }
+
+       return rc;
+}
+
 static int wil_cfg80211_start_ap(struct wiphy *wiphy,
                                 struct net_device *ndev,
                                 struct cfg80211_ap_settings *info)
@@ -419,10 +447,18 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
        print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
                             info->ssid, info->ssid_len);
 
+       if (wil_fix_bcon(wil, bcon))
+               wil_dbg_misc(wil, "Fixed bcon\n");
+
        rc = wil_reset(wil);
        if (rc)
                return rc;
 
+       /* Rx VRING. */
+       rc = wil_rx_init(wil);
+       if (rc)
+               return rc;
+
        rc = wmi_set_ssid(wil, info->ssid_len, info->ssid);
        if (rc)
                return rc;
@@ -451,8 +487,6 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
        if (rc)
                return rc;
 
-       /* Rx VRING. After MAC and beacon */
-       rc = wil_rx_init(wil);
 
        netif_carrier_on(ndev);
 
diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c
new file mode 100644 (file)
index 0000000..9eeabf4
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "wil6210.h"
+#include "trace.h"
+
+int wil_err(struct wil6210_priv *wil, const char *fmt, ...)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       ret = netdev_err(ndev, "%pV", &vaf);
+       trace_wil6210_log_err(&vaf);
+       va_end(args);
+
+       return ret;
+}
+
+int wil_info(struct wil6210_priv *wil, const char *fmt, ...)
+{
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       ret = netdev_info(ndev, "%pV", &vaf);
+       trace_wil6210_log_info(&vaf);
+       va_end(args);
+
+       return ret;
+}
+
+int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       trace_wil6210_log_dbg(&vaf);
+       va_end(args);
+
+       return 0;
+}
index 727b1f5..e8308ec 100644 (file)
@@ -418,9 +418,15 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
                if (skb) {
                        unsigned char printbuf[16 * 3 + 2];
                        int i = 0;
-                       int len = skb_headlen(skb);
+                       int len = le16_to_cpu(d->dma.length);
                        void *p = skb->data;
 
+                       if (len != skb_headlen(skb)) {
+                               seq_printf(s, "!!! len: desc = %d skb = %d\n",
+                                          len, skb_headlen(skb));
+                               len = min_t(int, len, skb_headlen(skb));
+                       }
+
                        seq_printf(s, "    len = %d\n", len);
 
                        while (i < len) {
index e3c1e76..8205d3e 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/interrupt.h>
 
 #include "wil6210.h"
+#include "trace.h"
 
 /**
  * Theory of operation:
@@ -103,14 +104,14 @@ static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
        clear_bit(wil_status_irqen, &wil->status);
 }
 
-static void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
+void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
 {
        iowrite32(WIL6210_IMC_TX, wil->csr +
                  HOSTADDR(RGF_DMA_EP_TX_ICR) +
                  offsetof(struct RGF_ICR, IMC));
 }
 
-static void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
+void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
 {
        iowrite32(WIL6210_IMC_RX, wil->csr +
                  HOSTADDR(RGF_DMA_EP_RX_ICR) +
@@ -168,6 +169,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
                                         HOSTADDR(RGF_DMA_EP_RX_ICR) +
                                         offsetof(struct RGF_ICR, ICR));
 
+       trace_wil6210_irq_rx(isr);
        wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
 
        if (!isr) {
@@ -180,13 +182,14 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
        if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
                wil_dbg_irq(wil, "RX done\n");
                isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
-               wil_rx_handle(wil);
+               wil_dbg_txrx(wil, "NAPI schedule\n");
+               napi_schedule(&wil->napi_rx);
        }
 
        if (isr)
                wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);
 
-       wil6210_unmask_irq_rx(wil);
+       /* Rx IRQ will be enabled when NAPI processing finished */
 
        return IRQ_HANDLED;
 }
@@ -198,6 +201,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
                                         HOSTADDR(RGF_DMA_EP_TX_ICR) +
                                         offsetof(struct RGF_ICR, ICR));
 
+       trace_wil6210_irq_tx(isr);
        wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
 
        if (!isr) {
@@ -208,23 +212,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
        wil6210_mask_irq_tx(wil);
 
        if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
-               uint i;
                wil_dbg_irq(wil, "TX done\n");
+               napi_schedule(&wil->napi_tx);
                isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
-               for (i = 0; i < 24; i++) {
-                       u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i);
-                       if (isr & mask) {
-                               isr &= ~mask;
-                               wil_dbg_irq(wil, "TX done(%i)\n", i);
-                               wil_tx_complete(wil, i);
-                       }
-               }
+               /* clear also all VRING interrupts */
+               isr &= ~(BIT(25) - 1UL);
        }
 
        if (isr)
                wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
 
-       wil6210_unmask_irq_tx(wil);
+       /* Tx IRQ will be enabled when NAPI processing finished */
 
        return IRQ_HANDLED;
 }
@@ -256,6 +254,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
                                         HOSTADDR(RGF_DMA_EP_MISC_ICR) +
                                         offsetof(struct RGF_ICR, ICR));
 
+       trace_wil6210_irq_misc(isr);
        wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr);
 
        if (!isr) {
@@ -301,6 +300,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
        struct wil6210_priv *wil = cookie;
        u32 isr = wil->isr_misc;
 
+       trace_wil6210_irq_misc_thread(isr);
        wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
 
        if (isr & ISR_MISC_FW_ERROR) {
@@ -408,6 +408,7 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
        if (wil6210_debug_irq_mask(wil, pseudo_cause))
                return IRQ_NONE;
 
+       trace_wil6210_irq_pseudo(pseudo_cause);
        wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause);
 
        wil6210_mask_irq_pseudo(wil);
index a0478e2..0a2844c 100644 (file)
@@ -56,27 +56,21 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
 {
        uint i;
        struct net_device *ndev = wil_to_ndev(wil);
-       struct wireless_dev *wdev = wil->wdev;
 
        wil_dbg_misc(wil, "%s()\n", __func__);
 
        wil_link_off(wil);
-       clear_bit(wil_status_fwconnected, &wil->status);
-
-       switch (wdev->sme_state) {
-       case CFG80211_SME_CONNECTED:
-               cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE,
+       if (test_bit(wil_status_fwconnected, &wil->status)) {
+               clear_bit(wil_status_fwconnected, &wil->status);
+               cfg80211_disconnected(ndev,
+                                     WLAN_STATUS_UNSPECIFIED_FAILURE,
                                      NULL, 0, GFP_KERNEL);
-               break;
-       case CFG80211_SME_CONNECTING:
+       } else if (test_bit(wil_status_fwconnecting, &wil->status)) {
                cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
                                        WLAN_STATUS_UNSPECIFIED_FAILURE,
                                        GFP_KERNEL);
-               break;
-       default:
-               break;
        }
-
+       clear_bit(wil_status_fwconnecting, &wil->status);
        for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
                wil_vring_fini_tx(wil, i);
 
@@ -292,41 +286,36 @@ static int __wil_up(struct wil6210_priv *wil)
 {
        struct net_device *ndev = wil_to_ndev(wil);
        struct wireless_dev *wdev = wil->wdev;
-       struct ieee80211_channel *channel = wdev->preset_chandef.chan;
        int rc;
-       int bi;
-       u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
 
        rc = wil_reset(wil);
        if (rc)
                return rc;
 
-       /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */
-       wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC);
+       /* Rx VRING. After MAC and beacon */
+       rc = wil_rx_init(wil);
+       if (rc)
+               return rc;
+
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
                wil_dbg_misc(wil, "type: STATION\n");
-               bi = 0;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_AP:
                wil_dbg_misc(wil, "type: AP\n");
-               bi = 100;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
                wil_dbg_misc(wil, "type: P2P_CLIENT\n");
-               bi = 0;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_P2P_GO:
                wil_dbg_misc(wil, "type: P2P_GO\n");
-               bi = 100;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_MONITOR:
                wil_dbg_misc(wil, "type: Monitor\n");
-               bi = 0;
                ndev->type = ARPHRD_IEEE80211_RADIOTAP;
                /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
                break;
@@ -334,36 +323,12 @@ static int __wil_up(struct wil6210_priv *wil)
                return -EOPNOTSUPP;
        }
 
-       /* Apply profile in the following order: */
-       /* SSID and channel for the AP */
-       switch (wdev->iftype) {
-       case NL80211_IFTYPE_AP:
-       case NL80211_IFTYPE_P2P_GO:
-               if (wdev->ssid_len == 0) {
-                       wil_err(wil, "SSID not set\n");
-                       return -EINVAL;
-               }
-               rc = wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid);
-               if (rc)
-                       return rc;
-               break;
-       default:
-               break;
-       }
-
        /* MAC address - pre-requisite for other commands */
        wmi_set_mac_address(wil, ndev->dev_addr);
 
-       /* Set up beaconing if required. */
-       if (bi > 0) {
-               rc = wmi_pcp_start(wil, bi, wmi_nettype,
-                                  (channel ? channel->hw_value : 0));
-               if (rc)
-                       return rc;
-       }
 
-       /* Rx VRING. After MAC and beacon */
-       wil_rx_init(wil);
+       napi_enable(&wil->napi_rx);
+       napi_enable(&wil->napi_tx);
 
        return 0;
 }
@@ -381,6 +346,9 @@ int wil_up(struct wil6210_priv *wil)
 
 static int __wil_down(struct wil6210_priv *wil)
 {
+       napi_disable(&wil->napi_rx);
+       napi_disable(&wil->napi_tx);
+
        if (wil->scan_request) {
                cfg80211_scan_done(wil->scan_request, true);
                wil->scan_request = NULL;
index 098a8ec..29dd1e5 100644 (file)
@@ -40,6 +40,55 @@ static const struct net_device_ops wil_netdev_ops = {
        .ndo_validate_addr      = eth_validate_addr,
 };
 
+static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
+{
+       struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
+                                               napi_rx);
+       int quota = budget;
+       int done;
+
+       wil_rx_handle(wil, &quota);
+       done = budget - quota;
+
+       if (done <= 1) { /* burst ends - only one packet processed */
+               napi_complete(napi);
+               wil6210_unmask_irq_rx(wil);
+               wil_dbg_txrx(wil, "NAPI RX complete\n");
+       }
+
+       wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done);
+
+       return done;
+}
+
+static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
+{
+       struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
+                                               napi_tx);
+       int tx_done = 0;
+       uint i;
+
+       /* always process ALL Tx complete, regardless budget - it is fast */
+       for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
+               struct vring *vring = &wil->vring_tx[i];
+
+               if (!vring->va)
+                       continue;
+
+               tx_done += wil_tx_complete(wil, i);
+       }
+
+       if (tx_done <= 1) { /* burst ends - only one packet processed */
+               napi_complete(napi);
+               wil6210_unmask_irq_tx(wil);
+               wil_dbg_txrx(wil, "NAPI TX complete\n");
+       }
+
+       wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done);
+
+       return min(tx_done, budget);
+}
+
 void *wil_if_alloc(struct device *dev, void __iomem *csr)
 {
        struct net_device *ndev;
@@ -81,6 +130,11 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
        SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
        wdev->netdev = ndev;
 
+       netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
+                      WIL6210_NAPI_BUDGET);
+       netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
+                      WIL6210_NAPI_BUDGET);
+
        wil_link_off(wil);
 
        return wil;
diff --git a/drivers/net/wireless/ath/wil6210/trace.c b/drivers/net/wireless/ath/wil6210/trace.c
new file mode 100644 (file)
index 0000000..cd2534b
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/net/wireless/ath/wil6210/trace.h b/drivers/net/wireless/ath/wil6210/trace.h
new file mode 100644 (file)
index 0000000..eff1239
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM wil6210
+#if !defined(WIL6210_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define WIL6210_TRACE_H
+
+#include <linux/tracepoint.h>
+#include "wil6210.h"
+#include "txrx.h"
+
+/* create empty functions when tracing is disabled */
+#if !defined(CONFIG_WIL6210_TRACING) || defined(__CHECKER__)
+
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */
+
+DECLARE_EVENT_CLASS(wil6210_wmi,
+       TP_PROTO(u16 id, void *buf, u16 buf_len),
+
+       TP_ARGS(id, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(u16, id)
+               __field(u16, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->id = id;
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "id 0x%04x len %d",
+               __entry->id, __entry->buf_len
+       )
+);
+
+DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd,
+       TP_PROTO(u16 id, void *buf, u16 buf_len),
+       TP_ARGS(id, buf, buf_len)
+);
+
+DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event,
+       TP_PROTO(u16 id, void *buf, u16 buf_len),
+       TP_ARGS(id, buf, buf_len)
+);
+
+#define WIL6210_MSG_MAX (200)
+
+DECLARE_EVENT_CLASS(wil6210_log_event,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf),
+       TP_STRUCT__entry(
+               __dynamic_array(char, msg, WIL6210_MSG_MAX)
+       ),
+       TP_fast_assign(
+               WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+                                      WIL6210_MSG_MAX,
+                                      vaf->fmt,
+                                      *vaf->va) >= WIL6210_MSG_MAX);
+       ),
+       TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_err,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_info,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_dbg,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf)
+);
+
+#define wil_pseudo_irq_cause(x) __print_flags(x, "|",  \
+       {BIT_DMA_PSEUDO_CAUSE_RX,       "Rx" },         \
+       {BIT_DMA_PSEUDO_CAUSE_TX,       "Tx" },         \
+       {BIT_DMA_PSEUDO_CAUSE_MISC,     "Misc" })
+
+TRACE_EVENT(wil6210_irq_pseudo,
+       TP_PROTO(u32 x),
+       TP_ARGS(x),
+       TP_STRUCT__entry(
+               __field(u32, x)
+       ),
+       TP_fast_assign(
+               __entry->x = x;
+       ),
+       TP_printk("cause 0x%08x : %s", __entry->x,
+                 wil_pseudo_irq_cause(__entry->x))
+);
+
+DECLARE_EVENT_CLASS(wil6210_irq,
+       TP_PROTO(u32 x),
+       TP_ARGS(x),
+       TP_STRUCT__entry(
+               __field(u32, x)
+       ),
+       TP_fast_assign(
+               __entry->x = x;
+       ),
+       TP_printk("cause 0x%08x", __entry->x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_rx,
+       TP_PROTO(u32 x),
+       TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_tx,
+       TP_PROTO(u32 x),
+       TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_misc,
+       TP_PROTO(u32 x),
+       TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_misc_thread,
+       TP_PROTO(u32 x),
+       TP_ARGS(x)
+);
+
+TRACE_EVENT(wil6210_rx,
+       TP_PROTO(u16 index, struct vring_rx_desc *d),
+       TP_ARGS(index, d),
+       TP_STRUCT__entry(
+               __field(u16, index)
+               __field(unsigned int, len)
+               __field(u8, mid)
+               __field(u8, cid)
+               __field(u8, tid)
+               __field(u8, type)
+               __field(u8, subtype)
+               __field(u16, seq)
+               __field(u8, mcs)
+       ),
+       TP_fast_assign(
+               __entry->index = index;
+               __entry->len = d->dma.length;
+               __entry->mid = wil_rxdesc_mid(d);
+               __entry->cid = wil_rxdesc_cid(d);
+               __entry->tid = wil_rxdesc_tid(d);
+               __entry->type = wil_rxdesc_ftype(d);
+               __entry->subtype = wil_rxdesc_subtype(d);
+               __entry->seq = wil_rxdesc_seq(d);
+               __entry->mcs = wil_rxdesc_mcs(d);
+       ),
+       TP_printk("index %d len %d mid %d cid %d tid %d mcs %d seq 0x%03x"
+                 " type 0x%1x subtype 0x%1x", __entry->index, __entry->len,
+                 __entry->mid, __entry->cid, __entry->tid, __entry->mcs,
+                 __entry->seq, __entry->type, __entry->subtype)
+);
+
+TRACE_EVENT(wil6210_tx,
+       TP_PROTO(u8 vring, u16 index, unsigned int len, u8 frags),
+       TP_ARGS(vring, index, len, frags),
+       TP_STRUCT__entry(
+               __field(u8, vring)
+               __field(u8, frags)
+               __field(u16, index)
+               __field(unsigned int, len)
+       ),
+       TP_fast_assign(
+               __entry->vring = vring;
+               __entry->frags = frags;
+               __entry->index = index;
+               __entry->len = len;
+       ),
+       TP_printk("vring %d index %d len %d frags %d",
+                 __entry->vring, __entry->index, __entry->len, __entry->frags)
+);
+
+TRACE_EVENT(wil6210_tx_done,
+       TP_PROTO(u8 vring, u16 index, unsigned int len, u8 err),
+       TP_ARGS(vring, index, len, err),
+       TP_STRUCT__entry(
+               __field(u8, vring)
+               __field(u8, err)
+               __field(u16, index)
+               __field(unsigned int, len)
+       ),
+       TP_fast_assign(
+               __entry->vring = vring;
+               __entry->index = index;
+               __entry->len = len;
+               __entry->err = err;
+       ),
+       TP_printk("vring %d index %d len %d err 0x%02x",
+                 __entry->vring, __entry->index, __entry->len,
+                 __entry->err)
+);
+
+#endif /* WIL6210_TRACE_H || TRACE_HEADER_MULTI_READ*/
+
+#if defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__)
+/* we don't want to use include/trace/events */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
+#endif /* defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__) */
index 7970245..d240b24 100644 (file)
@@ -22,6 +22,7 @@
 #include "wil6210.h"
 #include "wmi.h"
 #include "txrx.h"
+#include "trace.h"
 
 static bool rtap_include_phy_info;
 module_param(rtap_include_phy_info, bool, S_IRUGO);
@@ -89,8 +90,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
         * we can use any
         */
        for (i = 0; i < vring->size; i++) {
-               volatile struct vring_tx_desc *d = &(vring->va[i].tx);
-               d->dma.status = TX_DMA_STATUS_DU;
+               volatile struct vring_tx_desc *_d = &(vring->va[i].tx);
+               _d->dma.status = TX_DMA_STATUS_DU;
        }
 
        wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
@@ -106,30 +107,39 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
        size_t sz = vring->size * sizeof(vring->va[0]);
 
        while (!wil_vring_is_empty(vring)) {
+               dma_addr_t pa;
+               struct sk_buff *skb;
+               u16 dmalen;
+
                if (tx) {
-                       volatile struct vring_tx_desc *d =
+                       struct vring_tx_desc dd, *d = &dd;
+                       volatile struct vring_tx_desc *_d =
                                        &vring->va[vring->swtail].tx;
-                       dma_addr_t pa = d->dma.addr_low |
-                                       ((u64)d->dma.addr_high << 32);
-                       struct sk_buff *skb = vring->ctx[vring->swtail];
+
+                       *d = *_d;
+                       pa = wil_desc_addr(&d->dma.addr);
+                       dmalen = le16_to_cpu(d->dma.length);
+                       skb = vring->ctx[vring->swtail];
                        if (skb) {
-                               dma_unmap_single(dev, pa, d->dma.length,
+                               dma_unmap_single(dev, pa, dmalen,
                                                 DMA_TO_DEVICE);
                                dev_kfree_skb_any(skb);
                                vring->ctx[vring->swtail] = NULL;
                        } else {
-                               dma_unmap_page(dev, pa, d->dma.length,
+                               dma_unmap_page(dev, pa, dmalen,
                                               DMA_TO_DEVICE);
                        }
                        vring->swtail = wil_vring_next_tail(vring);
                } else { /* rx */
-                       volatile struct vring_rx_desc *d =
+                       struct vring_rx_desc dd, *d = &dd;
+                       volatile struct vring_rx_desc *_d =
                                        &vring->va[vring->swtail].rx;
-                       dma_addr_t pa = d->dma.addr_low |
-                                       ((u64)d->dma.addr_high << 32);
-                       struct sk_buff *skb = vring->ctx[vring->swhead];
-                       dma_unmap_single(dev, pa, d->dma.length,
-                                        DMA_FROM_DEVICE);
+
+                       *d = *_d;
+                       pa = wil_desc_addr(&d->dma.addr);
+                       dmalen = le16_to_cpu(d->dma.length);
+                       skb = vring->ctx[vring->swhead];
+                       dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
                        kfree_skb(skb);
                        wil_vring_advance_head(vring, 1);
                }
@@ -151,7 +161,8 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
 {
        struct device *dev = wil_to_dev(wil);
        unsigned int sz = RX_BUF_LEN;
-       volatile struct vring_rx_desc *d = &(vring->va[i].rx);
+       struct vring_rx_desc dd, *d = &dd;
+       volatile struct vring_rx_desc *_d = &(vring->va[i].rx);
        dma_addr_t pa;
 
        /* TODO align */
@@ -169,13 +180,13 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
        }
 
        d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT;
-       d->dma.addr_low = lower_32_bits(pa);
-       d->dma.addr_high = (u16)upper_32_bits(pa);
+       wil_desc_addr_set(&d->dma.addr, pa);
        /* ip_length don't care */
        /* b11 don't care */
        /* error don't care */
        d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
-       d->dma.length = sz;
+       d->dma.length = cpu_to_le16(sz);
+       *_d = *d;
        vring->ctx[i] = skb;
 
        return 0;
@@ -321,11 +332,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
 {
        struct device *dev = wil_to_dev(wil);
        struct net_device *ndev = wil_to_ndev(wil);
-       volatile struct vring_rx_desc *d;
-       struct vring_rx_desc *d1;
+       volatile struct vring_rx_desc *_d;
+       struct vring_rx_desc *d;
        struct sk_buff *skb;
        dma_addr_t pa;
        unsigned int sz = RX_BUF_LEN;
+       u16 dmalen;
        u8 ftype;
        u8 ds_bits;
 
@@ -334,32 +346,44 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
        if (wil_vring_is_empty(vring))
                return NULL;
 
-       d = &(vring->va[vring->swhead].rx);
-       if (!(d->dma.status & RX_DMA_STATUS_DU)) {
+       _d = &(vring->va[vring->swhead].rx);
+       if (!(_d->dma.status & RX_DMA_STATUS_DU)) {
                /* it is not error, we just reached end of Rx done area */
                return NULL;
        }
 
-       pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
        skb = vring->ctx[vring->swhead];
+       d = wil_skb_rxdesc(skb);
+       *d = *_d;
+       pa = wil_desc_addr(&d->dma.addr);
+       vring->ctx[vring->swhead] = NULL;
+       wil_vring_advance_head(vring, 1);
+
        dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
-       skb_trim(skb, d->dma.length);
+       dmalen = le16_to_cpu(d->dma.length);
+
+       trace_wil6210_rx(vring->swhead, d);
+       wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, dmalen);
+       wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
+                         (const void *)d, sizeof(*d), false);
+
+       if (dmalen > sz) {
+               wil_err(wil, "Rx size too large: %d bytes!\n", dmalen);
+               kfree_skb(skb);
+               return NULL;
+       }
+       skb_trim(skb, dmalen);
+
+       wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
+                         skb->data, skb_headlen(skb), false);
 
-       d1 = wil_skb_rxdesc(skb);
-       *d1 = *d;
 
-       wil->stats.last_mcs_rx = wil_rxdesc_mcs(d1);
+       wil->stats.last_mcs_rx = wil_rxdesc_mcs(d);
 
        /* use radiotap header only if required */
        if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
                wil_rx_add_radiotap_header(wil, skb);
 
-       wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
-       wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
-                         (const void *)d, sizeof(*d), false);
-
-       wil_vring_advance_head(vring, 1);
-
        /* no extra checks if in sniffer mode */
        if (ndev->type != ARPHRD_ETHER)
                return skb;
@@ -368,7 +392,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
         * Driver should recognize it by frame type, that is found
         * in Rx descriptor. If type is not data, it is 802.11 frame as is
         */
-       ftype = wil_rxdesc_ftype(d1) << 2;
+       ftype = wil_rxdesc_ftype(d) << 2;
        if (ftype != IEEE80211_FTYPE_DATA) {
                wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype);
                /* TODO: process it */
@@ -383,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
                return NULL;
        }
 
-       ds_bits = wil_rxdesc_ds_bits(d1);
+       ds_bits = wil_rxdesc_ds_bits(d);
        if (ds_bits == 1) {
                /*
                 * HW bug - in ToDS mode, i.e. Rx on AP side,
@@ -425,6 +449,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)
 
 /*
  * Pass Rx packet to the netif. Update statistics.
+ * Called in softirq context (NAPI poll).
  */
 static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
 {
@@ -433,10 +458,7 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
 
        skb_orphan(skb);
 
-       if (in_interrupt())
-               rc = netif_rx(skb);
-       else
-               rc = netif_rx_ni(skb);
+       rc = netif_receive_skb(skb);
 
        if (likely(rc == NET_RX_SUCCESS)) {
                ndev->stats.rx_packets++;
@@ -450,9 +472,9 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
 /**
  * Proceed all completed skb's from Rx VRING
  *
- * Safe to call from IRQ
+ * Safe to call from NAPI poll, i.e. softirq with interrupts enabled
  */
-void wil_rx_handle(struct wil6210_priv *wil)
+void wil_rx_handle(struct wil6210_priv *wil, int *quota)
 {
        struct net_device *ndev = wil_to_ndev(wil);
        struct vring *v = &wil->vring_rx;
@@ -463,9 +485,8 @@ void wil_rx_handle(struct wil6210_priv *wil)
                return;
        }
        wil_dbg_txrx(wil, "%s()\n", __func__);
-       while (NULL != (skb = wil_vring_reap_rx(wil, v))) {
-               wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
-                                 skb->data, skb_headlen(skb), false);
+       while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) {
+               (*quota)--;
 
                if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
                        skb->dev = ndev;
@@ -600,18 +621,17 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
        return NULL;
 }
 
-static int wil_tx_desc_map(volatile struct vring_tx_desc *d,
-                          dma_addr_t pa, u32 len)
+static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
+                          int vring_index)
 {
-       d->dma.addr_low = lower_32_bits(pa);
-       d->dma.addr_high = (u16)upper_32_bits(pa);
+       wil_desc_addr_set(&d->dma.addr, pa);
        d->dma.ip_length = 0;
        /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/
        d->dma.b11 = 0/*14 | BIT(7)*/;
        d->dma.error = 0;
        d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
-       d->dma.length = len;
-       d->dma.d0 = 0;
+       d->dma.length = cpu_to_le16((u16)len);
+       d->dma.d0 = (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
        d->mac.d[0] = 0;
        d->mac.d[1] = 0;
        d->mac.d[2] = 0;
@@ -630,7 +650,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                        struct sk_buff *skb)
 {
        struct device *dev = wil_to_dev(wil);
-       volatile struct vring_tx_desc *d;
+       struct vring_tx_desc dd, *d = &dd;
+       volatile struct vring_tx_desc *_d;
        u32 swhead = vring->swhead;
        int avail = wil_vring_avail_tx(vring);
        int nr_frags = skb_shinfo(skb)->nr_frags;
@@ -648,7 +669,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                        1 + nr_frags);
                return -ENOMEM;
        }
-       d = &(vring->va[i].tx);
+       _d = &(vring->va[i].tx);
 
        /* FIXME FW can accept only unicast frames for the peer */
        memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN);
@@ -664,28 +685,32 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        if (unlikely(dma_mapping_error(dev, pa)))
                return -EINVAL;
        /* 1-st segment */
-       wil_tx_desc_map(d, pa, skb_headlen(skb));
+       wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);
        d->mac.d[2] |= ((nr_frags + 1) <<
                       MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
+       if (nr_frags)
+               *_d = *d;
+
        /* middle segments */
        for (f = 0; f < nr_frags; f++) {
                const struct skb_frag_struct *frag =
                                &skb_shinfo(skb)->frags[f];
                int len = skb_frag_size(frag);
                i = (swhead + f + 1) % vring->size;
-               d = &(vring->va[i].tx);
+               _d = &(vring->va[i].tx);
                pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
                                DMA_TO_DEVICE);
                if (unlikely(dma_mapping_error(dev, pa)))
                        goto dma_error;
-               wil_tx_desc_map(d, pa, len);
+               wil_tx_desc_map(d, pa, len, vring_index);
                vring->ctx[i] = NULL;
+               *_d = *d;
        }
        /* for the last seg only */
        d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS);
-       d->dma.d0 |= BIT(9); /* BUG: undocumented bit */
+       d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS);
        d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
-       d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
+       *_d = *d;
 
        wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
                          (const void *)d, sizeof(*d), false);
@@ -693,6 +718,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        /* advance swhead */
        wil_vring_advance_head(vring, nr_frags + 1);
        wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
+       trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
        iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
        /* hold reference to skb
         * to prevent skb release before accounting
@@ -705,14 +731,18 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        /* unmap what we have mapped */
        /* Note: increment @f to operate with positive index */
        for (f++; f > 0; f--) {
+               u16 dmalen;
+
                i = (swhead + f) % vring->size;
-               d = &(vring->va[i].tx);
-               d->dma.status = TX_DMA_STATUS_DU;
-               pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
+               _d = &(vring->va[i].tx);
+               *d = *_d;
+               _d->dma.status = TX_DMA_STATUS_DU;
+               pa = wil_desc_addr(&d->dma.addr);
+               dmalen = le16_to_cpu(d->dma.length);
                if (vring->ctx[i])
-                       dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
+                       dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
                else
-                       dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
+                       dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
        }
 
        return -EINVAL;
@@ -738,18 +768,16 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                wil_err(wil, "Xmit in monitor mode not supported\n");
                goto drop;
        }
-       if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
-               rc = wmi_tx_eapol(wil, skb);
-       } else {
-               /* find vring */
-               vring = wil_find_tx_vring(wil, skb);
-               if (!vring) {
-                       wil_err(wil, "No Tx VRING available\n");
-                       goto drop;
-               }
-               /* set up vring entry */
-               rc = wil_tx_vring(wil, vring, skb);
+
+       /* find vring */
+       vring = wil_find_tx_vring(wil, skb);
+       if (!vring) {
+               wil_err(wil, "No Tx VRING available\n");
+               goto drop;
        }
+       /* set up vring entry */
+       rc = wil_tx_vring(wil, vring, skb);
+
        switch (rc) {
        case 0:
                /* statistics will be updated on the tx_complete */
@@ -761,7 +789,6 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                break; /* goto drop; */
        }
  drop:
-       netif_tx_stop_all_queues(ndev);
        ndev->stats.tx_dropped++;
        dev_kfree_skb_any(skb);
 
@@ -771,41 +798,48 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 /**
  * Clean up transmitted skb's from the Tx VRING
  *
+ * Return number of descriptors cleared
+ *
  * Safe to call from IRQ
  */
-void wil_tx_complete(struct wil6210_priv *wil, int ringid)
+int wil_tx_complete(struct wil6210_priv *wil, int ringid)
 {
        struct net_device *ndev = wil_to_ndev(wil);
        struct device *dev = wil_to_dev(wil);
        struct vring *vring = &wil->vring_tx[ringid];
+       int done = 0;
 
        if (!vring->va) {
                wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
-               return;
+               return 0;
        }
 
        wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
 
        while (!wil_vring_is_empty(vring)) {
-               volatile struct vring_tx_desc *d1 =
+               volatile struct vring_tx_desc *_d =
                                              &vring->va[vring->swtail].tx;
                struct vring_tx_desc dd, *d = &dd;
                dma_addr_t pa;
                struct sk_buff *skb;
+               u16 dmalen;
 
-               dd = *d1;
+               *d = *_d;
 
                if (!(d->dma.status & TX_DMA_STATUS_DU))
                        break;
 
+               dmalen = le16_to_cpu(d->dma.length);
+               trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
+                                     d->dma.error);
                wil_dbg_txrx(wil,
                             "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
-                            vring->swtail, d->dma.length, d->dma.status,
+                            vring->swtail, dmalen, d->dma.status,
                             d->dma.error);
                wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
                                  (const void *)d, sizeof(*d), false);
 
-               pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
+               pa = wil_desc_addr(&d->dma.addr);
                skb = vring->ctx[vring->swtail];
                if (skb) {
                        if (d->dma.error == 0) {
@@ -815,18 +849,21 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid)
                                ndev->stats.tx_errors++;
                        }
 
-                       dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
+                       dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
                        dev_kfree_skb_any(skb);
                        vring->ctx[vring->swtail] = NULL;
                } else {
-                       dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
+                       dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
                }
-               d->dma.addr_low = 0;
-               d->dma.addr_high = 0;
+               d->dma.addr.addr_low = 0;
+               d->dma.addr.addr_high = 0;
                d->dma.length = 0;
                d->dma.status = TX_DMA_STATUS_DU;
                vring->swtail = wil_vring_next_tail(vring);
+               done++;
        }
        if (wil_vring_avail_tx(vring) > vring->size/4)
                netif_tx_wake_all_queues(wil_to_ndev(wil));
+
+       return done;
 }
index adef12f..859aea6 100644 (file)
 #define WIL6210_RTAP_SIZE (128)
 
 /* Tx/Rx path */
+
+/*
+ * Common representation of physical address in Vring
+ */
+struct vring_dma_addr {
+       __le32 addr_low;
+       __le16 addr_high;
+} __packed;
+
+static inline dma_addr_t wil_desc_addr(struct vring_dma_addr *addr)
+{
+       return le32_to_cpu(addr->addr_low) |
+                          ((u64)le16_to_cpu(addr->addr_high) << 32);
+}
+
+static inline void wil_desc_addr_set(struct vring_dma_addr *addr,
+                                    dma_addr_t pa)
+{
+       addr->addr_low = cpu_to_le32(lower_32_bits(pa));
+       addr->addr_high = cpu_to_le16((u16)upper_32_bits(pa));
+}
+
 /*
  * Tx descriptor - MAC part
  * [dword 0]
@@ -179,6 +201,10 @@ struct vring_tx_mac {
 #define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1
 #define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100
 
+#define DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS 9
+#define DMA_CFG_DESC_TX_0_CMD_MARK_WB_LEN 1
+#define DMA_CFG_DESC_TX_0_CMD_MARK_WB_MSK 0x200
+
 #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10
 #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1
 #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400
@@ -216,13 +242,12 @@ struct vring_tx_mac {
 
 struct vring_tx_dma {
        u32 d0;
-       u32 addr_low;
-       u16 addr_high;
+       struct vring_dma_addr addr;
        u8  ip_length;
        u8  b11;       /* 0..6: mac_length; 7:ip_version */
        u8  error;     /* 0..2: err; 3..7: reserved; */
        u8  status;    /* 0: used; 1..7; reserved */
-       u16 length;
+       __le16 length;
 } __packed;
 
 /*
@@ -315,13 +340,12 @@ struct vring_rx_mac {
 
 struct vring_rx_dma {
        u32 d0;
-       u32 addr_low;
-       u16 addr_high;
+       struct vring_dma_addr addr;
        u8  ip_length;
        u8  b11;
        u8  error;
        u8  status;
-       u16 length;
+       __le16 length;
 } __packed;
 
 struct vring_tx_desc {
index 8f76ecd..44fdab5 100644 (file)
@@ -34,9 +34,11 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
 
 #define WIL6210_MEM_SIZE (2*1024*1024UL)
 
-#define WIL6210_RX_RING_SIZE (128)
-#define WIL6210_TX_RING_SIZE (128)
-#define WIL6210_MAX_TX_RINGS (24)
+#define WIL6210_RX_RING_SIZE   (128)
+#define WIL6210_TX_RING_SIZE   (128)
+#define WIL6210_MAX_TX_RINGS   (24) /* HW limit */
+#define WIL6210_MAX_CID                (8) /* HW limit */
+#define WIL6210_NAPI_BUDGET    (16) /* arbitrary */
 
 /* Hardware definitions begin */
 
@@ -184,6 +186,7 @@ struct vring {
 
 enum { /* for wil6210_priv.status */
        wil_status_fwready = 0,
+       wil_status_fwconnecting,
        wil_status_fwconnected,
        wil_status_dontscan,
        wil_status_reset_done,
@@ -239,6 +242,8 @@ struct wil6210_priv {
         * - consumed in thread by wmi_event_worker
         */
        spinlock_t wmi_ev_lock;
+       struct napi_struct napi_rx;
+       struct napi_struct napi_tx;
        /* DMA related */
        struct vring vring_rx;
        struct vring vring_tx[WIL6210_MAX_TX_RINGS];
@@ -267,9 +272,13 @@ struct wil6210_priv {
 #define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
 #define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
 
-#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg)
-#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg)
-#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg)
+int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
+int wil_err(struct wil6210_priv *wil, const char *fmt, ...);
+int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
+#define wil_dbg(wil, fmt, arg...) do { \
+       netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
+       wil_dbg_trace(wil, fmt, ##arg); \
+} while (0)
 
 #define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
 #define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
@@ -320,7 +329,6 @@ int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid);
 int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid);
 int wmi_set_channel(struct wil6210_priv *wil, int channel);
 int wmi_get_channel(struct wil6210_priv *wil, int *channel);
-int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb);
 int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
                       const void *mac_addr);
 int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
@@ -356,10 +364,12 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
 void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
 
 netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
-void wil_tx_complete(struct wil6210_priv *wil, int ringid);
+int wil_tx_complete(struct wil6210_priv *wil, int ringid);
+void wil6210_unmask_irq_tx(struct wil6210_priv *wil);
 
 /* RX API */
-void wil_rx_handle(struct wil6210_priv *wil);
+void wil_rx_handle(struct wil6210_priv *wil, int *quota);
+void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
 
 int wil_iftype_nl2wmi(enum nl80211_iftype type);
 
index 45b04e3..dc8059a 100644 (file)
@@ -20,6 +20,7 @@
 #include "wil6210.h"
 #include "txrx.h"
 #include "wmi.h"
+#include "trace.h"
 
 /**
  * WMI event receiving - theory of operations
@@ -74,10 +75,11 @@ static const struct {
        {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */
        {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */
        {0x880000, 0x88a000, 0x880000}, /* various RGF */
-       {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */
+       {0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */
        /*
         * 920000..930000 ucode code RAM
         * 930000..932000 ucode data RAM
+        * 932000..949000 back-door debug data
         */
 };
 
@@ -246,6 +248,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
        iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
                  offsetof(struct wil6210_mbox_ctl, tx.head));
 
+       trace_wil6210_wmi_cmd(cmdid, buf, len);
+
        /* interrupt to FW */
        iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
 
@@ -311,8 +315,8 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
 
        wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d\n",
                    data->info.channel, data->info.mcs, data->info.snr);
-       wil_dbg_wmi(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len,
-                   le16_to_cpu(data->info.stype));
+       wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len,
+                   le16_to_cpu(fc));
        wil_dbg_wmi(wil, "qid %d mid %d cid %d\n",
                    data->info.qid, data->info.mid, data->info.cid);
 
@@ -406,7 +410,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
 
        if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
            (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
-               if (wdev->sme_state != CFG80211_SME_CONNECTING) {
+               if (!test_bit(wil_status_fwconnecting, &wil->status)) {
                        wil_err(wil, "Not in connecting state\n");
                        return;
                }
@@ -430,6 +434,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
 
                cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
        }
+       clear_bit(wil_status_fwconnecting, &wil->status);
        set_bit(wil_status_fwconnected, &wil->status);
 
        /* FIXME FW can transmit only ucast frames to peer */
@@ -635,8 +640,9 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                            hdr.flags);
                if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
                    (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
-                       wil_dbg_wmi(wil, "WMI event 0x%04x\n",
-                                   evt->event.wmi.id);
+                       u16 id = le16_to_cpu(evt->event.wmi.id);
+                       wil_dbg_wmi(wil, "WMI event 0x%04x\n", id);
+                       trace_wil6210_wmi_event(id, &evt->event.wmi, len);
                }
                wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
                                 &evt->event.hdr, sizeof(hdr) + len, true);
@@ -724,7 +730,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
                .bcon_interval = cpu_to_le16(bi),
                .network_type = wmi_nettype,
                .disable_sec_offload = 1,
-               .channel = chan,
+               .channel = chan - 1,
        };
        struct {
                struct wil6210_mbox_hdr_wmi wmi;
@@ -734,8 +740,12 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
        if (!wil->secure_pcp)
                cmd.disable_sec = 1;
 
+       /*
+        * Processing time may be huge, in case of secure AP it takes about
+        * 3500ms for FW to start AP
+        */
        rc = wmi_call(wil, WMI_PCP_START_CMDID, &cmd, sizeof(cmd),
-                     WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 100);
+                     WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 5000);
        if (rc)
                return rc;
 
@@ -829,40 +839,6 @@ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel)
        return wmi_send(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd));
 }
 
-int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb)
-{
-       struct wmi_eapol_tx_cmd *cmd;
-       struct ethhdr *eth;
-       u16 eapol_len = skb->len - ETH_HLEN;
-       void *eapol = skb->data + ETH_HLEN;
-       uint i;
-       int rc;
-
-       skb_set_mac_header(skb, 0);
-       eth = eth_hdr(skb);
-       wil_dbg_wmi(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest);
-       for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
-               if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0)
-                       goto found_dest;
-       }
-
-       return -EINVAL;
-
- found_dest:
-       /* find out eapol data & len */
-       cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL);
-       if (!cmd)
-               return -EINVAL;
-
-       memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN);
-       cmd->eapol_len = cpu_to_le16(eapol_len);
-       memcpy(cmd->eapol, eapol, eapol_len);
-       rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len);
-       kfree(cmd);
-
-       return rc;
-}
-
 int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
                       const void *mac_addr)
 {
index 078e6f3..51ff0b1 100644 (file)
@@ -28,18 +28,12 @@ config B43
 
 config B43_BCMA
        bool "Support for BCMA bus"
-       depends on B43 && BCMA
-       default y
-
-config B43_BCMA_EXTRA
-       bool "Hardware support that overlaps with the brcmsmac driver"
-       depends on B43_BCMA
-       default n if BRCMSMAC
+       depends on B43 && (BCMA = y || BCMA = B43)
        default y
 
 config B43_SSB
        bool
-       depends on B43 && SSB
+       depends on B43 && (SSB = y || SSB = B43)
        default y
 
 # Auto-select SSB PCI-HOST support, if possible
@@ -111,6 +105,7 @@ config B43_PIO
 config B43_PHY_N
        bool "Support for 802.11n (N-PHY) devices"
        depends on B43
+       default y
        ---help---
          Support for the N-PHY.
 
@@ -132,6 +127,7 @@ config B43_PHY_LP
 config B43_PHY_HT
        bool "Support for HT-PHY (high throughput) devices"
        depends on B43 && B43_BCMA
+       default y
        ---help---
          Support for the HT-PHY.
 
index a95b77a..0e933bb 100644 (file)
@@ -113,13 +113,15 @@ static int b43_modparam_pio = 0;
 module_param_named(pio, b43_modparam_pio, int, 0644);
 MODULE_PARM_DESC(pio, "Use PIO accesses by default: 0=DMA, 1=PIO");
 
+static int modparam_allhwsupport = !IS_ENABLED(CONFIG_BRCMSMAC);
+module_param_named(allhwsupport, modparam_allhwsupport, int, 0444);
+MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if overlaps with the brcmsmac driver)");
+
 #ifdef CONFIG_B43_BCMA
 static const struct bcma_device_id b43_bcma_tbl[] = {
        BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS),
-#ifdef CONFIG_B43_BCMA_EXTRA
        BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS),
        BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS),
-#endif
        BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS),
        BCMA_CORETABLE_END
 };
@@ -5396,6 +5398,12 @@ static int b43_bcma_probe(struct bcma_device *core)
        struct b43_wl *wl;
        int err;
 
+       if (!modparam_allhwsupport &&
+           (core->id.rev == 0x17 || core->id.rev == 0x18)) {
+               pr_err("Support for cores revisions 0x17 and 0x18 disabled by module param allhwsupport=0. Try b43.allhwsupport=1\n");
+               return -ENOTSUPP;
+       }
+
        dev = b43_bus_dev_bcma_init(core);
        if (!dev)
                return -ENODEV;
index 4891e3d..e3f3c48 100644 (file)
 #include <linux/pci_ids.h>
 #include <linux/sched.h>
 #include <linux/completion.h>
+#include <linux/scatterlist.h>
 #include <linux/mmc/sdio.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
 #include <linux/platform_data/brcmfmac-sdio.h>
 
 #include <defs.h>
@@ -160,7 +162,7 @@ int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev)
        return 0;
 }
 
-int
+static int
 brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
 {
        int err = 0, i;
@@ -191,12 +193,33 @@ brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
        return err;
 }
 
+static int
+brcmf_sdio_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr)
+{
+       uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+       int err = 0;
+
+       if (bar0 != sdiodev->sbwad) {
+               err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0);
+               if (err)
+                       return err;
+
+               sdiodev->sbwad = bar0;
+       }
+
+       *addr &= SBSDIO_SB_OFT_ADDR_MASK;
+
+       if (width == 4)
+               *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+       return 0;
+}
+
 int
 brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
                        void *data, bool write)
 {
        u8 func_num, reg_size;
-       u32 bar;
        s32 retry = 0;
        int ret;
 
@@ -216,18 +239,7 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
                func_num = SDIO_FUNC_1;
                reg_size = 4;
 
-               /* Set the window for SB core register */
-               bar = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
-               if (bar != sdiodev->sbwad) {
-                       ret = brcmf_sdcard_set_sbaddr_window(sdiodev, bar);
-                       if (ret != 0) {
-                               memset(data, 0xFF, reg_size);
-                               return ret;
-                       }
-                       sdiodev->sbwad = bar;
-               }
-               addr &= SBSDIO_SB_OFT_ADDR_MASK;
-               addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+               brcmf_sdio_addrprep(sdiodev, reg_size, &addr);
        }
 
        do {
@@ -303,30 +315,207 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
                *ret = retval;
 }
 
-static int brcmf_sdcard_recv_prepare(struct brcmf_sdio_dev *sdiodev, uint fn,
-                                    uint flags, uint width, u32 *addr)
+/**
+ * brcmf_sdio_buffrw - SDIO interface function for block data access
+ * @sdiodev: brcmfmac sdio device
+ * @fn: SDIO function number
+ * @write: direction flag
+ * @addr: dongle memory address as source/destination
+ * @pkt: skb pointer
+ *
+ * This function takes the respbonsibility as the interface function to MMC
+ * stack for block data access. It assumes that the skb passed down by the
+ * caller has already been padded and aligned.
+ */
+static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
+                            bool write, u32 addr, struct sk_buff_head *pktlist)
 {
-       uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK;
-       int err = 0;
+       unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
+       unsigned int max_blks, max_req_sz, orig_offset, dst_offset;
+       unsigned short max_seg_sz, seg_sz;
+       unsigned char *pkt_data, *orig_data, *dst_data;
+       struct sk_buff *pkt_next = NULL, *local_pkt_next;
+       struct sk_buff_head local_list, *target_list;
+       struct mmc_request mmc_req;
+       struct mmc_command mmc_cmd;
+       struct mmc_data mmc_dat;
+       struct sg_table st;
+       struct scatterlist *sgl;
+       struct mmc_host *host;
+       int ret = 0;
 
-       /* Async not implemented yet */
-       if (flags & SDIO_REQ_ASYNC)
-               return -ENOTSUPP;
+       if (!pktlist->qlen)
+               return -EINVAL;
 
-       if (bar0 != sdiodev->sbwad) {
-               err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0);
-               if (err)
-                       return err;
+       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
+       if (brcmf_pm_resume_error(sdiodev))
+               return -EIO;
 
-               sdiodev->sbwad = bar0;
+       /* Single skb use the standard mmc interface */
+       if (pktlist->qlen == 1) {
+               pkt_next = pktlist->next;
+               req_sz = pkt_next->len + 3;
+               req_sz &= (uint)~3;
+
+               if (write)
+                       return sdio_memcpy_toio(sdiodev->func[fn], addr,
+                                               ((u8 *)(pkt_next->data)),
+                                               req_sz);
+               else if (fn == 1)
+                       return sdio_memcpy_fromio(sdiodev->func[fn],
+                                                 ((u8 *)(pkt_next->data)),
+                                                 addr, req_sz);
+               else
+                       /* function 2 read is FIFO operation */
+                       return sdio_readsb(sdiodev->func[fn],
+                                          ((u8 *)(pkt_next->data)), addr,
+                                          req_sz);
        }
 
-       *addr &= SBSDIO_SB_OFT_ADDR_MASK;
+       target_list = pktlist;
+       /* for host with broken sg support, prepare a page aligned list */
+       __skb_queue_head_init(&local_list);
+       if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) {
+               req_sz = 0;
+               skb_queue_walk(pktlist, pkt_next)
+                       req_sz += pkt_next->len;
+               req_sz = ALIGN(req_sz, sdiodev->func[fn]->cur_blksize);
+               while (req_sz > PAGE_SIZE) {
+                       pkt_next = brcmu_pkt_buf_get_skb(PAGE_SIZE);
+                       if (pkt_next == NULL) {
+                               ret = -ENOMEM;
+                               goto exit;
+                       }
+                       __skb_queue_tail(&local_list, pkt_next);
+                       req_sz -= PAGE_SIZE;
+               }
+               pkt_next = brcmu_pkt_buf_get_skb(req_sz);
+               if (pkt_next == NULL) {
+                       ret = -ENOMEM;
+                       goto exit;
+               }
+               __skb_queue_tail(&local_list, pkt_next);
+               target_list = &local_list;
+       }
 
-       if (width == 4)
-               *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+       host = sdiodev->func[fn]->card->host;
+       func_blk_sz = sdiodev->func[fn]->cur_blksize;
+       /* Blocks per command is limited by host count, host transfer
+        * size and the maximum for IO_RW_EXTENDED of 511 blocks.
+        */
+       max_blks = min_t(unsigned int, host->max_blk_count, 511u);
+       max_req_sz = min_t(unsigned int, host->max_req_size,
+                          max_blks * func_blk_sz);
+       max_seg_sz = min_t(unsigned short, host->max_segs, SG_MAX_SINGLE_ALLOC);
+       max_seg_sz = min_t(unsigned short, max_seg_sz, target_list->qlen);
+       seg_sz = target_list->qlen;
+       pkt_offset = 0;
+       pkt_next = target_list->next;
+
+       if (sg_alloc_table(&st, max_seg_sz, GFP_KERNEL)) {
+               ret = -ENOMEM;
+               goto exit;
+       }
 
-       return 0;
+       while (seg_sz) {
+               req_sz = 0;
+               sg_cnt = 0;
+               memset(&mmc_req, 0, sizeof(struct mmc_request));
+               memset(&mmc_cmd, 0, sizeof(struct mmc_command));
+               memset(&mmc_dat, 0, sizeof(struct mmc_data));
+               sgl = st.sgl;
+               /* prep sg table */
+               while (pkt_next != (struct sk_buff *)target_list) {
+                       pkt_data = pkt_next->data + pkt_offset;
+                       sg_data_sz = pkt_next->len - pkt_offset;
+                       if (sg_data_sz > host->max_seg_size)
+                               sg_data_sz = host->max_seg_size;
+                       if (sg_data_sz > max_req_sz - req_sz)
+                               sg_data_sz = max_req_sz - req_sz;
+
+                       sg_set_buf(sgl, pkt_data, sg_data_sz);
+
+                       sg_cnt++;
+                       sgl = sg_next(sgl);
+                       req_sz += sg_data_sz;
+                       pkt_offset += sg_data_sz;
+                       if (pkt_offset == pkt_next->len) {
+                               pkt_offset = 0;
+                               pkt_next = pkt_next->next;
+                       }
+
+                       if (req_sz >= max_req_sz || sg_cnt >= max_seg_sz)
+                               break;
+               }
+               seg_sz -= sg_cnt;
+
+               if (req_sz % func_blk_sz != 0) {
+                       brcmf_err("sg request length %u is not %u aligned\n",
+                                 req_sz, func_blk_sz);
+                       ret = -ENOTBLK;
+                       goto exit;
+               }
+               mmc_dat.sg = st.sgl;
+               mmc_dat.sg_len = sg_cnt;
+               mmc_dat.blksz = func_blk_sz;
+               mmc_dat.blocks = req_sz / func_blk_sz;
+               mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+               mmc_cmd.opcode = SD_IO_RW_EXTENDED;
+               mmc_cmd.arg = write ? 1<<31 : 0;        /* write flag  */
+               mmc_cmd.arg |= (fn & 0x7) << 28;        /* SDIO func num */
+               mmc_cmd.arg |= 1<<27;                   /* block mode */
+               /* incrementing addr for function 1 */
+               mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0;
+               mmc_cmd.arg |= (addr & 0x1FFFF) << 9;   /* address */
+               mmc_cmd.arg |= mmc_dat.blocks & 0x1FF;  /* block count */
+               mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+               mmc_req.cmd = &mmc_cmd;
+               mmc_req.data = &mmc_dat;
+               if (fn == 1)
+                       addr += req_sz;
+
+               mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card);
+               mmc_wait_for_req(host, &mmc_req);
+
+               ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error;
+               if (ret != 0) {
+                       brcmf_err("CMD53 sg block %s failed %d\n",
+                                 write ? "write" : "read", ret);
+                       ret = -EIO;
+                       break;
+               }
+       }
+
+       if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) {
+               local_pkt_next = local_list.next;
+               orig_offset = 0;
+               skb_queue_walk(pktlist, pkt_next) {
+                       dst_offset = 0;
+                       do {
+                               req_sz = local_pkt_next->len - orig_offset;
+                               req_sz = min_t(uint, pkt_next->len - dst_offset,
+                                              req_sz);
+                               orig_data = local_pkt_next->data + orig_offset;
+                               dst_data = pkt_next->data + dst_offset;
+                               memcpy(dst_data, orig_data, req_sz);
+                               orig_offset += req_sz;
+                               dst_offset += req_sz;
+                               if (orig_offset == local_pkt_next->len) {
+                                       orig_offset = 0;
+                                       local_pkt_next = local_pkt_next->next;
+                               }
+                               if (dst_offset == pkt_next->len)
+                                       break;
+                       } while (!skb_queue_empty(&local_list));
+               }
+       }
+
+exit:
+       sg_free_table(&st);
+       while ((pkt_next = __skb_dequeue(&local_list)) != NULL)
+               brcmu_pkt_buf_free_skb(pkt_next);
+
+       return ret;
 }
 
 int
@@ -355,21 +544,22 @@ int
 brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, struct sk_buff *pkt)
 {
-       uint incr_fix;
        uint width;
        int err = 0;
+       struct sk_buff_head pkt_list;
 
        brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pkt->len);
 
        width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
-       err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr);
+       err = brcmf_sdio_addrprep(sdiodev, width, &addr);
        if (err)
                goto done;
 
-       incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
-       err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ,
-                                        fn, addr, pkt);
+       skb_queue_head_init(&pkt_list);
+       skb_queue_tail(&pkt_list, pkt);
+       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, &pkt_list);
+       skb_dequeue_tail(&pkt_list);
 
 done:
        return err;
@@ -386,13 +576,12 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                  fn, addr, pktq->qlen);
 
        width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
-       err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr);
+       err = brcmf_sdio_addrprep(sdiodev, width, &addr);
        if (err)
                goto done;
 
        incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
-       err = brcmf_sdioh_request_chain(sdiodev, incr_fix, SDIOH_READ, fn, addr,
-                                       pktq);
+       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq);
 
 done:
        return err;
@@ -424,37 +613,21 @@ int
 brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, struct sk_buff *pkt)
 {
-       uint incr_fix;
        uint width;
-       uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
        int err = 0;
+       struct sk_buff_head pkt_list;
 
        brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pkt->len);
 
-       /* Async not implemented yet */
-       if (flags & SDIO_REQ_ASYNC)
-               return -ENOTSUPP;
-
-       if (bar0 != sdiodev->sbwad) {
-               err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0);
-               if (err)
-                       goto done;
-
-               sdiodev->sbwad = bar0;
-       }
-
-       addr &= SBSDIO_SB_OFT_ADDR_MASK;
-
-       incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
        width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
-       if (width == 4)
-               addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+       brcmf_sdio_addrprep(sdiodev, width, &addr);
 
-       err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn,
-                                        addr, pkt);
+       skb_queue_head_init(&pkt_list);
+       skb_queue_tail(&pkt_list, pkt);
+       err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, &pkt_list);
+       skb_dequeue_tail(&pkt_list);
 
-done:
        return err;
 }
 
@@ -466,6 +639,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
        struct sk_buff *pkt;
        u32 sdaddr;
        uint dsize;
+       struct sk_buff_head pkt_list;
 
        dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
        pkt = dev_alloc_skb(dsize);
@@ -474,6 +648,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
                return -EIO;
        }
        pkt->priority = 0;
+       skb_queue_head_init(&pkt_list);
 
        /* Determine initial transfer parameters */
        sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
@@ -501,9 +676,10 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
                skb_put(pkt, dsize);
                if (write)
                        memcpy(pkt->data, data, dsize);
-               bcmerror = brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC,
-                                                     write, SDIO_FUNC_1,
-                                                     sdaddr, pkt);
+               skb_queue_tail(&pkt_list, pkt);
+               bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write,
+                                            sdaddr, &pkt_list);
+               skb_dequeue_tail(&pkt_list);
                if (bcmerror) {
                        brcmf_err("membytes transfer failed\n");
                        break;
index 44fa0cd..289e386 100644 (file)
@@ -66,7 +66,7 @@ MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
 static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata;
 
 
-static bool
+bool
 brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
 {
        bool is_err = false;
@@ -76,7 +76,7 @@ brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
        return is_err;
 }
 
-static void
+void
 brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq)
 {
 #ifdef CONFIG_PM_SLEEP
@@ -211,115 +211,6 @@ int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev,
        return err_ret;
 }
 
-/* precondition: host controller is claimed */
-static int
-brcmf_sdioh_request_data(struct brcmf_sdio_dev *sdiodev, uint write, bool fifo,
-                        uint func, uint addr, struct sk_buff *pkt, uint pktlen)
-{
-       int err_ret = 0;
-
-       if ((write) && (!fifo)) {
-               err_ret = sdio_memcpy_toio(sdiodev->func[func], addr,
-                                          ((u8 *) (pkt->data)), pktlen);
-       } else if (write) {
-               err_ret = sdio_memcpy_toio(sdiodev->func[func], addr,
-                                          ((u8 *) (pkt->data)), pktlen);
-       } else if (fifo) {
-               err_ret = sdio_readsb(sdiodev->func[func],
-                                     ((u8 *) (pkt->data)), addr, pktlen);
-       } else {
-               err_ret = sdio_memcpy_fromio(sdiodev->func[func],
-                                            ((u8 *) (pkt->data)),
-                                            addr, pktlen);
-       }
-
-       return err_ret;
-}
-
-/*
- * This function takes a queue of packets. The packets on the queue
- * are assumed to be properly aligned by the caller.
- */
-int
-brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc,
-                         uint write, uint func, uint addr,
-                         struct sk_buff_head *pktq)
-{
-       bool fifo = (fix_inc == SDIOH_DATA_FIX);
-       u32 SGCount = 0;
-       int err_ret = 0;
-
-       struct sk_buff *pkt;
-
-       brcmf_dbg(SDIO, "Enter\n");
-
-       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_chain_wait);
-       if (brcmf_pm_resume_error(sdiodev))
-               return -EIO;
-
-       skb_queue_walk(pktq, pkt) {
-               uint pkt_len = pkt->len;
-               pkt_len += 3;
-               pkt_len &= 0xFFFFFFFC;
-
-               err_ret = brcmf_sdioh_request_data(sdiodev, write, fifo, func,
-                                                  addr, pkt, pkt_len);
-               if (err_ret) {
-                       brcmf_err("%s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
-                                 write ? "TX" : "RX", pkt, SGCount, addr,
-                                 pkt_len, err_ret);
-               } else {
-                       brcmf_dbg(SDIO, "%s xfr'd %p[%d], addr=0x%05x, len=%d\n",
-                                 write ? "TX" : "RX", pkt, SGCount, addr,
-                                 pkt_len);
-               }
-               if (!fifo)
-                       addr += pkt_len;
-
-               SGCount++;
-       }
-
-       brcmf_dbg(SDIO, "Exit\n");
-       return err_ret;
-}
-
-/*
- * This function takes a single DMA-able packet.
- */
-int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev,
-                              uint fix_inc, uint write, uint func, uint addr,
-                              struct sk_buff *pkt)
-{
-       int status;
-       uint pkt_len;
-       bool fifo = (fix_inc == SDIOH_DATA_FIX);
-
-       brcmf_dbg(SDIO, "Enter\n");
-
-       if (pkt == NULL)
-               return -EINVAL;
-       pkt_len = pkt->len;
-
-       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
-       if (brcmf_pm_resume_error(sdiodev))
-               return -EIO;
-
-       pkt_len += 3;
-       pkt_len &= (uint)~3;
-
-       status = brcmf_sdioh_request_data(sdiodev, write, fifo, func,
-                                          addr, pkt, pkt_len);
-       if (status) {
-               brcmf_err("%s FAILED %p, addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
-                         write ? "TX" : "RX", pkt, addr, pkt_len, status);
-       } else {
-               brcmf_dbg(SDIO, "%s xfr'd %p, addr=0x%05x, len=%d\n",
-                         write ? "TX" : "RX", pkt, addr, pkt_len);
-       }
-
-       return status;
-}
-
 static int brcmf_sdioh_get_cisaddr(struct brcmf_sdio_dev *sdiodev, u32 regaddr)
 {
        /* read 24 bits and return valid 17 bit addr */
@@ -468,7 +359,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
        atomic_set(&sdiodev->suspend, false);
        init_waitqueue_head(&sdiodev->request_byte_wait);
        init_waitqueue_head(&sdiodev->request_word_wait);
-       init_waitqueue_head(&sdiodev->request_chain_wait);
        init_waitqueue_head(&sdiodev->request_buffer_wait);
 
        brcmf_dbg(SDIO, "F2 found, calling brcmf_sdio_probe...\n");
@@ -606,7 +496,8 @@ static int brcmf_sdio_pd_remove(struct platform_device *pdev)
 static struct platform_driver brcmf_sdio_pd = {
        .remove         = brcmf_sdio_pd_remove,
        .driver         = {
-               .name   = BRCMFMAC_SDIO_PDATA_NAME
+               .name   = BRCMFMAC_SDIO_PDATA_NAME,
+               .owner  = THIS_MODULE,
        }
 };
 
index 28db9cf..86cbfe2 100644 (file)
@@ -583,6 +583,7 @@ enum brcmf_netif_stop_reason {
  * @bssidx: index of bss associated with this interface.
  * @mac_addr: assigned mac address.
  * @netif_stop: bitmap indicates reason why netif queues are stopped.
+ * @netif_stop_lock: spinlock for update netif_stop from multiple sources.
  * @pend_8021x_cnt: tracks outstanding number of 802.1x frames.
  * @pend_8021x_wait: used for signalling change in count.
  */
@@ -598,6 +599,7 @@ struct brcmf_if {
        s32 bssidx;
        u8 mac_addr[ETH_ALEN];
        u8 netif_stop;
+       spinlock_t netif_stop_lock;
        atomic_t pend_8021x_cnt;
        wait_queue_head_t pend_8021x_wait;
 };
index 59c77aa..dd85401 100644 (file)
@@ -30,6 +30,7 @@
 #include "dhd_bus.h"
 #include "fwsignal.h"
 #include "dhd_dbg.h"
+#include "tracepoint.h"
 
 struct brcmf_proto_cdc_dcmd {
        __le32 cmd;     /* dongle command value */
@@ -292,6 +293,7 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
        h->flags2 = 0;
        h->data_offset = offset;
        BDC_SET_IF_IDX(h, ifidx);
+       trace_brcmf_bdchdr(pktbuf->data);
 }
 
 int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
@@ -309,6 +311,7 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
                return -EBADE;
        }
 
+       trace_brcmf_bdchdr(pktbuf->data);
        h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
 
        *ifidx = BDC_GET_IF_IDX(h);
index 202869c..c37b9d6 100644 (file)
@@ -156,8 +156,11 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
                        "txs_suppr_core:    %u\n"
                        "txs_suppr_ps:      %u\n"
                        "txs_tossed:        %u\n"
+                       "txs_host_tossed:   %u\n"
+                       "bus_flow_block:    %u\n"
+                       "fws_flow_block:    %u\n"
                        "send_pkts:         BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
-                       "fifo_credits_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
+                       "requested_sent:    BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
                        fwstats->header_pulls,
                        fwstats->header_only_pkt,
                        fwstats->tlv_parse_failed,
@@ -176,14 +179,17 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
                        fwstats->txs_supp_core,
                        fwstats->txs_supp_ps,
                        fwstats->txs_tossed,
+                       fwstats->txs_host_tossed,
+                       fwstats->bus_flow_block,
+                       fwstats->fws_flow_block,
                        fwstats->send_pkts[0], fwstats->send_pkts[1],
                        fwstats->send_pkts[2], fwstats->send_pkts[3],
                        fwstats->send_pkts[4],
-                       fwstats->fifo_credits_sent[0],
-                       fwstats->fifo_credits_sent[1],
-                       fwstats->fifo_credits_sent[2],
-                       fwstats->fifo_credits_sent[3],
-                       fwstats->fifo_credits_sent[4]);
+                       fwstats->requested_sent[0],
+                       fwstats->requested_sent[1],
+                       fwstats->requested_sent[2],
+                       fwstats->requested_sent[3],
+                       fwstats->requested_sent[4]);
 
        return simple_read_from_buffer(data, count, ppos, buf, res);
 }
index 009c87b..0af1f5d 100644 (file)
@@ -141,8 +141,7 @@ struct brcmf_fws_stats {
        u32 header_pulls;
        u32 pkt2bus;
        u32 send_pkts[5];
-       u32 fifo_credits_sent[5];
-       u32 fifo_credits_back[6];
+       u32 requested_sent[5];
        u32 generic_error;
        u32 mac_update_failed;
        u32 mac_ps_update_failed;
@@ -158,6 +157,9 @@ struct brcmf_fws_stats {
        u32 txs_supp_core;
        u32 txs_supp_ps;
        u32 txs_tossed;
+       u32 txs_host_tossed;
+       u32 bus_flow_block;
+       u32 fws_flow_block;
 };
 
 struct brcmf_pub;
index 2c59357..8e89755 100644 (file)
@@ -179,7 +179,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
        struct brcmf_pub *drvr = ifp->drvr;
        struct ethhdr *eh;
 
-       brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
+       brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx);
 
        /* Can the device send data? */
        if (drvr->bus_if->state != BRCMF_BUS_DATA) {
@@ -240,11 +240,15 @@ done:
 void brcmf_txflowblock_if(struct brcmf_if *ifp,
                          enum brcmf_netif_stop_reason reason, bool state)
 {
+       unsigned long flags;
+
        if (!ifp)
                return;
 
        brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n",
                  ifp->bssidx, ifp->netif_stop, reason, state);
+
+       spin_lock_irqsave(&ifp->netif_stop_lock, flags);
        if (state) {
                if (!ifp->netif_stop)
                        netif_stop_queue(ifp->ndev);
@@ -254,6 +258,7 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp,
                if (!ifp->netif_stop)
                        netif_wake_queue(ifp->ndev);
        }
+       spin_unlock_irqrestore(&ifp->netif_stop_lock, flags);
 }
 
 void brcmf_txflowblock(struct device *dev, bool state)
@@ -264,15 +269,18 @@ void brcmf_txflowblock(struct device *dev, bool state)
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       for (i = 0; i < BRCMF_MAX_IFS; i++)
-               brcmf_txflowblock_if(drvr->iflist[i],
-                                    BRCMF_NETIF_STOP_REASON_BLOCK_BUS, state);
+       if (brcmf_fws_fc_active(drvr->fws)) {
+               brcmf_fws_bus_blocked(drvr, state);
+       } else {
+               for (i = 0; i < BRCMF_MAX_IFS; i++)
+                       brcmf_txflowblock_if(drvr->iflist[i],
+                                            BRCMF_NETIF_STOP_REASON_BLOCK_BUS,
+                                            state);
+       }
 }
 
 void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
 {
-       unsigned char *eth;
-       uint len;
        struct sk_buff *skb, *pnext;
        struct brcmf_if *ifp;
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
@@ -280,7 +288,7 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
        u8 ifidx;
        int ret;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(DATA, "Enter\n");
 
        skb_queue_walk_safe(skb_list, skb, pnext) {
                skb_unlink(skb, skb_list);
@@ -296,33 +304,12 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
                        continue;
                }
 
-               /* Get the protocol, maintain skb around eth_type_trans()
-                * The main reason for this hack is for the limitation of
-                * Linux 2.4 where 'eth_type_trans' uses the
-                * 'net->hard_header_len'
-                * to perform skb_pull inside vs ETH_HLEN. Since to avoid
-                * coping of the packet coming from the network stack to add
-                * BDC, Hardware header etc, during network interface
-                * registration
-                * we set the 'net->hard_header_len' to ETH_HLEN + extra space
-                * required
-                * for BDC, Hardware header etc. and not just the ETH_HLEN
-                */
-               eth = skb->data;
-               len = skb->len;
-
                skb->dev = ifp->ndev;
                skb->protocol = eth_type_trans(skb, skb->dev);
 
                if (skb->pkt_type == PACKET_MULTICAST)
                        ifp->stats.multicast++;
 
-               skb->data = eth;
-               skb->len = len;
-
-               /* Strip header, count, deliver upward */
-               skb_pull(skb, ETH_HLEN);
-
                /* Process special event packets */
                brcmf_fweh_process_skb(drvr, skb);
 
@@ -338,10 +325,8 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
                        netif_rx(skb);
                else
                        /* If the receive is not processed inside an ISR,
-                        * the softirqd must be woken explicitly to service
-                        * the NET_RX_SOFTIRQ.  In 2.6 kernels, this is handled
-                        * by netif_rx_ni(), but in earlier kernels, we need
-                        * to do it manually.
+                        * the softirqd must be woken explicitly to service the
+                        * NET_RX_SOFTIRQ. This is handled by netif_rx_ni().
                         */
                        netif_rx_ni(skb);
        }
@@ -630,7 +615,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
        /* set appropriate operations */
        ndev->netdev_ops = &brcmf_netdev_ops_pri;
 
-       ndev->hard_header_len = ETH_HLEN + drvr->hdrlen;
+       ndev->hard_header_len += drvr->hdrlen;
        ndev->ethtool_ops = &brcmf_ethtool_ops;
 
        drvr->rxsz = ndev->mtu + ndev->hard_header_len +
@@ -779,6 +764,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
        ifp->bssidx = bssidx;
 
        init_waitqueue_head(&ifp->pend_8021x_wait);
+       spin_lock_init(&ifp->netif_stop_lock);
 
        if (mac_addr != NULL)
                memcpy(ifp->mac_addr, mac_addr, ETH_ALEN);
index d248751..2641119 100644 (file)
@@ -448,8 +448,6 @@ struct brcmf_sdio {
        uint rxblen;            /* Allocated length of rxbuf */
        u8 *rxctl;              /* Aligned pointer into rxbuf */
        u8 *rxctl_orig;         /* pointer for freeing rxctl */
-       u8 *databuf;            /* Buffer for receiving big glom packet */
-       u8 *dataptr;            /* Aligned pointer into databuf */
        uint rxlen;             /* Length of valid data in buffer */
        spinlock_t rxctl_lock;  /* protection lock for ctrl frame resources */
 
@@ -473,8 +471,6 @@ struct brcmf_sdio {
        s32 idletime;           /* Control for activity timeout */
        s32 idlecount;  /* Activity timeout counter */
        s32 idleclock;  /* How to set bus driver when idle */
-       s32 sd_rxchain;
-       bool use_rxchain;       /* If brcmf should use PKT chains */
        bool rxflow_mode;       /* Rx flow control mode */
        bool rxflow;            /* Is rx flow control on */
        bool alp_only;          /* Don't use HT clock (ALP only) */
@@ -495,8 +491,7 @@ struct brcmf_sdio {
 
        struct workqueue_struct *brcmf_wq;
        struct work_struct datawork;
-       struct list_head dpc_tsklst;
-       spinlock_t dpc_tl_lock;
+       atomic_t dpc_tskcnt;
 
        const struct firmware *firmware;
        u32 fw_ptr;
@@ -1026,29 +1021,6 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
                bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
 }
 
-/* copy a buffer into a pkt buffer chain */
-static uint brcmf_sdbrcm_glom_from_buf(struct brcmf_sdio *bus, uint len)
-{
-       uint n, ret = 0;
-       struct sk_buff *p;
-       u8 *buf;
-
-       buf = bus->dataptr;
-
-       /* copy the data */
-       skb_queue_walk(&bus->glom, p) {
-               n = min_t(uint, p->len, len);
-               memcpy(p->data, buf, n);
-               buf += n;
-               len -= n;
-               ret += n;
-               if (!len)
-                       break;
-       }
-
-       return ret;
-}
-
 /* return total length of buffer chain */
 static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus)
 {
@@ -1202,8 +1174,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
        int errcode;
        u8 doff, sfdoff;
 
-       bool usechain = bus->use_rxchain;
-
        struct brcmf_sdio_read rd_new;
 
        /* If packets, issue read(s) and send up packet chain */
@@ -1238,7 +1208,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                        if (sublen % BRCMF_SDALIGN) {
                                brcmf_err("sublen %d not multiple of %d\n",
                                          sublen, BRCMF_SDALIGN);
-                               usechain = false;
                        }
                        totlen += sublen;
 
@@ -1305,27 +1274,9 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                 * packet and and copy into the chain.
                 */
                sdio_claim_host(bus->sdiodev->func[1]);
-               if (usechain) {
-                       errcode = brcmf_sdcard_recv_chain(bus->sdiodev,
-                                       bus->sdiodev->sbwad,
-                                       SDIO_FUNC_2, F2SYNC, &bus->glom);
-               } else if (bus->dataptr) {
-                       errcode = brcmf_sdcard_recv_buf(bus->sdiodev,
-                                       bus->sdiodev->sbwad,
-                                       SDIO_FUNC_2, F2SYNC,
-                                       bus->dataptr, dlen);
-                       sublen = (u16) brcmf_sdbrcm_glom_from_buf(bus, dlen);
-                       if (sublen != dlen) {
-                               brcmf_err("FAILED TO COPY, dlen %d sublen %d\n",
-                                         dlen, sublen);
-                               errcode = -1;
-                       }
-                       pnext = NULL;
-               } else {
-                       brcmf_err("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n",
-                                 dlen);
-                       errcode = -1;
-               }
+               errcode = brcmf_sdcard_recv_chain(bus->sdiodev,
+                               bus->sdiodev->sbwad,
+                               SDIO_FUNC_2, F2SYNC, &bus->glom);
                sdio_release_host(bus->sdiodev->func[1]);
                bus->sdcnt.f2rxdata++;
 
@@ -2061,23 +2012,6 @@ static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus)
        }
 }
 
-static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus)
-{
-       struct list_head *new_hd;
-       unsigned long flags;
-
-       if (in_interrupt())
-               new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
-       else
-               new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL);
-       if (new_hd == NULL)
-               return;
-
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-       list_add_tail(new_hd, &bus->dpc_tsklst);
-       spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-}
-
 static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
 {
        u8 idx;
@@ -2312,7 +2246,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
                   (!atomic_read(&bus->fcstate) &&
                    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
                    data_ok(bus)) || PKT_AVAILABLE()) {
-               brcmf_sdbrcm_adddpctsk(bus);
+               atomic_inc(&bus->dpc_tskcnt);
        }
 
        /* If we're done for now, turn off clock request. */
@@ -2342,7 +2276,6 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
        struct brcmf_sdio *bus = sdiodev->bus;
-       unsigned long flags;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -2369,26 +2302,21 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
        } else {
                ret = 0;
        }
-       spin_unlock_bh(&bus->txqlock);
 
        if (pktq_len(&bus->txq) >= TXHI) {
                bus->txoff = true;
                brcmf_txflowblock(bus->sdiodev->dev, true);
        }
+       spin_unlock_bh(&bus->txqlock);
 
 #ifdef DEBUG
        if (pktq_plen(&bus->txq, prec) > qcount[prec])
                qcount[prec] = pktq_plen(&bus->txq, prec);
 #endif
 
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-       if (list_empty(&bus->dpc_tsklst)) {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-
-               brcmf_sdbrcm_adddpctsk(bus);
+       if (atomic_read(&bus->dpc_tskcnt) == 0) {
+               atomic_inc(&bus->dpc_tskcnt);
                queue_work(bus->brcmf_wq, &bus->datawork);
-       } else {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
        }
 
        return ret;
@@ -2525,7 +2453,6 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
        struct brcmf_sdio *bus = sdiodev->bus;
-       unsigned long flags;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -2612,18 +2539,13 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
                } while (ret < 0 && retries++ < TXRETRIES);
        }
 
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
        if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) &&
-           list_empty(&bus->dpc_tsklst)) {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-
+           atomic_read(&bus->dpc_tskcnt) == 0) {
                bus->activity = false;
                sdio_claim_host(bus->sdiodev->func[1]);
                brcmf_dbg(INFO, "idle\n");
                brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
                sdio_release_host(bus->sdiodev->func[1]);
-       } else {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
        }
 
        if (ret)
@@ -3451,7 +3373,7 @@ void brcmf_sdbrcm_isr(void *arg)
        if (!bus->intr)
                brcmf_err("isr w/o interrupt configured!\n");
 
-       brcmf_sdbrcm_adddpctsk(bus);
+       atomic_inc(&bus->dpc_tskcnt);
        queue_work(bus->brcmf_wq, &bus->datawork);
 }
 
@@ -3460,7 +3382,6 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
 #ifdef DEBUG
        struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev);
 #endif /* DEBUG */
-       unsigned long flags;
 
        brcmf_dbg(TIMER, "Enter\n");
 
@@ -3476,11 +3397,9 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                if (!bus->intr ||
                    (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {
 
-                       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-                       if (list_empty(&bus->dpc_tsklst)) {
+                       if (atomic_read(&bus->dpc_tskcnt) == 0) {
                                u8 devpend;
-                               spin_unlock_irqrestore(&bus->dpc_tl_lock,
-                                                      flags);
+
                                sdio_claim_host(bus->sdiodev->func[1]);
                                devpend = brcmf_sdio_regrb(bus->sdiodev,
                                                           SDIO_CCCR_INTx,
@@ -3489,9 +3408,6 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                                intstatus =
                                    devpend & (INTR_STATUS_FUNC1 |
                                               INTR_STATUS_FUNC2);
-                       } else {
-                               spin_unlock_irqrestore(&bus->dpc_tl_lock,
-                                                      flags);
                        }
 
                        /* If there is something, make like the ISR and
@@ -3500,7 +3416,7 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                                bus->sdcnt.pollcnt++;
                                atomic_set(&bus->ipend, 1);
 
-                               brcmf_sdbrcm_adddpctsk(bus);
+                               atomic_inc(&bus->dpc_tskcnt);
                                queue_work(bus->brcmf_wq, &bus->datawork);
                        }
                }
@@ -3545,41 +3461,15 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
        return (atomic_read(&bus->ipend) > 0);
 }
 
-static bool brcmf_sdbrcm_chipmatch(u16 chipid)
-{
-       if (chipid == BCM43143_CHIP_ID)
-               return true;
-       if (chipid == BCM43241_CHIP_ID)
-               return true;
-       if (chipid == BCM4329_CHIP_ID)
-               return true;
-       if (chipid == BCM4330_CHIP_ID)
-               return true;
-       if (chipid == BCM4334_CHIP_ID)
-               return true;
-       if (chipid == BCM4335_CHIP_ID)
-               return true;
-       return false;
-}
-
 static void brcmf_sdio_dataworker(struct work_struct *work)
 {
        struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio,
                                              datawork);
-       struct list_head *cur_hd, *tmp_hd;
-       unsigned long flags;
-
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-       list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
 
+       while (atomic_read(&bus->dpc_tskcnt)) {
                brcmf_sdbrcm_dpc(bus);
-
-               spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-               list_del(cur_hd);
-               kfree(cur_hd);
+               atomic_dec(&bus->dpc_tskcnt);
        }
-       spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
 }
 
 static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus)
@@ -3589,9 +3479,6 @@ static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus)
        kfree(bus->rxbuf);
        bus->rxctl = bus->rxbuf = NULL;
        bus->rxlen = 0;
-
-       kfree(bus->databuf);
-       bus->databuf = NULL;
 }
 
 static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus)
@@ -3604,29 +3491,10 @@ static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus)
                            ALIGNMENT) + BRCMF_SDALIGN;
                bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
                if (!(bus->rxbuf))
-                       goto fail;
-       }
-
-       /* Allocate buffer to receive glomed packet */
-       bus->databuf = kmalloc(MAX_DATA_BUF, GFP_ATOMIC);
-       if (!(bus->databuf)) {
-               /* release rxbuf which was already located as above */
-               if (!bus->rxblen)
-                       kfree(bus->rxbuf);
-               goto fail;
+                       return false;
        }
 
-       /* Align the buffer */
-       if ((unsigned long)bus->databuf % BRCMF_SDALIGN)
-               bus->dataptr = bus->databuf + (BRCMF_SDALIGN -
-                              ((unsigned long)bus->databuf % BRCMF_SDALIGN));
-       else
-               bus->dataptr = bus->databuf;
-
        return true;
-
-fail:
-       return false;
 }
 
 static bool
@@ -3667,11 +3535,6 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
                goto fail;
        }
 
-       if (!brcmf_sdbrcm_chipmatch((u16) bus->ci->chip)) {
-               brcmf_err("unsupported chip: 0x%04x\n", bus->ci->chip);
-               goto fail;
-       }
-
        if (brcmf_sdbrcm_kso_init(bus)) {
                brcmf_err("error enabling KSO\n");
                goto fail;
@@ -3770,10 +3633,6 @@ static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus)
        bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
        bus->roundup = min(max_roundup, bus->blocksize);
 
-       /* bus module does not support packet chaining */
-       bus->use_rxchain = false;
-       bus->sd_rxchain = false;
-
        /* SR state */
        bus->sleeping = false;
        bus->sr_enabled = false;
@@ -3927,8 +3786,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
                bus->watchdog_tsk = NULL;
        }
        /* Initialize DPC thread */
-       INIT_LIST_HEAD(&bus->dpc_tsklst);
-       spin_lock_init(&bus->dpc_tl_lock);
+       atomic_set(&bus->dpc_tskcnt, 0);
 
        /* Assign bus interface call back */
        bus->sdiodev->bus_if->dev = bus->sdiodev->dev;
index 6ec5db9..e679214 100644 (file)
@@ -101,7 +101,8 @@ struct brcmf_event;
        BRCMF_ENUM_DEF(P2P_PROBEREQ_MSG, 72) \
        BRCMF_ENUM_DEF(DCS_REQUEST, 73) \
        BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \
-       BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75)
+       BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \
+       BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127)
 
 #define BRCMF_ENUM_DEF(id, val) \
        BRCMF_E_##id = (val),
index 5352dc1..f0d9f7f 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/etherdevice.h>
 #include <linux/err.h>
 #include <linux/jiffies.h>
-#include <uapi/linux/nl80211.h>
 #include <net/cfg80211.h>
 
 #include <brcmu_utils.h>
@@ -142,7 +141,7 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
 #define BRCMF_FWS_FLOWCONTROL_HIWATER                  128
 #define BRCMF_FWS_FLOWCONTROL_LOWATER                  64
 
-#define BRCMF_FWS_PSQ_PREC_COUNT               ((NL80211_NUM_ACS + 1) * 2)
+#define BRCMF_FWS_PSQ_PREC_COUNT               ((BRCMF_FWS_FIFO_COUNT + 1) * 2)
 #define BRCMF_FWS_PSQ_LEN                              256
 
 #define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST                        0x01
@@ -157,11 +156,13 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
  * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver.
  * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue.
  * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware.
+ * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info.
  */
 enum brcmf_fws_skb_state {
        BRCMF_FWS_SKBSTATE_NEW,
        BRCMF_FWS_SKBSTATE_DELAYED,
-       BRCMF_FWS_SKBSTATE_SUPPRESSED
+       BRCMF_FWS_SKBSTATE_SUPPRESSED,
+       BRCMF_FWS_SKBSTATE_TIM
 };
 
 /**
@@ -193,9 +194,8 @@ struct brcmf_skbuff_cb {
  *     b[11]  - packet sent upon firmware request.
  *     b[10]  - packet only contains signalling data.
  *     b[9]   - packet is a tx packet.
- *     b[8]   - packet uses FIFO credit (non-pspoll).
+ *     b[8]   - packet used requested credit
  *     b[7]   - interface in AP mode.
- *     b[6:4] - AC FIFO number.
  *     b[3:0] - interface index.
  */
 #define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK      0x0800
@@ -204,12 +204,10 @@ struct brcmf_skbuff_cb {
 #define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT   10
 #define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK        0x0200
 #define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT      9
-#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK    0x0100
-#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_SHIFT   8
+#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK     0x0100
+#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT    8
 #define BRCMF_SKB_IF_FLAGS_IF_AP_MASK          0x0080
 #define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT         7
-#define BRCMF_SKB_IF_FLAGS_FIFO_MASK           0x0070
-#define BRCMF_SKB_IF_FLAGS_FIFO_SHIFT          4
 #define BRCMF_SKB_IF_FLAGS_INDEX_MASK          0x000f
 #define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT         0
 
@@ -246,7 +244,7 @@ struct brcmf_skbuff_cb {
 #define BRCMF_SKB_HTOD_TAG_HSLOT_MASK                  0x00ffff00
 #define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT                 8
 #define BRCMF_SKB_HTOD_TAG_FREERUN_MASK                        0x000000ff
-#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT                       0
+#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT               0
 
 #define brcmf_skb_htod_tag_set_field(skb, field, value) \
        brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \
@@ -278,6 +276,7 @@ struct brcmf_skbuff_cb {
 /**
  * enum brcmf_fws_fifo - fifo indices used by dongle firmware.
  *
+ * @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background.
  * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic.
  * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic.
  * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic.
@@ -287,7 +286,8 @@ struct brcmf_skbuff_cb {
  * @BRCMF_FWS_FIFO_COUNT: number of fifos.
  */
 enum brcmf_fws_fifo {
-       BRCMF_FWS_FIFO_AC_BK,
+       BRCMF_FWS_FIFO_FIRST,
+       BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST,
        BRCMF_FWS_FIFO_AC_BE,
        BRCMF_FWS_FIFO_AC_VI,
        BRCMF_FWS_FIFO_AC_VO,
@@ -307,12 +307,15 @@ enum brcmf_fws_fifo {
  *     firmware suppress the packet as device is already in PS mode.
  * @BRCMF_FWS_TXSTATUS_FW_TOSSED:
  *     firmware tossed the packet.
+ * @BRCMF_FWS_TXSTATUS_HOST_TOSSED:
+ *     host tossed the packet.
  */
 enum brcmf_fws_txstatus {
        BRCMF_FWS_TXSTATUS_DISCARD,
        BRCMF_FWS_TXSTATUS_CORE_SUPPRESS,
        BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS,
-       BRCMF_FWS_TXSTATUS_FW_TOSSED
+       BRCMF_FWS_TXSTATUS_FW_TOSSED,
+       BRCMF_FWS_TXSTATUS_HOST_TOSSED
 };
 
 enum brcmf_fws_fcmode {
@@ -343,6 +346,7 @@ enum brcmf_fws_mac_desc_state {
  * @transit_count: packet in transit to firmware.
  */
 struct brcmf_fws_mac_descriptor {
+       char name[16];
        u8 occupied;
        u8 mac_handle;
        u8 interface_id;
@@ -356,7 +360,6 @@ struct brcmf_fws_mac_descriptor {
        u8 seq[BRCMF_FWS_FIFO_COUNT];
        struct pktq psq;
        int transit_count;
-       int suppress_count;
        int suppr_transit_count;
        bool send_tim_signal;
        u8 traffic_pending_bmp;
@@ -383,12 +386,10 @@ enum brcmf_fws_hanger_item_state {
  * struct brcmf_fws_hanger_item - single entry for tx pending packet.
  *
  * @state: entry is either free or occupied.
- * @gen: generation.
  * @pkt: packet itself.
  */
 struct brcmf_fws_hanger_item {
        enum brcmf_fws_hanger_item_state state;
-       u8 gen;
        struct sk_buff *pkt;
 };
 
@@ -424,6 +425,7 @@ struct brcmf_fws_info {
        struct brcmf_fws_stats stats;
        struct brcmf_fws_hanger hanger;
        enum brcmf_fws_fcmode fcmode;
+       bool bcmc_credit_check;
        struct brcmf_fws_macdesc_table desc;
        struct workqueue_struct *fws_wq;
        struct work_struct fws_dequeue_work;
@@ -434,6 +436,8 @@ struct brcmf_fws_info {
        u32 fifo_credit_map;
        u32 fifo_delay_map;
        unsigned long borrow_defer_timestamp;
+       bool bus_flow_blocked;
+       bool creditmap_received;
 };
 
 /*
@@ -507,7 +511,6 @@ static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger)
 {
        int i;
 
-       brcmf_dbg(TRACE, "enter\n");
        memset(hanger, 0, sizeof(*hanger));
        for (i = 0; i < ARRAY_SIZE(hanger->items); i++)
                hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
@@ -517,7 +520,6 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
 {
        u32 i;
 
-       brcmf_dbg(TRACE, "enter\n");
        i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;
 
        while (i != h->slot_pos) {
@@ -533,14 +535,12 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
        h->failed_slotfind++;
        i = BRCMF_FWS_HANGER_MAXITEMS;
 done:
-       brcmf_dbg(TRACE, "exit: %d\n", i);
        return i;
 }
 
 static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,
-                                          struct sk_buff *pkt, u32 slot_id)
+                                   struct sk_buff *pkt, u32 slot_id)
 {
-       brcmf_dbg(TRACE, "enter\n");
        if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
                return -ENOENT;
 
@@ -560,7 +560,6 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
                                          u32 slot_id, struct sk_buff **pktout,
                                          bool remove_item)
 {
-       brcmf_dbg(TRACE, "enter\n");
        if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
                return -ENOENT;
 
@@ -574,23 +573,18 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
        if (remove_item) {
                h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
                h->items[slot_id].pkt = NULL;
-               h->items[slot_id].gen = 0xff;
                h->popped++;
        }
        return 0;
 }
 
 static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
-                                                  u32 slot_id, u8 gen)
+                                           u32 slot_id)
 {
-       brcmf_dbg(TRACE, "enter\n");
-
        if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
                return -ENOENT;
 
-       h->items[slot_id].gen = gen;
-
-       if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_INUSE) {
+       if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
                brcmf_err("entry not in use\n");
                return -EINVAL;
        }
@@ -599,25 +593,6 @@ static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
        return 0;
 }
 
-static int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger,
-                                             struct sk_buff *pkt, u32 slot_id,
-                                             int *gen)
-{
-       brcmf_dbg(TRACE, "enter\n");
-       *gen = 0xff;
-
-       if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
-               return -ENOENT;
-
-       if (hanger->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
-               brcmf_err("slot not in use\n");
-               return -EINVAL;
-       }
-
-       *gen = hanger->items[slot_id].gen;
-       return 0;
-}
-
 static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
                                     bool (*fn)(struct sk_buff *, void *),
                                     int ifidx)
@@ -627,7 +602,6 @@ static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
        int i;
        enum brcmf_fws_hanger_item_state s;
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        for (i = 0; i < ARRAY_SIZE(h->items); i++) {
                s = h->items[i].state;
                if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||
@@ -644,14 +618,28 @@ static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
        }
 }
 
-static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc,
-                                         u8 *addr, u8 ifidx)
+static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws,
+                                      struct brcmf_fws_mac_descriptor *desc)
+{
+       if (desc == &fws->desc.other)
+               strlcpy(desc->name, "MAC-OTHER", sizeof(desc->name));
+       else if (desc->mac_handle)
+               scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d",
+                         desc->mac_handle, desc->interface_id);
+       else
+               scnprintf(desc->name, sizeof(desc->name), "MACIF:%d",
+                         desc->interface_id);
+}
+
+static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc,
+                                  u8 *addr, u8 ifidx)
 {
        brcmf_dbg(TRACE,
                  "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx);
        desc->occupied = 1;
        desc->state = BRCMF_FWS_STATE_OPEN;
        desc->requested_credit = 0;
+       desc->requested_packet = 0;
        /* depending on use may need ifp->bssidx instead */
        desc->interface_id = ifidx;
        desc->ac_bitmap = 0xff; /* update this when handling APSD */
@@ -660,22 +648,22 @@ static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc,
 }
 
 static
-void brcmf_fws_clear_mac_descriptor(struct brcmf_fws_mac_descriptor *desc)
+void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc)
 {
        brcmf_dbg(TRACE,
                  "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id);
        desc->occupied = 0;
        desc->state = BRCMF_FWS_STATE_CLOSE;
        desc->requested_credit = 0;
+       desc->requested_packet = 0;
 }
 
 static struct brcmf_fws_mac_descriptor *
-brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea)
+brcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea)
 {
        struct brcmf_fws_mac_descriptor *entry;
        int i;
 
-       brcmf_dbg(TRACE, "enter: ea=%pM\n", ea);
        if (ea == NULL)
                return ERR_PTR(-EINVAL);
 
@@ -690,42 +678,33 @@ brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea)
 }
 
 static struct brcmf_fws_mac_descriptor*
-brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, struct brcmf_if *ifp,
-                       u8 *da)
+brcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da)
 {
        struct brcmf_fws_mac_descriptor *entry = &fws->desc.other;
        bool multicast;
-       enum nl80211_iftype iftype;
-
-       brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
 
        multicast = is_multicast_ether_addr(da);
-       iftype = brcmf_cfg80211_get_iftype(ifp);
 
-       /* Multicast destination and P2P clients get the interface entry.
-        * STA gets the interface entry if there is no exact match. For
-        * example, TDLS destinations have their own entry.
+       /* Multicast destination, STA and P2P clients get the interface entry.
+        * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
+        * have their own entry.
         */
-       entry = NULL;
-       if ((multicast || iftype == NL80211_IFTYPE_STATION ||
-            iftype == NL80211_IFTYPE_P2P_CLIENT) && ifp->fws_desc)
+       if (multicast && ifp->fws_desc) {
                entry = ifp->fws_desc;
-
-       if (entry != NULL && iftype != NL80211_IFTYPE_STATION)
                goto done;
+       }
 
-       entry = brcmf_fws_mac_descriptor_lookup(fws, da);
+       entry = brcmf_fws_macdesc_lookup(fws, da);
        if (IS_ERR(entry))
-               entry = &fws->desc.other;
+               entry = ifp->fws_desc;
 
 done:
-       brcmf_dbg(TRACE, "exit: entry=%p\n", entry);
        return entry;
 }
 
-static bool brcmf_fws_mac_desc_closed(struct brcmf_fws_info *fws,
-                                     struct brcmf_fws_mac_descriptor *entry,
-                                     int fifo)
+static bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws,
+                                    struct brcmf_fws_mac_descriptor *entry,
+                                    int fifo)
 {
        struct brcmf_fws_mac_descriptor *if_entry;
        bool closed;
@@ -748,15 +727,11 @@ static bool brcmf_fws_mac_desc_closed(struct brcmf_fws_info *fws,
        return closed || !(entry->ac_bitmap & BIT(fifo));
 }
 
-static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_info *fws,
-                                      struct brcmf_fws_mac_descriptor *entry,
-                                      int ifidx)
+static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws,
+                                     struct brcmf_fws_mac_descriptor *entry,
+                                     int ifidx)
 {
-       brcmf_dbg(TRACE, "enter: entry=(ea=%pM, ifid=%d), ifidx=%d\n",
-                 entry->ea, entry->interface_id, ifidx);
        if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {
-               brcmf_dbg(TRACE, "flush psq: ifidx=%d, qlen=%d\n",
-                         ifidx, entry->psq.len);
                brcmf_fws_psq_flush(fws, &entry->psq, ifidx);
                entry->occupied = !!(entry->psq.len);
        }
@@ -772,7 +747,6 @@ static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws,
        int prec;
        u32 hslot;
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        txq = brcmf_bus_gettxq(fws->drvr->bus_if);
        if (IS_ERR(txq)) {
                brcmf_dbg(TRACE, "no txq to clean up\n");
@@ -798,7 +772,6 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
        struct brcmf_fws_mac_descriptor *table;
        bool (*matchfn)(struct sk_buff *, void *) = NULL;
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        if (fws == NULL)
                return;
 
@@ -808,51 +781,121 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
        /* cleanup individual nodes */
        table = &fws->desc.nodes[0];
        for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++)
-               brcmf_fws_mac_desc_cleanup(fws, &table[i], ifidx);
+               brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx);
 
-       brcmf_fws_mac_desc_cleanup(fws, &fws->desc.other, ifidx);
+       brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx);
        brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx);
        brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);
 }
 
-static void brcmf_fws_tim_update(struct brcmf_fws_info *ctx,
-                                struct brcmf_fws_mac_descriptor *entry,
-                                int prec)
+static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
 {
-       brcmf_dbg(TRACE, "enter: ea=%pM\n", entry->ea);
-       if (entry->state == BRCMF_FWS_STATE_CLOSE) {
-               /* check delayedQ and suppressQ in one call using bitmap */
-               if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0)
-                       entry->traffic_pending_bmp =
-                               entry->traffic_pending_bmp & ~NBITVAL(prec);
-               else
-                       entry->traffic_pending_bmp =
-                               entry->traffic_pending_bmp | NBITVAL(prec);
+       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+       u8 *wlh;
+       u16 data_offset = 0;
+       u8 fillers;
+       __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
+
+       brcmf_dbg(TRACE, "enter: %s, idx=%d pkttag=0x%08X, hslot=%d\n",
+                 entry->name, brcmf_skb_if_flags_get_field(skb, INDEX),
+                 le32_to_cpu(pkttag), (le32_to_cpu(pkttag) >> 8) & 0xffff);
+       if (entry->send_tim_signal)
+               data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+
+       /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
+       data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
+       fillers = round_up(data_offset, 4) - data_offset;
+       data_offset += fillers;
+
+       skb_push(skb, data_offset);
+       wlh = skb->data;
+
+       wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
+       wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
+       memcpy(&wlh[2], &pkttag, sizeof(pkttag));
+       wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2;
+
+       if (entry->send_tim_signal) {
+               entry->send_tim_signal = 0;
+               wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
+               wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+               wlh[2] = entry->mac_handle;
+               wlh[3] = entry->traffic_pending_bmp;
+               brcmf_dbg(TRACE, "adding TIM info: handle %d bmp 0x%X\n",
+                         entry->mac_handle, entry->traffic_pending_bmp);
+               wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
+               entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
        }
-       /* request a TIM update to firmware at the next piggyback opportunity */
+       if (fillers)
+               memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
+
+       brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX),
+                           data_offset >> 2, skb);
+       return 0;
+}
+
+static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
+                                struct brcmf_fws_mac_descriptor *entry,
+                                int fifo, bool send_immediately)
+{
+       struct sk_buff *skb;
+       struct brcmf_bus *bus;
+       struct brcmf_skbuff_cb *skcb;
+       s32 err;
+       u32 len;
+
+       /* check delayedQ and suppressQ in one call using bitmap */
+       if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)
+               entry->traffic_pending_bmp &= ~NBITVAL(fifo);
+       else
+               entry->traffic_pending_bmp |= NBITVAL(fifo);
+
+       entry->send_tim_signal = false;
        if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp)
                entry->send_tim_signal = true;
+       if (send_immediately && entry->send_tim_signal &&
+           entry->state == BRCMF_FWS_STATE_CLOSE) {
+               /* create a dummy packet and sent that. The traffic          */
+               /* bitmap info will automatically be attached to that packet */
+               len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +
+                     BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +
+                     4 + fws->drvr->hdrlen;
+               skb = brcmu_pkt_buf_get_skb(len);
+               if (skb == NULL)
+                       return false;
+               skb_pull(skb, len);
+               skcb = brcmf_skbcb(skb);
+               skcb->mac = entry;
+               skcb->state = BRCMF_FWS_SKBSTATE_TIM;
+               bus = fws->drvr->bus_if;
+               err = brcmf_fws_hdrpush(fws, skb);
+               if (err == 0)
+                       err = brcmf_bus_txdata(bus, skb);
+               if (err)
+                       brcmu_pkt_buf_free_skb(skb);
+               return true;
+       }
+       return false;
 }
 
 static void
 brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
                             u8 if_id)
 {
-       struct brcmf_if *ifp = fws->drvr->iflist[if_id];
+       struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1];
 
        if (WARN_ON(!ifp))
                return;
 
-       brcmf_dbg(TRACE,
-                 "enter: bssidx=%d, ifidx=%d\n", ifp->bssidx, ifp->ifidx);
-
        if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
            pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER)
                brcmf_txflowblock_if(ifp,
                                     BRCMF_NETIF_STOP_REASON_FWS_FC, false);
        if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
-           pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER)
+           pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) {
+               fws->stats.fws_flow_block++;
                brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true);
+       }
        return;
 }
 
@@ -862,10 +905,26 @@ static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
        return 0;
 }
 
+/* using macro so sparse checking does not complain
+ * about locking imbalance.
+ */
+#define brcmf_fws_lock(drvr, flags)                            \
+do {                                                           \
+       flags = 0;                                              \
+       spin_lock_irqsave(&((drvr)->fws_spinlock), (flags));    \
+} while (0)
+
+/* using macro so sparse checking does not complain
+ * about locking imbalance.
+ */
+#define brcmf_fws_unlock(drvr, flags) \
+       spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags))
+
 static
 int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
 {
        struct brcmf_fws_mac_descriptor *entry, *existing;
+       ulong flags;
        u8 mac_handle;
        u8 ifidx;
        u8 *addr;
@@ -876,34 +935,44 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
 
        entry = &fws->desc.nodes[mac_handle & 0x1F];
        if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
-               brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx);
                if (entry->occupied) {
-                       brcmf_fws_mac_desc_cleanup(fws, entry, -1);
-                       brcmf_fws_clear_mac_descriptor(entry);
+                       brcmf_dbg(TRACE, "deleting %s mac %pM\n",
+                                 entry->name, addr);
+                       brcmf_fws_lock(fws->drvr, flags);
+                       brcmf_fws_macdesc_cleanup(fws, entry, -1);
+                       brcmf_fws_macdesc_deinit(entry);
+                       brcmf_fws_unlock(fws->drvr, flags);
                } else
                        fws->stats.mac_update_failed++;
                return 0;
        }
 
-       brcmf_dbg(TRACE,
-                 "add mac %pM handle %u idx %d\n", addr, mac_handle, ifidx);
-       existing = brcmf_fws_mac_descriptor_lookup(fws, addr);
+       existing = brcmf_fws_macdesc_lookup(fws, addr);
        if (IS_ERR(existing)) {
                if (!entry->occupied) {
+                       brcmf_fws_lock(fws->drvr, flags);
                        entry->mac_handle = mac_handle;
-                       brcmf_fws_init_mac_descriptor(entry, addr, ifidx);
+                       brcmf_fws_macdesc_init(entry, addr, ifidx);
+                       brcmf_fws_macdesc_set_name(fws, entry);
                        brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
                                        BRCMF_FWS_PSQ_LEN);
+                       brcmf_fws_unlock(fws->drvr, flags);
+                       brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
                } else {
                        fws->stats.mac_update_failed++;
                }
        } else {
                if (entry != existing) {
-                       brcmf_dbg(TRACE, "relocate mac\n");
+                       brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
+                       brcmf_fws_lock(fws->drvr, flags);
                        memcpy(entry, existing,
                               offsetof(struct brcmf_fws_mac_descriptor, psq));
                        entry->mac_handle = mac_handle;
-                       brcmf_fws_clear_mac_descriptor(existing);
+                       brcmf_fws_macdesc_deinit(existing);
+                       brcmf_fws_macdesc_set_name(fws, entry);
+                       brcmf_fws_unlock(fws->drvr, flags);
+                       brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,
+                                 addr);
                } else {
                        brcmf_dbg(TRACE, "use existing\n");
                        WARN_ON(entry->mac_handle != mac_handle);
@@ -917,8 +986,9 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
                                            u8 type, u8 *data)
 {
        struct brcmf_fws_mac_descriptor *entry;
+       ulong flags;
        u8 mac_handle;
-       int i;
+       int ret;
 
        mac_handle = data[0];
        entry = &fws->desc.nodes[mac_handle & 0x1F];
@@ -926,30 +996,35 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
                fws->stats.mac_ps_update_failed++;
                return -ESRCH;
        }
-
-       /* a state update should wipe old credits? */
+       brcmf_fws_lock(fws->drvr, flags);
+       /* a state update should wipe old credits */
        entry->requested_credit = 0;
+       entry->requested_packet = 0;
        if (type == BRCMF_FWS_TYPE_MAC_OPEN) {
                entry->state = BRCMF_FWS_STATE_OPEN;
-               return BRCMF_FWS_RET_OK_SCHEDULE;
+               ret = BRCMF_FWS_RET_OK_SCHEDULE;
        } else {
                entry->state = BRCMF_FWS_STATE_CLOSE;
-               for (i = BRCMF_FWS_FIFO_AC_BE; i < NL80211_NUM_ACS; i++)
-                       brcmf_fws_tim_update(fws, entry, i);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
+               ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
        }
-       return BRCMF_FWS_RET_OK_NOSCHEDULE;
+       brcmf_fws_unlock(fws->drvr, flags);
+       return ret;
 }
 
 static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
                                              u8 type, u8 *data)
 {
        struct brcmf_fws_mac_descriptor *entry;
+       ulong flags;
        u8 ifidx;
        int ret;
 
        ifidx = data[0];
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        if (ifidx >= BRCMF_MAX_IFS) {
                ret = -ERANGE;
                goto fail;
@@ -961,17 +1036,26 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
                goto fail;
        }
 
+       brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
+                 entry->name);
+       brcmf_fws_lock(fws->drvr, flags);
        switch (type) {
        case BRCMF_FWS_TYPE_INTERFACE_OPEN:
                entry->state = BRCMF_FWS_STATE_OPEN;
-               return BRCMF_FWS_RET_OK_SCHEDULE;
+               ret = BRCMF_FWS_RET_OK_SCHEDULE;
+               break;
        case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
                entry->state = BRCMF_FWS_STATE_CLOSE;
-               return BRCMF_FWS_RET_OK_NOSCHEDULE;
+               ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
+               break;
        default:
                ret = -EINVAL;
-               break;
+               brcmf_fws_unlock(fws->drvr, flags);
+               goto fail;
        }
+       brcmf_fws_unlock(fws->drvr, flags);
+       return ret;
+
 fail:
        fws->stats.if_update_failed++;
        return ret;
@@ -981,6 +1065,7 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
                                      u8 *data)
 {
        struct brcmf_fws_mac_descriptor *entry;
+       ulong flags;
 
        entry = &fws->desc.nodes[data[1] & 0x1F];
        if (!entry->occupied) {
@@ -991,15 +1076,51 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
                return -ESRCH;
        }
 
+       brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",
+                 brcmf_fws_get_tlv_name(type), type, entry->name,
+                 data[0], data[2]);
+       brcmf_fws_lock(fws->drvr, flags);
        if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
                entry->requested_credit = data[0];
        else
                entry->requested_packet = data[0];
 
        entry->ac_bitmap = data[2];
+       brcmf_fws_unlock(fws->drvr, flags);
        return BRCMF_FWS_RET_OK_SCHEDULE;
 }
 
+static void
+brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry,
+                                struct sk_buff *skb)
+{
+       if (entry->requested_credit > 0) {
+               entry->requested_credit--;
+               brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
+               brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1);
+               if (entry->state != BRCMF_FWS_STATE_CLOSE)
+                       brcmf_err("requested credit set while mac not closed!\n");
+       } else if (entry->requested_packet > 0) {
+               entry->requested_packet--;
+               brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
+               brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
+               if (entry->state != BRCMF_FWS_STATE_CLOSE)
+                       brcmf_err("requested packet set while mac not closed!\n");
+       } else {
+               brcmf_skb_if_flags_set_field(skb, REQUESTED, 0);
+               brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
+       }
+}
+
+static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb)
+{
+       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+
+       if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) &&
+           (entry->state == BRCMF_FWS_STATE_CLOSE))
+               entry->requested_credit++;
+}
+
 static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
                                     u8 fifo, u8 credits)
 {
@@ -1010,6 +1131,8 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
        if (!credits)
                return;
 
+       fws->fifo_credit_map |= 1 << fifo;
+
        if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
            (fws->credits_borrowed[0])) {
                for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;
@@ -1031,7 +1154,6 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
                }
        }
 
-       fws->fifo_credit_map |= 1 << fifo;
        fws->fifo_credit[fifo] += credits;
 }
 
@@ -1042,27 +1164,6 @@ static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
                queue_work(fws->fws_wq, &fws->fws_dequeue_work);
 }
 
-static void brcmf_skb_pick_up_credit(struct brcmf_fws_info *fws, int fifo,
-                                    struct sk_buff *p)
-{
-       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(p)->mac;
-
-       if (brcmf_skbcb(p)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
-               if (fws->fcmode != BRCMF_FWS_FCMODE_IMPLIED_CREDIT)
-                       return;
-               brcmf_fws_return_credits(fws, fifo, 1);
-       } else {
-               /*
-                * if this packet did not count against FIFO credit, it
-                * must have taken a requested_credit from the destination
-                * entry (for pspoll etc.)
-                */
-               if (!brcmf_skb_if_flags_get_field(p, REQUESTED))
-                       entry->requested_credit++;
-       }
-       brcmf_fws_schedule_deq(fws);
-}
-
 static int brcmf_fws_enq(struct brcmf_fws_info *fws,
                         enum brcmf_fws_skb_state state, int fifo,
                         struct sk_buff *p)
@@ -1078,7 +1179,7 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws,
                return -ENOENT;
        }
 
-       brcmf_dbg(TRACE, "enter: ea=%pM, qlen=%d\n", entry->ea, entry->psq.len);
+       brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p);
        if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
                prec += 1;
                qfull_stat = &fws->stats.supprq_full_error;
@@ -1095,14 +1196,12 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws,
 
        /* update the sk_buff state */
        brcmf_skbcb(p)->state = state;
-       if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
-               entry->suppress_count++;
 
        /*
         * A packet has been pushed so update traffic
         * availability bitmap, if applicable
         */
-       brcmf_fws_tim_update(fws, entry, fifo);
+       brcmf_fws_tim_update(fws, entry, fifo, true);
        brcmf_fws_flow_control_check(fws, &entry->psq,
                                     brcmf_skb_if_flags_get_field(p, INDEX));
        return 0;
@@ -1113,7 +1212,6 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
        struct brcmf_fws_mac_descriptor *table;
        struct brcmf_fws_mac_descriptor *entry;
        struct sk_buff *p;
-       int use_credit = 1;
        int num_nodes;
        int node_pos;
        int prec_out;
@@ -1127,7 +1225,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
        for (i = 0; i < num_nodes; i++) {
                entry = &table[(node_pos + i) % num_nodes];
                if (!entry->occupied ||
-                   brcmf_fws_mac_desc_closed(fws, entry, fifo))
+                   brcmf_fws_macdesc_closed(fws, entry, fifo))
                        continue;
 
                if (entry->suppressed)
@@ -1137,9 +1235,8 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
                p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
                if (p == NULL) {
                        if (entry->suppressed) {
-                               if (entry->suppr_transit_count >
-                                   entry->suppress_count)
-                                       return NULL;
+                               if (entry->suppr_transit_count)
+                                       continue;
                                entry->suppressed = false;
                                p = brcmu_pktq_mdeq(&entry->psq,
                                                    1 << (fifo * 2), &prec_out);
@@ -1148,26 +1245,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
                if  (p == NULL)
                        continue;
 
-               /* did the packet come from suppress sub-queue? */
-               if (entry->requested_credit > 0) {
-                       entry->requested_credit--;
-                       /*
-                        * if the packet was pulled out while destination is in
-                        * closed state but had a non-zero packets requested,
-                        * then this should not count against the FIFO credit.
-                        * That is due to the fact that the firmware will
-                        * most likely hold onto this packet until a suitable
-                        * time later to push it to the appropriate AC FIFO.
-                        */
-                       if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                               use_credit = 0;
-               } else if (entry->requested_packet > 0) {
-                       entry->requested_packet--;
-                       brcmf_skb_if_flags_set_field(p, REQUESTED, 1);
-                       if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                               use_credit = 0;
-               }
-               brcmf_skb_if_flags_set_field(p, CREDITCHECK, use_credit);
+               brcmf_fws_macdesc_use_req_credit(entry, p);
 
                /* move dequeue position to ensure fair round-robin */
                fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes;
@@ -1179,7 +1257,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
                 * A packet has been picked up, update traffic
                 * availability bitmap, if applicable
                 */
-               brcmf_fws_tim_update(fws, entry, fifo);
+               brcmf_fws_tim_update(fws, entry, fifo, false);
 
                /*
                 * decrement total enqueued fifo packets and
@@ -1192,7 +1270,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
        }
        p = NULL;
 done:
-       brcmf_dbg(TRACE, "exit: fifo %d skb %p\n", fifo, p);
+       brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p);
        return p;
 }
 
@@ -1202,22 +1280,26 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
        struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
        u32 hslot;
        int ret;
+       u8 ifidx;
 
        hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
 
        /* this packet was suppressed */
-       if (!entry->suppressed || entry->generation != genbit) {
+       if (!entry->suppressed) {
                entry->suppressed = true;
-               entry->suppress_count = brcmu_pktq_mlen(&entry->psq,
-                                                       1 << (fifo * 2 + 1));
                entry->suppr_transit_count = entry->transit_count;
+               brcmf_dbg(DATA, "suppress %s: transit %d\n",
+                         entry->name, entry->transit_count);
        }
 
        entry->generation = genbit;
 
-       ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
+       ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
+       if (ret == 0)
+               ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo,
+                                   skb);
        if (ret != 0) {
-               /* suppress q is full, drop this packet */
+               /* suppress q is full or hdrpull failed, drop this packet */
                brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
                                        true);
        } else {
@@ -1225,26 +1307,24 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
                 * Mark suppressed to avoid a double free during
                 * wlfc cleanup
                 */
-               brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot,
-                                                genbit);
-               entry->suppress_count++;
+               brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
        }
 
        return ret;
 }
 
 static int
-brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
+brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
                           u32 genbit)
 {
        u32 fifo;
        int ret;
        bool remove_from_hanger = true;
        struct sk_buff *skb;
+       struct brcmf_skbuff_cb *skcb;
        struct brcmf_fws_mac_descriptor *entry = NULL;
 
-       brcmf_dbg(TRACE, "status: flags=0x%X, hslot=%d\n",
-                 flags, hslot);
+       brcmf_dbg(DATA, "flags %d\n", flags);
 
        if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
                fws->stats.txs_discard++;
@@ -1256,6 +1336,8 @@ brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
                remove_from_hanger = false;
        } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED)
                fws->stats.txs_tossed++;
+       else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)
+               fws->stats.txs_host_tossed++;
        else
                brcmf_err("unexpected txstatus\n");
 
@@ -1266,32 +1348,42 @@ brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
                return ret;
        }
 
-       entry = brcmf_skbcb(skb)->mac;
+       skcb = brcmf_skbcb(skb);
+       entry = skcb->mac;
        if (WARN_ON(!entry)) {
                brcmu_pkt_buf_free_skb(skb);
                return -EINVAL;
        }
+       entry->transit_count--;
+       if (entry->suppressed && entry->suppr_transit_count)
+               entry->suppr_transit_count--;
+
+       brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags,
+                 skcb->htod);
 
        /* pick up the implicit credit from this packet */
        fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
-       brcmf_skb_pick_up_credit(fws, fifo, skb);
+       if ((fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT) ||
+           (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
+           (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) {
+               brcmf_fws_return_credits(fws, fifo, 1);
+               brcmf_fws_schedule_deq(fws);
+       }
+       brcmf_fws_macdesc_return_req_credit(skb);
 
        if (!remove_from_hanger)
                ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit);
 
-       if (remove_from_hanger || ret) {
-               entry->transit_count--;
-               if (entry->suppressed)
-                       entry->suppr_transit_count--;
-
+       if (remove_from_hanger || ret)
                brcmf_txfinalize(fws->drvr, skb, true);
-       }
+
        return 0;
 }
 
 static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
                                             u8 *data)
 {
+       ulong flags;
        int i;
 
        if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
@@ -1299,17 +1391,20 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
                return BRCMF_FWS_RET_OK_NOSCHEDULE;
        }
 
-       brcmf_dbg(TRACE, "enter: data %pM\n", data);
+       brcmf_dbg(DATA, "enter: data %pM\n", data);
+       brcmf_fws_lock(fws->drvr, flags);
        for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
                brcmf_fws_return_credits(fws, i, data[i]);
 
-       brcmf_dbg(INFO, "map: credit %x delay %x\n", fws->fifo_credit_map,
+       brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
                  fws->fifo_delay_map);
+       brcmf_fws_unlock(fws->drvr, flags);
        return BRCMF_FWS_RET_OK_SCHEDULE;
 }
 
 static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
 {
+       ulong lflags;
        __le32 status_le;
        u32 status;
        u32 hslot;
@@ -1323,7 +1418,10 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
        hslot = brcmf_txstatus_get_field(status, HSLOT);
        genbit = brcmf_txstatus_get_field(status, GENERATION);
 
-       return brcmf_fws_txstatus_process(fws, flags, hslot, genbit);
+       brcmf_fws_lock(fws->drvr, lflags);
+       brcmf_fws_txs_process(fws, flags, hslot, genbit);
+       brcmf_fws_unlock(fws->drvr, lflags);
+       return BRCMF_FWS_RET_OK_NOSCHEDULE;
 }
 
 static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
@@ -1331,26 +1429,11 @@ static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
        __le32 timestamp;
 
        memcpy(&timestamp, &data[2], sizeof(timestamp));
-       brcmf_dbg(INFO, "received: seq %d, timestamp %d\n", data[1],
+       brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1],
                  le32_to_cpu(timestamp));
        return 0;
 }
 
-/* using macro so sparse checking does not complain
- * about locking imbalance.
- */
-#define brcmf_fws_lock(drvr, flags)                            \
-do {                                                           \
-       flags = 0;                                              \
-       spin_lock_irqsave(&((drvr)->fws_spinlock), (flags));    \
-} while (0)
-
-/* using macro so sparse checking does not complain
- * about locking imbalance.
- */
-#define brcmf_fws_unlock(drvr, flags) \
-       spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags))
-
 static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
                                       const struct brcmf_event_msg *e,
                                       void *data)
@@ -1364,6 +1447,10 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
                brcmf_err("event payload too small (%d)\n", e->datalen);
                return -EINVAL;
        }
+       if (fws->creditmap_received)
+               return 0;
+
+       fws->creditmap_received = true;
 
        brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
        brcmf_fws_lock(ifp->drvr, flags);
@@ -1379,11 +1466,24 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
        return 0;
 }
 
+static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
+                                               const struct brcmf_event_msg *e,
+                                               void *data)
+{
+       struct brcmf_fws_info *fws = ifp->drvr->fws;
+       ulong flags;
+
+       brcmf_fws_lock(ifp->drvr, flags);
+       if (fws)
+               fws->bcmc_credit_check = true;
+       brcmf_fws_unlock(ifp->drvr, flags);
+       return 0;
+}
+
 int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
                      struct sk_buff *skb)
 {
        struct brcmf_fws_info *fws = drvr->fws;
-       ulong flags;
        u8 *signal_data;
        s16 data_len;
        u8 type;
@@ -1392,7 +1492,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
        s32 status;
        s32 err;
 
-       brcmf_dbg(TRACE, "enter: ifidx %d, skblen %u, sig %d\n",
+       brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
                  ifidx, skb->len, signal_len);
 
        WARN_ON(signal_len > skb->len);
@@ -1403,9 +1503,6 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
                return 0;
        }
 
-       /* lock during tlv parsing */
-       brcmf_fws_lock(drvr, flags);
-
        fws->stats.header_pulls++;
        data_len = signal_len;
        signal_data = skb->data;
@@ -1426,14 +1523,15 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
                len = signal_data[1];
                data = signal_data + 2;
 
-               brcmf_dbg(INFO, "tlv type=%d (%s), len=%d, data[0]=%d\n", type,
-                         brcmf_fws_get_tlv_name(type), len, *data);
+               brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n",
+                         brcmf_fws_get_tlv_name(type), type, len,
+                         brcmf_fws_get_tlv_len(fws, type));
 
                /* abort parsing when length invalid */
                if (data_len < len + 2)
                        break;
 
-               if (len != brcmf_fws_get_tlv_len(fws, type))
+               if (len < brcmf_fws_get_tlv_len(fws, type))
                        break;
 
                err = BRCMF_FWS_RET_OK_NOSCHEDULE;
@@ -1498,203 +1596,74 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
        if (skb->len == 0)
                fws->stats.header_only_pkt++;
 
-       brcmf_fws_unlock(drvr, flags);
-       return 0;
-}
-
-static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
-{
-       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
-       u8 *wlh;
-       u16 data_offset = 0;
-       u8 fillers;
-       __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
-
-       brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u, pkttag=0x%08X\n",
-                 entry->ea, entry->interface_id, le32_to_cpu(pkttag));
-       if (entry->send_tim_signal)
-               data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
-
-       /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
-       data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
-       fillers = round_up(data_offset, 4) - data_offset;
-       data_offset += fillers;
-
-       skb_push(skb, data_offset);
-       wlh = skb->data;
-
-       wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
-       wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
-       memcpy(&wlh[2], &pkttag, sizeof(pkttag));
-       wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2;
-
-       if (entry->send_tim_signal) {
-               entry->send_tim_signal = 0;
-               wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
-               wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
-               wlh[2] = entry->mac_handle;
-               wlh[3] = entry->traffic_pending_bmp;
-               wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
-               entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
-       }
-       if (fillers)
-               memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
-
-       brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX),
-                           data_offset >> 2, skb);
        return 0;
 }
 
-static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
+static void brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
                                   struct sk_buff *p)
 {
        struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
        struct brcmf_fws_mac_descriptor *entry = skcb->mac;
-       int rc = 0;
-       bool header_needed;
-       int hslot = BRCMF_FWS_HANGER_MAXITEMS;
-       u8 free_ctr;
-       u8 ifidx;
        u8 flags;
 
-       header_needed = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED;
-
-       if (header_needed) {
-               /* obtaining free slot may fail, but that will be caught
-                * by the hanger push. This assures the packet has a BDC
-                * header upon return.
-                */
-               hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);
-               free_ctr = entry->seq[fifo];
-               brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
-               brcmf_skb_htod_tag_set_field(p, FREERUN, free_ctr);
-               brcmf_skb_htod_tag_set_field(p, GENERATION, 1);
-               entry->transit_count++;
-       }
        brcmf_skb_if_flags_set_field(p, TRANSMIT, 1);
-       brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
-
+       brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation);
        flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
-       if (!(skcb->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) {
+       if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
                /*
-               Indicate that this packet is being sent in response to an
-               explicit request from the firmware side.
-               */
+                * Indicate that this packet is being sent in response to an
+                * explicit request from the firmware side.
+                */
                flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
        }
        brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
-       if (header_needed) {
-               brcmf_fws_hdrpush(fws, p);
-               rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
-               if (rc)
-                       brcmf_err("hanger push failed: rc=%d\n", rc);
-       } else {
-               int gen;
-
-               /* remove old header */
-               rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, p);
-               if (rc == 0) {
-                       hslot = brcmf_skb_htod_tag_get_field(p, HSLOT);
-                       brcmf_fws_hanger_get_genbit(&fws->hanger, p,
-                                                   hslot, &gen);
-                       brcmf_skb_htod_tag_set_field(p, GENERATION, gen);
-
-                       /* push new header */
-                       brcmf_fws_hdrpush(fws, p);
-               }
-       }
-
-       return rc;
+       brcmf_fws_hdrpush(fws, p);
 }
 
-static void
-brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
+static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
+                                  struct sk_buff *skb, int fifo)
 {
-       /*
-       put the packet back to the head of queue
-
-       - suppressed packet goes back to suppress sub-queue
-       - pull out the header, if new or delayed packet
-
-       Note: hslot is used only when header removal is done.
-       */
        struct brcmf_fws_mac_descriptor *entry;
-       enum brcmf_fws_skb_state state;
        struct sk_buff *pktout;
+       int qidx, hslot;
        int rc = 0;
-       int fifo;
-       int hslot;
-       u8 ifidx;
 
-       fifo = brcmf_skb_if_flags_get_field(skb, FIFO);
-       state = brcmf_skbcb(skb)->state;
        entry = brcmf_skbcb(skb)->mac;
-
-       if (entry != NULL) {
-               if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
-                       /* wl-header is saved for suppressed packets */
-                       pktout = brcmu_pktq_penq_head(&entry->psq, 2 * fifo + 1,
-                                                     skb);
-                       if (pktout == NULL) {
-                               brcmf_err("suppress queue full\n");
-                               rc = -ENOSPC;
-                       }
-               } else {
-                       hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
-
-                       /* remove header first */
-                       rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
-                       if (rc) {
-                               brcmf_err("header removal failed\n");
-                               /* free the hanger slot */
-                               brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
-                                                       &pktout, true);
-                               rc = -EINVAL;
-                               goto fail;
-                       }
-
-                       /* delay-q packets are going to delay-q */
-                       pktout = brcmu_pktq_penq_head(&entry->psq,
-                                                     2 * fifo, skb);
-                       if (pktout == NULL) {
-                               brcmf_err("delay queue full\n");
-                               rc = -ENOSPC;
-                       }
-
-                       /* free the hanger slot */
-                       brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &pktout,
-                                               true);
-
-                       /* decrement sequence count */
-                       entry->seq[fifo]--;
+       if (entry->occupied) {
+               qidx = 2 * fifo;
+               if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
+                       qidx++;
+
+               pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb);
+               if (pktout == NULL) {
+                       brcmf_err("%s queue %d full\n", entry->name, qidx);
+                       rc = -ENOSPC;
                }
-               /*
-               if this packet did not count against FIFO credit, it must have
-               taken a requested_credit from the firmware (for pspoll etc.)
-               */
-               if (!(brcmf_skbcb(skb)->if_flags &
-                     BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK))
-                       entry->requested_credit++;
        } else {
-               brcmf_err("no mac entry linked\n");
+               brcmf_err("%s entry removed\n", entry->name);
                rc = -ENOENT;
        }
 
-
-fail:
        if (rc) {
-               brcmf_txfinalize(fws->drvr, skb, false);
                fws->stats.rollback_failed++;
-       } else
+               hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+               brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
+                                     hslot, 0);
+       } else {
                fws->stats.rollback_success++;
+               brcmf_fws_return_credits(fws, fifo, 1);
+               brcmf_fws_macdesc_return_req_credit(skb);
+       }
 }
 
 static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
 {
        int lender_ac;
 
-       if (time_after(fws->borrow_defer_timestamp, jiffies))
+       if (time_after(fws->borrow_defer_timestamp, jiffies)) {
+               fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
                return -ENAVAIL;
+       }
 
        for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) {
                if (fws->fifo_credit[lender_ac]) {
@@ -1702,66 +1671,15 @@ static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
                        fws->fifo_credit[lender_ac]--;
                        if (fws->fifo_credit[lender_ac] == 0)
                                fws->fifo_credit_map &= ~(1 << lender_ac);
-                       brcmf_dbg(TRACE, "borrow credit from: %d\n", lender_ac);
+                       fws->fifo_credit_map |= (1 << BRCMF_FWS_FIFO_AC_BE);
+                       brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac);
                        return 0;
                }
        }
+       fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
        return -ENAVAIL;
 }
 
-static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo,
-                                   struct sk_buff *skb)
-{
-       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
-       int *credit = &fws->fifo_credit[fifo];
-       int use_credit = 1;
-
-       brcmf_dbg(TRACE, "enter: ac=%d, credits=%d\n", fifo, *credit);
-
-       if (entry->requested_credit > 0) {
-               /*
-                * if the packet was pulled out while destination is in
-                * closed state but had a non-zero packets requested,
-                * then this should not count against the FIFO credit.
-                * That is due to the fact that the firmware will
-                * most likely hold onto this packet until a suitable
-                * time later to push it to the appropriate AC FIFO.
-                */
-               entry->requested_credit--;
-               if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                       use_credit = 0;
-       } else if (entry->requested_packet > 0) {
-               entry->requested_packet--;
-               brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
-               if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                       use_credit = 0;
-       }
-       brcmf_skb_if_flags_set_field(skb, CREDITCHECK, use_credit);
-       if (!use_credit) {
-               brcmf_dbg(TRACE, "exit: no creditcheck set\n");
-               return 0;
-       }
-
-       if (fifo != BRCMF_FWS_FIFO_AC_BE)
-               fws->borrow_defer_timestamp = jiffies +
-                                             BRCMF_FWS_BORROW_DEFER_PERIOD;
-
-       if (!(*credit)) {
-               /* Try to borrow a credit from other queue */
-               if (fifo == BRCMF_FWS_FIFO_AC_BE &&
-                   brcmf_fws_borrow_credit(fws) == 0)
-                       return 0;
-
-               brcmf_dbg(TRACE, "exit: ac=%d, credits depleted\n", fifo);
-               return -ENAVAIL;
-       }
-       (*credit)--;
-       if (!(*credit))
-               fws->fifo_credit_map &= ~(1 << fifo);
-       brcmf_dbg(TRACE, "exit: ac=%d, credits=%d\n", fifo, *credit);
-       return 0;
-}
-
 static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
                                struct sk_buff *skb)
 {
@@ -1769,32 +1687,51 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
        struct brcmf_fws_mac_descriptor *entry;
        struct brcmf_bus *bus = fws->drvr->bus_if;
        int rc;
+       u8 ifidx;
 
        entry = skcb->mac;
        if (IS_ERR(entry))
                return PTR_ERR(entry);
 
-       rc = brcmf_fws_precommit_skb(fws, fifo, skb);
+       brcmf_fws_precommit_skb(fws, fifo, skb);
+       rc = brcmf_bus_txdata(bus, skb);
+       brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,
+                 skcb->if_flags, skcb->htod, rc);
        if (rc < 0) {
-               fws->stats.generic_error++;
+               brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
                goto rollback;
        }
 
-       rc = brcmf_bus_txdata(bus, skb);
-       if (rc < 0)
-               goto rollback;
-
-       entry->seq[fifo]++;
+       entry->transit_count++;
+       if (entry->suppressed)
+               entry->suppr_transit_count++;
        fws->stats.pkt2bus++;
-       if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
-               fws->stats.send_pkts[fifo]++;
-               fws->stats.fifo_credits_sent[fifo]++;
-       }
+       fws->stats.send_pkts[fifo]++;
+       if (brcmf_skb_if_flags_get_field(skb, REQUESTED))
+               fws->stats.requested_sent[fifo]++;
 
        return rc;
 
 rollback:
-       brcmf_fws_rollback_toq(fws, skb);
+       brcmf_fws_rollback_toq(fws, skb, fifo);
+       return rc;
+}
+
+static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p,
+                                 int fifo)
+{
+       struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
+       int rc, hslot;
+
+       hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger);
+       brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
+       brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]);
+       brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
+       rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
+       if (!rc)
+               skcb->mac->seq[fifo]++;
+       else
+               fws->stats.generic_error++;
        return rc;
 }
 
@@ -1826,29 +1763,25 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
 
        /* set control buffer information */
        skcb->if_flags = 0;
-       skcb->mac = brcmf_fws_find_mac_desc(fws, ifp, eh->h_dest);
        skcb->state = BRCMF_FWS_SKBSTATE_NEW;
        brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
        if (!multicast)
                fifo = brcmf_fws_prio2fifo[skb->priority];
-       brcmf_skb_if_flags_set_field(skb, FIFO, fifo);
-
-       brcmf_dbg(TRACE, "ea=%pM, multi=%d, fifo=%d\n", eh->h_dest,
-                 multicast, fifo);
 
        brcmf_fws_lock(drvr, flags);
-       if (skcb->mac->suppressed ||
-           brcmf_fws_mac_desc_closed(fws, skcb->mac, fifo) ||
-           brcmu_pktq_mlen(&skcb->mac->psq, 3 << (fifo * 2)) ||
-           (!multicast &&
-            brcmf_fws_consume_credit(fws, fifo, skb) < 0)) {
-               /* enqueue the packet in delayQ */
-               drvr->fws->fifo_delay_map |= 1 << fifo;
+       if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
+               fws->borrow_defer_timestamp = jiffies +
+                                             BRCMF_FWS_BORROW_DEFER_PERIOD;
+
+       skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest);
+       brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name,
+                 eh->h_dest, multicast, fifo);
+       if (!brcmf_fws_assign_htod(fws, skb, fifo)) {
                brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
+               brcmf_fws_schedule_deq(fws);
        } else {
-               if (brcmf_fws_commit_skb(fws, fifo, skb))
-                       if (!multicast)
-                               brcmf_skb_pick_up_credit(fws, fifo, skb);
+               brcmf_err("drop skb: no hanger slot\n");
+               brcmu_pkt_buf_free_skb(skb);
        }
        brcmf_fws_unlock(drvr, flags);
        return 0;
@@ -1862,7 +1795,7 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp)
        if (!entry)
                return;
 
-       brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx);
+       brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
 }
 
 void brcmf_fws_add_interface(struct brcmf_if *ifp)
@@ -1870,16 +1803,16 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp)
        struct brcmf_fws_info *fws = ifp->drvr->fws;
        struct brcmf_fws_mac_descriptor *entry;
 
-       brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n",
-                 ifp->bssidx, ifp->mac_addr);
        if (!ifp->ndev || !ifp->drvr->fw_signals)
                return;
 
        entry = &fws->desc.iface[ifp->ifidx];
        ifp->fws_desc = entry;
-       brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx);
+       brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
+       brcmf_fws_macdesc_set_name(fws, entry);
        brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
                        BRCMF_FWS_PSQ_LEN);
+       brcmf_dbg(TRACE, "added %s\n", entry->name);
 }
 
 void brcmf_fws_del_interface(struct brcmf_if *ifp)
@@ -1887,13 +1820,13 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp)
        struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
        ulong flags;
 
-       brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
        if (!entry)
                return;
 
        brcmf_fws_lock(ifp->drvr, flags);
        ifp->fws_desc = NULL;
-       brcmf_fws_clear_mac_descriptor(entry);
+       brcmf_dbg(TRACE, "deleting %s\n", entry->name);
+       brcmf_fws_macdesc_deinit(entry);
        brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx);
        brcmf_fws_unlock(ifp->drvr, flags);
 }
@@ -1904,39 +1837,37 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
        struct sk_buff *skb;
        ulong flags;
        int fifo;
-       int credit;
 
        fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
 
-       brcmf_dbg(TRACE, "enter: fws=%p\n", fws);
        brcmf_fws_lock(fws->drvr, flags);
-       for (fifo = NL80211_NUM_ACS; fifo >= 0; fifo--) {
-               brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo,
-                         fws->fifo_credit[fifo]);
-               for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) {
+       for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
+            fifo--) {
+               while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
+                      (fifo == BRCMF_FWS_FIFO_BCMC))) {
                        skb = brcmf_fws_deq(fws, fifo);
-                       if (!skb || brcmf_fws_commit_skb(fws, fifo, skb))
+                       if (!skb)
+                               break;
+                       fws->fifo_credit[fifo]--;
+                       if (brcmf_fws_commit_skb(fws, fifo, skb))
+                               break;
+                       if (fws->bus_flow_blocked)
                                break;
-                       if (brcmf_skbcb(skb)->if_flags &
-                           BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)
-                               credit++;
                }
                if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
-                   (credit == fws->fifo_credit[fifo])) {
-                       fws->fifo_credit[fifo] -= credit;
+                   (fws->fifo_credit[fifo] == 0) &&
+                   (!fws->bus_flow_blocked)) {
                        while (brcmf_fws_borrow_credit(fws) == 0) {
                                skb = brcmf_fws_deq(fws, fifo);
                                if (!skb) {
                                        brcmf_fws_return_credits(fws, fifo, 1);
                                        break;
                                }
-                               if (brcmf_fws_commit_skb(fws, fifo, skb)) {
-                                       brcmf_fws_return_credits(fws, fifo, 1);
+                               if (brcmf_fws_commit_skb(fws, fifo, skb))
+                                       break;
+                               if (fws->bus_flow_blocked)
                                        break;
-                               }
                        }
-               } else {
-                       fws->fifo_credit[fifo] -= credit;
                }
        }
        brcmf_fws_unlock(fws->drvr, flags);
@@ -1982,6 +1913,13 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
                brcmf_err("register credit map handler failed\n");
                goto fail;
        }
+       rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT,
+                                brcmf_fws_notify_bcmc_credit_support);
+       if (rc < 0) {
+               brcmf_err("register bcmc credit handler failed\n");
+               brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
+               goto fail;
+       }
 
        /* setting the iovar may fail if feature is unsupported
         * so leave the rc as is so driver initialization can
@@ -1993,19 +1931,20 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
        }
 
        brcmf_fws_hanger_init(&drvr->fws->hanger);
-       brcmf_fws_init_mac_descriptor(&drvr->fws->desc.other, NULL, 0);
+       brcmf_fws_macdesc_init(&drvr->fws->desc.other, NULL, 0);
+       brcmf_fws_macdesc_set_name(drvr->fws, &drvr->fws->desc.other);
        brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
                        BRCMF_FWS_PSQ_LEN);
 
        /* create debugfs file for statistics */
        brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats);
 
-       /* TODO: remove upon feature delivery */
-       brcmf_err("%s bdcv2 tlv signaling [%x]\n",
+       brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
                  drvr->fw_signals ? "enabled" : "disabled", tlv);
        return 0;
 
 fail_event:
+       brcmf_fweh_unregister(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT);
        brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
 fail:
        brcmf_fws_deinit(drvr);
@@ -2043,25 +1982,31 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
        if (!fws)
                return false;
 
-       brcmf_dbg(TRACE, "enter: mode=%d\n", fws->fcmode);
        return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
 }
 
 void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
 {
        ulong flags;
+       u32 hslot;
 
-       brcmf_fws_lock(fws->drvr, flags);
-       brcmf_fws_txstatus_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED,
-                                  brcmf_skb_htod_tag_get_field(skb, HSLOT), 0);
-       /* the packet never reached firmware so reclaim credit */
-       if (fws->fcmode == BRCMF_FWS_FCMODE_EXPLICIT_CREDIT &&
-           brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
-               brcmf_fws_return_credits(fws,
-                                        brcmf_skb_htod_tag_get_field(skb,
-                                                                     FIFO),
-                                        1);
-               brcmf_fws_schedule_deq(fws);
+       if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
+               brcmu_pkt_buf_free_skb(skb);
+               return;
        }
+       brcmf_fws_lock(fws->drvr, flags);
+       hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+       brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0);
        brcmf_fws_unlock(fws->drvr, flags);
 }
+
+void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
+{
+       struct brcmf_fws_info *fws = drvr->fws;
+
+       fws->bus_flow_blocked = flow_blocked;
+       if (!flow_blocked)
+               brcmf_fws_schedule_deq(fws);
+       else
+               fws->stats.bus_flow_block++;
+}
index fbe483d..9fc8609 100644 (file)
@@ -29,5 +29,6 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp);
 void brcmf_fws_add_interface(struct brcmf_if *ifp);
 void brcmf_fws_del_interface(struct brcmf_if *ifp);
 void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb);
+void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked);
 
 #endif /* FWSIGNAL_H_ */
index 7c1b633..09786a5 100644 (file)
@@ -170,7 +170,6 @@ struct brcmf_sdio_dev {
        atomic_t suspend;               /* suspend flag */
        wait_queue_head_t request_byte_wait;
        wait_queue_head_t request_word_wait;
-       wait_queue_head_t request_chain_wait;
        wait_queue_head_t request_buffer_wait;
        struct device *dev;
        struct brcmf_bus *bus_if;
@@ -230,8 +229,6 @@ brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
 #define SDIO_REQ_4BYTE 0x1
 /* Fixed address (FIFO) (vs. incrementing address) */
 #define SDIO_REQ_FIXED 0x2
-/* Async request (vs. sync request) */
-#define SDIO_REQ_ASYNC 0x4
 
 /* Read/write to memory block (F1, no FIFO) via CMD53 (sync only).
  *   rw:       read or write (0/1)
@@ -252,9 +249,6 @@ extern int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
 extern int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
 extern int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev);
 
-extern int brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev,
-                                         u32 address);
-
 /* attach, return handler on success, NULL if failed.
  *  The handler shall be provided by all subsequent calls. No local cache
  *  cfghdl points to the starting address of pci device mapped memory
@@ -272,16 +266,6 @@ brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev,
                         uint rw, uint fnc, uint addr,
                         u32 *word, uint nbyte);
 
-/* read or write any buffer using cmd53 */
-extern int
-brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev,
-                          uint fix_inc, uint rw, uint fnc_num, u32 addr,
-                          struct sk_buff *pkt);
-extern int
-brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc,
-                         uint write, uint func, uint addr,
-                         struct sk_buff_head *pktq);
-
 /* Watchdog timer interface for pm ops */
 extern void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev,
                                    bool enable);
@@ -291,4 +275,8 @@ extern void brcmf_sdbrcm_disconnect(void *ptr);
 extern void brcmf_sdbrcm_isr(void *arg);
 
 extern void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick);
+
+extern void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev,
+                                wait_queue_head_t *wq);
+extern bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev);
 #endif                         /* _BRCM_SDH_H_ */
index 9df1f7a..bc29171 100644 (file)
@@ -87,6 +87,27 @@ TRACE_EVENT(brcmf_hexdump,
        TP_printk("hexdump [length=%lu]", __entry->len)
 );
 
+TRACE_EVENT(brcmf_bdchdr,
+       TP_PROTO(void *data),
+       TP_ARGS(data),
+       TP_STRUCT__entry(
+               __field(u8, flags)
+               __field(u8, prio)
+               __field(u8, flags2)
+               __field(u32, siglen)
+               __dynamic_array(u8, signal, *((u8 *)data + 3) * 4)
+       ),
+       TP_fast_assign(
+               __entry->flags = *(u8 *)data;
+               __entry->prio = *((u8 *)data + 1);
+               __entry->flags2 = *((u8 *)data + 2);
+               __entry->siglen = *((u8 *)data + 3) * 4;
+               memcpy(__get_dynamic_array(signal),
+                      (u8 *)data + 4, __entry->siglen);
+       ),
+       TP_printk("bdc: prio=%d siglen=%d", __entry->prio, __entry->siglen)
+);
+
 #ifdef CONFIG_BRCM_TRACING
 
 #undef TRACE_INCLUDE_PATH
index 01aed7a..322cadc 100644 (file)
@@ -82,6 +82,7 @@ struct brcmf_usbdev_info {
        int tx_high_watermark;
        int tx_freecount;
        bool tx_flowblock;
+       spinlock_t tx_flowblock_lock;
 
        struct brcmf_usbreq *tx_reqs;
        struct brcmf_usbreq *rx_reqs;
@@ -411,6 +412,7 @@ static void brcmf_usb_tx_complete(struct urb *urb)
 {
        struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context;
        struct brcmf_usbdev_info *devinfo = req->devinfo;
+       unsigned long flags;
 
        brcmf_dbg(USB, "Enter, urb->status=%d, skb=%p\n", urb->status,
                  req->skb);
@@ -419,11 +421,13 @@ static void brcmf_usb_tx_complete(struct urb *urb)
        brcmf_txcomplete(devinfo->dev, req->skb, urb->status == 0);
        req->skb = NULL;
        brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount);
+       spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags);
        if (devinfo->tx_freecount > devinfo->tx_high_watermark &&
                devinfo->tx_flowblock) {
                brcmf_txflowblock(devinfo->dev, false);
                devinfo->tx_flowblock = false;
        }
+       spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
 }
 
 static void brcmf_usb_rx_complete(struct urb *urb)
@@ -568,6 +572,7 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
        struct brcmf_usbreq  *req;
        int ret;
+       unsigned long flags;
 
        brcmf_dbg(USB, "Enter, skb=%p\n", skb);
        if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
@@ -599,11 +604,13 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
                goto fail;
        }
 
+       spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags);
        if (devinfo->tx_freecount < devinfo->tx_low_watermark &&
            !devinfo->tx_flowblock) {
                brcmf_txflowblock(dev, true);
                devinfo->tx_flowblock = true;
        }
+       spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
        return 0;
 
 fail:
@@ -1164,6 +1171,7 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
 
        /* Initialize the spinlocks */
        spin_lock_init(&devinfo->qlock);
+       spin_lock_init(&devinfo->tx_flowblock_lock);
 
        INIT_LIST_HEAD(&devinfo->rx_freeq);
        INIT_LIST_HEAD(&devinfo->rx_postq);
index 301e572..277b37a 100644 (file)
@@ -3982,6 +3982,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        struct brcmf_fil_af_params_le *af_params;
        bool ack;
        s32 chan_nr;
+       u32 freq;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -3994,6 +3995,8 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                return -EPERM;
        }
 
+       vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
        if (ieee80211_is_probe_resp(mgmt->frame_control)) {
                /* Right now the only reason to get a probe response */
                /* is for p2p listen response or for p2p GO from     */
@@ -4009,7 +4012,6 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                ie_offset =  DOT11_MGMT_HDR_LEN +
                             DOT11_BCN_PRB_FIXED_LEN;
                ie_len = len - ie_offset;
-               vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
                if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
                        vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
                err = brcmf_vif_set_mgmt_ie(vif,
@@ -4033,16 +4035,22 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
                /* Add the length exepted for 802.11 header  */
                action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
-               /* Add the channel */
-               chan_nr = ieee80211_frequency_to_channel(chan->center_freq);
+               /* Add the channel. Use the one specified as parameter if any or
+                * the current one (got from the firmware) otherwise
+                */
+               if (chan)
+                       freq = chan->center_freq;
+               else
+                       brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
+                                             &freq);
+               chan_nr = ieee80211_frequency_to_channel(freq);
                af_params->channel = cpu_to_le32(chan_nr);
 
                memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
                       le16_to_cpu(action_frame->len));
 
                brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
-                         *cookie, le16_to_cpu(action_frame->len),
-                         chan->center_freq);
+                         *cookie, le16_to_cpu(action_frame->len), freq);
 
                ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
                                                  af_params);
index 1585cc5..bd98285 100644 (file)
@@ -900,7 +900,7 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
                if (supr_status) {
                        update_rate = false;
                        if (supr_status == TX_STATUS_SUPR_BADCH) {
-                               brcms_err(wlc->hw->d11core,
+                               brcms_dbg_ht(wlc->hw->d11core,
                                          "%s: Pkt tx suppressed, illegal channel possibly %d\n",
                                          __func__, CHSPEC_CHANNEL(
                                          wlc->default_bss->chanspec));
diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/cw1200/Kconfig
new file mode 100644 (file)
index 0000000..0880742
--- /dev/null
@@ -0,0 +1,30 @@
+config CW1200
+       tristate "CW1200 WLAN support"
+       depends on MAC80211 && CFG80211
+       help
+         This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets.
+         This option just enables the driver core, see below for
+         specific bus support.
+
+if CW1200
+
+config CW1200_WLAN_SDIO
+       tristate "Support SDIO platforms"
+       depends on CW1200 && MMC
+       help
+         Enable support for the CW1200 connected via an SDIO bus.
+         By default this driver only supports the Sagrad SG901-1091/1098 EVK
+         and similar designs that utilize a hardware reset circuit. To
+         support different CW1200 SDIO designs you will need to override
+         the default platform data by calling cw1200_sdio_set_platform_data()
+         in your board setup file.
+
+config CW1200_WLAN_SPI
+       tristate "Support SPI platforms"
+       depends on CW1200 && SPI
+       help
+         Enables support for the CW1200 connected via a SPI bus.  You will
+         need to add appropriate platform data glue in your board setup
+         file.
+
+endif
diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/cw1200/Makefile
new file mode 100644 (file)
index 0000000..b086aac
--- /dev/null
@@ -0,0 +1,21 @@
+cw1200_core-y := \
+               fwio.o \
+               txrx.o \
+               main.o \
+               queue.o \
+               hwio.o \
+               bh.o \
+               wsm.o \
+               sta.o \
+               scan.o \
+               debug.o
+cw1200_core-$(CONFIG_PM)       += pm.o
+
+# CFLAGS_sta.o += -DDEBUG
+
+cw1200_wlan_sdio-y := cw1200_sdio.o
+cw1200_wlan_spi-y := cw1200_spi.o
+
+obj-$(CONFIG_CW1200) += cw1200_core.o
+obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o
+obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o
diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/cw1200/bh.c
new file mode 100644 (file)
index 0000000..c1ec2a4
--- /dev/null
@@ -0,0 +1,619 @@
+/*
+ * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver, which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.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 <net/mac80211.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+
+#include "cw1200.h"
+#include "bh.h"
+#include "hwio.h"
+#include "wsm.h"
+#include "hwbus.h"
+#include "debug.h"
+#include "fwio.h"
+
+static int cw1200_bh(void *arg);
+
+#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4)
+/* an SPI message cannot be bigger than (2"12-1)*2 bytes
+ * "*2" to cvt to bytes
+ */
+#define MAX_SZ_RD_WR_BUFFERS   (DOWNLOAD_BLOCK_SIZE_WR*2)
+#define PIGGYBACK_CTRL_REG     (2)
+#define EFFECTIVE_BUF_SIZE     (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG)
+
+/* Suspend state privates */
+enum cw1200_bh_pm_state {
+       CW1200_BH_RESUMED = 0,
+       CW1200_BH_SUSPEND,
+       CW1200_BH_SUSPENDED,
+       CW1200_BH_RESUME,
+};
+
+typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv,
+       u8 *data, size_t size);
+
+static void cw1200_bh_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+       container_of(work, struct cw1200_common, bh_work);
+       cw1200_bh(priv);
+}
+
+int cw1200_register_bh(struct cw1200_common *priv)
+{
+       int err = 0;
+       /* Realtime workqueue */
+       priv->bh_workqueue = alloc_workqueue("cw1200_bh",
+                               WQ_MEM_RECLAIM | WQ_HIGHPRI
+                               | WQ_CPU_INTENSIVE, 1);
+
+       if (!priv->bh_workqueue)
+               return -ENOMEM;
+
+       INIT_WORK(&priv->bh_work, cw1200_bh_work);
+
+       pr_debug("[BH] register.\n");
+
+       atomic_set(&priv->bh_rx, 0);
+       atomic_set(&priv->bh_tx, 0);
+       atomic_set(&priv->bh_term, 0);
+       atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
+       priv->bh_error = 0;
+       priv->hw_bufs_used = 0;
+       priv->buf_id_tx = 0;
+       priv->buf_id_rx = 0;
+       init_waitqueue_head(&priv->bh_wq);
+       init_waitqueue_head(&priv->bh_evt_wq);
+
+       err = !queue_work(priv->bh_workqueue, &priv->bh_work);
+       WARN_ON(err);
+       return err;
+}
+
+void cw1200_unregister_bh(struct cw1200_common *priv)
+{
+       atomic_add(1, &priv->bh_term);
+       wake_up(&priv->bh_wq);
+
+       flush_workqueue(priv->bh_workqueue);
+
+       destroy_workqueue(priv->bh_workqueue);
+       priv->bh_workqueue = NULL;
+
+       pr_debug("[BH] unregistered.\n");
+}
+
+void cw1200_irq_handler(struct cw1200_common *priv)
+{
+       pr_debug("[BH] irq.\n");
+
+       /* Disable Interrupts! */
+       /* NOTE:  hwbus_ops->lock already held */
+       __cw1200_irq_enable(priv, 0);
+
+       if (/* WARN_ON */(priv->bh_error))
+               return;
+
+       if (atomic_add_return(1, &priv->bh_rx) == 1)
+               wake_up(&priv->bh_wq);
+}
+EXPORT_SYMBOL_GPL(cw1200_irq_handler);
+
+void cw1200_bh_wakeup(struct cw1200_common *priv)
+{
+       pr_debug("[BH] wakeup.\n");
+       if (priv->bh_error) {
+               pr_err("[BH] wakeup failed (BH error)\n");
+               return;
+       }
+
+       if (atomic_add_return(1, &priv->bh_tx) == 1)
+               wake_up(&priv->bh_wq);
+}
+
+int cw1200_bh_suspend(struct cw1200_common *priv)
+{
+       pr_debug("[BH] suspend.\n");
+       if (priv->bh_error) {
+               wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n");
+               return -EINVAL;
+       }
+
+       atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND);
+       wake_up(&priv->bh_wq);
+       return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
+               (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)),
+                1 * HZ) ? 0 : -ETIMEDOUT;
+}
+
+int cw1200_bh_resume(struct cw1200_common *priv)
+{
+       pr_debug("[BH] resume.\n");
+       if (priv->bh_error) {
+               wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n");
+               return -EINVAL;
+       }
+
+       atomic_set(&priv->bh_suspend, CW1200_BH_RESUME);
+       wake_up(&priv->bh_wq);
+       return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
+               (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)),
+               1 * HZ) ? 0 : -ETIMEDOUT;
+}
+
+static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv)
+{
+       ++priv->hw_bufs_used;
+}
+
+int wsm_release_tx_buffer(struct cw1200_common *priv, int count)
+{
+       int ret = 0;
+       int hw_bufs_used = priv->hw_bufs_used;
+
+       priv->hw_bufs_used -= count;
+       if (WARN_ON(priv->hw_bufs_used < 0))
+               ret = -1;
+       else if (hw_bufs_used >= priv->wsm_caps.input_buffers)
+               ret = 1;
+       if (!priv->hw_bufs_used)
+               wake_up(&priv->bh_evt_wq);
+       return ret;
+}
+
+static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv,
+                                         u16 *ctrl_reg)
+{
+       int ret;
+
+       ret = cw1200_reg_read_16(priv,
+                       ST90TDS_CONTROL_REG_ID, ctrl_reg);
+       if (ret) {
+               ret = cw1200_reg_read_16(priv,
+                               ST90TDS_CONTROL_REG_ID, ctrl_reg);
+               if (ret)
+                       pr_err("[BH] Failed to read control register.\n");
+       }
+
+       return ret;
+}
+
+static int cw1200_device_wakeup(struct cw1200_common *priv)
+{
+       u16 ctrl_reg;
+       int ret;
+
+       pr_debug("[BH] Device wakeup.\n");
+
+       /* First, set the dpll register */
+       ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
+                                 cw1200_dpll_from_clk(priv->hw_refclk));
+       if (WARN_ON(ret))
+               return ret;
+
+       /* To force the device to be always-on, the host sets WLAN_UP to 1 */
+       ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
+                       ST90TDS_CONT_WUP_BIT);
+       if (WARN_ON(ret))
+               return ret;
+
+       ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg);
+       if (WARN_ON(ret))
+               return ret;
+
+       /* If the device returns WLAN_RDY as 1, the device is active and will
+        * remain active.
+        */
+       if (ctrl_reg & ST90TDS_CONT_RDY_BIT) {
+               pr_debug("[BH] Device awake.\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+/* Must be called from BH thraed. */
+void cw1200_enable_powersave(struct cw1200_common *priv,
+                            bool enable)
+{
+       pr_debug("[BH] Powerave is %s.\n",
+                enable ? "enabled" : "disabled");
+       priv->powersave_enabled = enable;
+}
+
+static int cw1200_bh_rx_helper(struct cw1200_common *priv,
+                              uint16_t *ctrl_reg,
+                              int *tx)
+{
+       size_t read_len = 0;
+       struct sk_buff *skb_rx = NULL;
+       struct wsm_hdr *wsm;
+       size_t wsm_len;
+       u16 wsm_id;
+       u8 wsm_seq;
+       int rx_resync = 1;
+
+       size_t alloc_len;
+       u8 *data;
+
+       read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2;
+       if (!read_len)
+               return 0; /* No more work */
+
+       if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
+                   (read_len > EFFECTIVE_BUF_SIZE))) {
+               pr_debug("Invalid read len: %zu (%04x)",
+                        read_len, *ctrl_reg);
+               goto err;
+       }
+
+       /* Add SIZE of PIGGYBACK reg (CONTROL Reg)
+        * to the NEXT Message length + 2 Bytes for SKB
+        */
+       read_len = read_len + 2;
+
+       alloc_len = priv->hwbus_ops->align_size(
+               priv->hwbus_priv, read_len);
+
+       /* Check if not exceeding CW1200 capabilities */
+       if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) {
+               pr_debug("Read aligned len: %zu\n",
+                        alloc_len);
+       }
+
+       skb_rx = dev_alloc_skb(alloc_len);
+       if (WARN_ON(!skb_rx))
+               goto err;
+
+       skb_trim(skb_rx, 0);
+       skb_put(skb_rx, read_len);
+       data = skb_rx->data;
+       if (WARN_ON(!data))
+               goto err;
+
+       if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) {
+               pr_err("rx blew up, len %zu\n", alloc_len);
+               goto err;
+       }
+
+       /* Piggyback */
+       *ctrl_reg = __le16_to_cpu(
+               ((__le16 *)data)[alloc_len / 2 - 1]);
+
+       wsm = (struct wsm_hdr *)data;
+       wsm_len = __le16_to_cpu(wsm->len);
+       if (WARN_ON(wsm_len > read_len))
+               goto err;
+
+       if (priv->wsm_enable_wsm_dumps)
+               print_hex_dump_bytes("<-- ",
+                                    DUMP_PREFIX_NONE,
+                                    data, wsm_len);
+
+       wsm_id  = __le16_to_cpu(wsm->id) & 0xFFF;
+       wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
+
+       skb_trim(skb_rx, wsm_len);
+
+       if (wsm_id == 0x0800) {
+               wsm_handle_exception(priv,
+                                    &data[sizeof(*wsm)],
+                                    wsm_len - sizeof(*wsm));
+               goto err;
+       } else if (!rx_resync) {
+               if (WARN_ON(wsm_seq != priv->wsm_rx_seq))
+                       goto err;
+       }
+       priv->wsm_rx_seq = (wsm_seq + 1) & 7;
+       rx_resync = 0;
+
+       if (wsm_id & 0x0400) {
+               int rc = wsm_release_tx_buffer(priv, 1);
+               if (WARN_ON(rc < 0))
+                       return rc;
+               else if (rc > 0)
+                       *tx = 1;
+       }
+
+       /* cw1200_wsm_rx takes care on SKB livetime */
+       if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx)))
+               goto err;
+
+       if (skb_rx) {
+               dev_kfree_skb(skb_rx);
+               skb_rx = NULL;
+       }
+
+       return 0;
+
+err:
+       if (skb_rx) {
+               dev_kfree_skb(skb_rx);
+               skb_rx = NULL;
+       }
+       return -1;
+}
+
+static int cw1200_bh_tx_helper(struct cw1200_common *priv,
+                              int *pending_tx,
+                              int *tx_burst)
+{
+       size_t tx_len;
+       u8 *data;
+       int ret;
+       struct wsm_hdr *wsm;
+
+       if (priv->device_can_sleep) {
+               ret = cw1200_device_wakeup(priv);
+               if (WARN_ON(ret < 0)) { /* Error in wakeup */
+                       *pending_tx = 1;
+                       return 0;
+               } else if (ret) { /* Woke up */
+                       priv->device_can_sleep = false;
+               } else { /* Did not awake */
+                       *pending_tx = 1;
+                       return 0;
+               }
+       }
+
+       wsm_alloc_tx_buffer(priv);
+       ret = wsm_get_tx(priv, &data, &tx_len, tx_burst);
+       if (ret <= 0) {
+               wsm_release_tx_buffer(priv, 1);
+               if (WARN_ON(ret < 0))
+                       return ret; /* Error */
+               return 0; /* No work */
+       }
+
+       wsm = (struct wsm_hdr *)data;
+       BUG_ON(tx_len < sizeof(*wsm));
+       BUG_ON(__le16_to_cpu(wsm->len) != tx_len);
+
+       atomic_add(1, &priv->bh_tx);
+
+       tx_len = priv->hwbus_ops->align_size(
+               priv->hwbus_priv, tx_len);
+
+       /* Check if not exceeding CW1200 capabilities */
+       if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
+               pr_debug("Write aligned len: %zu\n", tx_len);
+
+       wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX));
+       wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq));
+
+       if (WARN_ON(cw1200_data_write(priv, data, tx_len))) {
+               pr_err("tx blew up, len %zu\n", tx_len);
+               wsm_release_tx_buffer(priv, 1);
+               return -1; /* Error */
+       }
+
+       if (priv->wsm_enable_wsm_dumps)
+               print_hex_dump_bytes("--> ",
+                                    DUMP_PREFIX_NONE,
+                                    data,
+                                    __le16_to_cpu(wsm->len));
+
+       wsm_txed(priv, data);
+       priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX;
+
+       if (*tx_burst > 1) {
+               cw1200_debug_tx_burst(priv);
+               return 1; /* Work remains */
+       }
+
+       return 0;
+}
+
+static int cw1200_bh(void *arg)
+{
+       struct cw1200_common *priv = arg;
+       int rx, tx, term, suspend;
+       u16 ctrl_reg = 0;
+       int tx_allowed;
+       int pending_tx = 0;
+       int tx_burst;
+       long status;
+       u32 dummy;
+       int ret;
+
+       for (;;) {
+               if (!priv->hw_bufs_used &&
+                   priv->powersave_enabled &&
+                   !priv->device_can_sleep &&
+                   !atomic_read(&priv->recent_scan)) {
+                       status = 1 * HZ;
+                       pr_debug("[BH] Device wakedown. No data.\n");
+                       cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0);
+                       priv->device_can_sleep = true;
+               } else if (priv->hw_bufs_used) {
+                       /* Interrupt loss detection */
+                       status = 1 * HZ;
+               } else {
+                       status = MAX_SCHEDULE_TIMEOUT;
+               }
+
+               /* Dummy Read for SDIO retry mechanism*/
+               if ((priv->hw_type != -1) &&
+                   (atomic_read(&priv->bh_rx) == 0) &&
+                   (atomic_read(&priv->bh_tx) == 0))
+                       cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID,
+                                       &dummy, sizeof(dummy));
+
+               pr_debug("[BH] waiting ...\n");
+               status = wait_event_interruptible_timeout(priv->bh_wq, ({
+                               rx = atomic_xchg(&priv->bh_rx, 0);
+                               tx = atomic_xchg(&priv->bh_tx, 0);
+                               term = atomic_xchg(&priv->bh_term, 0);
+                               suspend = pending_tx ?
+                                       0 : atomic_read(&priv->bh_suspend);
+                               (rx || tx || term || suspend || priv->bh_error);
+                       }), status);
+
+               pr_debug("[BH] - rx: %d, tx: %d, term: %d, suspend: %d, status: %ld\n",
+                        rx, tx, term, suspend, status);
+
+               /* Did an error occur? */
+               if ((status < 0 && status != -ERESTARTSYS) ||
+                   term || priv->bh_error) {
+                       break;
+               }
+               if (!status) {  /* wait_event timed out */
+                       unsigned long timestamp = jiffies;
+                       long timeout;
+                       int pending = 0;
+                       int i;
+
+                       /* Check to see if we have any outstanding frames */
+                       if (priv->hw_bufs_used && (!rx || !tx)) {
+                               wiphy_warn(priv->hw->wiphy,
+                                          "Missed interrupt? (%d frames outstanding)\n",
+                                          priv->hw_bufs_used);
+                               rx = 1;
+
+                               /* Get a timestamp of "oldest" frame */
+                               for (i = 0; i < 4; ++i)
+                                       pending += cw1200_queue_get_xmit_timestamp(
+                                               &priv->tx_queue[i],
+                                               &timestamp,
+                                               priv->pending_frame_id);
+
+                               /* Check if frame transmission is timed out.
+                                * Add an extra second with respect to possible
+                                * interrupt loss.
+                                */
+                               timeout = timestamp +
+                                       WSM_CMD_LAST_CHANCE_TIMEOUT +
+                                       1 * HZ  -
+                                       jiffies;
+
+                               /* And terminate BH thread if the frame is "stuck" */
+                               if (pending && timeout < 0) {
+                                       wiphy_warn(priv->hw->wiphy,
+                                                  "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n",
+                                                  priv->hw_bufs_used, pending,
+                                                  timestamp, jiffies);
+                                       break;
+                               }
+                       } else if (!priv->device_can_sleep &&
+                                  !atomic_read(&priv->recent_scan)) {
+                               pr_debug("[BH] Device wakedown. Timeout.\n");
+                               cw1200_reg_write_16(priv,
+                                                   ST90TDS_CONTROL_REG_ID, 0);
+                               priv->device_can_sleep = true;
+                       }
+                       goto done;
+               } else if (suspend) {
+                       pr_debug("[BH] Device suspend.\n");
+                       if (priv->powersave_enabled) {
+                               pr_debug("[BH] Device wakedown. Suspend.\n");
+                               cw1200_reg_write_16(priv,
+                                                   ST90TDS_CONTROL_REG_ID, 0);
+                               priv->device_can_sleep = true;
+                       }
+
+                       atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED);
+                       wake_up(&priv->bh_evt_wq);
+                       status = wait_event_interruptible(priv->bh_wq,
+                                                         CW1200_BH_RESUME == atomic_read(&priv->bh_suspend));
+                       if (status < 0) {
+                               wiphy_err(priv->hw->wiphy,
+                                         "Failed to wait for resume: %ld.\n",
+                                         status);
+                               break;
+                       }
+                       pr_debug("[BH] Device resume.\n");
+                       atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
+                       wake_up(&priv->bh_evt_wq);
+                       atomic_add(1, &priv->bh_rx);
+                       goto done;
+               }
+
+       rx:
+               tx += pending_tx;
+               pending_tx = 0;
+
+               if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
+                       break;
+
+               /* Don't bother trying to rx unless we have data to read */
+               if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
+                       ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
+                       if (ret < 0)
+                               break;
+                       /* Double up here if there's more data.. */
+                       if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
+                               ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
+                               if (ret < 0)
+                                       break;
+                       }
+               }
+
+       tx:
+               if (tx) {
+                       tx = 0;
+
+                       BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers);
+                       tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used;
+                       tx_allowed = tx_burst > 0;
+
+                       if (!tx_allowed) {
+                               /* Buffers full.  Ensure we process tx
+                                * after we handle rx..
+                                */
+                               pending_tx = tx;
+                               goto done_rx;
+                       }
+                       ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst);
+                       if (ret < 0)
+                               break;
+                       if (ret > 0) /* More to transmit */
+                               tx = ret;
+
+                       /* Re-read ctrl reg */
+                       if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
+                               break;
+               }
+
+       done_rx:
+               if (priv->bh_error)
+                       break;
+               if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
+                       goto rx;
+               if (tx)
+                       goto tx;
+
+       done:
+               /* Re-enable device interrupts */
+               priv->hwbus_ops->lock(priv->hwbus_priv);
+               __cw1200_irq_enable(priv, 1);
+               priv->hwbus_ops->unlock(priv->hwbus_priv);
+       }
+
+       /* Explicitly disable device interrupts */
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       __cw1200_irq_enable(priv, 0);
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+
+       if (!term) {
+               pr_err("[BH] Fatal error, exiting.\n");
+               priv->bh_error = 1;
+               /* TODO: schedule_work(recovery) */
+       }
+       return 0;
+}
diff --git a/drivers/net/wireless/cw1200/bh.h b/drivers/net/wireless/cw1200/bh.h
new file mode 100644 (file)
index 0000000..af6a485
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 CW1200_BH_H
+#define CW1200_BH_H
+
+/* extern */ struct cw1200_common;
+
+int cw1200_register_bh(struct cw1200_common *priv);
+void cw1200_unregister_bh(struct cw1200_common *priv);
+void cw1200_irq_handler(struct cw1200_common *priv);
+void cw1200_bh_wakeup(struct cw1200_common *priv);
+int cw1200_bh_suspend(struct cw1200_common *priv);
+int cw1200_bh_resume(struct cw1200_common *priv);
+/* Must be called from BH thread. */
+void cw1200_enable_powersave(struct cw1200_common *priv,
+                            bool enable);
+int wsm_release_tx_buffer(struct cw1200_common *priv, int count);
+
+#endif /* CW1200_BH_H */
diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/cw1200/cw1200.h
new file mode 100644 (file)
index 0000000..1ad7d36
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * Common private data for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on the mac80211 Prism54 code, which is
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ *
+ * Based on the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * 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 CW1200_H
+#define CW1200_H
+
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <net/mac80211.h>
+
+#include "queue.h"
+#include "wsm.h"
+#include "scan.h"
+#include "txrx.h"
+#include "pm.h"
+
+/* Forward declarations */
+struct hwbus_ops;
+struct task_struct;
+struct cw1200_debug_priv;
+struct firmware;
+
+#define CW1200_MAX_CTRL_FRAME_LEN      (0x1000)
+
+#define CW1200_MAX_STA_IN_AP_MODE      (5)
+#define CW1200_LINK_ID_AFTER_DTIM      (CW1200_MAX_STA_IN_AP_MODE + 1)
+#define CW1200_LINK_ID_UAPSD           (CW1200_MAX_STA_IN_AP_MODE + 2)
+#define CW1200_LINK_ID_MAX             (CW1200_MAX_STA_IN_AP_MODE + 3)
+#define CW1200_MAX_REQUEUE_ATTEMPTS    (5)
+
+#define CW1200_MAX_TID                 (8)
+
+#define CW1200_BLOCK_ACK_CNT           (30)
+#define CW1200_BLOCK_ACK_THLD          (800)
+#define CW1200_BLOCK_ACK_HIST          (3)
+#define CW1200_BLOCK_ACK_INTERVAL      (1 * HZ / CW1200_BLOCK_ACK_HIST)
+
+#define CW1200_JOIN_TIMEOUT            (1 * HZ)
+#define CW1200_AUTH_TIMEOUT            (5 * HZ)
+
+struct cw1200_ht_info {
+       struct ieee80211_sta_ht_cap     ht_cap;
+       enum nl80211_channel_type       channel_type;
+       u16                             operation_mode;
+};
+
+/* Please keep order */
+enum cw1200_join_status {
+       CW1200_JOIN_STATUS_PASSIVE = 0,
+       CW1200_JOIN_STATUS_MONITOR,
+       CW1200_JOIN_STATUS_JOINING,
+       CW1200_JOIN_STATUS_PRE_STA,
+       CW1200_JOIN_STATUS_STA,
+       CW1200_JOIN_STATUS_IBSS,
+       CW1200_JOIN_STATUS_AP,
+};
+
+enum cw1200_link_status {
+       CW1200_LINK_OFF,
+       CW1200_LINK_RESERVE,
+       CW1200_LINK_SOFT,
+       CW1200_LINK_HARD,
+       CW1200_LINK_RESET,
+       CW1200_LINK_RESET_REMAP,
+};
+
+extern int cw1200_power_mode;
+extern const char * const cw1200_fw_types[];
+
+struct cw1200_link_entry {
+       unsigned long                   timestamp;
+       enum cw1200_link_status         status;
+       enum cw1200_link_status         prev_status;
+       u8                              mac[ETH_ALEN];
+       u8                              buffered[CW1200_MAX_TID];
+       struct sk_buff_head             rx_queue;
+};
+
+struct cw1200_common {
+       /* interfaces to the rest of the stack */
+       struct ieee80211_hw             *hw;
+       struct ieee80211_vif            *vif;
+       struct device                   *pdev;
+
+       /* Statistics */
+       struct ieee80211_low_level_stats stats;
+
+       /* Our macaddr */
+       u8 mac_addr[ETH_ALEN];
+
+       /* Hardware interface */
+       const struct hwbus_ops          *hwbus_ops;
+       struct hwbus_priv               *hwbus_priv;
+
+       /* Hardware information */
+       enum {
+               HIF_9000_SILICON_VERSATILE = 0,
+               HIF_8601_VERSATILE,
+               HIF_8601_SILICON,
+       } hw_type;
+       enum {
+               CW1200_HW_REV_CUT10 = 10,
+               CW1200_HW_REV_CUT11 = 11,
+               CW1200_HW_REV_CUT20 = 20,
+               CW1200_HW_REV_CUT22 = 22,
+               CW1X60_HW_REV       = 40,
+       } hw_revision;
+       int                             hw_refclk;
+       bool                            hw_have_5ghz;
+       const struct firmware           *sdd;
+       char                            *sdd_path;
+
+       struct cw1200_debug_priv        *debug;
+
+       struct workqueue_struct         *workqueue;
+       struct mutex                    conf_mutex;
+
+       struct cw1200_queue             tx_queue[4];
+       struct cw1200_queue_stats       tx_queue_stats;
+       int                             tx_burst_idx;
+
+       /* firmware/hardware info */
+       unsigned int tx_hdr_len;
+
+       /* Radio data */
+       int output_power;
+
+       /* BBP/MAC state */
+       struct ieee80211_rate           *rates;
+       struct ieee80211_rate           *mcs_rates;
+       struct ieee80211_channel        *channel;
+       struct wsm_edca_params          edca;
+       struct wsm_tx_queue_params      tx_queue_params;
+       struct wsm_mib_association_mode association_mode;
+       struct wsm_set_bss_params       bss_params;
+       struct cw1200_ht_info           ht_info;
+       struct wsm_set_pm               powersave_mode;
+       struct wsm_set_pm               firmware_ps_mode;
+       int                             cqm_rssi_thold;
+       unsigned                        cqm_rssi_hyst;
+       bool                            cqm_use_rssi;
+       int                             cqm_beacon_loss_count;
+       int                             channel_switch_in_progress;
+       wait_queue_head_t               channel_switch_done;
+       u8                              long_frame_max_tx_count;
+       u8                              short_frame_max_tx_count;
+       int                             mode;
+       bool                            enable_beacon;
+       int                             beacon_int;
+       bool                            listening;
+       struct wsm_rx_filter            rx_filter;
+       struct wsm_mib_multicast_filter multicast_filter;
+       bool                            has_multicast_subscription;
+       bool                            disable_beacon_filter;
+       struct work_struct              update_filtering_work;
+       struct work_struct              set_beacon_wakeup_period_work;
+
+       u8                              ba_rx_tid_mask;
+       u8                              ba_tx_tid_mask;
+
+       struct cw1200_pm_state          pm_state;
+
+       struct wsm_p2p_ps_modeinfo      p2p_ps_modeinfo;
+       struct wsm_uapsd_info           uapsd_info;
+       bool                            setbssparams_done;
+       bool                            bt_present;
+       u8                              conf_listen_interval;
+       u32                             listen_interval;
+       u32                             erp_info;
+       u32                             rts_threshold;
+
+       /* BH */
+       atomic_t                        bh_rx;
+       atomic_t                        bh_tx;
+       atomic_t                        bh_term;
+       atomic_t                        bh_suspend;
+
+       struct workqueue_struct         *bh_workqueue;
+       struct work_struct              bh_work;
+
+       int                             bh_error;
+       wait_queue_head_t               bh_wq;
+       wait_queue_head_t               bh_evt_wq;
+       u8                              buf_id_tx;
+       u8                              buf_id_rx;
+       u8                              wsm_rx_seq;
+       u8                              wsm_tx_seq;
+       int                             hw_bufs_used;
+       bool                            powersave_enabled;
+       bool                            device_can_sleep;
+
+       /* Scan status */
+       struct cw1200_scan scan;
+       /* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid
+        * FW issue with sleeping/waking up.
+        */
+       atomic_t                        recent_scan;
+       struct delayed_work             clear_recent_scan_work;
+
+       /* WSM */
+       struct wsm_startup_ind          wsm_caps;
+       struct mutex                    wsm_cmd_mux;
+       struct wsm_buf                  wsm_cmd_buf;
+       struct wsm_cmd                  wsm_cmd;
+       wait_queue_head_t               wsm_cmd_wq;
+       wait_queue_head_t               wsm_startup_done;
+       int                             firmware_ready;
+       atomic_t                        tx_lock;
+
+       /* WSM debug */
+       int                             wsm_enable_wsm_dumps;
+
+       /* WSM Join */
+       enum cw1200_join_status join_status;
+       u32                     pending_frame_id;
+       bool                    join_pending;
+       struct delayed_work     join_timeout;
+       struct work_struct      unjoin_work;
+       struct work_struct      join_complete_work;
+       int                     join_complete_status;
+       int                     join_dtim_period;
+       bool                    delayed_unjoin;
+
+       /* TX/RX and security */
+       s8                      wep_default_key_id;
+       struct work_struct      wep_key_work;
+       u32                     key_map;
+       struct wsm_add_key      keys[WSM_KEY_MAX_INDEX + 1];
+
+       /* AP powersave */
+       u32                     link_id_map;
+       struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE];
+       struct work_struct      link_id_work;
+       struct delayed_work     link_id_gc_work;
+       u32                     sta_asleep_mask;
+       u32                     pspoll_mask;
+       bool                    aid0_bit_set;
+       spinlock_t              ps_state_lock; /* Protect power save state */
+       bool                    buffered_multicasts;
+       bool                    tx_multicast;
+       struct work_struct      set_tim_work;
+       struct work_struct      set_cts_work;
+       struct work_struct      multicast_start_work;
+       struct work_struct      multicast_stop_work;
+       struct timer_list       mcast_timeout;
+
+       /* WSM events and CQM implementation */
+       spinlock_t              event_queue_lock; /* Protect event queue */
+       struct list_head        event_queue;
+       struct work_struct      event_handler;
+
+       struct delayed_work     bss_loss_work;
+       spinlock_t              bss_loss_lock; /* Protect BSS loss state */
+       int                     bss_loss_state;
+       u32                     bss_loss_confirm_id;
+       int                     delayed_link_loss;
+       struct work_struct      bss_params_work;
+
+       /* TX rate policy cache */
+       struct tx_policy_cache tx_policy_cache;
+       struct work_struct tx_policy_upload_work;
+
+       /* legacy PS mode switch in suspend */
+       int                     ps_mode_switch_in_progress;
+       wait_queue_head_t       ps_mode_switch_done;
+
+       /* Workaround for WFD testcase 6.1.10*/
+       struct work_struct      linkid_reset_work;
+       u8                      action_frame_sa[ETH_ALEN];
+       u8                      action_linkid;
+};
+
+struct cw1200_sta_priv {
+       int link_id;
+};
+
+/* interfaces for the drivers */
+int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
+                     struct hwbus_priv *hwbus,
+                     struct device *pdev,
+                     struct cw1200_common **pself,
+                     int ref_clk, const u8 *macaddr,
+                     const char *sdd_path, bool have_5ghz);
+void cw1200_core_release(struct cw1200_common *self);
+
+#define FWLOAD_BLOCK_SIZE (1024)
+
+static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info)
+{
+       return ht_info->channel_type != NL80211_CHAN_NO_HT;
+}
+
+static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info)
+{
+       return cw1200_is_ht(ht_info) &&
+               (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
+               !(ht_info->operation_mode &
+                 IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+}
+
+static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info)
+{
+       if (!cw1200_is_ht(ht_info))
+               return 0;
+       return ht_info->ht_cap.ampdu_density;
+}
+
+#endif /* CW1200_H */
diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c
new file mode 100644 (file)
index 0000000..ebdcdf4
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * Mac80211 SDIO driver for ST-Ericsson CW1200 device
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/gpio.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <net/mac80211.h>
+
+#include "cw1200.h"
+#include "hwbus.h"
+#include <linux/platform_data/net-cw1200.h>
+#include "hwio.h"
+
+MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
+MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver");
+MODULE_LICENSE("GPL");
+
+#define SDIO_BLOCK_SIZE (512)
+
+/* Default platform data for Sagrad modules */
+static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = {
+       .ref_clk = 38400,
+       .have_5ghz = false,
+       .sdd_file = "sdd_sagrad_1091_1098.bin",
+};
+
+/* Allow platform data to be overridden */
+static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data;
+
+void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata)
+{
+       global_plat_data = pdata;
+}
+
+struct hwbus_priv {
+       struct sdio_func        *func;
+       struct cw1200_common    *core;
+       const struct cw1200_platform_data_sdio *pdata;
+};
+
+#ifndef SDIO_VENDOR_ID_STE
+#define SDIO_VENDOR_ID_STE             0x0020
+#endif
+
+#ifndef SDIO_DEVICE_ID_STE_CW1200
+#define SDIO_DEVICE_ID_STE_CW1200      0x2280
+#endif
+
+static const struct sdio_device_id cw1200_sdio_ids[] = {
+       { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) },
+       { /* end: all zeroes */                 },
+};
+
+/* hwbus_ops implemetation */
+
+static int cw1200_sdio_memcpy_fromio(struct hwbus_priv *self,
+                                    unsigned int addr,
+                                    void *dst, int count)
+{
+       return sdio_memcpy_fromio(self->func, dst, addr, count);
+}
+
+static int cw1200_sdio_memcpy_toio(struct hwbus_priv *self,
+                                  unsigned int addr,
+                                  const void *src, int count)
+{
+       return sdio_memcpy_toio(self->func, addr, (void *)src, count);
+}
+
+static void cw1200_sdio_lock(struct hwbus_priv *self)
+{
+       sdio_claim_host(self->func);
+}
+
+static void cw1200_sdio_unlock(struct hwbus_priv *self)
+{
+       sdio_release_host(self->func);
+}
+
+static void cw1200_sdio_irq_handler(struct sdio_func *func)
+{
+       struct hwbus_priv *self = sdio_get_drvdata(func);
+
+       /* note:  sdio_host already claimed here. */
+       if (self->core)
+               cw1200_irq_handler(self->core);
+}
+
+static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id)
+{
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id)
+{
+       struct hwbus_priv *self = dev_id;
+
+       if (self->core) {
+               sdio_claim_host(self->func);
+               cw1200_irq_handler(self->core);
+               sdio_release_host(self->func);
+               return IRQ_HANDLED;
+       } else {
+               return IRQ_NONE;
+       }
+}
+
+static int cw1200_request_irq(struct hwbus_priv *self)
+{
+       int ret;
+       u8 cccr;
+
+       cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret);
+       if (WARN_ON(ret))
+               goto err;
+
+       /* Master interrupt enable ... */
+       cccr |= BIT(0);
+
+       /* ... for our function */
+       cccr |= BIT(self->func->num);
+
+       sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret);
+       if (WARN_ON(ret))
+               goto err;
+
+       ret = enable_irq_wake(self->pdata->irq);
+       if (WARN_ON(ret))
+               goto err;
+
+       /* Request the IRQ */
+       ret =  request_threaded_irq(self->pdata->irq, cw1200_gpio_hardirq,
+                                   cw1200_gpio_irq,
+                                   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                   "cw1200_wlan_irq", self);
+       if (WARN_ON(ret))
+               goto err;
+
+       return 0;
+
+err:
+       return ret;
+}
+
+static int cw1200_sdio_irq_subscribe(struct hwbus_priv *self)
+{
+       int ret = 0;
+
+       pr_debug("SW IRQ subscribe\n");
+       sdio_claim_host(self->func);
+       if (self->pdata->irq)
+               ret = cw1200_request_irq(self);
+       else
+               ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler);
+
+       sdio_release_host(self->func);
+       return ret;
+}
+
+static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self)
+{
+       int ret = 0;
+
+       pr_debug("SW IRQ unsubscribe\n");
+
+       if (self->pdata->irq) {
+               disable_irq_wake(self->pdata->irq);
+               free_irq(self->pdata->irq, self);
+       } else {
+               sdio_claim_host(self->func);
+               ret = sdio_release_irq(self->func);
+               sdio_release_host(self->func);
+       }
+       return ret;
+}
+
+static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata)
+{
+       if (pdata->reset) {
+               gpio_set_value(pdata->reset, 0);
+               msleep(30); /* Min is 2 * CLK32K cycles */
+               gpio_free(pdata->reset);
+       }
+
+       if (pdata->power_ctrl)
+               pdata->power_ctrl(pdata, false);
+       if (pdata->clk_ctrl)
+               pdata->clk_ctrl(pdata, false);
+
+       return 0;
+}
+
+static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata)
+{
+       /* Ensure I/Os are pulled low */
+       if (pdata->reset) {
+               gpio_request(pdata->reset, "cw1200_wlan_reset");
+               gpio_direction_output(pdata->reset, 0);
+       }
+       if (pdata->powerup) {
+               gpio_request(pdata->powerup, "cw1200_wlan_powerup");
+               gpio_direction_output(pdata->powerup, 0);
+       }
+       if (pdata->reset || pdata->powerup)
+               msleep(10); /* Settle time? */
+
+       /* Enable 3v3 and 1v8 to hardware */
+       if (pdata->power_ctrl) {
+               if (pdata->power_ctrl(pdata, true)) {
+                       pr_err("power_ctrl() failed!\n");
+                       return -1;
+               }
+       }
+
+       /* Enable CLK32K */
+       if (pdata->clk_ctrl) {
+               if (pdata->clk_ctrl(pdata, true)) {
+                       pr_err("clk_ctrl() failed!\n");
+                       return -1;
+               }
+               msleep(10); /* Delay until clock is stable for 2 cycles */
+       }
+
+       /* Enable POWERUP signal */
+       if (pdata->powerup) {
+               gpio_set_value(pdata->powerup, 1);
+               msleep(250); /* or more..? */
+       }
+       /* Enable RSTn signal */
+       if (pdata->reset) {
+               gpio_set_value(pdata->reset, 1);
+               msleep(50); /* Or more..? */
+       }
+       return 0;
+}
+
+static size_t cw1200_sdio_align_size(struct hwbus_priv *self, size_t size)
+{
+       if (self->pdata->no_nptb)
+               size = round_up(size, SDIO_BLOCK_SIZE);
+       else
+               size = sdio_align_size(self->func, size);
+
+       return size;
+}
+
+static int cw1200_sdio_pm(struct hwbus_priv *self, bool suspend)
+{
+       int ret = 0;
+
+       if (self->pdata->irq)
+               ret = irq_set_irq_wake(self->pdata->irq, suspend);
+       return ret;
+}
+
+static struct hwbus_ops cw1200_sdio_hwbus_ops = {
+       .hwbus_memcpy_fromio    = cw1200_sdio_memcpy_fromio,
+       .hwbus_memcpy_toio      = cw1200_sdio_memcpy_toio,
+       .lock                   = cw1200_sdio_lock,
+       .unlock                 = cw1200_sdio_unlock,
+       .align_size             = cw1200_sdio_align_size,
+       .power_mgmt             = cw1200_sdio_pm,
+};
+
+/* Probe Function to be called by SDIO stack when device is discovered */
+static int cw1200_sdio_probe(struct sdio_func *func,
+                            const struct sdio_device_id *id)
+{
+       struct hwbus_priv *self;
+       int status;
+
+       pr_info("cw1200_wlan_sdio: Probe called\n");
+
+       /* We are only able to handle the wlan function */
+       if (func->num != 0x01)
+               return -ENODEV;
+
+       self = kzalloc(sizeof(*self), GFP_KERNEL);
+       if (!self) {
+               pr_err("Can't allocate SDIO hwbus_priv.\n");
+               return -ENOMEM;
+       }
+
+       func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+
+       self->pdata = global_plat_data; /* FIXME */
+       self->func = func;
+       sdio_set_drvdata(func, self);
+       sdio_claim_host(func);
+       sdio_enable_func(func);
+       sdio_release_host(func);
+
+       status = cw1200_sdio_irq_subscribe(self);
+
+       status = cw1200_core_probe(&cw1200_sdio_hwbus_ops,
+                                  self, &func->dev, &self->core,
+                                  self->pdata->ref_clk,
+                                  self->pdata->macaddr,
+                                  self->pdata->sdd_file,
+                                  self->pdata->have_5ghz);
+       if (status) {
+               cw1200_sdio_irq_unsubscribe(self);
+               sdio_claim_host(func);
+               sdio_disable_func(func);
+               sdio_release_host(func);
+               sdio_set_drvdata(func, NULL);
+               kfree(self);
+       }
+
+       return status;
+}
+
+/* Disconnect Function to be called by SDIO stack when
+ * device is disconnected
+ */
+static void cw1200_sdio_disconnect(struct sdio_func *func)
+{
+       struct hwbus_priv *self = sdio_get_drvdata(func);
+
+       if (self) {
+               cw1200_sdio_irq_unsubscribe(self);
+               if (self->core) {
+                       cw1200_core_release(self->core);
+                       self->core = NULL;
+               }
+               sdio_claim_host(func);
+               sdio_disable_func(func);
+               sdio_release_host(func);
+               sdio_set_drvdata(func, NULL);
+               kfree(self);
+       }
+}
+
+#ifdef CONFIG_PM
+static int cw1200_sdio_suspend(struct device *dev)
+{
+       int ret;
+       struct sdio_func *func = dev_to_sdio_func(dev);
+       struct hwbus_priv *self = sdio_get_drvdata(func);
+
+       if (!cw1200_can_suspend(self->core))
+               return -EAGAIN;
+
+       /* Notify SDIO that CW1200 will remain powered during suspend */
+       ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+       if (ret)
+               pr_err("Error setting SDIO pm flags: %i\n", ret);
+
+       return ret;
+}
+
+static int cw1200_sdio_resume(struct device *dev)
+{
+       return 0;
+}
+
+static const struct dev_pm_ops cw1200_pm_ops = {
+       .suspend = cw1200_sdio_suspend,
+       .resume = cw1200_sdio_resume,
+};
+#endif
+
+static struct sdio_driver sdio_driver = {
+       .name           = "cw1200_wlan_sdio",
+       .id_table       = cw1200_sdio_ids,
+       .probe          = cw1200_sdio_probe,
+       .remove         = cw1200_sdio_disconnect,
+#ifdef CONFIG_PM
+       .drv = {
+               .pm = &cw1200_pm_ops,
+       }
+#endif
+};
+
+/* Init Module function -> Called by insmod */
+static int __init cw1200_sdio_init(void)
+{
+       const struct cw1200_platform_data_sdio *pdata;
+       int ret;
+
+       /* FIXME -- this won't support multiple devices */
+       pdata = global_plat_data;
+
+       if (cw1200_sdio_on(pdata)) {
+               ret = -1;
+               goto err;
+       }
+
+       ret = sdio_register_driver(&sdio_driver);
+       if (ret)
+               goto err;
+
+       return 0;
+
+err:
+       cw1200_sdio_off(pdata);
+       return ret;
+}
+
+/* Called at Driver Unloading */
+static void __exit cw1200_sdio_exit(void)
+{
+       const struct cw1200_platform_data_sdio *pdata;
+
+       /* FIXME -- this won't support multiple devices */
+       pdata = global_plat_data;
+       sdio_unregister_driver(&sdio_driver);
+       cw1200_sdio_off(pdata);
+}
+
+
+module_init(cw1200_sdio_init);
+module_exit(cw1200_sdio_exit);
diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c
new file mode 100644 (file)
index 0000000..d063760
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Mac80211 SPI driver for ST-Ericsson CW1200 device
+ *
+ * Copyright (c) 2011, Sagrad Inc.
+ * Author:  Solomon Peachy <speachy@sagrad.com>
+ *
+ * Based on cw1200_sdio.c
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/gpio.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <net/mac80211.h>
+
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+
+#include "cw1200.h"
+#include "hwbus.h"
+#include <linux/platform_data/net-cw1200.h>
+#include "hwio.h"
+
+MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
+MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:cw1200_wlan_spi");
+
+/* #define SPI_DEBUG */
+
+struct hwbus_priv {
+       struct spi_device       *func;
+       struct cw1200_common    *core;
+       const struct cw1200_platform_data_spi *pdata;
+       spinlock_t              lock; /* Serialize all bus operations */
+       int claimed;
+};
+
+#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
+#define SET_WRITE 0x7FFF /* usage: and operation */
+#define SET_READ 0x8000  /* usage: or operation */
+
+/* Notes on byte ordering:
+   LE:  B0 B1 B2 B3
+   BE:  B3 B2 B1 B0
+
+   Hardware expects 32-bit data to be written as 16-bit BE words:
+
+   B1 B0 B3 B2
+*/
+
+static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self,
+                                    unsigned int addr,
+                                    void *dst, int count)
+{
+       int ret, i;
+       u16 regaddr;
+       struct spi_message      m;
+
+       struct spi_transfer     t_addr = {
+               .tx_buf         = &regaddr,
+               .len            = sizeof(regaddr),
+       };
+       struct spi_transfer     t_msg = {
+               .rx_buf         = dst,
+               .len            = count,
+       };
+
+       regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
+       regaddr |= SET_READ;
+       regaddr |= (count>>1);
+
+#ifdef SPI_DEBUG
+       pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, regaddr);
+#endif
+
+       /* Header is LE16 */
+       regaddr = cpu_to_le16(regaddr);
+
+       /* We have to byteswap if the SPI bus is limited to 8b operation
+          or we are running on a Big Endian system
+       */
+#if defined(__LITTLE_ENDIAN)
+       if (self->func->bits_per_word == 8)
+#endif
+               regaddr = swab16(regaddr);
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t_addr, &m);
+       spi_message_add_tail(&t_msg, &m);
+       ret = spi_sync(self->func, &m);
+
+#ifdef SPI_DEBUG
+       pr_info("READ : ");
+       for (i = 0; i < t_addr.len; i++)
+               printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
+       printk(" : ");
+       for (i = 0; i < t_msg.len; i++)
+               printk("%02x ", ((u8 *)t_msg.rx_buf)[i]);
+       printk("\n");
+#endif
+
+       /* We have to byteswap if the SPI bus is limited to 8b operation
+          or we are running on a Big Endian system
+       */
+#if defined(__LITTLE_ENDIAN)
+       if (self->func->bits_per_word == 8)
+#endif
+       {
+               uint16_t *buf = (uint16_t *)dst;
+               for (i = 0; i < ((count + 1) >> 1); i++)
+                       buf[i] = swab16(buf[i]);
+       }
+
+       return ret;
+}
+
+static int cw1200_spi_memcpy_toio(struct hwbus_priv *self,
+                                  unsigned int addr,
+                                  const void *src, int count)
+{
+       int rval, i;
+       u16 regaddr;
+       struct spi_transfer     t_addr = {
+               .tx_buf         = &regaddr,
+               .len            = sizeof(regaddr),
+       };
+       struct spi_transfer     t_msg = {
+               .tx_buf         = src,
+               .len            = count,
+       };
+       struct spi_message      m;
+
+       regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
+       regaddr &= SET_WRITE;
+       regaddr |= (count>>1);
+
+#ifdef SPI_DEBUG
+       pr_info("WRITE: %04d  to  0x%02x (%04x)\n", count, addr, regaddr);
+#endif
+
+       /* Header is LE16 */
+       regaddr = cpu_to_le16(regaddr);
+
+       /* We have to byteswap if the SPI bus is limited to 8b operation
+          or we are running on a Big Endian system
+       */
+#if defined(__LITTLE_ENDIAN)
+       if (self->func->bits_per_word == 8)
+#endif
+       {
+               uint16_t *buf = (uint16_t *)src;
+               regaddr = swab16(regaddr);
+               for (i = 0; i < ((count + 1) >> 1); i++)
+                       buf[i] = swab16(buf[i]);
+       }
+
+#ifdef SPI_DEBUG
+       pr_info("WRITE: ");
+       for (i = 0; i < t_addr.len; i++)
+               printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
+       printk(" : ");
+       for (i = 0; i < t_msg.len; i++)
+               printk("%02x ", ((u8 *)t_msg.tx_buf)[i]);
+       printk("\n");
+#endif
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t_addr, &m);
+       spi_message_add_tail(&t_msg, &m);
+       rval = spi_sync(self->func, &m);
+
+#ifdef SPI_DEBUG
+       pr_info("WROTE: %d\n", m.actual_length);
+#endif
+
+#if defined(__LITTLE_ENDIAN)
+       /* We have to byteswap if the SPI bus is limited to 8b operation */
+       if (self->func->bits_per_word == 8)
+#endif
+       {
+               uint16_t *buf = (uint16_t *)src;
+               for (i = 0; i < ((count + 1) >> 1); i++)
+                       buf[i] = swab16(buf[i]);
+       }
+       return rval;
+}
+
+static void cw1200_spi_lock(struct hwbus_priv *self)
+{
+       unsigned long flags;
+
+       might_sleep();
+
+       spin_lock_irqsave(&self->lock, flags);
+       while (1) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               if (!self->claimed)
+                       break;
+               spin_unlock_irqrestore(&self->lock, flags);
+               schedule();
+               spin_lock_irqsave(&self->lock, flags);
+       }
+       set_current_state(TASK_RUNNING);
+       self->claimed = 1;
+       spin_unlock_irqrestore(&self->lock, flags);
+
+       return;
+}
+
+static void cw1200_spi_unlock(struct hwbus_priv *self)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&self->lock, flags);
+       self->claimed = 0;
+       spin_unlock_irqrestore(&self->lock, flags);
+       return;
+}
+
+static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
+{
+       struct hwbus_priv *self = dev_id;
+
+       if (self->core) {
+               cw1200_irq_handler(self->core);
+               return IRQ_HANDLED;
+       } else {
+               return IRQ_NONE;
+       }
+}
+
+static int cw1200_spi_irq_subscribe(struct hwbus_priv *self)
+{
+       int ret;
+
+       pr_debug("SW IRQ subscribe\n");
+
+       ret = request_any_context_irq(self->func->irq, cw1200_spi_irq_handler,
+                                     IRQF_TRIGGER_HIGH,
+                                     "cw1200_wlan_irq", self);
+       if (WARN_ON(ret < 0))
+               goto exit;
+
+       ret = enable_irq_wake(self->func->irq);
+       if (WARN_ON(ret))
+               goto free_irq;
+
+       return 0;
+
+free_irq:
+       free_irq(self->func->irq, self);
+exit:
+       return ret;
+}
+
+static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self)
+{
+       int ret = 0;
+
+       pr_debug("SW IRQ unsubscribe\n");
+       disable_irq_wake(self->func->irq);
+       free_irq(self->func->irq, self);
+
+       return ret;
+}
+
+static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
+{
+       if (pdata->reset) {
+               gpio_set_value(pdata->reset, 0);
+               msleep(30); /* Min is 2 * CLK32K cycles */
+               gpio_free(pdata->reset);
+       }
+
+       if (pdata->power_ctrl)
+               pdata->power_ctrl(pdata, false);
+       if (pdata->clk_ctrl)
+               pdata->clk_ctrl(pdata, false);
+
+       return 0;
+}
+
+static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata)
+{
+       /* Ensure I/Os are pulled low */
+       if (pdata->reset) {
+               gpio_request(pdata->reset, "cw1200_wlan_reset");
+               gpio_direction_output(pdata->reset, 0);
+       }
+       if (pdata->powerup) {
+               gpio_request(pdata->powerup, "cw1200_wlan_powerup");
+               gpio_direction_output(pdata->powerup, 0);
+       }
+       if (pdata->reset || pdata->powerup)
+               msleep(10); /* Settle time? */
+
+       /* Enable 3v3 and 1v8 to hardware */
+       if (pdata->power_ctrl) {
+               if (pdata->power_ctrl(pdata, true)) {
+                       pr_err("power_ctrl() failed!\n");
+                       return -1;
+               }
+       }
+
+       /* Enable CLK32K */
+       if (pdata->clk_ctrl) {
+               if (pdata->clk_ctrl(pdata, true)) {
+                       pr_err("clk_ctrl() failed!\n");
+                       return -1;
+               }
+               msleep(10); /* Delay until clock is stable for 2 cycles */
+       }
+
+       /* Enable POWERUP signal */
+       if (pdata->powerup) {
+               gpio_set_value(pdata->powerup, 1);
+               msleep(250); /* or more..? */
+       }
+       /* Enable RSTn signal */
+       if (pdata->reset) {
+               gpio_set_value(pdata->reset, 1);
+               msleep(50); /* Or more..? */
+       }
+       return 0;
+}
+
+static size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size)
+{
+       return size & 1 ? size + 1 : size;
+}
+
+static int cw1200_spi_pm(struct hwbus_priv *self, bool suspend)
+{
+       return irq_set_irq_wake(self->func->irq, suspend);
+}
+
+static struct hwbus_ops cw1200_spi_hwbus_ops = {
+       .hwbus_memcpy_fromio    = cw1200_spi_memcpy_fromio,
+       .hwbus_memcpy_toio      = cw1200_spi_memcpy_toio,
+       .lock                   = cw1200_spi_lock,
+       .unlock                 = cw1200_spi_unlock,
+       .align_size             = cw1200_spi_align_size,
+       .power_mgmt             = cw1200_spi_pm,
+};
+
+/* Probe Function to be called by SPI stack when device is discovered */
+static int cw1200_spi_probe(struct spi_device *func)
+{
+       const struct cw1200_platform_data_spi *plat_data =
+               func->dev.platform_data;
+       struct hwbus_priv *self;
+       int status;
+
+       /* Sanity check speed */
+       if (func->max_speed_hz > 52000000)
+               func->max_speed_hz = 52000000;
+       if (func->max_speed_hz < 1000000)
+               func->max_speed_hz = 1000000;
+
+       /* Fix up transfer size */
+       if (plat_data->spi_bits_per_word)
+               func->bits_per_word = plat_data->spi_bits_per_word;
+       if (!func->bits_per_word)
+               func->bits_per_word = 16;
+
+       /* And finally.. */
+       func->mode = SPI_MODE_0;
+
+       pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n",
+               func->chip_select, func->mode, func->bits_per_word,
+               func->max_speed_hz);
+
+       if (cw1200_spi_on(plat_data)) {
+               pr_err("spi_on() failed!\n");
+               return -1;
+       }
+
+       if (spi_setup(func)) {
+               pr_err("spi_setup() failed!\n");
+               return -1;
+       }
+
+       self = kzalloc(sizeof(*self), GFP_KERNEL);
+       if (!self) {
+               pr_err("Can't allocate SPI hwbus_priv.");
+               return -ENOMEM;
+       }
+
+       self->pdata = plat_data;
+       self->func = func;
+       spin_lock_init(&self->lock);
+
+       spi_set_drvdata(func, self);
+
+       status = cw1200_spi_irq_subscribe(self);
+
+       status = cw1200_core_probe(&cw1200_spi_hwbus_ops,
+                                  self, &func->dev, &self->core,
+                                  self->pdata->ref_clk,
+                                  self->pdata->macaddr,
+                                  self->pdata->sdd_file,
+                                  self->pdata->have_5ghz);
+
+       if (status) {
+               cw1200_spi_irq_unsubscribe(self);
+               cw1200_spi_off(plat_data);
+               kfree(self);
+       }
+
+       return status;
+}
+
+/* Disconnect Function to be called by SPI stack when device is disconnected */
+static int cw1200_spi_disconnect(struct spi_device *func)
+{
+       struct hwbus_priv *self = spi_get_drvdata(func);
+
+       if (self) {
+               cw1200_spi_irq_unsubscribe(self);
+               if (self->core) {
+                       cw1200_core_release(self->core);
+                       self->core = NULL;
+               }
+               kfree(self);
+       }
+       cw1200_spi_off(func->dev.platform_data);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int cw1200_spi_suspend(struct device *dev, pm_message_t state)
+{
+       struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev));
+
+       if (!cw1200_can_suspend(self->core))
+               return -EAGAIN;
+
+       /* XXX notify host that we have to keep CW1200 powered on? */
+       return 0;
+}
+
+static int cw1200_spi_resume(struct device *dev)
+{
+       return 0;
+}
+#endif
+
+static struct spi_driver spi_driver = {
+       .probe          = cw1200_spi_probe,
+       .remove         = cw1200_spi_disconnect,
+       .driver = {
+               .name           = "cw1200_wlan_spi",
+               .bus            = &spi_bus_type,
+               .owner          = THIS_MODULE,
+#ifdef CONFIG_PM
+               .suspend        = cw1200_spi_suspend,
+               .resume         = cw1200_spi_resume,
+#endif
+       },
+};
+
+module_spi_driver(spi_driver);
diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c
new file mode 100644 (file)
index 0000000..e323b4d
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
+ * DebugFS code
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/debugfs.h>
+#include <linux/seq_file.h>
+#include "cw1200.h"
+#include "debug.h"
+#include "fwio.h"
+
+/* join_status */
+static const char * const cw1200_debug_join_status[] = {
+       "passive",
+       "monitor",
+       "station (joining)",
+       "station (not authenticated yet)",
+       "station",
+       "adhoc",
+       "access point",
+};
+
+/* WSM_JOIN_PREAMBLE_... */
+static const char * const cw1200_debug_preamble[] = {
+       "long",
+       "short",
+       "long on 1 and 2 Mbps",
+};
+
+
+static const char * const cw1200_debug_link_id[] = {
+       "OFF",
+       "REQ",
+       "SOFT",
+       "HARD",
+};
+
+static const char *cw1200_debug_mode(int mode)
+{
+       switch (mode) {
+       case NL80211_IFTYPE_UNSPECIFIED:
+               return "unspecified";
+       case NL80211_IFTYPE_MONITOR:
+               return "monitor";
+       case NL80211_IFTYPE_STATION:
+               return "station";
+       case NL80211_IFTYPE_ADHOC:
+               return "adhoc";
+       case NL80211_IFTYPE_MESH_POINT:
+               return "mesh point";
+       case NL80211_IFTYPE_AP:
+               return "access point";
+       case NL80211_IFTYPE_P2P_CLIENT:
+               return "p2p client";
+       case NL80211_IFTYPE_P2P_GO:
+               return "p2p go";
+       default:
+               return "unsupported";
+       }
+}
+
+static void cw1200_queue_status_show(struct seq_file *seq,
+                                    struct cw1200_queue *q)
+{
+       int i;
+       seq_printf(seq, "Queue       %d:\n", q->queue_id);
+       seq_printf(seq, "  capacity: %zu\n", q->capacity);
+       seq_printf(seq, "  queued:   %zu\n", q->num_queued);
+       seq_printf(seq, "  pending:  %zu\n", q->num_pending);
+       seq_printf(seq, "  sent:     %zu\n", q->num_sent);
+       seq_printf(seq, "  locked:   %s\n", q->tx_locked_cnt ? "yes" : "no");
+       seq_printf(seq, "  overfull: %s\n", q->overfull ? "yes" : "no");
+       seq_puts(seq,   "  link map: 0-> ");
+       for (i = 0; i < q->stats->map_capacity; ++i)
+               seq_printf(seq, "%.2d ", q->link_map_cache[i]);
+       seq_printf(seq, "<-%zu\n", q->stats->map_capacity);
+}
+
+static void cw1200_debug_print_map(struct seq_file *seq,
+                                  struct cw1200_common *priv,
+                                  const char *label,
+                                  u32 map)
+{
+       int i;
+       seq_printf(seq, "%s0-> ", label);
+       for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i)
+               seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : "..");
+       seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1);
+}
+
+static int cw1200_status_show(struct seq_file *seq, void *v)
+{
+       int i;
+       struct list_head *item;
+       struct cw1200_common *priv = seq->private;
+       struct cw1200_debug_priv *d = priv->debug;
+
+       seq_puts(seq,   "CW1200 Wireless LAN driver status\n");
+       seq_printf(seq, "Hardware:   %d.%d\n",
+                  priv->wsm_caps.hw_id,
+                  priv->wsm_caps.hw_subid);
+       seq_printf(seq, "Firmware:   %s %d.%d\n",
+                  cw1200_fw_types[priv->wsm_caps.fw_type],
+                  priv->wsm_caps.fw_ver,
+                  priv->wsm_caps.fw_build);
+       seq_printf(seq, "FW API:     %d\n",
+                  priv->wsm_caps.fw_api);
+       seq_printf(seq, "FW caps:    0x%.4X\n",
+                  priv->wsm_caps.fw_cap);
+       seq_printf(seq, "FW label:  '%s'\n",
+                  priv->wsm_caps.fw_label);
+       seq_printf(seq, "Mode:       %s%s\n",
+                  cw1200_debug_mode(priv->mode),
+                  priv->listening ? " (listening)" : "");
+       seq_printf(seq, "Join state: %s\n",
+                  cw1200_debug_join_status[priv->join_status]);
+       if (priv->channel)
+               seq_printf(seq, "Channel:    %d%s\n",
+                          priv->channel->hw_value,
+                          priv->channel_switch_in_progress ?
+                          " (switching)" : "");
+       if (priv->rx_filter.promiscuous)
+               seq_puts(seq,   "Filter:     promisc\n");
+       else if (priv->rx_filter.fcs)
+               seq_puts(seq,   "Filter:     fcs\n");
+       if (priv->rx_filter.bssid)
+               seq_puts(seq,   "Filter:     bssid\n");
+       if (!priv->disable_beacon_filter)
+               seq_puts(seq,   "Filter:     beacons\n");
+
+       if (priv->enable_beacon ||
+           priv->mode == NL80211_IFTYPE_AP ||
+           priv->mode == NL80211_IFTYPE_ADHOC ||
+           priv->mode == NL80211_IFTYPE_MESH_POINT ||
+           priv->mode == NL80211_IFTYPE_P2P_GO)
+               seq_printf(seq, "Beaconing:  %s\n",
+                          priv->enable_beacon ?
+                          "enabled" : "disabled");
+
+       for (i = 0; i < 4; ++i)
+               seq_printf(seq, "EDCA(%d):    %d, %d, %d, %d, %d\n", i,
+                          priv->edca.params[i].cwmin,
+                          priv->edca.params[i].cwmax,
+                          priv->edca.params[i].aifns,
+                          priv->edca.params[i].txop_limit,
+                          priv->edca.params[i].max_rx_lifetime);
+
+       if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+               static const char *pm_mode = "unknown";
+               switch (priv->powersave_mode.mode) {
+               case WSM_PSM_ACTIVE:
+                       pm_mode = "off";
+                       break;
+               case WSM_PSM_PS:
+                       pm_mode = "on";
+                       break;
+               case WSM_PSM_FAST_PS:
+                       pm_mode = "dynamic";
+                       break;
+               }
+               seq_printf(seq, "Preamble:   %s\n",
+                          cw1200_debug_preamble[priv->association_mode.preamble]);
+               seq_printf(seq, "AMPDU spcn: %d\n",
+                          priv->association_mode.mpdu_start_spacing);
+               seq_printf(seq, "Basic rate: 0x%.8X\n",
+                          le32_to_cpu(priv->association_mode.basic_rate_set));
+               seq_printf(seq, "Bss lost:   %d beacons\n",
+                          priv->bss_params.beacon_lost_count);
+               seq_printf(seq, "AID:        %d\n",
+                          priv->bss_params.aid);
+               seq_printf(seq, "Rates:      0x%.8X\n",
+                          priv->bss_params.operational_rate_set);
+               seq_printf(seq, "Powersave:  %s\n", pm_mode);
+       }
+       seq_printf(seq, "HT:         %s\n",
+                  cw1200_is_ht(&priv->ht_info) ? "on" : "off");
+       if (cw1200_is_ht(&priv->ht_info)) {
+               seq_printf(seq, "Greenfield: %s\n",
+                          cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no");
+               seq_printf(seq, "AMPDU dens: %d\n",
+                          cw1200_ht_ampdu_density(&priv->ht_info));
+       }
+       seq_printf(seq, "RSSI thold: %d\n",
+                  priv->cqm_rssi_thold);
+       seq_printf(seq, "RSSI hyst:  %d\n",
+                  priv->cqm_rssi_hyst);
+       seq_printf(seq, "Long retr:  %d\n",
+                  priv->long_frame_max_tx_count);
+       seq_printf(seq, "Short retr: %d\n",
+                  priv->short_frame_max_tx_count);
+       spin_lock_bh(&priv->tx_policy_cache.lock);
+       i = 0;
+       list_for_each(item, &priv->tx_policy_cache.used)
+               ++i;
+       spin_unlock_bh(&priv->tx_policy_cache.lock);
+       seq_printf(seq, "RC in use:  %d\n", i);
+
+       seq_puts(seq, "\n");
+       for (i = 0; i < 4; ++i) {
+               cw1200_queue_status_show(seq, &priv->tx_queue[i]);
+               seq_puts(seq, "\n");
+       }
+
+       cw1200_debug_print_map(seq, priv, "Link map:   ",
+                              priv->link_id_map);
+       cw1200_debug_print_map(seq, priv, "Asleep map: ",
+                              priv->sta_asleep_mask);
+       cw1200_debug_print_map(seq, priv, "PSPOLL map: ",
+                              priv->pspoll_mask);
+
+       seq_puts(seq, "\n");
+
+       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+               if (priv->link_id_db[i].status) {
+                       seq_printf(seq, "Link %d:     %s, %pM\n",
+                                  i + 1,
+                                  cw1200_debug_link_id[priv->link_id_db[i].status],
+                                  priv->link_id_db[i].mac);
+               }
+       }
+
+       seq_puts(seq, "\n");
+
+       seq_printf(seq, "BH status:  %s\n",
+                  atomic_read(&priv->bh_term) ? "terminated" : "alive");
+       seq_printf(seq, "Pending RX: %d\n",
+                  atomic_read(&priv->bh_rx));
+       seq_printf(seq, "Pending TX: %d\n",
+                  atomic_read(&priv->bh_tx));
+       if (priv->bh_error)
+               seq_printf(seq, "BH errcode: %d\n",
+                          priv->bh_error);
+       seq_printf(seq, "TX bufs:    %d x %d bytes\n",
+                  priv->wsm_caps.input_buffers,
+                  priv->wsm_caps.input_buffer_size);
+       seq_printf(seq, "Used bufs:  %d\n",
+                  priv->hw_bufs_used);
+       seq_printf(seq, "Powermgmt:  %s\n",
+                  priv->powersave_enabled ? "on" : "off");
+       seq_printf(seq, "Device:     %s\n",
+                  priv->device_can_sleep ? "asleep" : "awake");
+
+       spin_lock(&priv->wsm_cmd.lock);
+       seq_printf(seq, "WSM status: %s\n",
+                  priv->wsm_cmd.done ? "idle" : "active");
+       seq_printf(seq, "WSM cmd:    0x%.4X (%td bytes)\n",
+                  priv->wsm_cmd.cmd, priv->wsm_cmd.len);
+       seq_printf(seq, "WSM retval: %d\n",
+                  priv->wsm_cmd.ret);
+       spin_unlock(&priv->wsm_cmd.lock);
+
+       seq_printf(seq, "Datapath:   %s\n",
+                  atomic_read(&priv->tx_lock) ? "locked" : "unlocked");
+       if (atomic_read(&priv->tx_lock))
+               seq_printf(seq, "TXlock cnt: %d\n",
+                          atomic_read(&priv->tx_lock));
+
+       seq_printf(seq, "TXed:       %d\n",
+                  d->tx);
+       seq_printf(seq, "AGG TXed:   %d\n",
+                  d->tx_agg);
+       seq_printf(seq, "MULTI TXed: %d (%d)\n",
+                  d->tx_multi, d->tx_multi_frames);
+       seq_printf(seq, "RXed:       %d\n",
+                  d->rx);
+       seq_printf(seq, "AGG RXed:   %d\n",
+                  d->rx_agg);
+       seq_printf(seq, "TX miss:    %d\n",
+                  d->tx_cache_miss);
+       seq_printf(seq, "TX align:   %d\n",
+                  d->tx_align);
+       seq_printf(seq, "TX burst:   %d\n",
+                  d->tx_burst);
+       seq_printf(seq, "TX TTL:     %d\n",
+                  d->tx_ttl);
+       seq_printf(seq, "Scan:       %s\n",
+                  atomic_read(&priv->scan.in_progress) ? "active" : "idle");
+
+       return 0;
+}
+
+static int cw1200_status_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, &cw1200_status_show,
+               inode->i_private);
+}
+
+static const struct file_operations fops_status = {
+       .open = cw1200_status_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int cw1200_counters_show(struct seq_file *seq, void *v)
+{
+       int ret;
+       struct cw1200_common *priv = seq->private;
+       struct wsm_mib_counters_table counters;
+
+       ret = wsm_get_counters_table(priv, &counters);
+       if (ret)
+               return ret;
+
+#define PUT_COUNTER(tab, name) \
+       seq_printf(seq, "%s:" tab "%d\n", #name, \
+               __le32_to_cpu(counters.name))
+
+       PUT_COUNTER("\t\t", plcp_errors);
+       PUT_COUNTER("\t\t", fcs_errors);
+       PUT_COUNTER("\t\t", tx_packets);
+       PUT_COUNTER("\t\t", rx_packets);
+       PUT_COUNTER("\t\t", rx_packet_errors);
+       PUT_COUNTER("\t",   rx_decryption_failures);
+       PUT_COUNTER("\t\t", rx_mic_failures);
+       PUT_COUNTER("\t",   rx_no_key_failures);
+       PUT_COUNTER("\t",   tx_multicast_frames);
+       PUT_COUNTER("\t",   tx_frames_success);
+       PUT_COUNTER("\t",   tx_frame_failures);
+       PUT_COUNTER("\t",   tx_frames_retried);
+       PUT_COUNTER("\t",   tx_frames_multi_retried);
+       PUT_COUNTER("\t",   rx_frame_duplicates);
+       PUT_COUNTER("\t\t", rts_success);
+       PUT_COUNTER("\t\t", rts_failures);
+       PUT_COUNTER("\t\t", ack_failures);
+       PUT_COUNTER("\t",   rx_multicast_frames);
+       PUT_COUNTER("\t",   rx_frames_success);
+       PUT_COUNTER("\t",   rx_cmac_icv_errors);
+       PUT_COUNTER("\t\t", rx_cmac_replays);
+       PUT_COUNTER("\t",   rx_mgmt_ccmp_replays);
+
+#undef PUT_COUNTER
+
+       return 0;
+}
+
+static int cw1200_counters_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, &cw1200_counters_show,
+               inode->i_private);
+}
+
+static const struct file_operations fops_counters = {
+       .open = cw1200_counters_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static ssize_t cw1200_wsm_dumps(struct file *file,
+       const char __user *user_buf, size_t count, loff_t *ppos)
+{
+       struct cw1200_common *priv = file->private_data;
+       char buf[1];
+
+       if (!count)
+               return -EINVAL;
+       if (copy_from_user(buf, user_buf, 1))
+               return -EFAULT;
+
+       if (buf[0] == '1')
+               priv->wsm_enable_wsm_dumps = 1;
+       else
+               priv->wsm_enable_wsm_dumps = 0;
+
+       return count;
+}
+
+static const struct file_operations fops_wsm_dumps = {
+       .open = simple_open,
+       .write = cw1200_wsm_dumps,
+       .llseek = default_llseek,
+};
+
+int cw1200_debug_init(struct cw1200_common *priv)
+{
+       int ret = -ENOMEM;
+       struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv),
+                       GFP_KERNEL);
+       priv->debug = d;
+       if (!d)
+               return ret;
+
+       d->debugfs_phy = debugfs_create_dir("cw1200",
+                                           priv->hw->wiphy->debugfsdir);
+       if (!d->debugfs_phy)
+               goto err;
+
+       if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
+                                priv, &fops_status))
+               goto err;
+
+       if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy,
+                                priv, &fops_counters))
+               goto err;
+
+       if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy,
+                                priv, &fops_wsm_dumps))
+               goto err;
+
+       return 0;
+
+err:
+       priv->debug = NULL;
+       debugfs_remove_recursive(d->debugfs_phy);
+       kfree(d);
+       return ret;
+}
+
+void cw1200_debug_release(struct cw1200_common *priv)
+{
+       struct cw1200_debug_priv *d = priv->debug;
+       if (d) {
+               debugfs_remove_recursive(d->debugfs_phy);
+               priv->debug = NULL;
+               kfree(d);
+       }
+}
diff --git a/drivers/net/wireless/cw1200/debug.h b/drivers/net/wireless/cw1200/debug.h
new file mode 100644 (file)
index 0000000..b525aba
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * DebugFS code for ST-Ericsson CW1200 mac80211 driver
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 CW1200_DEBUG_H_INCLUDED
+#define CW1200_DEBUG_H_INCLUDED
+
+struct cw1200_debug_priv {
+       struct dentry *debugfs_phy;
+       int tx;
+       int tx_agg;
+       int rx;
+       int rx_agg;
+       int tx_multi;
+       int tx_multi_frames;
+       int tx_cache_miss;
+       int tx_align;
+       int tx_ttl;
+       int tx_burst;
+       int ba_cnt;
+       int ba_acc;
+       int ba_cnt_rx;
+       int ba_acc_rx;
+};
+
+int cw1200_debug_init(struct cw1200_common *priv);
+void cw1200_debug_release(struct cw1200_common *priv);
+
+static inline void cw1200_debug_txed(struct cw1200_common *priv)
+{
+       ++priv->debug->tx;
+}
+
+static inline void cw1200_debug_txed_agg(struct cw1200_common *priv)
+{
+       ++priv->debug->tx_agg;
+}
+
+static inline void cw1200_debug_txed_multi(struct cw1200_common *priv,
+                                          int count)
+{
+       ++priv->debug->tx_multi;
+       priv->debug->tx_multi_frames += count;
+}
+
+static inline void cw1200_debug_rxed(struct cw1200_common *priv)
+{
+       ++priv->debug->rx;
+}
+
+static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv)
+{
+       ++priv->debug->rx_agg;
+}
+
+static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv)
+{
+       ++priv->debug->tx_cache_miss;
+}
+
+static inline void cw1200_debug_tx_align(struct cw1200_common *priv)
+{
+       ++priv->debug->tx_align;
+}
+
+static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv)
+{
+       ++priv->debug->tx_ttl;
+}
+
+static inline void cw1200_debug_tx_burst(struct cw1200_common *priv)
+{
+       ++priv->debug->tx_burst;
+}
+
+static inline void cw1200_debug_ba(struct cw1200_common *priv,
+                                  int ba_cnt, int ba_acc,
+                                  int ba_cnt_rx, int ba_acc_rx)
+{
+       priv->debug->ba_cnt = ba_cnt;
+       priv->debug->ba_acc = ba_acc;
+       priv->debug->ba_cnt_rx = ba_cnt_rx;
+       priv->debug->ba_acc_rx = ba_acc_rx;
+}
+
+#endif /* CW1200_DEBUG_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c
new file mode 100644 (file)
index 0000000..acdff0f
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "cw1200.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "hwbus.h"
+#include "bh.h"
+
+static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision)
+{
+       int hw_type = -1;
+       u32 silicon_type = (config_reg_val >> 24) & 0x7;
+       u32 silicon_vers = (config_reg_val >> 31) & 0x1;
+
+       switch (silicon_type) {
+       case 0x00:
+               *major_revision = 1;
+               hw_type = HIF_9000_SILICON_VERSATILE;
+               break;
+       case 0x01:
+       case 0x02: /* CW1x00 */
+       case 0x04: /* CW1x60 */
+               *major_revision = silicon_type;
+               if (silicon_vers)
+                       hw_type = HIF_8601_VERSATILE;
+               else
+                       hw_type = HIF_8601_SILICON;
+               break;
+       default:
+               break;
+       }
+
+       return hw_type;
+}
+
+static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
+{
+       int ret, block, num_blocks;
+       unsigned i;
+       u32 val32;
+       u32 put = 0, get = 0;
+       u8 *buf = NULL;
+       const char *fw_path;
+       const struct firmware *firmware = NULL;
+
+       /* Macroses are local. */
+#define APB_WRITE(reg, val) \
+       do { \
+               ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
+               if (ret < 0) \
+                       goto error; \
+       } while (0)
+#define APB_READ(reg, val) \
+       do { \
+               ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \
+               if (ret < 0) \
+                       goto error; \
+       } while (0)
+#define REG_WRITE(reg, val) \
+       do { \
+               ret = cw1200_reg_write_32(priv, (reg), (val)); \
+               if (ret < 0) \
+                       goto error; \
+       } while (0)
+#define REG_READ(reg, val) \
+       do { \
+               ret = cw1200_reg_read_32(priv, (reg), &(val)); \
+               if (ret < 0) \
+                       goto error; \
+       } while (0)
+
+       switch (priv->hw_revision) {
+       case CW1200_HW_REV_CUT10:
+               fw_path = FIRMWARE_CUT10;
+               if (!priv->sdd_path)
+                       priv->sdd_path = SDD_FILE_10;
+               break;
+       case CW1200_HW_REV_CUT11:
+               fw_path = FIRMWARE_CUT11;
+               if (!priv->sdd_path)
+                       priv->sdd_path = SDD_FILE_11;
+               break;
+       case CW1200_HW_REV_CUT20:
+               fw_path = FIRMWARE_CUT20;
+               if (!priv->sdd_path)
+                       priv->sdd_path = SDD_FILE_20;
+               break;
+       case CW1200_HW_REV_CUT22:
+               fw_path = FIRMWARE_CUT22;
+               if (!priv->sdd_path)
+                       priv->sdd_path = SDD_FILE_22;
+               break;
+       case CW1X60_HW_REV:
+               fw_path = FIRMWARE_CW1X60;
+               if (!priv->sdd_path)
+                       priv->sdd_path = SDD_FILE_CW1X60;
+               break;
+       default:
+               pr_err("Invalid silicon revision %d.\n", priv->hw_revision);
+               return -EINVAL;
+       }
+
+       /* Initialize common registers */
+       APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE);
+       APB_WRITE(DOWNLOAD_PUT_REG, 0);
+       APB_WRITE(DOWNLOAD_GET_REG, 0);
+       APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING);
+       APB_WRITE(DOWNLOAD_FLAGS_REG, 0);
+
+       /* Write the NOP Instruction */
+       REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000);
+       REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE);
+
+       /* Release CPU from RESET */
+       REG_READ(ST90TDS_CONFIG_REG_ID, val32);
+       val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT;
+       REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
+
+       /* Enable Clock */
+       val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT;
+       REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
+
+       /* Load a firmware file */
+       ret = request_firmware(&firmware, fw_path, priv->pdev);
+       if (ret) {
+               pr_err("Can't load firmware file %s.\n", fw_path);
+               goto error;
+       }
+
+       buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
+       if (!buf) {
+               pr_err("Can't allocate firmware load buffer.\n");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       /* Check if the bootloader is ready */
+       for (i = 0; i < 100; i += 1 + i / 2) {
+               APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32);
+               if (val32 == DOWNLOAD_I_AM_HERE)
+                       break;
+               mdelay(i);
+       } /* End of for loop */
+
+       if (val32 != DOWNLOAD_I_AM_HERE) {
+               pr_err("Bootloader is not ready.\n");
+               ret = -ETIMEDOUT;
+               goto error;
+       }
+
+       /* Calculcate number of download blocks */
+       num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1;
+
+       /* Updating the length in Download Ctrl Area */
+       val32 = firmware->size; /* Explicit cast from size_t to u32 */
+       APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32);
+
+       /* Firmware downloading loop */
+       for (block = 0; block < num_blocks; block++) {
+               size_t tx_size;
+               size_t block_size;
+
+               /* check the download status */
+               APB_READ(DOWNLOAD_STATUS_REG, val32);
+               if (val32 != DOWNLOAD_PENDING) {
+                       pr_err("Bootloader reported error %d.\n", val32);
+                       ret = -EIO;
+                       goto error;
+               }
+
+               /* loop until put - get <= 24K */
+               for (i = 0; i < 100; i++) {
+                       APB_READ(DOWNLOAD_GET_REG, get);
+                       if ((put - get) <=
+                           (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE))
+                               break;
+                       mdelay(i);
+               }
+
+               if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) {
+                       pr_err("Timeout waiting for FIFO.\n");
+                       ret = -ETIMEDOUT;
+                       goto error;
+               }
+
+               /* calculate the block size */
+               tx_size = block_size = min((size_t)(firmware->size - put),
+                       (size_t)DOWNLOAD_BLOCK_SIZE);
+
+               memcpy(buf, &firmware->data[put], block_size);
+               if (block_size < DOWNLOAD_BLOCK_SIZE) {
+                       memset(&buf[block_size], 0,
+                              DOWNLOAD_BLOCK_SIZE - block_size);
+                       tx_size = DOWNLOAD_BLOCK_SIZE;
+               }
+
+               /* send the block to sram */
+               ret = cw1200_apb_write(priv,
+                       CW1200_APB(DOWNLOAD_FIFO_OFFSET +
+                                  (put & (DOWNLOAD_FIFO_SIZE - 1))),
+                       buf, tx_size);
+               if (ret < 0) {
+                       pr_err("Can't write firmware block @ %d!\n",
+                              put & (DOWNLOAD_FIFO_SIZE - 1));
+                       goto error;
+               }
+
+               /* update the put register */
+               put += block_size;
+               APB_WRITE(DOWNLOAD_PUT_REG, put);
+       } /* End of firmware download loop */
+
+       /* Wait for the download completion */
+       for (i = 0; i < 300; i += 1 + i / 2) {
+               APB_READ(DOWNLOAD_STATUS_REG, val32);
+               if (val32 != DOWNLOAD_PENDING)
+                       break;
+               mdelay(i);
+       }
+       if (val32 != DOWNLOAD_SUCCESS) {
+               pr_err("Wait for download completion failed: 0x%.8X\n", val32);
+               ret = -ETIMEDOUT;
+               goto error;
+       } else {
+               pr_info("Firmware download completed.\n");
+               ret = 0;
+       }
+
+error:
+       kfree(buf);
+       if (firmware)
+               release_firmware(firmware);
+       return ret;
+
+#undef APB_WRITE
+#undef APB_READ
+#undef REG_WRITE
+#undef REG_READ
+}
+
+
+static int config_reg_read(struct cw1200_common *priv, u32 *val)
+{
+       switch (priv->hw_type) {
+       case HIF_9000_SILICON_VERSATILE: {
+               u16 val16;
+               int ret = cw1200_reg_read_16(priv,
+                                            ST90TDS_CONFIG_REG_ID,
+                                            &val16);
+               if (ret < 0)
+                       return ret;
+               *val = val16;
+               return 0;
+       }
+       case HIF_8601_VERSATILE:
+       case HIF_8601_SILICON:
+       default:
+               cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val);
+               break;
+       }
+       return 0;
+}
+
+static int config_reg_write(struct cw1200_common *priv, u32 val)
+{
+       switch (priv->hw_type) {
+       case HIF_9000_SILICON_VERSATILE:
+               return cw1200_reg_write_16(priv,
+                                          ST90TDS_CONFIG_REG_ID,
+                                          (u16)val);
+       case HIF_8601_VERSATILE:
+       case HIF_8601_SILICON:
+       default:
+               return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val);
+               break;
+       }
+       return 0;
+}
+
+int cw1200_load_firmware(struct cw1200_common *priv)
+{
+       int ret;
+       int i;
+       u32 val32;
+       u16 val16;
+       int major_revision = -1;
+
+       /* Read CONFIG Register */
+       ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+       if (ret < 0) {
+               pr_err("Can't read config register.\n");
+               goto out;
+       }
+
+       if (val32 == 0 || val32 == 0xffffffff) {
+               pr_err("Bad config register value (0x%08x)\n", val32);
+               ret = -EIO;
+               goto out;
+       }
+
+       priv->hw_type = cw1200_get_hw_type(val32, &major_revision);
+       if (priv->hw_type < 0) {
+               pr_err("Can't deduce hardware type.\n");
+               ret = -ENOTSUPP;
+               goto out;
+       }
+
+       /* Set DPLL Reg value, and read back to confirm writes work */
+       ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
+                                 cw1200_dpll_from_clk(priv->hw_refclk));
+       if (ret < 0) {
+               pr_err("Can't write DPLL register.\n");
+               goto out;
+       }
+
+       msleep(20);
+
+       ret = cw1200_reg_read_32(priv,
+               ST90TDS_TSET_GEN_R_W_REG_ID, &val32);
+       if (ret < 0) {
+               pr_err("Can't read DPLL register.\n");
+               goto out;
+       }
+
+       if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) {
+               pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n",
+                      cw1200_dpll_from_clk(priv->hw_refclk), val32);
+               ret = -EIO;
+               goto out;
+       }
+
+       /* Set wakeup bit in device */
+       ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16);
+       if (ret < 0) {
+               pr_err("set_wakeup: can't read control register.\n");
+               goto out;
+       }
+
+       ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
+               val16 | ST90TDS_CONT_WUP_BIT);
+       if (ret < 0) {
+               pr_err("set_wakeup: can't write control register.\n");
+               goto out;
+       }
+
+       /* Wait for wakeup */
+       for (i = 0; i < 300; i += (1 + i / 2)) {
+               ret = cw1200_reg_read_16(priv,
+                       ST90TDS_CONTROL_REG_ID, &val16);
+               if (ret < 0) {
+                       pr_err("wait_for_wakeup: can't read control register.\n");
+                       goto out;
+               }
+
+               if (val16 & ST90TDS_CONT_RDY_BIT)
+                       break;
+
+               msleep(i);
+       }
+
+       if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) {
+               pr_err("wait_for_wakeup: device is not responding.\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       switch (major_revision) {
+       case 1:
+               /* CW1200 Hardware detection logic : Check for CUT1.1 */
+               ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32);
+               if (ret) {
+                       pr_err("HW detection: can't read CUT ID.\n");
+                       goto out;
+               }
+
+               switch (val32) {
+               case CW1200_CUT_11_ID_STR:
+                       pr_info("CW1x00 Cut 1.1 silicon detected.\n");
+                       priv->hw_revision = CW1200_HW_REV_CUT11;
+                       break;
+               default:
+                       pr_info("CW1x00 Cut 1.0 silicon detected.\n");
+                       priv->hw_revision = CW1200_HW_REV_CUT10;
+                       break;
+               }
+
+               /* According to ST-E, CUT<2.0 has busted BA TID0-3.
+                  Just disable it entirely...
+               */
+               priv->ba_rx_tid_mask = 0;
+               priv->ba_tx_tid_mask = 0;
+               break;
+       case 2: {
+               u32 ar1, ar2, ar3;
+               ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1);
+               if (ret) {
+                       pr_err("(1) HW detection: can't read CUT ID\n");
+                       goto out;
+               }
+               ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2);
+               if (ret) {
+                       pr_err("(2) HW detection: can't read CUT ID.\n");
+                       goto out;
+               }
+
+               ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3);
+               if (ret) {
+                       pr_err("(3) HW detection: can't read CUT ID.\n");
+                       goto out;
+               }
+
+               if (ar1 == CW1200_CUT_22_ID_STR1 &&
+                   ar2 == CW1200_CUT_22_ID_STR2 &&
+                   ar3 == CW1200_CUT_22_ID_STR3) {
+                       pr_info("CW1x00 Cut 2.2 silicon detected.\n");
+                       priv->hw_revision = CW1200_HW_REV_CUT22;
+               } else {
+                       pr_info("CW1x00 Cut 2.0 silicon detected.\n");
+                       priv->hw_revision = CW1200_HW_REV_CUT20;
+               }
+               break;
+       }
+       case 4:
+               pr_info("CW1x60 silicon detected.\n");
+               priv->hw_revision = CW1X60_HW_REV;
+               break;
+       default:
+               pr_err("Unsupported silicon major revision %d.\n",
+                      major_revision);
+               ret = -ENOTSUPP;
+               goto out;
+       }
+
+       /* Checking for access mode */
+       ret = config_reg_read(priv, &val32);
+       if (ret < 0) {
+               pr_err("Can't read config register.\n");
+               goto out;
+       }
+
+       if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) {
+               pr_err("Device is already in QUEUE mode!\n");
+                       ret = -EINVAL;
+                       goto out;
+       }
+
+       switch (priv->hw_type)  {
+       case HIF_8601_SILICON:
+               if (priv->hw_revision == CW1X60_HW_REV) {
+                       pr_err("Can't handle CW1160/1260 firmware load yet.\n");
+                       ret = -ENOTSUPP;
+                       goto out;
+               }
+               ret = cw1200_load_firmware_cw1200(priv);
+               break;
+       default:
+               pr_err("Can't perform firmware load for hw type %d.\n",
+                      priv->hw_type);
+               ret = -ENOTSUPP;
+               goto out;
+       }
+       if (ret < 0) {
+               pr_err("Firmware load error.\n");
+               goto out;
+       }
+
+       /* Enable interrupt signalling */
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       ret = __cw1200_irq_enable(priv, 1);
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       if (ret < 0)
+               goto unsubscribe;
+
+       /* Configure device for MESSSAGE MODE */
+       ret = config_reg_read(priv, &val32);
+       if (ret < 0) {
+               pr_err("Can't read config register.\n");
+               goto unsubscribe;
+       }
+       ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT);
+       if (ret < 0) {
+               pr_err("Can't write config register.\n");
+               goto unsubscribe;
+       }
+
+       /* Unless we read the CONFIG Register we are
+        * not able to get an interrupt
+        */
+       mdelay(10);
+       config_reg_read(priv, &val32);
+
+out:
+       return ret;
+
+unsubscribe:
+       /* Disable interrupt signalling */
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       ret = __cw1200_irq_enable(priv, 0);
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
diff --git a/drivers/net/wireless/cw1200/fwio.h b/drivers/net/wireless/cw1200/fwio.h
new file mode 100644 (file)
index 0000000..ea30993
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Firmware API for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.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 FWIO_H_INCLUDED
+#define FWIO_H_INCLUDED
+
+#define BOOTLOADER_CW1X60       "boot_cw1x60.bin"
+#define FIRMWARE_CW1X60                "wsm_cw1x60.bin"
+#define FIRMWARE_CUT22         "wsm_22.bin"
+#define FIRMWARE_CUT20         "wsm_20.bin"
+#define FIRMWARE_CUT11         "wsm_11.bin"
+#define FIRMWARE_CUT10         "wsm_10.bin"
+#define SDD_FILE_CW1X60                "sdd_cw1x60.bin"
+#define SDD_FILE_22            "sdd_22.bin"
+#define SDD_FILE_20            "sdd_20.bin"
+#define SDD_FILE_11            "sdd_11.bin"
+#define SDD_FILE_10            "sdd_10.bin"
+
+int cw1200_load_firmware(struct cw1200_common *priv);
+
+/* SDD definitions */
+#define SDD_PTA_CFG_ELT_ID 0xEB
+#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5
+u32 cw1200_dpll_from_clk(u16 clk);
+
+#endif
diff --git a/drivers/net/wireless/cw1200/hwbus.h b/drivers/net/wireless/cw1200/hwbus.h
new file mode 100644 (file)
index 0000000..8b2fc83
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Common hwbus abstraction layer interface for cw1200 wireless driver
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 CW1200_HWBUS_H
+#define CW1200_HWBUS_H
+
+struct hwbus_priv;
+
+void cw1200_irq_handler(struct cw1200_common *priv);
+
+/* This MUST be wrapped with hwbus_ops->lock/unlock! */
+int __cw1200_irq_enable(struct cw1200_common *priv, int enable);
+
+struct hwbus_ops {
+       int (*hwbus_memcpy_fromio)(struct hwbus_priv *self, unsigned int addr,
+                                       void *dst, int count);
+       int (*hwbus_memcpy_toio)(struct hwbus_priv *self, unsigned int addr,
+                                       const void *src, int count);
+       void (*lock)(struct hwbus_priv *self);
+       void (*unlock)(struct hwbus_priv *self);
+       size_t (*align_size)(struct hwbus_priv *self, size_t size);
+       int (*power_mgmt)(struct hwbus_priv *self, bool suspend);
+};
+
+#endif /* CW1200_HWBUS_H */
diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/cw1200/hwio.c
new file mode 100644 (file)
index 0000000..ff230b7
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * Low-level device IO routines for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver, which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@lockless.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+
+#include "cw1200.h"
+#include "hwio.h"
+#include "hwbus.h"
+
+ /* Sdio addr is 4*spi_addr */
+#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2)
+#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \
+                               ((((buf_id)    & 0x1F) << 7) \
+                               | (((mpf)        & 1) << 6) \
+                               | (((rfu)        & 1) << 5) \
+                               | (((reg_id_ofs) & 0x1F) << 0))
+#define MAX_RETRY              3
+
+
+static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr,
+                            void *buf, size_t buf_len, int buf_id)
+{
+       u16 addr_sdio;
+       u32 sdio_reg_addr_17bit;
+
+       /* Check if buffer is aligned to 4 byte boundary */
+       if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
+               pr_err("buffer is not aligned.\n");
+               return -EINVAL;
+       }
+
+       /* Convert to SDIO Register Address */
+       addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+       sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+
+       return priv->hwbus_ops->hwbus_memcpy_fromio(priv->hwbus_priv,
+                                                 sdio_reg_addr_17bit,
+                                                 buf, buf_len);
+}
+
+static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr,
+                               const void *buf, size_t buf_len, int buf_id)
+{
+       u16 addr_sdio;
+       u32 sdio_reg_addr_17bit;
+
+       /* Convert to SDIO Register Address */
+       addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+       sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+
+       return priv->hwbus_ops->hwbus_memcpy_toio(priv->hwbus_priv,
+                                               sdio_reg_addr_17bit,
+                                               buf, buf_len);
+}
+
+static inline int __cw1200_reg_read_32(struct cw1200_common *priv,
+                                       u16 addr, u32 *val)
+{
+       __le32 tmp;
+       int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0);
+       *val = le32_to_cpu(tmp);
+       return i;
+}
+
+static inline int __cw1200_reg_write_32(struct cw1200_common *priv,
+                                       u16 addr, u32 val)
+{
+       __le32 tmp = cpu_to_le32(val);
+       return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0);
+}
+
+static inline int __cw1200_reg_read_16(struct cw1200_common *priv,
+                                       u16 addr, u16 *val)
+{
+       __le16 tmp;
+       int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0);
+       *val = le16_to_cpu(tmp);
+       return i;
+}
+
+static inline int __cw1200_reg_write_16(struct cw1200_common *priv,
+                                       u16 addr, u16 val)
+{
+       __le16 tmp = cpu_to_le16(val);
+       return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0);
+}
+
+int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf,
+                       size_t buf_len)
+{
+       int ret;
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0);
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf,
+                       size_t buf_len)
+{
+       int ret;
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0);
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len)
+{
+       int ret, retry = 1;
+       int buf_id_rx = priv->buf_id_rx;
+
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+
+       while (retry <= MAX_RETRY) {
+               ret = __cw1200_reg_read(priv,
+                                       ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
+                                       buf_len, buf_id_rx + 1);
+               if (!ret) {
+                       buf_id_rx = (buf_id_rx + 1) & 3;
+                       priv->buf_id_rx = buf_id_rx;
+                       break;
+               } else {
+                       retry++;
+                       mdelay(1);
+                       pr_err("error :[%d]\n", ret);
+               }
+       }
+
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int cw1200_data_write(struct cw1200_common *priv, const void *buf,
+                       size_t buf_len)
+{
+       int ret, retry = 1;
+       int buf_id_tx = priv->buf_id_tx;
+
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+
+       while (retry <= MAX_RETRY) {
+               ret = __cw1200_reg_write(priv,
+                                        ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
+                                        buf_len, buf_id_tx);
+               if (!ret) {
+                       buf_id_tx = (buf_id_tx + 1) & 31;
+                       priv->buf_id_tx = buf_id_tx;
+                       break;
+               } else {
+                       retry++;
+                       mdelay(1);
+                       pr_err("error :[%d]\n", ret);
+               }
+       }
+
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
+                        size_t buf_len, u32 prefetch, u16 port_addr)
+{
+       u32 val32 = 0;
+       int i, ret;
+
+       if ((buf_len / 2) >= 0x1000) {
+               pr_err("Can't read more than 0xfff words.\n");
+               return -EINVAL;
+       }
+
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+       /* Write address */
+       ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
+       if (ret < 0) {
+               pr_err("Can't write address register.\n");
+               goto out;
+       }
+
+       /* Read CONFIG Register Value - We will read 32 bits */
+       ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+       if (ret < 0) {
+               pr_err("Can't read config register.\n");
+               goto out;
+       }
+
+       /* Set PREFETCH bit */
+       ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID,
+                                       val32 | prefetch);
+       if (ret < 0) {
+               pr_err("Can't write prefetch bit.\n");
+               goto out;
+       }
+
+       /* Check for PRE-FETCH bit to be cleared */
+       for (i = 0; i < 20; i++) {
+               ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+               if (ret < 0) {
+                       pr_err("Can't check prefetch bit.\n");
+                       goto out;
+               }
+               if (!(val32 & prefetch))
+                       break;
+
+               mdelay(i);
+       }
+
+       if (val32 & prefetch) {
+               pr_err("Prefetch bit is not cleared.\n");
+               goto out;
+       }
+
+       /* Read data port */
+       ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0);
+       if (ret < 0) {
+               pr_err("Can't read data port.\n");
+               goto out;
+       }
+
+out:
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
+                       size_t buf_len)
+{
+       int ret;
+
+       if ((buf_len / 2) >= 0x1000) {
+               pr_err("Can't write more than 0xfff words.\n");
+               return -EINVAL;
+       }
+
+       priv->hwbus_ops->lock(priv->hwbus_priv);
+
+       /* Write address */
+       ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
+       if (ret < 0) {
+               pr_err("Can't write address register.\n");
+               goto out;
+       }
+
+       /* Write data port */
+       ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID,
+                                       buf, buf_len, 0);
+       if (ret < 0) {
+               pr_err("Can't write data port.\n");
+               goto out;
+       }
+
+out:
+       priv->hwbus_ops->unlock(priv->hwbus_priv);
+       return ret;
+}
+
+int __cw1200_irq_enable(struct cw1200_common *priv, int enable)
+{
+       u32 val32;
+       u16 val16;
+       int ret;
+
+       if (HIF_8601_SILICON == priv->hw_type) {
+               ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+               if (ret < 0) {
+                       pr_err("Can't read config register.\n");
+                       return ret;
+               }
+
+               if (enable)
+                       val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE;
+               else
+                       val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE;
+
+               ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32);
+               if (ret < 0) {
+                       pr_err("Can't write config register.\n");
+                       return ret;
+               }
+       } else {
+               ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16);
+               if (ret < 0) {
+                       pr_err("Can't read control register.\n");
+                       return ret;
+               }
+
+               if (enable)
+                       val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE;
+               else
+                       val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE;
+
+               ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16);
+               if (ret < 0) {
+                       pr_err("Can't write control register.\n");
+                       return ret;
+               }
+       }
+       return 0;
+}
diff --git a/drivers/net/wireless/cw1200/hwio.h b/drivers/net/wireless/cw1200/hwio.h
new file mode 100644 (file)
index 0000000..ddf5266
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Low-level API for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.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 CW1200_HWIO_H_INCLUDED
+#define CW1200_HWIO_H_INCLUDED
+
+/* extern */ struct cw1200_common;
+
+#define CW1200_CUT_11_ID_STR           (0x302E3830)
+#define CW1200_CUT_22_ID_STR1          (0x302e3132)
+#define CW1200_CUT_22_ID_STR2          (0x32302e30)
+#define CW1200_CUT_22_ID_STR3          (0x3335)
+#define CW1200_CUT_ID_ADDR             (0xFFF17F90)
+#define CW1200_CUT2_ID_ADDR            (0xFFF1FF90)
+
+/* Download control area */
+/* boot loader start address in SRAM */
+#define DOWNLOAD_BOOT_LOADER_OFFSET    (0x00000000)
+/* 32K, 0x4000 to 0xDFFF */
+#define DOWNLOAD_FIFO_OFFSET           (0x00004000)
+/* 32K */
+#define DOWNLOAD_FIFO_SIZE             (0x00008000)
+/* 128 bytes, 0xFF80 to 0xFFFF */
+#define DOWNLOAD_CTRL_OFFSET           (0x0000FF80)
+#define DOWNLOAD_CTRL_DATA_DWORDS      (32-6)
+
+struct download_cntl_t {
+       /* size of whole firmware file (including Cheksum), host init */
+       u32 image_size;
+       /* downloading flags */
+       u32 flags;
+       /* No. of bytes put into the download, init & updated by host */
+       u32 put;
+       /* last traced program counter, last ARM reg_pc */
+       u32 trace_pc;
+       /* No. of bytes read from the download, host init, device updates */
+       u32 get;
+       /* r0, boot losader status, host init to pending, device updates */
+       u32 status;
+       /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */
+       u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS];
+};
+
+#define        DOWNLOAD_IMAGE_SIZE_REG         \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size))
+#define        DOWNLOAD_FLAGS_REG              \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags))
+#define DOWNLOAD_PUT_REG               \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put))
+#define DOWNLOAD_TRACE_PC_REG          \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc))
+#define        DOWNLOAD_GET_REG                \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get))
+#define        DOWNLOAD_STATUS_REG             \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status))
+#define DOWNLOAD_DEBUG_DATA_REG                \
+       (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data))
+#define DOWNLOAD_DEBUG_DATA_LEN                (108)
+
+#define DOWNLOAD_BLOCK_SIZE            (1024)
+
+/* For boot loader detection */
+#define DOWNLOAD_ARE_YOU_HERE          (0x87654321)
+#define DOWNLOAD_I_AM_HERE             (0x12345678)
+
+/* Download error code */
+#define DOWNLOAD_PENDING               (0xFFFFFFFF)
+#define DOWNLOAD_SUCCESS               (0)
+#define DOWNLOAD_EXCEPTION             (1)
+#define DOWNLOAD_ERR_MEM_1             (2)
+#define DOWNLOAD_ERR_MEM_2             (3)
+#define DOWNLOAD_ERR_SOFTWARE          (4)
+#define DOWNLOAD_ERR_FILE_SIZE         (5)
+#define DOWNLOAD_ERR_CHECKSUM          (6)
+#define DOWNLOAD_ERR_OVERFLOW          (7)
+#define DOWNLOAD_ERR_IMAGE             (8)
+#define DOWNLOAD_ERR_HOST              (9)
+#define DOWNLOAD_ERR_ABORT             (10)
+
+
+#define SYS_BASE_ADDR_SILICON          (0)
+#define PAC_BASE_ADDRESS_SILICON       (SYS_BASE_ADDR_SILICON + 0x09000000)
+#define PAC_SHARED_MEMORY_SILICON      (PAC_BASE_ADDRESS_SILICON)
+
+#define CW1200_APB(addr)               (PAC_SHARED_MEMORY_SILICON + (addr))
+
+/* Device register definitions */
+
+/* WBF - SPI Register Addresses */
+#define ST90TDS_ADDR_ID_BASE           (0x0000)
+/* 16/32 bits */
+#define ST90TDS_CONFIG_REG_ID          (0x0000)
+/* 16/32 bits */
+#define ST90TDS_CONTROL_REG_ID         (0x0001)
+/* 16 bits, Q mode W/R */
+#define ST90TDS_IN_OUT_QUEUE_REG_ID    (0x0002)
+/* 32 bits, AHB bus R/W */
+#define ST90TDS_AHB_DPORT_REG_ID       (0x0003)
+/* 16/32 bits */
+#define ST90TDS_SRAM_BASE_ADDR_REG_ID   (0x0004)
+/* 32 bits, APB bus R/W */
+#define ST90TDS_SRAM_DPORT_REG_ID      (0x0005)
+/* 32 bits, t_settle/general */
+#define ST90TDS_TSET_GEN_R_W_REG_ID    (0x0006)
+/* 16 bits, Q mode read, no length */
+#define ST90TDS_FRAME_OUT_REG_ID       (0x0007)
+#define ST90TDS_ADDR_ID_MAX            (ST90TDS_FRAME_OUT_REG_ID)
+
+/* WBF - Control register bit set */
+/* next o/p length, bit 11 to 0 */
+#define ST90TDS_CONT_NEXT_LEN_MASK     (0x0FFF)
+#define ST90TDS_CONT_WUP_BIT           (BIT(12))
+#define ST90TDS_CONT_RDY_BIT           (BIT(13))
+#define ST90TDS_CONT_IRQ_ENABLE                (BIT(14))
+#define ST90TDS_CONT_RDY_ENABLE                (BIT(15))
+#define ST90TDS_CONT_IRQ_RDY_ENABLE    (BIT(14)|BIT(15))
+
+/* SPI Config register bit set */
+#define ST90TDS_CONFIG_FRAME_BIT       (BIT(2))
+#define ST90TDS_CONFIG_WORD_MODE_BITS  (BIT(3)|BIT(4))
+#define ST90TDS_CONFIG_WORD_MODE_1     (BIT(3))
+#define ST90TDS_CONFIG_WORD_MODE_2     (BIT(4))
+#define ST90TDS_CONFIG_ERROR_0_BIT     (BIT(5))
+#define ST90TDS_CONFIG_ERROR_1_BIT     (BIT(6))
+#define ST90TDS_CONFIG_ERROR_2_BIT     (BIT(7))
+/* TBD: Sure??? */
+#define ST90TDS_CONFIG_CSN_FRAME_BIT   (BIT(7))
+#define ST90TDS_CONFIG_ERROR_3_BIT     (BIT(8))
+#define ST90TDS_CONFIG_ERROR_4_BIT     (BIT(9))
+/* QueueM */
+#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10))
+/* AHB bus */
+#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11))
+#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12))
+/* APB bus */
+#define ST90TDS_CONFIG_PRFETCH_BIT     (BIT(13))
+/* cpu reset */
+#define ST90TDS_CONFIG_CPU_RESET_BIT   (BIT(14))
+#define ST90TDS_CONFIG_CLEAR_INT_BIT   (BIT(15))
+
+/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */
+#define ST90TDS_CONF_IRQ_ENABLE                (BIT(16))
+#define ST90TDS_CONF_RDY_ENABLE                (BIT(17))
+#define ST90TDS_CONF_IRQ_RDY_ENABLE    (BIT(16)|BIT(17))
+
+int cw1200_data_read(struct cw1200_common *priv,
+                    void *buf, size_t buf_len);
+int cw1200_data_write(struct cw1200_common *priv,
+                     const void *buf, size_t buf_len);
+
+int cw1200_reg_read(struct cw1200_common *priv, u16 addr,
+                   void *buf, size_t buf_len);
+int cw1200_reg_write(struct cw1200_common *priv, u16 addr,
+                    const void *buf, size_t buf_len);
+
+static inline int cw1200_reg_read_16(struct cw1200_common *priv,
+                                    u16 addr, u16 *val)
+{
+       __le32 tmp;
+       int i;
+       i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp));
+       *val = le32_to_cpu(tmp) & 0xfffff;
+       return i;
+}
+
+static inline int cw1200_reg_write_16(struct cw1200_common *priv,
+                                     u16 addr, u16 val)
+{
+       __le32 tmp = cpu_to_le32((u32)val);
+       return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp));
+}
+
+static inline int cw1200_reg_read_32(struct cw1200_common *priv,
+                                    u16 addr, u32 *val)
+{
+       __le32 tmp;
+       int i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp));
+       *val = le32_to_cpu(tmp);
+       return i;
+}
+
+static inline int cw1200_reg_write_32(struct cw1200_common *priv,
+                                     u16 addr, u32 val)
+{
+       __le32 tmp = cpu_to_le32(val);
+       return cw1200_reg_write(priv, addr, &tmp, sizeof(val));
+}
+
+int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
+                        size_t buf_len, u32 prefetch, u16 port_addr);
+int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
+                    size_t buf_len);
+
+static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr,
+                                 void *buf, size_t buf_len)
+{
+       return cw1200_indirect_read(priv, addr, buf, buf_len,
+                                   ST90TDS_CONFIG_PRFETCH_BIT,
+                                   ST90TDS_SRAM_DPORT_REG_ID);
+}
+
+static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr,
+                                 void *buf, size_t buf_len)
+{
+       return cw1200_indirect_read(priv, addr, buf, buf_len,
+                                   ST90TDS_CONFIG_AHB_PRFETCH_BIT,
+                                   ST90TDS_AHB_DPORT_REG_ID);
+}
+
+static inline int cw1200_apb_read_32(struct cw1200_common *priv,
+                                    u32 addr, u32 *val)
+{
+       __le32 tmp;
+       int i = cw1200_apb_read(priv, addr, &tmp, sizeof(tmp));
+       *val = le32_to_cpu(tmp);
+       return i;
+}
+
+static inline int cw1200_apb_write_32(struct cw1200_common *priv,
+                                     u32 addr, u32 val)
+{
+       __le32 tmp = cpu_to_le32(val);
+       return cw1200_apb_write(priv, addr, &tmp, sizeof(val));
+}
+static inline int cw1200_ahb_read_32(struct cw1200_common *priv,
+                                    u32 addr, u32 *val)
+{
+       __le32 tmp;
+       int i = cw1200_ahb_read(priv, addr, &tmp, sizeof(tmp));
+       *val = le32_to_cpu(tmp);
+       return i;
+}
+
+#endif /* CW1200_HWIO_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c
new file mode 100644 (file)
index 0000000..3724e73
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+ * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ *   Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * - stlc45xx driver
+ *   Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+
+#include "cw1200.h"
+#include "txrx.h"
+#include "hwbus.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "bh.h"
+#include "sta.h"
+#include "scan.h"
+#include "debug.h"
+#include "pm.h"
+
+MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
+MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("cw1200_core");
+
+/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */
+static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00};
+module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO);
+MODULE_PARM_DESC(macaddr, "Override platform_data MAC address");
+
+static char *cw1200_sdd_path;
+module_param(cw1200_sdd_path, charp, 0644);
+MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file");
+static int cw1200_refclk;
+module_param(cw1200_refclk, int, 0644);
+MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock");
+
+int cw1200_power_mode = wsm_power_mode_quiescent;
+module_param(cw1200_power_mode, int, 0644);
+MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode.  0 == active, 1 == doze, 2 == quiescent (default)");
+
+#define RATETAB_ENT(_rate, _rateid, _flags)            \
+       {                                               \
+               .bitrate        = (_rate),              \
+               .hw_value       = (_rateid),            \
+               .flags          = (_flags),             \
+       }
+
+static struct ieee80211_rate cw1200_rates[] = {
+       RATETAB_ENT(10,  0,   0),
+       RATETAB_ENT(20,  1,   0),
+       RATETAB_ENT(55,  2,   0),
+       RATETAB_ENT(110, 3,   0),
+       RATETAB_ENT(60,  6,  0),
+       RATETAB_ENT(90,  7,  0),
+       RATETAB_ENT(120, 8,  0),
+       RATETAB_ENT(180, 9,  0),
+       RATETAB_ENT(240, 10, 0),
+       RATETAB_ENT(360, 11, 0),
+       RATETAB_ENT(480, 12, 0),
+       RATETAB_ENT(540, 13, 0),
+};
+
+static struct ieee80211_rate cw1200_mcs_rates[] = {
+       RATETAB_ENT(65,  14, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
+       RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
+};
+
+#define cw1200_a_rates         (cw1200_rates + 4)
+#define cw1200_a_rates_size    (ARRAY_SIZE(cw1200_rates) - 4)
+#define cw1200_g_rates         (cw1200_rates + 0)
+#define cw1200_g_rates_size    (ARRAY_SIZE(cw1200_rates))
+#define cw1200_n_rates         (cw1200_mcs_rates)
+#define cw1200_n_rates_size    (ARRAY_SIZE(cw1200_mcs_rates))
+
+
+#define CHAN2G(_channel, _freq, _flags) {                      \
+       .band                   = IEEE80211_BAND_2GHZ,          \
+       .center_freq            = (_freq),                      \
+       .hw_value               = (_channel),                   \
+       .flags                  = (_flags),                     \
+       .max_antenna_gain       = 0,                            \
+       .max_power              = 30,                           \
+}
+
+#define CHAN5G(_channel, _flags) {                             \
+       .band                   = IEEE80211_BAND_5GHZ,          \
+       .center_freq    = 5000 + (5 * (_channel)),              \
+       .hw_value               = (_channel),                   \
+       .flags                  = (_flags),                     \
+       .max_antenna_gain       = 0,                            \
+       .max_power              = 30,                           \
+}
+
+static struct ieee80211_channel cw1200_2ghz_chantable[] = {
+       CHAN2G(1, 2412, 0),
+       CHAN2G(2, 2417, 0),
+       CHAN2G(3, 2422, 0),
+       CHAN2G(4, 2427, 0),
+       CHAN2G(5, 2432, 0),
+       CHAN2G(6, 2437, 0),
+       CHAN2G(7, 2442, 0),
+       CHAN2G(8, 2447, 0),
+       CHAN2G(9, 2452, 0),
+       CHAN2G(10, 2457, 0),
+       CHAN2G(11, 2462, 0),
+       CHAN2G(12, 2467, 0),
+       CHAN2G(13, 2472, 0),
+       CHAN2G(14, 2484, 0),
+};
+
+static struct ieee80211_channel cw1200_5ghz_chantable[] = {
+       CHAN5G(34, 0),          CHAN5G(36, 0),
+       CHAN5G(38, 0),          CHAN5G(40, 0),
+       CHAN5G(42, 0),          CHAN5G(44, 0),
+       CHAN5G(46, 0),          CHAN5G(48, 0),
+       CHAN5G(52, 0),          CHAN5G(56, 0),
+       CHAN5G(60, 0),          CHAN5G(64, 0),
+       CHAN5G(100, 0),         CHAN5G(104, 0),
+       CHAN5G(108, 0),         CHAN5G(112, 0),
+       CHAN5G(116, 0),         CHAN5G(120, 0),
+       CHAN5G(124, 0),         CHAN5G(128, 0),
+       CHAN5G(132, 0),         CHAN5G(136, 0),
+       CHAN5G(140, 0),         CHAN5G(149, 0),
+       CHAN5G(153, 0),         CHAN5G(157, 0),
+       CHAN5G(161, 0),         CHAN5G(165, 0),
+       CHAN5G(184, 0),         CHAN5G(188, 0),
+       CHAN5G(192, 0),         CHAN5G(196, 0),
+       CHAN5G(200, 0),         CHAN5G(204, 0),
+       CHAN5G(208, 0),         CHAN5G(212, 0),
+       CHAN5G(216, 0),
+};
+
+static struct ieee80211_supported_band cw1200_band_2ghz = {
+       .channels = cw1200_2ghz_chantable,
+       .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable),
+       .bitrates = cw1200_g_rates,
+       .n_bitrates = cw1200_g_rates_size,
+       .ht_cap = {
+               .cap = IEEE80211_HT_CAP_GRN_FLD |
+                       (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
+                       IEEE80211_HT_CAP_MAX_AMSDU,
+               .ht_supported = 1,
+               .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+               .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+               .mcs = {
+                       .rx_mask[0] = 0xFF,
+                       .rx_highest = __cpu_to_le16(0x41),
+                       .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               },
+       },
+};
+
+static struct ieee80211_supported_band cw1200_band_5ghz = {
+       .channels = cw1200_5ghz_chantable,
+       .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable),
+       .bitrates = cw1200_a_rates,
+       .n_bitrates = cw1200_a_rates_size,
+       .ht_cap = {
+               .cap = IEEE80211_HT_CAP_GRN_FLD |
+                       (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
+                       IEEE80211_HT_CAP_MAX_AMSDU,
+               .ht_supported = 1,
+               .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+               .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+               .mcs = {
+                       .rx_mask[0] = 0xFF,
+                       .rx_highest = __cpu_to_le16(0x41),
+                       .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               },
+       },
+};
+
+static const unsigned long cw1200_ttl[] = {
+       1 * HZ, /* VO */
+       2 * HZ, /* VI */
+       5 * HZ, /* BE */
+       10 * HZ /* BK */
+};
+
+static const struct ieee80211_ops cw1200_ops = {
+       .start                  = cw1200_start,
+       .stop                   = cw1200_stop,
+       .add_interface          = cw1200_add_interface,
+       .remove_interface       = cw1200_remove_interface,
+       .change_interface       = cw1200_change_interface,
+       .tx                     = cw1200_tx,
+       .hw_scan                = cw1200_hw_scan,
+       .set_tim                = cw1200_set_tim,
+       .sta_notify             = cw1200_sta_notify,
+       .sta_add                = cw1200_sta_add,
+       .sta_remove             = cw1200_sta_remove,
+       .set_key                = cw1200_set_key,
+       .set_rts_threshold      = cw1200_set_rts_threshold,
+       .config                 = cw1200_config,
+       .bss_info_changed       = cw1200_bss_info_changed,
+       .prepare_multicast      = cw1200_prepare_multicast,
+       .configure_filter       = cw1200_configure_filter,
+       .conf_tx                = cw1200_conf_tx,
+       .get_stats              = cw1200_get_stats,
+       .ampdu_action           = cw1200_ampdu_action,
+       .flush                  = cw1200_flush,
+#ifdef CONFIG_PM
+       .suspend                = cw1200_wow_suspend,
+       .resume                 = cw1200_wow_resume,
+#endif
+       /* Intentionally not offloaded:                                 */
+       /*.channel_switch       = cw1200_channel_switch,                */
+       /*.remain_on_channel    = cw1200_remain_on_channel,             */
+       /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel,  */
+};
+
+static int cw1200_ba_rx_tids = -1;
+static int cw1200_ba_tx_tids = -1;
+module_param(cw1200_ba_rx_tids, int, 0644);
+module_param(cw1200_ba_tx_tids, int, 0644);
+MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs");
+MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs");
+
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support cw1200_wowlan_support = {
+       /* Support only for limited wowlan functionalities */
+       .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
+};
+#endif
+
+
+static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
+                                               const bool have_5ghz)
+{
+       int i, band;
+       struct ieee80211_hw *hw;
+       struct cw1200_common *priv;
+
+       hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops);
+       if (!hw)
+               return NULL;
+
+       priv = hw->priv;
+       priv->hw = hw;
+       priv->hw_type = -1;
+       priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+       priv->rates = cw1200_rates; /* TODO: fetch from FW */
+       priv->mcs_rates = cw1200_n_rates;
+       if (cw1200_ba_rx_tids != -1)
+               priv->ba_rx_tid_mask = cw1200_ba_rx_tids;
+       else
+               priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */
+       if (cw1200_ba_tx_tids != -1)
+               priv->ba_tx_tid_mask = cw1200_ba_tx_tids;
+       else
+               priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */
+
+       hw->flags = IEEE80211_HW_SIGNAL_DBM |
+                   IEEE80211_HW_SUPPORTS_PS |
+                   IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+                   IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+                   IEEE80211_HW_SUPPORTS_UAPSD |
+                   IEEE80211_HW_CONNECTION_MONITOR |
+                   IEEE80211_HW_AMPDU_AGGREGATION |
+                   IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
+                   IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC;
+
+       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                         BIT(NL80211_IFTYPE_ADHOC) |
+                                         BIT(NL80211_IFTYPE_AP) |
+                                         BIT(NL80211_IFTYPE_MESH_POINT) |
+                                         BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                                         BIT(NL80211_IFTYPE_P2P_GO);
+
+#ifdef CONFIG_PM
+       hw->wiphy->wowlan = &cw1200_wowlan_support;
+#endif
+
+       hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
+       hw->channel_change_time = 1000; /* TODO: find actual value */
+       hw->queues = 4;
+
+       priv->rts_threshold = -1;
+
+       hw->max_rates = 8;
+       hw->max_rate_tries = 15;
+       hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
+               8;  /* TKIP IV */
+
+       hw->sta_data_size = sizeof(struct cw1200_sta_priv);
+
+       hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz;
+       if (have_5ghz)
+               hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz;
+
+       /* Channel params have to be cleared before registering wiphy again */
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               struct ieee80211_supported_band *sband = hw->wiphy->bands[band];
+               if (!sband)
+                       continue;
+               for (i = 0; i < sband->n_channels; i++) {
+                       sband->channels[i].flags = 0;
+                       sband->channels[i].max_antenna_gain = 0;
+                       sband->channels[i].max_power = 30;
+               }
+       }
+
+       hw->wiphy->max_scan_ssids = 2;
+       hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+
+       if (macaddr)
+               SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr);
+       else
+               SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template);
+
+       /* Fix up mac address if necessary */
+       if (hw->wiphy->perm_addr[3] == 0 &&
+           hw->wiphy->perm_addr[4] == 0 &&
+           hw->wiphy->perm_addr[5] == 0) {
+               get_random_bytes(&hw->wiphy->perm_addr[3], 3);
+       }
+
+       mutex_init(&priv->wsm_cmd_mux);
+       mutex_init(&priv->conf_mutex);
+       priv->workqueue = create_singlethread_workqueue("cw1200_wq");
+       sema_init(&priv->scan.lock, 1);
+       INIT_WORK(&priv->scan.work, cw1200_scan_work);
+       INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work);
+       INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout);
+       INIT_DELAYED_WORK(&priv->clear_recent_scan_work,
+                         cw1200_clear_recent_scan_work);
+       INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout);
+       INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work);
+       INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work);
+       INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work);
+       INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work);
+       spin_lock_init(&priv->event_queue_lock);
+       INIT_LIST_HEAD(&priv->event_queue);
+       INIT_WORK(&priv->event_handler, cw1200_event_handler);
+       INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work);
+       INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work);
+       spin_lock_init(&priv->bss_loss_lock);
+       spin_lock_init(&priv->ps_state_lock);
+       INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work);
+       INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work);
+       INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work);
+       INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work);
+       INIT_WORK(&priv->link_id_work, cw1200_link_id_work);
+       INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work);
+       INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset);
+       INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work);
+       INIT_WORK(&priv->set_beacon_wakeup_period_work,
+                 cw1200_set_beacon_wakeup_period_work);
+       init_timer(&priv->mcast_timeout);
+       priv->mcast_timeout.data = (unsigned long)priv;
+       priv->mcast_timeout.function = cw1200_mcast_timeout;
+
+       if (cw1200_queue_stats_init(&priv->tx_queue_stats,
+                                   CW1200_LINK_ID_MAX,
+                                   cw1200_skb_dtor,
+                                   priv)) {
+               ieee80211_free_hw(hw);
+               return NULL;
+       }
+
+       for (i = 0; i < 4; ++i) {
+               if (cw1200_queue_init(&priv->tx_queue[i],
+                                     &priv->tx_queue_stats, i, 16,
+                                     cw1200_ttl[i])) {
+                       for (; i > 0; i--)
+                               cw1200_queue_deinit(&priv->tx_queue[i - 1]);
+                       cw1200_queue_stats_deinit(&priv->tx_queue_stats);
+                       ieee80211_free_hw(hw);
+                       return NULL;
+               }
+       }
+
+       init_waitqueue_head(&priv->channel_switch_done);
+       init_waitqueue_head(&priv->wsm_cmd_wq);
+       init_waitqueue_head(&priv->wsm_startup_done);
+       init_waitqueue_head(&priv->ps_mode_switch_done);
+       wsm_buf_init(&priv->wsm_cmd_buf);
+       spin_lock_init(&priv->wsm_cmd.lock);
+       priv->wsm_cmd.done = 1;
+       tx_policy_init(priv);
+
+       return hw;
+}
+
+static int cw1200_register_common(struct ieee80211_hw *dev)
+{
+       struct cw1200_common *priv = dev->priv;
+       int err;
+
+#ifdef CONFIG_PM
+       err = cw1200_pm_init(&priv->pm_state, priv);
+       if (err) {
+               pr_err("Cannot init PM. (%d).\n",
+                      err);
+               return err;
+       }
+#endif
+
+       err = ieee80211_register_hw(dev);
+       if (err) {
+               pr_err("Cannot register device (%d).\n",
+                      err);
+#ifdef CONFIG_PM
+               cw1200_pm_deinit(&priv->pm_state);
+#endif
+               return err;
+       }
+
+       cw1200_debug_init(priv);
+
+       pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy));
+       return 0;
+}
+
+static void cw1200_free_common(struct ieee80211_hw *dev)
+{
+       ieee80211_free_hw(dev);
+}
+
+static void cw1200_unregister_common(struct ieee80211_hw *dev)
+{
+       struct cw1200_common *priv = dev->priv;
+       int i;
+
+       ieee80211_unregister_hw(dev);
+
+       del_timer_sync(&priv->mcast_timeout);
+       cw1200_unregister_bh(priv);
+
+       cw1200_debug_release(priv);
+
+       mutex_destroy(&priv->conf_mutex);
+
+       wsm_buf_deinit(&priv->wsm_cmd_buf);
+
+       destroy_workqueue(priv->workqueue);
+       priv->workqueue = NULL;
+
+       if (priv->sdd) {
+               release_firmware(priv->sdd);
+               priv->sdd = NULL;
+       }
+
+       for (i = 0; i < 4; ++i)
+               cw1200_queue_deinit(&priv->tx_queue[i]);
+
+       cw1200_queue_stats_deinit(&priv->tx_queue_stats);
+#ifdef CONFIG_PM
+       cw1200_pm_deinit(&priv->pm_state);
+#endif
+}
+
+/* Clock is in KHz */
+u32 cw1200_dpll_from_clk(u16 clk_khz)
+{
+       switch (clk_khz) {
+       case 0x32C8: /* 13000 KHz */
+               return 0x1D89D241;
+       case 0x3E80: /* 16000 KHz */
+               return 0x000001E1;
+       case 0x41A0: /* 16800 KHz */
+               return 0x124931C1;
+       case 0x4B00: /* 19200 KHz */
+               return 0x00000191;
+       case 0x5DC0: /* 24000 KHz */
+               return 0x00000141;
+       case 0x6590: /* 26000 KHz */
+               return 0x0EC4F121;
+       case 0x8340: /* 33600 KHz */
+               return 0x092490E1;
+       case 0x9600: /* 38400 KHz */
+               return 0x100010C1;
+       case 0x9C40: /* 40000 KHz */
+               return 0x000000C1;
+       case 0xBB80: /* 48000 KHz */
+               return 0x000000A1;
+       case 0xCB20: /* 52000 KHz */
+               return 0x07627091;
+       default:
+               pr_err("Unknown Refclk freq (0x%04x), using 2600KHz\n",
+                      clk_khz);
+               return 0x0EC4F121;
+       }
+}
+
+int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
+                     struct hwbus_priv *hwbus,
+                     struct device *pdev,
+                     struct cw1200_common **core,
+                     int ref_clk, const u8 *macaddr,
+                     const char *sdd_path, bool have_5ghz)
+{
+       int err = -EINVAL;
+       struct ieee80211_hw *dev;
+       struct cw1200_common *priv;
+       struct wsm_operational_mode mode = {
+               .power_mode = cw1200_power_mode,
+               .disable_more_flag_usage = true,
+       };
+
+       dev = cw1200_init_common(macaddr, have_5ghz);
+       if (!dev)
+               goto err;
+
+       priv = dev->priv;
+       priv->hw_refclk = ref_clk;
+       if (cw1200_refclk)
+               priv->hw_refclk = cw1200_refclk;
+
+       priv->sdd_path = (char *)sdd_path;
+       if (cw1200_sdd_path)
+               priv->sdd_path = cw1200_sdd_path;
+
+       priv->hwbus_ops = hwbus_ops;
+       priv->hwbus_priv = hwbus;
+       priv->pdev = pdev;
+       SET_IEEE80211_DEV(priv->hw, pdev);
+
+       /* Pass struct cw1200_common back up */
+       *core = priv;
+
+       err = cw1200_register_bh(priv);
+       if (err)
+               goto err1;
+
+       err = cw1200_load_firmware(priv);
+       if (err)
+               goto err2;
+
+       if (wait_event_interruptible_timeout(priv->wsm_startup_done,
+                                            priv->firmware_ready,
+                                            3*HZ) <= 0) {
+               /* TODO: Need to find how to reset device
+                  in QUEUE mode properly.
+               */
+               pr_err("Timeout waiting on device startup\n");
+               err = -ETIMEDOUT;
+               goto err2;
+       }
+
+       /* Set low-power mode. */
+       wsm_set_operational_mode(priv, &mode);
+
+       /* Enable multi-TX confirmation */
+       wsm_use_multi_tx_conf(priv, true);
+
+       err = cw1200_register_common(dev);
+       if (err)
+               goto err2;
+
+       return err;
+
+err2:
+       cw1200_unregister_bh(priv);
+err1:
+       cw1200_free_common(dev);
+err:
+       *core = NULL;
+       return err;
+}
+EXPORT_SYMBOL_GPL(cw1200_core_probe);
+
+void cw1200_core_release(struct cw1200_common *self)
+{
+       /* Disable device interrupts */
+       self->hwbus_ops->lock(self->hwbus_priv);
+       __cw1200_irq_enable(self, 0);
+       self->hwbus_ops->unlock(self->hwbus_priv);
+
+       /* And then clean up */
+       cw1200_unregister_common(self->hw);
+       cw1200_free_common(self->hw);
+       return;
+}
+EXPORT_SYMBOL_GPL(cw1200_core_release);
diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c
new file mode 100644 (file)
index 0000000..b37abb9
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Mac80211 power management API for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/if_ether.h>
+#include "cw1200.h"
+#include "pm.h"
+#include "sta.h"
+#include "bh.h"
+#include "hwbus.h"
+
+#define CW1200_BEACON_SKIPPING_MULTIPLIER 3
+
+struct cw1200_udp_port_filter {
+       struct wsm_udp_port_filter_hdr hdr;
+       /* Up to 4 filters are allowed. */
+       struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS];
+} __packed;
+
+struct cw1200_ether_type_filter {
+       struct wsm_ether_type_filter_hdr hdr;
+       /* Up to 4 filters are allowed. */
+       struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS];
+} __packed;
+
+static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = {
+       .hdr.num = 2,
+       .filters = {
+               [0] = {
+                       .action = WSM_FILTER_ACTION_FILTER_OUT,
+                       .type = WSM_FILTER_PORT_TYPE_DST,
+                       .port = __cpu_to_le16(67), /* DHCP Bootps */
+               },
+               [1] = {
+                       .action = WSM_FILTER_ACTION_FILTER_OUT,
+                       .type = WSM_FILTER_PORT_TYPE_DST,
+                       .port = __cpu_to_le16(68), /* DHCP Bootpc */
+               },
+       }
+};
+
+static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = {
+       .num = 0,
+};
+
+#ifndef ETH_P_WAPI
+#define ETH_P_WAPI     0x88B4
+#endif
+
+static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = {
+       .hdr.num = 4,
+       .filters = {
+               [0] = {
+                       .action = WSM_FILTER_ACTION_FILTER_IN,
+                       .type = __cpu_to_le16(ETH_P_IP),
+               },
+               [1] = {
+                       .action = WSM_FILTER_ACTION_FILTER_IN,
+                       .type = __cpu_to_le16(ETH_P_PAE),
+               },
+               [2] = {
+                       .action = WSM_FILTER_ACTION_FILTER_IN,
+                       .type = __cpu_to_le16(ETH_P_WAPI),
+               },
+               [3] = {
+                       .action = WSM_FILTER_ACTION_FILTER_IN,
+                       .type = __cpu_to_le16(ETH_P_ARP),
+               },
+       },
+};
+
+static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = {
+       .num = 0,
+};
+
+/* private */
+struct cw1200_suspend_state {
+       unsigned long bss_loss_tmo;
+       unsigned long join_tmo;
+       unsigned long direct_probe;
+       unsigned long link_id_gc;
+       bool beacon_skipping;
+       u8 prev_ps_mode;
+};
+
+static void cw1200_pm_stay_awake_tmo(unsigned long arg)
+{
+       /* XXX what's the point of this ? */
+}
+
+int cw1200_pm_init(struct cw1200_pm_state *pm,
+                  struct cw1200_common *priv)
+{
+       spin_lock_init(&pm->lock);
+
+       init_timer(&pm->stay_awake);
+       pm->stay_awake.data = (unsigned long)pm;
+       pm->stay_awake.function = cw1200_pm_stay_awake_tmo;
+
+       return 0;
+}
+
+void cw1200_pm_deinit(struct cw1200_pm_state *pm)
+{
+       del_timer_sync(&pm->stay_awake);
+}
+
+void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
+                         unsigned long tmo)
+{
+       long cur_tmo;
+       spin_lock_bh(&pm->lock);
+       cur_tmo = pm->stay_awake.expires - jiffies;
+       if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo)
+               mod_timer(&pm->stay_awake, jiffies + tmo);
+       spin_unlock_bh(&pm->lock);
+}
+
+static long cw1200_suspend_work(struct delayed_work *work)
+{
+       int ret = cancel_delayed_work(work);
+       long tmo;
+       if (ret > 0) {
+               /* Timer is pending */
+               tmo = work->timer.expires - jiffies;
+               if (tmo < 0)
+                       tmo = 0;
+       } else {
+               tmo = -1;
+       }
+       return tmo;
+}
+
+static int cw1200_resume_work(struct cw1200_common *priv,
+                              struct delayed_work *work,
+                              unsigned long tmo)
+{
+       if ((long)tmo < 0)
+               return 1;
+
+       return queue_delayed_work(priv->workqueue, work, tmo);
+}
+
+int cw1200_can_suspend(struct cw1200_common *priv)
+{
+       if (atomic_read(&priv->bh_rx)) {
+               wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n");
+               return 0;
+       }
+       return 1;
+}
+EXPORT_SYMBOL_GPL(cw1200_can_suspend);
+
+int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+       struct cw1200_common *priv = hw->priv;
+       struct cw1200_pm_state *pm_state = &priv->pm_state;
+       struct cw1200_suspend_state *state;
+       int ret;
+
+       spin_lock_bh(&pm_state->lock);
+       ret = timer_pending(&pm_state->stay_awake);
+       spin_unlock_bh(&pm_state->lock);
+       if (ret)
+               return -EAGAIN;
+
+       /* Do not suspend when datapath is not idle */
+       if (priv->tx_queue_stats.num_queued)
+               return -EBUSY;
+
+       /* Make sure there is no configuration requests in progress. */
+       if (!mutex_trylock(&priv->conf_mutex))
+               return -EBUSY;
+
+       /* Ensure pending operations are done.
+        * Note also that wow_suspend must return in ~2.5sec, before
+        * watchdog is triggered.
+        */
+       if (priv->channel_switch_in_progress)
+               goto revert1;
+
+       /* Do not suspend when join is pending */
+       if (priv->join_pending)
+               goto revert1;
+
+       /* Do not suspend when scanning */
+       if (down_trylock(&priv->scan.lock))
+               goto revert1;
+
+       /* Lock TX. */
+       wsm_lock_tx_async(priv);
+
+       /* Wait to avoid possible race with bh code.
+        * But do not wait too long...
+        */
+       if (wait_event_timeout(priv->bh_evt_wq,
+                              !priv->hw_bufs_used, HZ / 10) <= 0)
+               goto revert2;
+
+       /* Set UDP filter */
+       wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr);
+
+       /* Set ethernet frame type filter */
+       wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr);
+
+       /* Allocate state */
+       state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL);
+       if (!state)
+               goto revert3;
+
+       /* Change to legacy PS while going to suspend */
+       if (!priv->vif->p2p &&
+           priv->join_status == CW1200_JOIN_STATUS_STA &&
+           priv->powersave_mode.mode != WSM_PSM_PS) {
+               state->prev_ps_mode = priv->powersave_mode.mode;
+               priv->powersave_mode.mode = WSM_PSM_PS;
+               cw1200_set_pm(priv, &priv->powersave_mode);
+               if (wait_event_interruptible_timeout(priv->ps_mode_switch_done,
+                                                    !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) {
+                       goto revert3;
+               }
+       }
+
+       /* Store delayed work states. */
+       state->bss_loss_tmo =
+               cw1200_suspend_work(&priv->bss_loss_work);
+       state->join_tmo =
+               cw1200_suspend_work(&priv->join_timeout);
+       state->direct_probe =
+               cw1200_suspend_work(&priv->scan.probe_work);
+       state->link_id_gc =
+               cw1200_suspend_work(&priv->link_id_gc_work);
+
+       cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+       atomic_set(&priv->recent_scan, 0);
+
+       /* Enable beacon skipping */
+       if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+           priv->join_dtim_period &&
+           !priv->has_multicast_subscription) {
+               state->beacon_skipping = true;
+               wsm_set_beacon_wakeup_period(priv,
+                                            priv->join_dtim_period,
+                                            CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period);
+       }
+
+       /* Stop serving thread */
+       if (cw1200_bh_suspend(priv))
+               goto revert4;
+
+       ret = timer_pending(&priv->mcast_timeout);
+       if (ret)
+               goto revert5;
+
+       /* Store suspend state */
+       pm_state->suspend_state = state;
+
+       /* Enable IRQ wake */
+       ret = priv->hwbus_ops->power_mgmt(priv->hwbus_priv, true);
+       if (ret) {
+               wiphy_err(priv->hw->wiphy,
+                         "PM request failed: %d. WoW is disabled.\n", ret);
+               cw1200_wow_resume(hw);
+               return -EBUSY;
+       }
+
+       /* Force resume if event is coming from the device. */
+       if (atomic_read(&priv->bh_rx)) {
+               cw1200_wow_resume(hw);
+               return -EAGAIN;
+       }
+
+       return 0;
+
+revert5:
+       WARN_ON(cw1200_bh_resume(priv));
+revert4:
+       cw1200_resume_work(priv, &priv->bss_loss_work,
+                          state->bss_loss_tmo);
+       cw1200_resume_work(priv, &priv->join_timeout,
+                          state->join_tmo);
+       cw1200_resume_work(priv, &priv->scan.probe_work,
+                          state->direct_probe);
+       cw1200_resume_work(priv, &priv->link_id_gc_work,
+                          state->link_id_gc);
+       kfree(state);
+revert3:
+       wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
+       wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
+revert2:
+       wsm_unlock_tx(priv);
+       up(&priv->scan.lock);
+revert1:
+       mutex_unlock(&priv->conf_mutex);
+       return -EBUSY;
+}
+
+int cw1200_wow_resume(struct ieee80211_hw *hw)
+{
+       struct cw1200_common *priv = hw->priv;
+       struct cw1200_pm_state *pm_state = &priv->pm_state;
+       struct cw1200_suspend_state *state;
+
+       state = pm_state->suspend_state;
+       pm_state->suspend_state = NULL;
+
+       /* Disable IRQ wake */
+       priv->hwbus_ops->power_mgmt(priv->hwbus_priv, false);
+
+       /* Scan.lock must be released before BH is resumed other way
+        * in case when BSS_LOST command arrived the processing of the
+        * command will be delayed.
+        */
+       up(&priv->scan.lock);
+
+       /* Resume BH thread */
+       WARN_ON(cw1200_bh_resume(priv));
+
+       /* Restores previous PS mode */
+       if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) {
+               priv->powersave_mode.mode = state->prev_ps_mode;
+               cw1200_set_pm(priv, &priv->powersave_mode);
+       }
+
+       if (state->beacon_skipping) {
+               wsm_set_beacon_wakeup_period(priv, priv->beacon_int *
+                                            priv->join_dtim_period >
+                                            MAX_BEACON_SKIP_TIME_MS ? 1 :
+                                            priv->join_dtim_period, 0);
+               state->beacon_skipping = false;
+       }
+
+       /* Resume delayed work */
+       cw1200_resume_work(priv, &priv->bss_loss_work,
+                          state->bss_loss_tmo);
+       cw1200_resume_work(priv, &priv->join_timeout,
+                          state->join_tmo);
+       cw1200_resume_work(priv, &priv->scan.probe_work,
+                          state->direct_probe);
+       cw1200_resume_work(priv, &priv->link_id_gc_work,
+                          state->link_id_gc);
+
+       /* Remove UDP port filter */
+       wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
+
+       /* Remove ethernet frame type filter */
+       wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
+
+       /* Unlock datapath */
+       wsm_unlock_tx(priv);
+
+       /* Unlock configuration mutex */
+       mutex_unlock(&priv->conf_mutex);
+
+       /* Free memory */
+       kfree(state);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/cw1200/pm.h b/drivers/net/wireless/cw1200/pm.h
new file mode 100644 (file)
index 0000000..3ed90ff
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 PM_H_INCLUDED
+#define PM_H_INCLUDED
+
+/* ******************************************************************** */
+/* mac80211 API                                                                */
+
+/* extern */  struct cw1200_common;
+/* private */ struct cw1200_suspend_state;
+
+struct cw1200_pm_state {
+       struct cw1200_suspend_state *suspend_state;
+       struct timer_list stay_awake;
+       struct platform_device *pm_dev;
+       spinlock_t lock; /* Protect access */
+};
+
+#ifdef CONFIG_PM
+int cw1200_pm_init(struct cw1200_pm_state *pm,
+                   struct cw1200_common *priv);
+void cw1200_pm_deinit(struct cw1200_pm_state *pm);
+int cw1200_wow_suspend(struct ieee80211_hw *hw,
+                      struct cfg80211_wowlan *wowlan);
+int cw1200_wow_resume(struct ieee80211_hw *hw);
+int cw1200_can_suspend(struct cw1200_common *priv);
+void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
+                         unsigned long tmo);
+#else
+static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
+                                       unsigned long tmo) {
+}
+#endif
+#endif
diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/cw1200/queue.c
new file mode 100644 (file)
index 0000000..9c3925f
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+ * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <net/mac80211.h>
+#include <linux/sched.h>
+#include "queue.h"
+#include "cw1200.h"
+#include "debug.h"
+
+/* private */ struct cw1200_queue_item
+{
+       struct list_head        head;
+       struct sk_buff          *skb;
+       u32                     packet_id;
+       unsigned long           queue_timestamp;
+       unsigned long           xmit_timestamp;
+       struct cw1200_txpriv    txpriv;
+       u8                      generation;
+};
+
+static inline void __cw1200_queue_lock(struct cw1200_queue *queue)
+{
+       struct cw1200_queue_stats *stats = queue->stats;
+       if (queue->tx_locked_cnt++ == 0) {
+               pr_debug("[TX] Queue %d is locked.\n",
+                        queue->queue_id);
+               ieee80211_stop_queue(stats->priv->hw, queue->queue_id);
+       }
+}
+
+static inline void __cw1200_queue_unlock(struct cw1200_queue *queue)
+{
+       struct cw1200_queue_stats *stats = queue->stats;
+       BUG_ON(!queue->tx_locked_cnt);
+       if (--queue->tx_locked_cnt == 0) {
+               pr_debug("[TX] Queue %d is unlocked.\n",
+                        queue->queue_id);
+               ieee80211_wake_queue(stats->priv->hw, queue->queue_id);
+       }
+}
+
+static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation,
+                                        u8 *queue_id, u8 *item_generation,
+                                        u8 *item_id)
+{
+       *item_id                = (packet_id >>  0) & 0xFF;
+       *item_generation        = (packet_id >>  8) & 0xFF;
+       *queue_id               = (packet_id >> 16) & 0xFF;
+       *queue_generation       = (packet_id >> 24) & 0xFF;
+}
+
+static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id,
+                                           u8 item_generation, u8 item_id)
+{
+       return ((u32)item_id << 0) |
+               ((u32)item_generation << 8) |
+               ((u32)queue_id << 16) |
+               ((u32)queue_generation << 24);
+}
+
+static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats,
+                                struct list_head *gc_list)
+{
+       struct cw1200_queue_item *item, *tmp;
+
+       list_for_each_entry_safe(item, tmp, gc_list, head) {
+               list_del(&item->head);
+               stats->skb_dtor(stats->priv, item->skb, &item->txpriv);
+               kfree(item);
+       }
+}
+
+static void cw1200_queue_register_post_gc(struct list_head *gc_list,
+                                         struct cw1200_queue_item *item)
+{
+       struct cw1200_queue_item *gc_item;
+       gc_item = kmalloc(sizeof(struct cw1200_queue_item),
+                       GFP_ATOMIC);
+       BUG_ON(!gc_item);
+       memcpy(gc_item, item, sizeof(struct cw1200_queue_item));
+       list_add_tail(&gc_item->head, gc_list);
+}
+
+static void __cw1200_queue_gc(struct cw1200_queue *queue,
+                             struct list_head *head,
+                             bool unlock)
+{
+       struct cw1200_queue_stats *stats = queue->stats;
+       struct cw1200_queue_item *item = NULL, *tmp;
+       bool wakeup_stats = false;
+
+       list_for_each_entry_safe(item, tmp, &queue->queue, head) {
+               if (jiffies - item->queue_timestamp < queue->ttl)
+                       break;
+               --queue->num_queued;
+               --queue->link_map_cache[item->txpriv.link_id];
+               spin_lock_bh(&stats->lock);
+               --stats->num_queued;
+               if (!--stats->link_map_cache[item->txpriv.link_id])
+                       wakeup_stats = true;
+               spin_unlock_bh(&stats->lock);
+               cw1200_debug_tx_ttl(stats->priv);
+               cw1200_queue_register_post_gc(head, item);
+               item->skb = NULL;
+               list_move_tail(&item->head, &queue->free_pool);
+       }
+
+       if (wakeup_stats)
+               wake_up(&stats->wait_link_id_empty);
+
+       if (queue->overfull) {
+               if (queue->num_queued <= (queue->capacity >> 1)) {
+                       queue->overfull = false;
+                       if (unlock)
+                               __cw1200_queue_unlock(queue);
+               } else if (item) {
+                       unsigned long tmo = item->queue_timestamp + queue->ttl;
+                       mod_timer(&queue->gc, tmo);
+                       cw1200_pm_stay_awake(&stats->priv->pm_state,
+                                            tmo - jiffies);
+               }
+       }
+}
+
+static void cw1200_queue_gc(unsigned long arg)
+{
+       LIST_HEAD(list);
+       struct cw1200_queue *queue =
+               (struct cw1200_queue *)arg;
+
+       spin_lock_bh(&queue->lock);
+       __cw1200_queue_gc(queue, &list, true);
+       spin_unlock_bh(&queue->lock);
+       cw1200_queue_post_gc(queue->stats, &list);
+}
+
+int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
+                           size_t map_capacity,
+                           cw1200_queue_skb_dtor_t skb_dtor,
+                           struct cw1200_common *priv)
+{
+       memset(stats, 0, sizeof(*stats));
+       stats->map_capacity = map_capacity;
+       stats->skb_dtor = skb_dtor;
+       stats->priv = priv;
+       spin_lock_init(&stats->lock);
+       init_waitqueue_head(&stats->wait_link_id_empty);
+
+       stats->link_map_cache = kzalloc(sizeof(int) * map_capacity,
+                                       GFP_KERNEL);
+       if (!stats->link_map_cache)
+               return -ENOMEM;
+
+       return 0;
+}
+
+int cw1200_queue_init(struct cw1200_queue *queue,
+                     struct cw1200_queue_stats *stats,
+                     u8 queue_id,
+                     size_t capacity,
+                     unsigned long ttl)
+{
+       size_t i;
+
+       memset(queue, 0, sizeof(*queue));
+       queue->stats = stats;
+       queue->capacity = capacity;
+       queue->queue_id = queue_id;
+       queue->ttl = ttl;
+       INIT_LIST_HEAD(&queue->queue);
+       INIT_LIST_HEAD(&queue->pending);
+       INIT_LIST_HEAD(&queue->free_pool);
+       spin_lock_init(&queue->lock);
+       init_timer(&queue->gc);
+       queue->gc.data = (unsigned long)queue;
+       queue->gc.function = cw1200_queue_gc;
+
+       queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity,
+                       GFP_KERNEL);
+       if (!queue->pool)
+               return -ENOMEM;
+
+       queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity,
+                       GFP_KERNEL);
+       if (!queue->link_map_cache) {
+               kfree(queue->pool);
+               queue->pool = NULL;
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < capacity; ++i)
+               list_add_tail(&queue->pool[i].head, &queue->free_pool);
+
+       return 0;
+}
+
+int cw1200_queue_clear(struct cw1200_queue *queue)
+{
+       int i;
+       LIST_HEAD(gc_list);
+       struct cw1200_queue_stats *stats = queue->stats;
+       struct cw1200_queue_item *item, *tmp;
+
+       spin_lock_bh(&queue->lock);
+       queue->generation++;
+       list_splice_tail_init(&queue->queue, &queue->pending);
+       list_for_each_entry_safe(item, tmp, &queue->pending, head) {
+               WARN_ON(!item->skb);
+               cw1200_queue_register_post_gc(&gc_list, item);
+               item->skb = NULL;
+               list_move_tail(&item->head, &queue->free_pool);
+       }
+       queue->num_queued = 0;
+       queue->num_pending = 0;
+
+       spin_lock_bh(&stats->lock);
+       for (i = 0; i < stats->map_capacity; ++i) {
+               stats->num_queued -= queue->link_map_cache[i];
+               stats->link_map_cache[i] -= queue->link_map_cache[i];
+               queue->link_map_cache[i] = 0;
+       }
+       spin_unlock_bh(&stats->lock);
+       if (queue->overfull) {
+               queue->overfull = false;
+               __cw1200_queue_unlock(queue);
+       }
+       spin_unlock_bh(&queue->lock);
+       wake_up(&stats->wait_link_id_empty);
+       cw1200_queue_post_gc(stats, &gc_list);
+       return 0;
+}
+
+void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats)
+{
+       kfree(stats->link_map_cache);
+       stats->link_map_cache = NULL;
+}
+
+void cw1200_queue_deinit(struct cw1200_queue *queue)
+{
+       cw1200_queue_clear(queue);
+       del_timer_sync(&queue->gc);
+       INIT_LIST_HEAD(&queue->free_pool);
+       kfree(queue->pool);
+       kfree(queue->link_map_cache);
+       queue->pool = NULL;
+       queue->link_map_cache = NULL;
+       queue->capacity = 0;
+}
+
+size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
+                                  u32 link_id_map)
+{
+       size_t ret;
+       int i, bit;
+       size_t map_capacity = queue->stats->map_capacity;
+
+       if (!link_id_map)
+               return 0;
+
+       spin_lock_bh(&queue->lock);
+       if (link_id_map == (u32)-1) {
+               ret = queue->num_queued - queue->num_pending;
+       } else {
+               ret = 0;
+               for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) {
+                       if (link_id_map & bit)
+                               ret += queue->link_map_cache[i];
+               }
+       }
+       spin_unlock_bh(&queue->lock);
+       return ret;
+}
+
+int cw1200_queue_put(struct cw1200_queue *queue,
+                    struct sk_buff *skb,
+                    struct cw1200_txpriv *txpriv)
+{
+       int ret = 0;
+       LIST_HEAD(gc_list);
+       struct cw1200_queue_stats *stats = queue->stats;
+
+       if (txpriv->link_id >= queue->stats->map_capacity)
+               return -EINVAL;
+
+       spin_lock_bh(&queue->lock);
+       if (!WARN_ON(list_empty(&queue->free_pool))) {
+               struct cw1200_queue_item *item = list_first_entry(
+                       &queue->free_pool, struct cw1200_queue_item, head);
+               BUG_ON(item->skb);
+
+               list_move_tail(&item->head, &queue->queue);
+               item->skb = skb;
+               item->txpriv = *txpriv;
+               item->generation = 0;
+               item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
+                                                           queue->queue_id,
+                                                           item->generation,
+                                                           item - queue->pool);
+               item->queue_timestamp = jiffies;
+
+               ++queue->num_queued;
+               ++queue->link_map_cache[txpriv->link_id];
+
+               spin_lock_bh(&stats->lock);
+               ++stats->num_queued;
+               ++stats->link_map_cache[txpriv->link_id];
+               spin_unlock_bh(&stats->lock);
+
+               /* TX may happen in parallel sometimes.
+                * Leave extra queue slots so we don't overflow.
+                */
+               if (queue->overfull == false &&
+                   queue->num_queued >=
+                   (queue->capacity - (num_present_cpus() - 1))) {
+                       queue->overfull = true;
+                       __cw1200_queue_lock(queue);
+                       mod_timer(&queue->gc, jiffies);
+               }
+       } else {
+               ret = -ENOENT;
+       }
+       spin_unlock_bh(&queue->lock);
+       return ret;
+}
+
+int cw1200_queue_get(struct cw1200_queue *queue,
+                    u32 link_id_map,
+                    struct wsm_tx **tx,
+                    struct ieee80211_tx_info **tx_info,
+                    const struct cw1200_txpriv **txpriv)
+{
+       int ret = -ENOENT;
+       struct cw1200_queue_item *item;
+       struct cw1200_queue_stats *stats = queue->stats;
+       bool wakeup_stats = false;
+
+       spin_lock_bh(&queue->lock);
+       list_for_each_entry(item, &queue->queue, head) {
+               if (link_id_map & BIT(item->txpriv.link_id)) {
+                       ret = 0;
+                       break;
+               }
+       }
+
+       if (!WARN_ON(ret)) {
+               *tx = (struct wsm_tx *)item->skb->data;
+               *tx_info = IEEE80211_SKB_CB(item->skb);
+               *txpriv = &item->txpriv;
+               (*tx)->packet_id = item->packet_id;
+               list_move_tail(&item->head, &queue->pending);
+               ++queue->num_pending;
+               --queue->link_map_cache[item->txpriv.link_id];
+               item->xmit_timestamp = jiffies;
+
+               spin_lock_bh(&stats->lock);
+               --stats->num_queued;
+               if (!--stats->link_map_cache[item->txpriv.link_id])
+                       wakeup_stats = true;
+               spin_unlock_bh(&stats->lock);
+       }
+       spin_unlock_bh(&queue->lock);
+       if (wakeup_stats)
+               wake_up(&stats->wait_link_id_empty);
+       return ret;
+}
+
+int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id)
+{
+       int ret = 0;
+       u8 queue_generation, queue_id, item_generation, item_id;
+       struct cw1200_queue_item *item;
+       struct cw1200_queue_stats *stats = queue->stats;
+
+       cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+                             &item_generation, &item_id);
+
+       item = &queue->pool[item_id];
+
+       spin_lock_bh(&queue->lock);
+       BUG_ON(queue_id != queue->queue_id);
+       if (queue_generation != queue->generation) {
+               ret = -ENOENT;
+       } else if (item_id >= (unsigned) queue->capacity) {
+               WARN_ON(1);
+               ret = -EINVAL;
+       } else if (item->generation != item_generation) {
+               WARN_ON(1);
+               ret = -ENOENT;
+       } else {
+               --queue->num_pending;
+               ++queue->link_map_cache[item->txpriv.link_id];
+
+               spin_lock_bh(&stats->lock);
+               ++stats->num_queued;
+               ++stats->link_map_cache[item->txpriv.link_id];
+               spin_unlock_bh(&stats->lock);
+
+               item->generation = ++item_generation;
+               item->packet_id = cw1200_queue_mk_packet_id(queue_generation,
+                                                           queue_id,
+                                                           item_generation,
+                                                           item_id);
+               list_move(&item->head, &queue->queue);
+       }
+       spin_unlock_bh(&queue->lock);
+       return ret;
+}
+
+int cw1200_queue_requeue_all(struct cw1200_queue *queue)
+{
+       struct cw1200_queue_item *item, *tmp;
+       struct cw1200_queue_stats *stats = queue->stats;
+       spin_lock_bh(&queue->lock);
+
+       list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) {
+               --queue->num_pending;
+               ++queue->link_map_cache[item->txpriv.link_id];
+
+               spin_lock_bh(&stats->lock);
+               ++stats->num_queued;
+               ++stats->link_map_cache[item->txpriv.link_id];
+               spin_unlock_bh(&stats->lock);
+
+               ++item->generation;
+               item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
+                                                           queue->queue_id,
+                                                           item->generation,
+                                                           item - queue->pool);
+               list_move(&item->head, &queue->queue);
+       }
+       spin_unlock_bh(&queue->lock);
+
+       return 0;
+}
+
+int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id)
+{
+       int ret = 0;
+       u8 queue_generation, queue_id, item_generation, item_id;
+       struct cw1200_queue_item *item;
+       struct cw1200_queue_stats *stats = queue->stats;
+       struct sk_buff *gc_skb = NULL;
+       struct cw1200_txpriv gc_txpriv;
+
+       cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+                             &item_generation, &item_id);
+
+       item = &queue->pool[item_id];
+
+       spin_lock_bh(&queue->lock);
+       BUG_ON(queue_id != queue->queue_id);
+       if (queue_generation != queue->generation) {
+               ret = -ENOENT;
+       } else if (item_id >= (unsigned) queue->capacity) {
+               WARN_ON(1);
+               ret = -EINVAL;
+       } else if (item->generation != item_generation) {
+               WARN_ON(1);
+               ret = -ENOENT;
+       } else {
+               gc_txpriv = item->txpriv;
+               gc_skb = item->skb;
+               item->skb = NULL;
+               --queue->num_pending;
+               --queue->num_queued;
+               ++queue->num_sent;
+               ++item->generation;
+               /* Do not use list_move_tail here, but list_move:
+                * try to utilize cache row.
+                */
+               list_move(&item->head, &queue->free_pool);
+
+               if (queue->overfull &&
+                   (queue->num_queued <= (queue->capacity >> 1))) {
+                       queue->overfull = false;
+                       __cw1200_queue_unlock(queue);
+               }
+       }
+       spin_unlock_bh(&queue->lock);
+
+       if (gc_skb)
+               stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv);
+
+       return ret;
+}
+
+int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
+                        struct sk_buff **skb,
+                        const struct cw1200_txpriv **txpriv)
+{
+       int ret = 0;
+       u8 queue_generation, queue_id, item_generation, item_id;
+       struct cw1200_queue_item *item;
+       cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+                             &item_generation, &item_id);
+
+       item = &queue->pool[item_id];
+
+       spin_lock_bh(&queue->lock);
+       BUG_ON(queue_id != queue->queue_id);
+       if (queue_generation != queue->generation) {
+               ret = -ENOENT;
+       } else if (item_id >= (unsigned) queue->capacity) {
+               WARN_ON(1);
+               ret = -EINVAL;
+       } else if (item->generation != item_generation) {
+               WARN_ON(1);
+               ret = -ENOENT;
+       } else {
+               *skb = item->skb;
+               *txpriv = &item->txpriv;
+       }
+       spin_unlock_bh(&queue->lock);
+       return ret;
+}
+
+void cw1200_queue_lock(struct cw1200_queue *queue)
+{
+       spin_lock_bh(&queue->lock);
+       __cw1200_queue_lock(queue);
+       spin_unlock_bh(&queue->lock);
+}
+
+void cw1200_queue_unlock(struct cw1200_queue *queue)
+{
+       spin_lock_bh(&queue->lock);
+       __cw1200_queue_unlock(queue);
+       spin_unlock_bh(&queue->lock);
+}
+
+bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
+                                    unsigned long *timestamp,
+                                    u32 pending_frame_id)
+{
+       struct cw1200_queue_item *item;
+       bool ret;
+
+       spin_lock_bh(&queue->lock);
+       ret = !list_empty(&queue->pending);
+       if (ret) {
+               list_for_each_entry(item, &queue->pending, head) {
+                       if (item->packet_id != pending_frame_id)
+                               if (time_before(item->xmit_timestamp,
+                                               *timestamp))
+                                       *timestamp = item->xmit_timestamp;
+               }
+       }
+       spin_unlock_bh(&queue->lock);
+       return ret;
+}
+
+bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
+                                u32 link_id_map)
+{
+       bool empty = true;
+
+       spin_lock_bh(&stats->lock);
+       if (link_id_map == (u32)-1) {
+               empty = stats->num_queued == 0;
+       } else {
+               int i;
+               for (i = 0; i < stats->map_capacity; ++i) {
+                       if (link_id_map & BIT(i)) {
+                               if (stats->link_map_cache[i]) {
+                                       empty = false;
+                                       break;
+                               }
+                       }
+               }
+       }
+       spin_unlock_bh(&stats->lock);
+
+       return empty;
+}
diff --git a/drivers/net/wireless/cw1200/queue.h b/drivers/net/wireless/cw1200/queue.h
new file mode 100644 (file)
index 0000000..119f9c7
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 CW1200_QUEUE_H_INCLUDED
+#define CW1200_QUEUE_H_INCLUDED
+
+/* private */ struct cw1200_queue_item;
+
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct cw1200_common;
+/* extern */ struct ieee80211_tx_queue_stats;
+/* extern */ struct cw1200_txpriv;
+
+/* forward */ struct cw1200_queue_stats;
+
+typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv,
+                                       struct sk_buff *skb,
+                                       const struct cw1200_txpriv *txpriv);
+
+struct cw1200_queue {
+       struct cw1200_queue_stats *stats;
+       size_t                  capacity;
+       size_t                  num_queued;
+       size_t                  num_pending;
+       size_t                  num_sent;
+       struct cw1200_queue_item *pool;
+       struct list_head        queue;
+       struct list_head        free_pool;
+       struct list_head        pending;
+       int                     tx_locked_cnt;
+       int                     *link_map_cache;
+       bool                    overfull;
+       spinlock_t              lock; /* Protect queue entry */
+       u8                      queue_id;
+       u8                      generation;
+       struct timer_list       gc;
+       unsigned long           ttl;
+};
+
+struct cw1200_queue_stats {
+       spinlock_t              lock; /* Protect stats entry */
+       int                     *link_map_cache;
+       int                     num_queued;
+       size_t                  map_capacity;
+       wait_queue_head_t       wait_link_id_empty;
+       cw1200_queue_skb_dtor_t skb_dtor;
+       struct cw1200_common    *priv;
+};
+
+struct cw1200_txpriv {
+       u8 link_id;
+       u8 raw_link_id;
+       u8 tid;
+       u8 rate_id;
+       u8 offset;
+};
+
+int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
+                           size_t map_capacity,
+                           cw1200_queue_skb_dtor_t skb_dtor,
+                           struct cw1200_common *priv);
+int cw1200_queue_init(struct cw1200_queue *queue,
+                     struct cw1200_queue_stats *stats,
+                     u8 queue_id,
+                     size_t capacity,
+                     unsigned long ttl);
+int cw1200_queue_clear(struct cw1200_queue *queue);
+void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats);
+void cw1200_queue_deinit(struct cw1200_queue *queue);
+
+size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
+                                  u32 link_id_map);
+int cw1200_queue_put(struct cw1200_queue *queue,
+                    struct sk_buff *skb,
+                    struct cw1200_txpriv *txpriv);
+int cw1200_queue_get(struct cw1200_queue *queue,
+                    u32 link_id_map,
+                    struct wsm_tx **tx,
+                    struct ieee80211_tx_info **tx_info,
+                    const struct cw1200_txpriv **txpriv);
+int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id);
+int cw1200_queue_requeue_all(struct cw1200_queue *queue);
+int cw1200_queue_remove(struct cw1200_queue *queue,
+                       u32 packet_id);
+int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
+                        struct sk_buff **skb,
+                        const struct cw1200_txpriv **txpriv);
+void cw1200_queue_lock(struct cw1200_queue *queue);
+void cw1200_queue_unlock(struct cw1200_queue *queue);
+bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
+                                    unsigned long *timestamp,
+                                    u32 pending_frame_id);
+
+bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
+                                u32 link_id_map);
+
+static inline u8 cw1200_queue_get_queue_id(u32 packet_id)
+{
+       return (packet_id >> 16) & 0xFF;
+}
+
+static inline u8 cw1200_queue_get_generation(u32 packet_id)
+{
+       return (packet_id >>  8) & 0xFF;
+}
+
+#endif /* CW1200_QUEUE_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c
new file mode 100644 (file)
index 0000000..ee3c190
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * Scan implementation for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/sched.h>
+#include "cw1200.h"
+#include "scan.h"
+#include "sta.h"
+#include "pm.h"
+
+static void cw1200_scan_restart_delayed(struct cw1200_common *priv);
+
+static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
+{
+       int ret, i;
+       int tmo = 2000;
+
+       switch (priv->join_status) {
+       case CW1200_JOIN_STATUS_PRE_STA:
+       case CW1200_JOIN_STATUS_JOINING:
+               return -EBUSY;
+       default:
+               break;
+       }
+
+       wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n",
+                 scan->type, scan->num_channels, scan->flags);
+
+       for (i = 0; i < scan->num_channels; ++i)
+               tmo += scan->ch[i].max_chan_time + 10;
+
+       cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+       atomic_set(&priv->scan.in_progress, 1);
+       atomic_set(&priv->recent_scan, 1);
+       cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000);
+       queue_delayed_work(priv->workqueue, &priv->scan.timeout,
+                          tmo * HZ / 1000);
+       ret = wsm_scan(priv, scan);
+       if (ret) {
+               atomic_set(&priv->scan.in_progress, 0);
+               cancel_delayed_work_sync(&priv->scan.timeout);
+               cw1200_scan_restart_delayed(priv);
+       }
+       return ret;
+}
+
+int cw1200_hw_scan(struct ieee80211_hw *hw,
+                  struct ieee80211_vif *vif,
+                  struct cfg80211_scan_request *req)
+{
+       struct cw1200_common *priv = hw->priv;
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+       };
+       int i, ret;
+
+       if (!priv->vif)
+               return -EINVAL;
+
+       /* Scan when P2P_GO corrupt firmware MiniAP mode */
+       if (priv->join_status == CW1200_JOIN_STATUS_AP)
+               return -EOPNOTSUPP;
+
+       if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
+               req->n_ssids = 0;
+
+       wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
+                 req->n_ssids);
+
+       if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
+               return -EINVAL;
+
+       frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0,
+               req->ie_len);
+       if (!frame.skb)
+               return -ENOMEM;
+
+       if (req->ie_len)
+               memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len);
+
+       /* will be unlocked in cw1200_scan_work() */
+       down(&priv->scan.lock);
+       mutex_lock(&priv->conf_mutex);
+
+       ret = wsm_set_template_frame(priv, &frame);
+       if (!ret) {
+               /* Host want to be the probe responder. */
+               ret = wsm_set_probe_responder(priv, true);
+       }
+       if (ret) {
+               mutex_unlock(&priv->conf_mutex);
+               up(&priv->scan.lock);
+               dev_kfree_skb(frame.skb);
+               return ret;
+       }
+
+       wsm_lock_tx(priv);
+
+       BUG_ON(priv->scan.req);
+       priv->scan.req = req;
+       priv->scan.n_ssids = 0;
+       priv->scan.status = 0;
+       priv->scan.begin = &req->channels[0];
+       priv->scan.curr = priv->scan.begin;
+       priv->scan.end = &req->channels[req->n_channels];
+       priv->scan.output_power = priv->output_power;
+
+       for (i = 0; i < req->n_ssids; ++i) {
+               struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids];
+               memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
+               dst->length = req->ssids[i].ssid_len;
+               ++priv->scan.n_ssids;
+       }
+
+       mutex_unlock(&priv->conf_mutex);
+
+       if (frame.skb)
+               dev_kfree_skb(frame.skb);
+       queue_work(priv->workqueue, &priv->scan.work);
+       return 0;
+}
+
+void cw1200_scan_work(struct work_struct *work)
+{
+       struct cw1200_common *priv = container_of(work, struct cw1200_common,
+                                                       scan.work);
+       struct ieee80211_channel **it;
+       struct wsm_scan scan = {
+               .type = WSM_SCAN_TYPE_FOREGROUND,
+               .flags = WSM_SCAN_FLAG_SPLIT_METHOD,
+       };
+       bool first_run = (priv->scan.begin == priv->scan.curr &&
+                         priv->scan.begin != priv->scan.end);
+       int i;
+
+       if (first_run) {
+               /* Firmware gets crazy if scan request is sent
+                * when STA is joined but not yet associated.
+                * Force unjoin in this case.
+                */
+               if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
+                       cw1200_join_timeout(&priv->join_timeout.work);
+       }
+
+       mutex_lock(&priv->conf_mutex);
+
+       if (first_run) {
+               if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+                   !(priv->powersave_mode.mode & WSM_PSM_PS)) {
+                       struct wsm_set_pm pm = priv->powersave_mode;
+                       pm.mode = WSM_PSM_PS;
+                       cw1200_set_pm(priv, &pm);
+               } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+                       /* FW bug: driver has to restart p2p-dev mode
+                        * after scan
+                        */
+                       cw1200_disable_listening(priv);
+               }
+       }
+
+       if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
+               if (priv->scan.output_power != priv->output_power)
+                       wsm_set_output_power(priv, priv->output_power * 10);
+               if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+                   !(priv->powersave_mode.mode & WSM_PSM_PS))
+                       cw1200_set_pm(priv, &priv->powersave_mode);
+
+               if (priv->scan.status < 0)
+                       wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan failed (%d).\n",
+                                 priv->scan.status);
+               else if (priv->scan.req)
+                       wiphy_dbg(priv->hw->wiphy,
+                                 "[SCAN] Scan completed.\n");
+               else
+                       wiphy_dbg(priv->hw->wiphy,
+                                 "[SCAN] Scan canceled.\n");
+
+               priv->scan.req = NULL;
+               cw1200_scan_restart_delayed(priv);
+               wsm_unlock_tx(priv);
+               mutex_unlock(&priv->conf_mutex);
+               ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0);
+               up(&priv->scan.lock);
+               return;
+       } else {
+               struct ieee80211_channel *first = *priv->scan.curr;
+               for (it = priv->scan.curr + 1, i = 1;
+                    it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
+                    ++it, ++i) {
+                       if ((*it)->band != first->band)
+                               break;
+                       if (((*it)->flags ^ first->flags) &
+                                       IEEE80211_CHAN_PASSIVE_SCAN)
+                               break;
+                       if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
+                           (*it)->max_power != first->max_power)
+                               break;
+               }
+               scan.band = first->band;
+
+               if (priv->scan.req->no_cck)
+                       scan.max_tx_rate = WSM_TRANSMIT_RATE_6;
+               else
+                       scan.max_tx_rate = WSM_TRANSMIT_RATE_1;
+               scan.num_probes =
+                       (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2;
+               scan.num_ssids = priv->scan.n_ssids;
+               scan.ssids = &priv->scan.ssids[0];
+               scan.num_channels = it - priv->scan.curr;
+               /* TODO: Is it optimal? */
+               scan.probe_delay = 100;
+               /* It is not stated in WSM specification, however
+                * FW team says that driver may not use FG scan
+                * when joined.
+                */
+               if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+                       scan.type = WSM_SCAN_TYPE_BACKGROUND;
+                       scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+               }
+               scan.ch = kzalloc(
+                       sizeof(struct wsm_scan_ch) * (it - priv->scan.curr),
+                       GFP_KERNEL);
+               if (!scan.ch) {
+                       priv->scan.status = -ENOMEM;
+                       goto fail;
+               }
+               for (i = 0; i < scan.num_channels; ++i) {
+                       scan.ch[i].number = priv->scan.curr[i]->hw_value;
+                       if (priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
+                               scan.ch[i].min_chan_time = 50;
+                               scan.ch[i].max_chan_time = 100;
+                       } else {
+                               scan.ch[i].min_chan_time = 10;
+                               scan.ch[i].max_chan_time = 25;
+                       }
+               }
+               if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
+                   priv->scan.output_power != first->max_power) {
+                       priv->scan.output_power = first->max_power;
+                       wsm_set_output_power(priv,
+                                            priv->scan.output_power * 10);
+               }
+               priv->scan.status = cw1200_scan_start(priv, &scan);
+               kfree(scan.ch);
+               if (priv->scan.status)
+                       goto fail;
+               priv->scan.curr = it;
+       }
+       mutex_unlock(&priv->conf_mutex);
+       return;
+
+fail:
+       priv->scan.curr = priv->scan.end;
+       mutex_unlock(&priv->conf_mutex);
+       queue_work(priv->workqueue, &priv->scan.work);
+       return;
+}
+
+static void cw1200_scan_restart_delayed(struct cw1200_common *priv)
+{
+       /* FW bug: driver has to restart p2p-dev mode after scan. */
+       if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+               cw1200_enable_listening(priv);
+               cw1200_update_filtering(priv);
+       }
+
+       if (priv->delayed_unjoin) {
+               priv->delayed_unjoin = false;
+               if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+                       wsm_unlock_tx(priv);
+       } else if (priv->delayed_link_loss) {
+                       wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n");
+                       priv->delayed_link_loss = 0;
+                       cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
+       }
+}
+
+static void cw1200_scan_complete(struct cw1200_common *priv)
+{
+       queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
+       if (priv->scan.direct_probe) {
+               wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n");
+               cw1200_scan_restart_delayed(priv);
+               priv->scan.direct_probe = 0;
+               up(&priv->scan.lock);
+               wsm_unlock_tx(priv);
+       } else {
+               cw1200_scan_work(&priv->scan.work);
+       }
+}
+
+void cw1200_scan_failed_cb(struct cw1200_common *priv)
+{
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+               /* STA is stopped. */
+               return;
+
+       if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
+               priv->scan.status = -EIO;
+               queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
+       }
+}
+
+
+void cw1200_scan_complete_cb(struct cw1200_common *priv,
+                               struct wsm_scan_complete *arg)
+{
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+               /* STA is stopped. */
+               return;
+
+       if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
+               priv->scan.status = 1;
+               queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
+       }
+}
+
+void cw1200_clear_recent_scan_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common,
+                            clear_recent_scan_work.work);
+       atomic_xchg(&priv->recent_scan, 0);
+}
+
+void cw1200_scan_timeout(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, scan.timeout.work);
+       if (atomic_xchg(&priv->scan.in_progress, 0)) {
+               if (priv->scan.status > 0) {
+                       priv->scan.status = 0;
+               } else if (!priv->scan.status) {
+                       wiphy_warn(priv->hw->wiphy,
+                                  "Timeout waiting for scan complete notification.\n");
+                       priv->scan.status = -ETIMEDOUT;
+                       priv->scan.curr = priv->scan.end;
+                       wsm_stop_scan(priv);
+               }
+               cw1200_scan_complete(priv);
+       }
+}
+
+void cw1200_probe_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, scan.probe_work.work);
+       u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
+       struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+       const struct cw1200_txpriv *txpriv;
+       struct wsm_tx *wsm;
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+       };
+       struct wsm_ssid ssids[1] = {{
+               .length = 0,
+       } };
+       struct wsm_scan_ch ch[1] = {{
+               .min_chan_time = 0,
+               .max_chan_time = 10,
+       } };
+       struct wsm_scan scan = {
+               .type = WSM_SCAN_TYPE_FOREGROUND,
+               .num_probes = 1,
+               .probe_delay = 0,
+               .num_channels = 1,
+               .ssids = ssids,
+               .ch = ch,
+       };
+       u8 *ies;
+       size_t ies_len;
+       int ret;
+
+       wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n");
+
+       mutex_lock(&priv->conf_mutex);
+       if (down_trylock(&priv->scan.lock)) {
+               /* Scan is already in progress. Requeue self. */
+               schedule();
+               queue_delayed_work(priv->workqueue,
+                                  &priv->scan.probe_work, HZ / 10);
+               mutex_unlock(&priv->conf_mutex);
+               return;
+       }
+
+       /* Make sure we still have a pending probe req */
+       if (cw1200_queue_get_skb(queue, priv->pending_frame_id,
+                                &frame.skb, &txpriv)) {
+               up(&priv->scan.lock);
+               mutex_unlock(&priv->conf_mutex);
+               wsm_unlock_tx(priv);
+               return;
+       }
+       wsm = (struct wsm_tx *)frame.skb->data;
+       scan.max_tx_rate = wsm->max_tx_rate;
+       scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+               WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+       if (priv->join_status == CW1200_JOIN_STATUS_STA ||
+           priv->join_status == CW1200_JOIN_STATUS_IBSS) {
+               scan.type = WSM_SCAN_TYPE_BACKGROUND;
+               scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+       }
+       ch[0].number = priv->channel->hw_value;
+
+       skb_pull(frame.skb, txpriv->offset);
+
+       ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
+       ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
+
+       if (ies_len) {
+               u8 *ssidie =
+                       (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
+               if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
+                       u8 *nextie = &ssidie[2 + ssidie[1]];
+                       /* Remove SSID from the IE list. It has to be provided
+                        * as a separate argument in cw1200_scan_start call
+                        */
+
+                       /* Store SSID localy */
+                       ssids[0].length = ssidie[1];
+                       memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
+                       scan.num_ssids = 1;
+
+                       /* Remove SSID from IE list */
+                       ssidie[1] = 0;
+                       memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
+                       skb_trim(frame.skb, frame.skb->len - ssids[0].length);
+               }
+       }
+
+       /* FW bug: driver has to restart p2p-dev mode after scan */
+       if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+               cw1200_disable_listening(priv);
+       ret = wsm_set_template_frame(priv, &frame);
+       priv->scan.direct_probe = 1;
+       if (!ret) {
+               wsm_flush_tx(priv);
+               ret = cw1200_scan_start(priv, &scan);
+       }
+       mutex_unlock(&priv->conf_mutex);
+
+       skb_push(frame.skb, txpriv->offset);
+       if (!ret)
+               IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
+       BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id));
+
+       if (ret) {
+               priv->scan.direct_probe = 0;
+               up(&priv->scan.lock);
+               wsm_unlock_tx(priv);
+       }
+
+       return;
+}
diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/cw1200/scan.h
new file mode 100644 (file)
index 0000000..5a8296c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Scan interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 SCAN_H_INCLUDED
+#define SCAN_H_INCLUDED
+
+#include <linux/semaphore.h>
+#include "wsm.h"
+
+/* external */ struct sk_buff;
+/* external */ struct cfg80211_scan_request;
+/* external */ struct ieee80211_channel;
+/* external */ struct ieee80211_hw;
+/* external */ struct work_struct;
+
+struct cw1200_scan {
+       struct semaphore lock;
+       struct work_struct work;
+       struct delayed_work timeout;
+       struct cfg80211_scan_request *req;
+       struct ieee80211_channel **begin;
+       struct ieee80211_channel **curr;
+       struct ieee80211_channel **end;
+       struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS];
+       int output_power;
+       int n_ssids;
+       int status;
+       atomic_t in_progress;
+       /* Direct probe requests workaround */
+       struct delayed_work probe_work;
+       int direct_probe;
+};
+
+int cw1200_hw_scan(struct ieee80211_hw *hw,
+                  struct ieee80211_vif *vif,
+                  struct cfg80211_scan_request *req);
+void cw1200_scan_work(struct work_struct *work);
+void cw1200_scan_timeout(struct work_struct *work);
+void cw1200_clear_recent_scan_work(struct work_struct *work);
+void cw1200_scan_complete_cb(struct cw1200_common *priv,
+                            struct wsm_scan_complete *arg);
+void cw1200_scan_failed_cb(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* Raw probe requests TX workaround                                    */
+void cw1200_probe_work(struct work_struct *work);
+
+#endif
diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c
new file mode 100644 (file)
index 0000000..7365674
--- /dev/null
@@ -0,0 +1,2403 @@
+/*
+ * Mac80211 STA API for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+
+#include "cw1200.h"
+#include "sta.h"
+#include "fwio.h"
+#include "bh.h"
+#include "debug.h"
+
+#ifndef ERP_INFO_BYTE_OFFSET
+#define ERP_INFO_BYTE_OFFSET 2
+#endif
+
+static void cw1200_do_join(struct cw1200_common *priv);
+static void cw1200_do_unjoin(struct cw1200_common *priv);
+
+static int cw1200_upload_beacon(struct cw1200_common *priv);
+static int cw1200_upload_pspoll(struct cw1200_common *priv);
+static int cw1200_upload_null(struct cw1200_common *priv);
+static int cw1200_upload_qosnull(struct cw1200_common *priv);
+static int cw1200_start_ap(struct cw1200_common *priv);
+static int cw1200_update_beaconing(struct cw1200_common *priv);
+static int cw1200_enable_beaconing(struct cw1200_common *priv,
+                                  bool enable);
+static void __cw1200_sta_notify(struct ieee80211_hw *dev,
+                               struct ieee80211_vif *vif,
+                               enum sta_notify_cmd notify_cmd,
+                               int link_id);
+static int __cw1200_flush(struct cw1200_common *priv, bool drop);
+
+static inline void __cw1200_free_event_queue(struct list_head *list)
+{
+       struct cw1200_wsm_event *event, *tmp;
+       list_for_each_entry_safe(event, tmp, list, link) {
+               list_del(&event->link);
+               kfree(event);
+       }
+}
+
+/* ******************************************************************** */
+/* STA API                                                             */
+
+int cw1200_start(struct ieee80211_hw *dev)
+{
+       struct cw1200_common *priv = dev->priv;
+       int ret = 0;
+
+       cw1200_pm_stay_awake(&priv->pm_state, HZ);
+
+       mutex_lock(&priv->conf_mutex);
+
+       /* default EDCA */
+       WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false);
+       WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false);
+       WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false);
+       WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false);
+       ret = wsm_set_edca_params(priv, &priv->edca);
+       if (ret)
+               goto out;
+
+       ret = cw1200_set_uapsd_param(priv, &priv->edca);
+       if (ret)
+               goto out;
+
+       priv->setbssparams_done = false;
+
+       memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
+       priv->mode = NL80211_IFTYPE_MONITOR;
+       priv->wep_default_key_id = -1;
+
+       priv->cqm_beacon_loss_count = 10;
+
+       ret = cw1200_setup_mac(priv);
+       if (ret)
+               goto out;
+
+out:
+       mutex_unlock(&priv->conf_mutex);
+       return ret;
+}
+
+void cw1200_stop(struct ieee80211_hw *dev)
+{
+       struct cw1200_common *priv = dev->priv;
+       LIST_HEAD(list);
+       int i;
+
+       wsm_lock_tx(priv);
+
+       while (down_trylock(&priv->scan.lock)) {
+               /* Scan is in progress. Force it to stop. */
+               priv->scan.req = NULL;
+               schedule();
+       }
+       up(&priv->scan.lock);
+
+       cancel_delayed_work_sync(&priv->scan.probe_work);
+       cancel_delayed_work_sync(&priv->scan.timeout);
+       cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+       cancel_delayed_work_sync(&priv->join_timeout);
+       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+       cancel_work_sync(&priv->unjoin_work);
+       cancel_delayed_work_sync(&priv->link_id_gc_work);
+       flush_workqueue(priv->workqueue);
+       del_timer_sync(&priv->mcast_timeout);
+       mutex_lock(&priv->conf_mutex);
+       priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+       priv->listening = false;
+
+       spin_lock(&priv->event_queue_lock);
+       list_splice_init(&priv->event_queue, &list);
+       spin_unlock(&priv->event_queue_lock);
+       __cw1200_free_event_queue(&list);
+
+
+       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+       priv->join_pending = false;
+
+       for (i = 0; i < 4; i++)
+               cw1200_queue_clear(&priv->tx_queue[i]);
+       mutex_unlock(&priv->conf_mutex);
+       tx_policy_clean(priv);
+
+       /* HACK! */
+       if (atomic_xchg(&priv->tx_lock, 1) != 1)
+               pr_debug("[STA] TX is force-unlocked due to stop request.\n");
+
+       wsm_unlock_tx(priv);
+       atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */
+}
+
+static int cw1200_bssloss_mitigation = 1;
+module_param(cw1200_bssloss_mitigation, int, 0644);
+MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)");
+
+
+void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
+                            int init, int good, int bad)
+{
+       int tx = 0;
+
+       priv->delayed_link_loss = 0;
+       cancel_work_sync(&priv->bss_params_work);
+
+       pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n",
+                priv->bss_loss_state,
+                init, good, bad,
+                atomic_read(&priv->tx_lock),
+                priv->delayed_unjoin);
+
+       /* If we have a pending unjoin */
+       if (priv->delayed_unjoin)
+               return;
+
+       if (init) {
+               queue_delayed_work(priv->workqueue,
+                                  &priv->bss_loss_work,
+                                  HZ);
+               priv->bss_loss_state = 0;
+
+               /* Skip the confimration procedure in P2P case */
+               if (!priv->vif->p2p && !atomic_read(&priv->tx_lock))
+                       tx = 1;
+       } else if (good) {
+               cancel_delayed_work_sync(&priv->bss_loss_work);
+               priv->bss_loss_state = 0;
+               queue_work(priv->workqueue, &priv->bss_params_work);
+       } else if (bad) {
+               /* XXX Should we just keep going until we time out? */
+               if (priv->bss_loss_state < 3)
+                       tx = 1;
+       } else {
+               cancel_delayed_work_sync(&priv->bss_loss_work);
+               priv->bss_loss_state = 0;
+       }
+
+       /* Bypass mitigation if it's disabled */
+       if (!cw1200_bssloss_mitigation)
+               tx = 0;
+
+       /* Spit out a NULL packet to our AP if necessary */
+       if (tx) {
+               struct sk_buff *skb;
+
+               priv->bss_loss_state++;
+
+               skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+               WARN_ON(!skb);
+               if (skb)
+                       cw1200_tx(priv->hw, NULL, skb);
+       }
+}
+
+int cw1200_add_interface(struct ieee80211_hw *dev,
+                        struct ieee80211_vif *vif)
+{
+       int ret;
+       struct cw1200_common *priv = dev->priv;
+       /* __le32 auto_calibration_mode = __cpu_to_le32(1); */
+
+       vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+                            IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+
+       mutex_lock(&priv->conf_mutex);
+
+       if (priv->mode != NL80211_IFTYPE_MONITOR) {
+               mutex_unlock(&priv->conf_mutex);
+               return -EOPNOTSUPP;
+       }
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
+       case NL80211_IFTYPE_AP:
+               priv->mode = vif->type;
+               break;
+       default:
+               mutex_unlock(&priv->conf_mutex);
+               return -EOPNOTSUPP;
+       }
+
+       priv->vif = vif;
+       memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
+       ret = cw1200_setup_mac(priv);
+       /* Enable auto-calibration */
+       /* Exception in subsequent channel switch; disabled.
+        *  wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
+        *      &auto_calibration_mode, sizeof(auto_calibration_mode));
+       */
+
+       mutex_unlock(&priv->conf_mutex);
+       return ret;
+}
+
+void cw1200_remove_interface(struct ieee80211_hw *dev,
+                            struct ieee80211_vif *vif)
+{
+       struct cw1200_common *priv = dev->priv;
+       struct wsm_reset reset = {
+               .reset_statistics = true,
+       };
+       int i;
+
+       mutex_lock(&priv->conf_mutex);
+       switch (priv->join_status) {
+       case CW1200_JOIN_STATUS_JOINING:
+       case CW1200_JOIN_STATUS_PRE_STA:
+       case CW1200_JOIN_STATUS_STA:
+       case CW1200_JOIN_STATUS_IBSS:
+               wsm_lock_tx(priv);
+               if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+                       wsm_unlock_tx(priv);
+               break;
+       case CW1200_JOIN_STATUS_AP:
+               for (i = 0; priv->link_id_map; ++i) {
+                       if (priv->link_id_map & BIT(i)) {
+                               reset.link_id = i;
+                               wsm_reset(priv, &reset);
+                               priv->link_id_map &= ~BIT(i);
+                       }
+               }
+               memset(priv->link_id_db, 0, sizeof(priv->link_id_db));
+               priv->sta_asleep_mask = 0;
+               priv->enable_beacon = false;
+               priv->tx_multicast = false;
+               priv->aid0_bit_set = false;
+               priv->buffered_multicasts = false;
+               priv->pspoll_mask = 0;
+               reset.link_id = 0;
+               wsm_reset(priv, &reset);
+               break;
+       case CW1200_JOIN_STATUS_MONITOR:
+               cw1200_update_listening(priv, false);
+               break;
+       default:
+               break;
+       }
+       priv->vif = NULL;
+       priv->mode = NL80211_IFTYPE_MONITOR;
+       memset(priv->mac_addr, 0, ETH_ALEN);
+       memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo));
+       cw1200_free_keys(priv);
+       cw1200_setup_mac(priv);
+       priv->listening = false;
+       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+       if (!__cw1200_flush(priv, true))
+               wsm_unlock_tx(priv);
+
+       mutex_unlock(&priv->conf_mutex);
+}
+
+int cw1200_change_interface(struct ieee80211_hw *dev,
+                           struct ieee80211_vif *vif,
+                           enum nl80211_iftype new_type,
+                           bool p2p)
+{
+       int ret = 0;
+       pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type,
+                p2p, vif->type, vif->p2p);
+
+       if (new_type != vif->type || vif->p2p != p2p) {
+               cw1200_remove_interface(dev, vif);
+               vif->type = new_type;
+               vif->p2p = p2p;
+               ret = cw1200_add_interface(dev, vif);
+       }
+
+       return ret;
+}
+
+int cw1200_config(struct ieee80211_hw *dev, u32 changed)
+{
+       int ret = 0;
+       struct cw1200_common *priv = dev->priv;
+       struct ieee80211_conf *conf = &dev->conf;
+
+       pr_debug("CONFIG CHANGED:  %08x\n", changed);
+
+       down(&priv->scan.lock);
+       mutex_lock(&priv->conf_mutex);
+       /* TODO: IEEE80211_CONF_CHANGE_QOS */
+       /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */
+
+       if (changed & IEEE80211_CONF_CHANGE_POWER) {
+               priv->output_power = conf->power_level;
+               pr_debug("[STA] TX power: %d\n", priv->output_power);
+               wsm_set_output_power(priv, priv->output_power * 10);
+       }
+
+       if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) &&
+           (priv->channel != conf->chandef.chan)) {
+               struct ieee80211_channel *ch = conf->chandef.chan;
+               struct wsm_switch_channel channel = {
+                       .channel_number = ch->hw_value,
+               };
+               pr_debug("[STA] Freq %d (wsm ch: %d).\n",
+                        ch->center_freq, ch->hw_value);
+
+               /* __cw1200_flush() implicitly locks tx, if successful */
+               if (!__cw1200_flush(priv, false)) {
+                       if (!wsm_switch_channel(priv, &channel)) {
+                               ret = wait_event_timeout(priv->channel_switch_done,
+                                                        !priv->channel_switch_in_progress,
+                                                        3 * HZ);
+                               if (ret) {
+                                       /* Already unlocks if successful */
+                                       priv->channel = ch;
+                                       ret = 0;
+                               } else {
+                                       ret = -ETIMEDOUT;
+                               }
+                       } else {
+                               /* Unlock if switch channel fails */
+                               wsm_unlock_tx(priv);
+                       }
+               }
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_PS) {
+               if (!(conf->flags & IEEE80211_CONF_PS))
+                       priv->powersave_mode.mode = WSM_PSM_ACTIVE;
+               else if (conf->dynamic_ps_timeout <= 0)
+                       priv->powersave_mode.mode = WSM_PSM_PS;
+               else
+                       priv->powersave_mode.mode = WSM_PSM_FAST_PS;
+
+               /* Firmware requires that value for this 1-byte field must
+                * be specified in units of 500us. Values above the 128ms
+                * threshold are not supported.
+                */
+               if (conf->dynamic_ps_timeout >= 0x80)
+                       priv->powersave_mode.fast_psm_idle_period = 0xFF;
+               else
+                       priv->powersave_mode.fast_psm_idle_period =
+                                       conf->dynamic_ps_timeout << 1;
+
+               if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+                   priv->bss_params.aid)
+                       cw1200_set_pm(priv, &priv->powersave_mode);
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+               /* TBD: It looks like it's transparent
+                * there's a monitor interface present -- use this
+                * to determine for example whether to calculate
+                * timestamps for packets or not, do not use instead
+                * of filter flags!
+                */
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+               struct wsm_operational_mode mode = {
+                       .power_mode = cw1200_power_mode,
+                       .disable_more_flag_usage = true,
+               };
+
+               wsm_lock_tx(priv);
+               /* Disable p2p-dev mode forced by TX request */
+               if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) &&
+                   (conf->flags & IEEE80211_CONF_IDLE) &&
+                   !priv->listening) {
+                       cw1200_disable_listening(priv);
+                       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+               }
+               wsm_set_operational_mode(priv, &mode);
+               wsm_unlock_tx(priv);
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+               pr_debug("[STA] Retry limits: %d (long), %d (short).\n",
+                        conf->long_frame_max_tx_count,
+                        conf->short_frame_max_tx_count);
+               spin_lock_bh(&priv->tx_policy_cache.lock);
+               priv->long_frame_max_tx_count = conf->long_frame_max_tx_count;
+               priv->short_frame_max_tx_count =
+                       (conf->short_frame_max_tx_count < 0x0F) ?
+                       conf->short_frame_max_tx_count : 0x0F;
+               priv->hw->max_rate_tries = priv->short_frame_max_tx_count;
+               spin_unlock_bh(&priv->tx_policy_cache.lock);
+       }
+       mutex_unlock(&priv->conf_mutex);
+       up(&priv->scan.lock);
+       return ret;
+}
+
+void cw1200_update_filtering(struct cw1200_common *priv)
+{
+       int ret;
+       bool bssid_filtering = !priv->rx_filter.bssid;
+       bool is_p2p = priv->vif && priv->vif->p2p;
+       bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type;
+
+       static struct wsm_beacon_filter_control bf_ctrl;
+       static struct wsm_mib_beacon_filter_table bf_tbl = {
+               .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC,
+               .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+                                       WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+                                       WSM_BEACON_FILTER_IE_HAS_APPEARED,
+               .entry[0].oui[0] = 0x50,
+               .entry[0].oui[1] = 0x6F,
+               .entry[0].oui[2] = 0x9A,
+               .entry[1].ie_id = WLAN_EID_HT_OPERATION,
+               .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+                                       WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+                                       WSM_BEACON_FILTER_IE_HAS_APPEARED,
+               .entry[2].ie_id = WLAN_EID_ERP_INFO,
+               .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+                                       WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+                                       WSM_BEACON_FILTER_IE_HAS_APPEARED,
+       };
+
+       if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE)
+               return;
+       else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+               bssid_filtering = false;
+
+       if (priv->disable_beacon_filter) {
+               bf_ctrl.enabled = 0;
+               bf_ctrl.bcn_count = 1;
+               bf_tbl.num = __cpu_to_le32(0);
+       } else if (is_p2p || !is_sta) {
+               bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE |
+                       WSM_BEACON_FILTER_AUTO_ERP;
+               bf_ctrl.bcn_count = 0;
+               bf_tbl.num = __cpu_to_le32(2);
+       } else {
+               bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE;
+               bf_ctrl.bcn_count = 0;
+               bf_tbl.num = __cpu_to_le32(3);
+       }
+
+       /* When acting as p2p client being connected to p2p GO, in order to
+        * receive frames from a different p2p device, turn off bssid filter.
+        *
+        * WARNING: FW dependency!
+        * This can only be used with FW WSM371 and its successors.
+        * In that FW version even with bssid filter turned off,
+        * device will block most of the unwanted frames.
+        */
+       if (is_p2p)
+               bssid_filtering = false;
+
+       ret = wsm_set_rx_filter(priv, &priv->rx_filter);
+       if (!ret)
+               ret = wsm_set_beacon_filter_table(priv, &bf_tbl);
+       if (!ret)
+               ret = wsm_beacon_filter_control(priv, &bf_ctrl);
+       if (!ret)
+               ret = wsm_set_bssid_filtering(priv, bssid_filtering);
+       if (!ret)
+               ret = wsm_set_multicast_filter(priv, &priv->multicast_filter);
+       if (ret)
+               wiphy_err(priv->hw->wiphy,
+                         "Update filtering failed: %d.\n", ret);
+       return;
+}
+
+void cw1200_update_filtering_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common,
+                            update_filtering_work);
+
+       cw1200_update_filtering(priv);
+}
+
+void cw1200_set_beacon_wakeup_period_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common,
+                            set_beacon_wakeup_period_work);
+
+       wsm_set_beacon_wakeup_period(priv,
+                                    priv->beacon_int * priv->join_dtim_period >
+                                    MAX_BEACON_SKIP_TIME_MS ? 1 :
+                                    priv->join_dtim_period, 0);
+}
+
+u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
+                            struct netdev_hw_addr_list *mc_list)
+{
+       static u8 broadcast_ipv6[ETH_ALEN] = {
+               0x33, 0x33, 0x00, 0x00, 0x00, 0x01
+       };
+       static u8 broadcast_ipv4[ETH_ALEN] = {
+               0x01, 0x00, 0x5e, 0x00, 0x00, 0x01
+       };
+       struct cw1200_common *priv = hw->priv;
+       struct netdev_hw_addr *ha;
+       int count = 0;
+
+       /* Disable multicast filtering */
+       priv->has_multicast_subscription = false;
+       memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter));
+
+       if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES)
+               return 0;
+
+       /* Enable if requested */
+       netdev_hw_addr_list_for_each(ha, mc_list) {
+               pr_debug("[STA] multicast: %pM\n", ha->addr);
+               memcpy(&priv->multicast_filter.macaddrs[count],
+                      ha->addr, ETH_ALEN);
+               if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) &&
+                   memcmp(ha->addr, broadcast_ipv6, ETH_ALEN))
+                       priv->has_multicast_subscription = true;
+               count++;
+       }
+
+       if (count) {
+               priv->multicast_filter.enable = __cpu_to_le32(1);
+               priv->multicast_filter.num_addrs = __cpu_to_le32(count);
+       }
+
+       return netdev_hw_addr_list_count(mc_list);
+}
+
+void cw1200_configure_filter(struct ieee80211_hw *dev,
+                            unsigned int changed_flags,
+                            unsigned int *total_flags,
+                            u64 multicast)
+{
+       struct cw1200_common *priv = dev->priv;
+       bool listening = !!(*total_flags &
+                           (FIF_PROMISC_IN_BSS |
+                            FIF_OTHER_BSS |
+                            FIF_BCN_PRBRESP_PROMISC |
+                            FIF_PROBE_REQ));
+
+       *total_flags &= FIF_PROMISC_IN_BSS |
+                       FIF_OTHER_BSS |
+                       FIF_FCSFAIL |
+                       FIF_BCN_PRBRESP_PROMISC |
+                       FIF_PROBE_REQ;
+
+       down(&priv->scan.lock);
+       mutex_lock(&priv->conf_mutex);
+
+       priv->rx_filter.promiscuous = (*total_flags & FIF_PROMISC_IN_BSS)
+                       ? 1 : 0;
+       priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS |
+                       FIF_PROBE_REQ)) ? 1 : 0;
+       priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0;
+       priv->disable_beacon_filter = !(*total_flags &
+                                       (FIF_BCN_PRBRESP_PROMISC |
+                                        FIF_PROMISC_IN_BSS |
+                                        FIF_PROBE_REQ));
+       if (priv->listening != listening) {
+               priv->listening = listening;
+               wsm_lock_tx(priv);
+               cw1200_update_listening(priv, listening);
+               wsm_unlock_tx(priv);
+       }
+       cw1200_update_filtering(priv);
+       mutex_unlock(&priv->conf_mutex);
+       up(&priv->scan.lock);
+}
+
+int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+                  u16 queue, const struct ieee80211_tx_queue_params *params)
+{
+       struct cw1200_common *priv = dev->priv;
+       int ret = 0;
+       /* To prevent re-applying PM request OID again and again*/
+       bool old_uapsd_flags;
+
+       mutex_lock(&priv->conf_mutex);
+
+       if (queue < dev->queues) {
+               old_uapsd_flags = le16_to_cpu(priv->uapsd_info.uapsd_flags);
+
+               WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0);
+               ret = wsm_set_tx_queue_params(priv,
+                                             &priv->tx_queue_params.params[queue], queue);
+               if (ret) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               WSM_EDCA_SET(&priv->edca, queue, params->aifs,
+                            params->cw_min, params->cw_max,
+                            params->txop, 0xc8,
+                            params->uapsd);
+               ret = wsm_set_edca_params(priv, &priv->edca);
+               if (ret) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if (priv->mode == NL80211_IFTYPE_STATION) {
+                       ret = cw1200_set_uapsd_param(priv, &priv->edca);
+                       if (!ret && priv->setbssparams_done &&
+                           (priv->join_status == CW1200_JOIN_STATUS_STA) &&
+                           (old_uapsd_flags != le16_to_cpu(priv->uapsd_info.uapsd_flags)))
+                               ret = cw1200_set_pm(priv, &priv->powersave_mode);
+               }
+       } else {
+               ret = -EINVAL;
+       }
+
+out:
+       mutex_unlock(&priv->conf_mutex);
+       return ret;
+}
+
+int cw1200_get_stats(struct ieee80211_hw *dev,
+                    struct ieee80211_low_level_stats *stats)
+{
+       struct cw1200_common *priv = dev->priv;
+
+       memcpy(stats, &priv->stats, sizeof(*stats));
+       return 0;
+}
+
+int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
+{
+       struct wsm_set_pm pm = *arg;
+
+       if (priv->uapsd_info.uapsd_flags != 0)
+               pm.mode &= ~WSM_PSM_FAST_PS_FLAG;
+
+       if (memcmp(&pm, &priv->firmware_ps_mode,
+                  sizeof(struct wsm_set_pm))) {
+               priv->firmware_ps_mode = pm;
+               return wsm_set_pm(priv, &pm);
+       } else {
+               return 0;
+       }
+}
+
+int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+                  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+                  struct ieee80211_key_conf *key)
+{
+       int ret = -EOPNOTSUPP;
+       struct cw1200_common *priv = dev->priv;
+       struct ieee80211_key_seq seq;
+
+       mutex_lock(&priv->conf_mutex);
+
+       if (cmd == SET_KEY) {
+               u8 *peer_addr = NULL;
+               int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+                       1 : 0;
+               int idx = cw1200_alloc_key(priv);
+               struct wsm_add_key *wsm_key = &priv->keys[idx];
+
+               if (idx < 0) {
+                       ret = -EINVAL;
+                       goto finally;
+               }
+
+               if (sta)
+                       peer_addr = sta->addr;
+
+               key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+
+               switch (key->cipher) {
+               case WLAN_CIPHER_SUITE_WEP40:
+               case WLAN_CIPHER_SUITE_WEP104:
+                       if (key->keylen > 16) {
+                               cw1200_free_key(priv, idx);
+                               ret = -EINVAL;
+                               goto finally;
+                       }
+
+                       if (pairwise) {
+                               wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
+                               memcpy(wsm_key->wep_pairwise.peer,
+                                      peer_addr, ETH_ALEN);
+                               memcpy(wsm_key->wep_pairwise.keydata,
+                                      &key->key[0], key->keylen);
+                               wsm_key->wep_pairwise.keylen = key->keylen;
+                       } else {
+                               wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
+                               memcpy(wsm_key->wep_group.keydata,
+                                      &key->key[0], key->keylen);
+                               wsm_key->wep_group.keylen = key->keylen;
+                               wsm_key->wep_group.keyid = key->keyidx;
+                       }
+                       break;
+               case WLAN_CIPHER_SUITE_TKIP:
+                       ieee80211_get_key_rx_seq(key, 0, &seq);
+                       if (pairwise) {
+                               wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
+                               memcpy(wsm_key->tkip_pairwise.peer,
+                                      peer_addr, ETH_ALEN);
+                               memcpy(wsm_key->tkip_pairwise.keydata,
+                                      &key->key[0], 16);
+                               memcpy(wsm_key->tkip_pairwise.tx_mic_key,
+                                      &key->key[16], 8);
+                               memcpy(wsm_key->tkip_pairwise.rx_mic_key,
+                                      &key->key[24], 8);
+                       } else {
+                               size_t mic_offset =
+                                       (priv->mode == NL80211_IFTYPE_AP) ?
+                                       16 : 24;
+                               wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
+                               memcpy(wsm_key->tkip_group.keydata,
+                                      &key->key[0], 16);
+                               memcpy(wsm_key->tkip_group.rx_mic_key,
+                                      &key->key[mic_offset], 8);
+
+                               wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff;
+                               wsm_key->tkip_group.rx_seqnum[6] = 0;
+                               wsm_key->tkip_group.rx_seqnum[7] = 0;
+
+                               wsm_key->tkip_group.keyid = key->keyidx;
+                       }
+                       break;
+               case WLAN_CIPHER_SUITE_CCMP:
+                       ieee80211_get_key_rx_seq(key, 0, &seq);
+                       if (pairwise) {
+                               wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
+                               memcpy(wsm_key->aes_pairwise.peer,
+                                      peer_addr, ETH_ALEN);
+                               memcpy(wsm_key->aes_pairwise.keydata,
+                                      &key->key[0], 16);
+                       } else {
+                               wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
+                               memcpy(wsm_key->aes_group.keydata,
+                                      &key->key[0], 16);
+
+                               wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5];
+                               wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4];
+                               wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3];
+                               wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2];
+                               wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1];
+                               wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0];
+                               wsm_key->aes_group.rx_seqnum[6] = 0;
+                               wsm_key->aes_group.rx_seqnum[7] = 0;
+                               wsm_key->aes_group.keyid = key->keyidx;
+                       }
+                       break;
+               case WLAN_CIPHER_SUITE_SMS4:
+                       if (pairwise) {
+                               wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
+                               memcpy(wsm_key->wapi_pairwise.peer,
+                                      peer_addr, ETH_ALEN);
+                               memcpy(wsm_key->wapi_pairwise.keydata,
+                                      &key->key[0], 16);
+                               memcpy(wsm_key->wapi_pairwise.mic_key,
+                                      &key->key[16], 16);
+                               wsm_key->wapi_pairwise.keyid = key->keyidx;
+                       } else {
+                               wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
+                               memcpy(wsm_key->wapi_group.keydata,
+                                      &key->key[0],  16);
+                               memcpy(wsm_key->wapi_group.mic_key,
+                                      &key->key[16], 16);
+                               wsm_key->wapi_group.keyid = key->keyidx;
+                       }
+                       break;
+               default:
+                       pr_warn("Unhandled key type %d\n", key->cipher);
+                       cw1200_free_key(priv, idx);
+                       ret = -EOPNOTSUPP;
+                       goto finally;
+               }
+               ret = wsm_add_key(priv, wsm_key);
+               if (!ret)
+                       key->hw_key_idx = idx;
+               else
+                       cw1200_free_key(priv, idx);
+       } else if (cmd == DISABLE_KEY) {
+               struct wsm_remove_key wsm_key = {
+                       .index = key->hw_key_idx,
+               };
+
+               if (wsm_key.index > WSM_KEY_MAX_INDEX) {
+                       ret = -EINVAL;
+                       goto finally;
+               }
+
+               cw1200_free_key(priv, wsm_key.index);
+               ret = wsm_remove_key(priv, &wsm_key);
+       } else {
+               pr_warn("Unhandled key command %d\n", cmd);
+       }
+
+finally:
+       mutex_unlock(&priv->conf_mutex);
+       return ret;
+}
+
+void cw1200_wep_key_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, wep_key_work);
+       u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
+       struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+       __le32 wep_default_key_id = __cpu_to_le32(
+               priv->wep_default_key_id);
+
+       pr_debug("[STA] Setting default WEP key: %d\n",
+                priv->wep_default_key_id);
+       wsm_flush_tx(priv);
+       wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
+                     &wep_default_key_id, sizeof(wep_default_key_id));
+       cw1200_queue_requeue(queue, priv->pending_frame_id);
+       wsm_unlock_tx(priv);
+}
+
+int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       int ret = 0;
+       __le32 val32;
+       struct cw1200_common *priv = hw->priv;
+
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+               return 0;
+
+       if (value != (u32) -1)
+               val32 = __cpu_to_le32(value);
+       else
+               val32 = 0; /* disabled */
+
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+               /* device is down, can _not_ set threshold */
+               ret = -ENODEV;
+               goto out;
+       }
+
+       if (priv->rts_threshold == value)
+               goto out;
+
+       pr_debug("[STA] Setting RTS threshold: %d\n",
+                priv->rts_threshold);
+
+       /* mutex_lock(&priv->conf_mutex); */
+       ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
+                           &val32, sizeof(val32));
+       if (!ret)
+               priv->rts_threshold = value;
+       /* mutex_unlock(&priv->conf_mutex); */
+
+out:
+       return ret;
+}
+
+/* If successful, LOCKS the TX queue! */
+static int __cw1200_flush(struct cw1200_common *priv, bool drop)
+{
+       int i, ret;
+
+       for (;;) {
+               /* TODO: correct flush handling is required when dev_stop.
+                * Temporary workaround: 2s
+                */
+               if (drop) {
+                       for (i = 0; i < 4; ++i)
+                               cw1200_queue_clear(&priv->tx_queue[i]);
+               } else {
+                       ret = wait_event_timeout(
+                               priv->tx_queue_stats.wait_link_id_empty,
+                               cw1200_queue_stats_is_empty(
+                                       &priv->tx_queue_stats, -1),
+                               2 * HZ);
+               }
+
+               if (!drop && ret <= 0) {
+                       ret = -ETIMEDOUT;
+                       break;
+               } else {
+                       ret = 0;
+               }
+
+               wsm_lock_tx(priv);
+               if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) {
+                       /* Highly unlikely: WSM requeued frames. */
+                       wsm_unlock_tx(priv);
+                       continue;
+               }
+               break;
+       }
+       return ret;
+}
+
+void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+{
+       struct cw1200_common *priv = hw->priv;
+
+       switch (priv->mode) {
+       case NL80211_IFTYPE_MONITOR:
+               drop = true;
+               break;
+       case NL80211_IFTYPE_AP:
+               if (!priv->enable_beacon)
+                       drop = true;
+               break;
+       }
+
+       if (!__cw1200_flush(priv, drop))
+               wsm_unlock_tx(priv);
+
+       return;
+}
+
+/* ******************************************************************** */
+/* WSM callbacks                                                       */
+
+void cw1200_free_event_queue(struct cw1200_common *priv)
+{
+       LIST_HEAD(list);
+
+       spin_lock(&priv->event_queue_lock);
+       list_splice_init(&priv->event_queue, &list);
+       spin_unlock(&priv->event_queue_lock);
+
+       __cw1200_free_event_queue(&list);
+}
+
+void cw1200_event_handler(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, event_handler);
+       struct cw1200_wsm_event *event;
+       LIST_HEAD(list);
+
+       spin_lock(&priv->event_queue_lock);
+       list_splice_init(&priv->event_queue, &list);
+       spin_unlock(&priv->event_queue_lock);
+
+       list_for_each_entry(event, &list, link) {
+               switch (event->evt.id) {
+               case WSM_EVENT_ERROR:
+                       pr_err("Unhandled WSM Error from LMAC\n");
+                       break;
+               case WSM_EVENT_BSS_LOST:
+                       pr_debug("[CQM] BSS lost.\n");
+                       cancel_work_sync(&priv->unjoin_work);
+                       if (!down_trylock(&priv->scan.lock)) {
+                               cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
+                               up(&priv->scan.lock);
+                       } else {
+                               /* Scan is in progress. Delay reporting.
+                                * Scan complete will trigger bss_loss_work
+                                */
+                               priv->delayed_link_loss = 1;
+                               /* Also start a watchdog. */
+                               queue_delayed_work(priv->workqueue,
+                                                  &priv->bss_loss_work, 5*HZ);
+                       }
+                       break;
+               case WSM_EVENT_BSS_REGAINED:
+                       pr_debug("[CQM] BSS regained.\n");
+                       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+                       cancel_work_sync(&priv->unjoin_work);
+                       break;
+               case WSM_EVENT_RADAR_DETECTED:
+                       wiphy_info(priv->hw->wiphy, "radar pulse detected\n");
+                       break;
+               case WSM_EVENT_RCPI_RSSI:
+               {
+                       /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+                        * RSSI = RCPI / 2 - 110
+                        */
+                       int rcpi_rssi = (int)(event->evt.data & 0xFF);
+                       int cqm_evt;
+                       if (priv->cqm_use_rssi)
+                               rcpi_rssi = (s8)rcpi_rssi;
+                       else
+                               rcpi_rssi =  rcpi_rssi / 2 - 110;
+
+                       cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ?
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+                       pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi);
+                       ieee80211_cqm_rssi_notify(priv->vif, cqm_evt,
+                                                 GFP_KERNEL);
+                       break;
+               }
+               case WSM_EVENT_BT_INACTIVE:
+                       pr_warn("Unhandled BT INACTIVE from LMAC\n");
+                       break;
+               case WSM_EVENT_BT_ACTIVE:
+                       pr_warn("Unhandled BT ACTIVE from LMAC\n");
+                       break;
+               }
+       }
+       __cw1200_free_event_queue(&list);
+}
+
+void cw1200_bss_loss_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, bss_loss_work.work);
+
+       pr_debug("[CQM] Reporting connection loss.\n");
+       wsm_lock_tx(priv);
+       if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+               wsm_unlock_tx(priv);
+}
+
+void cw1200_bss_params_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, bss_params_work);
+       mutex_lock(&priv->conf_mutex);
+
+       priv->bss_params.reset_beacon_loss = 1;
+       wsm_set_bss_params(priv, &priv->bss_params);
+       priv->bss_params.reset_beacon_loss = 0;
+
+       mutex_unlock(&priv->conf_mutex);
+}
+
+/* ******************************************************************** */
+/* Internal API                                                                */
+
+/* This function is called to Parse the SDD file
+ * to extract listen_interval and PTA related information
+ * sdd is a TLV: u8 id, u8 len, u8 data[]
+ */
+static int cw1200_parse_sdd_file(struct cw1200_common *priv)
+{
+       const u8 *p = priv->sdd->data;
+       int ret = 0;
+
+       while (p + 2 <= priv->sdd->data + priv->sdd->size) {
+               if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) {
+                       pr_warn("Malformed sdd structure\n");
+                       return -1;
+               }
+               switch (p[0]) {
+               case SDD_PTA_CFG_ELT_ID: {
+                       u16 v;
+                       if (p[1] < 4) {
+                               pr_warn("SDD_PTA_CFG_ELT_ID malformed\n");
+                               ret = -1;
+                               break;
+                       }
+                       v = le16_to_cpu(*((__le16 *)(p + 2)));
+                       if (!v)  /* non-zero means this is enabled */
+                               break;
+
+                       v = le16_to_cpu(*((__le16 *)(p + 4)));
+                       priv->conf_listen_interval = (v >> 7) & 0x1F;
+                       pr_debug("PTA found; Listen Interval %d\n",
+                                priv->conf_listen_interval);
+                       break;
+               }
+               case SDD_REFERENCE_FREQUENCY_ELT_ID: {
+                       u16 clk = le16_to_cpu(*((__le16 *)(p + 2)));
+                       if (clk != priv->hw_refclk)
+                               pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n",
+                                       clk, priv->hw_refclk);
+                       break;
+               }
+               default:
+                       break;
+               }
+               p += p[1] + 2;
+       }
+
+       if (!priv->bt_present) {
+               pr_debug("PTA element NOT found.\n");
+               priv->conf_listen_interval = 0;
+       }
+       return ret;
+}
+
+int cw1200_setup_mac(struct cw1200_common *priv)
+{
+       int ret = 0;
+
+       /* NOTE: There is a bug in FW: it reports signal
+        * as RSSI if RSSI subscription is enabled.
+        * It's not enough to set WSM_RCPI_RSSI_USE_RSSI.
+        *
+        * NOTE2: RSSI based reports have been switched to RCPI, since
+        * FW has a bug and RSSI reported values are not stable,
+        * what can leads to signal level oscilations in user-end applications
+        */
+       struct wsm_rcpi_rssi_threshold threshold = {
+               .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+               WSM_RCPI_RSSI_DONT_USE_UPPER |
+               WSM_RCPI_RSSI_DONT_USE_LOWER,
+               .rollingAverageCount = 16,
+       };
+
+       struct wsm_configuration cfg = {
+               .dot11StationId = &priv->mac_addr[0],
+       };
+
+       /* Remember the decission here to make sure, we will handle
+        * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS
+        */
+       if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI)
+               priv->cqm_use_rssi = true;
+
+       if (!priv->sdd) {
+               ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev);
+               if (ret) {
+                       pr_err("Can't load sdd file %s.\n", priv->sdd_path);
+                       return ret;
+               }
+               cw1200_parse_sdd_file(priv);
+       }
+
+       cfg.dpdData = priv->sdd->data;
+       cfg.dpdData_size = priv->sdd->size;
+       ret = wsm_configuration(priv, &cfg);
+       if (ret)
+               return ret;
+
+       /* Configure RSSI/SCPI reporting as RSSI. */
+       wsm_set_rcpi_rssi_threshold(priv, &threshold);
+
+       return 0;
+}
+
+static void cw1200_join_complete(struct cw1200_common *priv)
+{
+       pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status);
+
+       priv->join_pending = false;
+       if (priv->join_complete_status) {
+               priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+               cw1200_update_listening(priv, priv->listening);
+               cw1200_do_unjoin(priv);
+               ieee80211_connection_loss(priv->vif);
+       } else {
+               if (priv->mode == NL80211_IFTYPE_ADHOC)
+                       priv->join_status = CW1200_JOIN_STATUS_IBSS;
+               else
+                       priv->join_status = CW1200_JOIN_STATUS_PRE_STA;
+       }
+       wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */
+}
+
+void cw1200_join_complete_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, join_complete_work);
+       mutex_lock(&priv->conf_mutex);
+       cw1200_join_complete(priv);
+       mutex_unlock(&priv->conf_mutex);
+}
+
+void cw1200_join_complete_cb(struct cw1200_common *priv,
+                            struct wsm_join_complete *arg)
+{
+       pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n",
+                arg->status);
+
+       if (cancel_delayed_work(&priv->join_timeout)) {
+               priv->join_complete_status = arg->status;
+               queue_work(priv->workqueue, &priv->join_complete_work);
+       }
+}
+
+/* MUST be called with tx_lock held!  It will be unlocked for us. */
+static void cw1200_do_join(struct cw1200_common *priv)
+{
+       const u8 *bssid;
+       struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+       struct cfg80211_bss *bss = NULL;
+       struct wsm_protected_mgmt_policy mgmt_policy;
+       struct wsm_join join = {
+               .mode = conf->ibss_joined ?
+                               WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
+               .preamble_type = WSM_JOIN_PREAMBLE_LONG,
+               .probe_for_join = 1,
+               .atim_window = 0,
+               .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
+                                                         conf->basic_rates),
+       };
+       if (delayed_work_pending(&priv->join_timeout)) {
+               pr_warn("[STA] - Join request already pending, skipping..\n");
+               wsm_unlock_tx(priv);
+               return;
+       }
+
+       if (priv->join_status)
+               cw1200_do_unjoin(priv);
+
+       bssid = priv->vif->bss_conf.bssid;
+
+       bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel,
+                       bssid, NULL, 0, 0, 0);
+
+       if (!bss && !conf->ibss_joined) {
+               wsm_unlock_tx(priv);
+               return;
+       }
+
+       mutex_lock(&priv->conf_mutex);
+
+       /* Under the conf lock: check scan status and
+        * bail out if it is in progress.
+        */
+       if (atomic_read(&priv->scan.in_progress)) {
+               wsm_unlock_tx(priv);
+               goto done_put;
+       }
+
+       priv->join_pending = true;
+
+       /* Sanity check basic rates */
+       if (!join.basic_rate_set)
+               join.basic_rate_set = 7;
+
+       /* Sanity check beacon interval */
+       if (!priv->beacon_int)
+               priv->beacon_int = 1;
+
+       join.beacon_interval = priv->beacon_int;
+
+       /* BT Coex related changes */
+       if (priv->bt_present) {
+               if (((priv->conf_listen_interval * 100) %
+                    priv->beacon_int) == 0)
+                       priv->listen_interval =
+                               ((priv->conf_listen_interval * 100) /
+                                priv->beacon_int);
+               else
+                       priv->listen_interval =
+                               ((priv->conf_listen_interval * 100) /
+                                priv->beacon_int + 1);
+       }
+
+       if (priv->hw->conf.ps_dtim_period)
+               priv->join_dtim_period = priv->hw->conf.ps_dtim_period;
+       join.dtim_period = priv->join_dtim_period;
+
+       join.channel_number = priv->channel->hw_value;
+       join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+               WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+
+       memcpy(join.bssid, bssid, sizeof(join.bssid));
+
+       pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n",
+                join.bssid,
+                join.dtim_period, priv->beacon_int);
+
+       if (!conf->ibss_joined) {
+               const u8 *ssidie;
+               rcu_read_lock();
+               ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
+               if (ssidie) {
+                       join.ssid_len = ssidie[1];
+                       memcpy(join.ssid, &ssidie[2], join.ssid_len);
+               }
+               rcu_read_unlock();
+       }
+
+       if (priv->vif->p2p) {
+               join.flags |= WSM_JOIN_FLAGS_P2P_GO;
+               join.basic_rate_set =
+                       cw1200_rate_mask_to_wsm(priv, 0xFF0);
+       }
+
+       /* Enable asynchronous join calls */
+       if (!conf->ibss_joined) {
+               join.flags |= WSM_JOIN_FLAGS_FORCE;
+               join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND;
+       }
+
+       wsm_flush_tx(priv);
+
+       /* Stay Awake for Join and Auth Timeouts and a bit more */
+       cw1200_pm_stay_awake(&priv->pm_state,
+                            CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT);
+
+       cw1200_update_listening(priv, false);
+
+       /* Turn on Block ACKs */
+       wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask,
+                                priv->ba_rx_tid_mask);
+
+       /* Set up timeout */
+       if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) {
+               priv->join_status = CW1200_JOIN_STATUS_JOINING;
+               queue_delayed_work(priv->workqueue,
+                                  &priv->join_timeout,
+                                  CW1200_JOIN_TIMEOUT);
+       }
+
+       /* 802.11w protected mgmt frames */
+       mgmt_policy.protectedMgmtEnable = 0;
+       mgmt_policy.unprotectedMgmtFramesAllowed = 1;
+       mgmt_policy.encryptionForAuthFrame = 1;
+       wsm_set_protected_mgmt_policy(priv, &mgmt_policy);
+
+       /* Perform actual join */
+       if (wsm_join(priv, &join)) {
+               pr_err("[STA] cw1200_join_work: wsm_join failed!\n");
+               cancel_delayed_work_sync(&priv->join_timeout);
+               cw1200_update_listening(priv, priv->listening);
+               /* Tx lock still held, unjoin will clear it. */
+               if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+                       wsm_unlock_tx(priv);
+       } else {
+               if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND))
+                       cw1200_join_complete(priv); /* Will clear tx_lock */
+
+               /* Upload keys */
+               cw1200_upload_keys(priv);
+
+               /* Due to beacon filtering it is possible that the
+                * AP's beacon is not known for the mac80211 stack.
+                * Disable filtering temporary to make sure the stack
+                * receives at least one
+                */
+               priv->disable_beacon_filter = true;
+       }
+       cw1200_update_filtering(priv);
+
+done_put:
+       mutex_unlock(&priv->conf_mutex);
+       if (bss)
+               cfg80211_put_bss(priv->hw->wiphy, bss);
+}
+
+void cw1200_join_timeout(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, join_timeout.work);
+       pr_debug("[WSM] Join timed out.\n");
+       wsm_lock_tx(priv);
+       if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+               wsm_unlock_tx(priv);
+}
+
+static void cw1200_do_unjoin(struct cw1200_common *priv)
+{
+       struct wsm_reset reset = {
+               .reset_statistics = true,
+       };
+
+       cancel_delayed_work_sync(&priv->join_timeout);
+
+       mutex_lock(&priv->conf_mutex);
+       priv->join_pending = false;
+
+       if (atomic_read(&priv->scan.in_progress)) {
+               if (priv->delayed_unjoin)
+                       wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n");
+               else
+                       priv->delayed_unjoin = true;
+               goto done;
+       }
+
+       priv->delayed_link_loss = false;
+
+       if (!priv->join_status)
+               goto done;
+
+       if (priv->join_status > CW1200_JOIN_STATUS_IBSS) {
+               wiphy_err(priv->hw->wiphy, "Unexpected: join status: %d\n",
+                         priv->join_status);
+               BUG_ON(1);
+       }
+
+       cancel_work_sync(&priv->update_filtering_work);
+       cancel_work_sync(&priv->set_beacon_wakeup_period_work);
+       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+
+       /* Unjoin is a reset. */
+       wsm_flush_tx(priv);
+       wsm_keep_alive_period(priv, 0);
+       wsm_reset(priv, &reset);
+       wsm_set_output_power(priv, priv->output_power * 10);
+       priv->join_dtim_period = 0;
+       cw1200_setup_mac(priv);
+       cw1200_free_event_queue(priv);
+       cancel_work_sync(&priv->event_handler);
+       cw1200_update_listening(priv, priv->listening);
+       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+
+       /* Disable Block ACKs */
+       wsm_set_block_ack_policy(priv, 0, 0);
+
+       priv->disable_beacon_filter = false;
+       cw1200_update_filtering(priv);
+       memset(&priv->association_mode, 0,
+              sizeof(priv->association_mode));
+       memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+       priv->setbssparams_done = false;
+       memset(&priv->firmware_ps_mode, 0,
+              sizeof(priv->firmware_ps_mode));
+
+       pr_debug("[STA] Unjoin completed.\n");
+
+done:
+       mutex_unlock(&priv->conf_mutex);
+}
+
+void cw1200_unjoin_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, unjoin_work);
+
+       cw1200_do_unjoin(priv);
+
+       /* Tell the stack we're dead */
+       ieee80211_connection_loss(priv->vif);
+
+       wsm_unlock_tx(priv);
+}
+
+int cw1200_enable_listening(struct cw1200_common *priv)
+{
+       struct wsm_start start = {
+               .mode = WSM_START_MODE_P2P_DEV,
+               .band = WSM_PHY_BAND_2_4G,
+               .beacon_interval = 100,
+               .dtim_period = 1,
+               .probe_delay = 0,
+               .basic_rate_set = 0x0F,
+       };
+
+       if (priv->channel) {
+               start.band = priv->channel->band == IEEE80211_BAND_5GHZ ?
+                            WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+               start.channel_number = priv->channel->hw_value;
+       } else {
+               start.band = WSM_PHY_BAND_2_4G;
+               start.channel_number = 1;
+       }
+
+       return wsm_start(priv, &start);
+}
+
+int cw1200_disable_listening(struct cw1200_common *priv)
+{
+       int ret;
+       struct wsm_reset reset = {
+               .reset_statistics = true,
+       };
+       ret = wsm_reset(priv, &reset);
+       return ret;
+}
+
+void cw1200_update_listening(struct cw1200_common *priv, bool enabled)
+{
+       if (enabled) {
+               if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) {
+                       if (!cw1200_enable_listening(priv))
+                               priv->join_status = CW1200_JOIN_STATUS_MONITOR;
+                       wsm_set_probe_responder(priv, true);
+               }
+       } else {
+               if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+                       if (!cw1200_disable_listening(priv))
+                               priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+                       wsm_set_probe_responder(priv, false);
+               }
+       }
+}
+
+int cw1200_set_uapsd_param(struct cw1200_common *priv,
+                          const struct wsm_edca_params *arg)
+{
+       int ret;
+       u16 uapsd_flags = 0;
+
+       /* Here's the mapping AC [queue, bit]
+        *  VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]
+        */
+
+       if (arg->uapsd_enable[0])
+               uapsd_flags |= 1 << 3;
+
+       if (arg->uapsd_enable[1])
+               uapsd_flags |= 1 << 2;
+
+       if (arg->uapsd_enable[2])
+               uapsd_flags |= 1 << 1;
+
+       if (arg->uapsd_enable[3])
+               uapsd_flags |= 1;
+
+       /* Currently pseudo U-APSD operation is not supported, so setting
+        * MinAutoTriggerInterval, MaxAutoTriggerInterval and
+        * AutoTriggerStep to 0
+        */
+
+       priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags);
+       priv->uapsd_info.min_auto_trigger_interval = 0;
+       priv->uapsd_info.max_auto_trigger_interval = 0;
+       priv->uapsd_info.auto_trigger_step = 0;
+
+       ret = wsm_set_uapsd_info(priv, &priv->uapsd_info);
+       return ret;
+}
+
+/* ******************************************************************** */
+/* AP API                                                              */
+
+int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta)
+{
+       struct cw1200_common *priv = hw->priv;
+       struct cw1200_sta_priv *sta_priv =
+                       (struct cw1200_sta_priv *)&sta->drv_priv;
+       struct cw1200_link_entry *entry;
+       struct sk_buff *skb;
+
+       if (priv->mode != NL80211_IFTYPE_AP)
+               return 0;
+
+       sta_priv->link_id = cw1200_find_link_id(priv, sta->addr);
+       if (WARN_ON(!sta_priv->link_id)) {
+               wiphy_info(priv->hw->wiphy,
+                          "[AP] No more link IDs available.\n");
+               return -ENOENT;
+       }
+
+       entry = &priv->link_id_db[sta_priv->link_id - 1];
+       spin_lock_bh(&priv->ps_state_lock);
+       if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) ==
+                                       IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
+               priv->sta_asleep_mask |= BIT(sta_priv->link_id);
+       entry->status = CW1200_LINK_HARD;
+       while ((skb = skb_dequeue(&entry->rx_queue)))
+               ieee80211_rx_irqsafe(priv->hw, skb);
+       spin_unlock_bh(&priv->ps_state_lock);
+       return 0;
+}
+
+int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                     struct ieee80211_sta *sta)
+{
+       struct cw1200_common *priv = hw->priv;
+       struct cw1200_sta_priv *sta_priv =
+                       (struct cw1200_sta_priv *)&sta->drv_priv;
+       struct cw1200_link_entry *entry;
+
+       if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id)
+               return 0;
+
+       entry = &priv->link_id_db[sta_priv->link_id - 1];
+       spin_lock_bh(&priv->ps_state_lock);
+       entry->status = CW1200_LINK_RESERVE;
+       entry->timestamp = jiffies;
+       wsm_lock_tx_async(priv);
+       if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+               wsm_unlock_tx(priv);
+       spin_unlock_bh(&priv->ps_state_lock);
+       flush_workqueue(priv->workqueue);
+       return 0;
+}
+
+static void __cw1200_sta_notify(struct ieee80211_hw *dev,
+                               struct ieee80211_vif *vif,
+                               enum sta_notify_cmd notify_cmd,
+                               int link_id)
+{
+       struct cw1200_common *priv = dev->priv;
+       u32 bit, prev;
+
+       /* Zero link id means "for all link IDs" */
+       if (link_id)
+               bit = BIT(link_id);
+       else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
+               bit = 0;
+       else
+               bit = priv->link_id_map;
+       prev = priv->sta_asleep_mask & bit;
+
+       switch (notify_cmd) {
+       case STA_NOTIFY_SLEEP:
+               if (!prev) {
+                       if (priv->buffered_multicasts &&
+                           !priv->sta_asleep_mask)
+                               queue_work(priv->workqueue,
+                                          &priv->multicast_start_work);
+                       priv->sta_asleep_mask |= bit;
+               }
+               break;
+       case STA_NOTIFY_AWAKE:
+               if (prev) {
+                       priv->sta_asleep_mask &= ~bit;
+                       priv->pspoll_mask &= ~bit;
+                       if (priv->tx_multicast && link_id &&
+                           !priv->sta_asleep_mask)
+                               queue_work(priv->workqueue,
+                                          &priv->multicast_stop_work);
+                       cw1200_bh_wakeup(priv);
+               }
+               break;
+       }
+}
+
+void cw1200_sta_notify(struct ieee80211_hw *dev,
+                      struct ieee80211_vif *vif,
+                      enum sta_notify_cmd notify_cmd,
+                      struct ieee80211_sta *sta)
+{
+       struct cw1200_common *priv = dev->priv;
+       struct cw1200_sta_priv *sta_priv =
+               (struct cw1200_sta_priv *)&sta->drv_priv;
+
+       spin_lock_bh(&priv->ps_state_lock);
+       __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id);
+       spin_unlock_bh(&priv->ps_state_lock);
+}
+
+static void cw1200_ps_notify(struct cw1200_common *priv,
+                     int link_id, bool ps)
+{
+       if (link_id > CW1200_MAX_STA_IN_AP_MODE)
+               return;
+
+       pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n",
+                ps ? "Stop" : "Start",
+                link_id, priv->sta_asleep_mask);
+
+       __cw1200_sta_notify(priv->hw, priv->vif,
+                           ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
+}
+
+static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set)
+{
+       struct sk_buff *skb;
+       struct wsm_update_ie update_ie = {
+               .what = WSM_UPDATE_IE_BEACON,
+               .count = 1,
+       };
+       u16 tim_offset, tim_length;
+
+       pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis");
+
+       skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+                       &tim_offset, &tim_length);
+       if (!skb) {
+               if (!__cw1200_flush(priv, true))
+                       wsm_unlock_tx(priv);
+               return -ENOENT;
+       }
+
+       if (tim_offset && tim_length >= 6) {
+               /* Ignore DTIM count from mac80211:
+                * firmware handles DTIM internally.
+                */
+               skb->data[tim_offset + 2] = 0;
+
+               /* Set/reset aid0 bit */
+               if (aid0_bit_set)
+                       skb->data[tim_offset + 4] |= 1;
+               else
+                       skb->data[tim_offset + 4] &= ~1;
+       }
+
+       update_ie.ies = &skb->data[tim_offset];
+       update_ie.length = tim_length;
+       wsm_update_ie(priv, &update_ie);
+
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+void cw1200_set_tim_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, set_tim_work);
+       (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set);
+}
+
+int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+                  bool set)
+{
+       struct cw1200_common *priv = dev->priv;
+       queue_work(priv->workqueue, &priv->set_tim_work);
+       return 0;
+}
+
+void cw1200_set_cts_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, set_cts_work);
+
+       u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0};
+       struct wsm_update_ie update_ie = {
+               .what = WSM_UPDATE_IE_BEACON,
+               .count = 1,
+               .ies = erp_ie,
+               .length = 3,
+       };
+       u32 erp_info;
+       __le32 use_cts_prot;
+       mutex_lock(&priv->conf_mutex);
+       erp_info = priv->erp_info;
+       mutex_unlock(&priv->conf_mutex);
+       use_cts_prot =
+               erp_info & WLAN_ERP_USE_PROTECTION ?
+               __cpu_to_le32(1) : 0;
+
+       erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
+
+       pr_debug("[STA] ERP information 0x%x\n", erp_info);
+
+       wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION,
+                     &use_cts_prot, sizeof(use_cts_prot));
+       wsm_update_ie(priv, &update_ie);
+
+       return;
+}
+
+static int cw1200_set_btcoexinfo(struct cw1200_common *priv)
+{
+       struct wsm_override_internal_txrate arg;
+       int ret = 0;
+
+       if (priv->mode == NL80211_IFTYPE_STATION) {
+               /* Plumb PSPOLL and NULL template */
+               cw1200_upload_pspoll(priv);
+               cw1200_upload_null(priv);
+               cw1200_upload_qosnull(priv);
+       } else {
+               return 0;
+       }
+
+       memset(&arg, 0, sizeof(struct wsm_override_internal_txrate));
+
+       if (!priv->vif->p2p) {
+               /* STATION mode */
+               if (priv->bss_params.operational_rate_set & ~0xF) {
+                       pr_debug("[STA] STA has ERP rates\n");
+                       /* G or BG mode */
+                       arg.internalTxRate = (__ffs(
+                       priv->bss_params.operational_rate_set & ~0xF));
+               } else {
+                       pr_debug("[STA] STA has non ERP rates\n");
+                       /* B only mode */
+                       arg.internalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
+               }
+               arg.nonErpInternalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
+       } else {
+               /* P2P mode */
+               arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
+               arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
+       }
+
+       pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n",
+                priv->mode,
+                arg.internalTxRate,
+                arg.nonErpInternalTxRate);
+
+       ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+                           &arg, sizeof(arg));
+
+       return ret;
+}
+
+void cw1200_bss_info_changed(struct ieee80211_hw *dev,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_bss_conf *info,
+                            u32 changed)
+{
+       struct cw1200_common *priv = dev->priv;
+       bool do_join = false;
+
+       mutex_lock(&priv->conf_mutex);
+
+       pr_debug("BSS CHANGED:  %08x\n", changed);
+
+       /* TODO: BSS_CHANGED_QOS */
+       /* TODO: BSS_CHANGED_TXPOWER */
+
+       if (changed & BSS_CHANGED_ARP_FILTER) {
+               struct wsm_mib_arp_ipv4_filter filter = {0};
+               int i;
+
+               pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n",
+                        info->arp_addr_cnt);
+
+               /* Currently only one IP address is supported by firmware.
+                * In case of more IPs arp filtering will be disabled.
+                */
+               if (info->arp_addr_cnt > 0 &&
+                   info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) {
+                       for (i = 0; i < info->arp_addr_cnt; i++) {
+                               filter.ipv4addrs[i] = info->arp_addr_list[i];
+                               pr_debug("[STA] addr[%d]: 0x%X\n",
+                                        i, filter.ipv4addrs[i]);
+                       }
+                       filter.enable = __cpu_to_le32(1);
+               }
+
+               pr_debug("[STA] arp ip filter enable: %d\n",
+                        __le32_to_cpu(filter.enable));
+
+               wsm_set_arp_ipv4_filter(priv, &filter);
+       }
+
+       if (changed &
+           (BSS_CHANGED_BEACON |
+            BSS_CHANGED_AP_PROBE_RESP |
+            BSS_CHANGED_BSSID |
+            BSS_CHANGED_SSID |
+            BSS_CHANGED_IBSS)) {
+               pr_debug("BSS_CHANGED_BEACON\n");
+               priv->beacon_int = info->beacon_int;
+               cw1200_update_beaconing(priv);
+               cw1200_upload_beacon(priv);
+       }
+
+       if (changed & BSS_CHANGED_BEACON_ENABLED) {
+               pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon);
+
+               if (priv->enable_beacon != info->enable_beacon) {
+                       cw1200_enable_beaconing(priv, info->enable_beacon);
+                       priv->enable_beacon = info->enable_beacon;
+               }
+       }
+
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               pr_debug("CHANGED_BEACON_INT\n");
+               if (info->ibss_joined)
+                       do_join = true;
+               else if (priv->join_status == CW1200_JOIN_STATUS_AP)
+                       cw1200_update_beaconing(priv);
+       }
+
+       /* assoc/disassoc, or maybe AID changed */
+       if (changed & BSS_CHANGED_ASSOC) {
+               wsm_lock_tx(priv);
+               priv->wep_default_key_id = -1;
+               wsm_unlock_tx(priv);
+       }
+
+       if (changed & BSS_CHANGED_BSSID) {
+               pr_debug("BSS_CHANGED_BSSID\n");
+               do_join = true;
+       }
+
+       if (changed &
+           (BSS_CHANGED_ASSOC |
+            BSS_CHANGED_BSSID |
+            BSS_CHANGED_IBSS |
+            BSS_CHANGED_BASIC_RATES |
+            BSS_CHANGED_HT)) {
+               pr_debug("BSS_CHANGED_ASSOC\n");
+               if (info->assoc) {
+                       if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) {
+                               ieee80211_connection_loss(vif);
+                               mutex_unlock(&priv->conf_mutex);
+                               return;
+                       } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) {
+                               priv->join_status = CW1200_JOIN_STATUS_STA;
+                       }
+               } else {
+                       do_join = true;
+               }
+
+               if (info->assoc || info->ibss_joined) {
+                       struct ieee80211_sta *sta = NULL;
+                       __le32 htprot = 0;
+
+                       if (info->dtim_period)
+                               priv->join_dtim_period = info->dtim_period;
+                       priv->beacon_int = info->beacon_int;
+
+                       rcu_read_lock();
+
+                       if (info->bssid && !info->ibss_joined)
+                               sta = ieee80211_find_sta(vif, info->bssid);
+                       if (sta) {
+                               priv->ht_info.ht_cap = sta->ht_cap;
+                               priv->bss_params.operational_rate_set =
+                                       cw1200_rate_mask_to_wsm(priv,
+                                                               sta->supp_rates[priv->channel->band]);
+                               priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef);
+                               priv->ht_info.operation_mode = info->ht_operation_mode;
+                       } else {
+                               memset(&priv->ht_info, 0,
+                                      sizeof(priv->ht_info));
+                               priv->bss_params.operational_rate_set = -1;
+                       }
+                       rcu_read_unlock();
+
+                       /* Non Greenfield stations present */
+                       if (priv->ht_info.operation_mode &
+                           IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
+                               htprot |= cpu_to_le32(WSM_NON_GREENFIELD_STA_PRESENT);
+
+                       /* Set HT protection method */
+                       htprot |= cpu_to_le32((priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2);
+
+                       /* TODO:
+                        * STBC_param.dual_cts
+                        *  STBC_param.LSIG_TXOP_FILL
+                        */
+
+                       wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION,
+                                     &htprot, sizeof(htprot));
+
+                       priv->association_mode.greenfield =
+                               cw1200_ht_greenfield(&priv->ht_info);
+                       priv->association_mode.flags =
+                               WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES |
+                               WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE |
+                               WSM_ASSOCIATION_MODE_USE_HT_MODE |
+                               WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET |
+                               WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING;
+                       priv->association_mode.preamble =
+                               info->use_short_preamble ?
+                               WSM_JOIN_PREAMBLE_SHORT :
+                               WSM_JOIN_PREAMBLE_LONG;
+                       priv->association_mode.basic_rate_set = __cpu_to_le32(
+                               cw1200_rate_mask_to_wsm(priv,
+                                                       info->basic_rates));
+                       priv->association_mode.mpdu_start_spacing =
+                               cw1200_ht_ampdu_density(&priv->ht_info);
+
+                       cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+                       cancel_work_sync(&priv->unjoin_work);
+
+                       priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count;
+                       priv->bss_params.aid = info->aid;
+
+                       if (priv->join_dtim_period < 1)
+                               priv->join_dtim_period = 1;
+
+                       pr_debug("[STA] DTIM %d, interval: %d\n",
+                                priv->join_dtim_period, priv->beacon_int);
+                       pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n",
+                                priv->association_mode.preamble,
+                                priv->association_mode.greenfield,
+                                priv->bss_params.aid,
+                                priv->bss_params.operational_rate_set,
+                                priv->association_mode.basic_rate_set);
+                       wsm_set_association_mode(priv, &priv->association_mode);
+
+                       if (!info->ibss_joined) {
+                               wsm_keep_alive_period(priv, 30 /* sec */);
+                               wsm_set_bss_params(priv, &priv->bss_params);
+                               priv->setbssparams_done = true;
+                               cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work);
+                               cw1200_set_pm(priv, &priv->powersave_mode);
+                       }
+                       if (priv->vif->p2p) {
+                               pr_debug("[STA] Setting p2p powersave configuration.\n");
+                               wsm_set_p2p_ps_modeinfo(priv,
+                                                       &priv->p2p_ps_modeinfo);
+                       }
+                       if (priv->bt_present)
+                               cw1200_set_btcoexinfo(priv);
+               } else {
+                       memset(&priv->association_mode, 0,
+                              sizeof(priv->association_mode));
+                       memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+               }
+       }
+
+       /* ERP Protection */
+       if (changed & (BSS_CHANGED_ASSOC |
+                      BSS_CHANGED_ERP_CTS_PROT |
+                      BSS_CHANGED_ERP_PREAMBLE)) {
+               u32 prev_erp_info = priv->erp_info;
+               if (info->use_cts_prot)
+                       priv->erp_info |= WLAN_ERP_USE_PROTECTION;
+               else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT))
+                       priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
+
+               if (info->use_short_preamble)
+                       priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE;
+               else
+                       priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE;
+
+               pr_debug("[STA] ERP Protection: %x\n", priv->erp_info);
+
+               if (prev_erp_info != priv->erp_info)
+                       queue_work(priv->workqueue, &priv->set_cts_work);
+       }
+
+       /* ERP Slottime */
+       if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) {
+               __le32 slot_time = info->use_short_slot ?
+                       __cpu_to_le32(9) : __cpu_to_le32(20);
+               pr_debug("[STA] Slot time: %d us.\n",
+                        __le32_to_cpu(slot_time));
+               wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME,
+                             &slot_time, sizeof(slot_time));
+       }
+
+       if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) {
+               struct wsm_rcpi_rssi_threshold threshold = {
+                       .rollingAverageCount = 8,
+               };
+               pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n",
+                        info->cqm_rssi_thold, info->cqm_rssi_hyst);
+               priv->cqm_rssi_thold = info->cqm_rssi_thold;
+               priv->cqm_rssi_hyst = info->cqm_rssi_hyst;
+
+               if (info->cqm_rssi_thold || info->cqm_rssi_hyst) {
+                       /* RSSI subscription enabled */
+                       /* TODO: It's not a correct way of setting threshold.
+                        * Upper and lower must be set equal here and adjusted
+                        * in callback. However current implementation is much
+                        * more relaible and stable.
+                        */
+
+                       /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+                        * RSSI = RCPI / 2 - 110
+                        */
+                       if (priv->cqm_use_rssi) {
+                               threshold.upperThreshold =
+                                       info->cqm_rssi_thold + info->cqm_rssi_hyst;
+                               threshold.lowerThreshold =
+                                       info->cqm_rssi_thold;
+                               threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
+                       } else {
+                               threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2;
+                               threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2;
+                       }
+                       threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE;
+               } else {
+                       /* There is a bug in FW, see sta.c. We have to enable
+                        * dummy subscription to get correct RSSI values.
+                        */
+                       threshold.rssiRcpiMode |=
+                               WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+                               WSM_RCPI_RSSI_DONT_USE_UPPER |
+                               WSM_RCPI_RSSI_DONT_USE_LOWER;
+                       if (priv->cqm_use_rssi)
+                               threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
+               }
+               wsm_set_rcpi_rssi_threshold(priv, &threshold);
+       }
+       mutex_unlock(&priv->conf_mutex);
+
+       if (do_join) {
+               wsm_lock_tx(priv);
+               cw1200_do_join(priv); /* Will unlock it for us */
+       }
+}
+
+void cw1200_multicast_start_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, multicast_start_work);
+       long tmo = priv->join_dtim_period *
+                       (priv->beacon_int + 20) * HZ / 1024;
+
+       cancel_work_sync(&priv->multicast_stop_work);
+
+       if (!priv->aid0_bit_set) {
+               wsm_lock_tx(priv);
+               cw1200_set_tim_impl(priv, true);
+               priv->aid0_bit_set = true;
+               mod_timer(&priv->mcast_timeout, jiffies + tmo);
+               wsm_unlock_tx(priv);
+       }
+}
+
+void cw1200_multicast_stop_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, multicast_stop_work);
+
+       if (priv->aid0_bit_set) {
+               del_timer_sync(&priv->mcast_timeout);
+               wsm_lock_tx(priv);
+               priv->aid0_bit_set = false;
+               cw1200_set_tim_impl(priv, false);
+               wsm_unlock_tx(priv);
+       }
+}
+
+void cw1200_mcast_timeout(unsigned long arg)
+{
+       struct cw1200_common *priv =
+               (struct cw1200_common *)arg;
+
+       wiphy_warn(priv->hw->wiphy,
+                  "Multicast delivery timeout.\n");
+       spin_lock_bh(&priv->ps_state_lock);
+       priv->tx_multicast = priv->aid0_bit_set &&
+                       priv->buffered_multicasts;
+       if (priv->tx_multicast)
+               cw1200_bh_wakeup(priv);
+       spin_unlock_bh(&priv->ps_state_lock);
+}
+
+int cw1200_ampdu_action(struct ieee80211_hw *hw,
+                       struct ieee80211_vif *vif,
+                       enum ieee80211_ampdu_mlme_action action,
+                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                       u8 buf_size)
+{
+       /* Aggregation is implemented fully in firmware,
+        * including block ack negotiation. Do not allow
+        * mac80211 stack to do anything: it interferes with
+        * the firmware.
+        */
+
+       /* Note that we still need this function stubbed. */
+       return -ENOTSUPP;
+}
+
+/* ******************************************************************** */
+/* WSM callback                                                                */
+void cw1200_suspend_resume(struct cw1200_common *priv,
+                         struct wsm_suspend_resume *arg)
+{
+       pr_debug("[AP] %s: %s\n",
+                arg->stop ? "stop" : "start",
+                arg->multicast ? "broadcast" : "unicast");
+
+       if (arg->multicast) {
+               bool cancel_tmo = false;
+               spin_lock_bh(&priv->ps_state_lock);
+               if (arg->stop) {
+                       priv->tx_multicast = false;
+               } else {
+                       /* Firmware sends this indication every DTIM if there
+                        * is a STA in powersave connected. There is no reason
+                        * to suspend, following wakeup will consume much more
+                        * power than it could be saved.
+                        */
+                       cw1200_pm_stay_awake(&priv->pm_state,
+                                            priv->join_dtim_period *
+                                            (priv->beacon_int + 20) * HZ / 1024);
+                       priv->tx_multicast = (priv->aid0_bit_set &&
+                                             priv->buffered_multicasts);
+                       if (priv->tx_multicast) {
+                               cancel_tmo = true;
+                               cw1200_bh_wakeup(priv);
+                       }
+               }
+               spin_unlock_bh(&priv->ps_state_lock);
+               if (cancel_tmo)
+                       del_timer_sync(&priv->mcast_timeout);
+       } else {
+               spin_lock_bh(&priv->ps_state_lock);
+               cw1200_ps_notify(priv, arg->link_id, arg->stop);
+               spin_unlock_bh(&priv->ps_state_lock);
+               if (!arg->stop)
+                       cw1200_bh_wakeup(priv);
+       }
+       return;
+}
+
+/* ******************************************************************** */
+/* AP privates                                                         */
+
+static int cw1200_upload_beacon(struct cw1200_common *priv)
+{
+       int ret = 0;
+       struct ieee80211_mgmt *mgmt;
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_BEACON,
+       };
+
+       u16 tim_offset;
+       u16 tim_len;
+
+       if (priv->mode == NL80211_IFTYPE_STATION ||
+           priv->mode == NL80211_IFTYPE_MONITOR ||
+           priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+               goto done;
+
+       if (priv->vif->p2p)
+               frame.rate = WSM_TRANSMIT_RATE_6;
+
+       frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+                                            &tim_offset, &tim_len);
+       if (!frame.skb)
+               return -ENOMEM;
+
+       ret = wsm_set_template_frame(priv, &frame);
+
+       if (ret)
+               goto done;
+
+       /* TODO: Distill probe resp; remove TIM
+        * and any other beacon-specific IEs
+        */
+       mgmt = (void *)frame.skb->data;
+       mgmt->frame_control =
+               __cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                             IEEE80211_STYPE_PROBE_RESP);
+
+       frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE;
+       if (priv->vif->p2p) {
+               ret = wsm_set_probe_responder(priv, true);
+       } else {
+               ret = wsm_set_template_frame(priv, &frame);
+               wsm_set_probe_responder(priv, false);
+       }
+
+done:
+       dev_kfree_skb(frame.skb);
+
+       return ret;
+}
+
+static int cw1200_upload_pspoll(struct cw1200_common *priv)
+{
+       int ret = 0;
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_PS_POLL,
+               .rate = 0xFF,
+       };
+
+
+       frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif);
+       if (!frame.skb)
+               return -ENOMEM;
+
+       ret = wsm_set_template_frame(priv, &frame);
+
+       dev_kfree_skb(frame.skb);
+
+       return ret;
+}
+
+static int cw1200_upload_null(struct cw1200_common *priv)
+{
+       int ret = 0;
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_NULL,
+               .rate = 0xFF,
+       };
+
+       frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+       if (!frame.skb)
+               return -ENOMEM;
+
+       ret = wsm_set_template_frame(priv, &frame);
+
+       dev_kfree_skb(frame.skb);
+
+       return ret;
+}
+
+static int cw1200_upload_qosnull(struct cw1200_common *priv)
+{
+       int ret = 0;
+       /* TODO:  This needs to be implemented
+
+       struct wsm_template_frame frame = {
+               .frame_type = WSM_FRAME_TYPE_QOS_NULL,
+               .rate = 0xFF,
+       };
+
+       frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif);
+       if (!frame.skb)
+               return -ENOMEM;
+
+       ret = wsm_set_template_frame(priv, &frame);
+
+       dev_kfree_skb(frame.skb);
+
+       */
+       return ret;
+}
+
+static int cw1200_enable_beaconing(struct cw1200_common *priv,
+                                  bool enable)
+{
+       struct wsm_beacon_transmit transmit = {
+               .enable_beaconing = enable,
+       };
+
+       return wsm_beacon_transmit(priv, &transmit);
+}
+
+static int cw1200_start_ap(struct cw1200_common *priv)
+{
+       int ret;
+       struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+       struct wsm_start start = {
+               .mode = priv->vif->p2p ?
+                               WSM_START_MODE_P2P_GO : WSM_START_MODE_AP,
+               .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+                               WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
+               .channel_number = priv->channel->hw_value,
+               .beacon_interval = conf->beacon_int,
+               .dtim_period = conf->dtim_period,
+               .preamble = conf->use_short_preamble ?
+                               WSM_JOIN_PREAMBLE_SHORT :
+                               WSM_JOIN_PREAMBLE_LONG,
+               .probe_delay = 100,
+               .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
+                               conf->basic_rates),
+       };
+       struct wsm_operational_mode mode = {
+               .power_mode = cw1200_power_mode,
+               .disable_more_flag_usage = true,
+       };
+
+       memset(start.ssid, 0, sizeof(start.ssid));
+       if (!conf->hidden_ssid) {
+               start.ssid_len = conf->ssid_len;
+               memcpy(start.ssid, conf->ssid, start.ssid_len);
+       }
+
+       priv->beacon_int = conf->beacon_int;
+       priv->join_dtim_period = conf->dtim_period;
+
+       memset(&priv->link_id_db, 0, sizeof(priv->link_id_db));
+
+       pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n",
+                start.channel_number, start.band,
+                start.beacon_interval, start.dtim_period,
+                start.basic_rate_set,
+                start.ssid_len, start.ssid);
+       ret = wsm_start(priv, &start);
+       if (!ret)
+               ret = cw1200_upload_keys(priv);
+       if (!ret && priv->vif->p2p) {
+               pr_debug("[AP] Setting p2p powersave configuration.\n");
+               wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo);
+       }
+       if (!ret) {
+               wsm_set_block_ack_policy(priv, 0, 0);
+               priv->join_status = CW1200_JOIN_STATUS_AP;
+               cw1200_update_filtering(priv);
+       }
+       wsm_set_operational_mode(priv, &mode);
+       return ret;
+}
+
+static int cw1200_update_beaconing(struct cw1200_common *priv)
+{
+       struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+       struct wsm_reset reset = {
+               .link_id = 0,
+               .reset_statistics = true,
+       };
+
+       if (priv->mode == NL80211_IFTYPE_AP) {
+               /* TODO: check if changed channel, band */
+               if (priv->join_status != CW1200_JOIN_STATUS_AP ||
+                   priv->beacon_int != conf->beacon_int) {
+                       pr_debug("ap restarting\n");
+                       wsm_lock_tx(priv);
+                       if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE)
+                               wsm_reset(priv, &reset);
+                       priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+                       cw1200_start_ap(priv);
+                       wsm_unlock_tx(priv);
+               } else
+                       pr_debug("ap started join_status: %d\n",
+                                priv->join_status);
+       }
+       return 0;
+}
diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h
new file mode 100644 (file)
index 0000000..35babb6
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 STA_H_INCLUDED
+#define STA_H_INCLUDED
+
+/* ******************************************************************** */
+/* mac80211 API                                                                */
+
+int cw1200_start(struct ieee80211_hw *dev);
+void cw1200_stop(struct ieee80211_hw *dev);
+int cw1200_add_interface(struct ieee80211_hw *dev,
+                        struct ieee80211_vif *vif);
+void cw1200_remove_interface(struct ieee80211_hw *dev,
+                            struct ieee80211_vif *vif);
+int cw1200_change_interface(struct ieee80211_hw *dev,
+                           struct ieee80211_vif *vif,
+                           enum nl80211_iftype new_type,
+                           bool p2p);
+int cw1200_config(struct ieee80211_hw *dev, u32 changed);
+void cw1200_configure_filter(struct ieee80211_hw *dev,
+                            unsigned int changed_flags,
+                            unsigned int *total_flags,
+                            u64 multicast);
+int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+                  u16 queue, const struct ieee80211_tx_queue_params *params);
+int cw1200_get_stats(struct ieee80211_hw *dev,
+                    struct ieee80211_low_level_stats *stats);
+int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+                  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+                  struct ieee80211_key_conf *key);
+
+int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
+
+void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
+
+u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
+                            struct netdev_hw_addr_list *mc_list);
+
+int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
+
+/* ******************************************************************** */
+/* WSM callbacks                                                       */
+
+void cw1200_join_complete_cb(struct cw1200_common *priv,
+                               struct wsm_join_complete *arg);
+
+/* ******************************************************************** */
+/* WSM events                                                          */
+
+void cw1200_free_event_queue(struct cw1200_common *priv);
+void cw1200_event_handler(struct work_struct *work);
+void cw1200_bss_loss_work(struct work_struct *work);
+void cw1200_bss_params_work(struct work_struct *work);
+void cw1200_keep_alive_work(struct work_struct *work);
+void cw1200_tx_failure_work(struct work_struct *work);
+
+void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good,
+                            int bad);
+static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
+                                        int init, int good, int bad)
+{
+       spin_lock(&priv->bss_loss_lock);
+       __cw1200_cqm_bssloss_sm(priv, init, good, bad);
+       spin_unlock(&priv->bss_loss_lock);
+}
+
+/* ******************************************************************** */
+/* Internal API                                                                */
+
+int cw1200_setup_mac(struct cw1200_common *priv);
+void cw1200_join_timeout(struct work_struct *work);
+void cw1200_unjoin_work(struct work_struct *work);
+void cw1200_join_complete_work(struct work_struct *work);
+void cw1200_wep_key_work(struct work_struct *work);
+void cw1200_update_listening(struct cw1200_common *priv, bool enabled);
+void cw1200_update_filtering(struct cw1200_common *priv);
+void cw1200_update_filtering_work(struct work_struct *work);
+void cw1200_set_beacon_wakeup_period_work(struct work_struct *work);
+int cw1200_enable_listening(struct cw1200_common *priv);
+int cw1200_disable_listening(struct cw1200_common *priv);
+int cw1200_set_uapsd_param(struct cw1200_common *priv,
+                               const struct wsm_edca_params *arg);
+void cw1200_ba_work(struct work_struct *work);
+void cw1200_ba_timer(unsigned long arg);
+
+/* AP stuffs */
+int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+                  bool set);
+int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta);
+int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                     struct ieee80211_sta *sta);
+void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+                      enum sta_notify_cmd notify_cmd,
+                      struct ieee80211_sta *sta);
+void cw1200_bss_info_changed(struct ieee80211_hw *dev,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_bss_conf *info,
+                            u32 changed);
+int cw1200_ampdu_action(struct ieee80211_hw *hw,
+                       struct ieee80211_vif *vif,
+                       enum ieee80211_ampdu_mlme_action action,
+                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                       u8 buf_size);
+
+void cw1200_suspend_resume(struct cw1200_common *priv,
+                         struct wsm_suspend_resume *arg);
+void cw1200_set_tim_work(struct work_struct *work);
+void cw1200_set_cts_work(struct work_struct *work);
+void cw1200_multicast_start_work(struct work_struct *work);
+void cw1200_multicast_stop_work(struct work_struct *work);
+void cw1200_mcast_timeout(unsigned long arg);
+
+#endif
diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c
new file mode 100644 (file)
index 0000000..5862c37
--- /dev/null
@@ -0,0 +1,1473 @@
+/*
+ * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <net/mac80211.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "cw1200.h"
+#include "wsm.h"
+#include "bh.h"
+#include "sta.h"
+#include "debug.h"
+
+#define CW1200_INVALID_RATE_ID (0xFF)
+
+static int cw1200_handle_action_rx(struct cw1200_common *priv,
+                                  struct sk_buff *skb);
+static const struct ieee80211_rate *
+cw1200_get_tx_rate(const struct cw1200_common *priv,
+                  const struct ieee80211_tx_rate *rate);
+
+/* ******************************************************************** */
+/* TX queue lock / unlock                                              */
+
+static inline void cw1200_tx_queues_lock(struct cw1200_common *priv)
+{
+       int i;
+       for (i = 0; i < 4; ++i)
+               cw1200_queue_lock(&priv->tx_queue[i]);
+}
+
+static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv)
+{
+       int i;
+       for (i = 0; i < 4; ++i)
+               cw1200_queue_unlock(&priv->tx_queue[i]);
+}
+
+/* ******************************************************************** */
+/* TX policy cache implementation                                      */
+
+static void tx_policy_dump(struct tx_policy *policy)
+{
+       pr_debug("[TX policy] %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n",
+                policy->raw[0] & 0x0F,  policy->raw[0] >> 4,
+                policy->raw[1] & 0x0F,  policy->raw[1] >> 4,
+                policy->raw[2] & 0x0F,  policy->raw[2] >> 4,
+                policy->raw[3] & 0x0F,  policy->raw[3] >> 4,
+                policy->raw[4] & 0x0F,  policy->raw[4] >> 4,
+                policy->raw[5] & 0x0F,  policy->raw[5] >> 4,
+                policy->raw[6] & 0x0F,  policy->raw[6] >> 4,
+                policy->raw[7] & 0x0F,  policy->raw[7] >> 4,
+                policy->raw[8] & 0x0F,  policy->raw[8] >> 4,
+                policy->raw[9] & 0x0F,  policy->raw[9] >> 4,
+                policy->raw[10] & 0x0F,  policy->raw[10] >> 4,
+                policy->raw[11] & 0x0F,  policy->raw[11] >> 4,
+                policy->defined);
+}
+
+static void tx_policy_build(const struct cw1200_common *priv,
+       /* [out] */ struct tx_policy *policy,
+       struct ieee80211_tx_rate *rates, size_t count)
+{
+       int i, j;
+       unsigned limit = priv->short_frame_max_tx_count;
+       unsigned total = 0;
+       BUG_ON(rates[0].idx < 0);
+       memset(policy, 0, sizeof(*policy));
+
+       /* Sort rates in descending order. */
+       for (i = 1; i < count; ++i) {
+               if (rates[i].idx < 0) {
+                       count = i;
+                       break;
+               }
+               if (rates[i].idx > rates[i - 1].idx) {
+                       struct ieee80211_tx_rate tmp = rates[i - 1];
+                       rates[i - 1] = rates[i];
+                       rates[i] = tmp;
+               }
+       }
+
+       /* Eliminate duplicates. */
+       total = rates[0].count;
+       for (i = 0, j = 1; j < count; ++j) {
+               if (rates[j].idx == rates[i].idx) {
+                       rates[i].count += rates[j].count;
+               } else if (rates[j].idx > rates[i].idx) {
+                       break;
+               } else {
+                       ++i;
+                       if (i != j)
+                               rates[i] = rates[j];
+               }
+               total += rates[j].count;
+       }
+       count = i + 1;
+
+       /* Re-fill policy trying to keep every requested rate and with
+        * respect to the global max tx retransmission count.
+        */
+       if (limit < count)
+               limit = count;
+       if (total > limit) {
+               for (i = 0; i < count; ++i) {
+                       int left = count - i - 1;
+                       if (rates[i].count > limit - left)
+                               rates[i].count = limit - left;
+                       limit -= rates[i].count;
+               }
+       }
+
+       /* HACK!!! Device has problems (at least) switching from
+        * 54Mbps CTS to 1Mbps. This switch takes enormous amount
+        * of time (100-200 ms), leading to valuable throughput drop.
+        * As a workaround, additional g-rates are injected to the
+        * policy.
+        */
+       if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) &&
+           rates[0].idx > 4 && rates[0].count > 2 &&
+           rates[1].idx < 2) {
+               int mid_rate = (rates[0].idx + 4) >> 1;
+
+               /* Decrease number of retries for the initial rate */
+               rates[0].count -= 2;
+
+               if (mid_rate != 4) {
+                       /* Keep fallback rate at 1Mbps. */
+                       rates[3] = rates[1];
+
+                       /* Inject 1 transmission on lowest g-rate */
+                       rates[2].idx = 4;
+                       rates[2].count = 1;
+                       rates[2].flags = rates[1].flags;
+
+                       /* Inject 1 transmission on mid-rate */
+                       rates[1].idx = mid_rate;
+                       rates[1].count = 1;
+
+                       /* Fallback to 1 Mbps is a really bad thing,
+                        * so let's try to increase probability of
+                        * successful transmission on the lowest g rate
+                        * even more
+                        */
+                       if (rates[0].count >= 3) {
+                               --rates[0].count;
+                               ++rates[2].count;
+                       }
+
+                       /* Adjust amount of rates defined */
+                       count += 2;
+               } else {
+                       /* Keep fallback rate at 1Mbps. */
+                       rates[2] = rates[1];
+
+                       /* Inject 2 transmissions on lowest g-rate */
+                       rates[1].idx = 4;
+                       rates[1].count = 2;
+
+                       /* Adjust amount of rates defined */
+                       count += 1;
+               }
+       }
+
+       policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1;
+
+       for (i = 0; i < count; ++i) {
+               register unsigned rateid, off, shift, retries;
+
+               rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value;
+               off = rateid >> 3;              /* eq. rateid / 8 */
+               shift = (rateid & 0x07) << 2;   /* eq. (rateid % 8) * 4 */
+
+               retries = rates[i].count;
+               if (retries > 0x0F) {
+                       rates[i].count = 0x0f;
+                       retries = 0x0F;
+               }
+               policy->tbl[off] |= __cpu_to_le32(retries << shift);
+               policy->retry_count += retries;
+       }
+
+       pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d\n",
+                count,
+                rates[0].idx, rates[0].count,
+                rates[1].idx, rates[1].count,
+                rates[2].idx, rates[2].count,
+                rates[3].idx, rates[3].count);
+}
+
+static inline bool tx_policy_is_equal(const struct tx_policy *wanted,
+                                       const struct tx_policy *cached)
+{
+       size_t count = wanted->defined >> 1;
+       if (wanted->defined > cached->defined)
+               return false;
+       if (count) {
+               if (memcmp(wanted->raw, cached->raw, count))
+                       return false;
+       }
+       if (wanted->defined & 1) {
+               if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F))
+                       return false;
+       }
+       return true;
+}
+
+static int tx_policy_find(struct tx_policy_cache *cache,
+                               const struct tx_policy *wanted)
+{
+       /* O(n) complexity. Not so good, but there's only 8 entries in
+        * the cache.
+        * Also lru helps to reduce search time.
+        */
+       struct tx_policy_cache_entry *it;
+       /* First search for policy in "used" list */
+       list_for_each_entry(it, &cache->used, link) {
+               if (tx_policy_is_equal(wanted, &it->policy))
+                       return it - cache->cache;
+       }
+       /* Then - in "free list" */
+       list_for_each_entry(it, &cache->free, link) {
+               if (tx_policy_is_equal(wanted, &it->policy))
+                       return it - cache->cache;
+       }
+       return -1;
+}
+
+static inline void tx_policy_use(struct tx_policy_cache *cache,
+                                struct tx_policy_cache_entry *entry)
+{
+       ++entry->policy.usage_count;
+       list_move(&entry->link, &cache->used);
+}
+
+static inline int tx_policy_release(struct tx_policy_cache *cache,
+                                   struct tx_policy_cache_entry *entry)
+{
+       int ret = --entry->policy.usage_count;
+       if (!ret)
+               list_move(&entry->link, &cache->free);
+       return ret;
+}
+
+void tx_policy_clean(struct cw1200_common *priv)
+{
+       int idx, locked;
+       struct tx_policy_cache *cache = &priv->tx_policy_cache;
+       struct tx_policy_cache_entry *entry;
+
+       cw1200_tx_queues_lock(priv);
+       spin_lock_bh(&cache->lock);
+       locked = list_empty(&cache->free);
+
+       for (idx = 0; idx < TX_POLICY_CACHE_SIZE; idx++) {
+               entry = &cache->cache[idx];
+               /* Policy usage count should be 0 at this time as all queues
+                  should be empty
+                */
+               if (WARN_ON(entry->policy.usage_count)) {
+                       entry->policy.usage_count = 0;
+                       list_move(&entry->link, &cache->free);
+               }
+               memset(&entry->policy, 0, sizeof(entry->policy));
+       }
+       if (locked)
+               cw1200_tx_queues_unlock(priv);
+
+       cw1200_tx_queues_unlock(priv);
+       spin_unlock_bh(&cache->lock);
+}
+
+/* ******************************************************************** */
+/* External TX policy cache API                                                */
+
+void tx_policy_init(struct cw1200_common *priv)
+{
+       struct tx_policy_cache *cache = &priv->tx_policy_cache;
+       int i;
+
+       memset(cache, 0, sizeof(*cache));
+
+       spin_lock_init(&cache->lock);
+       INIT_LIST_HEAD(&cache->used);
+       INIT_LIST_HEAD(&cache->free);
+
+       for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i)
+               list_add(&cache->cache[i].link, &cache->free);
+}
+
+static int tx_policy_get(struct cw1200_common *priv,
+                 struct ieee80211_tx_rate *rates,
+                 size_t count, bool *renew)
+{
+       int idx;
+       struct tx_policy_cache *cache = &priv->tx_policy_cache;
+       struct tx_policy wanted;
+
+       tx_policy_build(priv, &wanted, rates, count);
+
+       spin_lock_bh(&cache->lock);
+       if (WARN_ON_ONCE(list_empty(&cache->free))) {
+               spin_unlock_bh(&cache->lock);
+               return CW1200_INVALID_RATE_ID;
+       }
+       idx = tx_policy_find(cache, &wanted);
+       if (idx >= 0) {
+               pr_debug("[TX policy] Used TX policy: %d\n", idx);
+               *renew = false;
+       } else {
+               struct tx_policy_cache_entry *entry;
+               *renew = true;
+               /* If policy is not found create a new one
+                * using the oldest entry in "free" list
+                */
+               entry = list_entry(cache->free.prev,
+                       struct tx_policy_cache_entry, link);
+               entry->policy = wanted;
+               idx = entry - cache->cache;
+               pr_debug("[TX policy] New TX policy: %d\n", idx);
+               tx_policy_dump(&entry->policy);
+       }
+       tx_policy_use(cache, &cache->cache[idx]);
+       if (list_empty(&cache->free)) {
+               /* Lock TX queues. */
+               cw1200_tx_queues_lock(priv);
+       }
+       spin_unlock_bh(&cache->lock);
+       return idx;
+}
+
+static void tx_policy_put(struct cw1200_common *priv, int idx)
+{
+       int usage, locked;
+       struct tx_policy_cache *cache = &priv->tx_policy_cache;
+
+       spin_lock_bh(&cache->lock);
+       locked = list_empty(&cache->free);
+       usage = tx_policy_release(cache, &cache->cache[idx]);
+       if (locked && !usage) {
+               /* Unlock TX queues. */
+               cw1200_tx_queues_unlock(priv);
+       }
+       spin_unlock_bh(&cache->lock);
+}
+
+static int tx_policy_upload(struct cw1200_common *priv)
+{
+       struct tx_policy_cache *cache = &priv->tx_policy_cache;
+       int i;
+       struct wsm_set_tx_rate_retry_policy arg = {
+               .num = 0,
+       };
+       spin_lock_bh(&cache->lock);
+
+       /* Upload only modified entries. */
+       for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) {
+               struct tx_policy *src = &cache->cache[i].policy;
+               if (src->retry_count && !src->uploaded) {
+                       struct wsm_tx_rate_retry_policy *dst =
+                               &arg.tbl[arg.num];
+                       dst->index = i;
+                       dst->short_retries = priv->short_frame_max_tx_count;
+                       dst->long_retries = priv->long_frame_max_tx_count;
+
+                       dst->flags = WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED |
+                               WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT;
+                       memcpy(dst->rate_count_indices, src->tbl,
+                              sizeof(dst->rate_count_indices));
+                       src->uploaded = 1;
+                       ++arg.num;
+               }
+       }
+       spin_unlock_bh(&cache->lock);
+       cw1200_debug_tx_cache_miss(priv);
+       pr_debug("[TX policy] Upload %d policies\n", arg.num);
+       return wsm_set_tx_rate_retry_policy(priv, &arg);
+}
+
+void tx_policy_upload_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, tx_policy_upload_work);
+
+       pr_debug("[TX] TX policy upload.\n");
+       tx_policy_upload(priv);
+
+       wsm_unlock_tx(priv);
+       cw1200_tx_queues_unlock(priv);
+}
+
+/* ******************************************************************** */
+/* cw1200 TX implementation                                            */
+
+struct cw1200_txinfo {
+       struct sk_buff *skb;
+       unsigned queue;
+       struct ieee80211_tx_info *tx_info;
+       const struct ieee80211_rate *rate;
+       struct ieee80211_hdr *hdr;
+       size_t hdrlen;
+       const u8 *da;
+       struct cw1200_sta_priv *sta_priv;
+       struct ieee80211_sta *sta;
+       struct cw1200_txpriv txpriv;
+};
+
+u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates)
+{
+       u32 ret = 0;
+       int i;
+       for (i = 0; i < 32; ++i) {
+               if (rates & BIT(i))
+                       ret |= BIT(priv->rates[i].hw_value);
+       }
+       return ret;
+}
+
+static const struct ieee80211_rate *
+cw1200_get_tx_rate(const struct cw1200_common *priv,
+                  const struct ieee80211_tx_rate *rate)
+{
+       if (rate->idx < 0)
+               return NULL;
+       if (rate->flags & IEEE80211_TX_RC_MCS)
+               return &priv->mcs_rates[rate->idx];
+       return &priv->hw->wiphy->bands[priv->channel->band]->
+               bitrates[rate->idx];
+}
+
+static int
+cw1200_tx_h_calc_link_ids(struct cw1200_common *priv,
+                         struct cw1200_txinfo *t)
+{
+       if (t->sta && t->sta_priv->link_id)
+               t->txpriv.raw_link_id =
+                               t->txpriv.link_id =
+                               t->sta_priv->link_id;
+       else if (priv->mode != NL80211_IFTYPE_AP)
+               t->txpriv.raw_link_id =
+                               t->txpriv.link_id = 0;
+       else if (is_multicast_ether_addr(t->da)) {
+               if (priv->enable_beacon) {
+                       t->txpriv.raw_link_id = 0;
+                       t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM;
+               } else {
+                       t->txpriv.raw_link_id = 0;
+                       t->txpriv.link_id = 0;
+               }
+       } else {
+               t->txpriv.link_id = cw1200_find_link_id(priv, t->da);
+               if (!t->txpriv.link_id)
+                       t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da);
+               if (!t->txpriv.link_id) {
+                       wiphy_err(priv->hw->wiphy,
+                                 "No more link IDs available.\n");
+                       return -ENOENT;
+               }
+               t->txpriv.raw_link_id = t->txpriv.link_id;
+       }
+       if (t->txpriv.raw_link_id)
+               priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp =
+                               jiffies;
+       if (t->sta && (t->sta->uapsd_queues & BIT(t->queue)))
+               t->txpriv.link_id = CW1200_LINK_ID_UAPSD;
+       return 0;
+}
+
+static void
+cw1200_tx_h_pm(struct cw1200_common *priv,
+              struct cw1200_txinfo *t)
+{
+       if (ieee80211_is_auth(t->hdr->frame_control)) {
+               u32 mask = ~BIT(t->txpriv.raw_link_id);
+               spin_lock_bh(&priv->ps_state_lock);
+               priv->sta_asleep_mask &= mask;
+               priv->pspoll_mask &= mask;
+               spin_unlock_bh(&priv->ps_state_lock);
+       }
+}
+
+static void
+cw1200_tx_h_calc_tid(struct cw1200_common *priv,
+                    struct cw1200_txinfo *t)
+{
+       if (ieee80211_is_data_qos(t->hdr->frame_control)) {
+               u8 *qos = ieee80211_get_qos_ctl(t->hdr);
+               t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK;
+       } else if (ieee80211_is_data(t->hdr->frame_control)) {
+               t->txpriv.tid = 0;
+       }
+}
+
+static int
+cw1200_tx_h_crypt(struct cw1200_common *priv,
+                 struct cw1200_txinfo *t)
+{
+       if (!t->tx_info->control.hw_key ||
+           !ieee80211_has_protected(t->hdr->frame_control))
+               return 0;
+
+       t->hdrlen += t->tx_info->control.hw_key->iv_len;
+       skb_put(t->skb, t->tx_info->control.hw_key->icv_len);
+
+       if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+               skb_put(t->skb, 8); /* MIC space */
+
+       return 0;
+}
+
+static int
+cw1200_tx_h_align(struct cw1200_common *priv,
+                 struct cw1200_txinfo *t,
+                 u8 *flags)
+{
+       size_t offset = (size_t)t->skb->data & 3;
+
+       if (!offset)
+               return 0;
+
+       if (offset & 1) {
+               wiphy_err(priv->hw->wiphy,
+                         "Bug: attempt to transmit a frame with wrong alignment: %zu\n",
+                         offset);
+               return -EINVAL;
+       }
+
+       if (skb_headroom(t->skb) < offset) {
+               wiphy_err(priv->hw->wiphy,
+                         "Bug: no space allocated for DMA alignment. headroom: %d\n",
+                         skb_headroom(t->skb));
+               return -ENOMEM;
+       }
+       skb_push(t->skb, offset);
+       t->hdrlen += offset;
+       t->txpriv.offset += offset;
+       *flags |= WSM_TX_2BYTES_SHIFT;
+       cw1200_debug_tx_align(priv);
+       return 0;
+}
+
+static int
+cw1200_tx_h_action(struct cw1200_common *priv,
+                  struct cw1200_txinfo *t)
+{
+       struct ieee80211_mgmt *mgmt =
+               (struct ieee80211_mgmt *)t->hdr;
+       if (ieee80211_is_action(t->hdr->frame_control) &&
+           mgmt->u.action.category == WLAN_CATEGORY_BACK)
+               return 1;
+       else
+               return 0;
+}
+
+/* Add WSM header */
+static struct wsm_tx *
+cw1200_tx_h_wsm(struct cw1200_common *priv,
+               struct cw1200_txinfo *t)
+{
+       struct wsm_tx *wsm;
+
+       if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) {
+               wiphy_err(priv->hw->wiphy,
+                         "Bug: no space allocated for WSM header. headroom: %d\n",
+                         skb_headroom(t->skb));
+               return NULL;
+       }
+
+       wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx));
+       t->txpriv.offset += sizeof(struct wsm_tx);
+       memset(wsm, 0, sizeof(*wsm));
+       wsm->hdr.len = __cpu_to_le16(t->skb->len);
+       wsm->hdr.id = __cpu_to_le16(0x0004);
+       wsm->queue_id = wsm_queue_id_to_wsm(t->queue);
+       return wsm;
+}
+
+/* BT Coex specific handling */
+static void
+cw1200_tx_h_bt(struct cw1200_common *priv,
+              struct cw1200_txinfo *t,
+              struct wsm_tx *wsm)
+{
+       u8 priority = 0;
+
+       if (!priv->bt_present)
+               return;
+
+       if (ieee80211_is_nullfunc(t->hdr->frame_control)) {
+               priority = WSM_EPTA_PRIORITY_MGT;
+       } else if (ieee80211_is_data(t->hdr->frame_control)) {
+               /* Skip LLC SNAP header (+6) */
+               u8 *payload = &t->skb->data[t->hdrlen];
+               __be16 *ethertype = (__be16 *)&payload[6];
+               if (be16_to_cpu(*ethertype) == ETH_P_PAE)
+                       priority = WSM_EPTA_PRIORITY_EAPOL;
+       } else if (ieee80211_is_assoc_req(t->hdr->frame_control) ||
+               ieee80211_is_reassoc_req(t->hdr->frame_control)) {
+               struct ieee80211_mgmt *mgt_frame =
+                               (struct ieee80211_mgmt *)t->hdr;
+
+               if (le16_to_cpu(mgt_frame->u.assoc_req.listen_interval) <
+                                               priv->listen_interval) {
+                       pr_debug("Modified Listen Interval to %d from %d\n",
+                                priv->listen_interval,
+                                mgt_frame->u.assoc_req.listen_interval);
+                       /* Replace listen interval derieved from
+                        * the one read from SDD
+                        */
+                       mgt_frame->u.assoc_req.listen_interval = cpu_to_le16(priv->listen_interval);
+               }
+       }
+
+       if (!priority) {
+               if (ieee80211_is_action(t->hdr->frame_control))
+                       priority = WSM_EPTA_PRIORITY_ACTION;
+               else if (ieee80211_is_mgmt(t->hdr->frame_control))
+                       priority = WSM_EPTA_PRIORITY_MGT;
+               else if ((wsm->queue_id == WSM_QUEUE_VOICE))
+                       priority = WSM_EPTA_PRIORITY_VOICE;
+               else if ((wsm->queue_id == WSM_QUEUE_VIDEO))
+                       priority = WSM_EPTA_PRIORITY_VIDEO;
+               else
+                       priority = WSM_EPTA_PRIORITY_DATA;
+       }
+
+       pr_debug("[TX] EPTA priority %d.\n", priority);
+
+       wsm->flags |= priority << 1;
+}
+
+static int
+cw1200_tx_h_rate_policy(struct cw1200_common *priv,
+                       struct cw1200_txinfo *t,
+                       struct wsm_tx *wsm)
+{
+       bool tx_policy_renew = false;
+
+       t->txpriv.rate_id = tx_policy_get(priv,
+               t->tx_info->control.rates, IEEE80211_TX_MAX_RATES,
+               &tx_policy_renew);
+       if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID)
+               return -EFAULT;
+
+       wsm->flags |= t->txpriv.rate_id << 4;
+
+       t->rate = cw1200_get_tx_rate(priv,
+               &t->tx_info->control.rates[0]),
+       wsm->max_tx_rate = t->rate->hw_value;
+       if (t->rate->flags & IEEE80211_TX_RC_MCS) {
+               if (cw1200_ht_greenfield(&priv->ht_info))
+                       wsm->ht_tx_parameters |=
+                               __cpu_to_le32(WSM_HT_TX_GREENFIELD);
+               else
+                       wsm->ht_tx_parameters |=
+                               __cpu_to_le32(WSM_HT_TX_MIXED);
+       }
+
+       if (tx_policy_renew) {
+               pr_debug("[TX] TX policy renew.\n");
+               /* It's not so optimal to stop TX queues every now and then.
+                * Better to reimplement task scheduling with
+                * a counter. TODO.
+                */
+               wsm_lock_tx_async(priv);
+               cw1200_tx_queues_lock(priv);
+               if (queue_work(priv->workqueue,
+                              &priv->tx_policy_upload_work) <= 0) {
+                       cw1200_tx_queues_unlock(priv);
+                       wsm_unlock_tx(priv);
+               }
+       }
+       return 0;
+}
+
+static bool
+cw1200_tx_h_pm_state(struct cw1200_common *priv,
+                    struct cw1200_txinfo *t)
+{
+       int was_buffered = 1;
+
+       if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM &&
+           !priv->buffered_multicasts) {
+               priv->buffered_multicasts = true;
+               if (priv->sta_asleep_mask)
+                       queue_work(priv->workqueue,
+                                  &priv->multicast_start_work);
+       }
+
+       if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID)
+               was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1].buffered[t->txpriv.tid]++;
+
+       return !was_buffered;
+}
+
+/* ******************************************************************** */
+
+void cw1200_tx(struct ieee80211_hw *dev,
+              struct ieee80211_tx_control *control,
+              struct sk_buff *skb)
+{
+       struct cw1200_common *priv = dev->priv;
+       struct cw1200_txinfo t = {
+               .skb = skb,
+               .queue = skb_get_queue_mapping(skb),
+               .tx_info = IEEE80211_SKB_CB(skb),
+               .hdr = (struct ieee80211_hdr *)skb->data,
+               .txpriv.tid = CW1200_MAX_TID,
+               .txpriv.rate_id = CW1200_INVALID_RATE_ID,
+       };
+       struct ieee80211_sta *sta;
+       struct wsm_tx *wsm;
+       bool tid_update = 0;
+       u8 flags = 0;
+       int ret;
+
+       if (priv->bh_error)
+               goto drop;
+
+       t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control);
+       t.da = ieee80211_get_DA(t.hdr);
+       if (control) {
+               t.sta = control->sta;
+               t.sta_priv = (struct cw1200_sta_priv *)&t.sta->drv_priv;
+       }
+
+       if (WARN_ON(t.queue >= 4))
+               goto drop;
+
+       ret = cw1200_tx_h_calc_link_ids(priv, &t);
+       if (ret)
+               goto drop;
+
+       pr_debug("[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n",
+                skb->len, t.queue, t.txpriv.link_id,
+                t.txpriv.raw_link_id);
+
+       cw1200_tx_h_pm(priv, &t);
+       cw1200_tx_h_calc_tid(priv, &t);
+       ret = cw1200_tx_h_crypt(priv, &t);
+       if (ret)
+               goto drop;
+       ret = cw1200_tx_h_align(priv, &t, &flags);
+       if (ret)
+               goto drop;
+       ret = cw1200_tx_h_action(priv, &t);
+       if (ret)
+               goto drop;
+       wsm = cw1200_tx_h_wsm(priv, &t);
+       if (!wsm) {
+               ret = -ENOMEM;
+               goto drop;
+       }
+       wsm->flags |= flags;
+       cw1200_tx_h_bt(priv, &t, wsm);
+       ret = cw1200_tx_h_rate_policy(priv, &t, wsm);
+       if (ret)
+               goto drop;
+
+       rcu_read_lock();
+       sta = rcu_dereference(t.sta);
+
+       spin_lock_bh(&priv->ps_state_lock);
+       {
+               tid_update = cw1200_tx_h_pm_state(priv, &t);
+               BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue],
+                                       t.skb, &t.txpriv));
+       }
+       spin_unlock_bh(&priv->ps_state_lock);
+
+       if (tid_update && sta)
+               ieee80211_sta_set_buffered(sta, t.txpriv.tid, true);
+
+       rcu_read_unlock();
+
+       cw1200_bh_wakeup(priv);
+
+       return;
+
+drop:
+       cw1200_skb_dtor(priv, skb, &t.txpriv);
+       return;
+}
+
+/* ******************************************************************** */
+
+static int cw1200_handle_action_rx(struct cw1200_common *priv,
+                                  struct sk_buff *skb)
+{
+       struct ieee80211_mgmt *mgmt = (void *)skb->data;
+
+       /* Filter block ACK negotiation: fully controlled by firmware */
+       if (mgmt->u.action.category == WLAN_CATEGORY_BACK)
+               return 1;
+
+       return 0;
+}
+
+static int cw1200_handle_pspoll(struct cw1200_common *priv,
+                               struct sk_buff *skb)
+{
+       struct ieee80211_sta *sta;
+       struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data;
+       int link_id = 0;
+       u32 pspoll_mask = 0;
+       int drop = 1;
+       int i;
+
+       if (priv->join_status != CW1200_JOIN_STATUS_AP)
+               goto done;
+       if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN))
+               goto done;
+
+       rcu_read_lock();
+       sta = ieee80211_find_sta(priv->vif, pspoll->ta);
+       if (sta) {
+               struct cw1200_sta_priv *sta_priv;
+               sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv;
+               link_id = sta_priv->link_id;
+               pspoll_mask = BIT(sta_priv->link_id);
+       }
+       rcu_read_unlock();
+       if (!link_id)
+               goto done;
+
+       priv->pspoll_mask |= pspoll_mask;
+       drop = 0;
+
+       /* Do not report pspols if data for given link id is queued already. */
+       for (i = 0; i < 4; ++i) {
+               if (cw1200_queue_get_num_queued(&priv->tx_queue[i],
+                                               pspoll_mask)) {
+                       cw1200_bh_wakeup(priv);
+                       drop = 1;
+                       break;
+               }
+       }
+       pr_debug("[RX] PSPOLL: %s\n", drop ? "local" : "fwd");
+done:
+       return drop;
+}
+
+/* ******************************************************************** */
+
+void cw1200_tx_confirm_cb(struct cw1200_common *priv,
+                         int link_id,
+                         struct wsm_tx_confirm *arg)
+{
+       u8 queue_id = cw1200_queue_get_queue_id(arg->packet_id);
+       struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+       struct sk_buff *skb;
+       const struct cw1200_txpriv *txpriv;
+
+       pr_debug("[TX] TX confirm: %d, %d.\n",
+                arg->status, arg->ack_failures);
+
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+               /* STA is stopped. */
+               return;
+       }
+
+       if (WARN_ON(queue_id >= 4))
+               return;
+
+       if (arg->status)
+               pr_debug("TX failed: %d.\n", arg->status);
+
+       if ((arg->status == WSM_REQUEUE) &&
+           (arg->flags & WSM_TX_STATUS_REQUEUE)) {
+               /* "Requeue" means "implicit suspend" */
+               struct wsm_suspend_resume suspend = {
+                       .link_id = link_id,
+                       .stop = 1,
+                       .multicast = !link_id,
+               };
+               cw1200_suspend_resume(priv, &suspend);
+               wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d). STAs asleep: 0x%.8X\n",
+                          link_id,
+                          cw1200_queue_get_generation(arg->packet_id) + 1,
+                          priv->sta_asleep_mask);
+               cw1200_queue_requeue(queue, arg->packet_id);
+               spin_lock_bh(&priv->ps_state_lock);
+               if (!link_id) {
+                       priv->buffered_multicasts = true;
+                       if (priv->sta_asleep_mask) {
+                               queue_work(priv->workqueue,
+                                          &priv->multicast_start_work);
+                       }
+               }
+               spin_unlock_bh(&priv->ps_state_lock);
+       } else if (!cw1200_queue_get_skb(queue, arg->packet_id,
+                                        &skb, &txpriv)) {
+               struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb);
+               int tx_count = arg->ack_failures;
+               u8 ht_flags = 0;
+               int i;
+
+               if (cw1200_ht_greenfield(&priv->ht_info))
+                       ht_flags |= IEEE80211_TX_RC_GREEN_FIELD;
+
+               spin_lock(&priv->bss_loss_lock);
+               if (priv->bss_loss_state &&
+                   arg->packet_id == priv->bss_loss_confirm_id) {
+                       if (arg->status) {
+                               /* Recovery failed */
+                               __cw1200_cqm_bssloss_sm(priv, 0, 0, 1);
+                       } else {
+                               /* Recovery succeeded */
+                               __cw1200_cqm_bssloss_sm(priv, 0, 1, 0);
+                       }
+               }
+               spin_unlock(&priv->bss_loss_lock);
+
+               if (!arg->status) {
+                       tx->flags |= IEEE80211_TX_STAT_ACK;
+                       ++tx_count;
+                       cw1200_debug_txed(priv);
+                       if (arg->flags & WSM_TX_STATUS_AGGREGATION) {
+                               /* Do not report aggregation to mac80211:
+                                * it confuses minstrel a lot.
+                                */
+                               /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */
+                               cw1200_debug_txed_agg(priv);
+                       }
+               } else {
+                       if (tx_count)
+                               ++tx_count;
+               }
+
+               for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) {
+                       if (tx->status.rates[i].count >= tx_count) {
+                               tx->status.rates[i].count = tx_count;
+                               break;
+                       }
+                       tx_count -= tx->status.rates[i].count;
+                       if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS)
+                               tx->status.rates[i].flags |= ht_flags;
+               }
+
+               for (++i; i < IEEE80211_TX_MAX_RATES; ++i) {
+                       tx->status.rates[i].count = 0;
+                       tx->status.rates[i].idx = -1;
+               }
+
+               /* Pull off any crypto trailers that we added on */
+               if (tx->control.hw_key) {
+                       skb_trim(skb, skb->len - tx->control.hw_key->icv_len);
+                       if (tx->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+                               skb_trim(skb, skb->len - 8); /* MIC space */
+               }
+               cw1200_queue_remove(queue, arg->packet_id);
+       }
+       /* XXX TODO:  Only wake if there are pending transmits.. */
+       cw1200_bh_wakeup(priv);
+}
+
+static void cw1200_notify_buffered_tx(struct cw1200_common *priv,
+                              struct sk_buff *skb, int link_id, int tid)
+{
+       struct ieee80211_sta *sta;
+       struct ieee80211_hdr *hdr;
+       u8 *buffered;
+       u8 still_buffered = 0;
+
+       if (link_id && tid < CW1200_MAX_TID) {
+               buffered = priv->link_id_db
+                               [link_id - 1].buffered;
+
+               spin_lock_bh(&priv->ps_state_lock);
+               if (!WARN_ON(!buffered[tid]))
+                       still_buffered = --buffered[tid];
+               spin_unlock_bh(&priv->ps_state_lock);
+
+               if (!still_buffered && tid < CW1200_MAX_TID) {
+                       hdr = (struct ieee80211_hdr *)skb->data;
+                       rcu_read_lock();
+                       sta = ieee80211_find_sta(priv->vif, hdr->addr1);
+                       if (sta)
+                               ieee80211_sta_set_buffered(sta, tid, false);
+                       rcu_read_unlock();
+               }
+       }
+}
+
+void cw1200_skb_dtor(struct cw1200_common *priv,
+                    struct sk_buff *skb,
+                    const struct cw1200_txpriv *txpriv)
+{
+       skb_pull(skb, txpriv->offset);
+       if (txpriv->rate_id != CW1200_INVALID_RATE_ID) {
+               cw1200_notify_buffered_tx(priv, skb,
+                                         txpriv->raw_link_id, txpriv->tid);
+               tx_policy_put(priv, txpriv->rate_id);
+       }
+       ieee80211_tx_status(priv->hw, skb);
+}
+
+void cw1200_rx_cb(struct cw1200_common *priv,
+                 struct wsm_rx *arg,
+                 int link_id,
+                 struct sk_buff **skb_p)
+{
+       struct sk_buff *skb = *skb_p;
+       struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+       struct cw1200_link_entry *entry = NULL;
+       unsigned long grace_period;
+
+       bool early_data = false;
+       bool p2p = priv->vif && priv->vif->p2p;
+       size_t hdrlen;
+       hdr->flag = 0;
+
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+               /* STA is stopped. */
+               goto drop;
+       }
+
+       if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) {
+               entry = &priv->link_id_db[link_id - 1];
+               if (entry->status == CW1200_LINK_SOFT &&
+                   ieee80211_is_data(frame->frame_control))
+                       early_data = true;
+               entry->timestamp = jiffies;
+       } else if (p2p &&
+                  ieee80211_is_action(frame->frame_control) &&
+                  (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+               pr_debug("[RX] Going to MAP&RESET link ID\n");
+               WARN_ON(work_pending(&priv->linkid_reset_work));
+               memcpy(&priv->action_frame_sa[0],
+                      ieee80211_get_SA(frame), ETH_ALEN);
+               priv->action_linkid = 0;
+               schedule_work(&priv->linkid_reset_work);
+       }
+
+       if (link_id && p2p &&
+           ieee80211_is_action(frame->frame_control) &&
+           (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+               /* Link ID already exists for the ACTION frame.
+                * Reset and Remap
+                */
+               WARN_ON(work_pending(&priv->linkid_reset_work));
+               memcpy(&priv->action_frame_sa[0],
+                      ieee80211_get_SA(frame), ETH_ALEN);
+               priv->action_linkid = link_id;
+               schedule_work(&priv->linkid_reset_work);
+       }
+       if (arg->status) {
+               if (arg->status == WSM_STATUS_MICFAILURE) {
+                       pr_debug("[RX] MIC failure.\n");
+                       hdr->flag |= RX_FLAG_MMIC_ERROR;
+               } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) {
+                       pr_debug("[RX] No key found.\n");
+                       goto drop;
+               } else {
+                       pr_debug("[RX] Receive failure: %d.\n",
+                                arg->status);
+                       goto drop;
+               }
+       }
+
+       if (skb->len < sizeof(struct ieee80211_pspoll)) {
+               wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. Size is lesser than IEEE header.\n");
+               goto drop;
+       }
+
+       if (ieee80211_is_pspoll(frame->frame_control))
+               if (cw1200_handle_pspoll(priv, skb))
+                       goto drop;
+
+       hdr->band = ((arg->channel_number & 0xff00) ||
+                    (arg->channel_number > 14)) ?
+                       IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
+       hdr->freq = ieee80211_channel_to_frequency(
+                       arg->channel_number,
+                       hdr->band);
+
+       if (arg->rx_rate >= 14) {
+               hdr->flag |= RX_FLAG_HT;
+               hdr->rate_idx = arg->rx_rate - 14;
+       } else if (arg->rx_rate >= 4) {
+               hdr->rate_idx = arg->rx_rate - 2;
+       } else {
+               hdr->rate_idx = arg->rx_rate;
+       }
+
+       hdr->signal = (s8)arg->rcpi_rssi;
+       hdr->antenna = 0;
+
+       hdrlen = ieee80211_hdrlen(frame->frame_control);
+
+       if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+               size_t iv_len = 0, icv_len = 0;
+
+               hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED;
+
+               /* Oops... There is no fast way to ask mac80211 about
+                * IV/ICV lengths. Even defineas are not exposed.
+                */
+               switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+               case WSM_RX_STATUS_WEP:
+                       iv_len = 4 /* WEP_IV_LEN */;
+                       icv_len = 4 /* WEP_ICV_LEN */;
+                       break;
+               case WSM_RX_STATUS_TKIP:
+                       iv_len = 8 /* TKIP_IV_LEN */;
+                       icv_len = 4 /* TKIP_ICV_LEN */
+                               + 8 /*MICHAEL_MIC_LEN*/;
+                       hdr->flag |= RX_FLAG_MMIC_STRIPPED;
+                       break;
+               case WSM_RX_STATUS_AES:
+                       iv_len = 8 /* CCMP_HDR_LEN */;
+                       icv_len = 8 /* CCMP_MIC_LEN */;
+                       break;
+               case WSM_RX_STATUS_WAPI:
+                       iv_len = 18 /* WAPI_HDR_LEN */;
+                       icv_len = 16 /* WAPI_MIC_LEN */;
+                       break;
+               default:
+                       pr_warn("Unknown encryption type %d\n",
+                               WSM_RX_STATUS_ENCRYPTION(arg->flags));
+                       goto drop;
+               }
+
+               /* Firmware strips ICV in case of MIC failure. */
+               if (arg->status == WSM_STATUS_MICFAILURE)
+                       icv_len = 0;
+
+               if (skb->len < hdrlen + iv_len + icv_len) {
+                       wiphy_warn(priv->hw->wiphy, "Malformed SDU rx'ed. Size is lesser than crypto headers.\n");
+                       goto drop;
+               }
+
+               /* Remove IV, ICV and MIC */
+               skb_trim(skb, skb->len - icv_len);
+               memmove(skb->data + iv_len, skb->data, hdrlen);
+               skb_pull(skb, iv_len);
+       }
+
+       /* Remove TSF from the end of frame */
+       if (arg->flags & WSM_RX_STATUS_TSF_INCLUDED) {
+               memcpy(&hdr->mactime, skb->data + skb->len - 8, 8);
+               hdr->mactime = le64_to_cpu(hdr->mactime);
+               if (skb->len >= 8)
+                       skb_trim(skb, skb->len - 8);
+       } else {
+               hdr->mactime = 0;
+       }
+
+       cw1200_debug_rxed(priv);
+       if (arg->flags & WSM_RX_STATUS_AGGREGATE)
+               cw1200_debug_rxed_agg(priv);
+
+       if (ieee80211_is_action(frame->frame_control) &&
+           (arg->flags & WSM_RX_STATUS_ADDRESS1)) {
+               if (cw1200_handle_action_rx(priv, skb))
+                       return;
+       } else if (ieee80211_is_beacon(frame->frame_control) &&
+                  !arg->status &&
+                  !memcmp(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid,
+                          ETH_ALEN)) {
+               const u8 *tim_ie;
+               u8 *ies = ((struct ieee80211_mgmt *)
+                         (skb->data))->u.beacon.variable;
+               size_t ies_len = skb->len - (ies - (u8 *)(skb->data));
+
+               tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len);
+               if (tim_ie) {
+                       struct ieee80211_tim_ie *tim =
+                               (struct ieee80211_tim_ie *)&tim_ie[2];
+
+                       if (priv->join_dtim_period != tim->dtim_period) {
+                               priv->join_dtim_period = tim->dtim_period;
+                               queue_work(priv->workqueue,
+                                          &priv->set_beacon_wakeup_period_work);
+                       }
+               }
+
+               /* Disable beacon filter once we're associated... */
+               if (priv->disable_beacon_filter &&
+                   (priv->vif->bss_conf.assoc ||
+                    priv->vif->bss_conf.ibss_joined)) {
+                       priv->disable_beacon_filter = false;
+                       queue_work(priv->workqueue,
+                                  &priv->update_filtering_work);
+               }
+       }
+
+       /* Stay awake after frame is received to give
+        * userspace chance to react and acquire appropriate
+        * wakelock.
+        */
+       if (ieee80211_is_auth(frame->frame_control))
+               grace_period = 5 * HZ;
+       else if (ieee80211_is_deauth(frame->frame_control))
+               grace_period = 5 * HZ;
+       else
+               grace_period = 1 * HZ;
+       cw1200_pm_stay_awake(&priv->pm_state, grace_period);
+
+       if (early_data) {
+               spin_lock_bh(&priv->ps_state_lock);
+               /* Double-check status with lock held */
+               if (entry->status == CW1200_LINK_SOFT)
+                       skb_queue_tail(&entry->rx_queue, skb);
+               else
+                       ieee80211_rx_irqsafe(priv->hw, skb);
+               spin_unlock_bh(&priv->ps_state_lock);
+       } else {
+               ieee80211_rx_irqsafe(priv->hw, skb);
+       }
+       *skb_p = NULL;
+
+       return;
+
+drop:
+       /* TODO: update failure counters */
+       return;
+}
+
+/* ******************************************************************** */
+/* Security                                                            */
+
+int cw1200_alloc_key(struct cw1200_common *priv)
+{
+       int idx;
+
+       idx = ffs(~priv->key_map) - 1;
+       if (idx < 0 || idx > WSM_KEY_MAX_INDEX)
+               return -1;
+
+       priv->key_map |= BIT(idx);
+       priv->keys[idx].index = idx;
+       return idx;
+}
+
+void cw1200_free_key(struct cw1200_common *priv, int idx)
+{
+       BUG_ON(!(priv->key_map & BIT(idx)));
+       memset(&priv->keys[idx], 0, sizeof(priv->keys[idx]));
+       priv->key_map &= ~BIT(idx);
+}
+
+void cw1200_free_keys(struct cw1200_common *priv)
+{
+       memset(&priv->keys, 0, sizeof(priv->keys));
+       priv->key_map = 0;
+}
+
+int cw1200_upload_keys(struct cw1200_common *priv)
+{
+       int idx, ret = 0;
+       for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx)
+               if (priv->key_map & BIT(idx)) {
+                       ret = wsm_add_key(priv, &priv->keys[idx]);
+                       if (ret < 0)
+                               break;
+               }
+       return ret;
+}
+
+/* Workaround for WFD test case 6.1.10 */
+void cw1200_link_id_reset(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, linkid_reset_work);
+       int temp_linkid;
+
+       if (!priv->action_linkid) {
+               /* In GO mode we can receive ACTION frames without a linkID */
+               temp_linkid = cw1200_alloc_link_id(priv,
+                               &priv->action_frame_sa[0]);
+               WARN_ON(!temp_linkid);
+               if (temp_linkid) {
+                       /* Make sure we execute the WQ */
+                       flush_workqueue(priv->workqueue);
+                       /* Release the link ID */
+                       spin_lock_bh(&priv->ps_state_lock);
+                       priv->link_id_db[temp_linkid - 1].prev_status =
+                               priv->link_id_db[temp_linkid - 1].status;
+                       priv->link_id_db[temp_linkid - 1].status =
+                               CW1200_LINK_RESET;
+                       spin_unlock_bh(&priv->ps_state_lock);
+                       wsm_lock_tx_async(priv);
+                       if (queue_work(priv->workqueue,
+                                      &priv->link_id_work) <= 0)
+                               wsm_unlock_tx(priv);
+               }
+       } else {
+               spin_lock_bh(&priv->ps_state_lock);
+               priv->link_id_db[priv->action_linkid - 1].prev_status =
+                       priv->link_id_db[priv->action_linkid - 1].status;
+               priv->link_id_db[priv->action_linkid - 1].status =
+                       CW1200_LINK_RESET_REMAP;
+               spin_unlock_bh(&priv->ps_state_lock);
+               wsm_lock_tx_async(priv);
+               if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+                       wsm_unlock_tx(priv);
+               flush_workqueue(priv->workqueue);
+       }
+}
+
+int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac)
+{
+       int i, ret = 0;
+       spin_lock_bh(&priv->ps_state_lock);
+       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+               if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) &&
+                   priv->link_id_db[i].status) {
+                       priv->link_id_db[i].timestamp = jiffies;
+                       ret = i + 1;
+                       break;
+               }
+       }
+       spin_unlock_bh(&priv->ps_state_lock);
+       return ret;
+}
+
+int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac)
+{
+       int i, ret = 0;
+       unsigned long max_inactivity = 0;
+       unsigned long now = jiffies;
+
+       spin_lock_bh(&priv->ps_state_lock);
+       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+               if (!priv->link_id_db[i].status) {
+                       ret = i + 1;
+                       break;
+               } else if (priv->link_id_db[i].status != CW1200_LINK_HARD &&
+                          !priv->tx_queue_stats.link_map_cache[i + 1]) {
+                       unsigned long inactivity =
+                               now - priv->link_id_db[i].timestamp;
+                       if (inactivity < max_inactivity)
+                               continue;
+                       max_inactivity = inactivity;
+                       ret = i + 1;
+               }
+       }
+       if (ret) {
+               struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1];
+               pr_debug("[AP] STA added, link_id: %d\n", ret);
+               entry->status = CW1200_LINK_RESERVE;
+               memcpy(&entry->mac, mac, ETH_ALEN);
+               memset(&entry->buffered, 0, CW1200_MAX_TID);
+               skb_queue_head_init(&entry->rx_queue);
+               wsm_lock_tx_async(priv);
+               if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+                       wsm_unlock_tx(priv);
+       } else {
+               wiphy_info(priv->hw->wiphy,
+                          "[AP] Early: no more link IDs available.\n");
+       }
+
+       spin_unlock_bh(&priv->ps_state_lock);
+       return ret;
+}
+
+void cw1200_link_id_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, link_id_work);
+       wsm_flush_tx(priv);
+       cw1200_link_id_gc_work(&priv->link_id_gc_work.work);
+       wsm_unlock_tx(priv);
+}
+
+void cw1200_link_id_gc_work(struct work_struct *work)
+{
+       struct cw1200_common *priv =
+               container_of(work, struct cw1200_common, link_id_gc_work.work);
+       struct wsm_reset reset = {
+               .reset_statistics = false,
+       };
+       struct wsm_map_link map_link = {
+               .link_id = 0,
+       };
+       unsigned long now = jiffies;
+       unsigned long next_gc = -1;
+       long ttl;
+       bool need_reset;
+       u32 mask;
+       int i;
+
+       if (priv->join_status != CW1200_JOIN_STATUS_AP)
+               return;
+
+       wsm_lock_tx(priv);
+       spin_lock_bh(&priv->ps_state_lock);
+       for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+               need_reset = false;
+               mask = BIT(i + 1);
+               if (priv->link_id_db[i].status == CW1200_LINK_RESERVE ||
+                   (priv->link_id_db[i].status == CW1200_LINK_HARD &&
+                    !(priv->link_id_map & mask))) {
+                       if (priv->link_id_map & mask) {
+                               priv->sta_asleep_mask &= ~mask;
+                               priv->pspoll_mask &= ~mask;
+                               need_reset = true;
+                       }
+                       priv->link_id_map |= mask;
+                       if (priv->link_id_db[i].status != CW1200_LINK_HARD)
+                               priv->link_id_db[i].status = CW1200_LINK_SOFT;
+                       memcpy(map_link.mac_addr, priv->link_id_db[i].mac,
+                              ETH_ALEN);
+                       spin_unlock_bh(&priv->ps_state_lock);
+                       if (need_reset) {
+                               reset.link_id = i + 1;
+                               wsm_reset(priv, &reset);
+                       }
+                       map_link.link_id = i + 1;
+                       wsm_map_link(priv, &map_link);
+                       next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT);
+                       spin_lock_bh(&priv->ps_state_lock);
+               } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) {
+                       ttl = priv->link_id_db[i].timestamp - now +
+                                       CW1200_LINK_ID_GC_TIMEOUT;
+                       if (ttl <= 0) {
+                               need_reset = true;
+                               priv->link_id_db[i].status = CW1200_LINK_OFF;
+                               priv->link_id_map &= ~mask;
+                               priv->sta_asleep_mask &= ~mask;
+                               priv->pspoll_mask &= ~mask;
+                               memset(map_link.mac_addr, 0, ETH_ALEN);
+                               spin_unlock_bh(&priv->ps_state_lock);
+                               reset.link_id = i + 1;
+                               wsm_reset(priv, &reset);
+                               spin_lock_bh(&priv->ps_state_lock);
+                       } else {
+                               next_gc = min_t(unsigned long, next_gc, ttl);
+                       }
+               } else if (priv->link_id_db[i].status == CW1200_LINK_RESET ||
+                               priv->link_id_db[i].status ==
+                               CW1200_LINK_RESET_REMAP) {
+                       int status = priv->link_id_db[i].status;
+                       priv->link_id_db[i].status =
+                                       priv->link_id_db[i].prev_status;
+                       priv->link_id_db[i].timestamp = now;
+                       reset.link_id = i + 1;
+                       spin_unlock_bh(&priv->ps_state_lock);
+                       wsm_reset(priv, &reset);
+                       if (status == CW1200_LINK_RESET_REMAP) {
+                               memcpy(map_link.mac_addr,
+                                      priv->link_id_db[i].mac,
+                                      ETH_ALEN);
+                               map_link.link_id = i + 1;
+                               wsm_map_link(priv, &map_link);
+                               next_gc = min(next_gc,
+                                               CW1200_LINK_ID_GC_TIMEOUT);
+                       }
+                       spin_lock_bh(&priv->ps_state_lock);
+               }
+               if (need_reset) {
+                       skb_queue_purge(&priv->link_id_db[i].rx_queue);
+                       pr_debug("[AP] STA removed, link_id: %d\n",
+                                reset.link_id);
+               }
+       }
+       spin_unlock_bh(&priv->ps_state_lock);
+       if (next_gc != -1)
+               queue_delayed_work(priv->workqueue,
+                                  &priv->link_id_gc_work, next_gc);
+       wsm_unlock_tx(priv);
+}
diff --git a/drivers/net/wireless/cw1200/txrx.h b/drivers/net/wireless/cw1200/txrx.h
new file mode 100644 (file)
index 0000000..492a4e1
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Datapath interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 CW1200_TXRX_H
+#define CW1200_TXRX_H
+
+#include <linux/list.h>
+
+/* extern */ struct ieee80211_hw;
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct wsm_rx;
+/* extern */ struct wsm_tx_confirm;
+/* extern */ struct cw1200_txpriv;
+
+struct tx_policy {
+       union {
+               __le32 tbl[3];
+               u8 raw[12];
+       };
+       u8  defined;
+       u8  usage_count;
+       u8  retry_count;
+       u8  uploaded;
+};
+
+struct tx_policy_cache_entry {
+       struct tx_policy policy;
+       struct list_head link;
+};
+
+#define TX_POLICY_CACHE_SIZE   (8)
+struct tx_policy_cache {
+       struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE];
+       struct list_head used;
+       struct list_head free;
+       spinlock_t lock; /* Protect policy cache */
+};
+
+/* ******************************************************************** */
+/* TX policy cache                                                     */
+/* Intention of TX policy cache is an overcomplicated WSM API.
+ * Device does not accept per-PDU tx retry sequence.
+ * It uses "tx retry policy id" instead, so driver code has to sync
+ * linux tx retry sequences with a retry policy table in the device.
+ */
+void tx_policy_init(struct cw1200_common *priv);
+void tx_policy_upload_work(struct work_struct *work);
+void tx_policy_clean(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* TX implementation                                                   */
+
+u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv,
+                              u32 rates);
+void cw1200_tx(struct ieee80211_hw *dev,
+              struct ieee80211_tx_control *control,
+              struct sk_buff *skb);
+void cw1200_skb_dtor(struct cw1200_common *priv,
+                    struct sk_buff *skb,
+                    const struct cw1200_txpriv *txpriv);
+
+/* ******************************************************************** */
+/* WSM callbacks                                                       */
+
+void cw1200_tx_confirm_cb(struct cw1200_common *priv,
+                         int link_id,
+                         struct wsm_tx_confirm *arg);
+void cw1200_rx_cb(struct cw1200_common *priv,
+                 struct wsm_rx *arg,
+                 int link_id,
+                 struct sk_buff **skb_p);
+
+/* ******************************************************************** */
+/* Timeout                                                             */
+
+void cw1200_tx_timeout(struct work_struct *work);
+
+/* ******************************************************************** */
+/* Security                                                            */
+int cw1200_alloc_key(struct cw1200_common *priv);
+void cw1200_free_key(struct cw1200_common *priv, int idx);
+void cw1200_free_keys(struct cw1200_common *priv);
+int cw1200_upload_keys(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* Workaround for WFD test case 6.1.10                                 */
+void cw1200_link_id_reset(struct work_struct *work);
+
+#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
+
+int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac);
+int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac);
+void cw1200_link_id_work(struct work_struct *work);
+void cw1200_link_id_gc_work(struct work_struct *work);
+
+
+#endif /* CW1200_TXRX_H */
diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c
new file mode 100644 (file)
index 0000000..cbb74d7
--- /dev/null
@@ -0,0 +1,1822 @@
+/*
+ * WSM host interface (HI) implementation for
+ * ST-Ericsson CW1200 mac80211 drivers.
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/skbuff.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+
+#include "cw1200.h"
+#include "wsm.h"
+#include "bh.h"
+#include "sta.h"
+#include "debug.h"
+
+#define WSM_CMD_TIMEOUT                (2 * HZ) /* With respect to interrupt loss */
+#define WSM_CMD_START_TIMEOUT  (7 * HZ)
+#define WSM_CMD_RESET_TIMEOUT  (3 * HZ) /* 2 sec. timeout was observed.   */
+#define WSM_CMD_MAX_TIMEOUT    (3 * HZ)
+
+#define WSM_SKIP(buf, size)                                            \
+       do {                                                            \
+               if ((buf)->data + size > (buf)->end)                    \
+                       goto underflow;                                 \
+               (buf)->data += size;                                    \
+       } while (0)
+
+#define WSM_GET(buf, ptr, size)                                                \
+       do {                                                            \
+               if ((buf)->data + size > (buf)->end)                    \
+                       goto underflow;                                 \
+               memcpy(ptr, (buf)->data, size);                         \
+               (buf)->data += size;                                    \
+       } while (0)
+
+#define __WSM_GET(buf, type, type2, cvt)                               \
+       ({                                                              \
+               type val;                                               \
+               if ((buf)->data + sizeof(type) > (buf)->end)            \
+                       goto underflow;                                 \
+               val = cvt(*(type2 *)(buf)->data);                       \
+               (buf)->data += sizeof(type);                            \
+               val;                                                    \
+       })
+
+#define WSM_GET8(buf)  __WSM_GET(buf, u8, u8, (u8))
+#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16, __le16_to_cpu)
+#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32, __le32_to_cpu)
+
+#define WSM_PUT(buf, ptr, size)                                                \
+       do {                                                            \
+               if ((buf)->data + size > (buf)->end)            \
+                       if (wsm_buf_reserve((buf), size))       \
+                               goto nomem;                             \
+               memcpy((buf)->data, ptr, size);                         \
+               (buf)->data += size;                                    \
+       } while (0)
+
+#define __WSM_PUT(buf, val, type, type2, cvt)                          \
+       do {                                                            \
+               if ((buf)->data + sizeof(type) > (buf)->end)            \
+                       if (wsm_buf_reserve((buf), sizeof(type))) \
+                               goto nomem;                             \
+               *(type2 *)(buf)->data = cvt(val);                       \
+               (buf)->data += sizeof(type);                            \
+       } while (0)
+
+#define WSM_PUT8(buf, val)  __WSM_PUT(buf, val, u8, u8, (u8))
+#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __le16, __cpu_to_le16)
+#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __le32, __cpu_to_le32)
+
+static void wsm_buf_reset(struct wsm_buf *buf);
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size);
+
+static int wsm_cmd_send(struct cw1200_common *priv,
+                       struct wsm_buf *buf,
+                       void *arg, u16 cmd, long tmo);
+
+#define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux))
+#define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux))
+
+/* ******************************************************************** */
+/* WSM API implementation                                              */
+
+static int wsm_generic_confirm(struct cw1200_common *priv,
+                            void *arg,
+                            struct wsm_buf *buf)
+{
+       u32 status = WSM_GET32(buf);
+       if (status != WSM_STATUS_SUCCESS)
+               return -EINVAL;
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime);
+       WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime);
+       WSM_PUT32(buf, arg->dot11RtsThreshold);
+
+       /* DPD block. */
+       WSM_PUT16(buf, arg->dpdData_size + 12);
+       WSM_PUT16(buf, 1); /* DPD version */
+       WSM_PUT(buf, arg->dot11StationId, ETH_ALEN);
+       WSM_PUT16(buf, 5); /* DPD flags */
+       WSM_PUT(buf, arg->dpdData, arg->dpdData_size);
+
+       ret = wsm_cmd_send(priv, buf, arg,
+                          WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+static int wsm_configuration_confirm(struct cw1200_common *priv,
+                                    struct wsm_configuration *arg,
+                                    struct wsm_buf *buf)
+{
+       int i;
+       int status;
+
+       status = WSM_GET32(buf);
+       if (WARN_ON(status != WSM_STATUS_SUCCESS))
+               return -EINVAL;
+
+       WSM_GET(buf, arg->dot11StationId, ETH_ALEN);
+       arg->dot11FrequencyBandsSupported = WSM_GET8(buf);
+       WSM_SKIP(buf, 1);
+       arg->supportedRateMask = WSM_GET32(buf);
+       for (i = 0; i < 2; ++i) {
+               arg->txPowerRange[i].min_power_level = WSM_GET32(buf);
+               arg->txPowerRange[i].max_power_level = WSM_GET32(buf);
+               arg->txPowerRange[i].stepping = WSM_GET32(buf);
+       }
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id);
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT32(buf, arg->reset_statistics ? 0 : 1);
+       ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+struct wsm_mib {
+       u16 mib_id;
+       void *buf;
+       size_t buf_size;
+};
+
+int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
+                       size_t buf_size)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       struct wsm_mib mib_buf = {
+               .mib_id = mib_id,
+               .buf = _buf,
+               .buf_size = buf_size,
+       };
+       wsm_cmd_lock(priv);
+
+       WSM_PUT16(buf, mib_id);
+       WSM_PUT16(buf, 0);
+
+       ret = wsm_cmd_send(priv, buf, &mib_buf,
+                          WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+static int wsm_read_mib_confirm(struct cw1200_common *priv,
+                               struct wsm_mib *arg,
+                               struct wsm_buf *buf)
+{
+       u16 size;
+       if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS))
+               return -EINVAL;
+
+       if (WARN_ON(WSM_GET16(buf) != arg->mib_id))
+               return -EINVAL;
+
+       size = WSM_GET16(buf);
+       if (size > arg->buf_size)
+               size = arg->buf_size;
+
+       WSM_GET(buf, arg->buf, size);
+       arg->buf_size = size;
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
+                       size_t buf_size)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       struct wsm_mib mib_buf = {
+               .mib_id = mib_id,
+               .buf = _buf,
+               .buf_size = buf_size,
+       };
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT16(buf, mib_id);
+       WSM_PUT16(buf, buf_size);
+       WSM_PUT(buf, _buf, buf_size);
+
+       ret = wsm_cmd_send(priv, buf, &mib_buf,
+                          WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+static int wsm_write_mib_confirm(struct cw1200_common *priv,
+                               struct wsm_mib *arg,
+                               struct wsm_buf *buf)
+{
+       int ret;
+
+       ret = wsm_generic_confirm(priv, arg, buf);
+       if (ret)
+               return ret;
+
+       if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) {
+               /* OperationalMode: update PM status. */
+               const char *p = arg->buf;
+               cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false);
+       }
+       return 0;
+}
+
+/* ******************************************************************** */
+
+int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg)
+{
+       int i;
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       if (arg->num_channels > 48)
+               return -EINVAL;
+
+       if (arg->num_ssids > 2)
+               return -EINVAL;
+
+       if (arg->band > 1)
+               return -EINVAL;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->band);
+       WSM_PUT8(buf, arg->type);
+       WSM_PUT8(buf, arg->flags);
+       WSM_PUT8(buf, arg->max_tx_rate);
+       WSM_PUT32(buf, arg->auto_scan_interval);
+       WSM_PUT8(buf, arg->num_probes);
+       WSM_PUT8(buf, arg->num_channels);
+       WSM_PUT8(buf, arg->num_ssids);
+       WSM_PUT8(buf, arg->probe_delay);
+
+       for (i = 0; i < arg->num_channels; ++i) {
+               WSM_PUT16(buf, arg->ch[i].number);
+               WSM_PUT16(buf, 0);
+               WSM_PUT32(buf, arg->ch[i].min_chan_time);
+               WSM_PUT32(buf, arg->ch[i].max_chan_time);
+               WSM_PUT32(buf, 0);
+       }
+
+       for (i = 0; i < arg->num_ssids; ++i) {
+               WSM_PUT32(buf, arg->ssids[i].length);
+               WSM_PUT(buf, &arg->ssids[i].ssid[0],
+                       sizeof(arg->ssids[i].ssid));
+       }
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_scan(struct cw1200_common *priv)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       wsm_cmd_lock(priv);
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+}
+
+
+static int wsm_tx_confirm(struct cw1200_common *priv,
+                         struct wsm_buf *buf,
+                         int link_id)
+{
+       struct wsm_tx_confirm tx_confirm;
+
+       tx_confirm.packet_id = WSM_GET32(buf);
+       tx_confirm.status = WSM_GET32(buf);
+       tx_confirm.tx_rate = WSM_GET8(buf);
+       tx_confirm.ack_failures = WSM_GET8(buf);
+       tx_confirm.flags = WSM_GET16(buf);
+       tx_confirm.media_delay = WSM_GET32(buf);
+       tx_confirm.tx_queue_delay = WSM_GET32(buf);
+
+       cw1200_tx_confirm_cb(priv, link_id, &tx_confirm);
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+static int wsm_multi_tx_confirm(struct cw1200_common *priv,
+                               struct wsm_buf *buf, int link_id)
+{
+       int ret;
+       int count;
+       int i;
+
+       count = WSM_GET32(buf);
+       if (WARN_ON(count <= 0))
+               return -EINVAL;
+
+       if (count > 1) {
+               /* We already released one buffer, now for the rest */
+               ret = wsm_release_tx_buffer(priv, count - 1);
+               if (ret < 0)
+                       return ret;
+               else if (ret > 0)
+                       cw1200_bh_wakeup(priv);
+       }
+
+       cw1200_debug_txed_multi(priv, count);
+       for (i = 0; i < count; ++i) {
+               ret = wsm_tx_confirm(priv, buf, link_id);
+               if (ret)
+                       return ret;
+       }
+       return ret;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+static int wsm_join_confirm(struct cw1200_common *priv,
+                           struct wsm_join_cnf *arg,
+                           struct wsm_buf *buf)
+{
+       arg->status = WSM_GET32(buf);
+       if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS)
+               return -EINVAL;
+
+       arg->min_power_level = WSM_GET32(buf);
+       arg->max_power_level = WSM_GET32(buf);
+
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+int wsm_join(struct cw1200_common *priv, struct wsm_join *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       struct wsm_join_cnf resp;
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->mode);
+       WSM_PUT8(buf, arg->band);
+       WSM_PUT16(buf, arg->channel_number);
+       WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid));
+       WSM_PUT16(buf, arg->atim_window);
+       WSM_PUT8(buf, arg->preamble_type);
+       WSM_PUT8(buf, arg->probe_for_join);
+       WSM_PUT8(buf, arg->dtim_period);
+       WSM_PUT8(buf, arg->flags);
+       WSM_PUT32(buf, arg->ssid_len);
+       WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid));
+       WSM_PUT32(buf, arg->beacon_interval);
+       WSM_PUT32(buf, arg->basic_rate_set);
+
+       priv->tx_burst_idx = -1;
+       ret = wsm_cmd_send(priv, buf, &resp,
+                          WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT);
+       /* TODO:  Update state based on resp.min|max_power_level */
+
+       priv->join_complete_status = resp.status;
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_bss_params(struct cw1200_common *priv,
+                      const struct wsm_set_bss_params *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, (arg->reset_beacon_loss ?  0x1 : 0));
+       WSM_PUT8(buf, arg->beacon_lost_count);
+       WSM_PUT16(buf, arg->aid);
+       WSM_PUT32(buf, arg->operational_rate_set);
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT(buf, arg, sizeof(*arg));
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->index);
+       WSM_PUT8(buf, 0);
+       WSM_PUT16(buf, 0);
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_tx_queue_params(struct cw1200_common *priv,
+               const struct wsm_set_tx_queue_params *arg, u8 id)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1};
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, queue_id_to_wmm_aci[id]);
+       WSM_PUT8(buf, 0);
+       WSM_PUT8(buf, arg->ackPolicy);
+       WSM_PUT8(buf, 0);
+       WSM_PUT32(buf, arg->maxTransmitLifetime);
+       WSM_PUT16(buf, arg->allowedMediumTime);
+       WSM_PUT16(buf, 0);
+
+       ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_edca_params(struct cw1200_common *priv,
+                               const struct wsm_edca_params *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       /* Implemented according to specification. */
+
+       WSM_PUT16(buf, arg->params[3].cwmin);
+       WSM_PUT16(buf, arg->params[2].cwmin);
+       WSM_PUT16(buf, arg->params[1].cwmin);
+       WSM_PUT16(buf, arg->params[0].cwmin);
+
+       WSM_PUT16(buf, arg->params[3].cwmax);
+       WSM_PUT16(buf, arg->params[2].cwmax);
+       WSM_PUT16(buf, arg->params[1].cwmax);
+       WSM_PUT16(buf, arg->params[0].cwmax);
+
+       WSM_PUT8(buf, arg->params[3].aifns);
+       WSM_PUT8(buf, arg->params[2].aifns);
+       WSM_PUT8(buf, arg->params[1].aifns);
+       WSM_PUT8(buf, arg->params[0].aifns);
+
+       WSM_PUT16(buf, arg->params[3].txop_limit);
+       WSM_PUT16(buf, arg->params[2].txop_limit);
+       WSM_PUT16(buf, arg->params[1].txop_limit);
+       WSM_PUT16(buf, arg->params[0].txop_limit);
+
+       WSM_PUT32(buf, arg->params[3].max_rx_lifetime);
+       WSM_PUT32(buf, arg->params[2].max_rx_lifetime);
+       WSM_PUT32(buf, arg->params[1].max_rx_lifetime);
+       WSM_PUT32(buf, arg->params[0].max_rx_lifetime);
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_switch_channel(struct cw1200_common *priv,
+                       const struct wsm_switch_channel *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->mode);
+       WSM_PUT8(buf, arg->switch_count);
+       WSM_PUT16(buf, arg->channel_number);
+
+       priv->channel_switch_in_progress = 1;
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT);
+       if (ret)
+               priv->channel_switch_in_progress = 0;
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       priv->ps_mode_switch_in_progress = 1;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->mode);
+       WSM_PUT8(buf, arg->fast_psm_idle_period);
+       WSM_PUT8(buf, arg->ap_psm_change_period);
+       WSM_PUT8(buf, arg->min_auto_pspoll_period);
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT8(buf, arg->mode);
+       WSM_PUT8(buf, arg->band);
+       WSM_PUT16(buf, arg->channel_number);
+       WSM_PUT32(buf, arg->ct_window);
+       WSM_PUT32(buf, arg->beacon_interval);
+       WSM_PUT8(buf, arg->dtim_period);
+       WSM_PUT8(buf, arg->preamble);
+       WSM_PUT8(buf, arg->probe_delay);
+       WSM_PUT8(buf, arg->ssid_len);
+       WSM_PUT(buf, arg->ssid, sizeof(arg->ssid));
+       WSM_PUT32(buf, arg->basic_rate_set);
+
+       priv->tx_burst_idx = -1;
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_beacon_transmit(struct cw1200_common *priv,
+                       const struct wsm_beacon_transmit *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0);
+
+       ret = wsm_cmd_send(priv, buf, NULL,
+                          WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_start_find(struct cw1200_common *priv)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+       ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_find(struct cw1200_common *priv)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+       ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT);
+       wsm_cmd_unlock(priv);
+       return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+       u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id);
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr));
+       WSM_PUT16(buf, 0);
+
+       ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_update_ie(struct cw1200_common *priv,
+                 const struct wsm_update_ie *arg)
+{
+       int ret;
+       struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+       wsm_cmd_lock(priv);
+
+       WSM_PUT16(buf, arg->what);
+       WSM_PUT16(buf, arg->count);
+       WSM_PUT(buf, arg->ies, arg->length);
+
+       ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT);
+
+       wsm_cmd_unlock(priv);
+       return ret;
+
+nomem:
+       wsm_cmd_unlock(priv);
+       return -ENOMEM;
+}
+
+/* ******************************************************************** */
+int wsm_set_probe_responder(struct cw1200_common *priv, bool enable)
+{
+       priv->rx_filter.probeResponder = enable;
+       return wsm_set_rx_filter(priv, &priv->rx_filter);
+}
+
+/* ******************************************************************** */
+/* WSM indication events implementation                                        */
+const char * const cw1200_fw_types[] = {
+       "ETF",
+       "WFM",
+       "WSM",
+       "HI test",
+       "Platform test"
+};
+
+static int wsm_startup_indication(struct cw1200_common *priv,
+                                       struct wsm_buf *buf)
+{
+       priv->wsm_caps.input_buffers     = WSM_GET16(buf);
+       priv->wsm_caps.input_buffer_size = WSM_GET16(buf);
+       priv->wsm_caps.hw_id      = WSM_GET16(buf);
+       priv->wsm_caps.hw_subid   = WSM_GET16(buf);
+       priv->wsm_caps.status     = WSM_GET16(buf);
+       priv->wsm_caps.fw_cap     = WSM_GET16(buf);
+       priv->wsm_caps.fw_type    = WSM_GET16(buf);
+       priv->wsm_caps.fw_api     = WSM_GET16(buf);
+       priv->wsm_caps.fw_build   = WSM_GET16(buf);
+       priv->wsm_caps.fw_ver     = WSM_GET16(buf);
+       WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label));
+       priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */
+
+       if (WARN_ON(priv->wsm_caps.status))
+               return -EINVAL;
+
+       if (WARN_ON(priv->wsm_caps.fw_type > 4))
+               return -EINVAL;
+
+       pr_info("CW1200 WSM init done.\n"
+               "   Input buffers: %d x %d bytes\n"
+               "   Hardware: %d.%d\n"
+               "   %s firmware [%s], ver: %d, build: %d,"
+               "   api: %d, cap: 0x%.4X\n",
+               priv->wsm_caps.input_buffers,
+               priv->wsm_caps.input_buffer_size,
+               priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid,
+               cw1200_fw_types[priv->wsm_caps.fw_type],
+               priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver,
+               priv->wsm_caps.fw_build,
+               priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap);
+
+       /* Disable unsupported frequency bands */
+       if (!(priv->wsm_caps.fw_cap & 0x1))
+               priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
+       if (!(priv->wsm_caps.fw_cap & 0x2))
+               priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
+
+       priv->firmware_ready = 1;
+       wake_up(&priv->wsm_startup_done);
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+static int wsm_receive_indication(struct cw1200_common *priv,
+                                 int link_id,
+                                 struct wsm_buf *buf,
+                                 struct sk_buff **skb_p)
+{
+       struct wsm_rx rx;
+       struct ieee80211_hdr *hdr;
+       size_t hdr_len;
+       __le16 fctl;
+
+       rx.status = WSM_GET32(buf);
+       rx.channel_number = WSM_GET16(buf);
+       rx.rx_rate = WSM_GET8(buf);
+       rx.rcpi_rssi = WSM_GET8(buf);
+       rx.flags = WSM_GET32(buf);
+
+       /* FW Workaround: Drop probe resp or
+          beacon when RSSI is 0
+       */
+       hdr = (struct ieee80211_hdr *)(*skb_p)->data;
+
+       if (!rx.rcpi_rssi &&
+           (ieee80211_is_probe_resp(hdr->frame_control) ||
+            ieee80211_is_beacon(hdr->frame_control)))
+               return 0;
+
+       /* If no RSSI subscription has been made,
+        * convert RCPI to RSSI here
+        */
+       if (!priv->cqm_use_rssi)
+               rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110;
+
+       fctl = *(__le16 *)buf->data;
+       hdr_len = buf->data - buf->begin;
+       skb_pull(*skb_p, hdr_len);
+       if (!rx.status && ieee80211_is_deauth(fctl)) {
+               if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+                       /* Shedule unjoin work */
+                       pr_debug("[WSM] Issue unjoin command (RX).\n");
+                       wsm_lock_tx_async(priv);
+                       if (queue_work(priv->workqueue,
+                                      &priv->unjoin_work) <= 0)
+                               wsm_unlock_tx(priv);
+               }
+       }
+       cw1200_rx_cb(priv, &rx, link_id, skb_p);
+       if (*skb_p)
+               skb_push(*skb_p, hdr_len);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf)
+{
+       int first;
+       struct cw1200_wsm_event *event;
+
+       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+               /* STA is stopped. */
+               return 0;
+       }
+
+       event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL);
+       if (!event)
+               return -ENOMEM;
+
+       event->evt.id = WSM_GET32(buf);
+       event->evt.data = WSM_GET32(buf);
+
+       pr_debug("[WSM] Event: %d(%d)\n",
+                event->evt.id, event->evt.data);
+
+       spin_lock(&priv->event_queue_lock);
+       first = list_empty(&priv->event_queue);
+       list_add_tail(&event->link, &priv->event_queue);
+       spin_unlock(&priv->event_queue_lock);
+
+       if (first)
+               queue_work(priv->workqueue, &priv->event_handler);
+
+       return 0;
+
+underflow:
+       kfree(event);
+       return -EINVAL;
+}
+
+static int wsm_channel_switch_indication(struct cw1200_common *priv,
+                                        struct wsm_buf *buf)
+{
+       WARN_ON(WSM_GET32(buf));
+
+       priv->channel_switch_in_progress = 0;
+       wake_up(&priv->channel_switch_done);
+
+       wsm_unlock_tx(priv);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+static int wsm_set_pm_indication(struct cw1200_common *priv,
+                                struct wsm_buf *buf)
+{
+       /* TODO:  Check buf (struct wsm_set_pm_complete) for validity */
+       if (priv->ps_mode_switch_in_progress) {
+               priv->ps_mode_switch_in_progress = 0;
+               wake_up(&priv->ps_mode_switch_done);
+       }
+       return 0;
+}
+
+static int wsm_scan_started(struct cw1200_common *priv, void *arg,
+                           struct wsm_buf *buf)
+{
+       u32 status = WSM_GET32(buf);
+       if (status != WSM_STATUS_SUCCESS) {
+               cw1200_scan_failed_cb(priv);
+               return -EINVAL;
+       }
+       return 0;
+
+underflow:
+       WARN_ON(1);
+       return -EINVAL;
+}
+
+static int wsm_scan_complete_indication(struct cw1200_common *priv,
+                                       struct wsm_buf *buf)
+{
+       struct wsm_scan_complete arg;
+       arg.status = WSM_GET32(buf);
+       arg.psm = WSM_GET8(buf);
+       arg.num_channels = WSM_GET8(buf);
+       cw1200_scan_complete_cb(priv, &arg);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+static int wsm_join_complete_indication(struct cw1200_common *priv,
+                                       struct wsm_buf *buf)
+{
+       struct wsm_join_complete arg;
+       arg.status = WSM_GET32(buf);
+       pr_debug("[WSM] Join complete indication, status: %d\n", arg.status);
+       cw1200_join_complete_cb(priv, &arg);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+static int wsm_find_complete_indication(struct cw1200_common *priv,
+                                       struct wsm_buf *buf)
+{
+       pr_warn("Implement find_complete_indication\n");
+       return 0;
+}
+
+static int wsm_ba_timeout_indication(struct cw1200_common *priv,
+                                    struct wsm_buf *buf)
+{
+       u32 dummy;
+       u8 tid;
+       u8 dummy2;
+       u8 addr[ETH_ALEN];
+
+       dummy = WSM_GET32(buf);
+       tid = WSM_GET8(buf);
+       dummy2 = WSM_GET8(buf);
+       WSM_GET(buf, addr, ETH_ALEN);
+
+       pr_info("BlockACK timeout, tid %d, addr %pM\n",
+               tid, addr);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+static int wsm_suspend_resume_indication(struct cw1200_common *priv,
+                                        int link_id, struct wsm_buf *buf)
+{
+       u32 flags;
+       struct wsm_suspend_resume arg;
+
+       flags = WSM_GET32(buf);
+       arg.link_id = link_id;
+       arg.stop = !(flags & 1);
+       arg.multicast = !!(flags & 8);
+       arg.queue = (flags >> 1) & 3;
+
+       cw1200_suspend_resume(priv, &arg);
+
+       return 0;
+
+underflow:
+       return -EINVAL;
+}
+
+
+/* ******************************************************************** */
+/* WSM TX                                                              */
+
+static int wsm_cmd_send(struct cw1200_common *priv,
+                       struct wsm_buf *buf,
+                       void *arg, u16 cmd, long tmo)
+{
+       size_t buf_len = buf->data - buf->begin;
+       int ret;
+
+       /* Don't bother if we're dead. */
+       if (priv->bh_error) {
+               ret = 0;
+               goto done;
+       }
+
+       /* Block until the cmd buffer is completed.  Tortuous. */
+       spin_lock(&priv->wsm_cmd.lock);
+       while (!priv->wsm_cmd.done) {
+               spin_unlock(&priv->wsm_cmd.lock);
+               spin_lock(&priv->wsm_cmd.lock);
+       }
+       priv->wsm_cmd.done = 0;
+       spin_unlock(&priv->wsm_cmd.lock);
+
+       if (cmd == WSM_WRITE_MIB_REQ_ID ||
+           cmd == WSM_READ_MIB_REQ_ID)
+               pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n",
+                        cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]),
+                        buf_len);
+       else
+               pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len);
+
+       /* Due to buggy SPI on CW1200, we need to
+        * pad the message by a few bytes to ensure
+        * that it's completely received.
+        */
+       buf_len += 4;
+
+       /* Fill HI message header */
+       /* BH will add sequence number */
+       ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len);
+       ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd);
+
+       spin_lock(&priv->wsm_cmd.lock);
+       BUG_ON(priv->wsm_cmd.ptr);
+       priv->wsm_cmd.ptr = buf->begin;
+       priv->wsm_cmd.len = buf_len;
+       priv->wsm_cmd.arg = arg;
+       priv->wsm_cmd.cmd = cmd;
+       spin_unlock(&priv->wsm_cmd.lock);
+
+       cw1200_bh_wakeup(priv);
+
+       /* Wait for command completion */
+       ret = wait_event_timeout(priv->wsm_cmd_wq,
+                                priv->wsm_cmd.done, tmo);
+
+       if (!ret && !priv->wsm_cmd.done) {
+               spin_lock(&priv->wsm_cmd.lock);
+               priv->wsm_cmd.done = 1;
+               priv->wsm_cmd.ptr = NULL;
+               spin_unlock(&priv->wsm_cmd.lock);
+               if (priv->bh_error) {
+                       /* Return ok to help system cleanup */
+                       ret = 0;
+               } else {
+                       pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd);
+                       print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE,
+                                            buf->begin, buf_len);
+                       pr_err("Outstanding outgoing frames:  %d\n", priv->hw_bufs_used);
+
+                       /* Kill BH thread to report the error to the top layer. */
+                       atomic_add(1, &priv->bh_term);
+                       wake_up(&priv->bh_wq);
+                       ret = -ETIMEDOUT;
+               }
+       } else {
+               spin_lock(&priv->wsm_cmd.lock);
+               BUG_ON(!priv->wsm_cmd.done);
+               ret = priv->wsm_cmd.ret;
+               spin_unlock(&priv->wsm_cmd.lock);
+       }
+done:
+       wsm_buf_reset(buf);
+       return ret;
+}
+
+/* ******************************************************************** */
+/* WSM TX port control                                                 */
+
+void wsm_lock_tx(struct cw1200_common *priv)
+{
+       wsm_cmd_lock(priv);
+       if (atomic_add_return(1, &priv->tx_lock) == 1) {
+               if (wsm_flush_tx(priv))
+                       pr_debug("[WSM] TX is locked.\n");
+       }
+       wsm_cmd_unlock(priv);
+}
+
+void wsm_lock_tx_async(struct cw1200_common *priv)
+{
+       if (atomic_add_return(1, &priv->tx_lock) == 1)
+               pr_debug("[WSM] TX is locked (async).\n");
+}
+
+bool wsm_flush_tx(struct cw1200_common *priv)
+{
+       unsigned long timestamp = jiffies;
+       bool pending = false;
+       long timeout;
+       int i;
+
+       /* Flush must be called with TX lock held. */
+       BUG_ON(!atomic_read(&priv->tx_lock));
+
+       /* First check if we really need to do something.
+        * It is safe to use unprotected access, as hw_bufs_used
+        * can only decrements.
+        */
+       if (!priv->hw_bufs_used)
+               return true;
+
+       if (priv->bh_error) {
+               /* In case of failure do not wait for magic. */
+               pr_err("[WSM] Fatal error occured, will not flush TX.\n");
+               return false;
+       } else {
+               /* Get a timestamp of "oldest" frame */
+               for (i = 0; i < 4; ++i)
+                       pending |= cw1200_queue_get_xmit_timestamp(
+                                       &priv->tx_queue[i],
+                                       &timestamp, 0xffffffff);
+               /* If there's nothing pending, we're good */
+               if (!pending)
+                       return true;
+
+               timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies;
+               if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq,
+                                                     !priv->hw_bufs_used,
+                                                     timeout) <= 0) {
+                       /* Hmmm... Not good. Frame had stuck in firmware. */
+                       priv->bh_error = 1;
+                       wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used);
+                       wake_up(&priv->bh_wq);
+                       return false;
+               }
+
+               /* Ok, everything is flushed. */
+               return true;
+       }
+}
+
+void wsm_unlock_tx(struct cw1200_common *priv)
+{
+       int tx_lock;
+       tx_lock = atomic_sub_return(1, &priv->tx_lock);
+       BUG_ON(tx_lock < 0);
+
+       if (tx_lock == 0) {
+               if (!priv->bh_error)
+                       cw1200_bh_wakeup(priv);
+               pr_debug("[WSM] TX is unlocked.\n");
+       }
+}
+
+/* ******************************************************************** */
+/* WSM RX                                                              */
+
+int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len)
+{
+       struct wsm_buf buf;
+       u32 reason;
+       u32 reg[18];
+       char fname[48];
+       unsigned int i;
+
+       static const char * const reason_str[] = {
+               "undefined instruction",
+               "prefetch abort",
+               "data abort",
+               "unknown error",
+       };
+
+       buf.begin = buf.data = data;
+       buf.end = &buf.begin[len];
+
+       reason = WSM_GET32(&buf);
+       for (i = 0; i < ARRAY_SIZE(reg); ++i)
+               reg[i] = WSM_GET32(&buf);
+       WSM_GET(&buf, fname, sizeof(fname));
+
+       if (reason < 4)
+               wiphy_err(priv->hw->wiphy,
+                         "Firmware exception: %s.\n",
+                         reason_str[reason]);
+       else
+               wiphy_err(priv->hw->wiphy,
+                         "Firmware assert at %.*s, line %d\n",
+                         (int) sizeof(fname), fname, reg[1]);
+
+       for (i = 0; i < 12; i += 4)
+               wiphy_err(priv->hw->wiphy,
+                         "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n",
+                         i + 0, reg[i + 0], i + 1, reg[i + 1],
+                         i + 2, reg[i + 2], i + 3, reg[i + 3]);
+       wiphy_err(priv->hw->wiphy,
+                 "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n",
+                 reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]);
+       i += 4;
+       wiphy_err(priv->hw->wiphy,
+                 "CPSR: 0x%.8X, SPSR: 0x%.8X\n",
+                 reg[i + 0], reg[i + 1]);
+
+       print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE,
+                            fname, sizeof(fname));
+       return 0;
+
+underflow:
+       wiphy_err(priv->hw->wiphy, "Firmware exception.\n");
+       print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE,
+                            data, len);
+       return -EINVAL;
+}
+
+int wsm_handle_rx(struct cw1200_common *priv, u16 id,
+                 struct wsm_hdr *wsm, struct sk_buff **skb_p)
+{
+       int ret = 0;
+       struct wsm_buf wsm_buf;
+       int link_id = (id >> 6) & 0x0F;
+
+       /* Strip link id. */
+       id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+
+       wsm_buf.begin = (u8 *)&wsm[0];
+       wsm_buf.data = (u8 *)&wsm[1];
+       wsm_buf.end = &wsm_buf.begin[__le16_to_cpu(wsm->len)];
+
+       pr_debug("[WSM] <<< 0x%.4X (%td)\n", id,
+                wsm_buf.end - wsm_buf.begin);
+
+       if (id == WSM_TX_CONFIRM_IND_ID) {
+               ret = wsm_tx_confirm(priv, &wsm_buf, link_id);
+       } else if (id == WSM_MULTI_TX_CONFIRM_ID) {
+               ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id);
+       } else if (id & 0x0400) {
+               void *wsm_arg;
+               u16 wsm_cmd;
+
+               /* Do not trust FW too much. Protection against repeated
+                * response and race condition removal (see above).
+                */
+               spin_lock(&priv->wsm_cmd.lock);
+               wsm_arg = priv->wsm_cmd.arg;
+               wsm_cmd = priv->wsm_cmd.cmd &
+                               ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+               priv->wsm_cmd.cmd = 0xFFFF;
+               spin_unlock(&priv->wsm_cmd.lock);
+
+               if (WARN_ON((id & ~0x0400) != wsm_cmd)) {
+                       /* Note that any non-zero is a fatal retcode. */
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               /* Note that wsm_arg can be NULL in case of timeout in
+                * wsm_cmd_send().
+                */
+
+               switch (id) {
+               case WSM_READ_MIB_RESP_ID:
+                       if (wsm_arg)
+                               ret = wsm_read_mib_confirm(priv, wsm_arg,
+                                                               &wsm_buf);
+                       break;
+               case WSM_WRITE_MIB_RESP_ID:
+                       if (wsm_arg)
+                               ret = wsm_write_mib_confirm(priv, wsm_arg,
+                                                           &wsm_buf);
+                       break;
+               case WSM_START_SCAN_RESP_ID:
+                       if (wsm_arg)
+                               ret = wsm_scan_started(priv, wsm_arg, &wsm_buf);
+                       break;
+               case WSM_CONFIGURATION_RESP_ID:
+                       if (wsm_arg)
+                               ret = wsm_configuration_confirm(priv, wsm_arg,
+                                                               &wsm_buf);
+                       break;
+               case WSM_JOIN_RESP_ID:
+                       if (wsm_arg)
+                               ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf);
+                       break;
+               case WSM_STOP_SCAN_RESP_ID:
+               case WSM_RESET_RESP_ID:
+               case WSM_ADD_KEY_RESP_ID:
+               case WSM_REMOVE_KEY_RESP_ID:
+               case WSM_SET_PM_RESP_ID:
+               case WSM_SET_BSS_PARAMS_RESP_ID:
+               case 0x0412: /* set_tx_queue_params */
+               case WSM_EDCA_PARAMS_RESP_ID:
+               case WSM_SWITCH_CHANNEL_RESP_ID:
+               case WSM_START_RESP_ID:
+               case WSM_BEACON_TRANSMIT_RESP_ID:
+               case 0x0419: /* start_find */
+               case 0x041A: /* stop_find */
+               case 0x041B: /* update_ie */
+               case 0x041C: /* map_link */
+                       WARN_ON(wsm_arg != NULL);
+                       ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf);
+                       if (ret) {
+                               wiphy_warn(priv->hw->wiphy,
+                                          "wsm_generic_confirm failed for request 0x%04x.\n",
+                                          id & ~0x0400);
+
+                               /* often 0x407 and 0x410 occur, this means we're dead.. */
+                               if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) {
+                                       wsm_lock_tx(priv);
+                                       if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+                                               wsm_unlock_tx(priv);
+                               }
+                       }
+                       break;
+               default:
+                       wiphy_warn(priv->hw->wiphy,
+                                  "Unrecognized confirmation 0x%04x\n",
+                                  id & ~0x0400);
+               }
+
+               spin_lock(&priv->wsm_cmd.lock);
+               priv->wsm_cmd.ret = ret;
+               priv->wsm_cmd.done = 1;
+               spin_unlock(&priv->wsm_cmd.lock);
+
+               ret = 0; /* Error response from device should ne stop BH. */
+
+               wake_up(&priv->wsm_cmd_wq);
+       } else if (id & 0x0800) {
+               switch (id) {
+               case WSM_STARTUP_IND_ID:
+                       ret = wsm_startup_indication(priv, &wsm_buf);
+                       break;
+               case WSM_RECEIVE_IND_ID:
+                       ret = wsm_receive_indication(priv, link_id,
+                                                    &wsm_buf, skb_p);
+                       break;
+               case 0x0805:
+                       ret = wsm_event_indication(priv, &wsm_buf);
+                       break;
+               case WSM_SCAN_COMPLETE_IND_ID:
+                       ret = wsm_scan_complete_indication(priv, &wsm_buf);
+                       break;
+               case 0x0808:
+                       ret = wsm_ba_timeout_indication(priv, &wsm_buf);
+                       break;
+               case 0x0809:
+                       ret = wsm_set_pm_indication(priv, &wsm_buf);
+                       break;
+               case 0x080A:
+                       ret = wsm_channel_switch_indication(priv, &wsm_buf);
+                       break;
+               case 0x080B:
+                       ret = wsm_find_complete_indication(priv, &wsm_buf);
+                       break;
+               case 0x080C:
+                       ret = wsm_suspend_resume_indication(priv,
+                                       link_id, &wsm_buf);
+                       break;
+               case 0x080F:
+                       ret = wsm_join_complete_indication(priv, &wsm_buf);
+                       break;
+               default:
+                       pr_warn("Unrecognised WSM ID %04x\n", id);
+               }
+       } else {
+               WARN_ON(1);
+               ret = -EINVAL;
+       }
+out:
+       return ret;
+}
+
+static bool wsm_handle_tx_data(struct cw1200_common *priv,
+                              struct wsm_tx *wsm,
+                              const struct ieee80211_tx_info *tx_info,
+                              const struct cw1200_txpriv *txpriv,
+                              struct cw1200_queue *queue)
+{
+       bool handled = false;
+       const struct ieee80211_hdr *frame =
+               (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset];
+       __le16 fctl = frame->frame_control;
+       enum {
+               do_probe,
+               do_drop,
+               do_wep,
+               do_tx,
+       } action = do_tx;
+
+       switch (priv->mode) {
+       case NL80211_IFTYPE_STATION:
+               if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+                       action = do_tx;
+               else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA)
+                       action = do_drop;
+               break;
+       case NL80211_IFTYPE_AP:
+               if (!priv->join_status) {
+                       action = do_drop;
+               } else if (!(BIT(txpriv->raw_link_id) &
+                            (BIT(0) | priv->link_id_map))) {
+                       wiphy_warn(priv->hw->wiphy,
+                                  "A frame with expired link id is dropped.\n");
+                       action = do_drop;
+               }
+               if (cw1200_queue_get_generation(wsm->packet_id) >
+                               CW1200_MAX_REQUEUE_ATTEMPTS) {
+                       /* HACK!!! WSM324 firmware has tendency to requeue
+                        * multicast frames in a loop, causing performance
+                        * drop and high power consumption of the driver.
+                        * In this situation it is better just to drop
+                        * the problematic frame.
+                        */
+                       wiphy_warn(priv->hw->wiphy,
+                                  "Too many attempts to requeue a frame; dropped.\n");
+                       action = do_drop;
+               }
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               if (priv->join_status != CW1200_JOIN_STATUS_IBSS)
+                       action = do_drop;
+               break;
+       case NL80211_IFTYPE_MESH_POINT:
+               action = do_tx; /* TODO:  Test me! */
+               break;
+       case NL80211_IFTYPE_MONITOR:
+       default:
+               action = do_drop;
+               break;
+       }
+
+       if (action == do_tx) {
+               if (ieee80211_is_nullfunc(fctl)) {
+                       spin_lock(&priv->bss_loss_lock);
+                       if (priv->bss_loss_state) {
+                               priv->bss_loss_confirm_id = wsm->packet_id;
+                               wsm->queue_id = WSM_QUEUE_VOICE;
+                       }
+                       spin_unlock(&priv->bss_loss_lock);
+               } else if (ieee80211_is_probe_req(fctl)) {
+                       action = do_probe;
+               } else if (ieee80211_is_deauth(fctl) &&
+                          priv->mode != NL80211_IFTYPE_AP) {
+                       pr_debug("[WSM] Issue unjoin command due to tx deauth.\n");
+                       wsm_lock_tx_async(priv);
+                       if (queue_work(priv->workqueue,
+                                      &priv->unjoin_work) <= 0)
+                               wsm_unlock_tx(priv);
+               } else if (ieee80211_has_protected(fctl) &&
+                          tx_info->control.hw_key &&
+                          tx_info->control.hw_key->keyidx != priv->wep_default_key_id &&
+                          (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+                           tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+                       action = do_wep;
+               }
+       }
+
+       switch (action) {
+       case do_probe:
+               /* An interesting FW "feature". Device filters probe responses.
+                * The easiest way to get it back is to convert
+                * probe request into WSM start_scan command.
+                */
+               pr_debug("[WSM] Convert probe request to scan.\n");
+               wsm_lock_tx_async(priv);
+               priv->pending_frame_id = wsm->packet_id;
+               if (queue_delayed_work(priv->workqueue,
+                                      &priv->scan.probe_work, 0) <= 0)
+                       wsm_unlock_tx(priv);
+               handled = true;
+               break;
+       case do_drop:
+               pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl);
+               BUG_ON(cw1200_queue_remove(queue, wsm->packet_id));
+               handled = true;
+               break;
+       case do_wep:
+               pr_debug("[WSM] Issue set_default_wep_key.\n");
+               wsm_lock_tx_async(priv);
+               priv->wep_default_key_id = tx_info->control.hw_key->keyidx;
+               priv->pending_frame_id = wsm->packet_id;
+               if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0)
+                       wsm_unlock_tx(priv);
+               handled = true;
+               break;
+       case do_tx:
+               pr_debug("[WSM] Transmit frame.\n");
+               break;
+       default:
+               /* Do nothing */
+               break;
+       }
+       return handled;
+}
+
+static int cw1200_get_prio_queue(struct cw1200_common *priv,
+                                u32 link_id_map, int *total)
+{
+       static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) |
+               BIT(CW1200_LINK_ID_UAPSD);
+       struct wsm_edca_queue_params *edca;
+       unsigned score, best = -1;
+       int winner = -1;
+       int queued;
+       int i;
+
+       /* search for a winner using edca params */
+       for (i = 0; i < 4; ++i) {
+               queued = cw1200_queue_get_num_queued(&priv->tx_queue[i],
+                               link_id_map);
+               if (!queued)
+                       continue;
+               *total += queued;
+               edca = &priv->edca.params[i];
+               score = ((edca->aifns + edca->cwmin) << 16) +
+                       ((edca->cwmax - edca->cwmin) *
+                        (get_random_int() & 0xFFFF));
+               if (score < best && (winner < 0 || i != 3)) {
+                       best = score;
+                       winner = i;
+               }
+       }
+
+       /* override winner if bursting */
+       if (winner >= 0 && priv->tx_burst_idx >= 0 &&
+           winner != priv->tx_burst_idx &&
+           !cw1200_queue_get_num_queued(
+                   &priv->tx_queue[winner],
+                   link_id_map & urgent) &&
+           cw1200_queue_get_num_queued(
+                   &priv->tx_queue[priv->tx_burst_idx],
+                   link_id_map))
+               winner = priv->tx_burst_idx;
+
+       return winner;
+}
+
+static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv,
+                                    struct cw1200_queue **queue_p,
+                                    u32 *tx_allowed_mask_p,
+                                    bool *more)
+{
+       int idx;
+       u32 tx_allowed_mask;
+       int total = 0;
+
+       /* Search for a queue with multicast frames buffered */
+       if (priv->tx_multicast) {
+               tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM);
+               idx = cw1200_get_prio_queue(priv,
+                               tx_allowed_mask, &total);
+               if (idx >= 0) {
+                       *more = total > 1;
+                       goto found;
+               }
+       }
+
+       /* Search for unicast traffic */
+       tx_allowed_mask = ~priv->sta_asleep_mask;
+       tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD);
+       if (priv->sta_asleep_mask) {
+               tx_allowed_mask |= priv->pspoll_mask;
+               tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM);
+       } else {
+               tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM);
+       }
+       idx = cw1200_get_prio_queue(priv,
+                       tx_allowed_mask, &total);
+       if (idx < 0)
+               return -ENOENT;
+
+found:
+       *queue_p = &priv->tx_queue[idx];
+       *tx_allowed_mask_p = tx_allowed_mask;
+       return 0;
+}
+
+int wsm_get_tx(struct cw1200_common *priv, u8 **data,
+              size_t *tx_len, int *burst)
+{
+       struct wsm_tx *wsm = NULL;
+       struct ieee80211_tx_info *tx_info;
+       struct cw1200_queue *queue = NULL;
+       int queue_num;
+       u32 tx_allowed_mask = 0;
+       const struct cw1200_txpriv *txpriv = NULL;
+       int count = 0;
+
+       /* More is used only for broadcasts. */
+       bool more = false;
+
+       if (priv->wsm_cmd.ptr) { /* CMD request */
+               ++count;
+               spin_lock(&priv->wsm_cmd.lock);
+               BUG_ON(!priv->wsm_cmd.ptr);
+               *data = priv->wsm_cmd.ptr;
+               *tx_len = priv->wsm_cmd.len;
+               *burst = 1;
+               spin_unlock(&priv->wsm_cmd.lock);
+       } else {
+               for (;;) {
+                       int ret;
+
+                       if (atomic_add_return(0, &priv->tx_lock))
+                               break;
+
+                       spin_lock_bh(&priv->ps_state_lock);
+
+                       ret = wsm_get_tx_queue_and_mask(priv, &queue,
+                                                       &tx_allowed_mask, &more);
+                       queue_num = queue - priv->tx_queue;
+
+                       if (priv->buffered_multicasts &&
+                           (ret || !more) &&
+                           (priv->tx_multicast || !priv->sta_asleep_mask)) {
+                               priv->buffered_multicasts = false;
+                               if (priv->tx_multicast) {
+                                       priv->tx_multicast = false;
+                                       queue_work(priv->workqueue,
+                                                  &priv->multicast_stop_work);
+                               }
+                       }
+
+                       spin_unlock_bh(&priv->ps_state_lock);
+
+                       if (ret)
+                               break;
+
+                       if (cw1200_queue_get(queue,
+                                            tx_allowed_mask,
+                                            &wsm, &tx_info, &txpriv))
+                               continue;
+
+                       if (wsm_handle_tx_data(priv, wsm,
+                                              tx_info, txpriv, queue))
+                               continue;  /* Handled by WSM */
+
+                       wsm->hdr.id &= __cpu_to_le16(
+                               ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX));
+                       wsm->hdr.id |= cpu_to_le16(
+                               WSM_TX_LINK_ID(txpriv->raw_link_id));
+                       priv->pspoll_mask &= ~BIT(txpriv->raw_link_id);
+
+                       *data = (u8 *)wsm;
+                       *tx_len = __le16_to_cpu(wsm->hdr.len);
+
+                       /* allow bursting if txop is set */
+                       if (priv->edca.params[queue_num].txop_limit)
+                               *burst = min(*burst,
+                                            (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1);
+                       else
+                               *burst = 1;
+
+                       /* store index of bursting queue */
+                       if (*burst > 1)
+                               priv->tx_burst_idx = queue_num;
+                       else
+                               priv->tx_burst_idx = -1;
+
+                       if (more) {
+                               struct ieee80211_hdr *hdr =
+                                       (struct ieee80211_hdr *)
+                                       &((u8 *)wsm)[txpriv->offset];
+                               /* more buffered multicast/broadcast frames
+                                *  ==> set MoreData flag in IEEE 802.11 header
+                                *  to inform PS STAs
+                                */
+                               hdr->frame_control |=
+                                       cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+                       }
+
+                       pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n",
+                                0x0004, *tx_len, *data,
+                                wsm->more ? 'M' : ' ');
+                       ++count;
+                       break;
+               }
+       }
+
+       return count;
+}
+
+void wsm_txed(struct cw1200_common *priv, u8 *data)
+{
+       if (data == priv->wsm_cmd.ptr) {
+               spin_lock(&priv->wsm_cmd.lock);
+               priv->wsm_cmd.ptr = NULL;
+               spin_unlock(&priv->wsm_cmd.lock);
+       }
+}
+
+/* ******************************************************************** */
+/* WSM buffer                                                          */
+
+void wsm_buf_init(struct wsm_buf *buf)
+{
+       BUG_ON(buf->begin);
+       buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
+       buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin;
+       wsm_buf_reset(buf);
+}
+
+void wsm_buf_deinit(struct wsm_buf *buf)
+{
+       kfree(buf->begin);
+       buf->begin = buf->data = buf->end = NULL;
+}
+
+static void wsm_buf_reset(struct wsm_buf *buf)
+{
+       if (buf->begin) {
+               buf->data = &buf->begin[4];
+               *(u32 *)buf->begin = 0;
+       } else {
+               buf->data = buf->begin;
+       }
+}
+
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size)
+{
+       size_t pos = buf->data - buf->begin;
+       size_t size = pos + extra_size;
+
+       size = round_up(size, FWLOAD_BLOCK_SIZE);
+
+       buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA);
+       if (buf->begin) {
+               buf->data = &buf->begin[pos];
+               buf->end = &buf->begin[size];
+               return 0;
+       } else {
+               buf->end = buf->data = buf->begin;
+               return -ENOMEM;
+       }
+}
diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/cw1200/wsm.h
new file mode 100644 (file)
index 0000000..7afc613
--- /dev/null
@@ -0,0 +1,1870 @@
+/*
+ * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on CW1200 UMAC WSM API, which is
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Stewart Mathers <stewart.mathers@stericsson.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 CW1200_WSM_H_INCLUDED
+#define CW1200_WSM_H_INCLUDED
+
+#include <linux/spinlock.h>
+
+struct cw1200_common;
+
+/* Bands */
+/* Radio band 2.412 -2.484 GHz. */
+#define WSM_PHY_BAND_2_4G              (0)
+
+/* Radio band 4.9375-5.8250 GHz. */
+#define WSM_PHY_BAND_5G                        (1)
+
+/* Transmit rates */
+/* 1   Mbps            ERP-DSSS */
+#define WSM_TRANSMIT_RATE_1            (0)
+
+/* 2   Mbps            ERP-DSSS */
+#define WSM_TRANSMIT_RATE_2            (1)
+
+/* 5.5 Mbps            ERP-CCK */
+#define WSM_TRANSMIT_RATE_5            (2)
+
+/* 11  Mbps            ERP-CCK */
+#define WSM_TRANSMIT_RATE_11           (3)
+
+/* 22  Mbps            ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_22                (4) */
+
+/* 33  Mbps            ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_33                (5) */
+
+/* 6   Mbps   (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_6            (6)
+
+/* 9   Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_9            (7)
+
+/* 12  Mbps  (6 Mbps)  ERP-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_12           (8)
+
+/* 18  Mbps  (9 Mbps)  ERP-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_18           (9)
+
+/* 24  Mbps (12 Mbps)  ERP-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_24           (10)
+
+/* 36  Mbps (18 Mbps)  ERP-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_36           (11)
+
+/* 48  Mbps (24 Mbps)  ERP-OFDM, 64QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_48           (12)
+
+/* 54  Mbps (27 Mbps)  ERP-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_54           (13)
+
+/* 6.5 Mbps            HT-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_6         (14)
+
+/* 13  Mbps            HT-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_13                (15)
+
+/* 19.5 Mbps           HT-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_19                (16)
+
+/* 26  Mbps            HT-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_26                (17)
+
+/* 39  Mbps            HT-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_39                (18)
+
+/* 52  Mbps            HT-OFDM, 64QAM coding rate 2/3 */
+#define WSM_TRANSMIT_RATE_HT_52                (19)
+
+/* 58.5 Mbps           HT-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_58                (20)
+
+/* 65  Mbps            HT-OFDM, 64QAM coding rate 5/6 */
+#define WSM_TRANSMIT_RATE_HT_65                (21)
+
+/* Scan types */
+/* Foreground scan */
+#define WSM_SCAN_TYPE_FOREGROUND       (0)
+
+/* Background scan */
+#define WSM_SCAN_TYPE_BACKGROUND       (1)
+
+/* Auto scan */
+#define WSM_SCAN_TYPE_AUTO             (2)
+
+/* Scan flags */
+/* Forced background scan means if the station cannot */
+/* enter the power-save mode, it shall force to perform a */
+/* background scan. Only valid when ScanType is */
+/* background scan. */
+#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0))
+
+/* The WLAN device scans one channel at a time so */
+/* that disturbance to the data traffic is minimized. */
+#define WSM_SCAN_FLAG_SPLIT_METHOD     (BIT(1))
+
+/* Preamble Type. Long if not set. */
+#define WSM_SCAN_FLAG_SHORT_PREAMBLE   (BIT(2))
+
+/* 11n Tx Mode. Mixed if not set. */
+#define WSM_SCAN_FLAG_11N_GREENFIELD   (BIT(3))
+
+/* Scan constraints */
+/* Maximum number of channels to be scanned. */
+#define WSM_SCAN_MAX_NUM_OF_CHANNELS   (48)
+
+/* The maximum number of SSIDs that the device can scan for. */
+#define WSM_SCAN_MAX_NUM_OF_SSIDS      (2)
+
+/* Power management modes */
+/* 802.11 Active mode */
+#define WSM_PSM_ACTIVE                 (0)
+
+/* 802.11 PS mode */
+#define WSM_PSM_PS                     BIT(0)
+
+/* Fast Power Save bit */
+#define WSM_PSM_FAST_PS_FLAG           BIT(7)
+
+/* Dynamic aka Fast power save */
+#define WSM_PSM_FAST_PS                        (BIT(0) | BIT(7))
+
+/* Undetermined */
+/* Note : Undetermined status is reported when the */
+/* NULL data frame used to advertise the PM mode to */
+/* the AP at Pre or Post Background Scan is not Acknowledged */
+#define WSM_PSM_UNKNOWN                        BIT(1)
+
+/* Queue IDs */
+/* best effort/legacy */
+#define WSM_QUEUE_BEST_EFFORT          (0)
+
+/* background */
+#define WSM_QUEUE_BACKGROUND           (1)
+
+/* video */
+#define WSM_QUEUE_VIDEO                        (2)
+
+/* voice */
+#define WSM_QUEUE_VOICE                        (3)
+
+/* HT TX parameters */
+/* Non-HT */
+#define WSM_HT_TX_NON_HT               (0)
+
+/* Mixed format */
+#define WSM_HT_TX_MIXED                        (1)
+
+/* Greenfield format */
+#define WSM_HT_TX_GREENFIELD           (2)
+
+/* STBC allowed */
+#define WSM_HT_TX_STBC                 (BIT(7))
+
+/* EPTA prioirty flags for BT Coex */
+/* default epta priority */
+#define WSM_EPTA_PRIORITY_DEFAULT      4
+/* use for normal data */
+#define WSM_EPTA_PRIORITY_DATA         4
+/* use for connect/disconnect/roaming*/
+#define WSM_EPTA_PRIORITY_MGT          5
+/* use for action frames */
+#define WSM_EPTA_PRIORITY_ACTION       5
+/* use for AC_VI data */
+#define WSM_EPTA_PRIORITY_VIDEO                5
+/* use for AC_VO data */
+#define WSM_EPTA_PRIORITY_VOICE                6
+/* use for EAPOL exchange */
+#define WSM_EPTA_PRIORITY_EAPOL                7
+
+/* TX status */
+/* Frame was sent aggregated */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_AGGREGATION      (BIT(0))
+
+/* Host should requeue this frame later. */
+/* Valid only when status is WSM_REQUEUE. */
+#define WSM_TX_STATUS_REQUEUE          (BIT(1))
+
+/* Normal Ack */
+#define WSM_TX_STATUS_NORMAL_ACK       (0<<2)
+
+/* No Ack */
+#define WSM_TX_STATUS_NO_ACK           (1<<2)
+
+/* No explicit acknowledgement */
+#define WSM_TX_STATUS_NO_EXPLICIT_ACK  (2<<2)
+
+/* Block Ack */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_BLOCK_ACK                (3<<2)
+
+/* RX status */
+/* Unencrypted */
+#define WSM_RX_STATUS_UNENCRYPTED      (0<<0)
+
+/* WEP */
+#define WSM_RX_STATUS_WEP              (1<<0)
+
+/* TKIP */
+#define WSM_RX_STATUS_TKIP             (2<<0)
+
+/* AES */
+#define WSM_RX_STATUS_AES              (3<<0)
+
+/* WAPI */
+#define WSM_RX_STATUS_WAPI             (4<<0)
+
+/* Macro to fetch encryption subfield. */
+#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07)
+
+/* Frame was part of an aggregation */
+#define WSM_RX_STATUS_AGGREGATE                (BIT(3))
+
+/* Frame was first in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_FIRST  (BIT(4))
+
+/* Frame was last in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_LAST   (BIT(5))
+
+/* Indicates a defragmented frame */
+#define WSM_RX_STATUS_DEFRAGMENTED     (BIT(6))
+
+/* Indicates a Beacon frame */
+#define WSM_RX_STATUS_BEACON           (BIT(7))
+
+/* Indicates STA bit beacon TIM field */
+#define WSM_RX_STATUS_TIM              (BIT(8))
+
+/* Indicates Beacon frame's virtual bitmap contains multicast bit */
+#define WSM_RX_STATUS_MULTICAST                (BIT(9))
+
+/* Indicates frame contains a matching SSID */
+#define WSM_RX_STATUS_MATCHING_SSID    (BIT(10))
+
+/* Indicates frame contains a matching BSSI */
+#define WSM_RX_STATUS_MATCHING_BSSI    (BIT(11))
+
+/* Indicates More bit set in Framectl field */
+#define WSM_RX_STATUS_MORE_DATA                (BIT(12))
+
+/* Indicates frame received during a measurement process */
+#define WSM_RX_STATUS_MEASUREMENT      (BIT(13))
+
+/* Indicates frame received as an HT packet */
+#define WSM_RX_STATUS_HT               (BIT(14))
+
+/* Indicates frame received with STBC */
+#define WSM_RX_STATUS_STBC             (BIT(15))
+
+/* Indicates Address 1 field matches dot11StationId */
+#define WSM_RX_STATUS_ADDRESS1         (BIT(16))
+
+/* Indicates Group address present in the Address 1 field */
+#define WSM_RX_STATUS_GROUP            (BIT(17))
+
+/* Indicates Broadcast address present in the Address 1 field */
+#define WSM_RX_STATUS_BROADCAST                (BIT(18))
+
+/* Indicates group key used with encrypted frames */
+#define WSM_RX_STATUS_GROUP_KEY                (BIT(19))
+
+/* Macro to fetch encryption key index. */
+#define WSM_RX_STATUS_KEY_IDX(status)  (((status >> 20)) & 0x0F)
+
+/* Indicates TSF inclusion after 802.11 frame body */
+#define WSM_RX_STATUS_TSF_INCLUDED     (BIT(24))
+
+/* Frame Control field starts at Frame offset + 2 */
+#define WSM_TX_2BYTES_SHIFT            (BIT(7))
+
+/* Join mode */
+/* IBSS */
+#define WSM_JOIN_MODE_IBSS             (0)
+
+/* BSS */
+#define WSM_JOIN_MODE_BSS              (1)
+
+/* PLCP preamble type */
+/* For long preamble */
+#define WSM_JOIN_PREAMBLE_LONG         (0)
+
+/* For short preamble (Long for 1Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT                (1)
+
+/* For short preamble (Long for 1 and 2Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT_2      (2)
+
+/* Join flags */
+/* Unsynchronized */
+#define WSM_JOIN_FLAGS_UNSYNCRONIZED   BIT(0)
+/* The BSS owner is a P2P GO */
+#define WSM_JOIN_FLAGS_P2P_GO          BIT(1)
+/* Force to join BSS with the BSSID and the
+ * SSID specified without waiting for beacons. The
+ * ProbeForJoin parameter is ignored.
+ */
+#define WSM_JOIN_FLAGS_FORCE           BIT(2)
+/* Give probe request/response higher
+ * priority over the BT traffic
+ */
+#define WSM_JOIN_FLAGS_PRIO            BIT(3)
+/* Issue immediate join confirmation and use
+ * join complete to notify about completion
+ */
+#define WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND BIT(5)
+
+/* Key types */
+#define WSM_KEY_TYPE_WEP_DEFAULT       (0)
+#define WSM_KEY_TYPE_WEP_PAIRWISE      (1)
+#define WSM_KEY_TYPE_TKIP_GROUP                (2)
+#define WSM_KEY_TYPE_TKIP_PAIRWISE     (3)
+#define WSM_KEY_TYPE_AES_GROUP         (4)
+#define WSM_KEY_TYPE_AES_PAIRWISE      (5)
+#define WSM_KEY_TYPE_WAPI_GROUP                (6)
+#define WSM_KEY_TYPE_WAPI_PAIRWISE     (7)
+
+/* Key indexes */
+#define WSM_KEY_MAX_INDEX              (10)
+
+/* ACK policy */
+#define WSM_ACK_POLICY_NORMAL          (0)
+#define WSM_ACK_POLICY_NO_ACK          (1)
+
+/* Start modes */
+#define WSM_START_MODE_AP              (0)     /* Mini AP */
+#define WSM_START_MODE_P2P_GO          (1)     /* P2P GO */
+#define WSM_START_MODE_P2P_DEV         (2)     /* P2P device */
+
+/* SetAssociationMode MIB flags */
+#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE         (BIT(0))
+#define WSM_ASSOCIATION_MODE_USE_HT_MODE               (BIT(1))
+#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET                (BIT(2))
+#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING    (BIT(3))
+#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES                (BIT(4))
+
+/* RcpiRssiThreshold MIB flags */
+#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0))
+#define WSM_RCPI_RSSI_USE_RSSI         (BIT(1))
+#define WSM_RCPI_RSSI_DONT_USE_UPPER   (BIT(2))
+#define WSM_RCPI_RSSI_DONT_USE_LOWER   (BIT(3))
+
+/* Update-ie constants */
+#define WSM_UPDATE_IE_BEACON           (BIT(0))
+#define WSM_UPDATE_IE_PROBE_RESP       (BIT(1))
+#define WSM_UPDATE_IE_PROBE_REQ                (BIT(2))
+
+/* WSM events */
+/* Error */
+#define WSM_EVENT_ERROR                        (0)
+
+/* BSS lost */
+#define WSM_EVENT_BSS_LOST             (1)
+
+/* BSS regained */
+#define WSM_EVENT_BSS_REGAINED         (2)
+
+/* Radar detected */
+#define WSM_EVENT_RADAR_DETECTED       (3)
+
+/* RCPI or RSSI threshold triggered */
+#define WSM_EVENT_RCPI_RSSI            (4)
+
+/* BT inactive */
+#define WSM_EVENT_BT_INACTIVE          (5)
+
+/* BT active */
+#define WSM_EVENT_BT_ACTIVE            (6)
+
+/* MIB IDs */
+/* 4.1  dot11StationId */
+#define WSM_MIB_ID_DOT11_STATION_ID            0x0000
+
+/* 4.2  dot11MaxtransmitMsduLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME  0x0001
+
+/* 4.3  dot11MaxReceiveLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME  0x0002
+
+/* 4.4  dot11SlotTime */
+#define WSM_MIB_ID_DOT11_SLOT_TIME             0x0003
+
+/* 4.5  dot11GroupAddressesTable */
+#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004
+#define WSM_MAX_GRP_ADDRTABLE_ENTRIES          8
+
+/* 4.6  dot11WepDefaultKeyId */
+#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID    0x0005
+
+/* 4.7  dot11CurrentTxPowerLevel */
+#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL        0x0006
+
+/* 4.8  dot11RTSThreshold */
+#define WSM_MIB_ID_DOT11_RTS_THRESHOLD         0x0007
+
+/* 4.9  NonErpProtection */
+#define WSM_MIB_ID_NON_ERP_PROTECTION          0x1000
+
+/* 4.10 ArpIpAddressesTable */
+#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE      0x1001
+#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES       1
+
+/* 4.11 TemplateFrame */
+#define WSM_MIB_ID_TEMPLATE_FRAME              0x1002
+
+/* 4.12 RxFilter */
+#define WSM_MIB_ID_RX_FILTER                   0x1003
+
+/* 4.13 BeaconFilterTable */
+#define WSM_MIB_ID_BEACON_FILTER_TABLE         0x1004
+
+/* 4.14 BeaconFilterEnable */
+#define WSM_MIB_ID_BEACON_FILTER_ENABLE                0x1005
+
+/* 4.15 OperationalPowerMode */
+#define WSM_MIB_ID_OPERATIONAL_POWER_MODE      0x1006
+
+/* 4.16 BeaconWakeUpPeriod */
+#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD                0x1007
+
+/* 4.17 RcpiRssiThreshold */
+#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD         0x1009
+
+/* 4.18 StatisticsTable */
+#define WSM_MIB_ID_STATISTICS_TABLE            0x100A
+
+/* 4.19 IbssPsConfig */
+#define WSM_MIB_ID_IBSS_PS_CONFIG              0x100B
+
+/* 4.20 CountersTable */
+#define WSM_MIB_ID_COUNTERS_TABLE              0x100C
+
+/* 4.21 BlockAckPolicy */
+#define WSM_MIB_ID_BLOCK_ACK_POLICY            0x100E
+
+/* 4.22 OverrideInternalTxRate */
+#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE   0x100F
+
+/* 4.23 SetAssociationMode */
+#define WSM_MIB_ID_SET_ASSOCIATION_MODE                0x1010
+
+/* 4.24 UpdateEptaConfigData */
+#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA     0x1011
+
+/* 4.25 SelectCcaMethod */
+#define WSM_MIB_ID_SELECT_CCA_METHOD           0x1012
+
+/* 4.26 SetUpasdInformation */
+#define WSM_MIB_ID_SET_UAPSD_INFORMATION       0x1013
+
+/* 4.27 SetAutoCalibrationMode  WBF00004073 */
+#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE   0x1015
+
+/* 4.28 SetTxRateRetryPolicy */
+#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY    0x1016
+
+/* 4.29 SetHostMessageTypeFilter */
+#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER    0x1017
+
+/* 4.30 P2PFindInfo */
+#define WSM_MIB_ID_P2P_FIND_INFO               0x1018
+
+/* 4.31 P2PPsModeInfo */
+#define WSM_MIB_ID_P2P_PS_MODE_INFO            0x1019
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A
+
+/* 4.33 SetUDPPortDataFrameFilter */
+#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER        0x101B
+
+/* 4.34 SetMagicDataFrameFilter */
+#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER  0x101C
+
+/* 4.35 P2PDeviceInfo */
+#define WSM_MIB_ID_P2P_DEVICE_INFO             0x101D
+
+/* 4.36 SetWCDMABand */
+#define WSM_MIB_ID_SET_WCDMA_BAND              0x101E
+
+/* 4.37 GroupTxSequenceCounter */
+#define WSM_MIB_ID_GRP_SEQ_COUNTER             0x101F
+
+/* 4.38 ProtectedMgmtPolicy */
+#define WSM_MIB_ID_PROTECTED_MGMT_POLICY       0x1020
+
+/* 4.39 SetHtProtection */
+#define WSM_MIB_ID_SET_HT_PROTECTION           0x1021
+
+/* 4.40 GPIO Command */
+#define WSM_MIB_ID_GPIO_COMMAND                        0x1022
+
+/* 4.41 TSF Counter Value */
+#define WSM_MIB_ID_TSF_COUNTER                 0x1023
+
+/* Test Purposes Only */
+#define WSM_MIB_ID_BLOCK_ACK_INFO              0x100D
+
+/* 4.42 UseMultiTxConfMessage */
+#define WSM_MIB_USE_MULTI_TX_CONF              0x1024
+
+/* 4.43 Keep-alive period */
+#define WSM_MIB_ID_KEEP_ALIVE_PERIOD           0x1025
+
+/* 4.44 Disable BSSID filter */
+#define WSM_MIB_ID_DISABLE_BSSID_FILTER                0x1026
+
+/* Frame template types */
+#define WSM_FRAME_TYPE_PROBE_REQUEST   (0)
+#define WSM_FRAME_TYPE_BEACON          (1)
+#define WSM_FRAME_TYPE_NULL            (2)
+#define WSM_FRAME_TYPE_QOS_NULL                (3)
+#define WSM_FRAME_TYPE_PS_POLL         (4)
+#define WSM_FRAME_TYPE_PROBE_RESPONSE  (5)
+
+#define WSM_FRAME_GREENFIELD           (0x80)  /* See 4.11 */
+
+/* Status */
+/* The WSM firmware has completed a request */
+/* successfully. */
+#define WSM_STATUS_SUCCESS              (0)
+
+/* This is a generic failure code if other error codes do */
+/* not apply. */
+#define WSM_STATUS_FAILURE              (1)
+
+/* A request contains one or more invalid parameters. */
+#define WSM_INVALID_PARAMETER           (2)
+
+/* The request cannot perform because the device is in */
+/* an inappropriate mode. */
+#define WSM_ACCESS_DENIED               (3)
+
+/* The frame received includes a decryption error. */
+#define WSM_STATUS_DECRYPTFAILURE       (4)
+
+/* A MIC failure is detected in the received packets. */
+#define WSM_STATUS_MICFAILURE           (5)
+
+/* The transmit request failed due to retry limit being */
+/* exceeded. */
+#define WSM_STATUS_RETRY_EXCEEDED       (6)
+
+/* The transmit request failed due to MSDU life time */
+/* being exceeded. */
+#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7)
+
+/* The link to the AP is lost. */
+#define WSM_STATUS_LINK_LOST            (8)
+
+/* No key was found for the encrypted frame */
+#define WSM_STATUS_NO_KEY_FOUND         (9)
+
+/* Jammer was detected when transmitting this frame */
+#define WSM_STATUS_JAMMER_DETECTED      (10)
+
+/* The message should be requeued later. */
+/* This is applicable only to Transmit */
+#define WSM_REQUEUE                     (11)
+
+/* Advanced filtering options */
+#define WSM_MAX_FILTER_ELEMENTS                (4)
+
+#define WSM_FILTER_ACTION_IGNORE       (0)
+#define WSM_FILTER_ACTION_FILTER_IN    (1)
+#define WSM_FILTER_ACTION_FILTER_OUT   (2)
+
+#define WSM_FILTER_PORT_TYPE_DST       (0)
+#define WSM_FILTER_PORT_TYPE_SRC       (1)
+
+/* Actual header of WSM messages */
+struct wsm_hdr {
+       __le16 len;
+       __le16 id;
+};
+
+#define WSM_TX_SEQ_MAX                 (7)
+#define WSM_TX_SEQ(seq)                        \
+               ((seq & WSM_TX_SEQ_MAX) << 13)
+#define WSM_TX_LINK_ID_MAX             (0x0F)
+#define WSM_TX_LINK_ID(link_id)                \
+               ((link_id & WSM_TX_LINK_ID_MAX) << 6)
+
+#define MAX_BEACON_SKIP_TIME_MS 1000
+
+#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2)
+
+/* ******************************************************************** */
+/* WSM capability                                                      */
+
+#define WSM_STARTUP_IND_ID 0x0801
+
+struct wsm_startup_ind {
+       u16 input_buffers;
+       u16 input_buffer_size;
+       u16 status;
+       u16 hw_id;
+       u16 hw_subid;
+       u16 fw_cap;
+       u16 fw_type;
+       u16 fw_api;
+       u16 fw_build;
+       u16 fw_ver;
+       char fw_label[128];
+       u32 config[4];
+};
+
+/* ******************************************************************** */
+/* WSM commands                                                                */
+
+/* 3.1 */
+#define WSM_CONFIGURATION_REQ_ID 0x0009
+#define WSM_CONFIGURATION_RESP_ID 0x0409
+
+struct wsm_tx_power_range {
+       int min_power_level;
+       int max_power_level;
+       u32 stepping;
+};
+
+struct wsm_configuration {
+       /* [in] */ u32 dot11MaxTransmitMsduLifeTime;
+       /* [in] */ u32 dot11MaxReceiveLifeTime;
+       /* [in] */ u32 dot11RtsThreshold;
+       /* [in, out] */ u8 *dot11StationId;
+       /* [in] */ const void *dpdData;
+       /* [in] */ size_t dpdData_size;
+       /* [out] */ u8 dot11FrequencyBandsSupported;
+       /* [out] */ u32 supportedRateMask;
+       /* [out] */ struct wsm_tx_power_range txPowerRange[2];
+};
+
+int wsm_configuration(struct cw1200_common *priv,
+                     struct wsm_configuration *arg);
+
+/* 3.3 */
+#define WSM_RESET_REQ_ID 0x000A
+#define WSM_RESET_RESP_ID 0x040A
+struct wsm_reset {
+       /* [in] */ int link_id;
+       /* [in] */ bool reset_statistics;
+};
+
+int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg);
+
+/* 3.5 */
+#define WSM_READ_MIB_REQ_ID 0x0005
+#define WSM_READ_MIB_RESP_ID 0x0405
+int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *buf,
+                size_t buf_size);
+
+/* 3.7 */
+#define WSM_WRITE_MIB_REQ_ID 0x0006
+#define WSM_WRITE_MIB_RESP_ID 0x0406
+int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *buf,
+                 size_t buf_size);
+
+/* 3.9 */
+#define WSM_START_SCAN_REQ_ID 0x0007
+#define WSM_START_SCAN_RESP_ID 0x0407
+
+struct wsm_ssid {
+       u8 ssid[32];
+       u32 length;
+};
+
+struct wsm_scan_ch {
+       u16 number;
+       u32 min_chan_time;
+       u32 max_chan_time;
+       u32 tx_power_level;
+};
+
+struct wsm_scan {
+       /* WSM_PHY_BAND_... */
+       u8 band;
+
+       /* WSM_SCAN_TYPE_... */
+       u8 type;
+
+       /* WSM_SCAN_FLAG_... */
+       u8 flags;
+
+       /* WSM_TRANSMIT_RATE_... */
+       u8 max_tx_rate;
+
+       /* Interval period in TUs that the device shall the re- */
+       /* execute the requested scan. Max value supported by the device */
+       /* is 256s. */
+       u32 auto_scan_interval;
+
+       /* Number of probe requests (per SSID) sent to one (1) */
+       /* channel. Zero (0) means that none is send, which */
+       /* means that a passive scan is to be done. Value */
+       /* greater than zero (0) means that an active scan is to */
+       /* be done. */
+       u32 num_probes;
+
+       /* Number of channels to be scanned. */
+       /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */
+       u8 num_channels;
+
+       /* Number of SSID provided in the scan command (this */
+       /* is zero (0) in broadcast scan) */
+       /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */
+       u8 num_ssids;
+
+       /* The delay time (in microseconds) period */
+       /* before sending a probe-request. */
+       u8 probe_delay;
+
+       /* SSIDs to be scanned [numOfSSIDs]; */
+       struct wsm_ssid *ssids;
+
+       /* Channels to be scanned [numOfChannels]; */
+       struct wsm_scan_ch *ch;
+};
+
+int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg);
+
+/* 3.11 */
+#define WSM_STOP_SCAN_REQ_ID 0x0008
+#define WSM_STOP_SCAN_RESP_ID 0x0408
+int wsm_stop_scan(struct cw1200_common *priv);
+
+/* 3.13 */
+#define WSM_SCAN_COMPLETE_IND_ID 0x0806
+struct wsm_scan_complete {
+       /* WSM_STATUS_... */
+       u32 status;
+
+       /* WSM_PSM_... */
+       u8 psm;
+
+       /* Number of channels that the scan operation completed. */
+       u8 num_channels;
+};
+
+/* 3.14 */
+#define WSM_TX_CONFIRM_IND_ID 0x0404
+#define WSM_MULTI_TX_CONFIRM_ID 0x041E
+
+struct wsm_tx_confirm {
+       /* Packet identifier used in wsm_tx. */
+       u32 packet_id;
+
+       /* WSM_STATUS_... */
+       u32 status;
+
+       /* WSM_TRANSMIT_RATE_... */
+       u8 tx_rate;
+
+       /* The number of times the frame was transmitted */
+       /* without receiving an acknowledgement. */
+       u8 ack_failures;
+
+       /* WSM_TX_STATUS_... */
+       u16 flags;
+
+       /* The total time in microseconds that the frame spent in */
+       /* the WLAN device before transmission as completed. */
+       u32 media_delay;
+
+       /* The total time in microseconds that the frame spent in */
+       /* the WLAN device before transmission was started. */
+       u32 tx_queue_delay;
+};
+
+/* 3.15 */
+typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv,
+                                  struct wsm_tx_confirm *arg);
+
+/* Note that ideology of wsm_tx struct is different against the rest of
+ * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input
+ * argument for WSM call, but a prepared bytestream to be sent to firmware.
+ * It is filled partly in cw1200_tx, partly in low-level WSM code.
+ * Please pay attention once again: ideology is different.
+ *
+ * Legend:
+ * - [in]: cw1200_tx must fill this field.
+ * - [wsm]: the field is filled by low-level WSM.
+ */
+struct wsm_tx {
+       /* common WSM header */
+       struct wsm_hdr hdr;
+
+       /* Packet identifier that meant to be used in completion. */
+       u32 packet_id;  /* Note this is actually a cookie */
+
+       /* WSM_TRANSMIT_RATE_... */
+       u8 max_tx_rate;
+
+       /* WSM_QUEUE_... */
+       u8 queue_id;
+
+       /* True: another packet is pending on the host for transmission. */
+       u8 more;
+
+       /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */
+       /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */
+       /* Bits 3:1  - PTA Priority */
+       /* Bits 6:4  - Tx Rate Retry Policy */
+       /* Bit 7 - Reserved */
+       u8 flags;
+
+       /* Should be 0. */
+       u32 reserved;
+
+       /* The elapsed time in TUs, after the initial transmission */
+       /* of an MSDU, after which further attempts to transmit */
+       /* the MSDU shall be terminated. Overrides the global */
+       /* dot11MaxTransmitMsduLifeTime setting [optional] */
+       /* Device will set the default value if this is 0. */
+       u32 expire_time;
+
+       /* WSM_HT_TX_... */
+       __le32 ht_tx_parameters;
+} __packed;
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */
+#define WSM_TX_EXTRA_HEADROOM (28)
+
+/* 3.16 */
+#define WSM_RECEIVE_IND_ID 0x0804
+
+struct wsm_rx {
+       /* WSM_STATUS_... */
+       u32 status;
+
+       /* Specifies the channel of the received packet. */
+       u16 channel_number;
+
+       /* WSM_TRANSMIT_RATE_... */
+       u8 rx_rate;
+
+       /* This value is expressed in signed Q8.0 format for */
+       /* RSSI and unsigned Q7.1 format for RCPI. */
+       u8 rcpi_rssi;
+
+       /* WSM_RX_STATUS_... */
+       u32 flags;
+};
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */
+#define WSM_RX_EXTRA_HEADROOM (16)
+
+typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg,
+                          struct sk_buff **skb_p);
+
+/* 3.17 */
+struct wsm_event {
+       /* WSM_STATUS_... */
+       /* [out] */ u32 id;
+
+       /* Indication parameters. */
+       /* For error indication, this shall be a 32-bit WSM status. */
+       /* For RCPI or RSSI indication, this should be an 8-bit */
+       /* RCPI or RSSI value. */
+       /* [out] */ u32 data;
+};
+
+struct cw1200_wsm_event {
+       struct list_head link;
+       struct wsm_event evt;
+};
+
+/* 3.18 - 3.22 */
+/* Measurement. Skipped for now. Irrelevent. */
+
+typedef void (*wsm_event_cb) (struct cw1200_common *priv,
+                             struct wsm_event *arg);
+
+/* 3.23 */
+#define WSM_JOIN_REQ_ID 0x000B
+#define WSM_JOIN_RESP_ID 0x040B
+
+struct wsm_join {
+       /* WSM_JOIN_MODE_... */
+       u8 mode;
+
+       /* WSM_PHY_BAND_... */
+       u8 band;
+
+       /* Specifies the channel number to join. The channel */
+       /* number will be mapped to an actual frequency */
+       /* according to the band */
+       u16 channel_number;
+
+       /* Specifies the BSSID of the BSS or IBSS to be joined */
+       /* or the IBSS to be started. */
+       u8 bssid[6];
+
+       /* ATIM window of IBSS */
+       /* When ATIM window is zero the initiated IBSS does */
+       /* not support power saving. */
+       u16 atim_window;
+
+       /* WSM_JOIN_PREAMBLE_... */
+       u8 preamble_type;
+
+       /* Specifies if a probe request should be send with the */
+       /* specified SSID when joining to the network. */
+       u8 probe_for_join;
+
+       /* DTIM Period (In multiples of beacon interval) */
+       u8 dtim_period;
+
+       /* WSM_JOIN_FLAGS_... */
+       u8 flags;
+
+       /* Length of the SSID */
+       u32 ssid_len;
+
+       /* Specifies the SSID of the IBSS to join or start */
+       u8 ssid[32];
+
+       /* Specifies the time between TBTTs in TUs */
+       u32 beacon_interval;
+
+       /* A bit mask that defines the BSS basic rate set. */
+       u32 basic_rate_set;
+};
+
+struct wsm_join_cnf {
+       u32 status;
+
+       /* Minimum transmission power level in units of 0.1dBm */
+       u32 min_power_level;
+
+       /* Maximum transmission power level in units of 0.1dBm */
+       u32 max_power_level;
+};
+
+int wsm_join(struct cw1200_common *priv, struct wsm_join *arg);
+
+/* 3.24 */
+struct wsm_join_complete {
+       /* WSM_STATUS_... */
+       u32 status;
+};
+
+/* 3.25 */
+#define WSM_SET_PM_REQ_ID 0x0010
+#define WSM_SET_PM_RESP_ID 0x0410
+struct wsm_set_pm {
+       /* WSM_PSM_... */
+       u8 mode;
+
+       /* in unit of 500us; 0 to use default */
+       u8 fast_psm_idle_period;
+
+       /* in unit of 500us; 0 to use default */
+       u8 ap_psm_change_period;
+
+       /* in unit of 500us; 0 to disable auto-pspoll */
+       u8 min_auto_pspoll_period;
+};
+
+int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
+
+/* 3.27 */
+struct wsm_set_pm_complete {
+       u8 psm;                 /* WSM_PSM_... */
+};
+
+/* 3.28 */
+#define WSM_SET_BSS_PARAMS_REQ_ID 0x0011
+#define WSM_SET_BSS_PARAMS_RESP_ID 0x0411
+struct wsm_set_bss_params {
+       /* This resets the beacon loss counters only */
+       u8 reset_beacon_loss;
+
+       /* The number of lost consecutive beacons after which */
+       /* the WLAN device should indicate the BSS-Lost event */
+       /* to the WLAN host driver. */
+       u8 beacon_lost_count;
+
+       /* The AID received during the association process. */
+       u16 aid;
+
+       /* The operational rate set mask */
+       u32 operational_rate_set;
+};
+
+int wsm_set_bss_params(struct cw1200_common *priv,
+                      const struct wsm_set_bss_params *arg);
+
+/* 3.30 */
+#define WSM_ADD_KEY_REQ_ID         0x000C
+#define WSM_ADD_KEY_RESP_ID        0x040C
+struct wsm_add_key {
+       u8 type;                /* WSM_KEY_TYPE_... */
+       u8 index;               /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */
+       u16 reserved;
+       union {
+               struct {
+                       u8 peer[6];     /* MAC address of the peer station */
+                       u8 reserved;
+                       u8 keylen;              /* Key length in bytes */
+                       u8 keydata[16];         /* Key data */
+               } __packed wep_pairwise;
+               struct {
+                       u8 keyid;       /* Unique per key identifier (0..3) */
+                       u8 keylen;              /* Key length in bytes */
+                       u16 reserved;
+                       u8 keydata[16];         /* Key data */
+               } __packed wep_group;
+               struct {
+                       u8 peer[6];     /* MAC address of the peer station */
+                       u16 reserved;
+                       u8 keydata[16]; /* TKIP key data */
+                       u8 rx_mic_key[8];               /* Rx MIC key */
+                       u8 tx_mic_key[8];               /* Tx MIC key */
+               } __packed tkip_pairwise;
+               struct {
+                       u8 keydata[16]; /* TKIP key data */
+                       u8 rx_mic_key[8];               /* Rx MIC key */
+                       u8 keyid;               /* Key ID */
+                       u8 reserved[3];
+                       u8 rx_seqnum[8];        /* Receive Sequence Counter */
+               } __packed tkip_group;
+               struct {
+                       u8 peer[6];     /* MAC address of the peer station */
+                       u16 reserved;
+                       u8 keydata[16]; /* AES key data */
+               } __packed aes_pairwise;
+               struct {
+                       u8 keydata[16]; /* AES key data */
+                       u8 keyid;               /* Key ID */
+                       u8 reserved[3];
+                       u8 rx_seqnum[8];        /* Receive Sequence Counter */
+               } __packed aes_group;
+               struct {
+                       u8 peer[6];     /* MAC address of the peer station */
+                       u8 keyid;               /* Key ID */
+                       u8 reserved;
+                       u8 keydata[16]; /* WAPI key data */
+                       u8 mic_key[16]; /* MIC key data */
+               } __packed wapi_pairwise;
+               struct {
+                       u8 keydata[16]; /* WAPI key data */
+                       u8 mic_key[16]; /* MIC key data */
+                       u8 keyid;               /* Key ID */
+                       u8 reserved[3];
+               } __packed wapi_group;
+       } __packed;
+} __packed;
+
+int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg);
+
+/* 3.32 */
+#define WSM_REMOVE_KEY_REQ_ID         0x000D
+#define WSM_REMOVE_KEY_RESP_ID        0x040D
+struct wsm_remove_key {
+       u8 index; /* Key entry index : 0-10 */
+};
+
+int wsm_remove_key(struct cw1200_common *priv,
+                  const struct wsm_remove_key *arg);
+
+/* 3.34 */
+struct wsm_set_tx_queue_params {
+       /* WSM_ACK_POLICY_... */
+       u8 ackPolicy;
+
+       /* Medium Time of TSPEC (in 32us units) allowed per */
+       /* One Second Averaging Period for this queue. */
+       u16 allowedMediumTime;
+
+       /* dot11MaxTransmitMsduLifetime to be used for the */
+       /* specified queue. */
+       u32 maxTransmitLifetime;
+};
+
+struct wsm_tx_queue_params {
+       /* NOTE: index is a linux queue id. */
+       struct wsm_set_tx_queue_params params[4];
+};
+
+
+#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,\
+               max_life_time)  \
+do {                                                   \
+       struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \
+       p->ackPolicy = (ack_policy);                            \
+       p->allowedMediumTime = (allowed_time);                          \
+       p->maxTransmitLifetime = (max_life_time);                       \
+} while (0)
+
+int wsm_set_tx_queue_params(struct cw1200_common *priv,
+                           const struct wsm_set_tx_queue_params *arg, u8 id);
+
+/* 3.36 */
+#define WSM_EDCA_PARAMS_REQ_ID 0x0013
+#define WSM_EDCA_PARAMS_RESP_ID 0x0413
+struct wsm_edca_queue_params {
+       /* CWmin (in slots) for the access class. */
+       u16 cwmin;
+
+       /* CWmax (in slots) for the access class. */
+       u16 cwmax;
+
+       /* AIFS (in slots) for the access class. */
+       u16 aifns;
+
+       /* TX OP Limit (in microseconds) for the access class. */
+       u16 txop_limit;
+
+       /* dot11MaxReceiveLifetime to be used for the specified */
+       /* the access class. Overrides the global */
+       /* dot11MaxReceiveLifetime value */
+       u32 max_rx_lifetime;
+};
+
+struct wsm_edca_params {
+       /* NOTE: index is a linux queue id. */
+       struct wsm_edca_queue_params params[4];
+       bool uapsd_enable[4];
+};
+
+#define TXOP_UNIT 32
+#define WSM_EDCA_SET(__edca, __queue, __aifs, __cw_min, __cw_max, __txop, __lifetime,\
+                    __uapsd) \
+       do {                                                    \
+               struct wsm_edca_queue_params *p = &(__edca)->params[__queue]; \
+               p->cwmin = __cw_min;                                    \
+               p->cwmax = __cw_max;                                    \
+               p->aifns = __aifs;                                      \
+               p->txop_limit = ((__txop) * TXOP_UNIT);                 \
+               p->max_rx_lifetime = __lifetime;                        \
+               (__edca)->uapsd_enable[__queue] = (__uapsd);            \
+       } while (0)
+
+int wsm_set_edca_params(struct cw1200_common *priv,
+                       const struct wsm_edca_params *arg);
+
+int wsm_set_uapsd_param(struct cw1200_common *priv,
+                       const struct wsm_edca_params *arg);
+
+/* 3.38 */
+/* Set-System info. Skipped for now. Irrelevent. */
+
+/* 3.40 */
+#define WSM_SWITCH_CHANNEL_REQ_ID 0x0016
+#define WSM_SWITCH_CHANNEL_RESP_ID 0x0416
+
+struct wsm_switch_channel {
+       /* 1 - means the STA shall not transmit any further */
+       /* frames until the channel switch has completed */
+       u8 mode;
+
+       /* Number of TBTTs until channel switch occurs. */
+       /* 0 - indicates switch shall occur at any time */
+       /* 1 - occurs immediately before the next TBTT */
+       u8 switch_count;
+
+       /* The new channel number to switch to. */
+       /* Note this is defined as per section 2.7. */
+       u16 channel_number;
+};
+
+int wsm_switch_channel(struct cw1200_common *priv,
+                      const struct wsm_switch_channel *arg);
+
+typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv);
+
+#define WSM_START_REQ_ID 0x0017
+#define WSM_START_RESP_ID 0x0417
+
+struct wsm_start {
+       /* WSM_START_MODE_... */
+       /* [in] */ u8 mode;
+
+       /* WSM_PHY_BAND_... */
+       /* [in] */ u8 band;
+
+       /* Channel number */
+       /* [in] */ u16 channel_number;
+
+       /* Client Traffic window in units of TU */
+       /* Valid only when mode == ..._P2P */
+       /* [in] */ u32 ct_window;
+
+       /* Interval between two consecutive */
+       /* beacon transmissions in TU. */
+       /* [in] */ u32 beacon_interval;
+
+       /* DTIM period in terms of beacon intervals */
+       /* [in] */ u8 dtim_period;
+
+       /* WSM_JOIN_PREAMBLE_... */
+       /* [in] */ u8 preamble;
+
+       /* The delay time (in microseconds) period */
+       /* before sending a probe-request. */
+       /* [in] */ u8 probe_delay;
+
+       /* Length of the SSID */
+       /* [in] */ u8 ssid_len;
+
+       /* SSID of the BSS or P2P_GO to be started now. */
+       /* [in] */ u8 ssid[32];
+
+       /* The basic supported rates for the MiniAP. */
+       /* [in] */ u32 basic_rate_set;
+};
+
+int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg);
+
+#define WSM_BEACON_TRANSMIT_REQ_ID 0x0018
+#define WSM_BEACON_TRANSMIT_RESP_ID 0x0418
+
+struct wsm_beacon_transmit {
+       /* 1: enable; 0: disable */
+       /* [in] */ u8 enable_beaconing;
+};
+
+int wsm_beacon_transmit(struct cw1200_common *priv,
+                       const struct wsm_beacon_transmit *arg);
+
+int wsm_start_find(struct cw1200_common *priv);
+
+int wsm_stop_find(struct cw1200_common *priv);
+
+typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status);
+
+struct wsm_suspend_resume {
+       /* See 3.52 */
+       /* Link ID */
+       /* [out] */ int link_id;
+       /* Stop sending further Tx requests down to device for this link */
+       /* [out] */ bool stop;
+       /* Transmit multicast Frames */
+       /* [out] */ bool multicast;
+       /* The AC on which Tx to be suspended /resumed. */
+       /* This is applicable only for U-APSD */
+       /* WSM_QUEUE_... */
+       /* [out] */ int queue;
+};
+
+typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv,
+                                      struct wsm_suspend_resume *arg);
+
+/* 3.54 Update-IE request. */
+struct wsm_update_ie {
+       /* WSM_UPDATE_IE_... */
+       /* [in] */ u16 what;
+       /* [in] */ u16 count;
+       /* [in] */ u8 *ies;
+       /* [in] */ size_t length;
+};
+
+int wsm_update_ie(struct cw1200_common *priv,
+                 const struct wsm_update_ie *arg);
+
+/* 3.56 */
+struct wsm_map_link {
+       /* MAC address of the remote device */
+       /* [in] */ u8 mac_addr[6];
+       /* [in] */ u8 link_id;
+};
+
+int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg);
+
+/* ******************************************************************** */
+/* MIB shortcats                                                       */
+
+static inline int wsm_set_output_power(struct cw1200_common *priv,
+                                      int power_level)
+{
+       __le32 val = __cpu_to_le32(power_level);
+       return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL,
+                            &val, sizeof(val));
+}
+
+static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv,
+                                              unsigned dtim_interval,
+                                              unsigned listen_interval)
+{
+       struct {
+               u8 numBeaconPeriods;
+               u8 reserved;
+               __le16 listenInterval;
+       } val = {
+               dtim_interval, 0, __cpu_to_le16(listen_interval)
+       };
+
+       if (dtim_interval > 0xFF || listen_interval > 0xFFFF)
+               return -EINVAL;
+       else
+               return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD,
+                                    &val, sizeof(val));
+}
+
+struct wsm_rcpi_rssi_threshold {
+       u8 rssiRcpiMode;        /* WSM_RCPI_RSSI_... */
+       u8 lowerThreshold;
+       u8 upperThreshold;
+       u8 rollingAverageCount;
+};
+
+static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv,
+                                       struct wsm_rcpi_rssi_threshold *arg)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg,
+                            sizeof(*arg));
+}
+
+struct wsm_mib_counters_table {
+       __le32 plcp_errors;
+       __le32 fcs_errors;
+       __le32 tx_packets;
+       __le32 rx_packets;
+       __le32 rx_packet_errors;
+       __le32 rx_decryption_failures;
+       __le32 rx_mic_failures;
+       __le32 rx_no_key_failures;
+       __le32 tx_multicast_frames;
+       __le32 tx_frames_success;
+       __le32 tx_frame_failures;
+       __le32 tx_frames_retried;
+       __le32 tx_frames_multi_retried;
+       __le32 rx_frame_duplicates;
+       __le32 rts_success;
+       __le32 rts_failures;
+       __le32 ack_failures;
+       __le32 rx_multicast_frames;
+       __le32 rx_frames_success;
+       __le32 rx_cmac_icv_errors;
+       __le32 rx_cmac_replays;
+       __le32 rx_mgmt_ccmp_replays;
+} __packed;
+
+static inline int wsm_get_counters_table(struct cw1200_common *priv,
+                                        struct wsm_mib_counters_table *arg)
+{
+       return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE,
+                           arg, sizeof(*arg));
+}
+
+static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac)
+{
+       return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN);
+}
+
+struct wsm_rx_filter {
+       bool promiscuous;
+       bool bssid;
+       bool fcs;
+       bool probeResponder;
+};
+
+static inline int wsm_set_rx_filter(struct cw1200_common *priv,
+                                   const struct wsm_rx_filter *arg)
+{
+       __le32 val = 0;
+       if (arg->promiscuous)
+               val |= __cpu_to_le32(BIT(0));
+       if (arg->bssid)
+               val |= __cpu_to_le32(BIT(1));
+       if (arg->fcs)
+               val |= __cpu_to_le32(BIT(2));
+       if (arg->probeResponder)
+               val |= __cpu_to_le32(BIT(3));
+       return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val));
+}
+
+int wsm_set_probe_responder(struct cw1200_common *priv, bool enable);
+
+#define WSM_BEACON_FILTER_IE_HAS_CHANGED       BIT(0)
+#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1)
+#define WSM_BEACON_FILTER_IE_HAS_APPEARED      BIT(2)
+
+struct wsm_beacon_filter_table_entry {
+       u8      ie_id;
+       u8      flags;
+       u8      oui[3];
+       u8      match_data[3];
+} __packed;
+
+struct wsm_mib_beacon_filter_table {
+       __le32 num;
+       struct wsm_beacon_filter_table_entry entry[10];
+} __packed;
+
+static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv,
+                                             struct wsm_mib_beacon_filter_table *ft)
+{
+       size_t size = __le32_to_cpu(ft->num) *
+                    sizeof(struct wsm_beacon_filter_table_entry) +
+                    sizeof(__le32);
+
+       return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size);
+}
+
+#define WSM_BEACON_FILTER_ENABLE       BIT(0) /* Enable/disable beacon filtering */
+#define WSM_BEACON_FILTER_AUTO_ERP     BIT(1) /* If 1 FW will handle ERP IE changes internally */
+
+struct wsm_beacon_filter_control {
+       int enabled;
+       int bcn_count;
+};
+
+static inline int wsm_beacon_filter_control(struct cw1200_common *priv,
+                                       struct wsm_beacon_filter_control *arg)
+{
+       struct {
+               __le32 enabled;
+               __le32 bcn_count;
+       } val;
+       val.enabled = __cpu_to_le32(arg->enabled);
+       val.bcn_count = __cpu_to_le32(arg->bcn_count);
+       return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val,
+                            sizeof(val));
+}
+
+enum wsm_power_mode {
+       wsm_power_mode_active = 0,
+       wsm_power_mode_doze = 1,
+       wsm_power_mode_quiescent = 2,
+};
+
+struct wsm_operational_mode {
+       enum wsm_power_mode power_mode;
+       int disable_more_flag_usage;
+       int perform_ant_diversity;
+};
+
+static inline int wsm_set_operational_mode(struct cw1200_common *priv,
+                                       const struct wsm_operational_mode *arg)
+{
+       u8 val = arg->power_mode;
+       if (arg->disable_more_flag_usage)
+               val |= BIT(4);
+       if (arg->perform_ant_diversity)
+               val |= BIT(5);
+       return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val,
+                            sizeof(val));
+}
+
+struct wsm_template_frame {
+       u8 frame_type;
+       u8 rate;
+       struct sk_buff *skb;
+};
+
+static inline int wsm_set_template_frame(struct cw1200_common *priv,
+                                        struct wsm_template_frame *arg)
+{
+       int ret;
+       u8 *p = skb_push(arg->skb, 4);
+       p[0] = arg->frame_type;
+       p[1] = arg->rate;
+       ((__le16 *)p)[1] = __cpu_to_le16(arg->skb->len - 4);
+       ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len);
+       skb_pull(arg->skb, 4);
+       return ret;
+}
+
+
+struct wsm_protected_mgmt_policy {
+       bool protectedMgmtEnable;
+       bool unprotectedMgmtFramesAllowed;
+       bool encryptionForAuthFrame;
+};
+
+static inline int wsm_set_protected_mgmt_policy(struct cw1200_common *priv,
+               struct wsm_protected_mgmt_policy *arg)
+{
+       __le32 val = 0;
+       int ret;
+       if (arg->protectedMgmtEnable)
+               val |= __cpu_to_le32(BIT(0));
+       if (arg->unprotectedMgmtFramesAllowed)
+               val |= __cpu_to_le32(BIT(1));
+       if (arg->encryptionForAuthFrame)
+               val |= __cpu_to_le32(BIT(2));
+       ret = wsm_write_mib(priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY,
+                       &val, sizeof(val));
+       return ret;
+}
+
+struct wsm_mib_block_ack_policy {
+       u8 tx_tid;
+       u8 reserved1;
+       u8 rx_tid;
+       u8 reserved2;
+} __packed;
+
+static inline int wsm_set_block_ack_policy(struct cw1200_common *priv,
+                                          u8 tx_tid_policy,
+                                          u8 rx_tid_policy)
+{
+       struct wsm_mib_block_ack_policy val = {
+               .tx_tid = tx_tid_policy,
+               .rx_tid = rx_tid_policy,
+       };
+       return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val,
+                            sizeof(val));
+}
+
+struct wsm_mib_association_mode {
+       u8 flags;               /* WSM_ASSOCIATION_MODE_... */
+       u8 preamble;    /* WSM_JOIN_PREAMBLE_... */
+       u8 greenfield;  /* 1 for greenfield */
+       u8 mpdu_start_spacing;
+       __le32 basic_rate_set;
+} __packed;
+
+static inline int wsm_set_association_mode(struct cw1200_common *priv,
+                                          struct wsm_mib_association_mode *arg)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg,
+                            sizeof(*arg));
+}
+
+#define WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED BIT(2)
+#define WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT BIT(3)
+struct wsm_tx_rate_retry_policy {
+       u8 index;
+       u8 short_retries;
+       u8 long_retries;
+       /* BIT(2) - Terminate retries when Tx rate retry policy
+        *          finishes.
+        * BIT(3) - Count initial frame transmission as part of
+        *          rate retry counting but not as a retry
+        *          attempt
+        */
+       u8 flags;
+       u8 rate_recoveries;
+       u8 reserved[3];
+       __le32 rate_count_indices[3];
+} __packed;
+
+struct wsm_set_tx_rate_retry_policy {
+       u8 num;
+       u8 reserved[3];
+       struct wsm_tx_rate_retry_policy tbl[8];
+} __packed;
+
+static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv,
+                               struct wsm_set_tx_rate_retry_policy *arg)
+{
+       size_t size = 4 + arg->num * sizeof(struct wsm_tx_rate_retry_policy);
+       return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg,
+                            size);
+}
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+struct wsm_ether_type_filter_hdr {
+       u8 num;         /* Up to WSM_MAX_FILTER_ELEMENTS */
+       u8 reserved[3];
+} __packed;
+
+struct wsm_ether_type_filter {
+       u8 action;      /* WSM_FILTER_ACTION_XXX */
+       u8 reserved;
+       __le16 type;    /* Type of ethernet frame */
+} __packed;
+
+static inline int wsm_set_ether_type_filter(struct cw1200_common *priv,
+                               struct wsm_ether_type_filter_hdr *arg)
+{
+       size_t size = sizeof(struct wsm_ether_type_filter_hdr) +
+               arg->num * sizeof(struct wsm_ether_type_filter);
+       return wsm_write_mib(priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER,
+               arg, size);
+}
+
+/* 4.33 SetUDPPortDataFrameFilter */
+struct wsm_udp_port_filter_hdr {
+       u8 num;         /* Up to WSM_MAX_FILTER_ELEMENTS */
+       u8 reserved[3];
+} __packed;
+
+struct wsm_udp_port_filter {
+       u8 action;      /* WSM_FILTER_ACTION_XXX */
+       u8 type;                /* WSM_FILTER_PORT_TYPE_XXX */
+       __le16 port;            /* Port number */
+} __packed;
+
+static inline int wsm_set_udp_port_filter(struct cw1200_common *priv,
+                               struct wsm_udp_port_filter_hdr *arg)
+{
+       size_t size = sizeof(struct wsm_udp_port_filter_hdr) +
+               arg->num * sizeof(struct wsm_udp_port_filter);
+       return wsm_write_mib(priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER,
+               arg, size);
+}
+
+/* Undocumented MIBs: */
+/* 4.35 P2PDeviceInfo */
+#define D11_MAX_SSID_LEN               (32)
+
+struct wsm_p2p_device_type {
+       __le16 category_id;
+       u8 oui[4];
+       __le16 subcategory_id;
+} __packed;
+
+struct wsm_p2p_device_info {
+       struct wsm_p2p_device_type primaryDevice;
+       u8 reserved1[3];
+       u8 devname_size;
+       u8 local_devname[D11_MAX_SSID_LEN];
+       u8 reserved2[3];
+       u8 num_secdev_supported;
+       struct wsm_p2p_device_type secdevs[0];
+} __packed;
+
+/* 4.36 SetWCDMABand - WO */
+struct wsm_cdma_band {
+       u8 wcdma_band;
+       u8 reserved[3];
+} __packed;
+
+/* 4.37 GroupTxSequenceCounter - RO */
+struct wsm_group_tx_seq {
+       __le32 bits_47_16;
+       __le16 bits_15_00;
+       __le16 reserved;
+} __packed;
+
+/* 4.39 SetHtProtection - WO */
+#define WSM_DUAL_CTS_PROT_ENB          (1 << 0)
+#define WSM_NON_GREENFIELD_STA_PRESENT  (1 << 1)
+#define WSM_HT_PROT_MODE__NO_PROT      (0 << 2)
+#define WSM_HT_PROT_MODE__NON_MEMBER   (1 << 2)
+#define WSM_HT_PROT_MODE__20_MHZ       (2 << 2)
+#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2)
+#define WSM_LSIG_TXOP_PROT_FULL                (1 << 4)
+#define WSM_LARGE_L_LENGTH_PROT                (1 << 5)
+
+struct wsm_ht_protection {
+       __le32 flags;
+} __packed;
+
+/* 4.40 GPIO Command - R/W */
+#define WSM_GPIO_COMMAND_SETUP 0
+#define WSM_GPIO_COMMAND_READ  1
+#define WSM_GPIO_COMMAND_WRITE 2
+#define WSM_GPIO_COMMAND_RESET 3
+#define WSM_GPIO_ALL_PINS      0xFF
+
+struct wsm_gpio_command {
+       u8 command;
+       u8 pin;
+       __le16 config;
+} __packed;
+
+/* 4.41 TSFCounter - RO */
+struct wsm_tsf_counter {
+       __le64 tsf_counter;
+} __packed;
+
+/* 4.43 Keep alive period */
+struct wsm_keep_alive_period {
+       __le16 period;
+       u8 reserved[2];
+} __packed;
+
+static inline int wsm_keep_alive_period(struct cw1200_common *priv,
+                                       int period)
+{
+       struct wsm_keep_alive_period arg = {
+               .period = __cpu_to_le16(period),
+       };
+       return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD,
+                       &arg, sizeof(arg));
+};
+
+/* BSSID filtering */
+struct wsm_set_bssid_filtering {
+       u8 filter;
+       u8 reserved[3];
+} __packed;
+
+static inline int wsm_set_bssid_filtering(struct cw1200_common *priv,
+                                         bool enabled)
+{
+       struct wsm_set_bssid_filtering arg = {
+               .filter = !enabled,
+       };
+       return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER,
+                       &arg, sizeof(arg));
+}
+
+/* Multicast filtering - 4.5 */
+struct wsm_mib_multicast_filter {
+       __le32 enable;
+       __le32 num_addrs;
+       u8 macaddrs[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN];
+} __packed;
+
+static inline int wsm_set_multicast_filter(struct cw1200_common *priv,
+                                          struct wsm_mib_multicast_filter *fp)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE,
+                            fp, sizeof(*fp));
+}
+
+/* ARP IPv4 filtering - 4.10 */
+struct wsm_mib_arp_ipv4_filter {
+       __le32 enable;
+       __be32 ipv4addrs[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES];
+} __packed;
+
+static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv,
+                                         struct wsm_mib_arp_ipv4_filter *fp)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE,
+                           fp, sizeof(*fp));
+}
+
+/* P2P Power Save Mode Info - 4.31 */
+struct wsm_p2p_ps_modeinfo {
+       u8      opp_ps_ct_window;
+       u8      count;
+       u8      reserved;
+       u8      dtim_count;
+       __le32  duration;
+       __le32  interval;
+       __le32  start_time;
+} __packed;
+
+static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv,
+                                         struct wsm_p2p_ps_modeinfo *mi)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+                            mi, sizeof(*mi));
+}
+
+static inline int wsm_get_p2p_ps_modeinfo(struct cw1200_common *priv,
+                                         struct wsm_p2p_ps_modeinfo *mi)
+{
+       return wsm_read_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+                           mi, sizeof(*mi));
+}
+
+/* UseMultiTxConfMessage */
+
+static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv,
+                                       bool enabled)
+{
+       __le32 arg = enabled ? __cpu_to_le32(1) : 0;
+
+       return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF,
+                       &arg, sizeof(arg));
+}
+
+
+/* 4.26 SetUpasdInformation */
+struct wsm_uapsd_info {
+       __le16 uapsd_flags;
+       __le16 min_auto_trigger_interval;
+       __le16 max_auto_trigger_interval;
+       __le16 auto_trigger_step;
+};
+
+static inline int wsm_set_uapsd_info(struct cw1200_common *priv,
+                                    struct wsm_uapsd_info *arg)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION,
+                               arg, sizeof(*arg));
+}
+
+/* 4.22 OverrideInternalTxRate */
+struct wsm_override_internal_txrate {
+       u8 internalTxRate;
+       u8 nonErpInternalTxRate;
+       u8 reserved[2];
+} __packed;
+
+static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv,
+                                    struct wsm_override_internal_txrate *arg)
+{
+       return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+                               arg, sizeof(*arg));
+}
+
+/* ******************************************************************** */
+/* WSM TX port control                                                 */
+
+void wsm_lock_tx(struct cw1200_common *priv);
+void wsm_lock_tx_async(struct cw1200_common *priv);
+bool wsm_flush_tx(struct cw1200_common *priv);
+void wsm_unlock_tx(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* WSM / BH API                                                                */
+
+int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len);
+int wsm_handle_rx(struct cw1200_common *priv, u16 id, struct wsm_hdr *wsm,
+                 struct sk_buff **skb_p);
+
+/* ******************************************************************** */
+/* wsm_buf API                                                         */
+
+struct wsm_buf {
+       u8 *begin;
+       u8 *data;
+       u8 *end;
+};
+
+void wsm_buf_init(struct wsm_buf *buf);
+void wsm_buf_deinit(struct wsm_buf *buf);
+
+/* ******************************************************************** */
+/* wsm_cmd API                                                         */
+
+struct wsm_cmd {
+       spinlock_t lock; /* Protect structure from multiple access */
+       int done;
+       u8 *ptr;
+       size_t len;
+       void *arg;
+       int ret;
+       u16 cmd;
+};
+
+/* ******************************************************************** */
+/* WSM TX buffer access                                                        */
+
+int wsm_get_tx(struct cw1200_common *priv, u8 **data,
+              size_t *tx_len, int *burst);
+void wsm_txed(struct cw1200_common *priv, u8 *data);
+
+/* ******************************************************************** */
+/* Queue mapping: WSM <---> linux                                      */
+/* Linux: VO VI BE BK                                                  */
+/* WSM:   BE BK VI VO                                                  */
+
+static inline u8 wsm_queue_id_to_linux(u8 queue_id)
+{
+       static const u8 queue_mapping[] = {
+               2, 3, 1, 0
+       };
+       return queue_mapping[queue_id];
+}
+
+static inline u8 wsm_queue_id_to_wsm(u8 queue_id)
+{
+       static const u8 queue_mapping[] = {
+               3, 2, 0, 1
+       };
+       return queue_mapping[queue_id];
+}
+
+#endif /* CW1200_HWIO_H_INCLUDED */
index 15920aa..f8ab193 100644 (file)
@@ -6242,8 +6242,6 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
        if ((val & 0x0000ff00) != 0)
                pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
 
-       pci_set_power_state(pci_dev, PCI_D0);
-
        if (!ipw2100_hw_is_adapter_in_system(dev)) {
                printk(KERN_WARNING DRV_NAME
                       "Device not found via register read.\n");
index 4ed5e45..6b823a1 100644 (file)
@@ -3548,6 +3548,7 @@ static int ipw_load(struct ipw_priv *priv)
                ipw_rx_queue_reset(priv, priv->rxq);
        if (!priv->rxq) {
                IPW_ERROR("Unable to initialize Rx queue\n");
+               rc = -ENOMEM;
                goto error;
        }
 
index 95a1ca1..9ffe659 100644 (file)
@@ -1195,7 +1195,7 @@ static int libipw_parse_info_param(struct libipw_info_element
 #ifdef CONFIG_LIBIPW_DEBUG
                                p += snprintf(p, sizeof(rates_str) -
                                              (p - rates_str), "%02X ",
-                                             network->rates[i]);
+                                             network->rates_ex[i]);
 #endif
                                if (libipw_is_ofdm_rate
                                    (info_element->data[i])) {
index b37a582..9581d07 100644 (file)
@@ -3119,7 +3119,7 @@ il3945_store_debug_level(struct device *d, struct device_attribute *attr,
        unsigned long val;
        int ret;
 
-       ret = strict_strtoul(buf, 0, &val);
+       ret = kstrtoul(buf, 0, &val);
        if (ret)
                IL_INFO("%s is not in hex or decimal form.\n", buf);
        else
@@ -3727,7 +3727,8 @@ il3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
         * 5. Setup HW Constants
         * ********************/
        /* Device-specific setup */
-       if (il3945_hw_set_hw_params(il)) {
+       err = il3945_hw_set_hw_params(il);
+       if (err) {
                IL_ERR("failed to set hw settings\n");
                goto out_eeprom_free;
        }
index dc1e6da..c092033 100644 (file)
@@ -331,6 +331,19 @@ il3945_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb)
                return;
        }
 
+       /*
+        * Firmware will not transmit frame on passive channel, if it not yet
+        * received some valid frame on that channel. When this error happen
+        * we have to wait until firmware will unblock itself i.e. when we
+        * note received beacon or other frame. We unblock queues in
+        * il3945_pass_packet_to_mac80211 or in il_mac_bss_info_changed.
+        */
+       if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) &&
+           il->iw_mode == NL80211_IFTYPE_STATION) {
+               il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Stopped queues - RX waiting on passive channel\n");
+       }
+
        txq->time_stamp = jiffies;
        info = IEEE80211_SKB_CB(txq->skbs[txq->q.read_ptr]);
        ieee80211_tx_info_clear_status(info);
@@ -488,6 +501,11 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb,
                return;
        }
 
+       if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) {
+               il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Woke queues - frame received on passive channel\n");
+       }
+
        skb = dev_alloc_skb(128);
        if (!skb) {
                IL_ERR("dev_alloc_skb failed\n");
index 9a95045..b9b2bb5 100644 (file)
@@ -588,6 +588,11 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
                return;
        }
 
+       if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) {
+               il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Woke queues - frame received on passive channel\n");
+       }
+
        /* In case of HW accelerated crypto and bad decryption, drop */
        if (!il->cfg->mod_params->sw_crypto &&
            il_set_decrypted_flag(il, hdr, ampdu_status, stats))
@@ -2806,6 +2811,19 @@ il4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb)
                return;
        }
 
+       /*
+        * Firmware will not transmit frame on passive channel, if it not yet
+        * received some valid frame on that channel. When this error happen
+        * we have to wait until firmware will unblock itself i.e. when we
+        * note received beacon or other frame. We unblock queues in
+        * il4965_pass_packet_to_mac80211 or in il_mac_bss_info_changed.
+        */
+       if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) &&
+           il->iw_mode == NL80211_IFTYPE_STATION) {
+               il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Stopped queues - RX waiting on passive channel\n");
+       }
+
        spin_lock_irqsave(&il->sta_lock, flags);
        if (txq->sched_retry) {
                const u32 scd_ssn = il4965_get_scd_ssn(tx_resp);
@@ -4567,7 +4585,7 @@ il4965_store_debug_level(struct device *d, struct device_attribute *attr,
        unsigned long val;
        int ret;
 
-       ret = strict_strtoul(buf, 0, &val);
+       ret = kstrtoul(buf, 0, &val);
        if (ret)
                IL_ERR("%s is not in hex or decimal form.\n", buf);
        else
@@ -4614,7 +4632,7 @@ il4965_store_tx_power(struct device *d, struct device_attribute *attr,
        unsigned long val;
        int ret;
 
-       ret = strict_strtoul(buf, 10, &val);
+       ret = kstrtoul(buf, 10, &val);
        if (ret)
                IL_INFO("%s is not in decimal form.\n", buf);
        else {
@@ -5741,7 +5759,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length)
        hw->flags =
            IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION |
            IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SPECTRUM_MGMT |
-           IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
+           IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS |
+           IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
        if (il->cfg->sku & IL_SKU_N)
                hw->flags |=
                    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
index 3b6c994..0484215 100644 (file)
@@ -1347,14 +1347,6 @@ struct il_rx_mpdu_res_start {
 #define TX_CMD_SEC_SHIFT       6
 #define TX_CMD_SEC_KEY128      0x08
 
-/*
- * security overhead sizes
- */
-#define WEP_IV_LEN 4
-#define WEP_ICV_LEN 4
-#define CCMP_MIC_LEN 8
-#define TKIP_ICV_LEN 4
-
 /*
  * C_TX = 0x1c (command)
  */
index e9a3cbc..3195aad 100644 (file)
@@ -5306,6 +5306,17 @@ il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        if (changes & BSS_CHANGED_BSSID) {
                D_MAC80211("BSSID %pM\n", bss_conf->bssid);
 
+               /*
+                * On passive channel we wait with blocked queues to see if
+                * there is traffic on that channel. If no frame will be
+                * received (what is very unlikely since scan detects AP on
+                * that channel, but theoretically possible), mac80211 associate
+                * procedure will time out and mac80211 will call us with NULL
+                * bssid. We have to unblock queues on such condition.
+                */
+               if (is_zero_ether_addr(bss_conf->bssid))
+                       il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+
                /*
                 * If there is currently a HW scan going on in the background,
                 * then we need to cancel it, otherwise sometimes we are not
index 4caaf52..83f8ed8 100644 (file)
@@ -1299,6 +1299,8 @@ struct il_priv {
        /* queue refcounts */
 #define IL_MAX_HW_QUEUES       32
        unsigned long queue_stopped[BITS_TO_LONGS(IL_MAX_HW_QUEUES)];
+#define IL_STOP_REASON_PASSIVE 0
+       unsigned long stop_reason;
        /* for each AC */
        atomic_t queue_stop_count[4];
 
@@ -2256,6 +2258,19 @@ il_set_swq_id(struct il_tx_queue *txq, u8 ac, u8 hwq)
        txq->swq_id = (hwq << 2) | ac;
 }
 
+static inline void
+_il_wake_queue(struct il_priv *il, u8 ac)
+{
+       if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0)
+               ieee80211_wake_queue(il->hw, ac);
+}
+
+static inline void
+_il_stop_queue(struct il_priv *il, u8 ac)
+{
+       if (atomic_inc_return(&il->queue_stop_count[ac]) > 0)
+               ieee80211_stop_queue(il->hw, ac);
+}
 static inline void
 il_wake_queue(struct il_priv *il, struct il_tx_queue *txq)
 {
@@ -2264,8 +2279,7 @@ il_wake_queue(struct il_priv *il, struct il_tx_queue *txq)
        u8 hwq = (queue >> 2) & 0x1f;
 
        if (test_and_clear_bit(hwq, il->queue_stopped))
-               if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0)
-                       ieee80211_wake_queue(il->hw, ac);
+               _il_wake_queue(il, ac);
 }
 
 static inline void
@@ -2276,8 +2290,27 @@ il_stop_queue(struct il_priv *il, struct il_tx_queue *txq)
        u8 hwq = (queue >> 2) & 0x1f;
 
        if (!test_and_set_bit(hwq, il->queue_stopped))
-               if (atomic_inc_return(&il->queue_stop_count[ac]) > 0)
-                       ieee80211_stop_queue(il->hw, ac);
+               _il_stop_queue(il, ac);
+}
+
+static inline void
+il_wake_queues_by_reason(struct il_priv *il, int reason)
+{
+       u8 ac;
+
+       if (test_and_clear_bit(reason, &il->stop_reason))
+               for (ac = 0; ac < 4; ac++)
+                       _il_wake_queue(il, ac);
+}
+
+static inline void
+il_stop_queues_by_reason(struct il_priv *il, int reason)
+{
+       u8 ac;
+
+       if (!test_and_set_bit(reason, &il->stop_reason))
+               for (ac = 0; ac < 4; ac++)
+                       _il_stop_queue(il, ac);
 }
 
 #ifdef ieee80211_stop_queue
index 56c2040..cbaa5c2 100644 (file)
@@ -128,16 +128,6 @@ config IWLWIFI_DEVICE_TRACING
          occur.
 endmenu
 
-config IWLWIFI_DEVICE_TESTMODE
-       def_bool y
-       depends on IWLWIFI
-       depends on NL80211_TESTMODE
-       help
-         This option enables the testmode support for iwlwifi device through
-         NL80211_TESTMODE. This provide the capabilities of enable user space
-         validation applications to interacts with the device through the
-         generic netlink message via NL80211_TESTMODE channel.
-
 config IWLWIFI_P2P
        def_bool y
        bool "iwlwifi experimental P2P support"
index 3b5613e..1fa6442 100644 (file)
@@ -7,14 +7,15 @@ iwlwifi-objs          += iwl-notif-wait.o
 iwlwifi-objs           += iwl-eeprom-read.o iwl-eeprom-parse.o
 iwlwifi-objs           += iwl-phy-db.o iwl-nvm-parse.o
 iwlwifi-objs           += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
-iwlwifi-objs           += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o iwl-7000.o
+iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o
+iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o
+
+iwlwifi-objs += $(iwlwifi-m)
 
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
-iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o
 
 ccflags-y += -D__CHECK_ENDIAN__ -I$(src)
 
-
 obj-$(CONFIG_IWLDVM)   += dvm/
 obj-$(CONFIG_IWLMVM)   += mvm/
 
index 5ff76b2..dce7ab2 100644 (file)
@@ -8,6 +8,5 @@ iwldvm-objs             += scan.o led.o
 iwldvm-objs            += rxon.o devices.o
 
 iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
-iwldvm-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += testmode.o
 
 ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
index 48545ab..1835511 100644 (file)
 #define IWL_INVALID_STATION    255
 
 /* device operations */
-extern struct iwl_lib_ops iwl1000_lib;
-extern struct iwl_lib_ops iwl2000_lib;
-extern struct iwl_lib_ops iwl2030_lib;
-extern struct iwl_lib_ops iwl5000_lib;
-extern struct iwl_lib_ops iwl5150_lib;
-extern struct iwl_lib_ops iwl6000_lib;
-extern struct iwl_lib_ops iwl6030_lib;
+extern const struct iwl_dvm_cfg iwl_dvm_1000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_2000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_105_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_2030_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_5000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_5150_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6000_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6005_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6050_cfg;
+extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg;
 
 
 #define TIME_UNIT              1024
@@ -291,8 +294,8 @@ void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena);
 
 static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv)
 {
-       return priv->cfg->bt_params &&
-              priv->cfg->bt_params->advanced_bt_coexist;
+       return priv->lib->bt_params &&
+              priv->lib->bt_params->advanced_bt_coexist;
 }
 
 #ifdef CONFIG_IWLWIFI_DEBUG
@@ -402,43 +405,6 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags)
 
 extern int iwl_alive_start(struct iwl_priv *priv);
 
-/* testmode support */
-#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
-
-extern int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data,
-                                  int len);
-extern int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw,
-                                   struct sk_buff *skb,
-                                   struct netlink_callback *cb,
-                                   void *data, int len);
-extern void iwl_testmode_init(struct iwl_priv *priv);
-extern void iwl_testmode_free(struct iwl_priv *priv);
-
-#else
-
-static inline
-int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
-{
-       return -ENOSYS;
-}
-
-static inline
-int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
-                     struct netlink_callback *cb,
-                     void *data, int len)
-{
-       return -ENOSYS;
-}
-
-static inline void iwl_testmode_init(struct iwl_priv *priv)
-{
-}
-
-static inline void iwl_testmode_free(struct iwl_priv *priv)
-{
-}
-#endif
-
 #ifdef CONFIG_IWLWIFI_DEBUG
 void iwl_print_rx_config_cmd(struct iwl_priv *priv,
                             enum iwl_rxon_context_id ctxid);
index d6c4cf2..1b0f0d5 100644 (file)
@@ -521,7 +521,7 @@ static int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
 
        iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]);
 
-       if (priv->cfg->base_params->hd_v2) {
+       if (priv->lib->hd_v2) {
                cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
                        HD_INA_NON_SQUARE_DET_OFDM_DATA_V2;
                cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
@@ -895,7 +895,7 @@ static void iwlagn_gain_computation(struct iwl_priv *priv,
                        continue;
                }
 
-               delta_g = (priv->cfg->base_params->chain_noise_scale *
+               delta_g = (priv->lib->chain_noise_scale *
                        ((s32)average_noise[default_chain] -
                        (s32)average_noise[i])) / 1500;
 
@@ -1051,8 +1051,8 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv)
                return;
 
        /* Analyze signal for disconnected antenna */
-       if (priv->cfg->bt_params &&
-           priv->cfg->bt_params->advanced_bt_coexist) {
+       if (priv->lib->bt_params &&
+           priv->lib->bt_params->advanced_bt_coexist) {
                /* Disable disconnected antenna algorithm for advanced
                   bt coex, assuming valid antennas are connected */
                data->active_chains = priv->nvm_data->valid_rx_ant;
index 95ca026..ebdac90 100644 (file)
@@ -838,10 +838,6 @@ struct iwl_qosparam_cmd {
 #define STA_MODIFY_DELBA_TID_MSK       0x10
 #define STA_MODIFY_SLEEP_TX_COUNT_MSK  0x20
 
-/* Receiver address (actually, Rx station's index into station table),
- * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
-#define BUILD_RAxTID(sta_id, tid)      (((sta_id) << 4) + (tid))
-
 /* agn */
 struct iwl_keyinfo {
        __le16 key_flags;
@@ -1224,14 +1220,6 @@ struct iwl_rx_mpdu_res_start {
 #define TX_CMD_SEC_SHIFT       6
 #define TX_CMD_SEC_KEY128      0x08
 
-/*
- * security overhead sizes
- */
-#define WEP_IV_LEN 4
-#define WEP_ICV_LEN 4
-#define CCMP_MIC_LEN 8
-#define TKIP_ICV_LEN 4
-
 /*
  * REPLY_TX = 0x1c (command)
  */
index 71ea775..60a4e0d 100644 (file)
@@ -52,8 +52,6 @@
 #include "rs.h"
 #include "tt.h"
 
-#include "iwl-test.h"
-
 /* CT-KILL constants */
 #define CT_KILL_THRESHOLD_LEGACY   110 /* in Celsius */
 #define CT_KILL_THRESHOLD         114 /* in Celsius */
@@ -568,16 +566,61 @@ struct iwl_hw_params {
        const struct iwl_sensitivity_ranges *sens;
 };
 
-struct iwl_lib_ops {
-       /* set hw dependent parameters */
+/**
+ * struct iwl_dvm_bt_params - DVM specific BT (coex) parameters
+ * @advanced_bt_coexist: support advanced bt coexist
+ * @bt_init_traffic_load: specify initial bt traffic load
+ * @bt_prio_boost: default bt priority boost value
+ * @agg_time_limit: maximum number of uSec in aggregation
+ * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode
+ */
+struct iwl_dvm_bt_params {
+       bool advanced_bt_coexist;
+       u8 bt_init_traffic_load;
+       u32 bt_prio_boost;
+       u16 agg_time_limit;
+       bool bt_sco_disable;
+       bool bt_session_2;
+};
+
+/**
+ * struct iwl_dvm_cfg - DVM firmware specific device configuration
+ * @set_hw_params: set hardware parameters
+ * @set_channel_switch: send channel switch command
+ * @nic_config: apply device specific configuration
+ * @temperature: read temperature
+ * @adv_thermal_throttle: support advance thermal throttle
+ * @support_ct_kill_exit: support ct kill exit condition
+ * @plcp_delta_threshold: plcp error rate threshold used to trigger
+ *     radio tuning when there is a high receiving plcp error rate
+ * @chain_noise_scale: default chain noise scale used for gain computation
+ * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
+ * @no_idle_support: do not support idle mode
+ * @bt_params: pointer to BT parameters
+ * @need_temp_offset_calib: need to perform temperature offset calibration
+ * @no_xtal_calib: some devices do not need crystal calibration data,
+ *     don't send it to those
+ * @temp_offset_v2: support v2 of temperature offset calibration
+ * @adv_pm: advanced power management
+ */
+struct iwl_dvm_cfg {
        void (*set_hw_params)(struct iwl_priv *priv);
        int (*set_channel_switch)(struct iwl_priv *priv,
                                  struct ieee80211_channel_switch *ch_switch);
-       /* device specific configuration */
        void (*nic_config)(struct iwl_priv *priv);
-
-       /* temperature */
        void (*temperature)(struct iwl_priv *priv);
+
+       const struct iwl_dvm_bt_params *bt_params;
+       s32 chain_noise_scale;
+       u8 plcp_delta_threshold;
+       bool adv_thermal_throttle;
+       bool support_ct_kill_exit;
+       bool hd_v2;
+       bool no_idle_support;
+       bool need_temp_offset_calib;
+       bool no_xtal_calib;
+       bool temp_offset_v2;
+       bool adv_pm;
 };
 
 struct iwl_wipan_noa_data {
@@ -610,7 +653,7 @@ struct iwl_priv {
        struct device *dev;             /* for debug prints only */
        const struct iwl_cfg *cfg;
        const struct iwl_fw *fw;
-       const struct iwl_lib_ops *lib;
+       const struct iwl_dvm_cfg *lib;
        unsigned long status;
 
        spinlock_t sta_lock;
@@ -646,10 +689,6 @@ struct iwl_priv {
        struct iwl_spectrum_notification measure_report;
        u8 measurement_status;
 
-#define IWL_OWNERSHIP_DRIVER   0
-#define IWL_OWNERSHIP_TM       1
-       u8 ucode_owner;
-
        /* ucode beacon time */
        u32 ucode_beacon_time;
        int missed_beacon_threshold;
@@ -844,7 +883,7 @@ struct iwl_priv {
 #endif /* CONFIG_IWLWIFI_DEBUGFS */
 
        struct iwl_nvm_data *nvm_data;
-       /* eeprom blob for debugfs/testmode */
+       /* eeprom blob for debugfs */
        u8 *eeprom_blob;
        size_t eeprom_blob_size;
 
@@ -860,16 +899,14 @@ struct iwl_priv {
        unsigned long blink_on, blink_off;
        bool led_registered;
 
-#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
-       struct iwl_test tst;
-       u32 tm_fixed_rate;
-#endif
-
        /* WoWLAN GTK rekey data */
        u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
        __le64 replay_ctr;
        __le16 last_seq_ctl;
        bool have_rekey_data;
+#ifdef CONFIG_PM_SLEEP
+       struct wiphy_wowlan_support wowlan_support;
+#endif
 
        /* device_pointers: pointers to ucode event tables */
        struct {
index c48907c..352c6cb 100644 (file)
@@ -174,10 +174,13 @@ static void iwl1000_hw_set_hw_params(struct iwl_priv *priv)
        priv->hw_params.sens = &iwl1000_sensitivity;
 }
 
-struct iwl_lib_ops iwl1000_lib = {
+const struct iwl_dvm_cfg iwl_dvm_1000_cfg = {
        .set_hw_params = iwl1000_hw_set_hw_params,
        .nic_config = iwl1000_nic_config,
        .temperature = iwlagn_temperature,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 
@@ -232,16 +235,56 @@ static void iwl2000_hw_set_hw_params(struct iwl_priv *priv)
        priv->hw_params.sens = &iwl2000_sensitivity;
 }
 
-struct iwl_lib_ops iwl2000_lib = {
+const struct iwl_dvm_cfg iwl_dvm_2000_cfg = {
        .set_hw_params = iwl2000_hw_set_hw_params,
        .nic_config = iwl2000_nic_config,
        .temperature = iwlagn_temperature,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .hd_v2 = true,
+       .need_temp_offset_calib = true,
+       .temp_offset_v2 = true,
 };
 
-struct iwl_lib_ops iwl2030_lib = {
+const struct iwl_dvm_cfg iwl_dvm_105_cfg = {
        .set_hw_params = iwl2000_hw_set_hw_params,
        .nic_config = iwl2000_nic_config,
        .temperature = iwlagn_temperature,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .hd_v2 = true,
+       .need_temp_offset_calib = true,
+       .temp_offset_v2 = true,
+       .adv_pm = true,
+};
+
+static const struct iwl_dvm_bt_params iwl2030_bt_params = {
+       /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
+       .advanced_bt_coexist = true,
+       .agg_time_limit = BT_AGG_THRESHOLD_DEF,
+       .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
+       .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32,
+       .bt_sco_disable = true,
+       .bt_session_2 = true,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_2030_cfg = {
+       .set_hw_params = iwl2000_hw_set_hw_params,
+       .nic_config = iwl2000_nic_config,
+       .temperature = iwlagn_temperature,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .hd_v2 = true,
+       .bt_params = &iwl2030_bt_params,
+       .need_temp_offset_calib = true,
+       .temp_offset_v2 = true,
+       .adv_pm = true,
 };
 
 /*
@@ -420,16 +463,23 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
        return iwl_dvm_send_cmd(priv, &hcmd);
 }
 
-struct iwl_lib_ops iwl5000_lib = {
+const struct iwl_dvm_cfg iwl_dvm_5000_cfg = {
        .set_hw_params = iwl5000_hw_set_hw_params,
        .set_channel_switch = iwl5000_hw_channel_switch,
        .temperature = iwlagn_temperature,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .no_idle_support = true,
 };
 
-struct iwl_lib_ops iwl5150_lib = {
+const struct iwl_dvm_cfg iwl_dvm_5150_cfg = {
        .set_hw_params = iwl5150_hw_set_hw_params,
        .set_channel_switch = iwl5000_hw_channel_switch,
        .temperature = iwl5150_temperature,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .no_idle_support = true,
+       .no_xtal_calib = true,
 };
 
 
@@ -584,16 +634,59 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
        return err;
 }
 
-struct iwl_lib_ops iwl6000_lib = {
+const struct iwl_dvm_cfg iwl_dvm_6000_cfg = {
        .set_hw_params = iwl6000_hw_set_hw_params,
        .set_channel_switch = iwl6000_hw_channel_switch,
        .nic_config = iwl6000_nic_config,
        .temperature = iwlagn_temperature,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_6005_cfg = {
+       .set_hw_params = iwl6000_hw_set_hw_params,
+       .set_channel_switch = iwl6000_hw_channel_switch,
+       .nic_config = iwl6000_nic_config,
+       .temperature = iwlagn_temperature,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .need_temp_offset_calib = true,
+};
+
+const struct iwl_dvm_cfg iwl_dvm_6050_cfg = {
+       .set_hw_params = iwl6000_hw_set_hw_params,
+       .set_channel_switch = iwl6000_hw_channel_switch,
+       .nic_config = iwl6000_nic_config,
+       .temperature = iwlagn_temperature,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1500,
+};
+
+static const struct iwl_dvm_bt_params iwl6000_bt_params = {
+       /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
+       .advanced_bt_coexist = true,
+       .agg_time_limit = BT_AGG_THRESHOLD_DEF,
+       .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
+       .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT,
+       .bt_sco_disable = true,
 };
 
-struct iwl_lib_ops iwl6030_lib = {
+const struct iwl_dvm_cfg iwl_dvm_6030_cfg = {
        .set_hw_params = iwl6000_hw_set_hw_params,
        .set_channel_switch = iwl6000_hw_channel_switch,
        .nic_config = iwl6000_nic_config,
        .temperature = iwlagn_temperature,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .bt_params = &iwl6000_bt_params,
+       .need_temp_offset_calib = true,
+       .adv_pm = true,
 };
index 54f5533..3d5bdc4 100644 (file)
@@ -254,23 +254,23 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
        BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) !=
                        sizeof(basic.bt3_lookup_table));
 
-       if (priv->cfg->bt_params) {
+       if (priv->lib->bt_params) {
                /*
                 * newer generation of devices (2000 series and newer)
                 * use the version 2 of the bt command
                 * we need to make sure sending the host command
                 * with correct data structure to avoid uCode assert
                 */
-               if (priv->cfg->bt_params->bt_session_2) {
+               if (priv->lib->bt_params->bt_session_2) {
                        bt_cmd_v2.prio_boost = cpu_to_le32(
-                               priv->cfg->bt_params->bt_prio_boost);
+                               priv->lib->bt_params->bt_prio_boost);
                        bt_cmd_v2.tx_prio_boost = 0;
                        bt_cmd_v2.rx_prio_boost = 0;
                } else {
                        /* older version only has 8 bits */
-                       WARN_ON(priv->cfg->bt_params->bt_prio_boost & ~0xFF);
+                       WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF);
                        bt_cmd_v1.prio_boost =
-                               priv->cfg->bt_params->bt_prio_boost;
+                               priv->lib->bt_params->bt_prio_boost;
                        bt_cmd_v1.tx_prio_boost = 0;
                        bt_cmd_v1.rx_prio_boost = 0;
                }
@@ -330,7 +330,7 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
                       priv->bt_full_concurrent ?
                       "full concurrency" : "3-wire");
 
-       if (priv->cfg->bt_params->bt_session_2) {
+       if (priv->lib->bt_params->bt_session_2) {
                memcpy(&bt_cmd_v2.basic, &basic,
                        sizeof(basic));
                ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
@@ -758,8 +758,8 @@ static bool is_single_rx_stream(struct iwl_priv *priv)
  */
 static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
 {
-       if (priv->cfg->bt_params &&
-           priv->cfg->bt_params->advanced_bt_coexist &&
+       if (priv->lib->bt_params &&
+           priv->lib->bt_params->advanced_bt_coexist &&
            (priv->bt_full_concurrent ||
             priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
                /*
@@ -830,8 +830,8 @@ void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
        else
                active_chains = priv->nvm_data->valid_rx_ant;
 
-       if (priv->cfg->bt_params &&
-           priv->cfg->bt_params->advanced_bt_coexist &&
+       if (priv->lib->bt_params &&
+           priv->lib->bt_params->advanced_bt_coexist &&
            (priv->bt_full_concurrent ||
             priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
                /*
@@ -1288,12 +1288,6 @@ int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
        if (!(cmd->flags & CMD_ASYNC))
                lockdep_assert_held(&priv->mutex);
 
-       if (priv->ucode_owner == IWL_OWNERSHIP_TM &&
-           !(cmd->flags & CMD_ON_DEMAND)) {
-               IWL_DEBUG_HC(priv, "tm own the uCode, no regular hcmd send\n");
-               return -EIO;
-       }
-
        return iwl_trans_send_cmd(priv->trans, cmd);
 }
 
index cab23af..822f1a0 100644 (file)
@@ -208,20 +208,21 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
            priv->trans->ops->d3_suspend &&
            priv->trans->ops->d3_resume &&
            device_can_wakeup(priv->trans->dev)) {
-               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                                         WIPHY_WOWLAN_DISCONNECT |
-                                         WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-                                         WIPHY_WOWLAN_RFKILL_RELEASE;
+               priv->wowlan_support.flags = WIPHY_WOWLAN_MAGIC_PKT |
+                                            WIPHY_WOWLAN_DISCONNECT |
+                                            WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+                                            WIPHY_WOWLAN_RFKILL_RELEASE;
                if (!iwlwifi_mod_params.sw_crypto)
-                       hw->wiphy->wowlan.flags |=
+                       priv->wowlan_support.flags |=
                                WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
                                WIPHY_WOWLAN_GTK_REKEY_FAILURE;
 
-               hw->wiphy->wowlan.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS;
-               hw->wiphy->wowlan.pattern_min_len =
+               priv->wowlan_support.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS;
+               priv->wowlan_support.pattern_min_len =
                                        IWLAGN_WOWLAN_MIN_PATTERN_LEN;
-               hw->wiphy->wowlan.pattern_max_len =
+               priv->wowlan_support.pattern_max_len =
                                        IWLAGN_WOWLAN_MAX_PATTERN_LEN;
+               hw->wiphy->wowlan = &priv->wowlan_support;
        }
 #endif
 
@@ -426,7 +427,11 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
        if (ret)
                goto error;
 
-       iwl_trans_d3_suspend(priv->trans);
+       /* let the ucode operate on its own */
+       iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET,
+                   CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
+
+       iwl_trans_d3_suspend(priv->trans, false);
 
        goto out;
 
@@ -500,7 +505,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
        /* we'll clear ctx->vif during iwlagn_prepare_restart() */
        vif = ctx->vif;
 
-       ret = iwl_trans_d3_resume(priv->trans, &d3_status);
+       ret = iwl_trans_d3_resume(priv->trans, &d3_status, false);
        if (ret)
                goto out_unlock;
 
@@ -509,6 +514,10 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
                goto out_unlock;
        }
 
+       /* uCode is no longer operating by itself */
+       iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
+                   CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
+
        base = priv->device_pointers.error_event_table;
        if (!iwlagn_hw_valid_rtc_data_addr(base)) {
                IWL_WARN(priv, "Invalid error table during resume!\n");
@@ -1276,8 +1285,8 @@ static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
        IWL_DEBUG_MAC80211(priv, "enter\n");
        mutex_lock(&priv->mutex);
 
-       if (priv->cfg->bt_params &&
-                       priv->cfg->bt_params->advanced_bt_coexist) {
+       if (priv->lib->bt_params &&
+           priv->lib->bt_params->advanced_bt_coexist) {
                if (rssi_event == RSSI_EVENT_LOW)
                        priv->bt_enable_pspoll = true;
                else if (rssi_event == RSSI_EVENT_HIGH)
@@ -1387,7 +1396,7 @@ static int iwl_setup_interface(struct iwl_priv *priv,
                return err;
        }
 
-       if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist &&
+       if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist &&
            vif->type == NL80211_IFTYPE_ADHOC) {
                /*
                 * pretend to have high BT traffic as long as we
@@ -1757,8 +1766,6 @@ struct ieee80211_ops iwlagn_hw_ops = {
        .remain_on_channel = iwlagn_mac_remain_on_channel,
        .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel,
        .rssi_callback = iwlagn_mac_rssi_callback,
-       CFG80211_TESTMODE_CMD(iwlagn_mac_testmode_cmd)
-       CFG80211_TESTMODE_DUMP(iwlagn_mac_testmode_dump)
        .set_tim = iwlagn_mac_set_tim,
 };
 
index 74d7572..3952ddf 100644 (file)
@@ -615,7 +615,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
 
        priv->thermal_throttle.ct_kill_toggle = false;
 
-       if (priv->cfg->base_params->support_ct_kill_exit) {
+       if (priv->lib->support_ct_kill_exit) {
                adv_cmd.critical_temperature_enter =
                        cpu_to_le32(priv->hw_params.ct_kill_threshold);
                adv_cmd.critical_temperature_exit =
@@ -732,10 +732,10 @@ int iwl_alive_start(struct iwl_priv *priv)
        }
 
        /* download priority table before any calibration request */
-       if (priv->cfg->bt_params &&
-           priv->cfg->bt_params->advanced_bt_coexist) {
+       if (priv->lib->bt_params &&
+           priv->lib->bt_params->advanced_bt_coexist) {
                /* Configure Bluetooth device coexistence support */
-               if (priv->cfg->bt_params->bt_sco_disable)
+               if (priv->lib->bt_params->bt_sco_disable)
                        priv->bt_enable_pspoll = false;
                else
                        priv->bt_enable_pspoll = true;
@@ -873,9 +873,9 @@ void iwl_down(struct iwl_priv *priv)
        priv->bt_status = 0;
        priv->cur_rssi_ctx = NULL;
        priv->bt_is_sco = 0;
-       if (priv->cfg->bt_params)
+       if (priv->lib->bt_params)
                priv->bt_traffic_load =
-                        priv->cfg->bt_params->bt_init_traffic_load;
+                        priv->lib->bt_params->bt_init_traffic_load;
        else
                priv->bt_traffic_load = 0;
        priv->bt_full_concurrent = false;
@@ -1058,7 +1058,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
 
        iwl_setup_scan_deferred_work(priv);
 
-       if (priv->cfg->bt_params)
+       if (priv->lib->bt_params)
                iwlagn_bt_setup_deferred_work(priv);
 
        init_timer(&priv->statistics_periodic);
@@ -1072,7 +1072,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
 
 void iwl_cancel_deferred_work(struct iwl_priv *priv)
 {
-       if (priv->cfg->bt_params)
+       if (priv->lib->bt_params)
                iwlagn_bt_cancel_deferred_work(priv);
 
        cancel_work_sync(&priv->run_time_calib_work);
@@ -1098,16 +1098,13 @@ static int iwl_init_drv(struct iwl_priv *priv)
 
        priv->band = IEEE80211_BAND_2GHZ;
 
-       priv->plcp_delta_threshold =
-               priv->cfg->base_params->plcp_delta_threshold;
+       priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold;
 
        priv->iw_mode = NL80211_IFTYPE_STATION;
        priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
        priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
        priv->agg_tids_count = 0;
 
-       priv->ucode_owner = IWL_OWNERSHIP_DRIVER;
-
        priv->rx_statistics_jiffies = jiffies;
 
        /* Choose which receivers/antennas to use */
@@ -1116,8 +1113,8 @@ static int iwl_init_drv(struct iwl_priv *priv)
        iwl_init_scan_params(priv);
 
        /* init bt coex */
-       if (priv->cfg->bt_params &&
-           priv->cfg->bt_params->advanced_bt_coexist) {
+       if (priv->lib->bt_params &&
+           priv->lib->bt_params->advanced_bt_coexist) {
                priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
                priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
                priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
@@ -1173,12 +1170,6 @@ static void iwl_option_config(struct iwl_priv *priv)
        IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n");
 #endif
 
-#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
-       IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE enabled\n");
-#else
-       IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE disabled\n");
-#endif
-
 #ifdef CONFIG_IWLWIFI_P2P
        IWL_INFO(priv, "CONFIG_IWLWIFI_P2P enabled\n");
 #else
@@ -1264,31 +1255,37 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
        switch (priv->cfg->device_family) {
        case IWL_DEVICE_FAMILY_1000:
        case IWL_DEVICE_FAMILY_100:
-               priv->lib = &iwl1000_lib;
+               priv->lib = &iwl_dvm_1000_cfg;
                break;
        case IWL_DEVICE_FAMILY_2000:
+               priv->lib = &iwl_dvm_2000_cfg;
+               break;
        case IWL_DEVICE_FAMILY_105:
-               priv->lib = &iwl2000_lib;
+               priv->lib = &iwl_dvm_105_cfg;
                break;
        case IWL_DEVICE_FAMILY_2030:
        case IWL_DEVICE_FAMILY_135:
-               priv->lib = &iwl2030_lib;
+               priv->lib = &iwl_dvm_2030_cfg;
                break;
        case IWL_DEVICE_FAMILY_5000:
-               priv->lib = &iwl5000_lib;
+               priv->lib = &iwl_dvm_5000_cfg;
                break;
        case IWL_DEVICE_FAMILY_5150:
-               priv->lib = &iwl5150_lib;
+               priv->lib = &iwl_dvm_5150_cfg;
                break;
        case IWL_DEVICE_FAMILY_6000:
-       case IWL_DEVICE_FAMILY_6005:
        case IWL_DEVICE_FAMILY_6000i:
+               priv->lib = &iwl_dvm_6000_cfg;
+               break;
+       case IWL_DEVICE_FAMILY_6005:
+               priv->lib = &iwl_dvm_6005_cfg;
+               break;
        case IWL_DEVICE_FAMILY_6050:
        case IWL_DEVICE_FAMILY_6150:
-               priv->lib = &iwl6000_lib;
+               priv->lib = &iwl_dvm_6050_cfg;
                break;
        case IWL_DEVICE_FAMILY_6030:
-               priv->lib = &iwl6030_lib;
+               priv->lib = &iwl_dvm_6030_cfg;
                break;
        default:
                break;
@@ -1350,8 +1347,8 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
                        IWL_BT_ANTENNA_COUPLING_THRESHOLD) ?
                        true : false;
 
-       /* enable/disable bt channel inhibition */
-       priv->bt_ch_announce = iwlwifi_mod_params.bt_ch_announce;
+       /* bt channel inhibition enabled*/
+       priv->bt_ch_announce = true;
        IWL_DEBUG_INFO(priv, "BT channel inhibition is %s\n",
                       (priv->bt_ch_announce) ? "On" : "Off");
 
@@ -1446,7 +1443,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
         ********************/
        iwl_setup_deferred_work(priv);
        iwl_setup_rx_handlers(priv);
-       iwl_testmode_init(priv);
 
        iwl_power_initialize(priv);
        iwl_tt_initialize(priv);
@@ -1483,7 +1479,6 @@ out_mac80211_unregister:
        iwlagn_mac_unregister(priv);
 out_destroy_workqueue:
        iwl_tt_exit(priv);
-       iwl_testmode_free(priv);
        iwl_cancel_deferred_work(priv);
        destroy_workqueue(priv->workqueue);
        priv->workqueue = NULL;
@@ -1505,7 +1500,6 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode)
 
        IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n");
 
-       iwl_testmode_free(priv);
        iwlagn_mac_unregister(priv);
 
        iwl_tt_exit(priv);
@@ -1854,14 +1848,9 @@ int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
                return pos;
        }
 
-#ifdef CONFIG_IWLWIFI_DEBUG
        if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log)
                size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
                        ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#else
-       size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
-               ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#endif
        IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n",
                size);
 
@@ -1905,10 +1894,8 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
        unsigned int reload_msec;
        unsigned long reload_jiffies;
 
-#ifdef CONFIG_IWLWIFI_DEBUG
        if (iwl_have_debug_level(IWL_DL_FW_ERRORS))
                iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS);
-#endif
 
        /* uCode is no longer loaded. */
        priv->ucode_loaded = false;
index bd69018..77cb597 100644 (file)
@@ -163,7 +163,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
        u8 skip;
        u32 slp_itrvl;
 
-       if (priv->cfg->adv_pm) {
+       if (priv->lib->adv_pm) {
                table = apm_range_2;
                if (period <= IWL_DTIM_RANGE_1_MAX)
                        table = apm_range_1;
@@ -217,7 +217,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
                cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
 
        if (iwl_advanced_bt_coexist(priv)) {
-               if (!priv->cfg->bt_params->bt_sco_disable)
+               if (!priv->lib->bt_params->bt_sco_disable)
                        cmd->flags |= IWL_POWER_BT_SCO_ENA;
                else
                        cmd->flags &= ~IWL_POWER_BT_SCO_ENA;
@@ -293,7 +293,7 @@ static void iwl_power_build_cmd(struct iwl_priv *priv,
 
        if (priv->wowlan)
                iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);
-       else if (!priv->cfg->base_params->no_idle_support &&
+       else if (!priv->lib->no_idle_support &&
                 priv->hw->conf.flags & IEEE80211_CONF_IDLE)
                iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
        else if (iwl_tt_is_low_power_state(priv)) {
index 10fbb17..1b69394 100644 (file)
@@ -351,12 +351,6 @@ static void rs_program_fix_rate(struct iwl_priv *priv,
        lq_sta->active_mimo2_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
        lq_sta->active_mimo3_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
 
-#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
-       /* testmode has higher priority to overwirte the fixed rate */
-       if (priv->tm_fixed_rate)
-               lq_sta->dbg_fixed_rate = priv->tm_fixed_rate;
-#endif
-
        IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n",
                lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
 
@@ -419,23 +413,18 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv,
 
        load = rs_tl_get_load(lq_data, tid);
 
-       if ((iwlwifi_mod_params.auto_agg) || (load > IWL_AGG_LOAD_THRESHOLD)) {
-               IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n",
-                               sta->addr, tid);
-               ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
-               if (ret == -EAGAIN) {
-                       /*
-                        * driver and mac80211 is out of sync
-                        * this might be cause by reloading firmware
-                        * stop the tx ba session here
-                        */
-                       IWL_ERR(priv, "Fail start Tx agg on tid: %d\n",
-                               tid);
-                       ieee80211_stop_tx_ba_session(sta, tid);
-               }
-       } else {
-               IWL_DEBUG_HT(priv, "Aggregation not enabled for tid %d "
-                       "because load = %u\n", tid, load);
+       IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n",
+                       sta->addr, tid);
+       ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
+       if (ret == -EAGAIN) {
+               /*
+                * driver and mac80211 is out of sync
+                * this might be cause by reloading firmware
+                * stop the tx ba session here
+                */
+               IWL_ERR(priv, "Fail start Tx agg on tid: %d\n",
+                       tid);
+               ieee80211_stop_tx_ba_session(sta, tid);
        }
        return ret;
 }
@@ -1083,12 +1072,7 @@ done:
        if (sta && sta->supp_rates[sband->band])
                rs_rate_scale_perform(priv, skb, sta, lq_sta);
 
-#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_IWLWIFI_DEVICE_TESTMODE)
-       if ((priv->tm_fixed_rate) &&
-           (priv->tm_fixed_rate != lq_sta->dbg_fixed_rate))
-               rs_program_fix_rate(priv, lq_sta);
-#endif
-       if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist)
+       if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist)
                rs_bt_update_lq(priv, ctx, lq_sta);
 }
 
@@ -2913,9 +2897,6 @@ void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_i
        if (sband->band == IEEE80211_BAND_5GHZ)
                lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
        lq_sta->is_agg = 0;
-#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
-       priv->tm_fixed_rate = 0;
-#endif
 #ifdef CONFIG_MAC80211_DEBUGFS
        lq_sta->dbg_fixed_rate = 0;
 #endif
@@ -3064,11 +3045,11 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
         * overwrite if needed, pass aggregation time limit
         * to uCode in uSec
         */
-       if (priv && priv->cfg->bt_params &&
-           priv->cfg->bt_params->agg_time_limit &&
+       if (priv && priv->lib->bt_params &&
+           priv->lib->bt_params->agg_time_limit &&
            priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)
                lq_cmd->agg_params.agg_time_limit =
-                       cpu_to_le16(priv->cfg->bt_params->agg_time_limit);
+                       cpu_to_le16(priv->lib->bt_params->agg_time_limit);
 }
 
 static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
index a4eed20..d71776d 100644 (file)
@@ -335,8 +335,7 @@ static void iwlagn_recover_from_statistics(struct iwl_priv *priv,
        if (msecs < 99)
                return;
 
-       if (iwlwifi_mod_params.plcp_check &&
-           !iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs))
+       if (!iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs))
                iwl_force_rf_reset(priv, false);
 }
 
@@ -1102,7 +1101,7 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv)
        iwl_notification_wait_init(&priv->notif_wait);
 
        /* Set up BT Rx handlers */
-       if (priv->cfg->bt_params)
+       if (priv->lib->bt_params)
                iwlagn_bt_rx_handler_setup(priv);
 }
 
@@ -1120,32 +1119,17 @@ int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb,
         */
        iwl_notification_wait_notify(&priv->notif_wait, pkt);
 
-#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
-       /*
-        * RX data may be forwarded to userspace in one
-        * of two cases: the user owns the fw through testmode or when
-        * the user requested to monitor the rx w/o affecting the regular flow.
-        * In these cases the iwl_test object will handle forwarding the rx
-        * data to user space.
-        * Note that if the ownership flag != IWL_OWNERSHIP_TM the flow
-        * continues.
-        */
-       iwl_test_rx(&priv->tst, rxb);
-#endif
-
-       if (priv->ucode_owner != IWL_OWNERSHIP_TM) {
-               /* Based on type of command response or notification,
-                *   handle those that need handling via function in
-                *   rx_handlers table.  See iwl_setup_rx_handlers() */
-               if (priv->rx_handlers[pkt->hdr.cmd]) {
-                       priv->rx_handlers_stats[pkt->hdr.cmd]++;
-                       err = priv->rx_handlers[pkt->hdr.cmd] (priv, rxb, cmd);
-               } else {
-                       /* No handling needed */
-                       IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n",
-                                    iwl_dvm_get_cmd_string(pkt->hdr.cmd),
-                                    pkt->hdr.cmd);
-               }
+       /* Based on type of command response or notification,
+        *   handle those that need handling via function in
+        *   rx_handlers table.  See iwl_setup_rx_handlers() */
+       if (priv->rx_handlers[pkt->hdr.cmd]) {
+               priv->rx_handlers_stats[pkt->hdr.cmd]++;
+               err = priv->rx_handlers[pkt->hdr.cmd] (priv, rxb, cmd);
+       } else {
+               /* No handling needed */
+               IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n",
+                            iwl_dvm_get_cmd_string(pkt->hdr.cmd),
+                            pkt->hdr.cmd);
        }
        return err;
 }
index d69b558..8c686a5 100644 (file)
@@ -801,8 +801,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                 * Internal scans are passive, so we can indiscriminately set
                 * the BT ignore flag on 2.4 GHz since it applies to TX only.
                 */
-               if (priv->cfg->bt_params &&
-                   priv->cfg->bt_params->advanced_bt_coexist)
+               if (priv->lib->bt_params &&
+                   priv->lib->bt_params->advanced_bt_coexist)
                        scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT;
                break;
        case IEEE80211_BAND_5GHZ:
@@ -844,8 +844,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        band = priv->scan_band;
 
        if (band == IEEE80211_BAND_2GHZ &&
-           priv->cfg->bt_params &&
-           priv->cfg->bt_params->advanced_bt_coexist) {
+           priv->lib->bt_params &&
+           priv->lib->bt_params->advanced_bt_coexist) {
                /* transmit 2.4 GHz probes only on first antenna */
                scan_tx_antennas = first_antenna(scan_tx_antennas);
        }
@@ -873,8 +873,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 
                rx_ant = first_antenna(active_chains);
        }
-       if (priv->cfg->bt_params &&
-           priv->cfg->bt_params->advanced_bt_coexist &&
+       if (priv->lib->bt_params &&
+           priv->lib->bt_params->advanced_bt_coexist &&
            priv->bt_full_concurrent) {
                /* operated as 1x1 in full concurrency mode */
                rx_ant = first_antenna(rx_ant);
diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.c b/drivers/net/wireless/iwlwifi/dvm/testmode.c
deleted file mode 100644 (file)
index b89b9d9..0000000
+++ /dev/null
@@ -1,471 +0,0 @@
-/******************************************************************************
- *
- * 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) 2010 - 2013 Intel Corporation. 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 Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- *  Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
- * 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 Intel Corporation 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 <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <net/net_namespace.h>
-#include <linux/netdevice.h>
-#include <net/cfg80211.h>
-#include <net/mac80211.h>
-#include <net/netlink.h>
-
-#include "iwl-debug.h"
-#include "iwl-trans.h"
-#include "dev.h"
-#include "agn.h"
-#include "iwl-test.h"
-#include "iwl-testmode.h"
-
-static int iwl_testmode_send_cmd(struct iwl_op_mode *op_mode,
-                                struct iwl_host_cmd *cmd)
-{
-       struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-       return iwl_dvm_send_cmd(priv, cmd);
-}
-
-static bool iwl_testmode_valid_hw_addr(u32 addr)
-{
-       if (iwlagn_hw_valid_rtc_data_addr(addr))
-               return true;
-
-       if (IWLAGN_RTC_INST_LOWER_BOUND <= addr &&
-           addr < IWLAGN_RTC_INST_UPPER_BOUND)
-               return true;
-
-       return false;
-}
-
-static u32 iwl_testmode_get_fw_ver(struct iwl_op_mode *op_mode)
-{
-       struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-       return priv->fw->ucode_ver;
-}
-
-static struct sk_buff*
-iwl_testmode_alloc_reply(struct iwl_op_mode *op_mode, int len)
-{
-       struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-       return cfg80211_testmode_alloc_reply_skb(priv->hw->wiphy, len);
-}
-
-static int iwl_testmode_reply(struct iwl_op_mode *op_mode, struct sk_buff *skb)
-{
-       return cfg80211_testmode_reply(skb);
-}
-
-static struct sk_buff *iwl_testmode_alloc_event(struct iwl_op_mode *op_mode,
-                                               int len)
-{
-       struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-       return cfg80211_testmode_alloc_event_skb(priv->hw->wiphy, len,
-                                                GFP_ATOMIC);
-}
-
-static void iwl_testmode_event(struct iwl_op_mode *op_mode, struct sk_buff *skb)
-{
-       return cfg80211_testmode_event(skb, GFP_ATOMIC);
-}
-
-static struct iwl_test_ops tst_ops = {
-       .send_cmd = iwl_testmode_send_cmd,
-       .valid_hw_addr = iwl_testmode_valid_hw_addr,
-       .get_fw_ver = iwl_testmode_get_fw_ver,
-       .alloc_reply = iwl_testmode_alloc_reply,
-       .reply = iwl_testmode_reply,
-       .alloc_event = iwl_testmode_alloc_event,
-       .event = iwl_testmode_event,
-};
-
-void iwl_testmode_init(struct iwl_priv *priv)
-{
-       iwl_test_init(&priv->tst, priv->trans, &tst_ops);
-}
-
-void iwl_testmode_free(struct iwl_priv *priv)
-{
-       iwl_test_free(&priv->tst);
-}
-
-static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv)
-{
-       struct iwl_notification_wait calib_wait;
-       static const u8 calib_complete[] = {
-               CALIBRATION_COMPLETE_NOTIFICATION
-       };
-       int ret;
-
-       iwl_init_notification_wait(&priv->notif_wait, &calib_wait,
-                                  calib_complete, ARRAY_SIZE(calib_complete),
-                                  NULL, NULL);
-       ret = iwl_init_alive_start(priv);
-       if (ret) {
-               IWL_ERR(priv, "Fail init calibration: %d\n", ret);
-               goto cfg_init_calib_error;
-       }
-
-       ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, 2 * HZ);
-       if (ret)
-               IWL_ERR(priv, "Error detecting"
-                       " CALIBRATION_COMPLETE_NOTIFICATION: %d\n", ret);
-       return ret;
-
-cfg_init_calib_error:
-       iwl_remove_notification(&priv->notif_wait, &calib_wait);
-       return ret;
-}
-
-/*
- * This function handles the user application commands for driver.
- *
- * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the
- * handlers respectively.
- *
- * If it's an unknown commdn ID, -ENOSYS is replied; otherwise, the returned
- * value of the actual command execution is replied to the user application.
- *
- * If there's any message responding to the user space, IWL_TM_ATTR_SYNC_RSP
- * is used for carry the message while IWL_TM_ATTR_COMMAND must set to
- * IWL_TM_CMD_DEV2APP_SYNC_RSP.
- *
- * @hw: ieee80211_hw object that represents the device
- * @tb: gnl message fields from the user space
- */
-static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
-{
-       struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-       struct iwl_trans *trans = priv->trans;
-       struct sk_buff *skb;
-       unsigned char *rsp_data_ptr = NULL;
-       int status = 0, rsp_data_len = 0;
-       u32 inst_size = 0, data_size = 0;
-       const struct fw_img *img;
-
-       switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
-       case IWL_TM_CMD_APP2DEV_GET_DEVICENAME:
-               rsp_data_ptr = (unsigned char *)priv->cfg->name;
-               rsp_data_len = strlen(priv->cfg->name);
-               skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
-                                                       rsp_data_len + 20);
-               if (!skb) {
-                       IWL_ERR(priv, "Memory allocation fail\n");
-                       return -ENOMEM;
-               }
-               if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND,
-                               IWL_TM_CMD_DEV2APP_SYNC_RSP) ||
-                   nla_put(skb, IWL_TM_ATTR_SYNC_RSP,
-                           rsp_data_len, rsp_data_ptr))
-                       goto nla_put_failure;
-               status = cfg80211_testmode_reply(skb);
-               if (status < 0)
-                       IWL_ERR(priv, "Error sending msg : %d\n", status);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW:
-               status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT);
-               if (status)
-                       IWL_ERR(priv, "Error loading init ucode: %d\n", status);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB:
-               iwl_testmode_cfg_init_calib(priv);
-               priv->ucode_loaded = false;
-               iwl_trans_stop_device(trans);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW:
-               status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR);
-               if (status) {
-                       IWL_ERR(priv,
-                               "Error loading runtime ucode: %d\n", status);
-                       break;
-               }
-               status = iwl_alive_start(priv);
-               if (status)
-                       IWL_ERR(priv,
-                               "Error starting the device: %d\n", status);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW:
-               iwl_scan_cancel_timeout(priv, 200);
-               priv->ucode_loaded = false;
-               iwl_trans_stop_device(trans);
-               status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN);
-               if (status) {
-                       IWL_ERR(priv,
-                               "Error loading WOWLAN ucode: %d\n", status);
-                       break;
-               }
-               status = iwl_alive_start(priv);
-               if (status)
-                       IWL_ERR(priv,
-                               "Error starting the device: %d\n", status);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_GET_EEPROM:
-               if (priv->eeprom_blob) {
-                       skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
-                               priv->eeprom_blob_size + 20);
-                       if (!skb) {
-                               IWL_ERR(priv, "Memory allocation fail\n");
-                               return -ENOMEM;
-                       }
-                       if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND,
-                                       IWL_TM_CMD_DEV2APP_EEPROM_RSP) ||
-                           nla_put(skb, IWL_TM_ATTR_EEPROM,
-                                   priv->eeprom_blob_size,
-                                   priv->eeprom_blob))
-                               goto nla_put_failure;
-                       status = cfg80211_testmode_reply(skb);
-                       if (status < 0)
-                               IWL_ERR(priv, "Error sending msg : %d\n",
-                                       status);
-               } else
-                       return -ENODATA;
-               break;
-
-       case IWL_TM_CMD_APP2DEV_FIXRATE_REQ:
-               if (!tb[IWL_TM_ATTR_FIXRATE]) {
-                       IWL_ERR(priv, "Missing fixrate setting\n");
-                       return -ENOMSG;
-               }
-               priv->tm_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_GET_FW_INFO:
-               skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20 + 8);
-               if (!skb) {
-                       IWL_ERR(priv, "Memory allocation fail\n");
-                       return -ENOMEM;
-               }
-               if (!priv->ucode_loaded) {
-                       IWL_ERR(priv, "No uCode has not been loaded\n");
-                       return -EINVAL;
-               } else {
-                       img = &priv->fw->img[priv->cur_ucode];
-                       inst_size = img->sec[IWL_UCODE_SECTION_INST].len;
-                       data_size = img->sec[IWL_UCODE_SECTION_DATA].len;
-               }
-               if (nla_put_u32(skb, IWL_TM_ATTR_FW_TYPE, priv->cur_ucode) ||
-                   nla_put_u32(skb, IWL_TM_ATTR_FW_INST_SIZE, inst_size) ||
-                   nla_put_u32(skb, IWL_TM_ATTR_FW_DATA_SIZE, data_size))
-                       goto nla_put_failure;
-               status = cfg80211_testmode_reply(skb);
-               if (status < 0)
-                       IWL_ERR(priv, "Error sending msg : %d\n", status);
-               break;
-
-       default:
-               IWL_ERR(priv, "Unknown testmode driver command ID\n");
-               return -ENOSYS;
-       }
-       return status;
-
-nla_put_failure:
-       kfree_skb(skb);
-       return -EMSGSIZE;
-}
-
-/*
- * This function handles the user application switch ucode ownership.
- *
- * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_OWNER and
- * decide who the current owner of the uCode
- *
- * If the current owner is OWNERSHIP_TM, then the only host command
- * can deliver to uCode is from testmode, all the other host commands
- * will dropped.
- *
- * default driver is the owner of uCode in normal operational mode
- *
- * @hw: ieee80211_hw object that represents the device
- * @tb: gnl message fields from the user space
- */
-static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb)
-{
-       struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-       u8 owner;
-
-       if (!tb[IWL_TM_ATTR_UCODE_OWNER]) {
-               IWL_ERR(priv, "Missing ucode owner\n");
-               return -ENOMSG;
-       }
-
-       owner = nla_get_u8(tb[IWL_TM_ATTR_UCODE_OWNER]);
-       if (owner == IWL_OWNERSHIP_DRIVER) {
-               priv->ucode_owner = owner;
-               iwl_test_enable_notifications(&priv->tst, false);
-       } else if (owner == IWL_OWNERSHIP_TM) {
-               priv->ucode_owner = owner;
-               iwl_test_enable_notifications(&priv->tst, true);
-       } else {
-               IWL_ERR(priv, "Invalid owner\n");
-               return -EINVAL;
-       }
-       return 0;
-}
-
-/* The testmode gnl message handler that takes the gnl message from the
- * user space and parses it per the policy iwl_testmode_gnl_msg_policy, then
- * invoke the corresponding handlers.
- *
- * This function is invoked when there is user space application sending
- * gnl message through the testmode tunnel NL80211_CMD_TESTMODE regulated
- * by nl80211.
- *
- * It retrieves the mandatory field, IWL_TM_ATTR_COMMAND, before
- * dispatching it to the corresponding handler.
- *
- * If IWL_TM_ATTR_COMMAND is missing, -ENOMSG is replied to user application;
- * -ENOSYS is replied to the user application if the command is unknown;
- * Otherwise, the command is dispatched to the respective handler.
- *
- * @hw: ieee80211_hw object that represents the device
- * @data: pointer to user space message
- * @len: length in byte of @data
- */
-int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
-{
-       struct nlattr *tb[IWL_TM_ATTR_MAX];
-       struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-       int result;
-
-       result = iwl_test_parse(&priv->tst, tb, data, len);
-       if (result)
-               return result;
-
-       /* in case multiple accesses to the device happens */
-       mutex_lock(&priv->mutex);
-       switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
-       case IWL_TM_CMD_APP2DEV_UCODE:
-       case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
-       case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
-       case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
-       case IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
-       case IWL_TM_CMD_APP2DEV_END_TRACE:
-       case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ:
-       case IWL_TM_CMD_APP2DEV_NOTIFICATIONS:
-       case IWL_TM_CMD_APP2DEV_GET_FW_VERSION:
-       case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID:
-       case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE:
-               result = iwl_test_handle_cmd(&priv->tst, tb);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_GET_DEVICENAME:
-       case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW:
-       case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB:
-       case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW:
-       case IWL_TM_CMD_APP2DEV_GET_EEPROM:
-       case IWL_TM_CMD_APP2DEV_FIXRATE_REQ:
-       case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW:
-       case IWL_TM_CMD_APP2DEV_GET_FW_INFO:
-               IWL_DEBUG_INFO(priv, "testmode cmd to driver\n");
-               result = iwl_testmode_driver(hw, tb);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_OWNERSHIP:
-               IWL_DEBUG_INFO(priv, "testmode change uCode ownership\n");
-               result = iwl_testmode_ownership(hw, tb);
-               break;
-
-       default:
-               IWL_ERR(priv, "Unknown testmode command\n");
-               result = -ENOSYS;
-               break;
-       }
-       mutex_unlock(&priv->mutex);
-
-       if (result)
-               IWL_ERR(priv, "Test cmd failed result=%d\n", result);
-       return result;
-}
-
-int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
-                     struct netlink_callback *cb,
-                     void *data, int len)
-{
-       struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-       int result;
-       u32 cmd;
-
-       if (cb->args[3]) {
-               /* offset by 1 since commands start at 0 */
-               cmd = cb->args[3] - 1;
-       } else {
-               struct nlattr *tb[IWL_TM_ATTR_MAX];
-
-               result = iwl_test_parse(&priv->tst, tb, data, len);
-               if (result)
-                       return result;
-
-               cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
-               cb->args[3] = cmd + 1;
-       }
-
-       /* in case multiple accesses to the device happens */
-       mutex_lock(&priv->mutex);
-       result = iwl_test_dump(&priv->tst, cmd, skb, cb);
-       mutex_unlock(&priv->mutex);
-       return result;
-}
index 03f9bc0..fbeee08 100644 (file)
@@ -627,7 +627,7 @@ void iwl_tt_initialize(struct iwl_priv *priv)
        INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter);
        INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit);
 
-       if (priv->cfg->base_params->adv_thermal_throttle) {
+       if (priv->lib->adv_thermal_throttle) {
                IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n");
                tt->restriction = kcalloc(IWL_TI_STATE_MAX,
                                          sizeof(struct iwl_tt_restriction),
index a900aaf..5ee983f 100644 (file)
@@ -83,8 +83,8 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
        else if (ieee80211_is_back_req(fc))
                tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;
        else if (info->band == IEEE80211_BAND_2GHZ &&
-                priv->cfg->bt_params &&
-                priv->cfg->bt_params->advanced_bt_coexist &&
+                priv->lib->bt_params &&
+                priv->lib->bt_params->advanced_bt_coexist &&
                 (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) ||
                 ieee80211_is_reassoc_req(fc) ||
                 skb->protocol == cpu_to_be16(ETH_P_PAE)))
@@ -162,18 +162,6 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
        if (ieee80211_is_data(fc)) {
                tx_cmd->initial_rate_index = 0;
                tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
-#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
-               if (priv->tm_fixed_rate) {
-                       /*
-                        * rate overwrite by testmode
-                        * we not only send lq command to change rate
-                        * we also re-enforce per data pkt base.
-                        */
-                       tx_cmd->tx_flags &= ~TX_CMD_FLG_STA_RATE_MSK;
-                       memcpy(&tx_cmd->rate_n_flags, &priv->tm_fixed_rate,
-                              sizeof(tx_cmd->rate_n_flags));
-               }
-#endif
                return;
        } else if (ieee80211_is_back_req(fc))
                tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
@@ -202,8 +190,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
                rate_flags |= RATE_MCS_CCK_MSK;
 
        /* Set up antennas */
-        if (priv->cfg->bt_params &&
-            priv->cfg->bt_params->advanced_bt_coexist &&
+        if (priv->lib->bt_params &&
+            priv->lib->bt_params->advanced_bt_coexist &&
             priv->bt_full_concurrent) {
                /* operated as 1x1 in full concurrency mode */
                priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
@@ -986,8 +974,8 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
         * notification again.
         */
        if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 &&
-           priv->cfg->bt_params &&
-           priv->cfg->bt_params->advanced_bt_coexist) {
+           priv->lib->bt_params &&
+           priv->lib->bt_params->advanced_bt_coexist) {
                IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n");
        }
 
index 0a1cdc5..86270b6 100644 (file)
@@ -132,8 +132,8 @@ int iwl_init_alive_start(struct iwl_priv *priv)
 {
        int ret;
 
-       if (priv->cfg->bt_params &&
-           priv->cfg->bt_params->advanced_bt_coexist) {
+       if (priv->lib->bt_params &&
+           priv->lib->bt_params->advanced_bt_coexist) {
                /*
                 * Tell uCode we are ready to perform calibration
                 * need to perform this before any calibration
@@ -155,8 +155,8 @@ int iwl_init_alive_start(struct iwl_priv *priv)
         * temperature offset calibration is only needed for runtime ucode,
         * so prepare the value now.
         */
-       if (priv->cfg->need_temp_offset_calib) {
-               if (priv->cfg->temp_offset_v2)
+       if (priv->lib->need_temp_offset_calib) {
+               if (priv->lib->temp_offset_v2)
                        return iwl_set_temperature_offset_calib_v2(priv);
                else
                        return iwl_set_temperature_offset_calib(priv);
@@ -277,7 +277,7 @@ static int iwl_alive_notify(struct iwl_priv *priv)
        if (ret)
                return ret;
 
-       if (!priv->cfg->no_xtal_calib) {
+       if (!priv->lib->no_xtal_calib) {
                ret = iwl_set_Xtal_calib(priv);
                if (ret)
                        return ret;
index c080ae3..0d2afe0 100644 (file)
@@ -60,9 +60,6 @@ static const struct iwl_base_params iwl1000_base_params = {
        .max_ll_items = OTP_MAX_LL_ITEMS_1000,
        .shadow_ram_support = false,
        .led_compensation = 51,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
        .wd_timeout = IWL_WATCHDOG_DISABLED,
        .max_event_log_size = 128,
 };
index a6ddd2f..c727ec7 100644 (file)
@@ -72,14 +72,9 @@ static const struct iwl_base_params iwl2000_base_params = {
        .max_ll_items = OTP_MAX_LL_ITEMS_2x00,
        .shadow_ram_support = true,
        .led_compensation = 51,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
-       .hd_v2 = true,
 };
 
 
@@ -90,14 +85,9 @@ static const struct iwl_base_params iwl2030_base_params = {
        .max_ll_items = OTP_MAX_LL_ITEMS_2x00,
        .shadow_ram_support = true,
        .led_compensation = 57,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
-       .hd_v2 = true,
 };
 
 static const struct iwl_ht_params iwl2000_ht_params = {
@@ -106,16 +96,6 @@ static const struct iwl_ht_params iwl2000_ht_params = {
        .ht40_bands = BIT(IEEE80211_BAND_2GHZ),
 };
 
-static const struct iwl_bt_params iwl2030_bt_params = {
-       /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
-       .advanced_bt_coexist = true,
-       .agg_time_limit = BT_AGG_THRESHOLD_DEF,
-       .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
-       .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32,
-       .bt_sco_disable = true,
-       .bt_session_2 = true,
-};
-
 static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
        .regulatory_bands = {
                EEPROM_REG_BAND_1_CHANNELS,
@@ -137,12 +117,10 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
        .device_family = IWL_DEVICE_FAMILY_2000,                \
        .max_inst_size = IWL60_RTC_INST_SIZE,                   \
        .max_data_size = IWL60_RTC_DATA_SIZE,                   \
-       .nvm_ver = EEPROM_2000_EEPROM_VERSION,          \
-       .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION,  \
+       .nvm_ver = EEPROM_2000_EEPROM_VERSION,                  \
+       .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION,          \
        .base_params = &iwl2000_base_params,                    \
        .eeprom_params = &iwl20x0_eeprom_params,                \
-       .need_temp_offset_calib = true,                         \
-       .temp_offset_v2 = true,                                 \
        .led_mode = IWL_LED_RF_STATE
 
 const struct iwl_cfg iwl2000_2bgn_cfg = {
@@ -168,12 +146,8 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = {
        .nvm_ver = EEPROM_2000_EEPROM_VERSION,          \
        .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION,  \
        .base_params = &iwl2030_base_params,                    \
-       .bt_params = &iwl2030_bt_params,                        \
        .eeprom_params = &iwl20x0_eeprom_params,                \
-       .need_temp_offset_calib = true,                         \
-       .temp_offset_v2 = true,                                 \
-       .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true
+       .led_mode = IWL_LED_RF_STATE
 
 const struct iwl_cfg iwl2030_2bgn_cfg = {
        .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN",
@@ -193,10 +167,7 @@ const struct iwl_cfg iwl2030_2bgn_cfg = {
        .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION,  \
        .base_params = &iwl2000_base_params,                    \
        .eeprom_params = &iwl20x0_eeprom_params,                \
-       .need_temp_offset_calib = true,                         \
-       .temp_offset_v2 = true,                                 \
        .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true,                                         \
        .rx_with_siso_diversity = true
 
 const struct iwl_cfg iwl105_bgn_cfg = {
@@ -222,12 +193,8 @@ const struct iwl_cfg iwl105_bgn_d_cfg = {
        .nvm_ver = EEPROM_2000_EEPROM_VERSION,          \
        .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION,  \
        .base_params = &iwl2030_base_params,                    \
-       .bt_params = &iwl2030_bt_params,                        \
        .eeprom_params = &iwl20x0_eeprom_params,                \
-       .need_temp_offset_calib = true,                         \
-       .temp_offset_v2 = true,                                 \
        .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true,                                         \
        .rx_with_siso_diversity = true
 
 const struct iwl_cfg iwl135_bgn_cfg = {
index 403f3f2..ecc01e1 100644 (file)
@@ -59,11 +59,8 @@ static const struct iwl_base_params iwl5000_base_params = {
        .num_of_queues = IWLAGN_NUM_QUEUES,
        .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
        .led_compensation = 51,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
        .wd_timeout = IWL_WATCHDOG_DISABLED,
        .max_event_log_size = 512,
-       .no_idle_support = true,
 };
 
 static const struct iwl_ht_params iwl5000_ht_params = {
@@ -159,7 +156,6 @@ const struct iwl_cfg iwl5350_agn_cfg = {
        .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION,  \
        .base_params = &iwl5000_base_params,                    \
        .eeprom_params = &iwl5000_eeprom_params,                \
-       .no_xtal_calib = true,                                  \
        .led_mode = IWL_LED_BLINK,                              \
        .internal_wimax_coex = true
 
index b5ab8d1..30d45e2 100644 (file)
@@ -82,10 +82,6 @@ static const struct iwl_base_params iwl6000_base_params = {
        .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
        .shadow_ram_support = true,
        .led_compensation = 51,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
@@ -98,10 +94,6 @@ static const struct iwl_base_params iwl6050_base_params = {
        .max_ll_items = OTP_MAX_LL_ITEMS_6x50,
        .shadow_ram_support = true,
        .led_compensation = 51,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1500,
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 1024,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
@@ -114,10 +106,6 @@ static const struct iwl_base_params iwl6000_g2_base_params = {
        .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
        .shadow_ram_support = true,
        .led_compensation = 57,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
@@ -129,15 +117,6 @@ static const struct iwl_ht_params iwl6000_ht_params = {
        .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
 };
 
-static const struct iwl_bt_params iwl6000_bt_params = {
-       /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
-       .advanced_bt_coexist = true,
-       .agg_time_limit = BT_AGG_THRESHOLD_DEF,
-       .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
-       .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT,
-       .bt_sco_disable = true,
-};
-
 static const struct iwl_eeprom_params iwl6000_eeprom_params = {
        .regulatory_bands = {
                EEPROM_REG_BAND_1_CHANNELS,
@@ -163,7 +142,6 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = {
        .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION,  \
        .base_params = &iwl6000_g2_base_params,                 \
        .eeprom_params = &iwl6000_eeprom_params,                \
-       .need_temp_offset_calib = true,                         \
        .led_mode = IWL_LED_RF_STATE
 
 const struct iwl_cfg iwl6005_2agn_cfg = {
@@ -217,11 +195,8 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = {
        .nvm_ver = EEPROM_6030_EEPROM_VERSION,          \
        .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION,  \
        .base_params = &iwl6000_g2_base_params,                 \
-       .bt_params = &iwl6000_bt_params,                        \
        .eeprom_params = &iwl6000_eeprom_params,                \
-       .need_temp_offset_calib = true,                         \
-       .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true                                          \
+       .led_mode = IWL_LED_RF_STATE
 
 const struct iwl_cfg iwl6030_2agn_cfg = {
        .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN",
@@ -256,11 +231,8 @@ const struct iwl_cfg iwl6030_2bg_cfg = {
        .nvm_ver = EEPROM_6030_EEPROM_VERSION,          \
        .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION,  \
        .base_params = &iwl6000_g2_base_params,                 \
-       .bt_params = &iwl6000_bt_params,                        \
        .eeprom_params = &iwl6000_eeprom_params,                \
-       .need_temp_offset_calib = true,                         \
-       .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true
+       .led_mode = IWL_LED_RF_STATE
 
 const struct iwl_cfg iwl6035_2agn_cfg = {
        .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN",
index 50263e8..22b7fa5 100644 (file)
 #include "iwl-agn-hw.h"
 
 /* Highest firmware API version supported */
-#define IWL7260_UCODE_API_MAX  6
-#define IWL3160_UCODE_API_MAX  6
+#define IWL7260_UCODE_API_MAX  7
+#define IWL3160_UCODE_API_MAX  7
 
 /* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK   6
-#define IWL3160_UCODE_API_OK   6
+#define IWL7260_UCODE_API_OK   7
+#define IWL3160_UCODE_API_OK   7
 
 /* Lowest firmware API version supported */
-#define IWL7260_UCODE_API_MIN  6
-#define IWL3160_UCODE_API_MIN  6
+#define IWL7260_UCODE_API_MIN  7
+#define IWL3160_UCODE_API_MIN  7
 
 /* NVM versions */
 #define IWL7260_NVM_VERSION            0x0a1d
@@ -96,13 +96,9 @@ static const struct iwl_base_params iwl7000_base_params = {
        .pll_cfg_val = 0,
        .shadow_ram_support = true,
        .led_compensation = 57,
-       .adv_thermal_throttle = true,
-       .support_ct_kill_exit = true,
-       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
-       .chain_noise_scale = 1000,
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
-       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .shadow_reg_enable = true,
 };
 
 static const struct iwl_ht_params iwl7000_ht_params = {
@@ -118,14 +114,11 @@ static const struct iwl_ht_params iwl7000_ht_params = {
        .max_inst_size = IWL60_RTC_INST_SIZE,                   \
        .max_data_size = IWL60_RTC_DATA_SIZE,                   \
        .base_params = &iwl7000_base_params,                    \
-       /* TODO: .bt_params? */                                 \
-       .need_temp_offset_calib = true,                         \
-       .led_mode = IWL_LED_RF_STATE,                           \
-       .adv_pm = true                                          \
+       .led_mode = IWL_LED_RF_STATE
 
 
 const struct iwl_cfg iwl7260_2ac_cfg = {
-       .name = "Intel(R) Dual Band Wireless AC7260",
+       .name = "Intel(R) Dual Band Wireless AC 7260",
        .fw_name_pre = IWL7260_FW_PRE,
        IWL_DEVICE_7000,
        .ht_params = &iwl7000_ht_params,
@@ -133,8 +126,44 @@ const struct iwl_cfg iwl7260_2ac_cfg = {
        .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
 };
 
-const struct iwl_cfg iwl3160_ac_cfg = {
-       .name = "Intel(R) Dual Band Wireless AC3160",
+const struct iwl_cfg iwl7260_2n_cfg = {
+       .name = "Intel(R) Dual Band Wireless N 7260",
+       .fw_name_pre = IWL7260_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL7260_NVM_VERSION,
+       .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl7260_n_cfg = {
+       .name = "Intel(R) Wireless N 7260",
+       .fw_name_pre = IWL7260_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL7260_NVM_VERSION,
+       .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl3160_2ac_cfg = {
+       .name = "Intel(R) Dual Band Wireless AC 3160",
+       .fw_name_pre = IWL3160_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL3160_NVM_VERSION,
+       .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl3160_2n_cfg = {
+       .name = "Intel(R) Dual Band Wireless N 3160",
+       .fw_name_pre = IWL3160_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL3160_NVM_VERSION,
+       .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl3160_n_cfg = {
+       .name = "Intel(R) Wireless N 3160",
        .fw_name_pre = IWL3160_FW_PRE,
        IWL_DEVICE_7000,
        .ht_params = &iwl7000_ht_params,
index c38aa8f..83b9ff6 100644 (file)
@@ -136,17 +136,9 @@ enum iwl_led_mode {
  * @led_compensation: compensate on the led on/off time per HW according
  *     to the deviation to achieve the desired led frequency.
  *     The detail algorithm is described in iwl-led.c
- * @chain_noise_num_beacons: number of beacons used to compute chain noise
- * @adv_thermal_throttle: support advance thermal throttle
- * @support_ct_kill_exit: support ct kill exit condition
- * @plcp_delta_threshold: plcp error rate threshold used to trigger
- *     radio tuning when there is a high receiving plcp error rate
- * @chain_noise_scale: default chain noise scale used for gain computation
  * @wd_timeout: TX queues watchdog timeout
  * @max_event_log_size: size of event log buffer size for ucode event logging
  * @shadow_reg_enable: HW shadow register support
- * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
- * @no_idle_support: do not support idle mode
  */
 struct iwl_base_params {
        int eeprom_size;
@@ -157,31 +149,9 @@ struct iwl_base_params {
        const u16 max_ll_items;
        const bool shadow_ram_support;
        u16 led_compensation;
-       bool adv_thermal_throttle;
-       bool support_ct_kill_exit;
-       u8 plcp_delta_threshold;
-       s32 chain_noise_scale;
        unsigned int wd_timeout;
        u32 max_event_log_size;
        const bool shadow_reg_enable;
-       const bool hd_v2;
-       const bool no_idle_support;
-};
-
-/*
- * @advanced_bt_coexist: support advanced bt coexist
- * @bt_init_traffic_load: specify initial bt traffic load
- * @bt_prio_boost: default bt priority boost value
- * @agg_time_limit: maximum number of uSec in aggregation
- * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode
- */
-struct iwl_bt_params {
-       bool advanced_bt_coexist;
-       u8 bt_init_traffic_load;
-       u32 bt_prio_boost;
-       u16 agg_time_limit;
-       bool bt_sco_disable;
-       bool bt_session_2;
 };
 
 /*
@@ -231,16 +201,10 @@ struct iwl_eeprom_params {
  * @nvm_calib_ver: NVM calibration version
  * @lib: pointer to the lib ops
  * @base_params: pointer to basic parameters
- * @ht_params: point to ht patameters
- * @bt_params: pointer to bt parameters
- * @need_temp_offset_calib: need to perform temperature offset calibration
- * @no_xtal_calib: some devices do not need crystal calibration data,
- *     don't send it to those
+ * @ht_params: point to ht parameters
  * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off)
- * @adv_pm: advance power management
  * @rx_with_siso_diversity: 1x1 device with rx antenna diversity
  * @internal_wimax_coex: internal wifi/wimax combo device
- * @temp_offset_v2: support v2 of temperature offset calibration
  *
  * We enable the driver to be backward compatible wrt. hardware features.
  * API differences in uCode shouldn't be handled here but through TLVs
@@ -258,26 +222,23 @@ struct iwl_cfg {
        const u32 max_inst_size;
        u8   valid_tx_ant;
        u8   valid_rx_ant;
+       bool bt_shared_single_ant;
        u16  nvm_ver;
        u16  nvm_calib_ver;
        /* params not likely to change within a device family */
        const struct iwl_base_params *base_params;
        /* params likely to change within a device family */
        const struct iwl_ht_params *ht_params;
-       const struct iwl_bt_params *bt_params;
        const struct iwl_eeprom_params *eeprom_params;
-       const bool need_temp_offset_calib; /* if used set to true */
-       const bool no_xtal_calib;
        enum iwl_led_mode led_mode;
-       const bool adv_pm;
        const bool rx_with_siso_diversity;
        const bool internal_wimax_coex;
-       const bool temp_offset_v2;
 };
 
 /*
  * This list declares the config structures for all devices.
  */
+#if IS_ENABLED(CONFIG_IWLDVM)
 extern const struct iwl_cfg iwl5300_agn_cfg;
 extern const struct iwl_cfg iwl5100_agn_cfg;
 extern const struct iwl_cfg iwl5350_agn_cfg;
@@ -319,7 +280,14 @@ extern const struct iwl_cfg iwl6035_2agn_cfg;
 extern const struct iwl_cfg iwl105_bgn_cfg;
 extern const struct iwl_cfg iwl105_bgn_d_cfg;
 extern const struct iwl_cfg iwl135_bgn_cfg;
+#endif /* CONFIG_IWLDVM */
+#if IS_ENABLED(CONFIG_IWLMVM)
 extern const struct iwl_cfg iwl7260_2ac_cfg;
-extern const struct iwl_cfg iwl3160_ac_cfg;
+extern const struct iwl_cfg iwl7260_2n_cfg;
+extern const struct iwl_cfg iwl7260_n_cfg;
+extern const struct iwl_cfg iwl3160_2ac_cfg;
+extern const struct iwl_cfg iwl3160_2n_cfg;
+extern const struct iwl_cfg iwl3160_n_cfg;
+#endif /* CONFIG_IWLMVM */
 
 #endif /* __IWL_CONFIG_H__ */
index 20e845d..a276af4 100644 (file)
 #define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10)
 #define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0)
 
+/*****************************************************************************
+ *                        7000/3000 series SHR DTS addresses                 *
+ *****************************************************************************/
+
+/* Diode Results Register Structure: */
+enum dtd_diode_reg {
+       DTS_DIODE_REG_DIG_VAL                   = 0x000000FF, /* bits [7:0] */
+       DTS_DIODE_REG_VREF_LOW                  = 0x0000FF00, /* bits [15:8] */
+       DTS_DIODE_REG_VREF_HIGH                 = 0x00FF0000, /* bits [23:16] */
+       DTS_DIODE_REG_VREF_ID                   = 0x03000000, /* bits [25:24] */
+       DTS_DIODE_REG_PASS_ONCE                 = 0x80000000, /* bits [31:31] */
+       DTS_DIODE_REG_FLAGS_MSK                 = 0xFF000000, /* bits [31:24] */
+/* Those are the masks INSIDE the flags bit-field: */
+       DTS_DIODE_REG_FLAGS_VREFS_ID_POS        = 0,
+       DTS_DIODE_REG_FLAGS_VREFS_ID            = 0x00000003, /* bits [1:0] */
+       DTS_DIODE_REG_FLAGS_PASS_ONCE_POS       = 7,
+       DTS_DIODE_REG_FLAGS_PASS_ONCE           = 0x00000080, /* bits [7:7] */
+};
+
 #endif /* !__iwl_csr_h__ */
index 8cf5db7..7edb851 100644 (file)
 
 static inline bool iwl_have_debug_level(u32 level)
 {
+#ifdef CONFIG_IWLWIFI_DEBUG
        return iwlwifi_mod_params.debug_level & level;
+#else
+       return false;
+#endif
 }
 
 void __iwl_err(struct device *dev, bool rfkill_prefix, bool only_trace,
index 40fed1f..d0162d4 100644 (file)
@@ -1111,11 +1111,8 @@ void iwl_drv_stop(struct iwl_drv *drv)
 /* shared module parameters */
 struct iwl_mod_params iwlwifi_mod_params = {
        .restart_fw = true,
-       .plcp_check = true,
        .bt_coex_active = true,
        .power_level = IWL_POWER_INDEX_1,
-       .bt_ch_announce = true,
-       .auto_agg = true,
        .wd_disable = true,
        /* the rest are 0 by default */
 };
@@ -1223,19 +1220,14 @@ module_param_named(antenna_coupling, iwlwifi_mod_params.ant_coupling,
 MODULE_PARM_DESC(antenna_coupling,
                 "specify antenna coupling in dB (defualt: 0 dB)");
 
-module_param_named(bt_ch_inhibition, iwlwifi_mod_params.bt_ch_announce,
-                  bool, S_IRUGO);
-MODULE_PARM_DESC(bt_ch_inhibition,
-                "Enable BT channel inhibition (default: enable)");
-
-module_param_named(plcp_check, iwlwifi_mod_params.plcp_check, bool, S_IRUGO);
-MODULE_PARM_DESC(plcp_check, "Check plcp health (default: 1 [enabled])");
-
 module_param_named(wd_disable, iwlwifi_mod_params.wd_disable, int, S_IRUGO);
 MODULE_PARM_DESC(wd_disable,
                "Disable stuck queue watchdog timer 0=system default, "
                "1=disable, 2=enable (default: 0)");
 
+module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
+MODULE_PARM_DESC(nvm_file, "NVM file name");
+
 /*
  * set bt_coex_active to true, uCode will do kill/defer
  * every time the priority line is asserted (BT is sending signals on the
@@ -1269,8 +1261,3 @@ module_param_named(power_level, iwlwifi_mod_params.power_level,
                int, S_IRUGO);
 MODULE_PARM_DESC(power_level,
                 "default power save level (range from 1 - 5, default: 1)");
-
-module_param_named(auto_agg, iwlwifi_mod_params.auto_agg,
-               bool, S_IRUGO);
-MODULE_PARM_DESC(auto_agg,
-                "enable agg w/o check traffic load (default: enable)");
index 7d14509..429337a 100644 (file)
@@ -62,8 +62,7 @@
 
 #ifndef __iwl_drv_h__
 #define __iwl_drv_h__
-
-#include <linux/module.h>
+#include <linux/export.h>
 
 /* for all modules */
 #define DRV_NAME        "iwlwifi"
index 600c9fd..4c887f3 100644 (file)
@@ -732,17 +732,16 @@ int iwl_init_sband_channels(struct iwl_nvm_data *data,
 void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
                          struct iwl_nvm_data *data,
                          struct ieee80211_sta_ht_cap *ht_info,
-                         enum ieee80211_band band)
+                         enum ieee80211_band band,
+                         u8 tx_chains, u8 rx_chains)
 {
        int max_bit_rate = 0;
-       u8 rx_chains;
-       u8 tx_chains;
 
-       tx_chains = hweight8(data->valid_tx_ant);
+       tx_chains = hweight8(tx_chains);
        if (cfg->rx_with_siso_diversity)
                rx_chains = 1;
        else
-               rx_chains = hweight8(data->valid_rx_ant);
+               rx_chains = hweight8(rx_chains);
 
        if (!(data->sku_cap_11n_enable) || !cfg->ht_params) {
                ht_info->ht_supported = false;
@@ -806,7 +805,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
        sband->n_bitrates = N_RATES_24;
        n_used += iwl_init_sband_channels(data, sband, n_channels,
                                          IEEE80211_BAND_2GHZ);
-       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ);
+       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ,
+                            data->valid_tx_ant, data->valid_rx_ant);
 
        sband = &data->bands[IEEE80211_BAND_5GHZ];
        sband->band = IEEE80211_BAND_5GHZ;
@@ -814,7 +814,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
        sband->n_bitrates = N_RATES_52;
        n_used += iwl_init_sband_channels(data, sband, n_channels,
                                          IEEE80211_BAND_5GHZ);
-       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ);
+       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
+                            data->valid_tx_ant, data->valid_rx_ant);
 
        if (n_channels != n_used)
                IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n",
index 37f1153..d73304a 100644 (file)
@@ -133,6 +133,7 @@ int iwl_init_sband_channels(struct iwl_nvm_data *data,
 void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
                          struct iwl_nvm_data *data,
                          struct ieee80211_sta_ht_cap *ht_info,
-                         enum ieee80211_band band);
+                         enum ieee80211_band band,
+                         u8 tx_chains, u8 rx_chains);
 
 #endif /* __iwl_eeprom_parse_h__ */
index c4c446d..f844d5c 100644 (file)
@@ -106,11 +106,14 @@ enum iwl_ucode_type {
 
 /*
  * enumeration of ucode section.
- * This enumeration is used for legacy tlv style (before 16.0 uCode).
+ * This enumeration is used directly for older firmware (before 16.0).
+ * For new firmware, there can be up to 4 sections (see below) but the
+ * first one packaged into the firmware file is the DATA section and
+ * some debugging code accesses that.
  */
 enum iwl_ucode_sec {
-       IWL_UCODE_SECTION_INST,
        IWL_UCODE_SECTION_DATA,
+       IWL_UCODE_SECTION_INST,
 };
 /*
  * For 16.0 uCode and above, there is no differentiation between sections,
index d6f6c37..a1f580c 100644 (file)
@@ -93,7 +93,6 @@ enum iwl_power_level {
  *     use IWL_DISABLE_HT_* constants
  * @amsdu_size_8K: enable 8K amsdu size, default = 0
  * @restart_fw: restart firmware, default = 1
- * @plcp_check: enable plcp health check, default = true
  * @wd_disable: enable stuck queue check, default = 0
  * @bt_coex_active: enable bt coex, default = true
  * @led_mode: system default, default = 0
@@ -101,24 +100,22 @@ enum iwl_power_level {
  * @power_level: power level, default = 1
  * @debug_level: levels are IWL_DL_*
  * @ant_coupling: antenna coupling in dB, default = 0
- * @bt_ch_announce: BT channel inhibition, default = enable
- * @auto_agg: enable agg. without check, default = true
  */
 struct iwl_mod_params {
        int sw_crypto;
        unsigned int disable_11n;
        int amsdu_size_8K;
        bool restart_fw;
-       bool plcp_check;
        int  wd_disable;
        bool bt_coex_active;
        int led_mode;
        bool power_save;
        int power_level;
+#ifdef CONFIG_IWLWIFI_DEBUG
        u32 debug_level;
+#endif
        int ant_coupling;
-       bool bt_ch_announce;
-       bool auto_agg;
+       char *nvm_file;
 };
 
 #endif /* #__iwl_modparams_h__ */
index 6199a0a..acd2665 100644 (file)
@@ -89,6 +89,7 @@ enum nvm_sku_bits {
        NVM_SKU_CAP_BAND_24GHZ  = BIT(0),
        NVM_SKU_CAP_BAND_52GHZ  = BIT(1),
        NVM_SKU_CAP_11N_ENABLE  = BIT(2),
+       NVM_SKU_CAP_11AC_ENABLE = BIT(3),
 };
 
 /* radio config bits (actual values from NVM definition) */
@@ -258,8 +259,6 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
                                  struct iwl_nvm_data *data,
                                  struct ieee80211_sta_vht_cap *vht_cap)
 {
-       /* For now, assume new devices with NVM are VHT capable */
-
        vht_cap->vht_supported = true;
 
        vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 |
@@ -292,7 +291,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
 }
 
 static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
-                           struct iwl_nvm_data *data, const __le16 *nvm_sw)
+                           struct iwl_nvm_data *data, const __le16 *nvm_sw,
+                           bool enable_vht, u8 tx_chains, u8 rx_chains)
 {
        int n_channels = iwl_init_channel_map(dev, cfg, data,
                        &nvm_sw[NVM_CHANNELS]);
@@ -305,7 +305,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
        sband->n_bitrates = N_RATES_24;
        n_used += iwl_init_sband_channels(data, sband, n_channels,
                                          IEEE80211_BAND_2GHZ);
-       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ);
+       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ,
+                            tx_chains, rx_chains);
 
        sband = &data->bands[IEEE80211_BAND_5GHZ];
        sband->band = IEEE80211_BAND_5GHZ;
@@ -313,8 +314,10 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
        sband->n_bitrates = N_RATES_52;
        n_used += iwl_init_sband_channels(data, sband, n_channels,
                                          IEEE80211_BAND_5GHZ);
-       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ);
-       iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap);
+       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
+                            tx_chains, rx_chains);
+       if (enable_vht)
+               iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap);
 
        if (n_channels != n_used)
                IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
@@ -324,7 +327,7 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
 struct iwl_nvm_data *
 iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                   const __le16 *nvm_hw, const __le16 *nvm_sw,
-                  const __le16 *nvm_calib)
+                  const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains)
 {
        struct iwl_nvm_data *data;
        u8 hw_addr[ETH_ALEN];
@@ -380,7 +383,8 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
        data->hw_addr[4] = hw_addr[5];
        data->hw_addr[5] = hw_addr[4];
 
-       iwl_init_sbands(dev, cfg, data, nvm_sw);
+       iwl_init_sbands(dev, cfg, data, nvm_sw, sku & NVM_SKU_CAP_11AC_ENABLE,
+                       tx_chains, rx_chains);
 
        data->calib_version = 255;   /* TODO:
                                        this value will prevent some checks from
index e57fb98..3325059 100644 (file)
@@ -75,6 +75,6 @@
 struct iwl_nvm_data *
 iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                   const __le16 *nvm_hw, const __le16 *nvm_sw,
-                  const __le16 *nvm_calib);
+                  const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains);
 
 #endif /* __iwl_nvm_parse_h__ */
index 25745da..1a405ae 100644 (file)
@@ -92,20 +92,16 @@ struct iwl_phy_db_entry {
 struct iwl_phy_db {
        struct iwl_phy_db_entry cfg;
        struct iwl_phy_db_entry calib_nch;
-       struct iwl_phy_db_entry calib_ch;
        struct iwl_phy_db_entry calib_ch_group_papd[IWL_NUM_PAPD_CH_GROUPS];
        struct iwl_phy_db_entry calib_ch_group_txp[IWL_NUM_TXP_CH_GROUPS];
 
-       u32 channel_num;
-       u32 channel_size;
-
        struct iwl_trans *trans;
 };
 
 enum iwl_phy_db_section_type {
        IWL_PHY_DB_CFG = 1,
        IWL_PHY_DB_CALIB_NCH,
-       IWL_PHY_DB_CALIB_CH,
+       IWL_PHY_DB_UNUSED,
        IWL_PHY_DB_CALIB_CHG_PAPD,
        IWL_PHY_DB_CALIB_CHG_TXP,
        IWL_PHY_DB_MAX
@@ -169,8 +165,6 @@ iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
                return &phy_db->cfg;
        case IWL_PHY_DB_CALIB_NCH:
                return &phy_db->calib_nch;
-       case IWL_PHY_DB_CALIB_CH:
-               return &phy_db->calib_ch;
        case IWL_PHY_DB_CALIB_CHG_PAPD:
                if (chg_id >= IWL_NUM_PAPD_CH_GROUPS)
                        return NULL;
@@ -208,7 +202,6 @@ void iwl_phy_db_free(struct iwl_phy_db *phy_db)
 
        iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
        iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
-       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CH, 0);
        for (i = 0; i < IWL_NUM_PAPD_CH_GROUPS; i++)
                iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
        for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++)
@@ -248,13 +241,6 @@ int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
 
        entry->size = size;
 
-       if (type == IWL_PHY_DB_CALIB_CH) {
-               phy_db->channel_num =
-                       le32_to_cpup((__le32 *)phy_db_notif->data);
-               phy_db->channel_size =
-                       (size - CHANNEL_NUM_SIZE) / phy_db->channel_num;
-       }
-
        IWL_DEBUG_INFO(phy_db->trans,
                       "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
                       __func__, __LINE__, type, size);
@@ -328,10 +314,7 @@ int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
                                u32 type, u8 **data, u16 *size, u16 ch_id)
 {
        struct iwl_phy_db_entry *entry;
-       u32 channel_num;
-       u32 channel_size;
        u16 ch_group_id = 0;
-       u16 index;
 
        if (!phy_db)
                return -EINVAL;
@@ -346,21 +329,8 @@ int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
        if (!entry)
                return -EINVAL;
 
-       if (type == IWL_PHY_DB_CALIB_CH) {
-               index = ch_id_to_ch_index(ch_id);
-               channel_num = phy_db->channel_num;
-               channel_size = phy_db->channel_size;
-               if (index >= channel_num) {
-                       IWL_ERR(phy_db->trans, "Wrong channel number %d\n",
-                               ch_id);
-                       return -EINVAL;
-               }
-               *data = entry->data + CHANNEL_NUM_SIZE + index * channel_size;
-               *size = channel_size;
-       } else {
-               *data = entry->data;
-               *size = entry->size;
-       }
+       *data = entry->data;
+       *size = entry->size;
 
        IWL_DEBUG_INFO(phy_db->trans,
                       "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
@@ -413,6 +383,9 @@ static int iwl_phy_db_send_all_channel_groups(
                if (!entry)
                        return -EINVAL;
 
+               if (WARN_ON_ONCE(!entry->size))
+                       continue;
+
                /* Send the requested PHY DB section */
                err = iwl_send_phy_db_cmd(phy_db,
                                          type,
index 386f2a7..ff8cc75 100644 (file)
 /* Device system time */
 #define DEVICE_SYSTEM_TIME_REG 0xA0206C
 
+/*****************************************************************************
+ *                        7000/3000 series SHR DTS addresses                 *
+ *****************************************************************************/
+
+#define SHR_MISC_WFM_DTS_EN    (0x00a10024)
+#define DTSC_CFG_MODE          (0x00a10604)
+#define DTSC_VREF_AVG          (0x00a10648)
+#define DTSC_VREF5_AVG         (0x00a1064c)
+#define DTSC_CFG_MODE_PERIODIC (0x2)
+#define DTSC_PTAT_AVG          (0x00a10650)
+
+
 /**
  * Tx Scheduler
  *
diff --git a/drivers/net/wireless/iwlwifi/iwl-test.c b/drivers/net/wireless/iwlwifi/iwl-test.c
deleted file mode 100644 (file)
index 5cfd55b..0000000
+++ /dev/null
@@ -1,852 +0,0 @@
-/******************************************************************************
- *
- * 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) 2010 - 2013 Intel Corporation. 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 Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- *  Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
- * 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 Intel Corporation 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 <linux/export.h>
-#include <net/netlink.h>
-
-#include "iwl-drv.h"
-#include "iwl-io.h"
-#include "iwl-fh.h"
-#include "iwl-prph.h"
-#include "iwl-trans.h"
-#include "iwl-test.h"
-#include "iwl-csr.h"
-#include "iwl-testmode.h"
-
-/*
- * Periphery registers absolute lower bound. This is used in order to
- * differentiate registery access through HBUS_TARG_PRPH_* and
- * HBUS_TARG_MEM_* accesses.
- */
-#define IWL_ABS_PRPH_START (0xA00000)
-
-/*
- * The TLVs used in the gnl message policy between the kernel module and
- * user space application. iwl_testmode_gnl_msg_policy is to be carried
- * through the NL80211_CMD_TESTMODE channel regulated by nl80211.
- * See iwl-testmode.h
- */
-static
-struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = {
-       [IWL_TM_ATTR_COMMAND] = { .type = NLA_U32, },
-
-       [IWL_TM_ATTR_UCODE_CMD_ID] = { .type = NLA_U8, },
-       [IWL_TM_ATTR_UCODE_CMD_DATA] = { .type = NLA_UNSPEC, },
-
-       [IWL_TM_ATTR_REG_OFFSET] = { .type = NLA_U32, },
-       [IWL_TM_ATTR_REG_VALUE8] = { .type = NLA_U8, },
-       [IWL_TM_ATTR_REG_VALUE32] = { .type = NLA_U32, },
-
-       [IWL_TM_ATTR_SYNC_RSP] = { .type = NLA_UNSPEC, },
-       [IWL_TM_ATTR_UCODE_RX_PKT] = { .type = NLA_UNSPEC, },
-
-       [IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, },
-
-       [IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, },
-       [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, },
-       [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, },
-
-       [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, },
-
-       [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, },
-
-       [IWL_TM_ATTR_MEM_ADDR] = { .type = NLA_U32, },
-       [IWL_TM_ATTR_BUFFER_SIZE] = { .type = NLA_U32, },
-       [IWL_TM_ATTR_BUFFER_DUMP] = { .type = NLA_UNSPEC, },
-
-       [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, },
-       [IWL_TM_ATTR_DEVICE_ID] = { .type = NLA_U32, },
-       [IWL_TM_ATTR_FW_TYPE] = { .type = NLA_U32, },
-       [IWL_TM_ATTR_FW_INST_SIZE] = { .type = NLA_U32, },
-       [IWL_TM_ATTR_FW_DATA_SIZE] = { .type = NLA_U32, },
-
-       [IWL_TM_ATTR_ENABLE_NOTIFICATION] = {.type = NLA_FLAG, },
-};
-
-static inline void iwl_test_trace_clear(struct iwl_test *tst)
-{
-       memset(&tst->trace, 0, sizeof(struct iwl_test_trace));
-}
-
-static void iwl_test_trace_stop(struct iwl_test *tst)
-{
-       if (!tst->trace.enabled)
-               return;
-
-       if (tst->trace.cpu_addr && tst->trace.dma_addr)
-               dma_free_coherent(tst->trans->dev,
-                                 tst->trace.tsize,
-                                 tst->trace.cpu_addr,
-                                 tst->trace.dma_addr);
-
-       iwl_test_trace_clear(tst);
-}
-
-static inline void iwl_test_mem_clear(struct iwl_test *tst)
-{
-       memset(&tst->mem, 0, sizeof(struct iwl_test_mem));
-}
-
-static inline void iwl_test_mem_stop(struct iwl_test *tst)
-{
-       if (!tst->mem.in_read)
-               return;
-
-       iwl_test_mem_clear(tst);
-}
-
-/*
- * Initializes the test object
- * During the lifetime of the test object it is assumed that the transport is
- * started. The test object should be stopped before the transport is stopped.
- */
-void iwl_test_init(struct iwl_test *tst, struct iwl_trans *trans,
-                  struct iwl_test_ops *ops)
-{
-       tst->trans = trans;
-       tst->ops = ops;
-
-       iwl_test_trace_clear(tst);
-       iwl_test_mem_clear(tst);
-}
-EXPORT_SYMBOL_GPL(iwl_test_init);
-
-/*
- * Stop the test object
- */
-void iwl_test_free(struct iwl_test *tst)
-{
-       iwl_test_mem_stop(tst);
-       iwl_test_trace_stop(tst);
-}
-EXPORT_SYMBOL_GPL(iwl_test_free);
-
-static inline int iwl_test_send_cmd(struct iwl_test *tst,
-                                   struct iwl_host_cmd *cmd)
-{
-       return tst->ops->send_cmd(tst->trans->op_mode, cmd);
-}
-
-static inline bool iwl_test_valid_hw_addr(struct iwl_test *tst, u32 addr)
-{
-       return tst->ops->valid_hw_addr(addr);
-}
-
-static inline u32 iwl_test_fw_ver(struct iwl_test *tst)
-{
-       return tst->ops->get_fw_ver(tst->trans->op_mode);
-}
-
-static inline struct sk_buff*
-iwl_test_alloc_reply(struct iwl_test *tst, int len)
-{
-       return tst->ops->alloc_reply(tst->trans->op_mode, len);
-}
-
-static inline int iwl_test_reply(struct iwl_test *tst, struct sk_buff *skb)
-{
-       return tst->ops->reply(tst->trans->op_mode, skb);
-}
-
-static inline struct sk_buff*
-iwl_test_alloc_event(struct iwl_test *tst, int len)
-{
-       return tst->ops->alloc_event(tst->trans->op_mode, len);
-}
-
-static inline void
-iwl_test_event(struct iwl_test *tst, struct sk_buff *skb)
-{
-       return tst->ops->event(tst->trans->op_mode, skb);
-}
-
-/*
- * This function handles the user application commands to the fw. The fw
- * commands are sent in a synchronuous manner. In case that the user requested
- * to get commands response, it is send to the user.
- */
-static int iwl_test_fw_cmd(struct iwl_test *tst, struct nlattr **tb)
-{
-       struct iwl_host_cmd cmd;
-       struct iwl_rx_packet *pkt;
-       struct sk_buff *skb;
-       void *reply_buf;
-       u32 reply_len;
-       int ret;
-       bool cmd_want_skb;
-
-       memset(&cmd, 0, sizeof(struct iwl_host_cmd));
-
-       if (!tb[IWL_TM_ATTR_UCODE_CMD_ID] ||
-           !tb[IWL_TM_ATTR_UCODE_CMD_DATA]) {
-               IWL_ERR(tst->trans, "Missing fw command mandatory fields\n");
-               return -ENOMSG;
-       }
-
-       cmd.flags = CMD_ON_DEMAND | CMD_SYNC;
-       cmd_want_skb = nla_get_flag(tb[IWL_TM_ATTR_UCODE_CMD_SKB]);
-       if (cmd_want_skb)
-               cmd.flags |= CMD_WANT_SKB;
-
-       cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]);
-       cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
-       cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
-       cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
-       IWL_DEBUG_INFO(tst->trans, "test fw cmd=0x%x, flags 0x%x, len %d\n",
-                      cmd.id, cmd.flags, cmd.len[0]);
-
-       ret = iwl_test_send_cmd(tst, &cmd);
-       if (ret) {
-               IWL_ERR(tst->trans, "Failed to send hcmd\n");
-               return ret;
-       }
-       if (!cmd_want_skb)
-               return ret;
-
-       /* Handling return of SKB to the user */
-       pkt = cmd.resp_pkt;
-       if (!pkt) {
-               IWL_ERR(tst->trans, "HCMD received a null response packet\n");
-               return ret;
-       }
-
-       reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
-       skb = iwl_test_alloc_reply(tst, reply_len + 20);
-       reply_buf = kmemdup(&pkt->hdr, reply_len, GFP_KERNEL);
-       if (!skb || !reply_buf) {
-               kfree_skb(skb);
-               kfree(reply_buf);
-               return -ENOMEM;
-       }
-
-       /* The reply is in a page, that we cannot send to user space. */
-       iwl_free_resp(&cmd);
-
-       if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND,
-                       IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) ||
-           nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf))
-               goto nla_put_failure;
-       return iwl_test_reply(tst, skb);
-
-nla_put_failure:
-       IWL_DEBUG_INFO(tst->trans, "Failed creating NL attributes\n");
-       kfree(reply_buf);
-       kfree_skb(skb);
-       return -ENOMSG;
-}
-
-/*
- * Handles the user application commands for register access.
- */
-static int iwl_test_reg(struct iwl_test *tst, struct nlattr **tb)
-{
-       u32 ofs, val32, cmd;
-       u8 val8;
-       struct sk_buff *skb;
-       int status = 0;
-       struct iwl_trans *trans = tst->trans;
-
-       if (!tb[IWL_TM_ATTR_REG_OFFSET]) {
-               IWL_ERR(trans, "Missing reg offset\n");
-               return -ENOMSG;
-       }
-
-       ofs = nla_get_u32(tb[IWL_TM_ATTR_REG_OFFSET]);
-       IWL_DEBUG_INFO(trans, "test reg access cmd offset=0x%x\n", ofs);
-
-       cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
-
-       /*
-        * Allow access only to FH/CSR/HBUS in direct mode.
-        * Since we don't have the upper bounds for the CSR and HBUS segments,
-        * we will use only the upper bound of FH for sanity check.
-        */
-       if (ofs >= FH_MEM_UPPER_BOUND) {
-               IWL_ERR(trans, "offset out of segment (0x0 - 0x%x)\n",
-                       FH_MEM_UPPER_BOUND);
-               return -EINVAL;
-       }
-
-       switch (cmd) {
-       case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
-               val32 = iwl_read_direct32(tst->trans, ofs);
-               IWL_DEBUG_INFO(trans, "32 value to read 0x%x\n", val32);
-
-               skb = iwl_test_alloc_reply(tst, 20);
-               if (!skb) {
-                       IWL_ERR(trans, "Memory allocation fail\n");
-                       return -ENOMEM;
-               }
-               if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32))
-                       goto nla_put_failure;
-               status = iwl_test_reply(tst, skb);
-               if (status < 0)
-                       IWL_ERR(trans, "Error sending msg : %d\n", status);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
-               if (!tb[IWL_TM_ATTR_REG_VALUE32]) {
-                       IWL_ERR(trans, "Missing value to write\n");
-                       return -ENOMSG;
-               } else {
-                       val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]);
-                       IWL_DEBUG_INFO(trans, "32b write val=0x%x\n", val32);
-                       iwl_write_direct32(tst->trans, ofs, val32);
-               }
-               break;
-
-       case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
-               if (!tb[IWL_TM_ATTR_REG_VALUE8]) {
-                       IWL_ERR(trans, "Missing value to write\n");
-                       return -ENOMSG;
-               } else {
-                       val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]);
-                       IWL_DEBUG_INFO(trans, "8b write val=0x%x\n", val8);
-                       iwl_write8(tst->trans, ofs, val8);
-               }
-               break;
-
-       default:
-               IWL_ERR(trans, "Unknown test register cmd ID\n");
-               return -ENOMSG;
-       }
-
-       return status;
-
-nla_put_failure:
-       kfree_skb(skb);
-       return -EMSGSIZE;
-}
-
-/*
- * Handles the request to start FW tracing. Allocates of the trace buffer
- * and sends a reply to user space with the address of the allocated buffer.
- */
-static int iwl_test_trace_begin(struct iwl_test *tst, struct nlattr **tb)
-{
-       struct sk_buff *skb;
-       int status = 0;
-
-       if (tst->trace.enabled)
-               return -EBUSY;
-
-       if (!tb[IWL_TM_ATTR_TRACE_SIZE])
-               tst->trace.size = TRACE_BUFF_SIZE_DEF;
-       else
-               tst->trace.size =
-                       nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]);
-
-       if (!tst->trace.size)
-               return -EINVAL;
-
-       if (tst->trace.size < TRACE_BUFF_SIZE_MIN ||
-           tst->trace.size > TRACE_BUFF_SIZE_MAX)
-               return -EINVAL;
-
-       tst->trace.tsize = tst->trace.size + TRACE_BUFF_PADD;
-       tst->trace.cpu_addr = dma_alloc_coherent(tst->trans->dev,
-                                                tst->trace.tsize,
-                                                &tst->trace.dma_addr,
-                                                GFP_KERNEL);
-       if (!tst->trace.cpu_addr)
-               return -ENOMEM;
-
-       tst->trace.enabled = true;
-       tst->trace.trace_addr = (u8 *)PTR_ALIGN(tst->trace.cpu_addr, 0x100);
-
-       memset(tst->trace.trace_addr, 0x03B, tst->trace.size);
-
-       skb = iwl_test_alloc_reply(tst, sizeof(tst->trace.dma_addr) + 20);
-       if (!skb) {
-               IWL_ERR(tst->trans, "Memory allocation fail\n");
-               iwl_test_trace_stop(tst);
-               return -ENOMEM;
-       }
-
-       if (nla_put(skb, IWL_TM_ATTR_TRACE_ADDR,
-                   sizeof(tst->trace.dma_addr),
-                   (u64 *)&tst->trace.dma_addr))
-               goto nla_put_failure;
-
-       status = iwl_test_reply(tst, skb);
-       if (status < 0)
-               IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
-
-       tst->trace.nchunks = DIV_ROUND_UP(tst->trace.size,
-                                         DUMP_CHUNK_SIZE);
-
-       return status;
-
-nla_put_failure:
-       kfree_skb(skb);
-       if (nla_get_u32(tb[IWL_TM_ATTR_COMMAND]) ==
-           IWL_TM_CMD_APP2DEV_BEGIN_TRACE)
-               iwl_test_trace_stop(tst);
-       return -EMSGSIZE;
-}
-
-/*
- * Handles indirect read from the periphery or the SRAM. The read is performed
- * to a temporary buffer. The user space application should later issue a dump
- */
-static int iwl_test_indirect_read(struct iwl_test *tst, u32 addr, u32 size)
-{
-       struct iwl_trans *trans = tst->trans;
-       unsigned long flags;
-       int i;
-
-       if (size & 0x3)
-               return -EINVAL;
-
-       tst->mem.size = size;
-       tst->mem.addr = kmalloc(tst->mem.size, GFP_KERNEL);
-       if (tst->mem.addr == NULL)
-               return -ENOMEM;
-
-       /* Hard-coded periphery absolute address */
-       if (IWL_ABS_PRPH_START <= addr &&
-           addr < IWL_ABS_PRPH_START + PRPH_END) {
-                       if (!iwl_trans_grab_nic_access(trans, false, &flags)) {
-                               return -EIO;
-                       }
-                       iwl_write32(trans, HBUS_TARG_PRPH_RADDR,
-                                   addr | (3 << 24));
-                       for (i = 0; i < size; i += 4)
-                               *(u32 *)(tst->mem.addr + i) =
-                                       iwl_read32(trans, HBUS_TARG_PRPH_RDAT);
-                       iwl_trans_release_nic_access(trans, &flags);
-       } else { /* target memory (SRAM) */
-               iwl_trans_read_mem(trans, addr, tst->mem.addr,
-                                  tst->mem.size / 4);
-       }
-
-       tst->mem.nchunks =
-               DIV_ROUND_UP(tst->mem.size, DUMP_CHUNK_SIZE);
-       tst->mem.in_read = true;
-       return 0;
-
-}
-
-/*
- * Handles indirect write to the periphery or SRAM. The  is performed to a
- * temporary buffer.
- */
-static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr,
-       u32 size, unsigned char *buf)
-{
-       struct iwl_trans *trans = tst->trans;
-       u32 val, i;
-       unsigned long flags;
-
-       if (IWL_ABS_PRPH_START <= addr &&
-           addr < IWL_ABS_PRPH_START + PRPH_END) {
-               /* Periphery writes can be 1-3 bytes long, or DWORDs */
-               if (size < 4) {
-                       memcpy(&val, buf, size);
-                       if (!iwl_trans_grab_nic_access(trans, false, &flags))
-                                       return -EIO;
-                       iwl_write32(trans, HBUS_TARG_PRPH_WADDR,
-                                   (addr & 0x0000FFFF) |
-                                   ((size - 1) << 24));
-                       iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val);
-                       iwl_trans_release_nic_access(trans, &flags);
-               } else {
-                       if (size % 4)
-                               return -EINVAL;
-                       for (i = 0; i < size; i += 4)
-                               iwl_write_prph(trans, addr+i,
-                                              *(u32 *)(buf+i));
-               }
-       } else if (iwl_test_valid_hw_addr(tst, addr)) {
-               iwl_trans_write_mem(trans, addr, buf, size / 4);
-       } else {
-               return -EINVAL;
-       }
-       return 0;
-}
-
-/*
- * Handles the user application commands for indirect read/write
- * to/from the periphery or the SRAM.
- */
-static int iwl_test_indirect_mem(struct iwl_test *tst, struct nlattr **tb)
-{
-       u32 addr, size, cmd;
-       unsigned char *buf;
-
-       /* Both read and write should be blocked, for atomicity */
-       if (tst->mem.in_read)
-               return -EBUSY;
-
-       cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
-       if (!tb[IWL_TM_ATTR_MEM_ADDR]) {
-               IWL_ERR(tst->trans, "Error finding memory offset address\n");
-               return -ENOMSG;
-       }
-       addr = nla_get_u32(tb[IWL_TM_ATTR_MEM_ADDR]);
-       if (!tb[IWL_TM_ATTR_BUFFER_SIZE]) {
-               IWL_ERR(tst->trans, "Error finding size for memory reading\n");
-               return -ENOMSG;
-       }
-       size = nla_get_u32(tb[IWL_TM_ATTR_BUFFER_SIZE]);
-
-       if (cmd == IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ) {
-               return iwl_test_indirect_read(tst, addr,  size);
-       } else {
-               if (!tb[IWL_TM_ATTR_BUFFER_DUMP])
-                       return -EINVAL;
-               buf = (unsigned char *)nla_data(tb[IWL_TM_ATTR_BUFFER_DUMP]);
-               return iwl_test_indirect_write(tst, addr, size, buf);
-       }
-}
-
-/*
- * Enable notifications to user space
- */
-static int iwl_test_notifications(struct iwl_test *tst,
-                                 struct nlattr **tb)
-{
-       tst->notify = nla_get_flag(tb[IWL_TM_ATTR_ENABLE_NOTIFICATION]);
-       return 0;
-}
-
-/*
- * Handles the request to get the device id
- */
-static int iwl_test_get_dev_id(struct iwl_test *tst, struct nlattr **tb)
-{
-       u32 devid = tst->trans->hw_id;
-       struct sk_buff *skb;
-       int status;
-
-       IWL_DEBUG_INFO(tst->trans, "hw version: 0x%x\n", devid);
-
-       skb = iwl_test_alloc_reply(tst, 20);
-       if (!skb) {
-               IWL_ERR(tst->trans, "Memory allocation fail\n");
-               return -ENOMEM;
-       }
-
-       if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid))
-               goto nla_put_failure;
-       status = iwl_test_reply(tst, skb);
-       if (status < 0)
-               IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
-
-       return 0;
-
-nla_put_failure:
-       kfree_skb(skb);
-       return -EMSGSIZE;
-}
-
-/*
- * Handles the request to get the FW version
- */
-static int iwl_test_get_fw_ver(struct iwl_test *tst, struct nlattr **tb)
-{
-       struct sk_buff *skb;
-       int status;
-       u32 ver = iwl_test_fw_ver(tst);
-
-       IWL_DEBUG_INFO(tst->trans, "uCode version raw: 0x%x\n", ver);
-
-       skb = iwl_test_alloc_reply(tst, 20);
-       if (!skb) {
-               IWL_ERR(tst->trans, "Memory allocation fail\n");
-               return -ENOMEM;
-       }
-
-       if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION, ver))
-               goto nla_put_failure;
-
-       status = iwl_test_reply(tst, skb);
-       if (status < 0)
-               IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
-
-       return 0;
-
-nla_put_failure:
-       kfree_skb(skb);
-       return -EMSGSIZE;
-}
-
-/*
- * Parse the netlink message and validate that the IWL_TM_ATTR_CMD exists
- */
-int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb,
-                  void *data, int len)
-{
-       int result;
-
-       result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len,
-                       iwl_testmode_gnl_msg_policy);
-       if (result) {
-               IWL_ERR(tst->trans, "Fail parse gnl msg: %d\n", result);
-               return result;
-       }
-
-       /* IWL_TM_ATTR_COMMAND is absolutely mandatory */
-       if (!tb[IWL_TM_ATTR_COMMAND]) {
-               IWL_ERR(tst->trans, "Missing testmode command type\n");
-               return -ENOMSG;
-       }
-       return 0;
-}
-IWL_EXPORT_SYMBOL(iwl_test_parse);
-
-/*
- * Handle test commands.
- * Returns 1 for unknown commands (not handled by the test object); negative
- * value in case of error.
- */
-int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb)
-{
-       int result;
-
-       switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
-       case IWL_TM_CMD_APP2DEV_UCODE:
-               IWL_DEBUG_INFO(tst->trans, "test cmd to uCode\n");
-               result = iwl_test_fw_cmd(tst, tb);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
-       case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
-       case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
-               IWL_DEBUG_INFO(tst->trans, "test cmd to register\n");
-               result = iwl_test_reg(tst, tb);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
-               IWL_DEBUG_INFO(tst->trans, "test uCode trace cmd to driver\n");
-               result = iwl_test_trace_begin(tst, tb);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_END_TRACE:
-               iwl_test_trace_stop(tst);
-               result = 0;
-               break;
-
-       case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ:
-       case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE:
-               IWL_DEBUG_INFO(tst->trans, "test indirect memory cmd\n");
-               result = iwl_test_indirect_mem(tst, tb);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_NOTIFICATIONS:
-               IWL_DEBUG_INFO(tst->trans, "test notifications cmd\n");
-               result = iwl_test_notifications(tst, tb);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_GET_FW_VERSION:
-               IWL_DEBUG_INFO(tst->trans, "test get FW ver cmd\n");
-               result = iwl_test_get_fw_ver(tst, tb);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID:
-               IWL_DEBUG_INFO(tst->trans, "test Get device ID cmd\n");
-               result = iwl_test_get_dev_id(tst, tb);
-               break;
-
-       default:
-               IWL_DEBUG_INFO(tst->trans, "Unknown test command\n");
-               result = 1;
-               break;
-       }
-       return result;
-}
-IWL_EXPORT_SYMBOL(iwl_test_handle_cmd);
-
-static int iwl_test_trace_dump(struct iwl_test *tst, struct sk_buff *skb,
-                              struct netlink_callback *cb)
-{
-       int idx, length;
-
-       if (!tst->trace.enabled || !tst->trace.trace_addr)
-               return -EFAULT;
-
-       idx = cb->args[4];
-       if (idx >= tst->trace.nchunks)
-               return -ENOENT;
-
-       length = DUMP_CHUNK_SIZE;
-       if (((idx + 1) == tst->trace.nchunks) &&
-           (tst->trace.size % DUMP_CHUNK_SIZE))
-               length = tst->trace.size %
-                       DUMP_CHUNK_SIZE;
-
-       if (nla_put(skb, IWL_TM_ATTR_TRACE_DUMP, length,
-                   tst->trace.trace_addr + (DUMP_CHUNK_SIZE * idx)))
-               goto nla_put_failure;
-
-       cb->args[4] = ++idx;
-       return 0;
-
- nla_put_failure:
-       return -ENOBUFS;
-}
-
-static int iwl_test_buffer_dump(struct iwl_test *tst, struct sk_buff *skb,
-                               struct netlink_callback *cb)
-{
-       int idx, length;
-
-       if (!tst->mem.in_read)
-               return -EFAULT;
-
-       idx = cb->args[4];
-       if (idx >= tst->mem.nchunks) {
-               iwl_test_mem_stop(tst);
-               return -ENOENT;
-       }
-
-       length = DUMP_CHUNK_SIZE;
-       if (((idx + 1) == tst->mem.nchunks) &&
-           (tst->mem.size % DUMP_CHUNK_SIZE))
-               length = tst->mem.size % DUMP_CHUNK_SIZE;
-
-       if (nla_put(skb, IWL_TM_ATTR_BUFFER_DUMP, length,
-                   tst->mem.addr + (DUMP_CHUNK_SIZE * idx)))
-               goto nla_put_failure;
-
-       cb->args[4] = ++idx;
-       return 0;
-
- nla_put_failure:
-       return -ENOBUFS;
-}
-
-/*
- * Handle dump commands.
- * Returns 1 for unknown commands (not handled by the test object); negative
- * value in case of error.
- */
-int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb,
-                 struct netlink_callback *cb)
-{
-       int result;
-
-       switch (cmd) {
-       case IWL_TM_CMD_APP2DEV_READ_TRACE:
-               IWL_DEBUG_INFO(tst->trans, "uCode trace cmd\n");
-               result = iwl_test_trace_dump(tst, skb, cb);
-               break;
-
-       case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP:
-               IWL_DEBUG_INFO(tst->trans, "testmode sram dump cmd\n");
-               result = iwl_test_buffer_dump(tst, skb, cb);
-               break;
-
-       default:
-               result = 1;
-               break;
-       }
-       return result;
-}
-IWL_EXPORT_SYMBOL(iwl_test_dump);
-
-/*
- * Multicast a spontaneous messages from the device to the user space.
- */
-static void iwl_test_send_rx(struct iwl_test *tst,
-                            struct iwl_rx_cmd_buffer *rxb)
-{
-       struct sk_buff *skb;
-       struct iwl_rx_packet *data;
-       int length;
-
-       data = rxb_addr(rxb);
-       length = le32_to_cpu(data->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
-
-       /* the length doesn't include len_n_flags field, so add it manually */
-       length += sizeof(__le32);
-
-       skb = iwl_test_alloc_event(tst, length + 20);
-       if (skb == NULL) {
-               IWL_ERR(tst->trans, "Out of memory for message to user\n");
-               return;
-       }
-
-       if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND,
-                       IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) ||
-           nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length, data))
-               goto nla_put_failure;
-
-       iwl_test_event(tst, skb);
-       return;
-
-nla_put_failure:
-       kfree_skb(skb);
-       IWL_ERR(tst->trans, "Ouch, overran buffer, check allocation!\n");
-}
-
-/*
- * Called whenever a Rx frames is recevied from the device. If notifications to
- * the user space are requested, sends the frames to the user.
- */
-void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb)
-{
-       if (tst->notify)
-               iwl_test_send_rx(tst, rxb);
-}
-IWL_EXPORT_SYMBOL(iwl_test_rx);
diff --git a/drivers/net/wireless/iwlwifi/iwl-test.h b/drivers/net/wireless/iwlwifi/iwl-test.h
deleted file mode 100644 (file)
index 8fbd217..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/******************************************************************************
- *
- * 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) 2010 - 2013 Intel Corporation. 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 Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- *  Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
- * 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 Intel Corporation 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.
- *
- *****************************************************************************/
-
-#ifndef __IWL_TEST_H__
-#define __IWL_TEST_H__
-
-#include <linux/types.h>
-#include "iwl-trans.h"
-
-struct iwl_test_trace {
-       u32 size;
-       u32 tsize;
-       u32 nchunks;
-       u8 *cpu_addr;
-       u8 *trace_addr;
-       dma_addr_t dma_addr;
-       bool enabled;
-};
-
-struct iwl_test_mem {
-       u32 size;
-       u32 nchunks;
-       u8 *addr;
-       bool in_read;
-};
-
-/*
- * struct iwl_test_ops: callback to the op mode
- *
- * The structure defines the callbacks that the op_mode should handle,
- * inorder to handle logic that is out of the scope of iwl_test. The
- * op_mode must set all the callbacks.
-
- * @send_cmd: handler that is used by the test object to request the
- *  op_mode to send a command to the fw.
- *
- * @valid_hw_addr: handler that is used by the test object to request the
- *  op_mode to check if the given address is a valid address.
- *
- * @get_fw_ver: handler used to get the FW version.
- *
- * @alloc_reply: handler used by the test object to request the op_mode
- *  to allocate an skb for sending a reply to the user, and initialize
- *  the skb. It is assumed that the test object only fills the required
- *  attributes.
- *
- * @reply: handler used by the test object to request the op_mode to reply
- *  to a request. The skb is an skb previously allocated by the the
- *  alloc_reply callback.
- I
- * @alloc_event: handler used by the test object to request the op_mode
- *  to allocate an skb for sending an event, and initialize
- *  the skb. It is assumed that the test object only fills the required
- *  attributes.
- *
- * @reply: handler used by the test object to request the op_mode to send
- *  an event. The skb is an skb previously allocated by the the
- *  alloc_event callback.
- */
-struct iwl_test_ops {
-       int (*send_cmd)(struct iwl_op_mode *op_modes,
-                       struct iwl_host_cmd *cmd);
-       bool (*valid_hw_addr)(u32 addr);
-       u32 (*get_fw_ver)(struct iwl_op_mode *op_mode);
-
-       struct sk_buff *(*alloc_reply)(struct iwl_op_mode *op_mode, int len);
-       int (*reply)(struct iwl_op_mode *op_mode, struct sk_buff *skb);
-       struct sk_buff* (*alloc_event)(struct iwl_op_mode *op_mode, int len);
-       void (*event)(struct iwl_op_mode *op_mode, struct sk_buff *skb);
-};
-
-struct iwl_test {
-       struct iwl_trans *trans;
-       struct iwl_test_ops *ops;
-       struct iwl_test_trace trace;
-       struct iwl_test_mem mem;
-       bool notify;
-};
-
-void iwl_test_init(struct iwl_test *tst, struct iwl_trans *trans,
-                  struct iwl_test_ops *ops);
-
-void iwl_test_free(struct iwl_test *tst);
-
-int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb,
-                  void *data, int len);
-
-int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb);
-
-int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb,
-                 struct netlink_callback *cb);
-
-void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb);
-
-static inline void iwl_test_enable_notifications(struct iwl_test *tst,
-                                                bool enable)
-{
-       tst->notify = enable;
-}
-
-#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.h b/drivers/net/wireless/iwlwifi/iwl-testmode.h
deleted file mode 100644 (file)
index 98f48a9..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-/******************************************************************************
- *
- * 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) 2010 - 2013 Intel Corporation. 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 Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- *  Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
- * 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 Intel Corporation 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.
- *
- *****************************************************************************/
-#ifndef __IWL_TESTMODE_H__
-#define __IWL_TESTMODE_H__
-
-#include <linux/types.h>
-
-
-/*
- * Commands from user space to kernel space(IWL_TM_CMD_ID_APP2DEV_XX) and
- * from and kernel space to user space(IWL_TM_CMD_ID_DEV2APP_XX).
- * The command ID is carried with IWL_TM_ATTR_COMMAND.
- *
- * @IWL_TM_CMD_APP2DEV_UCODE:
- *     commands from user application to the uCode,
- *     the actual uCode host command ID is carried with
- *     IWL_TM_ATTR_UCODE_CMD_ID
- *
- * @IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
- * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
- * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
- *     commands from user applicaiton to access register
- *
- * @IWL_TM_CMD_APP2DEV_GET_DEVICENAME: retrieve device name
- * @IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: load initial uCode image
- * @IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: perform calibration
- * @IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: load runtime uCode image
- * @IWL_TM_CMD_APP2DEV_GET_EEPROM: request EEPROM data
- * @IWL_TM_CMD_APP2DEV_FIXRATE_REQ: set fix MCS
- *     commands fom user space for pure driver level operations
- *
- * @IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
- * @IWL_TM_CMD_APP2DEV_END_TRACE:
- * @IWL_TM_CMD_APP2DEV_READ_TRACE:
- *     commands fom user space for uCode trace operations
- *
- * @IWL_TM_CMD_DEV2APP_SYNC_RSP:
- *     commands from kernel space to carry the synchronous response
- *     to user application
- * @IWL_TM_CMD_DEV2APP_UCODE_RX_PKT:
- *     commands from kernel space to multicast the spontaneous messages
- *     to user application, or reply of host commands
- * @IWL_TM_CMD_DEV2APP_EEPROM_RSP:
- *     commands from kernel space to carry the eeprom response
- *     to user application
- *
- * @IWL_TM_CMD_APP2DEV_OWNERSHIP:
- *     commands from user application to own change the ownership of the uCode
- *     if application has the ownership, the only host command from
- *     testmode will deliver to uCode. Default owner is driver
- *
- * @IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: load Wake On Wireless LAN uCode image
- * @IWL_TM_CMD_APP2DEV_GET_FW_VERSION: retrieve uCode version
- * @IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: retrieve ID information in device
- * @IWL_TM_CMD_APP2DEV_GET_FW_INFO:
- *     retrieve information of existing loaded uCode image
- *
- * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ:
- * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP:
- * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE:
- *     Commands to read/write data from periphery or SRAM memory ranges.
- *     Fore reading, a READ command is sent from the userspace and the data
- *     is returned when the user calls a DUMP command.
- *     For writing, only a WRITE command is used.
- * @IWL_TM_CMD_APP2DEV_NOTIFICATIONS:
- *     Command to enable/disable notifications (currently RX packets) from the
- *     driver to userspace.
- */
-enum iwl_tm_cmd_t {
-       IWL_TM_CMD_APP2DEV_UCODE                = 1,
-       IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32    = 2,
-       IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32   = 3,
-       IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8    = 4,
-       IWL_TM_CMD_APP2DEV_GET_DEVICENAME       = 5,
-       IWL_TM_CMD_APP2DEV_LOAD_INIT_FW         = 6,
-       IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB       = 7,
-       IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW      = 8,
-       IWL_TM_CMD_APP2DEV_GET_EEPROM           = 9,
-       IWL_TM_CMD_APP2DEV_FIXRATE_REQ          = 10,
-       IWL_TM_CMD_APP2DEV_BEGIN_TRACE          = 11,
-       IWL_TM_CMD_APP2DEV_END_TRACE            = 12,
-       IWL_TM_CMD_APP2DEV_READ_TRACE           = 13,
-       IWL_TM_CMD_DEV2APP_SYNC_RSP             = 14,
-       IWL_TM_CMD_DEV2APP_UCODE_RX_PKT         = 15,
-       IWL_TM_CMD_DEV2APP_EEPROM_RSP           = 16,
-       IWL_TM_CMD_APP2DEV_OWNERSHIP            = 17,
-       RESERVED_18                             = 18,
-       RESERVED_19                             = 19,
-       RESERVED_20                             = 20,
-       RESERVED_21                             = 21,
-       IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW       = 22,
-       IWL_TM_CMD_APP2DEV_GET_FW_VERSION       = 23,
-       IWL_TM_CMD_APP2DEV_GET_DEVICE_ID        = 24,
-       IWL_TM_CMD_APP2DEV_GET_FW_INFO          = 25,
-       IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ = 26,
-       IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP = 27,
-       IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE = 28,
-       IWL_TM_CMD_APP2DEV_NOTIFICATIONS        = 29,
-       IWL_TM_CMD_MAX                          = 30,
-};
-
-/*
- * Atrribute filed in testmode command
- * See enum iwl_tm_cmd_t.
- *
- * @IWL_TM_ATTR_NOT_APPLICABLE:
- *     The attribute is not applicable or invalid
- * @IWL_TM_ATTR_COMMAND:
- *     From user space to kernel space:
- *     the command either destines to ucode, driver, or register;
- *     From kernel space to user space:
- *     the command either carries synchronous response,
- *     or the spontaneous message multicast from the device;
- *
- * @IWL_TM_ATTR_UCODE_CMD_ID:
- * @IWL_TM_ATTR_UCODE_CMD_DATA:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE,
- *     The mandatory fields are :
- *     IWL_TM_ATTR_UCODE_CMD_ID for recognizable command ID;
- *     IWL_TM_ATTR_UCODE_CMD_DATA for the actual command payload
- *     to the ucode
- *
- * @IWL_TM_ATTR_REG_OFFSET:
- * @IWL_TM_ATTR_REG_VALUE8:
- * @IWL_TM_ATTR_REG_VALUE32:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_XXX,
- *     The mandatory fields are:
- *     IWL_TM_ATTR_REG_OFFSET for the offset of the target register;
- *     IWL_TM_ATTR_REG_VALUE8 or IWL_TM_ATTR_REG_VALUE32 for value
- *
- * @IWL_TM_ATTR_SYNC_RSP:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_SYNC_RSP,
- *     The mandatory fields are:
- *     IWL_TM_ATTR_SYNC_RSP for the data content responding to the user
- *     application command
- *
- * @IWL_TM_ATTR_UCODE_RX_PKT:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_UCODE_RX_PKT,
- *     The mandatory fields are:
- *     IWL_TM_ATTR_UCODE_RX_PKT for the data content multicast to the user
- *     application
- *
- * @IWL_TM_ATTR_EEPROM:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_EEPROM,
- *     The mandatory fields are:
- *     IWL_TM_ATTR_EEPROM for the data content responging to the user
- *     application
- *
- * @IWL_TM_ATTR_TRACE_ADDR:
- * @IWL_TM_ATTR_TRACE_SIZE:
- * @IWL_TM_ATTR_TRACE_DUMP:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_XXX_TRACE,
- *     The mandatory fields are:
- *     IWL_TM_ATTR_MEM_TRACE_ADDR for the trace address
- *     IWL_TM_ATTR_MEM_TRACE_SIZE for the trace buffer size
- *     IWL_TM_ATTR_MEM_TRACE_DUMP for the trace dump
- *
- * @IWL_TM_ATTR_FIXRATE:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_FIXRATE_REQ,
- *     The mandatory fields are:
- *     IWL_TM_ATTR_FIXRATE for the fixed rate
- *
- * @IWL_TM_ATTR_UCODE_OWNER:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_OWNERSHIP,
- *     The mandatory fields are:
- *     IWL_TM_ATTR_UCODE_OWNER for the new owner
- *
- * @IWL_TM_ATTR_MEM_ADDR:
- * @IWL_TM_ATTR_BUFFER_SIZE:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ
- *     or IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE.
- *     The mandatory fields are:
- *     IWL_TM_ATTR_MEM_ADDR for the address in SRAM/periphery to read/write
- *     IWL_TM_ATTR_BUFFER_SIZE for the buffer size of data to read/write.
- *
- * @IWL_TM_ATTR_BUFFER_DUMP:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP,
- *     IWL_TM_ATTR_BUFFER_DUMP is used for the data that was read.
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE,
- *     this attribute contains the data to write.
- *
- * @IWL_TM_ATTR_FW_VERSION:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_VERSION,
- *     IWL_TM_ATTR_FW_VERSION for the uCode version
- *
- * @IWL_TM_ATTR_DEVICE_ID:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_DEVICE_ID,
- *     IWL_TM_ATTR_DEVICE_ID for the device ID information
- *
- * @IWL_TM_ATTR_FW_TYPE:
- * @IWL_TM_ATTR_FW_INST_SIZE:
- * @IWL_TM_ATTR_FW_DATA_SIZE:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_INFO,
- *     The mandatory fields are:
- *     IWL_TM_ATTR_FW_TYPE for the uCode type (INIT/RUNTIME/...)
- *     IWL_TM_ATTR_FW_INST_SIZE for the size of instruction section
- *     IWL_TM_ATTR_FW_DATA_SIZE for the size of data section
- *
- * @IWL_TM_ATTR_UCODE_CMD_SKB:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE this flag
- *     indicates that the user wants to receive the response of the command
- *     in a reply SKB. If it's not present, the response is not returned.
- * @IWL_TM_ATTR_ENABLE_NOTIFICATIONS:
- *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_NOTIFICATIONS, this
- *     flag enables (if present) or disables (if not) the forwarding
- *     to userspace.
- */
-enum iwl_tm_attr_t {
-       IWL_TM_ATTR_NOT_APPLICABLE              = 0,
-       IWL_TM_ATTR_COMMAND                     = 1,
-       IWL_TM_ATTR_UCODE_CMD_ID                = 2,
-       IWL_TM_ATTR_UCODE_CMD_DATA              = 3,
-       IWL_TM_ATTR_REG_OFFSET                  = 4,
-       IWL_TM_ATTR_REG_VALUE8                  = 5,
-       IWL_TM_ATTR_REG_VALUE32                 = 6,
-       IWL_TM_ATTR_SYNC_RSP                    = 7,
-       IWL_TM_ATTR_UCODE_RX_PKT                = 8,
-       IWL_TM_ATTR_EEPROM                      = 9,
-       IWL_TM_ATTR_TRACE_ADDR                  = 10,
-       IWL_TM_ATTR_TRACE_SIZE                  = 11,
-       IWL_TM_ATTR_TRACE_DUMP                  = 12,
-       IWL_TM_ATTR_FIXRATE                     = 13,
-       IWL_TM_ATTR_UCODE_OWNER                 = 14,
-       IWL_TM_ATTR_MEM_ADDR                    = 15,
-       IWL_TM_ATTR_BUFFER_SIZE                 = 16,
-       IWL_TM_ATTR_BUFFER_DUMP                 = 17,
-       IWL_TM_ATTR_FW_VERSION                  = 18,
-       IWL_TM_ATTR_DEVICE_ID                   = 19,
-       IWL_TM_ATTR_FW_TYPE                     = 20,
-       IWL_TM_ATTR_FW_INST_SIZE                = 21,
-       IWL_TM_ATTR_FW_DATA_SIZE                = 22,
-       IWL_TM_ATTR_UCODE_CMD_SKB               = 23,
-       IWL_TM_ATTR_ENABLE_NOTIFICATION         = 24,
-       IWL_TM_ATTR_MAX                         = 25,
-};
-
-/* uCode trace buffer */
-#define TRACE_BUFF_SIZE_MAX    0x200000
-#define TRACE_BUFF_SIZE_MIN    0x20000
-#define TRACE_BUFF_SIZE_DEF    TRACE_BUFF_SIZE_MIN
-#define TRACE_BUFF_PADD                0x2000
-
-/* Maximum data size of each dump it packet */
-#define DUMP_CHUNK_SIZE                (PAGE_SIZE - 1024)
-
-/* Address offset of data segment in SRAM */
-#define SRAM_DATA_SEG_OFFSET   0x800000
-
-#endif
index 7a13790..8d91422 100644 (file)
@@ -183,13 +183,12 @@ struct iwl_rx_packet {
  * @CMD_ASYNC: Return right away and don't want for the response
  * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the
  *     response. The caller needs to call iwl_free_resp when done.
- * @CMD_ON_DEMAND: This command is sent by the test mode pipe.
  */
 enum CMD_MODE {
        CMD_SYNC                = 0,
        CMD_ASYNC               = BIT(0),
        CMD_WANT_SKB            = BIT(1),
-       CMD_ON_DEMAND           = BIT(2),
+       CMD_SEND_IN_RFKILL      = BIT(2),
 };
 
 #define DEF_CMD_PAYLOAD_SIZE 320
@@ -427,8 +426,9 @@ struct iwl_trans_ops {
        void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
        void (*stop_device)(struct iwl_trans *trans);
 
-       void (*d3_suspend)(struct iwl_trans *trans);
-       int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status);
+       void (*d3_suspend)(struct iwl_trans *trans, bool test);
+       int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status,
+                        bool test);
 
        int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
 
@@ -455,7 +455,7 @@ struct iwl_trans_ops {
        int (*read_mem)(struct iwl_trans *trans, u32 addr,
                        void *buf, int dwords);
        int (*write_mem)(struct iwl_trans *trans, u32 addr,
-                        void *buf, int dwords);
+                        const void *buf, int dwords);
        void (*configure)(struct iwl_trans *trans,
                          const struct iwl_trans_config *trans_cfg);
        void (*set_pmi)(struct iwl_trans *trans, bool state);
@@ -587,17 +587,18 @@ static inline void iwl_trans_stop_device(struct iwl_trans *trans)
        trans->state = IWL_TRANS_NO_FW;
 }
 
-static inline void iwl_trans_d3_suspend(struct iwl_trans *trans)
+static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test)
 {
        might_sleep();
-       trans->ops->d3_suspend(trans);
+       trans->ops->d3_suspend(trans, test);
 }
 
 static inline int iwl_trans_d3_resume(struct iwl_trans *trans,
-                                     enum iwl_d3_status *status)
+                                     enum iwl_d3_status *status,
+                                     bool test)
 {
        might_sleep();
-       return trans->ops->d3_resume(trans, status);
+       return trans->ops->d3_resume(trans, status, test);
 }
 
 static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
@@ -761,7 +762,7 @@ static inline u32 iwl_trans_read_mem32(struct iwl_trans *trans, u32 addr)
 }
 
 static inline int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr,
-                                     void *buf, int dwords)
+                                     const void *buf, int dwords)
 {
        return trans->ops->write_mem(trans, addr, buf, dwords);
 }
index 2acc44b..ff856e5 100644 (file)
@@ -3,7 +3,7 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
 iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
 iwlmvm-y += scan.o time-event.o rs.o
 iwlmvm-y += power.o bt-coex.o
-iwlmvm-y += led.o
+iwlmvm-y += led.o tt.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
 iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
 
index 810bfa5..dbd622a 100644 (file)
@@ -174,7 +174,7 @@ static const __le32 iwl_tight_lookup[BT_COEX_LUT_SIZE] = {
 static const __le32 iwl_loose_lookup[BT_COEX_LUT_SIZE] = {
        cpu_to_le32(0xaaaaaaaa),
        cpu_to_le32(0xaaaaaaaa),
-       cpu_to_le32(0xaeaaaaaa),
+       cpu_to_le32(0xaaaaaaaa),
        cpu_to_le32(0xaaaaaaaa),
        cpu_to_le32(0xcc00ff28),
        cpu_to_le32(0x0000aaaa),
@@ -202,6 +202,22 @@ static const __le32 iwl_concurrent_lookup[BT_COEX_LUT_SIZE] = {
        cpu_to_le32(0x00000000),
 };
 
+/* single shared antenna */
+static const __le32 iwl_single_shared_ant_lookup[BT_COEX_LUT_SIZE] = {
+       cpu_to_le32(0x40000000),
+       cpu_to_le32(0x00000000),
+       cpu_to_le32(0x44000000),
+       cpu_to_le32(0x00000000),
+       cpu_to_le32(0x40000000),
+       cpu_to_le32(0x00000000),
+       cpu_to_le32(0x44000000),
+       cpu_to_le32(0x00000000),
+       cpu_to_le32(0xC0004000),
+       cpu_to_le32(0xF0005000),
+       cpu_to_le32(0xC0004000),
+       cpu_to_le32(0xF0005000),
+};
+
 int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
 {
        struct iwl_bt_coex_cmd cmd = {
@@ -225,7 +241,10 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
                                        BT_VALID_REDUCED_TX_POWER |
                                        BT_VALID_LUT);
 
-       if (is_loose_coex())
+       if (mvm->cfg->bt_shared_single_ant)
+               memcpy(&cmd.decision_lut, iwl_single_shared_ant_lookup,
+                      sizeof(iwl_single_shared_ant_lookup));
+       else if (is_loose_coex())
                memcpy(&cmd.decision_lut, iwl_loose_lookup,
                       sizeof(iwl_tight_lookup));
        else
@@ -351,6 +370,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        enum ieee80211_band band;
        int ave_rssi;
 
+       lockdep_assert_held(&mvm->mutex);
        if (vif->type != NL80211_IFTYPE_STATION)
                return;
 
@@ -365,7 +385,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
        if (band != IEEE80211_BAND_2GHZ) {
-               ieee80211_request_smps(vif, smps_mode);
+               iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+                                   smps_mode);
                return;
        }
 
@@ -380,7 +401,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                       mvmvif->id,  data->notif->bt_status,
                       data->notif->bt_traffic_load, smps_mode);
 
-       ieee80211_request_smps(vif, smps_mode);
+       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
 
        /* don't reduce the Tx power if in loose scheme */
        if (is_loose_coex())
index 16bbdcc..7e5e5c2 100644 (file)
@@ -63,6 +63,7 @@
 
 #include <linux/etherdevice.h>
 #include <linux/ip.h>
+#include <linux/fs.h>
 #include <net/cfg80211.h>
 #include <net/ipv6.h>
 #include <net/tcp.h>
@@ -419,8 +420,7 @@ static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr)
        return cpu_to_le16(be16_to_cpu((__force __be16)check));
 }
 
-static void iwl_mvm_build_tcp_packet(struct iwl_mvm *mvm,
-                                    struct ieee80211_vif *vif,
+static void iwl_mvm_build_tcp_packet(struct ieee80211_vif *vif,
                                     struct cfg80211_wowlan_tcp *tcp,
                                     void *_pkt, u8 *mask,
                                     __le16 *pseudo_hdr_csum,
@@ -566,21 +566,21 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* SYN (TX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->syn_tx.data, NULL,
+               vif, tcp, cfg->syn_tx.data, NULL,
                &cfg->syn_tx.info.tcp_pseudo_header_checksum,
                MVM_TCP_TX_SYN);
        cfg->syn_tx.info.tcp_payload_length = 0;
 
        /* SYN/ACK (RX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask,
+               vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask,
                &cfg->synack_rx.info.tcp_pseudo_header_checksum,
                MVM_TCP_RX_SYNACK);
        cfg->synack_rx.info.tcp_payload_length = 0;
 
        /* KEEPALIVE/ACK (TX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->keepalive_tx.data, NULL,
+               vif, tcp, cfg->keepalive_tx.data, NULL,
                &cfg->keepalive_tx.info.tcp_pseudo_header_checksum,
                MVM_TCP_TX_DATA);
        cfg->keepalive_tx.info.tcp_payload_length =
@@ -604,7 +604,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* ACK (RX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->keepalive_ack_rx.data,
+               vif, tcp, cfg->keepalive_ack_rx.data,
                cfg->keepalive_ack_rx.rx_mask,
                &cfg->keepalive_ack_rx.info.tcp_pseudo_header_checksum,
                MVM_TCP_RX_ACK);
@@ -612,7 +612,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* WAKEUP (RX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask,
+               vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask,
                &cfg->wake_rx.info.tcp_pseudo_header_checksum,
                MVM_TCP_RX_WAKE);
        cfg->wake_rx.info.tcp_payload_length =
@@ -620,7 +620,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* FIN */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->fin_tx.data, NULL,
+               vif, tcp, cfg->fin_tx.data, NULL,
                &cfg->fin_tx.info.tcp_pseudo_header_checksum,
                MVM_TCP_TX_FIN);
        cfg->fin_tx.info.tcp_payload_length = 0;
@@ -756,7 +756,9 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        return 0;
 }
 
-int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
+                            struct cfg80211_wowlan *wowlan,
+                            bool test)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_d3_iter_data suspend_iter_data = {
@@ -769,7 +771,7 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
        struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
        struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
-       struct iwl_d3_manager_config d3_cfg_cmd = {
+       struct iwl_d3_manager_config d3_cfg_cmd_data = {
                /*
                 * Program the minimum sleep time to 10 seconds, as many
                 * platforms have issues processing a wakeup signal while
@@ -777,17 +779,30 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                 */
                .min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
        };
+       struct iwl_host_cmd d3_cfg_cmd = {
+               .id = D3_CONFIG_CMD,
+               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .data[0] = &d3_cfg_cmd_data,
+               .len[0] = sizeof(d3_cfg_cmd_data),
+       };
        struct wowlan_key_data key_data = {
                .use_rsc_tsc = false,
                .tkip = &tkip_cmd,
                .use_tkip = false,
        };
        int ret, i;
+       int len __maybe_unused;
        u16 seq;
        u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT;
 
-       if (WARN_ON(!wowlan))
+       if (!wowlan) {
+               /*
+                * mac80211 shouldn't get here, but for D3 test
+                * it doesn't warrant a warning
+                */
+               WARN_ON(!test);
                return -EINVAL;
+       }
 
        key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
        if (!key_data.rsc_tsc)
@@ -1007,15 +1022,37 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        if (ret)
                goto out;
 
+       ret = iwl_mvm_power_update_mode(mvm, vif);
+       if (ret)
+               goto out;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (mvm->d3_wake_sysassert)
+               d3_cfg_cmd_data.wakeup_flags |=
+                       cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR);
+#endif
+
        /* must be last -- this switches firmware state */
-       ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC,
-                                  sizeof(d3_cfg_cmd), &d3_cfg_cmd);
+       ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd);
        if (ret)
                goto out;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       len = le32_to_cpu(d3_cfg_cmd.resp_pkt->len_n_flags) &
+               FH_RSCSR_FRAME_SIZE_MSK;
+       if (len >= sizeof(u32) * 2) {
+               mvm->d3_test_pme_ptr =
+                       le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data);
+       } else if (test) {
+               /* in test mode we require the pointer */
+               ret = -EIO;
+               goto out;
+       }
+#endif
+       iwl_free_resp(&d3_cfg_cmd);
 
        clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
 
-       iwl_trans_d3_suspend(mvm->trans);
+       iwl_trans_d3_suspend(mvm->trans, test);
  out:
        mvm->aux_sta.sta_id = old_aux_sta_id;
        mvm_ap_sta->sta_id = old_ap_sta_id;
@@ -1030,6 +1067,11 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        return ret;
 }
 
+int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+       return __iwl_mvm_suspend(hw, wowlan, false);
+}
+
 static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
                                         struct ieee80211_vif *vif)
 {
@@ -1214,9 +1256,28 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
        iwl_free_resp(&cmd);
 }
 
-int iwl_mvm_resume(struct ieee80211_hw *hw)
+static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
+{
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       const struct fw_img *img = &mvm->fw->img[IWL_UCODE_WOWLAN];
+       u32 len = img->sec[IWL_UCODE_SECTION_DATA].len;
+       u32 offs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+
+       if (!mvm->store_d3_resume_sram)
+               return;
+
+       if (!mvm->d3_resume_sram) {
+               mvm->d3_resume_sram = kzalloc(len, GFP_KERNEL);
+               if (!mvm->d3_resume_sram)
+                       return;
+       }
+
+       iwl_trans_read_mem_bytes(mvm->trans, offs, mvm->d3_resume_sram, len);
+#endif
+}
+
+static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
 {
-       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_d3_iter_data resume_iter_data = {
                .mvm = mvm,
        };
@@ -1236,7 +1297,7 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
 
        vif = resume_iter_data.vif;
 
-       ret = iwl_trans_d3_resume(mvm->trans, &d3_status);
+       ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test);
        if (ret)
                goto out_unlock;
 
@@ -1245,12 +1306,15 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
                goto out_unlock;
        }
 
+       /* query SRAM first in case we want event logging */
+       iwl_mvm_read_d3_sram(mvm);
+
        iwl_mvm_query_wakeup_reasons(mvm, vif);
 
  out_unlock:
        mutex_unlock(&mvm->mutex);
 
-       if (vif)
+       if (!test && vif)
                ieee80211_resume_disconnect(vif);
 
        /* return 1 to reconfigure the device */
@@ -1258,9 +1322,106 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
        return 1;
 }
 
+int iwl_mvm_resume(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       return __iwl_mvm_resume(mvm, false);
+}
+
 void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
        device_set_wakeup_enable(mvm->trans->dev, enabled);
 }
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
+{
+       struct iwl_mvm *mvm = inode->i_private;
+       int err;
+
+       if (mvm->d3_test_active)
+               return -EBUSY;
+
+       file->private_data = inode->i_private;
+
+       ieee80211_stop_queues(mvm->hw);
+       synchronize_net();
+
+       /* start pseudo D3 */
+       rtnl_lock();
+       err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true);
+       rtnl_unlock();
+       if (err > 0)
+               err = -EINVAL;
+       if (err) {
+               ieee80211_wake_queues(mvm->hw);
+               return err;
+       }
+       mvm->d3_test_active = true;
+       return 0;
+}
+
+static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       u32 pme_asserted;
+
+       while (true) {
+               pme_asserted = iwl_trans_read_mem32(mvm->trans,
+                                                   mvm->d3_test_pme_ptr);
+               if (pme_asserted)
+                       break;
+               if (msleep_interruptible(100))
+                       break;
+       }
+
+       return 0;
+}
+
+static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac,
+                                             struct ieee80211_vif *vif)
+{
+       if (vif->type == NL80211_IFTYPE_STATION)
+               ieee80211_connection_loss(vif);
+}
+
+static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
+{
+       struct iwl_mvm *mvm = inode->i_private;
+       int remaining_time = 10;
+
+       mvm->d3_test_active = false;
+       __iwl_mvm_resume(mvm, true);
+       iwl_abort_notification_waits(&mvm->notif_wait);
+       ieee80211_restart_hw(mvm->hw);
+
+       /* wait for restart and disconnect all interfaces */
+       while (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+              remaining_time > 0) {
+               remaining_time--;
+               msleep(1000);
+       }
+
+       if (remaining_time == 0)
+               IWL_ERR(mvm, "Timed out waiting for HW restart to finish!\n");
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_d3_test_disconn_work_iter, NULL);
+
+       ieee80211_wake_queues(mvm->hw);
+
+       return 0;
+}
+
+const struct file_operations iwl_dbgfs_d3_test_ops = {
+       .llseek = no_llseek,
+       .open = iwl_mvm_d3_test_open,
+       .read = iwl_mvm_d3_test_read,
+       .release = iwl_mvm_d3_test_release,
+};
+#endif
index 2053dcc..e56ed2a 100644 (file)
@@ -145,15 +145,18 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
        char *buf;
        u8 *ptr;
 
+       if (!mvm->ucode_loaded)
+               return -EINVAL;
+
        /* default is to dump the entire data segment */
        if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) {
-               mvm->dbgfs_sram_offset = 0x800000;
-               if (!mvm->ucode_loaded)
-                       return -EINVAL;
                img = &mvm->fw->img[mvm->cur_ucode];
-               mvm->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+               ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+               len = img->sec[IWL_UCODE_SECTION_DATA].len;
+       } else {
+               ofs = mvm->dbgfs_sram_offset;
+               len = mvm->dbgfs_sram_len;
        }
-       len = mvm->dbgfs_sram_len;
 
        bufsz = len * 4 + 256;
        buf = kzalloc(bufsz, GFP_KERNEL);
@@ -167,12 +170,9 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
        }
 
        pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len);
-       pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n",
-                        mvm->dbgfs_sram_offset);
+       pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", ofs);
 
-       iwl_trans_read_mem_bytes(mvm->trans,
-                                mvm->dbgfs_sram_offset,
-                                ptr, len);
+       iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len);
        for (ofs = 0; ofs < len; ofs += 16) {
                pos += scnprintf(buf + pos, bufsz - pos, "0x%.4x ", ofs);
                hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos,
@@ -300,6 +300,168 @@ static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file,
        return count;
 }
 
+static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                enum iwl_dbgfs_pm_mask param, int val)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm;
+
+       dbgfs_pm->mask |= param;
+
+       switch (param) {
+       case MVM_DEBUGFS_PM_KEEP_ALIVE: {
+               struct ieee80211_hw *hw = mvm->hw;
+               int dtimper = hw->conf.ps_dtim_period ?: 1;
+               int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+
+               IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val);
+               if (val * MSEC_PER_SEC < 3 * dtimper_msec) {
+                       IWL_WARN(mvm,
+                                "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n",
+                                val * MSEC_PER_SEC, 3 * dtimper_msec);
+               }
+               dbgfs_pm->keep_alive_seconds = val;
+               break;
+       }
+       case MVM_DEBUGFS_PM_SKIP_OVER_DTIM:
+               IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n",
+                               val ? "enabled" : "disabled");
+               dbgfs_pm->skip_over_dtim = val;
+               break;
+       case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS:
+               IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val);
+               dbgfs_pm->skip_dtim_periods = val;
+               break;
+       case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT:
+               IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val);
+               dbgfs_pm->rx_data_timeout = val;
+               break;
+       case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT:
+               IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val);
+               dbgfs_pm->tx_data_timeout = val;
+               break;
+       case MVM_DEBUGFS_PM_DISABLE_POWER_OFF:
+               IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val);
+               dbgfs_pm->disable_power_off = val;
+       case MVM_DEBUGFS_PM_LPRX_ENA:
+               IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled");
+               dbgfs_pm->lprx_ena = val;
+               break;
+       case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD:
+               IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val);
+               dbgfs_pm->lprx_rssi_threshold = val;
+               break;
+       }
+}
+
+static ssize_t iwl_dbgfs_pm_params_write(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct ieee80211_vif *vif = file->private_data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->dbgfs_data;
+       enum iwl_dbgfs_pm_mask param;
+       char buf[32] = {};
+       int val;
+       int ret;
+
+       if (copy_from_user(buf, user_buf, sizeof(buf)))
+               return -EFAULT;
+
+       if (!strncmp("keep_alive=", buf, 11)) {
+               if (sscanf(buf + 11, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_KEEP_ALIVE;
+       } else if (!strncmp("skip_over_dtim=", buf, 15)) {
+               if (sscanf(buf + 15, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM;
+       } else if (!strncmp("skip_dtim_periods=", buf, 18)) {
+               if (sscanf(buf + 18, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS;
+       } else if (!strncmp("rx_data_timeout=", buf, 16)) {
+               if (sscanf(buf + 16, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT;
+       } else if (!strncmp("tx_data_timeout=", buf, 16)) {
+               if (sscanf(buf + 16, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
+       } else if (!strncmp("disable_power_off=", buf, 18)) {
+               if (sscanf(buf + 18, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF;
+       } else if (!strncmp("lprx=", buf, 5)) {
+               if (sscanf(buf + 5, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_LPRX_ENA;
+       } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) {
+               if (sscanf(buf + 20, "%d", &val) != 1)
+                       return -EINVAL;
+               if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val <
+                   POWER_LPRX_RSSI_THRESHOLD_MIN)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD;
+       } else {
+               return -EINVAL;
+       }
+
+       mutex_lock(&mvm->mutex);
+       iwl_dbgfs_update_pm(mvm, vif, param, val);
+       ret = iwl_mvm_power_update_mode(mvm, vif);
+       mutex_unlock(&mvm->mutex);
+
+       return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ieee80211_vif *vif = file->private_data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->dbgfs_data;
+       struct iwl_powertable_cmd cmd = {};
+       char buf[256];
+       int bufsz = sizeof(buf);
+       int pos = 0;
+
+       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
+                        (cmd.flags &
+                        cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
+                        0 : 1);
+       pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
+                        le32_to_cpu(cmd.skip_dtim_periods));
+       pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
+                        iwlmvm_mod_params.power_scheme);
+       pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
+                        le16_to_cpu(cmd.flags));
+       pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
+                        cmd.keep_alive_seconds);
+
+       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
+               pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
+                                (cmd.flags &
+                                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
+                                1 : 0);
+               pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.rx_data_timeout));
+               pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.tx_data_timeout));
+               if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "lprx_rssi_threshold = %d\n",
+                                        le32_to_cpu(cmd.lprx_rssi_threshold));
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
 static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
                                         char __user *user_buf,
                                         size_t count, loff_t *ppos)
@@ -481,6 +643,255 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
        return count;
 }
 
+static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
+                               enum iwl_dbgfs_bf_mask param, int value)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
+
+       dbgfs_bf->mask |= param;
+
+       switch (param) {
+       case MVM_DEBUGFS_BF_ENERGY_DELTA:
+               dbgfs_bf->bf_energy_delta = value;
+               break;
+       case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA:
+               dbgfs_bf->bf_roaming_energy_delta = value;
+               break;
+       case MVM_DEBUGFS_BF_ROAMING_STATE:
+               dbgfs_bf->bf_roaming_state = value;
+               break;
+       case MVM_DEBUGFS_BF_TEMPERATURE_DELTA:
+               dbgfs_bf->bf_temperature_delta = value;
+               break;
+       case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER:
+               dbgfs_bf->bf_enable_beacon_filter = value;
+               break;
+       case MVM_DEBUGFS_BF_DEBUG_FLAG:
+               dbgfs_bf->bf_debug_flag = value;
+               break;
+       case MVM_DEBUGFS_BF_ESCAPE_TIMER:
+               dbgfs_bf->bf_escape_timer = value;
+               break;
+       case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT:
+               dbgfs_bf->ba_enable_beacon_abort = value;
+               break;
+       case MVM_DEBUGFS_BA_ESCAPE_TIMER:
+               dbgfs_bf->ba_escape_timer = value;
+               break;
+       }
+}
+
+static ssize_t iwl_dbgfs_bf_params_write(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct ieee80211_vif *vif = file->private_data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->dbgfs_data;
+       enum iwl_dbgfs_bf_mask param;
+       char buf[256];
+       int buf_size;
+       int value;
+       int ret = 0;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       if (!strncmp("bf_energy_delta=", buf, 16)) {
+               if (sscanf(buf+16, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ENERGY_DELTA_MIN ||
+                   value > IWL_BF_ENERGY_DELTA_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ENERGY_DELTA;
+       } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) {
+               if (sscanf(buf+24, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN ||
+                   value > IWL_BF_ROAMING_ENERGY_DELTA_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA;
+       } else if (!strncmp("bf_roaming_state=", buf, 17)) {
+               if (sscanf(buf+17, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ROAMING_STATE_MIN ||
+                   value > IWL_BF_ROAMING_STATE_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ROAMING_STATE;
+       } else if (!strncmp("bf_temperature_delta=", buf, 21)) {
+               if (sscanf(buf+21, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_TEMPERATURE_DELTA_MIN ||
+                   value > IWL_BF_TEMPERATURE_DELTA_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_TEMPERATURE_DELTA;
+       } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
+               if (sscanf(buf+24, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < 0 || value > 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER;
+       } else if (!strncmp("bf_debug_flag=", buf, 14)) {
+               if (sscanf(buf+14, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < 0 || value > 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_DEBUG_FLAG;
+       } else if (!strncmp("bf_escape_timer=", buf, 16)) {
+               if (sscanf(buf+16, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ESCAPE_TIMER_MIN ||
+                   value > IWL_BF_ESCAPE_TIMER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ESCAPE_TIMER;
+       } else if (!strncmp("ba_escape_timer=", buf, 16)) {
+               if (sscanf(buf+16, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BA_ESCAPE_TIMER_MIN ||
+                   value > IWL_BA_ESCAPE_TIMER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BA_ESCAPE_TIMER;
+       } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) {
+               if (sscanf(buf+23, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < 0 || value > 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT;
+       } else {
+               return -EINVAL;
+       }
+
+       mutex_lock(&mvm->mutex);
+       iwl_dbgfs_update_bf(vif, param, value);
+       if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) {
+               ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+       } else {
+               if (mvmvif->bf_enabled)
+                       ret = iwl_mvm_enable_beacon_filter(mvm, vif);
+               else
+                       ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+       }
+       mutex_unlock(&mvm->mutex);
+
+       return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ieee80211_vif *vif = file->private_data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       char buf[256];
+       int pos = 0;
+       const size_t bufsz = sizeof(buf);
+       struct iwl_beacon_filter_cmd cmd = {
+               .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT,
+               .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT,
+               .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT,
+               .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT,
+               .bf_enable_beacon_filter = IWL_BF_ENABLE_BEACON_FILTER_DEFAULT,
+               .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT,
+               .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT),
+               .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT),
+               .ba_enable_beacon_abort = IWL_BA_ENABLE_BEACON_ABORT_DEFAULT,
+       };
+
+       iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
+       if (mvmvif->bf_enabled)
+               cmd.bf_enable_beacon_filter = 1;
+       else
+               cmd.bf_enable_beacon_filter = 0;
+
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n",
+                        cmd.bf_energy_delta);
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
+                        cmd.bf_roaming_energy_delta);
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
+                        cmd.bf_roaming_state);
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temperature_delta = %d\n",
+                        cmd.bf_temperature_delta);
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n",
+                        cmd.bf_enable_beacon_filter);
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
+                        cmd.bf_debug_flag);
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
+                        cmd.bf_escape_timer);
+       pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
+                        cmd.ba_escape_timer);
+       pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
+                        cmd.ba_enable_beacon_abort);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static ssize_t iwl_dbgfs_d3_sram_write(struct file *file,
+                                      const char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char buf[8] = {};
+       int store;
+
+       if (copy_from_user(buf, user_buf, sizeof(buf)))
+               return -EFAULT;
+
+       if (sscanf(buf, "%d", &store) != 1)
+               return -EINVAL;
+
+       mvm->store_d3_resume_sram = store;
+
+       return count;
+}
+
+static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       const struct fw_img *img;
+       int ofs, len, pos = 0;
+       size_t bufsz, ret;
+       char *buf;
+       u8 *ptr = mvm->d3_resume_sram;
+
+       img = &mvm->fw->img[IWL_UCODE_WOWLAN];
+       len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+       bufsz = len * 4 + 256;
+       buf = kzalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       pos += scnprintf(buf, bufsz, "D3 SRAM capture: %sabled\n",
+                        mvm->store_d3_resume_sram ? "en" : "dis");
+
+       if (ptr) {
+               for (ofs = 0; ofs < len; ofs += 16) {
+                       pos += scnprintf(buf + pos, bufsz - pos,
+                                        "0x%.4x ", ofs);
+                       hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos,
+                                          bufsz - pos, false);
+                       pos += strlen(buf + pos);
+                       if (bufsz - pos > 0)
+                               buf[pos++] = '\n';
+               }
+       } else {
+               pos += scnprintf(buf + pos, bufsz - pos,
+                                "(no data captured)\n");
+       }
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+
+       kfree(buf);
+
+       return ret;
+}
+#endif
+
 #define MVM_DEBUGFS_READ_FILE_OPS(name)                                        \
 static const struct file_operations iwl_dbgfs_##name##_ops = { \
        .read = iwl_dbgfs_##name##_read,                                \
@@ -524,9 +935,14 @@ MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
 MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
 MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart);
+#ifdef CONFIG_PM_SLEEP
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram);
+#endif
 
 /* Interface specific debugfs entries */
 MVM_DEBUGFS_READ_FILE_OPS(mac_params);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params);
 
 int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
 {
@@ -542,6 +958,13 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
+#ifdef CONFIG_PM_SLEEP
+       MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR);
+       if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR,
+                                mvm->debugfs_dir, &mvm->d3_wake_sysassert))
+               goto err;
+#endif
 
        /*
         * Create a symlink with mac80211. It will be removed when mac80211
@@ -577,9 +1000,19 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                return;
        }
 
+       if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
+           vif->type == NL80211_IFTYPE_STATION && !vif->p2p)
+               MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR |
+                                        S_IRUSR);
+
        MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir,
                                 S_IRUSR);
 
+       if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
+           mvmvif == mvm->bf_allowed_vif)
+               MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir,
+                                        S_IRUSR | S_IWUSR);
+
        /*
         * Create symlink for convenience pointing to interface specific
         * debugfs entries for the driver. For example, under
index 51e015d..6f8b2c1 100644 (file)
@@ -75,13 +75,15 @@ enum iwl_d3_wakeup_flags {
  * struct iwl_d3_manager_config - D3 manager configuration command
  * @min_sleep_time: minimum sleep time (in usec)
  * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags
+ * @wakeup_host_timer: force wakeup after this many seconds
  *
  * The structure is used for the D3_CONFIG_CMD command.
  */
 struct iwl_d3_manager_config {
        __le32 min_sleep_time;
        __le32 wakeup_flags;
-} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_3 */
+       __le32 wakeup_host_timer;
+} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */
 
 
 /* TODO: OFFLOADS_QUERY_API_S_VER_1 */
index d68640e..98b1feb 100644 (file)
 #define MAC_INDEX_MIN_DRIVER   0
 #define NUM_MAC_INDEX_DRIVER   MAC_INDEX_AUX
 
-#define AC_NUM 4 /* Number of access categories */
+enum iwl_ac {
+       AC_BK,
+       AC_BE,
+       AC_VI,
+       AC_VO,
+       AC_NUM,
+};
 
 /**
  * enum iwl_mac_protection_flags - MAC context flags
index 81fe45f..a6da359 100644 (file)
 
 /* Power Management Commands, Responses, Notifications */
 
+/* Radio LP RX Energy Threshold measured in dBm */
+#define POWER_LPRX_RSSI_THRESHOLD      75
+#define POWER_LPRX_RSSI_THRESHOLD_MAX  94
+#define POWER_LPRX_RSSI_THRESHOLD_MIN  30
+
 /**
  * enum iwl_scan_flags - masks for power table command flags
  * @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
@@ -101,20 +106,107 @@ enum iwl_power_flags {
  * @tx_data_timeout:    Minimum time (usec) from last Tx packet for AM to
  *                     PSM transition - legacy PM
  * @sleep_interval:    not in use
- * @keep_alive_beacons:        not in use
+ * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
+ *                     is set. For example, if it is required to skip over
+ *                     one DTIM, this value need to be set to 2 (DTIM periods).
  * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
  *                     Default: 80dbm
  */
 struct iwl_powertable_cmd {
-       /* PM_POWER_TABLE_CMD_API_S_VER_5 */
+       /* PM_POWER_TABLE_CMD_API_S_VER_6 */
        __le16 flags;
        u8 keep_alive_seconds;
        u8 debug_flags;
        __le32 rx_data_timeout;
        __le32 tx_data_timeout;
        __le32 sleep_interval[IWL_POWER_VEC_SIZE];
-       __le32 keep_alive_beacons;
+       __le32 skip_dtim_periods;
        __le32 lprx_rssi_threshold;
 } __packed;
 
+/**
+ * struct iwl_beacon_filter_cmd
+ * REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
+ * @id_and_color: MAC contex identifier
+ * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon
+ *      to driver if delta in Energy values calculated for this and last
+ *      passed beacon is greater than this threshold. Zero value means that
+ *      the Energy change is ignored for beacon filtering, and beacon will
+ *      not be forced to be sent to driver regardless of this delta. Typical
+ *      energy delta 5dB.
+ * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state.
+ *      Send beacon to driver if delta in Energy values calculated for this
+ *      and last passed beacon is greater than this threshold. Zero value
+ *      means that the Energy change is ignored for beacon filtering while in
+ *      Roaming state, typical energy delta 1dB.
+ * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values
+ *      calculated for current beacon is less than the threshold, use
+ *      Roaming Energy Delta Threshold, otherwise use normal Energy Delta
+ *      Threshold. Typical energy threshold is -72dBm.
+ * @bf_temperature_delta: Send Beacon to driver if delta in temperature values
+ *      calculated for this and the last passed beacon is greater than  this
+ *      threshold. Zero value means that the temperature changeis ignored for
+ *      beacon filtering; beacons will not be  forced to be sent to driver
+ *      regardless of whether its temerature has been changed.
+ * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled.
+ * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed
+ *      for a specific period of time. Units: Beacons.
+ * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed
+ *      for a longer period of time then this escape-timeout. Units: Beacons.
+ * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled.
+ */
+struct iwl_beacon_filter_cmd {
+       u8 bf_energy_delta;
+       u8 bf_roaming_energy_delta;
+       u8 bf_roaming_state;
+       u8 bf_temperature_delta;
+       u8 bf_enable_beacon_filter;
+       u8 bf_debug_flag;
+       __le16 reserved1;
+       __le32 bf_escape_timer;
+       __le32 ba_escape_timer;
+       u8 ba_enable_beacon_abort;
+       u8 reserved2[3];
+} __packed;
+
+/* Beacon filtering and beacon abort */
+#define IWL_BF_ENERGY_DELTA_DEFAULT 5
+#define IWL_BF_ENERGY_DELTA_MAX 255
+#define IWL_BF_ENERGY_DELTA_MIN 0
+
+#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1
+#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255
+#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0
+
+#define IWL_BF_ROAMING_STATE_DEFAULT 72
+#define IWL_BF_ROAMING_STATE_MAX 255
+#define IWL_BF_ROAMING_STATE_MIN 0
+
+#define IWL_BF_TEMPERATURE_DELTA_DEFAULT 5
+#define IWL_BF_TEMPERATURE_DELTA_MAX 255
+#define IWL_BF_TEMPERATURE_DELTA_MIN 0
+
+#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1
+
+#define IWL_BF_DEBUG_FLAG_DEFAULT 0
+
+#define IWL_BF_ESCAPE_TIMER_DEFAULT 50
+#define IWL_BF_ESCAPE_TIMER_MAX 1024
+#define IWL_BF_ESCAPE_TIMER_MIN 0
+
+#define IWL_BA_ESCAPE_TIMER_DEFAULT 3
+#define IWL_BA_ESCAPE_TIMER_MAX 1024
+#define IWL_BA_ESCAPE_TIMER_MIN 0
+
+#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1
+
+#define IWL_BF_CMD_CONFIG_DEFAULTS                                     \
+       .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT,                 \
+       .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, \
+       .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT,               \
+       .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT,       \
+       .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT,                     \
+       .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT),    \
+       .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT)
+
 #endif
index 007a93b..700cce7 100644 (file)
@@ -134,6 +134,7 @@ enum iwl_tx_flags {
 #define TX_CMD_SEC_WEP                 0x01
 #define TX_CMD_SEC_CCM                 0x02
 #define TX_CMD_SEC_TKIP                        0x03
+#define TX_CMD_SEC_MSK                 0x07
 #define TX_CMD_SEC_WEP_KEY_IDX_POS     6
 #define TX_CMD_SEC_WEP_KEY_IDX_MSK     0xc0
 #define TX_CMD_SEC_KEY128              0x08
@@ -227,10 +228,11 @@ struct iwl_tx_cmd {
        __le16 len;
        __le16 next_frame_len;
        __le32 tx_flags;
-       /* DRAM_SCRATCH_API_U_VER_1 */
-       u8 try_cnt;
-       u8 btkill_cnt;
-       __le16 reserved;
+       struct {
+               u8 try_cnt;
+               u8 btkill_cnt;
+               __le16 reserved;
+       } scratch; /* DRAM_SCRATCH_API_U_VER_1 */
        __le32 rate_n_flags;
        u8 sta_id;
        u8 sec_ctl;
index c638455..cbfb3be 100644 (file)
@@ -139,6 +139,9 @@ enum {
        /* Power */
        POWER_TABLE_CMD = 0x77,
 
+       /* Thermal Throttling*/
+       REPLY_THERMAL_MNG_BACKOFF = 0x7e,
+
        /* Scanning */
        SCAN_REQUEST_CMD = 0x80,
        SCAN_ABORT_CMD = 0x81,
@@ -161,6 +164,8 @@ enum {
        CARD_STATE_CMD = 0xa0,
        CARD_STATE_NOTIFICATION = 0xa1,
 
+       MISSED_BEACONS_NOTIFICATION = 0xa2,
+
        REPLY_RX_PHY_CMD = 0xc0,
        REPLY_RX_MPDU_CMD = 0xc1,
        BA_NOTIF = 0xc5,
@@ -170,6 +175,8 @@ enum {
        BT_COEX_PROT_ENV = 0xcd,
        BT_PROFILE_NOTIFICATION = 0xce,
 
+       REPLY_BEACON_FILTERING_CMD = 0xd2,
+
        REPLY_DEBUG_CMD = 0xf0,
        DEBUG_LOG_MSG = 0xf7,
 
@@ -937,6 +944,24 @@ struct iwl_card_state_notif {
        __le32 flags;
 } __packed; /* CARD_STATE_NTFY_API_S_VER_1 */
 
+/**
+ * struct iwl_missed_beacons_notif - information on missed beacons
+ * ( MISSED_BEACONS_NOTIFICATION = 0xa2 )
+ * @mac_id: interface ID
+ * @consec_missed_beacons_since_last_rx: number of consecutive missed
+ *     beacons since last RX.
+ * @consec_missed_beacons: number of consecutive missed beacons
+ * @num_expected_beacons:
+ * @num_recvd_beacons:
+ */
+struct iwl_missed_beacons_notif {
+       __le32 mac_id;
+       __le32 consec_missed_beacons_since_last_rx;
+       __le32 consec_missed_beacons;
+       __le32 num_expected_beacons;
+       __le32 num_recvd_beacons;
+} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */
+
 /**
  * struct iwl_set_calib_default_cmd - set default value for calibration.
  * ( SET_CALIB_DEFAULT_CMD = 0x8e )
@@ -975,4 +1000,212 @@ struct iwl_mcast_filter_cmd {
        u8 addr_list[0];
 } __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */
 
+struct mvm_statistics_dbg {
+       __le32 burst_check;
+       __le32 burst_count;
+       __le32 wait_for_silence_timeout_cnt;
+       __le32 reserved[3];
+} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */
+
+struct mvm_statistics_div {
+       __le32 tx_on_a;
+       __le32 tx_on_b;
+       __le32 exec_time;
+       __le32 probe_time;
+       __le32 rssi_ant;
+       __le32 reserved2;
+} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */
+
+struct mvm_statistics_general_common {
+       __le32 temperature;   /* radio temperature */
+       __le32 temperature_m; /* radio voltage */
+       struct mvm_statistics_dbg dbg;
+       __le32 sleep_time;
+       __le32 slots_out;
+       __le32 slots_idle;
+       __le32 ttl_timestamp;
+       struct mvm_statistics_div div;
+       __le32 rx_enable_counter;
+       /*
+        * num_of_sos_states:
+        *  count the number of times we have to re-tune
+        *  in order to get out of bad PHY status
+        */
+       __le32 num_of_sos_states;
+} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */
+
+struct mvm_statistics_rx_non_phy {
+       __le32 bogus_cts;       /* CTS received when not expecting CTS */
+       __le32 bogus_ack;       /* ACK received when not expecting ACK */
+       __le32 non_bssid_frames;        /* number of frames with BSSID that
+                                        * doesn't belong to the STA BSSID */
+       __le32 filtered_frames; /* count frames that were dumped in the
+                                * filtering process */
+       __le32 non_channel_beacons;     /* beacons with our bss id but not on
+                                        * our serving channel */
+       __le32 channel_beacons; /* beacons with our bss id and in our
+                                * serving channel */
+       __le32 num_missed_bcon; /* number of missed beacons */
+       __le32 adc_rx_saturation_time;  /* count in 0.8us units the time the
+                                        * ADC was in saturation */
+       __le32 ina_detection_search_time;/* total time (in 0.8us) searched
+                                         * for INA */
+       __le32 beacon_silence_rssi_a;   /* RSSI silence after beacon frame */
+       __le32 beacon_silence_rssi_b;   /* RSSI silence after beacon frame */
+       __le32 beacon_silence_rssi_c;   /* RSSI silence after beacon frame */
+       __le32 interference_data_flag;  /* flag for interference data
+                                        * availability. 1 when data is
+                                        * available. */
+       __le32 channel_load;            /* counts RX Enable time in uSec */
+       __le32 dsp_false_alarms;        /* DSP false alarm (both OFDM
+                                        * and CCK) counter */
+       __le32 beacon_rssi_a;
+       __le32 beacon_rssi_b;
+       __le32 beacon_rssi_c;
+       __le32 beacon_energy_a;
+       __le32 beacon_energy_b;
+       __le32 beacon_energy_c;
+       __le32 num_bt_kills;
+       __le32 mac_id;
+       __le32 directed_data_mpdu;
+} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */
+
+struct mvm_statistics_rx_phy {
+       __le32 ina_cnt;
+       __le32 fina_cnt;
+       __le32 plcp_err;
+       __le32 crc32_err;
+       __le32 overrun_err;
+       __le32 early_overrun_err;
+       __le32 crc32_good;
+       __le32 false_alarm_cnt;
+       __le32 fina_sync_err_cnt;
+       __le32 sfd_timeout;
+       __le32 fina_timeout;
+       __le32 unresponded_rts;
+       __le32 rxe_frame_limit_overrun;
+       __le32 sent_ack_cnt;
+       __le32 sent_cts_cnt;
+       __le32 sent_ba_rsp_cnt;
+       __le32 dsp_self_kill;
+       __le32 mh_format_err;
+       __le32 re_acq_main_rssi_sum;
+       __le32 reserved;
+} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */
+
+struct mvm_statistics_rx_ht_phy {
+       __le32 plcp_err;
+       __le32 overrun_err;
+       __le32 early_overrun_err;
+       __le32 crc32_good;
+       __le32 crc32_err;
+       __le32 mh_format_err;
+       __le32 agg_crc32_good;
+       __le32 agg_mpdu_cnt;
+       __le32 agg_cnt;
+       __le32 unsupport_mcs;
+} __packed;  /* STATISTICS_HT_RX_PHY_API_S_VER_1 */
+
+#define MAX_CHAINS 3
+
+struct mvm_statistics_tx_non_phy_agg {
+       __le32 ba_timeout;
+       __le32 ba_reschedule_frames;
+       __le32 scd_query_agg_frame_cnt;
+       __le32 scd_query_no_agg;
+       __le32 scd_query_agg;
+       __le32 scd_query_mismatch;
+       __le32 frame_not_ready;
+       __le32 underrun;
+       __le32 bt_prio_kill;
+       __le32 rx_ba_rsp_cnt;
+       __s8 txpower[MAX_CHAINS];
+       __s8 reserved;
+       __le32 reserved2;
+} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */
+
+struct mvm_statistics_tx_channel_width {
+       __le32 ext_cca_narrow_ch20[1];
+       __le32 ext_cca_narrow_ch40[2];
+       __le32 ext_cca_narrow_ch80[3];
+       __le32 ext_cca_narrow_ch160[4];
+       __le32 last_tx_ch_width_indx;
+       __le32 rx_detected_per_ch_width[4];
+       __le32 success_per_ch_width[4];
+       __le32 fail_per_ch_width[4];
+}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */
+
+struct mvm_statistics_tx {
+       __le32 preamble_cnt;
+       __le32 rx_detected_cnt;
+       __le32 bt_prio_defer_cnt;
+       __le32 bt_prio_kill_cnt;
+       __le32 few_bytes_cnt;
+       __le32 cts_timeout;
+       __le32 ack_timeout;
+       __le32 expected_ack_cnt;
+       __le32 actual_ack_cnt;
+       __le32 dump_msdu_cnt;
+       __le32 burst_abort_next_frame_mismatch_cnt;
+       __le32 burst_abort_missing_next_frame_cnt;
+       __le32 cts_timeout_collision;
+       __le32 ack_or_ba_timeout_collision;
+       struct mvm_statistics_tx_non_phy_agg agg;
+       struct mvm_statistics_tx_channel_width channel_width;
+} __packed; /* STATISTICS_TX_API_S_VER_4 */
+
+
+struct mvm_statistics_bt_activity {
+       __le32 hi_priority_tx_req_cnt;
+       __le32 hi_priority_tx_denied_cnt;
+       __le32 lo_priority_tx_req_cnt;
+       __le32 lo_priority_tx_denied_cnt;
+       __le32 hi_priority_rx_req_cnt;
+       __le32 hi_priority_rx_denied_cnt;
+       __le32 lo_priority_rx_req_cnt;
+       __le32 lo_priority_rx_denied_cnt;
+} __packed;  /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */
+
+struct mvm_statistics_general {
+       struct mvm_statistics_general_common common;
+       __le32 beacon_filtered;
+       __le32 missed_beacons;
+       __s8 beacon_filter_everage_energy;
+       __s8 beacon_filter_reason;
+       __s8 beacon_filter_current_energy;
+       __s8 beacon_filter_reserved;
+       __le32 beacon_filter_delta_time;
+       struct mvm_statistics_bt_activity bt_activity;
+} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */
+
+struct mvm_statistics_rx {
+       struct mvm_statistics_rx_phy ofdm;
+       struct mvm_statistics_rx_phy cck;
+       struct mvm_statistics_rx_non_phy general;
+       struct mvm_statistics_rx_ht_phy ofdm_ht;
+} __packed; /* STATISTICS_RX_API_S_VER_3 */
+
+/*
+ * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command)
+ *
+ * By default, uCode issues this notification after receiving a beacon
+ * while associated.  To disable this behavior, set DISABLE_NOTIF flag in the
+ * REPLY_STATISTICS_CMD 0x9c, above.
+ *
+ * Statistics counters continue to increment beacon after beacon, but are
+ * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD
+ * 0x9c with CLEAR_STATS bit set (see above).
+ *
+ * uCode also issues this notification during scans.  uCode clears statistics
+ * appropriately so that each notification contains statistics for only the
+ * one channel that has just been scanned.
+ */
+
+struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */
+       __le32 flag;
+       struct mvm_statistics_rx rx;
+       struct mvm_statistics_tx tx;
+       struct mvm_statistics_general general;
+} __packed;
+
 #endif /* __fw_api_h__ */
index e18c92d..cd7c003 100644 (file)
@@ -326,6 +326,17 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
        WARN_ON(ret);
 
+       /*
+        * abort after reading the nvm in case RF Kill is on, we will complete
+        * the init seq later when RF kill will switch to off
+        */
+       if (iwl_mvm_is_radio_killed(mvm)) {
+               IWL_DEBUG_RF_KILL(mvm,
+                                 "jump over all phy activities due to RF kill\n");
+               iwl_remove_notification(&mvm->notif_wait, &calib_wait);
+               return 1;
+       }
+
        /* Send TX valid antennas before triggering calibrations */
        ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
        if (ret)
@@ -388,6 +399,8 @@ out:
 int iwl_mvm_up(struct iwl_mvm *mvm)
 {
        int ret, i;
+       struct ieee80211_channel *chan;
+       struct cfg80211_chan_def chandef;
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -400,8 +413,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                ret = iwl_run_init_mvm_ucode(mvm, false);
                if (ret && !iwlmvm_mod_params.init_dbg) {
                        IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret);
+                       /* this can't happen */
+                       if (WARN_ON(ret > 0))
+                               ret = -ERFKILL;
                        goto error;
                }
+               /* should stop & start HW since that INIT image just loaded */
+               iwl_trans_stop_hw(mvm->trans, false);
+               ret = iwl_trans_start_hw(mvm->trans);
+               if (ret)
+                       return ret;
        }
 
        if (iwlmvm_mod_params.init_dbg)
@@ -443,8 +464,22 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        if (ret)
                goto error;
 
-       IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
+       /* Add all the PHY contexts */
+       chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0];
+       cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+       for (i = 0; i < NUM_PHY_CTX; i++) {
+               /*
+                * The channel used here isn't relevant as it's
+                * going to be overwritten in the other flows.
+                * For now use the first channel we have.
+                */
+               ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i],
+                                          &chandef, 1, 1);
+               if (ret)
+                       goto error;
+       }
 
+       IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
        return 0;
  error:
        iwl_trans_stop_device(mvm->trans);
index b2cc3d9..94aae9c 100644 (file)
@@ -193,14 +193,11 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
 u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
                                struct ieee80211_vif *vif)
 {
-       u32 qmask, ac;
+       u32 qmask = 0, ac;
 
        if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
                return BIT(IWL_MVM_OFFCHANNEL_QUEUE);
 
-       qmask = (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) ?
-               BIT(vif->cab_queue) : 0;
-
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
                if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
                        qmask |= BIT(vif->hw_queue[ac]);
@@ -227,7 +224,7 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
                .found_vif = false,
        };
        u32 ac;
-       int ret;
+       int ret, i;
 
        /*
         * Allocate a MAC ID and a TSF for this MAC, along with the queues
@@ -335,6 +332,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
        mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT;
        mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
 
+       for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++)
+               mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC;
+
        return 0;
 
 exit_fail:
@@ -362,7 +362,7 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                break;
        case NL80211_IFTYPE_AP:
                iwl_trans_ac_txq_enable(mvm->trans, vif->cab_queue,
-                                       IWL_MVM_TX_FIFO_VO);
+                                       IWL_MVM_TX_FIFO_MCAST);
                /* fall through */
        default:
                for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
@@ -550,6 +550,10 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
                cmd->ac[i].fifos_mask = BIT(iwl_mvm_ac_to_tx_fifo[i]);
        }
 
+       /* in AP mode, the MCAST FIFO takes the EDCA params from VO */
+       if (vif->type == NL80211_IFTYPE_AP)
+               cmd->ac[AC_VO].fifos_mask |= BIT(IWL_MVM_TX_FIFO_MCAST);
+
        if (vif->bss_conf.qos)
                cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
 
@@ -861,6 +865,30 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
        return ret;
 }
 
+struct iwl_mvm_mac_ap_iterator_data {
+       struct iwl_mvm *mvm;
+       struct ieee80211_vif *vif;
+       u32 beacon_device_ts;
+       u16 beacon_int;
+};
+
+/* Find the beacon_device_ts and beacon_int for a managed interface */
+static void iwl_mvm_mac_ap_iterator(void *_data, u8 *mac,
+                                   struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_mac_ap_iterator_data *data = _data;
+
+       if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc)
+               return;
+
+       /* Station client has higher priority over P2P client*/
+       if (vif->p2p && data->beacon_device_ts)
+               return;
+
+       data->beacon_device_ts = vif->bss_conf.sync_device_ts;
+       data->beacon_int = vif->bss_conf.beacon_int;
+}
+
 /*
  * Fill the specific data for mac context of type AP of P2P GO
  */
@@ -870,6 +898,11 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
                                         bool add)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_mac_ap_iterator_data data = {
+               .mvm = mvm,
+               .vif = vif,
+               .beacon_device_ts = 0
+       };
 
        ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int);
        ctxt_ap->bi_reciprocal =
@@ -883,16 +916,33 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
        ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue);
 
        /*
-        * Only read the system time when the MAC is being added, when we
+        * Only set the beacon time when the MAC is being added, when we
         * just modify the MAC then we should keep the time -- the firmware
         * can otherwise have a "jumping" TBTT.
         */
-       if (add)
-               mvmvif->ap_beacon_time =
-                       iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG);
+       if (add) {
+               /*
+                * If there is a station/P2P client interface which is
+                * associated, set the AP's TBTT far enough from the station's
+                * TBTT. Otherwise, set it to the current system time
+                */
+               ieee80211_iterate_active_interfaces_atomic(
+                       mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+                       iwl_mvm_mac_ap_iterator, &data);
+
+               if (data.beacon_device_ts) {
+                       u32 rand = (prandom_u32() % (80 - 20)) + 20;
+                       mvmvif->ap_beacon_time = data.beacon_device_ts +
+                               ieee80211_tu_to_usec(data.beacon_int * rand /
+                                                    100);
+               } else {
+                       mvmvif->ap_beacon_time =
+                               iwl_read_prph(mvm->trans,
+                                             DEVICE_SYSTEM_TIME_REG);
+               }
+       }
 
        ctxt_ap->beacon_time = cpu_to_le32(mvmvif->ap_beacon_time);
-
        ctxt_ap->beacon_tsf = 0; /* unused */
 
        /* TODO: Assume that the beacon id == mac context id */
@@ -1047,3 +1097,28 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
                     rate);
        return 0;
 }
+
+static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac,
+                                        struct ieee80211_vif *vif)
+{
+       u16 *id = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (mvmvif->id == *id)
+               ieee80211_beacon_loss(vif);
+}
+
+int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
+                                   struct iwl_rx_cmd_buffer *rxb,
+                                   struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_missed_beacons_notif *missed_beacons = (void *)pkt->data;
+       u16 id = (u16)le32_to_cpu(missed_beacons->mac_id);
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_beacon_loss_iterator,
+                                                  &id);
+       return 0;
+}
index a5eb8c8..e08683b 100644 (file)
 static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
                .max = 1,
-               .types = BIT(NL80211_IFTYPE_STATION) |
-                       BIT(NL80211_IFTYPE_AP),
+               .types = BIT(NL80211_IFTYPE_STATION),
        },
        {
                .max = 1,
-               .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+               .types = BIT(NL80211_IFTYPE_AP) |
+                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
                        BIT(NL80211_IFTYPE_P2P_GO),
        },
        {
@@ -127,6 +127,17 @@ static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
 };
 #endif
 
+static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
+{
+       int i;
+
+       memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts));
+       for (i = 0; i < NUM_PHY_CTX; i++) {
+               mvm->phy_ctxts[i].id = i;
+               mvm->phy_ctxts[i].ref = 0;
+       }
+}
+
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 {
        struct ieee80211_hw *hw = mvm->hw;
@@ -141,7 +152,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_SUPPORTS_PS |
                    IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
                    IEEE80211_HW_AMPDU_AGGREGATION |
-                   IEEE80211_HW_TIMING_BEACON_ONLY;
+                   IEEE80211_HW_TIMING_BEACON_ONLY |
+                   IEEE80211_HW_CONNECTION_MONITOR;
 
        hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
        hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
@@ -158,7 +170,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
-       hw->chanctx_data_size = sizeof(struct iwl_mvm_phy_ctxt);
+       hw->chanctx_data_size = sizeof(u16);
 
        hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_P2P_CLIENT) |
@@ -193,6 +205,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                hw->wiphy->n_addresses++;
        }
 
+       iwl_mvm_reset_phy_ctxts(mvm);
+
        /* we create the 802.11 header and a max-length SSID element */
        hw->wiphy->max_scan_ie_len =
                mvm->fw->ucode_capa.max_probe_length - 24 - 34;
@@ -222,20 +236,20 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
            mvm->trans->ops->d3_suspend &&
            mvm->trans->ops->d3_resume &&
            device_can_wakeup(mvm->trans->dev)) {
-               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                                         WIPHY_WOWLAN_DISCONNECT |
-                                         WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-                                         WIPHY_WOWLAN_RFKILL_RELEASE;
+               mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
+                                   WIPHY_WOWLAN_DISCONNECT |
+                                   WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+                                   WIPHY_WOWLAN_RFKILL_RELEASE;
                if (!iwlwifi_mod_params.sw_crypto)
-                       hw->wiphy->wowlan.flags |=
-                               WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
-                               WIPHY_WOWLAN_GTK_REKEY_FAILURE |
-                               WIPHY_WOWLAN_4WAY_HANDSHAKE;
-
-               hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
-               hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
-               hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
-               hw->wiphy->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
+                       mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+                                            WIPHY_WOWLAN_GTK_REKEY_FAILURE |
+                                            WIPHY_WOWLAN_4WAY_HANDSHAKE;
+
+               mvm->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
+               mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
+               mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+               mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
+               hw->wiphy->wowlan = &mvm->wowlan;
        }
 #endif
 
@@ -252,8 +266,8 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
-       if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) {
-               IWL_DEBUG_DROP(mvm, "Dropping - RF KILL\n");
+       if (iwl_mvm_is_radio_killed(mvm)) {
+               IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
                goto drop;
        }
 
@@ -345,8 +359,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
        iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);
        spin_unlock_bh(&mvm->time_event_lock);
 
-       if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
-               mvmvif->phy_ctxt = NULL;
+       mvmvif->phy_ctxt = NULL;
 }
 
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
@@ -363,6 +376,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
                mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
                iwl_mvm_cleanup_iterator, mvm);
 
+       mvm->p2p_device_vif = NULL;
+
+       iwl_mvm_reset_phy_ctxts(mvm);
        memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
        memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
 
@@ -456,6 +472,20 @@ static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
        iwl_mvm_power_update_mode(mvm, vif);
 }
 
+static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
+{
+       u16 i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       for (i = 0; i < NUM_PHY_CTX; i++)
+               if (!mvm->phy_ctxts[i].ref)
+                       return &mvm->phy_ctxts[i];
+
+       IWL_ERR(mvm, "No available PHY context\n");
+       return NULL;
+}
+
 static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif)
 {
@@ -530,32 +560,34 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
         */
        iwl_mvm_power_update_mode(mvm, vif);
 
+       /* beacon filtering */
+       if (!mvm->bf_allowed_vif &&
+           vif->type == NL80211_IFTYPE_STATION && !vif->p2p){
+               mvm->bf_allowed_vif = mvmvif;
+               vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
+       }
+
+       ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+       if (ret)
+               goto out_release;
+
        /*
         * P2P_DEVICE interface does not have a channel context assigned to it,
         * so a dedicated PHY context is allocated to it and the corresponding
         * MAC context is bound to it at this stage.
         */
        if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
-               struct ieee80211_channel *chan;
-               struct cfg80211_chan_def chandef;
 
-               mvmvif->phy_ctxt = &mvm->phy_ctxt_roc;
-
-               /*
-                * The channel used here isn't relevant as it's
-                * going to be overwritten as part of the ROC flow.
-                * For now use the first channel we have.
-                */
-               chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0];
-               cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
-               ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt,
-                                          &chandef, 1, 1);
-               if (ret)
+               mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+               if (!mvmvif->phy_ctxt) {
+                       ret = -ENOSPC;
                        goto out_remove_mac;
+               }
 
+               iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
                ret = iwl_mvm_binding_add_vif(mvm, vif);
                if (ret)
-                       goto out_remove_phy;
+                       goto out_unref_phy;
 
                ret = iwl_mvm_add_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
                if (ret)
@@ -571,27 +603,17 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
 
  out_unbind:
        iwl_mvm_binding_remove_vif(mvm, vif);
- out_remove_phy:
-       iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt);
+ out_unref_phy:
+       iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
  out_remove_mac:
        mvmvif->phy_ctxt = NULL;
        iwl_mvm_mac_ctxt_remove(mvm, vif);
  out_release:
-       /*
-        * TODO: remove this temporary code.
-        * Currently MVM FW supports power management only on single MAC.
-        * Check if only one additional interface remains after releasing
-        * current one. Update power mode on the remaining interface.
-        */
        if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
-       IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
-                          mvm->vif_count);
-       if (mvm->vif_count == 1) {
-               ieee80211_iterate_active_interfaces(
-                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
-                                       iwl_mvm_power_update_iterator, mvm);
-       }
+       ieee80211_iterate_active_interfaces(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_power_update_iterator, mvm);
        iwl_mvm_mac_ctxt_release(mvm, vif);
  out_unlock:
        mutex_unlock(&mvm->mutex);
@@ -629,8 +651,7 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
                 * By now, all the AC queues are empty. The AGG queues are
                 * empty too. We already got all the Tx responses for all the
                 * packets in the queues. The drain work can have been
-                * triggered. Flush it. This work item takes the mutex, so kill
-                * it before we take it.
+                * triggered. Flush it.
                 */
                flush_work(&mvm->sta_drained_wk);
        }
@@ -646,6 +667,11 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
+       if (mvm->bf_allowed_vif == mvmvif) {
+               mvm->bf_allowed_vif = NULL;
+               vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER;
+       }
+
        iwl_mvm_vif_dbgfs_clean(mvm, vif);
 
        /*
@@ -661,7 +687,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
                mvm->p2p_device_vif = NULL;
                iwl_mvm_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
                iwl_mvm_binding_remove_vif(mvm, vif);
-               iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt);
+               iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
                mvmvif->phy_ctxt = NULL;
        }
 
@@ -748,7 +774,10 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                        if (ret)
                                IWL_ERR(mvm, "failed to update quotas\n");
                }
-       } else if (changes & BSS_CHANGED_DTIM_PERIOD) {
+               ret = iwl_mvm_power_update_mode(mvm, vif);
+               if (ret)
+                       IWL_ERR(mvm, "failed to update power mode\n");
+       } else if (changes & BSS_CHANGED_BEACON_INFO) {
                /*
                 * We received a beacon _after_ association so
                 * remove the session protection.
@@ -756,19 +785,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                iwl_mvm_remove_time_event(mvm, mvmvif,
                                          &mvmvif->time_event_data);
        } else if (changes & BSS_CHANGED_PS) {
-               /*
-                * TODO: remove this temporary code.
-                * Currently MVM FW supports power management only on single
-                * MAC. Avoid power mode update if more than one interface
-                * is active.
-                */
-               IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
-                                  mvm->vif_count);
-               if (mvm->vif_count == 1) {
-                       ret = iwl_mvm_power_update_mode(mvm, vif);
-                       if (ret)
-                               IWL_ERR(mvm, "failed to update power mode\n");
-               }
+               ret = iwl_mvm_power_update_mode(mvm, vif);
+               if (ret)
+                       IWL_ERR(mvm, "failed to update power mode\n");
        }
 }
 
@@ -999,9 +1018,13 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                                             mvmvif->phy_ctxt->channel->band);
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTHORIZED) {
+               /* enable beacon filtering */
+               WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif));
                ret = 0;
        } else if (old_state == IEEE80211_STA_AUTHORIZED &&
                   new_state == IEEE80211_STA_ASSOC) {
+               /* disable beacon filtering */
+               WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif));
                ret = 0;
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTH) {
@@ -1167,29 +1190,107 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
                       enum ieee80211_roc_type type)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct cfg80211_chan_def chandef;
-       int ret;
+       struct iwl_mvm_phy_ctxt *phy_ctxt;
+       int ret, i;
+
+       IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
+                          duration, type);
 
        if (vif->type != NL80211_IFTYPE_P2P_DEVICE) {
                IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type);
                return -EINVAL;
        }
 
-       IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
-                          duration, type);
-
        mutex_lock(&mvm->mutex);
 
+       for (i = 0; i < NUM_PHY_CTX; i++) {
+               phy_ctxt = &mvm->phy_ctxts[i];
+               if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt)
+                       continue;
+
+               if (phy_ctxt->ref && channel == phy_ctxt->channel) {
+                       /*
+                        * Unbind the P2P_DEVICE from the current PHY context,
+                        * and if the PHY context is not used remove it.
+                        */
+                       ret = iwl_mvm_binding_remove_vif(mvm, vif);
+                       if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
+                               goto out_unlock;
+
+                       iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+
+                       /* Bind the P2P_DEVICE to the current PHY Context */
+                       mvmvif->phy_ctxt = phy_ctxt;
+
+                       ret = iwl_mvm_binding_add_vif(mvm, vif);
+                       if (WARN(ret, "Failed binding P2P_DEVICE\n"))
+                               goto out_unlock;
+
+                       iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
+                       goto schedule_time_event;
+               }
+       }
+
+       /* Need to update the PHY context only if the ROC channel changed */
+       if (channel == mvmvif->phy_ctxt->channel)
+               goto schedule_time_event;
+
        cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
-       ret = iwl_mvm_phy_ctxt_changed(mvm, &mvm->phy_ctxt_roc,
-                                      &chandef, 1, 1);
 
+       /*
+        * Change the PHY context configuration as it is currently referenced
+        * only by the P2P Device MAC
+        */
+       if (mvmvif->phy_ctxt->ref == 1) {
+               ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt,
+                                              &chandef, 1, 1);
+               if (ret)
+                       goto out_unlock;
+       } else {
+               /*
+                * The PHY context is shared with other MACs. Need to remove the
+                * P2P Device from the binding, allocate an new PHY context and
+                * create a new binding
+                */
+               phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+               if (!phy_ctxt) {
+                       ret = -ENOSPC;
+                       goto out_unlock;
+               }
+
+               ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef,
+                                              1, 1);
+               if (ret) {
+                       IWL_ERR(mvm, "Failed to change PHY context\n");
+                       goto out_unlock;
+               }
+
+               /* Unbind the P2P_DEVICE from the current PHY context */
+               ret = iwl_mvm_binding_remove_vif(mvm, vif);
+               if (WARN(ret, "Failed unbinding P2P_DEVICE\n"))
+                       goto out_unlock;
+
+               iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+
+               /* Bind the P2P_DEVICE to the new allocated PHY context */
+               mvmvif->phy_ctxt = phy_ctxt;
+
+               ret = iwl_mvm_binding_add_vif(mvm, vif);
+               if (WARN(ret, "Failed binding P2P_DEVICE\n"))
+                       goto out_unlock;
+
+               iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
+       }
+
+schedule_time_event:
        /* Schedule the time events */
        ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type);
 
+out_unlock:
        mutex_unlock(&mvm->mutex);
        IWL_DEBUG_MAC80211(mvm, "leave\n");
-
        return ret;
 }
 
@@ -1211,15 +1312,30 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
                               struct ieee80211_chanctx_conf *ctx)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+       struct iwl_mvm_phy_ctxt *phy_ctxt;
        int ret;
 
+       IWL_DEBUG_MAC80211(mvm, "Add channel context\n");
+
        mutex_lock(&mvm->mutex);
+       phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
+       if (!phy_ctxt) {
+               ret = -ENOSPC;
+               goto out;
+       }
 
-       IWL_DEBUG_MAC80211(mvm, "Add PHY context\n");
-       ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, &ctx->def,
-                                  ctx->rx_chains_static,
-                                  ctx->rx_chains_dynamic);
+       ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
+                                      ctx->rx_chains_static,
+                                      ctx->rx_chains_dynamic);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to add PHY context\n");
+               goto out;
+       }
+
+       iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt);
+       *phy_ctxt_id = phy_ctxt->id;
+out:
        mutex_unlock(&mvm->mutex);
        return ret;
 }
@@ -1228,10 +1344,11 @@ static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
                                   struct ieee80211_chanctx_conf *ctx)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+       struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
 
        mutex_lock(&mvm->mutex);
-       iwl_mvm_phy_ctxt_remove(mvm, phy_ctxt);
+       iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -1240,7 +1357,16 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
                                   u32 changed)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+       struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
+
+       if (WARN_ONCE((phy_ctxt->ref > 1) &&
+                     (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH |
+                                  IEEE80211_CHANCTX_CHANGE_RX_CHAINS |
+                                  IEEE80211_CHANCTX_CHANGE_RADAR)),
+                     "Cannot change PHY. Ref=%d, changed=0x%X\n",
+                     phy_ctxt->ref, changed))
+               return;
 
        mutex_lock(&mvm->mutex);
        iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
@@ -1254,13 +1380,14 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
                                      struct ieee80211_chanctx_conf *ctx)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_phy_ctxt *phyctx = (void *)ctx->drv_priv;
+       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+       struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
        mutex_lock(&mvm->mutex);
 
-       mvmvif->phy_ctxt = phyctx;
+       mvmvif->phy_ctxt = phy_ctxt;
 
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
index 9f46b23..d40d7db 100644 (file)
@@ -73,7 +73,6 @@
 #include "iwl-trans.h"
 #include "iwl-notif-wait.h"
 #include "iwl-eeprom-parse.h"
-#include "iwl-test.h"
 #include "iwl-trans.h"
 #include "sta.h"
 #include "fw-api.h"
@@ -88,6 +87,7 @@ enum iwl_mvm_tx_fifo {
        IWL_MVM_TX_FIFO_BE,
        IWL_MVM_TX_FIFO_VI,
        IWL_MVM_TX_FIFO_VO,
+       IWL_MVM_TX_FIFO_MCAST = 5,
 };
 
 extern struct ieee80211_ops iwl_mvm_hw_ops;
@@ -109,6 +109,7 @@ extern struct iwl_mvm_mod_params iwlmvm_mod_params;
 struct iwl_mvm_phy_ctxt {
        u16 id;
        u16 color;
+       u32 ref;
 
        /*
         * TODO: This should probably be removed. Currently here only for rate
@@ -149,6 +150,64 @@ enum iwl_power_scheme {
 
 #define IWL_CONN_MAX_LISTEN_INTERVAL   70
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+enum iwl_dbgfs_pm_mask {
+       MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0),
+       MVM_DEBUGFS_PM_SKIP_OVER_DTIM = BIT(1),
+       MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2),
+       MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3),
+       MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4),
+       MVM_DEBUGFS_PM_DISABLE_POWER_OFF = BIT(5),
+       MVM_DEBUGFS_PM_LPRX_ENA = BIT(6),
+       MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7),
+};
+
+struct iwl_dbgfs_pm {
+       u8 keep_alive_seconds;
+       u32 rx_data_timeout;
+       u32 tx_data_timeout;
+       bool skip_over_dtim;
+       u8 skip_dtim_periods;
+       bool disable_power_off;
+       bool lprx_ena;
+       u32 lprx_rssi_threshold;
+       int mask;
+};
+
+/* beacon filtering */
+
+enum iwl_dbgfs_bf_mask {
+       MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0),
+       MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1),
+       MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2),
+       MVM_DEBUGFS_BF_TEMPERATURE_DELTA = BIT(3),
+       MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(4),
+       MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(5),
+       MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(6),
+       MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(7),
+       MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(8),
+};
+
+struct iwl_dbgfs_bf {
+       u8 bf_energy_delta;
+       u8 bf_roaming_energy_delta;
+       u8 bf_roaming_state;
+       u8 bf_temperature_delta;
+       u8 bf_enable_beacon_filter;
+       u8 bf_debug_flag;
+       u32 bf_escape_timer;
+       u32 ba_escape_timer;
+       u8 ba_enable_beacon_abort;
+       int mask;
+};
+#endif
+
+enum iwl_mvm_smps_type_request {
+       IWL_MVM_SMPS_REQ_BT_COEX,
+       IWL_MVM_SMPS_REQ_TT,
+       NUM_IWL_MVM_SMPS_REQ,
+};
+
 /**
  * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context
  * @id: between 0 and 3
@@ -163,6 +222,8 @@ enum iwl_power_scheme {
  * @bcast_sta: station used for broadcast packets. Used by the following
  *  vifs: P2P_DEVICE, GO and AP.
  * @beacon_skb: the skb used to hold the AP/GO beacon template
+ * @smps_requests: the requests of of differents parts of the driver, regard
+       the desired smps mode.
  */
 struct iwl_mvm_vif {
        u16 id;
@@ -172,6 +233,8 @@ struct iwl_mvm_vif {
        bool uploaded;
        bool ap_active;
        bool monitor_active;
+       /* indicate whether beacon filtering is enabled */
+       bool bf_enabled;
 
        u32 ap_beacon_time;
 
@@ -214,7 +277,11 @@ struct iwl_mvm_vif {
        struct dentry *dbgfs_dir;
        struct dentry *dbgfs_slink;
        void *dbgfs_data;
+       struct iwl_dbgfs_pm dbgfs_pm;
+       struct iwl_dbgfs_bf dbgfs_bf;
 #endif
+
+       enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ];
 };
 
 static inline struct iwl_mvm_vif *
@@ -223,12 +290,6 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)
        return (void *)vif->drv_priv;
 }
 
-enum iwl_mvm_status {
-       IWL_MVM_STATUS_HW_RFKILL,
-       IWL_MVM_STATUS_ROC_RUNNING,
-       IWL_MVM_STATUS_IN_HW_RESTART,
-};
-
 enum iwl_scan_status {
        IWL_MVM_SCAN_NONE,
        IWL_MVM_SCAN_OS,
@@ -246,6 +307,65 @@ struct iwl_nvm_section {
        const u8 *data;
 };
 
+/*
+ * Tx-backoff threshold
+ * @temperature: The threshold in Celsius
+ * @backoff: The tx-backoff in uSec
+ */
+struct iwl_tt_tx_backoff {
+       s32 temperature;
+       u32 backoff;
+};
+
+#define TT_TX_BACKOFF_SIZE 6
+
+/**
+ * struct iwl_tt_params - thermal throttling parameters
+ * @ct_kill_entry: CT Kill entry threshold
+ * @ct_kill_exit: CT Kill exit threshold
+ * @ct_kill_duration: The time  intervals (in uSec) in which the driver needs
+ *     to checks whether to exit CT Kill.
+ * @dynamic_smps_entry: Dynamic SMPS entry threshold
+ * @dynamic_smps_exit: Dynamic SMPS exit threshold
+ * @tx_protection_entry: TX protection entry threshold
+ * @tx_protection_exit: TX protection exit threshold
+ * @tx_backoff: Array of thresholds for tx-backoff , in ascending order.
+ * @support_ct_kill: Support CT Kill?
+ * @support_dynamic_smps: Support dynamic SMPS?
+ * @support_tx_protection: Support tx protection?
+ * @support_tx_backoff: Support tx-backoff?
+ */
+struct iwl_tt_params {
+       s32 ct_kill_entry;
+       s32 ct_kill_exit;
+       u32 ct_kill_duration;
+       s32 dynamic_smps_entry;
+       s32 dynamic_smps_exit;
+       s32 tx_protection_entry;
+       s32 tx_protection_exit;
+       struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE];
+       bool support_ct_kill;
+       bool support_dynamic_smps;
+       bool support_tx_protection;
+       bool support_tx_backoff;
+};
+
+/**
+ * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure
+ * @ct_kill_exit: worker to exit thermal kill
+ * @dynamic_smps: Is thermal throttling enabled dynamic_smps?
+ * @tx_backoff: The current thremal throttling tx backoff in uSec.
+ * @params: Parameters to configure the thermal throttling algorithm.
+ * @throttle: Is thermal throttling is active?
+ */
+struct iwl_mvm_tt_mgmt {
+       struct delayed_work ct_kill_exit;
+       bool dynamic_smps;
+       u32 tx_backoff;
+       const struct iwl_tt_params *params;
+       bool throttle;
+};
+
 struct iwl_mvm {
        /* for logger access */
        struct device *dev;
@@ -266,6 +386,12 @@ struct iwl_mvm {
 
        unsigned long status;
 
+       /*
+        * for beacon filtering -
+        * currently only one interface can be supported
+        */
+       struct iwl_mvm_vif *bf_allowed_vif;
+
        enum iwl_ucode_type cur_ucode;
        bool ucode_loaded;
        bool init_ucode_run;
@@ -313,7 +439,7 @@ struct iwl_mvm {
        bool prevent_power_down_d3;
 #endif
 
-       struct iwl_mvm_phy_ctxt phy_ctxt_roc;
+       struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX];
 
        struct list_head time_event_list;
        spinlock_t time_event_lock;
@@ -337,12 +463,24 @@ struct iwl_mvm {
        struct ieee80211_vif *p2p_device_vif;
 
 #ifdef CONFIG_PM_SLEEP
+       struct wiphy_wowlan_support wowlan;
        int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       u32 d3_wake_sysassert; /* must be u32 for debugfs_create_bool */
+       bool d3_test_active;
+       bool store_d3_resume_sram;
+       void *d3_resume_sram;
+       u32 d3_test_pme_ptr;
+#endif
 #endif
 
        /* BT-Coex */
        u8 bt_kill_msk;
        struct iwl_bt_coex_profile_notif last_bt_notif;
+
+       /* Thermal Throttling and CTkill */
+       struct iwl_mvm_tt_mgmt thermal_throttle;
+       s32 temperature;        /* Celsius */
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -352,6 +490,19 @@ struct iwl_mvm {
 #define IWL_MAC80211_GET_MVM(_hw)                      \
        IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv))
 
+enum iwl_mvm_status {
+       IWL_MVM_STATUS_HW_RFKILL,
+       IWL_MVM_STATUS_HW_CTKILL,
+       IWL_MVM_STATUS_ROC_RUNNING,
+       IWL_MVM_STATUS_IN_HW_RESTART,
+};
+
+static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
+{
+       return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status) ||
+              test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
+}
+
 extern const u8 iwl_mvm_ac_to_tx_fifo[];
 
 struct iwl_rate_info {
@@ -443,8 +594,10 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
 int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
                             struct cfg80211_chan_def *chandef,
                             u8 chains_static, u8 chains_dynamic);
-void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm,
-                            struct iwl_mvm_phy_ctxt *ctxt);
+void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm,
+                         struct iwl_mvm_phy_ctxt *ctxt);
+void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
+                           struct iwl_mvm_phy_ctxt *ctxt);
 
 /* MAC (virtual interface) programming */
 int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -459,6 +612,9 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
 int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
                            struct iwl_rx_cmd_buffer *rxb,
                            struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
+                                   struct iwl_rx_cmd_buffer *rxb,
+                                   struct iwl_device_cmd *cmd);
 
 /* Bindings */
 int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -523,6 +679,7 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
                              struct inet6_dev *idev);
 void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif, int idx);
+extern const struct file_operations iwl_dbgfs_d3_test_ops;
 
 /* BT Coex */
 int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
@@ -534,4 +691,31 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                           enum ieee80211_rssi_event rssi_event);
 void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 
+/* beacon filtering */
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+void
+iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
+                                        struct iwl_beacon_filter_cmd *cmd);
+#else
+static inline void
+iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
+                                        struct iwl_beacon_filter_cmd *cmd)
+{}
+#endif
+int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif);
+int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
+                                 struct ieee80211_vif *vif);
+
+/* SMPS */
+void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               enum iwl_mvm_smps_type_request req_type,
+                               enum ieee80211_smps_mode smps_request);
+
+/* Thermal management and CT-kill */
+void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
+void iwl_mvm_tt_initialize(struct iwl_mvm *mvm);
+void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
+void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
+
 #endif /* __IWL_MVM_H__ */
index b8ec02f..edb94ea 100644 (file)
@@ -60,6 +60,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  *****************************************************************************/
+#include <linux/firmware.h>
 #include "iwl-trans.h"
 #include "mvm.h"
 #include "iwl-eeprom-parse.h"
@@ -75,31 +76,56 @@ static const int nvm_to_read[] = {
 };
 
 /* Default NVM size to read */
-#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024);
+#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
+#define IWL_MAX_NVM_SECTION_SIZE 6000
 
-static inline void iwl_nvm_fill_read(struct iwl_nvm_access_cmd *cmd,
-                                    u16 offset, u16 length, u16 section)
+#define NVM_WRITE_OPCODE 1
+#define NVM_READ_OPCODE 0
+
+/*
+ * prepare the NVM host command w/ the pointers to the nvm buffer
+ * and send it to fw
+ */
+static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section,
+                              u16 offset, u16 length, const u8 *data)
 {
-       cmd->offset = cpu_to_le16(offset);
-       cmd->length = cpu_to_le16(length);
-       cmd->type = cpu_to_le16(section);
+       struct iwl_nvm_access_cmd nvm_access_cmd = {
+               .offset = cpu_to_le16(offset),
+               .length = cpu_to_le16(length),
+               .type = cpu_to_le16(section),
+               .op_code = NVM_WRITE_OPCODE,
+       };
+       struct iwl_host_cmd cmd = {
+               .id = NVM_ACCESS_CMD,
+               .len = { sizeof(struct iwl_nvm_access_cmd), length },
+               .flags = CMD_SYNC | CMD_SEND_IN_RFKILL,
+               .data = { &nvm_access_cmd, data },
+               /* data may come from vmalloc, so use _DUP */
+               .dataflags = { 0, IWL_HCMD_DFL_DUP },
+       };
+
+       return iwl_mvm_send_cmd(mvm, &cmd);
 }
 
 static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
                              u16 offset, u16 length, u8 *data)
 {
-       struct iwl_nvm_access_cmd nvm_access_cmd = {};
+       struct iwl_nvm_access_cmd nvm_access_cmd = {
+               .offset = cpu_to_le16(offset),
+               .length = cpu_to_le16(length),
+               .type = cpu_to_le16(section),
+               .op_code = NVM_READ_OPCODE,
+       };
        struct iwl_nvm_access_resp *nvm_resp;
        struct iwl_rx_packet *pkt;
        struct iwl_host_cmd cmd = {
                .id = NVM_ACCESS_CMD,
-               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .flags = CMD_SYNC | CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
                .data = { &nvm_access_cmd, },
        };
        int ret, bytes_read, offset_read;
        u8 *resp_data;
 
-       iwl_nvm_fill_read(&nvm_access_cmd, offset, length, section);
        cmd.len[0] = sizeof(struct iwl_nvm_access_cmd);
 
        ret = iwl_mvm_send_cmd(mvm, &cmd);
@@ -144,6 +170,30 @@ exit:
        return ret;
 }
 
+static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section,
+                                const u8 *data, u16 length)
+{
+       int offset = 0;
+
+       /* copy data in chunks of 2k (and remainder if any) */
+
+       while (offset < length) {
+               int chunk_size, ret;
+
+               chunk_size = min(IWL_NVM_DEFAULT_CHUNK_SIZE,
+                                length - offset);
+
+               ret = iwl_nvm_write_chunk(mvm, section, offset,
+                                         chunk_size, data + offset);
+               if (ret < 0)
+                       return ret;
+
+               offset += chunk_size;
+       }
+
+       return 0;
+}
+
 /*
  * Reads an NVM section completely.
  * NICs prior to 7000 family doesn't have a real NVM, but just read
@@ -177,7 +227,8 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
                offset += ret;
        }
 
-       IWL_INFO(mvm, "NVM section %d read completed\n", section);
+       IWL_DEBUG_EEPROM(mvm->trans->dev,
+                        "NVM section %d read completed\n", section);
        return offset;
 }
 
@@ -200,7 +251,130 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
        hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data;
        sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
        calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
-       return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib);
+       return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
+                                 iwl_fw_valid_tx_ant(mvm->fw),
+                                 iwl_fw_valid_rx_ant(mvm->fw));
+}
+
+#define MAX_NVM_FILE_LEN       16384
+
+/*
+ * HOW TO CREATE THE NVM FILE FORMAT:
+ * ------------------------------
+ * 1. create hex file, format:
+ *      3800 -> header
+ *      0000 -> header
+ *      5a40 -> data
+ *
+ *   rev - 6 bit (word1)
+ *   len - 10 bit (word1)
+ *   id - 4 bit (word2)
+ *   rsv - 12 bit (word2)
+ *
+ * 2. flip 8bits with 8 bits per line to get the right NVM file format
+ *
+ * 3. create binary file from the hex file
+ *
+ * 4. save as "iNVM_xxx.bin" under /lib/firmware
+ */
+static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm)
+{
+       int ret, section_id, section_size;
+       const struct firmware *fw_entry;
+       const struct {
+               __le16 word1;
+               __le16 word2;
+               u8 data[];
+       } *file_sec;
+       const u8 *eof;
+
+#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
+#define NVM_WORD2_ID(x) (x >> 12)
+
+       /*
+        * Obtain NVM image via request_firmware. Since we already used
+        * request_firmware_nowait() for the firmware binary load and only
+        * get here after that we assume the NVM request can be satisfied
+        * synchronously.
+        */
+       ret = request_firmware(&fw_entry, iwlwifi_mod_params.nvm_file,
+                              mvm->trans->dev);
+       if (ret) {
+               IWL_ERR(mvm, "ERROR: %s isn't available %d\n",
+                       iwlwifi_mod_params.nvm_file, ret);
+               return ret;
+       }
+
+       IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n",
+                iwlwifi_mod_params.nvm_file, fw_entry->size);
+
+       if (fw_entry->size < sizeof(*file_sec)) {
+               IWL_ERR(mvm, "NVM file too small\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (fw_entry->size > MAX_NVM_FILE_LEN) {
+               IWL_ERR(mvm, "NVM file too large\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       eof = fw_entry->data + fw_entry->size;
+
+       file_sec = (void *)fw_entry->data;
+
+       while (true) {
+               if (file_sec->data > eof) {
+                       IWL_ERR(mvm,
+                               "ERROR - NVM file too short for section header\n");
+                       ret = -EINVAL;
+                       break;
+               }
+
+               /* check for EOF marker */
+               if (!file_sec->word1 && !file_sec->word2) {
+                       ret = 0;
+                       break;
+               }
+
+               section_size = 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1));
+               section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2));
+
+               if (section_size > IWL_MAX_NVM_SECTION_SIZE) {
+                       IWL_ERR(mvm, "ERROR - section too large (%d)\n",
+                               section_size);
+                       ret = -EINVAL;
+                       break;
+               }
+
+               if (!section_size) {
+                       IWL_ERR(mvm, "ERROR - section empty\n");
+                       ret = -EINVAL;
+                       break;
+               }
+
+               if (file_sec->data + section_size > eof) {
+                       IWL_ERR(mvm,
+                               "ERROR - NVM file too short for section (%d bytes)\n",
+                               section_size);
+                       ret = -EINVAL;
+                       break;
+               }
+
+               ret = iwl_nvm_write_section(mvm, section_id, file_sec->data,
+                                           section_size);
+               if (ret < 0) {
+                       IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret);
+                       break;
+               }
+
+               /* advance to the next section */
+               file_sec = (void *)(file_sec->data + section_size);
+       }
+out:
+       release_firmware(fw_entry);
+       return ret;
 }
 
 int iwl_nvm_init(struct iwl_mvm *mvm)
@@ -208,6 +382,17 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
        int ret, i, section;
        u8 *nvm_buffer, *temp;
 
+       /* load external NVM if configured */
+       if (iwlwifi_mod_params.nvm_file) {
+               /* move to External NVM flow */
+               ret = iwl_mvm_load_external_nvm(mvm);
+               if (ret)
+                       return ret;
+       }
+
+       /* Read From FW NVM */
+       IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
+
        /* TODO: find correct NVM max size for a section */
        nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
                             GFP_KERNEL);
@@ -231,8 +416,9 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
        if (ret < 0)
                return ret;
 
-       ret = 0;
        mvm->nvm_data = iwl_parse_nvm_sections(mvm);
+       if (!mvm->nvm_data)
+               return -ENODATA;
 
-       return ret;
+       return 0;
 }
index b29c31a..af79a14 100644 (file)
@@ -215,17 +215,22 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
        RX_HANDLER(REPLY_RX_PHY_CMD, iwl_mvm_rx_rx_phy_cmd, false),
        RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false),
        RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
+
+       RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
+       RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false),
+       RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true),
+
        RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
 
        RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
        RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
 
-       RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
-       RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false),
-
        RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
        RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
 
+       RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif,
+                  false),
+
        RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
 };
 #undef RX_HANDLER
@@ -288,11 +293,14 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(NET_DETECT_HOTSPOTS_CMD),
        CMD(NET_DETECT_HOTSPOTS_QUERY_CMD),
        CMD(CARD_STATE_NOTIFICATION),
+       CMD(MISSED_BEACONS_NOTIFICATION),
        CMD(BT_COEX_PRIO_TABLE),
        CMD(BT_COEX_PROT_ENV),
        CMD(BT_PROFILE_NOTIFICATION),
        CMD(BT_CONFIG),
        CMD(MCAST_FILTER_CMD),
+       CMD(REPLY_BEACON_FILTERING_CMD),
+       CMD(REPLY_THERMAL_MNG_BACKOFF),
 };
 #undef CMD
 
@@ -393,10 +401,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        if (err)
                goto out_free;
 
+       iwl_mvm_tt_initialize(mvm);
+
        mutex_lock(&mvm->mutex);
        err = iwl_run_init_mvm_ucode(mvm, true);
        mutex_unlock(&mvm->mutex);
-       if (err && !iwlmvm_mod_params.init_dbg) {
+       /* returns 0 if successful, 1 if success but in rfkill */
+       if (err < 0 && !iwlmvm_mod_params.init_dbg) {
                IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
                goto out_free;
        }
@@ -439,10 +450,16 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
 
        iwl_mvm_leds_exit(mvm);
 
+       iwl_mvm_tt_exit(mvm);
+
        ieee80211_unregister_hw(mvm->hw);
 
        kfree(mvm->scan_cmd);
 
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
+       kfree(mvm->d3_resume_sram);
+#endif
+
        iwl_trans_stop_hw(mvm->trans, true);
 
        iwl_phy_db_free(mvm->phy_db);
@@ -589,6 +606,16 @@ static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
        ieee80211_wake_queue(mvm->hw, mq);
 }
 
+void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state)
+{
+       if (state)
+               set_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
+       else
+               clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
+
+       wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
+}
+
 static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
 {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
@@ -598,7 +625,7 @@ static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
        else
                clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
 
-       wiphy_rfkill_set_hw_state(mvm->hw->wiphy, state);
+       wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
 }
 
 static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
index a28a1d1..a8652dd 100644 (file)
@@ -195,21 +195,6 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
        return ret;
 }
 
-
-struct phy_ctx_used_data {
-       unsigned long used[BITS_TO_LONGS(NUM_PHY_CTX)];
-};
-
-static void iwl_mvm_phy_ctx_used_iter(struct ieee80211_hw *hw,
-                                     struct ieee80211_chanctx_conf *ctx,
-                                     void *_data)
-{
-       struct phy_ctx_used_data *data = _data;
-       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
-
-       __set_bit(phy_ctxt->id, data->used);
-}
-
 /*
  * Send a command to add a PHY context based on the current HW configuration.
  */
@@ -217,34 +202,28 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
                         struct cfg80211_chan_def *chandef,
                         u8 chains_static, u8 chains_dynamic)
 {
-       struct phy_ctx_used_data data = {
-               .used = { },
-       };
-
-       /*
-        * If this is a regular PHY context (not the ROC one)
-        * skip the ROC PHY context's ID.
-        */
-       if (ctxt != &mvm->phy_ctxt_roc)
-               __set_bit(mvm->phy_ctxt_roc.id, data.used);
+       int ret;
 
+       WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+               ctxt->ref);
        lockdep_assert_held(&mvm->mutex);
-       ctxt->color++;
 
-       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
-               ieee80211_iter_chan_contexts_atomic(
-                       mvm->hw, iwl_mvm_phy_ctx_used_iter, &data);
+       ctxt->channel = chandef->chan;
+       ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
+                                    chains_static, chains_dynamic,
+                                    FW_CTXT_ACTION_ADD, 0);
 
-               ctxt->id = find_first_zero_bit(data.used, NUM_PHY_CTX);
-               if (WARN_ONCE(ctxt->id == NUM_PHY_CTX,
-                             "Failed to init PHY context - no free ID!\n"))
-                       return -EIO;
-       }
+       return ret;
+}
 
-       ctxt->channel = chandef->chan;
-       return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
-                                     chains_static, chains_dynamic,
-                                     FW_CTXT_ACTION_ADD, 0);
+/*
+ * Update the number of references to the given PHY context. This is valid only
+ * in case the PHY context was already created, i.e., its reference count > 0.
+ */
+void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
+{
+       lockdep_assert_held(&mvm->mutex);
+       ctxt->ref++;
 }
 
 /*
@@ -264,23 +243,12 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
                                      FW_CTXT_ACTION_MODIFY, 0);
 }
 
-/*
- * Send a command to the FW to remove the given phy context.
- * Once the command is sent, regardless of success or failure, the context is
- * marked as invalid
- */
-void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
+void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
 {
-       struct iwl_phy_context_cmd cmd;
-       int ret;
-
        lockdep_assert_held(&mvm->mutex);
 
-       iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, FW_CTXT_ACTION_REMOVE, 0);
-       ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC,
-                                  sizeof(struct iwl_phy_context_cmd),
-                                  &cmd);
-       if (ret)
-               IWL_ERR(mvm, "Failed to send PHY remove: ctxt id=%d\n",
-                       ctxt->id);
+       if (WARN_ON_ONCE(!ctxt))
+               return;
+
+       ctxt->ref--;
 }
index ed77e43..e7ca965 100644 (file)
 
 #define POWER_KEEP_ALIVE_PERIOD_SEC    25
 
+static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
+                                         struct iwl_beacon_filter_cmd *cmd)
+{
+       int ret;
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC,
+                                  sizeof(struct iwl_beacon_filter_cmd), cmd);
+
+       if (!ret) {
+               IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
+                               cmd->ba_enable_beacon_abort);
+               IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
+                               cmd->ba_escape_timer);
+               IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
+                               cmd->bf_debug_flag);
+               IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
+                               cmd->bf_enable_beacon_filter);
+               IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
+                               cmd->bf_energy_delta);
+               IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
+                               cmd->bf_escape_timer);
+               IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
+                               cmd->bf_roaming_energy_delta);
+               IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
+                               cmd->bf_roaming_state);
+               IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n",
+                               cmd->bf_temperature_delta);
+       }
+       return ret;
+}
+
+static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif, bool enable)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_beacon_filter_cmd cmd = {
+               IWL_BF_CMD_CONFIG_DEFAULTS,
+               .bf_enable_beacon_filter = 1,
+               .ba_enable_beacon_abort = enable,
+       };
+
+       if (!mvmvif->bf_enabled)
+               return 0;
+
+       iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
+       return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+}
+
 static void iwl_mvm_power_log(struct iwl_mvm *mvm,
                              struct iwl_powertable_cmd *cmd)
 {
@@ -89,8 +137,12 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm,
                                le32_to_cpu(cmd->rx_data_timeout));
                IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
                                le32_to_cpu(cmd->tx_data_timeout));
-               IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
-                               cmd->lprx_rssi_threshold);
+               if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
+                       IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
+                                       le32_to_cpu(cmd->skip_dtim_periods));
+               if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+                       IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
+                                       le32_to_cpu(cmd->lprx_rssi_threshold));
        }
 }
 
@@ -103,6 +155,8 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        int dtimper, dtimper_msec;
        int keep_alive;
        bool radar_detect = false;
+       struct iwl_mvm_vif *mvmvif __maybe_unused =
+               iwl_mvm_vif_from_mac80211(vif);
 
        /*
         * Regardless of power management state the driver must set
@@ -115,12 +169,27 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
+       if (!vif->bss_conf.assoc)
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
+           mvmvif->dbgfs_pm.disable_power_off)
+               cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#endif
        if (!vif->bss_conf.ps)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
 
+       if (vif->bss_conf.beacon_rate &&
+           (vif->bss_conf.beacon_rate->bitrate == 10 ||
+            vif->bss_conf.beacon_rate->bitrate == 60)) {
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
+               cmd->lprx_rssi_threshold =
+                       cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD);
+       }
+
        dtimper = hw->conf.ps_dtim_period ?: 1;
 
        /* Check if radar detection is required on current channel */
@@ -135,8 +204,11 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        /* Check skip over DTIM conditions */
        if (!radar_detect && (dtimper <= 10) &&
-           (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP))
+           (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
+            mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
                cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+               cmd->skip_dtim_periods = cpu_to_le32(3);
+       }
 
        /* Check that keep alive period is at least 3 * DTIM */
        dtimper_msec = dtimper * vif->bss_conf.beacon_int;
@@ -145,27 +217,85 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
        cmd->keep_alive_seconds = keep_alive;
 
-       cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
-       cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+       if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
+               cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+               cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+       } else {
+               cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
+               cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
+       }
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
+               cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
+               if (mvmvif->dbgfs_pm.skip_over_dtim)
+                       cmd->flags |=
+                               cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+               else
+                       cmd->flags &=
+                               cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+       }
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
+               cmd->rx_data_timeout =
+                       cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
+               cmd->tx_data_timeout =
+                       cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
+               cmd->skip_dtim_periods =
+                       cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
+               if (mvmvif->dbgfs_pm.lprx_ena)
+                       cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
+               else
+                       cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
+       }
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
+               cmd->lprx_rssi_threshold =
+                       cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold);
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
 }
 
 int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
+       int ret;
+       bool ba_enable;
        struct iwl_powertable_cmd cmd = {};
 
        if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
+       /*
+        * TODO: The following vif_count verification is temporary condition.
+        * Avoid power mode update if more than one interface is currently
+        * active. Remove this condition when FW will support power management
+        * on multiple MACs.
+        */
+       IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n",
+                       mvm->vif_count);
+       if (mvm->vif_count > 1)
+               return 0;
+
        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
        iwl_mvm_power_log(mvm, &cmd);
 
-       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
-                                   sizeof(cmd), &cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+                                  sizeof(cmd), &cmd);
+       if (ret)
+               return ret;
+
+       ba_enable = !!(cmd.flags &
+                      cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
+
+       return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
 }
 
 int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
        struct iwl_powertable_cmd cmd = {};
+       struct iwl_mvm_vif *mvmvif __maybe_unused =
+               iwl_mvm_vif_from_mac80211(vif);
 
        if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
@@ -173,8 +303,82 @@ int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
                cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
+           mvmvif->dbgfs_pm.disable_power_off)
+               cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#endif
        iwl_mvm_power_log(mvm, &cmd);
 
        return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
                                    sizeof(cmd), &cmd);
 }
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+void
+iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
+                                        struct iwl_beacon_filter_cmd *cmd)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
+
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA)
+               cmd->bf_energy_delta = dbgfs_bf->bf_energy_delta;
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA)
+               cmd->bf_roaming_energy_delta =
+                                dbgfs_bf->bf_roaming_energy_delta;
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE)
+               cmd->bf_roaming_state = dbgfs_bf->bf_roaming_state;
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMPERATURE_DELTA)
+               cmd->bf_temperature_delta = dbgfs_bf->bf_temperature_delta;
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG)
+               cmd->bf_debug_flag = dbgfs_bf->bf_debug_flag;
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER)
+               cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer);
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER)
+               cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer);
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT)
+               cmd->ba_enable_beacon_abort = dbgfs_bf->ba_enable_beacon_abort;
+}
+#endif
+
+int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_beacon_filter_cmd cmd = {
+               IWL_BF_CMD_CONFIG_DEFAULTS,
+               .bf_enable_beacon_filter = 1,
+       };
+       int ret;
+
+       if (mvmvif != mvm->bf_allowed_vif ||
+           vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return 0;
+
+       iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
+       ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+
+       if (!ret)
+               mvmvif->bf_enabled = true;
+
+       return ret;
+}
+
+int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
+                                 struct ieee80211_vif *vif)
+{
+       struct iwl_beacon_filter_cmd cmd = {};
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return 0;
+
+       ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+
+       if (!ret)
+               mvmvif->bf_enabled = false;
+
+       return ret;
+}
index a1e3e92..29d49cf 100644 (file)
@@ -169,27 +169,34 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
                        num_active_bindings++;
        }
 
-       if (!num_active_bindings)
-               goto send_cmd;
-
-       quota = IWL_MVM_MAX_QUOTA / num_active_bindings;
-       quota_rem = IWL_MVM_MAX_QUOTA % num_active_bindings;
+       quota = 0;
+       quota_rem = 0;
+       if (num_active_bindings) {
+               quota = IWL_MVM_MAX_QUOTA / num_active_bindings;
+               quota_rem = IWL_MVM_MAX_QUOTA % num_active_bindings;
+       }
 
        for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
-               if (data.n_interfaces[i] <= 0)
+               if (data.colors[i] < 0)
                        continue;
 
                cmd.quotas[idx].id_and_color =
                        cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
-               cmd.quotas[idx].quota = cpu_to_le32(quota);
-               cmd.quotas[idx].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA);
+
+               if (data.n_interfaces[i] <= 0) {
+                       cmd.quotas[idx].quota = cpu_to_le32(0);
+                       cmd.quotas[idx].max_duration = cpu_to_le32(0);
+               } else {
+                       cmd.quotas[idx].quota = cpu_to_le32(quota);
+                       cmd.quotas[idx].max_duration =
+                               cpu_to_le32(IWL_MVM_MAX_QUOTA);
+               }
                idx++;
        }
 
        /* Give the remainder of the session to the first binding */
        le32_add_cpu(&cmd.quotas[0].quota, quota_rem);
 
-send_cmd:
        ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
                                   sizeof(cmd), &cmd);
        if (ret)
index b99fe31..b328a98 100644 (file)
@@ -401,24 +401,29 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
 
        load = rs_tl_get_load(lq_data, tid);
 
-       if ((iwlwifi_mod_params.auto_agg) || (load > IWL_AGG_LOAD_THRESHOLD)) {
-               IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n",
-                            sta->addr, tid);
-               ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
-               if (ret == -EAGAIN) {
-                       /*
-                        * driver and mac80211 is out of sync
-                        * this might be cause by reloading firmware
-                        * stop the tx ba session here
-                        */
-                       IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n",
-                               tid);
-                       ieee80211_stop_tx_ba_session(sta, tid);
-               }
-       } else {
-               IWL_DEBUG_HT(mvm,
-                            "Aggregation not enabled for tid %d because load = %u\n",
-                            tid, load);
+       /*
+        * Don't create TX aggregation sessions when in high
+        * BT traffic, as they would just be disrupted by BT.
+        */
+       if (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2) {
+               IWL_DEBUG_COEX(mvm, "BT traffic (%d), no aggregation allowed\n",
+                              BT_MBOX_MSG(&mvm->last_bt_notif,
+                                          3, TRAFFIC_LOAD));
+               return ret;
+       }
+
+       IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n",
+                    sta->addr, tid);
+       ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
+       if (ret == -EAGAIN) {
+               /*
+                * driver and mac80211 is out of sync
+                * this might be cause by reloading firmware
+                * stop the tx ba session here
+                */
+               IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n",
+                       tid);
+               ieee80211_stop_tx_ba_session(sta, tid);
        }
        return ret;
 }
@@ -1519,6 +1524,29 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
        u8 update_search_tbl_counter = 0;
        int ret;
 
+       switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+       case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+               /* nothing */
+               break;
+       case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+               /* avoid antenna B unless MIMO */
+               if (tbl->action == IWL_SISO_SWITCH_ANTENNA2)
+                       tbl->action = IWL_SISO_SWITCH_MIMO2_AB;
+               break;
+       case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+       case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+               /* avoid antenna B and MIMO */
+               valid_tx_ant =
+                       first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
+               if (tbl->action != IWL_SISO_SWITCH_ANTENNA1)
+                       tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+               break;
+       default:
+               IWL_ERR(mvm, "Invalid BT load %d",
+                       BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD));
+               break;
+       }
+
        start_action = tbl->action;
        while (1) {
                lq_sta->action_counter++;
@@ -1532,7 +1560,9 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
                             tx_chains_num <= 2))
                                break;
 
-                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+                       if (window->success_ratio >= IWL_RS_GOOD_RATIO &&
+                           BT_MBOX_MSG(&mvm->last_bt_notif, 3,
+                                       TRAFFIC_LOAD) == 0)
                                break;
 
                        memcpy(search_tbl, tbl, sz);
@@ -1654,6 +1684,28 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
        u8 update_search_tbl_counter = 0;
        int ret;
 
+       switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+       case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+               /* nothing */
+               break;
+       case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+       case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+               /* avoid antenna B and MIMO */
+               if (tbl->action != IWL_MIMO2_SWITCH_SISO_A)
+                       tbl->action = IWL_MIMO2_SWITCH_SISO_A;
+               break;
+       case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+               /* avoid antenna B unless MIMO */
+               if (tbl->action == IWL_MIMO2_SWITCH_SISO_B ||
+                   tbl->action == IWL_MIMO2_SWITCH_SISO_C)
+                       tbl->action = IWL_MIMO2_SWITCH_SISO_A;
+               break;
+       default:
+               IWL_ERR(mvm, "Invalid BT load %d",
+                       BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD));
+               break;
+       }
+
        start_action = tbl->action;
        while (1) {
                lq_sta->action_counter++;
@@ -1791,6 +1843,28 @@ static int rs_move_mimo3_to_other(struct iwl_mvm *mvm,
        int ret;
        u8 update_search_tbl_counter = 0;
 
+       switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+       case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
+               /* nothing */
+               break;
+       case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
+       case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
+               /* avoid antenna B and MIMO */
+               if (tbl->action != IWL_MIMO3_SWITCH_SISO_A)
+                       tbl->action = IWL_MIMO3_SWITCH_SISO_A;
+               break;
+       case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
+               /* avoid antenna B unless MIMO */
+               if (tbl->action == IWL_MIMO3_SWITCH_SISO_B ||
+                   tbl->action == IWL_MIMO3_SWITCH_SISO_C)
+                       tbl->action = IWL_MIMO3_SWITCH_SISO_A;
+               break;
+       default:
+               IWL_ERR(mvm, "Invalid BT load %d",
+                       BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD));
+               break;
+       }
+
        start_action = tbl->action;
        while (1) {
                lq_sta->action_counter++;
@@ -2302,6 +2376,32 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
             (current_tpt > (100 * tbl->expected_tpt[low]))))
                scale_action = 0;
 
+       if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
+            IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
+            (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
+               if (lq_sta->last_bt_traffic >
+                   BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+                       /*
+                        * don't set scale_action, don't want to scale up if
+                        * the rate scale doesn't otherwise think that is a
+                        * good idea.
+                        */
+               } else if (lq_sta->last_bt_traffic <=
+                          BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
+                       scale_action = -1;
+               }
+       }
+       lq_sta->last_bt_traffic =
+               BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD);
+
+       if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
+            IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
+            (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
+               /* search for a new modulation */
+               rs_stay_in_table(lq_sta, true);
+               goto lq_update;
+       }
+
        switch (scale_action) {
        case -1:
                /* Decrease starting rate, update uCode's rate table */
@@ -2783,6 +2883,13 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
 
        lq_cmd->agg_time_limit =
                cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
+
+       /*
+        * overwrite if needed, pass aggregation time limit
+        * to uCode in uSec - This is racy - but heh, at least it helps...
+        */
+       if (mvm && BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2)
+               lq_cmd->agg_time_limit = cpu_to_le16(1200);
 }
 
 static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
@@ -3081,3 +3188,29 @@ void iwl_mvm_rate_control_unregister(void)
 {
        ieee80211_rate_control_unregister(&rs_mvm_ops);
 }
+
+/**
+ * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
+ * Tx protection, according to this rquest and previous requests,
+ * and send the LQ command.
+ * @lq: The LQ command
+ * @mvmsta: The station
+ * @enable: Enable Tx protection?
+ */
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
+                         struct iwl_mvm_sta *mvmsta, bool enable)
+{
+       lockdep_assert_held(&mvm->mutex);
+
+       if (enable) {
+               if (mvmsta->tx_protection == 0)
+                       lq->flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK;
+               mvmsta->tx_protection++;
+       } else {
+               mvmsta->tx_protection--;
+               if (mvmsta->tx_protection == 0)
+                       lq->flags &= ~LQ_FLAG_SET_STA_TLC_RTS_MSK;
+       }
+
+       return iwl_mvm_send_lq_cmd(mvm, lq, CMD_ASYNC, false);
+}
index 219c685..cff4f6d 100644 (file)
@@ -358,6 +358,18 @@ struct iwl_lq_sta {
        u8 last_bt_traffic;
 };
 
+enum iwl_bt_coex_profile_traffic_load {
+       IWL_BT_COEX_TRAFFIC_LOAD_NONE           = 0,
+       IWL_BT_COEX_TRAFFIC_LOAD_LOW            = 1,
+       IWL_BT_COEX_TRAFFIC_LOAD_HIGH           = 2,
+       IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS     = 3,
+/*
+ * There are no more even though below is a u8, the
+ * indication from the BT device only has two bits.
+ */
+};
+
+
 static inline u8 num_of_ant(u8 mask)
 {
        return  !!((mask) & ANT_A) +
@@ -390,4 +402,9 @@ extern int iwl_mvm_rate_control_register(void);
  */
 extern void iwl_mvm_rate_control_unregister(void);
 
+struct iwl_mvm_sta;
+
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
+                         struct iwl_mvm_sta *mvmsta, bool enable);
+
 #endif /* __rs__ */
index 4dfc21a..e4930d5 100644 (file)
@@ -363,3 +363,25 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                                        rxb, &rx_status);
        return 0;
 }
+
+/*
+ * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler
+ *
+ * TODO: This handler is implemented partially.
+ * It only gets the NIC's temperature.
+ */
+int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
+                         struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_notif_statistics *stats = (void *)&pkt->data;
+       struct mvm_statistics_general_common *common = &stats->general.common;
+
+       if (mvm->temperature != le32_to_cpu(common->temperature)) {
+               mvm->temperature = le32_to_cpu(common->temperature);
+               iwl_mvm_tt_handler(mvm);
+       }
+
+       return 0;
+}
index 2476e43..2157b0f 100644 (file)
@@ -298,12 +298,6 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
        else
                cmd->type = cpu_to_le32(SCAN_TYPE_FORCED);
 
-       /*
-        * TODO: This is a WA due to a bug in the FW AUX framework that does not
-        * properly handle time events that fail to be scheduled
-        */
-       cmd->type = cpu_to_le32(SCAN_TYPE_FORCED);
-
        cmd->repeats = cpu_to_le32(1);
 
        /*
index 5c664ed..62fe520 100644 (file)
@@ -64,6 +64,7 @@
 
 #include "mvm.h"
 #include "sta.h"
+#include "rs.h"
 
 static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm)
 {
@@ -217,6 +218,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
                                                      mvmvif->color);
        mvm_sta->vif = vif;
        mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+       mvm_sta->tx_protection = 0;
+       mvm_sta->tt_tx_protection = false;
 
        /* HW restart, don't assume the memory has been zeroed */
        atomic_set(&mvm->pending_frames[sta_id], 0);
@@ -226,9 +229,6 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
                if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
                        mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
 
-       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
-               mvm_sta->tfd_queue_msk |= BIT(vif->cab_queue);
-
        /* for HW restart - need to reset the seq_number etc... */
        memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data));
 
@@ -798,21 +798,23 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                min(mvmsta->max_agg_bufsize, buf_size);
        mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
 
+       IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
+                    sta->addr, tid);
+
        if (mvm->cfg->ht_params->use_rts_for_aggregation) {
                /*
                 * switch to RTS/CTS if it is the prefer protection
                 * method for HT traffic
+                * this function also sends the LQ command
                 */
-               mvmsta->lq_sta.lq.flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK;
+               return iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
+                                            mvmsta, true);
                /*
                 * TODO: remove the TLC_RTS flag when we tear down the last
                 * AGG session (agg_tids_count in DVM)
                 */
        }
 
-       IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
-                    sta->addr, tid);
-
        return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false);
 }
 
@@ -1287,17 +1289,11 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
        struct iwl_mvm_add_sta_cmd cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
-               .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
-               .sleep_state_flags = cpu_to_le16(STA_SLEEP_STATE_AWAKE),
+               .station_flags_msk = cpu_to_le32(STA_FLG_PS),
                .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
        };
        int ret;
 
-       /*
-        * Same modify mask for sleep_tx_count and sleep_state_flags but this
-        * should be fine since if we set the STA as "awake", then
-        * sleep_tx_count is not relevant.
-        */
        ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
index a4ddce7..94b265e 100644 (file)
@@ -250,7 +250,6 @@ enum iwl_mvm_agg_state {
  *     the first packet to be sent in legacy HW queue in Tx AGG stop flow.
  *     Basically when next_reclaimed reaches ssn, we can tell mac80211 that
  *     we are ready to finish the Tx AGG stop / start flow.
- * @wait_for_ba: Expect block-ack before next Tx reply
  */
 struct iwl_mvm_tid_data {
        u16 seq_number;
@@ -260,7 +259,6 @@ struct iwl_mvm_tid_data {
        enum iwl_mvm_agg_state state;
        u16 txq_id;
        u16 ssn;
-       bool wait_for_ba;
 };
 
 /**
@@ -275,6 +273,8 @@ struct iwl_mvm_tid_data {
  * @lock: lock to protect the whole struct. Since %tid_data is access from Tx
  * and from Tx response flow, it needs a spinlock.
  * @tid_data: per tid data. Look at %iwl_mvm_tid_data.
+ * @tx_protection: reference counter for controlling the Tx protection.
+ * @tt_tx_protection: is thermal throttling enable Tx protection?
  *
  * When mac80211 creates a station it reserves some space (hw->sta_data_size)
  * in the structure for use by driver. This structure is placed in that
@@ -296,6 +296,10 @@ struct iwl_mvm_sta {
 #ifdef CONFIG_PM_SLEEP
        u16 last_seq_ctl;
 #endif
+
+       /* Temporary, until the new TLC will control the Tx protection */
+       s8 tx_protection;
+       bool tt_tx_protection;
 };
 
 /**
diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c
new file mode 100644 (file)
index 0000000..d6ae7f1
--- /dev/null
@@ -0,0 +1,530 @@
+/******************************************************************************
+ *
+ * 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 Intel Corporation. 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 Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * 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 Intel Corporation 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 "mvm.h"
+#include "iwl-config.h"
+#include "iwl-io.h"
+#include "iwl-csr.h"
+#include "iwl-prph.h"
+
+#define OTP_DTS_DIODE_DEVIATION 96 /*in words*/
+/* VBG - Voltage Band Gap error data (temperature offset) */
+#define OTP_WP_DTS_VBG                 (OTP_DTS_DIODE_DEVIATION + 2)
+#define MEAS_VBG_MIN_VAL               2300
+#define MEAS_VBG_MAX_VAL               3000
+#define MEAS_VBG_DEFAULT_VAL           2700
+#define DTS_DIODE_VALID(flags)         (flags & DTS_DIODE_REG_FLAGS_PASS_ONCE)
+#define MIN_TEMPERATURE                        0
+#define MAX_TEMPERATURE                        125
+#define TEMPERATURE_ERROR              (MAX_TEMPERATURE + 1)
+#define PTAT_DIGITAL_VALUE_MIN_VALUE   0
+#define PTAT_DIGITAL_VALUE_MAX_VALUE   0xFF
+#define DTS_VREFS_NUM                  5
+static inline u32 DTS_DIODE_GET_VREFS_ID(u32 flags)
+{
+       return (flags & DTS_DIODE_REG_FLAGS_VREFS_ID) >>
+                                       DTS_DIODE_REG_FLAGS_VREFS_ID_POS;
+}
+
+#define CALC_VREFS_MIN_DIFF    43
+#define CALC_VREFS_MAX_DIFF    51
+#define CALC_LUT_SIZE          (1 + CALC_VREFS_MAX_DIFF - CALC_VREFS_MIN_DIFF)
+#define CALC_LUT_INDEX_OFFSET  CALC_VREFS_MIN_DIFF
+#define CALC_TEMPERATURE_RESULT_SHIFT_OFFSET   23
+
+/*
+ * @digital_value: The diode's digital-value sampled (temperature/voltage)
+ * @vref_low: The lower voltage-reference (the vref just below the diode's
+ *     sampled digital-value)
+ * @vref_high: The higher voltage-reference (the vref just above the diode's
+ *     sampled digital-value)
+ * @flags: bits[1:0]: The ID of the Vrefs pair (lowVref,highVref)
+ *     bits[6:2]: Reserved.
+ *     bits[7:7]: Indicates completion of at least 1 successful sample
+ *     since last DTS reset.
+ */
+struct iwl_mvm_dts_diode_bits {
+       u8 digital_value;
+       u8 vref_low;
+       u8 vref_high;
+       u8 flags;
+} __packed;
+
+union dts_diode_results {
+       u32 reg_value;
+       struct iwl_mvm_dts_diode_bits bits;
+} __packed;
+
+static s16 iwl_mvm_dts_get_volt_band_gap(struct iwl_mvm *mvm)
+{
+       struct iwl_nvm_section calib_sec;
+       const __le16 *calib;
+       u16 vbg;
+
+       /* TODO: move parsing to NVM code */
+       calib_sec = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION];
+       calib = (__le16 *)calib_sec.data;
+
+       vbg = le16_to_cpu(calib[OTP_WP_DTS_VBG]);
+
+       if (vbg < MEAS_VBG_MIN_VAL || vbg > MEAS_VBG_MAX_VAL)
+               vbg = MEAS_VBG_DEFAULT_VAL;
+
+       return vbg;
+}
+
+static u16 iwl_mvm_dts_get_ptat_deviation_offset(struct iwl_mvm *mvm)
+{
+       const u8 *calib;
+       u8 ptat, pa1, pa2, median;
+
+       /* TODO: move parsing to NVM code */
+       calib = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION].data;
+       ptat = calib[OTP_DTS_DIODE_DEVIATION];
+       pa1 = calib[OTP_DTS_DIODE_DEVIATION + 1];
+       pa2 = calib[OTP_DTS_DIODE_DEVIATION + 2];
+
+       /* get the median: */
+       if (ptat > pa1) {
+               if (ptat > pa2)
+                       median = (pa1 > pa2) ? pa1 : pa2;
+               else
+                       median = ptat;
+       } else {
+               if (pa1 > pa2)
+                       median = (ptat > pa2) ? ptat : pa2;
+               else
+                       median = pa1;
+       }
+
+       return ptat - median;
+}
+
+static u8 iwl_mvm_dts_calibrate_ptat_deviation(struct iwl_mvm *mvm, u8 value)
+{
+       /* Calibrate the PTAT digital value, based on PTAT deviation data: */
+       s16 new_val = value - iwl_mvm_dts_get_ptat_deviation_offset(mvm);
+
+       if (new_val > PTAT_DIGITAL_VALUE_MAX_VALUE)
+               new_val = PTAT_DIGITAL_VALUE_MAX_VALUE;
+       else if (new_val < PTAT_DIGITAL_VALUE_MIN_VALUE)
+               new_val = PTAT_DIGITAL_VALUE_MIN_VALUE;
+
+       return new_val;
+}
+
+static bool dts_get_adjacent_vrefs(struct iwl_mvm *mvm,
+                                  union dts_diode_results *avg_ptat)
+{
+       u8 vrefs_results[DTS_VREFS_NUM];
+       u8 low_vref_index = 0, flags;
+       u32 reg;
+
+       reg = iwl_read_prph(mvm->trans, DTSC_VREF_AVG);
+       memcpy(vrefs_results, &reg, sizeof(reg));
+       reg = iwl_read_prph(mvm->trans, DTSC_VREF5_AVG);
+       vrefs_results[4] = reg & 0xff;
+
+       if (avg_ptat->bits.digital_value < vrefs_results[0] ||
+           avg_ptat->bits.digital_value > vrefs_results[4])
+               return false;
+
+       if (avg_ptat->bits.digital_value > vrefs_results[3])
+               low_vref_index = 3;
+       else if (avg_ptat->bits.digital_value > vrefs_results[2])
+               low_vref_index = 2;
+       else if (avg_ptat->bits.digital_value > vrefs_results[1])
+               low_vref_index = 1;
+
+       avg_ptat->bits.vref_low  = vrefs_results[low_vref_index];
+       avg_ptat->bits.vref_high = vrefs_results[low_vref_index + 1];
+       flags = avg_ptat->bits.flags;
+       avg_ptat->bits.flags =
+               (flags & ~DTS_DIODE_REG_FLAGS_VREFS_ID) |
+               (low_vref_index & DTS_DIODE_REG_FLAGS_VREFS_ID);
+       return true;
+}
+
+/*
+ * return true it the results are valid, and false otherwise.
+ */
+static bool dts_read_ptat_avg_results(struct iwl_mvm *mvm,
+                                     union dts_diode_results *avg_ptat)
+{
+       u32 reg;
+       u8 tmp;
+
+       /* fill the diode value and pass_once with avg-reg results */
+       reg = iwl_read_prph(mvm->trans, DTSC_PTAT_AVG);
+       reg &= DTS_DIODE_REG_DIG_VAL | DTS_DIODE_REG_PASS_ONCE;
+       avg_ptat->reg_value = reg;
+
+       /* calibrate the PTAT digital value */
+       tmp = avg_ptat->bits.digital_value;
+       tmp = iwl_mvm_dts_calibrate_ptat_deviation(mvm, tmp);
+       avg_ptat->bits.digital_value = tmp;
+
+       /*
+        * fill vrefs fields, based on the avgVrefs results
+        * and the diode value
+        */
+       return dts_get_adjacent_vrefs(mvm, avg_ptat) &&
+               DTS_DIODE_VALID(avg_ptat->bits.flags);
+}
+
+static s32 calculate_nic_temperature(union dts_diode_results avg_ptat,
+                                    u16 volt_band_gap)
+{
+       u32 tmp_result;
+       u8 vrefs_diff;
+       /*
+        * For temperature calculation (at the end, shift right by 23)
+        * LUT[(D2-D1)] = ROUND{ 2^23 / ((D2-D1)*9*10) }
+        * (D2-D1) ==   43    44    45    46    47    48    49    50    51
+        */
+       static const u16 calc_lut[CALC_LUT_SIZE] = {
+               2168, 2118, 2071, 2026, 1983, 1942, 1902, 1864, 1828,
+       };
+
+       /*
+        * The diff between the high and low voltage-references is assumed
+        * to be strictly be in range of [60,68]
+        */
+       vrefs_diff = avg_ptat.bits.vref_high - avg_ptat.bits.vref_low;
+
+       if (vrefs_diff < CALC_VREFS_MIN_DIFF ||
+           vrefs_diff > CALC_VREFS_MAX_DIFF)
+               return TEMPERATURE_ERROR;
+
+       /* calculate the result: */
+       tmp_result =
+               vrefs_diff * (DTS_DIODE_GET_VREFS_ID(avg_ptat.bits.flags) + 9);
+       tmp_result += avg_ptat.bits.digital_value;
+       tmp_result -= avg_ptat.bits.vref_high;
+
+       /* multiply by the LUT value (based on the diff) */
+       tmp_result *= calc_lut[vrefs_diff - CALC_LUT_INDEX_OFFSET];
+
+       /*
+        * Get the BandGap (the voltage refereces source) error data
+        * (temperature offset)
+        */
+       tmp_result *= volt_band_gap;
+
+       /*
+        * here, tmp_result value can be up to 32-bits. We want to right-shift
+        * it *without* sign-extend.
+        */
+       tmp_result = tmp_result >> CALC_TEMPERATURE_RESULT_SHIFT_OFFSET;
+
+       /*
+        * at this point, tmp_result should be in the range:
+        * 200 <= tmp_result <= 365
+        */
+       return (s16)tmp_result - 240;
+}
+
+static s32 check_nic_temperature(struct iwl_mvm *mvm)
+{
+       u16 volt_band_gap;
+       union dts_diode_results avg_ptat;
+
+       volt_band_gap = iwl_mvm_dts_get_volt_band_gap(mvm);
+
+       /* disable DTS */
+       iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0);
+
+       /* SV initialization */
+       iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 1);
+       iwl_write_prph(mvm->trans, DTSC_CFG_MODE,
+                      DTSC_CFG_MODE_PERIODIC);
+
+       /* wait for results */
+       msleep(100);
+       if (!dts_read_ptat_avg_results(mvm, &avg_ptat))
+               return TEMPERATURE_ERROR;
+
+       /* disable DTS */
+       iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0);
+
+       return calculate_nic_temperature(avg_ptat, volt_band_gap);
+}
+
+static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
+{
+       u32 duration = mvm->thermal_throttle.params->ct_kill_duration;
+
+       IWL_ERR(mvm, "Enter CT Kill\n");
+       iwl_mvm_set_hw_ctkill_state(mvm, true);
+       schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
+                             round_jiffies_relative(duration * HZ));
+}
+
+static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm)
+{
+       IWL_ERR(mvm, "Exit CT Kill\n");
+       iwl_mvm_set_hw_ctkill_state(mvm, false);
+}
+
+static void check_exit_ctkill(struct work_struct *work)
+{
+       struct iwl_mvm_tt_mgmt *tt;
+       struct iwl_mvm *mvm;
+       u32 duration;
+       s32 temp;
+
+       tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
+       mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
+
+       duration = tt->params->ct_kill_duration;
+
+       iwl_trans_start_hw(mvm->trans);
+       temp = check_nic_temperature(mvm);
+       iwl_trans_stop_hw(mvm->trans, false);
+
+       if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) {
+               IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n");
+               goto reschedule;
+       }
+       IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
+
+       if (temp <= tt->params->ct_kill_exit) {
+               iwl_mvm_exit_ctkill(mvm);
+               return;
+       }
+
+reschedule:
+       schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
+                             round_jiffies(duration * HZ));
+}
+
+static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = _data;
+       enum ieee80211_smps_mode smps_mode;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (mvm->thermal_throttle.dynamic_smps)
+               smps_mode = IEEE80211_SMPS_DYNAMIC;
+       else
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
+}
+
+static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
+{
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
+       int i, err;
+
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+                                               lockdep_is_held(&mvm->mutex));
+               if (IS_ERR_OR_NULL(sta))
+                       continue;
+               mvmsta = (void *)sta->drv_priv;
+               if (enable == mvmsta->tt_tx_protection)
+                       continue;
+               err = iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
+                                           mvmsta, enable);
+               if (err) {
+                       IWL_ERR(mvm, "Failed to %s Tx protection\n",
+                               enable ? "enable" : "disable");
+               } else {
+                       IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
+                                      enable ? "Enable" : "Disable");
+                       mvmsta->tt_tx_protection = enable;
+               }
+       }
+}
+
+static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
+{
+       struct iwl_host_cmd cmd = {
+               .id = REPLY_THERMAL_MNG_BACKOFF,
+               .len = { sizeof(u32), },
+               .data = { &backoff, },
+               .flags = CMD_SYNC,
+       };
+
+       if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
+               IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
+                              backoff);
+               mvm->thermal_throttle.tx_backoff = backoff;
+       } else {
+               IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
+       }
+}
+
+void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
+{
+       const struct iwl_tt_params *params = mvm->thermal_throttle.params;
+       struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
+       s32 temperature = mvm->temperature;
+       bool throttle_enable = false;
+       int i;
+       u32 tx_backoff;
+
+       IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
+
+       if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
+               iwl_mvm_enter_ctkill(mvm);
+               return;
+       }
+
+       if (params->support_dynamic_smps) {
+               if (!tt->dynamic_smps &&
+                   temperature >= params->dynamic_smps_entry) {
+                       IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
+                       tt->dynamic_smps = true;
+                       ieee80211_iterate_active_interfaces_atomic(
+                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                                       iwl_mvm_tt_smps_iterator, mvm);
+                       throttle_enable = true;
+               } else if (tt->dynamic_smps &&
+                          temperature <= params->dynamic_smps_exit) {
+                       IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
+                       tt->dynamic_smps = false;
+                       ieee80211_iterate_active_interfaces_atomic(
+                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                                       iwl_mvm_tt_smps_iterator, mvm);
+               }
+       }
+
+       if (params->support_tx_protection) {
+               if (temperature >= params->tx_protection_entry) {
+                       iwl_mvm_tt_tx_protection(mvm, true);
+                       throttle_enable = true;
+               } else if (temperature <= params->tx_protection_exit) {
+                       iwl_mvm_tt_tx_protection(mvm, false);
+               }
+       }
+
+       if (params->support_tx_backoff) {
+               tx_backoff = 0;
+               for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
+                       if (temperature < params->tx_backoff[i].temperature)
+                               break;
+                       tx_backoff = params->tx_backoff[i].backoff;
+               }
+               if (tx_backoff != 0)
+                       throttle_enable = true;
+               if (tt->tx_backoff != tx_backoff)
+                       iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
+       }
+
+       if (!tt->throttle && throttle_enable) {
+               IWL_WARN(mvm,
+                        "Due to high temperature thermal throttling initiated\n");
+               tt->throttle = true;
+       } else if (tt->throttle && !tt->dynamic_smps && tt->tx_backoff == 0 &&
+                  temperature <= params->tx_protection_exit) {
+               IWL_WARN(mvm,
+                        "Temperature is back to normal thermal throttling stopped\n");
+               tt->throttle = false;
+       }
+}
+
+static const struct iwl_tt_params iwl7000_tt_params = {
+       .ct_kill_entry = 118,
+       .ct_kill_exit = 96,
+       .ct_kill_duration = 5,
+       .dynamic_smps_entry = 114,
+       .dynamic_smps_exit = 110,
+       .tx_protection_entry = 114,
+       .tx_protection_exit = 108,
+       .tx_backoff = {
+               {.temperature = 112, .backoff = 200},
+               {.temperature = 113, .backoff = 600},
+               {.temperature = 114, .backoff = 1200},
+               {.temperature = 115, .backoff = 2000},
+               {.temperature = 116, .backoff = 4000},
+               {.temperature = 117, .backoff = 10000},
+       },
+       .support_ct_kill = true,
+       .support_dynamic_smps = true,
+       .support_tx_protection = true,
+       .support_tx_backoff = true,
+};
+
+void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
+{
+       struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
+
+       IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
+       tt->params = &iwl7000_tt_params;
+       tt->throttle = false;
+       INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
+}
+
+void iwl_mvm_tt_exit(struct iwl_mvm *mvm)
+{
+       cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
+       IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
+}
index 48c1891..f0e96a9 100644 (file)
@@ -175,7 +175,7 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
         * table is controlled by LINK_QUALITY commands
         */
 
-       if (ieee80211_is_data(fc)) {
+       if (ieee80211_is_data(fc) && sta) {
                tx_cmd->initial_rate_index = 0;
                tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
                return;
@@ -408,7 +408,6 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
        IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id,
                     tid, txq_id, seq_number);
 
-       /* NOTE: aggregation will need changes here (for txq id) */
        if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id))
                goto drop_unlock_sta;
 
@@ -610,8 +609,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                    !(info->flags & IEEE80211_TX_STAT_ACK))
                        info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
 
-               /* W/A FW bug: seq_ctl is wrong when the queue is flushed */
-               if (status == TX_STATUS_FAIL_FIFO_FLUSHED) {
+               /* W/A FW bug: seq_ctl is wrong when the status isn't success */
+               if (status != TX_STATUS_SUCCESS) {
                        struct ieee80211_hdr *hdr = (void *)skb->data;
                        seq_ctl = le16_to_cpu(hdr->seq_ctrl);
                }
index 687b34e..1e13328 100644 (file)
@@ -76,6 +76,11 @@ int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
 {
        int ret;
 
+#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
+       if (WARN_ON(mvm->d3_test_active))
+               return -EIO;
+#endif
+
        /*
         * Synchronous commands from this op-mode must hold
         * the mutex, this ensures we don't try to send two
@@ -125,6 +130,11 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
 
        lockdep_assert_held(&mvm->mutex);
 
+#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP)
+       if (WARN_ON(mvm->d3_test_active))
+               return -EIO;
+#endif
+
        /*
         * Only synchronous commands can wait for status,
         * we use WANT_SKB so the caller can't.
@@ -471,3 +481,34 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
 
        return iwl_mvm_send_cmd(mvm, &cmd);
 }
+
+/**
+ * iwl_mvm_update_smps - Get a requst to change the SMPS mode
+ * @req_type: The part of the driver who call for a change.
+ * @smps_requests: The request to change the SMPS mode.
+ *
+ * Get a requst to change the SMPS mode,
+ * and change it according to all other requests in the driver.
+ */
+void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                        enum iwl_mvm_smps_type_request req_type,
+                        enum ieee80211_smps_mode smps_request)
+{
+       struct iwl_mvm_vif *mvmvif;
+       enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC;
+       int i;
+
+       lockdep_assert_held(&mvm->mutex);
+       mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       mvmvif->smps_requests[req_type] = smps_request;
+       for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
+               if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) {
+                       smps_mode = IEEE80211_SMPS_STATIC;
+                       break;
+               }
+               if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC)
+                       smps_mode = IEEE80211_SMPS_DYNAMIC;
+       }
+
+       ieee80211_request_smps(vif, smps_mode);
+}
index 8cb53ec..81f3ea5 100644 (file)
@@ -78,6 +78,7 @@
 
 /* Hardware specific file defines the PCI IDs table for that hardware module */
 static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
+#if IS_ENABLED(CONFIG_IWLDVM)
        {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */
        {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */
        {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */
@@ -253,13 +254,60 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)},
        {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)},
        {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)},
+#endif /* CONFIG_IWLDVM */
 
+#if IS_ENABLED(CONFIG_IWLMVM)
 /* 7000 Series */
        {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_ac_cfg)},
-       {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)},
+
+/* 3160 Series */
+       {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)},
+       {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
+#endif /* CONFIG_IWLMVM */
 
        {0}
 };
index 148843e..b654dcd 100644 (file)
@@ -217,6 +217,7 @@ struct iwl_pcie_txq_scratch_buf {
  * @trans_pcie: pointer back to transport (for timer)
  * @need_update: indicates need to update read/write index
  * @active: stores if queue is active
+ * @ampdu: true if this queue is an ampdu queue for an specific RA/TID
  *
  * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
  * descriptors) and required locking structures.
@@ -232,6 +233,7 @@ struct iwl_txq {
        struct iwl_trans_pcie *trans_pcie;
        u8 need_update;
        u8 active;
+       bool ampdu;
 };
 
 static inline dma_addr_t
index 567e67a..fd848cd 100644 (file)
 /*
  * iwl_rxq_space - Return number of free slots available in queue.
  */
-static int iwl_rxq_space(const struct iwl_rxq *q)
+static int iwl_rxq_space(const struct iwl_rxq *rxq)
 {
-       int s = q->read - q->write;
+       int s = rxq->read - rxq->write;
+
        if (s <= 0)
                s += RX_QUEUE_SIZE;
        /* keep some buffer to not confuse full and empty queue */
@@ -143,21 +144,22 @@ int iwl_pcie_rx_stop(struct iwl_trans *trans)
 /*
  * iwl_pcie_rxq_inc_wr_ptr - Update the write pointer for the RX queue
  */
-static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_rxq *q)
+static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
+                                   struct iwl_rxq *rxq)
 {
        unsigned long flags;
        u32 reg;
 
-       spin_lock_irqsave(&q->lock, flags);
+       spin_lock_irqsave(&rxq->lock, flags);
 
-       if (q->need_update == 0)
+       if (rxq->need_update == 0)
                goto exit_unlock;
 
        if (trans->cfg->base_params->shadow_reg_enable) {
                /* shadow register enabled */
                /* Device expects a multiple of 8 */
-               q->write_actual = (q->write & ~0x7);
-               iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, q->write_actual);
+               rxq->write_actual = (rxq->write & ~0x7);
+               iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
        } else {
                struct iwl_trans_pcie *trans_pcie =
                        IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -175,22 +177,22 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_rxq *q)
                                goto exit_unlock;
                        }
 
-                       q->write_actual = (q->write & ~0x7);
+                       rxq->write_actual = (rxq->write & ~0x7);
                        iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR,
-                                       q->write_actual);
+                                          rxq->write_actual);
 
                /* Else device is assumed to be awake */
                } else {
                        /* Device expects a multiple of 8 */
-                       q->write_actual = (q->write & ~0x7);
+                       rxq->write_actual = (rxq->write & ~0x7);
                        iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR,
-                               q->write_actual);
+                                          rxq->write_actual);
                }
        }
-       q->need_update = 0;
+       rxq->need_update = 0;
 
  exit_unlock:
-       spin_unlock_irqrestore(&q->lock, flags);
+       spin_unlock_irqrestore(&rxq->lock, flags);
 }
 
 /*
@@ -355,19 +357,16 @@ static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans)
        struct iwl_rxq *rxq = &trans_pcie->rxq;
        int i;
 
-       /* Fill the rx_used queue with _all_ of the Rx buffers */
+       lockdep_assert_held(&rxq->lock);
+
        for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
-               /* In the reset function, these buffers may have been allocated
-                * to an SKB, so we need to unmap and free potential storage */
-               if (rxq->pool[i].page != NULL) {
-                       dma_unmap_page(trans->dev, rxq->pool[i].page_dma,
-                                      PAGE_SIZE << trans_pcie->rx_page_order,
-                                      DMA_FROM_DEVICE);
-                       __free_pages(rxq->pool[i].page,
-                                    trans_pcie->rx_page_order);
-                       rxq->pool[i].page = NULL;
-               }
-               list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+               if (!rxq->pool[i].page)
+                       continue;
+               dma_unmap_page(trans->dev, rxq->pool[i].page_dma,
+                              PAGE_SIZE << trans_pcie->rx_page_order,
+                              DMA_FROM_DEVICE);
+               __free_pages(rxq->pool[i].page, trans_pcie->rx_page_order);
+               rxq->pool[i].page = NULL;
        }
 }
 
@@ -491,6 +490,20 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq)
        iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF);
 }
 
+static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq)
+{
+       int i;
+
+       lockdep_assert_held(&rxq->lock);
+
+       INIT_LIST_HEAD(&rxq->rx_free);
+       INIT_LIST_HEAD(&rxq->rx_used);
+       rxq->free_count = 0;
+
+       for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
+               list_add(&rxq->pool[i].list, &rxq->rx_used);
+}
+
 int iwl_pcie_rx_init(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -505,13 +518,12 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
        }
 
        spin_lock_irqsave(&rxq->lock, flags);
-       INIT_LIST_HEAD(&rxq->rx_free);
-       INIT_LIST_HEAD(&rxq->rx_used);
 
-       INIT_WORK(&trans_pcie->rx_replenish,
-                 iwl_pcie_rx_replenish_work);
+       INIT_WORK(&trans_pcie->rx_replenish, iwl_pcie_rx_replenish_work);
 
+       /* free all first - we might be reconfigured for a different size */
        iwl_pcie_rxq_free_rbs(trans);
+       iwl_pcie_rx_init_rxb_lists(rxq);
 
        for (i = 0; i < RX_QUEUE_SIZE; i++)
                rxq->queue[i] = NULL;
@@ -520,7 +532,6 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
         * not restocked the Rx queue with fresh buffers */
        rxq->read = rxq->write = 0;
        rxq->write_actual = 0;
-       rxq->free_count = 0;
        memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts));
        spin_unlock_irqrestore(&rxq->lock, flags);
 
@@ -802,9 +813,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
        u32 handled = 0;
        unsigned long flags;
        u32 i;
-#ifdef CONFIG_IWLWIFI_DEBUG
-       u32 inta_mask;
-#endif
 
        lock_map_acquire(&trans->sync_cmd_lockdep_map);
 
@@ -826,14 +834,9 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
 
        inta = trans_pcie->inta;
 
-#ifdef CONFIG_IWLWIFI_DEBUG
-       if (iwl_have_debug_level(IWL_DL_ISR)) {
-               /* just for debug */
-               inta_mask = iwl_read32(trans, CSR_INT_MASK);
+       if (iwl_have_debug_level(IWL_DL_ISR))
                IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n",
-                             inta, inta_mask);
-       }
-#endif
+                             inta, iwl_read32(trans, CSR_INT_MASK));
 
        /* saved interrupt in inta variable now we can reset trans_pcie->inta */
        trans_pcie->inta = 0;
@@ -855,12 +858,11 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                goto out;
        }
 
-#ifdef CONFIG_IWLWIFI_DEBUG
        if (iwl_have_debug_level(IWL_DL_ISR)) {
                /* NIC fires this, but we don't use it, redundant with WAKEUP */
                if (inta & CSR_INT_BIT_SCD) {
-                       IWL_DEBUG_ISR(trans, "Scheduler finished to transmit "
-                                     "the frame/frames.\n");
+                       IWL_DEBUG_ISR(trans,
+                                     "Scheduler finished to transmit the frame/frames.\n");
                        isr_stats->sch++;
                }
 
@@ -870,7 +872,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                        isr_stats->alive++;
                }
        }
-#endif
+
        /* Safely ignore these bits for debug checks below */
        inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
 
@@ -1118,9 +1120,6 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data)
        struct iwl_trans *trans = data;
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        u32 inta, inta_mask;
-#ifdef CONFIG_IWLWIFI_DEBUG
-       u32 inta_fh;
-#endif
 
        lockdep_assert_held(&trans_pcie->irq_lock);
 
@@ -1159,13 +1158,11 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data)
                return IRQ_HANDLED;
        }
 
-#ifdef CONFIG_IWLWIFI_DEBUG
-       if (iwl_have_debug_level(IWL_DL_ISR)) {
-               inta_fh = iwl_read32(trans, CSR_FH_INT_STATUS);
-               IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x, "
-                             "fh 0x%08x\n", inta, inta_mask, inta_fh);
-       }
-#endif
+       if (iwl_have_debug_level(IWL_DL_ISR))
+               IWL_DEBUG_ISR(trans,
+                             "ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n",
+                             inta, inta_mask,
+                             iwl_read32(trans, CSR_FH_INT_STATUS));
 
        trans_pcie->inta |= inta;
        /* the thread will service interrupts and re-enable them */
@@ -1198,7 +1195,7 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
 {
        struct iwl_trans *trans = data;
        struct iwl_trans_pcie *trans_pcie;
-       u32 inta, inta_mask;
+       u32 inta;
        u32 val = 0;
        u32 read;
        unsigned long flags;
@@ -1226,7 +1223,6 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
         * If we have something to service, the tasklet will re-enable ints.
         * If we *don't* have something, we'll re-enable before leaving here.
         */
-       inta_mask = iwl_read32(trans, CSR_INT_MASK);
        iwl_write32(trans, CSR_INT_MASK, 0x00000000);
 
        /* Ignore interrupt if there's nothing in NIC to service.
@@ -1271,8 +1267,11 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
                val |= 0x8000;
 
        inta = (0xff & val) | ((0xff00 & val) << 16);
-       IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n",
-                     inta, inta_mask, val);
+       IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled(sw) 0x%08x ict 0x%08x\n",
+                     inta, trans_pcie->inta_mask, val);
+       if (iwl_have_debug_level(IWL_DL_ISR))
+               IWL_DEBUG_ISR(trans, "enabled(hw) 0x%08x\n",
+                             iwl_read32(trans, CSR_INT_MASK));
 
        inta &= trans_pcie->inta_mask;
        trans_pcie->inta |= inta;
index 50ba0a4..826c156 100644 (file)
@@ -405,20 +405,27 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
 {
        u8 *v_addr;
        dma_addr_t p_addr;
-       u32 offset;
+       u32 offset, chunk_sz = section->len;
        int ret = 0;
 
        IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
                     section_num);
 
-       v_addr = dma_alloc_coherent(trans->dev, PAGE_SIZE, &p_addr, GFP_KERNEL);
-       if (!v_addr)
-               return -ENOMEM;
+       v_addr = dma_alloc_coherent(trans->dev, chunk_sz, &p_addr,
+                                   GFP_KERNEL | __GFP_NOWARN);
+       if (!v_addr) {
+               IWL_DEBUG_INFO(trans, "Falling back to small chunks of DMA\n");
+               chunk_sz = PAGE_SIZE;
+               v_addr = dma_alloc_coherent(trans->dev, chunk_sz,
+                                           &p_addr, GFP_KERNEL);
+               if (!v_addr)
+                       return -ENOMEM;
+       }
 
-       for (offset = 0; offset < section->len; offset += PAGE_SIZE) {
+       for (offset = 0; offset < section->len; offset += chunk_sz) {
                u32 copy_size;
 
-               copy_size = min_t(u32, PAGE_SIZE, section->len - offset);
+               copy_size = min_t(u32, chunk_sz, section->len - offset);
 
                memcpy(v_addr, (u8 *)section->data + offset, copy_size);
                ret = iwl_pcie_load_firmware_chunk(trans,
@@ -432,7 +439,7 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
                }
        }
 
-       dma_free_coherent(trans->dev, PAGE_SIZE, v_addr, p_addr);
+       dma_free_coherent(trans->dev, chunk_sz, v_addr, p_addr);
        return ret;
 }
 
@@ -571,13 +578,17 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
        clear_bit(STATUS_RFKILL, &trans_pcie->status);
 }
 
-static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans)
+static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
 {
-       /* let the ucode operate on its own */
-       iwl_write32(trans, CSR_UCODE_DRV_GP1_SET,
-                   CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
-
        iwl_disable_interrupts(trans);
+
+       /*
+        * in testing mode, the host stays awake and the
+        * hardware won't be reset (not even partially)
+        */
+       if (test)
+               return;
+
        iwl_pcie_disable_ict(trans);
 
        iwl_clear_bit(trans, CSR_GP_CNTRL,
@@ -596,11 +607,18 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans)
 }
 
 static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
-                                   enum iwl_d3_status *status)
+                                   enum iwl_d3_status *status,
+                                   bool test)
 {
        u32 val;
        int ret;
 
+       if (test) {
+               iwl_enable_interrupts(trans);
+               *status = IWL_D3_STATUS_ALIVE;
+               return 0;
+       }
+
        iwl_pcie_set_pwr(trans, false);
 
        val = iwl_read32(trans, CSR_RESET);
@@ -636,9 +654,6 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
                return ret;
        }
 
-       iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
-                   CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
-
        *status = IWL_D3_STATUS_ALIVE;
        return 0;
 }
@@ -823,8 +838,9 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
                                                unsigned long *flags)
 {
        int ret;
-       struct iwl_trans_pcie *pcie_trans = IWL_TRANS_GET_PCIE_TRANS(trans);
-       spin_lock_irqsave(&pcie_trans->reg_lock, *flags);
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       spin_lock_irqsave(&trans_pcie->reg_lock, *flags);
 
        /* this bit wakes up the NIC */
        __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
@@ -860,7 +876,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
                        WARN_ONCE(1,
                                  "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n",
                                  val);
-                       spin_unlock_irqrestore(&pcie_trans->reg_lock, *flags);
+                       spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags);
                        return false;
                }
        }
@@ -869,22 +885,22 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
         * Fool sparse by faking we release the lock - sparse will
         * track nic_access anyway.
         */
-       __release(&pcie_trans->reg_lock);
+       __release(&trans_pcie->reg_lock);
        return true;
 }
 
 static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
                                              unsigned long *flags)
 {
-       struct iwl_trans_pcie *pcie_trans = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
-       lockdep_assert_held(&pcie_trans->reg_lock);
+       lockdep_assert_held(&trans_pcie->reg_lock);
 
        /*
         * Fool sparse by faking we acquiring the lock - sparse will
         * track nic_access anyway.
         */
-       __acquire(&pcie_trans->reg_lock);
+       __acquire(&trans_pcie->reg_lock);
 
        __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
                                   CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
@@ -895,7 +911,7 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
         * scheduled on different CPUs (after we drop reg_lock).
         */
        mmiowb();
-       spin_unlock_irqrestore(&pcie_trans->reg_lock, *flags);
+       spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags);
 }
 
 static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
@@ -917,11 +933,11 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
 }
 
 static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr,
-                                   void *buf, int dwords)
+                                   const void *buf, int dwords)
 {
        unsigned long flags;
        int offs, ret = 0;
-       u32 *vals = buf;
+       const u32 *vals = buf;
 
        if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr);
index c5e3029..c47c921 100644 (file)
@@ -224,13 +224,13 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
 
        switch (sec_ctl & TX_CMD_SEC_MSK) {
        case TX_CMD_SEC_CCM:
-               len += CCMP_MIC_LEN;
+               len += IEEE80211_CCMP_MIC_LEN;
                break;
        case TX_CMD_SEC_TKIP:
-               len += TKIP_ICV_LEN;
+               len += IEEE80211_TKIP_ICV_LEN;
                break;
        case TX_CMD_SEC_WEP:
-               len += WEP_IV_LEN + WEP_ICV_LEN;
+               len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN;
                break;
        }
 
@@ -576,10 +576,16 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
 
        spin_lock_bh(&txq->lock);
        while (q->write_ptr != q->read_ptr) {
+               IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
+                                  txq_id, q->read_ptr);
                iwl_pcie_txq_free_tfd(trans, txq);
                q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
        }
+       txq->active = false;
        spin_unlock_bh(&txq->lock);
+
+       /* just in case - this queue may have been stopped */
+       iwl_wake_queue(trans, txq);
 }
 
 /*
@@ -927,6 +933,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 
        spin_lock_bh(&txq->lock);
 
+       if (!txq->active) {
+               IWL_DEBUG_TX_QUEUES(trans, "Q %d inactive - ignoring idx %d\n",
+                                   txq_id, ssn);
+               goto out;
+       }
+
        if (txq->q.read_ptr == tfd_num)
                goto out;
 
@@ -1045,6 +1057,10 @@ static inline void iwl_pcie_txq_set_inactive(struct iwl_trans *trans,
                (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
 }
 
+/* Receiver address (actually, Rx station's index into station table),
+ * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
+#define BUILD_RAxTID(sta_id, tid)      (((sta_id) << 4) + (tid))
+
 void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
                               int sta_id, int tid, int frame_limit, u16 ssn)
 {
@@ -1069,6 +1085,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
 
                /* enable aggregations for the queue */
                iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
+               trans_pcie->txq[txq_id].ampdu = true;
        } else {
                /*
                 * disable aggregations for the queue, this will also make the
@@ -1103,6 +1120,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
                       (fifo << SCD_QUEUE_STTS_REG_POS_TXF) |
                       (1 << SCD_QUEUE_STTS_REG_POS_WSL) |
                       SCD_QUEUE_STTS_REG_MSK);
+       trans_pcie->txq[txq_id].active = true;
        IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n",
                            txq_id, fifo, ssn & 0xff);
 }
@@ -1125,6 +1143,7 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id)
                            ARRAY_SIZE(zero_val));
 
        iwl_pcie_txq_unmap(trans, txq_id);
+       trans_pcie->txq[txq_id].ampdu = false;
 
        IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id);
 }
@@ -1518,11 +1537,13 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) {
                IWL_ERR(trans, "FW error in SYNC CMD %s\n",
                        get_cmd_string(trans_pcie, cmd->id));
+               dump_stack();
                ret = -EIO;
                goto cancel;
        }
 
-       if (test_bit(STATUS_RFKILL, &trans_pcie->status)) {
+       if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
+           test_bit(STATUS_RFKILL, &trans_pcie->status)) {
                IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n");
                ret = -ERFKILL;
                goto cancel;
@@ -1564,7 +1585,8 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
        if (test_bit(STATUS_FW_ERROR, &trans_pcie->status))
                return -EIO;
 
-       if (test_bit(STATUS_RFKILL, &trans_pcie->status)) {
+       if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
+           test_bit(STATUS_RFKILL, &trans_pcie->status)) {
                IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n",
                                  cmd->id);
                return -ERFKILL;
@@ -1592,7 +1614,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        u8 wait_write_ptr = 0;
        __le16 fc = hdr->frame_control;
        u8 hdr_len = ieee80211_hdrlen(fc);
-       u16 __maybe_unused wifi_seq;
+       u16 wifi_seq;
 
        txq = &trans_pcie->txq[txq_id];
        q = &txq->q;
@@ -1609,13 +1631,11 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
         * the BA.
         * Check here that the packets are in the right place on the ring.
         */
-#ifdef CONFIG_IWLWIFI_DEBUG
        wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
-       WARN_ONCE((iwl_read_prph(trans, SCD_AGGR_SEL) & BIT(txq_id)) &&
-                 ((wifi_seq & 0xff) != q->write_ptr),
+       WARN_ONCE(trans_pcie->txq[txq_id].ampdu &&
+                 (wifi_seq & 0xff) != q->write_ptr,
                  "Q: %d WiFi Seq %d tfdNum %d",
                  txq_id, wifi_seq, q->write_ptr);
-#endif
 
        /* Set up driver data for this TFD */
        txq->entries[q->write_ptr].skb = skb;
index 3e81264..efae07e 100644 (file)
@@ -240,7 +240,7 @@ static ssize_t lbs_prb_rsp_limit_set(struct device *dev,
        memset(&mesh_access, 0, sizeof(mesh_access));
        mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET);
 
-       if (!strict_strtoul(buf, 10, &retry_limit))
+       if (!kstrtoul(buf, 10, &retry_limit))
                return -ENOTSUPP;
        if (retry_limit > 15)
                return -ENOTSUPP;
diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c
new file mode 100644 (file)
index 0000000..8d68307
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Marvell Wireless LAN device driver: 802.11h
+ *
+ * Copyright (C) 2013, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include "main.h"
+#include "fw.h"
+
+
+/* This function appends 11h info to a buffer while joining an
+ * infrastructure BSS
+ */
+static void
+mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer,
+                              struct mwifiex_bssdescriptor *bss_desc)
+{
+       struct mwifiex_ie_types_header *ie_header;
+       struct mwifiex_ie_types_pwr_capability *cap;
+       struct mwifiex_ie_types_local_pwr_constraint *constraint;
+       struct ieee80211_supported_band *sband;
+       u8 radio_type;
+       int i;
+
+       if (!buffer || !(*buffer))
+               return;
+
+       radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
+       sband = priv->wdev->wiphy->bands[radio_type];
+
+       cap = (struct mwifiex_ie_types_pwr_capability *)*buffer;
+       cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY);
+       cap->header.len = cpu_to_le16(2);
+       cap->min_pwr = 0;
+       cap->max_pwr = 0;
+       *buffer += sizeof(*cap);
+
+       constraint = (struct mwifiex_ie_types_local_pwr_constraint *)*buffer;
+       constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT);
+       constraint->header.len = cpu_to_le16(2);
+       constraint->chan = bss_desc->channel;
+       constraint->constraint = bss_desc->local_constraint;
+       *buffer += sizeof(*constraint);
+
+       ie_header = (struct mwifiex_ie_types_header *)*buffer;
+       ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
+       ie_header->len  = cpu_to_le16(2 * sband->n_channels + 2);
+       *buffer += sizeof(*ie_header);
+       *(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS;
+       *(*buffer)++ = 2 * sband->n_channels;
+       for (i = 0; i < sband->n_channels; i++) {
+               *(*buffer)++ = ieee80211_frequency_to_channel(
+                                       sband->channels[i].center_freq);
+               *(*buffer)++ = 1; /* one channel in the subband */
+       }
+}
+
+/* Enable or disable the 11h extensions in the firmware */
+static int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag)
+{
+       u32 enable = flag;
+
+       return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
+                                    HostCmd_ACT_GEN_SET, DOT11H_I, &enable);
+}
+
+/* This functions processes TLV buffer for a pending BSS Join command.
+ *
+ * Activate 11h functionality in the firmware if the spectrum management
+ * capability bit is found in the network we are joining. Also, necessary
+ * TLVs are set based on requested network's 11h capability.
+ */
+void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
+                             struct mwifiex_bssdescriptor *bss_desc)
+{
+       if (bss_desc->sensed_11h) {
+               /* Activate 11h functions in firmware, turns on capability
+                * bit
+                */
+               mwifiex_11h_activate(priv, true);
+               bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+               mwifiex_11h_process_infra_join(priv, buffer, bss_desc);
+       } else {
+               /* Deactivate 11h functions in the firmware */
+               mwifiex_11h_activate(priv, false);
+               bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT;
+       }
+}
index 4f614aa..f7ff472 100644 (file)
@@ -3,13 +3,13 @@ config MWIFIEX
        depends on CFG80211
        ---help---
          This adds support for wireless adapters based on Marvell
-         802.11n chipsets.
+         802.11n/ac chipsets.
 
          If you choose to build it as a module, it will be called
          mwifiex.
 
 config MWIFIEX_SDIO
-       tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797"
+       tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8897"
        depends on MWIFIEX && MMC
        select FW_LOADER
        ---help---
index ecf2846..a42a506 100644 (file)
@@ -40,6 +40,7 @@ mwifiex-y += sta_rx.o
 mwifiex-y += uap_txrx.o
 mwifiex-y += cfg80211.o
 mwifiex-y += ethtool.o
+mwifiex-y += 11h.o
 mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o
 obj-$(CONFIG_MWIFIEX) += mwifiex.o
 
index e42b266..ef5fa89 100644 (file)
@@ -20,6 +20,9 @@
 #include "cfg80211.h"
 #include "main.h"
 
+static char *reg_alpha2;
+module_param(reg_alpha2, charp, 0);
+
 static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
        {
                .max = 2, .types = BIT(NL80211_IFTYPE_STATION),
@@ -1231,6 +1234,51 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy,
        return 0;
 }
 
+/* cfg80211 operation handler for del_station.
+ * Function deauthenticates station which value is provided in mac parameter.
+ * If mac is NULL/broadcast, all stations in associated station list are
+ * deauthenticated. If bss is not started or there are no stations in
+ * associated stations list, no action is taken.
+ */
+static int
+mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
+                            u8 *mac)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       struct mwifiex_sta_node *sta_node;
+       unsigned long flags;
+
+       if (list_empty(&priv->sta_list) || !priv->bss_started)
+               return 0;
+
+       if (!mac || is_broadcast_ether_addr(mac)) {
+               wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
+               list_for_each_entry(sta_node, &priv->sta_list, list) {
+                       if (mwifiex_send_cmd_sync(priv,
+                                                 HostCmd_CMD_UAP_STA_DEAUTH,
+                                                 HostCmd_ACT_GEN_SET, 0,
+                                                 sta_node->mac_addr))
+                               return -1;
+                       mwifiex_uap_del_sta_data(priv, sta_node);
+               }
+       } else {
+               wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, mac);
+               spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+               sta_node = mwifiex_get_sta_entry(priv, mac);
+               spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+               if (sta_node) {
+                       if (mwifiex_send_cmd_sync(priv,
+                                                 HostCmd_CMD_UAP_STA_DEAUTH,
+                                                 HostCmd_ACT_GEN_SET, 0,
+                                                 sta_node->mac_addr))
+                               return -1;
+                       mwifiex_uap_del_sta_data(priv, sta_node);
+               }
+       }
+
+       return 0;
+}
+
 static int
 mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
 {
@@ -1859,6 +1907,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
        int i, offset, ret;
        struct ieee80211_channel *chan;
        struct ieee_types_header *ie;
+       struct mwifiex_user_scan_cfg *user_scan_cfg;
 
        wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name);
 
@@ -1869,20 +1918,22 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
                return -EBUSY;
        }
 
-       if (priv->user_scan_cfg) {
+       /* Block scan request if scan operation or scan cleanup when interface
+        * is disabled is in process
+        */
+       if (priv->scan_request || priv->scan_aborting) {
                dev_err(priv->adapter->dev, "cmd: Scan already in process..\n");
                return -EBUSY;
        }
 
-       priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg),
-                                     GFP_KERNEL);
-       if (!priv->user_scan_cfg)
+       user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
+       if (!user_scan_cfg)
                return -ENOMEM;
 
        priv->scan_request = request;
 
-       priv->user_scan_cfg->num_ssids = request->n_ssids;
-       priv->user_scan_cfg->ssid_list = request->ssids;
+       user_scan_cfg->num_ssids = request->n_ssids;
+       user_scan_cfg->ssid_list = request->ssids;
 
        if (request->ie && request->ie_len) {
                offset = 0;
@@ -1902,25 +1953,25 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
        for (i = 0; i < min_t(u32, request->n_channels,
                              MWIFIEX_USER_SCAN_CHAN_MAX); i++) {
                chan = request->channels[i];
-               priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
-               priv->user_scan_cfg->chan_list[i].radio_type = chan->band;
+               user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
+               user_scan_cfg->chan_list[i].radio_type = chan->band;
 
                if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
-                       priv->user_scan_cfg->chan_list[i].scan_type =
+                       user_scan_cfg->chan_list[i].scan_type =
                                                MWIFIEX_SCAN_TYPE_PASSIVE;
                else
-                       priv->user_scan_cfg->chan_list[i].scan_type =
+                       user_scan_cfg->chan_list[i].scan_type =
                                                MWIFIEX_SCAN_TYPE_ACTIVE;
 
-               priv->user_scan_cfg->chan_list[i].scan_time = 0;
+               user_scan_cfg->chan_list[i].scan_time = 0;
        }
 
-       ret = mwifiex_scan_networks(priv, priv->user_scan_cfg);
+       ret = mwifiex_scan_networks(priv, user_scan_cfg);
+       kfree(user_scan_cfg);
        if (ret) {
                dev_err(priv->adapter->dev, "scan failed: %d\n", ret);
+               priv->scan_aborting = false;
                priv->scan_request = NULL;
-               kfree(priv->user_scan_cfg);
-               priv->user_scan_cfg = NULL;
                return ret;
        }
 
@@ -2419,6 +2470,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .change_beacon = mwifiex_cfg80211_change_beacon,
        .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
        .set_antenna = mwifiex_cfg80211_set_antenna,
+       .del_station = mwifiex_cfg80211_del_station,
 #ifdef CONFIG_PM
        .suspend = mwifiex_cfg80211_suspend,
        .resume = mwifiex_cfg80211_resume,
@@ -2426,6 +2478,27 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
 #endif
 };
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support mwifiex_wowlan_support = {
+       .flags = WIPHY_WOWLAN_MAGIC_PKT,
+       .n_patterns = MWIFIEX_MAX_FILTERS,
+       .pattern_min_len = 1,
+       .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
+       .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
+};
+#endif
+
+static bool mwifiex_is_valid_alpha2(const char *alpha2)
+{
+       if (!alpha2 || strlen(alpha2) != 2)
+               return false;
+
+       if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
+               return true;
+
+       return false;
+}
+
 /*
  * This function registers the device with CFG802.11 subsystem.
  *
@@ -2478,16 +2551,13 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                        WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
                        WIPHY_FLAG_AP_UAPSD |
                        WIPHY_FLAG_CUSTOM_REGULATORY |
+                       WIPHY_FLAG_STRICT_REGULATORY |
                        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 
        wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom);
 
 #ifdef CONFIG_PM
-       wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT;
-       wiphy->wowlan.n_patterns = MWIFIEX_MAX_FILTERS;
-       wiphy->wowlan.pattern_min_len = 1;
-       wiphy->wowlan.pattern_max_len = MWIFIEX_MAX_PATTERN_LEN;
-       wiphy->wowlan.max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN;
+       wiphy->wowlan = &mwifiex_wowlan_support;
 #endif
 
        wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
@@ -2519,10 +2589,16 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                wiphy_free(wiphy);
                return ret;
        }
-       country_code = mwifiex_11d_code_2_region(priv->adapter->region_code);
-       if (country_code)
-               dev_info(adapter->dev,
-                        "ignoring F/W country code %2.2s\n", country_code);
+
+       if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) {
+               wiphy_info(wiphy, "driver hint alpha2: %2.2s\n", reg_alpha2);
+               regulatory_hint(wiphy, reg_alpha2);
+       } else {
+               country_code = mwifiex_11d_code_2_region(adapter->region_code);
+               if (country_code)
+                       wiphy_info(wiphy, "ignoring F/W country code %2.2s\n",
+                                  country_code);
+       }
 
        adapter->wiphy = wiphy;
        return ret;
index 26755d9..2d76147 100644 (file)
@@ -570,6 +570,7 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
                case HostCmd_CMD_UAP_SYS_CONFIG:
                case HostCmd_CMD_UAP_BSS_START:
                case HostCmd_CMD_UAP_BSS_STOP:
+               case HostCmd_CMD_UAP_STA_DEAUTH:
                        ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action,
                                                      cmd_oid, data_buf,
                                                      cmd_ptr);
index 1f7578d..1b45aa5 100644 (file)
@@ -245,6 +245,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HT_BW_20    0
 #define HT_BW_40    1
 
+#define DFS_CHAN_MOVE_TIME      10000
+
 #define HostCmd_CMD_GET_HW_SPEC                       0x0003
 #define HostCmd_CMD_802_11_SCAN                       0x0006
 #define HostCmd_CMD_802_11_GET_LOG                    0x000b
@@ -271,6 +273,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HostCmd_CMD_802_11_SUBSCRIBE_EVENT            0x0075
 #define HostCmd_CMD_802_11_TX_RATE_QUERY              0x007f
 #define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS     0x0083
+#define HostCmd_CMD_CFG_DATA                          0x008f
 #define HostCmd_CMD_VERSION_EXT                       0x0097
 #define HostCmd_CMD_MEF_CFG                           0x009a
 #define HostCmd_CMD_RSSI_INFO                         0x00a4
@@ -279,6 +282,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HostCmd_CMD_UAP_SYS_CONFIG                    0x00b0
 #define HostCmd_CMD_UAP_BSS_START                     0x00b1
 #define HostCmd_CMD_UAP_BSS_STOP                      0x00b2
+#define HostCmd_CMD_UAP_STA_DEAUTH                    0x00b5
 #define HostCmd_CMD_11N_CFG                           0x00cd
 #define HostCmd_CMD_11N_ADDBA_REQ                     0x00ce
 #define HostCmd_CMD_11N_ADDBA_RSP                     0x00cf
@@ -436,6 +440,7 @@ enum P2P_MODES {
 #define EVENT_BW_CHANGE                 0x00000048
 #define EVENT_UAP_MIC_COUNTERMEASURES   0x0000004c
 #define EVENT_HOSTWAKE_STAIE           0x0000004d
+#define EVENT_CHANNEL_SWITCH_ANN        0x00000050
 #define EVENT_REMAIN_ON_CHAN_EXPIRED    0x0000005f
 
 #define EVENT_ID_MASK                   0xffff
@@ -464,6 +469,8 @@ enum P2P_MODES {
 #define MWIFIEX_CRITERIA_UNICAST       BIT(1)
 #define MWIFIEX_CRITERIA_MULTICAST     BIT(3)
 
+#define CFG_DATA_TYPE_CAL              2
+
 struct mwifiex_ie_types_header {
        __le16 type;
        __le16 len;
@@ -971,6 +978,7 @@ enum SNMP_MIB_INDEX {
        LONG_RETRY_LIM_I = 7,
        FRAG_THRESH_I = 8,
        DOT11D_I = 9,
+       DOT11H_I = 10,
 };
 
 #define MAX_SNMP_BUF_SIZE   128
@@ -1197,6 +1205,23 @@ struct host_cmd_ds_amsdu_aggr_ctrl {
        __le16 curr_buf_size;
 } __packed;
 
+struct host_cmd_ds_sta_deauth {
+       u8 mac[ETH_ALEN];
+       __le16 reason;
+} __packed;
+
+struct mwifiex_ie_types_pwr_capability {
+       struct mwifiex_ie_types_header header;
+       s8 min_pwr;
+       s8 max_pwr;
+};
+
+struct mwifiex_ie_types_local_pwr_constraint {
+       struct mwifiex_ie_types_header header;
+       u8 chan;
+       u8 constraint;
+};
+
 struct mwifiex_ie_types_wmm_param_set {
        struct mwifiex_ie_types_header header;
        u8 wmm_ie[1];
@@ -1573,6 +1598,12 @@ struct mwifiex_ie_list {
        struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX];
 } __packed;
 
+struct host_cmd_ds_802_11_cfg_data {
+       __le16 action;
+       __le16 type;
+       __le16 data_len;
+} __packed;
+
 struct host_cmd_ds_command {
        __le16 command;
        __le16 size;
@@ -1630,7 +1661,9 @@ struct host_cmd_ds_command {
                struct host_cmd_ds_802_11_eeprom_access eeprom;
                struct host_cmd_ds_802_11_subsc_evt subsc_evt;
                struct host_cmd_ds_sys_config uap_sys_config;
+               struct host_cmd_ds_sta_deauth sta_deauth;
                struct host_cmd_11ac_vht_cfg vht_cfg;
+               struct host_cmd_ds_802_11_cfg_data cfg_data;
        } params;
 } __packed;
 
index 9f44fda..caaf4bd 100644 (file)
@@ -52,87 +52,6 @@ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv)
        return 0;
 }
 
-static void scan_delay_timer_fn(unsigned long data)
-{
-       struct mwifiex_private *priv = (struct mwifiex_private *)data;
-       struct mwifiex_adapter *adapter = priv->adapter;
-       struct cmd_ctrl_node *cmd_node, *tmp_node;
-       unsigned long flags;
-
-       if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
-               /*
-                * Abort scan operation by cancelling all pending scan
-                * commands
-                */
-               spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
-               list_for_each_entry_safe(cmd_node, tmp_node,
-                                        &adapter->scan_pending_q, list) {
-                       list_del(&cmd_node->list);
-                       mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
-               }
-               spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
-
-               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
-               adapter->scan_processing = false;
-               adapter->scan_delay_cnt = 0;
-               adapter->empty_tx_q_cnt = 0;
-               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
-
-               if (priv->user_scan_cfg) {
-                       if (priv->scan_request) {
-                               dev_dbg(priv->adapter->dev,
-                                       "info: aborting scan\n");
-                               cfg80211_scan_done(priv->scan_request, 1);
-                               priv->scan_request = NULL;
-                       } else {
-                               dev_dbg(priv->adapter->dev,
-                                       "info: scan already aborted\n");
-                       }
-
-                       kfree(priv->user_scan_cfg);
-                       priv->user_scan_cfg = NULL;
-               }
-               goto done;
-       }
-
-       if (!atomic_read(&priv->adapter->is_tx_received)) {
-               adapter->empty_tx_q_cnt++;
-               if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) {
-                       /*
-                        * No Tx traffic for 200msec. Get scan command from
-                        * scan pending queue and put to cmd pending queue to
-                        * resume scan operation
-                        */
-                       adapter->scan_delay_cnt = 0;
-                       adapter->empty_tx_q_cnt = 0;
-                       spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
-                       cmd_node = list_first_entry(&adapter->scan_pending_q,
-                                                   struct cmd_ctrl_node, list);
-                       list_del(&cmd_node->list);
-                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
-                                              flags);
-
-                       mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
-                                                       true);
-                       queue_work(adapter->workqueue, &adapter->main_work);
-                       goto done;
-               }
-       } else {
-               adapter->empty_tx_q_cnt = 0;
-       }
-
-       /* Delay scan operation further by 20msec */
-       mod_timer(&priv->scan_delay_timer, jiffies +
-                 msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
-       adapter->scan_delay_cnt++;
-
-done:
-       if (atomic_read(&priv->adapter->is_tx_received))
-               atomic_set(&priv->adapter->is_tx_received, false);
-
-       return;
-}
-
 /*
  * This function initializes the private structure and sets default
  * values to the members.
@@ -214,8 +133,8 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
 
        priv->scan_block = false;
 
-       setup_timer(&priv->scan_delay_timer, scan_delay_timer_fn,
-                   (unsigned long)priv);
+       priv->csa_chan = 0;
+       priv->csa_expire_time = 0;
 
        return mwifiex_add_bss_prio_tbl(priv);
 }
@@ -447,23 +366,29 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
 }
 
 /*
- * This function frees the adapter structure.
+ * This function performs cleanup for adapter structure.
  *
- * The freeing operation is done recursively, by canceling all
- * pending commands, freeing the member buffers previously
- * allocated (command buffers, scan table buffer, sleep confirm
- * command buffer), stopping the timers and calling the cleanup
- * routines for every interface, before the actual adapter
- * structure is freed.
+ * The cleanup is done recursively, by canceling all pending
+ * commands, freeing the member buffers previously allocated
+ * (command buffers, scan table buffer, sleep confirm command
+ * buffer), stopping the timers and calling the cleanup routines
+ * for every interface.
  */
 static void
-mwifiex_free_adapter(struct mwifiex_adapter *adapter)
+mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
 {
+       int i;
+
        if (!adapter) {
                pr_err("%s: adapter is NULL\n", __func__);
                return;
        }
 
+       for (i = 0; i < adapter->priv_num; i++) {
+               if (adapter->priv[i])
+                       del_timer_sync(&adapter->priv[i]->scan_delay_timer);
+       }
+
        mwifiex_cancel_all_pending_cmd(adapter);
 
        /* Free lock variables */
@@ -684,7 +609,6 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
        int ret = -EINPROGRESS;
        struct mwifiex_private *priv;
        s32 i;
-       unsigned long flags;
        struct sk_buff *skb;
 
        /* mwifiex already shutdown */
@@ -719,7 +643,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
                }
        }
 
-       spin_lock_irqsave(&adapter->mwifiex_lock, flags);
+       spin_lock(&adapter->mwifiex_lock);
 
        if (adapter->if_ops.data_complete) {
                while ((skb = skb_dequeue(&adapter->usb_rx_data_q))) {
@@ -733,10 +657,9 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
                }
        }
 
-       /* Free adapter structure */
-       mwifiex_free_adapter(adapter);
+       mwifiex_adapter_cleanup(adapter);
 
-       spin_unlock_irqrestore(&adapter->mwifiex_lock, flags);
+       spin_unlock(&adapter->mwifiex_lock);
 
        /* Notify completion */
        ret = mwifiex_shutdown_fw_complete(adapter);
index 6bcb66e..1c8a771 100644 (file)
@@ -534,6 +534,8 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
 
        mwifiex_cmd_append_tsf_tlv(priv, &pos, bss_desc);
 
+       mwifiex_11h_process_join(priv, &pos, bss_desc);
+
        cmd->size = cpu_to_le16((u16) (pos - (u8 *) assoc) + S_DS_GEN);
 
        /* Set the Capability info at last */
@@ -919,9 +921,8 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
        memcpy(&priv->curr_bss_params.data_rates,
               &adhoc_start->data_rate, priv->curr_bss_params.num_of_rates);
 
-       dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%02x %02x %02x %02x\n",
-               adhoc_start->data_rate[0], adhoc_start->data_rate[1],
-               adhoc_start->data_rate[2], adhoc_start->data_rate[3]);
+       dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%4ph\n",
+               adhoc_start->data_rate);
 
        dev_dbg(adapter->dev, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n");
 
index 2eb88ea..e15ab72 100644 (file)
 #define VERSION        "1.0"
 
 const char driver_version[] = "mwifiex " VERSION " (%s) ";
+static char *cal_data_cfg;
+module_param(cal_data_cfg, charp, 0);
+
+static void scan_delay_timer_fn(unsigned long data)
+{
+       struct mwifiex_private *priv = (struct mwifiex_private *)data;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *cmd_node, *tmp_node;
+       unsigned long flags;
+
+       if (adapter->surprise_removed)
+               return;
+
+       if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
+               /*
+                * Abort scan operation by cancelling all pending scan
+                * commands
+                */
+               spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+               list_for_each_entry_safe(cmd_node, tmp_node,
+                                        &adapter->scan_pending_q, list) {
+                       list_del(&cmd_node->list);
+                       mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+               }
+               spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+
+               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+               adapter->scan_processing = false;
+               adapter->scan_delay_cnt = 0;
+               adapter->empty_tx_q_cnt = 0;
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+
+               if (priv->scan_request) {
+                       dev_dbg(adapter->dev, "info: aborting scan\n");
+                       cfg80211_scan_done(priv->scan_request, 1);
+                       priv->scan_request = NULL;
+               } else {
+                       priv->scan_aborting = false;
+                       dev_dbg(adapter->dev, "info: scan already aborted\n");
+               }
+               goto done;
+       }
+
+       if (!atomic_read(&priv->adapter->is_tx_received)) {
+               adapter->empty_tx_q_cnt++;
+               if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) {
+                       /*
+                        * No Tx traffic for 200msec. Get scan command from
+                        * scan pending queue and put to cmd pending queue to
+                        * resume scan operation
+                        */
+                       adapter->scan_delay_cnt = 0;
+                       adapter->empty_tx_q_cnt = 0;
+                       spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+                       cmd_node = list_first_entry(&adapter->scan_pending_q,
+                                                   struct cmd_ctrl_node, list);
+                       list_del(&cmd_node->list);
+                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+                                              flags);
+
+                       mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
+                                                       true);
+                       queue_work(adapter->workqueue, &adapter->main_work);
+                       goto done;
+               }
+       } else {
+               adapter->empty_tx_q_cnt = 0;
+       }
+
+       /* Delay scan operation further by 20msec */
+       mod_timer(&priv->scan_delay_timer, jiffies +
+                 msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
+       adapter->scan_delay_cnt++;
+
+done:
+       if (atomic_read(&priv->adapter->is_tx_received))
+               atomic_set(&priv->adapter->is_tx_received, false);
+
+       return;
+}
 
 /*
  * This function registers the device and performs all the necessary
@@ -73,6 +153,10 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
 
                adapter->priv[i]->adapter = adapter;
                adapter->priv_num++;
+
+               setup_timer(&adapter->priv[i]->scan_delay_timer,
+                           scan_delay_timer_fn,
+                           (unsigned long)adapter->priv[i]);
        }
        mwifiex_init_lock_list(adapter);
 
@@ -336,6 +420,13 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
 
        dev_notice(adapter->dev, "WLAN FW is active\n");
 
+       if (cal_data_cfg) {
+               if ((request_firmware(&adapter->cal_data, cal_data_cfg,
+                                     adapter->dev)) < 0)
+                       dev_err(adapter->dev,
+                               "Cal data request_firmware() failed\n");
+       }
+
        adapter->init_wait_q_woken = false;
        ret = mwifiex_init_fw(adapter);
        if (ret == -1) {
@@ -390,6 +481,10 @@ err_init_fw:
        pr_debug("info: %s: unregister device\n", __func__);
        adapter->if_ops.unregister_dev(adapter);
 done:
+       if (adapter->cal_data) {
+               release_firmware(adapter->cal_data);
+               adapter->cal_data = NULL;
+       }
        release_firmware(adapter->firmware);
        complete(&adapter->fw_load);
        return;
@@ -436,6 +531,7 @@ mwifiex_close(struct net_device *dev)
                dev_dbg(priv->adapter->dev, "aborting scan on ndo_stop\n");
                cfg80211_scan_done(priv->scan_request, 1);
                priv->scan_request = NULL;
+               priv->scan_aborting = true;
        }
 
        return 0;
@@ -573,9 +669,8 @@ static void mwifiex_set_multicast_list(struct net_device *dev)
                mcast_list.mode = MWIFIEX_ALL_MULTI_MODE;
        } else {
                mcast_list.mode = MWIFIEX_MULTICAST_MODE;
-               if (netdev_mc_count(dev))
-                       mcast_list.num_multicast_addr =
-                               mwifiex_copy_mcast_addr(&mcast_list, dev);
+               mcast_list.num_multicast_addr =
+                       mwifiex_copy_mcast_addr(&mcast_list, dev);
        }
        mwifiex_request_set_multicast_list(priv, &mcast_list);
 }
index 4ef67fc..3da73d3 100644 (file)
@@ -309,6 +309,9 @@ struct mwifiex_bssdescriptor {
        u16 wapi_offset;
        u8 *beacon_buf;
        u32 beacon_buf_size;
+       u8 sensed_11h;
+       u8 local_constraint;
+       u8 chan_sw_ie_present;
 };
 
 struct mwifiex_current_bss_params {
@@ -492,7 +495,6 @@ struct mwifiex_private {
        struct semaphore async_sem;
        u8 report_scan_result;
        struct cfg80211_scan_request *scan_request;
-       struct mwifiex_user_scan_cfg *user_scan_cfg;
        u8 cfg_bssid[6];
        struct wps wps;
        u8 scan_block;
@@ -510,6 +512,9 @@ struct mwifiex_private {
        u8 ap_11ac_enabled;
        u32 mgmt_frame_mask;
        struct mwifiex_roc_cfg roc_cfg;
+       bool scan_aborting;
+       u8 csa_chan;
+       unsigned long csa_expire_time;
 };
 
 enum mwifiex_ba_status {
@@ -730,6 +735,7 @@ struct mwifiex_adapter {
        u16 max_mgmt_ie_index;
        u8 scan_delay_cnt;
        u8 empty_tx_q_cnt;
+       const struct firmware *cal_data;
 
        /* 11AC */
        u32 is_hw_11ac_capable;
@@ -1017,6 +1023,24 @@ static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb)
        return (*(u32 *)skb->data == PKT_TYPE_MGMT);
 }
 
+/* This function retrieves channel closed for operation by Channel
+ * Switch Announcement.
+ */
+static inline u8
+mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv)
+{
+       if (!priv->csa_chan)
+               return 0;
+
+       /* Clear csa channel, if DFS channel move time has passed */
+       if (jiffies > priv->csa_expire_time) {
+               priv->csa_chan = 0;
+               priv->csa_expire_time = 0;
+       }
+
+       return priv->csa_chan;
+}
+
 int mwifiex_init_shutdown_fw(struct mwifiex_private *priv,
                             u32 func_init_shutdown);
 int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8);
@@ -1115,6 +1139,12 @@ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
                         struct cfg80211_beacon_data *data);
 int mwifiex_del_mgmt_ies(struct mwifiex_private *priv);
 u8 *mwifiex_11d_code_2_region(u8 code);
+void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
+                             struct mwifiex_sta_node *node);
+
+void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
+                             struct mwifiex_bssdescriptor *bss_desc);
+int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv);
 
 extern const struct ethtool_ops mwifiex_ethtool_ops;
 
index 9cf5d8f..c447d9b 100644 (file)
@@ -391,6 +391,12 @@ mwifiex_is_network_compatible(struct mwifiex_private *priv,
                return 0;
        }
 
+       if (bss_desc->chan_sw_ie_present) {
+               dev_err(adapter->dev,
+                       "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n");
+               return -1;
+       }
+
        if (mwifiex_is_bss_wapi(priv, bss_desc)) {
                dev_dbg(adapter->dev, "info: return success for WAPI AP\n");
                return 0;
@@ -569,6 +575,9 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                return -1;
        }
 
+       /* Check csa channel expiry before preparing scan list */
+       mwifiex_11h_get_csa_closed_channel(priv);
+
        chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
 
        /* Set the temp channel struct pointer to the start of the desired
@@ -598,6 +607,11 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                while (tlv_idx < max_chan_per_scan &&
                       tmp_chan_list->chan_number && !done_early) {
 
+                       if (tmp_chan_list->chan_number == priv->csa_chan) {
+                               tmp_chan_list++;
+                               continue;
+                       }
+
                        dev_dbg(priv->adapter->dev,
                                "info: Scan: Chan(%3d), Radio(%d),"
                                " Mode(%d, %d), Dur(%d)\n",
@@ -1169,6 +1183,19 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
                        bss_entry->erp_flags = *(current_ptr + 2);
                        break;
 
+               case WLAN_EID_PWR_CONSTRAINT:
+                       bss_entry->local_constraint = *(current_ptr + 2);
+                       bss_entry->sensed_11h = true;
+                       break;
+
+               case WLAN_EID_CHANNEL_SWITCH:
+                       bss_entry->chan_sw_ie_present = true;
+               case WLAN_EID_PWR_CAPABILITY:
+               case WLAN_EID_TPC_REPORT:
+               case WLAN_EID_QUIET:
+                       bss_entry->sensed_11h = true;
+                   break;
+
                case WLAN_EID_EXT_SUPP_RATES:
                        /*
                         * Only process extended supported rate
@@ -1575,6 +1602,9 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
                goto check_next_scan;
        }
 
+       /* Check csa channel expiry before parsing scan response */
+       mwifiex_11h_get_csa_closed_channel(priv);
+
        bytes_left = le16_to_cpu(scan_rsp->bss_descript_size);
        dev_dbg(adapter->dev, "info: SCAN_RESP: bss_descript_size %d\n",
                bytes_left);
@@ -1727,6 +1757,13 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
                        struct ieee80211_channel *chan;
                        u8 band;
 
+                       /* Skip entry if on csa closed channel */
+                       if (channel == priv->csa_chan) {
+                               dev_dbg(adapter->dev,
+                                       "Dropping entry on csa closed channel\n");
+                               continue;
+                       }
+
                        band = BAND_G;
                        if (chan_band_tlv) {
                                chan_band =
@@ -1784,22 +1821,17 @@ check_next_scan:
                if (priv->report_scan_result)
                        priv->report_scan_result = false;
 
-               if (priv->user_scan_cfg) {
-                       if (priv->scan_request) {
-                               dev_dbg(priv->adapter->dev,
-                                       "info: notifying scan done\n");
-                               cfg80211_scan_done(priv->scan_request, 0);
-                               priv->scan_request = NULL;
-                       } else {
-                               dev_dbg(priv->adapter->dev,
-                                       "info: scan already aborted\n");
-                       }
-
-                       kfree(priv->user_scan_cfg);
-                       priv->user_scan_cfg = NULL;
+               if (priv->scan_request) {
+                       dev_dbg(adapter->dev, "info: notifying scan done\n");
+                       cfg80211_scan_done(priv->scan_request, 0);
+                       priv->scan_request = NULL;
+               } else {
+                       priv->scan_aborting = false;
+                       dev_dbg(adapter->dev, "info: scan already aborted\n");
                }
        } else {
-               if (priv->user_scan_cfg && !priv->scan_request) {
+               if ((priv->scan_aborting && !priv->scan_request) ||
+                   priv->scan_block) {
                        spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
                                               flags);
                        adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT;
index 363ba31..5ee5ed0 100644 (file)
@@ -77,6 +77,17 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
 
        func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
 
+       if (id->driver_data) {
+               struct mwifiex_sdio_device *data = (void *)id->driver_data;
+
+               card->firmware = data->firmware;
+               card->reg = data->reg;
+               card->max_ports = data->max_ports;
+               card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
+               card->supports_sdio_new_mode = data->supports_sdio_new_mode;
+               card->has_control_mask = data->has_control_mask;
+       }
+
        sdio_claim_host(func);
        ret = sdio_enable_func(func);
        sdio_release_host(func);
@@ -251,12 +262,19 @@ static int mwifiex_sdio_resume(struct device *dev)
 #define SDIO_DEVICE_ID_MARVELL_8787   (0x9119)
 /* Device ID for SD8797 */
 #define SDIO_DEVICE_ID_MARVELL_8797   (0x9129)
+/* Device ID for SD8897 */
+#define SDIO_DEVICE_ID_MARVELL_8897   (0x912d)
 
 /* WLAN IDs */
 static const struct sdio_device_id mwifiex_ids[] = {
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786),
+               .driver_data = (unsigned long) &mwifiex_sdio_sd8786},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787),
+               .driver_data = (unsigned long) &mwifiex_sdio_sd8787},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797),
+               .driver_data = (unsigned long) &mwifiex_sdio_sd8797},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897),
+               .driver_data = (unsigned long) &mwifiex_sdio_sd8897},
        {},
 };
 
@@ -282,13 +300,13 @@ static struct sdio_driver mwifiex_sdio = {
  * This function writes data into SDIO card register.
  */
 static int
-mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data)
+mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data)
 {
        struct sdio_mmc_card *card = adapter->card;
        int ret = -1;
 
        sdio_claim_host(card->func);
-       sdio_writeb(card->func, (u8) data, reg, &ret);
+       sdio_writeb(card->func, data, reg, &ret);
        sdio_release_host(card->func);
 
        return ret;
@@ -298,7 +316,7 @@ mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data)
  * This function reads data from SDIO card register.
  */
 static int
-mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u32 *data)
+mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data)
 {
        struct sdio_mmc_card *card = adapter->card;
        int ret = -1;
@@ -400,7 +418,40 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
 }
 
 /*
- * This function initializes the IO ports.
+ * This function is used to initialize IO ports for the
+ * chipsets supporting SDIO new mode eg SD8897.
+ */
+static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter)
+{
+       u8 reg;
+
+       adapter->ioport = MEM_PORT;
+
+       /* enable sdio new mode */
+       if (mwifiex_read_reg(adapter, CARD_CONFIG_2_1_REG, &reg))
+               return -1;
+       if (mwifiex_write_reg(adapter, CARD_CONFIG_2_1_REG,
+                             reg | CMD53_NEW_MODE))
+               return -1;
+
+       /* Configure cmd port and enable reading rx length from the register */
+       if (mwifiex_read_reg(adapter, CMD_CONFIG_0, &reg))
+               return -1;
+       if (mwifiex_write_reg(adapter, CMD_CONFIG_0, reg | CMD_PORT_RD_LEN_EN))
+               return -1;
+
+       /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is
+        * completed
+        */
+       if (mwifiex_read_reg(adapter, CMD_CONFIG_1, &reg))
+               return -1;
+       if (mwifiex_write_reg(adapter, CMD_CONFIG_1, reg | CMD_PORT_AUTO_EN))
+               return -1;
+
+       return 0;
+}
+
+/* This function initializes the IO ports.
  *
  * The following operations are performed -
  *      - Read the IO ports (0, 1 and 2)
@@ -409,10 +460,17 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
  */
 static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter)
 {
-       u32 reg;
+       u8 reg;
+       struct sdio_mmc_card *card = adapter->card;
 
        adapter->ioport = 0;
 
+       if (card->supports_sdio_new_mode) {
+               if (mwifiex_init_sdio_new_mode(adapter))
+                       return -1;
+               goto cont;
+       }
+
        /* Read the IO port */
        if (!mwifiex_read_reg(adapter, IO_PORT_0_REG, &reg))
                adapter->ioport |= (reg & 0xff);
@@ -428,19 +486,19 @@ static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter)
                adapter->ioport |= ((reg & 0xff) << 16);
        else
                return -1;
-
+cont:
        pr_debug("info: SDIO FUNC1 IO port: %#x\n", adapter->ioport);
 
        /* Set Host interrupt reset to read to clear */
        if (!mwifiex_read_reg(adapter, HOST_INT_RSR_REG, &reg))
                mwifiex_write_reg(adapter, HOST_INT_RSR_REG,
-                                 reg | SDIO_INT_MASK);
+                                 reg | card->reg->sdio_int_mask);
        else
                return -1;
 
        /* Dnld/Upld ready set to auto reset */
-       if (!mwifiex_read_reg(adapter, CARD_MISC_CFG_REG, &reg))
-               mwifiex_write_reg(adapter, CARD_MISC_CFG_REG,
+       if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, &reg))
+               mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg,
                                  reg | AUTO_RE_ENABLE_INT);
        else
                return -1;
@@ -486,34 +544,42 @@ static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter,
 static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port)
 {
        struct sdio_mmc_card *card = adapter->card;
-       u16 rd_bitmap = card->mp_rd_bitmap;
+       const struct mwifiex_sdio_card_reg *reg = card->reg;
+       u32 rd_bitmap = card->mp_rd_bitmap;
 
-       dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%04x\n", rd_bitmap);
+       dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%08x\n", rd_bitmap);
 
-       if (!(rd_bitmap & (CTRL_PORT_MASK | DATA_PORT_MASK)))
-               return -1;
+       if (card->supports_sdio_new_mode) {
+               if (!(rd_bitmap & reg->data_port_mask))
+                       return -1;
+       } else {
+               if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask)))
+                       return -1;
+       }
 
-       if (card->mp_rd_bitmap & CTRL_PORT_MASK) {
-               card->mp_rd_bitmap &= (u16) (~CTRL_PORT_MASK);
+       if ((card->has_control_mask) &&
+           (card->mp_rd_bitmap & CTRL_PORT_MASK)) {
+               card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK);
                *port = CTRL_PORT;
-               dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%04x\n",
+               dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%08x\n",
                        *port, card->mp_rd_bitmap);
-       } else {
-               if (card->mp_rd_bitmap & (1 << card->curr_rd_port)) {
-                       card->mp_rd_bitmap &= (u16)
-                                               (~(1 << card->curr_rd_port));
-                       *port = card->curr_rd_port;
+               return 0;
+       }
 
-                       if (++card->curr_rd_port == MAX_PORT)
-                               card->curr_rd_port = 1;
-               } else {
-                       return -1;
-               }
+       if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port)))
+               return -1;
+
+       /* We are now handling the SDIO data ports */
+       card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port));
+       *port = card->curr_rd_port;
+
+       if (++card->curr_rd_port == card->max_ports)
+               card->curr_rd_port = reg->start_rd_port;
+
+       dev_dbg(adapter->dev,
+               "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n",
+               *port, rd_bitmap, card->mp_rd_bitmap);
 
-               dev_dbg(adapter->dev,
-                       "data: port=%d mp_rd_bitmap=0x%04x -> 0x%04x\n",
-                       *port, rd_bitmap, card->mp_rd_bitmap);
-       }
        return 0;
 }
 
@@ -524,35 +590,45 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port)
  * increased (provided it does not reach the maximum limit, in which
  * case it is reset to 1)
  */
-static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port)
+static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port)
 {
        struct sdio_mmc_card *card = adapter->card;
-       u16 wr_bitmap = card->mp_wr_bitmap;
+       const struct mwifiex_sdio_card_reg *reg = card->reg;
+       u32 wr_bitmap = card->mp_wr_bitmap;
 
-       dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%04x\n", wr_bitmap);
+       dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%08x\n", wr_bitmap);
 
-       if (!(wr_bitmap & card->mp_data_port_mask))
+       if (card->supports_sdio_new_mode &&
+           !(wr_bitmap & reg->data_port_mask)) {
+               adapter->data_sent = true;
+               return -EBUSY;
+       } else if (!card->supports_sdio_new_mode &&
+                  !(wr_bitmap & card->mp_data_port_mask)) {
                return -1;
+       }
 
        if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) {
-               card->mp_wr_bitmap &= (u16) (~(1 << card->curr_wr_port));
+               card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port));
                *port = card->curr_wr_port;
-               if (++card->curr_wr_port == card->mp_end_port)
-                       card->curr_wr_port = 1;
+               if (((card->supports_sdio_new_mode) &&
+                    (++card->curr_wr_port == card->max_ports)) ||
+                   ((!card->supports_sdio_new_mode) &&
+                    (++card->curr_wr_port == card->mp_end_port)))
+                       card->curr_wr_port = reg->start_wr_port;
        } else {
                adapter->data_sent = true;
                return -EBUSY;
        }
 
-       if (*port == CTRL_PORT) {
-               dev_err(adapter->dev, "invalid data port=%d cur port=%d"
-                       " mp_wr_bitmap=0x%04x -> 0x%04x\n",
+       if ((card->has_control_mask) && (*port == CTRL_PORT)) {
+               dev_err(adapter->dev,
+                       "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
                        *port, card->curr_wr_port, wr_bitmap,
                        card->mp_wr_bitmap);
                return -1;
        }
 
-       dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%04x -> 0x%04x\n",
+       dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
                *port, wr_bitmap, card->mp_wr_bitmap);
 
        return 0;
@@ -564,11 +640,12 @@ static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port)
 static int
 mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits)
 {
+       struct sdio_mmc_card *card = adapter->card;
        u32 tries;
-       u32 cs;
+       u8 cs;
 
        for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
-               if (mwifiex_read_reg(adapter, CARD_STATUS_REG, &cs))
+               if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs))
                        break;
                else if ((cs & bits) == bits)
                        return 0;
@@ -587,12 +664,14 @@ mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits)
 static int
 mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
 {
-       u32 fws0, fws1;
+       struct sdio_mmc_card *card = adapter->card;
+       const struct mwifiex_sdio_card_reg *reg = card->reg;
+       u8 fws0, fws1;
 
-       if (mwifiex_read_reg(adapter, CARD_FW_STATUS0_REG, &fws0))
+       if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0))
                return -1;
 
-       if (mwifiex_read_reg(adapter, CARD_FW_STATUS1_REG, &fws1))
+       if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1))
                return -1;
 
        *dat = (u16) ((fws1 << 8) | fws0);
@@ -608,14 +687,14 @@ mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
  */
 static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
 {
-       u32 host_int_mask;
+       u8 host_int_mask, host_int_disable = HOST_INT_DISABLE;
 
        /* Read back the host_int_mask register */
        if (mwifiex_read_reg(adapter, HOST_INT_MASK_REG, &host_int_mask))
                return -1;
 
        /* Update with the mask and write back to the register */
-       host_int_mask &= ~HOST_INT_DISABLE;
+       host_int_mask &= ~host_int_disable;
 
        if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, host_int_mask)) {
                dev_err(adapter->dev, "disable host interrupt failed\n");
@@ -633,8 +712,11 @@ static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
  */
 static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter)
 {
+       struct sdio_mmc_card *card = adapter->card;
+
        /* Simply write the mask to the register */
-       if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, HOST_INT_ENABLE)) {
+       if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG,
+                             card->reg->host_int_enable)) {
                dev_err(adapter->dev, "enable host interrupt failed\n");
                return -1;
        }
@@ -686,11 +768,13 @@ static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter,
 static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                                    struct mwifiex_fw_image *fw)
 {
+       struct sdio_mmc_card *card = adapter->card;
+       const struct mwifiex_sdio_card_reg *reg = card->reg;
        int ret;
        u8 *firmware = fw->fw_buf;
        u32 firmware_len = fw->fw_len;
        u32 offset = 0;
-       u32 base0, base1;
+       u8 base0, base1;
        u8 *fwbuf;
        u16 len = 0;
        u32 txlen, tx_blocks = 0, tries;
@@ -727,7 +811,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                        break;
 
                for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
-                       ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_0,
+                       ret = mwifiex_read_reg(adapter, reg->base_0_reg,
                                               &base0);
                        if (ret) {
                                dev_err(adapter->dev,
@@ -736,7 +820,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                                        base0, base0);
                                goto done;
                        }
-                       ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_1,
+                       ret = mwifiex_read_reg(adapter, reg->base_1_reg,
                                               &base1);
                        if (ret) {
                                dev_err(adapter->dev,
@@ -828,10 +912,11 @@ done:
 static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
                                   u32 poll_num)
 {
+       struct sdio_mmc_card *card = adapter->card;
        int ret = 0;
        u16 firmware_stat;
        u32 tries;
-       u32 winner_status;
+       u8 winner_status;
 
        /* Wait for firmware initialization event */
        for (tries = 0; tries < poll_num; tries++) {
@@ -849,7 +934,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
 
        if (ret) {
                if (mwifiex_read_reg
-                   (adapter, CARD_FW_STATUS0_REG, &winner_status))
+                   (adapter, card->reg->status_reg_0, &winner_status))
                        winner_status = 0;
 
                if (winner_status)
@@ -866,12 +951,12 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
 static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
 {
        struct sdio_mmc_card *card = adapter->card;
-       u32 sdio_ireg;
+       u8 sdio_ireg;
        unsigned long flags;
 
-       if (mwifiex_read_data_sync(adapter, card->mp_regs, MAX_MP_REGS,
-                                  REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK,
-                                  0)) {
+       if (mwifiex_read_data_sync(adapter, card->mp_regs,
+                                  card->reg->max_mp_regs,
+                                  REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
                dev_err(adapter->dev, "read mp_regs failed\n");
                return;
        }
@@ -880,6 +965,9 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
        if (sdio_ireg) {
                /*
                 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+                * For SDIO new mode CMD port interrupts
+                *      DN_LD_CMD_PORT_HOST_INT_STATUS and/or
+                *      UP_LD_CMD_PORT_HOST_INT_STATUS
                 * Clear the interrupt status register
                 */
                dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
@@ -1003,11 +1091,11 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
        s32 f_aggr_cur = 0;
        struct sk_buff *skb_deaggr;
        u32 pind;
-       u32 pkt_len, pkt_type = 0;
+       u32 pkt_len, pkt_type, mport;
        u8 *curr_ptr;
        u32 rx_len = skb->len;
 
-       if (port == CTRL_PORT) {
+       if ((card->has_control_mask) && (port == CTRL_PORT)) {
                /* Read the command Resp without aggr */
                dev_dbg(adapter->dev, "info: %s: no aggregation for cmd "
                        "response\n", __func__);
@@ -1024,7 +1112,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
                goto rx_curr_single;
        }
 
-       if (card->mp_rd_bitmap & (~((u16) CTRL_PORT_MASK))) {
+       if ((!card->has_control_mask && (card->mp_rd_bitmap &
+                                        card->reg->data_port_mask)) ||
+           (card->has_control_mask && (card->mp_rd_bitmap &
+                                       (~((u32) CTRL_PORT_MASK))))) {
                /* Some more data RX pending */
                dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__);
 
@@ -1060,10 +1151,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
        if (f_aggr_cur) {
                dev_dbg(adapter->dev, "info: current packet aggregation\n");
                /* Curr pkt can be aggregated */
-               MP_RX_AGGR_SETUP(card, skb, port);
+               mp_rx_aggr_setup(card, skb, port);
 
                if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) ||
-                   MP_RX_AGGR_PORT_LIMIT_REACHED(card)) {
+                   mp_rx_aggr_port_limit_reached(card)) {
                        dev_dbg(adapter->dev, "info: %s: aggregated packet "
                                "limit reached\n", __func__);
                        /* No more pkts allowed in Aggr buf, rx it */
@@ -1076,11 +1167,28 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
                dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n",
                        card->mpa_rx.pkt_cnt);
 
+               if (card->supports_sdio_new_mode) {
+                       int i;
+                       u32 port_count;
+
+                       for (i = 0, port_count = 0; i < card->max_ports; i++)
+                               if (card->mpa_rx.ports & BIT(i))
+                                       port_count++;
+
+                       /* Reading data from "start_port + 0" to "start_port +
+                        * port_count -1", so decrease the count by 1
+                        */
+                       port_count--;
+                       mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+                                (port_count << 8)) + card->mpa_rx.start_port;
+               } else {
+                       mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+                                (card->mpa_rx.ports << 4)) +
+                                card->mpa_rx.start_port;
+               }
+
                if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf,
-                                          card->mpa_rx.buf_len,
-                                          (adapter->ioport | 0x1000 |
-                                           (card->mpa_rx.ports << 4)) +
-                                          card->mpa_rx.start_port, 1))
+                                          card->mpa_rx.buf_len, mport, 1))
                        goto error;
 
                curr_ptr = card->mpa_rx.buf;
@@ -1167,6 +1275,7 @@ error:
 static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
 {
        struct sdio_mmc_card *card = adapter->card;
+       const struct mwifiex_sdio_card_reg *reg = card->reg;
        int ret = 0;
        u8 sdio_ireg;
        struct sk_buff *skb;
@@ -1175,6 +1284,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
        u32 rx_blocks;
        u16 rx_len;
        unsigned long flags;
+       u32 bitmap;
+       u8 cr;
 
        spin_lock_irqsave(&adapter->int_lock, flags);
        sdio_ireg = adapter->int_status;
@@ -1184,10 +1295,60 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
        if (!sdio_ireg)
                return ret;
 
+       /* Following interrupt is only for SDIO new mode */
+       if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent)
+               adapter->cmd_sent = false;
+
+       /* Following interrupt is only for SDIO new mode */
+       if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) {
+               u32 pkt_type;
+
+               /* read the len of control packet */
+               rx_len = card->mp_regs[CMD_RD_LEN_1] << 8;
+               rx_len |= (u16) card->mp_regs[CMD_RD_LEN_0];
+               rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE);
+               if (rx_len <= INTF_HEADER_LEN ||
+                   (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
+                    MWIFIEX_RX_DATA_BUF_SIZE)
+                       return -1;
+               rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
+
+               skb = dev_alloc_skb(rx_len);
+               if (!skb)
+                       return -1;
+
+               skb_put(skb, rx_len);
+
+               if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data,
+                                             skb->len, adapter->ioport |
+                                                       CMD_PORT_SLCT)) {
+                       dev_err(adapter->dev,
+                               "%s: failed to card_to_host", __func__);
+                       dev_kfree_skb_any(skb);
+                       goto term_cmd;
+               }
+
+               if ((pkt_type != MWIFIEX_TYPE_CMD) &&
+                   (pkt_type != MWIFIEX_TYPE_EVENT))
+                       dev_err(adapter->dev,
+                               "%s:Received wrong packet on cmd port",
+                               __func__);
+
+               mwifiex_decode_rx_packet(adapter, skb, pkt_type);
+       }
+
        if (sdio_ireg & DN_LD_HOST_INT_STATUS) {
-               card->mp_wr_bitmap = ((u16) card->mp_regs[WR_BITMAP_U]) << 8;
-               card->mp_wr_bitmap |= (u16) card->mp_regs[WR_BITMAP_L];
-               dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%04x\n",
+               bitmap = (u32) card->mp_regs[reg->wr_bitmap_l];
+               bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8;
+               if (card->supports_sdio_new_mode) {
+                       bitmap |=
+                               ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16;
+                       bitmap |=
+                               ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24;
+               }
+               card->mp_wr_bitmap = bitmap;
+
+               dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%x\n",
                        card->mp_wr_bitmap);
                if (adapter->data_sent &&
                    (card->mp_wr_bitmap & card->mp_data_port_mask)) {
@@ -1200,11 +1361,11 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
        /* As firmware will not generate download ready interrupt if the port
           updated is command port only, cmd_sent should be done for any SDIO
           interrupt. */
-       if (adapter->cmd_sent) {
+       if (card->has_control_mask && adapter->cmd_sent) {
                /* Check if firmware has attach buffer at command port and
                   update just that in wr_bit_map. */
                card->mp_wr_bitmap |=
-                       (u16) card->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK;
+                       (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK;
                if (card->mp_wr_bitmap & CTRL_PORT_MASK)
                        adapter->cmd_sent = false;
        }
@@ -1212,9 +1373,16 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
        dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n",
                adapter->cmd_sent, adapter->data_sent);
        if (sdio_ireg & UP_LD_HOST_INT_STATUS) {
-               card->mp_rd_bitmap = ((u16) card->mp_regs[RD_BITMAP_U]) << 8;
-               card->mp_rd_bitmap |= (u16) card->mp_regs[RD_BITMAP_L];
-               dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%04x\n",
+               bitmap = (u32) card->mp_regs[reg->rd_bitmap_l];
+               bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8;
+               if (card->supports_sdio_new_mode) {
+                       bitmap |=
+                               ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16;
+                       bitmap |=
+                               ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24;
+               }
+               card->mp_rd_bitmap = bitmap;
+               dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%x\n",
                        card->mp_rd_bitmap);
 
                while (true) {
@@ -1224,8 +1392,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
                                        "info: no more rd_port available\n");
                                break;
                        }
-                       len_reg_l = RD_LEN_P0_L + (port << 1);
-                       len_reg_u = RD_LEN_P0_U + (port << 1);
+                       len_reg_l = reg->rd_len_p0_l + (port << 1);
+                       len_reg_u = reg->rd_len_p0_u + (port << 1);
                        rx_len = ((u16) card->mp_regs[len_reg_u]) << 8;
                        rx_len |= (u16) card->mp_regs[len_reg_l];
                        dev_dbg(adapter->dev, "info: RX: port=%d rx_len=%u\n",
@@ -1257,37 +1425,33 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
 
                        if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb,
                                                              port)) {
-                               u32 cr = 0;
-
                                dev_err(adapter->dev, "card_to_host_mpa failed:"
                                        " int status=%#x\n", sdio_ireg);
-                               if (mwifiex_read_reg(adapter,
-                                                    CONFIGURATION_REG, &cr))
-                                       dev_err(adapter->dev,
-                                               "read CFG reg failed\n");
-
-                               dev_dbg(adapter->dev,
-                                       "info: CFG reg val = %d\n", cr);
-                               if (mwifiex_write_reg(adapter,
-                                                     CONFIGURATION_REG,
-                                                     (cr | 0x04)))
-                                       dev_err(adapter->dev,
-                                               "write CFG reg failed\n");
-
-                               dev_dbg(adapter->dev, "info: write success\n");
-                               if (mwifiex_read_reg(adapter,
-                                                    CONFIGURATION_REG, &cr))
-                                       dev_err(adapter->dev,
-                                               "read CFG reg failed\n");
-
-                               dev_dbg(adapter->dev,
-                                       "info: CFG reg val =%x\n", cr);
-                               return -1;
+                               goto term_cmd;
                        }
                }
        }
 
        return 0;
+
+term_cmd:
+       /* terminate cmd */
+       if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
+               dev_err(adapter->dev, "read CFG reg failed\n");
+       else
+               dev_dbg(adapter->dev, "info: CFG reg val = %d\n", cr);
+
+       if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04)))
+               dev_err(adapter->dev, "write CFG reg failed\n");
+       else
+               dev_dbg(adapter->dev, "info: write success\n");
+
+       if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
+               dev_err(adapter->dev, "read CFG reg failed\n");
+       else
+               dev_dbg(adapter->dev, "info: CFG reg val =%x\n", cr);
+
+       return -1;
 }
 
 /*
@@ -1305,7 +1469,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
  * and return.
  */
 static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
-                                       u8 *payload, u32 pkt_len, u8 port,
+                                       u8 *payload, u32 pkt_len, u32 port,
                                        u32 next_pkt_len)
 {
        struct sdio_mmc_card *card = adapter->card;
@@ -1314,8 +1478,11 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
        s32 f_send_cur_buf = 0;
        s32 f_precopy_cur_buf = 0;
        s32 f_postcopy_cur_buf = 0;
+       u32 mport;
 
-       if ((!card->mpa_tx.enabled) || (port == CTRL_PORT)) {
+       if (!card->mpa_tx.enabled ||
+           (card->has_control_mask && (port == CTRL_PORT)) ||
+           (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) {
                dev_dbg(adapter->dev, "info: %s: tx aggregation disabled\n",
                        __func__);
 
@@ -1329,7 +1496,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
                        __func__);
 
                if (MP_TX_AGGR_IN_PROGRESS(card)) {
-                       if (!MP_TX_AGGR_PORT_LIMIT_REACHED(card) &&
+                       if (!mp_tx_aggr_port_limit_reached(card) &&
                            MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) {
                                f_precopy_cur_buf = 1;
 
@@ -1342,7 +1509,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
                                /* No room in Aggr buf, send it */
                                f_send_aggr_buf = 1;
 
-                               if (MP_TX_AGGR_PORT_LIMIT_REACHED(card) ||
+                               if (mp_tx_aggr_port_limit_reached(card) ||
                                    !(card->mp_wr_bitmap &
                                      (1 << card->curr_wr_port)))
                                        f_send_cur_buf = 1;
@@ -1381,7 +1548,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
                MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port);
 
                if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) ||
-                   MP_TX_AGGR_PORT_LIMIT_REACHED(card))
+                   mp_tx_aggr_port_limit_reached(card))
                        /* No more pkts allowed in Aggr buf, send it */
                        f_send_aggr_buf = 1;
        }
@@ -1390,11 +1557,28 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
                dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n",
                        __func__,
                                card->mpa_tx.start_port, card->mpa_tx.ports);
+               if (card->supports_sdio_new_mode) {
+                       u32 port_count;
+                       int i;
+
+                       for (i = 0, port_count = 0; i < card->max_ports; i++)
+                               if (card->mpa_tx.ports & BIT(i))
+                                       port_count++;
+
+                       /* Writing data from "start_port + 0" to "start_port +
+                        * port_count -1", so decrease the count by 1
+                        */
+                       port_count--;
+                       mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+                                (port_count << 8)) + card->mpa_tx.start_port;
+               } else {
+                       mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+                                (card->mpa_tx.ports << 4)) +
+                                card->mpa_tx.start_port;
+               }
+
                ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf,
-                                                card->mpa_tx.buf_len,
-                                                (adapter->ioport | 0x1000 |
-                                                (card->mpa_tx.ports << 4)) +
-                                                 card->mpa_tx.start_port);
+                                                card->mpa_tx.buf_len, mport);
 
                MP_TX_AGGR_BUF_RESET(card);
        }
@@ -1434,7 +1618,7 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
        int ret;
        u32 buf_block_len;
        u32 blk_size;
-       u8 port = CTRL_PORT;
+       u32 port = CTRL_PORT;
        u8 *payload = (u8 *)skb->data;
        u32 pkt_len = skb->len;
 
@@ -1465,6 +1649,9 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
                    pkt_len > MWIFIEX_UPLD_SIZE)
                        dev_err(adapter->dev, "%s: payload=%p, nb=%d\n",
                                __func__, payload, pkt_len);
+
+               if (card->supports_sdio_new_mode)
+                       port = CMD_PORT_SLCT;
        }
 
        /* Transfer data to card */
@@ -1586,18 +1773,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
 
        adapter->dev = &func->dev;
 
-       switch (func->device) {
-       case SDIO_DEVICE_ID_MARVELL_8786:
-               strcpy(adapter->fw_name, SD8786_DEFAULT_FW_NAME);
-               break;
-       case SDIO_DEVICE_ID_MARVELL_8797:
-               strcpy(adapter->fw_name, SD8797_DEFAULT_FW_NAME);
-               break;
-       case SDIO_DEVICE_ID_MARVELL_8787:
-       default:
-               strcpy(adapter->fw_name, SD8787_DEFAULT_FW_NAME);
-               break;
-       }
+       strcpy(adapter->fw_name, card->firmware);
 
        return 0;
 
@@ -1626,8 +1802,9 @@ disable_func:
 static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
 {
        struct sdio_mmc_card *card = adapter->card;
+       const struct mwifiex_sdio_card_reg *reg = card->reg;
        int ret;
-       u32 sdio_ireg;
+       u8 sdio_ireg;
 
        /*
         * Read the HOST_INT_STATUS_REG for ACK the first interrupt got
@@ -1645,30 +1822,35 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
        /* Initialize SDIO variables in card */
        card->mp_rd_bitmap = 0;
        card->mp_wr_bitmap = 0;
-       card->curr_rd_port = 1;
-       card->curr_wr_port = 1;
+       card->curr_rd_port = reg->start_rd_port;
+       card->curr_wr_port = reg->start_wr_port;
 
-       card->mp_data_port_mask = DATA_PORT_MASK;
+       card->mp_data_port_mask = reg->data_port_mask;
 
        card->mpa_tx.buf_len = 0;
        card->mpa_tx.pkt_cnt = 0;
        card->mpa_tx.start_port = 0;
 
        card->mpa_tx.enabled = 1;
-       card->mpa_tx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT;
+       card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit;
 
        card->mpa_rx.buf_len = 0;
        card->mpa_rx.pkt_cnt = 0;
        card->mpa_rx.start_port = 0;
 
        card->mpa_rx.enabled = 1;
-       card->mpa_rx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT;
+       card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit;
 
        /* Allocate buffers for SDIO MP-A */
-       card->mp_regs = kzalloc(MAX_MP_REGS, GFP_KERNEL);
+       card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL);
        if (!card->mp_regs)
                return -ENOMEM;
 
+       /* Allocate skb pointer buffers */
+       card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) *
+                                      card->mp_agg_pkt_limit, GFP_KERNEL);
+       card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) *
+                                      card->mp_agg_pkt_limit, GFP_KERNEL);
        ret = mwifiex_alloc_sdio_mpa_buffers(adapter,
                                             SDIO_MP_TX_AGGR_DEF_BUF_SIZE,
                                             SDIO_MP_RX_AGGR_DEF_BUF_SIZE);
@@ -1705,6 +1887,8 @@ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter)
        struct sdio_mmc_card *card = adapter->card;
 
        kfree(card->mp_regs);
+       kfree(card->mpa_rx.skb_arr);
+       kfree(card->mpa_rx.len_arr);
        kfree(card->mpa_tx.buf);
        kfree(card->mpa_rx.buf);
 }
@@ -1716,16 +1900,20 @@ static void
 mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port)
 {
        struct sdio_mmc_card *card = adapter->card;
+       const struct mwifiex_sdio_card_reg *reg = card->reg;
        int i;
 
        card->mp_end_port = port;
 
-       card->mp_data_port_mask = DATA_PORT_MASK;
+       card->mp_data_port_mask = reg->data_port_mask;
 
-       for (i = 1; i <= MAX_PORT - card->mp_end_port; i++)
-               card->mp_data_port_mask &= ~(1 << (MAX_PORT - i));
+       if (reg->start_wr_port) {
+               for (i = 1; i <= card->max_ports - card->mp_end_port; i++)
+                       card->mp_data_port_mask &=
+                                       ~(1 << (card->max_ports - i));
+       }
 
-       card->curr_wr_port = 1;
+       card->curr_wr_port = reg->start_wr_port;
 
        dev_dbg(adapter->dev, "cmd: mp_end_port %d, data port mask 0x%x\n",
                port, card->mp_data_port_mask);
@@ -1831,3 +2019,4 @@ MODULE_LICENSE("GPL v2");
 MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME);
index 8cc5468..6d51dfd 100644 (file)
 #define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin"
 #define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin"
 #define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin"
+#define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin"
 
 #define BLOCK_MODE     1
 #define BYTE_MODE      0
 
 #define REG_PORT                       0
-#define RD_BITMAP_L                    0x04
-#define RD_BITMAP_U                    0x05
-#define WR_BITMAP_L                    0x06
-#define WR_BITMAP_U                    0x07
-#define RD_LEN_P0_L                    0x08
-#define RD_LEN_P0_U                    0x09
 
 #define MWIFIEX_SDIO_IO_PORT_MASK              0xfffff
 
 #define MWIFIEX_SDIO_BYTE_MODE_MASK    0x80000000
 
+#define SDIO_MPA_ADDR_BASE             0x1000
 #define CTRL_PORT                      0
 #define CTRL_PORT_MASK                 0x0001
-#define DATA_PORT_MASK                 0xfffe
 
-#define MAX_MP_REGS                    64
-#define MAX_PORT                       16
-
-#define SDIO_MP_AGGR_DEF_PKT_LIMIT     8
+#define CMD_PORT_UPLD_INT_MASK         (0x1U<<6)
+#define CMD_PORT_DNLD_INT_MASK         (0x1U<<7)
+#define HOST_TERM_CMD53                        (0x1U << 2)
+#define REG_PORT                       0
+#define MEM_PORT                       0x10000
+#define CMD_RD_LEN_0                   0xB4
+#define CMD_RD_LEN_1                   0xB5
+#define CARD_CONFIG_2_1_REG             0xCD
+#define CMD53_NEW_MODE                 (0x1U << 0)
+#define CMD_CONFIG_0                   0xB8
+#define CMD_PORT_RD_LEN_EN             (0x1U << 2)
+#define CMD_CONFIG_1                   0xB9
+#define CMD_PORT_AUTO_EN               (0x1U << 0)
+#define CMD_PORT_SLCT                  0x8000
+#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U)
+#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U)
 
 #define SDIO_MP_TX_AGGR_DEF_BUF_SIZE        (8192)     /* 8K */
 
 
 /* Host Control Registers : Configuration */
 #define CONFIGURATION_REG              0x00
-/* Host Control Registers : Host without Command 53 finish host*/
-#define HOST_TO_CARD_EVENT       (0x1U << 3)
-/* Host Control Registers : Host without Command 53 finish host */
-#define HOST_WO_CMD53_FINISH_HOST      (0x1U << 2)
 /* Host Control Registers : Host power up */
 #define HOST_POWER_UP                  (0x1U << 1)
-/* Host Control Registers : Host power down */
-#define HOST_POWER_DOWN                        (0x1U << 0)
 
 /* Host Control Registers : Host interrupt mask */
 #define HOST_INT_MASK_REG              0x02
@@ -90,8 +91,7 @@
 #define UP_LD_HOST_INT_MASK            (0x1U)
 /* Host Control Registers : Download host interrupt mask */
 #define DN_LD_HOST_INT_MASK            (0x2U)
-/* Enable Host interrupt mask */
-#define HOST_INT_ENABLE        (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK)
+
 /* Disable Host interrupt mask */
 #define        HOST_INT_DISABLE                0xff
 
 
 /* Host Control Registers : Host interrupt RSR */
 #define HOST_INT_RSR_REG               0x01
-/* Host Control Registers : Upload host interrupt RSR */
-#define UP_LD_HOST_INT_RSR             (0x1U)
-#define SDIO_INT_MASK                  0x3F
 
 /* Host Control Registers : Host interrupt status */
 #define HOST_INT_STATUS_REG            0x28
-/* Host Control Registers : Upload CRC error */
-#define UP_LD_CRC_ERR                  (0x1U << 2)
-/* Host Control Registers : Upload restart */
-#define UP_LD_RESTART                   (0x1U << 1)
-/* Host Control Registers : Download restart */
-#define DN_LD_RESTART                   (0x1U << 0)
-
-/* Card Control Registers : Card status register */
-#define CARD_STATUS_REG                 0x30
+
 /* Card Control Registers : Card I/O ready */
 #define CARD_IO_READY                   (0x1U << 3)
-/* Card Control Registers : CIS card ready */
-#define CIS_CARD_RDY                    (0x1U << 2)
-/* Card Control Registers : Upload card ready */
-#define UP_LD_CARD_RDY                  (0x1U << 1)
 /* Card Control Registers : Download card ready */
 #define DN_LD_CARD_RDY                  (0x1U << 0)
 
-/* Card Control Registers : Host interrupt mask register */
-#define HOST_INTERRUPT_MASK_REG         0x34
-/* Card Control Registers : Host power interrupt mask */
-#define HOST_POWER_INT_MASK             (0x1U << 3)
-/* Card Control Registers : Abort card interrupt mask */
-#define ABORT_CARD_INT_MASK             (0x1U << 2)
-/* Card Control Registers : Upload card interrupt mask */
-#define UP_LD_CARD_INT_MASK             (0x1U << 1)
-/* Card Control Registers : Download card interrupt mask */
-#define DN_LD_CARD_INT_MASK             (0x1U << 0)
-
-/* Card Control Registers : Card interrupt status register */
-#define CARD_INTERRUPT_STATUS_REG       0x38
-/* Card Control Registers : Power up interrupt */
-#define POWER_UP_INT                    (0x1U << 4)
-/* Card Control Registers : Power down interrupt */
-#define POWER_DOWN_INT                  (0x1U << 3)
-
-/* Card Control Registers : Card interrupt RSR register */
-#define CARD_INTERRUPT_RSR_REG          0x3c
-/* Card Control Registers : Power up RSR */
-#define POWER_UP_RSR                    (0x1U << 4)
-/* Card Control Registers : Power down RSR */
-#define POWER_DOWN_RSR                  (0x1U << 3)
-
-/* Card Control Registers : Miscellaneous Configuration Register */
-#define CARD_MISC_CFG_REG               0x6C
-
-/* Host F1 read base 0 */
-#define HOST_F1_RD_BASE_0              0x0040
-/* Host F1 read base 1 */
-#define HOST_F1_RD_BASE_1              0x0041
-/* Host F1 card ready */
-#define HOST_F1_CARD_RDY               0x0020
-
-/* Firmware status 0 register */
-#define CARD_FW_STATUS0_REG            0x60
-/* Firmware status 1 register */
-#define CARD_FW_STATUS1_REG            0x61
-/* Rx length register */
-#define CARD_RX_LEN_REG                        0x62
-/* Rx unit register */
-#define CARD_RX_UNIT_REG               0x63
-
 /* Max retry number of CMD53 write */
 #define MAX_WRITE_IOMEM_RETRY          2
 
        if (a->mpa_tx.start_port <= port)                               \
                a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt));            \
        else                                                            \
-               a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+(MAX_PORT - \
+               a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+            \
+                                               (a->max_ports - \
                                                a->mp_end_port)));      \
        a->mpa_tx.pkt_cnt++;                                            \
 } while (0)
 #define MP_TX_AGGR_PKT_LIMIT_REACHED(a)                                        \
                        (a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit)
 
-/* SDIO Tx aggregation port limit ? */
-#define MP_TX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_wr_port <           \
-                       a->mpa_tx.start_port) && (((MAX_PORT -          \
-                       a->mpa_tx.start_port) + a->curr_wr_port) >=     \
-                               SDIO_MP_AGGR_DEF_PKT_LIMIT))
-
 /* Reset SDIO Tx aggregation buffer parameters */
 #define MP_TX_AGGR_BUF_RESET(a) do {                                   \
        a->mpa_tx.pkt_cnt = 0;                                          \
 #define MP_RX_AGGR_PKT_LIMIT_REACHED(a)                                        \
                        (a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit)
 
-/* SDIO Tx aggregation port limit ? */
-#define MP_RX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_rd_port <           \
-                       a->mpa_rx.start_port) && (((MAX_PORT -          \
-                       a->mpa_rx.start_port) + a->curr_rd_port) >=     \
-                       SDIO_MP_AGGR_DEF_PKT_LIMIT))
-
 /* SDIO Rx aggregation in progress ? */
 #define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0)
 
 #define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len)                             \
                        ((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size)
 
-/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
-#define MP_RX_AGGR_SETUP(a, skb, port) do {                            \
-       a->mpa_rx.buf_len += skb->len;                                  \
-       if (!a->mpa_rx.pkt_cnt)                                         \
-               a->mpa_rx.start_port = port;                            \
-       if (a->mpa_rx.start_port <= port)                               \
-               a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt));            \
-       else                                                            \
-               a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt+1));          \
-       a->mpa_rx.skb_arr[a->mpa_rx.pkt_cnt] = skb;                     \
-       a->mpa_rx.len_arr[a->mpa_rx.pkt_cnt] = skb->len;                \
-       a->mpa_rx.pkt_cnt++;                                            \
-} while (0)
-
 /* Reset SDIO Rx aggregation buffer parameters */
 #define MP_RX_AGGR_BUF_RESET(a) do {                                   \
        a->mpa_rx.pkt_cnt = 0;                                          \
        a->mpa_rx.start_port = 0;                                       \
 } while (0)
 
-
 /* data structure for SDIO MPA TX */
 struct mwifiex_sdio_mpa_tx {
        /* multiport tx aggregation buffer pointer */
        u8 *buf;
        u32 buf_len;
        u32 pkt_cnt;
-       u16 ports;
+       u32 ports;
        u16 start_port;
        u8 enabled;
        u32 buf_size;
@@ -272,11 +187,11 @@ struct mwifiex_sdio_mpa_rx {
        u8 *buf;
        u32 buf_len;
        u32 pkt_cnt;
-       u16 ports;
+       u32 ports;
        u16 start_port;
 
-       struct sk_buff *skb_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT];
-       u32 len_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT];
+       struct sk_buff **skb_arr;
+       u32 *len_arr;
 
        u8 enabled;
        u32 buf_size;
@@ -286,15 +201,47 @@ struct mwifiex_sdio_mpa_rx {
 int mwifiex_bus_register(void);
 void mwifiex_bus_unregister(void);
 
+struct mwifiex_sdio_card_reg {
+       u8 start_rd_port;
+       u8 start_wr_port;
+       u8 base_0_reg;
+       u8 base_1_reg;
+       u8 poll_reg;
+       u8 host_int_enable;
+       u8 status_reg_0;
+       u8 status_reg_1;
+       u8 sdio_int_mask;
+       u32 data_port_mask;
+       u8 max_mp_regs;
+       u8 rd_bitmap_l;
+       u8 rd_bitmap_u;
+       u8 rd_bitmap_1l;
+       u8 rd_bitmap_1u;
+       u8 wr_bitmap_l;
+       u8 wr_bitmap_u;
+       u8 wr_bitmap_1l;
+       u8 wr_bitmap_1u;
+       u8 rd_len_p0_l;
+       u8 rd_len_p0_u;
+       u8 card_misc_cfg_reg;
+};
+
 struct sdio_mmc_card {
        struct sdio_func *func;
        struct mwifiex_adapter *adapter;
 
-       u16 mp_rd_bitmap;
-       u16 mp_wr_bitmap;
+       const char *firmware;
+       const struct mwifiex_sdio_card_reg *reg;
+       u8 max_ports;
+       u8 mp_agg_pkt_limit;
+       bool supports_sdio_new_mode;
+       bool has_control_mask;
+
+       u32 mp_rd_bitmap;
+       u32 mp_wr_bitmap;
 
        u16 mp_end_port;
-       u16 mp_data_port_mask;
+       u32 mp_data_port_mask;
 
        u8 curr_rd_port;
        u8 curr_wr_port;
@@ -305,6 +252,98 @@ struct sdio_mmc_card {
        struct mwifiex_sdio_mpa_rx mpa_rx;
 };
 
+struct mwifiex_sdio_device {
+       const char *firmware;
+       const struct mwifiex_sdio_card_reg *reg;
+       u8 max_ports;
+       u8 mp_agg_pkt_limit;
+       bool supports_sdio_new_mode;
+       bool has_control_mask;
+};
+
+static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = {
+       .start_rd_port = 1,
+       .start_wr_port = 1,
+       .base_0_reg = 0x0040,
+       .base_1_reg = 0x0041,
+       .poll_reg = 0x30,
+       .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK,
+       .status_reg_0 = 0x60,
+       .status_reg_1 = 0x61,
+       .sdio_int_mask = 0x3f,
+       .data_port_mask = 0x0000fffe,
+       .max_mp_regs = 64,
+       .rd_bitmap_l = 0x04,
+       .rd_bitmap_u = 0x05,
+       .wr_bitmap_l = 0x06,
+       .wr_bitmap_u = 0x07,
+       .rd_len_p0_l = 0x08,
+       .rd_len_p0_u = 0x09,
+       .card_misc_cfg_reg = 0x6c,
+};
+
+static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = {
+       .start_rd_port = 0,
+       .start_wr_port = 0,
+       .base_0_reg = 0x60,
+       .base_1_reg = 0x61,
+       .poll_reg = 0x50,
+       .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK |
+                       CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK,
+       .status_reg_0 = 0xc0,
+       .status_reg_1 = 0xc1,
+       .sdio_int_mask = 0xff,
+       .data_port_mask = 0xffffffff,
+       .max_mp_regs = 184,
+       .rd_bitmap_l = 0x04,
+       .rd_bitmap_u = 0x05,
+       .rd_bitmap_1l = 0x06,
+       .rd_bitmap_1u = 0x07,
+       .wr_bitmap_l = 0x08,
+       .wr_bitmap_u = 0x09,
+       .wr_bitmap_1l = 0x0a,
+       .wr_bitmap_1u = 0x0b,
+       .rd_len_p0_l = 0x0c,
+       .rd_len_p0_u = 0x0d,
+       .card_misc_cfg_reg = 0xcc,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
+       .firmware = SD8786_DEFAULT_FW_NAME,
+       .reg = &mwifiex_reg_sd87xx,
+       .max_ports = 16,
+       .mp_agg_pkt_limit = 8,
+       .supports_sdio_new_mode = false,
+       .has_control_mask = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
+       .firmware = SD8787_DEFAULT_FW_NAME,
+       .reg = &mwifiex_reg_sd87xx,
+       .max_ports = 16,
+       .mp_agg_pkt_limit = 8,
+       .supports_sdio_new_mode = false,
+       .has_control_mask = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
+       .firmware = SD8797_DEFAULT_FW_NAME,
+       .reg = &mwifiex_reg_sd87xx,
+       .max_ports = 16,
+       .mp_agg_pkt_limit = 8,
+       .supports_sdio_new_mode = false,
+       .has_control_mask = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
+       .firmware = SD8897_DEFAULT_FW_NAME,
+       .reg = &mwifiex_reg_sd8897,
+       .max_ports = 32,
+       .mp_agg_pkt_limit = 16,
+       .supports_sdio_new_mode = true,
+       .has_control_mask = false,
+};
+
 /*
  * .cmdrsp_complete handler
  */
@@ -325,4 +364,77 @@ static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter,
        return 0;
 }
 
+static inline bool
+mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card)
+{
+       u8 tmp;
+
+       if (card->curr_rd_port < card->mpa_rx.start_port) {
+               if (card->supports_sdio_new_mode)
+                       tmp = card->mp_end_port >> 1;
+               else
+                       tmp = card->mp_agg_pkt_limit;
+
+               if (((card->max_ports - card->mpa_rx.start_port) +
+                   card->curr_rd_port) >= tmp)
+                       return true;
+       }
+
+       if (!card->supports_sdio_new_mode)
+               return false;
+
+       if ((card->curr_rd_port - card->mpa_rx.start_port) >=
+           (card->mp_end_port >> 1))
+               return true;
+
+       return false;
+}
+
+static inline bool
+mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card)
+{
+       u16 tmp;
+
+       if (card->curr_wr_port < card->mpa_tx.start_port) {
+               if (card->supports_sdio_new_mode)
+                       tmp = card->mp_end_port >> 1;
+               else
+                       tmp = card->mp_agg_pkt_limit;
+
+               if (((card->max_ports - card->mpa_tx.start_port) +
+                   card->curr_wr_port) >= tmp)
+                       return true;
+       }
+
+       if (!card->supports_sdio_new_mode)
+               return false;
+
+       if ((card->curr_wr_port - card->mpa_tx.start_port) >=
+           (card->mp_end_port >> 1))
+               return true;
+
+       return false;
+}
+
+/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
+static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card,
+                                   struct sk_buff *skb, u8 port)
+{
+       card->mpa_rx.buf_len += skb->len;
+
+       if (!card->mpa_rx.pkt_cnt)
+               card->mpa_rx.start_port = port;
+
+       if (card->supports_sdio_new_mode) {
+               card->mpa_rx.ports |= (1 << port);
+       } else {
+               if (card->mpa_rx.start_port <= port)
+                       card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt);
+               else
+                       card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1);
+       }
+       card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = skb;
+       card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = skb->len;
+       card->mpa_rx.pkt_cnt++;
+}
 #endif /* _MWIFIEX_SDIO_H */
index b193e25..8ece485 100644 (file)
@@ -1134,6 +1134,55 @@ mwifiex_cmd_mef_cfg(struct mwifiex_private *priv,
        return 0;
 }
 
+/* This function parse cal data from ASCII to hex */
+static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst)
+{
+       u8 *s = src, *d = dst;
+
+       while (s - src < len) {
+               if (*s && (isspace(*s) || *s == '\t')) {
+                       s++;
+                       continue;
+               }
+               if (isxdigit(*s)) {
+                       *d++ = simple_strtol(s, NULL, 16);
+                       s += 2;
+               } else {
+                       s++;
+               }
+       }
+
+       return d - dst;
+}
+
+/* This function prepares command of set_cfg_data. */
+static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
+                               struct host_cmd_ds_command *cmd,
+                               u16 cmd_action)
+{
+       struct host_cmd_ds_802_11_cfg_data *cfg_data = &cmd->params.cfg_data;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       u32 len, cal_data_offset;
+       u8 *tmp_cmd = (u8 *)cmd;
+
+       cal_data_offset = S_DS_GEN + sizeof(*cfg_data);
+       if ((adapter->cal_data->data) && (adapter->cal_data->size > 0))
+               len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data,
+                                           adapter->cal_data->size,
+                                           (u8 *)(tmp_cmd + cal_data_offset));
+       else
+               return -1;
+
+       cfg_data->action = cpu_to_le16(cmd_action);
+       cfg_data->type = cpu_to_le16(CFG_DATA_TYPE_CAL);
+       cfg_data->data_len = cpu_to_le16(len);
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA);
+       cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*cfg_data) + len);
+
+       return 0;
+}
+
 /*
  * This function prepares the commands before sending them to the firmware.
  *
@@ -1152,6 +1201,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
        case HostCmd_CMD_GET_HW_SPEC:
                ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr);
                break;
+       case HostCmd_CMD_CFG_DATA:
+               ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, cmd_action);
+               break;
        case HostCmd_CMD_MAC_CONTROL:
                ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action,
                                              data_buf);
@@ -1384,6 +1436,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
  */
 int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
 {
+       struct mwifiex_adapter *adapter = priv->adapter;
        int ret;
        u16 enable = true;
        struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl;
@@ -1404,6 +1457,15 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
                                            HostCmd_ACT_GEN_SET, 0, NULL);
                if (ret)
                        return -1;
+
+               /* Download calibration data to firmware */
+               if (adapter->cal_data) {
+                       ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA,
+                                               HostCmd_ACT_GEN_SET, 0, NULL);
+                       if (ret)
+                               return -1;
+               }
+
                /* Read MAC address from HW */
                ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_GET_HW_SPEC,
                                            HostCmd_ACT_GEN_GET, 0, NULL);
index 9f990e1..d85df15 100644 (file)
@@ -818,6 +818,18 @@ static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv,
        return 0;
 }
 
+/* This function handles the command response of set_cfg_data */
+static int mwifiex_ret_cfg_data(struct mwifiex_private *priv,
+                               struct host_cmd_ds_command *resp)
+{
+       if (resp->result != HostCmd_RESULT_OK) {
+               dev_err(priv->adapter->dev, "Cal data cmd resp failed\n");
+               return -1;
+       }
+
+       return 0;
+}
+
 /*
  * This function handles the command responses.
  *
@@ -841,6 +853,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
        case HostCmd_CMD_GET_HW_SPEC:
                ret = mwifiex_ret_get_hw_spec(priv, resp);
                break;
+       case HostCmd_CMD_CFG_DATA:
+               ret = mwifiex_ret_cfg_data(priv, resp);
+               break;
        case HostCmd_CMD_MAC_CONTROL:
                break;
        case HostCmd_CMD_802_11_MAC_ADDRESS:
@@ -978,6 +993,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
        case HostCmd_CMD_UAP_BSS_STOP:
                priv->bss_started = 0;
                break;
+       case HostCmd_CMD_UAP_STA_DEAUTH:
+               break;
        case HostCmd_CMD_MEF_CFG:
                break;
        default:
index 41aafc7..ea265ec 100644 (file)
@@ -427,6 +427,17 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
 
                break;
 
+       case EVENT_CHANNEL_SWITCH_ANN:
+               dev_dbg(adapter->dev, "event: Channel Switch Announcement\n");
+               priv->csa_expire_time =
+                               jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME);
+               priv->csa_chan = priv->curr_bss_params.bss_descriptor.channel;
+               ret = mwifiex_send_cmd_async(priv,
+                       HostCmd_CMD_802_11_DEAUTHENTICATE,
+                       HostCmd_ACT_GEN_SET, 0,
+                       priv->curr_bss_params.bss_descriptor.mac_address);
+               break;
+
        default:
                dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
                        eventcause);
index 1a8a19d..206c3e0 100644 (file)
@@ -104,16 +104,14 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
                } else {
                        priv->curr_pkt_filter &=
                                ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
-                       if (mcast_list->num_multicast_addr) {
-                               dev_dbg(priv->adapter->dev,
-                                       "info: Set multicast list=%d\n",
-                                      mcast_list->num_multicast_addr);
-                               /* Send multicast addresses to firmware */
-                               ret = mwifiex_send_cmd_async(priv,
-                                       HostCmd_CMD_MAC_MULTICAST_ADR,
-                                       HostCmd_ACT_GEN_SET, 0,
-                                       mcast_list);
-                       }
+                       dev_dbg(priv->adapter->dev,
+                               "info: Set multicast list=%d\n",
+                               mcast_list->num_multicast_addr);
+                       /* Send multicast addresses to firmware */
+                       ret = mwifiex_send_cmd_async(priv,
+                               HostCmd_CMD_MAC_MULTICAST_ADR,
+                               HostCmd_ACT_GEN_SET, 0,
+                               mcast_list);
                }
        }
        dev_dbg(priv->adapter->dev,
@@ -180,6 +178,9 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
         */
        bss_desc->disable_11ac = true;
 
+       if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT)
+               bss_desc->sensed_11h = true;
+
        return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc);
 }
 
@@ -257,30 +258,37 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
        }
 
        if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+               u8 config_bands;
+
                /* Infra mode */
                ret = mwifiex_deauthenticate(priv, NULL);
                if (ret)
                        goto done;
 
-               if (bss_desc) {
-                       u8 config_bands = 0;
+               if (!bss_desc)
+                       return -1;
 
-                       if (mwifiex_band_to_radio_type((u8) bss_desc->bss_band)
-                           == HostCmd_SCAN_RADIO_TYPE_BG)
-                               config_bands = BAND_B | BAND_G | BAND_GN |
-                                              BAND_GAC;
-                       else
-                               config_bands = BAND_A | BAND_AN | BAND_AAC;
+               if (mwifiex_band_to_radio_type(bss_desc->bss_band) ==
+                                               HostCmd_SCAN_RADIO_TYPE_BG)
+                       config_bands = BAND_B | BAND_G | BAND_GN | BAND_GAC;
+               else
+                       config_bands = BAND_A | BAND_AN | BAND_AAC;
 
-                       if (!((config_bands | adapter->fw_bands) &
-                             ~adapter->fw_bands))
-                               adapter->config_bands = config_bands;
-               }
+               if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands))
+                       adapter->config_bands = config_bands;
 
                ret = mwifiex_check_network_compatibility(priv, bss_desc);
                if (ret)
                        goto done;
 
+               if (mwifiex_11h_get_csa_closed_channel(priv) ==
+                                                       (u8)bss_desc->channel) {
+                       dev_err(adapter->dev,
+                               "Attempt to reconnect on csa closed chan(%d)\n",
+                               bss_desc->channel);
+                       goto done;
+               }
+
                dev_dbg(adapter->dev, "info: SSID found in scan list ... "
                                      "associating...\n");
 
index b04b1db..2de882d 100644 (file)
@@ -689,6 +689,23 @@ mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action,
        return 0;
 }
 
+/* This function prepares AP specific deauth command with mac supplied in
+ * function parameter.
+ */
+static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv,
+                                     struct host_cmd_ds_command *cmd, u8 *mac)
+{
+       struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth;
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH);
+       memcpy(sta_deauth->mac, mac, ETH_ALEN);
+       sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);
+
+       cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) +
+                               S_DS_GEN);
+       return 0;
+}
+
 /* This function prepares the AP specific commands before sending them
  * to the firmware.
  * This is a generic function which calls specific command preparation
@@ -710,6 +727,10 @@ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no,
                cmd->command = cpu_to_le16(cmd_no);
                cmd->size = cpu_to_le16(S_DS_GEN);
                break;
+       case HostCmd_CMD_UAP_STA_DEAUTH:
+               if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf))
+                       return -1;
+               break;
        default:
                dev_err(priv->adapter->dev,
                        "PREP_CMD: unknown cmd %#x\n", cmd_no);
index 21c640d..7180665 100644 (file)
@@ -107,18 +107,15 @@ mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
  */
 static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
 {
-       struct mwifiex_sta_node *node, *tmp;
+       struct mwifiex_sta_node *node;
        unsigned long flags;
 
        spin_lock_irqsave(&priv->sta_list_spinlock, flags);
 
        node = mwifiex_get_sta_entry(priv, mac);
        if (node) {
-               list_for_each_entry_safe(node, tmp, &priv->sta_list,
-                                        list) {
-                       list_del(&node->list);
-                       kfree(node);
-               }
+               list_del(&node->list);
+               kfree(node);
        }
 
        spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
@@ -295,3 +292,19 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
 
        return 0;
 }
+
+/* This function deletes station entry from associated station list.
+ * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream
+ * tables created for this station are deleted.
+ */
+void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
+                             struct mwifiex_sta_node *node)
+{
+       if (priv->ap_11n_enabled && node->is_11n_enabled) {
+               mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr);
+               mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr);
+       }
+       mwifiex_del_sta_entry(priv, node->mac_addr);
+
+       return;
+}
index 4be3d33..944e884 100644 (file)
@@ -37,6 +37,9 @@
 /* Offset for TOS field in the IP header */
 #define IPTOS_OFFSET 5
 
+static bool enable_tx_amsdu;
+module_param(enable_tx_amsdu, bool, 0644);
+
 /* WMM information IE */
 static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07,
        0x00, 0x50, 0xf2, 0x02,
@@ -1233,7 +1236,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
                                mwifiex_send_delba(priv, tid_del, ra, 1);
                        }
                }
-               if (mwifiex_is_amsdu_allowed(priv, tid) &&
+               if (enable_tx_amsdu && mwifiex_is_amsdu_allowed(priv, tid) &&
                    mwifiex_is_11n_aggragation_possible(priv, ptr,
                                                        adapter->tx_buf_size))
                        mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN,
index 6820fce..a3707fd 100644 (file)
@@ -1548,7 +1548,7 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
        if (!priv->pending_tx_pkts)
                return 0;
 
-       retry = 0;
+       retry = 1;
        rc = 0;
 
        spin_lock_bh(&priv->tx_lock);
@@ -1572,13 +1572,19 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
 
                spin_lock_bh(&priv->tx_lock);
 
-               if (timeout) {
+               if (timeout || !priv->pending_tx_pkts) {
                        WARN_ON(priv->pending_tx_pkts);
                        if (retry)
                                wiphy_notice(hw->wiphy, "tx rings drained\n");
                        break;
                }
 
+               if (retry) {
+                       mwl8k_tx_start(priv);
+                       retry = 0;
+                       continue;
+               }
+
                if (priv->pending_tx_pkts < oldcount) {
                        wiphy_notice(hw->wiphy,
                                     "waiting for tx rings to drain (%d -> %d pkts)\n",
@@ -2055,6 +2061,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw,
                                mwl8k_remove_stream(hw, stream);
                                spin_unlock(&priv->stream_lock);
                        }
+                       mwl8k_tx_start(priv);
                        spin_unlock_bh(&priv->tx_lock);
                        pci_unmap_single(priv->pdev, dma, skb->len,
                                         PCI_DMA_TODEVICE);
index ea7231a..43f5b9f 100644 (file)
@@ -38,7 +38,7 @@ static int orinoco_pci_resume(struct pci_dev *pdev)
        struct net_device *dev = priv->ndev;
        int err;
 
-       pci_set_power_state(pdev, 0);
+       pci_set_power_state(pdev, PCI_D0);
        err = pci_enable_device(pdev);
        if (err) {
                printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
index 1f9cb55..bdfe637 100644 (file)
@@ -881,7 +881,8 @@ static int ezusb_access_ltv(struct ezusb_priv *upriv,
 
        if (!upriv->udev) {
                dbg("Device disconnected");
-               return -ENODEV;
+               retval = -ENODEV;
+               goto exit;
        }
 
        if (upriv->read_urb->status != -EINPROGRESS)
index 978e7eb..7fc46f2 100644 (file)
@@ -42,8 +42,7 @@
 
 MODULE_FIRMWARE("3826.arm");
 
-/*
- * gpios should be handled in board files and provided via platform data,
+/* gpios should be handled in board files and provided via platform data,
  * but because it's currently impossible for p54spi to have a header file
  * in include/linux, let's use module paramaters for now
  */
@@ -191,8 +190,7 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev)
        const struct firmware *eeprom;
        int ret;
 
-       /*
-        * allow users to customize their eeprom.
+       /* allow users to customize their eeprom.
         */
 
        ret = request_firmware(&eeprom, "3826.eeprom", &priv->spi->dev);
@@ -285,8 +283,7 @@ static void p54spi_power_on(struct p54s_priv *priv)
        gpio_set_value(p54spi_gpio_power, 1);
        enable_irq(gpio_to_irq(p54spi_gpio_irq));
 
-       /*
-        * need to wait a while before device can be accessed, the length
+       /* need to wait a while before device can be accessed, the length
         * is just a guess
         */
        msleep(10);
@@ -365,7 +362,8 @@ static int p54spi_rx(struct p54s_priv *priv)
        /* Firmware may insert up to 4 padding bytes after the lmac header,
         * but it does not amend the size of SPI data transfer.
         * Such packets has correct data size in header, thus referencing
-        * past the end of allocated skb. Reserve extra 4 bytes for this case */
+        * past the end of allocated skb. Reserve extra 4 bytes for this case
+        */
        skb = dev_alloc_skb(len + 4);
        if (!skb) {
                p54spi_sleep(priv);
@@ -383,7 +381,8 @@ static int p54spi_rx(struct p54s_priv *priv)
        }
        p54spi_sleep(priv);
        /* Put additional bytes to compensate for the possible
-        * alignment-caused truncation */
+        * alignment-caused truncation
+        */
        skb_put(skb, 4);
 
        if (p54_rx(priv->hw, skb) == 0)
@@ -713,27 +712,7 @@ static struct spi_driver p54spi_driver = {
        .remove         = p54spi_remove,
 };
 
-static int __init p54spi_init(void)
-{
-       int ret;
-
-       ret = spi_register_driver(&p54spi_driver);
-       if (ret < 0) {
-               printk(KERN_ERR "failed to register SPI driver: %d", ret);
-               goto out;
-       }
-
-out:
-       return ret;
-}
-
-static void __exit p54spi_exit(void)
-{
-       spi_unregister_driver(&p54spi_driver);
-}
-
-module_init(p54spi_init);
-module_exit(p54spi_exit);
+module_spi_driver(p54spi_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");
index f714373..3d53a09 100644 (file)
@@ -1767,33 +1767,45 @@ static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = {
        .config                 = rt2400pci_config,
 };
 
-static const struct data_queue_desc rt2400pci_queue_rx = {
-       .entry_num              = 24,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt2400pci_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 24;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2400pci_queue_tx = {
-       .entry_num              = 24,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 24;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2400pci_queue_bcn = {
-       .entry_num              = 1,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 1;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2400pci_queue_atim = {
-       .entry_num              = 8,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_ATIM:
+               queue->limit = 8;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2400pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -1801,11 +1813,7 @@ static const struct rt2x00_ops rt2400pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = 0,
-       .rx                     = &rt2400pci_queue_rx,
-       .tx                     = &rt2400pci_queue_tx,
-       .bcn                    = &rt2400pci_queue_bcn,
-       .atim                   = &rt2400pci_queue_atim,
+       .queue_init             = rt2400pci_queue_init,
        .lib                    = &rt2400pci_rt2x00_ops,
        .hw                     = &rt2400pci_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index 77e45b2..0ac5c58 100644 (file)
@@ -2056,33 +2056,45 @@ static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = {
        .config                 = rt2500pci_config,
 };
 
-static const struct data_queue_desc rt2500pci_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt2500pci_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2500pci_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2500pci_queue_bcn = {
-       .entry_num              = 1,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 1;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2500pci_queue_atim = {
-       .entry_num              = 8,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_ATIM:
+               queue->limit = 8;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2500pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -2090,11 +2102,7 @@ static const struct rt2x00_ops rt2500pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = 0,
-       .rx                     = &rt2500pci_queue_rx,
-       .tx                     = &rt2500pci_queue_tx,
-       .bcn                    = &rt2500pci_queue_bcn,
-       .atim                   = &rt2500pci_queue_atim,
+       .queue_init             = rt2500pci_queue_init,
        .lib                    = &rt2500pci_rt2x00_ops,
        .hw                     = &rt2500pci_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index a7f7b36..85acc79 100644 (file)
@@ -1867,33 +1867,45 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
        .config                 = rt2500usb_config,
 };
 
-static const struct data_queue_desc rt2500usb_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+static void rt2500usb_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2500usb_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2500usb_queue_bcn = {
-       .entry_num              = 1,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb_bcn),
-};
+       case QID_BEACON:
+               queue->limit = 1;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb_bcn);
+               break;
 
-static const struct data_queue_desc rt2500usb_queue_atim = {
-       .entry_num              = 8,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_ATIM:
+               queue->limit = 8;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2500usb_ops = {
        .name                   = KBUILD_MODNAME,
@@ -1901,11 +1913,7 @@ static const struct rt2x00_ops rt2500usb_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXD_DESC_SIZE,
-       .rx                     = &rt2500usb_queue_rx,
-       .tx                     = &rt2500usb_queue_tx,
-       .bcn                    = &rt2500usb_queue_bcn,
-       .atim                   = &rt2500usb_queue_atim,
+       .queue_init             = rt2500usb_queue_init,
        .lib                    = &rt2500usb_rt2x00_ops,
        .hw                     = &rt2500usb_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index a7630d5..d78c495 100644 (file)
 #define CSR_REG_BASE                   0x1000
 #define CSR_REG_SIZE                   0x0800
 #define EEPROM_BASE                    0x0000
-#define EEPROM_SIZE                    0x0110
+#define EEPROM_SIZE                    0x0200
 #define BBP_BASE                       0x0000
 #define BBP_SIZE                       0x00ff
 #define RF_BASE                                0x0004
@@ -2625,11 +2625,13 @@ struct mac_iveiv_entry {
 /*
  * DMA descriptor defines.
  */
-#define TXWI_DESC_SIZE                 (4 * sizeof(__le32))
-#define RXWI_DESC_SIZE                 (4 * sizeof(__le32))
 
-#define TXWI_DESC_SIZE_5592            (5 * sizeof(__le32))
-#define RXWI_DESC_SIZE_5592            (6 * sizeof(__le32))
+#define TXWI_DESC_SIZE_4WORDS          (4 * sizeof(__le32))
+#define TXWI_DESC_SIZE_5WORDS          (5 * sizeof(__le32))
+
+#define RXWI_DESC_SIZE_4WORDS          (4 * sizeof(__le32))
+#define RXWI_DESC_SIZE_6WORDS          (6 * sizeof(__le32))
+
 /*
  * TX WI structure
  */
index 72f32e5..1f80ea5 100644 (file)
@@ -840,7 +840,7 @@ static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev,
                                                unsigned int beacon_base)
 {
        int i;
-       const int txwi_desc_size = rt2x00dev->ops->bcn->winfo_size;
+       const int txwi_desc_size = rt2x00dev->bcn->winfo_size;
 
        /*
         * For the Beacon base registers we only need to clear
@@ -2392,7 +2392,7 @@ static void rt2800_config_channel_rf55xx(struct rt2x00_dev *rt2x00dev,
        rt2800_rfcsr_write(rt2x00dev, 49, rfcsr);
 
        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, info->default_power2);
@@ -2678,30 +2678,53 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
 
        tx_pin = 0;
 
-       /* Turn on unused PA or LNA when not using 1T or 1R */
-       if (rt2x00dev->default_ant.tx_chain_num == 2) {
+       switch (rt2x00dev->default_ant.tx_chain_num) {
+       case 3:
+               /* Turn on tertiary PAs */
+               rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A2_EN,
+                                  rf->channel > 14);
+               rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G2_EN,
+                                  rf->channel <= 14);
+               /* fall-through */
+       case 2:
+               /* Turn on secondary PAs */
                rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN,
                                   rf->channel > 14);
                rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN,
                                   rf->channel <= 14);
+               /* fall-through */
+       case 1:
+               /* Turn on primary PAs */
+               rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN,
+                                  rf->channel > 14);
+               if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
+                       rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1);
+               else
+                       rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN,
+                                          rf->channel <= 14);
+               break;
        }
 
-       /* Turn on unused PA or LNA when not using 1T or 1R */
-       if (rt2x00dev->default_ant.rx_chain_num == 2) {
+       switch (rt2x00dev->default_ant.rx_chain_num) {
+       case 3:
+               /* Turn on tertiary LNAs */
+               rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A2_EN, 1);
+               rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G2_EN, 1);
+               /* fall-through */
+       case 2:
+               /* Turn on secondary LNAs */
                rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, 1);
                rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, 1);
+               /* fall-through */
+       case 1:
+               /* Turn on primary LNAs */
+               rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1);
+               rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1);
+               break;
        }
 
-       rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1);
-       rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1);
        rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1);
        rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1);
-       if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
-               rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1);
-       else
-               rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN,
-                                  rf->channel <= 14);
-       rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, rf->channel > 14);
 
        rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin);
 
@@ -3960,379 +3983,577 @@ static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev)
        rt2800_bbp_write(rt2x00dev, 106, 0x35);
 }
 
-static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev)
+static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev)
 {
-       int ant, div_mode;
        u16 eeprom;
        u8 value;
 
-       rt2800_init_bbp_early(rt2x00dev);
+       rt2800_bbp_read(rt2x00dev, 138, &value);
+       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+       if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
+               value |= 0x20;
+       if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
+               value &= ~0x02;
+       rt2800_bbp_write(rt2x00dev, 138, value);
+}
 
-       rt2800_bbp_read(rt2x00dev, 105, &value);
-       rt2x00_set_field8(&value, BBP105_MLD,
-                         rt2x00dev->default_ant.rx_chain_num == 2);
-       rt2800_bbp_write(rt2x00dev, 105, value);
+static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_bbp_write(rt2x00dev, 31, 0x08);
+
+       rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+       rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+       rt2800_bbp_write(rt2x00dev, 69, 0x12);
+       rt2800_bbp_write(rt2x00dev, 73, 0x10);
+
+       rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+       rt2800_bbp_write(rt2x00dev, 78, 0x0e);
+       rt2800_bbp_write(rt2x00dev, 80, 0x08);
+
+       rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+       rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+       rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+       rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+       rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+       rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+       rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
+       rt2800_bbp_write(rt2x00dev, 105, 0x01);
+
+       rt2800_bbp_write(rt2x00dev, 106, 0x35);
+}
+
+static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+       rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+       if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) {
+               rt2800_bbp_write(rt2x00dev, 69, 0x16);
+               rt2800_bbp_write(rt2x00dev, 73, 0x12);
+       } else {
+               rt2800_bbp_write(rt2x00dev, 69, 0x12);
+               rt2800_bbp_write(rt2x00dev, 73, 0x10);
+       }
+
+       rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+       rt2800_bbp_write(rt2x00dev, 81, 0x37);
+
+       rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+       rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+       if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D))
+               rt2800_bbp_write(rt2x00dev, 84, 0x19);
+       else
+               rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+       rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+       rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+       rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+       rt2800_bbp_write(rt2x00dev, 103, 0x00);
+
+       rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+       rt2800_bbp_write(rt2x00dev, 106, 0x35);
+}
+
+static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+       rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+       rt2800_bbp_write(rt2x00dev, 69, 0x12);
+       rt2800_bbp_write(rt2x00dev, 73, 0x10);
+
+       rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+       rt2800_bbp_write(rt2x00dev, 79, 0x13);
+       rt2800_bbp_write(rt2x00dev, 80, 0x05);
+       rt2800_bbp_write(rt2x00dev, 81, 0x33);
+
+       rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+       rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+       rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+       rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+       rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+       rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) ||
+           rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) ||
+           rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E))
+               rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+       else
+               rt2800_bbp_write(rt2x00dev, 103, 0x00);
+
+       rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+       rt2800_bbp_write(rt2x00dev, 106, 0x35);
+
+       if (rt2x00_rt(rt2x00dev, RT3071) ||
+           rt2x00_rt(rt2x00dev, RT3090))
+               rt2800_disable_unused_dac_adc(rt2x00dev);
+}
+
+static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev)
+{
+       u8 value;
 
        rt2800_bbp4_mac_if_ctrl(rt2x00dev);
 
-       rt2800_bbp_write(rt2x00dev, 20, 0x06);
        rt2800_bbp_write(rt2x00dev, 31, 0x08);
-       rt2800_bbp_write(rt2x00dev, 65, 0x2C);
-       rt2800_bbp_write(rt2x00dev, 68, 0xDD);
-       rt2800_bbp_write(rt2x00dev, 69, 0x1A);
-       rt2800_bbp_write(rt2x00dev, 70, 0x05);
+
+       rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+       rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+       rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+
+       rt2800_bbp_write(rt2x00dev, 69, 0x12);
        rt2800_bbp_write(rt2x00dev, 73, 0x13);
-       rt2800_bbp_write(rt2x00dev, 74, 0x0F);
-       rt2800_bbp_write(rt2x00dev, 75, 0x4F);
+       rt2800_bbp_write(rt2x00dev, 75, 0x46);
        rt2800_bbp_write(rt2x00dev, 76, 0x28);
+
+       rt2800_bbp_write(rt2x00dev, 77, 0x58);
+
+       rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+       rt2800_bbp_write(rt2x00dev, 74, 0x0b);
+       rt2800_bbp_write(rt2x00dev, 79, 0x18);
+       rt2800_bbp_write(rt2x00dev, 80, 0x09);
+       rt2800_bbp_write(rt2x00dev, 81, 0x33);
+
+       rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+       rt2800_bbp_write(rt2x00dev, 83, 0x7a);
+
+       rt2800_bbp_write(rt2x00dev, 84, 0x9a);
+
+       rt2800_bbp_write(rt2x00dev, 86, 0x38);
+
+       rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+       rt2800_bbp_write(rt2x00dev, 92, 0x02);
+
+       rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
+       rt2800_bbp_write(rt2x00dev, 104, 0x92);
+
+       rt2800_bbp_write(rt2x00dev, 105, 0x1c);
+
+       rt2800_bbp_write(rt2x00dev, 106, 0x03);
+
+       rt2800_bbp_write(rt2x00dev, 128, 0x12);
+
+       rt2800_bbp_write(rt2x00dev, 67, 0x24);
+       rt2800_bbp_write(rt2x00dev, 143, 0x04);
+       rt2800_bbp_write(rt2x00dev, 142, 0x99);
+       rt2800_bbp_write(rt2x00dev, 150, 0x30);
+       rt2800_bbp_write(rt2x00dev, 151, 0x2e);
+       rt2800_bbp_write(rt2x00dev, 152, 0x20);
+       rt2800_bbp_write(rt2x00dev, 153, 0x34);
+       rt2800_bbp_write(rt2x00dev, 154, 0x40);
+       rt2800_bbp_write(rt2x00dev, 155, 0x3b);
+       rt2800_bbp_write(rt2x00dev, 253, 0x04);
+
+       rt2800_bbp_read(rt2x00dev, 47, &value);
+       rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1);
+       rt2800_bbp_write(rt2x00dev, 47, value);
+
+       /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */
+       rt2800_bbp_read(rt2x00dev, 3, &value);
+       rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1);
+       rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1);
+       rt2800_bbp_write(rt2x00dev, 3, value);
+}
+
+static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_bbp_write(rt2x00dev, 3, 0x00);
+       rt2800_bbp_write(rt2x00dev, 4, 0x50);
+
+       rt2800_bbp_write(rt2x00dev, 31, 0x08);
+
+       rt2800_bbp_write(rt2x00dev, 47, 0x48);
+
+       rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+       rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+       rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+
+       rt2800_bbp_write(rt2x00dev, 69, 0x12);
+       rt2800_bbp_write(rt2x00dev, 73, 0x13);
+       rt2800_bbp_write(rt2x00dev, 75, 0x46);
+       rt2800_bbp_write(rt2x00dev, 76, 0x28);
+
        rt2800_bbp_write(rt2x00dev, 77, 0x59);
-       rt2800_bbp_write(rt2x00dev, 84, 0x9A);
+
+       rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+       rt2800_bbp_write(rt2x00dev, 78, 0x0e);
+       rt2800_bbp_write(rt2x00dev, 80, 0x08);
+       rt2800_bbp_write(rt2x00dev, 81, 0x37);
+
+       rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+       rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+       rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
        rt2800_bbp_write(rt2x00dev, 86, 0x38);
+
        rt2800_bbp_write(rt2x00dev, 88, 0x90);
+
        rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
        rt2800_bbp_write(rt2x00dev, 92, 0x02);
-       rt2800_bbp_write(rt2x00dev, 95, 0x9a);
-       rt2800_bbp_write(rt2x00dev, 98, 0x12);
-       rt2800_bbp_write(rt2x00dev, 103, 0xC0);
+
+       rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
        rt2800_bbp_write(rt2x00dev, 104, 0x92);
-       /* FIXME BBP105 owerwrite */
-       rt2800_bbp_write(rt2x00dev, 105, 0x3C);
-       rt2800_bbp_write(rt2x00dev, 106, 0x35);
-       rt2800_bbp_write(rt2x00dev, 128, 0x12);
-       rt2800_bbp_write(rt2x00dev, 134, 0xD0);
-       rt2800_bbp_write(rt2x00dev, 135, 0xF6);
-       rt2800_bbp_write(rt2x00dev, 137, 0x0F);
 
-       /* Initialize GLRT (Generalized Likehood Radio Test) */
-       rt2800_init_bbp_5592_glrt(rt2x00dev);
+       rt2800_bbp_write(rt2x00dev, 105, 0x34);
+
+       rt2800_bbp_write(rt2x00dev, 106, 0x05);
+
+       rt2800_bbp_write(rt2x00dev, 120, 0x50);
+
+       rt2800_bbp_write(rt2x00dev, 137, 0x0f);
+
+       rt2800_bbp_write(rt2x00dev, 163, 0xbd);
+       /* Set ITxBF timeout to 0x9c40=1000msec */
+       rt2800_bbp_write(rt2x00dev, 179, 0x02);
+       rt2800_bbp_write(rt2x00dev, 180, 0x00);
+       rt2800_bbp_write(rt2x00dev, 182, 0x40);
+       rt2800_bbp_write(rt2x00dev, 180, 0x01);
+       rt2800_bbp_write(rt2x00dev, 182, 0x9c);
+       rt2800_bbp_write(rt2x00dev, 179, 0x00);
+       /* Reprogram the inband interface to put right values in RXWI */
+       rt2800_bbp_write(rt2x00dev, 142, 0x04);
+       rt2800_bbp_write(rt2x00dev, 143, 0x3b);
+       rt2800_bbp_write(rt2x00dev, 142, 0x06);
+       rt2800_bbp_write(rt2x00dev, 143, 0xa0);
+       rt2800_bbp_write(rt2x00dev, 142, 0x07);
+       rt2800_bbp_write(rt2x00dev, 143, 0xa1);
+       rt2800_bbp_write(rt2x00dev, 142, 0x08);
+       rt2800_bbp_write(rt2x00dev, 143, 0xa2);
+
+       rt2800_bbp_write(rt2x00dev, 148, 0xc8);
+}
 
-       rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+       rt2800_bbp_write(rt2x00dev, 66, 0x38);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
-       div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY);
-       ant = (div_mode == 3) ? 1 : 0;
-       rt2800_bbp_read(rt2x00dev, 152, &value);
-       if (ant == 0) {
-               /* Main antenna */
-               rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
-       } else {
-               /* Auxiliary antenna */
-               rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
-       }
-       rt2800_bbp_write(rt2x00dev, 152, value);
+       rt2800_bbp_write(rt2x00dev, 69, 0x12);
+       rt2800_bbp_write(rt2x00dev, 73, 0x10);
 
-       if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) {
-               rt2800_bbp_read(rt2x00dev, 254, &value);
-               rt2x00_set_field8(&value, BBP254_BIT7, 1);
-               rt2800_bbp_write(rt2x00dev, 254, value);
-       }
+       rt2800_bbp_write(rt2x00dev, 70, 0x0a);
 
-       rt2800_init_freq_calibration(rt2x00dev);
+       rt2800_bbp_write(rt2x00dev, 79, 0x13);
+       rt2800_bbp_write(rt2x00dev, 80, 0x05);
+       rt2800_bbp_write(rt2x00dev, 81, 0x33);
 
-       rt2800_bbp_write(rt2x00dev, 84, 0x19);
-       if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C))
+       rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+       rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+       rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+       rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+       rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+       rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E))
                rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+       else
+               rt2800_bbp_write(rt2x00dev, 103, 0x00);
+
+       rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+       rt2800_bbp_write(rt2x00dev, 106, 0x35);
+
+       rt2800_disable_unused_dac_adc(rt2x00dev);
 }
 
-static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
+static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev)
 {
-       unsigned int i;
-       u16 eeprom;
-       u8 reg_id;
-       u8 value;
+       rt2800_bbp_write(rt2x00dev, 31, 0x08);
 
-       if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) ||
-                    rt2800_wait_bbp_ready(rt2x00dev)))
-               return -EACCES;
+       rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+       rt2800_bbp_write(rt2x00dev, 66, 0x38);
 
-       if (rt2x00_rt(rt2x00dev, RT5592)) {
-               rt2800_init_bbp_5592(rt2x00dev);
-               return 0;
-       }
+       rt2800_bbp_write(rt2x00dev, 69, 0x12);
+       rt2800_bbp_write(rt2x00dev, 73, 0x10);
 
-       if (rt2x00_rt(rt2x00dev, RT3352)) {
-               rt2800_bbp_write(rt2x00dev, 3, 0x00);
-               rt2800_bbp_write(rt2x00dev, 4, 0x50);
-       }
+       rt2800_bbp_write(rt2x00dev, 70, 0x0a);
 
-       if (rt2x00_rt(rt2x00dev, RT3290) ||
-           rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392))
-               rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+       rt2800_bbp_write(rt2x00dev, 79, 0x13);
+       rt2800_bbp_write(rt2x00dev, 80, 0x05);
+       rt2800_bbp_write(rt2x00dev, 81, 0x33);
 
-       if (rt2800_is_305x_soc(rt2x00dev) ||
-           rt2x00_rt(rt2x00dev, RT3290) ||
-           rt2x00_rt(rt2x00dev, RT3352) ||
-           rt2x00_rt(rt2x00dev, RT3572) ||
-           rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392))
-               rt2800_bbp_write(rt2x00dev, 31, 0x08);
+       rt2800_bbp_write(rt2x00dev, 82, 0x62);
 
-       if (rt2x00_rt(rt2x00dev, RT3352))
-               rt2800_bbp_write(rt2x00dev, 47, 0x48);
+       rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+       rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+       rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+       rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+       rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+       rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
+       rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+       rt2800_bbp_write(rt2x00dev, 106, 0x35);
+
+       rt2800_disable_unused_dac_adc(rt2x00dev);
+}
+
+static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
+{
+       int ant, div_mode;
+       u16 eeprom;
+       u8 value;
+
+       rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+       rt2800_bbp_write(rt2x00dev, 31, 0x08);
 
        rt2800_bbp_write(rt2x00dev, 65, 0x2c);
        rt2800_bbp_write(rt2x00dev, 66, 0x38);
 
-       if (rt2x00_rt(rt2x00dev, RT3290) ||
-           rt2x00_rt(rt2x00dev, RT3352) ||
-           rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392))
-               rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+       rt2800_bbp_write(rt2x00dev, 68, 0x0b);
 
-       if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) {
-               rt2800_bbp_write(rt2x00dev, 69, 0x16);
-               rt2800_bbp_write(rt2x00dev, 73, 0x12);
-       } else if (rt2x00_rt(rt2x00dev, RT3290) ||
-                  rt2x00_rt(rt2x00dev, RT3352) ||
-                  rt2x00_rt(rt2x00dev, RT5390) ||
-                  rt2x00_rt(rt2x00dev, RT5392)) {
-               rt2800_bbp_write(rt2x00dev, 69, 0x12);
-               rt2800_bbp_write(rt2x00dev, 73, 0x13);
-               rt2800_bbp_write(rt2x00dev, 75, 0x46);
-               rt2800_bbp_write(rt2x00dev, 76, 0x28);
+       rt2800_bbp_write(rt2x00dev, 69, 0x12);
+       rt2800_bbp_write(rt2x00dev, 73, 0x13);
+       rt2800_bbp_write(rt2x00dev, 75, 0x46);
+       rt2800_bbp_write(rt2x00dev, 76, 0x28);
 
-               if (rt2x00_rt(rt2x00dev, RT3290))
-                       rt2800_bbp_write(rt2x00dev, 77, 0x58);
-               else
-                       rt2800_bbp_write(rt2x00dev, 77, 0x59);
-       } else {
-               rt2800_bbp_write(rt2x00dev, 69, 0x12);
-               rt2800_bbp_write(rt2x00dev, 73, 0x10);
-       }
+       rt2800_bbp_write(rt2x00dev, 77, 0x59);
 
        rt2800_bbp_write(rt2x00dev, 70, 0x0a);
 
-       if (rt2x00_rt(rt2x00dev, RT3070) ||
-           rt2x00_rt(rt2x00dev, RT3071) ||
-           rt2x00_rt(rt2x00dev, RT3090) ||
-           rt2x00_rt(rt2x00dev, RT3390) ||
-           rt2x00_rt(rt2x00dev, RT3572) ||
-           rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392)) {
-               rt2800_bbp_write(rt2x00dev, 79, 0x13);
-               rt2800_bbp_write(rt2x00dev, 80, 0x05);
-               rt2800_bbp_write(rt2x00dev, 81, 0x33);
-       } else if (rt2800_is_305x_soc(rt2x00dev)) {
-               rt2800_bbp_write(rt2x00dev, 78, 0x0e);
-               rt2800_bbp_write(rt2x00dev, 80, 0x08);
-       } else if (rt2x00_rt(rt2x00dev, RT3290)) {
-               rt2800_bbp_write(rt2x00dev, 74, 0x0b);
-               rt2800_bbp_write(rt2x00dev, 79, 0x18);
-               rt2800_bbp_write(rt2x00dev, 80, 0x09);
-               rt2800_bbp_write(rt2x00dev, 81, 0x33);
-       } else if (rt2x00_rt(rt2x00dev, RT3352)) {
-               rt2800_bbp_write(rt2x00dev, 78, 0x0e);
-               rt2800_bbp_write(rt2x00dev, 80, 0x08);
-               rt2800_bbp_write(rt2x00dev, 81, 0x37);
-       } else {
-               rt2800_bbp_write(rt2x00dev, 81, 0x37);
-       }
+       rt2800_bbp_write(rt2x00dev, 79, 0x13);
+       rt2800_bbp_write(rt2x00dev, 80, 0x05);
+       rt2800_bbp_write(rt2x00dev, 81, 0x33);
 
        rt2800_bbp_write(rt2x00dev, 82, 0x62);
-       if (rt2x00_rt(rt2x00dev, RT3290) ||
-           rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392))
-               rt2800_bbp_write(rt2x00dev, 83, 0x7a);
-       else
-               rt2800_bbp_write(rt2x00dev, 83, 0x6a);
 
-       if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D))
-               rt2800_bbp_write(rt2x00dev, 84, 0x19);
-       else if (rt2x00_rt(rt2x00dev, RT3290) ||
-                rt2x00_rt(rt2x00dev, RT5390) ||
-                rt2x00_rt(rt2x00dev, RT5392))
-               rt2800_bbp_write(rt2x00dev, 84, 0x9a);
-       else
-               rt2800_bbp_write(rt2x00dev, 84, 0x99);
+       rt2800_bbp_write(rt2x00dev, 83, 0x7a);
 
-       if (rt2x00_rt(rt2x00dev, RT3290) ||
-           rt2x00_rt(rt2x00dev, RT3352) ||
-           rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392))
-               rt2800_bbp_write(rt2x00dev, 86, 0x38);
-       else
-               rt2800_bbp_write(rt2x00dev, 86, 0x00);
+       rt2800_bbp_write(rt2x00dev, 84, 0x9a);
 
-       if (rt2x00_rt(rt2x00dev, RT3352) ||
-           rt2x00_rt(rt2x00dev, RT5392))
+       rt2800_bbp_write(rt2x00dev, 86, 0x38);
+
+       if (rt2x00_rt(rt2x00dev, RT5392))
                rt2800_bbp_write(rt2x00dev, 88, 0x90);
 
        rt2800_bbp_write(rt2x00dev, 91, 0x04);
 
-       if (rt2x00_rt(rt2x00dev, RT3290) ||
-           rt2x00_rt(rt2x00dev, RT3352) ||
-           rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392))
-               rt2800_bbp_write(rt2x00dev, 92, 0x02);
-       else
-               rt2800_bbp_write(rt2x00dev, 92, 0x00);
+       rt2800_bbp_write(rt2x00dev, 92, 0x02);
 
        if (rt2x00_rt(rt2x00dev, RT5392)) {
                rt2800_bbp_write(rt2x00dev, 95, 0x9a);
                rt2800_bbp_write(rt2x00dev, 98, 0x12);
        }
 
-       if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) ||
-           rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) ||
-           rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E) ||
-           rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E) ||
-           rt2x00_rt(rt2x00dev, RT3290) ||
-           rt2x00_rt(rt2x00dev, RT3352) ||
-           rt2x00_rt(rt2x00dev, RT3572) ||
-           rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392) ||
-           rt2800_is_305x_soc(rt2x00dev))
-               rt2800_bbp_write(rt2x00dev, 103, 0xc0);
-       else
-               rt2800_bbp_write(rt2x00dev, 103, 0x00);
+       rt2800_bbp_write(rt2x00dev, 103, 0xc0);
 
-       if (rt2x00_rt(rt2x00dev, RT3290) ||
-           rt2x00_rt(rt2x00dev, RT3352) ||
-           rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392))
-               rt2800_bbp_write(rt2x00dev, 104, 0x92);
+       rt2800_bbp_write(rt2x00dev, 104, 0x92);
 
-       if (rt2800_is_305x_soc(rt2x00dev))
-               rt2800_bbp_write(rt2x00dev, 105, 0x01);
-       else if (rt2x00_rt(rt2x00dev, RT3290))
-               rt2800_bbp_write(rt2x00dev, 105, 0x1c);
-       else if (rt2x00_rt(rt2x00dev, RT3352))
-               rt2800_bbp_write(rt2x00dev, 105, 0x34);
-       else if (rt2x00_rt(rt2x00dev, RT5390) ||
-                rt2x00_rt(rt2x00dev, RT5392))
-               rt2800_bbp_write(rt2x00dev, 105, 0x3c);
-       else
-               rt2800_bbp_write(rt2x00dev, 105, 0x05);
+       rt2800_bbp_write(rt2x00dev, 105, 0x3c);
 
-       if (rt2x00_rt(rt2x00dev, RT3290) ||
-           rt2x00_rt(rt2x00dev, RT5390))
+       if (rt2x00_rt(rt2x00dev, RT5390))
                rt2800_bbp_write(rt2x00dev, 106, 0x03);
-       else if (rt2x00_rt(rt2x00dev, RT3352))
-               rt2800_bbp_write(rt2x00dev, 106, 0x05);
        else if (rt2x00_rt(rt2x00dev, RT5392))
                rt2800_bbp_write(rt2x00dev, 106, 0x12);
        else
-               rt2800_bbp_write(rt2x00dev, 106, 0x35);
-
-       if (rt2x00_rt(rt2x00dev, RT3352))
-               rt2800_bbp_write(rt2x00dev, 120, 0x50);
+               WARN_ON(1);
 
-       if (rt2x00_rt(rt2x00dev, RT3290) ||
-           rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392))
-               rt2800_bbp_write(rt2x00dev, 128, 0x12);
+       rt2800_bbp_write(rt2x00dev, 128, 0x12);
 
        if (rt2x00_rt(rt2x00dev, RT5392)) {
                rt2800_bbp_write(rt2x00dev, 134, 0xd0);
                rt2800_bbp_write(rt2x00dev, 135, 0xf6);
        }
 
-       if (rt2x00_rt(rt2x00dev, RT3352))
-               rt2800_bbp_write(rt2x00dev, 137, 0x0f);
+       rt2800_disable_unused_dac_adc(rt2x00dev);
 
-       if (rt2x00_rt(rt2x00dev, RT3071) ||
-           rt2x00_rt(rt2x00dev, RT3090) ||
-           rt2x00_rt(rt2x00dev, RT3390) ||
-           rt2x00_rt(rt2x00dev, RT3572) ||
-           rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392)) {
-               rt2800_bbp_read(rt2x00dev, 138, &value);
+       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+       div_mode = rt2x00_get_field16(eeprom,
+                                     EEPROM_NIC_CONF1_ANT_DIVERSITY);
+       ant = (div_mode == 3) ? 1 : 0;
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
-               if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
-                       value |= 0x20;
-               if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
-                       value &= ~0x02;
+       /* check if this is a Bluetooth combo card */
+       if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+               u32 reg;
 
-               rt2800_bbp_write(rt2x00dev, 138, value);
+               rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
+               rt2x00_set_field32(&reg, GPIO_CTRL_DIR3, 0);
+               rt2x00_set_field32(&reg, GPIO_CTRL_DIR6, 0);
+               rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 0);
+               rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 0);
+               if (ant == 0)
+                       rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 1);
+               else if (ant == 1)
+                       rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 1);
+               rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
        }
 
-       if (rt2x00_rt(rt2x00dev, RT3290)) {
-               rt2800_bbp_write(rt2x00dev, 67, 0x24);
-               rt2800_bbp_write(rt2x00dev, 143, 0x04);
-               rt2800_bbp_write(rt2x00dev, 142, 0x99);
-               rt2800_bbp_write(rt2x00dev, 150, 0x30);
-               rt2800_bbp_write(rt2x00dev, 151, 0x2e);
-               rt2800_bbp_write(rt2x00dev, 152, 0x20);
-               rt2800_bbp_write(rt2x00dev, 153, 0x34);
-               rt2800_bbp_write(rt2x00dev, 154, 0x40);
-               rt2800_bbp_write(rt2x00dev, 155, 0x3b);
-               rt2800_bbp_write(rt2x00dev, 253, 0x04);
-
-               rt2800_bbp_read(rt2x00dev, 47, &value);
-               rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1);
-               rt2800_bbp_write(rt2x00dev, 47, value);
-
-               /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */
-               rt2800_bbp_read(rt2x00dev, 3, &value);
-               rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1);
-               rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1);
-               rt2800_bbp_write(rt2x00dev, 3, value);
+       /* This chip has hardware antenna diversity*/
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) {
+               rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */
+               rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */
+               rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */
        }
 
-       if (rt2x00_rt(rt2x00dev, RT3352)) {
-               rt2800_bbp_write(rt2x00dev, 163, 0xbd);
-               /* Set ITxBF timeout to 0x9c40=1000msec */
-               rt2800_bbp_write(rt2x00dev, 179, 0x02);
-               rt2800_bbp_write(rt2x00dev, 180, 0x00);
-               rt2800_bbp_write(rt2x00dev, 182, 0x40);
-               rt2800_bbp_write(rt2x00dev, 180, 0x01);
-               rt2800_bbp_write(rt2x00dev, 182, 0x9c);
-               rt2800_bbp_write(rt2x00dev, 179, 0x00);
-               /* Reprogram the inband interface to put right values in RXWI */
-               rt2800_bbp_write(rt2x00dev, 142, 0x04);
-               rt2800_bbp_write(rt2x00dev, 143, 0x3b);
-               rt2800_bbp_write(rt2x00dev, 142, 0x06);
-               rt2800_bbp_write(rt2x00dev, 143, 0xa0);
-               rt2800_bbp_write(rt2x00dev, 142, 0x07);
-               rt2800_bbp_write(rt2x00dev, 143, 0xa1);
-               rt2800_bbp_write(rt2x00dev, 142, 0x08);
-               rt2800_bbp_write(rt2x00dev, 143, 0xa2);
-
-               rt2800_bbp_write(rt2x00dev, 148, 0xc8);
+       rt2800_bbp_read(rt2x00dev, 152, &value);
+       if (ant == 0)
+               rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
+       else
+               rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
+       rt2800_bbp_write(rt2x00dev, 152, value);
+
+       rt2800_init_freq_calibration(rt2x00dev);
+}
+
+static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev)
+{
+       int ant, div_mode;
+       u16 eeprom;
+       u8 value;
+
+       rt2800_init_bbp_early(rt2x00dev);
+
+       rt2800_bbp_read(rt2x00dev, 105, &value);
+       rt2x00_set_field8(&value, BBP105_MLD,
+                         rt2x00dev->default_ant.rx_chain_num == 2);
+       rt2800_bbp_write(rt2x00dev, 105, value);
+
+       rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+       rt2800_bbp_write(rt2x00dev, 20, 0x06);
+       rt2800_bbp_write(rt2x00dev, 31, 0x08);
+       rt2800_bbp_write(rt2x00dev, 65, 0x2C);
+       rt2800_bbp_write(rt2x00dev, 68, 0xDD);
+       rt2800_bbp_write(rt2x00dev, 69, 0x1A);
+       rt2800_bbp_write(rt2x00dev, 70, 0x05);
+       rt2800_bbp_write(rt2x00dev, 73, 0x13);
+       rt2800_bbp_write(rt2x00dev, 74, 0x0F);
+       rt2800_bbp_write(rt2x00dev, 75, 0x4F);
+       rt2800_bbp_write(rt2x00dev, 76, 0x28);
+       rt2800_bbp_write(rt2x00dev, 77, 0x59);
+       rt2800_bbp_write(rt2x00dev, 84, 0x9A);
+       rt2800_bbp_write(rt2x00dev, 86, 0x38);
+       rt2800_bbp_write(rt2x00dev, 88, 0x90);
+       rt2800_bbp_write(rt2x00dev, 91, 0x04);
+       rt2800_bbp_write(rt2x00dev, 92, 0x02);
+       rt2800_bbp_write(rt2x00dev, 95, 0x9a);
+       rt2800_bbp_write(rt2x00dev, 98, 0x12);
+       rt2800_bbp_write(rt2x00dev, 103, 0xC0);
+       rt2800_bbp_write(rt2x00dev, 104, 0x92);
+       /* FIXME BBP105 owerwrite */
+       rt2800_bbp_write(rt2x00dev, 105, 0x3C);
+       rt2800_bbp_write(rt2x00dev, 106, 0x35);
+       rt2800_bbp_write(rt2x00dev, 128, 0x12);
+       rt2800_bbp_write(rt2x00dev, 134, 0xD0);
+       rt2800_bbp_write(rt2x00dev, 135, 0xF6);
+       rt2800_bbp_write(rt2x00dev, 137, 0x0F);
+
+       /* Initialize GLRT (Generalized Likehood Radio Test) */
+       rt2800_init_bbp_5592_glrt(rt2x00dev);
+
+       rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+       div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY);
+       ant = (div_mode == 3) ? 1 : 0;
+       rt2800_bbp_read(rt2x00dev, 152, &value);
+       if (ant == 0) {
+               /* Main antenna */
+               rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
+       } else {
+               /* Auxiliary antenna */
+               rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
        }
+       rt2800_bbp_write(rt2x00dev, 152, value);
 
-       if (rt2x00_rt(rt2x00dev, RT5390) ||
-           rt2x00_rt(rt2x00dev, RT5392)) {
-               int ant, div_mode;
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) {
+               rt2800_bbp_read(rt2x00dev, 254, &value);
+               rt2x00_set_field8(&value, BBP254_BIT7, 1);
+               rt2800_bbp_write(rt2x00dev, 254, value);
+       }
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
-               div_mode = rt2x00_get_field16(eeprom,
-                                             EEPROM_NIC_CONF1_ANT_DIVERSITY);
-               ant = (div_mode == 3) ? 1 : 0;
+       rt2800_init_freq_calibration(rt2x00dev);
 
-               /* check if this is a Bluetooth combo card */
-               if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
-                       u32 reg;
-
-                       rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
-                       rt2x00_set_field32(&reg, GPIO_CTRL_DIR3, 0);
-                       rt2x00_set_field32(&reg, GPIO_CTRL_DIR6, 0);
-                       rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 0);
-                       rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 0);
-                       if (ant == 0)
-                               rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 1);
-                       else if (ant == 1)
-                               rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 1);
-                       rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
-               }
+       rt2800_bbp_write(rt2x00dev, 84, 0x19);
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C))
+               rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+}
 
-               /* This chip has hardware antenna diversity*/
-               if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) {
-                       rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */
-                       rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */
-                       rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */
-               }
+static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
+{
+       unsigned int i;
+       u16 eeprom;
+       u8 reg_id;
+       u8 value;
 
-               rt2800_bbp_read(rt2x00dev, 152, &value);
-               if (ant == 0)
-                       rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
-               else
-                       rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
-               rt2800_bbp_write(rt2x00dev, 152, value);
+       if (rt2800_is_305x_soc(rt2x00dev))
+               rt2800_init_bbp_305x_soc(rt2x00dev);
 
-               rt2800_init_freq_calibration(rt2x00dev);
+       switch (rt2x00dev->chip.rt) {
+       case RT2860:
+       case RT2872:
+       case RT2883:
+               rt2800_init_bbp_28xx(rt2x00dev);
+               break;
+       case RT3070:
+       case RT3071:
+       case RT3090:
+               rt2800_init_bbp_30xx(rt2x00dev);
+               break;
+       case RT3290:
+               rt2800_init_bbp_3290(rt2x00dev);
+               break;
+       case RT3352:
+               rt2800_init_bbp_3352(rt2x00dev);
+               break;
+       case RT3390:
+               rt2800_init_bbp_3390(rt2x00dev);
+               break;
+       case RT3572:
+               rt2800_init_bbp_3572(rt2x00dev);
+               break;
+       case RT5390:
+       case RT5392:
+               rt2800_init_bbp_53xx(rt2x00dev);
+               break;
+       case RT5592:
+               rt2800_init_bbp_5592(rt2x00dev);
+               return;
        }
 
        for (i = 0; i < EEPROM_BBP_SIZE; i++) {
@@ -4344,8 +4565,6 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
                        rt2800_bbp_write(rt2x00dev, reg_id, value);
                }
        }
-
-       return 0;
 }
 
 static void rt2800_led_open_drain_enable(struct rt2x00_dev *rt2x00dev)
@@ -5196,9 +5415,11 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
        }
        msleep(1);
 
-       if (unlikely(rt2800_init_bbp(rt2x00dev)))
+       if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) ||
+                    rt2800_wait_bbp_ready(rt2x00dev)))
                return -EIO;
 
+       rt2800_init_bbp(rt2x00dev);
        rt2800_init_rfcsr(rt2x00dev);
 
        if (rt2x00_is_usb(rt2x00dev) &&
@@ -6056,8 +6277,8 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
                default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2);
 
                for (i = 14; i < spec->num_channels; i++) {
-                       info[i].default_power1 = default_power1[i];
-                       info[i].default_power2 = default_power2[i];
+                       info[i].default_power1 = default_power1[i - 14];
+                       info[i].default_power2 = default_power2[i - 14];
                }
        }
 
index 6f4a861..0005562 100644 (file)
@@ -637,6 +637,7 @@ static void rt2800pci_write_tx_desc(struct queue_entry *entry,
        struct queue_entry_priv_mmio *entry_priv = entry->priv_data;
        __le32 *txd = entry_priv->desc;
        u32 word;
+       const unsigned int txwi_size = entry->queue->winfo_size;
 
        /*
         * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1
@@ -659,14 +660,14 @@ static void rt2800pci_write_tx_desc(struct queue_entry *entry,
                           !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
        rt2x00_set_field32(&word, TXD_W1_BURST,
                           test_bit(ENTRY_TXD_BURST, &txdesc->flags));
-       rt2x00_set_field32(&word, TXD_W1_SD_LEN0, TXWI_DESC_SIZE);
+       rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size);
        rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0);
        rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0);
        rt2x00_desc_write(txd, 1, word);
 
        word = 0;
        rt2x00_set_field32(&word, TXD_W2_SD_PTR1,
-                          skbdesc->skb_dma + TXWI_DESC_SIZE);
+                          skbdesc->skb_dma + txwi_size);
        rt2x00_desc_write(txd, 2, word);
 
        word = 0;
@@ -1014,7 +1015,7 @@ static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
         * Since we have only one producer and one consumer we don't
         * need to lock the kfifo.
         */
-       for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) {
+       for (i = 0; i < rt2x00dev->tx->limit; i++) {
                rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
 
                if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
@@ -1186,29 +1187,43 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
        .sta_remove             = rt2800_sta_remove,
 };
 
-static const struct data_queue_desc rt2800pci_queue_rx = {
-       .entry_num              = 128,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .winfo_size             = RXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt2800pci_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 128;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->winfo_size = RXWI_DESC_SIZE_4WORDS;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2800pci_queue_tx = {
-       .entry_num              = 64,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 64;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->winfo_size = TXWI_DESC_SIZE_4WORDS;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2800pci_queue_bcn = {
-       .entry_num              = 8,
-       .data_size              = 0, /* No DMA required for beacons */
-       .desc_size              = TXD_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 8;
+               queue->data_size = 0; /* No DMA required for beacons */
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->winfo_size = TXWI_DESC_SIZE_4WORDS;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2800pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -1217,10 +1232,7 @@ static const struct rt2x00_ops rt2800pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXWI_DESC_SIZE,
-       .rx                     = &rt2800pci_queue_rx,
-       .tx                     = &rt2800pci_queue_tx,
-       .bcn                    = &rt2800pci_queue_bcn,
+       .queue_init             = rt2800pci_queue_init,
        .lib                    = &rt2800pci_rt2x00_ops,
        .drv                    = &rt2800pci_rt2800_ops,
        .hw                     = &rt2800pci_mac80211_ops,
index ac854d7..840833b 100644 (file)
@@ -327,7 +327,7 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev)
         * this limit so reduce the number to prevent errors.
         */
        rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_LIMIT,
-                          ((rt2x00dev->ops->rx->entry_num * DATA_FRAME_SIZE)
+                          ((rt2x00dev->rx->limit * DATA_FRAME_SIZE)
                            / 1024) - 3);
        rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_EN, 1);
        rt2x00_set_field32(&reg, USB_DMA_CFG_TX_BULK_EN, 1);
@@ -849,85 +849,63 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
        .sta_remove             = rt2800_sta_remove,
 };
 
-static const struct data_queue_desc rt2800usb_queue_rx = {
-       .entry_num              = 128,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = RXINFO_DESC_SIZE,
-       .winfo_size             = RXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
-
-static const struct data_queue_desc rt2800usb_queue_tx = {
-       .entry_num              = 16,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
-
-static const struct data_queue_desc rt2800usb_queue_bcn = {
-       .entry_num              = 8,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+static void rt2800usb_queue_init(struct data_queue *queue)
+{
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       unsigned short txwi_size, rxwi_size;
 
-static const struct rt2x00_ops rt2800usb_ops = {
-       .name                   = KBUILD_MODNAME,
-       .drv_data_size          = sizeof(struct rt2800_drv_data),
-       .max_ap_intf            = 8,
-       .eeprom_size            = EEPROM_SIZE,
-       .rf_size                = RF_SIZE,
-       .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXINFO_DESC_SIZE + TXWI_DESC_SIZE,
-       .rx                     = &rt2800usb_queue_rx,
-       .tx                     = &rt2800usb_queue_tx,
-       .bcn                    = &rt2800usb_queue_bcn,
-       .lib                    = &rt2800usb_rt2x00_ops,
-       .drv                    = &rt2800usb_rt2800_ops,
-       .hw                     = &rt2800usb_mac80211_ops,
-#ifdef CONFIG_RT2X00_LIB_DEBUGFS
-       .debugfs                = &rt2800_rt2x00debug,
-#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
-};
+       if (rt2x00_rt(rt2x00dev, RT5592)) {
+               txwi_size = TXWI_DESC_SIZE_5WORDS;
+               rxwi_size = RXWI_DESC_SIZE_6WORDS;
+       } else {
+               txwi_size = TXWI_DESC_SIZE_4WORDS;
+               rxwi_size = RXWI_DESC_SIZE_4WORDS;
+       }
 
-static const struct data_queue_desc rt2800usb_queue_rx_5592 = {
-       .entry_num              = 128,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = RXINFO_DESC_SIZE,
-       .winfo_size             = RXWI_DESC_SIZE_5592,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 128;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = RXINFO_DESC_SIZE;
+               queue->winfo_size = rxwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2800usb_queue_tx_5592 = {
-       .entry_num              = 16,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE_5592,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 16;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = TXINFO_DESC_SIZE;
+               queue->winfo_size = txwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2800usb_queue_bcn_5592 = {
-       .entry_num              = 8,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE_5592,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_BEACON:
+               queue->limit = 8;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXINFO_DESC_SIZE;
+               queue->winfo_size = txwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
-static const struct rt2x00_ops rt2800usb_ops_5592 = {
+static const struct rt2x00_ops rt2800usb_ops = {
        .name                   = KBUILD_MODNAME,
        .drv_data_size          = sizeof(struct rt2800_drv_data),
        .max_ap_intf            = 8,
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXINFO_DESC_SIZE + TXWI_DESC_SIZE_5592,
-       .rx                     = &rt2800usb_queue_rx_5592,
-       .tx                     = &rt2800usb_queue_tx_5592,
-       .bcn                    = &rt2800usb_queue_bcn_5592,
+       .queue_init             = rt2800usb_queue_init,
        .lib                    = &rt2800usb_rt2x00_ops,
        .drv                    = &rt2800usb_rt2800_ops,
        .hw                     = &rt2800usb_mac80211_ops,
@@ -1248,15 +1226,15 @@ static struct usb_device_id rt2800usb_device_table[] = {
 #endif
 #ifdef CONFIG_RT2800USB_RT55XX
        /* Arcadyan */
-       { USB_DEVICE(0x043e, 0x7a32), .driver_info = 5592 },
+       { USB_DEVICE(0x043e, 0x7a32) },
        /* AVM GmbH */
-       { USB_DEVICE(0x057c, 0x8501), .driver_info = 5592 },
+       { USB_DEVICE(0x057c, 0x8501) },
        /* D-Link DWA-160-B2 */
-       { USB_DEVICE(0x2001, 0x3c1a), .driver_info = 5592 },
+       { USB_DEVICE(0x2001, 0x3c1a) },
        /* Proware */
-       { USB_DEVICE(0x043e, 0x7a13), .driver_info = 5592 },
+       { USB_DEVICE(0x043e, 0x7a13) },
        /* Ralink */
-       { USB_DEVICE(0x148f, 0x5572), .driver_info = 5592 },
+       { USB_DEVICE(0x148f, 0x5572) },
 #endif
 #ifdef CONFIG_RT2800USB_UNKNOWN
        /*
@@ -1361,9 +1339,6 @@ MODULE_LICENSE("GPL");
 static int rt2800usb_probe(struct usb_interface *usb_intf,
                           const struct usb_device_id *id)
 {
-       if (id->driver_info == 5592)
-               return rt2x00usb_probe(usb_intf, &rt2800usb_ops_5592);
-
        return rt2x00usb_probe(usb_intf, &rt2800usb_ops);
 }
 
index 7510723..ee3fc57 100644 (file)
@@ -648,11 +648,7 @@ struct rt2x00_ops {
        const unsigned int eeprom_size;
        const unsigned int rf_size;
        const unsigned int tx_queues;
-       const unsigned int extra_tx_headroom;
-       const struct data_queue_desc *rx;
-       const struct data_queue_desc *tx;
-       const struct data_queue_desc *bcn;
-       const struct data_queue_desc *atim;
+       void (*queue_init)(struct data_queue *queue);
        const struct rt2x00lib_ops *lib;
        const void *drv;
        const struct ieee80211_ops *hw;
@@ -1010,6 +1006,9 @@ struct rt2x00_dev {
         */
        struct list_head bar_list;
        spinlock_t bar_list_lock;
+
+       /* Extra TX headroom required for alignment purposes. */
+       unsigned int extra_tx_headroom;
 };
 
 struct rt2x00_bar_list_entry {
index c8b9ef0..b16521e 100644 (file)
@@ -334,7 +334,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
        /*
         * Remove the extra tx headroom from the skb.
         */
-       skb_pull(entry->skb, rt2x00dev->ops->extra_tx_headroom);
+       skb_pull(entry->skb, rt2x00dev->extra_tx_headroom);
 
        /*
         * Signal that the TX descriptor is no longer in the skb.
@@ -1049,7 +1049,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
         */
        rt2x00dev->hw->extra_tx_headroom =
                max_t(unsigned int, IEEE80211_TX_STATUS_HEADROOM,
-                     rt2x00dev->ops->extra_tx_headroom);
+                     rt2x00dev->extra_tx_headroom);
 
        /*
         * Take TX headroom required for alignment into account.
@@ -1077,7 +1077,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
                 */
                int kfifo_size =
                        roundup_pow_of_two(rt2x00dev->ops->tx_queues *
-                                          rt2x00dev->ops->tx->entry_num *
+                                          rt2x00dev->tx->limit *
                                           sizeof(u32));
 
                status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size,
@@ -1256,6 +1256,17 @@ static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev)
        rt2x00dev->hw->wiphy->n_iface_combinations = 1;
 }
 
+static unsigned int rt2x00dev_extra_tx_headroom(struct rt2x00_dev *rt2x00dev)
+{
+       if (WARN_ON(!rt2x00dev->tx))
+               return 0;
+
+       if (rt2x00_is_usb(rt2x00dev))
+               return rt2x00dev->tx[0].winfo_size + rt2x00dev->tx[0].desc_size;
+
+       return rt2x00dev->tx[0].winfo_size;
+}
+
 /*
  * driver allocation handlers.
  */
@@ -1300,23 +1311,6 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
        rt2x00dev->hw->wiphy->addr_mask[ETH_ALEN - 1] =
                (rt2x00dev->ops->max_ap_intf - 1);
 
-       /*
-        * Determine which operating modes are supported, all modes
-        * which require beaconing, depend on the availability of
-        * beacon entries.
-        */
-       rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
-       if (rt2x00dev->ops->bcn->entry_num > 0)
-               rt2x00dev->hw->wiphy->interface_modes |=
-                   BIT(NL80211_IFTYPE_ADHOC) |
-                   BIT(NL80211_IFTYPE_AP) |
-#ifdef CONFIG_MAC80211_MESH
-                   BIT(NL80211_IFTYPE_MESH_POINT) |
-#endif
-                   BIT(NL80211_IFTYPE_WDS);
-
-       rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
-
        /*
         * Initialize work.
         */
@@ -1347,6 +1341,26 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
        if (retval)
                goto exit;
 
+       /* Cache TX headroom value */
+       rt2x00dev->extra_tx_headroom = rt2x00dev_extra_tx_headroom(rt2x00dev);
+
+       /*
+        * Determine which operating modes are supported, all modes
+        * which require beaconing, depend on the availability of
+        * beacon entries.
+        */
+       rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+       if (rt2x00dev->bcn->limit > 0)
+               rt2x00dev->hw->wiphy->interface_modes |=
+                   BIT(NL80211_IFTYPE_ADHOC) |
+                   BIT(NL80211_IFTYPE_AP) |
+#ifdef CONFIG_MAC80211_MESH
+                   BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+                   BIT(NL80211_IFTYPE_WDS);
+
+       rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+
        /*
         * Initialize ieee80211 structure.
         */
index dc49e52..76d95de 100644 (file)
@@ -105,11 +105,13 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
                goto exit_release_regions;
        }
 
+       pci_enable_msi(pci_dev);
+
        hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
        if (!hw) {
                rt2x00_probe_err("Failed to allocate hardware\n");
                retval = -ENOMEM;
-               goto exit_release_regions;
+               goto exit_disable_msi;
        }
 
        pci_set_drvdata(pci_dev, hw);
@@ -150,6 +152,9 @@ exit_free_reg:
 exit_free_device:
        ieee80211_free_hw(hw);
 
+exit_disable_msi:
+       pci_disable_msi(pci_dev);
+
 exit_release_regions:
        pci_release_regions(pci_dev);
 
@@ -174,6 +179,8 @@ void rt2x00pci_remove(struct pci_dev *pci_dev)
        rt2x00pci_free_reg(rt2x00dev);
        ieee80211_free_hw(hw);
 
+       pci_disable_msi(pci_dev);
+
        /*
         * Free the PCI device data.
         */
index 2c12311..6c0a91f 100644 (file)
@@ -542,8 +542,8 @@ static int rt2x00queue_write_tx_data(struct queue_entry *entry,
        /*
         * Add the requested extra tx headroom in front of the skb.
         */
-       skb_push(entry->skb, rt2x00dev->ops->extra_tx_headroom);
-       memset(entry->skb->data, 0, rt2x00dev->ops->extra_tx_headroom);
+       skb_push(entry->skb, rt2x00dev->extra_tx_headroom);
+       memset(entry->skb->data, 0, rt2x00dev->extra_tx_headroom);
 
        /*
         * Call the driver's write_tx_data function, if it exists.
@@ -596,7 +596,7 @@ static void rt2x00queue_bar_check(struct queue_entry *entry)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
        struct ieee80211_bar *bar = (void *) (entry->skb->data +
-                                   rt2x00dev->ops->extra_tx_headroom);
+                                   rt2x00dev->extra_tx_headroom);
        struct rt2x00_bar_list_entry *bar_entry;
 
        if (likely(!ieee80211_is_back_req(bar->frame_control)))
@@ -1161,8 +1161,7 @@ void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev)
        }
 }
 
-static int rt2x00queue_alloc_entries(struct data_queue *queue,
-                                    const struct data_queue_desc *qdesc)
+static int rt2x00queue_alloc_entries(struct data_queue *queue)
 {
        struct queue_entry *entries;
        unsigned int entry_size;
@@ -1170,16 +1169,10 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue,
 
        rt2x00queue_reset(queue);
 
-       queue->limit = qdesc->entry_num;
-       queue->threshold = DIV_ROUND_UP(qdesc->entry_num, 10);
-       queue->data_size = qdesc->data_size;
-       queue->desc_size = qdesc->desc_size;
-       queue->winfo_size = qdesc->winfo_size;
-
        /*
         * Allocate all queue entries.
         */
-       entry_size = sizeof(*entries) + qdesc->priv_size;
+       entry_size = sizeof(*entries) + queue->priv_size;
        entries = kcalloc(queue->limit, entry_size, GFP_KERNEL);
        if (!entries)
                return -ENOMEM;
@@ -1195,7 +1188,7 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue,
                entries[i].entry_idx = i;
                entries[i].priv_data =
                    QUEUE_ENTRY_PRIV_OFFSET(entries, i, queue->limit,
-                                           sizeof(*entries), qdesc->priv_size);
+                                           sizeof(*entries), queue->priv_size);
        }
 
 #undef QUEUE_ENTRY_PRIV_OFFSET
@@ -1237,23 +1230,22 @@ int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev)
        struct data_queue *queue;
        int status;
 
-       status = rt2x00queue_alloc_entries(rt2x00dev->rx, rt2x00dev->ops->rx);
+       status = rt2x00queue_alloc_entries(rt2x00dev->rx);
        if (status)
                goto exit;
 
        tx_queue_for_each(rt2x00dev, queue) {
-               status = rt2x00queue_alloc_entries(queue, rt2x00dev->ops->tx);
+               status = rt2x00queue_alloc_entries(queue);
                if (status)
                        goto exit;
        }
 
-       status = rt2x00queue_alloc_entries(rt2x00dev->bcn, rt2x00dev->ops->bcn);
+       status = rt2x00queue_alloc_entries(rt2x00dev->bcn);
        if (status)
                goto exit;
 
        if (test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) {
-               status = rt2x00queue_alloc_entries(rt2x00dev->atim,
-                                                  rt2x00dev->ops->atim);
+               status = rt2x00queue_alloc_entries(rt2x00dev->atim);
                if (status)
                        goto exit;
        }
@@ -1297,6 +1289,10 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev,
        queue->aifs = 2;
        queue->cw_min = 5;
        queue->cw_max = 10;
+
+       rt2x00dev->ops->queue_init(queue);
+
+       queue->threshold = DIV_ROUND_UP(queue->limit, 10);
 }
 
 int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev)
index 4a7b34e..ebe1172 100644 (file)
@@ -453,6 +453,7 @@ enum data_queue_flags {
  * @cw_max: The cw max value for outgoing frames (field ignored in RX queue).
  * @data_size: Maximum data size for the frames in this queue.
  * @desc_size: Hardware descriptor size for the data in this queue.
+ * @priv_size: Size of per-queue_entry private data.
  * @usb_endpoint: Device endpoint used for communication (USB only)
  * @usb_maxpacket: Max packet size for given endpoint (USB only)
  */
@@ -481,30 +482,12 @@ struct data_queue {
        unsigned short data_size;
        unsigned char  desc_size;
        unsigned char  winfo_size;
+       unsigned short priv_size;
 
        unsigned short usb_endpoint;
        unsigned short usb_maxpacket;
 };
 
-/**
- * struct data_queue_desc: Data queue description
- *
- * The information in this structure is used by drivers
- * to inform rt2x00lib about the creation of the data queue.
- *
- * @entry_num: Maximum number of entries for a queue.
- * @data_size: Maximum data size for the frames in this queue.
- * @desc_size: Hardware descriptor size for the data in this queue.
- * @priv_size: Size of per-queue_entry private data.
- */
-struct data_queue_desc {
-       unsigned short entry_num;
-       unsigned short data_size;
-       unsigned char  desc_size;
-       unsigned char  winfo_size;
-       unsigned short priv_size;
-};
-
 /**
  * queue_end - Return pointer to the last queue (HELPER MACRO).
  * @__dev: Pointer to &struct rt2x00_dev
index 0dc8180..54d3ddf 100644 (file)
@@ -2175,7 +2175,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
         * that the TX_STA_FIFO stack has a size of 16. We stick to our
         * tx ring size for now.
         */
-       for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) {
+       for (i = 0; i < rt2x00dev->tx->limit; i++) {
                rt2x00mmio_register_read(rt2x00dev, STA_CSR4, &reg);
                if (!rt2x00_get_field32(reg, STA_CSR4_VALID))
                        break;
@@ -2825,7 +2825,8 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
                tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START);
                for (i = 14; i < spec->num_channels; i++) {
                        info[i].max_power = MAX_TXPOWER;
-                       info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+                       info[i].default_power1 =
+                                       TXPOWER_FROM_DEV(tx_power[i - 14]);
                }
        }
 
@@ -3025,26 +3026,40 @@ static const struct rt2x00lib_ops rt61pci_rt2x00_ops = {
        .config                 = rt61pci_config,
 };
 
-static const struct data_queue_desc rt61pci_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt61pci_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt61pci_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt61pci_queue_bcn = {
-       .entry_num              = 4,
-       .data_size              = 0, /* No DMA required for beacons */
-       .desc_size              = TXINFO_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 4;
+               queue->data_size = 0; /* No DMA required for beacons */
+               queue->desc_size = TXINFO_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt61pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -3052,10 +3067,7 @@ static const struct rt2x00_ops rt61pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = 0,
-       .rx                     = &rt61pci_queue_rx,
-       .tx                     = &rt61pci_queue_tx,
-       .bcn                    = &rt61pci_queue_bcn,
+       .queue_init             = rt61pci_queue_init,
        .lib                    = &rt61pci_rt2x00_ops,
        .hw                     = &rt61pci_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index 377e09b..1d3880e 100644 (file)
@@ -2167,7 +2167,8 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
                tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START);
                for (i = 14; i < spec->num_channels; i++) {
                        info[i].max_power = MAX_TXPOWER;
-                       info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+                       info[i].default_power1 =
+                                       TXPOWER_FROM_DEV(tx_power[i - 14]);
                }
        }
 
@@ -2359,26 +2360,40 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = {
        .config                 = rt73usb_config,
 };
 
-static const struct data_queue_desc rt73usb_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+static void rt73usb_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt73usb_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt73usb_queue_bcn = {
-       .entry_num              = 4,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXINFO_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_BEACON:
+               queue->limit = 4;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXINFO_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
+
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt73usb_ops = {
        .name                   = KBUILD_MODNAME,
@@ -2386,10 +2401,7 @@ static const struct rt2x00_ops rt73usb_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXD_DESC_SIZE,
-       .rx                     = &rt73usb_queue_rx,
-       .tx                     = &rt73usb_queue_tx,
-       .bcn                    = &rt73usb_queue_bcn,
+       .queue_init             = rt73usb_queue_init,
        .lib                    = &rt73usb_rt2x00_ops,
        .hw                     = &rt73usb_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index a5f2231..9d558ac 100644 (file)
@@ -1817,7 +1817,7 @@ static ssize_t rtl_store_debug_level(struct device *d,
        unsigned long val;
        int ret;
 
-       ret = strict_strtoul(buf, 0, &val);
+       ret = kstrtoul(buf, 0, &val);
        if (ret) {
                printk(KERN_DEBUG "%s is not in hex or decimal form.\n", buf);
        } else {
index 953f1a0..2119313 100644 (file)
@@ -104,7 +104,7 @@ void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
                        tx_agc[RF90_PATH_A] = 0x10101010;
                        tx_agc[RF90_PATH_B] = 0x10101010;
                } else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
-                          TXHIGHPWRLEVEL_LEVEL1) {
+                          TXHIGHPWRLEVEL_LEVEL2) {
                        tx_agc[RF90_PATH_A] = 0x00000000;
                        tx_agc[RF90_PATH_B] = 0x00000000;
                } else{
index 826f085..2bd5985 100644 (file)
@@ -359,6 +359,7 @@ static struct usb_device_id rtl8192c_usb_ids[] = {
        {RTL_USB_DEVICE(0x2001, 0x330a, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/
        {RTL_USB_DEVICE(0x2019, 0xab2b, rtl92cu_hal_cfg)}, /*Planex -Abocom*/
        {RTL_USB_DEVICE(0x20f4, 0x624d, rtl92cu_hal_cfg)}, /*TRENDNet*/
+       {RTL_USB_DEVICE(0x2357, 0x0100, rtl92cu_hal_cfg)}, /*TP-Link WN8200ND*/
        {RTL_USB_DEVICE(0x7392, 0x7822, rtl92cu_hal_cfg)}, /*Edimax -Edimax*/
        {}
 };
index 19a7655..47875ba 100644 (file)
@@ -842,7 +842,7 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
        long val_y, ele_c = 0;
        u8 ofdm_index[2];
        s8 cck_index = 0;
-       u8 ofdm_index_old[2];
+       u8 ofdm_index_old[2] = {0, 0};
        s8 cck_index_old = 0;
        u8 index;
        int i;
index e4c4cdc..d9ee2ef 100644 (file)
@@ -251,7 +251,7 @@ static struct rtl_hal_cfg rtl8723ae_hal_cfg = {
        .bar_id = 2,
        .write_readback = true,
        .name = "rtl8723ae_pci",
-       .fw_name = "rtlwifi/rtl8723aefw.bin",
+       .fw_name = "rtlwifi/rtl8723fw.bin",
        .ops = &rtl8723ae_hal_ops,
        .mod_params = &rtl8723ae_mod_params,
        .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL,
@@ -353,8 +353,8 @@ MODULE_AUTHOR("Realtek WlanFAE      <wlanfae@realtek.com>");
 MODULE_AUTHOR("Larry Finger    <Larry.Finger@lwfinger.net>");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Realtek 8723E 802.11n PCI wireless");
-MODULE_FIRMWARE("rtlwifi/rtl8723aefw.bin");
-MODULE_FIRMWARE("rtlwifi/rtl8723aefw_B.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8723fw.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8723fw_B.bin");
 
 module_param_named(swenc, rtl8723ae_mod_params.sw_crypto, bool, 0444);
 module_param_named(debug, rtl8723ae_mod_params.debug, int, 0444);
index 4c67c2f..c7dc6fe 100644 (file)
@@ -93,8 +93,7 @@ static void wl1251_spi_wake(struct wl1251 *wl)
        memset(&t, 0, sizeof(t));
        spi_message_init(&m);
 
-       /*
-        * Set WSPI_INIT_COMMAND
+       /* Set WSPI_INIT_COMMAND
         * the data is being send from the MSB to LSB
         */
        cmd[2] = 0xff;
@@ -262,7 +261,8 @@ static int wl1251_spi_probe(struct spi_device *spi)
        wl->if_ops = &wl1251_spi_ops;
 
        /* This is the only SPI value that we need to set here, the rest
-        * comes from the board-peripherals file */
+        * comes from the board-peripherals file
+        */
        spi->bits_per_word = 32;
 
        ret = spi_setup(spi);
@@ -329,29 +329,7 @@ static struct spi_driver wl1251_spi_driver = {
        .remove         = wl1251_spi_remove,
 };
 
-static int __init wl1251_spi_init(void)
-{
-       int ret;
-
-       ret = spi_register_driver(&wl1251_spi_driver);
-       if (ret < 0) {
-               wl1251_error("failed to register spi driver: %d", ret);
-               goto out;
-       }
-
-out:
-       return ret;
-}
-
-static void __exit wl1251_spi_exit(void)
-{
-       spi_unregister_driver(&wl1251_spi_driver);
-
-       wl1251_notice("unloaded");
-}
-
-module_init(wl1251_spi_init);
-module_exit(wl1251_spi_exit);
+module_spi_driver(wl1251_spi_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");
index 9fa692d..7aa0eb8 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/platform_device.h>
 #include <linux/ip.h>
 #include <linux/firmware.h>
+#include <linux/etherdevice.h>
 
 #include "../wlcore/wlcore.h"
 #include "../wlcore/debug.h"
@@ -594,8 +595,8 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
                .mem3 = { .start = 0x00000000, .size  = 0x00000000 },
        },
        [PART_PHY_INIT] = {
-               .mem  = { .start = 0x80926000,
-                         .size = sizeof(struct wl18xx_mac_and_phy_params) },
+               .mem  = { .start = WL18XX_PHY_INIT_MEM_ADDR,
+                         .size  = WL18XX_PHY_INIT_MEM_SIZE },
                .reg  = { .start = 0x00000000, .size = 0x00000000 },
                .mem2 = { .start = 0x00000000, .size = 0x00000000 },
                .mem3 = { .start = 0x00000000, .size = 0x00000000 },
@@ -799,6 +800,9 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
        u32 tmp;
        int ret;
 
+       BUILD_BUG_ON(sizeof(struct wl18xx_mac_and_phy_params) >
+               WL18XX_PHY_INIT_MEM_SIZE);
+
        ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
        if (ret < 0)
                goto out;
@@ -815,6 +819,35 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
        wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
 
        ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp);
+       if (ret < 0)
+               goto out;
+
+       /*
+        * Workaround for FDSP code RAM corruption (needed for PG2.1
+        * and newer; for older chips it's a NOP).  Change FDSP clock
+        * settings so that it's muxed to the ATGP clock instead of
+        * its own clock.
+        */
+
+       ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]);
+       if (ret < 0)
+               goto out;
+
+       /* disable FDSP clock */
+       ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+                            MEM_FDSP_CLK_120_DISABLE);
+       if (ret < 0)
+               goto out;
+
+       /* set ATPG clock toward FDSP Code RAM rather than its own clock */
+       ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+                            MEM_FDSP_CODERAM_FUNC_CLK_SEL);
+       if (ret < 0)
+               goto out;
+
+       /* re-enable FDSP clock */
+       ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+                            MEM_FDSP_CLK_120_ENABLE);
 
 out:
        return ret;
@@ -1286,6 +1319,16 @@ static int wl18xx_get_mac(struct wl1271 *wl)
                ((mac1 & 0xff000000) >> 24);
        wl->fuse_nic_addr = (mac1 & 0xffffff);
 
+       if (!wl->fuse_oui_addr && !wl->fuse_nic_addr) {
+               u8 mac[ETH_ALEN];
+
+               eth_random_addr(mac);
+
+               wl->fuse_oui_addr = (mac[0] << 16) + (mac[1] << 8) + mac[2];
+               wl->fuse_nic_addr = (mac[3] << 16) + (mac[4] << 8) + mac[5];
+               wl1271_warning("MAC address from fuse not available, using random locally administered addresses.");
+       }
+
        ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
 
 out:
index 6306e04..05dd8ba 100644 (file)
@@ -38,6 +38,9 @@
 #define WL18XX_REG_BOOT_PART_SIZE  0x00014578
 
 #define WL18XX_PHY_INIT_MEM_ADDR   0x80926000
+#define WL18XX_PHY_END_MEM_ADDR           0x8093CA44
+#define WL18XX_PHY_INIT_MEM_SIZE \
+       (WL18XX_PHY_END_MEM_ADDR - WL18XX_PHY_INIT_MEM_ADDR)
 
 #define WL18XX_SDIO_WSPI_BASE          (WL18XX_REGISTERS_BASE)
 #define WL18XX_REG_CONFIG_BASE         (WL18XX_REGISTERS_BASE + 0x02000)
@@ -217,4 +220,16 @@ static const char * const rdl_names[] = {
        [RDL_4_SP]      = "1897 MIMO",
 };
 
+/* FPGA_SPARE_1 register - used to change the PHY ATPG clock at boot time */
+#define WL18XX_PHY_FPGA_SPARE_1                0x8093CA40
+
+/* command to disable FDSP clock */
+#define MEM_FDSP_CLK_120_DISABLE        0x80000000
+
+/* command to set ATPG clock toward FDSP Code RAM rather than its own clock */
+#define MEM_FDSP_CODERAM_FUNC_CLK_SEL  0xC0000000
+
+/* command to re-enable FDSP clock */
+#define MEM_FDSP_CLK_120_ENABLE                0x40000000
+
 #endif /* __REG_H__ */
index b21398f..4f23931 100644 (file)
@@ -1,5 +1,5 @@
 wlcore-objs            = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \
-                         boot.o init.o debugfs.o scan.o
+                         boot.o init.o debugfs.o scan.o sysfs.o
 
 wlcore_spi-objs        = spi.o
 wlcore_sdio-objs       = sdio.o
index 953111a..b8db55c 100644 (file)
@@ -1,10 +1,9 @@
 
 /*
- * This file is part of wl1271
+ * This file is part of wlcore
  *
  * Copyright (C) 2008-2010 Nokia Corporation
- *
- * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ * Copyright (C) 2011-2013 Texas Instruments Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 
 #include <linux/module.h>
 #include <linux/firmware.h>
-#include <linux/delay.h>
-#include <linux/spi/spi.h>
-#include <linux/crc32.h>
 #include <linux/etherdevice.h>
 #include <linux/vmalloc.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
 #include <linux/wl12xx.h>
-#include <linux/sched.h>
 #include <linux/interrupt.h>
 
 #include "wlcore.h"
 #include "debug.h"
 #include "wl12xx_80211.h"
 #include "io.h"
-#include "event.h"
 #include "tx.h"
-#include "rx.h"
 #include "ps.h"
 #include "init.h"
 #include "debugfs.h"
-#include "cmd.h"
-#include "boot.h"
 #include "testmode.h"
 #include "scan.h"
 #include "hw_ops.h"
-
-#define WL1271_BOOT_RETRIES 3
+#include "sysfs.h"
 
 #define WL1271_BOOT_RETRIES 3
 
@@ -65,8 +53,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
 static void wlcore_op_stop_locked(struct wl1271 *wl);
 static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 
-static int wl12xx_set_authorized(struct wl1271 *wl,
-                                struct wl12xx_vif *wlvif)
+static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
 
@@ -983,7 +970,7 @@ static int wlcore_fw_wakeup(struct wl1271 *wl)
 
 static int wl1271_setup(struct wl1271 *wl)
 {
-       wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
+       wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
                                  sizeof(*wl->fw_status_2) +
                                  wl->fw_status_priv_len, GFP_KERNEL);
        if (!wl->fw_status_1)
@@ -993,7 +980,7 @@ static int wl1271_setup(struct wl1271 *wl)
                                (((u8 *) wl->fw_status_1) +
                                WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
 
-       wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
+       wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
        if (!wl->tx_res_if) {
                kfree(wl->fw_status_1);
                return -ENOMEM;
@@ -1668,8 +1655,7 @@ static int wl1271_configure_suspend(struct wl1271 *wl,
        return 0;
 }
 
-static void wl1271_configure_resume(struct wl1271 *wl,
-                                   struct wl12xx_vif *wlvif)
+static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret = 0;
        bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
@@ -2603,6 +2589,7 @@ unlock:
        cancel_work_sync(&wlvif->rx_streaming_enable_work);
        cancel_work_sync(&wlvif->rx_streaming_disable_work);
        cancel_delayed_work_sync(&wlvif->connection_loss_work);
+       cancel_delayed_work_sync(&wlvif->channel_switch_work);
 
        mutex_lock(&wl->mutex);
 }
@@ -3210,14 +3197,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                if (ret < 0)
                        return ret;
 
-               /* the default WEP key needs to be configured at least once */
-               if (key_type == KEY_WEP) {
-                       ret = wl12xx_cmd_set_default_wep_key(wl,
-                                                       wlvif->default_key,
-                                                       wlvif->sta.hlid);
-                       if (ret < 0)
-                               return ret;
-               }
        }
 
        return 0;
@@ -3374,6 +3353,46 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
 }
 EXPORT_SYMBOL_GPL(wlcore_set_key);
 
+static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw,
+                                         struct ieee80211_vif *vif,
+                                         int key_idx)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int ret;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d",
+                    key_idx);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EAGAIN;
+               goto out_unlock;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out_unlock;
+
+       wlvif->default_key = key_idx;
+
+       /* the default WEP key needs to be configured at least once */
+       if (wlvif->encryption_type == KEY_WEP) {
+               ret = wl12xx_cmd_set_default_wep_key(wl,
+                               key_idx,
+                               wlvif->sta.hlid);
+               if (ret < 0)
+                       goto out_sleep;
+       }
+
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+
+out_unlock:
+       mutex_unlock(&wl->mutex);
+}
+
 void wlcore_regdomain_config(struct wl1271 *wl)
 {
        int ret;
@@ -3782,8 +3801,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
        struct ieee80211_hdr *hdr;
        u32 min_rate;
        int ret;
-       int ieoffset = offsetof(struct ieee80211_mgmt,
-                               u.beacon.variable);
+       int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
        struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
        u16 tmpl_id;
 
@@ -4230,8 +4248,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
        }
 
        /* Handle new association with HT. Do this after join. */
-       if (sta_exists &&
-           (changed & BSS_CHANGED_HT)) {
+       if (sta_exists) {
                bool enabled =
                        bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
 
@@ -5368,6 +5385,7 @@ static const struct ieee80211_ops wl1271_ops = {
        .ampdu_action = wl1271_op_ampdu_action,
        .tx_frames_pending = wl1271_tx_frames_pending,
        .set_bitrate_mask = wl12xx_set_bitrate_mask,
+       .set_default_unicast_key = wl1271_op_set_default_key_idx,
        .channel_switch = wl12xx_op_channel_switch,
        .flush = wlcore_op_flush,
        .remain_on_channel = wlcore_op_remain_on_channel,
@@ -5403,151 +5421,6 @@ u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band)
        return idx;
 }
 
-static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
-                                              struct device_attribute *attr,
-                                              char *buf)
-{
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       ssize_t len;
-
-       len = PAGE_SIZE;
-
-       mutex_lock(&wl->mutex);
-       len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
-                      wl->sg_enabled);
-       mutex_unlock(&wl->mutex);
-
-       return len;
-
-}
-
-static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
-                                               struct device_attribute *attr,
-                                               const char *buf, size_t count)
-{
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       unsigned long res;
-       int ret;
-
-       ret = kstrtoul(buf, 10, &res);
-       if (ret < 0) {
-               wl1271_warning("incorrect value written to bt_coex_mode");
-               return count;
-       }
-
-       mutex_lock(&wl->mutex);
-
-       res = !!res;
-
-       if (res == wl->sg_enabled)
-               goto out;
-
-       wl->sg_enabled = res;
-
-       if (unlikely(wl->state != WLCORE_STATE_ON))
-               goto out;
-
-       ret = wl1271_ps_elp_wakeup(wl);
-       if (ret < 0)
-               goto out;
-
-       wl1271_acx_sg_enable(wl, wl->sg_enabled);
-       wl1271_ps_elp_sleep(wl);
-
- out:
-       mutex_unlock(&wl->mutex);
-       return count;
-}
-
-static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
-                  wl1271_sysfs_show_bt_coex_state,
-                  wl1271_sysfs_store_bt_coex_state);
-
-static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
-                                          struct device_attribute *attr,
-                                          char *buf)
-{
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       ssize_t len;
-
-       len = PAGE_SIZE;
-
-       mutex_lock(&wl->mutex);
-       if (wl->hw_pg_ver >= 0)
-               len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
-       else
-               len = snprintf(buf, len, "n/a\n");
-       mutex_unlock(&wl->mutex);
-
-       return len;
-}
-
-static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
-                  wl1271_sysfs_show_hw_pg_ver, NULL);
-
-static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
-                                      struct bin_attribute *bin_attr,
-                                      char *buffer, loff_t pos, size_t count)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       ssize_t len;
-       int ret;
-
-       ret = mutex_lock_interruptible(&wl->mutex);
-       if (ret < 0)
-               return -ERESTARTSYS;
-
-       /* Let only one thread read the log at a time, blocking others */
-       while (wl->fwlog_size == 0) {
-               DEFINE_WAIT(wait);
-
-               prepare_to_wait_exclusive(&wl->fwlog_waitq,
-                                         &wait,
-                                         TASK_INTERRUPTIBLE);
-
-               if (wl->fwlog_size != 0) {
-                       finish_wait(&wl->fwlog_waitq, &wait);
-                       break;
-               }
-
-               mutex_unlock(&wl->mutex);
-
-               schedule();
-               finish_wait(&wl->fwlog_waitq, &wait);
-
-               if (signal_pending(current))
-                       return -ERESTARTSYS;
-
-               ret = mutex_lock_interruptible(&wl->mutex);
-               if (ret < 0)
-                       return -ERESTARTSYS;
-       }
-
-       /* Check if the fwlog is still valid */
-       if (wl->fwlog_size < 0) {
-               mutex_unlock(&wl->mutex);
-               return 0;
-       }
-
-       /* Seeking is not supported - old logs are not kept. Disregard pos. */
-       len = min(count, (size_t)wl->fwlog_size);
-       wl->fwlog_size -= len;
-       memcpy(buffer, wl->fwlog, len);
-
-       /* Make room for new messages */
-       memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
-
-       mutex_unlock(&wl->mutex);
-
-       return len;
-}
-
-static struct bin_attribute fwlog_attr = {
-       .attr = {.name = "fwlog", .mode = S_IRUSR},
-       .read = wl1271_sysfs_read_fwlog,
-};
-
 static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
 {
        int i;
@@ -5827,8 +5700,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        return 0;
 }
 
-#define WL1271_DEFAULT_CHANNEL 0
-
 struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
                                     u32 mbox_size)
 {
@@ -5881,7 +5752,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
                goto err_hw;
        }
 
-       wl->channel = WL1271_DEFAULT_CHANNEL;
+       wl->channel = 0;
        wl->rx_counter = 0;
        wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
        wl->band = IEEE80211_BAND_2GHZ;
@@ -5988,11 +5859,8 @@ int wlcore_free_hw(struct wl1271 *wl)
        wake_up_interruptible_all(&wl->fwlog_waitq);
        mutex_unlock(&wl->mutex);
 
-       device_remove_bin_file(wl->dev, &fwlog_attr);
-
-       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
+       wlcore_sysfs_free(wl);
 
-       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
        kfree(wl->buffer_32);
        kfree(wl->mbox);
        free_page((unsigned long)wl->fwlog);
@@ -6018,6 +5886,15 @@ int wlcore_free_hw(struct wl1271 *wl)
 }
 EXPORT_SYMBOL_GPL(wlcore_free_hw);
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support wlcore_wowlan_support = {
+       .flags = WIPHY_WOWLAN_ANY,
+       .n_patterns = WL1271_MAX_RX_FILTERS,
+       .pattern_min_len = 1,
+       .pattern_max_len = WL1271_RX_FILTER_MAX_PATTERN_SIZE,
+};
+#endif
+
 static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 {
        struct wl1271 *wl = context;
@@ -6071,14 +5948,8 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
        if (!ret) {
                wl->irq_wake_enabled = true;
                device_init_wakeup(wl->dev, 1);
-               if (pdata->pwr_in_suspend) {
-                       wl->hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
-                       wl->hw->wiphy->wowlan.n_patterns =
-                               WL1271_MAX_RX_FILTERS;
-                       wl->hw->wiphy->wowlan.pattern_min_len = 1;
-                       wl->hw->wiphy->wowlan.pattern_max_len =
-                               WL1271_RX_FILTER_MAX_PATTERN_SIZE;
-               }
+               if (pdata->pwr_in_suspend)
+                       wl->hw->wiphy->wowlan = &wlcore_wowlan_support;
        }
 #endif
        disable_irq(wl->irq);
@@ -6101,36 +5972,13 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
        if (ret)
                goto out_irq;
 
-       /* Create sysfs file to control bt coex state */
-       ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file bt_coex_state");
+       ret = wlcore_sysfs_init(wl);
+       if (ret)
                goto out_unreg;
-       }
-
-       /* Create sysfs file to get HW PG version */
-       ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file hw_pg_ver");
-               goto out_bt_coex_state;
-       }
-
-       /* Create sysfs file for the FW log */
-       ret = device_create_bin_file(wl->dev, &fwlog_attr);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file fwlog");
-               goto out_hw_pg_ver;
-       }
 
        wl->initialized = true;
        goto out;
 
-out_hw_pg_ver:
-       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
-
-out_bt_coex_state:
-       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
-
 out_unreg:
        wl1271_unregister_hw(wl);
 
index 9654577..98066d4 100644 (file)
@@ -110,7 +110,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
        DECLARE_COMPLETION_ONSTACK(compl);
        unsigned long flags;
        int ret;
-       u32 start_time = jiffies;
+       unsigned long start_time = jiffies;
        bool pending = false;
 
        /*
index e264478..1b0cd98 100644 (file)
@@ -434,19 +434,7 @@ static struct spi_driver wl1271_spi_driver = {
        .remove         = wl1271_remove,
 };
 
-static int __init wl1271_init(void)
-{
-       return spi_register_driver(&wl1271_spi_driver);
-}
-
-static void __exit wl1271_exit(void)
-{
-       spi_unregister_driver(&wl1271_spi_driver);
-}
-
-module_init(wl1271_init);
-module_exit(wl1271_exit);
-
+module_spi_driver(wl1271_spi_driver);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
 MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c
new file mode 100644 (file)
index 0000000..8e58349
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2013 Texas Instruments Inc.
+ *
+ * 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 "wlcore.h"
+#include "debug.h"
+#include "ps.h"
+#include "sysfs.h"
+
+static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
+                                              struct device_attribute *attr,
+                                              char *buf)
+{
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       ssize_t len;
+
+       len = PAGE_SIZE;
+
+       mutex_lock(&wl->mutex);
+       len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
+                      wl->sg_enabled);
+       mutex_unlock(&wl->mutex);
+
+       return len;
+
+}
+
+static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t count)
+{
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       unsigned long res;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &res);
+       if (ret < 0) {
+               wl1271_warning("incorrect value written to bt_coex_mode");
+               return count;
+       }
+
+       mutex_lock(&wl->mutex);
+
+       res = !!res;
+
+       if (res == wl->sg_enabled)
+               goto out;
+
+       wl->sg_enabled = res;
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       wl1271_acx_sg_enable(wl, wl->sg_enabled);
+       wl1271_ps_elp_sleep(wl);
+
+ out:
+       mutex_unlock(&wl->mutex);
+       return count;
+}
+
+static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
+                  wl1271_sysfs_show_bt_coex_state,
+                  wl1271_sysfs_store_bt_coex_state);
+
+static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       ssize_t len;
+
+       len = PAGE_SIZE;
+
+       mutex_lock(&wl->mutex);
+       if (wl->hw_pg_ver >= 0)
+               len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
+       else
+               len = snprintf(buf, len, "n/a\n");
+       mutex_unlock(&wl->mutex);
+
+       return len;
+}
+
+static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
+                  wl1271_sysfs_show_hw_pg_ver, NULL);
+
+static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
+                                      struct bin_attribute *bin_attr,
+                                      char *buffer, loff_t pos, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       ssize_t len;
+       int ret;
+
+       ret = mutex_lock_interruptible(&wl->mutex);
+       if (ret < 0)
+               return -ERESTARTSYS;
+
+       /* Let only one thread read the log at a time, blocking others */
+       while (wl->fwlog_size == 0) {
+               DEFINE_WAIT(wait);
+
+               prepare_to_wait_exclusive(&wl->fwlog_waitq,
+                                         &wait,
+                                         TASK_INTERRUPTIBLE);
+
+               if (wl->fwlog_size != 0) {
+                       finish_wait(&wl->fwlog_waitq, &wait);
+                       break;
+               }
+
+               mutex_unlock(&wl->mutex);
+
+               schedule();
+               finish_wait(&wl->fwlog_waitq, &wait);
+
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+
+               ret = mutex_lock_interruptible(&wl->mutex);
+               if (ret < 0)
+                       return -ERESTARTSYS;
+       }
+
+       /* Check if the fwlog is still valid */
+       if (wl->fwlog_size < 0) {
+               mutex_unlock(&wl->mutex);
+               return 0;
+       }
+
+       /* Seeking is not supported - old logs are not kept. Disregard pos. */
+       len = min(count, (size_t)wl->fwlog_size);
+       wl->fwlog_size -= len;
+       memcpy(buffer, wl->fwlog, len);
+
+       /* Make room for new messages */
+       memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
+
+       mutex_unlock(&wl->mutex);
+
+       return len;
+}
+
+static struct bin_attribute fwlog_attr = {
+       .attr = {.name = "fwlog", .mode = S_IRUSR},
+       .read = wl1271_sysfs_read_fwlog,
+};
+
+int wlcore_sysfs_init(struct wl1271 *wl)
+{
+       int ret;
+
+       /* Create sysfs file to control bt coex state */
+       ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file bt_coex_state");
+               goto out;
+       }
+
+       /* Create sysfs file to get HW PG version */
+       ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file hw_pg_ver");
+               goto out_bt_coex_state;
+       }
+
+       /* Create sysfs file for the FW log */
+       ret = device_create_bin_file(wl->dev, &fwlog_attr);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file fwlog");
+               goto out_hw_pg_ver;
+       }
+
+       goto out;
+
+out_hw_pg_ver:
+       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
+
+out_bt_coex_state:
+       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
+
+out:
+       return ret;
+}
+
+void wlcore_sysfs_free(struct wl1271 *wl)
+{
+       device_remove_bin_file(wl->dev, &fwlog_attr);
+
+       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
+
+       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
+}
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.h b/drivers/net/wireless/ti/wlcore/sysfs.h
new file mode 100644 (file)
index 0000000..c148892
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2013 Texas Instruments Inc.
+ *
+ * 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 __SYSFS_H__
+#define __SYSFS_H__
+
+int wlcore_sysfs_init(struct wl1271 *wl);
+void wlcore_sysfs_free(struct wl1271 *wl);
+
+#endif
index 004d02e..7e93fe6 100644 (file)
@@ -386,7 +386,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
                         (cipher == WLAN_CIPHER_SUITE_WEP104);
 
-               if (unlikely(is_wep && wlvif->default_key != idx)) {
+               if (WARN_ON(is_wep && wlvif->default_key != idx)) {
                        ret = wl1271_set_default_wep_key(wl, wlvif, idx);
                        if (ret < 0)
                                return ret;
index 9d7f172..8a4d77e 100644 (file)
@@ -57,8 +57,12 @@ struct xenvif {
 
        u8               fe_dev_addr[6];
 
-       /* Physical parameters of the comms window. */
-       unsigned int     irq;
+       /* When feature-split-event-channels = 0, tx_irq = rx_irq. */
+       unsigned int tx_irq;
+       unsigned int rx_irq;
+       /* Only used when feature-split-event-channels = 1 */
+       char tx_irq_name[IFNAMSIZ+4]; /* DEVNAME-tx */
+       char rx_irq_name[IFNAMSIZ+4]; /* DEVNAME-rx */
 
        /* List of frontends to notify after a batch of frames sent. */
        struct list_head notify_list;
@@ -113,13 +117,15 @@ struct xenvif *xenvif_alloc(struct device *parent,
                            unsigned int handle);
 
 int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
-                  unsigned long rx_ring_ref, unsigned int evtchn);
+                  unsigned long rx_ring_ref, unsigned int tx_evtchn,
+                  unsigned int rx_evtchn);
 void xenvif_disconnect(struct xenvif *vif);
 
 void xenvif_get(struct xenvif *vif);
 void xenvif_put(struct xenvif *vif);
 
 int xenvif_xenbus_init(void);
+void xenvif_xenbus_fini(void);
 
 int xenvif_schedulable(struct xenvif *vif);
 
@@ -157,4 +163,6 @@ void xenvif_carrier_off(struct xenvif *vif);
 /* Returns number of ring slots required to send an skb to the frontend */
 unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb);
 
+extern bool separate_tx_rx_irq;
+
 #endif /* __XEN_NETBACK__COMMON_H__ */
index d984141..087d2db 100644 (file)
@@ -60,21 +60,39 @@ static int xenvif_rx_schedulable(struct xenvif *vif)
        return xenvif_schedulable(vif) && !xen_netbk_rx_ring_full(vif);
 }
 
-static irqreturn_t xenvif_interrupt(int irq, void *dev_id)
+static irqreturn_t xenvif_tx_interrupt(int irq, void *dev_id)
 {
        struct xenvif *vif = dev_id;
 
        if (vif->netbk == NULL)
-               return IRQ_NONE;
+               return IRQ_HANDLED;
 
        xen_netbk_schedule_xenvif(vif);
 
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t xenvif_rx_interrupt(int irq, void *dev_id)
+{
+       struct xenvif *vif = dev_id;
+
+       if (vif->netbk == NULL)
+               return IRQ_HANDLED;
+
        if (xenvif_rx_schedulable(vif))
                netif_wake_queue(vif->dev);
 
        return IRQ_HANDLED;
 }
 
+static irqreturn_t xenvif_interrupt(int irq, void *dev_id)
+{
+       xenvif_tx_interrupt(irq, dev_id);
+       xenvif_rx_interrupt(irq, dev_id);
+
+       return IRQ_HANDLED;
+}
+
 static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct xenvif *vif = netdev_priv(dev);
@@ -125,13 +143,17 @@ static struct net_device_stats *xenvif_get_stats(struct net_device *dev)
 static void xenvif_up(struct xenvif *vif)
 {
        xen_netbk_add_xenvif(vif);
-       enable_irq(vif->irq);
+       enable_irq(vif->tx_irq);
+       if (vif->tx_irq != vif->rx_irq)
+               enable_irq(vif->rx_irq);
        xen_netbk_check_rx_xenvif(vif);
 }
 
 static void xenvif_down(struct xenvif *vif)
 {
-       disable_irq(vif->irq);
+       disable_irq(vif->tx_irq);
+       if (vif->tx_irq != vif->rx_irq)
+               disable_irq(vif->rx_irq);
        del_timer_sync(&vif->credit_timeout);
        xen_netbk_deschedule_xenvif(vif);
        xen_netbk_remove_xenvif(vif);
@@ -308,25 +330,52 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
 }
 
 int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
-                  unsigned long rx_ring_ref, unsigned int evtchn)
+                  unsigned long rx_ring_ref, unsigned int tx_evtchn,
+                  unsigned int rx_evtchn)
 {
        int err = -ENOMEM;
 
        /* Already connected through? */
-       if (vif->irq)
+       if (vif->tx_irq)
                return 0;
 
+       __module_get(THIS_MODULE);
+
        err = xen_netbk_map_frontend_rings(vif, tx_ring_ref, rx_ring_ref);
        if (err < 0)
                goto err;
 
-       err = bind_interdomain_evtchn_to_irqhandler(
-               vif->domid, evtchn, xenvif_interrupt, 0,
-               vif->dev->name, vif);
-       if (err < 0)
-               goto err_unmap;
-       vif->irq = err;
-       disable_irq(vif->irq);
+       if (tx_evtchn == rx_evtchn) {
+               /* feature-split-event-channels == 0 */
+               err = bind_interdomain_evtchn_to_irqhandler(
+                       vif->domid, tx_evtchn, xenvif_interrupt, 0,
+                       vif->dev->name, vif);
+               if (err < 0)
+                       goto err_unmap;
+               vif->tx_irq = vif->rx_irq = err;
+               disable_irq(vif->tx_irq);
+       } else {
+               /* feature-split-event-channels == 1 */
+               snprintf(vif->tx_irq_name, sizeof(vif->tx_irq_name),
+                        "%s-tx", vif->dev->name);
+               err = bind_interdomain_evtchn_to_irqhandler(
+                       vif->domid, tx_evtchn, xenvif_tx_interrupt, 0,
+                       vif->tx_irq_name, vif);
+               if (err < 0)
+                       goto err_unmap;
+               vif->tx_irq = err;
+               disable_irq(vif->tx_irq);
+
+               snprintf(vif->rx_irq_name, sizeof(vif->rx_irq_name),
+                        "%s-rx", vif->dev->name);
+               err = bind_interdomain_evtchn_to_irqhandler(
+                       vif->domid, rx_evtchn, xenvif_rx_interrupt, 0,
+                       vif->rx_irq_name, vif);
+               if (err < 0)
+                       goto err_tx_unbind;
+               vif->rx_irq = err;
+               disable_irq(vif->rx_irq);
+       }
 
        xenvif_get(vif);
 
@@ -340,9 +389,13 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
        rtnl_unlock();
 
        return 0;
+err_tx_unbind:
+       unbind_from_irqhandler(vif->tx_irq, vif);
+       vif->tx_irq = 0;
 err_unmap:
        xen_netbk_unmap_frontend_rings(vif);
 err:
+       module_put(THIS_MODULE);
        return err;
 }
 
@@ -360,18 +413,37 @@ void xenvif_carrier_off(struct xenvif *vif)
 
 void xenvif_disconnect(struct xenvif *vif)
 {
+       /* Disconnect funtion might get called by generic framework
+        * even before vif connects, so we need to check if we really
+        * need to do a module_put.
+        */
+       int need_module_put = 0;
+
        if (netif_carrier_ok(vif->dev))
                xenvif_carrier_off(vif);
 
        atomic_dec(&vif->refcnt);
        wait_event(vif->waiting_to_free, atomic_read(&vif->refcnt) == 0);
 
-       if (vif->irq)
-               unbind_from_irqhandler(vif->irq, vif);
+       if (vif->tx_irq) {
+               if (vif->tx_irq == vif->rx_irq)
+                       unbind_from_irqhandler(vif->tx_irq, vif);
+               else {
+                       unbind_from_irqhandler(vif->tx_irq, vif);
+                       unbind_from_irqhandler(vif->rx_irq, vif);
+               }
+               /* vif->irq is valid, we had a module_get in
+                * xenvif_connect.
+                */
+               need_module_put = 1;
+       }
 
        unregister_netdev(vif->dev);
 
        xen_netbk_unmap_frontend_rings(vif);
 
        free_netdev(vif->dev);
+
+       if (need_module_put)
+               module_put(THIS_MODULE);
 }
index 8c20935..64828de 100644 (file)
 #include <asm/xen/hypercall.h>
 #include <asm/xen/page.h>
 
+/* Provide an option to disable split event channels at load time as
+ * event channels are limited resource. Split event channels are
+ * enabled by default.
+ */
+bool separate_tx_rx_irq = 1;
+module_param(separate_tx_rx_irq, bool, 0644);
+
 /*
  * This is the maximum slots a skb can have. If a guest sends a skb
  * which exceeds this limit it is considered malicious.
@@ -783,7 +790,7 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk)
        }
 
        list_for_each_entry_safe(vif, tmp, &notify, notify_list) {
-               notify_remote_via_irq(vif->irq);
+               notify_remote_via_irq(vif->rx_irq);
                list_del_init(&vif->notify_list);
                xenvif_put(vif);
        }
@@ -1763,7 +1770,7 @@ static void make_tx_response(struct xenvif *vif,
        vif->tx.rsp_prod_pvt = ++i;
        RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->tx, notify);
        if (notify)
-               notify_remote_via_irq(vif->irq);
+               notify_remote_via_irq(vif->tx_irq);
 }
 
 static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
@@ -1883,9 +1890,8 @@ static int __init netback_init(void)
                return -ENODEV;
 
        if (fatal_skb_slots < XEN_NETBK_LEGACY_SLOTS_MAX) {
-               printk(KERN_INFO
-                      "xen-netback: fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n",
-                      fatal_skb_slots, XEN_NETBK_LEGACY_SLOTS_MAX);
+               pr_info("fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n",
+                       fatal_skb_slots, XEN_NETBK_LEGACY_SLOTS_MAX);
                fatal_skb_slots = XEN_NETBK_LEGACY_SLOTS_MAX;
        }
 
@@ -1914,7 +1920,7 @@ static int __init netback_init(void)
                                             "netback/%u", group);
 
                if (IS_ERR(netbk->task)) {
-                       printk(KERN_ALERT "kthread_create() fails at netback\n");
+                       pr_alert("kthread_create() fails at netback\n");
                        del_timer(&netbk->net_timer);
                        rc = PTR_ERR(netbk->task);
                        goto failed_init;
@@ -1940,10 +1946,6 @@ static int __init netback_init(void)
 failed_init:
        while (--group >= 0) {
                struct xen_netbk *netbk = &xen_netbk[group];
-               for (i = 0; i < MAX_PENDING_REQS; i++) {
-                       if (netbk->mmap_pages[i])
-                               __free_page(netbk->mmap_pages[i]);
-               }
                del_timer(&netbk->net_timer);
                kthread_stop(netbk->task);
        }
@@ -1954,5 +1956,25 @@ failed_init:
 
 module_init(netback_init);
 
+static void __exit netback_fini(void)
+{
+       int i, j;
+
+       xenvif_xenbus_fini();
+
+       for (i = 0; i < xen_netbk_group_nr; i++) {
+               struct xen_netbk *netbk = &xen_netbk[i];
+               del_timer_sync(&netbk->net_timer);
+               kthread_stop(netbk->task);
+               for (j = 0; j < MAX_PENDING_REQS; j++) {
+                       if (netbk->mmap_pages[j])
+                               __free_page(netbk->mmap_pages[j]);
+               }
+       }
+
+       vfree(xen_netbk);
+}
+module_exit(netback_fini);
+
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_ALIAS("xen-backend:vif");
index 410018c..1fe48fe 100644 (file)
@@ -122,6 +122,16 @@ static int netback_probe(struct xenbus_device *dev,
                goto fail;
        }
 
+       /*
+        * Split event channels support, this is optional so it is not
+        * put inside the above loop.
+        */
+       err = xenbus_printf(XBT_NIL, dev->nodename,
+                           "feature-split-event-channels",
+                           "%u", separate_tx_rx_irq);
+       if (err)
+               pr_debug("Error writing feature-split-event-channels\n");
+
        err = xenbus_switch_state(dev, XenbusStateInitWait);
        if (err)
                goto fail;
@@ -135,7 +145,7 @@ abort_transaction:
        xenbus_transaction_end(xbt, 1);
        xenbus_dev_fatal(dev, err, "%s", message);
 fail:
-       pr_debug("failed");
+       pr_debug("failed\n");
        netback_remove(dev);
        return err;
 }
@@ -218,15 +228,14 @@ static void frontend_changed(struct xenbus_device *dev,
 {
        struct backend_info *be = dev_get_drvdata(&dev->dev);
 
-       pr_debug("frontend state %s", xenbus_strstate(frontend_state));
+       pr_debug("frontend state %s\n", xenbus_strstate(frontend_state));
 
        be->frontend_state = frontend_state;
 
        switch (frontend_state) {
        case XenbusStateInitialising:
                if (dev->state == XenbusStateClosed) {
-                       printk(KERN_INFO "%s: %s: prepare for reconnect\n",
-                              __func__, dev->nodename);
+                       pr_info("%s: prepare for reconnect\n", dev->nodename);
                        xenbus_switch_state(dev, XenbusStateInitWait);
                }
                break;
@@ -393,21 +402,36 @@ static int connect_rings(struct backend_info *be)
        struct xenvif *vif = be->vif;
        struct xenbus_device *dev = be->dev;
        unsigned long tx_ring_ref, rx_ring_ref;
-       unsigned int evtchn, rx_copy;
+       unsigned int tx_evtchn, rx_evtchn, rx_copy;
        int err;
        int val;
 
        err = xenbus_gather(XBT_NIL, dev->otherend,
                            "tx-ring-ref", "%lu", &tx_ring_ref,
-                           "rx-ring-ref", "%lu", &rx_ring_ref,
-                           "event-channel", "%u", &evtchn, NULL);
+                           "rx-ring-ref", "%lu", &rx_ring_ref, NULL);
        if (err) {
                xenbus_dev_fatal(dev, err,
-                                "reading %s/ring-ref and event-channel",
+                                "reading %s/ring-ref",
                                 dev->otherend);
                return err;
        }
 
+       /* Try split event channels first, then single event channel. */
+       err = xenbus_gather(XBT_NIL, dev->otherend,
+                           "event-channel-tx", "%u", &tx_evtchn,
+                           "event-channel-rx", "%u", &rx_evtchn, NULL);
+       if (err < 0) {
+               err = xenbus_scanf(XBT_NIL, dev->otherend,
+                                  "event-channel", "%u", &tx_evtchn);
+               if (err < 0) {
+                       xenbus_dev_fatal(dev, err,
+                                        "reading %s/event-channel(-tx/rx)",
+                                        dev->otherend);
+                       return err;
+               }
+               rx_evtchn = tx_evtchn;
+       }
+
        err = xenbus_scanf(XBT_NIL, dev->otherend, "request-rx-copy", "%u",
                           &rx_copy);
        if (err == -ENOENT) {
@@ -454,11 +478,13 @@ static int connect_rings(struct backend_info *be)
        vif->csum = !val;
 
        /* Map the shared frame, irq etc. */
-       err = xenvif_connect(vif, tx_ring_ref, rx_ring_ref, evtchn);
+       err = xenvif_connect(vif, tx_ring_ref, rx_ring_ref,
+                            tx_evtchn, rx_evtchn);
        if (err) {
                xenbus_dev_fatal(dev, err,
-                                "mapping shared-frames %lu/%lu port %u",
-                                tx_ring_ref, rx_ring_ref, evtchn);
+                                "mapping shared-frames %lu/%lu port tx %u rx %u",
+                                tx_ring_ref, rx_ring_ref,
+                                tx_evtchn, rx_evtchn);
                return err;
        }
        return 0;
@@ -485,3 +511,8 @@ int xenvif_xenbus_init(void)
 {
        return xenbus_register_backend(&netback_driver);
 }
+
+void xenvif_xenbus_fini(void)
+{
+       return xenbus_unregister_driver(&netback_driver);
+}
index 1db1014..ff7f111 100644 (file)
@@ -29,6 +29,8 @@
  * IN THE SOFTWARE.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
@@ -85,7 +87,15 @@ struct netfront_info {
 
        struct napi_struct napi;
 
-       unsigned int evtchn;
+       /* Split event channels support, tx_* == rx_* when using
+        * single event channel.
+        */
+       unsigned int tx_evtchn, rx_evtchn;
+       unsigned int tx_irq, rx_irq;
+       /* Only used when split event channels support is enabled */
+       char tx_irq_name[IFNAMSIZ+4]; /* DEVNAME-tx */
+       char rx_irq_name[IFNAMSIZ+4]; /* DEVNAME-rx */
+
        struct xenbus_device *xbdev;
 
        spinlock_t   tx_lock;
@@ -330,7 +340,7 @@ no_skb:
  push:
        RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->rx, notify);
        if (notify)
-               notify_remote_via_irq(np->netdev->irq);
+               notify_remote_via_irq(np->rx_irq);
 }
 
 static int xennet_open(struct net_device *dev)
@@ -377,9 +387,8 @@ static void xennet_tx_buf_gc(struct net_device *dev)
                        skb = np->tx_skbs[id].skb;
                        if (unlikely(gnttab_query_foreign_access(
                                np->grant_tx_ref[id]) != 0)) {
-                               printk(KERN_ALERT "xennet_tx_buf_gc: warning "
-                                      "-- grant still in use by backend "
-                                      "domain.\n");
+                               pr_alert("%s: warning -- grant still in use by backend domain\n",
+                                        __func__);
                                BUG();
                        }
                        gnttab_end_foreign_access_ref(
@@ -623,7 +632,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->tx, notify);
        if (notify)
-               notify_remote_via_irq(np->netdev->irq);
+               notify_remote_via_irq(np->tx_irq);
 
        u64_stats_update_begin(&stats->syncp);
        stats->tx_bytes += skb->len;
@@ -796,14 +805,14 @@ static int xennet_set_skb_gso(struct sk_buff *skb,
 {
        if (!gso->u.gso.size) {
                if (net_ratelimit())
-                       printk(KERN_WARNING "GSO size must not be zero.\n");
+                       pr_warn("GSO size must not be zero\n");
                return -EINVAL;
        }
 
        /* Currently only TCPv4 S.O. is supported. */
        if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) {
                if (net_ratelimit())
-                       printk(KERN_WARNING "Bad GSO type %d.\n", gso->u.gso.type);
+                       pr_warn("Bad GSO type %d\n", gso->u.gso.type);
                return -EINVAL;
        }
 
@@ -850,7 +859,6 @@ static RING_IDX xennet_fill_frags(struct netfront_info *np,
 static int checksum_setup(struct net_device *dev, struct sk_buff *skb)
 {
        struct iphdr *iph;
-       unsigned char *th;
        int err = -EPROTO;
        int recalculate_partial_csum = 0;
 
@@ -875,27 +883,27 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb)
                goto out;
 
        iph = (void *)skb->data;
-       th = skb->data + 4 * iph->ihl;
-       if (th >= skb_tail_pointer(skb))
-               goto out;
 
-       skb->csum_start = th - skb->head;
        switch (iph->protocol) {
        case IPPROTO_TCP:
-               skb->csum_offset = offsetof(struct tcphdr, check);
+               if (!skb_partial_csum_set(skb, 4 * iph->ihl,
+                                         offsetof(struct tcphdr, check)))
+                       goto out;
 
                if (recalculate_partial_csum) {
-                       struct tcphdr *tcph = (struct tcphdr *)th;
+                       struct tcphdr *tcph = tcp_hdr(skb);
                        tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
                                                         skb->len - iph->ihl*4,
                                                         IPPROTO_TCP, 0);
                }
                break;
        case IPPROTO_UDP:
-               skb->csum_offset = offsetof(struct udphdr, check);
+               if (!skb_partial_csum_set(skb, 4 * iph->ihl,
+                                         offsetof(struct udphdr, check)))
+                       goto out;
 
                if (recalculate_partial_csum) {
-                       struct udphdr *udph = (struct udphdr *)th;
+                       struct udphdr *udph = udp_hdr(skb);
                        udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
                                                         skb->len - iph->ihl*4,
                                                         IPPROTO_UDP, 0);
@@ -903,15 +911,11 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb)
                break;
        default:
                if (net_ratelimit())
-                       printk(KERN_ERR "Attempting to checksum a non-"
-                              "TCP/UDP packet, dropping a protocol"
-                              " %d packet", iph->protocol);
+                       pr_err("Attempting to checksum a non-TCP/UDP packet, dropping a protocol %d packet\n",
+                              iph->protocol);
                goto out;
        }
 
-       if ((th + skb->csum_offset + 2) > skb_tail_pointer(skb))
-               goto out;
-
        err = 0;
 
 out:
@@ -1254,23 +1258,35 @@ static int xennet_set_features(struct net_device *dev,
        return 0;
 }
 
-static irqreturn_t xennet_interrupt(int irq, void *dev_id)
+static irqreturn_t xennet_tx_interrupt(int irq, void *dev_id)
 {
-       struct net_device *dev = dev_id;
-       struct netfront_info *np = netdev_priv(dev);
+       struct netfront_info *np = dev_id;
+       struct net_device *dev = np->netdev;
        unsigned long flags;
 
        spin_lock_irqsave(&np->tx_lock, flags);
+       xennet_tx_buf_gc(dev);
+       spin_unlock_irqrestore(&np->tx_lock, flags);
 
-       if (likely(netif_carrier_ok(dev))) {
-               xennet_tx_buf_gc(dev);
-               /* Under tx_lock: protects access to rx shared-ring indexes. */
-               if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx))
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t xennet_rx_interrupt(int irq, void *dev_id)
+{
+       struct netfront_info *np = dev_id;
+       struct net_device *dev = np->netdev;
+
+       if (likely(netif_carrier_ok(dev) &&
+                  RING_HAS_UNCONSUMED_RESPONSES(&np->rx)))
                        napi_schedule(&np->napi);
-       }
 
-       spin_unlock_irqrestore(&np->tx_lock, flags);
+       return IRQ_HANDLED;
+}
 
+static irqreturn_t xennet_interrupt(int irq, void *dev_id)
+{
+       xennet_tx_interrupt(irq, dev_id);
+       xennet_rx_interrupt(irq, dev_id);
        return IRQ_HANDLED;
 }
 
@@ -1343,14 +1359,14 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev)
        /* A grant for every tx ring slot */
        if (gnttab_alloc_grant_references(TX_MAX_TARGET,
                                          &np->gref_tx_head) < 0) {
-               printk(KERN_ALERT "#### netfront can't alloc tx grant refs\n");
+               pr_alert("can't alloc tx grant refs\n");
                err = -ENOMEM;
                goto exit_free_stats;
        }
        /* A grant for every rx ring slot */
        if (gnttab_alloc_grant_references(RX_MAX_TARGET,
                                          &np->gref_rx_head) < 0) {
-               printk(KERN_ALERT "#### netfront can't alloc rx grant refs\n");
+               pr_alert("can't alloc rx grant refs\n");
                err = -ENOMEM;
                goto exit_free_tx;
        }
@@ -1414,16 +1430,14 @@ static int netfront_probe(struct xenbus_device *dev,
 
        err = register_netdev(info->netdev);
        if (err) {
-               printk(KERN_WARNING "%s: register_netdev err=%d\n",
-                      __func__, err);
+               pr_warn("%s: register_netdev err=%d\n", __func__, err);
                goto fail;
        }
 
        err = xennet_sysfs_addif(info->netdev);
        if (err) {
                unregister_netdev(info->netdev);
-               printk(KERN_WARNING "%s: add sysfs failed err=%d\n",
-                      __func__, err);
+               pr_warn("%s: add sysfs failed err=%d\n", __func__, err);
                goto fail;
        }
 
@@ -1451,9 +1465,14 @@ static void xennet_disconnect_backend(struct netfront_info *info)
        spin_unlock_irq(&info->tx_lock);
        spin_unlock_bh(&info->rx_lock);
 
-       if (info->netdev->irq)
-               unbind_from_irqhandler(info->netdev->irq, info->netdev);
-       info->evtchn = info->netdev->irq = 0;
+       if (info->tx_irq && (info->tx_irq == info->rx_irq))
+               unbind_from_irqhandler(info->tx_irq, info);
+       if (info->tx_irq && (info->tx_irq != info->rx_irq)) {
+               unbind_from_irqhandler(info->tx_irq, info);
+               unbind_from_irqhandler(info->rx_irq, info);
+       }
+       info->tx_evtchn = info->rx_evtchn = 0;
+       info->tx_irq = info->rx_irq = 0;
 
        /* End access and free the pages */
        xennet_end_access(info->tx_ring_ref, info->tx.sring);
@@ -1503,12 +1522,82 @@ static int xen_net_read_mac(struct xenbus_device *dev, u8 mac[])
        return 0;
 }
 
+static int setup_netfront_single(struct netfront_info *info)
+{
+       int err;
+
+       err = xenbus_alloc_evtchn(info->xbdev, &info->tx_evtchn);
+       if (err < 0)
+               goto fail;
+
+       err = bind_evtchn_to_irqhandler(info->tx_evtchn,
+                                       xennet_interrupt,
+                                       0, info->netdev->name, info);
+       if (err < 0)
+               goto bind_fail;
+       info->rx_evtchn = info->tx_evtchn;
+       info->rx_irq = info->tx_irq = err;
+
+       return 0;
+
+bind_fail:
+       xenbus_free_evtchn(info->xbdev, info->tx_evtchn);
+       info->tx_evtchn = 0;
+fail:
+       return err;
+}
+
+static int setup_netfront_split(struct netfront_info *info)
+{
+       int err;
+
+       err = xenbus_alloc_evtchn(info->xbdev, &info->tx_evtchn);
+       if (err < 0)
+               goto fail;
+       err = xenbus_alloc_evtchn(info->xbdev, &info->rx_evtchn);
+       if (err < 0)
+               goto alloc_rx_evtchn_fail;
+
+       snprintf(info->tx_irq_name, sizeof(info->tx_irq_name),
+                "%s-tx", info->netdev->name);
+       err = bind_evtchn_to_irqhandler(info->tx_evtchn,
+                                       xennet_tx_interrupt,
+                                       0, info->tx_irq_name, info);
+       if (err < 0)
+               goto bind_tx_fail;
+       info->tx_irq = err;
+
+       snprintf(info->rx_irq_name, sizeof(info->rx_irq_name),
+                "%s-rx", info->netdev->name);
+       err = bind_evtchn_to_irqhandler(info->rx_evtchn,
+                                       xennet_rx_interrupt,
+                                       0, info->rx_irq_name, info);
+       if (err < 0)
+               goto bind_rx_fail;
+       info->rx_irq = err;
+
+       return 0;
+
+bind_rx_fail:
+       unbind_from_irqhandler(info->tx_irq, info);
+       info->tx_irq = 0;
+bind_tx_fail:
+       xenbus_free_evtchn(info->xbdev, info->rx_evtchn);
+       info->rx_evtchn = 0;
+alloc_rx_evtchn_fail:
+       xenbus_free_evtchn(info->xbdev, info->tx_evtchn);
+       info->tx_evtchn = 0;
+fail:
+       return err;
+}
+
 static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info)
 {
        struct xen_netif_tx_sring *txs;
        struct xen_netif_rx_sring *rxs;
        int err;
        struct net_device *netdev = info->netdev;
+       unsigned int feature_split_evtchn;
 
        info->tx_ring_ref = GRANT_INVALID_REF;
        info->rx_ring_ref = GRANT_INVALID_REF;
@@ -1516,6 +1605,12 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info)
        info->tx.sring = NULL;
        netdev->irq = 0;
 
+       err = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+                          "feature-split-event-channels", "%u",
+                          &feature_split_evtchn);
+       if (err < 0)
+               feature_split_evtchn = 0;
+
        err = xen_net_read_mac(dev, netdev->dev_addr);
        if (err) {
                xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename);
@@ -1532,40 +1627,50 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info)
        FRONT_RING_INIT(&info->tx, txs, PAGE_SIZE);
 
        err = xenbus_grant_ring(dev, virt_to_mfn(txs));
-       if (err < 0) {
-               free_page((unsigned long)txs);
-               goto fail;
-       }
+       if (err < 0)
+               goto grant_tx_ring_fail;
 
        info->tx_ring_ref = err;
        rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH);
        if (!rxs) {
                err = -ENOMEM;
                xenbus_dev_fatal(dev, err, "allocating rx ring page");
-               goto fail;
+               goto alloc_rx_ring_fail;
        }
        SHARED_RING_INIT(rxs);
        FRONT_RING_INIT(&info->rx, rxs, PAGE_SIZE);
 
        err = xenbus_grant_ring(dev, virt_to_mfn(rxs));
-       if (err < 0) {
-               free_page((unsigned long)rxs);
-               goto fail;
-       }
+       if (err < 0)
+               goto grant_rx_ring_fail;
        info->rx_ring_ref = err;
 
-       err = xenbus_alloc_evtchn(dev, &info->evtchn);
+       if (feature_split_evtchn)
+               err = setup_netfront_split(info);
+       /* setup single event channel if
+        *  a) feature-split-event-channels == 0
+        *  b) feature-split-event-channels == 1 but failed to setup
+        */
+       if (!feature_split_evtchn || (feature_split_evtchn && err))
+               err = setup_netfront_single(info);
+
        if (err)
-               goto fail;
+               goto alloc_evtchn_fail;
 
-       err = bind_evtchn_to_irqhandler(info->evtchn, xennet_interrupt,
-                                       0, netdev->name, netdev);
-       if (err < 0)
-               goto fail;
-       netdev->irq = err;
        return 0;
 
- fail:
+       /* If we fail to setup netfront, it is safe to just revoke access to
+        * granted pages because backend is not accessing it at this point.
+        */
+alloc_evtchn_fail:
+       gnttab_end_foreign_access_ref(info->rx_ring_ref, 0);
+grant_rx_ring_fail:
+       free_page((unsigned long)rxs);
+alloc_rx_ring_fail:
+       gnttab_end_foreign_access_ref(info->tx_ring_ref, 0);
+grant_tx_ring_fail:
+       free_page((unsigned long)txs);
+fail:
        return err;
 }
 
@@ -1601,11 +1706,27 @@ again:
                message = "writing rx ring-ref";
                goto abort_transaction;
        }
-       err = xenbus_printf(xbt, dev->nodename,
-                           "event-channel", "%u", info->evtchn);
-       if (err) {
-               message = "writing event-channel";
-               goto abort_transaction;
+
+       if (info->tx_evtchn == info->rx_evtchn) {
+               err = xenbus_printf(xbt, dev->nodename,
+                                   "event-channel", "%u", info->tx_evtchn);
+               if (err) {
+                       message = "writing event-channel";
+                       goto abort_transaction;
+               }
+       } else {
+               err = xenbus_printf(xbt, dev->nodename,
+                                   "event-channel-tx", "%u", info->tx_evtchn);
+               if (err) {
+                       message = "writing event-channel-tx";
+                       goto abort_transaction;
+               }
+               err = xenbus_printf(xbt, dev->nodename,
+                                   "event-channel-rx", "%u", info->rx_evtchn);
+               if (err) {
+                       message = "writing event-channel-rx";
+                       goto abort_transaction;
+               }
        }
 
        err = xenbus_printf(xbt, dev->nodename, "request-rx-copy", "%u",
@@ -1718,7 +1839,9 @@ static int xennet_connect(struct net_device *dev)
         * packets.
         */
        netif_carrier_on(np->netdev);
-       notify_remote_via_irq(np->netdev->irq);
+       notify_remote_via_irq(np->tx_irq);
+       if (np->tx_irq != np->rx_irq)
+               notify_remote_via_irq(np->rx_irq);
        xennet_tx_buf_gc(dev);
        xennet_alloc_rx_buffers(dev);
 
@@ -1991,7 +2114,7 @@ static int __init netif_init(void)
        if (xen_hvm_domain() && !xen_platform_pci_unplug)
                return -ENODEV;
 
-       printk(KERN_INFO "Initialising Xen virtual ethernet driver.\n");
+       pr_info("Initialising Xen virtual ethernet driver\n");
 
        return xenbus_register_frontend(&netfront_driver);
 }
index 74a852e..b0b64cc 100644 (file)
@@ -36,6 +36,16 @@ config NFC_MEI_PHY
 
          If unsure, say N.
 
+config NFC_SIM
+       tristate "NFC hardware simulator driver"
+       help
+         This driver declares two virtual NFC devices supporting NFC-DEP
+         protocol. An LLCP connection can be established between them and
+         all packets sent from one device is sent back to the other, acting as
+         loopback devices.
+
+         If unsure, say N.
+
 source "drivers/nfc/pn544/Kconfig"
 source "drivers/nfc/microread/Kconfig"
 
index aa6bd65..be7636a 100644 (file)
@@ -7,5 +7,6 @@ obj-$(CONFIG_NFC_MICROREAD)     += microread/
 obj-$(CONFIG_NFC_PN533)                += pn533.o
 obj-$(CONFIG_NFC_WILINK)       += nfcwilink.o
 obj-$(CONFIG_NFC_MEI_PHY)      += mei_phy.o
+obj-$(CONFIG_NFC_SIM)          += nfcsim.o
 
 ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
index 1201bdb..606bf55 100644 (file)
@@ -30,7 +30,7 @@ struct mei_nfc_hdr {
        u16 req_id;
        u32 reserved;
        u16 data_size;
-} __attribute__((packed));
+} __packed;
 
 #define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
 
@@ -60,8 +60,8 @@ int nfc_mei_phy_enable(void *phy_id)
 
        r = mei_cl_enable_device(phy->device);
        if (r < 0) {
-                pr_err("MEI_PHY: Could not enable device\n");
-                return r;
+               pr_err("MEI_PHY: Could not enable device\n");
+               return r;
        }
 
        r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy);
index 3420d83..cdb9f6d 100644 (file)
@@ -650,7 +650,7 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
 {
        struct microread_info *info;
        unsigned long quirks = 0;
-       u32 protocols, se;
+       u32 protocols;
        struct nfc_hci_init_data init_data;
        int r;
 
@@ -678,10 +678,8 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    NFC_PROTO_ISO14443_B_MASK |
                    NFC_PROTO_NFC_DEP_MASK;
 
-       se = NFC_SE_UICC | NFC_SE_EMBEDDED;
-
        info->hdev = nfc_hci_allocate_device(&microread_hci_ops, &init_data,
-                                            quirks, protocols, se, llc_name,
+                                            quirks, protocols, llc_name,
                                             phy_headroom +
                                             MICROREAD_CMDS_HEADROOM,
                                             phy_tailroom +
diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c
new file mode 100644 (file)
index 0000000..c5c30fb
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ * NFC hardware simulation driver
+ * Copyright (c) 2013, Intel 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/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/nfc.h>
+
+#define DEV_ERR(_dev, fmt, args...) nfc_dev_err(&_dev->nfc_dev->dev, \
+                                               "%s: " fmt, __func__, ## args)
+
+#define DEV_DBG(_dev, fmt, args...) nfc_dev_dbg(&_dev->nfc_dev->dev, \
+                                               "%s: " fmt, __func__, ## args)
+
+#define NFCSIM_VERSION "0.1"
+
+#define NFCSIM_POLL_NONE       0
+#define NFCSIM_POLL_INITIATOR  1
+#define NFCSIM_POLL_TARGET     2
+#define NFCSIM_POLL_DUAL       (NFCSIM_POLL_INITIATOR | NFCSIM_POLL_TARGET)
+
+struct nfcsim {
+       struct nfc_dev *nfc_dev;
+
+       struct mutex lock;
+
+       struct delayed_work recv_work;
+
+       struct sk_buff *clone_skb;
+
+       struct delayed_work poll_work;
+       u8 polling_mode;
+       u8 curr_polling_mode;
+
+       u8 shutting_down;
+
+       u8 up;
+
+       u8 initiator;
+
+       data_exchange_cb_t cb;
+       void *cb_context;
+
+       struct nfcsim *peer_dev;
+};
+
+static struct nfcsim *dev0;
+static struct nfcsim *dev1;
+
+struct workqueue_struct *wq;
+
+static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown)
+{
+       DEV_DBG(dev, "shutdown=%d", shutdown);
+
+       mutex_lock(&dev->lock);
+
+       dev->polling_mode = NFCSIM_POLL_NONE;
+       dev->shutting_down = shutdown;
+       dev->cb = NULL;
+       dev_kfree_skb(dev->clone_skb);
+       dev->clone_skb = NULL;
+
+       mutex_unlock(&dev->lock);
+
+       cancel_delayed_work_sync(&dev->poll_work);
+       cancel_delayed_work_sync(&dev->recv_work);
+}
+
+static int nfcsim_target_found(struct nfcsim *dev)
+{
+       struct nfc_target nfc_tgt;
+
+       DEV_DBG(dev, "");
+
+       memset(&nfc_tgt, 0, sizeof(struct nfc_target));
+
+       nfc_tgt.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+       nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1);
+
+       return 0;
+}
+
+static int nfcsim_dev_up(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       mutex_lock(&dev->lock);
+
+       dev->up = 1;
+
+       mutex_unlock(&dev->lock);
+
+       return 0;
+}
+
+static int nfcsim_dev_down(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       mutex_lock(&dev->lock);
+
+       dev->up = 0;
+
+       mutex_unlock(&dev->lock);
+
+       return 0;
+}
+
+static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev,
+                             struct nfc_target *target,
+                             u8 comm_mode, u8 *gb, size_t gb_len)
+{
+       int rc;
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+       struct nfcsim *peer = dev->peer_dev;
+       u8 *remote_gb;
+       size_t remote_gb_len;
+
+       DEV_DBG(dev, "target_idx: %d, comm_mode: %d\n", target->idx, comm_mode);
+
+       mutex_lock(&peer->lock);
+
+       nfc_tm_activated(peer->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
+                        NFC_COMM_ACTIVE, gb, gb_len);
+
+       remote_gb = nfc_get_local_general_bytes(peer->nfc_dev, &remote_gb_len);
+       if (!remote_gb) {
+               DEV_ERR(peer, "Can't get remote general bytes");
+
+               mutex_unlock(&peer->lock);
+               return -EINVAL;
+       }
+
+       mutex_unlock(&peer->lock);
+
+       mutex_lock(&dev->lock);
+
+       rc = nfc_set_remote_general_bytes(nfc_dev, remote_gb, remote_gb_len);
+       if (rc) {
+               DEV_ERR(dev, "Can't set remote general bytes");
+               mutex_unlock(&dev->lock);
+               return rc;
+       }
+
+       rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_ACTIVE,
+                               NFC_RF_INITIATOR);
+
+       mutex_unlock(&dev->lock);
+
+       return rc;
+}
+
+static int nfcsim_dep_link_down(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       nfcsim_cleanup_dev(dev, 0);
+
+       return 0;
+}
+
+static int nfcsim_start_poll(struct nfc_dev *nfc_dev,
+                            u32 im_protocols, u32 tm_protocols)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+       int rc;
+
+       mutex_lock(&dev->lock);
+
+       if (dev->polling_mode != NFCSIM_POLL_NONE) {
+               DEV_ERR(dev, "Already in polling mode");
+               rc = -EBUSY;
+               goto exit;
+       }
+
+       if (im_protocols & NFC_PROTO_NFC_DEP_MASK)
+               dev->polling_mode |= NFCSIM_POLL_INITIATOR;
+
+       if (tm_protocols & NFC_PROTO_NFC_DEP_MASK)
+               dev->polling_mode |= NFCSIM_POLL_TARGET;
+
+       if (dev->polling_mode == NFCSIM_POLL_NONE) {
+               DEV_ERR(dev, "Unsupported polling mode");
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       dev->initiator = 0;
+       dev->curr_polling_mode = NFCSIM_POLL_NONE;
+
+       queue_delayed_work(wq, &dev->poll_work, 0);
+
+       DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X", im_protocols,
+               tm_protocols);
+
+       rc = 0;
+exit:
+       mutex_unlock(&dev->lock);
+
+       return rc;
+}
+
+static void nfcsim_stop_poll(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "Stop poll");
+
+       mutex_lock(&dev->lock);
+
+       dev->polling_mode = NFCSIM_POLL_NONE;
+
+       mutex_unlock(&dev->lock);
+
+       cancel_delayed_work_sync(&dev->poll_work);
+}
+
+static int nfcsim_activate_target(struct nfc_dev *nfc_dev,
+                                 struct nfc_target *target, u32 protocol)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       return -ENOTSUPP;
+}
+
+static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev,
+                                    struct nfc_target *target)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+}
+
+static void nfcsim_wq_recv(struct work_struct *work)
+{
+       struct nfcsim *dev = container_of(work, struct nfcsim,
+                                         recv_work.work);
+
+       mutex_lock(&dev->lock);
+
+       if (dev->shutting_down || !dev->up || !dev->clone_skb) {
+               dev_kfree_skb(dev->clone_skb);
+               goto exit;
+       }
+
+       if (dev->initiator) {
+               if (!dev->cb) {
+                       DEV_ERR(dev, "Null recv callback");
+                       dev_kfree_skb(dev->clone_skb);
+                       goto exit;
+               }
+
+               dev->cb(dev->cb_context, dev->clone_skb, 0);
+               dev->cb = NULL;
+       } else {
+               nfc_tm_data_received(dev->nfc_dev, dev->clone_skb);
+       }
+
+exit:
+       dev->clone_skb = NULL;
+
+       mutex_unlock(&dev->lock);
+}
+
+static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target,
+                    struct sk_buff *skb, data_exchange_cb_t cb,
+                    void *cb_context)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+       struct nfcsim *peer = dev->peer_dev;
+       int err;
+
+       mutex_lock(&dev->lock);
+
+       if (dev->shutting_down || !dev->up) {
+               mutex_unlock(&dev->lock);
+               err = -ENODEV;
+               goto exit;
+       }
+
+       dev->cb = cb;
+       dev->cb_context = cb_context;
+
+       mutex_unlock(&dev->lock);
+
+       mutex_lock(&peer->lock);
+
+       peer->clone_skb = skb_clone(skb, GFP_KERNEL);
+
+       if (!peer->clone_skb) {
+               DEV_ERR(dev, "skb_clone failed");
+               mutex_unlock(&peer->lock);
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       /* This simulates an arbitrary transmission delay between the 2 devices.
+        * If packet transmission occurs immediately between them, we have a
+        * non-stop flow of several tens of thousands SYMM packets per second
+        * and a burning cpu.
+        *
+        * TODO: Add support for a sysfs entry to control this delay.
+        */
+       queue_delayed_work(wq, &peer->recv_work, msecs_to_jiffies(5));
+
+       mutex_unlock(&peer->lock);
+
+       err = 0;
+exit:
+       dev_kfree_skb(skb);
+
+       return err;
+}
+
+static int nfcsim_im_transceive(struct nfc_dev *nfc_dev,
+                               struct nfc_target *target, struct sk_buff *skb,
+                               data_exchange_cb_t cb, void *cb_context)
+{
+       return nfcsim_tx(nfc_dev, target, skb, cb, cb_context);
+}
+
+static int nfcsim_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+       return nfcsim_tx(nfc_dev, NULL, skb, NULL, NULL);
+}
+
+static struct nfc_ops nfcsim_nfc_ops = {
+       .dev_up = nfcsim_dev_up,
+       .dev_down = nfcsim_dev_down,
+       .dep_link_up = nfcsim_dep_link_up,
+       .dep_link_down = nfcsim_dep_link_down,
+       .start_poll = nfcsim_start_poll,
+       .stop_poll = nfcsim_stop_poll,
+       .activate_target = nfcsim_activate_target,
+       .deactivate_target = nfcsim_deactivate_target,
+       .im_transceive = nfcsim_im_transceive,
+       .tm_send = nfcsim_tm_send,
+};
+
+static void nfcsim_set_polling_mode(struct nfcsim *dev)
+{
+       if (dev->polling_mode == NFCSIM_POLL_NONE) {
+               dev->curr_polling_mode = NFCSIM_POLL_NONE;
+               return;
+       }
+
+       if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
+               if (dev->polling_mode & NFCSIM_POLL_INITIATOR)
+                       dev->curr_polling_mode = NFCSIM_POLL_INITIATOR;
+               else
+                       dev->curr_polling_mode = NFCSIM_POLL_TARGET;
+
+               return;
+       }
+
+       if (dev->polling_mode == NFCSIM_POLL_DUAL) {
+               if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
+                       dev->curr_polling_mode = NFCSIM_POLL_INITIATOR;
+               else
+                       dev->curr_polling_mode = NFCSIM_POLL_TARGET;
+       }
+}
+
+static void nfcsim_wq_poll(struct work_struct *work)
+{
+       struct nfcsim *dev = container_of(work, struct nfcsim, poll_work.work);
+       struct nfcsim *peer = dev->peer_dev;
+
+       /* These work items run on an ordered workqueue and are therefore
+        * serialized. So we can take both mutexes without being dead locked.
+        */
+       mutex_lock(&dev->lock);
+       mutex_lock(&peer->lock);
+
+       nfcsim_set_polling_mode(dev);
+
+       if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
+               DEV_DBG(dev, "Not polling");
+               goto unlock;
+       }
+
+       DEV_DBG(dev, "Polling as %s",
+               dev->curr_polling_mode == NFCSIM_POLL_INITIATOR ?
+               "initiator" : "target");
+
+       if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
+               goto sched_work;
+
+       if (peer->curr_polling_mode == NFCSIM_POLL_TARGET) {
+               peer->polling_mode = NFCSIM_POLL_NONE;
+               dev->polling_mode = NFCSIM_POLL_NONE;
+
+               dev->initiator = 1;
+
+               nfcsim_target_found(dev);
+
+               goto unlock;
+       }
+
+sched_work:
+       /* This defines the delay for an initiator to check if the other device
+        * is polling in target mode.
+        * If the device starts in dual mode polling, it switches between
+        * initiator and target at every round.
+        * Because the wq is ordered and only 1 work item is executed at a time,
+        * we'll always have one device polling as initiator and the other as
+        * target at some point, even if both are started in dual mode.
+        */
+       queue_delayed_work(wq, &dev->poll_work, msecs_to_jiffies(200));
+
+unlock:
+       mutex_unlock(&peer->lock);
+       mutex_unlock(&dev->lock);
+}
+
+static struct nfcsim *nfcsim_init_dev(void)
+{
+       struct nfcsim *dev;
+       int rc = -ENOMEM;
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (dev == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       mutex_init(&dev->lock);
+
+       INIT_DELAYED_WORK(&dev->recv_work, nfcsim_wq_recv);
+       INIT_DELAYED_WORK(&dev->poll_work, nfcsim_wq_poll);
+
+       dev->nfc_dev = nfc_allocate_device(&nfcsim_nfc_ops,
+                                          NFC_PROTO_NFC_DEP_MASK,
+                                          0, 0);
+       if (!dev->nfc_dev)
+               goto error;
+
+       nfc_set_drvdata(dev->nfc_dev, dev);
+
+       rc = nfc_register_device(dev->nfc_dev);
+       if (rc)
+               goto free_nfc_dev;
+
+       return dev;
+
+free_nfc_dev:
+       nfc_free_device(dev->nfc_dev);
+
+error:
+       kfree(dev);
+
+       return ERR_PTR(rc);
+}
+
+static void nfcsim_free_device(struct nfcsim *dev)
+{
+       nfc_unregister_device(dev->nfc_dev);
+
+       nfc_free_device(dev->nfc_dev);
+
+       kfree(dev);
+}
+
+int __init nfcsim_init(void)
+{
+       int rc;
+
+       /* We need an ordered wq to ensure that poll_work items are executed
+        * one at a time.
+        */
+       wq = alloc_ordered_workqueue("nfcsim", 0);
+       if (!wq) {
+               rc = -ENOMEM;
+               goto exit;
+       }
+
+       dev0 = nfcsim_init_dev();
+       if (IS_ERR(dev0)) {
+               rc = PTR_ERR(dev0);
+               goto exit;
+       }
+
+       dev1 = nfcsim_init_dev();
+       if (IS_ERR(dev1)) {
+               kfree(dev0);
+
+               rc = PTR_ERR(dev1);
+               goto exit;
+       }
+
+       dev0->peer_dev = dev1;
+       dev1->peer_dev = dev0;
+
+       pr_debug("NFCsim " NFCSIM_VERSION " initialized\n");
+
+       rc = 0;
+exit:
+       if (rc)
+               pr_err("Failed to initialize nfcsim driver (%d)\n",
+                      rc);
+
+       return rc;
+}
+
+void __exit nfcsim_exit(void)
+{
+       nfcsim_cleanup_dev(dev0, 1);
+       nfcsim_cleanup_dev(dev1, 1);
+
+       nfcsim_free_device(dev0);
+       nfcsim_free_device(dev1);
+
+       destroy_workqueue(wq);
+}
+
+module_init(nfcsim_init);
+module_exit(nfcsim_exit);
+
+MODULE_DESCRIPTION("NFCSim driver ver " NFCSIM_VERSION);
+MODULE_VERSION(NFCSIM_VERSION);
+MODULE_LICENSE("GPL");
index 3b731ac..59f95d8 100644 (file)
@@ -109,7 +109,7 @@ enum {
        NFCWILINK_FW_DOWNLOAD,
 };
 
-static int nfcwilink_send(struct sk_buff *skb);
+static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb);
 
 static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how)
 {
@@ -156,8 +156,6 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
                return -ENOMEM;
        }
 
-       skb->dev = (void *)drv->ndev;
-
        cmd = (struct nci_vs_nfcc_info_cmd *)
                        skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd));
        cmd->gid = NCI_VS_NFCC_INFO_CMD_GID;
@@ -166,7 +164,7 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
 
        drv->nfcc_info.plen = 0;
 
-       rc = nfcwilink_send(skb);
+       rc = nfcwilink_send(drv->ndev, skb);
        if (rc)
                return rc;
 
@@ -232,11 +230,9 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
                return -ENOMEM;
        }
 
-       skb->dev = (void *)drv->ndev;
-
        memcpy(skb_put(skb, len), data, len);
 
-       rc = nfcwilink_send(skb);
+       rc = nfcwilink_send(drv->ndev, skb);
        if (rc)
                return rc;
 
@@ -371,10 +367,8 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
                return 0;
        }
 
-       skb->dev = (void *) drv->ndev;
-
        /* Forward skb to NCI core layer */
-       rc = nci_recv_frame(skb);
+       rc = nci_recv_frame(drv->ndev, skb);
        if (rc < 0) {
                nfc_dev_err(&drv->pdev->dev, "nci_recv_frame failed %d", rc);
                return rc;
@@ -480,9 +474,8 @@ static int nfcwilink_close(struct nci_dev *ndev)
        return rc;
 }
 
-static int nfcwilink_send(struct sk_buff *skb)
+static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb)
 {
-       struct nci_dev *ndev = (struct nci_dev *)skb->dev;
        struct nfcwilink *drv = nci_get_drvdata(ndev);
        struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000};
        long len;
@@ -542,7 +535,6 @@ static int nfcwilink_probe(struct platform_device *pdev)
 
        drv->ndev = nci_allocate_device(&nfcwilink_ops,
                                        protocols,
-                                       NFC_SE_NONE,
                                        NFCWILINK_HDR_LEN,
                                        0);
        if (!drv->ndev) {
index ec269e6..daf92ac 100644 (file)
@@ -258,7 +258,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
                                .opcode = PN533_FELICA_OPC_SENSF_REQ,
                                .sc = PN533_FELICA_SENSF_SC_ALL,
                                .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
-                               .tsn = 0,
+                               .tsn = 0x03,
                        },
                },
                .len = 7,
@@ -271,7 +271,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
                                .opcode = PN533_FELICA_OPC_SENSF_REQ,
                                .sc = PN533_FELICA_SENSF_SC_ALL,
                                .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
-                               .tsn = 0,
+                               .tsn = 0x03,
                        },
                 },
                .len = 7,
@@ -1235,7 +1235,7 @@ static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data,
 struct pn533_target_felica {
        u8 pol_res;
        u8 opcode;
-       u8 nfcid2[8];
+       u8 nfcid2[NFC_NFCID2_MAXSIZE];
        u8 pad[8];
        /* optional */
        u8 syst_code[];
@@ -1275,6 +1275,9 @@ static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data,
        memcpy(nfc_tgt->sensf_res, &tgt_felica->opcode, 9);
        nfc_tgt->sensf_res_len = 9;
 
+       memcpy(nfc_tgt->nfcid2, tgt_felica->nfcid2, NFC_NFCID2_MAXSIZE);
+       nfc_tgt->nfcid2_len = NFC_NFCID2_MAXSIZE;
+
        return 0;
 }
 
@@ -2084,6 +2087,9 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
        if (comm_mode == NFC_COMM_PASSIVE)
                skb_len += PASSIVE_DATA_LEN;
 
+       if (target && target->nfcid2_len)
+               skb_len += NFC_NFCID3_MAXSIZE;
+
        skb = pn533_alloc_skb(dev, skb_len);
        if (!skb)
                return -ENOMEM;
@@ -2100,6 +2106,12 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                *next |= 1;
        }
 
+       if (target && target->nfcid2_len) {
+               memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), target->nfcid2,
+                      target->nfcid2_len);
+               *next |= 2;
+       }
+
        if (gb != NULL && gb_len > 0) {
                memcpy(skb_put(skb, gb_len), gb, gb_len);
                *next |= 4; /* We have some Gi */
@@ -2489,7 +2501,7 @@ static void pn533_acr122_poweron_rdr_resp(struct urb *urb)
 
        nfc_dev_dbg(&urb->dev->dev, "%s", __func__);
 
-       print_hex_dump(KERN_ERR, "ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1,
+       print_hex_dump_debug("ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1,
                       urb->transfer_buffer, urb->transfer_buffer_length,
                       false);
 
@@ -2520,7 +2532,7 @@ static int pn533_acr122_poweron_rdr(struct pn533 *dev)
        dev->out_urb->transfer_buffer = cmd;
        dev->out_urb->transfer_buffer_length = sizeof(cmd);
 
-       print_hex_dump(KERN_ERR, "ACR122 TX: ", DUMP_PREFIX_NONE, 16, 1,
+       print_hex_dump_debug("ACR122 TX: ", DUMP_PREFIX_NONE, 16, 1,
                       cmd, sizeof(cmd), false);
 
        rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
@@ -2774,17 +2786,18 @@ static int pn533_probe(struct usb_interface *interface,
                goto destroy_wq;
 
        nfc_dev_info(&dev->interface->dev,
-                    "NXP PN533 firmware ver %d.%d now attached",
-                    fw_ver.ver, fw_ver.rev);
+                    "NXP PN5%02X firmware ver %d.%d now attached",
+                    fw_ver.ic, fw_ver.ver, fw_ver.rev);
 
 
        dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols,
-                                          NFC_SE_NONE,
                                           dev->ops->tx_header_len +
                                           PN533_CMD_DATAEXCH_HEAD_LEN,
                                           dev->ops->tx_tail_len);
-       if (!dev->nfc_dev)
+       if (!dev->nfc_dev) {
+               rc = -ENOMEM;
                goto destroy_wq;
+       }
 
        nfc_set_parent_dev(dev->nfc_dev, &interface->dev);
        nfc_set_drvdata(dev->nfc_dev, dev);
index 9c5f16e..0d17da7 100644 (file)
@@ -551,20 +551,25 @@ static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
                        return -EPROTO;
                }
 
-               r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
-                                    PN544_RF_READER_CMD_ACTIVATE_NEXT,
-                                    uid_skb->data, uid_skb->len, NULL);
-               kfree_skb(uid_skb);
-
-               r = nfc_hci_send_cmd(hdev,
+               /* Type F NFC-DEP IDm has prefix 0x01FE */
+               if ((uid_skb->data[0] == 0x01) && (uid_skb->data[1] == 0xfe)) {
+                       kfree_skb(uid_skb);
+                       r = nfc_hci_send_cmd(hdev,
                                        PN544_RF_READER_NFCIP1_INITIATOR_GATE,
                                        PN544_HCI_CMD_CONTINUE_ACTIVATION,
                                        NULL, 0, NULL);
-               if (r < 0)
-                       return r;
+                       if (r < 0)
+                               return r;
 
-               target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE;
-               target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+                       target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+                       target->hci_reader_gate =
+                               PN544_RF_READER_NFCIP1_INITIATOR_GATE;
+               } else {
+                       r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
+                                            PN544_RF_READER_CMD_ACTIVATE_NEXT,
+                                            uid_skb->data, uid_skb->len, NULL);
+                       kfree_skb(uid_skb);
+               }
        } else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) {
                /*
                 * TODO: maybe other ISO 14443 require some kind of continue
@@ -706,12 +711,9 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
                 return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE,
                                     PN544_RF_READER_CMD_ACTIVATE_NEXT,
                                     target->nfcid1, target->nfcid1_len, NULL);
-       } else if (target->supported_protocols & NFC_PROTO_JEWEL_MASK) {
-               return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-                                       PN544_JEWEL_RAW_CMD, NULL, 0, NULL);
-       } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) {
-               return nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
-                                       PN544_FELICA_RAW, NULL, 0, NULL);
+       } else if (target->supported_protocols & (NFC_PROTO_JEWEL_MASK |
+                                               NFC_PROTO_FELICA_MASK)) {
+               return -EOPNOTSUPP;
        } else if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) {
                return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
                                        PN544_HCI_CMD_ATTREQUEST,
@@ -801,7 +803,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    struct nfc_hci_dev **hdev)
 {
        struct pn544_hci_info *info;
-       u32 protocols, se;
+       u32 protocols;
        struct nfc_hci_init_data init_data;
        int r;
 
@@ -834,10 +836,8 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    NFC_PROTO_ISO14443_B_MASK |
                    NFC_PROTO_NFC_DEP_MASK;
 
-       se = NFC_SE_UICC | NFC_SE_EMBEDDED;
-
        info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0,
-                                            protocols, se, llc_name,
+                                            protocols, llc_name,
                                             phy_headroom + PN544_CMDS_HEADROOM,
                                             phy_tailroom, phy_payload);
        if (!info->hdev) {
index ffab033..ea174c8 100644 (file)
@@ -22,6 +22,7 @@ static const char *phy_modes[] = {
        [PHY_INTERFACE_MODE_GMII]       = "gmii",
        [PHY_INTERFACE_MODE_SGMII]      = "sgmii",
        [PHY_INTERFACE_MODE_TBI]        = "tbi",
+       [PHY_INTERFACE_MODE_REVMII]     = "rev-mii",
        [PHY_INTERFACE_MODE_RMII]       = "rmii",
        [PHY_INTERFACE_MODE_RGMII]      = "rgmii",
        [PHY_INTERFACE_MODE_RGMII_ID]   = "rgmii-id",
index 059cd15..5758033 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/of_platform.h>
 #include <linux/module.h>
+#include <linux/reboot.h>
 #include <asm/system_misc.h>
 
 static void restart_poweroff_do_poweroff(void)
 {
-       arm_pm_restart('h', NULL);
+       arm_pm_restart(REBOOT_HARD, NULL);
 }
 
 static int restart_poweroff_probe(struct platform_device *pdev)
index 469e696..476aa49 100644 (file)
@@ -48,7 +48,7 @@ static void vexpress_power_off(void)
 
 static struct device *vexpress_restart_device;
 
-static void vexpress_restart(char str, const char *cmd)
+static void vexpress_restart(enum reboot_mode reboot_mode, const char *cmd)
 {
        vexpress_reset_do(vexpress_restart_device, "restart");
 }
index 90a3e86..767fee2 100644 (file)
@@ -261,7 +261,12 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, rtc_data);
 
-       stmp_reset_block(rtc_data->io);
+       err = stmp_reset_block(rtc_data->io);
+       if (err) {
+               dev_err(&pdev->dev, "stmp_reset_block failed: %d\n", err);
+               return err;
+       }
+
        writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
                        STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN |
                        STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE,
index 9ca3996..279ad50 100644 (file)
@@ -130,26 +130,6 @@ static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)
 /**
  * some more debug stuff
  */
-#define IUCV_HEXDUMP16(importance,header,ptr) \
-PRINT_##importance(header "%02x %02x %02x %02x  %02x %02x %02x %02x  " \
-                  "%02x %02x %02x %02x  %02x %02x %02x %02x\n", \
-                  *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
-                  *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
-                  *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
-                  *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
-                  *(((char*)ptr)+12),*(((char*)ptr)+13), \
-                  *(((char*)ptr)+14),*(((char*)ptr)+15)); \
-PRINT_##importance(header "%02x %02x %02x %02x  %02x %02x %02x %02x  " \
-                  "%02x %02x %02x %02x  %02x %02x %02x %02x\n", \
-                  *(((char*)ptr)+16),*(((char*)ptr)+17), \
-                  *(((char*)ptr)+18),*(((char*)ptr)+19), \
-                  *(((char*)ptr)+20),*(((char*)ptr)+21), \
-                  *(((char*)ptr)+22),*(((char*)ptr)+23), \
-                  *(((char*)ptr)+24),*(((char*)ptr)+25), \
-                  *(((char*)ptr)+26),*(((char*)ptr)+27), \
-                  *(((char*)ptr)+28),*(((char*)ptr)+29), \
-                  *(((char*)ptr)+30),*(((char*)ptr)+31));
-
 #define PRINTK_HEADER " iucv: "       /* for debugging */
 
 /* dummy device to make sure netiucv_pm functions are called */
index c4f392d..41ef943 100644 (file)
@@ -738,7 +738,7 @@ struct qeth_rx {
        int qdio_err;
 };
 
-#define QETH_NAPI_WEIGHT 128
+#define QETH_NAPI_WEIGHT NAPI_POLL_WEIGHT
 
 struct qeth_card {
        struct list_head list;
index 70ce6b6..0a328d0 100644 (file)
@@ -1282,8 +1282,10 @@ static void qeth_free_qdio_buffers(struct qeth_card *card)
 
        qeth_free_cq(card);
        cancel_delayed_work_sync(&card->buffer_reclaim_work);
-       for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
-               dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb);
+       for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {
+               if (card->qdio.in_q->bufs[j].rx_skb)
+                       dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb);
+       }
        kfree(card->qdio.in_q);
        card->qdio.in_q = NULL;
        /* inbound buffer pool */
@@ -1729,14 +1731,14 @@ static void qeth_configure_blkt_default(struct qeth_card *card, char *prcd)
        QETH_DBF_TEXT(SETUP, 2, "cfgblkt");
 
        if (prcd[74] == 0xF0 && prcd[75] == 0xF0 &&
-           (prcd[76] == 0xF5 || prcd[76] == 0xF6)) {
-               card->info.blkt.time_total = 250;
-               card->info.blkt.inter_packet = 5;
-               card->info.blkt.inter_packet_jumbo = 15;
-       } else {
+           prcd[76] >= 0xF1 && prcd[76] <= 0xF4) {
                card->info.blkt.time_total = 0;
                card->info.blkt.inter_packet = 0;
                card->info.blkt.inter_packet_jumbo = 0;
+       } else {
+               card->info.blkt.time_total = 250;
+               card->info.blkt.inter_packet = 5;
+               card->info.blkt.inter_packet_jumbo = 15;
        }
 }
 
@@ -2198,11 +2200,11 @@ static inline int qeth_get_initial_mtu_for_card(struct qeth_card *card)
                case QETH_LINK_TYPE_LANE_TR:
                        return 2000;
                default:
-                       return 1492;
+                       return card->options.layer2 ? 1500 : 1492;
                }
        case QETH_CARD_TYPE_OSM:
        case QETH_CARD_TYPE_OSX:
-               return 1492;
+               return card->options.layer2 ? 1500 : 1492;
        default:
                return 1500;
        }
@@ -2275,9 +2277,10 @@ static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply,
                card->info.max_mtu = mtu;
                card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE;
        } else {
-               card->info.initial_mtu = qeth_get_initial_mtu_for_card(card);
                card->info.max_mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU(
                        iob->data);
+               card->info.initial_mtu = min(card->info.max_mtu,
+                                       qeth_get_initial_mtu_for_card(card));
                card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT;
        }
 
index e659feb..5a9f842 100644 (file)
@@ -433,7 +433,7 @@ static inline unsigned int calc_tx_flits_ofld(const struct sk_buff *skb)
                return DIV_ROUND_UP(skb->len, 8);
        flits = skb_transport_offset(skb) / 8;
        cnt = skb_shinfo(skb)->nr_frags;
-       if (skb->tail != skb->transport_header)
+       if (skb_tail_pointer(skb) != skb_transport_header(skb))
                cnt++;
        return flits + sgl_len(cnt);
 }
index 32ae6c6..4a05d04 100644 (file)
@@ -1978,7 +1978,7 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 {
        struct fcoe_ctlr_device *cdev;
        struct fc_lport *lport = NULL;
-       struct net_device *netdev = ptr;
+       struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
        struct fcoe_ctlr *ctlr;
        struct fcoe_interface *fcoe;
        struct fcoe_port *port;
index f3a5a53..01adbe0 100644 (file)
@@ -704,7 +704,7 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer)
 static int libfcoe_device_notification(struct notifier_block *notifier,
                                    ulong event, void *ptr)
 {
-       struct net_device *netdev = ptr;
+       struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
 
        switch (event) {
        case NETDEV_UNREGISTER:
index 92deec5..1d58d53 100644 (file)
@@ -906,7 +906,6 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
                        ISCSI_DBG_TCP(conn, "no more data avail. Consumed %d\n",
                                      consumed);
                        *status = ISCSI_TCP_SKB_DONE;
-                       skb_abort_seq_read(&seq);
                        goto skb_done;
                }
                BUG_ON(segment->copied >= segment->size);
index 0a537a0..d055450 100644 (file)
@@ -439,10 +439,7 @@ static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
 
        act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents,
                                      arr, arr_len);
-       if (sdb->resid)
-               sdb->resid -= act_len;
-       else
-               sdb->resid = scsi_bufflen(scp) - act_len;
+       sdb->resid = scsi_bufflen(scp) - act_len;
 
        return 0;
 }
@@ -1693,24 +1690,48 @@ static int check_device_access_params(struct sdebug_dev_info *devi,
        return 0;
 }
 
+/* Returns number of bytes copied or -1 if error. */
 static int do_device_access(struct scsi_cmnd *scmd,
                            struct sdebug_dev_info *devi,
                            unsigned long long lba, unsigned int num, int write)
 {
        int ret;
        unsigned long long block, rest = 0;
-       int (*func)(struct scsi_cmnd *, unsigned char *, int);
+       struct scsi_data_buffer *sdb;
+       enum dma_data_direction dir;
+       size_t (*func)(struct scatterlist *, unsigned int, void *, size_t,
+                      off_t);
+
+       if (write) {
+               sdb = scsi_out(scmd);
+               dir = DMA_TO_DEVICE;
+               func = sg_pcopy_to_buffer;
+       } else {
+               sdb = scsi_in(scmd);
+               dir = DMA_FROM_DEVICE;
+               func = sg_pcopy_from_buffer;
+       }
 
-       func = write ? fetch_to_dev_buffer : fill_from_dev_buffer;
+       if (!sdb->length)
+               return 0;
+       if (!(scsi_bidi_cmnd(scmd) || scmd->sc_data_direction == dir))
+               return -1;
 
        block = do_div(lba, sdebug_store_sectors);
        if (block + num > sdebug_store_sectors)
                rest = block + num - sdebug_store_sectors;
 
-       ret = func(scmd, fake_storep + (block * scsi_debug_sector_size),
-                  (num - rest) * scsi_debug_sector_size);
-       if (!ret && rest)
-               ret = func(scmd, fake_storep, rest * scsi_debug_sector_size);
+       ret = func(sdb->table.sgl, sdb->table.nents,
+                  fake_storep + (block * scsi_debug_sector_size),
+                  (num - rest) * scsi_debug_sector_size, 0);
+       if (ret != (num - rest) * scsi_debug_sector_size)
+               return ret;
+
+       if (rest) {
+               ret += func(sdb->table.sgl, sdb->table.nents,
+                           fake_storep, rest * scsi_debug_sector_size,
+                           (num - rest) * scsi_debug_sector_size);
+       }
 
        return ret;
 }
@@ -1849,7 +1870,12 @@ static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba,
        read_lock_irqsave(&atomic_rw, iflags);
        ret = do_device_access(SCpnt, devip, lba, num, 0);
        read_unlock_irqrestore(&atomic_rw, iflags);
-       return ret;
+       if (ret == -1)
+               return DID_ERROR << 16;
+
+       scsi_in(SCpnt)->resid = scsi_bufflen(SCpnt) - ret;
+
+       return 0;
 }
 
 void dump_sector(unsigned char *buf, int len)
index 720665c..e84cf04 100644 (file)
@@ -9,6 +9,19 @@
 
 #include "ssb_private.h"
 
+static struct resource ssb_sflash_resource = {
+       .name   = "ssb_sflash",
+       .start  = SSB_FLASH2,
+       .end    = 0,
+       .flags  = IORESOURCE_MEM | IORESOURCE_READONLY,
+};
+
+struct platform_device ssb_sflash_dev = {
+       .name           = "ssb_sflash",
+       .resource       = &ssb_sflash_resource,
+       .num_resources  = 1,
+};
+
 struct ssb_sflash_tbl_e {
        char *name;
        u32 id;
@@ -16,7 +29,7 @@ struct ssb_sflash_tbl_e {
        u16 numblocks;
 };
 
-static struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
+static const struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
        { "M25P20", 0x11, 0x10000, 4, },
        { "M25P40", 0x12, 0x10000, 8, },
 
@@ -27,7 +40,7 @@ static struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
        { 0 },
 };
 
-static struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
+static const struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
        { "SST25WF512", 1, 0x1000, 16, },
        { "SST25VF512", 0x48, 0x1000, 16, },
        { "SST25WF010", 2, 0x1000, 32, },
@@ -45,7 +58,7 @@ static struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
        { 0 },
 };
 
-static struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = {
+static const struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = {
        { "AT45DB011", 0xc, 256, 512, },
        { "AT45DB021", 0x14, 256, 1024, },
        { "AT45DB041", 0x1c, 256, 2048, },
@@ -73,7 +86,8 @@ static void ssb_sflash_cmd(struct ssb_chipcommon *cc, u32 opcode)
 /* Initialize serial flash access */
 int ssb_sflash_init(struct ssb_chipcommon *cc)
 {
-       struct ssb_sflash_tbl_e *e;
+       struct ssb_sflash *sflash = &cc->dev->bus->mipscore.sflash;
+       const struct ssb_sflash_tbl_e *e;
        u32 id, id2;
 
        switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) {
@@ -131,9 +145,21 @@ int ssb_sflash_init(struct ssb_chipcommon *cc)
                return -ENOTSUPP;
        }
 
+       sflash->window = SSB_FLASH2;
+       sflash->blocksize = e->blocksize;
+       sflash->numblocks = e->numblocks;
+       sflash->size = sflash->blocksize * sflash->numblocks;
+       sflash->present = true;
+
        pr_info("Found %s serial flash (blocksize: 0x%X, blocks: %d)\n",
                e->name, e->blocksize, e->numblocks);
 
+       /* Prepare platform device, but don't register it yet. It's too early,
+        * malloc (required by device_private_init) is not available yet. */
+       ssb_sflash_dev.resource[0].end = ssb_sflash_dev.resource[0].start +
+                                        sflash->size;
+       ssb_sflash_dev.dev.platform_data = sflash;
+
        pr_err("Serial flash support is not implemented yet!\n");
 
        return -ENOTSUPP;
index 812775a..e55ddf7 100644 (file)
@@ -553,6 +553,14 @@ static int ssb_devices_register(struct ssb_bus *bus)
        }
 #endif
 
+#ifdef CONFIG_SSB_SFLASH
+       if (bus->mipscore.sflash.present) {
+               err = platform_device_register(&ssb_sflash_dev);
+               if (err)
+                       pr_err("Error registering serial flash\n");
+       }
+#endif
+
        return 0;
 error:
        /* Unwind the already registered devices. */
index 32ed1fa..69161bb 100644 (file)
@@ -38,7 +38,7 @@ static int ssb_pcihost_resume(struct pci_dev *dev)
        struct ssb_bus *ssb = pci_get_drvdata(dev);
        int err;
 
-       pci_set_power_state(dev, 0);
+       pci_set_power_state(dev, PCI_D0);
        err = pci_enable_device(dev);
        if (err)
                return err;
index a3b2364..e753fbe 100644 (file)
@@ -54,7 +54,7 @@ static int hex2sprom(u16 *sprom, const char *dump, size_t len,
        while (cnt < sprom_size_words) {
                memcpy(tmp, dump, 4);
                dump += 4;
-               err = strict_strtoul(tmp, 16, &parsed);
+               err = kstrtoul(tmp, 16, &parsed);
                if (err)
                        return err;
                sprom[cnt++] = swab16((u16)parsed);
index 4671f17..eb507a5 100644 (file)
@@ -243,6 +243,10 @@ static inline int ssb_sflash_init(struct ssb_chipcommon *cc)
 extern struct platform_device ssb_pflash_dev;
 #endif
 
+#ifdef CONFIG_SSB_SFLASH
+extern struct platform_device ssb_sflash_dev;
+#endif
+
 #ifdef CONFIG_SSB_DRIVER_EXTIF
 extern u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks);
 extern u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms);
index 5ead2d4..9c716c1 100644 (file)
@@ -2891,7 +2891,7 @@ void uf_net_get_name(struct net_device *dev, char *name, int len)
  */
 static int
 uf_netdev_event(struct notifier_block *notif, unsigned long event, void* ptr) {
-    struct net_device *netdev = ptr;
+    struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
     netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(netdev);
     unifi_priv_t *priv = NULL;
     static const CsrWifiMacAddress broadcast_address = {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
index 94e426e..b2330f1 100644 (file)
@@ -164,7 +164,7 @@ static const struct file_operations ft1000_proc_fops = {
 static int ft1000NotifyProc(struct notifier_block *this, unsigned long event,
                                void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct ft1000_info *info;
 
        info = netdev_priv(dev);
index eca6f02..5ead942 100644 (file)
@@ -166,7 +166,7 @@ static const struct file_operations ft1000_proc_fops = {
 static int
 ft1000NotifyProc(struct notifier_block *this, unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct ft1000_info *info;
        struct proc_dir_entry *ft1000_proc_file;
 
index 4a7eedf..9176a81 100644 (file)
@@ -359,17 +359,12 @@ static void ipu_crtc_commit(struct drm_crtc *crtc)
        ipu_fb_enable(ipu_crtc);
 }
 
-static void ipu_crtc_load_lut(struct drm_crtc *crtc)
-{
-}
-
 static struct drm_crtc_helper_funcs ipu_helper_funcs = {
        .dpms = ipu_crtc_dpms,
        .mode_fixup = ipu_crtc_mode_fixup,
        .mode_set = ipu_crtc_mode_set,
        .prepare = ipu_crtc_prepare,
        .commit = ipu_crtc_commit,
-       .load_lut = ipu_crtc_load_lut,
 };
 
 static int ipu_enable_vblank(struct drm_crtc *crtc)
index f050808..a8e9c0c 100644 (file)
@@ -60,8 +60,6 @@ truncate_complete_page(struct address_space *mapping, struct page *page)
        ll_delete_from_page_cache(page);
 }
 
-#  define d_refcount(d)                 ((d)->d_count)
-
 #ifdef ATTR_OPEN
 # define ATTR_FROM_OPEN ATTR_OPEN
 #else
index b4db6cb..eb59ac7 100644 (file)
@@ -99,7 +99,7 @@ static inline void l_dput(struct dentry *de)
        if (!de || IS_ERR(de))
                return;
        //shrink_dcache_parent(de);
-       LASSERT(d_refcount(de) > 0);
+       LASSERT(d_count(de) > 0);
        dput(de);
 }
 
index e770d02..55f1822 100644 (file)
@@ -53,7 +53,7 @@ struct lprocfs_vars {
        /**
         * /proc file mode.
         */
-       mode_t                  proc_mode;
+       umode_t                 proc_mode;
 };
 
 struct lprocfs_static_vars {
@@ -600,11 +600,11 @@ extern int lprocfs_obd_setup(struct obd_device *obd, struct lprocfs_vars *list);
 extern int lprocfs_obd_cleanup(struct obd_device *obd);
 
 extern int lprocfs_seq_create(proc_dir_entry_t *parent, const char *name,
-                             mode_t mode,
+                             umode_t mode,
                              const struct file_operations *seq_fops,
                              void *data);
 extern int lprocfs_obd_seq_create(struct obd_device *dev, const char *name,
-                                 mode_t mode,
+                                 umode_t mode,
                                  const struct file_operations *seq_fops,
                                  void *data);
 
index 7d6abff..ff0d085 100644 (file)
@@ -98,7 +98,7 @@ int ll_dcompare(const struct dentry *parent, const struct inode *pinode,
 
        CDEBUG(D_DENTRY, "found name %.*s(%p) flags %#x refc %d\n",
               name->len, name->name, dentry, dentry->d_flags,
-              d_refcount(dentry));
+              d_count(dentry));
 
        /* mountpoint is always valid */
        if (d_mountpoint((struct dentry *)dentry))
@@ -165,7 +165,7 @@ static int ll_ddelete(const struct dentry *de)
               list_empty(&de->d_subdirs) ? "" : "subdirs");
 
        /* kernel >= 2.6.38 last refcount is decreased after this function. */
-       LASSERT(d_refcount(de) == 1);
+       LASSERT(d_count(de) == 1);
 
        /* Disable this piece of code temproarily because this is called
         * inside dcache_lock so it's not appropriate to do lots of work
@@ -190,7 +190,7 @@ static int ll_set_dd(struct dentry *de)
 
        CDEBUG(D_DENTRY, "ldd on dentry %.*s (%p) parent %p inode %p refc %d\n",
                de->d_name.len, de->d_name.name, de, de->d_parent, de->d_inode,
-               d_refcount(de));
+               d_count(de));
 
        if (de->d_fsdata == NULL) {
                struct ll_dentry_data *lld;
@@ -540,7 +540,7 @@ out:
                CDEBUG(D_DENTRY, "revalidated dentry %.*s (%p) parent %p "
                       "inode %p refc %d\n", de->d_name.len,
                       de->d_name.name, de, de->d_parent, de->d_inode,
-                      d_refcount(de));
+                      d_count(de));
 
                ll_set_lock_data(exp, de->d_inode, it, &bits);
 
index 992cd20..5227c5c 100644 (file)
@@ -1529,12 +1529,12 @@ static inline void d_lustre_invalidate(struct dentry *dentry, int nested)
 {
        CDEBUG(D_DENTRY, "invalidate dentry %.*s (%p) parent %p inode %p "
               "refc %d\n", dentry->d_name.len, dentry->d_name.name, dentry,
-              dentry->d_parent, dentry->d_inode, d_refcount(dentry));
+              dentry->d_parent, dentry->d_inode, d_count(dentry));
 
        spin_lock_nested(&dentry->d_lock,
                         nested ? DENTRY_D_LOCK_NESTED : DENTRY_D_LOCK_NORMAL);
        __d_lustre_invalidate(dentry);
-       if (d_refcount(dentry) == 0)
+       if (d_count(dentry) == 0)
                __d_drop(dentry);
        spin_unlock(&dentry->d_lock);
 }
index 2311b20..afae801 100644 (file)
@@ -659,7 +659,7 @@ void lustre_dump_dentry(struct dentry *dentry, int recur)
               " flags=0x%x, fsdata=%p, %d subdirs\n", dentry,
               dentry->d_name.len, dentry->d_name.name,
               dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
-              dentry->d_parent, dentry->d_inode, d_refcount(dentry),
+              dentry->d_parent, dentry->d_inode, d_count(dentry),
               dentry->d_flags, dentry->d_fsdata, subdirs);
        if (dentry->d_inode != NULL)
                ll_dump_inode(dentry->d_inode);
index 58d59aa..ff8f63d 100644 (file)
@@ -409,7 +409,7 @@ struct dentry *ll_splice_alias(struct inode *inode, struct dentry *de)
                        iput(inode);
                        CDEBUG(D_DENTRY,
                               "Reuse dentry %p inode %p refc %d flags %#x\n",
-                             new, new->d_inode, d_refcount(new), new->d_flags);
+                             new, new->d_inode, d_count(new), new->d_flags);
                        return new;
                }
        }
@@ -417,7 +417,7 @@ struct dentry *ll_splice_alias(struct inode *inode, struct dentry *de)
        __d_lustre_invalidate(de);
        d_add(de, inode);
        CDEBUG(D_DENTRY, "Add dentry %p inode %p refc %d flags %#x\n",
-              de, de->d_inode, d_refcount(de), de->d_flags);
+              de, de->d_inode, d_count(de), de->d_flags);
        return de;
 }
 
index 1e6f32c..e70d8fe 100644 (file)
@@ -121,8 +121,8 @@ void push_ctxt(struct lvfs_run_ctxt *save, struct lvfs_run_ctxt *new_ctx,
        OBD_SET_CTXT_MAGIC(save);
 
        save->fs = get_fs();
-       LASSERT(d_refcount(cfs_fs_pwd(current->fs)));
-       LASSERT(d_refcount(new_ctx->pwd));
+       LASSERT(d_count(cfs_fs_pwd(current->fs)));
+       LASSERT(d_count(new_ctx->pwd));
        save->pwd = dget(cfs_fs_pwd(current->fs));
        save->pwdmnt = mntget(cfs_fs_mnt(current->fs));
        save->luc.luc_umask = current_umask();
index 3b157f8..f7af3d6 100644 (file)
@@ -73,7 +73,7 @@ proc_dir_entry_t *lprocfs_add_simple(struct proc_dir_entry *root,
                                     struct file_operations *fops)
 {
        proc_dir_entry_t *proc;
-       mode_t mode = 0;
+       umode_t mode = 0;
 
        if (root == NULL || name == NULL || fops == NULL)
                return ERR_PTR(-EINVAL);
@@ -140,7 +140,7 @@ int lprocfs_add_vars(struct proc_dir_entry *root, struct lprocfs_vars *list,
 
        while (list->name != NULL) {
                struct proc_dir_entry *proc;
-               mode_t mode = 0;
+               umode_t mode = 0;
 
                if (list->proc_mode != 0000) {
                        mode = list->proc_mode;
@@ -1899,7 +1899,7 @@ EXPORT_SYMBOL(lprocfs_find_named_value);
 
 int lprocfs_seq_create(proc_dir_entry_t *parent,
                       const char *name,
-                      mode_t mode,
+                      umode_t mode,
                       const struct file_operations *seq_fops,
                       void *data)
 {
@@ -1919,7 +1919,7 @@ EXPORT_SYMBOL(lprocfs_seq_create);
 
 int lprocfs_obd_seq_create(struct obd_device *dev,
                           const char *name,
-                          mode_t mode,
+                          umode_t mode,
                           const struct file_operations *seq_fops,
                           void *data)
 {
index c880adc..14c14c2 100644 (file)
@@ -1144,8 +1144,8 @@ struct sk_buff *DrvAggr_Aggregation(struct net_device *dev, struct ieee80211_drv
                /* Subframe drv Tx descriptor and firmware info setting */
                skb = pSendList->tx_agg_frames[i];
                tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
-               tx_agg_desc = (tx_desc_819x_usb_aggr_subframe *)agg_skb->tail;
-               tx_fwinfo = (tx_fwinfo_819x_usb *)(agg_skb->tail + sizeof(tx_desc_819x_usb_aggr_subframe));
+               tx_agg_desc = (tx_desc_819x_usb_aggr_subframe *)skb_tail_pointer(agg_skb);
+               tx_fwinfo = (tx_fwinfo_819x_usb *)(skb_tail_pointer(agg_skb) + sizeof(tx_desc_819x_usb_aggr_subframe));
 
                memset(tx_fwinfo, 0, sizeof(tx_fwinfo_819x_usb));
                /* DWORD 0 */
index eda2e7d..6651bd8 100644 (file)
@@ -5,7 +5,7 @@
 config NET_VENDOR_SILICOM
        bool "Silicom devices"
        default y
-       depends on PCI
+       depends on PCI && NETDEVICES
        ---help---
          If you have a network card (Ethernet) belonging to this class,
          say Y.
@@ -19,7 +19,7 @@ if NET_VENDOR_SILICOM
 
 config SBYPASS
        tristate "Silicom BypassCTL library support"
-       depends on PCI && NET
+       depends on PCI
        depends on m
        ---help---
          If you have a network (Ethernet) controller of this type, say Y
@@ -29,10 +29,9 @@ config SBYPASS
 
 config BPCTL
        tristate "Silicom BypassCTL net support"
-       depends on PCI && NET
+       depends on PCI
        depends on m
        select SBYPASS
-       select NET_CORE
        select MII
        ---help---
          If you have a network (Ethernet) controller of this type, say Y
index 4b3a1ae..48b9fb1 100644 (file)
@@ -128,7 +128,7 @@ static unsigned long str_to_hex(char *p);
 static int bp_device_event(struct notifier_block *unused,
                           unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        static bpctl_dev_t *pbpctl_dev, *pbpctl_dev_m;
        int dev_num = 0, ret = 0, ret_d = 0, time_left = 0;
        /* printk("BP_PROC_SUPPORT event =%d %s %d\n", event,dev->name, dev->ifindex ); */
index f80d3dd..8ca5ac7 100644 (file)
@@ -150,6 +150,11 @@ static void vhost_net_ubuf_put_and_wait(struct vhost_net_ubuf_ref *ubufs)
 {
        kref_put(&ubufs->kref, vhost_net_zerocopy_done_signal);
        wait_event(ubufs->wait, !atomic_read(&ubufs->kref.refcount));
+}
+
+static void vhost_net_ubuf_put_wait_and_free(struct vhost_net_ubuf_ref *ubufs)
+{
+       vhost_net_ubuf_put_and_wait(ubufs);
        kfree(ubufs);
 }
 
@@ -948,7 +953,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
        mutex_unlock(&vq->mutex);
 
        if (oldubufs) {
-               vhost_net_ubuf_put_and_wait(oldubufs);
+               vhost_net_ubuf_put_wait_and_free(oldubufs);
                mutex_lock(&vq->mutex);
                vhost_zerocopy_signal_used(n, vq);
                mutex_unlock(&vq->mutex);
@@ -966,7 +971,7 @@ err_used:
        rcu_assign_pointer(vq->private_data, oldsock);
        vhost_net_enable_vq(n, vq);
        if (ubufs)
-               vhost_net_ubuf_put_and_wait(ubufs);
+               vhost_net_ubuf_put_wait_and_free(ubufs);
 err_ubufs:
        fput(sock->file);
 err_vq:
index 2e937bd..4cf1e1d 100644 (file)
@@ -367,6 +367,8 @@ config FB_IMX
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
+       select FB_MODE_HELPERS
+       select VIDEOMODE_HELPERS
 
 config FB_CYBER2000
        tristate "CyberPro 2000/2010/5000 support"
@@ -2188,7 +2190,7 @@ config FB_PS3_DEFAULT_SIZE_M
 
 config FB_XILINX
        tristate "Xilinx frame buffer support"
-       depends on FB && (XILINX_VIRTEX || MICROBLAZE)
+       depends on FB && (XILINX_VIRTEX || MICROBLAZE || ARCH_ZYNQ)
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
index 8c55011..a4dfe8c 100644 (file)
@@ -2016,7 +2016,7 @@ static int aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        aty128_init_engine(par);
 
-       par->pm_reg = pci_find_capability(pdev, PCI_CAP_ID_PM);
+       par->pm_reg = pdev->pm_cap;
        par->pdev = pdev;
        par->asleep = 0;
        par->lock_blank = 0;
index 4f27fdc..a89c15d 100644 (file)
@@ -58,6 +58,7 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/delay.h>
+#include <linux/compiler.h>
 #include <linux/console.h>
 #include <linux/fb.h>
 #include <linux/init.h>
@@ -434,8 +435,8 @@ static int correct_chipset(struct atyfb_par *par)
        const char *name;
        int i;
 
-       for (i = ARRAY_SIZE(aty_chips) - 1; i >= 0; i--)
-               if (par->pci_id == aty_chips[i].pci_id)
+       for (i = ARRAY_SIZE(aty_chips); i > 0; i--)
+               if (par->pci_id == aty_chips[i - 1].pci_id)
                        break;
 
        if (i < 0)
@@ -531,8 +532,8 @@ static int correct_chipset(struct atyfb_par *par)
        return 0;
 }
 
-static char ram_dram[] = "DRAM";
-static char ram_resv[] = "RESV";
+static char ram_dram[] __maybe_unused = "DRAM";
+static char ram_resv[] __maybe_unused = "RESV";
 #ifdef CONFIG_FB_ATY_GX
 static char ram_vram[] = "VRAM";
 #endif /* CONFIG_FB_ATY_GX */
index 92bda58..f7091ec 100644 (file)
@@ -2805,7 +2805,7 @@ static void radeonfb_early_resume(void *data)
 void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlist, int force_sleep)
 {
        /* Find PM registers in config space if any*/
-       rinfo->pm_reg = pci_find_capability(rinfo->pdev, PCI_CAP_ID_PM);
+       rinfo->pm_reg = rinfo->pdev->pm_cap;
 
        /* Enable/Disable dynamic clocks: TODO add sysfs access */
        if (rinfo->family == CHIP_FAMILY_RS480)
index ebeb971..a54ccdc 100644 (file)
@@ -577,7 +577,6 @@ failed:
        if (fbdev->info.cmap.len != 0) {
                fb_dealloc_cmap(&fbdev->info.cmap);
        }
-       platform_set_drvdata(dev, NULL);
 
        return -ENODEV;
 }
index 2726a5b..87f288b 100644 (file)
@@ -681,7 +681,6 @@ out3:
 out2:
        free_dma(CH_EPPI0);
 out1:
-       platform_set_drvdata(pdev, NULL);
 
        return ret;
 }
index 29d8c04..b594a58 100644 (file)
@@ -170,16 +170,19 @@ static int lq035q1_spidev_remove(struct spi_device *spi)
        return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_SHUT);
 }
 
-#ifdef CONFIG_PM
-static int lq035q1_spidev_suspend(struct spi_device *spi, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int lq035q1_spidev_suspend(struct device *dev)
 {
+       struct spi_device *spi = to_spi_device(dev);
+
        return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_SHUT);
 }
 
-static int lq035q1_spidev_resume(struct spi_device *spi)
+static int lq035q1_spidev_resume(struct device *dev)
 {
-       int ret;
+       struct spi_device *spi = to_spi_device(dev);
        struct spi_control *ctl = spi_get_drvdata(spi);
+       int ret;
 
        ret = lq035q1_control(spi, LQ035_DRIVER_OUTPUT_CTL, ctl->mode);
        if (ret)
@@ -187,9 +190,13 @@ static int lq035q1_spidev_resume(struct spi_device *spi)
 
        return lq035q1_control(spi, LQ035_SHUT_CTL, LQ035_ON);
 }
+
+static SIMPLE_DEV_PM_OPS(lq035q1_spidev_pm_ops, lq035q1_spidev_suspend,
+       lq035q1_spidev_resume);
+#define LQ035Q1_SPIDEV_PM_OPS (&lq035q1_spidev_pm_ops)
+
 #else
-# define lq035q1_spidev_suspend NULL
-# define lq035q1_spidev_resume  NULL
+#define LQ035Q1_SPIDEV_PM_OPS NULL
 #endif
 
 /* Power down all displays on reboot, poweroff or halt */
@@ -708,8 +715,7 @@ static int bfin_lq035q1_probe(struct platform_device *pdev)
        info->spidrv.probe    = lq035q1_spidev_probe;
        info->spidrv.remove   = lq035q1_spidev_remove;
        info->spidrv.shutdown = lq035q1_spidev_shutdown;
-       info->spidrv.suspend  = lq035q1_spidev_suspend;
-       info->spidrv.resume   = lq035q1_spidev_resume;
+       info->spidrv.driver.pm = LQ035Q1_SPIDEV_PM_OPS;
 
        ret = spi_register_driver(&info->spidrv);
        if (ret < 0) {
@@ -759,7 +765,6 @@ static int bfin_lq035q1_probe(struct platform_device *pdev)
  out2:
        free_dma(CH_PPI);
  out1:
-       platform_set_drvdata(pdev, NULL);
 
        return ret;
 }
@@ -788,7 +793,6 @@ static int bfin_lq035q1_remove(struct platform_device *pdev)
        bfin_lq035q1_free_ports(info->disp_info->ppi_mode ==
                                USE_RGB565_16_BIT_PPI);
 
-       platform_set_drvdata(pdev, NULL);
        framebuffer_release(fbinfo);
 
        dev_info(&pdev->dev, "unregistered LCD driver\n");
index d46da01..48c0c4e 100644 (file)
@@ -578,7 +578,6 @@ out3:
 out2:
        free_dma(CH_PPI);
 out1:
-       platform_set_drvdata(pdev, NULL);
 
        return ret;
 }
@@ -608,7 +607,6 @@ static int bfin_t350mcqb_remove(struct platform_device *pdev)
 
        bfin_t350mcqb_request_ports(0);
 
-       platform_set_drvdata(pdev, NULL);
        framebuffer_release(fbinfo);
 
        printk(KERN_INFO DRIVER_NAME ": Unregister LCD driver.\n");
index 5855d17..9d8feac 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/kd.h>
 #include <linux/slab.h>
 #include <linux/vt_kern.h>
+#include <linux/sched.h>
 #include <linux/selection.h>
 #include <linux/spinlock.h>
 #include <linux/ioport.h>
@@ -1124,11 +1125,15 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
 
        if (arg) {
                if (set)
-                       for (i = 0; i < cmapsz; i++)
+                       for (i = 0; i < cmapsz; i++) {
                                vga_writeb(arg[i], charmap + i);
+                               cond_resched();
+                       }
                else
-                       for (i = 0; i < cmapsz; i++)
+                       for (i = 0; i < cmapsz; i++) {
                                arg[i] = vga_readb(charmap + i);
+                               cond_resched();
+                       }
 
                /*
                 * In 512-character mode, the character map is not contiguous if
@@ -1139,11 +1144,15 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
                        charmap += 2 * cmapsz;
                        arg += cmapsz;
                        if (set)
-                               for (i = 0; i < cmapsz; i++)
+                               for (i = 0; i < cmapsz; i++) {
                                        vga_writeb(arg[i], charmap + i);
+                                       cond_resched();
+                               }
                        else
-                               for (i = 0; i < cmapsz; i++)
+                               for (i = 0; i < cmapsz; i++) {
                                        arg[i] = vga_readb(charmap + i);
+                                       cond_resched();
+                               }
                }
        }
 
index ee1ee54..28a837d 100644 (file)
@@ -595,7 +595,6 @@ failed_videomem:
        fb_dealloc_cmap(&info->cmap);
 failed_cmap:
        kfree(info);
-       platform_set_drvdata(pdev, NULL);
 
        return err;
 }
@@ -614,7 +613,6 @@ static int ep93xxfb_remove(struct platform_device *pdev)
                fbi->mach_info->teardown(pdev);
 
        kfree(info);
-       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
index 098bfc6..36e1fe2 100644 (file)
@@ -1305,7 +1305,9 @@ static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
        err |= copy_to_user(fix32->reserved, fix->reserved,
                            sizeof(fix->reserved));
 
-       return err;
+       if (err)
+               return -EFAULT;
+       return 0;
 }
 
 static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
@@ -1881,7 +1883,7 @@ static int ofonly __read_mostly;
  *
  * NOTE: Needed to maintain backwards compatibility
  */
-int fb_get_options(char *name, char **option)
+int fb_get_options(const char *name, char **option)
 {
        char *opt, *options = NULL;
        int retval = 0;
index 6c27805..6dd7225 100644 (file)
@@ -469,7 +469,7 @@ static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s)
        unsigned long val;
 
        if (s) {
-               if (!strict_strtoul(s, 10, &val) && (val <= 2))
+               if (!kstrtoul(s, 10, &val) && (val <= 2))
                        port = (enum fsl_diu_monitor_port) val;
                else if (strncmp(s, "lvds", 4) == 0)
                        port = FSL_DIU_PORT_LVDS;
@@ -1853,7 +1853,7 @@ static int __init fsl_diu_setup(char *options)
                if (!strncmp(opt, "monitor=", 8)) {
                        monitor_port = fsl_diu_name_to_port(opt + 8);
                } else if (!strncmp(opt, "bpp=", 4)) {
-                       if (!strict_strtoul(opt + 4, 10, &val))
+                       if (!kstrtoul(opt + 4, 10, &val))
                                default_bpp = val;
                } else
                        fb_mode = opt;
index cfd0c52..6c48388 100644 (file)
@@ -1302,7 +1302,7 @@ static int  __init i740fb_setup(char *options)
 }
 #endif
 
-int __init i740fb_init(void)
+static int __init i740fb_init(void)
 {
 #ifndef MODULE
        char *option = NULL;
index 0abf2bf..38733ac 100644 (file)
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
 #include <linux/math64.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
 
 #include <linux/platform_data/video-imxfb.h>
 
 #define LCDISR_EOF     (1<<1)
 #define LCDISR_BOF     (1<<0)
 
+#define IMXFB_LSCR1_DEFAULT 0x00120300
+
 /* Used fb-mode. Can be set on kernel command line, therefore file-static. */
 static const char *fb_mode;
 
-
 /*
  * These are the bitfields for each
  * display depth that we support.
@@ -187,6 +194,19 @@ static struct platform_device_id imxfb_devtype[] = {
 };
 MODULE_DEVICE_TABLE(platform, imxfb_devtype);
 
+static struct of_device_id imxfb_of_dev_id[] = {
+       {
+               .compatible = "fsl,imx1-fb",
+               .data = &imxfb_devtype[IMX1_FB],
+       }, {
+               .compatible = "fsl,imx21-fb",
+               .data = &imxfb_devtype[IMX21_FB],
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, imxfb_of_dev_id);
+
 static inline int is_imx1_fb(struct imxfb_info *fbi)
 {
        return fbi->devtype == IMX1_FB;
@@ -319,6 +339,9 @@ static const struct imx_fb_videomode *imxfb_find_mode(struct imxfb_info *fbi)
        struct imx_fb_videomode *m;
        int i;
 
+       if (!fb_mode)
+               return &fbi->mode[0];
+
        for (i = 0, m = &fbi->mode[0]; i < fbi->num_modes; i++, m++) {
                if (!strcmp(m->mode.name, fb_mode))
                        return m;
@@ -474,6 +497,9 @@ static int imxfb_bl_update_status(struct backlight_device *bl)
        struct imxfb_info *fbi = bl_get_data(bl);
        int brightness = bl->props.brightness;
 
+       if (!fbi->pwmr)
+               return 0;
+
        if (bl->props.power != FB_BLANK_UNBLANK)
                brightness = 0;
        if (bl->props.fb_blank != FB_BLANK_UNBLANK)
@@ -684,10 +710,14 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
 
        writel(fbi->pcr, fbi->regs + LCDC_PCR);
 #ifndef PWMR_BACKLIGHT_AVAILABLE
-       writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+       if (fbi->pwmr)
+               writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
 #endif
        writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
-       writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
+
+       /* dmacr = 0 is no valid value, as we need DMA control marks. */
+       if (fbi->dmacr)
+               writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
 
        return 0;
 }
@@ -723,13 +753,12 @@ static int imxfb_resume(struct platform_device *dev)
 #define imxfb_resume   NULL
 #endif
 
-static int __init imxfb_init_fbinfo(struct platform_device *pdev)
+static int imxfb_init_fbinfo(struct platform_device *pdev)
 {
        struct imx_fb_platform_data *pdata = pdev->dev.platform_data;
        struct fb_info *info = dev_get_drvdata(&pdev->dev);
        struct imxfb_info *fbi = info->par;
-       struct imx_fb_videomode *m;
-       int i;
+       struct device_node *np;
 
        pr_debug("%s\n",__func__);
 
@@ -760,41 +789,95 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
        info->fbops                     = &imxfb_ops;
        info->flags                     = FBINFO_FLAG_DEFAULT |
                                          FBINFO_READS_FAST;
-       info->var.grayscale             = pdata->cmap_greyscale;
-       fbi->cmap_inverse               = pdata->cmap_inverse;
-       fbi->cmap_static                = pdata->cmap_static;
-       fbi->lscr1                      = pdata->lscr1;
-       fbi->dmacr                      = pdata->dmacr;
-       fbi->pwmr                       = pdata->pwmr;
-       fbi->lcd_power                  = pdata->lcd_power;
-       fbi->backlight_power            = pdata->backlight_power;
-
-       for (i = 0, m = &pdata->mode[0]; i < pdata->num_modes; i++, m++)
-               info->fix.smem_len = max_t(size_t, info->fix.smem_len,
-                               m->mode.xres * m->mode.yres * m->bpp / 8);
+       if (pdata) {
+               info->var.grayscale             = pdata->cmap_greyscale;
+               fbi->cmap_inverse               = pdata->cmap_inverse;
+               fbi->cmap_static                = pdata->cmap_static;
+               fbi->lscr1                      = pdata->lscr1;
+               fbi->dmacr                      = pdata->dmacr;
+               fbi->pwmr                       = pdata->pwmr;
+               fbi->lcd_power                  = pdata->lcd_power;
+               fbi->backlight_power            = pdata->backlight_power;
+       } else {
+               np = pdev->dev.of_node;
+               info->var.grayscale = of_property_read_bool(np,
+                                               "cmap-greyscale");
+               fbi->cmap_inverse = of_property_read_bool(np, "cmap-inverse");
+               fbi->cmap_static = of_property_read_bool(np, "cmap-static");
+
+               fbi->lscr1 = IMXFB_LSCR1_DEFAULT;
+               of_property_read_u32(np, "fsl,lscr1", &fbi->lscr1);
+
+               of_property_read_u32(np, "fsl,dmacr", &fbi->dmacr);
+
+               /* These two function pointers could be used by some specific
+                * platforms. */
+               fbi->lcd_power = NULL;
+               fbi->backlight_power = NULL;
+       }
 
        return 0;
 }
 
-static int __init imxfb_probe(struct platform_device *pdev)
+static int imxfb_of_read_mode(struct device *dev, struct device_node *np,
+               struct imx_fb_videomode *imxfb_mode)
+{
+       int ret;
+       struct fb_videomode *of_mode = &imxfb_mode->mode;
+       u32 bpp;
+       u32 pcr;
+
+       ret = of_property_read_string(np, "model", &of_mode->name);
+       if (ret)
+               of_mode->name = NULL;
+
+       ret = of_get_fb_videomode(np, of_mode, OF_USE_NATIVE_MODE);
+       if (ret) {
+               dev_err(dev, "Failed to get videomode from DT\n");
+               return ret;
+       }
+
+       ret = of_property_read_u32(np, "bits-per-pixel", &bpp);
+       ret |= of_property_read_u32(np, "fsl,pcr", &pcr);
+
+       if (ret) {
+               dev_err(dev, "Failed to read bpp and pcr from DT\n");
+               return -EINVAL;
+       }
+
+       if (bpp < 1 || bpp > 255) {
+               dev_err(dev, "Bits per pixel have to be between 1 and 255\n");
+               return -EINVAL;
+       }
+
+       imxfb_mode->bpp = bpp;
+       imxfb_mode->pcr = pcr;
+
+       return 0;
+}
+
+static int imxfb_probe(struct platform_device *pdev)
 {
        struct imxfb_info *fbi;
        struct fb_info *info;
        struct imx_fb_platform_data *pdata;
        struct resource *res;
+       struct imx_fb_videomode *m;
+       const struct of_device_id *of_id;
        int ret, i;
+       int bytes_per_pixel;
 
        dev_info(&pdev->dev, "i.MX Framebuffer driver\n");
 
+       of_id = of_match_device(imxfb_of_dev_id, &pdev->dev);
+       if (of_id)
+               pdev->id_entry = of_id->data;
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res)
                return -ENODEV;
 
        pdata = pdev->dev.platform_data;
-       if (!pdata) {
-               dev_err(&pdev->dev,"No platform_data available\n");
-               return -ENOMEM;
-       }
 
        info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev);
        if (!info)
@@ -802,15 +885,55 @@ static int __init imxfb_probe(struct platform_device *pdev)
 
        fbi = info->par;
 
-       if (!fb_mode)
-               fb_mode = pdata->mode[0].mode.name;
-
        platform_set_drvdata(pdev, info);
 
        ret = imxfb_init_fbinfo(pdev);
        if (ret < 0)
                goto failed_init;
 
+       if (pdata) {
+               if (!fb_mode)
+                       fb_mode = pdata->mode[0].mode.name;
+
+               fbi->mode = pdata->mode;
+               fbi->num_modes = pdata->num_modes;
+       } else {
+               struct device_node *display_np;
+               fb_mode = NULL;
+
+               display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
+               if (!display_np) {
+                       dev_err(&pdev->dev, "No display defined in devicetree\n");
+                       ret = -EINVAL;
+                       goto failed_of_parse;
+               }
+
+               /*
+                * imxfb does not support more modes, we choose only the native
+                * mode.
+                */
+               fbi->num_modes = 1;
+
+               fbi->mode = devm_kzalloc(&pdev->dev,
+                               sizeof(struct imx_fb_videomode), GFP_KERNEL);
+               if (!fbi->mode) {
+                       ret = -ENOMEM;
+                       goto failed_of_parse;
+               }
+
+               ret = imxfb_of_read_mode(&pdev->dev, display_np, fbi->mode);
+               if (ret)
+                       goto failed_of_parse;
+       }
+
+       /* Calculate maximum bytes used per pixel. In most cases this should
+        * be the same as m->bpp/8 */
+       m = &fbi->mode[0];
+       bytes_per_pixel = (m->bpp + 7) / 8;
+       for (i = 0; i < fbi->num_modes; i++, m++)
+               info->fix.smem_len = max_t(size_t, info->fix.smem_len,
+                               m->mode.xres * m->mode.yres * bytes_per_pixel);
+
        res = request_mem_region(res->start, resource_size(res),
                                DRIVER_NAME);
        if (!res) {
@@ -843,7 +966,8 @@ static int __init imxfb_probe(struct platform_device *pdev)
                goto failed_ioremap;
        }
 
-       if (!pdata->fixed_screen_cpu) {
+       /* Seems not being used by anyone, so no support for oftree */
+       if (!pdata || !pdata->fixed_screen_cpu) {
                fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
                fbi->map_cpu = dma_alloc_writecombine(&pdev->dev,
                                fbi->map_size, &fbi->map_dma, GFP_KERNEL);
@@ -868,18 +992,16 @@ static int __init imxfb_probe(struct platform_device *pdev)
                info->fix.smem_start = fbi->screen_dma;
        }
 
-       if (pdata->init) {
+       if (pdata && pdata->init) {
                ret = pdata->init(fbi->pdev);
                if (ret)
                        goto failed_platform_init;
        }
 
-       fbi->mode = pdata->mode;
-       fbi->num_modes = pdata->num_modes;
 
        INIT_LIST_HEAD(&info->modelist);
-       for (i = 0; i < pdata->num_modes; i++)
-               fb_add_videomode(&pdata->mode[i].mode, &info->modelist);
+       for (i = 0; i < fbi->num_modes; i++)
+               fb_add_videomode(&fbi->mode[i].mode, &info->modelist);
 
        /*
         * This makes sure that our colour bitfield
@@ -909,10 +1031,10 @@ static int __init imxfb_probe(struct platform_device *pdev)
 failed_register:
        fb_dealloc_cmap(&info->cmap);
 failed_cmap:
-       if (pdata->exit)
+       if (pdata && pdata->exit)
                pdata->exit(fbi->pdev);
 failed_platform_init:
-       if (!pdata->fixed_screen_cpu)
+       if (pdata && !pdata->fixed_screen_cpu)
                dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu,
                        fbi->map_dma);
 failed_map:
@@ -921,9 +1043,9 @@ failed_ioremap:
 failed_getclock:
        release_mem_region(res->start, resource_size(res));
 failed_req:
+failed_of_parse:
        kfree(info->pseudo_palette);
 failed_init:
-       platform_set_drvdata(pdev, NULL);
        framebuffer_release(info);
        return ret;
 }
@@ -945,7 +1067,7 @@ static int imxfb_remove(struct platform_device *pdev)
        unregister_framebuffer(info);
 
        pdata = pdev->dev.platform_data;
-       if (pdata->exit)
+       if (pdata && pdata->exit)
                pdata->exit(fbi->pdev);
 
        fb_dealloc_cmap(&info->cmap);
@@ -955,12 +1077,10 @@ static int imxfb_remove(struct platform_device *pdev)
        iounmap(fbi->regs);
        release_mem_region(res->start, resource_size(res));
 
-       platform_set_drvdata(pdev, NULL);
-
        return 0;
 }
 
-void  imxfb_shutdown(struct platform_device * dev)
+static void imxfb_shutdown(struct platform_device *dev)
 {
        struct fb_info *info = platform_get_drvdata(dev);
        struct imxfb_info *fbi = info->par;
@@ -974,6 +1094,7 @@ static struct platform_driver imxfb_driver = {
        .shutdown       = imxfb_shutdown,
        .driver         = {
                .name   = DRIVER_NAME,
+               .of_match_table = imxfb_of_dev_id,
        },
        .id_table       = imxfb_devtype,
 };
@@ -999,7 +1120,7 @@ static int imxfb_setup(void)
        return 0;
 }
 
-int __init imxfb_init(void)
+static int __init imxfb_init(void)
 {
        int ret = imxfb_setup();
 
index 36979b4..2c49112 100644 (file)
@@ -737,8 +737,6 @@ static int jzfb_remove(struct platform_device *pdev)
        fb_dealloc_cmap(&jzfb->fb->cmap);
        jzfb_free_devmem(jzfb);
 
-       platform_set_drvdata(pdev, NULL);
-
        framebuffer_release(jzfb->fb);
 
        return 0;
index 6d1fa96..4ab95b8 100644 (file)
@@ -659,7 +659,6 @@ failed_destroy_mutex:
        mutex_destroy(&fbi->access_ok);
 failed:
        dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
-       platform_set_drvdata(pdev, NULL);
 
        framebuffer_release(info);
 
index 4bd31b2..75dca19 100644 (file)
@@ -165,9 +165,9 @@ static void overlay_set_win(struct mmp_overlay *overlay, struct mmp_win *win)
 
 static void dmafetch_onoff(struct mmp_overlay *overlay, int on)
 {
-       u32 mask = overlay_is_vid(overlay) ? CFG_GRA_ENA_MASK :
-                  CFG_DMA_ENA_MASK;
-       u32 enable = overlay_is_vid(overlay) ? CFG_GRA_ENA(1) : CFG_DMA_ENA(1);
+       u32 mask = overlay_is_vid(overlay) ? CFG_DMA_ENA_MASK :
+                  CFG_GRA_ENA_MASK;
+       u32 enable = overlay_is_vid(overlay) ? CFG_DMA_ENA(1) : CFG_GRA_ENA(1);
        u32 tmp;
        struct mmp_path *path = overlay->path;
 
@@ -238,7 +238,7 @@ static int overlay_set_addr(struct mmp_overlay *overlay, struct mmp_addr *addr)
        struct lcd_regs *regs = path_regs(overlay->path);
 
        /* FIXME: assert addr supported */
-       memcpy(&overlay->addr, addr, sizeof(struct mmp_win));
+       memcpy(&overlay->addr, addr, sizeof(struct mmp_addr));
        writel(addr->phys[0], &regs->g_0);
 
        return overlay->addr.phys[0];
@@ -566,7 +566,6 @@ failed:
                devm_kfree(ctrl->dev, ctrl);
        }
 
-       platform_set_drvdata(pdev, NULL);
        dev_err(&pdev->dev, "device init failed\n");
 
        return ret;
index 21223d4..3ba3771 100644 (file)
@@ -899,7 +899,6 @@ static int mxsfb_probe(struct platform_device *pdev)
 
        host->base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(host->base)) {
-               dev_err(&pdev->dev, "ioremap failed\n");
                ret = PTR_ERR(host->base);
                goto fb_release;
        }
@@ -986,8 +985,6 @@ static int mxsfb_remove(struct platform_device *pdev)
 
        framebuffer_release(fb_info);
 
-       platform_set_drvdata(pdev, NULL);
-
        return 0;
 }
 
index 32581c7..8c527e5 100644 (file)
@@ -707,7 +707,6 @@ static int nuc900fb_remove(struct platform_device *pdev)
        release_resource(fbi->mem);
        kfree(fbi->mem);
 
-       platform_set_drvdata(pdev, NULL);
        framebuffer_release(fbinfo);
 
        return 0;
index 56009bc..171821d 100644 (file)
@@ -23,7 +23,7 @@
  * Every display_timing can be specified with either just the typical value or
  * a range consisting of min/typ/max. This function helps handling this
  **/
-static int parse_timing_property(struct device_node *np, const char *name,
+static int parse_timing_property(const struct device_node *np, const char *name,
                          struct timing_entry *result)
 {
        struct property *prop;
@@ -53,21 +53,16 @@ static int parse_timing_property(struct device_node *np, const char *name,
 }
 
 /**
- * of_get_display_timing - parse display_timing entry from device_node
+ * of_parse_display_timing - parse display_timing entry from device_node
  * @np: device_node with the properties
  **/
-static struct display_timing *of_get_display_timing(struct device_node *np)
+static int of_parse_display_timing(const struct device_node *np,
+               struct display_timing *dt)
 {
-       struct display_timing *dt;
        u32 val = 0;
        int ret = 0;
 
-       dt = kzalloc(sizeof(*dt), GFP_KERNEL);
-       if (!dt) {
-               pr_err("%s: could not allocate display_timing struct\n",
-                       of_node_full_name(np));
-               return NULL;
-       }
+       memset(dt, 0, sizeof(*dt));
 
        ret |= parse_timing_property(np, "hback-porch", &dt->hback_porch);
        ret |= parse_timing_property(np, "hfront-porch", &dt->hfront_porch);
@@ -97,16 +92,44 @@ static struct display_timing *of_get_display_timing(struct device_node *np)
                dt->flags |= DISPLAY_FLAGS_INTERLACED;
        if (of_property_read_bool(np, "doublescan"))
                dt->flags |= DISPLAY_FLAGS_DOUBLESCAN;
+       if (of_property_read_bool(np, "doubleclk"))
+               dt->flags |= DISPLAY_FLAGS_DOUBLECLK;
 
        if (ret) {
                pr_err("%s: error reading timing properties\n",
                        of_node_full_name(np));
-               kfree(dt);
-               return NULL;
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * of_get_display_timing - parse a display_timing entry
+ * @np: device_node with the timing subnode
+ * @name: name of the timing node
+ * @dt: display_timing struct to fill
+ **/
+int of_get_display_timing(struct device_node *np, const char *name,
+               struct display_timing *dt)
+{
+       struct device_node *timing_np;
+
+       if (!np) {
+               pr_err("%s: no devicenode given\n", of_node_full_name(np));
+               return -EINVAL;
        }
 
-       return dt;
+       timing_np = of_find_node_by_name(np, name);
+       if (!timing_np) {
+               pr_err("%s: could not find node '%s'\n",
+                       of_node_full_name(np), name);
+               return -ENOENT;
+       }
+
+       return of_parse_display_timing(timing_np, dt);
 }
+EXPORT_SYMBOL_GPL(of_get_display_timing);
 
 /**
  * of_get_display_timings - parse all display_timing entries from a device_node
@@ -174,9 +197,17 @@ struct display_timings *of_get_display_timings(struct device_node *np)
 
        for_each_child_of_node(timings_np, entry) {
                struct display_timing *dt;
+               int r;
 
-               dt = of_get_display_timing(entry);
+               dt = kzalloc(sizeof(*dt), GFP_KERNEL);
                if (!dt) {
+                       pr_err("%s: could not allocate display_timing struct\n",
+                                       of_node_full_name(np));
+                       goto timingfail;
+               }
+
+               r = of_parse_display_timing(entry, dt);
+               if (r) {
                        /*
                         * to not encourage wrong devicetrees, fail in case of
                         * an error
index b07b2b0..56cad0f 100644 (file)
@@ -6,5 +6,6 @@ if ARCH_OMAP2PLUS
 source "drivers/video/omap2/dss/Kconfig"
 source "drivers/video/omap2/omapfb/Kconfig"
 source "drivers/video/omap2/displays/Kconfig"
+source "drivers/video/omap2/displays-new/Kconfig"
 
 endif
index 296e5c5..86873c2 100644 (file)
@@ -2,4 +2,5 @@ obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
 
 obj-$(CONFIG_OMAP2_DSS) += dss/
 obj-y += displays/
+obj-y += displays-new/
 obj-$(CONFIG_FB_OMAP2) += omapfb/
diff --git a/drivers/video/omap2/displays-new/Kconfig b/drivers/video/omap2/displays-new/Kconfig
new file mode 100644 (file)
index 0000000..6c90885
--- /dev/null
@@ -0,0 +1,73 @@
+menu "OMAP Display Device Drivers (new device model)"
+        depends on OMAP2_DSS
+
+config DISPLAY_ENCODER_TFP410
+        tristate "TFP410 DPI to DVI Encoder"
+       help
+         Driver for TFP410 DPI to DVI encoder.
+
+config DISPLAY_ENCODER_TPD12S015
+        tristate "TPD12S015 HDMI ESD protection and level shifter"
+       help
+         Driver for TPD12S015, which offers HDMI ESD protection and level
+         shifting.
+
+config DISPLAY_CONNECTOR_DVI
+        tristate "DVI Connector"
+       depends on I2C
+       help
+         Driver for a generic DVI connector.
+
+config DISPLAY_CONNECTOR_HDMI
+        tristate "HDMI Connector"
+       help
+         Driver for a generic HDMI connector.
+
+config DISPLAY_CONNECTOR_ANALOG_TV
+        tristate "Analog TV Connector"
+       help
+         Driver for a generic analog TV connector.
+
+config DISPLAY_PANEL_DPI
+       tristate "Generic DPI panel"
+       help
+         Driver for generic DPI panels.
+
+config DISPLAY_PANEL_DSI_CM
+       tristate "Generic DSI Command Mode Panel"
+       help
+         Driver for generic DSI command mode panels.
+
+config DISPLAY_PANEL_SONY_ACX565AKM
+       tristate "ACX565AKM Panel"
+       depends on SPI && BACKLIGHT_CLASS_DEVICE
+       help
+         This is the LCD panel used on Nokia N900
+
+config DISPLAY_PANEL_LGPHILIPS_LB035Q02
+       tristate "LG.Philips LB035Q02 LCD Panel"
+       depends on SPI
+       help
+         LCD Panel used on the Gumstix Overo Palo35
+
+config DISPLAY_PANEL_SHARP_LS037V7DW01
+        tristate "Sharp LS037V7DW01 LCD Panel"
+        depends on BACKLIGHT_CLASS_DEVICE
+        help
+          LCD Panel used in TI's SDP3430 and EVM boards
+
+config DISPLAY_PANEL_TPO_TD043MTEA1
+        tristate "TPO TD043MTEA1 LCD Panel"
+        depends on SPI
+        help
+          LCD Panel used in OMAP3 Pandora
+
+config DISPLAY_PANEL_NEC_NL8048HL11
+       tristate "NEC NL8048HL11 Panel"
+       depends on SPI
+       depends on BACKLIGHT_CLASS_DEVICE
+       help
+               This NEC NL8048HL11 panel is TFT LCD used in the
+               Zoom2/3/3630 sdp boards.
+
+endmenu
diff --git a/drivers/video/omap2/displays-new/Makefile b/drivers/video/omap2/displays-new/Makefile
new file mode 100644 (file)
index 0000000..5aeb11b
--- /dev/null
@@ -0,0 +1,12 @@
+obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o
+obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o
+obj-$(CONFIG_DISPLAY_CONNECTOR_DVI) += connector-dvi.o
+obj-$(CONFIG_DISPLAY_CONNECTOR_HDMI) += connector-hdmi.o
+obj-$(CONFIG_DISPLAY_CONNECTOR_ANALOG_TV) += connector-analog-tv.o
+obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
+obj-$(CONFIG_DISPLAY_PANEL_DSI_CM) += panel-dsi-cm.o
+obj-$(CONFIG_DISPLAY_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
+obj-$(CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
+obj-$(CONFIG_DISPLAY_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
+obj-$(CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
+obj-$(CONFIG_DISPLAY_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
diff --git a/drivers/video/omap2/displays-new/connector-analog-tv.c b/drivers/video/omap2/displays-new/connector-analog-tv.c
new file mode 100644 (file)
index 0000000..5338f36
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Analog TV Connector driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@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/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+struct panel_drv_data {
+       struct omap_dss_device dssdev;
+       struct omap_dss_device *in;
+
+       struct device *dev;
+
+       struct omap_video_timings timings;
+
+       enum omap_dss_venc_type connector_type;
+       bool invert_polarity;
+};
+
+#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
+
+static int tvc_connect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       dev_dbg(ddata->dev, "connect\n");
+
+       if (omapdss_device_is_connected(dssdev))
+               return 0;
+
+       r = in->ops.atv->connect(in, dssdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static void tvc_disconnect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       dev_dbg(ddata->dev, "disconnect\n");
+
+       if (!omapdss_device_is_connected(dssdev))
+               return;
+
+       in->ops.atv->disconnect(in, dssdev);
+}
+
+static int tvc_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       dev_dbg(ddata->dev, "enable\n");
+
+       if (!omapdss_device_is_connected(dssdev))
+               return -ENODEV;
+
+       if (omapdss_device_is_enabled(dssdev))
+               return 0;
+
+       in->ops.atv->set_timings(in, &ddata->timings);
+
+       in->ops.atv->set_type(in, ddata->connector_type);
+       in->ops.atv->invert_vid_out_polarity(in, ddata->invert_polarity);
+
+       r = in->ops.atv->enable(in);
+       if (r)
+               return r;
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return r;
+}
+
+static void tvc_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       dev_dbg(ddata->dev, "disable\n");
+
+       if (!omapdss_device_is_enabled(dssdev))
+               return;
+
+       in->ops.atv->disable(in);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void tvc_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       ddata->timings = *timings;
+       dssdev->panel.timings = *timings;
+
+       in->ops.atv->set_timings(in, timings);
+}
+
+static void tvc_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       *timings = ddata->timings;
+}
+
+static int tvc_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.atv->check_timings(in, timings);
+}
+
+static u32 tvc_get_wss(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.atv->get_wss(in);
+}
+
+static int tvc_set_wss(struct omap_dss_device *dssdev, u32 wss)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.atv->set_wss(in, wss);
+}
+
+static struct omap_dss_driver tvc_driver = {
+       .connect                = tvc_connect,
+       .disconnect             = tvc_disconnect,
+
+       .enable                 = tvc_enable,
+       .disable                = tvc_disable,
+
+       .set_timings            = tvc_set_timings,
+       .get_timings            = tvc_get_timings,
+       .check_timings          = tvc_check_timings,
+
+       .get_resolution         = omapdss_default_get_resolution,
+
+       .get_wss                = tvc_get_wss,
+       .set_wss                = tvc_set_wss,
+};
+
+static int tvc_probe_pdata(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct connector_atv_platform_data *pdata;
+       struct omap_dss_device *in, *dssdev;
+
+       pdata = dev_get_platdata(&pdev->dev);
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&pdev->dev, "Failed to find video source\n");
+               return -ENODEV;
+       }
+
+       ddata->in = in;
+
+       ddata->connector_type = pdata->connector_type;
+       ddata->invert_polarity = ddata->invert_polarity;
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       return 0;
+}
+
+static int tvc_probe(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata;
+       struct omap_dss_device *dssdev;
+       int r;
+
+       ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+       if (!ddata)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ddata);
+       ddata->dev = &pdev->dev;
+
+       if (dev_get_platdata(&pdev->dev)) {
+               r = tvc_probe_pdata(pdev);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       ddata->timings = omap_dss_pal_timings;
+
+       dssdev = &ddata->dssdev;
+       dssdev->driver = &tvc_driver;
+       dssdev->dev = &pdev->dev;
+       dssdev->type = OMAP_DISPLAY_TYPE_VENC;
+       dssdev->owner = THIS_MODULE;
+       dssdev->panel.timings = omap_dss_pal_timings;
+
+       r = omapdss_register_display(dssdev);
+       if (r) {
+               dev_err(&pdev->dev, "Failed to register panel\n");
+               goto err_reg;
+       }
+
+       return 0;
+err_reg:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int __exit tvc_remove(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       omapdss_unregister_display(&ddata->dssdev);
+
+       tvc_disable(dssdev);
+       tvc_disconnect(dssdev);
+
+       omap_dss_put_device(in);
+
+       return 0;
+}
+
+static struct platform_driver tvc_connector_driver = {
+       .probe  = tvc_probe,
+       .remove = __exit_p(tvc_remove),
+       .driver = {
+               .name   = "connector-analog-tv",
+               .owner  = THIS_MODULE,
+       },
+};
+
+module_platform_driver(tvc_connector_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("Analog TV Connector driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays-new/connector-dvi.c b/drivers/video/omap2/displays-new/connector-dvi.c
new file mode 100644 (file)
index 0000000..bc5f8ce
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * Generic DVI Connector driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@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/i2c.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <drm/drm_edid.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+static const struct omap_video_timings dvic_default_timings = {
+       .x_res          = 640,
+       .y_res          = 480,
+
+       .pixel_clock    = 23500,
+
+       .hfp            = 48,
+       .hsw            = 32,
+       .hbp            = 80,
+
+       .vfp            = 3,
+       .vsw            = 4,
+       .vbp            = 7,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+};
+
+struct panel_drv_data {
+       struct omap_dss_device dssdev;
+       struct omap_dss_device *in;
+
+       struct omap_video_timings timings;
+
+       struct i2c_adapter *i2c_adapter;
+};
+
+#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
+
+static int dvic_connect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (omapdss_device_is_connected(dssdev))
+               return 0;
+
+       r = in->ops.dvi->connect(in, dssdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static void dvic_disconnect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return;
+
+       in->ops.dvi->disconnect(in, dssdev);
+}
+
+static int dvic_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return -ENODEV;
+
+       if (omapdss_device_is_enabled(dssdev))
+               return 0;
+
+       in->ops.dvi->set_timings(in, &ddata->timings);
+
+       r = in->ops.dvi->enable(in);
+       if (r)
+               return r;
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+}
+
+static void dvic_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_enabled(dssdev))
+               return;
+
+       in->ops.dvi->disable(in);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void dvic_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       ddata->timings = *timings;
+       dssdev->panel.timings = *timings;
+
+       in->ops.dvi->set_timings(in, timings);
+}
+
+static void dvic_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       *timings = ddata->timings;
+}
+
+static int dvic_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.dvi->check_timings(in, timings);
+}
+
+static int dvic_ddc_read(struct i2c_adapter *adapter,
+               unsigned char *buf, u16 count, u8 offset)
+{
+       int r, retries;
+
+       for (retries = 3; retries > 0; retries--) {
+               struct i2c_msg msgs[] = {
+                       {
+                               .addr   = DDC_ADDR,
+                               .flags  = 0,
+                               .len    = 1,
+                               .buf    = &offset,
+                       }, {
+                               .addr   = DDC_ADDR,
+                               .flags  = I2C_M_RD,
+                               .len    = count,
+                               .buf    = buf,
+                       }
+               };
+
+               r = i2c_transfer(adapter, msgs, 2);
+               if (r == 2)
+                       return 0;
+
+               if (r != -EAGAIN)
+                       break;
+       }
+
+       return r < 0 ? r : -EIO;
+}
+
+static int dvic_read_edid(struct omap_dss_device *dssdev,
+               u8 *edid, int len)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       int r, l, bytes_read;
+
+       if (!ddata->i2c_adapter)
+               return -ENODEV;
+
+       l = min(EDID_LENGTH, len);
+       r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0);
+       if (r)
+               return r;
+
+       bytes_read = l;
+
+       /* if there are extensions, read second block */
+       if (len > EDID_LENGTH && edid[0x7e] > 0) {
+               l = min(EDID_LENGTH, len - EDID_LENGTH);
+
+               r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
+                               l, EDID_LENGTH);
+               if (r)
+                       return r;
+
+               bytes_read += l;
+       }
+
+       return bytes_read;
+}
+
+static bool dvic_detect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       unsigned char out;
+       int r;
+
+       if (!ddata->i2c_adapter)
+               return true;
+
+       r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0);
+
+       return r == 0;
+}
+
+static struct omap_dss_driver dvic_driver = {
+       .connect        = dvic_connect,
+       .disconnect     = dvic_disconnect,
+
+       .enable         = dvic_enable,
+       .disable        = dvic_disable,
+
+       .set_timings    = dvic_set_timings,
+       .get_timings    = dvic_get_timings,
+       .check_timings  = dvic_check_timings,
+
+       .get_resolution = omapdss_default_get_resolution,
+
+       .read_edid      = dvic_read_edid,
+       .detect         = dvic_detect,
+};
+
+static int dvic_probe_pdata(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct connector_dvi_platform_data *pdata;
+       struct omap_dss_device *in, *dssdev;
+       int i2c_bus_num;
+
+       pdata = dev_get_platdata(&pdev->dev);
+       i2c_bus_num = pdata->i2c_bus_num;
+
+       if (i2c_bus_num != -1) {
+               struct i2c_adapter *adapter;
+
+               adapter = i2c_get_adapter(i2c_bus_num);
+               if (!adapter) {
+                       dev_err(&pdev->dev,
+                                       "Failed to get I2C adapter, bus %d\n",
+                                       i2c_bus_num);
+                       return -EPROBE_DEFER;
+               }
+
+               ddata->i2c_adapter = adapter;
+       }
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&pdev->dev, "Failed to find video source\n");
+               return -ENODEV;
+       }
+
+       ddata->in = in;
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       return 0;
+}
+
+static int dvic_probe(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata;
+       struct omap_dss_device *dssdev;
+       int r;
+
+       ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+       if (!ddata)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ddata);
+
+       if (dev_get_platdata(&pdev->dev)) {
+               r = dvic_probe_pdata(pdev);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       ddata->timings = dvic_default_timings;
+
+       dssdev = &ddata->dssdev;
+       dssdev->driver = &dvic_driver;
+       dssdev->dev = &pdev->dev;
+       dssdev->type = OMAP_DISPLAY_TYPE_DVI;
+       dssdev->owner = THIS_MODULE;
+       dssdev->panel.timings = dvic_default_timings;
+
+       r = omapdss_register_display(dssdev);
+       if (r) {
+               dev_err(&pdev->dev, "Failed to register panel\n");
+               goto err_reg;
+       }
+
+       return 0;
+
+err_reg:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int __exit dvic_remove(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       omapdss_unregister_display(&ddata->dssdev);
+
+       dvic_disable(dssdev);
+       dvic_disconnect(dssdev);
+
+       omap_dss_put_device(in);
+
+       if (ddata->i2c_adapter)
+               i2c_put_adapter(ddata->i2c_adapter);
+
+       return 0;
+}
+
+static struct platform_driver dvi_connector_driver = {
+       .probe  = dvic_probe,
+       .remove = __exit_p(dvic_remove),
+       .driver = {
+               .name   = "connector-dvi",
+               .owner  = THIS_MODULE,
+       },
+};
+
+module_platform_driver(dvi_connector_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("Generic DVI Connector driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays-new/connector-hdmi.c b/drivers/video/omap2/displays-new/connector-hdmi.c
new file mode 100644 (file)
index 0000000..c582671
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * HDMI Connector driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@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/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_edid.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+static const struct omap_video_timings hdmic_default_timings = {
+       .x_res          = 640,
+       .y_res          = 480,
+       .pixel_clock    = 25175,
+       .hsw            = 96,
+       .hfp            = 16,
+       .hbp            = 48,
+       .vsw            = 2,
+       .vfp            = 11,
+       .vbp            = 31,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+
+       .interlace      = false,
+};
+
+struct panel_drv_data {
+       struct omap_dss_device dssdev;
+       struct omap_dss_device *in;
+
+       struct device *dev;
+
+       struct omap_video_timings timings;
+};
+
+#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
+
+static int hdmic_connect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       dev_dbg(ddata->dev, "connect\n");
+
+       if (omapdss_device_is_connected(dssdev))
+               return 0;
+
+       r = in->ops.hdmi->connect(in, dssdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static void hdmic_disconnect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       dev_dbg(ddata->dev, "disconnect\n");
+
+       if (!omapdss_device_is_connected(dssdev))
+               return;
+
+       in->ops.hdmi->disconnect(in, dssdev);
+}
+
+static int hdmic_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       dev_dbg(ddata->dev, "enable\n");
+
+       if (!omapdss_device_is_connected(dssdev))
+               return -ENODEV;
+
+       if (omapdss_device_is_enabled(dssdev))
+               return 0;
+
+       in->ops.hdmi->set_timings(in, &ddata->timings);
+
+       r = in->ops.hdmi->enable(in);
+       if (r)
+               return r;
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return r;
+}
+
+static void hdmic_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       dev_dbg(ddata->dev, "disable\n");
+
+       if (!omapdss_device_is_enabled(dssdev))
+               return;
+
+       in->ops.hdmi->disable(in);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void hdmic_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       ddata->timings = *timings;
+       dssdev->panel.timings = *timings;
+
+       in->ops.hdmi->set_timings(in, timings);
+}
+
+static void hdmic_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       *timings = ddata->timings;
+}
+
+static int hdmic_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.hdmi->check_timings(in, timings);
+}
+
+static int hdmic_read_edid(struct omap_dss_device *dssdev,
+               u8 *edid, int len)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.hdmi->read_edid(in, edid, len);
+}
+
+static bool hdmic_detect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.hdmi->detect(in);
+}
+
+static int hdmic_audio_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       /* enable audio only if the display is active */
+       if (!omapdss_device_is_enabled(dssdev))
+               return -EPERM;
+
+       r = in->ops.hdmi->audio_enable(in);
+       if (r)
+               return r;
+
+       dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
+
+       return 0;
+}
+
+static void hdmic_audio_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       in->ops.hdmi->audio_disable(in);
+
+       dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED;
+}
+
+static int hdmic_audio_start(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       /*
+        * No need to check the panel state. It was checked when trasitioning
+        * to AUDIO_ENABLED.
+        */
+       if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED)
+               return -EPERM;
+
+       r = in->ops.hdmi->audio_start(in);
+       if (r)
+               return r;
+
+       dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING;
+
+       return 0;
+}
+
+static void hdmic_audio_stop(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       in->ops.hdmi->audio_stop(in);
+
+       dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
+}
+
+static bool hdmic_audio_supported(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_enabled(dssdev))
+               return false;
+
+       return in->ops.hdmi->audio_supported(in);
+}
+
+static int hdmic_audio_config(struct omap_dss_device *dssdev,
+               struct omap_dss_audio *audio)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       /* config audio only if the display is active */
+       if (!omapdss_device_is_enabled(dssdev))
+               return -EPERM;
+
+       r = in->ops.hdmi->audio_config(in, audio);
+       if (r)
+               return r;
+
+       dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED;
+
+       return 0;
+}
+
+static struct omap_dss_driver hdmic_driver = {
+       .connect                = hdmic_connect,
+       .disconnect             = hdmic_disconnect,
+
+       .enable                 = hdmic_enable,
+       .disable                = hdmic_disable,
+
+       .set_timings            = hdmic_set_timings,
+       .get_timings            = hdmic_get_timings,
+       .check_timings          = hdmic_check_timings,
+
+       .get_resolution         = omapdss_default_get_resolution,
+
+       .read_edid              = hdmic_read_edid,
+       .detect                 = hdmic_detect,
+
+       .audio_enable           = hdmic_audio_enable,
+       .audio_disable          = hdmic_audio_disable,
+       .audio_start            = hdmic_audio_start,
+       .audio_stop             = hdmic_audio_stop,
+       .audio_supported        = hdmic_audio_supported,
+       .audio_config           = hdmic_audio_config,
+};
+
+static int hdmic_probe_pdata(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct connector_hdmi_platform_data *pdata;
+       struct omap_dss_device *in, *dssdev;
+
+       pdata = dev_get_platdata(&pdev->dev);
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&pdev->dev, "Failed to find video source\n");
+               return -ENODEV;
+       }
+
+       ddata->in = in;
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       return 0;
+}
+
+static int hdmic_probe(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata;
+       struct omap_dss_device *dssdev;
+       int r;
+
+       ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+       if (!ddata)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ddata);
+       ddata->dev = &pdev->dev;
+
+       if (dev_get_platdata(&pdev->dev)) {
+               r = hdmic_probe_pdata(pdev);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       ddata->timings = hdmic_default_timings;
+
+       dssdev = &ddata->dssdev;
+       dssdev->driver = &hdmic_driver;
+       dssdev->dev = &pdev->dev;
+       dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
+       dssdev->owner = THIS_MODULE;
+       dssdev->panel.timings = hdmic_default_timings;
+
+       r = omapdss_register_display(dssdev);
+       if (r) {
+               dev_err(&pdev->dev, "Failed to register panel\n");
+               goto err_reg;
+       }
+
+       return 0;
+err_reg:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int __exit hdmic_remove(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       omapdss_unregister_display(&ddata->dssdev);
+
+       hdmic_disable(dssdev);
+       hdmic_disconnect(dssdev);
+
+       omap_dss_put_device(in);
+
+       return 0;
+}
+
+static struct platform_driver hdmi_connector_driver = {
+       .probe  = hdmic_probe,
+       .remove = __exit_p(hdmic_remove),
+       .driver = {
+               .name   = "connector-hdmi",
+               .owner  = THIS_MODULE,
+       },
+};
+
+module_platform_driver(hdmi_connector_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("HDMI Connector driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays-new/encoder-tfp410.c b/drivers/video/omap2/displays-new/encoder-tfp410.c
new file mode 100644 (file)
index 0000000..a04f658
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * TFP410 DPI-to-DVI encoder driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@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/gpio.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+struct panel_drv_data {
+       struct omap_dss_device dssdev;
+       struct omap_dss_device *in;
+
+       int pd_gpio;
+       int data_lines;
+
+       struct omap_video_timings timings;
+};
+
+#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
+
+static int tfp410_connect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (omapdss_device_is_connected(dssdev))
+               return -EBUSY;
+
+       r = in->ops.dpi->connect(in, dssdev);
+       if (r)
+               return r;
+
+       dst->output = dssdev;
+       dssdev->device = dst;
+
+       return 0;
+}
+
+static void tfp410_disconnect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       WARN_ON(!omapdss_device_is_connected(dssdev));
+       if (!omapdss_device_is_connected(dssdev))
+               return;
+
+       WARN_ON(dst != dssdev->device);
+       if (dst != dssdev->device)
+               return;
+
+       dst->output = NULL;
+       dssdev->device = NULL;
+
+       in->ops.dpi->disconnect(in, &ddata->dssdev);
+}
+
+static int tfp410_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return -ENODEV;
+
+       if (omapdss_device_is_enabled(dssdev))
+               return 0;
+
+       in->ops.dpi->set_timings(in, &ddata->timings);
+       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+
+       r = in->ops.dpi->enable(in);
+       if (r)
+               return r;
+
+       if (gpio_is_valid(ddata->pd_gpio))
+               gpio_set_value_cansleep(ddata->pd_gpio, 1);
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+}
+
+static void tfp410_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_enabled(dssdev))
+               return;
+
+       if (gpio_is_valid(ddata->pd_gpio))
+               gpio_set_value_cansleep(ddata->pd_gpio, 0);
+
+       in->ops.dpi->disable(in);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void tfp410_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       ddata->timings = *timings;
+       dssdev->panel.timings = *timings;
+
+       in->ops.dpi->set_timings(in, timings);
+}
+
+static void tfp410_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       *timings = ddata->timings;
+}
+
+static int tfp410_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.dpi->check_timings(in, timings);
+}
+
+static const struct omapdss_dvi_ops tfp410_dvi_ops = {
+       .connect        = tfp410_connect,
+       .disconnect     = tfp410_disconnect,
+
+       .enable         = tfp410_enable,
+       .disable        = tfp410_disable,
+
+       .check_timings  = tfp410_check_timings,
+       .set_timings    = tfp410_set_timings,
+       .get_timings    = tfp410_get_timings,
+};
+
+static int tfp410_probe_pdata(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct encoder_tfp410_platform_data *pdata;
+       struct omap_dss_device *dssdev, *in;
+
+       pdata = dev_get_platdata(&pdev->dev);
+
+       ddata->pd_gpio = pdata->power_down_gpio;
+
+       ddata->data_lines = pdata->data_lines;
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&pdev->dev, "Failed to find video source\n");
+               return -ENODEV;
+       }
+
+       ddata->in = in;
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       return 0;
+}
+
+static int tfp410_probe(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata;
+       struct omap_dss_device *dssdev;
+       int r;
+
+       ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+       if (!ddata)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ddata);
+
+       if (dev_get_platdata(&pdev->dev)) {
+               r = tfp410_probe_pdata(pdev);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       if (gpio_is_valid(ddata->pd_gpio)) {
+               r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio,
+                               GPIOF_OUT_INIT_LOW, "tfp410 PD");
+               if (r) {
+                       dev_err(&pdev->dev, "Failed to request PD GPIO %d\n",
+                                       ddata->pd_gpio);
+                       goto err_gpio;
+               }
+       }
+
+       dssdev = &ddata->dssdev;
+       dssdev->ops.dvi = &tfp410_dvi_ops;
+       dssdev->dev = &pdev->dev;
+       dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+       dssdev->output_type = OMAP_DISPLAY_TYPE_DVI;
+       dssdev->owner = THIS_MODULE;
+       dssdev->phy.dpi.data_lines = ddata->data_lines;
+
+       r = omapdss_register_output(dssdev);
+       if (r) {
+               dev_err(&pdev->dev, "Failed to register output\n");
+               goto err_reg;
+       }
+
+       return 0;
+err_reg:
+err_gpio:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int __exit tfp410_remove(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       omapdss_unregister_output(&ddata->dssdev);
+
+       WARN_ON(omapdss_device_is_enabled(dssdev));
+       if (omapdss_device_is_enabled(dssdev))
+               tfp410_disable(dssdev);
+
+       WARN_ON(omapdss_device_is_connected(dssdev));
+       if (omapdss_device_is_connected(dssdev))
+               tfp410_disconnect(dssdev, dssdev->device);
+
+       omap_dss_put_device(in);
+
+       return 0;
+}
+
+static struct platform_driver tfp410_driver = {
+       .probe  = tfp410_probe,
+       .remove = __exit_p(tfp410_remove),
+       .driver = {
+               .name   = "tfp410",
+               .owner  = THIS_MODULE,
+       },
+};
+
+module_platform_driver(tfp410_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays-new/encoder-tpd12s015.c b/drivers/video/omap2/displays-new/encoder-tpd12s015.c
new file mode 100644 (file)
index 0000000..ce0e010
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * TPD12S015 HDMI ESD protection & level shifter chip driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@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/completion.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+struct panel_drv_data {
+       struct omap_dss_device dssdev;
+       struct omap_dss_device *in;
+
+       int ct_cp_hpd_gpio;
+       int ls_oe_gpio;
+       int hpd_gpio;
+
+       struct omap_video_timings timings;
+
+       struct completion hpd_completion;
+};
+
+#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
+
+static irqreturn_t tpd_hpd_irq_handler(int irq, void *data)
+{
+       struct panel_drv_data *ddata = data;
+       bool hpd;
+
+       hpd = gpio_get_value_cansleep(ddata->hpd_gpio);
+
+       dev_dbg(ddata->dssdev.dev, "hpd %d\n", hpd);
+
+       if (gpio_is_valid(ddata->ls_oe_gpio)) {
+               if (hpd)
+                       gpio_set_value_cansleep(ddata->ls_oe_gpio, 1);
+               else
+                       gpio_set_value_cansleep(ddata->ls_oe_gpio, 0);
+       }
+
+       complete_all(&ddata->hpd_completion);
+
+       return IRQ_HANDLED;
+}
+
+static int tpd_connect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       r = in->ops.hdmi->connect(in, dssdev);
+       if (r)
+               return r;
+
+       dst->output = dssdev;
+       dssdev->device = dst;
+
+       INIT_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 */
+       udelay(300);
+
+       /*
+        * If there's a cable connected, wait for the hpd irq to trigger,
+        * which turns on the level shifters.
+        */
+       if (gpio_get_value_cansleep(ddata->hpd_gpio)) {
+               unsigned long to;
+               to = wait_for_completion_timeout(&ddata->hpd_completion,
+                               msecs_to_jiffies(250));
+               WARN_ON_ONCE(to == 0);
+       }
+
+       return 0;
+}
+
+static void tpd_disconnect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       WARN_ON(dst != dssdev->device);
+
+       if (dst != dssdev->device)
+               return;
+
+       gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
+
+       dst->output = NULL;
+       dssdev->device = NULL;
+
+       in->ops.hdmi->disconnect(in, &ddata->dssdev);
+}
+
+static int tpd_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+               return 0;
+
+       in->ops.hdmi->set_timings(in, &ddata->timings);
+
+       r = in->ops.hdmi->enable(in);
+       if (r)
+               return r;
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return r;
+}
+
+static void tpd_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+               return;
+
+       in->ops.hdmi->disable(in);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void tpd_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       ddata->timings = *timings;
+       dssdev->panel.timings = *timings;
+
+       in->ops.hdmi->set_timings(in, timings);
+}
+
+static void tpd_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       *timings = ddata->timings;
+}
+
+static int tpd_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       r = in->ops.hdmi->check_timings(in, timings);
+
+       return r;
+}
+
+static int tpd_read_edid(struct omap_dss_device *dssdev,
+               u8 *edid, int len)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!gpio_get_value_cansleep(ddata->hpd_gpio))
+               return -ENODEV;
+
+       return in->ops.hdmi->read_edid(in, edid, len);
+}
+
+static bool tpd_detect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       return gpio_get_value_cansleep(ddata->hpd_gpio);
+}
+
+static int tpd_audio_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.hdmi->audio_enable(in);
+}
+
+static void tpd_audio_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       in->ops.hdmi->audio_disable(in);
+}
+
+static int tpd_audio_start(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.hdmi->audio_start(in);
+}
+
+static void tpd_audio_stop(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       in->ops.hdmi->audio_stop(in);
+}
+
+static bool tpd_audio_supported(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.hdmi->audio_supported(in);
+}
+
+static int tpd_audio_config(struct omap_dss_device *dssdev,
+               struct omap_dss_audio *audio)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.hdmi->audio_config(in, audio);
+}
+
+static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
+       .connect                = tpd_connect,
+       .disconnect             = tpd_disconnect,
+
+       .enable                 = tpd_enable,
+       .disable                = tpd_disable,
+
+       .check_timings          = tpd_check_timings,
+       .set_timings            = tpd_set_timings,
+       .get_timings            = tpd_get_timings,
+
+       .read_edid              = tpd_read_edid,
+       .detect                 = tpd_detect,
+
+       .audio_enable           = tpd_audio_enable,
+       .audio_disable          = tpd_audio_disable,
+       .audio_start            = tpd_audio_start,
+       .audio_stop             = tpd_audio_stop,
+       .audio_supported        = tpd_audio_supported,
+       .audio_config           = tpd_audio_config,
+};
+
+static int tpd_probe_pdata(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct encoder_tpd12s015_platform_data *pdata;
+       struct omap_dss_device *dssdev, *in;
+
+       pdata = dev_get_platdata(&pdev->dev);
+
+       ddata->ct_cp_hpd_gpio = pdata->ct_cp_hpd_gpio;
+       ddata->ls_oe_gpio = pdata->ls_oe_gpio;
+       ddata->hpd_gpio = pdata->hpd_gpio;
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&pdev->dev, "Failed to find video source\n");
+               return -ENODEV;
+       }
+
+       ddata->in = in;
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       return 0;
+}
+
+static int tpd_probe(struct platform_device *pdev)
+{
+       struct omap_dss_device *in, *dssdev;
+       struct panel_drv_data *ddata;
+       int r;
+
+       ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+       if (!ddata)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ddata);
+
+       init_completion(&ddata->hpd_completion);
+
+       if (dev_get_platdata(&pdev->dev)) {
+               r = tpd_probe_pdata(pdev);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       r = devm_gpio_request_one(&pdev->dev, ddata->ct_cp_hpd_gpio,
+                       GPIOF_OUT_INIT_LOW, "hdmi_ct_cp_hpd");
+       if (r)
+               goto err_gpio;
+
+       if (gpio_is_valid(ddata->ls_oe_gpio)) {
+               r = devm_gpio_request_one(&pdev->dev, ddata->ls_oe_gpio,
+                               GPIOF_OUT_INIT_LOW, "hdmi_ls_oe");
+               if (r)
+                       goto err_gpio;
+       }
+
+       r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
+                       GPIOF_DIR_IN, "hdmi_hpd");
+       if (r)
+               goto err_gpio;
+
+       r = devm_request_threaded_irq(&pdev->dev, gpio_to_irq(ddata->hpd_gpio),
+                                NULL, tpd_hpd_irq_handler,
+                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+                                IRQF_ONESHOT, "hpd", ddata);
+       if (r)
+               goto err_irq;
+
+       dssdev = &ddata->dssdev;
+       dssdev->ops.hdmi = &tpd_hdmi_ops;
+       dssdev->dev = &pdev->dev;
+       dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
+       dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
+       dssdev->owner = THIS_MODULE;
+
+       in = ddata->in;
+
+       r = omapdss_register_output(dssdev);
+       if (r) {
+               dev_err(&pdev->dev, "Failed to register output\n");
+               goto err_reg;
+       }
+
+       return 0;
+err_reg:
+err_irq:
+err_gpio:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int __exit tpd_remove(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       omapdss_unregister_output(&ddata->dssdev);
+
+       WARN_ON(omapdss_device_is_enabled(dssdev));
+       if (omapdss_device_is_enabled(dssdev))
+               tpd_disable(dssdev);
+
+       WARN_ON(omapdss_device_is_connected(dssdev));
+       if (omapdss_device_is_connected(dssdev))
+               tpd_disconnect(dssdev, dssdev->device);
+
+       omap_dss_put_device(in);
+
+       return 0;
+}
+
+static struct platform_driver tpd_driver = {
+       .probe  = tpd_probe,
+       .remove = __exit_p(tpd_remove),
+       .driver = {
+               .name   = "tpd12s015",
+               .owner  = THIS_MODULE,
+       },
+};
+
+module_platform_driver(tpd_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("TPD12S015 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays-new/panel-dpi.c b/drivers/video/omap2/displays-new/panel-dpi.c
new file mode 100644 (file)
index 0000000..5f8f7e7
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Generic MIPI DPI Panel Driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@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/gpio.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+struct panel_drv_data {
+       struct omap_dss_device dssdev;
+       struct omap_dss_device *in;
+
+       int data_lines;
+
+       struct omap_video_timings videomode;
+
+       int backlight_gpio;
+       int enable_gpio;
+};
+
+#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
+
+static int panel_dpi_connect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (omapdss_device_is_connected(dssdev))
+               return 0;
+
+       r = in->ops.dpi->connect(in, dssdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static void panel_dpi_disconnect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return;
+
+       in->ops.dpi->disconnect(in, dssdev);
+}
+
+static int panel_dpi_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return -ENODEV;
+
+       if (omapdss_device_is_enabled(dssdev))
+               return 0;
+
+       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+       in->ops.dpi->set_timings(in, &ddata->videomode);
+
+       r = in->ops.dpi->enable(in);
+       if (r)
+               return r;
+
+       if (gpio_is_valid(ddata->enable_gpio))
+               gpio_set_value_cansleep(ddata->enable_gpio, 1);
+
+       if (gpio_is_valid(ddata->backlight_gpio))
+               gpio_set_value_cansleep(ddata->backlight_gpio, 1);
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+}
+
+static void panel_dpi_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_enabled(dssdev))
+               return;
+
+       if (gpio_is_valid(ddata->enable_gpio))
+               gpio_set_value_cansleep(ddata->enable_gpio, 0);
+
+       if (gpio_is_valid(ddata->backlight_gpio))
+               gpio_set_value_cansleep(ddata->backlight_gpio, 0);
+
+       in->ops.dpi->disable(in);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void panel_dpi_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       ddata->videomode = *timings;
+       dssdev->panel.timings = *timings;
+
+       in->ops.dpi->set_timings(in, timings);
+}
+
+static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       *timings = ddata->videomode;
+}
+
+static int panel_dpi_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.dpi->check_timings(in, timings);
+}
+
+static struct omap_dss_driver panel_dpi_ops = {
+       .connect        = panel_dpi_connect,
+       .disconnect     = panel_dpi_disconnect,
+
+       .enable         = panel_dpi_enable,
+       .disable        = panel_dpi_disable,
+
+       .set_timings    = panel_dpi_set_timings,
+       .get_timings    = panel_dpi_get_timings,
+       .check_timings  = panel_dpi_check_timings,
+
+       .get_resolution = omapdss_default_get_resolution,
+};
+
+static int panel_dpi_probe_pdata(struct platform_device *pdev)
+{
+       const struct panel_dpi_platform_data *pdata;
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *dssdev, *in;
+       struct videomode vm;
+
+       pdata = dev_get_platdata(&pdev->dev);
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&pdev->dev, "failed to find video source '%s'\n",
+                               pdata->source);
+               return -EPROBE_DEFER;
+       }
+
+       ddata->in = in;
+
+       ddata->data_lines = pdata->data_lines;
+
+       videomode_from_timing(pdata->display_timing, &vm);
+       videomode_to_omap_video_timings(&vm, &ddata->videomode);
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       ddata->enable_gpio = pdata->enable_gpio;
+       ddata->backlight_gpio = pdata->backlight_gpio;
+
+       return 0;
+}
+
+static int panel_dpi_probe(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata;
+       struct omap_dss_device *dssdev;
+       int r;
+
+       ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+       if (ddata == NULL)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ddata);
+
+       if (dev_get_platdata(&pdev->dev)) {
+               r = panel_dpi_probe_pdata(pdev);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       if (gpio_is_valid(ddata->enable_gpio)) {
+               r = devm_gpio_request_one(&pdev->dev, ddata->enable_gpio,
+                               GPIOF_OUT_INIT_LOW, "panel enable");
+               if (r)
+                       goto err_gpio;
+       }
+
+       if (gpio_is_valid(ddata->backlight_gpio)) {
+               r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio,
+                               GPIOF_OUT_INIT_LOW, "panel backlight");
+               if (r)
+                       goto err_gpio;
+       }
+
+       dssdev = &ddata->dssdev;
+       dssdev->dev = &pdev->dev;
+       dssdev->driver = &panel_dpi_ops;
+       dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+       dssdev->owner = THIS_MODULE;
+       dssdev->panel.timings = ddata->videomode;
+       dssdev->phy.dpi.data_lines = ddata->data_lines;
+
+       r = omapdss_register_display(dssdev);
+       if (r) {
+               dev_err(&pdev->dev, "Failed to register panel\n");
+               goto err_reg;
+       }
+
+       return 0;
+
+err_reg:
+err_gpio:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int __exit panel_dpi_remove(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       omapdss_unregister_display(dssdev);
+
+       panel_dpi_disable(dssdev);
+       panel_dpi_disconnect(dssdev);
+
+       omap_dss_put_device(in);
+
+       return 0;
+}
+
+static struct platform_driver panel_dpi_driver = {
+       .probe = panel_dpi_probe,
+       .remove = __exit_p(panel_dpi_remove),
+       .driver = {
+               .name = "panel-dpi",
+               .owner = THIS_MODULE,
+       },
+};
+
+module_platform_driver(panel_dpi_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays-new/panel-dsi-cm.c b/drivers/video/omap2/displays-new/panel-dsi-cm.c
new file mode 100644 (file)
index 0000000..aaaea64
--- /dev/null
@@ -0,0 +1,1336 @@
+/*
+ * Generic DSI Command Mode panel driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@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.
+ */
+
+/* #define DEBUG */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+#include <video/mipi_display.h>
+
+/* DSI Virtual channel. Hardcoded for now. */
+#define TCH 0
+
+#define DCS_READ_NUM_ERRORS    0x05
+#define DCS_BRIGHTNESS         0x51
+#define DCS_CTRL_DISPLAY       0x53
+#define DCS_GET_ID1            0xda
+#define DCS_GET_ID2            0xdb
+#define DCS_GET_ID3            0xdc
+
+struct panel_drv_data {
+       struct omap_dss_device dssdev;
+       struct omap_dss_device *in;
+
+       struct omap_video_timings timings;
+
+       struct platform_device *pdev;
+
+       struct mutex lock;
+
+       struct backlight_device *bldev;
+
+       unsigned long   hw_guard_end;   /* next value of jiffies when we can
+                                        * issue the next sleep in/out command
+                                        */
+       unsigned long   hw_guard_wait;  /* max guard time in jiffies */
+
+       /* panel HW configuration from DT or platform data */
+       int reset_gpio;
+       int ext_te_gpio;
+
+       bool use_dsi_backlight;
+
+       struct omap_dsi_pin_config pin_config;
+
+       /* runtime variables */
+       bool enabled;
+
+       bool te_enabled;
+
+       atomic_t do_update;
+       int channel;
+
+       struct delayed_work te_timeout_work;
+
+       bool intro_printed;
+
+       struct workqueue_struct *workqueue;
+
+       bool ulps_enabled;
+       unsigned ulps_timeout;
+       struct delayed_work ulps_work;
+};
+
+#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
+
+static irqreturn_t dsicm_te_isr(int irq, void *data);
+static void dsicm_te_timeout_work_callback(struct work_struct *work);
+static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable);
+
+static int dsicm_panel_reset(struct panel_drv_data *ddata);
+
+static void dsicm_ulps_work(struct work_struct *work);
+
+static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
+{
+       ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
+       ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
+}
+
+static void hw_guard_wait(struct panel_drv_data *ddata)
+{
+       unsigned long wait = ddata->hw_guard_end - jiffies;
+
+       if ((long)wait > 0 && wait <= ddata->hw_guard_wait) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(wait);
+       }
+}
+
+static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data)
+{
+       struct omap_dss_device *in = ddata->in;
+       int r;
+       u8 buf[1];
+
+       r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, buf, 1);
+
+       if (r < 0)
+               return r;
+
+       *data = buf[0];
+
+       return 0;
+}
+
+static int dsicm_dcs_write_0(struct panel_drv_data *ddata, u8 dcs_cmd)
+{
+       struct omap_dss_device *in = ddata->in;
+       return in->ops.dsi->dcs_write(in, ddata->channel, &dcs_cmd, 1);
+}
+
+static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param)
+{
+       struct omap_dss_device *in = ddata->in;
+       u8 buf[2] = { dcs_cmd, param };
+
+       return in->ops.dsi->dcs_write(in, ddata->channel, buf, 2);
+}
+
+static int dsicm_sleep_in(struct panel_drv_data *ddata)
+
+{
+       struct omap_dss_device *in = ddata->in;
+       u8 cmd;
+       int r;
+
+       hw_guard_wait(ddata);
+
+       cmd = MIPI_DCS_ENTER_SLEEP_MODE;
+       r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, &cmd, 1);
+       if (r)
+               return r;
+
+       hw_guard_start(ddata, 120);
+
+       usleep_range(5000, 10000);
+
+       return 0;
+}
+
+static int dsicm_sleep_out(struct panel_drv_data *ddata)
+{
+       int r;
+
+       hw_guard_wait(ddata);
+
+       r = dsicm_dcs_write_0(ddata, MIPI_DCS_EXIT_SLEEP_MODE);
+       if (r)
+               return r;
+
+       hw_guard_start(ddata, 120);
+
+       usleep_range(5000, 10000);
+
+       return 0;
+}
+
+static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3)
+{
+       int r;
+
+       r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1);
+       if (r)
+               return r;
+       r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2);
+       if (r)
+               return r;
+       r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static int dsicm_set_update_window(struct panel_drv_data *ddata,
+               u16 x, u16 y, u16 w, u16 h)
+{
+       struct omap_dss_device *in = ddata->in;
+       int r;
+       u16 x1 = x;
+       u16 x2 = x + w - 1;
+       u16 y1 = y;
+       u16 y2 = y + h - 1;
+
+       u8 buf[5];
+       buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS;
+       buf[1] = (x1 >> 8) & 0xff;
+       buf[2] = (x1 >> 0) & 0xff;
+       buf[3] = (x2 >> 8) & 0xff;
+       buf[4] = (x2 >> 0) & 0xff;
+
+       r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf));
+       if (r)
+               return r;
+
+       buf[0] = MIPI_DCS_SET_PAGE_ADDRESS;
+       buf[1] = (y1 >> 8) & 0xff;
+       buf[2] = (y1 >> 0) & 0xff;
+       buf[3] = (y2 >> 8) & 0xff;
+       buf[4] = (y2 >> 0) & 0xff;
+
+       r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf));
+       if (r)
+               return r;
+
+       in->ops.dsi->bta_sync(in, ddata->channel);
+
+       return r;
+}
+
+static void dsicm_queue_ulps_work(struct panel_drv_data *ddata)
+{
+       if (ddata->ulps_timeout > 0)
+               queue_delayed_work(ddata->workqueue, &ddata->ulps_work,
+                               msecs_to_jiffies(ddata->ulps_timeout));
+}
+
+static void dsicm_cancel_ulps_work(struct panel_drv_data *ddata)
+{
+       cancel_delayed_work(&ddata->ulps_work);
+}
+
+static int dsicm_enter_ulps(struct panel_drv_data *ddata)
+{
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (ddata->ulps_enabled)
+               return 0;
+
+       dsicm_cancel_ulps_work(ddata);
+
+       r = _dsicm_enable_te(ddata, false);
+       if (r)
+               goto err;
+
+       if (gpio_is_valid(ddata->ext_te_gpio))
+               disable_irq(gpio_to_irq(ddata->ext_te_gpio));
+
+       in->ops.dsi->disable(in, false, true);
+
+       ddata->ulps_enabled = true;
+
+       return 0;
+
+err:
+       dev_err(&ddata->pdev->dev, "enter ULPS failed");
+       dsicm_panel_reset(ddata);
+
+       ddata->ulps_enabled = false;
+
+       dsicm_queue_ulps_work(ddata);
+
+       return r;
+}
+
+static int dsicm_exit_ulps(struct panel_drv_data *ddata)
+{
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (!ddata->ulps_enabled)
+               return 0;
+
+       r = in->ops.dsi->enable(in);
+       if (r) {
+               dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
+               goto err1;
+       }
+
+       in->ops.dsi->enable_hs(in, ddata->channel, true);
+
+       r = _dsicm_enable_te(ddata, true);
+       if (r) {
+               dev_err(&ddata->pdev->dev, "failed to re-enable TE");
+               goto err2;
+       }
+
+       if (gpio_is_valid(ddata->ext_te_gpio))
+               enable_irq(gpio_to_irq(ddata->ext_te_gpio));
+
+       dsicm_queue_ulps_work(ddata);
+
+       ddata->ulps_enabled = false;
+
+       return 0;
+
+err2:
+       dev_err(&ddata->pdev->dev, "failed to exit ULPS");
+
+       r = dsicm_panel_reset(ddata);
+       if (!r) {
+               if (gpio_is_valid(ddata->ext_te_gpio))
+                       enable_irq(gpio_to_irq(ddata->ext_te_gpio));
+               ddata->ulps_enabled = false;
+       }
+err1:
+       dsicm_queue_ulps_work(ddata);
+
+       return r;
+}
+
+static int dsicm_wake_up(struct panel_drv_data *ddata)
+{
+       if (ddata->ulps_enabled)
+               return dsicm_exit_ulps(ddata);
+
+       dsicm_cancel_ulps_work(ddata);
+       dsicm_queue_ulps_work(ddata);
+       return 0;
+}
+
+static int dsicm_bl_update_status(struct backlight_device *dev)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+       int level;
+
+       if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+                       dev->props.power == FB_BLANK_UNBLANK)
+               level = dev->props.brightness;
+       else
+               level = 0;
+
+       dev_dbg(&ddata->pdev->dev, "update brightness to %d\n", level);
+
+       mutex_lock(&ddata->lock);
+
+       if (ddata->enabled) {
+               in->ops.dsi->bus_lock(in);
+
+               r = dsicm_wake_up(ddata);
+               if (!r)
+                       r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level);
+
+               in->ops.dsi->bus_unlock(in);
+       } else {
+               r = 0;
+       }
+
+       mutex_unlock(&ddata->lock);
+
+       return r;
+}
+
+static int dsicm_bl_get_intensity(struct backlight_device *dev)
+{
+       if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+                       dev->props.power == FB_BLANK_UNBLANK)
+               return dev->props.brightness;
+
+       return 0;
+}
+
+static const struct backlight_ops dsicm_bl_ops = {
+       .get_brightness = dsicm_bl_get_intensity,
+       .update_status  = dsicm_bl_update_status,
+};
+
+static void dsicm_get_resolution(struct omap_dss_device *dssdev,
+               u16 *xres, u16 *yres)
+{
+       *xres = dssdev->panel.timings.x_res;
+       *yres = dssdev->panel.timings.y_res;
+}
+
+static ssize_t dsicm_num_errors_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *in = ddata->in;
+       u8 errors = 0;
+       int r;
+
+       mutex_lock(&ddata->lock);
+
+       if (ddata->enabled) {
+               in->ops.dsi->bus_lock(in);
+
+               r = dsicm_wake_up(ddata);
+               if (!r)
+                       r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS,
+                                       &errors);
+
+               in->ops.dsi->bus_unlock(in);
+       } else {
+               r = -ENODEV;
+       }
+
+       mutex_unlock(&ddata->lock);
+
+       if (r)
+               return r;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", errors);
+}
+
+static ssize_t dsicm_hw_revision_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *in = ddata->in;
+       u8 id1, id2, id3;
+       int r;
+
+       mutex_lock(&ddata->lock);
+
+       if (ddata->enabled) {
+               in->ops.dsi->bus_lock(in);
+
+               r = dsicm_wake_up(ddata);
+               if (!r)
+                       r = dsicm_get_id(ddata, &id1, &id2, &id3);
+
+               in->ops.dsi->bus_unlock(in);
+       } else {
+               r = -ENODEV;
+       }
+
+       mutex_unlock(&ddata->lock);
+
+       if (r)
+               return r;
+
+       return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
+}
+
+static ssize_t dsicm_store_ulps(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *in = ddata->in;
+       unsigned long t;
+       int r;
+
+       r = kstrtoul(buf, 0, &t);
+       if (r)
+               return r;
+
+       mutex_lock(&ddata->lock);
+
+       if (ddata->enabled) {
+               in->ops.dsi->bus_lock(in);
+
+               if (t)
+                       r = dsicm_enter_ulps(ddata);
+               else
+                       r = dsicm_wake_up(ddata);
+
+               in->ops.dsi->bus_unlock(in);
+       }
+
+       mutex_unlock(&ddata->lock);
+
+       if (r)
+               return r;
+
+       return count;
+}
+
+static ssize_t dsicm_show_ulps(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       unsigned t;
+
+       mutex_lock(&ddata->lock);
+       t = ddata->ulps_enabled;
+       mutex_unlock(&ddata->lock);
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", t);
+}
+
+static ssize_t dsicm_store_ulps_timeout(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *in = ddata->in;
+       unsigned long t;
+       int r;
+
+       r = kstrtoul(buf, 0, &t);
+       if (r)
+               return r;
+
+       mutex_lock(&ddata->lock);
+       ddata->ulps_timeout = t;
+
+       if (ddata->enabled) {
+               /* dsicm_wake_up will restart the timer */
+               in->ops.dsi->bus_lock(in);
+               r = dsicm_wake_up(ddata);
+               in->ops.dsi->bus_unlock(in);
+       }
+
+       mutex_unlock(&ddata->lock);
+
+       if (r)
+               return r;
+
+       return count;
+}
+
+static ssize_t dsicm_show_ulps_timeout(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       unsigned t;
+
+       mutex_lock(&ddata->lock);
+       t = ddata->ulps_timeout;
+       mutex_unlock(&ddata->lock);
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", t);
+}
+
+static DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL);
+static DEVICE_ATTR(hw_revision, S_IRUGO, dsicm_hw_revision_show, NULL);
+static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR,
+               dsicm_show_ulps, dsicm_store_ulps);
+static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR,
+               dsicm_show_ulps_timeout, dsicm_store_ulps_timeout);
+
+static struct attribute *dsicm_attrs[] = {
+       &dev_attr_num_dsi_errors.attr,
+       &dev_attr_hw_revision.attr,
+       &dev_attr_ulps.attr,
+       &dev_attr_ulps_timeout.attr,
+       NULL,
+};
+
+static struct attribute_group dsicm_attr_group = {
+       .attrs = dsicm_attrs,
+};
+
+static void dsicm_hw_reset(struct panel_drv_data *ddata)
+{
+       if (!gpio_is_valid(ddata->reset_gpio))
+               return;
+
+       gpio_set_value(ddata->reset_gpio, 1);
+       udelay(10);
+       /* reset the panel */
+       gpio_set_value(ddata->reset_gpio, 0);
+       /* assert reset */
+       udelay(10);
+       gpio_set_value(ddata->reset_gpio, 1);
+       /* wait after releasing reset */
+       usleep_range(5000, 10000);
+}
+
+static int dsicm_power_on(struct panel_drv_data *ddata)
+{
+       struct omap_dss_device *in = ddata->in;
+       u8 id1, id2, id3;
+       int r;
+       struct omap_dss_dsi_config dsi_config = {
+               .mode = OMAP_DSS_DSI_CMD_MODE,
+               .pixel_format = OMAP_DSS_DSI_FMT_RGB888,
+               .timings = &ddata->timings,
+               .hs_clk_min = 150000000,
+               .hs_clk_max = 300000000,
+               .lp_clk_min = 7000000,
+               .lp_clk_max = 10000000,
+       };
+
+       r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
+       if (r) {
+               dev_err(&ddata->pdev->dev, "failed to configure DSI pins\n");
+               goto err0;
+       };
+
+       r = in->ops.dsi->set_config(in, &dsi_config);
+       if (r) {
+               dev_err(&ddata->pdev->dev, "failed to configure DSI\n");
+               goto err0;
+       }
+
+       r = in->ops.dsi->enable(in);
+       if (r) {
+               dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
+               goto err0;
+       }
+
+       dsicm_hw_reset(ddata);
+
+       in->ops.dsi->enable_hs(in, ddata->channel, false);
+
+       r = dsicm_sleep_out(ddata);
+       if (r)
+               goto err;
+
+       r = dsicm_get_id(ddata, &id1, &id2, &id3);
+       if (r)
+               goto err;
+
+       r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, 0xff);
+       if (r)
+               goto err;
+
+       r = dsicm_dcs_write_1(ddata, DCS_CTRL_DISPLAY,
+                       (1<<2) | (1<<5));       /* BL | BCTRL */
+       if (r)
+               goto err;
+
+       r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_PIXEL_FORMAT,
+               MIPI_DCS_PIXEL_FMT_24BIT);
+       if (r)
+               goto err;
+
+       r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_ON);
+       if (r)
+               goto err;
+
+       r = _dsicm_enable_te(ddata, ddata->te_enabled);
+       if (r)
+               goto err;
+
+       r = in->ops.dsi->enable_video_output(in, ddata->channel);
+       if (r)
+               goto err;
+
+       ddata->enabled = 1;
+
+       if (!ddata->intro_printed) {
+               dev_info(&ddata->pdev->dev, "panel revision %02x.%02x.%02x\n",
+                       id1, id2, id3);
+               ddata->intro_printed = true;
+       }
+
+       in->ops.dsi->enable_hs(in, ddata->channel, true);
+
+       return 0;
+err:
+       dev_err(&ddata->pdev->dev, "error while enabling panel, issuing HW reset\n");
+
+       dsicm_hw_reset(ddata);
+
+       in->ops.dsi->disable(in, true, false);
+err0:
+       return r;
+}
+
+static void dsicm_power_off(struct panel_drv_data *ddata)
+{
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       in->ops.dsi->disable_video_output(in, ddata->channel);
+
+       r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_OFF);
+       if (!r)
+               r = dsicm_sleep_in(ddata);
+
+       if (r) {
+               dev_err(&ddata->pdev->dev,
+                               "error disabling panel, issuing HW reset\n");
+               dsicm_hw_reset(ddata);
+       }
+
+       in->ops.dsi->disable(in, true, false);
+
+       ddata->enabled = 0;
+}
+
+static int dsicm_panel_reset(struct panel_drv_data *ddata)
+{
+       dev_err(&ddata->pdev->dev, "performing LCD reset\n");
+
+       dsicm_power_off(ddata);
+       dsicm_hw_reset(ddata);
+       return dsicm_power_on(ddata);
+}
+
+static int dsicm_connect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       struct device *dev = &ddata->pdev->dev;
+       int r;
+
+       if (omapdss_device_is_connected(dssdev))
+               return 0;
+
+       r = in->ops.dsi->connect(in, dssdev);
+       if (r) {
+               dev_err(dev, "Failed to connect to video source\n");
+               return r;
+       }
+
+       r = in->ops.dsi->request_vc(ddata->in, &ddata->channel);
+       if (r) {
+               dev_err(dev, "failed to get virtual channel\n");
+               goto err_req_vc;
+       }
+
+       r = in->ops.dsi->set_vc_id(ddata->in, ddata->channel, TCH);
+       if (r) {
+               dev_err(dev, "failed to set VC_ID\n");
+               goto err_vc_id;
+       }
+
+       return 0;
+
+err_vc_id:
+       in->ops.dsi->release_vc(ddata->in, ddata->channel);
+err_req_vc:
+       in->ops.dsi->disconnect(in, dssdev);
+       return r;
+}
+
+static void dsicm_disconnect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return;
+
+       in->ops.dsi->release_vc(in, ddata->channel);
+       in->ops.dsi->disconnect(in, dssdev);
+}
+
+static int dsicm_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       dev_dbg(&ddata->pdev->dev, "enable\n");
+
+       mutex_lock(&ddata->lock);
+
+       if (!omapdss_device_is_connected(dssdev)) {
+               r = -ENODEV;
+               goto err;
+       }
+
+       if (omapdss_device_is_enabled(dssdev)) {
+               r = 0;
+               goto err;
+       }
+
+       in->ops.dsi->bus_lock(in);
+
+       r = dsicm_power_on(ddata);
+
+       in->ops.dsi->bus_unlock(in);
+
+       if (r)
+               goto err;
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       mutex_unlock(&ddata->lock);
+
+       return 0;
+err:
+       dev_dbg(&ddata->pdev->dev, "enable failed\n");
+       mutex_unlock(&ddata->lock);
+       return r;
+}
+
+static void dsicm_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       dev_dbg(&ddata->pdev->dev, "disable\n");
+
+       mutex_lock(&ddata->lock);
+
+       dsicm_cancel_ulps_work(ddata);
+
+       in->ops.dsi->bus_lock(in);
+
+       if (omapdss_device_is_enabled(dssdev)) {
+               r = dsicm_wake_up(ddata);
+               if (!r)
+                       dsicm_power_off(ddata);
+       }
+
+       in->ops.dsi->bus_unlock(in);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+       mutex_unlock(&ddata->lock);
+}
+
+static void dsicm_framedone_cb(int err, void *data)
+{
+       struct panel_drv_data *ddata = data;
+       struct omap_dss_device *in = ddata->in;
+
+       dev_dbg(&ddata->pdev->dev, "framedone, err %d\n", err);
+       in->ops.dsi->bus_unlock(ddata->in);
+}
+
+static irqreturn_t dsicm_te_isr(int irq, void *data)
+{
+       struct panel_drv_data *ddata = data;
+       struct omap_dss_device *in = ddata->in;
+       int old;
+       int r;
+
+       old = atomic_cmpxchg(&ddata->do_update, 1, 0);
+
+       if (old) {
+               cancel_delayed_work(&ddata->te_timeout_work);
+
+               r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
+                               ddata);
+               if (r)
+                       goto err;
+       }
+
+       return IRQ_HANDLED;
+err:
+       dev_err(&ddata->pdev->dev, "start update failed\n");
+       in->ops.dsi->bus_unlock(in);
+       return IRQ_HANDLED;
+}
+
+static void dsicm_te_timeout_work_callback(struct work_struct *work)
+{
+       struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
+                                       te_timeout_work.work);
+       struct omap_dss_device *in = ddata->in;
+
+       dev_err(&ddata->pdev->dev, "TE not received for 250ms!\n");
+
+       atomic_set(&ddata->do_update, 0);
+       in->ops.dsi->bus_unlock(in);
+}
+
+static int dsicm_update(struct omap_dss_device *dssdev,
+                                   u16 x, u16 y, u16 w, u16 h)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       dev_dbg(&ddata->pdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
+
+       mutex_lock(&ddata->lock);
+       in->ops.dsi->bus_lock(in);
+
+       r = dsicm_wake_up(ddata);
+       if (r)
+               goto err;
+
+       if (!ddata->enabled) {
+               r = 0;
+               goto err;
+       }
+
+       /* XXX no need to send this every frame, but dsi break if not done */
+       r = dsicm_set_update_window(ddata, 0, 0,
+                       dssdev->panel.timings.x_res,
+                       dssdev->panel.timings.y_res);
+       if (r)
+               goto err;
+
+       if (ddata->te_enabled && gpio_is_valid(ddata->ext_te_gpio)) {
+               schedule_delayed_work(&ddata->te_timeout_work,
+                               msecs_to_jiffies(250));
+               atomic_set(&ddata->do_update, 1);
+       } else {
+               r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
+                               ddata);
+               if (r)
+                       goto err;
+       }
+
+       /* note: no bus_unlock here. unlock is in framedone_cb */
+       mutex_unlock(&ddata->lock);
+       return 0;
+err:
+       in->ops.dsi->bus_unlock(in);
+       mutex_unlock(&ddata->lock);
+       return r;
+}
+
+static int dsicm_sync(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       dev_dbg(&ddata->pdev->dev, "sync\n");
+
+       mutex_lock(&ddata->lock);
+       in->ops.dsi->bus_lock(in);
+       in->ops.dsi->bus_unlock(in);
+       mutex_unlock(&ddata->lock);
+
+       dev_dbg(&ddata->pdev->dev, "sync done\n");
+
+       return 0;
+}
+
+static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
+{
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (enable)
+               r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_TEAR_ON, 0);
+       else
+               r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF);
+
+       if (!gpio_is_valid(ddata->ext_te_gpio))
+               in->ops.dsi->enable_te(in, enable);
+
+       /* possible panel bug */
+       msleep(100);
+
+       return r;
+}
+
+static int dsicm_enable_te(struct omap_dss_device *dssdev, bool enable)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       mutex_lock(&ddata->lock);
+
+       if (ddata->te_enabled == enable)
+               goto end;
+
+       in->ops.dsi->bus_lock(in);
+
+       if (ddata->enabled) {
+               r = dsicm_wake_up(ddata);
+               if (r)
+                       goto err;
+
+               r = _dsicm_enable_te(ddata, enable);
+               if (r)
+                       goto err;
+       }
+
+       ddata->te_enabled = enable;
+
+       in->ops.dsi->bus_unlock(in);
+end:
+       mutex_unlock(&ddata->lock);
+
+       return 0;
+err:
+       in->ops.dsi->bus_unlock(in);
+       mutex_unlock(&ddata->lock);
+
+       return r;
+}
+
+static int dsicm_get_te(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       int r;
+
+       mutex_lock(&ddata->lock);
+       r = ddata->te_enabled;
+       mutex_unlock(&ddata->lock);
+
+       return r;
+}
+
+static int dsicm_memory_read(struct omap_dss_device *dssdev,
+               void *buf, size_t size,
+               u16 x, u16 y, u16 w, u16 h)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+       int first = 1;
+       int plen;
+       unsigned buf_used = 0;
+
+       if (size < w * h * 3)
+               return -ENOMEM;
+
+       mutex_lock(&ddata->lock);
+
+       if (!ddata->enabled) {
+               r = -ENODEV;
+               goto err1;
+       }
+
+       size = min(w * h * 3,
+                       dssdev->panel.timings.x_res *
+                       dssdev->panel.timings.y_res * 3);
+
+       in->ops.dsi->bus_lock(in);
+
+       r = dsicm_wake_up(ddata);
+       if (r)
+               goto err2;
+
+       /* plen 1 or 2 goes into short packet. until checksum error is fixed,
+        * use short packets. plen 32 works, but bigger packets seem to cause
+        * an error. */
+       if (size % 2)
+               plen = 1;
+       else
+               plen = 2;
+
+       dsicm_set_update_window(ddata, x, y, w, h);
+
+       r = in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, plen);
+       if (r)
+               goto err2;
+
+       while (buf_used < size) {
+               u8 dcs_cmd = first ? 0x2e : 0x3e;
+               first = 0;
+
+               r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd,
+                               buf + buf_used, size - buf_used);
+
+               if (r < 0) {
+                       dev_err(dssdev->dev, "read error\n");
+                       goto err3;
+               }
+
+               buf_used += r;
+
+               if (r < plen) {
+                       dev_err(&ddata->pdev->dev, "short read\n");
+                       break;
+               }
+
+               if (signal_pending(current)) {
+                       dev_err(&ddata->pdev->dev, "signal pending, "
+                                       "aborting memory read\n");
+                       r = -ERESTARTSYS;
+                       goto err3;
+               }
+       }
+
+       r = buf_used;
+
+err3:
+       in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, 1);
+err2:
+       in->ops.dsi->bus_unlock(in);
+err1:
+       mutex_unlock(&ddata->lock);
+       return r;
+}
+
+static void dsicm_ulps_work(struct work_struct *work)
+{
+       struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
+                       ulps_work.work);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       mutex_lock(&ddata->lock);
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !ddata->enabled) {
+               mutex_unlock(&ddata->lock);
+               return;
+       }
+
+       in->ops.dsi->bus_lock(in);
+
+       dsicm_enter_ulps(ddata);
+
+       in->ops.dsi->bus_unlock(in);
+       mutex_unlock(&ddata->lock);
+}
+
+static struct omap_dss_driver dsicm_ops = {
+       .connect        = dsicm_connect,
+       .disconnect     = dsicm_disconnect,
+
+       .enable         = dsicm_enable,
+       .disable        = dsicm_disable,
+
+       .update         = dsicm_update,
+       .sync           = dsicm_sync,
+
+       .get_resolution = dsicm_get_resolution,
+       .get_recommended_bpp = omapdss_default_get_recommended_bpp,
+
+       .enable_te      = dsicm_enable_te,
+       .get_te         = dsicm_get_te,
+
+       .memory_read    = dsicm_memory_read,
+};
+
+static int dsicm_probe_pdata(struct platform_device *pdev)
+{
+       const struct panel_dsicm_platform_data *pdata;
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *dssdev, *in;
+
+       pdata = dev_get_platdata(&pdev->dev);
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&pdev->dev, "failed to find video source\n");
+               return -EPROBE_DEFER;
+       }
+       ddata->in = in;
+
+       ddata->reset_gpio = pdata->reset_gpio;
+
+       if (pdata->use_ext_te)
+               ddata->ext_te_gpio = pdata->ext_te_gpio;
+       else
+               ddata->ext_te_gpio = -1;
+
+       ddata->ulps_timeout = pdata->ulps_timeout;
+
+       ddata->use_dsi_backlight = pdata->use_dsi_backlight;
+
+       ddata->pin_config = pdata->pin_config;
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       return 0;
+}
+
+static int dsicm_probe(struct platform_device *pdev)
+{
+       struct backlight_properties props;
+       struct panel_drv_data *ddata;
+       struct backlight_device *bldev = NULL;
+       struct device *dev = &pdev->dev;
+       struct omap_dss_device *dssdev;
+       int r;
+
+       dev_dbg(dev, "probe\n");
+
+       ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
+       if (!ddata)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ddata);
+       ddata->pdev = pdev;
+
+       if (dev_get_platdata(dev)) {
+               r = dsicm_probe_pdata(pdev);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       ddata->timings.x_res = 864;
+       ddata->timings.y_res = 480;
+       ddata->timings.pixel_clock = DIV_ROUND_UP(864 * 480 * 60, 1000);
+
+       dssdev = &ddata->dssdev;
+       dssdev->dev = dev;
+       dssdev->driver = &dsicm_ops;
+       dssdev->panel.timings = ddata->timings;
+       dssdev->type = OMAP_DISPLAY_TYPE_DSI;
+       dssdev->owner = THIS_MODULE;
+
+       dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
+       dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
+               OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
+
+       r = omapdss_register_display(dssdev);
+       if (r) {
+               dev_err(dev, "Failed to register panel\n");
+               goto err_reg;
+       }
+
+       mutex_init(&ddata->lock);
+
+       atomic_set(&ddata->do_update, 0);
+
+       if (gpio_is_valid(ddata->reset_gpio)) {
+               r = devm_gpio_request_one(dev, ddata->reset_gpio,
+                               GPIOF_OUT_INIT_LOW, "taal rst");
+               if (r) {
+                       dev_err(dev, "failed to request reset gpio\n");
+                       return r;
+               }
+       }
+
+       if (gpio_is_valid(ddata->ext_te_gpio)) {
+               r = devm_gpio_request_one(dev, ddata->ext_te_gpio,
+                               GPIOF_IN, "taal irq");
+               if (r) {
+                       dev_err(dev, "GPIO request failed\n");
+                       return r;
+               }
+
+               r = devm_request_irq(dev, gpio_to_irq(ddata->ext_te_gpio),
+                               dsicm_te_isr,
+                               IRQF_TRIGGER_RISING,
+                               "taal vsync", ddata);
+
+               if (r) {
+                       dev_err(dev, "IRQ request failed\n");
+                       return r;
+               }
+
+               INIT_DEFERRABLE_WORK(&ddata->te_timeout_work,
+                                       dsicm_te_timeout_work_callback);
+
+               dev_dbg(dev, "Using GPIO TE\n");
+       }
+
+       ddata->workqueue = create_singlethread_workqueue("dsicm_wq");
+       if (ddata->workqueue == NULL) {
+               dev_err(dev, "can't create workqueue\n");
+               return -ENOMEM;
+       }
+       INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work);
+
+       dsicm_hw_reset(ddata);
+
+       if (ddata->use_dsi_backlight) {
+               memset(&props, 0, sizeof(struct backlight_properties));
+               props.max_brightness = 255;
+
+               props.type = BACKLIGHT_RAW;
+               bldev = backlight_device_register(dev_name(dev),
+                               dev, ddata, &dsicm_bl_ops, &props);
+               if (IS_ERR(bldev)) {
+                       r = PTR_ERR(bldev);
+                       goto err_bl;
+               }
+
+               ddata->bldev = bldev;
+
+               bldev->props.fb_blank = FB_BLANK_UNBLANK;
+               bldev->props.power = FB_BLANK_UNBLANK;
+               bldev->props.brightness = 255;
+
+               dsicm_bl_update_status(bldev);
+       }
+
+       r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);
+       if (r) {
+               dev_err(dev, "failed to create sysfs files\n");
+               goto err_sysfs_create;
+       }
+
+       return 0;
+
+err_sysfs_create:
+       if (bldev != NULL)
+               backlight_device_unregister(bldev);
+err_bl:
+       destroy_workqueue(ddata->workqueue);
+err_reg:
+       return r;
+}
+
+static int __exit dsicm_remove(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct backlight_device *bldev;
+
+       dev_dbg(&pdev->dev, "remove\n");
+
+       omapdss_unregister_display(dssdev);
+
+       dsicm_disable(dssdev);
+       dsicm_disconnect(dssdev);
+
+       sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group);
+
+       bldev = ddata->bldev;
+       if (bldev != NULL) {
+               bldev->props.power = FB_BLANK_POWERDOWN;
+               dsicm_bl_update_status(bldev);
+               backlight_device_unregister(bldev);
+       }
+
+       omap_dss_put_device(ddata->in);
+
+       dsicm_cancel_ulps_work(ddata);
+       destroy_workqueue(ddata->workqueue);
+
+       /* reset, to be sure that the panel is in a valid state */
+       dsicm_hw_reset(ddata);
+
+       return 0;
+}
+
+static struct platform_driver dsicm_driver = {
+       .probe = dsicm_probe,
+       .remove = __exit_p(dsicm_remove),
+       .driver = {
+               .name = "panel-dsi-cm",
+               .owner = THIS_MODULE,
+       },
+};
+
+module_platform_driver(dsicm_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c
new file mode 100644 (file)
index 0000000..6e8977b
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * LG.Philips LB035Q02 LCD Panel driver
+ *
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ * Based on a driver by: Steve Sakoman <steve@sakoman.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/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+static struct omap_video_timings lb035q02_timings = {
+       .x_res = 320,
+       .y_res = 240,
+
+       .pixel_clock    = 6500,
+
+       .hsw            = 2,
+       .hfp            = 20,
+       .hbp            = 68,
+
+       .vsw            = 2,
+       .vfp            = 4,
+       .vbp            = 18,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+};
+
+struct panel_drv_data {
+       struct omap_dss_device dssdev;
+       struct omap_dss_device *in;
+
+       struct spi_device *spi;
+
+       int data_lines;
+
+       struct omap_video_timings videomode;
+
+       int reset_gpio;
+       int backlight_gpio;
+       int enable_gpio;
+};
+
+#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
+
+static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val)
+{
+       struct spi_message msg;
+       struct spi_transfer index_xfer = {
+               .len            = 3,
+               .cs_change      = 1,
+       };
+       struct spi_transfer value_xfer = {
+               .len            = 3,
+       };
+       u8      buffer[16];
+
+       spi_message_init(&msg);
+
+       /* register index */
+       buffer[0] = 0x70;
+       buffer[1] = 0x00;
+       buffer[2] = reg & 0x7f;
+       index_xfer.tx_buf = buffer;
+       spi_message_add_tail(&index_xfer, &msg);
+
+       /* register value */
+       buffer[4] = 0x72;
+       buffer[5] = val >> 8;
+       buffer[6] = val;
+       value_xfer.tx_buf = buffer + 4;
+       spi_message_add_tail(&value_xfer, &msg);
+
+       return spi_sync(spi, &msg);
+}
+
+static void init_lb035q02_panel(struct spi_device *spi)
+{
+       /* Init sequence from page 28 of the lb035q02 spec */
+       lb035q02_write_reg(spi, 0x01, 0x6300);
+       lb035q02_write_reg(spi, 0x02, 0x0200);
+       lb035q02_write_reg(spi, 0x03, 0x0177);
+       lb035q02_write_reg(spi, 0x04, 0x04c7);
+       lb035q02_write_reg(spi, 0x05, 0xffc0);
+       lb035q02_write_reg(spi, 0x06, 0xe806);
+       lb035q02_write_reg(spi, 0x0a, 0x4008);
+       lb035q02_write_reg(spi, 0x0b, 0x0000);
+       lb035q02_write_reg(spi, 0x0d, 0x0030);
+       lb035q02_write_reg(spi, 0x0e, 0x2800);
+       lb035q02_write_reg(spi, 0x0f, 0x0000);
+       lb035q02_write_reg(spi, 0x16, 0x9f80);
+       lb035q02_write_reg(spi, 0x17, 0x0a0f);
+       lb035q02_write_reg(spi, 0x1e, 0x00c1);
+       lb035q02_write_reg(spi, 0x30, 0x0300);
+       lb035q02_write_reg(spi, 0x31, 0x0007);
+       lb035q02_write_reg(spi, 0x32, 0x0000);
+       lb035q02_write_reg(spi, 0x33, 0x0000);
+       lb035q02_write_reg(spi, 0x34, 0x0707);
+       lb035q02_write_reg(spi, 0x35, 0x0004);
+       lb035q02_write_reg(spi, 0x36, 0x0302);
+       lb035q02_write_reg(spi, 0x37, 0x0202);
+       lb035q02_write_reg(spi, 0x3a, 0x0a0d);
+       lb035q02_write_reg(spi, 0x3b, 0x0806);
+}
+
+static int lb035q02_connect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (omapdss_device_is_connected(dssdev))
+               return 0;
+
+       r = in->ops.dpi->connect(in, dssdev);
+       if (r)
+               return r;
+
+       init_lb035q02_panel(ddata->spi);
+
+       return 0;
+}
+
+static void lb035q02_disconnect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return;
+
+       in->ops.dpi->disconnect(in, dssdev);
+}
+
+static int lb035q02_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return -ENODEV;
+
+       if (omapdss_device_is_enabled(dssdev))
+               return 0;
+
+       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+       in->ops.dpi->set_timings(in, &ddata->videomode);
+
+       r = in->ops.dpi->enable(in);
+       if (r)
+               return r;
+
+       if (gpio_is_valid(ddata->enable_gpio))
+               gpio_set_value_cansleep(ddata->enable_gpio, 1);
+
+       if (gpio_is_valid(ddata->backlight_gpio))
+               gpio_set_value_cansleep(ddata->backlight_gpio, 1);
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+}
+
+static void lb035q02_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_enabled(dssdev))
+               return;
+
+       if (gpio_is_valid(ddata->enable_gpio))
+               gpio_set_value_cansleep(ddata->enable_gpio, 0);
+
+       if (gpio_is_valid(ddata->backlight_gpio))
+               gpio_set_value_cansleep(ddata->backlight_gpio, 0);
+
+       in->ops.dpi->disable(in);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void lb035q02_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       ddata->videomode = *timings;
+       dssdev->panel.timings = *timings;
+
+       in->ops.dpi->set_timings(in, timings);
+}
+
+static void lb035q02_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       *timings = ddata->videomode;
+}
+
+static int lb035q02_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.dpi->check_timings(in, timings);
+}
+
+static struct omap_dss_driver lb035q02_ops = {
+       .connect        = lb035q02_connect,
+       .disconnect     = lb035q02_disconnect,
+
+       .enable         = lb035q02_enable,
+       .disable        = lb035q02_disable,
+
+       .set_timings    = lb035q02_set_timings,
+       .get_timings    = lb035q02_get_timings,
+       .check_timings  = lb035q02_check_timings,
+
+       .get_resolution = omapdss_default_get_resolution,
+};
+
+static int lb035q02_probe_pdata(struct spi_device *spi)
+{
+       const struct panel_lb035q02_platform_data *pdata;
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *dssdev, *in;
+
+       pdata = dev_get_platdata(&spi->dev);
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&spi->dev, "failed to find video source '%s'\n",
+                               pdata->source);
+               return -EPROBE_DEFER;
+       }
+
+       ddata->in = in;
+
+       ddata->data_lines = pdata->data_lines;
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       ddata->enable_gpio = pdata->enable_gpio;
+       ddata->backlight_gpio = pdata->backlight_gpio;
+
+       return 0;
+}
+
+static int lb035q02_panel_spi_probe(struct spi_device *spi)
+{
+       struct panel_drv_data *ddata;
+       struct omap_dss_device *dssdev;
+       int r;
+
+       ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
+       if (ddata == NULL)
+               return -ENOMEM;
+
+       dev_set_drvdata(&spi->dev, ddata);
+
+       ddata->spi = spi;
+
+       if (dev_get_platdata(&spi->dev)) {
+               r = lb035q02_probe_pdata(spi);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       if (gpio_is_valid(ddata->enable_gpio)) {
+               r = devm_gpio_request_one(&spi->dev, ddata->enable_gpio,
+                               GPIOF_OUT_INIT_LOW, "panel enable");
+               if (r)
+                       goto err_gpio;
+       }
+
+       if (gpio_is_valid(ddata->backlight_gpio)) {
+               r = devm_gpio_request_one(&spi->dev, ddata->backlight_gpio,
+                               GPIOF_OUT_INIT_LOW, "panel backlight");
+               if (r)
+                       goto err_gpio;
+       }
+
+       ddata->videomode = lb035q02_timings;
+
+       dssdev = &ddata->dssdev;
+       dssdev->dev = &spi->dev;
+       dssdev->driver = &lb035q02_ops;
+       dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+       dssdev->owner = THIS_MODULE;
+       dssdev->panel.timings = ddata->videomode;
+       dssdev->phy.dpi.data_lines = ddata->data_lines;
+
+       r = omapdss_register_display(dssdev);
+       if (r) {
+               dev_err(&spi->dev, "Failed to register panel\n");
+               goto err_reg;
+       }
+
+       return 0;
+
+err_reg:
+err_gpio:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int lb035q02_panel_spi_remove(struct spi_device *spi)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       omapdss_unregister_display(dssdev);
+
+       lb035q02_disable(dssdev);
+       lb035q02_disconnect(dssdev);
+
+       omap_dss_put_device(in);
+
+       return 0;
+}
+
+static struct spi_driver lb035q02_spi_driver = {
+       .probe          = lb035q02_panel_spi_probe,
+       .remove         = lb035q02_panel_spi_remove,
+       .driver         = {
+               .name   = "panel_lgphilips_lb035q02",
+               .owner  = THIS_MODULE,
+       },
+};
+
+module_spi_driver(lb035q02_spi_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c b/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c
new file mode 100644 (file)
index 0000000..bb217da
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * NEC NL8048HL11 Panel driver
+ *
+ * Copyright (C) 2010 Texas Instruments Inc.
+ * Author: Erik Gilling <konkers@android.com>
+ * Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+struct panel_drv_data {
+       struct omap_dss_device  dssdev;
+       struct omap_dss_device *in;
+
+       struct omap_video_timings videomode;
+
+       int data_lines;
+
+       int res_gpio;
+       int qvga_gpio;
+
+       struct spi_device *spi;
+};
+
+#define LCD_XRES               800
+#define LCD_YRES               480
+/*
+ * NEC PIX Clock Ratings
+ * MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz
+ */
+#define LCD_PIXEL_CLOCK                23800
+
+static const struct {
+       unsigned char addr;
+       unsigned char dat;
+} nec_8048_init_seq[] = {
+       { 3, 0x01 }, { 0, 0x00 }, { 1, 0x01 }, { 4, 0x00 }, { 5, 0x14 },
+       { 6, 0x24 }, { 16, 0xD7 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x55 },
+       { 20, 0x01 }, { 21, 0x70 }, { 22, 0x1E }, { 23, 0x25 }, { 24, 0x25 },
+       { 25, 0x02 }, { 26, 0x02 }, { 27, 0xA0 }, { 32, 0x2F }, { 33, 0x0F },
+       { 34, 0x0F }, { 35, 0x0F }, { 36, 0x0F }, { 37, 0x0F }, { 38, 0x0F },
+       { 39, 0x00 }, { 40, 0x02 }, { 41, 0x02 }, { 42, 0x02 }, { 43, 0x0F },
+       { 44, 0x0F }, { 45, 0x0F }, { 46, 0x0F }, { 47, 0x0F }, { 48, 0x0F },
+       { 49, 0x0F }, { 50, 0x00 }, { 51, 0x02 }, { 52, 0x02 }, { 53, 0x02 },
+       { 80, 0x0C }, { 83, 0x42 }, { 84, 0x42 }, { 85, 0x41 }, { 86, 0x14 },
+       { 89, 0x88 }, { 90, 0x01 }, { 91, 0x00 }, { 92, 0x02 }, { 93, 0x0C },
+       { 94, 0x1C }, { 95, 0x27 }, { 98, 0x49 }, { 99, 0x27 }, { 102, 0x76 },
+       { 103, 0x27 }, { 112, 0x01 }, { 113, 0x0E }, { 114, 0x02 },
+       { 115, 0x0C }, { 118, 0x0C }, { 121, 0x30 }, { 130, 0x00 },
+       { 131, 0x00 }, { 132, 0xFC }, { 134, 0x00 }, { 136, 0x00 },
+       { 138, 0x00 }, { 139, 0x00 }, { 140, 0x00 }, { 141, 0xFC },
+       { 143, 0x00 }, { 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 },
+       { 149, 0x00 }, { 150, 0xFC }, { 152, 0x00 }, { 154, 0x00 },
+       { 156, 0x00 }, { 157, 0x00 }, { 2, 0x00 },
+};
+
+static const struct omap_video_timings nec_8048_panel_timings = {
+       .x_res          = LCD_XRES,
+       .y_res          = LCD_YRES,
+       .pixel_clock    = LCD_PIXEL_CLOCK,
+       .hfp            = 6,
+       .hsw            = 1,
+       .hbp            = 4,
+       .vfp            = 3,
+       .vsw            = 1,
+       .vbp            = 4,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+};
+
+#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
+
+static int nec_8048_spi_send(struct spi_device *spi, unsigned char reg_addr,
+                       unsigned char reg_data)
+{
+       int ret = 0;
+       unsigned int cmd = 0, data = 0;
+
+       cmd = 0x0000 | reg_addr; /* register address write */
+       data = 0x0100 | reg_data; /* register data write */
+       data = (cmd << 16) | data;
+
+       ret = spi_write(spi, (unsigned char *)&data, 4);
+       if (ret)
+               pr_err("error in spi_write %x\n", data);
+
+       return ret;
+}
+
+static int init_nec_8048_wvga_lcd(struct spi_device *spi)
+{
+       unsigned int i;
+       /* Initialization Sequence */
+       /* nec_8048_spi_send(spi, REG, VAL) */
+       for (i = 0; i < (ARRAY_SIZE(nec_8048_init_seq) - 1); i++)
+               nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
+                               nec_8048_init_seq[i].dat);
+       udelay(20);
+       nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
+                               nec_8048_init_seq[i].dat);
+       return 0;
+}
+
+static int nec_8048_connect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (omapdss_device_is_connected(dssdev))
+               return 0;
+
+       r = in->ops.dpi->connect(in, dssdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static void nec_8048_disconnect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return;
+
+       in->ops.dpi->disconnect(in, dssdev);
+}
+
+static int nec_8048_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return -ENODEV;
+
+       if (omapdss_device_is_enabled(dssdev))
+               return 0;
+
+       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+       in->ops.dpi->set_timings(in, &ddata->videomode);
+
+       r = in->ops.dpi->enable(in);
+       if (r)
+               return r;
+
+       if (gpio_is_valid(ddata->res_gpio))
+               gpio_set_value_cansleep(ddata->res_gpio, 1);
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+}
+
+static void nec_8048_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_enabled(dssdev))
+               return;
+
+       if (gpio_is_valid(ddata->res_gpio))
+               gpio_set_value_cansleep(ddata->res_gpio, 0);
+
+       in->ops.dpi->disable(in);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void nec_8048_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       ddata->videomode = *timings;
+       dssdev->panel.timings = *timings;
+
+       in->ops.dpi->set_timings(in, timings);
+}
+
+static void nec_8048_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       *timings = ddata->videomode;
+}
+
+static int nec_8048_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.dpi->check_timings(in, timings);
+}
+
+static struct omap_dss_driver nec_8048_ops = {
+       .connect        = nec_8048_connect,
+       .disconnect     = nec_8048_disconnect,
+
+       .enable         = nec_8048_enable,
+       .disable        = nec_8048_disable,
+
+       .set_timings    = nec_8048_set_timings,
+       .get_timings    = nec_8048_get_timings,
+       .check_timings  = nec_8048_check_timings,
+
+       .get_resolution = omapdss_default_get_resolution,
+};
+
+
+static int nec_8048_probe_pdata(struct spi_device *spi)
+{
+       const struct panel_nec_nl8048hl11_platform_data *pdata;
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *dssdev, *in;
+
+       pdata = dev_get_platdata(&spi->dev);
+
+       ddata->qvga_gpio = pdata->qvga_gpio;
+       ddata->res_gpio = pdata->res_gpio;
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&spi->dev, "failed to find video source '%s'\n",
+                               pdata->source);
+               return -EPROBE_DEFER;
+       }
+       ddata->in = in;
+
+       ddata->data_lines = pdata->data_lines;
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       return 0;
+}
+
+static int nec_8048_probe(struct spi_device *spi)
+{
+       struct panel_drv_data *ddata;
+       struct omap_dss_device *dssdev;
+       int r;
+
+       dev_dbg(&spi->dev, "%s\n", __func__);
+
+       spi->mode = SPI_MODE_0;
+       spi->bits_per_word = 32;
+
+       r = spi_setup(spi);
+       if (r < 0) {
+               dev_err(&spi->dev, "spi_setup failed: %d\n", r);
+               return r;
+       }
+
+       init_nec_8048_wvga_lcd(spi);
+
+       ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
+       if (ddata == NULL)
+               return -ENOMEM;
+
+       dev_set_drvdata(&spi->dev, ddata);
+
+       ddata->spi = spi;
+
+       if (dev_get_platdata(&spi->dev)) {
+               r = nec_8048_probe_pdata(spi);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       if (gpio_is_valid(ddata->qvga_gpio)) {
+               r = devm_gpio_request_one(&spi->dev, ddata->qvga_gpio,
+                               GPIOF_OUT_INIT_HIGH, "lcd QVGA");
+               if (r)
+                       goto err_gpio;
+       }
+
+       if (gpio_is_valid(ddata->res_gpio)) {
+               r = devm_gpio_request_one(&spi->dev, ddata->res_gpio,
+                               GPIOF_OUT_INIT_LOW, "lcd RES");
+               if (r)
+                       goto err_gpio;
+       }
+
+       ddata->videomode = nec_8048_panel_timings;
+
+       dssdev = &ddata->dssdev;
+       dssdev->dev = &spi->dev;
+       dssdev->driver = &nec_8048_ops;
+       dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+       dssdev->owner = THIS_MODULE;
+       dssdev->panel.timings = ddata->videomode;
+
+       r = omapdss_register_display(dssdev);
+       if (r) {
+               dev_err(&spi->dev, "Failed to register panel\n");
+               goto err_reg;
+       }
+
+       return 0;
+
+err_reg:
+err_gpio:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int nec_8048_remove(struct spi_device *spi)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       dev_dbg(&ddata->spi->dev, "%s\n", __func__);
+
+       omapdss_unregister_display(dssdev);
+
+       nec_8048_disable(dssdev);
+       nec_8048_disconnect(dssdev);
+
+       omap_dss_put_device(in);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int nec_8048_suspend(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+
+       nec_8048_spi_send(spi, 2, 0x01);
+       mdelay(40);
+
+       return 0;
+}
+
+static int nec_8048_resume(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+
+       /* reinitialize the panel */
+       spi_setup(spi);
+       nec_8048_spi_send(spi, 2, 0x00);
+       init_nec_8048_wvga_lcd(spi);
+
+       return 0;
+}
+static SIMPLE_DEV_PM_OPS(nec_8048_pm_ops, nec_8048_suspend,
+               nec_8048_resume);
+#define NEC_8048_PM_OPS (&nec_8048_pm_ops)
+#else
+#define NEC_8048_PM_OPS NULL
+#endif
+
+static struct spi_driver nec_8048_driver = {
+       .driver = {
+               .name   = "panel-nec-nl8048hl11",
+               .owner  = THIS_MODULE,
+               .pm     = NEC_8048_PM_OPS,
+       },
+       .probe  = nec_8048_probe,
+       .remove = nec_8048_remove,
+};
+
+module_spi_driver(nec_8048_driver);
+
+MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
+MODULE_DESCRIPTION("NEC-NL8048HL11 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c
new file mode 100644 (file)
index 0000000..72a4fb5
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * LCD panel driver for Sharp LS037V7DW01
+ *
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@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/gpio.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+struct panel_drv_data {
+       struct omap_dss_device dssdev;
+       struct omap_dss_device *in;
+
+       int data_lines;
+
+       struct omap_video_timings videomode;
+
+       int resb_gpio;
+       int ini_gpio;
+       int mo_gpio;
+       int lr_gpio;
+       int ud_gpio;
+};
+
+static const struct omap_video_timings sharp_ls_timings = {
+       .x_res = 480,
+       .y_res = 640,
+
+       .pixel_clock    = 19200,
+
+       .hsw            = 2,
+       .hfp            = 1,
+       .hbp            = 28,
+
+       .vsw            = 1,
+       .vfp            = 1,
+       .vbp            = 1,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+};
+
+#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
+
+static int sharp_ls_connect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (omapdss_device_is_connected(dssdev))
+               return 0;
+
+       r = in->ops.dpi->connect(in, dssdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static void sharp_ls_disconnect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return;
+
+       in->ops.dpi->disconnect(in, dssdev);
+}
+
+static int sharp_ls_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return -ENODEV;
+
+       if (omapdss_device_is_enabled(dssdev))
+               return 0;
+
+       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+       in->ops.dpi->set_timings(in, &ddata->videomode);
+
+       r = in->ops.dpi->enable(in);
+       if (r)
+               return r;
+
+       /* wait couple of vsyncs until enabling the LCD */
+       msleep(50);
+
+       if (gpio_is_valid(ddata->resb_gpio))
+               gpio_set_value_cansleep(ddata->resb_gpio, 1);
+
+       if (gpio_is_valid(ddata->ini_gpio))
+               gpio_set_value_cansleep(ddata->ini_gpio, 1);
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+}
+
+static void sharp_ls_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_enabled(dssdev))
+               return;
+
+       if (gpio_is_valid(ddata->ini_gpio))
+               gpio_set_value_cansleep(ddata->ini_gpio, 0);
+
+       if (gpio_is_valid(ddata->resb_gpio))
+               gpio_set_value_cansleep(ddata->resb_gpio, 0);
+
+       /* wait at least 5 vsyncs after disabling the LCD */
+
+       msleep(100);
+
+       in->ops.dpi->disable(in);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void sharp_ls_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       ddata->videomode = *timings;
+       dssdev->panel.timings = *timings;
+
+       in->ops.dpi->set_timings(in, timings);
+}
+
+static void sharp_ls_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       *timings = ddata->videomode;
+}
+
+static int sharp_ls_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.dpi->check_timings(in, timings);
+}
+
+static struct omap_dss_driver sharp_ls_ops = {
+       .connect        = sharp_ls_connect,
+       .disconnect     = sharp_ls_disconnect,
+
+       .enable         = sharp_ls_enable,
+       .disable        = sharp_ls_disable,
+
+       .set_timings    = sharp_ls_set_timings,
+       .get_timings    = sharp_ls_get_timings,
+       .check_timings  = sharp_ls_check_timings,
+
+       .get_resolution = omapdss_default_get_resolution,
+};
+
+static int sharp_ls_probe_pdata(struct platform_device *pdev)
+{
+       const struct panel_sharp_ls037v7dw01_platform_data *pdata;
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *dssdev, *in;
+
+       pdata = dev_get_platdata(&pdev->dev);
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&pdev->dev, "failed to find video source '%s'\n",
+                               pdata->source);
+               return -EPROBE_DEFER;
+       }
+
+       ddata->in = in;
+
+       ddata->data_lines = pdata->data_lines;
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       ddata->resb_gpio = pdata->resb_gpio;
+       ddata->ini_gpio = pdata->ini_gpio;
+       ddata->mo_gpio = pdata->mo_gpio;
+       ddata->lr_gpio = pdata->lr_gpio;
+       ddata->ud_gpio = pdata->ud_gpio;
+
+       return 0;
+}
+
+static int sharp_ls_probe(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata;
+       struct omap_dss_device *dssdev;
+       int r;
+
+       ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+       if (ddata == NULL)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ddata);
+
+       if (dev_get_platdata(&pdev->dev)) {
+               r = sharp_ls_probe_pdata(pdev);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       if (gpio_is_valid(ddata->mo_gpio)) {
+               r = devm_gpio_request_one(&pdev->dev, ddata->mo_gpio,
+                               GPIOF_OUT_INIT_LOW, "lcd MO");
+               if (r)
+                       goto err_gpio;
+       }
+
+       if (gpio_is_valid(ddata->lr_gpio)) {
+               r = devm_gpio_request_one(&pdev->dev, ddata->lr_gpio,
+                               GPIOF_OUT_INIT_HIGH, "lcd LR");
+               if (r)
+                       goto err_gpio;
+       }
+
+       if (gpio_is_valid(ddata->ud_gpio)) {
+               r = devm_gpio_request_one(&pdev->dev, ddata->ud_gpio,
+                               GPIOF_OUT_INIT_HIGH, "lcd UD");
+               if (r)
+                       goto err_gpio;
+       }
+
+       if (gpio_is_valid(ddata->resb_gpio)) {
+               r = devm_gpio_request_one(&pdev->dev, ddata->resb_gpio,
+                               GPIOF_OUT_INIT_LOW, "lcd RESB");
+               if (r)
+                       goto err_gpio;
+       }
+
+       if (gpio_is_valid(ddata->ini_gpio)) {
+               r = devm_gpio_request_one(&pdev->dev, ddata->ini_gpio,
+                               GPIOF_OUT_INIT_LOW, "lcd INI");
+               if (r)
+                       goto err_gpio;
+       }
+
+       ddata->videomode = sharp_ls_timings;
+
+       dssdev = &ddata->dssdev;
+       dssdev->dev = &pdev->dev;
+       dssdev->driver = &sharp_ls_ops;
+       dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+       dssdev->owner = THIS_MODULE;
+       dssdev->panel.timings = ddata->videomode;
+       dssdev->phy.dpi.data_lines = ddata->data_lines;
+
+       r = omapdss_register_display(dssdev);
+       if (r) {
+               dev_err(&pdev->dev, "Failed to register panel\n");
+               goto err_reg;
+       }
+
+       return 0;
+
+err_reg:
+err_gpio:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int __exit sharp_ls_remove(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       omapdss_unregister_display(dssdev);
+
+       sharp_ls_disable(dssdev);
+       sharp_ls_disconnect(dssdev);
+
+       omap_dss_put_device(in);
+
+       return 0;
+}
+
+static struct platform_driver sharp_ls_driver = {
+       .probe = sharp_ls_probe,
+       .remove = __exit_p(sharp_ls_remove),
+       .driver = {
+               .name = "panel-sharp-ls037v7dw01",
+               .owner = THIS_MODULE,
+       },
+};
+
+module_platform_driver(sharp_ls_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays-new/panel-sony-acx565akm.c b/drivers/video/omap2/displays-new/panel-sony-acx565akm.c
new file mode 100644 (file)
index 0000000..e6d56f7
--- /dev/null
@@ -0,0 +1,865 @@
+/*
+ * Sony ACX565AKM LCD Panel driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Original Driver Author: Imre Deak <imre.deak@nokia.com>
+ * Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ * Adapted to new DSS2 framework: Roger Quadros <roger.quadros@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+#define MIPID_CMD_READ_DISP_ID         0x04
+#define MIPID_CMD_READ_RED             0x06
+#define MIPID_CMD_READ_GREEN           0x07
+#define MIPID_CMD_READ_BLUE            0x08
+#define MIPID_CMD_READ_DISP_STATUS     0x09
+#define MIPID_CMD_RDDSDR               0x0F
+#define MIPID_CMD_SLEEP_IN             0x10
+#define MIPID_CMD_SLEEP_OUT            0x11
+#define MIPID_CMD_DISP_OFF             0x28
+#define MIPID_CMD_DISP_ON              0x29
+#define MIPID_CMD_WRITE_DISP_BRIGHTNESS        0x51
+#define MIPID_CMD_READ_DISP_BRIGHTNESS 0x52
+#define MIPID_CMD_WRITE_CTRL_DISP      0x53
+
+#define CTRL_DISP_BRIGHTNESS_CTRL_ON   (1 << 5)
+#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON        (1 << 4)
+#define CTRL_DISP_BACKLIGHT_ON         (1 << 2)
+#define CTRL_DISP_AUTO_BRIGHTNESS_ON   (1 << 1)
+
+#define MIPID_CMD_READ_CTRL_DISP       0x54
+#define MIPID_CMD_WRITE_CABC           0x55
+#define MIPID_CMD_READ_CABC            0x56
+
+#define MIPID_VER_LPH8923              3
+#define MIPID_VER_LS041Y3              4
+#define MIPID_VER_L4F00311             8
+#define MIPID_VER_ACX565AKM            9
+
+struct panel_drv_data {
+       struct omap_dss_device  dssdev;
+       struct omap_dss_device *in;
+
+       int reset_gpio;
+       int datapairs;
+
+       struct omap_video_timings videomode;
+
+       char            *name;
+       int             enabled;
+       int             model;
+       int             revision;
+       u8              display_id[3];
+       unsigned        has_bc:1;
+       unsigned        has_cabc:1;
+       unsigned        cabc_mode;
+       unsigned long   hw_guard_end;           /* next value of jiffies
+                                                  when we can issue the
+                                                  next sleep in/out command */
+       unsigned long   hw_guard_wait;          /* max guard time in jiffies */
+
+       struct spi_device       *spi;
+       struct mutex            mutex;
+
+       struct backlight_device *bl_dev;
+};
+
+static const struct omap_video_timings acx565akm_panel_timings = {
+       .x_res          = 800,
+       .y_res          = 480,
+       .pixel_clock    = 24000,
+       .hfp            = 28,
+       .hsw            = 4,
+       .hbp            = 24,
+       .vfp            = 3,
+       .vsw            = 3,
+       .vbp            = 4,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+};
+
+#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
+
+static void acx565akm_transfer(struct panel_drv_data *ddata, int cmd,
+                             const u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+       struct spi_message      m;
+       struct spi_transfer     *x, xfer[5];
+       int                     r;
+
+       BUG_ON(ddata->spi == NULL);
+
+       spi_message_init(&m);
+
+       memset(xfer, 0, sizeof(xfer));
+       x = &xfer[0];
+
+       cmd &=  0xff;
+       x->tx_buf = &cmd;
+       x->bits_per_word = 9;
+       x->len = 2;
+
+       if (rlen > 1 && wlen == 0) {
+               /*
+                * Between the command and the response data there is a
+                * dummy clock cycle. Add an extra bit after the command
+                * word to account for this.
+                */
+               x->bits_per_word = 10;
+               cmd <<= 1;
+       }
+       spi_message_add_tail(x, &m);
+
+       if (wlen) {
+               x++;
+               x->tx_buf = wbuf;
+               x->len = wlen;
+               x->bits_per_word = 9;
+               spi_message_add_tail(x, &m);
+       }
+
+       if (rlen) {
+               x++;
+               x->rx_buf       = rbuf;
+               x->len          = rlen;
+               spi_message_add_tail(x, &m);
+       }
+
+       r = spi_sync(ddata->spi, &m);
+       if (r < 0)
+               dev_dbg(&ddata->spi->dev, "spi_sync %d\n", r);
+}
+
+static inline void acx565akm_cmd(struct panel_drv_data *ddata, int cmd)
+{
+       acx565akm_transfer(ddata, cmd, NULL, 0, NULL, 0);
+}
+
+static inline void acx565akm_write(struct panel_drv_data *ddata,
+                              int reg, const u8 *buf, int len)
+{
+       acx565akm_transfer(ddata, reg, buf, len, NULL, 0);
+}
+
+static inline void acx565akm_read(struct panel_drv_data *ddata,
+                             int reg, u8 *buf, int len)
+{
+       acx565akm_transfer(ddata, reg, NULL, 0, buf, len);
+}
+
+static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
+{
+       ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
+       ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
+}
+
+static void hw_guard_wait(struct panel_drv_data *ddata)
+{
+       unsigned long wait = ddata->hw_guard_end - jiffies;
+
+       if ((long)wait > 0 && wait <= ddata->hw_guard_wait) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(wait);
+       }
+}
+
+static void set_sleep_mode(struct panel_drv_data *ddata, int on)
+{
+       int cmd;
+
+       if (on)
+               cmd = MIPID_CMD_SLEEP_IN;
+       else
+               cmd = MIPID_CMD_SLEEP_OUT;
+       /*
+        * We have to keep 120msec between sleep in/out commands.
+        * (8.2.15, 8.2.16).
+        */
+       hw_guard_wait(ddata);
+       acx565akm_cmd(ddata, cmd);
+       hw_guard_start(ddata, 120);
+}
+
+static void set_display_state(struct panel_drv_data *ddata, int enabled)
+{
+       int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF;
+
+       acx565akm_cmd(ddata, cmd);
+}
+
+static int panel_enabled(struct panel_drv_data *ddata)
+{
+       u32 disp_status;
+       int enabled;
+
+       acx565akm_read(ddata, MIPID_CMD_READ_DISP_STATUS,
+                       (u8 *)&disp_status, 4);
+       disp_status = __be32_to_cpu(disp_status);
+       enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10));
+       dev_dbg(&ddata->spi->dev,
+               "LCD panel %senabled by bootloader (status 0x%04x)\n",
+               enabled ? "" : "not ", disp_status);
+       return enabled;
+}
+
+static int panel_detect(struct panel_drv_data *ddata)
+{
+       acx565akm_read(ddata, MIPID_CMD_READ_DISP_ID, ddata->display_id, 3);
+       dev_dbg(&ddata->spi->dev, "MIPI display ID: %02x%02x%02x\n",
+               ddata->display_id[0],
+               ddata->display_id[1],
+               ddata->display_id[2]);
+
+       switch (ddata->display_id[0]) {
+       case 0x10:
+               ddata->model = MIPID_VER_ACX565AKM;
+               ddata->name = "acx565akm";
+               ddata->has_bc = 1;
+               ddata->has_cabc = 1;
+               break;
+       case 0x29:
+               ddata->model = MIPID_VER_L4F00311;
+               ddata->name = "l4f00311";
+               break;
+       case 0x45:
+               ddata->model = MIPID_VER_LPH8923;
+               ddata->name = "lph8923";
+               break;
+       case 0x83:
+               ddata->model = MIPID_VER_LS041Y3;
+               ddata->name = "ls041y3";
+               break;
+       default:
+               ddata->name = "unknown";
+               dev_err(&ddata->spi->dev, "invalid display ID\n");
+               return -ENODEV;
+       }
+
+       ddata->revision = ddata->display_id[1];
+
+       dev_info(&ddata->spi->dev, "omapfb: %s rev %02x LCD detected\n",
+                       ddata->name, ddata->revision);
+
+       return 0;
+}
+
+/*----------------------Backlight Control-------------------------*/
+
+static void enable_backlight_ctrl(struct panel_drv_data *ddata, int enable)
+{
+       u16 ctrl;
+
+       acx565akm_read(ddata, MIPID_CMD_READ_CTRL_DISP, (u8 *)&ctrl, 1);
+       if (enable) {
+               ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON |
+                       CTRL_DISP_BACKLIGHT_ON;
+       } else {
+               ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON |
+                         CTRL_DISP_BACKLIGHT_ON);
+       }
+
+       ctrl |= 1 << 8;
+       acx565akm_write(ddata, MIPID_CMD_WRITE_CTRL_DISP, (u8 *)&ctrl, 2);
+}
+
+static void set_cabc_mode(struct panel_drv_data *ddata, unsigned mode)
+{
+       u16 cabc_ctrl;
+
+       ddata->cabc_mode = mode;
+       if (!ddata->enabled)
+               return;
+       cabc_ctrl = 0;
+       acx565akm_read(ddata, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1);
+       cabc_ctrl &= ~3;
+       cabc_ctrl |= (1 << 8) | (mode & 3);
+       acx565akm_write(ddata, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2);
+}
+
+static unsigned get_cabc_mode(struct panel_drv_data *ddata)
+{
+       return ddata->cabc_mode;
+}
+
+static unsigned get_hw_cabc_mode(struct panel_drv_data *ddata)
+{
+       u8 cabc_ctrl;
+
+       acx565akm_read(ddata, MIPID_CMD_READ_CABC, &cabc_ctrl, 1);
+       return cabc_ctrl & 3;
+}
+
+static void acx565akm_set_brightness(struct panel_drv_data *ddata, int level)
+{
+       int bv;
+
+       bv = level | (1 << 8);
+       acx565akm_write(ddata, MIPID_CMD_WRITE_DISP_BRIGHTNESS, (u8 *)&bv, 2);
+
+       if (level)
+               enable_backlight_ctrl(ddata, 1);
+       else
+               enable_backlight_ctrl(ddata, 0);
+}
+
+static int acx565akm_get_actual_brightness(struct panel_drv_data *ddata)
+{
+       u8 bv;
+
+       acx565akm_read(ddata, MIPID_CMD_READ_DISP_BRIGHTNESS, &bv, 1);
+
+       return bv;
+}
+
+
+static int acx565akm_bl_update_status(struct backlight_device *dev)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
+       int r;
+       int level;
+
+       dev_dbg(&ddata->spi->dev, "%s\n", __func__);
+
+       mutex_lock(&ddata->mutex);
+
+       if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+                       dev->props.power == FB_BLANK_UNBLANK)
+               level = dev->props.brightness;
+       else
+               level = 0;
+
+       r = 0;
+       if (ddata->has_bc)
+               acx565akm_set_brightness(ddata, level);
+       else
+               r = -ENODEV;
+
+       mutex_unlock(&ddata->mutex);
+
+       return r;
+}
+
+static int acx565akm_bl_get_intensity(struct backlight_device *dev)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
+
+       dev_dbg(&dev->dev, "%s\n", __func__);
+
+       if (!ddata->has_bc)
+               return -ENODEV;
+
+       if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+                       dev->props.power == FB_BLANK_UNBLANK) {
+               if (ddata->has_bc)
+                       return acx565akm_get_actual_brightness(ddata);
+               else
+                       return dev->props.brightness;
+       }
+
+       return 0;
+}
+
+static const struct backlight_ops acx565akm_bl_ops = {
+       .get_brightness = acx565akm_bl_get_intensity,
+       .update_status  = acx565akm_bl_update_status,
+};
+
+/*--------------------Auto Brightness control via Sysfs---------------------*/
+
+static const char * const cabc_modes[] = {
+       "off",          /* always used when CABC is not supported */
+       "ui",
+       "still-image",
+       "moving-image",
+};
+
+static ssize_t show_cabc_mode(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+       const char *mode_str;
+       int mode;
+       int len;
+
+       if (!ddata->has_cabc)
+               mode = 0;
+       else
+               mode = get_cabc_mode(ddata);
+       mode_str = "unknown";
+       if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
+               mode_str = cabc_modes[mode];
+       len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
+
+       return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
+}
+
+static ssize_t store_cabc_mode(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
+               const char *mode_str = cabc_modes[i];
+               int cmp_len = strlen(mode_str);
+
+               if (count > 0 && buf[count - 1] == '\n')
+                       count--;
+               if (count != cmp_len)
+                       continue;
+
+               if (strncmp(buf, mode_str, cmp_len) == 0)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(cabc_modes))
+               return -EINVAL;
+
+       if (!ddata->has_cabc && i != 0)
+               return -EINVAL;
+
+       mutex_lock(&ddata->mutex);
+       set_cabc_mode(ddata, i);
+       mutex_unlock(&ddata->mutex);
+
+       return count;
+}
+
+static ssize_t show_cabc_available_modes(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+       int len;
+       int i;
+
+       if (!ddata->has_cabc)
+               return snprintf(buf, PAGE_SIZE, "%s\n", cabc_modes[0]);
+
+       for (i = 0, len = 0;
+            len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
+               len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
+                       i ? " " : "", cabc_modes[i],
+                       i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
+
+       return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
+}
+
+static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
+               show_cabc_mode, store_cabc_mode);
+static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
+               show_cabc_available_modes, NULL);
+
+static struct attribute *bldev_attrs[] = {
+       &dev_attr_cabc_mode.attr,
+       &dev_attr_cabc_available_modes.attr,
+       NULL,
+};
+
+static struct attribute_group bldev_attr_group = {
+       .attrs = bldev_attrs,
+};
+
+static int acx565akm_connect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (omapdss_device_is_connected(dssdev))
+               return 0;
+
+       r = in->ops.sdi->connect(in, dssdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static void acx565akm_disconnect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return;
+
+       in->ops.sdi->disconnect(in, dssdev);
+}
+
+static int acx565akm_panel_power_on(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       dev_dbg(&ddata->spi->dev, "%s\n", __func__);
+
+       in->ops.sdi->set_timings(in, &ddata->videomode);
+       in->ops.sdi->set_datapairs(in, ddata->datapairs);
+
+       r = in->ops.sdi->enable(in);
+       if (r) {
+               pr_err("%s sdi enable failed\n", __func__);
+               return r;
+       }
+
+       /*FIXME tweak me */
+       msleep(50);
+
+       if (gpio_is_valid(ddata->reset_gpio))
+               gpio_set_value(ddata->reset_gpio, 1);
+
+       if (ddata->enabled) {
+               dev_dbg(&ddata->spi->dev, "panel already enabled\n");
+               return 0;
+       }
+
+       /*
+        * We have to meet all the following delay requirements:
+        * 1. tRW: reset pulse width 10usec (7.12.1)
+        * 2. tRT: reset cancel time 5msec (7.12.1)
+        * 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst
+        *    case (7.6.2)
+        * 4. 120msec before the sleep out command (7.12.1)
+        */
+       msleep(120);
+
+       set_sleep_mode(ddata, 0);
+       ddata->enabled = 1;
+
+       /* 5msec between sleep out and the next command. (8.2.16) */
+       usleep_range(5000, 10000);
+       set_display_state(ddata, 1);
+       set_cabc_mode(ddata, ddata->cabc_mode);
+
+       mutex_unlock(&ddata->mutex);
+
+       return acx565akm_bl_update_status(ddata->bl_dev);
+}
+
+static void acx565akm_panel_power_off(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       dev_dbg(dssdev->dev, "%s\n", __func__);
+
+       if (!ddata->enabled)
+               return;
+
+       set_display_state(ddata, 0);
+       set_sleep_mode(ddata, 1);
+       ddata->enabled = 0;
+       /*
+        * We have to provide PCLK,HS,VS signals for 2 frames (worst case
+        * ~50msec) after sending the sleep in command and asserting the
+        * reset signal. We probably could assert the reset w/o the delay
+        * but we still delay to avoid possible artifacts. (7.6.1)
+        */
+       msleep(50);
+
+       if (gpio_is_valid(ddata->reset_gpio))
+               gpio_set_value(ddata->reset_gpio, 0);
+
+       /* FIXME need to tweak this delay */
+       msleep(100);
+
+       in->ops.sdi->disable(in);
+}
+
+static int acx565akm_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       int r;
+
+       dev_dbg(dssdev->dev, "%s\n", __func__);
+
+       if (!omapdss_device_is_connected(dssdev))
+               return -ENODEV;
+
+       if (omapdss_device_is_enabled(dssdev))
+               return 0;
+
+       mutex_lock(&ddata->mutex);
+       r = acx565akm_panel_power_on(dssdev);
+       mutex_unlock(&ddata->mutex);
+
+       if (r)
+               return r;
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+}
+
+static void acx565akm_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       dev_dbg(dssdev->dev, "%s\n", __func__);
+
+       if (!omapdss_device_is_enabled(dssdev))
+               return;
+
+       mutex_lock(&ddata->mutex);
+       acx565akm_panel_power_off(dssdev);
+       mutex_unlock(&ddata->mutex);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void acx565akm_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       ddata->videomode = *timings;
+       dssdev->panel.timings = *timings;
+
+       in->ops.sdi->set_timings(in, timings);
+}
+
+static void acx565akm_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       *timings = ddata->videomode;
+}
+
+static int acx565akm_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.sdi->check_timings(in, timings);
+}
+
+static struct omap_dss_driver acx565akm_ops = {
+       .connect        = acx565akm_connect,
+       .disconnect     = acx565akm_disconnect,
+
+       .enable         = acx565akm_enable,
+       .disable        = acx565akm_disable,
+
+       .set_timings    = acx565akm_set_timings,
+       .get_timings    = acx565akm_get_timings,
+       .check_timings  = acx565akm_check_timings,
+
+       .get_resolution = omapdss_default_get_resolution,
+};
+
+static int acx565akm_probe_pdata(struct spi_device *spi)
+{
+       const struct panel_acx565akm_platform_data *pdata;
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *dssdev, *in;
+
+       pdata = dev_get_platdata(&spi->dev);
+
+       ddata->reset_gpio = pdata->reset_gpio;
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&spi->dev, "failed to find video source '%s'\n",
+                               pdata->source);
+               return -EPROBE_DEFER;
+       }
+       ddata->in = in;
+
+       ddata->datapairs = pdata->datapairs;
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       return 0;
+}
+
+static int acx565akm_probe(struct spi_device *spi)
+{
+       struct panel_drv_data *ddata;
+       struct omap_dss_device *dssdev;
+       struct backlight_device *bldev;
+       int max_brightness, brightness;
+       struct backlight_properties props;
+       int r;
+
+       dev_dbg(&spi->dev, "%s\n", __func__);
+
+       spi->mode = SPI_MODE_3;
+
+       ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
+       if (ddata == NULL)
+               return -ENOMEM;
+
+       dev_set_drvdata(&spi->dev, ddata);
+
+       ddata->spi = spi;
+
+       mutex_init(&ddata->mutex);
+
+       if (dev_get_platdata(&spi->dev)) {
+               r = acx565akm_probe_pdata(spi);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       if (gpio_is_valid(ddata->reset_gpio)) {
+               r = devm_gpio_request_one(&spi->dev, ddata->reset_gpio,
+                               GPIOF_OUT_INIT_LOW, "lcd reset");
+               if (r)
+                       goto err_gpio;
+       }
+
+       if (gpio_is_valid(ddata->reset_gpio))
+               gpio_set_value(ddata->reset_gpio, 1);
+
+       /*
+        * After reset we have to wait 5 msec before the first
+        * command can be sent.
+        */
+       usleep_range(5000, 10000);
+
+       ddata->enabled = panel_enabled(ddata);
+
+       r = panel_detect(ddata);
+
+       if (!ddata->enabled && gpio_is_valid(ddata->reset_gpio))
+               gpio_set_value(ddata->reset_gpio, 0);
+
+       if (r) {
+               dev_err(&spi->dev, "%s panel detect error\n", __func__);
+               goto err_detect;
+       }
+
+       memset(&props, 0, sizeof(props));
+       props.fb_blank = FB_BLANK_UNBLANK;
+       props.power = FB_BLANK_UNBLANK;
+       props.type = BACKLIGHT_RAW;
+
+       bldev = backlight_device_register("acx565akm", &ddata->spi->dev,
+                       ddata, &acx565akm_bl_ops, &props);
+       ddata->bl_dev = bldev;
+       if (ddata->has_cabc) {
+               r = sysfs_create_group(&bldev->dev.kobj, &bldev_attr_group);
+               if (r) {
+                       dev_err(&bldev->dev,
+                               "%s failed to create sysfs files\n", __func__);
+                       goto err_sysfs;
+               }
+               ddata->cabc_mode = get_hw_cabc_mode(ddata);
+       }
+
+       max_brightness = 255;
+
+       if (ddata->has_bc)
+               brightness = acx565akm_get_actual_brightness(ddata);
+       else
+               brightness = 0;
+
+       bldev->props.max_brightness = max_brightness;
+       bldev->props.brightness = brightness;
+
+       acx565akm_bl_update_status(bldev);
+
+
+       ddata->videomode = acx565akm_panel_timings;
+
+       dssdev = &ddata->dssdev;
+       dssdev->dev = &spi->dev;
+       dssdev->driver = &acx565akm_ops;
+       dssdev->type = OMAP_DISPLAY_TYPE_SDI;
+       dssdev->owner = THIS_MODULE;
+       dssdev->panel.timings = ddata->videomode;
+
+       r = omapdss_register_display(dssdev);
+       if (r) {
+               dev_err(&spi->dev, "Failed to register panel\n");
+               goto err_reg;
+       }
+
+       return 0;
+
+err_reg:
+       sysfs_remove_group(&bldev->dev.kobj, &bldev_attr_group);
+err_sysfs:
+       backlight_device_unregister(bldev);
+err_detect:
+err_gpio:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int acx565akm_remove(struct spi_device *spi)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       dev_dbg(&ddata->spi->dev, "%s\n", __func__);
+
+       sysfs_remove_group(&ddata->bl_dev->dev.kobj, &bldev_attr_group);
+       backlight_device_unregister(ddata->bl_dev);
+
+       omapdss_unregister_display(dssdev);
+
+       acx565akm_disable(dssdev);
+       acx565akm_disconnect(dssdev);
+
+       omap_dss_put_device(in);
+
+       return 0;
+}
+
+static struct spi_driver acx565akm_driver = {
+       .driver = {
+               .name   = "acx565akm",
+               .owner  = THIS_MODULE,
+       },
+       .probe  = acx565akm_probe,
+       .remove = acx565akm_remove,
+};
+
+module_spi_driver(acx565akm_driver);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("acx565akm LCD Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c
new file mode 100644 (file)
index 0000000..eadc652
--- /dev/null
@@ -0,0 +1,646 @@
+/*
+ * TPO TD043MTEA1 Panel driver
+ *
+ * Author: Gražvydas Ignotas <notasas@gmail.com>
+ * Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+#define TPO_R02_MODE(x)                ((x) & 7)
+#define TPO_R02_MODE_800x480   7
+#define TPO_R02_NCLK_RISING    BIT(3)
+#define TPO_R02_HSYNC_HIGH     BIT(4)
+#define TPO_R02_VSYNC_HIGH     BIT(5)
+
+#define TPO_R03_NSTANDBY       BIT(0)
+#define TPO_R03_EN_CP_CLK      BIT(1)
+#define TPO_R03_EN_VGL_PUMP    BIT(2)
+#define TPO_R03_EN_PWM         BIT(3)
+#define TPO_R03_DRIVING_CAP_100        BIT(4)
+#define TPO_R03_EN_PRE_CHARGE  BIT(6)
+#define TPO_R03_SOFTWARE_CTL   BIT(7)
+
+#define TPO_R04_NFLIP_H                BIT(0)
+#define TPO_R04_NFLIP_V                BIT(1)
+#define TPO_R04_CP_CLK_FREQ_1H BIT(2)
+#define TPO_R04_VGL_FREQ_1H    BIT(4)
+
+#define TPO_R03_VAL_NORMAL (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | \
+                       TPO_R03_EN_VGL_PUMP |  TPO_R03_EN_PWM | \
+                       TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
+                       TPO_R03_SOFTWARE_CTL)
+
+#define TPO_R03_VAL_STANDBY (TPO_R03_DRIVING_CAP_100 | \
+                       TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL)
+
+static const u16 tpo_td043_def_gamma[12] = {
+       105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
+};
+
+struct panel_drv_data {
+       struct omap_dss_device  dssdev;
+       struct omap_dss_device *in;
+
+       struct omap_video_timings videomode;
+
+       int data_lines;
+
+       struct spi_device *spi;
+       struct regulator *vcc_reg;
+       int nreset_gpio;
+       u16 gamma[12];
+       u32 mode;
+       u32 hmirror:1;
+       u32 vmirror:1;
+       u32 powered_on:1;
+       u32 spi_suspended:1;
+       u32 power_on_resume:1;
+};
+
+static const struct omap_video_timings tpo_td043_timings = {
+       .x_res          = 800,
+       .y_res          = 480,
+
+       .pixel_clock    = 36000,
+
+       .hsw            = 1,
+       .hfp            = 68,
+       .hbp            = 214,
+
+       .vsw            = 1,
+       .vfp            = 39,
+       .vbp            = 34,
+
+       .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
+       .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+       .de_level       = OMAPDSS_SIG_ACTIVE_HIGH,
+       .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+};
+
+#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
+
+static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data)
+{
+       struct spi_message      m;
+       struct spi_transfer     xfer;
+       u16                     w;
+       int                     r;
+
+       spi_message_init(&m);
+
+       memset(&xfer, 0, sizeof(xfer));
+
+       w = ((u16)addr << 10) | (1 << 8) | data;
+       xfer.tx_buf = &w;
+       xfer.bits_per_word = 16;
+       xfer.len = 2;
+       spi_message_add_tail(&xfer, &m);
+
+       r = spi_sync(spi, &m);
+       if (r < 0)
+               dev_warn(&spi->dev, "failed to write to LCD reg (%d)\n", r);
+       return r;
+}
+
+static void tpo_td043_write_gamma(struct spi_device *spi, u16 gamma[12])
+{
+       u8 i, val;
+
+       /* gamma bits [9:8] */
+       for (val = i = 0; i < 4; i++)
+               val |= (gamma[i] & 0x300) >> ((i + 1) * 2);
+       tpo_td043_write(spi, 0x11, val);
+
+       for (val = i = 0; i < 4; i++)
+               val |= (gamma[i+4] & 0x300) >> ((i + 1) * 2);
+       tpo_td043_write(spi, 0x12, val);
+
+       for (val = i = 0; i < 4; i++)
+               val |= (gamma[i+8] & 0x300) >> ((i + 1) * 2);
+       tpo_td043_write(spi, 0x13, val);
+
+       /* gamma bits [7:0] */
+       for (val = i = 0; i < 12; i++)
+               tpo_td043_write(spi, 0x14 + i, gamma[i] & 0xff);
+}
+
+static int tpo_td043_write_mirror(struct spi_device *spi, bool h, bool v)
+{
+       u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V |
+               TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H;
+       if (h)
+               reg4 &= ~TPO_R04_NFLIP_H;
+       if (v)
+               reg4 &= ~TPO_R04_NFLIP_V;
+
+       return tpo_td043_write(spi, 4, reg4);
+}
+
+static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
+
+       ddata->hmirror = enable;
+       return tpo_td043_write_mirror(ddata->spi, ddata->hmirror,
+                       ddata->vmirror);
+}
+
+static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
+
+       return ddata->hmirror;
+}
+
+static ssize_t tpo_td043_vmirror_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", ddata->vmirror);
+}
+
+static ssize_t tpo_td043_vmirror_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+       int val;
+       int ret;
+
+       ret = kstrtoint(buf, 0, &val);
+       if (ret < 0)
+               return ret;
+
+       val = !!val;
+
+       ret = tpo_td043_write_mirror(ddata->spi, ddata->hmirror, val);
+       if (ret < 0)
+               return ret;
+
+       ddata->vmirror = val;
+
+       return count;
+}
+
+static ssize_t tpo_td043_mode_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", ddata->mode);
+}
+
+static ssize_t tpo_td043_mode_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+       long val;
+       int ret;
+
+       ret = kstrtol(buf, 0, &val);
+       if (ret != 0 || val & ~7)
+               return -EINVAL;
+
+       ddata->mode = val;
+
+       val |= TPO_R02_NCLK_RISING;
+       tpo_td043_write(ddata->spi, 2, val);
+
+       return count;
+}
+
+static ssize_t tpo_td043_gamma_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+       ssize_t len = 0;
+       int ret;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ddata->gamma); i++) {
+               ret = snprintf(buf + len, PAGE_SIZE - len, "%u ",
+                               ddata->gamma[i]);
+               if (ret < 0)
+                       return ret;
+               len += ret;
+       }
+       buf[len - 1] = '\n';
+
+       return len;
+}
+
+static ssize_t tpo_td043_gamma_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+       unsigned int g[12];
+       int ret;
+       int i;
+
+       ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u",
+                       &g[0], &g[1], &g[2], &g[3], &g[4], &g[5],
+                       &g[6], &g[7], &g[8], &g[9], &g[10], &g[11]);
+
+       if (ret != 12)
+               return -EINVAL;
+
+       for (i = 0; i < 12; i++)
+               ddata->gamma[i] = g[i];
+
+       tpo_td043_write_gamma(ddata->spi, ddata->gamma);
+
+       return count;
+}
+
+static DEVICE_ATTR(vmirror, S_IRUGO | S_IWUSR,
+               tpo_td043_vmirror_show, tpo_td043_vmirror_store);
+static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+               tpo_td043_mode_show, tpo_td043_mode_store);
+static DEVICE_ATTR(gamma, S_IRUGO | S_IWUSR,
+               tpo_td043_gamma_show, tpo_td043_gamma_store);
+
+static struct attribute *tpo_td043_attrs[] = {
+       &dev_attr_vmirror.attr,
+       &dev_attr_mode.attr,
+       &dev_attr_gamma.attr,
+       NULL,
+};
+
+static struct attribute_group tpo_td043_attr_group = {
+       .attrs = tpo_td043_attrs,
+};
+
+static int tpo_td043_power_on(struct panel_drv_data *ddata)
+{
+       int r;
+
+       if (ddata->powered_on)
+               return 0;
+
+       r = regulator_enable(ddata->vcc_reg);
+       if (r != 0)
+               return r;
+
+       /* wait for panel to stabilize */
+       msleep(160);
+
+       if (gpio_is_valid(ddata->nreset_gpio))
+               gpio_set_value(ddata->nreset_gpio, 1);
+
+       tpo_td043_write(ddata->spi, 2,
+                       TPO_R02_MODE(ddata->mode) | TPO_R02_NCLK_RISING);
+       tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_NORMAL);
+       tpo_td043_write(ddata->spi, 0x20, 0xf0);
+       tpo_td043_write(ddata->spi, 0x21, 0xf0);
+       tpo_td043_write_mirror(ddata->spi, ddata->hmirror,
+                       ddata->vmirror);
+       tpo_td043_write_gamma(ddata->spi, ddata->gamma);
+
+       ddata->powered_on = 1;
+       return 0;
+}
+
+static void tpo_td043_power_off(struct panel_drv_data *ddata)
+{
+       if (!ddata->powered_on)
+               return;
+
+       tpo_td043_write(ddata->spi, 3,
+                       TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
+
+       if (gpio_is_valid(ddata->nreset_gpio))
+               gpio_set_value(ddata->nreset_gpio, 0);
+
+       /* wait for at least 2 vsyncs before cutting off power */
+       msleep(50);
+
+       tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_STANDBY);
+
+       regulator_disable(ddata->vcc_reg);
+
+       ddata->powered_on = 0;
+}
+
+static int tpo_td043_connect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (omapdss_device_is_connected(dssdev))
+               return 0;
+
+       r = in->ops.dpi->connect(in, dssdev);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static void tpo_td043_disconnect(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return;
+
+       in->ops.dpi->disconnect(in, dssdev);
+}
+
+static int tpo_td043_enable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+       int r;
+
+       if (!omapdss_device_is_connected(dssdev))
+               return -ENODEV;
+
+       if (omapdss_device_is_enabled(dssdev))
+               return 0;
+
+       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+       in->ops.dpi->set_timings(in, &ddata->videomode);
+
+       r = in->ops.dpi->enable(in);
+       if (r)
+               return r;
+
+       /*
+        * If we are resuming from system suspend, SPI clocks might not be
+        * enabled yet, so we'll program the LCD from SPI PM resume callback.
+        */
+       if (!ddata->spi_suspended) {
+               r = tpo_td043_power_on(ddata);
+               if (r) {
+                       in->ops.dpi->disable(in);
+                       return r;
+               }
+       }
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+}
+
+static void tpo_td043_disable(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (!omapdss_device_is_enabled(dssdev))
+               return;
+
+       in->ops.dpi->disable(in);
+
+       if (!ddata->spi_suspended)
+               tpo_td043_power_off(ddata);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void tpo_td043_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       ddata->videomode = *timings;
+       dssdev->panel.timings = *timings;
+
+       in->ops.dpi->set_timings(in, timings);
+}
+
+static void tpo_td043_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+       *timings = ddata->videomode;
+}
+
+static int tpo_td043_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       return in->ops.dpi->check_timings(in, timings);
+}
+
+static struct omap_dss_driver tpo_td043_ops = {
+       .connect        = tpo_td043_connect,
+       .disconnect     = tpo_td043_disconnect,
+
+       .enable         = tpo_td043_enable,
+       .disable        = tpo_td043_disable,
+
+       .set_timings    = tpo_td043_set_timings,
+       .get_timings    = tpo_td043_get_timings,
+       .check_timings  = tpo_td043_check_timings,
+
+       .set_mirror     = tpo_td043_set_hmirror,
+       .get_mirror     = tpo_td043_get_hmirror,
+
+       .get_resolution = omapdss_default_get_resolution,
+};
+
+
+static int tpo_td043_probe_pdata(struct spi_device *spi)
+{
+       const struct panel_tpo_td043mtea1_platform_data *pdata;
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *dssdev, *in;
+
+       pdata = dev_get_platdata(&spi->dev);
+
+       ddata->nreset_gpio = pdata->nreset_gpio;
+
+       in = omap_dss_find_output(pdata->source);
+       if (in == NULL) {
+               dev_err(&spi->dev, "failed to find video source '%s'\n",
+                               pdata->source);
+               return -EPROBE_DEFER;
+       }
+       ddata->in = in;
+
+       ddata->data_lines = pdata->data_lines;
+
+       dssdev = &ddata->dssdev;
+       dssdev->name = pdata->name;
+
+       return 0;
+}
+
+static int tpo_td043_probe(struct spi_device *spi)
+{
+       struct panel_drv_data *ddata;
+       struct omap_dss_device *dssdev;
+       int r;
+
+       dev_dbg(&spi->dev, "%s\n", __func__);
+
+       spi->bits_per_word = 16;
+       spi->mode = SPI_MODE_0;
+
+       r = spi_setup(spi);
+       if (r < 0) {
+               dev_err(&spi->dev, "spi_setup failed: %d\n", r);
+               return r;
+       }
+
+       ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL);
+       if (ddata == NULL)
+               return -ENOMEM;
+
+       dev_set_drvdata(&spi->dev, ddata);
+
+       ddata->spi = spi;
+
+       if (dev_get_platdata(&spi->dev)) {
+               r = tpo_td043_probe_pdata(spi);
+               if (r)
+                       return r;
+       } else {
+               return -ENODEV;
+       }
+
+       ddata->mode = TPO_R02_MODE_800x480;
+       memcpy(ddata->gamma, tpo_td043_def_gamma, sizeof(ddata->gamma));
+
+       ddata->vcc_reg = devm_regulator_get(&spi->dev, "vcc");
+       if (IS_ERR(ddata->vcc_reg)) {
+               dev_err(&spi->dev, "failed to get LCD VCC regulator\n");
+               r = PTR_ERR(ddata->vcc_reg);
+               goto err_regulator;
+       }
+
+       if (gpio_is_valid(ddata->nreset_gpio)) {
+               r = devm_gpio_request_one(&spi->dev,
+                               ddata->nreset_gpio, GPIOF_OUT_INIT_LOW,
+                               "lcd reset");
+               if (r < 0) {
+                       dev_err(&spi->dev, "couldn't request reset GPIO\n");
+                       goto err_gpio_req;
+               }
+       }
+
+       r = sysfs_create_group(&spi->dev.kobj, &tpo_td043_attr_group);
+       if (r) {
+               dev_err(&spi->dev, "failed to create sysfs files\n");
+               goto err_sysfs;
+       }
+
+       ddata->videomode = tpo_td043_timings;
+
+       dssdev = &ddata->dssdev;
+       dssdev->dev = &spi->dev;
+       dssdev->driver = &tpo_td043_ops;
+       dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+       dssdev->owner = THIS_MODULE;
+       dssdev->panel.timings = ddata->videomode;
+
+       r = omapdss_register_display(dssdev);
+       if (r) {
+               dev_err(&spi->dev, "Failed to register panel\n");
+               goto err_reg;
+       }
+
+       return 0;
+
+err_reg:
+       sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group);
+err_sysfs:
+err_gpio_req:
+err_regulator:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int tpo_td043_remove(struct spi_device *spi)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *dssdev = &ddata->dssdev;
+       struct omap_dss_device *in = ddata->in;
+
+       dev_dbg(&ddata->spi->dev, "%s\n", __func__);
+
+       omapdss_unregister_display(dssdev);
+
+       tpo_td043_disable(dssdev);
+       tpo_td043_disconnect(dssdev);
+
+       omap_dss_put_device(in);
+
+       sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tpo_td043_spi_suspend(struct device *dev)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", ddata);
+
+       ddata->power_on_resume = ddata->powered_on;
+       tpo_td043_power_off(ddata);
+       ddata->spi_suspended = 1;
+
+       return 0;
+}
+
+static int tpo_td043_spi_resume(struct device *dev)
+{
+       struct panel_drv_data *ddata = dev_get_drvdata(dev);
+       int ret;
+
+       dev_dbg(dev, "tpo_td043_spi_resume\n");
+
+       if (ddata->power_on_resume) {
+               ret = tpo_td043_power_on(ddata);
+               if (ret)
+                       return ret;
+       }
+       ddata->spi_suspended = 0;
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm,
+       tpo_td043_spi_suspend, tpo_td043_spi_resume);
+
+static struct spi_driver tpo_td043_spi_driver = {
+       .driver = {
+               .name   = "panel-tpo-td043mtea1",
+               .owner  = THIS_MODULE,
+               .pm     = &tpo_td043_spi_pm,
+       },
+       .probe  = tpo_td043_probe,
+       .remove = tpo_td043_remove,
+};
+
+module_spi_driver(tpo_td043_spi_driver);
+
+MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
+MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver");
+MODULE_LICENSE("GPL");
index c3853c9..e80ac1c 100644 (file)
@@ -1,4 +1,4 @@
-menu "OMAP2/3 Display Device Drivers"
+menu "OMAP2/3 Display Device Drivers (old device model)"
         depends on OMAP2_DSS
 
 config PANEL_GENERIC_DPI
index d7f69c0..3fd100f 100644 (file)
@@ -510,7 +510,7 @@ static int acx_panel_probe(struct omap_dss_device *dssdev)
        int max_brightness, brightness;
        struct backlight_properties props;
 
-       dev_dbg(&dssdev->dev, "%s\n", __func__);
+       dev_dbg(dssdev->dev, "%s\n", __func__);
 
        if (!panel_data)
                return -EINVAL;
@@ -519,7 +519,7 @@ static int acx_panel_probe(struct omap_dss_device *dssdev)
        dssdev->panel.timings = acx_panel_timings;
 
        if (gpio_is_valid(panel_data->reset_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev, panel_data->reset_gpio,
+               r = devm_gpio_request_one(dssdev->dev, panel_data->reset_gpio,
                                GPIOF_OUT_INIT_LOW, "lcd reset");
                if (r)
                        return r;
@@ -538,7 +538,7 @@ static int acx_panel_probe(struct omap_dss_device *dssdev)
 
        r = panel_detect(md);
        if (r) {
-               dev_err(&dssdev->dev, "%s panel detect error\n", __func__);
+               dev_err(dssdev->dev, "%s panel detect error\n", __func__);
                if (!md->enabled && gpio_is_valid(panel_data->reset_gpio))
                        gpio_set_value(panel_data->reset_gpio, 0);
 
@@ -593,7 +593,7 @@ static void acx_panel_remove(struct omap_dss_device *dssdev)
 {
        struct acx565akm_device *md = &acx_dev;
 
-       dev_dbg(&dssdev->dev, "%s\n", __func__);
+       dev_dbg(dssdev->dev, "%s\n", __func__);
        sysfs_remove_group(&md->bl_dev->dev.kobj, &bldev_attr_group);
        backlight_device_unregister(md->bl_dev);
        mutex_lock(&acx_dev.mutex);
@@ -607,7 +607,7 @@ static int acx_panel_power_on(struct omap_dss_device *dssdev)
        struct panel_acx565akm_data *panel_data = get_panel_data(dssdev);
        int r;
 
-       dev_dbg(&dssdev->dev, "%s\n", __func__);
+       dev_dbg(dssdev->dev, "%s\n", __func__);
 
        if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
                return 0;
@@ -667,7 +667,7 @@ static void acx_panel_power_off(struct omap_dss_device *dssdev)
        struct acx565akm_device *md = &acx_dev;
        struct panel_acx565akm_data *panel_data = get_panel_data(dssdev);
 
-       dev_dbg(&dssdev->dev, "%s\n", __func__);
+       dev_dbg(dssdev->dev, "%s\n", __func__);
 
        if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
                return;
@@ -704,7 +704,7 @@ static int acx_panel_enable(struct omap_dss_device *dssdev)
 {
        int r;
 
-       dev_dbg(&dssdev->dev, "%s\n", __func__);
+       dev_dbg(dssdev->dev, "%s\n", __func__);
        r = acx_panel_power_on(dssdev);
 
        if (r)
@@ -716,7 +716,7 @@ static int acx_panel_enable(struct omap_dss_device *dssdev)
 
 static void acx_panel_disable(struct omap_dss_device *dssdev)
 {
-       dev_dbg(&dssdev->dev, "%s\n", __func__);
+       dev_dbg(dssdev->dev, "%s\n", __func__);
        acx_panel_power_off(dssdev);
        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 }
index 97363f7..bebebd4 100644 (file)
@@ -536,7 +536,7 @@ static int generic_dpi_panel_power_on(struct omap_dss_device *dssdev)
 {
        int r, i;
        struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
-       struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev);
        struct panel_config *panel_config = drv_data->panel_config;
 
        if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
@@ -567,7 +567,7 @@ err0:
 static void generic_dpi_panel_power_off(struct omap_dss_device *dssdev)
 {
        struct panel_generic_dpi_data *panel_data = get_panel_data(dssdev);
-       struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev);
        struct panel_config *panel_config = drv_data->panel_config;
        int i;
 
@@ -593,7 +593,7 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
        struct panel_drv_data *drv_data = NULL;
        int i, r;
 
-       dev_dbg(&dssdev->dev, "probe\n");
+       dev_dbg(dssdev->dev, "probe\n");
 
        if (!panel_data || !panel_data->name)
                return -EINVAL;
@@ -609,7 +609,7 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
                return -EINVAL;
 
        for (i = 0; i < panel_data->num_gpios; ++i) {
-               r = devm_gpio_request_one(&dssdev->dev, panel_data->gpios[i],
+               r = devm_gpio_request_one(dssdev->dev, panel_data->gpios[i],
                                panel_data->gpio_invert[i] ?
                                GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
                                "panel gpio");
@@ -619,7 +619,7 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
 
        dssdev->panel.timings = panel_config->timings;
 
-       drv_data = devm_kzalloc(&dssdev->dev, sizeof(*drv_data), GFP_KERNEL);
+       drv_data = devm_kzalloc(dssdev->dev, sizeof(*drv_data), GFP_KERNEL);
        if (!drv_data)
                return -ENOMEM;
 
@@ -628,21 +628,21 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
 
        mutex_init(&drv_data->lock);
 
-       dev_set_drvdata(&dssdev->dev, drv_data);
+       dev_set_drvdata(dssdev->dev, drv_data);
 
        return 0;
 }
 
 static void __exit generic_dpi_panel_remove(struct omap_dss_device *dssdev)
 {
-       dev_dbg(&dssdev->dev, "remove\n");
+       dev_dbg(dssdev->dev, "remove\n");
 
-       dev_set_drvdata(&dssdev->dev, NULL);
+       dev_set_drvdata(dssdev->dev, NULL);
 }
 
 static int generic_dpi_panel_enable(struct omap_dss_device *dssdev)
 {
-       struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev);
        int r;
 
        mutex_lock(&drv_data->lock);
@@ -660,7 +660,7 @@ err:
 
 static void generic_dpi_panel_disable(struct omap_dss_device *dssdev)
 {
-       struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev);
 
        mutex_lock(&drv_data->lock);
 
@@ -674,7 +674,7 @@ static void generic_dpi_panel_disable(struct omap_dss_device *dssdev)
 static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev,
                struct omap_video_timings *timings)
 {
-       struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev);
 
        mutex_lock(&drv_data->lock);
 
@@ -688,7 +688,7 @@ static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev,
 static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev,
                struct omap_video_timings *timings)
 {
-       struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev);
 
        mutex_lock(&drv_data->lock);
 
@@ -700,7 +700,7 @@ static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev,
 static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev,
                struct omap_video_timings *timings)
 {
-       struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *drv_data = dev_get_drvdata(dssdev->dev);
        int r;
 
        mutex_lock(&drv_data->lock);
index 4ea6548..6c51430 100644 (file)
@@ -109,12 +109,12 @@ static int lb035q02_panel_probe(struct omap_dss_device *dssdev)
 
        dssdev->panel.timings = lb035q02_timings;
 
-       ld = devm_kzalloc(&dssdev->dev, sizeof(*ld), GFP_KERNEL);
+       ld = devm_kzalloc(dssdev->dev, sizeof(*ld), GFP_KERNEL);
        if (!ld)
                return -ENOMEM;
 
        for (i = 0; i < panel_data->num_gpios; ++i) {
-               r = devm_gpio_request_one(&dssdev->dev, panel_data->gpios[i],
+               r = devm_gpio_request_one(dssdev->dev, panel_data->gpios[i],
                                panel_data->gpio_invert[i] ?
                                GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
                                "panel gpio");
@@ -123,7 +123,7 @@ static int lb035q02_panel_probe(struct omap_dss_device *dssdev)
        }
 
        mutex_init(&ld->lock);
-       dev_set_drvdata(&dssdev->dev, ld);
+       dev_set_drvdata(dssdev->dev, ld);
 
        return 0;
 }
@@ -134,7 +134,7 @@ static void lb035q02_panel_remove(struct omap_dss_device *dssdev)
 
 static int lb035q02_panel_enable(struct omap_dss_device *dssdev)
 {
-       struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
+       struct lb035q02_data *ld = dev_get_drvdata(dssdev->dev);
        int r;
 
        mutex_lock(&ld->lock);
@@ -153,7 +153,7 @@ err:
 
 static void lb035q02_panel_disable(struct omap_dss_device *dssdev)
 {
-       struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
+       struct lb035q02_data *ld = dev_get_drvdata(dssdev->dev);
 
        mutex_lock(&ld->lock);
 
index 860b180..1d525fc 100644 (file)
@@ -311,16 +311,16 @@ static int n8x0_panel_power_on(struct omap_dss_device *dssdev)
        switch (rev & 0xfc) {
        case 0x9c:
                ddata->blizzard_ver = BLIZZARD_VERSION_S1D13744;
-               dev_info(&dssdev->dev, "s1d13744 LCD controller rev %d "
+               dev_info(dssdev->dev, "s1d13744 LCD controller rev %d "
                        "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
                break;
        case 0xa4:
                ddata->blizzard_ver = BLIZZARD_VERSION_S1D13745;
-               dev_info(&dssdev->dev, "s1d13745 LCD controller rev %d "
+               dev_info(dssdev->dev, "s1d13745 LCD controller rev %d "
                        "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
                break;
        default:
-               dev_err(&dssdev->dev, "invalid s1d1374x revision %02x\n", rev);
+               dev_err(dssdev->dev, "invalid s1d1374x revision %02x\n", rev);
                r = -ENODEV;
                goto err_inv_chip;
        }
@@ -341,13 +341,13 @@ static int n8x0_panel_power_on(struct omap_dss_device *dssdev)
                panel_name = "ls041y3";
                break;
        default:
-               dev_err(&dssdev->dev, "invalid display ID 0x%x\n",
+               dev_err(dssdev->dev, "invalid display ID 0x%x\n",
                                display_id[0]);
                r = -ENODEV;
                goto err_inv_panel;
        }
 
-       dev_info(&dssdev->dev, "%s rev %02x LCD detected\n",
+       dev_info(dssdev->dev, "%s rev %02x LCD detected\n",
                        panel_name, display_id[1]);
 
        send_sleep_out(spi);
@@ -416,7 +416,7 @@ static int n8x0_panel_probe(struct omap_dss_device *dssdev)
        struct panel_drv_data *ddata;
        int r;
 
-       dev_dbg(&dssdev->dev, "probe\n");
+       dev_dbg(dssdev->dev, "probe\n");
 
        if (!bdata)
                return -EINVAL;
@@ -434,14 +434,14 @@ static int n8x0_panel_probe(struct omap_dss_device *dssdev)
        dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
 
        if (gpio_is_valid(bdata->panel_reset)) {
-               r = devm_gpio_request_one(&dssdev->dev, bdata->panel_reset,
+               r = devm_gpio_request_one(dssdev->dev, bdata->panel_reset,
                                GPIOF_OUT_INIT_LOW, "PANEL RESET");
                if (r)
                        return r;
        }
 
        if (gpio_is_valid(bdata->ctrl_pwrdown)) {
-               r = devm_gpio_request_one(&dssdev->dev, bdata->ctrl_pwrdown,
+               r = devm_gpio_request_one(dssdev->dev, bdata->ctrl_pwrdown,
                                GPIOF_OUT_INIT_LOW, "PANEL PWRDOWN");
                if (r)
                        return r;
@@ -452,9 +452,9 @@ static int n8x0_panel_probe(struct omap_dss_device *dssdev)
 
 static void n8x0_panel_remove(struct omap_dss_device *dssdev)
 {
-       dev_dbg(&dssdev->dev, "remove\n");
+       dev_dbg(dssdev->dev, "remove\n");
 
-       dev_set_drvdata(&dssdev->dev, NULL);
+       dev_set_drvdata(dssdev->dev, NULL);
 }
 
 static int n8x0_panel_enable(struct omap_dss_device *dssdev)
@@ -462,7 +462,7 @@ static int n8x0_panel_enable(struct omap_dss_device *dssdev)
        struct panel_drv_data *ddata = get_drv_data(dssdev);
        int r;
 
-       dev_dbg(&dssdev->dev, "enable\n");
+       dev_dbg(dssdev->dev, "enable\n");
 
        mutex_lock(&ddata->lock);
 
@@ -488,7 +488,7 @@ static void n8x0_panel_disable(struct omap_dss_device *dssdev)
 {
        struct panel_drv_data *ddata = get_drv_data(dssdev);
 
-       dev_dbg(&dssdev->dev, "disable\n");
+       dev_dbg(dssdev->dev, "disable\n");
 
        mutex_lock(&ddata->lock);
 
@@ -521,13 +521,13 @@ static int n8x0_panel_update(struct omap_dss_device *dssdev,
        struct panel_drv_data *ddata = get_drv_data(dssdev);
        u16 dw, dh;
 
-       dev_dbg(&dssdev->dev, "update\n");
+       dev_dbg(dssdev->dev, "update\n");
 
        dw = dssdev->panel.timings.x_res;
        dh = dssdev->panel.timings.y_res;
 
        if (x != 0 || y != 0 || w != dw || h != dh) {
-               dev_err(&dssdev->dev, "invalid update region %d, %d, %d, %d\n",
+               dev_err(dssdev->dev, "invalid update region %d, %d, %d, %d\n",
                        x, y, w, h);
                return -EINVAL;
        }
@@ -548,7 +548,7 @@ static int n8x0_panel_sync(struct omap_dss_device *dssdev)
 {
        struct panel_drv_data *ddata = get_drv_data(dssdev);
 
-       dev_dbg(&dssdev->dev, "sync\n");
+       dev_dbg(dssdev->dev, "sync\n");
 
        mutex_lock(&ddata->lock);
        rfbi_bus_lock();
index 20c3cd9..6b9f792 100644 (file)
@@ -98,14 +98,14 @@ static int nec_8048_panel_probe(struct omap_dss_device *dssdev)
        dssdev->panel.timings = nec_8048_panel_timings;
 
        if (gpio_is_valid(pd->qvga_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev, pd->qvga_gpio,
+               r = devm_gpio_request_one(dssdev->dev, pd->qvga_gpio,
                                GPIOF_OUT_INIT_HIGH, "lcd QVGA");
                if (r)
                        return r;
        }
 
        if (gpio_is_valid(pd->res_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev, pd->res_gpio,
+               r = devm_gpio_request_one(dssdev->dev, pd->res_gpio,
                                GPIOF_OUT_INIT_LOW, "lcd RES");
                if (r)
                        return r;
index 62f2db0..153e9be 100644 (file)
@@ -351,7 +351,7 @@ static struct i2c_driver picodlp_i2c_driver = {
 static int picodlp_panel_power_on(struct omap_dss_device *dssdev)
 {
        int r, trial = 100;
-       struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
+       struct picodlp_data *picod = dev_get_drvdata(dssdev->dev);
        struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev);
 
        gpio_set_value(picodlp_pdata->pwrgood_gpio, 0);
@@ -360,7 +360,7 @@ static int picodlp_panel_power_on(struct omap_dss_device *dssdev)
 
        while (!gpio_get_value(picodlp_pdata->emu_done_gpio)) {
                if (!trial--) {
-                       dev_err(&dssdev->dev, "emu_done signal not"
+                       dev_err(dssdev->dev, "emu_done signal not"
                                                " going high\n");
                        return -ETIMEDOUT;
                }
@@ -378,7 +378,7 @@ static int picodlp_panel_power_on(struct omap_dss_device *dssdev)
 
        r = omapdss_dpi_display_enable(dssdev);
        if (r) {
-               dev_err(&dssdev->dev, "failed to enable DPI\n");
+               dev_err(dssdev->dev, "failed to enable DPI\n");
                goto err1;
        }
 
@@ -418,7 +418,7 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev)
        if (!picodlp_pdata)
                return -EINVAL;
 
-       picod = devm_kzalloc(&dssdev->dev, sizeof(*picod), GFP_KERNEL);
+       picod = devm_kzalloc(dssdev->dev, sizeof(*picod), GFP_KERNEL);
        if (!picod)
                return -ENOMEM;
 
@@ -428,23 +428,23 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev)
 
        adapter = i2c_get_adapter(picodlp_adapter_id);
        if (!adapter) {
-               dev_err(&dssdev->dev, "can't get i2c adapter\n");
+               dev_err(dssdev->dev, "can't get i2c adapter\n");
                return -ENODEV;
        }
 
        picodlp_i2c_client = i2c_new_device(adapter, &picodlp_i2c_board_info);
        if (!picodlp_i2c_client) {
-               dev_err(&dssdev->dev, "can't add i2c device::"
+               dev_err(dssdev->dev, "can't add i2c device::"
                                         " picodlp_i2c_client is NULL\n");
                return -ENODEV;
        }
 
        picod->picodlp_i2c_client = picodlp_i2c_client;
 
-       dev_set_drvdata(&dssdev->dev, picod);
+       dev_set_drvdata(dssdev->dev, picod);
 
        if (gpio_is_valid(picodlp_pdata->emu_done_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev,
+               r = devm_gpio_request_one(dssdev->dev,
                                picodlp_pdata->emu_done_gpio,
                                GPIOF_IN, "DLP EMU DONE");
                if (r)
@@ -452,7 +452,7 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev)
        }
 
        if (gpio_is_valid(picodlp_pdata->pwrgood_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev,
+               r = devm_gpio_request_one(dssdev->dev,
                                picodlp_pdata->pwrgood_gpio,
                                GPIOF_OUT_INIT_LOW, "DLP PWRGOOD");
                if (r)
@@ -464,21 +464,19 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev)
 
 static void picodlp_panel_remove(struct omap_dss_device *dssdev)
 {
-       struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
+       struct picodlp_data *picod = dev_get_drvdata(dssdev->dev);
 
        i2c_unregister_device(picod->picodlp_i2c_client);
-       dev_set_drvdata(&dssdev->dev, NULL);
-       dev_dbg(&dssdev->dev, "removing picodlp panel\n");
-
-       kfree(picod);
+       dev_set_drvdata(dssdev->dev, NULL);
+       dev_dbg(dssdev->dev, "removing picodlp panel\n");
 }
 
 static int picodlp_panel_enable(struct omap_dss_device *dssdev)
 {
-       struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
+       struct picodlp_data *picod = dev_get_drvdata(dssdev->dev);
        int r;
 
-       dev_dbg(&dssdev->dev, "enabling picodlp panel\n");
+       dev_dbg(dssdev->dev, "enabling picodlp panel\n");
 
        mutex_lock(&picod->lock);
        if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
@@ -494,7 +492,7 @@ static int picodlp_panel_enable(struct omap_dss_device *dssdev)
 
 static void picodlp_panel_disable(struct omap_dss_device *dssdev)
 {
-       struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
+       struct picodlp_data *picod = dev_get_drvdata(dssdev->dev);
 
        mutex_lock(&picod->lock);
        /* Turn off DLP Power */
@@ -504,7 +502,7 @@ static void picodlp_panel_disable(struct omap_dss_device *dssdev)
        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
        mutex_unlock(&picod->lock);
 
-       dev_dbg(&dssdev->dev, "disabling picodlp panel\n");
+       dev_dbg(dssdev->dev, "disabling picodlp panel\n");
 }
 
 static void picodlp_get_resolution(struct omap_dss_device *dssdev,
index 74cb0eb..78f0a67 100644 (file)
@@ -66,35 +66,35 @@ static int sharp_ls_panel_probe(struct omap_dss_device *dssdev)
        dssdev->panel.timings = sharp_ls_timings;
 
        if (gpio_is_valid(pd->mo_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev, pd->mo_gpio,
+               r = devm_gpio_request_one(dssdev->dev, pd->mo_gpio,
                                GPIOF_OUT_INIT_LOW, "lcd MO");
                if (r)
                        return r;
        }
 
        if (gpio_is_valid(pd->lr_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev, pd->lr_gpio,
+               r = devm_gpio_request_one(dssdev->dev, pd->lr_gpio,
                                GPIOF_OUT_INIT_HIGH, "lcd LR");
                if (r)
                        return r;
        }
 
        if (gpio_is_valid(pd->ud_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev, pd->ud_gpio,
+               r = devm_gpio_request_one(dssdev->dev, pd->ud_gpio,
                                GPIOF_OUT_INIT_HIGH, "lcd UD");
                if (r)
                        return r;
        }
 
        if (gpio_is_valid(pd->resb_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev, pd->resb_gpio,
+               r = devm_gpio_request_one(dssdev->dev, pd->resb_gpio,
                                GPIOF_OUT_INIT_LOW, "lcd RESB");
                if (r)
                        return r;
        }
 
        if (gpio_is_valid(pd->ini_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev, pd->ini_gpio,
+               r = devm_gpio_request_one(dssdev->dev, pd->ini_gpio,
                                GPIOF_OUT_INIT_LOW, "lcd INI");
                if (r)
                        return r;
index c4f78bd..54a07da 100644 (file)
@@ -237,7 +237,7 @@ static int taal_set_update_window(struct taal_data *td,
 
 static void taal_queue_esd_work(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
 
        if (td->esd_interval > 0)
                queue_delayed_work(td->workqueue, &td->esd_work,
@@ -246,14 +246,14 @@ static void taal_queue_esd_work(struct omap_dss_device *dssdev)
 
 static void taal_cancel_esd_work(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
 
        cancel_delayed_work(&td->esd_work);
 }
 
 static void taal_queue_ulps_work(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
 
        if (td->ulps_timeout > 0)
                queue_delayed_work(td->workqueue, &td->ulps_work,
@@ -262,14 +262,14 @@ static void taal_queue_ulps_work(struct omap_dss_device *dssdev)
 
 static void taal_cancel_ulps_work(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
 
        cancel_delayed_work(&td->ulps_work);
 }
 
 static int taal_enter_ulps(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        int r;
 
        if (td->ulps_enabled)
@@ -291,7 +291,7 @@ static int taal_enter_ulps(struct omap_dss_device *dssdev)
        return 0;
 
 err:
-       dev_err(&dssdev->dev, "enter ULPS failed");
+       dev_err(dssdev->dev, "enter ULPS failed");
        taal_panel_reset(dssdev);
 
        td->ulps_enabled = false;
@@ -303,7 +303,7 @@ err:
 
 static int taal_exit_ulps(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        int r;
 
        if (!td->ulps_enabled)
@@ -311,7 +311,7 @@ static int taal_exit_ulps(struct omap_dss_device *dssdev)
 
        r = omapdss_dsi_display_enable(dssdev);
        if (r) {
-               dev_err(&dssdev->dev, "failed to enable DSI\n");
+               dev_err(dssdev->dev, "failed to enable DSI\n");
                goto err1;
        }
 
@@ -319,7 +319,7 @@ static int taal_exit_ulps(struct omap_dss_device *dssdev)
 
        r = _taal_enable_te(dssdev, true);
        if (r) {
-               dev_err(&dssdev->dev, "failed to re-enable TE");
+               dev_err(dssdev->dev, "failed to re-enable TE");
                goto err2;
        }
 
@@ -333,7 +333,7 @@ static int taal_exit_ulps(struct omap_dss_device *dssdev)
        return 0;
 
 err2:
-       dev_err(&dssdev->dev, "failed to exit ULPS");
+       dev_err(dssdev->dev, "failed to exit ULPS");
 
        r = taal_panel_reset(dssdev);
        if (!r) {
@@ -349,7 +349,7 @@ err1:
 
 static int taal_wake_up(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
 
        if (td->ulps_enabled)
                return taal_exit_ulps(dssdev);
@@ -362,7 +362,7 @@ static int taal_wake_up(struct omap_dss_device *dssdev)
 static int taal_bl_update_status(struct backlight_device *dev)
 {
        struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        int r;
        int level;
 
@@ -372,7 +372,7 @@ static int taal_bl_update_status(struct backlight_device *dev)
        else
                level = 0;
 
-       dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
+       dev_dbg(dssdev->dev, "update brightness to %d\n", level);
 
        mutex_lock(&td->lock);
 
@@ -418,7 +418,7 @@ static ssize_t taal_num_errors_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
        struct omap_dss_device *dssdev = to_dss_device(dev);
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        u8 errors = 0;
        int r;
 
@@ -448,7 +448,7 @@ static ssize_t taal_hw_revision_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
        struct omap_dss_device *dssdev = to_dss_device(dev);
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        u8 id1, id2, id3;
        int r;
 
@@ -486,7 +486,7 @@ static ssize_t show_cabc_mode(struct device *dev,
                char *buf)
 {
        struct omap_dss_device *dssdev = to_dss_device(dev);
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        const char *mode_str;
        int mode;
        int len;
@@ -506,7 +506,7 @@ static ssize_t store_cabc_mode(struct device *dev,
                const char *buf, size_t count)
 {
        struct omap_dss_device *dssdev = to_dss_device(dev);
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        int i;
        int r;
 
@@ -568,12 +568,12 @@ static ssize_t taal_store_esd_interval(struct device *dev,
                const char *buf, size_t count)
 {
        struct omap_dss_device *dssdev = to_dss_device(dev);
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
 
        unsigned long t;
        int r;
 
-       r = strict_strtoul(buf, 10, &t);
+       r = kstrtoul(buf, 10, &t);
        if (r)
                return r;
 
@@ -592,7 +592,7 @@ static ssize_t taal_show_esd_interval(struct device *dev,
                char *buf)
 {
        struct omap_dss_device *dssdev = to_dss_device(dev);
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        unsigned t;
 
        mutex_lock(&td->lock);
@@ -607,11 +607,11 @@ static ssize_t taal_store_ulps(struct device *dev,
                const char *buf, size_t count)
 {
        struct omap_dss_device *dssdev = to_dss_device(dev);
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        unsigned long t;
        int r;
 
-       r = strict_strtoul(buf, 10, &t);
+       r = kstrtoul(buf, 10, &t);
        if (r)
                return r;
 
@@ -641,7 +641,7 @@ static ssize_t taal_show_ulps(struct device *dev,
                char *buf)
 {
        struct omap_dss_device *dssdev = to_dss_device(dev);
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        unsigned t;
 
        mutex_lock(&td->lock);
@@ -656,11 +656,11 @@ static ssize_t taal_store_ulps_timeout(struct device *dev,
                const char *buf, size_t count)
 {
        struct omap_dss_device *dssdev = to_dss_device(dev);
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        unsigned long t;
        int r;
 
-       r = strict_strtoul(buf, 10, &t);
+       r = kstrtoul(buf, 10, &t);
        if (r)
                return r;
 
@@ -687,7 +687,7 @@ static ssize_t taal_show_ulps_timeout(struct device *dev,
                char *buf)
 {
        struct omap_dss_device *dssdev = to_dss_device(dev);
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        unsigned t;
 
        mutex_lock(&td->lock);
@@ -727,7 +727,7 @@ static struct attribute_group taal_attr_group = {
 
 static void taal_hw_reset(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
 
        if (!gpio_is_valid(td->reset_gpio))
                return;
@@ -768,13 +768,13 @@ static int taal_probe(struct omap_dss_device *dssdev)
        struct backlight_device *bldev = NULL;
        int r;
 
-       dev_dbg(&dssdev->dev, "probe\n");
+       dev_dbg(dssdev->dev, "probe\n");
 
-       td = devm_kzalloc(&dssdev->dev, sizeof(*td), GFP_KERNEL);
+       td = devm_kzalloc(dssdev->dev, sizeof(*td), GFP_KERNEL);
        if (!td)
                return -ENOMEM;
 
-       dev_set_drvdata(&dssdev->dev, td);
+       dev_set_drvdata(dssdev->dev, td);
        td->dssdev = dssdev;
 
        if (dssdev->data) {
@@ -797,41 +797,41 @@ static int taal_probe(struct omap_dss_device *dssdev)
        atomic_set(&td->do_update, 0);
 
        if (gpio_is_valid(td->reset_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev, td->reset_gpio,
+               r = devm_gpio_request_one(dssdev->dev, td->reset_gpio,
                                GPIOF_OUT_INIT_LOW, "taal rst");
                if (r) {
-                       dev_err(&dssdev->dev, "failed to request reset gpio\n");
+                       dev_err(dssdev->dev, "failed to request reset gpio\n");
                        return r;
                }
        }
 
        if (gpio_is_valid(td->ext_te_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev, td->ext_te_gpio,
+               r = devm_gpio_request_one(dssdev->dev, td->ext_te_gpio,
                                GPIOF_IN, "taal irq");
                if (r) {
-                       dev_err(&dssdev->dev, "GPIO request failed\n");
+                       dev_err(dssdev->dev, "GPIO request failed\n");
                        return r;
                }
 
-               r = devm_request_irq(&dssdev->dev, gpio_to_irq(td->ext_te_gpio),
+               r = devm_request_irq(dssdev->dev, gpio_to_irq(td->ext_te_gpio),
                                taal_te_isr,
                                IRQF_TRIGGER_RISING,
                                "taal vsync", dssdev);
 
                if (r) {
-                       dev_err(&dssdev->dev, "IRQ request failed\n");
+                       dev_err(dssdev->dev, "IRQ request failed\n");
                        return r;
                }
 
                INIT_DEFERRABLE_WORK(&td->te_timeout_work,
                                        taal_te_timeout_work_callback);
 
-               dev_dbg(&dssdev->dev, "Using GPIO TE\n");
+               dev_dbg(dssdev->dev, "Using GPIO TE\n");
        }
 
        td->workqueue = create_singlethread_workqueue("taal_esd");
        if (td->workqueue == NULL) {
-               dev_err(&dssdev->dev, "can't create ESD workqueue\n");
+               dev_err(dssdev->dev, "can't create ESD workqueue\n");
                return -ENOMEM;
        }
        INIT_DEFERRABLE_WORK(&td->esd_work, taal_esd_work);
@@ -844,8 +844,8 @@ static int taal_probe(struct omap_dss_device *dssdev)
                props.max_brightness = 255;
 
                props.type = BACKLIGHT_RAW;
-               bldev = backlight_device_register(dev_name(&dssdev->dev),
-                               &dssdev->dev, dssdev, &taal_bl_ops, &props);
+               bldev = backlight_device_register(dev_name(dssdev->dev),
+                               dssdev->dev, dssdev, &taal_bl_ops, &props);
                if (IS_ERR(bldev)) {
                        r = PTR_ERR(bldev);
                        goto err_bl;
@@ -862,19 +862,19 @@ static int taal_probe(struct omap_dss_device *dssdev)
 
        r = omap_dsi_request_vc(dssdev, &td->channel);
        if (r) {
-               dev_err(&dssdev->dev, "failed to get virtual channel\n");
+               dev_err(dssdev->dev, "failed to get virtual channel\n");
                goto err_req_vc;
        }
 
        r = omap_dsi_set_vc_id(dssdev, td->channel, TCH);
        if (r) {
-               dev_err(&dssdev->dev, "failed to set VC_ID\n");
+               dev_err(dssdev->dev, "failed to set VC_ID\n");
                goto err_vc_id;
        }
 
-       r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
+       r = sysfs_create_group(&dssdev->dev->kobj, &taal_attr_group);
        if (r) {
-               dev_err(&dssdev->dev, "failed to create sysfs files\n");
+               dev_err(dssdev->dev, "failed to create sysfs files\n");
                goto err_vc_id;
        }
 
@@ -892,12 +892,12 @@ err_bl:
 
 static void __exit taal_remove(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        struct backlight_device *bldev;
 
-       dev_dbg(&dssdev->dev, "remove\n");
+       dev_dbg(dssdev->dev, "remove\n");
 
-       sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
+       sysfs_remove_group(&dssdev->dev->kobj, &taal_attr_group);
        omap_dsi_release_vc(dssdev, td->channel);
 
        bldev = td->bldev;
@@ -917,7 +917,7 @@ static void __exit taal_remove(struct omap_dss_device *dssdev)
 
 static int taal_power_on(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        u8 id1, id2, id3;
        int r;
        struct omap_dss_dsi_config dsi_config = {
@@ -932,19 +932,19 @@ static int taal_power_on(struct omap_dss_device *dssdev)
 
        r = omapdss_dsi_configure_pins(dssdev, &td->pin_config);
        if (r) {
-               dev_err(&dssdev->dev, "failed to configure DSI pins\n");
+               dev_err(dssdev->dev, "failed to configure DSI pins\n");
                goto err0;
        };
 
        r = omapdss_dsi_set_config(dssdev, &dsi_config);
        if (r) {
-               dev_err(&dssdev->dev, "failed to configure DSI\n");
+               dev_err(dssdev->dev, "failed to configure DSI\n");
                goto err0;
        }
 
        r = omapdss_dsi_display_enable(dssdev);
        if (r) {
-               dev_err(&dssdev->dev, "failed to enable DSI\n");
+               dev_err(dssdev->dev, "failed to enable DSI\n");
                goto err0;
        }
 
@@ -999,10 +999,10 @@ static int taal_power_on(struct omap_dss_device *dssdev)
        td->enabled = 1;
 
        if (!td->intro_printed) {
-               dev_info(&dssdev->dev, "panel revision %02x.%02x.%02x\n",
+               dev_info(dssdev->dev, "panel revision %02x.%02x.%02x\n",
                        id1, id2, id3);
                if (td->cabc_broken)
-                       dev_info(&dssdev->dev,
+                       dev_info(dssdev->dev,
                                        "old Taal version, CABC disabled\n");
                td->intro_printed = true;
        }
@@ -1011,7 +1011,7 @@ static int taal_power_on(struct omap_dss_device *dssdev)
 
        return 0;
 err:
-       dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
+       dev_err(dssdev->dev, "error while enabling panel, issuing HW reset\n");
 
        taal_hw_reset(dssdev);
 
@@ -1022,7 +1022,7 @@ err0:
 
 static void taal_power_off(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        int r;
 
        dsi_disable_video_output(dssdev, td->channel);
@@ -1032,7 +1032,7 @@ static void taal_power_off(struct omap_dss_device *dssdev)
                r = taal_sleep_in(td);
 
        if (r) {
-               dev_err(&dssdev->dev,
+               dev_err(dssdev->dev,
                                "error disabling panel, issuing HW reset\n");
                taal_hw_reset(dssdev);
        }
@@ -1044,7 +1044,7 @@ static void taal_power_off(struct omap_dss_device *dssdev)
 
 static int taal_panel_reset(struct omap_dss_device *dssdev)
 {
-       dev_err(&dssdev->dev, "performing LCD reset\n");
+       dev_err(dssdev->dev, "performing LCD reset\n");
 
        taal_power_off(dssdev);
        taal_hw_reset(dssdev);
@@ -1053,10 +1053,10 @@ static int taal_panel_reset(struct omap_dss_device *dssdev)
 
 static int taal_enable(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        int r;
 
-       dev_dbg(&dssdev->dev, "enable\n");
+       dev_dbg(dssdev->dev, "enable\n");
 
        mutex_lock(&td->lock);
 
@@ -1082,16 +1082,16 @@ static int taal_enable(struct omap_dss_device *dssdev)
 
        return 0;
 err:
-       dev_dbg(&dssdev->dev, "enable failed\n");
+       dev_dbg(dssdev->dev, "enable failed\n");
        mutex_unlock(&td->lock);
        return r;
 }
 
 static void taal_disable(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
 
-       dev_dbg(&dssdev->dev, "disable\n");
+       dev_dbg(dssdev->dev, "disable\n");
 
        mutex_lock(&td->lock);
 
@@ -1118,14 +1118,14 @@ static void taal_disable(struct omap_dss_device *dssdev)
 static void taal_framedone_cb(int err, void *data)
 {
        struct omap_dss_device *dssdev = data;
-       dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
+       dev_dbg(dssdev->dev, "framedone, err %d\n", err);
        dsi_bus_unlock(dssdev);
 }
 
 static irqreturn_t taal_te_isr(int irq, void *data)
 {
        struct omap_dss_device *dssdev = data;
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        int old;
        int r;
 
@@ -1142,7 +1142,7 @@ static irqreturn_t taal_te_isr(int irq, void *data)
 
        return IRQ_HANDLED;
 err:
-       dev_err(&dssdev->dev, "start update failed\n");
+       dev_err(dssdev->dev, "start update failed\n");
        dsi_bus_unlock(dssdev);
        return IRQ_HANDLED;
 }
@@ -1153,7 +1153,7 @@ static void taal_te_timeout_work_callback(struct work_struct *work)
                                        te_timeout_work.work);
        struct omap_dss_device *dssdev = td->dssdev;
 
-       dev_err(&dssdev->dev, "TE not received for 250ms!\n");
+       dev_err(dssdev->dev, "TE not received for 250ms!\n");
 
        atomic_set(&td->do_update, 0);
        dsi_bus_unlock(dssdev);
@@ -1162,10 +1162,10 @@ static void taal_te_timeout_work_callback(struct work_struct *work)
 static int taal_update(struct omap_dss_device *dssdev,
                                    u16 x, u16 y, u16 w, u16 h)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        int r;
 
-       dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
+       dev_dbg(dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
 
        mutex_lock(&td->lock);
        dsi_bus_lock(dssdev);
@@ -1208,23 +1208,23 @@ err:
 
 static int taal_sync(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
 
-       dev_dbg(&dssdev->dev, "sync\n");
+       dev_dbg(dssdev->dev, "sync\n");
 
        mutex_lock(&td->lock);
        dsi_bus_lock(dssdev);
        dsi_bus_unlock(dssdev);
        mutex_unlock(&td->lock);
 
-       dev_dbg(&dssdev->dev, "sync done\n");
+       dev_dbg(dssdev->dev, "sync done\n");
 
        return 0;
 }
 
 static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        int r;
 
        if (enable)
@@ -1243,7 +1243,7 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable)
 
 static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        int r;
 
        mutex_lock(&td->lock);
@@ -1279,7 +1279,7 @@ err:
 
 static int taal_get_te(struct omap_dss_device *dssdev)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        int r;
 
        mutex_lock(&td->lock);
@@ -1291,7 +1291,7 @@ static int taal_get_te(struct omap_dss_device *dssdev)
 
 static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
 {
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
        u8 id1, id2, id3;
        int r;
 
@@ -1336,7 +1336,7 @@ static int taal_memory_read(struct omap_dss_device *dssdev,
        int first = 1;
        int plen;
        unsigned buf_used = 0;
-       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct taal_data *td = dev_get_drvdata(dssdev->dev);
 
        if (size < w * h * 3)
                return -ENOMEM;
@@ -1380,19 +1380,19 @@ static int taal_memory_read(struct omap_dss_device *dssdev,
                                buf + buf_used, size - buf_used);
 
                if (r < 0) {
-                       dev_err(&dssdev->dev, "read error\n");
+                       dev_err(dssdev->dev, "read error\n");
                        goto err3;
                }
 
                buf_used += r;
 
                if (r < plen) {
-                       dev_err(&dssdev->dev, "short read\n");
+                       dev_err(dssdev->dev, "short read\n");
                        break;
                }
 
                if (signal_pending(current)) {
-                       dev_err(&dssdev->dev, "signal pending, "
+                       dev_err(dssdev->dev, "signal pending, "
                                        "aborting memory read\n");
                        r = -ERESTARTSYS;
                        goto err3;
@@ -1450,26 +1450,26 @@ static void taal_esd_work(struct work_struct *work)
 
        r = taal_wake_up(dssdev);
        if (r) {
-               dev_err(&dssdev->dev, "failed to exit ULPS\n");
+               dev_err(dssdev->dev, "failed to exit ULPS\n");
                goto err;
        }
 
        r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state1);
        if (r) {
-               dev_err(&dssdev->dev, "failed to read Taal status\n");
+               dev_err(dssdev->dev, "failed to read Taal status\n");
                goto err;
        }
 
        /* Run self diagnostics */
        r = taal_sleep_out(td);
        if (r) {
-               dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
+               dev_err(dssdev->dev, "failed to run Taal self-diagnostics\n");
                goto err;
        }
 
        r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state2);
        if (r) {
-               dev_err(&dssdev->dev, "failed to read Taal status\n");
+               dev_err(dssdev->dev, "failed to read Taal status\n");
                goto err;
        }
 
@@ -1477,7 +1477,7 @@ static void taal_esd_work(struct work_struct *work)
         * Bit6 if the test passes.
         */
        if (!((state1 ^ state2) & (1 << 6))) {
-               dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
+               dev_err(dssdev->dev, "LCD self diagnostics failed\n");
                goto err;
        }
        /* Self-diagnostics result is also shown on TE GPIO line. We need
@@ -1495,7 +1495,7 @@ static void taal_esd_work(struct work_struct *work)
        mutex_unlock(&td->lock);
        return;
 err:
-       dev_err(&dssdev->dev, "performing LCD reset\n");
+       dev_err(dssdev->dev, "performing LCD reset\n");
 
        taal_panel_reset(dssdev);
 
index 46039c4..1fdfb15 100644 (file)
@@ -59,7 +59,7 @@ struct panel_drv_data {
 
 static int tfp410_power_on(struct omap_dss_device *dssdev)
 {
-       struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
        int r;
 
        if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
@@ -82,7 +82,7 @@ err0:
 
 static void tfp410_power_off(struct omap_dss_device *dssdev)
 {
-       struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
 
        if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
                return;
@@ -99,7 +99,7 @@ static int tfp410_probe(struct omap_dss_device *dssdev)
        int r;
        int i2c_bus_num;
 
-       ddata = devm_kzalloc(&dssdev->dev, sizeof(*ddata), GFP_KERNEL);
+       ddata = devm_kzalloc(dssdev->dev, sizeof(*ddata), GFP_KERNEL);
        if (!ddata)
                return -ENOMEM;
 
@@ -119,10 +119,10 @@ static int tfp410_probe(struct omap_dss_device *dssdev)
        }
 
        if (gpio_is_valid(ddata->pd_gpio)) {
-               r = devm_gpio_request_one(&dssdev->dev, ddata->pd_gpio,
+               r = devm_gpio_request_one(dssdev->dev, ddata->pd_gpio,
                                GPIOF_OUT_INIT_LOW, "tfp410 pd");
                if (r) {
-                       dev_err(&dssdev->dev, "Failed to request PD GPIO %d\n",
+                       dev_err(dssdev->dev, "Failed to request PD GPIO %d\n",
                                        ddata->pd_gpio);
                        return r;
                }
@@ -133,7 +133,7 @@ static int tfp410_probe(struct omap_dss_device *dssdev)
 
                adapter = i2c_get_adapter(i2c_bus_num);
                if (!adapter) {
-                       dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n",
+                       dev_err(dssdev->dev, "Failed to get I2C adapter, bus %d\n",
                                        i2c_bus_num);
                        return -EPROBE_DEFER;
                }
@@ -141,28 +141,28 @@ static int tfp410_probe(struct omap_dss_device *dssdev)
                ddata->i2c_adapter = adapter;
        }
 
-       dev_set_drvdata(&dssdev->dev, ddata);
+       dev_set_drvdata(dssdev->dev, ddata);
 
        return 0;
 }
 
 static void __exit tfp410_remove(struct omap_dss_device *dssdev)
 {
-       struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
 
        mutex_lock(&ddata->lock);
 
        if (ddata->i2c_adapter)
                i2c_put_adapter(ddata->i2c_adapter);
 
-       dev_set_drvdata(&dssdev->dev, NULL);
+       dev_set_drvdata(dssdev->dev, NULL);
 
        mutex_unlock(&ddata->lock);
 }
 
 static int tfp410_enable(struct omap_dss_device *dssdev)
 {
-       struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
        int r;
 
        mutex_lock(&ddata->lock);
@@ -178,7 +178,7 @@ static int tfp410_enable(struct omap_dss_device *dssdev)
 
 static void tfp410_disable(struct omap_dss_device *dssdev)
 {
-       struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
 
        mutex_lock(&ddata->lock);
 
@@ -192,7 +192,7 @@ static void tfp410_disable(struct omap_dss_device *dssdev)
 static void tfp410_set_timings(struct omap_dss_device *dssdev,
                struct omap_video_timings *timings)
 {
-       struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
 
        mutex_lock(&ddata->lock);
        omapdss_dpi_set_timings(dssdev, timings);
@@ -203,7 +203,7 @@ static void tfp410_set_timings(struct omap_dss_device *dssdev,
 static void tfp410_get_timings(struct omap_dss_device *dssdev,
                struct omap_video_timings *timings)
 {
-       struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
 
        mutex_lock(&ddata->lock);
        *timings = dssdev->panel.timings;
@@ -213,7 +213,7 @@ static void tfp410_get_timings(struct omap_dss_device *dssdev,
 static int tfp410_check_timings(struct omap_dss_device *dssdev,
                struct omap_video_timings *timings)
 {
-       struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
        int r;
 
        mutex_lock(&ddata->lock);
@@ -258,7 +258,7 @@ static int tfp410_ddc_read(struct i2c_adapter *adapter,
 static int tfp410_read_edid(struct omap_dss_device *dssdev,
                u8 *edid, int len)
 {
-       struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
        int r, l, bytes_read;
 
        mutex_lock(&ddata->lock);
@@ -298,7 +298,7 @@ err:
 
 static bool tfp410_detect(struct omap_dss_device *dssdev)
 {
-       struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
+       struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev);
        unsigned char out;
        int r;
 
index abf2bc4..7729b6f 100644 (file)
@@ -126,7 +126,7 @@ static int tpo_td043_write_mirror(struct spi_device *spi, bool h, bool v)
 
 static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable)
 {
-       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
+       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dssdev->dev);
 
        tpo_td043->hmirror = enable;
        return tpo_td043_write_mirror(tpo_td043->spi, tpo_td043->hmirror,
@@ -135,7 +135,7 @@ static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable)
 
 static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev)
 {
-       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
+       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dssdev->dev);
 
        return tpo_td043->hmirror;
 }
@@ -338,7 +338,7 @@ static void tpo_td043_power_off(struct tpo_td043_device *tpo_td043)
 
 static int tpo_td043_enable_dss(struct omap_dss_device *dssdev)
 {
-       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
+       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dssdev->dev);
        int r;
 
        if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
@@ -372,7 +372,7 @@ err0:
 
 static void tpo_td043_disable_dss(struct omap_dss_device *dssdev)
 {
-       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
+       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dssdev->dev);
 
        if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
                return;
@@ -385,14 +385,14 @@ static void tpo_td043_disable_dss(struct omap_dss_device *dssdev)
 
 static int tpo_td043_enable(struct omap_dss_device *dssdev)
 {
-       dev_dbg(&dssdev->dev, "enable\n");
+       dev_dbg(dssdev->dev, "enable\n");
 
        return tpo_td043_enable_dss(dssdev);
 }
 
 static void tpo_td043_disable(struct omap_dss_device *dssdev)
 {
-       dev_dbg(&dssdev->dev, "disable\n");
+       dev_dbg(dssdev->dev, "disable\n");
 
        tpo_td043_disable_dss(dssdev);
 
@@ -405,10 +405,10 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev)
        struct panel_tpo_td043_data *pdata = get_panel_data(dssdev);
        int ret = 0;
 
-       dev_dbg(&dssdev->dev, "probe\n");
+       dev_dbg(dssdev->dev, "probe\n");
 
        if (tpo_td043 == NULL) {
-               dev_err(&dssdev->dev, "missing tpo_td043_device\n");
+               dev_err(dssdev->dev, "missing tpo_td043_device\n");
                return -ENODEV;
        }
 
@@ -423,28 +423,28 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev)
        tpo_td043->mode = TPO_R02_MODE_800x480;
        memcpy(tpo_td043->gamma, tpo_td043_def_gamma, sizeof(tpo_td043->gamma));
 
-       tpo_td043->vcc_reg = regulator_get(&dssdev->dev, "vcc");
+       tpo_td043->vcc_reg = regulator_get(dssdev->dev, "vcc");
        if (IS_ERR(tpo_td043->vcc_reg)) {
-               dev_err(&dssdev->dev, "failed to get LCD VCC regulator\n");
+               dev_err(dssdev->dev, "failed to get LCD VCC regulator\n");
                ret = PTR_ERR(tpo_td043->vcc_reg);
                goto fail_regulator;
        }
 
        if (gpio_is_valid(tpo_td043->nreset_gpio)) {
-               ret = devm_gpio_request_one(&dssdev->dev,
+               ret = devm_gpio_request_one(dssdev->dev,
                                tpo_td043->nreset_gpio, GPIOF_OUT_INIT_LOW,
                                "lcd reset");
                if (ret < 0) {
-                       dev_err(&dssdev->dev, "couldn't request reset GPIO\n");
+                       dev_err(dssdev->dev, "couldn't request reset GPIO\n");
                        goto fail_gpio_req;
                }
        }
 
-       ret = sysfs_create_group(&dssdev->dev.kobj, &tpo_td043_attr_group);
+       ret = sysfs_create_group(&dssdev->dev->kobj, &tpo_td043_attr_group);
        if (ret)
-               dev_warn(&dssdev->dev, "failed to create sysfs files\n");
+               dev_warn(dssdev->dev, "failed to create sysfs files\n");
 
-       dev_set_drvdata(&dssdev->dev, tpo_td043);
+       dev_set_drvdata(dssdev->dev, tpo_td043);
 
        return 0;
 
@@ -457,11 +457,11 @@ fail_regulator:
 
 static void tpo_td043_remove(struct omap_dss_device *dssdev)
 {
-       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
+       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dssdev->dev);
 
-       dev_dbg(&dssdev->dev, "remove\n");
+       dev_dbg(dssdev->dev, "remove\n");
 
-       sysfs_remove_group(&dssdev->dev.kobj, &tpo_td043_attr_group);
+       sysfs_remove_group(&dssdev->dev->kobj, &tpo_td043_attr_group);
        regulator_put(tpo_td043->vcc_reg);
 }
 
index cb0f145..8f70a83 100644 (file)
@@ -1,5 +1,6 @@
 menuconfig OMAP2_DSS
         tristate "OMAP2+ Display Subsystem support"
+       select VIDEOMODE_HELPERS
         help
          OMAP2+ Display Subsystem support.
 
index a4b356a..d6212d6 100644 (file)
@@ -420,16 +420,26 @@ static void wait_pending_extra_info_updates(void)
                DSSWARN("timeout in wait_pending_extra_info_updates\n");
 }
 
-static inline struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl)
+static struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr)
 {
-       return ovl->manager ?
-               (ovl->manager->output ? ovl->manager->output->device : NULL) :
-               NULL;
+       struct omap_dss_device *dssdev;
+
+       dssdev = mgr->output;
+       if (dssdev == NULL)
+               return NULL;
+
+       while (dssdev->device)
+               dssdev = dssdev->device;
+
+       if (dssdev->driver)
+               return dssdev;
+       else
+               return NULL;
 }
 
-static inline struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr)
+static struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl)
 {
-       return mgr->output ? mgr->output->device : NULL;
+       return ovl->manager ? dss_mgr_get_device(ovl->manager) : NULL;
 }
 
 static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
@@ -792,6 +802,18 @@ static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr)
        }
 }
 
+static int dss_mgr_connect_compat(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dst)
+{
+       return mgr->set_output(mgr, dst);
+}
+
+static void dss_mgr_disconnect_compat(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dst)
+{
+       mgr->unset_output(mgr);
+}
+
 static void dss_mgr_start_update_compat(struct omap_overlay_manager *mgr)
 {
        struct mgr_priv_data *mp = get_mgr_priv(mgr);
@@ -1156,7 +1178,7 @@ static void dss_mgr_get_info(struct omap_overlay_manager *mgr,
 }
 
 static int dss_mgr_set_output(struct omap_overlay_manager *mgr,
-               struct omap_dss_output *output)
+               struct omap_dss_device *output)
 {
        int r;
 
@@ -1554,6 +1576,8 @@ static void dss_mgr_unregister_framedone_handler_compat(struct omap_overlay_mana
 }
 
 static const struct dss_mgr_ops apply_mgr_ops = {
+       .connect = dss_mgr_connect_compat,
+       .disconnect = dss_mgr_disconnect_compat,
        .start_update = dss_mgr_start_update_compat,
        .enable = dss_mgr_enable_compat,
        .disable = dss_mgr_disable_compat,
@@ -1569,7 +1593,6 @@ static DEFINE_MUTEX(compat_init_lock);
 int omapdss_compat_init(void)
 {
        struct platform_device *pdev = dss_get_core_pdev();
-       struct omap_dss_device *dssdev = NULL;
        int i, r;
 
        mutex_lock(&compat_init_lock);
@@ -1579,7 +1602,7 @@ int omapdss_compat_init(void)
 
        apply_init_priv();
 
-       dss_init_overlay_managers(pdev);
+       dss_init_overlay_managers_sysfs(pdev);
        dss_init_overlays(pdev);
 
        for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
@@ -1615,12 +1638,9 @@ int omapdss_compat_init(void)
        if (r)
                goto err_mgr_ops;
 
-       for_each_dss_dev(dssdev) {
-               r = display_init_sysfs(pdev, dssdev);
-               /* XXX uninit sysfs files on error */
-               if (r)
-                       goto err_disp_sysfs;
-       }
+       r = display_init_sysfs(pdev);
+       if (r)
+               goto err_disp_sysfs;
 
        dispc_runtime_get();
 
@@ -1637,12 +1657,13 @@ out:
 
 err_init_irq:
        dispc_runtime_put();
+       display_uninit_sysfs(pdev);
 
 err_disp_sysfs:
        dss_uninstall_mgr_ops();
 
 err_mgr_ops:
-       dss_uninit_overlay_managers(pdev);
+       dss_uninit_overlay_managers_sysfs(pdev);
        dss_uninit_overlays(pdev);
 
        compat_refcnt--;
@@ -1656,7 +1677,6 @@ EXPORT_SYMBOL(omapdss_compat_init);
 void omapdss_compat_uninit(void)
 {
        struct platform_device *pdev = dss_get_core_pdev();
-       struct omap_dss_device *dssdev = NULL;
 
        mutex_lock(&compat_init_lock);
 
@@ -1665,12 +1685,11 @@ void omapdss_compat_uninit(void)
 
        dss_dispc_uninitialize_irq();
 
-       for_each_dss_dev(dssdev)
-               display_uninit_sysfs(pdev, dssdev);
+       display_uninit_sysfs(pdev);
 
        dss_uninstall_mgr_ops();
 
-       dss_uninit_overlay_managers(pdev);
+       dss_uninit_overlay_managers_sysfs(pdev);
        dss_uninit_overlays(pdev);
 out:
        mutex_unlock(&compat_init_lock);
index c9c2252..1aeb274 100644 (file)
@@ -88,7 +88,7 @@ struct regulator *dss_get_vdds_dsi(void)
        if (core.vdds_dsi_reg != NULL)
                return core.vdds_dsi_reg;
 
-       reg = regulator_get(&core.pdev->dev, "vdds_dsi");
+       reg = devm_regulator_get(&core.pdev->dev, "vdds_dsi");
        if (!IS_ERR(reg))
                core.vdds_dsi_reg = reg;
 
@@ -102,7 +102,7 @@ struct regulator *dss_get_vdds_sdi(void)
        if (core.vdds_sdi_reg != NULL)
                return core.vdds_sdi_reg;
 
-       reg = regulator_get(&core.pdev->dev, "vdds_sdi");
+       reg = devm_regulator_get(&core.pdev->dev, "vdds_sdi");
        if (!IS_ERR(reg))
                core.vdds_sdi_reg = reg;
 
@@ -243,6 +243,8 @@ static int __init omap_dss_probe(struct platform_device *pdev)
 
        if (def_disp_name)
                core.default_display_name = def_disp_name;
+       else if (pdata->default_display_name)
+               core.default_display_name = pdata->default_display_name;
        else if (pdata->default_device)
                core.default_display_name = pdata->default_device->name;
 
@@ -290,37 +292,9 @@ static int dss_bus_match(struct device *dev, struct device_driver *driver)
        return strcmp(dssdev->driver_name, driver->name) == 0;
 }
 
-static ssize_t device_name_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct omap_dss_device *dssdev = to_dss_device(dev);
-       return snprintf(buf, PAGE_SIZE, "%s\n",
-                       dssdev->name ?
-                       dssdev->name : "");
-}
-
-static struct device_attribute default_dev_attrs[] = {
-       __ATTR(name, S_IRUGO, device_name_show, NULL),
-       __ATTR_NULL,
-};
-
-static ssize_t driver_name_show(struct device_driver *drv, char *buf)
-{
-       struct omap_dss_driver *dssdrv = to_dss_driver(drv);
-       return snprintf(buf, PAGE_SIZE, "%s\n",
-                       dssdrv->driver.name ?
-                       dssdrv->driver.name : "");
-}
-static struct driver_attribute default_drv_attrs[] = {
-       __ATTR(name, S_IRUGO, driver_name_show, NULL),
-       __ATTR_NULL,
-};
-
 static struct bus_type dss_bus_type = {
        .name = "omapdss",
        .match = dss_bus_match,
-       .dev_attrs = default_dev_attrs,
-       .drv_attrs = default_drv_attrs,
 };
 
 static void dss_bus_release(struct device *dev)
@@ -377,6 +351,46 @@ static int dss_driver_remove(struct device *dev)
        return 0;
 }
 
+static int omapdss_default_connect(struct omap_dss_device *dssdev)
+{
+       struct omap_dss_device *out;
+       struct omap_overlay_manager *mgr;
+       int r;
+
+       out = dssdev->output;
+
+       if (out == NULL)
+               return -ENODEV;
+
+       mgr = omap_dss_get_overlay_manager(out->dispc_channel);
+       if (!mgr)
+               return -ENODEV;
+
+       r = dss_mgr_connect(mgr, out);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static void omapdss_default_disconnect(struct omap_dss_device *dssdev)
+{
+       struct omap_dss_device *out;
+       struct omap_overlay_manager *mgr;
+
+       out = dssdev->output;
+
+       if (out == NULL)
+               return;
+
+       mgr = out->manager;
+
+       if (mgr == NULL)
+               return;
+
+       dss_mgr_disconnect(mgr, out);
+}
+
 int omap_dss_register_driver(struct omap_dss_driver *dssdriver)
 {
        dssdriver->driver.bus = &dss_bus_type;
@@ -390,6 +404,10 @@ int omap_dss_register_driver(struct omap_dss_driver *dssdriver)
                        omapdss_default_get_recommended_bpp;
        if (dssdriver->get_timings == NULL)
                dssdriver->get_timings = omapdss_default_get_timings;
+       if (dssdriver->connect == NULL)
+               dssdriver->connect = omapdss_default_connect;
+       if (dssdriver->disconnect == NULL)
+               dssdriver->disconnect = omapdss_default_disconnect;
 
        return driver_register(&dssdriver->driver);
 }
@@ -419,29 +437,33 @@ struct omap_dss_device *dss_alloc_and_init_device(struct device *parent)
        if (!dssdev)
                return NULL;
 
-       dssdev->dev.bus = &dss_bus_type;
-       dssdev->dev.parent = parent;
-       dssdev->dev.release = omap_dss_dev_release;
-       dev_set_name(&dssdev->dev, "display%d", disp_num_counter++);
+       dssdev->old_dev.bus = &dss_bus_type;
+       dssdev->old_dev.parent = parent;
+       dssdev->old_dev.release = omap_dss_dev_release;
+       dev_set_name(&dssdev->old_dev, "display%d", disp_num_counter++);
 
-       device_initialize(&dssdev->dev);
+       device_initialize(&dssdev->old_dev);
 
        return dssdev;
 }
 
 int dss_add_device(struct omap_dss_device *dssdev)
 {
-       return device_add(&dssdev->dev);
+       dssdev->dev = &dssdev->old_dev;
+
+       omapdss_register_display(dssdev);
+       return device_add(&dssdev->old_dev);
 }
 
 void dss_put_device(struct omap_dss_device *dssdev)
 {
-       put_device(&dssdev->dev);
+       put_device(&dssdev->old_dev);
 }
 
 void dss_unregister_device(struct omap_dss_device *dssdev)
 {
-       device_unregister(&dssdev->dev);
+       device_unregister(&dssdev->old_dev);
+       omapdss_unregister_display(dssdev);
 }
 
 static int dss_unregister_dss_dev(struct device *dev, void *data)
@@ -618,16 +640,6 @@ static int __init omap_dss_init(void)
 
 static void __exit omap_dss_exit(void)
 {
-       if (core.vdds_dsi_reg != NULL) {
-               regulator_put(core.vdds_dsi_reg);
-               core.vdds_dsi_reg = NULL;
-       }
-
-       if (core.vdds_sdi_reg != NULL) {
-               regulator_put(core.vdds_sdi_reg);
-               core.vdds_sdi_reg = NULL;
-       }
-
        omap_dss_unregister_drivers();
 
        omap_dss_bus_unregister();
index 928884c..83779c2 100644 (file)
@@ -360,8 +360,7 @@ static void dispc_error_worker(struct work_struct *work)
                if (bit & errors) {
                        DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n",
                                        ovl->name);
-                       dispc_ovl_enable(ovl->id, false);
-                       dispc_mgr_go(ovl->manager->id);
+                       ovl->disable(ovl);
                        msleep(50);
                }
        }
index b33b016..02a7340 100644 (file)
@@ -103,6 +103,7 @@ static struct {
        int irq;
 
        unsigned long core_clk_rate;
+       unsigned long tv_pclk_rate;
 
        u32 fifo_size[DISPC_MAX_NR_FIFOS];
        /* maps which plane is using a fifo. fifo-id -> plane-id */
@@ -3071,22 +3072,15 @@ unsigned long dispc_mgr_pclk_rate(enum omap_channel channel)
 
                return r / pcd;
        } else {
-               enum dss_hdmi_venc_clk_source_select source;
-
-               source = dss_get_hdmi_venc_clk_source();
-
-               switch (source) {
-               case DSS_VENC_TV_CLK:
-                       return venc_get_pixel_clock();
-               case DSS_HDMI_M_PCLK:
-                       return hdmi_get_pixel_clock();
-               default:
-                       BUG();
-                       return 0;
-               }
+               return dispc.tv_pclk_rate;
        }
 }
 
+void dispc_set_tv_pclk(unsigned long pclk)
+{
+       dispc.tv_pclk_rate = pclk;
+}
+
 unsigned long dispc_core_clk_rate(void)
 {
        return dispc.core_clk_rate;
@@ -3710,6 +3704,8 @@ static int __init omap_dispchw_probe(struct platform_device *pdev)
 
        dispc_runtime_put();
 
+       dss_init_overlay_managers();
+
        dss_debugfs_create_file("dispc", dispc_dump_regs);
 
        return 0;
@@ -3723,6 +3719,8 @@ static int __exit omap_dispchw_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
 
+       dss_uninit_overlay_managers();
+
        return 0;
 }
 
index 18211a9..21d7f77 100644 (file)
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/jiffies.h>
 #include <linux/platform_device.h>
+#include <linux/sysfs.h>
 
 #include <video/omapdss.h>
 #include "dss.h"
-#include "dss_features.h"
+
+static struct omap_dss_device *to_dss_device_sysfs(struct device *dev)
+{
+       struct omap_dss_device *dssdev = NULL;
+
+       for_each_dss_dev(dssdev) {
+               if (dssdev->dev == dev) {
+                       omap_dss_put_device(dssdev);
+                       return dssdev;
+               }
+       }
+
+       return NULL;
+}
+
+static ssize_t display_name_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%s\n",
+                       dssdev->name ?
+                       dssdev->name : "");
+}
 
 static ssize_t display_enabled_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
-       bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED;
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", enabled);
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       omapdss_device_is_enabled(dssdev));
 }
 
 static ssize_t display_enabled_store(struct device *dev,
                struct device_attribute *attr,
                const char *buf, size_t size)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
        int r;
-       bool enabled;
+       bool enable;
 
-       r = strtobool(buf, &enabled);
+       r = strtobool(buf, &enable);
        if (r)
                return r;
 
-       if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) {
-               if (enabled) {
-                       r = dssdev->driver->enable(dssdev);
-                       if (r)
-                               return r;
-               } else {
-                       dssdev->driver->disable(dssdev);
-               }
+       if (enable == omapdss_device_is_enabled(dssdev))
+               return size;
+
+       if (omapdss_device_is_connected(dssdev) == false)
+               return -ENODEV;
+
+       if (enable) {
+               r = dssdev->driver->enable(dssdev);
+               if (r)
+                       return r;
+       } else {
+               dssdev->driver->disable(dssdev);
        }
 
        return size;
@@ -66,7 +93,7 @@ static ssize_t display_enabled_store(struct device *dev,
 static ssize_t display_tear_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
        return snprintf(buf, PAGE_SIZE, "%d\n",
                        dssdev->driver->get_te ?
                        dssdev->driver->get_te(dssdev) : 0);
@@ -75,7 +102,7 @@ static ssize_t display_tear_show(struct device *dev,
 static ssize_t display_tear_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
        int r;
        bool te;
 
@@ -96,7 +123,7 @@ static ssize_t display_tear_store(struct device *dev,
 static ssize_t display_timings_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
        struct omap_video_timings t;
 
        if (!dssdev->driver->get_timings)
@@ -113,7 +140,7 @@ static ssize_t display_timings_show(struct device *dev,
 static ssize_t display_timings_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
        struct omap_video_timings t = dssdev->panel.timings;
        int r, found;
 
@@ -152,7 +179,7 @@ static ssize_t display_timings_store(struct device *dev,
 static ssize_t display_rotate_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
        int rotate;
        if (!dssdev->driver->get_rotate)
                return -ENOENT;
@@ -163,7 +190,7 @@ static ssize_t display_rotate_show(struct device *dev,
 static ssize_t display_rotate_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
        int rot, r;
 
        if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
@@ -183,7 +210,7 @@ static ssize_t display_rotate_store(struct device *dev,
 static ssize_t display_mirror_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
        int mirror;
        if (!dssdev->driver->get_mirror)
                return -ENOENT;
@@ -194,7 +221,7 @@ static ssize_t display_mirror_show(struct device *dev,
 static ssize_t display_mirror_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
        int r;
        bool mirror;
 
@@ -215,7 +242,7 @@ static ssize_t display_mirror_store(struct device *dev,
 static ssize_t display_wss_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
        unsigned int wss;
 
        if (!dssdev->driver->get_wss)
@@ -229,7 +256,7 @@ static ssize_t display_wss_show(struct device *dev,
 static ssize_t display_wss_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
        u32 wss;
        int r;
 
@@ -250,6 +277,7 @@ static ssize_t display_wss_store(struct device *dev,
        return size;
 }
 
+static DEVICE_ATTR(name, S_IRUGO, display_name_show, NULL);
 static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR,
                display_enabled_show, display_enabled_store);
 static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR,
@@ -263,59 +291,55 @@ static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR,
 static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR,
                display_wss_show, display_wss_store);
 
-static struct device_attribute *display_sysfs_attrs[] = {
-       &dev_attr_enabled,
-       &dev_attr_tear_elim,
-       &dev_attr_timings,
-       &dev_attr_rotate,
-       &dev_attr_mirror,
-       &dev_attr_wss,
+static const struct attribute *display_sysfs_attrs[] = {
+       &dev_attr_name.attr,
+       &dev_attr_enabled.attr,
+       &dev_attr_tear_elim.attr,
+       &dev_attr_timings.attr,
+       &dev_attr_rotate.attr,
+       &dev_attr_mirror.attr,
+       &dev_attr_wss.attr,
        NULL
 };
 
-int display_init_sysfs(struct platform_device *pdev,
-               struct omap_dss_device *dssdev)
+int display_init_sysfs(struct platform_device *pdev)
 {
-       struct device_attribute *attr;
-       int i, r;
+       struct omap_dss_device *dssdev = NULL;
+       int r;
 
-       /* create device sysfs files */
-       i = 0;
-       while ((attr = display_sysfs_attrs[i++]) != NULL) {
-               r = device_create_file(&dssdev->dev, attr);
-               if (r) {
-                       for (i = i - 2; i >= 0; i--) {
-                               attr = display_sysfs_attrs[i];
-                               device_remove_file(&dssdev->dev, attr);
-                       }
+       for_each_dss_dev(dssdev) {
+               struct kobject *kobj = &dssdev->dev->kobj;
 
-                       DSSERR("failed to create sysfs file\n");
-                       return r;
+               r = sysfs_create_files(kobj, display_sysfs_attrs);
+               if (r) {
+                       DSSERR("failed to create sysfs files\n");
+                       goto err;
                }
-       }
 
-       /* create display? sysfs links */
-       r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj,
-                       dev_name(&dssdev->dev));
-       if (r) {
-               while ((attr = display_sysfs_attrs[i++]) != NULL)
-                       device_remove_file(&dssdev->dev, attr);
+               r = sysfs_create_link(&pdev->dev.kobj, kobj, dssdev->alias);
+               if (r) {
+                       sysfs_remove_files(kobj, display_sysfs_attrs);
 
-               DSSERR("failed to create sysfs display link\n");
-               return r;
+                       DSSERR("failed to create sysfs display link\n");
+                       goto err;
+               }
        }
 
        return 0;
+
+err:
+       display_uninit_sysfs(pdev);
+
+       return r;
 }
 
-void display_uninit_sysfs(struct platform_device *pdev,
-               struct omap_dss_device *dssdev)
+void display_uninit_sysfs(struct platform_device *pdev)
 {
-       struct device_attribute *attr;
-       int i = 0;
-
-       sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev));
+       struct omap_dss_device *dssdev = NULL;
 
-       while ((attr = display_sysfs_attrs[i++]) != NULL)
-               device_remove_file(&dssdev->dev, attr);
+       for_each_dss_dev(dssdev) {
+               sysfs_remove_link(&pdev->dev.kobj, dssdev->alias);
+               sysfs_remove_files(&dssdev->dev->kobj,
+                               display_sysfs_attrs);
+       }
 }
index 0aa8ad8..fafe7c9 100644 (file)
@@ -61,6 +61,7 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
        case OMAP_DISPLAY_TYPE_VENC:
        case OMAP_DISPLAY_TYPE_SDI:
        case OMAP_DISPLAY_TYPE_HDMI:
+       case OMAP_DISPLAY_TYPE_DVI:
                return 24;
        default:
                BUG();
@@ -76,110 +77,154 @@ void omapdss_default_get_timings(struct omap_dss_device *dssdev,
 }
 EXPORT_SYMBOL(omapdss_default_get_timings);
 
-static int dss_suspend_device(struct device *dev, void *data)
+int dss_suspend_all_devices(void)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
-
-       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
-               dssdev->activate_after_resume = false;
-               return 0;
-       }
-
-       dssdev->driver->disable(dssdev);
-
-       dssdev->activate_after_resume = true;
+       struct omap_dss_device *dssdev = NULL;
 
-       return 0;
-}
+       for_each_dss_dev(dssdev) {
+               if (!dssdev->driver)
+                       continue;
 
-int dss_suspend_all_devices(void)
-{
-       int r;
-       struct bus_type *bus = dss_get_bus();
-
-       r = bus_for_each_dev(bus, NULL, NULL, dss_suspend_device);
-       if (r) {
-               /* resume all displays that were suspended */
-               dss_resume_all_devices();
-               return r;
+               if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+                       dssdev->driver->disable(dssdev);
+                       dssdev->activate_after_resume = true;
+               } else {
+                       dssdev->activate_after_resume = false;
+               }
        }
 
        return 0;
 }
 
-static int dss_resume_device(struct device *dev, void *data)
+int dss_resume_all_devices(void)
 {
-       int r;
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_device *dssdev = NULL;
 
-       if (dssdev->activate_after_resume) {
-               r = dssdev->driver->enable(dssdev);
-               if (r)
-                       return r;
-       }
+       for_each_dss_dev(dssdev) {
+               if (!dssdev->driver)
+                       continue;
 
-       dssdev->activate_after_resume = false;
+               if (dssdev->activate_after_resume) {
+                       dssdev->driver->enable(dssdev);
+                       dssdev->activate_after_resume = false;
+               }
+       }
 
        return 0;
 }
 
-int dss_resume_all_devices(void)
+void dss_disable_all_devices(void)
 {
-       struct bus_type *bus = dss_get_bus();
+       struct omap_dss_device *dssdev = NULL;
+
+       for_each_dss_dev(dssdev) {
+               if (!dssdev->driver)
+                       continue;
 
-       return bus_for_each_dev(bus, NULL, NULL, dss_resume_device);
+               if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+                       dssdev->driver->disable(dssdev);
+       }
 }
 
-static int dss_disable_device(struct device *dev, void *data)
+static LIST_HEAD(panel_list);
+static DEFINE_MUTEX(panel_list_mutex);
+static int disp_num_counter;
+
+int omapdss_register_display(struct omap_dss_device *dssdev)
 {
-       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_driver *drv = dssdev->driver;
 
-       if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
-               dssdev->driver->disable(dssdev);
+       snprintf(dssdev->alias, sizeof(dssdev->alias),
+                       "display%d", disp_num_counter++);
 
+       if (drv && drv->get_resolution == NULL)
+               drv->get_resolution = omapdss_default_get_resolution;
+       if (drv && drv->get_recommended_bpp == NULL)
+               drv->get_recommended_bpp = omapdss_default_get_recommended_bpp;
+       if (drv && drv->get_timings == NULL)
+               drv->get_timings = omapdss_default_get_timings;
+
+       mutex_lock(&panel_list_mutex);
+       list_add_tail(&dssdev->panel_list, &panel_list);
+       mutex_unlock(&panel_list_mutex);
        return 0;
 }
+EXPORT_SYMBOL(omapdss_register_display);
 
-void dss_disable_all_devices(void)
+void omapdss_unregister_display(struct omap_dss_device *dssdev)
 {
-       struct bus_type *bus = dss_get_bus();
-       bus_for_each_dev(bus, NULL, NULL, dss_disable_device);
+       mutex_lock(&panel_list_mutex);
+       list_del(&dssdev->panel_list);
+       mutex_unlock(&panel_list_mutex);
 }
+EXPORT_SYMBOL(omapdss_unregister_display);
 
-
-void omap_dss_get_device(struct omap_dss_device *dssdev)
+struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev)
 {
-       get_device(&dssdev->dev);
+       if (!try_module_get(dssdev->owner))
+               return NULL;
+
+       if (get_device(dssdev->dev) == NULL) {
+               module_put(dssdev->owner);
+               return NULL;
+       }
+
+       return dssdev;
 }
 EXPORT_SYMBOL(omap_dss_get_device);
 
 void omap_dss_put_device(struct omap_dss_device *dssdev)
 {
-       put_device(&dssdev->dev);
+       put_device(dssdev->dev);
+       module_put(dssdev->owner);
 }
 EXPORT_SYMBOL(omap_dss_put_device);
 
-/* ref count of the found device is incremented. ref count
- * of from-device is decremented. */
+/*
+ * ref count of the found device is incremented.
+ * ref count of from-device is decremented.
+ */
 struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from)
 {
-       struct device *dev;
-       struct device *dev_start = NULL;
-       struct omap_dss_device *dssdev = NULL;
+       struct list_head *l;
+       struct omap_dss_device *dssdev;
+
+       mutex_lock(&panel_list_mutex);
 
-       int match(struct device *dev, void *data)
-       {
-               return 1;
+       if (list_empty(&panel_list)) {
+               dssdev = NULL;
+               goto out;
        }
 
-       if (from)
-               dev_start = &from->dev;
-       dev = bus_find_device(dss_get_bus(), dev_start, NULL, match);
-       if (dev)
-               dssdev = to_dss_device(dev);
-       if (from)
-               put_device(&from->dev);
+       if (from == NULL) {
+               dssdev = list_first_entry(&panel_list, struct omap_dss_device,
+                               panel_list);
+               omap_dss_get_device(dssdev);
+               goto out;
+       }
+
+       omap_dss_put_device(from);
+
+       list_for_each(l, &panel_list) {
+               dssdev = list_entry(l, struct omap_dss_device, panel_list);
+               if (dssdev == from) {
+                       if (list_is_last(l, &panel_list)) {
+                               dssdev = NULL;
+                               goto out;
+                       }
+
+                       dssdev = list_entry(l->next, struct omap_dss_device,
+                                       panel_list);
+                       omap_dss_get_device(dssdev);
+                       goto out;
+               }
+       }
 
+       WARN(1, "'from' dssdev not found\n");
+
+       dssdev = NULL;
+out:
+       mutex_unlock(&panel_list_mutex);
        return dssdev;
 }
 EXPORT_SYMBOL(omap_dss_get_next_device);
@@ -198,24 +243,72 @@ struct omap_dss_device *omap_dss_find_device(void *data,
 }
 EXPORT_SYMBOL(omap_dss_find_device);
 
-int omap_dss_start_device(struct omap_dss_device *dssdev)
+void videomode_to_omap_video_timings(const struct videomode *vm,
+               struct omap_video_timings *ovt)
 {
-       if (!dssdev->driver) {
-               DSSDBG("no driver\n");
-               return -ENODEV;
-       }
-
-       if (!try_module_get(dssdev->dev.driver->owner)) {
-               return -ENODEV;
-       }
-
-       return 0;
+       memset(ovt, 0, sizeof(*ovt));
+
+       ovt->pixel_clock = vm->pixelclock / 1000;
+       ovt->x_res = vm->hactive;
+       ovt->hbp = vm->hback_porch;
+       ovt->hfp = vm->hfront_porch;
+       ovt->hsw = vm->hsync_len;
+       ovt->y_res = vm->vactive;
+       ovt->vbp = vm->vback_porch;
+       ovt->vfp = vm->vfront_porch;
+       ovt->vsw = vm->vsync_len;
+
+       ovt->vsync_level = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ?
+               OMAPDSS_SIG_ACTIVE_HIGH :
+               OMAPDSS_SIG_ACTIVE_LOW;
+       ovt->hsync_level = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ?
+               OMAPDSS_SIG_ACTIVE_HIGH :
+               OMAPDSS_SIG_ACTIVE_LOW;
+       ovt->de_level = vm->flags & DISPLAY_FLAGS_DE_HIGH ?
+               OMAPDSS_SIG_ACTIVE_HIGH :
+               OMAPDSS_SIG_ACTIVE_HIGH;
+       ovt->data_pclk_edge = vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ?
+               OMAPDSS_DRIVE_SIG_RISING_EDGE :
+               OMAPDSS_DRIVE_SIG_FALLING_EDGE;
+
+       ovt->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
 }
-EXPORT_SYMBOL(omap_dss_start_device);
+EXPORT_SYMBOL(videomode_to_omap_video_timings);
 
-void omap_dss_stop_device(struct omap_dss_device *dssdev)
+void omap_video_timings_to_videomode(const struct omap_video_timings *ovt,
+               struct videomode *vm)
 {
-       module_put(dssdev->dev.driver->owner);
+       memset(vm, 0, sizeof(*vm));
+
+       vm->pixelclock = ovt->pixel_clock * 1000;
+
+       vm->hactive = ovt->x_res;
+       vm->hback_porch = ovt->hbp;
+       vm->hfront_porch = ovt->hfp;
+       vm->hsync_len = ovt->hsw;
+       vm->vactive = ovt->y_res;
+       vm->vback_porch = ovt->vbp;
+       vm->vfront_porch = ovt->vfp;
+       vm->vsync_len = ovt->vsw;
+
+       if (ovt->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
+               vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH;
+       else
+               vm->flags |= DISPLAY_FLAGS_HSYNC_LOW;
+
+       if (ovt->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
+               vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH;
+       else
+               vm->flags |= DISPLAY_FLAGS_VSYNC_LOW;
+
+       if (ovt->de_level == OMAPDSS_SIG_ACTIVE_HIGH)
+               vm->flags |= DISPLAY_FLAGS_DE_HIGH;
+       else
+               vm->flags |= DISPLAY_FLAGS_DE_LOW;
+
+       if (ovt->data_pclk_edge == OMAPDSS_DRIVE_SIG_RISING_EDGE)
+               vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
+       else
+               vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
 }
-EXPORT_SYMBOL(omap_dss_stop_device);
-
+EXPORT_SYMBOL(omap_video_timings_to_videomode);
index 757b57f..a6b331e 100644 (file)
@@ -37,6 +37,8 @@
 #include "dss_features.h"
 
 static struct {
+       struct platform_device *pdev;
+
        struct regulator *vdds_dsi_reg;
        struct platform_device *dsidev;
 
@@ -46,7 +48,7 @@ static struct {
        struct dss_lcd_mgr_config mgr_config;
        int data_lines;
 
-       struct omap_dss_output output;
+       struct omap_dss_device output;
 } dpi;
 
 static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
@@ -129,7 +131,7 @@ static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
         * shifted. So skip all odd dividers when the pixel clock is on the
         * higher side.
         */
-       if (ctx->pck_min >= 1000000) {
+       if (ctx->pck_min >= 100000000) {
                if (lckd > 1 && lckd % 2 != 0)
                        return false;
 
@@ -156,7 +158,7 @@ static bool dpi_calc_hsdiv_cb(int regm_dispc, unsigned long dispc,
         * shifted. So skip all odd dividers when the pixel clock is on the
         * higher side.
         */
-       if (regm_dispc > 1 && regm_dispc % 2 != 0 && ctx->pck_min >= 1000000)
+       if (regm_dispc > 1 && regm_dispc % 2 != 0 && ctx->pck_min >= 100000000)
                return false;
 
        ctx->dsi_cinfo.regm_dispc = regm_dispc;
@@ -345,7 +347,7 @@ static void dpi_config_lcd_manager(struct omap_overlay_manager *mgr)
 
 int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
 {
-       struct omap_dss_output *out = &dpi.output;
+       struct omap_dss_device *out = &dpi.output;
        int r;
 
        mutex_lock(&dpi.lock);
@@ -362,12 +364,6 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
                goto err_no_out_mgr;
        }
 
-       r = omap_dss_start_device(dssdev);
-       if (r) {
-               DSSERR("failed to start device\n");
-               goto err_start_dev;
-       }
-
        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
                r = regulator_enable(dpi.vdds_dsi_reg);
                if (r)
@@ -422,8 +418,6 @@ err_get_dispc:
        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
                regulator_disable(dpi.vdds_dsi_reg);
 err_reg_enable:
-       omap_dss_stop_device(dssdev);
-err_start_dev:
 err_no_out_mgr:
 err_no_reg:
        mutex_unlock(&dpi.lock);
@@ -450,8 +444,6 @@ void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
        if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
                regulator_disable(dpi.vdds_dsi_reg);
 
-       omap_dss_stop_device(dssdev);
-
        mutex_unlock(&dpi.lock);
 }
 EXPORT_SYMBOL(omapdss_dpi_display_disable);
@@ -469,6 +461,16 @@ void omapdss_dpi_set_timings(struct omap_dss_device *dssdev,
 }
 EXPORT_SYMBOL(omapdss_dpi_set_timings);
 
+static void dpi_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       mutex_lock(&dpi.lock);
+
+       *timings = dpi.timings;
+
+       mutex_unlock(&dpi.lock);
+}
+
 int dpi_check_timings(struct omap_dss_device *dssdev,
                        struct omap_video_timings *timings)
 {
@@ -542,6 +544,50 @@ static int dpi_verify_dsi_pll(struct platform_device *dsidev)
        return 0;
 }
 
+static int dpi_init_regulator(void)
+{
+       struct regulator *vdds_dsi;
+
+       if (!dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
+               return 0;
+
+       if (dpi.vdds_dsi_reg)
+               return 0;
+
+       vdds_dsi = dss_get_vdds_dsi();
+
+       if (IS_ERR(vdds_dsi)) {
+               vdds_dsi = devm_regulator_get(&dpi.pdev->dev, "vdds_dsi");
+               if (IS_ERR(vdds_dsi)) {
+                       DSSERR("can't get VDDS_DSI regulator\n");
+                       return PTR_ERR(vdds_dsi);
+               }
+       }
+
+       dpi.vdds_dsi_reg = vdds_dsi;
+
+       return 0;
+}
+
+static void dpi_init_pll(void)
+{
+       struct platform_device *dsidev;
+
+       if (dpi.dsidev)
+               return;
+
+       dsidev = dpi_get_dsidev(dpi.output.dispc_channel);
+       if (!dsidev)
+               return;
+
+       if (dpi_verify_dsi_pll(dsidev)) {
+               DSSWARN("DSI PLL not operational\n");
+               return;
+       }
+
+       dpi.dsidev = dsidev;
+}
+
 /*
  * Return a hardcoded channel for the DPI output. This should work for
  * current use cases, but this can be later expanded to either resolve
@@ -572,41 +618,6 @@ static enum omap_channel dpi_get_channel(void)
        }
 }
 
-static int dpi_init_display(struct omap_dss_device *dssdev)
-{
-       struct platform_device *dsidev;
-
-       DSSDBG("init_display\n");
-
-       if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) &&
-                                       dpi.vdds_dsi_reg == NULL) {
-               struct regulator *vdds_dsi;
-
-               vdds_dsi = dss_get_vdds_dsi();
-
-               if (IS_ERR(vdds_dsi)) {
-                       DSSERR("can't get VDDS_DSI regulator\n");
-                       return PTR_ERR(vdds_dsi);
-               }
-
-               dpi.vdds_dsi_reg = vdds_dsi;
-       }
-
-       dsidev = dpi_get_dsidev(dpi.output.dispc_channel);
-
-       if (dsidev && dpi_verify_dsi_pll(dsidev)) {
-               dsidev = NULL;
-               DSSWARN("DSI PLL not operational\n");
-       }
-
-       if (dsidev)
-               DSSDBG("using DSI PLL for DPI clock\n");
-
-       dpi.dsidev = dsidev;
-
-       return 0;
-}
-
 static struct omap_dss_device *dpi_find_dssdev(struct platform_device *pdev)
 {
        struct omap_dss_board_info *pdata = pdev->dev.platform_data;
@@ -646,19 +657,18 @@ static int dpi_probe_pdata(struct platform_device *dpidev)
        if (!plat_dssdev)
                return 0;
 
+       r = dpi_init_regulator();
+       if (r)
+               return r;
+
+       dpi_init_pll();
+
        dssdev = dss_alloc_and_init_device(&dpidev->dev);
        if (!dssdev)
                return -ENOMEM;
 
        dss_copy_device_pdata(dssdev, plat_dssdev);
 
-       r = dpi_init_display(dssdev);
-       if (r) {
-               DSSERR("device %s init failed: %d\n", dssdev->name, r);
-               dss_put_device(dssdev);
-               return r;
-       }
-
        r = omapdss_output_set_device(&dpi.output, dssdev);
        if (r) {
                DSSERR("failed to connect output to new device: %s\n",
@@ -678,41 +688,108 @@ static int dpi_probe_pdata(struct platform_device *dpidev)
        return 0;
 }
 
+static int dpi_connect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       struct omap_overlay_manager *mgr;
+       int r;
+
+       r = dpi_init_regulator();
+       if (r)
+               return r;
+
+       dpi_init_pll();
+
+       mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
+       if (!mgr)
+               return -ENODEV;
+
+       r = dss_mgr_connect(mgr, dssdev);
+       if (r)
+               return r;
+
+       r = omapdss_output_set_device(dssdev, dst);
+       if (r) {
+               DSSERR("failed to connect output to new device: %s\n",
+                               dst->name);
+               dss_mgr_disconnect(mgr, dssdev);
+               return r;
+       }
+
+       return 0;
+}
+
+static void dpi_disconnect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       WARN_ON(dst != dssdev->device);
+
+       if (dst != dssdev->device)
+               return;
+
+       omapdss_output_unset_device(dssdev);
+
+       if (dssdev->manager)
+               dss_mgr_disconnect(dssdev->manager, dssdev);
+}
+
+static const struct omapdss_dpi_ops dpi_ops = {
+       .connect = dpi_connect,
+       .disconnect = dpi_disconnect,
+
+       .enable = omapdss_dpi_display_enable,
+       .disable = omapdss_dpi_display_disable,
+
+       .check_timings = dpi_check_timings,
+       .set_timings = omapdss_dpi_set_timings,
+       .get_timings = dpi_get_timings,
+
+       .set_data_lines = omapdss_dpi_set_data_lines,
+};
+
 static void dpi_init_output(struct platform_device *pdev)
 {
-       struct omap_dss_output *out = &dpi.output;
+       struct omap_dss_device *out = &dpi.output;
 
-       out->pdev = pdev;
+       out->dev = &pdev->dev;
        out->id = OMAP_DSS_OUTPUT_DPI;
-       out->type = OMAP_DISPLAY_TYPE_DPI;
+       out->output_type = OMAP_DISPLAY_TYPE_DPI;
        out->name = "dpi.0";
        out->dispc_channel = dpi_get_channel();
+       out->ops.dpi = &dpi_ops;
+       out->owner = THIS_MODULE;
 
-       dss_register_output(out);
+       omapdss_register_output(out);
 }
 
 static void __exit dpi_uninit_output(struct platform_device *pdev)
 {
-       struct omap_dss_output *out = &dpi.output;
+       struct omap_dss_device *out = &dpi.output;
 
-       dss_unregister_output(out);
+       omapdss_unregister_output(out);
 }
 
 static int omap_dpi_probe(struct platform_device *pdev)
 {
        int r;
 
+       dpi.pdev = pdev;
+
        mutex_init(&dpi.lock);
 
        dpi_init_output(pdev);
 
-       r = dpi_probe_pdata(pdev);
-       if (r) {
-               dpi_uninit_output(pdev);
-               return r;
+       if (pdev->dev.platform_data) {
+               r = dpi_probe_pdata(pdev);
+               if (r)
+                       goto err_probe;
        }
 
        return 0;
+
+err_probe:
+       dpi_uninit_output(pdev);
+       return r;
 }
 
 static int __exit omap_dpi_remove(struct platform_device *pdev)
index a73dedc..99a043b 100644 (file)
@@ -363,7 +363,7 @@ struct dsi_data {
        enum omap_dss_dsi_mode mode;
        struct omap_dss_dsi_videomode_timings vm_timings;
 
-       struct omap_dss_output output;
+       struct omap_dss_device output;
 };
 
 struct dsi_packet_sent_handler_data {
@@ -383,12 +383,21 @@ static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dside
 
 static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev)
 {
-       return dssdev->output->pdev;
+       /* HACK: dssdev can be either the panel device, when using old API, or
+        * the dsi device itself, when using the new API. So we solve this for
+        * now by checking the dssdev->id. This will be removed when the old API
+        * is removed.
+        */
+       if (dssdev->id == OMAP_DSS_OUTPUT_DSI1 ||
+                       dssdev->id == OMAP_DSS_OUTPUT_DSI2)
+               return to_platform_device(dssdev->dev);
+
+       return to_platform_device(dssdev->output->dev);
 }
 
 struct platform_device *dsi_get_dsidev_from_id(int module)
 {
-       struct omap_dss_output *out;
+       struct omap_dss_device *out;
        enum omap_dss_output_id id;
 
        switch (module) {
@@ -404,7 +413,7 @@ struct platform_device *dsi_get_dsidev_from_id(int module)
 
        out = omap_dss_get_output(id);
 
-       return out ? out->pdev : NULL;
+       return out ? to_platform_device(out->dev) : NULL;
 }
 
 static inline void dsi_write_reg(struct platform_device *dsidev,
@@ -1114,6 +1123,30 @@ void dsi_runtime_put(struct platform_device *dsidev)
        WARN_ON(r < 0 && r != -ENOSYS);
 }
 
+static int dsi_regulator_init(struct platform_device *dsidev)
+{
+       struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+       struct regulator *vdds_dsi;
+
+       if (dsi->vdds_dsi_reg != NULL)
+               return 0;
+
+       vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "vdds_dsi");
+
+       /* DT HACK: try VCXIO to make omapdss work for o4 sdp/panda */
+       if (IS_ERR(vdds_dsi))
+               vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "VCXIO");
+
+       if (IS_ERR(vdds_dsi)) {
+               DSSERR("can't get VDDS_DSI regulator\n");
+               return PTR_ERR(vdds_dsi);
+       }
+
+       dsi->vdds_dsi_reg = vdds_dsi;
+
+       return 0;
+}
+
 /* source clock for DSI PLL. this could also be PCLKFREE */
 static inline void dsi_enable_pll_clock(struct platform_device *dsidev,
                bool enable)
@@ -1592,22 +1625,9 @@ int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk,
         */
        enable_hsclk = enable_hsdiv = true;
 
-       if (dsi->vdds_dsi_reg == NULL) {
-               struct regulator *vdds_dsi;
-
-               vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi");
-
-               /* DT HACK: try VCXIO to make omapdss work for o4 sdp/panda */
-               if (IS_ERR(vdds_dsi))
-                       vdds_dsi = regulator_get(&dsi->pdev->dev, "VCXIO");
-
-               if (IS_ERR(vdds_dsi)) {
-                       DSSERR("can't get VDDS_DSI regulator\n");
-                       return PTR_ERR(vdds_dsi);
-               }
-
-               dsi->vdds_dsi_reg = vdds_dsi;
-       }
+       r = dsi_regulator_init(dsidev);
+       if (r)
+               return r;
 
        dsi_enable_pll_clock(dsidev, 1);
        /*
@@ -4122,7 +4142,7 @@ int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
        struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
        struct omap_overlay_manager *mgr = dsi->output.manager;
        int bpp = dsi_get_pixel_size(dsi->pix_fmt);
-       struct omap_dss_output *out = &dsi->output;
+       struct omap_dss_device *out = &dsi->output;
        u8 data_type;
        u16 word_count;
        int r;
@@ -4581,12 +4601,6 @@ int omapdss_dsi_display_enable(struct omap_dss_device *dssdev)
 
        mutex_lock(&dsi->lock);
 
-       r = omap_dss_start_device(dssdev);
-       if (r) {
-               DSSERR("failed to start device\n");
-               goto err_start_dev;
-       }
-
        r = dsi_runtime_get(dsidev);
        if (r)
                goto err_get_dsi;
@@ -4607,8 +4621,6 @@ err_init_dsi:
        dsi_enable_pll_clock(dsidev, 0);
        dsi_runtime_put(dsidev);
 err_get_dsi:
-       omap_dss_stop_device(dssdev);
-err_start_dev:
        mutex_unlock(&dsi->lock);
        DSSDBG("dsi_display_enable FAILED\n");
        return r;
@@ -4637,8 +4649,6 @@ void omapdss_dsi_display_disable(struct omap_dss_device *dssdev,
        dsi_runtime_put(dsidev);
        dsi_enable_pll_clock(dsidev, 0);
 
-       omap_dss_stop_device(dssdev);
-
        mutex_unlock(&dsi->lock);
 }
 EXPORT_SYMBOL(omapdss_dsi_display_disable);
@@ -5225,34 +5235,6 @@ static enum omap_channel dsi_get_channel(int module_id)
        }
 }
 
-static int dsi_init_display(struct omap_dss_device *dssdev)
-{
-       struct platform_device *dsidev =
-                       dsi_get_dsidev_from_id(dssdev->phy.dsi.module);
-       struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
-
-       DSSDBG("DSI init\n");
-
-       if (dsi->vdds_dsi_reg == NULL) {
-               struct regulator *vdds_dsi;
-
-               vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi");
-
-               /* DT HACK: try VCXIO to make omapdss work for o4 sdp/panda */
-               if (IS_ERR(vdds_dsi))
-                       vdds_dsi = regulator_get(&dsi->pdev->dev, "VCXIO");
-
-               if (IS_ERR(vdds_dsi)) {
-                       DSSERR("can't get VDDS_DSI regulator\n");
-                       return PTR_ERR(vdds_dsi);
-               }
-
-               dsi->vdds_dsi_reg = vdds_dsi;
-       }
-
-       return 0;
-}
-
 int omap_dsi_request_vc(struct omap_dss_device *dssdev, int *channel)
 {
        struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -5410,19 +5392,16 @@ static int dsi_probe_pdata(struct platform_device *dsidev)
        if (!plat_dssdev)
                return 0;
 
+       r = dsi_regulator_init(dsidev);
+       if (r)
+               return r;
+
        dssdev = dss_alloc_and_init_device(&dsidev->dev);
        if (!dssdev)
                return -ENOMEM;
 
        dss_copy_device_pdata(dssdev, plat_dssdev);
 
-       r = dsi_init_display(dssdev);
-       if (r) {
-               DSSERR("device %s init failed: %d\n", dssdev->name, r);
-               dss_put_device(dssdev);
-               return r;
-       }
-
        r = omapdss_output_set_device(&dsi->output, dssdev);
        if (r) {
                DSSERR("failed to connect output to new device: %s\n",
@@ -5442,28 +5421,113 @@ static int dsi_probe_pdata(struct platform_device *dsidev)
        return 0;
 }
 
+static int dsi_connect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+       struct omap_overlay_manager *mgr;
+       int r;
+
+       r = dsi_regulator_init(dsidev);
+       if (r)
+               return r;
+
+       mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
+       if (!mgr)
+               return -ENODEV;
+
+       r = dss_mgr_connect(mgr, dssdev);
+       if (r)
+               return r;
+
+       r = omapdss_output_set_device(dssdev, dst);
+       if (r) {
+               DSSERR("failed to connect output to new device: %s\n",
+                               dssdev->name);
+               dss_mgr_disconnect(mgr, dssdev);
+               return r;
+       }
+
+       return 0;
+}
+
+static void dsi_disconnect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       WARN_ON(dst != dssdev->device);
+
+       if (dst != dssdev->device)
+               return;
+
+       omapdss_output_unset_device(dssdev);
+
+       if (dssdev->manager)
+               dss_mgr_disconnect(dssdev->manager, dssdev);
+}
+
+static const struct omapdss_dsi_ops dsi_ops = {
+       .connect = dsi_connect,
+       .disconnect = dsi_disconnect,
+
+       .bus_lock = dsi_bus_lock,
+       .bus_unlock = dsi_bus_unlock,
+
+       .enable = omapdss_dsi_display_enable,
+       .disable = omapdss_dsi_display_disable,
+
+       .enable_hs = omapdss_dsi_vc_enable_hs,
+
+       .configure_pins = omapdss_dsi_configure_pins,
+       .set_config = omapdss_dsi_set_config,
+
+       .enable_video_output = dsi_enable_video_output,
+       .disable_video_output = dsi_disable_video_output,
+
+       .update = omap_dsi_update,
+
+       .enable_te = omapdss_dsi_enable_te,
+
+       .request_vc = omap_dsi_request_vc,
+       .set_vc_id = omap_dsi_set_vc_id,
+       .release_vc = omap_dsi_release_vc,
+
+       .dcs_write = dsi_vc_dcs_write,
+       .dcs_write_nosync = dsi_vc_dcs_write_nosync,
+       .dcs_read = dsi_vc_dcs_read,
+
+       .gen_write = dsi_vc_generic_write,
+       .gen_write_nosync = dsi_vc_generic_write_nosync,
+       .gen_read = dsi_vc_generic_read,
+
+       .bta_sync = dsi_vc_send_bta_sync,
+
+       .set_max_rx_packet_size = dsi_vc_set_max_rx_packet_size,
+};
+
 static void dsi_init_output(struct platform_device *dsidev)
 {
        struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
-       struct omap_dss_output *out = &dsi->output;
+       struct omap_dss_device *out = &dsi->output;
 
-       out->pdev = dsidev;
+       out->dev = &dsidev->dev;
        out->id = dsi->module_id == 0 ?
                        OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2;
 
-       out->type = OMAP_DISPLAY_TYPE_DSI;
+       out->output_type = OMAP_DISPLAY_TYPE_DSI;
        out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1";
        out->dispc_channel = dsi_get_channel(dsi->module_id);
+       out->ops.dsi = &dsi_ops;
+       out->owner = THIS_MODULE;
 
-       dss_register_output(out);
+       omapdss_register_output(out);
 }
 
 static void dsi_uninit_output(struct platform_device *dsidev)
 {
        struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
-       struct omap_dss_output *out = &dsi->output;
+       struct omap_dss_device *out = &dsi->output;
 
-       dss_unregister_output(out);
+       omapdss_unregister_output(out);
 }
 
 /* DSI1 HW IP initialisation */
@@ -5563,12 +5627,10 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
 
        dsi_init_output(dsidev);
 
-       r = dsi_probe_pdata(dsidev);
-       if (r) {
-               dsi_runtime_put(dsidev);
-               dsi_uninit_output(dsidev);
-               pm_runtime_disable(&dsidev->dev);
-               return r;
+       if (dsidev->dev.platform_data) {
+               r = dsi_probe_pdata(dsidev);
+               if (r)
+                       goto err_probe;
        }
 
        dsi_runtime_put(dsidev);
@@ -5586,6 +5648,9 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
 #endif
        return 0;
 
+err_probe:
+       dsi_runtime_put(dsidev);
+       dsi_uninit_output(dsidev);
 err_runtime_get:
        pm_runtime_disable(&dsidev->dev);
        return r;
@@ -5603,14 +5668,9 @@ static int __exit omap_dsihw_remove(struct platform_device *dsidev)
 
        pm_runtime_disable(&dsidev->dev);
 
-       if (dsi->vdds_dsi_reg != NULL) {
-               if (dsi->vdds_dsi_enabled) {
-                       regulator_disable(dsi->vdds_dsi_reg);
-                       dsi->vdds_dsi_enabled = false;
-               }
-
-               regulator_put(dsi->vdds_dsi_reg);
-               dsi->vdds_dsi_reg = NULL;
+       if (dsi->vdds_dsi_reg != NULL && dsi->vdds_dsi_enabled) {
+               regulator_disable(dsi->vdds_dsi_reg);
+               dsi->vdds_dsi_enabled = false;
        }
 
        return 0;
index 94f66f9..bd01608 100644 (file)
@@ -157,7 +157,8 @@ static void dss_restore_context(void)
 
 int dss_get_ctx_loss_count(void)
 {
-       struct omap_dss_board_info *board_data = dss.pdev->dev.platform_data;
+       struct platform_device *core_pdev = dss_get_core_pdev();
+       struct omap_dss_board_info *board_data = core_pdev->dev.platform_data;
        int cnt;
 
        if (!board_data->get_context_loss_count)
index 8475893..50a2362 100644 (file)
@@ -179,23 +179,19 @@ void dss_put_device(struct omap_dss_device *dssdev);
 void dss_copy_device_pdata(struct omap_dss_device *dst,
                const struct omap_dss_device *src);
 
-/* output */
-void dss_register_output(struct omap_dss_output *out);
-void dss_unregister_output(struct omap_dss_output *out);
-
 /* display */
 int dss_suspend_all_devices(void);
 int dss_resume_all_devices(void);
 void dss_disable_all_devices(void);
 
-int display_init_sysfs(struct platform_device *pdev,
-               struct omap_dss_device *dssdev);
-void display_uninit_sysfs(struct platform_device *pdev,
-               struct omap_dss_device *dssdev);
+int display_init_sysfs(struct platform_device *pdev);
+void display_uninit_sysfs(struct platform_device *pdev);
 
 /* manager */
-int dss_init_overlay_managers(struct platform_device *pdev);
-void dss_uninit_overlay_managers(struct platform_device *pdev);
+int dss_init_overlay_managers(void);
+void dss_uninit_overlay_managers(void);
+int dss_init_overlay_managers_sysfs(struct platform_device *pdev);
+void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev);
 int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
                const struct omap_overlay_manager_info *info);
 int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
@@ -426,6 +422,7 @@ void dispc_mgr_set_clock_div(enum omap_channel channel,
                const struct dispc_clock_info *cinfo);
 int dispc_mgr_get_clock_div(enum omap_channel channel,
                struct dispc_clock_info *cinfo);
+void dispc_set_tv_pclk(unsigned long pclk);
 
 u32 dispc_wb_get_framedone_irq(void);
 bool dispc_wb_go_busy(void);
@@ -437,17 +434,8 @@ int dispc_wb_setup(const struct omap_dss_writeback_info *wi,
                bool mem_to_mem, const struct omap_video_timings *timings);
 
 /* VENC */
-#ifdef CONFIG_OMAP2_DSS_VENC
 int venc_init_platform_driver(void) __init;
 void venc_uninit_platform_driver(void) __exit;
-unsigned long venc_get_pixel_clock(void);
-#else
-static inline unsigned long venc_get_pixel_clock(void)
-{
-       WARN("%s: VENC not compiled in, returning pclk as 0\n", __func__);
-       return 0;
-}
-#endif
 int omapdss_venc_display_enable(struct omap_dss_device *dssdev);
 void omapdss_venc_display_disable(struct omap_dss_device *dssdev);
 void omapdss_venc_set_timings(struct omap_dss_device *dssdev,
@@ -464,17 +452,8 @@ int venc_panel_init(void);
 void venc_panel_exit(void);
 
 /* HDMI */
-#ifdef CONFIG_OMAP4_DSS_HDMI
 int hdmi_init_platform_driver(void) __init;
 void hdmi_uninit_platform_driver(void) __exit;
-unsigned long hdmi_get_pixel_clock(void);
-#else
-static inline unsigned long hdmi_get_pixel_clock(void)
-{
-       WARN("%s: HDMI not compiled in, returning pclk as 0\n", __func__);
-       return 0;
-}
-#endif
 int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev);
 void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev);
 int omapdss_hdmi_core_enable(struct omap_dss_device *dssdev);
index 77dbe0c..b9cfebb 100644 (file)
@@ -797,7 +797,6 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
        .phy_enable             =       ti_hdmi_4xxx_phy_enable,
        .phy_disable            =       ti_hdmi_4xxx_phy_disable,
        .read_edid              =       ti_hdmi_4xxx_read_edid,
-       .detect                 =       ti_hdmi_4xxx_detect,
        .pll_enable             =       ti_hdmi_4xxx_pll_enable,
        .pll_disable            =       ti_hdmi_4xxx_pll_disable,
        .video_enable           =       ti_hdmi_4xxx_wp_video_start,
index a109934..44a885b 100644 (file)
@@ -70,7 +70,9 @@ static struct {
        int ls_oe_gpio;
        int hpd_gpio;
 
-       struct omap_dss_output output;
+       bool core_enabled;
+
+       struct omap_dss_device output;
 } hdmi;
 
 /*
@@ -328,6 +330,29 @@ static void hdmi_runtime_put(void)
        WARN_ON(r < 0 && r != -ENOSYS);
 }
 
+static int hdmi_init_regulator(void)
+{
+       struct regulator *reg;
+
+       if (hdmi.vdda_hdmi_dac_reg != NULL)
+               return 0;
+
+       reg = devm_regulator_get(&hdmi.pdev->dev, "vdda_hdmi_dac");
+
+       /* DT HACK: try VDAC to make omapdss work for o4 sdp/panda */
+       if (IS_ERR(reg))
+               reg = devm_regulator_get(&hdmi.pdev->dev, "VDAC");
+
+       if (IS_ERR(reg)) {
+               DSSERR("can't get VDDA_HDMI_DAC regulator\n");
+               return PTR_ERR(reg);
+       }
+
+       hdmi.vdda_hdmi_dac_reg = reg;
+
+       return 0;
+}
+
 static int hdmi_init_display(struct omap_dss_device *dssdev)
 {
        int r;
@@ -342,22 +367,9 @@ static int hdmi_init_display(struct omap_dss_device *dssdev)
 
        dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version());
 
-       if (hdmi.vdda_hdmi_dac_reg == NULL) {
-               struct regulator *reg;
-
-               reg = devm_regulator_get(&hdmi.pdev->dev, "vdda_hdmi_dac");
-
-               /* DT HACK: try VDAC to make omapdss work for o4 sdp/panda */
-               if (IS_ERR(reg))
-                       reg = devm_regulator_get(&hdmi.pdev->dev, "VDAC");
-
-               if (IS_ERR(reg)) {
-                       DSSERR("can't get VDDA_HDMI_DAC regulator\n");
-                       return PTR_ERR(reg);
-               }
-
-               hdmi.vdda_hdmi_dac_reg = reg;
-       }
+       r = hdmi_init_regulator();
+       if (r)
+               return r;
 
        r = gpio_request_array(gpios, ARRAY_SIZE(gpios));
        if (r)
@@ -455,12 +467,6 @@ end:       return cm;
 
 }
 
-unsigned long hdmi_get_pixel_clock(void)
-{
-       /* HDMI Pixel Clock in Mhz */
-       return hdmi.ip_data.cfg.timings.pixel_clock * 1000;
-}
-
 static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
                struct hdmi_pll_info *pi)
 {
@@ -511,8 +517,10 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev)
 {
        int r;
 
-       gpio_set_value(hdmi.ct_cp_hpd_gpio, 1);
-       gpio_set_value(hdmi.ls_oe_gpio, 1);
+       if (gpio_is_valid(hdmi.ct_cp_hpd_gpio))
+               gpio_set_value(hdmi.ct_cp_hpd_gpio, 1);
+       if (gpio_is_valid(hdmi.ls_oe_gpio))
+               gpio_set_value(hdmi.ls_oe_gpio, 1);
 
        /* wait 300us after CT_CP_HPD for the 5V power output to reach 90% */
        udelay(300);
@@ -528,29 +536,37 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev)
        /* Make selection of HDMI in DSS */
        dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
 
+       hdmi.core_enabled = true;
+
        return 0;
 
 err_runtime_get:
        regulator_disable(hdmi.vdda_hdmi_dac_reg);
 err_vdac_enable:
-       gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
-       gpio_set_value(hdmi.ls_oe_gpio, 0);
+       if (gpio_is_valid(hdmi.ct_cp_hpd_gpio))
+               gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
+       if (gpio_is_valid(hdmi.ls_oe_gpio))
+               gpio_set_value(hdmi.ls_oe_gpio, 0);
        return r;
 }
 
 static void hdmi_power_off_core(struct omap_dss_device *dssdev)
 {
+       hdmi.core_enabled = false;
+
        hdmi_runtime_put();
        regulator_disable(hdmi.vdda_hdmi_dac_reg);
-       gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
-       gpio_set_value(hdmi.ls_oe_gpio, 0);
+       if (gpio_is_valid(hdmi.ct_cp_hpd_gpio))
+               gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
+       if (gpio_is_valid(hdmi.ls_oe_gpio))
+               gpio_set_value(hdmi.ls_oe_gpio, 0);
 }
 
 static int hdmi_power_on_full(struct omap_dss_device *dssdev)
 {
        int r;
        struct omap_video_timings *p;
-       struct omap_overlay_manager *mgr = dssdev->output->manager;
+       struct omap_overlay_manager *mgr = hdmi.output.manager;
        unsigned long phy;
 
        r = hdmi_power_on_core(dssdev);
@@ -613,7 +629,7 @@ err_pll_enable:
 
 static void hdmi_power_off_full(struct omap_dss_device *dssdev)
 {
-       struct omap_overlay_manager *mgr = dssdev->output->manager;
+       struct omap_overlay_manager *mgr = hdmi.output.manager;
 
        dss_mgr_disable(mgr);
 
@@ -653,9 +669,23 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev,
        if (t != NULL)
                hdmi.ip_data.cfg = *t;
 
+       dispc_set_tv_pclk(t->timings.pixel_clock * 1000);
+
        mutex_unlock(&hdmi.lock);
 }
 
+static void omapdss_hdmi_display_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       const struct hdmi_config *cfg;
+
+       cfg = hdmi_get_timings();
+       if (cfg == NULL)
+               cfg = &vesa_timings[0];
+
+       memcpy(timings, &cfg->timings, sizeof(cfg->timings));
+}
+
 static void hdmi_dump_regs(struct seq_file *s)
 {
        mutex_lock(&hdmi.lock);
@@ -700,7 +730,7 @@ bool omapdss_hdmi_detect(void)
        r = hdmi_runtime_get();
        BUG_ON(r);
 
-       r = hdmi.ip_data.ops->detect(&hdmi.ip_data);
+       r = gpio_get_value(hdmi.hpd_gpio);
 
        hdmi_runtime_put();
        mutex_unlock(&hdmi.lock);
@@ -710,7 +740,7 @@ bool omapdss_hdmi_detect(void)
 
 int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev)
 {
-       struct omap_dss_output *out = dssdev->output;
+       struct omap_dss_device *out = &hdmi.output;
        int r = 0;
 
        DSSDBG("ENTER hdmi_display_enable\n");
@@ -723,25 +753,15 @@ int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev)
                goto err0;
        }
 
-       hdmi.ip_data.hpd_gpio = hdmi.hpd_gpio;
-
-       r = omap_dss_start_device(dssdev);
-       if (r) {
-               DSSERR("failed to start device\n");
-               goto err0;
-       }
-
        r = hdmi_power_on_full(dssdev);
        if (r) {
                DSSERR("failed to power on device\n");
-               goto err1;
+               goto err0;
        }
 
        mutex_unlock(&hdmi.lock);
        return 0;
 
-err1:
-       omap_dss_stop_device(dssdev);
 err0:
        mutex_unlock(&hdmi.lock);
        return r;
@@ -755,8 +775,6 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev)
 
        hdmi_power_off_full(dssdev);
 
-       omap_dss_stop_device(dssdev);
-
        mutex_unlock(&hdmi.lock);
 }
 
@@ -768,8 +786,6 @@ int omapdss_hdmi_core_enable(struct omap_dss_device *dssdev)
 
        mutex_lock(&hdmi.lock);
 
-       hdmi.ip_data.hpd_gpio = hdmi.hpd_gpio;
-
        r = hdmi_power_on_core(dssdev);
        if (r) {
                DSSERR("failed to power on device\n");
@@ -1033,24 +1049,219 @@ static int hdmi_probe_pdata(struct platform_device *pdev)
        return 0;
 }
 
+static int hdmi_connect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       struct omap_overlay_manager *mgr;
+       int r;
+
+       dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version());
+
+       r = hdmi_init_regulator();
+       if (r)
+               return r;
+
+       mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
+       if (!mgr)
+               return -ENODEV;
+
+       r = dss_mgr_connect(mgr, dssdev);
+       if (r)
+               return r;
+
+       r = omapdss_output_set_device(dssdev, dst);
+       if (r) {
+               DSSERR("failed to connect output to new device: %s\n",
+                               dst->name);
+               dss_mgr_disconnect(mgr, dssdev);
+               return r;
+       }
+
+       return 0;
+}
+
+static void hdmi_disconnect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       WARN_ON(dst != dssdev->device);
+
+       if (dst != dssdev->device)
+               return;
+
+       omapdss_output_unset_device(dssdev);
+
+       if (dssdev->manager)
+               dss_mgr_disconnect(dssdev->manager, dssdev);
+}
+
+static int hdmi_read_edid(struct omap_dss_device *dssdev,
+               u8 *edid, int len)
+{
+       bool need_enable;
+       int r;
+
+       need_enable = hdmi.core_enabled == false;
+
+       if (need_enable) {
+               r = omapdss_hdmi_core_enable(dssdev);
+               if (r)
+                       return r;
+       }
+
+       r = omapdss_hdmi_read_edid(edid, len);
+
+       if (need_enable)
+               omapdss_hdmi_core_disable(dssdev);
+
+       return r;
+}
+
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+static int omapdss_hdmi_audio_enable(struct omap_dss_device *dssdev)
+{
+       int r;
+
+       mutex_lock(&hdmi.lock);
+
+       if (!hdmi_mode_has_audio()) {
+               r = -EPERM;
+               goto err;
+       }
+
+       r = hdmi_audio_enable();
+       if (r)
+               goto err;
+
+       mutex_unlock(&hdmi.lock);
+       return 0;
+
+err:
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+
+static void omapdss_hdmi_audio_disable(struct omap_dss_device *dssdev)
+{
+       hdmi_audio_disable();
+}
+
+static int omapdss_hdmi_audio_start(struct omap_dss_device *dssdev)
+{
+       return hdmi_audio_start();
+}
+
+static void omapdss_hdmi_audio_stop(struct omap_dss_device *dssdev)
+{
+       hdmi_audio_stop();
+}
+
+static bool omapdss_hdmi_audio_supported(struct omap_dss_device *dssdev)
+{
+       bool r;
+
+       mutex_lock(&hdmi.lock);
+
+       r = hdmi_mode_has_audio();
+
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+
+static int omapdss_hdmi_audio_config(struct omap_dss_device *dssdev,
+               struct omap_dss_audio *audio)
+{
+       int r;
+
+       mutex_lock(&hdmi.lock);
+
+       if (!hdmi_mode_has_audio()) {
+               r = -EPERM;
+               goto err;
+       }
+
+       r = hdmi_audio_config(audio);
+       if (r)
+               goto err;
+
+       mutex_unlock(&hdmi.lock);
+       return 0;
+
+err:
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+#else
+static int omapdss_hdmi_audio_enable(struct omap_dss_device *dssdev)
+{
+       return -EPERM;
+}
+
+static void omapdss_hdmi_audio_disable(struct omap_dss_device *dssdev)
+{
+}
+
+static int omapdss_hdmi_audio_start(struct omap_dss_device *dssdev)
+{
+       return -EPERM;
+}
+
+static void omapdss_hdmi_audio_stop(struct omap_dss_device *dssdev)
+{
+}
+
+static bool omapdss_hdmi_audio_supported(struct omap_dss_device *dssdev)
+{
+       return false;
+}
+
+static int omapdss_hdmi_audio_config(struct omap_dss_device *dssdev,
+               struct omap_dss_audio *audio)
+{
+       return -EPERM;
+}
+#endif
+
+static const struct omapdss_hdmi_ops hdmi_ops = {
+       .connect                = hdmi_connect,
+       .disconnect             = hdmi_disconnect,
+
+       .enable                 = omapdss_hdmi_display_enable,
+       .disable                = omapdss_hdmi_display_disable,
+
+       .check_timings          = omapdss_hdmi_display_check_timing,
+       .set_timings            = omapdss_hdmi_display_set_timing,
+       .get_timings            = omapdss_hdmi_display_get_timings,
+
+       .read_edid              = hdmi_read_edid,
+
+       .audio_enable           = omapdss_hdmi_audio_enable,
+       .audio_disable          = omapdss_hdmi_audio_disable,
+       .audio_start            = omapdss_hdmi_audio_start,
+       .audio_stop             = omapdss_hdmi_audio_stop,
+       .audio_supported        = omapdss_hdmi_audio_supported,
+       .audio_config           = omapdss_hdmi_audio_config,
+};
+
 static void hdmi_init_output(struct platform_device *pdev)
 {
-       struct omap_dss_output *out = &hdmi.output;
+       struct omap_dss_device *out = &hdmi.output;
 
-       out->pdev = pdev;
+       out->dev = &pdev->dev;
        out->id = OMAP_DSS_OUTPUT_HDMI;
-       out->type = OMAP_DISPLAY_TYPE_HDMI;
+       out->output_type = OMAP_DISPLAY_TYPE_HDMI;
        out->name = "hdmi.0";
        out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
+       out->ops.hdmi = &hdmi_ops;
+       out->owner = THIS_MODULE;
 
-       dss_register_output(out);
+       omapdss_register_output(out);
 }
 
 static void __exit hdmi_uninit_output(struct platform_device *pdev)
 {
-       struct omap_dss_output *out = &hdmi.output;
+       struct omap_dss_device *out = &hdmi.output;
 
-       dss_unregister_output(out);
+       omapdss_unregister_output(out);
 }
 
 /* HDMI HW IP initialisation */
@@ -1071,6 +1282,12 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
        if (IS_ERR(hdmi.ip_data.base_wp))
                return PTR_ERR(hdmi.ip_data.base_wp);
 
+       hdmi.ip_data.irq = platform_get_irq(pdev, 0);
+       if (hdmi.ip_data.irq < 0) {
+               DSSERR("platform_get_irq failed\n");
+               return -ENODEV;
+       }
+
        r = hdmi_get_clocks(pdev);
        if (r) {
                DSSERR("can't get clocks\n");
@@ -1084,6 +1301,10 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
        hdmi.ip_data.pll_offset = HDMI_PLLCTRL;
        hdmi.ip_data.phy_offset = HDMI_PHY;
 
+       hdmi.ct_cp_hpd_gpio = -1;
+       hdmi.ls_oe_gpio = -1;
+       hdmi.hpd_gpio = -1;
+
        hdmi_init_output(pdev);
 
        r = hdmi_panel_init();
@@ -1094,15 +1315,19 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
 
        dss_debugfs_create_file("hdmi", hdmi_dump_regs);
 
-       r = hdmi_probe_pdata(pdev);
-       if (r) {
-               hdmi_panel_exit();
-               hdmi_uninit_output(pdev);
-               pm_runtime_disable(&pdev->dev);
-               return r;
+       if (pdev->dev.platform_data) {
+               r = hdmi_probe_pdata(pdev);
+               if (r)
+                       goto err_probe;
        }
 
        return 0;
+
+err_probe:
+       hdmi_panel_exit();
+       hdmi_uninit_output(pdev);
+       pm_runtime_disable(&pdev->dev);
+       return r;
 }
 
 static int __exit hdmi_remove_child(struct device *dev, void *data)
index 9a2fb59..de7e7b5 100644 (file)
@@ -50,6 +50,7 @@ static ssize_t manager_display_store(struct omap_overlay_manager *mgr,
        int r = 0;
        size_t len = size;
        struct omap_dss_device *dssdev = NULL;
+       struct omap_dss_device *old_dssdev;
 
        int match(struct omap_dss_device *dssdev, void *data)
        {
@@ -66,32 +67,44 @@ static ssize_t manager_display_store(struct omap_overlay_manager *mgr,
        if (len > 0 && dssdev == NULL)
                return -EINVAL;
 
-       if (dssdev)
+       if (dssdev) {
                DSSDBG("display %s found\n", dssdev->name);
 
-       if (mgr->output) {
-               r = mgr->unset_output(mgr);
-               if (r) {
-                       DSSERR("failed to unset current output\n");
+               if (omapdss_device_is_connected(dssdev)) {
+                       DSSERR("new display is already connected\n");
+                       r = -EINVAL;
+                       goto put_device;
+               }
+
+               if (omapdss_device_is_enabled(dssdev)) {
+                       DSSERR("new display is not disabled\n");
+                       r = -EINVAL;
                        goto put_device;
                }
        }
 
-       if (dssdev) {
-               struct omap_dss_output *out = dssdev->output;
-
-               /*
-                * a registered device should have an output connected to it
-                * already
-                */
-               if (!out) {
-                       DSSERR("device has no output connected to it\n");
+       old_dssdev = mgr->get_device(mgr);
+       if (old_dssdev) {
+               if (omapdss_device_is_enabled(old_dssdev)) {
+                       DSSERR("old display is not disabled\n");
+                       r = -EINVAL;
                        goto put_device;
                }
 
-               r = mgr->set_output(mgr, out);
+               old_dssdev->driver->disconnect(old_dssdev);
+       }
+
+       if (dssdev) {
+               r = dssdev->driver->connect(dssdev);
                if (r) {
-                       DSSERR("failed to set manager output\n");
+                       DSSERR("failed to connect new device\n");
+                       goto put_device;
+               }
+
+               old_dssdev = mgr->get_device(mgr);
+               if (old_dssdev != dssdev) {
+                       DSSERR("failed to connect device to this manager\n");
+                       dssdev->driver->disconnect(dssdev);
                        goto put_device;
                }
 
@@ -509,4 +522,6 @@ void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr)
 {
        kobject_del(&mgr->kobj);
        kobject_put(&mgr->kobj);
+
+       memset(&mgr->kobj, 0, sizeof(mgr->kobj));
 }
index 2551eaa..1aac9b4 100644 (file)
@@ -36,9 +36,9 @@
 static int num_managers;
 static struct omap_overlay_manager *managers;
 
-int dss_init_overlay_managers(struct platform_device *pdev)
+int dss_init_overlay_managers(void)
 {
-       int i, r;
+       int i;
 
        num_managers = dss_feat_get_num_mgrs();
 
@@ -76,6 +76,17 @@ int dss_init_overlay_managers(struct platform_device *pdev)
                        dss_feat_get_supported_outputs(mgr->id);
 
                INIT_LIST_HEAD(&mgr->overlays);
+       }
+
+       return 0;
+}
+
+int dss_init_overlay_managers_sysfs(struct platform_device *pdev)
+{
+       int i, r;
+
+       for (i = 0; i < num_managers; ++i) {
+               struct omap_overlay_manager *mgr = &managers[i];
 
                r = dss_manager_kobj_init(mgr, pdev);
                if (r)
@@ -85,18 +96,22 @@ int dss_init_overlay_managers(struct platform_device *pdev)
        return 0;
 }
 
-void dss_uninit_overlay_managers(struct platform_device *pdev)
+void dss_uninit_overlay_managers(void)
+{
+       kfree(managers);
+       managers = NULL;
+       num_managers = 0;
+}
+
+void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev)
 {
        int i;
 
        for (i = 0; i < num_managers; ++i) {
                struct omap_overlay_manager *mgr = &managers[i];
+
                dss_manager_kobj_uninit(mgr);
        }
-
-       kfree(managers);
-       managers = NULL;
-       num_managers = 0;
 }
 
 int omap_dss_get_num_overlay_managers(void)
index 5214df6..3f5c0a7 100644 (file)
@@ -27,7 +27,7 @@
 static LIST_HEAD(output_list);
 static DEFINE_MUTEX(output_lock);
 
-int omapdss_output_set_device(struct omap_dss_output *out,
+int omapdss_output_set_device(struct omap_dss_device *out,
                struct omap_dss_device *dssdev)
 {
        int r;
@@ -41,7 +41,7 @@ int omapdss_output_set_device(struct omap_dss_output *out,
                goto err;
        }
 
-       if (out->type != dssdev->type) {
+       if (out->output_type != dssdev->type) {
                DSSERR("output type and display type don't match\n");
                r = -EINVAL;
                goto err;
@@ -60,7 +60,7 @@ err:
 }
 EXPORT_SYMBOL(omapdss_output_set_device);
 
-int omapdss_output_unset_device(struct omap_dss_output *out)
+int omapdss_output_unset_device(struct omap_dss_device *out)
 {
        int r;
 
@@ -92,19 +92,22 @@ err:
 }
 EXPORT_SYMBOL(omapdss_output_unset_device);
 
-void dss_register_output(struct omap_dss_output *out)
+int omapdss_register_output(struct omap_dss_device *out)
 {
        list_add_tail(&out->list, &output_list);
+       return 0;
 }
+EXPORT_SYMBOL(omapdss_register_output);
 
-void dss_unregister_output(struct omap_dss_output *out)
+void omapdss_unregister_output(struct omap_dss_device *out)
 {
        list_del(&out->list);
 }
+EXPORT_SYMBOL(omapdss_unregister_output);
 
-struct omap_dss_output *omap_dss_get_output(enum omap_dss_output_id id)
+struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id)
 {
-       struct omap_dss_output *out;
+       struct omap_dss_device *out;
 
        list_for_each_entry(out, &output_list, list) {
                if (out->id == id)
@@ -115,6 +118,62 @@ struct omap_dss_output *omap_dss_get_output(enum omap_dss_output_id id)
 }
 EXPORT_SYMBOL(omap_dss_get_output);
 
+struct omap_dss_device *omap_dss_find_output(const char *name)
+{
+       struct omap_dss_device *out;
+
+       list_for_each_entry(out, &output_list, list) {
+               if (strcmp(out->name, name) == 0)
+                       return omap_dss_get_device(out);
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL(omap_dss_find_output);
+
+struct omap_dss_device *omap_dss_find_output_by_node(struct device_node *node)
+{
+       struct omap_dss_device *out;
+
+       list_for_each_entry(out, &output_list, list) {
+               if (out->dev->of_node == node)
+                       return omap_dss_get_device(out);
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL(omap_dss_find_output_by_node);
+
+struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev)
+{
+       while (dssdev->output)
+               dssdev = dssdev->output;
+
+       if (dssdev->id != 0)
+               return omap_dss_get_device(dssdev);
+
+       return NULL;
+}
+EXPORT_SYMBOL(omapdss_find_output_from_display);
+
+struct omap_overlay_manager *omapdss_find_mgr_from_display(struct omap_dss_device *dssdev)
+{
+       struct omap_dss_device *out;
+       struct omap_overlay_manager *mgr;
+
+       out = omapdss_find_output_from_display(dssdev);
+
+       if (out == NULL)
+               return NULL;
+
+       mgr = out->manager;
+
+       omap_dss_put_device(out);
+
+       return mgr;
+}
+EXPORT_SYMBOL(omapdss_find_mgr_from_display);
+
 static const struct dss_mgr_ops *dss_mgr_ops;
 
 int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops)
@@ -134,6 +193,20 @@ void dss_uninstall_mgr_ops(void)
 }
 EXPORT_SYMBOL(dss_uninstall_mgr_ops);
 
+int dss_mgr_connect(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dst)
+{
+       return dss_mgr_ops->connect(mgr, dst);
+}
+EXPORT_SYMBOL(dss_mgr_connect);
+
+void dss_mgr_disconnect(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dst)
+{
+       dss_mgr_ops->disconnect(mgr, dst);
+}
+EXPORT_SYMBOL(dss_mgr_disconnect);
+
 void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
                const struct omap_video_timings *timings)
 {
index 1a17dd1..fdfe6e6 100644 (file)
@@ -117,7 +117,7 @@ static struct {
        int data_lines;
        struct rfbi_timings intf_timings;
 
-       struct omap_dss_output output;
+       struct omap_dss_device output;
 } rfbi;
 
 static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val)
@@ -312,7 +312,7 @@ static int rfbi_transfer_area(struct omap_dss_device *dssdev,
 {
        u32 l;
        int r;
-       struct omap_overlay_manager *mgr = dssdev->output->manager;
+       struct omap_overlay_manager *mgr = rfbi.output.manager;
        u16 width = rfbi.timings.x_res;
        u16 height = rfbi.timings.y_res;
 
@@ -852,7 +852,7 @@ static void rfbi_dump_regs(struct seq_file *s)
 
 static void rfbi_config_lcd_manager(struct omap_dss_device *dssdev)
 {
-       struct omap_overlay_manager *mgr = dssdev->output->manager;
+       struct omap_overlay_manager *mgr = rfbi.output.manager;
        struct dss_lcd_mgr_config mgr_config;
 
        mgr_config.io_pad_mode = DSS_IO_PAD_MODE_RFBI;
@@ -890,7 +890,7 @@ static void rfbi_config_lcd_manager(struct omap_dss_device *dssdev)
 
 int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
 {
-       struct omap_dss_output *out = dssdev->output;
+       struct omap_dss_device *out = &rfbi.output;
        int r;
 
        if (out == NULL || out->manager == NULL) {
@@ -902,12 +902,6 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
        if (r)
                return r;
 
-       r = omap_dss_start_device(dssdev);
-       if (r) {
-               DSSERR("failed to start device\n");
-               goto err0;
-       }
-
        r = dss_mgr_register_framedone_handler(out->manager,
                        framedone_callback, NULL);
        if (r) {
@@ -924,8 +918,6 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
 
        return 0;
 err1:
-       omap_dss_stop_device(dssdev);
-err0:
        rfbi_runtime_put();
        return r;
 }
@@ -933,11 +925,10 @@ EXPORT_SYMBOL(omapdss_rfbi_display_enable);
 
 void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev)
 {
-       struct omap_dss_output *out = dssdev->output;
+       struct omap_dss_device *out = &rfbi.output;
 
        dss_mgr_unregister_framedone_handler(out->manager,
                        framedone_callback, NULL);
-       omap_dss_stop_device(dssdev);
 
        rfbi_runtime_put();
 }
@@ -1022,22 +1013,23 @@ static int rfbi_probe_pdata(struct platform_device *rfbidev)
 
 static void rfbi_init_output(struct platform_device *pdev)
 {
-       struct omap_dss_output *out = &rfbi.output;
+       struct omap_dss_device *out = &rfbi.output;
 
-       out->pdev = pdev;
+       out->dev = &pdev->dev;
        out->id = OMAP_DSS_OUTPUT_DBI;
-       out->type = OMAP_DISPLAY_TYPE_DBI;
+       out->output_type = OMAP_DISPLAY_TYPE_DBI;
        out->name = "rfbi.0";
        out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
+       out->owner = THIS_MODULE;
 
-       dss_register_output(out);
+       omapdss_register_output(out);
 }
 
 static void __exit rfbi_uninit_output(struct platform_device *pdev)
 {
-       struct omap_dss_output *out = &rfbi.output;
+       struct omap_dss_device *out = &rfbi.output;
 
-       dss_unregister_output(out);
+       omapdss_unregister_output(out);
 }
 
 /* RFBI HW IP initialisation */
@@ -1093,15 +1085,16 @@ static int omap_rfbihw_probe(struct platform_device *pdev)
 
        rfbi_init_output(pdev);
 
-       r = rfbi_probe_pdata(pdev);
-       if (r) {
-               rfbi_uninit_output(pdev);
-               pm_runtime_disable(&pdev->dev);
-               return r;
+       if (pdev->dev.platform_data) {
+               r = rfbi_probe_pdata(pdev);
+               if (r)
+                       goto err_probe;
        }
 
        return 0;
 
+err_probe:
+       rfbi_uninit_output(pdev);
 err_runtime_get:
        pm_runtime_disable(&pdev->dev);
        return r;
index 0bcd302..856af2e 100644 (file)
@@ -31,6 +31,8 @@
 #include "dss.h"
 
 static struct {
+       struct platform_device *pdev;
+
        bool update_enabled;
        struct regulator *vdds_sdi_reg;
 
@@ -38,7 +40,7 @@ static struct {
        struct omap_video_timings timings;
        int datapairs;
 
-       struct omap_dss_output output;
+       struct omap_dss_device output;
 } sdi;
 
 struct sdi_clk_calc_ctx {
@@ -109,7 +111,7 @@ static int sdi_calc_clock_div(unsigned long pclk,
 
 static void sdi_config_lcd_manager(struct omap_dss_device *dssdev)
 {
-       struct omap_overlay_manager *mgr = dssdev->output->manager;
+       struct omap_overlay_manager *mgr = sdi.output.manager;
 
        sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
 
@@ -124,7 +126,7 @@ static void sdi_config_lcd_manager(struct omap_dss_device *dssdev)
 
 int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
 {
-       struct omap_dss_output *out = dssdev->output;
+       struct omap_dss_device *out = &sdi.output;
        struct omap_video_timings *t = &sdi.timings;
        struct dss_clock_info dss_cinfo;
        struct dispc_clock_info dispc_cinfo;
@@ -136,12 +138,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
                return -ENODEV;
        }
 
-       r = omap_dss_start_device(dssdev);
-       if (r) {
-               DSSERR("failed to start device\n");
-               goto err_start_dev;
-       }
-
        r = regulator_enable(sdi.vdds_sdi_reg);
        if (r)
                goto err_reg_enable;
@@ -213,15 +209,13 @@ err_calc_clock_div:
 err_get_dispc:
        regulator_disable(sdi.vdds_sdi_reg);
 err_reg_enable:
-       omap_dss_stop_device(dssdev);
-err_start_dev:
        return r;
 }
 EXPORT_SYMBOL(omapdss_sdi_display_enable);
 
 void omapdss_sdi_display_disable(struct omap_dss_device *dssdev)
 {
-       struct omap_overlay_manager *mgr = dssdev->output->manager;
+       struct omap_overlay_manager *mgr = sdi.output.manager;
 
        dss_mgr_disable(mgr);
 
@@ -230,8 +224,6 @@ void omapdss_sdi_display_disable(struct omap_dss_device *dssdev)
        dispc_runtime_put();
 
        regulator_disable(sdi.vdds_sdi_reg);
-
-       omap_dss_stop_device(dssdev);
 }
 EXPORT_SYMBOL(omapdss_sdi_display_disable);
 
@@ -242,29 +234,51 @@ void omapdss_sdi_set_timings(struct omap_dss_device *dssdev,
 }
 EXPORT_SYMBOL(omapdss_sdi_set_timings);
 
+static void sdi_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       *timings = sdi.timings;
+}
+
+static int sdi_check_timings(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings)
+{
+       struct omap_overlay_manager *mgr = sdi.output.manager;
+
+       if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
+               return -EINVAL;
+
+       if (timings->pixel_clock == 0)
+               return -EINVAL;
+
+       return 0;
+}
+
 void omapdss_sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs)
 {
        sdi.datapairs = datapairs;
 }
 EXPORT_SYMBOL(omapdss_sdi_set_datapairs);
 
-static int sdi_init_display(struct omap_dss_device *dssdev)
+static int sdi_init_regulator(void)
 {
-       DSSDBG("SDI init\n");
+       struct regulator *vdds_sdi;
 
-       if (sdi.vdds_sdi_reg == NULL) {
-               struct regulator *vdds_sdi;
+       if (sdi.vdds_sdi_reg)
+               return 0;
 
-               vdds_sdi = dss_get_vdds_sdi();
+       vdds_sdi = dss_get_vdds_sdi();
 
+       if (IS_ERR(vdds_sdi)) {
+               vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi");
                if (IS_ERR(vdds_sdi)) {
                        DSSERR("can't get VDDS_SDI regulator\n");
                        return PTR_ERR(vdds_sdi);
                }
-
-               sdi.vdds_sdi_reg = vdds_sdi;
        }
 
+       sdi.vdds_sdi_reg = vdds_sdi;
+
        return 0;
 }
 
@@ -313,7 +327,7 @@ static int sdi_probe_pdata(struct platform_device *sdidev)
 
        dss_copy_device_pdata(dssdev, plat_dssdev);
 
-       r = sdi_init_display(dssdev);
+       r = sdi_init_regulator();
        if (r) {
                DSSERR("device %s init failed: %d\n", dssdev->name, r);
                dss_put_device(dssdev);
@@ -339,39 +353,104 @@ static int sdi_probe_pdata(struct platform_device *sdidev)
        return 0;
 }
 
+static int sdi_connect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       struct omap_overlay_manager *mgr;
+       int r;
+
+       r = sdi_init_regulator();
+       if (r)
+               return r;
+
+       mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
+       if (!mgr)
+               return -ENODEV;
+
+       r = dss_mgr_connect(mgr, dssdev);
+       if (r)
+               return r;
+
+       r = omapdss_output_set_device(dssdev, dst);
+       if (r) {
+               DSSERR("failed to connect output to new device: %s\n",
+                               dst->name);
+               dss_mgr_disconnect(mgr, dssdev);
+               return r;
+       }
+
+       return 0;
+}
+
+static void sdi_disconnect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       WARN_ON(dst != dssdev->device);
+
+       if (dst != dssdev->device)
+               return;
+
+       omapdss_output_unset_device(dssdev);
+
+       if (dssdev->manager)
+               dss_mgr_disconnect(dssdev->manager, dssdev);
+}
+
+static const struct omapdss_sdi_ops sdi_ops = {
+       .connect = sdi_connect,
+       .disconnect = sdi_disconnect,
+
+       .enable = omapdss_sdi_display_enable,
+       .disable = omapdss_sdi_display_disable,
+
+       .check_timings = sdi_check_timings,
+       .set_timings = omapdss_sdi_set_timings,
+       .get_timings = sdi_get_timings,
+
+       .set_datapairs = omapdss_sdi_set_datapairs,
+};
+
 static void sdi_init_output(struct platform_device *pdev)
 {
-       struct omap_dss_output *out = &sdi.output;
+       struct omap_dss_device *out = &sdi.output;
 
-       out->pdev = pdev;
+       out->dev = &pdev->dev;
        out->id = OMAP_DSS_OUTPUT_SDI;
-       out->type = OMAP_DISPLAY_TYPE_SDI;
+       out->output_type = OMAP_DISPLAY_TYPE_SDI;
        out->name = "sdi.0";
        out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
+       out->ops.sdi = &sdi_ops;
+       out->owner = THIS_MODULE;
 
-       dss_register_output(out);
+       omapdss_register_output(out);
 }
 
 static void __exit sdi_uninit_output(struct platform_device *pdev)
 {
-       struct omap_dss_output *out = &sdi.output;
+       struct omap_dss_device *out = &sdi.output;
 
-       dss_unregister_output(out);
+       omapdss_unregister_output(out);
 }
 
 static int omap_sdi_probe(struct platform_device *pdev)
 {
        int r;
 
+       sdi.pdev = pdev;
+
        sdi_init_output(pdev);
 
-       r = sdi_probe_pdata(pdev);
-       if (r) {
-               sdi_uninit_output(pdev);
-               return r;
+       if (pdev->dev.platform_data) {
+               r = sdi_probe_pdata(pdev);
+               if (r)
+                       goto err_probe;
        }
 
        return 0;
+
+err_probe:
+       sdi_uninit_output(pdev);
+       return r;
 }
 
 static int __exit omap_sdi_remove(struct platform_device *pdev)
index 216aa70..45215f4 100644 (file)
@@ -73,8 +73,6 @@ struct ti_hdmi_ip_ops {
 
        int (*read_edid)(struct hdmi_ip_data *ip_data, u8 *edid, int len);
 
-       bool (*detect)(struct hdmi_ip_data *ip_data);
-
        int (*pll_enable)(struct hdmi_ip_data *ip_data);
 
        void (*pll_disable)(struct hdmi_ip_data *ip_data);
@@ -155,19 +153,18 @@ struct hdmi_ip_data {
        unsigned long   core_av_offset;
        unsigned long   pll_offset;
        unsigned long   phy_offset;
+       int             irq;
        const struct ti_hdmi_ip_ops *ops;
        struct hdmi_config cfg;
        struct hdmi_pll_info pll_data;
        struct hdmi_core_infoframe_avi avi_cfg;
 
        /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */
-       int hpd_gpio;
        struct mutex lock;
 };
 int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data);
 void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data);
 int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len);
-bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data);
 int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data);
 void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data);
 int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data);
index e18b222..e242ed8 100644 (file)
@@ -28,7 +28,6 @@
 #include <linux/delay.h>
 #include <linux/string.h>
 #include <linux/seq_file.h>
-#include <linux/gpio.h>
 #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
 #include <sound/asound.h>
 #include <sound/asoundef.h>
@@ -38,6 +37,9 @@
 #include "dss.h"
 #include "dss_features.h"
 
+#define HDMI_IRQ_LINK_CONNECT          (1 << 25)
+#define HDMI_IRQ_LINK_DISCONNECT       (1 << 26)
+
 static inline void hdmi_write_reg(void __iomem *base_addr,
                                const u16 idx, u32 val)
 {
@@ -233,37 +235,39 @@ void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data)
        hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF);
 }
 
-static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data)
+static irqreturn_t hdmi_irq_handler(int irq, void *data)
 {
-       bool hpd;
-       int r;
-
-       mutex_lock(&ip_data->lock);
-
-       hpd = gpio_get_value(ip_data->hpd_gpio);
+       struct hdmi_ip_data *ip_data = data;
+       void __iomem *wp_base = hdmi_wp_base(ip_data);
+       u32 irqstatus;
+
+       irqstatus = hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
+       hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS, irqstatus);
+       /* flush posted write */
+       hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
+
+       if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
+                       irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
+               /*
+                * If we get both connect and disconnect interrupts at the same
+                * time, turn off the PHY, clear interrupts, and restart, which
+                * raises connect interrupt if a cable is connected, or nothing
+                * if cable is not connected.
+                */
+               hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
 
-       if (hpd)
-               r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
-       else
-               r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
+               hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS,
+                       HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
+               /* flush posted write */
+               hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
 
-       if (r) {
-               DSSERR("Failed to %s PHY TX power\n",
-                               hpd ? "enable" : "disable");
-               goto err;
+               hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
+       } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
+               hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
+       } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
+               hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
        }
 
-err:
-       mutex_unlock(&ip_data->lock);
-       return r;
-}
-
-static irqreturn_t hpd_irq_handler(int irq, void *data)
-{
-       struct hdmi_ip_data *ip_data = data;
-
-       hdmi_check_hpd_state(ip_data);
-
        return IRQ_HANDLED;
 }
 
@@ -272,6 +276,12 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
        u16 r = 0;
        void __iomem *phy_base = hdmi_phy_base(ip_data);
 
+       hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_CLR,
+                       0xffffffff);
+
+       hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQSTATUS,
+                       HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
+
        r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
        if (r)
                return r;
@@ -297,29 +307,23 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
        /* Write to phy address 3 to change the polarity control */
        REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
 
-       r = request_threaded_irq(gpio_to_irq(ip_data->hpd_gpio),
-                                NULL, hpd_irq_handler,
-                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
-                                IRQF_ONESHOT, "hpd", ip_data);
+       r = request_threaded_irq(ip_data->irq, NULL, hdmi_irq_handler,
+                                IRQF_ONESHOT, "OMAP HDMI", ip_data);
        if (r) {
-               DSSERR("HPD IRQ request failed\n");
+               DSSERR("HDMI IRQ request failed\n");
                hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
                return r;
        }
 
-       r = hdmi_check_hpd_state(ip_data);
-       if (r) {
-               free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data);
-               hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
-               return r;
-       }
+       hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_SET,
+                       HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
 
        return 0;
 }
 
 void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data)
 {
-       free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data);
+       free_irq(ip_data->irq, ip_data);
 
        hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
 }
@@ -476,11 +480,6 @@ int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data,
        return l;
 }
 
-bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data)
-{
-       return gpio_get_value(ip_data->hpd_gpio);
-}
-
 static void hdmi_core_init(struct hdmi_core_video_config *video_cfg,
                        struct hdmi_core_infoframe_avi *avi_cfg,
                        struct hdmi_core_packet_enable_repeat *repeat_cfg)
index 8366ae1..6ef2f92 100644 (file)
@@ -33,6 +33,7 @@
 #define HDMI_WP_IRQSTATUS                      0x28
 #define HDMI_WP_PWR_CTRL                       0x40
 #define HDMI_WP_IRQENABLE_SET                  0x2C
+#define HDMI_WP_IRQENABLE_CLR                  0x30
 #define HDMI_WP_VIDEO_CFG                      0x50
 #define HDMI_WP_VIDEO_SIZE                     0x60
 #define HDMI_WP_VIDEO_TIMING_H                 0x68
index 74fdb3e..496a106 100644 (file)
@@ -304,7 +304,7 @@ static struct {
        enum omap_dss_venc_type type;
        bool invert_polarity;
 
-       struct omap_dss_output output;
+       struct omap_dss_device output;
 } venc;
 
 static inline void venc_write_reg(int idx, u32 val)
@@ -429,7 +429,7 @@ static const struct venc_config *venc_timings_to_config(
 
 static int venc_power_on(struct omap_dss_device *dssdev)
 {
-       struct omap_overlay_manager *mgr = dssdev->output->manager;
+       struct omap_overlay_manager *mgr = venc.output.manager;
        u32 l;
        int r;
 
@@ -480,7 +480,7 @@ err0:
 
 static void venc_power_off(struct omap_dss_device *dssdev)
 {
-       struct omap_overlay_manager *mgr = dssdev->output->manager;
+       struct omap_overlay_manager *mgr = venc.output.manager;
 
        venc_write_reg(VENC_OUTPUT_CONTROL, 0);
        dss_set_dac_pwrdn_bgz(0);
@@ -492,15 +492,9 @@ static void venc_power_off(struct omap_dss_device *dssdev)
        venc_runtime_put();
 }
 
-unsigned long venc_get_pixel_clock(void)
-{
-       /* VENC Pixel Clock in Mhz */
-       return 13500000;
-}
-
 int omapdss_venc_display_enable(struct omap_dss_device *dssdev)
 {
-       struct omap_dss_output *out = dssdev->output;
+       struct omap_dss_device *out = &venc.output;
        int r;
 
        DSSDBG("venc_display_enable\n");
@@ -513,23 +507,15 @@ int omapdss_venc_display_enable(struct omap_dss_device *dssdev)
                goto err0;
        }
 
-       r = omap_dss_start_device(dssdev);
-       if (r) {
-               DSSERR("failed to start device\n");
-               goto err0;
-       }
-
        r = venc_power_on(dssdev);
        if (r)
-               goto err1;
+               goto err0;
 
        venc.wss_data = 0;
 
        mutex_unlock(&venc.venc_lock);
 
        return 0;
-err1:
-       omap_dss_stop_device(dssdev);
 err0:
        mutex_unlock(&venc.venc_lock);
        return r;
@@ -543,8 +529,6 @@ void omapdss_venc_display_disable(struct omap_dss_device *dssdev)
 
        venc_power_off(dssdev);
 
-       omap_dss_stop_device(dssdev);
-
        mutex_unlock(&venc.venc_lock);
 }
 
@@ -561,6 +545,8 @@ void omapdss_venc_set_timings(struct omap_dss_device *dssdev,
 
        venc.timings = *timings;
 
+       dispc_set_tv_pclk(13500000);
+
        mutex_unlock(&venc.venc_lock);
 }
 
@@ -578,6 +564,16 @@ int omapdss_venc_check_timings(struct omap_dss_device *dssdev,
        return -EINVAL;
 }
 
+static void venc_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       mutex_lock(&venc.venc_lock);
+
+       *timings = venc.timings;
+
+       mutex_unlock(&venc.venc_lock);
+}
+
 u32 omapdss_venc_get_wss(struct omap_dss_device *dssdev)
 {
        /* Invert due to VENC_L21_WC_CTL:INV=1 */
@@ -633,23 +629,22 @@ void omapdss_venc_invert_vid_out_polarity(struct omap_dss_device *dssdev,
        mutex_unlock(&venc.venc_lock);
 }
 
-static int venc_init_display(struct omap_dss_device *dssdev)
+static int venc_init_regulator(void)
 {
-       DSSDBG("init_display\n");
-
-       if (venc.vdda_dac_reg == NULL) {
-               struct regulator *vdda_dac;
+       struct regulator *vdda_dac;
 
-               vdda_dac = regulator_get(&venc.pdev->dev, "vdda_dac");
+       if (venc.vdda_dac_reg != NULL)
+               return 0;
 
-               if (IS_ERR(vdda_dac)) {
-                       DSSERR("can't get VDDA_DAC regulator\n");
-                       return PTR_ERR(vdda_dac);
-               }
+       vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda_dac");
 
-               venc.vdda_dac_reg = vdda_dac;
+       if (IS_ERR(vdda_dac)) {
+               DSSERR("can't get VDDA_DAC regulator\n");
+               return PTR_ERR(vdda_dac);
        }
 
+       venc.vdda_dac_reg = vdda_dac;
+
        return 0;
 }
 
@@ -765,19 +760,16 @@ static int venc_probe_pdata(struct platform_device *vencdev)
        if (!plat_dssdev)
                return 0;
 
+       r = venc_init_regulator();
+       if (r)
+               return r;
+
        dssdev = dss_alloc_and_init_device(&vencdev->dev);
        if (!dssdev)
                return -ENOMEM;
 
        dss_copy_device_pdata(dssdev, plat_dssdev);
 
-       r = venc_init_display(dssdev);
-       if (r) {
-               DSSERR("device %s init failed: %d\n", dssdev->name, r);
-               dss_put_device(dssdev);
-               return r;
-       }
-
        r = omapdss_output_set_device(&venc.output, dssdev);
        if (r) {
                DSSERR("failed to connect output to new device: %s\n",
@@ -797,24 +789,87 @@ static int venc_probe_pdata(struct platform_device *vencdev)
        return 0;
 }
 
+static int venc_connect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       struct omap_overlay_manager *mgr;
+       int r;
+
+       r = venc_init_regulator();
+       if (r)
+               return r;
+
+       mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
+       if (!mgr)
+               return -ENODEV;
+
+       r = dss_mgr_connect(mgr, dssdev);
+       if (r)
+               return r;
+
+       r = omapdss_output_set_device(dssdev, dst);
+       if (r) {
+               DSSERR("failed to connect output to new device: %s\n",
+                               dst->name);
+               dss_mgr_disconnect(mgr, dssdev);
+               return r;
+       }
+
+       return 0;
+}
+
+static void venc_disconnect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       WARN_ON(dst != dssdev->device);
+
+       if (dst != dssdev->device)
+               return;
+
+       omapdss_output_unset_device(dssdev);
+
+       if (dssdev->manager)
+               dss_mgr_disconnect(dssdev->manager, dssdev);
+}
+
+static const struct omapdss_atv_ops venc_ops = {
+       .connect = venc_connect,
+       .disconnect = venc_disconnect,
+
+       .enable = omapdss_venc_display_enable,
+       .disable = omapdss_venc_display_disable,
+
+       .check_timings = omapdss_venc_check_timings,
+       .set_timings = omapdss_venc_set_timings,
+       .get_timings = venc_get_timings,
+
+       .set_type = omapdss_venc_set_type,
+       .invert_vid_out_polarity = omapdss_venc_invert_vid_out_polarity,
+
+       .set_wss = omapdss_venc_set_wss,
+       .get_wss = omapdss_venc_get_wss,
+};
+
 static void venc_init_output(struct platform_device *pdev)
 {
-       struct omap_dss_output *out = &venc.output;
+       struct omap_dss_device *out = &venc.output;
 
-       out->pdev = pdev;
+       out->dev = &pdev->dev;
        out->id = OMAP_DSS_OUTPUT_VENC;
-       out->type = OMAP_DISPLAY_TYPE_VENC;
+       out->output_type = OMAP_DISPLAY_TYPE_VENC;
        out->name = "venc.0";
        out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
+       out->ops.atv = &venc_ops;
+       out->owner = THIS_MODULE;
 
-       dss_register_output(out);
+       omapdss_register_output(out);
 }
 
 static void __exit venc_uninit_output(struct platform_device *pdev)
 {
-       struct omap_dss_output *out = &venc.output;
+       struct omap_dss_device *out = &venc.output;
 
-       dss_unregister_output(out);
+       omapdss_unregister_output(out);
 }
 
 /* VENC HW IP initialisation */
@@ -866,16 +921,17 @@ static int omap_venchw_probe(struct platform_device *pdev)
 
        venc_init_output(pdev);
 
-       r = venc_probe_pdata(pdev);
-       if (r) {
-               venc_panel_exit();
-               venc_uninit_output(pdev);
-               pm_runtime_disable(&pdev->dev);
-               return r;
+       if (pdev->dev.platform_data) {
+               r = venc_probe_pdata(pdev);
+               if (r)
+                       goto err_probe;
        }
 
        return 0;
 
+err_probe:
+       venc_panel_exit();
+       venc_uninit_output(pdev);
 err_panel_init:
 err_runtime_get:
        pm_runtime_disable(&pdev->dev);
@@ -886,11 +942,6 @@ static int __exit omap_venchw_remove(struct platform_device *pdev)
 {
        dss_unregister_child_devices(&pdev->dev);
 
-       if (venc.vdda_dac_reg != NULL) {
-               regulator_put(venc.vdda_dac_reg);
-               venc.vdda_dac_reg = NULL;
-       }
-
        venc_panel_exit();
 
        venc_uninit_output(pdev);
index 0d2b1a0..f7d92c5 100644 (file)
@@ -107,19 +107,19 @@ static int venc_panel_probe(struct omap_dss_device *dssdev)
 
        dssdev->panel.timings = default_timings;
 
-       return device_create_file(&dssdev->dev, &dev_attr_output_type);
+       return device_create_file(dssdev->dev, &dev_attr_output_type);
 }
 
 static void venc_panel_remove(struct omap_dss_device *dssdev)
 {
-       device_remove_file(&dssdev->dev, &dev_attr_output_type);
+       device_remove_file(dssdev->dev, &dev_attr_output_type);
 }
 
 static int venc_panel_enable(struct omap_dss_device *dssdev)
 {
        int r;
 
-       dev_dbg(&dssdev->dev, "venc_panel_enable\n");
+       dev_dbg(dssdev->dev, "venc_panel_enable\n");
 
        mutex_lock(&venc_panel.lock);
 
@@ -150,7 +150,7 @@ err:
 
 static void venc_panel_disable(struct omap_dss_device *dssdev)
 {
-       dev_dbg(&dssdev->dev, "venc_panel_disable\n");
+       dev_dbg(dssdev->dev, "venc_panel_disable\n");
 
        mutex_lock(&venc_panel.lock);
 
@@ -167,7 +167,7 @@ end:
 static void venc_panel_set_timings(struct omap_dss_device *dssdev,
                struct omap_video_timings *timings)
 {
-       dev_dbg(&dssdev->dev, "venc_panel_set_timings\n");
+       dev_dbg(dssdev->dev, "venc_panel_set_timings\n");
 
        mutex_lock(&venc_panel.lock);
 
@@ -180,21 +180,21 @@ static void venc_panel_set_timings(struct omap_dss_device *dssdev,
 static int venc_panel_check_timings(struct omap_dss_device *dssdev,
                struct omap_video_timings *timings)
 {
-       dev_dbg(&dssdev->dev, "venc_panel_check_timings\n");
+       dev_dbg(dssdev->dev, "venc_panel_check_timings\n");
 
        return omapdss_venc_check_timings(dssdev, timings);
 }
 
 static u32 venc_panel_get_wss(struct omap_dss_device *dssdev)
 {
-       dev_dbg(&dssdev->dev, "venc_panel_get_wss\n");
+       dev_dbg(dssdev->dev, "venc_panel_get_wss\n");
 
        return omapdss_venc_get_wss(dssdev);
 }
 
 static int venc_panel_set_wss(struct omap_dss_device *dssdev, u32 wss)
 {
-       dev_dbg(&dssdev->dev, "venc_panel_set_wss\n");
+       dev_dbg(dssdev->dev, "venc_panel_set_wss\n");
 
        return omapdss_venc_set_wss(dssdev, wss);
 }
index d30b45d..146b6f5 100644 (file)
@@ -770,12 +770,17 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
 
        case OMAPFB_WAITFORVSYNC:
                DBG("ioctl WAITFORVSYNC\n");
-               if (!display || !display->output || !display->output->manager) {
+
+               if (!display) {
                        r = -EINVAL;
                        break;
                }
 
-               mgr = display->output->manager;
+               mgr = omapdss_find_mgr_from_display(display);
+               if (!mgr) {
+                       r = -EINVAL;
+                       break;
+               }
 
                r = mgr->wait_for_vsync(mgr);
                break;
index 856917b..27d6905 100644 (file)
@@ -1853,6 +1853,8 @@ static void omapfb_free_resources(struct omapfb2_device *fbdev)
                if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
                        dssdev->driver->disable(dssdev);
 
+               dssdev->driver->disconnect(dssdev);
+
                omap_dss_put_device(dssdev);
        }
 
@@ -2363,27 +2365,26 @@ static int omapfb_init_connections(struct omapfb2_device *fbdev,
        int i, r;
        struct omap_overlay_manager *mgr;
 
-       if (!def_dssdev->output) {
-               dev_err(fbdev->dev, "no output for the default display\n");
-               return -EINVAL;
+       r = def_dssdev->driver->connect(def_dssdev);
+       if (r) {
+               dev_err(fbdev->dev, "failed to connect default display\n");
+               return r;
        }
 
        for (i = 0; i < fbdev->num_displays; ++i) {
                struct omap_dss_device *dssdev = fbdev->displays[i].dssdev;
-               struct omap_dss_output *out = dssdev->output;
-
-               mgr = omap_dss_get_overlay_manager(out->dispc_channel);
 
-               if (!mgr || !out)
+               if (dssdev == def_dssdev)
                        continue;
 
-               if (mgr->output)
-                       mgr->unset_output(mgr);
-
-               mgr->set_output(mgr, out);
+               /*
+                * We don't care if the connect succeeds or not. We just want to
+                * connect as many displays as possible.
+                */
+               dssdev->driver->connect(dssdev);
        }
 
-       mgr = def_dssdev->output->manager;
+       mgr = omapdss_find_mgr_from_display(def_dssdev);
 
        if (!mgr) {
                dev_err(fbdev->dev, "no ovl manager for the default display\n");
@@ -2502,7 +2503,7 @@ static int omapfb_probe(struct platform_device *pdev)
 
        if (def_display == NULL) {
                dev_err(fbdev->dev, "failed to find default display\n");
-               r = -EINVAL;
+               r = -EPROBE_DEFER;
                goto cleanup;
        }
 
index 7cf0b13..ad382b3 100644 (file)
@@ -710,7 +710,6 @@ err_misc_deregister:
        misc_deregister(&priv->misc_dev);
 
 err_free_priv:
-       platform_set_drvdata(dev, NULL);
        free_buffers(dev, priv);
        kfree(priv);
        return ret;
@@ -728,7 +727,6 @@ static int pxa3xx_gcu_remove(struct platform_device *dev)
                        priv->shared, priv->shared_phys);
        iounmap(priv->mmio_base);
        release_mem_region(r->start, resource_size(r));
-       platform_set_drvdata(dev, NULL);
        clk_disable(priv->clk);
        free_buffers(dev, priv);
        kfree(priv);
index 580f80c..eca2de4 100644 (file)
@@ -2256,7 +2256,6 @@ failed_free_res:
        release_mem_region(r->start, resource_size(r));
 failed_fbi:
        clk_put(fbi->clk);
-       platform_set_drvdata(dev, NULL);
        kfree(fbi);
 failed:
        return ret;
index 76a0e7f..21a32ad 100644 (file)
@@ -1005,7 +1005,6 @@ release_regs:
 release_mem:
        release_mem_region(res->start, size);
 dealloc_fb:
-       platform_set_drvdata(pdev, NULL);
        framebuffer_release(fbinfo);
        return ret;
 }
@@ -1051,7 +1050,6 @@ static int s3c2410fb_remove(struct platform_device *pdev)
 
        release_mem_region(info->mem->start, resource_size(info->mem));
 
-       platform_set_drvdata(pdev, NULL);
        framebuffer_release(fbinfo);
 
        return 0;
index f34c858..de76da0 100644 (file)
@@ -1271,7 +1271,6 @@ static int sa1100fb_probe(struct platform_device *pdev)
  failed:
        if (fbi)
                iounmap(fbi->base);
-       platform_set_drvdata(pdev, NULL);
        kfree(fbi);
        release_mem_region(res->start, resource_size(res));
        return ret;
index 5fbb0c7..a8c6c43 100644 (file)
@@ -571,7 +571,6 @@ static int sh7760fb_remove(struct platform_device *dev)
        iounmap(par->base);
        release_mem_region(par->ioarea->start, resource_size(par->ioarea));
        framebuffer_release(info);
-       platform_set_drvdata(dev, NULL);
 
        return 0;
 }
index 6cad530..8f6e8ff 100644 (file)
@@ -567,7 +567,6 @@ static int sh_mipi_remove(struct platform_device *pdev)
        iounmap(mipi->base);
        if (res)
                release_mem_region(res->start, resource_size(res));
-       platform_set_drvdata(pdev, NULL);
        kfree(mipi);
 
        return 0;
index b2b33fc..e188ada 100644 (file)
@@ -1622,7 +1622,7 @@ static int ufx_usb_probe(struct usb_interface *interface,
 {
        struct usb_device *usbdev;
        struct ufx_data *dev;
-       struct fb_info *info = 0;
+       struct fb_info *info = NULL;
        int retval = -ENOMEM;
        u32 id_rev, fpga_rev;
 
index 9ef05d3..44967c8 100644 (file)
 #include <linux/pwm.h>
 #include <linux/delay.h>
 
-#define SSD1307FB_WIDTH                        96
-#define SSD1307FB_HEIGHT               16
-
 #define SSD1307FB_DATA                 0x40
 #define SSD1307FB_COMMAND              0x80
 
+#define SSD1307FB_SET_ADDRESS_MODE     0x20
+#define SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL  (0x00)
+#define SSD1307FB_SET_ADDRESS_MODE_VERTICAL    (0x01)
+#define SSD1307FB_SET_ADDRESS_MODE_PAGE                (0x02)
+#define SSD1307FB_SET_COL_RANGE                0x21
+#define SSD1307FB_SET_PAGE_RANGE       0x22
 #define SSD1307FB_CONTRAST             0x81
+#define        SSD1307FB_CHARGE_PUMP           0x8d
 #define SSD1307FB_SEG_REMAP_ON         0xa1
 #define SSD1307FB_DISPLAY_OFF          0xae
+#define SSD1307FB_SET_MULTIPLEX_RATIO  0xa8
 #define SSD1307FB_DISPLAY_ON           0xaf
 #define SSD1307FB_START_PAGE_ADDRESS   0xb0
+#define SSD1307FB_SET_DISPLAY_OFFSET   0xd3
+#define        SSD1307FB_SET_CLOCK_FREQ        0xd5
+#define        SSD1307FB_SET_PRECHARGE_PERIOD  0xd9
+#define        SSD1307FB_SET_COM_PINS_CONFIG   0xda
+#define        SSD1307FB_SET_VCOMH             0xdb
+
+struct ssd1307fb_par;
+
+struct ssd1307fb_ops {
+       int (*init)(struct ssd1307fb_par *);
+       int (*remove)(struct ssd1307fb_par *);
+};
 
 struct ssd1307fb_par {
        struct i2c_client *client;
+       u32 height;
        struct fb_info *info;
+       struct ssd1307fb_ops *ops;
+       u32 page_offset;
        struct pwm_device *pwm;
        u32 pwm_period;
        int reset;
+       u32 width;
+};
+
+struct ssd1307fb_array {
+       u8      type;
+       u8      data[0];
 };
 
 static struct fb_fix_screeninfo ssd1307fb_fix = {
@@ -43,68 +69,87 @@ static struct fb_fix_screeninfo ssd1307fb_fix = {
        .xpanstep       = 0,
        .ypanstep       = 0,
        .ywrapstep      = 0,
-       .line_length    = SSD1307FB_WIDTH / 8,
        .accel          = FB_ACCEL_NONE,
 };
 
 static struct fb_var_screeninfo ssd1307fb_var = {
-       .xres           = SSD1307FB_WIDTH,
-       .yres           = SSD1307FB_HEIGHT,
-       .xres_virtual   = SSD1307FB_WIDTH,
-       .yres_virtual   = SSD1307FB_HEIGHT,
        .bits_per_pixel = 1,
 };
 
-static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8 *cmd, u32 len)
+static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
 {
-       u8 *buf;
-       int ret = 0;
-
-       buf = kzalloc(len + 1, GFP_KERNEL);
-       if (!buf) {
-               dev_err(&client->dev, "Couldn't allocate sending buffer.\n");
-               return -ENOMEM;
-       }
+       struct ssd1307fb_array *array;
 
-       buf[0] = type;
-       memcpy(buf + 1, cmd, len);
+       array = kzalloc(sizeof(struct ssd1307fb_array) + len, GFP_KERNEL);
+       if (!array)
+               return NULL;
 
-       ret = i2c_master_send(client, buf, len + 1);
-       if (ret != len + 1) {
-               dev_err(&client->dev, "Couldn't send I2C command.\n");
-               goto error;
-       }
+       array->type = type;
 
-error:
-       kfree(buf);
-       return ret;
+       return array;
 }
 
-static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8 *cmd, u32 len)
+static int ssd1307fb_write_array(struct i2c_client *client,
+                                struct ssd1307fb_array *array, u32 len)
 {
-       return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len);
+       int ret;
+
+       len += sizeof(struct ssd1307fb_array);
+
+       ret = i2c_master_send(client, (u8 *)array, len);
+       if (ret != len) {
+               dev_err(&client->dev, "Couldn't send I2C command.\n");
+               return ret;
+       }
+
+       return 0;
 }
 
 static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
 {
-       return ssd1307fb_write_cmd_array(client, &cmd, 1);
-}
+       struct ssd1307fb_array *array;
+       int ret;
 
-static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8 *cmd, u32 len)
-{
-       return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len);
+       array = ssd1307fb_alloc_array(1, SSD1307FB_COMMAND);
+       if (!array)
+               return -ENOMEM;
+
+       array->data[0] = cmd;
+
+       ret = ssd1307fb_write_array(client, array, 1);
+       kfree(array);
+
+       return ret;
 }
 
 static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data)
 {
-       return ssd1307fb_write_data_array(client, &data, 1);
+       struct ssd1307fb_array *array;
+       int ret;
+
+       array = ssd1307fb_alloc_array(1, SSD1307FB_DATA);
+       if (!array)
+               return -ENOMEM;
+
+       array->data[0] = data;
+
+       ret = ssd1307fb_write_array(client, array, 1);
+       kfree(array);
+
+       return ret;
 }
 
 static void ssd1307fb_update_display(struct ssd1307fb_par *par)
 {
+       struct ssd1307fb_array *array;
        u8 *vmem = par->info->screen_base;
        int i, j, k;
 
+       array = ssd1307fb_alloc_array(par->width * par->height / 8,
+                                     SSD1307FB_DATA);
+       if (!array)
+               return;
+
        /*
         * The screen is divided in pages, each having a height of 8
         * pixels, and the width of the screen. When sending a byte of
@@ -134,24 +179,23 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
         *  (5) A4 B4 C4 D4 E4 F4 G4 H4
         */
 
-       for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) {
-               ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1));
-               ssd1307fb_write_cmd(par->client, 0x00);
-               ssd1307fb_write_cmd(par->client, 0x10);
-
-               for (j = 0; j < SSD1307FB_WIDTH; j++) {
-                       u8 buf = 0;
+       for (i = 0; i < (par->height / 8); i++) {
+               for (j = 0; j < par->width; j++) {
+                       u32 array_idx = i * par->width + j;
+                       array->data[array_idx] = 0;
                        for (k = 0; k < 8; k++) {
-                               u32 page_length = SSD1307FB_WIDTH * i;
-                               u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8;
+                               u32 page_length = par->width * i;
+                               u32 index = page_length + (par->width * k + j) / 8;
                                u8 byte = *(vmem + index);
                                u8 bit = byte & (1 << (j % 8));
                                bit = bit >> (j % 8);
-                               buf |= bit << k;
+                               array->data[array_idx] |= bit << k;
                        }
-                       ssd1307fb_write_data(par->client, buf);
                }
        }
+
+       ssd1307fb_write_array(par->client, array, par->width * par->height / 8);
+       kfree(array);
 }
 
 
@@ -227,16 +271,167 @@ static struct fb_deferred_io ssd1307fb_defio = {
        .deferred_io    = ssd1307fb_deferred_io,
 };
 
+static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par)
+{
+       int ret;
+
+       par->pwm = pwm_get(&par->client->dev, NULL);
+       if (IS_ERR(par->pwm)) {
+               dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
+               return PTR_ERR(par->pwm);
+       }
+
+       par->pwm_period = pwm_get_period(par->pwm);
+       /* Enable the PWM */
+       pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
+       pwm_enable(par->pwm);
+
+       dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
+               par->pwm->pwm, par->pwm_period);
+
+       /* Map column 127 of the OLED to segment 0 */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+       if (ret < 0)
+               return ret;
+
+       /* Turn on the display */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
+{
+       pwm_disable(par->pwm);
+       pwm_put(par->pwm);
+       return 0;
+}
+
+static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
+       .init   = ssd1307fb_ssd1307_init,
+       .remove = ssd1307fb_ssd1307_remove,
+};
+
+static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
+{
+       int ret;
+
+       /* Set initial contrast */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
+       ret = ret & ssd1307fb_write_cmd(par->client, 0x7f);
+       if (ret < 0)
+               return ret;
+
+       /* Set COM direction */
+       ret = ssd1307fb_write_cmd(par->client, 0xc8);
+       if (ret < 0)
+               return ret;
+
+       /* Set segment re-map */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+       if (ret < 0)
+               return ret;
+
+       /* Set multiplex ratio value */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
+       ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1);
+       if (ret < 0)
+               return ret;
+
+       /* set display offset value */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
+       ret = ssd1307fb_write_cmd(par->client, 0x20);
+       if (ret < 0)
+               return ret;
+
+       /* Set clock frequency */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
+       ret = ret & ssd1307fb_write_cmd(par->client, 0xf0);
+       if (ret < 0)
+               return ret;
+
+       /* Set precharge period in number of ticks from the internal clock */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
+       ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
+       if (ret < 0)
+               return ret;
+
+       /* Set COM pins configuration */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
+       ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
+       if (ret < 0)
+               return ret;
+
+       /* Set VCOMH */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
+       ret = ret & ssd1307fb_write_cmd(par->client, 0x49);
+       if (ret < 0)
+               return ret;
+
+       /* Turn on the DC-DC Charge Pump */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
+       ret = ret & ssd1307fb_write_cmd(par->client, 0x14);
+       if (ret < 0)
+               return ret;
+
+       /* Switch to horizontal addressing mode */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
+       ret = ret & ssd1307fb_write_cmd(par->client,
+                                       SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
+       if (ret < 0)
+               return ret;
+
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
+       ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
+       ret = ret & ssd1307fb_write_cmd(par->client, par->width - 1);
+       if (ret < 0)
+               return ret;
+
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
+       ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
+       ret = ret & ssd1307fb_write_cmd(par->client,
+                                       par->page_offset + (par->height / 8) - 1);
+       if (ret < 0)
+               return ret;
+
+       /* Turn on the display */
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
+       .init   = ssd1307fb_ssd1306_init,
+};
+
+static const struct of_device_id ssd1307fb_of_match[] = {
+       {
+               .compatible = "solomon,ssd1306fb-i2c",
+               .data = (void *)&ssd1307fb_ssd1306_ops,
+       },
+       {
+               .compatible = "solomon,ssd1307fb-i2c",
+               .data = (void *)&ssd1307fb_ssd1307_ops,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
+
 static int ssd1307fb_probe(struct i2c_client *client,
                           const struct i2c_device_id *id)
 {
        struct fb_info *info;
-       u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8;
+       struct device_node *node = client->dev.of_node;
+       u32 vmem_size;
        struct ssd1307fb_par *par;
        u8 *vmem;
        int ret;
 
-       if (!client->dev.of_node) {
+       if (!node) {
                dev_err(&client->dev, "No device tree data found!\n");
                return -EINVAL;
        }
@@ -247,6 +442,31 @@ static int ssd1307fb_probe(struct i2c_client *client,
                return -ENOMEM;
        }
 
+       par = info->par;
+       par->info = info;
+       par->client = client;
+
+       par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match,
+                                                          &client->dev)->data;
+
+       par->reset = of_get_named_gpio(client->dev.of_node,
+                                        "reset-gpios", 0);
+       if (!gpio_is_valid(par->reset)) {
+               ret = -EINVAL;
+               goto fb_alloc_error;
+       }
+
+       if (of_property_read_u32(node, "solomon,width", &par->width))
+               par->width = 96;
+
+       if (of_property_read_u32(node, "solomon,height", &par->height))
+               par->width = 16;
+
+       if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
+               par->page_offset = 1;
+
+       vmem_size = par->width * par->height / 8;
+
        vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
        if (!vmem) {
                dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
@@ -256,9 +476,15 @@ static int ssd1307fb_probe(struct i2c_client *client,
 
        info->fbops = &ssd1307fb_ops;
        info->fix = ssd1307fb_fix;
+       info->fix.line_length = par->width / 8;
        info->fbdefio = &ssd1307fb_defio;
 
        info->var = ssd1307fb_var;
+       info->var.xres = par->width;
+       info->var.xres_virtual = par->width;
+       info->var.yres = par->height;
+       info->var.yres_virtual = par->height;
+
        info->var.red.length = 1;
        info->var.red.offset = 0;
        info->var.green.length = 1;
@@ -272,17 +498,6 @@ static int ssd1307fb_probe(struct i2c_client *client,
 
        fb_deferred_io_init(info);
 
-       par = info->par;
-       par->info = info;
-       par->client = client;
-
-       par->reset = of_get_named_gpio(client->dev.of_node,
-                                        "reset-gpios", 0);
-       if (!gpio_is_valid(par->reset)) {
-               ret = -EINVAL;
-               goto reset_oled_error;
-       }
-
        ret = devm_gpio_request_one(&client->dev, par->reset,
                                    GPIOF_OUT_INIT_HIGH,
                                    "oled-reset");
@@ -293,23 +508,6 @@ static int ssd1307fb_probe(struct i2c_client *client,
                goto reset_oled_error;
        }
 
-       par->pwm = pwm_get(&client->dev, NULL);
-       if (IS_ERR(par->pwm)) {
-               dev_err(&client->dev, "Could not get PWM from device tree!\n");
-               ret = PTR_ERR(par->pwm);
-               goto pwm_error;
-       }
-
-       par->pwm_period = pwm_get_period(par->pwm);
-
-       dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period);
-
-       ret = register_framebuffer(info);
-       if (ret) {
-               dev_err(&client->dev, "Couldn't register the framebuffer\n");
-               goto fbreg_error;
-       }
-
        i2c_set_clientdata(client, info);
 
        /* Reset the screen */
@@ -318,34 +516,25 @@ static int ssd1307fb_probe(struct i2c_client *client,
        gpio_set_value(par->reset, 1);
        udelay(4);
 
-       /* Enable the PWM */
-       pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
-       pwm_enable(par->pwm);
-
-       /* Map column 127 of the OLED to segment 0 */
-       ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON);
-       if (ret < 0) {
-               dev_err(&client->dev, "Couldn't remap the screen.\n");
-               goto remap_error;
+       if (par->ops->init) {
+               ret = par->ops->init(par);
+               if (ret)
+                       goto reset_oled_error;
        }
 
-       /* Turn on the display */
-       ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON);
-       if (ret < 0) {
-               dev_err(&client->dev, "Couldn't turn the display on.\n");
-               goto remap_error;
+       ret = register_framebuffer(info);
+       if (ret) {
+               dev_err(&client->dev, "Couldn't register the framebuffer\n");
+               goto panel_init_error;
        }
 
        dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
 
        return 0;
 
-remap_error:
-       unregister_framebuffer(info);
-       pwm_disable(par->pwm);
-fbreg_error:
-       pwm_put(par->pwm);
-pwm_error:
+panel_init_error:
+       if (par->ops->remove)
+               par->ops->remove(par);
 reset_oled_error:
        fb_deferred_io_cleanup(info);
 fb_alloc_error:
@@ -359,8 +548,8 @@ static int ssd1307fb_remove(struct i2c_client *client)
        struct ssd1307fb_par *par = info->par;
 
        unregister_framebuffer(info);
-       pwm_disable(par->pwm);
-       pwm_put(par->pwm);
+       if (par->ops->remove)
+               par->ops->remove(par);
        fb_deferred_io_cleanup(info);
        framebuffer_release(info);
 
@@ -368,17 +557,12 @@ static int ssd1307fb_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id ssd1307fb_i2c_id[] = {
+       { "ssd1306fb", 0 },
        { "ssd1307fb", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
 
-static const struct of_device_id ssd1307fb_of_match[] = {
-       { .compatible = "solomon,ssd1307fb-i2c" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
-
 static struct i2c_driver ssd1307fb_driver = {
        .probe = ssd1307fb_probe,
        .remove = ssd1307fb_remove,
index dc4fb86..deb8733 100644 (file)
@@ -794,7 +794,6 @@ err_hw_init:
                cell->disable(dev);
 err_enable:
 err_find_mode:
-       platform_set_drvdata(dev, NULL);
        free_irq(irq, info);
 err_request_irq:
        iounmap(info->screen_base);
@@ -823,8 +822,6 @@ static int tmiofb_remove(struct platform_device *dev)
                if (cell->disable)
                        cell->disable(dev);
 
-               platform_set_drvdata(dev, NULL);
-
                free_irq(irq, info);
 
                iounmap(info->screen_base);
index ec03e72..d2e5bc3 100644 (file)
@@ -434,10 +434,10 @@ static void dlfb_compress_hline(
 
        while ((pixel_end > pixel) &&
               (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) {
-               uint8_t *raw_pixels_count_byte = 0;
-               uint8_t *cmd_pixels_count_byte = 0;
-               const uint16_t *raw_pixel_start = 0;
-               const uint16_t *cmd_pixel_start, *cmd_pixel_end = 0;
+               uint8_t *raw_pixels_count_byte = NULL;
+               uint8_t *cmd_pixels_count_byte = NULL;
+               const uint16_t *raw_pixel_start = NULL;
+               const uint16_t *cmd_pixel_start, *cmd_pixel_end = NULL;
 
                prefetchw((void *) cmd); /* pull in one cache line at least */
 
@@ -573,7 +573,7 @@ static int dlfb_render_hline(struct dlfb_data *dev, struct urb **urb_ptr,
        return 0;
 }
 
-int dlfb_handle_damage(struct dlfb_data *dev, int x, int y,
+static int dlfb_handle_damage(struct dlfb_data *dev, int x, int y,
               int width, int height, char *data)
 {
        int i, ret;
@@ -1588,7 +1588,7 @@ static int dlfb_usb_probe(struct usb_interface *interface,
                        const struct usb_device_id *id)
 {
        struct usb_device *usbdev;
-       struct dlfb_data *dev = 0;
+       struct dlfb_data *dev = NULL;
        int retval = -ENOMEM;
 
        /* usb initialization */
index e328a61..b963ea1 100644 (file)
@@ -24,9 +24,6 @@
 #ifdef CONFIG_X86
 #include <video/vga.h>
 #endif
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
 #include "edid.h"
 
 static struct cb_id uvesafb_cn_id = {
@@ -819,8 +816,8 @@ static int uvesafb_vbe_init(struct fb_info *info)
        if (par->pmi_setpal || par->ypan) {
                if (__supported_pte_mask & _PAGE_NX) {
                        par->pmi_setpal = par->ypan = 0;
-                       printk(KERN_WARNING "uvesafb: NX protection is actively."
-                               "We have better not to use the PMI.\n");
+                       printk(KERN_WARNING "uvesafb: NX protection is active"
+                                           "better not use the PMI.\n");
                } else {
                        uvesafb_vbe_getpmi(task, par);
                }
@@ -1540,67 +1537,30 @@ static void uvesafb_init_info(struct fb_info *info, struct vbe_mode_ib *mode)
 
 static void uvesafb_init_mtrr(struct fb_info *info)
 {
-#ifdef CONFIG_MTRR
+       struct uvesafb_par *par = info->par;
+
        if (mtrr && !(info->fix.smem_start & (PAGE_SIZE - 1))) {
                int temp_size = info->fix.smem_len;
-               unsigned int type = 0;
 
-               switch (mtrr) {
-               case 1:
-                       type = MTRR_TYPE_UNCACHABLE;
-                       break;
-               case 2:
-                       type = MTRR_TYPE_WRBACK;
-                       break;
-               case 3:
-                       type = MTRR_TYPE_WRCOMB;
-                       break;
-               case 4:
-                       type = MTRR_TYPE_WRTHROUGH;
-                       break;
-               default:
-                       type = 0;
-                       break;
-               }
+               int rc;
 
-               if (type) {
-                       int rc;
+               /* Find the largest power-of-two */
+               temp_size = roundup_pow_of_two(temp_size);
 
-                       /* Find the largest power-of-two */
-                       temp_size = roundup_pow_of_two(temp_size);
+               /* Try and find a power of two to add */
+               do {
+                       rc = arch_phys_wc_add(info->fix.smem_start, temp_size);
+                       temp_size >>= 1;
+               } while (temp_size >= PAGE_SIZE && rc == -EINVAL);
 
-                       /* Try and find a power of two to add */
-                       do {
-                               rc = mtrr_add(info->fix.smem_start,
-                                             temp_size, type, 1);
-                               temp_size >>= 1;
-                       } while (temp_size >= PAGE_SIZE && rc == -EINVAL);
-               }
+               if (rc >= 0)
+                       par->mtrr_handle = rc;
        }
-#endif /* CONFIG_MTRR */
 }
 
 static void uvesafb_ioremap(struct fb_info *info)
 {
-#ifdef CONFIG_X86
-       switch (mtrr) {
-       case 1: /* uncachable */
-               info->screen_base = ioremap_nocache(info->fix.smem_start, info->fix.smem_len);
-               break;
-       case 2: /* write-back */
-               info->screen_base = ioremap_cache(info->fix.smem_start, info->fix.smem_len);
-               break;
-       case 3: /* write-combining */
-               info->screen_base = ioremap_wc(info->fix.smem_start, info->fix.smem_len);
-               break;
-       case 4: /* write-through */
-       default:
-               info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
-               break;
-       }
-#else
-       info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
-#endif /* CONFIG_X86 */
+       info->screen_base = ioremap_wc(info->fix.smem_start, info->fix.smem_len);
 }
 
 static ssize_t uvesafb_show_vbe_ver(struct device *dev,
@@ -1851,6 +1811,7 @@ static int uvesafb_remove(struct platform_device *dev)
                unregister_framebuffer(info);
                release_region(0x3c0, 32);
                iounmap(info->screen_base);
+               arch_phys_wc_del(par->mtrr_handle);
                release_mem_region(info->fix.smem_start, info->fix.smem_len);
                fb_destroy_modedb(info->monspecs.modedb);
                fb_dealloc_cmap(&info->cmap);
@@ -1930,6 +1891,9 @@ static int uvesafb_setup(char *options)
                }
        }
 
+       if (mtrr != 3 && mtrr != 1)
+               pr_warn("uvesafb: mtrr should be set to 0 or 3; %d is unsupported", mtrr);
+
        return 0;
 }
 #endif /* !MODULE */
index 545faec..830ded4 100644 (file)
@@ -1269,7 +1269,6 @@ static void vga16fb_destroy(struct fb_info *info)
        iounmap(info->screen_base);
        fb_dealloc_cmap(&info->cmap);
        /* XXX unshare VGA regions */
-       platform_set_drvdata(dev, NULL);
        framebuffer_release(info);
 }
 
index 9547e18..8974849 100644 (file)
@@ -448,7 +448,6 @@ failed_free_io:
 failed_free_res:
        release_mem_region(res->start, resource_size(res));
 failed_fbi:
-       platform_set_drvdata(pdev, NULL);
        kfree(fbi);
 failed:
        return ret;
index 01f9ace..3072f30 100644 (file)
@@ -173,7 +173,7 @@ static ssize_t contrast_store(struct device *dev,
        struct wm8505fb_info *fbi = to_wm8505fb_info(info);
        unsigned long tmp;
 
-       if (strict_strtoul(buf, 10, &tmp) || (tmp > 0xff))
+       if (kstrtoul(buf, 10, &tmp) || (tmp > 0xff))
                return -EINVAL;
        fbi->contrast = tmp;
 
index af0b4fd..f3d4a69 100644 (file)
@@ -44,7 +44,7 @@
 
 
 /*
- * Xilinx calls it "PLB TFT LCD Controller" though it can also be used for
+ * Xilinx calls it "TFT LCD Controller" though it can also be used for
  * the VGA port on the Xilinx ML40x board. This is a hardware display
  * controller for a 640x480 resolution TFT or VGA screen.
  *
  * don't start thinking about scrolling).  The second allows the LCD to
  * be turned on or off as well as rotated 180 degrees.
  *
- * In case of direct PLB access the second control register will be at
+ * In case of direct BUS access the second control register will be at
  * an offset of 4 as compared to the DCR access where the offset is 1
  * i.e. REG_CTRL. So this is taken care in the function
- * xilinx_fb_out_be32 where it left shifts the offset 2 times in case of
- * direct PLB access.
+ * xilinx_fb_out32 where it left shifts the offset 2 times in case of
+ * direct BUS access.
  */
 #define NUM_REGS       2
 #define REG_FB_ADDR    0
@@ -116,7 +116,8 @@ static struct fb_var_screeninfo xilinx_fb_var = {
 };
 
 
-#define PLB_ACCESS_FLAG        0x1             /* 1 = PLB, 0 = DCR */
+#define BUS_ACCESS_FLAG                0x1 /* 1 = BUS, 0 = DCR */
+#define LITTLE_ENDIAN_ACCESS   0x2 /* LITTLE ENDIAN IO functions */
 
 struct xilinxfb_drvdata {
 
@@ -146,21 +147,40 @@ struct xilinxfb_drvdata {
        container_of(_info, struct xilinxfb_drvdata, info)
 
 /*
- * The XPS TFT Controller can be accessed through PLB or DCR interface.
+ * The XPS TFT Controller can be accessed through BUS or DCR interface.
  * To perform the read/write on the registers we need to check on
  * which bus its connected and call the appropriate write API.
  */
-static void xilinx_fb_out_be32(struct xilinxfb_drvdata *drvdata, u32 offset,
+static void xilinx_fb_out32(struct xilinxfb_drvdata *drvdata, u32 offset,
                                u32 val)
 {
-       if (drvdata->flags & PLB_ACCESS_FLAG)
-               out_be32(drvdata->regs + (offset << 2), val);
+       if (drvdata->flags & BUS_ACCESS_FLAG) {
+               if (drvdata->flags & LITTLE_ENDIAN_ACCESS)
+                       iowrite32(val, drvdata->regs + (offset << 2));
+               else
+                       iowrite32be(val, drvdata->regs + (offset << 2));
+       }
 #ifdef CONFIG_PPC_DCR
        else
                dcr_write(drvdata->dcr_host, offset, val);
 #endif
 }
 
+static u32 xilinx_fb_in32(struct xilinxfb_drvdata *drvdata, u32 offset)
+{
+       if (drvdata->flags & BUS_ACCESS_FLAG) {
+               if (drvdata->flags & LITTLE_ENDIAN_ACCESS)
+                       return ioread32(drvdata->regs + (offset << 2));
+               else
+                       return ioread32be(drvdata->regs + (offset << 2));
+       }
+#ifdef CONFIG_PPC_DCR
+       else
+               return dcr_read(drvdata->dcr_host, offset);
+#endif
+       return 0;
+}
+
 static int
 xilinx_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
        unsigned transp, struct fb_info *fbi)
@@ -197,7 +217,7 @@ xilinx_fb_blank(int blank_mode, struct fb_info *fbi)
        switch (blank_mode) {
        case FB_BLANK_UNBLANK:
                /* turn on panel */
-               xilinx_fb_out_be32(drvdata, REG_CTRL, drvdata->reg_ctrl_default);
+               xilinx_fb_out32(drvdata, REG_CTRL, drvdata->reg_ctrl_default);
                break;
 
        case FB_BLANK_NORMAL:
@@ -205,7 +225,7 @@ xilinx_fb_blank(int blank_mode, struct fb_info *fbi)
        case FB_BLANK_HSYNC_SUSPEND:
        case FB_BLANK_POWERDOWN:
                /* turn off panel */
-               xilinx_fb_out_be32(drvdata, REG_CTRL, 0);
+               xilinx_fb_out32(drvdata, REG_CTRL, 0);
        default:
                break;
 
@@ -227,33 +247,23 @@ static struct fb_ops xilinxfb_ops =
  * Bus independent setup/teardown
  */
 
-static int xilinxfb_assign(struct device *dev,
+static int xilinxfb_assign(struct platform_device *pdev,
                           struct xilinxfb_drvdata *drvdata,
-                          unsigned long physaddr,
                           struct xilinxfb_platform_data *pdata)
 {
        int rc;
+       struct device *dev = &pdev->dev;
        int fbsize = pdata->xvirt * pdata->yvirt * BYTES_PER_PIXEL;
 
-       if (drvdata->flags & PLB_ACCESS_FLAG) {
-               /*
-                * Map the control registers in if the controller
-                * is on direct PLB interface.
-                */
-               if (!request_mem_region(physaddr, 8, DRIVER_NAME)) {
-                       dev_err(dev, "Couldn't lock memory region at 0x%08lX\n",
-                               physaddr);
-                       rc = -ENODEV;
-                       goto err_region;
-               }
+       if (drvdata->flags & BUS_ACCESS_FLAG) {
+               struct resource *res;
 
-               drvdata->regs_phys = physaddr;
-               drvdata->regs = ioremap(physaddr, 8);
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               drvdata->regs_phys = res->start;
+               drvdata->regs = devm_request_and_ioremap(&pdev->dev, res);
                if (!drvdata->regs) {
-                       dev_err(dev, "Couldn't lock memory region at 0x%08lX\n",
-                               physaddr);
-                       rc = -ENODEV;
-                       goto err_map;
+                       rc = -EADDRNOTAVAIL;
+                       goto err_region;
                }
        }
 
@@ -270,7 +280,7 @@ static int xilinxfb_assign(struct device *dev,
        if (!drvdata->fb_virt) {
                dev_err(dev, "Could not allocate frame buffer memory\n");
                rc = -ENOMEM;
-               if (drvdata->flags & PLB_ACCESS_FLAG)
+               if (drvdata->flags & BUS_ACCESS_FLAG)
                        goto err_fbmem;
                else
                        goto err_region;
@@ -280,13 +290,19 @@ static int xilinxfb_assign(struct device *dev,
        memset_io((void __iomem *)drvdata->fb_virt, 0, fbsize);
 
        /* Tell the hardware where the frame buffer is */
-       xilinx_fb_out_be32(drvdata, REG_FB_ADDR, drvdata->fb_phys);
+       xilinx_fb_out32(drvdata, REG_FB_ADDR, drvdata->fb_phys);
+       rc = xilinx_fb_in32(drvdata, REG_FB_ADDR);
+       /* Endianess detection */
+       if (rc != drvdata->fb_phys) {
+               drvdata->flags |= LITTLE_ENDIAN_ACCESS;
+               xilinx_fb_out32(drvdata, REG_FB_ADDR, drvdata->fb_phys);
+       }
 
        /* Turn on the display */
        drvdata->reg_ctrl_default = REG_CTRL_ENABLE;
        if (pdata->rotate_screen)
                drvdata->reg_ctrl_default |= REG_CTRL_ROTATE;
-       xilinx_fb_out_be32(drvdata, REG_CTRL,
+       xilinx_fb_out32(drvdata, REG_CTRL,
                                        drvdata->reg_ctrl_default);
 
        /* Fill struct fb_info */
@@ -323,9 +339,9 @@ static int xilinxfb_assign(struct device *dev,
                goto err_regfb;
        }
 
-       if (drvdata->flags & PLB_ACCESS_FLAG) {
+       if (drvdata->flags & BUS_ACCESS_FLAG) {
                /* Put a banner in the log (for DEBUG) */
-               dev_dbg(dev, "regs: phys=%lx, virt=%p\n", physaddr,
+               dev_dbg(dev, "regs: phys=%x, virt=%p\n", drvdata->regs_phys,
                                        drvdata->regs);
        }
        /* Put a banner in the log (for DEBUG) */
@@ -345,15 +361,11 @@ err_cmap:
                iounmap(drvdata->fb_virt);
 
        /* Turn off the display */
-       xilinx_fb_out_be32(drvdata, REG_CTRL, 0);
+       xilinx_fb_out32(drvdata, REG_CTRL, 0);
 
 err_fbmem:
-       if (drvdata->flags & PLB_ACCESS_FLAG)
-               iounmap(drvdata->regs);
-
-err_map:
-       if (drvdata->flags & PLB_ACCESS_FLAG)
-               release_mem_region(physaddr, 8);
+       if (drvdata->flags & BUS_ACCESS_FLAG)
+               devm_iounmap(dev, drvdata->regs);
 
 err_region:
        kfree(drvdata);
@@ -381,13 +393,11 @@ static int xilinxfb_release(struct device *dev)
                iounmap(drvdata->fb_virt);
 
        /* Turn off the display */
-       xilinx_fb_out_be32(drvdata, REG_CTRL, 0);
+       xilinx_fb_out32(drvdata, REG_CTRL, 0);
 
        /* Release the resources, as allocated based on interface */
-       if (drvdata->flags & PLB_ACCESS_FLAG) {
-               iounmap(drvdata->regs);
-               release_mem_region(drvdata->regs_phys, 8);
-       }
+       if (drvdata->flags & BUS_ACCESS_FLAG)
+               devm_iounmap(dev, drvdata->regs);
 #ifdef CONFIG_PPC_DCR
        else
                dcr_unmap(drvdata->dcr_host, drvdata->dcr_len);
@@ -406,11 +416,9 @@ static int xilinxfb_release(struct device *dev)
 static int xilinxfb_of_probe(struct platform_device *op)
 {
        const u32 *prop;
-       u32 *p;
-       u32 tft_access;
+       u32 tft_access = 0;
        struct xilinxfb_platform_data pdata;
-       struct resource res;
-       int size, rc;
+       int size;
        struct xilinxfb_drvdata *drvdata;
 
        /* Copy with the default pdata (not a ptr reference!) */
@@ -424,34 +432,29 @@ static int xilinxfb_of_probe(struct platform_device *op)
        }
 
        /*
-        * To check whether the core is connected directly to DCR or PLB
+        * To check whether the core is connected directly to DCR or BUS
         * interface and initialize the tft_access accordingly.
         */
-       p = (u32 *)of_get_property(op->dev.of_node, "xlnx,dcr-splb-slave-if", NULL);
-       tft_access = p ? *p : 0;
+       of_property_read_u32(op->dev.of_node, "xlnx,dcr-splb-slave-if",
+                            &tft_access);
 
        /*
-        * Fill the resource structure if its direct PLB interface
+        * Fill the resource structure if its direct BUS interface
         * otherwise fill the dcr_host structure.
         */
        if (tft_access) {
-               drvdata->flags |= PLB_ACCESS_FLAG;
-               rc = of_address_to_resource(op->dev.of_node, 0, &res);
-               if (rc) {
-                       dev_err(&op->dev, "invalid address\n");
-                       goto err;
-               }
+               drvdata->flags |= BUS_ACCESS_FLAG;
        }
 #ifdef CONFIG_PPC_DCR
        else {
                int start;
-               res.start = 0;
                start = dcr_resource_start(op->dev.of_node, 0);
                drvdata->dcr_len = dcr_resource_len(op->dev.of_node, 0);
                drvdata->dcr_host = dcr_map(op->dev.of_node, start, drvdata->dcr_len);
                if (!DCR_MAP_OK(drvdata->dcr_host)) {
                        dev_err(&op->dev, "invalid DCR address\n");
-                       goto err;
+                       kfree(drvdata);
+                       return -ENODEV;
                }
        }
 #endif
@@ -478,11 +481,7 @@ static int xilinxfb_of_probe(struct platform_device *op)
                pdata.rotate_screen = 1;
 
        dev_set_drvdata(&op->dev, drvdata);
-       return xilinxfb_assign(&op->dev, drvdata, res.start, &pdata);
-
- err:
-       kfree(drvdata);
-       return -ENODEV;
+       return xilinxfb_assign(op, drvdata, &pdata);
 }
 
 static int xilinxfb_of_remove(struct platform_device *op)
index c702074..6b4a4db 100644 (file)
@@ -576,19 +576,21 @@ void virtqueue_disable_cb(struct virtqueue *_vq)
 EXPORT_SYMBOL_GPL(virtqueue_disable_cb);
 
 /**
- * virtqueue_enable_cb - restart callbacks after disable_cb.
+ * virtqueue_enable_cb_prepare - restart callbacks after disable_cb
  * @vq: the struct virtqueue we're talking about.
  *
- * This re-enables callbacks; it returns "false" if there are pending
- * buffers in the queue, to detect a possible race between the driver
- * checking for more work, and enabling callbacks.
+ * This re-enables callbacks; it returns current queue state
+ * in an opaque unsigned value. This value should be later tested by
+ * virtqueue_poll, to detect a possible race between the driver checking for
+ * more work, and enabling callbacks.
  *
  * Caller must ensure we don't call this with other virtqueue
  * operations at the same time (except where noted).
  */
-bool virtqueue_enable_cb(struct virtqueue *_vq)
+unsigned virtqueue_enable_cb_prepare(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 last_used_idx;
 
        START_USE(vq);
 
@@ -598,15 +600,45 @@ bool virtqueue_enable_cb(struct virtqueue *_vq)
         * either clear the flags bit or point the event index at the next
         * entry. Always do both to keep code simple. */
        vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
-       vring_used_event(&vq->vring) = vq->last_used_idx;
+       vring_used_event(&vq->vring) = last_used_idx = vq->last_used_idx;
+       END_USE(vq);
+       return last_used_idx;
+}
+EXPORT_SYMBOL_GPL(virtqueue_enable_cb_prepare);
+
+/**
+ * virtqueue_poll - query pending used buffers
+ * @vq: the struct virtqueue we're talking about.
+ * @last_used_idx: virtqueue state (from call to virtqueue_enable_cb_prepare).
+ *
+ * Returns "true" if there are pending used buffers in the queue.
+ *
+ * This does not need to be serialized.
+ */
+bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
        virtio_mb(vq->weak_barriers);
-       if (unlikely(more_used(vq))) {
-               END_USE(vq);
-               return false;
-       }
+       return (u16)last_used_idx != vq->vring.used->idx;
+}
+EXPORT_SYMBOL_GPL(virtqueue_poll);
 
-       END_USE(vq);
-       return true;
+/**
+ * virtqueue_enable_cb - restart callbacks after disable_cb.
+ * @vq: the struct virtqueue we're talking about.
+ *
+ * This re-enables callbacks; it returns "false" if there are pending
+ * buffers in the queue, to detect a possible race between the driver
+ * checking for more work, and enabling callbacks.
+ *
+ * Caller must ensure we don't call this with other virtqueue
+ * operations at the same time (except where noted).
+ */
+bool virtqueue_enable_cb(struct virtqueue *_vq)
+{
+       unsigned last_used_idx = virtqueue_enable_cb_prepare(_vq);
+       return !virtqueue_poll(_vq, last_used_idx);
 }
 EXPORT_SYMBOL_GPL(virtqueue_enable_cb);
 
index 13ddec9..3d9d3f5 100644 (file)
@@ -109,7 +109,7 @@ cont:
 
        spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED);
        /* Already gone or negative dentry (under construction) - try next */
-       if (q->d_count == 0 || !simple_positive(q)) {
+       if (!d_count(q) || !simple_positive(q)) {
                spin_unlock(&q->d_lock);
                next = q->d_u.d_child.next;
                goto cont;
@@ -267,7 +267,7 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
                        else
                                ino_count++;
 
-                       if (p->d_count > ino_count) {
+                       if (d_count(p) > ino_count) {
                                top_ino->last_used = jiffies;
                                dput(p);
                                return 1;
@@ -409,7 +409,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
                if (!exp_leaves) {
                        /* Path walk currently on this dentry? */
                        ino_count = atomic_read(&ino->count) + 1;
-                       if (dentry->d_count > ino_count)
+                       if (d_count(dentry) > ino_count)
                                goto next;
 
                        if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) {
@@ -423,7 +423,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
                } else {
                        /* Path walk currently on this dentry? */
                        ino_count = atomic_read(&ino->count) + 1;
-                       if (dentry->d_count > ino_count)
+                       if (d_count(dentry) > ino_count)
                                goto next;
 
                        expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
index ca8e555..92ef341 100644 (file)
@@ -179,7 +179,7 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
                spin_lock(&active->d_lock);
 
                /* Already gone? */
-               if (active->d_count == 0)
+               if (!d_count(active))
                        goto next;
 
                qstr = &active->d_name;
index 290e347..eaf1333 100644 (file)
@@ -255,13 +255,11 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
  * to a logical address
  */
 static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
-                                       int search_commit_root,
-                                       u64 time_seq,
-                                       struct __prelim_ref *ref,
-                                       struct ulist *parents,
-                                       const u64 *extent_item_pos)
+                                 struct btrfs_path *path, u64 time_seq,
+                                 struct __prelim_ref *ref,
+                                 struct ulist *parents,
+                                 const u64 *extent_item_pos)
 {
-       struct btrfs_path *path;
        struct btrfs_root *root;
        struct btrfs_key root_key;
        struct extent_buffer *eb;
@@ -269,11 +267,6 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
        int root_level;
        int level = ref->level;
 
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
-       path->search_commit_root = !!search_commit_root;
-
        root_key.objectid = ref->root_id;
        root_key.type = BTRFS_ROOT_ITEM_KEY;
        root_key.offset = (u64)-1;
@@ -314,7 +307,8 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
                                time_seq, ref->wanted_disk_byte,
                                extent_item_pos);
 out:
-       btrfs_free_path(path);
+       path->lowest_level = 0;
+       btrfs_release_path(path);
        return ret;
 }
 
@@ -322,7 +316,7 @@ out:
  * resolve all indirect backrefs from the list
  */
 static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
-                                  int search_commit_root, u64 time_seq,
+                                  struct btrfs_path *path, u64 time_seq,
                                   struct list_head *head,
                                   const u64 *extent_item_pos)
 {
@@ -349,9 +343,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
                        continue;
                if (ref->count == 0)
                        continue;
-               err = __resolve_indirect_ref(fs_info, search_commit_root,
-                                            time_seq, ref, parents,
-                                            extent_item_pos);
+               err = __resolve_indirect_ref(fs_info, path, time_seq, ref,
+                                            parents, extent_item_pos);
                if (err == -ENOMEM)
                        goto out;
                if (err)
@@ -604,6 +597,7 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
        int slot;
        struct extent_buffer *leaf;
        struct btrfs_key key;
+       struct btrfs_key found_key;
        unsigned long ptr;
        unsigned long end;
        struct btrfs_extent_item *ei;
@@ -621,17 +615,21 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
 
        ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
        flags = btrfs_extent_flags(leaf, ei);
+       btrfs_item_key_to_cpu(leaf, &found_key, slot);
 
        ptr = (unsigned long)(ei + 1);
        end = (unsigned long)ei + item_size;
 
-       if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
+       if (found_key.type == BTRFS_EXTENT_ITEM_KEY &&
+           flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
                struct btrfs_tree_block_info *info;
 
                info = (struct btrfs_tree_block_info *)ptr;
                *info_level = btrfs_tree_block_level(leaf, info);
                ptr += sizeof(struct btrfs_tree_block_info);
                BUG_ON(ptr > end);
+       } else if (found_key.type == BTRFS_METADATA_ITEM_KEY) {
+               *info_level = found_key.offset;
        } else {
                BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA));
        }
@@ -795,7 +793,6 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
        struct btrfs_delayed_ref_head *head;
        int info_level = 0;
        int ret;
-       int search_commit_root = (trans == BTRFS_BACKREF_SEARCH_COMMIT_ROOT);
        struct list_head prefs_delayed;
        struct list_head prefs;
        struct __prelim_ref *ref;
@@ -804,13 +801,17 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
        INIT_LIST_HEAD(&prefs_delayed);
 
        key.objectid = bytenr;
-       key.type = BTRFS_EXTENT_ITEM_KEY;
        key.offset = (u64)-1;
+       if (btrfs_fs_incompat(fs_info, SKINNY_METADATA))
+               key.type = BTRFS_METADATA_ITEM_KEY;
+       else
+               key.type = BTRFS_EXTENT_ITEM_KEY;
 
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
-       path->search_commit_root = !!search_commit_root;
+       if (!trans)
+               path->search_commit_root = 1;
 
        /*
         * grab both a lock on the path and a lock on the delayed ref head.
@@ -825,7 +826,7 @@ again:
                goto out;
        BUG_ON(ret == 0);
 
-       if (trans != BTRFS_BACKREF_SEARCH_COMMIT_ROOT) {
+       if (trans) {
                /*
                 * look if there are updates for this ref queued and lock the
                 * head
@@ -869,7 +870,8 @@ again:
                slot = path->slots[0];
                btrfs_item_key_to_cpu(leaf, &key, slot);
                if (key.objectid == bytenr &&
-                   key.type == BTRFS_EXTENT_ITEM_KEY) {
+                   (key.type == BTRFS_EXTENT_ITEM_KEY ||
+                    key.type == BTRFS_METADATA_ITEM_KEY)) {
                        ret = __add_inline_refs(fs_info, path, bytenr,
                                                &info_level, &prefs);
                        if (ret)
@@ -890,8 +892,8 @@ again:
 
        __merge_refs(&prefs, 1);
 
-       ret = __resolve_indirect_refs(fs_info, search_commit_root, time_seq,
-                                     &prefs, extent_item_pos);
+       ret = __resolve_indirect_refs(fs_info, path, time_seq, &prefs,
+                                     extent_item_pos);
        if (ret)
                goto out;
 
@@ -1283,12 +1285,16 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
 {
        int ret;
        u64 flags;
+       u64 size = 0;
        u32 item_size;
        struct extent_buffer *eb;
        struct btrfs_extent_item *ei;
        struct btrfs_key key;
 
-       key.type = BTRFS_EXTENT_ITEM_KEY;
+       if (btrfs_fs_incompat(fs_info, SKINNY_METADATA))
+               key.type = BTRFS_METADATA_ITEM_KEY;
+       else
+               key.type = BTRFS_EXTENT_ITEM_KEY;
        key.objectid = logical;
        key.offset = (u64)-1;
 
@@ -1301,9 +1307,15 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
                return ret;
 
        btrfs_item_key_to_cpu(path->nodes[0], found_key, path->slots[0]);
-       if (found_key->type != BTRFS_EXTENT_ITEM_KEY ||
+       if (found_key->type == BTRFS_METADATA_ITEM_KEY)
+               size = fs_info->extent_root->leafsize;
+       else if (found_key->type == BTRFS_EXTENT_ITEM_KEY)
+               size = found_key->offset;
+
+       if ((found_key->type != BTRFS_EXTENT_ITEM_KEY &&
+            found_key->type != BTRFS_METADATA_ITEM_KEY) ||
            found_key->objectid > logical ||
-           found_key->objectid + found_key->offset <= logical) {
+           found_key->objectid + size <= logical) {
                pr_debug("logical %llu is not within any extent\n",
                         (unsigned long long)logical);
                return -ENOENT;
@@ -1459,7 +1471,7 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
                                iterate_extent_inodes_t *iterate, void *ctx)
 {
        int ret;
-       struct btrfs_trans_handle *trans;
+       struct btrfs_trans_handle *trans = NULL;
        struct ulist *refs = NULL;
        struct ulist *roots = NULL;
        struct ulist_node *ref_node = NULL;
@@ -1471,9 +1483,7 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
        pr_debug("resolving all inodes for extent %llu\n",
                        extent_item_objectid);
 
-       if (search_commit_root) {
-               trans = BTRFS_BACKREF_SEARCH_COMMIT_ROOT;
-       } else {
+       if (!search_commit_root) {
                trans = btrfs_join_transaction(fs_info->extent_root);
                if (IS_ERR(trans))
                        return PTR_ERR(trans);
index 0f446d7..8f2e767 100644 (file)
@@ -23,8 +23,6 @@
 #include "ulist.h"
 #include "extent_io.h"
 
-#define BTRFS_BACKREF_SEARCH_COMMIT_ROOT ((struct btrfs_trans_handle *)0)
-
 struct inode_fs_paths {
        struct btrfs_path               *btrfs_path;
        struct btrfs_root               *fs_root;
index 17dffe3..5bf4c39 100644 (file)
@@ -1089,7 +1089,8 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
                btrfs_set_node_ptr_generation(parent, parent_slot,
                                              trans->transid);
                btrfs_mark_buffer_dirty(parent);
-               tree_mod_log_free_eb(root->fs_info, buf);
+               if (last_ref)
+                       tree_mod_log_free_eb(root->fs_info, buf);
                btrfs_free_tree_block(trans, root, buf, parent_start,
                                      last_ref);
        }
@@ -1161,8 +1162,8 @@ __tree_mod_log_oldest_root(struct btrfs_fs_info *fs_info,
  * time_seq).
  */
 static void
-__tree_mod_log_rewind(struct extent_buffer *eb, u64 time_seq,
-                     struct tree_mod_elem *first_tm)
+__tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb,
+                     u64 time_seq, struct tree_mod_elem *first_tm)
 {
        u32 n;
        struct rb_node *next;
@@ -1172,6 +1173,7 @@ __tree_mod_log_rewind(struct extent_buffer *eb, u64 time_seq,
        unsigned long p_size = sizeof(struct btrfs_key_ptr);
 
        n = btrfs_header_nritems(eb);
+       tree_mod_log_read_lock(fs_info);
        while (tm && tm->seq >= time_seq) {
                /*
                 * all the operations are recorded with the operator used for
@@ -1226,6 +1228,7 @@ __tree_mod_log_rewind(struct extent_buffer *eb, u64 time_seq,
                if (tm->index != first_tm->index)
                        break;
        }
+       tree_mod_log_read_unlock(fs_info);
        btrfs_set_header_nritems(eb, n);
 }
 
@@ -1274,7 +1277,7 @@ tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb,
 
        extent_buffer_get(eb_rewin);
        btrfs_tree_read_lock(eb_rewin);
-       __tree_mod_log_rewind(eb_rewin, time_seq, tm);
+       __tree_mod_log_rewind(fs_info, eb_rewin, time_seq, tm);
        WARN_ON(btrfs_header_nritems(eb_rewin) >
                BTRFS_NODEPTRS_PER_BLOCK(fs_info->tree_root));
 
@@ -1350,7 +1353,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
                btrfs_set_header_generation(eb, old_generation);
        }
        if (tm)
-               __tree_mod_log_rewind(eb, time_seq, tm);
+               __tree_mod_log_rewind(root->fs_info, eb, time_seq, tm);
        else
                WARN_ON(btrfs_header_level(eb) != 0);
        WARN_ON(btrfs_header_nritems(eb) > BTRFS_NODEPTRS_PER_BLOCK(root));
@@ -2178,12 +2181,8 @@ static void reada_for_search(struct btrfs_root *root,
        }
 }
 
-/*
- * returns -EAGAIN if it had to drop the path, or zero if everything was in
- * cache
- */
-static noinline int reada_for_balance(struct btrfs_root *root,
-                                     struct btrfs_path *path, int level)
+static noinline void reada_for_balance(struct btrfs_root *root,
+                                      struct btrfs_path *path, int level)
 {
        int slot;
        int nritems;
@@ -2192,12 +2191,11 @@ static noinline int reada_for_balance(struct btrfs_root *root,
        u64 gen;
        u64 block1 = 0;
        u64 block2 = 0;
-       int ret = 0;
        int blocksize;
 
        parent = path->nodes[level + 1];
        if (!parent)
-               return 0;
+               return;
 
        nritems = btrfs_header_nritems(parent);
        slot = path->slots[level + 1];
@@ -2224,28 +2222,11 @@ static noinline int reada_for_balance(struct btrfs_root *root,
                        block2 = 0;
                free_extent_buffer(eb);
        }
-       if (block1 || block2) {
-               ret = -EAGAIN;
-
-               /* release the whole path */
-               btrfs_release_path(path);
-
-               /* read the blocks */
-               if (block1)
-                       readahead_tree_block(root, block1, blocksize, 0);
-               if (block2)
-                       readahead_tree_block(root, block2, blocksize, 0);
 
-               if (block1) {
-                       eb = read_tree_block(root, block1, blocksize, 0);
-                       free_extent_buffer(eb);
-               }
-               if (block2) {
-                       eb = read_tree_block(root, block2, blocksize, 0);
-                       free_extent_buffer(eb);
-               }
-       }
-       return ret;
+       if (block1)
+               readahead_tree_block(root, block1, blocksize, 0);
+       if (block2)
+               readahead_tree_block(root, block2, blocksize, 0);
 }
 
 
@@ -2359,35 +2340,28 @@ read_block_for_search(struct btrfs_trans_handle *trans,
        tmp = btrfs_find_tree_block(root, blocknr, blocksize);
        if (tmp) {
                /* first we do an atomic uptodate check */
-               if (btrfs_buffer_uptodate(tmp, 0, 1) > 0) {
-                       if (btrfs_buffer_uptodate(tmp, gen, 1) > 0) {
-                               /*
-                                * we found an up to date block without
-                                * sleeping, return
-                                * right away
-                                */
-                               *eb_ret = tmp;
-                               return 0;
-                       }
-                       /* the pages were up to date, but we failed
-                        * the generation number check.  Do a full
-                        * read for the generation number that is correct.
-                        * We must do this without dropping locks so
-                        * we can trust our generation number
-                        */
-                       free_extent_buffer(tmp);
-                       btrfs_set_path_blocking(p);
+               if (btrfs_buffer_uptodate(tmp, gen, 1) > 0) {
+                       *eb_ret = tmp;
+                       return 0;
+               }
 
-                       /* now we're allowed to do a blocking uptodate check */
-                       tmp = read_tree_block(root, blocknr, blocksize, gen);
-                       if (tmp && btrfs_buffer_uptodate(tmp, gen, 0) > 0) {
-                               *eb_ret = tmp;
-                               return 0;
-                       }
-                       free_extent_buffer(tmp);
-                       btrfs_release_path(p);
-                       return -EIO;
+               /* the pages were up to date, but we failed
+                * the generation number check.  Do a full
+                * read for the generation number that is correct.
+                * We must do this without dropping locks so
+                * we can trust our generation number
+                */
+               btrfs_set_path_blocking(p);
+
+               /* now we're allowed to do a blocking uptodate check */
+               ret = btrfs_read_buffer(tmp, gen);
+               if (!ret) {
+                       *eb_ret = tmp;
+                       return 0;
                }
+               free_extent_buffer(tmp);
+               btrfs_release_path(p);
+               return -EIO;
        }
 
        /*
@@ -2448,11 +2422,8 @@ setup_nodes_for_search(struct btrfs_trans_handle *trans,
                        goto again;
                }
 
-               sret = reada_for_balance(root, p, level);
-               if (sret)
-                       goto again;
-
                btrfs_set_path_blocking(p);
+               reada_for_balance(root, p, level);
                sret = split_node(trans, root, p, level);
                btrfs_clear_path_blocking(p, NULL, 0);
 
@@ -2472,11 +2443,8 @@ setup_nodes_for_search(struct btrfs_trans_handle *trans,
                        goto again;
                }
 
-               sret = reada_for_balance(root, p, level);
-               if (sret)
-                       goto again;
-
                btrfs_set_path_blocking(p);
+               reada_for_balance(root, p, level);
                sret = balance_level(trans, root, p, level);
                btrfs_clear_path_blocking(p, NULL, 0);
 
@@ -3143,7 +3111,7 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
  */
 static noinline int insert_new_root(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root,
-                          struct btrfs_path *path, int level, int log_removal)
+                          struct btrfs_path *path, int level)
 {
        u64 lower_gen;
        struct extent_buffer *lower;
@@ -3194,7 +3162,7 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
        btrfs_mark_buffer_dirty(c);
 
        old = root->node;
-       tree_mod_log_set_root_pointer(root, c, log_removal);
+       tree_mod_log_set_root_pointer(root, c, 0);
        rcu_assign_pointer(root->node, c);
 
        /* the super has an extra ref to root->node */
@@ -3278,14 +3246,14 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
                /*
                 * trying to split the root, lets make a new one
                 *
-                * tree mod log: We pass 0 as log_removal parameter to
+                * tree mod log: We don't log_removal old root in
                 * insert_new_root, because that root buffer will be kept as a
                 * normal node. We are going to log removal of half of the
                 * elements below with tree_mod_log_eb_copy. We're holding a
                 * tree lock on the buffer, which is why we cannot race with
                 * other tree_mod_log users.
                 */
-               ret = insert_new_root(trans, root, path, level + 1, 0);
+               ret = insert_new_root(trans, root, path, level + 1);
                if (ret)
                        return ret;
        } else {
@@ -3986,7 +3954,7 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
                return -EOVERFLOW;
 
        /* first try to make some room by pushing left and right */
-       if (data_size) {
+       if (data_size && path->nodes[1]) {
                wret = push_leaf_right(trans, root, path, data_size,
                                       data_size, 0, 0);
                if (wret < 0)
@@ -4005,7 +3973,7 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
        }
 
        if (!path->nodes[1]) {
-               ret = insert_new_root(trans, root, path, 1, 1);
+               ret = insert_new_root(trans, root, path, 1);
                if (ret)
                        return ret;
        }
index d6dd49b..e795bf1 100644 (file)
@@ -961,8 +961,8 @@ struct btrfs_dev_replace_item {
 #define BTRFS_BLOCK_GROUP_RAID1                (1ULL << 4)
 #define BTRFS_BLOCK_GROUP_DUP          (1ULL << 5)
 #define BTRFS_BLOCK_GROUP_RAID10       (1ULL << 6)
-#define BTRFS_BLOCK_GROUP_RAID5    (1 << 7)
-#define BTRFS_BLOCK_GROUP_RAID6    (1 << 8)
+#define BTRFS_BLOCK_GROUP_RAID5         (1ULL << 7)
+#define BTRFS_BLOCK_GROUP_RAID6         (1ULL << 8)
 #define BTRFS_BLOCK_GROUP_RESERVED     BTRFS_AVAIL_ALLOC_BIT_SINGLE
 
 enum btrfs_raid_types {
@@ -1101,6 +1101,18 @@ struct btrfs_space_info {
        u64 disk_total;         /* total bytes on disk, takes mirrors into
                                   account */
 
+       /*
+        * bytes_pinned is kept in line with what is actually pinned, as in
+        * we've called update_block_group and dropped the bytes_used counter
+        * and increased the bytes_pinned counter.  However this means that
+        * bytes_pinned does not reflect the bytes that will be pinned once the
+        * delayed refs are flushed, so this counter is inc'ed everytime we call
+        * btrfs_free_extent so it is a realtime count of what will be freed
+        * once the transaction is committed.  It will be zero'ed everytime the
+        * transaction commits.
+        */
+       struct percpu_counter total_bytes_pinned;
+
        /*
         * we bump reservation progress every time we decrement
         * bytes_reserved.  This way people waiting for reservations
@@ -1437,25 +1449,22 @@ struct btrfs_fs_info {
        atomic_t open_ioctl_trans;
 
        /*
-        * this is used by the balancing code to wait for all the pending
-        * ordered extents
+        * this is used to protect the following list -- ordered_roots.
         */
-       spinlock_t ordered_extent_lock;
+       spinlock_t ordered_root_lock;
 
        /*
-        * all of the data=ordered extents pending writeback
+        * all fs/file tree roots in which there are data=ordered extents
+        * pending writeback are added into this list.
+        *
         * these can span multiple transactions and basically include
         * every dirty data page that isn't from nodatacow
         */
-       struct list_head ordered_extents;
+       struct list_head ordered_roots;
 
-       spinlock_t delalloc_lock;
-       /*
-        * all of the inodes that have delalloc bytes.  It is possible for
-        * this list to be empty even when there is still dirty data=ordered
-        * extents waiting to finish IO.
-        */
-       struct list_head delalloc_inodes;
+       spinlock_t delalloc_root_lock;
+       /* all fs/file tree roots that have delalloc inodes. */
+       struct list_head delalloc_roots;
 
        /*
         * there is a pool of worker threads for checksumming during writes
@@ -1498,8 +1507,6 @@ struct btrfs_fs_info {
        int do_barriers;
        int closing;
        int log_root_recovering;
-       int enospc_unlink;
-       int trans_no_join;
 
        u64 total_pinned;
 
@@ -1594,6 +1601,12 @@ struct btrfs_fs_info {
        struct rb_root qgroup_tree;
        spinlock_t qgroup_lock;
 
+       /*
+        * used to avoid frequently calling ulist_alloc()/ulist_free()
+        * when doing qgroup accounting, it must be protected by qgroup_lock.
+        */
+       struct ulist *qgroup_ulist;
+
        /* protect user change for quota operations */
        struct mutex qgroup_ioctl_lock;
 
@@ -1607,6 +1620,8 @@ struct btrfs_fs_info {
        struct mutex qgroup_rescan_lock; /* protects the progress item */
        struct btrfs_key qgroup_rescan_progress;
        struct btrfs_workers qgroup_rescan_workers;
+       struct completion qgroup_rescan_completion;
+       struct btrfs_work qgroup_rescan_work;
 
        /* filesystem state */
        unsigned long fs_state;
@@ -1739,6 +1754,31 @@ struct btrfs_root {
        int force_cow;
 
        spinlock_t root_item_lock;
+       atomic_t refs;
+
+       spinlock_t delalloc_lock;
+       /*
+        * all of the inodes that have delalloc bytes.  It is possible for
+        * this list to be empty even when there is still dirty data=ordered
+        * extents waiting to finish IO.
+        */
+       struct list_head delalloc_inodes;
+       struct list_head delalloc_root;
+       u64 nr_delalloc_inodes;
+       /*
+        * this is used by the balancing code to wait for all the pending
+        * ordered extents
+        */
+       spinlock_t ordered_extent_lock;
+
+       /*
+        * all of the data=ordered extents pending writeback
+        * these can span multiple transactions and basically include
+        * every dirty data page that isn't from nodatacow
+        */
+       struct list_head ordered_extents;
+       struct list_head ordered_root;
+       u64 nr_ordered_extents;
 };
 
 struct btrfs_ioctl_defrag_range_args {
@@ -3028,6 +3068,8 @@ static inline u64 btrfs_calc_trunc_metadata_size(struct btrfs_root *root,
                num_items;
 }
 
+int btrfs_should_throttle_delayed_refs(struct btrfs_trans_handle *trans,
+                                      struct btrfs_root *root);
 void btrfs_put_block_group(struct btrfs_block_group_cache *cache);
 int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root, unsigned long count);
@@ -3039,6 +3081,8 @@ int btrfs_pin_extent(struct btrfs_root *root,
                     u64 bytenr, u64 num, int reserved);
 int btrfs_pin_extent_for_log_replay(struct btrfs_root *root,
                                    u64 bytenr, u64 num_bytes);
+int btrfs_exclude_logged_extents(struct btrfs_root *root,
+                                struct extent_buffer *eb);
 int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans,
                          struct btrfs_root *root,
                          u64 objectid, u64 offset, u64 bytenr);
@@ -3155,6 +3199,9 @@ int btrfs_block_rsv_refill(struct btrfs_root *root,
 int btrfs_block_rsv_migrate(struct btrfs_block_rsv *src_rsv,
                            struct btrfs_block_rsv *dst_rsv,
                            u64 num_bytes);
+int btrfs_cond_migrate_bytes(struct btrfs_fs_info *fs_info,
+                            struct btrfs_block_rsv *dest, u64 num_bytes,
+                            int min_factor);
 void btrfs_block_rsv_release(struct btrfs_root *root,
                             struct btrfs_block_rsv *block_rsv,
                             u64 num_bytes);
@@ -3311,6 +3358,18 @@ static inline int btrfs_fs_closing(struct btrfs_fs_info *fs_info)
        smp_mb();
        return fs_info->closing;
 }
+
+/*
+ * If we remount the fs to be R/O or umount the fs, the cleaner needn't do
+ * anything except sleeping. This function is used to check the status of
+ * the fs.
+ */
+static inline int btrfs_need_cleaner_sleep(struct btrfs_root *root)
+{
+       return (root->fs_info->sb->s_flags & MS_RDONLY ||
+               btrfs_fs_closing(root->fs_info));
+}
+
 static inline void free_fs_info(struct btrfs_fs_info *fs_info)
 {
        kfree(fs_info->balance_ctl);
@@ -3357,9 +3416,9 @@ int __must_check btrfs_update_root(struct btrfs_trans_handle *trans,
                                   struct btrfs_root_item *item);
 void btrfs_read_root_item(struct extent_buffer *eb, int slot,
                          struct btrfs_root_item *item);
-int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct
-                        btrfs_root_item *item, struct btrfs_key *key);
-int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
+int btrfs_find_root(struct btrfs_root *root, struct btrfs_key *search_key,
+                   struct btrfs_path *path, struct btrfs_root_item *root_item,
+                   struct btrfs_key *root_key);
 int btrfs_find_orphan_roots(struct btrfs_root *tree_root);
 void btrfs_set_root_node(struct btrfs_root_item *item,
                         struct extent_buffer *node);
@@ -3493,6 +3552,10 @@ void btrfs_wait_and_free_delalloc_work(struct btrfs_delalloc_work *work);
 struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *page,
                                           size_t pg_offset, u64 start, u64 len,
                                           int create);
+noinline int can_nocow_extent(struct btrfs_trans_handle *trans,
+                             struct inode *inode, u64 offset, u64 *len,
+                             u64 *orig_start, u64 *orig_block_len,
+                             u64 *ram_bytes);
 
 /* RHEL and EL kernels have a patch that renames PG_checked to FsMisc */
 #if defined(ClearPageFsMisc) && !defined(ClearPageChecked)
@@ -3530,6 +3593,8 @@ 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_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,
@@ -3814,6 +3879,8 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
 int btrfs_quota_disable(struct btrfs_trans_handle *trans,
                        struct btrfs_fs_info *fs_info);
 int btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info);
+void btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info);
+int btrfs_qgroup_wait_for_completion(struct btrfs_fs_info *fs_info);
 int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
                              struct btrfs_fs_info *fs_info, u64 src, u64 dst);
 int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans,
index eb34438..3755109 100644 (file)
@@ -535,20 +535,6 @@ static struct btrfs_delayed_item *__btrfs_next_delayed_item(
        return next;
 }
 
-static inline struct btrfs_root *btrfs_get_fs_root(struct btrfs_root *root,
-                                                  u64 root_id)
-{
-       struct btrfs_key root_key;
-
-       if (root->objectid == root_id)
-               return root;
-
-       root_key.objectid = root_id;
-       root_key.type = BTRFS_ROOT_ITEM_KEY;
-       root_key.offset = (u64)-1;
-       return btrfs_read_fs_root_no_name(root->fs_info, &root_key);
-}
-
 static int btrfs_delayed_item_reserve_metadata(struct btrfs_trans_handle *trans,
                                               struct btrfs_root *root,
                                               struct btrfs_delayed_item *item)
index 65241f3..4253ad5 100644 (file)
@@ -400,7 +400,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_ordered_extents(root, 0);
+       btrfs_wait_all_ordered_extents(root->fs_info, 0);
 
        /* force writing the updated state information to disk */
        trans = btrfs_start_transaction(root, 0);
@@ -470,12 +470,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_delalloc_inodes(root, 0);
+       ret = btrfs_start_all_delalloc_inodes(root->fs_info, 0);
        if (ret) {
                mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
                return ret;
        }
-       btrfs_wait_ordered_extents(root, 0);
+       btrfs_wait_all_ordered_extents(root->fs_info, 0);
 
        trans = btrfs_start_transaction(root, 0);
        if (IS_ERR(trans)) {
index b0292b3..6b092a1 100644 (file)
@@ -1192,6 +1192,8 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
        root->objectid = objectid;
        root->last_trans = 0;
        root->highest_objectid = 0;
+       root->nr_delalloc_inodes = 0;
+       root->nr_ordered_extents = 0;
        root->name = NULL;
        root->inode_tree = RB_ROOT;
        INIT_RADIX_TREE(&root->delayed_nodes_tree, GFP_ATOMIC);
@@ -1200,10 +1202,16 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
 
        INIT_LIST_HEAD(&root->dirty_list);
        INIT_LIST_HEAD(&root->root_list);
+       INIT_LIST_HEAD(&root->delalloc_inodes);
+       INIT_LIST_HEAD(&root->delalloc_root);
+       INIT_LIST_HEAD(&root->ordered_extents);
+       INIT_LIST_HEAD(&root->ordered_root);
        INIT_LIST_HEAD(&root->logged_list[0]);
        INIT_LIST_HEAD(&root->logged_list[1]);
        spin_lock_init(&root->orphan_lock);
        spin_lock_init(&root->inode_lock);
+       spin_lock_init(&root->delalloc_lock);
+       spin_lock_init(&root->ordered_extent_lock);
        spin_lock_init(&root->accounting_lock);
        spin_lock_init(&root->log_extents_lock[0]);
        spin_lock_init(&root->log_extents_lock[1]);
@@ -1217,6 +1225,7 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
        atomic_set(&root->log_writers, 0);
        atomic_set(&root->log_batch, 0);
        atomic_set(&root->orphan_inodes, 0);
+       atomic_set(&root->refs, 1);
        root->log_transid = 0;
        root->last_log_commit = 0;
        extent_io_tree_init(&root->dirty_log_pages,
@@ -1235,39 +1244,6 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
        spin_lock_init(&root->root_item_lock);
 }
 
-static int __must_check find_and_setup_root(struct btrfs_root *tree_root,
-                                           struct btrfs_fs_info *fs_info,
-                                           u64 objectid,
-                                           struct btrfs_root *root)
-{
-       int ret;
-       u32 blocksize;
-       u64 generation;
-
-       __setup_root(tree_root->nodesize, tree_root->leafsize,
-                    tree_root->sectorsize, tree_root->stripesize,
-                    root, fs_info, objectid);
-       ret = btrfs_find_last_root(tree_root, objectid,
-                                  &root->root_item, &root->root_key);
-       if (ret > 0)
-               return -ENOENT;
-       else if (ret < 0)
-               return ret;
-
-       generation = btrfs_root_generation(&root->root_item);
-       blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
-       root->commit_root = NULL;
-       root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
-                                    blocksize, generation);
-       if (!root->node || !btrfs_buffer_uptodate(root->node, generation, 0)) {
-               free_extent_buffer(root->node);
-               root->node = NULL;
-               return -EIO;
-       }
-       root->commit_root = btrfs_root_node(root);
-       return 0;
-}
-
 static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info)
 {
        struct btrfs_root *root = kzalloc(sizeof(*root), GFP_NOFS);
@@ -1452,70 +1428,73 @@ int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
        return 0;
 }
 
-struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
-                                              struct btrfs_key *location)
+struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root,
+                                       struct btrfs_key *key)
 {
        struct btrfs_root *root;
        struct btrfs_fs_info *fs_info = tree_root->fs_info;
        struct btrfs_path *path;
-       struct extent_buffer *l;
        u64 generation;
        u32 blocksize;
-       int ret = 0;
-       int slot;
+       int ret;
 
-       root = btrfs_alloc_root(fs_info);
-       if (!root)
+       path = btrfs_alloc_path();
+       if (!path)
                return ERR_PTR(-ENOMEM);
-       if (location->offset == (u64)-1) {
-               ret = find_and_setup_root(tree_root, fs_info,
-                                         location->objectid, root);
-               if (ret) {
-                       kfree(root);
-                       return ERR_PTR(ret);
-               }
-               goto out;
+
+       root = btrfs_alloc_root(fs_info);
+       if (!root) {
+               ret = -ENOMEM;
+               goto alloc_fail;
        }
 
        __setup_root(tree_root->nodesize, tree_root->leafsize,
                     tree_root->sectorsize, tree_root->stripesize,
-                    root, fs_info, location->objectid);
+                    root, fs_info, key->objectid);
 
-       path = btrfs_alloc_path();
-       if (!path) {
-               kfree(root);
-               return ERR_PTR(-ENOMEM);
-       }
-       ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0);
-       if (ret == 0) {
-               l = path->nodes[0];
-               slot = path->slots[0];
-               btrfs_read_root_item(l, slot, &root->root_item);
-               memcpy(&root->root_key, location, sizeof(*location));
-       }
-       btrfs_free_path(path);
+       ret = btrfs_find_root(tree_root, key, path,
+                             &root->root_item, &root->root_key);
        if (ret) {
-               kfree(root);
                if (ret > 0)
                        ret = -ENOENT;
-               return ERR_PTR(ret);
+               goto find_fail;
        }
 
        generation = btrfs_root_generation(&root->root_item);
        blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
        root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
                                     blocksize, generation);
-       if (!root->node || !extent_buffer_uptodate(root->node)) {
-               ret = (!root->node) ? -ENOMEM : -EIO;
-
-               free_extent_buffer(root->node);
-               kfree(root);
-               return ERR_PTR(ret);
+       if (!root->node) {
+               ret = -ENOMEM;
+               goto find_fail;
+       } else if (!btrfs_buffer_uptodate(root->node, generation, 0)) {
+               ret = -EIO;
+               goto read_fail;
        }
-
        root->commit_root = btrfs_root_node(root);
 out:
-       if (location->objectid != BTRFS_TREE_LOG_OBJECTID) {
+       btrfs_free_path(path);
+       return root;
+
+read_fail:
+       free_extent_buffer(root->node);
+find_fail:
+       kfree(root);
+alloc_fail:
+       root = ERR_PTR(ret);
+       goto out;
+}
+
+struct btrfs_root *btrfs_read_fs_root(struct btrfs_root *tree_root,
+                                     struct btrfs_key *location)
+{
+       struct btrfs_root *root;
+
+       root = btrfs_read_tree_root(tree_root, location);
+       if (IS_ERR(root))
+               return root;
+
+       if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) {
                root->ref_cows = 1;
                btrfs_check_and_init_root_item(&root->root_item);
        }
@@ -1523,6 +1502,66 @@ out:
        return root;
 }
 
+int btrfs_init_fs_root(struct btrfs_root *root)
+{
+       int ret;
+
+       root->free_ino_ctl = kzalloc(sizeof(*root->free_ino_ctl), GFP_NOFS);
+       root->free_ino_pinned = kzalloc(sizeof(*root->free_ino_pinned),
+                                       GFP_NOFS);
+       if (!root->free_ino_pinned || !root->free_ino_ctl) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       btrfs_init_free_ino_ctl(root);
+       mutex_init(&root->fs_commit_mutex);
+       spin_lock_init(&root->cache_lock);
+       init_waitqueue_head(&root->cache_wait);
+
+       ret = get_anon_bdev(&root->anon_dev);
+       if (ret)
+               goto fail;
+       return 0;
+fail:
+       kfree(root->free_ino_ctl);
+       kfree(root->free_ino_pinned);
+       return ret;
+}
+
+struct btrfs_root *btrfs_lookup_fs_root(struct btrfs_fs_info *fs_info,
+                                       u64 root_id)
+{
+       struct btrfs_root *root;
+
+       spin_lock(&fs_info->fs_roots_radix_lock);
+       root = radix_tree_lookup(&fs_info->fs_roots_radix,
+                                (unsigned long)root_id);
+       spin_unlock(&fs_info->fs_roots_radix_lock);
+       return root;
+}
+
+int btrfs_insert_fs_root(struct btrfs_fs_info *fs_info,
+                        struct btrfs_root *root)
+{
+       int ret;
+
+       ret = radix_tree_preload(GFP_NOFS & ~__GFP_HIGHMEM);
+       if (ret)
+               return ret;
+
+       spin_lock(&fs_info->fs_roots_radix_lock);
+       ret = radix_tree_insert(&fs_info->fs_roots_radix,
+                               (unsigned long)root->root_key.objectid,
+                               root);
+       if (ret == 0)
+               root->in_radix = 1;
+       spin_unlock(&fs_info->fs_roots_radix_lock);
+       radix_tree_preload_end();
+
+       return ret;
+}
+
 struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
                                              struct btrfs_key *location)
 {
@@ -1543,58 +1582,30 @@ struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
                return fs_info->quota_root ? fs_info->quota_root :
                                             ERR_PTR(-ENOENT);
 again:
-       spin_lock(&fs_info->fs_roots_radix_lock);
-       root = radix_tree_lookup(&fs_info->fs_roots_radix,
-                                (unsigned long)location->objectid);
-       spin_unlock(&fs_info->fs_roots_radix_lock);
+       root = btrfs_lookup_fs_root(fs_info, location->objectid);
        if (root)
                return root;
 
-       root = btrfs_read_fs_root_no_radix(fs_info->tree_root, location);
+       root = btrfs_read_fs_root(fs_info->tree_root, location);
        if (IS_ERR(root))
                return root;
 
-       root->free_ino_ctl = kzalloc(sizeof(*root->free_ino_ctl), GFP_NOFS);
-       root->free_ino_pinned = kzalloc(sizeof(*root->free_ino_pinned),
-                                       GFP_NOFS);
-       if (!root->free_ino_pinned || !root->free_ino_ctl) {
-               ret = -ENOMEM;
+       if (btrfs_root_refs(&root->root_item) == 0) {
+               ret = -ENOENT;
                goto fail;
        }
 
-       btrfs_init_free_ino_ctl(root);
-       mutex_init(&root->fs_commit_mutex);
-       spin_lock_init(&root->cache_lock);
-       init_waitqueue_head(&root->cache_wait);
-
-       ret = get_anon_bdev(&root->anon_dev);
+       ret = btrfs_init_fs_root(root);
        if (ret)
                goto fail;
 
-       if (btrfs_root_refs(&root->root_item) == 0) {
-               ret = -ENOENT;
-               goto fail;
-       }
-
        ret = btrfs_find_orphan_item(fs_info->tree_root, location->objectid);
        if (ret < 0)
                goto fail;
        if (ret == 0)
                root->orphan_item_inserted = 1;
 
-       ret = radix_tree_preload(GFP_NOFS & ~__GFP_HIGHMEM);
-       if (ret)
-               goto fail;
-
-       spin_lock(&fs_info->fs_roots_radix_lock);
-       ret = radix_tree_insert(&fs_info->fs_roots_radix,
-                               (unsigned long)root->root_key.objectid,
-                               root);
-       if (ret == 0)
-               root->in_radix = 1;
-
-       spin_unlock(&fs_info->fs_roots_radix_lock);
-       radix_tree_preload_end();
+       ret = btrfs_insert_fs_root(fs_info, root);
        if (ret) {
                if (ret == -EEXIST) {
                        free_fs_root(root);
@@ -1602,10 +1613,6 @@ again:
                }
                goto fail;
        }
-
-       ret = btrfs_find_dead_roots(fs_info->tree_root,
-                                   root->root_key.objectid);
-       WARN_ON(ret);
        return root;
 fail:
        free_fs_root(root);
@@ -1677,21 +1684,37 @@ static void end_workqueue_fn(struct btrfs_work *work)
 static int cleaner_kthread(void *arg)
 {
        struct btrfs_root *root = arg;
+       int again;
 
        do {
-               int again = 0;
-
-               if (!(root->fs_info->sb->s_flags & MS_RDONLY) &&
-                   down_read_trylock(&root->fs_info->sb->s_umount)) {
-                       if (mutex_trylock(&root->fs_info->cleaner_mutex)) {
-                               btrfs_run_delayed_iputs(root);
-                               again = btrfs_clean_one_deleted_snapshot(root);
-                               mutex_unlock(&root->fs_info->cleaner_mutex);
-                       }
-                       btrfs_run_defrag_inodes(root->fs_info);
-                       up_read(&root->fs_info->sb->s_umount);
+               again = 0;
+
+               /* Make the cleaner go to sleep early. */
+               if (btrfs_need_cleaner_sleep(root))
+                       goto sleep;
+
+               if (!mutex_trylock(&root->fs_info->cleaner_mutex))
+                       goto sleep;
+
+               /*
+                * Avoid the problem that we change the status of the fs
+                * during the above check and trylock.
+                */
+               if (btrfs_need_cleaner_sleep(root)) {
+                       mutex_unlock(&root->fs_info->cleaner_mutex);
+                       goto sleep;
                }
 
+               btrfs_run_delayed_iputs(root);
+               again = btrfs_clean_one_deleted_snapshot(root);
+               mutex_unlock(&root->fs_info->cleaner_mutex);
+
+               /*
+                * The defragger has dealt with the R/O remount and umount,
+                * needn't do anything special here.
+                */
+               btrfs_run_defrag_inodes(root->fs_info);
+sleep:
                if (!try_to_freeze() && !again) {
                        set_current_state(TASK_INTERRUPTIBLE);
                        if (!kthread_should_stop())
@@ -1725,7 +1748,7 @@ static int transaction_kthread(void *arg)
                }
 
                now = get_seconds();
-               if (!cur->blocked &&
+               if (cur->state < TRANS_STATE_BLOCKED &&
                    (now < cur->start_time || now - cur->start_time < 30)) {
                        spin_unlock(&root->fs_info->trans_lock);
                        delay = HZ * 5;
@@ -2035,11 +2058,11 @@ static void del_fs_roots(struct btrfs_fs_info *fs_info)
                list_del(&gang[0]->root_list);
 
                if (gang[0]->in_radix) {
-                       btrfs_free_fs_root(fs_info, gang[0]);
+                       btrfs_drop_and_free_fs_root(fs_info, gang[0]);
                } else {
                        free_extent_buffer(gang[0]->node);
                        free_extent_buffer(gang[0]->commit_root);
-                       kfree(gang[0]);
+                       btrfs_put_fs_root(gang[0]);
                }
        }
 
@@ -2050,7 +2073,7 @@ static void del_fs_roots(struct btrfs_fs_info *fs_info)
                if (!ret)
                        break;
                for (i = 0; i < ret; i++)
-                       btrfs_free_fs_root(fs_info, gang[i]);
+                       btrfs_drop_and_free_fs_root(fs_info, gang[i]);
        }
 }
 
@@ -2082,14 +2105,8 @@ int open_ctree(struct super_block *sb,
        int backup_index = 0;
 
        tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info);
-       extent_root = fs_info->extent_root = btrfs_alloc_root(fs_info);
-       csum_root = fs_info->csum_root = btrfs_alloc_root(fs_info);
        chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info);
-       dev_root = fs_info->dev_root = btrfs_alloc_root(fs_info);
-       quota_root = fs_info->quota_root = btrfs_alloc_root(fs_info);
-
-       if (!tree_root || !extent_root || !csum_root ||
-           !chunk_root || !dev_root || !quota_root) {
+       if (!tree_root || !chunk_root) {
                err = -ENOMEM;
                goto fail;
        }
@@ -2132,9 +2149,9 @@ int open_ctree(struct super_block *sb,
        INIT_LIST_HEAD(&fs_info->trans_list);
        INIT_LIST_HEAD(&fs_info->dead_roots);
        INIT_LIST_HEAD(&fs_info->delayed_iputs);
-       INIT_LIST_HEAD(&fs_info->delalloc_inodes);
+       INIT_LIST_HEAD(&fs_info->delalloc_roots);
        INIT_LIST_HEAD(&fs_info->caching_block_groups);
-       spin_lock_init(&fs_info->delalloc_lock);
+       spin_lock_init(&fs_info->delalloc_root_lock);
        spin_lock_init(&fs_info->trans_lock);
        spin_lock_init(&fs_info->fs_roots_radix_lock);
        spin_lock_init(&fs_info->delayed_iput_lock);
@@ -2170,7 +2187,6 @@ int open_ctree(struct super_block *sb,
        fs_info->max_inline = 8192 * 1024;
        fs_info->metadata_ratio = 0;
        fs_info->defrag_inodes = RB_ROOT;
-       fs_info->trans_no_join = 0;
        fs_info->free_chunk_space = 0;
        fs_info->tree_mod_log = RB_ROOT;
 
@@ -2181,8 +2197,8 @@ int open_ctree(struct super_block *sb,
        fs_info->thread_pool_size = min_t(unsigned long,
                                          num_online_cpus() + 2, 8);
 
-       INIT_LIST_HEAD(&fs_info->ordered_extents);
-       spin_lock_init(&fs_info->ordered_extent_lock);
+       INIT_LIST_HEAD(&fs_info->ordered_roots);
+       spin_lock_init(&fs_info->ordered_root_lock);
        fs_info->delayed_root = kmalloc(sizeof(struct btrfs_delayed_root),
                                        GFP_NOFS);
        if (!fs_info->delayed_root) {
@@ -2275,6 +2291,7 @@ int open_ctree(struct super_block *sb,
        fs_info->qgroup_seq = 1;
        fs_info->quota_enabled = 0;
        fs_info->pending_quota_state = 0;
+       fs_info->qgroup_ulist = NULL;
        mutex_init(&fs_info->qgroup_rescan_lock);
 
        btrfs_init_free_cluster(&fs_info->meta_alloc_cluster);
@@ -2639,33 +2656,44 @@ retry_root_backup:
        btrfs_set_root_node(&tree_root->root_item, tree_root->node);
        tree_root->commit_root = btrfs_root_node(tree_root);
 
-       ret = find_and_setup_root(tree_root, fs_info,
-                                 BTRFS_EXTENT_TREE_OBJECTID, extent_root);
-       if (ret)
+       location.objectid = BTRFS_EXTENT_TREE_OBJECTID;
+       location.type = BTRFS_ROOT_ITEM_KEY;
+       location.offset = 0;
+
+       extent_root = btrfs_read_tree_root(tree_root, &location);
+       if (IS_ERR(extent_root)) {
+               ret = PTR_ERR(extent_root);
                goto recovery_tree_root;
+       }
        extent_root->track_dirty = 1;
+       fs_info->extent_root = extent_root;
 
-       ret = find_and_setup_root(tree_root, fs_info,
-                                 BTRFS_DEV_TREE_OBJECTID, dev_root);
-       if (ret)
+       location.objectid = BTRFS_DEV_TREE_OBJECTID;
+       dev_root = btrfs_read_tree_root(tree_root, &location);
+       if (IS_ERR(dev_root)) {
+               ret = PTR_ERR(dev_root);
                goto recovery_tree_root;
+       }
        dev_root->track_dirty = 1;
+       fs_info->dev_root = dev_root;
+       btrfs_init_devices_late(fs_info);
 
-       ret = find_and_setup_root(tree_root, fs_info,
-                                 BTRFS_CSUM_TREE_OBJECTID, csum_root);
-       if (ret)
+       location.objectid = BTRFS_CSUM_TREE_OBJECTID;
+       csum_root = btrfs_read_tree_root(tree_root, &location);
+       if (IS_ERR(csum_root)) {
+               ret = PTR_ERR(csum_root);
                goto recovery_tree_root;
+       }
        csum_root->track_dirty = 1;
+       fs_info->csum_root = csum_root;
 
-       ret = find_and_setup_root(tree_root, fs_info,
-                                 BTRFS_QUOTA_TREE_OBJECTID, quota_root);
-       if (ret) {
-               kfree(quota_root);
-               quota_root = fs_info->quota_root = NULL;
-       } else {
+       location.objectid = BTRFS_QUOTA_TREE_OBJECTID;
+       quota_root = btrfs_read_tree_root(tree_root, &location);
+       if (!IS_ERR(quota_root)) {
                quota_root->track_dirty = 1;
                fs_info->quota_enabled = 1;
                fs_info->pending_quota_state = 1;
+               fs_info->quota_root = quota_root;
        }
 
        fs_info->generation = generation;
@@ -2818,11 +2846,9 @@ retry_root_backup:
 
        location.objectid = BTRFS_FS_TREE_OBJECTID;
        location.type = BTRFS_ROOT_ITEM_KEY;
-       location.offset = (u64)-1;
+       location.offset = 0;
 
        fs_info->fs_root = btrfs_read_fs_root_no_name(fs_info, &location);
-       if (!fs_info->fs_root)
-               goto fail_qgroup;
        if (IS_ERR(fs_info->fs_root)) {
                err = PTR_ERR(fs_info->fs_root);
                goto fail_qgroup;
@@ -2854,6 +2880,8 @@ retry_root_backup:
                return ret;
        }
 
+       btrfs_qgroup_rescan_resume(fs_info);
+
        return 0;
 
 fail_qgroup:
@@ -3259,7 +3287,7 @@ int btrfs_calc_num_tolerated_disk_barrier_failures(
                                            BTRFS_BLOCK_GROUP_RAID10)) {
                                                num_tolerated_disk_barrier_failures = 1;
                                        } else if (flags &
-                                                  BTRFS_BLOCK_GROUP_RAID5) {
+                                                  BTRFS_BLOCK_GROUP_RAID6) {
                                                num_tolerated_disk_barrier_failures = 2;
                                        }
                                }
@@ -3367,7 +3395,9 @@ int write_ctree_super(struct btrfs_trans_handle *trans,
        return ret;
 }
 
-void btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
+/* Drop a fs root from the radix tree and free it. */
+void btrfs_drop_and_free_fs_root(struct btrfs_fs_info *fs_info,
+                                 struct btrfs_root *root)
 {
        spin_lock(&fs_info->fs_roots_radix_lock);
        radix_tree_delete(&fs_info->fs_roots_radix,
@@ -3398,7 +3428,12 @@ static void free_fs_root(struct btrfs_root *root)
        kfree(root->free_ino_ctl);
        kfree(root->free_ino_pinned);
        kfree(root->name);
-       kfree(root);
+       btrfs_put_fs_root(root);
+}
+
+void btrfs_free_fs_root(struct btrfs_root *root)
+{
+       free_fs_root(root);
 }
 
 int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info)
@@ -3654,7 +3689,7 @@ static void btrfs_destroy_ordered_operations(struct btrfs_transaction *t,
        INIT_LIST_HEAD(&splice);
 
        mutex_lock(&root->fs_info->ordered_operations_mutex);
-       spin_lock(&root->fs_info->ordered_extent_lock);
+       spin_lock(&root->fs_info->ordered_root_lock);
 
        list_splice_init(&t->ordered_operations, &splice);
        while (!list_empty(&splice)) {
@@ -3662,14 +3697,14 @@ static void btrfs_destroy_ordered_operations(struct btrfs_transaction *t,
                                         ordered_operations);
 
                list_del_init(&btrfs_inode->ordered_operations);
-               spin_unlock(&root->fs_info->ordered_extent_lock);
+               spin_unlock(&root->fs_info->ordered_root_lock);
 
                btrfs_invalidate_inodes(btrfs_inode->root);
 
-               spin_lock(&root->fs_info->ordered_extent_lock);
+               spin_lock(&root->fs_info->ordered_root_lock);
        }
 
-       spin_unlock(&root->fs_info->ordered_extent_lock);
+       spin_unlock(&root->fs_info->ordered_root_lock);
        mutex_unlock(&root->fs_info->ordered_operations_mutex);
 }
 
@@ -3677,15 +3712,36 @@ static void btrfs_destroy_ordered_extents(struct btrfs_root *root)
 {
        struct btrfs_ordered_extent *ordered;
 
-       spin_lock(&root->fs_info->ordered_extent_lock);
+       spin_lock(&root->ordered_extent_lock);
        /*
         * This will just short circuit the ordered completion stuff which will
         * make sure the ordered extent gets properly cleaned up.
         */
-       list_for_each_entry(ordered, &root->fs_info->ordered_extents,
+       list_for_each_entry(ordered, &root->ordered_extents,
                            root_extent_list)
                set_bit(BTRFS_ORDERED_IOERR, &ordered->flags);
-       spin_unlock(&root->fs_info->ordered_extent_lock);
+       spin_unlock(&root->ordered_extent_lock);
+}
+
+static void btrfs_destroy_all_ordered_extents(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_root *root;
+       struct list_head splice;
+
+       INIT_LIST_HEAD(&splice);
+
+       spin_lock(&fs_info->ordered_root_lock);
+       list_splice_init(&fs_info->ordered_roots, &splice);
+       while (!list_empty(&splice)) {
+               root = list_first_entry(&splice, struct btrfs_root,
+                                       ordered_root);
+               list_del_init(&root->ordered_root);
+
+               btrfs_destroy_ordered_extents(root);
+
+               cond_resched_lock(&fs_info->ordered_root_lock);
+       }
+       spin_unlock(&fs_info->ordered_root_lock);
 }
 
 int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
@@ -3707,6 +3763,7 @@ int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
 
        while ((node = rb_first(&delayed_refs->root)) != NULL) {
                struct btrfs_delayed_ref_head *head = NULL;
+               bool pin_bytes = false;
 
                ref = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
                atomic_set(&ref->refs, 1);
@@ -3727,8 +3784,7 @@ int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
                        }
 
                        if (head->must_insert_reserved)
-                               btrfs_pin_extent(root, ref->bytenr,
-                                                ref->num_bytes, 1);
+                               pin_bytes = true;
                        btrfs_free_delayed_extent_op(head->extent_op);
                        delayed_refs->num_heads--;
                        if (list_empty(&head->cluster))
@@ -3739,9 +3795,13 @@ int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
                ref->in_tree = 0;
                rb_erase(&ref->rb_node, &delayed_refs->root);
                delayed_refs->num_entries--;
-               if (head)
-                       mutex_unlock(&head->mutex);
                spin_unlock(&delayed_refs->lock);
+               if (head) {
+                       if (pin_bytes)
+                               btrfs_pin_extent(root, ref->bytenr,
+                                                ref->num_bytes, 1);
+                       mutex_unlock(&head->mutex);
+               }
                btrfs_put_delayed_ref(ref);
 
                cond_resched();
@@ -3778,24 +3838,49 @@ static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root)
 
        INIT_LIST_HEAD(&splice);
 
-       spin_lock(&root->fs_info->delalloc_lock);
-       list_splice_init(&root->fs_info->delalloc_inodes, &splice);
+       spin_lock(&root->delalloc_lock);
+       list_splice_init(&root->delalloc_inodes, &splice);
 
        while (!list_empty(&splice)) {
-               btrfs_inode = list_entry(splice.next, struct btrfs_inode,
-                                   delalloc_inodes);
+               btrfs_inode = list_first_entry(&splice, struct btrfs_inode,
+                                              delalloc_inodes);
 
                list_del_init(&btrfs_inode->delalloc_inodes);
                clear_bit(BTRFS_INODE_IN_DELALLOC_LIST,
                          &btrfs_inode->runtime_flags);
-               spin_unlock(&root->fs_info->delalloc_lock);
+               spin_unlock(&root->delalloc_lock);
 
                btrfs_invalidate_inodes(btrfs_inode->root);
 
-               spin_lock(&root->fs_info->delalloc_lock);
+               spin_lock(&root->delalloc_lock);
        }
 
-       spin_unlock(&root->fs_info->delalloc_lock);
+       spin_unlock(&root->delalloc_lock);
+}
+
+static void btrfs_destroy_all_delalloc_inodes(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_root *root;
+       struct list_head splice;
+
+       INIT_LIST_HEAD(&splice);
+
+       spin_lock(&fs_info->delalloc_root_lock);
+       list_splice_init(&fs_info->delalloc_roots, &splice);
+       while (!list_empty(&splice)) {
+               root = list_first_entry(&splice, struct btrfs_root,
+                                        delalloc_root);
+               list_del_init(&root->delalloc_root);
+               root = btrfs_grab_fs_root(root);
+               BUG_ON(!root);
+               spin_unlock(&fs_info->delalloc_root_lock);
+
+               btrfs_destroy_delalloc_inodes(root);
+               btrfs_put_fs_root(root);
+
+               spin_lock(&fs_info->delalloc_root_lock);
+       }
+       spin_unlock(&fs_info->delalloc_root_lock);
 }
 
 static int btrfs_destroy_marked_extents(struct btrfs_root *root,
@@ -3879,19 +3964,14 @@ void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
        btrfs_block_rsv_release(root, &root->fs_info->trans_block_rsv,
                                cur_trans->dirty_pages.dirty_bytes);
 
-       /* FIXME: cleanup wait for commit */
-       cur_trans->in_commit = 1;
-       cur_trans->blocked = 1;
+       cur_trans->state = TRANS_STATE_COMMIT_START;
        wake_up(&root->fs_info->transaction_blocked_wait);
 
        btrfs_evict_pending_snapshots(cur_trans);
 
-       cur_trans->blocked = 0;
+       cur_trans->state = TRANS_STATE_UNBLOCKED;
        wake_up(&root->fs_info->transaction_wait);
 
-       cur_trans->commit_done = 1;
-       wake_up(&cur_trans->commit_wait);
-
        btrfs_destroy_delayed_inodes(root);
        btrfs_assert_delayed_root_empty(root);
 
@@ -3900,6 +3980,9 @@ void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
        btrfs_destroy_pinned_extent(root,
                                    root->fs_info->pinned_extents);
 
+       cur_trans->state =TRANS_STATE_COMPLETED;
+       wake_up(&cur_trans->commit_wait);
+
        /*
        memset(cur_trans, 0, sizeof(*cur_trans));
        kmem_cache_free(btrfs_transaction_cachep, cur_trans);
@@ -3915,7 +3998,7 @@ static int btrfs_cleanup_transaction(struct btrfs_root *root)
 
        spin_lock(&root->fs_info->trans_lock);
        list_splice_init(&root->fs_info->trans_list, &list);
-       root->fs_info->trans_no_join = 1;
+       root->fs_info->running_transaction = NULL;
        spin_unlock(&root->fs_info->trans_lock);
 
        while (!list_empty(&list)) {
@@ -3923,37 +4006,31 @@ static int btrfs_cleanup_transaction(struct btrfs_root *root)
 
                btrfs_destroy_ordered_operations(t, root);
 
-               btrfs_destroy_ordered_extents(root);
+               btrfs_destroy_all_ordered_extents(root->fs_info);
 
                btrfs_destroy_delayed_refs(t, root);
 
-               /* FIXME: cleanup wait for commit */
-               t->in_commit = 1;
-               t->blocked = 1;
+               /*
+                *  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->blocked = 0;
+               t->state = TRANS_STATE_UNBLOCKED;
                smp_mb();
                if (waitqueue_active(&root->fs_info->transaction_wait))
                        wake_up(&root->fs_info->transaction_wait);
 
-               t->commit_done = 1;
-               smp_mb();
-               if (waitqueue_active(&t->commit_wait))
-                       wake_up(&t->commit_wait);
-
                btrfs_destroy_delayed_inodes(root);
                btrfs_assert_delayed_root_empty(root);
 
-               btrfs_destroy_delalloc_inodes(root);
-
-               spin_lock(&root->fs_info->trans_lock);
-               root->fs_info->running_transaction = NULL;
-               spin_unlock(&root->fs_info->trans_lock);
+               btrfs_destroy_all_delalloc_inodes(root->fs_info);
 
                btrfs_destroy_marked_extents(root, &t->dirty_pages,
                                             EXTENT_DIRTY);
@@ -3961,15 +4038,17 @@ static int btrfs_cleanup_transaction(struct btrfs_root *root)
                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);
+
                atomic_set(&t->use_count, 0);
                list_del_init(&t->list);
                memset(t, 0, sizeof(*t));
                kmem_cache_free(btrfs_transaction_cachep, t);
        }
 
-       spin_lock(&root->fs_info->trans_lock);
-       root->fs_info->trans_no_join = 0;
-       spin_unlock(&root->fs_info->trans_lock);
        mutex_unlock(&root->fs_info->transaction_kthread_mutex);
 
        return 0;
index be69ce1..b71acd6 100644 (file)
@@ -63,14 +63,40 @@ struct buffer_head *btrfs_read_dev_super(struct block_device *bdev);
 int btrfs_commit_super(struct btrfs_root *root);
 struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
                                            u64 bytenr, u32 blocksize);
-struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
-                                              struct btrfs_key *location);
+struct btrfs_root *btrfs_read_fs_root(struct btrfs_root *tree_root,
+                                     struct btrfs_key *location);
+int btrfs_init_fs_root(struct btrfs_root *root);
+int btrfs_insert_fs_root(struct btrfs_fs_info *fs_info,
+                        struct btrfs_root *root);
 struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
                                              struct btrfs_key *location);
 int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info);
 void btrfs_btree_balance_dirty(struct btrfs_root *root);
 void btrfs_btree_balance_dirty_nodelay(struct btrfs_root *root);
-void btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root);
+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);
+
+/*
+ * 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.
+ *
+ * If you want to ensure the whole tree is safe, you should use
+ *     fs_info->subvol_srcu
+ */
+static inline struct btrfs_root *btrfs_grab_fs_root(struct btrfs_root *root)
+{
+       if (atomic_inc_not_zero(&root->refs))
+               return root;
+       return NULL;
+}
+
+static inline void btrfs_put_fs_root(struct btrfs_root *root)
+{
+       if (atomic_dec_and_test(&root->refs))
+               kfree(root);
+}
+
 void btrfs_mark_buffer_dirty(struct extent_buffer *buf);
 int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid,
                          int atomic);
index 81ee29e..4b86916 100644 (file)
@@ -82,11 +82,6 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,
                goto fail;
        }
 
-       if (btrfs_root_refs(&root->root_item) == 0) {
-               err = -ENOENT;
-               goto fail;
-       }
-
        key.objectid = objectid;
        btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
        key.offset = 0;
index df472ab..0236de7 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/kthread.h>
 #include <linux/slab.h>
 #include <linux/ratelimit.h>
+#include <linux/percpu_counter.h>
 #include "compat.h"
 #include "hash.h"
 #include "ctree.h"
@@ -2526,6 +2527,51 @@ static int refs_newer(struct btrfs_delayed_ref_root *delayed_refs, int seq,
        return 0;
 }
 
+static inline u64 heads_to_leaves(struct btrfs_root *root, u64 heads)
+{
+       u64 num_bytes;
+
+       num_bytes = heads * (sizeof(struct btrfs_extent_item) +
+                            sizeof(struct btrfs_extent_inline_ref));
+       if (!btrfs_fs_incompat(root->fs_info, SKINNY_METADATA))
+               num_bytes += heads * sizeof(struct btrfs_tree_block_info);
+
+       /*
+        * We don't ever fill up leaves all the way so multiply by 2 just to be
+        * closer to what we're really going to want to ouse.
+        */
+       return div64_u64(num_bytes, BTRFS_LEAF_DATA_SIZE(root));
+}
+
+int btrfs_should_throttle_delayed_refs(struct btrfs_trans_handle *trans,
+                                      struct btrfs_root *root)
+{
+       struct btrfs_block_rsv *global_rsv;
+       u64 num_heads = trans->transaction->delayed_refs.num_heads_ready;
+       u64 num_bytes;
+       int ret = 0;
+
+       num_bytes = btrfs_calc_trans_metadata_size(root, 1);
+       num_heads = heads_to_leaves(root, num_heads);
+       if (num_heads > 1)
+               num_bytes += (num_heads - 1) * root->leafsize;
+       num_bytes <<= 1;
+       global_rsv = &root->fs_info->global_block_rsv;
+
+       /*
+        * If we can't allocate any more chunks lets make sure we have _lots_ of
+        * wiggle room since running delayed refs can create more delayed refs.
+        */
+       if (global_rsv->space_info->full)
+               num_bytes <<= 1;
+
+       spin_lock(&global_rsv->lock);
+       if (global_rsv->reserved <= num_bytes)
+               ret = 1;
+       spin_unlock(&global_rsv->lock);
+       return ret;
+}
+
 /*
  * this starts processing the delayed reference count updates and
  * extent insertions we have queued up so far.  count can be
@@ -2573,7 +2619,8 @@ progress:
                old = atomic_cmpxchg(&delayed_refs->procs_running_refs, 0, 1);
                if (old) {
                        DEFINE_WAIT(__wait);
-                       if (delayed_refs->num_entries < 16348)
+                       if (delayed_refs->flushing ||
+                           !btrfs_should_throttle_delayed_refs(trans, root))
                                return 0;
 
                        prepare_to_wait(&delayed_refs->wait, &__wait,
@@ -2608,7 +2655,7 @@ again:
 
        while (1) {
                if (!(run_all || run_most) &&
-                   delayed_refs->num_heads_ready < 64)
+                   !btrfs_should_throttle_delayed_refs(trans, root))
                        break;
 
                /*
@@ -2629,6 +2676,7 @@ again:
                        spin_unlock(&delayed_refs->lock);
                        btrfs_abort_transaction(trans, root, ret);
                        atomic_dec(&delayed_refs->procs_running_refs);
+                       wake_up(&delayed_refs->wait);
                        return ret;
                }
 
@@ -3310,6 +3358,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
        struct btrfs_space_info *found;
        int i;
        int factor;
+       int ret;
 
        if (flags & (BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1 |
                     BTRFS_BLOCK_GROUP_RAID10))
@@ -3333,6 +3382,12 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
        if (!found)
                return -ENOMEM;
 
+       ret = percpu_counter_init(&found->total_bytes_pinned, 0);
+       if (ret) {
+               kfree(found);
+               return ret;
+       }
+
        for (i = 0; i < BTRFS_NR_RAID_TYPES; i++)
                INIT_LIST_HEAD(&found->block_groups[i]);
        init_rwsem(&found->groups_sem);
@@ -3565,10 +3620,11 @@ alloc:
                }
 
                /*
-                * If we have less pinned bytes than we want to allocate then
-                * don't bother committing the transaction, it won't help us.
+                * If we don't have enough pinned space to deal with this
+                * allocation don't bother committing the transaction.
                 */
-               if (data_sinfo->bytes_pinned < bytes)
+               if (percpu_counter_compare(&data_sinfo->total_bytes_pinned,
+                                          bytes) < 0)
                        committed = 1;
                spin_unlock(&data_sinfo->lock);
 
@@ -3577,6 +3633,7 @@ commit_trans:
                if (!committed &&
                    !atomic_read(&root->fs_info->open_ioctl_trans)) {
                        committed = 1;
+
                        trans = btrfs_join_transaction(root);
                        if (IS_ERR(trans))
                                return PTR_ERR(trans);
@@ -3609,6 +3666,7 @@ void btrfs_free_reserved_data_space(struct inode *inode, u64 bytes)
 
        data_sinfo = root->fs_info->data_sinfo;
        spin_lock(&data_sinfo->lock);
+       WARN_ON(data_sinfo->bytes_may_use < bytes);
        data_sinfo->bytes_may_use -= bytes;
        trace_btrfs_space_reservation(root->fs_info, "space_info",
                                      data_sinfo->flags, bytes, 0);
@@ -3886,12 +3944,11 @@ static void btrfs_writeback_inodes_sb_nr(struct btrfs_root *root,
                                         unsigned long nr_pages)
 {
        struct super_block *sb = root->fs_info->sb;
-       int started;
 
-       /* If we can not start writeback, just sync all the delalloc file. */
-       started = try_to_writeback_inodes_sb_nr(sb, nr_pages,
-                                                     WB_REASON_FS_FREE_SPACE);
-       if (!started) {
+       if (down_read_trylock(&sb->s_umount)) {
+               writeback_inodes_sb_nr(sb, nr_pages, WB_REASON_FS_FREE_SPACE);
+               up_read(&sb->s_umount);
+       } else {
                /*
                 * We needn't worry the filesystem going from r/w to r/o though
                 * we don't acquire ->s_umount mutex, because the filesystem
@@ -3899,9 +3956,9 @@ 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_delalloc_inodes(root, 0);
+               btrfs_start_all_delalloc_inodes(root->fs_info, 0);
                if (!current->journal_info)
-                       btrfs_wait_ordered_extents(root, 0);
+                       btrfs_wait_all_ordered_extents(root->fs_info, 0);
        }
 }
 
@@ -3931,7 +3988,7 @@ static void shrink_delalloc(struct btrfs_root *root, u64 to_reclaim, u64 orig,
        if (delalloc_bytes == 0) {
                if (trans)
                        return;
-               btrfs_wait_ordered_extents(root, 0);
+               btrfs_wait_all_ordered_extents(root->fs_info, 0);
                return;
        }
 
@@ -3959,7 +4016,7 @@ static void shrink_delalloc(struct btrfs_root *root, u64 to_reclaim, u64 orig,
 
                loops++;
                if (wait_ordered && !trans) {
-                       btrfs_wait_ordered_extents(root, 0);
+                       btrfs_wait_all_ordered_extents(root->fs_info, 0);
                } else {
                        time_left = schedule_timeout_killable(1);
                        if (time_left)
@@ -3997,7 +4054,8 @@ static int may_commit_transaction(struct btrfs_root *root,
 
        /* See if there is enough pinned space to make this reservation */
        spin_lock(&space_info->lock);
-       if (space_info->bytes_pinned >= bytes) {
+       if (percpu_counter_compare(&space_info->total_bytes_pinned,
+                                  bytes) >= 0) {
                spin_unlock(&space_info->lock);
                goto commit;
        }
@@ -4012,7 +4070,8 @@ static int may_commit_transaction(struct btrfs_root *root,
 
        spin_lock(&space_info->lock);
        spin_lock(&delayed_rsv->lock);
-       if (space_info->bytes_pinned + delayed_rsv->size < bytes) {
+       if (percpu_counter_compare(&space_info->total_bytes_pinned,
+                                  bytes - delayed_rsv->size) >= 0) {
                spin_unlock(&delayed_rsv->lock);
                spin_unlock(&space_info->lock);
                return -ENOSPC;
@@ -4297,6 +4356,31 @@ static void block_rsv_add_bytes(struct btrfs_block_rsv *block_rsv,
        spin_unlock(&block_rsv->lock);
 }
 
+int btrfs_cond_migrate_bytes(struct btrfs_fs_info *fs_info,
+                            struct btrfs_block_rsv *dest, u64 num_bytes,
+                            int min_factor)
+{
+       struct btrfs_block_rsv *global_rsv = &fs_info->global_block_rsv;
+       u64 min_bytes;
+
+       if (global_rsv->space_info != dest->space_info)
+               return -ENOSPC;
+
+       spin_lock(&global_rsv->lock);
+       min_bytes = div_factor(global_rsv->size, min_factor);
+       if (global_rsv->reserved < min_bytes + num_bytes) {
+               spin_unlock(&global_rsv->lock);
+               return -ENOSPC;
+       }
+       global_rsv->reserved -= num_bytes;
+       if (global_rsv->reserved < global_rsv->size)
+               global_rsv->full = 0;
+       spin_unlock(&global_rsv->lock);
+
+       block_rsv_add_bytes(dest, num_bytes, 1);
+       return 0;
+}
+
 static void block_rsv_release_bytes(struct btrfs_fs_info *fs_info,
                                    struct btrfs_block_rsv *block_rsv,
                                    struct btrfs_block_rsv *dest, u64 num_bytes)
@@ -5030,14 +5114,14 @@ static int update_block_group(struct btrfs_root *root,
        int factor;
 
        /* block accounting for super block */
-       spin_lock(&info->delalloc_lock);
+       spin_lock(&info->delalloc_root_lock);
        old_val = btrfs_super_bytes_used(info->super_copy);
        if (alloc)
                old_val += num_bytes;
        else
                old_val -= num_bytes;
        btrfs_set_super_bytes_used(info->super_copy, old_val);
-       spin_unlock(&info->delalloc_lock);
+       spin_unlock(&info->delalloc_root_lock);
 
        while (total) {
                cache = btrfs_lookup_block_group(info, bytenr);
@@ -5189,6 +5273,80 @@ int btrfs_pin_extent_for_log_replay(struct btrfs_root *root,
        return ret;
 }
 
+static int __exclude_logged_extent(struct btrfs_root *root, u64 start, u64 num_bytes)
+{
+       int ret;
+       struct btrfs_block_group_cache *block_group;
+       struct btrfs_caching_control *caching_ctl;
+
+       block_group = btrfs_lookup_block_group(root->fs_info, start);
+       if (!block_group)
+               return -EINVAL;
+
+       cache_block_group(block_group, 0);
+       caching_ctl = get_caching_control(block_group);
+
+       if (!caching_ctl) {
+               /* Logic error */
+               BUG_ON(!block_group_cache_done(block_group));
+               ret = btrfs_remove_free_space(block_group, start, num_bytes);
+       } else {
+               mutex_lock(&caching_ctl->mutex);
+
+               if (start >= caching_ctl->progress) {
+                       ret = add_excluded_extent(root, start, num_bytes);
+               } else if (start + num_bytes <= caching_ctl->progress) {
+                       ret = btrfs_remove_free_space(block_group,
+                                                     start, num_bytes);
+               } else {
+                       num_bytes = caching_ctl->progress - start;
+                       ret = btrfs_remove_free_space(block_group,
+                                                     start, num_bytes);
+                       if (ret)
+                               goto out_lock;
+
+                       num_bytes = (start + num_bytes) -
+                               caching_ctl->progress;
+                       start = caching_ctl->progress;
+                       ret = add_excluded_extent(root, start, num_bytes);
+               }
+out_lock:
+               mutex_unlock(&caching_ctl->mutex);
+               put_caching_control(caching_ctl);
+       }
+       btrfs_put_block_group(block_group);
+       return ret;
+}
+
+int btrfs_exclude_logged_extents(struct btrfs_root *log,
+                                struct extent_buffer *eb)
+{
+       struct btrfs_file_extent_item *item;
+       struct btrfs_key key;
+       int found_type;
+       int i;
+
+       if (!btrfs_fs_incompat(log->fs_info, MIXED_GROUPS))
+               return 0;
+
+       for (i = 0; i < btrfs_header_nritems(eb); i++) {
+               btrfs_item_key_to_cpu(eb, &key, i);
+               if (key.type != BTRFS_EXTENT_DATA_KEY)
+                       continue;
+               item = btrfs_item_ptr(eb, i, struct btrfs_file_extent_item);
+               found_type = btrfs_file_extent_type(eb, item);
+               if (found_type == BTRFS_FILE_EXTENT_INLINE)
+                       continue;
+               if (btrfs_file_extent_disk_bytenr(eb, item) == 0)
+                       continue;
+               key.objectid = btrfs_file_extent_disk_bytenr(eb, item);
+               key.offset = btrfs_file_extent_disk_num_bytes(eb, item);
+               __exclude_logged_extent(log, key.objectid, key.offset);
+       }
+
+       return 0;
+}
+
 /**
  * btrfs_update_reserved_bytes - update the block_group and space info counters
  * @cache:     The cache we are manipulating
@@ -5251,6 +5409,7 @@ void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
        struct btrfs_caching_control *next;
        struct btrfs_caching_control *caching_ctl;
        struct btrfs_block_group_cache *cache;
+       struct btrfs_space_info *space_info;
 
        down_write(&fs_info->extent_commit_sem);
 
@@ -5273,6 +5432,9 @@ void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
 
        up_write(&fs_info->extent_commit_sem);
 
+       list_for_each_entry_rcu(space_info, &fs_info->space_info, list)
+               percpu_counter_set(&space_info->total_bytes_pinned, 0);
+
        update_global_block_rsv(fs_info);
 }
 
@@ -5370,6 +5532,27 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+static void add_pinned_bytes(struct btrfs_fs_info *fs_info, u64 num_bytes,
+                            u64 owner, u64 root_objectid)
+{
+       struct btrfs_space_info *space_info;
+       u64 flags;
+
+       if (owner < BTRFS_FIRST_FREE_OBJECTID) {
+               if (root_objectid == BTRFS_CHUNK_TREE_OBJECTID)
+                       flags = BTRFS_BLOCK_GROUP_SYSTEM;
+               else
+                       flags = BTRFS_BLOCK_GROUP_METADATA;
+       } else {
+               flags = BTRFS_BLOCK_GROUP_DATA;
+       }
+
+       space_info = __find_space_info(fs_info, flags);
+       BUG_ON(!space_info); /* Logic bug */
+       percpu_counter_add(&space_info->total_bytes_pinned, num_bytes);
+}
+
+
 static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                                struct btrfs_root *root,
                                u64 bytenr, u64 num_bytes, u64 parent,
@@ -5590,6 +5773,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                                goto out;
                        }
                }
+               add_pinned_bytes(root->fs_info, -num_bytes, owner_objectid,
+                                root_objectid);
        } else {
                if (found_extent) {
                        BUG_ON(is_data && refs_to_drop !=
@@ -5713,6 +5898,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
                           u64 parent, int last_ref)
 {
        struct btrfs_block_group_cache *cache = NULL;
+       int pin = 1;
        int ret;
 
        if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) {
@@ -5745,8 +5931,14 @@ 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);
+               pin = 0;
        }
 out:
+       if (pin)
+               add_pinned_bytes(root->fs_info, buf->len,
+                                btrfs_header_level(buf),
+                                root->root_key.objectid);
+
        /*
         * Deleting the buffer, clear the corrupt flag since it doesn't matter
         * anymore.
@@ -5763,6 +5955,8 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root,
        int ret;
        struct btrfs_fs_info *fs_info = root->fs_info;
 
+       add_pinned_bytes(root->fs_info, num_bytes, owner, root_objectid);
+
        /*
         * tree log blocks never actually go into the extent allocation
         * tree, just update pinning info and exit early.
@@ -6560,52 +6754,26 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
 {
        int ret;
        struct btrfs_block_group_cache *block_group;
-       struct btrfs_caching_control *caching_ctl;
-       u64 start = ins->objectid;
-       u64 num_bytes = ins->offset;
-
-       block_group = btrfs_lookup_block_group(root->fs_info, ins->objectid);
-       cache_block_group(block_group, 0);
-       caching_ctl = get_caching_control(block_group);
-
-       if (!caching_ctl) {
-               BUG_ON(!block_group_cache_done(block_group));
-               ret = btrfs_remove_free_space(block_group, start, num_bytes);
-               if (ret)
-                       goto out;
-       } else {
-               mutex_lock(&caching_ctl->mutex);
 
-               if (start >= caching_ctl->progress) {
-                       ret = add_excluded_extent(root, start, num_bytes);
-               } else if (start + num_bytes <= caching_ctl->progress) {
-                       ret = btrfs_remove_free_space(block_group,
-                                                     start, num_bytes);
-               } else {
-                       num_bytes = caching_ctl->progress - start;
-                       ret = btrfs_remove_free_space(block_group,
-                                                     start, num_bytes);
-                       if (ret)
-                               goto out_lock;
-
-                       start = caching_ctl->progress;
-                       num_bytes = ins->objectid + ins->offset -
-                                   caching_ctl->progress;
-                       ret = add_excluded_extent(root, start, num_bytes);
-               }
-out_lock:
-               mutex_unlock(&caching_ctl->mutex);
-               put_caching_control(caching_ctl);
+       /*
+        * Mixed block groups will exclude before processing the log so we only
+        * need to do the exlude dance if this fs isn't mixed.
+        */
+       if (!btrfs_fs_incompat(root->fs_info, MIXED_GROUPS)) {
+               ret = __exclude_logged_extent(root, ins->objectid, ins->offset);
                if (ret)
-                       goto out;
+                       return ret;
        }
 
+       block_group = btrfs_lookup_block_group(root->fs_info, ins->objectid);
+       if (!block_group)
+               return -EINVAL;
+
        ret = btrfs_update_reserved_bytes(block_group, ins->offset,
                                          RESERVE_ALLOC_NO_ACCOUNT);
        BUG_ON(ret); /* logic error */
        ret = alloc_reserved_file_extent(trans, root, 0, root_objectid,
                                         0, owner, offset, ins, 1);
-out:
        btrfs_put_block_group(block_group);
        return ret;
 }
@@ -7384,7 +7552,7 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
        wc->reada_count = BTRFS_NODEPTRS_PER_BLOCK(root);
 
        while (1) {
-               if (!for_reloc && btrfs_fs_closing(root->fs_info)) {
+               if (!for_reloc && btrfs_need_cleaner_sleep(root)) {
                        pr_debug("btrfs: drop snapshot early exit\n");
                        err = -EAGAIN;
                        goto out_end_trans;
@@ -7447,8 +7615,8 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
        }
 
        if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
-               ret = btrfs_find_last_root(tree_root, root->root_key.objectid,
-                                          NULL, NULL);
+               ret = btrfs_find_root(tree_root, &root->root_key, path,
+                                     NULL, NULL);
                if (ret < 0) {
                        btrfs_abort_transaction(trans, tree_root, ret);
                        err = ret;
@@ -7465,11 +7633,11 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
        }
 
        if (root->in_radix) {
-               btrfs_free_fs_root(tree_root->fs_info, root);
+               btrfs_drop_and_free_fs_root(tree_root->fs_info, root);
        } else {
                free_extent_buffer(root->node);
                free_extent_buffer(root->commit_root);
-               kfree(root);
+               btrfs_put_fs_root(root);
        }
 out_end_trans:
        btrfs_end_transaction_throttle(trans, tree_root);
@@ -7782,6 +7950,7 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr)
        struct btrfs_space_info *space_info;
        struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
        struct btrfs_device *device;
+       struct btrfs_trans_handle *trans;
        u64 min_free;
        u64 dev_min = 1;
        u64 dev_nr = 0;
@@ -7868,6 +8037,13 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr)
                do_div(min_free, dev_min);
        }
 
+       /* We need to do this so that we can look at pending chunks */
+       trans = btrfs_join_transaction(root);
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               goto out;
+       }
+
        mutex_lock(&root->fs_info->chunk_mutex);
        list_for_each_entry(device, &fs_devices->alloc_list, dev_alloc_list) {
                u64 dev_offset;
@@ -7878,7 +8054,7 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr)
                 */
                if (device->total_bytes > device->bytes_used + min_free &&
                    !device->is_tgtdev_for_dev_replace) {
-                       ret = find_free_dev_extent(device, min_free,
+                       ret = find_free_dev_extent(trans, device, min_free,
                                                   &dev_offset, NULL);
                        if (!ret)
                                dev_nr++;
@@ -7890,6 +8066,7 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr)
                }
        }
        mutex_unlock(&root->fs_info->chunk_mutex);
+       btrfs_end_transaction(trans, root);
 out:
        btrfs_put_block_group(block_group);
        return ret;
@@ -8032,6 +8209,7 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
                                dump_space_info(space_info, 0, 0);
                        }
                }
+               percpu_counter_destroy(&space_info->total_bytes_pinned);
                list_del(&space_info->list);
                kfree(space_info);
        }
@@ -8254,6 +8432,10 @@ void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans,
                                        sizeof(item));
                if (ret)
                        btrfs_abort_transaction(trans, extent_root, ret);
+               ret = btrfs_finish_chunk_alloc(trans, extent_root,
+                                              key.objectid, key.offset);
+               if (ret)
+                       btrfs_abort_transaction(trans, extent_root, ret);
        }
 }
 
@@ -8591,8 +8773,15 @@ int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range)
                if (end - start >= range->minlen) {
                        if (!block_group_cache_done(cache)) {
                                ret = cache_block_group(cache, 0);
-                               if (!ret)
-                                       wait_block_group_cache_done(cache);
+                               if (ret) {
+                                       btrfs_put_block_group(cache);
+                                       break;
+                               }
+                               ret = wait_block_group_cache_done(cache);
+                               if (ret) {
+                                       btrfs_put_block_group(cache);
+                                       break;
+                               }
                        }
                        ret = btrfs_trim_block_group(cache,
                                                     &group_trimmed,
index 6bca947..583d98b 100644 (file)
@@ -77,10 +77,29 @@ void btrfs_leak_debug_check(void)
                kmem_cache_free(extent_buffer_cache, eb);
        }
 }
+
+#define btrfs_debug_check_extent_io_range(inode, start, end)           \
+       __btrfs_debug_check_extent_io_range(__func__, (inode), (start), (end))
+static inline void __btrfs_debug_check_extent_io_range(const char *caller,
+               struct inode *inode, u64 start, u64 end)
+{
+       u64 isize = i_size_read(inode);
+
+       if (end >= PAGE_SIZE && (end % 2) == 0 && end != isize - 1) {
+               printk_ratelimited(KERN_DEBUG
+                   "btrfs: %s: ino %llu isize %llu odd range [%llu,%llu]\n",
+                               caller,
+                               (unsigned long long)btrfs_ino(inode),
+                               (unsigned long long)isize,
+                               (unsigned long long)start,
+                               (unsigned long long)end);
+       }
+}
 #else
 #define btrfs_leak_debug_add(new, head)        do {} while (0)
 #define btrfs_leak_debug_del(entry)    do {} while (0)
 #define btrfs_leak_debug_check()       do {} while (0)
+#define btrfs_debug_check_extent_io_range(c, s, e)     do {} while (0)
 #endif
 
 #define BUFFER_LRU_MAX 64
@@ -522,6 +541,11 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
        int err;
        int clear = 0;
 
+       btrfs_debug_check_extent_io_range(tree->mapping->host, start, end);
+
+       if (bits & EXTENT_DELALLOC)
+               bits |= EXTENT_NORESERVE;
+
        if (delete)
                bits |= ~EXTENT_CTLBITS;
        bits |= EXTENT_FIRST_DELALLOC;
@@ -677,6 +701,8 @@ static void wait_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
        struct extent_state *state;
        struct rb_node *node;
 
+       btrfs_debug_check_extent_io_range(tree->mapping->host, start, end);
+
        spin_lock(&tree->lock);
 again:
        while (1) {
@@ -769,6 +795,8 @@ __set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
        u64 last_start;
        u64 last_end;
 
+       btrfs_debug_check_extent_io_range(tree->mapping->host, start, end);
+
        bits |= EXTENT_FIRST_DELALLOC;
 again:
        if (!prealloc && (mask & __GFP_WAIT)) {
@@ -989,6 +1017,8 @@ int convert_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
        u64 last_start;
        u64 last_end;
 
+       btrfs_debug_check_extent_io_range(tree->mapping->host, start, end);
+
 again:
        if (!prealloc && (mask & __GFP_WAIT)) {
                prealloc = alloc_extent_state(mask);
@@ -2450,11 +2480,12 @@ static void end_bio_extent_readpage(struct bio *bio, int err)
                struct extent_state *cached = NULL;
                struct extent_state *state;
                struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+               struct inode *inode = page->mapping->host;
 
                pr_debug("end_bio_extent_readpage: bi_sector=%llu, err=%d, "
                         "mirror=%lu\n", (u64)bio->bi_sector, err,
                         io_bio->mirror_num);
-               tree = &BTRFS_I(page->mapping->host)->io_tree;
+               tree = &BTRFS_I(inode)->io_tree;
 
                /* We always issue full-page reads, but if some block
                 * in a page fails to read, blk_update_request() will
@@ -2528,6 +2559,14 @@ static void end_bio_extent_readpage(struct bio *bio, int err)
                unlock_extent_cached(tree, start, end, &cached, GFP_ATOMIC);
 
                if (uptodate) {
+                       loff_t i_size = i_size_read(inode);
+                       pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT;
+                       unsigned offset;
+
+                       /* Zero out the end if this page straddles i_size */
+                       offset = i_size & (PAGE_CACHE_SIZE-1);
+                       if (page->index == end_index && offset)
+                               zero_user_segment(page, offset, PAGE_CACHE_SIZE);
                        SetPageUptodate(page);
                } else {
                        ClearPageUptodate(page);
index 41fb81e..3b8c4e2 100644 (file)
@@ -19,6 +19,7 @@
 #define EXTENT_FIRST_DELALLOC (1 << 12)
 #define EXTENT_NEED_WAIT (1 << 13)
 #define EXTENT_DAMAGED (1 << 14)
+#define EXTENT_NORESERVE (1 << 15)
 #define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK)
 #define EXTENT_CTLBITS (EXTENT_DO_ACCOUNTING | EXTENT_FIRST_DELALLOC)
 
index b193bf3..a7bfc95 100644 (file)
@@ -34,8 +34,7 @@
 
 #define MAX_ORDERED_SUM_BYTES(r) ((PAGE_SIZE - \
                                   sizeof(struct btrfs_ordered_sum)) / \
-                                  sizeof(struct btrfs_sector_sum) * \
-                                  (r)->sectorsize - (r)->sectorsize)
+                                  sizeof(u32) * (r)->sectorsize)
 
 int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root,
@@ -297,7 +296,6 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
        struct btrfs_path *path;
        struct extent_buffer *leaf;
        struct btrfs_ordered_sum *sums;
-       struct btrfs_sector_sum *sector_sum;
        struct btrfs_csum_item *item;
        LIST_HEAD(tmplist);
        unsigned long offset;
@@ -368,34 +366,28 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
                                      struct btrfs_csum_item);
                while (start < csum_end) {
                        size = min_t(size_t, csum_end - start,
-                                       MAX_ORDERED_SUM_BYTES(root));
+                                    MAX_ORDERED_SUM_BYTES(root));
                        sums = kzalloc(btrfs_ordered_sum_size(root, size),
-                                       GFP_NOFS);
+                                      GFP_NOFS);
                        if (!sums) {
                                ret = -ENOMEM;
                                goto fail;
                        }
 
-                       sector_sum = sums->sums;
                        sums->bytenr = start;
-                       sums->len = size;
+                       sums->len = (int)size;
 
                        offset = (start - key.offset) >>
                                root->fs_info->sb->s_blocksize_bits;
                        offset *= csum_size;
+                       size >>= root->fs_info->sb->s_blocksize_bits;
 
-                       while (size > 0) {
-                               read_extent_buffer(path->nodes[0],
-                                               &sector_sum->sum,
-                                               ((unsigned long)item) +
-                                               offset, csum_size);
-                               sector_sum->bytenr = start;
-
-                               size -= root->sectorsize;
-                               start += root->sectorsize;
-                               offset += csum_size;
-                               sector_sum++;
-                       }
+                       read_extent_buffer(path->nodes[0],
+                                          sums->sums,
+                                          ((unsigned long)item) + offset,
+                                          csum_size * size);
+
+                       start += root->sectorsize * size;
                        list_add_tail(&sums->list, &tmplist);
                }
                path->slots[0]++;
@@ -417,23 +409,20 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
                       struct bio *bio, u64 file_start, int contig)
 {
        struct btrfs_ordered_sum *sums;
-       struct btrfs_sector_sum *sector_sum;
        struct btrfs_ordered_extent *ordered;
        char *data;
        struct bio_vec *bvec = bio->bi_io_vec;
        int bio_index = 0;
+       int index;
        unsigned long total_bytes = 0;
        unsigned long this_sum_bytes = 0;
        u64 offset;
-       u64 disk_bytenr;
 
        WARN_ON(bio->bi_vcnt <= 0);
        sums = kzalloc(btrfs_ordered_sum_size(root, bio->bi_size), GFP_NOFS);
        if (!sums)
                return -ENOMEM;
 
-       sector_sum = sums->sums;
-       disk_bytenr = (u64)bio->bi_sector << 9;
        sums->len = bio->bi_size;
        INIT_LIST_HEAD(&sums->list);
 
@@ -444,7 +433,8 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
 
        ordered = btrfs_lookup_ordered_extent(inode, offset);
        BUG_ON(!ordered); /* Logic error */
-       sums->bytenr = ordered->start;
+       sums->bytenr = (u64)bio->bi_sector << 9;
+       index = 0;
 
        while (bio_index < bio->bi_vcnt) {
                if (!contig)
@@ -463,28 +453,27 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode,
                        sums = kzalloc(btrfs_ordered_sum_size(root, bytes_left),
                                       GFP_NOFS);
                        BUG_ON(!sums); /* -ENOMEM */
-                       sector_sum = sums->sums;
                        sums->len = bytes_left;
                        ordered = btrfs_lookup_ordered_extent(inode, offset);
                        BUG_ON(!ordered); /* Logic error */
-                       sums->bytenr = ordered->start;
+                       sums->bytenr = ((u64)bio->bi_sector << 9) +
+                                      total_bytes;
+                       index = 0;
                }
 
                data = kmap_atomic(bvec->bv_page);
-               sector_sum->sum = ~(u32)0;
-               sector_sum->sum = btrfs_csum_data(data + bvec->bv_offset,
-                                                 sector_sum->sum,
-                                                 bvec->bv_len);
+               sums->sums[index] = ~(u32)0;
+               sums->sums[index] = btrfs_csum_data(data + bvec->bv_offset,
+                                                   sums->sums[index],
+                                                   bvec->bv_len);
                kunmap_atomic(data);
-               btrfs_csum_final(sector_sum->sum,
-                                (char *)&sector_sum->sum);
-               sector_sum->bytenr = disk_bytenr;
+               btrfs_csum_final(sums->sums[index],
+                                (char *)(sums->sums + index));
 
-               sector_sum++;
                bio_index++;
+               index++;
                total_bytes += bvec->bv_len;
                this_sum_bytes += bvec->bv_len;
-               disk_bytenr += bvec->bv_len;
                offset += bvec->bv_len;
                bvec++;
        }
@@ -672,62 +661,46 @@ out:
        return ret;
 }
 
-static u64 btrfs_sector_sum_left(struct btrfs_ordered_sum *sums,
-                                struct btrfs_sector_sum *sector_sum,
-                                u64 total_bytes, u64 sectorsize)
-{
-       u64 tmp = sectorsize;
-       u64 next_sector = sector_sum->bytenr;
-       struct btrfs_sector_sum *next = sector_sum + 1;
-
-       while ((tmp + total_bytes) < sums->len) {
-               if (next_sector + sectorsize != next->bytenr)
-                       break;
-               tmp += sectorsize;
-               next_sector = next->bytenr;
-               next++;
-       }
-       return tmp;
-}
-
 int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root,
                           struct btrfs_ordered_sum *sums)
 {
-       u64 bytenr;
-       int ret;
        struct btrfs_key file_key;
        struct btrfs_key found_key;
-       u64 next_offset;
-       u64 total_bytes = 0;
-       int found_next;
        struct btrfs_path *path;
        struct btrfs_csum_item *item;
        struct btrfs_csum_item *item_end;
        struct extent_buffer *leaf = NULL;
+       u64 next_offset;
+       u64 total_bytes = 0;
        u64 csum_offset;
-       struct btrfs_sector_sum *sector_sum;
+       u64 bytenr;
        u32 nritems;
        u32 ins_size;
+       int index = 0;
+       int found_next;
+       int ret;
        u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
 
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
-
-       sector_sum = sums->sums;
 again:
        next_offset = (u64)-1;
        found_next = 0;
+       bytenr = sums->bytenr + total_bytes;
        file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
-       file_key.offset = sector_sum->bytenr;
-       bytenr = sector_sum->bytenr;
+       file_key.offset = bytenr;
        btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY);
 
-       item = btrfs_lookup_csum(trans, root, path, sector_sum->bytenr, 1);
+       item = btrfs_lookup_csum(trans, root, path, bytenr, 1);
        if (!IS_ERR(item)) {
-               leaf = path->nodes[0];
                ret = 0;
+               leaf = path->nodes[0];
+               item_end = btrfs_item_ptr(leaf, path->slots[0],
+                                         struct btrfs_csum_item);
+               item_end = (struct btrfs_csum_item *)((char *)item_end +
+                          btrfs_item_size_nr(leaf, path->slots[0]));
                goto found;
        }
        ret = PTR_ERR(item);
@@ -807,8 +780,7 @@ again:
 
                free_space = btrfs_leaf_free_space(root, leaf) -
                                         sizeof(struct btrfs_item) - csum_size;
-               tmp = btrfs_sector_sum_left(sums, sector_sum, total_bytes,
-                                           root->sectorsize);
+               tmp = sums->len - total_bytes;
                tmp >>= root->fs_info->sb->s_blocksize_bits;
                WARN_ON(tmp < 1);
 
@@ -822,6 +794,7 @@ again:
                diff *= csum_size;
 
                btrfs_extend_item(root, path, diff);
+               ret = 0;
                goto csum;
        }
 
@@ -831,8 +804,7 @@ insert:
        if (found_next) {
                u64 tmp;
 
-               tmp = btrfs_sector_sum_left(sums, sector_sum, total_bytes,
-                                           root->sectorsize);
+               tmp = sums->len - total_bytes;
                tmp >>= root->fs_info->sb->s_blocksize_bits;
                tmp = min(tmp, (next_offset - file_key.offset) >>
                                         root->fs_info->sb->s_blocksize_bits);
@@ -853,31 +825,25 @@ insert:
                WARN_ON(1);
                goto fail_unlock;
        }
-csum:
        leaf = path->nodes[0];
+csum:
        item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
-       ret = 0;
+       item_end = (struct btrfs_csum_item *)((unsigned char *)item +
+                                     btrfs_item_size_nr(leaf, path->slots[0]));
        item = (struct btrfs_csum_item *)((unsigned char *)item +
                                          csum_offset * csum_size);
 found:
-       item_end = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
-       item_end = (struct btrfs_csum_item *)((unsigned char *)item_end +
-                                     btrfs_item_size_nr(leaf, path->slots[0]));
-next_sector:
-
-       write_extent_buffer(leaf, &sector_sum->sum, (unsigned long)item, csum_size);
-
-       total_bytes += root->sectorsize;
-       sector_sum++;
-       if (total_bytes < sums->len) {
-               item = (struct btrfs_csum_item *)((char *)item +
-                                                 csum_size);
-               if (item < item_end && bytenr + PAGE_CACHE_SIZE ==
-                   sector_sum->bytenr) {
-                       bytenr = sector_sum->bytenr;
-                       goto next_sector;
-               }
-       }
+       ins_size = (u32)(sums->len - total_bytes) >>
+                  root->fs_info->sb->s_blocksize_bits;
+       ins_size *= csum_size;
+       ins_size = min_t(u32, (unsigned long)item_end - (unsigned long)item,
+                             ins_size);
+       write_extent_buffer(leaf, sums->sums + index, (unsigned long)item,
+                           ins_size);
+
+       ins_size /= csum_size;
+       total_bytes += ins_size * root->sectorsize;
+       index += ins_size;
 
        btrfs_mark_buffer_dirty(path->nodes[0]);
        if (total_bytes < sums->len) {
index 89da56a..a005fe2 100644 (file)
@@ -309,10 +309,6 @@ static int __btrfs_run_defrag_inode(struct btrfs_fs_info *fs_info,
                ret = PTR_ERR(inode_root);
                goto cleanup;
        }
-       if (btrfs_root_refs(&inode_root->root_item) == 0) {
-               ret = -ENOENT;
-               goto cleanup;
-       }
 
        key.objectid = defrag->ino;
        btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
@@ -1317,6 +1313,56 @@ fail:
 
 }
 
+static noinline int check_can_nocow(struct inode *inode, loff_t pos,
+                                   size_t *write_bytes)
+{
+       struct btrfs_trans_handle *trans;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct btrfs_ordered_extent *ordered;
+       u64 lockstart, lockend;
+       u64 num_bytes;
+       int ret;
+
+       lockstart = round_down(pos, root->sectorsize);
+       lockend = lockstart + round_up(*write_bytes, root->sectorsize) - 1;
+
+       while (1) {
+               lock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend);
+               ordered = btrfs_lookup_ordered_range(inode, lockstart,
+                                                    lockend - lockstart + 1);
+               if (!ordered) {
+                       break;
+               }
+               unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend);
+               btrfs_start_ordered_extent(inode, ordered, 1);
+               btrfs_put_ordered_extent(ordered);
+       }
+
+       trans = btrfs_join_transaction(root);
+       if (IS_ERR(trans)) {
+               unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend);
+               return PTR_ERR(trans);
+       }
+
+       num_bytes = lockend - lockstart + 1;
+       ret = can_nocow_extent(trans, inode, lockstart, &num_bytes, NULL, NULL,
+                              NULL);
+       btrfs_end_transaction(trans, root);
+       if (ret <= 0) {
+               ret = 0;
+       } else {
+               clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend,
+                                EXTENT_DIRTY | EXTENT_DELALLOC |
+                                EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, 0, 0,
+                                NULL, GFP_NOFS);
+               *write_bytes = min_t(size_t, *write_bytes, num_bytes);
+       }
+
+       unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend);
+
+       return ret;
+}
+
 static noinline ssize_t __btrfs_buffered_write(struct file *file,
                                               struct iov_iter *i,
                                               loff_t pos)
@@ -1324,10 +1370,12 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
        struct inode *inode = file_inode(file);
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct page **pages = NULL;
+       u64 release_bytes = 0;
        unsigned long first_index;
        size_t num_written = 0;
        int nrptrs;
        int ret = 0;
+       bool only_release_metadata = false;
        bool force_page_uptodate = false;
 
        nrptrs = min((iov_iter_count(i) + PAGE_CACHE_SIZE - 1) /
@@ -1348,6 +1396,7 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
                                         offset);
                size_t num_pages = (write_bytes + offset +
                                    PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+               size_t reserve_bytes;
                size_t dirty_pages;
                size_t copied;
 
@@ -1362,11 +1411,41 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
                        break;
                }
 
-               ret = btrfs_delalloc_reserve_space(inode,
-                                       num_pages << PAGE_CACHE_SHIFT);
+               reserve_bytes = num_pages << PAGE_CACHE_SHIFT;
+               ret = btrfs_check_data_free_space(inode, reserve_bytes);
+               if (ret == -ENOSPC &&
+                   (BTRFS_I(inode)->flags & (BTRFS_INODE_NODATACOW |
+                                             BTRFS_INODE_PREALLOC))) {
+                       ret = check_can_nocow(inode, pos, &write_bytes);
+                       if (ret > 0) {
+                               only_release_metadata = true;
+                               /*
+                                * our prealloc extent may be smaller than
+                                * write_bytes, so scale down.
+                                */
+                               num_pages = (write_bytes + offset +
+                                            PAGE_CACHE_SIZE - 1) >>
+                                       PAGE_CACHE_SHIFT;
+                               reserve_bytes = num_pages << PAGE_CACHE_SHIFT;
+                               ret = 0;
+                       } else {
+                               ret = -ENOSPC;
+                       }
+               }
+
                if (ret)
                        break;
 
+               ret = btrfs_delalloc_reserve_metadata(inode, reserve_bytes);
+               if (ret) {
+                       if (!only_release_metadata)
+                               btrfs_free_reserved_data_space(inode,
+                                                              reserve_bytes);
+                       break;
+               }
+
+               release_bytes = reserve_bytes;
+
                /*
                 * This is going to setup the pages array with the number of
                 * pages we want, so we don't really need to worry about the
@@ -1375,11 +1454,8 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
                ret = prepare_pages(root, file, pages, num_pages,
                                    pos, first_index, write_bytes,
                                    force_page_uptodate);
-               if (ret) {
-                       btrfs_delalloc_release_space(inode,
-                                       num_pages << PAGE_CACHE_SHIFT);
+               if (ret)
                        break;
-               }
 
                copied = btrfs_copy_from_user(pos, num_pages,
                                           write_bytes, pages, i);
@@ -1409,30 +1485,46 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
                 * managed to copy.
                 */
                if (num_pages > dirty_pages) {
+                       release_bytes = (num_pages - dirty_pages) <<
+                               PAGE_CACHE_SHIFT;
                        if (copied > 0) {
                                spin_lock(&BTRFS_I(inode)->lock);
                                BTRFS_I(inode)->outstanding_extents++;
                                spin_unlock(&BTRFS_I(inode)->lock);
                        }
-                       btrfs_delalloc_release_space(inode,
-                                       (num_pages - dirty_pages) <<
-                                       PAGE_CACHE_SHIFT);
+                       if (only_release_metadata)
+                               btrfs_delalloc_release_metadata(inode,
+                                                               release_bytes);
+                       else
+                               btrfs_delalloc_release_space(inode,
+                                                            release_bytes);
                }
 
+               release_bytes = dirty_pages << PAGE_CACHE_SHIFT;
                if (copied > 0) {
                        ret = btrfs_dirty_pages(root, inode, pages,
                                                dirty_pages, pos, copied,
                                                NULL);
                        if (ret) {
-                               btrfs_delalloc_release_space(inode,
-                                       dirty_pages << PAGE_CACHE_SHIFT);
                                btrfs_drop_pages(pages, num_pages);
                                break;
                        }
                }
 
+               release_bytes = 0;
                btrfs_drop_pages(pages, num_pages);
 
+               if (only_release_metadata && copied > 0) {
+                       u64 lockstart = round_down(pos, root->sectorsize);
+                       u64 lockend = lockstart +
+                               (dirty_pages << PAGE_CACHE_SHIFT) - 1;
+
+                       set_extent_bit(&BTRFS_I(inode)->io_tree, lockstart,
+                                      lockend, EXTENT_NORESERVE, NULL,
+                                      NULL, GFP_NOFS);
+                       only_release_metadata = false;
+               }
+
                cond_resched();
 
                balance_dirty_pages_ratelimited(inode->i_mapping);
@@ -1445,6 +1537,13 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
 
        kfree(pages);
 
+       if (release_bytes) {
+               if (only_release_metadata)
+                       btrfs_delalloc_release_metadata(inode, release_bytes);
+               else
+                       btrfs_delalloc_release_space(inode, release_bytes);
+       }
+
        return num_written ? num_written : ret;
 }
 
@@ -2175,12 +2274,6 @@ static long btrfs_fallocate(struct file *file, int mode,
                        goto out_reserve_fail;
        }
 
-       /*
-        * 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);
-
        mutex_lock(&inode->i_mutex);
        ret = inode_newsize_ok(inode, alloc_end);
        if (ret)
@@ -2191,8 +2284,23 @@ static long btrfs_fallocate(struct file *file, int mode,
                                        alloc_start);
                if (ret)
                        goto out;
+       } else {
+               /*
+                * If we are fallocating from the end of the file onward we
+                * need to zero out the end of the page if i_size lands in the
+                * middle of a page.
+                */
+               ret = btrfs_truncate_page(inode, inode->i_size, 0, 0);
+               if (ret)
+                       goto out;
        }
 
+       /*
+        * 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);
+
        locked_end = alloc_end - 1;
        while (1) {
                struct btrfs_ordered_extent *ordered;
index 2750b50..b21a3cd 100644 (file)
@@ -213,7 +213,7 @@ int btrfs_check_trunc_cache_free_space(struct btrfs_root *root,
        else
                ret = 0;
        spin_unlock(&rsv->lock);
-       return 0;
+       return ret;
 }
 
 int btrfs_truncate_free_space_cache(struct btrfs_root *root,
@@ -3150,6 +3150,8 @@ again:
        return 0;
 }
 
+#define test_msg(fmt, ...) printk(KERN_INFO "btrfs: selftest: " fmt, ##__VA_ARGS__)
+
 /*
  * This test just does basic sanity checking, making sure we can add an exten
  * entry and remove space from either end and the middle, and make sure we can
@@ -3159,63 +3161,63 @@ static int test_extents(struct btrfs_block_group_cache *cache)
 {
        int ret = 0;
 
-       printk(KERN_ERR "Running extent only tests\n");
+       test_msg("Running extent only tests\n");
 
        /* First just make sure we can remove an entire entry */
        ret = btrfs_add_free_space(cache, 0, 4 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Error adding initial extents %d\n", ret);
+               test_msg("Error adding initial extents %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, 0, 4 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Error removing extent %d\n", ret);
+               test_msg("Error removing extent %d\n", ret);
                return ret;
        }
 
        if (check_exists(cache, 0, 4 * 1024 * 1024)) {
-               printk(KERN_ERR "Full remove left some lingering space\n");
+               test_msg("Full remove left some lingering space\n");
                return -1;
        }
 
        /* Ok edge and middle cases now */
        ret = btrfs_add_free_space(cache, 0, 4 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Error adding half extent %d\n", ret);
+               test_msg("Error adding half extent %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, 3 * 1024 * 1024, 1 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Error removing tail end %d\n", ret);
+               test_msg("Error removing tail end %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, 0, 1 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Error removing front end %d\n", ret);
+               test_msg("Error removing front end %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, 2 * 1024 * 1024, 4096);
        if (ret) {
-               printk(KERN_ERR "Error removing middle piece %d\n", ret);
+               test_msg("Error removing middle piece %d\n", ret);
                return ret;
        }
 
        if (check_exists(cache, 0, 1 * 1024 * 1024)) {
-               printk(KERN_ERR "Still have space at the front\n");
+               test_msg("Still have space at the front\n");
                return -1;
        }
 
        if (check_exists(cache, 2 * 1024 * 1024, 4096)) {
-               printk(KERN_ERR "Still have space in the middle\n");
+               test_msg("Still have space in the middle\n");
                return -1;
        }
 
        if (check_exists(cache, 3 * 1024 * 1024, 1 * 1024 * 1024)) {
-               printk(KERN_ERR "Still have space at the end\n");
+               test_msg("Still have space at the end\n");
                return -1;
        }
 
@@ -3230,34 +3232,34 @@ static int test_bitmaps(struct btrfs_block_group_cache *cache)
        u64 next_bitmap_offset;
        int ret;
 
-       printk(KERN_ERR "Running bitmap only tests\n");
+       test_msg("Running bitmap only tests\n");
 
        ret = add_free_space_entry(cache, 0, 4 * 1024 * 1024, 1);
        if (ret) {
-               printk(KERN_ERR "Couldn't create a bitmap entry %d\n", ret);
+               test_msg("Couldn't create a bitmap entry %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, 0, 4 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Error removing bitmap full range %d\n", ret);
+               test_msg("Error removing bitmap full range %d\n", ret);
                return ret;
        }
 
        if (check_exists(cache, 0, 4 * 1024 * 1024)) {
-               printk(KERN_ERR "Left some space in bitmap\n");
+               test_msg("Left some space in bitmap\n");
                return -1;
        }
 
        ret = add_free_space_entry(cache, 0, 4 * 1024 * 1024, 1);
        if (ret) {
-               printk(KERN_ERR "Couldn't add to our bitmap entry %d\n", ret);
+               test_msg("Couldn't add to our bitmap entry %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, 1 * 1024 * 1024, 2 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Couldn't remove middle chunk %d\n", ret);
+               test_msg("Couldn't remove middle chunk %d\n", ret);
                return ret;
        }
 
@@ -3271,21 +3273,21 @@ static int test_bitmaps(struct btrfs_block_group_cache *cache)
        ret = add_free_space_entry(cache, next_bitmap_offset -
                                   (2 * 1024 * 1024), 4 * 1024 * 1024, 1);
        if (ret) {
-               printk(KERN_ERR "Couldn't add space that straddles two bitmaps"
-                      " %d\n", ret);
+               test_msg("Couldn't add space that straddles two bitmaps %d\n",
+                               ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, next_bitmap_offset -
                                      (1 * 1024 * 1024), 2 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Couldn't remove overlapping space %d\n", ret);
+               test_msg("Couldn't remove overlapping space %d\n", ret);
                return ret;
        }
 
        if (check_exists(cache, next_bitmap_offset - (1 * 1024 * 1024),
                         2 * 1024 * 1024)) {
-               printk(KERN_ERR "Left some space when removing overlapping\n");
+               test_msg("Left some space when removing overlapping\n");
                return -1;
        }
 
@@ -3300,7 +3302,7 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache)
        u64 bitmap_offset = (u64)(BITS_PER_BITMAP * 4096);
        int ret;
 
-       printk(KERN_ERR "Running bitmap and extent tests\n");
+       test_msg("Running bitmap and extent tests\n");
 
        /*
         * First let's do something simple, an extent at the same offset as the
@@ -3309,42 +3311,42 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache)
         */
        ret = add_free_space_entry(cache, 4 * 1024 * 1024, 1 * 1024 * 1024, 1);
        if (ret) {
-               printk(KERN_ERR "Couldn't create bitmap entry %d\n", ret);
+               test_msg("Couldn't create bitmap entry %d\n", ret);
                return ret;
        }
 
        ret = add_free_space_entry(cache, 0, 1 * 1024 * 1024, 0);
        if (ret) {
-               printk(KERN_ERR "Couldn't add extent entry %d\n", ret);
+               test_msg("Couldn't add extent entry %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, 0, 1 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Couldn't remove extent entry %d\n", ret);
+               test_msg("Couldn't remove extent entry %d\n", ret);
                return ret;
        }
 
        if (check_exists(cache, 0, 1 * 1024 * 1024)) {
-               printk(KERN_ERR "Left remnants after our remove\n");
+               test_msg("Left remnants after our remove\n");
                return -1;
        }
 
        /* Now to add back the extent entry and remove from the bitmap */
        ret = add_free_space_entry(cache, 0, 1 * 1024 * 1024, 0);
        if (ret) {
-               printk(KERN_ERR "Couldn't re-add extent entry %d\n", ret);
+               test_msg("Couldn't re-add extent entry %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, 4 * 1024 * 1024, 1 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Couldn't remove from bitmap %d\n", ret);
+               test_msg("Couldn't remove from bitmap %d\n", ret);
                return ret;
        }
 
        if (check_exists(cache, 4 * 1024 * 1024, 1 * 1024 * 1024)) {
-               printk(KERN_ERR "Left remnants in the bitmap\n");
+               test_msg("Left remnants in the bitmap\n");
                return -1;
        }
 
@@ -3354,19 +3356,18 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache)
         */
        ret = add_free_space_entry(cache, 1 * 1024 * 1024, 4 * 1024 * 1024, 1);
        if (ret) {
-               printk(KERN_ERR "Couldn't add to a bitmap %d\n", ret);
+               test_msg("Couldn't add to a bitmap %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, 512 * 1024, 3 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Couldn't remove overlapping space %d\n", ret);
+               test_msg("Couldn't remove overlapping space %d\n", ret);
                return ret;
        }
 
        if (check_exists(cache, 512 * 1024, 3 * 1024 * 1024)) {
-               printk(KERN_ERR "Left over peices after removing "
-                      "overlapping\n");
+               test_msg("Left over peices after removing overlapping\n");
                return -1;
        }
 
@@ -3375,24 +3376,24 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache)
        /* Now with the extent entry offset into the bitmap */
        ret = add_free_space_entry(cache, 4 * 1024 * 1024, 4 * 1024 * 1024, 1);
        if (ret) {
-               printk(KERN_ERR "Couldn't add space to the bitmap %d\n", ret);
+               test_msg("Couldn't add space to the bitmap %d\n", ret);
                return ret;
        }
 
        ret = add_free_space_entry(cache, 2 * 1024 * 1024, 2 * 1024 * 1024, 0);
        if (ret) {
-               printk(KERN_ERR "Couldn't add extent to the cache %d\n", ret);
+               test_msg("Couldn't add extent to the cache %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, 3 * 1024 * 1024, 4 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Problem removing overlapping space %d\n", ret);
+               test_msg("Problem removing overlapping space %d\n", ret);
                return ret;
        }
 
        if (check_exists(cache, 3 * 1024 * 1024, 4 * 1024 * 1024)) {
-               printk(KERN_ERR "Left something behind when removing space");
+               test_msg("Left something behind when removing space");
                return -1;
        }
 
@@ -3410,27 +3411,27 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache)
        ret = add_free_space_entry(cache, bitmap_offset + 4 * 1024 * 1024,
                                   4 * 1024 * 1024, 1);
        if (ret) {
-               printk(KERN_ERR "Couldn't add bitmap %d\n", ret);
+               test_msg("Couldn't add bitmap %d\n", ret);
                return ret;
        }
 
        ret = add_free_space_entry(cache, bitmap_offset - 1 * 1024 * 1024,
                                   5 * 1024 * 1024, 0);
        if (ret) {
-               printk(KERN_ERR "Couldn't add extent entry %d\n", ret);
+               test_msg("Couldn't add extent entry %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, bitmap_offset + 1 * 1024 * 1024,
                                      5 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Failed to free our space %d\n", ret);
+               test_msg("Failed to free our space %d\n", ret);
                return ret;
        }
 
        if (check_exists(cache, bitmap_offset + 1 * 1024 * 1024,
                         5 * 1024 * 1024)) {
-               printk(KERN_ERR "Left stuff over\n");
+               test_msg("Left stuff over\n");
                return -1;
        }
 
@@ -3444,20 +3445,19 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache)
         */
        ret = add_free_space_entry(cache, 1 * 1024 * 1024, 2 * 1024 * 1024, 1);
        if (ret) {
-               printk(KERN_ERR "Couldn't add bitmap entry %d\n", ret);
+               test_msg("Couldn't add bitmap entry %d\n", ret);
                return ret;
        }
 
        ret = add_free_space_entry(cache, 3 * 1024 * 1024, 1 * 1024 * 1024, 0);
        if (ret) {
-               printk(KERN_ERR "Couldn't add extent entry %d\n", ret);
+               test_msg("Couldn't add extent entry %d\n", ret);
                return ret;
        }
 
        ret = btrfs_remove_free_space(cache, 1 * 1024 * 1024, 3 * 1024 * 1024);
        if (ret) {
-               printk(KERN_ERR "Error removing bitmap and extent "
-                      "overlapping %d\n", ret);
+               test_msg("Error removing bitmap and extent overlapping %d\n", ret);
                return ret;
        }
 
@@ -3469,11 +3469,11 @@ void btrfs_test_free_space_cache(void)
 {
        struct btrfs_block_group_cache *cache;
 
-       printk(KERN_ERR "Running btrfs free space cache tests\n");
+       test_msg("Running btrfs free space cache tests\n");
 
        cache = init_test_block_group();
        if (!cache) {
-               printk(KERN_ERR "Couldn't run the tests\n");
+               test_msg("Couldn't run the tests\n");
                return;
        }
 
@@ -3487,6 +3487,9 @@ out:
        __btrfs_remove_free_space_cache(cache->free_space_ctl);
        kfree(cache->free_space_ctl);
        kfree(cache);
-       printk(KERN_ERR "Free space cache tests finished\n");
+       test_msg("Free space cache tests finished\n");
 }
-#endif /* CONFIG_BTRFS_FS_RUN_SANITY_TESTS */
+#undef test_msg
+#else /* !CONFIG_BTRFS_FS_RUN_SANITY_TESTS */
+void btrfs_test_free_space_cache(void) {}
+#endif /* !CONFIG_BTRFS_FS_RUN_SANITY_TESTS */
index 8b7f19f..894116b 100644 (file)
@@ -113,8 +113,6 @@ int btrfs_return_cluster_to_free_space(
 int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group,
                           u64 *trimmed, u64 start, u64 end, u64 minlen);
 
-#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
 void btrfs_test_free_space_cache(void);
-#endif
 
 #endif
index 4f9d16b..6d1b93c 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/mount.h>
 #include <linux/btrfs.h>
 #include <linux/blkdev.h>
+#include <linux/posix_acl_xattr.h>
 #include "compat.h"
 #include "ctree.h"
 #include "disk-io.h"
@@ -57,6 +58,7 @@
 #include "free-space-cache.h"
 #include "inode-map.h"
 #include "backref.h"
+#include "hash.h"
 
 struct btrfs_iget_args {
        u64 ino;
@@ -701,8 +703,12 @@ retry:
                        async_extent->nr_pages = 0;
                        async_extent->pages = NULL;
 
-                       if (ret == -ENOSPC)
+                       if (ret == -ENOSPC) {
+                               unlock_extent(io_tree, async_extent->start,
+                                             async_extent->start +
+                                             async_extent->ram_size - 1);
                                goto retry;
+                       }
                        goto out_free;
                }
 
@@ -1529,6 +1535,46 @@ static void btrfs_merge_extent_hook(struct inode *inode,
        spin_unlock(&BTRFS_I(inode)->lock);
 }
 
+static void btrfs_add_delalloc_inodes(struct btrfs_root *root,
+                                     struct inode *inode)
+{
+       spin_lock(&root->delalloc_lock);
+       if (list_empty(&BTRFS_I(inode)->delalloc_inodes)) {
+               list_add_tail(&BTRFS_I(inode)->delalloc_inodes,
+                             &root->delalloc_inodes);
+               set_bit(BTRFS_INODE_IN_DELALLOC_LIST,
+                       &BTRFS_I(inode)->runtime_flags);
+               root->nr_delalloc_inodes++;
+               if (root->nr_delalloc_inodes == 1) {
+                       spin_lock(&root->fs_info->delalloc_root_lock);
+                       BUG_ON(!list_empty(&root->delalloc_root));
+                       list_add_tail(&root->delalloc_root,
+                                     &root->fs_info->delalloc_roots);
+                       spin_unlock(&root->fs_info->delalloc_root_lock);
+               }
+       }
+       spin_unlock(&root->delalloc_lock);
+}
+
+static void btrfs_del_delalloc_inode(struct btrfs_root *root,
+                                    struct inode *inode)
+{
+       spin_lock(&root->delalloc_lock);
+       if (!list_empty(&BTRFS_I(inode)->delalloc_inodes)) {
+               list_del_init(&BTRFS_I(inode)->delalloc_inodes);
+               clear_bit(BTRFS_INODE_IN_DELALLOC_LIST,
+                         &BTRFS_I(inode)->runtime_flags);
+               root->nr_delalloc_inodes--;
+               if (!root->nr_delalloc_inodes) {
+                       spin_lock(&root->fs_info->delalloc_root_lock);
+                       BUG_ON(list_empty(&root->delalloc_root));
+                       list_del_init(&root->delalloc_root);
+                       spin_unlock(&root->fs_info->delalloc_root_lock);
+               }
+       }
+       spin_unlock(&root->delalloc_lock);
+}
+
 /*
  * extent_io.c set_bit_hook, used to track delayed allocation
  * bytes in this file, and to maintain the list of inodes that
@@ -1561,16 +1607,8 @@ static void btrfs_set_bit_hook(struct inode *inode,
                spin_lock(&BTRFS_I(inode)->lock);
                BTRFS_I(inode)->delalloc_bytes += len;
                if (do_list && !test_bit(BTRFS_INODE_IN_DELALLOC_LIST,
-                                        &BTRFS_I(inode)->runtime_flags)) {
-                       spin_lock(&root->fs_info->delalloc_lock);
-                       if (list_empty(&BTRFS_I(inode)->delalloc_inodes)) {
-                               list_add_tail(&BTRFS_I(inode)->delalloc_inodes,
-                                             &root->fs_info->delalloc_inodes);
-                               set_bit(BTRFS_INODE_IN_DELALLOC_LIST,
-                                       &BTRFS_I(inode)->runtime_flags);
-                       }
-                       spin_unlock(&root->fs_info->delalloc_lock);
-               }
+                                        &BTRFS_I(inode)->runtime_flags))
+                       btrfs_add_delalloc_inodes(root, inode);
                spin_unlock(&BTRFS_I(inode)->lock);
        }
 }
@@ -1604,7 +1642,7 @@ static void btrfs_clear_bit_hook(struct inode *inode,
                        btrfs_delalloc_release_metadata(inode, len);
 
                if (root->root_key.objectid != BTRFS_DATA_RELOC_TREE_OBJECTID
-                   && do_list)
+                   && do_list && !(state->state & EXTENT_NORESERVE))
                        btrfs_free_reserved_data_space(inode, len);
 
                __percpu_counter_add(&root->fs_info->delalloc_bytes, -len,
@@ -1613,15 +1651,8 @@ static void btrfs_clear_bit_hook(struct inode *inode,
                BTRFS_I(inode)->delalloc_bytes -= len;
                if (do_list && BTRFS_I(inode)->delalloc_bytes == 0 &&
                    test_bit(BTRFS_INODE_IN_DELALLOC_LIST,
-                            &BTRFS_I(inode)->runtime_flags)) {
-                       spin_lock(&root->fs_info->delalloc_lock);
-                       if (!list_empty(&BTRFS_I(inode)->delalloc_inodes)) {
-                               list_del_init(&BTRFS_I(inode)->delalloc_inodes);
-                               clear_bit(BTRFS_INODE_IN_DELALLOC_LIST,
-                                         &BTRFS_I(inode)->runtime_flags);
-                       }
-                       spin_unlock(&root->fs_info->delalloc_lock);
-               }
+                            &BTRFS_I(inode)->runtime_flags))
+                       btrfs_del_delalloc_inode(root, inode);
                spin_unlock(&BTRFS_I(inode)->lock);
        }
 }
@@ -2263,11 +2294,6 @@ static noinline int relink_extent_backref(struct btrfs_path *path,
                        return 0;
                return PTR_ERR(root);
        }
-       if (btrfs_root_refs(&root->root_item) == 0) {
-               srcu_read_unlock(&fs_info->subvol_srcu, index);
-               /* parse ENOENT to 0 */
-               return 0;
-       }
 
        /* step 2: get inode */
        key.objectid = backref->inum;
@@ -3215,13 +3241,16 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
                        /* 1 for the orphan item deletion. */
                        trans = btrfs_start_transaction(root, 1);
                        if (IS_ERR(trans)) {
+                               iput(inode);
                                ret = PTR_ERR(trans);
                                goto out;
                        }
                        ret = btrfs_orphan_add(trans, inode);
                        btrfs_end_transaction(trans, root);
-                       if (ret)
+                       if (ret) {
+                               iput(inode);
                                goto out;
+                       }
 
                        ret = btrfs_truncate(inode);
                        if (ret)
@@ -3274,8 +3303,17 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
 {
        u32 nritems = btrfs_header_nritems(leaf);
        struct btrfs_key found_key;
+       static u64 xattr_access = 0;
+       static u64 xattr_default = 0;
        int scanned = 0;
 
+       if (!xattr_access) {
+               xattr_access = btrfs_name_hash(POSIX_ACL_XATTR_ACCESS,
+                                       strlen(POSIX_ACL_XATTR_ACCESS));
+               xattr_default = btrfs_name_hash(POSIX_ACL_XATTR_DEFAULT,
+                                       strlen(POSIX_ACL_XATTR_DEFAULT));
+       }
+
        slot++;
        while (slot < nritems) {
                btrfs_item_key_to_cpu(leaf, &found_key, slot);
@@ -3285,8 +3323,11 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
                        return 0;
 
                /* we found an xattr, assume we've got an acl */
-               if (found_key.type == BTRFS_XATTR_ITEM_KEY)
-                       return 1;
+               if (found_key.type == BTRFS_XATTR_ITEM_KEY) {
+                       if (found_key.offset == xattr_access ||
+                           found_key.offset == xattr_default)
+                               return 1;
+               }
 
                /*
                 * we found a key greater than an xattr key, there can't
@@ -3660,53 +3701,20 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
        }
        return ret;
 }
-               
-
-/* helper to check if there is any shared block in the path */
-static int check_path_shared(struct btrfs_root *root,
-                            struct btrfs_path *path)
-{
-       struct extent_buffer *eb;
-       int level;
-       u64 refs = 1;
-
-       for (level = 0; level < BTRFS_MAX_LEVEL; level++) {
-               int ret;
-
-               if (!path->nodes[level])
-                       break;
-               eb = path->nodes[level];
-               if (!btrfs_block_can_be_shared(root, eb))
-                       continue;
-               ret = btrfs_lookup_extent_info(NULL, root, eb->start, level, 1,
-                                              &refs, NULL);
-               if (refs > 1)
-                       return 1;
-       }
-       return 0;
-}
 
 /*
  * helper to start transaction for unlink and rmdir.
  *
- * unlink and rmdir are special in btrfs, they do not always free space.
- * so in enospc case, we should make sure they will free space before
- * allowing them to use the global metadata reservation.
+ * unlink and rmdir are special in btrfs, they do not always free space, so
+ * if we cannot make our reservations the normal way try and see if there is
+ * plenty of slack room in the global reserve to migrate, otherwise we cannot
+ * allow the unlink to occur.
  */
-static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir,
-                                                      struct dentry *dentry)
+static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir)
 {
        struct btrfs_trans_handle *trans;
        struct btrfs_root *root = BTRFS_I(dir)->root;
-       struct btrfs_path *path;
-       struct btrfs_dir_item *di;
-       struct inode *inode = dentry->d_inode;
-       u64 index;
-       int check_link = 1;
-       int err = -ENOSPC;
        int ret;
-       u64 ino = btrfs_ino(inode);
-       u64 dir_ino = btrfs_ino(dir);
 
        /*
         * 1 for the possible orphan item
@@ -3719,158 +3727,23 @@ static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir,
        if (!IS_ERR(trans) || PTR_ERR(trans) != -ENOSPC)
                return trans;
 
-       if (ino == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
-               return ERR_PTR(-ENOSPC);
-
-       /* check if there is someone else holds reference */
-       if (S_ISDIR(inode->i_mode) && atomic_read(&inode->i_count) > 1)
-               return ERR_PTR(-ENOSPC);
-
-       if (atomic_read(&inode->i_count) > 2)
-               return ERR_PTR(-ENOSPC);
-
-       if (xchg(&root->fs_info->enospc_unlink, 1))
-               return ERR_PTR(-ENOSPC);
-
-       path = btrfs_alloc_path();
-       if (!path) {
-               root->fs_info->enospc_unlink = 0;
-               return ERR_PTR(-ENOMEM);
-       }
+       if (PTR_ERR(trans) == -ENOSPC) {
+               u64 num_bytes = btrfs_calc_trans_metadata_size(root, 5);
 
-       /* 1 for the orphan item */
-       trans = btrfs_start_transaction(root, 1);
-       if (IS_ERR(trans)) {
-               btrfs_free_path(path);
-               root->fs_info->enospc_unlink = 0;
-               return trans;
-       }
-
-       path->skip_locking = 1;
-       path->search_commit_root = 1;
-
-       ret = btrfs_lookup_inode(trans, root, path,
-                               &BTRFS_I(dir)->location, 0);
-       if (ret < 0) {
-               err = ret;
-               goto out;
-       }
-       if (ret == 0) {
-               if (check_path_shared(root, path))
-                       goto out;
-       } else {
-               check_link = 0;
-       }
-       btrfs_release_path(path);
-
-       ret = btrfs_lookup_inode(trans, root, path,
-                               &BTRFS_I(inode)->location, 0);
-       if (ret < 0) {
-               err = ret;
-               goto out;
-       }
-       if (ret == 0) {
-               if (check_path_shared(root, path))
-                       goto out;
-       } else {
-               check_link = 0;
-       }
-       btrfs_release_path(path);
-
-       if (ret == 0 && S_ISREG(inode->i_mode)) {
-               ret = btrfs_lookup_file_extent(trans, root, path,
-                                              ino, (u64)-1, 0);
-               if (ret < 0) {
-                       err = ret;
-                       goto out;
+               trans = btrfs_start_transaction(root, 0);
+               if (IS_ERR(trans))
+                       return trans;
+               ret = btrfs_cond_migrate_bytes(root->fs_info,
+                                              &root->fs_info->trans_block_rsv,
+                                              num_bytes, 5);
+               if (ret) {
+                       btrfs_end_transaction(trans, root);
+                       return ERR_PTR(ret);
                }
-               BUG_ON(ret == 0); /* Corruption */
-               if (check_path_shared(root, path))
-                       goto out;
-               btrfs_release_path(path);
-       }
-
-       if (!check_link) {
-               err = 0;
-               goto out;
-       }
-
-       di = btrfs_lookup_dir_item(trans, root, path, dir_ino,
-                               dentry->d_name.name, dentry->d_name.len, 0);
-       if (IS_ERR(di)) {
-               err = PTR_ERR(di);
-               goto out;
-       }
-       if (di) {
-               if (check_path_shared(root, path))
-                       goto out;
-       } else {
-               err = 0;
-               goto out;
-       }
-       btrfs_release_path(path);
-
-       ret = btrfs_get_inode_ref_index(trans, root, path, dentry->d_name.name,
-                                       dentry->d_name.len, ino, dir_ino, 0,
-                                       &index);
-       if (ret) {
-               err = ret;
-               goto out;
-       }
-
-       if (check_path_shared(root, path))
-               goto out;
-
-       btrfs_release_path(path);
-
-       /*
-        * This is a commit root search, if we can lookup inode item and other
-        * relative items in the commit root, it means the transaction of
-        * dir/file creation has been committed, and the dir index item that we
-        * delay to insert has also been inserted into the commit root. So
-        * we needn't worry about the delayed insertion of the dir index item
-        * here.
-        */
-       di = btrfs_lookup_dir_index_item(trans, root, path, dir_ino, index,
-                               dentry->d_name.name, dentry->d_name.len, 0);
-       if (IS_ERR(di)) {
-               err = PTR_ERR(di);
-               goto out;
-       }
-       BUG_ON(ret == -ENOENT);
-       if (check_path_shared(root, path))
-               goto out;
-
-       err = 0;
-out:
-       btrfs_free_path(path);
-       /* Migrate the orphan reservation over */
-       if (!err)
-               err = btrfs_block_rsv_migrate(trans->block_rsv,
-                               &root->fs_info->global_block_rsv,
-                               trans->bytes_reserved);
-
-       if (err) {
-               btrfs_end_transaction(trans, root);
-               root->fs_info->enospc_unlink = 0;
-               return ERR_PTR(err);
-       }
-
-       trans->block_rsv = &root->fs_info->global_block_rsv;
-       return trans;
-}
-
-static void __unlink_end_trans(struct btrfs_trans_handle *trans,
-                              struct btrfs_root *root)
-{
-       if (trans->block_rsv->type == BTRFS_BLOCK_RSV_GLOBAL) {
-               btrfs_block_rsv_release(root, trans->block_rsv,
-                                       trans->bytes_reserved);
                trans->block_rsv = &root->fs_info->trans_block_rsv;
-               BUG_ON(!root->fs_info->enospc_unlink);
-               root->fs_info->enospc_unlink = 0;
+               trans->bytes_reserved = num_bytes;
        }
-       btrfs_end_transaction(trans, root);
+       return trans;
 }
 
 static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
@@ -3880,7 +3753,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
        struct inode *inode = dentry->d_inode;
        int ret;
 
-       trans = __unlink_start_trans(dir, dentry);
+       trans = __unlink_start_trans(dir);
        if (IS_ERR(trans))
                return PTR_ERR(trans);
 
@@ -3898,7 +3771,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
        }
 
 out:
-       __unlink_end_trans(trans, root);
+       btrfs_end_transaction(trans, root);
        btrfs_btree_balance_dirty(root);
        return ret;
 }
@@ -3995,7 +3868,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID)
                return -EPERM;
 
-       trans = __unlink_start_trans(dir, dentry);
+       trans = __unlink_start_trans(dir);
        if (IS_ERR(trans))
                return PTR_ERR(trans);
 
@@ -4017,7 +3890,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (!err)
                btrfs_i_size_write(inode, 0);
 out:
-       __unlink_end_trans(trans, root);
+       btrfs_end_transaction(trans, root);
        btrfs_btree_balance_dirty(root);
 
        return err;
@@ -4395,6 +4268,15 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
        u64 hole_size;
        int err = 0;
 
+       /*
+        * If our size started in the middle of a page we need to zero out the
+        * rest of the page before we expand the i_size, otherwise we could
+        * expose stale data.
+        */
+       err = btrfs_truncate_page(inode, oldsize, 0, 0);
+       if (err)
+               return err;
+
        if (size <= hole_start)
                return 0;
 
@@ -4822,11 +4704,6 @@ static int fixup_tree_root_location(struct btrfs_root *root,
                goto out;
        }
 
-       if (btrfs_root_refs(&new_root->root_item) == 0) {
-               err = -ENOENT;
-               goto out;
-       }
-
        *sub_root = new_root;
        location->objectid = btrfs_root_dirid(&new_root->root_item);
        location->type = BTRFS_INODE_ITEM_KEY;
@@ -5092,8 +4969,10 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
                if (!(inode->i_sb->s_flags & MS_RDONLY))
                        ret = btrfs_orphan_cleanup(sub_root);
                up_read(&root->fs_info->cleanup_work_sem);
-               if (ret)
+               if (ret) {
+                       iput(inode);
                        inode = ERR_PTR(ret);
+               }
        }
 
        return inode;
@@ -6501,10 +6380,10 @@ out:
  * returns 1 when the nocow is safe, < 1 on error, 0 if the
  * block must be cow'd
  */
-static noinline int can_nocow_odirect(struct btrfs_trans_handle *trans,
-                                     struct inode *inode, u64 offset, u64 *len,
-                                     u64 *orig_start, u64 *orig_block_len,
-                                     u64 *ram_bytes)
+noinline int can_nocow_extent(struct btrfs_trans_handle *trans,
+                             struct inode *inode, u64 offset, u64 *len,
+                             u64 *orig_start, u64 *orig_block_len,
+                             u64 *ram_bytes)
 {
        struct btrfs_path *path;
        int ret;
@@ -6518,7 +6397,7 @@ static noinline int can_nocow_odirect(struct btrfs_trans_handle *trans,
        u64 num_bytes;
        int slot;
        int found_type;
-
+       bool nocow = (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW);
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -6558,18 +6437,28 @@ static noinline int can_nocow_odirect(struct btrfs_trans_handle *trans,
                /* not a regular extent, must cow */
                goto out;
        }
+
+       if (!nocow && found_type == BTRFS_FILE_EXTENT_REG)
+               goto out;
+
        disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
+       if (disk_bytenr == 0)
+               goto out;
+
+       if (btrfs_file_extent_compression(leaf, fi) ||
+           btrfs_file_extent_encryption(leaf, fi) ||
+           btrfs_file_extent_other_encoding(leaf, fi))
+               goto out;
+
        backref_offset = btrfs_file_extent_offset(leaf, fi);
 
-       *orig_start = key.offset - backref_offset;
-       *orig_block_len = btrfs_file_extent_disk_num_bytes(leaf, fi);
-       *ram_bytes = btrfs_file_extent_ram_bytes(leaf, fi);
+       if (orig_start) {
+               *orig_start = key.offset - backref_offset;
+               *orig_block_len = btrfs_file_extent_disk_num_bytes(leaf, fi);
+               *ram_bytes = btrfs_file_extent_ram_bytes(leaf, fi);
+       }
 
        extent_end = key.offset + btrfs_file_extent_num_bytes(leaf, fi);
-       if (extent_end < offset + *len) {
-               /* extent doesn't include our full range, must cow */
-               goto out;
-       }
 
        if (btrfs_extent_readonly(root, disk_bytenr))
                goto out;
@@ -6813,8 +6702,8 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
                if (IS_ERR(trans))
                        goto must_cow;
 
-               if (can_nocow_odirect(trans, inode, start, &len, &orig_start,
-                                     &orig_block_len, &ram_bytes) == 1) {
+               if (can_nocow_extent(trans, inode, start, &len, &orig_start,
+                                    &orig_block_len, &ram_bytes) == 1) {
                        if (type == BTRFS_ORDERED_PREALLOC) {
                                free_extent_map(em);
                                em = create_pinned_em(inode, start, len,
@@ -7243,7 +7132,6 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio,
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_dio_private *dip;
-       struct bio_vec *bvec = dio_bio->bi_io_vec;
        struct bio *io_bio;
        int skip_sum;
        int write = rw & REQ_WRITE;
@@ -7265,16 +7153,9 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio,
        }
 
        dip->private = dio_bio->bi_private;
-       io_bio->bi_private = dio_bio->bi_private;
        dip->inode = inode;
        dip->logical_offset = file_offset;
-
-       dip->bytes = 0;
-       do {
-               dip->bytes += bvec->bv_len;
-               bvec++;
-       } while (bvec <= (dio_bio->bi_io_vec + dio_bio->bi_vcnt - 1));
-
+       dip->bytes = dio_bio->bi_size;
        dip->disk_bytenr = (u64)dio_bio->bi_sector << 9;
        io_bio->bi_private = dip;
        dip->errors = 0;
@@ -7373,8 +7254,16 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
        atomic_inc(&inode->i_dio_count);
        smp_mb__after_atomic_inc();
 
+       /*
+        * The generic stuff only does filemap_write_and_wait_range, which isn't
+        * enough if we've written compressed pages to this area, so we need to
+        * call btrfs_wait_ordered_range to make absolutely sure that any
+        * outstanding dirty pages are on disk.
+        */
+       count = iov_length(iov, nr_segs);
+       btrfs_wait_ordered_range(inode, offset, count);
+
        if (rw & WRITE) {
-               count = iov_length(iov, nr_segs);
                /*
                 * If the write DIO is beyond the EOF, we need update
                 * the isize, but it is protected by i_mutex. So we can
@@ -7694,16 +7583,12 @@ static int btrfs_truncate(struct inode *inode)
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_block_rsv *rsv;
-       int ret;
+       int ret = 0;
        int err = 0;
        struct btrfs_trans_handle *trans;
        u64 mask = root->sectorsize - 1;
        u64 min_size = btrfs_calc_trunc_metadata_size(root, 1);
 
-       ret = btrfs_truncate_page(inode, inode->i_size, 0, 0);
-       if (ret)
-               return ret;
-
        btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
        btrfs_ordered_update_i_size(inode, inode->i_size, NULL);
 
@@ -7961,9 +7846,9 @@ void btrfs_destroy_inode(struct inode *inode)
         */
        smp_mb();
        if (!list_empty(&BTRFS_I(inode)->ordered_operations)) {
-               spin_lock(&root->fs_info->ordered_extent_lock);
+               spin_lock(&root->fs_info->ordered_root_lock);
                list_del_init(&BTRFS_I(inode)->ordered_operations);
-               spin_unlock(&root->fs_info->ordered_extent_lock);
+               spin_unlock(&root->fs_info->ordered_root_lock);
        }
 
        if (test_bit(BTRFS_INODE_HAS_ORPHAN_ITEM,
@@ -8333,7 +8218,7 @@ void btrfs_wait_and_free_delalloc_work(struct btrfs_delalloc_work *work)
  * some fairly slow code that needs optimization. This walks the list
  * of all the inodes with pending delalloc and forces them to disk.
  */
-int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
+static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
 {
        struct btrfs_inode *binode;
        struct inode *inode;
@@ -8342,30 +8227,23 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
        struct list_head splice;
        int ret = 0;
 
-       if (root->fs_info->sb->s_flags & MS_RDONLY)
-               return -EROFS;
-
        INIT_LIST_HEAD(&works);
        INIT_LIST_HEAD(&splice);
 
-       spin_lock(&root->fs_info->delalloc_lock);
-       list_splice_init(&root->fs_info->delalloc_inodes, &splice);
+       spin_lock(&root->delalloc_lock);
+       list_splice_init(&root->delalloc_inodes, &splice);
        while (!list_empty(&splice)) {
                binode = list_entry(splice.next, struct btrfs_inode,
                                    delalloc_inodes);
 
-               list_del_init(&binode->delalloc_inodes);
-
+               list_move_tail(&binode->delalloc_inodes,
+                              &root->delalloc_inodes);
                inode = igrab(&binode->vfs_inode);
                if (!inode) {
-                       clear_bit(BTRFS_INODE_IN_DELALLOC_LIST,
-                                 &binode->runtime_flags);
+                       cond_resched_lock(&root->delalloc_lock);
                        continue;
                }
-
-               list_add_tail(&binode->delalloc_inodes,
-                             &root->fs_info->delalloc_inodes);
-               spin_unlock(&root->fs_info->delalloc_lock);
+               spin_unlock(&root->delalloc_lock);
 
                work = btrfs_alloc_delalloc_work(inode, 0, delay_iput);
                if (unlikely(!work)) {
@@ -8377,16 +8255,39 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
                                   &work->work);
 
                cond_resched();
-               spin_lock(&root->fs_info->delalloc_lock);
+               spin_lock(&root->delalloc_lock);
        }
-       spin_unlock(&root->fs_info->delalloc_lock);
+       spin_unlock(&root->delalloc_lock);
 
        list_for_each_entry_safe(work, next, &works, list) {
                list_del_init(&work->list);
                btrfs_wait_and_free_delalloc_work(work);
        }
+       return 0;
+out:
+       list_for_each_entry_safe(work, next, &works, list) {
+               list_del_init(&work->list);
+               btrfs_wait_and_free_delalloc_work(work);
+       }
+
+       if (!list_empty_careful(&splice)) {
+               spin_lock(&root->delalloc_lock);
+               list_splice_tail(&splice, &root->delalloc_inodes);
+               spin_unlock(&root->delalloc_lock);
+       }
+       return ret;
+}
+
+int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
+{
+       int ret;
 
-       /* the filemap_flush will queue IO into the worker threads, but
+       if (root->fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
+       ret = __start_delalloc_inodes(root, delay_iput);
+       /*
+        * the filemap_flush will queue IO into the worker threads, but
         * we have to make sure the IO is actually started and that
         * ordered extents get created before we return
         */
@@ -8398,17 +8299,55 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
                    atomic_read(&root->fs_info->async_delalloc_pages) == 0));
        }
        atomic_dec(&root->fs_info->async_submit_draining);
-       return 0;
-out:
-       list_for_each_entry_safe(work, next, &works, list) {
-               list_del_init(&work->list);
-               btrfs_wait_and_free_delalloc_work(work);
+       return ret;
+}
+
+int btrfs_start_all_delalloc_inodes(struct btrfs_fs_info *fs_info,
+                                   int delay_iput)
+{
+       struct btrfs_root *root;
+       struct list_head splice;
+       int ret;
+
+       if (fs_info->sb->s_flags & MS_RDONLY)
+               return -EROFS;
+
+       INIT_LIST_HEAD(&splice);
+
+       spin_lock(&fs_info->delalloc_root_lock);
+       list_splice_init(&fs_info->delalloc_roots, &splice);
+       while (!list_empty(&splice)) {
+               root = list_first_entry(&splice, struct btrfs_root,
+                                       delalloc_root);
+               root = btrfs_grab_fs_root(root);
+               BUG_ON(!root);
+               list_move_tail(&root->delalloc_root,
+                              &fs_info->delalloc_roots);
+               spin_unlock(&fs_info->delalloc_root_lock);
+
+               ret = __start_delalloc_inodes(root, delay_iput);
+               btrfs_put_fs_root(root);
+               if (ret)
+                       goto out;
+
+               spin_lock(&fs_info->delalloc_root_lock);
        }
+       spin_unlock(&fs_info->delalloc_root_lock);
 
+       atomic_inc(&fs_info->async_submit_draining);
+       while (atomic_read(&fs_info->nr_async_submits) ||
+             atomic_read(&fs_info->async_delalloc_pages)) {
+               wait_event(fs_info->async_submit_wait,
+                  (atomic_read(&fs_info->nr_async_submits) == 0 &&
+                   atomic_read(&fs_info->async_delalloc_pages) == 0));
+       }
+       atomic_dec(&fs_info->async_submit_draining);
+       return 0;
+out:
        if (!list_empty_careful(&splice)) {
-               spin_lock(&root->fs_info->delalloc_lock);
-               list_splice_tail(&splice, &root->fs_info->delalloc_inodes);
-               spin_unlock(&root->fs_info->delalloc_lock);
+               spin_lock(&fs_info->delalloc_root_lock);
+               list_splice_tail(&splice, &fs_info->delalloc_roots);
+               spin_unlock(&fs_info->delalloc_root_lock);
        }
        return ret;
 }
index cd7e96c..238a055 100644 (file)
@@ -555,6 +555,12 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
        if (!root->ref_cows)
                return -EINVAL;
 
+       ret = btrfs_start_delalloc_inodes(root, 0);
+       if (ret)
+               return ret;
+
+       btrfs_wait_ordered_extents(root, 0);
+
        pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_NOFS);
        if (!pending_snapshot)
                return -ENOMEM;
@@ -2354,14 +2360,6 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
        if (ret)
                return ret;
 
-       if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,
-                       1)) {
-               pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
-               mnt_drop_write_file(file);
-               return -EINVAL;
-       }
-
-       mutex_lock(&root->fs_info->volume_mutex);
        vol_args = memdup_user(arg, sizeof(*vol_args));
        if (IS_ERR(vol_args)) {
                ret = PTR_ERR(vol_args);
@@ -2369,12 +2367,20 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)
        }
 
        vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
-       ret = btrfs_rm_device(root, vol_args->name);
 
-       kfree(vol_args);
-out:
+       if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,
+                       1)) {
+               ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS;
+               goto out;
+       }
+
+       mutex_lock(&root->fs_info->volume_mutex);
+       ret = btrfs_rm_device(root, vol_args->name);
        mutex_unlock(&root->fs_info->volume_mutex);
        atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0);
+
+out:
+       kfree(vol_args);
        mnt_drop_write_file(file);
        return ret;
 }
@@ -2480,6 +2486,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
        int ret;
        u64 len = olen;
        u64 bs = root->fs_info->sb->s_blocksize;
+       int same_inode = 0;
 
        /*
         * TODO:
@@ -2516,7 +2523,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
 
        ret = -EINVAL;
        if (src == inode)
-               goto out_fput;
+               same_inode = 1;
 
        /* the src must be open for reading */
        if (!(src_file.file->f_mode & FMODE_READ))
@@ -2547,12 +2554,16 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
        }
        path->reada = 2;
 
-       if (inode < src) {
-               mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
-               mutex_lock_nested(&src->i_mutex, I_MUTEX_CHILD);
+       if (!same_inode) {
+               if (inode < src) {
+                       mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
+                       mutex_lock_nested(&src->i_mutex, I_MUTEX_CHILD);
+               } else {
+                       mutex_lock_nested(&src->i_mutex, I_MUTEX_PARENT);
+                       mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
+               }
        } else {
-               mutex_lock_nested(&src->i_mutex, I_MUTEX_PARENT);
-               mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
+               mutex_lock(&src->i_mutex);
        }
 
        /* determine range to clone */
@@ -2570,6 +2581,12 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
            !IS_ALIGNED(destoff, bs))
                goto out_unlock;
 
+       /* verify if ranges are overlapped within the same file */
+       if (same_inode) {
+               if (destoff + len > off && destoff < off + len)
+                       goto out_unlock;
+       }
+
        if (destoff > inode->i_size) {
                ret = btrfs_cont_expand(inode, inode->i_size, destoff);
                if (ret)
@@ -2846,7 +2863,8 @@ out:
        unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1);
 out_unlock:
        mutex_unlock(&src->i_mutex);
-       mutex_unlock(&inode->i_mutex);
+       if (!same_inode)
+               mutex_unlock(&inode->i_mutex);
        vfree(buf);
        btrfs_free_path(path);
 out_fput:
@@ -2951,11 +2969,6 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
                goto out;
        }
 
-       if (btrfs_root_refs(&new_root->root_item) == 0) {
-               ret = -ENOENT;
-               goto out;
-       }
-
        path = btrfs_alloc_path();
        if (!path) {
                ret = -ENOMEM;
@@ -3719,9 +3732,6 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)
                break;
        }
 
-       if (copy_to_user(arg, sa, sizeof(*sa)))
-               ret = -EFAULT;
-
        err = btrfs_commit_transaction(trans, root->fs_info->tree_root);
        if (err && !ret)
                ret = err;
@@ -3937,6 +3947,16 @@ static long btrfs_ioctl_quota_rescan_status(struct file *file, void __user *arg)
        return ret;
 }
 
+static long btrfs_ioctl_quota_rescan_wait(struct file *file, void __user *arg)
+{
+       struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       return btrfs_qgroup_wait_for_completion(root->fs_info);
+}
+
 static long btrfs_ioctl_set_received_subvol(struct file *file,
                                            void __user *arg)
 {
@@ -4179,6 +4199,8 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_quota_rescan(file, argp);
        case BTRFS_IOC_QUOTA_RESCAN_STATUS:
                return btrfs_ioctl_quota_rescan_status(file, argp);
+       case BTRFS_IOC_QUOTA_RESCAN_WAIT:
+               return btrfs_ioctl_quota_rescan_wait(file, argp);
        case BTRFS_IOC_DEV_REPLACE:
                return btrfs_ioctl_dev_replace(root, argp);
        case BTRFS_IOC_GET_FSLABEL:
index 743b86f..f93151a 100644 (file)
@@ -31,8 +31,8 @@
 
 struct workspace {
        void *mem;
-       void *buf;      /* where compressed data goes */
-       void *cbuf;     /* where decompressed data goes */
+       void *buf;      /* where decompressed data goes */
+       void *cbuf;     /* where compressed data goes */
        struct list_head list;
 };
 
index 1ddd728..8136982 100644 (file)
@@ -24,6 +24,7 @@
 #include "transaction.h"
 #include "btrfs_inode.h"
 #include "extent_io.h"
+#include "disk-io.h"
 
 static struct kmem_cache *btrfs_ordered_extent_cache;
 
@@ -184,6 +185,7 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
                                      u64 start, u64 len, u64 disk_len,
                                      int type, int dio, int compress_type)
 {
+       struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_ordered_inode_tree *tree;
        struct rb_node *node;
        struct btrfs_ordered_extent *entry;
@@ -227,10 +229,18 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
                ordered_data_tree_panic(inode, -EEXIST, file_offset);
        spin_unlock_irq(&tree->lock);
 
-       spin_lock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
+       spin_lock(&root->ordered_extent_lock);
        list_add_tail(&entry->root_extent_list,
-                     &BTRFS_I(inode)->root->fs_info->ordered_extents);
-       spin_unlock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
+                     &root->ordered_extents);
+       root->nr_ordered_extents++;
+       if (root->nr_ordered_extents == 1) {
+               spin_lock(&root->fs_info->ordered_root_lock);
+               BUG_ON(!list_empty(&root->ordered_root));
+               list_add_tail(&root->ordered_root,
+                             &root->fs_info->ordered_roots);
+               spin_unlock(&root->fs_info->ordered_root_lock);
+       }
+       spin_unlock(&root->ordered_extent_lock);
 
        return 0;
 }
@@ -516,8 +526,9 @@ void btrfs_remove_ordered_extent(struct inode *inode,
        set_bit(BTRFS_ORDERED_COMPLETE, &entry->flags);
        spin_unlock_irq(&tree->lock);
 
-       spin_lock(&root->fs_info->ordered_extent_lock);
+       spin_lock(&root->ordered_extent_lock);
        list_del_init(&entry->root_extent_list);
+       root->nr_ordered_extents--;
 
        trace_btrfs_ordered_extent_remove(inode, entry);
 
@@ -530,7 +541,14 @@ void btrfs_remove_ordered_extent(struct inode *inode,
            !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_DIRTY)) {
                list_del_init(&BTRFS_I(inode)->ordered_operations);
        }
-       spin_unlock(&root->fs_info->ordered_extent_lock);
+
+       if (!root->nr_ordered_extents) {
+               spin_lock(&root->fs_info->ordered_root_lock);
+               BUG_ON(list_empty(&root->ordered_root));
+               list_del_init(&root->ordered_root);
+               spin_unlock(&root->fs_info->ordered_root_lock);
+       }
+       spin_unlock(&root->ordered_extent_lock);
        wake_up(&entry->wait);
 }
 
@@ -550,7 +568,6 @@ static void btrfs_run_ordered_extent_work(struct btrfs_work *work)
 void btrfs_wait_ordered_extents(struct btrfs_root *root, int delay_iput)
 {
        struct list_head splice, works;
-       struct list_head *cur;
        struct btrfs_ordered_extent *ordered, *next;
        struct inode *inode;
 
@@ -558,35 +575,34 @@ void btrfs_wait_ordered_extents(struct btrfs_root *root, int delay_iput)
        INIT_LIST_HEAD(&works);
 
        mutex_lock(&root->fs_info->ordered_operations_mutex);
-       spin_lock(&root->fs_info->ordered_extent_lock);
-       list_splice_init(&root->fs_info->ordered_extents, &splice);
+       spin_lock(&root->ordered_extent_lock);
+       list_splice_init(&root->ordered_extents, &splice);
        while (!list_empty(&splice)) {
-               cur = splice.next;
-               ordered = list_entry(cur, struct btrfs_ordered_extent,
-                                    root_extent_list);
-               list_del_init(&ordered->root_extent_list);
-               atomic_inc(&ordered->refs);
-
+               ordered = list_first_entry(&splice, struct btrfs_ordered_extent,
+                                          root_extent_list);
+               list_move_tail(&ordered->root_extent_list,
+                              &root->ordered_extents);
                /*
                 * the inode may be getting freed (in sys_unlink path).
                 */
                inode = igrab(ordered->inode);
+               if (!inode) {
+                       cond_resched_lock(&root->ordered_extent_lock);
+                       continue;
+               }
 
-               spin_unlock(&root->fs_info->ordered_extent_lock);
+               atomic_inc(&ordered->refs);
+               spin_unlock(&root->ordered_extent_lock);
 
-               if (inode) {
-                       ordered->flush_work.func = btrfs_run_ordered_extent_work;
-                       list_add_tail(&ordered->work_list, &works);
-                       btrfs_queue_worker(&root->fs_info->flush_workers,
-                                          &ordered->flush_work);
-               } else {
-                       btrfs_put_ordered_extent(ordered);
-               }
+               ordered->flush_work.func = btrfs_run_ordered_extent_work;
+               list_add_tail(&ordered->work_list, &works);
+               btrfs_queue_worker(&root->fs_info->flush_workers,
+                                  &ordered->flush_work);
 
                cond_resched();
-               spin_lock(&root->fs_info->ordered_extent_lock);
+               spin_lock(&root->ordered_extent_lock);
        }
-       spin_unlock(&root->fs_info->ordered_extent_lock);
+       spin_unlock(&root->ordered_extent_lock);
 
        list_for_each_entry_safe(ordered, next, &works, work_list) {
                list_del_init(&ordered->work_list);
@@ -604,6 +620,33 @@ void btrfs_wait_ordered_extents(struct btrfs_root *root, int delay_iput)
        mutex_unlock(&root->fs_info->ordered_operations_mutex);
 }
 
+void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info,
+                                   int delay_iput)
+{
+       struct btrfs_root *root;
+       struct list_head splice;
+
+       INIT_LIST_HEAD(&splice);
+
+       spin_lock(&fs_info->ordered_root_lock);
+       list_splice_init(&fs_info->ordered_roots, &splice);
+       while (!list_empty(&splice)) {
+               root = list_first_entry(&splice, struct btrfs_root,
+                                       ordered_root);
+               root = btrfs_grab_fs_root(root);
+               BUG_ON(!root);
+               list_move_tail(&root->ordered_root,
+                              &fs_info->ordered_roots);
+               spin_unlock(&fs_info->ordered_root_lock);
+
+               btrfs_wait_ordered_extents(root, delay_iput);
+               btrfs_put_fs_root(root);
+
+               spin_lock(&fs_info->ordered_root_lock);
+       }
+       spin_unlock(&fs_info->ordered_root_lock);
+}
+
 /*
  * this is used during transaction commit to write all the inodes
  * added to the ordered operation list.  These files must be fully on
@@ -629,7 +672,7 @@ int btrfs_run_ordered_operations(struct btrfs_trans_handle *trans,
        INIT_LIST_HEAD(&works);
 
        mutex_lock(&root->fs_info->ordered_operations_mutex);
-       spin_lock(&root->fs_info->ordered_extent_lock);
+       spin_lock(&root->fs_info->ordered_root_lock);
        list_splice_init(&cur_trans->ordered_operations, &splice);
        while (!list_empty(&splice)) {
                btrfs_inode = list_entry(splice.next, struct btrfs_inode,
@@ -648,17 +691,17 @@ int btrfs_run_ordered_operations(struct btrfs_trans_handle *trans,
                if (!wait)
                        list_add_tail(&BTRFS_I(inode)->ordered_operations,
                                      &cur_trans->ordered_operations);
-               spin_unlock(&root->fs_info->ordered_extent_lock);
+               spin_unlock(&root->fs_info->ordered_root_lock);
 
                work = btrfs_alloc_delalloc_work(inode, wait, 1);
                if (!work) {
-                       spin_lock(&root->fs_info->ordered_extent_lock);
+                       spin_lock(&root->fs_info->ordered_root_lock);
                        if (list_empty(&BTRFS_I(inode)->ordered_operations))
                                list_add_tail(&btrfs_inode->ordered_operations,
                                              &splice);
                        list_splice_tail(&splice,
                                         &cur_trans->ordered_operations);
-                       spin_unlock(&root->fs_info->ordered_extent_lock);
+                       spin_unlock(&root->fs_info->ordered_root_lock);
                        ret = -ENOMEM;
                        goto out;
                }
@@ -667,9 +710,9 @@ int btrfs_run_ordered_operations(struct btrfs_trans_handle *trans,
                                   &work->work);
 
                cond_resched();
-               spin_lock(&root->fs_info->ordered_extent_lock);
+               spin_lock(&root->fs_info->ordered_root_lock);
        }
-       spin_unlock(&root->fs_info->ordered_extent_lock);
+       spin_unlock(&root->fs_info->ordered_root_lock);
 out:
        list_for_each_entry_safe(work, next, &works, list) {
                list_del_init(&work->list);
@@ -989,7 +1032,6 @@ int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr,
                           u32 *sum, int len)
 {
        struct btrfs_ordered_sum *ordered_sum;
-       struct btrfs_sector_sum *sector_sums;
        struct btrfs_ordered_extent *ordered;
        struct btrfs_ordered_inode_tree *tree = &BTRFS_I(inode)->ordered_tree;
        unsigned long num_sectors;
@@ -1007,18 +1049,16 @@ int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr,
                    disk_bytenr < ordered_sum->bytenr + ordered_sum->len) {
                        i = (disk_bytenr - ordered_sum->bytenr) >>
                            inode->i_sb->s_blocksize_bits;
-                       sector_sums = ordered_sum->sums + i;
                        num_sectors = ordered_sum->len >>
                                      inode->i_sb->s_blocksize_bits;
-                       for (; i < num_sectors; i++) {
-                               if (sector_sums[i].bytenr == disk_bytenr) {
-                                       sum[index] = sector_sums[i].sum;
-                                       index++;
-                                       if (index == len)
-                                               goto out;
-                                       disk_bytenr += sectorsize;
-                               }
-                       }
+                       num_sectors = min_t(int, len - index, num_sectors - i);
+                       memcpy(sum + index, ordered_sum->sums + i,
+                              num_sectors);
+
+                       index += (int)num_sectors;
+                       if (index == len)
+                               goto out;
+                       disk_bytenr += num_sectors * sectorsize;
                }
        }
 out:
@@ -1055,12 +1095,12 @@ void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
        if (last_mod < root->fs_info->last_trans_committed)
                return;
 
-       spin_lock(&root->fs_info->ordered_extent_lock);
+       spin_lock(&root->fs_info->ordered_root_lock);
        if (list_empty(&BTRFS_I(inode)->ordered_operations)) {
                list_add_tail(&BTRFS_I(inode)->ordered_operations,
                              &cur_trans->ordered_operations);
        }
-       spin_unlock(&root->fs_info->ordered_extent_lock);
+       spin_unlock(&root->fs_info->ordered_root_lock);
 }
 
 int __init ordered_data_init(void)
index 58b0e3b..68844d5 100644 (file)
@@ -26,18 +26,6 @@ struct btrfs_ordered_inode_tree {
        struct rb_node *last;
 };
 
-/*
- * these are used to collect checksums done just before bios submission.
- * They are attached via a list into the ordered extent, and
- * checksum items are inserted into the tree after all the blocks in
- * the ordered extent are on disk
- */
-struct btrfs_sector_sum {
-       /* bytenr on disk */
-       u64 bytenr;
-       u32 sum;
-};
-
 struct btrfs_ordered_sum {
        /* bytenr is the start of this extent on disk */
        u64 bytenr;
@@ -45,10 +33,10 @@ struct btrfs_ordered_sum {
        /*
         * this is the length in bytes covered by the sums array below.
         */
-       unsigned long len;
+       int len;
        struct list_head list;
-       /* last field is a variable length array of btrfs_sector_sums */
-       struct btrfs_sector_sum sums[];
+       /* last field is a variable length array of csums */
+       u32 sums[];
 };
 
 /*
@@ -149,11 +137,8 @@ struct btrfs_ordered_extent {
 static inline int btrfs_ordered_sum_size(struct btrfs_root *root,
                                         unsigned long bytes)
 {
-       unsigned long num_sectors = (bytes + root->sectorsize - 1) /
-               root->sectorsize;
-       num_sectors++;
-       return sizeof(struct btrfs_ordered_sum) +
-               num_sectors * sizeof(struct btrfs_sector_sum);
+       int num_sectors = (int)DIV_ROUND_UP(bytes, root->sectorsize);
+       return sizeof(struct btrfs_ordered_sum) + num_sectors * sizeof(u32);
 }
 
 static inline void
@@ -204,6 +189,8 @@ 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, int delay_iput);
+void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info,
+                                   int delay_iput);
 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 9d49c58..1280eff 100644 (file)
@@ -98,13 +98,10 @@ struct btrfs_qgroup_list {
        struct btrfs_qgroup *member;
 };
 
-struct qgroup_rescan {
-       struct btrfs_work       work;
-       struct btrfs_fs_info    *fs_info;
-};
-
-static void qgroup_rescan_start(struct btrfs_fs_info *fs_info,
-                               struct qgroup_rescan *qscan);
+static int
+qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
+                  int init_flags);
+static void qgroup_rescan_zero_tracking(struct btrfs_fs_info *fs_info);
 
 /* must be called with qgroup_ioctl_lock held */
 static struct btrfs_qgroup *find_qgroup_rb(struct btrfs_fs_info *fs_info,
@@ -255,10 +252,17 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
        int slot;
        int ret = 0;
        u64 flags = 0;
+       u64 rescan_progress = 0;
 
        if (!fs_info->quota_enabled)
                return 0;
 
+       fs_info->qgroup_ulist = ulist_alloc(GFP_NOFS);
+       if (!fs_info->qgroup_ulist) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
        path = btrfs_alloc_path();
        if (!path) {
                ret = -ENOMEM;
@@ -306,20 +310,7 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
                        }
                        fs_info->qgroup_flags = btrfs_qgroup_status_flags(l,
                                                                          ptr);
-                       fs_info->qgroup_rescan_progress.objectid =
-                                       btrfs_qgroup_status_rescan(l, ptr);
-                       if (fs_info->qgroup_flags &
-                           BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
-                               struct qgroup_rescan *qscan =
-                                       kmalloc(sizeof(*qscan), GFP_NOFS);
-                               if (!qscan) {
-                                       ret = -ENOMEM;
-                                       goto out;
-                               }
-                               fs_info->qgroup_rescan_progress.type = 0;
-                               fs_info->qgroup_rescan_progress.offset = 0;
-                               qgroup_rescan_start(fs_info, qscan);
-                       }
+                       rescan_progress = btrfs_qgroup_status_rescan(l, ptr);
                        goto next1;
                }
 
@@ -421,9 +412,18 @@ out:
        if (!(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_ON)) {
                fs_info->quota_enabled = 0;
                fs_info->pending_quota_state = 0;
+       } else if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN &&
+                  ret >= 0) {
+               ret = qgroup_rescan_init(fs_info, rescan_progress, 0);
        }
        btrfs_free_path(path);
 
+       if (ret < 0) {
+               ulist_free(fs_info->qgroup_ulist);
+               fs_info->qgroup_ulist = NULL;
+               fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+       }
+
        return ret < 0 ? ret : 0;
 }
 
@@ -460,6 +460,7 @@ void btrfs_free_qgroup_config(struct btrfs_fs_info *fs_info)
                }
                kfree(qgroup);
        }
+       ulist_free(fs_info->qgroup_ulist);
 }
 
 static int add_qgroup_relation_item(struct btrfs_trans_handle *trans,
@@ -819,6 +820,12 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
                goto out;
        }
 
+       fs_info->qgroup_ulist = ulist_alloc(GFP_NOFS);
+       if (!fs_info->qgroup_ulist) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
        /*
         * initially create the quota tree
         */
@@ -916,6 +923,10 @@ out_free_root:
                kfree(quota_root);
        }
 out:
+       if (ret) {
+               ulist_free(fs_info->qgroup_ulist);
+               fs_info->qgroup_ulist = NULL;
+       }
        mutex_unlock(&fs_info->qgroup_ioctl_lock);
        return ret;
 }
@@ -1355,7 +1366,6 @@ int btrfs_qgroup_account_ref(struct btrfs_trans_handle *trans,
        u64 ref_root;
        struct btrfs_qgroup *qgroup;
        struct ulist *roots = NULL;
-       struct ulist *tmp = NULL;
        u64 seq;
        int ret = 0;
        int sgn;
@@ -1428,14 +1438,7 @@ int btrfs_qgroup_account_ref(struct btrfs_trans_handle *trans,
        if (ret < 0)
                return ret;
 
-       mutex_lock(&fs_info->qgroup_rescan_lock);
        spin_lock(&fs_info->qgroup_lock);
-       if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
-               if (fs_info->qgroup_rescan_progress.objectid <= node->bytenr) {
-                       ret = 0;
-                       goto unlock;
-               }
-       }
 
        quota_root = fs_info->quota_root;
        if (!quota_root)
@@ -1448,39 +1451,34 @@ int btrfs_qgroup_account_ref(struct btrfs_trans_handle *trans,
        /*
         * step 1: for each old ref, visit all nodes once and inc refcnt
         */
-       tmp = ulist_alloc(GFP_ATOMIC);
-       if (!tmp) {
-               ret = -ENOMEM;
-               goto unlock;
-       }
+       ulist_reinit(fs_info->qgroup_ulist);
        seq = fs_info->qgroup_seq;
        fs_info->qgroup_seq += roots->nnodes + 1; /* max refcnt */
 
-       ret = qgroup_account_ref_step1(fs_info, roots, tmp, seq);
+       ret = qgroup_account_ref_step1(fs_info, roots, fs_info->qgroup_ulist,
+                                      seq);
        if (ret)
                goto unlock;
 
        /*
         * step 2: walk from the new root
         */
-       ret = qgroup_account_ref_step2(fs_info, roots, tmp, seq, sgn,
-                                      node->num_bytes, qgroup);
+       ret = qgroup_account_ref_step2(fs_info, roots, fs_info->qgroup_ulist,
+                                      seq, sgn, node->num_bytes, qgroup);
        if (ret)
                goto unlock;
 
        /*
         * step 3: walk again from old refs
         */
-       ret = qgroup_account_ref_step3(fs_info, roots, tmp, seq, sgn,
-                                      node->num_bytes);
+       ret = qgroup_account_ref_step3(fs_info, roots, fs_info->qgroup_ulist,
+                                      seq, sgn, node->num_bytes);
        if (ret)
                goto unlock;
 
 unlock:
        spin_unlock(&fs_info->qgroup_lock);
-       mutex_unlock(&fs_info->qgroup_rescan_lock);
        ulist_free(roots);
-       ulist_free(tmp);
 
        return ret;
 }
@@ -1527,9 +1525,12 @@ int btrfs_run_qgroups(struct btrfs_trans_handle *trans,
                fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
 
        if (!ret && start_rescan_worker) {
-               ret = btrfs_qgroup_rescan(fs_info);
-               if (ret)
-                       pr_err("btrfs: start rescan quota failed: %d\n", ret);
+               ret = qgroup_rescan_init(fs_info, 0, 1);
+               if (!ret) {
+                       qgroup_rescan_zero_tracking(fs_info);
+                       btrfs_queue_worker(&fs_info->qgroup_rescan_workers,
+                                          &fs_info->qgroup_rescan_work);
+               }
                ret = 0;
        }
 
@@ -1720,7 +1721,6 @@ int btrfs_qgroup_reserve(struct btrfs_root *root, u64 num_bytes)
        struct btrfs_fs_info *fs_info = root->fs_info;
        u64 ref_root = root->root_key.objectid;
        int ret = 0;
-       struct ulist *ulist = NULL;
        struct ulist_node *unode;
        struct ulist_iterator uiter;
 
@@ -1743,17 +1743,13 @@ int btrfs_qgroup_reserve(struct btrfs_root *root, u64 num_bytes)
         * in a first step, we check all affected qgroups if any limits would
         * be exceeded
         */
-       ulist = ulist_alloc(GFP_ATOMIC);
-       if (!ulist) {
-               ret = -ENOMEM;
-               goto out;
-       }
-       ret = ulist_add(ulist, qgroup->qgroupid,
+       ulist_reinit(fs_info->qgroup_ulist);
+       ret = ulist_add(fs_info->qgroup_ulist, qgroup->qgroupid,
                        (uintptr_t)qgroup, GFP_ATOMIC);
        if (ret < 0)
                goto out;
        ULIST_ITER_INIT(&uiter);
-       while ((unode = ulist_next(ulist, &uiter))) {
+       while ((unode = ulist_next(fs_info->qgroup_ulist, &uiter))) {
                struct btrfs_qgroup *qg;
                struct btrfs_qgroup_list *glist;
 
@@ -1774,7 +1770,8 @@ int btrfs_qgroup_reserve(struct btrfs_root *root, u64 num_bytes)
                }
 
                list_for_each_entry(glist, &qg->groups, next_group) {
-                       ret = ulist_add(ulist, glist->group->qgroupid,
+                       ret = ulist_add(fs_info->qgroup_ulist,
+                                       glist->group->qgroupid,
                                        (uintptr_t)glist->group, GFP_ATOMIC);
                        if (ret < 0)
                                goto out;
@@ -1785,7 +1782,7 @@ int btrfs_qgroup_reserve(struct btrfs_root *root, u64 num_bytes)
         * no limits exceeded, now record the reservation into all qgroups
         */
        ULIST_ITER_INIT(&uiter);
-       while ((unode = ulist_next(ulist, &uiter))) {
+       while ((unode = ulist_next(fs_info->qgroup_ulist, &uiter))) {
                struct btrfs_qgroup *qg;
 
                qg = (struct btrfs_qgroup *)(uintptr_t)unode->aux;
@@ -1795,8 +1792,6 @@ int btrfs_qgroup_reserve(struct btrfs_root *root, u64 num_bytes)
 
 out:
        spin_unlock(&fs_info->qgroup_lock);
-       ulist_free(ulist);
-
        return ret;
 }
 
@@ -1805,7 +1800,6 @@ void btrfs_qgroup_free(struct btrfs_root *root, u64 num_bytes)
        struct btrfs_root *quota_root;
        struct btrfs_qgroup *qgroup;
        struct btrfs_fs_info *fs_info = root->fs_info;
-       struct ulist *ulist = NULL;
        struct ulist_node *unode;
        struct ulist_iterator uiter;
        u64 ref_root = root->root_key.objectid;
@@ -1827,17 +1821,13 @@ void btrfs_qgroup_free(struct btrfs_root *root, u64 num_bytes)
        if (!qgroup)
                goto out;
 
-       ulist = ulist_alloc(GFP_ATOMIC);
-       if (!ulist) {
-               btrfs_std_error(fs_info, -ENOMEM);
-               goto out;
-       }
-       ret = ulist_add(ulist, qgroup->qgroupid,
+       ulist_reinit(fs_info->qgroup_ulist);
+       ret = ulist_add(fs_info->qgroup_ulist, qgroup->qgroupid,
                        (uintptr_t)qgroup, GFP_ATOMIC);
        if (ret < 0)
                goto out;
        ULIST_ITER_INIT(&uiter);
-       while ((unode = ulist_next(ulist, &uiter))) {
+       while ((unode = ulist_next(fs_info->qgroup_ulist, &uiter))) {
                struct btrfs_qgroup *qg;
                struct btrfs_qgroup_list *glist;
 
@@ -1846,7 +1836,8 @@ void btrfs_qgroup_free(struct btrfs_root *root, u64 num_bytes)
                qg->reserved -= num_bytes;
 
                list_for_each_entry(glist, &qg->groups, next_group) {
-                       ret = ulist_add(ulist, glist->group->qgroupid,
+                       ret = ulist_add(fs_info->qgroup_ulist,
+                                       glist->group->qgroupid,
                                        (uintptr_t)glist->group, GFP_ATOMIC);
                        if (ret < 0)
                                goto out;
@@ -1855,7 +1846,6 @@ void btrfs_qgroup_free(struct btrfs_root *root, u64 num_bytes)
 
 out:
        spin_unlock(&fs_info->qgroup_lock);
-       ulist_free(ulist);
 }
 
 void assert_qgroups_uptodate(struct btrfs_trans_handle *trans)
@@ -1874,12 +1864,11 @@ void assert_qgroups_uptodate(struct btrfs_trans_handle *trans)
  * returns 1 when done, 2 when done and FLAG_INCONSISTENT was cleared.
  */
 static int
-qgroup_rescan_leaf(struct qgroup_rescan *qscan, struct btrfs_path *path,
+qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
                   struct btrfs_trans_handle *trans, struct ulist *tmp,
                   struct extent_buffer *scratch_leaf)
 {
        struct btrfs_key found;
-       struct btrfs_fs_info *fs_info = qscan->fs_info;
        struct ulist *roots = NULL;
        struct ulist_node *unode;
        struct ulist_iterator uiter;
@@ -2007,11 +1996,10 @@ out:
 
 static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
 {
-       struct qgroup_rescan *qscan = container_of(work, struct qgroup_rescan,
-                                                  work);
+       struct btrfs_fs_info *fs_info = container_of(work, struct btrfs_fs_info,
+                                                    qgroup_rescan_work);
        struct btrfs_path *path;
        struct btrfs_trans_handle *trans = NULL;
-       struct btrfs_fs_info *fs_info = qscan->fs_info;
        struct ulist *tmp = NULL;
        struct extent_buffer *scratch_leaf = NULL;
        int err = -ENOMEM;
@@ -2036,7 +2024,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
                if (!fs_info->quota_enabled) {
                        err = -EINTR;
                } else {
-                       err = qgroup_rescan_leaf(qscan, path, trans,
+                       err = qgroup_rescan_leaf(fs_info, path, trans,
                                                 tmp, scratch_leaf);
                }
                if (err > 0)
@@ -2049,7 +2037,6 @@ out:
        kfree(scratch_leaf);
        ulist_free(tmp);
        btrfs_free_path(path);
-       kfree(qscan);
 
        mutex_lock(&fs_info->qgroup_rescan_lock);
        fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
@@ -2068,47 +2055,74 @@ out:
        } else {
                pr_err("btrfs: qgroup scan failed with %d\n", err);
        }
-}
 
-static void
-qgroup_rescan_start(struct btrfs_fs_info *fs_info, struct qgroup_rescan *qscan)
-{
-       memset(&qscan->work, 0, sizeof(qscan->work));
-       qscan->work.func = btrfs_qgroup_rescan_worker;
-       qscan->fs_info = fs_info;
-
-       pr_info("btrfs: qgroup scan started\n");
-       btrfs_queue_worker(&fs_info->qgroup_rescan_workers, &qscan->work);
+       complete_all(&fs_info->qgroup_rescan_completion);
 }
 
-int
-btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info)
+/*
+ * Checks that (a) no rescan is running and (b) quota is enabled. Allocates all
+ * memory required for the rescan context.
+ */
+static int
+qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
+                  int init_flags)
 {
        int ret = 0;
-       struct rb_node *n;
-       struct btrfs_qgroup *qgroup;
-       struct qgroup_rescan *qscan = kmalloc(sizeof(*qscan), GFP_NOFS);
 
-       if (!qscan)
-               return -ENOMEM;
+       if (!init_flags &&
+           (!(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) ||
+            !(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_ON))) {
+               ret = -EINVAL;
+               goto err;
+       }
 
        mutex_lock(&fs_info->qgroup_rescan_lock);
        spin_lock(&fs_info->qgroup_lock);
-       if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN)
-               ret = -EINPROGRESS;
-       else if (!(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_ON))
-               ret = -EINVAL;
-       if (ret) {
-               spin_unlock(&fs_info->qgroup_lock);
-               mutex_unlock(&fs_info->qgroup_rescan_lock);
-               kfree(qscan);
-               return ret;
+
+       if (init_flags) {
+               if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN)
+                       ret = -EINPROGRESS;
+               else if (!(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_ON))
+                       ret = -EINVAL;
+
+               if (ret) {
+                       spin_unlock(&fs_info->qgroup_lock);
+                       mutex_unlock(&fs_info->qgroup_rescan_lock);
+                       goto err;
+               }
+
+               fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_RESCAN;
        }
 
-       fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_RESCAN;
        memset(&fs_info->qgroup_rescan_progress, 0,
                sizeof(fs_info->qgroup_rescan_progress));
+       fs_info->qgroup_rescan_progress.objectid = progress_objectid;
+
+       spin_unlock(&fs_info->qgroup_lock);
+       mutex_unlock(&fs_info->qgroup_rescan_lock);
+
+       init_completion(&fs_info->qgroup_rescan_completion);
+
+       memset(&fs_info->qgroup_rescan_work, 0,
+              sizeof(fs_info->qgroup_rescan_work));
+       fs_info->qgroup_rescan_work.func = btrfs_qgroup_rescan_worker;
+
+       if (ret) {
+err:
+               pr_info("btrfs: qgroup_rescan_init failed with %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void
+qgroup_rescan_zero_tracking(struct btrfs_fs_info *fs_info)
+{
+       struct rb_node *n;
+       struct btrfs_qgroup *qgroup;
 
+       spin_lock(&fs_info->qgroup_lock);
        /* clear all current qgroup tracking information */
        for (n = rb_first(&fs_info->qgroup_tree); n; n = rb_next(n)) {
                qgroup = rb_entry(n, struct btrfs_qgroup, node);
@@ -2118,9 +2132,74 @@ btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info)
                qgroup->excl_cmpr = 0;
        }
        spin_unlock(&fs_info->qgroup_lock);
-       mutex_unlock(&fs_info->qgroup_rescan_lock);
+}
+
+int
+btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info)
+{
+       int ret = 0;
+       struct btrfs_trans_handle *trans;
 
-       qgroup_rescan_start(fs_info, qscan);
+       ret = qgroup_rescan_init(fs_info, 0, 1);
+       if (ret)
+               return ret;
+
+       /*
+        * We have set the rescan_progress to 0, which means no more
+        * delayed refs will be accounted by btrfs_qgroup_account_ref.
+        * However, btrfs_qgroup_account_ref may be right after its call
+        * to btrfs_find_all_roots, in which case it would still do the
+        * accounting.
+        * To solve this, we're committing the transaction, which will
+        * ensure we run all delayed refs and only after that, we are
+        * going to clear all tracking information for a clean start.
+        */
+
+       trans = btrfs_join_transaction(fs_info->fs_root);
+       if (IS_ERR(trans)) {
+               fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+               return PTR_ERR(trans);
+       }
+       ret = btrfs_commit_transaction(trans, fs_info->fs_root);
+       if (ret) {
+               fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+               return ret;
+       }
+
+       qgroup_rescan_zero_tracking(fs_info);
+
+       btrfs_queue_worker(&fs_info->qgroup_rescan_workers,
+                          &fs_info->qgroup_rescan_work);
 
        return 0;
 }
+
+int btrfs_qgroup_wait_for_completion(struct btrfs_fs_info *fs_info)
+{
+       int running;
+       int ret = 0;
+
+       mutex_lock(&fs_info->qgroup_rescan_lock);
+       spin_lock(&fs_info->qgroup_lock);
+       running = fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN;
+       spin_unlock(&fs_info->qgroup_lock);
+       mutex_unlock(&fs_info->qgroup_rescan_lock);
+
+       if (running)
+               ret = wait_for_completion_interruptible(
+                                       &fs_info->qgroup_rescan_completion);
+
+       return ret;
+}
+
+/*
+ * this is only called from open_ctree where we're still single threaded, thus
+ * locking is omitted here.
+ */
+void
+btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info)
+{
+       if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN)
+               btrfs_queue_worker(&fs_info->qgroup_rescan_workers,
+                                  &fs_info->qgroup_rescan_work);
+}
index 4febca4..1209649 100644 (file)
@@ -1305,6 +1305,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
        struct extent_buffer *eb;
        struct btrfs_root_item *root_item;
        struct btrfs_key root_key;
+       u64 last_snap = 0;
        int ret;
 
        root_item = kmalloc(sizeof(*root_item), GFP_NOFS);
@@ -1320,6 +1321,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
                                      BTRFS_TREE_RELOC_OBJECTID);
                BUG_ON(ret);
 
+               last_snap = btrfs_root_last_snapshot(&root->root_item);
                btrfs_set_root_last_snapshot(&root->root_item,
                                             trans->transid - 1);
        } else {
@@ -1345,6 +1347,12 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
                memset(&root_item->drop_progress, 0,
                       sizeof(struct btrfs_disk_key));
                root_item->drop_level = 0;
+               /*
+                * abuse rtransid, it is safe because it is impossible to
+                * receive data into a relocation tree.
+                */
+               btrfs_set_root_rtransid(root_item, last_snap);
+               btrfs_set_root_otransid(root_item, trans->transid);
        }
 
        btrfs_tree_unlock(eb);
@@ -1355,8 +1363,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
        BUG_ON(ret);
        kfree(root_item);
 
-       reloc_root = btrfs_read_fs_root_no_radix(root->fs_info->tree_root,
-                                                &root_key);
+       reloc_root = btrfs_read_fs_root(root->fs_info->tree_root, &root_key);
        BUG_ON(IS_ERR(reloc_root));
        reloc_root->last_trans = trans->transid;
        return reloc_root;
@@ -2273,8 +2280,12 @@ void free_reloc_roots(struct list_head *list)
 static noinline_for_stack
 int merge_reloc_roots(struct reloc_control *rc)
 {
+       struct btrfs_trans_handle *trans;
        struct btrfs_root *root;
        struct btrfs_root *reloc_root;
+       u64 last_snap;
+       u64 otransid;
+       u64 objectid;
        LIST_HEAD(reloc_roots);
        int found = 0;
        int ret = 0;
@@ -2308,12 +2319,44 @@ again:
                } else {
                        list_del_init(&reloc_root->root_list);
                }
+
+               /*
+                * we keep the old last snapshod transid in rtranid when we
+                * created the relocation tree.
+                */
+               last_snap = btrfs_root_rtransid(&reloc_root->root_item);
+               otransid = btrfs_root_otransid(&reloc_root->root_item);
+               objectid = reloc_root->root_key.offset;
+
                ret = btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1);
                if (ret < 0) {
                        if (list_empty(&reloc_root->root_list))
                                list_add_tail(&reloc_root->root_list,
                                              &reloc_roots);
                        goto out;
+               } else if (!ret) {
+                       /*
+                        * recover the last snapshot tranid to avoid
+                        * the space balance break NOCOW.
+                        */
+                       root = read_fs_root(rc->extent_root->fs_info,
+                                           objectid);
+                       if (IS_ERR(root))
+                               continue;
+
+                       if (btrfs_root_refs(&root->root_item) == 0)
+                               continue;
+
+                       trans = btrfs_join_transaction(root);
+                       BUG_ON(IS_ERR(trans));
+
+                       /* Check if the fs/file tree was snapshoted or not. */
+                       if (btrfs_root_last_snapshot(&root->root_item) ==
+                           otransid - 1)
+                               btrfs_set_root_last_snapshot(&root->root_item,
+                                                            last_snap);
+                               
+                       btrfs_end_transaction(trans, root);
                }
        }
 
@@ -3266,6 +3309,8 @@ static int __add_tree_block(struct reloc_control *rc,
        struct btrfs_path *path;
        struct btrfs_key key;
        int ret;
+       bool skinny = btrfs_fs_incompat(rc->extent_root->fs_info,
+                                       SKINNY_METADATA);
 
        if (tree_block_processed(bytenr, blocksize, rc))
                return 0;
@@ -3276,10 +3321,15 @@ static int __add_tree_block(struct reloc_control *rc,
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
-
+again:
        key.objectid = bytenr;
-       key.type = BTRFS_EXTENT_ITEM_KEY;
-       key.offset = blocksize;
+       if (skinny) {
+               key.type = BTRFS_METADATA_ITEM_KEY;
+               key.offset = (u64)-1;
+       } else {
+               key.type = BTRFS_EXTENT_ITEM_KEY;
+               key.offset = blocksize;
+       }
 
        path->search_commit_root = 1;
        path->skip_locking = 1;
@@ -3287,11 +3337,23 @@ static int __add_tree_block(struct reloc_control *rc,
        if (ret < 0)
                goto out;
 
-       btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
-       if (ret > 0) {
-               if (key.objectid == bytenr &&
-                   key.type == BTRFS_METADATA_ITEM_KEY)
-                       ret = 0;
+       if (ret > 0 && skinny) {
+               if (path->slots[0]) {
+                       path->slots[0]--;
+                       btrfs_item_key_to_cpu(path->nodes[0], &key,
+                                             path->slots[0]);
+                       if (key.objectid == bytenr &&
+                           (key.type == BTRFS_METADATA_ITEM_KEY ||
+                            (key.type == BTRFS_EXTENT_ITEM_KEY &&
+                             key.offset == blocksize)))
+                               ret = 0;
+               }
+
+               if (ret) {
+                       skinny = false;
+                       btrfs_release_path(path);
+                       goto again;
+               }
        }
        BUG_ON(ret);
 
@@ -4160,12 +4222,12 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
               (unsigned long long)rc->block_group->key.objectid,
               (unsigned long long)rc->block_group->flags);
 
-       ret = btrfs_start_delalloc_inodes(fs_info->tree_root, 0);
+       ret = btrfs_start_all_delalloc_inodes(fs_info, 0);
        if (ret < 0) {
                err = ret;
                goto out;
        }
-       btrfs_wait_ordered_extents(fs_info->tree_root, 0);
+       btrfs_wait_all_ordered_extents(fs_info, 0);
 
        while (1) {
                mutex_lock(&fs_info->cleaner_mutex);
@@ -4277,7 +4339,7 @@ int btrfs_recover_relocation(struct btrfs_root *root)
                    key.type != BTRFS_ROOT_ITEM_KEY)
                        break;
 
-               reloc_root = btrfs_read_fs_root_no_radix(root, &key);
+               reloc_root = btrfs_read_fs_root(root, &key);
                if (IS_ERR(reloc_root)) {
                        err = PTR_ERR(reloc_root);
                        goto out;
@@ -4396,10 +4458,8 @@ out:
 int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
 {
        struct btrfs_ordered_sum *sums;
-       struct btrfs_sector_sum *sector_sum;
        struct btrfs_ordered_extent *ordered;
        struct btrfs_root *root = BTRFS_I(inode)->root;
-       size_t offset;
        int ret;
        u64 disk_bytenr;
        LIST_HEAD(list);
@@ -4413,19 +4473,13 @@ 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);
 
-               sector_sum = sums->sums;
-               sums->bytenr = ordered->start;
-
-               offset = 0;
-               while (offset < sums->len) {
-                       sector_sum->bytenr += ordered->start - disk_bytenr;
-                       sector_sum++;
-                       offset += root->sectorsize;
-               }
+               sums->bytenr = disk_bytenr;
+               disk_bytenr += sums->len;
 
                btrfs_add_ordered_sum(inode, ordered, sums);
        }
index 5bf1ed5..ffb1036 100644 (file)
@@ -64,52 +64,59 @@ void btrfs_read_root_item(struct extent_buffer *eb, int slot,
 }
 
 /*
- * lookup the root with the highest offset for a given objectid.  The key we do
- * find is copied into 'key'.  If we find something return 0, otherwise 1, < 0
- * on error.
+ * btrfs_find_root - lookup the root by the key.
+ * root: the root of the root tree
+ * search_key: the key to search
+ * path: the path we search
+ * root_item: the root item of the tree we look for
+ * root_key: the reak key of the tree we look for
+ *
+ * If ->offset of 'seach_key' is -1ULL, it means we are not sure the offset
+ * of the search key, just lookup the root with the highest offset for a
+ * given objectid.
+ *
+ * If we find something return 0, otherwise > 0, < 0 on error.
  */
-int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
-                       struct btrfs_root_item *item, struct btrfs_key *key)
+int btrfs_find_root(struct btrfs_root *root, struct btrfs_key *search_key,
+                   struct btrfs_path *path, struct btrfs_root_item *root_item,
+                   struct btrfs_key *root_key)
 {
-       struct btrfs_path *path;
-       struct btrfs_key search_key;
        struct btrfs_key found_key;
        struct extent_buffer *l;
        int ret;
        int slot;
 
-       search_key.objectid = objectid;
-       search_key.type = BTRFS_ROOT_ITEM_KEY;
-       search_key.offset = (u64)-1;
-
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
-       ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
+       ret = btrfs_search_slot(NULL, root, search_key, path, 0, 0);
        if (ret < 0)
-               goto out;
+               return ret;
 
-       BUG_ON(ret == 0);
-       if (path->slots[0] == 0) {
-               ret = 1;
-               goto out;
+       if (search_key->offset != -1ULL) {      /* the search key is exact */
+               if (ret > 0)
+                       goto out;
+       } else {
+               BUG_ON(ret == 0);               /* Logical error */
+               if (path->slots[0] == 0)
+                       goto out;
+               path->slots[0]--;
+               ret = 0;
        }
+
        l = path->nodes[0];
-       slot = path->slots[0] - 1;
+       slot = path->slots[0];
+
        btrfs_item_key_to_cpu(l, &found_key, slot);
-       if (found_key.objectid != objectid ||
+       if (found_key.objectid != search_key->objectid ||
            found_key.type != BTRFS_ROOT_ITEM_KEY) {
                ret = 1;
                goto out;
        }
-       if (item)
-               btrfs_read_root_item(l, slot, item);
-       if (key)
-               memcpy(key, &found_key, sizeof(found_key));
 
-       ret = 0;
+       if (root_item)
+               btrfs_read_root_item(l, slot, root_item);
+       if (root_key)
+               memcpy(root_key, &found_key, sizeof(found_key));
 out:
-       btrfs_free_path(path);
+       btrfs_release_path(path);
        return ret;
 }
 
@@ -212,86 +219,6 @@ int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
        return btrfs_insert_item(trans, root, key, item, sizeof(*item));
 }
 
-/*
- * at mount time we want to find all the old transaction snapshots that were in
- * the process of being deleted if we crashed.  This is any root item with an
- * offset lower than the latest root.  They need to be queued for deletion to
- * finish what was happening when we crashed.
- */
-int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid)
-{
-       struct btrfs_root *dead_root;
-       struct btrfs_root_item *ri;
-       struct btrfs_key key;
-       struct btrfs_key found_key;
-       struct btrfs_path *path;
-       int ret;
-       u32 nritems;
-       struct extent_buffer *leaf;
-       int slot;
-
-       key.objectid = objectid;
-       btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
-       key.offset = 0;
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
-
-again:
-       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
-       if (ret < 0)
-               goto err;
-       while (1) {
-               leaf = path->nodes[0];
-               nritems = btrfs_header_nritems(leaf);
-               slot = path->slots[0];
-               if (slot >= nritems) {
-                       ret = btrfs_next_leaf(root, path);
-                       if (ret)
-                               break;
-                       leaf = path->nodes[0];
-                       nritems = btrfs_header_nritems(leaf);
-                       slot = path->slots[0];
-               }
-               btrfs_item_key_to_cpu(leaf, &key, slot);
-               if (btrfs_key_type(&key) != BTRFS_ROOT_ITEM_KEY)
-                       goto next;
-
-               if (key.objectid < objectid)
-                       goto next;
-
-               if (key.objectid > objectid)
-                       break;
-
-               ri = btrfs_item_ptr(leaf, slot, struct btrfs_root_item);
-               if (btrfs_disk_root_refs(leaf, ri) != 0)
-                       goto next;
-
-               memcpy(&found_key, &key, sizeof(key));
-               key.offset++;
-               btrfs_release_path(path);
-               dead_root =
-                       btrfs_read_fs_root_no_radix(root->fs_info->tree_root,
-                                                   &found_key);
-               if (IS_ERR(dead_root)) {
-                       ret = PTR_ERR(dead_root);
-                       goto err;
-               }
-
-               ret = btrfs_add_dead_root(dead_root);
-               if (ret)
-                       goto err;
-               goto again;
-next:
-               slot++;
-               path->slots[0]++;
-       }
-       ret = 0;
-err:
-       btrfs_free_path(path);
-       return ret;
-}
-
 int btrfs_find_orphan_roots(struct btrfs_root *tree_root)
 {
        struct extent_buffer *leaf;
@@ -301,6 +228,10 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root)
        struct btrfs_root *root;
        int err = 0;
        int ret;
+       bool can_recover = true;
+
+       if (tree_root->fs_info->sb->s_flags & MS_RDONLY)
+               can_recover = false;
 
        path = btrfs_alloc_path();
        if (!path)
@@ -340,20 +271,52 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root)
                root_key.objectid = key.offset;
                key.offset++;
 
-               root = btrfs_read_fs_root_no_name(tree_root->fs_info,
-                                                 &root_key);
-               if (!IS_ERR(root))
+               root = btrfs_read_fs_root(tree_root, &root_key);
+               err = PTR_RET(root);
+               if (err && err != -ENOENT) {
+                       break;
+               } else if (err == -ENOENT) {
+                       struct btrfs_trans_handle *trans;
+
+                       btrfs_release_path(path);
+
+                       trans = btrfs_join_transaction(tree_root);
+                       if (IS_ERR(trans)) {
+                               err = PTR_ERR(trans);
+                               btrfs_error(tree_root->fs_info, err,
+                                           "Failed to start trans to delete "
+                                           "orphan item");
+                               break;
+                       }
+                       err = btrfs_del_orphan_item(trans, tree_root,
+                                                   root_key.objectid);
+                       btrfs_end_transaction(trans, tree_root);
+                       if (err) {
+                               btrfs_error(tree_root->fs_info, err,
+                                           "Failed to delete root orphan "
+                                           "item");
+                               break;
+                       }
                        continue;
+               }
 
-               ret = PTR_ERR(root);
-               if (ret != -ENOENT) {
-                       err = ret;
+               if (btrfs_root_refs(&root->root_item) == 0) {
+                       btrfs_add_dead_root(root);
+                       continue;
+               }
+
+               err = btrfs_init_fs_root(root);
+               if (err) {
+                       btrfs_free_fs_root(root);
                        break;
                }
 
-               ret = btrfs_find_dead_roots(tree_root, root_key.objectid);
-               if (ret) {
-                       err = ret;
+               root->orphan_item_inserted = 1;
+
+               err = btrfs_insert_fs_root(root->fs_info, root);
+               if (err) {
+                       BUG_ON(err == -EEXIST);
+                       btrfs_free_fs_root(root);
                        break;
                }
        }
@@ -368,8 +331,6 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
 {
        struct btrfs_path *path;
        int ret;
-       struct btrfs_root_item *ri;
-       struct extent_buffer *leaf;
 
        path = btrfs_alloc_path();
        if (!path)
@@ -379,8 +340,6 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                goto out;
 
        BUG_ON(ret != 0);
-       leaf = path->nodes[0];
-       ri = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_item);
 
        ret = btrfs_del_item(trans, root, path);
 out:
index 79bd479..4ba2a69 100644 (file)
@@ -2126,8 +2126,7 @@ static int scrub_find_csum(struct scrub_ctx *sctx, u64 logical, u64 len,
                           u8 *csum)
 {
        struct btrfs_ordered_sum *sum = NULL;
-       int ret = 0;
-       unsigned long i;
+       unsigned long index;
        unsigned long num_sectors;
 
        while (!list_empty(&sctx->csum_list)) {
@@ -2146,19 +2145,14 @@ static int scrub_find_csum(struct scrub_ctx *sctx, u64 logical, u64 len,
        if (!sum)
                return 0;
 
+       index = ((u32)(logical - sum->bytenr)) / sctx->sectorsize;
        num_sectors = sum->len / sctx->sectorsize;
-       for (i = 0; i < num_sectors; ++i) {
-               if (sum->sums[i].bytenr == logical) {
-                       memcpy(csum, &sum->sums[i].sum, sctx->csum_size);
-                       ret = 1;
-                       break;
-               }
-       }
-       if (ret && i == num_sectors - 1) {
+       memcpy(csum, sum->sums + index, sctx->csum_size);
+       if (index == num_sectors - 1) {
                list_del(&sum->list);
                kfree(sum);
        }
-       return ret;
+       return 1;
 }
 
 /* scrub extent tries to collect up to 64 kB for each bio */
@@ -2505,6 +2499,7 @@ again:
                        if (ret)
                                goto out;
 
+                       scrub_free_csums(sctx);
                        if (extent_logical + extent_len <
                            key.objectid + bytes) {
                                logical += increment;
@@ -3204,16 +3199,18 @@ out:
 
 static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)
 {
-       unsigned long index;
        struct scrub_copy_nocow_ctx *nocow_ctx = ctx;
-       int ret = 0;
+       struct btrfs_fs_info *fs_info = nocow_ctx->sctx->dev_root->fs_info;
        struct btrfs_key key;
-       struct inode *inode = NULL;
+       struct inode *inode;
+       struct page *page;
        struct btrfs_root *local_root;
        u64 physical_for_dev_replace;
        u64 len;
-       struct btrfs_fs_info *fs_info = nocow_ctx->sctx->dev_root->fs_info;
+       unsigned long index;
        int srcu_index;
+       int ret;
+       int err;
 
        key.objectid = root;
        key.type = BTRFS_ROOT_ITEM_KEY;
@@ -3227,6 +3224,11 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)
                return PTR_ERR(local_root);
        }
 
+       if (btrfs_root_refs(&local_root->root_item) == 0) {
+               srcu_read_unlock(&fs_info->subvol_srcu, srcu_index);
+               return -ENOENT;
+       }
+
        key.type = BTRFS_INODE_ITEM_KEY;
        key.objectid = inum;
        key.offset = 0;
@@ -3235,19 +3237,21 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)
        if (IS_ERR(inode))
                return PTR_ERR(inode);
 
+       /* Avoid truncate/dio/punch hole.. */
+       mutex_lock(&inode->i_mutex);
+       inode_dio_wait(inode);
+
+       ret = 0;
        physical_for_dev_replace = nocow_ctx->physical_for_dev_replace;
        len = nocow_ctx->len;
        while (len >= PAGE_CACHE_SIZE) {
-               struct page *page = NULL;
-               int ret_sub;
-
                index = offset >> PAGE_CACHE_SHIFT;
-
+again:
                page = find_or_create_page(inode->i_mapping, index, GFP_NOFS);
                if (!page) {
                        pr_err("find_or_create_page() failed\n");
                        ret = -ENOMEM;
-                       goto next_page;
+                       goto out;
                }
 
                if (PageUptodate(page)) {
@@ -3255,39 +3259,49 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx)
                                goto next_page;
                } else {
                        ClearPageError(page);
-                       ret_sub = extent_read_full_page(&BTRFS_I(inode)->
+                       err = extent_read_full_page(&BTRFS_I(inode)->
                                                         io_tree,
                                                        page, btrfs_get_extent,
                                                        nocow_ctx->mirror_num);
-                       if (ret_sub) {
-                               ret = ret_sub;
+                       if (err) {
+                               ret = err;
                                goto next_page;
                        }
-                       wait_on_page_locked(page);
+
+                       lock_page(page);
+                       /*
+                        * If the page has been remove from the page cache,
+                        * the data on it is meaningless, because it may be
+                        * old one, the new data may be written into the new
+                        * page in the page cache.
+                        */
+                       if (page->mapping != inode->i_mapping) {
+                               page_cache_release(page);
+                               goto again;
+                       }
                        if (!PageUptodate(page)) {
                                ret = -EIO;
                                goto next_page;
                        }
                }
-               ret_sub = write_page_nocow(nocow_ctx->sctx,
-                                          physical_for_dev_replace, page);
-               if (ret_sub) {
-                       ret = ret_sub;
-                       goto next_page;
-               }
-
+               err = write_page_nocow(nocow_ctx->sctx,
+                                      physical_for_dev_replace, page);
+               if (err)
+                       ret = err;
 next_page:
-               if (page) {
-                       unlock_page(page);
-                       put_page(page);
-               }
+               unlock_page(page);
+               page_cache_release(page);
+
+               if (ret)
+                       break;
+
                offset += PAGE_CACHE_SIZE;
                physical_for_dev_replace += PAGE_CACHE_SIZE;
                len -= PAGE_CACHE_SIZE;
        }
-
-       if (inode)
-               iput(inode);
+out:
+       mutex_unlock(&inode->i_mutex);
+       iput(inode);
        return ret;
 }
 
index ff40f1c..d3f3b43 100644 (file)
@@ -158,7 +158,7 @@ static void fs_path_reset(struct fs_path *p)
        }
 }
 
-static struct fs_path *fs_path_alloc(struct send_ctx *sctx)
+static struct fs_path *fs_path_alloc(void)
 {
        struct fs_path *p;
 
@@ -173,11 +173,11 @@ static struct fs_path *fs_path_alloc(struct send_ctx *sctx)
        return p;
 }
 
-static struct fs_path *fs_path_alloc_reversed(struct send_ctx *sctx)
+static struct fs_path *fs_path_alloc_reversed(void)
 {
        struct fs_path *p;
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return NULL;
        p->reversed = 1;
@@ -185,7 +185,7 @@ static struct fs_path *fs_path_alloc_reversed(struct send_ctx *sctx)
        return p;
 }
 
-static void fs_path_free(struct send_ctx *sctx, struct fs_path *p)
+static void fs_path_free(struct fs_path *p)
 {
        if (!p)
                return;
@@ -753,8 +753,7 @@ typedef int (*iterate_inode_ref_t)(int num, u64 dir, int index,
  *
  * path must point to the INODE_REF or INODE_EXTREF when called.
  */
-static int iterate_inode_ref(struct send_ctx *sctx,
-                            struct btrfs_root *root, struct btrfs_path *path,
+static int iterate_inode_ref(struct btrfs_root *root, struct btrfs_path *path,
                             struct btrfs_key *found_key, int resolve,
                             iterate_inode_ref_t iterate, void *ctx)
 {
@@ -777,13 +776,13 @@ static int iterate_inode_ref(struct send_ctx *sctx,
        unsigned long elem_size;
        unsigned long ptr;
 
-       p = fs_path_alloc_reversed(sctx);
+       p = fs_path_alloc_reversed();
        if (!p)
                return -ENOMEM;
 
        tmp_path = alloc_path_for_send();
        if (!tmp_path) {
-               fs_path_free(sctx, p);
+               fs_path_free(p);
                return -ENOMEM;
        }
 
@@ -858,7 +857,7 @@ static int iterate_inode_ref(struct send_ctx *sctx,
 
 out:
        btrfs_free_path(tmp_path);
-       fs_path_free(sctx, p);
+       fs_path_free(p);
        return ret;
 }
 
@@ -874,8 +873,7 @@ typedef int (*iterate_dir_item_t)(int num, struct btrfs_key *di_key,
  *
  * path must point to the dir item when called.
  */
-static int iterate_dir_item(struct send_ctx *sctx,
-                           struct btrfs_root *root, struct btrfs_path *path,
+static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
                            struct btrfs_key *found_key,
                            iterate_dir_item_t iterate, void *ctx)
 {
@@ -990,7 +988,7 @@ static int __copy_first_ref(int num, u64 dir, int index,
  * Retrieve the first path of an inode. If an inode has more then one
  * ref/hardlink, this is ignored.
  */
-static int get_inode_path(struct send_ctx *sctx, struct btrfs_root *root,
+static int get_inode_path(struct btrfs_root *root,
                          u64 ino, struct fs_path *path)
 {
        int ret;
@@ -1022,8 +1020,8 @@ static int get_inode_path(struct send_ctx *sctx, struct btrfs_root *root,
                goto out;
        }
 
-       ret = iterate_inode_ref(sctx, root, p, &found_key, 1,
-                       __copy_first_ref, path);
+       ret = iterate_inode_ref(root, p, &found_key, 1,
+                               __copy_first_ref, path);
        if (ret < 0)
                goto out;
        ret = 0;
@@ -1314,8 +1312,7 @@ out:
        return ret;
 }
 
-static int read_symlink(struct send_ctx *sctx,
-                       struct btrfs_root *root,
+static int read_symlink(struct btrfs_root *root,
                        u64 ino,
                        struct fs_path *dest)
 {
@@ -1562,8 +1559,7 @@ out:
  * Looks up the first btrfs_inode_ref of a given ino. It returns the parent dir,
  * generation of the parent dir and the name of the dir entry.
  */
-static int get_first_ref(struct send_ctx *sctx,
-                        struct btrfs_root *root, u64 ino,
+static int get_first_ref(struct btrfs_root *root, u64 ino,
                         u64 *dir, u64 *dir_gen, struct fs_path *name)
 {
        int ret;
@@ -1628,8 +1624,7 @@ out:
        return ret;
 }
 
-static int is_first_ref(struct send_ctx *sctx,
-                       struct btrfs_root *root,
+static int is_first_ref(struct btrfs_root *root,
                        u64 ino, u64 dir,
                        const char *name, int name_len)
 {
@@ -1638,11 +1633,11 @@ static int is_first_ref(struct send_ctx *sctx,
        u64 tmp_dir;
        u64 tmp_dir_gen;
 
-       tmp_name = fs_path_alloc(sctx);
+       tmp_name = fs_path_alloc();
        if (!tmp_name)
                return -ENOMEM;
 
-       ret = get_first_ref(sctx, root, ino, &tmp_dir, &tmp_dir_gen, tmp_name);
+       ret = get_first_ref(root, ino, &tmp_dir, &tmp_dir_gen, tmp_name);
        if (ret < 0)
                goto out;
 
@@ -1654,7 +1649,7 @@ static int is_first_ref(struct send_ctx *sctx,
        ret = !memcmp(tmp_name->start, name, name_len);
 
 out:
-       fs_path_free(sctx, tmp_name);
+       fs_path_free(tmp_name);
        return ret;
 }
 
@@ -1783,11 +1778,11 @@ static int did_overwrite_first_ref(struct send_ctx *sctx, u64 ino, u64 gen)
        if (!sctx->parent_root)
                goto out;
 
-       name = fs_path_alloc(sctx);
+       name = fs_path_alloc();
        if (!name)
                return -ENOMEM;
 
-       ret = get_first_ref(sctx, sctx->parent_root, ino, &dir, &dir_gen, name);
+       ret = get_first_ref(sctx->parent_root, ino, &dir, &dir_gen, name);
        if (ret < 0)
                goto out;
 
@@ -1795,7 +1790,7 @@ static int did_overwrite_first_ref(struct send_ctx *sctx, u64 ino, u64 gen)
                        name->start, fs_path_len(name));
 
 out:
-       fs_path_free(sctx, name);
+       fs_path_free(name);
        return ret;
 }
 
@@ -1979,11 +1974,11 @@ static int __get_cur_name_and_parent(struct send_ctx *sctx,
         * send_root or parent_root for ref lookup.
         */
        if (ino < sctx->send_progress)
-               ret = get_first_ref(sctx, sctx->send_root, ino,
-                               parent_ino, parent_gen, dest);
+               ret = get_first_ref(sctx->send_root, ino,
+                                   parent_ino, parent_gen, dest);
        else
-               ret = get_first_ref(sctx, sctx->parent_root, ino,
-                               parent_ino, parent_gen, dest);
+               ret = get_first_ref(sctx->parent_root, ino,
+                                   parent_ino, parent_gen, dest);
        if (ret < 0)
                goto out;
 
@@ -2070,7 +2065,7 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen,
        u64 parent_gen = 0;
        int stop = 0;
 
-       name = fs_path_alloc(sctx);
+       name = fs_path_alloc();
        if (!name) {
                ret = -ENOMEM;
                goto out;
@@ -2098,7 +2093,7 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen,
        }
 
 out:
-       fs_path_free(sctx, name);
+       fs_path_free(name);
        if (!ret)
                fs_path_unreverse(dest);
        return ret;
@@ -2263,7 +2258,7 @@ static int send_truncate(struct send_ctx *sctx, u64 ino, u64 gen, u64 size)
 
 verbose_printk("btrfs: send_truncate %llu size=%llu\n", ino, size);
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -2281,7 +2276,7 @@ verbose_printk("btrfs: send_truncate %llu size=%llu\n", ino, size);
 
 tlv_put_failure:
 out:
-       fs_path_free(sctx, p);
+       fs_path_free(p);
        return ret;
 }
 
@@ -2292,7 +2287,7 @@ static int send_chmod(struct send_ctx *sctx, u64 ino, u64 gen, u64 mode)
 
 verbose_printk("btrfs: send_chmod %llu mode=%llu\n", ino, mode);
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -2310,7 +2305,7 @@ verbose_printk("btrfs: send_chmod %llu mode=%llu\n", ino, mode);
 
 tlv_put_failure:
 out:
-       fs_path_free(sctx, p);
+       fs_path_free(p);
        return ret;
 }
 
@@ -2321,7 +2316,7 @@ static int send_chown(struct send_ctx *sctx, u64 ino, u64 gen, u64 uid, u64 gid)
 
 verbose_printk("btrfs: send_chown %llu uid=%llu, gid=%llu\n", ino, uid, gid);
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -2340,7 +2335,7 @@ verbose_printk("btrfs: send_chown %llu uid=%llu, gid=%llu\n", ino, uid, gid);
 
 tlv_put_failure:
 out:
-       fs_path_free(sctx, p);
+       fs_path_free(p);
        return ret;
 }
 
@@ -2356,7 +2351,7 @@ static int send_utimes(struct send_ctx *sctx, u64 ino, u64 gen)
 
 verbose_printk("btrfs: send_utimes %llu\n", ino);
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -2397,7 +2392,7 @@ verbose_printk("btrfs: send_utimes %llu\n", ino);
 
 tlv_put_failure:
 out:
-       fs_path_free(sctx, p);
+       fs_path_free(p);
        btrfs_free_path(path);
        return ret;
 }
@@ -2418,7 +2413,7 @@ static int send_create_inode(struct send_ctx *sctx, u64 ino)
 
 verbose_printk("btrfs: send_create_inode %llu\n", ino);
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -2459,7 +2454,7 @@ verbose_printk("btrfs: send_create_inode %llu\n", ino);
 
        if (S_ISLNK(mode)) {
                fs_path_reset(p);
-               ret = read_symlink(sctx, sctx->send_root, ino, p);
+               ret = read_symlink(sctx->send_root, ino, p);
                if (ret < 0)
                        goto out;
                TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH_LINK, p);
@@ -2476,7 +2471,7 @@ verbose_printk("btrfs: send_create_inode %llu\n", ino);
 
 tlv_put_failure:
 out:
-       fs_path_free(sctx, p);
+       fs_path_free(p);
        return ret;
 }
 
@@ -2615,13 +2610,13 @@ static int record_ref(struct list_head *head, u64 dir,
        return 0;
 }
 
-static void __free_recorded_refs(struct send_ctx *sctx, struct list_head *head)
+static void __free_recorded_refs(struct list_head *head)
 {
        struct recorded_ref *cur;
 
        while (!list_empty(head)) {
                cur = list_entry(head->next, struct recorded_ref, list);
-               fs_path_free(sctx, cur->full_path);
+               fs_path_free(cur->full_path);
                list_del(&cur->list);
                kfree(cur);
        }
@@ -2629,8 +2624,8 @@ static void __free_recorded_refs(struct send_ctx *sctx, struct list_head *head)
 
 static void free_recorded_refs(struct send_ctx *sctx)
 {
-       __free_recorded_refs(sctx, &sctx->new_refs);
-       __free_recorded_refs(sctx, &sctx->deleted_refs);
+       __free_recorded_refs(&sctx->new_refs);
+       __free_recorded_refs(&sctx->deleted_refs);
 }
 
 /*
@@ -2644,7 +2639,7 @@ static int orphanize_inode(struct send_ctx *sctx, u64 ino, u64 gen,
        int ret;
        struct fs_path *orphan;
 
-       orphan = fs_path_alloc(sctx);
+       orphan = fs_path_alloc();
        if (!orphan)
                return -ENOMEM;
 
@@ -2655,7 +2650,7 @@ static int orphanize_inode(struct send_ctx *sctx, u64 ino, u64 gen,
        ret = send_rename(sctx, path, orphan);
 
 out:
-       fs_path_free(sctx, orphan);
+       fs_path_free(orphan);
        return ret;
 }
 
@@ -2746,7 +2741,7 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
         */
        BUG_ON(sctx->cur_ino <= BTRFS_FIRST_FREE_OBJECTID);
 
-       valid_path = fs_path_alloc(sctx);
+       valid_path = fs_path_alloc();
        if (!valid_path) {
                ret = -ENOMEM;
                goto out;
@@ -2843,9 +2838,9 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
                if (ret < 0)
                        goto out;
                if (ret) {
-                       ret = is_first_ref(sctx, sctx->parent_root,
-                                       ow_inode, cur->dir, cur->name,
-                                       cur->name_len);
+                       ret = is_first_ref(sctx->parent_root,
+                                          ow_inode, cur->dir, cur->name,
+                                          cur->name_len);
                        if (ret < 0)
                                goto out;
                        if (ret) {
@@ -3024,7 +3019,7 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
 out:
        free_recorded_refs(sctx);
        ulist_free(check_dirs);
-       fs_path_free(sctx, valid_path);
+       fs_path_free(valid_path);
        return ret;
 }
 
@@ -3037,7 +3032,7 @@ static int __record_new_ref(int num, u64 dir, int index,
        struct fs_path *p;
        u64 gen;
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -3057,7 +3052,7 @@ static int __record_new_ref(int num, u64 dir, int index,
 
 out:
        if (ret)
-               fs_path_free(sctx, p);
+               fs_path_free(p);
        return ret;
 }
 
@@ -3070,7 +3065,7 @@ static int __record_deleted_ref(int num, u64 dir, int index,
        struct fs_path *p;
        u64 gen;
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -3090,7 +3085,7 @@ static int __record_deleted_ref(int num, u64 dir, int index,
 
 out:
        if (ret)
-               fs_path_free(sctx, p);
+               fs_path_free(p);
        return ret;
 }
 
@@ -3098,8 +3093,8 @@ static int record_new_ref(struct send_ctx *sctx)
 {
        int ret;
 
-       ret = iterate_inode_ref(sctx, sctx->send_root, sctx->left_path,
-                       sctx->cmp_key, 0, __record_new_ref, sctx);
+       ret = iterate_inode_ref(sctx->send_root, sctx->left_path,
+                               sctx->cmp_key, 0, __record_new_ref, sctx);
        if (ret < 0)
                goto out;
        ret = 0;
@@ -3112,8 +3107,8 @@ static int record_deleted_ref(struct send_ctx *sctx)
 {
        int ret;
 
-       ret = iterate_inode_ref(sctx, sctx->parent_root, sctx->right_path,
-                       sctx->cmp_key, 0, __record_deleted_ref, sctx);
+       ret = iterate_inode_ref(sctx->parent_root, sctx->right_path,
+                               sctx->cmp_key, 0, __record_deleted_ref, sctx);
        if (ret < 0)
                goto out;
        ret = 0;
@@ -3142,8 +3137,7 @@ static int __find_iref(int num, u64 dir, int index,
        return 0;
 }
 
-static int find_iref(struct send_ctx *sctx,
-                    struct btrfs_root *root,
+static int find_iref(struct btrfs_root *root,
                     struct btrfs_path *path,
                     struct btrfs_key *key,
                     u64 dir, struct fs_path *name)
@@ -3155,7 +3149,7 @@ static int find_iref(struct send_ctx *sctx,
        ctx.name = name;
        ctx.found_idx = -1;
 
-       ret = iterate_inode_ref(sctx, root, path, key, 0, __find_iref, &ctx);
+       ret = iterate_inode_ref(root, path, key, 0, __find_iref, &ctx);
        if (ret < 0)
                return ret;
 
@@ -3172,7 +3166,7 @@ static int __record_changed_new_ref(int num, u64 dir, int index,
        int ret;
        struct send_ctx *sctx = ctx;
 
-       ret = find_iref(sctx, sctx->parent_root, sctx->right_path,
+       ret = find_iref(sctx->parent_root, sctx->right_path,
                        sctx->cmp_key, dir, name);
        if (ret == -ENOENT)
                ret = __record_new_ref(num, dir, index, name, sctx);
@@ -3189,7 +3183,7 @@ static int __record_changed_deleted_ref(int num, u64 dir, int index,
        int ret;
        struct send_ctx *sctx = ctx;
 
-       ret = find_iref(sctx, sctx->send_root, sctx->left_path, sctx->cmp_key,
+       ret = find_iref(sctx->send_root, sctx->left_path, sctx->cmp_key,
                        dir, name);
        if (ret == -ENOENT)
                ret = __record_deleted_ref(num, dir, index, name, sctx);
@@ -3203,11 +3197,11 @@ static int record_changed_ref(struct send_ctx *sctx)
 {
        int ret = 0;
 
-       ret = iterate_inode_ref(sctx, sctx->send_root, sctx->left_path,
+       ret = iterate_inode_ref(sctx->send_root, sctx->left_path,
                        sctx->cmp_key, 0, __record_changed_new_ref, sctx);
        if (ret < 0)
                goto out;
-       ret = iterate_inode_ref(sctx, sctx->parent_root, sctx->right_path,
+       ret = iterate_inode_ref(sctx->parent_root, sctx->right_path,
                        sctx->cmp_key, 0, __record_changed_deleted_ref, sctx);
        if (ret < 0)
                goto out;
@@ -3266,8 +3260,7 @@ static int process_all_refs(struct send_ctx *sctx,
                     found_key.type != BTRFS_INODE_EXTREF_KEY))
                        break;
 
-               ret = iterate_inode_ref(sctx, root, path, &found_key, 0, cb,
-                               sctx);
+               ret = iterate_inode_ref(root, path, &found_key, 0, cb, sctx);
                btrfs_release_path(path);
                if (ret < 0)
                        goto out;
@@ -3335,7 +3328,7 @@ static int __process_new_xattr(int num, struct btrfs_key *di_key,
        struct fs_path *p;
        posix_acl_xattr_header dummy_acl;
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -3362,7 +3355,7 @@ static int __process_new_xattr(int num, struct btrfs_key *di_key,
        ret = send_set_xattr(sctx, p, name, name_len, data, data_len);
 
 out:
-       fs_path_free(sctx, p);
+       fs_path_free(p);
        return ret;
 }
 
@@ -3375,7 +3368,7 @@ static int __process_deleted_xattr(int num, struct btrfs_key *di_key,
        struct send_ctx *sctx = ctx;
        struct fs_path *p;
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -3386,7 +3379,7 @@ static int __process_deleted_xattr(int num, struct btrfs_key *di_key,
        ret = send_remove_xattr(sctx, p, name, name_len);
 
 out:
-       fs_path_free(sctx, p);
+       fs_path_free(p);
        return ret;
 }
 
@@ -3394,8 +3387,8 @@ static int process_new_xattr(struct send_ctx *sctx)
 {
        int ret = 0;
 
-       ret = iterate_dir_item(sctx, sctx->send_root, sctx->left_path,
-                       sctx->cmp_key, __process_new_xattr, sctx);
+       ret = iterate_dir_item(sctx->send_root, sctx->left_path,
+                              sctx->cmp_key, __process_new_xattr, sctx);
 
        return ret;
 }
@@ -3404,8 +3397,8 @@ static int process_deleted_xattr(struct send_ctx *sctx)
 {
        int ret;
 
-       ret = iterate_dir_item(sctx, sctx->parent_root, sctx->right_path,
-                       sctx->cmp_key, __process_deleted_xattr, sctx);
+       ret = iterate_dir_item(sctx->parent_root, sctx->right_path,
+                              sctx->cmp_key, __process_deleted_xattr, sctx);
 
        return ret;
 }
@@ -3429,17 +3422,15 @@ static int __find_xattr(int num, struct btrfs_key *di_key,
            strncmp(name, ctx->name, name_len) == 0) {
                ctx->found_idx = num;
                ctx->found_data_len = data_len;
-               ctx->found_data = kmalloc(data_len, GFP_NOFS);
+               ctx->found_data = kmemdup(data, data_len, GFP_NOFS);
                if (!ctx->found_data)
                        return -ENOMEM;
-               memcpy(ctx->found_data, data, data_len);
                return 1;
        }
        return 0;
 }
 
-static int find_xattr(struct send_ctx *sctx,
-                     struct btrfs_root *root,
+static int find_xattr(struct btrfs_root *root,
                      struct btrfs_path *path,
                      struct btrfs_key *key,
                      const char *name, int name_len,
@@ -3454,7 +3445,7 @@ static int find_xattr(struct send_ctx *sctx,
        ctx.found_data = NULL;
        ctx.found_data_len = 0;
 
-       ret = iterate_dir_item(sctx, root, path, key, __find_xattr, &ctx);
+       ret = iterate_dir_item(root, path, key, __find_xattr, &ctx);
        if (ret < 0)
                return ret;
 
@@ -3480,9 +3471,9 @@ static int __process_changed_new_xattr(int num, struct btrfs_key *di_key,
        char *found_data = NULL;
        int found_data_len  = 0;
 
-       ret = find_xattr(sctx, sctx->parent_root, sctx->right_path,
-                       sctx->cmp_key, name, name_len, &found_data,
-                       &found_data_len);
+       ret = find_xattr(sctx->parent_root, sctx->right_path,
+                        sctx->cmp_key, name, name_len, &found_data,
+                        &found_data_len);
        if (ret == -ENOENT) {
                ret = __process_new_xattr(num, di_key, name, name_len, data,
                                data_len, type, ctx);
@@ -3508,8 +3499,8 @@ static int __process_changed_deleted_xattr(int num, struct btrfs_key *di_key,
        int ret;
        struct send_ctx *sctx = ctx;
 
-       ret = find_xattr(sctx, sctx->send_root, sctx->left_path, sctx->cmp_key,
-                       name, name_len, NULL, NULL);
+       ret = find_xattr(sctx->send_root, sctx->left_path, sctx->cmp_key,
+                        name, name_len, NULL, NULL);
        if (ret == -ENOENT)
                ret = __process_deleted_xattr(num, di_key, name, name_len, data,
                                data_len, type, ctx);
@@ -3523,11 +3514,11 @@ static int process_changed_xattr(struct send_ctx *sctx)
 {
        int ret = 0;
 
-       ret = iterate_dir_item(sctx, sctx->send_root, sctx->left_path,
+       ret = iterate_dir_item(sctx->send_root, sctx->left_path,
                        sctx->cmp_key, __process_changed_new_xattr, sctx);
        if (ret < 0)
                goto out;
-       ret = iterate_dir_item(sctx, sctx->parent_root, sctx->right_path,
+       ret = iterate_dir_item(sctx->parent_root, sctx->right_path,
                        sctx->cmp_key, __process_changed_deleted_xattr, sctx);
 
 out:
@@ -3572,8 +3563,8 @@ static int process_all_new_xattrs(struct send_ctx *sctx)
                        goto out;
                }
 
-               ret = iterate_dir_item(sctx, root, path, &found_key,
-                               __process_new_xattr, sctx);
+               ret = iterate_dir_item(root, path, &found_key,
+                                      __process_new_xattr, sctx);
                if (ret < 0)
                        goto out;
 
@@ -3598,7 +3589,7 @@ static int send_write(struct send_ctx *sctx, u64 offset, u32 len)
        int num_read = 0;
        mm_segment_t old_fs;
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -3640,7 +3631,7 @@ verbose_printk("btrfs: send_write offset=%llu, len=%d\n", offset, len);
 
 tlv_put_failure:
 out:
-       fs_path_free(sctx, p);
+       fs_path_free(p);
        set_fs(old_fs);
        if (ret < 0)
                return ret;
@@ -3663,7 +3654,7 @@ verbose_printk("btrfs: send_clone offset=%llu, len=%d, clone_root=%llu, "
                clone_root->root->objectid, clone_root->ino,
                clone_root->offset);
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -3686,8 +3677,7 @@ verbose_printk("btrfs: send_clone offset=%llu, len=%d, clone_root=%llu, "
                        goto out;
                ret = get_cur_path(sctx, clone_root->ino, gen, p);
        } else {
-               ret = get_inode_path(sctx, clone_root->root,
-                               clone_root->ino, p);
+               ret = get_inode_path(clone_root->root, clone_root->ino, p);
        }
        if (ret < 0)
                goto out;
@@ -3704,7 +3694,7 @@ verbose_printk("btrfs: send_clone offset=%llu, len=%d, clone_root=%llu, "
 
 tlv_put_failure:
 out:
-       fs_path_free(sctx, p);
+       fs_path_free(p);
        return ret;
 }
 
@@ -3717,7 +3707,7 @@ static int send_update_extent(struct send_ctx *sctx,
        int ret = 0;
        struct fs_path *p;
 
-       p = fs_path_alloc(sctx);
+       p = fs_path_alloc();
        if (!p)
                return -ENOMEM;
 
@@ -3737,7 +3727,7 @@ static int send_update_extent(struct send_ctx *sctx,
 
 tlv_put_failure:
 out:
-       fs_path_free(sctx, p);
+       fs_path_free(p);
        return ret;
 }
 
@@ -4579,6 +4569,41 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
        send_root = BTRFS_I(file_inode(mnt_file))->root;
        fs_info = send_root->fs_info;
 
+       /*
+        * This is done when we lookup the root, it should already be complete
+        * by the time we get here.
+        */
+       WARN_ON(send_root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE);
+
+       /*
+        * If we just created this root we need to make sure that the orphan
+        * cleanup has been done and committed since we search the commit root,
+        * so check its commit root transid with our otransid and if they match
+        * commit the transaction to make sure everything is updated.
+        */
+       down_read(&send_root->fs_info->extent_commit_sem);
+       if (btrfs_header_generation(send_root->commit_root) ==
+           btrfs_root_otransid(&send_root->root_item)) {
+               struct btrfs_trans_handle *trans;
+
+               up_read(&send_root->fs_info->extent_commit_sem);
+
+               trans = btrfs_attach_transaction_barrier(send_root);
+               if (IS_ERR(trans)) {
+                       if (PTR_ERR(trans) != -ENOENT) {
+                               ret = PTR_ERR(trans);
+                               goto out;
+                       }
+                       /* ENOENT means theres no transaction */
+               } else {
+                       ret = btrfs_commit_transaction(trans, send_root);
+                       if (ret)
+                               goto out;
+               }
+       } else {
+               up_read(&send_root->fs_info->extent_commit_sem);
+       }
+
        arg = memdup_user(arg_, sizeof(*arg));
        if (IS_ERR(arg)) {
                ret = PTR_ERR(arg);
@@ -4663,10 +4688,6 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
                        key.type = BTRFS_ROOT_ITEM_KEY;
                        key.offset = (u64)-1;
                        clone_root = btrfs_read_fs_root_no_name(fs_info, &key);
-                       if (!clone_root) {
-                               ret = -EINVAL;
-                               goto out;
-                       }
                        if (IS_ERR(clone_root)) {
                                ret = PTR_ERR(clone_root);
                                goto out;
@@ -4682,8 +4703,8 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
                key.type = BTRFS_ROOT_ITEM_KEY;
                key.offset = (u64)-1;
                sctx->parent_root = btrfs_read_fs_root_no_name(fs_info, &key);
-               if (!sctx->parent_root) {
-                       ret = -EINVAL;
+               if (IS_ERR(sctx->parent_root)) {
+                       ret = PTR_ERR(sctx->parent_root);
                        goto out;
                }
        }
index f0857e0..8eb6191 100644 (file)
@@ -51,7 +51,6 @@
 #include "print-tree.h"
 #include "xattr.h"
 #include "volumes.h"
-#include "version.h"
 #include "export.h"
 #include "compression.h"
 #include "rcu-string.h"
@@ -266,6 +265,9 @@ void __btrfs_abort_transaction(struct btrfs_trans_handle *trans,
                return;
        }
        ACCESS_ONCE(trans->transaction->aborted) = errno;
+       /* Wake up anybody who may be waiting on this transaction */
+       wake_up(&root->fs_info->transaction_wait);
+       wake_up(&root->fs_info->transaction_blocked_wait);
        __btrfs_std_error(root->fs_info, function, line, errno, NULL);
 }
 /*
@@ -776,9 +778,6 @@ find_root:
        if (IS_ERR(new_root))
                return ERR_CAST(new_root);
 
-       if (btrfs_root_refs(&new_root->root_item) == 0)
-               return ERR_PTR(-ENOENT);
-
        dir_id = btrfs_root_dirid(&new_root->root_item);
 setup_root:
        location.objectid = dir_id;
@@ -866,7 +865,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait)
                return 0;
        }
 
-       btrfs_wait_ordered_extents(root, 1);
+       btrfs_wait_all_ordered_extents(fs_info, 1);
 
        trans = btrfs_attach_transaction_barrier(root);
        if (IS_ERR(trans)) {
@@ -1685,6 +1684,18 @@ static void btrfs_interface_exit(void)
                printk(KERN_INFO "btrfs: misc_deregister failed for control device\n");
 }
 
+static void btrfs_print_info(void)
+{
+       printk(KERN_INFO "Btrfs loaded"
+#ifdef CONFIG_BTRFS_DEBUG
+                       ", debug=on"
+#endif
+#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
+                       ", integrity-checker=on"
+#endif
+                       "\n");
+}
+
 static int __init init_btrfs_fs(void)
 {
        int err;
@@ -1733,11 +1744,9 @@ static int __init init_btrfs_fs(void)
 
        btrfs_init_lockdep();
 
-#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+       btrfs_print_info();
        btrfs_test_free_space_cache();
-#endif
 
-       printk(KERN_INFO "%s loaded\n", BTRFS_BUILD_VERSION);
        return 0;
 
 unregister_ioctl:
index 0544587..d58cce7 100644 (file)
 
 #define BTRFS_ROOT_TRANS_TAG 0
 
+static unsigned int btrfs_blocked_trans_types[TRANS_STATE_MAX] = {
+       [TRANS_STATE_RUNNING]           = 0U,
+       [TRANS_STATE_BLOCKED]           = (__TRANS_USERSPACE |
+                                          __TRANS_START),
+       [TRANS_STATE_COMMIT_START]      = (__TRANS_USERSPACE |
+                                          __TRANS_START |
+                                          __TRANS_ATTACH),
+       [TRANS_STATE_COMMIT_DOING]      = (__TRANS_USERSPACE |
+                                          __TRANS_START |
+                                          __TRANS_ATTACH |
+                                          __TRANS_JOIN),
+       [TRANS_STATE_UNBLOCKED]         = (__TRANS_USERSPACE |
+                                          __TRANS_START |
+                                          __TRANS_ATTACH |
+                                          __TRANS_JOIN |
+                                          __TRANS_JOIN_NOLOCK),
+       [TRANS_STATE_COMPLETED]         = (__TRANS_USERSPACE |
+                                          __TRANS_START |
+                                          __TRANS_ATTACH |
+                                          __TRANS_JOIN |
+                                          __TRANS_JOIN_NOLOCK),
+};
+
 static void put_transaction(struct btrfs_transaction *transaction)
 {
        WARN_ON(atomic_read(&transaction->use_count) == 0);
        if (atomic_dec_and_test(&transaction->use_count)) {
                BUG_ON(!list_empty(&transaction->list));
                WARN_ON(transaction->delayed_refs.root.rb_node);
+               while (!list_empty(&transaction->pending_chunks)) {
+                       struct extent_map *em;
+
+                       em = list_first_entry(&transaction->pending_chunks,
+                                             struct extent_map, list);
+                       list_del_init(&em->list);
+                       free_extent_map(em);
+               }
                kmem_cache_free(btrfs_transaction_cachep, transaction);
        }
 }
@@ -50,18 +81,35 @@ static noinline void switch_commit_root(struct btrfs_root *root)
        root->commit_root = btrfs_root_node(root);
 }
 
-static inline int can_join_transaction(struct btrfs_transaction *trans,
-                                      int type)
+static inline void extwriter_counter_inc(struct btrfs_transaction *trans,
+                                        unsigned int type)
+{
+       if (type & TRANS_EXTWRITERS)
+               atomic_inc(&trans->num_extwriters);
+}
+
+static inline void extwriter_counter_dec(struct btrfs_transaction *trans,
+                                        unsigned int type)
+{
+       if (type & TRANS_EXTWRITERS)
+               atomic_dec(&trans->num_extwriters);
+}
+
+static inline void extwriter_counter_init(struct btrfs_transaction *trans,
+                                         unsigned int type)
+{
+       atomic_set(&trans->num_extwriters, ((type & TRANS_EXTWRITERS) ? 1 : 0));
+}
+
+static inline int extwriter_counter_read(struct btrfs_transaction *trans)
 {
-       return !(trans->in_commit &&
-                type != TRANS_JOIN &&
-                type != TRANS_JOIN_NOLOCK);
+       return atomic_read(&trans->num_extwriters);
 }
 
 /*
  * either allocate a new transaction or hop into the existing one
  */
-static noinline int join_transaction(struct btrfs_root *root, int type)
+static noinline int join_transaction(struct btrfs_root *root, unsigned int type)
 {
        struct btrfs_transaction *cur_trans;
        struct btrfs_fs_info *fs_info = root->fs_info;
@@ -74,32 +122,19 @@ loop:
                return -EROFS;
        }
 
-       if (fs_info->trans_no_join) {
-               /* 
-                * If we are JOIN_NOLOCK we're already committing a current
-                * transaction, we just need a handle to deal with something
-                * when committing the transaction, such as inode cache and
-                * space cache. It is a special case.
-                */
-               if (type != TRANS_JOIN_NOLOCK) {
-                       spin_unlock(&fs_info->trans_lock);
-                       return -EBUSY;
-               }
-       }
-
        cur_trans = fs_info->running_transaction;
        if (cur_trans) {
                if (cur_trans->aborted) {
                        spin_unlock(&fs_info->trans_lock);
                        return cur_trans->aborted;
                }
-               if (!can_join_transaction(cur_trans, type)) {
+               if (btrfs_blocked_trans_types[cur_trans->state] & type) {
                        spin_unlock(&fs_info->trans_lock);
                        return -EBUSY;
                }
                atomic_inc(&cur_trans->use_count);
                atomic_inc(&cur_trans->num_writers);
-               cur_trans->num_joined++;
+               extwriter_counter_inc(cur_trans, type);
                spin_unlock(&fs_info->trans_lock);
                return 0;
        }
@@ -112,6 +147,12 @@ loop:
        if (type == TRANS_ATTACH)
                return -ENOENT;
 
+       /*
+        * JOIN_NOLOCK only happens during the transaction commit, so
+        * it is impossible that ->running_transaction is NULL
+        */
+       BUG_ON(type == TRANS_JOIN_NOLOCK);
+
        cur_trans = kmem_cache_alloc(btrfs_transaction_cachep, GFP_NOFS);
        if (!cur_trans)
                return -ENOMEM;
@@ -120,7 +161,7 @@ loop:
        if (fs_info->running_transaction) {
                /*
                 * someone started a transaction after we unlocked.  Make sure
-                * to redo the trans_no_join checks above
+                * to redo the checks above
                 */
                kmem_cache_free(btrfs_transaction_cachep, cur_trans);
                goto loop;
@@ -131,17 +172,15 @@ loop:
        }
 
        atomic_set(&cur_trans->num_writers, 1);
-       cur_trans->num_joined = 0;
+       extwriter_counter_init(cur_trans, type);
        init_waitqueue_head(&cur_trans->writer_wait);
        init_waitqueue_head(&cur_trans->commit_wait);
-       cur_trans->in_commit = 0;
-       cur_trans->blocked = 0;
+       cur_trans->state = TRANS_STATE_RUNNING;
        /*
         * One for this trans handle, one so it will live on until we
         * commit the transaction.
         */
        atomic_set(&cur_trans->use_count, 2);
-       cur_trans->commit_done = 0;
        cur_trans->start_time = get_seconds();
 
        cur_trans->delayed_refs.root = RB_ROOT;
@@ -164,7 +203,6 @@ loop:
                        "creating a fresh transaction\n");
        atomic64_set(&fs_info->tree_mod_seq, 0);
 
-       spin_lock_init(&cur_trans->commit_lock);
        spin_lock_init(&cur_trans->delayed_refs.lock);
        atomic_set(&cur_trans->delayed_refs.procs_running_refs, 0);
        atomic_set(&cur_trans->delayed_refs.ref_seq, 0);
@@ -172,6 +210,7 @@ loop:
 
        INIT_LIST_HEAD(&cur_trans->pending_snapshots);
        INIT_LIST_HEAD(&cur_trans->ordered_operations);
+       INIT_LIST_HEAD(&cur_trans->pending_chunks);
        list_add_tail(&cur_trans->list, &fs_info->trans_list);
        extent_io_tree_init(&cur_trans->dirty_pages,
                             fs_info->btree_inode->i_mapping);
@@ -269,6 +308,13 @@ int btrfs_record_root_in_trans(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+static inline int is_transaction_blocked(struct btrfs_transaction *trans)
+{
+       return (trans->state >= TRANS_STATE_BLOCKED &&
+               trans->state < TRANS_STATE_UNBLOCKED &&
+               !trans->aborted);
+}
+
 /* wait for commit against the current transaction to become unblocked
  * when this is done, it is safe to start a new transaction, but the current
  * transaction might not be fully on disk.
@@ -279,12 +325,13 @@ static void wait_current_trans(struct btrfs_root *root)
 
        spin_lock(&root->fs_info->trans_lock);
        cur_trans = root->fs_info->running_transaction;
-       if (cur_trans && cur_trans->blocked) {
+       if (cur_trans && is_transaction_blocked(cur_trans)) {
                atomic_inc(&cur_trans->use_count);
                spin_unlock(&root->fs_info->trans_lock);
 
                wait_event(root->fs_info->transaction_wait,
-                          !cur_trans->blocked);
+                          cur_trans->state >= TRANS_STATE_UNBLOCKED ||
+                          cur_trans->aborted);
                put_transaction(cur_trans);
        } else {
                spin_unlock(&root->fs_info->trans_lock);
@@ -307,7 +354,7 @@ static int may_wait_transaction(struct btrfs_root *root, int type)
 }
 
 static struct btrfs_trans_handle *
-start_transaction(struct btrfs_root *root, u64 num_items, int type,
+start_transaction(struct btrfs_root *root, u64 num_items, unsigned int type,
                  enum btrfs_reserve_flush_enum flush)
 {
        struct btrfs_trans_handle *h;
@@ -320,7 +367,7 @@ start_transaction(struct btrfs_root *root, u64 num_items, int type,
                return ERR_PTR(-EROFS);
 
        if (current->journal_info) {
-               WARN_ON(type != TRANS_JOIN && type != TRANS_JOIN_NOLOCK);
+               WARN_ON(type & TRANS_EXTWRITERS);
                h = current->journal_info;
                h->use_count++;
                WARN_ON(h->use_count > 2);
@@ -366,7 +413,7 @@ again:
         * If we are ATTACH, it means we just want to catch the current
         * transaction and commit it, so we needn't do sb_start_intwrite(). 
         */
-       if (type < TRANS_JOIN_NOLOCK)
+       if (type & __TRANS_FREEZABLE)
                sb_start_intwrite(root->fs_info->sb);
 
        if (may_wait_transaction(root, type))
@@ -408,7 +455,8 @@ again:
        INIT_LIST_HEAD(&h->new_bgs);
 
        smp_mb();
-       if (cur_trans->blocked && may_wait_transaction(root, type)) {
+       if (cur_trans->state >= TRANS_STATE_BLOCKED &&
+           may_wait_transaction(root, type)) {
                btrfs_commit_transaction(h, root);
                goto again;
        }
@@ -429,7 +477,7 @@ got_it:
        return h;
 
 join_fail:
-       if (type < TRANS_JOIN_NOLOCK)
+       if (type & __TRANS_FREEZABLE)
                sb_end_intwrite(root->fs_info->sb);
        kmem_cache_free(btrfs_trans_handle_cachep, h);
 alloc_fail:
@@ -490,7 +538,7 @@ struct btrfs_trans_handle *btrfs_attach_transaction(struct btrfs_root *root)
 }
 
 /*
- * btrfs_attach_transaction() - catch the running transaction
+ * btrfs_attach_transaction_barrier() - catch the running transaction
  *
  * It is similar to the above function, the differentia is this one
  * will wait for all the inactive transactions until they fully
@@ -512,7 +560,7 @@ btrfs_attach_transaction_barrier(struct btrfs_root *root)
 static noinline void wait_for_commit(struct btrfs_root *root,
                                    struct btrfs_transaction *commit)
 {
-       wait_event(commit->commit_wait, commit->commit_done);
+       wait_event(commit->commit_wait, commit->state == TRANS_STATE_COMPLETED);
 }
 
 int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid)
@@ -548,8 +596,8 @@ int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid)
                spin_lock(&root->fs_info->trans_lock);
                list_for_each_entry_reverse(t, &root->fs_info->trans_list,
                                            list) {
-                       if (t->in_commit) {
-                               if (t->commit_done)
+                       if (t->state >= TRANS_STATE_COMMIT_START) {
+                               if (t->state == TRANS_STATE_COMPLETED)
                                        break;
                                cur_trans = t;
                                atomic_inc(&cur_trans->use_count);
@@ -576,10 +624,11 @@ void btrfs_throttle(struct btrfs_root *root)
 static int should_end_transaction(struct btrfs_trans_handle *trans,
                                  struct btrfs_root *root)
 {
-       int ret;
+       if (root->fs_info->global_block_rsv.space_info->full &&
+           btrfs_should_throttle_delayed_refs(trans, root))
+               return 1;
 
-       ret = btrfs_block_rsv_check(root, &root->fs_info->global_block_rsv, 5);
-       return ret ? 1 : 0;
+       return !!btrfs_block_rsv_check(root, &root->fs_info->global_block_rsv, 5);
 }
 
 int btrfs_should_end_transaction(struct btrfs_trans_handle *trans,
@@ -590,7 +639,8 @@ int btrfs_should_end_transaction(struct btrfs_trans_handle *trans,
        int err;
 
        smp_mb();
-       if (cur_trans->blocked || cur_trans->delayed_refs.flushing)
+       if (cur_trans->state >= TRANS_STATE_BLOCKED ||
+           cur_trans->delayed_refs.flushing)
                return 1;
 
        updates = trans->delayed_ref_updates;
@@ -609,7 +659,7 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
 {
        struct btrfs_transaction *cur_trans = trans->transaction;
        struct btrfs_fs_info *info = root->fs_info;
-       int count = 0;
+       unsigned long cur = trans->delayed_ref_updates;
        int lock = (trans->type != TRANS_JOIN_NOLOCK);
        int err = 0;
 
@@ -638,17 +688,11 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
        if (!list_empty(&trans->new_bgs))
                btrfs_create_pending_block_groups(trans, root);
 
-       while (count < 1) {
-               unsigned long cur = trans->delayed_ref_updates;
+       trans->delayed_ref_updates = 0;
+       if (btrfs_should_throttle_delayed_refs(trans, root)) {
+               cur = max_t(unsigned long, cur, 1);
                trans->delayed_ref_updates = 0;
-               if (cur &&
-                   trans->transaction->delayed_refs.num_heads_ready > 64) {
-                       trans->delayed_ref_updates = 0;
-                       btrfs_run_delayed_refs(trans, root, cur);
-               } else {
-                       break;
-               }
-               count++;
+               btrfs_run_delayed_refs(trans, root, cur);
        }
 
        btrfs_trans_release_metadata(trans, root);
@@ -658,12 +702,15 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
                btrfs_create_pending_block_groups(trans, root);
 
        if (lock && !atomic_read(&root->fs_info->open_ioctl_trans) &&
-           should_end_transaction(trans, root)) {
-               trans->transaction->blocked = 1;
-               smp_wmb();
+           should_end_transaction(trans, root) &&
+           ACCESS_ONCE(cur_trans->state) == TRANS_STATE_RUNNING) {
+               spin_lock(&info->trans_lock);
+               if (cur_trans->state == TRANS_STATE_RUNNING)
+                       cur_trans->state = TRANS_STATE_BLOCKED;
+               spin_unlock(&info->trans_lock);
        }
 
-       if (lock && cur_trans->blocked && !cur_trans->in_commit) {
+       if (lock && ACCESS_ONCE(cur_trans->state) == TRANS_STATE_BLOCKED) {
                if (throttle) {
                        /*
                         * We may race with somebody else here so end up having
@@ -677,12 +724,13 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
                }
        }
 
-       if (trans->type < TRANS_JOIN_NOLOCK)
+       if (trans->type & __TRANS_FREEZABLE)
                sb_end_intwrite(root->fs_info->sb);
 
        WARN_ON(cur_trans != info->running_transaction);
        WARN_ON(atomic_read(&cur_trans->num_writers) < 1);
        atomic_dec(&cur_trans->num_writers);
+       extwriter_counter_dec(cur_trans, trans->type);
 
        smp_mb();
        if (waitqueue_active(&cur_trans->writer_wait))
@@ -736,9 +784,7 @@ int btrfs_write_marked_extents(struct btrfs_root *root,
        struct extent_state *cached_state = NULL;
        u64 start = 0;
        u64 end;
-       struct blk_plug plug;
 
-       blk_start_plug(&plug);
        while (!find_first_extent_bit(dirty_pages, start, &start, &end,
                                      mark, &cached_state)) {
                convert_extent_bit(dirty_pages, start, end, EXTENT_NEED_WAIT,
@@ -752,7 +798,6 @@ int btrfs_write_marked_extents(struct btrfs_root *root,
        }
        if (err)
                werr = err;
-       blk_finish_plug(&plug);
        return werr;
 }
 
@@ -797,8 +842,11 @@ int btrfs_write_and_wait_marked_extents(struct btrfs_root *root,
 {
        int ret;
        int ret2;
+       struct blk_plug plug;
 
+       blk_start_plug(&plug);
        ret = btrfs_write_marked_extents(root, dirty_pages, mark);
+       blk_finish_plug(&plug);
        ret2 = btrfs_wait_marked_extents(root, dirty_pages, mark);
 
        if (ret)
@@ -1318,20 +1366,26 @@ static void update_super_roots(struct btrfs_root *root)
 
 int btrfs_transaction_in_commit(struct btrfs_fs_info *info)
 {
+       struct btrfs_transaction *trans;
        int ret = 0;
+
        spin_lock(&info->trans_lock);
-       if (info->running_transaction)
-               ret = info->running_transaction->in_commit;
+       trans = info->running_transaction;
+       if (trans)
+               ret = (trans->state >= TRANS_STATE_COMMIT_START);
        spin_unlock(&info->trans_lock);
        return ret;
 }
 
 int btrfs_transaction_blocked(struct btrfs_fs_info *info)
 {
+       struct btrfs_transaction *trans;
        int ret = 0;
+
        spin_lock(&info->trans_lock);
-       if (info->running_transaction)
-               ret = info->running_transaction->blocked;
+       trans = info->running_transaction;
+       if (trans)
+               ret = is_transaction_blocked(trans);
        spin_unlock(&info->trans_lock);
        return ret;
 }
@@ -1343,7 +1397,9 @@ int btrfs_transaction_blocked(struct btrfs_fs_info *info)
 static void wait_current_trans_commit_start(struct btrfs_root *root,
                                            struct btrfs_transaction *trans)
 {
-       wait_event(root->fs_info->transaction_blocked_wait, trans->in_commit);
+       wait_event(root->fs_info->transaction_blocked_wait,
+                  trans->state >= TRANS_STATE_COMMIT_START ||
+                  trans->aborted);
 }
 
 /*
@@ -1354,7 +1410,8 @@ static void wait_current_trans_commit_start_and_unblock(struct btrfs_root *root,
                                         struct btrfs_transaction *trans)
 {
        wait_event(root->fs_info->transaction_wait,
-                  trans->commit_done || (trans->in_commit && !trans->blocked));
+                  trans->state >= TRANS_STATE_UNBLOCKED ||
+                  trans->aborted);
 }
 
 /*
@@ -1450,26 +1507,31 @@ static void cleanup_transaction(struct btrfs_trans_handle *trans,
 
        spin_lock(&root->fs_info->trans_lock);
 
-       if (list_empty(&cur_trans->list)) {
-               spin_unlock(&root->fs_info->trans_lock);
-               btrfs_end_transaction(trans, root);
-               return;
-       }
+       /*
+        * If the transaction is removed from the list, it means this
+        * transaction has been committed successfully, so it is impossible
+        * to call the cleanup function.
+        */
+       BUG_ON(list_empty(&cur_trans->list));
 
        list_del_init(&cur_trans->list);
        if (cur_trans == root->fs_info->running_transaction) {
-               root->fs_info->trans_no_join = 1;
+               cur_trans->state = TRANS_STATE_COMMIT_DOING;
                spin_unlock(&root->fs_info->trans_lock);
                wait_event(cur_trans->writer_wait,
                           atomic_read(&cur_trans->num_writers) == 1);
 
                spin_lock(&root->fs_info->trans_lock);
-               root->fs_info->running_transaction = NULL;
        }
        spin_unlock(&root->fs_info->trans_lock);
 
        btrfs_cleanup_one_transaction(trans->transaction, root);
 
+       spin_lock(&root->fs_info->trans_lock);
+       if (cur_trans == root->fs_info->running_transaction)
+               root->fs_info->running_transaction = NULL;
+       spin_unlock(&root->fs_info->trans_lock);
+
        put_transaction(cur_trans);
        put_transaction(cur_trans);
 
@@ -1481,33 +1543,13 @@ static void cleanup_transaction(struct btrfs_trans_handle *trans,
                current->journal_info = NULL;
 
        kmem_cache_free(btrfs_trans_handle_cachep, trans);
-
-       spin_lock(&root->fs_info->trans_lock);
-       root->fs_info->trans_no_join = 0;
-       spin_unlock(&root->fs_info->trans_lock);
 }
 
 static int btrfs_flush_all_pending_stuffs(struct btrfs_trans_handle *trans,
                                          struct btrfs_root *root)
 {
-       int flush_on_commit = btrfs_test_opt(root, FLUSHONCOMMIT);
-       int snap_pending = 0;
        int ret;
 
-       if (!flush_on_commit) {
-               spin_lock(&root->fs_info->trans_lock);
-               if (!list_empty(&trans->transaction->pending_snapshots))
-                       snap_pending = 1;
-               spin_unlock(&root->fs_info->trans_lock);
-       }
-
-       if (flush_on_commit || snap_pending) {
-               ret = btrfs_start_delalloc_inodes(root, 1);
-               if (ret)
-                       return ret;
-               btrfs_wait_ordered_extents(root, 1);
-       }
-
        ret = btrfs_run_delayed_items(trans, root);
        if (ret)
                return ret;
@@ -1531,23 +1573,25 @@ static int btrfs_flush_all_pending_stuffs(struct btrfs_trans_handle *trans,
        return ret;
 }
 
-/*
- * btrfs_transaction state sequence:
- *    in_commit = 0, blocked = 0  (initial)
- *    in_commit = 1, blocked = 1
- *    blocked = 0
- *    commit_done = 1
- */
+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 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, 1);
+}
+
 int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root)
 {
-       unsigned long joined = 0;
        struct btrfs_transaction *cur_trans = trans->transaction;
        struct btrfs_transaction *prev_trans = NULL;
-       DEFINE_WAIT(wait);
        int ret;
-       int should_grow = 0;
-       unsigned long now = get_seconds();
 
        ret = btrfs_run_ordered_operations(trans, root, 0);
        if (ret) {
@@ -1586,6 +1630,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
         * start sending their work down.
         */
        cur_trans->delayed_refs.flushing = 1;
+       smp_wmb();
 
        if (!list_empty(&trans->new_bgs))
                btrfs_create_pending_block_groups(trans, root);
@@ -1596,9 +1641,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
                return ret;
        }
 
-       spin_lock(&cur_trans->commit_lock);
-       if (cur_trans->in_commit) {
-               spin_unlock(&cur_trans->commit_lock);
+       spin_lock(&root->fs_info->trans_lock);
+       if (cur_trans->state >= TRANS_STATE_COMMIT_START) {
+               spin_unlock(&root->fs_info->trans_lock);
                atomic_inc(&cur_trans->use_count);
                ret = btrfs_end_transaction(trans, root);
 
@@ -1609,16 +1654,13 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
                return ret;
        }
 
-       trans->transaction->in_commit = 1;
-       trans->transaction->blocked = 1;
-       spin_unlock(&cur_trans->commit_lock);
+       cur_trans->state = TRANS_STATE_COMMIT_START;
        wake_up(&root->fs_info->transaction_blocked_wait);
 
-       spin_lock(&root->fs_info->trans_lock);
        if (cur_trans->list.prev != &root->fs_info->trans_list) {
                prev_trans = list_entry(cur_trans->list.prev,
                                        struct btrfs_transaction, list);
-               if (!prev_trans->commit_done) {
+               if (prev_trans->state != TRANS_STATE_COMPLETED) {
                        atomic_inc(&prev_trans->use_count);
                        spin_unlock(&root->fs_info->trans_lock);
 
@@ -1632,42 +1674,32 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
                spin_unlock(&root->fs_info->trans_lock);
        }
 
-       if (!btrfs_test_opt(root, SSD) &&
-           (now < cur_trans->start_time || now - cur_trans->start_time < 1))
-               should_grow = 1;
-
-       do {
-               joined = cur_trans->num_joined;
-
-               WARN_ON(cur_trans != trans->transaction);
-
-               ret = btrfs_flush_all_pending_stuffs(trans, root);
-               if (ret)
-                       goto cleanup_transaction;
+       extwriter_counter_dec(cur_trans, trans->type);
 
-               prepare_to_wait(&cur_trans->writer_wait, &wait,
-                               TASK_UNINTERRUPTIBLE);
+       ret = btrfs_start_delalloc_flush(root->fs_info);
+       if (ret)
+               goto cleanup_transaction;
 
-               if (atomic_read(&cur_trans->num_writers) > 1)
-                       schedule_timeout(MAX_SCHEDULE_TIMEOUT);
-               else if (should_grow)
-                       schedule_timeout(1);
+       ret = btrfs_flush_all_pending_stuffs(trans, root);
+       if (ret)
+               goto cleanup_transaction;
 
-               finish_wait(&cur_trans->writer_wait, &wait);
-       } while (atomic_read(&cur_trans->num_writers) > 1 ||
-                (should_grow && cur_trans->num_joined != joined));
+       wait_event(cur_trans->writer_wait,
+                  extwriter_counter_read(cur_trans) == 0);
 
+       /* some pending stuffs might be added after the previous flush. */
        ret = btrfs_flush_all_pending_stuffs(trans, root);
        if (ret)
                goto cleanup_transaction;
 
+       btrfs_wait_delalloc_flush(root->fs_info);
        /*
         * Ok now we need to make sure to block out any other joins while we
         * commit the transaction.  We could have started a join before setting
-        * no_join so make sure to wait for num_writers to == 1 again.
+        * COMMIT_DOING so make sure to wait for num_writers to == 1 again.
         */
        spin_lock(&root->fs_info->trans_lock);
-       root->fs_info->trans_no_join = 1;
+       cur_trans->state = TRANS_STATE_COMMIT_DOING;
        spin_unlock(&root->fs_info->trans_lock);
        wait_event(cur_trans->writer_wait,
                   atomic_read(&cur_trans->num_writers) == 1);
@@ -1794,10 +1826,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        memcpy(root->fs_info->super_for_commit, root->fs_info->super_copy,
               sizeof(*root->fs_info->super_copy));
 
-       trans->transaction->blocked = 0;
        spin_lock(&root->fs_info->trans_lock);
+       cur_trans->state = TRANS_STATE_UNBLOCKED;
        root->fs_info->running_transaction = NULL;
-       root->fs_info->trans_no_join = 0;
        spin_unlock(&root->fs_info->trans_lock);
        mutex_unlock(&root->fs_info->reloc_mutex);
 
@@ -1825,10 +1856,12 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
 
        btrfs_finish_extent_commit(trans, root);
 
-       cur_trans->commit_done = 1;
-
        root->fs_info->last_trans_committed = cur_trans->transid;
-
+       /*
+        * We needn't acquire the lock here because there is no other task
+        * which can change it.
+        */
+       cur_trans->state = TRANS_STATE_COMPLETED;
        wake_up(&cur_trans->commit_wait);
 
        spin_lock(&root->fs_info->trans_lock);
@@ -1838,7 +1871,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        put_transaction(cur_trans);
        put_transaction(cur_trans);
 
-       if (trans->type < TRANS_JOIN_NOLOCK)
+       if (trans->type & __TRANS_FREEZABLE)
                sb_end_intwrite(root->fs_info->sb);
 
        trace_btrfs_transaction_commit(root);
@@ -1885,11 +1918,6 @@ int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root)
        int ret;
        struct btrfs_fs_info *fs_info = root->fs_info;
 
-       if (fs_info->sb->s_flags & MS_RDONLY) {
-               pr_debug("btrfs: cleaner called for RO fs!\n");
-               return 0;
-       }
-
        spin_lock(&fs_info->trans_lock);
        if (list_empty(&fs_info->dead_roots)) {
                spin_unlock(&fs_info->trans_lock);
index 24c9733..005b037 100644 (file)
 #include "delayed-ref.h"
 #include "ctree.h"
 
+enum btrfs_trans_state {
+       TRANS_STATE_RUNNING             = 0,
+       TRANS_STATE_BLOCKED             = 1,
+       TRANS_STATE_COMMIT_START        = 2,
+       TRANS_STATE_COMMIT_DOING        = 3,
+       TRANS_STATE_UNBLOCKED           = 4,
+       TRANS_STATE_COMPLETED           = 5,
+       TRANS_STATE_MAX                 = 6,
+};
+
 struct btrfs_transaction {
        u64 transid;
+       /*
+        * total external writers(USERSPACE/START/ATTACH) in this
+        * transaction, it must be zero before the transaction is
+        * being committed
+        */
+       atomic_t num_extwriters;
        /*
         * total writers in this transaction, it must be zero before the
         * transaction can end
@@ -31,12 +47,8 @@ struct btrfs_transaction {
        atomic_t num_writers;
        atomic_t use_count;
 
-       unsigned long num_joined;
-
-       spinlock_t commit_lock;
-       int in_commit;
-       int commit_done;
-       int blocked;
+       /* Be protected by fs_info->trans_lock when we want to change it. */
+       enum btrfs_trans_state state;
        struct list_head list;
        struct extent_io_tree dirty_pages;
        unsigned long start_time;
@@ -44,17 +56,27 @@ struct btrfs_transaction {
        wait_queue_head_t commit_wait;
        struct list_head pending_snapshots;
        struct list_head ordered_operations;
+       struct list_head pending_chunks;
        struct btrfs_delayed_ref_root delayed_refs;
        int aborted;
 };
 
-enum btrfs_trans_type {
-       TRANS_START,
-       TRANS_JOIN,
-       TRANS_USERSPACE,
-       TRANS_JOIN_NOLOCK,
-       TRANS_ATTACH,
-};
+#define __TRANS_FREEZABLE      (1U << 0)
+
+#define __TRANS_USERSPACE      (1U << 8)
+#define __TRANS_START          (1U << 9)
+#define __TRANS_ATTACH         (1U << 10)
+#define __TRANS_JOIN           (1U << 11)
+#define __TRANS_JOIN_NOLOCK    (1U << 12)
+
+#define TRANS_USERSPACE                (__TRANS_USERSPACE | __TRANS_FREEZABLE)
+#define TRANS_START            (__TRANS_START | __TRANS_FREEZABLE)
+#define TRANS_ATTACH           (__TRANS_ATTACH)
+#define TRANS_JOIN             (__TRANS_JOIN | __TRANS_FREEZABLE)
+#define TRANS_JOIN_NOLOCK      (__TRANS_JOIN_NOLOCK)
+
+#define TRANS_EXTWRITERS       (__TRANS_USERSPACE | __TRANS_START |    \
+                                __TRANS_ATTACH)
 
 struct btrfs_trans_handle {
        u64 transid;
@@ -70,7 +92,7 @@ struct btrfs_trans_handle {
        short aborted;
        short adding_csums;
        bool allocating_chunk;
-       enum btrfs_trans_type type;
+       unsigned int type;
        /*
         * this root is only needed to validate that the root passed to
         * start_transaction is the same as the one passed to end_transaction.
index c276ac9..2c67914 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/blkdev.h>
 #include <linux/list_sort.h>
 #include "ctree.h"
 #include "transaction.h"
@@ -279,11 +280,23 @@ static int process_one_buffer(struct btrfs_root *log,
 {
        int ret = 0;
 
+       /*
+        * If this fs is mixed then we need to be able to process the leaves to
+        * pin down any logged extents, so we have to read the block.
+        */
+       if (btrfs_fs_incompat(log->fs_info, MIXED_GROUPS)) {
+               ret = btrfs_read_buffer(eb, gen);
+               if (ret)
+                       return ret;
+       }
+
        if (wc->pin)
                ret = btrfs_pin_extent_for_log_replay(log->fs_info->extent_root,
                                                      eb->start, eb->len);
 
        if (!ret && btrfs_buffer_uptodate(eb, gen, 0)) {
+               if (wc->pin && btrfs_header_level(eb) == 0)
+                       ret = btrfs_exclude_logged_extents(log, eb);
                if (wc->write)
                        btrfs_write_tree_block(eb);
                if (wc->wait)
@@ -2016,13 +2029,8 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
                                             eb, i, &key);
                        if (ret)
                                break;
-               } else if (key.type == BTRFS_INODE_REF_KEY) {
-                       ret = add_inode_ref(wc->trans, root, log, path,
-                                           eb, i, &key);
-                       if (ret && ret != -ENOENT)
-                               break;
-                       ret = 0;
-               } else if (key.type == BTRFS_INODE_EXTREF_KEY) {
+               } else if (key.type == BTRFS_INODE_REF_KEY ||
+                          key.type == BTRFS_INODE_EXTREF_KEY) {
                        ret = add_inode_ref(wc->trans, root, log, path,
                                            eb, i, &key);
                        if (ret && ret != -ENOENT)
@@ -2358,6 +2366,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
        struct btrfs_root *log = root->log_root;
        struct btrfs_root *log_root_tree = root->fs_info->log_root_tree;
        unsigned long log_transid = 0;
+       struct blk_plug plug;
 
        mutex_lock(&root->log_mutex);
        log_transid = root->log_transid;
@@ -2401,8 +2410,10 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
        /* we start IO on  all the marked extents here, but we don't actually
         * wait for them until later.
         */
+       blk_start_plug(&plug);
        ret = btrfs_write_marked_extents(log, &log->dirty_log_pages, mark);
        if (ret) {
+               blk_finish_plug(&plug);
                btrfs_abort_transaction(trans, root, ret);
                btrfs_free_logged_extents(log, log_transid);
                mutex_unlock(&root->log_mutex);
@@ -2437,6 +2448,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
        }
 
        if (ret) {
+               blk_finish_plug(&plug);
                if (ret != -ENOSPC) {
                        btrfs_abort_transaction(trans, root, ret);
                        mutex_unlock(&log_root_tree->log_mutex);
@@ -2452,6 +2464,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
 
        index2 = log_root_tree->log_transid % 2;
        if (atomic_read(&log_root_tree->log_commit[index2])) {
+               blk_finish_plug(&plug);
                btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
                wait_log_commit(trans, log_root_tree,
                                log_root_tree->log_transid);
@@ -2474,6 +2487,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
         * check the full commit flag again
         */
        if (root->fs_info->last_trans_log_full_commit == trans->transid) {
+               blk_finish_plug(&plug);
                btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
                btrfs_free_logged_extents(log, log_transid);
                mutex_unlock(&log_root_tree->log_mutex);
@@ -2481,9 +2495,10 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
                goto out_wake_log_root;
        }
 
-       ret = btrfs_write_and_wait_marked_extents(log_root_tree,
-                               &log_root_tree->dirty_log_pages,
-                               EXTENT_DIRTY | EXTENT_NEW);
+       ret = btrfs_write_marked_extents(log_root_tree,
+                                        &log_root_tree->dirty_log_pages,
+                                        EXTENT_DIRTY | EXTENT_NEW);
+       blk_finish_plug(&plug);
        if (ret) {
                btrfs_abort_transaction(trans, root, ret);
                btrfs_free_logged_extents(log, log_transid);
@@ -2491,6 +2506,9 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
                goto out_wake_log_root;
        }
        btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
+       btrfs_wait_marked_extents(log_root_tree,
+                                 &log_root_tree->dirty_log_pages,
+                                 EXTENT_NEW | EXTENT_DIRTY);
        btrfs_wait_logged_extents(log, log_transid);
 
        btrfs_set_super_log_root(root->fs_info->super_for_commit,
@@ -4016,8 +4034,7 @@ again:
                if (found_key.objectid != BTRFS_TREE_LOG_OBJECTID)
                        break;
 
-               log = btrfs_read_fs_root_no_radix(log_root_tree,
-                                                 &found_key);
+               log = btrfs_read_fs_root(log_root_tree, &found_key);
                if (IS_ERR(log)) {
                        ret = PTR_ERR(log);
                        btrfs_error(fs_info, ret,
index 7b417e2..b0a523b 100644 (file)
@@ -205,6 +205,10 @@ int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux,
                u64 new_alloced = ulist->nodes_alloced + 128;
                struct ulist_node *new_nodes;
                void *old = NULL;
+               int i;
+
+               for (i = 0; i < ulist->nnodes; i++)
+                       rb_erase(&ulist->nodes[i].rb_node, &ulist->root);
 
                /*
                 * if nodes_alloced == ULIST_SIZE no memory has been allocated
@@ -224,6 +228,17 @@ int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux,
 
                ulist->nodes = new_nodes;
                ulist->nodes_alloced = new_alloced;
+
+               /*
+                * krealloc actually uses memcpy, which does not copy rb_node
+                * pointers, so we have to do it ourselves.  Otherwise we may
+                * be bitten by crashes.
+                */
+               for (i = 0; i < ulist->nnodes; i++) {
+                       ret = ulist_rbtree_insert(ulist, &ulist->nodes[i]);
+                       if (ret < 0)
+                               return ret;
+               }
        }
        ulist->nodes[ulist->nnodes].val = val;
        ulist->nodes[ulist->nnodes].aux = aux;
diff --git a/fs/btrfs/version.h b/fs/btrfs/version.h
deleted file mode 100644 (file)
index 9bf3946..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifndef __BTRFS_VERSION_H
-#define __BTRFS_VERSION_H
-#define BTRFS_BUILD_VERSION "Btrfs"
-#endif
index 8bffb91..78b8717 100644 (file)
@@ -982,6 +982,35 @@ out:
        return ret;
 }
 
+static int contains_pending_extent(struct btrfs_trans_handle *trans,
+                                  struct btrfs_device *device,
+                                  u64 *start, u64 len)
+{
+       struct extent_map *em;
+       int ret = 0;
+
+       list_for_each_entry(em, &trans->transaction->pending_chunks, list) {
+               struct map_lookup *map;
+               int i;
+
+               map = (struct map_lookup *)em->bdev;
+               for (i = 0; i < map->num_stripes; i++) {
+                       if (map->stripes[i].dev != device)
+                               continue;
+                       if (map->stripes[i].physical >= *start + len ||
+                           map->stripes[i].physical + em->orig_block_len <=
+                           *start)
+                               continue;
+                       *start = map->stripes[i].physical +
+                               em->orig_block_len;
+                       ret = 1;
+               }
+       }
+
+       return ret;
+}
+
+
 /*
  * find_free_dev_extent - find free space in the specified device
  * @device:    the device which we search the free space in
@@ -1002,7 +1031,8 @@ out:
  * But if we don't find suitable free space, it is used to store the size of
  * the max free space.
  */
-int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,
+int find_free_dev_extent(struct btrfs_trans_handle *trans,
+                        struct btrfs_device *device, u64 num_bytes,
                         u64 *start, u64 *len)
 {
        struct btrfs_key key;
@@ -1026,21 +1056,22 @@ int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,
         */
        search_start = max(root->fs_info->alloc_start, 1024ull * 1024);
 
+       path = btrfs_alloc_path();
+       if (!path)
+               return -ENOMEM;
+again:
        max_hole_start = search_start;
        max_hole_size = 0;
        hole_size = 0;
 
        if (search_start >= search_end || device->is_tgtdev_for_dev_replace) {
                ret = -ENOSPC;
-               goto error;
+               goto out;
        }
 
-       path = btrfs_alloc_path();
-       if (!path) {
-               ret = -ENOMEM;
-               goto error;
-       }
        path->reada = 2;
+       path->search_commit_root = 1;
+       path->skip_locking = 1;
 
        key.objectid = device->devid;
        key.offset = search_start;
@@ -1081,6 +1112,15 @@ int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,
                if (key.offset > search_start) {
                        hole_size = key.offset - search_start;
 
+                       /*
+                        * Have to check before we set max_hole_start, otherwise
+                        * we could end up sending back this offset anyway.
+                        */
+                       if (contains_pending_extent(trans, device,
+                                                   &search_start,
+                                                   hole_size))
+                               hole_size = 0;
+
                        if (hole_size > max_hole_size) {
                                max_hole_start = search_start;
                                max_hole_size = hole_size;
@@ -1124,6 +1164,11 @@ next:
                max_hole_size = hole_size;
        }
 
+       if (contains_pending_extent(trans, device, &search_start, hole_size)) {
+               btrfs_release_path(path);
+               goto again;
+       }
+
        /* See above. */
        if (hole_size < num_bytes)
                ret = -ENOSPC;
@@ -1132,7 +1177,6 @@ next:
 
 out:
        btrfs_free_path(path);
-error:
        *start = max_hole_start;
        if (len)
                *len = max_hole_size;
@@ -1244,47 +1288,22 @@ out:
        return ret;
 }
 
-static noinline int find_next_chunk(struct btrfs_root *root,
-                                   u64 objectid, u64 *offset)
+static u64 find_next_chunk(struct btrfs_fs_info *fs_info)
 {
-       struct btrfs_path *path;
-       int ret;
-       struct btrfs_key key;
-       struct btrfs_chunk *chunk;
-       struct btrfs_key found_key;
-
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
-
-       key.objectid = objectid;
-       key.offset = (u64)-1;
-       key.type = BTRFS_CHUNK_ITEM_KEY;
-
-       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
-       if (ret < 0)
-               goto error;
-
-       BUG_ON(ret == 0); /* Corruption */
+       struct extent_map_tree *em_tree;
+       struct extent_map *em;
+       struct rb_node *n;
+       u64 ret = 0;
 
-       ret = btrfs_previous_item(root, path, 0, BTRFS_CHUNK_ITEM_KEY);
-       if (ret) {
-               *offset = 0;
-       } else {
-               btrfs_item_key_to_cpu(path->nodes[0], &found_key,
-                                     path->slots[0]);
-               if (found_key.objectid != objectid)
-                       *offset = 0;
-               else {
-                       chunk = btrfs_item_ptr(path->nodes[0], path->slots[0],
-                                              struct btrfs_chunk);
-                       *offset = found_key.offset +
-                               btrfs_chunk_length(path->nodes[0], chunk);
-               }
+       em_tree = &fs_info->mapping_tree.map_tree;
+       read_lock(&em_tree->lock);
+       n = rb_last(&em_tree->map);
+       if (n) {
+               em = rb_entry(n, struct extent_map, rb_node);
+               ret = em->start + em->len;
        }
-       ret = 0;
-error:
-       btrfs_free_path(path);
+       read_unlock(&em_tree->lock);
+
        return ret;
 }
 
@@ -1462,31 +1481,23 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
        btrfs_dev_replace_unlock(&root->fs_info->dev_replace);
 
        if ((all_avail & BTRFS_BLOCK_GROUP_RAID10) && num_devices <= 4) {
-               printk(KERN_ERR "btrfs: unable to go below four devices "
-                      "on raid10\n");
-               ret = -EINVAL;
+               ret = BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET;
                goto out;
        }
 
        if ((all_avail & BTRFS_BLOCK_GROUP_RAID1) && num_devices <= 2) {
-               printk(KERN_ERR "btrfs: unable to go below two "
-                      "devices on raid1\n");
-               ret = -EINVAL;
+               ret = BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET;
                goto out;
        }
 
        if ((all_avail & BTRFS_BLOCK_GROUP_RAID5) &&
            root->fs_info->fs_devices->rw_devices <= 2) {
-               printk(KERN_ERR "btrfs: unable to go below two "
-                      "devices on raid5\n");
-               ret = -EINVAL;
+               ret = BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET;
                goto out;
        }
        if ((all_avail & BTRFS_BLOCK_GROUP_RAID6) &&
            root->fs_info->fs_devices->rw_devices <= 3) {
-               printk(KERN_ERR "btrfs: unable to go below three "
-                      "devices on raid6\n");
-               ret = -EINVAL;
+               ret = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET;
                goto out;
        }
 
@@ -1512,8 +1523,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
                bh = NULL;
                disk_super = NULL;
                if (!device) {
-                       printk(KERN_ERR "btrfs: no missing devices found to "
-                              "remove\n");
+                       ret = BTRFS_ERROR_DEV_MISSING_NOT_FOUND;
                        goto out;
                }
        } else {
@@ -1535,15 +1545,12 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
        }
 
        if (device->is_tgtdev_for_dev_replace) {
-               pr_err("btrfs: unable to remove the dev_replace target dev\n");
-               ret = -EINVAL;
+               ret = BTRFS_ERROR_DEV_TGT_REPLACE;
                goto error_brelse;
        }
 
        if (device->writeable && root->fs_info->fs_devices->rw_devices == 1) {
-               printk(KERN_ERR "btrfs: unable to remove the only writeable "
-                      "device\n");
-               ret = -EINVAL;
+               ret = BTRFS_ERROR_DEV_ONLY_WRITABLE;
                goto error_brelse;
        }
 
@@ -3295,10 +3302,7 @@ int btrfs_resume_balance_async(struct btrfs_fs_info *fs_info)
        }
 
        tsk = kthread_run(balance_kthread, fs_info, "btrfs-balance");
-       if (IS_ERR(tsk))
-               return PTR_ERR(tsk);
-
-       return 0;
+       return PTR_RET(tsk);
 }
 
 int btrfs_recover_balance(struct btrfs_fs_info *fs_info)
@@ -3681,10 +3685,8 @@ static void check_raid56_incompat_flag(struct btrfs_fs_info *info, u64 type)
 }
 
 static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
-                              struct btrfs_root *extent_root,
-                              struct map_lookup **map_ret,
-                              u64 *num_bytes_out, u64 *stripe_size_out,
-                              u64 start, u64 type)
+                              struct btrfs_root *extent_root, u64 start,
+                              u64 type)
 {
        struct btrfs_fs_info *info = extent_root->fs_info;
        struct btrfs_fs_devices *fs_devices = info->fs_devices;
@@ -3791,7 +3793,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
                if (total_avail == 0)
                        continue;
 
-               ret = find_free_dev_extent(device,
+               ret = find_free_dev_extent(trans, device,
                                           max_stripe_size * dev_stripes,
                                           &dev_offset, &max_avail);
                if (ret && ret != -ENOSPC)
@@ -3903,12 +3905,8 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
        map->type = type;
        map->sub_stripes = sub_stripes;
 
-       *map_ret = map;
        num_bytes = stripe_size * data_stripes;
 
-       *stripe_size_out = stripe_size;
-       *num_bytes_out = num_bytes;
-
        trace_btrfs_chunk_alloc(info->chunk_root, map, start, num_bytes);
 
        em = alloc_extent_map();
@@ -3921,38 +3919,26 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
        em->len = num_bytes;
        em->block_start = 0;
        em->block_len = em->len;
+       em->orig_block_len = stripe_size;
 
        em_tree = &extent_root->fs_info->mapping_tree.map_tree;
        write_lock(&em_tree->lock);
        ret = add_extent_mapping(em_tree, em, 0);
+       if (!ret) {
+               list_add_tail(&em->list, &trans->transaction->pending_chunks);
+               atomic_inc(&em->refs);
+       }
        write_unlock(&em_tree->lock);
        if (ret) {
                free_extent_map(em);
                goto error;
        }
 
-       for (i = 0; i < map->num_stripes; ++i) {
-               struct btrfs_device *device;
-               u64 dev_offset;
-
-               device = map->stripes[i].dev;
-               dev_offset = map->stripes[i].physical;
-
-               ret = btrfs_alloc_dev_extent(trans, device,
-                               info->chunk_root->root_key.objectid,
-                               BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-                               start, dev_offset, stripe_size);
-               if (ret)
-                       goto error_dev_extent;
-       }
-
        ret = btrfs_make_block_group(trans, extent_root, 0, type,
                                     BTRFS_FIRST_CHUNK_TREE_OBJECTID,
                                     start, num_bytes);
-       if (ret) {
-               i = map->num_stripes - 1;
-               goto error_dev_extent;
-       }
+       if (ret)
+               goto error_del_extent;
 
        free_extent_map(em);
        check_raid56_incompat_flag(extent_root->fs_info, type);
@@ -3960,18 +3946,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
        kfree(devices_info);
        return 0;
 
-error_dev_extent:
-       for (; i >= 0; i--) {
-               struct btrfs_device *device;
-               int err;
-
-               device = map->stripes[i].dev;
-               err = btrfs_free_dev_extent(trans, device, start);
-               if (err) {
-                       btrfs_abort_transaction(trans, extent_root, err);
-                       break;
-               }
-       }
+error_del_extent:
        write_lock(&em_tree->lock);
        remove_extent_mapping(em_tree, em);
        write_unlock(&em_tree->lock);
@@ -3986,33 +3961,68 @@ error:
        return ret;
 }
 
-static int __finish_chunk_alloc(struct btrfs_trans_handle *trans,
+int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans,
                                struct btrfs_root *extent_root,
-                               struct map_lookup *map, u64 chunk_offset,
-                               u64 chunk_size, u64 stripe_size)
+                               u64 chunk_offset, u64 chunk_size)
 {
-       u64 dev_offset;
        struct btrfs_key key;
        struct btrfs_root *chunk_root = extent_root->fs_info->chunk_root;
        struct btrfs_device *device;
        struct btrfs_chunk *chunk;
        struct btrfs_stripe *stripe;
-       size_t item_size = btrfs_chunk_item_size(map->num_stripes);
-       int index = 0;
+       struct extent_map_tree *em_tree;
+       struct extent_map *em;
+       struct map_lookup *map;
+       size_t item_size;
+       u64 dev_offset;
+       u64 stripe_size;
+       int i = 0;
        int ret;
 
+       em_tree = &extent_root->fs_info->mapping_tree.map_tree;
+       read_lock(&em_tree->lock);
+       em = lookup_extent_mapping(em_tree, chunk_offset, chunk_size);
+       read_unlock(&em_tree->lock);
+
+       if (!em) {
+               btrfs_crit(extent_root->fs_info, "unable to find logical "
+                          "%Lu len %Lu", chunk_offset, chunk_size);
+               return -EINVAL;
+       }
+
+       if (em->start != chunk_offset || em->len != chunk_size) {
+               btrfs_crit(extent_root->fs_info, "found a bad mapping, wanted"
+                         " %Lu-%Lu, found %Lu-%Lu\n", chunk_offset,
+                         chunk_size, em->start, em->len);
+               free_extent_map(em);
+               return -EINVAL;
+       }
+
+       map = (struct map_lookup *)em->bdev;
+       item_size = btrfs_chunk_item_size(map->num_stripes);
+       stripe_size = em->orig_block_len;
+
        chunk = kzalloc(item_size, GFP_NOFS);
-       if (!chunk)
-               return -ENOMEM;
+       if (!chunk) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       for (i = 0; i < map->num_stripes; i++) {
+               device = map->stripes[i].dev;
+               dev_offset = map->stripes[i].physical;
 
-       index = 0;
-       while (index < map->num_stripes) {
-               device = map->stripes[index].dev;
                device->bytes_used += stripe_size;
                ret = btrfs_update_device(trans, device);
                if (ret)
-                       goto out_free;
-               index++;
+                       goto out;
+               ret = btrfs_alloc_dev_extent(trans, device,
+                                            chunk_root->root_key.objectid,
+                                            BTRFS_FIRST_CHUNK_TREE_OBJECTID,
+                                            chunk_offset, dev_offset,
+                                            stripe_size);
+               if (ret)
+                       goto out;
        }
 
        spin_lock(&extent_root->fs_info->free_chunk_lock);
@@ -4020,17 +4030,15 @@ static int __finish_chunk_alloc(struct btrfs_trans_handle *trans,
                                                   map->num_stripes);
        spin_unlock(&extent_root->fs_info->free_chunk_lock);
 
-       index = 0;
        stripe = &chunk->stripe;
-       while (index < map->num_stripes) {
-               device = map->stripes[index].dev;
-               dev_offset = map->stripes[index].physical;
+       for (i = 0; i < map->num_stripes; i++) {
+               device = map->stripes[i].dev;
+               dev_offset = map->stripes[i].physical;
 
                btrfs_set_stack_stripe_devid(stripe, device->devid);
                btrfs_set_stack_stripe_offset(stripe, dev_offset);
                memcpy(stripe->dev_uuid, device->uuid, BTRFS_UUID_SIZE);
                stripe++;
-               index++;
        }
 
        btrfs_set_stack_chunk_length(chunk, chunk_size);
@@ -4048,7 +4056,6 @@ static int __finish_chunk_alloc(struct btrfs_trans_handle *trans,
        key.offset = chunk_offset;
 
        ret = btrfs_insert_item(trans, chunk_root, &key, chunk, item_size);
-
        if (ret == 0 && map->type & BTRFS_BLOCK_GROUP_SYSTEM) {
                /*
                 * TODO: Cleanup of inserted chunk root in case of
@@ -4058,8 +4065,9 @@ static int __finish_chunk_alloc(struct btrfs_trans_handle *trans,
                                             item_size);
        }
 
-out_free:
+out:
        kfree(chunk);
+       free_extent_map(em);
        return ret;
 }
 
@@ -4074,27 +4082,9 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
                      struct btrfs_root *extent_root, u64 type)
 {
        u64 chunk_offset;
-       u64 chunk_size;
-       u64 stripe_size;
-       struct map_lookup *map;
-       struct btrfs_root *chunk_root = extent_root->fs_info->chunk_root;
-       int ret;
-
-       ret = find_next_chunk(chunk_root, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-                             &chunk_offset);
-       if (ret)
-               return ret;
 
-       ret = __btrfs_alloc_chunk(trans, extent_root, &map, &chunk_size,
-                                 &stripe_size, chunk_offset, type);
-       if (ret)
-               return ret;
-
-       ret = __finish_chunk_alloc(trans, extent_root, map, chunk_offset,
-                                  chunk_size, stripe_size);
-       if (ret)
-               return ret;
-       return 0;
+       chunk_offset = find_next_chunk(extent_root->fs_info);
+       return __btrfs_alloc_chunk(trans, extent_root, chunk_offset, type);
 }
 
 static noinline int init_first_rw_device(struct btrfs_trans_handle *trans,
@@ -4103,66 +4093,31 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans,
 {
        u64 chunk_offset;
        u64 sys_chunk_offset;
-       u64 chunk_size;
-       u64 sys_chunk_size;
-       u64 stripe_size;
-       u64 sys_stripe_size;
        u64 alloc_profile;
-       struct map_lookup *map;
-       struct map_lookup *sys_map;
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct btrfs_root *extent_root = fs_info->extent_root;
        int ret;
 
-       ret = find_next_chunk(fs_info->chunk_root,
-                             BTRFS_FIRST_CHUNK_TREE_OBJECTID, &chunk_offset);
-       if (ret)
-               return ret;
-
+       chunk_offset = find_next_chunk(fs_info);
        alloc_profile = btrfs_get_alloc_profile(extent_root, 0);
-       ret = __btrfs_alloc_chunk(trans, extent_root, &map, &chunk_size,
-                                 &stripe_size, chunk_offset, alloc_profile);
+       ret = __btrfs_alloc_chunk(trans, extent_root, chunk_offset,
+                                 alloc_profile);
        if (ret)
                return ret;
 
-       sys_chunk_offset = chunk_offset + chunk_size;
-
+       sys_chunk_offset = find_next_chunk(root->fs_info);
        alloc_profile = btrfs_get_alloc_profile(fs_info->chunk_root, 0);
-       ret = __btrfs_alloc_chunk(trans, extent_root, &sys_map,
-                                 &sys_chunk_size, &sys_stripe_size,
-                                 sys_chunk_offset, alloc_profile);
+       ret = __btrfs_alloc_chunk(trans, extent_root, sys_chunk_offset,
+                                 alloc_profile);
        if (ret) {
                btrfs_abort_transaction(trans, root, ret);
                goto out;
        }
 
        ret = btrfs_add_device(trans, fs_info->chunk_root, device);
-       if (ret) {
-               btrfs_abort_transaction(trans, root, ret);
-               goto out;
-       }
-
-       /*
-        * Modifying chunk tree needs allocating new blocks from both
-        * system block group and metadata block group. So we only can
-        * do operations require modifying the chunk tree after both
-        * block groups were created.
-        */
-       ret = __finish_chunk_alloc(trans, extent_root, map, chunk_offset,
-                                  chunk_size, stripe_size);
-       if (ret) {
-               btrfs_abort_transaction(trans, root, ret);
-               goto out;
-       }
-
-       ret = __finish_chunk_alloc(trans, extent_root, sys_map,
-                                  sys_chunk_offset, sys_chunk_size,
-                                  sys_stripe_size);
        if (ret)
                btrfs_abort_transaction(trans, root, ret);
-
 out:
-
        return ret;
 }
 
@@ -4435,9 +4390,6 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw,
        map = (struct map_lookup *)em->bdev;
        offset = logical - em->start;
 
-       if (mirror_num > map->num_stripes)
-               mirror_num = 0;
-
        stripe_len = map->stripe_len;
        stripe_nr = offset;
        /*
@@ -5367,7 +5319,6 @@ static struct btrfs_device *add_missing_dev(struct btrfs_root *root,
                return NULL;
        list_add(&device->dev_list,
                 &fs_devices->devices);
-       device->dev_root = root->fs_info->dev_root;
        device->devid = devid;
        device->work.func = pending_bios_fn;
        device->fs_devices = fs_devices;
@@ -5593,7 +5544,6 @@ static int read_one_dev(struct btrfs_root *root,
        }
 
        fill_device_from_item(leaf, dev_item, device);
-       device->dev_root = root->fs_info->dev_root;
        device->in_fs_metadata = 1;
        if (device->writeable && !device->is_tgtdev_for_dev_replace) {
                device->fs_devices->total_rw_bytes += device->total_bytes;
@@ -5751,6 +5701,17 @@ error:
        return ret;
 }
 
+void btrfs_init_devices_late(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+       struct btrfs_device *device;
+
+       mutex_lock(&fs_devices->device_list_mutex);
+       list_for_each_entry(device, &fs_devices->devices, dev_list)
+               device->dev_root = fs_info->dev_root;
+       mutex_unlock(&fs_devices->device_list_mutex);
+}
+
 static void __btrfs_reset_dev_stats(struct btrfs_device *dev)
 {
        int i;
index f6247e2..8670558 100644 (file)
@@ -316,11 +316,13 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info);
 int btrfs_pause_balance(struct btrfs_fs_info *fs_info);
 int btrfs_cancel_balance(struct btrfs_fs_info *fs_info);
 int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);
-int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,
+int find_free_dev_extent(struct btrfs_trans_handle *trans,
+                        struct btrfs_device *device, u64 num_bytes,
                         u64 *start, u64 *max_avail);
 void btrfs_dev_stat_inc_and_print(struct btrfs_device *dev, int index);
 int btrfs_get_dev_stats(struct btrfs_root *root,
                        struct btrfs_ioctl_get_dev_stats *stats);
+void btrfs_init_devices_late(struct btrfs_fs_info *fs_info);
 int btrfs_init_dev_stats(struct btrfs_fs_info *fs_info);
 int btrfs_run_dev_stats(struct btrfs_trans_handle *trans,
                        struct btrfs_fs_info *fs_info);
@@ -336,6 +338,9 @@ int btrfs_is_parity_mirror(struct btrfs_mapping_tree *map_tree,
 unsigned long btrfs_full_stripe_len(struct btrfs_root *root,
                                    struct btrfs_mapping_tree *map_tree,
                                    u64 logical);
+int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans,
+                               struct btrfs_root *extent_root,
+                               u64 chunk_offset, u64 chunk_size);
 static inline void btrfs_dev_stat_inc(struct btrfs_device *dev,
                                      int index)
 {
index 38b5c1b..5318a3b 100644 (file)
@@ -439,13 +439,12 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
        struct ceph_inode_info *ci;
        struct ceph_fs_client *fsc;
        struct ceph_osd_client *osdc;
-       loff_t page_off = page_offset(page);
-       int len = PAGE_CACHE_SIZE;
-       loff_t i_size;
-       int err = 0;
        struct ceph_snap_context *snapc, *oldest;
-       u64 snap_size = 0;
+       loff_t page_off = page_offset(page);
        long writeback_stat;
+       u64 truncate_size, snap_size = 0;
+       u32 truncate_seq;
+       int err = 0, len = PAGE_CACHE_SIZE;
 
        dout("writepage %p idx %lu\n", page, page->index);
 
@@ -475,13 +474,20 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
        }
        ceph_put_snap_context(oldest);
 
+       spin_lock(&ci->i_ceph_lock);
+       truncate_seq = ci->i_truncate_seq;
+       truncate_size = ci->i_truncate_size;
+       if (!snap_size)
+               snap_size = i_size_read(inode);
+       spin_unlock(&ci->i_ceph_lock);
+
        /* is this a partial page at end of file? */
-       if (snap_size)
-               i_size = snap_size;
-       else
-               i_size = i_size_read(inode);
-       if (i_size < page_off + len)
-               len = i_size - page_off;
+       if (page_off >= snap_size) {
+               dout("%p page eof %llu\n", page, snap_size);
+               goto out;
+       }
+       if (snap_size < page_off + len)
+               len = snap_size - page_off;
 
        dout("writepage %p page %p index %lu on %llu~%u snapc %p\n",
             inode, page, page->index, page_off, len, snapc);
@@ -495,7 +501,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
        err = ceph_osdc_writepages(osdc, ceph_vino(inode),
                                   &ci->i_layout, snapc,
                                   page_off, len,
-                                  ci->i_truncate_seq, ci->i_truncate_size,
+                                  truncate_seq, truncate_size,
                                   &inode->i_mtime, &page, 1);
        if (err < 0) {
                dout("writepage setting page/mapping error %d %p\n", err, page);
@@ -632,25 +638,6 @@ static void writepages_finish(struct ceph_osd_request *req,
        ceph_osdc_put_request(req);
 }
 
-static struct ceph_osd_request *
-ceph_writepages_osd_request(struct inode *inode, u64 offset, u64 *len,
-                               struct ceph_snap_context *snapc, int num_ops)
-{
-       struct ceph_fs_client *fsc;
-       struct ceph_inode_info *ci;
-       struct ceph_vino vino;
-
-       fsc = ceph_inode_to_client(inode);
-       ci = ceph_inode(inode);
-       vino = ceph_vino(inode);
-       /* BUG_ON(vino.snap != CEPH_NOSNAP); */
-
-       return ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout,
-                       vino, offset, len, num_ops, CEPH_OSD_OP_WRITE,
-                       CEPH_OSD_FLAG_WRITE|CEPH_OSD_FLAG_ONDISK,
-                       snapc, ci->i_truncate_seq, ci->i_truncate_size, true);
-}
-
 /*
  * initiate async writeback
  */
@@ -659,7 +646,8 @@ static int ceph_writepages_start(struct address_space *mapping,
 {
        struct inode *inode = mapping->host;
        struct ceph_inode_info *ci = ceph_inode(inode);
-       struct ceph_fs_client *fsc;
+       struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
+       struct ceph_vino vino = ceph_vino(inode);
        pgoff_t index, start, end;
        int range_whole = 0;
        int should_loop = 1;
@@ -671,22 +659,22 @@ static int ceph_writepages_start(struct address_space *mapping,
        unsigned wsize = 1 << inode->i_blkbits;
        struct ceph_osd_request *req = NULL;
        int do_sync;
-       u64 snap_size;
+       u64 truncate_size, snap_size;
+       u32 truncate_seq;
 
        /*
         * Include a 'sync' in the OSD request if this is a data
         * integrity write (e.g., O_SYNC write or fsync()), or if our
         * cap is being revoked.
         */
-       do_sync = wbc->sync_mode == WB_SYNC_ALL;
-       if (ceph_caps_revoking(ci, CEPH_CAP_FILE_BUFFER))
+       if ((wbc->sync_mode == WB_SYNC_ALL) ||
+               ceph_caps_revoking(ci, CEPH_CAP_FILE_BUFFER))
                do_sync = 1;
        dout("writepages_start %p dosync=%d (mode=%s)\n",
             inode, do_sync,
             wbc->sync_mode == WB_SYNC_NONE ? "NONE" :
             (wbc->sync_mode == WB_SYNC_ALL ? "ALL" : "HOLD"));
 
-       fsc = ceph_inode_to_client(inode);
        if (fsc->mount_state == CEPH_MOUNT_SHUTDOWN) {
                pr_warning("writepage_start %p on forced umount\n", inode);
                return -EIO; /* we're in a forced umount, don't write! */
@@ -729,6 +717,14 @@ retry:
                snap_size = i_size_read(inode);
        dout(" oldest snapc is %p seq %lld (%d snaps)\n",
             snapc, snapc->seq, snapc->num_snaps);
+
+       spin_lock(&ci->i_ceph_lock);
+       truncate_seq = ci->i_truncate_seq;
+       truncate_size = ci->i_truncate_size;
+       if (!snap_size)
+               snap_size = i_size_read(inode);
+       spin_unlock(&ci->i_ceph_lock);
+
        if (last_snapc && snapc != last_snapc) {
                /* if we switched to a newer snapc, restart our scan at the
                 * start of the original file range. */
@@ -740,7 +736,6 @@ retry:
 
        while (!done && index <= end) {
                int num_ops = do_sync ? 2 : 1;
-               struct ceph_vino vino;
                unsigned i;
                int first;
                pgoff_t next;
@@ -834,17 +829,18 @@ get_more_pages:
                         * that it will use.
                         */
                        if (locked_pages == 0) {
-                               size_t size;
-
                                BUG_ON(pages);
-
                                /* prepare async write request */
                                offset = (u64)page_offset(page);
                                len = wsize;
-                               req = ceph_writepages_osd_request(inode,
-                                                       offset, &len, snapc,
-                                                       num_ops);
-
+                               req = ceph_osdc_new_request(&fsc->client->osdc,
+                                                       &ci->i_layout, vino,
+                                                       offset, &len, num_ops,
+                                                       CEPH_OSD_OP_WRITE,
+                                                       CEPH_OSD_FLAG_WRITE |
+                                                       CEPH_OSD_FLAG_ONDISK,
+                                                       snapc, truncate_seq,
+                                                       truncate_size, true);
                                if (IS_ERR(req)) {
                                        rc = PTR_ERR(req);
                                        unlock_page(page);
@@ -855,8 +851,8 @@ get_more_pages:
                                req->r_inode = inode;
 
                                max_pages = calc_pages_for(0, (u64)len);
-                               size = max_pages * sizeof (*pages);
-                               pages = kmalloc(size, GFP_NOFS);
+                               pages = kmalloc(max_pages * sizeof (*pages),
+                                               GFP_NOFS);
                                if (!pages) {
                                        pool = fsc->wb_pagevec_pool;
                                        pages = mempool_alloc(pool, GFP_NOFS);
index da0f9b8..25442b4 100644 (file)
@@ -147,7 +147,7 @@ void ceph_adjust_min_caps(struct ceph_mds_client *mdsc, int delta)
        spin_unlock(&mdsc->caps_list_lock);
 }
 
-int ceph_reserve_caps(struct ceph_mds_client *mdsc,
+void ceph_reserve_caps(struct ceph_mds_client *mdsc,
                      struct ceph_cap_reservation *ctx, int need)
 {
        int i;
@@ -155,7 +155,6 @@ int ceph_reserve_caps(struct ceph_mds_client *mdsc,
        int have;
        int alloc = 0;
        LIST_HEAD(newcaps);
-       int ret = 0;
 
        dout("reserve caps ctx=%p need=%d\n", ctx, need);
 
@@ -174,14 +173,15 @@ int ceph_reserve_caps(struct ceph_mds_client *mdsc,
 
        for (i = have; i < need; i++) {
                cap = kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS);
-               if (!cap) {
-                       ret = -ENOMEM;
-                       goto out_alloc_count;
-               }
+               if (!cap)
+                       break;
                list_add(&cap->caps_item, &newcaps);
                alloc++;
        }
-       BUG_ON(have + alloc != need);
+       /* we didn't manage to reserve as much as we needed */
+       if (have + alloc != need)
+               pr_warn("reserve caps ctx=%p ENOMEM need=%d got=%d\n",
+                       ctx, need, have + alloc);
 
        spin_lock(&mdsc->caps_list_lock);
        mdsc->caps_total_count += alloc;
@@ -197,13 +197,6 @@ int ceph_reserve_caps(struct ceph_mds_client *mdsc,
        dout("reserve caps ctx=%p %d = %d used + %d resv + %d avail\n",
             ctx, mdsc->caps_total_count, mdsc->caps_use_count,
             mdsc->caps_reserve_count, mdsc->caps_avail_count);
-       return 0;
-
-out_alloc_count:
-       /* we didn't manage to reserve as much as we needed */
-       pr_warning("reserve caps ctx=%p ENOMEM need=%d got=%d\n",
-                  ctx, need, have);
-       return ret;
 }
 
 int ceph_unreserve_caps(struct ceph_mds_client *mdsc,
@@ -612,9 +605,11 @@ retry:
                __cap_delay_requeue(mdsc, ci);
        }
 
-       if (flags & CEPH_CAP_FLAG_AUTH)
-               ci->i_auth_cap = cap;
-       else if (ci->i_auth_cap == cap) {
+       if (flags & CEPH_CAP_FLAG_AUTH) {
+               if (ci->i_auth_cap == NULL ||
+                   ceph_seq_cmp(ci->i_auth_cap->mseq, mseq) < 0)
+                       ci->i_auth_cap = cap;
+       } else if (ci->i_auth_cap == cap) {
                ci->i_auth_cap = NULL;
                spin_lock(&mdsc->cap_dirty_lock);
                if (!list_empty(&ci->i_dirty_item)) {
@@ -695,6 +690,15 @@ int __ceph_caps_issued(struct ceph_inode_info *ci, int *implemented)
                if (implemented)
                        *implemented |= cap->implemented;
        }
+       /*
+        * exclude caps issued by non-auth MDS, but are been revoking
+        * by the auth MDS. The non-auth MDS should be revoking/exporting
+        * these caps, but the message is delayed.
+        */
+       if (ci->i_auth_cap) {
+               cap = ci->i_auth_cap;
+               have &= ~cap->implemented | cap->issued;
+       }
        return have;
 }
 
@@ -802,22 +806,28 @@ int __ceph_caps_issued_mask(struct ceph_inode_info *ci, int mask, int touch)
 /*
  * Return true if mask caps are currently being revoked by an MDS.
  */
-int ceph_caps_revoking(struct ceph_inode_info *ci, int mask)
+int __ceph_caps_revoking_other(struct ceph_inode_info *ci,
+                              struct ceph_cap *ocap, int mask)
 {
-       struct inode *inode = &ci->vfs_inode;
        struct ceph_cap *cap;
        struct rb_node *p;
-       int ret = 0;
 
-       spin_lock(&ci->i_ceph_lock);
        for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) {
                cap = rb_entry(p, struct ceph_cap, ci_node);
-               if (__cap_is_valid(cap) &&
-                   (cap->implemented & ~cap->issued & mask)) {
-                       ret = 1;
-                       break;
-               }
+               if (cap != ocap && __cap_is_valid(cap) &&
+                   (cap->implemented & ~cap->issued & mask))
+                       return 1;
        }
+       return 0;
+}
+
+int ceph_caps_revoking(struct ceph_inode_info *ci, int mask)
+{
+       struct inode *inode = &ci->vfs_inode;
+       int ret;
+
+       spin_lock(&ci->i_ceph_lock);
+       ret = __ceph_caps_revoking_other(ci, NULL, mask);
        spin_unlock(&ci->i_ceph_lock);
        dout("ceph_caps_revoking %p %s = %d\n", inode,
             ceph_cap_string(mask), ret);
@@ -1980,8 +1990,15 @@ static void kick_flushing_inode_caps(struct ceph_mds_client *mdsc,
        cap = ci->i_auth_cap;
        dout("kick_flushing_inode_caps %p flushing %s flush_seq %lld\n", inode,
             ceph_cap_string(ci->i_flushing_caps), ci->i_cap_flush_seq);
+
        __ceph_flush_snaps(ci, &session, 1);
+
        if (ci->i_flushing_caps) {
+               spin_lock(&mdsc->cap_dirty_lock);
+               list_move_tail(&ci->i_flushing_item,
+                              &cap->session->s_cap_flushing);
+               spin_unlock(&mdsc->cap_dirty_lock);
+
                delayed = __send_cap(mdsc, cap, CEPH_CAP_OP_FLUSH,
                                     __ceph_caps_used(ci),
                                     __ceph_caps_wanted(ci),
@@ -2055,7 +2072,11 @@ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want,
        /* finish pending truncate */
        while (ci->i_truncate_pending) {
                spin_unlock(&ci->i_ceph_lock);
-               __ceph_do_pending_vmtruncate(inode, !(need & CEPH_CAP_FILE_WR));
+               if (!(need & CEPH_CAP_FILE_WR))
+                       mutex_lock(&inode->i_mutex);
+               __ceph_do_pending_vmtruncate(inode);
+               if (!(need & CEPH_CAP_FILE_WR))
+                       mutex_unlock(&inode->i_mutex);
                spin_lock(&ci->i_ceph_lock);
        }
 
@@ -2473,6 +2494,11 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
        } else {
                dout("grant: %s -> %s\n", ceph_cap_string(cap->issued),
                     ceph_cap_string(newcaps));
+               /* non-auth MDS is revoking the newly grant caps ? */
+               if (cap == ci->i_auth_cap &&
+                   __ceph_caps_revoking_other(ci, cap, newcaps))
+                   check_caps = 2;
+
                cap->issued = newcaps;
                cap->implemented |= newcaps; /* add bits only, to
                                              * avoid stepping on a
@@ -3042,21 +3068,19 @@ int ceph_encode_inode_release(void **p, struct inode *inode,
                     (cap->issued & unless) == 0)) {
                        if ((cap->issued & drop) &&
                            (cap->issued & unless) == 0) {
-                               dout("encode_inode_release %p cap %p %s -> "
-                                    "%s\n", inode, cap,
+                               int wanted = __ceph_caps_wanted(ci);
+                               if ((ci->i_ceph_flags & CEPH_I_NODELAY) == 0)
+                                       wanted |= cap->mds_wanted;
+                               dout("encode_inode_release %p cap %p "
+                                    "%s -> %s, wanted %s -> %s\n", inode, cap,
                                     ceph_cap_string(cap->issued),
-                                    ceph_cap_string(cap->issued & ~drop));
+                                    ceph_cap_string(cap->issued & ~drop),
+                                    ceph_cap_string(cap->mds_wanted),
+                                    ceph_cap_string(wanted));
+
                                cap->issued &= ~drop;
                                cap->implemented &= ~drop;
-                               if (ci->i_ceph_flags & CEPH_I_NODELAY) {
-                                       int wanted = __ceph_caps_wanted(ci);
-                                       dout("  wanted %s -> %s (act %s)\n",
-                                            ceph_cap_string(cap->mds_wanted),
-                                            ceph_cap_string(cap->mds_wanted &
-                                                            ~wanted),
-                                            ceph_cap_string(wanted));
-                                       cap->mds_wanted &= wanted;
-                               }
+                               cap->mds_wanted = wanted;
                        } else {
                                dout("encode_inode_release %p cap %p %s"
                                     " (force)\n", inode, cap,
index 16c989d..2ddf061 100644 (file)
@@ -716,7 +716,6 @@ static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov,
        if (ceph_snap(inode) != CEPH_NOSNAP)
                return -EROFS;
 
-       sb_start_write(inode->i_sb);
        mutex_lock(&inode->i_mutex);
        hold_mutex = true;
 
@@ -809,7 +808,6 @@ retry_snap:
 out:
        if (hold_mutex)
                mutex_unlock(&inode->i_mutex);
-       sb_end_write(inode->i_sb);
        current->backing_dev_info = NULL;
 
        return written ? written : err;
@@ -824,7 +822,7 @@ static loff_t ceph_llseek(struct file *file, loff_t offset, int whence)
        int ret;
 
        mutex_lock(&inode->i_mutex);
-       __ceph_do_pending_vmtruncate(inode, false);
+       __ceph_do_pending_vmtruncate(inode);
 
        if (whence == SEEK_END || whence == SEEK_DATA || whence == SEEK_HOLE) {
                ret = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE);
index be0f7e2..f3a2abf 100644 (file)
@@ -903,8 +903,8 @@ static struct dentry *splice_dentry(struct dentry *dn, struct inode *in,
        } else if (realdn) {
                dout("dn %p (%d) spliced with %p (%d) "
                     "inode %p ino %llx.%llx\n",
-                    dn, dn->d_count,
-                    realdn, realdn->d_count,
+                    dn, d_count(dn),
+                    realdn, d_count(realdn),
                     realdn->d_inode, ceph_vinop(realdn->d_inode));
                dput(dn);
                dn = realdn;
@@ -1465,7 +1465,9 @@ static void ceph_vmtruncate_work(struct work_struct *work)
        struct inode *inode = &ci->vfs_inode;
 
        dout("vmtruncate_work %p\n", inode);
-       __ceph_do_pending_vmtruncate(inode, true);
+       mutex_lock(&inode->i_mutex);
+       __ceph_do_pending_vmtruncate(inode);
+       mutex_unlock(&inode->i_mutex);
        iput(inode);
 }
 
@@ -1492,7 +1494,7 @@ void ceph_queue_vmtruncate(struct inode *inode)
  * Make sure any pending truncation is applied before doing anything
  * that may depend on it.
  */
-void __ceph_do_pending_vmtruncate(struct inode *inode, bool needlock)
+void __ceph_do_pending_vmtruncate(struct inode *inode)
 {
        struct ceph_inode_info *ci = ceph_inode(inode);
        u64 to;
@@ -1525,11 +1527,7 @@ retry:
             ci->i_truncate_pending, to);
        spin_unlock(&ci->i_ceph_lock);
 
-       if (needlock)
-               mutex_lock(&inode->i_mutex);
        truncate_inode_pages(inode->i_mapping, to);
-       if (needlock)
-               mutex_unlock(&inode->i_mutex);
 
        spin_lock(&ci->i_ceph_lock);
        if (to == ci->i_truncate_size) {
@@ -1588,7 +1586,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
        if (ceph_snap(inode) != CEPH_NOSNAP)
                return -EROFS;
 
-       __ceph_do_pending_vmtruncate(inode, false);
+       __ceph_do_pending_vmtruncate(inode);
 
        err = inode_change_ok(inode, attr);
        if (err != 0)
@@ -1770,7 +1768,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
             ceph_cap_string(dirtied), mask);
 
        ceph_mdsc_put_request(req);
-       __ceph_do_pending_vmtruncate(inode, false);
+       __ceph_do_pending_vmtruncate(inode);
        return err;
 out:
        spin_unlock(&ci->i_ceph_lock);
index 690f73f..ae6d14e 100644 (file)
@@ -169,7 +169,7 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl)
 }
 
 /**
- * Must be called with BKL already held. Fills in the passed
+ * Must be called with lock_flocks() already held. Fills in the passed
  * counter variables, so you can prepare pagelist metadata before calling
  * ceph_encode_locks.
  */
index 74fd289..187bf21 100644 (file)
@@ -1391,6 +1391,7 @@ static void discard_cap_releases(struct ceph_mds_client *mdsc,
        num = le32_to_cpu(head->num);
        dout("discard_cap_releases mds%d %p %u\n", session->s_mds, msg, num);
        head->num = cpu_to_le32(0);
+       msg->front.iov_len = sizeof(*head);
        session->s_num_cap_releases += num;
 
        /* requeue completed messages */
@@ -1553,7 +1554,7 @@ retry:
        *base = ceph_ino(temp->d_inode);
        *plen = len;
        dout("build_path on %p %d built %llx '%.*s'\n",
-            dentry, dentry->d_count, *base, len, path);
+            dentry, d_count(dentry), *base, len, path);
        return path;
 }
 
@@ -2454,6 +2455,7 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap,
        spin_lock(&ci->i_ceph_lock);
        cap->seq = 0;        /* reset cap seq */
        cap->issue_seq = 0;  /* and issue_seq */
+       cap->mseq = 0;       /* and migrate_seq */
 
        if (recon_state->flock) {
                rec.v2.cap_id = cpu_to_le64(cap->cap_id);
@@ -3040,8 +3042,10 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc)
        fsc->mdsc = mdsc;
        mutex_init(&mdsc->mutex);
        mdsc->mdsmap = kzalloc(sizeof(*mdsc->mdsmap), GFP_NOFS);
-       if (mdsc->mdsmap == NULL)
+       if (mdsc->mdsmap == NULL) {
+               kfree(mdsc);
                return -ENOMEM;
+       }
 
        init_completion(&mdsc->safe_umount_waiters);
        init_waitqueue_head(&mdsc->session_close_wq);
index 9278dec..132b64e 100644 (file)
@@ -92,6 +92,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end)
                u32 num_export_targets;
                void *pexport_targets = NULL;
                struct ceph_timespec laggy_since;
+               struct ceph_mds_info *info;
 
                ceph_decode_need(p, end, sizeof(u64)*2 + 1 + sizeof(u32), bad);
                global_id = ceph_decode_64(p);
@@ -126,24 +127,27 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end)
                     i+1, n, global_id, mds, inc,
                     ceph_pr_addr(&addr.in_addr),
                     ceph_mds_state_name(state));
-               if (mds >= 0 && mds < m->m_max_mds && state > 0) {
-                       m->m_info[mds].global_id = global_id;
-                       m->m_info[mds].state = state;
-                       m->m_info[mds].addr = addr;
-                       m->m_info[mds].laggy =
-                               (laggy_since.tv_sec != 0 ||
-                                laggy_since.tv_nsec != 0);
-                       m->m_info[mds].num_export_targets = num_export_targets;
-                       if (num_export_targets) {
-                               m->m_info[mds].export_targets =
-                                       kcalloc(num_export_targets, sizeof(u32),
-                                               GFP_NOFS);
-                               for (j = 0; j < num_export_targets; j++)
-                                       m->m_info[mds].export_targets[j] =
-                                              ceph_decode_32(&pexport_targets);
-                       } else {
-                               m->m_info[mds].export_targets = NULL;
-                       }
+
+               if (mds < 0 || mds >= m->m_max_mds || state <= 0)
+                       continue;
+
+               info = &m->m_info[mds];
+               info->global_id = global_id;
+               info->state = state;
+               info->addr = addr;
+               info->laggy = (laggy_since.tv_sec != 0 ||
+                              laggy_since.tv_nsec != 0);
+               info->num_export_targets = num_export_targets;
+               if (num_export_targets) {
+                       info->export_targets = kcalloc(num_export_targets,
+                                                      sizeof(u32), GFP_NOFS);
+                       if (info->export_targets == NULL)
+                               goto badmem;
+                       for (j = 0; j < num_export_targets; j++)
+                               info->export_targets[j] =
+                                      ceph_decode_32(&pexport_targets);
+               } else {
+                       info->export_targets = NULL;
                }
        }
 
@@ -170,7 +174,7 @@ bad:
                       DUMP_PREFIX_OFFSET, 16, 1,
                       start, end - start, true);
        ceph_mdsmap_destroy(m);
-       return ERR_PTR(-EINVAL);
+       return ERR_PTR(err);
 }
 
 void ceph_mdsmap_destroy(struct ceph_mdsmap *m)
index 7d377c9..6627b26 100644 (file)
@@ -357,7 +357,7 @@ static int parse_mount_options(struct ceph_mount_options **pfsopt,
        }
        err = -EINVAL;
        dev_name_end--;         /* back up to ':' separator */
-       if (*dev_name_end != ':') {
+       if (dev_name_end < dev_name || *dev_name_end != ':') {
                pr_err("device name is missing path (no : separator in %s)\n",
                                dev_name);
                goto out;
index 7ccfdb4..cbded57 100644 (file)
@@ -534,7 +534,7 @@ extern int __ceph_caps_mds_wanted(struct ceph_inode_info *ci);
 extern void ceph_caps_init(struct ceph_mds_client *mdsc);
 extern void ceph_caps_finalize(struct ceph_mds_client *mdsc);
 extern void ceph_adjust_min_caps(struct ceph_mds_client *mdsc, int delta);
-extern int ceph_reserve_caps(struct ceph_mds_client *mdsc,
+extern void ceph_reserve_caps(struct ceph_mds_client *mdsc,
                             struct ceph_cap_reservation *ctx, int need);
 extern int ceph_unreserve_caps(struct ceph_mds_client *mdsc,
                               struct ceph_cap_reservation *ctx);
@@ -692,7 +692,7 @@ extern int ceph_readdir_prepopulate(struct ceph_mds_request *req,
 extern int ceph_inode_holds_cap(struct inode *inode, int mask);
 
 extern int ceph_inode_set_size(struct inode *inode, loff_t size);
-extern void __ceph_do_pending_vmtruncate(struct inode *inode, bool needlock);
+extern void __ceph_do_pending_vmtruncate(struct inode *inode);
 extern void ceph_queue_vmtruncate(struct inode *inode);
 
 extern void ceph_queue_invalidate(struct inode *inode);
index 9b6b2b6..be661d8 100644 (file)
@@ -675,17 +675,18 @@ ssize_t ceph_getxattr(struct dentry *dentry, const char *name, void *value,
        if (!ceph_is_valid_xattr(name))
                return -ENODATA;
 
-       spin_lock(&ci->i_ceph_lock);
-       dout("getxattr %p ver=%lld index_ver=%lld\n", inode,
-            ci->i_xattrs.version, ci->i_xattrs.index_version);
 
        /* let's see if a virtual xattr was requested */
        vxattr = ceph_match_vxattr(inode, name);
        if (vxattr && !(vxattr->exists_cb && !vxattr->exists_cb(ci))) {
                err = vxattr->getxattr_cb(ci, value, size);
-               goto out;
+               return err;
        }
 
+       spin_lock(&ci->i_ceph_lock);
+       dout("getxattr %p ver=%lld index_ver=%lld\n", inode,
+            ci->i_xattrs.version, ci->i_xattrs.index_version);
+
        if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 1) &&
            (ci->i_xattrs.index_version >= ci->i_xattrs.version)) {
                goto get_xattr;
index 14a1480..190effc 100644 (file)
@@ -526,7 +526,7 @@ static int coda_dentry_revalidate(struct dentry *de, unsigned int flags)
        if (cii->c_flags & C_FLUSH) 
                coda_flag_inode_children(inode, C_FLUSH);
 
-       if (de->d_count > 1)
+       if (d_count(de) > 1)
                /* pretend it's valid, but don't change the flags */
                goto out;
 
index 64e5323..5e7c60c 100644 (file)
@@ -387,7 +387,7 @@ static void remove_dir(struct dentry * d)
        if (d->d_inode)
                simple_rmdir(parent->d_inode,d);
 
-       pr_debug(" o %s removing done (%d)\n",d->d_name.name, d->d_count);
+       pr_debug(" o %s removing done (%d)\n",d->d_name.name, d_count(d));
 
        dput(parent);
 }
index a2f2bb2..67e9b63 100644 (file)
@@ -358,7 +358,7 @@ static int ecryptfs_lookup_interpose(struct dentry *dentry,
 
        lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent));
        fsstack_copy_attr_atime(dir_inode, lower_dentry->d_parent->d_inode);
-       BUG_ON(!lower_dentry->d_count);
+       BUG_ON(!d_count(lower_dentry));
 
        ecryptfs_set_dentry_private(dentry, dentry_info);
        ecryptfs_set_dentry_lower(dentry, lower_dentry);
index b31dbd4..1cb9c7e 100644 (file)
@@ -48,9 +48,13 @@ int ext3_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
 
        trace_ext3_sync_file_enter(file, datasync);
 
-       if (inode->i_sb->s_flags & MS_RDONLY)
+       if (inode->i_sb->s_flags & MS_RDONLY) {
+               /* Make sure that we read updated state */
+               smp_rmb();
+               if (EXT3_SB(inode->i_sb)->s_mount_state & EXT3_ERROR_FS)
+                       return -EROFS;
                return 0;
-
+       }
        ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
        if (ret)
                goto out;
index 6356665..c47f147 100644 (file)
@@ -174,6 +174,11 @@ static void ext3_handle_error(struct super_block *sb)
        if (test_opt (sb, ERRORS_RO)) {
                ext3_msg(sb, KERN_CRIT,
                        "error: remounting filesystem read-only");
+               /*
+                * Make sure updated value of ->s_mount_state will be visible
+                * before ->s_flags update.
+                */
+               smp_wmb();
                sb->s_flags |= MS_RDONLY;
        }
        ext3_commit_super(sb, es, 1);
@@ -291,8 +296,14 @@ void ext3_abort(struct super_block *sb, const char *function,
        ext3_msg(sb, KERN_CRIT,
                "error: remounting filesystem read-only");
        EXT3_SB(sb)->s_mount_state |= EXT3_ERROR_FS;
-       sb->s_flags |= MS_RDONLY;
        set_opt(EXT3_SB(sb)->s_mount_opt, ABORT);
+       /*
+        * Make sure updated value of ->s_mount_state will be visible
+        * before ->s_flags update.
+        */
+       smp_wmb();
+       sb->s_flags |= MS_RDONLY;
+
        if (EXT3_SB(sb)->s_journal)
                journal_abort(EXT3_SB(sb)->s_journal, -EIO);
 }
index 9d1cd42..62f0d59 100644 (file)
@@ -610,13 +610,12 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
 {
        struct inode *inode = file_inode(file);
        unsigned long npages = dir_blocks(inode);
-       unsigned int bit_pos = 0, start_bit_pos = 0;
+       unsigned int bit_pos = 0;
        struct f2fs_dentry_block *dentry_blk = NULL;
        struct f2fs_dir_entry *de = NULL;
        struct page *dentry_page = NULL;
        unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK);
        unsigned char d_type = DT_UNKNOWN;
-       int slots;
 
        bit_pos = ((unsigned long)ctx->pos % NR_DENTRY_IN_BLOCK);
 
@@ -625,7 +624,6 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
                if (IS_ERR(dentry_page))
                        continue;
 
-               start_bit_pos = bit_pos;
                dentry_blk = kmap(dentry_page);
                while (bit_pos < NR_DENTRY_IN_BLOCK) {
                        bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
@@ -634,19 +632,19 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
                        if (bit_pos >= NR_DENTRY_IN_BLOCK)
                                break;
 
-                       ctx->pos += bit_pos - start_bit_pos;
                        de = &dentry_blk->dentry[bit_pos];
                        if (de->file_type < F2FS_FT_MAX)
                                d_type = f2fs_filetype_table[de->file_type];
                        else
                                d_type = DT_UNKNOWN;
                        if (!dir_emit(ctx,
-                                     dentry_blk->filename[bit_pos],
-                                     le16_to_cpu(de->name_len),
-                                     le32_to_cpu(de->ino), d_type))
-                               goto success;
-                       slots = GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
-                       bit_pos += slots;
+                                       dentry_blk->filename[bit_pos],
+                                       le16_to_cpu(de->name_len),
+                                       le32_to_cpu(de->ino), d_type))
+                               goto stop;
+
+                       bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
+                       ctx->pos = n * NR_DENTRY_IN_BLOCK + bit_pos;
                }
                bit_pos = 0;
                ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK;
@@ -654,7 +652,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
                f2fs_put_page(dentry_page, 1);
                dentry_page = NULL;
        }
-success:
+stop:
        if (dentry_page && !IS_ERR(dentry_page)) {
                kunmap(dentry_page);
                f2fs_put_page(dentry_page, 1);
index 21664fc..4241e6f 100644 (file)
@@ -86,6 +86,7 @@ struct msdos_sb_info {
        const void *dir_ops;          /* Opaque; default directory operations */
        int dir_per_block;            /* dir entries per block */
        int dir_per_block_bits;       /* log2(dir_per_block) */
+       unsigned int vol_id;            /*volume ID*/
 
        int fatent_shift;
        struct fatent_operations *fatent_ops;
index b0b632e..9b104f5 100644 (file)
@@ -114,6 +114,12 @@ out:
        return err;
 }
 
+static int fat_ioctl_get_volume_id(struct inode *inode, u32 __user *user_attr)
+{
+       struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
+       return put_user(sbi->vol_id, user_attr);
+}
+
 long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
        struct inode *inode = file_inode(filp);
@@ -124,6 +130,8 @@ long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                return fat_ioctl_get_attributes(inode, user_attr);
        case FAT_IOCTL_SET_ATTRIBUTES:
                return fat_ioctl_set_attributes(filp, user_attr);
+       case FAT_IOCTL_GET_VOLUME_ID:
+               return fat_ioctl_get_volume_id(inode, user_attr);
        default:
                return -ENOTTY; /* Inappropriate ioctl for device */
        }
index 5d4513c..11b51bb 100644 (file)
@@ -1415,6 +1415,18 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
                brelse(fsinfo_bh);
        }
 
+       /* interpret volume ID as a little endian 32 bit integer */
+       if (sbi->fat_bits == 32)
+               sbi->vol_id = (((u32)b->fat32.vol_id[0]) |
+                                       ((u32)b->fat32.vol_id[1] << 8) |
+                                       ((u32)b->fat32.vol_id[2] << 16) |
+                                       ((u32)b->fat32.vol_id[3] << 24));
+       else /* fat 16 or 12 */
+               sbi->vol_id = (((u32)b->fat16.vol_id[0]) |
+                                       ((u32)b->fat16.vol_id[1] << 8) |
+                                       ((u32)b->fat16.vol_id[2] << 16) |
+                                       ((u32)b->fat16.vol_id[3] << 24));
+
        sbi->dir_per_block = sb->s_blocksize / sizeof(struct msdos_dir_entry);
        sbi->dir_per_block_bits = ffs(sbi->dir_per_block) - 1;
 
index a85ac4e..68851ff 100644 (file)
@@ -963,7 +963,7 @@ static long wb_check_old_data_flush(struct bdi_writeback *wb)
 /*
  * Retrieve work items and do the writeback they describe
  */
-long wb_do_writeback(struct bdi_writeback *wb, int force_wait)
+static long wb_do_writeback(struct bdi_writeback *wb)
 {
        struct backing_dev_info *bdi = wb->bdi;
        struct wb_writeback_work *work;
@@ -971,12 +971,6 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait)
 
        set_bit(BDI_writeback_running, &wb->bdi->state);
        while ((work = get_next_work_item(bdi)) != NULL) {
-               /*
-                * Override sync mode, in case we must wait for completion
-                * because this thread is exiting now.
-                */
-               if (force_wait)
-                       work->sync_mode = WB_SYNC_ALL;
 
                trace_writeback_exec(bdi, work);
 
@@ -1025,7 +1019,7 @@ void bdi_writeback_workfn(struct work_struct *work)
                 * rescuer as work_list needs to be drained.
                 */
                do {
-                       pages_written = wb_do_writeback(wb, 0);
+                       pages_written = wb_do_writeback(wb);
                        trace_writeback_pages_written(pages_written);
                } while (!list_empty(&bdi->work_list));
        } else {
index 04e2c1f..b27a300 100644 (file)
 #include <linux/rcupdate.h>
 #include <linux/pid_namespace.h>
 #include <linux/hashtable.h>
+#include <linux/percpu.h>
+#include <linux/lglock.h>
 
 #include <asm/uaccess.h>
 
@@ -155,11 +157,13 @@ int lease_break_time = 45;
        for (lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next)
 
 /*
- * The global file_lock_list is only used for displaying /proc/locks. Protected
- * by the file_lock_lock.
+ * The global file_lock_list is only used for displaying /proc/locks, so we
+ * keep a list on each CPU, with each list protected by its own spinlock via
+ * the file_lock_lglock. Note that alterations to the list also require that
+ * the relevant i_lock is held.
  */
-static HLIST_HEAD(file_lock_list);
-static DEFINE_SPINLOCK(file_lock_lock);
+DEFINE_STATIC_LGLOCK(file_lock_lglock);
+static DEFINE_PER_CPU(struct hlist_head, file_lock_list);
 
 /*
  * The blocked_hash is used to find POSIX lock loops for deadlock detection.
@@ -506,20 +510,30 @@ static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2)
        return fl1->fl_owner == fl2->fl_owner;
 }
 
+/* Must be called with the i_lock held! */
 static inline void
 locks_insert_global_locks(struct file_lock *fl)
 {
-       spin_lock(&file_lock_lock);
-       hlist_add_head(&fl->fl_link, &file_lock_list);
-       spin_unlock(&file_lock_lock);
+       lg_local_lock(&file_lock_lglock);
+       fl->fl_link_cpu = smp_processor_id();
+       hlist_add_head(&fl->fl_link, this_cpu_ptr(&file_lock_list));
+       lg_local_unlock(&file_lock_lglock);
 }
 
+/* Must be called with the i_lock held! */
 static inline void
 locks_delete_global_locks(struct file_lock *fl)
 {
-       spin_lock(&file_lock_lock);
+       /*
+        * Avoid taking lock if already unhashed. This is safe since this check
+        * is done while holding the i_lock, and new insertions into the list
+        * also require that it be held.
+        */
+       if (hlist_unhashed(&fl->fl_link))
+               return;
+       lg_local_lock_cpu(&file_lock_lglock, fl->fl_link_cpu);
        hlist_del_init(&fl->fl_link);
-       spin_unlock(&file_lock_lock);
+       lg_local_unlock_cpu(&file_lock_lglock, fl->fl_link_cpu);
 }
 
 static unsigned long
@@ -1454,7 +1468,7 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
        if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
                goto out;
        if ((arg == F_WRLCK)
-           && ((dentry->d_count > 1)
+           && ((d_count(dentry) > 1)
                || (atomic_read(&inode->i_count) > 1)))
                goto out;
 
@@ -2243,6 +2257,11 @@ EXPORT_SYMBOL_GPL(vfs_cancel_lock);
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 
+struct locks_iterator {
+       int     li_cpu;
+       loff_t  li_pos;
+};
+
 static void lock_get_status(struct seq_file *f, struct file_lock *fl,
                            loff_t id, char *pfx)
 {
@@ -2316,39 +2335,41 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
 
 static int locks_show(struct seq_file *f, void *v)
 {
+       struct locks_iterator *iter = f->private;
        struct file_lock *fl, *bfl;
 
        fl = hlist_entry(v, struct file_lock, fl_link);
 
-       lock_get_status(f, fl, *((loff_t *)f->private), "");
+       lock_get_status(f, fl, iter->li_pos, "");
 
        list_for_each_entry(bfl, &fl->fl_block, fl_block)
-               lock_get_status(f, bfl, *((loff_t *)f->private), " ->");
+               lock_get_status(f, bfl, iter->li_pos, " ->");
 
        return 0;
 }
 
 static void *locks_start(struct seq_file *f, loff_t *pos)
 {
-       loff_t *p = f->private;
+       struct locks_iterator *iter = f->private;
 
-       spin_lock(&file_lock_lock);
+       iter->li_pos = *pos + 1;
+       lg_global_lock(&file_lock_lglock);
        spin_lock(&blocked_lock_lock);
-       *p = (*pos + 1);
-       return seq_hlist_start(&file_lock_list, *pos);
+       return seq_hlist_start_percpu(&file_lock_list, &iter->li_cpu, *pos);
 }
 
 static void *locks_next(struct seq_file *f, void *v, loff_t *pos)
 {
-       loff_t *p = f->private;
-       ++*p;
-       return seq_hlist_next(v, &file_lock_list, pos);
+       struct locks_iterator *iter = f->private;
+
+       ++iter->li_pos;
+       return seq_hlist_next_percpu(v, &file_lock_list, &iter->li_cpu, pos);
 }
 
 static void locks_stop(struct seq_file *f, void *v)
 {
        spin_unlock(&blocked_lock_lock);
-       spin_unlock(&file_lock_lock);
+       lg_global_unlock(&file_lock_lglock);
 }
 
 static const struct seq_operations locks_seq_operations = {
@@ -2360,7 +2381,8 @@ static const struct seq_operations locks_seq_operations = {
 
 static int locks_open(struct inode *inode, struct file *filp)
 {
-       return seq_open_private(filp, &locks_seq_operations, sizeof(loff_t));
+       return seq_open_private(filp, &locks_seq_operations,
+                                       sizeof(struct locks_iterator));
 }
 
 static const struct file_operations proc_locks_operations = {
@@ -2460,9 +2482,16 @@ EXPORT_SYMBOL(lock_may_write);
 
 static int __init filelock_init(void)
 {
+       int i;
+
        filelock_cache = kmem_cache_create("file_lock_cache",
                        sizeof(struct file_lock), 0, SLAB_PANIC, NULL);
 
+       lg_lock_init(&file_lock_lglock, "file_lock_lglock");
+
+       for_each_possible_cpu(i)
+               INIT_HLIST_HEAD(per_cpu_ptr(&file_lock_list, i));
+
        return 0;
 }
 
index 0765ad1..4659da6 100644 (file)
@@ -403,18 +403,24 @@ static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options)
                switch (optval) {
                        case 'u':
                                data->uid = make_kuid(current_user_ns(), optint);
-                               if (!uid_valid(data->uid))
+                               if (!uid_valid(data->uid)) {
+                                       ret = -EINVAL;
                                        goto err;
+                               }
                                break;
                        case 'g':
                                data->gid = make_kgid(current_user_ns(), optint);
-                               if (!gid_valid(data->gid))
+                               if (!gid_valid(data->gid)) {
+                                       ret = -EINVAL;
                                        goto err;
+                               }
                                break;
                        case 'o':
                                data->mounted_uid = make_kuid(current_user_ns(), optint);
-                               if (!uid_valid(data->mounted_uid))
+                               if (!uid_valid(data->mounted_uid)) {
+                                       ret = -EINVAL;
                                        goto err;
+                               }
                                break;
                        case 'm':
                                data->file_mode = optint;
index 13ca196..b5e80b0 100644 (file)
@@ -104,6 +104,15 @@ config NFS_V4_1
 
          If unsure, say N.
 
+config NFS_V4_2
+       bool "NFS client support for NFSv4.2"
+       depends on NFS_V4_1
+       help
+         This option enables support for minor version 2 of the NFSv4 protocol
+         in the kernel's NFS client.
+
+         If unsure, say N.
+
 config PNFS_FILE_LAYOUT
        tristate
        depends on NFS_V4_1
@@ -131,6 +140,11 @@ config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN
          If the NFS client is unchanged from the upstream kernel, this
          option should be set to the default "kernel.org".
 
+config NFS_V4_SECURITY_LABEL
+       bool
+       depends on NFS_V4_2 && SECURITY
+       default y
+
 config ROOT_NFS
        bool "Root file system on NFS"
        depends on NFS_FS=y && IP_PNP
index cce2c05..e0bb048 100644 (file)
@@ -6,8 +6,7 @@ obj-$(CONFIG_NFS_FS) += nfs.o
 
 nfs-y                  := client.o dir.o file.o getroot.o inode.o super.o \
                           direct.o pagelist.o read.o symlink.o unlink.o \
-                          write.o namespace.o mount_clnt.o \
-                          dns_resolve.o cache_lib.o
+                          write.o namespace.o mount_clnt.o
 nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
 nfs-$(CONFIG_SYSCTL)   += sysctl.o
 nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
@@ -22,7 +21,8 @@ nfsv3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
 obj-$(CONFIG_NFS_V4) += nfsv4.o
 nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \
          delegation.o idmap.o callback.o callback_xdr.o callback_proc.o \
-         nfs4namespace.o nfs4getroot.o nfs4client.o
+         nfs4namespace.o nfs4getroot.o nfs4client.o dns_resolve.o
+nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o
 nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o
 nfsv4-$(CONFIG_NFS_V4_1)       += nfs4session.o pnfs.o pnfs_dev.o
 
index 434b93e..e242bbf 100644 (file)
@@ -1089,9 +1089,10 @@ nfs4_blk_get_deviceinfo(struct nfs_server *server, const struct nfs_fh *fh,
        dev->pgbase = 0;
        dev->pglen = PAGE_SIZE * max_pages;
        dev->mincount = 0;
+       dev->maxcount = max_resp_sz - nfs41_maxgetdevinfo_overhead;
 
        dprintk("%s: dev_id: %s\n", __func__, dev->dev_id.data);
-       rc = nfs4_proc_getdeviceinfo(server, dev);
+       rc = nfs4_proc_getdeviceinfo(server, dev, NULL);
        dprintk("%s getdevice info returns %d\n", __func__, rc);
        if (rc) {
                rv = ERR_PTR(rc);
index da6a43d..67cd732 100644 (file)
@@ -281,6 +281,7 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, struct n
                        ret = nfs4_callback_up_net(serv, net);
                        break;
                case 1:
+               case 2:
                        ret = nfs41_callback_up_net(serv, net);
                        break;
                default:
index efd54f0..84326e9 100644 (file)
@@ -32,6 +32,8 @@ enum nfs4_callback_opnum {
        OP_CB_WANTS_CANCELLED = 12,
        OP_CB_NOTIFY_LOCK   = 13,
        OP_CB_NOTIFY_DEVICEID = 14,
+/* Callback operations new to NFSv4.2 */
+       OP_CB_OFFLOAD = 15,
        OP_CB_ILLEGAL = 10044,
 };
 
@@ -39,6 +41,7 @@ struct cb_process_state {
        __be32                  drc_status;
        struct nfs_client       *clp;
        u32                     slotid;
+       u32                     minorversion;
        struct net              *net;
 };
 
index 0bc2768..e6ebc4c 100644 (file)
@@ -406,7 +406,8 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
        int i;
        __be32 status = htonl(NFS4ERR_BADSESSION);
 
-       clp = nfs4_find_client_sessionid(cps->net, args->csa_addr, &args->csa_sessionid);
+       clp = nfs4_find_client_sessionid(cps->net, args->csa_addr,
+                                        &args->csa_sessionid, cps->minorversion);
        if (clp == NULL)
                goto out;
 
index a35582c..f4ccfe6 100644 (file)
@@ -166,9 +166,9 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
        if (unlikely(p == NULL))
                return htonl(NFS4ERR_RESOURCE);
        hdr->minorversion = ntohl(*p++);
-       /* Check minor version is zero or one. */
-       if (hdr->minorversion <= 1) {
-               hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */
+       /* Check for minor version support */
+       if (hdr->minorversion <= NFS4_MAX_MINOR_VERSION) {
+               hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 and v4.2 */
        } else {
                pr_warn_ratelimited("NFS: %s: NFSv4 server callback with "
                        "illegal minor version %u!\n",
@@ -786,6 +786,26 @@ static void nfs4_cb_free_slot(struct cb_process_state *cps)
 }
 #endif /* CONFIG_NFS_V4_1 */
 
+#ifdef CONFIG_NFS_V4_2
+static __be32
+preprocess_nfs42_op(int nop, unsigned int op_nr, struct callback_op **op)
+{
+       __be32 status = preprocess_nfs41_op(nop, op_nr, op);
+       if (status != htonl(NFS4ERR_OP_ILLEGAL))
+               return status;
+
+       if (op_nr == OP_CB_OFFLOAD)
+               return htonl(NFS4ERR_NOTSUPP);
+       return htonl(NFS4ERR_OP_ILLEGAL);
+}
+#else /* CONFIG_NFS_V4_2 */
+static __be32
+preprocess_nfs42_op(int nop, unsigned int op_nr, struct callback_op **op)
+{
+       return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
+}
+#endif /* CONFIG_NFS_V4_2 */
+
 static __be32
 preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
 {
@@ -801,8 +821,7 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
        return htonl(NFS_OK);
 }
 
-static __be32 process_op(uint32_t minorversion, int nop,
-               struct svc_rqst *rqstp,
+static __be32 process_op(int nop, struct svc_rqst *rqstp,
                struct xdr_stream *xdr_in, void *argp,
                struct xdr_stream *xdr_out, void *resp,
                struct cb_process_state *cps)
@@ -819,10 +838,22 @@ static __be32 process_op(uint32_t minorversion, int nop,
                return status;
 
        dprintk("%s: minorversion=%d nop=%d op_nr=%u\n",
-               __func__, minorversion, nop, op_nr);
+               __func__, cps->minorversion, nop, op_nr);
+
+       switch (cps->minorversion) {
+       case 0:
+               status = preprocess_nfs4_op(op_nr, &op);
+               break;
+       case 1:
+               status = preprocess_nfs41_op(nop, op_nr, &op);
+               break;
+       case 2:
+               status = preprocess_nfs42_op(nop, op_nr, &op);
+               break;
+       default:
+               status = htonl(NFS4ERR_MINOR_VERS_MISMATCH);
+       }
 
-       status = minorversion ? preprocess_nfs41_op(nop, op_nr, &op) :
-                               preprocess_nfs4_op(op_nr, &op);
        if (status == htonl(NFS4ERR_OP_ILLEGAL))
                op_nr = OP_CB_ILLEGAL;
        if (status)
@@ -885,14 +916,15 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
                        return rpc_drop_reply;
        }
 
+       cps.minorversion = hdr_arg.minorversion;
        hdr_res.taglen = hdr_arg.taglen;
        hdr_res.tag = hdr_arg.tag;
        if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0)
                return rpc_system_err;
 
        while (status == 0 && nops != hdr_arg.nops) {
-               status = process_op(hdr_arg.minorversion, nops, rqstp,
-                                   &xdr_in, argp, &xdr_out, resp, &cps);
+               status = process_op(nops, rqstp, &xdr_in,
+                                   argp, &xdr_out, resp, &cps);
                nops++;
        }
 
index c513b0c..340b1ef 100644 (file)
@@ -753,8 +753,6 @@ static int nfs_init_server(struct nfs_server *server,
                        data->timeo, data->retrans);
        if (data->flags & NFS_MOUNT_NORESVPORT)
                set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
-       if (server->options & NFS_OPTION_MIGRATION)
-               set_bit(NFS_CS_MIGRATION, &cl_init.init_flags);
 
        /* Allocate or find a client reference we can use */
        clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX);
@@ -1076,7 +1074,7 @@ struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
        }
 
        if (!(fattr->valid & NFS_ATTR_FATTR)) {
-               error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr);
+               error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr, NULL);
                if (error < 0) {
                        dprintk("nfs_create_server: getattr error = %d\n", -error);
                        goto error;
index d7ed697..0fac2cb 100644 (file)
@@ -437,6 +437,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
        struct dentry *alias;
        struct inode *dir = parent->d_inode;
        struct inode *inode;
+       int status;
 
        if (filename.name[0] == '.') {
                if (filename.len == 1)
@@ -449,7 +450,9 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
        dentry = d_lookup(parent, &filename);
        if (dentry != NULL) {
                if (nfs_same_file(dentry, entry)) {
-                       nfs_refresh_inode(dentry->d_inode, entry->fattr);
+                       status = nfs_refresh_inode(dentry->d_inode, entry->fattr);
+                       if (!status)
+                               nfs_setsecurity(dentry->d_inode, entry->fattr, entry->label);
                        goto out;
                } else {
                        if (d_invalidate(dentry) != 0)
@@ -462,7 +465,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
        if (dentry == NULL)
                return;
 
-       inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr);
+       inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr, entry->label);
        if (IS_ERR(inode))
                goto out;
 
@@ -587,10 +590,16 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
        if (entry.fh == NULL || entry.fattr == NULL)
                goto out;
 
+       entry.label = nfs4_label_alloc(NFS_SERVER(inode), GFP_NOWAIT);
+       if (IS_ERR(entry.label)) {
+               status = PTR_ERR(entry.label);
+               goto out;
+       }
+
        array = nfs_readdir_get_array(page);
        if (IS_ERR(array)) {
                status = PTR_ERR(array);
-               goto out;
+               goto out_label_free;
        }
        memset(array, 0, sizeof(struct nfs_cache_array));
        array->eof_index = -1;
@@ -616,6 +625,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
        nfs_readdir_free_large_page(pages_ptr, pages, array_size);
 out_release_array:
        nfs_readdir_release_array(page);
+out_label_free:
+       nfs4_label_free(entry.label);
 out:
        nfs_free_fattr(entry.fattr);
        nfs_free_fhandle(entry.fh);
@@ -1040,6 +1051,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
        struct dentry *parent;
        struct nfs_fh *fhandle = NULL;
        struct nfs_fattr *fattr = NULL;
+       struct nfs4_label *label = NULL;
        int error;
 
        if (flags & LOOKUP_RCU)
@@ -1082,7 +1094,11 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
        if (fhandle == NULL || fattr == NULL)
                goto out_error;
 
-       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
+       label = nfs4_label_alloc(NFS_SERVER(inode), GFP_NOWAIT);
+       if (IS_ERR(label))
+               goto out_error;
+
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
        if (error)
                goto out_bad;
        if (nfs_compare_fh(NFS_FH(inode), fhandle))
@@ -1090,8 +1106,12 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
        if ((error = nfs_refresh_inode(inode, fattr)) != 0)
                goto out_bad;
 
+       nfs_setsecurity(inode, fattr, label);
+
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
+       nfs4_label_free(label);
+
 out_set_verifier:
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
  out_valid:
@@ -1108,6 +1128,7 @@ out_zap_parent:
  out_bad:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
+       nfs4_label_free(label);
        nfs_mark_for_revalidate(dir);
        if (inode && S_ISDIR(inode->i_mode)) {
                /* Purge readdir caches. */
@@ -1128,6 +1149,7 @@ out_zap_parent:
 out_error:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
+       nfs4_label_free(label);
        dput(parent);
        dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) lookup returned error %d\n",
                        __func__, dentry->d_parent->d_name.name,
@@ -1256,6 +1278,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
        struct inode *inode = NULL;
        struct nfs_fh *fhandle = NULL;
        struct nfs_fattr *fattr = NULL;
+       struct nfs4_label *label = NULL;
        int error;
 
        dfprintk(VFS, "NFS: lookup(%s/%s)\n",
@@ -1282,17 +1305,21 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
        if (fhandle == NULL || fattr == NULL)
                goto out;
 
+       label = nfs4_label_alloc(NFS_SERVER(dir), GFP_NOWAIT);
+       if (IS_ERR(label))
+               goto out;
+
        parent = dentry->d_parent;
        /* Protect against concurrent sillydeletes */
        nfs_block_sillyrename(parent);
-       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
        if (error == -ENOENT)
                goto no_entry;
        if (error < 0) {
                res = ERR_PTR(error);
                goto out_unblock_sillyrename;
        }
-       inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
+       inode = nfs_fhget(dentry->d_sb, fhandle, fattr, label);
        res = ERR_CAST(inode);
        if (IS_ERR(res))
                goto out_unblock_sillyrename;
@@ -1310,6 +1337,7 @@ no_entry:
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
 out_unblock_sillyrename:
        nfs_unblock_sillyrename(parent);
+       nfs4_label_free(label);
 out:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
@@ -1357,18 +1385,6 @@ static int nfs_finish_open(struct nfs_open_context *ctx,
 {
        int err;
 
-       if (ctx->dentry != dentry) {
-               dput(ctx->dentry);
-               ctx->dentry = dget(dentry);
-       }
-
-       /* If the open_intent is for execute, we have an extra check to make */
-       if (ctx->mode & FMODE_EXEC) {
-               err = nfs_may_open(dentry->d_inode, ctx->cred, open_flags);
-               if (err < 0)
-                       goto out;
-       }
-
        err = finish_open(file, dentry, do_open, opened);
        if (err)
                goto out;
@@ -1427,13 +1443,13 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
 
        nfs_block_sillyrename(dentry->d_parent);
        inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr);
-       d_drop(dentry);
+       nfs_unblock_sillyrename(dentry->d_parent);
        if (IS_ERR(inode)) {
-               nfs_unblock_sillyrename(dentry->d_parent);
                put_nfs_open_context(ctx);
                err = PTR_ERR(inode);
                switch (err) {
                case -ENOENT:
+                       d_drop(dentry);
                        d_add(dentry, NULL);
                        break;
                case -EISDIR:
@@ -1449,16 +1465,8 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
                }
                goto out;
        }
-       res = d_add_unique(dentry, inode);
-       if (res != NULL)
-               dentry = res;
-
-       nfs_unblock_sillyrename(dentry->d_parent);
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-
-       err = nfs_finish_open(ctx, dentry, file, open_flags, opened);
 
-       dput(res);
+       err = nfs_finish_open(ctx, ctx->dentry, file, open_flags, opened);
 out:
        return err;
 
@@ -1528,7 +1536,8 @@ no_open:
  * Code common to create, mkdir, and mknod.
  */
 int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
-                               struct nfs_fattr *fattr)
+                               struct nfs_fattr *fattr,
+                               struct nfs4_label *label)
 {
        struct dentry *parent = dget_parent(dentry);
        struct inode *dir = parent->d_inode;
@@ -1541,18 +1550,18 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
        if (dentry->d_inode)
                goto out;
        if (fhandle->size == 0) {
-               error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
+               error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, NULL);
                if (error)
                        goto out_error;
        }
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        if (!(fattr->valid & NFS_ATTR_FATTR)) {
                struct nfs_server *server = NFS_SB(dentry->d_sb);
-               error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr);
+               error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr, NULL);
                if (error < 0)
                        goto out_error;
        }
-       inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
+       inode = nfs_fhget(dentry->d_sb, fhandle, fattr, label);
        error = PTR_ERR(inode);
        if (IS_ERR(inode))
                goto out_error;
@@ -1721,7 +1730,7 @@ int nfs_unlink(struct inode *dir, struct dentry *dentry)
                dir->i_ino, dentry->d_name.name);
 
        spin_lock(&dentry->d_lock);
-       if (dentry->d_count > 1) {
+       if (d_count(dentry) > 1) {
                spin_unlock(&dentry->d_lock);
                /* Start asynchronous writeout of the inode */
                write_inode_now(dentry->d_inode, 0);
@@ -1866,7 +1875,7 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
                 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
                 new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
-                new_dentry->d_count);
+                d_count(new_dentry));
 
        /*
         * For non-directories, check whether the target is busy and if so,
@@ -1884,7 +1893,7 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        rehash = new_dentry;
                }
 
-               if (new_dentry->d_count > 2) {
+               if (d_count(new_dentry) > 2) {
                        int err;
 
                        /* copy the target dentry's name */
index 9455270..fc0f95e 100644 (file)
@@ -29,7 +29,6 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen,
        kfree(ip_addr);
        return ret;
 }
-EXPORT_SYMBOL_GPL(nfs_dns_resolve_name);
 
 #else
 
@@ -351,7 +350,6 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name,
                ret = -ESRCH;
        return ret;
 }
-EXPORT_SYMBOL_GPL(nfs_dns_resolve_name);
 
 static struct cache_detail nfs_dns_resolve_template = {
        .owner          = THIS_MODULE,
@@ -396,6 +394,21 @@ void nfs_dns_resolver_cache_destroy(struct net *net)
        cache_destroy_net(nn->nfs_dns_resolve, net);
 }
 
+static int nfs4_dns_net_init(struct net *net)
+{
+       return nfs_dns_resolver_cache_init(net);
+}
+
+static void nfs4_dns_net_exit(struct net *net)
+{
+       nfs_dns_resolver_cache_destroy(net);
+}
+
+static struct pernet_operations nfs4_dns_resolver_ops = {
+       .init = nfs4_dns_net_init,
+       .exit = nfs4_dns_net_exit,
+};
+
 static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event,
                           void *ptr)
 {
@@ -432,11 +445,24 @@ static struct notifier_block nfs_dns_resolver_block = {
 
 int nfs_dns_resolver_init(void)
 {
-       return rpc_pipefs_notifier_register(&nfs_dns_resolver_block);
+       int err;
+
+       err = register_pernet_subsys(&nfs4_dns_resolver_ops);
+       if (err < 0)
+               goto out;
+       err = rpc_pipefs_notifier_register(&nfs_dns_resolver_block);
+       if (err < 0)
+               goto out1;
+       return 0;
+out1:
+       unregister_pernet_subsys(&nfs4_dns_resolver_ops);
+out:
+       return err;
 }
 
 void nfs_dns_resolver_destroy(void)
 {
        rpc_pipefs_notifier_unregister(&nfs_dns_resolver_block);
+       unregister_pernet_subsys(&nfs4_dns_resolver_ops);
 }
 #endif
index 44efaa8..66984a9 100644 (file)
@@ -95,7 +95,7 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh,
                goto out;
        }
 
-       inode = nfs_fhget(sb, mntfh, fsinfo.fattr);
+       inode = nfs_fhget(sb, mntfh, fsinfo.fattr, NULL);
        if (IS_ERR(inode)) {
                dprintk("nfs_get_root: get root inode failed\n");
                ret = ERR_CAST(inode);
index c516da5..c2c4163 100644 (file)
@@ -262,29 +262,42 @@ static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen,
        return desclen;
 }
 
-static ssize_t nfs_idmap_request_key(struct key_type *key_type,
-                                    const char *name, size_t namelen,
-                                    const char *type, void *data,
-                                    size_t data_size, struct idmap *idmap)
+static struct key *nfs_idmap_request_key(const char *name, size_t namelen,
+                                        const char *type, struct idmap *idmap)
 {
-       const struct cred *saved_cred;
-       struct key *rkey;
        char *desc;
-       struct user_key_payload *payload;
+       struct key *rkey;
        ssize_t ret;
 
        ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc);
        if (ret <= 0)
-               goto out;
+               return ERR_PTR(ret);
+
+       rkey = request_key(&key_type_id_resolver, desc, "");
+       if (IS_ERR(rkey)) {
+               mutex_lock(&idmap->idmap_mutex);
+               rkey = request_key_with_auxdata(&key_type_id_resolver_legacy,
+                                               desc, "", 0, idmap);
+               mutex_unlock(&idmap->idmap_mutex);
+       }
+
+       kfree(desc);
+       return rkey;
+}
+
+static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
+                                const char *type, void *data,
+                                size_t data_size, struct idmap *idmap)
+{
+       const struct cred *saved_cred;
+       struct key *rkey;
+       struct user_key_payload *payload;
+       ssize_t ret;
 
        saved_cred = override_creds(id_resolver_cache);
-       if (idmap)
-               rkey = request_key_with_auxdata(key_type, desc, "", 0, idmap);
-       else
-               rkey = request_key(&key_type_id_resolver, desc, "");
+       rkey = nfs_idmap_request_key(name, namelen, type, idmap);
        revert_creds(saved_cred);
 
-       kfree(desc);
        if (IS_ERR(rkey)) {
                ret = PTR_ERR(rkey);
                goto out;
@@ -316,23 +329,6 @@ out:
        return ret;
 }
 
-static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
-                                const char *type, void *data,
-                                size_t data_size, struct idmap *idmap)
-{
-       ssize_t ret = nfs_idmap_request_key(&key_type_id_resolver,
-                                           name, namelen, type, data,
-                                           data_size, NULL);
-       if (ret < 0) {
-               mutex_lock(&idmap->idmap_mutex);
-               ret = nfs_idmap_request_key(&key_type_id_resolver_legacy,
-                                           name, namelen, type, data,
-                                           data_size, idmap);
-               mutex_unlock(&idmap->idmap_mutex);
-       }
-       return ret;
-}
-
 /* ID -> Name */
 static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf,
                                     size_t buflen, struct idmap *idmap)
index ce72704..c93639e 100644 (file)
@@ -48,7 +48,6 @@
 #include "iostat.h"
 #include "internal.h"
 #include "fscache.h"
-#include "dns_resolve.h"
 #include "pnfs.h"
 #include "nfs.h"
 #include "netns.h"
@@ -162,11 +161,19 @@ static void nfs_zap_caches_locked(struct inode *inode)
 
        memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf));
        if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) {
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
                nfs_fscache_invalidate(inode);
-       } else {
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
-       }
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                                       | NFS_INO_INVALID_LABEL
+                                       | NFS_INO_INVALID_DATA
+                                       | NFS_INO_INVALID_ACCESS
+                                       | NFS_INO_INVALID_ACL
+                                       | NFS_INO_REVAL_PAGECACHE;
+       } else
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                                       | NFS_INO_INVALID_LABEL
+                                       | NFS_INO_INVALID_ACCESS
+                                       | NFS_INO_INVALID_ACL
+                                       | NFS_INO_REVAL_PAGECACHE;
 }
 
 void nfs_zap_caches(struct inode *inode)
@@ -257,12 +264,72 @@ nfs_init_locked(struct inode *inode, void *opaque)
        return 0;
 }
 
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
+                                       struct nfs4_label *label)
+{
+       int error;
+
+       if (label == NULL)
+               return;
+
+       if (nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL) == 0)
+               return;
+
+       if (NFS_SERVER(inode)->nfs_client->cl_minorversion < 2)
+               return;
+
+       if ((fattr->valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL) && inode->i_security) {
+               error = security_inode_notifysecctx(inode, label->label,
+                               label->len);
+               if (error)
+                       printk(KERN_ERR "%s() %s %d "
+                                       "security_inode_notifysecctx() %d\n",
+                                       __func__,
+                                       (char *)label->label,
+                                       label->len, error);
+       }
+}
+
+struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags)
+{
+       struct nfs4_label *label = NULL;
+       int minor_version = server->nfs_client->cl_minorversion;
+
+       if (minor_version < 2)
+               return label;
+
+       if (!(server->caps & NFS_CAP_SECURITY_LABEL))
+               return label;
+
+       label = kzalloc(sizeof(struct nfs4_label), flags);
+       if (label == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       label->label = kzalloc(NFS4_MAXLABELLEN, flags);
+       if (label->label == NULL) {
+               kfree(label);
+               return ERR_PTR(-ENOMEM);
+       }
+       label->len = NFS4_MAXLABELLEN;
+
+       return label;
+}
+EXPORT_SYMBOL_GPL(nfs4_label_alloc);
+#else
+void inline nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
+                                       struct nfs4_label *label)
+{
+}
+#endif
+EXPORT_SYMBOL_GPL(nfs_setsecurity);
+
 /*
  * This is our front-end to iget that looks up inodes by file handle
  * instead of inode number.
  */
 struct inode *
-nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
+nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs_find_desc desc = {
                .fh     = fh,
@@ -384,6 +451,9 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                         */
                        inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
                }
+
+               nfs_setsecurity(inode, fattr, label);
+
                nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
                nfsi->attrtimeo_timestamp = now;
                nfsi->access_cache = RB_ROOT;
@@ -393,6 +463,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                unlock_new_inode(inode);
        } else
                nfs_refresh_inode(inode, fattr);
+               nfs_setsecurity(inode, fattr, label);
        dprintk("NFS: nfs_fhget(%s/%Ld fh_crc=0x%08x ct=%d)\n",
                inode->i_sb->s_id,
                (long long)NFS_FILEID(inode),
@@ -449,7 +520,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
                NFS_PROTO(inode)->return_delegation(inode);
        error = NFS_PROTO(inode)->setattr(dentry, fattr, attr);
        if (error == 0)
-               nfs_refresh_inode(inode, fattr);
+               error = nfs_refresh_inode(inode, fattr);
        nfs_free_fattr(fattr);
 out:
        return error;
@@ -713,16 +784,23 @@ EXPORT_SYMBOL_GPL(put_nfs_open_context);
  * Ensure that mmap has a recent RPC credential for use when writing out
  * shared pages
  */
-void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
+void nfs_inode_attach_open_context(struct nfs_open_context *ctx)
 {
-       struct inode *inode = file_inode(filp);
+       struct inode *inode = ctx->dentry->d_inode;
        struct nfs_inode *nfsi = NFS_I(inode);
 
-       filp->private_data = get_nfs_open_context(ctx);
        spin_lock(&inode->i_lock);
        list_add(&ctx->list, &nfsi->open_files);
        spin_unlock(&inode->i_lock);
 }
+EXPORT_SYMBOL_GPL(nfs_inode_attach_open_context);
+
+void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
+{
+       filp->private_data = get_nfs_open_context(ctx);
+       if (list_empty(&ctx->list))
+               nfs_inode_attach_open_context(ctx);
+}
 EXPORT_SYMBOL_GPL(nfs_file_set_open_context);
 
 /*
@@ -748,10 +826,11 @@ struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_c
 
 static void nfs_file_clear_open_context(struct file *filp)
 {
-       struct inode *inode = file_inode(filp);
        struct nfs_open_context *ctx = nfs_file_open_context(filp);
 
        if (ctx) {
+               struct inode *inode = ctx->dentry->d_inode;
+
                filp->private_data = NULL;
                spin_lock(&inode->i_lock);
                list_move_tail(&ctx->list, &NFS_I(inode)->open_files);
@@ -790,6 +869,7 @@ int
 __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 {
        int              status = -ESTALE;
+       struct nfs4_label *label = NULL;
        struct nfs_fattr *fattr = NULL;
        struct nfs_inode *nfsi = NFS_I(inode);
 
@@ -807,7 +887,14 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                goto out;
 
        nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
-       status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr);
+
+       label = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
+       if (IS_ERR(label)) {
+               status = PTR_ERR(label);
+               goto out;
+       }
+
+       status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr, label);
        if (status != 0) {
                dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
                         inode->i_sb->s_id,
@@ -817,7 +904,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                        if (!S_ISDIR(inode->i_mode))
                                set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
                }
-               goto out;
+               goto err_out;
        }
 
        status = nfs_refresh_inode(inode, fattr);
@@ -825,7 +912,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n",
                         inode->i_sb->s_id,
                         (long long)NFS_FILEID(inode), status);
-               goto out;
+               goto err_out;
        }
 
        if (nfsi->cache_validity & NFS_INO_INVALID_ACL)
@@ -835,7 +922,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                inode->i_sb->s_id,
                (long long)NFS_FILEID(inode));
 
- out:
+err_out:
+       nfs4_label_free(label);
+out:
        nfs_free_fattr(fattr);
        return status;
 }
@@ -863,7 +952,8 @@ static int nfs_attribute_cache_expired(struct inode *inode)
  */
 int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 {
-       if (!(NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATTR)
+       if (!(NFS_I(inode)->cache_validity &
+                       (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
                        && !nfs_attribute_cache_expired(inode))
                return NFS_STALE(inode) ? -ESTALE : 0;
        return __nfs_revalidate_inode(server, inode);
@@ -1243,6 +1333,7 @@ int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
        spin_lock(&inode->i_lock);
        status = nfs_post_op_update_inode_locked(inode, fattr);
        spin_unlock(&inode->i_lock);
+
        return status;
 }
 EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
@@ -1483,7 +1574,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                inode->i_blocks = fattr->du.nfs2.blocks;
 
        /* Update attrtimeo value if we're out of the unstable period */
-       if (invalid & NFS_INO_INVALID_ATTR) {
+       if (invalid & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) {
                nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
                nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
                nfsi->attrtimeo_timestamp = now;
@@ -1496,6 +1587,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                }
        }
        invalid &= ~NFS_INO_INVALID_ATTR;
+       invalid &= ~NFS_INO_INVALID_LABEL;
        /* Don't invalidate the data if we were to blame */
        if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
                                || S_ISLNK(inode->i_mode)))
@@ -1638,12 +1730,11 @@ EXPORT_SYMBOL_GPL(nfs_net_id);
 static int nfs_net_init(struct net *net)
 {
        nfs_clients_init(net);
-       return nfs_dns_resolver_cache_init(net);
+       return 0;
 }
 
 static void nfs_net_exit(struct net *net)
 {
-       nfs_dns_resolver_cache_destroy(net);
        nfs_cleanup_cb_ident_idr(net);
 }
 
@@ -1661,10 +1752,6 @@ static int __init init_nfs_fs(void)
 {
        int err;
 
-       err = nfs_dns_resolver_init();
-       if (err < 0)
-               goto out10;;
-
        err = register_pernet_subsys(&nfs_net_ops);
        if (err < 0)
                goto out9;
@@ -1730,8 +1817,6 @@ out7:
 out8:
        unregister_pernet_subsys(&nfs_net_ops);
 out9:
-       nfs_dns_resolver_destroy();
-out10:
        return err;
 }
 
@@ -1744,7 +1829,6 @@ static void __exit exit_nfs_fs(void)
        nfs_destroy_nfspagecache();
        nfs_fscache_unregister();
        unregister_pernet_subsys(&nfs_net_ops);
-       nfs_dns_resolver_destroy();
 #ifdef CONFIG_PROC_FS
        rpc_proc_unregister(&init_net, "nfs");
 #endif
index 91e59a3..3c8373f 100644 (file)
@@ -165,7 +165,7 @@ extern void nfs_free_client(struct nfs_client *);
 extern struct nfs_client *nfs4_find_client_ident(struct net *, int);
 extern struct nfs_client *
 nfs4_find_client_sessionid(struct net *, const struct sockaddr *,
-                               struct nfs4_sessionid *);
+                               struct nfs4_sessionid *, u32);
 extern struct nfs_server *nfs_create_server(struct nfs_mount_info *,
                                        struct nfs_subversion *);
 extern struct nfs_server *nfs4_create_server(
@@ -255,6 +255,7 @@ extern int nfs4_decode_dirent(struct xdr_stream *,
 #ifdef CONFIG_NFS_V4_1
 extern const u32 nfs41_maxread_overhead;
 extern const u32 nfs41_maxwrite_overhead;
+extern const u32 nfs41_maxgetdevinfo_overhead;
 #endif
 
 /* nfs4proc.c */
index 91a6faf..99a4528 100644 (file)
@@ -139,7 +139,10 @@ struct mnt_fhstatus {
  * nfs_mount - Obtain an NFS file handle for the given host and path
  * @info: pointer to mount request arguments
  *
- * Uses default timeout parameters specified by underlying transport.
+ * Uses default timeout parameters specified by underlying transport. On
+ * successful return, the auth_flavs list and auth_flav_len will be populated
+ * with the list from the server or a faked-up list if the server didn't
+ * provide one.
  */
 int nfs_mount(struct nfs_mount_request *info)
 {
@@ -195,6 +198,15 @@ int nfs_mount(struct nfs_mount_request *info)
        dprintk("NFS: MNT request succeeded\n");
        status = 0;
 
+       /*
+        * If the server didn't provide a flavor list, allow the
+        * client to try any flavor.
+        */
+       if (info->version != NFS_MNT3_VERSION || *info->auth_flav_len == 0) {
+               dprintk("NFS: Faking up auth_flavs list\n");
+               info->auth_flavs[0] = RPC_AUTH_NULL;
+               *info->auth_flav_len = 1;
+       }
 out:
        return status;
 
index fc8dc20..348b535 100644 (file)
@@ -280,7 +280,7 @@ struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry,
        struct dentry *parent = dget_parent(dentry);
 
        /* Look it up again to get its attributes */
-       err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &dentry->d_name, fh, fattr);
+       err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &dentry->d_name, fh, fattr, NULL);
        dput(parent);
        if (err != 0)
                return ERR_PTR(err);
index ce90eb4..f5c84c3 100644 (file)
@@ -98,7 +98,7 @@ nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
  */
 static int
 nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
-               struct nfs_fattr *fattr)
+               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct rpc_message msg = {
                .rpc_proc       = &nfs3_procedures[NFS3PROC_GETATTR],
@@ -143,7 +143,8 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
 
 static int
 nfs3_proc_lookup(struct inode *dir, struct qstr *name,
-                struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+                struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+                struct nfs4_label *label)
 {
        struct nfs3_diropargs   arg = {
                .fh             = NFS_FH(dir),
@@ -300,7 +301,7 @@ static int nfs3_do_create(struct inode *dir, struct dentry *dentry, struct nfs3_
        status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0);
        nfs_post_op_update_inode(dir, data->res.dir_attr);
        if (status == 0)
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL);
        return status;
 }
 
index a1dd768..ee81e35 100644 (file)
@@ -194,7 +194,7 @@ struct nfs4_state_recovery_ops {
        int (*recover_lock)(struct nfs4_state *, struct file_lock *);
        int (*establish_clid)(struct nfs_client *, struct rpc_cred *);
        struct rpc_cred * (*get_clid_cred)(struct nfs_client *);
-       int (*reclaim_complete)(struct nfs_client *);
+       int (*reclaim_complete)(struct nfs_client *, struct rpc_cred *);
        int (*detect_trunking)(struct nfs_client *, struct nfs_client **,
                struct rpc_cred *);
 };
@@ -303,10 +303,10 @@ is_ds_client(struct nfs_client *clp)
 extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[];
 
 extern const u32 nfs4_fattr_bitmap[3];
-extern const u32 nfs4_statfs_bitmap[2];
-extern const u32 nfs4_pathconf_bitmap[2];
+extern const u32 nfs4_statfs_bitmap[3];
+extern const u32 nfs4_pathconf_bitmap[3];
 extern const u32 nfs4_fsinfo_bitmap[3];
-extern const u32 nfs4_fs_locations_bitmap[2];
+extern const u32 nfs4_fs_locations_bitmap[3];
 
 void nfs4_free_client(struct nfs_client *);
 
index 4cbad5d..90dce91 100644 (file)
@@ -66,6 +66,11 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
        if (err)
                goto error;
 
+       if (cl_init->minorversion > NFS4_MAX_MINOR_VERSION) {
+               err = -EINVAL;
+               goto error;
+       }
+
        spin_lock_init(&clp->cl_lock);
        INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
        rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
@@ -562,14 +567,14 @@ static bool nfs4_cb_match_client(const struct sockaddr *addr,
  */
 struct nfs_client *
 nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
-                          struct nfs4_sessionid *sid)
+                          struct nfs4_sessionid *sid, u32 minorversion)
 {
        struct nfs_client *clp;
        struct nfs_net *nn = net_generic(net, nfs_net_id);
 
        spin_lock(&nn->nfs_client_lock);
        list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
-               if (nfs4_cb_match_client(addr, clp, 1) == false)
+               if (nfs4_cb_match_client(addr, clp, minorversion) == false)
                        continue;
 
                if (!nfs4_has_session(clp))
@@ -592,7 +597,7 @@ nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
 
 struct nfs_client *
 nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
-                          struct nfs4_sessionid *sid)
+                          struct nfs4_sessionid *sid, u32 minorversion)
 {
        return NULL;
 }
@@ -626,6 +631,8 @@ static int nfs4_set_client(struct nfs_server *server,
 
        if (server->flags & NFS_MOUNT_NORESVPORT)
                set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
+       if (server->options & NFS_OPTION_MIGRATION)
+               set_bit(NFS_CS_MIGRATION, &cl_init.init_flags);
 
        /* Allocate or find a client reference we can use */
        clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour);
@@ -730,7 +737,7 @@ static int nfs4_server_common_setup(struct nfs_server *server,
                return -ENOMEM;
 
        /* We must ensure the session is initialised first */
-       error = nfs4_init_session(server);
+       error = nfs4_init_session(server->nfs_client);
        if (error < 0)
                goto out;
 
index 13e6bb3..e5b804d 100644 (file)
@@ -69,7 +69,6 @@ nfs4_file_open(struct inode *inode, struct file *filp)
                        goto out_drop;
                }
        }
-       iput(inode);
        if (inode != dentry->d_inode)
                goto out_drop;
 
index 22d1062..17ed87e 100644 (file)
@@ -643,7 +643,8 @@ filelayout_check_layout(struct pnfs_layout_hdr *lo,
        d = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode)->pnfs_curr_ld,
                                   NFS_SERVER(lo->plh_inode)->nfs_client, id);
        if (d == NULL) {
-               dsaddr = filelayout_get_device_info(lo->plh_inode, id, gfp_flags);
+               dsaddr = filelayout_get_device_info(lo->plh_inode, id,
+                               lo->plh_lc_cred, gfp_flags);
                if (dsaddr == NULL)
                        goto out;
        } else
index 235ff95..cebd20e 100644 (file)
@@ -150,6 +150,7 @@ struct nfs4_pnfs_ds *nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg,
 extern void nfs4_fl_put_deviceid(struct nfs4_file_layout_dsaddr *dsaddr);
 extern void nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr);
 struct nfs4_file_layout_dsaddr *
-filelayout_get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_flags);
+filelayout_get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id,
+               struct rpc_cred *cred, gfp_t gfp_flags);
 
 #endif /* FS_NFS_NFS4FILELAYOUT_H */
index 661a0f6..95604f6 100644 (file)
@@ -668,7 +668,10 @@ decode_and_add_device(struct inode *inode, struct pnfs_device *dev, gfp_t gfp_fl
  * of available devices, and return it.
  */
 struct nfs4_file_layout_dsaddr *
-filelayout_get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_flags)
+filelayout_get_device_info(struct inode *inode,
+               struct nfs4_deviceid *dev_id,
+               struct rpc_cred *cred,
+               gfp_t gfp_flags)
 {
        struct pnfs_device *pdev = NULL;
        u32 max_resp_sz;
@@ -708,8 +711,9 @@ filelayout_get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gf
        pdev->pgbase = 0;
        pdev->pglen = max_resp_sz;
        pdev->mincount = 0;
+       pdev->maxcount = max_resp_sz - nfs41_maxgetdevinfo_overhead;
 
-       rc = nfs4_proc_getdeviceinfo(server, pdev);
+       rc = nfs4_proc_getdeviceinfo(server, pdev, cred);
        dprintk("%s getdevice info returns %d\n", __func__, rc);
        if (rc)
                goto out_free;
index 28241a4..cf11799 100644 (file)
@@ -77,15 +77,68 @@ static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
 static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
 static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
 static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
-static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *);
-static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
+static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *label);
+static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label);
 static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                            struct nfs_fattr *fattr, struct iattr *sattr,
-                           struct nfs4_state *state);
+                           struct nfs4_state *state, struct nfs4_label *ilabel,
+                           struct nfs4_label *olabel);
 #ifdef CONFIG_NFS_V4_1
-static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *);
-static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *);
+static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
+               struct rpc_cred *);
+static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *,
+               struct rpc_cred *);
 #endif
+
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+static inline struct nfs4_label *
+nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
+       struct iattr *sattr, struct nfs4_label *label)
+{
+       int err;
+
+       if (label == NULL)
+               return NULL;
+
+       if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL) == 0)
+               return NULL;
+
+       if (NFS_SERVER(dir)->nfs_client->cl_minorversion < 2)
+               return NULL;
+
+       err = security_dentry_init_security(dentry, sattr->ia_mode,
+                               &dentry->d_name, (void **)&label->label, &label->len);
+       if (err == 0)
+               return label;
+
+       return NULL;
+}
+static inline void
+nfs4_label_release_security(struct nfs4_label *label)
+{
+       if (label)
+               security_release_secctx(label->label, label->len);
+}
+static inline u32 *nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label)
+{
+       if (label)
+               return server->attr_bitmask;
+
+       return server->attr_bitmask_nl;
+}
+#else
+static inline struct nfs4_label *
+nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
+       struct iattr *sattr, struct nfs4_label *l)
+{ return NULL; }
+static inline void
+nfs4_label_release_security(struct nfs4_label *label)
+{ return; }
+static inline u32 *
+nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label)
+{ return server->attr_bitmask; }
+#endif
+
 /* Prevent leaks of NFSv4 errors into userland */
 static int nfs4_map_errors(int err)
 {
@@ -134,7 +187,10 @@ const u32 nfs4_fattr_bitmap[3] = {
        | FATTR4_WORD1_SPACE_USED
        | FATTR4_WORD1_TIME_ACCESS
        | FATTR4_WORD1_TIME_METADATA
-       | FATTR4_WORD1_TIME_MODIFY
+       | FATTR4_WORD1_TIME_MODIFY,
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+       FATTR4_WORD2_SECURITY_LABEL
+#endif
 };
 
 static const u32 nfs4_pnfs_open_bitmap[3] = {
@@ -161,7 +217,7 @@ static const u32 nfs4_open_noattr_bitmap[3] = {
        | FATTR4_WORD0_FILEID,
 };
 
-const u32 nfs4_statfs_bitmap[2] = {
+const u32 nfs4_statfs_bitmap[3] = {
        FATTR4_WORD0_FILES_AVAIL
        | FATTR4_WORD0_FILES_FREE
        | FATTR4_WORD0_FILES_TOTAL,
@@ -170,7 +226,7 @@ const u32 nfs4_statfs_bitmap[2] = {
        | FATTR4_WORD1_SPACE_TOTAL
 };
 
-const u32 nfs4_pathconf_bitmap[2] = {
+const u32 nfs4_pathconf_bitmap[3] = {
        FATTR4_WORD0_MAXLINK
        | FATTR4_WORD0_MAXNAME,
        0
@@ -185,7 +241,7 @@ const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE
                        FATTR4_WORD2_LAYOUT_BLKSIZE
 };
 
-const u32 nfs4_fs_locations_bitmap[2] = {
+const u32 nfs4_fs_locations_bitmap[3] = {
        FATTR4_WORD0_TYPE
        | FATTR4_WORD0_CHANGE
        | FATTR4_WORD0_SIZE
@@ -201,7 +257,7 @@ const u32 nfs4_fs_locations_bitmap[2] = {
        | FATTR4_WORD1_TIME_ACCESS
        | FATTR4_WORD1_TIME_METADATA
        | FATTR4_WORD1_TIME_MODIFY
-       | FATTR4_WORD1_MOUNTED_ON_FILEID
+       | FATTR4_WORD1_MOUNTED_ON_FILEID,
 };
 
 static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dentry,
@@ -762,6 +818,7 @@ struct nfs4_opendata {
        struct nfs4_string owner_name;
        struct nfs4_string group_name;
        struct nfs_fattr f_attr;
+       struct nfs4_label *f_label;
        struct dentry *dir;
        struct dentry *dentry;
        struct nfs4_state_owner *owner;
@@ -807,6 +864,7 @@ nfs4_map_atomic_open_claim(struct nfs_server *server,
 static void nfs4_init_opendata_res(struct nfs4_opendata *p)
 {
        p->o_res.f_attr = &p->f_attr;
+       p->o_res.f_label = p->f_label;
        p->o_res.seqid = p->o_arg.seqid;
        p->c_res.seqid = p->c_arg.seqid;
        p->o_res.server = p->o_arg.server;
@@ -818,6 +876,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
 static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
                struct nfs4_state_owner *sp, fmode_t fmode, int flags,
                const struct iattr *attrs,
+               struct nfs4_label *label,
                enum open_claim_type4 claim,
                gfp_t gfp_mask)
 {
@@ -829,9 +888,14 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        p = kzalloc(sizeof(*p), gfp_mask);
        if (p == NULL)
                goto err;
+
+       p->f_label = nfs4_label_alloc(server, gfp_mask);
+       if (IS_ERR(p->f_label))
+               goto err_free_p;
+
        p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask);
        if (p->o_arg.seqid == NULL)
-               goto err_free;
+               goto err_free_label;
        nfs_sb_active(dentry->d_sb);
        p->dentry = dget(dentry);
        p->dir = parent;
@@ -852,8 +916,9 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        p->o_arg.id.uniquifier = sp->so_seqid.owner_id;
        p->o_arg.name = &dentry->d_name;
        p->o_arg.server = server;
-       p->o_arg.bitmask = server->attr_bitmask;
+       p->o_arg.bitmask = nfs4_bitmask(server, label);
        p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0];
+       p->o_arg.label = label;
        p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim);
        switch (p->o_arg.claim) {
        case NFS4_OPEN_CLAIM_NULL:
@@ -884,7 +949,10 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        nfs4_init_opendata_res(p);
        kref_init(&p->kref);
        return p;
-err_free:
+
+err_free_label:
+       nfs4_label_free(p->f_label);
+err_free_p:
        kfree(p);
 err:
        dput(parent);
@@ -901,6 +969,9 @@ static void nfs4_opendata_free(struct kref *kref)
        if (p->state != NULL)
                nfs4_put_open_state(p->state);
        nfs4_put_state_owner(p->owner);
+
+       nfs4_label_free(p->f_label);
+
        dput(p->dir);
        dput(p->dentry);
        nfs_sb_deactive(sb);
@@ -1179,6 +1250,8 @@ _nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data)
        if (ret)
                goto err;
 
+       nfs_setsecurity(inode, &data->f_attr, data->f_label);
+
        if (data->o_res.delegation_type != 0)
                nfs4_opendata_check_deleg(data, state);
        update_open_stateid(state, &data->o_res.stateid, NULL,
@@ -1205,7 +1278,7 @@ _nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
        ret = -EAGAIN;
        if (!(data->f_attr.valid & NFS_ATTR_FATTR))
                goto err;
-       inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr);
+       inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr, data->f_label);
        ret = PTR_ERR(inode);
        if (IS_ERR(inode))
                goto err;
@@ -1258,7 +1331,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
        struct nfs4_opendata *opendata;
 
        opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0,
-                       NULL, claim, GFP_NOFS);
+                       NULL, NULL, claim, GFP_NOFS);
        if (opendata == NULL)
                return ERR_PTR(-ENOMEM);
        opendata->state = state;
@@ -1784,7 +1857,7 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
                        return status;
        }
        if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
-               _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr);
+               _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr, o_res->f_label);
        return 0;
 }
 
@@ -1855,18 +1928,30 @@ static void nfs41_clear_delegation_stateid(struct nfs4_state *state)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
        nfs4_stateid *stateid = &state->stateid;
-       int status;
+       struct nfs_delegation *delegation;
+       struct rpc_cred *cred = NULL;
+       int status = -NFS4ERR_BAD_STATEID;
 
        /* If a state reset has been done, test_stateid is unneeded */
        if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
                return;
 
-       status = nfs41_test_stateid(server, stateid);
+       /* Get the delegation credential for use by test/free_stateid */
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(state->inode)->delegation);
+       if (delegation != NULL &&
+           nfs4_stateid_match(&delegation->stateid, stateid)) {
+               cred = get_rpccred(delegation->cred);
+               rcu_read_unlock();
+               status = nfs41_test_stateid(server, stateid, cred);
+       } else
+               rcu_read_unlock();
+
        if (status != NFS_OK) {
                /* Free the stateid unless the server explicitly
                 * informs us the stateid is unrecognized. */
                if (status != -NFS4ERR_BAD_STATEID)
-                       nfs41_free_stateid(server, stateid);
+                       nfs41_free_stateid(server, stateid, cred);
                nfs_remove_bad_delegation(state->inode);
 
                write_seqlock(&state->seqlock);
@@ -1874,6 +1959,9 @@ static void nfs41_clear_delegation_stateid(struct nfs4_state *state)
                write_sequnlock(&state->seqlock);
                clear_bit(NFS_DELEGATED_STATE, &state->flags);
        }
+
+       if (cred != NULL)
+               put_rpccred(cred);
 }
 
 /**
@@ -1888,6 +1976,7 @@ static int nfs41_check_open_stateid(struct nfs4_state *state)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
        nfs4_stateid *stateid = &state->open_stateid;
+       struct rpc_cred *cred = state->owner->so_cred;
        int status;
 
        /* If a state reset has been done, test_stateid is unneeded */
@@ -1896,12 +1985,12 @@ static int nfs41_check_open_stateid(struct nfs4_state *state)
            (test_bit(NFS_O_RDWR_STATE, &state->flags) == 0))
                return -NFS4ERR_BAD_STATEID;
 
-       status = nfs41_test_stateid(server, stateid);
+       status = nfs41_test_stateid(server, stateid, cred);
        if (status != NFS_OK) {
                /* Free the stateid unless the server explicitly
                 * informs us the stateid is unrecognized. */
                if (status != -NFS4ERR_BAD_STATEID)
-                       nfs41_free_stateid(server, stateid);
+                       nfs41_free_stateid(server, stateid, cred);
 
                clear_bit(NFS_O_RDONLY_STATE, &state->flags);
                clear_bit(NFS_O_WRONLY_STATE, &state->flags);
@@ -1942,10 +2031,11 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct
 static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
                fmode_t fmode,
                int flags,
-               struct nfs4_state **res)
+               struct nfs_open_context *ctx)
 {
        struct nfs4_state_owner *sp = opendata->owner;
        struct nfs_server *server = sp->so_server;
+       struct dentry *dentry;
        struct nfs4_state *state;
        unsigned int seq;
        int ret;
@@ -1963,13 +2053,31 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
        if (server->caps & NFS_CAP_POSIX_LOCK)
                set_bit(NFS_STATE_POSIX_LOCKS, &state->flags);
 
+       dentry = opendata->dentry;
+       if (dentry->d_inode == NULL) {
+               /* FIXME: Is this d_drop() ever needed? */
+               d_drop(dentry);
+               dentry = d_add_unique(dentry, igrab(state->inode));
+               if (dentry == NULL) {
+                       dentry = opendata->dentry;
+               } else if (dentry != ctx->dentry) {
+                       dput(ctx->dentry);
+                       ctx->dentry = dget(dentry);
+               }
+               nfs_set_verifier(dentry,
+                               nfs_save_change_attribute(opendata->dir->d_inode));
+       }
+
        ret = nfs4_opendata_access(sp->so_cred, opendata, state, fmode, flags);
        if (ret != 0)
                goto out;
 
-       if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
-               nfs4_schedule_stateid_recovery(server, state);
-       *res = state;
+       ctx->state = state;
+       if (dentry->d_inode == state->inode) {
+               nfs_inode_attach_open_context(ctx);
+               if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
+                       nfs4_schedule_stateid_recovery(server, state);
+       }
 out:
        return ret;
 }
@@ -1978,19 +2086,21 @@ out:
  * Returns a referenced nfs4_state
  */
 static int _nfs4_do_open(struct inode *dir,
-                       struct dentry *dentry,
-                       fmode_t fmode,
+                       struct nfs_open_context *ctx,
                        int flags,
                        struct iattr *sattr,
-                       struct rpc_cred *cred,
-                       struct nfs4_state **res,
-                       struct nfs4_threshold **ctx_th)
+                       struct nfs4_label *label)
 {
        struct nfs4_state_owner  *sp;
        struct nfs4_state     *state = NULL;
        struct nfs_server       *server = NFS_SERVER(dir);
        struct nfs4_opendata *opendata;
+       struct dentry *dentry = ctx->dentry;
+       struct rpc_cred *cred = ctx->cred;
+       struct nfs4_threshold **ctx_th = &ctx->mdsthreshold;
+       fmode_t fmode = ctx->mode & (FMODE_READ|FMODE_WRITE|FMODE_EXEC);
        enum open_claim_type4 claim = NFS4_OPEN_CLAIM_NULL;
+       struct nfs4_label *olabel = NULL;
        int status;
 
        /* Protect against reboot recovery conflicts */
@@ -2009,22 +2119,31 @@ static int _nfs4_do_open(struct inode *dir,
        if (dentry->d_inode)
                claim = NFS4_OPEN_CLAIM_FH;
        opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr,
-                       claim, GFP_KERNEL);
+                       label, claim, GFP_KERNEL);
        if (opendata == NULL)
                goto err_put_state_owner;
 
+       if (label) {
+               olabel = nfs4_label_alloc(server, GFP_KERNEL);
+               if (IS_ERR(olabel)) {
+                       status = PTR_ERR(olabel);
+                       goto err_opendata_put;
+               }
+       }
+
        if (ctx_th && server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) {
                opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc();
                if (!opendata->f_attr.mdsthreshold)
-                       goto err_opendata_put;
+                       goto err_free_label;
                opendata->o_arg.open_bitmap = &nfs4_pnfs_open_bitmap[0];
        }
        if (dentry->d_inode != NULL)
                opendata->state = nfs4_get_open_state(dentry->d_inode, sp);
 
-       status = _nfs4_open_and_get_state(opendata, fmode, flags, &state);
+       status = _nfs4_open_and_get_state(opendata, fmode, flags, ctx);
        if (status != 0)
-               goto err_opendata_put;
+               goto err_free_label;
+       state = ctx->state;
 
        if ((opendata->o_arg.open_flags & O_EXCL) &&
            (opendata->o_arg.createmode != NFS4_CREATE_GUARDED)) {
@@ -2033,10 +2152,12 @@ static int _nfs4_do_open(struct inode *dir,
                nfs_fattr_init(opendata->o_res.f_attr);
                status = nfs4_do_setattr(state->inode, cred,
                                opendata->o_res.f_attr, sattr,
-                               state);
-               if (status == 0)
+                               state, label, olabel);
+               if (status == 0) {
                        nfs_setattr_update_inode(state->inode, sattr);
-               nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
+                       nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
+                       nfs_setsecurity(state->inode, opendata->o_res.f_attr, olabel);
+               }
        }
 
        if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server))
@@ -2045,38 +2166,37 @@ static int _nfs4_do_open(struct inode *dir,
                kfree(opendata->f_attr.mdsthreshold);
        opendata->f_attr.mdsthreshold = NULL;
 
+       nfs4_label_free(olabel);
+
        nfs4_opendata_put(opendata);
        nfs4_put_state_owner(sp);
-       *res = state;
        return 0;
+err_free_label:
+       nfs4_label_free(olabel);
 err_opendata_put:
        kfree(opendata->f_attr.mdsthreshold);
        nfs4_opendata_put(opendata);
 err_put_state_owner:
        nfs4_put_state_owner(sp);
 out_err:
-       *res = NULL;
        return status;
 }
 
 
 static struct nfs4_state *nfs4_do_open(struct inode *dir,
-                                       struct dentry *dentry,
-                                       fmode_t fmode,
+                                       struct nfs_open_context *ctx,
                                        int flags,
                                        struct iattr *sattr,
-                                       struct rpc_cred *cred,
-                                       struct nfs4_threshold **ctx_th)
+                                       struct nfs4_label *label)
 {
        struct nfs_server *server = NFS_SERVER(dir);
        struct nfs4_exception exception = { };
        struct nfs4_state *res;
        int status;
 
-       fmode &= FMODE_READ|FMODE_WRITE|FMODE_EXEC;
        do {
-               status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred,
-                                      &res, ctx_th);
+               status = _nfs4_do_open(dir, ctx, flags, sattr, label);
+               res = ctx->state;
                if (status == 0)
                        break;
                /* NOTE: BAD_SEQID means the server and client disagree about the
@@ -2122,7 +2242,8 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
 
 static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                            struct nfs_fattr *fattr, struct iattr *sattr,
-                           struct nfs4_state *state)
+                           struct nfs4_state *state, struct nfs4_label *ilabel,
+                           struct nfs4_label *olabel)
 {
        struct nfs_server *server = NFS_SERVER(inode);
         struct nfs_setattrargs  arg = {
@@ -2130,9 +2251,11 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                 .iap            = sattr,
                .server         = server,
                .bitmask = server->attr_bitmask,
+               .label          = ilabel,
         };
         struct nfs_setattrres  res = {
                .fattr          = fattr,
+               .label          = olabel,
                .server         = server,
         };
         struct rpc_message msg = {
@@ -2146,6 +2269,10 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        bool truncate;
        int status;
 
+       arg.bitmask = nfs4_bitmask(server, ilabel);
+       if (ilabel)
+               arg.bitmask = nfs4_bitmask(server, olabel);
+
        nfs_fattr_init(fattr);
 
        /* Servers should only apply open mode checks for file size changes */
@@ -2172,7 +2299,8 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
 
 static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                           struct nfs_fattr *fattr, struct iattr *sattr,
-                          struct nfs4_state *state)
+                          struct nfs4_state *state, struct nfs4_label *ilabel,
+                          struct nfs4_label *olabel)
 {
        struct nfs_server *server = NFS_SERVER(inode);
        struct nfs4_exception exception = {
@@ -2181,7 +2309,7 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        };
        int err;
        do {
-               err = _nfs4_do_setattr(inode, cred, fattr, sattr, state);
+               err = _nfs4_do_setattr(inode, cred, fattr, sattr, state, ilabel, olabel);
                switch (err) {
                case -NFS4ERR_OPENMODE:
                        if (!(sattr->ia_valid & ATTR_SIZE)) {
@@ -2426,14 +2554,18 @@ static struct inode *
 nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr)
 {
        struct nfs4_state *state;
+       struct nfs4_label l = {0, 0, 0, NULL}, *label = NULL;
+
+       label = nfs4_label_init_security(dir, ctx->dentry, attr, &l);
 
        /* Protect against concurrent sillydeletes */
-       state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr,
-                            ctx->cred, &ctx->mdsthreshold);
+       state = nfs4_do_open(dir, ctx, open_flags, attr, label);
+
+       nfs4_label_release_security(label);
+
        if (IS_ERR(state))
                return ERR_CAST(state);
-       ctx->state = state;
-       return igrab(state->inode);
+       return state->inode;
 }
 
 static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
@@ -2489,7 +2621,17 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
                        server->caps |= NFS_CAP_CTIME;
                if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY)
                        server->caps |= NFS_CAP_MTIME;
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+               if (res.attr_bitmask[2] & FATTR4_WORD2_SECURITY_LABEL)
+                       server->caps |= NFS_CAP_SECURITY_LABEL;
+#endif
+               memcpy(server->attr_bitmask_nl, res.attr_bitmask,
+                               sizeof(server->attr_bitmask));
 
+               if (server->caps & NFS_CAP_SECURITY_LABEL) {
+                       server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+                       res.attr_bitmask[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+               }
                memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
                server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
                server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
@@ -2515,8 +2657,9 @@ int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
 static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
                struct nfs_fsinfo *info)
 {
+       u32 bitmask[3];
        struct nfs4_lookup_root_arg args = {
-               .bitmask = nfs4_fattr_bitmap,
+               .bitmask = bitmask,
        };
        struct nfs4_lookup_res res = {
                .server = server,
@@ -2529,6 +2672,13 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
                .rpc_resp = &res,
        };
 
+       bitmask[0] = nfs4_fattr_bitmap[0];
+       bitmask[1] = nfs4_fattr_bitmap[1];
+       /*
+        * Process the label in the upcoming getfattr
+        */
+       bitmask[2] = nfs4_fattr_bitmap[2] & ~FATTR4_WORD2_SECURITY_LABEL;
+
        nfs_fattr_init(info->fattr);
        return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
 }
@@ -2648,6 +2798,7 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
 {
        int error;
        struct nfs_fattr *fattr = info->fattr;
+       struct nfs4_label *label = NULL;
 
        error = nfs4_server_capabilities(server, mntfh);
        if (error < 0) {
@@ -2655,16 +2806,23 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
                return error;
        }
 
-       error = nfs4_proc_getattr(server, mntfh, fattr);
+       label = nfs4_label_alloc(server, GFP_KERNEL);
+       if (IS_ERR(label))
+               return PTR_ERR(label);
+
+       error = nfs4_proc_getattr(server, mntfh, fattr, label);
        if (error < 0) {
                dprintk("nfs4_get_root: getattr error = %d\n", -error);
-               return error;
+               goto err_free_label;
        }
 
        if (fattr->valid & NFS_ATTR_FATTR_FSID &&
            !nfs_fsid_equal(&server->fsid, &fattr->fsid))
                memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
 
+err_free_label:
+       nfs4_label_free(label);
+
        return error;
 }
 
@@ -2711,7 +2869,8 @@ out:
        return status;
 }
 
-static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+                               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs4_getattr_arg args = {
                .fh = fhandle,
@@ -2719,6 +2878,7 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
        };
        struct nfs4_getattr_res res = {
                .fattr = fattr,
+               .label = label,
                .server = server,
        };
        struct rpc_message msg = {
@@ -2726,18 +2886,21 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
-       
+
+       args.bitmask = nfs4_bitmask(server, label);
+
        nfs_fattr_init(fattr);
        return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
 }
 
-static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+                               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs4_exception exception = { };
        int err;
        do {
                err = nfs4_handle_exception(server,
-                               _nfs4_proc_getattr(server, fhandle, fattr),
+                               _nfs4_proc_getattr(server, fhandle, fattr, label),
                                &exception);
        } while (exception.retry);
        return err;
@@ -2767,6 +2930,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
        struct inode *inode = dentry->d_inode;
        struct rpc_cred *cred = NULL;
        struct nfs4_state *state = NULL;
+       struct nfs4_label *label = NULL;
        int status;
 
        if (pnfs_ld_layoutret_on_setattr(inode))
@@ -2793,15 +2957,22 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
                }
        }
 
-       status = nfs4_do_setattr(inode, cred, fattr, sattr, state);
-       if (status == 0)
+       label = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
+       if (IS_ERR(label))
+               return PTR_ERR(label);
+
+       status = nfs4_do_setattr(inode, cred, fattr, sattr, state, NULL, label);
+       if (status == 0) {
                nfs_setattr_update_inode(inode, sattr);
+               nfs_setsecurity(inode, fattr, label);
+       }
+       nfs4_label_free(label);
        return status;
 }
 
 static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
                const struct qstr *name, struct nfs_fh *fhandle,
-               struct nfs_fattr *fattr)
+               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs_server *server = NFS_SERVER(dir);
        int                    status;
@@ -2813,6 +2984,7 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
        struct nfs4_lookup_res res = {
                .server = server,
                .fattr = fattr,
+               .label = label,
                .fh = fhandle,
        };
        struct rpc_message msg = {
@@ -2821,6 +2993,8 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
                .rpc_resp = &res,
        };
 
+       args.bitmask = nfs4_bitmask(server, label);
+
        nfs_fattr_init(fattr);
 
        dprintk("NFS call  lookup %s\n", name->name);
@@ -2839,13 +3013,13 @@ static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr)
 
 static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
                                   struct qstr *name, struct nfs_fh *fhandle,
-                                  struct nfs_fattr *fattr)
+                                  struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs4_exception exception = { };
        struct rpc_clnt *client = *clnt;
        int err;
        do {
-               err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr);
+               err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr, label);
                switch (err) {
                case -NFS4ERR_BADNAME:
                        err = -ENOENT;
@@ -2879,12 +3053,13 @@ out:
 }
 
 static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
-                           struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+                           struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+                           struct nfs4_label *label)
 {
        int status;
        struct rpc_clnt *client = NFS_CLIENT(dir);
 
-       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
+       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, label);
        if (client != NFS_CLIENT(dir)) {
                rpc_shutdown_client(client);
                nfs_fixup_secinfo_attributes(fattr);
@@ -2899,7 +3074,7 @@ nfs4_proc_lookup_mountpoint(struct inode *dir, struct qstr *name,
        int status;
        struct rpc_clnt *client = rpc_clone_client(NFS_CLIENT(dir));
 
-       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
+       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, NULL);
        if (status < 0) {
                rpc_shutdown_client(client);
                return ERR_PTR(status);
@@ -2924,7 +3099,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
                .rpc_cred = entry->cred,
        };
        int mode = entry->mask;
-       int status;
+       int status = 0;
 
        /*
         * Determine which access bits we want to ask for...
@@ -3029,6 +3204,7 @@ static int
 nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                 int flags)
 {
+       struct nfs4_label l, *ilabel = NULL;
        struct nfs_open_context *ctx;
        struct nfs4_state *state;
        int status = 0;
@@ -3037,19 +3213,16 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
        if (IS_ERR(ctx))
                return PTR_ERR(ctx);
 
+       ilabel = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        sattr->ia_mode &= ~current_umask();
-       state = nfs4_do_open(dir, dentry, ctx->mode,
-                       flags, sattr, ctx->cred,
-                       &ctx->mdsthreshold);
-       d_drop(dentry);
+       state = nfs4_do_open(dir, ctx, flags, sattr, ilabel);
        if (IS_ERR(state)) {
                status = PTR_ERR(state);
                goto out;
        }
-       d_add(dentry, igrab(state->inode));
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-       ctx->state = state;
 out:
+       nfs4_label_release_security(ilabel);
        put_nfs_open_context(ctx);
        return status;
 }
@@ -3098,6 +3271,8 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
        res->server = server;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
        nfs41_init_sequence(&args->seq_args, &res->seq_res, 1);
+
+       nfs_fattr_init(res->dir_attr);
 }
 
 static void nfs4_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data)
@@ -3173,7 +3348,7 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
                .rpc_resp = &res,
        };
        int status = -ENOMEM;
-       
+
        status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(old_dir, &res.old_cinfo);
@@ -3207,6 +3382,7 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
        };
        struct nfs4_link_res res = {
                .server = server,
+               .label = NULL,
        };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK],
@@ -3219,11 +3395,24 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
        if (res.fattr == NULL)
                goto out;
 
+       res.label = nfs4_label_alloc(server, GFP_KERNEL);
+       if (IS_ERR(res.label)) {
+               status = PTR_ERR(res.label);
+               goto out;
+       }
+       arg.bitmask = nfs4_bitmask(server, res.label);
+
        status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(dir, &res.cinfo);
-               nfs_post_op_update_inode(inode, res.fattr);
+               status = nfs_post_op_update_inode(inode, res.fattr);
+               if (!status)
+                       nfs_setsecurity(inode, res.fattr, res.label);
        }
+
+
+       nfs4_label_free(res.label);
+
 out:
        nfs_free_fattr(res.fattr);
        return status;
@@ -3247,6 +3436,7 @@ struct nfs4_createdata {
        struct nfs4_create_res res;
        struct nfs_fh fh;
        struct nfs_fattr fattr;
+       struct nfs4_label *label;
 };
 
 static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
@@ -3258,6 +3448,10 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
        if (data != NULL) {
                struct nfs_server *server = NFS_SERVER(dir);
 
+               data->label = nfs4_label_alloc(server, GFP_KERNEL);
+               if (IS_ERR(data->label))
+                       goto out_free;
+
                data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE];
                data->msg.rpc_argp = &data->arg;
                data->msg.rpc_resp = &data->res;
@@ -3266,13 +3460,17 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
                data->arg.name = name;
                data->arg.attrs = sattr;
                data->arg.ftype = ftype;
-               data->arg.bitmask = server->attr_bitmask;
+               data->arg.bitmask = nfs4_bitmask(server, data->label);
                data->res.server = server;
                data->res.fh = &data->fh;
                data->res.fattr = &data->fattr;
+               data->res.label = data->label;
                nfs_fattr_init(data->res.fattr);
        }
        return data;
+out_free:
+       kfree(data);
+       return NULL;
 }
 
 static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data)
@@ -3281,18 +3479,20 @@ static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_
                                    &data->arg.seq_args, &data->res.seq_res, 1);
        if (status == 0) {
                update_changeattr(dir, &data->res.dir_cinfo);
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, data->res.label);
        }
        return status;
 }
 
 static void nfs4_free_createdata(struct nfs4_createdata *data)
 {
+       nfs4_label_free(data->label);
        kfree(data);
 }
 
 static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
-               struct page *page, unsigned int len, struct iattr *sattr)
+               struct page *page, unsigned int len, struct iattr *sattr,
+               struct nfs4_label *label)
 {
        struct nfs4_createdata *data;
        int status = -ENAMETOOLONG;
@@ -3308,6 +3508,7 @@ static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
        data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK];
        data->arg.u.symlink.pages = &page;
        data->arg.u.symlink.len = len;
+       data->arg.label = label;
        
        status = nfs4_do_create(dir, dentry, data);
 
@@ -3320,18 +3521,24 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
                struct page *page, unsigned int len, struct iattr *sattr)
 {
        struct nfs4_exception exception = { };
+       struct nfs4_label l, *label = NULL;
        int err;
+
+       label = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
                                _nfs4_proc_symlink(dir, dentry, page,
-                                                       len, sattr),
+                                                       len, sattr, label),
                                &exception);
        } while (exception.retry);
+
+       nfs4_label_release_security(label);
        return err;
 }
 
 static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
-               struct iattr *sattr)
+               struct iattr *sattr, struct nfs4_label *label)
 {
        struct nfs4_createdata *data;
        int status = -ENOMEM;
@@ -3340,6 +3547,7 @@ static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
        if (data == NULL)
                goto out;
 
+       data->arg.label = label;
        status = nfs4_do_create(dir, dentry, data);
 
        nfs4_free_createdata(data);
@@ -3351,14 +3559,19 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
                struct iattr *sattr)
 {
        struct nfs4_exception exception = { };
+       struct nfs4_label l, *label = NULL;
        int err;
 
+       label = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        sattr->ia_mode &= ~current_umask();
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
-                               _nfs4_proc_mkdir(dir, dentry, sattr),
+                               _nfs4_proc_mkdir(dir, dentry, sattr, label),
                                &exception);
        } while (exception.retry);
+       nfs4_label_release_security(label);
+
        return err;
 }
 
@@ -3416,7 +3629,7 @@ static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
 }
 
 static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
-               struct iattr *sattr, dev_t rdev)
+               struct iattr *sattr, struct nfs4_label *label, dev_t rdev)
 {
        struct nfs4_createdata *data;
        int mode = sattr->ia_mode;
@@ -3441,7 +3654,8 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
                status = -EINVAL;
                goto out_free;
        }
-       
+
+       data->arg.label = label;
        status = nfs4_do_create(dir, dentry, data);
 out_free:
        nfs4_free_createdata(data);
@@ -3453,14 +3667,20 @@ static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
                struct iattr *sattr, dev_t rdev)
 {
        struct nfs4_exception exception = { };
+       struct nfs4_label l, *label = NULL;
        int err;
 
+       label = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        sattr->ia_mode &= ~current_umask();
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
-                               _nfs4_proc_mknod(dir, dentry, sattr, rdev),
+                               _nfs4_proc_mknod(dir, dentry, sattr, label, rdev),
                                &exception);
        } while (exception.retry);
+
+       nfs4_label_release_security(label);
+
        return err;
 }
 
@@ -4187,6 +4407,155 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
        return err;
 }
 
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+static int _nfs4_get_security_label(struct inode *inode, void *buf,
+                                       size_t buflen)
+{
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct nfs_fattr fattr;
+       struct nfs4_label label = {0, 0, buflen, buf};
+
+       u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL };
+       struct nfs4_getattr_arg args = {
+               .fh             = NFS_FH(inode),
+               .bitmask        = bitmask,
+       };
+       struct nfs4_getattr_res res = {
+               .fattr          = &fattr,
+               .label          = &label,
+               .server         = server,
+       };
+       struct rpc_message msg = {
+               .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_GETATTR],
+               .rpc_argp       = &args,
+               .rpc_resp       = &res,
+       };
+       int ret;
+
+       nfs_fattr_init(&fattr);
+
+       ret = rpc_call_sync(server->client, &msg, 0);
+       if (ret)
+               return ret;
+       if (!(fattr.valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL))
+               return -ENOENT;
+       if (buflen < label.len)
+               return -ERANGE;
+       return 0;
+}
+
+static int nfs4_get_security_label(struct inode *inode, void *buf,
+                                       size_t buflen)
+{
+       struct nfs4_exception exception = { };
+       int err;
+
+       if (!nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL))
+               return -EOPNOTSUPP;
+
+       do {
+               err = nfs4_handle_exception(NFS_SERVER(inode),
+                               _nfs4_get_security_label(inode, buf, buflen),
+                               &exception);
+       } while (exception.retry);
+       return err;
+}
+
+static int _nfs4_do_set_security_label(struct inode *inode,
+               struct nfs4_label *ilabel,
+               struct nfs_fattr *fattr,
+               struct nfs4_label *olabel)
+{
+
+       struct iattr sattr = {0};
+       struct nfs_server *server = NFS_SERVER(inode);
+       const u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL };
+       struct nfs_setattrargs args = {
+               .fh             = NFS_FH(inode),
+               .iap            = &sattr,
+               .server         = server,
+               .bitmask        = bitmask,
+               .label          = ilabel,
+       };
+       struct nfs_setattrres res = {
+               .fattr          = fattr,
+               .label          = olabel,
+               .server         = server,
+       };
+       struct rpc_message msg = {
+               .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
+               .rpc_argp       = &args,
+               .rpc_resp       = &res,
+       };
+       int status;
+
+       nfs4_stateid_copy(&args.stateid, &zero_stateid);
+
+       status = rpc_call_sync(server->client, &msg, 0);
+       if (status)
+               dprintk("%s failed: %d\n", __func__, status);
+
+       return status;
+}
+
+static int nfs4_do_set_security_label(struct inode *inode,
+               struct nfs4_label *ilabel,
+               struct nfs_fattr *fattr,
+               struct nfs4_label *olabel)
+{
+       struct nfs4_exception exception = { };
+       int err;
+
+       do {
+               err = nfs4_handle_exception(NFS_SERVER(inode),
+                               _nfs4_do_set_security_label(inode, ilabel,
+                               fattr, olabel),
+                               &exception);
+       } while (exception.retry);
+       return err;
+}
+
+static int
+nfs4_set_security_label(struct dentry *dentry, const void *buf, size_t buflen)
+{
+       struct nfs4_label ilabel, *olabel = NULL;
+       struct nfs_fattr fattr;
+       struct rpc_cred *cred;
+       struct inode *inode = dentry->d_inode;
+       int status;
+
+       if (!nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL))
+               return -EOPNOTSUPP;
+
+       nfs_fattr_init(&fattr);
+
+       ilabel.pi = 0;
+       ilabel.lfs = 0;
+       ilabel.label = (char *)buf;
+       ilabel.len = buflen;
+
+       cred = rpc_lookup_cred();
+       if (IS_ERR(cred))
+               return PTR_ERR(cred);
+
+       olabel = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
+       if (IS_ERR(olabel)) {
+               status = -PTR_ERR(olabel);
+               goto out;
+       }
+
+       status = nfs4_do_set_security_label(inode, &ilabel, &fattr, olabel);
+       if (status == 0)
+               nfs_setsecurity(inode, &fattr, olabel);
+
+       nfs4_label_free(olabel);
+out:
+       put_rpccred(cred);
+       return status;
+}
+#endif /* CONFIG_NFS_V4_SECURITY_LABEL */
+
+
 static int
 nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
 {
@@ -4345,7 +4714,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
        /* cb_client4 */
        rcu_read_lock();
        setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
-                               sizeof(setclientid.sc_netid),
+                               sizeof(setclientid.sc_netid), "%s",
                                rpc_peeraddr2str(clp->cl_rpcclient,
                                                        RPC_DISPLAY_NETID));
        rcu_read_unlock();
@@ -5056,13 +5425,18 @@ static int nfs41_check_expired_locks(struct nfs4_state *state)
 
        list_for_each_entry(lsp, &state->lock_states, ls_locks) {
                if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
-                       status = nfs41_test_stateid(server, &lsp->ls_stateid);
+                       struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
+
+                       status = nfs41_test_stateid(server,
+                                       &lsp->ls_stateid,
+                                       cred);
                        if (status != NFS_OK) {
                                /* Free the stateid unless the server
                                 * informs us the stateid is unrecognized. */
                                if (status != -NFS4ERR_BAD_STATEID)
                                        nfs41_free_stateid(server,
-                                                       &lsp->ls_stateid);
+                                                       &lsp->ls_stateid,
+                                                       cred);
                                clear_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags);
                                ret = status;
                        }
@@ -5295,6 +5669,53 @@ static size_t nfs4_xattr_list_nfs4_acl(struct dentry *dentry, char *list,
        return len;
 }
 
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+static inline int nfs4_server_supports_labels(struct nfs_server *server)
+{
+       return server->caps & NFS_CAP_SECURITY_LABEL;
+}
+
+static int nfs4_xattr_set_nfs4_label(struct dentry *dentry, const char *key,
+                                  const void *buf, size_t buflen,
+                                  int flags, int type)
+{
+       if (security_ismaclabel(key))
+               return nfs4_set_security_label(dentry, buf, buflen);
+
+       return -EOPNOTSUPP;
+}
+
+static int nfs4_xattr_get_nfs4_label(struct dentry *dentry, const char *key,
+                                  void *buf, size_t buflen, int type)
+{
+       if (security_ismaclabel(key))
+               return nfs4_get_security_label(dentry->d_inode, buf, buflen);
+       return -EOPNOTSUPP;
+}
+
+static size_t nfs4_xattr_list_nfs4_label(struct dentry *dentry, char *list,
+                                      size_t list_len, const char *name,
+                                      size_t name_len, int type)
+{
+       size_t len = 0;
+
+       if (nfs_server_capable(dentry->d_inode, NFS_CAP_SECURITY_LABEL)) {
+               len = security_inode_listsecurity(dentry->d_inode, NULL, 0);
+               if (list && len <= list_len)
+                       security_inode_listsecurity(dentry->d_inode, list, len);
+       }
+       return len;
+}
+
+static const struct xattr_handler nfs4_xattr_nfs4_label_handler = {
+       .prefix = XATTR_SECURITY_PREFIX,
+       .list   = nfs4_xattr_list_nfs4_label,
+       .get    = nfs4_xattr_get_nfs4_label,
+       .set    = nfs4_xattr_set_nfs4_label,
+};
+#endif
+
+
 /*
  * nfs_fhget will use either the mounted_on_fileid or the fileid
  */
@@ -5318,7 +5739,7 @@ static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
                                   struct page *page)
 {
        struct nfs_server *server = NFS_SERVER(dir);
-       u32 bitmask[2] = {
+       u32 bitmask[3] = {
                [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
        };
        struct nfs4_fs_locations_arg args = {
@@ -5505,7 +5926,8 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
        struct nfs41_exchange_id_args args = {
                .verifier = &verifier,
                .client = clp,
-               .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER,
+               .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
+                       EXCHGID4_FLAG_BIND_PRINC_STATEID,
        };
        struct nfs41_exchange_id_res res = {
                0
@@ -5762,17 +6184,14 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
  */
 static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
 {
-       struct nfs4_session *session = args->client->cl_session;
-       unsigned int mxrqst_sz = session->fc_target_max_rqst_sz,
-                    mxresp_sz = session->fc_target_max_resp_sz;
+       unsigned int max_rqst_sz, max_resp_sz;
+
+       max_rqst_sz = NFS_MAX_FILE_IO_SIZE + nfs41_maxwrite_overhead;
+       max_resp_sz = NFS_MAX_FILE_IO_SIZE + nfs41_maxread_overhead;
 
-       if (mxrqst_sz == 0)
-               mxrqst_sz = NFS_MAX_FILE_IO_SIZE;
-       if (mxresp_sz == 0)
-               mxresp_sz = NFS_MAX_FILE_IO_SIZE;
        /* Fore channel attributes */
-       args->fc_attrs.max_rqst_sz = mxrqst_sz;
-       args->fc_attrs.max_resp_sz = mxresp_sz;
+       args->fc_attrs.max_rqst_sz = max_rqst_sz;
+       args->fc_attrs.max_resp_sz = max_resp_sz;
        args->fc_attrs.max_ops = NFS4_MAX_OPS;
        args->fc_attrs.max_reqs = max_session_slots;
 
@@ -6159,12 +6578,14 @@ static const struct rpc_call_ops nfs4_reclaim_complete_call_ops = {
 /*
  * Issue a global reclaim complete.
  */
-static int nfs41_proc_reclaim_complete(struct nfs_client *clp)
+static int nfs41_proc_reclaim_complete(struct nfs_client *clp,
+               struct rpc_cred *cred)
 {
        struct nfs4_reclaim_complete_data *calldata;
        struct rpc_task *task;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RECLAIM_COMPLETE],
+               .rpc_cred = cred,
        };
        struct rpc_task_setup task_setup_data = {
                .rpc_client = clp->cl_rpcclient,
@@ -6348,6 +6769,7 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET],
                .rpc_argp = &lgp->args,
                .rpc_resp = &lgp->res,
+               .rpc_cred = lgp->cred,
        };
        struct rpc_task_setup task_setup_data = {
                .rpc_client = server->client,
@@ -6451,6 +6873,7 @@ int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp)
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTRETURN],
                .rpc_argp = &lrp->args,
                .rpc_resp = &lrp->res,
+               .rpc_cred = lrp->cred,
        };
        struct rpc_task_setup task_setup_data = {
                .rpc_client = lrp->clp->cl_rpcclient,
@@ -6520,7 +6943,9 @@ int nfs4_proc_getdevicelist(struct nfs_server *server,
 EXPORT_SYMBOL_GPL(nfs4_proc_getdevicelist);
 
 static int
-_nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
+_nfs4_proc_getdeviceinfo(struct nfs_server *server,
+               struct pnfs_device *pdev,
+               struct rpc_cred *cred)
 {
        struct nfs4_getdeviceinfo_args args = {
                .pdev = pdev,
@@ -6532,6 +6957,7 @@ _nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICEINFO],
                .rpc_argp = &args,
                .rpc_resp = &res,
+               .rpc_cred = cred,
        };
        int status;
 
@@ -6542,14 +6968,16 @@ _nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
        return status;
 }
 
-int nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
+int nfs4_proc_getdeviceinfo(struct nfs_server *server,
+               struct pnfs_device *pdev,
+               struct rpc_cred *cred)
 {
        struct nfs4_exception exception = { };
        int err;
 
        do {
                err = nfs4_handle_exception(server,
-                                       _nfs4_proc_getdeviceinfo(server, pdev),
+                                       _nfs4_proc_getdeviceinfo(server, pdev, cred),
                                        &exception);
        } while (exception.retry);
        return err;
@@ -6733,7 +7161,9 @@ out:
        return err;
 }
 
-static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
+static int _nfs41_test_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
 {
        int status;
        struct nfs41_test_stateid_args args = {
@@ -6744,6 +7174,7 @@ static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_TEST_STATEID],
                .rpc_argp = &args,
                .rpc_resp = &res,
+               .rpc_cred = cred,
        };
 
        dprintk("NFS call  test_stateid %p\n", stateid);
@@ -6764,17 +7195,20 @@ static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
  *
  * @server: server / transport on which to perform the operation
  * @stateid: state ID to test
+ * @cred: credential
  *
  * Returns NFS_OK if the server recognizes that "stateid" is valid.
  * Otherwise a negative NFS4ERR value is returned if the operation
  * failed or the state ID is not currently valid.
  */
-static int nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
+static int nfs41_test_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
 {
        struct nfs4_exception exception = { };
        int err;
        do {
-               err = _nfs41_test_stateid(server, stateid);
+               err = _nfs41_test_stateid(server, stateid, cred);
                if (err != -NFS4ERR_DELAY)
                        break;
                nfs4_handle_exception(server, err, &exception);
@@ -6823,10 +7257,12 @@ const struct rpc_call_ops nfs41_free_stateid_ops = {
 
 static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server,
                nfs4_stateid *stateid,
+               struct rpc_cred *cred,
                bool privileged)
 {
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID],
+               .rpc_cred = cred,
        };
        struct rpc_task_setup task_setup = {
                .rpc_client = server->client,
@@ -6859,16 +7295,19 @@ static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server,
  *
  * @server: server / transport on which to perform the operation
  * @stateid: state ID to release
+ * @cred: credential
  *
  * Returns NFS_OK if the server freed "stateid".  Otherwise a
  * negative NFS4ERR value is returned.
  */
-static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
+static int nfs41_free_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
 {
        struct rpc_task *task;
        int ret;
 
-       task = _nfs41_free_stateid(server, stateid, true);
+       task = _nfs41_free_stateid(server, stateid, cred, true);
        if (IS_ERR(task))
                return PTR_ERR(task);
        ret = rpc_wait_for_completion_task(task);
@@ -6881,8 +7320,9 @@ static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
 static int nfs41_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
 {
        struct rpc_task *task;
+       struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
 
-       task = _nfs41_free_stateid(server, &lsp->ls_stateid, false);
+       task = _nfs41_free_stateid(server, &lsp->ls_stateid, cred, false);
        nfs4_free_lock_state(server, lsp);
        if (IS_ERR(task))
                return PTR_ERR(task);
@@ -7004,11 +7444,33 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
 };
 #endif
 
+#if defined(CONFIG_NFS_V4_2)
+static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
+       .minor_version = 2,
+       .init_caps = NFS_CAP_READDIRPLUS
+               | NFS_CAP_ATOMIC_OPEN
+               | NFS_CAP_CHANGE_ATTR
+               | NFS_CAP_POSIX_LOCK
+               | NFS_CAP_STATEID_NFSV41
+               | NFS_CAP_ATOMIC_OPEN_V1,
+       .call_sync = nfs4_call_sync_sequence,
+       .match_stateid = nfs41_match_stateid,
+       .find_root_sec = nfs41_find_root_sec,
+       .free_lock_state = nfs41_free_lock_state,
+       .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
+       .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
+       .state_renewal_ops = &nfs41_state_renewal_ops,
+};
+#endif
+
 const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
        [0] = &nfs_v4_0_minor_ops,
 #if defined(CONFIG_NFS_V4_1)
        [1] = &nfs_v4_1_minor_ops,
 #endif
+#if defined(CONFIG_NFS_V4_2)
+       [2] = &nfs_v4_2_minor_ops,
+#endif
 };
 
 const struct inode_operations nfs4_dir_inode_operations = {
@@ -7108,6 +7570,9 @@ static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
 
 const struct xattr_handler *nfs4_xattr_handlers[] = {
        &nfs4_xattr_nfs4_acl_handler,
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+       &nfs4_xattr_nfs4_label_handler,
+#endif
        NULL
 };
 
index c4e225e..36e21cb 100644 (file)
@@ -478,48 +478,12 @@ static int nfs41_check_session_ready(struct nfs_client *clp)
        return 0;
 }
 
-int nfs4_init_session(struct nfs_server *server)
+int nfs4_init_session(struct nfs_client *clp)
 {
-       struct nfs_client *clp = server->nfs_client;
-       struct nfs4_session *session;
-       unsigned int target_max_rqst_sz = NFS_MAX_FILE_IO_SIZE;
-       unsigned int target_max_resp_sz = NFS_MAX_FILE_IO_SIZE;
-
        if (!nfs4_has_session(clp))
                return 0;
 
-       if (server->rsize != 0)
-               target_max_resp_sz = server->rsize;
-       target_max_resp_sz += nfs41_maxread_overhead;
-
-       if (server->wsize != 0)
-               target_max_rqst_sz = server->wsize;
-       target_max_rqst_sz += nfs41_maxwrite_overhead;
-
-       session = clp->cl_session;
-       spin_lock(&clp->cl_lock);
-       if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) {
-               /* Initialise targets and channel attributes */
-               session->fc_target_max_rqst_sz = target_max_rqst_sz;
-               session->fc_attrs.max_rqst_sz = target_max_rqst_sz;
-               session->fc_target_max_resp_sz = target_max_resp_sz;
-               session->fc_attrs.max_resp_sz = target_max_resp_sz;
-       } else {
-               /* Just adjust the targets */
-               if (target_max_rqst_sz > session->fc_target_max_rqst_sz) {
-                       session->fc_target_max_rqst_sz = target_max_rqst_sz;
-                       set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
-               }
-               if (target_max_resp_sz > session->fc_target_max_resp_sz) {
-                       session->fc_target_max_resp_sz = target_max_resp_sz;
-                       set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
-               }
-       }
-       spin_unlock(&clp->cl_lock);
-
-       if (test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state))
-               nfs4_schedule_lease_recovery(clp);
-
+       clear_bit(NFS4_SESSION_INITING, &clp->cl_session->session_state);
        return nfs41_check_session_ready(clp);
 }
 
index ff7d9f0..3a153d8 100644 (file)
@@ -66,9 +66,6 @@ struct nfs4_session {
        struct nfs4_channel_attrs       bc_attrs;
        struct nfs4_slot_table          bc_slot_table;
        struct nfs_client               *clp;
-       /* Create session arguments */
-       unsigned int                    fc_target_max_rqst_sz;
-       unsigned int                    fc_target_max_resp_sz;
 };
 
 enum nfs4_session_state {
@@ -89,7 +86,7 @@ extern int nfs4_setup_session_slot_tables(struct nfs4_session *ses);
 
 extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp);
 extern void nfs4_destroy_session(struct nfs4_session *session);
-extern int nfs4_init_session(struct nfs_server *server);
+extern int nfs4_init_session(struct nfs_client *clp);
 extern int nfs4_init_ds_session(struct nfs_client *, unsigned long);
 
 extern void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl);
@@ -122,7 +119,7 @@ static inline int nfs4_has_persistent_session(const struct nfs_client *clp)
 
 #else /* defined(CONFIG_NFS_V4_1) */
 
-static inline int nfs4_init_session(struct nfs_server *server)
+static inline int nfs4_init_session(struct nfs_client *clp)
 {
        return 0;
 }
index 5541881..e22862f 100644 (file)
@@ -228,19 +228,8 @@ static int nfs41_setup_state_renewal(struct nfs_client *clp)
        return status;
 }
 
-/*
- * Back channel returns NFS4ERR_DELAY for new requests when
- * NFS4_SESSION_DRAINING is set so there is no work to be done when draining
- * is ended.
- */
-static void nfs4_end_drain_session(struct nfs_client *clp)
+static void nfs4_end_drain_slot_table(struct nfs4_slot_table *tbl)
 {
-       struct nfs4_session *ses = clp->cl_session;
-       struct nfs4_slot_table *tbl;
-
-       if (ses == NULL)
-               return;
-       tbl = &ses->fc_slot_table;
        if (test_and_clear_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) {
                spin_lock(&tbl->slot_tbl_lock);
                nfs41_wake_slot_table(tbl);
@@ -248,6 +237,16 @@ static void nfs4_end_drain_session(struct nfs_client *clp)
        }
 }
 
+static void nfs4_end_drain_session(struct nfs_client *clp)
+{
+       struct nfs4_session *ses = clp->cl_session;
+
+       if (ses != NULL) {
+               nfs4_end_drain_slot_table(&ses->bc_slot_table);
+               nfs4_end_drain_slot_table(&ses->fc_slot_table);
+       }
+}
+
 /*
  * Signal state manager thread if session fore channel is drained
  */
@@ -1563,11 +1562,12 @@ static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp)
 }
 
 static void nfs4_reclaim_complete(struct nfs_client *clp,
-                                const struct nfs4_state_recovery_ops *ops)
+                                const struct nfs4_state_recovery_ops *ops,
+                                struct rpc_cred *cred)
 {
        /* Notify the server we're done reclaiming our state */
        if (ops->reclaim_complete)
-               (void)ops->reclaim_complete(clp);
+               (void)ops->reclaim_complete(clp, cred);
 }
 
 static void nfs4_clear_reclaim_server(struct nfs_server *server)
@@ -1612,9 +1612,15 @@ static int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp)
 
 static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
 {
+       const struct nfs4_state_recovery_ops *ops;
+       struct rpc_cred *cred;
+
        if (!nfs4_state_clear_reclaim_reboot(clp))
                return;
-       nfs4_reclaim_complete(clp, clp->cl_mvops->reboot_recovery_ops);
+       ops = clp->cl_mvops->reboot_recovery_ops;
+       cred = ops->get_clid_cred(clp);
+       nfs4_reclaim_complete(clp, ops, cred);
+       put_rpccred(cred);
 }
 
 static void nfs_delegation_clear_all(struct nfs_client *clp)
index a5e1a30..5dbe2d2 100644 (file)
@@ -9,6 +9,7 @@
 #include "delegation.h"
 #include "internal.h"
 #include "nfs4_fs.h"
+#include "dns_resolve.h"
 #include "pnfs.h"
 #include "nfs.h"
 
@@ -331,18 +332,24 @@ static int __init init_nfs_v4(void)
 {
        int err;
 
-       err = nfs_idmap_init();
+       err = nfs_dns_resolver_init();
        if (err)
                goto out;
 
-       err = nfs4_register_sysctl();
+       err = nfs_idmap_init();
        if (err)
                goto out1;
 
+       err = nfs4_register_sysctl();
+       if (err)
+               goto out2;
+
        register_nfs_version(&nfs_v4);
        return 0;
-out1:
+out2:
        nfs_idmap_quit();
+out1:
+       nfs_dns_resolver_destroy();
 out:
        return err;
 }
@@ -352,6 +359,7 @@ static void __exit exit_nfs_v4(void)
        unregister_nfs_version(&nfs_v4);
        nfs4_unregister_sysctl();
        nfs_idmap_quit();
+       nfs_dns_resolver_destroy();
 }
 
 MODULE_LICENSE("GPL");
index 4be8d13..0abfb84 100644 (file)
@@ -102,12 +102,23 @@ static int nfs4_stat_to_errno(int);
 #define nfs4_path_maxsz                (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
 #define nfs4_owner_maxsz       (1 + XDR_QUADLEN(IDMAP_NAMESZ))
 #define nfs4_group_maxsz       (1 + XDR_QUADLEN(IDMAP_NAMESZ))
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+/* PI(4 bytes) + LFS(4 bytes) + 1(for null terminator?) + MAXLABELLEN */
+#define        nfs4_label_maxsz        (4 + 4 + 1 + XDR_QUADLEN(NFS4_MAXLABELLEN))
+#define encode_readdir_space 24
+#define encode_readdir_bitmask_sz 3
+#else
+#define        nfs4_label_maxsz        0
+#define encode_readdir_space 20
+#define encode_readdir_bitmask_sz 2
+#endif
 /* We support only one layout type per file system */
 #define decode_mdsthreshold_maxsz (1 + 1 + nfs4_fattr_bitmap_maxsz + 1 + 8)
 /* This is based on getfattr, which uses the most attributes: */
 #define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \
                                3 + 3 + 3 + nfs4_owner_maxsz + \
-                               nfs4_group_maxsz + decode_mdsthreshold_maxsz))
+                               nfs4_group_maxsz + nfs4_label_maxsz + \
+                                decode_mdsthreshold_maxsz))
 #define nfs4_fattr_maxsz       (nfs4_fattr_bitmap_maxsz + \
                                nfs4_fattr_value_maxsz)
 #define decode_getattr_maxsz    (op_decode_hdr_maxsz + nfs4_fattr_maxsz)
@@ -115,6 +126,7 @@ static int nfs4_stat_to_errno(int);
                                 1 + 2 + 1 + \
                                nfs4_owner_maxsz + \
                                nfs4_group_maxsz + \
+                               nfs4_label_maxsz + \
                                4 + 4)
 #define encode_savefh_maxsz     (op_encode_hdr_maxsz)
 #define decode_savefh_maxsz     (op_decode_hdr_maxsz)
@@ -192,9 +204,11 @@ static int nfs4_stat_to_errno(int);
                                 encode_stateid_maxsz + 3)
 #define decode_read_maxsz      (op_decode_hdr_maxsz + 2)
 #define encode_readdir_maxsz   (op_encode_hdr_maxsz + \
-                                2 + encode_verifier_maxsz + 5)
+                                2 + encode_verifier_maxsz + 5 + \
+                               nfs4_label_maxsz)
 #define decode_readdir_maxsz   (op_decode_hdr_maxsz + \
-                                decode_verifier_maxsz)
+                                decode_verifier_maxsz + \
+                               nfs4_label_maxsz + nfs4_fattr_maxsz)
 #define encode_readlink_maxsz  (op_encode_hdr_maxsz)
 #define decode_readlink_maxsz  (op_decode_hdr_maxsz + 1)
 #define encode_write_maxsz     (op_encode_hdr_maxsz + \
@@ -853,6 +867,12 @@ const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
                                     decode_sequence_maxsz +
                                     decode_putfh_maxsz) *
                                    XDR_UNIT);
+
+const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH +
+                                          compound_decode_hdr_maxsz +
+                                          decode_sequence_maxsz) *
+                                         XDR_UNIT);
+EXPORT_SYMBOL_GPL(nfs41_maxgetdevinfo_overhead);
 #endif /* CONFIG_NFS_V4_1 */
 
 static const umode_t nfs_type2fmt[] = {
@@ -968,7 +988,9 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve
        encode_opaque_fixed(xdr, verf->data, NFS4_VERIFIER_SIZE);
 }
 
-static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
+static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
+                               const struct nfs4_label *label,
+                               const struct nfs_server *server)
 {
        char owner_name[IDMAP_NAMESZ];
        char owner_group[IDMAP_NAMESZ];
@@ -979,15 +1001,16 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
        int len;
        uint32_t bmval0 = 0;
        uint32_t bmval1 = 0;
+       uint32_t bmval2 = 0;
 
        /*
         * We reserve enough space to write the entire attribute buffer at once.
         * In the worst-case, this would be
-        *   12(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime)
-        *          = 36 bytes, plus any contribution from variable-length fields
+        * 16(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime)
+        * = 40 bytes, plus any contribution from variable-length fields
         *            such as owner/group.
         */
-       len = 16;
+       len = 20;
 
        /* Sigh */
        if (iap->ia_valid & ATTR_SIZE)
@@ -1017,6 +1040,8 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
                }
                len += 4 + (XDR_QUADLEN(owner_grouplen) << 2);
        }
+       if (label)
+               len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2);
        if (iap->ia_valid & ATTR_ATIME_SET)
                len += 16;
        else if (iap->ia_valid & ATTR_ATIME)
@@ -1031,9 +1056,9 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
         * We write the bitmap length now, but leave the bitmap and the attribute
         * buffer length to be backfilled at the end of this routine.
         */
-       *p++ = cpu_to_be32(2);
+       *p++ = cpu_to_be32(3);
        q = p;
-       p += 3;
+       p += 4;
 
        if (iap->ia_valid & ATTR_SIZE) {
                bmval0 |= FATTR4_WORD0_SIZE;
@@ -1071,6 +1096,13 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
                bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
                *p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
        }
+       if (label) {
+               bmval2 |= FATTR4_WORD2_SECURITY_LABEL;
+               *p++ = cpu_to_be32(label->lfs);
+               *p++ = cpu_to_be32(label->pi);
+               *p++ = cpu_to_be32(label->len);
+               p = xdr_encode_opaque_fixed(p, label->label, label->len);
+       }
 
        /*
         * Now we backfill the bitmap and the attribute buffer length.
@@ -1080,9 +1112,10 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
                                len, ((char *)p - (char *)q) + 4);
                BUG();
        }
-       len = (char *)p - (char *)q - 12;
+       len = (char *)p - (char *)q - 16;
        *q++ = htonl(bmval0);
        *q++ = htonl(bmval1);
+       *q++ = htonl(bmval2);
        *q = htonl(len);
 
 /* out: */
@@ -1136,7 +1169,7 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *
        }
 
        encode_string(xdr, create->name->len, create->name->name);
-       encode_attrs(xdr, create->attrs, create->server);
+       encode_attrs(xdr, create->attrs, create->label, create->server);
 }
 
 static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr)
@@ -1188,8 +1221,10 @@ encode_getattr_three(struct xdr_stream *xdr,
 
 static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
 {
-       encode_getattr_two(xdr, bitmask[0] & nfs4_fattr_bitmap[0],
-                          bitmask[1] & nfs4_fattr_bitmap[1], hdr);
+       encode_getattr_three(xdr, bitmask[0] & nfs4_fattr_bitmap[0],
+                          bitmask[1] & nfs4_fattr_bitmap[1],
+                          bitmask[2] & nfs4_fattr_bitmap[2],
+                          hdr);
 }
 
 static void encode_getfattr_open(struct xdr_stream *xdr, const u32 *bitmask,
@@ -1367,11 +1402,11 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
        switch(arg->createmode) {
        case NFS4_CREATE_UNCHECKED:
                *p = cpu_to_be32(NFS4_CREATE_UNCHECKED);
-               encode_attrs(xdr, arg->u.attrs, arg->server);
+               encode_attrs(xdr, arg->u.attrs, arg->label, arg->server);
                break;
        case NFS4_CREATE_GUARDED:
                *p = cpu_to_be32(NFS4_CREATE_GUARDED);
-               encode_attrs(xdr, arg->u.attrs, arg->server);
+               encode_attrs(xdr, arg->u.attrs, arg->label, arg->server);
                break;
        case NFS4_CREATE_EXCLUSIVE:
                *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
@@ -1381,7 +1416,7 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
                *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
                encode_nfs4_verifier(xdr, &arg->u.verifier);
                dummy.ia_valid = 0;
-               encode_attrs(xdr, &dummy, arg->server);
+               encode_attrs(xdr, &dummy, arg->label, arg->server);
        }
 }
 
@@ -1532,7 +1567,7 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args,
 
 static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr)
 {
-       uint32_t attrs[2] = {
+       uint32_t attrs[3] = {
                FATTR4_WORD0_RDATTR_ERROR,
                FATTR4_WORD1_MOUNTED_ON_FILEID,
        };
@@ -1555,20 +1590,26 @@ static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
        encode_op_hdr(xdr, OP_READDIR, decode_readdir_maxsz, hdr);
        encode_uint64(xdr, readdir->cookie);
        encode_nfs4_verifier(xdr, &readdir->verifier);
-       p = reserve_space(xdr, 20);
+       p = reserve_space(xdr, encode_readdir_space);
        *p++ = cpu_to_be32(dircount);
        *p++ = cpu_to_be32(readdir->count);
-       *p++ = cpu_to_be32(2);
-
+       *p++ = cpu_to_be32(encode_readdir_bitmask_sz);
        *p++ = cpu_to_be32(attrs[0] & readdir->bitmask[0]);
-       *p = cpu_to_be32(attrs[1] & readdir->bitmask[1]);
+       *p   = cpu_to_be32(attrs[1] & readdir->bitmask[1]);
+       if (encode_readdir_bitmask_sz > 2) {
+               if (hdr->minorversion > 1)
+                       attrs[2] |= FATTR4_WORD2_SECURITY_LABEL;
+               p++, *p++ = cpu_to_be32(attrs[2] & readdir->bitmask[2]);
+       }
        memcpy(verf, readdir->verifier.data, sizeof(verf));
-       dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n",
+
+       dprintk("%s: cookie = %llu, verifier = %08x:%08x, bitmap = %08x:%08x:%08x\n",
                        __func__,
                        (unsigned long long)readdir->cookie,
                        verf[0], verf[1],
                        attrs[0] & readdir->bitmask[0],
-                       attrs[1] & readdir->bitmask[1]);
+                       attrs[1] & readdir->bitmask[1],
+                       attrs[2] & readdir->bitmask[2]);
 }
 
 static void encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req, struct compound_hdr *hdr)
@@ -1627,7 +1668,7 @@ static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs
 {
        encode_op_hdr(xdr, OP_SETATTR, decode_setattr_maxsz, hdr);
        encode_nfs4_stateid(xdr, &arg->stateid);
-       encode_attrs(xdr, arg->iap, server);
+       encode_attrs(xdr, arg->iap, arg->label, server);
 }
 
 static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
@@ -1889,7 +1930,7 @@ encode_getdeviceinfo(struct xdr_stream *xdr,
        p = xdr_encode_opaque_fixed(p, args->pdev->dev_id.data,
                                    NFS4_DEVICEID4_SIZE);
        *p++ = cpu_to_be32(args->pdev->layout_type);
-       *p++ = cpu_to_be32(args->pdev->pglen);          /* gdia_maxcount */
+       *p++ = cpu_to_be32(args->pdev->maxcount);       /* gdia_maxcount */
        *p++ = cpu_to_be32(0);                          /* bitmap length 0 */
 }
 
@@ -4038,6 +4079,56 @@ static int decode_attr_time_delta(struct xdr_stream *xdr, uint32_t *bitmap,
        return status;
 }
 
+static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap,
+                                       struct nfs4_label *label)
+{
+       uint32_t pi = 0;
+       uint32_t lfs = 0;
+       __u32 len;
+       __be32 *p;
+       int status = 0;
+
+       if (unlikely(bitmap[2] & (FATTR4_WORD2_SECURITY_LABEL - 1U)))
+               return -EIO;
+       if (likely(bitmap[2] & FATTR4_WORD2_SECURITY_LABEL)) {
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               lfs = be32_to_cpup(p++);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               pi = be32_to_cpup(p++);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               len = be32_to_cpup(p++);
+               p = xdr_inline_decode(xdr, len);
+               if (unlikely(!p))
+                       goto out_overflow;
+               if (len < NFS4_MAXLABELLEN) {
+                       if (label) {
+                               memcpy(label->label, p, len);
+                               label->len = len;
+                               label->pi = pi;
+                               label->lfs = lfs;
+                               status = NFS_ATTR_FATTR_V4_SECURITY_LABEL;
+                       }
+                       bitmap[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+               } else
+                       printk(KERN_WARNING "%s: label too long (%u)!\n",
+                                       __func__, len);
+       }
+       if (label && label->label)
+               dprintk("%s: label=%s, len=%d, PI=%d, LFS=%d\n", __func__,
+                       (char *)label->label, label->len, label->pi, label->lfs);
+       return status;
+
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
 static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time)
 {
        int status = 0;
@@ -4380,7 +4471,7 @@ out_overflow:
 
 static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
                struct nfs_fattr *fattr, struct nfs_fh *fh,
-               struct nfs4_fs_locations *fs_loc,
+               struct nfs4_fs_locations *fs_loc, struct nfs4_label *label,
                const struct nfs_server *server)
 {
        int status;
@@ -4488,6 +4579,13 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
        if (status < 0)
                goto xdr_error;
 
+       if (label) {
+               status = decode_attr_security_label(xdr, bitmap, label);
+               if (status < 0)
+                       goto xdr_error;
+               fattr->valid |= status;
+       }
+
 xdr_error:
        dprintk("%s: xdr returned %d\n", __func__, -status);
        return status;
@@ -4495,7 +4593,7 @@ xdr_error:
 
 static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fattr,
                struct nfs_fh *fh, struct nfs4_fs_locations *fs_loc,
-               const struct nfs_server *server)
+               struct nfs4_label *label, const struct nfs_server *server)
 {
        unsigned int savep;
        uint32_t attrlen,
@@ -4514,7 +4612,8 @@ static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fat
        if (status < 0)
                goto xdr_error;
 
-       status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, fs_loc, server);
+       status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, fs_loc,
+                                       label, server);
        if (status < 0)
                goto xdr_error;
 
@@ -4524,10 +4623,16 @@ xdr_error:
        return status;
 }
 
+static int decode_getfattr_label(struct xdr_stream *xdr, struct nfs_fattr *fattr,
+               struct nfs4_label *label, const struct nfs_server *server)
+{
+       return decode_getfattr_generic(xdr, fattr, NULL, NULL, label, server);
+}
+
 static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
                const struct nfs_server *server)
 {
-       return decode_getfattr_generic(xdr, fattr, NULL, NULL, server);
+       return decode_getfattr_generic(xdr, fattr, NULL, NULL, NULL, server);
 }
 
 /*
@@ -5919,7 +6024,7 @@ static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_getfh(xdr, res->fh);
        if (status)
                goto out;
-       status = decode_getfattr(xdr, res->fattr, res->server);
+       status = decode_getfattr_label(xdr, res->fattr, res->label, res->server);
 out:
        return status;
 }
@@ -5945,7 +6050,8 @@ static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp,
                goto out;
        status = decode_getfh(xdr, res->fh);
        if (status == 0)
-               status = decode_getfattr(xdr, res->fattr, res->server);
+               status = decode_getfattr_label(xdr, res->fattr,
+                                               res->label, res->server);
 out:
        return status;
 }
@@ -6036,7 +6142,7 @@ static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_restorefh(xdr);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server);
+       decode_getfattr_label(xdr, res->fattr, res->label, res->server);
 out:
        return status;
 }
@@ -6065,7 +6171,7 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_getfh(xdr, res->fh);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server);
+       decode_getfattr_label(xdr, res->fattr, res->label, res->server);
 out:
        return status;
 }
@@ -6097,7 +6203,7 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_getfattr(xdr, res->fattr, res->server);
+       status = decode_getfattr_label(xdr, res->fattr, res->label, res->server);
 out:
        return status;
 }
@@ -6230,7 +6336,7 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
                goto out;
        if (res->access_request)
                decode_access(xdr, &res->access_supported, &res->access_result);
-       decode_getfattr(xdr, res->f_attr, res->server);
+       decode_getfattr_label(xdr, res->f_attr, res->f_label, res->server);
 out:
        return status;
 }
@@ -6307,7 +6413,7 @@ static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp,
        status = decode_setattr(xdr);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server);
+       decode_getfattr_label(xdr, res->fattr, res->label, res->server);
 out:
        return status;
 }
@@ -6696,7 +6802,7 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req,
        xdr_enter_page(xdr, PAGE_SIZE);
        status = decode_getfattr_generic(xdr, &res->fs_locations->fattr,
                                         NULL, res->fs_locations,
-                                        res->fs_locations->server);
+                                        NULL, res->fs_locations->server);
 out:
        return status;
 }
@@ -7109,7 +7215,7 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
                goto out_overflow;
 
        if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh,
-                                 NULL, entry->server) < 0)
+                       NULL, entry->label, entry->server) < 0)
                goto out_overflow;
        if (entry->fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID)
                entry->ino = entry->fattr->mounted_on_fileid;
index a9ebd81..e4f9cbf 100644 (file)
@@ -613,8 +613,10 @@ int objlayout_get_deviceinfo(struct pnfs_layout_hdr *pnfslay,
        pd.pgbase = 0;
        pd.pglen = PAGE_SIZE;
        pd.mincount = 0;
+       pd.maxcount = PAGE_SIZE;
 
-       err = nfs4_proc_getdeviceinfo(NFS_SERVER(pnfslay->plh_inode), &pd);
+       err = nfs4_proc_getdeviceinfo(NFS_SERVER(pnfslay->plh_inode), &pd,
+                       pnfslay->plh_lc_cred);
        dprintk("%s nfs_getdeviceinfo returned %d\n", __func__, err);
        if (err)
                goto err_out;
index c5bd758..3a3a79d 100644 (file)
@@ -360,7 +360,7 @@ pnfs_put_lseg(struct pnfs_layout_segment *lseg)
 }
 EXPORT_SYMBOL_GPL(pnfs_put_lseg);
 
-static inline u64
+static u64
 end_offset(u64 start, u64 len)
 {
        u64 end;
@@ -376,9 +376,9 @@ end_offset(u64 start, u64 len)
  *           start2           end2
  *           [----------------)
  */
-static inline int
-lo_seg_contained(struct pnfs_layout_range *l1,
-                struct pnfs_layout_range *l2)
+static bool
+pnfs_lseg_range_contained(const struct pnfs_layout_range *l1,
+                const struct pnfs_layout_range *l2)
 {
        u64 start1 = l1->offset;
        u64 end1 = end_offset(start1, l1->length);
@@ -395,9 +395,9 @@ lo_seg_contained(struct pnfs_layout_range *l1,
  *                              start2           end2
  *                              [----------------)
  */
-static inline int
-lo_seg_intersecting(struct pnfs_layout_range *l1,
-                   struct pnfs_layout_range *l2)
+static bool
+pnfs_lseg_range_intersecting(const struct pnfs_layout_range *l1,
+                   const struct pnfs_layout_range *l2)
 {
        u64 start1 = l1->offset;
        u64 end1 = end_offset(start1, l1->length);
@@ -409,12 +409,12 @@ lo_seg_intersecting(struct pnfs_layout_range *l1,
 }
 
 static bool
-should_free_lseg(struct pnfs_layout_range *lseg_range,
-                struct pnfs_layout_range *recall_range)
+should_free_lseg(const struct pnfs_layout_range *lseg_range,
+                const struct pnfs_layout_range *recall_range)
 {
        return (recall_range->iomode == IOMODE_ANY ||
                lseg_range->iomode == recall_range->iomode) &&
-              lo_seg_intersecting(lseg_range, recall_range);
+              pnfs_lseg_range_intersecting(lseg_range, recall_range);
 }
 
 static bool pnfs_lseg_dec_and_remove_zero(struct pnfs_layout_segment *lseg,
@@ -766,6 +766,7 @@ send_layoutget(struct pnfs_layout_hdr *lo,
        lgp->args.inode = ino;
        lgp->args.ctx = get_nfs_open_context(ctx);
        lgp->gfp_flags = gfp_flags;
+       lgp->cred = lo->plh_lc_cred;
 
        /* Synchronously retrieve layout information from server and
         * store in lseg.
@@ -860,6 +861,7 @@ _pnfs_return_layout(struct inode *ino)
        lrp->args.inode = ino;
        lrp->args.layout = lo;
        lrp->clp = NFS_SERVER(ino)->nfs_client;
+       lrp->cred = lo->plh_lc_cred;
 
        status = nfs4_proc_layoutreturn(lrp);
 out:
@@ -984,8 +986,8 @@ out:
  * are seen first.
  */
 static s64
-cmp_layout(struct pnfs_layout_range *l1,
-          struct pnfs_layout_range *l2)
+pnfs_lseg_range_cmp(const struct pnfs_layout_range *l1,
+          const struct pnfs_layout_range *l2)
 {
        s64 d;
 
@@ -1012,7 +1014,7 @@ pnfs_layout_insert_lseg(struct pnfs_layout_hdr *lo,
        dprintk("%s:Begin\n", __func__);
 
        list_for_each_entry(lp, &lo->plh_segs, pls_list) {
-               if (cmp_layout(&lseg->pls_range, &lp->pls_range) > 0)
+               if (pnfs_lseg_range_cmp(&lseg->pls_range, &lp->pls_range) > 0)
                        continue;
                list_add_tail(&lseg->pls_list, &lp->pls_list);
                dprintk("%s: inserted lseg %p "
@@ -1050,7 +1052,7 @@ alloc_init_layout_hdr(struct inode *ino,
        INIT_LIST_HEAD(&lo->plh_segs);
        INIT_LIST_HEAD(&lo->plh_bulk_destroy);
        lo->plh_inode = ino;
-       lo->plh_lc_cred = get_rpccred(ctx->state->owner->so_cred);
+       lo->plh_lc_cred = get_rpccred(ctx->cred);
        return lo;
 }
 
@@ -1091,21 +1093,21 @@ out_existing:
  * READ                READ    true
  * READ                RW      true
  */
-static int
-is_matching_lseg(struct pnfs_layout_range *ls_range,
-                struct pnfs_layout_range *range)
+static bool
+pnfs_lseg_range_match(const struct pnfs_layout_range *ls_range,
+                const struct pnfs_layout_range *range)
 {
        struct pnfs_layout_range range1;
 
        if ((range->iomode == IOMODE_RW &&
             ls_range->iomode != IOMODE_RW) ||
-           !lo_seg_intersecting(ls_range, range))
+           !pnfs_lseg_range_intersecting(ls_range, range))
                return 0;
 
        /* range1 covers only the first byte in the range */
        range1 = *range;
        range1.length = 1;
-       return lo_seg_contained(ls_range, &range1);
+       return pnfs_lseg_range_contained(ls_range, &range1);
 }
 
 /*
@@ -1121,7 +1123,7 @@ pnfs_find_lseg(struct pnfs_layout_hdr *lo,
 
        list_for_each_entry(lseg, &lo->plh_segs, pls_list) {
                if (test_bit(NFS_LSEG_VALID, &lseg->pls_flags) &&
-                   is_matching_lseg(&lseg->pls_range, range)) {
+                   pnfs_lseg_range_match(&lseg->pls_range, range)) {
                        ret = pnfs_get_lseg(lseg);
                        break;
                }
index f5f8a47..a4f4181 100644 (file)
@@ -149,9 +149,10 @@ struct pnfs_device {
        struct nfs4_deviceid dev_id;
        unsigned int  layout_type;
        unsigned int  mincount;
+       unsigned int  maxcount; /* gdia_maxcount */
        struct page **pages;
        unsigned int  pgbase;
-       unsigned int  pglen;
+       unsigned int  pglen;    /* reply buffer length */
 };
 
 #define NFS4_PNFS_GETDEVLIST_MAXNUM 16
@@ -170,7 +171,8 @@ extern int nfs4_proc_getdevicelist(struct nfs_server *server,
                                   const struct nfs_fh *fh,
                                   struct pnfs_devicelist *devlist);
 extern int nfs4_proc_getdeviceinfo(struct nfs_server *server,
-                                  struct pnfs_device *dev);
+                                  struct pnfs_device *dev,
+                                  struct rpc_cred *cred);
 extern struct pnfs_layout_segment* nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags);
 extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp);
 
index fc8de90..c041c41 100644 (file)
@@ -98,7 +98,7 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
  */
 static int
 nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
-               struct nfs_fattr *fattr)
+               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct rpc_message msg = {
                .rpc_proc       = &nfs_procedures[NFSPROC_GETATTR],
@@ -146,7 +146,8 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
 
 static int
 nfs_proc_lookup(struct inode *dir, struct qstr *name,
-               struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+               struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+               struct nfs4_label *label)
 {
        struct nfs_diropargs    arg = {
                .fh             = NFS_FH(dir),
@@ -243,7 +244,7 @@ nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
        status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
        nfs_mark_for_revalidate(dir);
        if (status == 0)
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL);
        nfs_free_createdata(data);
 out:
        dprintk("NFS reply create: %d\n", status);
@@ -290,7 +291,7 @@ nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
        }
        if (status == 0)
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL);
        nfs_free_createdata(data);
 out:
        dprintk("NFS reply mknod: %d\n", status);
@@ -442,7 +443,7 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
         * should fill in the data with a LOOKUP call on the wire.
         */
        if (status == 0)
-               status = nfs_instantiate(dentry, fh, fattr);
+               status = nfs_instantiate(dentry, fh, fattr, NULL);
 
 out_free:
        nfs_free_fattr(fattr);
@@ -471,7 +472,7 @@ nfs_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
        status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
        nfs_mark_for_revalidate(dir);
        if (status == 0)
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL);
        nfs_free_createdata(data);
 out:
        dprintk("NFS reply mkdir: %d\n", status);
index 2d7525f..71fdc0d 100644 (file)
@@ -269,7 +269,7 @@ static match_table_t nfs_local_lock_tokens = {
 
 enum {
        Opt_vers_2, Opt_vers_3, Opt_vers_4, Opt_vers_4_0,
-       Opt_vers_4_1,
+       Opt_vers_4_1, Opt_vers_4_2,
 
        Opt_vers_err
 };
@@ -280,6 +280,7 @@ static match_table_t nfs_vers_tokens = {
        { Opt_vers_4, "4" },
        { Opt_vers_4_0, "4.0" },
        { Opt_vers_4_1, "4.1" },
+       { Opt_vers_4_2, "4.2" },
 
        { Opt_vers_err, NULL }
 };
@@ -832,6 +833,7 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root)
                seq_printf(m, "\n\tnfsv4:\t");
                seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
                seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]);
+               seq_printf(m, ",bm2=0x%x", nfss->attr_bitmask[2]);
                seq_printf(m, ",acl=0x%x", nfss->acl_bitmask);
                show_sessions(m, nfss);
                show_pnfs(m, nfss);
@@ -1097,6 +1099,10 @@ static int nfs_parse_version_string(char *string,
                mnt->version = 4;
                mnt->minorversion = 1;
                break;
+       case Opt_vers_4_2:
+               mnt->version = 4;
+               mnt->minorversion = 2;
+               break;
        default:
                return 0;
        }
@@ -1608,29 +1614,13 @@ out_security_failure:
 }
 
 /*
- * Select a security flavor for this mount.  The selected flavor
- * is planted in args->auth_flavors[0].
- *
- * Returns 0 on success, -EACCES on failure.
+ * Ensure that the specified authtype in args->auth_flavors[0] is supported by
+ * the server. Returns 0 if it's ok, and -EACCES if not.
  */
-static int nfs_select_flavor(struct nfs_parsed_mount_data *args,
-                             struct nfs_mount_request *request)
+static int nfs_verify_authflavor(struct nfs_parsed_mount_data *args,
+                       rpc_authflavor_t *server_authlist, unsigned int count)
 {
-       unsigned int i, count = *(request->auth_flav_len);
-       rpc_authflavor_t flavor;
-
-       /*
-        * The NFSv2 MNT operation does not return a flavor list.
-        */
-       if (args->mount_server.version != NFS_MNT3_VERSION)
-               goto out_default;
-
-       /*
-        * Certain releases of Linux's mountd return an empty
-        * flavor list in some cases.
-        */
-       if (count == 0)
-               goto out_default;
+       unsigned int i;
 
        /*
         * If the sec= mount option is used, the specified flavor or AUTH_NULL
@@ -1640,60 +1630,19 @@ static int nfs_select_flavor(struct nfs_parsed_mount_data *args,
         * means that the server will ignore the rpc creds, so any flavor
         * can be used.
         */
-       if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR) {
-               for (i = 0; i < count; i++) {
-                       if (args->auth_flavors[0] == request->auth_flavs[i] ||
-                           request->auth_flavs[i] == RPC_AUTH_NULL)
-                               goto out;
-               }
-               dfprintk(MOUNT, "NFS: auth flavor %d not supported by server\n",
-                       args->auth_flavors[0]);
-               goto out_err;
-       }
-
-       /*
-        * RFC 2623, section 2.7 suggests we SHOULD prefer the
-        * flavor listed first.  However, some servers list
-        * AUTH_NULL first.  Avoid ever choosing AUTH_NULL.
-        */
        for (i = 0; i < count; i++) {
-               struct rpcsec_gss_info info;
-
-               flavor = request->auth_flavs[i];
-               switch (flavor) {
-               case RPC_AUTH_UNIX:
-                       goto out_set;
-               case RPC_AUTH_NULL:
-                       continue;
-               default:
-                       if (rpcauth_get_gssinfo(flavor, &info) == 0)
-                               goto out_set;
-               }
+               if (args->auth_flavors[0] == server_authlist[i] ||
+                   server_authlist[i] == RPC_AUTH_NULL)
+                       goto out;
        }
 
-       /*
-        * As a last chance, see if the server list contains AUTH_NULL -
-        * if it does, use the default flavor.
-        */
-       for (i = 0; i < count; i++) {
-               if (request->auth_flavs[i] == RPC_AUTH_NULL)
-                       goto out_default;
-       }
-
-       dfprintk(MOUNT, "NFS: no auth flavors in common with server\n");
-       goto out_err;
+       dfprintk(MOUNT, "NFS: auth flavor %u not supported by server\n",
+               args->auth_flavors[0]);
+       return -EACCES;
 
-out_default:
-       /* use default if flavor not already set */
-       flavor = (args->auth_flavors[0] == RPC_AUTH_MAXFLAVOR) ?
-               RPC_AUTH_UNIX : args->auth_flavors[0];
-out_set:
-       args->auth_flavors[0] = flavor;
 out:
-       dfprintk(MOUNT, "NFS: using auth flavor %d\n", args->auth_flavors[0]);
+       dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->auth_flavors[0]);
        return 0;
-out_err:
-       return -EACCES;
 }
 
 /*
@@ -1701,10 +1650,10 @@ out_err:
  * corresponding to the provided path.
  */
 static int nfs_request_mount(struct nfs_parsed_mount_data *args,
-                            struct nfs_fh *root_fh)
+                            struct nfs_fh *root_fh,
+                            rpc_authflavor_t *server_authlist,
+                            unsigned int *server_authlist_len)
 {
-       rpc_authflavor_t server_authlist[NFS_MAX_SECFLAVORS];
-       unsigned int server_authlist_len = ARRAY_SIZE(server_authlist);
        struct nfs_mount_request request = {
                .sap            = (struct sockaddr *)
                                                &args->mount_server.address,
@@ -1712,7 +1661,7 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args,
                .protocol       = args->mount_server.protocol,
                .fh             = root_fh,
                .noresvport     = args->flags & NFS_MOUNT_NORESVPORT,
-               .auth_flav_len  = &server_authlist_len,
+               .auth_flav_len  = server_authlist_len,
                .auth_flavs     = server_authlist,
                .net            = args->net,
        };
@@ -1756,24 +1705,92 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args,
                return status;
        }
 
-       return nfs_select_flavor(args, &request);
+       return 0;
 }
 
-struct dentry *nfs_try_mount(int flags, const char *dev_name,
-                            struct nfs_mount_info *mount_info,
-                            struct nfs_subversion *nfs_mod)
+static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_info,
+                                       struct nfs_subversion *nfs_mod)
 {
        int status;
-       struct nfs_server *server;
+       unsigned int i;
+       bool tried_auth_unix = false;
+       bool auth_null_in_list = false;
+       struct nfs_server *server = ERR_PTR(-EACCES);
+       struct nfs_parsed_mount_data *args = mount_info->parsed;
+       rpc_authflavor_t authlist[NFS_MAX_SECFLAVORS];
+       unsigned int authlist_len = ARRAY_SIZE(authlist);
+
+       status = nfs_request_mount(args, mount_info->mntfh, authlist,
+                                       &authlist_len);
+       if (status)
+               return ERR_PTR(status);
 
-       if (mount_info->parsed->need_mount) {
-               status = nfs_request_mount(mount_info->parsed, mount_info->mntfh);
+       /*
+        * Was a sec= authflavor specified in the options? First, verify
+        * whether the server supports it, and then just try to use it if so.
+        */
+       if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR) {
+               status = nfs_verify_authflavor(args, authlist, authlist_len);
+               dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->auth_flavors[0]);
                if (status)
                        return ERR_PTR(status);
+               return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+       }
+
+       /*
+        * No sec= option was provided. RFC 2623, section 2.7 suggests we
+        * SHOULD prefer the flavor listed first. However, some servers list
+        * AUTH_NULL first. Avoid ever choosing AUTH_NULL.
+        */
+       for (i = 0; i < authlist_len; ++i) {
+               rpc_authflavor_t flavor;
+               struct rpcsec_gss_info info;
+
+               flavor = authlist[i];
+               switch (flavor) {
+               case RPC_AUTH_UNIX:
+                       tried_auth_unix = true;
+                       break;
+               case RPC_AUTH_NULL:
+                       auth_null_in_list = true;
+                       continue;
+               default:
+                       if (rpcauth_get_gssinfo(flavor, &info) != 0)
+                               continue;
+                       /* Fallthrough */
+               }
+               dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", flavor);
+               args->auth_flavors[0] = flavor;
+               server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+               if (!IS_ERR(server))
+                       return server;
        }
 
-       /* Get a volume representation */
-       server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+       /*
+        * Nothing we tried so far worked. At this point, give up if we've
+        * already tried AUTH_UNIX or if the server's list doesn't contain
+        * AUTH_NULL
+        */
+       if (tried_auth_unix || !auth_null_in_list)
+               return server;
+
+       /* Last chance! Try AUTH_UNIX */
+       dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", RPC_AUTH_UNIX);
+       args->auth_flavors[0] = RPC_AUTH_UNIX;
+       return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+}
+
+struct dentry *nfs_try_mount(int flags, const char *dev_name,
+                            struct nfs_mount_info *mount_info,
+                            struct nfs_subversion *nfs_mod)
+{
+       struct nfs_server *server;
+
+       if (mount_info->parsed->need_mount)
+               server = nfs_try_mount_request(mount_info, nfs_mod);
+       else
+               server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+
        if (IS_ERR(server))
                return ERR_CAST(server);
 
@@ -2412,7 +2429,21 @@ static int nfs_bdi_register(struct nfs_server *server)
 int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
                        struct nfs_mount_info *mount_info)
 {
-       return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts);
+       int error;
+       unsigned long kflags = 0, kflags_out = 0;
+       if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
+               kflags |= SECURITY_LSM_NATIVE_LABELS;
+
+       error = security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts,
+                                               kflags, &kflags_out);
+       if (error)
+               goto err;
+
+       if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL &&
+               !(kflags_out & SECURITY_LSM_NATIVE_LABELS))
+               NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL;
+err:
+       return error;
 }
 EXPORT_SYMBOL_GPL(nfs_set_sb_security);
 
index 1f1f38f..60395ad 100644 (file)
@@ -479,7 +479,7 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
 
        dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
                dentry->d_parent->d_name.name, dentry->d_name.name,
-               dentry->d_count);
+               d_count(dentry));
        nfs_inc_stats(dir, NFSIOS_SILLYRENAME);
 
        /*
index 07a473f..c0d9317 100644 (file)
@@ -243,6 +243,12 @@ void               nfsd_lockd_shutdown(void);
 #define nfserr_reject_deleg            cpu_to_be32(NFS4ERR_REJECT_DELEG)
 #define nfserr_returnconflict          cpu_to_be32(NFS4ERR_RETURNCONFLICT)
 #define nfserr_deleg_revoked           cpu_to_be32(NFS4ERR_DELEG_REVOKED)
+#define nfserr_partner_notsupp         cpu_to_be32(NFS4ERR_PARTNER_NOTSUPP)
+#define nfserr_partner_no_auth         cpu_to_be32(NFS4ERR_PARTNER_NO_AUTH)
+#define nfserr_metadata_notsupp                cpu_to_be32(NFS4ERR_METADATA_NOTSUPP)
+#define nfserr_offload_denied          cpu_to_be32(NFS4ERR_OFFLOAD_DENIED)
+#define nfserr_wrong_lfs               cpu_to_be32(NFS4ERR_WRONG_LFS)
+#define nfserr_badlabel                cpu_to_be32(NFS4ERR_BADLABEL)
 
 /* error codes for internal use */
 /* if a request fails due to kmalloc failure, it gets dropped.
index 1427de5..af3ba04 100644 (file)
@@ -996,7 +996,7 @@ static int nilfs_attach_snapshot(struct super_block *s, __u64 cno,
 
 static int nilfs_tree_was_touched(struct dentry *root_dentry)
 {
-       return root_dentry->d_count > 1;
+       return d_count(root_dentry) > 1;
 }
 
 /**
index 2bfe6dc..1fedd5f 100644 (file)
@@ -31,7 +31,6 @@ int dir_notify_enable __read_mostly = 1;
 static struct kmem_cache *dnotify_struct_cache __read_mostly;
 static struct kmem_cache *dnotify_mark_cache __read_mostly;
 static struct fsnotify_group *dnotify_group __read_mostly;
-static DEFINE_MUTEX(dnotify_mark_mutex);
 
 /*
  * dnotify will attach one of these to each inode (i_fsnotify_marks) which
@@ -183,7 +182,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
                return;
        dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark);
 
-       mutex_lock(&dnotify_mark_mutex);
+       mutex_lock(&dnotify_group->mark_mutex);
 
        spin_lock(&fsn_mark->lock);
        prev = &dn_mark->dn;
@@ -199,11 +198,12 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
 
        spin_unlock(&fsn_mark->lock);
 
-       /* nothing else could have found us thanks to the dnotify_mark_mutex */
+       /* nothing else could have found us thanks to the dnotify_groups
+          mark_mutex */
        if (dn_mark->dn == NULL)
-               fsnotify_destroy_mark(fsn_mark, dnotify_group);
+               fsnotify_destroy_mark_locked(fsn_mark, dnotify_group);
 
-       mutex_unlock(&dnotify_mark_mutex);
+       mutex_unlock(&dnotify_group->mark_mutex);
 
        fsnotify_put_mark(fsn_mark);
 }
@@ -326,7 +326,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
        new_dn_mark->dn = NULL;
 
        /* this is needed to prevent the fcntl/close race described below */
-       mutex_lock(&dnotify_mark_mutex);
+       mutex_lock(&dnotify_group->mark_mutex);
 
        /* add the new_fsn_mark or find an old one. */
        fsn_mark = fsnotify_find_inode_mark(dnotify_group, inode);
@@ -334,7 +334,8 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
                dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark);
                spin_lock(&fsn_mark->lock);
        } else {
-               fsnotify_add_mark(new_fsn_mark, dnotify_group, inode, NULL, 0);
+               fsnotify_add_mark_locked(new_fsn_mark, dnotify_group, inode,
+                                        NULL, 0);
                spin_lock(&new_fsn_mark->lock);
                fsn_mark = new_fsn_mark;
                dn_mark = new_dn_mark;
@@ -348,9 +349,9 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
 
        /* if (f != filp) means that we lost a race and another task/thread
         * actually closed the fd we are still playing with before we grabbed
-        * the dnotify_mark_mutex and fsn_mark->lock.  Since closing the fd is the
-        * only time we clean up the marks we need to get our mark off
-        * the list. */
+        * the dnotify_groups mark_mutex and fsn_mark->lock.  Since closing the
+        * fd is the only time we clean up the marks we need to get our mark
+        * off the list. */
        if (f != filp) {
                /* if we added ourselves, shoot ourselves, it's possible that
                 * the flush actually did shoot this fsn_mark.  That's fine too
@@ -385,9 +386,9 @@ out:
        spin_unlock(&fsn_mark->lock);
 
        if (destroy)
-               fsnotify_destroy_mark(fsn_mark, dnotify_group);
+               fsnotify_destroy_mark_locked(fsn_mark, dnotify_group);
 
-       mutex_unlock(&dnotify_mark_mutex);
+       mutex_unlock(&dnotify_group->mark_mutex);
        fsnotify_put_mark(fsn_mark);
 out_err:
        if (new_fsn_mark)
index 1ea52f7..e44cb64 100644 (file)
@@ -122,6 +122,7 @@ static int fill_event_metadata(struct fsnotify_group *group,
        metadata->event_len = FAN_EVENT_METADATA_LEN;
        metadata->metadata_len = FAN_EVENT_METADATA_LEN;
        metadata->vers = FANOTIFY_METADATA_VERSION;
+       metadata->reserved = 0;
        metadata->mask = event->mask & FAN_ALL_OUTGOING_EVENTS;
        metadata->pid = pid_vnr(event->tgid);
        if (unlikely(event->mask & FAN_Q_OVERFLOW))
@@ -523,14 +524,18 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
        __u32 removed;
        int destroy_mark;
 
+       mutex_lock(&group->mark_mutex);
        fsn_mark = fsnotify_find_vfsmount_mark(group, mnt);
-       if (!fsn_mark)
+       if (!fsn_mark) {
+               mutex_unlock(&group->mark_mutex);
                return -ENOENT;
+       }
 
        removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
                                                 &destroy_mark);
        if (destroy_mark)
-               fsnotify_destroy_mark(fsn_mark, group);
+               fsnotify_destroy_mark_locked(fsn_mark, group);
+       mutex_unlock(&group->mark_mutex);
 
        fsnotify_put_mark(fsn_mark);
        if (removed & real_mount(mnt)->mnt_fsnotify_mask)
@@ -547,14 +552,19 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group,
        __u32 removed;
        int destroy_mark;
 
+       mutex_lock(&group->mark_mutex);
        fsn_mark = fsnotify_find_inode_mark(group, inode);
-       if (!fsn_mark)
+       if (!fsn_mark) {
+               mutex_unlock(&group->mark_mutex);
                return -ENOENT;
+       }
 
        removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
                                                 &destroy_mark);
        if (destroy_mark)
-               fsnotify_destroy_mark(fsn_mark, group);
+               fsnotify_destroy_mark_locked(fsn_mark, group);
+       mutex_unlock(&group->mark_mutex);
+
        /* matches the fsnotify_find_inode_mark() */
        fsnotify_put_mark(fsn_mark);
        if (removed & inode->i_fsnotify_mask)
@@ -590,35 +600,55 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
        return mask & ~oldmask;
 }
 
+static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
+                                                  struct inode *inode,
+                                                  struct vfsmount *mnt)
+{
+       struct fsnotify_mark *mark;
+       int ret;
+
+       if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks)
+               return ERR_PTR(-ENOSPC);
+
+       mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
+       if (!mark)
+               return ERR_PTR(-ENOMEM);
+
+       fsnotify_init_mark(mark, fanotify_free_mark);
+       ret = fsnotify_add_mark_locked(mark, group, inode, mnt, 0);
+       if (ret) {
+               fsnotify_put_mark(mark);
+               return ERR_PTR(ret);
+       }
+
+       return mark;
+}
+
+
 static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
                                      struct vfsmount *mnt, __u32 mask,
                                      unsigned int flags)
 {
        struct fsnotify_mark *fsn_mark;
        __u32 added;
-       int ret = 0;
 
+       mutex_lock(&group->mark_mutex);
        fsn_mark = fsnotify_find_vfsmount_mark(group, mnt);
        if (!fsn_mark) {
-               if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks)
-                       return -ENOSPC;
-
-               fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
-               if (!fsn_mark)
-                       return -ENOMEM;
-
-               fsnotify_init_mark(fsn_mark, fanotify_free_mark);
-               ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0);
-               if (ret)
-                       goto err;
+               fsn_mark = fanotify_add_new_mark(group, NULL, mnt);
+               if (IS_ERR(fsn_mark)) {
+                       mutex_unlock(&group->mark_mutex);
+                       return PTR_ERR(fsn_mark);
+               }
        }
        added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
+       mutex_unlock(&group->mark_mutex);
 
        if (added & ~real_mount(mnt)->mnt_fsnotify_mask)
                fsnotify_recalc_vfsmount_mask(mnt);
-err:
+
        fsnotify_put_mark(fsn_mark);
-       return ret;
+       return 0;
 }
 
 static int fanotify_add_inode_mark(struct fsnotify_group *group,
@@ -627,7 +657,6 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,
 {
        struct fsnotify_mark *fsn_mark;
        __u32 added;
-       int ret = 0;
 
        pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);
 
@@ -641,27 +670,23 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,
            (atomic_read(&inode->i_writecount) > 0))
                return 0;
 
+       mutex_lock(&group->mark_mutex);
        fsn_mark = fsnotify_find_inode_mark(group, inode);
        if (!fsn_mark) {
-               if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks)
-                       return -ENOSPC;
-
-               fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
-               if (!fsn_mark)
-                       return -ENOMEM;
-
-               fsnotify_init_mark(fsn_mark, fanotify_free_mark);
-               ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0);
-               if (ret)
-                       goto err;
+               fsn_mark = fanotify_add_new_mark(group, inode, NULL);
+               if (IS_ERR(fsn_mark)) {
+                       mutex_unlock(&group->mark_mutex);
+                       return PTR_ERR(fsn_mark);
+               }
        }
        added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
+       mutex_unlock(&group->mark_mutex);
 
        if (added & ~inode->i_fsnotify_mask)
                fsnotify_recalc_inode_mask(inode);
-err:
+
        fsnotify_put_mark(fsn_mark);
-       return ret;
+       return 0;
 }
 
 /* fanotify syscalls */
index 959815c..60f954a 100644 (file)
@@ -636,7 +636,8 @@ static int inotify_new_watch(struct fsnotify_group *group,
                goto out_err;
 
        /* we are on the idr, now get on the inode */
-       ret = fsnotify_add_mark(&tmp_i_mark->fsn_mark, group, inode, NULL, 0);
+       ret = fsnotify_add_mark_locked(&tmp_i_mark->fsn_mark, group, inode,
+                                      NULL, 0);
        if (ret) {
                /* we failed to get on the inode, get off the idr */
                inotify_remove_from_idr(group, tmp_i_mark);
@@ -660,19 +661,13 @@ static int inotify_update_watch(struct fsnotify_group *group, struct inode *inod
 {
        int ret = 0;
 
-retry:
+       mutex_lock(&group->mark_mutex);
        /* try to update and existing watch with the new arg */
        ret = inotify_update_existing_watch(group, inode, arg);
        /* no mark present, try to add a new one */
        if (ret == -ENOENT)
                ret = inotify_new_watch(group, inode, arg);
-       /*
-        * inotify_new_watch could race with another thread which did an
-        * inotify_new_watch between the update_existing and the add watch
-        * here, go back and try to update an existing mark again.
-        */
-       if (ret == -EEXIST)
-               goto retry;
+       mutex_unlock(&group->mark_mutex);
 
        return ret;
 }
index fc6b49b..923fe4a 100644 (file)
  * fsnotify inode mark locking/lifetime/and refcnting
  *
  * REFCNT:
- * The mark->refcnt tells how many "things" in the kernel currently are
- * referencing this object.  The object typically will live inside the kernel
- * with a refcnt of 2, one for each list it is on (i_list, g_list).  Any task
- * which can find this object holding the appropriete locks, can take a reference
- * and the object itself is guaranteed to survive until the reference is dropped.
+ * The group->recnt and mark->refcnt tell how many "things" in the kernel
+ * currently are referencing the objects. Both kind of objects typically will
+ * live inside the kernel with a refcnt of 2, one for its creation and one for
+ * the reference a group and a mark hold to each other.
+ * If you are holding the appropriate locks, you can take a reference and the
+ * object itself is guaranteed to survive until the reference is dropped.
  *
  * LOCKING:
- * There are 3 spinlocks involved with fsnotify inode marks and they MUST
- * be taken in order as follows:
+ * There are 3 locks involved with fsnotify inode marks and they MUST be taken
+ * in order as follows:
  *
+ * group->mark_mutex
  * mark->lock
- * group->mark_lock
  * inode->i_lock
  *
- * mark->lock protects 2 things, mark->group and mark->inode.  You must hold
- * that lock to dereference either of these things (they could be NULL even with
- * the lock)
- *
- * group->mark_lock protects the marks_list anchored inside a given group
- * and each mark is hooked via the g_list.  It also sorta protects the
- * free_g_list, which when used is anchored by a private list on the stack of the
- * task which held the group->mark_lock.
+ * group->mark_mutex protects the marks_list anchored inside a given group and
+ * each mark is hooked via the g_list.  It also protects the groups private
+ * data (i.e group limits).
+
+ * mark->lock protects the marks attributes like its masks and flags.
+ * Furthermore it protects the access to a reference of the group that the mark
+ * is assigned to as well as the access to a reference of the inode/vfsmount
+ * that is being watched by the mark.
  *
  * inode->i_lock protects the i_fsnotify_marks list anchored inside a
  * given inode and each mark is hooked via the i_list. (and sorta the
  * inode.  We take i_lock and walk the i_fsnotify_marks safely.  For each
  * mark on the list we take a reference (so the mark can't disappear under us).
  * We remove that mark form the inode's list of marks and we add this mark to a
- * private list anchored on the stack using i_free_list;  At this point we no
- * longer fear anything finding the mark using the inode's list of marks.
- *
- * We can safely and locklessly run the private list on the stack of everything
- * we just unattached from the original inode.  For each mark on the private list
- * we grab the mark-> and can thus dereference mark->group and mark->inode.  If
- * we see the group and inode are not NULL we take those locks.  Now holding all
- * 3 locks we can completely remove the mark from other tasks finding it in the
- * future.  Remember, 10 things might already be referencing this mark, but they
- * better be holding a ref.  We drop our reference we took before we unhooked it
- * from the inode.  When the ref hits 0 we can free the mark.
- *
+ * private list anchored on the stack using i_free_list; we walk i_free_list
+ * and before we destroy the mark we make sure that we dont race with a
+ * concurrent destroy_group by getting a ref to the marks group and taking the
+ * groups mutex.
+
  * Very similarly for freeing by group, except we use free_g_list.
  *
  * This has the very interesting property of being able to run concurrently with
index 3e64169..fbad622 100644 (file)
@@ -2585,7 +2585,7 @@ static int do_proc_dqstats(struct ctl_table *table, int write,
        return proc_dointvec(table, write, buffer, lenp, ppos);
 }
 
-static ctl_table fs_dqstats_table[] = {
+static struct ctl_table fs_dqstats_table[] = {
        {
                .procname       = "lookups",
                .data           = &dqstats.stat[DQST_LOOKUPS],
@@ -2654,7 +2654,7 @@ static ctl_table fs_dqstats_table[] = {
        { },
 };
 
-static ctl_table fs_table[] = {
+static struct ctl_table fs_table[] = {
        {
                .procname       = "quota",
                .mode           = 0555,
@@ -2663,7 +2663,7 @@ static ctl_table fs_table[] = {
        { },
 };
 
-static ctl_table sys_table[] = {
+static struct ctl_table sys_table[] = {
        {
                .procname       = "fs",
                .mode           = 0555,
index 6b14dc7..f9f49c4 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/hrtimer.h>
 #include <linux/sched/rt.h>
 #include <linux/freezer.h>
+#include <net/ll_poll.h>
 
 #include <asm/uaccess.h>
 
@@ -386,9 +387,10 @@ get_max:
 #define POLLEX_SET (POLLPRI)
 
 static inline void wait_key_set(poll_table *wait, unsigned long in,
-                               unsigned long out, unsigned long bit)
+                               unsigned long out, unsigned long bit,
+                               unsigned int ll_flag)
 {
-       wait->_key = POLLEX_SET;
+       wait->_key = POLLEX_SET | ll_flag;
        if (in & bit)
                wait->_key |= POLLIN_SET;
        if (out & bit)
@@ -402,6 +404,8 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
        poll_table *wait;
        int retval, i, timed_out = 0;
        unsigned long slack = 0;
+       unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0;
+       unsigned long busy_end = 0;
 
        rcu_read_lock();
        retval = max_select_fd(n, fds);
@@ -424,6 +428,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
        retval = 0;
        for (;;) {
                unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
+               bool can_busy_loop = false;
 
                inp = fds->in; outp = fds->out; exp = fds->ex;
                rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;
@@ -451,7 +456,8 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
                                        f_op = f.file->f_op;
                                        mask = DEFAULT_POLLMASK;
                                        if (f_op && f_op->poll) {
-                                               wait_key_set(wait, in, out, bit);
+                                               wait_key_set(wait, in, out,
+                                                            bit, busy_flag);
                                                mask = (*f_op->poll)(f.file, wait);
                                        }
                                        fdput(f);
@@ -470,6 +476,18 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
                                                retval++;
                                                wait->_qproc = NULL;
                                        }
+                                       /* got something, stop busy polling */
+                                       if (retval) {
+                                               can_busy_loop = false;
+                                               busy_flag = 0;
+
+                                       /*
+                                        * only remember a returned
+                                        * POLL_BUSY_LOOP if we asked for it
+                                        */
+                                       } else if (busy_flag & mask)
+                                               can_busy_loop = true;
+
                                }
                        }
                        if (res_in)
@@ -488,6 +506,17 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
                        break;
                }
 
+               /* only if found POLL_BUSY_LOOP sockets && not out of time */
+               if (can_busy_loop && !need_resched()) {
+                       if (!busy_end) {
+                               busy_end = busy_loop_end_time();
+                               continue;
+                       }
+                       if (!busy_loop_timeout(busy_end))
+                               continue;
+               }
+               busy_flag = 0;
+
                /*
                 * If this is the first loop and we have a timeout
                 * given, then we convert to ktime_t and set the to
@@ -719,7 +748,9 @@ struct poll_list {
  * pwait poll_table will be used by the fd-provided poll handler for waiting,
  * if pwait->_qproc is non-NULL.
  */
-static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
+static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait,
+                                    bool *can_busy_poll,
+                                    unsigned int busy_flag)
 {
        unsigned int mask;
        int fd;
@@ -733,7 +764,10 @@ static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
                        mask = DEFAULT_POLLMASK;
                        if (f.file->f_op && f.file->f_op->poll) {
                                pwait->_key = pollfd->events|POLLERR|POLLHUP;
+                               pwait->_key |= busy_flag;
                                mask = f.file->f_op->poll(f.file, pwait);
+                               if (mask & busy_flag)
+                                       *can_busy_poll = true;
                        }
                        /* Mask out unneeded events. */
                        mask &= pollfd->events | POLLERR | POLLHUP;
@@ -752,6 +786,8 @@ static int do_poll(unsigned int nfds,  struct poll_list *list,
        ktime_t expire, *to = NULL;
        int timed_out = 0, count = 0;
        unsigned long slack = 0;
+       unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0;
+       unsigned long busy_end = 0;
 
        /* Optimise the no-wait case */
        if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
@@ -764,6 +800,7 @@ static int do_poll(unsigned int nfds,  struct poll_list *list,
 
        for (;;) {
                struct poll_list *walk;
+               bool can_busy_loop = false;
 
                for (walk = list; walk != NULL; walk = walk->next) {
                        struct pollfd * pfd, * pfd_end;
@@ -778,9 +815,13 @@ static int do_poll(unsigned int nfds,  struct poll_list *list,
                                 * this. They'll get immediately deregistered
                                 * when we break out and return.
                                 */
-                               if (do_pollfd(pfd, pt)) {
+                               if (do_pollfd(pfd, pt, &can_busy_loop,
+                                             busy_flag)) {
                                        count++;
                                        pt->_qproc = NULL;
+                                       /* found something, stop busy polling */
+                                       busy_flag = 0;
+                                       can_busy_loop = false;
                                }
                        }
                }
@@ -797,6 +838,17 @@ static int do_poll(unsigned int nfds,  struct poll_list *list,
                if (count || timed_out)
                        break;
 
+               /* only if found POLL_BUSY_LOOP sockets && not out of time */
+               if (can_busy_loop && !need_resched()) {
+                       if (!busy_end) {
+                               busy_end = busy_loop_end_time();
+                               continue;
+                       }
+                       if (!busy_loop_timeout(busy_end))
+                               continue;
+               }
+               busy_flag = 0;
+
                /*
                 * If this is the first loop and we have a timeout
                 * given, then we convert to ktime_t and set the to
index 774c1eb..3135c25 100644 (file)
@@ -921,3 +921,57 @@ struct hlist_node *seq_hlist_next_rcu(void *v,
                return rcu_dereference(node->next);
 }
 EXPORT_SYMBOL(seq_hlist_next_rcu);
+
+/**
+ * seq_hlist_start_precpu - start an iteration of a percpu hlist array
+ * @head: pointer to percpu array of struct hlist_heads
+ * @cpu:  pointer to cpu "cursor"
+ * @pos:  start position of sequence
+ *
+ * Called at seq_file->op->start().
+ */
+struct hlist_node *
+seq_hlist_start_percpu(struct hlist_head __percpu *head, int *cpu, loff_t pos)
+{
+       struct hlist_node *node;
+
+       for_each_possible_cpu(*cpu) {
+               hlist_for_each(node, per_cpu_ptr(head, *cpu)) {
+                       if (pos-- == 0)
+                               return node;
+               }
+       }
+       return NULL;
+}
+EXPORT_SYMBOL(seq_hlist_start_percpu);
+
+/**
+ * seq_hlist_next_percpu - move to the next position of the percpu hlist array
+ * @v:    pointer to current hlist_node
+ * @head: pointer to percpu array of struct hlist_heads
+ * @cpu:  pointer to cpu "cursor"
+ * @pos:  start position of sequence
+ *
+ * Called at seq_file->op->next().
+ */
+struct hlist_node *
+seq_hlist_next_percpu(void *v, struct hlist_head __percpu *head,
+                       int *cpu, loff_t *pos)
+{
+       struct hlist_node *node = v;
+
+       ++*pos;
+
+       if (node->next)
+               return node->next;
+
+       for (*cpu = cpumask_next(*cpu, cpu_possible_mask); *cpu < nr_cpu_ids;
+            *cpu = cpumask_next(*cpu, cpu_possible_mask)) {
+               struct hlist_head *bucket = per_cpu_ptr(head, *cpu);
+
+               if (!hlist_empty(bucket))
+                       return bucket->first;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL(seq_hlist_next_percpu);
index 6313b69..4a45080 100644 (file)
@@ -71,6 +71,7 @@ xfs-y                         += xfs_alloc.o \
                                   xfs_dir2_sf.o \
                                   xfs_ialloc.o \
                                   xfs_ialloc_btree.o \
+                                  xfs_icreate_item.o \
                                   xfs_inode.o \
                                   xfs_log_recover.o \
                                   xfs_mount.o \
index 5673bcf..71596e5 100644 (file)
@@ -175,6 +175,7 @@ xfs_alloc_compute_diff(
        xfs_agblock_t   wantbno,        /* target starting block */
        xfs_extlen_t    wantlen,        /* target length */
        xfs_extlen_t    alignment,      /* target alignment */
+       char            userdata,       /* are we allocating data? */
        xfs_agblock_t   freebno,        /* freespace's starting block */
        xfs_extlen_t    freelen,        /* freespace's length */
        xfs_agblock_t   *newbnop)       /* result: best start block from free */
@@ -189,7 +190,14 @@ xfs_alloc_compute_diff(
        ASSERT(freelen >= wantlen);
        freeend = freebno + freelen;
        wantend = wantbno + wantlen;
-       if (freebno >= wantbno) {
+       /*
+        * We want to allocate from the start of a free extent if it is past
+        * the desired block or if we are allocating user data and the free
+        * extent is before desired block. The second case is there to allow
+        * for contiguous allocation from the remaining free space if the file
+        * grows in the short term.
+        */
+       if (freebno >= wantbno || (userdata && freeend < wantend)) {
                if ((newbno1 = roundup(freebno, alignment)) >= freeend)
                        newbno1 = NULLAGBLOCK;
        } else if (freeend >= wantend && alignment > 1) {
@@ -805,7 +813,8 @@ xfs_alloc_find_best_extent(
                        xfs_alloc_fix_len(args);
 
                        sdiff = xfs_alloc_compute_diff(args->agbno, args->len,
-                                                      args->alignment, *sbnoa,
+                                                      args->alignment,
+                                                      args->userdata, *sbnoa,
                                                       *slena, &new);
 
                        /*
@@ -976,7 +985,8 @@ restart:
                        if (args->len < blen)
                                continue;
                        ltdiff = xfs_alloc_compute_diff(args->agbno, args->len,
-                               args->alignment, ltbnoa, ltlena, &ltnew);
+                               args->alignment, args->userdata, ltbnoa,
+                               ltlena, &ltnew);
                        if (ltnew != NULLAGBLOCK &&
                            (args->len > blen || ltdiff < bdiff)) {
                                bdiff = ltdiff;
@@ -1128,7 +1138,8 @@ restart:
                        args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen);
                        xfs_alloc_fix_len(args);
                        ltdiff = xfs_alloc_compute_diff(args->agbno, args->len,
-                               args->alignment, ltbnoa, ltlena, &ltnew);
+                               args->alignment, args->userdata, ltbnoa,
+                               ltlena, &ltnew);
 
                        error = xfs_alloc_find_best_extent(args,
                                                &bno_cur_lt, &bno_cur_gt,
@@ -1144,7 +1155,8 @@ restart:
                        args->len = XFS_EXTLEN_MIN(gtlena, args->maxlen);
                        xfs_alloc_fix_len(args);
                        gtdiff = xfs_alloc_compute_diff(args->agbno, args->len,
-                               args->alignment, gtbnoa, gtlena, &gtnew);
+                               args->alignment, args->userdata, gtbnoa,
+                               gtlena, &gtnew);
 
                        error = xfs_alloc_find_best_extent(args,
                                                &bno_cur_gt, &bno_cur_lt,
@@ -1203,7 +1215,7 @@ restart:
        }
        rlen = args->len;
        (void)xfs_alloc_compute_diff(args->agbno, rlen, args->alignment,
-                                    ltbnoa, ltlena, &ltnew);
+                                    args->userdata, ltbnoa, ltlena, &ltnew);
        ASSERT(ltnew >= ltbno);
        ASSERT(ltnew + rlen <= ltbnoa + ltlena);
        ASSERT(ltnew + rlen <= be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length));
index 70c43d9..1b726d6 100644 (file)
@@ -196,6 +196,8 @@ typedef __be64 xfs_bmbt_ptr_t, xfs_bmdr_ptr_t;
 #define XFS_BMDR_SPACE_CALC(nrecs) \
        (int)(sizeof(xfs_bmdr_block_t) + \
               ((nrecs) * (sizeof(xfs_bmbt_key_t) + sizeof(xfs_bmbt_ptr_t))))
+#define XFS_BMAP_BMDR_SPACE(bb) \
+       (XFS_BMDR_SPACE_CALC(be16_to_cpu((bb)->bb_numrecs)))
 
 /*
  * Maximum number of bmap btree levels.
index 4ec4317..bfc4e0c 100644 (file)
@@ -140,6 +140,16 @@ xfs_buf_item_size(
 
        ASSERT(bip->bli_flags & XFS_BLI_LOGGED);
 
+       if (bip->bli_flags & XFS_BLI_ORDERED) {
+               /*
+                * The buffer has been logged just to order it.
+                * It is not being included in the transaction
+                * commit, so no vectors are used at all.
+                */
+               trace_xfs_buf_item_size_ordered(bip);
+               return XFS_LOG_VEC_ORDERED;
+       }
+
        /*
         * the vector count is based on the number of buffer vectors we have
         * dirty bits in. This will only be greater than one when we have a
@@ -212,6 +222,7 @@ xfs_buf_item_format_segment(
                goto out;
        }
 
+
        /*
         * Fill in an iovec for each set of contiguous chunks.
         */
@@ -299,18 +310,36 @@ xfs_buf_item_format(
 
        /*
         * If it is an inode buffer, transfer the in-memory state to the
-        * format flags and clear the in-memory state. We do not transfer
+        * format flags and clear the in-memory state.
+        *
+        * For buffer based inode allocation, we do not transfer
         * this state if the inode buffer allocation has not yet been committed
         * to the log as setting the XFS_BLI_INODE_BUF flag will prevent
         * correct replay of the inode allocation.
+        *
+        * For icreate item based inode allocation, the buffers aren't written
+        * to the journal during allocation, and hence we should always tag the
+        * buffer as an inode buffer so that the correct unlinked list replay
+        * occurs during recovery.
         */
        if (bip->bli_flags & XFS_BLI_INODE_BUF) {
-               if (!((bip->bli_flags & XFS_BLI_INODE_ALLOC_BUF) &&
+               if (xfs_sb_version_hascrc(&lip->li_mountp->m_sb) ||
+                   !((bip->bli_flags & XFS_BLI_INODE_ALLOC_BUF) &&
                      xfs_log_item_in_current_chkpt(lip)))
                        bip->__bli_format.blf_flags |= XFS_BLF_INODE_BUF;
                bip->bli_flags &= ~XFS_BLI_INODE_BUF;
        }
 
+       if ((bip->bli_flags & (XFS_BLI_ORDERED|XFS_BLI_STALE)) ==
+                                                       XFS_BLI_ORDERED) {
+               /*
+                * The buffer has been logged just to order it.  It is not being
+                * included in the transaction commit, so don't format it.
+                */
+               trace_xfs_buf_item_format_ordered(bip);
+               return;
+       }
+
        for (i = 0; i < bip->bli_format_count; i++) {
                vecp = xfs_buf_item_format_segment(bip, vecp, offset,
                                                &bip->bli_formats[i]);
@@ -340,6 +369,7 @@ xfs_buf_item_pin(
 
        ASSERT(atomic_read(&bip->bli_refcount) > 0);
        ASSERT((bip->bli_flags & XFS_BLI_LOGGED) ||
+              (bip->bli_flags & XFS_BLI_ORDERED) ||
               (bip->bli_flags & XFS_BLI_STALE));
 
        trace_xfs_buf_item_pin(bip);
@@ -512,8 +542,9 @@ xfs_buf_item_unlock(
 {
        struct xfs_buf_log_item *bip = BUF_ITEM(lip);
        struct xfs_buf          *bp = bip->bli_buf;
-       int                     aborted, clean, i;
-       uint                    hold;
+       bool                    clean;
+       bool                    aborted;
+       int                     flags;
 
        /* Clear the buffer's association with this transaction. */
        bp->b_transp = NULL;
@@ -524,23 +555,21 @@ xfs_buf_item_unlock(
         * (cancelled) buffers at unpin time, but we'll never go through the
         * pin/unpin cycle if we abort inside commit.
         */
-       aborted = (lip->li_flags & XFS_LI_ABORTED) != 0;
-
+       aborted = (lip->li_flags & XFS_LI_ABORTED) ? true : false;
        /*
-        * Before possibly freeing the buf item, determine if we should
-        * release the buffer at the end of this routine.
+        * Before possibly freeing the buf item, copy the per-transaction state
+        * so we can reference it safely later after clearing it from the
+        * buffer log item.
         */
-       hold = bip->bli_flags & XFS_BLI_HOLD;
-
-       /* Clear the per transaction state. */
-       bip->bli_flags &= ~(XFS_BLI_LOGGED | XFS_BLI_HOLD);
+       flags = bip->bli_flags;
+       bip->bli_flags &= ~(XFS_BLI_LOGGED | XFS_BLI_HOLD | XFS_BLI_ORDERED);
 
        /*
         * If the buf item is marked stale, then don't do anything.  We'll
         * unlock the buffer and free the buf item when the buffer is unpinned
         * for the last time.
         */
-       if (bip->bli_flags & XFS_BLI_STALE) {
+       if (flags & XFS_BLI_STALE) {
                trace_xfs_buf_item_unlock_stale(bip);
                ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL);
                if (!aborted) {
@@ -557,13 +586,19 @@ xfs_buf_item_unlock(
         * be the only reference to the buf item, so we free it anyway
         * regardless of whether it is dirty or not. A dirty abort implies a
         * shutdown, anyway.
+        *
+        * Ordered buffers are dirty but may have no recorded changes, so ensure
+        * we only release clean items here.
         */
-       clean = 1;
-       for (i = 0; i < bip->bli_format_count; i++) {
-               if (!xfs_bitmap_empty(bip->bli_formats[i].blf_data_map,
-                            bip->bli_formats[i].blf_map_size)) {
-                       clean = 0;
-                       break;
+       clean = (flags & XFS_BLI_DIRTY) ? false : true;
+       if (clean) {
+               int i;
+               for (i = 0; i < bip->bli_format_count; i++) {
+                       if (!xfs_bitmap_empty(bip->bli_formats[i].blf_data_map,
+                                    bip->bli_formats[i].blf_map_size)) {
+                               clean = false;
+                               break;
+                       }
                }
        }
        if (clean)
@@ -576,7 +611,7 @@ xfs_buf_item_unlock(
        } else
                atomic_dec(&bip->bli_refcount);
 
-       if (!hold)
+       if (!(flags & XFS_BLI_HOLD))
                xfs_buf_relse(bp);
 }
 
@@ -841,12 +876,6 @@ xfs_buf_item_log(
        uint                    end;
        struct xfs_buf          *bp = bip->bli_buf;
 
-       /*
-        * Mark the item as having some dirty data for
-        * quick reference in xfs_buf_item_dirty.
-        */
-       bip->bli_flags |= XFS_BLI_DIRTY;
-
        /*
         * walk each buffer segment and mark them dirty appropriately.
         */
@@ -873,7 +902,7 @@ xfs_buf_item_log(
 
 
 /*
- * Return 1 if the buffer has some data that has been logged (at any
+ * Return 1 if the buffer has been logged or ordered in a transaction (at any
  * point, not just the current transaction) and 0 if not.
  */
 uint
@@ -907,11 +936,11 @@ void
 xfs_buf_item_relse(
        xfs_buf_t       *bp)
 {
-       xfs_buf_log_item_t      *bip;
+       xfs_buf_log_item_t      *bip = bp->b_fspriv;
 
        trace_xfs_buf_item_relse(bp, _RET_IP_);
+       ASSERT(!(bip->bli_item.li_flags & XFS_LI_IN_AIL));
 
-       bip = bp->b_fspriv;
        bp->b_fspriv = bip->bli_item.li_bio_list;
        if (bp->b_fspriv == NULL)
                bp->b_iodone = NULL;
index 2573d2a..0f1c247 100644 (file)
@@ -120,6 +120,7 @@ xfs_blft_from_flags(struct xfs_buf_log_format *blf)
 #define        XFS_BLI_INODE_ALLOC_BUF 0x10
 #define XFS_BLI_STALE_INODE    0x20
 #define        XFS_BLI_INODE_BUF       0x40
+#define        XFS_BLI_ORDERED         0x80
 
 #define XFS_BLI_FLAGS \
        { XFS_BLI_HOLD,         "HOLD" }, \
@@ -128,7 +129,8 @@ xfs_blft_from_flags(struct xfs_buf_log_format *blf)
        { XFS_BLI_LOGGED,       "LOGGED" }, \
        { XFS_BLI_INODE_ALLOC_BUF, "INODE_ALLOC" }, \
        { XFS_BLI_STALE_INODE,  "STALE_INODE" }, \
-       { XFS_BLI_INODE_BUF,    "INODE_BUF" }
+       { XFS_BLI_INODE_BUF,    "INODE_BUF" }, \
+       { XFS_BLI_ORDERED,      "ORDERED" }
 
 
 #ifdef __KERNEL__
index c407e1c..e36445c 100644 (file)
@@ -24,6 +24,9 @@
 #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_btree.h"
 #include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_inode_item.h"
@@ -182,7 +185,7 @@ xfs_swap_extents_check_format(
         */
        if (tip->i_d.di_format == XFS_DINODE_FMT_BTREE) {
                if (XFS_IFORK_BOFF(ip) &&
-                   tip->i_df.if_broot_bytes > XFS_IFORK_BOFF(ip))
+                   XFS_BMAP_BMDR_SPACE(tip->i_df.if_broot) > XFS_IFORK_BOFF(ip))
                        return EINVAL;
                if (XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK) <=
                    XFS_IFORK_MAXEXT(ip, XFS_DATA_FORK))
@@ -192,9 +195,8 @@ xfs_swap_extents_check_format(
        /* Reciprocal target->temp btree format checks */
        if (ip->i_d.di_format == XFS_DINODE_FMT_BTREE) {
                if (XFS_IFORK_BOFF(tip) &&
-                   ip->i_df.if_broot_bytes > XFS_IFORK_BOFF(tip))
+                   XFS_BMAP_BMDR_SPACE(ip->i_df.if_broot) > XFS_IFORK_BOFF(tip))
                        return EINVAL;
-
                if (XFS_IFORK_NEXTENTS(ip, XFS_DATA_FORK) <=
                    XFS_IFORK_MAXEXT(tip, XFS_DATA_FORK))
                        return EINVAL;
index e0cc124..2aed25c 100644 (file)
@@ -1108,6 +1108,7 @@ xfs_dir2_leaf_readbuf(
        struct xfs_mount        *mp = dp->i_mount;
        struct xfs_buf          *bp = *bpp;
        struct xfs_bmbt_irec    *map = mip->map;
+       struct blk_plug         plug;
        int                     error = 0;
        int                     length;
        int                     i;
@@ -1236,6 +1237,7 @@ xfs_dir2_leaf_readbuf(
        /*
         * Do we need more readahead?
         */
+       blk_start_plug(&plug);
        for (mip->ra_index = mip->ra_offset = i = 0;
             mip->ra_want > mip->ra_current && i < mip->map_blocks;
             i += mp->m_dirblkfsbs) {
@@ -1287,6 +1289,7 @@ xfs_dir2_leaf_readbuf(
                        }
                }
        }
+       blk_finish_plug(&plug);
 
 out:
        *bpp = bp;
index 044e97a..f01012d 100644 (file)
@@ -570,13 +570,13 @@ xfs_qm_dqtobp(
        xfs_buf_t               **O_bpp,
        uint                    flags)
 {
-       xfs_bmbt_irec_t map;
-       int             nmaps = 1, error;
-       xfs_buf_t       *bp;
-       xfs_inode_t     *quotip = XFS_DQ_TO_QIP(dqp);
-       xfs_mount_t     *mp = dqp->q_mount;
-       xfs_dqid_t      id = be32_to_cpu(dqp->q_core.d_id);
-       xfs_trans_t     *tp = (tpp ? *tpp : NULL);
+       struct xfs_bmbt_irec    map;
+       int                     nmaps = 1, error;
+       struct xfs_buf          *bp;
+       struct xfs_inode        *quotip = xfs_dq_to_quota_inode(dqp);
+       struct xfs_mount        *mp = dqp->q_mount;
+       xfs_dqid_t              id = be32_to_cpu(dqp->q_core.d_id);
+       struct xfs_trans        *tp = (tpp ? *tpp : NULL);
 
        dqp->q_fileoffset = (xfs_fileoff_t)id / mp->m_quotainfo->qi_dqperchunk;
 
@@ -804,7 +804,7 @@ xfs_qm_dqget(
        xfs_dquot_t     **O_dqpp) /* OUT : locked incore dquot */
 {
        struct xfs_quotainfo    *qi = mp->m_quotainfo;
-       struct radix_tree_root *tree = XFS_DQUOT_TREE(qi, type);
+       struct radix_tree_root *tree = xfs_dquot_tree(qi, type);
        struct xfs_dquot        *dqp;
        int                     error;
 
index 4f0ebfc..b596626 100644 (file)
@@ -143,10 +143,6 @@ static inline xfs_dquot_t *xfs_inode_dquot(struct xfs_inode *ip, int type)
 #define XFS_QM_ISUDQ(dqp)      ((dqp)->dq_flags & XFS_DQ_USER)
 #define XFS_QM_ISPDQ(dqp)      ((dqp)->dq_flags & XFS_DQ_PROJ)
 #define XFS_QM_ISGDQ(dqp)      ((dqp)->dq_flags & XFS_DQ_GROUP)
-#define XFS_DQ_TO_QINF(dqp)    ((dqp)->q_mount->m_quotainfo)
-#define XFS_DQ_TO_QIP(dqp)     (XFS_QM_ISUDQ(dqp) ? \
-                                XFS_DQ_TO_QINF(dqp)->qi_uquotaip : \
-                                XFS_DQ_TO_QINF(dqp)->qi_gquotaip)
 
 extern int             xfs_qm_dqread(struct xfs_mount *, xfs_dqid_t, uint,
                                        uint, struct xfs_dquot  **);
index 3c3644e..614eb0c 100644 (file)
@@ -176,7 +176,7 @@ xfs_growfs_data_private(
        if (!bp)
                return EIO;
        if (bp->b_error) {
-               int     error = bp->b_error;
+               error = bp->b_error;
                xfs_buf_relse(bp);
                return error;
        }
index c8f5ae1..7a0c17d 100644 (file)
@@ -38,6 +38,7 @@
 #include "xfs_bmap.h"
 #include "xfs_cksum.h"
 #include "xfs_buf_item.h"
+#include "xfs_icreate_item.h"
 
 
 /*
@@ -150,12 +151,16 @@ xfs_check_agi_freecount(
 #endif
 
 /*
- * Initialise a new set of inodes.
+ * Initialise a new set of inodes. When called without a transaction context
+ * (e.g. from recovery) we initiate a delayed write of the inode buffers rather
+ * than logging them (which in a transaction context puts them into the AIL
+ * for writeback rather than the xfsbufd queue).
  */
-STATIC int
+int
 xfs_ialloc_inode_init(
        struct xfs_mount        *mp,
        struct xfs_trans        *tp,
+       struct list_head        *buffer_list,
        xfs_agnumber_t          agno,
        xfs_agblock_t           agbno,
        xfs_agblock_t           length,
@@ -208,6 +213,18 @@ xfs_ialloc_inode_init(
                version = 3;
                ino = XFS_AGINO_TO_INO(mp, agno,
                                       XFS_OFFBNO_TO_AGINO(mp, agbno, 0));
+
+               /*
+                * log the initialisation that is about to take place as an
+                * logical operation. This means the transaction does not
+                * need to log the physical changes to the inode buffers as log
+                * recovery will know what initialisation is actually needed.
+                * Hence we only need to log the buffers as "ordered" buffers so
+                * they track in the AIL as if they were physically logged.
+                */
+               if (tp)
+                       xfs_icreate_log(tp, agno, agbno, XFS_IALLOC_INODES(mp),
+                                       mp->m_sb.sb_inodesize, length, gen);
        } else if (xfs_sb_version_hasnlink(&mp->m_sb))
                version = 2;
        else
@@ -223,13 +240,8 @@ xfs_ialloc_inode_init(
                                         XBF_UNMAPPED);
                if (!fbuf)
                        return ENOMEM;
-               /*
-                * Initialize all inodes in this buffer and then log them.
-                *
-                * XXX: It would be much better if we had just one transaction
-                *      to log a whole cluster of inodes instead of all the
-                *      individual transactions causing a lot of log traffic.
-                */
+
+               /* Initialize the inode buffers and log them appropriately. */
                fbuf->b_ops = &xfs_inode_buf_ops;
                xfs_buf_zero(fbuf, 0, BBTOB(fbuf->b_length));
                for (i = 0; i < ninodes; i++) {
@@ -247,18 +259,39 @@ xfs_ialloc_inode_init(
                                ino++;
                                uuid_copy(&free->di_uuid, &mp->m_sb.sb_uuid);
                                xfs_dinode_calc_crc(mp, free);
-                       } else {
+                       } else if (tp) {
                                /* just log the inode core */
                                xfs_trans_log_buf(tp, fbuf, ioffset,
                                                  ioffset + isize - 1);
                        }
                }
-               if (version == 3) {
-                       /* need to log the entire buffer */
-                       xfs_trans_log_buf(tp, fbuf, 0,
-                                         BBTOB(fbuf->b_length) - 1);
+
+               if (tp) {
+                       /*
+                        * Mark the buffer as an inode allocation buffer so it
+                        * sticks in AIL at the point of this allocation
+                        * transaction. This ensures the they are on disk before
+                        * the tail of the log can be moved past this
+                        * transaction (i.e. by preventing relogging from moving
+                        * it forward in the log).
+                        */
+                       xfs_trans_inode_alloc_buf(tp, fbuf);
+                       if (version == 3) {
+                               /*
+                                * Mark the buffer as ordered so that they are
+                                * not physically logged in the transaction but
+                                * still tracked in the AIL as part of the
+                                * transaction and pin the log appropriately.
+                                */
+                               xfs_trans_ordered_buf(tp, fbuf);
+                               xfs_trans_log_buf(tp, fbuf, 0,
+                                                 BBTOB(fbuf->b_length) - 1);
+                       }
+               } else {
+                       fbuf->b_flags |= XBF_DONE;
+                       xfs_buf_delwri_queue(fbuf, buffer_list);
+                       xfs_buf_relse(fbuf);
                }
-               xfs_trans_inode_alloc_buf(tp, fbuf);
        }
        return 0;
 }
@@ -303,7 +336,7 @@ xfs_ialloc_ag_alloc(
         * First try to allocate inodes contiguous with the last-allocated
         * chunk of inodes.  If the filesystem is striped, this will fill
         * an entire stripe unit with inodes.
-        */
+        */
        agi = XFS_BUF_TO_AGI(agbp);
        newino = be32_to_cpu(agi->agi_newino);
        agno = be32_to_cpu(agi->agi_seqno);
@@ -402,7 +435,7 @@ xfs_ialloc_ag_alloc(
         * rather than a linear progression to prevent the next generation
         * number from being easily guessable.
         */
-       error = xfs_ialloc_inode_init(args.mp, tp, agno, args.agbno,
+       error = xfs_ialloc_inode_init(args.mp, tp, NULL, agno, args.agbno,
                        args.len, prandom_u32());
 
        if (error)
@@ -615,8 +648,7 @@ xfs_ialloc_get_rec(
        struct xfs_btree_cur    *cur,
        xfs_agino_t             agino,
        xfs_inobt_rec_incore_t  *rec,
-       int                     *done,
-       int                     left)
+       int                     *done)
 {
        int                     error;
        int                     i;
@@ -724,12 +756,12 @@ xfs_dialloc_ag(
                    pag->pagl_leftrec != NULLAGINO &&
                    pag->pagl_rightrec != NULLAGINO) {
                        error = xfs_ialloc_get_rec(tcur, pag->pagl_leftrec,
-                                                  &trec, &doneleft, 1);
+                                                  &trec, &doneleft);
                        if (error)
                                goto error1;
 
                        error = xfs_ialloc_get_rec(cur, pag->pagl_rightrec,
-                                                  &rec, &doneright, 0);
+                                                  &rec, &doneright);
                        if (error)
                                goto error1;
                } else {
index c8da3df..68c0732 100644 (file)
@@ -150,6 +150,14 @@ int xfs_inobt_lookup(struct xfs_btree_cur *cur, xfs_agino_t ino,
 int xfs_inobt_get_rec(struct xfs_btree_cur *cur,
                xfs_inobt_rec_incore_t *rec, int *stat);
 
+/*
+ * Inode chunk initialisation routine
+ */
+int xfs_ialloc_inode_init(struct xfs_mount *mp, struct xfs_trans *tp,
+                         struct list_head *buffer_list,
+                         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 96e344e..9560dc1 100644 (file)
@@ -335,7 +335,8 @@ xfs_iget_cache_miss(
        iflags = XFS_INEW;
        if (flags & XFS_IGET_DONTCACHE)
                iflags |= XFS_IDONTCACHE;
-       ip->i_udquot = ip->i_gdquot = NULL;
+       ip->i_udquot = NULL;
+       ip->i_gdquot = NULL;
        xfs_iflags_set(ip, iflags);
 
        /* insert the new inode */
index e0f138c..a01afbb 100644 (file)
@@ -40,7 +40,6 @@ void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
 int xfs_icache_free_eofblocks(struct xfs_mount *, struct xfs_eofblocks *);
 void xfs_eofblocks_worker(struct work_struct *);
 
-int xfs_sync_inode_grab(struct xfs_inode *ip);
 int xfs_inode_ag_iterator(struct xfs_mount *mp,
        int (*execute)(struct xfs_inode *ip, struct xfs_perag *pag,
                int flags, void *args),
diff --git a/fs/xfs/xfs_icreate_item.c b/fs/xfs/xfs_icreate_item.c
new file mode 100644 (file)
index 0000000..7716a4e
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2008-2010, 2013 Dave Chinner
+ * 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_types.h"
+#include "xfs_bit.h"
+#include "xfs_log.h"
+#include "xfs_inum.h"
+#include "xfs_trans.h"
+#include "xfs_buf_item.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir2.h"
+#include "xfs_mount.h"
+#include "xfs_trans_priv.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_inode_item.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_error.h"
+#include "xfs_icreate_item.h"
+
+kmem_zone_t    *xfs_icreate_zone;              /* inode create item zone */
+
+static inline struct xfs_icreate_item *ICR_ITEM(struct xfs_log_item *lip)
+{
+       return container_of(lip, struct xfs_icreate_item, ic_item);
+}
+
+/*
+ * This returns the number of iovecs needed to log the given inode item.
+ *
+ * We only need one iovec for the icreate log structure.
+ */
+STATIC uint
+xfs_icreate_item_size(
+       struct xfs_log_item     *lip)
+{
+       return 1;
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given inode create log item.
+ */
+STATIC void
+xfs_icreate_item_format(
+       struct xfs_log_item     *lip,
+       struct xfs_log_iovec    *log_vector)
+{
+       struct xfs_icreate_item *icp = ICR_ITEM(lip);
+
+       log_vector->i_addr = (xfs_caddr_t)&icp->ic_format;
+       log_vector->i_len  = sizeof(struct xfs_icreate_log);
+       log_vector->i_type = XLOG_REG_TYPE_ICREATE;
+}
+
+
+/* Pinning has no meaning for the create item, so just return. */
+STATIC void
+xfs_icreate_item_pin(
+       struct xfs_log_item     *lip)
+{
+}
+
+
+/* pinning has no meaning for the create item, so just return. */
+STATIC void
+xfs_icreate_item_unpin(
+       struct xfs_log_item     *lip,
+       int                     remove)
+{
+}
+
+STATIC void
+xfs_icreate_item_unlock(
+       struct xfs_log_item     *lip)
+{
+       struct xfs_icreate_item *icp = ICR_ITEM(lip);
+
+       if (icp->ic_item.li_flags & XFS_LI_ABORTED)
+               kmem_zone_free(xfs_icreate_zone, icp);
+       return;
+}
+
+/*
+ * Because we have ordered buffers being tracked in the AIL for the inode
+ * creation, we don't need the create item after this. Hence we can free
+ * the log item and return -1 to tell the caller we're done with the item.
+ */
+STATIC xfs_lsn_t
+xfs_icreate_item_committed(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+       struct xfs_icreate_item *icp = ICR_ITEM(lip);
+
+       kmem_zone_free(xfs_icreate_zone, icp);
+       return (xfs_lsn_t)-1;
+}
+
+/* item can never get into the AIL */
+STATIC uint
+xfs_icreate_item_push(
+       struct xfs_log_item     *lip,
+       struct list_head        *buffer_list)
+{
+       ASSERT(0);
+       return XFS_ITEM_SUCCESS;
+}
+
+/* Ordered buffers do the dependency tracking here, so this does nothing. */
+STATIC void
+xfs_icreate_item_committing(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+}
+
+/*
+ * This is the ops vector shared by all buf log items.
+ */
+static struct xfs_item_ops xfs_icreate_item_ops = {
+       .iop_size       = xfs_icreate_item_size,
+       .iop_format     = xfs_icreate_item_format,
+       .iop_pin        = xfs_icreate_item_pin,
+       .iop_unpin      = xfs_icreate_item_unpin,
+       .iop_push       = xfs_icreate_item_push,
+       .iop_unlock     = xfs_icreate_item_unlock,
+       .iop_committed  = xfs_icreate_item_committed,
+       .iop_committing = xfs_icreate_item_committing,
+};
+
+
+/*
+ * Initialize the inode log item for a newly allocated (in-core) inode.
+ *
+ * Inode extents can only reside within an AG. Hence specify the starting
+ * block for the inode chunk by offset within an AG as well as the
+ * length of the allocated extent.
+ *
+ * This joins the item to the transaction and marks it dirty so
+ * that we don't need a separate call to do this, nor does the
+ * caller need to know anything about the icreate item.
+ */
+void
+xfs_icreate_log(
+       struct xfs_trans        *tp,
+       xfs_agnumber_t          agno,
+       xfs_agblock_t           agbno,
+       unsigned int            count,
+       unsigned int            inode_size,
+       xfs_agblock_t           length,
+       unsigned int            generation)
+{
+       struct xfs_icreate_item *icp;
+
+       icp = kmem_zone_zalloc(xfs_icreate_zone, KM_SLEEP);
+
+       xfs_log_item_init(tp->t_mountp, &icp->ic_item, XFS_LI_ICREATE,
+                         &xfs_icreate_item_ops);
+
+       icp->ic_format.icl_type = XFS_LI_ICREATE;
+       icp->ic_format.icl_size = 1;    /* single vector */
+       icp->ic_format.icl_ag = cpu_to_be32(agno);
+       icp->ic_format.icl_agbno = cpu_to_be32(agbno);
+       icp->ic_format.icl_count = cpu_to_be32(count);
+       icp->ic_format.icl_isize = cpu_to_be32(inode_size);
+       icp->ic_format.icl_length = cpu_to_be32(length);
+       icp->ic_format.icl_gen = cpu_to_be32(generation);
+
+       xfs_trans_add_item(tp, &icp->ic_item);
+       tp->t_flags |= XFS_TRANS_DIRTY;
+       icp->ic_item.li_desc->lid_flags |= XFS_LID_DIRTY;
+}
diff --git a/fs/xfs/xfs_icreate_item.h b/fs/xfs/xfs_icreate_item.h
new file mode 100644 (file)
index 0000000..88ba8aa
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2008-2010, Dave Chinner
+ * 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_ICREATE_ITEM_H
+#define XFS_ICREATE_ITEM_H     1
+
+/*
+ * on disk log item structure
+ *
+ * Log recovery assumes the first two entries are the type and size and they fit
+ * in 32 bits. Also in host order (ugh) so they have to be 32 bit aligned so
+ * decoding can be done correctly.
+ */
+struct xfs_icreate_log {
+       __uint16_t      icl_type;       /* type of log format structure */
+       __uint16_t      icl_size;       /* size of log format structure */
+       __be32          icl_ag;         /* ag being allocated in */
+       __be32          icl_agbno;      /* start block of inode range */
+       __be32          icl_count;      /* number of inodes to initialise */
+       __be32          icl_isize;      /* size of inodes */
+       __be32          icl_length;     /* length of extent to initialise */
+       __be32          icl_gen;        /* inode generation number to use */
+};
+
+/* in memory log item structure */
+struct xfs_icreate_item {
+       struct xfs_log_item     ic_item;
+       struct xfs_icreate_log  ic_format;
+};
+
+extern kmem_zone_t *xfs_icreate_zone;  /* inode create item zone */
+
+void xfs_icreate_log(struct xfs_trans *tp, xfs_agnumber_t agno,
+                       xfs_agblock_t agbno, unsigned int count,
+                       unsigned int inode_size, xfs_agblock_t length,
+                       unsigned int generation);
+
+#endif /* XFS_ICREATE_ITEM_H */
index 7f7be5f..9ecfe1e 100644 (file)
@@ -1028,6 +1028,11 @@ xfs_dinode_calc_crc(
 
 /*
  * Read the disk inode attributes into the in-core inode structure.
+ *
+ * If we are initialising a new inode and we are not utilising the
+ * XFS_MOUNT_IKEEP inode cluster mode, we can simple build the new inode core
+ * with a random generation number. If we are keeping inodes around, we need to
+ * read the inode cluster to get the existing generation number off disk.
  */
 int
 xfs_iread(
@@ -1047,6 +1052,22 @@ xfs_iread(
        if (error)
                return error;
 
+       /* shortcut IO on inode allocation if possible */
+       if ((iget_flags & XFS_IGET_CREATE) &&
+           !(mp->m_flags & XFS_MOUNT_IKEEP)) {
+               /* initialise the on-disk inode core */
+               memset(&ip->i_d, 0, sizeof(ip->i_d));
+               ip->i_d.di_magic = XFS_DINODE_MAGIC;
+               ip->i_d.di_gen = prandom_u32();
+               if (xfs_sb_version_hascrc(&mp->m_sb)) {
+                       ip->i_d.di_version = 3;
+                       ip->i_d.di_ino = ip->i_ino;
+                       uuid_copy(&ip->i_d.di_uuid, &mp->m_sb.sb_uuid);
+               } else
+                       ip->i_d.di_version = 2;
+               return 0;
+       }
+
        /*
         * Get pointers to the on-disk inode and the buffer containing it.
         */
@@ -1133,17 +1154,16 @@ xfs_iread(
        xfs_buf_set_ref(bp, XFS_INO_REF);
 
        /*
-        * Use xfs_trans_brelse() to release the buffer containing the
-        * on-disk inode, because it was acquired with xfs_trans_read_buf()
-        * in xfs_imap_to_bp() above.  If tp is NULL, this is just a normal
+        * Use xfs_trans_brelse() to release the buffer containing the on-disk
+        * inode, because it was acquired with xfs_trans_read_buf() in
+        * xfs_imap_to_bp() above.  If tp is NULL, this is just a normal
         * brelse().  If we're within a transaction, then xfs_trans_brelse()
         * will only release the buffer if it is not dirty within the
         * transaction.  It will be OK to release the buffer in this case,
-        * because inodes on disk are never destroyed and we will be
-        * locking the new in-core inode before putting it in the hash
-        * table where other processes can find it.  Thus we don't have
-        * to worry about the inode being changed just because we released
-        * the buffer.
+        * because inodes on disk are never destroyed and we will be locking the
+        * new in-core inode before putting it in the cache where other
+        * processes can find it.  Thus we don't have to worry about the inode
+        * being changed just because we released the buffer.
         */
  out_brelse:
        xfs_trans_brelse(tp, bp);
@@ -2028,8 +2048,6 @@ xfs_ifree(
        int                     error;
        int                     delete;
        xfs_ino_t               first_ino;
-       xfs_dinode_t            *dip;
-       xfs_buf_t               *ibp;
 
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
        ASSERT(ip->i_d.di_nlink == 0);
@@ -2042,14 +2060,13 @@ xfs_ifree(
         * Pull the on-disk inode from the AGI unlinked list.
         */
        error = xfs_iunlink_remove(tp, ip);
-       if (error != 0) {
+       if (error)
                return error;
-       }
 
        error = xfs_difree(tp, ip->i_ino, flist, &delete, &first_ino);
-       if (error != 0) {
+       if (error)
                return error;
-       }
+
        ip->i_d.di_mode = 0;            /* mark incore inode as free */
        ip->i_d.di_flags = 0;
        ip->i_d.di_dmevmask = 0;
@@ -2061,31 +2078,10 @@ xfs_ifree(
         * by reincarnations of this inode.
         */
        ip->i_d.di_gen++;
-
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
-       error = xfs_imap_to_bp(ip->i_mount, tp, &ip->i_imap, &dip, &ibp,
-                              0, 0);
-       if (error)
-               return error;
-
-        /*
-       * Clear the on-disk di_mode. This is to prevent xfs_bulkstat
-       * from picking up this inode when it is reclaimed (its incore state
-       * initialzed but not flushed to disk yet). The in-core di_mode is
-       * already cleared  and a corresponding transaction logged.
-       * The hack here just synchronizes the in-core to on-disk
-       * di_mode value in advance before the actual inode sync to disk.
-       * This is OK because the inode is already unlinked and would never
-       * change its di_mode again for this inode generation.
-       * This is a temporary hack that would require a proper fix
-       * in the future.
-       */
-       dip->di_mode = 0;
-
-       if (delete) {
+       if (delete)
                error = xfs_ifree_cluster(ip, tp, first_ino);
-       }
 
        return error;
 }
index 8f8aaee..6a70964 100644 (file)
@@ -283,6 +283,15 @@ xfs_iomap_eof_want_preallocate(
        if (offset + count <= XFS_ISIZE(ip))
                return 0;
 
+       /*
+        * If the file is smaller than the minimum prealloc and we are using
+        * dynamic preallocation, don't do any preallocation at all as it is
+        * likely this is the only write to the file that is going to be done.
+        */
+       if (!(mp->m_flags & XFS_MOUNT_DFLT_IOSIZE) &&
+           XFS_ISIZE(ip) < XFS_FSB_TO_B(mp, mp->m_writeio_blocks))
+               return 0;
+
        /*
         * If there are any real blocks past eof, then don't
         * do any speculative allocation.
@@ -345,6 +354,10 @@ xfs_iomap_eof_prealloc_initial_size(
        if (mp->m_flags & XFS_MOUNT_DFLT_IOSIZE)
                return 0;
 
+       /* If the file is small, then use the minimum prealloc */
+       if (XFS_ISIZE(ip) < XFS_FSB_TO_B(mp, mp->m_dalign))
+               return 0;
+
        /*
         * As we write multiple pages, the offset will always align to the
         * start of a page and hence point to a hole at EOF. i.e. if the size is
index ca9ecaa..c69bbc4 100644 (file)
@@ -987,7 +987,8 @@ xfs_fiemap_format(
        if (bmv->bmv_oflags & BMV_OF_PREALLOC)
                fiemap_flags |= FIEMAP_EXTENT_UNWRITTEN;
        else if (bmv->bmv_oflags & BMV_OF_DELALLOC) {
-               fiemap_flags |= FIEMAP_EXTENT_DELALLOC;
+               fiemap_flags |= (FIEMAP_EXTENT_DELALLOC |
+                                FIEMAP_EXTENT_UNKNOWN);
                physical = 0;   /* no block yet */
        }
        if (bmv->bmv_oflags & BMV_OF_LAST)
index 2ea7d40..bc92c53 100644 (file)
@@ -43,7 +43,7 @@ xfs_internal_inum(
 {
        return (ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino ||
                (xfs_sb_version_hasquota(&mp->m_sb) &&
-                (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino)));
+                xfs_is_quota_inode(&mp->m_sb, ino)));
 }
 
 /*
@@ -383,11 +383,13 @@ xfs_bulkstat(
                         * Also start read-ahead now for this chunk.
                         */
                        if (r.ir_freecount < XFS_INODES_PER_CHUNK) {
+                               struct blk_plug plug;
                                /*
                                 * Loop over all clusters in the next chunk.
                                 * Do a readahead if there are any allocated
                                 * inodes in that cluster.
                                 */
+                               blk_start_plug(&plug);
                                agbno = XFS_AGINO_TO_AGBNO(mp, r.ir_startino);
                                for (chunkidx = 0;
                                     chunkidx < XFS_INODES_PER_CHUNK;
@@ -399,6 +401,7 @@ xfs_bulkstat(
                                                        agbno, nbcluster,
                                                        &xfs_inode_buf_ops);
                                }
+                               blk_finish_plug(&plug);
                                irbp->ir_startino = r.ir_startino;
                                irbp->ir_freecount = r.ir_freecount;
                                irbp->ir_free = r.ir_free;
index b345a7c..d852a2b 100644 (file)
@@ -1963,6 +1963,10 @@ xlog_write_calc_vec_length(
                headers++;
 
        for (lv = log_vector; lv; lv = lv->lv_next) {
+               /* we don't write ordered log vectors */
+               if (lv->lv_buf_len == XFS_LOG_VEC_ORDERED)
+                       continue;
+
                headers += lv->lv_niovecs;
 
                for (i = 0; i < lv->lv_niovecs; i++) {
@@ -2216,7 +2220,7 @@ xlog_write(
        index = 0;
        lv = log_vector;
        vecp = lv->lv_iovecp;
-       while (lv && index < lv->lv_niovecs) {
+       while (lv && (!lv->lv_niovecs || index < lv->lv_niovecs)) {
                void            *ptr;
                int             log_offset;
 
@@ -2236,13 +2240,22 @@ xlog_write(
                 * This loop writes out as many regions as can fit in the amount
                 * of space which was allocated by xlog_state_get_iclog_space().
                 */
-               while (lv && index < lv->lv_niovecs) {
-                       struct xfs_log_iovec    *reg = &vecp[index];
+               while (lv && (!lv->lv_niovecs || index < lv->lv_niovecs)) {
+                       struct xfs_log_iovec    *reg;
                        struct xlog_op_header   *ophdr;
                        int                     start_rec_copy;
                        int                     copy_len;
                        int                     copy_off;
+                       bool                    ordered = false;
+
+                       /* ordered log vectors have no regions to write */
+                       if (lv->lv_buf_len == XFS_LOG_VEC_ORDERED) {
+                               ASSERT(lv->lv_niovecs == 0);
+                               ordered = true;
+                               goto next_lv;
+                       }
 
+                       reg = &vecp[index];
                        ASSERT(reg->i_len % sizeof(__int32_t) == 0);
                        ASSERT((unsigned long)ptr % sizeof(__int32_t) == 0);
 
@@ -2302,12 +2315,13 @@ xlog_write(
                                break;
 
                        if (++index == lv->lv_niovecs) {
+next_lv:
                                lv = lv->lv_next;
                                index = 0;
                                if (lv)
                                        vecp = lv->lv_iovecp;
                        }
-                       if (record_cnt == 0) {
+                       if (record_cnt == 0 && ordered == false) {
                                if (!lv)
                                        return 0;
                                break;
index 5caee96..fb630e4 100644 (file)
@@ -88,7 +88,8 @@ static inline xfs_lsn_t       _lsn_cmp(xfs_lsn_t lsn1, xfs_lsn_t lsn2)
 #define XLOG_REG_TYPE_UNMOUNT          17
 #define XLOG_REG_TYPE_COMMIT           18
 #define XLOG_REG_TYPE_TRANSHDR         19
-#define XLOG_REG_TYPE_MAX              19
+#define XLOG_REG_TYPE_ICREATE          20
+#define XLOG_REG_TYPE_MAX              20
 
 typedef struct xfs_log_iovec {
        void            *i_addr;        /* beginning address of region */
@@ -105,6 +106,8 @@ struct xfs_log_vec {
        int                     lv_buf_len;     /* size of formatted buffer */
 };
 
+#define XFS_LOG_VEC_ORDERED    (-1)
+
 /*
  * Structure used to pass callback function and the function's argument
  * to the log manager.
index d0833b5..02b9cf3 100644 (file)
@@ -127,6 +127,7 @@ xlog_cil_prepare_log_vecs(
                int     index;
                int     len = 0;
                uint    niovecs;
+               bool    ordered = false;
 
                /* Skip items which aren't dirty in this transaction. */
                if (!(lidp->lid_flags & XFS_LID_DIRTY))
@@ -137,14 +138,30 @@ xlog_cil_prepare_log_vecs(
                if (!niovecs)
                        continue;
 
+               /*
+                * Ordered items need to be tracked but we do not wish to write
+                * them. We need a logvec to track the object, but we do not
+                * need an iovec or buffer to be allocated for copying data.
+                */
+               if (niovecs == XFS_LOG_VEC_ORDERED) {
+                       ordered = true;
+                       niovecs = 0;
+               }
+
                new_lv = kmem_zalloc(sizeof(*new_lv) +
                                niovecs * sizeof(struct xfs_log_iovec),
                                KM_SLEEP|KM_NOFS);
 
+               new_lv->lv_item = lidp->lid_item;
+               new_lv->lv_niovecs = niovecs;
+               if (ordered) {
+                       /* track as an ordered logvec */
+                       new_lv->lv_buf_len = XFS_LOG_VEC_ORDERED;
+                       goto next;
+               }
+
                /* The allocated iovec region lies beyond the log vector. */
                new_lv->lv_iovecp = (struct xfs_log_iovec *)&new_lv[1];
-               new_lv->lv_niovecs = niovecs;
-               new_lv->lv_item = lidp->lid_item;
 
                /* build the vector array and calculate it's length */
                IOP_FORMAT(new_lv->lv_item, new_lv->lv_iovecp);
@@ -165,6 +182,7 @@ xlog_cil_prepare_log_vecs(
                }
                ASSERT(ptr == new_lv->lv_buf + new_lv->lv_buf_len);
 
+next:
                if (!ret_lv)
                        ret_lv = new_lv;
                else
@@ -191,8 +209,18 @@ xfs_cil_prepare_item(
 
        if (old) {
                /* existing lv on log item, space used is a delta */
-               ASSERT(!list_empty(&lv->lv_item->li_cil));
-               ASSERT(old->lv_buf && old->lv_buf_len && old->lv_niovecs);
+               ASSERT((old->lv_buf && old->lv_buf_len && old->lv_niovecs) ||
+                       old->lv_buf_len == XFS_LOG_VEC_ORDERED);
+
+               /*
+                * If the new item is ordered, keep the old one that is already
+                * tracking dirty or ordered regions
+                */
+               if (lv->lv_buf_len == XFS_LOG_VEC_ORDERED) {
+                       ASSERT(!lv->lv_buf);
+                       kmem_free(lv);
+                       return;
+               }
 
                *len += lv->lv_buf_len - old->lv_buf_len;
                *diff_iovecs += lv->lv_niovecs - old->lv_niovecs;
@@ -201,10 +229,11 @@ xfs_cil_prepare_item(
        } else {
                /* new lv, must pin the log item */
                ASSERT(!lv->lv_item->li_lv);
-               ASSERT(list_empty(&lv->lv_item->li_cil));
 
-               *len += lv->lv_buf_len;
-               *diff_iovecs += lv->lv_niovecs;
+               if (lv->lv_buf_len != XFS_LOG_VEC_ORDERED) {
+                       *len += lv->lv_buf_len;
+                       *diff_iovecs += lv->lv_niovecs;
+               }
                IOP_PIN(lv->lv_item);
 
        }
@@ -259,18 +288,24 @@ xlog_cil_insert_items(
         * We can do this safely because the context can't checkpoint until we
         * are done so it doesn't matter exactly how we update the CIL.
         */
-       for (lv = log_vector; lv; lv = lv->lv_next)
-               xfs_cil_prepare_item(log, lv, &len, &diff_iovecs);
-
-       /* account for space used by new iovec headers  */
-       len += diff_iovecs * sizeof(xlog_op_header_t);
-
        spin_lock(&cil->xc_cil_lock);
+       for (lv = log_vector; lv; ) {
+               struct xfs_log_vec *next = lv->lv_next;
 
-       /* move the items to the tail of the CIL */
-       for (lv = log_vector; lv; lv = lv->lv_next)
+               ASSERT(lv->lv_item->li_lv || list_empty(&lv->lv_item->li_cil));
+               lv->lv_next = NULL;
+
+               /*
+                * xfs_cil_prepare_item() may free the lv, so move the item on
+                * the CIL first.
+                */
                list_move_tail(&lv->lv_item->li_cil, &cil->xc_cil);
+               xfs_cil_prepare_item(log, lv, &len, &diff_iovecs);
+               lv = next;
+       }
 
+       /* account for space used by new iovec headers  */
+       len += diff_iovecs * sizeof(xlog_op_header_t);
        ctx->nvecs += diff_iovecs;
 
        /*
@@ -381,9 +416,7 @@ xlog_cil_push(
        struct xfs_cil_ctx      *new_ctx;
        struct xlog_in_core     *commit_iclog;
        struct xlog_ticket      *tic;
-       int                     num_lv;
        int                     num_iovecs;
-       int                     len;
        int                     error = 0;
        struct xfs_trans_header thdr;
        struct xfs_log_iovec    lhdr;
@@ -428,12 +461,9 @@ xlog_cil_push(
         * side which is currently locked out by the flush lock.
         */
        lv = NULL;
-       num_lv = 0;
        num_iovecs = 0;
-       len = 0;
        while (!list_empty(&cil->xc_cil)) {
                struct xfs_log_item     *item;
-               int                     i;
 
                item = list_first_entry(&cil->xc_cil,
                                        struct xfs_log_item, li_cil);
@@ -444,11 +474,7 @@ xlog_cil_push(
                        lv->lv_next = item->li_lv;
                lv = item->li_lv;
                item->li_lv = NULL;
-
-               num_lv++;
                num_iovecs += lv->lv_niovecs;
-               for (i = 0; i < lv->lv_niovecs; i++)
-                       len += lv->lv_iovecp[i].i_len;
        }
 
        /*
@@ -701,6 +727,7 @@ xfs_log_commit_cil(
        if (commit_lsn)
                *commit_lsn = log->l_cilp->xc_ctx->sequence;
 
+       /* xlog_cil_insert_items() destroys log_vector list */
        xlog_cil_insert_items(log, log_vector, tp->t_ticket);
 
        /* check we didn't blow the reservation */
index 7cf5e4e..6fcc910 100644 (file)
@@ -45,6 +45,7 @@
 #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"
@@ -1617,7 +1618,10 @@ xlog_recover_add_to_trans(
  *        form the cancelled buffer table. Hence they have tobe done last.
  *
  *     3. Inode allocation buffers must be replayed before inode items that
- *        read the buffer and replay changes into it.
+ *        read the buffer and replay changes into it. For filesystems using the
+ *        ICREATE transactions, this means XFS_LI_ICREATE objects need to get
+ *        treated the same as inode allocation buffers as they create and
+ *        initialise the buffers directly.
  *
  *     4. Inode unlink buffers must be replayed after inode items are replayed.
  *        This ensures that inodes are completely flushed to the inode buffer
@@ -1632,10 +1636,17 @@ xlog_recover_add_to_trans(
  * from all the other buffers and move them to last.
  *
  * Hence, 4 lists, in order from head to tail:
- *     - buffer_list for all buffers except cancelled/inode unlink buffers
- *     - item_list for all non-buffer items
- *     - inode_buffer_list for inode unlink buffers
- *     - cancel_list for the cancelled buffers
+ *     - buffer_list for all buffers except cancelled/inode unlink buffers
+ *     - item_list for all non-buffer items
+ *     - inode_buffer_list for inode unlink buffers
+ *     - cancel_list for the cancelled buffers
+ *
+ * Note that we add objects to the tail of the lists so that first-to-last
+ * ordering is preserved within the lists. Adding objects to the head of the
+ * list means when we traverse from the head we walk them in last-to-first
+ * order. For cancelled buffers and inode unlink buffers this doesn't matter,
+ * but for all other items there may be specific ordering that we need to
+ * preserve.
  */
 STATIC int
 xlog_recover_reorder_trans(
@@ -1655,6 +1666,9 @@ xlog_recover_reorder_trans(
                xfs_buf_log_format_t    *buf_f = item->ri_buf[0].i_addr;
 
                switch (ITEM_TYPE(item)) {
+               case XFS_LI_ICREATE:
+                       list_move_tail(&item->ri_list, &buffer_list);
+                       break;
                case XFS_LI_BUF:
                        if (buf_f->blf_flags & XFS_BLF_CANCEL) {
                                trace_xfs_log_recover_item_reorder_head(log,
@@ -2981,6 +2995,93 @@ xlog_recover_efd_pass2(
        return 0;
 }
 
+/*
+ * This routine is called when an inode create format structure is found in a
+ * committed transaction in the log.  It's purpose is to initialise the inodes
+ * being allocated on disk. This requires us to get inode cluster buffers that
+ * match the range to be intialised, stamped with inode templates and written
+ * by delayed write so that subsequent modifications will hit the cached buffer
+ * and only need writing out at the end of recovery.
+ */
+STATIC int
+xlog_recover_do_icreate_pass2(
+       struct xlog             *log,
+       struct list_head        *buffer_list,
+       xlog_recover_item_t     *item)
+{
+       struct xfs_mount        *mp = log->l_mp;
+       struct xfs_icreate_log  *icl;
+       xfs_agnumber_t          agno;
+       xfs_agblock_t           agbno;
+       unsigned int            count;
+       unsigned int            isize;
+       xfs_agblock_t           length;
+
+       icl = (struct xfs_icreate_log *)item->ri_buf[0].i_addr;
+       if (icl->icl_type != XFS_LI_ICREATE) {
+               xfs_warn(log->l_mp, "xlog_recover_do_icreate_trans: bad type");
+               return EINVAL;
+       }
+
+       if (icl->icl_size != 1) {
+               xfs_warn(log->l_mp, "xlog_recover_do_icreate_trans: bad icl size");
+               return EINVAL;
+       }
+
+       agno = be32_to_cpu(icl->icl_ag);
+       if (agno >= mp->m_sb.sb_agcount) {
+               xfs_warn(log->l_mp, "xlog_recover_do_icreate_trans: bad agno");
+               return EINVAL;
+       }
+       agbno = be32_to_cpu(icl->icl_agbno);
+       if (!agbno || agbno == NULLAGBLOCK || agbno >= mp->m_sb.sb_agblocks) {
+               xfs_warn(log->l_mp, "xlog_recover_do_icreate_trans: bad agbno");
+               return EINVAL;
+       }
+       isize = be32_to_cpu(icl->icl_isize);
+       if (isize != mp->m_sb.sb_inodesize) {
+               xfs_warn(log->l_mp, "xlog_recover_do_icreate_trans: bad isize");
+               return EINVAL;
+       }
+       count = be32_to_cpu(icl->icl_count);
+       if (!count) {
+               xfs_warn(log->l_mp, "xlog_recover_do_icreate_trans: bad count");
+               return EINVAL;
+       }
+       length = be32_to_cpu(icl->icl_length);
+       if (!length || length >= mp->m_sb.sb_agblocks) {
+               xfs_warn(log->l_mp, "xlog_recover_do_icreate_trans: bad length");
+               return EINVAL;
+       }
+
+       /* existing allocation is fixed value */
+       ASSERT(count == XFS_IALLOC_INODES(mp));
+       ASSERT(length == XFS_IALLOC_BLOCKS(mp));
+       if (count != XFS_IALLOC_INODES(mp) ||
+            length != XFS_IALLOC_BLOCKS(mp)) {
+               xfs_warn(log->l_mp, "xlog_recover_do_icreate_trans: bad count 2");
+               return EINVAL;
+       }
+
+       /*
+        * Inode buffers can be freed. Do not replay the inode initialisation as
+        * we could be overwriting something written after this inode buffer was
+        * cancelled.
+        *
+        * XXX: we need to iterate all buffers and only init those that are not
+        * cancelled. I think that a more fine grained factoring of
+        * xfs_ialloc_inode_init may be appropriate here to enable this to be
+        * done easily.
+        */
+       if (xlog_check_buffer_cancelled(log,
+                       XFS_AGB_TO_DADDR(mp, agno, agbno), length, 0))
+               return 0;
+
+       xfs_ialloc_inode_init(mp, NULL, buffer_list, agno, agbno, length,
+                                       be32_to_cpu(icl->icl_gen));
+       return 0;
+}
+
 /*
  * Free up any resources allocated by the transaction
  *
@@ -3023,6 +3124,7 @@ xlog_recover_commit_pass1(
        case XFS_LI_EFI:
        case XFS_LI_EFD:
        case XFS_LI_DQUOT:
+       case XFS_LI_ICREATE:
                /* nothing to do in pass 1 */
                return 0;
        default:
@@ -3053,6 +3155,8 @@ xlog_recover_commit_pass2(
                return xlog_recover_efd_pass2(log, item);
        case XFS_LI_DQUOT:
                return xlog_recover_dquot_pass2(log, buffer_list, item);
+       case XFS_LI_ICREATE:
+               return xlog_recover_do_icreate_pass2(log, buffer_list, item);
        case XFS_LI_QUOTAOFF:
                /* nothing to do in pass2 */
                return 0;
index e8e310c..2b0ba35 100644 (file)
@@ -336,6 +336,14 @@ xfs_mount_validate_sb(
                return XFS_ERROR(EWRONGFS);
        }
 
+       if ((sbp->sb_qflags & (XFS_OQUOTA_ENFD | XFS_OQUOTA_CHKD)) &&
+                       (sbp->sb_qflags & (XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD |
+                               XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD))) {
+               xfs_notice(mp,
+"Super block has XFS_OQUOTA bits along with XFS_PQUOTA and/or XFS_GQUOTA bits.\n");
+               return XFS_ERROR(EFSCORRUPTED);
+       }
+
        /*
         * Version 5 superblock feature mask validation. Reject combinations the
         * kernel cannot support up front before checking anything else. For
@@ -561,6 +569,18 @@ out_unwind:
        return error;
 }
 
+static void
+xfs_sb_quota_from_disk(struct xfs_sb *sbp)
+{
+       if (sbp->sb_qflags & XFS_OQUOTA_ENFD)
+               sbp->sb_qflags |= (sbp->sb_qflags & XFS_PQUOTA_ACCT) ?
+                                       XFS_PQUOTA_ENFD : XFS_GQUOTA_ENFD;
+       if (sbp->sb_qflags & XFS_OQUOTA_CHKD)
+               sbp->sb_qflags |= (sbp->sb_qflags & XFS_PQUOTA_ACCT) ?
+                                       XFS_PQUOTA_CHKD : XFS_GQUOTA_CHKD;
+       sbp->sb_qflags &= ~(XFS_OQUOTA_ENFD | XFS_OQUOTA_CHKD);
+}
+
 void
 xfs_sb_from_disk(
        struct xfs_sb   *to,
@@ -622,6 +642,35 @@ xfs_sb_from_disk(
        to->sb_lsn = be64_to_cpu(from->sb_lsn);
 }
 
+static inline void
+xfs_sb_quota_to_disk(
+       xfs_dsb_t       *to,
+       xfs_sb_t        *from,
+       __int64_t       *fields)
+{
+       __uint16_t      qflags = from->sb_qflags;
+
+       if (*fields & XFS_SB_QFLAGS) {
+               /*
+                * The in-core version of sb_qflags do not have
+                * XFS_OQUOTA_* flags, whereas the on-disk version
+                * does.  So, convert incore XFS_{PG}QUOTA_* flags
+                * to on-disk XFS_OQUOTA_* flags.
+                */
+               qflags &= ~(XFS_PQUOTA_ENFD | XFS_PQUOTA_CHKD |
+                               XFS_GQUOTA_ENFD | XFS_GQUOTA_CHKD);
+
+               if (from->sb_qflags &
+                               (XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD))
+                       qflags |= XFS_OQUOTA_ENFD;
+               if (from->sb_qflags &
+                               (XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD))
+                       qflags |= XFS_OQUOTA_CHKD;
+               to->sb_qflags = cpu_to_be16(qflags);
+               *fields &= ~XFS_SB_QFLAGS;
+       }
+}
+
 /*
  * Copy in core superblock to ondisk one.
  *
@@ -643,6 +692,7 @@ xfs_sb_to_disk(
        if (!fields)
                return;
 
+       xfs_sb_quota_to_disk(to, from, &fields);
        while (fields) {
                f = (xfs_sb_field_t)xfs_lowbit64((__uint64_t)fields);
                first = xfs_sb_info[f].offset;
@@ -835,6 +885,7 @@ reread:
         */
        xfs_sb_from_disk(&mp->m_sb, XFS_BUF_TO_SBP(bp));
 
+       xfs_sb_quota_from_disk(&mp->m_sb);
        /*
         * We must be able to do sector-sized and sector-aligned IO.
         */
@@ -987,42 +1038,27 @@ xfs_update_alignment(xfs_mount_t *mp)
                 */
                if ((BBTOB(mp->m_dalign) & mp->m_blockmask) ||
                    (BBTOB(mp->m_swidth) & mp->m_blockmask)) {
-                       if (mp->m_flags & XFS_MOUNT_RETERR) {
-                               xfs_warn(mp, "alignment check failed: "
-                                        "(sunit/swidth vs. blocksize)");
-                               return XFS_ERROR(EINVAL);
-                       }
-                       mp->m_dalign = mp->m_swidth = 0;
+                       xfs_warn(mp,
+               "alignment check failed: sunit/swidth vs. blocksize(%d)",
+                               sbp->sb_blocksize);
+                       return XFS_ERROR(EINVAL);
                } else {
                        /*
                         * Convert the stripe unit and width to FSBs.
                         */
                        mp->m_dalign = XFS_BB_TO_FSBT(mp, mp->m_dalign);
                        if (mp->m_dalign && (sbp->sb_agblocks % mp->m_dalign)) {
-                               if (mp->m_flags & XFS_MOUNT_RETERR) {
-                                       xfs_warn(mp, "alignment check failed: "
-                                                "(sunit/swidth vs. ag size)");
-                                       return XFS_ERROR(EINVAL);
-                               }
                                xfs_warn(mp,
-               "stripe alignment turned off: sunit(%d)/swidth(%d) "
-               "incompatible with agsize(%d)",
-                                       mp->m_dalign, mp->m_swidth,
-                                       sbp->sb_agblocks);
-
-                               mp->m_dalign = 0;
-                               mp->m_swidth = 0;
+                       "alignment check failed: sunit/swidth vs. agsize(%d)",
+                                        sbp->sb_agblocks);
+                               return XFS_ERROR(EINVAL);
                        } else if (mp->m_dalign) {
                                mp->m_swidth = XFS_BB_TO_FSBT(mp, mp->m_swidth);
                        } else {
-                               if (mp->m_flags & XFS_MOUNT_RETERR) {
-                                       xfs_warn(mp, "alignment check failed: "
-                                               "sunit(%d) less than bsize(%d)",
-                                               mp->m_dalign,
-                                               mp->m_blockmask +1);
-                                       return XFS_ERROR(EINVAL);
-                               }
-                               mp->m_swidth = 0;
+                               xfs_warn(mp,
+                       "alignment check failed: sunit(%d) less than bsize(%d)",
+                                        mp->m_dalign, sbp->sb_blocksize);
+                               return XFS_ERROR(EINVAL);
                        }
                }
 
@@ -1039,6 +1075,10 @@ xfs_update_alignment(xfs_mount_t *mp)
                                sbp->sb_width = mp->m_swidth;
                                mp->m_update_flags |= XFS_SB_WIDTH;
                        }
+               } else {
+                       xfs_warn(mp,
+       "cannot change alignment: superblock does not support data alignment");
+                       return XFS_ERROR(EINVAL);
                }
        } else if ((mp->m_flags & XFS_MOUNT_NOALIGN) != XFS_MOUNT_NOALIGN &&
                    xfs_sb_version_hasdalign(&mp->m_sb)) {
index b004cec..4e374d4 100644 (file)
@@ -192,8 +192,6 @@ typedef struct xfs_mount {
        xfs_dablk_t             m_dirleafblk;   /* blockno of dir non-data v2 */
        xfs_dablk_t             m_dirfreeblk;   /* blockno of dirfreeindex v2 */
        uint                    m_chsize;       /* size of next field */
-       struct xfs_chash        *m_chash;       /* fs private inode per-cluster
-                                                * hash table */
        atomic_t                m_active_trans; /* number trans frozen */
 #ifdef HAVE_PERCPU_SB
        xfs_icsb_cnts_t __percpu *m_sb_cnts;    /* per-cpu superblock counters */
@@ -229,8 +227,6 @@ typedef struct xfs_mount {
                                                   operations, typically for
                                                   disk errors in metadata */
 #define XFS_MOUNT_DISCARD      (1ULL << 5)     /* discard unused blocks */
-#define XFS_MOUNT_RETERR       (1ULL << 6)     /* return alignment errors to
-                                                  user */
 #define XFS_MOUNT_NOALIGN      (1ULL << 7)     /* turn off stripe alignment
                                                   allocations */
 #define XFS_MOUNT_ATTR2                (1ULL << 8)     /* allow use of attr2 format */
index b75c9bb..7a3e007 100644 (file)
@@ -70,7 +70,7 @@ xfs_qm_dquot_walk(
        void                    *data)
 {
        struct xfs_quotainfo    *qi = mp->m_quotainfo;
-       struct radix_tree_root  *tree = XFS_DQUOT_TREE(qi, type);
+       struct radix_tree_root  *tree = xfs_dquot_tree(qi, type);
        uint32_t                next_index;
        int                     last_error = 0;
        int                     skipped;
@@ -189,7 +189,7 @@ xfs_qm_dqpurge(
        xfs_dqfunlock(dqp);
        xfs_dqunlock(dqp);
 
-       radix_tree_delete(XFS_DQUOT_TREE(qi, dqp->q_core.d_flags),
+       radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags),
                          be32_to_cpu(dqp->q_core.d_id));
        qi->qi_dquots--;
 
@@ -299,8 +299,10 @@ xfs_qm_mount_quotas(
         */
        if (!XFS_IS_UQUOTA_ON(mp))
                mp->m_qflags &= ~XFS_UQUOTA_CHKD;
-       if (!(XFS_IS_GQUOTA_ON(mp) || XFS_IS_PQUOTA_ON(mp)))
-               mp->m_qflags &= ~XFS_OQUOTA_CHKD;
+       if (!XFS_IS_GQUOTA_ON(mp))
+               mp->m_qflags &= ~XFS_GQUOTA_CHKD;
+       if (!XFS_IS_PQUOTA_ON(mp))
+               mp->m_qflags &= ~XFS_PQUOTA_CHKD;
 
  write_changes:
        /*
@@ -489,8 +491,7 @@ xfs_qm_need_dqattach(
                return false;
        if (!XFS_NOT_DQATTACHED(mp, ip))
                return false;
-       if (ip->i_ino == mp->m_sb.sb_uquotino ||
-           ip->i_ino == mp->m_sb.sb_gquotino)
+       if (xfs_is_quota_inode(&mp->m_sb, ip->i_ino))
                return false;
        return true;
 }
@@ -606,8 +607,7 @@ xfs_qm_dqdetach(
 
        trace_xfs_dquot_dqdetach(ip);
 
-       ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_uquotino);
-       ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_gquotino);
+       ASSERT(!xfs_is_quota_inode(&ip->i_mount->m_sb, ip->i_ino));
        if (ip->i_udquot) {
                xfs_qm_dqrele(ip->i_udquot);
                ip->i_udquot = NULL;
@@ -1152,7 +1152,7 @@ xfs_qm_dqusage_adjust(
         * rootino must have its resources accounted for, not so with the quota
         * inodes.
         */
-       if (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino) {
+       if (xfs_is_quota_inode(&mp->m_sb, ino)) {
                *res = BULKSTAT_RV_NOTHING;
                return XFS_ERROR(EINVAL);
        }
@@ -1262,19 +1262,20 @@ int
 xfs_qm_quotacheck(
        xfs_mount_t     *mp)
 {
-       int             done, count, error, error2;
-       xfs_ino_t       lastino;
-       size_t          structsz;
-       xfs_inode_t     *uip, *gip;
-       uint            flags;
-       LIST_HEAD       (buffer_list);
+       int                     done, count, error, error2;
+       xfs_ino_t               lastino;
+       size_t                  structsz;
+       uint                    flags;
+       LIST_HEAD               (buffer_list);
+       struct xfs_inode        *uip = mp->m_quotainfo->qi_uquotaip;
+       struct xfs_inode        *gip = mp->m_quotainfo->qi_gquotaip;
 
        count = INT_MAX;
        structsz = 1;
        lastino = 0;
        flags = 0;
 
-       ASSERT(mp->m_quotainfo->qi_uquotaip || mp->m_quotainfo->qi_gquotaip);
+       ASSERT(uip || gip);
        ASSERT(XFS_IS_QUOTA_RUNNING(mp));
 
        xfs_notice(mp, "Quotacheck needed: Please wait.");
@@ -1284,7 +1285,6 @@ xfs_qm_quotacheck(
         * their counters to zero. We need a clean slate.
         * We don't log our changes till later.
         */
-       uip = mp->m_quotainfo->qi_uquotaip;
        if (uip) {
                error = xfs_qm_dqiterate(mp, uip, XFS_QMOPT_UQUOTA,
                                         &buffer_list);
@@ -1293,14 +1293,14 @@ xfs_qm_quotacheck(
                flags |= XFS_UQUOTA_CHKD;
        }
 
-       gip = mp->m_quotainfo->qi_gquotaip;
        if (gip) {
                error = xfs_qm_dqiterate(mp, gip, XFS_IS_GQUOTA_ON(mp) ?
                                         XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA,
                                         &buffer_list);
                if (error)
                        goto error_return;
-               flags |= XFS_OQUOTA_CHKD;
+               flags |= XFS_IS_GQUOTA_ON(mp) ?
+                                       XFS_GQUOTA_CHKD : XFS_PQUOTA_CHKD;
        }
 
        do {
@@ -1395,15 +1395,13 @@ STATIC int
 xfs_qm_init_quotainos(
        xfs_mount_t     *mp)
 {
-       xfs_inode_t     *uip, *gip;
-       int             error;
-       __int64_t       sbflags;
-       uint            flags;
+       struct xfs_inode        *uip = NULL;
+       struct xfs_inode        *gip = NULL;
+       int                     error;
+       __int64_t               sbflags = 0;
+       uint                    flags = 0;
 
        ASSERT(mp->m_quotainfo);
-       uip = gip = NULL;
-       sbflags = 0;
-       flags = 0;
 
        /*
         * Get the uquota and gquota inodes
@@ -1412,19 +1410,18 @@ xfs_qm_init_quotainos(
                if (XFS_IS_UQUOTA_ON(mp) &&
                    mp->m_sb.sb_uquotino != NULLFSINO) {
                        ASSERT(mp->m_sb.sb_uquotino > 0);
-                       if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino,
-                                            0, 0, &uip)))
+                       error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino,
+                                            0, 0, &uip);
+                       if (error)
                                return XFS_ERROR(error);
                }
                if (XFS_IS_OQUOTA_ON(mp) &&
                    mp->m_sb.sb_gquotino != NULLFSINO) {
                        ASSERT(mp->m_sb.sb_gquotino > 0);
-                       if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
-                                            0, 0, &gip))) {
-                               if (uip)
-                                       IRELE(uip);
-                               return XFS_ERROR(error);
-                       }
+                       error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
+                                            0, 0, &gip);
+                       if (error)
+                               goto error_rele;
                }
        } else {
                flags |= XFS_QMOPT_SBVERSION;
@@ -1439,10 +1436,11 @@ xfs_qm_init_quotainos(
         * temporarily switch to read-write to do this.
         */
        if (XFS_IS_UQUOTA_ON(mp) && uip == NULL) {
-               if ((error = xfs_qm_qino_alloc(mp, &uip,
+               error = xfs_qm_qino_alloc(mp, &uip,
                                              sbflags | XFS_SB_UQUOTINO,
-                                             flags | XFS_QMOPT_UQUOTA)))
-                       return XFS_ERROR(error);
+                                             flags | XFS_QMOPT_UQUOTA);
+               if (error)
+                       goto error_rele;
 
                flags &= ~XFS_QMOPT_SBVERSION;
        }
@@ -1451,18 +1449,21 @@ xfs_qm_init_quotainos(
                                XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA);
                error = xfs_qm_qino_alloc(mp, &gip,
                                          sbflags | XFS_SB_GQUOTINO, flags);
-               if (error) {
-                       if (uip)
-                               IRELE(uip);
-
-                       return XFS_ERROR(error);
-               }
+               if (error)
+                       goto error_rele;
        }
 
        mp->m_quotainfo->qi_uquotaip = uip;
        mp->m_quotainfo->qi_gquotaip = gip;
 
        return 0;
+
+error_rele:
+       if (uip)
+               IRELE(uip);
+       if (gip)
+               IRELE(gip);
+       return XFS_ERROR(error);
 }
 
 STATIC void
@@ -1473,7 +1474,7 @@ xfs_qm_dqfree_one(
        struct xfs_quotainfo    *qi = mp->m_quotainfo;
 
        mutex_lock(&qi->qi_tree_lock);
-       radix_tree_delete(XFS_DQUOT_TREE(qi, dqp->q_core.d_flags),
+       radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags),
                          be32_to_cpu(dqp->q_core.d_id));
 
        qi->qi_dquots--;
@@ -1659,7 +1660,8 @@ xfs_qm_vop_dqalloc(
        struct xfs_dquot        **O_gdqpp)
 {
        struct xfs_mount        *mp = ip->i_mount;
-       struct xfs_dquot        *uq, *gq;
+       struct xfs_dquot        *uq = NULL;
+       struct xfs_dquot        *gq = NULL;
        int                     error;
        uint                    lockflags;
 
@@ -1684,7 +1686,6 @@ xfs_qm_vop_dqalloc(
                }
        }
 
-       uq = gq = NULL;
        if ((flags & XFS_QMOPT_UQUOTA) && XFS_IS_UQUOTA_ON(mp)) {
                if (ip->i_d.di_uid != uid) {
                        /*
@@ -1697,11 +1698,12 @@ xfs_qm_vop_dqalloc(
                         * holding ilock.
                         */
                        xfs_iunlock(ip, lockflags);
-                       if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t) uid,
+                       error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t) uid,
                                                 XFS_DQ_USER,
                                                 XFS_QMOPT_DQALLOC |
                                                 XFS_QMOPT_DOWARN,
-                                                &uq))) {
+                                                &uq);
+                       if (error) {
                                ASSERT(error != ENOENT);
                                return error;
                        }
@@ -1723,15 +1725,14 @@ xfs_qm_vop_dqalloc(
        if ((flags & XFS_QMOPT_GQUOTA) && XFS_IS_GQUOTA_ON(mp)) {
                if (ip->i_d.di_gid != gid) {
                        xfs_iunlock(ip, lockflags);
-                       if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)gid,
+                       error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)gid,
                                                 XFS_DQ_GROUP,
                                                 XFS_QMOPT_DQALLOC |
                                                 XFS_QMOPT_DOWARN,
-                                                &gq))) {
-                               if (uq)
-                                       xfs_qm_dqrele(uq);
+                                                &gq);
+                       if (error) {
                                ASSERT(error != ENOENT);
-                               return error;
+                               goto error_rele;
                        }
                        xfs_dqunlock(gq);
                        lockflags = XFS_ILOCK_SHARED;
@@ -1743,15 +1744,14 @@ xfs_qm_vop_dqalloc(
        } else if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) {
                if (xfs_get_projid(ip) != prid) {
                        xfs_iunlock(ip, lockflags);
-                       if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)prid,
+                       error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)prid,
                                                 XFS_DQ_PROJ,
                                                 XFS_QMOPT_DQALLOC |
                                                 XFS_QMOPT_DOWARN,
-                                                &gq))) {
-                               if (uq)
-                                       xfs_qm_dqrele(uq);
+                                                &gq);
+                       if (error) {
                                ASSERT(error != ENOENT);
-                               return (error);
+                               goto error_rele;
                        }
                        xfs_dqunlock(gq);
                        lockflags = XFS_ILOCK_SHARED;
@@ -1774,6 +1774,11 @@ xfs_qm_vop_dqalloc(
        else if (gq)
                xfs_qm_dqrele(gq);
        return 0;
+
+error_rele:
+       if (uq)
+               xfs_qm_dqrele(uq);
+       return error;
 }
 
 /*
@@ -1821,29 +1826,31 @@ xfs_qm_vop_chown(
  */
 int
 xfs_qm_vop_chown_reserve(
-       xfs_trans_t     *tp,
-       xfs_inode_t     *ip,
-       xfs_dquot_t     *udqp,
-       xfs_dquot_t     *gdqp,
-       uint            flags)
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip,
+       struct xfs_dquot        *udqp,
+       struct xfs_dquot        *gdqp,
+       uint                    flags)
 {
-       xfs_mount_t     *mp = ip->i_mount;
-       uint            delblks, blkflags, prjflags = 0;
-       xfs_dquot_t     *unresudq, *unresgdq, *delblksudq, *delblksgdq;
-       int             error;
+       struct xfs_mount        *mp = ip->i_mount;
+       uint                    delblks, blkflags, prjflags = 0;
+       struct xfs_dquot        *udq_unres = NULL;
+       struct xfs_dquot        *gdq_unres = NULL;
+       struct xfs_dquot        *udq_delblks = NULL;
+       struct xfs_dquot        *gdq_delblks = NULL;
+       int                     error;
 
 
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
        ASSERT(XFS_IS_QUOTA_RUNNING(mp));
 
        delblks = ip->i_delayed_blks;
-       delblksudq = delblksgdq = unresudq = unresgdq = NULL;
        blkflags = XFS_IS_REALTIME_INODE(ip) ?
                        XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS;
 
        if (XFS_IS_UQUOTA_ON(mp) && udqp &&
            ip->i_d.di_uid != (uid_t)be32_to_cpu(udqp->q_core.d_id)) {
-               delblksudq = udqp;
+               udq_delblks = udqp;
                /*
                 * If there are delayed allocation blocks, then we have to
                 * unreserve those from the old dquot, and add them to the
@@ -1851,7 +1858,7 @@ xfs_qm_vop_chown_reserve(
                 */
                if (delblks) {
                        ASSERT(ip->i_udquot);
-                       unresudq = ip->i_udquot;
+                       udq_unres = ip->i_udquot;
                }
        }
        if (XFS_IS_OQUOTA_ON(ip->i_mount) && gdqp) {
@@ -1862,18 +1869,19 @@ xfs_qm_vop_chown_reserve(
                if (prjflags ||
                    (XFS_IS_GQUOTA_ON(ip->i_mount) &&
                     ip->i_d.di_gid != be32_to_cpu(gdqp->q_core.d_id))) {
-                       delblksgdq = gdqp;
+                       gdq_delblks = gdqp;
                        if (delblks) {
                                ASSERT(ip->i_gdquot);
-                               unresgdq = ip->i_gdquot;
+                               gdq_unres = ip->i_gdquot;
                        }
                }
        }
 
-       if ((error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
-                               delblksudq, delblksgdq, ip->i_d.di_nblocks, 1,
-                               flags | blkflags | prjflags)))
-               return (error);
+       error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
+                               udq_delblks, gdq_delblks, ip->i_d.di_nblocks, 1,
+                               flags | blkflags | prjflags);
+       if (error)
+               return error;
 
        /*
         * Do the delayed blks reservations/unreservations now. Since, these
@@ -1885,14 +1893,15 @@ xfs_qm_vop_chown_reserve(
                /*
                 * Do the reservations first. Unreservation can't fail.
                 */
-               ASSERT(delblksudq || delblksgdq);
-               ASSERT(unresudq || unresgdq);
-               if ((error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
-                               delblksudq, delblksgdq, (xfs_qcnt_t)delblks, 0,
-                               flags | blkflags | prjflags)))
-                       return (error);
+               ASSERT(udq_delblks || gdq_delblks);
+               ASSERT(udq_unres || gdq_unres);
+               error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
+                           udq_delblks, gdq_delblks, (xfs_qcnt_t)delblks, 0,
+                           flags | blkflags | prjflags);
+               if (error)
+                       return error;
                xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
-                               unresudq, unresgdq, -((xfs_qcnt_t)delblks), 0,
+                               udq_unres, gdq_unres, -((xfs_qcnt_t)delblks), 0,
                                blkflags);
        }
 
index 5d16a6e..bdb4f8b 100644 (file)
@@ -69,30 +69,62 @@ typedef struct xfs_quotainfo {
        struct shrinker  qi_shrinker;
 } xfs_quotainfo_t;
 
-#define XFS_DQUOT_TREE(qi, type) \
-       ((type & XFS_DQ_USER) ? \
-        &((qi)->qi_uquota_tree) : \
-        &((qi)->qi_gquota_tree))
+static inline struct radix_tree_root *
+xfs_dquot_tree(
+       struct xfs_quotainfo    *qi,
+       int                     type)
+{
+       switch (type) {
+       case XFS_DQ_USER:
+               return &qi->qi_uquota_tree;
+       case XFS_DQ_GROUP:
+       case XFS_DQ_PROJ:
+               return &qi->qi_gquota_tree;
+       default:
+               ASSERT(0);
+       }
+       return NULL;
+}
 
+static inline struct xfs_inode *
+xfs_dq_to_quota_inode(struct xfs_dquot *dqp)
+{
+       switch (dqp->dq_flags & XFS_DQ_ALLTYPES) {
+       case XFS_DQ_USER:
+               return dqp->q_mount->m_quotainfo->qi_uquotaip;
+       case XFS_DQ_GROUP:
+       case XFS_DQ_PROJ:
+               return dqp->q_mount->m_quotainfo->qi_gquotaip;
+       default:
+               ASSERT(0);
+       }
+       return NULL;
+}
 
 extern int     xfs_qm_calc_dquots_per_chunk(struct xfs_mount *mp,
                                             unsigned int nbblks);
-extern void    xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long);
-extern int     xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *,
-                       xfs_dquot_t *, xfs_dquot_t *, long, long, uint);
-extern void    xfs_trans_dqjoin(xfs_trans_t *, xfs_dquot_t *);
-extern void    xfs_trans_log_dquot(xfs_trans_t *, xfs_dquot_t *);
+extern void    xfs_trans_mod_dquot(struct xfs_trans *,
+                                       struct xfs_dquot *, uint, long);
+extern int     xfs_trans_reserve_quota_bydquots(struct xfs_trans *,
+                       struct xfs_mount *, struct xfs_dquot *,
+                       struct xfs_dquot *, long, long, uint);
+extern void    xfs_trans_dqjoin(struct xfs_trans *, struct xfs_dquot *);
+extern void    xfs_trans_log_dquot(struct xfs_trans *, struct xfs_dquot *);
 
 /*
  * We keep the usr and grp dquots separately so that locking will be easier
  * to do at commit time. All transactions that we know of at this point
  * affect no more than two dquots of one type. Hence, the TRANS_MAXDQS value.
  */
+enum {
+       XFS_QM_TRANS_USR = 0,
+       XFS_QM_TRANS_GRP,
+       XFS_QM_TRANS_DQTYPES
+};
 #define XFS_QM_TRANS_MAXDQS            2
-typedef struct xfs_dquot_acct {
-       xfs_dqtrx_t     dqa_usrdquots[XFS_QM_TRANS_MAXDQS];
-       xfs_dqtrx_t     dqa_grpdquots[XFS_QM_TRANS_MAXDQS];
-} xfs_dquot_acct_t;
+struct xfs_dquot_acct {
+       struct xfs_dqtrx        dqs[XFS_QM_TRANS_DQTYPES][XFS_QM_TRANS_MAXDQS];
+};
 
 /*
  * Users are allowed to have a usage exceeding their softlimit for
@@ -106,22 +138,23 @@ typedef struct xfs_dquot_acct {
 #define XFS_QM_IWARNLIMIT      5
 #define XFS_QM_RTBWARNLIMIT    5
 
-extern void            xfs_qm_destroy_quotainfo(xfs_mount_t *);
-extern int             xfs_qm_quotacheck(xfs_mount_t *);
-extern int             xfs_qm_write_sb_changes(xfs_mount_t *, __int64_t);
+extern void            xfs_qm_destroy_quotainfo(struct xfs_mount *);
+extern int             xfs_qm_quotacheck(struct xfs_mount *);
+extern int             xfs_qm_write_sb_changes(struct xfs_mount *, __int64_t);
 
 /* dquot stuff */
-extern void            xfs_qm_dqpurge_all(xfs_mount_t *, uint);
-extern void            xfs_qm_dqrele_all_inodes(xfs_mount_t *, uint);
+extern void            xfs_qm_dqpurge_all(struct xfs_mount *, uint);
+extern void            xfs_qm_dqrele_all_inodes(struct xfs_mount *, uint);
 
 /* quota ops */
-extern int             xfs_qm_scall_trunc_qfiles(xfs_mount_t *, uint);
-extern int             xfs_qm_scall_getquota(xfs_mount_t *, xfs_dqid_t, uint,
-                                       fs_disk_quota_t *);
+extern int             xfs_qm_scall_trunc_qfiles(struct xfs_mount *, uint);
+extern int             xfs_qm_scall_getquota(struct xfs_mount *, xfs_dqid_t,
+                                       uint, struct fs_disk_quota *);
 extern int             xfs_qm_scall_setqlim(struct xfs_mount *, xfs_dqid_t, uint,
-                                       fs_disk_quota_t *);
-extern int             xfs_qm_scall_getqstat(xfs_mount_t *, fs_quota_stat_t *);
-extern int             xfs_qm_scall_quotaon(xfs_mount_t *, uint);
-extern int             xfs_qm_scall_quotaoff(xfs_mount_t *, uint);
+                                       struct fs_disk_quota *);
+extern int             xfs_qm_scall_getqstat(struct xfs_mount *,
+                                       struct fs_quota_stat *);
+extern int             xfs_qm_scall_quotaon(struct xfs_mount *, uint);
+extern int             xfs_qm_scall_quotaoff(struct xfs_mount *, uint);
 
 #endif /* __XFS_QM_H__ */
index 6cdf6ff..a08801a 100644 (file)
@@ -117,11 +117,11 @@ xfs_qm_scall_quotaoff(
        }
        if (flags & XFS_GQUOTA_ACCT) {
                dqtype |= XFS_QMOPT_GQUOTA;
-               flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD);
+               flags |= (XFS_GQUOTA_CHKD | XFS_GQUOTA_ENFD);
                inactivate_flags |= XFS_GQUOTA_ACTIVE;
        } else if (flags & XFS_PQUOTA_ACCT) {
                dqtype |= XFS_QMOPT_PQUOTA;
-               flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD);
+               flags |= (XFS_PQUOTA_CHKD | XFS_PQUOTA_ENFD);
                inactivate_flags |= XFS_PQUOTA_ACTIVE;
        }
 
@@ -335,14 +335,14 @@ xfs_qm_scall_quotaon(
         * quota acct on ondisk without m_qflags' knowing.
         */
        if (((flags & XFS_UQUOTA_ACCT) == 0 &&
-           (mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) == 0 &&
-           (flags & XFS_UQUOTA_ENFD))
-           ||
+            (mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) == 0 &&
+            (flags & XFS_UQUOTA_ENFD)) ||
+           ((flags & XFS_GQUOTA_ACCT) == 0 &&
+            (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) == 0 &&
+            (flags & XFS_GQUOTA_ENFD)) ||
            ((flags & XFS_PQUOTA_ACCT) == 0 &&
-           (mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) == 0 &&
-           (flags & XFS_GQUOTA_ACCT) == 0 &&
-           (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) == 0 &&
-           (flags & XFS_OQUOTA_ENFD))) {
+            (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",
                        __func__, flags, mp->m_sb.sb_qflags);
@@ -407,11 +407,11 @@ xfs_qm_scall_getqstat(
        struct fs_quota_stat    *out)
 {
        struct xfs_quotainfo    *q = mp->m_quotainfo;
-       struct xfs_inode        *uip, *gip;
-       bool                    tempuqip, tempgqip;
+       struct xfs_inode        *uip = NULL;
+       struct xfs_inode        *gip = NULL;
+       bool                    tempuqip = false;
+       bool                    tempgqip = false;
 
-       uip = gip = NULL;
-       tempuqip = tempgqip = false;
        memset(out, 0, sizeof(fs_quota_stat_t));
 
        out->qs_version = FS_QSTAT_VERSION;
@@ -776,9 +776,12 @@ xfs_qm_scall_getquota(
         * gets turned off. No need to confuse the user level code,
         * so return zeroes in that case.
         */
-       if ((!XFS_IS_UQUOTA_ENFORCED(mp) && dqp->q_core.d_flags == XFS_DQ_USER) ||
-           (!XFS_IS_OQUOTA_ENFORCED(mp) &&
-                       (dqp->q_core.d_flags & (XFS_DQ_PROJ | XFS_DQ_GROUP)))) {
+       if ((!XFS_IS_UQUOTA_ENFORCED(mp) &&
+            dqp->q_core.d_flags == XFS_DQ_USER) ||
+           (!XFS_IS_GQUOTA_ENFORCED(mp) &&
+            dqp->q_core.d_flags == XFS_DQ_GROUP) ||
+           (!XFS_IS_PQUOTA_ENFORCED(mp) &&
+            dqp->q_core.d_flags == XFS_DQ_PROJ)) {
                dst->d_btimer = 0;
                dst->d_itimer = 0;
                dst->d_rtbtimer = 0;
@@ -786,8 +789,8 @@ xfs_qm_scall_getquota(
 
 #ifdef DEBUG
        if (((XFS_IS_UQUOTA_ENFORCED(mp) && dst->d_flags == FS_USER_QUOTA) ||
-            (XFS_IS_OQUOTA_ENFORCED(mp) &&
-                       (dst->d_flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)))) &&
+            (XFS_IS_GQUOTA_ENFORCED(mp) && dst->d_flags == FS_GROUP_QUOTA) ||
+            (XFS_IS_PQUOTA_ENFORCED(mp) && dst->d_flags == FS_PROJ_QUOTA)) &&
            dst->d_id != 0) {
                if ((dst->d_bcount > dst->d_blk_softlimit) &&
                    (dst->d_blk_softlimit > 0)) {
@@ -833,16 +836,16 @@ xfs_qm_export_flags(
        uflags = 0;
        if (flags & XFS_UQUOTA_ACCT)
                uflags |= FS_QUOTA_UDQ_ACCT;
-       if (flags & XFS_PQUOTA_ACCT)
-               uflags |= FS_QUOTA_PDQ_ACCT;
        if (flags & XFS_GQUOTA_ACCT)
                uflags |= FS_QUOTA_GDQ_ACCT;
+       if (flags & XFS_PQUOTA_ACCT)
+               uflags |= FS_QUOTA_PDQ_ACCT;
        if (flags & XFS_UQUOTA_ENFD)
                uflags |= FS_QUOTA_UDQ_ENFD;
-       if (flags & (XFS_OQUOTA_ENFD)) {
-               uflags |= (flags & XFS_GQUOTA_ACCT) ?
-                       FS_QUOTA_GDQ_ENFD : FS_QUOTA_PDQ_ENFD;
-       }
+       if (flags & XFS_GQUOTA_ENFD)
+               uflags |= FS_QUOTA_GDQ_ENFD;
+       if (flags & XFS_PQUOTA_ENFD)
+               uflags |= FS_QUOTA_PDQ_ENFD;
        return (uflags);
 }
 
index c38068f..c3483ba 100644 (file)
@@ -160,31 +160,43 @@ typedef struct xfs_qoff_logformat {
 #define XFS_OQUOTA_CHKD        0x0020  /* quotacheck run on other (grp/prj) quotas */
 #define XFS_GQUOTA_ACCT        0x0040  /* group quota accounting ON */
 
+/*
+ * Conversion to and from the combined OQUOTA flag (if necessary)
+ * is done only in xfs_sb_qflags_to_disk() and xfs_sb_qflags_from_disk()
+ */
+#define XFS_GQUOTA_ENFD        0x0080  /* group quota limits enforced */
+#define XFS_GQUOTA_CHKD        0x0100  /* quotacheck run on group quotas */
+#define XFS_PQUOTA_ENFD        0x0200  /* project quota limits enforced */
+#define XFS_PQUOTA_CHKD        0x0400  /* quotacheck run on project quotas */
+
 /*
  * Quota Accounting/Enforcement flags
  */
 #define XFS_ALL_QUOTA_ACCT     \
                (XFS_UQUOTA_ACCT | XFS_GQUOTA_ACCT | XFS_PQUOTA_ACCT)
-#define XFS_ALL_QUOTA_ENFD     (XFS_UQUOTA_ENFD | XFS_OQUOTA_ENFD)
-#define XFS_ALL_QUOTA_CHKD     (XFS_UQUOTA_CHKD | XFS_OQUOTA_CHKD)
+#define XFS_ALL_QUOTA_ENFD     \
+               (XFS_UQUOTA_ENFD | XFS_GQUOTA_ENFD | XFS_PQUOTA_ENFD)
+#define XFS_ALL_QUOTA_CHKD     \
+               (XFS_UQUOTA_CHKD | XFS_GQUOTA_CHKD | XFS_PQUOTA_CHKD)
 
 #define XFS_IS_QUOTA_RUNNING(mp)       ((mp)->m_qflags & XFS_ALL_QUOTA_ACCT)
 #define XFS_IS_UQUOTA_RUNNING(mp)      ((mp)->m_qflags & XFS_UQUOTA_ACCT)
 #define XFS_IS_PQUOTA_RUNNING(mp)      ((mp)->m_qflags & XFS_PQUOTA_ACCT)
 #define XFS_IS_GQUOTA_RUNNING(mp)      ((mp)->m_qflags & XFS_GQUOTA_ACCT)
 #define XFS_IS_UQUOTA_ENFORCED(mp)     ((mp)->m_qflags & XFS_UQUOTA_ENFD)
-#define XFS_IS_OQUOTA_ENFORCED(mp)     ((mp)->m_qflags & XFS_OQUOTA_ENFD)
+#define XFS_IS_GQUOTA_ENFORCED(mp)     ((mp)->m_qflags & XFS_GQUOTA_ENFD)
+#define XFS_IS_PQUOTA_ENFORCED(mp)     ((mp)->m_qflags & XFS_PQUOTA_ENFD)
 
 /*
  * Incore only flags for quotaoff - these bits get cleared when quota(s)
  * are in the process of getting turned off. These flags are in m_qflags but
  * never in sb_qflags.
  */
-#define XFS_UQUOTA_ACTIVE      0x0100  /* uquotas are being turned off */
-#define XFS_PQUOTA_ACTIVE      0x0200  /* pquotas are being turned off */
-#define XFS_GQUOTA_ACTIVE      0x0400  /* gquotas are being turned off */
+#define XFS_UQUOTA_ACTIVE      0x1000  /* uquotas are being turned off */
+#define XFS_GQUOTA_ACTIVE      0x2000  /* gquotas are being turned off */
+#define XFS_PQUOTA_ACTIVE      0x4000  /* pquotas are being turned off */
 #define XFS_ALL_QUOTA_ACTIVE   \
-       (XFS_UQUOTA_ACTIVE | XFS_PQUOTA_ACTIVE | XFS_GQUOTA_ACTIVE)
+       (XFS_UQUOTA_ACTIVE | XFS_GQUOTA_ACTIVE | XFS_PQUOTA_ACTIVE)
 
 /*
  * Checking XFS_IS_*QUOTA_ON() while holding any inode lock guarantees
@@ -268,24 +280,23 @@ typedef struct xfs_qoff_logformat {
        ((XFS_IS_UQUOTA_ON(mp) && \
                (mp->m_sb.sb_qflags & XFS_UQUOTA_CHKD) == 0) || \
         (XFS_IS_GQUOTA_ON(mp) && \
-               ((mp->m_sb.sb_qflags & XFS_OQUOTA_CHKD) == 0 || \
-                (mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT))) || \
+               (mp->m_sb.sb_qflags & XFS_GQUOTA_CHKD) == 0) || \
         (XFS_IS_PQUOTA_ON(mp) && \
-               ((mp->m_sb.sb_qflags & XFS_OQUOTA_CHKD) == 0 || \
-                (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT))))
+               (mp->m_sb.sb_qflags & XFS_PQUOTA_CHKD) == 0))
 
 #define XFS_MOUNT_QUOTA_SET1   (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\
-                                XFS_UQUOTA_CHKD|XFS_PQUOTA_ACCT|\
-                                XFS_OQUOTA_ENFD|XFS_OQUOTA_CHKD)
+                                XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\
+                                XFS_GQUOTA_ENFD|XFS_GQUOTA_CHKD)
 
 #define XFS_MOUNT_QUOTA_SET2   (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\
-                                XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\
-                                XFS_OQUOTA_ENFD|XFS_OQUOTA_CHKD)
+                                XFS_UQUOTA_CHKD|XFS_PQUOTA_ACCT|\
+                                XFS_PQUOTA_ENFD|XFS_PQUOTA_CHKD)
 
 #define XFS_MOUNT_QUOTA_ALL    (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\
-                                XFS_UQUOTA_CHKD|XFS_PQUOTA_ACCT|\
-                                XFS_OQUOTA_ENFD|XFS_OQUOTA_CHKD|\
-                                XFS_GQUOTA_ACCT)
+                                XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\
+                                XFS_GQUOTA_ENFD|XFS_GQUOTA_CHKD|\
+                                XFS_PQUOTA_ACCT|XFS_PQUOTA_ENFD|\
+                                XFS_PQUOTA_CHKD)
 
 
 /*
index 71926d6..20e30f9 100644 (file)
@@ -75,8 +75,10 @@ xfs_fs_set_xstate(
                flags |= XFS_GQUOTA_ACCT;
        if (uflags & FS_QUOTA_UDQ_ENFD)
                flags |= XFS_UQUOTA_ENFD;
-       if (uflags & (FS_QUOTA_PDQ_ENFD|FS_QUOTA_GDQ_ENFD))
-               flags |= XFS_OQUOTA_ENFD;
+       if (uflags & FS_QUOTA_GDQ_ENFD)
+               flags |= XFS_GQUOTA_ENFD;
+       if (uflags & FS_QUOTA_PDQ_ENFD)
+               flags |= XFS_PQUOTA_ENFD;
 
        switch (op) {
        case Q_XQUOTAON:
index 2de58a8..78f9e70 100644 (file)
@@ -618,6 +618,12 @@ xfs_sb_has_incompat_log_feature(
        return (sbp->sb_features_log_incompat & feature) != 0;
 }
 
+static inline bool
+xfs_is_quota_inode(struct xfs_sb *sbp, xfs_ino_t ino)
+{
+       return (ino == sbp->sb_uquotino || ino == sbp->sb_gquotino);
+}
+
 /*
  * end of superblock version macros
  */
index 3033ba5..1d68ffc 100644 (file)
@@ -51,6 +51,7 @@
 #include "xfs_inode_item.h"
 #include "xfs_icache.h"
 #include "xfs_trace.h"
+#include "xfs_icreate_item.h"
 
 #include <linux/namei.h>
 #include <linux/init.h>
@@ -359,17 +360,17 @@ xfs_parseargs(
                } else if (!strcmp(this_char, MNTOPT_PQUOTA) ||
                           !strcmp(this_char, MNTOPT_PRJQUOTA)) {
                        mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE |
-                                        XFS_OQUOTA_ENFD);
+                                        XFS_PQUOTA_ENFD);
                } else if (!strcmp(this_char, MNTOPT_PQUOTANOENF)) {
                        mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE);
-                       mp->m_qflags &= ~XFS_OQUOTA_ENFD;
+                       mp->m_qflags &= ~XFS_PQUOTA_ENFD;
                } else if (!strcmp(this_char, MNTOPT_GQUOTA) ||
                           !strcmp(this_char, MNTOPT_GRPQUOTA)) {
                        mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE |
-                                        XFS_OQUOTA_ENFD);
+                                        XFS_GQUOTA_ENFD);
                } else if (!strcmp(this_char, MNTOPT_GQUOTANOENF)) {
                        mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE);
-                       mp->m_qflags &= ~XFS_OQUOTA_ENFD;
+                       mp->m_qflags &= ~XFS_GQUOTA_ENFD;
                } else if (!strcmp(this_char, MNTOPT_DELAYLOG)) {
                        xfs_warn(mp,
        "delaylog is the default now, option is deprecated.");
@@ -439,20 +440,15 @@ xfs_parseargs(
        }
 
 done:
-       if (!(mp->m_flags & XFS_MOUNT_NOALIGN)) {
+       if (dsunit && !(mp->m_flags & XFS_MOUNT_NOALIGN)) {
                /*
                 * At this point the superblock has not been read
                 * in, therefore we do not know the block size.
                 * Before the mount call ends we will convert
                 * these to FSBs.
                 */
-               if (dsunit) {
-                       mp->m_dalign = dsunit;
-                       mp->m_flags |= XFS_MOUNT_RETERR;
-               }
-
-               if (dswidth)
-                       mp->m_swidth = dswidth;
+               mp->m_dalign = dsunit;
+               mp->m_swidth = dswidth;
        }
 
        if (mp->m_logbufs != -1 &&
@@ -563,12 +559,12 @@ xfs_showargs(
        /* Either project or group quotas can be active, not both */
 
        if (mp->m_qflags & XFS_PQUOTA_ACCT) {
-               if (mp->m_qflags & XFS_OQUOTA_ENFD)
+               if (mp->m_qflags & XFS_PQUOTA_ENFD)
                        seq_puts(m, "," MNTOPT_PRJQUOTA);
                else
                        seq_puts(m, "," MNTOPT_PQUOTANOENF);
        } else if (mp->m_qflags & XFS_GQUOTA_ACCT) {
-               if (mp->m_qflags & XFS_OQUOTA_ENFD)
+               if (mp->m_qflags & XFS_GQUOTA_ENFD)
                        seq_puts(m, "," MNTOPT_GRPQUOTA);
                else
                        seq_puts(m, "," MNTOPT_GQUOTANOENF);
@@ -1136,8 +1132,8 @@ xfs_fs_statfs(
        spin_unlock(&mp->m_sb_lock);
 
        if ((ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) &&
-           ((mp->m_qflags & (XFS_PQUOTA_ACCT|XFS_OQUOTA_ENFD))) ==
-                             (XFS_PQUOTA_ACCT|XFS_OQUOTA_ENFD))
+           ((mp->m_qflags & (XFS_PQUOTA_ACCT|XFS_PQUOTA_ENFD))) ==
+                             (XFS_PQUOTA_ACCT|XFS_PQUOTA_ENFD))
                xfs_qm_statvfs(ip, statp);
        return 0;
 }
@@ -1481,6 +1477,10 @@ xfs_fs_fill_super(
        sb->s_time_gran = 1;
        set_posix_acl_flag(sb);
 
+       /* version 5 superblocks support inode version counters. */
+       if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5)
+               sb->s_flags |= MS_I_VERSION;
+
        error = xfs_mountfs(mp);
        if (error)
                goto out_filestream_unmount;
@@ -1655,9 +1655,15 @@ xfs_init_zones(void)
                                        KM_ZONE_SPREAD, NULL);
        if (!xfs_ili_zone)
                goto out_destroy_inode_zone;
+       xfs_icreate_zone = kmem_zone_init(sizeof(struct xfs_icreate_item),
+                                       "xfs_icr");
+       if (!xfs_icreate_zone)
+               goto out_destroy_ili_zone;
 
        return 0;
 
+ out_destroy_ili_zone:
+       kmem_zone_destroy(xfs_ili_zone);
  out_destroy_inode_zone:
        kmem_zone_destroy(xfs_inode_zone);
  out_destroy_efi_zone:
@@ -1696,6 +1702,7 @@ xfs_destroy_zones(void)
         * destroy caches.
         */
        rcu_barrier();
+       kmem_zone_destroy(xfs_icreate_zone);
        kmem_zone_destroy(xfs_ili_zone);
        kmem_zone_destroy(xfs_inode_zone);
        kmem_zone_destroy(xfs_efi_zone);
index 195a403..e830fb5 100644 (file)
@@ -358,7 +358,8 @@ xfs_symlink(
        int                     n;
        xfs_buf_t               *bp;
        prid_t                  prid;
-       struct xfs_dquot        *udqp, *gdqp;
+       struct xfs_dquot        *udqp = NULL;
+       struct xfs_dquot        *gdqp = NULL;
        uint                    resblks;
 
        *ipp = NULL;
@@ -585,7 +586,7 @@ xfs_symlink(
 /*
  * Free a symlink that has blocks associated with it.
  */
-int
+STATIC int
 xfs_inactive_symlink_rmt(
        xfs_inode_t     *ip,
        xfs_trans_t     **tpp)
@@ -606,7 +607,7 @@ xfs_inactive_symlink_rmt(
 
        tp = *tpp;
        mp = ip->i_mount;
-       ASSERT(ip->i_d.di_size > XFS_IFORK_DSIZE(ip));
+       ASSERT(ip->i_df.if_flags & XFS_IFEXTENTS);
        /*
         * We're freeing a symlink that has some
         * blocks allocated to it.  Free the
@@ -720,3 +721,47 @@ xfs_inactive_symlink_rmt(
  error0:
        return error;
 }
+
+/*
+ * xfs_inactive_symlink - free a symlink
+ */
+int
+xfs_inactive_symlink(
+       struct xfs_inode        *ip,
+       struct xfs_trans        **tp)
+{
+       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);
+
+       /*
+        * Zero length symlinks _can_ exist.
+        */
+       pathlen = (int)ip->i_d.di_size;
+       if (!pathlen)
+               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);
+               ASSERT(0);
+               return XFS_ERROR(EFSCORRUPTED);
+       }
+
+       if (ip->i_df.if_flags & XFS_IFINLINE) {
+               if (ip->i_df.if_bytes > 0)
+                       xfs_idata_realloc(ip, -(ip->i_df.if_bytes),
+                                         XFS_DATA_FORK);
+               ASSERT(ip->i_df.if_bytes == 0);
+               return 0;
+       }
+
+       /* remove the remote symlink */
+       return xfs_inactive_symlink_rmt(ip, tp);
+}
index b39398d..3743948 100644 (file)
@@ -60,7 +60,7 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
 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_rmt(struct xfs_inode *ip, struct xfs_trans **tpp);
+int xfs_inactive_symlink(struct xfs_inode *ip, struct xfs_trans **tpp);
 
 #endif /* __KERNEL__ */
 #endif /* __XFS_SYMLINK_H */
index 2801b5c..1743b9f 100644 (file)
@@ -25,11 +25,11 @@ static struct ctl_table_header *xfs_table_header;
 #ifdef CONFIG_PROC_FS
 STATIC int
 xfs_stats_clear_proc_handler(
-       ctl_table       *ctl,
-       int             write,
-       void            __user *buffer,
-       size_t          *lenp,
-       loff_t          *ppos)
+       struct ctl_table        *ctl,
+       int                     write,
+       void                    __user *buffer,
+       size_t                  *lenp,
+       loff_t                  *ppos)
 {
        int             c, ret, *valp = ctl->data;
        __uint32_t      vn_active;
@@ -55,11 +55,11 @@ xfs_stats_clear_proc_handler(
 
 STATIC int
 xfs_panic_mask_proc_handler(
-       ctl_table       *ctl,
-       int             write,
-       void            __user *buffer,
-       size_t          *lenp,
-       loff_t          *ppos)
+       struct ctl_table        *ctl,
+       int                     write,
+       void                    __user *buffer,
+       size_t                  *lenp,
+       loff_t                  *ppos)
 {
        int             ret, *valp = ctl->data;
 
@@ -74,7 +74,7 @@ xfs_panic_mask_proc_handler(
 }
 #endif /* CONFIG_PROC_FS */
 
-static ctl_table xfs_table[] = {
+static struct ctl_table xfs_table[] = {
        {
                .procname       = "irix_sgid_inherit",
                .data           = &xfs_params.sgid_inherit.val,
@@ -227,7 +227,7 @@ static ctl_table xfs_table[] = {
        {}
 };
 
-static ctl_table xfs_dir_table[] = {
+static struct ctl_table xfs_dir_table[] = {
        {
                .procname       = "xfs",
                .mode           = 0555,
@@ -236,7 +236,7 @@ static ctl_table xfs_dir_table[] = {
        {}
 };
 
-static ctl_table xfs_root_table[] = {
+static struct ctl_table xfs_root_table[] = {
        {
                .procname       = "fs",
                .mode           = 0555,
index a04701d..47910e6 100644 (file)
@@ -486,9 +486,12 @@ DEFINE_EVENT(xfs_buf_item_class, name, \
        TP_PROTO(struct xfs_buf_log_item *bip), \
        TP_ARGS(bip))
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_size);
+DEFINE_BUF_ITEM_EVENT(xfs_buf_item_size_ordered);
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_size_stale);
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_format);
+DEFINE_BUF_ITEM_EVENT(xfs_buf_item_format_ordered);
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_format_stale);
+DEFINE_BUF_ITEM_EVENT(xfs_buf_item_ordered);
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_pin);
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unpin);
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unpin_stale);
@@ -508,6 +511,7 @@ DEFINE_BUF_ITEM_EVENT(xfs_trans_bjoin);
 DEFINE_BUF_ITEM_EVENT(xfs_trans_bhold);
 DEFINE_BUF_ITEM_EVENT(xfs_trans_bhold_release);
 DEFINE_BUF_ITEM_EVENT(xfs_trans_binval);
+DEFINE_BUF_ITEM_EVENT(xfs_trans_buf_ordered);
 
 DECLARE_EVENT_CLASS(xfs_lock_class,
        TP_PROTO(struct xfs_inode *ip, unsigned lock_flags,
@@ -571,6 +575,7 @@ DEFINE_INODE_EVENT(xfs_iget_miss);
 DEFINE_INODE_EVENT(xfs_getattr);
 DEFINE_INODE_EVENT(xfs_setattr);
 DEFINE_INODE_EVENT(xfs_readlink);
+DEFINE_INODE_EVENT(xfs_inactive_symlink);
 DEFINE_INODE_EVENT(xfs_alloc_file_space);
 DEFINE_INODE_EVENT(xfs_free_file_space);
 DEFINE_INODE_EVENT(xfs_readdir);
index 2fd7c1f..35a2299 100644 (file)
@@ -234,71 +234,93 @@ xfs_calc_remove_reservation(
 }
 
 /*
- * For symlink we can modify:
+ * For create, break it in to the two cases that the transaction
+ * covers. We start with the modify case - allocation done by modification
+ * of the state of existing inodes - and the allocation case.
+ */
+
+/*
+ * For create we can modify:
  *    the parent directory inode: inode size
  *    the new inode: inode size
- *    the inode btree entry: 1 block
+ *    the inode btree entry: block size
+ *    the superblock for the nlink flag: sector size
  *    the directory btree: (max depth + v2) * dir block size
  *    the directory inode's bmap btree: (max depth + v2) * block size
- *    the blocks for the symlink: 1 kB
- * Or in the first xact we allocate some inodes giving:
+ */
+STATIC uint
+xfs_calc_create_resv_modify(
+       struct xfs_mount        *mp)
+{
+       return xfs_calc_buf_res(2, mp->m_sb.sb_inodesize) +
+               xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
+               (uint)XFS_FSB_TO_B(mp, 1) +
+               xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1));
+}
+
+/*
+ * For create we can allocate some inodes giving:
  *    the agi and agf of the ag getting the new inodes: 2 * sectorsize
+ *    the superblock for the nlink flag: sector size
  *    the inode blocks allocated: XFS_IALLOC_BLOCKS * blocksize
  *    the inode btree: max depth * blocksize
- *    the allocation btrees: 2 trees * (2 * max depth - 1) * block size
+ *    the allocation btrees: 2 trees * (max depth - 1) * block size
  */
 STATIC uint
-xfs_calc_symlink_reservation(
+xfs_calc_create_resv_alloc(
+       struct xfs_mount        *mp)
+{
+       return xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +
+               mp->m_sb.sb_sectsize +
+               xfs_calc_buf_res(XFS_IALLOC_BLOCKS(mp), XFS_FSB_TO_B(mp, 1)) +
+               xfs_calc_buf_res(mp->m_in_maxlevels, XFS_FSB_TO_B(mp, 1)) +
+               xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
+                                XFS_FSB_TO_B(mp, 1));
+}
+
+STATIC uint
+__xfs_calc_create_reservation(
        struct xfs_mount        *mp)
 {
        return XFS_DQUOT_LOGRES(mp) +
-               MAX((xfs_calc_buf_res(2, mp->m_sb.sb_inodesize) +
-                    xfs_calc_buf_res(1, XFS_FSB_TO_B(mp, 1)) +
-                    xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
-                                     XFS_FSB_TO_B(mp, 1)) +
-                    xfs_calc_buf_res(1, 1024)),
-                   (xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +
-                    xfs_calc_buf_res(XFS_IALLOC_BLOCKS(mp),
-                                     XFS_FSB_TO_B(mp, 1)) +
-                    xfs_calc_buf_res(mp->m_in_maxlevels,
-                                     XFS_FSB_TO_B(mp, 1)) +
-                    xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
-                                     XFS_FSB_TO_B(mp, 1))));
+               MAX(xfs_calc_create_resv_alloc(mp),
+                   xfs_calc_create_resv_modify(mp));
 }
 
 /*
- * For create we can modify:
- *    the parent directory inode: inode size
- *    the new inode: inode size
- *    the inode btree entry: block size
- *    the superblock for the nlink flag: sector size
- *    the directory btree: (max depth + v2) * dir block size
- *    the directory inode's bmap btree: (max depth + v2) * block size
- * Or in the first xact we allocate some inodes giving:
+ * For icreate we can allocate some inodes giving:
  *    the agi and agf of the ag getting the new inodes: 2 * sectorsize
  *    the superblock for the nlink flag: sector size
- *    the inode blocks allocated: XFS_IALLOC_BLOCKS * blocksize
  *    the inode btree: max depth * blocksize
  *    the allocation btrees: 2 trees * (max depth - 1) * block size
  */
 STATIC uint
-xfs_calc_create_reservation(
+xfs_calc_icreate_resv_alloc(
        struct xfs_mount        *mp)
+{
+       return xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +
+               mp->m_sb.sb_sectsize +
+               xfs_calc_buf_res(mp->m_in_maxlevels, XFS_FSB_TO_B(mp, 1)) +
+               xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
+                                XFS_FSB_TO_B(mp, 1));
+}
+
+STATIC uint
+xfs_calc_icreate_reservation(xfs_mount_t *mp)
 {
        return XFS_DQUOT_LOGRES(mp) +
-               MAX((xfs_calc_buf_res(2, mp->m_sb.sb_inodesize) +
-                    xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
-                    (uint)XFS_FSB_TO_B(mp, 1) +
-                    xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
-                                     XFS_FSB_TO_B(mp, 1))),
-                   (xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +
-                    mp->m_sb.sb_sectsize +
-                    xfs_calc_buf_res(XFS_IALLOC_BLOCKS(mp),
-                                     XFS_FSB_TO_B(mp, 1)) +
-                    xfs_calc_buf_res(mp->m_in_maxlevels,
-                                     XFS_FSB_TO_B(mp, 1)) +
-                    xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
-                                     XFS_FSB_TO_B(mp, 1))));
+               MAX(xfs_calc_icreate_resv_alloc(mp),
+                   xfs_calc_create_resv_modify(mp));
+}
+
+STATIC uint
+xfs_calc_create_reservation(
+       struct xfs_mount        *mp)
+{
+       if (xfs_sb_version_hascrc(&mp->m_sb))
+               return xfs_calc_icreate_reservation(mp);
+       return __xfs_calc_create_reservation(mp);
+
 }
 
 /*
@@ -311,6 +333,20 @@ xfs_calc_mkdir_reservation(
        return xfs_calc_create_reservation(mp);
 }
 
+
+/*
+ * Making a new symplink is the same as creating a new file, but
+ * with the added blocks for remote symlink data which can be up to 1kB in
+ * length (MAXPATHLEN).
+ */
+STATIC uint
+xfs_calc_symlink_reservation(
+       struct xfs_mount        *mp)
+{
+       return xfs_calc_create_reservation(mp) +
+              xfs_calc_buf_res(1, MAXPATHLEN);
+}
+
 /*
  * In freeing an inode we can modify:
  *    the inode being freed: inode size
index a44dba5..2b49463 100644 (file)
@@ -48,6 +48,7 @@ typedef struct xfs_trans_header {
 #define        XFS_LI_BUF              0x123c  /* v2 bufs, variable sized inode bufs */
 #define        XFS_LI_DQUOT            0x123d
 #define        XFS_LI_QUOTAOFF         0x123e
+#define        XFS_LI_ICREATE          0x123f
 
 #define XFS_LI_TYPE_DESC \
        { XFS_LI_EFI,           "XFS_LI_EFI" }, \
@@ -107,7 +108,8 @@ typedef struct xfs_trans_header {
 #define        XFS_TRANS_SWAPEXT               40
 #define        XFS_TRANS_SB_COUNT              41
 #define        XFS_TRANS_CHECKPOINT            42
-#define        XFS_TRANS_TYPE_MAX              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 \
@@ -210,23 +212,18 @@ struct xfs_log_item_desc {
 /*
  * Per-extent log reservation for the allocation btree changes
  * involved in freeing or allocating an extent.
- * 2 trees * (2 blocks/level * max depth - 1) * block size
+ * 2 trees * (2 blocks/level * max depth - 1)
  */
-#define        XFS_ALLOCFREE_LOG_RES(mp,nx) \
-       ((nx) * (2 * XFS_FSB_TO_B((mp), 2 * XFS_AG_MAXLEVELS(mp) - 1)))
 #define        XFS_ALLOCFREE_LOG_COUNT(mp,nx) \
        ((nx) * (2 * (2 * XFS_AG_MAXLEVELS(mp) - 1)))
 
 /*
  * Per-directory log reservation for any directory change.
- * dir blocks: (1 btree block per level + data block + free block) * dblock size
- * bmap btree: (levels + 2) * max depth * block size
+ * dir blocks: (1 btree block per level + data block + free block)
+ * bmap btree: (levels + 2) * max depth
  * v2 directory blocks can be fragmented below the dirblksize down to the fsb
  * size, so account for that in the DAENTER macros.
  */
-#define        XFS_DIROP_LOG_RES(mp)   \
-       (XFS_FSB_TO_B(mp, XFS_DAENTER_BLOCKS(mp, XFS_DATA_FORK)) + \
-        (XFS_FSB_TO_B(mp, XFS_DAENTER_BMAPS(mp, XFS_DATA_FORK) + 1)))
 #define        XFS_DIROP_LOG_COUNT(mp) \
        (XFS_DAENTER_BLOCKS(mp, XFS_DATA_FORK) + \
         XFS_DAENTER_BMAPS(mp, XFS_DATA_FORK) + 1)
@@ -503,6 +500,7 @@ void                xfs_trans_bhold_release(xfs_trans_t *, struct xfs_buf *);
 void           xfs_trans_binval(xfs_trans_t *, struct xfs_buf *);
 void           xfs_trans_inode_buf(xfs_trans_t *, struct xfs_buf *);
 void           xfs_trans_stale_inode_buf(xfs_trans_t *, struct xfs_buf *);
+void           xfs_trans_ordered_buf(xfs_trans_t *, struct xfs_buf *);
 void           xfs_trans_dquot_buf(xfs_trans_t *, struct xfs_buf *, uint);
 void           xfs_trans_inode_alloc_buf(xfs_trans_t *, struct xfs_buf *);
 void           xfs_trans_ichgtime(struct xfs_trans *, struct xfs_inode *, int);
index 73a5fa4..aa5a04b 100644 (file)
@@ -397,7 +397,6 @@ shutdown_abort:
        return XFS_ERROR(EIO);
 }
 
-
 /*
  * Release the buffer bp which was previously acquired with one of the
  * xfs_trans_... buffer allocation routines if the buffer has not
@@ -603,8 +602,14 @@ xfs_trans_log_buf(xfs_trans_t      *tp,
 
        tp->t_flags |= XFS_TRANS_DIRTY;
        bip->bli_item.li_desc->lid_flags |= XFS_LID_DIRTY;
-       bip->bli_flags |= XFS_BLI_LOGGED;
-       xfs_buf_item_log(bip, first, last);
+
+       /*
+        * If we have an ordered buffer we are not logging any dirty range but
+        * it still needs to be marked dirty and that it has been logged.
+        */
+       bip->bli_flags |= XFS_BLI_DIRTY | XFS_BLI_LOGGED;
+       if (!(bip->bli_flags & XFS_BLI_ORDERED))
+               xfs_buf_item_log(bip, first, last);
 }
 
 
@@ -756,6 +761,29 @@ xfs_trans_inode_alloc_buf(
        xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DINO_BUF);
 }
 
+/*
+ * Mark the buffer as ordered for this transaction. This means
+ * that the contents of the buffer are not recorded in the transaction
+ * but it is tracked in the AIL as though it was. This allows us
+ * to record logical changes in transactions rather than the physical
+ * changes we make to the buffer without changing writeback ordering
+ * constraints of metadata buffers.
+ */
+void
+xfs_trans_ordered_buf(
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp)
+{
+       struct xfs_buf_log_item *bip = bp->b_fspriv;
+
+       ASSERT(bp->b_transp == tp);
+       ASSERT(bip != NULL);
+       ASSERT(atomic_read(&bip->bli_refcount) > 0);
+
+       bip->bli_flags |= XFS_BLI_ORDERED;
+       trace_xfs_buf_item_ordered(bip);
+}
+
 /*
  * Set the type of the buffer for log recovery so that it can correctly identify
  * and hence attach the correct buffer ops to the buffer after replay.
index fec75d0..3ba64d5 100644 (file)
@@ -103,8 +103,6 @@ xfs_trans_dup_dqinfo(
                return;
 
        xfs_trans_alloc_dqinfo(ntp);
-       oqa = otp->t_dqinfo->dqa_usrdquots;
-       nqa = ntp->t_dqinfo->dqa_usrdquots;
 
        /*
         * Because the quota blk reservation is carried forward,
@@ -113,7 +111,9 @@ xfs_trans_dup_dqinfo(
        if(otp->t_flags & XFS_TRANS_DQ_DIRTY)
                ntp->t_flags |= XFS_TRANS_DQ_DIRTY;
 
-       for (j = 0; j < 2; j++) {
+       for (j = 0; j < XFS_QM_TRANS_DQTYPES; j++) {
+               oqa = otp->t_dqinfo->dqs[j];
+               nqa = ntp->t_dqinfo->dqs[j];
                for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
                        if (oqa[i].qt_dquot == NULL)
                                break;
@@ -138,8 +138,6 @@ xfs_trans_dup_dqinfo(
                        oq->qt_ino_res = oq->qt_ino_res_used;
 
                }
-               oqa = otp->t_dqinfo->dqa_grpdquots;
-               nqa = ntp->t_dqinfo->dqa_grpdquots;
        }
 }
 
@@ -157,8 +155,7 @@ xfs_trans_mod_dquot_byino(
 
        if (!XFS_IS_QUOTA_RUNNING(mp) ||
            !XFS_IS_QUOTA_ON(mp) ||
-           ip->i_ino == mp->m_sb.sb_uquotino ||
-           ip->i_ino == mp->m_sb.sb_gquotino)
+           xfs_is_quota_inode(&mp->m_sb, ip->i_ino))
                return;
 
        if (tp->t_dqinfo == NULL)
@@ -170,16 +167,18 @@ xfs_trans_mod_dquot_byino(
                (void) xfs_trans_mod_dquot(tp, ip->i_gdquot, field, delta);
 }
 
-STATIC xfs_dqtrx_t *
+STATIC struct xfs_dqtrx *
 xfs_trans_get_dqtrx(
-       xfs_trans_t     *tp,
-       xfs_dquot_t     *dqp)
+       struct xfs_trans        *tp,
+       struct xfs_dquot        *dqp)
 {
-       int             i;
-       xfs_dqtrx_t     *qa;
+       int                     i;
+       struct xfs_dqtrx        *qa;
 
-       qa = XFS_QM_ISUDQ(dqp) ?
-               tp->t_dqinfo->dqa_usrdquots : tp->t_dqinfo->dqa_grpdquots;
+       if (XFS_QM_ISUDQ(dqp))
+               qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_USR];
+       else
+               qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_GRP];
 
        for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
                if (qa[i].qt_dquot == NULL ||
@@ -339,12 +338,10 @@ xfs_trans_apply_dquot_deltas(
                return;
 
        ASSERT(tp->t_dqinfo);
-       qa = tp->t_dqinfo->dqa_usrdquots;
-       for (j = 0; j < 2; j++) {
-               if (qa[0].qt_dquot == NULL) {
-                       qa = tp->t_dqinfo->dqa_grpdquots;
+       for (j = 0; j < XFS_QM_TRANS_DQTYPES; j++) {
+               qa = tp->t_dqinfo->dqs[j];
+               if (qa[0].qt_dquot == NULL)
                        continue;
-               }
 
                /*
                 * Lock all of the dquots and join them to the transaction.
@@ -495,10 +492,6 @@ xfs_trans_apply_dquot_deltas(
                        ASSERT(dqp->q_res_rtbcount >=
                                be64_to_cpu(dqp->q_core.d_rtbcount));
                }
-               /*
-                * Do the group quotas next
-                */
-               qa = tp->t_dqinfo->dqa_grpdquots;
        }
 }
 
@@ -521,9 +514,9 @@ xfs_trans_unreserve_and_mod_dquots(
        if (!tp->t_dqinfo || !(tp->t_flags & XFS_TRANS_DQ_DIRTY))
                return;
 
-       qa = tp->t_dqinfo->dqa_usrdquots;
+       for (j = 0; j < XFS_QM_TRANS_DQTYPES; j++) {
+               qa = tp->t_dqinfo->dqs[j];
 
-       for (j = 0; j < 2; j++) {
                for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
                        qtrx = &qa[i];
                        /*
@@ -565,7 +558,6 @@ xfs_trans_unreserve_and_mod_dquots(
                                xfs_dqunlock(dqp);
 
                }
-               qa = tp->t_dqinfo->dqa_grpdquots;
        }
 }
 
@@ -640,8 +632,8 @@ xfs_trans_dqresv(
        if ((flags & XFS_QMOPT_FORCE_RES) == 0 &&
            dqp->q_core.d_id &&
            ((XFS_IS_UQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISUDQ(dqp)) ||
-            (XFS_IS_OQUOTA_ENFORCED(dqp->q_mount) &&
-             (XFS_QM_ISPDQ(dqp) || XFS_QM_ISGDQ(dqp))))) {
+            (XFS_IS_GQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISGDQ(dqp)) ||
+            (XFS_IS_PQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISPDQ(dqp)))) {
                if (nblks > 0) {
                        /*
                         * dquot is locked already. See if we'd go over the
@@ -748,15 +740,15 @@ error_return:
  */
 int
 xfs_trans_reserve_quota_bydquots(
-       xfs_trans_t     *tp,
-       xfs_mount_t     *mp,
-       xfs_dquot_t     *udqp,
-       xfs_dquot_t     *gdqp,
-       long            nblks,
-       long            ninos,
-       uint            flags)
+       struct xfs_trans        *tp,
+       struct xfs_mount        *mp,
+       struct xfs_dquot        *udqp,
+       struct xfs_dquot        *gdqp,
+       long                    nblks,
+       long                    ninos,
+       uint                    flags)
 {
-       int             resvd = 0, error;
+       int             error;
 
        if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))
                return 0;
@@ -771,28 +763,24 @@ xfs_trans_reserve_quota_bydquots(
                                        (flags & ~XFS_QMOPT_ENOSPC));
                if (error)
                        return error;
-               resvd = 1;
        }
 
        if (gdqp) {
                error = xfs_trans_dqresv(tp, mp, gdqp, nblks, ninos, flags);
-               if (error) {
-                       /*
-                        * can't do it, so backout previous reservation
-                        */
-                       if (resvd) {
-                               flags |= XFS_QMOPT_FORCE_RES;
-                               xfs_trans_dqresv(tp, mp, udqp,
-                                                -nblks, -ninos, flags);
-                       }
-                       return error;
-               }
+               if (error)
+                       goto unwind_usr;
        }
 
        /*
         * Didn't change anything critical, so, no need to log
         */
        return 0;
+
+unwind_usr:
+       flags |= XFS_QMOPT_FORCE_RES;
+       if (udqp)
+               xfs_trans_dqresv(tp, mp, udqp, -nblks, -ninos, flags);
+       return error;
 }
 
 
@@ -816,8 +804,7 @@ xfs_trans_reserve_quota_nblks(
        if (XFS_IS_PQUOTA_ON(mp))
                flags |= XFS_QMOPT_ENOSPC;
 
-       ASSERT(ip->i_ino != mp->m_sb.sb_uquotino);
-       ASSERT(ip->i_ino != mp->m_sb.sb_gquotino);
+       ASSERT(!xfs_is_quota_inode(&mp->m_sb, ip->i_ino));
 
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
        ASSERT((flags & ~(XFS_QMOPT_FORCE_RES | XFS_QMOPT_ENOSPC)) ==
index ac6d567..53dfe46 100644 (file)
@@ -112,6 +112,17 @@ xfs_trans_log_inode(
        ASSERT(ip->i_itemp != NULL);
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
 
+       /*
+        * First time we log the inode in a transaction, bump the inode change
+        * counter if it is configured for this to occur.
+        */
+       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;
+               flags |= XFS_ILOG_CORE;
+       }
+
        tp->t_flags |= XFS_TRANS_DIRTY;
        ip->i_itemp->ili_item.li_desc->lid_flags |= XFS_LID_DIRTY;
 
index 0176bb2..42c0ef2 100644 (file)
@@ -322,18 +322,9 @@ xfs_inactive(
        xfs_trans_ijoin(tp, ip, 0);
 
        if (S_ISLNK(ip->i_d.di_mode)) {
-               /*
-                * Zero length symlinks _can_ exist.
-                */
-               if (ip->i_d.di_size > XFS_IFORK_DSIZE(ip)) {
-                       error = xfs_inactive_symlink_rmt(ip, &tp);
-                       if (error)
-                               goto out_cancel;
-               } else if (ip->i_df.if_bytes > 0) {
-                       xfs_idata_realloc(ip, -(ip->i_df.if_bytes),
-                                         XFS_DATA_FORK);
-                       ASSERT(ip->i_df.if_bytes == 0);
-               }
+               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);
index 63d17ee..12083dc 100644 (file)
 #include <linux/mm.h>
 #include <linux/cdev.h>
 #include <linux/mutex.h>
+#include <linux/io.h>
 #include <linux/slab.h>
 #if defined(__alpha__) || defined(__powerpc__)
 #include <asm/pgtable.h>       /* For pte_wrprotect */
 #endif
-#include <asm/io.h>
 #include <asm/mman.h>
 #include <asm/uaccess.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
 #if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
 #include <linux/types.h>
 #include <linux/agp_backend.h>
@@ -933,12 +930,15 @@ struct drm_driver {
                                struct dma_buf *dma_buf);
        /* low-level interface used by drm_gem_prime_{import,export} */
        int (*gem_prime_pin)(struct drm_gem_object *obj);
+       void (*gem_prime_unpin)(struct drm_gem_object *obj);
        struct sg_table *(*gem_prime_get_sg_table)(struct drm_gem_object *obj);
        struct drm_gem_object *(*gem_prime_import_sg_table)(
                                struct drm_device *dev, size_t size,
                                struct sg_table *sgt);
        void *(*gem_prime_vmap)(struct drm_gem_object *obj);
        void (*gem_prime_vunmap)(struct drm_gem_object *obj, void *vaddr);
+       int (*gem_prime_mmap)(struct drm_gem_object *obj,
+                               struct vm_area_struct *vma);
 
        /* vga arb irq handler */
        void (*vgaarb_irq)(struct drm_device *dev, bool state);
@@ -1250,37 +1250,8 @@ static inline int drm_core_has_MTRR(struct drm_device *dev)
 {
        return drm_core_check_feature(dev, DRIVER_USE_MTRR);
 }
-
-#define DRM_MTRR_WC            MTRR_TYPE_WRCOMB
-
-static inline int drm_mtrr_add(unsigned long offset, unsigned long size,
-                              unsigned int flags)
-{
-       return mtrr_add(offset, size, flags, 1);
-}
-
-static inline int drm_mtrr_del(int handle, unsigned long offset,
-                              unsigned long size, unsigned int flags)
-{
-       return mtrr_del(handle, offset, size);
-}
-
 #else
 #define drm_core_has_MTRR(dev) (0)
-
-#define DRM_MTRR_WC            0
-
-static inline int drm_mtrr_add(unsigned long offset, unsigned long size,
-                              unsigned int flags)
-{
-       return 0;
-}
-
-static inline int drm_mtrr_del(int handle, unsigned long offset,
-                              unsigned long size, unsigned int flags)
-{
-       return 0;
-}
 #endif
 
 static inline void drm_device_set_unplugged(struct drm_device *dev)
@@ -1630,7 +1601,6 @@ extern void drm_sysfs_destroy(void);
 extern int drm_sysfs_device_add(struct drm_minor *minor);
 extern void drm_sysfs_hotplug_event(struct drm_device *dev);
 extern void drm_sysfs_device_remove(struct drm_minor *minor);
-extern char *drm_get_connector_status_name(enum drm_connector_status status);
 extern int drm_sysfs_connector_add(struct drm_connector *connector);
 extern void drm_sysfs_connector_remove(struct drm_connector *connector);
 
@@ -1648,6 +1618,8 @@ int drm_gem_private_object_init(struct drm_device *dev,
 void drm_gem_object_handle_free(struct drm_gem_object *obj);
 void drm_gem_vm_open(struct vm_area_struct *vma);
 void drm_gem_vm_close(struct vm_area_struct *vma);
+int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size,
+                    struct vm_area_struct *vma);
 int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
 
 #include <drm/drm_global.h>
index adb3f9b..fa12a2f 100644 (file)
@@ -339,6 +339,9 @@ struct drm_crtc_funcs {
        /* cursor controls */
        int (*cursor_set)(struct drm_crtc *crtc, struct drm_file *file_priv,
                          uint32_t handle, uint32_t width, uint32_t height);
+       int (*cursor_set2)(struct drm_crtc *crtc, struct drm_file *file_priv,
+                          uint32_t handle, uint32_t width, uint32_t height,
+                          int32_t hot_x, int32_t hot_y);
        int (*cursor_move)(struct drm_crtc *crtc, int x, int y);
 
        /* Set gamma on the CRTC */
@@ -409,6 +412,10 @@ struct drm_crtc {
        /* framebuffer the connector is currently bound to */
        struct drm_framebuffer *fb;
 
+       /* Temporary tracking of the old fb while a modeset is ongoing. Used
+        * by drm_mode_set_config_internal to implement correct refcounting. */
+       struct drm_framebuffer *old_fb;
+
        bool enabled;
 
        /* Requested mode from modesetting. */
@@ -654,11 +661,7 @@ struct drm_plane_funcs {
  * @format_count: number of formats supported
  * @crtc: currently bound CRTC
  * @fb: currently bound fb
- * @gamma_size: size of gamma table
- * @gamma_store: gamma correction table
- * @enabled: enabled flag
  * @funcs: helper functions
- * @helper_private: storage for drver layer
  * @properties: property tracking for this plane
  */
 struct drm_plane {
@@ -674,14 +677,7 @@ struct drm_plane {
        struct drm_crtc *crtc;
        struct drm_framebuffer *fb;
 
-       /* CRTC gamma size for reporting to userspace */
-       uint32_t gamma_size;
-       uint16_t *gamma_store;
-
-       bool enabled;
-
        const struct drm_plane_funcs *funcs;
-       void *helper_private;
 
        struct drm_object_properties properties;
 };
@@ -894,15 +890,17 @@ extern int drm_plane_init(struct drm_device *dev,
                          const uint32_t *formats, uint32_t format_count,
                          bool priv);
 extern void drm_plane_cleanup(struct drm_plane *plane);
+extern void drm_plane_force_disable(struct drm_plane *plane);
 
 extern void drm_encoder_cleanup(struct drm_encoder *encoder);
 
-extern char *drm_get_connector_name(struct drm_connector *connector);
-extern char *drm_get_dpms_name(int val);
-extern char *drm_get_dvi_i_subconnector_name(int val);
-extern char *drm_get_dvi_i_select_name(int val);
-extern char *drm_get_tv_subconnector_name(int val);
-extern char *drm_get_tv_select_name(int val);
+extern const char *drm_get_connector_name(const struct drm_connector *connector);
+extern const char *drm_get_connector_status_name(enum drm_connector_status status);
+extern const char *drm_get_dpms_name(int val);
+extern const char *drm_get_dvi_i_subconnector_name(int val);
+extern const char *drm_get_dvi_i_select_name(int val);
+extern const char *drm_get_tv_subconnector_name(int val);
+extern const char *drm_get_tv_select_name(int val);
 extern void drm_fb_release(struct drm_file *file_priv);
 extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
 extern bool drm_probe_ddc(struct i2c_adapter *adapter);
@@ -994,7 +992,7 @@ extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats
 extern int drm_mode_create_scaling_mode_property(struct drm_device *dev);
 extern int drm_mode_create_dithering_property(struct drm_device *dev);
 extern int drm_mode_create_dirty_info_property(struct drm_device *dev);
-extern char *drm_get_encoder_name(struct drm_encoder *encoder);
+extern const char *drm_get_encoder_name(const struct drm_encoder *encoder);
 
 extern int drm_mode_connector_attach_encoder(struct drm_connector *connector,
                                             struct drm_encoder *encoder);
@@ -1022,6 +1020,8 @@ extern int drm_mode_setplane(struct drm_device *dev,
                               void *data, struct drm_file *file_priv);
 extern int drm_mode_cursor_ioctl(struct drm_device *dev,
                                void *data, struct drm_file *file_priv);
+extern int drm_mode_cursor2_ioctl(struct drm_device *dev,
+                               void *data, struct drm_file *file_priv);
 extern int drm_mode_addfb(struct drm_device *dev,
                          void *data, struct drm_file *file_priv);
 extern int drm_mode_addfb2(struct drm_device *dev,
@@ -1094,5 +1094,6 @@ extern int drm_format_num_planes(uint32_t format);
 extern int drm_format_plane_cpp(uint32_t format, int plane);
 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);
 
 #endif /* __DRM_CRTC_H__ */
index 0ead502..f5e1168 100644 (file)
  * OTHER DEALINGS IN THE SOFTWARE.
  *
  * Authors: Dave Airlie
+ *          Christian König
  */
 #ifndef DRM_FIXED_H
 #define DRM_FIXED_H
 
+#include <linux/math64.h>
+
 typedef union dfixed {
        u32 full;
 } fixed20_12;
@@ -65,4 +68,95 @@ static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B)
        tmp /= 2;
        return lower_32_bits(tmp);
 }
+
+#define DRM_FIXED_POINT                32
+#define DRM_FIXED_ONE          (1ULL << DRM_FIXED_POINT)
+#define DRM_FIXED_DECIMAL_MASK (DRM_FIXED_ONE - 1)
+#define DRM_FIXED_DIGITS_MASK  (~DRM_FIXED_DECIMAL_MASK)
+
+static inline s64 drm_int2fixp(int a)
+{
+       return ((s64)a) << DRM_FIXED_POINT;
+}
+
+static inline int drm_fixp2int(int64_t a)
+{
+       return ((s64)a) >> DRM_FIXED_POINT;
+}
+
+static inline s64 drm_fixp_msbset(int64_t a)
+{
+       unsigned shift, sign = (a >> 63) & 1;
+
+       for (shift = 62; shift > 0; --shift)
+               if ((a >> shift) != sign)
+                       return shift;
+
+       return 0;
+}
+
+static inline s64 drm_fixp_mul(s64 a, s64 b)
+{
+       unsigned shift = drm_fixp_msbset(a) + drm_fixp_msbset(b);
+       s64 result;
+
+       if (shift > 63) {
+               shift = shift - 63;
+               a >>= shift >> 1;
+               b >>= shift >> 1;
+       } else
+               shift = 0;
+
+       result = a * b;
+
+       if (shift > DRM_FIXED_POINT)
+               return result << (shift - DRM_FIXED_POINT);
+
+       if (shift < DRM_FIXED_POINT)
+               return result >> (DRM_FIXED_POINT - shift);
+
+       return result;
+}
+
+static inline s64 drm_fixp_div(s64 a, s64 b)
+{
+       unsigned shift = 63 - drm_fixp_msbset(a);
+       s64 result;
+
+       a <<= shift;
+
+       if (shift < DRM_FIXED_POINT)
+               b >>= (DRM_FIXED_POINT - shift);
+
+       result = div64_s64(a, b);
+
+       if (shift > DRM_FIXED_POINT)
+               return result >> (shift - DRM_FIXED_POINT);
+
+       return result;
+}
+
+static inline s64 drm_fixp_exp(s64 x)
+{
+       s64 tolerance = div64_s64(DRM_FIXED_ONE, 1000000);
+       s64 sum = DRM_FIXED_ONE, term, y = x;
+       u64 count = 1;
+
+       if (x < 0)
+               y = -1 * x;
+
+       term = y;
+
+       while (term >= tolerance) {
+               sum = sum + term;
+               count = count + 1;
+               term = drm_fixp_mul(term, div64_s64(y, count));
+       }
+
+       if (x < 0)
+               sum = drm_fixp_div(1, sum);
+
+       return sum;
+}
+
 #endif
index 63397ce..c34f27f 100644 (file)
@@ -4,6 +4,9 @@
 struct drm_gem_cma_object {
        struct drm_gem_object base;
        dma_addr_t paddr;
+       struct sg_table *sgt;
+
+       /* For objects with DMA memory allocated by GEM CMA */
        void *vaddr;
 };
 
@@ -45,4 +48,13 @@ extern const struct vm_operations_struct drm_gem_cma_vm_ops;
 void drm_gem_cma_describe(struct drm_gem_cma_object *obj, struct seq_file *m);
 #endif
 
+struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *
+drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size,
+                                 struct sg_table *sgt);
+int drm_gem_cma_prime_mmap(struct drm_gem_object *obj,
+                          struct vm_area_struct *vma);
+void *drm_gem_cma_prime_vmap(struct drm_gem_object *obj);
+void drm_gem_cma_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+
 #endif /* __DRM_GEM_CMA_HELPER_H__ */
index 88591ef..4d06edb 100644 (file)
@@ -177,17 +177,6 @@ static inline struct drm_mm_node *drm_mm_get_block_range(
        return drm_mm_get_block_range_generic(parent, size, alignment, 0,
                                              start, end, 0);
 }
-static inline struct drm_mm_node *drm_mm_get_color_block_range(
-                                               struct drm_mm_node *parent,
-                                               unsigned long size,
-                                               unsigned alignment,
-                                               unsigned long color,
-                                               unsigned long start,
-                                               unsigned long end)
-{
-       return drm_mm_get_block_range_generic(parent, size, alignment, color,
-                                             start, end, 0);
-}
 static inline struct drm_mm_node *drm_mm_get_block_atomic_range(
                                                struct drm_mm_node *parent,
                                                unsigned long size,
@@ -255,29 +244,10 @@ static inline  struct drm_mm_node *drm_mm_search_free_in_range(
        return drm_mm_search_free_in_range_generic(mm, size, alignment, 0,
                                                   start, end, best_match);
 }
-static inline struct drm_mm_node *drm_mm_search_free_color(const struct drm_mm *mm,
-                                                          unsigned long size,
-                                                          unsigned alignment,
-                                                          unsigned long color,
-                                                          bool best_match)
-{
-       return drm_mm_search_free_generic(mm,size, alignment, color, best_match);
-}
-static inline  struct drm_mm_node *drm_mm_search_free_in_range_color(
-                                               const struct drm_mm *mm,
-                                               unsigned long size,
-                                               unsigned alignment,
-                                               unsigned long color,
-                                               unsigned long start,
-                                               unsigned long end,
-                                               bool best_match)
-{
-       return drm_mm_search_free_in_range_generic(mm, size, alignment, color,
-                                                  start, end, best_match);
-}
-extern int drm_mm_init(struct drm_mm *mm,
-                      unsigned long start,
-                      unsigned long size);
+
+extern void drm_mm_init(struct drm_mm *mm,
+                       unsigned long start,
+                       unsigned long size);
 extern void drm_mm_takedown(struct drm_mm *mm);
 extern int drm_mm_clean(struct drm_mm *mm);
 extern int drm_mm_pre_get(struct drm_mm *mm);
index 675ddf4..815fafc 100644 (file)
@@ -65,22 +65,6 @@ struct no_agp_kern {
 #define DRM_AGP_KERN            struct no_agp_kern
 #endif
 
-#if !(__OS_HAS_MTRR)
-static __inline__ int mtrr_add(unsigned long base, unsigned long size,
-                              unsigned int type, char increment)
-{
-       return -ENODEV;
-}
-
-static __inline__ int mtrr_del(int reg, unsigned long base, unsigned long size)
-{
-       return -ENODEV;
-}
-
-#define MTRR_TYPE_WRCOMB     1
-
-#endif
-
 /** Other copying of data to kernel space */
 #define DRM_COPY_FROM_USER(arg1, arg2, arg3)           \
        copy_from_user(arg1, arg2, arg3)
index bb1bc48..34efaf6 100644 (file)
        {0x1002, 0x6621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x6623, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x6631, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x6640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x6641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x6658, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x665c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x665d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x6660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x6663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x6664, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x9808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
        {0x1002, 0x9809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
        {0x1002, 0x980A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x9830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x9831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x9832, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x9833, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x9834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x9835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x9836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x9837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x9838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x9839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x983a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x983b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x983c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x983d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x983e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+       {0x1002, 0x983f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
        {0x1002, 0x9900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
        {0x1002, 0x9901, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
        {0x1002, 0x9903, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h
new file mode 100644 (file)
index 0000000..d128629
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2011-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 DRM_RECT_H
+#define DRM_RECT_H
+
+/**
+ * DOC: rect utils
+ *
+ * Utility functions to help manage rectangular areas for
+ * clipping, scaling, etc. calculations.
+ */
+
+/**
+ * struct drm_rect - two dimensional rectangle
+ * @x1: horizontal starting coordinate (inclusive)
+ * @x2: horizontal ending coordinate (exclusive)
+ * @y1: vertical starting coordinate (inclusive)
+ * @y2: vertical ending coordinate (exclusive)
+ */
+struct drm_rect {
+       int x1, y1, x2, y2;
+};
+
+/**
+ * drm_rect_adjust_size - adjust the size of the rectangle
+ * @r: rectangle to be adjusted
+ * @dw: horizontal adjustment
+ * @dh: vertical adjustment
+ *
+ * Change the size of rectangle @r by @dw in the horizontal direction,
+ * and by @dh in the vertical direction, while keeping the center
+ * of @r stationary.
+ *
+ * Positive @dw and @dh increase the size, negative values decrease it.
+ */
+static inline void drm_rect_adjust_size(struct drm_rect *r, int dw, int dh)
+{
+       r->x1 -= dw >> 1;
+       r->y1 -= dh >> 1;
+       r->x2 += (dw + 1) >> 1;
+       r->y2 += (dh + 1) >> 1;
+}
+
+/**
+ * drm_rect_translate - translate the rectangle
+ * @r: rectangle to be tranlated
+ * @dx: horizontal translation
+ * @dy: vertical translation
+ *
+ * Move rectangle @r by @dx in the horizontal direction,
+ * and by @dy in the vertical direction.
+ */
+static inline void drm_rect_translate(struct drm_rect *r, int dx, int dy)
+{
+       r->x1 += dx;
+       r->y1 += dy;
+       r->x2 += dx;
+       r->y2 += dy;
+}
+
+/**
+ * drm_rect_downscale - downscale a rectangle
+ * @r: rectangle to be downscaled
+ * @horz: horizontal downscale factor
+ * @vert: vertical downscale factor
+ *
+ * Divide the coordinates of rectangle @r by @horz and @vert.
+ */
+static inline void drm_rect_downscale(struct drm_rect *r, int horz, int vert)
+{
+       r->x1 /= horz;
+       r->y1 /= vert;
+       r->x2 /= horz;
+       r->y2 /= vert;
+}
+
+/**
+ * drm_rect_width - determine the rectangle width
+ * @r: rectangle whose width is returned
+ *
+ * RETURNS:
+ * The width of the rectangle.
+ */
+static inline int drm_rect_width(const struct drm_rect *r)
+{
+       return r->x2 - r->x1;
+}
+
+/**
+ * drm_rect_height - determine the rectangle height
+ * @r: rectangle whose height is returned
+ *
+ * RETURNS:
+ * The height of the rectangle.
+ */
+static inline int drm_rect_height(const struct drm_rect *r)
+{
+       return r->y2 - r->y1;
+}
+
+/**
+ * drm_rect_visible - determine if the the rectangle is visible
+ * @r: rectangle whose visibility is returned
+ *
+ * RETURNS:
+ * %true if the rectangle is visible, %false otherwise.
+ */
+static inline bool drm_rect_visible(const struct drm_rect *r)
+{
+       return drm_rect_width(r) > 0 && drm_rect_height(r) > 0;
+}
+
+/**
+ * drm_rect_equals - determine if two rectangles are equal
+ * @r1: first rectangle
+ * @r2: second rectangle
+ *
+ * RETURNS:
+ * %true if the rectangles are equal, %false otherwise.
+ */
+static inline bool drm_rect_equals(const struct drm_rect *r1,
+                                  const struct drm_rect *r2)
+{
+       return r1->x1 == r2->x1 && r1->x2 == r2->x2 &&
+               r1->y1 == r2->y1 && r1->y2 == r2->y2;
+}
+
+bool drm_rect_intersect(struct drm_rect *r, const struct drm_rect *clip);
+bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst,
+                         const struct drm_rect *clip,
+                         int hscale, int vscale);
+int drm_rect_calc_hscale(const struct drm_rect *src,
+                        const struct drm_rect *dst,
+                        int min_hscale, int max_hscale);
+int drm_rect_calc_vscale(const struct drm_rect *src,
+                        const struct drm_rect *dst,
+                        int min_vscale, int max_vscale);
+int drm_rect_calc_hscale_relaxed(struct drm_rect *src,
+                                struct drm_rect *dst,
+                                int min_hscale, int max_hscale);
+int drm_rect_calc_vscale_relaxed(struct drm_rect *src,
+                                struct drm_rect *dst,
+                                int min_vscale, int max_vscale);
+void drm_rect_debug_print(const struct drm_rect *r, bool fixed_point);
+
+#endif
diff --git a/include/drm/i915_powerwell.h b/include/drm/i915_powerwell.h
new file mode 100644 (file)
index 0000000..cfdc884
--- /dev/null
@@ -0,0 +1,36 @@
+/**************************************************************************
+ *
+ * Copyright 2013 Intel Inc.
+ * 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.
+ *
+ *
+ **************************************************************************/
+
+#ifndef _I915_POWERWELL_H_
+#define _I915_POWERWELL_H_
+
+/* For use by hda_i915 driver */
+extern void i915_request_power_well(void);
+extern void i915_release_power_well(void);
+
+#endif                         /* _I915_POWERWELL_H_ */
index 3cb5d84..8a6aa56 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/mm.h>
 #include <linux/rbtree.h>
 #include <linux/bitmap.h>
+#include <linux/reservation.h>
 
 struct ttm_bo_device;
 
@@ -153,7 +154,6 @@ struct ttm_tt;
  * Lru lists may keep one refcount, the delayed delete list, and kref != 0
  * keeps one refcount. When this refcount reaches zero,
  * the object is destroyed.
- * @event_queue: Queue for processes waiting on buffer object status change.
  * @mem: structure describing current placement.
  * @persistent_swap_storage: Usually the swap storage is deleted for buffers
  * pinned in physical memory. If this behaviour is not desired, this member
@@ -164,12 +164,6 @@ struct ttm_tt;
  * @lru: List head for the lru list.
  * @ddestroy: List head for the delayed destroy list.
  * @swap: List head for swap LRU list.
- * @val_seq: Sequence of the validation holding the @reserved lock.
- * Used to avoid starvation when many processes compete to validate the
- * buffer. This member is protected by the bo_device::lru_lock.
- * @seq_valid: The value of @val_seq is valid. This value is protected by
- * the bo_device::lru_lock.
- * @reserved: Deadlock-free lock used for synchronization state transitions.
  * @sync_obj: Pointer to a synchronization object.
  * @priv_flags: Flags describing buffer object internal state.
  * @vm_rb: Rb node for the vm rb tree.
@@ -209,10 +203,9 @@ struct ttm_buffer_object {
 
        struct kref kref;
        struct kref list_kref;
-       wait_queue_head_t event_queue;
 
        /**
-        * Members protected by the bo::reserved lock.
+        * Members protected by the bo::resv::reserved lock.
         */
 
        struct ttm_mem_reg mem;
@@ -234,15 +227,6 @@ struct ttm_buffer_object {
        struct list_head ddestroy;
        struct list_head swap;
        struct list_head io_reserve_lru;
-       uint32_t val_seq;
-       bool seq_valid;
-
-       /**
-        * Members protected by the bdev::lru_lock
-        * only when written to.
-        */
-
-       atomic_t reserved;
 
        /**
         * Members protected by struct buffer_object_device::fence_lock
@@ -272,6 +256,9 @@ struct ttm_buffer_object {
        uint32_t cur_placement;
 
        struct sg_table *sg;
+
+       struct reservation_object *resv;
+       struct reservation_object ttm_resv;
 };
 
 /**
@@ -725,18 +712,4 @@ extern ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
 
 extern void ttm_bo_swapout_all(struct ttm_bo_device *bdev);
 
-/**
- * ttm_bo_is_reserved - return an indication if a ttm buffer object is reserved
- *
- * @bo:     The buffer object to check.
- *
- * This function returns an indication if a bo is reserved or not, and should
- * only be used to print an error when it is not from incorrect api usage, since
- * there's no guarantee that it is the caller that is holding the reservation.
- */
-static inline bool ttm_bo_is_reserved(struct ttm_buffer_object *bo)
-{
-       return atomic_read(&bo->reserved);
-}
-
 #endif
index 9c8dca7..984fc2d 100644 (file)
 #include <ttm/ttm_bo_api.h>
 #include <ttm/ttm_memory.h>
 #include <ttm/ttm_module.h>
+#include <ttm/ttm_placement.h>
 #include <drm/drm_mm.h>
 #include <drm/drm_global.h>
 #include <linux/workqueue.h>
 #include <linux/fs.h>
 #include <linux/spinlock.h>
+#include <linux/reservation.h>
 
 struct ttm_backend_func {
        /**
@@ -771,6 +773,55 @@ extern int ttm_mem_io_lock(struct ttm_mem_type_manager *man,
                           bool interruptible);
 extern void ttm_mem_io_unlock(struct ttm_mem_type_manager *man);
 
+extern void ttm_bo_del_sub_from_lru(struct ttm_buffer_object *bo);
+extern void ttm_bo_add_to_lru(struct ttm_buffer_object *bo);
+
+/**
+ * ttm_bo_reserve_nolru:
+ *
+ * @bo: A pointer to a struct ttm_buffer_object.
+ * @interruptible: Sleep interruptible if waiting.
+ * @no_wait: Don't sleep while trying to reserve, rather return -EBUSY.
+ * @use_ticket: If @bo is already reserved, Only sleep waiting for
+ * it to become unreserved if @ticket->stamp is older.
+ *
+ * Will not remove reserved buffers from the lru lists.
+ * Otherwise identical to ttm_bo_reserve.
+ *
+ * Returns:
+ * -EDEADLK: The reservation may cause a deadlock.
+ * Release all buffer reservations, wait for @bo to become unreserved and
+ * try again. (only if use_sequence == 1).
+ * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
+ * a signal. Release all buffer reservations and return to user-space.
+ * -EBUSY: The function needed to sleep, but @no_wait was true
+ * -EALREADY: Bo already reserved using @ticket. This error code will only
+ * be returned if @use_ticket is set to true.
+ */
+static inline int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo,
+                                      bool interruptible,
+                                      bool no_wait, bool use_ticket,
+                                      struct ww_acquire_ctx *ticket)
+{
+       int ret = 0;
+
+       if (no_wait) {
+               bool success;
+               if (WARN_ON(ticket))
+                       return -EBUSY;
+
+               success = ww_mutex_trylock(&bo->resv->lock);
+               return success ? 0 : -EBUSY;
+       }
+
+       if (interruptible)
+               ret = ww_mutex_lock_interruptible(&bo->resv->lock, ticket);
+       else
+               ret = ww_mutex_lock(&bo->resv->lock, ticket);
+       if (ret == -EINTR)
+               return -ERESTARTSYS;
+       return ret;
+}
 
 /**
  * ttm_bo_reserve:
@@ -778,8 +829,8 @@ extern void ttm_mem_io_unlock(struct ttm_mem_type_manager *man);
  * @bo: A pointer to a struct ttm_buffer_object.
  * @interruptible: Sleep interruptible if waiting.
  * @no_wait: Don't sleep while trying to reserve, rather return -EBUSY.
- * @use_sequence: If @bo is already reserved, Only sleep waiting for
- * it to become unreserved if @sequence < (@bo)->sequence.
+ * @use_ticket: If @bo is already reserved, Only sleep waiting for
+ * it to become unreserved if @ticket->stamp is older.
  *
  * Locks a buffer object for validation. (Or prevents other processes from
  * locking it for validation) and removes it from lru lists, while taking
@@ -793,7 +844,7 @@ extern void ttm_mem_io_unlock(struct ttm_mem_type_manager *man);
  * Processes attempting to reserve multiple buffers other than for eviction,
  * (typically execbuf), should first obtain a unique 32-bit
  * validation sequence number,
- * and call this function with @use_sequence == 1 and @sequence == the unique
+ * and call this function with @use_ticket == 1 and @ticket->stamp == the unique
  * sequence number. If upon call of this function, the buffer object is already
  * reserved, the validation sequence is checked against the validation
  * sequence of the process currently reserving the buffer,
@@ -808,36 +859,31 @@ extern void ttm_mem_io_unlock(struct ttm_mem_type_manager *man);
  * will eventually succeed, preventing both deadlocks and starvation.
  *
  * Returns:
- * -EAGAIN: The reservation may cause a deadlock.
+ * -EDEADLK: The reservation may cause a deadlock.
  * Release all buffer reservations, wait for @bo to become unreserved and
  * try again. (only if use_sequence == 1).
  * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
  * a signal. Release all buffer reservations and return to user-space.
  * -EBUSY: The function needed to sleep, but @no_wait was true
- * -EDEADLK: Bo already reserved using @sequence. This error code will only
- * be returned if @use_sequence is set to true.
+ * -EALREADY: Bo already reserved using @ticket. This error code will only
+ * be returned if @use_ticket is set to true.
  */
-extern int ttm_bo_reserve(struct ttm_buffer_object *bo,
-                         bool interruptible,
-                         bool no_wait, bool use_sequence, uint32_t sequence);
+static inline int ttm_bo_reserve(struct ttm_buffer_object *bo,
+                                bool interruptible,
+                                bool no_wait, bool use_ticket,
+                                struct ww_acquire_ctx *ticket)
+{
+       int ret;
 
-/**
- * ttm_bo_reserve_slowpath_nolru:
- * @bo: A pointer to a struct ttm_buffer_object.
- * @interruptible: Sleep interruptible if waiting.
- * @sequence: Set (@bo)->sequence to this value after lock
- *
- * This is called after ttm_bo_reserve returns -EAGAIN and we backed off
- * from all our other reservations. Because there are no other reservations
- * held by us, this function cannot deadlock any more.
- *
- * Will not remove reserved buffers from the lru lists.
- * Otherwise identical to ttm_bo_reserve_slowpath.
- */
-extern int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo,
-                                        bool interruptible,
-                                        uint32_t sequence);
+       WARN_ON(!atomic_read(&bo->kref.refcount));
 
+       ret = ttm_bo_reserve_nolru(bo, interruptible, no_wait, use_ticket,
+                                   ticket);
+       if (likely(ret == 0))
+               ttm_bo_del_sub_from_lru(bo);
+
+       return ret;
+}
 
 /**
  * ttm_bo_reserve_slowpath:
@@ -849,54 +895,57 @@ extern int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo,
  * from all our other reservations. Because there are no other reservations
  * held by us, this function cannot deadlock any more.
  */
-extern int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo,
-                                  bool interruptible, uint32_t sequence);
+static inline int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo,
+                                         bool interruptible,
+                                         struct ww_acquire_ctx *ticket)
+{
+       int ret = 0;
 
-/**
- * ttm_bo_reserve_nolru:
- *
- * @bo: A pointer to a struct ttm_buffer_object.
- * @interruptible: Sleep interruptible if waiting.
- * @no_wait: Don't sleep while trying to reserve, rather return -EBUSY.
- * @use_sequence: If @bo is already reserved, Only sleep waiting for
- * it to become unreserved if @sequence < (@bo)->sequence.
- *
- * Will not remove reserved buffers from the lru lists.
- * Otherwise identical to ttm_bo_reserve.
- *
- * Returns:
- * -EAGAIN: The reservation may cause a deadlock.
- * Release all buffer reservations, wait for @bo to become unreserved and
- * try again. (only if use_sequence == 1).
- * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
- * a signal. Release all buffer reservations and return to user-space.
- * -EBUSY: The function needed to sleep, but @no_wait was true
- * -EDEADLK: Bo already reserved using @sequence. This error code will only
- * be returned if @use_sequence is set to true.
- */
-extern int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo,
-                                bool interruptible,
-                                bool no_wait, bool use_sequence,
-                                uint32_t sequence);
+       WARN_ON(!atomic_read(&bo->kref.refcount));
+
+       if (interruptible)
+               ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
+                                                      ticket);
+       else
+               ww_mutex_lock_slow(&bo->resv->lock, ticket);
+
+       if (likely(ret == 0))
+               ttm_bo_del_sub_from_lru(bo);
+       else if (ret == -EINTR)
+               ret = -ERESTARTSYS;
+
+       return ret;
+}
 
 /**
- * ttm_bo_unreserve
- *
+ * ttm_bo_unreserve_ticket
  * @bo: A pointer to a struct ttm_buffer_object.
+ * @ticket: ww_acquire_ctx used for reserving
  *
- * Unreserve a previous reservation of @bo.
+ * Unreserve a previous reservation of @bo made with @ticket.
  */
-extern void ttm_bo_unreserve(struct ttm_buffer_object *bo);
+static inline void ttm_bo_unreserve_ticket(struct ttm_buffer_object *bo,
+                                          struct ww_acquire_ctx *t)
+{
+       if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) {
+               spin_lock(&bo->glob->lru_lock);
+               ttm_bo_add_to_lru(bo);
+               spin_unlock(&bo->glob->lru_lock);
+       }
+       ww_mutex_unlock(&bo->resv->lock);
+}
 
 /**
- * ttm_bo_unreserve_locked
+ * ttm_bo_unreserve
  *
  * @bo: A pointer to a struct ttm_buffer_object.
  *
  * Unreserve a previous reservation of @bo.
- * Needs to be called with struct ttm_bo_global::lru_lock held.
  */
-extern void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo);
+static inline void ttm_bo_unreserve(struct ttm_buffer_object *bo)
+{
+       ttm_bo_unreserve_ticket(bo, NULL);
+}
 
 /*
  * ttm_bo_util.c
index 547e19f..ec8a1d3 100644 (file)
@@ -57,17 +57,20 @@ struct ttm_validate_buffer {
 /**
  * function ttm_eu_backoff_reservation
  *
+ * @ticket:   ww_acquire_ctx from reserve call
  * @list:     thread private list of ttm_validate_buffer structs.
  *
  * Undoes all buffer validation reservations for bos pointed to by
  * the list entries.
  */
 
-extern void ttm_eu_backoff_reservation(struct list_head *list);
+extern void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket,
+                                      struct list_head *list);
 
 /**
  * function ttm_eu_reserve_buffers
  *
+ * @ticket:  [out] ww_acquire_ctx returned by call.
  * @list:    thread private list of ttm_validate_buffer structs.
  *
  * Tries to reserve bos pointed to by the list entries for validation.
@@ -90,11 +93,13 @@ extern void ttm_eu_backoff_reservation(struct list_head *list);
  * has failed.
  */
 
-extern int ttm_eu_reserve_buffers(struct list_head *list);
+extern int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
+                                 struct list_head *list);
 
 /**
  * function ttm_eu_fence_buffer_objects.
  *
+ * @ticket:      ww_acquire_ctx from reserve call
  * @list:        thread private list of ttm_validate_buffer structs.
  * @sync_obj:    The new sync object for the buffers.
  *
@@ -104,6 +109,7 @@ extern int ttm_eu_reserve_buffers(struct list_head *list);
  *
  */
 
-extern void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj);
+extern void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,
+                                       struct list_head *list, void *sync_obj);
 
 #endif
index b20b038..729a4d1 100644 (file)
@@ -103,8 +103,11 @@ extern void __audit_syscall_exit(int ret_success, long ret_value);
 extern struct filename *__audit_reusename(const __user char *uptr);
 extern void __audit_getname(struct filename *name);
 extern void audit_putname(struct filename *name);
+
+#define AUDIT_INODE_PARENT     1       /* dentry represents the parent */
+#define AUDIT_INODE_HIDDEN     2       /* audit record should be hidden */
 extern void __audit_inode(struct filename *name, const struct dentry *dentry,
-                               unsigned int parent);
+                               unsigned int flags);
 extern void __audit_inode_child(const struct inode *parent,
                                const struct dentry *dentry,
                                const unsigned char type);
@@ -148,10 +151,22 @@ static inline void audit_getname(struct filename *name)
        if (unlikely(!audit_dummy_context()))
                __audit_getname(name);
 }
-static inline void audit_inode(struct filename *name, const struct dentry *dentry,
+static inline void audit_inode(struct filename *name,
+                               const struct dentry *dentry,
                                unsigned int parent) {
+       if (unlikely(!audit_dummy_context())) {
+               unsigned int flags = 0;
+               if (parent)
+                       flags |= AUDIT_INODE_PARENT;
+               __audit_inode(name, dentry, flags);
+       }
+}
+static inline void audit_inode_parent_hidden(struct filename *name,
+                                               const struct dentry *dentry)
+{
        if (unlikely(!audit_dummy_context()))
-               __audit_inode(name, dentry, parent);
+               __audit_inode(name, dentry,
+                               AUDIT_INODE_PARENT | AUDIT_INODE_HIDDEN);
 }
 static inline void audit_inode_child(const struct inode *parent,
                                     const struct dentry *dentry,
@@ -311,7 +326,7 @@ static inline void audit_putname(struct filename *name)
 { }
 static inline void __audit_inode(struct filename *name,
                                        const struct dentry *dentry,
-                                       unsigned int parent)
+                                       unsigned int flags)
 { }
 static inline void __audit_inode_child(const struct inode *parent,
                                        const struct dentry *dentry,
@@ -321,6 +336,9 @@ static inline void audit_inode(struct filename *name,
                                const struct dentry *dentry,
                                unsigned int parent)
 { }
+static inline void audit_inode_parent_hidden(struct filename *name,
+                               const struct dentry *dentry)
+{ }
 static inline void audit_inode_child(const struct inode *parent,
                                     const struct dentry *dentry,
                                     const unsigned char type)
index 2e34db8..622fc50 100644 (file)
@@ -144,6 +144,7 @@ struct bcma_host_ops {
 
 /* Chip IDs of PCIe devices */
 #define BCMA_CHIP_ID_BCM4313   0x4313
+#define BCMA_CHIP_ID_BCM43142  43142
 #define BCMA_CHIP_ID_BCM43224  43224
 #define  BCMA_PKG_ID_BCM43224_FAB_CSM  0x8
 #define  BCMA_PKG_ID_BCM43224_FAB_SMIC 0xa
index b8b09ea..c49e1a1 100644 (file)
 #define BCMA_CC_PMU_CAP                        0x0604 /* PMU capabilities */
 #define  BCMA_CC_PMU_CAP_REVISION      0x000000FF /* Revision mask */
 #define BCMA_CC_PMU_STAT               0x0608 /* PMU status */
+#define  BCMA_CC_PMU_STAT_EXT_LPO_AVAIL        0x00000100
+#define  BCMA_CC_PMU_STAT_WDRESET      0x00000080
 #define  BCMA_CC_PMU_STAT_INTPEND      0x00000040 /* Interrupt pending */
 #define  BCMA_CC_PMU_STAT_SBCLKST      0x00000030 /* Backplane clock status? */
 #define  BCMA_CC_PMU_STAT_HAVEALP      0x00000008 /* ALP available */
 #define BCMA_CC_REGCTL_DATA            0x065C
 #define BCMA_CC_PLLCTL_ADDR            0x0660
 #define BCMA_CC_PLLCTL_DATA            0x0664
+#define BCMA_CC_PMU_STRAPOPT           0x0668 /* (corerev >= 28) */
+#define BCMA_CC_PMU_XTAL_FREQ          0x066C /* (pmurev >= 10) */
+#define  BCMA_CC_PMU_XTAL_FREQ_ILPCTL_MASK     0x00001FFF
+#define  BCMA_CC_PMU_XTAL_FREQ_MEASURE_MASK    0x80000000
+#define  BCMA_CC_PMU_XTAL_FREQ_MEASURE_SHIFT   31
 #define BCMA_CC_SPROM                  0x0800 /* SPROM beginning */
 /* NAND flash MLC controller registers (corerev >= 38) */
 #define BCMA_CC_NAND_REVISION          0x0C00
 #define  BCMA_CC_PMU6_4706_PROC_NDIV_MODE_MASK 0x00000007
 #define  BCMA_CC_PMU6_4706_PROC_NDIV_MODE_SHIFT        0
 
+/* PMU rev 15 */
+#define BCMA_CC_PMU15_PLL_PLLCTL0      0
+#define  BCMA_CC_PMU15_PLL_PC0_CLKSEL_MASK     0x00000003
+#define  BCMA_CC_PMU15_PLL_PC0_CLKSEL_SHIFT    0
+#define  BCMA_CC_PMU15_PLL_PC0_FREQTGT_MASK    0x003FFFFC
+#define  BCMA_CC_PMU15_PLL_PC0_FREQTGT_SHIFT   2
+#define  BCMA_CC_PMU15_PLL_PC0_PRESCALE_MASK   0x00C00000
+#define  BCMA_CC_PMU15_PLL_PC0_PRESCALE_SHIFT  22
+#define  BCMA_CC_PMU15_PLL_PC0_KPCTRL_MASK     0x07000000
+#define  BCMA_CC_PMU15_PLL_PC0_KPCTRL_SHIFT    24
+#define  BCMA_CC_PMU15_PLL_PC0_FCNTCTRL_MASK   0x38000000
+#define  BCMA_CC_PMU15_PLL_PC0_FCNTCTRL_SHIFT  27
+#define  BCMA_CC_PMU15_PLL_PC0_FDCMODE_MASK    0x40000000
+#define  BCMA_CC_PMU15_PLL_PC0_FDCMODE_SHIFT   30
+#define  BCMA_CC_PMU15_PLL_PC0_CTRLBIAS_MASK   0x80000000
+#define  BCMA_CC_PMU15_PLL_PC0_CTRLBIAS_SHIFT  31
+
 /* ALP clock on pre-PMU chips */
 #define BCMA_CC_PMU_ALP_CLOCK          20000000
 /* HT clock for systems with PMU-enabled chipcommon */
 #define BCMA_CHIPCTL_5357_I2S_PINS_ENABLE      BIT(18)
 #define BCMA_CHIPCTL_5357_I2CSPI_PINS_ENABLE   BIT(19)
 
+#define BCMA_RES_4314_LPLDO_PU                 BIT(0)
+#define BCMA_RES_4314_PMU_SLEEP_DIS            BIT(1)
+#define BCMA_RES_4314_PMU_BG_PU                        BIT(2)
+#define BCMA_RES_4314_CBUCK_LPOM_PU            BIT(3)
+#define BCMA_RES_4314_CBUCK_PFM_PU             BIT(4)
+#define BCMA_RES_4314_CLDO_PU                  BIT(5)
+#define BCMA_RES_4314_LPLDO2_LVM               BIT(6)
+#define BCMA_RES_4314_WL_PMU_PU                        BIT(7)
+#define BCMA_RES_4314_LNLDO_PU                 BIT(8)
+#define BCMA_RES_4314_LDO3P3_PU                        BIT(9)
+#define BCMA_RES_4314_OTP_PU                   BIT(10)
+#define BCMA_RES_4314_XTAL_PU                  BIT(11)
+#define BCMA_RES_4314_WL_PWRSW_PU              BIT(12)
+#define BCMA_RES_4314_LQ_AVAIL                 BIT(13)
+#define BCMA_RES_4314_LOGIC_RET                        BIT(14)
+#define BCMA_RES_4314_MEM_SLEEP                        BIT(15)
+#define BCMA_RES_4314_MACPHY_RET               BIT(16)
+#define BCMA_RES_4314_WL_CORE_READY            BIT(17)
+#define BCMA_RES_4314_ILP_REQ                  BIT(18)
+#define BCMA_RES_4314_ALP_AVAIL                        BIT(19)
+#define BCMA_RES_4314_MISC_PWRSW_PU            BIT(20)
+#define BCMA_RES_4314_SYNTH_PWRSW_PU           BIT(21)
+#define BCMA_RES_4314_RX_PWRSW_PU              BIT(22)
+#define BCMA_RES_4314_RADIO_PU                 BIT(23)
+#define BCMA_RES_4314_VCO_LDO_PU               BIT(24)
+#define BCMA_RES_4314_AFE_LDO_PU               BIT(25)
+#define BCMA_RES_4314_RX_LDO_PU                        BIT(26)
+#define BCMA_RES_4314_TX_LDO_PU                        BIT(27)
+#define BCMA_RES_4314_HT_AVAIL                 BIT(28)
+#define BCMA_RES_4314_MACPHY_CLK_AVAIL         BIT(29)
+
 /* Data for the PMU, if available.
  * Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU)
  */
diff --git a/include/linux/can/platform/flexcan.h b/include/linux/can/platform/flexcan.h
deleted file mode 100644 (file)
index 72b713a..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2010 Marc Kleine-Budde <kernel@pengutronix.de>
- *
- * This file is released under the GPLv2
- *
- */
-
-#ifndef __CAN_PLATFORM_FLEXCAN_H
-#define __CAN_PLATFORM_FLEXCAN_H
-
-/**
- * struct flexcan_platform_data - flex CAN controller platform data
- * @transceiver_enable:         - called to power on/off the transceiver
- *
- */
-struct flexcan_platform_data {
-       void (*transceiver_switch)(int enable);
-};
-
-#endif /* __CAN_PLATFORM_FLEXCAN_H */
index 379f715..0442c3d 100644 (file)
@@ -160,11 +160,6 @@ static inline void ceph_decode_timespec(struct timespec *ts,
 static inline void ceph_encode_timespec(struct ceph_timespec *tv,
                                        const struct timespec *ts)
 {
-       BUG_ON(ts->tv_sec < 0);
-       BUG_ON(ts->tv_sec > (__kernel_time_t)U32_MAX);
-       BUG_ON(ts->tv_nsec < 0);
-       BUG_ON(ts->tv_nsec > (long)U32_MAX);
-
        tv->tv_sec = cpu_to_le32((u32)ts->tv_sec);
        tv->tv_nsec = cpu_to_le32((u32)ts->tv_nsec);
 }
index 186db0b..ce6df39 100644 (file)
@@ -145,7 +145,6 @@ struct ceph_osd_request {
        s32               r_reply_op_result[CEPH_OSD_MAX_OP];
        int               r_got_reply;
        int               r_linger;
-       int               r_completed;
 
        struct ceph_osd_client *r_osdc;
        struct kref       r_kref;
index f42dbe1..3092df3 100644 (file)
@@ -324,6 +324,11 @@ static inline int __d_rcu_to_refcount(struct dentry *dentry, unsigned seq)
        return ret;
 }
 
+static inline unsigned d_count(struct dentry *dentry)
+{
+       return dentry->d_count;
+}
+
 /* validate "insecure" dentry pointer */
 extern int d_validate(struct dentry *, struct dentry *);
 
diff --git a/include/linux/decompress/unlz4.h b/include/linux/decompress/unlz4.h
new file mode 100644 (file)
index 0000000..d5b68bf
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef DECOMPRESS_UNLZ4_H
+#define DECOMPRESS_UNLZ4_H
+
+int unlz4(unsigned char *inbuf, int len,
+       int(*fill)(void*, unsigned int),
+       int(*flush)(void*, unsigned int),
+       unsigned char *output,
+       int *pos,
+       void(*error)(char *x));
+#endif
index d49c60f..ffac70a 100644 (file)
@@ -624,7 +624,7 @@ extern void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u3
 extern void fb_set_suspend(struct fb_info *info, int state);
 extern int fb_get_color_depth(struct fb_var_screeninfo *var,
                              struct fb_fix_screeninfo *fix);
-extern int fb_get_options(char *name, char **option);
+extern int fb_get_options(const char *name, char **option);
 extern int fb_new_modelist(struct fb_info *info);
 
 extern struct fb_info *registered_fb[FB_MAX];
index f65f5a6..a6ac848 100644 (file)
@@ -59,10 +59,10 @@ extern void bpf_jit_free(struct sk_filter *fp);
 static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen,
                                u32 pass, void *image)
 {
-       pr_err("flen=%u proglen=%u pass=%u image=%p\n",
+       pr_err("flen=%u proglen=%u pass=%u image=%pK\n",
               flen, proglen, pass, image);
        if (image)
-               print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_ADDRESS,
+               print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_OFFSET,
                               16, 1, image, proglen, false);
 }
 #define SK_RUN_FILTER(FILTER, SKB) (*FILTER->bpf_func)(SKB, FILTER->insns)
index 99be011..a35b10e 100644 (file)
@@ -372,8 +372,8 @@ struct address_space_operations {
        int (*get_xip_mem)(struct address_space *, pgoff_t, int,
                                                void **, unsigned long *);
        /*
-        * migrate the contents of a page to the specified target. If sync
-        * is false, it must not block.
+        * migrate the contents of a page to the specified target. If
+        * migrate_mode is MIGRATE_ASYNC, it must not block.
         */
        int (*migratepage) (struct address_space *,
                        struct page *, struct page *, enum migrate_mode);
@@ -954,6 +954,7 @@ struct file_lock {
        unsigned int fl_flags;
        unsigned char fl_type;
        unsigned int fl_pid;
+       int fl_link_cpu;                /* what cpu's list is this on? */
        struct pid *fl_nspid;
        wait_queue_head_t fl_wait;
        struct file *fl_file;
index 0f615eb..9b4dd49 100644 (file)
@@ -209,7 +209,7 @@ static inline int allocflags_to_migratetype(gfp_t gfp_flags)
  *       0x9    => DMA or NORMAL (MOVABLE+DMA)
  *       0xa    => MOVABLE (Movable is valid only if HIGHMEM is set too)
  *       0xb    => BAD (MOVABLE+HIGHMEM+DMA)
- *       0xc    => DMA32 (MOVABLE+HIGHMEM+DMA32)
+ *       0xc    => DMA32 (MOVABLE+DMA32)
  *       0xd    => BAD (MOVABLE+DMA32+DMA)
  *       0xe    => BAD (MOVABLE+DMA32+HIGHMEM)
  *       0xf    => BAD (MOVABLE+DMA32+HIGHMEM+DMA)
index 06b0ed0..b0dc87a 100644 (file)
@@ -146,6 +146,7 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
 #define IEEE80211_MAX_RTS_THRESHOLD    2353
 #define IEEE80211_MAX_AID              2007
 #define IEEE80211_MAX_TIM_LEN          251
+#define IEEE80211_MAX_MESH_PEERINGS    63
 /* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
    6.2.1.1.2.
 
@@ -1829,6 +1830,15 @@ enum ieee80211_key_len {
        WLAN_KEY_LEN_AES_CMAC = 16,
 };
 
+#define IEEE80211_WEP_IV_LEN           4
+#define IEEE80211_WEP_ICV_LEN          4
+#define IEEE80211_CCMP_HDR_LEN         8
+#define IEEE80211_CCMP_MIC_LEN         8
+#define IEEE80211_CCMP_PN_LEN          6
+#define IEEE80211_TKIP_IV_LEN          8
+#define IEEE80211_TKIP_ICV_LEN         4
+#define IEEE80211_CMAC_PN_LEN          6
+
 /* Public action codes */
 enum ieee80211_pub_actioncode {
        WLAN_PUB_ACTION_EXT_CHANSW_ANN = 4,
index c3f817c..a86784d 100644 (file)
@@ -12,5 +12,6 @@ struct ifla_vf_info {
        __u32 qos;
        __u32 tx_rate;
        __u32 spoofchk;
+       __u32 linkstate;
 };
 #endif /* _LINUX_IF_LINK_H */
index 84dde1d..ddd33fd 100644 (file)
@@ -8,7 +8,7 @@
 #include <net/netlink.h>
 #include <linux/u64_stats_sync.h>
 
-#if defined(CONFIG_MACVTAP) || defined(CONFIG_MACVTAP_MODULE)
+#if IS_ENABLED(CONFIG_MACVTAP)
 struct socket *macvtap_get_socket(struct file *);
 #else
 #include <linux/err.h>
@@ -50,7 +50,7 @@ struct macvlan_pcpu_stats {
  * Maximum times a macvtap device can be opened. This can be used to
  * configure the number of receive queue, e.g. for multiqueue virtio.
  */
-#define MAX_MACVTAP_QUEUES     (NR_CPUS < 16 ? NR_CPUS : 16)
+#define MAX_MACVTAP_QUEUES     16
 
 #define MACVLAN_MC_FILTER_BITS 8
 #define MACVLAN_MC_FILTER_SZ   (1 << MACVLAN_MC_FILTER_BITS)
@@ -65,12 +65,18 @@ struct macvlan_dev {
 
        DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ);
 
+       netdev_features_t       set_features;
        enum macvlan_mode       mode;
        u16                     flags;
        int (*receive)(struct sk_buff *skb);
        int (*forward)(struct net_device *dev, struct sk_buff *skb);
-       struct macvtap_queue    *taps[MAX_MACVTAP_QUEUES];
+       /* This array tracks active taps. */
+       struct macvtap_queue    __rcu *taps[MAX_MACVTAP_QUEUES];
+       /* This list tracks all taps (both enabled and disabled) */
+       struct list_head        queue_list;
        int                     numvtaps;
+       int                     numqueues;
+       netdev_features_t       tap_features;
        int                     minor;
 };
 
index 16fae64..f6156f9 100644 (file)
@@ -69,6 +69,7 @@ struct team_port {
        s32 priority; /* lower number ~ higher priority */
        u16 queue_id;
        struct list_head qom_list; /* node in queue override mapping list */
+       struct rcu_head rcu;
        long mode_priv[0];
 };
 
@@ -228,6 +229,16 @@ static inline struct team_port *team_get_port_by_index(struct team *team,
                        return port;
        return NULL;
 }
+
+static inline int team_num_to_port_index(struct team *team, int num)
+{
+       int en_port_count = ACCESS_ONCE(team->en_port_count);
+
+       if (unlikely(!en_port_count))
+               return 0;
+       return num % en_port_count;
+}
+
 static inline struct team_port *team_get_port_by_index_rcu(struct team *team,
                                                           int port_index)
 {
index 637fa71..cdcbafa 100644 (file)
@@ -243,8 +243,6 @@ static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb,
        return skb;
 }
 
-#define HAVE_VLAN_PUT_TAG
-
 /**
  * vlan_put_tag - inserts VLAN tag according to device features
  * @skb: skbuff to tag
index 7f2bf15..e3362b5 100644 (file)
@@ -84,6 +84,7 @@ struct ip_mc_list {
                struct ip_mc_list *next;
                struct ip_mc_list __rcu *next_rcu;
        };
+       struct ip_mc_list __rcu *next_hash;
        struct timer_list       timer;
        int                     users;
        atomic_t                refcnt;
index ea1e3b8..b99cd23 100644 (file)
@@ -50,12 +50,17 @@ struct ipv4_devconf {
        DECLARE_BITMAP(state, IPV4_DEVCONF_MAX);
 };
 
+#define MC_HASH_SZ_LOG 9
+
 struct in_device {
        struct net_device       *dev;
        atomic_t                refcnt;
        int                     dead;
        struct in_ifaddr        *ifa_list;      /* IP ifaddr chain              */
+
        struct ip_mc_list __rcu *mc_list;       /* IP multicast filter chain    */
+       struct ip_mc_list __rcu * __rcu *mc_hash;
+
        int                     mc_count;       /* Number of installed mcasts   */
        spinlock_t              mc_tomb_lock;
        struct ip_mc_list       *mc_tomb;
index 069e407..f4f42fa 100644 (file)
@@ -76,4 +76,29 @@ void devm_ioremap_release(struct device *dev, void *res);
 #define arch_has_dev_port()     (1)
 #endif
 
+/*
+ * Some systems (x86 without PAT) have a somewhat reliable way to mark a
+ * physical address range such that uncached mappings will actually
+ * end up write-combining.  This facility should be used in conjunction
+ * with pgprot_writecombine, ioremap-wc, or set_memory_wc, since it has
+ * no effect if the per-page mechanisms are functional.
+ * (On x86 without PAT, these functions manipulate MTRRs.)
+ *
+ * arch_phys_del_wc(0) or arch_phys_del_wc(any error code) is guaranteed
+ * to have no effect.
+ */
+#ifndef arch_phys_wc_add
+static inline int __must_check arch_phys_wc_add(unsigned long base,
+                                               unsigned long size)
+{
+       return 0;  /* It worked (i.e. did nothing). */
+}
+
+static inline void arch_phys_wc_del(int handle)
+{
+}
+
+#define arch_phys_wc_add arch_phys_wc_add
+#endif
+
 #endif /* _LINUX_IO_H */
index 8fb8edf..97ba4e7 100644 (file)
@@ -139,6 +139,10 @@ static inline u64 get_jiffies_64(void)
         ((__s64)(a) - (__s64)(b) >= 0))
 #define time_before_eq64(a,b)  time_after_eq64(b,a)
 
+#define time_in_range64(a, b, c) \
+       (time_after_eq64(a, b) && \
+        time_before_eq64(a, c))
+
 /*
  * These four macros compare jiffies and 'a' for convenience.
  */
index fc66b30..debf208 100644 (file)
@@ -324,6 +324,11 @@ static inline ktime_t ktime_add_us(const ktime_t kt, const u64 usec)
        return ktime_add_ns(kt, usec * NSEC_PER_USEC);
 }
 
+static inline ktime_t ktime_add_ms(const ktime_t kt, const u64 msec)
+{
+       return ktime_add_ns(kt, msec * NSEC_PER_MSEC);
+}
+
 static inline ktime_t ktime_sub_us(const ktime_t kt, const u64 usec)
 {
        return ktime_sub_ns(kt, usec * NSEC_PER_USEC);
@@ -368,7 +373,15 @@ extern void ktime_get_ts(struct timespec *ts);
 static inline ktime_t ns_to_ktime(u64 ns)
 {
        static const ktime_t ktime_zero = { .tv64 = 0 };
+
        return ktime_add_ns(ktime_zero, ns);
 }
 
+static inline ktime_t ms_to_ktime(u64 ms)
+{
+       static const ktime_t ktime_zero = { .tv64 = 0 };
+
+       return ktime_add_ms(ktime_zero, ms);
+}
+
 #endif
diff --git a/include/linux/lz4.h b/include/linux/lz4.h
new file mode 100644 (file)
index 0000000..d21c13f
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef __LZ4_H__
+#define __LZ4_H__
+/*
+ * LZ4 Kernel Interface
+ *
+ * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.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.
+ */
+#define LZ4_MEM_COMPRESS       (4096 * sizeof(unsigned char *))
+#define LZ4HC_MEM_COMPRESS     (65538 * sizeof(unsigned char *))
+
+/*
+ * lz4_compressbound()
+ * Provides the maximum size that LZ4 may output in a "worst case" scenario
+ * (input data not compressible)
+ */
+static inline size_t lz4_compressbound(size_t isize)
+{
+       return isize + (isize / 255) + 16;
+}
+
+/*
+ * lz4_compress()
+ *     src     : source address of the original data
+ *     src_len : size of the original data
+ *     dst     : output buffer address of the compressed data
+ *             This requires 'dst' of size LZ4_COMPRESSBOUND.
+ *     dst_len : is the output size, which is returned after compress done
+ *     workmem : address of the working memory.
+ *             This requires 'workmem' of size LZ4_MEM_COMPRESS.
+ *     return  : Success if return 0
+ *               Error if return (< 0)
+ *     note :  Destination buffer and workmem must be already allocated with
+ *             the defined size.
+ */
+int lz4_compress(const unsigned char *src, size_t src_len,
+               unsigned char *dst, size_t *dst_len, void *wrkmem);
+
+ /*
+  * lz4hc_compress()
+  *     src     : source address of the original data
+  *     src_len : size of the original data
+  *     dst     : output buffer address of the compressed data
+  *            This requires 'dst' of size LZ4_COMPRESSBOUND.
+  *     dst_len : is the output size, which is returned after compress done
+  *     workmem : address of the working memory.
+  *            This requires 'workmem' of size LZ4HC_MEM_COMPRESS.
+  *     return  : Success if return 0
+  *               Error if return (< 0)
+  *     note :  Destination buffer and workmem must be already allocated with
+  *             the defined size.
+  */
+int lz4hc_compress(const unsigned char *src, size_t src_len,
+               unsigned char *dst, size_t *dst_len, void *wrkmem);
+
+/*
+ * lz4_decompress()
+ *     src     : source address of the compressed data
+ *     src_len : is the input size, whcih is returned after decompress done
+ *     dest    : output buffer address of the decompressed data
+ *     actual_dest_len: is the size of uncompressed data, supposing it's known
+ *     return  : Success if return 0
+ *               Error if return (< 0)
+ *     note :  Destination buffer must be already allocated.
+ *             slightly faster than lz4_decompress_unknownoutputsize()
+ */
+int lz4_decompress(const char *src, size_t *src_len, char *dest,
+               size_t actual_dest_len);
+
+/*
+ * lz4_decompress_unknownoutputsize()
+ *     src     : source address of the compressed data
+ *     src_len : is the input size, therefore the compressed size
+ *     dest    : output buffer address of the decompressed data
+ *     dest_len: is the max size of the destination buffer, which is
+ *                     returned with actual size of decompressed data after
+ *                     decompress done
+ *     return  : Success if return 0
+ *               Error if return (< 0)
+ *     note :  Destination buffer must be already allocated.
+ */
+int lz4_decompress_unknownoutputsize(const char *src, size_t src_len,
+               char *dest, size_t *dest_len);
+#endif
index dd3c34e..8e9a029 100644 (file)
@@ -14,6 +14,8 @@
 #define MARVELL_PHY_ID_88E1149R                0x01410e50
 #define MARVELL_PHY_ID_88E1240         0x01410e30
 #define MARVELL_PHY_ID_88E1318S                0x01410e90
+#define MARVELL_PHY_ID_88E1116R                0x01410e40
+#define MARVELL_PHY_ID_88E1510         0x01410dd0
 
 /* struct phy_device dev_flags definitions */
 #define MARVELL_PHY_M1145_FLAGS_RESISTANCE     0x00000001
index adf6e06..bb1c809 100644 (file)
@@ -111,6 +111,7 @@ enum {
        MLX4_CMD_INIT2INIT_QP    = 0x2d,
        MLX4_CMD_SUSPEND_QP      = 0x32,
        MLX4_CMD_UNSUSPEND_QP    = 0x33,
+       MLX4_CMD_UPDATE_QP       = 0x61,
        /* special QP and management commands */
        MLX4_CMD_CONF_SPECIAL_QP = 0x23,
        MLX4_CMD_MAD_IFC         = 0x24,
@@ -237,7 +238,7 @@ int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac);
 int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos);
 int mlx4_set_vf_spoofchk(struct mlx4_dev *dev, int port, int vf, bool setting);
 int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_info *ivf);
-
+int mlx4_set_vf_link_state(struct mlx4_dev *dev, int port, int vf, int link_state);
 
 #define MLX4_COMM_GET_IF_REV(cmd_chan_ver) (u8)((cmd_chan_ver) >> 8)
 
index a51b013..52c23a8 100644 (file)
@@ -157,7 +157,8 @@ enum {
        MLX4_DEV_CAP_FLAGS2_REASSIGN_MAC_EN     = 1LL <<  4,
        MLX4_DEV_CAP_FLAG2_TS                   = 1LL <<  5,
        MLX4_DEV_CAP_FLAG2_VLAN_CONTROL         = 1LL <<  6,
-       MLX4_DEV_CAP_FLAG2_FSM                  = 1LL <<  7
+       MLX4_DEV_CAP_FLAG2_FSM                  = 1LL <<  7,
+       MLX4_DEV_CAP_FLAG2_UPDATE_QP            = 1LL <<  8
 };
 
 enum {
index 352eec9..262deac 100644 (file)
@@ -152,6 +152,8 @@ enum { /* fl */
 };
 enum { /* vlan_control */
        MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED      = 1 << 6,
+       MLX4_VLAN_CTRL_ETH_TX_BLOCK_PRIO_TAGGED = 1 << 5, /* 802.1p priority tag */
+       MLX4_VLAN_CTRL_ETH_TX_BLOCK_UNTAGGED    = 1 << 4,
        MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED      = 1 << 2,
        MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED = 1 << 1, /* 802.1p priority tag */
        MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED    = 1 << 0
@@ -206,6 +208,40 @@ struct mlx4_qp_context {
        u32                     reserved5[10];
 };
 
+struct mlx4_update_qp_context {
+       __be64                  qp_mask;
+       __be64                  primary_addr_path_mask;
+       __be64                  secondary_addr_path_mask;
+       u64                     reserved1;
+       struct mlx4_qp_context  qp_context;
+       u64                     reserved2[58];
+};
+
+enum {
+       MLX4_UPD_QP_MASK_PM_STATE       = 32,
+       MLX4_UPD_QP_MASK_VSD            = 33,
+};
+
+enum {
+       MLX4_UPD_QP_PATH_MASK_PKEY_INDEX                = 0 + 32,
+       MLX4_UPD_QP_PATH_MASK_FSM                       = 1 + 32,
+       MLX4_UPD_QP_PATH_MASK_MAC_INDEX                 = 2 + 32,
+       MLX4_UPD_QP_PATH_MASK_FVL                       = 3 + 32,
+       MLX4_UPD_QP_PATH_MASK_CV                        = 4 + 32,
+       MLX4_UPD_QP_PATH_MASK_VLAN_INDEX                = 5 + 32,
+       MLX4_UPD_QP_PATH_MASK_ETH_HIDE_CQE_VLAN         = 6 + 32,
+       MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_UNTAGGED     = 7 + 32,
+       MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_1P           = 8 + 32,
+       MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_TAGGED       = 9 + 32,
+       MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_UNTAGGED     = 10 + 32,
+       MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_1P           = 11 + 32,
+       MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_TAGGED       = 12 + 32,
+       MLX4_UPD_QP_PATH_MASK_FEUP                      = 13 + 32,
+       MLX4_UPD_QP_PATH_MASK_SCHED_QUEUE               = 14 + 32,
+       MLX4_UPD_QP_PATH_MASK_IF_COUNTER_INDEX          = 15 + 32,
+       MLX4_UPD_QP_PATH_MASK_FVL_RX                    = 16 + 32,
+};
+
 enum { /* param3 */
        MLX4_STRIP_VLAN = 1 << 30
 };
index b87681a..f022460 100644 (file)
@@ -151,12 +151,6 @@ extern unsigned int kobjsize(const void *objp);
 #define VM_STACK_FLAGS (VM_GROWSDOWN | VM_STACK_DEFAULT_FLAGS | VM_ACCOUNT)
 #endif
 
-#define VM_READHINTMASK                        (VM_SEQ_READ | VM_RAND_READ)
-#define VM_ClearReadHint(v)            (v)->vm_flags &= ~VM_READHINTMASK
-#define VM_NormalReadHint(v)           (!((v)->vm_flags & VM_READHINTMASK))
-#define VM_SequentialReadHint(v)       ((v)->vm_flags & VM_SEQ_READ)
-#define VM_RandomReadHint(v)           ((v)->vm_flags & VM_RAND_READ)
-
 /*
  * Special vmas that are non-mergable, non-mlock()able.
  * Note: mm/huge_memory.c VM_NO_THP depends on this definition.
index ae19af5..af4a3b7 100644 (file)
@@ -869,11 +869,6 @@ static inline int is_highmem_idx(enum zone_type idx)
 #endif
 }
 
-static inline int is_normal_idx(enum zone_type idx)
-{
-       return (idx == ZONE_NORMAL);
-}
-
 /**
  * is_highmem - helper function to quickly check if a struct zone is a 
  *              highmem zone or not.  This is an attempt to keep references
@@ -892,29 +887,6 @@ static inline int is_highmem(struct zone *zone)
 #endif
 }
 
-static inline int is_normal(struct zone *zone)
-{
-       return zone == zone->zone_pgdat->node_zones + ZONE_NORMAL;
-}
-
-static inline int is_dma32(struct zone *zone)
-{
-#ifdef CONFIG_ZONE_DMA32
-       return zone == zone->zone_pgdat->node_zones + ZONE_DMA32;
-#else
-       return 0;
-#endif
-}
-
-static inline int is_dma(struct zone *zone)
-{
-#ifdef CONFIG_ZONE_DMA
-       return zone == zone->zone_pgdat->node_zones + ZONE_DMA;
-#else
-       return 0;
-#endif
-}
-
 /* These two functions are used to setup the per zone pages min values */
 struct ctl_table;
 int min_free_kbytes_sysctl_handler(struct ctl_table *, int,
index 141d395..6e8215b 100644 (file)
@@ -30,6 +30,7 @@ struct mv643xx_eth_shared_platform_data {
 #define MV643XX_ETH_PHY_ADDR(x)                (0x80 | (x))
 #define MV643XX_ETH_PHY_NONE           0xff
 
+struct device_node;
 struct mv643xx_eth_platform_data {
        /*
         * Pointer back to our parent instance, and our port number.
@@ -41,6 +42,7 @@ struct mv643xx_eth_platform_data {
         * Whether a PHY is present, and if yes, at which address.
         */
        int                     phy_addr;
+       struct device_node      *phy_node;
 
        /*
         * Use this MAC address if it is valid, overriding the
index 99c9f0c..4f27575 100644 (file)
@@ -79,9 +79,9 @@ enum sock_type {
 #endif /* ARCH_HAS_SOCKET_TYPES */
 
 enum sock_shutdown_cmd {
-       SHUT_RD         = 0,
-       SHUT_WR         = 1,
-       SHUT_RDWR       = 2,
+       SHUT_RD,
+       SHUT_WR,
+       SHUT_RDWR,
 };
 
 struct socket_wq {
index 09906b7..a2a89a5 100644 (file)
@@ -43,8 +43,9 @@ enum {
        NETIF_F_FSO_BIT,                /* ... FCoE segmentation */
        NETIF_F_GSO_GRE_BIT,            /* ... GRE with TSO */
        NETIF_F_GSO_UDP_TUNNEL_BIT,     /* ... UDP TUNNEL with TSO */
+       NETIF_F_GSO_MPLS_BIT,           /* ... MPLS segmentation */
        /**/NETIF_F_GSO_LAST =          /* last bit, see GSO_MASK */
-               NETIF_F_GSO_UDP_TUNNEL_BIT,
+               NETIF_F_GSO_MPLS_BIT,
 
        NETIF_F_FCOE_CRC_BIT,           /* FCoE CRC32 */
        NETIF_F_SCTP_CSUM_BIT,          /* SCTP checksum offload */
@@ -107,6 +108,7 @@ enum {
 #define NETIF_F_RXALL          __NETIF_F(RXALL)
 #define NETIF_F_GSO_GRE                __NETIF_F(GSO_GRE)
 #define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL)
+#define NETIF_F_GSO_MPLS       __NETIF_F(GSO_MPLS)
 #define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER)
 #define NETIF_F_HW_VLAN_STAG_RX        __NETIF_F(HW_VLAN_STAG_RX)
 #define NETIF_F_HW_VLAN_STAG_TX        __NETIF_F(HW_VLAN_STAG_TX)
index 96e4c21..bb82871 100644 (file)
@@ -324,12 +324,15 @@ struct napi_struct {
        struct sk_buff          *gro_list;
        struct sk_buff          *skb;
        struct list_head        dev_list;
+       struct hlist_node       napi_hash_node;
+       unsigned int            napi_id;
 };
 
 enum {
        NAPI_STATE_SCHED,       /* Poll is scheduled */
        NAPI_STATE_DISABLE,     /* Disable pending */
        NAPI_STATE_NPSVC,       /* Netpoll - don't dequeue from poll_list */
+       NAPI_STATE_HASHED,      /* In NAPI hash */
 };
 
 enum gro_result {
@@ -445,6 +448,32 @@ static inline bool napi_reschedule(struct napi_struct *napi)
 extern void __napi_complete(struct napi_struct *n);
 extern void napi_complete(struct napi_struct *n);
 
+/**
+ *     napi_by_id - lookup a NAPI by napi_id
+ *     @napi_id: hashed napi_id
+ *
+ * lookup @napi_id in napi_hash table
+ * must be called under rcu_read_lock()
+ */
+extern struct napi_struct *napi_by_id(unsigned int napi_id);
+
+/**
+ *     napi_hash_add - add a NAPI to global hashtable
+ *     @napi: napi context
+ *
+ * generate a new napi_id and store a @napi under it in napi_hash
+ */
+extern void napi_hash_add(struct napi_struct *napi);
+
+/**
+ *     napi_hash_del - remove a NAPI from global table
+ *     @napi: napi context
+ *
+ * Warning: caller must observe rcu grace period
+ * before freeing memory containing @napi
+ */
+extern void napi_hash_del(struct napi_struct *napi);
+
 /**
  *     napi_disable - prevent NAPI from scheduling
  *     @n: napi context
@@ -800,6 +829,7 @@ struct netdev_fcoe_hbainfo {
  * int (*ndo_set_vf_spoofchk)(struct net_device *dev, int vf, bool setting);
  * int (*ndo_get_vf_config)(struct net_device *dev,
  *                         int vf, struct ifla_vf_info *ivf);
+ * int (*ndo_set_vf_link_state)(struct net_device *dev, int vf, int link_state);
  * int (*ndo_set_vf_port)(struct net_device *dev, int vf,
  *                       struct nlattr *port[]);
  * int (*ndo_get_vf_port)(struct net_device *dev, int vf, struct sk_buff *skb);
@@ -942,6 +972,9 @@ struct net_device_ops {
                                                     struct netpoll_info *info,
                                                     gfp_t gfp);
        void                    (*ndo_netpoll_cleanup)(struct net_device *dev);
+#endif
+#ifdef CONFIG_NET_LL_RX_POLL
+       int                     (*ndo_ll_poll)(struct napi_struct *dev);
 #endif
        int                     (*ndo_set_vf_mac)(struct net_device *dev,
                                                  int queue, u8 *mac);
@@ -954,6 +987,8 @@ struct net_device_ops {
        int                     (*ndo_get_vf_config)(struct net_device *dev,
                                                     int vf,
                                                     struct ifla_vf_info *ivf);
+       int                     (*ndo_set_vf_link_state)(struct net_device *dev,
+                                                        int vf, int link_state);
        int                     (*ndo_set_vf_port)(struct net_device *dev,
                                                   int vf,
                                                   struct nlattr *port[]);
@@ -1088,6 +1123,8 @@ struct net_device {
         * need to set them appropriately.
         */
        netdev_features_t       hw_enc_features;
+       /* mask of fetures inheritable by MPLS */
+       netdev_features_t       mpls_features;
 
        /* Interface index. Unique device identifier    */
        int                     ifindex;
@@ -1140,8 +1177,10 @@ struct net_device {
        unsigned char           addr_assign_type; /* hw address assignment type */
        unsigned char           addr_len;       /* hardware address length      */
        unsigned char           neigh_priv_len;
-       unsigned short          dev_id;         /* for shared network cards */
-
+       unsigned short          dev_id;         /* Used to differentiate devices
+                                                * that share the same link
+                                                * layer address
+                                                */
        spinlock_t              addr_list_lock;
        struct netdev_hw_addr_list      uc;     /* Unicast mac addresses */
        struct netdev_hw_addr_list      mc;     /* Multicast mac addresses */
@@ -1593,9 +1632,34 @@ struct packet_offload {
 #define NETDEV_RELEASE         0x0012
 #define NETDEV_NOTIFY_PEERS    0x0013
 #define NETDEV_JOIN            0x0014
+#define NETDEV_CHANGEUPPER     0x0015
 
 extern int register_netdevice_notifier(struct notifier_block *nb);
 extern int unregister_netdevice_notifier(struct notifier_block *nb);
+
+struct netdev_notifier_info {
+       struct net_device *dev;
+};
+
+struct netdev_notifier_change_info {
+       struct netdev_notifier_info info; /* must be first */
+       unsigned int flags_changed;
+};
+
+static inline void netdev_notifier_info_init(struct netdev_notifier_info *info,
+                                            struct net_device *dev)
+{
+       info->dev = dev;
+}
+
+static inline struct net_device *
+netdev_notifier_info_to_dev(const struct netdev_notifier_info *info)
+{
+       return info->dev;
+}
+
+extern int call_netdevice_notifiers_info(unsigned long val, struct net_device *dev,
+                                        struct netdev_notifier_info *info);
 extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev);
 
 
@@ -1779,6 +1843,19 @@ static inline int unregister_gifconf(unsigned int family)
        return register_gifconf(family, NULL);
 }
 
+#ifdef CONFIG_NET_FLOW_LIMIT
+#define FLOW_LIMIT_HISTORY     (1 << 7)  /* must be ^2 and !overflow buckets */
+struct sd_flow_limit {
+       u64                     count;
+       unsigned int            num_buckets;
+       unsigned int            history_head;
+       u16                     history[FLOW_LIMIT_HISTORY];
+       u8                      buckets[];
+};
+
+extern int netdev_flow_limit_table_len;
+#endif /* CONFIG_NET_FLOW_LIMIT */
+
 /*
  * Incoming packets are placed on per-cpu queues
  */
@@ -1808,6 +1885,10 @@ struct softnet_data {
        unsigned int            dropped;
        struct sk_buff_head     input_pkt_queue;
        struct napi_struct      backlog;
+
+#ifdef CONFIG_NET_FLOW_LIMIT
+       struct sd_flow_limit __rcu *flow_limit;
+#endif
 };
 
 static inline void input_queue_head_incr(struct softnet_data *sd)
index 0060fde..de70f7b 100644 (file)
@@ -35,7 +35,7 @@ static inline void nf_inet_addr_mask(const union nf_inet_addr *a1,
        result->all[3] = a1->all[3] & mask->all[3];
 }
 
-extern void netfilter_init(void);
+extern int netfilter_init(void);
 
 /* Largest hook number + 1 */
 #define NF_MAX_HOOKS 8
index 6358da5..7a6c396 100644 (file)
@@ -46,6 +46,7 @@ struct netlink_kernel_cfg {
        void            (*input)(struct sk_buff *skb);
        struct mutex    *cb_mutex;
        void            (*bind)(int group);
+       bool            (*compare)(struct net *net, struct sock *sk);
 };
 
 extern struct sock *__netlink_kernel_create(struct net *net, int unit,
@@ -84,6 +85,22 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb,
 void netlink_detachskb(struct sock *sk, struct sk_buff *skb);
 int netlink_sendskb(struct sock *sk, struct sk_buff *skb);
 
+static inline struct sk_buff *
+netlink_skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
+{
+       struct sk_buff *nskb;
+
+       nskb = skb_clone(skb, gfp_mask);
+       if (!nskb)
+               return NULL;
+
+       /* This is a large skb, set destructor callback to release head */
+       if (is_vmalloc_addr(skb->head))
+               nskb->destructor = skb->destructor;
+
+       return nskb;
+}
+
 /*
  *     skb should fit one page. This choice is good for headerless malloc.
  *     But we should limit to 8K so that userspace does not have to
@@ -144,4 +161,14 @@ static inline int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
        return __netlink_dump_start(ssk, skb, nlh, control);
 }
 
+struct netlink_tap {
+       struct net_device *dev;
+       struct module *module;
+       struct list_head list;
+};
+
+extern int netlink_add_tap(struct netlink_tap *nt);
+extern int __netlink_remove_tap(struct netlink_tap *nt);
+extern int netlink_remove_tap(struct netlink_tap *nt);
+
 #endif /* __LINUX_NETLINK_H */
index fa2cb76..f3c7c24 100644 (file)
@@ -53,10 +53,10 @@ struct netpoll_info {
 };
 
 #ifdef CONFIG_NETPOLL
-extern int netpoll_rx_disable(struct net_device *dev);
+extern void netpoll_rx_disable(struct net_device *dev);
 extern void netpoll_rx_enable(struct net_device *dev);
 #else
-static inline int netpoll_rx_disable(struct net_device *dev) { return 0; }
+static inline void netpoll_rx_disable(struct net_device *dev) { return; }
 static inline void netpoll_rx_enable(struct net_device *dev) { return; }
 #endif
 
index 7b8fc73..e36dee5 100644 (file)
@@ -32,6 +32,15 @@ struct nfs4_acl {
        struct nfs4_ace aces[0];
 };
 
+#define NFS4_MAXLABELLEN       2048
+
+struct nfs4_label {
+       uint32_t        lfs;
+       uint32_t        pi;
+       u32             len;
+       char    *label;
+};
+
 typedef struct { char data[NFS4_VERIFIER_SIZE]; } nfs4_verifier;
 
 struct nfs_stateid4 {
@@ -219,6 +228,14 @@ enum nfsstat4 {
        NFS4ERR_REJECT_DELEG    = 10085,        /* on callback */
        NFS4ERR_RETURNCONFLICT  = 10086,        /* outstanding layoutreturn */
        NFS4ERR_DELEG_REVOKED   = 10087,        /* deleg./layout revoked */
+
+       /* nfs42 */
+       NFS4ERR_PARTNER_NOTSUPP = 10088,
+       NFS4ERR_PARTNER_NO_AUTH = 10089,
+       NFS4ERR_METADATA_NOTSUPP = 10090,
+       NFS4ERR_OFFLOAD_DENIED = 10091,
+       NFS4ERR_WRONG_LFS = 10092,
+       NFS4ERR_BADLABEL = 10093,
 };
 
 static inline bool seqid_mutating_err(u32 err)
@@ -378,6 +395,7 @@ enum lock_type4 {
 #define FATTR4_WORD1_FS_LAYOUT_TYPES    (1UL << 30)
 #define FATTR4_WORD2_LAYOUT_BLKSIZE     (1UL << 1)
 #define FATTR4_WORD2_MDSTHRESHOLD       (1UL << 4)
+#define FATTR4_WORD2_SECURITY_LABEL     (1UL << 17)
 
 /* MDS threshold bitmap bits */
 #define THRESHOLD_RD                    (1UL << 0)
@@ -390,11 +408,15 @@ enum lock_type4 {
 #define NFS4_VERSION 4
 #define NFS4_MINOR_VERSION 0
 
+#if defined(CONFIG_NFS_V4_2)
+#define NFS4_MAX_MINOR_VERSION 2
+#else
 #if defined(CONFIG_NFS_V4_1)
 #define NFS4_MAX_MINOR_VERSION 1
 #else
 #define NFS4_MAX_MINOR_VERSION 0
 #endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_NFS_V4_2 */
 
 #define NFS4_DEBUG 1
 
index fc01d5c..0b17629 100644 (file)
@@ -207,6 +207,7 @@ struct nfs_inode {
 #define NFS_INO_INVALID_ACL    0x0010          /* cached acls are invalid */
 #define NFS_INO_REVAL_PAGECACHE        0x0020          /* must revalidate pagecache */
 #define NFS_INO_REVAL_FORCED   0x0040          /* force revalidation ignoring a delegation */
+#define NFS_INO_INVALID_LABEL  0x0080          /* cached label is invalid */
 
 /*
  * Bit offsets in flags field
@@ -336,7 +337,7 @@ extern void nfs_zap_mapping(struct inode *inode, struct address_space *mapping);
 extern void nfs_zap_caches(struct inode *);
 extern void nfs_invalidate_atime(struct inode *);
 extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
-                               struct nfs_fattr *);
+                               struct nfs_fattr *, struct nfs4_label *);
 extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
 extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr);
 extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr);
@@ -352,10 +353,13 @@ extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
 extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
 extern int nfs_setattr(struct dentry *, struct iattr *);
 extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
+extern void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
+                               struct nfs4_label *label);
 extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
 extern void put_nfs_open_context(struct nfs_open_context *ctx);
 extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode);
 extern struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, fmode_t f_mode);
+extern void nfs_inode_attach_open_context(struct nfs_open_context *ctx);
 extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx);
 extern struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx);
 extern void nfs_put_lock_context(struct nfs_lock_context *l_ctx);
@@ -468,7 +472,8 @@ extern const struct file_operations nfs_dir_operations;
 extern const struct dentry_operations nfs_dentry_operations;
 
 extern void nfs_force_lookup_revalidate(struct inode *dir);
-extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr);
+extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh,
+                       struct nfs_fattr *fattr, struct nfs4_label *label);
 extern int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags);
 extern void nfs_access_zap_cache(struct inode *inode);
 
@@ -496,6 +501,24 @@ extern const struct inode_operations nfs_referral_inode_operations;
 extern int nfs_mountpoint_expiry_timeout;
 extern void nfs_release_automount_timer(void);
 
+/*
+ * linux/fs/nfs/nfs4proc.c
+ */
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+extern struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags);
+static inline void nfs4_label_free(struct nfs4_label *label)
+{
+       if (label) {
+               kfree(label->label);
+               kfree(label);
+       }
+       return;
+}
+#else
+static inline struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags) { return NULL; }
+static inline void nfs4_label_free(void *label) {}
+#endif
+
 /*
  * linux/fs/nfs/unlink.c
  */
index 3b7fa2a..d221243 100644 (file)
@@ -146,7 +146,12 @@ struct nfs_server {
        u32                     attr_bitmask[3];/* V4 bitmask representing the set
                                                   of attributes supported on this
                                                   filesystem */
-       u32                     cache_consistency_bitmask[2];
+       u32                     attr_bitmask_nl[3];
+                                               /* V4 bitmask representing the
+                                                  set of attributes supported
+                                                  on this filesystem excluding
+                                                  the label support bit. */
+       u32                     cache_consistency_bitmask[3];
                                                /* V4 bitmask representing the subset
                                                   of change attribute, size, ctime
                                                   and mtime attributes supported by
@@ -200,5 +205,6 @@ struct nfs_server {
 #define NFS_CAP_UIDGID_NOMAP   (1U << 15)
 #define NFS_CAP_STATEID_NFSV41 (1U << 16)
 #define NFS_CAP_ATOMIC_OPEN_V1 (1U << 17)
+#define NFS_CAP_SECURITY_LABEL (1U << 18)
 
 #endif
index 104b62f..8651574 100644 (file)
@@ -101,6 +101,7 @@ struct nfs_fattr {
 #define NFS_ATTR_FATTR_MOUNTED_ON_FILEID (1U << 22)
 #define NFS_ATTR_FATTR_OWNER_NAME      (1U << 23)
 #define NFS_ATTR_FATTR_GROUP_NAME      (1U << 24)
+#define NFS_ATTR_FATTR_V4_SECURITY_LABEL (1U << 25)
 
 #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
                | NFS_ATTR_FATTR_MODE \
@@ -120,7 +121,8 @@ struct nfs_fattr {
 #define NFS_ATTR_FATTR_V3 (NFS_ATTR_FATTR \
                | NFS_ATTR_FATTR_SPACE_USED)
 #define NFS_ATTR_FATTR_V4 (NFS_ATTR_FATTR \
-               | NFS_ATTR_FATTR_SPACE_USED)
+               | NFS_ATTR_FATTR_SPACE_USED \
+               | NFS_ATTR_FATTR_V4_SECURITY_LABEL)
 
 /*
  * Info on the file system
@@ -246,6 +248,7 @@ struct nfs4_layoutget_res {
 struct nfs4_layoutget {
        struct nfs4_layoutget_args args;
        struct nfs4_layoutget_res res;
+       struct rpc_cred *cred;
        gfp_t gfp_flags;
 };
 
@@ -347,6 +350,7 @@ struct nfs_openargs {
        const u32 *             open_bitmap;
        __u32                   claim;
        enum createmode4        createmode;
+       const struct nfs4_label *label;
 };
 
 struct nfs_openres {
@@ -356,6 +360,7 @@ struct nfs_openres {
        struct nfs4_change_info cinfo;
        __u32                   rflags;
        struct nfs_fattr *      f_attr;
+       struct nfs4_label       *f_label;
        struct nfs_seqid *      seqid;
        const struct nfs_server *server;
        fmode_t                 delegation_type;
@@ -598,6 +603,7 @@ struct nfs_entry {
        int                     eof;
        struct nfs_fh *         fh;
        struct nfs_fattr *      fattr;
+       struct nfs4_label  *label;
        unsigned char           d_type;
        struct nfs_server *     server;
 };
@@ -630,6 +636,7 @@ struct nfs_setattrargs {
        struct iattr *                  iap;
        const struct nfs_server *       server; /* Needed for name mapping */
        const u32 *                     bitmask;
+       const struct nfs4_label         *label;
 };
 
 struct nfs_setaclargs {
@@ -665,6 +672,7 @@ struct nfs_getaclres {
 struct nfs_setattrres {
        struct nfs4_sequence_res        seq_res;
        struct nfs_fattr *              fattr;
+       struct nfs4_label               *label;
        const struct nfs_server *       server;
 };
 
@@ -862,6 +870,7 @@ struct nfs4_create_arg {
        const struct iattr *            attrs;
        const struct nfs_fh *           dir_fh;
        const u32 *                     bitmask;
+       const struct nfs4_label         *label;
 };
 
 struct nfs4_create_res {
@@ -869,6 +878,7 @@ struct nfs4_create_res {
        const struct nfs_server *       server;
        struct nfs_fh *                 fh;
        struct nfs_fattr *              fattr;
+       struct nfs4_label               *label;
        struct nfs4_change_info         dir_cinfo;
 };
 
@@ -893,6 +903,7 @@ struct nfs4_getattr_res {
        struct nfs4_sequence_res        seq_res;
        const struct nfs_server *       server;
        struct nfs_fattr *              fattr;
+       struct nfs4_label               *label;
 };
 
 struct nfs4_link_arg {
@@ -907,6 +918,7 @@ struct nfs4_link_res {
        struct nfs4_sequence_res        seq_res;
        const struct nfs_server *       server;
        struct nfs_fattr *              fattr;
+       struct nfs4_label               *label;
        struct nfs4_change_info         cinfo;
        struct nfs_fattr *              dir_attr;
 };
@@ -924,6 +936,7 @@ struct nfs4_lookup_res {
        const struct nfs_server *       server;
        struct nfs_fattr *              fattr;
        struct nfs_fh *                 fh;
+       struct nfs4_label               *label;
 };
 
 struct nfs4_lookup_root_arg {
@@ -1366,11 +1379,12 @@ struct nfs_rpc_ops {
        struct dentry *(*try_mount) (int, const char *, struct nfs_mount_info *,
                                     struct nfs_subversion *);
        int     (*getattr) (struct nfs_server *, struct nfs_fh *,
-                           struct nfs_fattr *);
+                           struct nfs_fattr *, struct nfs4_label *);
        int     (*setattr) (struct dentry *, struct nfs_fattr *,
                            struct iattr *);
        int     (*lookup)  (struct inode *, struct qstr *,
-                           struct nfs_fh *, struct nfs_fattr *);
+                           struct nfs_fh *, struct nfs_fattr *,
+                           struct nfs4_label *);
        int     (*access)  (struct inode *, struct nfs_access_entry *);
        int     (*readlink)(struct inode *, struct page *, unsigned int,
                            unsigned int);
index 9e11039..64ab823 100644 (file)
@@ -49,6 +49,7 @@
 
 #define PHY_HAS_INTERRUPT      0x00000001
 #define PHY_HAS_MAGICANEG      0x00000002
+#define PHY_IS_INTERNAL                0x00000004
 
 /* Interface Mode definitions */
 typedef enum {
@@ -57,6 +58,7 @@ typedef enum {
        PHY_INTERFACE_MODE_GMII,
        PHY_INTERFACE_MODE_SGMII,
        PHY_INTERFACE_MODE_TBI,
+       PHY_INTERFACE_MODE_REVMII,
        PHY_INTERFACE_MODE_RMII,
        PHY_INTERFACE_MODE_RGMII,
        PHY_INTERFACE_MODE_RGMII_ID,
@@ -261,6 +263,7 @@ struct phy_c45_device_ids {
  * phy_id: UID for this device found during discovery
  * c45_ids: 802.3-c45 Device Identifers if is_c45.
  * is_c45:  Set to true if this phy uses clause 45 addressing.
+ * is_internal: Set to true if this phy is internal to a MAC.
  * state: state of the PHY for management purposes
  * dev_flags: Device-specific flags used by the PHY driver.
  * addr: Bus address of PHY
@@ -298,6 +301,7 @@ struct phy_device {
 
        struct phy_c45_device_ids c45_ids;
        bool is_c45;
+       bool is_internal;
 
        enum phy_state state;
 
@@ -508,6 +512,27 @@ static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val)
        return mdiobus_write(phydev->bus, phydev->addr, regnum, val);
 }
 
+/**
+ * phy_interrupt_is_valid - Convenience function for testing a given PHY irq
+ * @phydev: the phy_device struct
+ *
+ * NOTE: must be kept in sync with addition/removal of PHY_POLL and
+ * PHY_IGNORE_INTERRUPT
+ */
+static inline bool phy_interrupt_is_valid(struct phy_device *phydev)
+{
+       return phydev->irq != PHY_POLL && phydev->irq != PHY_IGNORE_INTERRUPT;
+}
+
+/**
+ * phy_is_internal - Convenience function for testing if a PHY is internal
+ * @phydev: the phy_device struct
+ */
+static inline bool phy_is_internal(struct phy_device *phydev)
+{
+       return phydev->is_internal;
+}
+
 struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
                bool is_c45, struct phy_c45_device_ids *c45_ids);
 struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45);
@@ -545,6 +570,8 @@ void phy_drivers_unregister(struct phy_driver *drv, int n);
 int phy_driver_register(struct phy_driver *new_driver);
 int phy_drivers_register(struct phy_driver *new_driver, int n);
 void phy_state_machine(struct work_struct *work);
+void phy_change(struct work_struct *work);
+void phy_mac_interrupt(struct phy_device *phydev, int new_link);
 void phy_start_machine(struct phy_device *phydev,
                void (*handler)(struct net_device *));
 void phy_stop_machine(struct phy_device *phydev);
index 1ade657..b717499 100644 (file)
@@ -90,6 +90,10 @@ void __init brcmfmac_init_pdata(void)
  * oob_irq_nr, oob_irq_flags: the OOB interrupt information. The values are
  * used for registering the irq using request_irq function.
  *
+ * broken_sg_support: flag for broken sg list support of SDIO host controller.
+ * Set this to true if the SDIO host controller has higher align requirement
+ * than 32 bytes for each scatterlist item.
+ *
  * power_on: This function is called by the brcmfmac when the module gets
  * loaded. This can be particularly useful for low power devices. The platform
  * spcific routine may for example decide to power up the complete device.
@@ -116,6 +120,7 @@ struct brcmfmac_sdio_platform_data {
        bool oob_irq_supported;
        unsigned int oob_irq_nr;
        unsigned long oob_irq_flags;
+       bool broken_sg_support;
        void (*power_on)(void);
        void (*power_off)(void);
        void (*reset)(void);
diff --git a/include/linux/platform_data/net-cw1200.h b/include/linux/platform_data/net-cw1200.h
new file mode 100644 (file)
index 0000000..c6fbc3c
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CW1200_PLAT_H_INCLUDED
+#define CW1200_PLAT_H_INCLUDED
+
+struct cw1200_platform_data_spi {
+       u8 spi_bits_per_word;           /* REQUIRED */
+       u16 ref_clk;                    /* REQUIRED (in KHz) */
+
+       /* All others are optional */
+       bool have_5ghz;
+       int reset;                     /* GPIO to RSTn signal (0 disables) */
+       int powerup;                   /* GPIO to POWERUP signal (0 disables) */
+       int (*power_ctrl)(const struct cw1200_platform_data_spi *pdata,
+                         bool enable); /* Control 3v3 / 1v8 supply */
+       int (*clk_ctrl)(const struct cw1200_platform_data_spi *pdata,
+                       bool enable); /* Control CLK32K */
+       const u8 *macaddr;  /* if NULL, use cw1200_mac_template module parameter */
+       const char *sdd_file;  /* if NULL, will use default for detected hw type */
+};
+
+struct cw1200_platform_data_sdio {
+       u16 ref_clk;                    /* REQUIRED (in KHz) */
+
+       /* All others are optional */
+       bool have_5ghz;
+       bool no_nptb;       /* SDIO hardware does not support non-power-of-2-blocksizes */
+       int reset;          /* GPIO to RSTn signal (0 disables) */
+       int powerup;        /* GPIO to POWERUP signal (0 disables) */
+       int irq;            /* IRQ line or 0 to use SDIO IRQ */
+       int (*power_ctrl)(const struct cw1200_platform_data_sdio *pdata,
+                         bool enable); /* Control 3v3 / 1v8 supply */
+       int (*clk_ctrl)(const struct cw1200_platform_data_sdio *pdata,
+                       bool enable); /* Control CLK32K */
+       const u8 *macaddr;  /* if NULL, use cw1200_mac_template module parameter */
+       const char *sdd_file;  /* if NULL, will use default for detected hw type */
+};
+
+
+/* An example of SPI support in your board setup file:
+
+   static struct cw1200_platform_data_spi cw1200_platform_data = {
+       .ref_clk = 38400,
+       .spi_bits_per_word = 16,
+       .reset = GPIO_RF_RESET,
+       .powerup = GPIO_RF_POWERUP,
+       .macaddr = wifi_mac_addr,
+       .sdd_file = "sdd_sagrad_1091_1098.bin",
+  };
+  static struct spi_board_info myboard_spi_devices[] __initdata = {
+       {
+               .modalias = "cw1200_wlan_spi",
+               .max_speed_hz = 52000000,
+               .bus_num = 0,
+               .irq = WIFI_IRQ,
+               .platform_data = &cw1200_platform_data,
+               .chip_select = 0,
+       },
+  };
+
+ */
+
+/* An example of SDIO support in your board setup file:
+
+  static struct cw1200_platform_data_sdio my_cw1200_platform_data = {
+       .ref_clk = 38400,
+       .have_5ghz = false,
+       .sdd_file = "sdd_myplatform.bin",
+  };
+  cw1200_sdio_set_platform_data(&my_cw1200_platform_data);
+
+ */
+
+void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata);
+
+#endif /* CW1200_PLAT_H_INCLUDED */
diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h
new file mode 100644 (file)
index 0000000..80587fd
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * rcar_du.h  --  R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 __RCAR_DU_H__
+#define __RCAR_DU_H__
+
+#include <drm/drm_mode.h>
+
+enum rcar_du_encoder_type {
+       RCAR_DU_ENCODER_UNUSED = 0,
+       RCAR_DU_ENCODER_VGA,
+       RCAR_DU_ENCODER_LVDS,
+};
+
+struct rcar_du_panel_data {
+       unsigned int width_mm;          /* Panel width in mm */
+       unsigned int height_mm;         /* Panel height in mm */
+       struct drm_mode_modeinfo mode;
+};
+
+struct rcar_du_encoder_lvds_data {
+       struct rcar_du_panel_data panel;
+};
+
+struct rcar_du_encoder_vga_data {
+       /* TODO: Add DDC information for EDID retrieval */
+};
+
+struct rcar_du_encoder_data {
+       enum rcar_du_encoder_type encoder;
+       unsigned int output;
+
+       union {
+               struct rcar_du_encoder_lvds_data lvds;
+               struct rcar_du_encoder_vga_data vga;
+       } u;
+};
+
+struct rcar_du_platform_data {
+       struct rcar_du_encoder_data *encoders;
+       unsigned int num_encoders;
+};
+
+#endif /* __RCAR_DU_H__ */
index 89573a3..07d0df6 100644 (file)
@@ -142,9 +142,6 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
 {
        INIT_LIST_HEAD(&child->ptrace_entry);
        INIT_LIST_HEAD(&child->ptraced);
-#ifdef CONFIG_HAVE_HW_BREAKPOINT
-       atomic_set(&child->ptrace_bp_refcnt, 1);
-#endif
        child->jobctl = 0;
        child->ptrace = 0;
        child->parent = child->real_parent;
@@ -351,11 +348,4 @@ extern int task_current_syscall(struct task_struct *target, long *callno,
                                unsigned long args[6], unsigned int maxargs,
                                unsigned long *sp, unsigned long *pc);
 
-#ifdef CONFIG_HAVE_HW_BREAKPOINT
-extern int ptrace_get_breakpoints(struct task_struct *tsk);
-extern void ptrace_put_breakpoints(struct task_struct *tsk);
-#else
-static inline void ptrace_put_breakpoints(struct task_struct *tsk) { }
-#endif /* CONFIG_HAVE_HW_BREAKPOINT */
-
 #endif
index 23b3630..8e00f9f 100644 (file)
 #define SYS_HALT       0x0002  /* Notify of system halt */
 #define SYS_POWER_OFF  0x0003  /* Notify of system power off */
 
+enum reboot_mode {
+       REBOOT_COLD = 0,
+       REBOOT_WARM,
+       REBOOT_HARD,
+       REBOOT_SOFT,
+       REBOOT_GPIO,
+};
+extern enum reboot_mode reboot_mode;
+
+enum reboot_type {
+       BOOT_TRIPLE = 't',
+       BOOT_KBD = 'k',
+       BOOT_BIOS = 'b',
+       BOOT_ACPI = 'a',
+       BOOT_EFI = 'e',
+       BOOT_CF9 = 'p',
+       BOOT_CF9_COND = 'q',
+};
+extern enum reboot_type reboot_type;
+
+extern int reboot_default;
+extern int reboot_cpu;
+extern int reboot_force;
+
+
 extern int register_reboot_notifier(struct notifier_block *);
 extern int unregister_reboot_notifier(struct notifier_block *);
 
@@ -26,7 +51,7 @@ extern void machine_shutdown(void);
 struct pt_regs;
 extern void machine_crash_shutdown(struct pt_regs *);
 
-/* 
+/*
  * Architecture independent implemenations of sys_reboot commands.
  */
 
diff --git a/include/linux/reservation.h b/include/linux/reservation.h
new file mode 100644 (file)
index 0000000..e9ee806
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Header file for reservations for dma-buf and ttm
+ *
+ * Copyright(C) 2011 Linaro Limited. All rights reserved.
+ * Copyright (C) 2012-2013 Canonical Ltd
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * Authors:
+ * Rob Clark <rob.clark@linaro.org>
+ * Maarten Lankhorst <maarten.lankhorst@canonical.com>
+ * Thomas Hellstrom <thellstrom-at-vmware-dot-com>
+ *
+ * Based on bo.c which bears the following copyright notice,
+ * but is dual licensed:
+ *
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _LINUX_RESERVATION_H
+#define _LINUX_RESERVATION_H
+
+#include <linux/mutex.h>
+
+extern struct ww_class reservation_ww_class;
+
+struct reservation_object {
+       struct ww_mutex lock;
+};
+
+static inline void
+reservation_object_init(struct reservation_object *obj)
+{
+       ww_mutex_init(&obj->lock, &reservation_ww_class);
+}
+
+static inline void
+reservation_object_fini(struct reservation_object *obj)
+{
+       ww_mutex_destroy(&obj->lock);
+}
+
+#endif /* _LINUX_RESERVATION_H */
index 2680677..adae88f 100644 (file)
@@ -244,6 +244,11 @@ size_t sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents,
 size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                         void *buf, size_t buflen);
 
+size_t sg_pcopy_from_buffer(struct scatterlist *sgl, unsigned int nents,
+                           void *buf, size_t buflen, off_t skip);
+size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
+                         void *buf, size_t buflen, off_t skip);
+
 /*
  * Maximum number of entries that will be allocated in one piece, if
  * a list larger than this is required then chaining will be utilized.
index cdd5407..f99d57e 100644 (file)
@@ -1401,9 +1401,6 @@ struct task_struct {
        } memcg_batch;
        unsigned int memcg_kmem_skip_account;
 #endif
-#ifdef CONFIG_HAVE_HW_BREAKPOINT
-       atomic_t ptrace_bp_refcnt;
-#endif
 #ifdef CONFIG_UPROBES
        struct uprobe_task *utask;
 #endif
@@ -2437,6 +2434,15 @@ extern int __cond_resched_softirq(void);
        __cond_resched_softirq();                                       \
 })
 
+static inline void cond_resched_rcu(void)
+{
+#if defined(CONFIG_DEBUG_ATOMIC_SLEEP) || !defined(CONFIG_PREEMPT_RCU)
+       rcu_read_unlock();
+       cond_resched();
+       rcu_read_lock();
+#endif
+}
+
 /*
  * Does a critical section need to be broken due to another
  * task waiting?: (technically does not depend on CONFIG_PREEMPT,
index 40560f4..7ce53ae 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/capability.h>
 #include <linux/slab.h>
 #include <linux/err.h>
+#include <linux/string.h>
 
 struct linux_binprm;
 struct cred;
@@ -60,6 +61,9 @@ struct mm_struct;
 #define SECURITY_CAP_NOAUDIT 0
 #define SECURITY_CAP_AUDIT 1
 
+/* LSM Agnostic defines for sb_set_mnt_opts */
+#define SECURITY_LSM_NATIVE_LABELS     1
+
 struct ctl_table;
 struct audit_krule;
 struct user_namespace;
@@ -306,6 +310,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     Parse a string of security data filling in the opts structure
  *     @options string containing all mount options known by the LSM
  *     @opts binary data structure usable by the LSM
+ * @dentry_init_security:
+ *     Compute a context for a dentry as the inode is not yet available
+ *     since NFSv4 has no label backed by an EA anyway.
+ *     @dentry dentry to use in calculating the context.
+ *     @mode mode used to determine resource type.
+ *     @name name of the last path component used to create file
+ *     @ctx pointer to place the pointer to the resulting context in.
+ *     @ctxlen point to place the length of the resulting context.
+ *
  *
  * Security hooks for inode operations.
  *
@@ -1313,6 +1326,13 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @pages contains the number of pages.
  *     Return 0 if permission is granted.
  *
+ * @ismaclabel:
+ *     Check if the extended attribute specified by @name
+ *     represents a MAC label. Returns 1 if name is a MAC
+ *     attribute otherwise returns 0.
+ *     @name full extended attribute name to check against
+ *     LSM as a MAC label.
+ *
  * @secid_to_secctx:
  *     Convert secid to security context.  If secdata is NULL the length of
  *     the result will be returned in seclen, but no secdata will be returned.
@@ -1440,10 +1460,16 @@ struct security_operations {
        int (*sb_pivotroot) (struct path *old_path,
                             struct path *new_path);
        int (*sb_set_mnt_opts) (struct super_block *sb,
-                               struct security_mnt_opts *opts);
+                               struct security_mnt_opts *opts,
+                               unsigned long kern_flags,
+                               unsigned long *set_kern_flags);
        int (*sb_clone_mnt_opts) (const struct super_block *oldsb,
                                   struct super_block *newsb);
        int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts);
+       int (*dentry_init_security) (struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen);
+
 
 #ifdef CONFIG_SECURITY_PATH
        int (*path_unlink) (struct path *dir, struct dentry *dentry);
@@ -1591,6 +1617,7 @@ struct security_operations {
 
        int (*getprocattr) (struct task_struct *p, char *name, char **value);
        int (*setprocattr) (struct task_struct *p, char *name, void *value, size_t size);
+       int (*ismaclabel) (const char *name);
        int (*secid_to_secctx) (u32 secid, char **secdata, u32 *seclen);
        int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid);
        void (*release_secctx) (char *secdata, u32 seclen);
@@ -1726,10 +1753,16 @@ int security_sb_mount(const char *dev_name, struct path *path,
                      const char *type, unsigned long flags, void *data);
 int security_sb_umount(struct vfsmount *mnt, int flags);
 int security_sb_pivotroot(struct path *old_path, struct path *new_path);
-int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts);
+int security_sb_set_mnt_opts(struct super_block *sb,
+                               struct security_mnt_opts *opts,
+                               unsigned long kern_flags,
+                               unsigned long *set_kern_flags);
 int security_sb_clone_mnt_opts(const struct super_block *oldsb,
                                struct super_block *newsb);
 int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
+int security_dentry_init_security(struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen);
 
 int security_inode_alloc(struct inode *inode);
 void security_inode_free(struct inode *inode);
@@ -1841,6 +1874,7 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode);
 int security_getprocattr(struct task_struct *p, char *name, char **value);
 int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size);
 int security_netlink_send(struct sock *sk, struct sk_buff *skb);
+int security_ismaclabel(const char *name);
 int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
 int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
 void security_release_secctx(char *secdata, u32 seclen);
@@ -2012,7 +2046,9 @@ static inline int security_sb_pivotroot(struct path *old_path,
 }
 
 static inline int security_sb_set_mnt_opts(struct super_block *sb,
-                                          struct security_mnt_opts *opts)
+                                          struct security_mnt_opts *opts,
+                                          unsigned long kern_flags,
+                                          unsigned long *set_kern_flags)
 {
        return 0;
 }
@@ -2036,6 +2072,16 @@ static inline int security_inode_alloc(struct inode *inode)
 static inline void security_inode_free(struct inode *inode)
 { }
 
+static inline int security_dentry_init_security(struct dentry *dentry,
+                                                int mode,
+                                                struct qstr *name,
+                                                void **ctx,
+                                                u32 *ctxlen)
+{
+       return -EOPNOTSUPP;
+}
+
+
 static inline int security_inode_init_security(struct inode *inode,
                                                struct inode *dir,
                                                const struct qstr *qstr,
@@ -2521,6 +2567,11 @@ static inline int security_netlink_send(struct sock *sk, struct sk_buff *skb)
        return cap_netlink_send(sk, skb);
 }
 
+static inline int security_ismaclabel(const char *name)
+{
+       return 0;
+}
+
 static inline int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 {
        return -EOPNOTSUPP;
index 53d4265..976ce3a 100644 (file)
@@ -12,10 +12,12 @@ struct task_struct;
 struct sem_array {
        struct kern_ipc_perm    ____cacheline_aligned_in_smp
                                sem_perm;       /* permissions .. see ipc.h */
-       time_t                  sem_otime;      /* last semop time */
        time_t                  sem_ctime;      /* last change time */
        struct sem              *sem_base;      /* ptr to first semaphore in array */
-       struct list_head        sem_pending;    /* pending operations to be processed */
+       struct list_head        pending_alter;  /* pending operations */
+                                               /* that alter the array */
+       struct list_head        pending_const;  /* pending complex operations */
+                                               /* that do not alter semvals */
        struct list_head        list_id;        /* undo requests on this array */
        int                     sem_nsems;      /* no. of semaphores in array */
        int                     complex_count;  /* pending complex operations */
index 2da29ac..4e32edc 100644 (file)
@@ -173,4 +173,10 @@ extern struct hlist_node *seq_hlist_start_head_rcu(struct hlist_head *head,
 extern struct hlist_node *seq_hlist_next_rcu(void *v,
                                                   struct hlist_head *head,
                                                   loff_t *ppos);
+
+/* Helpers for iterating over per-cpu hlist_head-s in seq_files */
+extern struct hlist_node *seq_hlist_start_percpu(struct hlist_head __percpu *head, int *cpu, loff_t pos);
+
+extern struct hlist_node *seq_hlist_next_percpu(void *v, struct hlist_head __percpu *head, int *cpu, loff_t *pos);
+
 #endif
index dec1748..5afefa0 100644 (file)
@@ -319,6 +319,8 @@ enum {
        SKB_GSO_GRE = 1 << 6,
 
        SKB_GSO_UDP_TUNNEL = 1 << 7,
+
+       SKB_GSO_MPLS = 1 << 8,
 };
 
 #if BITS_PER_LONG > 32
@@ -384,11 +386,13 @@ typedef unsigned char *sk_buff_data_t;
  *     @no_fcs:  Request NIC to treat last 4 bytes as Ethernet FCS
  *     @dma_cookie: a cookie to one of several possible DMA operations
  *             done by skb DMA functions
+  *    @napi_id: id of the NAPI struct this skb came from
  *     @secmark: security marking
  *     @mark: Generic packet mark
  *     @dropcount: total number of sk_receive_queue overflows
  *     @vlan_proto: vlan encapsulation protocol
  *     @vlan_tci: vlan tag control information
+ *     @inner_protocol: Protocol (encapsulation)
  *     @inner_transport_header: Inner transport layer header (encapsulation)
  *     @inner_network_header: Network layer header (encapsulation)
  *     @inner_mac_header: Link layer header (encapsulation)
@@ -497,8 +501,11 @@ struct sk_buff {
        /* 7/9 bit hole (depending on ndisc_nodetype presence) */
        kmemcheck_bitfield_end(flags2);
 
-#ifdef CONFIG_NET_DMA
-       dma_cookie_t            dma_cookie;
+#if defined CONFIG_NET_DMA || defined CONFIG_NET_LL_RX_POLL
+       union {
+               unsigned int    napi_id;
+               dma_cookie_t    dma_cookie;
+       };
 #endif
 #ifdef CONFIG_NETWORK_SECMARK
        __u32                   secmark;
@@ -509,12 +516,13 @@ struct sk_buff {
                __u32           reserved_tailroom;
        };
 
-       sk_buff_data_t          inner_transport_header;
-       sk_buff_data_t          inner_network_header;
-       sk_buff_data_t          inner_mac_header;
-       sk_buff_data_t          transport_header;
-       sk_buff_data_t          network_header;
-       sk_buff_data_t          mac_header;
+       __be16                  inner_protocol;
+       __u16                   inner_transport_header;
+       __u16                   inner_network_header;
+       __u16                   inner_mac_header;
+       __u16                   transport_header;
+       __u16                   network_header;
+       __u16                   mac_header;
        /* These elements must be at the end, see alloc_skb() for details.  */
        sk_buff_data_t          tail;
        sk_buff_data_t          end;
@@ -1388,6 +1396,7 @@ static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset)
        skb_reset_tail_pointer(skb);
        skb->tail += offset;
 }
+
 #else /* NET_SKBUFF_DATA_USES_OFFSET */
 static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb)
 {
@@ -1528,7 +1537,6 @@ static inline void skb_reset_mac_len(struct sk_buff *skb)
        skb->mac_len = skb->network_header - skb->mac_header;
 }
 
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
 static inline unsigned char *skb_inner_transport_header(const struct sk_buff
                                                        *skb)
 {
@@ -1582,7 +1590,7 @@ static inline void skb_set_inner_mac_header(struct sk_buff *skb,
 }
 static inline bool skb_transport_header_was_set(const struct sk_buff *skb)
 {
-       return skb->transport_header != ~0U;
+       return skb->transport_header != (typeof(skb->transport_header))~0U;
 }
 
 static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
@@ -1625,7 +1633,7 @@ static inline unsigned char *skb_mac_header(const struct sk_buff *skb)
 
 static inline int skb_mac_header_was_set(const struct sk_buff *skb)
 {
-       return skb->mac_header != ~0U;
+       return skb->mac_header != (typeof(skb->mac_header))~0U;
 }
 
 static inline void skb_reset_mac_header(struct sk_buff *skb)
@@ -1639,112 +1647,6 @@ static inline void skb_set_mac_header(struct sk_buff *skb, const int offset)
        skb->mac_header += offset;
 }
 
-#else /* NET_SKBUFF_DATA_USES_OFFSET */
-static inline unsigned char *skb_inner_transport_header(const struct sk_buff
-                                                       *skb)
-{
-       return skb->inner_transport_header;
-}
-
-static inline void skb_reset_inner_transport_header(struct sk_buff *skb)
-{
-       skb->inner_transport_header = skb->data;
-}
-
-static inline void skb_set_inner_transport_header(struct sk_buff *skb,
-                                                  const int offset)
-{
-       skb->inner_transport_header = skb->data + offset;
-}
-
-static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb)
-{
-       return skb->inner_network_header;
-}
-
-static inline void skb_reset_inner_network_header(struct sk_buff *skb)
-{
-       skb->inner_network_header = skb->data;
-}
-
-static inline void skb_set_inner_network_header(struct sk_buff *skb,
-                                               const int offset)
-{
-       skb->inner_network_header = skb->data + offset;
-}
-
-static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb)
-{
-       return skb->inner_mac_header;
-}
-
-static inline void skb_reset_inner_mac_header(struct sk_buff *skb)
-{
-       skb->inner_mac_header = skb->data;
-}
-
-static inline void skb_set_inner_mac_header(struct sk_buff *skb,
-                                               const int offset)
-{
-       skb->inner_mac_header = skb->data + offset;
-}
-static inline bool skb_transport_header_was_set(const struct sk_buff *skb)
-{
-       return skb->transport_header != NULL;
-}
-
-static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
-{
-       return skb->transport_header;
-}
-
-static inline void skb_reset_transport_header(struct sk_buff *skb)
-{
-       skb->transport_header = skb->data;
-}
-
-static inline void skb_set_transport_header(struct sk_buff *skb,
-                                           const int offset)
-{
-       skb->transport_header = skb->data + offset;
-}
-
-static inline unsigned char *skb_network_header(const struct sk_buff *skb)
-{
-       return skb->network_header;
-}
-
-static inline void skb_reset_network_header(struct sk_buff *skb)
-{
-       skb->network_header = skb->data;
-}
-
-static inline void skb_set_network_header(struct sk_buff *skb, const int offset)
-{
-       skb->network_header = skb->data + offset;
-}
-
-static inline unsigned char *skb_mac_header(const struct sk_buff *skb)
-{
-       return skb->mac_header;
-}
-
-static inline int skb_mac_header_was_set(const struct sk_buff *skb)
-{
-       return skb->mac_header != NULL;
-}
-
-static inline void skb_reset_mac_header(struct sk_buff *skb)
-{
-       skb->mac_header = skb->data;
-}
-
-static inline void skb_set_mac_header(struct sk_buff *skb, const int offset)
-{
-       skb->mac_header = skb->data + offset;
-}
-#endif /* NET_SKBUFF_DATA_USES_OFFSET */
-
 static inline void skb_probe_transport_header(struct sk_buff *skb,
                                              const int offset_hint)
 {
@@ -2483,6 +2385,7 @@ extern void              skb_split(struct sk_buff *skb,
                                 struct sk_buff *skb1, const u32 len);
 extern int            skb_shift(struct sk_buff *tgt, struct sk_buff *skb,
                                 int shiftlen);
+extern void           skb_scrub_packet(struct sk_buff *skb);
 
 extern struct sk_buff *skb_segment(struct sk_buff *skb,
                                   netdev_features_t features);
index afe79d4..6535e47 100644 (file)
@@ -20,6 +20,18 @@ struct ssb_pflash {
        u32 window_size;
 };
 
+#ifdef CONFIG_SSB_SFLASH
+struct ssb_sflash {
+       bool present;
+       u32 window;
+       u32 blocksize;
+       u16 numblocks;
+       u32 size;
+
+       void *priv;
+};
+#endif
+
 struct ssb_mipscore {
        struct ssb_device *dev;
 
@@ -27,6 +39,9 @@ struct ssb_mipscore {
        struct ssb_serial_port serial_ports[4];
 
        struct ssb_pflash pflash;
+#ifdef CONFIG_SSB_SFLASH
+       struct ssb_sflash sflash;
+#endif
 };
 
 extern void ssb_mipscore_init(struct ssb_mipscore *mcore);
index 3a72569..f9f931c 100644 (file)
 #define SSB_SPROMSIZE_WORDS_R4         220
 #define SSB_SPROMSIZE_BYTES_R123       (SSB_SPROMSIZE_WORDS_R123 * sizeof(u16))
 #define SSB_SPROMSIZE_BYTES_R4         (SSB_SPROMSIZE_WORDS_R4 * sizeof(u16))
+#define SSB_SPROMSIZE_WORDS_R10                230
 #define SSB_SPROM_BASE1                        0x1000
 #define SSB_SPROM_BASE31               0x0800
 #define SSB_SPROM_REVISION             0x007E
index c1b3ed3..9e495d3 100644 (file)
@@ -80,6 +80,10 @@ struct stmmac_mdio_bus_data {
        unsigned int phy_mask;
        int *irqs;
        int probed_phy_irq;
+#ifdef CONFIG_OF
+       int reset_gpio, active_low;
+       u32 delays[3];
+#endif
 };
 
 struct stmmac_dma_cfg {
index 84ca436..6d87035 100644 (file)
@@ -88,15 +88,6 @@ struct rpc_task {
                                tk_rebind_retry : 2;
 };
 
-/* support walking a list of tasks on a wait queue */
-#define        task_for_each(task, pos, head) \
-       list_for_each(pos, head) \
-               if ((task=list_entry(pos, struct rpc_task, u.tk_wait.list)),1)
-
-#define        task_for_first(task, head) \
-       if (!list_empty(head) &&  \
-           ((task=list_entry((head)->next, struct rpc_task, u.tk_wait.list)),1))
-
 typedef void                   (*rpc_action)(struct rpc_task *);
 
 struct rpc_call_ops {
@@ -238,7 +229,6 @@ struct rpc_task *rpc_wake_up_first(struct rpc_wait_queue *,
                                        bool (*)(struct rpc_task *, void *),
                                        void *);
 void           rpc_wake_up_status(struct rpc_wait_queue *, int);
-int            rpc_queue_empty(struct rpc_wait_queue *);
 void           rpc_delay(struct rpc_task *, unsigned long);
 void *         rpc_malloc(struct rpc_task *, size_t);
 void           rpc_free(void *);
@@ -259,16 +249,6 @@ static inline int rpc_wait_for_completion_task(struct rpc_task *task)
        return __rpc_wait_for_completion_task(task, NULL);
 }
 
-static inline void rpc_task_set_priority(struct rpc_task *task, unsigned char prio)
-{
-       task->tk_priority = prio - RPC_PRIORITY_LOW;
-}
-
-static inline int rpc_task_has_priority(struct rpc_task *task, unsigned char prio)
-{
-       return (task->tk_priority + RPC_PRIORITY_LOW == prio);
-}
-
 #if defined(RPC_DEBUG) || defined (RPC_TRACEPOINTS)
 static inline const char * rpc_qname(const struct rpc_wait_queue *q)
 {
index 5adbc33..472120b 100644 (file)
@@ -246,7 +246,6 @@ struct tcp_sock {
 
        /* from STCP, retrans queue hinting */
        struct sk_buff* lost_skb_hint;
-       struct sk_buff *scoreboard_skb_hint;
        struct sk_buff *retransmit_skb_hint;
 
        struct sk_buff_head     out_of_order_queue; /* Out of order segments go here */
index ea7168a..617c01b 100644 (file)
@@ -15,6 +15,7 @@
 #define _LINUX_VEXPRESS_H
 
 #include <linux/device.h>
+#include <linux/reboot.h>
 
 #define VEXPRESS_SITE_MB               0
 #define VEXPRESS_SITE_DB1              1
index e94c75d..36d36cc 100644 (file)
@@ -63,6 +63,10 @@ void virtqueue_disable_cb(struct virtqueue *vq);
 
 bool virtqueue_enable_cb(struct virtqueue *vq);
 
+unsigned virtqueue_enable_cb_prepare(struct virtqueue *vq);
+
+bool virtqueue_poll(struct virtqueue *vq, unsigned);
+
 bool virtqueue_enable_cb_delayed(struct virtqueue *vq);
 
 void *virtqueue_detach_unused_buf(struct virtqueue *vq);
index dd0a2c8..4b8a891 100644 (file)
 struct vm_area_struct;         /* vma defining user mapping in mm_types.h */
 
 /* bits in flags of vmalloc's vm_struct below */
-#define VM_IOREMAP     0x00000001      /* ioremap() and friends */
-#define VM_ALLOC       0x00000002      /* vmalloc() */
-#define VM_MAP         0x00000004      /* vmap()ed pages */
-#define VM_USERMAP     0x00000008      /* suitable for remap_vmalloc_range */
-#define VM_VPAGES      0x00000010      /* buffer for pages was vmalloc'ed */
-#define VM_UNLIST      0x00000020      /* vm_struct is not listed in vmlist */
+#define VM_IOREMAP             0x00000001      /* ioremap() and friends */
+#define VM_ALLOC               0x00000002      /* vmalloc() */
+#define VM_MAP                 0x00000004      /* vmap()ed pages */
+#define VM_USERMAP             0x00000008      /* suitable for remap_vmalloc_range */
+#define VM_VPAGES              0x00000010      /* buffer for pages was vmalloc'ed */
+#define VM_UNINITIALIZED       0x00000020      /* vm_struct is not fully initialized */
 /* bits [20..32] reserved for arch specific ioremap internals */
 
 /*
index abfe117..4e198ca 100644 (file)
@@ -47,11 +47,16 @@ enum wb_reason {
        WB_REASON_LAPTOP_TIMER,
        WB_REASON_FREE_MORE_MEM,
        WB_REASON_FS_FREE_SPACE,
+       /*
+        * There is no bdi forker thread any more and works are done
+        * by emergency worker, however, this is TPs userland visible
+        * and we'll be exposing exactly the same information,
+        * so it has a mismatch name.
+        */
        WB_REASON_FORKER_THREAD,
 
        WB_REASON_MAX,
 };
-extern const char *wb_reason_name[];
 
 /*
  * A control structure which tells the writeback code what to do.  These are
@@ -95,7 +100,6 @@ int try_to_writeback_inodes_sb_nr(struct super_block *, unsigned long nr,
 void sync_inodes_sb(struct super_block *);
 long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages,
                                enum wb_reason reason);
-long wb_do_writeback(struct bdi_writeback *wb, int force_wait);
 void wakeup_flusher_threads(long nr_pages, enum wb_reason reason);
 void inode_wait_for_writeback(struct inode *inode);
 
index 06ef7e9..b8ffac7 100644 (file)
@@ -18,7 +18,7 @@ struct tcf_common {
        struct tcf_t                    tcfc_tm;
        struct gnet_stats_basic_packed  tcfc_bstats;
        struct gnet_stats_queue         tcfc_qstats;
-       struct gnet_stats_rate_est      tcfc_rate_est;
+       struct gnet_stats_rate_est64    tcfc_rate_est;
        spinlock_t                      tcfc_lock;
        struct rcu_head                 tcfc_rcu;
 };
index 21f7027..c7b181c 100644 (file)
@@ -86,6 +86,9 @@ extern int                    ipv6_dev_get_saddr(struct net *net,
                                               const struct in6_addr *daddr,
                                               unsigned int srcprefs,
                                               struct in6_addr *saddr);
+extern int                     __ipv6_get_lladdr(struct inet6_dev *idev,
+                                                 struct in6_addr *addr,
+                                                 unsigned char banned_flags);
 extern int                     ipv6_get_lladdr(struct net_device *dev,
                                                struct in6_addr *addr,
                                                unsigned char banned_flags);
@@ -155,6 +158,7 @@ extern bool ipv6_chk_mcast_addr(struct net_device *dev,
                                const struct in6_addr *group,
                                const struct in6_addr *src_addr);
 
+extern void ipv6_mc_dad_complete(struct inet6_dev *idev);
 /*
  * identify MLD packets for MLD filter exceptions
  */
index e0512aa..3c592cf 100644 (file)
@@ -107,7 +107,6 @@ enum {
        HCI_MGMT,
        HCI_PAIRABLE,
        HCI_SERVICE_CACHE,
-       HCI_LINK_KEYS,
        HCI_DEBUG_KEYS,
        HCI_UNREGISTER,
 
index 7cb6d36..f77885e 100644 (file)
@@ -117,13 +117,6 @@ struct oob_data {
        u8 randomizer[16];
 };
 
-struct le_scan_params {
-       u8 type;
-       u16 interval;
-       u16 window;
-       int timeout;
-};
-
 #define HCI_MAX_SHORT_NAME_LENGTH      10
 
 struct amp_assoc {
@@ -283,9 +276,6 @@ struct hci_dev {
 
        struct delayed_work     le_scan_disable;
 
-       struct work_struct      le_scan;
-       struct le_scan_params   le_scan_params;
-
        __s8                    adv_tx_power;
        __u8                    adv_data[HCI_MAX_AD_LENGTH];
        __u8                    adv_data_len;
@@ -432,6 +422,7 @@ void hci_inquiry_cache_update_resolve(struct hci_dev *hdev,
                                      struct inquiry_entry *ie);
 bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
                              bool name_known, bool *ssp);
+void hci_inquiry_cache_flush(struct hci_dev *hdev);
 
 /* ----- HCI Connections ----- */
 enum {
@@ -1114,6 +1105,16 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event);
                                         BIT(BDADDR_LE_PUBLIC) | \
                                         BIT(BDADDR_LE_RANDOM))
 
+/* These LE scan and inquiry parameters were chosen according to LE General
+ * Discovery Procedure specification.
+ */
+#define DISCOV_LE_SCAN_WIN             0x12
+#define DISCOV_LE_SCAN_INT             0x12
+#define DISCOV_LE_TIMEOUT              msecs_to_jiffies(10240)
+#define DISCOV_INTERLEAVED_TIMEOUT     msecs_to_jiffies(5120)
+#define DISCOV_INTERLEAVED_INQUIRY_LEN 0x04
+#define DISCOV_BREDR_INQUIRY_LEN       0x08
+
 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len);
 int mgmt_index_added(struct hci_dev *hdev);
 int mgmt_index_removed(struct hci_dev *hdev);
@@ -1169,10 +1170,7 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                      u8 ssp, u8 *eir, u16 eir_len);
 int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                     u8 addr_type, s8 rssi, u8 *name, u8 name_len);
-int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status);
-int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status);
 int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
-int mgmt_interleaved_discovery(struct hci_dev *hdev);
 int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 bool mgmt_valid_hdev(struct hci_dev *hdev);
@@ -1212,11 +1210,6 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
                                        u16 latency, u16 to_multiplier);
 void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
                                                        __u8 ltk[16]);
-int hci_do_inquiry(struct hci_dev *hdev, u8 length);
-int hci_cancel_inquiry(struct hci_dev *hdev);
-int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
-               int timeout);
-int hci_cancel_le_scan(struct hci_dev *hdev);
 
 u8 bdaddr_to_le(u8 bdaddr_type);
 
index fb94cf1..1a966af 100644 (file)
@@ -242,7 +242,7 @@ struct l2cap_conn_rsp {
 #define L2CAP_CID_SIGNALING    0x0001
 #define L2CAP_CID_CONN_LESS    0x0002
 #define L2CAP_CID_A2MP         0x0003
-#define L2CAP_CID_LE_DATA      0x0004
+#define L2CAP_CID_ATT          0x0004
 #define L2CAP_CID_LE_SIGNALING 0x0005
 #define L2CAP_CID_SMP          0x0006
 #define L2CAP_CID_DYN_START    0x0040
index 26b5b69..7b0730a 100644 (file)
@@ -188,6 +188,8 @@ struct ieee80211_channel {
  *     when used with 802.11g (on the 2.4 GHz band); filled by the
  *     core code when registering the wiphy.
  * @IEEE80211_RATE_ERP_G: This is an ERP rate in 802.11g mode.
+ * @IEEE80211_RATE_SUPPORTS_5MHZ: Rate can be used in 5 MHz mode
+ * @IEEE80211_RATE_SUPPORTS_10MHZ: Rate can be used in 10 MHz mode
  */
 enum ieee80211_rate_flags {
        IEEE80211_RATE_SHORT_PREAMBLE   = 1<<0,
@@ -195,6 +197,8 @@ enum ieee80211_rate_flags {
        IEEE80211_RATE_MANDATORY_B      = 1<<2,
        IEEE80211_RATE_MANDATORY_G      = 1<<3,
        IEEE80211_RATE_ERP_G            = 1<<4,
+       IEEE80211_RATE_SUPPORTS_5MHZ    = 1<<5,
+       IEEE80211_RATE_SUPPORTS_10MHZ   = 1<<6,
 };
 
 /**
@@ -432,6 +436,30 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
                             const struct cfg80211_chan_def *chandef,
                             u32 prohibited_flags);
 
+/**
+ * ieee80211_chandef_rate_flags - returns rate flags for a channel
+ *
+ * In some channel types, not all rates may be used - for example CCK
+ * rates may not be used in 5/10 MHz channels.
+ *
+ * @chandef: channel definition for the channel
+ *
+ * Returns: rate flags which apply for this channel
+ */
+static inline enum ieee80211_rate_flags
+ieee80211_chandef_rate_flags(struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               return IEEE80211_RATE_SUPPORTS_5MHZ;
+       case NL80211_CHAN_WIDTH_10:
+               return IEEE80211_RATE_SUPPORTS_10MHZ;
+       default:
+               break;
+       }
+       return 0;
+}
+
 /**
  * enum survey_info_flags - survey information flags
  *
@@ -753,6 +781,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
  * @STATION_INFO_LOCAL_PM: @local_pm filled
  * @STATION_INFO_PEER_PM: @peer_pm filled
  * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled
+ * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled
+ * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled
  */
 enum station_info_flags {
        STATION_INFO_INACTIVE_TIME      = 1<<0,
@@ -781,6 +811,8 @@ enum station_info_flags {
        STATION_INFO_NONPEER_PM         = 1<<23,
        STATION_INFO_RX_BYTES64         = 1<<24,
        STATION_INFO_TX_BYTES64         = 1<<25,
+       STATION_INFO_CHAIN_SIGNAL       = 1<<26,
+       STATION_INFO_CHAIN_SIGNAL_AVG   = 1<<27,
 };
 
 /**
@@ -857,6 +889,8 @@ struct sta_bss_parameters {
        u16 beacon_interval;
 };
 
+#define IEEE80211_MAX_CHAINS   4
+
 /**
  * struct station_info - station information
  *
@@ -874,6 +908,9 @@ struct sta_bss_parameters {
  *     For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_.
  * @signal_avg: Average signal strength, type depends on the wiphy's signal_type.
  *     For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_.
+ * @chains: bitmask for filled values in @chain_signal, @chain_signal_avg
+ * @chain_signal: per-chain signal strength of last received packet in dBm
+ * @chain_signal_avg: per-chain signal strength average in dBm
  * @txrate: current unicast bitrate from this station
  * @rxrate: current unicast bitrate to this station
  * @rx_packets: packets received from this station
@@ -909,6 +946,11 @@ struct station_info {
        u8 plink_state;
        s8 signal;
        s8 signal_avg;
+
+       u8 chains;
+       s8 chain_signal[IEEE80211_MAX_CHAINS];
+       s8 chain_signal_avg[IEEE80211_MAX_CHAINS];
+
        struct rate_info txrate;
        struct rate_info rxrate;
        u32 rx_packets;
@@ -947,6 +989,7 @@ struct station_info {
  * @MONITOR_FLAG_CONTROL: pass control frames
  * @MONITOR_FLAG_OTHER_BSS: disable BSSID filtering
  * @MONITOR_FLAG_COOK_FRAMES: report frames after processing
+ * @MONITOR_FLAG_ACTIVE: active monitor, ACKs frames on its MAC address
  */
 enum monitor_flags {
        MONITOR_FLAG_FCSFAIL            = 1<<NL80211_MNTR_FLAG_FCSFAIL,
@@ -954,6 +997,7 @@ enum monitor_flags {
        MONITOR_FLAG_CONTROL            = 1<<NL80211_MNTR_FLAG_CONTROL,
        MONITOR_FLAG_OTHER_BSS          = 1<<NL80211_MNTR_FLAG_OTHER_BSS,
        MONITOR_FLAG_COOK_FRAMES        = 1<<NL80211_MNTR_FLAG_COOK_FRAMES,
+       MONITOR_FLAG_ACTIVE             = 1<<NL80211_MNTR_FLAG_ACTIVE,
 };
 
 /**
@@ -1108,6 +1152,9 @@ struct bss_parameters {
  *     setting for new peer links.
  * @dot11MeshAwakeWindowDuration: The duration in TUs the STA will remain awake
  *     after transmitting its beacon.
+ * @plink_timeout: If no tx activity is seen from a STA we've established
+ *     peering with for longer than this time (in seconds), then remove it
+ *     from the STA's list of peers.  Default is 30 minutes.
  */
 struct mesh_config {
        u16 dot11MeshRetryTimeout;
@@ -1137,6 +1184,7 @@ struct mesh_config {
        u16 dot11MeshHWMPconfirmationInterval;
        enum nl80211_mesh_power_mode power_mode;
        u16 dot11MeshAwakeWindowDuration;
+       u32 plink_timeout;
 };
 
 /**
@@ -1147,6 +1195,7 @@ struct mesh_config {
  * @sync_method: which synchronization method to use
  * @path_sel_proto: which path selection protocol to use
  * @path_metric: which metric to use
+ * @auth_id: which authentication method this mesh is using
  * @ie: vendor information elements (optional)
  * @ie_len: length of vendor information elements
  * @is_authenticated: this mesh requires authentication
@@ -1155,6 +1204,7 @@ struct mesh_config {
  * @dtim_period: DTIM period to use
  * @beacon_interval: beacon interval to use
  * @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a]
+ * @basic_rates: basic rates to use when creating the mesh
  *
  * These parameters are fixed when the mesh is created.
  */
@@ -1165,6 +1215,7 @@ struct mesh_setup {
        u8 sync_method;
        u8 path_sel_proto;
        u8 path_metric;
+       u8 auth_id;
        const u8 *ie;
        u8 ie_len;
        bool is_authenticated;
@@ -1173,6 +1224,7 @@ struct mesh_setup {
        u8 dtim_period;
        u16 beacon_interval;
        int mcast_rate[IEEE80211_NUM_BANDS];
+       u32 basic_rates;
 };
 
 /**
@@ -1241,6 +1293,7 @@ struct cfg80211_ssid {
  * @scan_start: time (in jiffies) when the scan started
  * @wdev: the wireless device to scan for
  * @aborted: (internal) scan request was notified as aborted
+ * @notified: (internal) scan request was notified as done or aborted
  * @no_cck: used to send probe requests at non CCK rate in 2GHz band
  */
 struct cfg80211_scan_request {
@@ -1258,7 +1311,7 @@ struct cfg80211_scan_request {
        /* internal */
        struct wiphy *wiphy;
        unsigned long scan_start;
-       bool aborted;
+       bool aborted, notified;
        bool no_cck;
 
        /* keep last */
@@ -1406,7 +1459,8 @@ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie);
  * This structure provides information needed to complete IEEE 802.11
  * authentication.
  *
- * @bss: The BSS to authenticate with.
+ * @bss: The BSS to authenticate with, the callee must obtain a reference
+ *     to it if it needs to keep it.
  * @auth_type: Authentication type (algorithm)
  * @ie: Extra IEs to add to Authentication frame or %NULL
  * @ie_len: Length of ie buffer in octets
@@ -1444,11 +1498,10 @@ enum cfg80211_assoc_req_flags {
  *
  * This structure provides information needed to complete IEEE 802.11
  * (re)association.
- * @bss: The BSS to associate with. If the call is successful the driver
- *     is given a reference that it must release, normally via a call to
- *     cfg80211_send_rx_assoc(), or, if association timed out, with a
- *     call to cfg80211_put_bss() (in addition to calling
- *     cfg80211_send_assoc_timeout())
+ * @bss: The BSS to associate with. If the call is successful the driver is
+ *     given a reference that it must give back to cfg80211_send_rx_assoc()
+ *     or to cfg80211_assoc_timeout(). To ensure proper refcounting, new
+ *     association requests while already associating must be rejected.
  * @ie: Extra IEs to add to (Re)Association Request frame or %NULL
  * @ie_len: Length of ie buffer in octets
  * @use_mfp: Use management frame protection (IEEE 802.11w) in this association
@@ -1850,7 +1903,9 @@ struct cfg80211_update_ft_ies_params {
  * @get_mpath: get a mesh path for the given parameters
  * @dump_mpath: dump mesh path callback -- resume dump at index @idx
  * @join_mesh: join the mesh network with the specified parameters
+ *     (invoked with the wireless_dev mutex held)
  * @leave_mesh: leave the current mesh network
+ *     (invoked with the wireless_dev mutex held)
  *
  * @get_mesh_config: Get the current mesh configuration
  *
@@ -1877,20 +1932,28 @@ struct cfg80211_update_ft_ies_params {
  *     the scan/scan_done bracket too.
  *
  * @auth: Request to authenticate with the specified peer
+ *     (invoked with the wireless_dev mutex held)
  * @assoc: Request to (re)associate with the specified peer
+ *     (invoked with the wireless_dev mutex held)
  * @deauth: Request to deauthenticate from the specified peer
+ *     (invoked with the wireless_dev mutex held)
  * @disassoc: Request to disassociate from the specified peer
+ *     (invoked with the wireless_dev mutex held)
  *
  * @connect: Connect to the ESS with the specified parameters. When connected,
  *     call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
  *     If the connection fails for some reason, call cfg80211_connect_result()
  *     with the status from the AP.
+ *     (invoked with the wireless_dev mutex held)
  * @disconnect: Disconnect from the BSS/ESS.
+ *     (invoked with the wireless_dev mutex held)
  *
  * @join_ibss: Join the specified IBSS (or create if necessary). Once done, call
  *     cfg80211_ibss_joined(), also call that function when changing BSSID due
  *     to a merge.
+ *     (invoked with the wireless_dev mutex held)
  * @leave_ibss: Leave the IBSS.
+ *     (invoked with the wireless_dev mutex held)
  *
  * @set_mcast_rate: Set the specified multicast rate (only if vif is in ADHOC or
  *     MESH mode)
@@ -2307,6 +2370,7 @@ struct cfg80211_ops {
  *     responds to probe-requests in hardware.
  * @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX.
  * @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call.
+ * @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
  */
 enum wiphy_flags {
        WIPHY_FLAG_CUSTOM_REGULATORY            = BIT(0),
@@ -2330,6 +2394,7 @@ enum wiphy_flags {
        WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD        = BIT(19),
        WIPHY_FLAG_OFFCHAN_TX                   = BIT(20),
        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL        = BIT(21),
+       WIPHY_FLAG_SUPPORTS_5_10_MHZ            = BIT(22),
 };
 
 /**
@@ -2556,6 +2621,9 @@ struct wiphy_wowlan_support {
  *     may request, if implemented.
  *
  * @wowlan: WoWLAN support information
+ * @wowlan_config: current WoWLAN configuration; this should usually not be
+ *     used since access to it is necessarily racy, use the parameter passed
+ *     to the suspend() operation instead.
  *
  * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
  * @ht_capa_mod_mask:  Specify what ht_cap values can be over-ridden.
@@ -2622,7 +2690,8 @@ struct wiphy {
        u32 hw_version;
 
 #ifdef CONFIG_PM
-       struct wiphy_wowlan_support wowlan;
+       const struct wiphy_wowlan_support *wowlan;
+       struct cfg80211_wowlan *wowlan_config;
 #endif
 
        u16 max_remain_on_channel_duration;
@@ -2820,7 +2889,7 @@ struct cfg80211_cached_keys;
  * @current_bss: (private) Used by the internal configuration code
  * @channel: (private) Used by the internal configuration code to track
  *     the user-set AP, monitor and WDS channel
- * @preset_chan: (private) Used by the internal configuration code to
+ * @preset_chandef: (private) Used by the internal configuration code to
  *     track the channel to be used for AP later
  * @bssid: (private) Used by the internal configuration code
  * @ssid: (private) Used by the internal configuration code
@@ -2834,14 +2903,23 @@ struct cfg80211_cached_keys;
  *     by cfg80211 on change_interface
  * @mgmt_registrations: list of registrations for management frames
  * @mgmt_registrations_lock: lock for the list
- * @mtx: mutex used to lock data in this struct
- * @cleanup_work: work struct used for cleanup that can't be done directly
+ * @mtx: mutex used to lock data in this struct, may be used by drivers
+ *     and some API functions require it held
  * @beacon_interval: beacon interval used on this device for transmitting
  *     beacons, 0 when not valid
  * @address: The address for this device, valid only if @netdev is %NULL
  * @p2p_started: true if this is a P2P Device that has been started
  * @cac_started: true if DFS channel availability check has been started
  * @cac_start_time: timestamp (jiffies) when the dfs state was entered.
+ * @ps: powersave mode is enabled
+ * @ps_timeout: dynamic powersave timeout
+ * @ap_unexpected_nlportid: (private) netlink port ID of application
+ *     registered for unexpected class 3 frames (AP mode)
+ * @conn: (private) cfg80211 software SME connection state machine data
+ * @connect_keys: (private) keys to set after connection is established
+ * @ibss_fixed: (private) IBSS is using fixed BSSID
+ * @event_list: (private) list for internal event processing
+ * @event_lock: (private) lock for event list
  */
 struct wireless_dev {
        struct wiphy *wiphy;
@@ -2858,8 +2936,6 @@ struct wireless_dev {
 
        struct mutex mtx;
 
-       struct work_struct cleanup_work;
-
        bool use_4addr, p2p_started;
 
        u8 address[ETH_ALEN] __aligned(sizeof(u16));
@@ -2867,11 +2943,6 @@ struct wireless_dev {
        /* currently used for IBSS and SME - might be rearranged later */
        u8 ssid[IEEE80211_MAX_SSID_LEN];
        u8 ssid_len, mesh_id_len, mesh_id_up_len;
-       enum {
-               CFG80211_SME_IDLE,
-               CFG80211_SME_CONNECTING,
-               CFG80211_SME_CONNECTED,
-       } sme_state;
        struct cfg80211_conn *conn;
        struct cfg80211_cached_keys *connect_keys;
 
@@ -2989,6 +3060,15 @@ struct ieee80211_rate *
 ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
                            u32 basic_rates, int bitrate);
 
+/**
+ * ieee80211_mandatory_rates - get mandatory rates for a given band
+ * @sband: the band to look for rates in
+ *
+ * This function returns a bitmap of the mandatory rates for the given
+ * band, bits are set according to the rate position in the bitrates array.
+ */
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband);
+
 /*
  * Radiotap parsing functions -- for controlled injection support
  *
@@ -3392,122 +3472,87 @@ void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
 void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
 
 /**
- * cfg80211_send_rx_auth - notification of processed authentication
+ * cfg80211_rx_mlme_mgmt - notification of processed MLME management frame
  * @dev: network device
  * @buf: authentication frame (header + body)
  * @len: length of the frame data
  *
- * This function is called whenever an authentication has been processed in
- * station mode. The driver is required to call either this function or
- * cfg80211_send_auth_timeout() to indicate the result of cfg80211_ops::auth()
- * call. This function may sleep.
+ * This function is called whenever an authentication, disassociation or
+ * deauthentication frame has been received and processed in station mode.
+ * After being asked to authenticate via cfg80211_ops::auth() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ * After being asked to associate via cfg80211_ops::assoc() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ * While connected, the driver must calls this for received and processed
+ * disassociation and deauthentication frames. If the frame couldn't be used
+ * because it was unprotected, the driver must call the function
+ * cfg80211_rx_unprot_mlme_mgmt() instead.
+ *
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
  */
-void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
+void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len);
 
 /**
- * cfg80211_send_auth_timeout - notification of timed out authentication
+ * cfg80211_auth_timeout - notification of timed out authentication
  * @dev: network device
  * @addr: The MAC address of the device with which the authentication timed out
  *
- * This function may sleep.
+ * This function may sleep. The caller must hold the corresponding wdev's
+ * mutex.
  */
-void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr);
+void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr);
 
 /**
- * cfg80211_send_rx_assoc - notification of processed association
+ * cfg80211_rx_assoc_resp - notification of processed association response
  * @dev: network device
- * @bss: the BSS struct association was requested for, the struct reference
- *     is owned by cfg80211 after this call
- * @buf: (re)association response frame (header + body)
+ * @bss: the BSS that association was requested with, ownership of the pointer
+ *     moves to cfg80211 in this call
+ * @buf: authentication frame (header + body)
  * @len: length of the frame data
  *
- * This function is called whenever a (re)association response has been
- * processed in station mode. The driver is required to call either this
- * function or cfg80211_send_assoc_timeout() to indicate the result of
- * cfg80211_ops::assoc() call. This function may sleep.
+ * After being asked to associate via cfg80211_ops::assoc() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ *
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
  */
-void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
+void cfg80211_rx_assoc_resp(struct net_device *dev,
+                           struct cfg80211_bss *bss,
                            const u8 *buf, size_t len);
 
 /**
- * cfg80211_send_assoc_timeout - notification of timed out association
+ * cfg80211_assoc_timeout - notification of timed out association
  * @dev: network device
- * @addr: The MAC address of the device with which the association timed out
+ * @bss: The BSS entry with which association timed out.
  *
- * This function may sleep.
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
  */
-void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr);
+void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss);
 
 /**
- * cfg80211_send_deauth - notification of processed deauthentication
+ * cfg80211_tx_mlme_mgmt - notification of transmitted deauth/disassoc frame
  * @dev: network device
- * @buf: deauthentication frame (header + body)
+ * @buf: 802.11 frame (header + body)
  * @len: length of the frame data
  *
  * This function is called whenever deauthentication has been processed in
  * station mode. This includes both received deauthentication frames and
- * locally generated ones. This function may sleep.
- */
-void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len);
-
-/**
- * __cfg80211_send_deauth - notification of processed deauthentication
- * @dev: network device
- * @buf: deauthentication frame (header + body)
- * @len: length of the frame data
- *
- * Like cfg80211_send_deauth(), but doesn't take the wdev lock.
- */
-void __cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len);
-
-/**
- * cfg80211_send_disassoc - notification of processed disassociation
- * @dev: network device
- * @buf: disassociation response frame (header + body)
- * @len: length of the frame data
- *
- * This function is called whenever disassociation has been processed in
- * station mode. This includes both received disassociation frames and locally
- * generated ones. This function may sleep.
- */
-void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len);
-
-/**
- * __cfg80211_send_disassoc - notification of processed disassociation
- * @dev: network device
- * @buf: disassociation response frame (header + body)
- * @len: length of the frame data
- *
- * Like cfg80211_send_disassoc(), but doesn't take the wdev lock.
+ * locally generated ones. This function may sleep. The caller must hold the
+ * corresponding wdev's mutex.
  */
-void __cfg80211_send_disassoc(struct net_device *dev, const u8 *buf,
-       size_t len);
+void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len);
 
 /**
- * cfg80211_send_unprot_deauth - notification of unprotected deauthentication
+ * cfg80211_rx_unprot_mlme_mgmt - notification of unprotected mlme mgmt frame
  * @dev: network device
  * @buf: deauthentication frame (header + body)
  * @len: length of the frame data
  *
- * This function is called whenever a received Deauthentication frame has been
- * dropped in station mode because of MFP being used but the Deauthentication
+ * This function is called whenever a received deauthentication or dissassoc
+ * frame has been dropped in station mode because of MFP being used but the
  * frame was not protected. This function may sleep.
  */
-void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
-                                size_t len);
-
-/**
- * cfg80211_send_unprot_disassoc - notification of unprotected disassociation
- * @dev: network device
- * @buf: disassociation frame (header + body)
- * @len: length of the frame data
- *
- * This function is called whenever a received Disassociation frame has been
- * dropped in station mode because of MFP being used but the Disassociation
- * frame was not protected. This function may sleep.
- */
-void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
-                                  size_t len);
+void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev,
+                                 const u8 *buf, size_t len);
 
 /**
  * cfg80211_michael_mic_failure - notification of Michael MIC failure (TKIP)
@@ -4153,6 +4198,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
  * cfg80211_crit_proto_stopped() - indicate critical protocol stopped by driver.
  *
  * @wdev: the wireless device for which critical protocol is stopped.
+ * @gfp: allocation flags
  *
  * This function can be called by the driver to indicate it has reverted
  * operation back to normal. One reason could be that the duration given
index a79b6cf..cf8439b 100644 (file)
@@ -30,7 +30,7 @@ extern int gnet_stats_copy_basic(struct gnet_dump *d,
                                 struct gnet_stats_basic_packed *b);
 extern int gnet_stats_copy_rate_est(struct gnet_dump *d,
                                    const struct gnet_stats_basic_packed *b,
-                                   struct gnet_stats_rate_est *r);
+                                   struct gnet_stats_rate_est64 *r);
 extern int gnet_stats_copy_queue(struct gnet_dump *d,
                                 struct gnet_stats_queue *q);
 extern int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len);
@@ -38,13 +38,13 @@ extern int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len);
 extern int gnet_stats_finish_copy(struct gnet_dump *d);
 
 extern int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
-                            struct gnet_stats_rate_est *rate_est,
+                            struct gnet_stats_rate_est64 *rate_est,
                             spinlock_t *stats_lock, struct nlattr *opt);
 extern void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
-                              struct gnet_stats_rate_est *rate_est);
+                              struct gnet_stats_rate_est64 *rate_est);
 extern int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
-                                struct gnet_stats_rate_est *rate_est,
+                                struct gnet_stats_rate_est64 *rate_est,
                                 spinlock_t *stats_lock, struct nlattr *opt);
 extern bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
-                                const struct gnet_stats_rate_est *rate_est);
+                                const struct gnet_stats_rate_est64 *rate_est);
 #endif
index 9f03a39..57e4afd 100644 (file)
@@ -7,6 +7,7 @@
 #define GREPROTO_CISCO         0
 #define GREPROTO_PPTP          1
 #define GREPROTO_MAX           2
+#define GRE_IP_PROTO_MAX       2
 
 struct gre_protocol {
        int  (*handler)(struct sk_buff *skb);
@@ -22,6 +23,36 @@ struct gre_base_hdr {
 int gre_add_protocol(const struct gre_protocol *proto, u8 version);
 int gre_del_protocol(const struct gre_protocol *proto, u8 version);
 
+struct gre_cisco_protocol {
+       int (*handler)(struct sk_buff *skb, const struct tnl_ptk_info *tpi);
+       int (*err_handler)(struct sk_buff *skb, u32 info,
+                          const struct tnl_ptk_info *tpi);
+       u8 priority;
+};
+
+int gre_cisco_register(struct gre_cisco_protocol *proto);
+int gre_cisco_unregister(struct gre_cisco_protocol *proto);
+
+int gre_offload_init(void);
+void gre_offload_exit(void);
+
+void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
+                     int hdr_len);
+struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum);
+
+static inline int ip_gre_calc_hlen(__be16 o_flags)
+{
+       int addend = 4;
+
+       if (o_flags&TUNNEL_CSUM)
+               addend += 4;
+       if (o_flags&TUNNEL_KEY)
+               addend += 4;
+       if (o_flags&TUNNEL_SEQ)
+               addend += 4;
+       return addend;
+}
+
 static inline __be16 gre_flags_to_tnl_flags(__be16 flags)
 {
        __be16 tflags = 0;
index c399963..c6d07cb 100644 (file)
@@ -269,6 +269,7 @@ enum ieee80211_radiotap_type {
 #define IEEE80211_RADIOTAP_MCS_HAVE_GI         0x04
 #define IEEE80211_RADIOTAP_MCS_HAVE_FMT                0x08
 #define IEEE80211_RADIOTAP_MCS_HAVE_FEC                0x10
+#define IEEE80211_RADIOTAP_MCS_HAVE_STBC       0x20
 
 #define IEEE80211_RADIOTAP_MCS_BW_MASK         0x03
 #define                IEEE80211_RADIOTAP_MCS_BW_20    0
@@ -278,6 +279,12 @@ enum ieee80211_radiotap_type {
 #define IEEE80211_RADIOTAP_MCS_SGI             0x04
 #define IEEE80211_RADIOTAP_MCS_FMT_GF          0x08
 #define IEEE80211_RADIOTAP_MCS_FEC_LDPC                0x10
+#define IEEE80211_RADIOTAP_MCS_STBC_MASK       0x60
+#define                IEEE80211_RADIOTAP_MCS_STBC_1   1
+#define                IEEE80211_RADIOTAP_MCS_STBC_2   2
+#define                IEEE80211_RADIOTAP_MCS_STBC_3   3
+
+#define IEEE80211_RADIOTAP_MCS_STBC_SHIFT      5
 
 /* For IEEE80211_RADIOTAP_AMPDU_STATUS */
 #define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN                0x0001
index 100fb8c..736b5fb 100644 (file)
@@ -50,7 +50,7 @@ struct inet6_ifaddr {
 
        int                     state;
 
-       __u8                    probes;
+       __u8                    dad_probes;
        __u8                    flags;
 
        __u16                   scope;
@@ -58,7 +58,7 @@ struct inet6_ifaddr {
        unsigned long           cstamp; /* created timestamp */
        unsigned long           tstamp; /* updated timestamp */
 
-       struct timer_list       timer;
+       struct timer_list       dad_timer;
 
        struct inet6_dev        *idev;
        struct rt6_info         *rt;
@@ -74,6 +74,7 @@ struct inet6_ifaddr {
        bool                    tokenized;
 
        struct rcu_head         rcu;
+       struct in6_addr         peer_addr;
 };
 
 struct ip6_sf_socklist {
@@ -165,6 +166,7 @@ struct inet6_dev {
        struct net_device       *dev;
 
        struct list_head        addr_list;
+       int                     valid_ll_addr_cnt;
 
        struct ifmcaddr6        *mc_list;
        struct ifmcaddr6        *mc_tomb;
@@ -172,10 +174,12 @@ struct inet6_dev {
        unsigned char           mc_qrv;
        unsigned char           mc_gq_running;
        unsigned char           mc_ifc_count;
+       unsigned char           mc_dad_count;
        unsigned long           mc_v1_seen;
        unsigned long           mc_maxdelay;
        struct timer_list       mc_gq_timer;    /* general query timer */
        struct timer_list       mc_ifc_timer;   /* interface change timer */
+       struct timer_list       mc_dad_timer;   /* dad complete mc timer */
 
        struct ifacaddr6        *ac_list;
        rwlock_t                lock;
@@ -192,9 +196,12 @@ struct inet6_dev {
        struct in6_addr         token;
 
        struct neigh_parms      *nd_parms;
-       struct inet6_dev        *next;
        struct ipv6_devconf     cnf;
        struct ipv6_devstat     stats;
+
+       struct timer_list       rs_timer;
+       __u8                    rs_probes;
+
        unsigned long           tstamp; /* ipv6InterfaceTable update timestamp */
        struct rcu_head         rcu;
 };
index aab7375..3bd2279 100644 (file)
@@ -134,12 +134,14 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb)
 {
        switch (skb->protocol) {
        case cpu_to_be16(ETH_P_IP):
-               if (skb->network_header + sizeof(struct iphdr) <= skb->tail)
+               if (skb_network_header(skb) + sizeof(struct iphdr) <=
+                   skb_tail_pointer(skb))
                        return IP_ECN_set_ce(ip_hdr(skb));
                break;
 
        case cpu_to_be16(ETH_P_IPV6):
-               if (skb->network_header + sizeof(struct ipv6hdr) <= skb->tail)
+               if (skb_network_header(skb) + sizeof(struct ipv6hdr) <=
+                   skb_tail_pointer(skb))
                        return IP6_ECN_set_ce(ipv6_hdr(skb));
                break;
        }
index 7235ae7..b21a7f0 100644 (file)
@@ -32,7 +32,6 @@
  *
  * @faddr - Saved first hop address
  * @nexthop - Saved nexthop address in LSRR and SSRR
- * @is_data - Options in __data, rather than skb
  * @is_strictroute - Strict source route
  * @srr_is_hit - Packet destination addr was our one
  * @is_changed - IP checksum more not valid
index e49db91..cbf2be3 100644 (file)
@@ -51,11 +51,13 @@ struct rtable;
 
 struct fib_nh_exception {
        struct fib_nh_exception __rcu   *fnhe_next;
+       int                             fnhe_genid;
        __be32                          fnhe_daddr;
        u32                             fnhe_pmtu;
        __be32                          fnhe_gw;
        unsigned long                   fnhe_expires;
-       struct rtable __rcu             *fnhe_rth;
+       struct rtable __rcu             *fnhe_rth_input;
+       struct rtable __rcu             *fnhe_rth_output;
        unsigned long                   fnhe_stamp;
 };
 
@@ -289,7 +291,6 @@ static inline int fib_num_tclassid_users(struct net *net)
 extern int ip_fib_check_default(__be32 gw, struct net_device *dev);
 extern int fib_sync_down_dev(struct net_device *dev, int force);
 extern int fib_sync_down_addr(struct net *net, __be32 local);
-extern void fib_update_nh_saddrs(struct net_device *dev);
 extern int fib_sync_up(struct net_device *dev);
 extern void fib_select_multipath(struct fib_result *res);
 
index 09b1360..781b3cf 100644 (file)
@@ -42,6 +42,7 @@ struct ip_tunnel {
        struct ip_tunnel __rcu  *next;
        struct hlist_node hash_node;
        struct net_device       *dev;
+       struct net              *net;   /* netns for packet i/o */
 
        int             err_count;      /* Number of arrived ICMP errors */
        unsigned long   err_time;       /* Time when the last ICMP error
@@ -73,6 +74,7 @@ struct ip_tunnel {
 #define TUNNEL_REC     __cpu_to_be16(0x20)
 #define TUNNEL_VERSION __cpu_to_be16(0x40)
 #define TUNNEL_NO_KEY  __cpu_to_be16(0x80)
+#define TUNNEL_DONT_FRAGMENT    __cpu_to_be16(0x0100)
 
 struct tnl_ptk_info {
        __be16 flags;
@@ -92,6 +94,8 @@ struct ip_tunnel_net {
        struct net_device *fb_tunnel_dev;
 };
 
+#ifdef CONFIG_INET
+
 int ip_tunnel_init(struct net_device *dev);
 void ip_tunnel_uninit(struct net_device *dev);
 void  ip_tunnel_dellink(struct net_device *dev, struct list_head *head);
@@ -101,7 +105,7 @@ int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id,
 void ip_tunnel_delete_net(struct ip_tunnel_net *itn);
 
 void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
-                   const struct iphdr *tnl_params);
+                   const struct iphdr *tnl_params, const u8 protocol);
 int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd);
 int ip_tunnel_change_mtu(struct net_device *dev, int new_mtu);
 
@@ -155,23 +159,31 @@ static inline void tunnel_ip_select_ident(struct sk_buff *skb,
                                  (skb_shinfo(skb)->gso_segs ?: 1) - 1);
 }
 
-static inline void iptunnel_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-       int err;
-       int pkt_len = skb->len - skb_transport_offset(skb);
-       struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats);
+int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto);
+int iptunnel_xmit(struct net *net, struct rtable *rt,
+                 struct sk_buff *skb,
+                 __be32 src, __be32 dst, __u8 proto,
+                 __u8 tos, __u8 ttl, __be16 df);
 
-       nf_reset(skb);
+static inline void iptunnel_xmit_stats(int err,
+                                      struct net_device_stats *err_stats,
+                                      struct pcpu_tstats __percpu *stats)
+{
+       if (err > 0) {
+               struct pcpu_tstats *tstats = this_cpu_ptr(stats);
 
-       err = ip_local_out(skb);
-       if (likely(net_xmit_eval(err) == 0)) {
                u64_stats_update_begin(&tstats->syncp);
-               tstats->tx_bytes += pkt_len;
+               tstats->tx_bytes += err;
                tstats->tx_packets++;
                u64_stats_update_end(&tstats->syncp);
+       } else if (err < 0) {
+               err_stats->tx_errors++;
+               err_stats->tx_aborted_errors++;
        } else {
-               dev->stats.tx_errors++;
-               dev->stats.tx_aborted_errors++;
+               err_stats->tx_dropped++;
        }
 }
+
+#endif /* CONFIG_INET */
+
 #endif /* __NET_IP_TUNNELS_H */
index 4c062cc..f0d70f0 100644 (file)
@@ -197,31 +197,6 @@ ip_vs_fill_iph_skb(int af, const struct sk_buff *skb, struct ip_vs_iphdr *iphdr)
        }
 }
 
-/* This function is a faster version of ip_vs_fill_iph_skb().
- * Where we only populate {s,d}addr (and avoid calling ipv6_find_hdr()).
- * This is used by the some of the ip_vs_*_schedule() functions.
- * (Mostly done to avoid ABI breakage of external schedulers)
- */
-static inline void
-ip_vs_fill_iph_addr_only(int af, const struct sk_buff *skb,
-                        struct ip_vs_iphdr *iphdr)
-{
-#ifdef CONFIG_IP_VS_IPV6
-       if (af == AF_INET6) {
-               const struct ipv6hdr *iph =
-                       (struct ipv6hdr *)skb_network_header(skb);
-               iphdr->saddr.in6 = iph->saddr;
-               iphdr->daddr.in6 = iph->daddr;
-       } else
-#endif
-       {
-               const struct iphdr *iph =
-                       (struct iphdr *)skb_network_header(skb);
-               iphdr->saddr.ip = iph->saddr;
-               iphdr->daddr.ip = iph->daddr;
-       }
-}
-
 static inline void ip_vs_addr_copy(int af, union nf_inet_addr *dst,
                                   const union nf_inet_addr *src)
 {
@@ -405,17 +380,18 @@ enum {
  */
 enum ip_vs_sctp_states {
        IP_VS_SCTP_S_NONE,
-       IP_VS_SCTP_S_INIT_CLI,
-       IP_VS_SCTP_S_INIT_SER,
-       IP_VS_SCTP_S_INIT_ACK_CLI,
-       IP_VS_SCTP_S_INIT_ACK_SER,
-       IP_VS_SCTP_S_ECHO_CLI,
-       IP_VS_SCTP_S_ECHO_SER,
+       IP_VS_SCTP_S_INIT1,
+       IP_VS_SCTP_S_INIT,
+       IP_VS_SCTP_S_COOKIE_SENT,
+       IP_VS_SCTP_S_COOKIE_REPLIED,
+       IP_VS_SCTP_S_COOKIE_WAIT,
+       IP_VS_SCTP_S_COOKIE,
+       IP_VS_SCTP_S_COOKIE_ECHOED,
        IP_VS_SCTP_S_ESTABLISHED,
-       IP_VS_SCTP_S_SHUT_CLI,
-       IP_VS_SCTP_S_SHUT_SER,
-       IP_VS_SCTP_S_SHUT_ACK_CLI,
-       IP_VS_SCTP_S_SHUT_ACK_SER,
+       IP_VS_SCTP_S_SHUTDOWN_SENT,
+       IP_VS_SCTP_S_SHUTDOWN_RECEIVED,
+       IP_VS_SCTP_S_SHUTDOWN_ACK_SENT,
+       IP_VS_SCTP_S_REJECTED,
        IP_VS_SCTP_S_CLOSED,
        IP_VS_SCTP_S_LAST
 };
@@ -814,7 +790,8 @@ struct ip_vs_scheduler {
 
        /* selecting a server from the given service */
        struct ip_vs_dest* (*schedule)(struct ip_vs_service *svc,
-                                      const struct sk_buff *skb);
+                                      const struct sk_buff *skb,
+                                      struct ip_vs_iphdr *iph);
 };
 
 /* The persistence engine object */
@@ -905,7 +882,7 @@ struct ip_vs_app {
 struct ipvs_master_sync_state {
        struct list_head        sync_queue;
        struct ip_vs_sync_buff  *sync_buff;
-       int                     sync_queue_len;
+       unsigned long           sync_queue_len;
        unsigned int            sync_queue_delay;
        struct task_struct      *master_thread;
        struct delayed_work     master_wakeup_work;
@@ -998,10 +975,13 @@ struct netns_ipvs {
        int                     sysctl_snat_reroute;
        int                     sysctl_sync_ver;
        int                     sysctl_sync_ports;
-       int                     sysctl_sync_qlen_max;
+       int                     sysctl_sync_persist_mode;
+       unsigned long           sysctl_sync_qlen_max;
        int                     sysctl_sync_sock_size;
        int                     sysctl_cache_bypass;
        int                     sysctl_expire_nodest_conn;
+       int                     sysctl_sloppy_tcp;
+       int                     sysctl_sloppy_sctp;
        int                     sysctl_expire_quiescent_template;
        int                     sysctl_sync_threshold[2];
        unsigned int            sysctl_sync_refresh_period;
@@ -1044,6 +1024,8 @@ struct netns_ipvs {
 #define DEFAULT_SYNC_THRESHOLD 3
 #define DEFAULT_SYNC_PERIOD    50
 #define DEFAULT_SYNC_VER       1
+#define DEFAULT_SLOPPY_TCP     0
+#define DEFAULT_SLOPPY_SCTP    0
 #define DEFAULT_SYNC_REFRESH_PERIOD    (0U * HZ)
 #define DEFAULT_SYNC_RETRIES           0
 #define IPVS_SYNC_WAKEUP_RATE  8
@@ -1080,12 +1062,27 @@ static inline int sysctl_sync_ver(struct netns_ipvs *ipvs)
        return ipvs->sysctl_sync_ver;
 }
 
+static inline int sysctl_sloppy_tcp(struct netns_ipvs *ipvs)
+{
+       return ipvs->sysctl_sloppy_tcp;
+}
+
+static inline int sysctl_sloppy_sctp(struct netns_ipvs *ipvs)
+{
+       return ipvs->sysctl_sloppy_sctp;
+}
+
 static inline int sysctl_sync_ports(struct netns_ipvs *ipvs)
 {
        return ACCESS_ONCE(ipvs->sysctl_sync_ports);
 }
 
-static inline int sysctl_sync_qlen_max(struct netns_ipvs *ipvs)
+static inline int sysctl_sync_persist_mode(struct netns_ipvs *ipvs)
+{
+       return ipvs->sysctl_sync_persist_mode;
+}
+
+static inline unsigned long sysctl_sync_qlen_max(struct netns_ipvs *ipvs)
 {
        return ipvs->sysctl_sync_qlen_max;
 }
@@ -1133,12 +1130,27 @@ static inline int sysctl_sync_ver(struct netns_ipvs *ipvs)
        return DEFAULT_SYNC_VER;
 }
 
+static inline int sysctl_sloppy_tcp(struct netns_ipvs *ipvs)
+{
+       return DEFAULT_SLOPPY_TCP;
+}
+
+static inline int sysctl_sloppy_sctp(struct netns_ipvs *ipvs)
+{
+       return DEFAULT_SLOPPY_SCTP;
+}
+
 static inline int sysctl_sync_ports(struct netns_ipvs *ipvs)
 {
        return 1;
 }
 
-static inline int sysctl_sync_qlen_max(struct netns_ipvs *ipvs)
+static inline int sysctl_sync_persist_mode(struct netns_ipvs *ipvs)
+{
+       return 0;
+}
+
+static inline unsigned long sysctl_sync_qlen_max(struct netns_ipvs *ipvs)
 {
        return IPVS_SYNC_QLEN_MAX;
 }
index 0810aa5..5fe5649 100644 (file)
@@ -260,6 +260,12 @@ static inline void fl6_sock_release(struct ip6_flowlabel *fl)
 
 extern void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info);
 
+int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
+                              struct icmp6hdr *thdr, int len);
+
+struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb,
+                                     struct sock *sk, struct flowi6 *fl6);
+
 extern int                     ip6_ra_control(struct sock *sk, int sel);
 
 extern int                     ipv6_parse_hopopts(struct sk_buff *skb);
@@ -853,8 +859,8 @@ static inline int snmp6_unregister_dev(struct inet6_dev *idev) { return 0; }
 #endif
 
 #ifdef CONFIG_SYSCTL
-extern ctl_table ipv6_route_table_template[];
-extern ctl_table ipv6_icmp_table_template[];
+extern struct ctl_table ipv6_route_table_template[];
+extern struct ctl_table ipv6_icmp_table_template[];
 
 extern struct ctl_table *ipv6_icmp_sysctl_init(struct net *net);
 extern struct ctl_table *ipv6_route_sysctl_init(struct net *net);
diff --git a/include/net/ll_poll.h b/include/net/ll_poll.h
new file mode 100644 (file)
index 0000000..76f0340
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Low Latency Sockets
+ * Copyright(c) 2013 Intel 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Eliezer Tamir
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ */
+
+#ifndef _LINUX_NET_LL_POLL_H
+#define _LINUX_NET_LL_POLL_H
+
+#include <linux/netdevice.h>
+#include <net/ip.h>
+
+#ifdef CONFIG_NET_LL_RX_POLL
+
+struct napi_struct;
+extern unsigned int sysctl_net_ll_read __read_mostly;
+extern unsigned int sysctl_net_ll_poll __read_mostly;
+
+/* return values from ndo_ll_poll */
+#define LL_FLUSH_FAILED                -1
+#define LL_FLUSH_BUSY          -2
+
+static inline bool net_busy_loop_on(void)
+{
+       return sysctl_net_ll_poll;
+}
+
+/* a wrapper to make debug_smp_processor_id() happy
+ * we can use sched_clock() because we don't care much about precision
+ * we only care that the average is bounded
+ */
+#ifdef CONFIG_DEBUG_PREEMPT
+static inline u64 busy_loop_us_clock(void)
+{
+       u64 rc;
+
+       preempt_disable_notrace();
+       rc = sched_clock();
+       preempt_enable_no_resched_notrace();
+
+       return rc >> 10;
+}
+#else /* CONFIG_DEBUG_PREEMPT */
+static inline u64 busy_loop_us_clock(void)
+{
+       return sched_clock() >> 10;
+}
+#endif /* CONFIG_DEBUG_PREEMPT */
+
+static inline unsigned long sk_busy_loop_end_time(struct sock *sk)
+{
+       return busy_loop_us_clock() + ACCESS_ONCE(sk->sk_ll_usec);
+}
+
+/* in poll/select we use the global sysctl_net_ll_poll value */
+static inline unsigned long busy_loop_end_time(void)
+{
+       return busy_loop_us_clock() + ACCESS_ONCE(sysctl_net_ll_poll);
+}
+
+static inline bool sk_can_busy_loop(struct sock *sk)
+{
+       return sk->sk_ll_usec && sk->sk_napi_id &&
+              !need_resched() && !signal_pending(current);
+}
+
+
+static inline bool busy_loop_timeout(unsigned long end_time)
+{
+       unsigned long now = busy_loop_us_clock();
+
+       return time_after(now, end_time);
+}
+
+/* when used in sock_poll() nonblock is known at compile time to be true
+ * so the loop and end_time will be optimized out
+ */
+static inline bool sk_busy_loop(struct sock *sk, int nonblock)
+{
+       unsigned long end_time = !nonblock ? sk_busy_loop_end_time(sk) : 0;
+       const struct net_device_ops *ops;
+       struct napi_struct *napi;
+       int rc = false;
+
+       /*
+        * rcu read lock for napi hash
+        * bh so we don't race with net_rx_action
+        */
+       rcu_read_lock_bh();
+
+       napi = napi_by_id(sk->sk_napi_id);
+       if (!napi)
+               goto out;
+
+       ops = napi->dev->netdev_ops;
+       if (!ops->ndo_ll_poll)
+               goto out;
+
+       do {
+               rc = ops->ndo_ll_poll(napi);
+
+               if (rc == LL_FLUSH_FAILED)
+                       break; /* permanent failure */
+
+               if (rc > 0)
+                       /* local bh are disabled so it is ok to use _BH */
+                       NET_ADD_STATS_BH(sock_net(sk),
+                                        LINUX_MIB_LOWLATENCYRXPACKETS, rc);
+
+       } while (!nonblock && skb_queue_empty(&sk->sk_receive_queue) &&
+                !need_resched() && !busy_loop_timeout(end_time));
+
+       rc = !skb_queue_empty(&sk->sk_receive_queue);
+out:
+       rcu_read_unlock_bh();
+       return rc;
+}
+
+/* used in the NIC receive handler to mark the skb */
+static inline void skb_mark_ll(struct sk_buff *skb, struct napi_struct *napi)
+{
+       skb->napi_id = napi->napi_id;
+}
+
+/* used in the protocol hanlder to propagate the napi_id to the socket */
+static inline void sk_mark_ll(struct sock *sk, struct sk_buff *skb)
+{
+       sk->sk_napi_id = skb->napi_id;
+}
+
+#else /* CONFIG_NET_LL_RX_POLL */
+static inline unsigned long net_busy_loop_on(void)
+{
+       return 0;
+}
+
+static inline unsigned long busy_loop_end_time(void)
+{
+       return 0;
+}
+
+static inline bool sk_can_busy_loop(struct sock *sk)
+{
+       return false;
+}
+
+static inline bool sk_busy_poll(struct sock *sk, int nonblock)
+{
+       return false;
+}
+
+static inline void skb_mark_ll(struct sk_buff *skb, struct napi_struct *napi)
+{
+}
+
+static inline void sk_mark_ll(struct sock *sk, struct sk_buff *skb)
+{
+}
+
+static inline bool busy_loop_timeout(unsigned long end_time)
+{
+       return true;
+}
+
+#endif /* CONFIG_NET_LL_RX_POLL */
+#endif /* _LINUX_NET_LL_POLL_H */
index 885898a..5b7a3da 100644 (file)
@@ -217,8 +217,8 @@ struct ieee80211_chanctx_conf {
  * @BSS_CHANGED_TXPOWER: TX power setting changed for this interface
  * @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS)
  *     changed (currently only in P2P client mode, GO mode will be later)
- * @BSS_CHANGED_DTIM_PERIOD: the DTIM period value was changed (set when
- *     it becomes valid, managed mode only)
+ * @BSS_CHANGED_BEACON_INFO: Data from the AP's beacon became available:
+ *     currently dtim_period only is under consideration.
  * @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
  *     note that this is only called when it changes after the channel
  *     context had been assigned.
@@ -244,7 +244,7 @@ enum ieee80211_bss_change {
        BSS_CHANGED_PS                  = 1<<17,
        BSS_CHANGED_TXPOWER             = 1<<18,
        BSS_CHANGED_P2P_PS              = 1<<19,
-       BSS_CHANGED_DTIM_PERIOD         = 1<<20,
+       BSS_CHANGED_BEACON_INFO         = 1<<20,
        BSS_CHANGED_BANDWIDTH           = 1<<21,
 
        /* when adding here, make sure to change ieee80211_reconfig */
@@ -288,7 +288,7 @@ enum ieee80211_rssi_event {
  *     IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag
  * @dtim_period: num of beacons before the next DTIM, for beaconing,
  *     valid in station mode only if after the driver was notified
- *     with the %BSS_CHANGED_DTIM_PERIOD flag, will be non-zero then.
+ *     with the %BSS_CHANGED_BEACON_INFO flag, will be non-zero then.
  * @sync_tsf: last beacon's/probe response's TSF timestamp (could be old
  *     as it may have been received during scanning long ago). If the
  *     HW flag %IEEE80211_HW_TIMING_BEACON_ONLY is set, then this can
@@ -305,6 +305,7 @@ enum ieee80211_rssi_event {
  * @basic_rates: bitmap of basic rates, each bit stands for an
  *     index into the rate table configured by the driver in
  *     the current band.
+ * @beacon_rate: associated AP's beacon TX rate
  * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
  * @bssid: The BSSID for this BSS
  * @enable_beacon: whether beaconing should be enabled or not
@@ -352,6 +353,7 @@ struct ieee80211_bss_conf {
        u32 sync_device_ts;
        u8 sync_dtim_count;
        u32 basic_rates;
+       struct ieee80211_rate *beacon_rate;
        int mcast_rate[IEEE80211_NUM_BANDS];
        u16 ht_operation_mode;
        s32 cqm_rssi_thold;
@@ -460,6 +462,8 @@ struct ieee80211_bss_conf {
  * @IEEE80211_TX_CTL_DONTFRAG: Don't fragment this packet even if it
  *     would be fragmented by size (this is optional, only used for
  *     monitor injection).
+ * @IEEE80211_TX_CTL_PS_RESPONSE: This frame is a response to a poll
+ *     frame (PS-Poll or uAPSD).
  *
  * Note: If you have to add new flags to the enumeration, then don't
  *      forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
@@ -495,6 +499,7 @@ enum mac80211_tx_control_flags {
        IEEE80211_TX_STATUS_EOSP                = BIT(28),
        IEEE80211_TX_CTL_USE_MINRATE            = BIT(29),
        IEEE80211_TX_CTL_DONTFRAG               = BIT(30),
+       IEEE80211_TX_CTL_PS_RESPONSE            = BIT(31),
 };
 
 #define IEEE80211_TX_CTL_STBC_SHIFT            23
@@ -805,6 +810,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  *     on this subframe
  * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
  *     is stored in the @ampdu_delimiter_crc field)
+ * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
  */
 enum mac80211_rx_flags {
        RX_FLAG_MMIC_ERROR              = BIT(0),
@@ -832,8 +838,11 @@ enum mac80211_rx_flags {
        RX_FLAG_80MHZ                   = BIT(23),
        RX_FLAG_80P80MHZ                = BIT(24),
        RX_FLAG_160MHZ                  = BIT(25),
+       RX_FLAG_STBC_MASK               = BIT(26) | BIT(27),
 };
 
+#define RX_FLAG_STBC_SHIFT             26
+
 /**
  * struct ieee80211_rx_status - receive status
  *
@@ -850,6 +859,10 @@ enum mac80211_rx_flags {
  * @signal: signal strength when receiving this frame, either in dBm, in dB or
  *     unspecified depending on the hardware capabilities flags
  *     @IEEE80211_HW_SIGNAL_*
+ * @chains: bitmask of receive chains for which separate signal strength
+ *     values were filled.
+ * @chain_signal: per-chain signal strength, in dBm (unlike @signal, doesn't
+ *     support dB or unspecified units)
  * @antenna: antenna used
  * @rate_idx: index of data rate into band's supported rates or MCS index if
  *     HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT)
@@ -881,6 +894,8 @@ struct ieee80211_rx_status {
        u8 band;
        u8 antenna;
        s8 signal;
+       u8 chains;
+       s8 chain_signal[IEEE80211_MAX_CHAINS];
        u8 ampdu_delimiter_crc;
        u8 vendor_radiotap_align;
        u8 vendor_radiotap_oui[3];
@@ -1235,7 +1250,7 @@ enum ieee80211_sta_rx_bandwidth {
  * struct ieee80211_sta_rates - station rate selection table
  *
  * @rcu_head: RCU head used for freeing the table on update
- * @rates: transmit rates/flags to be used by default.
+ * @rate: transmit rates/flags to be used by default.
  *     Overriding entries per-packet is possible by using cb tx control.
  */
 struct ieee80211_sta_rates {
@@ -1276,7 +1291,7 @@ struct ieee80211_sta_rates {
  *     notifications and capabilities. The value is only valid after
  *     the station moves to associated state.
  * @smps_mode: current SMPS mode (off, static or dynamic)
- * @tx_rates: rate control selection table
+ * @rates: rate control selection table
  */
 struct ieee80211_sta {
        u32 supp_rates[IEEE80211_NUM_BANDS];
index 745bf74..949d775 100644 (file)
@@ -230,7 +230,7 @@ extern int                  ndisc_ifinfo_sysctl_change(struct ctl_table *ctl,
                                                           void __user *buffer,
                                                           size_t *lenp,
                                                           loff_t *ppos);
-int ndisc_ifinfo_sysctl_strategy(ctl_table *ctl,
+int ndisc_ifinfo_sysctl_strategy(struct ctl_table *ctl,
                                 void __user *oldval, size_t __user *oldlenp,
                                 void __user *newval, size_t newlen);
 #endif
index b176978..84e37b1 100644 (file)
@@ -115,9 +115,12 @@ struct net {
 #ifdef CONFIG_XFRM
        struct netns_xfrm       xfrm;
 #endif
+#if IS_ENABLED(CONFIG_IP_VS)
        struct netns_ipvs       *ipvs;
+#endif
        struct sock             *diag_nlsk;
        atomic_t                rt_genid;
+       atomic_t                fnhe_genid;
 };
 
 /*
@@ -340,4 +343,14 @@ static inline void rt_genid_bump(struct net *net)
        atomic_inc(&net->rt_genid);
 }
 
+static inline int fnhe_genid(struct net *net)
+{
+       return atomic_read(&net->fnhe_genid);
+}
+
+static inline void fnhe_genid_bump(struct net *net)
+{
+       atomic_inc(&net->fnhe_genid);
+}
+
 #endif /* __NET_NET_NAMESPACE_H */
index 5a2978d..495c71f 100644 (file)
@@ -6,7 +6,7 @@ struct xt_rateest {
        struct gnet_stats_basic_packed  bstats;
        spinlock_t                      lock;
        /* keep rstats and lock on same cache line to speedup xt_rateest_mt() */
-       struct gnet_stats_rate_est      rstats;
+       struct gnet_stats_rate_est64    rstats;
 
        /* following fields not accessed in hot path */
        struct hlist_node               list;
index c24060e..02fe40f 100644 (file)
@@ -15,5 +15,11 @@ struct netns_xt {
        struct ebt_table *frame_filter;
        struct ebt_table *frame_nat;
 #endif
+#if IS_ENABLED(CONFIG_IP_NF_TARGET_ULOG)
+       bool ulog_warn_deprecated;
+#endif
+#if IS_ENABLED(CONFIG_BRIDGE_EBT_ULOG)
+       bool ebt_ulog_warn_deprecated;
+#endif
 };
 #endif
index b87a169..0af851c 100644 (file)
@@ -59,8 +59,10 @@ struct nfc_hci_ops {
                              struct nfc_target *target);
        int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
                              struct sk_buff *skb);
-       int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
-       int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
+       int (*fw_upload)(struct nfc_hci_dev *hdev, const char *firmware_name);
+       int (*discover_se)(struct nfc_hci_dev *dev);
+       int (*enable_se)(struct nfc_hci_dev *dev, u32 se_idx);
+       int (*disable_se)(struct nfc_hci_dev *dev, u32 se_idx);
 };
 
 /* Pipes */
@@ -152,7 +154,6 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
                                            unsigned long quirks,
                                            u32 protocols,
-                                           u32 supported_se,
                                            const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
index 5bc0c46..99fc1f3 100644 (file)
@@ -3,6 +3,7 @@
  *  NFC Controller (NFCC) and a Device Host (DH).
  *
  *  Copyright (C) 2011 Texas Instruments, Inc.
+ *  Copyright (C) 2013 Intel Corporation. All rights reserved.
  *
  *  Written by Ilan Elias <ilane@ti.com>
  *
@@ -66,7 +67,7 @@ struct nci_dev;
 struct nci_ops {
        int (*open)(struct nci_dev *ndev);
        int (*close)(struct nci_dev *ndev);
-       int (*send)(struct sk_buff *skb);
+       int (*send)(struct nci_dev *ndev, struct sk_buff *skb);
 };
 
 #define NCI_MAX_SUPPORTED_RF_INTERFACES                4
@@ -147,13 +148,12 @@ struct nci_dev {
 /* ----- NCI Devices ----- */
 struct nci_dev *nci_allocate_device(struct nci_ops *ops,
                                    __u32 supported_protocols,
-                                   __u32 supported_se,
                                    int tx_headroom,
                                    int tx_tailroom);
 void nci_free_device(struct nci_dev *ndev);
 int nci_register_device(struct nci_dev *ndev);
 void nci_unregister_device(struct nci_dev *ndev);
-int nci_recv_frame(struct sk_buff *skb);
+int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
 
 static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev,
                                            unsigned int len,
@@ -202,4 +202,56 @@ void nci_req_complete(struct nci_dev *ndev, int result);
 /* ----- NCI status code ----- */
 int nci_to_errno(__u8 code);
 
+/* ----- NCI over SPI acknowledge modes ----- */
+#define NCI_SPI_CRC_DISABLED   0x00
+#define NCI_SPI_CRC_ENABLED    0x01
+
+/* ----- NCI SPI structures ----- */
+struct nci_spi_dev;
+
+struct nci_spi_ops {
+       int (*open)(struct nci_spi_dev *ndev);
+       int (*close)(struct nci_spi_dev *ndev);
+       void (*assert_int)(struct nci_spi_dev *ndev);
+       void (*deassert_int)(struct nci_spi_dev *ndev);
+};
+
+struct nci_spi_dev {
+       struct nci_dev          *nci_dev;
+       struct spi_device       *spi;
+       struct nci_spi_ops      *ops;
+
+       unsigned int            xfer_udelay;    /* microseconds delay between
+                                                 transactions */
+       u8                      acknowledge_mode;
+
+       struct completion       req_completion;
+       u8                      req_result;
+
+       void                    *driver_data;
+};
+
+/* ----- NCI SPI Devices ----- */
+struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
+                                               struct nci_spi_ops *ops,
+                                               u32 supported_protocols,
+                                               u32 supported_se,
+                                               u8 acknowledge_mode,
+                                               unsigned int delay);
+void nci_spi_free_device(struct nci_spi_dev *ndev);
+int nci_spi_register_device(struct nci_spi_dev *ndev);
+void nci_spi_unregister_device(struct nci_spi_dev *ndev);
+int nci_spi_recv_frame(struct nci_spi_dev *ndev);
+
+static inline void nci_spi_set_drvdata(struct nci_spi_dev *ndev,
+                                           void *data)
+{
+       ndev->driver_data = data;
+}
+
+static inline void *nci_spi_get_drvdata(struct nci_spi_dev *ndev)
+{
+       return ndev->driver_data;
+}
+
 #endif /* __NCI_CORE_H */
index 5eb80bb..0e353f1 100644 (file)
@@ -68,8 +68,12 @@ struct nfc_ops {
                             void *cb_context);
        int (*tm_send)(struct nfc_dev *dev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target);
-       int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
-       int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
+       int (*fw_upload)(struct nfc_dev *dev, const char *firmware_name);
+
+       /* Secure Element API */
+       int (*discover_se)(struct nfc_dev *dev);
+       int (*enable_se)(struct nfc_dev *dev, u32 se_idx);
+       int (*disable_se)(struct nfc_dev *dev, u32 se_idx);
 };
 
 #define NFC_TARGET_IDX_ANY -1
@@ -83,6 +87,8 @@ struct nfc_target {
        u8 sel_res;
        u8 nfcid1_len;
        u8 nfcid1[NFC_NFCID1_MAXSIZE];
+       u8 nfcid2_len;
+       u8 nfcid2[NFC_NFCID2_MAXSIZE];
        u8 sensb_res_len;
        u8 sensb_res[NFC_SENSB_RES_MAXSIZE];
        u8 sensf_res_len;
@@ -91,6 +97,23 @@ struct nfc_target {
        u8 logical_idx;
 };
 
+/**
+ * nfc_se - A structure for NFC accessible secure elements.
+ *
+ * @idx: The secure element index. User space will enable or
+ *       disable a secure element by its index.
+ * @type: The secure element type. It can be SE_UICC or
+ *        SE_EMBEDDED.
+ * @state: The secure element state, either enabled or disabled.
+ *
+ */
+struct nfc_se {
+       struct list_head list;
+       u32 idx;
+       u16 type;
+       u16 state;
+};
+
 struct nfc_genl_data {
        u32 poll_req_portid;
        struct mutex genl_data_mutex;
@@ -104,6 +127,7 @@ struct nfc_dev {
        int targets_generation;
        struct device dev;
        bool dev_up;
+       bool fw_upload_in_progress;
        u8 rf_mode;
        bool polling;
        struct nfc_target *active_target;
@@ -111,8 +135,7 @@ struct nfc_dev {
        struct nfc_genl_data genl_data;
        u32 supported_protocols;
 
-       u32 supported_se;
-       u32 active_se;
+       struct list_head secure_elements;
 
        int tx_headroom;
        int tx_tailroom;
@@ -132,7 +155,6 @@ extern struct class nfc_class;
 
 struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                                    u32 supported_protocols,
-                                   u32 supported_se,
                                    int tx_headroom,
                                    int tx_tailroom);
 
@@ -216,4 +238,7 @@ int nfc_tm_data_received(struct nfc_dev *dev, struct sk_buff *skb);
 
 void nfc_driver_failure(struct nfc_dev *dev, int err);
 
+int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type);
+int nfc_remove_se(struct nfc_dev *dev, u32 se_idx);
+
 #endif /* __NET_NFC_H */
index 682b5ae..5db0224 100644 (file)
@@ -13,6 +13,7 @@
 #ifndef _PING_H
 #define _PING_H
 
+#include <net/icmp.h>
 #include <net/netns/hash.h>
 
 /* PING_HTABLE_SIZE must be power of 2 */
  */
 #define GID_T_MAX (((gid_t)~0U) >> 1)
 
+/* Compatibility glue so we can support IPv6 when it's compiled as a module */
+struct pingv6_ops {
+       int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len);
+       int (*ip6_datagram_recv_ctl)(struct sock *sk, struct msghdr *msg,
+                                    struct sk_buff *skb);
+       int (*icmpv6_err_convert)(u8 type, u8 code, int *err);
+       void (*ipv6_icmp_error)(struct sock *sk, struct sk_buff *skb, int err,
+                               __be16 port, u32 info, u8 *payload);
+       int (*ipv6_chk_addr)(struct net *net, const struct in6_addr *addr,
+                            const struct net_device *dev, int strict);
+};
+
 struct ping_table {
        struct hlist_nulls_head hash[PING_HTABLE_SIZE];
        rwlock_t                lock;
@@ -36,20 +49,66 @@ struct ping_table {
 struct ping_iter_state {
        struct seq_net_private  p;
        int                     bucket;
+       sa_family_t             family;
 };
 
 extern struct proto ping_prot;
+extern struct ping_table ping_table;
+#if IS_ENABLED(CONFIG_IPV6)
+extern struct pingv6_ops pingv6_ops;
+#endif
+
+struct pingfakehdr {
+       struct icmphdr icmph;
+       struct iovec *iov;
+       sa_family_t family;
+       __wsum wcheck;
+};
+
+int  ping_get_port(struct sock *sk, unsigned short ident);
+void ping_hash(struct sock *sk);
+void ping_unhash(struct sock *sk);
 
+int  ping_init_sock(struct sock *sk);
+void ping_close(struct sock *sk, long timeout);
+int  ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len);
+void ping_err(struct sk_buff *skb, int offset, u32 info);
+int  ping_getfrag(void *from, char *to, int offset, int fraglen, int odd,
+                 struct sk_buff *);
 
-extern void ping_rcv(struct sk_buff *);
-extern void ping_err(struct sk_buff *, u32 info);
+int  ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+                 size_t len, int noblock, int flags, int *addr_len);
+int  ping_common_sendmsg(int family, struct msghdr *msg, size_t len,
+                        void *user_icmph, size_t icmph_len);
+int  ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+                    size_t len);
+int  ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+                    size_t len);
+int  ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+void ping_rcv(struct sk_buff *skb);
 
 #ifdef CONFIG_PROC_FS
+struct ping_seq_afinfo {
+       char                            *name;
+       sa_family_t                     family;
+       const struct file_operations    *seq_fops;
+       const struct seq_operations     seq_ops;
+};
+
+extern const struct file_operations ping_seq_fops;
+
+void *ping_seq_start(struct seq_file *seq, loff_t *pos, sa_family_t family);
+void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos);
+void ping_seq_stop(struct seq_file *seq, void *v);
+int ping_proc_register(struct net *net, struct ping_seq_afinfo *afinfo);
+void ping_proc_unregister(struct net *net, struct ping_seq_afinfo *afinfo);
+
 extern int __init ping_proc_init(void);
 extern void ping_proc_exit(void);
 #endif
 
 void __init ping_init(void);
-
+int  __init pingv6_init(void);
+void pingv6_exit(void);
 
 #endif /* _PING_H */
index e7f4e21..6eab633 100644 (file)
@@ -58,14 +58,12 @@ struct Qdisc {
                                      * multiqueue device.
                                      */
 #define TCQ_F_WARN_NONWC       (1 << 16)
-       int                     padded;
+       u32                     limit;
        const struct Qdisc_ops  *ops;
        struct qdisc_size_table __rcu *stab;
        struct list_head        list;
        u32                     handle;
        u32                     parent;
-       atomic_t                refcnt;
-       struct gnet_stats_rate_est      rate_est;
        int                     (*reshape_fail)(struct sk_buff *skb,
                                        struct Qdisc *q);
 
@@ -76,8 +74,9 @@ struct Qdisc {
         */
        struct Qdisc            *__parent;
        struct netdev_queue     *dev_queue;
-       struct Qdisc            *next_sched;
 
+       struct gnet_stats_rate_est64    rate_est;
+       struct Qdisc            *next_sched;
        struct sk_buff          *gso_skb;
        /*
         * For performance sake on SMP, we put highly modified fields at the end
@@ -88,8 +87,10 @@ struct Qdisc {
        unsigned int            __state;
        struct gnet_stats_queue qstats;
        struct rcu_head         rcu_head;
-       spinlock_t              busylock;
-       u32                     limit;
+       int                     padded;
+       atomic_t                refcnt;
+
+       spinlock_t              busylock ____cacheline_aligned_in_smp;
 };
 
 static inline bool qdisc_is_running(const struct Qdisc *qdisc)
@@ -679,7 +680,7 @@ static inline struct sk_buff *skb_act_clone(struct sk_buff *skb, gfp_t gfp_mask,
 #endif
 
 struct psched_ratecfg {
-       u64     rate_bps;
+       u64     rate_bytes_ps; /* bytes per second */
        u32     mult;
        u16     overhead;
        u8      shift;
@@ -697,7 +698,7 @@ static inline void psched_ratecfg_getrate(struct tc_ratespec *res,
                                          const struct psched_ratecfg *r)
 {
        memset(res, 0, sizeof(*res));
-       res->rate = r->rate_bps >> 3;
+       res->rate = r->rate_bytes_ps;
        res->overhead = r->overhead;
 }
 
index 5a2110d..0cb08e6 100644 (file)
@@ -42,6 +42,9 @@
  * be incorporated into the next SCTP release.
  */
 
+#ifndef __sctp_checksum_h__
+#define __sctp_checksum_h__
+
 #include <linux/types.h>
 #include <net/sctp/sctp.h>
 #include <linux/crc32c.h>
@@ -81,3 +84,5 @@ static inline __le32 sctp_end_cksum(__u32 crc32)
 {
        return cpu_to_le32(~crc32);
 }
+
+#endif /* __sctp_checksum_h__ */
index cd89510..d8e37ec 100644 (file)
 #include <net/sctp/structs.h>
 #include <net/sctp/constants.h>
 
-
-/* Set SCTP_DEBUG flag via config if not already set. */
-#ifndef SCTP_DEBUG
-#ifdef CONFIG_SCTP_DBG_MSG
-#define SCTP_DEBUG     1
-#else
-#define SCTP_DEBUG      0
-#endif /* CONFIG_SCTP_DBG */
-#endif /* SCTP_DEBUG */
-
 #ifdef CONFIG_IP_SCTP_MODULE
 #define SCTP_PROTOSW_FLAG 0
 #else /* static! */
 #define SCTP_PROTOSW_FLAG INET_PROTOSW_PERMANENT
 #endif
 
-
-/* Certain internal static functions need to be exported when
- * compiled into the test frame.
- */
-#ifndef SCTP_STATIC
-#define SCTP_STATIC static
-#endif
-
 /*
  * Function declarations.
  */
@@ -196,11 +178,6 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly;
  *  Section:  Macros, externs, and inlines
  */
 
-
-#ifdef TEST_FRAME
-#include <test_frame.h>
-#else
-
 /* spin lock wrappers. */
 #define sctp_spin_lock_irqsave(lock, flags) spin_lock_irqsave(lock, flags)
 #define sctp_spin_unlock_irqrestore(lock, flags)  \
@@ -226,8 +203,6 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly;
 #define SCTP_INC_STATS_USER(net, field) SNMP_INC_STATS_USER((net)->sctp.sctp_statistics, field)
 #define SCTP_DEC_STATS(net, field)      SNMP_DEC_STATS((net)->sctp.sctp_statistics, field)
 
-#endif /* !TEST_FRAME */
-
 /* sctp mib definitions */
 enum {
        SCTP_MIB_NUM = 0,
@@ -285,61 +260,6 @@ static inline void sctp_max_rto(struct sctp_association *asoc,
        }
 }
 
-/* Print debugging messages.  */
-#if SCTP_DEBUG
-extern int sctp_debug_flag;
-#define SCTP_DEBUG_PRINTK(fmt, args...)                        \
-do {                                                   \
-       if (sctp_debug_flag)                            \
-               printk(KERN_DEBUG pr_fmt(fmt), ##args); \
-} while (0)
-#define SCTP_DEBUG_PRINTK_CONT(fmt, args...)           \
-do {                                                   \
-       if (sctp_debug_flag)                            \
-               pr_cont(fmt, ##args);                   \
-} while (0)
-#define SCTP_DEBUG_PRINTK_IPADDR(fmt_lead, fmt_trail,                  \
-                                args_lead, addr, args_trail...)        \
-do {                                                                   \
-       const union sctp_addr *_addr = (addr);                          \
-       if (sctp_debug_flag) {                                          \
-               if (_addr->sa.sa_family == AF_INET6) {                  \
-                       printk(KERN_DEBUG                               \
-                              pr_fmt(fmt_lead "%pI6" fmt_trail),       \
-                              args_lead,                               \
-                              &_addr->v6.sin6_addr,                    \
-                              args_trail);                             \
-               } else {                                                \
-                       printk(KERN_DEBUG                               \
-                              pr_fmt(fmt_lead "%pI4" fmt_trail),       \
-                              args_lead,                               \
-                              &_addr->v4.sin_addr.s_addr,              \
-                              args_trail);                             \
-               }                                                       \
-       }                                                               \
-} while (0)
-#define SCTP_ENABLE_DEBUG { sctp_debug_flag = 1; }
-#define SCTP_DISABLE_DEBUG { sctp_debug_flag = 0; }
-
-#define SCTP_ASSERT(expr, str, func) \
-       if (!(expr)) { \
-               SCTP_DEBUG_PRINTK("Assertion Failed: %s(%s) at %s:%s:%d\n", \
-                       str, (#expr), __FILE__, __func__, __LINE__); \
-               func; \
-       }
-
-#else  /* SCTP_DEBUG */
-
-#define SCTP_DEBUG_PRINTK(whatever...)
-#define SCTP_DEBUG_PRINTK_CONT(fmt, args...)
-#define SCTP_DEBUG_PRINTK_IPADDR(whatever...)
-#define SCTP_ENABLE_DEBUG
-#define SCTP_DISABLE_DEBUG
-#define SCTP_ASSERT(expr, str, func)
-
-#endif /* SCTP_DEBUG */
-
-
 /*
  * Macros for keeping a global reference of object allocations.
  */
@@ -575,27 +495,6 @@ for (pos = chunk->subh.fwdtsn_hdr->skip;\
 /* Round an int up to the next multiple of 4.  */
 #define WORD_ROUND(s) (((s)+3)&~3)
 
-/* Make a new instance of type.  */
-#define t_new(type, flags)     kzalloc(sizeof(type), flags)
-
-/* Compare two timevals.  */
-#define tv_lt(s, t) \
-   (s.tv_sec < t.tv_sec || (s.tv_sec == t.tv_sec && s.tv_usec < t.tv_usec))
-
-/* Add tv1 to tv2. */
-#define TIMEVAL_ADD(tv1, tv2) \
-({ \
-        suseconds_t usecs = (tv2).tv_usec + (tv1).tv_usec; \
-        time_t secs = (tv2).tv_sec + (tv1).tv_sec; \
-\
-        if (usecs >= 1000000) { \
-                usecs -= 1000000; \
-                secs++; \
-        } \
-        (tv2).tv_sec = secs; \
-        (tv2).tv_usec = usecs; \
-})
-
 /* External references. */
 
 extern struct proto sctp_prot;
@@ -633,16 +532,6 @@ static inline int param_type2af(__be16 type)
        }
 }
 
-/* Perform some sanity checks. */
-static inline int sctp_sanity_check(void)
-{
-       SCTP_ASSERT(sizeof(struct sctp_ulpevent) <=
-                   sizeof(((struct sk_buff *)0)->cb),
-                   "SCTP: ulpevent does not fit in skb!\n", return 0);
-
-       return 1;
-}
-
 /* Warning: The following hash functions assume a power of two 'size'. */
 /* This is the hash function for the SCTP port hash table. */
 static inline int sctp_phashfn(struct net *net, __u16 lport)
index 1bd4c41..e745c92 100644 (file)
@@ -54,7 +54,7 @@
 #ifndef __sctp_structs_h__
 #define __sctp_structs_h__
 
-#include <linux/time.h>                /* We get struct timespec.    */
+#include <linux/ktime.h>
 #include <linux/socket.h>      /* linux/in.h needs this!!    */
 #include <linux/in.h>          /* We get struct sockaddr_in. */
 #include <linux/in6.h>         /* We get struct in6_addr     */
@@ -284,7 +284,7 @@ struct sctp_cookie {
        __u32 peer_ttag;
 
        /* When does this cookie expire? */
-       struct timeval expiration;
+       ktime_t expiration;
 
        /* Number of inbound/outbound streams which are set
         * and negotiated during the INIT process.
@@ -1537,7 +1537,7 @@ struct sctp_association {
        sctp_state_t state;
 
        /* The cookie life I award for any cookie.  */
-       struct timeval cookie_life;
+       ktime_t cookie_life;
 
        /* Overall     : The overall association error count.
         * Error Count : [Clear this any time I get something.]
index 66772cf..95a5a2c 100644 (file)
@@ -229,6 +229,8 @@ struct cg_proto;
   *    @sk_omem_alloc: "o" is "option" or "other"
   *    @sk_wmem_queued: persistent queue size
   *    @sk_forward_alloc: space allocated forward
+  *    @sk_napi_id: id of the last napi context to receive data for sk
+  *    @sk_ll_usec: usecs to busypoll when there is no data
   *    @sk_allocation: allocation mode
   *    @sk_sndbuf: size of send buffer in bytes
   *    @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE,
@@ -324,6 +326,10 @@ struct sock {
        int                     sk_forward_alloc;
 #ifdef CONFIG_RPS
        __u32                   sk_rxhash;
+#endif
+#ifdef CONFIG_NET_LL_RX_POLL
+       unsigned int            sk_napi_id;
+       unsigned int            sk_ll_usec;
 #endif
        atomic_t                sk_drops;
        int                     sk_rcvbuf;
@@ -2041,18 +2047,21 @@ static inline void sk_wake_async(struct sock *sk, int how, int band)
                sock_wake_async(sk->sk_socket, how, band);
 }
 
-#define SOCK_MIN_SNDBUF 2048
-/*
- * Since sk_rmem_alloc sums skb->truesize, even a small frame might need
- * sizeof(sk_buff) + MTU + padding, unless net driver perform copybreak
+/* Since sk_{r,w}mem_alloc sums skb->truesize, even a small frame might
+ * need sizeof(sk_buff) + MTU + padding, unless net driver perform copybreak.
+ * Note: for send buffers, TCP works better if we can build two skbs at
+ * minimum.
  */
-#define SOCK_MIN_RCVBUF (2048 + sizeof(struct sk_buff))
+#define TCP_SKB_MIN_TRUESIZE   (2048 + SKB_DATA_ALIGN(sizeof(struct sk_buff)))
+
+#define SOCK_MIN_SNDBUF                (TCP_SKB_MIN_TRUESIZE * 2)
+#define SOCK_MIN_RCVBUF                 TCP_SKB_MIN_TRUESIZE
 
 static inline void sk_stream_moderate_sndbuf(struct sock *sk)
 {
        if (!(sk->sk_userlocks & SOCK_SNDBUF_LOCK)) {
                sk->sk_sndbuf = min(sk->sk_sndbuf, sk->sk_wmem_queued >> 1);
-               sk->sk_sndbuf = max(sk->sk_sndbuf, SOCK_MIN_SNDBUF);
+               sk->sk_sndbuf = max_t(u32, sk->sk_sndbuf, SOCK_MIN_SNDBUF);
        }
 }
 
index 5bba80f..d198005 100644 (file)
@@ -61,9 +61,6 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
  */
 #define MAX_TCP_WINDOW         32767U
 
-/* Offer an initial receive window of 10 mss. */
-#define TCP_DEFAULT_INIT_RCVWND        10
-
 /* Minimal accepted MSS. It is (60+60+8) - (20+20). */
 #define TCP_MIN_MSS            88U
 
@@ -1047,6 +1044,8 @@ static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
        rx_opt->num_sacks = 0;
 }
 
+extern u32 tcp_default_init_rwnd(u32 mss);
+
 /* Determine a window scaling and initial window to offer. */
 extern void tcp_select_initial_window(int __space, __u32 mss,
                                      __u32 *rcv_wnd, __u32 *window_clamp,
@@ -1193,7 +1192,6 @@ static inline void tcp_mib_init(struct net *net)
 static inline void tcp_clear_retrans_hints_partial(struct tcp_sock *tp)
 {
        tp->lost_skb_hint = NULL;
-       tp->scoreboard_skb_hint = NULL;
 }
 
 static inline void tcp_clear_all_retrans_hints(struct tcp_sock *tp)
@@ -1284,11 +1282,13 @@ static inline struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
 #define tcp_twsk_md5_key(twsk) NULL
 #endif
 
-extern struct tcp_md5sig_pool __percpu *tcp_alloc_md5sig_pool(struct sock *);
-extern void tcp_free_md5sig_pool(void);
+extern bool tcp_alloc_md5sig_pool(void);
 
 extern struct tcp_md5sig_pool  *tcp_get_md5sig_pool(void);
-extern void tcp_put_md5sig_pool(void);
+static inline void tcp_put_md5sig_pool(void)
+{
+       local_bh_enable();
+}
 
 extern int tcp_md5_hash_header(struct tcp_md5sig_pool *, const struct tcphdr *);
 extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *,
@@ -1319,9 +1319,9 @@ void tcp_fastopen_cookie_gen(__be32 addr, struct tcp_fastopen_cookie *foc);
 
 /* Fastopen key context */
 struct tcp_fastopen_context {
-       struct crypto_cipher __rcu      *tfm;
-       __u8                            key[TCP_FASTOPEN_KEY_LENGTH];
-       struct rcu_head                 rcu;
+       struct crypto_cipher    *tfm;
+       __u8                    key[TCP_FASTOPEN_KEY_LENGTH];
+       struct rcu_head         rcu;
 };
 
 /* write queue abstraction */
@@ -1540,15 +1540,14 @@ extern struct request_sock_ops tcp6_request_sock_ops;
 
 extern void tcp_v4_destroy_sock(struct sock *sk);
 
-extern int tcp_v4_gso_send_check(struct sk_buff *skb);
 extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                                       netdev_features_t features);
 extern struct sk_buff **tcp_gro_receive(struct sk_buff **head,
                                        struct sk_buff *skb);
-extern struct sk_buff **tcp4_gro_receive(struct sk_buff **head,
-                                        struct sk_buff *skb);
 extern int tcp_gro_complete(struct sk_buff *skb);
-extern int tcp4_gro_complete(struct sk_buff *skb);
+
+extern void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr,
+                               __be32 daddr);
 
 #ifdef CONFIG_PROC_FS
 extern int tcp4_proc_init(void);
@@ -1583,6 +1582,8 @@ struct tcp_request_sock_ops {
 #endif
 };
 
+extern int tcpv4_offload_init(void);
+
 extern void tcp_v4_init(void);
 extern void tcp_init(void);
 
index 938b7fd..48660e5 100644 (file)
@@ -3,56 +3,57 @@
 
 #include <net/checksum.h>
 
-/*
- *     IPv6 transport protocols
- */
-
+/* IPv6 transport protocols */
 extern struct proto rawv6_prot;
 extern struct proto udpv6_prot;
 extern struct proto udplitev6_prot;
 extern struct proto tcpv6_prot;
+extern struct proto pingv6_prot;
 
 struct flowi6;
 
 /* extension headers */
-extern int                             ipv6_exthdrs_init(void);
-extern void                            ipv6_exthdrs_exit(void);
-extern int                             ipv6_frag_init(void);
-extern void                            ipv6_frag_exit(void);
+int ipv6_exthdrs_init(void);
+void ipv6_exthdrs_exit(void);
+int ipv6_frag_init(void);
+void ipv6_frag_exit(void);
 
 /* transport protocols */
-extern int                             rawv6_init(void);
-extern void                            rawv6_exit(void);
-extern int                             udpv6_init(void);
-extern void                            udpv6_exit(void);
-extern int                             udplitev6_init(void);
-extern void                            udplitev6_exit(void);
-extern int                             tcpv6_init(void);
-extern void                            tcpv6_exit(void);
-
-extern int                             udpv6_connect(struct sock *sk,
-                                                     struct sockaddr *uaddr,
-                                                     int addr_len);
-
-extern int                     ip6_datagram_recv_ctl(struct sock *sk,
-                                                     struct msghdr *msg,
-                                                     struct sk_buff *skb);
-
-extern int                     ip6_datagram_send_ctl(struct net *net,
-                                                     struct sock *sk,
-                                                     struct msghdr *msg,
-                                                     struct flowi6 *fl6,
-                                                     struct ipv6_txoptions *opt,
-                                                     int *hlimit, int *tclass,
-                                                     int *dontfrag);
-
-#define                LOOPBACK4_IPV6          cpu_to_be32(0x7f000006)
-
-/*
- *     address family specific functions
- */
+int pingv6_init(void);
+void pingv6_exit(void);
+int rawv6_init(void);
+void rawv6_exit(void);
+int udpv6_init(void);
+void udpv6_exit(void);
+int udplitev6_init(void);
+void udplitev6_exit(void);
+int tcpv6_init(void);
+void tcpv6_exit(void);
+
+int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
+
+int ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg,
+                         struct sk_buff *skb);
+
+int ip6_datagram_send_ctl(struct net *net, struct sock *sk, struct msghdr *msg,
+                         struct flowi6 *fl6, struct ipv6_txoptions *opt,
+                         int *hlimit, int *tclass, int *dontfrag);
+
+void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
+                            __u16 srcp, __u16 destp, int bucket);
+
+#define LOOPBACK4_IPV6 cpu_to_be32(0x7f000006)
+
+/* address family specific functions */
 extern const struct inet_connection_sock_af_ops ipv4_specific;
 
-extern void inet6_destroy_sock(struct sock *sk);
+void inet6_destroy_sock(struct sock *sk);
+
+#define IPV6_SEQ_DGRAM_HEADER                                         \
+       "  sl  "                                                       \
+       "local_address                         "                       \
+       "remote_address                        "                       \
+       "st tx_queue rx_queue tr tm->when retrnsmt"                    \
+       "   uid  timeout inode ref pointer drops\n"
 
 #endif
index 065f379..74c10ec 100644 (file)
@@ -181,12 +181,15 @@ extern int udp_get_port(struct sock *sk, unsigned short snum,
 extern void udp_err(struct sk_buff *, u32);
 extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk,
                            struct msghdr *msg, size_t len);
+extern int udp_push_pending_frames(struct sock *sk);
 extern void udp_flush_pending_frames(struct sock *sk);
 extern int udp_rcv(struct sk_buff *skb);
 extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
 extern int udp_disconnect(struct sock *sk, int flags);
 extern unsigned int udp_poll(struct file *file, struct socket *sock,
                             poll_table *wait);
+extern struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
+                                             netdev_features_t features);
 extern int udp_lib_getsockopt(struct sock *sk, int level, int optname,
                              char __user *optval, int __user *optlen);
 extern int udp_lib_setsockopt(struct sock *sk, int level, int optname,
@@ -262,11 +265,10 @@ extern int udp4_proc_init(void);
 extern void udp4_proc_exit(void);
 #endif
 
+extern int udpv4_offload_init(void);
+
 extern void udp_init(void);
 
-extern int udp4_ufo_send_check(struct sk_buff *skb);
-extern struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
-       netdev_features_t features);
 extern void udp_encap_enable(void);
 #if IS_ENABLED(CONFIG_IPV6)
 extern void udpv6_encap_enable(void);
index beeaed8..a066636 100644 (file)
@@ -143,31 +143,9 @@ TRACE_EVENT(9p_protocol_dump,
                    __entry->tag    =  pdu->tag;
                    memcpy(__entry->line, pdu->sdata, P9_PROTO_DUMP_SZ);
                    ),
-           TP_printk("clnt %lu %s(tag = %d)\n%.3x: "
-                     "%02x %02x %02x %02x %02x %02x %02x %02x "
-                     "%02x %02x %02x %02x %02x %02x %02x %02x\n"
-                     "%.3x: "
-                     "%02x %02x %02x %02x %02x %02x %02x %02x "
-                     "%02x %02x %02x %02x %02x %02x %02x %02x\n",
-                     (long)__entry->clnt, show_9p_op(__entry->type),
-                     __entry->tag, 0,
-                     __entry->line[0],  __entry->line[1],
-                     __entry->line[2],  __entry->line[3],
-                     __entry->line[4],  __entry->line[5],
-                     __entry->line[6],  __entry->line[7],
-                     __entry->line[8],  __entry->line[9],
-                     __entry->line[10], __entry->line[11],
-                     __entry->line[12], __entry->line[13],
-                     __entry->line[14], __entry->line[15],
-                     16,
-                     __entry->line[16], __entry->line[17],
-                     __entry->line[18], __entry->line[19],
-                     __entry->line[20], __entry->line[21],
-                     __entry->line[22], __entry->line[23],
-                     __entry->line[24], __entry->line[25],
-                     __entry->line[26], __entry->line[27],
-                     __entry->line[28], __entry->line[29],
-                     __entry->line[30], __entry->line[31])
+           TP_printk("clnt %lu %s(tag = %d)\n%.3x: %16ph\n%.3x: %16ph\n",
+                     (unsigned long)__entry->clnt, show_9p_op(__entry->type),
+                     __entry->tag, 0, __entry->line, 16, __entry->line + 16)
  );
 
 #endif /* _TRACE_9P_H */
index ea546a4..2902657 100644 (file)
@@ -40,22 +40,25 @@ struct extent_buffer;
                { BTRFS_ROOT_TREE_DIR_OBJECTID, "ROOT_TREE_DIR" },      \
                { BTRFS_CSUM_TREE_OBJECTID,     "CSUM_TREE"     },      \
                { BTRFS_TREE_LOG_OBJECTID,      "TREE_LOG"      },      \
+               { BTRFS_QUOTA_TREE_OBJECTID,    "QUOTA_TREE"    },      \
                { BTRFS_TREE_RELOC_OBJECTID,    "TREE_RELOC"    },      \
                { BTRFS_DATA_RELOC_TREE_OBJECTID, "DATA_RELOC_TREE" })
 
 #define show_root_type(obj)                                            \
        obj, ((obj >= BTRFS_DATA_RELOC_TREE_OBJECTID) ||                \
              (obj >= BTRFS_ROOT_TREE_OBJECTID &&                       \
-              obj <= BTRFS_CSUM_TREE_OBJECTID)) ? __show_root_type(obj) : "-"
+              obj <= BTRFS_QUOTA_TREE_OBJECTID)) ? __show_root_type(obj) : "-"
 
 #define BTRFS_GROUP_FLAGS      \
-       { BTRFS_BLOCK_GROUP_DATA,       "DATA"}, \
-       { BTRFS_BLOCK_GROUP_SYSTEM,     "SYSTEM"}, \
-       { BTRFS_BLOCK_GROUP_METADATA,   "METADATA"}, \
-       { BTRFS_BLOCK_GROUP_RAID0,      "RAID0"}, \
-       { BTRFS_BLOCK_GROUP_RAID1,      "RAID1"}, \
-       { BTRFS_BLOCK_GROUP_DUP,        "DUP"}, \
-       { BTRFS_BLOCK_GROUP_RAID10,     "RAID10"}
+       { BTRFS_BLOCK_GROUP_DATA,       "DATA"},        \
+       { BTRFS_BLOCK_GROUP_SYSTEM,     "SYSTEM"},      \
+       { BTRFS_BLOCK_GROUP_METADATA,   "METADATA"},    \
+       { BTRFS_BLOCK_GROUP_RAID0,      "RAID0"},       \
+       { BTRFS_BLOCK_GROUP_RAID1,      "RAID1"},       \
+       { BTRFS_BLOCK_GROUP_DUP,        "DUP"},         \
+       { BTRFS_BLOCK_GROUP_RAID10,     "RAID10"},      \
+       { BTRFS_BLOCK_GROUP_RAID5,      "RAID5"},       \
+       { BTRFS_BLOCK_GROUP_RAID6,      "RAID6"}
 
 #define BTRFS_UUID_SIZE 16
 
@@ -154,7 +157,9 @@ DEFINE_EVENT(btrfs__inode, btrfs_inode_evict,
                { EXTENT_FLAG_PINNED,           "PINNED"        },      \
                { EXTENT_FLAG_COMPRESSED,       "COMPRESSED"    },      \
                { EXTENT_FLAG_VACANCY,          "VACANCY"       },      \
-               { EXTENT_FLAG_PREALLOC,         "PREALLOC"      })
+               { EXTENT_FLAG_PREALLOC,         "PREALLOC"      },      \
+               { EXTENT_FLAG_LOGGING,          "LOGGING"       },      \
+               { EXTENT_FLAG_FILLING,          "FILLING"       })
 
 TRACE_EVENT(btrfs_get_extent,
 
@@ -201,13 +206,17 @@ TRACE_EVENT(btrfs_get_extent,
 );
 
 #define show_ordered_flags(flags)                                      \
-       __print_symbolic(flags,                                 \
+       __print_symbolic(flags,                                         \
                { BTRFS_ORDERED_IO_DONE,        "IO_DONE"       },      \
                { BTRFS_ORDERED_COMPLETE,       "COMPLETE"      },      \
                { BTRFS_ORDERED_NOCOW,          "NOCOW"         },      \
                { BTRFS_ORDERED_COMPRESSED,     "COMPRESSED"    },      \
                { BTRFS_ORDERED_PREALLOC,       "PREALLOC"      },      \
-               { BTRFS_ORDERED_DIRECT,         "DIRECT"        })
+               { BTRFS_ORDERED_DIRECT,         "DIRECT"        },      \
+               { BTRFS_ORDERED_IOERR,          "IOERR"         },      \
+               { BTRFS_ORDERED_UPDATED_ISIZE,  "UPDATED_ISIZE" },      \
+               { BTRFS_ORDERED_LOGGED_CSUM,    "LOGGED_CSUM"   })
+
 
 DECLARE_EVENT_CLASS(btrfs__ordered_extent,
 
@@ -555,7 +564,9 @@ TRACE_EVENT(btrfs_delayed_ref_head,
                { BTRFS_BLOCK_GROUP_RAID0,      "RAID0" },      \
                { BTRFS_BLOCK_GROUP_RAID1,      "RAID1" },      \
                { BTRFS_BLOCK_GROUP_DUP,        "DUP"   },      \
-               { BTRFS_BLOCK_GROUP_RAID10,     "RAID10"})
+               { BTRFS_BLOCK_GROUP_RAID10,     "RAID10"},      \
+               { BTRFS_BLOCK_GROUP_RAID5,      "RAID5" },      \
+               { BTRFS_BLOCK_GROUP_RAID6,      "RAID6" })
 
 DECLARE_EVENT_CLASS(btrfs__chunk,
 
index 9ce7f44..a969498 100644 (file)
@@ -30,6 +30,8 @@
 
 #define POLLFREE       0x4000  /* currently only for epoll */
 
+#define POLL_BUSY_LOOP 0x8000
+
 struct pollfd {
        int fd;
        short events;
index c5d2e3a..ca3a20d 100644 (file)
@@ -76,4 +76,6 @@
 
 #define SO_SELECT_ERR_QUEUE    45
 
+#define SO_LL                  46
+
 #endif /* __ASM_GENERIC_SOCKET_H */
index 5a57be6..238a166 100644 (file)
@@ -732,6 +732,7 @@ struct drm_prime_handle {
 #define DRM_IOCTL_MODE_ADDFB2          DRM_IOWR(0xB8, struct drm_mode_fb_cmd2)
 #define DRM_IOCTL_MODE_OBJ_GETPROPERTIES       DRM_IOWR(0xB9, struct drm_mode_obj_get_properties)
 #define DRM_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xBA, struct drm_mode_obj_set_property)
+#define DRM_IOCTL_MODE_CURSOR2         DRM_IOWR(0xBB, struct drm_mode_cursor2)
 
 /**
  * Device specific ioctls should only be in their respective headers
index 090e533..53db7ce 100644 (file)
@@ -388,6 +388,19 @@ struct drm_mode_cursor {
        __u32 handle;
 };
 
+struct drm_mode_cursor2 {
+       __u32 flags;
+       __u32 crtc_id;
+       __s32 x;
+       __s32 y;
+       __u32 width;
+       __u32 height;
+       /* driver specific handle */
+       __u32 handle;
+       __s32 hot_x;
+       __s32 hot_y;
+};
+
 struct drm_mode_crtc_lut {
        __u32 crtc_id;
        __u32 gamma_size;
index 07d5941..923ed7f 100644 (file)
@@ -305,7 +305,7 @@ typedef struct drm_i915_irq_wait {
 #define I915_PARAM_HAS_WAIT_TIMEOUT     19
 #define I915_PARAM_HAS_SEMAPHORES       20
 #define I915_PARAM_HAS_PRIME_VMAP_FLUSH         21
-#define I915_PARAM_RSVD_FOR_FUTURE_USE  22
+#define I915_PARAM_HAS_VEBOX            22
 #define I915_PARAM_HAS_SECURE_BATCHES   23
 #define I915_PARAM_HAS_PINNED_BATCHES   24
 #define I915_PARAM_HAS_EXEC_NO_RELOC    25
@@ -660,6 +660,7 @@ struct drm_i915_gem_execbuffer2 {
 #define I915_EXEC_RENDER                 (1<<0)
 #define I915_EXEC_BSD                    (2<<0)
 #define I915_EXEC_BLT                    (3<<0)
+#define I915_EXEC_VEBOX                  (4<<0)
 
 /* Used for switching the constants addressing mode on gen4+ RENDER ring.
  * Gen6+ only supports relative addressing to dynamic state (default) and
index 6e132a2..73bde4e 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef _UAPI_TEGRA_DRM_H_
 #define _UAPI_TEGRA_DRM_H_
 
+#include <drm/drm.h>
+
 struct drm_tegra_gem_create {
        __u64 size;
        __u32 flags;
index 5ef0df5..05aed70 100644 (file)
@@ -447,6 +447,46 @@ struct btrfs_ioctl_send_args {
        __u64 reserved[4];              /* in */
 };
 
+/* Error codes as returned by the kernel */
+enum btrfs_err_code {
+       notused,
+       BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET,
+       BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET,
+       BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET,
+       BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET,
+       BTRFS_ERROR_DEV_TGT_REPLACE,
+       BTRFS_ERROR_DEV_MISSING_NOT_FOUND,
+       BTRFS_ERROR_DEV_ONLY_WRITABLE,
+       BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS
+};
+/* An error code to error string mapping for the kernel
+*  error codes
+*/
+static inline char *btrfs_err_str(enum btrfs_err_code err_code)
+{
+       switch (err_code) {
+               case BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET:
+                       return "unable to go below two devices on raid1";
+               case BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET:
+                       return "unable to go below four devices on raid10";
+               case BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET:
+                       return "unable to go below two devices on raid5";
+               case BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET:
+                       return "unable to go below three devices on raid6";
+               case BTRFS_ERROR_DEV_TGT_REPLACE:
+                       return "unable to remove the dev_replace target dev";
+               case BTRFS_ERROR_DEV_MISSING_NOT_FOUND:
+                       return "no missing devices found to remove";
+               case BTRFS_ERROR_DEV_ONLY_WRITABLE:
+                       return "unable to remove the only writeable device";
+               case BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS:
+                       return "add/delete/balance/replace/resize operation "\
+                               "in progress";
+               default:
+                       return NULL;
+       }
+}
+
 #define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
                                   struct btrfs_ioctl_vol_args)
 #define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
@@ -530,6 +570,7 @@ struct btrfs_ioctl_send_args {
                               struct btrfs_ioctl_quota_rescan_args)
 #define BTRFS_IOC_QUOTA_RESCAN_STATUS _IOR(BTRFS_IOCTL_MAGIC, 45, \
                               struct btrfs_ioctl_quota_rescan_args)
+#define BTRFS_IOC_QUOTA_RESCAN_WAIT _IO(BTRFS_IOCTL_MAGIC, 46)
 #define BTRFS_IOC_GET_FSLABEL _IOR(BTRFS_IOCTL_MAGIC, 49, \
                                   char[BTRFS_LABEL_SIZE])
 #define BTRFS_IOC_SET_FSLABEL _IOW(BTRFS_IOCTL_MAGIC, 50, \
@@ -538,5 +579,4 @@ struct btrfs_ioctl_send_args {
                                      struct btrfs_ioctl_get_dev_stats)
 #define BTRFS_IOC_DEV_REPLACE _IOWR(BTRFS_IOCTL_MAGIC, 53, \
                                    struct btrfs_ioctl_dev_replace_args)
-
 #endif /* _UAPI_LINUX_BTRFS_H */
index 0c9b448..38dbafa 100644 (file)
@@ -993,8 +993,8 @@ enum ethtool_sfeatures_retval_bits {
 #define PORT_OTHER             0xff
 
 /* Which transceiver to use. */
-#define XCVR_INTERNAL          0x00
-#define XCVR_EXTERNAL          0x01
+#define XCVR_INTERNAL          0x00 /* PHY and MAC are in the same package */
+#define XCVR_EXTERNAL          0x01 /* PHY and MAC are in different packages */
 #define XCVR_DUMMY1            0x02
 #define XCVR_DUMMY2            0x03
 #define XCVR_DUMMY3            0x04
index 552c8a0..6487317 100644 (file)
@@ -9,6 +9,7 @@ enum {
        TCA_STATS_RATE_EST,
        TCA_STATS_QUEUE,
        TCA_STATS_APP,
+       TCA_STATS_RATE_EST64,
        __TCA_STATS_MAX,
 };
 #define TCA_STATS_MAX (__TCA_STATS_MAX - 1)
@@ -37,6 +38,16 @@ struct gnet_stats_rate_est {
        __u32   pps;
 };
 
+/**
+ * struct gnet_stats_rate_est64 - rate estimator
+ * @bps: current byte rate
+ * @pps: current packet rate
+ */
+struct gnet_stats_rate_est64 {
+       __u64   bps;
+       __u64   pps;
+};
+
 /**
  * struct gnet_stats_queue - queuing statistics
  * @qlen: queue length
index 82c7d1b..d7fea34 100644 (file)
@@ -93,6 +93,7 @@
 #define ARPHRD_PHONET_PIPE 821         /* PhoNet pipe header           */
 #define ARPHRD_CAIF    822             /* CAIF media type              */
 #define ARPHRD_IP6GRE  823             /* GRE over IPv6                */
+#define ARPHRD_NETLINK 824             /* Netlink header               */
 
 #define ARPHRD_VOID      0xFFFF        /* Void type, nothing is known */
 #define ARPHRD_NONE      0xFFFE        /* zero header length */
index b05823c..03f6170 100644 (file)
@@ -221,6 +221,8 @@ enum {
        IFLA_BRPORT_GUARD,      /* bpdu guard              */
        IFLA_BRPORT_PROTECT,    /* root port protection    */
        IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave    */
+       IFLA_BRPORT_LEARNING,   /* mac learning */
+       IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */
        __IFLA_BRPORT_MAX
 };
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
@@ -336,6 +338,7 @@ enum {
        IFLA_VF_VLAN,
        IFLA_VF_TX_RATE,        /* TX Bandwidth Allocation */
        IFLA_VF_SPOOFCHK,       /* Spoof Checking on/off switch */
+       IFLA_VF_LINK_STATE,     /* link state enable/disable/auto switch */
        __IFLA_VF_MAX,
 };
 
@@ -362,6 +365,18 @@ struct ifla_vf_spoofchk {
        __u32 setting;
 };
 
+enum {
+       IFLA_VF_LINK_STATE_AUTO,        /* link state of the uplink */
+       IFLA_VF_LINK_STATE_ENABLE,      /* link always up */
+       IFLA_VF_LINK_STATE_DISABLE,     /* link always down */
+       __IFLA_VF_LINK_STATE_MAX,
+};
+
+struct ifla_vf_link_state {
+       __u32 vf;
+       __u32 link_state;
+};
+
 /* VF ports management section
  *
  *     Nested layout of set/get msg is:
index 0b46fd5..e36a4ae 100644 (file)
@@ -135,11 +135,11 @@ struct pppoe_tag {
 
 struct pppoe_hdr {
 #if defined(__LITTLE_ENDIAN_BITFIELD)
-       __u8 ver : 4;
        __u8 type : 4;
+       __u8 ver : 4;
 #elif defined(__BIG_ENDIAN_BITFIELD)
-       __u8 type : 4;
        __u8 ver : 4;
+       __u8 type : 4;
 #else
 #error "Please fix <asm/byteorder.h>"
 #endif
index 2835b85..82334f8 100644 (file)
@@ -68,6 +68,8 @@
 #define IFF_MULTI_QUEUE 0x0100
 #define IFF_ATTACH_QUEUE 0x0200
 #define IFF_DETACH_QUEUE 0x0400
+/* read-only flag */
+#define IFF_PERSIST    0x0800
 
 /* Features for GSO (TUNSETOFFLOAD). */
 #define TUN_F_CSUM     0x01    /* You can hand me unchecksummed packets. */
index a245377..2945822 100644 (file)
 #define IP_VS_SVC_F_PERSISTENT 0x0001          /* persistent port */
 #define IP_VS_SVC_F_HASHED     0x0002          /* hashed entry */
 #define IP_VS_SVC_F_ONEPACKET  0x0004          /* one-packet scheduling */
+#define IP_VS_SVC_F_SCHED1     0x0008          /* scheduler flag 1 */
+#define IP_VS_SVC_F_SCHED2     0x0010          /* scheduler flag 2 */
+#define IP_VS_SVC_F_SCHED3     0x0020          /* scheduler flag 3 */
+
+#define IP_VS_SVC_F_SCHED_SH_FALLBACK  IP_VS_SVC_F_SCHED1 /* SH fallback */
+#define IP_VS_SVC_F_SCHED_SH_PORT      IP_VS_SVC_F_SCHED2 /* SH use port */
 
 /*
  *      Destination Server Flags
index f055e58..e284ff9 100644 (file)
@@ -104,6 +104,8 @@ struct __fat_dirent {
 /* <linux/videotext.h> has used 0x72 ('r') in collision, so skip a few */
 #define FAT_IOCTL_GET_ATTRIBUTES       _IOR('r', 0x10, __u32)
 #define FAT_IOCTL_SET_ATTRIBUTES       _IOW('r', 0x11, __u32)
+/*Android kernel has used 0x12, so we use 0x13*/
+#define FAT_IOCTL_GET_VOLUME_ID                _IOR('r', 0x13, __u32)
 
 struct fat_boot_sector {
        __u8    ignored[3];     /* Boot strap short or near jump */
@@ -128,6 +130,10 @@ struct fat_boot_sector {
                        __u8    drive_number;   /* Physical drive number */
                        __u8    state;          /* undocumented, but used
                                                   for mount state. */
+                       __u8    signature;  /* extended boot signature */
+                       __u8    vol_id[4];      /* volume ID */
+                       __u8    vol_label[11];  /* volume label */
+                       __u8    fs_type[8];             /* file system type */
                        /* other fiealds are not added here */
                } fat16;
 
@@ -147,6 +153,10 @@ struct fat_boot_sector {
                        __u8    drive_number;   /* Physical drive number */
                        __u8    state;          /* undocumented, but used
                                                   for mount state. */
+                       __u8    signature;  /* extended boot signature */
+                       __u8    vol_id[4];      /* volume ID */
+                       __u8    vol_label[11];  /* volume label */
+                       __u8    fs_type[8];             /* file system type */
                        /* other fiealds are not added here */
                } fat32;
        };
index a2308ae..3a9b921 100644 (file)
@@ -105,5 +105,7 @@ enum nfqnl_attr_config {
 #define NFQA_SKB_CSUMNOTREADY (1 << 0)
 /* packet is GSO (i.e., exceeds device mtu) */
 #define NFQA_SKB_GSO (1 << 1)
+/* csum not validated (incoming device doesn't support hw checksum, etc.) */
+#define NFQA_SKB_CSUM_NOTVERIFIED (1 << 2)
 
 #endif /* _NFNETLINK_QUEUE_H */
index 26d7217..6315e2a 100644 (file)
@@ -5,10 +5,17 @@
 
 enum {
        XT_SOCKET_TRANSPARENT = 1 << 0,
+       XT_SOCKET_NOWILDCARD = 1 << 1,
 };
 
 struct xt_socket_mtinfo1 {
        __u8 flags;
 };
+#define XT_SOCKET_FLAGS_V1 XT_SOCKET_TRANSPARENT
+
+struct xt_socket_mtinfo2 {
+       __u8 flags;
+};
+#define XT_SOCKET_FLAGS_V2 (XT_SOCKET_TRANSPARENT | XT_SOCKET_NOWILDCARD)
 
 #endif /* _XT_SOCKET_H */
index 7c6f627..caed0f3 100644 (file)
@@ -69,6 +69,8 @@
  *     starting a poll from a device which has a secure element enabled means
  *     we want to do SE based card emulation.
  * @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element.
+ * @NFC_CMD_FW_UPLOAD: Request to Load/flash firmware, or event to inform that
+ *     some firmware was loaded
  */
 enum nfc_commands {
        NFC_CMD_UNSPEC,
@@ -92,6 +94,9 @@ enum nfc_commands {
        NFC_CMD_DISABLE_SE,
        NFC_CMD_LLC_SDREQ,
        NFC_EVENT_LLC_SDRES,
+       NFC_CMD_FW_UPLOAD,
+       NFC_EVENT_SE_ADDED,
+       NFC_EVENT_SE_REMOVED,
 /* private: internal use only */
        __NFC_CMD_AFTER_LAST
 };
@@ -121,6 +126,9 @@ enum nfc_commands {
  * @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter
  * @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter
  * @NFC_ATTR_SE: Available Secure Elements
+ * @NFC_ATTR_FIRMWARE_NAME: Free format firmware version
+ * @NFC_ATTR_SE_INDEX: Secure element index
+ * @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED)
  */
 enum nfc_attrs {
        NFC_ATTR_UNSPEC,
@@ -143,6 +151,9 @@ enum nfc_attrs {
        NFC_ATTR_LLC_PARAM_MIUX,
        NFC_ATTR_SE,
        NFC_ATTR_LLC_SDP,
+       NFC_ATTR_FIRMWARE_NAME,
+       NFC_ATTR_SE_INDEX,
+       NFC_ATTR_SE_TYPE,
 /* private: internal use only */
        __NFC_ATTR_AFTER_LAST
 };
@@ -159,9 +170,12 @@ enum nfc_sdp_attr {
 
 #define NFC_DEVICE_NAME_MAXSIZE 8
 #define NFC_NFCID1_MAXSIZE 10
+#define NFC_NFCID2_MAXSIZE 8
+#define NFC_NFCID3_MAXSIZE 10
 #define NFC_SENSB_RES_MAXSIZE 12
 #define NFC_SENSF_RES_MAXSIZE 18
 #define NFC_GB_MAXSIZE        48
+#define NFC_FIRMWARE_NAME_MAXSIZE 32
 
 /* NFC protocols */
 #define NFC_PROTO_JEWEL                1
@@ -191,10 +205,12 @@ enum nfc_sdp_attr {
 #define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B)
 
 /* NFC Secure Elements */
-#define NFC_SE_NONE     0x0
 #define NFC_SE_UICC     0x1
 #define NFC_SE_EMBEDDED 0x2
 
+#define NFC_SE_DISABLED 0x0
+#define NFC_SE_ENABLED  0x1
+
 struct sockaddr_nfc {
        sa_family_t sa_family;
        __u32 dev_idx;
index d1e48b5..861e5eb 100644 (file)
@@ -27,6 +27,8 @@
 
 #include <linux/types.h>
 
+#define NL80211_GENL_NAME "nl80211"
+
 /**
  * DOC: Station handling
  *
@@ -1429,6 +1431,11 @@ enum nl80211_commands {
  * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which
  *      the connection should have increased reliability (u16).
  *
+ * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16).
+ *     This is similar to @NL80211_ATTR_STA_AID but with a difference of being
+ *     allowed to be used with the first @NL80211_CMD_SET_STATION command to
+ *     update a TDLS peer STA entry.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1727,6 +1734,8 @@ enum nl80211_attrs {
        NL80211_ATTR_CRIT_PROT_ID,
        NL80211_ATTR_MAX_CRIT_PROT_DURATION,
 
+       NL80211_ATTR_PEER_AID,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -1991,6 +2000,10 @@ enum nl80211_sta_bss_param {
  * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode
  * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards
  *     non-peer STA
+ * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU
+ *     Contains a nested array of signal strength attributes (u8, dBm)
+ * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
+ *     Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -2020,6 +2033,8 @@ enum nl80211_sta_info {
        NL80211_STA_INFO_NONPEER_PM,
        NL80211_STA_INFO_RX_BYTES64,
        NL80211_STA_INFO_TX_BYTES64,
+       NL80211_STA_INFO_CHAIN_SIGNAL,
+       NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
 
        /* keep last */
        __NL80211_STA_INFO_AFTER_LAST,
@@ -2413,6 +2428,8 @@ enum nl80211_survey_info {
  * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering
  * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing.
  *     overrides all other flags.
+ * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address
+ *     and ACK incoming unicast packets.
  *
  * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use
  * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag
@@ -2424,6 +2441,7 @@ enum nl80211_mntr_flags {
        NL80211_MNTR_FLAG_CONTROL,
        NL80211_MNTR_FLAG_OTHER_BSS,
        NL80211_MNTR_FLAG_COOK_FRAMES,
+       NL80211_MNTR_FLAG_ACTIVE,
 
        /* keep last */
        __NL80211_MNTR_FLAG_AFTER_LAST,
@@ -2559,6 +2577,10 @@ enum nl80211_mesh_power_mode {
  *
  * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
  *
+ * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
+ *     established peering with for longer than this time (in seconds), then
+ *     remove it from the STA's list of peers.  Default is 30 minutes.
+ *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_meshconf_params {
@@ -2590,6 +2612,7 @@ enum nl80211_meshconf_params {
        NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
        NL80211_MESHCONF_POWER_MODE,
        NL80211_MESHCONF_AWAKE_WINDOW,
+       NL80211_MESHCONF_PLINK_TIMEOUT,
 
        /* keep last */
        __NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -2637,6 +2660,10 @@ enum nl80211_meshconf_params {
  * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will
  *     implement an MPM which handles peer allocation and state.
  *
+ * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication
+ *     method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE).
+ *     Default is no authentication method required.
+ *
  * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
  *
  * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
@@ -2650,6 +2677,7 @@ enum nl80211_mesh_setup_params {
        NL80211_MESH_SETUP_USERSPACE_AMPE,
        NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
        NL80211_MESH_SETUP_USERSPACE_MPM,
+       NL80211_MESH_SETUP_AUTH_PROTOCOL,
 
        /* keep last */
        __NL80211_MESH_SETUP_ATTR_AFTER_LAST,
@@ -2730,6 +2758,8 @@ enum nl80211_channel_type {
  *     and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well
  * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
  *     attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel
  */
 enum nl80211_chan_width {
        NL80211_CHAN_WIDTH_20_NOHT,
@@ -2738,6 +2768,8 @@ enum nl80211_chan_width {
        NL80211_CHAN_WIDTH_80,
        NL80211_CHAN_WIDTH_80P80,
        NL80211_CHAN_WIDTH_160,
+       NL80211_CHAN_WIDTH_5,
+       NL80211_CHAN_WIDTH_10,
 };
 
 /**
@@ -3556,6 +3588,10 @@ enum nl80211_ap_sme_features {
  *     Peering Management entity which may be implemented by registering for
  *     beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is
  *     still generated by the driver.
+ * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor
+ *     interface. An active monitor interface behaves like a normal monitor
+ *     interface, but gets added to the driver. It ensures that incoming
+ *     unicast packets directed at the configured interface address get ACKed.
  */
 enum nl80211_feature_flags {
        NL80211_FEATURE_SK_TX_STATUS                    = 1 << 0,
@@ -3575,6 +3611,7 @@ enum nl80211_feature_flags {
        NL80211_FEATURE_ADVERTISE_CHAN_LIMITS           = 1 << 14,
        NL80211_FEATURE_FULL_AP_CLIENT_STATE            = 1 << 15,
        NL80211_FEATURE_USERSPACE_MPM                   = 1 << 16,
+       NL80211_FEATURE_ACTIVE_MONITOR                  = 1 << 17,
 };
 
 /**
index 405918d..c55efaa 100644 (file)
@@ -164,6 +164,7 @@ enum ovs_vport_type {
        OVS_VPORT_TYPE_UNSPEC,
        OVS_VPORT_TYPE_NETDEV,   /* network device */
        OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */
+       OVS_VPORT_TYPE_GRE,      /* GRE tunnel. */
        __OVS_VPORT_TYPE_MAX
 };
 
@@ -192,7 +193,6 @@ enum ovs_vport_type {
  * optional; if not specified a free port number is automatically selected.
  * Whether %OVS_VPORT_ATTR_OPTIONS is required or optional depends on the type
  * of vport.
- * and other attributes are ignored.
  *
  * For other requests, if %OVS_VPORT_ATTR_NAME is specified then it is used to
  * look up the vport to operate on; otherwise dp_idx from the &struct
@@ -247,11 +247,29 @@ enum ovs_key_attr {
        OVS_KEY_ATTR_ARP,       /* struct ovs_key_arp */
        OVS_KEY_ATTR_ND,        /* struct ovs_key_nd */
        OVS_KEY_ATTR_SKB_MARK,  /* u32 skb mark */
+       OVS_KEY_ATTR_TUNNEL,    /* Nested set of ovs_tunnel attributes */
+
+#ifdef __KERNEL__
+       OVS_KEY_ATTR_IPV4_TUNNEL,  /* struct ovs_key_ipv4_tunnel */
+#endif
        __OVS_KEY_ATTR_MAX
 };
 
 #define OVS_KEY_ATTR_MAX (__OVS_KEY_ATTR_MAX - 1)
 
+enum ovs_tunnel_key_attr {
+       OVS_TUNNEL_KEY_ATTR_ID,                 /* be64 Tunnel ID */
+       OVS_TUNNEL_KEY_ATTR_IPV4_SRC,           /* be32 src IP address. */
+       OVS_TUNNEL_KEY_ATTR_IPV4_DST,           /* be32 dst IP address. */
+       OVS_TUNNEL_KEY_ATTR_TOS,                /* u8 Tunnel IP ToS. */
+       OVS_TUNNEL_KEY_ATTR_TTL,                /* u8 Tunnel IP TTL. */
+       OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT,      /* No argument, set DF. */
+       OVS_TUNNEL_KEY_ATTR_CSUM,               /* No argument. CSUM packet. */
+       __OVS_TUNNEL_KEY_ATTR_MAX
+};
+
+#define OVS_TUNNEL_KEY_ATTR_MAX (__OVS_TUNNEL_KEY_ATTR_MAX - 1)
+
 /**
  * enum ovs_frag_type - IPv4 and IPv6 fragment type
  * @OVS_FRAG_TYPE_NONE: Packet is not a fragment.
index 7a2144e..eb0f1a5 100644 (file)
@@ -386,6 +386,8 @@ enum {
 #define RTAX_RTO_MIN RTAX_RTO_MIN
        RTAX_INITRWND,
 #define RTAX_INITRWND RTAX_INITRWND
+       RTAX_QUICKACK,
+#define RTAX_QUICKACK RTAX_QUICKACK
        __RTAX_MAX
 };
 
index df2e8b4..af0a674 100644 (file)
@@ -253,6 +253,7 @@ enum
        LINUX_MIB_TCPFASTOPENLISTENOVERFLOW,    /* TCPFastOpenListenOverflow */
        LINUX_MIB_TCPFASTOPENCOOKIEREQD,        /* TCPFastOpenCookieReqd */
        LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */
+       LINUX_MIB_LOWLATENCYRXPACKETS,          /* LowLatencyRxPackets */
        __LINUX_MIB_MAX
 };
 
@@ -287,6 +288,7 @@ enum
        LINUX_MIB_XFRMOUTPOLERROR,              /* XfrmOutPolError */
        LINUX_MIB_XFRMFWDHDRERROR,              /* XfrmFwdHdrError*/
        LINUX_MIB_XFRMOUTSTATEINVALID,          /* XfrmOutStateInvalid */
+       LINUX_MIB_XFRMACQUIREERROR,             /* XfrmAcquireError */
        __LINUX_MIB_XFRMMAX
 };
 
index f2d9009..852373d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * include/linux/tipc.h: Include file for TIPC socket interface
+ * include/uapi/linux/tipc.h: Header for TIPC socket interface
  *
  * Copyright (c) 2003-2006, Ericsson AB
  * Copyright (c) 2005, 2010-2011, Wind River Systems
index 0b1e3f2..6b0bff0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * include/linux/tipc_config.h: Include file for TIPC configuration interface
+ * include/uapi/linux/tipc_config.h: Header for TIPC configuration interface
  *
  * Copyright (c) 2003-2006, Ericsson AB
  * Copyright (c) 2005-2007, 2010-2011, Wind River Systems
index 5d0259b..28d9d0d 100644 (file)
@@ -27,6 +27,7 @@ enum display_flags {
        DISPLAY_FLAGS_PIXDATA_NEGEDGE   = BIT(7),
        DISPLAY_FLAGS_INTERLACED        = BIT(8),
        DISPLAY_FLAGS_DOUBLESCAN        = BIT(9),
+       DISPLAY_FLAGS_DOUBLECLK         = BIT(10),
 };
 
 /*
index 8016eb7..79e6697 100644 (file)
 #define __LINUX_OF_DISPLAY_TIMING_H
 
 struct device_node;
+struct display_timing;
 struct display_timings;
 
 #define OF_USE_NATIVE_MODE -1
 
+int of_get_display_timing(struct device_node *np, const char *name,
+               struct display_timing *dt);
 struct display_timings *of_get_display_timings(struct device_node *np);
 int of_display_timings_exist(struct device_node *np);
 
index 0c3b46d..6b2366f 100644 (file)
@@ -27,6 +27,9 @@
 #ifndef __OMAP_PANEL_DATA_H
 #define __OMAP_PANEL_DATA_H
 
+#include <video/omapdss.h>
+#include <video/display_timing.h>
+
 struct omap_dss_device;
 
 /**
@@ -147,4 +150,210 @@ struct panel_tpo_td043_data {
        int nreset_gpio;
 };
 
+/**
+ * encoder_tfp410 platform data
+ * @name: name for this display entity
+ * @power_down_gpio: gpio number for PD pin (or -1 if not available)
+ * @data_lines: number of DPI datalines
+ */
+struct encoder_tfp410_platform_data {
+       const char *name;
+       const char *source;
+       int power_down_gpio;
+       int data_lines;
+};
+
+/**
+ * encoder_tpd12s015 platform data
+ * @name: name for this display entity
+ * @ct_cp_hpd_gpio: CT_CP_HPD gpio number
+ * @ls_oe_gpio: LS_OE gpio number
+ * @hpd_gpio: HPD gpio number
+ */
+struct encoder_tpd12s015_platform_data {
+       const char *name;
+       const char *source;
+
+       int ct_cp_hpd_gpio;
+       int ls_oe_gpio;
+       int hpd_gpio;
+};
+
+/**
+ * connector_dvi platform data
+ * @name: name for this display entity
+ * @source: name of the display entity used as a video source
+ * @i2c_bus_num: i2c bus number to be used for reading EDID
+ */
+struct connector_dvi_platform_data {
+       const char *name;
+       const char *source;
+       int i2c_bus_num;
+};
+
+/**
+ * connector_hdmi platform data
+ * @name: name for this display entity
+ * @source: name of the display entity used as a video source
+ */
+struct connector_hdmi_platform_data {
+       const char *name;
+       const char *source;
+};
+
+/**
+ * connector_atv platform data
+ * @name: name for this display entity
+ * @source: name of the display entity used as a video source
+ * @connector_type: composite/svideo
+ * @invert_polarity: invert signal polarity
+ */
+struct connector_atv_platform_data {
+       const char *name;
+       const char *source;
+
+       enum omap_dss_venc_type connector_type;
+       bool invert_polarity;
+};
+
+/**
+ * panel_dpi platform data
+ * @name: name for this display entity
+ * @source: name of the display entity used as a video source
+ * @data_lines: number of DPI datalines
+ * @display_timing: timings for this panel
+ * @backlight_gpio: gpio to enable/disable the backlight (or -1)
+ * @enable_gpio: gpio to enable/disable the panel (or -1)
+ */
+struct panel_dpi_platform_data {
+       const char *name;
+       const char *source;
+
+       int data_lines;
+
+       const struct display_timing *display_timing;
+
+       int backlight_gpio;
+       int enable_gpio;
+};
+
+/**
+ * panel_dsicm platform data
+ * @name: name for this display entity
+ * @source: name of the display entity used as a video source
+ * @reset_gpio: gpio to reset the panel (or -1)
+ * @use_ext_te: use external TE GPIO
+ * @ext_te_gpio: external TE GPIO
+ * @ulps_timeout: time to wait before entering ULPS, 0 = disabled (ms)
+ * @use_dsi_backlight: true if panel uses DSI command to control backlight
+ * @pin_config: DSI pin configuration
+ */
+struct panel_dsicm_platform_data {
+       const char *name;
+       const char *source;
+
+       int reset_gpio;
+
+       bool use_ext_te;
+       int ext_te_gpio;
+
+       unsigned ulps_timeout;
+
+       bool use_dsi_backlight;
+
+       struct omap_dsi_pin_config pin_config;
+};
+
+/**
+ * panel_acx565akm platform data
+ * @name: name for this display entity
+ * @source: name of the display entity used as a video source
+ * @reset_gpio: gpio to reset the panel (or -1)
+ * @datapairs: number of SDI datapairs
+ */
+struct panel_acx565akm_platform_data {
+       const char *name;
+       const char *source;
+
+       int reset_gpio;
+
+       int datapairs;
+};
+
+/**
+ * panel_lb035q02 platform data
+ * @name: name for this display entity
+ * @source: name of the display entity used as a video source
+ * @data_lines: number of DPI datalines
+ * @backlight_gpio: gpio to enable/disable the backlight (or -1)
+ * @enable_gpio: gpio to enable/disable the panel (or -1)
+ */
+struct panel_lb035q02_platform_data {
+       const char *name;
+       const char *source;
+
+       int data_lines;
+
+       int backlight_gpio;
+       int enable_gpio;
+};
+
+/**
+ * panel_sharp_ls037v7dw01 platform data
+ * @name: name for this display entity
+ * @source: name of the display entity used as a video source
+ * @data_lines: number of DPI datalines
+ * @resb_gpio: reset signal GPIO
+ * @ini_gpio: power on control GPIO
+ * @mo_gpio: selection for resolution(VGA/QVGA) GPIO
+ * @lr_gpio: selection for horizontal scanning direction GPIO
+ * @ud_gpio: selection for vertical scanning direction GPIO
+ */
+struct panel_sharp_ls037v7dw01_platform_data {
+       const char *name;
+       const char *source;
+
+       int data_lines;
+
+       int resb_gpio;
+       int ini_gpio;
+       int mo_gpio;
+       int lr_gpio;
+       int ud_gpio;
+};
+
+/**
+ * panel-tpo-td043mtea1 platform data
+ * @name: name for this display entity
+ * @source: name of the display entity used as a video source
+ * @data_lines: number of DPI datalines
+ * @nreset_gpio: reset signal
+ */
+struct panel_tpo_td043mtea1_platform_data {
+       const char *name;
+       const char *source;
+
+       int data_lines;
+
+       int nreset_gpio;
+};
+
+/**
+ * panel-nec-nl8048hl11 platform data
+ * @name: name for this display entity
+ * @source: name of the display entity used as a video source
+ * @data_lines: number of DPI datalines
+ * @res_gpio: reset signal
+ * @qvga_gpio: selection for resolution(QVGA/WVGA)
+ */
+struct panel_nec_nl8048hl11_platform_data {
+       const char *name;
+       const char *source;
+
+       int data_lines;
+
+       int res_gpio;
+       int qvga_gpio;
+};
+
 #endif /* __OMAP_PANEL_DATA_H */
index aeb4e9a..b394635 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/device.h>
 #include <linux/interrupt.h>
 
+#include <video/videomode.h>
+
 #define DISPC_IRQ_FRAMEDONE            (1 << 0)
 #define DISPC_IRQ_VSYNC                        (1 << 1)
 #define DISPC_IRQ_EVSYNC_EVEN          (1 << 2)
@@ -68,6 +70,7 @@ enum omap_display_type {
        OMAP_DISPLAY_TYPE_DSI           = 1 << 3,
        OMAP_DISPLAY_TYPE_VENC          = 1 << 4,
        OMAP_DISPLAY_TYPE_HDMI          = 1 << 5,
+       OMAP_DISPLAY_TYPE_DVI           = 1 << 6,
 };
 
 enum omap_plane {
@@ -169,6 +172,11 @@ enum omap_dss_audio_state {
        OMAP_DSS_AUDIO_PLAYING,
 };
 
+struct omap_dss_audio {
+       struct snd_aes_iec958 *iec;
+       struct snd_cea_861_aud_if *cea;
+};
+
 enum omap_dss_rotation_type {
        OMAP_DSS_ROT_DMA        = 1 << 0,
        OMAP_DSS_ROT_VRFB       = 1 << 1,
@@ -365,6 +373,7 @@ struct omap_dss_board_info {
        int num_devices;
        struct omap_dss_device **devices;
        struct omap_dss_device *default_device;
+       const char *default_display_name;
        int (*dsi_enable_pads)(int dsi_id, unsigned lane_mask);
        void (*dsi_disable_pads)(int dsi_id, unsigned lane_mask);
        int (*set_min_bus_tput)(struct device *dev, unsigned long r);
@@ -512,7 +521,7 @@ struct omap_overlay_manager {
        enum omap_dss_output_id supported_outputs;
 
        /* dynamic fields */
-       struct omap_dss_output *output;
+       struct omap_dss_device *output;
 
        /*
         * The following functions do not block:
@@ -526,7 +535,7 @@ struct omap_overlay_manager {
         */
 
        int (*set_output)(struct omap_overlay_manager *mgr,
-               struct omap_dss_output *output);
+               struct omap_dss_device *output);
        int (*unset_output)(struct omap_overlay_manager *mgr);
 
        int (*set_manager_info)(struct omap_overlay_manager *mgr,
@@ -569,33 +578,192 @@ struct omap_dss_writeback_info {
        u8 pre_mult_alpha;
 };
 
-struct omap_dss_output {
-       struct list_head list;
+struct omapdss_dpi_ops {
+       int (*connect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
+       void (*disconnect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
 
-       const char *name;
+       int (*enable)(struct omap_dss_device *dssdev);
+       void (*disable)(struct omap_dss_device *dssdev);
 
-       /* display type supported by the output */
-       enum omap_display_type type;
+       int (*check_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*set_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*get_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
 
-       /* DISPC channel for this output */
-       enum omap_channel dispc_channel;
+       void (*set_data_lines)(struct omap_dss_device *dssdev, int data_lines);
+};
 
-       /* output instance */
-       enum omap_dss_output_id id;
+struct omapdss_sdi_ops {
+       int (*connect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
+       void (*disconnect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
 
-       /* output's platform device pointer */
-       struct platform_device *pdev;
+       int (*enable)(struct omap_dss_device *dssdev);
+       void (*disable)(struct omap_dss_device *dssdev);
 
-       /* dynamic fields */
-       struct omap_overlay_manager *manager;
+       int (*check_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*set_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*get_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
 
-       struct omap_dss_device *device;
+       void (*set_datapairs)(struct omap_dss_device *dssdev, int datapairs);
+};
+
+struct omapdss_dvi_ops {
+       int (*connect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
+       void (*disconnect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
+
+       int (*enable)(struct omap_dss_device *dssdev);
+       void (*disable)(struct omap_dss_device *dssdev);
+
+       int (*check_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*set_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*get_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+};
+
+struct omapdss_atv_ops {
+       int (*connect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
+       void (*disconnect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
+
+       int (*enable)(struct omap_dss_device *dssdev);
+       void (*disable)(struct omap_dss_device *dssdev);
+
+       int (*check_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*set_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*get_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+
+       void (*set_type)(struct omap_dss_device *dssdev,
+               enum omap_dss_venc_type type);
+       void (*invert_vid_out_polarity)(struct omap_dss_device *dssdev,
+               bool invert_polarity);
+
+       int (*set_wss)(struct omap_dss_device *dssdev, u32 wss);
+       u32 (*get_wss)(struct omap_dss_device *dssdev);
+};
+
+struct omapdss_hdmi_ops {
+       int (*connect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
+       void (*disconnect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
+
+       int (*enable)(struct omap_dss_device *dssdev);
+       void (*disable)(struct omap_dss_device *dssdev);
+
+       int (*check_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*set_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*get_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+
+       int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len);
+       bool (*detect)(struct omap_dss_device *dssdev);
+
+       /*
+        * Note: These functions might sleep. Do not call while
+        * holding a spinlock/readlock.
+        */
+       int (*audio_enable)(struct omap_dss_device *dssdev);
+       void (*audio_disable)(struct omap_dss_device *dssdev);
+       bool (*audio_supported)(struct omap_dss_device *dssdev);
+       int (*audio_config)(struct omap_dss_device *dssdev,
+               struct omap_dss_audio *audio);
+       /* Note: These functions may not sleep */
+       int (*audio_start)(struct omap_dss_device *dssdev);
+       void (*audio_stop)(struct omap_dss_device *dssdev);
+};
+
+struct omapdss_dsi_ops {
+       int (*connect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
+       void (*disconnect)(struct omap_dss_device *dssdev,
+                       struct omap_dss_device *dst);
+
+       int (*enable)(struct omap_dss_device *dssdev);
+       void (*disable)(struct omap_dss_device *dssdev, bool disconnect_lanes,
+                       bool enter_ulps);
+
+       /* bus configuration */
+       int (*set_config)(struct omap_dss_device *dssdev,
+                       const struct omap_dss_dsi_config *cfg);
+       int (*configure_pins)(struct omap_dss_device *dssdev,
+                       const struct omap_dsi_pin_config *pin_cfg);
+
+       void (*enable_hs)(struct omap_dss_device *dssdev, int channel,
+                       bool enable);
+       int (*enable_te)(struct omap_dss_device *dssdev, bool enable);
+
+       int (*update)(struct omap_dss_device *dssdev, int channel,
+                       void (*callback)(int, void *), void *data);
+
+       void (*bus_lock)(struct omap_dss_device *dssdev);
+       void (*bus_unlock)(struct omap_dss_device *dssdev);
+
+       int (*enable_video_output)(struct omap_dss_device *dssdev, int channel);
+       void (*disable_video_output)(struct omap_dss_device *dssdev,
+                       int channel);
+
+       int (*request_vc)(struct omap_dss_device *dssdev, int *channel);
+       int (*set_vc_id)(struct omap_dss_device *dssdev, int channel,
+                       int vc_id);
+       void (*release_vc)(struct omap_dss_device *dssdev, int channel);
+
+       /* data transfer */
+       int (*dcs_write)(struct omap_dss_device *dssdev, int channel,
+                       u8 *data, int len);
+       int (*dcs_write_nosync)(struct omap_dss_device *dssdev, int channel,
+                       u8 *data, int len);
+       int (*dcs_read)(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
+                       u8 *data, int len);
+
+       int (*gen_write)(struct omap_dss_device *dssdev, int channel,
+                       u8 *data, int len);
+       int (*gen_write_nosync)(struct omap_dss_device *dssdev, int channel,
+                       u8 *data, int len);
+       int (*gen_read)(struct omap_dss_device *dssdev, int channel,
+                       u8 *reqdata, int reqlen,
+                       u8 *data, int len);
+
+       int (*bta_sync)(struct omap_dss_device *dssdev, int channel);
+
+       int (*set_max_rx_packet_size)(struct omap_dss_device *dssdev,
+                       int channel, u16 plen);
 };
 
 struct omap_dss_device {
-       struct device dev;
+       /* old device, to be removed */
+       struct device old_dev;
+
+       /* new device, pointer to panel device */
+       struct device *dev;
+
+       struct module *owner;
+
+       struct list_head panel_list;
+
+       /* alias in the form of "display%d" */
+       char alias[16];
 
        enum omap_display_type type;
+       enum omap_display_type output_type;
 
        /* obsolete, to be removed */
        enum omap_channel channel;
@@ -616,9 +784,6 @@ struct omap_dss_device {
 
                struct {
                        int module;
-
-                       bool ext_te;
-                       u8 ext_te_gpio;
                } dsi;
 
                struct {
@@ -639,10 +804,6 @@ struct omap_dss_device {
                struct rfbi_timings rfbi_timings;
        } ctrl;
 
-       int reset_gpio;
-
-       int max_backlight_level;
-
        const char *name;
 
        /* used to match device to driver */
@@ -652,22 +813,40 @@ struct omap_dss_device {
 
        struct omap_dss_driver *driver;
 
+       union {
+               const struct omapdss_dpi_ops *dpi;
+               const struct omapdss_sdi_ops *sdi;
+               const struct omapdss_dvi_ops *dvi;
+               const struct omapdss_hdmi_ops *hdmi;
+               const struct omapdss_atv_ops *atv;
+               const struct omapdss_dsi_ops *dsi;
+       } ops;
+
        /* helper variable for driver suspend/resume */
        bool activate_after_resume;
 
        enum omap_display_caps caps;
 
-       struct omap_dss_output *output;
+       struct omap_dss_device *output;
 
        enum omap_dss_display_state state;
 
        enum omap_dss_audio_state audio_state;
 
-       /* platform specific  */
-       int (*platform_enable)(struct omap_dss_device *dssdev);
-       void (*platform_disable)(struct omap_dss_device *dssdev);
-       int (*set_backlight)(struct omap_dss_device *dssdev, int level);
-       int (*get_backlight)(struct omap_dss_device *dssdev);
+       /* OMAP DSS output specific fields */
+
+       struct list_head list;
+
+       /* DISPC channel for this output */
+       enum omap_channel dispc_channel;
+
+       /* output instance */
+       enum omap_dss_output_id id;
+
+       /* dynamic fields */
+       struct omap_overlay_manager *manager;
+
+       struct omap_dss_device *device;
 };
 
 struct omap_dss_hdmi_data
@@ -677,17 +856,15 @@ struct omap_dss_hdmi_data
        int hpd_gpio;
 };
 
-struct omap_dss_audio {
-       struct snd_aes_iec958 *iec;
-       struct snd_cea_861_aud_if *cea;
-};
-
 struct omap_dss_driver {
        struct device_driver driver;
 
        int (*probe)(struct omap_dss_device *);
        void (*remove)(struct omap_dss_device *);
 
+       int (*connect)(struct omap_dss_device *dssdev);
+       void (*disconnect)(struct omap_dss_device *dssdev);
+
        int (*enable)(struct omap_dss_device *display);
        void (*disable)(struct omap_dss_device *display);
        int (*run_test)(struct omap_dss_device *display, int test);
@@ -753,7 +930,10 @@ bool omapdss_is_initialized(void);
 int omap_dss_register_driver(struct omap_dss_driver *);
 void omap_dss_unregister_driver(struct omap_dss_driver *);
 
-void omap_dss_get_device(struct omap_dss_device *dssdev);
+int omapdss_register_display(struct omap_dss_device *dssdev);
+void omapdss_unregister_display(struct omap_dss_device *dssdev);
+
+struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev);
 void omap_dss_put_device(struct omap_dss_device *dssdev);
 #define for_each_dss_dev(d) while ((d = omap_dss_get_next_device(d)) != NULL)
 struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from);
@@ -761,8 +941,10 @@ struct omap_dss_device *omap_dss_find_device(void *data,
                int (*match)(struct omap_dss_device *dssdev, void *data));
 const char *omapdss_get_default_display_name(void);
 
-int omap_dss_start_device(struct omap_dss_device *dssdev);
-void omap_dss_stop_device(struct omap_dss_device *dssdev);
+void videomode_to_omap_video_timings(const struct videomode *vm,
+               struct omap_video_timings *ovt);
+void omap_video_timings_to_videomode(const struct omap_video_timings *ovt,
+               struct videomode *vm);
 
 int dss_feat_get_num_mgrs(void);
 int dss_feat_get_num_ovls(void);
@@ -778,10 +960,17 @@ struct omap_overlay_manager *omap_dss_get_overlay_manager(int num);
 int omap_dss_get_num_overlays(void);
 struct omap_overlay *omap_dss_get_overlay(int num);
 
-struct omap_dss_output *omap_dss_get_output(enum omap_dss_output_id id);
-int omapdss_output_set_device(struct omap_dss_output *out,
+int omapdss_register_output(struct omap_dss_device *output);
+void omapdss_unregister_output(struct omap_dss_device *output);
+struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id);
+struct omap_dss_device *omap_dss_find_output(const char *name);
+struct omap_dss_device *omap_dss_find_output_by_node(struct device_node *node);
+int omapdss_output_set_device(struct omap_dss_device *out,
                struct omap_dss_device *dssdev);
-int omapdss_output_unset_device(struct omap_dss_output *out);
+int omapdss_output_unset_device(struct omap_dss_device *out);
+
+struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev);
+struct omap_overlay_manager *omapdss_find_mgr_from_display(struct omap_dss_device *dssdev);
 
 void omapdss_default_get_resolution(struct omap_dss_device *dssdev,
                u16 *xres, u16 *yres);
@@ -832,7 +1021,7 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
                bool mem_to_mem);
 
 #define to_dss_driver(x) container_of((x), struct omap_dss_driver, driver)
-#define to_dss_device(x) container_of((x), struct omap_dss_device, dev)
+#define to_dss_device(x) container_of((x), struct omap_dss_device, old_dev)
 
 void omapdss_dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel,
                bool enable);
@@ -883,6 +1072,11 @@ int omapdss_compat_init(void);
 void omapdss_compat_uninit(void);
 
 struct dss_mgr_ops {
+       int (*connect)(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dst);
+       void (*disconnect)(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dst);
+
        void (*start_update)(struct omap_overlay_manager *mgr);
        int (*enable)(struct omap_overlay_manager *mgr);
        void (*disable)(struct omap_overlay_manager *mgr);
@@ -899,6 +1093,10 @@ struct dss_mgr_ops {
 int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops);
 void dss_uninstall_mgr_ops(void);
 
+int dss_mgr_connect(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dst);
+void dss_mgr_disconnect(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dst);
 void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
                const struct omap_video_timings *timings);
 void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
@@ -910,4 +1108,15 @@ int dss_mgr_register_framedone_handler(struct omap_overlay_manager *mgr,
                void (*handler)(void *), void *data);
 void dss_mgr_unregister_framedone_handler(struct omap_overlay_manager *mgr,
                void (*handler)(void *), void *data);
+
+static inline bool omapdss_device_is_connected(struct omap_dss_device *dssdev)
+{
+       return dssdev->output;
+}
+
+static inline bool omapdss_device_is_enabled(struct omap_dss_device *dssdev)
+{
+       return dssdev->state == OMAP_DSS_DISPLAY_ACTIVE;
+}
+
 #endif
index 1a91850..30f5362 100644 (file)
@@ -134,6 +134,7 @@ struct uvesafb_par {
 
        int mode_idx;
        struct vbe_crtc_ib crtc;
+       int mtrr_handle;
 };
 
 #endif /* _UVESAFB_H */
index 3ef3fe0..eb262e3 100644 (file)
  * that it cannot safely queue packets (as it may not be kicked to send them).
  */
 
+ /*
+ * "feature-split-event-channels" is introduced to separate guest TX
+ * and RX notificaion. Backend either doesn't support this feature or
+ * advertise it via xenstore as 0 (disabled) or 1 (enabled).
+ *
+ * To make use of this feature, frontend should allocate two event
+ * channels for TX and RX, advertise them to backend as
+ * "event-channel-tx" and "event-channel-rx" respectively. If frontend
+ * doesn't want to use this feature, it just writes "event-channel"
+ * node as before.
+ */
+
 /*
  * This is the 'wire' format for packets:
  *  Request 1: xen_netif_tx_request  -- XEN_NETTXF_* (any flags)
index ea1be00..54d3fa5 100644 (file)
@@ -112,10 +112,13 @@ config HAVE_KERNEL_XZ
 config HAVE_KERNEL_LZO
        bool
 
+config HAVE_KERNEL_LZ4
+       bool
+
 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
+       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
@@ -182,6 +185,18 @@ config KERNEL_LZO
          size is about 10% bigger than gzip; however its speed
          (both compression and decompression) is the fastest.
 
+config KERNEL_LZ4
+       bool "LZ4"
+       depends on HAVE_KERNEL_LZ4
+       help
+         LZ4 is an LZ77-type compressor with a fixed, byte-oriented encoding.
+         A preliminary version of LZ4 de/compression tool is available at
+         <https://code.google.com/p/lz4/>.
+
+         Its compression ratio is worse than LZO. The size of the kernel
+         is about 8% bigger than LZO. But the decompression speed is
+         faster than LZO.
+
 endchoice
 
 config DEFAULT_HOSTNAME
index e4e47f6..ae1996d 100644 (file)
@@ -823,6 +823,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
                                error = ro;
                                goto out;
                        }
+                       audit_inode_parent_hidden(name, root);
                        filp = do_create(ipc_ns, root->d_inode,
                                                &path, oflag, mode,
                                                u_attr ? &attr : NULL);
@@ -868,6 +869,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
        if (IS_ERR(name))
                return PTR_ERR(name);
 
+       audit_inode_parent_hidden(name, mnt->mnt_root);
        err = mnt_want_write(mnt);
        if (err)
                goto out_name;
index d0c6d96..bd60d7e 100644 (file)
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -141,27 +141,23 @@ void __init msg_init(void)
                                IPC_MSG_IDS, sysvipc_msg_proc_show);
 }
 
-/*
- * msg_lock_(check_) routines are called in the paths where the rw_mutex
- * is not held.
- */
-static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id)
+static inline struct msg_queue *msq_obtain_object(struct ipc_namespace *ns, int id)
 {
-       struct kern_ipc_perm *ipcp = ipc_lock(&msg_ids(ns), id);
+       struct kern_ipc_perm *ipcp = ipc_obtain_object(&msg_ids(ns), id);
 
        if (IS_ERR(ipcp))
-               return (struct msg_queue *)ipcp;
+               return ERR_CAST(ipcp);
 
        return container_of(ipcp, struct msg_queue, q_perm);
 }
 
-static inline struct msg_queue *msg_lock_check(struct ipc_namespace *ns,
-                                               int id)
+static inline struct msg_queue *msq_obtain_object_check(struct ipc_namespace *ns,
+                                                       int id)
 {
-       struct kern_ipc_perm *ipcp = ipc_lock_check(&msg_ids(ns), id);
+       struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&msg_ids(ns), id);
 
        if (IS_ERR(ipcp))
-               return (struct msg_queue *)ipcp;
+               return ERR_CAST(ipcp);
 
        return container_of(ipcp, struct msg_queue, q_perm);
 }
@@ -199,9 +195,7 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
                return retval;
        }
 
-       /*
-        * ipc_addid() locks msq
-        */
+       /* ipc_addid() locks msq upon success. */
        id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
        if (id < 0) {
                security_msg_queue_free(msq);
@@ -218,7 +212,8 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
        INIT_LIST_HEAD(&msq->q_receivers);
        INIT_LIST_HEAD(&msq->q_senders);
 
-       msg_unlock(msq);
+       ipc_unlock_object(&msq->q_perm);
+       rcu_read_unlock();
 
        return msq->q_perm.id;
 }
@@ -408,31 +403,39 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
                        return -EFAULT;
        }
 
-       ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd,
-                              &msqid64.msg_perm, msqid64.msg_qbytes);
-       if (IS_ERR(ipcp))
-               return PTR_ERR(ipcp);
+       down_write(&msg_ids(ns).rw_mutex);
+       rcu_read_lock();
+
+       ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd,
+                                     &msqid64.msg_perm, msqid64.msg_qbytes);
+       if (IS_ERR(ipcp)) {
+               err = PTR_ERR(ipcp);
+               goto out_unlock1;
+       }
 
        msq = container_of(ipcp, struct msg_queue, q_perm);
 
        err = security_msg_queue_msgctl(msq, cmd);
        if (err)
-               goto out_unlock;
+               goto out_unlock1;
 
        switch (cmd) {
        case IPC_RMID:
+               ipc_lock_object(&msq->q_perm);
+               /* freeque unlocks the ipc object and rcu */
                freeque(ns, ipcp);
                goto out_up;
        case IPC_SET:
                if (msqid64.msg_qbytes > ns->msg_ctlmnb &&
                    !capable(CAP_SYS_RESOURCE)) {
                        err = -EPERM;
-                       goto out_unlock;
+                       goto out_unlock1;
                }
 
+               ipc_lock_object(&msq->q_perm);
                err = ipc_update_perm(&msqid64.msg_perm, ipcp);
                if (err)
-                       goto out_unlock;
+                       goto out_unlock0;
 
                msq->q_qbytes = msqid64.msg_qbytes;
 
@@ -448,25 +451,23 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
                break;
        default:
                err = -EINVAL;
+               goto out_unlock1;
        }
-out_unlock:
-       msg_unlock(msq);
+
+out_unlock0:
+       ipc_unlock_object(&msq->q_perm);
+out_unlock1:
+       rcu_read_unlock();
 out_up:
        up_write(&msg_ids(ns).rw_mutex);
        return err;
 }
 
-SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
+static int msgctl_nolock(struct ipc_namespace *ns, int msqid,
+                        int cmd, int version, void __user *buf)
 {
+       int err;
        struct msg_queue *msq;
-       int err, version;
-       struct ipc_namespace *ns;
-
-       if (msqid < 0 || cmd < 0)
-               return -EINVAL;
-
-       version = ipc_parse_version(&cmd);
-       ns = current->nsproxy->ipc_ns;
 
        switch (cmd) {
        case IPC_INFO:
@@ -477,6 +478,7 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
 
                if (!buf)
                        return -EFAULT;
+
                /*
                 * We must not return kernel stack data.
                 * due to padding, it's not enough
@@ -508,7 +510,8 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
                        return -EFAULT;
                return (max_id < 0) ? 0 : max_id;
        }
-       case MSG_STAT:  /* msqid is an index rather than a msg queue id */
+
+       case MSG_STAT:
        case IPC_STAT:
        {
                struct msqid64_ds tbuf;
@@ -517,17 +520,25 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
                if (!buf)
                        return -EFAULT;
 
+               memset(&tbuf, 0, sizeof(tbuf));
+
+               rcu_read_lock();
                if (cmd == MSG_STAT) {
-                       msq = msg_lock(ns, msqid);
-                       if (IS_ERR(msq))
-                               return PTR_ERR(msq);
+                       msq = msq_obtain_object(ns, msqid);
+                       if (IS_ERR(msq)) {
+                               err = PTR_ERR(msq);
+                               goto out_unlock;
+                       }
                        success_return = msq->q_perm.id;
                } else {
-                       msq = msg_lock_check(ns, msqid);
-                       if (IS_ERR(msq))
-                               return PTR_ERR(msq);
+                       msq = msq_obtain_object_check(ns, msqid);
+                       if (IS_ERR(msq)) {
+                               err = PTR_ERR(msq);
+                               goto out_unlock;
+                       }
                        success_return = 0;
                }
+
                err = -EACCES;
                if (ipcperms(ns, &msq->q_perm, S_IRUGO))
                        goto out_unlock;
@@ -536,8 +547,6 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
                if (err)
                        goto out_unlock;
 
-               memset(&tbuf, 0, sizeof(tbuf));
-
                kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm);
                tbuf.msg_stime  = msq->q_stime;
                tbuf.msg_rtime  = msq->q_rtime;
@@ -547,24 +556,48 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
                tbuf.msg_qbytes = msq->q_qbytes;
                tbuf.msg_lspid  = msq->q_lspid;
                tbuf.msg_lrpid  = msq->q_lrpid;
-               msg_unlock(msq);
+               rcu_read_unlock();
+
                if (copy_msqid_to_user(buf, &tbuf, version))
                        return -EFAULT;
                return success_return;
        }
-       case IPC_SET:
-       case IPC_RMID:
-               err = msgctl_down(ns, msqid, cmd, buf, version);
-               return err;
+
        default:
-               return  -EINVAL;
+               return -EINVAL;
        }
 
+       return err;
 out_unlock:
-       msg_unlock(msq);
+       rcu_read_unlock();
        return err;
 }
 
+SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf)
+{
+       int version;
+       struct ipc_namespace *ns;
+
+       if (msqid < 0 || cmd < 0)
+               return -EINVAL;
+
+       version = ipc_parse_version(&cmd);
+       ns = current->nsproxy->ipc_ns;
+
+       switch (cmd) {
+       case IPC_INFO:
+       case MSG_INFO:
+       case MSG_STAT:  /* msqid is an index rather than a msg queue id */
+       case IPC_STAT:
+               return msgctl_nolock(ns, msqid, cmd, version, buf);
+       case IPC_SET:
+       case IPC_RMID:
+               return msgctl_down(ns, msqid, cmd, buf, version);
+       default:
+               return  -EINVAL;
+       }
+}
+
 static int testmsg(struct msg_msg *msg, long type, int mode)
 {
        switch(mode)
@@ -640,10 +673,11 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
        msg->m_type = mtype;
        msg->m_ts = msgsz;
 
-       msq = msg_lock_check(ns, msqid);
+       rcu_read_lock();
+       msq = msq_obtain_object_check(ns, msqid);
        if (IS_ERR(msq)) {
                err = PTR_ERR(msq);
-               goto out_free;
+               goto out_unlock1;
        }
 
        for (;;) {
@@ -651,11 +685,11 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
 
                err = -EACCES;
                if (ipcperms(ns, &msq->q_perm, S_IWUGO))
-                       goto out_unlock_free;
+                       goto out_unlock1;
 
                err = security_msg_queue_msgsnd(msq, msg, msgflg);
                if (err)
-                       goto out_unlock_free;
+                       goto out_unlock1;
 
                if (msgsz + msq->q_cbytes <= msq->q_qbytes &&
                                1 + msq->q_qnum <= msq->q_qbytes) {
@@ -665,32 +699,41 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
                /* queue full, wait: */
                if (msgflg & IPC_NOWAIT) {
                        err = -EAGAIN;
-                       goto out_unlock_free;
+                       goto out_unlock1;
                }
+
+               ipc_lock_object(&msq->q_perm);
                ss_add(msq, &s);
 
                if (!ipc_rcu_getref(msq)) {
                        err = -EIDRM;
-                       goto out_unlock_free;
+                       goto out_unlock0;
                }
 
-               msg_unlock(msq);
+               ipc_unlock_object(&msq->q_perm);
+               rcu_read_unlock();
                schedule();
 
-               ipc_lock_by_ptr(&msq->q_perm);
+               rcu_read_lock();
+               ipc_lock_object(&msq->q_perm);
+
                ipc_rcu_putref(msq);
                if (msq->q_perm.deleted) {
                        err = -EIDRM;
-                       goto out_unlock_free;
+                       goto out_unlock0;
                }
+
                ss_del(&s);
 
                if (signal_pending(current)) {
                        err = -ERESTARTNOHAND;
-                       goto out_unlock_free;
+                       goto out_unlock0;
                }
+
+               ipc_unlock_object(&msq->q_perm);
        }
 
+       ipc_lock_object(&msq->q_perm);
        msq->q_lspid = task_tgid_vnr(current);
        msq->q_stime = get_seconds();
 
@@ -706,9 +749,10 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
        err = 0;
        msg = NULL;
 
-out_unlock_free:
-       msg_unlock(msq);
-out_free:
+out_unlock0:
+       ipc_unlock_object(&msq->q_perm);
+out_unlock1:
+       rcu_read_unlock();
        if (msg != NULL)
                free_msg(msg);
        return err;
@@ -816,21 +860,19 @@ static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode)
        return ERR_PTR(-EAGAIN);
 }
 
-
-long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
-              int msgflg,
+long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg,
               long (*msg_handler)(void __user *, struct msg_msg *, size_t))
 {
-       struct msg_queue *msq;
-       struct msg_msg *msg;
        int mode;
+       struct msg_queue *msq;
        struct ipc_namespace *ns;
-       struct msg_msg *copy = NULL;
+       struct msg_msg *msg, *copy = NULL;
 
        ns = current->nsproxy->ipc_ns;
 
        if (msqid < 0 || (long) bufsz < 0)
                return -EINVAL;
+
        if (msgflg & MSG_COPY) {
                copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax));
                if (IS_ERR(copy))
@@ -838,8 +880,10 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
        }
        mode = convert_mode(&msgtyp, msgflg);
 
-       msq = msg_lock_check(ns, msqid);
+       rcu_read_lock();
+       msq = msq_obtain_object_check(ns, msqid);
        if (IS_ERR(msq)) {
+               rcu_read_unlock();
                free_copy(copy);
                return PTR_ERR(msq);
        }
@@ -849,10 +893,10 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
 
                msg = ERR_PTR(-EACCES);
                if (ipcperms(ns, &msq->q_perm, S_IRUGO))
-                       goto out_unlock;
+                       goto out_unlock1;
 
+               ipc_lock_object(&msq->q_perm);
                msg = find_msg(msq, &msgtyp, mode);
-
                if (!IS_ERR(msg)) {
                        /*
                         * Found a suitable message.
@@ -860,7 +904,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
                         */
                        if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {
                                msg = ERR_PTR(-E2BIG);
-                               goto out_unlock;
+                               goto out_unlock0;
                        }
                        /*
                         * If we are copying, then do not unlink message and do
@@ -868,8 +912,9 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
                         */
                        if (msgflg & MSG_COPY) {
                                msg = copy_msg(msg, copy);
-                               goto out_unlock;
+                               goto out_unlock0;
                        }
+
                        list_del(&msg->m_list);
                        msq->q_qnum--;
                        msq->q_rtime = get_seconds();
@@ -878,14 +923,16 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
                        atomic_sub(msg->m_ts, &ns->msg_bytes);
                        atomic_dec(&ns->msg_hdrs);
                        ss_wakeup(&msq->q_senders, 0);
-                       msg_unlock(msq);
-                       break;
+
+                       goto out_unlock0;
                }
+
                /* No message waiting. Wait for a message */
                if (msgflg & IPC_NOWAIT) {
                        msg = ERR_PTR(-ENOMSG);
-                       goto out_unlock;
+                       goto out_unlock0;
                }
+
                list_add_tail(&msr_d.r_list, &msq->q_receivers);
                msr_d.r_tsk = current;
                msr_d.r_msgtype = msgtyp;
@@ -896,8 +943,9 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
                        msr_d.r_maxsize = bufsz;
                msr_d.r_msg = ERR_PTR(-EAGAIN);
                current->state = TASK_INTERRUPTIBLE;
-               msg_unlock(msq);
 
+               ipc_unlock_object(&msq->q_perm);
+               rcu_read_unlock();
                schedule();
 
                /* Lockless receive, part 1:
@@ -908,7 +956,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
                 * Prior to destruction, expunge_all(-EIRDM) changes r_msg.
                 * Thus if r_msg is -EAGAIN, then the queue not yet destroyed.
                 * rcu_read_lock() prevents preemption between reading r_msg
-                * and the spin_lock() inside ipc_lock_by_ptr().
+                * and acquiring the q_perm.lock in ipc_lock_object().
                 */
                rcu_read_lock();
 
@@ -927,32 +975,34 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
                 * If there is a message or an error then accept it without
                 * locking.
                 */
-               if (msg != ERR_PTR(-EAGAIN)) {
-                       rcu_read_unlock();
-                       break;
-               }
+               if (msg != ERR_PTR(-EAGAIN))
+                       goto out_unlock1;
 
                /* Lockless receive, part 3:
                 * Acquire the queue spinlock.
                 */
-               ipc_lock_by_ptr(&msq->q_perm);
-               rcu_read_unlock();
+               ipc_lock_object(&msq->q_perm);
 
                /* Lockless receive, part 4:
                 * Repeat test after acquiring the spinlock.
                 */
                msg = (struct msg_msg*)msr_d.r_msg;
                if (msg != ERR_PTR(-EAGAIN))
-                       goto out_unlock;
+                       goto out_unlock0;
 
                list_del(&msr_d.r_list);
                if (signal_pending(current)) {
                        msg = ERR_PTR(-ERESTARTNOHAND);
-out_unlock:
-                       msg_unlock(msq);
-                       break;
+                       goto out_unlock0;
                }
+
+               ipc_unlock_object(&msq->q_perm);
        }
+
+out_unlock0:
+       ipc_unlock_object(&msq->q_perm);
+out_unlock1:
+       rcu_read_unlock();
        if (IS_ERR(msg)) {
                free_copy(copy);
                return PTR_ERR(msg);
index 70480a3..4108889 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -95,8 +95,12 @@ struct sem {
        int     semval;         /* current value */
        int     sempid;         /* pid of last operation */
        spinlock_t      lock;   /* spinlock for fine-grained semtimedop */
-       struct list_head sem_pending; /* pending single-sop operations */
-};
+       struct list_head pending_alter; /* pending single-sop operations */
+                                       /* that alter the semaphore */
+       struct list_head pending_const; /* pending single-sop operations */
+                                       /* that do not alter the semaphore*/
+       time_t  sem_otime;      /* candidate for sem_otime */
+} ____cacheline_aligned_in_smp;
 
 /* One queue for each sleeping process in the system. */
 struct sem_queue {
@@ -150,12 +154,15 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
 #define SEMOPM_FAST    64  /* ~ 372 bytes on stack */
 
 /*
- * linked list protection:
+ * Locking:
  *     sem_undo.id_next,
- *     sem_array.sem_pending{,last},
- *     sem_array.sem_undo: sem_lock() for read/write
+ *     sem_array.complex_count,
+ *     sem_array.pending{_alter,_cont},
+ *     sem_array.sem_undo: global sem_lock() for read/write
  *     sem_undo.proc_next: only "current" is allowed to read/write that field.
  *     
+ *     sem_array.sem_base[i].pending_{const,alter}:
+ *             global or semaphore sem_lock() for read/write
  */
 
 #define sc_semmsl      sem_ctls[0]
@@ -189,6 +196,53 @@ void __init sem_init (void)
                                IPC_SEM_IDS, sysvipc_sem_proc_show);
 }
 
+/**
+ * unmerge_queues - unmerge queues, if possible.
+ * @sma: semaphore array
+ *
+ * The function unmerges the wait queues if complex_count is 0.
+ * It must be called prior to dropping the global semaphore array lock.
+ */
+static void unmerge_queues(struct sem_array *sma)
+{
+       struct sem_queue *q, *tq;
+
+       /* complex operations still around? */
+       if (sma->complex_count)
+               return;
+       /*
+        * We will switch back to simple mode.
+        * Move all pending operation back into the per-semaphore
+        * queues.
+        */
+       list_for_each_entry_safe(q, tq, &sma->pending_alter, list) {
+               struct sem *curr;
+               curr = &sma->sem_base[q->sops[0].sem_num];
+
+               list_add_tail(&q->list, &curr->pending_alter);
+       }
+       INIT_LIST_HEAD(&sma->pending_alter);
+}
+
+/**
+ * merge_queues - Merge single semop queues into global queue
+ * @sma: semaphore array
+ *
+ * This function merges all per-semaphore queues into the global queue.
+ * It is necessary to achieve FIFO ordering for the pending single-sop
+ * operations when a multi-semop operation must sleep.
+ * Only the alter operations must be moved, the const operations can stay.
+ */
+static void merge_queues(struct sem_array *sma)
+{
+       int i;
+       for (i = 0; i < sma->sem_nsems; i++) {
+               struct sem *sem = sma->sem_base + i;
+
+               list_splice_init(&sem->pending_alter, &sma->pending_alter);
+       }
+}
+
 /*
  * If the request contains only one semaphore operation, and there are
  * no complex transactions pending, lock only the semaphore involved.
@@ -246,7 +300,7 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
                 * their critical section while the array lock is held.
                 */
  lock_array:
-               spin_lock(&sma->sem_perm.lock);
+               ipc_lock_object(&sma->sem_perm);
                for (i = 0; i < sma->sem_nsems; i++) {
                        struct sem *sem = sma->sem_base + i;
                        spin_unlock_wait(&sem->lock);
@@ -259,7 +313,8 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
 static inline void sem_unlock(struct sem_array *sma, int locknum)
 {
        if (locknum == -1) {
-               spin_unlock(&sma->sem_perm.lock);
+               unmerge_queues(sma);
+               ipc_unlock_object(&sma->sem_perm);
        } else {
                struct sem *sem = sma->sem_base + locknum;
                spin_unlock(&sem->lock);
@@ -337,7 +392,7 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
  * Without the check/retry algorithm a lockless wakeup is possible:
  * - queue.status is initialized to -EINTR before blocking.
  * - wakeup is performed by
- *     * unlinking the queue entry from sma->sem_pending
+ *     * unlinking the queue entry from the pending list
  *     * setting queue.status to IN_WAKEUP
  *       This is the notification for the blocked thread that a
  *       result value is imminent.
@@ -418,12 +473,14 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
        sma->sem_base = (struct sem *) &sma[1];
 
        for (i = 0; i < nsems; i++) {
-               INIT_LIST_HEAD(&sma->sem_base[i].sem_pending);
+               INIT_LIST_HEAD(&sma->sem_base[i].pending_alter);
+               INIT_LIST_HEAD(&sma->sem_base[i].pending_const);
                spin_lock_init(&sma->sem_base[i].lock);
        }
 
        sma->complex_count = 0;
-       INIT_LIST_HEAD(&sma->sem_pending);
+       INIT_LIST_HEAD(&sma->pending_alter);
+       INIT_LIST_HEAD(&sma->pending_const);
        INIT_LIST_HEAD(&sma->list_id);
        sma->sem_nsems = nsems;
        sma->sem_ctime = get_seconds();
@@ -482,12 +539,19 @@ SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
        return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
 }
 
-/*
- * Determine whether a sequence of semaphore operations would succeed
- * all at once. Return 0 if yes, 1 if need to sleep, else return error code.
+/** perform_atomic_semop - Perform (if possible) a semaphore operation
+ * @sma: semaphore array
+ * @sops: array with operations that should be checked
+ * @nsems: number of sops
+ * @un: undo array
+ * @pid: pid that did the change
+ *
+ * Returns 0 if the operation was possible.
+ * Returns 1 if the operation is impossible, the caller must sleep.
+ * Negative values are error codes.
  */
 
-static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops,
+static int perform_atomic_semop(struct sem_array *sma, struct sembuf *sops,
                             int nsops, struct sem_undo *un, int pid)
 {
        int result, sem_op;
@@ -609,60 +673,132 @@ static void unlink_queue(struct sem_array *sma, struct sem_queue *q)
  * update_queue is O(N^2) when it restarts scanning the whole queue of
  * waiting operations. Therefore this function checks if the restart is
  * really necessary. It is called after a previously waiting operation
- * was completed.
+ * modified the array.
+ * Note that wait-for-zero operations are handled without restart.
  */
 static int check_restart(struct sem_array *sma, struct sem_queue *q)
 {
-       struct sem *curr;
-       struct sem_queue *h;
-
-       /* if the operation didn't modify the array, then no restart */
-       if (q->alter == 0)
-               return 0;
-
-       /* pending complex operations are too difficult to analyse */
-       if (sma->complex_count)
+       /* pending complex alter operations are too difficult to analyse */
+       if (!list_empty(&sma->pending_alter))
                return 1;
 
        /* we were a sleeping complex operation. Too difficult */
        if (q->nsops > 1)
                return 1;
 
-       curr = sma->sem_base + q->sops[0].sem_num;
+       /* It is impossible that someone waits for the new value:
+        * - complex operations always restart.
+        * - wait-for-zero are handled seperately.
+        * - q is a previously sleeping simple operation that
+        *   altered the array. It must be a decrement, because
+        *   simple increments never sleep.
+        * - If there are older (higher priority) decrements
+        *   in the queue, then they have observed the original
+        *   semval value and couldn't proceed. The operation
+        *   decremented to value - thus they won't proceed either.
+        */
+       return 0;
+}
+
+/**
+ * wake_const_ops(sma, semnum, pt) - Wake up non-alter tasks
+ * @sma: semaphore array.
+ * @semnum: semaphore that was modified.
+ * @pt: list head for the tasks that must be woken up.
+ *
+ * wake_const_ops must be called after a semaphore in a semaphore array
+ * was set to 0. If complex const operations are pending, wake_const_ops must
+ * be called with semnum = -1, as well as with the number of each modified
+ * semaphore.
+ * The tasks that must be woken up are added to @pt. The return code
+ * is stored in q->pid.
+ * The function returns 1 if at least one operation was completed successfully.
+ */
+static int wake_const_ops(struct sem_array *sma, int semnum,
+                               struct list_head *pt)
+{
+       struct sem_queue *q;
+       struct list_head *walk;
+       struct list_head *pending_list;
+       int semop_completed = 0;
 
-       /* No-one waits on this queue */
-       if (list_empty(&curr->sem_pending))
-               return 0;
+       if (semnum == -1)
+               pending_list = &sma->pending_const;
+       else
+               pending_list = &sma->sem_base[semnum].pending_const;
+
+       walk = pending_list->next;
+       while (walk != pending_list) {
+               int error;
+
+               q = container_of(walk, struct sem_queue, list);
+               walk = walk->next;
+
+               error = perform_atomic_semop(sma, q->sops, q->nsops,
+                                                q->undo, q->pid);
 
-       /* the new semaphore value */
-       if (curr->semval) {
-               /* It is impossible that someone waits for the new value:
-                * - q is a previously sleeping simple operation that
-                *   altered the array. It must be a decrement, because
-                *   simple increments never sleep.
-                * - The value is not 0, thus wait-for-zero won't proceed.
-                * - If there are older (higher priority) decrements
-                *   in the queue, then they have observed the original
-                *   semval value and couldn't proceed. The operation
-                *   decremented to value - thus they won't proceed either.
+               if (error <= 0) {
+                       /* operation completed, remove from queue & wakeup */
+
+                       unlink_queue(sma, q);
+
+                       wake_up_sem_queue_prepare(pt, q, error);
+                       if (error == 0)
+                               semop_completed = 1;
+               }
+       }
+       return semop_completed;
+}
+
+/**
+ * do_smart_wakeup_zero(sma, sops, nsops, pt) - wakeup all wait for zero tasks
+ * @sma: semaphore array
+ * @sops: operations that were performed
+ * @nsops: number of operations
+ * @pt: list head of the tasks that must be woken up.
+ *
+ * do_smart_wakeup_zero() checks all required queue for wait-for-zero
+ * operations, based on the actual changes that were performed on the
+ * semaphore array.
+ * The function returns 1 if at least one operation was completed successfully.
+ */
+static int do_smart_wakeup_zero(struct sem_array *sma, struct sembuf *sops,
+                                       int nsops, struct list_head *pt)
+{
+       int i;
+       int semop_completed = 0;
+       int got_zero = 0;
+
+       /* first: the per-semaphore queues, if known */
+       if (sops) {
+               for (i = 0; i < nsops; i++) {
+                       int num = sops[i].sem_num;
+
+                       if (sma->sem_base[num].semval == 0) {
+                               got_zero = 1;
+                               semop_completed |= wake_const_ops(sma, num, pt);
+                       }
+               }
+       } else {
+               /*
+                * No sops means modified semaphores not known.
+                * Assume all were changed.
                 */
-               BUG_ON(q->sops[0].sem_op >= 0);
-               return 0;
+               for (i = 0; i < sma->sem_nsems; i++) {
+                       if (sma->sem_base[i].semval == 0) {
+                               got_zero = 1;
+                               semop_completed |= wake_const_ops(sma, i, pt);
+                       }
+               }
        }
        /*
-        * semval is 0. Check if there are wait-for-zero semops.
-        * They must be the first entries in the per-semaphore queue
+        * If one of the modified semaphores got 0,
+        * then check the global queue, too.
         */
-       h = list_first_entry(&curr->sem_pending, struct sem_queue, list);
-       BUG_ON(h->nsops != 1);
-       BUG_ON(h->sops[0].sem_num != q->sops[0].sem_num);
-
-       /* Yes, there is a wait-for-zero semop. Restart */
-       if (h->sops[0].sem_op == 0)
-               return 1;
+       if (got_zero)
+               semop_completed |= wake_const_ops(sma, -1, pt);
 
-       /* Again - no-one is waiting for the new value. */
-       return 0;
+       return semop_completed;
 }
 
 
@@ -678,6 +814,8 @@ static int check_restart(struct sem_array *sma, struct sem_queue *q)
  * semaphore.
  * The tasks that must be woken up are added to @pt. The return code
  * is stored in q->pid.
+ * The function internally checks if const operations can now succeed.
+ *
  * The function return 1 if at least one semop was completed successfully.
  */
 static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt)
@@ -688,9 +826,9 @@ static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt)
        int semop_completed = 0;
 
        if (semnum == -1)
-               pending_list = &sma->sem_pending;
+               pending_list = &sma->pending_alter;
        else
-               pending_list = &sma->sem_base[semnum].sem_pending;
+               pending_list = &sma->sem_base[semnum].pending_alter;
 
 again:
        walk = pending_list->next;
@@ -702,16 +840,15 @@ again:
 
                /* If we are scanning the single sop, per-semaphore list of
                 * one semaphore and that semaphore is 0, then it is not
-                * necessary to scan the "alter" entries: simple increments
+                * necessary to scan further: simple increments
                 * that affect only one entry succeed immediately and cannot
                 * be in the  per semaphore pending queue, and decrements
                 * cannot be successful if the value is already 0.
                 */
-               if (semnum != -1 && sma->sem_base[semnum].semval == 0 &&
-                               q->alter)
+               if (semnum != -1 && sma->sem_base[semnum].semval == 0)
                        break;
 
-               error = try_atomic_semop(sma, q->sops, q->nsops,
+               error = perform_atomic_semop(sma, q->sops, q->nsops,
                                         q->undo, q->pid);
 
                /* Does q->sleeper still need to sleep? */
@@ -724,6 +861,7 @@ again:
                        restart = 0;
                } else {
                        semop_completed = 1;
+                       do_smart_wakeup_zero(sma, q->sops, q->nsops, pt);
                        restart = check_restart(sma, q);
                }
 
@@ -742,8 +880,8 @@ again:
  * @otime: force setting otime
  * @pt: list head of the tasks that must be woken up.
  *
- * do_smart_update() does the required called to update_queue, based on the
- * actual changes that were performed on the semaphore array.
+ * do_smart_update() does the required calls to update_queue and wakeup_zero,
+ * based on the actual changes that were performed on the semaphore array.
  * Note that the function does not do the actual wake-up: the caller is
  * responsible for calling wake_up_sem_queue_do(@pt).
  * It is safe to perform this call after dropping all locks.
@@ -752,49 +890,46 @@ static void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsop
                        int otime, struct list_head *pt)
 {
        int i;
-       int progress;
-
-       progress = 1;
-retry_global:
-       if (sma->complex_count) {
-               if (update_queue(sma, -1, pt)) {
-                       progress = 1;
-                       otime = 1;
-                       sops = NULL;
-               }
-       }
-       if (!progress)
-               goto done;
 
-       if (!sops) {
-               /* No semops; something special is going on. */
-               for (i = 0; i < sma->sem_nsems; i++) {
-                       if (update_queue(sma, i, pt)) {
-                               otime = 1;
-                               progress = 1;
-                       }
-               }
-               goto done_checkretry;
-       }
+       otime |= do_smart_wakeup_zero(sma, sops, nsops, pt);
 
-       /* Check the semaphores that were modified. */
-       for (i = 0; i < nsops; i++) {
-               if (sops[i].sem_op > 0 ||
-                       (sops[i].sem_op < 0 &&
-                               sma->sem_base[sops[i].sem_num].semval == 0))
-                       if (update_queue(sma, sops[i].sem_num, pt)) {
-                               otime = 1;
-                               progress = 1;
+       if (!list_empty(&sma->pending_alter)) {
+               /* semaphore array uses the global queue - just process it. */
+               otime |= update_queue(sma, -1, pt);
+       } else {
+               if (!sops) {
+                       /*
+                        * No sops, thus the modified semaphores are not
+                        * known. Check all.
+                        */
+                       for (i = 0; i < sma->sem_nsems; i++)
+                               otime |= update_queue(sma, i, pt);
+               } else {
+                       /*
+                        * Check the semaphores that were increased:
+                        * - No complex ops, thus all sleeping ops are
+                        *   decrease.
+                        * - if we decreased the value, then any sleeping
+                        *   semaphore ops wont be able to run: If the
+                        *   previous value was too small, then the new
+                        *   value will be too small, too.
+                        */
+                       for (i = 0; i < nsops; i++) {
+                               if (sops[i].sem_op > 0) {
+                                       otime |= update_queue(sma,
+                                                       sops[i].sem_num, pt);
+                               }
                        }
+               }
        }
-done_checkretry:
-       if (progress) {
-               progress = 0;
-               goto retry_global;
+       if (otime) {
+               if (sops == NULL) {
+                       sma->sem_base[0].sem_otime = get_seconds();
+               } else {
+                       sma->sem_base[sops[0].sem_num].sem_otime =
+                                                               get_seconds();
+               }
        }
-done:
-       if (otime)
-               sma->sem_otime = get_seconds();
 }
 
 
@@ -813,14 +948,14 @@ static int count_semncnt (struct sem_array * sma, ushort semnum)
        struct sem_queue * q;
 
        semncnt = 0;
-       list_for_each_entry(q, &sma->sem_base[semnum].sem_pending, list) {
+       list_for_each_entry(q, &sma->sem_base[semnum].pending_alter, list) {
                struct sembuf * sops = q->sops;
                BUG_ON(sops->sem_num != semnum);
                if ((sops->sem_op < 0) && !(sops->sem_flg & IPC_NOWAIT))
                        semncnt++;
        }
 
-       list_for_each_entry(q, &sma->sem_pending, list) {
+       list_for_each_entry(q, &sma->pending_alter, list) {
                struct sembuf * sops = q->sops;
                int nsops = q->nsops;
                int i;
@@ -839,14 +974,14 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum)
        struct sem_queue * q;
 
        semzcnt = 0;
-       list_for_each_entry(q, &sma->sem_base[semnum].sem_pending, list) {
+       list_for_each_entry(q, &sma->sem_base[semnum].pending_const, list) {
                struct sembuf * sops = q->sops;
                BUG_ON(sops->sem_num != semnum);
                if ((sops->sem_op == 0) && !(sops->sem_flg & IPC_NOWAIT))
                        semzcnt++;
        }
 
-       list_for_each_entry(q, &sma->sem_pending, list) {
+       list_for_each_entry(q, &sma->pending_const, list) {
                struct sembuf * sops = q->sops;
                int nsops = q->nsops;
                int i;
@@ -872,7 +1007,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
        int i;
 
        /* Free the existing undo structures for this semaphore set.  */
-       assert_spin_locked(&sma->sem_perm.lock);
+       ipc_assert_locked_object(&sma->sem_perm);
        list_for_each_entry_safe(un, tu, &sma->list_id, list_id) {
                list_del(&un->list_id);
                spin_lock(&un->ulp->lock);
@@ -884,13 +1019,22 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
 
        /* Wake up all pending processes and let them fail with EIDRM. */
        INIT_LIST_HEAD(&tasks);
-       list_for_each_entry_safe(q, tq, &sma->sem_pending, list) {
+       list_for_each_entry_safe(q, tq, &sma->pending_const, list) {
+               unlink_queue(sma, q);
+               wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
+       }
+
+       list_for_each_entry_safe(q, tq, &sma->pending_alter, list) {
                unlink_queue(sma, q);
                wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
        }
        for (i = 0; i < sma->sem_nsems; i++) {
                struct sem *sem = sma->sem_base + i;
-               list_for_each_entry_safe(q, tq, &sem->sem_pending, list) {
+               list_for_each_entry_safe(q, tq, &sem->pending_const, list) {
+                       unlink_queue(sma, q);
+                       wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
+               }
+               list_for_each_entry_safe(q, tq, &sem->pending_alter, list) {
                        unlink_queue(sma, q);
                        wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
                }
@@ -931,6 +1075,21 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in,
        }
 }
 
+static time_t get_semotime(struct sem_array *sma)
+{
+       int i;
+       time_t res;
+
+       res = sma->sem_base[0].sem_otime;
+       for (i = 1; i < sma->sem_nsems; i++) {
+               time_t to = sma->sem_base[i].sem_otime;
+
+               if (to > res)
+                       res = to;
+       }
+       return res;
+}
+
 static int semctl_nolock(struct ipc_namespace *ns, int semid,
                         int cmd, int version, void __user *p)
 {
@@ -1004,9 +1163,9 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
                        goto out_unlock;
 
                kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
-               tbuf.sem_otime  = sma->sem_otime;
-               tbuf.sem_ctime  = sma->sem_ctime;
-               tbuf.sem_nsems  = sma->sem_nsems;
+               tbuf.sem_otime = get_semotime(sma);
+               tbuf.sem_ctime = sma->sem_ctime;
+               tbuf.sem_nsems = sma->sem_nsems;
                rcu_read_unlock();
                if (copy_semid_to_user(p, &tbuf, version))
                        return -EFAULT;
@@ -1070,7 +1229,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
 
        curr = &sma->sem_base[semnum];
 
-       assert_spin_locked(&sma->sem_perm.lock);
+       ipc_assert_locked_object(&sma->sem_perm);
        list_for_each_entry(un, &sma->list_id, list_id)
                un->semadj[semnum] = 0;
 
@@ -1199,7 +1358,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
                for (i = 0; i < nsems; i++)
                        sma->sem_base[i].semval = sem_io[i];
 
-               assert_spin_locked(&sma->sem_perm.lock);
+               ipc_assert_locked_object(&sma->sem_perm);
                list_for_each_entry(un, &sma->list_id, list_id) {
                        for (i = 0; i < nsems; i++)
                                un->semadj[i] = 0;
@@ -1289,39 +1448,43 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
                        return -EFAULT;
        }
 
+       down_write(&sem_ids(ns).rw_mutex);
+       rcu_read_lock();
+
        ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd,
                                      &semid64.sem_perm, 0);
-       if (IS_ERR(ipcp))
-               return PTR_ERR(ipcp);
+       if (IS_ERR(ipcp)) {
+               err = PTR_ERR(ipcp);
+               goto out_unlock1;
+       }
 
        sma = container_of(ipcp, struct sem_array, sem_perm);
 
        err = security_sem_semctl(sma, cmd);
-       if (err) {
-               rcu_read_unlock();
-               goto out_up;
-       }
+       if (err)
+               goto out_unlock1;
 
-       switch(cmd){
+       switch (cmd) {
        case IPC_RMID:
                sem_lock(sma, NULL, -1);
+               /* freeary unlocks the ipc object and rcu */
                freeary(ns, ipcp);
                goto out_up;
        case IPC_SET:
                sem_lock(sma, NULL, -1);
                err = ipc_update_perm(&semid64.sem_perm, ipcp);
                if (err)
-                       goto out_unlock;
+                       goto out_unlock0;
                sma->sem_ctime = get_seconds();
                break;
        default:
-               rcu_read_unlock();
                err = -EINVAL;
-               goto out_up;
+               goto out_unlock1;
        }
 
-out_unlock:
+out_unlock0:
        sem_unlock(sma, -1);
+out_unlock1:
        rcu_read_unlock();
 out_up:
        up_write(&sem_ids(ns).rw_mutex);
@@ -1496,7 +1659,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
        new->semid = semid;
        assert_spin_locked(&ulp->lock);
        list_add_rcu(&new->list_proc, &ulp->list_proc);
-       assert_spin_locked(&sma->sem_perm.lock);
+       ipc_assert_locked_object(&sma->sem_perm);
        list_add(&new->list_id, &sma->list_id);
        un = new;
 
@@ -1533,7 +1696,6 @@ static int get_queue_result(struct sem_queue *q)
        return error;
 }
 
-
 SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
                unsigned, nsops, const struct timespec __user *, timeout)
 {
@@ -1631,7 +1793,8 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
        if (un && un->semid == -1)
                goto out_unlock_free;
 
-       error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current));
+       error = perform_atomic_semop(sma, sops, nsops, un,
+                                       task_tgid_vnr(current));
        if (error <= 0) {
                if (alter && error == 0)
                        do_smart_update(sma, sops, nsops, 1, &tasks);
@@ -1653,15 +1816,27 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
                struct sem *curr;
                curr = &sma->sem_base[sops->sem_num];
 
-               if (alter)
-                       list_add_tail(&queue.list, &curr->sem_pending);
-               else
-                       list_add(&queue.list, &curr->sem_pending);
+               if (alter) {
+                       if (sma->complex_count) {
+                               list_add_tail(&queue.list,
+                                               &sma->pending_alter);
+                       } else {
+
+                               list_add_tail(&queue.list,
+                                               &curr->pending_alter);
+                       }
+               } else {
+                       list_add_tail(&queue.list, &curr->pending_const);
+               }
        } else {
+               if (!sma->complex_count)
+                       merge_queues(sma);
+
                if (alter)
-                       list_add_tail(&queue.list, &sma->sem_pending);
+                       list_add_tail(&queue.list, &sma->pending_alter);
                else
-                       list_add(&queue.list, &sma->sem_pending);
+                       list_add_tail(&queue.list, &sma->pending_const);
+
                sma->complex_count++;
        }
 
@@ -1833,7 +2008,7 @@ void exit_sem(struct task_struct *tsk)
                }
 
                /* remove un from the linked lists */
-               assert_spin_locked(&sma->sem_perm.lock);
+               ipc_assert_locked_object(&sma->sem_perm);
                list_del(&un->list_id);
 
                spin_lock(&ulp->lock);
@@ -1882,6 +2057,9 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
 {
        struct user_namespace *user_ns = seq_user_ns(s);
        struct sem_array *sma = it;
+       time_t sem_otime;
+
+       sem_otime = get_semotime(sma);
 
        return seq_printf(s,
                          "%10d %10d  %4o %10u %5u %5u %5u %5u %10lu %10lu\n",
@@ -1893,7 +2071,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
                          from_kgid_munged(user_ns, sma->sem_perm.gid),
                          from_kuid_munged(user_ns, sma->sem_perm.cuid),
                          from_kgid_munged(user_ns, sma->sem_perm.cgid),
-                         sma->sem_otime,
+                         sem_otime,
                          sma->sem_ctime);
 }
 #endif
index 7e199fa..c6b4ad5 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -141,7 +141,7 @@ static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id)
 static inline void shm_lock_by_ptr(struct shmid_kernel *ipcp)
 {
        rcu_read_lock();
-       spin_lock(&ipcp->shm_perm.lock);
+       ipc_lock_object(&ipcp->shm_perm);
 }
 
 static inline struct shmid_kernel *shm_lock_check(struct ipc_namespace *ns,
@@ -491,10 +491,10 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
        sprintf (name, "SYSV%08x", key);
        if (shmflg & SHM_HUGETLB) {
-               struct hstate *hs = hstate_sizelog((shmflg >> SHM_HUGE_SHIFT)
-                                               & SHM_HUGE_MASK);
+               struct hstate *hs;
                size_t hugesize;
 
+               hs = hstate_sizelog((shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
                if (!hs) {
                        error = -EINVAL;
                        goto no_file;
@@ -535,6 +535,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
        shp->shm_nattch = 0;
        shp->shm_file = file;
        shp->shm_creator = current;
+
        /*
         * shmid gets reported as "inode#" in /proc/pid/maps.
         * proc-ps tools use this. Changing this will break them.
@@ -543,7 +544,9 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
        ns->shm_tot += numpages;
        error = shp->shm_perm.id;
-       shm_unlock(shp);
+
+       ipc_unlock_object(&shp->shm_perm);
+       rcu_read_unlock();
        return error;
 
 no_id:
@@ -754,31 +757,42 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
                        return -EFAULT;
        }
 
+       down_write(&shm_ids(ns).rw_mutex);
+       rcu_read_lock();
+
        ipcp = ipcctl_pre_down(ns, &shm_ids(ns), shmid, cmd,
                               &shmid64.shm_perm, 0);
-       if (IS_ERR(ipcp))
-               return PTR_ERR(ipcp);
+       if (IS_ERR(ipcp)) {
+               err = PTR_ERR(ipcp);
+               /* the ipc lock is not held upon failure */
+               goto out_unlock1;
+       }
 
        shp = container_of(ipcp, struct shmid_kernel, shm_perm);
 
        err = security_shm_shmctl(shp, cmd);
        if (err)
-               goto out_unlock;
+               goto out_unlock0;
+
        switch (cmd) {
        case IPC_RMID:
+               /* do_shm_rmid unlocks the ipc object and rcu */
                do_shm_rmid(ns, ipcp);
                goto out_up;
        case IPC_SET:
                err = ipc_update_perm(&shmid64.shm_perm, ipcp);
                if (err)
-                       goto out_unlock;
+                       goto out_unlock0;
                shp->shm_ctim = get_seconds();
                break;
        default:
                err = -EINVAL;
        }
-out_unlock:
-       shm_unlock(shp);
+
+out_unlock0:
+       ipc_unlock_object(&shp->shm_perm);
+out_unlock1:
+       rcu_read_unlock();
 out_up:
        up_write(&shm_ids(ns).rw_mutex);
        return err;
index 809ec5e..4704223 100644 (file)
@@ -246,9 +246,8 @@ int ipc_get_maxid(struct ipc_ids *ids)
  *     is returned. The 'new' entry is returned in a locked state on success.
  *     On failure the entry is not locked and a negative err-code is returned.
  *
- *     Called with ipc_ids.rw_mutex held as a writer.
+ *     Called with writer ipc_ids.rw_mutex held.
  */
 int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
 {
        kuid_t euid;
@@ -469,9 +468,7 @@ void ipc_free(void* ptr, int size)
 struct ipc_rcu {
        struct rcu_head rcu;
        atomic_t refcount;
-       /* "void *" makes sure alignment of following data is sane. */
-       void *data[0];
-};
+} ____cacheline_aligned_in_smp;
 
 /**
  *     ipc_rcu_alloc   -       allocate ipc and rcu space 
@@ -489,12 +486,14 @@ void *ipc_rcu_alloc(int size)
        if (unlikely(!out))
                return NULL;
        atomic_set(&out->refcount, 1);
-       return out->data;
+       return out + 1;
 }
 
 int ipc_rcu_getref(void *ptr)
 {
-       return atomic_inc_not_zero(&container_of(ptr, struct ipc_rcu, data)->refcount);
+       struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
+
+       return atomic_inc_not_zero(&p->refcount);
 }
 
 /**
@@ -508,7 +507,7 @@ static void ipc_schedule_free(struct rcu_head *head)
 
 void ipc_rcu_putref(void *ptr)
 {
-       struct ipc_rcu *p = container_of(ptr, struct ipc_rcu, data);
+       struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
 
        if (!atomic_dec_and_test(&p->refcount))
                return;
@@ -747,8 +746,10 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)
  * It must be called without any lock held and
  *  - retrieves the ipc with the given id in the given table.
  *  - performs some audit and permission check, depending on the given cmd
- *  - returns the ipc with both ipc and rw_mutex locks held in case of success
+ *  - returns the ipc with the ipc lock held in case of success
  *    or an err-code without any lock held otherwise.
+ *
+ * Call holding the both the rw_mutex and the rcu read lock.
  */
 struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
                                      struct ipc_ids *ids, int id, int cmd,
@@ -773,13 +774,10 @@ struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
        int err = -EPERM;
        struct kern_ipc_perm *ipcp;
 
-       down_write(&ids->rw_mutex);
-       rcu_read_lock();
-
        ipcp = ipc_obtain_object_check(ids, id);
        if (IS_ERR(ipcp)) {
                err = PTR_ERR(ipcp);
-               goto out_up;
+               goto err;
        }
 
        audit_ipc_obj(ipcp);
@@ -790,16 +788,8 @@ struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
        euid = current_euid();
        if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid)  ||
            ns_capable(ns->user_ns, CAP_SYS_ADMIN))
-               return ipcp;
-
-out_up:
-       /*
-        * Unsuccessful lookup, unlock and return
-        * the corresponding error.
-        */
-       rcu_read_unlock();
-       up_write(&ids->rw_mutex);
-
+               return ipcp; /* successful lookup */
+err:
        return ERR_PTR(err);
 }
 
index 2b0bdd5..b6a6a88 100644 (file)
@@ -159,21 +159,31 @@ static inline int ipc_checkid(struct kern_ipc_perm *ipcp, int uid)
        return uid / SEQ_MULTIPLIER != ipcp->seq;
 }
 
-static inline void ipc_lock_by_ptr(struct kern_ipc_perm *perm)
+static inline void ipc_lock_object(struct kern_ipc_perm *perm)
 {
-       rcu_read_lock();
        spin_lock(&perm->lock);
 }
 
-static inline void ipc_unlock(struct kern_ipc_perm *perm)
+static inline void ipc_unlock_object(struct kern_ipc_perm *perm)
 {
        spin_unlock(&perm->lock);
-       rcu_read_unlock();
 }
 
-static inline void ipc_lock_object(struct kern_ipc_perm *perm)
+static inline void ipc_assert_locked_object(struct kern_ipc_perm *perm)
 {
-       spin_lock(&perm->lock);
+       assert_spin_locked(&perm->lock);
+}
+
+static inline void ipc_lock_by_ptr(struct kern_ipc_perm *perm)
+{
+       rcu_read_lock();
+       ipc_lock_object(perm);
+}
+
+static inline void ipc_unlock(struct kern_ipc_perm *perm)
+{
+       ipc_unlock_object(perm);
+       rcu_read_unlock();
 }
 
 struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id);
index 271fd31..470839d 100644 (file)
@@ -9,7 +9,7 @@ obj-y     = fork.o exec_domain.o panic.o printk.o \
            rcupdate.o extable.o params.o posix-timers.o \
            kthread.o wait.o sys_ni.o posix-cpu-timers.o mutex.o \
            hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
-           notifier.o ksysfs.o cred.o \
+           notifier.o ksysfs.o cred.o reboot.o \
            async.o range.o groups.o lglock.o smpboot.o
 
 ifdef CONFIG_FUNCTION_TRACER
index 1c95131..123c9b7 100644 (file)
@@ -85,6 +85,7 @@ struct audit_names {
 
        struct filename         *name;
        int                     name_len;       /* number of chars to log */
+       bool                    hidden;         /* don't log this record */
        bool                    name_put;       /* call __putname()? */
 
        unsigned long           ino;
index 6bd4a90..f7aee8b 100644 (file)
@@ -423,7 +423,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 == 4294967295)) {
+               if ((f->type == AUDIT_LOGINUID) && (f->val == ~0U)) {
                        f->type = AUDIT_LOGINUID_SET;
                        f->val = 0;
                }
@@ -865,6 +865,12 @@ static inline int audit_add_rule(struct audit_entry *entry)
                err = audit_add_watch(&entry->rule, &list);
                if (err) {
                        mutex_unlock(&audit_filter_mutex);
+                       /*
+                        * normally audit_add_tree_rule() will free it
+                        * on failure
+                        */
+                       if (tree)
+                               audit_put_tree(tree);
                        goto error;
                }
        }
index 3c8a601..9845cb3 100644 (file)
@@ -1399,8 +1399,11 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
        }
 
        i = 0;
-       list_for_each_entry(n, &context->names_list, list)
+       list_for_each_entry(n, &context->names_list, list) {
+               if (n->hidden)
+                       continue;
                audit_log_name(context, n, NULL, i++, &call_panic);
+       }
 
        /* Send end of event record to help user space know we are finished */
        ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE);
@@ -1769,14 +1772,15 @@ void audit_putname(struct filename *name)
  * __audit_inode - store the inode and device from a lookup
  * @name: name being audited
  * @dentry: dentry being audited
- * @parent: does this dentry represent the parent?
+ * @flags: attributes for this particular entry
  */
 void __audit_inode(struct filename *name, const struct dentry *dentry,
-                  unsigned int parent)
+                  unsigned int flags)
 {
        struct audit_context *context = current->audit_context;
        const struct inode *inode = dentry->d_inode;
        struct audit_names *n;
+       bool parent = flags & AUDIT_INODE_PARENT;
 
        if (!context->in_syscall)
                return;
@@ -1831,6 +1835,8 @@ out:
        if (parent) {
                n->name_len = n->name ? parent_len(n->name->name) : AUDIT_NAME_FULL;
                n->type = AUDIT_TYPE_PARENT;
+               if (flags & AUDIT_INODE_HIDDEN)
+                       n->hidden = true;
        } else {
                n->name_len = AUDIT_NAME_FULL;
                n->type = AUDIT_TYPE_NORMAL;
index fafe75d..a949819 100644 (file)
@@ -808,7 +808,7 @@ void do_exit(long code)
        /*
         * FIXME: do that only when needed, using sched_exit tracepoint
         */
-       ptrace_put_breakpoints(tsk);
+       flush_ptrace_hw_breakpoint(tsk);
 
        exit_notify(tsk, group_dead);
 #ifdef CONFIG_NUMA
index 167ec09..9771231 100644 (file)
@@ -399,8 +399,9 @@ struct slowpath_args {
 static void warn_slowpath_common(const char *file, int line, void *caller,
                                 unsigned taint, struct slowpath_args *args)
 {
-       printk(KERN_WARNING "------------[ cut here ]------------\n");
-       printk(KERN_WARNING "WARNING: at %s:%d %pS()\n", file, line, caller);
+       pr_warn("------------[ cut here ]------------\n");
+       pr_warn("WARNING: CPU: %d PID: %d at %s:%d %pS()\n",
+               raw_smp_processor_id(), current->pid, file, line, caller);
 
        if (args)
                vprintk(args->fmt, args->args);
index ba5e6ce..4041f57 100644 (file)
@@ -469,6 +469,7 @@ static int ptrace_detach(struct task_struct *child, unsigned int data)
        /* Architecture-specific hardware disable .. */
        ptrace_disable(child);
        clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+       flush_ptrace_hw_breakpoint(child);
 
        write_lock_irq(&tasklist_lock);
        /*
@@ -1221,19 +1222,3 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
        return ret;
 }
 #endif /* CONFIG_COMPAT */
-
-#ifdef CONFIG_HAVE_HW_BREAKPOINT
-int ptrace_get_breakpoints(struct task_struct *tsk)
-{
-       if (atomic_inc_not_zero(&tsk->ptrace_bp_refcnt))
-               return 0;
-
-       return -1;
-}
-
-void ptrace_put_breakpoints(struct task_struct *tsk)
-{
-       if (atomic_dec_and_test(&tsk->ptrace_bp_refcnt))
-               flush_ptrace_hw_breakpoint(tsk);
-}
-#endif /* CONFIG_HAVE_HW_BREAKPOINT */
diff --git a/kernel/reboot.c b/kernel/reboot.c
new file mode 100644 (file)
index 0000000..269ed93
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ *  linux/kernel/reboot.c
+ *
+ *  Copyright (C) 2013  Linus Torvalds
+ */
+
+#define pr_fmt(fmt)    "reboot: " fmt
+
+#include <linux/ctype.h>
+#include <linux/export.h>
+#include <linux/kexec.h>
+#include <linux/kmod.h>
+#include <linux/kmsg_dump.h>
+#include <linux/reboot.h>
+#include <linux/suspend.h>
+#include <linux/syscalls.h>
+#include <linux/syscore_ops.h>
+#include <linux/uaccess.h>
+
+/*
+ * this indicates whether you can reboot with ctrl-alt-del: the default is yes
+ */
+
+int C_A_D = 1;
+struct pid *cad_pid;
+EXPORT_SYMBOL(cad_pid);
+
+#if defined(CONFIG_ARM) || defined(CONFIG_UNICORE32)
+#define DEFAULT_REBOOT_MODE            = REBOOT_HARD
+#else
+#define DEFAULT_REBOOT_MODE
+#endif
+enum reboot_mode reboot_mode DEFAULT_REBOOT_MODE;
+
+int reboot_default;
+int reboot_cpu;
+enum reboot_type reboot_type = BOOT_ACPI;
+int reboot_force;
+
+/*
+ * If set, this is used for preparing the system to power off.
+ */
+
+void (*pm_power_off_prepare)(void);
+
+/**
+ *     emergency_restart - reboot the system
+ *
+ *     Without shutting down any hardware or taking any locks
+ *     reboot the system.  This is called when we know we are in
+ *     trouble so this is our best effort to reboot.  This is
+ *     safe to call in interrupt context.
+ */
+void emergency_restart(void)
+{
+       kmsg_dump(KMSG_DUMP_EMERG);
+       machine_emergency_restart();
+}
+EXPORT_SYMBOL_GPL(emergency_restart);
+
+void kernel_restart_prepare(char *cmd)
+{
+       blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
+       system_state = SYSTEM_RESTART;
+       usermodehelper_disable();
+       device_shutdown();
+}
+
+/**
+ *     register_reboot_notifier - Register function to be called at reboot time
+ *     @nb: Info about notifier function to be called
+ *
+ *     Registers a function with the list of functions
+ *     to be called at reboot time.
+ *
+ *     Currently always returns zero, as blocking_notifier_chain_register()
+ *     always returns zero.
+ */
+int register_reboot_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&reboot_notifier_list, nb);
+}
+EXPORT_SYMBOL(register_reboot_notifier);
+
+/**
+ *     unregister_reboot_notifier - Unregister previously registered reboot notifier
+ *     @nb: Hook to be unregistered
+ *
+ *     Unregisters a previously registered reboot
+ *     notifier function.
+ *
+ *     Returns zero on success, or %-ENOENT on failure.
+ */
+int unregister_reboot_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&reboot_notifier_list, nb);
+}
+EXPORT_SYMBOL(unregister_reboot_notifier);
+
+static void migrate_to_reboot_cpu(void)
+{
+       /* The boot cpu is always logical cpu 0 */
+       int cpu = reboot_cpu;
+
+       cpu_hotplug_disable();
+
+       /* Make certain the cpu I'm about to reboot on is online */
+       if (!cpu_online(cpu))
+               cpu = cpumask_first(cpu_online_mask);
+
+       /* Prevent races with other tasks migrating this task */
+       current->flags |= PF_NO_SETAFFINITY;
+
+       /* Make certain I only run on the appropriate processor */
+       set_cpus_allowed_ptr(current, cpumask_of(cpu));
+}
+
+/**
+ *     kernel_restart - reboot the system
+ *     @cmd: pointer to buffer containing command to execute for restart
+ *             or %NULL
+ *
+ *     Shutdown everything and perform a clean reboot.
+ *     This is not safe to call in interrupt context.
+ */
+void kernel_restart(char *cmd)
+{
+       kernel_restart_prepare(cmd);
+       migrate_to_reboot_cpu();
+       syscore_shutdown();
+       if (!cmd)
+               pr_emerg("Restarting system\n");
+       else
+               pr_emerg("Restarting system with command '%s'\n", cmd);
+       kmsg_dump(KMSG_DUMP_RESTART);
+       machine_restart(cmd);
+}
+EXPORT_SYMBOL_GPL(kernel_restart);
+
+static void kernel_shutdown_prepare(enum system_states state)
+{
+       blocking_notifier_call_chain(&reboot_notifier_list,
+               (state == SYSTEM_HALT) ? SYS_HALT : SYS_POWER_OFF, NULL);
+       system_state = state;
+       usermodehelper_disable();
+       device_shutdown();
+}
+/**
+ *     kernel_halt - halt the system
+ *
+ *     Shutdown everything and perform a clean system halt.
+ */
+void kernel_halt(void)
+{
+       kernel_shutdown_prepare(SYSTEM_HALT);
+       migrate_to_reboot_cpu();
+       syscore_shutdown();
+       pr_emerg("System halted\n");
+       kmsg_dump(KMSG_DUMP_HALT);
+       machine_halt();
+}
+EXPORT_SYMBOL_GPL(kernel_halt);
+
+/**
+ *     kernel_power_off - power_off the system
+ *
+ *     Shutdown everything and perform a clean system power_off.
+ */
+void kernel_power_off(void)
+{
+       kernel_shutdown_prepare(SYSTEM_POWER_OFF);
+       if (pm_power_off_prepare)
+               pm_power_off_prepare();
+       migrate_to_reboot_cpu();
+       syscore_shutdown();
+       pr_emerg("Power down\n");
+       kmsg_dump(KMSG_DUMP_POWEROFF);
+       machine_power_off();
+}
+EXPORT_SYMBOL_GPL(kernel_power_off);
+
+static DEFINE_MUTEX(reboot_mutex);
+
+/*
+ * Reboot system call: for obvious reasons only root may call it,
+ * and even root needs to set up some magic numbers in the registers
+ * so that some mistake won't make this reboot the whole machine.
+ * You can also set the meaning of the ctrl-alt-del-key here.
+ *
+ * reboot doesn't sync: do that yourself before calling this.
+ */
+SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
+               void __user *, arg)
+{
+       struct pid_namespace *pid_ns = task_active_pid_ns(current);
+       char buffer[256];
+       int ret = 0;
+
+       /* We only trust the superuser with rebooting the system. */
+       if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
+               return -EPERM;
+
+       /* For safety, we require "magic" arguments. */
+       if (magic1 != LINUX_REBOOT_MAGIC1 ||
+                       (magic2 != LINUX_REBOOT_MAGIC2 &&
+                       magic2 != LINUX_REBOOT_MAGIC2A &&
+                       magic2 != LINUX_REBOOT_MAGIC2B &&
+                       magic2 != LINUX_REBOOT_MAGIC2C))
+               return -EINVAL;
+
+       /*
+        * If pid namespaces are enabled and the current task is in a child
+        * pid_namespace, the command is handled by reboot_pid_ns() which will
+        * call do_exit().
+        */
+       ret = reboot_pid_ns(pid_ns, cmd);
+       if (ret)
+               return ret;
+
+       /* Instead of trying to make the power_off code look like
+        * halt when pm_power_off is not set do it the easy way.
+        */
+       if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
+               cmd = LINUX_REBOOT_CMD_HALT;
+
+       mutex_lock(&reboot_mutex);
+       switch (cmd) {
+       case LINUX_REBOOT_CMD_RESTART:
+               kernel_restart(NULL);
+               break;
+
+       case LINUX_REBOOT_CMD_CAD_ON:
+               C_A_D = 1;
+               break;
+
+       case LINUX_REBOOT_CMD_CAD_OFF:
+               C_A_D = 0;
+               break;
+
+       case LINUX_REBOOT_CMD_HALT:
+               kernel_halt();
+               do_exit(0);
+               panic("cannot halt");
+
+       case LINUX_REBOOT_CMD_POWER_OFF:
+               kernel_power_off();
+               do_exit(0);
+               break;
+
+       case LINUX_REBOOT_CMD_RESTART2:
+               ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
+               if (ret < 0) {
+                       ret = -EFAULT;
+                       break;
+               }
+               buffer[sizeof(buffer) - 1] = '\0';
+
+               kernel_restart(buffer);
+               break;
+
+#ifdef CONFIG_KEXEC
+       case LINUX_REBOOT_CMD_KEXEC:
+               ret = kernel_kexec();
+               break;
+#endif
+
+#ifdef CONFIG_HIBERNATION
+       case LINUX_REBOOT_CMD_SW_SUSPEND:
+               ret = hibernate();
+               break;
+#endif
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       mutex_unlock(&reboot_mutex);
+       return ret;
+}
+
+static void deferred_cad(struct work_struct *dummy)
+{
+       kernel_restart(NULL);
+}
+
+/*
+ * This function gets called by ctrl-alt-del - ie the keyboard interrupt.
+ * As it's called within an interrupt, it may NOT sync: the only choice
+ * is whether to reboot at once, or just ignore the ctrl-alt-del.
+ */
+void ctrl_alt_del(void)
+{
+       static DECLARE_WORK(cad_work, deferred_cad);
+
+       if (C_A_D)
+               schedule_work(&cad_work);
+       else
+               kill_cad_pid(SIGINT, 1);
+}
+
+char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
+
+static int __orderly_poweroff(bool force)
+{
+       char **argv;
+       static char *envp[] = {
+               "HOME=/",
+               "PATH=/sbin:/bin:/usr/sbin:/usr/bin",
+               NULL
+       };
+       int ret;
+
+       argv = argv_split(GFP_KERNEL, poweroff_cmd, NULL);
+       if (argv) {
+               ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
+               argv_free(argv);
+       } else {
+               ret = -ENOMEM;
+       }
+
+       if (ret && force) {
+               pr_warn("Failed to start orderly shutdown: forcing the issue\n");
+               /*
+                * I guess this should try to kick off some daemon to sync and
+                * poweroff asap.  Or not even bother syncing if we're doing an
+                * emergency shutdown?
+                */
+               emergency_sync();
+               kernel_power_off();
+       }
+
+       return ret;
+}
+
+static bool poweroff_force;
+
+static void poweroff_work_func(struct work_struct *work)
+{
+       __orderly_poweroff(poweroff_force);
+}
+
+static DECLARE_WORK(poweroff_work, poweroff_work_func);
+
+/**
+ * orderly_poweroff - Trigger an orderly system poweroff
+ * @force: force poweroff if command execution fails
+ *
+ * This may be called from any context to trigger a system shutdown.
+ * If the orderly shutdown fails, it will force an immediate shutdown.
+ */
+int orderly_poweroff(bool force)
+{
+       if (force) /* do not override the pending "true" */
+               poweroff_force = true;
+       schedule_work(&poweroff_work);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(orderly_poweroff);
+
+static int __init reboot_setup(char *str)
+{
+       for (;;) {
+               /*
+                * Having anything passed on the command line via
+                * reboot= will cause us to disable DMI checking
+                * below.
+                */
+               reboot_default = 0;
+
+               switch (*str) {
+               case 'w':
+                       reboot_mode = REBOOT_WARM;
+                       break;
+
+               case 'c':
+                       reboot_mode = REBOOT_COLD;
+                       break;
+
+               case 'h':
+                       reboot_mode = REBOOT_HARD;
+                       break;
+
+               case 's':
+                       if (isdigit(*(str+1)))
+                               reboot_cpu = simple_strtoul(str+1, NULL, 0);
+                       else if (str[1] == 'm' && str[2] == 'p' &&
+                                                       isdigit(*(str+3)))
+                               reboot_cpu = simple_strtoul(str+3, NULL, 0);
+                       else
+                               reboot_mode = REBOOT_SOFT;
+                       break;
+
+               case 'g':
+                       reboot_mode = REBOOT_GPIO;
+                       break;
+
+               case 'b':
+               case 'a':
+               case 'k':
+               case 't':
+               case 'e':
+               case 'p':
+                       reboot_type = *str;
+                       break;
+
+               case 'f':
+                       reboot_force = 1;
+                       break;
+               }
+
+               str = strchr(str, ',');
+               if (str)
+                       str++;
+               else
+                       break;
+       }
+       return 1;
+}
+__setup("reboot=", reboot_setup);
index 071de90..771129b 100644 (file)
@@ -115,20 +115,6 @@ int fs_overflowgid = DEFAULT_FS_OVERFLOWUID;
 EXPORT_SYMBOL(fs_overflowuid);
 EXPORT_SYMBOL(fs_overflowgid);
 
-/*
- * this indicates whether you can reboot with ctrl-alt-del: the default is yes
- */
-
-int C_A_D = 1;
-struct pid *cad_pid;
-EXPORT_SYMBOL(cad_pid);
-
-/*
- * If set, this is used for preparing the system to power off.
- */
-
-void (*pm_power_off_prepare)(void);
-
 /*
  * Returns true if current's euid is same as p's uid or euid,
  * or has CAP_SYS_NICE to p's user_ns.
@@ -308,266 +294,6 @@ out_unlock:
        return retval;
 }
 
-/**
- *     emergency_restart - reboot the system
- *
- *     Without shutting down any hardware or taking any locks
- *     reboot the system.  This is called when we know we are in
- *     trouble so this is our best effort to reboot.  This is
- *     safe to call in interrupt context.
- */
-void emergency_restart(void)
-{
-       kmsg_dump(KMSG_DUMP_EMERG);
-       machine_emergency_restart();
-}
-EXPORT_SYMBOL_GPL(emergency_restart);
-
-void kernel_restart_prepare(char *cmd)
-{
-       blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
-       system_state = SYSTEM_RESTART;
-       usermodehelper_disable();
-       device_shutdown();
-}
-
-/**
- *     register_reboot_notifier - Register function to be called at reboot time
- *     @nb: Info about notifier function to be called
- *
- *     Registers a function with the list of functions
- *     to be called at reboot time.
- *
- *     Currently always returns zero, as blocking_notifier_chain_register()
- *     always returns zero.
- */
-int register_reboot_notifier(struct notifier_block *nb)
-{
-       return blocking_notifier_chain_register(&reboot_notifier_list, nb);
-}
-EXPORT_SYMBOL(register_reboot_notifier);
-
-/**
- *     unregister_reboot_notifier - Unregister previously registered reboot notifier
- *     @nb: Hook to be unregistered
- *
- *     Unregisters a previously registered reboot
- *     notifier function.
- *
- *     Returns zero on success, or %-ENOENT on failure.
- */
-int unregister_reboot_notifier(struct notifier_block *nb)
-{
-       return blocking_notifier_chain_unregister(&reboot_notifier_list, nb);
-}
-EXPORT_SYMBOL(unregister_reboot_notifier);
-
-/* Add backwards compatibility for stable trees. */
-#ifndef PF_NO_SETAFFINITY
-#define PF_NO_SETAFFINITY              PF_THREAD_BOUND
-#endif
-
-static void migrate_to_reboot_cpu(void)
-{
-       /* The boot cpu is always logical cpu 0 */
-       int cpu = 0;
-
-       cpu_hotplug_disable();
-
-       /* Make certain the cpu I'm about to reboot on is online */
-       if (!cpu_online(cpu))
-               cpu = cpumask_first(cpu_online_mask);
-
-       /* Prevent races with other tasks migrating this task */
-       current->flags |= PF_NO_SETAFFINITY;
-
-       /* Make certain I only run on the appropriate processor */
-       set_cpus_allowed_ptr(current, cpumask_of(cpu));
-}
-
-/**
- *     kernel_restart - reboot the system
- *     @cmd: pointer to buffer containing command to execute for restart
- *             or %NULL
- *
- *     Shutdown everything and perform a clean reboot.
- *     This is not safe to call in interrupt context.
- */
-void kernel_restart(char *cmd)
-{
-       kernel_restart_prepare(cmd);
-       migrate_to_reboot_cpu();
-       syscore_shutdown();
-       if (!cmd)
-               printk(KERN_EMERG "Restarting system.\n");
-       else
-               printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);
-       kmsg_dump(KMSG_DUMP_RESTART);
-       machine_restart(cmd);
-}
-EXPORT_SYMBOL_GPL(kernel_restart);
-
-static void kernel_shutdown_prepare(enum system_states state)
-{
-       blocking_notifier_call_chain(&reboot_notifier_list,
-               (state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL);
-       system_state = state;
-       usermodehelper_disable();
-       device_shutdown();
-}
-/**
- *     kernel_halt - halt the system
- *
- *     Shutdown everything and perform a clean system halt.
- */
-void kernel_halt(void)
-{
-       kernel_shutdown_prepare(SYSTEM_HALT);
-       migrate_to_reboot_cpu();
-       syscore_shutdown();
-       printk(KERN_EMERG "System halted.\n");
-       kmsg_dump(KMSG_DUMP_HALT);
-       machine_halt();
-}
-
-EXPORT_SYMBOL_GPL(kernel_halt);
-
-/**
- *     kernel_power_off - power_off the system
- *
- *     Shutdown everything and perform a clean system power_off.
- */
-void kernel_power_off(void)
-{
-       kernel_shutdown_prepare(SYSTEM_POWER_OFF);
-       if (pm_power_off_prepare)
-               pm_power_off_prepare();
-       migrate_to_reboot_cpu();
-       syscore_shutdown();
-       printk(KERN_EMERG "Power down.\n");
-       kmsg_dump(KMSG_DUMP_POWEROFF);
-       machine_power_off();
-}
-EXPORT_SYMBOL_GPL(kernel_power_off);
-
-static DEFINE_MUTEX(reboot_mutex);
-
-/*
- * Reboot system call: for obvious reasons only root may call it,
- * and even root needs to set up some magic numbers in the registers
- * so that some mistake won't make this reboot the whole machine.
- * You can also set the meaning of the ctrl-alt-del-key here.
- *
- * reboot doesn't sync: do that yourself before calling this.
- */
-SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
-               void __user *, arg)
-{
-       struct pid_namespace *pid_ns = task_active_pid_ns(current);
-       char buffer[256];
-       int ret = 0;
-
-       /* We only trust the superuser with rebooting the system. */
-       if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
-               return -EPERM;
-
-       /* For safety, we require "magic" arguments. */
-       if (magic1 != LINUX_REBOOT_MAGIC1 ||
-           (magic2 != LINUX_REBOOT_MAGIC2 &&
-                       magic2 != LINUX_REBOOT_MAGIC2A &&
-                       magic2 != LINUX_REBOOT_MAGIC2B &&
-                       magic2 != LINUX_REBOOT_MAGIC2C))
-               return -EINVAL;
-
-       /*
-        * If pid namespaces are enabled and the current task is in a child
-        * pid_namespace, the command is handled by reboot_pid_ns() which will
-        * call do_exit().
-        */
-       ret = reboot_pid_ns(pid_ns, cmd);
-       if (ret)
-               return ret;
-
-       /* Instead of trying to make the power_off code look like
-        * halt when pm_power_off is not set do it the easy way.
-        */
-       if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
-               cmd = LINUX_REBOOT_CMD_HALT;
-
-       mutex_lock(&reboot_mutex);
-       switch (cmd) {
-       case LINUX_REBOOT_CMD_RESTART:
-               kernel_restart(NULL);
-               break;
-
-       case LINUX_REBOOT_CMD_CAD_ON:
-               C_A_D = 1;
-               break;
-
-       case LINUX_REBOOT_CMD_CAD_OFF:
-               C_A_D = 0;
-               break;
-
-       case LINUX_REBOOT_CMD_HALT:
-               kernel_halt();
-               do_exit(0);
-               panic("cannot halt.\n");
-
-       case LINUX_REBOOT_CMD_POWER_OFF:
-               kernel_power_off();
-               do_exit(0);
-               break;
-
-       case LINUX_REBOOT_CMD_RESTART2:
-               if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
-                       ret = -EFAULT;
-                       break;
-               }
-               buffer[sizeof(buffer) - 1] = '\0';
-
-               kernel_restart(buffer);
-               break;
-
-#ifdef CONFIG_KEXEC
-       case LINUX_REBOOT_CMD_KEXEC:
-               ret = kernel_kexec();
-               break;
-#endif
-
-#ifdef CONFIG_HIBERNATION
-       case LINUX_REBOOT_CMD_SW_SUSPEND:
-               ret = hibernate();
-               break;
-#endif
-
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       mutex_unlock(&reboot_mutex);
-       return ret;
-}
-
-static void deferred_cad(struct work_struct *dummy)
-{
-       kernel_restart(NULL);
-}
-
-/*
- * This function gets called by ctrl-alt-del - ie the keyboard interrupt.
- * As it's called within an interrupt, it may NOT sync: the only choice
- * is whether to reboot at once, or just ignore the ctrl-alt-del.
- */
-void ctrl_alt_del(void)
-{
-       static DECLARE_WORK(cad_work, deferred_cad);
-
-       if (C_A_D)
-               schedule_work(&cad_work);
-       else
-               kill_cad_pid(SIGINT, 1);
-}
-       
 /*
  * Unprivileged users may change the real gid to the effective gid
  * or vice versa.  (BSD-style)
@@ -2292,68 +2018,6 @@ SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
        return err ? -EFAULT : 0;
 }
 
-char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
-
-static int __orderly_poweroff(bool force)
-{
-       char **argv;
-       static char *envp[] = {
-               "HOME=/",
-               "PATH=/sbin:/bin:/usr/sbin:/usr/bin",
-               NULL
-       };
-       int ret;
-
-       argv = argv_split(GFP_KERNEL, poweroff_cmd, NULL);
-       if (argv) {
-               ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
-               argv_free(argv);
-       } else {
-               printk(KERN_WARNING "%s failed to allocate memory for \"%s\"\n",
-                                        __func__, poweroff_cmd);
-               ret = -ENOMEM;
-       }
-
-       if (ret && force) {
-               printk(KERN_WARNING "Failed to start orderly shutdown: "
-                                       "forcing the issue\n");
-               /*
-                * I guess this should try to kick off some daemon to sync and
-                * poweroff asap.  Or not even bother syncing if we're doing an
-                * emergency shutdown?
-                */
-               emergency_sync();
-               kernel_power_off();
-       }
-
-       return ret;
-}
-
-static bool poweroff_force;
-
-static void poweroff_work_func(struct work_struct *work)
-{
-       __orderly_poweroff(poweroff_force);
-}
-
-static DECLARE_WORK(poweroff_work, poweroff_work_func);
-
-/**
- * orderly_poweroff - Trigger an orderly system poweroff
- * @force: force poweroff if command execution fails
- *
- * This may be called from any context to trigger a system shutdown.
- * If the orderly shutdown fails, it will force an immediate shutdown.
- */
-int orderly_poweroff(bool force)
-{
-       if (force) /* do not override the pending "true" */
-               poweroff_force = true;
-       schedule_work(&poweroff_work);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(orderly_poweroff);
-
 /**
  * do_sysinfo - fill in sysinfo struct
  * @info: pointer to buffer to fill
index aea4a9e..b609213 100644 (file)
@@ -3,7 +3,6 @@
 #include "../fs/xfs/xfs_sysctl.h"
 #include <linux/sunrpc/debug.h>
 #include <linux/string.h>
-#include <net/ip_vs.h>
 #include <linux/syscalls.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
index f1ed53c..35da513 100644 (file)
@@ -194,6 +194,15 @@ config LZO_COMPRESS
 config LZO_DECOMPRESS
        tristate
 
+config LZ4_COMPRESS
+       tristate
+
+config LZ4HC_COMPRESS
+       tristate
+
+config LZ4_DECOMPRESS
+       tristate
+
 source "lib/xz/Kconfig"
 
 #
@@ -218,6 +227,10 @@ config DECOMPRESS_LZO
        select LZO_DECOMPRESS
        tristate
 
+config DECOMPRESS_LZ4
+       select LZ4_DECOMPRESS
+       tristate
+
 #
 # Generic allocator support is selected if needed
 #
index c09e38e..7baccfd 100644 (file)
@@ -23,7 +23,7 @@ lib-y += kobject.o klist.o
 
 obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
         bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
-        gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.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
 obj-y += string_helpers.o
 obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
@@ -75,6 +75,9 @@ obj-$(CONFIG_REED_SOLOMON) += reed_solomon/
 obj-$(CONFIG_BCH) += bch.o
 obj-$(CONFIG_LZO_COMPRESS) += lzo/
 obj-$(CONFIG_LZO_DECOMPRESS) += lzo/
+obj-$(CONFIG_LZ4_COMPRESS) += lz4/
+obj-$(CONFIG_LZ4HC_COMPRESS) += lz4/
+obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/
 obj-$(CONFIG_XZ_DEC) += xz/
 obj-$(CONFIG_RAID6_PQ) += raid6/
 
@@ -83,6 +86,7 @@ lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o
 lib-$(CONFIG_DECOMPRESS_LZMA) += decompress_unlzma.o
 lib-$(CONFIG_DECOMPRESS_XZ) += decompress_unxz.o
 lib-$(CONFIG_DECOMPRESS_LZO) += decompress_unlzo.o
+lib-$(CONFIG_DECOMPRESS_LZ4) += decompress_unlz4.o
 
 obj-$(CONFIG_TEXTSEARCH) += textsearch.o
 obj-$(CONFIG_TEXTSEARCH_KMP) += ts_kmp.o
diff --git a/lib/clz_ctz.c b/lib/clz_ctz.c
new file mode 100644 (file)
index 0000000..a8f8379
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * lib/clz_ctz.c
+ *
+ * Copyright (C) 2013 Chanho Min <chanho.min@lge.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.
+ *
+ * __c[lt]z[sd]i2 can be overridden by linking arch-specific versions.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+
+int __weak __ctzsi2(int val)
+{
+       return __ffs(val);
+}
+EXPORT_SYMBOL(__ctzsi2);
+
+int __weak __clzsi2(int val)
+{
+       return 32 - fls(val);
+}
+EXPORT_SYMBOL(__clzsi2);
+
+#if BITS_PER_LONG == 32
+
+int __weak __clzdi2(long val)
+{
+       return 32 - fls((int)val);
+}
+EXPORT_SYMBOL(__clzdi2);
+
+int __weak __ctzdi2(long val)
+{
+       return __ffs((u32)val);
+}
+EXPORT_SYMBOL(__ctzdi2);
+
+#elif BITS_PER_LONG == 64
+
+int __weak __clzdi2(long val)
+{
+       return 64 - fls64((u64)val);
+}
+EXPORT_SYMBOL(__clzdi2);
+
+int __weak __ctzdi2(long val)
+{
+       return __ffs64((u64)val);
+}
+EXPORT_SYMBOL(__ctzdi2);
+
+#else
+#error BITS_PER_LONG not 32 or 64
+#endif
index f8fdeda..4d1cd03 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/decompress/unxz.h>
 #include <linux/decompress/inflate.h>
 #include <linux/decompress/unlzo.h>
+#include <linux/decompress/unlz4.h>
 
 #include <linux/types.h>
 #include <linux/string.h>
@@ -31,6 +32,9 @@
 #ifndef CONFIG_DECOMPRESS_LZO
 # define unlzo NULL
 #endif
+#ifndef CONFIG_DECOMPRESS_LZ4
+# define unlz4 NULL
+#endif
 
 struct compress_format {
        unsigned char magic[2];
@@ -45,6 +49,7 @@ static const struct compress_format compressed_formats[] __initconst = {
        { {0x5d, 0x00}, "lzma", unlzma },
        { {0xfd, 0x37}, "xz", unxz },
        { {0x89, 0x4c}, "lzo", unlzo },
+       { {0x02, 0x21}, "lz4", unlz4 },
        { {0, 0}, NULL, NULL }
 };
 
diff --git a/lib/decompress_unlz4.c b/lib/decompress_unlz4.c
new file mode 100644 (file)
index 0000000..3e67cfa
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd
+ *
+ * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.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.
+ */
+
+#ifdef STATIC
+#define PREBOOT
+#include "lz4/lz4_decompress.c"
+#else
+#include <linux/decompress/unlz4.h>
+#endif
+#include <linux/types.h>
+#include <linux/lz4.h>
+#include <linux/decompress/mm.h>
+#include <linux/compiler.h>
+
+#include <asm/unaligned.h>
+
+/*
+ * Note: Uncompressed chunk size is used in the compressor side
+ * (userspace side for compression).
+ * It is hardcoded because there is not proper way to extract it
+ * from the binary stream which is generated by the preliminary
+ * version of LZ4 tool so far.
+ */
+#define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20)
+#define ARCHIVE_MAGICNUMBER 0x184C2102
+
+STATIC inline int INIT unlz4(u8 *input, int in_len,
+                               int (*fill) (void *, unsigned int),
+                               int (*flush) (void *, unsigned int),
+                               u8 *output, int *posp,
+                               void (*error) (char *x))
+{
+       int ret = -1;
+       size_t chunksize = 0;
+       size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE;
+       u8 *inp;
+       u8 *inp_start;
+       u8 *outp;
+       int size = in_len;
+#ifdef PREBOOT
+       size_t out_len = get_unaligned_le32(input + in_len);
+#endif
+       size_t dest_len;
+
+
+       if (output) {
+               outp = output;
+       } else if (!flush) {
+               error("NULL output pointer and no flush function provided");
+               goto exit_0;
+       } else {
+               outp = large_malloc(uncomp_chunksize);
+               if (!outp) {
+                       error("Could not allocate output buffer");
+                       goto exit_0;
+               }
+       }
+
+       if (input && fill) {
+               error("Both input pointer and fill function provided,");
+               goto exit_1;
+       } else if (input) {
+               inp = input;
+       } else if (!fill) {
+               error("NULL input pointer and missing fill function");
+               goto exit_1;
+       } else {
+               inp = large_malloc(lz4_compressbound(uncomp_chunksize));
+               if (!inp) {
+                       error("Could not allocate input buffer");
+                       goto exit_1;
+               }
+       }
+       inp_start = inp;
+
+       if (posp)
+               *posp = 0;
+
+       if (fill)
+               fill(inp, 4);
+
+       chunksize = get_unaligned_le32(inp);
+       if (chunksize == ARCHIVE_MAGICNUMBER) {
+               inp += 4;
+               size -= 4;
+       } else {
+               error("invalid header");
+               goto exit_2;
+       }
+
+       if (posp)
+               *posp += 4;
+
+       for (;;) {
+
+               if (fill)
+                       fill(inp, 4);
+
+               chunksize = get_unaligned_le32(inp);
+               if (chunksize == ARCHIVE_MAGICNUMBER) {
+                       inp += 4;
+                       size -= 4;
+                       if (posp)
+                               *posp += 4;
+                       continue;
+               }
+               inp += 4;
+               size -= 4;
+
+               if (posp)
+                       *posp += 4;
+
+               if (fill) {
+                       if (chunksize > lz4_compressbound(uncomp_chunksize)) {
+                               error("chunk length is longer than allocated");
+                               goto exit_2;
+                       }
+                       fill(inp, chunksize);
+               }
+#ifdef PREBOOT
+               if (out_len >= uncomp_chunksize) {
+                       dest_len = uncomp_chunksize;
+                       out_len -= dest_len;
+               } else
+                       dest_len = out_len;
+               ret = lz4_decompress(inp, &chunksize, outp, dest_len);
+#else
+               dest_len = uncomp_chunksize;
+               ret = lz4_decompress_unknownoutputsize(inp, chunksize, outp,
+                               &dest_len);
+#endif
+               if (ret < 0) {
+                       error("Decoding failed");
+                       goto exit_2;
+               }
+
+               if (flush && flush(outp, dest_len) != dest_len)
+                       goto exit_2;
+               if (output)
+                       outp += dest_len;
+               if (posp)
+                       *posp += chunksize;
+
+               size -= chunksize;
+
+               if (size == 0)
+                       break;
+               else if (size < 0) {
+                       error("data corrupted");
+                       goto exit_2;
+               }
+
+               inp += chunksize;
+               if (fill)
+                       inp = inp_start;
+       }
+
+       ret = 0;
+exit_2:
+       if (!input)
+               large_free(inp_start);
+exit_1:
+       if (!output)
+               large_free(outp);
+exit_0:
+       return ret;
+}
+
+#ifdef PREBOOT
+STATIC int INIT decompress(unsigned char *buf, int in_len,
+                             int(*fill)(void*, unsigned int),
+                             int(*flush)(void*, unsigned int),
+                             unsigned char *output,
+                             int *posp,
+                             void(*error)(char *x)
+       )
+{
+       return unlz4(buf, in_len - 4, fill, flush, output, posp, error);
+}
+#endif
diff --git a/lib/lz4/Makefile b/lib/lz4/Makefile
new file mode 100644 (file)
index 0000000..8085d04
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_LZ4_COMPRESS) += lz4_compress.o
+obj-$(CONFIG_LZ4HC_COMPRESS) += lz4hc_compress.o
+obj-$(CONFIG_LZ4_DECOMPRESS) += lz4_decompress.o
diff --git a/lib/lz4/lz4_compress.c b/lib/lz4/lz4_compress.c
new file mode 100644 (file)
index 0000000..fd94058
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * LZ4 - Fast LZ compression algorithm
+ * Copyright (C) 2011-2012, Yann Collet.
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ * 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.
+ *
+ * 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.
+ *
+ * You can contact the author at :
+ * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
+ * - LZ4 source repository : http://code.google.com/p/lz4/
+ *
+ *  Changed for kernel use by:
+ *  Chanho Min <chanho.min@lge.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/lz4.h>
+#include <asm/unaligned.h>
+#include "lz4defs.h"
+
+/*
+ * LZ4_compressCtx :
+ * -----------------
+ * Compress 'isize' bytes from 'source' into an output buffer 'dest' of
+ * maximum size 'maxOutputSize'.  * If it cannot achieve it, compression
+ * will stop, and result of the function will be zero.
+ * return : the number of bytes written in buffer 'dest', or 0 if the
+ * compression fails
+ */
+static inline int lz4_compressctx(void *ctx,
+               const char *source,
+               char *dest,
+               int isize,
+               int maxoutputsize)
+{
+       HTYPE *hashtable = (HTYPE *)ctx;
+       const u8 *ip = (u8 *)source;
+#if LZ4_ARCH64
+       const BYTE * const base = ip;
+#else
+       const int base = 0;
+#endif
+       const u8 *anchor = ip;
+       const u8 *const iend = ip + isize;
+       const u8 *const mflimit = iend - MFLIMIT;
+       #define MATCHLIMIT (iend - LASTLITERALS)
+
+       u8 *op = (u8 *) dest;
+       u8 *const oend = op + maxoutputsize;
+       int length;
+       const int skipstrength = SKIPSTRENGTH;
+       u32 forwardh;
+       int lastrun;
+
+       /* Init */
+       if (isize < MINLENGTH)
+               goto _last_literals;
+
+       memset((void *)hashtable, 0, LZ4_MEM_COMPRESS);
+
+       /* First Byte */
+       hashtable[LZ4_HASH_VALUE(ip)] = ip - base;
+       ip++;
+       forwardh = LZ4_HASH_VALUE(ip);
+
+       /* Main Loop */
+       for (;;) {
+               int findmatchattempts = (1U << skipstrength) + 3;
+               const u8 *forwardip = ip;
+               const u8 *ref;
+               u8 *token;
+
+               /* Find a match */
+               do {
+                       u32 h = forwardh;
+                       int step = findmatchattempts++ >> skipstrength;
+                       ip = forwardip;
+                       forwardip = ip + step;
+
+                       if (unlikely(forwardip > mflimit))
+                               goto _last_literals;
+
+                       forwardh = LZ4_HASH_VALUE(forwardip);
+                       ref = base + hashtable[h];
+                       hashtable[h] = ip - base;
+               } while ((ref < ip - MAX_DISTANCE) || (A32(ref) != A32(ip)));
+
+               /* Catch up */
+               while ((ip > anchor) && (ref > (u8 *)source) &&
+                       unlikely(ip[-1] == ref[-1])) {
+                       ip--;
+                       ref--;
+               }
+
+               /* Encode Literal length */
+               length = (int)(ip - anchor);
+               token = op++;
+               /* check output limit */
+               if (unlikely(op + length + (2 + 1 + LASTLITERALS) +
+                       (length >> 8) > oend))
+                       return 0;
+
+               if (length >= (int)RUN_MASK) {
+                       int len;
+                       *token = (RUN_MASK << ML_BITS);
+                       len = length - RUN_MASK;
+                       for (; len > 254 ; len -= 255)
+                               *op++ = 255;
+                       *op++ = (u8)len;
+               } else
+                       *token = (length << ML_BITS);
+
+               /* Copy Literals */
+               LZ4_BLINDCOPY(anchor, op, length);
+_next_match:
+               /* Encode Offset */
+               LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref));
+
+               /* Start Counting */
+               ip += MINMATCH;
+               /* MinMatch verified */
+               ref += MINMATCH;
+               anchor = ip;
+               while (likely(ip < MATCHLIMIT - (STEPSIZE - 1))) {
+                       #if LZ4_ARCH64
+                       u64 diff = A64(ref) ^ A64(ip);
+                       #else
+                       u32 diff = A32(ref) ^ A32(ip);
+                       #endif
+                       if (!diff) {
+                               ip += STEPSIZE;
+                               ref += STEPSIZE;
+                               continue;
+                       }
+                       ip += LZ4_NBCOMMONBYTES(diff);
+                       goto _endcount;
+               }
+               #if LZ4_ARCH64
+               if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) {
+                       ip += 4;
+                       ref += 4;
+               }
+               #endif
+               if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) {
+                       ip += 2;
+                       ref += 2;
+               }
+               if ((ip < MATCHLIMIT) && (*ref == *ip))
+                       ip++;
+_endcount:
+               /* Encode MatchLength */
+               length = (int)(ip - anchor);
+               /* Check output limit */
+               if (unlikely(op + (1 + LASTLITERALS) + (length >> 8) > oend))
+                       return 0;
+               if (length >= (int)ML_MASK) {
+                       *token += ML_MASK;
+                       length -= ML_MASK;
+                       for (; length > 509 ; length -= 510) {
+                               *op++ = 255;
+                               *op++ = 255;
+                       }
+                       if (length > 254) {
+                               length -= 255;
+                               *op++ = 255;
+                       }
+                       *op++ = (u8)length;
+               } else
+                       *token += length;
+
+               /* Test end of chunk */
+               if (ip > mflimit) {
+                       anchor = ip;
+                       break;
+               }
+
+               /* Fill table */
+               hashtable[LZ4_HASH_VALUE(ip-2)] = ip - 2 - base;
+
+               /* Test next position */
+               ref = base + hashtable[LZ4_HASH_VALUE(ip)];
+               hashtable[LZ4_HASH_VALUE(ip)] = ip - base;
+               if ((ref > ip - (MAX_DISTANCE + 1)) && (A32(ref) == A32(ip))) {
+                       token = op++;
+                       *token = 0;
+                       goto _next_match;
+               }
+
+               /* Prepare next loop */
+               anchor = ip++;
+               forwardh = LZ4_HASH_VALUE(ip);
+       }
+
+_last_literals:
+       /* Encode Last Literals */
+       lastrun = (int)(iend - anchor);
+       if (((char *)op - dest) + lastrun + 1
+               + ((lastrun + 255 - RUN_MASK) / 255) > (u32)maxoutputsize)
+               return 0;
+
+       if (lastrun >= (int)RUN_MASK) {
+               *op++ = (RUN_MASK << ML_BITS);
+               lastrun -= RUN_MASK;
+               for (; lastrun > 254 ; lastrun -= 255)
+                       *op++ = 255;
+               *op++ = (u8)lastrun;
+       } else
+               *op++ = (lastrun << ML_BITS);
+       memcpy(op, anchor, iend - anchor);
+       op += iend - anchor;
+
+       /* End */
+       return (int)(((char *)op) - dest);
+}
+
+static inline int lz4_compress64kctx(void *ctx,
+               const char *source,
+               char *dest,
+               int isize,
+               int maxoutputsize)
+{
+       u16 *hashtable = (u16 *)ctx;
+       const u8 *ip = (u8 *) source;
+       const u8 *anchor = ip;
+       const u8 *const base = ip;
+       const u8 *const iend = ip + isize;
+       const u8 *const mflimit = iend - MFLIMIT;
+       #define MATCHLIMIT (iend - LASTLITERALS)
+
+       u8 *op = (u8 *) dest;
+       u8 *const oend = op + maxoutputsize;
+       int len, length;
+       const int skipstrength = SKIPSTRENGTH;
+       u32 forwardh;
+       int lastrun;
+
+       /* Init */
+       if (isize < MINLENGTH)
+               goto _last_literals;
+
+       memset((void *)hashtable, 0, LZ4_MEM_COMPRESS);
+
+       /* First Byte */
+       ip++;
+       forwardh = LZ4_HASH64K_VALUE(ip);
+
+       /* Main Loop */
+       for (;;) {
+               int findmatchattempts = (1U << skipstrength) + 3;
+               const u8 *forwardip = ip;
+               const u8 *ref;
+               u8 *token;
+
+               /* Find a match */
+               do {
+                       u32 h = forwardh;
+                       int step = findmatchattempts++ >> skipstrength;
+                       ip = forwardip;
+                       forwardip = ip + step;
+
+                       if (forwardip > mflimit)
+                               goto _last_literals;
+
+                       forwardh = LZ4_HASH64K_VALUE(forwardip);
+                       ref = base + hashtable[h];
+                       hashtable[h] = (u16)(ip - base);
+               } while (A32(ref) != A32(ip));
+
+               /* Catch up */
+               while ((ip > anchor) && (ref > (u8 *)source)
+                       && (ip[-1] == ref[-1])) {
+                       ip--;
+                       ref--;
+               }
+
+               /* Encode Literal length */
+               length = (int)(ip - anchor);
+               token = op++;
+               /* Check output limit */
+               if (unlikely(op + length + (2 + 1 + LASTLITERALS)
+                       + (length >> 8) > oend))
+                       return 0;
+               if (length >= (int)RUN_MASK) {
+                       *token = (RUN_MASK << ML_BITS);
+                       len = length - RUN_MASK;
+                       for (; len > 254 ; len -= 255)
+                               *op++ = 255;
+                       *op++ = (u8)len;
+               } else
+                       *token = (length << ML_BITS);
+
+               /* Copy Literals */
+               LZ4_BLINDCOPY(anchor, op, length);
+
+_next_match:
+               /* Encode Offset */
+               LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref));
+
+               /* Start Counting */
+               ip += MINMATCH;
+               /* MinMatch verified */
+               ref += MINMATCH;
+               anchor = ip;
+
+               while (ip < MATCHLIMIT - (STEPSIZE - 1)) {
+                       #if LZ4_ARCH64
+                       u64 diff = A64(ref) ^ A64(ip);
+                       #else
+                       u32 diff = A32(ref) ^ A32(ip);
+                       #endif
+
+                       if (!diff) {
+                               ip += STEPSIZE;
+                               ref += STEPSIZE;
+                               continue;
+                       }
+                       ip += LZ4_NBCOMMONBYTES(diff);
+                       goto _endcount;
+               }
+               #if LZ4_ARCH64
+               if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) {
+                       ip += 4;
+                       ref += 4;
+               }
+               #endif
+               if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) {
+                       ip += 2;
+                       ref += 2;
+               }
+               if ((ip < MATCHLIMIT) && (*ref == *ip))
+                       ip++;
+_endcount:
+
+               /* Encode MatchLength */
+               len = (int)(ip - anchor);
+               /* Check output limit */
+               if (unlikely(op + (1 + LASTLITERALS) + (len >> 8) > oend))
+                       return 0;
+               if (len >= (int)ML_MASK) {
+                       *token += ML_MASK;
+                       len -= ML_MASK;
+                       for (; len > 509 ; len -= 510) {
+                               *op++ = 255;
+                               *op++ = 255;
+                       }
+                       if (len > 254) {
+                               len -= 255;
+                               *op++ = 255;
+                       }
+                       *op++ = (u8)len;
+               } else
+                       *token += len;
+
+               /* Test end of chunk */
+               if (ip > mflimit) {
+                       anchor = ip;
+                       break;
+               }
+
+               /* Fill table */
+               hashtable[LZ4_HASH64K_VALUE(ip-2)] = (u16)(ip - 2 - base);
+
+               /* Test next position */
+               ref = base + hashtable[LZ4_HASH64K_VALUE(ip)];
+               hashtable[LZ4_HASH64K_VALUE(ip)] = (u16)(ip - base);
+               if (A32(ref) == A32(ip)) {
+                       token = op++;
+                       *token = 0;
+                       goto _next_match;
+               }
+
+               /* Prepare next loop */
+               anchor = ip++;
+               forwardh = LZ4_HASH64K_VALUE(ip);
+       }
+
+_last_literals:
+       /* Encode Last Literals */
+       lastrun = (int)(iend - anchor);
+       if (op + lastrun + 1 + (lastrun - RUN_MASK + 255) / 255 > oend)
+               return 0;
+       if (lastrun >= (int)RUN_MASK) {
+               *op++ = (RUN_MASK << ML_BITS);
+               lastrun -= RUN_MASK;
+               for (; lastrun > 254 ; lastrun -= 255)
+                       *op++ = 255;
+               *op++ = (u8)lastrun;
+       } else
+               *op++ = (lastrun << ML_BITS);
+       memcpy(op, anchor, iend - anchor);
+       op += iend - anchor;
+       /* End */
+       return (int)(((char *)op) - dest);
+}
+
+int lz4_compress(const unsigned char *src, size_t src_len,
+                       unsigned char *dst, size_t *dst_len, void *wrkmem)
+{
+       int ret = -1;
+       int out_len = 0;
+
+       if (src_len < LZ4_64KLIMIT)
+               out_len = lz4_compress64kctx(wrkmem, src, dst, src_len,
+                               lz4_compressbound(src_len));
+       else
+               out_len = lz4_compressctx(wrkmem, src, dst, src_len,
+                               lz4_compressbound(src_len));
+
+       if (out_len < 0)
+               goto exit;
+
+       *dst_len = out_len;
+
+       return 0;
+exit:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(lz4_compress);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LZ4 compressor");
diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c
new file mode 100644 (file)
index 0000000..d3414ea
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * LZ4 Decompressor for Linux kernel
+ *
+ * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>
+ *
+ * Based on LZ4 implementation by Yann Collet.
+ *
+ * LZ4 - Fast LZ compression algorithm
+ * Copyright (C) 2011-2012, Yann Collet.
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * 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.
+ *
+ * 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.
+ *
+ *  You can contact the author at :
+ *  - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
+ *  - LZ4 source repository : http://code.google.com/p/lz4/
+ */
+
+#ifndef STATIC
+#include <linux/module.h>
+#include <linux/kernel.h>
+#endif
+#include <linux/lz4.h>
+
+#include <asm/unaligned.h>
+
+#include "lz4defs.h"
+
+static int lz4_uncompress(const char *source, char *dest, int osize)
+{
+       const BYTE *ip = (const BYTE *) source;
+       const BYTE *ref;
+       BYTE *op = (BYTE *) dest;
+       BYTE * const oend = op + osize;
+       BYTE *cpy;
+       unsigned token;
+       size_t length;
+       size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0};
+#if LZ4_ARCH64
+       size_t dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
+#endif
+
+       while (1) {
+
+               /* get runlength */
+               token = *ip++;
+               length = (token >> ML_BITS);
+               if (length == RUN_MASK) {
+                       size_t len;
+
+                       len = *ip++;
+                       for (; len == 255; length += 255)
+                               len = *ip++;
+                       length += len;
+               }
+
+               /* copy literals */
+               cpy = op + length;
+               if (unlikely(cpy > oend - COPYLENGTH)) {
+                       /*
+                        * Error: not enough place for another match
+                        * (min 4) + 5 literals
+                        */
+                       if (cpy != oend)
+                               goto _output_error;
+
+                       memcpy(op, ip, length);
+                       ip += length;
+                       break; /* EOF */
+               }
+               LZ4_WILDCOPY(ip, op, cpy);
+               ip -= (op - cpy);
+               op = cpy;
+
+               /* get offset */
+               LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip);
+               ip += 2;
+
+               /* Error: offset create reference outside destination buffer */
+               if (unlikely(ref < (BYTE *const) dest))
+                       goto _output_error;
+
+               /* get matchlength */
+               length = token & ML_MASK;
+               if (length == ML_MASK) {
+                       for (; *ip == 255; length += 255)
+                               ip++;
+                       length += *ip++;
+               }
+
+               /* copy repeated sequence */
+               if (unlikely((op - ref) < STEPSIZE)) {
+#if LZ4_ARCH64
+                       size_t dec64 = dec64table[op - ref];
+#else
+                       const int dec64 = 0;
+#endif
+                       op[0] = ref[0];
+                       op[1] = ref[1];
+                       op[2] = ref[2];
+                       op[3] = ref[3];
+                       op += 4;
+                       ref += 4;
+                       ref -= dec32table[op-ref];
+                       PUT4(ref, op);
+                       op += STEPSIZE - 4;
+                       ref -= dec64;
+               } else {
+                       LZ4_COPYSTEP(ref, op);
+               }
+               cpy = op + length - (STEPSIZE - 4);
+               if (cpy > (oend - COPYLENGTH)) {
+
+                       /* Error: request to write beyond destination buffer */
+                       if (cpy > oend)
+                               goto _output_error;
+                       LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
+                       while (op < cpy)
+                               *op++ = *ref++;
+                       op = cpy;
+                       /*
+                        * Check EOF (should never happen, since last 5 bytes
+                        * are supposed to be literals)
+                        */
+                       if (op == oend)
+                               goto _output_error;
+                       continue;
+               }
+               LZ4_SECURECOPY(ref, op, cpy);
+               op = cpy; /* correction */
+       }
+       /* end of decoding */
+       return (int) (((char *)ip) - source);
+
+       /* write overflow error detected */
+_output_error:
+       return (int) (-(((char *)ip) - source));
+}
+
+static int lz4_uncompress_unknownoutputsize(const char *source, char *dest,
+                               int isize, size_t maxoutputsize)
+{
+       const BYTE *ip = (const BYTE *) source;
+       const BYTE *const iend = ip + isize;
+       const BYTE *ref;
+
+
+       BYTE *op = (BYTE *) dest;
+       BYTE * const oend = op + maxoutputsize;
+       BYTE *cpy;
+
+       size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0};
+#if LZ4_ARCH64
+       size_t dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
+#endif
+
+       /* Main Loop */
+       while (ip < iend) {
+
+               unsigned token;
+               size_t length;
+
+               /* get runlength */
+               token = *ip++;
+               length = (token >> ML_BITS);
+               if (length == RUN_MASK) {
+                       int s = 255;
+                       while ((ip < iend) && (s == 255)) {
+                               s = *ip++;
+                               length += s;
+                       }
+               }
+               /* copy literals */
+               cpy = op + length;
+               if ((cpy > oend - COPYLENGTH) ||
+                       (ip + length > iend - COPYLENGTH)) {
+
+                       if (cpy > oend)
+                               goto _output_error;/* writes beyond buffer */
+
+                       if (ip + length != iend)
+                               goto _output_error;/*
+                                                   * Error: LZ4 format requires
+                                                   * to consume all input
+                                                   * at this stage
+                                                   */
+                       memcpy(op, ip, length);
+                       op += length;
+                       break;/* Necessarily EOF, due to parsing restrictions */
+               }
+               LZ4_WILDCOPY(ip, op, cpy);
+               ip -= (op - cpy);
+               op = cpy;
+
+               /* get offset */
+               LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip);
+               ip += 2;
+               if (ref < (BYTE * const) dest)
+                       goto _output_error;
+                       /*
+                        * Error : offset creates reference
+                        * outside of destination buffer
+                        */
+
+               /* get matchlength */
+               length = (token & ML_MASK);
+               if (length == ML_MASK) {
+                       while (ip < iend) {
+                               int s = *ip++;
+                               length += s;
+                               if (s == 255)
+                                       continue;
+                               break;
+                       }
+               }
+
+               /* copy repeated sequence */
+               if (unlikely((op - ref) < STEPSIZE)) {
+#if LZ4_ARCH64
+                       size_t dec64 = dec64table[op - ref];
+#else
+                       const int dec64 = 0;
+#endif
+                               op[0] = ref[0];
+                               op[1] = ref[1];
+                               op[2] = ref[2];
+                               op[3] = ref[3];
+                               op += 4;
+                               ref += 4;
+                               ref -= dec32table[op - ref];
+                               PUT4(ref, op);
+                               op += STEPSIZE - 4;
+                               ref -= dec64;
+               } else {
+                       LZ4_COPYSTEP(ref, op);
+               }
+               cpy = op + length - (STEPSIZE-4);
+               if (cpy > oend - COPYLENGTH) {
+                       if (cpy > oend)
+                               goto _output_error; /* write outside of buf */
+
+                       LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
+                       while (op < cpy)
+                               *op++ = *ref++;
+                       op = cpy;
+                       /*
+                        * Check EOF (should never happen, since last 5 bytes
+                        * are supposed to be literals)
+                        */
+                       if (op == oend)
+                               goto _output_error;
+                       continue;
+               }
+               LZ4_SECURECOPY(ref, op, cpy);
+               op = cpy; /* correction */
+       }
+       /* end of decoding */
+       return (int) (((char *) op) - dest);
+
+       /* write overflow error detected */
+_output_error:
+       return (int) (-(((char *) ip) - source));
+}
+
+int lz4_decompress(const char *src, size_t *src_len, char *dest,
+               size_t actual_dest_len)
+{
+       int ret = -1;
+       int input_len = 0;
+
+       input_len = lz4_uncompress(src, dest, actual_dest_len);
+       if (input_len < 0)
+               goto exit_0;
+       *src_len = input_len;
+
+       return 0;
+exit_0:
+       return ret;
+}
+#ifndef STATIC
+EXPORT_SYMBOL_GPL(lz4_decompress);
+#endif
+
+int lz4_decompress_unknownoutputsize(const char *src, size_t src_len,
+               char *dest, size_t *dest_len)
+{
+       int ret = -1;
+       int out_len = 0;
+
+       out_len = lz4_uncompress_unknownoutputsize(src, dest, src_len,
+                                       *dest_len);
+       if (out_len < 0)
+               goto exit_0;
+       *dest_len = out_len;
+
+       return 0;
+exit_0:
+       return ret;
+}
+#ifndef STATIC
+EXPORT_SYMBOL_GPL(lz4_decompress_unknownoutputsize);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LZ4 Decompressor");
+#endif
diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h
new file mode 100644 (file)
index 0000000..abcecdc
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * lz4defs.h -- architecture specific defines
+ *
+ * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.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.
+ */
+
+/*
+ * Detects 64 bits mode
+ */
+#if (defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) \
+       || defined(__ppc64__) || defined(__LP64__))
+#define LZ4_ARCH64 1
+#else
+#define LZ4_ARCH64 0
+#endif
+
+/*
+ * Architecture-specific macros
+ */
+#define BYTE   u8
+typedef struct _U16_S { u16 v; } U16_S;
+typedef struct _U32_S { u32 v; } U32_S;
+typedef struct _U64_S { u64 v; } U64_S;
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)            \
+       || defined(CONFIG_ARM) && __LINUX_ARM_ARCH__ >= 6       \
+       && defined(ARM_EFFICIENT_UNALIGNED_ACCESS)
+
+#define A16(x) (((U16_S *)(x))->v)
+#define A32(x) (((U32_S *)(x))->v)
+#define A64(x) (((U64_S *)(x))->v)
+
+#define PUT4(s, d) (A32(d) = A32(s))
+#define PUT8(s, d) (A64(d) = A64(s))
+#define LZ4_WRITE_LITTLEENDIAN_16(p, v)        \
+       do {    \
+               A16(p) = v; \
+               p += 2; \
+       } while (0)
+#else /* CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS */
+
+#define A64(x) get_unaligned((u64 *)&(((U16_S *)(x))->v))
+#define A32(x) get_unaligned((u32 *)&(((U16_S *)(x))->v))
+#define A16(x) get_unaligned((u16 *)&(((U16_S *)(x))->v))
+
+#define PUT4(s, d) \
+       put_unaligned(get_unaligned((const u32 *) s), (u32 *) d)
+#define PUT8(s, d) \
+       put_unaligned(get_unaligned((const u64 *) s), (u64 *) d)
+
+#define LZ4_WRITE_LITTLEENDIAN_16(p, v)        \
+       do {    \
+               put_unaligned(v, (u16 *)(p)); \
+               p += 2; \
+       } while (0)
+#endif
+
+#define COPYLENGTH 8
+#define ML_BITS  4
+#define ML_MASK  ((1U << ML_BITS) - 1)
+#define RUN_BITS (8 - ML_BITS)
+#define RUN_MASK ((1U << RUN_BITS) - 1)
+#define MEMORY_USAGE   14
+#define MINMATCH       4
+#define SKIPSTRENGTH   6
+#define LASTLITERALS   5
+#define MFLIMIT                (COPYLENGTH + MINMATCH)
+#define MINLENGTH      (MFLIMIT + 1)
+#define MAXD_LOG       16
+#define MAXD           (1 << MAXD_LOG)
+#define MAXD_MASK      (u32)(MAXD - 1)
+#define MAX_DISTANCE   (MAXD - 1)
+#define HASH_LOG       (MAXD_LOG - 1)
+#define HASHTABLESIZE  (1 << HASH_LOG)
+#define MAX_NB_ATTEMPTS        256
+#define OPTIMAL_ML     (int)((ML_MASK-1)+MINMATCH)
+#define LZ4_64KLIMIT   ((1<<16) + (MFLIMIT - 1))
+#define HASHLOG64K     ((MEMORY_USAGE - 2) + 1)
+#define HASH64KTABLESIZE       (1U << HASHLOG64K)
+#define LZ4_HASH_VALUE(p)      (((A32(p)) * 2654435761U) >> \
+                               ((MINMATCH * 8) - (MEMORY_USAGE-2)))
+#define LZ4_HASH64K_VALUE(p)   (((A32(p)) * 2654435761U) >> \
+                               ((MINMATCH * 8) - HASHLOG64K))
+#define HASH_VALUE(p)          (((A32(p)) * 2654435761U) >> \
+                               ((MINMATCH * 8) - HASH_LOG))
+
+#if LZ4_ARCH64/* 64-bit */
+#define STEPSIZE 8
+
+#define LZ4_COPYSTEP(s, d)     \
+       do {                    \
+               PUT8(s, d);     \
+               d += 8;         \
+               s += 8;         \
+       } while (0)
+
+#define LZ4_COPYPACKET(s, d)   LZ4_COPYSTEP(s, d)
+
+#define LZ4_SECURECOPY(s, d, e)                        \
+       do {                                    \
+               if (d < e) {                    \
+                       LZ4_WILDCOPY(s, d, e);  \
+               }                               \
+       } while (0)
+#define HTYPE u32
+
+#ifdef __BIG_ENDIAN
+#define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3)
+#else
+#define LZ4_NBCOMMONBYTES(val) (__builtin_ctzll(val) >> 3)
+#endif
+
+#else  /* 32-bit */
+#define STEPSIZE 4
+
+#define LZ4_COPYSTEP(s, d)     \
+       do {                    \
+               PUT4(s, d);     \
+               d += 4;         \
+               s += 4;         \
+       } while (0)
+
+#define LZ4_COPYPACKET(s, d)           \
+       do {                            \
+               LZ4_COPYSTEP(s, d);     \
+               LZ4_COPYSTEP(s, d);     \
+       } while (0)
+
+#define LZ4_SECURECOPY LZ4_WILDCOPY
+#define HTYPE const u8*
+
+#ifdef __BIG_ENDIAN
+#define LZ4_NBCOMMONBYTES(val) (__builtin_clz(val) >> 3)
+#else
+#define LZ4_NBCOMMONBYTES(val) (__builtin_ctz(val) >> 3)
+#endif
+
+#endif
+
+#define LZ4_READ_LITTLEENDIAN_16(d, s, p) \
+       (d = s - get_unaligned_le16(p))
+
+#define LZ4_WILDCOPY(s, d, e)          \
+       do {                            \
+               LZ4_COPYPACKET(s, d);   \
+       } while (d < e)
+
+#define LZ4_BLINDCOPY(s, d, l) \
+       do {    \
+               u8 *e = (d) + l;        \
+               LZ4_WILDCOPY(s, d, e);  \
+               d = e;  \
+       } while (0)
diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c
new file mode 100644 (file)
index 0000000..eb1a74f
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+ * LZ4 HC - High Compression Mode of LZ4
+ * Copyright (C) 2011-2012, Yann Collet.
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You can contact the author at :
+ * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
+ * - LZ4 source repository : http://code.google.com/p/lz4/
+ *
+ *  Changed for kernel use by:
+ *  Chanho Min <chanho.min@lge.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/lz4.h>
+#include <asm/unaligned.h>
+#include "lz4defs.h"
+
+struct lz4hc_data {
+       const u8 *base;
+       HTYPE hashtable[HASHTABLESIZE];
+       u16 chaintable[MAXD];
+       const u8 *nexttoupdate;
+} __attribute__((__packed__));
+
+static inline int lz4hc_init(struct lz4hc_data *hc4, const u8 *base)
+{
+       memset((void *)hc4->hashtable, 0, sizeof(hc4->hashtable));
+       memset(hc4->chaintable, 0xFF, sizeof(hc4->chaintable));
+
+#if LZ4_ARCH64
+       hc4->nexttoupdate = base + 1;
+#else
+       hc4->nexttoupdate = base;
+#endif
+       hc4->base = base;
+       return 1;
+}
+
+/* Update chains up to ip (excluded) */
+static inline void lz4hc_insert(struct lz4hc_data *hc4, const u8 *ip)
+{
+       u16 *chaintable = hc4->chaintable;
+       HTYPE *hashtable  = hc4->hashtable;
+#if LZ4_ARCH64
+       const BYTE * const base = hc4->base;
+#else
+       const int base = 0;
+#endif
+
+       while (hc4->nexttoupdate < ip) {
+               const u8 *p = hc4->nexttoupdate;
+               size_t delta = p - (hashtable[HASH_VALUE(p)] + base);
+               if (delta > MAX_DISTANCE)
+                       delta = MAX_DISTANCE;
+               chaintable[(size_t)(p) & MAXD_MASK] = (u16)delta;
+               hashtable[HASH_VALUE(p)] = (p) - base;
+               hc4->nexttoupdate++;
+       }
+}
+
+static inline size_t lz4hc_commonlength(const u8 *p1, const u8 *p2,
+               const u8 *const matchlimit)
+{
+       const u8 *p1t = p1;
+
+       while (p1t < matchlimit - (STEPSIZE - 1)) {
+#if LZ4_ARCH64
+               u64 diff = A64(p2) ^ A64(p1t);
+#else
+               u32 diff = A32(p2) ^ A32(p1t);
+#endif
+               if (!diff) {
+                       p1t += STEPSIZE;
+                       p2 += STEPSIZE;
+                       continue;
+               }
+               p1t += LZ4_NBCOMMONBYTES(diff);
+               return p1t - p1;
+       }
+#if LZ4_ARCH64
+       if ((p1t < (matchlimit-3)) && (A32(p2) == A32(p1t))) {
+               p1t += 4;
+               p2 += 4;
+       }
+#endif
+
+       if ((p1t < (matchlimit - 1)) && (A16(p2) == A16(p1t))) {
+               p1t += 2;
+               p2 += 2;
+       }
+       if ((p1t < matchlimit) && (*p2 == *p1t))
+               p1t++;
+       return p1t - p1;
+}
+
+static inline int lz4hc_insertandfindbestmatch(struct lz4hc_data *hc4,
+               const u8 *ip, const u8 *const matchlimit, const u8 **matchpos)
+{
+       u16 *const chaintable = hc4->chaintable;
+       HTYPE *const hashtable = hc4->hashtable;
+       const u8 *ref;
+#if LZ4_ARCH64
+       const BYTE * const base = hc4->base;
+#else
+       const int base = 0;
+#endif
+       int nbattempts = MAX_NB_ATTEMPTS;
+       size_t repl = 0, ml = 0;
+       u16 delta;
+
+       /* HC4 match finder */
+       lz4hc_insert(hc4, ip);
+       ref = hashtable[HASH_VALUE(ip)] + base;
+
+       /* potential repetition */
+       if (ref >= ip-4) {
+               /* confirmed */
+               if (A32(ref) == A32(ip)) {
+                       delta = (u16)(ip-ref);
+                       repl = ml  = lz4hc_commonlength(ip + MINMATCH,
+                                       ref + MINMATCH, matchlimit) + MINMATCH;
+                       *matchpos = ref;
+               }
+               ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
+       }
+
+       while ((ref >= ip - MAX_DISTANCE) && nbattempts) {
+               nbattempts--;
+               if (*(ref + ml) == *(ip + ml)) {
+                       if (A32(ref) == A32(ip)) {
+                               size_t mlt =
+                                       lz4hc_commonlength(ip + MINMATCH,
+                                       ref + MINMATCH, matchlimit) + MINMATCH;
+                               if (mlt > ml) {
+                                       ml = mlt;
+                                       *matchpos = ref;
+                               }
+                       }
+               }
+               ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
+       }
+
+       /* Complete table */
+       if (repl) {
+               const BYTE *ptr = ip;
+               const BYTE *end;
+               end = ip + repl - (MINMATCH-1);
+               /* Pre-Load */
+               while (ptr < end - delta) {
+                       chaintable[(size_t)(ptr) & MAXD_MASK] = delta;
+                       ptr++;
+               }
+               do {
+                       chaintable[(size_t)(ptr) & MAXD_MASK] = delta;
+                       /* Head of chain */
+                       hashtable[HASH_VALUE(ptr)] = (ptr) - base;
+                       ptr++;
+               } while (ptr < end);
+               hc4->nexttoupdate = end;
+       }
+
+       return (int)ml;
+}
+
+static inline int lz4hc_insertandgetwidermatch(struct lz4hc_data *hc4,
+       const u8 *ip, const u8 *startlimit, const u8 *matchlimit, int longest,
+       const u8 **matchpos, const u8 **startpos)
+{
+       u16 *const chaintable = hc4->chaintable;
+       HTYPE *const hashtable = hc4->hashtable;
+#if LZ4_ARCH64
+       const BYTE * const base = hc4->base;
+#else
+       const int base = 0;
+#endif
+       const u8 *ref;
+       int nbattempts = MAX_NB_ATTEMPTS;
+       int delta = (int)(ip - startlimit);
+
+       /* First Match */
+       lz4hc_insert(hc4, ip);
+       ref = hashtable[HASH_VALUE(ip)] + base;
+
+       while ((ref >= ip - MAX_DISTANCE) && (ref >= hc4->base)
+               && (nbattempts)) {
+               nbattempts--;
+               if (*(startlimit + longest) == *(ref - delta + longest)) {
+                       if (A32(ref) == A32(ip)) {
+                               const u8 *reft = ref + MINMATCH;
+                               const u8 *ipt = ip + MINMATCH;
+                               const u8 *startt = ip;
+
+                               while (ipt < matchlimit-(STEPSIZE - 1)) {
+                                       #if LZ4_ARCH64
+                                       u64 diff = A64(reft) ^ A64(ipt);
+                                       #else
+                                       u32 diff = A32(reft) ^ A32(ipt);
+                                       #endif
+
+                                       if (!diff) {
+                                               ipt += STEPSIZE;
+                                               reft += STEPSIZE;
+                                               continue;
+                                       }
+                                       ipt += LZ4_NBCOMMONBYTES(diff);
+                                       goto _endcount;
+                               }
+                               #if LZ4_ARCH64
+                               if ((ipt < (matchlimit - 3))
+                                       && (A32(reft) == A32(ipt))) {
+                                       ipt += 4;
+                                       reft += 4;
+                               }
+                               ipt += 2;
+                               #endif
+                               if ((ipt < (matchlimit - 1))
+                                       && (A16(reft) == A16(ipt))) {
+                                       reft += 2;
+                               }
+                               if ((ipt < matchlimit) && (*reft == *ipt))
+                                       ipt++;
+_endcount:
+                               reft = ref;
+
+                               while ((startt > startlimit)
+                                       && (reft > hc4->base)
+                                       && (startt[-1] == reft[-1])) {
+                                       startt--;
+                                       reft--;
+                               }
+
+                               if ((ipt - startt) > longest) {
+                                       longest = (int)(ipt - startt);
+                                       *matchpos = reft;
+                                       *startpos = startt;
+                               }
+                       }
+               }
+               ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
+       }
+       return longest;
+}
+
+static inline int lz4_encodesequence(const u8 **ip, u8 **op, const u8 **anchor,
+               int ml, const u8 *ref)
+{
+       int length, len;
+       u8 *token;
+
+       /* Encode Literal length */
+       length = (int)(*ip - *anchor);
+       token = (*op)++;
+       if (length >= (int)RUN_MASK) {
+               *token = (RUN_MASK << ML_BITS);
+               len = length - RUN_MASK;
+               for (; len > 254 ; len -= 255)
+                       *(*op)++ = 255;
+               *(*op)++ = (u8)len;
+       } else
+               *token = (length << ML_BITS);
+
+       /* Copy Literals */
+       LZ4_BLINDCOPY(*anchor, *op, length);
+
+       /* Encode Offset */
+       LZ4_WRITE_LITTLEENDIAN_16(*op, (u16)(*ip - ref));
+
+       /* Encode MatchLength */
+       len = (int)(ml - MINMATCH);
+       if (len >= (int)ML_MASK) {
+               *token += ML_MASK;
+               len -= ML_MASK;
+               for (; len > 509 ; len -= 510) {
+                       *(*op)++ = 255;
+                       *(*op)++ = 255;
+               }
+               if (len > 254) {
+                       len -= 255;
+                       *(*op)++ = 255;
+               }
+               *(*op)++ = (u8)len;
+       } else
+               *token += len;
+
+       /* Prepare next loop */
+       *ip += ml;
+       *anchor = *ip;
+
+       return 0;
+}
+
+static int lz4_compresshcctx(struct lz4hc_data *ctx,
+               const char *source,
+               char *dest,
+               int isize)
+{
+       const u8 *ip = (const u8 *)source;
+       const u8 *anchor = ip;
+       const u8 *const iend = ip + isize;
+       const u8 *const mflimit = iend - MFLIMIT;
+       const u8 *const matchlimit = (iend - LASTLITERALS);
+
+       u8 *op = (u8 *)dest;
+
+       int ml, ml2, ml3, ml0;
+       const u8 *ref = NULL;
+       const u8 *start2 = NULL;
+       const u8 *ref2 = NULL;
+       const u8 *start3 = NULL;
+       const u8 *ref3 = NULL;
+       const u8 *start0;
+       const u8 *ref0;
+       int lastrun;
+
+       ip++;
+
+       /* Main Loop */
+       while (ip < mflimit) {
+               ml = lz4hc_insertandfindbestmatch(ctx, ip, matchlimit, (&ref));
+               if (!ml) {
+                       ip++;
+                       continue;
+               }
+
+               /* saved, in case we would skip too much */
+               start0 = ip;
+               ref0 = ref;
+               ml0 = ml;
+_search2:
+               if (ip+ml < mflimit)
+                       ml2 = lz4hc_insertandgetwidermatch(ctx, ip + ml - 2,
+                               ip + 1, matchlimit, ml, &ref2, &start2);
+               else
+                       ml2 = ml;
+               /* No better match */
+               if (ml2 == ml) {
+                       lz4_encodesequence(&ip, &op, &anchor, ml, ref);
+                       continue;
+               }
+
+               if (start0 < ip) {
+                       /* empirical */
+                       if (start2 < ip + ml0) {
+                               ip = start0;
+                               ref = ref0;
+                               ml = ml0;
+                       }
+               }
+               /*
+                * Here, start0==ip
+                * First Match too small : removed
+                */
+               if ((start2 - ip) < 3) {
+                       ml = ml2;
+                       ip = start2;
+                       ref = ref2;
+                       goto _search2;
+               }
+
+_search3:
+               /*
+                * Currently we have :
+                * ml2 > ml1, and
+                * ip1+3 <= ip2 (usually < ip1+ml1)
+                */
+               if ((start2 - ip) < OPTIMAL_ML) {
+                       int correction;
+                       int new_ml = ml;
+                       if (new_ml > OPTIMAL_ML)
+                               new_ml = OPTIMAL_ML;
+                       if (ip + new_ml > start2 + ml2 - MINMATCH)
+                               new_ml = (int)(start2 - ip) + ml2 - MINMATCH;
+                       correction = new_ml - (int)(start2 - ip);
+                       if (correction > 0) {
+                               start2 += correction;
+                               ref2 += correction;
+                               ml2 -= correction;
+                       }
+               }
+               /*
+                * Now, we have start2 = ip+new_ml,
+                * with new_ml=min(ml, OPTIMAL_ML=18)
+                */
+               if (start2 + ml2 < mflimit)
+                       ml3 = lz4hc_insertandgetwidermatch(ctx,
+                               start2 + ml2 - 3, start2, matchlimit,
+                               ml2, &ref3, &start3);
+               else
+                       ml3 = ml2;
+
+               /* No better match : 2 sequences to encode */
+               if (ml3 == ml2) {
+                       /* ip & ref are known; Now for ml */
+                       if (start2 < ip+ml)
+                               ml = (int)(start2 - ip);
+
+                       /* Now, encode 2 sequences */
+                       lz4_encodesequence(&ip, &op, &anchor, ml, ref);
+                       ip = start2;
+                       lz4_encodesequence(&ip, &op, &anchor, ml2, ref2);
+                       continue;
+               }
+
+               /* Not enough space for match 2 : remove it */
+               if (start3 < ip + ml + 3) {
+                       /*
+                        * can write Seq1 immediately ==> Seq2 is removed,
+                        * so Seq3 becomes Seq1
+                        */
+                       if (start3 >= (ip + ml)) {
+                               if (start2 < ip + ml) {
+                                       int correction =
+                                               (int)(ip + ml - start2);
+                                       start2 += correction;
+                                       ref2 += correction;
+                                       ml2 -= correction;
+                                       if (ml2 < MINMATCH) {
+                                               start2 = start3;
+                                               ref2 = ref3;
+                                               ml2 = ml3;
+                                       }
+                               }
+
+                               lz4_encodesequence(&ip, &op, &anchor, ml, ref);
+                               ip  = start3;
+                               ref = ref3;
+                               ml  = ml3;
+
+                               start0 = start2;
+                               ref0 = ref2;
+                               ml0 = ml2;
+                               goto _search2;
+                       }
+
+                       start2 = start3;
+                       ref2 = ref3;
+                       ml2 = ml3;
+                       goto _search3;
+               }
+
+               /*
+                * OK, now we have 3 ascending matches; let's write at least
+                * the first one ip & ref are known; Now for ml
+                */
+               if (start2 < ip + ml) {
+                       if ((start2 - ip) < (int)ML_MASK) {
+                               int correction;
+                               if (ml > OPTIMAL_ML)
+                                       ml = OPTIMAL_ML;
+                               if (ip + ml > start2 + ml2 - MINMATCH)
+                                       ml = (int)(start2 - ip) + ml2
+                                               - MINMATCH;
+                               correction = ml - (int)(start2 - ip);
+                               if (correction > 0) {
+                                       start2 += correction;
+                                       ref2 += correction;
+                                       ml2 -= correction;
+                               }
+                       } else
+                               ml = (int)(start2 - ip);
+               }
+               lz4_encodesequence(&ip, &op, &anchor, ml, ref);
+
+               ip = start2;
+               ref = ref2;
+               ml = ml2;
+
+               start2 = start3;
+               ref2 = ref3;
+               ml2 = ml3;
+
+               goto _search3;
+       }
+
+       /* Encode Last Literals */
+       lastrun = (int)(iend - anchor);
+       if (lastrun >= (int)RUN_MASK) {
+               *op++ = (RUN_MASK << ML_BITS);
+               lastrun -= RUN_MASK;
+               for (; lastrun > 254 ; lastrun -= 255)
+                       *op++ = 255;
+               *op++ = (u8) lastrun;
+       } else
+               *op++ = (lastrun << ML_BITS);
+       memcpy(op, anchor, iend - anchor);
+       op += iend - anchor;
+       /* End */
+       return (int) (((char *)op) - dest);
+}
+
+int lz4hc_compress(const unsigned char *src, size_t src_len,
+                       unsigned char *dst, size_t *dst_len, void *wrkmem)
+{
+       int ret = -1;
+       int out_len = 0;
+
+       struct lz4hc_data *hc4 = (struct lz4hc_data *)wrkmem;
+       lz4hc_init(hc4, (const u8 *)src);
+       out_len = lz4_compresshcctx((struct lz4hc_data *)hc4, (const u8 *)src,
+               (char *)dst, (int)src_len);
+
+       if (out_len < 0)
+               goto exit;
+
+       *dst_len = out_len;
+       return 0;
+
+exit:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(lz4hc_compress);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LZ4HC compressor");
index a1cf8ca..a685c8a 100644 (file)
@@ -247,13 +247,15 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents,
        struct scatterlist *sg, *prv;
        unsigned int left;
 
+       memset(table, 0, sizeof(*table));
+
+       if (nents == 0)
+               return -EINVAL;
 #ifndef ARCH_HAS_SG_CHAIN
        if (WARN_ON_ONCE(nents > max_ents))
                return -EINVAL;
 #endif
 
-       memset(table, 0, sizeof(*table));
-
        left = nents;
        prv = NULL;
        do {
@@ -453,6 +455,65 @@ void sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl,
 }
 EXPORT_SYMBOL(sg_miter_start);
 
+static bool sg_miter_get_next_page(struct sg_mapping_iter *miter)
+{
+       if (!miter->__remaining) {
+               struct scatterlist *sg;
+               unsigned long pgoffset;
+
+               if (!__sg_page_iter_next(&miter->piter))
+                       return false;
+
+               sg = miter->piter.sg;
+               pgoffset = miter->piter.sg_pgoffset;
+
+               miter->__offset = pgoffset ? 0 : sg->offset;
+               miter->__remaining = sg->offset + sg->length -
+                               (pgoffset << PAGE_SHIFT) - miter->__offset;
+               miter->__remaining = min_t(unsigned long, miter->__remaining,
+                                          PAGE_SIZE - miter->__offset);
+       }
+
+       return true;
+}
+
+/**
+ * sg_miter_skip - reposition mapping iterator
+ * @miter: sg mapping iter to be skipped
+ * @offset: number of bytes to plus the current location
+ *
+ * Description:
+ *   Sets the offset of @miter to its current location plus @offset bytes.
+ *   If mapping iterator @miter has been proceeded by sg_miter_next(), this
+ *   stops @miter.
+ *
+ * Context:
+ *   Don't care if @miter is stopped, or not proceeded yet.
+ *   Otherwise, preemption disabled if the SG_MITER_ATOMIC is set.
+ *
+ * Returns:
+ *   true if @miter contains the valid mapping.  false if end of sg
+ *   list is reached.
+ */
+static bool sg_miter_skip(struct sg_mapping_iter *miter, off_t offset)
+{
+       sg_miter_stop(miter);
+
+       while (offset) {
+               off_t consumed;
+
+               if (!sg_miter_get_next_page(miter))
+                       return false;
+
+               consumed = min_t(off_t, offset, miter->__remaining);
+               miter->__offset += consumed;
+               miter->__remaining -= consumed;
+               offset -= consumed;
+       }
+
+       return true;
+}
+
 /**
  * sg_miter_next - proceed mapping iterator to the next mapping
  * @miter: sg mapping iter to proceed
@@ -478,22 +539,9 @@ bool sg_miter_next(struct sg_mapping_iter *miter)
         * Get to the next page if necessary.
         * __remaining, __offset is adjusted by sg_miter_stop
         */
-       if (!miter->__remaining) {
-               struct scatterlist *sg;
-               unsigned long pgoffset;
-
-               if (!__sg_page_iter_next(&miter->piter))
-                       return false;
-
-               sg = miter->piter.sg;
-               pgoffset = miter->piter.sg_pgoffset;
+       if (!sg_miter_get_next_page(miter))
+               return false;
 
-               miter->__offset = pgoffset ? 0 : sg->offset;
-               miter->__remaining = sg->offset + sg->length -
-                               (pgoffset << PAGE_SHIFT) - miter->__offset;
-               miter->__remaining = min_t(unsigned long, miter->__remaining,
-                                          PAGE_SIZE - miter->__offset);
-       }
        miter->page = sg_page_iter_page(&miter->piter);
        miter->consumed = miter->length = miter->__remaining;
 
@@ -552,14 +600,16 @@ EXPORT_SYMBOL(sg_miter_stop);
  * @nents:              Number of SG entries
  * @buf:                Where to copy from
  * @buflen:             The number of bytes to copy
- * @to_buffer:                  transfer direction (non zero == from an sg list to a
- *                      buffer, 0 == from a buffer to an sg list
+ * @skip:               Number of bytes to skip before copying
+ * @to_buffer:          transfer direction (true == from an sg list to a
+ *                      buffer, false == from a buffer to an sg list
  *
  * Returns the number of copied bytes.
  *
  **/
 static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
-                            void *buf, size_t buflen, int to_buffer)
+                            void *buf, size_t buflen, off_t skip,
+                            bool to_buffer)
 {
        unsigned int offset = 0;
        struct sg_mapping_iter miter;
@@ -573,6 +623,9 @@ static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
 
        sg_miter_start(&miter, sgl, nents, sg_flags);
 
+       if (!sg_miter_skip(&miter, skip))
+               return false;
+
        local_irq_save(flags);
 
        while (sg_miter_next(&miter) && offset < buflen) {
@@ -607,7 +660,7 @@ static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
 size_t sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents,
                           void *buf, size_t buflen)
 {
-       return sg_copy_buffer(sgl, nents, buf, buflen, 0);
+       return sg_copy_buffer(sgl, nents, buf, buflen, 0, false);
 }
 EXPORT_SYMBOL(sg_copy_from_buffer);
 
@@ -624,6 +677,42 @@ EXPORT_SYMBOL(sg_copy_from_buffer);
 size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                         void *buf, size_t buflen)
 {
-       return sg_copy_buffer(sgl, nents, buf, buflen, 1);
+       return sg_copy_buffer(sgl, nents, buf, buflen, 0, true);
 }
 EXPORT_SYMBOL(sg_copy_to_buffer);
+
+/**
+ * sg_pcopy_from_buffer - Copy from a linear buffer to an SG list
+ * @sgl:                The SG list
+ * @nents:              Number of SG entries
+ * @buf:                Where to copy from
+ * @skip:               Number of bytes to skip before copying
+ * @buflen:             The number of bytes to copy
+ *
+ * Returns the number of copied bytes.
+ *
+ **/
+size_t sg_pcopy_from_buffer(struct scatterlist *sgl, unsigned int nents,
+                           void *buf, size_t buflen, off_t skip)
+{
+       return sg_copy_buffer(sgl, nents, buf, buflen, skip, false);
+}
+EXPORT_SYMBOL(sg_pcopy_from_buffer);
+
+/**
+ * sg_pcopy_to_buffer - Copy from an SG list to a linear buffer
+ * @sgl:                The SG list
+ * @nents:              Number of SG entries
+ * @buf:                Where to copy to
+ * @skip:               Number of bytes to skip before copying
+ * @buflen:             The number of bytes to copy
+ *
+ * Returns the number of copied bytes.
+ *
+ **/
+size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
+                         void *buf, size_t buflen, off_t skip)
+{
+       return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
+}
+EXPORT_SYMBOL(sg_pcopy_to_buffer);
index 7d84676..739a363 100644 (file)
@@ -922,6 +922,103 @@ char *ip4_addr_string(char *buf, char *end, const u8 *addr,
        return string(buf, end, ip4_addr, spec);
 }
 
+static noinline_for_stack
+char *ip6_addr_string_sa(char *buf, char *end, const struct sockaddr_in6 *sa,
+                        struct printf_spec spec, const char *fmt)
+{
+       bool have_p = false, have_s = false, have_f = false, have_c = false;
+       char ip6_addr[sizeof("[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255]") +
+                     sizeof(":12345") + sizeof("/123456789") +
+                     sizeof("%1234567890")];
+       char *p = ip6_addr, *pend = ip6_addr + sizeof(ip6_addr);
+       const u8 *addr = (const u8 *) &sa->sin6_addr;
+       char fmt6[2] = { fmt[0], '6' };
+       u8 off = 0;
+
+       fmt++;
+       while (isalpha(*++fmt)) {
+               switch (*fmt) {
+               case 'p':
+                       have_p = true;
+                       break;
+               case 'f':
+                       have_f = true;
+                       break;
+               case 's':
+                       have_s = true;
+                       break;
+               case 'c':
+                       have_c = true;
+                       break;
+               }
+       }
+
+       if (have_p || have_s || have_f) {
+               *p = '[';
+               off = 1;
+       }
+
+       if (fmt6[0] == 'I' && have_c)
+               p = ip6_compressed_string(ip6_addr + off, addr);
+       else
+               p = ip6_string(ip6_addr + off, addr, fmt6);
+
+       if (have_p || have_s || have_f)
+               *p++ = ']';
+
+       if (have_p) {
+               *p++ = ':';
+               p = number(p, pend, ntohs(sa->sin6_port), spec);
+       }
+       if (have_f) {
+               *p++ = '/';
+               p = number(p, pend, ntohl(sa->sin6_flowinfo &
+                                         IPV6_FLOWINFO_MASK), spec);
+       }
+       if (have_s) {
+               *p++ = '%';
+               p = number(p, pend, sa->sin6_scope_id, spec);
+       }
+       *p = '\0';
+
+       return string(buf, end, ip6_addr, spec);
+}
+
+static noinline_for_stack
+char *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa,
+                        struct printf_spec spec, const char *fmt)
+{
+       bool have_p = false;
+       char *p, ip4_addr[sizeof("255.255.255.255") + sizeof(":12345")];
+       char *pend = ip4_addr + sizeof(ip4_addr);
+       const u8 *addr = (const u8 *) &sa->sin_addr.s_addr;
+       char fmt4[3] = { fmt[0], '4', 0 };
+
+       fmt++;
+       while (isalpha(*++fmt)) {
+               switch (*fmt) {
+               case 'p':
+                       have_p = true;
+                       break;
+               case 'h':
+               case 'l':
+               case 'n':
+               case 'b':
+                       fmt4[2] = *fmt;
+                       break;
+               }
+       }
+
+       p = ip4_string(ip4_addr, addr, fmt4);
+       if (have_p) {
+               *p++ = ':';
+               p = number(p, pend, ntohs(sa->sin_port), spec);
+       }
+       *p = '\0';
+
+       return string(buf, end, ip4_addr, spec);
+}
+
 static noinline_for_stack
 char *uuid_string(char *buf, char *end, const u8 *addr,
                  struct printf_spec spec, const char *fmt)
@@ -1007,11 +1104,17 @@ int kptr_restrict __read_mostly;
  * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way
  *       IPv4 uses dot-separated decimal without leading 0's (1.2.3.4)
  *       IPv6 uses colon separated network-order 16 bit hex with leading 0's
+ *       [S][pfs]
+ *       Generic IPv4/IPv6 address (struct sockaddr *) that falls back to
+ *       [4] or [6] and is able to print port [p], flowinfo [f], scope [s]
  * - 'i' [46] for 'raw' IPv4/IPv6 addresses
  *       IPv6 omits the colons (01020304...0f)
  *       IPv4 uses dot-separated decimal with leading 0's (010.123.045.006)
- * - '[Ii]4[hnbl]' IPv4 addresses in host, network, big or little endian order
- * - 'I6c' for IPv6 addresses printed as specified by
+ *       [S][pfs]
+ *       Generic IPv4/IPv6 address (struct sockaddr *) that falls back to
+ *       [4] or [6] and is able to print port [p], flowinfo [f], scope [s]
+ * - '[Ii][4S][hnbl]' IPv4 addresses in host, network, big or little endian order
+ * - 'I[6S]c' for IPv6 addresses printed as specified by
  *       http://tools.ietf.org/html/rfc5952
  * - 'U' For a 16 byte UUID/GUID, it prints the UUID/GUID in the form
  *       "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
@@ -1093,6 +1196,21 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
                        return ip6_addr_string(buf, end, ptr, spec, fmt);
                case '4':
                        return ip4_addr_string(buf, end, ptr, spec, fmt);
+               case 'S': {
+                       const union {
+                               struct sockaddr         raw;
+                               struct sockaddr_in      v4;
+                               struct sockaddr_in6     v6;
+                       } *sa = ptr;
+
+                       switch (sa->raw.sa_family) {
+                       case AF_INET:
+                               return ip4_addr_string_sa(buf, end, &sa->v4, spec, fmt);
+                       case AF_INET6:
+                               return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt);
+                       default:
+                               return string(buf, end, "(invalid address)", spec);
+                       }}
                }
                break;
        case 'U':
@@ -1370,6 +1488,8 @@ qualifier:
  * %pI6 print an IPv6 address with colons
  * %pi6 print an IPv6 address without colons
  * %pI6c print an IPv6 address as specified by RFC 5952
+ * %pIS depending on sa_family of 'struct sockaddr *' print IPv4/IPv6 address
+ * %piS depending on sa_family of 'struct sockaddr *' print IPv4/IPv6 address
  * %pU[bBlL] print a UUID/GUID in big or little endian using lower or upper
  *   case.
  * %*ph[CDN] a variable-length hex string with a separator (supports up to 64
index 7905fe7..4b51ac1 100644 (file)
@@ -1539,12 +1539,12 @@ static void do_sync_mmap_readahead(struct vm_area_struct *vma,
        struct address_space *mapping = file->f_mapping;
 
        /* If we don't want any read-ahead, don't bother */
-       if (VM_RandomReadHint(vma))
+       if (vma->vm_flags & VM_RAND_READ)
                return;
        if (!ra->ra_pages)
                return;
 
-       if (VM_SequentialReadHint(vma)) {
+       if (vma->vm_flags & VM_SEQ_READ) {
                page_cache_sync_readahead(mapping, ra, file, offset,
                                          ra->ra_pages);
                return;
@@ -1584,7 +1584,7 @@ static void do_async_mmap_readahead(struct vm_area_struct *vma,
        struct address_space *mapping = file->f_mapping;
 
        /* If we don't want any read-ahead, don't bother */
-       if (VM_RandomReadHint(vma))
+       if (vma->vm_flags & VM_RAND_READ)
                return;
        if (ra->mmap_miss > 0)
                ra->mmap_miss--;
index 8562de0..4390ac6 100644 (file)
@@ -32,11 +32,6 @@ static inline void set_page_refcounted(struct page *page)
        set_page_count(page, 1);
 }
 
-static inline void __put_page(struct page *page)
-{
-       atomic_dec(&page->_count);
-}
-
 static inline void __get_page_tail_foll(struct page *page,
                                        bool get_page_head)
 {
index c5fad93..a847bfe 100644 (file)
@@ -566,7 +566,7 @@ int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size)
 /**
  * __next_free_mem_range - next function for for_each_free_mem_range()
  * @idx: pointer to u64 loop variable
- * @nid: nid: node selector, %MAX_NUMNODES for all nodes
+ * @nid: node selector, %MAX_NUMNODES for all nodes
  * @out_start: ptr to phys_addr_t for start address of the range, can be %NULL
  * @out_end: ptr to phys_addr_t for end address of the range, can be %NULL
  * @out_nid: ptr to int for nid of the range, can be %NULL
index 2e851f4..d12ca6f 100644 (file)
@@ -187,10 +187,6 @@ struct mem_cgroup_per_node {
        struct mem_cgroup_per_zone zoneinfo[MAX_NR_ZONES];
 };
 
-struct mem_cgroup_lru_info {
-       struct mem_cgroup_per_node *nodeinfo[0];
-};
-
 /*
  * Cgroups above their limits are maintained in a RB-Tree, independent of
  * their hierarchy representation
@@ -267,28 +263,10 @@ struct mem_cgroup {
        /* vmpressure notifications */
        struct vmpressure vmpressure;
 
-       union {
-               /*
-                * the counter to account for mem+swap usage.
-                */
-               struct res_counter memsw;
-
-               /*
-                * rcu_freeing is used only when freeing struct mem_cgroup,
-                * so put it into a union to avoid wasting more memory.
-                * It must be disjoint from the css field.  It could be
-                * in a union with the res field, but res plays a much
-                * larger part in mem_cgroup life than memsw, and might
-                * be of interest, even at time of free, when debugging.
-                * So share rcu_head with the less interesting memsw.
-                */
-               struct rcu_head rcu_freeing;
-               /*
-                * We also need some space for a worker in deferred freeing.
-                * By the time we call it, rcu_freeing is no longer in use.
-                */
-               struct work_struct work_freeing;
-       };
+       /*
+        * the counter to account for mem+swap usage.
+        */
+       struct res_counter memsw;
 
        /*
         * the counter to account for kernel memory usage.
@@ -303,8 +281,6 @@ struct mem_cgroup {
        bool            oom_lock;
        atomic_t        under_oom;
 
-       atomic_t        refcnt;
-
        int     swappiness;
        /* OOM-Killer disable */
        int             oom_kill_disable;
@@ -366,14 +342,8 @@ struct mem_cgroup {
        atomic_t        numainfo_updating;
 #endif
 
-       /*
-        * Per cgroup active and inactive list, similar to the
-        * per zone LRU lists.
-        *
-        * WARNING: This has to be the last element of the struct. Don't
-        * add new fields after this point.
-        */
-       struct mem_cgroup_lru_info info;
+       struct mem_cgroup_per_node *nodeinfo[0];
+       /* WARNING: nodeinfo must be the last member here */
 };
 
 static size_t memcg_size(void)
@@ -416,6 +386,11 @@ static void memcg_kmem_clear_activated(struct mem_cgroup *memcg)
 
 static void memcg_kmem_mark_dead(struct mem_cgroup *memcg)
 {
+       /*
+        * Our caller must use css_get() first, because memcg_uncharge_kmem()
+        * will call css_put() if it sees the memcg is dead.
+        */
+       smp_wmb();
        if (test_bit(KMEM_ACCOUNTED_ACTIVE, &memcg->kmem_account_flags))
                set_bit(KMEM_ACCOUNTED_DEAD, &memcg->kmem_account_flags);
 }
@@ -508,9 +483,6 @@ enum res_type {
  */
 static DEFINE_MUTEX(memcg_create_mutex);
 
-static void mem_cgroup_get(struct mem_cgroup *memcg);
-static void mem_cgroup_put(struct mem_cgroup *memcg);
-
 static inline
 struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *s)
 {
@@ -561,15 +533,15 @@ void sock_update_memcg(struct sock *sk)
                 */
                if (sk->sk_cgrp) {
                        BUG_ON(mem_cgroup_is_root(sk->sk_cgrp->memcg));
-                       mem_cgroup_get(sk->sk_cgrp->memcg);
+                       css_get(&sk->sk_cgrp->memcg->css);
                        return;
                }
 
                rcu_read_lock();
                memcg = mem_cgroup_from_task(current);
                cg_proto = sk->sk_prot->proto_cgroup(memcg);
-               if (!mem_cgroup_is_root(memcg) && memcg_proto_active(cg_proto)) {
-                       mem_cgroup_get(memcg);
+               if (!mem_cgroup_is_root(memcg) &&
+                   memcg_proto_active(cg_proto) && css_tryget(&memcg->css)) {
                        sk->sk_cgrp = cg_proto;
                }
                rcu_read_unlock();
@@ -583,7 +555,7 @@ void sock_release_memcg(struct sock *sk)
                struct mem_cgroup *memcg;
                WARN_ON(!sk->sk_cgrp->memcg);
                memcg = sk->sk_cgrp->memcg;
-               mem_cgroup_put(memcg);
+               css_put(&sk->sk_cgrp->memcg->css);
        }
 }
 
@@ -683,7 +655,7 @@ static struct mem_cgroup_per_zone *
 mem_cgroup_zoneinfo(struct mem_cgroup *memcg, int nid, int zid)
 {
        VM_BUG_ON((unsigned)nid >= nr_node_ids);
-       return &memcg->info.nodeinfo[nid]->zoneinfo[zid];
+       return &memcg->nodeinfo[nid]->zoneinfo[zid];
 }
 
 struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *memcg)
@@ -3060,8 +3032,16 @@ static void memcg_uncharge_kmem(struct mem_cgroup *memcg, u64 size)
        if (res_counter_uncharge(&memcg->kmem, size))
                return;
 
+       /*
+        * Releases a reference taken in kmem_cgroup_css_offline in case
+        * this last uncharge is racing with the offlining code or it is
+        * outliving the memcg existence.
+        *
+        * The memory barrier imposed by test&clear is paired with the
+        * explicit one in memcg_kmem_mark_dead().
+        */
        if (memcg_kmem_test_and_clear_dead(memcg))
-               mem_cgroup_put(memcg);
+               css_put(&memcg->css);
 }
 
 void memcg_cache_list_add(struct mem_cgroup *memcg, struct kmem_cache *cachep)
@@ -3252,7 +3232,7 @@ void memcg_release_cache(struct kmem_cache *s)
        list_del(&s->memcg_params->list);
        mutex_unlock(&memcg->slab_caches_mutex);
 
-       mem_cgroup_put(memcg);
+       css_put(&memcg->css);
 out:
        kfree(s->memcg_params);
 }
@@ -3412,16 +3392,18 @@ static struct kmem_cache *memcg_create_kmem_cache(struct mem_cgroup *memcg,
 
        mutex_lock(&memcg_cache_mutex);
        new_cachep = cachep->memcg_params->memcg_caches[idx];
-       if (new_cachep)
+       if (new_cachep) {
+               css_put(&memcg->css);
                goto out;
+       }
 
        new_cachep = kmem_cache_dup(memcg, cachep);
        if (new_cachep == NULL) {
                new_cachep = cachep;
+               css_put(&memcg->css);
                goto out;
        }
 
-       mem_cgroup_get(memcg);
        atomic_set(&new_cachep->memcg_params->nr_pages , 0);
 
        cachep->memcg_params->memcg_caches[idx] = new_cachep;
@@ -3509,8 +3491,6 @@ static void memcg_create_cache_work_func(struct work_struct *w)
 
        cw = container_of(w, struct create_work, work);
        memcg_create_kmem_cache(cw->memcg, cw->cachep);
-       /* Drop the reference gotten when we enqueued. */
-       css_put(&cw->memcg->css);
        kfree(cw);
 }
 
@@ -3647,6 +3627,34 @@ __memcg_kmem_newpage_charge(gfp_t gfp, struct mem_cgroup **_memcg, int order)
        int ret;
 
        *_memcg = NULL;
+
+       /*
+        * Disabling accounting is only relevant for some specific memcg
+        * internal allocations. Therefore we would initially not have such
+        * check here, since direct calls to the page allocator that are marked
+        * with GFP_KMEMCG only happen outside memcg core. We are mostly
+        * concerned with cache allocations, and by having this test at
+        * memcg_kmem_get_cache, we are already able to relay the allocation to
+        * the root cache and bypass the memcg cache altogether.
+        *
+        * There is one exception, though: the SLUB allocator does not create
+        * large order caches, but rather service large kmallocs directly from
+        * the page allocator. Therefore, the following sequence when backed by
+        * the SLUB allocator:
+        *
+        *      memcg_stop_kmem_account();
+        *      kmalloc(<large_number>)
+        *      memcg_resume_kmem_account();
+        *
+        * would effectively ignore the fact that we should skip accounting,
+        * since it will drive us directly to this function without passing
+        * through the cache selector memcg_kmem_get_cache. Such large
+        * allocations are extremely rare but can happen, for instance, for the
+        * cache arrays. We bring this test here.
+        */
+       if (!current->mm || current->memcg_kmem_skip_account)
+               return true;
+
        memcg = try_get_mem_cgroup_from_mm(current->mm);
 
        /*
@@ -4200,12 +4208,12 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype,
        unlock_page_cgroup(pc);
        /*
         * even after unlock, we have memcg->res.usage here and this memcg
-        * will never be freed.
+        * will never be freed, so it's safe to call css_get().
         */
        memcg_check_events(memcg, page);
        if (do_swap_account && ctype == MEM_CGROUP_CHARGE_TYPE_SWAPOUT) {
                mem_cgroup_swap_statistics(memcg, true);
-               mem_cgroup_get(memcg);
+               css_get(&memcg->css);
        }
        /*
         * Migration does not charge the res_counter for the
@@ -4317,7 +4325,7 @@ mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout)
 
        /*
         * record memcg information,  if swapout && memcg != NULL,
-        * mem_cgroup_get() was called in uncharge().
+        * css_get() was called in uncharge().
         */
        if (do_swap_account && swapout && memcg)
                swap_cgroup_record(ent, css_id(&memcg->css));
@@ -4348,7 +4356,7 @@ void mem_cgroup_uncharge_swap(swp_entry_t ent)
                if (!mem_cgroup_is_root(memcg))
                        res_counter_uncharge(&memcg->memsw, PAGE_SIZE);
                mem_cgroup_swap_statistics(memcg, false);
-               mem_cgroup_put(memcg);
+               css_put(&memcg->css);
        }
        rcu_read_unlock();
 }
@@ -4382,11 +4390,14 @@ static int mem_cgroup_move_swap_account(swp_entry_t entry,
                 * This function is only called from task migration context now.
                 * It postpones res_counter and refcount handling till the end
                 * of task migration(mem_cgroup_clear_mc()) for performance
-                * improvement. But we cannot postpone mem_cgroup_get(to)
-                * because if the process that has been moved to @to does
-                * swap-in, the refcount of @to might be decreased to 0.
+                * improvement. But we cannot postpone css_get(to)  because if
+                * the process that has been moved to @to does swap-in, the
+                * refcount of @to might be decreased to 0.
+                *
+                * We are in attach() phase, so the cgroup is guaranteed to be
+                * alive, so we can just call css_get().
                 */
-               mem_cgroup_get(to);
+               css_get(&to->css);
                return 0;
        }
        return -EINVAL;
@@ -5165,14 +5176,6 @@ static int memcg_update_kmem_limit(struct cgroup *cont, u64 val)
                 * starts accounting before all call sites are patched
                 */
                memcg_kmem_set_active(memcg);
-
-               /*
-                * kmem charges can outlive the cgroup. In the case of slab
-                * pages, for instance, a page contain objects from various
-                * processes, so it is unfeasible to migrate them away. We
-                * need to reference count the memcg because of that.
-                */
-               mem_cgroup_get(memcg);
        } else
                ret = res_counter_set_limit(&memcg->kmem, val);
 out:
@@ -5205,16 +5208,16 @@ static int memcg_propagate_kmem(struct mem_cgroup *memcg)
                goto out;
 
        /*
-        * destroy(), called if we fail, will issue static_key_slow_inc() and
-        * mem_cgroup_put() if kmem is enabled. We have to either call them
-        * unconditionally, or clear the KMEM_ACTIVE flag. I personally find
-        * this more consistent, since it always leads to the same destroy path
+        * __mem_cgroup_free() will issue static_key_slow_dec() because this
+        * memcg is active already. If the later initialization fails then the
+        * cgroup core triggers the cleanup so we do not have to do it here.
         */
-       mem_cgroup_get(memcg);
        static_key_slow_inc(&memcg_kmem_enabled_key);
 
        mutex_lock(&set_limit_mutex);
+       memcg_stop_kmem_account();
        ret = memcg_update_cache_sizes(memcg);
+       memcg_resume_kmem_account();
        mutex_unlock(&set_limit_mutex);
 out:
        return ret;
@@ -5893,23 +5896,43 @@ static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
        return mem_cgroup_sockets_init(memcg, ss);
 }
 
-static void kmem_cgroup_destroy(struct mem_cgroup *memcg)
+static void memcg_destroy_kmem(struct mem_cgroup *memcg)
 {
        mem_cgroup_sockets_destroy(memcg);
+}
+
+static void kmem_cgroup_css_offline(struct mem_cgroup *memcg)
+{
+       if (!memcg_kmem_is_active(memcg))
+               return;
+
+       /*
+        * kmem charges can outlive the cgroup. In the case of slab
+        * pages, for instance, a page contain objects from various
+        * processes. As we prevent from taking a reference for every
+        * such allocation we have to be careful when doing uncharge
+        * (see memcg_uncharge_kmem) and here during offlining.
+        *
+        * The idea is that that only the _last_ uncharge which sees
+        * the dead memcg will drop the last reference. An additional
+        * reference is taken here before the group is marked dead
+        * which is then paired with css_put during uncharge resp. here.
+        *
+        * Although this might sound strange as this path is called from
+        * css_offline() when the referencemight have dropped down to 0
+        * and shouldn't be incremented anymore (css_tryget would fail)
+        * we do not have other options because of the kmem allocations
+        * lifetime.
+        */
+       css_get(&memcg->css);
 
        memcg_kmem_mark_dead(memcg);
 
        if (res_counter_read_u64(&memcg->kmem, RES_USAGE) != 0)
                return;
 
-       /*
-        * Charges already down to 0, undo mem_cgroup_get() done in the charge
-        * path here, being careful not to race with memcg_uncharge_kmem: it is
-        * possible that the charges went down to 0 between mark_dead and the
-        * res_counter read, so in that case, we don't need the put
-        */
        if (memcg_kmem_test_and_clear_dead(memcg))
-               mem_cgroup_put(memcg);
+               css_put(&memcg->css);
 }
 #else
 static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
@@ -5917,7 +5940,11 @@ static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
        return 0;
 }
 
-static void kmem_cgroup_destroy(struct mem_cgroup *memcg)
+static void memcg_destroy_kmem(struct mem_cgroup *memcg)
+{
+}
+
+static void kmem_cgroup_css_offline(struct mem_cgroup *memcg)
 {
 }
 #endif
@@ -6087,13 +6114,13 @@ static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *memcg, int node)
                mz->on_tree = false;
                mz->memcg = memcg;
        }
-       memcg->info.nodeinfo[node] = pn;
+       memcg->nodeinfo[node] = pn;
        return 0;
 }
 
 static void free_mem_cgroup_per_zone_info(struct mem_cgroup *memcg, int node)
 {
-       kfree(memcg->info.nodeinfo[node]);
+       kfree(memcg->nodeinfo[node]);
 }
 
 static struct mem_cgroup *mem_cgroup_alloc(void)
@@ -6166,49 +6193,6 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg)
                vfree(memcg);
 }
 
-
-/*
- * Helpers for freeing a kmalloc()ed/vzalloc()ed mem_cgroup by RCU,
- * but in process context.  The work_freeing structure is overlaid
- * on the rcu_freeing structure, which itself is overlaid on memsw.
- */
-static void free_work(struct work_struct *work)
-{
-       struct mem_cgroup *memcg;
-
-       memcg = container_of(work, struct mem_cgroup, work_freeing);
-       __mem_cgroup_free(memcg);
-}
-
-static void free_rcu(struct rcu_head *rcu_head)
-{
-       struct mem_cgroup *memcg;
-
-       memcg = container_of(rcu_head, struct mem_cgroup, rcu_freeing);
-       INIT_WORK(&memcg->work_freeing, free_work);
-       schedule_work(&memcg->work_freeing);
-}
-
-static void mem_cgroup_get(struct mem_cgroup *memcg)
-{
-       atomic_inc(&memcg->refcnt);
-}
-
-static void __mem_cgroup_put(struct mem_cgroup *memcg, int count)
-{
-       if (atomic_sub_and_test(count, &memcg->refcnt)) {
-               struct mem_cgroup *parent = parent_mem_cgroup(memcg);
-               call_rcu(&memcg->rcu_freeing, free_rcu);
-               if (parent)
-                       mem_cgroup_put(parent);
-       }
-}
-
-static void mem_cgroup_put(struct mem_cgroup *memcg)
-{
-       __mem_cgroup_put(memcg, 1);
-}
-
 /*
  * Returns the parent mem_cgroup in memcgroup hierarchy with hierarchy enabled.
  */
@@ -6268,7 +6252,6 @@ mem_cgroup_css_alloc(struct cgroup *cont)
 
        memcg->last_scanned_node = MAX_NUMNODES;
        INIT_LIST_HEAD(&memcg->oom_notify);
-       atomic_set(&memcg->refcnt, 1);
        memcg->move_charge_at_immigrate = 0;
        mutex_init(&memcg->thresholds_lock);
        spin_lock_init(&memcg->move_lock);
@@ -6304,12 +6287,9 @@ mem_cgroup_css_online(struct cgroup *cont)
                res_counter_init(&memcg->kmem, &parent->kmem);
 
                /*
-                * We increment refcnt of the parent to ensure that we can
-                * safely access it on res_counter_charge/uncharge.
-                * This refcnt will be decremented when freeing this
-                * mem_cgroup(see mem_cgroup_put).
+                * No need to take a reference to the parent because cgroup
+                * core guarantees its existence.
                 */
-               mem_cgroup_get(parent);
        } else {
                res_counter_init(&memcg->res, NULL);
                res_counter_init(&memcg->memsw, NULL);
@@ -6325,16 +6305,6 @@ mem_cgroup_css_online(struct cgroup *cont)
 
        error = memcg_init_kmem(memcg, &mem_cgroup_subsys);
        mutex_unlock(&memcg_create_mutex);
-       if (error) {
-               /*
-                * We call put now because our (and parent's) refcnts
-                * are already in place. mem_cgroup_put() will internally
-                * call __mem_cgroup_free, so return directly
-                */
-               mem_cgroup_put(memcg);
-               if (parent->use_hierarchy)
-                       mem_cgroup_put(parent);
-       }
        return error;
 }
 
@@ -6360,6 +6330,8 @@ static void mem_cgroup_css_offline(struct cgroup *cont)
 {
        struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
 
+       kmem_cgroup_css_offline(memcg);
+
        mem_cgroup_invalidate_reclaim_iterators(memcg);
        mem_cgroup_reparent_charges(memcg);
        mem_cgroup_destroy_all_caches(memcg);
@@ -6369,9 +6341,8 @@ static void mem_cgroup_css_free(struct cgroup *cont)
 {
        struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
 
-       kmem_cgroup_destroy(memcg);
-
-       mem_cgroup_put(memcg);
+       memcg_destroy_kmem(memcg);
+       __mem_cgroup_free(memcg);
 }
 
 #ifdef CONFIG_MMU
@@ -6680,6 +6651,7 @@ static void __mem_cgroup_clear_mc(void)
 {
        struct mem_cgroup *from = mc.from;
        struct mem_cgroup *to = mc.to;
+       int i;
 
        /* we must uncharge all the leftover precharges from mc.to */
        if (mc.precharge) {
@@ -6700,7 +6672,9 @@ static void __mem_cgroup_clear_mc(void)
                if (!mem_cgroup_is_root(mc.from))
                        res_counter_uncharge(&mc.from->memsw,
                                                PAGE_SIZE * mc.moved_swap);
-               __mem_cgroup_put(mc.from, mc.moved_swap);
+
+               for (i = 0; i < mc.moved_swap; i++)
+                       css_put(&mc.from->css);
 
                if (!mem_cgroup_is_root(mc.to)) {
                        /*
@@ -6710,7 +6684,7 @@ static void __mem_cgroup_clear_mc(void)
                        res_counter_uncharge(&mc.to->res,
                                                PAGE_SIZE * mc.moved_swap);
                }
-               /* we've already done mem_cgroup_get(mc.to) */
+               /* we've already done css_get(mc.to) */
                mc.moved_swap = 0;
        }
        memcg_oom_recover(from);
index b68812d..1ce2e2a 100644 (file)
@@ -1150,7 +1150,7 @@ again:
                                if (pte_dirty(ptent))
                                        set_page_dirty(page);
                                if (pte_young(ptent) &&
-                                   likely(!VM_SequentialReadHint(vma)))
+                                   likely(!(vma->vm_flags & VM_SEQ_READ)))
                                        mark_page_accessed(page);
                                rss[MM_FILEPAGES]--;
                        }
index f5ba127..ca1dd3a 100644 (file)
@@ -208,13 +208,13 @@ void register_page_bootmem_info_node(struct pglist_data *pgdat)
        pfn = pgdat->node_start_pfn;
        end_pfn = pgdat_end_pfn(pgdat);
 
-       /* register_section info */
+       /* register section info */
        for (; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
                /*
                 * Some platforms can assign the same pfn to multiple nodes - on
                 * node0 as well as nodeN.  To avoid registering a pfn against
                 * multiple nodes we check that this pfn does not already
-                * reside in some other node.
+                * reside in some other nodes.
                 */
                if (pfn_valid(pfn) && (pfn_to_nid(pfn) == node))
                        register_page_bootmem_info_section(pfn);
@@ -914,19 +914,19 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
        if ((zone_idx(zone) > ZONE_NORMAL || online_type == ONLINE_MOVABLE) &&
            !can_online_high_movable(zone)) {
                unlock_memory_hotplug();
-               return -1;
+               return -EINVAL;
        }
 
        if (online_type == ONLINE_KERNEL && zone_idx(zone) == ZONE_MOVABLE) {
                if (move_pfn_range_left(zone - 1, zone, pfn, pfn + nr_pages)) {
                        unlock_memory_hotplug();
-                       return -1;
+                       return -EINVAL;
                }
        }
        if (online_type == ONLINE_MOVABLE && zone_idx(zone) == ZONE_MOVABLE - 1) {
                if (move_pfn_range_right(zone, zone + 1, pfn, pfn + nr_pages)) {
                        unlock_memory_hotplug();
-                       return -1;
+                       return -EINVAL;
                }
        }
 
index 8468ffd..f813111 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1358,18 +1358,19 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
 
        if (!(flags & MAP_ANONYMOUS)) {
                audit_mmap_fd(fd, flags);
-               if (unlikely(flags & MAP_HUGETLB))
-                       return -EINVAL;
                file = fget(fd);
                if (!file)
                        goto out;
                if (is_file_hugepages(file))
                        len = ALIGN(len, huge_page_size(hstate_file(file)));
+               retval = -EINVAL;
+               if (unlikely(flags & MAP_HUGETLB && !is_file_hugepages(file)))
+                       goto out_fput;
        } else if (flags & MAP_HUGETLB) {
                struct user_struct *user = NULL;
-               struct hstate *hs = hstate_sizelog((flags >> MAP_HUGE_SHIFT) &
-                                                  SHM_HUGE_MASK);
+               struct hstate *hs;
 
+               hs = hstate_sizelog((flags >> MAP_HUGE_SHIFT) & SHM_HUGE_MASK);
                if (!hs)
                        return -EINVAL;
 
@@ -1391,6 +1392,7 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 
        retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+out_fput:
        if (file)
                fput(file);
 out:
index 3708655..457d34e 100644 (file)
@@ -456,13 +456,14 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
        unsigned long charged = 0;
        bool locked = false;
 
-       down_write(&current->mm->mmap_sem);
-
        if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE))
-               goto out;
+               return ret;
+
+       if (flags & MREMAP_FIXED && !(flags & MREMAP_MAYMOVE))
+               return ret;
 
        if (addr & ~PAGE_MASK)
-               goto out;
+               return ret;
 
        old_len = PAGE_ALIGN(old_len);
        new_len = PAGE_ALIGN(new_len);
@@ -473,12 +474,13 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
         * a zero new-len is nonsensical.
         */
        if (!new_len)
-               goto out;
+               return ret;
+
+       down_write(&current->mm->mmap_sem);
 
        if (flags & MREMAP_FIXED) {
-               if (flags & MREMAP_MAYMOVE)
-                       ret = mremap_to(addr, old_len, new_addr, new_len,
-                                       &locked);
+               ret = mremap_to(addr, old_len, new_addr, new_len,
+                               &locked);
                goto out;
        }
 
index 327516b..b100255 100644 (file)
@@ -204,6 +204,7 @@ static char * const zone_names[MAX_NR_ZONES] = {
 };
 
 int min_free_kbytes = 1024;
+int user_min_free_kbytes;
 
 static unsigned long __meminitdata nr_kernel_pages;
 static unsigned long __meminitdata nr_all_pages;
@@ -1046,7 +1047,7 @@ __rmqueue_fallback(struct zone *zone, int order, int start_migratetype)
                         * MIGRATE_CMA areas.
                         */
                        if (!is_migrate_cma(migratetype) &&
-                           (unlikely(current_order >= pageblock_order / 2) ||
+                           (current_order >= pageblock_order / 2 ||
                             start_migratetype == MIGRATE_RECLAIMABLE ||
                             page_group_by_mobility_disabled)) {
                                int pages;
@@ -3153,12 +3154,10 @@ static void zoneref_set_zone(struct zone *zone, struct zoneref *zoneref)
  * Add all populated zones of a node to the zonelist.
  */
 static int build_zonelists_node(pg_data_t *pgdat, struct zonelist *zonelist,
-                               int nr_zones, enum zone_type zone_type)
+                               int nr_zones)
 {
        struct zone *zone;
-
-       BUG_ON(zone_type >= MAX_NR_ZONES);
-       zone_type++;
+       enum zone_type zone_type = MAX_NR_ZONES;
 
        do {
                zone_type--;
@@ -3168,8 +3167,8 @@ static int build_zonelists_node(pg_data_t *pgdat, struct zonelist *zonelist,
                                &zonelist->_zonerefs[nr_zones++]);
                        check_highest_zone(zone_type);
                }
-
        } while (zone_type);
+
        return nr_zones;
 }
 
@@ -3363,8 +3362,7 @@ static void build_zonelists_in_node_order(pg_data_t *pgdat, int node)
        zonelist = &pgdat->node_zonelists[0];
        for (j = 0; zonelist->_zonerefs[j].zone != NULL; j++)
                ;
-       j = build_zonelists_node(NODE_DATA(node), zonelist, j,
-                                                       MAX_NR_ZONES - 1);
+       j = build_zonelists_node(NODE_DATA(node), zonelist, j);
        zonelist->_zonerefs[j].zone = NULL;
        zonelist->_zonerefs[j].zone_idx = 0;
 }
@@ -3378,7 +3376,7 @@ static void build_thisnode_zonelists(pg_data_t *pgdat)
        struct zonelist *zonelist;
 
        zonelist = &pgdat->node_zonelists[1];
-       j = build_zonelists_node(pgdat, zonelist, 0, MAX_NR_ZONES - 1);
+       j = build_zonelists_node(pgdat, zonelist, 0);
        zonelist->_zonerefs[j].zone = NULL;
        zonelist->_zonerefs[j].zone_idx = 0;
 }
@@ -3586,7 +3584,7 @@ static void build_zonelists(pg_data_t *pgdat)
        local_node = pgdat->node_id;
 
        zonelist = &pgdat->node_zonelists[0];
-       j = build_zonelists_node(pgdat, zonelist, 0, MAX_NR_ZONES - 1);
+       j = build_zonelists_node(pgdat, zonelist, 0);
 
        /*
         * Now we build the zonelist so that it contains the zones
@@ -3599,14 +3597,12 @@ static void build_zonelists(pg_data_t *pgdat)
        for (node = local_node + 1; node < MAX_NUMNODES; node++) {
                if (!node_online(node))
                        continue;
-               j = build_zonelists_node(NODE_DATA(node), zonelist, j,
-                                                       MAX_NR_ZONES - 1);
+               j = build_zonelists_node(NODE_DATA(node), zonelist, j);
        }
        for (node = 0; node < local_node; node++) {
                if (!node_online(node))
                        continue;
-               j = build_zonelists_node(NODE_DATA(node), zonelist, j,
-                                                       MAX_NR_ZONES - 1);
+               j = build_zonelists_node(NODE_DATA(node), zonelist, j);
        }
 
        zonelist->_zonerefs[j].zone = NULL;
@@ -4421,13 +4417,13 @@ static void __meminit adjust_zone_range_for_zone_movable(int nid,
  */
 static unsigned long __meminit zone_spanned_pages_in_node(int nid,
                                        unsigned long zone_type,
+                                       unsigned long node_start_pfn,
+                                       unsigned long node_end_pfn,
                                        unsigned long *ignored)
 {
-       unsigned long node_start_pfn, node_end_pfn;
        unsigned long zone_start_pfn, zone_end_pfn;
 
-       /* Get the start and end of the node and zone */
-       get_pfn_range_for_nid(nid, &node_start_pfn, &node_end_pfn);
+       /* Get the start and end of the zone */
        zone_start_pfn = arch_zone_lowest_possible_pfn[zone_type];
        zone_end_pfn = arch_zone_highest_possible_pfn[zone_type];
        adjust_zone_range_for_zone_movable(nid, zone_type,
@@ -4482,14 +4478,14 @@ unsigned long __init absent_pages_in_range(unsigned long start_pfn,
 /* Return the number of page frames in holes in a zone on a node */
 static unsigned long __meminit zone_absent_pages_in_node(int nid,
                                        unsigned long zone_type,
+                                       unsigned long node_start_pfn,
+                                       unsigned long node_end_pfn,
                                        unsigned long *ignored)
 {
        unsigned long zone_low = arch_zone_lowest_possible_pfn[zone_type];
        unsigned long zone_high = arch_zone_highest_possible_pfn[zone_type];
-       unsigned long node_start_pfn, node_end_pfn;
        unsigned long zone_start_pfn, zone_end_pfn;
 
-       get_pfn_range_for_nid(nid, &node_start_pfn, &node_end_pfn);
        zone_start_pfn = clamp(node_start_pfn, zone_low, zone_high);
        zone_end_pfn = clamp(node_end_pfn, zone_low, zone_high);
 
@@ -4502,6 +4498,8 @@ static unsigned long __meminit zone_absent_pages_in_node(int nid,
 #else /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
 static inline unsigned long __meminit zone_spanned_pages_in_node(int nid,
                                        unsigned long zone_type,
+                                       unsigned long node_start_pfn,
+                                       unsigned long node_end_pfn,
                                        unsigned long *zones_size)
 {
        return zones_size[zone_type];
@@ -4509,6 +4507,8 @@ static inline unsigned long __meminit zone_spanned_pages_in_node(int nid,
 
 static inline unsigned long __meminit zone_absent_pages_in_node(int nid,
                                                unsigned long zone_type,
+                                               unsigned long node_start_pfn,
+                                               unsigned long node_end_pfn,
                                                unsigned long *zholes_size)
 {
        if (!zholes_size)
@@ -4520,21 +4520,27 @@ static inline unsigned long __meminit zone_absent_pages_in_node(int nid,
 #endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
 
 static void __meminit calculate_node_totalpages(struct pglist_data *pgdat,
-               unsigned long *zones_size, unsigned long *zholes_size)
+                                               unsigned long node_start_pfn,
+                                               unsigned long node_end_pfn,
+                                               unsigned long *zones_size,
+                                               unsigned long *zholes_size)
 {
        unsigned long realtotalpages, totalpages = 0;
        enum zone_type i;
 
        for (i = 0; i < MAX_NR_ZONES; i++)
                totalpages += zone_spanned_pages_in_node(pgdat->node_id, i,
-                                                               zones_size);
+                                                        node_start_pfn,
+                                                        node_end_pfn,
+                                                        zones_size);
        pgdat->node_spanned_pages = totalpages;
 
        realtotalpages = totalpages;
        for (i = 0; i < MAX_NR_ZONES; i++)
                realtotalpages -=
                        zone_absent_pages_in_node(pgdat->node_id, i,
-                                                               zholes_size);
+                                                 node_start_pfn, node_end_pfn,
+                                                 zholes_size);
        pgdat->node_present_pages = realtotalpages;
        printk(KERN_DEBUG "On node %d totalpages: %lu\n", pgdat->node_id,
                                                        realtotalpages);
@@ -4643,6 +4649,7 @@ static unsigned long __paginginit calc_memmap_size(unsigned long spanned_pages,
  * NOTE: pgdat should get zeroed by caller.
  */
 static void __paginginit free_area_init_core(struct pglist_data *pgdat,
+               unsigned long node_start_pfn, unsigned long node_end_pfn,
                unsigned long *zones_size, unsigned long *zholes_size)
 {
        enum zone_type j;
@@ -4664,8 +4671,11 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
                struct zone *zone = pgdat->node_zones + j;
                unsigned long size, realsize, freesize, memmap_pages;
 
-               size = zone_spanned_pages_in_node(nid, j, zones_size);
+               size = zone_spanned_pages_in_node(nid, j, node_start_pfn,
+                                                 node_end_pfn, zones_size);
                realsize = freesize = size - zone_absent_pages_in_node(nid, j,
+                                                               node_start_pfn,
+                                                               node_end_pfn,
                                                                zholes_size);
 
                /*
@@ -4779,6 +4789,8 @@ void __paginginit free_area_init_node(int nid, unsigned long *zones_size,
                unsigned long node_start_pfn, unsigned long *zholes_size)
 {
        pg_data_t *pgdat = NODE_DATA(nid);
+       unsigned long start_pfn = 0;
+       unsigned long end_pfn = 0;
 
        /* pg_data_t should be reset to zero when it's allocated */
        WARN_ON(pgdat->nr_zones || pgdat->classzone_idx);
@@ -4786,7 +4798,11 @@ void __paginginit free_area_init_node(int nid, unsigned long *zones_size,
        pgdat->node_id = nid;
        pgdat->node_start_pfn = node_start_pfn;
        init_zone_allows_reclaim(nid);
-       calculate_node_totalpages(pgdat, zones_size, zholes_size);
+#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
+       get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
+#endif
+       calculate_node_totalpages(pgdat, start_pfn, end_pfn,
+                                 zones_size, zholes_size);
 
        alloc_node_mem_map(pgdat);
 #ifdef CONFIG_FLAT_NODE_MEM_MAP
@@ -4795,7 +4811,8 @@ void __paginginit free_area_init_node(int nid, unsigned long *zones_size,
                (unsigned long)pgdat->node_mem_map);
 #endif
 
-       free_area_init_core(pgdat, zones_size, zholes_size);
+       free_area_init_core(pgdat, start_pfn, end_pfn,
+                           zones_size, zholes_size);
 }
 
 #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
@@ -5573,14 +5590,21 @@ static void __meminit setup_per_zone_inactive_ratio(void)
 int __meminit init_per_zone_wmark_min(void)
 {
        unsigned long lowmem_kbytes;
+       int new_min_free_kbytes;
 
        lowmem_kbytes = nr_free_buffer_pages() * (PAGE_SIZE >> 10);
-
-       min_free_kbytes = int_sqrt(lowmem_kbytes * 16);
-       if (min_free_kbytes < 128)
-               min_free_kbytes = 128;
-       if (min_free_kbytes > 65536)
-               min_free_kbytes = 65536;
+       new_min_free_kbytes = int_sqrt(lowmem_kbytes * 16);
+
+       if (new_min_free_kbytes > user_min_free_kbytes) {
+               min_free_kbytes = new_min_free_kbytes;
+               if (min_free_kbytes < 128)
+                       min_free_kbytes = 128;
+               if (min_free_kbytes > 65536)
+                       min_free_kbytes = 65536;
+       } else {
+               pr_warn("min_free_kbytes is not updated to %d because user defined value %d is preferred\n",
+                               new_min_free_kbytes, user_min_free_kbytes);
+       }
        setup_per_zone_wmarks();
        refresh_zone_stat_thresholds();
        setup_per_zone_lowmem_reserve();
@@ -5598,8 +5622,10 @@ int min_free_kbytes_sysctl_handler(ctl_table *table, int write,
        void __user *buffer, size_t *length, loff_t *ppos)
 {
        proc_dointvec(table, write, buffer, length, ppos);
-       if (write)
+       if (write) {
+               user_min_free_kbytes = min_free_kbytes;
                setup_per_zone_wmarks();
+       }
        return 0;
 }
 
index e22ceeb..cd356df 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -720,7 +720,7 @@ int page_referenced_one(struct page *page, struct vm_area_struct *vma,
                         * mapping is already gone, the unmap path will have
                         * set PG_referenced or activated the page.
                         */
-                       if (likely(!VM_SequentialReadHint(vma)))
+                       if (likely(!(vma->vm_flags & VM_SEQ_READ)))
                                referenced++;
                }
                pte_unmap_unlock(pte, ptl);
index b38400f..308d503 100644 (file)
@@ -753,6 +753,7 @@ out:
        return ret;
 }
 
+#ifdef CONFIG_MEMORY_HOTREMOVE
 #ifdef CONFIG_MEMORY_FAILURE
 static void clear_hwpoisoned_pages(struct page *memmap, int nr_pages)
 {
@@ -774,7 +775,6 @@ static inline void clear_hwpoisoned_pages(struct page *memmap, int nr_pages)
 }
 #endif
 
-#ifdef CONFIG_MEMORY_HOTREMOVE
 static void free_section_usemap(struct page *memmap, unsigned long *usemap)
 {
        struct page *usemap_page;
index 91a1047..13a5495 100644 (file)
@@ -388,12 +388,12 @@ nocache:
                addr = ALIGN(first->va_end, align);
                if (addr < vstart)
                        goto nocache;
-               if (addr + size - 1 < addr)
+               if (addr + size < addr)
                        goto overflow;
 
        } else {
                addr = ALIGN(vstart, align);
-               if (addr + size - 1 < addr)
+               if (addr + size < addr)
                        goto overflow;
 
                n = vmap_area_root.rb_node;
@@ -420,7 +420,7 @@ nocache:
                if (addr + cached_hole_size < first->va_start)
                        cached_hole_size = first->va_start - addr;
                addr = ALIGN(first->va_end, align);
-               if (addr + size - 1 < addr)
+               if (addr + size < addr)
                        goto overflow;
 
                if (list_is_last(&first->list, &vmap_area_list))
@@ -754,7 +754,6 @@ struct vmap_block {
        struct vmap_area *va;
        struct vmap_block_queue *vbq;
        unsigned long free, dirty;
-       DECLARE_BITMAP(alloc_map, VMAP_BBMAP_BITS);
        DECLARE_BITMAP(dirty_map, VMAP_BBMAP_BITS);
        struct list_head free_list;
        struct rcu_head rcu_head;
@@ -820,7 +819,6 @@ static struct vmap_block *new_vmap_block(gfp_t gfp_mask)
        vb->va = va;
        vb->free = VMAP_BBMAP_BITS;
        vb->dirty = 0;
-       bitmap_zero(vb->alloc_map, VMAP_BBMAP_BITS);
        bitmap_zero(vb->dirty_map, VMAP_BBMAP_BITS);
        INIT_LIST_HEAD(&vb->free_list);
 
@@ -873,7 +871,6 @@ static void purge_fragmented_blocks(int cpu)
                if (vb->free + vb->dirty == VMAP_BBMAP_BITS && vb->dirty != VMAP_BBMAP_BITS) {
                        vb->free = 0; /* prevent further allocs after releasing lock */
                        vb->dirty = VMAP_BBMAP_BITS; /* prevent purging it again */
-                       bitmap_fill(vb->alloc_map, VMAP_BBMAP_BITS);
                        bitmap_fill(vb->dirty_map, VMAP_BBMAP_BITS);
                        spin_lock(&vbq->lock);
                        list_del_rcu(&vb->free_list);
@@ -891,11 +888,6 @@ static void purge_fragmented_blocks(int cpu)
        }
 }
 
-static void purge_fragmented_blocks_thiscpu(void)
-{
-       purge_fragmented_blocks(smp_processor_id());
-}
-
 static void purge_fragmented_blocks_allcpus(void)
 {
        int cpu;
@@ -910,7 +902,6 @@ static void *vb_alloc(unsigned long size, gfp_t gfp_mask)
        struct vmap_block *vb;
        unsigned long addr = 0;
        unsigned int order;
-       int purge = 0;
 
        BUG_ON(size & ~PAGE_MASK);
        BUG_ON(size > PAGE_SIZE*VMAP_MAX_ALLOC);
@@ -934,17 +925,7 @@ again:
                if (vb->free < 1UL << order)
                        goto next;
 
-               i = bitmap_find_free_region(vb->alloc_map,
-                                               VMAP_BBMAP_BITS, order);
-
-               if (i < 0) {
-                       if (vb->free + vb->dirty == VMAP_BBMAP_BITS) {
-                               /* fragmented and no outstanding allocations */
-                               BUG_ON(vb->dirty != VMAP_BBMAP_BITS);
-                               purge = 1;
-                       }
-                       goto next;
-               }
+               i = VMAP_BBMAP_BITS - vb->free;
                addr = vb->va->va_start + (i << PAGE_SHIFT);
                BUG_ON(addr_to_vb_idx(addr) !=
                                addr_to_vb_idx(vb->va->va_start));
@@ -960,9 +941,6 @@ next:
                spin_unlock(&vb->lock);
        }
 
-       if (purge)
-               purge_fragmented_blocks_thiscpu();
-
        put_cpu_var(vmap_block_queue);
        rcu_read_unlock();
 
@@ -1311,15 +1289,15 @@ static void setup_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,
        spin_unlock(&vmap_area_lock);
 }
 
-static void clear_vm_unlist(struct vm_struct *vm)
+static void clear_vm_uninitialized_flag(struct vm_struct *vm)
 {
        /*
-        * Before removing VM_UNLIST,
+        * Before removing VM_UNINITIALIZED,
         * we should make sure that vm has proper values.
         * Pair with smp_rmb() in show_numa_info().
         */
        smp_wmb();
-       vm->flags &= ~VM_UNLIST;
+       vm->flags &= ~VM_UNINITIALIZED;
 }
 
 static struct vm_struct *__get_vm_area_node(unsigned long size,
@@ -1453,7 +1431,7 @@ static void __vunmap(const void *addr, int deallocate_pages)
                return;
 
        if (WARN(!PAGE_ALIGNED(addr), "Trying to vfree() bad address (%p)\n",
-                       addr));
+                       addr))
                return;
 
        area = remove_vm_area(addr);
@@ -1499,7 +1477,6 @@ static void __vunmap(const void *addr, int deallocate_pages)
  *     conventions for vfree() arch-depenedent would be a really bad idea)
  *
  *     NOTE: assumes that the object at *addr has a size >= sizeof(llist_node)
- *     
  */
 void vfree(const void *addr)
 {
@@ -1511,8 +1488,8 @@ void vfree(const void *addr)
                return;
        if (unlikely(in_interrupt())) {
                struct vfree_deferred *p = &__get_cpu_var(vfree_deferred);
-               llist_add((struct llist_node *)addr, &p->list);
-               schedule_work(&p->wq);
+               if (llist_add((struct llist_node *)addr, &p->list))
+                       schedule_work(&p->wq);
        } else
                __vunmap(addr, 1);
 }
@@ -1657,21 +1634,21 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align,
        if (!size || (size >> PAGE_SHIFT) > totalram_pages)
                goto fail;
 
-       area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNLIST,
+       area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNINITIALIZED,
                                  start, end, node, gfp_mask, caller);
        if (!area)
                goto fail;
 
        addr = __vmalloc_area_node(area, gfp_mask, prot, node, caller);
        if (!addr)
-               return NULL;
+               goto fail;
 
        /*
-        * In this function, newly allocated vm_struct has VM_UNLIST flag.
-        * It means that vm_struct is not fully initialized.
+        * In this function, newly allocated vm_struct has VM_UNINITIALIZED
+        * flag. It means that vm_struct is not fully initialized.
         * Now, it is fully initialized, so remove this flag here.
         */
-       clear_vm_unlist(area);
+       clear_vm_uninitialized_flag(area);
 
        /*
         * A ref_count = 3 is needed because the vm_struct and vmap_area
@@ -2591,11 +2568,6 @@ static void show_numa_info(struct seq_file *m, struct vm_struct *v)
                if (!counters)
                        return;
 
-               /* Pair with smp_wmb() in clear_vm_unlist() */
-               smp_rmb();
-               if (v->flags & VM_UNLIST)
-                       return;
-
                memset(counters, 0, nr_node_ids * sizeof(unsigned int));
 
                for (nr = 0; nr < v->nr_pages; nr++)
@@ -2624,6 +2596,11 @@ static int s_show(struct seq_file *m, void *p)
 
        v = va->vm;
 
+       /* Pair with smp_wmb() in clear_vm_uninitialized_flag() */
+       smp_rmb();
+       if (v->flags & VM_UNINITIALIZED)
+               return 0;
+
        seq_printf(m, "0x%pK-0x%pK %7ld",
                v->addr, v->addr + v->size, v->size);
 
index 99b3ac7..2cff0d4 100644 (file)
@@ -1443,25 +1443,11 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec,
         * as there is no guarantee the dirtying process is throttled in the
         * same way balance_dirty_pages() manages.
         *
-        * This scales the number of dirty pages that must be under writeback
-        * before a zone gets flagged ZONE_WRITEBACK. It is a simple backoff
-        * function that has the most effect in the range DEF_PRIORITY to
-        * DEF_PRIORITY-2 which is the priority reclaim is considered to be
-        * in trouble and reclaim is considered to be in trouble.
-        *
-        * DEF_PRIORITY   100% isolated pages must be PageWriteback to throttle
-        * DEF_PRIORITY-1  50% must be PageWriteback
-        * DEF_PRIORITY-2  25% must be PageWriteback, kswapd in trouble
-        * ...
-        * DEF_PRIORITY-6 For SWAP_CLUSTER_MAX isolated pages, throttle if any
-        *                     isolated page is PageWriteback
-        *
         * Once a zone is flagged ZONE_WRITEBACK, kswapd will count the number
         * of pages under pages flagged for immediate reclaim and stall if any
         * are encountered in the nr_immediate check below.
         */
-       if (nr_writeback && nr_writeback >=
-                       (nr_taken >> (DEF_PRIORITY - sc->priority)))
+       if (nr_writeback && nr_writeback == nr_taken)
                zone_set_flag(zone, ZONE_WRITEBACK);
 
        /*
@@ -2361,8 +2347,10 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
                aborted_reclaim = shrink_zones(zonelist, sc);
 
                /*
-                * Don't shrink slabs when reclaiming memory from
-                * over limit cgroups
+                * Don't shrink slabs when reclaiming memory from over limit
+                * cgroups but do shrink slab at least once when aborting
+                * reclaim for compaction to avoid unevenly scanning file/anon
+                * LRU pages over slab pages.
                 */
                if (global_reclaim(sc)) {
                        unsigned long lru_pages = 0;
@@ -2404,7 +2392,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
                                                WB_REASON_TRY_TO_FREE_PAGES);
                        sc->may_writepage = 1;
                }
-       } while (--sc->priority >= 0);
+       } while (--sc->priority >= 0 && !aborted_reclaim);
 
 out:
        delayacct_freepages_end();
index 9424f37..2fb2d88 100644 (file)
@@ -341,7 +341,7 @@ static void __vlan_device_event(struct net_device *dev, unsigned long event)
 static int vlan_device_event(struct notifier_block *unused, unsigned long event,
                             void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct vlan_group *grp;
        struct vlan_info *vlan_info;
        int i, flgs;
index addc116..01f1779 100644 (file)
@@ -127,7 +127,7 @@ static int parse_opts(char *opts, struct p9_client *clnt)
        char *s;
        int ret = 0;
 
-       clnt->proto_version = p9_proto_2000u;
+       clnt->proto_version = p9_proto_2000L;
        clnt->msize = 8192;
 
        if (!opts)
@@ -995,6 +995,9 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
        if (err < 0)
                goto destroy_tagpool;
 
+       if (!clnt->trans_mod)
+               clnt->trans_mod = v9fs_get_trans_by_name("virtio");
+
        if (!clnt->trans_mod)
                clnt->trans_mod = v9fs_get_default_trans();
 
index 6dfe1c6..3770249 100644 (file)
@@ -219,6 +219,7 @@ source "net/batman-adv/Kconfig"
 source "net/openvswitch/Kconfig"
 source "net/vmw_vsock/Kconfig"
 source "net/netlink/Kconfig"
+source "net/mpls/Kconfig"
 
 config RPS
        boolean
@@ -243,6 +244,10 @@ config NETPRIO_CGROUP
          Cgroup subsystem for use in assigning processes to network priorities on
          a per-interface basis
 
+config NET_LL_RX_POLL
+       boolean
+       default y
+
 config BQL
        boolean
        depends on SYSFS
@@ -260,6 +265,18 @@ config BPF_JIT
          packet sniffing (libpcap/tcpdump). Note : Admin should enable
          this feature changing /proc/sys/net/core/bpf_jit_enable
 
+config NET_FLOW_LIMIT
+       boolean
+       depends on RPS
+       default y
+       ---help---
+         The network stack has to drop packets when a receive processing CPU's
+         backlog reaches netdev_max_backlog. If a few out of many active flows
+         generate the vast majority of load, drop their traffic earlier to
+         maintain capacity for the other flows. This feature provides servers
+         with many clients some protection against DoS by a single (spoofed)
+         flow that greatly exceeds average workload.
+
 menu "Network testing"
 
 config NET_PKTGEN
index 091e7b0..9492e8c 100644 (file)
@@ -70,3 +70,4 @@ obj-$(CONFIG_BATMAN_ADV)      += batman-adv/
 obj-$(CONFIG_NFC)              += nfc/
 obj-$(CONFIG_OPENVSWITCH)      += openvswitch/
 obj-$(CONFIG_VSOCKETS) += vmw_vsock/
+obj-$(CONFIG_NET_MPLS_GSO)     += mpls/
index 173a2e8..690356f 100644 (file)
@@ -332,7 +332,7 @@ static void aarp_expire_timeout(unsigned long unused)
 static int aarp_device_event(struct notifier_block *this, unsigned long event,
                             void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        int ct;
 
        if (!net_eq(dev_net(dev), &init_net))
index ef12839..7fee50d 100644 (file)
@@ -644,7 +644,7 @@ static inline void atalk_dev_down(struct net_device *dev)
 static int ddp_device_event(struct notifier_block *this, unsigned long event,
                            void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
index 8ae3a78..8215f7c 100644 (file)
@@ -539,9 +539,9 @@ static int clip_create(int number)
 }
 
 static int clip_device_event(struct notifier_block *this, unsigned long event,
-                            void *arg)
+                            void *ptr)
 {
-       struct net_device *dev = arg;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
@@ -575,6 +575,7 @@ static int clip_inet_event(struct notifier_block *this, unsigned long event,
                           void *ifa)
 {
        struct in_device *in_dev;
+       struct netdev_notifier_info info;
 
        in_dev = ((struct in_ifaddr *)ifa)->ifa_dev;
        /*
@@ -583,7 +584,8 @@ static int clip_inet_event(struct notifier_block *this, unsigned long event,
         */
        if (event != NETDEV_UP)
                return NOTIFY_DONE;
-       return clip_device_event(this, NETDEV_CHANGE, in_dev->dev);
+       netdev_notifier_info_init(&info, in_dev->dev);
+       return clip_device_event(this, NETDEV_CHANGE, &info);
 }
 
 static struct notifier_block clip_dev_notifier = {
index d4cc1be..3af1275 100644 (file)
@@ -998,14 +998,12 @@ int msg_to_mpoad(struct k_message *mesg, struct mpoa_client *mpc)
 }
 
 static int mpoa_event_listener(struct notifier_block *mpoa_notifier,
-                              unsigned long event, void *dev_ptr)
+                              unsigned long event, void *ptr)
 {
-       struct net_device *dev;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct mpoa_client *mpc;
        struct lec_priv *priv;
 
-       dev = dev_ptr;
-
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
 
index e277e38..4b4d2b7 100644 (file)
@@ -111,9 +111,9 @@ again:
  *     Handle device status changes.
  */
 static int ax25_device_event(struct notifier_block *this, unsigned long event,
-       void *ptr)
+                            void *ptr)
 {
-       struct net_device *dev = (struct net_device *)ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
@@ -1974,7 +1974,7 @@ static struct packet_type ax25_packet_type __read_mostly = {
 };
 
 static struct notifier_block ax25_dev_notifier = {
-       .notifier_call =ax25_device_event,
+       .notifier_call = ax25_device_event,
 };
 
 static int __init ax25_init(void)
index d5744b7..919a5ce 100644 (file)
@@ -29,7 +29,7 @@ static int min_proto[1],              max_proto[] = { AX25_PROTO_MAX };
 static int min_ds_timeout[1],          max_ds_timeout[] = {65535000};
 #endif
 
-static const ctl_table ax25_param_table[] = {
+static const struct ctl_table ax25_param_table[] = {
        {
                .procname       = "ip_default_mode",
                .maxlen         = sizeof(int),
index acbac2a..489bb36 100644 (file)
@@ -32,7 +32,6 @@ batman-adv-y += icmp_socket.o
 batman-adv-y += main.o
 batman-adv-$(CONFIG_BATMAN_ADV_NC) += network-coding.o
 batman-adv-y += originator.o
-batman-adv-y += ring_buffer.o
 batman-adv-y += routing.o
 batman-adv-y += send.o
 batman-adv-y += soft-interface.o
index f680ee1..62da527 100644 (file)
@@ -19,7 +19,6 @@
 
 #include "main.h"
 #include "translation-table.h"
-#include "ring_buffer.h"
 #include "originator.h"
 #include "routing.h"
 #include "gateway_common.h"
 #include "network-coding.h"
 
 /**
+ * batadv_ring_buffer_set - update the ring buffer with the given value
+ * @lq_recv: pointer to the ring buffer
+ * @lq_index: index to store the value at
+ * @value: value to store in the ring buffer
+ */
+static void batadv_ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index,
+                                  uint8_t value)
+{
+       lq_recv[*lq_index] = value;
+       *lq_index = (*lq_index + 1) % BATADV_TQ_GLOBAL_WINDOW_SIZE;
+}
+
+/**
+ * batadv_ring_buffer_set - compute the average of all non-zero values stored
+ * in the given ring buffer
+ * @lq_recv: pointer to the ring buffer
+ *
+ * Returns computed average value.
+ */
+static uint8_t batadv_ring_buffer_avg(const uint8_t lq_recv[])
+{
+       const uint8_t *ptr;
+       uint16_t count = 0, i = 0, sum = 0;
+
+       ptr = lq_recv;
+
+       while (i < BATADV_TQ_GLOBAL_WINDOW_SIZE) {
+               if (*ptr != 0) {
+                       count++;
+                       sum += *ptr;
+               }
+
+               i++;
+               ptr++;
+       }
+
+       if (count == 0)
+               return 0;
+
+       return (uint8_t)(sum / count);
+}
+
+/*
  * batadv_dup_status - duplicate status
  * @BATADV_NO_DUP: the packet is a duplicate
  * @BATADV_ORIG_DUP: OGM is a duplicate in the originator (but not for the
@@ -48,12 +90,11 @@ static struct batadv_neigh_node *
 batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
                        const uint8_t *neigh_addr,
                        struct batadv_orig_node *orig_node,
-                       struct batadv_orig_node *orig_neigh, __be32 seqno)
+                       struct batadv_orig_node *orig_neigh)
 {
        struct batadv_neigh_node *neigh_node;
 
-       neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr,
-                                          ntohl(seqno));
+       neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr);
        if (!neigh_node)
                goto out;
 
@@ -428,18 +469,16 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
        else
                skb_size = packet_len;
 
-       skb_size += ETH_HLEN + NET_IP_ALIGN;
+       skb_size += ETH_HLEN;
 
-       forw_packet_aggr->skb = dev_alloc_skb(skb_size);
+       forw_packet_aggr->skb = netdev_alloc_skb_ip_align(NULL, skb_size);
        if (!forw_packet_aggr->skb) {
                if (!own_packet)
                        atomic_inc(&bat_priv->batman_queue_left);
                kfree(forw_packet_aggr);
                goto out;
        }
-       skb_reserve(forw_packet_aggr->skb, ETH_HLEN + NET_IP_ALIGN);
-
-       INIT_HLIST_NODE(&forw_packet_aggr->list);
+       skb_reserve(forw_packet_aggr->skb, ETH_HLEN);
 
        skb_buff = skb_put(forw_packet_aggr->skb, packet_len);
        forw_packet_aggr->packet_len = packet_len;
@@ -605,6 +644,41 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
                                if_incoming, 0, batadv_iv_ogm_fwd_send_time());
 }
 
+/**
+ * batadv_iv_ogm_slide_own_bcast_window - bitshift own OGM broadcast windows for
+ * the given interface
+ * @hard_iface: the interface for which the windows have to be shifted
+ */
+static void
+batadv_iv_ogm_slide_own_bcast_window(struct batadv_hard_iface *hard_iface)
+{
+       struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+       struct batadv_hashtable *hash = bat_priv->orig_hash;
+       struct hlist_head *head;
+       struct batadv_orig_node *orig_node;
+       unsigned long *word;
+       uint32_t i;
+       size_t word_index;
+       uint8_t *w;
+
+       for (i = 0; i < hash->size; i++) {
+               head = &hash->table[i];
+
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
+                       spin_lock_bh(&orig_node->ogm_cnt_lock);
+                       word_index = hard_iface->if_num * BATADV_NUM_WORDS;
+                       word = &(orig_node->bcast_own[word_index]);
+
+                       batadv_bit_get_packet(bat_priv, word, 1, 0);
+                       w = &orig_node->bcast_own_sum[hard_iface->if_num];
+                       *w = bitmap_weight(word, BATADV_TQ_LOCAL_WINDOW_SIZE);
+                       spin_unlock_bh(&orig_node->ogm_cnt_lock);
+               }
+               rcu_read_unlock();
+       }
+}
+
 static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
 {
        struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
@@ -649,7 +723,7 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
                batadv_ogm_packet->gw_flags = BATADV_NO_FLAGS;
        }
 
-       batadv_slide_own_bcast_window(hard_iface);
+       batadv_iv_ogm_slide_own_bcast_window(hard_iface);
        batadv_iv_ogm_queue_add(bat_priv, hard_iface->bat_iv.ogm_buff,
                                hard_iface->bat_iv.ogm_buff_len, hard_iface, 1,
                                batadv_iv_ogm_emit_send_time(bat_priv));
@@ -685,7 +759,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
                if (batadv_compare_eth(neigh_addr, ethhdr->h_source) &&
                    tmp_neigh_node->if_incoming == if_incoming &&
                    atomic_inc_not_zero(&tmp_neigh_node->refcount)) {
-                       if (neigh_node)
+                       if (WARN(neigh_node, "too many matching neigh_nodes"))
                                batadv_neigh_node_free_ref(neigh_node);
                        neigh_node = tmp_neigh_node;
                        continue;
@@ -711,8 +785,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
 
                neigh_node = batadv_iv_ogm_neigh_new(if_incoming,
                                                     ethhdr->h_source,
-                                                    orig_node, orig_tmp,
-                                                    batadv_ogm_packet->seqno);
+                                                    orig_node, orig_tmp);
 
                batadv_orig_node_free_ref(orig_tmp);
                if (!neigh_node)
@@ -844,8 +917,7 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
                neigh_node = batadv_iv_ogm_neigh_new(if_incoming,
                                                     orig_neigh_node->orig,
                                                     orig_neigh_node,
-                                                    orig_neigh_node,
-                                                    batadv_ogm_packet->seqno);
+                                                    orig_neigh_node);
 
        if (!neigh_node)
                goto out;
@@ -1013,7 +1085,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
        struct batadv_neigh_node *orig_neigh_router = NULL;
        int has_directlink_flag;
        int is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0;
-       int is_broadcast = 0, is_bidirect;
+       int is_bidirect;
        bool is_single_hop_neigh = false;
        bool is_from_best_next_hop = false;
        int sameseq, similar_ttl;
@@ -1077,19 +1149,9 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
                if (batadv_compare_eth(batadv_ogm_packet->prev_sender,
                                       hard_iface->net_dev->dev_addr))
                        is_my_oldorig = 1;
-
-               if (is_broadcast_ether_addr(ethhdr->h_source))
-                       is_broadcast = 1;
        }
        rcu_read_unlock();
 
-       if (batadv_ogm_packet->header.version != BATADV_COMPAT_VERSION) {
-               batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                          "Drop packet: incompatible batman version (%i)\n",
-                          batadv_ogm_packet->header.version);
-               return;
-       }
-
        if (is_my_addr) {
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                           "Drop packet: received my own broadcast (sender: %pM)\n",
@@ -1097,13 +1159,6 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
                return;
        }
 
-       if (is_broadcast) {
-               batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                          "Drop packet: ignoring all packets with broadcast source addr (sender: %pM)\n",
-                          ethhdr->h_source);
-               return;
-       }
-
        if (is_my_orig) {
                unsigned long *word;
                int offset;
@@ -1312,7 +1367,7 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
                           skb->len + ETH_HLEN);
 
        packet_len = skb_headlen(skb);
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
        packet_buff = skb->data;
        batadv_ogm_packet = (struct batadv_ogm_packet *)packet_buff;
 
index de27b31..e14531f 100644 (file)
@@ -180,7 +180,7 @@ static struct batadv_bla_claim
  */
 static struct batadv_bla_backbone_gw *
 batadv_backbone_hash_find(struct batadv_priv *bat_priv,
-                         uint8_t *addr, short vid)
+                         uint8_t *addr, unsigned short vid)
 {
        struct batadv_hashtable *hash = bat_priv->bla.backbone_hash;
        struct hlist_head *head;
@@ -257,7 +257,7 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw)
  * @claimtype: the type of the claim (CLAIM, UNCLAIM, ANNOUNCE, ...)
  */
 static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,
-                                 short vid, int claimtype)
+                                 unsigned short vid, int claimtype)
 {
        struct sk_buff *skb;
        struct ethhdr *ethhdr;
@@ -307,7 +307,8 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,
                 */
                memcpy(ethhdr->h_source, mac, ETH_ALEN);
                batadv_dbg(BATADV_DBG_BLA, bat_priv,
-                          "bla_send_claim(): CLAIM %pM on vid %d\n", mac, vid);
+                          "bla_send_claim(): CLAIM %pM on vid %d\n", mac,
+                          BATADV_PRINT_VID(vid));
                break;
        case BATADV_CLAIM_TYPE_UNCLAIM:
                /* unclaim frame
@@ -316,7 +317,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,
                memcpy(hw_src, mac, ETH_ALEN);
                batadv_dbg(BATADV_DBG_BLA, bat_priv,
                           "bla_send_claim(): UNCLAIM %pM on vid %d\n", mac,
-                          vid);
+                          BATADV_PRINT_VID(vid));
                break;
        case BATADV_CLAIM_TYPE_ANNOUNCE:
                /* announcement frame
@@ -325,7 +326,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,
                memcpy(hw_src, mac, ETH_ALEN);
                batadv_dbg(BATADV_DBG_BLA, bat_priv,
                           "bla_send_claim(): ANNOUNCE of %pM on vid %d\n",
-                          ethhdr->h_source, vid);
+                          ethhdr->h_source, BATADV_PRINT_VID(vid));
                break;
        case BATADV_CLAIM_TYPE_REQUEST:
                /* request frame
@@ -335,13 +336,15 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,
                memcpy(hw_src, mac, ETH_ALEN);
                memcpy(ethhdr->h_dest, mac, ETH_ALEN);
                batadv_dbg(BATADV_DBG_BLA, bat_priv,
-                          "bla_send_claim(): REQUEST of %pM to %pMon vid %d\n",
-                          ethhdr->h_source, ethhdr->h_dest, vid);
+                          "bla_send_claim(): REQUEST of %pM to %pM on vid %d\n",
+                          ethhdr->h_source, ethhdr->h_dest,
+                          BATADV_PRINT_VID(vid));
                break;
        }
 
-       if (vid != -1)
-               skb = vlan_insert_tag(skb, htons(ETH_P_8021Q), vid);
+       if (vid & BATADV_VLAN_HAS_TAG)
+               skb = vlan_insert_tag(skb, htons(ETH_P_8021Q),
+                                     vid & VLAN_VID_MASK);
 
        skb_reset_mac_header(skb);
        skb->protocol = eth_type_trans(skb, soft_iface);
@@ -367,7 +370,7 @@ out:
  */
 static struct batadv_bla_backbone_gw *
 batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig,
-                          short vid, bool own_backbone)
+                          unsigned short vid, bool own_backbone)
 {
        struct batadv_bla_backbone_gw *entry;
        struct batadv_orig_node *orig_node;
@@ -380,7 +383,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig,
 
        batadv_dbg(BATADV_DBG_BLA, bat_priv,
                   "bla_get_backbone_gw(): not found (%pM, %d), creating new entry\n",
-                  orig, vid);
+                  orig, BATADV_PRINT_VID(vid));
 
        entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
        if (!entry)
@@ -434,7 +437,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig,
 static void
 batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,
                                  struct batadv_hard_iface *primary_if,
-                                 short vid)
+                                 unsigned short vid)
 {
        struct batadv_bla_backbone_gw *backbone_gw;
 
@@ -456,7 +459,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,
  */
 static void batadv_bla_answer_request(struct batadv_priv *bat_priv,
                                      struct batadv_hard_iface *primary_if,
-                                     short vid)
+                                     unsigned short vid)
 {
        struct hlist_head *head;
        struct batadv_hashtable *hash;
@@ -547,7 +550,7 @@ static void batadv_bla_send_announce(struct batadv_priv *bat_priv,
  * @backbone_gw: the backbone gateway which claims it
  */
 static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
-                                const uint8_t *mac, const short vid,
+                                const uint8_t *mac, const unsigned short vid,
                                 struct batadv_bla_backbone_gw *backbone_gw)
 {
        struct batadv_bla_claim *claim;
@@ -572,7 +575,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
                atomic_set(&claim->refcount, 2);
                batadv_dbg(BATADV_DBG_BLA, bat_priv,
                           "bla_add_claim(): adding new entry %pM, vid %d to hash ...\n",
-                          mac, vid);
+                          mac, BATADV_PRINT_VID(vid));
                hash_added = batadv_hash_add(bat_priv->bla.claim_hash,
                                             batadv_compare_claim,
                                             batadv_choose_claim, claim,
@@ -591,7 +594,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
 
                batadv_dbg(BATADV_DBG_BLA, bat_priv,
                           "bla_add_claim(): changing ownership for %pM, vid %d\n",
-                          mac, vid);
+                          mac, BATADV_PRINT_VID(vid));
 
                claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
                batadv_backbone_gw_free_ref(claim->backbone_gw);
@@ -611,7 +614,7 @@ claim_free_ref:
  * given mac address and vid.
  */
 static void batadv_bla_del_claim(struct batadv_priv *bat_priv,
-                                const uint8_t *mac, const short vid)
+                                const uint8_t *mac, const unsigned short vid)
 {
        struct batadv_bla_claim search_claim, *claim;
 
@@ -622,7 +625,7 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv,
                return;
 
        batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_del_claim(): %pM, vid %d\n",
-                  mac, vid);
+                  mac, BATADV_PRINT_VID(vid));
 
        batadv_hash_remove(bat_priv->bla.claim_hash, batadv_compare_claim,
                           batadv_choose_claim, claim);
@@ -637,7 +640,7 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv,
 /* check for ANNOUNCE frame, return 1 if handled */
 static int batadv_handle_announce(struct batadv_priv *bat_priv,
                                  uint8_t *an_addr, uint8_t *backbone_addr,
-                                 short vid)
+                                 unsigned short vid)
 {
        struct batadv_bla_backbone_gw *backbone_gw;
        uint16_t crc;
@@ -658,12 +661,13 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv,
 
        batadv_dbg(BATADV_DBG_BLA, bat_priv,
                   "handle_announce(): ANNOUNCE vid %d (sent by %pM)... CRC = %#.4x\n",
-                  vid, backbone_gw->orig, crc);
+                  BATADV_PRINT_VID(vid), backbone_gw->orig, crc);
 
        if (backbone_gw->crc != crc) {
                batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv,
                           "handle_announce(): CRC FAILED for %pM/%d (my = %#.4x, sent = %#.4x)\n",
-                          backbone_gw->orig, backbone_gw->vid,
+                          backbone_gw->orig,
+                          BATADV_PRINT_VID(backbone_gw->vid),
                           backbone_gw->crc, crc);
 
                batadv_bla_send_request(backbone_gw);
@@ -685,7 +689,7 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv,
 static int batadv_handle_request(struct batadv_priv *bat_priv,
                                 struct batadv_hard_iface *primary_if,
                                 uint8_t *backbone_addr,
-                                struct ethhdr *ethhdr, short vid)
+                                struct ethhdr *ethhdr, unsigned short vid)
 {
        /* check for REQUEST frame */
        if (!batadv_compare_eth(backbone_addr, ethhdr->h_dest))
@@ -699,7 +703,7 @@ static int batadv_handle_request(struct batadv_priv *bat_priv,
 
        batadv_dbg(BATADV_DBG_BLA, bat_priv,
                   "handle_request(): REQUEST vid %d (sent by %pM)...\n",
-                  vid, ethhdr->h_source);
+                  BATADV_PRINT_VID(vid), ethhdr->h_source);
 
        batadv_bla_answer_request(bat_priv, primary_if, vid);
        return 1;
@@ -709,7 +713,7 @@ static int batadv_handle_request(struct batadv_priv *bat_priv,
 static int batadv_handle_unclaim(struct batadv_priv *bat_priv,
                                 struct batadv_hard_iface *primary_if,
                                 uint8_t *backbone_addr,
-                                uint8_t *claim_addr, short vid)
+                                uint8_t *claim_addr, unsigned short vid)
 {
        struct batadv_bla_backbone_gw *backbone_gw;
 
@@ -727,7 +731,7 @@ static int batadv_handle_unclaim(struct batadv_priv *bat_priv,
        /* this must be an UNCLAIM frame */
        batadv_dbg(BATADV_DBG_BLA, bat_priv,
                   "handle_unclaim(): UNCLAIM %pM on vid %d (sent by %pM)...\n",
-                  claim_addr, vid, backbone_gw->orig);
+                  claim_addr, BATADV_PRINT_VID(vid), backbone_gw->orig);
 
        batadv_bla_del_claim(bat_priv, claim_addr, vid);
        batadv_backbone_gw_free_ref(backbone_gw);
@@ -738,7 +742,7 @@ static int batadv_handle_unclaim(struct batadv_priv *bat_priv,
 static int batadv_handle_claim(struct batadv_priv *bat_priv,
                               struct batadv_hard_iface *primary_if,
                               uint8_t *backbone_addr, uint8_t *claim_addr,
-                              short vid)
+                              unsigned short vid)
 {
        struct batadv_bla_backbone_gw *backbone_gw;
 
@@ -861,14 +865,15 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
        struct batadv_bla_claim_dst *bla_dst;
        uint16_t proto;
        int headlen;
-       short vid = -1;
+       unsigned short vid = BATADV_NO_FLAGS;
        int ret;
 
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
 
        if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
                vhdr = (struct vlan_ethhdr *)ethhdr;
                vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
+               vid |= BATADV_VLAN_HAS_TAG;
                proto = ntohs(vhdr->h_vlan_encapsulated_proto);
                headlen = sizeof(*vhdr);
        } else {
@@ -885,7 +890,7 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
                return 0;
 
        /* pskb_may_pull() may have modified the pointers, get ethhdr again */
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
        arphdr = (struct arphdr *)((uint8_t *)ethhdr + headlen);
 
        /* Check whether the ARP frame carries a valid
@@ -910,7 +915,8 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
        if (ret == 1)
                batadv_dbg(BATADV_DBG_BLA, bat_priv,
                           "bla_process_claim(): received a claim frame from another group. From: %pM on vid %d ...(hw_src %pM, hw_dst %pM)\n",
-                          ethhdr->h_source, vid, hw_src, hw_dst);
+                          ethhdr->h_source, BATADV_PRINT_VID(vid), hw_src,
+                          hw_dst);
 
        if (ret < 2)
                return ret;
@@ -945,7 +951,7 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
 
        batadv_dbg(BATADV_DBG_BLA, bat_priv,
                   "bla_process_claim(): ERROR - this looks like a claim frame, but is useless. eth src %pM on vid %d ...(hw_src %pM, hw_dst %pM)\n",
-                  ethhdr->h_source, vid, hw_src, hw_dst);
+                  ethhdr->h_source, BATADV_PRINT_VID(vid), hw_src, hw_dst);
        return 1;
 }
 
@@ -1362,7 +1368,7 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb,
        struct ethhdr *ethhdr;
        struct vlan_ethhdr *vhdr;
        struct batadv_bla_backbone_gw *backbone_gw;
-       short vid = -1;
+       unsigned short vid = BATADV_NO_FLAGS;
 
        if (!atomic_read(&orig_node->bat_priv->bridge_loop_avoidance))
                return 0;
@@ -1379,6 +1385,7 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb,
 
                vhdr = (struct vlan_ethhdr *)(skb->data + hdr_size);
                vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
+               vid |= BATADV_VLAN_HAS_TAG;
        }
 
        /* see if this originator is a backbone gw for this VLAN */
@@ -1428,15 +1435,15 @@ void batadv_bla_free(struct batadv_priv *bat_priv)
  * returns 1, otherwise it returns 0 and the caller shall further
  * process the skb.
  */
-int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid,
-                 bool is_bcast)
+int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+                 unsigned short vid, bool is_bcast)
 {
        struct ethhdr *ethhdr;
        struct batadv_bla_claim search_claim, *claim = NULL;
        struct batadv_hard_iface *primary_if;
        int ret;
 
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
 
        primary_if = batadv_primary_if_get_selected(bat_priv);
        if (!primary_if)
@@ -1523,7 +1530,8 @@ out:
  * returns 1, otherwise it returns 0 and the caller shall further
  * process the skb.
  */
-int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid)
+int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+                 unsigned short vid)
 {
        struct ethhdr *ethhdr;
        struct batadv_bla_claim search_claim, *claim = NULL;
@@ -1543,7 +1551,7 @@ int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid)
        if (batadv_bla_process_claim(bat_priv, primary_if, skb))
                goto handled;
 
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
 
        if (unlikely(atomic_read(&bat_priv->bla.num_requests)))
                /* don't allow broadcasts while requests are in flight */
@@ -1627,8 +1635,8 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
                hlist_for_each_entry_rcu(claim, head, hash_entry) {
                        is_own = batadv_compare_eth(claim->backbone_gw->orig,
                                                    primary_addr);
-                       seq_printf(seq, " * %pM on % 5d by %pM [%c] (%#.4x)\n",
-                                  claim->addr, claim->vid,
+                       seq_printf(seq, " * %pM on %5d by %pM [%c] (%#.4x)\n",
+                                  claim->addr, BATADV_PRINT_VID(claim->vid),
                                   claim->backbone_gw->orig,
                                   (is_own ? 'x' : ' '),
                                   claim->backbone_gw->crc);
@@ -1680,10 +1688,10 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
                        if (is_own)
                                continue;
 
-                       seq_printf(seq,
-                                  " * %pM on % 5d % 4i.%03is (%#.4x)\n",
-                                  backbone_gw->orig, backbone_gw->vid,
-                                  secs, msecs, backbone_gw->crc);
+                       seq_printf(seq, " * %pM on %5d %4i.%03is (%#.4x)\n",
+                                  backbone_gw->orig,
+                                  BATADV_PRINT_VID(backbone_gw->vid), secs,
+                                  msecs, backbone_gw->crc);
                }
                rcu_read_unlock();
        }
index dea2fbc..4b102e7 100644 (file)
 #define _NET_BATMAN_ADV_BLA_H_
 
 #ifdef CONFIG_BATMAN_ADV_BLA
-int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid,
-                 bool is_bcast);
-int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid);
+int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+                 unsigned short vid, bool is_bcast);
+int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
+                 unsigned short vid);
 int batadv_bla_is_backbone_gw(struct sk_buff *skb,
                              struct batadv_orig_node *orig_node, int hdr_size);
 int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset);
@@ -42,13 +43,14 @@ void batadv_bla_free(struct batadv_priv *bat_priv);
 #else /* ifdef CONFIG_BATMAN_ADV_BLA */
 
 static inline int batadv_bla_rx(struct batadv_priv *bat_priv,
-                               struct sk_buff *skb, short vid, bool is_bcast)
+                               struct sk_buff *skb, unsigned short vid,
+                               bool is_bcast)
 {
        return 0;
 }
 
 static inline int batadv_bla_tx(struct batadv_priv *bat_priv,
-                               struct sk_buff *skb, short vid)
+                               struct sk_buff *skb, unsigned short vid)
 {
        return 0;
 }
index 2399920..06345d4 100644 (file)
@@ -45,9 +45,9 @@ static void batadv_dat_start_timer(struct batadv_priv *bat_priv)
 }
 
 /**
- * batadv_dat_entry_free_ref - decrements the dat_entry refcounter and possibly
+ * batadv_dat_entry_free_ref - decrement the dat_entry refcounter and possibly
  * free it
- * @dat_entry: the oentry to free
+ * @dat_entry: the entry to free
  */
 static void batadv_dat_entry_free_ref(struct batadv_dat_entry *dat_entry)
 {
@@ -56,10 +56,10 @@ static void batadv_dat_entry_free_ref(struct batadv_dat_entry *dat_entry)
 }
 
 /**
- * batadv_dat_to_purge - checks whether a dat_entry has to be purged or not
+ * batadv_dat_to_purge - check whether a dat_entry has to be purged or not
  * @dat_entry: the entry to check
  *
- * Returns true if the entry has to be purged now, false otherwise
+ * Returns true if the entry has to be purged now, false otherwise.
  */
 static bool batadv_dat_to_purge(struct batadv_dat_entry *dat_entry)
 {
@@ -75,8 +75,8 @@ static bool batadv_dat_to_purge(struct batadv_dat_entry *dat_entry)
  *           returns a boolean value: true is the entry has to be deleted,
  *           false otherwise
  *
- * Loops over each entry in the DAT local storage and delete it if and only if
- * the to_purge function passed as argument returns true
+ * Loops over each entry in the DAT local storage and deletes it if and only if
+ * the to_purge function passed as argument returns true.
  */
 static void __batadv_dat_purge(struct batadv_priv *bat_priv,
                               bool (*to_purge)(struct batadv_dat_entry *))
@@ -97,7 +97,7 @@ static void __batadv_dat_purge(struct batadv_priv *bat_priv,
                spin_lock_bh(list_lock);
                hlist_for_each_entry_safe(dat_entry, node_tmp, head,
                                          hash_entry) {
-                       /* if an helper function has been passed as parameter,
+                       /* if a helper function has been passed as parameter,
                         * ask it if the entry has to be purged or not
                         */
                        if (to_purge && !to_purge(dat_entry))
@@ -134,7 +134,7 @@ static void batadv_dat_purge(struct work_struct *work)
  * @node: node in the local table
  * @data2: second object to compare the node to
  *
- * Returns 1 if the two entry are the same, 0 otherwise
+ * Returns 1 if the two entries are the same, 0 otherwise.
  */
 static int batadv_compare_dat(const struct hlist_node *node, const void *data2)
 {
@@ -149,7 +149,7 @@ static int batadv_compare_dat(const struct hlist_node *node, const void *data2)
  * @skb: ARP packet
  * @hdr_size: size of the possible header before the ARP packet
  *
- * Returns the value of the hw_src field in the ARP packet
+ * Returns the value of the hw_src field in the ARP packet.
  */
 static uint8_t *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size)
 {
@@ -166,7 +166,7 @@ static uint8_t *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size)
  * @skb: ARP packet
  * @hdr_size: size of the possible header before the ARP packet
  *
- * Returns the value of the ip_src field in the ARP packet
+ * Returns the value of the ip_src field in the ARP packet.
  */
 static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size)
 {
@@ -178,7 +178,7 @@ static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size)
  * @skb: ARP packet
  * @hdr_size: size of the possible header before the ARP packet
  *
- * Returns the value of the hw_dst field in the ARP packet
+ * Returns the value of the hw_dst field in the ARP packet.
  */
 static uint8_t *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size)
 {
@@ -190,7 +190,7 @@ static uint8_t *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size)
  * @skb: ARP packet
  * @hdr_size: size of the possible header before the ARP packet
  *
- * Returns the value of the ip_dst field in the ARP packet
+ * Returns the value of the ip_dst field in the ARP packet.
  */
 static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
 {
@@ -202,7 +202,7 @@ static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
  * @data: data to hash
  * @size: size of the hash table
  *
- * Returns the selected index in the hash table for the given data
+ * Returns the selected index in the hash table for the given data.
  */
 static uint32_t batadv_hash_dat(const void *data, uint32_t size)
 {
@@ -224,12 +224,12 @@ static uint32_t batadv_hash_dat(const void *data, uint32_t size)
 }
 
 /**
- * batadv_dat_entry_hash_find - looks for a given dat_entry in the local hash
+ * batadv_dat_entry_hash_find - look for a given dat_entry in the local hash
  * table
  * @bat_priv: the bat priv with all the soft interface information
  * @ip: search key
  *
- * Returns the dat_entry if found, NULL otherwise
+ * Returns the dat_entry if found, NULL otherwise.
  */
 static struct batadv_dat_entry *
 batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip)
@@ -343,9 +343,6 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,
        if (hdr_size == 0)
                return;
 
-       /* if the ARP packet is encapsulated in a batman packet, let's print
-        * some debug messages
-        */
        unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
 
        switch (unicast_4addr_packet->u.header.packet_type) {
@@ -409,7 +406,8 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,
  * @candidate: orig_node under evaluation
  * @max_orig_node: last selected candidate
  *
- * Returns true if the node has been elected as next candidate or false othrwise
+ * Returns true if the node has been elected as next candidate or false
+ * otherwise.
  */
 static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
                                         int select, batadv_dat_addr_t tmp_max,
@@ -472,7 +470,7 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv,
         */
        cands[select].type = BATADV_DAT_CANDIDATE_NOT_FOUND;
 
-       /* iterate over the originator list and find the node with closest
+       /* iterate over the originator list and find the node with the closest
         * dat_address which has not been selected yet
         */
        for (i = 0; i < hash->size; i++) {
@@ -480,7 +478,7 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv,
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       /* the dht space is a ring and addresses are unsigned */
+                       /* the dht space is a ring using unsigned addresses */
                        tmp_max = BATADV_DAT_ADDR_MAX - orig_node->dat_addr +
                                  ip_key;
 
@@ -512,7 +510,7 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv,
 }
 
 /**
- * batadv_dat_select_candidates - selects the nodes which the DHT message has to
+ * batadv_dat_select_candidates - select the nodes which the DHT message has to
  * be sent to
  * @bat_priv: the bat priv with all the soft interface information
  * @ip_dst: ipv4 to look up in the DHT
@@ -521,7 +519,7 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv,
  * closest values (from the LEFT, with wrap around if needed) then the hash
  * value of the key. ip_dst is the key.
  *
- * Returns the candidate array of size BATADV_DAT_CANDIDATE_NUM
+ * Returns the candidate array of size BATADV_DAT_CANDIDATE_NUM.
  */
 static struct batadv_dat_candidate *
 batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
@@ -558,10 +556,11 @@ batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
  * @ip: the DHT key
  * @packet_subtype: unicast4addr packet subtype to use
  *
- * In this function the skb is copied by means of pskb_copy() and is sent as
- * unicast packet to each of the selected candidates
+ * This function copies the skb with pskb_copy() and is sent as unicast packet
+ * to each of the selected candidates.
  *
- * Returns true if the packet is sent to at least one candidate, false otherwise
+ * Returns true if the packet is sent to at least one candidate, false
+ * otherwise.
  */
 static bool batadv_dat_send_data(struct batadv_priv *bat_priv,
                                 struct sk_buff *skb, __be32 ip,
@@ -727,7 +726,7 @@ out:
  * @skb: packet to analyse
  * @hdr_size: size of the possible header before the ARP packet in the skb
  *
- * Returns the ARP type if the skb contains a valid ARP packet, 0 otherwise
+ * Returns the ARP type if the skb contains a valid ARP packet, 0 otherwise.
  */
 static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv,
                                    struct sk_buff *skb, int hdr_size)
@@ -754,9 +753,7 @@ static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv,
 
        arphdr = (struct arphdr *)(skb->data + hdr_size + ETH_HLEN);
 
-       /* Check whether the ARP packet carries a valid
-        * IP information
-        */
+       /* check whether the ARP packet carries a valid IP information */
        if (arphdr->ar_hrd != htons(ARPHRD_ETHER))
                goto out;
 
@@ -784,7 +781,7 @@ static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv,
        if (is_zero_ether_addr(hw_src) || is_multicast_ether_addr(hw_src))
                goto out;
 
-       /* we don't care about the destination MAC address in ARP requests */
+       /* don't care about the destination MAC address in ARP requests */
        if (arphdr->ar_op != htons(ARPOP_REQUEST)) {
                hw_dst = batadv_arp_hw_dst(skb, hdr_size);
                if (is_zero_ether_addr(hw_dst) ||
@@ -804,8 +801,8 @@ out:
  * @skb: packet to check
  *
  * Returns true if the message has been sent to the dht candidates, false
- * otherwise. In case of true the message has to be enqueued to permit the
- * fallback
+ * otherwise. In case of a positive return value the message has to be enqueued
+ * to permit the fallback.
  */
 bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
                                           struct sk_buff *skb)
@@ -867,7 +864,7 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
                batadv_dbg(BATADV_DBG_DAT, bat_priv, "ARP request replied locally\n");
                ret = true;
        } else {
-               /* Send the request on the DHT */
+               /* Send the request to the DHT */
                ret = batadv_dat_send_data(bat_priv, skb, ip_dst,
                                           BATADV_P_DAT_DHT_GET);
        }
@@ -884,7 +881,7 @@ out:
  * @skb: packet to check
  * @hdr_size: size of the encapsulation header
  *
- * Returns true if the request has been answered, false otherwise
+ * Returns true if the request has been answered, false otherwise.
  */
 bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
                                           struct sk_buff *skb, int hdr_size)
@@ -924,10 +921,9 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
        if (!skb_new)
                goto out;
 
-       /* to preserve backwards compatibility, here the node has to answer
-        * using the same packet type it received for the request. This is due
-        * to that if a node is not using the 4addr packet format it may not
-        * support it.
+       /* To preserve backwards compatibility, the node has choose the outgoing
+        * format based on the incoming request packet type. The assumption is
+        * that a node not using the 4addr packet format doesn't support it.
         */
        if (hdr_size == sizeof(struct batadv_unicast_4addr_packet))
                err = batadv_unicast_4addr_send_skb(bat_priv, skb_new,
@@ -977,7 +973,7 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
        batadv_dat_entry_add(bat_priv, ip_dst, hw_dst);
 
        /* Send the ARP reply to the candidates for both the IP addresses that
-        * the node got within the ARP reply
+        * the node obtained from the ARP reply
         */
        batadv_dat_send_data(bat_priv, skb, ip_src, BATADV_P_DAT_DHT_PUT);
        batadv_dat_send_data(bat_priv, skb, ip_dst, BATADV_P_DAT_DHT_PUT);
@@ -987,7 +983,7 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
  * DAT storage only
  * @bat_priv: the bat priv with all the soft interface information
  * @skb: packet to check
- * @hdr_size: siaze of the encapsulation header
+ * @hdr_size: size of the encapsulation header
  */
 bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
                                         struct sk_buff *skb, int hdr_size)
@@ -1031,11 +1027,11 @@ out:
 
 /**
  * batadv_dat_drop_broadcast_packet - check if an ARP request has to be dropped
- * (because the node has already got the reply via DAT) or not
+ * (because the node has already obtained the reply via DAT) or not
  * @bat_priv: the bat priv with all the soft interface information
  * @forw_packet: the broadcast packet
  *
- * Returns true if the node can drop the packet, false otherwise
+ * Returns true if the node can drop the packet, false otherwise.
  */
 bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
                                      struct batadv_forw_packet *forw_packet)
index 522243a..c478e6b 100644 (file)
@@ -117,6 +117,58 @@ static int batadv_is_valid_iface(const struct net_device *net_dev)
        return 1;
 }
 
+/**
+ * batadv_is_wifi_netdev - check if the given net_device struct is a wifi
+ *  interface
+ * @net_device: the device to check
+ *
+ * Returns true if the net device is a 802.11 wireless device, false otherwise.
+ */
+static bool batadv_is_wifi_netdev(struct net_device *net_device)
+{
+#ifdef CONFIG_WIRELESS_EXT
+       /* pre-cfg80211 drivers have to implement WEXT, so it is possible to
+        * check for wireless_handlers != NULL
+        */
+       if (net_device->wireless_handlers)
+               return true;
+#endif
+
+       /* cfg80211 drivers have to set ieee80211_ptr */
+       if (net_device->ieee80211_ptr)
+               return true;
+
+       return false;
+}
+
+/**
+ * batadv_is_wifi_iface - check if the given interface represented by ifindex
+ *  is a wifi interface
+ * @ifindex: interface index to check
+ *
+ * Returns true if the interface represented by ifindex is a 802.11 wireless
+ * device, false otherwise.
+ */
+bool batadv_is_wifi_iface(int ifindex)
+{
+       struct net_device *net_device = NULL;
+       bool ret = false;
+
+       if (ifindex == BATADV_NULL_IFINDEX)
+               goto out;
+
+       net_device = dev_get_by_index(&init_net, ifindex);
+       if (!net_device)
+               goto out;
+
+       ret = batadv_is_wifi_netdev(net_device);
+
+out:
+       if (net_device)
+               dev_put(net_device);
+       return ret;
+}
+
 static struct batadv_hard_iface *
 batadv_hardif_get_active(const struct net_device *soft_iface)
 {
@@ -525,7 +577,7 @@ batadv_hardif_add_interface(struct net_device *net_dev)
 
        dev_hold(net_dev);
 
-       hard_iface = kmalloc(sizeof(*hard_iface), GFP_ATOMIC);
+       hard_iface = kzalloc(sizeof(*hard_iface), GFP_ATOMIC);
        if (!hard_iface)
                goto release_dev;
 
@@ -541,18 +593,16 @@ batadv_hardif_add_interface(struct net_device *net_dev)
        INIT_WORK(&hard_iface->cleanup_work,
                  batadv_hardif_remove_interface_finish);
 
+       hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
+       if (batadv_is_wifi_netdev(net_dev))
+               hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
+
        /* extra reference for return */
        atomic_set(&hard_iface->refcount, 2);
 
        batadv_check_known_mac_addr(hard_iface->net_dev);
        list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
 
-       /* This can't be called via a bat_priv callback because
-        * we have no bat_priv yet.
-        */
-       atomic_set(&hard_iface->bat_iv.ogm_seqno, 1);
-       hard_iface->bat_iv.ogm_buff = NULL;
-
        return hard_iface;
 
 free_if:
@@ -595,7 +645,7 @@ void batadv_hardif_remove_interfaces(void)
 static int batadv_hard_if_event(struct notifier_block *this,
                                unsigned long event, void *ptr)
 {
-       struct net_device *net_dev = ptr;
+       struct net_device *net_dev = netdev_notifier_info_to_dev(ptr);
        struct batadv_hard_iface *hard_iface;
        struct batadv_hard_iface *primary_if = NULL;
        struct batadv_priv *bat_priv;
@@ -657,38 +707,6 @@ out:
        return NOTIFY_DONE;
 }
 
-/* This function returns true if the interface represented by ifindex is a
- * 802.11 wireless device
- */
-bool batadv_is_wifi_iface(int ifindex)
-{
-       struct net_device *net_device = NULL;
-       bool ret = false;
-
-       if (ifindex == BATADV_NULL_IFINDEX)
-               goto out;
-
-       net_device = dev_get_by_index(&init_net, ifindex);
-       if (!net_device)
-               goto out;
-
-#ifdef CONFIG_WIRELESS_EXT
-       /* pre-cfg80211 drivers have to implement WEXT, so it is possible to
-        * check for wireless_handlers != NULL
-        */
-       if (net_device->wireless_handlers)
-               ret = true;
-       else
-#endif
-               /* cfg80211 drivers have to set ieee80211_ptr */
-               if (net_device->ieee80211_ptr)
-                       ret = true;
-out:
-       if (net_device)
-               dev_put(net_device);
-       return ret;
-}
-
 struct notifier_block batadv_hard_if_notifier = {
        .notifier_call = batadv_hard_if_event,
 };
index 0ba6c89..b27508b 100644 (file)
@@ -177,13 +177,13 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
        if (len >= sizeof(struct batadv_icmp_packet_rr))
                packet_len = sizeof(struct batadv_icmp_packet_rr);
 
-       skb = dev_alloc_skb(packet_len + ETH_HLEN + NET_IP_ALIGN);
+       skb = netdev_alloc_skb_ip_align(NULL, packet_len + ETH_HLEN);
        if (!skb) {
                len = -ENOMEM;
                goto out;
        }
 
-       skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
+       skb_reserve(skb, ETH_HLEN);
        icmp_packet = (struct batadv_icmp_packet_rr *)skb_put(skb, packet_len);
 
        if (copy_from_user(icmp_packet, buff, packet_len)) {
index 51aafd6..08125f3 100644 (file)
@@ -473,7 +473,6 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr)
                crc = crc32c(crc, data, len);
                consumed += len;
        }
-       skb_abort_seq_read(&st);
 
        return htonl(crc);
 }
index 59a0d6a..5e9aebb 100644 (file)
@@ -26,7 +26,7 @@
 #define BATADV_DRIVER_DEVICE "batman-adv"
 
 #ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2013.2.0"
+#define BATADV_SOURCE_VERSION "2013.3.0"
 #endif
 
 /* B.A.T.M.A.N. parameters */
 
 #define BATADV_LOG_BUF_LEN 8192          /* has to be a power of 2 */
 
+/* number of packets to send for broadcasts on different interface types */
+#define BATADV_NUM_BCASTS_DEFAULT 1
+#define BATADV_NUM_BCASTS_WIRELESS 3
+#define BATADV_NUM_BCASTS_MAX 3
+
 /* msecs after which an ARP_REQUEST is sent in broadcast as fallback */
 #define ARP_REQ_DELAY 250
 /* numbers of originator to contact for any PUT/GET DHT operation */
@@ -157,6 +162,17 @@ enum batadv_uev_type {
 #include <linux/seq_file.h>
 #include "types.h"
 
+/**
+ * batadv_vlan_flags - flags for the four MSB of any vlan ID field
+ * @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not
+ */
+enum batadv_vlan_flags {
+       BATADV_VLAN_HAS_TAG     = BIT(15),
+};
+
+#define BATADV_PRINT_VID(vid) (vid & BATADV_VLAN_HAS_TAG ? \
+                              (int)(vid & VLAN_VID_MASK) : -1)
+
 extern char batadv_routing_algo[];
 extern struct list_head batadv_hardif_list;
 
index e84629e..a487d46 100644 (file)
@@ -1245,7 +1245,7 @@ static void batadv_nc_skb_store_before_coding(struct batadv_priv *bat_priv,
                return;
 
        /* Set the mac header as if we actually sent the packet uncoded */
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
        memcpy(ethhdr->h_source, ethhdr->h_dest, ETH_ALEN);
        memcpy(ethhdr->h_dest, eth_dst_new, ETH_ALEN);
 
@@ -1359,18 +1359,17 @@ static bool batadv_nc_skb_add_to_path(struct sk_buff *skb,
  *  buffer
  * @skb: data skb to forward
  * @neigh_node: next hop to forward packet to
- * @ethhdr: pointer to the ethernet header inside the skb
  *
  * Returns true if the skb was consumed (encoded packet sent) or false otherwise
  */
 bool batadv_nc_skb_forward(struct sk_buff *skb,
-                          struct batadv_neigh_node *neigh_node,
-                          struct ethhdr *ethhdr)
+                          struct batadv_neigh_node *neigh_node)
 {
        const struct net_device *netdev = neigh_node->if_incoming->soft_iface;
        struct batadv_priv *bat_priv = netdev_priv(netdev);
        struct batadv_unicast_packet *packet;
        struct batadv_nc_path *nc_path;
+       struct ethhdr *ethhdr = eth_hdr(skb);
        __be32 packet_id;
        u8 *payload;
 
@@ -1423,7 +1422,7 @@ void batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv,
 {
        struct batadv_unicast_packet *packet;
        struct batadv_nc_path *nc_path;
-       struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       struct ethhdr *ethhdr = eth_hdr(skb);
        __be32 packet_id;
        u8 *payload;
 
@@ -1482,7 +1481,7 @@ out:
 void batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv,
                                         struct sk_buff *skb)
 {
-       struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       struct ethhdr *ethhdr = eth_hdr(skb);
 
        if (batadv_is_my_mac(bat_priv, ethhdr->h_dest))
                return;
@@ -1533,7 +1532,7 @@ batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb,
        skb_reset_network_header(skb);
 
        /* Reconstruct original mac header */
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
        memcpy(ethhdr, &ethhdr_tmp, sizeof(*ethhdr));
 
        /* Select the correct unicast header information based on the location
@@ -1677,7 +1676,7 @@ static int batadv_nc_recv_coded_packet(struct sk_buff *skb,
                return NET_RX_DROP;
 
        coded_packet = (struct batadv_coded_packet *)skb->data;
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
 
        /* Verify frame is destined for us */
        if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest) &&
@@ -1763,6 +1762,13 @@ int batadv_nc_nodes_seq_print_text(struct seq_file *seq, void *offset)
                /* For each orig_node in this bin */
                rcu_read_lock();
                hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
+                       /* no need to print the orig node if it does not have
+                        * network coding neighbors
+                        */
+                       if (list_empty(&orig_node->in_coding_list) &&
+                           list_empty(&orig_node->out_coding_list))
+                               continue;
+
                        seq_printf(seq, "Node:      %pM\n", orig_node->orig);
 
                        seq_puts(seq, " Ingoing:  ");
index 4fa6d0c..85a4ec8 100644 (file)
@@ -36,8 +36,7 @@ void batadv_nc_purge_orig(struct batadv_priv *bat_priv,
 void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv);
 void batadv_nc_init_orig(struct batadv_orig_node *orig_node);
 bool batadv_nc_skb_forward(struct sk_buff *skb,
-                          struct batadv_neigh_node *neigh_node,
-                          struct ethhdr *ethhdr);
+                          struct batadv_neigh_node *neigh_node);
 void batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv,
                                      struct sk_buff *skb);
 void batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv,
@@ -87,8 +86,7 @@ static inline void batadv_nc_init_orig(struct batadv_orig_node *orig_node)
 }
 
 static inline bool batadv_nc_skb_forward(struct sk_buff *skb,
-                                        struct batadv_neigh_node *neigh_node,
-                                        struct ethhdr *ethhdr)
+                                        struct batadv_neigh_node *neigh_node)
 {
        return false;
 }
index fad1a20..f50553a 100644 (file)
@@ -92,7 +92,7 @@ batadv_orig_node_get_router(struct batadv_orig_node *orig_node)
 
 struct batadv_neigh_node *
 batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
-                     const uint8_t *neigh_addr, uint32_t seqno)
+                     const uint8_t *neigh_addr)
 {
        struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
        struct batadv_neigh_node *neigh_node;
@@ -110,8 +110,8 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
        atomic_set(&neigh_node->refcount, 2);
 
        batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                  "Creating new neighbor %pM, initial seqno %d\n",
-                  neigh_addr, seqno);
+                  "Creating new neighbor %pM on interface %s\n", neigh_addr,
+                  hard_iface->net_dev->name);
 
 out:
        return neigh_node;
index 734e5a3..7887b84 100644 (file)
@@ -31,7 +31,7 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
                                              const uint8_t *addr);
 struct batadv_neigh_node *
 batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
-                     const uint8_t *neigh_addr, uint32_t seqno);
+                     const uint8_t *neigh_addr);
 void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node);
 struct batadv_neigh_node *
 batadv_orig_node_get_router(struct batadv_orig_node *orig_node);
diff --git a/net/batman-adv/ring_buffer.c b/net/batman-adv/ring_buffer.c
deleted file mode 100644 (file)
index ccab0bb..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors:
- *
- * Marek Lindner
- *
- * 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 Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#include "main.h"
-#include "ring_buffer.h"
-
-void batadv_ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index,
-                           uint8_t value)
-{
-       lq_recv[*lq_index] = value;
-       *lq_index = (*lq_index + 1) % BATADV_TQ_GLOBAL_WINDOW_SIZE;
-}
-
-uint8_t batadv_ring_buffer_avg(const uint8_t lq_recv[])
-{
-       const uint8_t *ptr;
-       uint16_t count = 0, i = 0, sum = 0;
-
-       ptr = lq_recv;
-
-       while (i < BATADV_TQ_GLOBAL_WINDOW_SIZE) {
-               if (*ptr != 0) {
-                       count++;
-                       sum += *ptr;
-               }
-
-               i++;
-               ptr++;
-       }
-
-       if (count == 0)
-               return 0;
-
-       return (uint8_t)(sum / count);
-}
diff --git a/net/batman-adv/ring_buffer.h b/net/batman-adv/ring_buffer.h
deleted file mode 100644 (file)
index 3f92ae2..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors:
- *
- * Marek Lindner
- *
- * 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 Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#ifndef _NET_BATMAN_ADV_RING_BUFFER_H_
-#define _NET_BATMAN_ADV_RING_BUFFER_H_
-
-void batadv_ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index,
-                           uint8_t value);
-uint8_t batadv_ring_buffer_avg(const uint8_t lq_recv[]);
-
-#endif /* _NET_BATMAN_ADV_RING_BUFFER_H_ */
index b27a4d7..2f0bd3f 100644 (file)
 static int batadv_route_unicast_packet(struct sk_buff *skb,
                                       struct batadv_hard_iface *recv_if);
 
-void batadv_slide_own_bcast_window(struct batadv_hard_iface *hard_iface)
-{
-       struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
-       struct batadv_hashtable *hash = bat_priv->orig_hash;
-       struct hlist_head *head;
-       struct batadv_orig_node *orig_node;
-       unsigned long *word;
-       uint32_t i;
-       size_t word_index;
-       uint8_t *w;
-
-       for (i = 0; i < hash->size; i++) {
-               head = &hash->table[i];
-
-               rcu_read_lock();
-               hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       spin_lock_bh(&orig_node->ogm_cnt_lock);
-                       word_index = hard_iface->if_num * BATADV_NUM_WORDS;
-                       word = &(orig_node->bcast_own[word_index]);
-
-                       batadv_bit_get_packet(bat_priv, word, 1, 0);
-                       w = &orig_node->bcast_own_sum[hard_iface->if_num];
-                       *w = bitmap_weight(word, BATADV_TQ_LOCAL_WINDOW_SIZE);
-                       spin_unlock_bh(&orig_node->ogm_cnt_lock);
-               }
-               rcu_read_unlock();
-       }
-}
-
 static void _batadv_update_route(struct batadv_priv *bat_priv,
                                 struct batadv_orig_node *orig_node,
                                 struct batadv_neigh_node *neigh_node)
@@ -256,7 +227,7 @@ bool batadv_check_management_packet(struct sk_buff *skb,
        if (unlikely(!pskb_may_pull(skb, header_len)))
                return false;
 
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
 
        /* packet with broadcast indication but unicast recipient */
        if (!is_broadcast_ether_addr(ethhdr->h_dest))
@@ -314,7 +285,7 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
        icmp_packet->msg_type = BATADV_ECHO_REPLY;
        icmp_packet->header.ttl = BATADV_TTL;
 
-       if (batadv_send_skb_to_orig(skb, orig_node, NULL))
+       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
                ret = NET_RX_SUCCESS;
 
 out:
@@ -362,7 +333,7 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
        icmp_packet->msg_type = BATADV_TTL_EXCEEDED;
        icmp_packet->header.ttl = BATADV_TTL;
 
-       if (batadv_send_skb_to_orig(skb, orig_node, NULL))
+       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
                ret = NET_RX_SUCCESS;
 
 out:
@@ -392,7 +363,7 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
        if (unlikely(!pskb_may_pull(skb, hdr_size)))
                goto out;
 
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
 
        /* packet with unicast indication but broadcast recipient */
        if (is_broadcast_ether_addr(ethhdr->h_dest))
@@ -439,7 +410,7 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
        icmp_packet->header.ttl--;
 
        /* route it */
-       if (batadv_send_skb_to_orig(skb, orig_node, recv_if))
+       if (batadv_send_skb_to_orig(skb, orig_node, recv_if) != NET_XMIT_DROP)
                ret = NET_RX_SUCCESS;
 
 out:
@@ -569,7 +540,7 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
        if (unlikely(!pskb_may_pull(skb, hdr_size)))
                return -ENODATA;
 
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
 
        /* packet with unicast indication but broadcast recipient */
        if (is_broadcast_ether_addr(ethhdr->h_dest))
@@ -803,8 +774,8 @@ static int batadv_route_unicast_packet(struct sk_buff *skb,
        struct batadv_orig_node *orig_node = NULL;
        struct batadv_neigh_node *neigh_node = NULL;
        struct batadv_unicast_packet *unicast_packet;
-       struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb);
-       int ret = NET_RX_DROP;
+       struct ethhdr *ethhdr = eth_hdr(skb);
+       int res, ret = NET_RX_DROP;
        struct sk_buff *new_skb;
 
        unicast_packet = (struct batadv_unicast_packet *)skb->data;
@@ -864,16 +835,19 @@ static int batadv_route_unicast_packet(struct sk_buff *skb,
        /* decrement ttl */
        unicast_packet->header.ttl--;
 
-       /* network code packet if possible */
-       if (batadv_nc_skb_forward(skb, neigh_node, ethhdr)) {
-               ret = NET_RX_SUCCESS;
-       } else if (batadv_send_skb_to_orig(skb, orig_node, recv_if)) {
-               ret = NET_RX_SUCCESS;
+       res = batadv_send_skb_to_orig(skb, orig_node, recv_if);
 
-               /* Update stats counter */
+       /* translate transmit result into receive result */
+       if (res == NET_XMIT_SUCCESS) {
+               /* skb was transmitted and consumed */
                batadv_inc_counter(bat_priv, BATADV_CNT_FORWARD);
                batadv_add_counter(bat_priv, BATADV_CNT_FORWARD_BYTES,
                                   skb->len + ETH_HLEN);
+
+               ret = NET_RX_SUCCESS;
+       } else if (res == NET_XMIT_POLICED) {
+               /* skb was buffered and consumed */
+               ret = NET_RX_SUCCESS;
        }
 
 out:
@@ -1165,7 +1139,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
        if (unlikely(!pskb_may_pull(skb, hdr_size)))
                goto out;
 
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
 
        /* packet with broadcast indication but unicast recipient */
        if (!is_broadcast_ether_addr(ethhdr->h_dest))
@@ -1265,7 +1239,7 @@ int batadv_recv_vis_packet(struct sk_buff *skb,
                return NET_RX_DROP;
 
        vis_packet = (struct batadv_vis_packet *)skb->data;
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
 
        /* not for me */
        if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest))
index 99eeafa..72a29bd 100644 (file)
@@ -20,7 +20,6 @@
 #ifndef _NET_BATMAN_ADV_ROUTING_H_
 #define _NET_BATMAN_ADV_ROUTING_H_
 
-void batadv_slide_own_bcast_window(struct batadv_hard_iface *hard_iface);
 bool batadv_check_management_packet(struct sk_buff *skb,
                                    struct batadv_hard_iface *hard_iface,
                                    int header_len);
index 263cfd1..e9ff8d8 100644 (file)
@@ -61,7 +61,7 @@ int batadv_send_skb_packet(struct sk_buff *skb,
 
        skb_reset_mac_header(skb);
 
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
        memcpy(ethhdr->h_source, hard_iface->net_dev->dev_addr, ETH_ALEN);
        memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
        ethhdr->h_proto = __constant_htons(ETH_P_BATMAN);
@@ -96,26 +96,37 @@ send_skb_err:
  * host, NULL can be passed as recv_if and no interface alternating is
  * attempted.
  *
- * Returns TRUE on success; FALSE otherwise.
+ * Returns NET_XMIT_SUCCESS on success, NET_XMIT_DROP on failure, or
+ * NET_XMIT_POLICED if the skb is buffered for later transmit.
  */
-bool batadv_send_skb_to_orig(struct sk_buff *skb,
-                            struct batadv_orig_node *orig_node,
-                            struct batadv_hard_iface *recv_if)
+int batadv_send_skb_to_orig(struct sk_buff *skb,
+                           struct batadv_orig_node *orig_node,
+                           struct batadv_hard_iface *recv_if)
 {
        struct batadv_priv *bat_priv = orig_node->bat_priv;
        struct batadv_neigh_node *neigh_node;
+       int ret = NET_XMIT_DROP;
 
        /* batadv_find_router() increases neigh_nodes refcount if found. */
        neigh_node = batadv_find_router(bat_priv, orig_node, recv_if);
        if (!neigh_node)
-               return false;
+               return ret;
 
-       /* route it */
-       batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
+       /* try to network code the packet, if it is received on an interface
+        * (i.e. being forwarded). If the packet originates from this node or if
+        * network coding fails, then send the packet as usual.
+        */
+       if (recv_if && batadv_nc_skb_forward(skb, neigh_node)) {
+               ret = NET_XMIT_POLICED;
+       } else {
+               batadv_send_skb_packet(skb, neigh_node->if_incoming,
+                                      neigh_node->addr);
+               ret = NET_XMIT_SUCCESS;
+       }
 
        batadv_neigh_node_free_ref(neigh_node);
 
-       return true;
+       return ret;
 }
 
 void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface)
@@ -152,8 +163,6 @@ _batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
                                 struct batadv_forw_packet *forw_packet,
                                 unsigned long send_time)
 {
-       INIT_HLIST_NODE(&forw_packet->list);
-
        /* add new packet to packet list */
        spin_lock_bh(&bat_priv->forw_bcast_list_lock);
        hlist_add_head(&forw_packet->list, &bat_priv->forw_bcast_list);
@@ -260,6 +269,9 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
                if (hard_iface->soft_iface != soft_iface)
                        continue;
 
+               if (forw_packet->num_packets >= hard_iface->num_bcasts)
+                       continue;
+
                /* send a copy of the saved skb */
                skb1 = skb_clone(forw_packet->skb, GFP_ATOMIC);
                if (skb1)
@@ -271,7 +283,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
        forw_packet->num_packets++;
 
        /* if we still have some more bcasts to send */
-       if (forw_packet->num_packets < 3) {
+       if (forw_packet->num_packets < BATADV_NUM_BCASTS_MAX) {
                _batadv_add_bcast_packet_to_list(bat_priv, forw_packet,
                                                 msecs_to_jiffies(5));
                return;
index 38e662f..e7b1788 100644 (file)
@@ -23,9 +23,9 @@
 int batadv_send_skb_packet(struct sk_buff *skb,
                           struct batadv_hard_iface *hard_iface,
                           const uint8_t *dst_addr);
-bool batadv_send_skb_to_orig(struct sk_buff *skb,
-                            struct batadv_orig_node *orig_node,
-                            struct batadv_hard_iface *recv_if);
+int batadv_send_skb_to_orig(struct sk_buff *skb,
+                           struct batadv_orig_node *orig_node,
+                           struct batadv_hard_iface *recv_if);
 void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface);
 int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
                                    const struct sk_buff *skb,
index 819dfb0..700d0b4 100644 (file)
@@ -154,7 +154,7 @@ static int batadv_interface_tx(struct sk_buff *skb,
                                                    0x00, 0x00};
        unsigned int header_len = 0;
        int data_len = skb->len, ret;
-       short vid __maybe_unused = -1;
+       unsigned short vid __maybe_unused = BATADV_NO_FLAGS;
        bool do_bcast = false;
        uint32_t seqno;
        unsigned long brd_delay = 1;
@@ -303,7 +303,7 @@ void batadv_interface_rx(struct net_device *soft_iface,
        struct ethhdr *ethhdr;
        struct vlan_ethhdr *vhdr;
        struct batadv_header *batadv_header = (struct batadv_header *)skb->data;
-       short vid __maybe_unused = -1;
+       unsigned short vid __maybe_unused = BATADV_NO_FLAGS;
        __be16 ethertype = __constant_htons(ETH_P_BATMAN);
        bool is_bcast;
 
@@ -316,7 +316,7 @@ void batadv_interface_rx(struct net_device *soft_iface,
        skb_pull_rcsum(skb, hdr_size);
        skb_reset_mac_header(skb);
 
-       ethhdr = (struct ethhdr *)skb_mac_header(skb);
+       ethhdr = eth_hdr(skb);
 
        switch (ntohs(ethhdr->h_proto)) {
        case ETH_P_8021Q:
index 9e87485..429aeef 100644 (file)
@@ -163,10 +163,19 @@ batadv_tt_orig_list_entry_free_ref(struct batadv_tt_orig_list_entry *orig_entry)
        call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu);
 }
 
+/**
+ * batadv_tt_local_event - store a local TT event (ADD/DEL)
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_local_entry: the TT entry involved in the event
+ * @event_flags: flags to store in the event structure
+ */
 static void batadv_tt_local_event(struct batadv_priv *bat_priv,
-                                 const uint8_t *addr, uint8_t flags)
+                                 struct batadv_tt_local_entry *tt_local_entry,
+                                 uint8_t event_flags)
 {
        struct batadv_tt_change_node *tt_change_node, *entry, *safe;
+       struct batadv_tt_common_entry *common = &tt_local_entry->common;
+       uint8_t flags = common->flags | event_flags;
        bool event_removed = false;
        bool del_op_requested, del_op_entry;
 
@@ -176,7 +185,7 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
                return;
 
        tt_change_node->change.flags = flags;
-       memcpy(tt_change_node->change.addr, addr, ETH_ALEN);
+       memcpy(tt_change_node->change.addr, common->addr, ETH_ALEN);
 
        del_op_requested = flags & BATADV_TT_CLIENT_DEL;
 
@@ -184,7 +193,7 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
        spin_lock_bh(&bat_priv->tt.changes_list_lock);
        list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list,
                                 list) {
-               if (!batadv_compare_eth(entry->change.addr, addr))
+               if (!batadv_compare_eth(entry->change.addr, common->addr))
                        continue;
 
                /* DEL+ADD in the same orig interval have no effect and can be
@@ -332,7 +341,7 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
        }
 
 add_event:
-       batadv_tt_local_event(bat_priv, addr, tt_local->common.flags);
+       batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS);
 
 check_roaming:
        /* Check whether it is a roaming, but don't do anything if the roaming
@@ -529,8 +538,7 @@ batadv_tt_local_set_pending(struct batadv_priv *bat_priv,
                            struct batadv_tt_local_entry *tt_local_entry,
                            uint16_t flags, const char *message)
 {
-       batadv_tt_local_event(bat_priv, tt_local_entry->common.addr,
-                             tt_local_entry->common.flags | flags);
+       batadv_tt_local_event(bat_priv, tt_local_entry, flags);
 
        /* The local client has to be marked as "pending to be removed" but has
         * to be kept in the table in order to send it in a full table
@@ -584,8 +592,7 @@ uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
        /* if this client has been added right now, it is possible to
         * immediately purge it
         */
-       batadv_tt_local_event(bat_priv, tt_local_entry->common.addr,
-                             curr_flags | BATADV_TT_CLIENT_DEL);
+       batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL);
        hlist_del_rcu(&tt_local_entry->common.hash_entry);
        batadv_tt_local_entry_free_ref(tt_local_entry);
 
@@ -791,10 +798,25 @@ out:
                batadv_tt_orig_list_entry_free_ref(orig_entry);
 }
 
-/* caller must hold orig_node refcount */
+/**
+ * batadv_tt_global_add - add a new TT global entry or update an existing one
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: the originator announcing the client
+ * @tt_addr: the mac address of the non-mesh client
+ * @flags: TT flags that have to be set for this non-mesh client
+ * @ttvn: the tt version number ever announcing this non-mesh client
+ *
+ * Add a new TT global entry for the given originator. If the entry already
+ * exists add a new reference to the given originator (a global entry can have
+ * references to multiple originators) and adjust the flags attribute to reflect
+ * the function argument.
+ * If a TT local entry exists for this non-mesh client remove it.
+ *
+ * The caller must hold orig_node refcount.
+ */
 int batadv_tt_global_add(struct batadv_priv *bat_priv,
                         struct batadv_orig_node *orig_node,
-                        const unsigned char *tt_addr, uint8_t flags,
+                        const unsigned char *tt_addr, uint16_t flags,
                         uint8_t ttvn)
 {
        struct batadv_tt_global_entry *tt_global_entry;
@@ -1600,11 +1622,11 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
        tt_tot = tt_len / sizeof(struct batadv_tt_change);
 
        len = tt_query_size + tt_len;
-       skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN);
+       skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
        if (!skb)
                goto out;
 
-       skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
+       skb_reserve(skb, ETH_HLEN);
        tt_response = (struct batadv_tt_query_packet *)skb_put(skb, len);
        tt_response->ttvn = ttvn;
 
@@ -1665,11 +1687,11 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv,
        if (!tt_req_node)
                goto out;
 
-       skb = dev_alloc_skb(sizeof(*tt_request) + ETH_HLEN + NET_IP_ALIGN);
+       skb = netdev_alloc_skb_ip_align(NULL, sizeof(*tt_request) + ETH_HLEN);
        if (!skb)
                goto out;
 
-       skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
+       skb_reserve(skb, ETH_HLEN);
 
        tt_req_len = sizeof(*tt_request);
        tt_request = (struct batadv_tt_query_packet *)skb_put(skb, tt_req_len);
@@ -1691,7 +1713,7 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv,
 
        batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_TX);
 
-       if (batadv_send_skb_to_orig(skb, dst_orig_node, NULL))
+       if (batadv_send_skb_to_orig(skb, dst_orig_node, NULL) != NET_XMIT_DROP)
                ret = 0;
 
 out:
@@ -1715,7 +1737,7 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv,
        struct batadv_orig_node *req_dst_orig_node;
        struct batadv_orig_node *res_dst_orig_node = NULL;
        uint8_t orig_ttvn, req_ttvn, ttvn;
-       int ret = false;
+       int res, ret = false;
        unsigned char *tt_buff;
        bool full_table;
        uint16_t tt_len, tt_tot;
@@ -1762,11 +1784,11 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv,
                tt_tot = tt_len / sizeof(struct batadv_tt_change);
 
                len = sizeof(*tt_response) + tt_len;
-               skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN);
+               skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
                if (!skb)
                        goto unlock;
 
-               skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
+               skb_reserve(skb, ETH_HLEN);
                packet_pos = skb_put(skb, len);
                tt_response = (struct batadv_tt_query_packet *)packet_pos;
                tt_response->ttvn = req_ttvn;
@@ -1810,8 +1832,10 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv,
 
        batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
 
-       if (batadv_send_skb_to_orig(skb, res_dst_orig_node, NULL))
+       res = batadv_send_skb_to_orig(skb, res_dst_orig_node, NULL);
+       if (res != NET_XMIT_DROP)
                ret = true;
+
        goto out;
 
 unlock:
@@ -1878,11 +1902,11 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv,
                tt_tot = tt_len / sizeof(struct batadv_tt_change);
 
                len = sizeof(*tt_response) + tt_len;
-               skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN);
+               skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
                if (!skb)
                        goto unlock;
 
-               skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
+               skb_reserve(skb, ETH_HLEN);
                packet_pos = skb_put(skb, len);
                tt_response = (struct batadv_tt_query_packet *)packet_pos;
                tt_response->ttvn = req_ttvn;
@@ -1925,7 +1949,7 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv,
 
        batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
 
-       if (batadv_send_skb_to_orig(skb, orig_node, NULL))
+       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
                ret = true;
        goto out;
 
@@ -2212,11 +2236,11 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client,
        if (!batadv_tt_check_roam_count(bat_priv, client))
                goto out;
 
-       skb = dev_alloc_skb(sizeof(*roam_adv_packet) + ETH_HLEN + NET_IP_ALIGN);
+       skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
        if (!skb)
                goto out;
 
-       skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
+       skb_reserve(skb, ETH_HLEN);
 
        roam_adv_packet = (struct batadv_roam_adv_packet *)skb_put(skb, len);
 
@@ -2238,7 +2262,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client,
 
        batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_TX);
 
-       if (batadv_send_skb_to_orig(skb, orig_node, NULL))
+       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
                ret = 0;
 
 out:
index ab8e683..659a3bb 100644 (file)
@@ -33,7 +33,7 @@ void batadv_tt_global_add_orig(struct batadv_priv *bat_priv,
                               const unsigned char *tt_buff, int tt_buff_len);
 int batadv_tt_global_add(struct batadv_priv *bat_priv,
                         struct batadv_orig_node *orig_node,
-                        const unsigned char *addr, uint8_t flags,
+                        const unsigned char *addr, uint16_t flags,
                         uint8_t ttvn);
 int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset);
 void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
index aba8364..b2c94e1 100644 (file)
@@ -61,6 +61,7 @@ struct batadv_hard_iface_bat_iv {
  * @if_status: status of the interface for batman-adv
  * @net_dev: pointer to the net_device
  * @frag_seqno: last fragment sequence number sent by this interface
+ * @num_bcasts: number of payload re-broadcasts on this interface (ARQ)
  * @hardif_obj: kobject of the per interface sysfs "mesh" directory
  * @refcount: number of contexts the object is used
  * @batman_adv_ptype: packet type describing packets that should be processed by
@@ -76,6 +77,7 @@ struct batadv_hard_iface {
        char if_status;
        struct net_device *net_dev;
        atomic_t frag_seqno;
+       uint8_t num_bcasts;
        struct kobject *hardif_obj;
        atomic_t refcount;
        struct packet_type batman_adv_ptype;
@@ -640,7 +642,7 @@ struct batadv_socket_packet {
 #ifdef CONFIG_BATMAN_ADV_BLA
 struct batadv_bla_backbone_gw {
        uint8_t orig[ETH_ALEN];
-       short vid;
+       unsigned short vid;
        struct hlist_node hash_entry;
        struct batadv_priv *bat_priv;
        unsigned long lasttime;
@@ -663,7 +665,7 @@ struct batadv_bla_backbone_gw {
  */
 struct batadv_bla_claim {
        uint8_t addr[ETH_ALEN];
-       short vid;
+       unsigned short vid;
        struct batadv_bla_backbone_gw *backbone_gw;
        unsigned long lasttime;
        struct hlist_node hash_entry;
index 0bb3b59..dc8b5d4 100644 (file)
@@ -464,7 +464,7 @@ find_router:
                goto out;
        }
 
-       if (batadv_send_skb_to_orig(skb, orig_node, NULL))
+       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
                ret = 0;
 
 out:
index 1625e57..4983340 100644 (file)
@@ -392,12 +392,12 @@ batadv_add_packet(struct batadv_priv *bat_priv,
                return NULL;
 
        len = sizeof(*packet) + vis_info_len;
-       info->skb_packet = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN);
+       info->skb_packet = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
        if (!info->skb_packet) {
                kfree(info);
                return NULL;
        }
-       skb_reserve(info->skb_packet, ETH_HLEN + NET_IP_ALIGN);
+       skb_reserve(info->skb_packet, ETH_HLEN);
        packet = (struct batadv_vis_packet *)skb_put(info->skb_packet, len);
 
        kref_init(&info->refcount);
@@ -697,7 +697,7 @@ static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv,
        struct batadv_orig_node *orig_node;
        struct batadv_vis_packet *packet;
        struct sk_buff *skb;
-       uint32_t i;
+       uint32_t i, res;
 
 
        packet = (struct batadv_vis_packet *)info->skb_packet->data;
@@ -724,7 +724,8 @@ static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv,
                        if (!skb)
                                continue;
 
-                       if (!batadv_send_skb_to_orig(skb, orig_node, NULL))
+                       res = batadv_send_skb_to_orig(skb, orig_node, NULL);
+                       if (res == NET_XMIT_DROP)
                                kfree_skb(skb);
                }
                rcu_read_unlock();
@@ -748,7 +749,7 @@ static void batadv_unicast_vis_packet(struct batadv_priv *bat_priv,
        if (!skb)
                goto out;
 
-       if (!batadv_send_skb_to_orig(skb, orig_node, NULL))
+       if (batadv_send_skb_to_orig(skb, orig_node, NULL) == NET_XMIT_DROP)
                kfree_skb(skb);
 
 out:
@@ -854,13 +855,13 @@ int batadv_vis_init(struct batadv_priv *bat_priv)
        if (!bat_priv->vis.my_info)
                goto err;
 
-       len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE;
-       len += ETH_HLEN + NET_IP_ALIGN;
-       bat_priv->vis.my_info->skb_packet = dev_alloc_skb(len);
+       len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE + ETH_HLEN;
+       bat_priv->vis.my_info->skb_packet = netdev_alloc_skb_ip_align(NULL,
+                                                                     len);
        if (!bat_priv->vis.my_info->skb_packet)
                goto free_info;
 
-       skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN + NET_IP_ALIGN);
+       skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN);
        tmp_skb = bat_priv->vis.my_info->skb_packet;
        packet = (struct batadv_vis_packet *)skb_put(tmp_skb, sizeof(*packet));
 
index db7de80..e3a3499 100644 (file)
@@ -597,7 +597,15 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
        struct hci_dev *hdev = req->hdev;
        u8 p;
 
-       /* Only send HCI_Delete_Stored_Link_Key if it is supported */
+       /* Some Broadcom based Bluetooth controllers do not support the
+        * Delete Stored Link Key command. They are clearly indicating its
+        * absence in the bit mask of supported commands.
+        *
+        * Check the supported commands and only if the the command is marked
+        * as supported send it. If not supported assume that the controller
+        * does not have actual support for stored link keys which makes this
+        * command redundant anyway.
+         */
        if (hdev->commands[6] & 0x80) {
                struct hci_cp_delete_stored_link_key cp;
 
@@ -751,7 +759,7 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state)
        hdev->discovery.state = state;
 }
 
-static void inquiry_cache_flush(struct hci_dev *hdev)
+void hci_inquiry_cache_flush(struct hci_dev *hdev)
 {
        struct discovery_state *cache = &hdev->discovery;
        struct inquiry_entry *p, *n;
@@ -964,7 +972,7 @@ int hci_inquiry(void __user *arg)
        hci_dev_lock(hdev);
        if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
            inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) {
-               inquiry_cache_flush(hdev);
+               hci_inquiry_cache_flush(hdev);
                do_inquiry = 1;
        }
        hci_dev_unlock(hdev);
@@ -1201,8 +1209,6 @@ static int hci_dev_do_close(struct hci_dev *hdev)
 {
        BT_DBG("%s %p", hdev->name, hdev);
 
-       cancel_work_sync(&hdev->le_scan);
-
        cancel_delayed_work(&hdev->power_off);
 
        hci_req_cancel(hdev, ENODEV);
@@ -1230,7 +1236,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        cancel_delayed_work_sync(&hdev->le_scan_disable);
 
        hci_dev_lock(hdev);
-       inquiry_cache_flush(hdev);
+       hci_inquiry_cache_flush(hdev);
        hci_conn_hash_flush(hdev);
        hci_dev_unlock(hdev);
 
@@ -1331,7 +1337,7 @@ int hci_dev_reset(__u16 dev)
        skb_queue_purge(&hdev->cmd_q);
 
        hci_dev_lock(hdev);
-       inquiry_cache_flush(hdev);
+       hci_inquiry_cache_flush(hdev);
        hci_conn_hash_flush(hdev);
        hci_dev_unlock(hdev);
 
@@ -1991,80 +1997,59 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
        return mgmt_device_unblocked(hdev, bdaddr, type);
 }
 
-static void le_scan_param_req(struct hci_request *req, unsigned long opt)
+static void inquiry_complete(struct hci_dev *hdev, u8 status)
 {
-       struct le_scan_params *param =  (struct le_scan_params *) opt;
-       struct hci_cp_le_set_scan_param cp;
-
-       memset(&cp, 0, sizeof(cp));
-       cp.type = param->type;
-       cp.interval = cpu_to_le16(param->interval);
-       cp.window = cpu_to_le16(param->window);
+       if (status) {
+               BT_ERR("Failed to start inquiry: status %d", status);
 
-       hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp);
-}
-
-static void le_scan_enable_req(struct hci_request *req, unsigned long opt)
-{
-       struct hci_cp_le_set_scan_enable cp;
-
-       memset(&cp, 0, sizeof(cp));
-       cp.enable = LE_SCAN_ENABLE;
-       cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
-
-       hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+               hci_dev_lock(hdev);
+               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+               hci_dev_unlock(hdev);
+               return;
+       }
 }
 
-static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,
-                         u16 window, int timeout)
+static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status)
 {
-       long timeo = msecs_to_jiffies(3000);
-       struct le_scan_params param;
+       /* General inquiry access code (GIAC) */
+       u8 lap[3] = { 0x33, 0x8b, 0x9e };
+       struct hci_request req;
+       struct hci_cp_inquiry cp;
        int err;
 
-       BT_DBG("%s", hdev->name);
-
-       if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
-               return -EINPROGRESS;
-
-       param.type = type;
-       param.interval = interval;
-       param.window = window;
-
-       hci_req_lock(hdev);
-
-       err = __hci_req_sync(hdev, le_scan_param_req, (unsigned long) &param,
-                            timeo);
-       if (!err)
-               err = __hci_req_sync(hdev, le_scan_enable_req, 0, timeo);
-
-       hci_req_unlock(hdev);
+       if (status) {
+               BT_ERR("Failed to disable LE scanning: status %d", status);
+               return;
+       }
 
-       if (err < 0)
-               return err;
+       switch (hdev->discovery.type) {
+       case DISCOV_TYPE_LE:
+               hci_dev_lock(hdev);
+               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+               hci_dev_unlock(hdev);
+               break;
 
-       queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
-                          timeout);
+       case DISCOV_TYPE_INTERLEAVED:
+               hci_req_init(&req, hdev);
 
-       return 0;
-}
+               memset(&cp, 0, sizeof(cp));
+               memcpy(&cp.lap, lap, sizeof(cp.lap));
+               cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN;
+               hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp);
 
-int hci_cancel_le_scan(struct hci_dev *hdev)
-{
-       BT_DBG("%s", hdev->name);
+               hci_dev_lock(hdev);
 
-       if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags))
-               return -EALREADY;
+               hci_inquiry_cache_flush(hdev);
 
-       if (cancel_delayed_work(&hdev->le_scan_disable)) {
-               struct hci_cp_le_set_scan_enable cp;
+               err = hci_req_run(&req, inquiry_complete);
+               if (err) {
+                       BT_ERR("Inquiry request failed: err %d", err);
+                       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+               }
 
-               /* Send HCI command to disable LE Scan */
-               memset(&cp, 0, sizeof(cp));
-               hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+               hci_dev_unlock(hdev);
+               break;
        }
-
-       return 0;
 }
 
 static void le_scan_disable_work(struct work_struct *work)
@@ -2072,46 +2057,20 @@ static void le_scan_disable_work(struct work_struct *work)
        struct hci_dev *hdev = container_of(work, struct hci_dev,
                                            le_scan_disable.work);
        struct hci_cp_le_set_scan_enable cp;
+       struct hci_request req;
+       int err;
 
        BT_DBG("%s", hdev->name);
 
-       memset(&cp, 0, sizeof(cp));
-
-       hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
-}
-
-static void le_scan_work(struct work_struct *work)
-{
-       struct hci_dev *hdev = container_of(work, struct hci_dev, le_scan);
-       struct le_scan_params *param = &hdev->le_scan_params;
+       hci_req_init(&req, hdev);
 
-       BT_DBG("%s", hdev->name);
+       memset(&cp, 0, sizeof(cp));
+       cp.enable = LE_SCAN_DISABLE;
+       hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
 
-       hci_do_le_scan(hdev, param->type, param->interval, param->window,
-                      param->timeout);
-}
-
-int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
-               int timeout)
-{
-       struct le_scan_params *param = &hdev->le_scan_params;
-
-       BT_DBG("%s", hdev->name);
-
-       if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
-               return -ENOTSUPP;
-
-       if (work_busy(&hdev->le_scan))
-               return -EINPROGRESS;
-
-       param->type = type;
-       param->interval = interval;
-       param->window = window;
-       param->timeout = timeout;
-
-       queue_work(system_long_wq, &hdev->le_scan);
-
-       return 0;
+       err = hci_req_run(&req, le_scan_disable_work_complete);
+       if (err)
+               BT_ERR("Disable LE scanning request failed: err %d", err);
 }
 
 /* Alloc HCI device */
@@ -2148,7 +2107,6 @@ struct hci_dev *hci_alloc_dev(void)
        INIT_WORK(&hdev->cmd_work, hci_cmd_work);
        INIT_WORK(&hdev->tx_work, hci_tx_work);
        INIT_WORK(&hdev->power_on, hci_power_on);
-       INIT_WORK(&hdev->le_scan, le_scan_work);
 
        INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
        INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
@@ -3550,36 +3508,6 @@ static void hci_cmd_work(struct work_struct *work)
        }
 }
 
-int hci_do_inquiry(struct hci_dev *hdev, u8 length)
-{
-       /* General inquiry access code (GIAC) */
-       u8 lap[3] = { 0x33, 0x8b, 0x9e };
-       struct hci_cp_inquiry cp;
-
-       BT_DBG("%s", hdev->name);
-
-       if (test_bit(HCI_INQUIRY, &hdev->flags))
-               return -EINPROGRESS;
-
-       inquiry_cache_flush(hdev);
-
-       memset(&cp, 0, sizeof(cp));
-       memcpy(&cp.lap, lap, sizeof(cp.lap));
-       cp.length  = length;
-
-       return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
-}
-
-int hci_cancel_inquiry(struct hci_dev *hdev)
-{
-       BT_DBG("%s", hdev->name);
-
-       if (!test_bit(HCI_INQUIRY, &hdev->flags))
-               return -EALREADY;
-
-       return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
-}
-
 u8 bdaddr_to_le(u8 bdaddr_type)
 {
        switch (bdaddr_type) {
index b93cd2e..0437200 100644 (file)
@@ -40,21 +40,13 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
-       if (status) {
-               hci_dev_lock(hdev);
-               mgmt_stop_discovery_failed(hdev, status);
-               hci_dev_unlock(hdev);
+       if (status)
                return;
-       }
 
        clear_bit(HCI_INQUIRY, &hdev->flags);
        smp_mb__after_clear_bit(); /* wake_up_bit advises about this barrier */
        wake_up_bit(&hdev->flags, HCI_INQUIRY);
 
-       hci_dev_lock(hdev);
-       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-       hci_dev_unlock(hdev);
-
        hci_conn_check_pending(hdev);
 }
 
@@ -937,20 +929,6 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
        hci_dev_unlock(hdev);
 }
 
-static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
-{
-       __u8 status = *((__u8 *) skb->data);
-
-       BT_DBG("%s status 0x%2.2x", hdev->name, status);
-
-       if (status) {
-               hci_dev_lock(hdev);
-               mgmt_start_discovery_failed(hdev, status);
-               hci_dev_unlock(hdev);
-               return;
-       }
-}
-
 static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
                                      struct sk_buff *skb)
 {
@@ -963,41 +941,16 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
        if (!cp)
                return;
 
+       if (status)
+               return;
+
        switch (cp->enable) {
        case LE_SCAN_ENABLE:
-               if (status) {
-                       hci_dev_lock(hdev);
-                       mgmt_start_discovery_failed(hdev, status);
-                       hci_dev_unlock(hdev);
-                       return;
-               }
-
                set_bit(HCI_LE_SCAN, &hdev->dev_flags);
-
-               hci_dev_lock(hdev);
-               hci_discovery_set_state(hdev, DISCOVERY_FINDING);
-               hci_dev_unlock(hdev);
                break;
 
        case LE_SCAN_DISABLE:
-               if (status) {
-                       hci_dev_lock(hdev);
-                       mgmt_stop_discovery_failed(hdev, status);
-                       hci_dev_unlock(hdev);
-                       return;
-               }
-
                clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
-
-               if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
-                   hdev->discovery.state == DISCOVERY_FINDING) {
-                       mgmt_interleaved_discovery(hdev);
-               } else {
-                       hci_dev_lock(hdev);
-                       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-                       hci_dev_unlock(hdev);
-               }
-
                break;
 
        default:
@@ -1077,18 +1030,10 @@ static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
 
        if (status) {
                hci_conn_check_pending(hdev);
-               hci_dev_lock(hdev);
-               if (test_bit(HCI_MGMT, &hdev->dev_flags))
-                       mgmt_start_discovery_failed(hdev, status);
-               hci_dev_unlock(hdev);
                return;
        }
 
        set_bit(HCI_INQUIRY, &hdev->flags);
-
-       hci_dev_lock(hdev);
-       hci_discovery_set_state(hdev, DISCOVERY_FINDING);
-       hci_dev_unlock(hdev);
 }
 
 static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
@@ -2298,10 +2243,6 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_user_passkey_neg_reply(hdev, skb);
                break;
 
-       case HCI_OP_LE_SET_SCAN_PARAM:
-               hci_cc_le_set_scan_param(hdev, skb);
-               break;
-
        case HCI_OP_LE_SET_ADV_ENABLE:
                hci_cc_le_set_adv_enable(hdev, skb);
                break;
@@ -2670,7 +2611,7 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s", hdev->name);
 
-       if (!test_bit(HCI_LINK_KEYS, &hdev->dev_flags))
+       if (!test_bit(HCI_MGMT, &hdev->dev_flags))
                return;
 
        hci_dev_lock(hdev);
@@ -2746,7 +2687,7 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_conn_drop(conn);
        }
 
-       if (test_bit(HCI_LINK_KEYS, &hdev->dev_flags))
+       if (test_bit(HCI_MGMT, &hdev->dev_flags))
                hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key,
                                 ev->key_type, pin_len);
 
index 46c6a14..0c699cd 100644 (file)
@@ -76,25 +76,19 @@ static void hidp_copy_session(struct hidp_session *session, struct hidp_conninfo
        ci->flags = session->flags;
        ci->state = BT_CONNECTED;
 
-       ci->vendor  = 0x0000;
-       ci->product = 0x0000;
-       ci->version = 0x0000;
-
        if (session->input) {
                ci->vendor  = session->input->id.vendor;
                ci->product = session->input->id.product;
                ci->version = session->input->id.version;
                if (session->input->name)
-                       strncpy(ci->name, session->input->name, 128);
+                       strlcpy(ci->name, session->input->name, 128);
                else
-                       strncpy(ci->name, "HID Boot Device", 128);
-       }
-
-       if (session->hid) {
+                       strlcpy(ci->name, "HID Boot Device", 128);
+       } else if (session->hid) {
                ci->vendor  = session->hid->vendor;
                ci->product = session->hid->product;
                ci->version = session->hid->version;
-               strncpy(ci->name, session->hid->name, 128);
+               strlcpy(ci->name, session->hid->name, 128);
        }
 }
 
index 68843a2..8c3499b 100644 (file)
@@ -504,8 +504,10 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
                if (conn->hcon->type == LE_LINK) {
                        /* LE connection */
                        chan->omtu = L2CAP_DEFAULT_MTU;
-                       chan->scid = L2CAP_CID_LE_DATA;
-                       chan->dcid = L2CAP_CID_LE_DATA;
+                       if (chan->dcid == L2CAP_CID_ATT)
+                               chan->scid = L2CAP_CID_ATT;
+                       else
+                               chan->scid = l2cap_alloc_cid(conn);
                } else {
                        /* Alloc CID for connection-oriented socket */
                        chan->scid = l2cap_alloc_cid(conn);
@@ -543,6 +545,8 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
 
        l2cap_chan_hold(chan);
 
+       hci_conn_hold(conn->hcon);
+
        list_add(&chan->list, &conn->chan_l);
 }
 
@@ -1338,17 +1342,21 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
 
 static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 {
-       struct sock *parent, *sk;
+       struct sock *parent;
        struct l2cap_chan *chan, *pchan;
 
        BT_DBG("");
 
        /* Check if we have socket listening on cid */
-       pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA,
+       pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
                                          conn->src, conn->dst);
        if (!pchan)
                return;
 
+       /* Client ATT sockets should override the server one */
+       if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT))
+               return;
+
        parent = pchan->sk;
 
        lock_sock(parent);
@@ -1357,17 +1365,12 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
        if (!chan)
                goto clean;
 
-       sk = chan->sk;
-
-       hci_conn_hold(conn->hcon);
-       conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
-
-       bacpy(&bt_sk(sk)->src, conn->src);
-       bacpy(&bt_sk(sk)->dst, conn->dst);
+       chan->dcid = L2CAP_CID_ATT;
 
-       l2cap_chan_add(conn, chan);
+       bacpy(&bt_sk(chan->sk)->src, conn->src);
+       bacpy(&bt_sk(chan->sk)->dst, conn->dst);
 
-       l2cap_chan_ready(chan);
+       __l2cap_chan_add(conn, chan);
 
 clean:
        release_sock(parent);
@@ -1380,14 +1383,17 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
 
        BT_DBG("conn %p", conn);
 
-       if (!hcon->out && hcon->type == LE_LINK)
-               l2cap_le_conn_ready(conn);
-
+       /* For outgoing pairing which doesn't necessarily have an
+        * associated socket (e.g. mgmt_pair_device).
+        */
        if (hcon->out && hcon->type == LE_LINK)
                smp_conn_security(hcon, hcon->pending_sec_level);
 
        mutex_lock(&conn->chan_lock);
 
+       if (hcon->type == LE_LINK)
+               l2cap_le_conn_ready(conn);
+
        list_for_each_entry(chan, &conn->chan_l, list) {
 
                l2cap_chan_lock(chan);
@@ -1792,7 +1798,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
 
        auth_type = l2cap_get_auth_type(chan);
 
-       if (chan->dcid == L2CAP_CID_LE_DATA)
+       if (bdaddr_type_is_le(dst_type))
                hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
                                   chan->sec_level, auth_type);
        else
@@ -1811,16 +1817,10 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                goto done;
        }
 
-       if (hcon->type == LE_LINK) {
-               err = 0;
-
-               if (!list_empty(&conn->chan_l)) {
-                       err = -EBUSY;
-                       hci_conn_drop(hcon);
-               }
-
-               if (err)
-                       goto done;
+       if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
+               hci_conn_drop(hcon);
+               err = -EBUSY;
+               goto done;
        }
 
        /* Update source addr of the socket */
@@ -1830,6 +1830,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        l2cap_chan_add(conn, chan);
        l2cap_chan_lock(chan);
 
+       /* l2cap_chan_add takes its own ref so we can drop this one */
+       hci_conn_drop(hcon);
+
        l2cap_state_change(chan, BT_CONNECT);
        __set_chan_timer(chan, sk->sk_sndtimeo);
 
@@ -3751,8 +3754,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
 
        sk = chan->sk;
 
-       hci_conn_hold(conn->hcon);
-
        bacpy(&bt_sk(sk)->src, conn->src);
        bacpy(&bt_sk(sk)->dst, conn->dst);
        chan->psm  = psm;
@@ -5292,6 +5293,51 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
        }
 }
 
+static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
+                                       struct sk_buff *skb)
+{
+       u8 *data = skb->data;
+       int len = skb->len;
+       struct l2cap_cmd_hdr cmd;
+       int err;
+
+       l2cap_raw_recv(conn, skb);
+
+       while (len >= L2CAP_CMD_HDR_SIZE) {
+               u16 cmd_len;
+               memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
+               data += L2CAP_CMD_HDR_SIZE;
+               len  -= L2CAP_CMD_HDR_SIZE;
+
+               cmd_len = le16_to_cpu(cmd.len);
+
+               BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len,
+                      cmd.ident);
+
+               if (cmd_len > len || !cmd.ident) {
+                       BT_DBG("corrupted command");
+                       break;
+               }
+
+               err = l2cap_le_sig_cmd(conn, &cmd, data);
+               if (err) {
+                       struct l2cap_cmd_rej_unk rej;
+
+                       BT_ERR("Wrong link type (%d)", err);
+
+                       /* FIXME: Map err to a valid reason */
+                       rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
+                       l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
+                                      sizeof(rej), &rej);
+               }
+
+               data += cmd_len;
+               len  -= cmd_len;
+       }
+
+       kfree_skb(skb);
+}
+
 static inline void l2cap_sig_channel(struct l2cap_conn *conn,
                                     struct sk_buff *skb)
 {
@@ -5318,11 +5364,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
                        break;
                }
 
-               if (conn->hcon->type == LE_LINK)
-                       err = l2cap_le_sig_cmd(conn, &cmd, data);
-               else
-                       err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
-
+               err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
                if (err) {
                        struct l2cap_cmd_rej_unk rej;
 
@@ -6356,16 +6398,13 @@ static void l2cap_att_channel(struct l2cap_conn *conn,
 {
        struct l2cap_chan *chan;
 
-       chan = l2cap_global_chan_by_scid(0, L2CAP_CID_LE_DATA,
+       chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
                                         conn->src, conn->dst);
        if (!chan)
                goto drop;
 
        BT_DBG("chan %p, len %d", chan, skb->len);
 
-       if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
-               goto drop;
-
        if (chan->imtu < skb->len)
                goto drop;
 
@@ -6395,6 +6434,8 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
 
        switch (cid) {
        case L2CAP_CID_LE_SIGNALING:
+               l2cap_le_sig_channel(conn, skb);
+               break;
        case L2CAP_CID_SIGNALING:
                l2cap_sig_channel(conn, skb);
                break;
@@ -6405,7 +6446,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
                l2cap_conless_channel(conn, psm, skb);
                break;
 
-       case L2CAP_CID_LE_DATA:
+       case L2CAP_CID_ATT:
                l2cap_att_channel(conn, skb);
                break;
 
@@ -6531,7 +6572,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
                        continue;
                }
 
-               if (chan->scid == L2CAP_CID_LE_DATA) {
+               if (chan->scid == L2CAP_CID_ATT) {
                        if (!status && encrypt) {
                                chan->sec_level = hcon->sec_level;
                                l2cap_chan_ready(chan);
index 36fed40..0098af8 100644 (file)
@@ -466,7 +466,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
 static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu)
 {
        switch (chan->scid) {
-       case L2CAP_CID_LE_DATA:
+       case L2CAP_CID_ATT:
                if (mtu < L2CAP_LE_MIN_MTU)
                        return false;
                break;
@@ -630,7 +630,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
                conn = chan->conn;
 
                /*change security for LE channels */
-               if (chan->scid == L2CAP_CID_LE_DATA) {
+               if (chan->scid == L2CAP_CID_ATT) {
                        if (!conn->hcon->out) {
                                err = -EINVAL;
                                break;
index f8ecbc7..fedc539 100644 (file)
@@ -102,18 +102,6 @@ static const u16 mgmt_events[] = {
        MGMT_EV_PASSKEY_NOTIFY,
 };
 
-/*
- * These LE scan and inquiry parameters were chosen according to LE General
- * Discovery Procedure specification.
- */
-#define LE_SCAN_WIN                    0x12
-#define LE_SCAN_INT                    0x12
-#define LE_SCAN_TIMEOUT_LE_ONLY                msecs_to_jiffies(10240)
-#define LE_SCAN_TIMEOUT_BREDR_LE       msecs_to_jiffies(5120)
-
-#define INQUIRY_LEN_BREDR              0x08    /* TGAP(100) */
-#define INQUIRY_LEN_BREDR_LE           0x04    /* TGAP(100)/2 */
-
 #define CACHE_TIMEOUT  msecs_to_jiffies(2 * 1000)
 
 #define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
@@ -1748,8 +1736,6 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
 
        hci_link_keys_clear(hdev);
 
-       set_bit(HCI_LINK_KEYS, &hdev->dev_flags);
-
        if (cp->debug_keys)
                set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
        else
@@ -2633,28 +2619,72 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
        return err;
 }
 
-int mgmt_interleaved_discovery(struct hci_dev *hdev)
+static int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
 {
+       struct pending_cmd *cmd;
+       u8 type;
        int err;
 
-       BT_DBG("%s", hdev->name);
+       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
 
-       hci_dev_lock(hdev);
+       cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
+       if (!cmd)
+               return -ENOENT;
 
-       err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE);
-       if (err < 0)
-               hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+       type = hdev->discovery.type;
 
-       hci_dev_unlock(hdev);
+       err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
+                          &type, sizeof(type));
+       mgmt_pending_remove(cmd);
 
        return err;
 }
 
+static void start_discovery_complete(struct hci_dev *hdev, u8 status)
+{
+       BT_DBG("status %d", status);
+
+       if (status) {
+               hci_dev_lock(hdev);
+               mgmt_start_discovery_failed(hdev, status);
+               hci_dev_unlock(hdev);
+               return;
+       }
+
+       hci_dev_lock(hdev);
+       hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+       hci_dev_unlock(hdev);
+
+       switch (hdev->discovery.type) {
+       case DISCOV_TYPE_LE:
+               queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
+                                  DISCOV_LE_TIMEOUT);
+               break;
+
+       case DISCOV_TYPE_INTERLEAVED:
+               queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
+                                  DISCOV_INTERLEAVED_TIMEOUT);
+               break;
+
+       case DISCOV_TYPE_BREDR:
+               break;
+
+       default:
+               BT_ERR("Invalid discovery type %d", hdev->discovery.type);
+       }
+}
+
 static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                           void *data, u16 len)
 {
        struct mgmt_cp_start_discovery *cp = data;
        struct pending_cmd *cmd;
+       struct hci_cp_le_set_scan_param param_cp;
+       struct hci_cp_le_set_scan_enable enable_cp;
+       struct hci_cp_inquiry inq_cp;
+       struct hci_request req;
+       /* General inquiry access code (GIAC) */
+       u8 lap[3] = { 0x33, 0x8b, 0x9e };
        int err;
 
        BT_DBG("%s", hdev->name);
@@ -2687,6 +2717,8 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
 
        hdev->discovery.type = cp->type;
 
+       hci_req_init(&req, hdev);
+
        switch (hdev->discovery.type) {
        case DISCOV_TYPE_BREDR:
                if (!lmp_bredr_capable(hdev)) {
@@ -2696,10 +2728,23 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                        goto failed;
                }
 
-               err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
+               if (test_bit(HCI_INQUIRY, &hdev->flags)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                        MGMT_STATUS_BUSY);
+                       mgmt_pending_remove(cmd);
+                       goto failed;
+               }
+
+               hci_inquiry_cache_flush(hdev);
+
+               memset(&inq_cp, 0, sizeof(inq_cp));
+               memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap));
+               inq_cp.length = DISCOV_BREDR_INQUIRY_LEN;
+               hci_req_add(&req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp);
                break;
 
        case DISCOV_TYPE_LE:
+       case DISCOV_TYPE_INTERLEAVED:
                if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
                        err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
                                         MGMT_STATUS_NOT_SUPPORTED);
@@ -2707,20 +2752,40 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                        goto failed;
                }
 
-               err = hci_le_scan(hdev, LE_SCAN_ACTIVE, LE_SCAN_INT,
-                                 LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
-               break;
-
-       case DISCOV_TYPE_INTERLEAVED:
-               if (!lmp_host_le_capable(hdev) || !lmp_bredr_capable(hdev)) {
+               if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
+                   !lmp_bredr_capable(hdev)) {
                        err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
                                         MGMT_STATUS_NOT_SUPPORTED);
                        mgmt_pending_remove(cmd);
                        goto failed;
                }
 
-               err = hci_le_scan(hdev, LE_SCAN_ACTIVE, LE_SCAN_INT,
-                                 LE_SCAN_WIN, LE_SCAN_TIMEOUT_BREDR_LE);
+               if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                        MGMT_STATUS_REJECTED);
+                       mgmt_pending_remove(cmd);
+                       goto failed;
+               }
+
+               if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                        MGMT_STATUS_BUSY);
+                       mgmt_pending_remove(cmd);
+                       goto failed;
+               }
+
+               memset(&param_cp, 0, sizeof(param_cp));
+               param_cp.type = LE_SCAN_ACTIVE;
+               param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
+               param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
+               hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
+                           &param_cp);
+
+               memset(&enable_cp, 0, sizeof(enable_cp));
+               enable_cp.enable = LE_SCAN_ENABLE;
+               enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+               hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
+                           &enable_cp);
                break;
 
        default:
@@ -2730,6 +2795,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                goto failed;
        }
 
+       err = hci_req_run(&req, start_discovery_complete);
        if (err < 0)
                mgmt_pending_remove(cmd);
        else
@@ -2740,6 +2806,39 @@ failed:
        return err;
 }
 
+static int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
+{
+       struct pending_cmd *cmd;
+       int err;
+
+       cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
+       if (!cmd)
+               return -ENOENT;
+
+       err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
+                          &hdev->discovery.type, sizeof(hdev->discovery.type));
+       mgmt_pending_remove(cmd);
+
+       return err;
+}
+
+static void stop_discovery_complete(struct hci_dev *hdev, u8 status)
+{
+       BT_DBG("status %d", status);
+
+       hci_dev_lock(hdev);
+
+       if (status) {
+               mgmt_stop_discovery_failed(hdev, status);
+               goto unlock;
+       }
+
+       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
 static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
                          u16 len)
 {
@@ -2747,6 +2846,8 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
        struct pending_cmd *cmd;
        struct hci_cp_remote_name_req_cancel cp;
        struct inquiry_entry *e;
+       struct hci_request req;
+       struct hci_cp_le_set_scan_enable enable_cp;
        int err;
 
        BT_DBG("%s", hdev->name);
@@ -2773,12 +2874,20 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
+       hci_req_init(&req, hdev);
+
        switch (hdev->discovery.state) {
        case DISCOVERY_FINDING:
-               if (test_bit(HCI_INQUIRY, &hdev->flags))
-                       err = hci_cancel_inquiry(hdev);
-               else
-                       err = hci_cancel_le_scan(hdev);
+               if (test_bit(HCI_INQUIRY, &hdev->flags)) {
+                       hci_req_add(&req, HCI_OP_INQUIRY_CANCEL, 0, NULL);
+               } else {
+                       cancel_delayed_work(&hdev->le_scan_disable);
+
+                       memset(&enable_cp, 0, sizeof(enable_cp));
+                       enable_cp.enable = LE_SCAN_DISABLE;
+                       hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE,
+                                   sizeof(enable_cp), &enable_cp);
+               }
 
                break;
 
@@ -2796,16 +2905,22 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
                }
 
                bacpy(&cp.bdaddr, &e->data.bdaddr);
-               err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL,
-                                  sizeof(cp), &cp);
+               hci_req_add(&req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp),
+                           &cp);
 
                break;
 
        default:
                BT_DBG("unknown discovery state %u", hdev->discovery.state);
-               err = -EFAULT;
+
+               mgmt_pending_remove(cmd);
+               err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
+                                  MGMT_STATUS_FAILED, &mgmt_cp->type,
+                                  sizeof(mgmt_cp->type));
+               goto unlock;
        }
 
+       err = hci_req_run(&req, stop_discovery_complete);
        if (err < 0)
                mgmt_pending_remove(cmd);
        else
@@ -4063,6 +4178,9 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        struct mgmt_ev_device_found *ev = (void *) buf;
        size_t ev_size;
 
+       if (!hci_discovery_active(hdev))
+               return -EPERM;
+
        /* Leave 5 bytes for a potential CoD field */
        if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
                return -EINVAL;
@@ -4114,43 +4232,6 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                          sizeof(*ev) + eir_len, NULL);
 }
 
-int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
-{
-       struct pending_cmd *cmd;
-       u8 type;
-       int err;
-
-       hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-
-       cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
-       if (!cmd)
-               return -ENOENT;
-
-       type = hdev->discovery.type;
-
-       err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
-                          &type, sizeof(type));
-       mgmt_pending_remove(cmd);
-
-       return err;
-}
-
-int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
-{
-       struct pending_cmd *cmd;
-       int err;
-
-       cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
-       if (!cmd)
-               return -ENOENT;
-
-       err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
-                          &hdev->discovery.type, sizeof(hdev->discovery.type));
-       mgmt_pending_remove(cmd);
-
-       return err;
-}
-
 int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
 {
        struct mgmt_ev_discovering ev;
index 9673128..2ef6678 100644 (file)
@@ -22,6 +22,9 @@
 #include <asm/uaccess.h>
 #include "br_private.h"
 
+#define COMMON_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | \
+                        NETIF_F_GSO_MASK | NETIF_F_HW_CSUM)
+
 /* net device transmit always called with BH disabled */
 netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 {
@@ -55,10 +58,10 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
        skb_pull(skb, ETH_HLEN);
 
        if (is_broadcast_ether_addr(dest))
-               br_flood_deliver(br, skb);
+               br_flood_deliver(br, skb, false);
        else if (is_multicast_ether_addr(dest)) {
                if (unlikely(netpoll_tx_running(dev))) {
-                       br_flood_deliver(br, skb);
+                       br_flood_deliver(br, skb, false);
                        goto out;
                }
                if (br_multicast_rcv(br, NULL, skb)) {
@@ -70,11 +73,11 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
                if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb))
                        br_multicast_deliver(mdst, skb);
                else
-                       br_flood_deliver(br, skb);
+                       br_flood_deliver(br, skb, false);
        } else if ((dst = __br_fdb_get(br, dest, vid)) != NULL)
                br_deliver(dst->dst, skb);
        else
-               br_flood_deliver(br, skb);
+               br_flood_deliver(br, skb, true);
 
 out:
        rcu_read_unlock();
@@ -346,12 +349,10 @@ void br_dev_setup(struct net_device *dev)
        dev->tx_queue_len = 0;
        dev->priv_flags = IFF_EBRIDGE;
 
-       dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
-                       NETIF_F_GSO_MASK | NETIF_F_HW_CSUM | NETIF_F_LLTX |
-                       NETIF_F_NETNS_LOCAL | NETIF_F_HW_VLAN_CTAG_TX;
-       dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
-                          NETIF_F_GSO_MASK | NETIF_F_HW_CSUM |
-                          NETIF_F_HW_VLAN_CTAG_TX;
+       dev->features = COMMON_FEATURES | NETIF_F_LLTX | NETIF_F_NETNS_LOCAL |
+                       NETIF_F_HW_VLAN_CTAG_TX;
+       dev->hw_features = COMMON_FEATURES | NETIF_F_HW_VLAN_CTAG_TX;
+       dev->vlan_features = COMMON_FEATURES;
 
        br->dev = dev;
        spin_lock_init(&br->lock);
index ebfa444..60aca91 100644 (file)
@@ -707,6 +707,11 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
                }
        }
 
+       if (is_zero_ether_addr(addr)) {
+               pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n");
+               return -EINVAL;
+       }
+
        p = br_port_get_rtnl(dev);
        if (p == NULL) {
                pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n",
index 092b20e..4b81b14 100644 (file)
@@ -174,7 +174,8 @@ out:
 static void br_flood(struct net_bridge *br, struct sk_buff *skb,
                     struct sk_buff *skb0,
                     void (*__packet_hook)(const struct net_bridge_port *p,
-                                          struct sk_buff *skb))
+                                          struct sk_buff *skb),
+                    bool unicast)
 {
        struct net_bridge_port *p;
        struct net_bridge_port *prev;
@@ -182,6 +183,9 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
        prev = NULL;
 
        list_for_each_entry_rcu(p, &br->port_list, list) {
+               /* Do not flood unicast traffic to ports that turn it off */
+               if (unicast && !(p->flags & BR_FLOOD))
+                       continue;
                prev = maybe_deliver(prev, p, skb, __packet_hook);
                if (IS_ERR(prev))
                        goto out;
@@ -203,16 +207,16 @@ out:
 
 
 /* called with rcu_read_lock */
-void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb)
+void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, bool unicast)
 {
-       br_flood(br, skb, NULL, __br_deliver);
+       br_flood(br, skb, NULL, __br_deliver, unicast);
 }
 
 /* called under bridge lock */
 void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
-                     struct sk_buff *skb2)
+                     struct sk_buff *skb2, bool unicast)
 {
-       br_flood(br, skb, skb2, __br_forward);
+       br_flood(br, skb, skb2, __br_forward, unicast);
 }
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
index 4cdba60..5623be6 100644 (file)
@@ -221,7 +221,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
        p->path_cost = port_cost(dev);
        p->priority = 0x8000 >> BR_PORT_BITS;
        p->port_no = index;
-       p->flags = 0;
+       p->flags = BR_LEARNING | BR_FLOOD;
        br_init_port(p);
        p->state = BR_STATE_DISABLED;
        br_stp_port_timer_init(p);
index 828e2bc..1b8b8b8 100644 (file)
@@ -65,6 +65,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
        struct net_bridge_fdb_entry *dst;
        struct net_bridge_mdb_entry *mdst;
        struct sk_buff *skb2;
+       bool unicast = true;
        u16 vid = 0;
 
        if (!p || p->state == BR_STATE_DISABLED)
@@ -75,7 +76,8 @@ int br_handle_frame_finish(struct sk_buff *skb)
 
        /* insert into forwarding database after filtering to avoid spoofing */
        br = p->br;
-       br_fdb_update(br, p, eth_hdr(skb)->h_source, vid);
+       if (p->flags & BR_LEARNING)
+               br_fdb_update(br, p, eth_hdr(skb)->h_source, vid);
 
        if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) &&
            br_multicast_rcv(br, p, skb))
@@ -94,9 +96,10 @@ int br_handle_frame_finish(struct sk_buff *skb)
 
        dst = NULL;
 
-       if (is_broadcast_ether_addr(dest))
+       if (is_broadcast_ether_addr(dest)) {
                skb2 = skb;
-       else if (is_multicast_ether_addr(dest)) {
+               unicast = false;
+       } else if (is_multicast_ether_addr(dest)) {
                mdst = br_mdb_get(br, skb, vid);
                if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) {
                        if ((mdst && mdst->mglist) ||
@@ -109,6 +112,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
                } else
                        skb2 = skb;
 
+               unicast = false;
                br->dev->stats.multicast++;
        } else if ((dst = __br_fdb_get(br, dest, vid)) &&
                        dst->is_local) {
@@ -122,7 +126,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
                        dst->used = jiffies;
                        br_forward(dst->dst, skb, skb2);
                } else
-                       br_flood_forward(br, skb, skb2);
+                       br_flood_forward(br, skb, skb2, unicast);
        }
 
        if (skb2)
@@ -142,7 +146,8 @@ static int br_handle_local_finish(struct sk_buff *skb)
        u16 vid = 0;
 
        br_vlan_get_tag(skb, &vid);
-       br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid);
+       if (p->flags & BR_LEARNING)
+               br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid);
        return 0;        /* process further */
 }
 
index 19942e3..0daae3e 100644 (file)
@@ -447,7 +447,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
                call_rcu_bh(&p->rcu, br_multicast_free_pg);
                err = 0;
 
-               if (!mp->ports && !mp->mglist &&
+               if (!mp->ports && !mp->mglist && mp->timer_armed &&
                    netif_running(br->dev))
                        mod_timer(&mp->timer, jiffies);
                break;
index d6448e3..69af490 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/skbuff.h>
 #include <linux/slab.h>
 #include <linux/timer.h>
+#include <linux/inetdevice.h>
 #include <net/ip.h>
 #if IS_ENABLED(CONFIG_IPV6)
 #include <net/ipv6.h>
@@ -269,7 +270,7 @@ static void br_multicast_del_pg(struct net_bridge *br,
                del_timer(&p->timer);
                call_rcu_bh(&p->rcu, br_multicast_free_pg);
 
-               if (!mp->ports && !mp->mglist &&
+               if (!mp->ports && !mp->mglist && mp->timer_armed &&
                    netif_running(br->dev))
                        mod_timer(&mp->timer, jiffies);
 
@@ -381,7 +382,8 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
        iph->frag_off = htons(IP_DF);
        iph->ttl = 1;
        iph->protocol = IPPROTO_IGMP;
-       iph->saddr = 0;
+       iph->saddr = br->multicast_query_use_ifaddr ?
+                    inet_select_addr(br->dev, 0, RT_SCOPE_LINK) : 0;
        iph->daddr = htonl(INADDR_ALLHOSTS_GROUP);
        ((u8 *)&iph[1])[0] = IPOPT_RA;
        ((u8 *)&iph[1])[1] = 4;
@@ -616,8 +618,6 @@ rehash:
 
        mp->br = br;
        mp->addr = *group;
-       setup_timer(&mp->timer, br_multicast_group_expired,
-                   (unsigned long)mp);
 
        hlist_add_head_rcu(&mp->hlist[mdb->ver], &mdb->mhash[hash]);
        mdb->size++;
@@ -655,7 +655,6 @@ static int br_multicast_add_group(struct net_bridge *br,
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
-       unsigned long now = jiffies;
        int err;
 
        spin_lock(&br->multicast_lock);
@@ -670,7 +669,6 @@ static int br_multicast_add_group(struct net_bridge *br,
 
        if (!port) {
                mp->mglist = true;
-               mod_timer(&mp->timer, now + br->multicast_membership_interval);
                goto out;
        }
 
@@ -678,7 +676,7 @@ static int br_multicast_add_group(struct net_bridge *br,
             (p = mlock_dereference(*pp, br)) != NULL;
             pp = &p->next) {
                if (p->port == port)
-                       goto found;
+                       goto out;
                if ((unsigned long)p->port < (unsigned long)port)
                        break;
        }
@@ -689,8 +687,6 @@ static int br_multicast_add_group(struct net_bridge *br,
        rcu_assign_pointer(*pp, p);
        br_mdb_notify(br->dev, port, group, RTM_NEWMDB);
 
-found:
-       mod_timer(&p->timer, now + br->multicast_membership_interval);
 out:
        err = 0;
 
@@ -1016,7 +1012,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
 #endif
 
 /*
- * Add port to rotuer_list
+ * Add port to router_list
  *  list is maintained ordered by pointer value
  *  and locked by br->multicast_lock and RCU
  */
@@ -1130,6 +1126,10 @@ static int br_ip4_multicast_query(struct net_bridge *br,
        if (!mp)
                goto out;
 
+       setup_timer(&mp->timer, br_multicast_group_expired, (unsigned long)mp);
+       mod_timer(&mp->timer, now + br->multicast_membership_interval);
+       mp->timer_armed = true;
+
        max_delay *= br->multicast_last_member_count;
 
        if (mp->mglist &&
@@ -1204,6 +1204,10 @@ static int br_ip6_multicast_query(struct net_bridge *br,
        if (!mp)
                goto out;
 
+       setup_timer(&mp->timer, br_multicast_group_expired, (unsigned long)mp);
+       mod_timer(&mp->timer, now + br->multicast_membership_interval);
+       mp->timer_armed = true;
+
        max_delay *= br->multicast_last_member_count;
        if (mp->mglist &&
            (timer_pending(&mp->timer) ?
@@ -1247,6 +1251,32 @@ static void br_multicast_leave_group(struct net_bridge *br,
        if (!mp)
                goto out;
 
+       if (br->multicast_querier &&
+           !timer_pending(&br->multicast_querier_timer)) {
+               __br_multicast_send_query(br, port, &mp->addr);
+
+               time = jiffies + br->multicast_last_member_count *
+                                br->multicast_last_member_interval;
+               mod_timer(port ? &port->multicast_query_timer :
+                                &br->multicast_query_timer, time);
+
+               for (p = mlock_dereference(mp->ports, br);
+                    p != NULL;
+                    p = mlock_dereference(p->next, br)) {
+                       if (p->port != port)
+                               continue;
+
+                       if (!hlist_unhashed(&p->mglist) &&
+                           (timer_pending(&p->timer) ?
+                            time_after(p->timer.expires, time) :
+                            try_to_del_timer_sync(&p->timer) >= 0)) {
+                               mod_timer(&p->timer, time);
+                       }
+
+                       break;
+               }
+       }
+
        if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) {
                struct net_bridge_port_group __rcu **pp;
 
@@ -1262,7 +1292,7 @@ static void br_multicast_leave_group(struct net_bridge *br,
                        call_rcu_bh(&p->rcu, br_multicast_free_pg);
                        br_mdb_notify(br->dev, port, group, RTM_DELMDB);
 
-                       if (!mp->ports && !mp->mglist &&
+                       if (!mp->ports && !mp->mglist && mp->timer_armed &&
                            netif_running(br->dev))
                                mod_timer(&mp->timer, jiffies);
                }
@@ -1274,30 +1304,12 @@ static void br_multicast_leave_group(struct net_bridge *br,
                     br->multicast_last_member_interval;
 
        if (!port) {
-               if (mp->mglist &&
+               if (mp->mglist && mp->timer_armed &&
                    (timer_pending(&mp->timer) ?
                     time_after(mp->timer.expires, time) :
                     try_to_del_timer_sync(&mp->timer) >= 0)) {
                        mod_timer(&mp->timer, time);
                }
-
-               goto out;
-       }
-
-       for (p = mlock_dereference(mp->ports, br);
-            p != NULL;
-            p = mlock_dereference(p->next, br)) {
-               if (p->port != port)
-                       continue;
-
-               if (!hlist_unhashed(&p->mglist) &&
-                   (timer_pending(&p->timer) ?
-                    time_after(p->timer.expires, time) :
-                    try_to_del_timer_sync(&p->timer) >= 0)) {
-                       mod_timer(&p->timer, time);
-               }
-
-               break;
        }
 
 out:
@@ -1619,6 +1631,7 @@ void br_multicast_init(struct net_bridge *br)
 
        br->multicast_router = 1;
        br->multicast_querier = 0;
+       br->multicast_query_use_ifaddr = 0;
        br->multicast_last_member_count = 2;
        br->multicast_startup_query_count = 2;
 
@@ -1672,6 +1685,7 @@ void br_multicast_stop(struct net_bridge *br)
                hlist_for_each_entry_safe(mp, n, &mdb->mhash[i],
                                          hlist[ver]) {
                        del_timer(&mp->timer);
+                       mp->timer_armed = false;
                        call_rcu_bh(&mp->rcu, br_multicast_free_group);
                }
        }
index 1ed75bf..f877362 100644 (file)
@@ -992,7 +992,7 @@ static struct nf_hook_ops br_nf_ops[] __read_mostly = {
 
 #ifdef CONFIG_SYSCTL
 static
-int brnf_sysctl_call_tables(ctl_table * ctl, int write,
+int brnf_sysctl_call_tables(struct ctl_table *ctl, int write,
                            void __user * buffer, size_t * lenp, loff_t * ppos)
 {
        int ret;
@@ -1004,7 +1004,7 @@ int brnf_sysctl_call_tables(ctl_table * ctl, int write,
        return ret;
 }
 
-static ctl_table brnf_table[] = {
+static struct ctl_table brnf_table[] = {
        {
                .procname       = "bridge-nf-call-arptables",
                .data           = &brnf_call_arptables,
index 8e3abf5..1fc30ab 100644 (file)
@@ -30,6 +30,8 @@ static inline size_t br_port_info_size(void)
                + nla_total_size(1)     /* IFLA_BRPORT_GUARD */
                + nla_total_size(1)     /* IFLA_BRPORT_PROTECT */
                + nla_total_size(1)     /* IFLA_BRPORT_FAST_LEAVE */
+               + nla_total_size(1)     /* IFLA_BRPORT_LEARNING */
+               + nla_total_size(1)     /* IFLA_BRPORT_UNICAST_FLOOD */
                + 0;
 }
 
@@ -56,7 +58,9 @@ static int br_port_fill_attrs(struct sk_buff *skb,
            nla_put_u8(skb, IFLA_BRPORT_MODE, mode) ||
            nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(p->flags & BR_BPDU_GUARD)) ||
            nla_put_u8(skb, IFLA_BRPORT_PROTECT, !!(p->flags & BR_ROOT_BLOCK)) ||
-           nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, !!(p->flags & BR_MULTICAST_FAST_LEAVE)))
+           nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, !!(p->flags & BR_MULTICAST_FAST_LEAVE)) ||
+           nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING)) ||
+           nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD)))
                return -EMSGSIZE;
 
        return 0;
@@ -281,6 +285,8 @@ static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = {
        [IFLA_BRPORT_MODE]      = { .type = NLA_U8 },
        [IFLA_BRPORT_GUARD]     = { .type = NLA_U8 },
        [IFLA_BRPORT_PROTECT]   = { .type = NLA_U8 },
+       [IFLA_BRPORT_LEARNING]  = { .type = NLA_U8 },
+       [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 },
 };
 
 /* Change the state of the port and notify spanning tree */
@@ -328,6 +334,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
        br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
        br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, BR_MULTICAST_FAST_LEAVE);
        br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK);
+       br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING);
+       br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD);
 
        if (tb[IFLA_BRPORT_COST]) {
                err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
index 1644b3e..3a3f371 100644 (file)
@@ -31,7 +31,7 @@ struct notifier_block br_device_notifier = {
  */
 static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net_bridge_port *p;
        struct net_bridge *br;
        bool changed_addr;
index d2c043a..3be89b3 100644 (file)
@@ -112,6 +112,7 @@ struct net_bridge_mdb_entry
        struct timer_list               timer;
        struct br_ip                    addr;
        bool                            mglist;
+       bool                            timer_armed;
 };
 
 struct net_bridge_mdb_htable
@@ -157,6 +158,8 @@ struct net_bridge_port
 #define BR_ROOT_BLOCK          0x00000004
 #define BR_MULTICAST_FAST_LEAVE        0x00000008
 #define BR_ADMIN_COST          0x00000010
+#define BR_LEARNING            0x00000020
+#define BR_FLOOD               0x00000040
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
        u32                             multicast_startup_queries_sent;
@@ -249,6 +252,7 @@ struct net_bridge
 
        u8                              multicast_disabled:1;
        u8                              multicast_querier:1;
+       u8                              multicast_query_use_ifaddr:1;
 
        u32                             hash_elasticity;
        u32                             hash_max;
@@ -411,9 +415,10 @@ extern int br_dev_queue_push_xmit(struct sk_buff *skb);
 extern void br_forward(const struct net_bridge_port *to,
                struct sk_buff *skb, struct sk_buff *skb0);
 extern int br_forward_finish(struct sk_buff *skb);
-extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb);
+extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb,
+                            bool unicast);
 extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
-                            struct sk_buff *skb2);
+                            struct sk_buff *skb2, bool unicast);
 
 /* br_if.c */
 extern void br_port_carrier_check(struct net_bridge_port *p);
index 8baa9c0..394bb96 100644 (file)
@@ -375,6 +375,31 @@ static ssize_t store_multicast_snooping(struct device *d,
 static DEVICE_ATTR(multicast_snooping, S_IRUGO | S_IWUSR,
                   show_multicast_snooping, store_multicast_snooping);
 
+static ssize_t show_multicast_query_use_ifaddr(struct device *d,
+                                     struct device_attribute *attr,
+                                     char *buf)
+{
+       struct net_bridge *br = to_bridge(d);
+       return sprintf(buf, "%d\n", br->multicast_query_use_ifaddr);
+}
+
+static int set_query_use_ifaddr(struct net_bridge *br, unsigned long val)
+{
+       br->multicast_query_use_ifaddr = !!val;
+       return 0;
+}
+
+static ssize_t
+store_multicast_query_use_ifaddr(struct device *d,
+                                struct device_attribute *attr,
+                                const char *buf, size_t len)
+{
+       return store_bridge_parm(d, buf, len, set_query_use_ifaddr);
+}
+static DEVICE_ATTR(multicast_query_use_ifaddr, S_IRUGO | S_IWUSR,
+                  show_multicast_query_use_ifaddr,
+                  store_multicast_query_use_ifaddr);
+
 static ssize_t show_multicast_querier(struct device *d,
                                      struct device_attribute *attr,
                                      char *buf)
@@ -734,6 +759,7 @@ static struct attribute *bridge_attrs[] = {
        &dev_attr_multicast_router.attr,
        &dev_attr_multicast_snooping.attr,
        &dev_attr_multicast_querier.attr,
+       &dev_attr_multicast_query_use_ifaddr.attr,
        &dev_attr_hash_elasticity.attr,
        &dev_attr_hash_max.attr,
        &dev_attr_multicast_last_member_count.attr,
index a1ef1b6..2a2cdb7 100644 (file)
@@ -158,6 +158,8 @@ static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush);
 BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE);
 BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD);
 BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK);
+BRPORT_ATTR_FLAG(learning, BR_LEARNING);
+BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD);
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
@@ -195,6 +197,8 @@ static const struct brport_attribute *brport_attrs[] = {
        &brport_attr_hairpin_mode,
        &brport_attr_bpdu_guard,
        &brport_attr_root_block,
+       &brport_attr_learning,
+       &brport_attr_unicast_flood,
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
        &brport_attr_multicast_router,
        &brport_attr_multicast_fast_leave,
index df0364a..5180938 100644 (file)
@@ -271,6 +271,12 @@ static int ebt_ulog_tg_check(const struct xt_tgchk_param *par)
 {
        struct ebt_ulog_info *uloginfo = par->targinfo;
 
+       if (!par->net->xt.ebt_ulog_warn_deprecated) {
+               pr_info("ebt_ulog is deprecated and it will be removed soon, "
+                       "use ebt_nflog instead\n");
+               par->net->xt.ebt_ulog_warn_deprecated = true;
+       }
+
        if (uloginfo->nlgroup > 31)
                return -EINVAL;
 
index 3d110c4..ac78024 100644 (file)
@@ -1339,7 +1339,7 @@ static inline int ebt_make_matchname(const struct ebt_entry_match *m,
 
        /* ebtables expects 32 bytes long names but xt_match names are 29 bytes
           long. Copy 29 bytes and fill remaining bytes with zeroes. */
-       strncpy(name, m->u.match->name, sizeof(name));
+       strlcpy(name, m->u.match->name, sizeof(name));
        if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN))
                return -EFAULT;
        return 0;
@@ -1351,7 +1351,7 @@ static inline int ebt_make_watchername(const struct ebt_entry_watcher *w,
        char __user *hlp = ubase + ((char *)w - base);
        char name[EBT_FUNCTION_MAXNAMELEN] = {};
 
-       strncpy(name, w->u.watcher->name, sizeof(name));
+       strlcpy(name, w->u.watcher->name, sizeof(name));
        if (copy_to_user(hlp , name, EBT_FUNCTION_MAXNAMELEN))
                return -EFAULT;
        return 0;
@@ -1377,7 +1377,7 @@ ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase)
        ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
        if (ret != 0)
                return ret;
-       strncpy(name, t->u.target->name, sizeof(name));
+       strlcpy(name, t->u.target->name, sizeof(name));
        if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN))
                return -EFAULT;
        return 0;
index 1f9ece1..4dca159 100644 (file)
@@ -352,9 +352,9 @@ EXPORT_SYMBOL(caif_enroll_dev);
 
 /* notify Caif of device events */
 static int caif_device_notify(struct notifier_block *me, unsigned long what,
-                             void *arg)
+                             void *ptr)
 {
-       struct net_device *dev = arg;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct caif_device_entry *caifd = NULL;
        struct caif_dev_common *caifdev;
        struct cfcnfg *cfg;
index 942e00a..75ed04b 100644 (file)
@@ -121,9 +121,9 @@ static struct packet_type caif_usb_type __read_mostly = {
 };
 
 static int cfusbl_device_notify(struct notifier_block *me, unsigned long what,
-                               void *arg)
+                               void *ptr)
 {
-       struct net_device *dev = arg;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct caif_dev_common common;
        struct cflayer *layer, *link_support;
        struct usbnet *usbnet;
index c4e5085..3ab8dd2 100644 (file)
@@ -794,9 +794,9 @@ EXPORT_SYMBOL(can_proto_unregister);
  * af_can notifier to create/remove CAN netdevice specific structs
  */
 static int can_notifier(struct notifier_block *nb, unsigned long msg,
-                       void *data)
+                       void *ptr)
 {
-       struct net_device *dev = (struct net_device *)data;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct dev_rcv_lists *d;
 
        if (!net_eq(dev_net(dev), &init_net))
index 8f113e6..46f20bf 100644 (file)
@@ -1350,9 +1350,9 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
  * notification handler for netdevice status changes
  */
 static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
-                       void *data)
+                       void *ptr)
 {
-       struct net_device *dev = (struct net_device *)data;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct bcm_sock *bo = container_of(nb, struct bcm_sock, notifier);
        struct sock *sk = &bo->sk;
        struct bcm_op *op;
index 3ee690e..2f291f9 100644 (file)
@@ -445,9 +445,9 @@ static inline void cgw_unregister_filter(struct cgw_job *gwj)
 }
 
 static int cgw_notifier(struct notifier_block *nb,
-                       unsigned long msg, void *data)
+                       unsigned long msg, void *ptr)
 {
-       struct net_device *dev = (struct net_device *)data;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
index 1085e65..641e1c8 100644 (file)
@@ -239,9 +239,9 @@ static int raw_enable_allfilters(struct net_device *dev, struct sock *sk)
 }
 
 static int raw_notifier(struct notifier_block *nb,
-                       unsigned long msg, void *data)
+                       unsigned long msg, void *ptr)
 {
-       struct net_device *dev = (struct net_device *)data;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct raw_sock *ro = container_of(nb, struct raw_sock, notifier);
        struct sock *sk = &ro->sk;
 
index 925ca58..8c93fa8 100644 (file)
@@ -39,6 +39,11 @@ static int should_authenticate(struct ceph_auth_client *ac)
        return xi->starting;
 }
 
+static int build_request(struct ceph_auth_client *ac, void *buf, void *end)
+{
+       return 0;
+}
+
 /*
  * the generic auth code decode the global_id, and we carry no actual
  * authenticate state, so nothing happens here.
@@ -106,6 +111,7 @@ static const struct ceph_auth_client_ops ceph_auth_none_ops = {
        .destroy = destroy,
        .is_authenticated = is_authenticated,
        .should_authenticate = should_authenticate,
+       .build_request = build_request,
        .handle_reply = handle_reply,
        .create_authorizer = ceph_auth_none_create_authorizer,
        .destroy_authorizer = ceph_auth_none_destroy_authorizer,
index 3a246a6..dd47889 100644 (file)
@@ -733,12 +733,14 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc,
 
        object_size = le32_to_cpu(layout->fl_object_size);
        object_base = off - objoff;
-       if (truncate_size <= object_base) {
-               truncate_size = 0;
-       } else {
-               truncate_size -= object_base;
-               if (truncate_size > object_size)
-                       truncate_size = object_size;
+       if (!(truncate_seq == 1 && truncate_size == -1ULL)) {
+               if (truncate_size <= object_base) {
+                       truncate_size = 0;
+               } else {
+                       truncate_size -= object_base;
+                       if (truncate_size > object_size)
+                               truncate_size = object_size;
+               }
        }
 
        osd_req_op_extent_init(req, 0, opcode, objoff, objlen,
@@ -1174,6 +1176,7 @@ static void __register_linger_request(struct ceph_osd_client *osdc,
                                    struct ceph_osd_request *req)
 {
        dout("__register_linger_request %p\n", req);
+       ceph_osdc_get_request(req);
        list_add_tail(&req->r_linger_item, &osdc->req_linger);
        if (req->r_osd)
                list_add_tail(&req->r_linger_osd,
@@ -1196,6 +1199,7 @@ static void __unregister_linger_request(struct ceph_osd_client *osdc,
                if (list_empty(&req->r_osd_item))
                        req->r_osd = NULL;
        }
+       ceph_osdc_put_request(req);
 }
 
 void ceph_osdc_unregister_linger_request(struct ceph_osd_client *osdc,
@@ -1203,9 +1207,8 @@ void ceph_osdc_unregister_linger_request(struct ceph_osd_client *osdc,
 {
        mutex_lock(&osdc->request_mutex);
        if (req->r_linger) {
-               __unregister_linger_request(osdc, req);
                req->r_linger = 0;
-               ceph_osdc_put_request(req);
+               __unregister_linger_request(osdc, req);
        }
        mutex_unlock(&osdc->request_mutex);
 }
@@ -1217,11 +1220,6 @@ void ceph_osdc_set_request_linger(struct ceph_osd_client *osdc,
        if (!req->r_linger) {
                dout("set_request_linger %p\n", req);
                req->r_linger = 1;
-               /*
-                * caller is now responsible for calling
-                * unregister_linger_request
-                */
-               ceph_osdc_get_request(req);
        }
 }
 EXPORT_SYMBOL(ceph_osdc_set_request_linger);
@@ -1339,10 +1337,6 @@ static void __send_request(struct ceph_osd_client *osdc,
 
        ceph_msg_get(req->r_request); /* send consumes a ref */
 
-       /* Mark the request unsafe if this is the first timet's being sent. */
-
-       if (!req->r_sent && req->r_unsafe_callback)
-               req->r_unsafe_callback(req, true);
        req->r_sent = req->r_osd->o_incarnation;
 
        ceph_con_send(&req->r_osd->o_con, req->r_request);
@@ -1433,8 +1427,6 @@ static void handle_osds_timeout(struct work_struct *work)
 
 static void complete_request(struct ceph_osd_request *req)
 {
-       if (req->r_unsafe_callback)
-               req->r_unsafe_callback(req, false);
        complete_all(&req->r_safe_completion);  /* fsync waiter */
 }
 
@@ -1526,6 +1518,8 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg,
        for (i = 0; i < numops; i++)
                req->r_reply_op_result[i] = ceph_decode_32(&p);
 
+       already_completed = req->r_got_reply;
+
        if (!req->r_got_reply) {
 
                req->r_result = result;
@@ -1556,19 +1550,23 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg,
            ((flags & CEPH_OSD_FLAG_WRITE) == 0))
                __unregister_request(osdc, req);
 
-       already_completed = req->r_completed;
-       req->r_completed = 1;
        mutex_unlock(&osdc->request_mutex);
-       if (already_completed)
-               goto done;
 
-       if (req->r_callback)
-               req->r_callback(req, msg);
-       else
-               complete_all(&req->r_completion);
+       if (!already_completed) {
+               if (req->r_unsafe_callback &&
+                   result >= 0 && !(flags & CEPH_OSD_FLAG_ONDISK))
+                       req->r_unsafe_callback(req, true);
+               if (req->r_callback)
+                       req->r_callback(req, msg);
+               else
+                       complete_all(&req->r_completion);
+       }
 
-       if (flags & CEPH_OSD_FLAG_ONDISK)
+       if (flags & CEPH_OSD_FLAG_ONDISK) {
+               if (req->r_unsafe_callback && already_completed)
+                       req->r_unsafe_callback(req, false);
                complete_request(req);
+       }
 
 done:
        dout("req=%p req->r_linger=%d\n", req, req->r_linger);
@@ -1633,8 +1631,10 @@ static void kick_requests(struct ceph_osd_client *osdc, int force_resend)
                        dout("%p tid %llu restart on osd%d\n",
                             req, req->r_tid,
                             req->r_osd ? req->r_osd->o_osd : -1);
+                       ceph_osdc_get_request(req);
                        __unregister_request(osdc, req);
                        __register_linger_request(osdc, req);
+                       ceph_osdc_put_request(req);
                        continue;
                }
 
@@ -2123,7 +2123,6 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc,
        __register_request(osdc, req);
        req->r_sent = 0;
        req->r_got_reply = 0;
-       req->r_completed = 0;
        rc = __map_request(osdc, req, 0);
        if (rc < 0) {
                if (nofail) {
@@ -2456,8 +2455,10 @@ static struct ceph_msg *get_reply(struct ceph_connection *con,
        ceph_msg_revoke_incoming(req->r_reply);
 
        if (front > req->r_reply->front.iov_len) {
-               pr_warning("get_reply front %d > preallocated %d\n",
-                          front, (int)req->r_reply->front.iov_len);
+               pr_warning("get_reply front %d > preallocated %d (%u#%llu)\n",
+                          front, (int)req->r_reply->front.iov_len,
+                          (unsigned int)con->peer_name.type,
+                          le64_to_cpu(con->peer_name.num));
                m = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, front, GFP_NOFS, false);
                if (!m)
                        goto out;
index b71423d..6e9ab31 100644 (file)
@@ -56,6 +56,7 @@
 #include <net/sock.h>
 #include <net/tcp_states.h>
 #include <trace/events/skb.h>
+#include <net/ll_poll.h>
 
 /*
  *     Is a socket 'connection oriented' ?
@@ -207,6 +208,10 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
                }
                spin_unlock_irqrestore(&queue->lock, cpu_flags);
 
+               if (sk_can_busy_loop(sk) &&
+                   sk_busy_loop(sk, flags & MSG_DONTWAIT))
+                       continue;
+
                /* User doesn't want to wait */
                error = -EAGAIN;
                if (!timeo)
index faebb39..560dafd 100644 (file)
 #include <linux/inetdevice.h>
 #include <linux/cpu_rmap.h>
 #include <linux/static_key.h>
+#include <linux/hashtable.h>
+#include <linux/vmalloc.h>
 
 #include "net-sysfs.h"
 
@@ -166,6 +168,12 @@ static struct list_head offload_base __read_mostly;
 DEFINE_RWLOCK(dev_base_lock);
 EXPORT_SYMBOL(dev_base_lock);
 
+/* protects napi_hash addition/deletion and napi_gen_id */
+static DEFINE_SPINLOCK(napi_hash_lock);
+
+static unsigned int napi_gen_id;
+static DEFINE_HASHTABLE(napi_hash, 8);
+
 seqcount_t devnet_rename_seq;
 
 static inline void dev_base_seq_inc(struct net *net)
@@ -1232,9 +1240,7 @@ static int __dev_open(struct net_device *dev)
         * If we don't do this there is a chance ndo_poll_controller
         * or ndo_poll may be running while we open the device
         */
-       ret = netpoll_rx_disable(dev);
-       if (ret)
-               return ret;
+       netpoll_rx_disable(dev);
 
        ret = call_netdevice_notifiers(NETDEV_PRE_UP, dev);
        ret = notifier_to_errno(ret);
@@ -1343,9 +1349,7 @@ static int __dev_close(struct net_device *dev)
        LIST_HEAD(single);
 
        /* Temporarily disable netpoll until the interface is down */
-       retval = netpoll_rx_disable(dev);
-       if (retval)
-               return retval;
+       netpoll_rx_disable(dev);
 
        list_add(&dev->unreg_list, &single);
        retval = __dev_close_many(&single);
@@ -1387,14 +1391,11 @@ static int dev_close_many(struct list_head *head)
  */
 int dev_close(struct net_device *dev)
 {
-       int ret = 0;
        if (dev->flags & IFF_UP) {
                LIST_HEAD(single);
 
                /* Block netpoll rx while the interface is going down */
-               ret = netpoll_rx_disable(dev);
-               if (ret)
-                       return ret;
+               netpoll_rx_disable(dev);
 
                list_add(&dev->unreg_list, &single);
                dev_close_many(&single);
@@ -1402,7 +1403,7 @@ int dev_close(struct net_device *dev)
 
                netpoll_rx_enable(dev);
        }
-       return ret;
+       return 0;
 }
 EXPORT_SYMBOL(dev_close);
 
@@ -1432,6 +1433,14 @@ void dev_disable_lro(struct net_device *dev)
 }
 EXPORT_SYMBOL(dev_disable_lro);
 
+static int call_netdevice_notifier(struct notifier_block *nb, unsigned long val,
+                                  struct net_device *dev)
+{
+       struct netdev_notifier_info info;
+
+       netdev_notifier_info_init(&info, dev);
+       return nb->notifier_call(nb, val, &info);
+}
 
 static int dev_boot_phase = 1;
 
@@ -1464,7 +1473,7 @@ int register_netdevice_notifier(struct notifier_block *nb)
                goto unlock;
        for_each_net(net) {
                for_each_netdev(net, dev) {
-                       err = nb->notifier_call(nb, NETDEV_REGISTER, dev);
+                       err = call_netdevice_notifier(nb, NETDEV_REGISTER, dev);
                        err = notifier_to_errno(err);
                        if (err)
                                goto rollback;
@@ -1472,7 +1481,7 @@ int register_netdevice_notifier(struct notifier_block *nb)
                        if (!(dev->flags & IFF_UP))
                                continue;
 
-                       nb->notifier_call(nb, NETDEV_UP, dev);
+                       call_netdevice_notifier(nb, NETDEV_UP, dev);
                }
        }
 
@@ -1488,10 +1497,11 @@ rollback:
                                goto outroll;
 
                        if (dev->flags & IFF_UP) {
-                               nb->notifier_call(nb, NETDEV_GOING_DOWN, dev);
-                               nb->notifier_call(nb, NETDEV_DOWN, dev);
+                               call_netdevice_notifier(nb, NETDEV_GOING_DOWN,
+                                                       dev);
+                               call_netdevice_notifier(nb, NETDEV_DOWN, dev);
                        }
-                       nb->notifier_call(nb, NETDEV_UNREGISTER, dev);
+                       call_netdevice_notifier(nb, NETDEV_UNREGISTER, dev);
                }
        }
 
@@ -1529,10 +1539,11 @@ int unregister_netdevice_notifier(struct notifier_block *nb)
        for_each_net(net) {
                for_each_netdev(net, dev) {
                        if (dev->flags & IFF_UP) {
-                               nb->notifier_call(nb, NETDEV_GOING_DOWN, dev);
-                               nb->notifier_call(nb, NETDEV_DOWN, dev);
+                               call_netdevice_notifier(nb, NETDEV_GOING_DOWN,
+                                                       dev);
+                               call_netdevice_notifier(nb, NETDEV_DOWN, dev);
                        }
-                       nb->notifier_call(nb, NETDEV_UNREGISTER, dev);
+                       call_netdevice_notifier(nb, NETDEV_UNREGISTER, dev);
                }
        }
 unlock:
@@ -1541,6 +1552,25 @@ unlock:
 }
 EXPORT_SYMBOL(unregister_netdevice_notifier);
 
+/**
+ *     call_netdevice_notifiers_info - call all network notifier blocks
+ *     @val: value passed unmodified to notifier function
+ *     @dev: net_device pointer passed unmodified to notifier function
+ *     @info: notifier information data
+ *
+ *     Call all network notifier blocks.  Parameters and return value
+ *     are as for raw_notifier_call_chain().
+ */
+
+int call_netdevice_notifiers_info(unsigned long val, struct net_device *dev,
+                                 struct netdev_notifier_info *info)
+{
+       ASSERT_RTNL();
+       netdev_notifier_info_init(info, dev);
+       return raw_notifier_call_chain(&netdev_chain, val, info);
+}
+EXPORT_SYMBOL(call_netdevice_notifiers_info);
+
 /**
  *     call_netdevice_notifiers - call all network notifier blocks
  *      @val: value passed unmodified to notifier function
@@ -1552,8 +1582,9 @@ EXPORT_SYMBOL(unregister_netdevice_notifier);
 
 int call_netdevice_notifiers(unsigned long val, struct net_device *dev)
 {
-       ASSERT_RTNL();
-       return raw_notifier_call_chain(&netdev_chain, val, dev);
+       struct netdev_notifier_info info;
+
+       return call_netdevice_notifiers_info(val, dev, &info);
 }
 EXPORT_SYMBOL(call_netdevice_notifiers);
 
@@ -1655,23 +1686,19 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
                }
        }
 
-       skb_orphan(skb);
-
        if (unlikely(!is_skb_forwardable(dev, skb))) {
                atomic_long_inc(&dev->rx_dropped);
                kfree_skb(skb);
                return NET_RX_DROP;
        }
-       skb->skb_iif = 0;
-       skb->dev = dev;
-       skb_dst_drop(skb);
-       skb->tstamp.tv64 = 0;
-       skb->pkt_type = PACKET_HOST;
+       skb_scrub_packet(skb);
        skb->protocol = eth_type_trans(skb, dev);
-       skb->mark = 0;
-       secpath_reset(skb);
-       nf_reset(skb);
-       nf_reset_trace(skb);
+
+       /* eth_type_trans() can set pkt_type.
+        * clear pkt_type _after_ calling eth_type_trans()
+        */
+       skb->pkt_type = PACKET_HOST;
+
        return netif_rx(skb);
 }
 EXPORT_SYMBOL_GPL(dev_forward_skb);
@@ -1736,7 +1763,7 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
                        skb_reset_mac_header(skb2);
 
                        if (skb_network_header(skb2) < skb2->data ||
-                           skb2->network_header > skb2->tail) {
+                           skb_network_header(skb2) > skb_tail_pointer(skb2)) {
                                net_crit_ratelimited("protocol %04x is buggy, dev %s\n",
                                                     ntohs(skb2->protocol),
                                                     dev->name);
@@ -3099,6 +3126,46 @@ static int rps_ipi_queued(struct softnet_data *sd)
        return 0;
 }
 
+#ifdef CONFIG_NET_FLOW_LIMIT
+int netdev_flow_limit_table_len __read_mostly = (1 << 12);
+#endif
+
+static bool skb_flow_limit(struct sk_buff *skb, unsigned int qlen)
+{
+#ifdef CONFIG_NET_FLOW_LIMIT
+       struct sd_flow_limit *fl;
+       struct softnet_data *sd;
+       unsigned int old_flow, new_flow;
+
+       if (qlen < (netdev_max_backlog >> 1))
+               return false;
+
+       sd = &__get_cpu_var(softnet_data);
+
+       rcu_read_lock();
+       fl = rcu_dereference(sd->flow_limit);
+       if (fl) {
+               new_flow = skb_get_rxhash(skb) & (fl->num_buckets - 1);
+               old_flow = fl->history[fl->history_head];
+               fl->history[fl->history_head] = new_flow;
+
+               fl->history_head++;
+               fl->history_head &= FLOW_LIMIT_HISTORY - 1;
+
+               if (likely(fl->buckets[old_flow]))
+                       fl->buckets[old_flow]--;
+
+               if (++fl->buckets[new_flow] > (FLOW_LIMIT_HISTORY >> 1)) {
+                       fl->count++;
+                       rcu_read_unlock();
+                       return true;
+               }
+       }
+       rcu_read_unlock();
+#endif
+       return false;
+}
+
 /*
  * enqueue_to_backlog is called to queue an skb to a per CPU backlog
  * queue (may be a remote CPU queue).
@@ -3108,13 +3175,15 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
 {
        struct softnet_data *sd;
        unsigned long flags;
+       unsigned int qlen;
 
        sd = &per_cpu(softnet_data, cpu);
 
        local_irq_save(flags);
 
        rps_lock(sd);
-       if (skb_queue_len(&sd->input_pkt_queue) <= netdev_max_backlog) {
+       qlen = skb_queue_len(&sd->input_pkt_queue);
+       if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) {
                if (skb_queue_len(&sd->input_pkt_queue)) {
 enqueue:
                        __skb_queue_tail(&sd->input_pkt_queue, skb);
@@ -3862,7 +3931,7 @@ static void skb_gro_reset_offset(struct sk_buff *skb)
        NAPI_GRO_CB(skb)->frag0 = NULL;
        NAPI_GRO_CB(skb)->frag0_len = 0;
 
-       if (skb->mac_header == skb->tail &&
+       if (skb_mac_header(skb) == skb_tail_pointer(skb) &&
            pinfo->nr_frags &&
            !PageHighMem(skb_frag_page(frag0))) {
                NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0);
@@ -4106,6 +4175,58 @@ void napi_complete(struct napi_struct *n)
 }
 EXPORT_SYMBOL(napi_complete);
 
+/* must be called under rcu_read_lock(), as we dont take a reference */
+struct napi_struct *napi_by_id(unsigned int napi_id)
+{
+       unsigned int hash = napi_id % HASH_SIZE(napi_hash);
+       struct napi_struct *napi;
+
+       hlist_for_each_entry_rcu(napi, &napi_hash[hash], napi_hash_node)
+               if (napi->napi_id == napi_id)
+                       return napi;
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(napi_by_id);
+
+void napi_hash_add(struct napi_struct *napi)
+{
+       if (!test_and_set_bit(NAPI_STATE_HASHED, &napi->state)) {
+
+               spin_lock(&napi_hash_lock);
+
+               /* 0 is not a valid id, we also skip an id that is taken
+                * we expect both events to be extremely rare
+                */
+               napi->napi_id = 0;
+               while (!napi->napi_id) {
+                       napi->napi_id = ++napi_gen_id;
+                       if (napi_by_id(napi->napi_id))
+                               napi->napi_id = 0;
+               }
+
+               hlist_add_head_rcu(&napi->napi_hash_node,
+                       &napi_hash[napi->napi_id % HASH_SIZE(napi_hash)]);
+
+               spin_unlock(&napi_hash_lock);
+       }
+}
+EXPORT_SYMBOL_GPL(napi_hash_add);
+
+/* Warning : caller is responsible to make sure rcu grace period
+ * is respected before freeing memory containing @napi
+ */
+void napi_hash_del(struct napi_struct *napi)
+{
+       spin_lock(&napi_hash_lock);
+
+       if (test_and_clear_bit(NAPI_STATE_HASHED, &napi->state))
+               hlist_del_rcu(&napi->napi_hash_node);
+
+       spin_unlock(&napi_hash_lock);
+}
+EXPORT_SYMBOL_GPL(napi_hash_del);
+
 void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
                    int (*poll)(struct napi_struct *, int), int weight)
 {
@@ -4404,7 +4525,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
        else
                list_add_tail_rcu(&upper->list, &dev->upper_dev_list);
        dev_hold(upper_dev);
-
+       call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev);
        return 0;
 }
 
@@ -4464,6 +4585,7 @@ void netdev_upper_dev_unlink(struct net_device *dev,
        list_del_rcu(&upper->list);
        dev_put(upper_dev);
        kfree_rcu(upper, rcu);
+       call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev);
 }
 EXPORT_SYMBOL(netdev_upper_dev_unlink);
 
@@ -4734,8 +4856,13 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags)
        }
 
        if (dev->flags & IFF_UP &&
-           (changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE)))
-               call_netdevice_notifiers(NETDEV_CHANGE, dev);
+           (changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE))) {
+               struct netdev_notifier_change_info change_info;
+
+               change_info.flags_changed = changes;
+               call_netdevice_notifiers_info(NETDEV_CHANGE, dev,
+                                             &change_info.info);
+       }
 }
 
 /**
@@ -5158,17 +5285,28 @@ static void netdev_init_one_queue(struct net_device *dev,
 #endif
 }
 
+static void netif_free_tx_queues(struct net_device *dev)
+{
+       if (is_vmalloc_addr(dev->_tx))
+               vfree(dev->_tx);
+       else
+               kfree(dev->_tx);
+}
+
 static int netif_alloc_netdev_queues(struct net_device *dev)
 {
        unsigned int count = dev->num_tx_queues;
        struct netdev_queue *tx;
+       size_t sz = count * sizeof(*tx);
 
-       BUG_ON(count < 1);
-
-       tx = kcalloc(count, sizeof(struct netdev_queue), GFP_KERNEL);
-       if (!tx)
-               return -ENOMEM;
+       BUG_ON(count < 1 || count > 0xffff);
 
+       tx = kzalloc(sz, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
+       if (!tx) {
+               tx = vzalloc(sz);
+               if (!tx)
+                       return -ENOMEM;
+       }
        dev->_tx = tx;
 
        netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL);
@@ -5269,6 +5407,10 @@ int register_netdevice(struct net_device *dev)
         */
        dev->hw_enc_features |= NETIF_F_SG;
 
+       /* Make NETIF_F_SG inheritable to MPLS.
+        */
+       dev->mpls_features |= NETIF_F_SG;
+
        ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev);
        ret = notifier_to_errno(ret);
        if (ret)
@@ -5712,7 +5854,7 @@ free_all:
 
 free_pcpu:
        free_percpu(dev->pcpu_refcnt);
-       kfree(dev->_tx);
+       netif_free_tx_queues(dev);
 #ifdef CONFIG_RPS
        kfree(dev->_rx);
 #endif
@@ -5737,7 +5879,7 @@ void free_netdev(struct net_device *dev)
 
        release_net(dev_net(dev));
 
-       kfree(dev->_tx);
+       netif_free_tx_queues(dev);
 #ifdef CONFIG_RPS
        kfree(dev->_rx);
 #endif
@@ -6048,7 +6190,7 @@ netdev_features_t netdev_increment_features(netdev_features_t all,
 }
 EXPORT_SYMBOL(netdev_increment_features);
 
-static struct hlist_head *netdev_create_hash(void)
+static struct hlist_head * __net_init netdev_create_hash(void)
 {
        int i;
        struct hlist_head *hash;
@@ -6304,6 +6446,10 @@ static int __init net_dev_init(void)
                sd->backlog.weight = weight_p;
                sd->backlog.gro_list = NULL;
                sd->backlog.gro_count = 0;
+
+#ifdef CONFIG_NET_FLOW_LIMIT
+               sd->flow_limit = NULL;
+#endif
        }
 
        dev_boot_phase = 0;
index d23b668..5e78d44 100644 (file)
@@ -295,9 +295,9 @@ static int net_dm_cmd_trace(struct sk_buff *skb,
 }
 
 static int dropmon_net_event(struct notifier_block *ev_block,
-                       unsigned long event, void *ptr)
+                            unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct dm_hw_stat_delta *new_stat = NULL;
        struct dm_hw_stat_delta *tmp;
 
index df9cc81..ca4231e 100644 (file)
@@ -372,7 +372,7 @@ static void dst_ifdown(struct dst_entry *dst, struct net_device *dev,
 static int dst_dev_event(struct notifier_block *this, unsigned long event,
                         void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct dst_entry *dst, *last = NULL;
 
        switch (event) {
index ce91766..ab5fa63 100644 (file)
@@ -82,6 +82,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
        [NETIF_F_FSO_BIT] =              "tx-fcoe-segmentation",
        [NETIF_F_GSO_GRE_BIT] =          "tx-gre-segmentation",
        [NETIF_F_GSO_UDP_TUNNEL_BIT] =   "tx-udp_tnl-segmentation",
+       [NETIF_F_GSO_MPLS_BIT] =         "tx-mpls-segmentation",
 
        [NETIF_F_FCOE_CRC_BIT] =         "tx-checksum-fcoe-crc",
        [NETIF_F_SCTP_CSUM_BIT] =        "tx-checksum-sctp",
@@ -1319,10 +1320,19 @@ static int ethtool_get_dump_data(struct net_device *dev,
        if (ret)
                return ret;
 
-       len = (tmp.len > dump.len) ? dump.len : tmp.len;
+       len = min(tmp.len, dump.len);
        if (!len)
                return -EFAULT;
 
+       /* Don't ever let the driver think there's more space available
+        * than it requested with .get_dump_flag().
+        */
+       dump.len = len;
+
+       /* Always allocate enough space to hold the whole thing so that the
+        * driver does not need to check the length and bother with partial
+        * dumping.
+        */
        data = vzalloc(tmp.len);
        if (!data)
                return -ENOMEM;
@@ -1330,6 +1340,16 @@ static int ethtool_get_dump_data(struct net_device *dev,
        if (ret)
                goto out;
 
+       /* There are two sane possibilities:
+        * 1. The driver's .get_dump_data() does not touch dump.len.
+        * 2. Or it may set dump.len to how much it really writes, which
+        *    should be tmp.len (or len if it can do a partial dump).
+        * In any case respond to userspace with the actual length of data
+        * it's receiving.
+        */
+       WARN_ON(dump.len != len && dump.len != tmp.len);
+       dump.len = len;
+
        if (copy_to_user(useraddr, &dump, sizeof(dump))) {
                ret = -EFAULT;
                goto out;
@@ -1413,7 +1433,7 @@ static int ethtool_get_module_eeprom(struct net_device *dev,
                                      modinfo.eeprom_len);
 }
 
-/* The main entry point in this file.  Called from net/core/dev.c */
+/* The main entry point in this file.  Called from net/core/dev_ioctl.c */
 
 int dev_ethtool(struct net *net, struct ifreq *ifr)
 {
index d5a9f8e..2173544 100644 (file)
@@ -705,9 +705,9 @@ static void detach_rules(struct list_head *rules, struct net_device *dev)
 
 
 static int fib_rules_event(struct notifier_block *this, unsigned long event,
-                           void *ptr)
+                          void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net *net = dev_net(dev);
        struct fib_rules_ops *ops;
 
index d9d198a..6b5b6e7 100644 (file)
@@ -82,7 +82,7 @@ struct gen_estimator
 {
        struct list_head        list;
        struct gnet_stats_basic_packed  *bstats;
-       struct gnet_stats_rate_est      *rate_est;
+       struct gnet_stats_rate_est64    *rate_est;
        spinlock_t              *stats_lock;
        int                     ewma_log;
        u64                     last_bytes;
@@ -167,7 +167,7 @@ static void gen_add_node(struct gen_estimator *est)
 
 static
 struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats,
-                                   const struct gnet_stats_rate_est *rate_est)
+                                   const struct gnet_stats_rate_est64 *rate_est)
 {
        struct rb_node *p = est_root.rb_node;
 
@@ -203,7 +203,7 @@ struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats
  *
  */
 int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
-                     struct gnet_stats_rate_est *rate_est,
+                     struct gnet_stats_rate_est64 *rate_est,
                      spinlock_t *stats_lock,
                      struct nlattr *opt)
 {
@@ -258,7 +258,7 @@ EXPORT_SYMBOL(gen_new_estimator);
  * Note : Caller should respect an RCU grace period before freeing stats_lock
  */
 void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
-                       struct gnet_stats_rate_est *rate_est)
+                       struct gnet_stats_rate_est64 *rate_est)
 {
        struct gen_estimator *e;
 
@@ -290,7 +290,7 @@ EXPORT_SYMBOL(gen_kill_estimator);
  * Returns 0 on success or a negative error code.
  */
 int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
-                         struct gnet_stats_rate_est *rate_est,
+                         struct gnet_stats_rate_est64 *rate_est,
                          spinlock_t *stats_lock, struct nlattr *opt)
 {
        gen_kill_estimator(bstats, rate_est);
@@ -306,7 +306,7 @@ EXPORT_SYMBOL(gen_replace_estimator);
  * Returns true if estimator is active, and false if not.
  */
 bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
-                         const struct gnet_stats_rate_est *rate_est)
+                         const struct gnet_stats_rate_est64 *rate_est)
 {
        bool res;
 
index ddedf21..9d3d9e7 100644 (file)
@@ -143,18 +143,30 @@ EXPORT_SYMBOL(gnet_stats_copy_basic);
 int
 gnet_stats_copy_rate_est(struct gnet_dump *d,
                         const struct gnet_stats_basic_packed *b,
-                        struct gnet_stats_rate_est *r)
+                        struct gnet_stats_rate_est64 *r)
 {
+       struct gnet_stats_rate_est est;
+       int res;
+
        if (b && !gen_estimator_active(b, r))
                return 0;
 
+       est.bps = min_t(u64, UINT_MAX, r->bps);
+       /* we have some time before reaching 2^32 packets per second */
+       est.pps = r->pps;
+
        if (d->compat_tc_stats) {
-               d->tc_stats.bps = r->bps;
-               d->tc_stats.pps = r->pps;
+               d->tc_stats.bps = est.bps;
+               d->tc_stats.pps = est.pps;
        }
 
-       if (d->tail)
-               return gnet_stats_copy(d, TCA_STATS_RATE_EST, r, sizeof(*r));
+       if (d->tail) {
+               res = gnet_stats_copy(d, TCA_STATS_RATE_EST, &est, sizeof(est));
+               if (res < 0 || est.bps == r->bps)
+                       return res;
+               /* emit 64bit stats only if needed */
+               return gnet_stats_copy(d, TCA_STATS_RATE_EST64, r, sizeof(*r));
+       }
 
        return 0;
 }
index 8f82a5c..9c3a839 100644 (file)
@@ -92,6 +92,9 @@ static bool linkwatch_urgent_event(struct net_device *dev)
        if (dev->ifindex != dev->iflink)
                return true;
 
+       if (dev->priv_flags & IFF_TEAM_PORT)
+               return true;
+
        return netif_carrier_ok(dev) && qdisc_tx_changing(dev);
 }
 
index 5c56b21..b7de821 100644 (file)
@@ -231,7 +231,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
                                   we must kill timers etc. and move
                                   it to safe state.
                                 */
-                               skb_queue_purge(&n->arp_queue);
+                               __skb_queue_purge(&n->arp_queue);
                                n->arp_queue_len_bytes = 0;
                                n->output = neigh_blackhole;
                                if (n->nud_state & NUD_VALID)
@@ -286,7 +286,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device
        if (!n)
                goto out_entries;
 
-       skb_queue_head_init(&n->arp_queue);
+       __skb_queue_head_init(&n->arp_queue);
        rwlock_init(&n->lock);
        seqlock_init(&n->ha_lock);
        n->updated        = n->used = now;
@@ -708,7 +708,9 @@ void neigh_destroy(struct neighbour *neigh)
        if (neigh_del_timer(neigh))
                pr_warn("Impossible event\n");
 
-       skb_queue_purge(&neigh->arp_queue);
+       write_lock_bh(&neigh->lock);
+       __skb_queue_purge(&neigh->arp_queue);
+       write_unlock_bh(&neigh->lock);
        neigh->arp_queue_len_bytes = 0;
 
        if (dev->netdev_ops->ndo_neigh_destroy)
@@ -858,7 +860,7 @@ static void neigh_invalidate(struct neighbour *neigh)
                neigh->ops->error_report(neigh, skb);
                write_lock(&neigh->lock);
        }
-       skb_queue_purge(&neigh->arp_queue);
+       __skb_queue_purge(&neigh->arp_queue);
        neigh->arp_queue_len_bytes = 0;
 }
 
@@ -1210,7 +1212,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
 
                        write_lock_bh(&neigh->lock);
                }
-               skb_queue_purge(&neigh->arp_queue);
+               __skb_queue_purge(&neigh->arp_queue);
                neigh->arp_queue_len_bytes = 0;
        }
 out:
@@ -1419,7 +1421,7 @@ static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
 
        for (p = &tbl->parms; p; p = p->next) {
                if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) ||
-                   (!p->dev && !ifindex))
+                   (!p->dev && !ifindex && net_eq(net, &init_net)))
                        return p;
        }
 
@@ -1429,15 +1431,11 @@ static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
 struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
                                      struct neigh_table *tbl)
 {
-       struct neigh_parms *p, *ref;
+       struct neigh_parms *p;
        struct net *net = dev_net(dev);
        const struct net_device_ops *ops = dev->netdev_ops;
 
-       ref = lookup_neigh_parms(tbl, net, 0);
-       if (!ref)
-               return NULL;
-
-       p = kmemdup(ref, sizeof(*p), GFP_KERNEL);
+       p = kmemdup(&tbl->parms, sizeof(*p), GFP_KERNEL);
        if (p) {
                p->tbl            = tbl;
                atomic_set(&p->refcnt, 1);
@@ -2053,6 +2051,12 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh)
                }
        }
 
+       err = -ENOENT;
+       if ((tb[NDTA_THRESH1] || tb[NDTA_THRESH2] ||
+            tb[NDTA_THRESH3] || tb[NDTA_GC_INTERVAL]) &&
+           !net_eq(net, &init_net))
+               goto errout_tbl_lock;
+
        if (tb[NDTA_THRESH1])
                tbl->gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]);
 
@@ -2765,11 +2769,11 @@ EXPORT_SYMBOL(neigh_app_ns);
 static int zero;
 static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN);
 
-static int proc_unres_qlen(ctl_table *ctl, int write, void __user *buffer,
-                          size_t *lenp, loff_t *ppos)
+static int proc_unres_qlen(struct ctl_table *ctl, int write,
+                          void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int size, ret;
-       ctl_table tmp = *ctl;
+       struct ctl_table tmp = *ctl;
 
        tmp.extra1 = &zero;
        tmp.extra2 = &unres_qlen_max;
index 569d355..2bf8329 100644 (file)
@@ -146,11 +146,23 @@ static void softnet_seq_stop(struct seq_file *seq, void *v)
 static int softnet_seq_show(struct seq_file *seq, void *v)
 {
        struct softnet_data *sd = v;
+       unsigned int flow_limit_count = 0;
 
-       seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+#ifdef CONFIG_NET_FLOW_LIMIT
+       struct sd_flow_limit *fl;
+
+       rcu_read_lock();
+       fl = rcu_dereference(sd->flow_limit);
+       if (fl)
+               flow_limit_count = fl->count;
+       rcu_read_unlock();
+#endif
+
+       seq_printf(seq,
+                  "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
                   sd->processed, sd->dropped, sd->time_squeeze, 0,
                   0, 0, 0, 0, /* was fastroute */
-                  sd->cpu_collision, sd->received_rps);
+                  sd->cpu_collision, sd->received_rps, flow_limit_count);
        return 0;
 }
 
index 35a9f08..2c637e9 100644 (file)
@@ -248,7 +248,7 @@ static void netpoll_poll_dev(struct net_device *dev)
        zap_completion_queue();
 }
 
-int netpoll_rx_disable(struct net_device *dev)
+void netpoll_rx_disable(struct net_device *dev)
 {
        struct netpoll_info *ni;
        int idx;
@@ -258,7 +258,6 @@ int netpoll_rx_disable(struct net_device *dev)
        if (ni)
                down(&ni->dev_lock);
        srcu_read_unlock(&netpoll_srcu, idx);
-       return 0;
 }
 EXPORT_SYMBOL(netpoll_rx_disable);
 
@@ -691,25 +690,20 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo
                        send_skb->dev = skb->dev;
 
                        skb_reset_network_header(send_skb);
-                       skb_put(send_skb, sizeof(struct ipv6hdr));
-                       hdr = ipv6_hdr(send_skb);
-
+                       hdr = (struct ipv6hdr *) skb_put(send_skb, sizeof(struct ipv6hdr));
                        *(__be32*)hdr = htonl(0x60000000);
-
                        hdr->payload_len = htons(size);
                        hdr->nexthdr = IPPROTO_ICMPV6;
                        hdr->hop_limit = 255;
                        hdr->saddr = *saddr;
                        hdr->daddr = *daddr;
 
-                       send_skb->transport_header = send_skb->tail;
-                       skb_put(send_skb, size);
-
-                       icmp6h = (struct icmp6hdr *)skb_transport_header(skb);
+                       icmp6h = (struct icmp6hdr *) skb_put(send_skb, sizeof(struct icmp6hdr));
                        icmp6h->icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
                        icmp6h->icmp6_router = 0;
                        icmp6h->icmp6_solicited = 1;
-                       target = (struct in6_addr *)(skb_transport_header(send_skb) + sizeof(struct icmp6hdr));
+
+                       target = (struct in6_addr *) skb_put(send_skb, sizeof(struct in6_addr));
                        *target = msg->target;
                        icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, size,
                                                              IPPROTO_ICMPV6,
index 0777d0a..e533259 100644 (file)
@@ -261,7 +261,7 @@ struct cgroup_subsys net_prio_subsys = {
 static int netprio_device_event(struct notifier_block *unused,
                                unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct netprio_map *old;
 
        /*
index 11f2704..9640972 100644 (file)
@@ -1921,7 +1921,7 @@ static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *d
 static int pktgen_device_event(struct notifier_block *unused,
                               unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct pktgen_net *pn = net_generic(dev_net(dev), pg_net_id);
 
        if (pn->pktgen_exiting)
@@ -2627,6 +2627,29 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
        pgh->tv_usec = htonl(timestamp.tv_usec);
 }
 
+static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
+                                       struct pktgen_dev *pkt_dev,
+                                       unsigned int extralen)
+{
+       struct sk_buff *skb = NULL;
+       unsigned int size = pkt_dev->cur_pkt_size + 64 + extralen +
+                           pkt_dev->pkt_overhead;
+
+       if (pkt_dev->flags & F_NODE) {
+               int node = pkt_dev->node >= 0 ? pkt_dev->node : numa_node_id();
+
+               skb = __alloc_skb(NET_SKB_PAD + size, GFP_NOWAIT, 0, node);
+               if (likely(skb)) {
+                       skb_reserve(skb, NET_SKB_PAD);
+                       skb->dev = dev;
+               }
+       } else {
+                skb = __netdev_alloc_skb(dev, size, GFP_NOWAIT);
+       }
+
+       return skb;
+}
+
 static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
                                        struct pktgen_dev *pkt_dev)
 {
@@ -2657,32 +2680,13 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 
        datalen = (odev->hard_header_len + 16) & ~0xf;
 
-       if (pkt_dev->flags & F_NODE) {
-               int node;
-
-               if (pkt_dev->node >= 0)
-                       node = pkt_dev->node;
-               else
-                       node =  numa_node_id();
-
-               skb = __alloc_skb(NET_SKB_PAD + pkt_dev->cur_pkt_size + 64
-                                 + datalen + pkt_dev->pkt_overhead, GFP_NOWAIT, 0, node);
-               if (likely(skb)) {
-                       skb_reserve(skb, NET_SKB_PAD);
-                       skb->dev = odev;
-               }
-       }
-       else
-         skb = __netdev_alloc_skb(odev,
-                                  pkt_dev->cur_pkt_size + 64
-                                  + datalen + pkt_dev->pkt_overhead, GFP_NOWAIT);
-
+       skb = pktgen_alloc_skb(odev, pkt_dev, datalen);
        if (!skb) {
                sprintf(pkt_dev->result, "No memory");
                return NULL;
        }
-       prefetchw(skb->data);
 
+       prefetchw(skb->data);
        skb_reserve(skb, datalen);
 
        /*  Reserve for ethernet and IP header  */
@@ -2708,15 +2712,15 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
                *vlan_encapsulated_proto = htons(ETH_P_IP);
        }
 
-       skb->network_header = skb->tail;
-       skb->transport_header = skb->network_header + sizeof(struct iphdr);
-       skb_put(skb, sizeof(struct iphdr) + sizeof(struct udphdr));
+       skb_set_mac_header(skb, 0);
+       skb_set_network_header(skb, skb->len);
+       iph = (struct iphdr *) skb_put(skb, sizeof(struct iphdr));
+
+       skb_set_transport_header(skb, skb->len);
+       udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));
        skb_set_queue_mapping(skb, queue_map);
        skb->priority = pkt_dev->skb_priority;
 
-       iph = ip_hdr(skb);
-       udph = udp_hdr(skb);
-
        memcpy(eth, pkt_dev->hh, 12);
        *(__be16 *) & eth[12] = protocol;
 
@@ -2746,8 +2750,6 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
        iph->check = 0;
        iph->check = ip_fast_csum((void *)iph, iph->ihl);
        skb->protocol = protocol;
-       skb->mac_header = (skb->network_header - ETH_HLEN -
-                          pkt_dev->pkt_overhead);
        skb->dev = odev;
        skb->pkt_type = PACKET_HOST;
        pktgen_finalize_skb(pkt_dev, skb, datalen);
@@ -2788,15 +2790,13 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
        mod_cur_headers(pkt_dev);
        queue_map = pkt_dev->cur_queue_map;
 
-       skb = __netdev_alloc_skb(odev,
-                                pkt_dev->cur_pkt_size + 64
-                                + 16 + pkt_dev->pkt_overhead, GFP_NOWAIT);
+       skb = pktgen_alloc_skb(odev, pkt_dev, 16);
        if (!skb) {
                sprintf(pkt_dev->result, "No memory");
                return NULL;
        }
-       prefetchw(skb->data);
 
+       prefetchw(skb->data);
        skb_reserve(skb, 16);
 
        /*  Reserve for ethernet and IP header  */
@@ -2822,13 +2822,14 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
                *vlan_encapsulated_proto = htons(ETH_P_IPV6);
        }
 
-       skb->network_header = skb->tail;
-       skb->transport_header = skb->network_header + sizeof(struct ipv6hdr);
-       skb_put(skb, sizeof(struct ipv6hdr) + sizeof(struct udphdr));
+       skb_set_mac_header(skb, 0);
+       skb_set_network_header(skb, skb->len);
+       iph = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+
+       skb_set_transport_header(skb, skb->len);
+       udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));
        skb_set_queue_mapping(skb, queue_map);
        skb->priority = pkt_dev->skb_priority;
-       iph = ipv6_hdr(skb);
-       udph = udp_hdr(skb);
 
        memcpy(eth, pkt_dev->hh, 12);
        *(__be16 *) &eth[12] = protocol;
@@ -2863,8 +2864,6 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
        iph->daddr = pkt_dev->cur_in6_daddr;
        iph->saddr = pkt_dev->cur_in6_saddr;
 
-       skb->mac_header = (skb->network_header - ETH_HLEN -
-                          pkt_dev->pkt_overhead);
        skb->protocol = protocol;
        skb->dev = odev;
        skb->pkt_type = PACKET_HOST;
index a08bd2b..3de7408 100644 (file)
@@ -947,6 +947,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        struct ifla_vf_vlan vf_vlan;
                        struct ifla_vf_tx_rate vf_tx_rate;
                        struct ifla_vf_spoofchk vf_spoofchk;
+                       struct ifla_vf_link_state vf_linkstate;
 
                        /*
                         * Not all SR-IOV capable drivers support the
@@ -956,18 +957,24 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                         */
                        ivi.spoofchk = -1;
                        memset(ivi.mac, 0, sizeof(ivi.mac));
+                       /* The default value for VF link state is "auto"
+                        * IFLA_VF_LINK_STATE_AUTO which equals zero
+                        */
+                       ivi.linkstate = 0;
                        if (dev->netdev_ops->ndo_get_vf_config(dev, i, &ivi))
                                break;
                        vf_mac.vf =
                                vf_vlan.vf =
                                vf_tx_rate.vf =
-                               vf_spoofchk.vf = ivi.vf;
+                               vf_spoofchk.vf =
+                               vf_linkstate.vf = ivi.vf;
 
                        memcpy(vf_mac.mac, ivi.mac, sizeof(ivi.mac));
                        vf_vlan.vlan = ivi.vlan;
                        vf_vlan.qos = ivi.qos;
                        vf_tx_rate.rate = ivi.tx_rate;
                        vf_spoofchk.setting = ivi.spoofchk;
+                       vf_linkstate.link_state = ivi.linkstate;
                        vf = nla_nest_start(skb, IFLA_VF_INFO);
                        if (!vf) {
                                nla_nest_cancel(skb, vfinfo);
@@ -978,7 +985,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                            nla_put(skb, IFLA_VF_TX_RATE, sizeof(vf_tx_rate),
                                    &vf_tx_rate) ||
                            nla_put(skb, IFLA_VF_SPOOFCHK, sizeof(vf_spoofchk),
-                                   &vf_spoofchk))
+                                   &vf_spoofchk) ||
+                           nla_put(skb, IFLA_VF_LINK_STATE, sizeof(vf_linkstate),
+                                   &vf_linkstate))
                                goto nla_put_failure;
                        nla_nest_end(skb, vf);
                }
@@ -1238,6 +1247,15 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr)
                                                               ivs->setting);
                        break;
                }
+               case IFLA_VF_LINK_STATE: {
+                       struct ifla_vf_link_state *ivl;
+                       ivl = nla_data(vf);
+                       err = -EOPNOTSUPP;
+                       if (ops->ndo_set_vf_link_state)
+                               err = ops->ndo_set_vf_link_state(dev, ivl->vf,
+                                                                ivl->link_state);
+                       break;
+               }
                default:
                        err = -EINVAL;
                        break;
@@ -2091,10 +2109,6 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
        }
 
        addr = nla_data(tb[NDA_LLADDR]);
-       if (is_zero_ether_addr(addr)) {
-               pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid ether address\n");
-               return -EINVAL;
-       }
 
        err = -EOPNOTSUPP;
 
@@ -2192,10 +2206,6 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
        }
 
        addr = nla_data(tb[NDA_LLADDR]);
-       if (is_zero_ether_addr(addr)) {
-               pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid ether address\n");
-               return -EINVAL;
-       }
 
        err = -EOPNOTSUPP;
 
@@ -2667,7 +2677,7 @@ static void rtnetlink_rcv(struct sk_buff *skb)
 
 static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        switch (event) {
        case NETDEV_UP:
index 1c1738c..724bb7c 100644 (file)
@@ -199,9 +199,7 @@ struct sk_buff *__alloc_skb_head(gfp_t gfp_mask, int node)
        skb->truesize = sizeof(struct sk_buff);
        atomic_set(&skb->users, 1);
 
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
-       skb->mac_header = ~0U;
-#endif
+       skb->mac_header = (typeof(skb->mac_header))~0U;
 out:
        return skb;
 }
@@ -275,10 +273,8 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
        skb->data = data;
        skb_reset_tail_pointer(skb);
        skb->end = skb->tail + size;
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
-       skb->mac_header = ~0U;
-       skb->transport_header = ~0U;
-#endif
+       skb->mac_header = (typeof(skb->mac_header))~0U;
+       skb->transport_header = (typeof(skb->transport_header))~0U;
 
        /* make sure we initialize shinfo sequentially */
        shinfo = skb_shinfo(skb);
@@ -344,10 +340,8 @@ struct sk_buff *build_skb(void *data, unsigned int frag_size)
        skb->data = data;
        skb_reset_tail_pointer(skb);
        skb->end = skb->tail + size;
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
-       skb->mac_header = ~0U;
-       skb->transport_header = ~0U;
-#endif
+       skb->mac_header = (typeof(skb->mac_header))~0U;
+       skb->transport_header = (typeof(skb->transport_header))~0U;
 
        /* make sure we initialize shinfo sequentially */
        shinfo = skb_shinfo(skb);
@@ -703,6 +697,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->transport_header   = old->transport_header;
        new->network_header     = old->network_header;
        new->mac_header         = old->mac_header;
+       new->inner_protocol     = old->inner_protocol;
        new->inner_transport_header = old->inner_transport_header;
        new->inner_network_header = old->inner_network_header;
        new->inner_mac_header = old->inner_mac_header;
@@ -743,6 +738,10 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->vlan_tci           = old->vlan_tci;
 
        skb_copy_secmark(new, old);
+
+#ifdef CONFIG_NET_LL_RX_POLL
+       new->napi_id    = old->napi_id;
+#endif
 }
 
 /*
@@ -915,18 +914,8 @@ static void skb_headers_offset_update(struct sk_buff *skb, int off)
 
 static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 {
-#ifndef NET_SKBUFF_DATA_USES_OFFSET
-       /*
-        *      Shift between the two data areas in bytes
-        */
-       unsigned long offset = new->data - old->data;
-#endif
-
        __copy_skb_header(new, old);
 
-#ifndef NET_SKBUFF_DATA_USES_OFFSET
-       skb_headers_offset_update(new, offset);
-#endif
        skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size;
        skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs;
        skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type;
@@ -1118,7 +1107,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
        skb->end      = skb->head + size;
 #endif
        skb->tail             += off;
-       skb_headers_offset_update(skb, off);
+       skb_headers_offset_update(skb, nhead);
        /* Only adjust this if it actually is csum_start rather than csum */
        if (skb->ip_summed == CHECKSUM_PARTIAL)
                skb->csum_start += nhead;
@@ -1213,9 +1202,8 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
        off                  = newheadroom - oldheadroom;
        if (n->ip_summed == CHECKSUM_PARTIAL)
                n->csum_start += off;
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
+
        skb_headers_offset_update(n, off);
-#endif
 
        return n;
 }
@@ -2558,8 +2546,13 @@ unsigned int skb_seq_read(unsigned int consumed, const u8 **data,
        unsigned int block_limit, abs_offset = consumed + st->lower_offset;
        skb_frag_t *frag;
 
-       if (unlikely(abs_offset >= st->upper_offset))
+       if (unlikely(abs_offset >= st->upper_offset)) {
+               if (st->frag_data) {
+                       kunmap_atomic(st->frag_data);
+                       st->frag_data = NULL;
+               }
                return 0;
+       }
 
 next_skb:
        block_limit = skb_headlen(st->cur_skb) + st->stepped_offset;
@@ -2857,7 +2850,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
                                                 doffset + tnl_hlen);
 
                if (fskb != skb_shinfo(skb)->frag_list)
-                       continue;
+                       goto perform_csum_check;
 
                if (!sg) {
                        nskb->ip_summed = CHECKSUM_NONE;
@@ -2921,6 +2914,7 @@ skip_fraglist:
                nskb->len += nskb->data_len;
                nskb->truesize += nskb->data_len;
 
+perform_csum_check:
                if (!csum) {
                        nskb->csum = skb_checksum(nskb, doffset,
                                                  nskb->len - doffset, 0);
@@ -3503,3 +3497,26 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
        return true;
 }
 EXPORT_SYMBOL(skb_try_coalesce);
+
+/**
+ * skb_scrub_packet - scrub an skb before sending it to another netns
+ *
+ * @skb: buffer to clean
+ *
+ * skb_scrub_packet can be used to clean an skb before injecting it in
+ * another namespace. We have to clear all information in the skb that
+ * could impact namespace isolation.
+ */
+void skb_scrub_packet(struct sk_buff *skb)
+{
+       skb_orphan(skb);
+       skb->tstamp.tv64 = 0;
+       skb->pkt_type = PACKET_HOST;
+       skb->skb_iif = 0;
+       skb_dst_drop(skb);
+       skb->mark = 0;
+       secpath_reset(skb);
+       nf_reset(skb);
+       nf_reset_trace(skb);
+}
+EXPORT_SYMBOL_GPL(skb_scrub_packet);
index d6d024c..ab06b71 100644 (file)
 #include <net/tcp.h>
 #endif
 
+#include <net/ll_poll.h>
+
 static DEFINE_MUTEX(proto_list_mutex);
 static LIST_HEAD(proto_list);
 
@@ -898,6 +900,19 @@ set_rcvbuf:
                sock_valbool_flag(sk, SOCK_SELECT_ERR_QUEUE, valbool);
                break;
 
+#ifdef CONFIG_NET_LL_RX_POLL
+       case SO_LL:
+               /* allow unprivileged users to decrease the value */
+               if ((val > sk->sk_ll_usec) && !capable(CAP_NET_ADMIN))
+                       ret = -EPERM;
+               else {
+                       if (val < 0)
+                               ret = -EINVAL;
+                       else
+                               sk->sk_ll_usec = val;
+               }
+               break;
+#endif
        default:
                ret = -ENOPROTOOPT;
                break;
@@ -1155,6 +1170,12 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                v.val = sock_flag(sk, SOCK_SELECT_ERR_QUEUE);
                break;
 
+#ifdef CONFIG_NET_LL_RX_POLL
+       case SO_LL:
+               v.val = sk->sk_ll_usec;
+               break;
+#endif
+
        default:
                return -ENOPROTOOPT;
        }
@@ -2271,6 +2292,11 @@ void sock_init_data(struct socket *sock, struct sock *sk)
 
        sk->sk_stamp = ktime_set(-1L, 0);
 
+#ifdef CONFIG_NET_LL_RX_POLL
+       sk->sk_napi_id          =       0;
+       sk->sk_ll_usec          =       sysctl_net_ll_read;
+#endif
+
        /*
         * Before updating sk_refcnt, we must commit prior changes to memory
         * (Documentation/RCU/rculist_nulls.txt for details)
index cfdb46a..afc677e 100644 (file)
 #include <net/ip.h>
 #include <net/sock.h>
 #include <net/net_ratelimit.h>
+#include <net/ll_poll.h>
 
 static int one = 1;
 
 #ifdef CONFIG_RPS
-static int rps_sock_flow_sysctl(ctl_table *table, int write,
+static int rps_sock_flow_sysctl(struct ctl_table *table, int write,
                                void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        unsigned int orig_size, size;
        int ret, i;
-       ctl_table tmp = {
+       struct ctl_table tmp = {
                .data = &size,
                .maxlen = sizeof(size),
                .mode = table->mode
@@ -87,6 +88,109 @@ static int rps_sock_flow_sysctl(ctl_table *table, int write,
 }
 #endif /* CONFIG_RPS */
 
+#ifdef CONFIG_NET_FLOW_LIMIT
+static DEFINE_MUTEX(flow_limit_update_mutex);
+
+static int flow_limit_cpu_sysctl(struct ctl_table *table, int write,
+                                void __user *buffer, size_t *lenp,
+                                loff_t *ppos)
+{
+       struct sd_flow_limit *cur;
+       struct softnet_data *sd;
+       cpumask_var_t mask;
+       int i, len, ret = 0;
+
+       if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+               return -ENOMEM;
+
+       if (write) {
+               ret = cpumask_parse_user(buffer, *lenp, mask);
+               if (ret)
+                       goto done;
+
+               mutex_lock(&flow_limit_update_mutex);
+               len = sizeof(*cur) + netdev_flow_limit_table_len;
+               for_each_possible_cpu(i) {
+                       sd = &per_cpu(softnet_data, i);
+                       cur = rcu_dereference_protected(sd->flow_limit,
+                                    lockdep_is_held(&flow_limit_update_mutex));
+                       if (cur && !cpumask_test_cpu(i, mask)) {
+                               RCU_INIT_POINTER(sd->flow_limit, NULL);
+                               synchronize_rcu();
+                               kfree(cur);
+                       } else if (!cur && cpumask_test_cpu(i, mask)) {
+                               cur = kzalloc(len, GFP_KERNEL);
+                               if (!cur) {
+                                       /* not unwinding previous changes */
+                                       ret = -ENOMEM;
+                                       goto write_unlock;
+                               }
+                               cur->num_buckets = netdev_flow_limit_table_len;
+                               rcu_assign_pointer(sd->flow_limit, cur);
+                       }
+               }
+write_unlock:
+               mutex_unlock(&flow_limit_update_mutex);
+       } else {
+               char kbuf[128];
+
+               if (*ppos || !*lenp) {
+                       *lenp = 0;
+                       goto done;
+               }
+
+               cpumask_clear(mask);
+               rcu_read_lock();
+               for_each_possible_cpu(i) {
+                       sd = &per_cpu(softnet_data, i);
+                       if (rcu_dereference(sd->flow_limit))
+                               cpumask_set_cpu(i, mask);
+               }
+               rcu_read_unlock();
+
+               len = min(sizeof(kbuf) - 1, *lenp);
+               len = cpumask_scnprintf(kbuf, len, mask);
+               if (!len) {
+                       *lenp = 0;
+                       goto done;
+               }
+               if (len < *lenp)
+                       kbuf[len++] = '\n';
+               if (copy_to_user(buffer, kbuf, len)) {
+                       ret = -EFAULT;
+                       goto done;
+               }
+               *lenp = len;
+               *ppos += len;
+       }
+
+done:
+       free_cpumask_var(mask);
+       return ret;
+}
+
+static int flow_limit_table_len_sysctl(struct ctl_table *table, int write,
+                                      void __user *buffer, size_t *lenp,
+                                      loff_t *ppos)
+{
+       unsigned int old, *ptr;
+       int ret;
+
+       mutex_lock(&flow_limit_update_mutex);
+
+       ptr = table->data;
+       old = *ptr;
+       ret = proc_dointvec(table, write, buffer, lenp, ppos);
+       if (!ret && write && !is_power_of_2(*ptr)) {
+               *ptr = old;
+               ret = -EINVAL;
+       }
+
+       mutex_unlock(&flow_limit_update_mutex);
+       return ret;
+}
+#endif /* CONFIG_NET_FLOW_LIMIT */
+
 static struct ctl_table net_core_table[] = {
 #ifdef CONFIG_NET
        {
@@ -180,6 +284,37 @@ static struct ctl_table net_core_table[] = {
                .proc_handler   = rps_sock_flow_sysctl
        },
 #endif
+#ifdef CONFIG_NET_FLOW_LIMIT
+       {
+               .procname       = "flow_limit_cpu_bitmap",
+               .mode           = 0644,
+               .proc_handler   = flow_limit_cpu_sysctl
+       },
+       {
+               .procname       = "flow_limit_table_len",
+               .data           = &netdev_flow_limit_table_len,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = flow_limit_table_len_sysctl
+       },
+#endif /* CONFIG_NET_FLOW_LIMIT */
+#ifdef CONFIG_NET_LL_RX_POLL
+       {
+               .procname       = "low_latency_poll",
+               .data           = &sysctl_net_ll_poll,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec
+       },
+       {
+               .procname       = "low_latency_read",
+               .data           = &sysctl_net_ll_read,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec
+       },
+#
+#endif
 #endif /* CONFIG_NET */
        {
                .procname       = "netdev_budget",
index c21f200..dd4d506 100644 (file)
@@ -2078,9 +2078,9 @@ out_err:
 }
 
 static int dn_device_event(struct notifier_block *this, unsigned long event,
-                       void *ptr)
+                          void *ptr)
 {
-       struct net_device *dev = (struct net_device *)ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
index 7d91970..dd0dfb2 100644 (file)
@@ -158,11 +158,11 @@ static int max_t3[] = { 8191 }; /* Must fit in 16 bits when multiplied by BCT3MU
 static int min_priority[1];
 static int max_priority[] = { 127 }; /* From DECnet spec */
 
-static int dn_forwarding_proc(ctl_table *, int,
+static int dn_forwarding_proc(struct ctl_table *, int,
                        void __user *, size_t *, loff_t *);
 static struct dn_dev_sysctl_table {
        struct ctl_table_header *sysctl_header;
-       ctl_table dn_dev_vars[5];
+       struct ctl_table dn_dev_vars[5];
 } dn_dev_sysctl = {
        NULL,
        {
@@ -242,7 +242,7 @@ static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms)
        }
 }
 
-static int dn_forwarding_proc(ctl_table *table, int write,
+static int dn_forwarding_proc(struct ctl_table *table, int write,
                                void __user *buffer,
                                size_t *lenp, loff_t *ppos)
 {
index a55eecc..5325b54 100644 (file)
@@ -132,7 +132,7 @@ static int parse_addr(__le16 *addr, char *str)
        return 0;
 }
 
-static int dn_node_address_handler(ctl_table *table, int write,
+static int dn_node_address_handler(struct ctl_table *table, int write,
                                void __user *buffer,
                                size_t *lenp, loff_t *ppos)
 {
@@ -183,7 +183,7 @@ static int dn_node_address_handler(ctl_table *table, int write,
        return 0;
 }
 
-static int dn_def_dev_handler(ctl_table *table, int write,
+static int dn_def_dev_handler(struct ctl_table *table, int write,
                                void __user *buffer,
                                size_t *lenp, loff_t *ppos)
 {
@@ -246,7 +246,7 @@ static int dn_def_dev_handler(ctl_table *table, int write,
        return 0;
 }
 
-static ctl_table dn_table[] = {
+static struct ctl_table dn_table[] = {
        {
                .procname = "node_address",
                .maxlen = 7,
index 55e1fd5..3b9d5f2 100644 (file)
@@ -1352,10 +1352,9 @@ static inline void lowpan_netlink_fini(void)
 }
 
 static int lowpan_device_event(struct notifier_block *unused,
-                               unsigned long event,
-                               void *ptr)
+                              unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        LIST_HEAD(del_list);
        struct lowpan_dev_record *entry, *tmp;
 
index 8603ca8..37cf1a6 100644 (file)
@@ -9,10 +9,7 @@ config IP_MULTICAST
          intend to participate in the MBONE, a high bandwidth network on top
          of the Internet which carries audio and video broadcasts. More
          information about the MBONE is on the WWW at
-         <http://www.savetz.com/mbone/>. Information about the multicast
-         capabilities of the various network cards is contained in
-         <file:Documentation/networking/multicast.txt>. For most people, it's
-         safe to say N.
+         <http://www.savetz.com/mbone/>. For most people, it's safe to say N.
 
 config IP_ADVANCED_ROUTER
        bool "IP: advanced router"
@@ -223,10 +220,8 @@ config IP_MROUTE
          packets that have several destination addresses. It is needed on the
          MBONE, a high bandwidth network on top of the Internet which carries
          audio and video broadcasts. In order to do that, you would most
-         likely run the program mrouted. Information about the multicast
-         capabilities of the various network cards is contained in
-         <file:Documentation/networking/multicast.txt>. If you haven't heard
-         about it, you don't need it.
+         likely run the program mrouted. If you haven't heard about it, you
+         don't need it.
 
 config IP_MROUTE_MULTIPLE_TABLES
        bool "IP: multicast policy routing"
index 089cb9f..4b81e91 100644 (file)
@@ -8,10 +8,10 @@ obj-y     := route.o inetpeer.o protocol.o \
             inet_timewait_sock.o inet_connection_sock.o \
             tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \
             tcp_minisocks.o tcp_cong.o tcp_metrics.o tcp_fastopen.o \
-            datagram.o raw.o udp.o udplite.o \
-            arp.o icmp.o devinet.o af_inet.o  igmp.o \
+            tcp_offload.o datagram.o raw.o udp.o udplite.o \
+            udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \
             fib_frontend.o fib_semantics.o fib_trie.o \
-            inet_fragment.o ping.o
+            inet_fragment.o ping.o ip_tunnel_core.o
 
 obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o
 obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o
@@ -19,6 +19,7 @@ obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o
 obj-$(CONFIG_IP_MROUTE) += ipmr.o
 obj-$(CONFIG_NET_IPIP) += ipip.o
+gre-y := gre_demux.o gre_offload.o
 obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o
 obj-$(CONFIG_NET_IPGRE) += ip_gre.o
 obj-$(CONFIG_NET_IPVTI) += ip_vti.o
index d01be2a..b4d0be2 100644 (file)
@@ -1295,6 +1295,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
                       SKB_GSO_GRE |
                       SKB_GSO_TCPV6 |
                       SKB_GSO_UDP_TUNNEL |
+                      SKB_GSO_MPLS |
                       0)))
                goto out;
 
@@ -1384,7 +1385,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
                goto out_unlock;
 
        id = ntohl(*(__be32 *)&iph->id);
-       flush = (u16)((ntohl(*(__be32 *)iph) ^ skb_gro_len(skb)) | (id IP_DF));
+       flush = (u16)((ntohl(*(__be32 *)iph) ^ skb_gro_len(skb)) | (id & ~IP_DF));
        id >>= 16;
 
        for (p = *head; p; p = p->next) {
@@ -1406,6 +1407,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
                NAPI_GRO_CB(p)->flush |=
                        (iph->ttl ^ iph2->ttl) |
                        (iph->tos ^ iph2->tos) |
+                       (__force int)((iph->frag_off ^ iph2->frag_off) & htons(IP_DF)) |
                        ((u16)(ntohs(iph2->id) + NAPI_GRO_CB(p)->count) ^ id);
 
                NAPI_GRO_CB(p)->flush |= flush;
@@ -1557,15 +1559,6 @@ static const struct net_protocol tcp_protocol = {
        .netns_ok       =       1,
 };
 
-static const struct net_offload tcp_offload = {
-       .callbacks = {
-               .gso_send_check =       tcp_v4_gso_send_check,
-               .gso_segment    =       tcp_tso_segment,
-               .gro_receive    =       tcp4_gro_receive,
-               .gro_complete   =       tcp4_gro_complete,
-       },
-};
-
 static const struct net_protocol udp_protocol = {
        .handler =      udp_rcv,
        .err_handler =  udp_err,
@@ -1573,13 +1566,6 @@ static const struct net_protocol udp_protocol = {
        .netns_ok =     1,
 };
 
-static const struct net_offload udp_offload = {
-       .callbacks = {
-               .gso_send_check = udp4_ufo_send_check,
-               .gso_segment = udp4_ufo_fragment,
-       },
-};
-
 static const struct net_protocol icmp_protocol = {
        .handler =      icmp_rcv,
        .err_handler =  icmp_err,
@@ -1679,10 +1665,10 @@ static int __init ipv4_offload_init(void)
        /*
         * Add offloads
         */
-       if (inet_add_offload(&udp_offload, IPPROTO_UDP) < 0)
+       if (udpv4_offload_init() < 0)
                pr_crit("%s: Cannot add UDP protocol offload\n", __func__);
-       if (inet_add_offload(&tcp_offload, IPPROTO_TCP) < 0)
-               pr_crit("%s: Cannot add TCP protocol offlaod\n", __func__);
+       if (tcpv4_offload_init() < 0)
+               pr_crit("%s: Cannot add TCP protocol offload\n", __func__);
 
        dev_add_offload(&ip_packet_offload);
        return 0;
index 2e7f194..7179026 100644 (file)
@@ -419,12 +419,9 @@ static void ah4_err(struct sk_buff *skb, u32 info)
        if (!x)
                return;
 
-       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) {
-               atomic_inc(&flow_cache_genid);
-               rt_genid_bump(net);
-
+       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
                ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0);
-       else
+       else
                ipv4_redirect(skb, net, 0, 0, IPPROTO_AH, 0);
        xfrm_state_put(x);
 }
index 247ec19..4429b01 100644 (file)
@@ -1234,13 +1234,19 @@ out:
 static int arp_netdev_event(struct notifier_block *this, unsigned long event,
                            void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct netdev_notifier_change_info *change_info;
 
        switch (event) {
        case NETDEV_CHANGEADDR:
                neigh_changeaddr(&arp_tbl, dev);
                rt_cache_flush(dev_net(dev));
                break;
+       case NETDEV_CHANGE:
+               change_info = ptr;
+               if (change_info->flags_changed & IFF_NOARP)
+                       neigh_changeaddr(&arp_tbl, dev);
+               break;
        default:
                break;
        }
index dfc39d4..8d48c39 100644 (file)
@@ -215,6 +215,7 @@ void in_dev_finish_destroy(struct in_device *idev)
 
        WARN_ON(idev->ifa_list);
        WARN_ON(idev->mc_list);
+       kfree(rcu_dereference_protected(idev->mc_hash, 1));
 #ifdef NET_REFCNT_DEBUG
        pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
 #endif
@@ -1333,7 +1334,7 @@ static void inetdev_send_gratuitous_arp(struct net_device *dev,
 static int inetdev_event(struct notifier_block *this, unsigned long event,
                         void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct in_device *in_dev = __in_dev_get_rtnl(dev);
 
        ASSERT_RTNL();
@@ -1941,7 +1942,7 @@ static void inet_forward_change(struct net *net)
        }
 }
 
-static int devinet_conf_proc(ctl_table *ctl, int write,
+static int devinet_conf_proc(struct ctl_table *ctl, int write,
                             void __user *buffer,
                             size_t *lenp, loff_t *ppos)
 {
@@ -1984,7 +1985,7 @@ static int devinet_conf_proc(ctl_table *ctl, int write,
        return ret;
 }
 
-static int devinet_sysctl_forward(ctl_table *ctl, int write,
+static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
                                  void __user *buffer,
                                  size_t *lenp, loff_t *ppos)
 {
@@ -2027,7 +2028,7 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
        return ret;
 }
 
-static int ipv4_doint_and_flush(ctl_table *ctl, int write,
+static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
                                void __user *buffer,
                                size_t *lenp, loff_t *ppos)
 {
index 4cfe34d..ab3d814 100644 (file)
@@ -502,12 +502,9 @@ static void esp4_err(struct sk_buff *skb, u32 info)
        if (!x)
                return;
 
-       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) {
-               atomic_inc(&flow_cache_genid);
-               rt_genid_bump(net);
-
+       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
                ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0);
-       else
+       else
                ipv4_redirect(skb, net, 0, 0, IPPROTO_ESP, 0);
        xfrm_state_put(x);
 }
index c7629a2..b3f627a 100644 (file)
@@ -961,7 +961,7 @@ static void nl_fib_input(struct sk_buff *skb)
            nlmsg_len(nlh) < sizeof(*frn))
                return;
 
-       skb = skb_clone(skb, GFP_KERNEL);
+       skb = netlink_skb_clone(skb, GFP_KERNEL);
        if (skb == NULL)
                return;
        nlh = nlmsg_hdr(skb);
@@ -1038,7 +1038,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
 
 static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct in_device *in_dev;
        struct net *net = dev_net(dev);
 
index 8f6cb7a..d5dbca5 100644 (file)
@@ -169,7 +169,8 @@ static void free_nh_exceptions(struct fib_nh *nh)
                        
                        next = rcu_dereference_protected(fnhe->fnhe_next, 1);
 
-                       rt_fibinfo_free(&fnhe->fnhe_rth);
+                       rt_fibinfo_free(&fnhe->fnhe_rth_input);
+                       rt_fibinfo_free(&fnhe->fnhe_rth_output);
 
                        kfree(fnhe);
 
diff --git a/net/ipv4/gre.c b/net/ipv4/gre.c
deleted file mode 100644 (file)
index 7856d16..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- *     GRE over IPv4 demultiplexer driver
- *
- *     Authors: Dmitry Kozlov (xeb@mail.ru)
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License
- *     as published by the Free Software Foundation; either version
- *     2 of the License, or (at your option) any later version.
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/kmod.h>
-#include <linux/skbuff.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/netdevice.h>
-#include <linux/if_tunnel.h>
-#include <linux/spinlock.h>
-#include <net/protocol.h>
-#include <net/gre.h>
-
-
-static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
-static DEFINE_SPINLOCK(gre_proto_lock);
-
-int gre_add_protocol(const struct gre_protocol *proto, u8 version)
-{
-       if (version >= GREPROTO_MAX)
-               goto err_out;
-
-       spin_lock(&gre_proto_lock);
-       if (gre_proto[version])
-               goto err_out_unlock;
-
-       RCU_INIT_POINTER(gre_proto[version], proto);
-       spin_unlock(&gre_proto_lock);
-       return 0;
-
-err_out_unlock:
-       spin_unlock(&gre_proto_lock);
-err_out:
-       return -1;
-}
-EXPORT_SYMBOL_GPL(gre_add_protocol);
-
-int gre_del_protocol(const struct gre_protocol *proto, u8 version)
-{
-       if (version >= GREPROTO_MAX)
-               goto err_out;
-
-       spin_lock(&gre_proto_lock);
-       if (rcu_dereference_protected(gre_proto[version],
-                       lockdep_is_held(&gre_proto_lock)) != proto)
-               goto err_out_unlock;
-       RCU_INIT_POINTER(gre_proto[version], NULL);
-       spin_unlock(&gre_proto_lock);
-       synchronize_rcu();
-       return 0;
-
-err_out_unlock:
-       spin_unlock(&gre_proto_lock);
-err_out:
-       return -1;
-}
-EXPORT_SYMBOL_GPL(gre_del_protocol);
-
-static int gre_rcv(struct sk_buff *skb)
-{
-       const struct gre_protocol *proto;
-       u8 ver;
-       int ret;
-
-       if (!pskb_may_pull(skb, 12))
-               goto drop;
-
-       ver = skb->data[1]&0x7f;
-       if (ver >= GREPROTO_MAX)
-               goto drop;
-
-       rcu_read_lock();
-       proto = rcu_dereference(gre_proto[ver]);
-       if (!proto || !proto->handler)
-               goto drop_unlock;
-       ret = proto->handler(skb);
-       rcu_read_unlock();
-       return ret;
-
-drop_unlock:
-       rcu_read_unlock();
-drop:
-       kfree_skb(skb);
-       return NET_RX_DROP;
-}
-
-static void gre_err(struct sk_buff *skb, u32 info)
-{
-       const struct gre_protocol *proto;
-       const struct iphdr *iph = (const struct iphdr *)skb->data;
-       u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f;
-
-       if (ver >= GREPROTO_MAX)
-               return;
-
-       rcu_read_lock();
-       proto = rcu_dereference(gre_proto[ver]);
-       if (proto && proto->err_handler)
-               proto->err_handler(skb, info);
-       rcu_read_unlock();
-}
-
-static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
-                                      netdev_features_t features)
-{
-       struct sk_buff *segs = ERR_PTR(-EINVAL);
-       netdev_features_t enc_features;
-       int ghl = GRE_HEADER_SECTION;
-       struct gre_base_hdr *greh;
-       int mac_len = skb->mac_len;
-       __be16 protocol = skb->protocol;
-       int tnl_hlen;
-       bool csum;
-
-       if (unlikely(skb_shinfo(skb)->gso_type &
-                               ~(SKB_GSO_TCPV4 |
-                                 SKB_GSO_TCPV6 |
-                                 SKB_GSO_UDP |
-                                 SKB_GSO_DODGY |
-                                 SKB_GSO_TCP_ECN |
-                                 SKB_GSO_GRE)))
-               goto out;
-
-       if (unlikely(!pskb_may_pull(skb, sizeof(*greh))))
-               goto out;
-
-       greh = (struct gre_base_hdr *)skb_transport_header(skb);
-
-       if (greh->flags & GRE_KEY)
-               ghl += GRE_HEADER_SECTION;
-       if (greh->flags & GRE_SEQ)
-               ghl += GRE_HEADER_SECTION;
-       if (greh->flags & GRE_CSUM) {
-               ghl += GRE_HEADER_SECTION;
-               csum = true;
-       } else
-               csum = false;
-
-       /* setup inner skb. */
-       skb->protocol = greh->protocol;
-       skb->encapsulation = 0;
-
-       if (unlikely(!pskb_may_pull(skb, ghl)))
-               goto out;
-       __skb_pull(skb, ghl);
-       skb_reset_mac_header(skb);
-       skb_set_network_header(skb, skb_inner_network_offset(skb));
-       skb->mac_len = skb_inner_network_offset(skb);
-
-       /* segment inner packet. */
-       enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
-       segs = skb_mac_gso_segment(skb, enc_features);
-       if (!segs || IS_ERR(segs))
-               goto out;
-
-       skb = segs;
-       tnl_hlen = skb_tnl_header_len(skb);
-       do {
-               __skb_push(skb, ghl);
-               if (csum) {
-                       __be32 *pcsum;
-
-                       if (skb_has_shared_frag(skb)) {
-                               int err;
-
-                               err = __skb_linearize(skb);
-                               if (err) {
-                                       kfree_skb_list(segs);
-                                       segs = ERR_PTR(err);
-                                       goto out;
-                               }
-                       }
-
-                       greh = (struct gre_base_hdr *)(skb->data);
-                       pcsum = (__be32 *)(greh + 1);
-                       *pcsum = 0;
-                       *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0));
-               }
-               __skb_push(skb, tnl_hlen - ghl);
-
-               skb_reset_mac_header(skb);
-               skb_set_network_header(skb, mac_len);
-               skb->mac_len = mac_len;
-               skb->protocol = protocol;
-       } while ((skb = skb->next));
-out:
-       return segs;
-}
-
-static int gre_gso_send_check(struct sk_buff *skb)
-{
-       if (!skb->encapsulation)
-               return -EINVAL;
-       return 0;
-}
-
-static const struct net_protocol net_gre_protocol = {
-       .handler     = gre_rcv,
-       .err_handler = gre_err,
-       .netns_ok    = 1,
-};
-
-static const struct net_offload gre_offload = {
-       .callbacks = {
-               .gso_send_check =       gre_gso_send_check,
-               .gso_segment    =       gre_gso_segment,
-       },
-};
-
-static int __init gre_init(void)
-{
-       pr_info("GRE over IPv4 demultiplexor driver\n");
-
-       if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
-               pr_err("can't add protocol\n");
-               return -EAGAIN;
-       }
-
-       if (inet_add_offload(&gre_offload, IPPROTO_GRE)) {
-               pr_err("can't add protocol offload\n");
-               inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
-               return -EAGAIN;
-       }
-
-       return 0;
-}
-
-static void __exit gre_exit(void)
-{
-       inet_del_offload(&gre_offload, IPPROTO_GRE);
-       inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
-}
-
-module_init(gre_init);
-module_exit(gre_exit);
-
-MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver");
-MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
-MODULE_LICENSE("GPL");
-
diff --git a/net/ipv4/gre_demux.c b/net/ipv4/gre_demux.c
new file mode 100644 (file)
index 0000000..736c9fc
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ *     GRE over IPv4 demultiplexer driver
+ *
+ *     Authors: Dmitry Kozlov (xeb@mail.ru)
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/if.h>
+#include <linux/icmp.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/netdevice.h>
+#include <linux/if_tunnel.h>
+#include <linux/spinlock.h>
+#include <net/protocol.h>
+#include <net/gre.h>
+
+#include <net/icmp.h>
+#include <net/route.h>
+#include <net/xfrm.h>
+
+static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
+static struct gre_cisco_protocol __rcu *gre_cisco_proto_list[GRE_IP_PROTO_MAX];
+
+int gre_add_protocol(const struct gre_protocol *proto, u8 version)
+{
+       if (version >= GREPROTO_MAX)
+               return -EINVAL;
+
+       return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ?
+               0 : -EBUSY;
+}
+EXPORT_SYMBOL_GPL(gre_add_protocol);
+
+int gre_del_protocol(const struct gre_protocol *proto, u8 version)
+{
+       int ret;
+
+       if (version >= GREPROTO_MAX)
+               return -EINVAL;
+
+       ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ?
+               0 : -EBUSY;
+
+       if (ret)
+               return ret;
+
+       synchronize_rcu();
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gre_del_protocol);
+
+void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
+                     int hdr_len)
+{
+       struct gre_base_hdr *greh;
+
+       skb_push(skb, hdr_len);
+
+       greh = (struct gre_base_hdr *)skb->data;
+       greh->flags = tnl_flags_to_gre_flags(tpi->flags);
+       greh->protocol = tpi->proto;
+
+       if (tpi->flags&(TUNNEL_KEY|TUNNEL_CSUM|TUNNEL_SEQ)) {
+               __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
+
+               if (tpi->flags&TUNNEL_SEQ) {
+                       *ptr = tpi->seq;
+                       ptr--;
+               }
+               if (tpi->flags&TUNNEL_KEY) {
+                       *ptr = tpi->key;
+                       ptr--;
+               }
+               if (tpi->flags&TUNNEL_CSUM &&
+                   !(skb_shinfo(skb)->gso_type & SKB_GSO_GRE)) {
+                       *ptr = 0;
+                       *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
+                                                                skb->len, 0));
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(gre_build_header);
+
+struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum)
+{
+       int err;
+
+       if (likely(!skb->encapsulation)) {
+               skb_reset_inner_headers(skb);
+               skb->encapsulation = 1;
+       }
+
+       if (skb_is_gso(skb)) {
+               err = skb_unclone(skb, GFP_ATOMIC);
+               if (unlikely(err))
+                       goto error;
+               skb_shinfo(skb)->gso_type |= SKB_GSO_GRE;
+               return skb;
+       } else if (skb->ip_summed == CHECKSUM_PARTIAL && gre_csum) {
+               err = skb_checksum_help(skb);
+               if (unlikely(err))
+                       goto error;
+       } else if (skb->ip_summed != CHECKSUM_PARTIAL)
+               skb->ip_summed = CHECKSUM_NONE;
+
+       return skb;
+error:
+       kfree_skb(skb);
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(gre_handle_offloads);
+
+static __sum16 check_checksum(struct sk_buff *skb)
+{
+       __sum16 csum = 0;
+
+       switch (skb->ip_summed) {
+       case CHECKSUM_COMPLETE:
+               csum = csum_fold(skb->csum);
+
+               if (!csum)
+                       break;
+               /* Fall through. */
+
+       case CHECKSUM_NONE:
+               skb->csum = 0;
+               csum = __skb_checksum_complete(skb);
+               skb->ip_summed = CHECKSUM_COMPLETE;
+               break;
+       }
+
+       return csum;
+}
+
+static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
+                           bool *csum_err)
+{
+       unsigned int ip_hlen = ip_hdrlen(skb);
+       const struct gre_base_hdr *greh;
+       __be32 *options;
+       int hdr_len;
+
+       if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
+               return -EINVAL;
+
+       greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
+       if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
+               return -EINVAL;
+
+       tpi->flags = gre_flags_to_tnl_flags(greh->flags);
+       hdr_len = ip_gre_calc_hlen(tpi->flags);
+
+       if (!pskb_may_pull(skb, hdr_len))
+               return -EINVAL;
+
+       greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
+       tpi->proto = greh->protocol;
+
+       options = (__be32 *)(greh + 1);
+       if (greh->flags & GRE_CSUM) {
+               if (check_checksum(skb)) {
+                       *csum_err = true;
+                       return -EINVAL;
+               }
+               options++;
+       }
+
+       if (greh->flags & GRE_KEY) {
+               tpi->key = *options;
+               options++;
+       } else
+               tpi->key = 0;
+
+       if (unlikely(greh->flags & GRE_SEQ)) {
+               tpi->seq = *options;
+               options++;
+       } else
+               tpi->seq = 0;
+
+       /* WCCP version 1 and 2 protocol decoding.
+        * - Change protocol to IP
+        * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
+        */
+       if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
+               tpi->proto = htons(ETH_P_IP);
+               if ((*(u8 *)options & 0xF0) != 0x40) {
+                       hdr_len += 4;
+                       if (!pskb_may_pull(skb, hdr_len))
+                               return -EINVAL;
+               }
+       }
+
+       return iptunnel_pull_header(skb, hdr_len, tpi->proto);
+}
+
+static int gre_cisco_rcv(struct sk_buff *skb)
+{
+       struct tnl_ptk_info tpi;
+       int i;
+       bool csum_err = false;
+
+       if (parse_gre_header(skb, &tpi, &csum_err) < 0)
+               goto drop;
+
+       rcu_read_lock();
+       for (i = 0; i < GRE_IP_PROTO_MAX; i++) {
+               struct gre_cisco_protocol *proto;
+               int ret;
+
+               proto = rcu_dereference(gre_cisco_proto_list[i]);
+               if (!proto)
+                       continue;
+               ret = proto->handler(skb, &tpi);
+               if (ret == PACKET_RCVD) {
+                       rcu_read_unlock();
+                       return 0;
+               }
+       }
+       rcu_read_unlock();
+
+       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+drop:
+       kfree_skb(skb);
+       return 0;
+}
+
+static void gre_cisco_err(struct sk_buff *skb, u32 info)
+{
+       /* All the routers (except for Linux) return only
+        * 8 bytes of packet payload. It means, that precise relaying of
+        * ICMP in the real Internet is absolutely infeasible.
+        *
+        * Moreover, Cisco "wise men" put GRE key to the third word
+        * in GRE header. It makes impossible maintaining even soft
+        * state for keyed
+        * GRE tunnels with enabled checksum. Tell them "thank you".
+        *
+        * Well, I wonder, rfc1812 was written by Cisco employee,
+        * what the hell these idiots break standards established
+        * by themselves???
+        */
+
+       const int type = icmp_hdr(skb)->type;
+       const int code = icmp_hdr(skb)->code;
+       struct tnl_ptk_info tpi;
+       bool csum_err = false;
+       int i;
+
+       if (parse_gre_header(skb, &tpi, &csum_err)) {
+               if (!csum_err)          /* ignore csum errors. */
+                       return;
+       }
+
+       if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+               ipv4_update_pmtu(skb, dev_net(skb->dev), info,
+                               skb->dev->ifindex, 0, IPPROTO_GRE, 0);
+               return;
+       }
+       if (type == ICMP_REDIRECT) {
+               ipv4_redirect(skb, dev_net(skb->dev), skb->dev->ifindex, 0,
+                               IPPROTO_GRE, 0);
+               return;
+       }
+
+       rcu_read_lock();
+       for (i = 0; i < GRE_IP_PROTO_MAX; i++) {
+               struct gre_cisco_protocol *proto;
+
+               proto = rcu_dereference(gre_cisco_proto_list[i]);
+               if (!proto)
+                       continue;
+
+               if (proto->err_handler(skb, info, &tpi) == PACKET_RCVD)
+                       goto out;
+
+       }
+out:
+       rcu_read_unlock();
+}
+
+static int gre_rcv(struct sk_buff *skb)
+{
+       const struct gre_protocol *proto;
+       u8 ver;
+       int ret;
+
+       if (!pskb_may_pull(skb, 12))
+               goto drop;
+
+       ver = skb->data[1]&0x7f;
+       if (ver >= GREPROTO_MAX)
+               goto drop;
+
+       rcu_read_lock();
+       proto = rcu_dereference(gre_proto[ver]);
+       if (!proto || !proto->handler)
+               goto drop_unlock;
+       ret = proto->handler(skb);
+       rcu_read_unlock();
+       return ret;
+
+drop_unlock:
+       rcu_read_unlock();
+drop:
+       kfree_skb(skb);
+       return NET_RX_DROP;
+}
+
+static void gre_err(struct sk_buff *skb, u32 info)
+{
+       const struct gre_protocol *proto;
+       const struct iphdr *iph = (const struct iphdr *)skb->data;
+       u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f;
+
+       if (ver >= GREPROTO_MAX)
+               return;
+
+       rcu_read_lock();
+       proto = rcu_dereference(gre_proto[ver]);
+       if (proto && proto->err_handler)
+               proto->err_handler(skb, info);
+       rcu_read_unlock();
+}
+
+static const struct net_protocol net_gre_protocol = {
+       .handler     = gre_rcv,
+       .err_handler = gre_err,
+       .netns_ok    = 1,
+};
+
+static const struct gre_protocol ipgre_protocol = {
+       .handler     = gre_cisco_rcv,
+       .err_handler = gre_cisco_err,
+};
+
+int gre_cisco_register(struct gre_cisco_protocol *newp)
+{
+       struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **)
+                                           &gre_cisco_proto_list[newp->priority];
+
+       return (cmpxchg(proto, NULL, newp) == NULL) ? 0 : -EBUSY;
+}
+EXPORT_SYMBOL_GPL(gre_cisco_register);
+
+int gre_cisco_unregister(struct gre_cisco_protocol *del_proto)
+{
+       struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **)
+                                           &gre_cisco_proto_list[del_proto->priority];
+       int ret;
+
+       ret = (cmpxchg(proto, del_proto, NULL) == del_proto) ? 0 : -EINVAL;
+
+       if (ret)
+               return ret;
+
+       synchronize_net();
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gre_cisco_unregister);
+
+static int __init gre_init(void)
+{
+       pr_info("GRE over IPv4 demultiplexor driver\n");
+
+       if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
+               pr_err("can't add protocol\n");
+               goto err;
+       }
+
+       if (gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO) < 0) {
+               pr_info("%s: can't add ipgre handler\n", __func__);
+               goto err_gre;
+       }
+
+       if (gre_offload_init()) {
+               pr_err("can't add protocol offload\n");
+               goto err_gso;
+       }
+
+       return 0;
+err_gso:
+       gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
+err_gre:
+       inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
+err:
+       return -EAGAIN;
+}
+
+static void __exit gre_exit(void)
+{
+       gre_offload_exit();
+
+       gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
+       inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
+}
+
+module_init(gre_init);
+module_exit(gre_exit);
+
+MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver");
+MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
+MODULE_LICENSE("GPL");
diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c
new file mode 100644 (file)
index 0000000..775d5b5
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *     IPV4 GSO/GRO offload support
+ *     Linux INET implementation
+ *
+ *     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.
+ *
+ *     GRE GSO support
+ */
+
+#include <linux/skbuff.h>
+#include <net/protocol.h>
+#include <net/gre.h>
+
+static int gre_gso_send_check(struct sk_buff *skb)
+{
+       if (!skb->encapsulation)
+               return -EINVAL;
+       return 0;
+}
+
+static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
+                                      netdev_features_t features)
+{
+       struct sk_buff *segs = ERR_PTR(-EINVAL);
+       netdev_features_t enc_features;
+       int ghl = GRE_HEADER_SECTION;
+       struct gre_base_hdr *greh;
+       int mac_len = skb->mac_len;
+       __be16 protocol = skb->protocol;
+       int tnl_hlen;
+       bool csum;
+
+       if (unlikely(skb_shinfo(skb)->gso_type &
+                               ~(SKB_GSO_TCPV4 |
+                                 SKB_GSO_TCPV6 |
+                                 SKB_GSO_UDP |
+                                 SKB_GSO_DODGY |
+                                 SKB_GSO_TCP_ECN |
+                                 SKB_GSO_GRE)))
+               goto out;
+
+       if (unlikely(!pskb_may_pull(skb, sizeof(*greh))))
+               goto out;
+
+       greh = (struct gre_base_hdr *)skb_transport_header(skb);
+
+       if (greh->flags & GRE_KEY)
+               ghl += GRE_HEADER_SECTION;
+       if (greh->flags & GRE_SEQ)
+               ghl += GRE_HEADER_SECTION;
+       if (greh->flags & GRE_CSUM) {
+               ghl += GRE_HEADER_SECTION;
+               csum = true;
+       } else
+               csum = false;
+
+       /* setup inner skb. */
+       skb->protocol = greh->protocol;
+       skb->encapsulation = 0;
+
+       if (unlikely(!pskb_may_pull(skb, ghl)))
+               goto out;
+
+       __skb_pull(skb, ghl);
+       skb_reset_mac_header(skb);
+       skb_set_network_header(skb, skb_inner_network_offset(skb));
+       skb->mac_len = skb_inner_network_offset(skb);
+
+       /* segment inner packet. */
+       enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
+       segs = skb_mac_gso_segment(skb, enc_features);
+       if (!segs || IS_ERR(segs))
+               goto out;
+
+       skb = segs;
+       tnl_hlen = skb_tnl_header_len(skb);
+       do {
+               __skb_push(skb, ghl);
+               if (csum) {
+                       __be32 *pcsum;
+
+                       if (skb_has_shared_frag(skb)) {
+                               int err;
+
+                               err = __skb_linearize(skb);
+                               if (err) {
+                                       kfree_skb_list(segs);
+                                       segs = ERR_PTR(err);
+                                       goto out;
+                               }
+                       }
+
+                       greh = (struct gre_base_hdr *)(skb->data);
+                       pcsum = (__be32 *)(greh + 1);
+                       *pcsum = 0;
+                       *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0));
+               }
+               __skb_push(skb, tnl_hlen - ghl);
+
+               skb_reset_mac_header(skb);
+               skb_set_network_header(skb, mac_len);
+               skb->mac_len = mac_len;
+               skb->protocol = protocol;
+       } while ((skb = skb->next));
+out:
+       return segs;
+}
+
+static const struct net_offload gre_offload = {
+       .callbacks = {
+               .gso_send_check = gre_gso_send_check,
+               .gso_segment = gre_gso_segment,
+       },
+};
+
+int __init gre_offload_init(void)
+{
+       return inet_add_offload(&gre_offload, IPPROTO_GRE);
+}
+
+void __exit gre_offload_exit(void)
+{
+       inet_del_offload(&gre_offload, IPPROTO_GRE);
+}
index 76e10b4..5f7d11a 100644 (file)
@@ -482,7 +482,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
 {
        struct iphdr *iph;
        int room;
-       struct icmp_bxm icmp_param;
+       struct icmp_bxm *icmp_param;
        struct rtable *rt = skb_rtable(skb_in);
        struct ipcm_cookie ipc;
        struct flowi4 fl4;
@@ -503,7 +503,8 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        iph = ip_hdr(skb_in);
 
        if ((u8 *)iph < skb_in->head ||
-           (skb_in->network_header + sizeof(*iph)) > skb_in->tail)
+           (skb_network_header(skb_in) + sizeof(*iph)) >
+           skb_tail_pointer(skb_in))
                goto out;
 
        /*
@@ -557,9 +558,13 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
                }
        }
 
+       icmp_param = kmalloc(sizeof(*icmp_param), GFP_ATOMIC);
+       if (!icmp_param)
+               return;
+
        sk = icmp_xmit_lock(net);
        if (sk == NULL)
-               return;
+               goto out_free;
 
        /*
         *      Construct source address and options.
@@ -585,7 +590,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
                                           IPTOS_PREC_INTERNETCONTROL) :
                                          iph->tos;
 
-       if (ip_options_echo(&icmp_param.replyopts.opt.opt, skb_in))
+       if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb_in))
                goto out_unlock;
 
 
@@ -593,19 +598,19 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
         *      Prepare data for ICMP header.
         */
 
-       icmp_param.data.icmph.type       = type;
-       icmp_param.data.icmph.code       = code;
-       icmp_param.data.icmph.un.gateway = info;
-       icmp_param.data.icmph.checksum   = 0;
-       icmp_param.skb    = skb_in;
-       icmp_param.offset = skb_network_offset(skb_in);
+       icmp_param->data.icmph.type      = type;
+       icmp_param->data.icmph.code      = code;
+       icmp_param->data.icmph.un.gateway = info;
+       icmp_param->data.icmph.checksum  = 0;
+       icmp_param->skb   = skb_in;
+       icmp_param->offset = skb_network_offset(skb_in);
        inet_sk(sk)->tos = tos;
        ipc.addr = iph->saddr;
-       ipc.opt = &icmp_param.replyopts.opt;
+       ipc.opt = &icmp_param->replyopts.opt;
        ipc.tx_flags = 0;
 
        rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos,
-                              type, code, &icmp_param);
+                              type, code, icmp_param);
        if (IS_ERR(rt))
                goto out_unlock;
 
@@ -617,19 +622,21 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        room = dst_mtu(&rt->dst);
        if (room > 576)
                room = 576;
-       room -= sizeof(struct iphdr) + icmp_param.replyopts.opt.opt.optlen;
+       room -= sizeof(struct iphdr) + icmp_param->replyopts.opt.opt.optlen;
        room -= sizeof(struct icmphdr);
 
-       icmp_param.data_len = skb_in->len - icmp_param.offset;
-       if (icmp_param.data_len > room)
-               icmp_param.data_len = room;
-       icmp_param.head_len = sizeof(struct icmphdr);
+       icmp_param->data_len = skb_in->len - icmp_param->offset;
+       if (icmp_param->data_len > room)
+               icmp_param->data_len = room;
+       icmp_param->head_len = sizeof(struct icmphdr);
 
-       icmp_push_reply(&icmp_param, &fl4, &ipc, &rt);
+       icmp_push_reply(icmp_param, &fl4, &ipc, &rt);
 ende:
        ip_rt_put(rt);
 out_unlock:
        icmp_xmit_unlock(sk);
+out_free:
+       kfree(icmp_param);
 out:;
 }
 EXPORT_SYMBOL(icmp_send);
@@ -657,7 +664,8 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
 }
 
 /*
- *     Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, and ICMP_QUENCH.
+ *     Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, ICMP_QUENCH, and
+ *     ICMP_PARAMETERPROB.
  */
 
 static void icmp_unreach(struct sk_buff *skb)
@@ -939,7 +947,8 @@ error:
 void icmp_err(struct sk_buff *skb, u32 info)
 {
        struct iphdr *iph = (struct iphdr *)skb->data;
-       struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2));
+       int offset = iph->ihl<<2;
+       struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset);
        int type = icmp_hdr(skb)->type;
        int code = icmp_hdr(skb)->code;
        struct net *net = dev_net(skb->dev);
@@ -949,7 +958,7 @@ void icmp_err(struct sk_buff *skb, u32 info)
         * triggered by ICMP_ECHOREPLY which sent from kernel.
         */
        if (icmph->type != ICMP_ECHOREPLY) {
-               ping_err(skb, info);
+               ping_err(skb, offset, info);
                return;
        }
 
index d8c2327..cd71190 100644 (file)
@@ -363,7 +363,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
 static int igmpv3_sendpack(struct sk_buff *skb)
 {
        struct igmphdr *pig = igmp_hdr(skb);
-       const int igmplen = skb->tail - skb->transport_header;
+       const int igmplen = skb_tail_pointer(skb) - skb_transport_header(skb);
 
        pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen);
 
@@ -1217,6 +1217,57 @@ static void igmp_group_added(struct ip_mc_list *im)
  *     Multicast list managers
  */
 
+static u32 ip_mc_hash(const struct ip_mc_list *im)
+{
+       return hash_32((__force u32)im->multiaddr, MC_HASH_SZ_LOG);
+}
+
+static void ip_mc_hash_add(struct in_device *in_dev,
+                          struct ip_mc_list *im)
+{
+       struct ip_mc_list __rcu **mc_hash;
+       u32 hash;
+
+       mc_hash = rtnl_dereference(in_dev->mc_hash);
+       if (mc_hash) {
+               hash = ip_mc_hash(im);
+               im->next_hash = mc_hash[hash];
+               rcu_assign_pointer(mc_hash[hash], im);
+               return;
+       }
+
+       /* do not use a hash table for small number of items */
+       if (in_dev->mc_count < 4)
+               return;
+
+       mc_hash = kzalloc(sizeof(struct ip_mc_list *) << MC_HASH_SZ_LOG,
+                         GFP_KERNEL);
+       if (!mc_hash)
+               return;
+
+       for_each_pmc_rtnl(in_dev, im) {
+               hash = ip_mc_hash(im);
+               im->next_hash = mc_hash[hash];
+               RCU_INIT_POINTER(mc_hash[hash], im);
+       }
+
+       rcu_assign_pointer(in_dev->mc_hash, mc_hash);
+}
+
+static void ip_mc_hash_remove(struct in_device *in_dev,
+                             struct ip_mc_list *im)
+{
+       struct ip_mc_list __rcu **mc_hash = rtnl_dereference(in_dev->mc_hash);
+       struct ip_mc_list *aux;
+
+       if (!mc_hash)
+               return;
+       mc_hash += ip_mc_hash(im);
+       while ((aux = rtnl_dereference(*mc_hash)) != im)
+               mc_hash = &aux->next_hash;
+       *mc_hash = im->next_hash;
+}
+
 
 /*
  *     A socket has joined a multicast group on device dev.
@@ -1258,6 +1309,8 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
        in_dev->mc_count++;
        rcu_assign_pointer(in_dev->mc_list, im);
 
+       ip_mc_hash_add(in_dev, im);
+
 #ifdef CONFIG_IP_MULTICAST
        igmpv3_del_delrec(in_dev, im->multiaddr);
 #endif
@@ -1314,6 +1367,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
             ip = &i->next_rcu) {
                if (i->multiaddr == addr) {
                        if (--i->users == 0) {
+                               ip_mc_hash_remove(in_dev, i);
                                *ip = i->next_rcu;
                                in_dev->mc_count--;
                                igmp_group_dropped(i);
@@ -1381,13 +1435,9 @@ void ip_mc_init_dev(struct in_device *in_dev)
 {
        ASSERT_RTNL();
 
-       in_dev->mc_tomb = NULL;
 #ifdef CONFIG_IP_MULTICAST
-       in_dev->mr_gq_running = 0;
        setup_timer(&in_dev->mr_gq_timer, igmp_gq_timer_expire,
                        (unsigned long)in_dev);
-       in_dev->mr_ifc_count = 0;
-       in_dev->mc_count     = 0;
        setup_timer(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire,
                        (unsigned long)in_dev);
        in_dev->mr_qrv = IGMP_Unsolicited_Report_Count;
@@ -2321,12 +2371,25 @@ void ip_mc_drop_socket(struct sock *sk)
 int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 proto)
 {
        struct ip_mc_list *im;
+       struct ip_mc_list __rcu **mc_hash;
        struct ip_sf_list *psf;
        int rv = 0;
 
-       for_each_pmc_rcu(in_dev, im) {
-               if (im->multiaddr == mc_addr)
-                       break;
+       mc_hash = rcu_dereference(in_dev->mc_hash);
+       if (mc_hash) {
+               u32 hash = hash_32((__force u32)mc_addr, MC_HASH_SZ_LOG);
+
+               for (im = rcu_dereference(mc_hash[hash]);
+                    im != NULL;
+                    im = rcu_dereference(im->next_hash)) {
+                       if (im->multiaddr == mc_addr)
+                               break;
+               }
+       } else {
+               for_each_pmc_rcu(in_dev, im) {
+                       if (im->multiaddr == mc_addr)
+                               break;
+               }
        }
        if (im && proto == IPPROTO_IGMP) {
                rv = 1;
index cec5394..c5313a9 100644 (file)
@@ -247,8 +247,6 @@ static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf,
 {
        struct inet_frag_bucket *hb;
        struct inet_frag_queue *qp;
-#ifdef CONFIG_SMP
-#endif
        unsigned int hash;
 
        read_lock(&f->lock); /* Protects against hash rebuild */
index 2a83591..1f6eab6 100644 (file)
@@ -121,103 +121,8 @@ static int ipgre_tunnel_init(struct net_device *dev);
 static int ipgre_net_id __read_mostly;
 static int gre_tap_net_id __read_mostly;
 
-static __sum16 check_checksum(struct sk_buff *skb)
-{
-       __sum16 csum = 0;
-
-       switch (skb->ip_summed) {
-       case CHECKSUM_COMPLETE:
-               csum = csum_fold(skb->csum);
-
-               if (!csum)
-                       break;
-               /* Fall through. */
-
-       case CHECKSUM_NONE:
-               skb->csum = 0;
-               csum = __skb_checksum_complete(skb);
-               skb->ip_summed = CHECKSUM_COMPLETE;
-               break;
-       }
-
-       return csum;
-}
-
-static int ip_gre_calc_hlen(__be16 o_flags)
-{
-       int addend = 4;
-
-       if (o_flags&TUNNEL_CSUM)
-               addend += 4;
-       if (o_flags&TUNNEL_KEY)
-               addend += 4;
-       if (o_flags&TUNNEL_SEQ)
-               addend += 4;
-       return addend;
-}
-
-static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
-                           bool *csum_err, int *hdr_len)
-{
-       unsigned int ip_hlen = ip_hdrlen(skb);
-       const struct gre_base_hdr *greh;
-       __be32 *options;
-
-       if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
-               return -EINVAL;
-
-       greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
-       if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
-               return -EINVAL;
-
-       tpi->flags = gre_flags_to_tnl_flags(greh->flags);
-       *hdr_len = ip_gre_calc_hlen(tpi->flags);
-
-       if (!pskb_may_pull(skb, *hdr_len))
-               return -EINVAL;
-
-       greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
-
-       tpi->proto = greh->protocol;
-
-       options = (__be32 *)(greh + 1);
-       if (greh->flags & GRE_CSUM) {
-               if (check_checksum(skb)) {
-                       *csum_err = true;
-                       return -EINVAL;
-               }
-               options++;
-       }
-
-       if (greh->flags & GRE_KEY) {
-               tpi->key = *options;
-               options++;
-       } else
-               tpi->key = 0;
-
-       if (unlikely(greh->flags & GRE_SEQ)) {
-               tpi->seq = *options;
-               options++;
-       } else
-               tpi->seq = 0;
-
-       /* WCCP version 1 and 2 protocol decoding.
-        * - Change protocol to IP
-        * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
-        */
-       if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
-               tpi->proto = htons(ETH_P_IP);
-               if ((*(u8 *)options & 0xF0) != 0x40) {
-                       *hdr_len += 4;
-                       if (!pskb_may_pull(skb, *hdr_len))
-                               return -EINVAL;
-               }
-       }
-
-       return 0;
-}
-
-static void ipgre_err(struct sk_buff *skb, u32 info)
+static int ipgre_err(struct sk_buff *skb, u32 info,
+                    const struct tnl_ptk_info *tpi)
 {
 
        /* All the routers (except for Linux) return only
@@ -239,26 +144,18 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
        const int type = icmp_hdr(skb)->type;
        const int code = icmp_hdr(skb)->code;
        struct ip_tunnel *t;
-       struct tnl_ptk_info tpi;
-       int hdr_len;
-       bool csum_err = false;
-
-       if (parse_gre_header(skb, &tpi, &csum_err, &hdr_len)) {
-               if (!csum_err)          /* ignore csum errors. */
-                       return;
-       }
 
        switch (type) {
        default:
        case ICMP_PARAMETERPROB:
-               return;
+               return PACKET_RCVD;
 
        case ICMP_DEST_UNREACH:
                switch (code) {
                case ICMP_SR_FAILED:
                case ICMP_PORT_UNREACH:
                        /* Impossible event. */
-                       return;
+                       return PACKET_RCVD;
                default:
                        /* All others are translated to HOST_UNREACH.
                           rfc2003 contains "deep thoughts" about NET_UNREACH,
@@ -269,138 +166,61 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
                break;
        case ICMP_TIME_EXCEEDED:
                if (code != ICMP_EXC_TTL)
-                       return;
+                       return PACKET_RCVD;
                break;
 
        case ICMP_REDIRECT:
                break;
        }
 
-       if (tpi.proto == htons(ETH_P_TEB))
+       if (tpi->proto == htons(ETH_P_TEB))
                itn = net_generic(net, gre_tap_net_id);
        else
                itn = net_generic(net, ipgre_net_id);
 
        iph = (const struct iphdr *)skb->data;
-       t = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi.flags,
-                            iph->daddr, iph->saddr, tpi.key);
+       t = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
+                            iph->daddr, iph->saddr, tpi->key);
 
        if (t == NULL)
-               return;
+               return PACKET_REJECT;
 
-       if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
-               ipv4_update_pmtu(skb, dev_net(skb->dev), info,
-                                t->parms.link, 0, IPPROTO_GRE, 0);
-               return;
-       }
-       if (type == ICMP_REDIRECT) {
-               ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
-                             IPPROTO_GRE, 0);
-               return;
-       }
        if (t->parms.iph.daddr == 0 ||
            ipv4_is_multicast(t->parms.iph.daddr))
-               return;
+               return PACKET_RCVD;
 
        if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
-               return;
+               return PACKET_RCVD;
 
        if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
                t->err_count++;
        else
                t->err_count = 1;
        t->err_time = jiffies;
+       return PACKET_RCVD;
 }
 
-static int ipgre_rcv(struct sk_buff *skb)
+static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
 {
        struct net *net = dev_net(skb->dev);
        struct ip_tunnel_net *itn;
        const struct iphdr *iph;
        struct ip_tunnel *tunnel;
-       struct tnl_ptk_info tpi;
-       int hdr_len;
-       bool csum_err = false;
-
-       if (parse_gre_header(skb, &tpi, &csum_err, &hdr_len) < 0)
-               goto drop;
 
-       if (tpi.proto == htons(ETH_P_TEB))
+       if (tpi->proto == htons(ETH_P_TEB))
                itn = net_generic(net, gre_tap_net_id);
        else
                itn = net_generic(net, ipgre_net_id);
 
        iph = ip_hdr(skb);
-       tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi.flags,
-                                 iph->saddr, iph->daddr, tpi.key);
+       tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
+                                 iph->saddr, iph->daddr, tpi->key);
 
        if (tunnel) {
-               ip_tunnel_rcv(tunnel, skb, &tpi, log_ecn_error);
-               return 0;
-       }
-       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
-drop:
-       kfree_skb(skb);
-       return 0;
-}
-
-static struct sk_buff *handle_offloads(struct ip_tunnel *tunnel, struct sk_buff *skb)
-{
-       int err;
-
-       if (skb_is_gso(skb)) {
-               err = skb_unclone(skb, GFP_ATOMIC);
-               if (unlikely(err))
-                       goto error;
-               skb_shinfo(skb)->gso_type |= SKB_GSO_GRE;
-               return skb;
-       } else if (skb->ip_summed == CHECKSUM_PARTIAL &&
-                  tunnel->parms.o_flags&TUNNEL_CSUM) {
-               err = skb_checksum_help(skb);
-               if (unlikely(err))
-                       goto error;
-       } else if (skb->ip_summed != CHECKSUM_PARTIAL)
-               skb->ip_summed = CHECKSUM_NONE;
-
-       return skb;
-
-error:
-       kfree_skb(skb);
-       return ERR_PTR(err);
-}
-
-static struct sk_buff *gre_build_header(struct sk_buff *skb,
-                                       const struct tnl_ptk_info *tpi,
-                                       int hdr_len)
-{
-       struct gre_base_hdr *greh;
-
-       skb_push(skb, hdr_len);
-
-       greh = (struct gre_base_hdr *)skb->data;
-       greh->flags = tnl_flags_to_gre_flags(tpi->flags);
-       greh->protocol = tpi->proto;
-
-       if (tpi->flags&(TUNNEL_KEY|TUNNEL_CSUM|TUNNEL_SEQ)) {
-               __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
-
-               if (tpi->flags&TUNNEL_SEQ) {
-                       *ptr = tpi->seq;
-                       ptr--;
-               }
-               if (tpi->flags&TUNNEL_KEY) {
-                       *ptr = tpi->key;
-                       ptr--;
-               }
-               if (tpi->flags&TUNNEL_CSUM &&
-                   !(skb_shinfo(skb)->gso_type & SKB_GSO_GRE)) {
-                       *(__sum16 *)ptr = 0;
-                       *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
-                                                                skb->len, 0));
-               }
+               ip_tunnel_rcv(tunnel, skb, tpi, log_ecn_error);
+               return PACKET_RCVD;
        }
-
-       return skb;
+       return PACKET_REJECT;
 }
 
 static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,
@@ -410,11 +230,6 @@ static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,
        struct ip_tunnel *tunnel = netdev_priv(dev);
        struct tnl_ptk_info tpi;
 
-       if (likely(!skb->encapsulation)) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
-
        tpi.flags = tunnel->parms.o_flags;
        tpi.proto = proto;
        tpi.key = tunnel->parms.o_key;
@@ -423,13 +238,9 @@ static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,
        tpi.seq = htonl(tunnel->o_seqno);
 
        /* Push GRE header. */
-       skb = gre_build_header(skb, &tpi, tunnel->hlen);
-       if (unlikely(!skb)) {
-               dev->stats.tx_dropped++;
-               return;
-       }
+       gre_build_header(skb, &tpi, tunnel->hlen);
 
-       ip_tunnel_xmit(skb, dev, tnl_params);
+       ip_tunnel_xmit(skb, dev, tnl_params, tnl_params->protocol);
 }
 
 static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
@@ -438,7 +249,7 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
        struct ip_tunnel *tunnel = netdev_priv(dev);
        const struct iphdr *tnl_params;
 
-       skb = handle_offloads(tunnel, skb);
+       skb = gre_handle_offloads(skb, !!(tunnel->parms.o_flags&TUNNEL_CSUM));
        if (IS_ERR(skb))
                goto out;
 
@@ -477,7 +288,7 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
 
-       skb = handle_offloads(tunnel, skb);
+       skb = gre_handle_offloads(skb, !!(tunnel->parms.o_flags&TUNNEL_CSUM));
        if (IS_ERR(skb))
                goto out;
 
@@ -503,10 +314,11 @@ static int ipgre_tunnel_ioctl(struct net_device *dev,
 
        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
                return -EFAULT;
-       if (p.iph.version != 4 || p.iph.protocol != IPPROTO_GRE ||
-           p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)) ||
-           ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING))) {
-               return -EINVAL;
+       if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
+               if (p.iph.version != 4 || p.iph.protocol != IPPROTO_GRE ||
+                   p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)) ||
+                   ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING)))
+                       return -EINVAL;
        }
        p.i_flags = gre_flags_to_tnl_flags(p.i_flags);
        p.o_flags = gre_flags_to_tnl_flags(p.o_flags);
@@ -708,9 +520,10 @@ static int ipgre_tunnel_init(struct net_device *dev)
        return ip_tunnel_init(dev);
 }
 
-static const struct gre_protocol ipgre_protocol = {
-       .handler     = ipgre_rcv,
-       .err_handler = ipgre_err,
+static struct gre_cisco_protocol ipgre_protocol = {
+       .handler        = ipgre_rcv,
+       .err_handler    = ipgre_err,
+       .priority       = 0,
 };
 
 static int __net_init ipgre_init_net(struct net *net)
@@ -978,7 +791,7 @@ static int __init ipgre_init(void)
        if (err < 0)
                goto pnet_tap_faied;
 
-       err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);
+       err = gre_cisco_register(&ipgre_protocol);
        if (err < 0) {
                pr_info("%s: can't add protocol\n", __func__);
                goto add_proto_failed;
@@ -997,7 +810,7 @@ static int __init ipgre_init(void)
 tap_ops_failed:
        rtnl_link_unregister(&ipgre_link_ops);
 rtnl_link_failed:
-       gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
+       gre_cisco_unregister(&ipgre_protocol);
 add_proto_failed:
        unregister_pernet_device(&ipgre_tap_net_ops);
 pnet_tap_faied:
@@ -1009,8 +822,7 @@ static void __exit ipgre_fini(void)
 {
        rtnl_link_unregister(&ipgre_tap_ops);
        rtnl_link_unregister(&ipgre_link_ops);
-       if (gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO) < 0)
-               pr_info("%s: can't remove protocol\n", __func__);
+       gre_cisco_unregister(&ipgre_protocol);
        unregister_pernet_device(&ipgre_tap_net_ops);
        unregister_pernet_device(&ipgre_net_ops);
 }
index 7fa8f08..945734b 100644 (file)
@@ -304,6 +304,7 @@ static struct net_device *__ip_tunnel_create(struct net *net,
 
        tunnel = netdev_priv(dev);
        tunnel->parms = *parms;
+       tunnel->net = net;
 
        err = register_netdevice(dev);
        if (err)
@@ -408,13 +409,6 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
        const struct iphdr *iph = ip_hdr(skb);
        int err;
 
-       secpath_reset(skb);
-
-       skb->protocol = tpi->proto;
-
-       skb->mac_header = skb->network_header;
-       __pskb_pull(skb, tunnel->hlen);
-       skb_postpull_rcsum(skb, skb_transport_header(skb), tunnel->hlen);
 #ifdef CONFIG_NET_IPGRE_BROADCAST
        if (ipv4_is_multicast(iph->daddr)) {
                /* Looped back packet, drop it! */
@@ -442,23 +436,6 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
                tunnel->i_seqno = ntohl(tpi->seq) + 1;
        }
 
-       /* Warning: All skb pointers will be invalidated! */
-       if (tunnel->dev->type == ARPHRD_ETHER) {
-               if (!pskb_may_pull(skb, ETH_HLEN)) {
-                       tunnel->dev->stats.rx_length_errors++;
-                       tunnel->dev->stats.rx_errors++;
-                       goto drop;
-               }
-
-               iph = ip_hdr(skb);
-               skb->protocol = eth_type_trans(skb, tunnel->dev);
-               skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
-       }
-
-       skb->pkt_type = PACKET_HOST;
-       __skb_tunnel_rx(skb, tunnel->dev);
-
-       skb_reset_network_header(skb);
        err = IP_ECN_decapsulate(iph, skb);
        if (unlikely(err)) {
                if (log_ecn_error)
@@ -477,6 +454,15 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
        tstats->rx_bytes += skb->len;
        u64_stats_update_end(&tstats->syncp);
 
+       if (tunnel->net != dev_net(tunnel->dev))
+               skb_scrub_packet(skb);
+
+       if (tunnel->dev->type == ARPHRD_ETHER) {
+               skb->protocol = eth_type_trans(skb, tunnel->dev);
+               skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
+       } else {
+               skb->dev = tunnel->dev;
+       }
        gro_cells_receive(&tunnel->gro_cells, skb);
        return 0;
 
@@ -486,24 +472,69 @@ drop:
 }
 EXPORT_SYMBOL_GPL(ip_tunnel_rcv);
 
+static int tnl_update_pmtu(struct net_device *dev, struct sk_buff *skb,
+                           struct rtable *rt, __be16 df)
+{
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       int pkt_size = skb->len - tunnel->hlen;
+       int mtu;
+
+       if (df)
+               mtu = dst_mtu(&rt->dst) - dev->hard_header_len
+                                       - sizeof(struct iphdr) - tunnel->hlen;
+       else
+               mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
+
+       if (skb_dst(skb))
+               skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
+
+       if (skb->protocol == htons(ETH_P_IP)) {
+               if (!skb_is_gso(skb) &&
+                   (df & htons(IP_DF)) && mtu < pkt_size) {
+                       memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
+                       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+                       return -E2BIG;
+               }
+       }
+#if IS_ENABLED(CONFIG_IPV6)
+       else if (skb->protocol == htons(ETH_P_IPV6)) {
+               struct rt6_info *rt6 = (struct rt6_info *)skb_dst(skb);
+
+               if (rt6 && mtu < dst_mtu(skb_dst(skb)) &&
+                          mtu >= IPV6_MIN_MTU) {
+                       if ((tunnel->parms.iph.daddr &&
+                           !ipv4_is_multicast(tunnel->parms.iph.daddr)) ||
+                           rt6->rt6i_dst.plen == 128) {
+                               rt6->rt6i_flags |= RTF_MODIFIED;
+                               dst_metric_set(skb_dst(skb), RTAX_MTU, mtu);
+                       }
+               }
+
+               if (!skb_is_gso(skb) && mtu >= IPV6_MIN_MTU &&
+                                       mtu < pkt_size) {
+                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+                       return -E2BIG;
+               }
+       }
+#endif
+       return 0;
+}
+
 void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
-                   const struct iphdr *tnl_params)
+                   const struct iphdr *tnl_params, const u8 protocol)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
        const struct iphdr *inner_iph;
-       struct iphdr *iph;
        struct flowi4 fl4;
        u8     tos, ttl;
        __be16 df;
        struct rtable *rt;              /* Route to the other host */
-       struct net_device *tdev;        /* Device to other host */
        unsigned int max_headroom;      /* The extra header space needed */
        __be32 dst;
-       int mtu;
+       int err;
 
        inner_iph = (const struct iphdr *)skb_inner_network_header(skb);
 
-       memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
        dst = tnl_params->daddr;
        if (dst == 0) {
                /* NBMA tunnel */
@@ -561,8 +592,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
                        tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph);
        }
 
-       rt = ip_route_output_tunnel(dev_net(dev), &fl4,
-                                   tunnel->parms.iph.protocol,
+       rt = ip_route_output_tunnel(tunnel->net, &fl4,
+                                   protocol,
                                    dst, tnl_params->saddr,
                                    tunnel->parms.o_key,
                                    RT_TOS(tos),
@@ -571,58 +602,19 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
                dev->stats.tx_carrier_errors++;
                goto tx_error;
        }
-       tdev = rt->dst.dev;
-
-       if (tdev == dev) {
+       if (rt->dst.dev == dev) {
                ip_rt_put(rt);
                dev->stats.collisions++;
                goto tx_error;
        }
 
-       df = tnl_params->frag_off;
-
-       if (df)
-               mtu = dst_mtu(&rt->dst) - dev->hard_header_len
-                                       - sizeof(struct iphdr);
-       else
-               mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
-
-       if (skb_dst(skb))
-               skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
-
-       if (skb->protocol == htons(ETH_P_IP)) {
-               df |= (inner_iph->frag_off&htons(IP_DF));
-
-               if (!skb_is_gso(skb) &&
-                   (inner_iph->frag_off&htons(IP_DF)) &&
-                    mtu < ntohs(inner_iph->tot_len)) {
-                       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
-                       ip_rt_put(rt);
-                       goto tx_error;
-               }
+       if (tnl_update_pmtu(dev, skb, rt, tnl_params->frag_off)) {
+               ip_rt_put(rt);
+               goto tx_error;
        }
-#if IS_ENABLED(CONFIG_IPV6)
-       else if (skb->protocol == htons(ETH_P_IPV6)) {
-               struct rt6_info *rt6 = (struct rt6_info *)skb_dst(skb);
-
-               if (rt6 && mtu < dst_mtu(skb_dst(skb)) &&
-                   mtu >= IPV6_MIN_MTU) {
-                       if ((tunnel->parms.iph.daddr &&
-                           !ipv4_is_multicast(tunnel->parms.iph.daddr)) ||
-                           rt6->rt6i_dst.plen == 128) {
-                               rt6->rt6i_flags |= RTF_MODIFIED;
-                               dst_metric_set(skb_dst(skb), RTAX_MTU, mtu);
-                       }
-               }
 
-               if (!skb_is_gso(skb) && mtu >= IPV6_MIN_MTU &&
-                   mtu < skb->len) {
-                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
-                       ip_rt_put(rt);
-                       goto tx_error;
-               }
-       }
-#endif
+       if (tunnel->net != dev_net(dev))
+               skb_scrub_packet(skb);
 
        if (tunnel->err_count > 0) {
                if (time_before(jiffies,
@@ -646,8 +638,12 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
                        ttl = ip4_dst_hoplimit(&rt->dst);
        }
 
-       max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct iphdr)
-                                              + rt->dst.header_len;
+       df = tnl_params->frag_off;
+       if (skb->protocol == htons(ETH_P_IP))
+               df |= (inner_iph->frag_off&htons(IP_DF));
+
+       max_headroom = LL_RESERVED_SPACE(rt->dst.dev) + sizeof(struct iphdr)
+                       + rt->dst.header_len;
        if (max_headroom > dev->needed_headroom) {
                dev->needed_headroom = max_headroom;
                if (skb_cow_head(skb, dev->needed_headroom)) {
@@ -657,27 +653,11 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
                }
        }
 
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
-
-       /* Push down and install the IP header. */
-       skb_push(skb, sizeof(struct iphdr));
-       skb_reset_network_header(skb);
-
-       iph = ip_hdr(skb);
-       inner_iph = (const struct iphdr *)skb_inner_network_header(skb);
+       err = iptunnel_xmit(dev_net(dev), rt, skb,
+                           fl4.saddr, fl4.daddr, protocol,
+                           ip_tunnel_ecn_encap(tos, inner_iph, skb), ttl, df);
+       iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
 
-       iph->version    =       4;
-       iph->ihl        =       sizeof(struct iphdr) >> 2;
-       iph->frag_off   =       df;
-       iph->protocol   =       tnl_params->protocol;
-       iph->tos        =       ip_tunnel_ecn_encap(tos, inner_iph, skb);
-       iph->daddr      =       fl4.daddr;
-       iph->saddr      =       fl4.saddr;
-       iph->ttl        =       ttl;
-       tunnel_ip_select_ident(skb, inner_iph, &rt->dst);
-
-       iptunnel_xmit(skb, dev);
        return;
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -926,6 +906,7 @@ int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[],
        if (ip_tunnel_find(itn, p, dev->type))
                return -EEXIST;
 
+       nt->net = net;
        nt->parms = *p;
        err = register_netdevice(dev);
        if (err)
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
new file mode 100644 (file)
index 0000000..7167b08
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2013 Nicira, Inc.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/in.h>
+#include <linux/if_arp.h>
+#include <linux/mroute.h>
+#include <linux/init.h>
+#include <linux/in6.h>
+#include <linux/inetdevice.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/protocol.h>
+#include <net/ip_tunnels.h>
+#include <net/arp.h>
+#include <net/checksum.h>
+#include <net/dsfield.h>
+#include <net/inet_ecn.h>
+#include <net/xfrm.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/rtnetlink.h>
+
+int iptunnel_xmit(struct net *net, struct rtable *rt,
+                 struct sk_buff *skb,
+                 __be32 src, __be32 dst, __u8 proto,
+                 __u8 tos, __u8 ttl, __be16 df)
+{
+       int pkt_len = skb->len;
+       struct iphdr *iph;
+       int err;
+
+       nf_reset(skb);
+       secpath_reset(skb);
+       skb->rxhash = 0;
+       skb_dst_drop(skb);
+       skb_dst_set(skb, &rt->dst);
+       memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
+
+       /* Push down and install the IP header. */
+       __skb_push(skb, sizeof(struct iphdr));
+       skb_reset_network_header(skb);
+
+       iph = ip_hdr(skb);
+
+       iph->version    =       4;
+       iph->ihl        =       sizeof(struct iphdr) >> 2;
+       iph->frag_off   =       df;
+       iph->protocol   =       proto;
+       iph->tos        =       tos;
+       iph->daddr      =       dst;
+       iph->saddr      =       src;
+       iph->ttl        =       ttl;
+       tunnel_ip_select_ident(skb,
+                              (const struct iphdr *)skb_inner_network_header(skb),
+                              &rt->dst);
+
+       err = ip_local_out(skb);
+       if (unlikely(net_xmit_eval(err)))
+               pkt_len = 0;
+       return pkt_len;
+}
+EXPORT_SYMBOL_GPL(iptunnel_xmit);
+
+int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto)
+{
+       if (unlikely(!pskb_may_pull(skb, hdr_len)))
+               return -ENOMEM;
+
+       skb_pull_rcsum(skb, hdr_len);
+
+       if (inner_proto == htons(ETH_P_TEB)) {
+               struct ethhdr *eh = (struct ethhdr *)skb->data;
+
+               if (unlikely(!pskb_may_pull(skb, ETH_HLEN)))
+                       return -ENOMEM;
+
+               if (likely(ntohs(eh->h_proto) >= ETH_P_802_3_MIN))
+                       skb->protocol = eh->h_proto;
+               else
+                       skb->protocol = htons(ETH_P_802_2);
+
+       } else {
+               skb->protocol = inner_proto;
+       }
+
+       nf_reset(skb);
+       secpath_reset(skb);
+       if (!skb->l4_rxhash)
+               skb->rxhash = 0;
+       skb_dst_drop(skb);
+       skb->vlan_tci = 0;
+       skb_set_queue_mapping(skb, 0);
+       skb->pkt_type = PACKET_HOST;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(iptunnel_pull_header);
index c118f6b..17cc0ff 100644 (file)
@@ -606,17 +606,10 @@ static int __net_init vti_fb_tunnel_init(struct net_device *dev)
        struct iphdr *iph = &tunnel->parms.iph;
        struct vti_net *ipn = net_generic(dev_net(dev), vti_net_id);
 
-       tunnel->dev = dev;
-       strcpy(tunnel->parms.name, dev->name);
-
        iph->version            = 4;
        iph->protocol           = IPPROTO_IPIP;
        iph->ihl                = 5;
 
-       dev->tstats = alloc_percpu(struct pcpu_tstats);
-       if (!dev->tstats)
-               return -ENOMEM;
-
        dev_hold(dev);
        rcu_assign_pointer(ipn->tunnels_wc[0], tunnel);
        return 0;
index 59cb8c7..826be4c 100644 (file)
@@ -47,12 +47,9 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info)
        if (!x)
                return;
 
-       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) {
-               atomic_inc(&flow_cache_genid);
-               rt_genid_bump(net);
-
+       if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
                ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_COMP, 0);
-       else
+       else
                ipv4_redirect(skb, net, 0, 0, IPPROTO_COMP, 0);
        xfrm_state_put(x);
 }
index 77bfcce..51fc2a1 100644 (file)
@@ -188,8 +188,12 @@ static int ipip_rcv(struct sk_buff *skb)
        struct net *net = dev_net(skb->dev);
        struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
        struct ip_tunnel *tunnel;
-       const struct iphdr *iph = ip_hdr(skb);
+       const struct iphdr *iph;
 
+       if (iptunnel_pull_header(skb, 0, tpi.proto))
+               goto drop;
+
+       iph = ip_hdr(skb);
        tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
                        iph->saddr, iph->daddr, 0);
        if (tunnel) {
@@ -222,7 +226,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                skb->encapsulation = 1;
        }
 
-       ip_tunnel_xmit(skb, dev, tiph);
+       ip_tunnel_xmit(skb, dev, tiph, tiph->protocol);
        return NETDEV_TX_OK;
 
 tx_error:
@@ -240,11 +244,13 @@ ipip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
                return -EFAULT;
 
-       if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP ||
-                       p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
-               return -EINVAL;
-       if (p.i_key || p.o_key || p.i_flags || p.o_flags)
-               return -EINVAL;
+       if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
+               if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP ||
+                   p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
+                       return -EINVAL;
+       }
+
+       p.i_key = p.o_key = p.i_flags = p.o_flags = 0;
        if (p.iph.ttl)
                p.iph.frag_off |= htons(IP_DF);
 
index 9d9610a..132a096 100644 (file)
@@ -980,7 +980,7 @@ static int ipmr_cache_report(struct mr_table *mrt,
 
        /* Copy the IP header */
 
-       skb->network_header = skb->tail;
+       skb_set_network_header(skb, skb->len);
        skb_put(skb, ihl);
        skb_copy_to_linear_data(skb, pkt->data, ihl);
        ip_hdr(skb)->protocol = 0;      /* Flag to the kernel this is a route add */
@@ -1609,7 +1609,7 @@ int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
 
 static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net *net = dev_net(dev);
        struct mr_table *mrt;
        struct vif_device *v;
index e7916c1..4e90280 100644 (file)
@@ -111,7 +111,7 @@ config IP_NF_TARGET_REJECT
          To compile it as a module, choose M here.  If unsure, say N.
 
 config IP_NF_TARGET_ULOG
-       tristate "ULOG target support"
+       tristate "ULOG target support (obsolete)"
        default m if NETFILTER_ADVANCED=n
        ---help---
 
index 5d5d4d1..30e4de9 100644 (file)
@@ -108,7 +108,7 @@ static int masq_device_event(struct notifier_block *this,
                             unsigned long event,
                             void *ptr)
 {
-       const struct net_device *dev = ptr;
+       const struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net *net = dev_net(dev);
 
        if (event == NETDEV_DOWN) {
@@ -129,7 +129,10 @@ static int masq_inet_event(struct notifier_block *this,
                           void *ptr)
 {
        struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev;
-       return masq_device_event(this, event, dev);
+       struct netdev_notifier_info info;
+
+       netdev_notifier_info_init(&info, dev);
+       return masq_device_event(this, event, &info);
 }
 
 static struct notifier_block masq_dev_notifier = {
index 32b0e97..cbc2215 100644 (file)
@@ -331,6 +331,12 @@ static int ulog_tg_check(const struct xt_tgchk_param *par)
 {
        const struct ipt_ulog_info *loginfo = par->targinfo;
 
+       if (!par->net->xt.ulog_warn_deprecated) {
+               pr_info("ULOG is deprecated and it will be removed soon, "
+                       "use NFLOG instead\n");
+               par->net->xt.ulog_warn_deprecated = true;
+       }
+
        if (loginfo->prefix[sizeof(loginfo->prefix) - 1] != '\0') {
                pr_debug("prefix not null-terminated\n");
                return -EINVAL;
index 567d841..0a2e0e3 100644 (file)
@@ -223,7 +223,7 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
 static int log_invalid_proto_min = 0;
 static int log_invalid_proto_max = 255;
 
-static ctl_table ip_ct_sysctl_table[] = {
+static struct ctl_table ip_ct_sysctl_table[] = {
        {
                .procname       = "ip_conntrack_max",
                .maxlen         = sizeof(int),
index 7d93d62..746427c 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/netdevice.h>
 #include <net/snmp.h>
 #include <net/ip.h>
-#include <net/ipv6.h>
 #include <net/icmp.h>
 #include <net/protocol.h>
 #include <linux/skbuff.h>
 #include <net/inet_common.h>
 #include <net/checksum.h>
 
+#if IS_ENABLED(CONFIG_IPV6)
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+#include <net/addrconf.h>
+#include <net/ipv6.h>
+#include <net/transp_v6.h>
+#endif
 
-static struct ping_table ping_table;
+
+struct ping_table ping_table;
+struct pingv6_ops pingv6_ops;
+EXPORT_SYMBOL_GPL(pingv6_ops);
 
 static u16 ping_port_rover;
 
@@ -58,6 +67,7 @@ static inline int ping_hashfn(struct net *net, unsigned int num, unsigned int ma
        pr_debug("hash(%d) = %d\n", num, res);
        return res;
 }
+EXPORT_SYMBOL_GPL(ping_hash);
 
 static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table,
                                             struct net *net, unsigned int num)
@@ -65,7 +75,7 @@ static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table,
        return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)];
 }
 
-static int ping_v4_get_port(struct sock *sk, unsigned short ident)
+int ping_get_port(struct sock *sk, unsigned short ident)
 {
        struct hlist_nulls_node *node;
        struct hlist_nulls_head *hlist;
@@ -103,6 +113,10 @@ next_port:
                ping_portaddr_for_each_entry(sk2, node, hlist) {
                        isk2 = inet_sk(sk2);
 
+                       /* BUG? Why is this reuse and not reuseaddr? ping.c
+                        * doesn't turn off SO_REUSEADDR, and it doesn't expect
+                        * that other ping processes can steal its packets.
+                        */
                        if ((isk2->inet_num == ident) &&
                            (sk2 != sk) &&
                            (!sk2->sk_reuse || !sk->sk_reuse))
@@ -125,17 +139,18 @@ fail:
        write_unlock_bh(&ping_table.lock);
        return 1;
 }
+EXPORT_SYMBOL_GPL(ping_get_port);
 
-static void ping_v4_hash(struct sock *sk)
+void ping_hash(struct sock *sk)
 {
-       pr_debug("ping_v4_hash(sk->port=%u)\n", inet_sk(sk)->inet_num);
+       pr_debug("ping_hash(sk->port=%u)\n", inet_sk(sk)->inet_num);
        BUG(); /* "Please do not press this button again." */
 }
 
-static void ping_v4_unhash(struct sock *sk)
+void ping_unhash(struct sock *sk)
 {
        struct inet_sock *isk = inet_sk(sk);
-       pr_debug("ping_v4_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
+       pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
        if (sk_hashed(sk)) {
                write_lock_bh(&ping_table.lock);
                hlist_nulls_del(&sk->sk_nulls_node);
@@ -146,31 +161,61 @@ static void ping_v4_unhash(struct sock *sk)
                write_unlock_bh(&ping_table.lock);
        }
 }
+EXPORT_SYMBOL_GPL(ping_unhash);
 
-static struct sock *ping_v4_lookup(struct net *net, __be32 saddr, __be32 daddr,
-                                  u16 ident, int dif)
+static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident)
 {
        struct hlist_nulls_head *hslot = ping_hashslot(&ping_table, net, ident);
        struct sock *sk = NULL;
        struct inet_sock *isk;
        struct hlist_nulls_node *hnode;
+       int dif = skb->dev->ifindex;
+
+       if (skb->protocol == htons(ETH_P_IP)) {
+               pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n",
+                        (int)ident, &ip_hdr(skb)->daddr, dif);
+#if IS_ENABLED(CONFIG_IPV6)
+       } else if (skb->protocol == htons(ETH_P_IPV6)) {
+               pr_debug("try to find: num = %d, daddr = %pI6c, dif = %d\n",
+                        (int)ident, &ipv6_hdr(skb)->daddr, dif);
+#endif
+       }
 
-       pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n",
-                (int)ident, &daddr, dif);
        read_lock_bh(&ping_table.lock);
 
        ping_portaddr_for_each_entry(sk, hnode, hslot) {
                isk = inet_sk(sk);
 
-               pr_debug("found: %p: num = %d, daddr = %pI4, dif = %d\n", sk,
-                        (int)isk->inet_num, &isk->inet_rcv_saddr,
-                        sk->sk_bound_dev_if);
-
                pr_debug("iterate\n");
                if (isk->inet_num != ident)
                        continue;
-               if (isk->inet_rcv_saddr && isk->inet_rcv_saddr != daddr)
-                       continue;
+
+               if (skb->protocol == htons(ETH_P_IP) &&
+                   sk->sk_family == AF_INET) {
+                       pr_debug("found: %p: num=%d, daddr=%pI4, dif=%d\n", sk,
+                                (int) isk->inet_num, &isk->inet_rcv_saddr,
+                                sk->sk_bound_dev_if);
+
+                       if (isk->inet_rcv_saddr &&
+                           isk->inet_rcv_saddr != ip_hdr(skb)->daddr)
+                               continue;
+#if IS_ENABLED(CONFIG_IPV6)
+               } else if (skb->protocol == htons(ETH_P_IPV6) &&
+                          sk->sk_family == AF_INET6) {
+                       struct ipv6_pinfo *np = inet6_sk(sk);
+
+                       pr_debug("found: %p: num=%d, daddr=%pI6c, dif=%d\n", sk,
+                                (int) isk->inet_num,
+                                &inet6_sk(sk)->rcv_saddr,
+                                sk->sk_bound_dev_if);
+
+                       if (!ipv6_addr_any(&np->rcv_saddr) &&
+                           !ipv6_addr_equal(&np->rcv_saddr,
+                                            &ipv6_hdr(skb)->daddr))
+                               continue;
+#endif
+               }
+
                if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
                        continue;
 
@@ -200,7 +245,7 @@ static void inet_get_ping_group_range_net(struct net *net, kgid_t *low,
 }
 
 
-static int ping_init_sock(struct sock *sk)
+int ping_init_sock(struct sock *sk)
 {
        struct net *net = sock_net(sk);
        kgid_t group = current_egid();
@@ -225,8 +270,9 @@ static int ping_init_sock(struct sock *sk)
 
        return -EACCES;
 }
+EXPORT_SYMBOL_GPL(ping_init_sock);
 
-static void ping_close(struct sock *sk, long timeout)
+void ping_close(struct sock *sk, long timeout)
 {
        pr_debug("ping_close(sk=%p,sk->num=%u)\n",
                 inet_sk(sk), inet_sk(sk)->inet_num);
@@ -234,36 +280,122 @@ static void ping_close(struct sock *sk, long timeout)
 
        sk_common_release(sk);
 }
+EXPORT_SYMBOL_GPL(ping_close);
+
+/* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */
+static int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk,
+                               struct sockaddr *uaddr, int addr_len) {
+       struct net *net = sock_net(sk);
+       if (sk->sk_family == AF_INET) {
+               struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
+               int chk_addr_ret;
+
+               if (addr_len < sizeof(*addr))
+                       return -EINVAL;
+
+               pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n",
+                        sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port));
+
+               chk_addr_ret = inet_addr_type(net, addr->sin_addr.s_addr);
+
+               if (addr->sin_addr.s_addr == htonl(INADDR_ANY))
+                       chk_addr_ret = RTN_LOCAL;
+
+               if ((sysctl_ip_nonlocal_bind == 0 &&
+                   isk->freebind == 0 && isk->transparent == 0 &&
+                    chk_addr_ret != RTN_LOCAL) ||
+                   chk_addr_ret == RTN_MULTICAST ||
+                   chk_addr_ret == RTN_BROADCAST)
+                       return -EADDRNOTAVAIL;
+
+#if IS_ENABLED(CONFIG_IPV6)
+       } else if (sk->sk_family == AF_INET6) {
+               struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
+               int addr_type, scoped, has_addr;
+               struct net_device *dev = NULL;
+
+               if (addr_len < sizeof(*addr))
+                       return -EINVAL;
+
+               pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n",
+                        sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port));
+
+               addr_type = ipv6_addr_type(&addr->sin6_addr);
+               scoped = __ipv6_addr_needs_scope_id(addr_type);
+               if ((addr_type != IPV6_ADDR_ANY &&
+                    !(addr_type & IPV6_ADDR_UNICAST)) ||
+                   (scoped && !addr->sin6_scope_id))
+                       return -EINVAL;
+
+               rcu_read_lock();
+               if (addr->sin6_scope_id) {
+                       dev = dev_get_by_index_rcu(net, addr->sin6_scope_id);
+                       if (!dev) {
+                               rcu_read_unlock();
+                               return -ENODEV;
+                       }
+               }
+               has_addr = pingv6_ops.ipv6_chk_addr(net, &addr->sin6_addr, dev,
+                                                   scoped);
+               rcu_read_unlock();
+
+               if (!(isk->freebind || isk->transparent || has_addr ||
+                     addr_type == IPV6_ADDR_ANY))
+                       return -EADDRNOTAVAIL;
+
+               if (scoped)
+                       sk->sk_bound_dev_if = addr->sin6_scope_id;
+#endif
+       } else {
+               return -EAFNOSUPPORT;
+       }
+       return 0;
+}
+
+static void ping_set_saddr(struct sock *sk, struct sockaddr *saddr)
+{
+       if (saddr->sa_family == AF_INET) {
+               struct inet_sock *isk = inet_sk(sk);
+               struct sockaddr_in *addr = (struct sockaddr_in *) saddr;
+               isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr;
+#if IS_ENABLED(CONFIG_IPV6)
+       } else if (saddr->sa_family == AF_INET6) {
+               struct sockaddr_in6 *addr = (struct sockaddr_in6 *) saddr;
+               struct ipv6_pinfo *np = inet6_sk(sk);
+               np->rcv_saddr = np->saddr = addr->sin6_addr;
+#endif
+       }
+}
 
+static void ping_clear_saddr(struct sock *sk, int dif)
+{
+       sk->sk_bound_dev_if = dif;
+       if (sk->sk_family == AF_INET) {
+               struct inet_sock *isk = inet_sk(sk);
+               isk->inet_rcv_saddr = isk->inet_saddr = 0;
+#if IS_ENABLED(CONFIG_IPV6)
+       } else if (sk->sk_family == AF_INET6) {
+               struct ipv6_pinfo *np = inet6_sk(sk);
+               memset(&np->rcv_saddr, 0, sizeof(np->rcv_saddr));
+               memset(&np->saddr, 0, sizeof(np->saddr));
+#endif
+       }
+}
 /*
  * We need our own bind because there are no privileged id's == local ports.
  * Moreover, we don't allow binding to multi- and broadcast addresses.
  */
 
-static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
-       struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;
        struct inet_sock *isk = inet_sk(sk);
        unsigned short snum;
-       int chk_addr_ret;
        int err;
+       int dif = sk->sk_bound_dev_if;
 
-       if (addr_len < sizeof(struct sockaddr_in))
-               return -EINVAL;
-
-       pr_debug("ping_v4_bind(sk=%p,sa_addr=%08x,sa_port=%d)\n",
-                sk, addr->sin_addr.s_addr, ntohs(addr->sin_port));
-
-       chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
-       if (addr->sin_addr.s_addr == htonl(INADDR_ANY))
-               chk_addr_ret = RTN_LOCAL;
-
-       if ((sysctl_ip_nonlocal_bind == 0 &&
-           isk->freebind == 0 && isk->transparent == 0 &&
-            chk_addr_ret != RTN_LOCAL) ||
-           chk_addr_ret == RTN_MULTICAST ||
-           chk_addr_ret == RTN_BROADCAST)
-               return -EADDRNOTAVAIL;
+       err = ping_check_bind_addr(sk, isk, uaddr, addr_len);
+       if (err)
+               return err;
 
        lock_sock(sk);
 
@@ -272,42 +404,50 @@ static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
                goto out;
 
        err = -EADDRINUSE;
-       isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr;
-       snum = ntohs(addr->sin_port);
-       if (ping_v4_get_port(sk, snum) != 0) {
-               isk->inet_saddr = isk->inet_rcv_saddr = 0;
+       ping_set_saddr(sk, uaddr);
+       snum = ntohs(((struct sockaddr_in *)uaddr)->sin_port);
+       if (ping_get_port(sk, snum) != 0) {
+               ping_clear_saddr(sk, dif);
                goto out;
        }
 
-       pr_debug("after bind(): num = %d, daddr = %pI4, dif = %d\n",
+       pr_debug("after bind(): num = %d, dif = %d\n",
                 (int)isk->inet_num,
-                &isk->inet_rcv_saddr,
                 (int)sk->sk_bound_dev_if);
 
        err = 0;
-       if (isk->inet_rcv_saddr)
+       if ((sk->sk_family == AF_INET && isk->inet_rcv_saddr) ||
+           (sk->sk_family == AF_INET6 &&
+            !ipv6_addr_any(&inet6_sk(sk)->rcv_saddr)))
                sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
+
        if (snum)
                sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
        isk->inet_sport = htons(isk->inet_num);
        isk->inet_daddr = 0;
        isk->inet_dport = 0;
+
+#if IS_ENABLED(CONFIG_IPV6)
+       if (sk->sk_family == AF_INET6)
+               memset(&inet6_sk(sk)->daddr, 0, sizeof(inet6_sk(sk)->daddr));
+#endif
+
        sk_dst_reset(sk);
 out:
        release_sock(sk);
        pr_debug("ping_v4_bind -> %d\n", err);
        return err;
 }
+EXPORT_SYMBOL_GPL(ping_bind);
 
 /*
  * Is this a supported type of ICMP message?
  */
 
-static inline int ping_supported(int type, int code)
+static inline int ping_supported(int family, int type, int code)
 {
-       if (type == ICMP_ECHO && code == 0)
-               return 1;
-       return 0;
+       return (family == AF_INET && type == ICMP_ECHO && code == 0) ||
+              (family == AF_INET6 && type == ICMPV6_ECHO_REQUEST && code == 0);
 }
 
 /*
@@ -315,30 +455,42 @@ static inline int ping_supported(int type, int code)
  * sort of error condition.
  */
 
-static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
-
-void ping_err(struct sk_buff *skb, u32 info)
+void ping_err(struct sk_buff *skb, int offset, u32 info)
 {
-       struct iphdr *iph = (struct iphdr *)skb->data;
-       struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2));
+       int family;
+       struct icmphdr *icmph;
        struct inet_sock *inet_sock;
-       int type = icmp_hdr(skb)->type;
-       int code = icmp_hdr(skb)->code;
+       int type;
+       int code;
        struct net *net = dev_net(skb->dev);
        struct sock *sk;
        int harderr;
        int err;
 
+       if (skb->protocol == htons(ETH_P_IP)) {
+               family = AF_INET;
+               type = icmp_hdr(skb)->type;
+               code = icmp_hdr(skb)->code;
+               icmph = (struct icmphdr *)(skb->data + offset);
+       } else if (skb->protocol == htons(ETH_P_IPV6)) {
+               family = AF_INET6;
+               type = icmp6_hdr(skb)->icmp6_type;
+               code = icmp6_hdr(skb)->icmp6_code;
+               icmph = (struct icmphdr *) (skb->data + offset);
+       } else {
+               BUG();
+       }
+
        /* We assume the packet has already been checked by icmp_unreach */
 
-       if (!ping_supported(icmph->type, icmph->code))
+       if (!ping_supported(family, icmph->type, icmph->code))
                return;
 
-       pr_debug("ping_err(type=%04x,code=%04x,id=%04x,seq=%04x)\n", type,
-                code, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence));
+       pr_debug("ping_err(proto=0x%x,type=%d,code=%d,id=%04x,seq=%04x)\n",
+                skb->protocol, type, code, ntohs(icmph->un.echo.id),
+                ntohs(icmph->un.echo.sequence));
 
-       sk = ping_v4_lookup(net, iph->daddr, iph->saddr,
-                           ntohs(icmph->un.echo.id), skb->dev->ifindex);
+       sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id));
        if (sk == NULL) {
                pr_debug("no socket, dropping\n");
                return; /* No socket for error */
@@ -349,72 +501,83 @@ void ping_err(struct sk_buff *skb, u32 info)
        harderr = 0;
        inet_sock = inet_sk(sk);
 
-       switch (type) {
-       default:
-       case ICMP_TIME_EXCEEDED:
-               err = EHOSTUNREACH;
-               break;
-       case ICMP_SOURCE_QUENCH:
-               /* This is not a real error but ping wants to see it.
-                * Report it with some fake errno. */
-               err = EREMOTEIO;
-               break;
-       case ICMP_PARAMETERPROB:
-               err = EPROTO;
-               harderr = 1;
-               break;
-       case ICMP_DEST_UNREACH:
-               if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
-                       ipv4_sk_update_pmtu(skb, sk, info);
-                       if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) {
-                               err = EMSGSIZE;
-                               harderr = 1;
-                               break;
+       if (skb->protocol == htons(ETH_P_IP)) {
+               switch (type) {
+               default:
+               case ICMP_TIME_EXCEEDED:
+                       err = EHOSTUNREACH;
+                       break;
+               case ICMP_SOURCE_QUENCH:
+                       /* This is not a real error but ping wants to see it.
+                        * Report it with some fake errno.
+                        */
+                       err = EREMOTEIO;
+                       break;
+               case ICMP_PARAMETERPROB:
+                       err = EPROTO;
+                       harderr = 1;
+                       break;
+               case ICMP_DEST_UNREACH:
+                       if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
+                               ipv4_sk_update_pmtu(skb, sk, info);
+                               if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) {
+                                       err = EMSGSIZE;
+                                       harderr = 1;
+                                       break;
+                               }
+                               goto out;
                        }
-                       goto out;
-               }
-               err = EHOSTUNREACH;
-               if (code <= NR_ICMP_UNREACH) {
-                       harderr = icmp_err_convert[code].fatal;
-                       err = icmp_err_convert[code].errno;
+                       err = EHOSTUNREACH;
+                       if (code <= NR_ICMP_UNREACH) {
+                               harderr = icmp_err_convert[code].fatal;
+                               err = icmp_err_convert[code].errno;
+                       }
+                       break;
+               case ICMP_REDIRECT:
+                       /* See ICMP_SOURCE_QUENCH */
+                       ipv4_sk_redirect(skb, sk);
+                       err = EREMOTEIO;
+                       break;
                }
-               break;
-       case ICMP_REDIRECT:
-               /* See ICMP_SOURCE_QUENCH */
-               ipv4_sk_redirect(skb, sk);
-               err = EREMOTEIO;
-               break;
+#if IS_ENABLED(CONFIG_IPV6)
+       } else if (skb->protocol == htons(ETH_P_IPV6)) {
+               harderr = pingv6_ops.icmpv6_err_convert(type, code, &err);
+#endif
        }
 
        /*
         *      RFC1122: OK.  Passes ICMP errors back to application, as per
         *      4.1.3.3.
         */
-       if (!inet_sock->recverr) {
+       if ((family == AF_INET && !inet_sock->recverr) ||
+           (family == AF_INET6 && !inet6_sk(sk)->recverr)) {
                if (!harderr || sk->sk_state != TCP_ESTABLISHED)
                        goto out;
        } else {
-               ip_icmp_error(sk, skb, err, 0 /* no remote port */,
-                        info, (u8 *)icmph);
+               if (family == AF_INET) {
+                       ip_icmp_error(sk, skb, err, 0 /* no remote port */,
+                                     info, (u8 *)icmph);
+#if IS_ENABLED(CONFIG_IPV6)
+               } else if (family == AF_INET6) {
+                       pingv6_ops.ipv6_icmp_error(sk, skb, err, 0,
+                                                  info, (u8 *)icmph);
+#endif
+               }
        }
        sk->sk_err = err;
        sk->sk_error_report(sk);
 out:
        sock_put(sk);
 }
+EXPORT_SYMBOL_GPL(ping_err);
 
 /*
- *     Copy and checksum an ICMP Echo packet from user space into a buffer.
+ *     Copy and checksum an ICMP Echo packet from user space into a buffer
+ *     starting from the payload.
  */
 
-struct pingfakehdr {
-       struct icmphdr icmph;
-       struct iovec *iov;
-       __wsum wcheck;
-};
-
-static int ping_getfrag(void *from, char *to,
-                       int offset, int fraglen, int odd, struct sk_buff *skb)
+int ping_getfrag(void *from, char *to,
+                int offset, int fraglen, int odd, struct sk_buff *skb)
 {
        struct pingfakehdr *pfh = (struct pingfakehdr *)from;
 
@@ -425,20 +588,33 @@ static int ping_getfrag(void *from, char *to,
                            pfh->iov, 0, fraglen - sizeof(struct icmphdr),
                            &pfh->wcheck))
                        return -EFAULT;
+       } else if (offset < sizeof(struct icmphdr)) {
+                       BUG();
+       } else {
+               if (csum_partial_copy_fromiovecend
+                               (to, pfh->iov, offset - sizeof(struct icmphdr),
+                                fraglen, &pfh->wcheck))
+                       return -EFAULT;
+       }
 
-               return 0;
+#if IS_ENABLED(CONFIG_IPV6)
+       /* For IPv6, checksum each skb as we go along, as expected by
+        * icmpv6_push_pending_frames. For IPv4, accumulate the checksum in
+        * wcheck, it will be finalized in ping_v4_push_pending_frames.
+        */
+       if (pfh->family == AF_INET6) {
+               skb->csum = pfh->wcheck;
+               skb->ip_summed = CHECKSUM_NONE;
+               pfh->wcheck = 0;
        }
-       if (offset < sizeof(struct icmphdr))
-               BUG();
-       if (csum_partial_copy_fromiovecend
-                       (to, pfh->iov, offset - sizeof(struct icmphdr),
-                        fraglen, &pfh->wcheck))
-               return -EFAULT;
+#endif
+
        return 0;
 }
+EXPORT_SYMBOL_GPL(ping_getfrag);
 
-static int ping_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh,
-                                   struct flowi4 *fl4)
+static int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh,
+                                      struct flowi4 *fl4)
 {
        struct sk_buff *skb = skb_peek(&sk->sk_write_queue);
 
@@ -450,24 +626,9 @@ static int ping_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh,
        return ip_push_pending_frames(sk, fl4);
 }
 
-static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
-                       size_t len)
-{
-       struct net *net = sock_net(sk);
-       struct flowi4 fl4;
-       struct inet_sock *inet = inet_sk(sk);
-       struct ipcm_cookie ipc;
-       struct icmphdr user_icmph;
-       struct pingfakehdr pfh;
-       struct rtable *rt = NULL;
-       struct ip_options_data opt_copy;
-       int free = 0;
-       __be32 saddr, daddr, faddr;
-       u8  tos;
-       int err;
-
-       pr_debug("ping_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num);
-
+int ping_common_sendmsg(int family, struct msghdr *msg, size_t len,
+                       void *user_icmph, size_t icmph_len) {
+       u8 type, code;
 
        if (len > 0xFFFF)
                return -EMSGSIZE;
@@ -482,15 +643,53 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 
        /*
         *      Fetch the ICMP header provided by the userland.
-        *      iovec is modified!
+        *      iovec is modified! The ICMP header is consumed.
         */
-
-       if (memcpy_fromiovec((u8 *)&user_icmph, msg->msg_iov,
-                            sizeof(struct icmphdr)))
+       if (memcpy_fromiovec(user_icmph, msg->msg_iov, icmph_len))
                return -EFAULT;
-       if (!ping_supported(user_icmph.type, user_icmph.code))
+
+       if (family == AF_INET) {
+               type = ((struct icmphdr *) user_icmph)->type;
+               code = ((struct icmphdr *) user_icmph)->code;
+#if IS_ENABLED(CONFIG_IPV6)
+       } else if (family == AF_INET6) {
+               type = ((struct icmp6hdr *) user_icmph)->icmp6_type;
+               code = ((struct icmp6hdr *) user_icmph)->icmp6_code;
+#endif
+       } else {
+               BUG();
+       }
+
+       if (!ping_supported(family, type, code))
                return -EINVAL;
 
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ping_common_sendmsg);
+
+int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+                   size_t len)
+{
+       struct net *net = sock_net(sk);
+       struct flowi4 fl4;
+       struct inet_sock *inet = inet_sk(sk);
+       struct ipcm_cookie ipc;
+       struct icmphdr user_icmph;
+       struct pingfakehdr pfh;
+       struct rtable *rt = NULL;
+       struct ip_options_data opt_copy;
+       int free = 0;
+       __be32 saddr, daddr, faddr;
+       u8  tos;
+       int err;
+
+       pr_debug("ping_v4_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num);
+
+       err = ping_common_sendmsg(AF_INET, msg, len, &user_icmph,
+                                 sizeof(user_icmph));
+       if (err)
+               return err;
+
        /*
         *      Get and verify the address.
         */
@@ -595,13 +794,14 @@ back_from_confirm:
        pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence;
        pfh.iov = msg->msg_iov;
        pfh.wcheck = 0;
+       pfh.family = AF_INET;
 
        err = ip_append_data(sk, &fl4, ping_getfrag, &pfh, len,
                        0, &ipc, &rt, msg->msg_flags);
        if (err)
                ip_flush_pending_frames(sk);
        else
-               err = ping_push_pending_frames(sk, &pfh, &fl4);
+               err = ping_v4_push_pending_frames(sk, &pfh, &fl4);
        release_sock(sk);
 
 out:
@@ -622,11 +822,13 @@ do_confirm:
        goto out;
 }
 
-static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
-                       size_t len, int noblock, int flags, int *addr_len)
+int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+                size_t len, int noblock, int flags, int *addr_len)
 {
        struct inet_sock *isk = inet_sk(sk);
-       struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
+       int family = sk->sk_family;
+       struct sockaddr_in *sin;
+       struct sockaddr_in6 *sin6;
        struct sk_buff *skb;
        int copied, err;
 
@@ -636,11 +838,22 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        if (flags & MSG_OOB)
                goto out;
 
-       if (addr_len)
-               *addr_len = sizeof(*sin);
+       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)
-               return ip_recv_error(sk, msg, len);
+       if (flags & MSG_ERRQUEUE) {
+               if (family == AF_INET) {
+                       return ip_recv_error(sk, msg, len);
+#if IS_ENABLED(CONFIG_IPV6)
+               } else if (family == AF_INET6) {
+                       return pingv6_ops.ipv6_recv_error(sk, msg, len);
+#endif
+               }
+       }
 
        skb = skb_recv_datagram(sk, flags, noblock, &err);
        if (!skb)
@@ -659,15 +872,40 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 
        sock_recv_timestamp(msg, sk, skb);
 
-       /* Copy the address. */
-       if (sin) {
+       /* 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));
+
+               if (isk->cmsg_flags)
+                       ip_cmsg_recv(msg, skb);
+
+#if IS_ENABLED(CONFIG_IPV6)
+       } 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);
+
+               if (inet6_sk(sk)->rxopt.all)
+                       pingv6_ops.ip6_datagram_recv_ctl(sk, msg, skb);
+#endif
+       } else {
+               BUG();
        }
-       if (isk->cmsg_flags)
-               ip_cmsg_recv(msg, skb);
+
        err = copied;
 
 done:
@@ -676,8 +914,9 @@ out:
        pr_debug("ping_recvmsg -> %d\n", err);
        return err;
 }
+EXPORT_SYMBOL_GPL(ping_recvmsg);
 
-static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
        pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n",
                 inet_sk(sk), inet_sk(sk)->inet_num, skb);
@@ -688,6 +927,7 @@ static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
        }
        return 0;
 }
+EXPORT_SYMBOL_GPL(ping_queue_rcv_skb);
 
 
 /*
@@ -698,10 +938,7 @@ void ping_rcv(struct sk_buff *skb)
 {
        struct sock *sk;
        struct net *net = dev_net(skb->dev);
-       struct iphdr *iph = ip_hdr(skb);
        struct icmphdr *icmph = icmp_hdr(skb);
-       __be32 saddr = iph->saddr;
-       __be32 daddr = iph->daddr;
 
        /* We assume the packet has already been checked by icmp_rcv */
 
@@ -711,8 +948,7 @@ void ping_rcv(struct sk_buff *skb)
        /* Push ICMP header back */
        skb_push(skb, skb->data - (u8 *)icmph);
 
-       sk = ping_v4_lookup(net, saddr, daddr, ntohs(icmph->un.echo.id),
-                           skb->dev->ifindex);
+       sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id));
        if (sk != NULL) {
                pr_debug("rcv on socket %p\n", sk);
                ping_queue_rcv_skb(sk, skb_get(skb));
@@ -723,6 +959,7 @@ void ping_rcv(struct sk_buff *skb)
 
        /* We're called from icmp_rcv(). kfree_skb() is done there. */
 }
+EXPORT_SYMBOL_GPL(ping_rcv);
 
 struct proto ping_prot = {
        .name =         "PING",
@@ -733,14 +970,14 @@ struct proto ping_prot = {
        .disconnect =   udp_disconnect,
        .setsockopt =   ip_setsockopt,
        .getsockopt =   ip_getsockopt,
-       .sendmsg =      ping_sendmsg,
+       .sendmsg =      ping_v4_sendmsg,
        .recvmsg =      ping_recvmsg,
        .bind =         ping_bind,
        .backlog_rcv =  ping_queue_rcv_skb,
        .release_cb =   ip4_datagram_release_cb,
-       .hash =         ping_v4_hash,
-       .unhash =       ping_v4_unhash,
-       .get_port =     ping_v4_get_port,
+       .hash =         ping_hash,
+       .unhash =       ping_unhash,
+       .get_port =     ping_get_port,
        .obj_size =     sizeof(struct inet_sock),
 };
 EXPORT_SYMBOL(ping_prot);
@@ -764,7 +1001,8 @@ static struct sock *ping_get_first(struct seq_file *seq, int start)
                        continue;
 
                sk_nulls_for_each(sk, node, hslot) {
-                       if (net_eq(sock_net(sk), net))
+                       if (net_eq(sock_net(sk), net) &&
+                           sk->sk_family == state->family)
                                goto found;
                }
        }
@@ -797,17 +1035,24 @@ static struct sock *ping_get_idx(struct seq_file *seq, loff_t pos)
        return pos ? NULL : sk;
 }
 
-static void *ping_seq_start(struct seq_file *seq, loff_t *pos)
+void *ping_seq_start(struct seq_file *seq, loff_t *pos, sa_family_t family)
 {
        struct ping_iter_state *state = seq->private;
        state->bucket = 0;
+       state->family = family;
 
        read_lock_bh(&ping_table.lock);
 
        return *pos ? ping_get_idx(seq, *pos-1) : SEQ_START_TOKEN;
 }
+EXPORT_SYMBOL_GPL(ping_seq_start);
+
+static void *ping_v4_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       return ping_seq_start(seq, pos, AF_INET);
+}
 
-static void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
        struct sock *sk;
 
@@ -819,13 +1064,15 @@ static void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos)
        ++*pos;
        return sk;
 }
+EXPORT_SYMBOL_GPL(ping_seq_next);
 
-static void ping_seq_stop(struct seq_file *seq, void *v)
+void ping_seq_stop(struct seq_file *seq, void *v)
 {
        read_unlock_bh(&ping_table.lock);
 }
+EXPORT_SYMBOL_GPL(ping_seq_stop);
 
-static void ping_format_sock(struct sock *sp, struct seq_file *f,
+static void ping_v4_format_sock(struct sock *sp, struct seq_file *f,
                int bucket, int *len)
 {
        struct inet_sock *inet = inet_sk(sp);
@@ -846,7 +1093,7 @@ static void ping_format_sock(struct sock *sp, struct seq_file *f,
                atomic_read(&sp->sk_drops), len);
 }
 
-static int ping_seq_show(struct seq_file *seq, void *v)
+static int ping_v4_seq_show(struct seq_file *seq, void *v)
 {
        if (v == SEQ_START_TOKEN)
                seq_printf(seq, "%-127s\n",
@@ -857,72 +1104,86 @@ static int ping_seq_show(struct seq_file *seq, void *v)
                struct ping_iter_state *state = seq->private;
                int len;
 
-               ping_format_sock(v, seq, state->bucket, &len);
+               ping_v4_format_sock(v, seq, state->bucket, &len);
                seq_printf(seq, "%*s\n", 127 - len, "");
        }
        return 0;
 }
 
-static const struct seq_operations ping_seq_ops = {
-       .show           = ping_seq_show,
-       .start          = ping_seq_start,
+static const struct seq_operations ping_v4_seq_ops = {
+       .show           = ping_v4_seq_show,
+       .start          = ping_v4_seq_start,
        .next           = ping_seq_next,
        .stop           = ping_seq_stop,
 };
 
 static int ping_seq_open(struct inode *inode, struct file *file)
 {
-       return seq_open_net(inode, file, &ping_seq_ops,
+       struct ping_seq_afinfo *afinfo = PDE_DATA(inode);
+       return seq_open_net(inode, file, &afinfo->seq_ops,
                           sizeof(struct ping_iter_state));
 }
 
-static const struct file_operations ping_seq_fops = {
+const struct file_operations ping_seq_fops = {
        .open           = ping_seq_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
        .release        = seq_release_net,
 };
+EXPORT_SYMBOL_GPL(ping_seq_fops);
+
+static struct ping_seq_afinfo ping_v4_seq_afinfo = {
+       .name           = "icmp",
+       .family         = AF_INET,
+       .seq_fops       = &ping_seq_fops,
+       .seq_ops        = {
+               .start          = ping_v4_seq_start,
+               .show           = ping_v4_seq_show,
+               .next           = ping_seq_next,
+               .stop           = ping_seq_stop,
+       },
+};
 
-static int ping_proc_register(struct net *net)
+int ping_proc_register(struct net *net, struct ping_seq_afinfo *afinfo)
 {
        struct proc_dir_entry *p;
-       int rc = 0;
-
-       p = proc_create("icmp", S_IRUGO, net->proc_net, &ping_seq_fops);
+       p = proc_create_data(afinfo->name, S_IRUGO, net->proc_net,
+                            afinfo->seq_fops, afinfo);
        if (!p)
-               rc = -ENOMEM;
-       return rc;
+               return -ENOMEM;
+       return 0;
 }
+EXPORT_SYMBOL_GPL(ping_proc_register);
 
-static void ping_proc_unregister(struct net *net)
+void ping_proc_unregister(struct net *net, struct ping_seq_afinfo *afinfo)
 {
-       remove_proc_entry("icmp", net->proc_net);
+       remove_proc_entry(afinfo->name, net->proc_net);
 }
+EXPORT_SYMBOL_GPL(ping_proc_unregister);
 
-
-static int __net_init ping_proc_init_net(struct net *net)
+static int __net_init ping_v4_proc_init_net(struct net *net)
 {
-       return ping_proc_register(net);
+       return ping_proc_register(net, &ping_v4_seq_afinfo);
 }
 
-static void __net_exit ping_proc_exit_net(struct net *net)
+static void __net_exit ping_v4_proc_exit_net(struct net *net)
 {
-       ping_proc_unregister(net);
+       ping_proc_unregister(net, &ping_v4_seq_afinfo);
 }
 
-static struct pernet_operations ping_net_ops = {
-       .init = ping_proc_init_net,
-       .exit = ping_proc_exit_net,
+static struct pernet_operations ping_v4_net_ops = {
+       .init = ping_v4_proc_init_net,
+       .exit = ping_v4_proc_exit_net,
 };
 
 int __init ping_proc_init(void)
 {
-       return register_pernet_subsys(&ping_net_ops);
+       return register_pernet_subsys(&ping_v4_net_ops);
 }
 
 void ping_proc_exit(void)
 {
-       unregister_pernet_subsys(&ping_net_ops);
+       unregister_pernet_subsys(&ping_v4_net_ops);
 }
 
 #endif
index 2a5bf86..6577a11 100644 (file)
@@ -273,6 +273,7 @@ static const struct snmp_mib snmp4_net_list[] = {
        SNMP_MIB_ITEM("TCPFastOpenListenOverflow", LINUX_MIB_TCPFASTOPENLISTENOVERFLOW),
        SNMP_MIB_ITEM("TCPFastOpenCookieReqd", LINUX_MIB_TCPFASTOPENCOOKIEREQD),
        SNMP_MIB_ITEM("TCPSpuriousRtxHostQueues", LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES),
+       SNMP_MIB_ITEM("LowLatencyRxPackets", LINUX_MIB_LOWLATENCYRXPACKETS),
        SNMP_MIB_SENTINEL
 };
 
index d35bbf0..a9a54a2 100644 (file)
@@ -565,10 +565,25 @@ static inline void rt_free(struct rtable *rt)
 
 static DEFINE_SPINLOCK(fnhe_lock);
 
+static void fnhe_flush_routes(struct fib_nh_exception *fnhe)
+{
+       struct rtable *rt;
+
+       rt = rcu_dereference(fnhe->fnhe_rth_input);
+       if (rt) {
+               RCU_INIT_POINTER(fnhe->fnhe_rth_input, NULL);
+               rt_free(rt);
+       }
+       rt = rcu_dereference(fnhe->fnhe_rth_output);
+       if (rt) {
+               RCU_INIT_POINTER(fnhe->fnhe_rth_output, NULL);
+               rt_free(rt);
+       }
+}
+
 static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash)
 {
        struct fib_nh_exception *fnhe, *oldest;
-       struct rtable *orig;
 
        oldest = rcu_dereference(hash->chain);
        for (fnhe = rcu_dereference(oldest->fnhe_next); fnhe;
@@ -576,11 +591,7 @@ static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash)
                if (time_before(fnhe->fnhe_stamp, oldest->fnhe_stamp))
                        oldest = fnhe;
        }
-       orig = rcu_dereference(oldest->fnhe_rth);
-       if (orig) {
-               RCU_INIT_POINTER(oldest->fnhe_rth, NULL);
-               rt_free(orig);
-       }
+       fnhe_flush_routes(oldest);
        return oldest;
 }
 
@@ -594,11 +605,25 @@ static inline u32 fnhe_hashfun(__be32 daddr)
        return hval & (FNHE_HASH_SIZE - 1);
 }
 
+static void fill_route_from_fnhe(struct rtable *rt, struct fib_nh_exception *fnhe)
+{
+       rt->rt_pmtu = fnhe->fnhe_pmtu;
+       rt->dst.expires = fnhe->fnhe_expires;
+
+       if (fnhe->fnhe_gw) {
+               rt->rt_flags |= RTCF_REDIRECTED;
+               rt->rt_gateway = fnhe->fnhe_gw;
+               rt->rt_uses_gateway = 1;
+       }
+}
+
 static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
                                  u32 pmtu, unsigned long expires)
 {
        struct fnhe_hash_bucket *hash;
        struct fib_nh_exception *fnhe;
+       struct rtable *rt;
+       unsigned int i;
        int depth;
        u32 hval = fnhe_hashfun(daddr);
 
@@ -627,8 +652,15 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
                        fnhe->fnhe_gw = gw;
                if (pmtu) {
                        fnhe->fnhe_pmtu = pmtu;
-                       fnhe->fnhe_expires = expires;
+                       fnhe->fnhe_expires = max(1UL, expires);
                }
+               /* Update all cached dsts too */
+               rt = rcu_dereference(fnhe->fnhe_rth_input);
+               if (rt)
+                       fill_route_from_fnhe(rt, fnhe);
+               rt = rcu_dereference(fnhe->fnhe_rth_output);
+               if (rt)
+                       fill_route_from_fnhe(rt, fnhe);
        } else {
                if (depth > FNHE_RECLAIM_DEPTH)
                        fnhe = fnhe_oldest(hash);
@@ -640,10 +672,27 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
                        fnhe->fnhe_next = hash->chain;
                        rcu_assign_pointer(hash->chain, fnhe);
                }
+               fnhe->fnhe_genid = fnhe_genid(dev_net(nh->nh_dev));
                fnhe->fnhe_daddr = daddr;
                fnhe->fnhe_gw = gw;
                fnhe->fnhe_pmtu = pmtu;
                fnhe->fnhe_expires = expires;
+
+               /* Exception created; mark the cached routes for the nexthop
+                * stale, so anyone caching it rechecks if this exception
+                * applies to them.
+                */
+               rt = rcu_dereference(nh->nh_rth_input);
+               if (rt)
+                       rt->dst.obsolete = DST_OBSOLETE_KILL;
+
+               for_each_possible_cpu(i) {
+                       struct rtable __rcu **prt;
+                       prt = per_cpu_ptr(nh->nh_pcpu_rth_output, i);
+                       rt = rcu_dereference(*prt);
+                       if (rt)
+                               rt->dst.obsolete = DST_OBSOLETE_KILL;
+               }
        }
 
        fnhe->fnhe_stamp = jiffies;
@@ -922,12 +971,9 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
        if (mtu < ip_rt_min_pmtu)
                mtu = ip_rt_min_pmtu;
 
-       if (!rt->rt_pmtu) {
-               dst->obsolete = DST_OBSOLETE_KILL;
-       } else {
-               rt->rt_pmtu = mtu;
-               dst->expires = max(1UL, jiffies + ip_rt_mtu_expires);
-       }
+       if (rt->rt_pmtu == mtu &&
+           time_before(jiffies, dst->expires - ip_rt_mtu_expires / 2))
+               return;
 
        rcu_read_lock();
        if (fib_lookup(dev_net(dst->dev), fl4, &res) == 0) {
@@ -1068,11 +1114,11 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
         * DST_OBSOLETE_FORCE_CHK which forces validation calls down
         * into this function always.
         *
-        * When a PMTU/redirect information update invalidates a
-        * route, this is indicated by setting obsolete to
-        * DST_OBSOLETE_KILL.
+        * When a PMTU/redirect information update invalidates a route,
+        * this is indicated by setting obsolete to DST_OBSOLETE_KILL or
+        * DST_OBSOLETE_DEAD by dst_free().
         */
-       if (dst->obsolete == DST_OBSOLETE_KILL || rt_is_expired(rt))
+       if (dst->obsolete != DST_OBSOLETE_FORCE_CHK || rt_is_expired(rt))
                return NULL;
        return dst;
 }
@@ -1214,34 +1260,36 @@ static bool rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe,
        spin_lock_bh(&fnhe_lock);
 
        if (daddr == fnhe->fnhe_daddr) {
-               struct rtable *orig = rcu_dereference(fnhe->fnhe_rth);
-               if (orig && rt_is_expired(orig)) {
+               struct rtable __rcu **porig;
+               struct rtable *orig;
+               int genid = fnhe_genid(dev_net(rt->dst.dev));
+
+               if (rt_is_input_route(rt))
+                       porig = &fnhe->fnhe_rth_input;
+               else
+                       porig = &fnhe->fnhe_rth_output;
+               orig = rcu_dereference(*porig);
+
+               if (fnhe->fnhe_genid != genid) {
+                       fnhe->fnhe_genid = genid;
                        fnhe->fnhe_gw = 0;
                        fnhe->fnhe_pmtu = 0;
                        fnhe->fnhe_expires = 0;
+                       fnhe_flush_routes(fnhe);
+                       orig = NULL;
                }
-               if (fnhe->fnhe_pmtu) {
-                       unsigned long expires = fnhe->fnhe_expires;
-                       unsigned long diff = expires - jiffies;
-
-                       if (time_before(jiffies, expires)) {
-                               rt->rt_pmtu = fnhe->fnhe_pmtu;
-                               dst_set_expires(&rt->dst, diff);
-                       }
-               }
-               if (fnhe->fnhe_gw) {
-                       rt->rt_flags |= RTCF_REDIRECTED;
-                       rt->rt_gateway = fnhe->fnhe_gw;
-                       rt->rt_uses_gateway = 1;
-               } else if (!rt->rt_gateway)
+               fill_route_from_fnhe(rt, fnhe);
+               if (!rt->rt_gateway)
                        rt->rt_gateway = daddr;
 
-               rcu_assign_pointer(fnhe->fnhe_rth, rt);
-               if (orig)
-                       rt_free(orig);
+               if (!(rt->dst.flags & DST_NOCACHE)) {
+                       rcu_assign_pointer(*porig, rt);
+                       if (orig)
+                               rt_free(orig);
+                       ret = true;
+               }
 
                fnhe->fnhe_stamp = jiffies;
-               ret = true;
        }
        spin_unlock_bh(&fnhe_lock);
 
@@ -1473,6 +1521,7 @@ static int __mkroute_input(struct sk_buff *skb,
                           struct in_device *in_dev,
                           __be32 daddr, __be32 saddr, u32 tos)
 {
+       struct fib_nh_exception *fnhe;
        struct rtable *rth;
        int err;
        struct in_device *out_dev;
@@ -1519,8 +1568,13 @@ static int __mkroute_input(struct sk_buff *skb,
                }
        }
 
+       fnhe = find_exception(&FIB_RES_NH(*res), daddr);
        if (do_cache) {
-               rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input);
+               if (fnhe != NULL)
+                       rth = rcu_dereference(fnhe->fnhe_rth_input);
+               else
+                       rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input);
+
                if (rt_cache_valid(rth)) {
                        skb_dst_set_noref(skb, &rth->dst);
                        goto out;
@@ -1548,7 +1602,7 @@ static int __mkroute_input(struct sk_buff *skb,
        rth->dst.input = ip_forward;
        rth->dst.output = ip_output;
 
-       rt_set_nexthop(rth, daddr, res, NULL, res->fi, res->type, itag);
+       rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag);
        skb_dst_set(skb, &rth->dst);
 out:
        err = 0;
@@ -1863,7 +1917,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
 
                fnhe = find_exception(nh, fl4->daddr);
                if (fnhe)
-                       prth = &fnhe->fnhe_rth;
+                       prth = &fnhe->fnhe_rth_output;
                else {
                        if (unlikely(fl4->flowi4_flags &
                                     FLOWI_FLAG_KNOWN_NH &&
@@ -2429,19 +2483,22 @@ static int ip_rt_gc_interval __read_mostly  = 60 * HZ;
 static int ip_rt_gc_min_interval __read_mostly = HZ / 2;
 static int ip_rt_gc_elasticity __read_mostly   = 8;
 
-static int ipv4_sysctl_rtcache_flush(ctl_table *__ctl, int write,
+static int ipv4_sysctl_rtcache_flush(struct ctl_table *__ctl, int write,
                                        void __user *buffer,
                                        size_t *lenp, loff_t *ppos)
 {
+       struct net *net = (struct net *)__ctl->extra1;
+
        if (write) {
-               rt_cache_flush((struct net *)__ctl->extra1);
+               rt_cache_flush(net);
+               fnhe_genid_bump(net);
                return 0;
        }
 
        return -EINVAL;
 }
 
-static ctl_table ipv4_route_table[] = {
+static struct ctl_table ipv4_route_table[] = {
        {
                .procname       = "gc_thresh",
                .data           = &ipv4_dst_ops.gc_thresh,
@@ -2609,6 +2666,7 @@ static __net_initdata struct pernet_operations sysctl_route_ops = {
 static __net_init int rt_genid_init(struct net *net)
 {
        atomic_set(&net->rt_genid, 0);
+       atomic_set(&net->fnhe_genid, 0);
        get_random_bytes(&net->ipv4.dev_addr_genid,
                         sizeof(net->ipv4.dev_addr_genid));
        return 0;
index fa2f63f..b2c123c 100644 (file)
@@ -49,13 +49,13 @@ static void set_local_port_range(int range[2])
 }
 
 /* Validate changes from /proc interface. */
-static int ipv4_local_port_range(ctl_table *table, int write,
+static int ipv4_local_port_range(struct ctl_table *table, int write,
                                 void __user *buffer,
                                 size_t *lenp, loff_t *ppos)
 {
        int ret;
        int range[2];
-       ctl_table tmp = {
+       struct ctl_table tmp = {
                .data = &range,
                .maxlen = sizeof(range),
                .mode = table->mode,
@@ -100,7 +100,7 @@ static void set_ping_group_range(struct ctl_table *table, kgid_t low, kgid_t hig
 }
 
 /* Validate changes from /proc interface. */
-static int ipv4_ping_group_range(ctl_table *table, int write,
+static int ipv4_ping_group_range(struct ctl_table *table, int write,
                                 void __user *buffer,
                                 size_t *lenp, loff_t *ppos)
 {
@@ -108,7 +108,7 @@ static int ipv4_ping_group_range(ctl_table *table, int write,
        int ret;
        gid_t urange[2];
        kgid_t low, high;
-       ctl_table tmp = {
+       struct ctl_table tmp = {
                .data = &urange,
                .maxlen = sizeof(urange),
                .mode = table->mode,
@@ -135,11 +135,11 @@ static int ipv4_ping_group_range(ctl_table *table, int write,
        return ret;
 }
 
-static int proc_tcp_congestion_control(ctl_table *ctl, int write,
+static int proc_tcp_congestion_control(struct ctl_table *ctl, int write,
                                       void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        char val[TCP_CA_NAME_MAX];
-       ctl_table tbl = {
+       struct ctl_table tbl = {
                .data = val,
                .maxlen = TCP_CA_NAME_MAX,
        };
@@ -153,12 +153,12 @@ static int proc_tcp_congestion_control(ctl_table *ctl, int write,
        return ret;
 }
 
-static int proc_tcp_available_congestion_control(ctl_table *ctl,
+static int proc_tcp_available_congestion_control(struct ctl_table *ctl,
                                                 int write,
                                                 void __user *buffer, size_t *lenp,
                                                 loff_t *ppos)
 {
-       ctl_table tbl = { .maxlen = TCP_CA_BUF_MAX, };
+       struct ctl_table tbl = { .maxlen = TCP_CA_BUF_MAX, };
        int ret;
 
        tbl.data = kmalloc(tbl.maxlen, GFP_USER);
@@ -170,12 +170,12 @@ static int proc_tcp_available_congestion_control(ctl_table *ctl,
        return ret;
 }
 
-static int proc_allowed_congestion_control(ctl_table *ctl,
+static int proc_allowed_congestion_control(struct ctl_table *ctl,
                                           int write,
                                           void __user *buffer, size_t *lenp,
                                           loff_t *ppos)
 {
-       ctl_table tbl = { .maxlen = TCP_CA_BUF_MAX };
+       struct ctl_table tbl = { .maxlen = TCP_CA_BUF_MAX };
        int ret;
 
        tbl.data = kmalloc(tbl.maxlen, GFP_USER);
@@ -190,7 +190,7 @@ static int proc_allowed_congestion_control(ctl_table *ctl,
        return ret;
 }
 
-static int ipv4_tcp_mem(ctl_table *ctl, int write,
+static int ipv4_tcp_mem(struct ctl_table *ctl, int write,
                           void __user *buffer, size_t *lenp,
                           loff_t *ppos)
 {
@@ -201,7 +201,7 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write,
        struct mem_cgroup *memcg;
 #endif
 
-       ctl_table tmp = {
+       struct ctl_table tmp = {
                .data = &vec,
                .maxlen = sizeof(vec),
                .mode = ctl->mode,
@@ -233,10 +233,11 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write,
        return 0;
 }
 
-static int proc_tcp_fastopen_key(ctl_table *ctl, int write, void __user *buffer,
-                                size_t *lenp, loff_t *ppos)
+static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write,
+                                void __user *buffer, size_t *lenp,
+                                loff_t *ppos)
 {
-       ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) };
+       struct ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) };
        struct tcp_fastopen_context *ctxt;
        int ret;
        u32  user_key[4]; /* 16 bytes, matching TCP_FASTOPEN_KEY_LENGTH */
index ab450c0..15cbfa9 100644 (file)
 
 #include <asm/uaccess.h>
 #include <asm/ioctls.h>
+#include <net/ll_poll.h>
 
 int sysctl_tcp_fin_timeout __read_mostly = TCP_FIN_TIMEOUT;
 
@@ -436,6 +437,8 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
        struct sock *sk = sock->sk;
        const struct tcp_sock *tp = tcp_sk(sk);
 
+       sock_rps_record_flow(sk);
+
        sock_poll_wait(file, sk_sleep(sk), wait);
        if (sk->sk_state == TCP_LISTEN)
                return inet_csk_listen_poll(sk);
@@ -1551,6 +1554,10 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        struct sk_buff *skb;
        u32 urg_hole = 0;
 
+       if (sk_can_busy_loop(sk) && skb_queue_empty(&sk->sk_receive_queue) &&
+           (sk->sk_state == TCP_ESTABLISHED))
+               sk_busy_loop(sk, nonblock);
+
        lock_sock(sk);
 
        err = -ENOTCONN;
@@ -2875,249 +2882,9 @@ int compat_tcp_getsockopt(struct sock *sk, int level, int optname,
 EXPORT_SYMBOL(compat_tcp_getsockopt);
 #endif
 
-struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
-       netdev_features_t features)
-{
-       struct sk_buff *segs = ERR_PTR(-EINVAL);
-       struct tcphdr *th;
-       unsigned int thlen;
-       unsigned int seq;
-       __be32 delta;
-       unsigned int oldlen;
-       unsigned int mss;
-       struct sk_buff *gso_skb = skb;
-       __sum16 newcheck;
-       bool ooo_okay, copy_destructor;
-
-       if (!pskb_may_pull(skb, sizeof(*th)))
-               goto out;
-
-       th = tcp_hdr(skb);
-       thlen = th->doff * 4;
-       if (thlen < sizeof(*th))
-               goto out;
-
-       if (!pskb_may_pull(skb, thlen))
-               goto out;
-
-       oldlen = (u16)~skb->len;
-       __skb_pull(skb, thlen);
-
-       mss = skb_shinfo(skb)->gso_size;
-       if (unlikely(skb->len <= mss))
-               goto out;
-
-       if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
-               /* Packet is from an untrusted source, reset gso_segs. */
-               int type = skb_shinfo(skb)->gso_type;
-
-               if (unlikely(type &
-                            ~(SKB_GSO_TCPV4 |
-                              SKB_GSO_DODGY |
-                              SKB_GSO_TCP_ECN |
-                              SKB_GSO_TCPV6 |
-                              SKB_GSO_GRE |
-                              SKB_GSO_UDP_TUNNEL |
-                              0) ||
-                            !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
-                       goto out;
-
-               skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
-
-               segs = NULL;
-               goto out;
-       }
-
-       copy_destructor = gso_skb->destructor == tcp_wfree;
-       ooo_okay = gso_skb->ooo_okay;
-       /* All segments but the first should have ooo_okay cleared */
-       skb->ooo_okay = 0;
-
-       segs = skb_segment(skb, features);
-       if (IS_ERR(segs))
-               goto out;
-
-       /* Only first segment might have ooo_okay set */
-       segs->ooo_okay = ooo_okay;
-
-       delta = htonl(oldlen + (thlen + mss));
-
-       skb = segs;
-       th = tcp_hdr(skb);
-       seq = ntohl(th->seq);
-
-       newcheck = ~csum_fold((__force __wsum)((__force u32)th->check +
-                                              (__force u32)delta));
-
-       do {
-               th->fin = th->psh = 0;
-               th->check = newcheck;
-
-               if (skb->ip_summed != CHECKSUM_PARTIAL)
-                       th->check =
-                            csum_fold(csum_partial(skb_transport_header(skb),
-                                                   thlen, skb->csum));
-
-               seq += mss;
-               if (copy_destructor) {
-                       skb->destructor = gso_skb->destructor;
-                       skb->sk = gso_skb->sk;
-                       /* {tcp|sock}_wfree() use exact truesize accounting :
-                        * sum(skb->truesize) MUST be exactly be gso_skb->truesize
-                        * So we account mss bytes of 'true size' for each segment.
-                        * The last segment will contain the remaining.
-                        */
-                       skb->truesize = mss;
-                       gso_skb->truesize -= mss;
-               }
-               skb = skb->next;
-               th = tcp_hdr(skb);
-
-               th->seq = htonl(seq);
-               th->cwr = 0;
-       } while (skb->next);
-
-       /* Following permits TCP Small Queues to work well with GSO :
-        * The callback to TCP stack will be called at the time last frag
-        * is freed at TX completion, and not right now when gso_skb
-        * is freed by GSO engine
-        */
-       if (copy_destructor) {
-               swap(gso_skb->sk, skb->sk);
-               swap(gso_skb->destructor, skb->destructor);
-               swap(gso_skb->truesize, skb->truesize);
-       }
-
-       delta = htonl(oldlen + (skb->tail - skb->transport_header) +
-                     skb->data_len);
-       th->check = ~csum_fold((__force __wsum)((__force u32)th->check +
-                               (__force u32)delta));
-       if (skb->ip_summed != CHECKSUM_PARTIAL)
-               th->check = csum_fold(csum_partial(skb_transport_header(skb),
-                                                  thlen, skb->csum));
-
-out:
-       return segs;
-}
-EXPORT_SYMBOL(tcp_tso_segment);
-
-struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb)
-{
-       struct sk_buff **pp = NULL;
-       struct sk_buff *p;
-       struct tcphdr *th;
-       struct tcphdr *th2;
-       unsigned int len;
-       unsigned int thlen;
-       __be32 flags;
-       unsigned int mss = 1;
-       unsigned int hlen;
-       unsigned int off;
-       int flush = 1;
-       int i;
-
-       off = skb_gro_offset(skb);
-       hlen = off + sizeof(*th);
-       th = skb_gro_header_fast(skb, off);
-       if (skb_gro_header_hard(skb, hlen)) {
-               th = skb_gro_header_slow(skb, hlen, off);
-               if (unlikely(!th))
-                       goto out;
-       }
-
-       thlen = th->doff * 4;
-       if (thlen < sizeof(*th))
-               goto out;
-
-       hlen = off + thlen;
-       if (skb_gro_header_hard(skb, hlen)) {
-               th = skb_gro_header_slow(skb, hlen, off);
-               if (unlikely(!th))
-                       goto out;
-       }
-
-       skb_gro_pull(skb, thlen);
-
-       len = skb_gro_len(skb);
-       flags = tcp_flag_word(th);
-
-       for (; (p = *head); head = &p->next) {
-               if (!NAPI_GRO_CB(p)->same_flow)
-                       continue;
-
-               th2 = tcp_hdr(p);
-
-               if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
-                       NAPI_GRO_CB(p)->same_flow = 0;
-                       continue;
-               }
-
-               goto found;
-       }
-
-       goto out_check_final;
-
-found:
-       flush = NAPI_GRO_CB(p)->flush;
-       flush |= (__force int)(flags & TCP_FLAG_CWR);
-       flush |= (__force int)((flags ^ tcp_flag_word(th2)) &
-                 ~(TCP_FLAG_CWR | TCP_FLAG_FIN | TCP_FLAG_PSH));
-       flush |= (__force int)(th->ack_seq ^ th2->ack_seq);
-       for (i = sizeof(*th); i < thlen; i += 4)
-               flush |= *(u32 *)((u8 *)th + i) ^
-                        *(u32 *)((u8 *)th2 + i);
-
-       mss = skb_shinfo(p)->gso_size;
-
-       flush |= (len - 1) >= mss;
-       flush |= (ntohl(th2->seq) + skb_gro_len(p)) ^ ntohl(th->seq);
-
-       if (flush || skb_gro_receive(head, skb)) {
-               mss = 1;
-               goto out_check_final;
-       }
-
-       p = *head;
-       th2 = tcp_hdr(p);
-       tcp_flag_word(th2) |= flags & (TCP_FLAG_FIN | TCP_FLAG_PSH);
-
-out_check_final:
-       flush = len < mss;
-       flush |= (__force int)(flags & (TCP_FLAG_URG | TCP_FLAG_PSH |
-                                       TCP_FLAG_RST | TCP_FLAG_SYN |
-                                       TCP_FLAG_FIN));
-
-       if (p && (!NAPI_GRO_CB(skb)->same_flow || flush))
-               pp = head;
-
-out:
-       NAPI_GRO_CB(skb)->flush |= flush;
-
-       return pp;
-}
-EXPORT_SYMBOL(tcp_gro_receive);
-
-int tcp_gro_complete(struct sk_buff *skb)
-{
-       struct tcphdr *th = tcp_hdr(skb);
-
-       skb->csum_start = skb_transport_header(skb) - skb->head;
-       skb->csum_offset = offsetof(struct tcphdr, check);
-       skb->ip_summed = CHECKSUM_PARTIAL;
-
-       skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
-
-       if (th->cwr)
-               skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;
-
-       return 0;
-}
-EXPORT_SYMBOL(tcp_gro_complete);
-
 #ifdef CONFIG_TCP_MD5SIG
-static unsigned long tcp_md5sig_users;
-static struct tcp_md5sig_pool __percpu *tcp_md5sig_pool;
-static DEFINE_SPINLOCK(tcp_md5sig_pool_lock);
+static struct tcp_md5sig_pool __percpu *tcp_md5sig_pool __read_mostly;
+static DEFINE_MUTEX(tcp_md5sig_mutex);
 
 static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool __percpu *pool)
 {
@@ -3132,30 +2899,14 @@ static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool __percpu *pool)
        free_percpu(pool);
 }
 
-void tcp_free_md5sig_pool(void)
-{
-       struct tcp_md5sig_pool __percpu *pool = NULL;
-
-       spin_lock_bh(&tcp_md5sig_pool_lock);
-       if (--tcp_md5sig_users == 0) {
-               pool = tcp_md5sig_pool;
-               tcp_md5sig_pool = NULL;
-       }
-       spin_unlock_bh(&tcp_md5sig_pool_lock);
-       if (pool)
-               __tcp_free_md5sig_pool(pool);
-}
-EXPORT_SYMBOL(tcp_free_md5sig_pool);
-
-static struct tcp_md5sig_pool __percpu *
-__tcp_alloc_md5sig_pool(struct sock *sk)
+static void __tcp_alloc_md5sig_pool(void)
 {
        int cpu;
        struct tcp_md5sig_pool __percpu *pool;
 
        pool = alloc_percpu(struct tcp_md5sig_pool);
        if (!pool)
-               return NULL;
+               return;
 
        for_each_possible_cpu(cpu) {
                struct crypto_hash *hash;
@@ -3166,53 +2917,27 @@ __tcp_alloc_md5sig_pool(struct sock *sk)
 
                per_cpu_ptr(pool, cpu)->md5_desc.tfm = hash;
        }
-       return pool;
+       /* before setting tcp_md5sig_pool, we must commit all writes
+        * to memory. See ACCESS_ONCE() in tcp_get_md5sig_pool()
+        */
+       smp_wmb();
+       tcp_md5sig_pool = pool;
+       return;
 out_free:
        __tcp_free_md5sig_pool(pool);
-       return NULL;
 }
 
-struct tcp_md5sig_pool __percpu *tcp_alloc_md5sig_pool(struct sock *sk)
+bool tcp_alloc_md5sig_pool(void)
 {
-       struct tcp_md5sig_pool __percpu *pool;
-       bool alloc = false;
-
-retry:
-       spin_lock_bh(&tcp_md5sig_pool_lock);
-       pool = tcp_md5sig_pool;
-       if (tcp_md5sig_users++ == 0) {
-               alloc = true;
-               spin_unlock_bh(&tcp_md5sig_pool_lock);
-       } else if (!pool) {
-               tcp_md5sig_users--;
-               spin_unlock_bh(&tcp_md5sig_pool_lock);
-               cpu_relax();
-               goto retry;
-       } else
-               spin_unlock_bh(&tcp_md5sig_pool_lock);
-
-       if (alloc) {
-               /* we cannot hold spinlock here because this may sleep. */
-               struct tcp_md5sig_pool __percpu *p;
-
-               p = __tcp_alloc_md5sig_pool(sk);
-               spin_lock_bh(&tcp_md5sig_pool_lock);
-               if (!p) {
-                       tcp_md5sig_users--;
-                       spin_unlock_bh(&tcp_md5sig_pool_lock);
-                       return NULL;
-               }
-               pool = tcp_md5sig_pool;
-               if (pool) {
-                       /* oops, it has already been assigned. */
-                       spin_unlock_bh(&tcp_md5sig_pool_lock);
-                       __tcp_free_md5sig_pool(p);
-               } else {
-                       tcp_md5sig_pool = pool = p;
-                       spin_unlock_bh(&tcp_md5sig_pool_lock);
-               }
+       if (unlikely(!tcp_md5sig_pool)) {
+               mutex_lock(&tcp_md5sig_mutex);
+
+               if (!tcp_md5sig_pool)
+                       __tcp_alloc_md5sig_pool();
+
+               mutex_unlock(&tcp_md5sig_mutex);
        }
-       return pool;
+       return tcp_md5sig_pool != NULL;
 }
 EXPORT_SYMBOL(tcp_alloc_md5sig_pool);
 
@@ -3229,28 +2954,15 @@ struct tcp_md5sig_pool *tcp_get_md5sig_pool(void)
        struct tcp_md5sig_pool __percpu *p;
 
        local_bh_disable();
-
-       spin_lock(&tcp_md5sig_pool_lock);
-       p = tcp_md5sig_pool;
+       p = ACCESS_ONCE(tcp_md5sig_pool);
        if (p)
-               tcp_md5sig_users++;
-       spin_unlock(&tcp_md5sig_pool_lock);
-
-       if (p)
-               return this_cpu_ptr(p);
+               return __this_cpu_ptr(p);
 
        local_bh_enable();
        return NULL;
 }
 EXPORT_SYMBOL(tcp_get_md5sig_pool);
 
-void tcp_put_md5sig_pool(void)
-{
-       local_bh_enable();
-       tcp_free_md5sig_pool();
-}
-EXPORT_SYMBOL(tcp_put_md5sig_pool);
-
 int tcp_md5_hash_header(struct tcp_md5sig_pool *hp,
                        const struct tcphdr *th)
 {
index 9c62257..28af45a 100644 (file)
@@ -347,24 +347,13 @@ static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb)
 }
 
 /* 3. Tuning rcvbuf, when connection enters established state. */
-
 static void tcp_fixup_rcvbuf(struct sock *sk)
 {
        u32 mss = tcp_sk(sk)->advmss;
-       u32 icwnd = TCP_DEFAULT_INIT_RCVWND;
        int rcvmem;
 
-       /* Limit to 10 segments if mss <= 1460,
-        * or 14600/mss segments, with a minimum of two segments.
-        */
-       if (mss > 1460)
-               icwnd = max_t(u32, (1460 * TCP_DEFAULT_INIT_RCVWND) / mss, 2);
-
-       rcvmem = SKB_TRUESIZE(mss + MAX_TCP_HEADER);
-       while (tcp_win_from_space(rcvmem) < mss)
-               rcvmem += 128;
-
-       rcvmem *= icwnd;
+       rcvmem = 2 * SKB_TRUESIZE(mss + MAX_TCP_HEADER) *
+                tcp_default_init_rwnd(mss);
 
        if (sk->sk_rcvbuf < rcvmem)
                sk->sk_rcvbuf = min(rcvmem, sysctl_tcp_rmem[2]);
@@ -1257,8 +1246,6 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
 
        if (skb == tp->retransmit_skb_hint)
                tp->retransmit_skb_hint = prev;
-       if (skb == tp->scoreboard_skb_hint)
-               tp->scoreboard_skb_hint = prev;
        if (skb == tp->lost_skb_hint) {
                tp->lost_skb_hint = prev;
                tp->lost_cnt_hint -= tcp_skb_pcount(prev);
@@ -1966,20 +1953,6 @@ static bool tcp_pause_early_retransmit(struct sock *sk, int flag)
        return true;
 }
 
-static inline int tcp_skb_timedout(const struct sock *sk,
-                                  const struct sk_buff *skb)
-{
-       return tcp_time_stamp - TCP_SKB_CB(skb)->when > inet_csk(sk)->icsk_rto;
-}
-
-static inline int tcp_head_timedout(const struct sock *sk)
-{
-       const struct tcp_sock *tp = tcp_sk(sk);
-
-       return tp->packets_out &&
-              tcp_skb_timedout(sk, tcp_write_queue_head(sk));
-}
-
 /* Linux NewReno/SACK/FACK/ECN state machine.
  * --------------------------------------
  *
@@ -2086,12 +2059,6 @@ static bool tcp_time_to_recover(struct sock *sk, int flag)
        if (tcp_dupack_heuristics(tp) > tp->reordering)
                return true;
 
-       /* Trick#3 : when we use RFC2988 timer restart, fast
-        * retransmit can be triggered by timeout of queue head.
-        */
-       if (tcp_is_fack(tp) && tcp_head_timedout(sk))
-               return true;
-
        /* Trick#4: It is still not OK... But will it be useful to delay
         * recovery more?
         */
@@ -2128,44 +2095,6 @@ static bool tcp_time_to_recover(struct sock *sk, int flag)
        return false;
 }
 
-/* New heuristics: it is possible only after we switched to restart timer
- * each time when something is ACKed. Hence, we can detect timed out packets
- * during fast retransmit without falling to slow start.
- *
- * Usefulness of this as is very questionable, since we should know which of
- * the segments is the next to timeout which is relatively expensive to find
- * in general case unless we add some data structure just for that. The
- * current approach certainly won't find the right one too often and when it
- * finally does find _something_ it usually marks large part of the window
- * right away (because a retransmission with a larger timestamp blocks the
- * loop from advancing). -ij
- */
-static void tcp_timeout_skbs(struct sock *sk)
-{
-       struct tcp_sock *tp = tcp_sk(sk);
-       struct sk_buff *skb;
-
-       if (!tcp_is_fack(tp) || !tcp_head_timedout(sk))
-               return;
-
-       skb = tp->scoreboard_skb_hint;
-       if (tp->scoreboard_skb_hint == NULL)
-               skb = tcp_write_queue_head(sk);
-
-       tcp_for_write_queue_from(skb, sk) {
-               if (skb == tcp_send_head(sk))
-                       break;
-               if (!tcp_skb_timedout(sk, skb))
-                       break;
-
-               tcp_skb_mark_lost(tp, skb);
-       }
-
-       tp->scoreboard_skb_hint = skb;
-
-       tcp_verify_left_out(tp);
-}
-
 /* Detect loss in event "A" above by marking head of queue up as lost.
  * For FACK or non-SACK(Reno) senders, the first "packets" number of segments
  * are considered lost. For RFC3517 SACK, a segment is considered lost if it
@@ -2251,8 +2180,6 @@ static void tcp_update_scoreboard(struct sock *sk, int fast_rexmit)
                else if (fast_rexmit)
                        tcp_mark_head_lost(sk, 1, 1);
        }
-
-       tcp_timeout_skbs(sk);
 }
 
 /* CWND moderation, preventing bursts due to too big ACKs
@@ -2307,10 +2234,22 @@ static void DBGUNDO(struct sock *sk, const char *msg)
 #define DBGUNDO(x...) do { } while (0)
 #endif
 
-static void tcp_undo_cwr(struct sock *sk, const bool undo_ssthresh)
+static void tcp_undo_cwnd_reduction(struct sock *sk, bool unmark_loss)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
+       if (unmark_loss) {
+               struct sk_buff *skb;
+
+               tcp_for_write_queue(skb, sk) {
+                       if (skb == tcp_send_head(sk))
+                               break;
+                       TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST;
+               }
+               tp->lost_out = 0;
+               tcp_clear_all_retrans_hints(tp);
+       }
+
        if (tp->prior_ssthresh) {
                const struct inet_connection_sock *icsk = inet_csk(sk);
 
@@ -2319,7 +2258,7 @@ static void tcp_undo_cwr(struct sock *sk, const bool undo_ssthresh)
                else
                        tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh << 1);
 
-               if (undo_ssthresh && tp->prior_ssthresh > tp->snd_ssthresh) {
+               if (tp->prior_ssthresh > tp->snd_ssthresh) {
                        tp->snd_ssthresh = tp->prior_ssthresh;
                        TCP_ECN_withdraw_cwr(tp);
                }
@@ -2327,6 +2266,7 @@ static void tcp_undo_cwr(struct sock *sk, const bool undo_ssthresh)
                tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh);
        }
        tp->snd_cwnd_stamp = tcp_time_stamp;
+       tp->undo_marker = 0;
 }
 
 static inline bool tcp_may_undo(const struct tcp_sock *tp)
@@ -2346,14 +2286,13 @@ static bool tcp_try_undo_recovery(struct sock *sk)
                 * or our original transmission succeeded.
                 */
                DBGUNDO(sk, inet_csk(sk)->icsk_ca_state == TCP_CA_Loss ? "loss" : "retrans");
-               tcp_undo_cwr(sk, true);
+               tcp_undo_cwnd_reduction(sk, false);
                if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss)
                        mib_idx = LINUX_MIB_TCPLOSSUNDO;
                else
                        mib_idx = LINUX_MIB_TCPFULLUNDO;
 
                NET_INC_STATS_BH(sock_net(sk), mib_idx);
-               tp->undo_marker = 0;
        }
        if (tp->snd_una == tp->high_seq && tcp_is_reno(tp)) {
                /* Hold old state until something *above* high_seq
@@ -2367,16 +2306,17 @@ static bool tcp_try_undo_recovery(struct sock *sk)
 }
 
 /* Try to undo cwnd reduction, because D-SACKs acked all retransmitted data */
-static void tcp_try_undo_dsack(struct sock *sk)
+static bool tcp_try_undo_dsack(struct sock *sk)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
        if (tp->undo_marker && !tp->undo_retrans) {
                DBGUNDO(sk, "D-SACK");
-               tcp_undo_cwr(sk, true);
-               tp->undo_marker = 0;
+               tcp_undo_cwnd_reduction(sk, false);
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDSACKUNDO);
+               return true;
        }
+       return false;
 }
 
 /* We can clear retrans_stamp when there are no retransmissions in the
@@ -2408,60 +2348,20 @@ static bool tcp_any_retrans_done(const struct sock *sk)
        return false;
 }
 
-/* Undo during fast recovery after partial ACK. */
-
-static int tcp_try_undo_partial(struct sock *sk, int acked)
-{
-       struct tcp_sock *tp = tcp_sk(sk);
-       /* Partial ACK arrived. Force Hoe's retransmit. */
-       int failed = tcp_is_reno(tp) || (tcp_fackets_out(tp) > tp->reordering);
-
-       if (tcp_may_undo(tp)) {
-               /* Plain luck! Hole if filled with delayed
-                * packet, rather than with a retransmit.
-                */
-               if (!tcp_any_retrans_done(sk))
-                       tp->retrans_stamp = 0;
-
-               tcp_update_reordering(sk, tcp_fackets_out(tp) + acked, 1);
-
-               DBGUNDO(sk, "Hoe");
-               tcp_undo_cwr(sk, false);
-               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPPARTIALUNDO);
-
-               /* So... Do not make Hoe's retransmit yet.
-                * If the first packet was delayed, the rest
-                * ones are most probably delayed as well.
-                */
-               failed = 0;
-       }
-       return failed;
-}
-
 /* Undo during loss recovery after partial ACK or using F-RTO. */
 static bool tcp_try_undo_loss(struct sock *sk, bool frto_undo)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
        if (frto_undo || tcp_may_undo(tp)) {
-               struct sk_buff *skb;
-               tcp_for_write_queue(skb, sk) {
-                       if (skb == tcp_send_head(sk))
-                               break;
-                       TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST;
-               }
-
-               tcp_clear_all_retrans_hints(tp);
+               tcp_undo_cwnd_reduction(sk, true);
 
                DBGUNDO(sk, "partial loss");
-               tp->lost_out = 0;
-               tcp_undo_cwr(sk, true);
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPLOSSUNDO);
                if (frto_undo)
                        NET_INC_STATS_BH(sock_net(sk),
                                         LINUX_MIB_TCPSPURIOUSRTOS);
                inet_csk(sk)->icsk_retransmits = 0;
-               tp->undo_marker = 0;
                if (frto_undo || tcp_is_sack(tp))
                        tcp_set_ca_state(sk, TCP_CA_Open);
                return true;
@@ -2494,12 +2394,14 @@ static void tcp_init_cwnd_reduction(struct sock *sk, const bool set_ssthresh)
        TCP_ECN_queue_cwr(tp);
 }
 
-static void tcp_cwnd_reduction(struct sock *sk, int newly_acked_sacked,
+static void tcp_cwnd_reduction(struct sock *sk, const int prior_unsacked,
                               int fast_rexmit)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        int sndcnt = 0;
        int delta = tp->snd_ssthresh - tcp_packets_in_flight(tp);
+       int newly_acked_sacked = prior_unsacked -
+                                (tp->packets_out - tp->sacked_out);
 
        tp->prr_delivered += newly_acked_sacked;
        if (tcp_packets_in_flight(tp) > tp->snd_ssthresh) {
@@ -2556,7 +2458,7 @@ static void tcp_try_keep_open(struct sock *sk)
        }
 }
 
-static void tcp_try_to_open(struct sock *sk, int flag, int newly_acked_sacked)
+static void tcp_try_to_open(struct sock *sk, int flag, const int prior_unsacked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
@@ -2573,7 +2475,7 @@ static void tcp_try_to_open(struct sock *sk, int flag, int newly_acked_sacked)
                if (inet_csk(sk)->icsk_ca_state != TCP_CA_Open)
                        tcp_moderate_cwnd(tp);
        } else {
-               tcp_cwnd_reduction(sk, newly_acked_sacked, 0);
+               tcp_cwnd_reduction(sk, prior_unsacked, 0);
        }
 }
 
@@ -2731,6 +2633,40 @@ static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack)
        tcp_xmit_retransmit_queue(sk);
 }
 
+/* Undo during fast recovery after partial ACK. */
+static bool tcp_try_undo_partial(struct sock *sk, const int acked,
+                                const int prior_unsacked)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       if (tp->undo_marker && tcp_packet_delayed(tp)) {
+               /* Plain luck! Hole if filled with delayed
+                * packet, rather than with a retransmit.
+                */
+               tcp_update_reordering(sk, tcp_fackets_out(tp) + acked, 1);
+
+               /* We are getting evidence that the reordering degree is higher
+                * than we realized. If there are no retransmits out then we
+                * can undo. Otherwise we clock out new packets but do not
+                * mark more packets lost or retransmit more.
+                */
+               if (tp->retrans_out) {
+                       tcp_cwnd_reduction(sk, prior_unsacked, 0);
+                       return true;
+               }
+
+               if (!tcp_any_retrans_done(sk))
+                       tp->retrans_stamp = 0;
+
+               DBGUNDO(sk, "partial recovery");
+               tcp_undo_cwnd_reduction(sk, true);
+               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPPARTIALUNDO);
+               tcp_try_keep_open(sk);
+               return true;
+       }
+       return false;
+}
+
 /* Process an event, which can update packets-in-flight not trivially.
  * Main goal of this function is to calculate new estimate for left_out,
  * taking into account both packets sitting in receiver's buffer and
@@ -2742,15 +2678,14 @@ static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack)
  * It does _not_ decide what to send, it is made in function
  * tcp_xmit_retransmit_queue().
  */
-static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked,
-                                 int prior_sacked, int prior_packets,
+static void tcp_fastretrans_alert(struct sock *sk, const int acked,
+                                 const int prior_unsacked,
                                  bool is_dupack, int flag)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
-       int do_lost = is_dupack || ((flag & FLAG_DATA_SACKED) &&
+       bool do_lost = is_dupack || ((flag & FLAG_DATA_SACKED) &&
                                    (tcp_fackets_out(tp) > tp->reordering));
-       int newly_acked_sacked = 0;
        int fast_rexmit = 0;
 
        if (WARN_ON(!tp->packets_out && tp->sacked_out))
@@ -2802,10 +2737,17 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked,
                if (!(flag & FLAG_SND_UNA_ADVANCED)) {
                        if (tcp_is_reno(tp) && is_dupack)
                                tcp_add_reno_sack(sk);
-               } else
-                       do_lost = tcp_try_undo_partial(sk, pkts_acked);
-               newly_acked_sacked = prior_packets - tp->packets_out +
-                                    tp->sacked_out - prior_sacked;
+               } else {
+                       if (tcp_try_undo_partial(sk, acked, prior_unsacked))
+                               return;
+                       /* Partial ACK arrived. Force fast retransmit. */
+                       do_lost = tcp_is_reno(tp) ||
+                                 tcp_fackets_out(tp) > tp->reordering;
+               }
+               if (tcp_try_undo_dsack(sk)) {
+                       tcp_try_keep_open(sk);
+                       return;
+               }
                break;
        case TCP_CA_Loss:
                tcp_process_loss(sk, flag, is_dupack);
@@ -2819,14 +2761,12 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked,
                        if (is_dupack)
                                tcp_add_reno_sack(sk);
                }
-               newly_acked_sacked = prior_packets - tp->packets_out +
-                                    tp->sacked_out - prior_sacked;
 
                if (icsk->icsk_ca_state <= TCP_CA_Disorder)
                        tcp_try_undo_dsack(sk);
 
                if (!tcp_time_to_recover(sk, flag)) {
-                       tcp_try_to_open(sk, flag, newly_acked_sacked);
+                       tcp_try_to_open(sk, flag, prior_unsacked);
                        return;
                }
 
@@ -2846,9 +2786,9 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked,
                fast_rexmit = 1;
        }
 
-       if (do_lost || (tcp_is_fack(tp) && tcp_head_timedout(sk)))
+       if (do_lost)
                tcp_update_scoreboard(sk, fast_rexmit);
-       tcp_cwnd_reduction(sk, newly_acked_sacked, fast_rexmit);
+       tcp_cwnd_reduction(sk, prior_unsacked, fast_rexmit);
        tcp_xmit_retransmit_queue(sk);
 }
 
@@ -3079,7 +3019,6 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
 
                tcp_unlink_write_queue(skb, sk);
                sk_wmem_free_skb(sk, skb);
-               tp->scoreboard_skb_hint = NULL;
                if (skb == tp->retransmit_skb_hint)
                        tp->retransmit_skb_hint = NULL;
                if (skb == tp->lost_skb_hint)
@@ -3333,9 +3272,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
        u32 prior_in_flight;
        u32 prior_fackets;
        int prior_packets = tp->packets_out;
-       int prior_sacked = tp->sacked_out;
-       int pkts_acked = 0;
-       int previous_packets_out = 0;
+       const int prior_unsacked = tp->packets_out - tp->sacked_out;
+       int acked = 0; /* Number of packets newly acked */
 
        /* If the ack is older than previous acks
         * then we can probably ignore it.
@@ -3410,18 +3348,17 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
                goto no_queue;
 
        /* See if we can take anything off of the retransmit queue. */
-       previous_packets_out = tp->packets_out;
+       acked = tp->packets_out;
        flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una);
-
-       pkts_acked = previous_packets_out - tp->packets_out;
+       acked -= tp->packets_out;
 
        if (tcp_ack_is_dubious(sk, flag)) {
                /* Advance CWND, if state allows this. */
                if ((flag & FLAG_DATA_ACKED) && tcp_may_raise_cwnd(sk, flag))
                        tcp_cong_avoid(sk, ack, prior_in_flight);
                is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));
-               tcp_fastretrans_alert(sk, pkts_acked, prior_sacked,
-                                     prior_packets, is_dupack, flag);
+               tcp_fastretrans_alert(sk, acked, prior_unsacked,
+                                     is_dupack, flag);
        } else {
                if (flag & FLAG_DATA_ACKED)
                        tcp_cong_avoid(sk, ack, prior_in_flight);
@@ -3443,8 +3380,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
 no_queue:
        /* If data was DSACKed, see if we can undo a cwnd reduction. */
        if (flag & FLAG_DSACKING_ACK)
-               tcp_fastretrans_alert(sk, pkts_acked, prior_sacked,
-                                     prior_packets, is_dupack, flag);
+               tcp_fastretrans_alert(sk, acked, prior_unsacked,
+                                     is_dupack, flag);
        /* If this ack opens up a zero window, clear backoff.  It was
         * being used to time the probes, and is probably far higher than
         * it needs to be for normal retransmission.
@@ -3466,8 +3403,8 @@ old_ack:
         */
        if (TCP_SKB_CB(skb)->sacked) {
                flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una);
-               tcp_fastretrans_alert(sk, pkts_acked, prior_sacked,
-                                     prior_packets, is_dupack, flag);
+               tcp_fastretrans_alert(sk, acked, prior_unsacked,
+                                     is_dupack, flag);
        }
 
        SOCK_DEBUG(sk, "Ack %u before %u:%u\n", ack, tp->snd_una, tp->snd_nxt);
@@ -3780,6 +3717,7 @@ void tcp_reset(struct sock *sk)
 static void tcp_fin(struct sock *sk)
 {
        struct tcp_sock *tp = tcp_sk(sk);
+       const struct dst_entry *dst;
 
        inet_csk_schedule_ack(sk);
 
@@ -3791,7 +3729,9 @@ static void tcp_fin(struct sock *sk)
        case TCP_ESTABLISHED:
                /* Move to CLOSE_WAIT */
                tcp_set_state(sk, TCP_CLOSE_WAIT);
-               inet_csk(sk)->icsk_ack.pingpong = 1;
+               dst = __sk_dst_get(sk);
+               if (!dst || !dst_metric(dst, RTAX_QUICKACK))
+                       inet_csk(sk)->icsk_ack.pingpong = 1;
                break;
 
        case TCP_CLOSE_WAIT:
@@ -5601,6 +5541,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct request_sock *req;
        int queued = 0;
+       bool acceptable;
 
        tp->rx_opt.saw_tstamp = 0;
 
@@ -5671,157 +5612,147 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                return 0;
 
        /* step 5: check the ACK field */
-       if (true) {
-               int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH |
-                                                 FLAG_UPDATE_TS_RECENT) > 0;
-
-               switch (sk->sk_state) {
-               case TCP_SYN_RECV:
-                       if (acceptable) {
-                               /* Once we leave TCP_SYN_RECV, we no longer
-                                * need req so release it.
-                                */
-                               if (req) {
-                                       tcp_synack_rtt_meas(sk, req);
-                                       tp->total_retrans = req->num_retrans;
-
-                                       reqsk_fastopen_remove(sk, req, false);
-                               } else {
-                                       /* Make sure socket is routed, for
-                                        * correct metrics.
-                                        */
-                                       icsk->icsk_af_ops->rebuild_header(sk);
-                                       tcp_init_congestion_control(sk);
+       acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH |
+                                     FLAG_UPDATE_TS_RECENT) > 0;
 
-                                       tcp_mtup_init(sk);
-                                       tcp_init_buffer_space(sk);
-                                       tp->copied_seq = tp->rcv_nxt;
-                               }
-                               smp_mb();
-                               tcp_set_state(sk, TCP_ESTABLISHED);
-                               sk->sk_state_change(sk);
-
-                               /* Note, that this wakeup is only for marginal
-                                * crossed SYN case. Passively open sockets
-                                * are not waked up, because sk->sk_sleep ==
-                                * NULL and sk->sk_socket == NULL.
-                                */
-                               if (sk->sk_socket)
-                                       sk_wake_async(sk,
-                                                     SOCK_WAKE_IO, POLL_OUT);
-
-                               tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
-                               tp->snd_wnd = ntohs(th->window) <<
-                                             tp->rx_opt.snd_wscale;
-                               tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);
-
-                               if (tp->rx_opt.tstamp_ok)
-                                       tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
-
-                               if (req) {
-                                       /* Re-arm the timer because data may
-                                        * have been sent out. This is similar
-                                        * to the regular data transmission case
-                                        * when new data has just been ack'ed.
-                                        *
-                                        * (TFO) - we could try to be more
-                                        * aggressive and retranmitting any data
-                                        * sooner based on when they were sent
-                                        * out.
-                                        */
-                                       tcp_rearm_rto(sk);
-                               } else
-                                       tcp_init_metrics(sk);
+       switch (sk->sk_state) {
+       case TCP_SYN_RECV:
+               if (!acceptable)
+                       return 1;
 
-                               /* Prevent spurious tcp_cwnd_restart() on
-                                * first data packet.
-                                */
-                               tp->lsndtime = tcp_time_stamp;
+               /* Once we leave TCP_SYN_RECV, we no longer need req
+                * so release it.
+                */
+               if (req) {
+                       tcp_synack_rtt_meas(sk, req);
+                       tp->total_retrans = req->num_retrans;
 
-                               tcp_initialize_rcv_mss(sk);
-                               tcp_fast_path_on(tp);
-                       } else {
-                               return 1;
-                       }
-                       break;
+                       reqsk_fastopen_remove(sk, req, false);
+               } else {
+                       /* Make sure socket is routed, for correct metrics. */
+                       icsk->icsk_af_ops->rebuild_header(sk);
+                       tcp_init_congestion_control(sk);
+
+                       tcp_mtup_init(sk);
+                       tcp_init_buffer_space(sk);
+                       tp->copied_seq = tp->rcv_nxt;
+               }
+               smp_mb();
+               tcp_set_state(sk, TCP_ESTABLISHED);
+               sk->sk_state_change(sk);
+
+               /* Note, that this wakeup is only for marginal crossed SYN case.
+                * Passively open sockets are not waked up, because
+                * sk->sk_sleep == NULL and sk->sk_socket == NULL.
+                */
+               if (sk->sk_socket)
+                       sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT);
+
+               tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
+               tp->snd_wnd = ntohs(th->window) << tp->rx_opt.snd_wscale;
+               tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);
 
-               case TCP_FIN_WAIT1:
-                       /* If we enter the TCP_FIN_WAIT1 state and we are a
-                        * Fast Open socket and this is the first acceptable
-                        * ACK we have received, this would have acknowledged
-                        * our SYNACK so stop the SYNACK timer.
+               if (tp->rx_opt.tstamp_ok)
+                       tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
+
+               if (req) {
+                       /* Re-arm the timer because data may have been sent out.
+                        * This is similar to the regular data transmission case
+                        * when new data has just been ack'ed.
+                        *
+                        * (TFO) - we could try to be more aggressive and
+                        * retransmitting any data sooner based on when they
+                        * are sent out.
                         */
-                       if (req != NULL) {
-                               /* Return RST if ack_seq is invalid.
-                                * Note that RFC793 only says to generate a
-                                * DUPACK for it but for TCP Fast Open it seems
-                                * better to treat this case like TCP_SYN_RECV
-                                * above.
-                                */
-                               if (!acceptable)
-                                       return 1;
-                               /* We no longer need the request sock. */
-                               reqsk_fastopen_remove(sk, req, false);
-                               tcp_rearm_rto(sk);
-                       }
-                       if (tp->snd_una == tp->write_seq) {
-                               struct dst_entry *dst;
-
-                               tcp_set_state(sk, TCP_FIN_WAIT2);
-                               sk->sk_shutdown |= SEND_SHUTDOWN;
-
-                               dst = __sk_dst_get(sk);
-                               if (dst)
-                                       dst_confirm(dst);
-
-                               if (!sock_flag(sk, SOCK_DEAD))
-                                       /* Wake up lingering close() */
-                                       sk->sk_state_change(sk);
-                               else {
-                                       int tmo;
-
-                                       if (tp->linger2 < 0 ||
-                                           (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
-                                            after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt))) {
-                                               tcp_done(sk);
-                                               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
-                                               return 1;
-                                       }
+                       tcp_rearm_rto(sk);
+               } else
+                       tcp_init_metrics(sk);
 
-                                       tmo = tcp_fin_time(sk);
-                                       if (tmo > TCP_TIMEWAIT_LEN) {
-                                               inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN);
-                                       } else if (th->fin || sock_owned_by_user(sk)) {
-                                               /* Bad case. We could lose such FIN otherwise.
-                                                * It is not a big problem, but it looks confusing
-                                                * and not so rare event. We still can lose it now,
-                                                * if it spins in bh_lock_sock(), but it is really
-                                                * marginal case.
-                                                */
-                                               inet_csk_reset_keepalive_timer(sk, tmo);
-                                       } else {
-                                               tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
-                                               goto discard;
-                                       }
-                               }
-                       }
-                       break;
+               /* Prevent spurious tcp_cwnd_restart() on first data packet */
+               tp->lsndtime = tcp_time_stamp;
 
-               case TCP_CLOSING:
-                       if (tp->snd_una == tp->write_seq) {
-                               tcp_time_wait(sk, TCP_TIME_WAIT, 0);
-                               goto discard;
-                       }
+               tcp_initialize_rcv_mss(sk);
+               tcp_fast_path_on(tp);
+               break;
+
+       case TCP_FIN_WAIT1: {
+               struct dst_entry *dst;
+               int tmo;
+
+               /* If we enter the TCP_FIN_WAIT1 state and we are a
+                * Fast Open socket and this is the first acceptable
+                * ACK we have received, this would have acknowledged
+                * our SYNACK so stop the SYNACK timer.
+                */
+               if (req != NULL) {
+                       /* Return RST if ack_seq is invalid.
+                        * Note that RFC793 only says to generate a
+                        * DUPACK for it but for TCP Fast Open it seems
+                        * better to treat this case like TCP_SYN_RECV
+                        * above.
+                        */
+                       if (!acceptable)
+                               return 1;
+                       /* We no longer need the request sock. */
+                       reqsk_fastopen_remove(sk, req, false);
+                       tcp_rearm_rto(sk);
+               }
+               if (tp->snd_una != tp->write_seq)
                        break;
 
-               case TCP_LAST_ACK:
-                       if (tp->snd_una == tp->write_seq) {
-                               tcp_update_metrics(sk);
-                               tcp_done(sk);
-                               goto discard;
-                       }
+               tcp_set_state(sk, TCP_FIN_WAIT2);
+               sk->sk_shutdown |= SEND_SHUTDOWN;
+
+               dst = __sk_dst_get(sk);
+               if (dst)
+                       dst_confirm(dst);
+
+               if (!sock_flag(sk, SOCK_DEAD)) {
+                       /* Wake up lingering close() */
+                       sk->sk_state_change(sk);
                        break;
                }
+
+               if (tp->linger2 < 0 ||
+                   (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
+                    after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt))) {
+                       tcp_done(sk);
+                       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
+                       return 1;
+               }
+
+               tmo = tcp_fin_time(sk);
+               if (tmo > TCP_TIMEWAIT_LEN) {
+                       inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN);
+               } else if (th->fin || sock_owned_by_user(sk)) {
+                       /* Bad case. We could lose such FIN otherwise.
+                        * It is not a big problem, but it looks confusing
+                        * and not so rare event. We still can lose it now,
+                        * if it spins in bh_lock_sock(), but it is really
+                        * marginal case.
+                        */
+                       inet_csk_reset_keepalive_timer(sk, tmo);
+               } else {
+                       tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
+                       goto discard;
+               }
+               break;
+       }
+
+       case TCP_CLOSING:
+               if (tp->snd_una == tp->write_seq) {
+                       tcp_time_wait(sk, TCP_TIME_WAIT, 0);
+                       goto discard;
+               }
+               break;
+
+       case TCP_LAST_ACK:
+               if (tp->snd_una == tp->write_seq) {
+                       tcp_update_metrics(sk);
+                       tcp_done(sk);
+                       goto discard;
+               }
+               break;
        }
 
        /* step 6: check the URG bit */
index 7999fc5..35675e4 100644 (file)
@@ -75,6 +75,7 @@
 #include <net/netdma.h>
 #include <net/secure_seq.h>
 #include <net/tcp_memcontrol.h>
+#include <net/ll_poll.h>
 
 #include <linux/inet.h>
 #include <linux/ipv6.h>
@@ -545,8 +546,7 @@ out:
        sock_put(sk);
 }
 
-static void __tcp_v4_send_check(struct sk_buff *skb,
-                               __be32 saddr, __be32 daddr)
+void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr)
 {
        struct tcphdr *th = tcp_hdr(skb);
 
@@ -571,23 +571,6 @@ void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(tcp_v4_send_check);
 
-int tcp_v4_gso_send_check(struct sk_buff *skb)
-{
-       const struct iphdr *iph;
-       struct tcphdr *th;
-
-       if (!pskb_may_pull(skb, sizeof(*th)))
-               return -EINVAL;
-
-       iph = ip_hdr(skb);
-       th = tcp_hdr(skb);
-
-       th->check = 0;
-       skb->ip_summed = CHECKSUM_PARTIAL;
-       __tcp_v4_send_check(skb, iph->saddr, iph->daddr);
-       return 0;
-}
-
 /*
  *     This routine will send an RST to the other tcp.
  *
@@ -1026,7 +1009,7 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
        key = sock_kmalloc(sk, sizeof(*key), gfp);
        if (!key)
                return -ENOMEM;
-       if (hlist_empty(&md5sig->head) && !tcp_alloc_md5sig_pool(sk)) {
+       if (!tcp_alloc_md5sig_pool()) {
                sock_kfree_s(sk, key, sizeof(*key));
                return -ENOMEM;
        }
@@ -1044,9 +1027,7 @@ EXPORT_SYMBOL(tcp_md5_do_add);
 
 int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
 {
-       struct tcp_sock *tp = tcp_sk(sk);
        struct tcp_md5sig_key *key;
-       struct tcp_md5sig_info *md5sig;
 
        key = tcp_md5_do_lookup(sk, addr, family);
        if (!key)
@@ -1054,10 +1035,6 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
        hlist_del_rcu(&key->node);
        atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
        kfree_rcu(key, rcu);
-       md5sig = rcu_dereference_protected(tp->md5sig_info,
-                                          sock_owned_by_user(sk));
-       if (hlist_empty(&md5sig->head))
-               tcp_free_md5sig_pool();
        return 0;
 }
 EXPORT_SYMBOL(tcp_md5_do_del);
@@ -1071,8 +1048,6 @@ static void tcp_clear_md5_list(struct sock *sk)
 
        md5sig = rcu_dereference_protected(tp->md5sig_info, 1);
 
-       if (!hlist_empty(&md5sig->head))
-               tcp_free_md5sig_pool();
        hlist_for_each_entry_safe(key, n, &md5sig->head, node) {
                hlist_del_rcu(&key->node);
                atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
@@ -2019,6 +1994,7 @@ process:
        if (sk_filter(sk, skb))
                goto discard_and_relse;
 
+       sk_mark_ll(sk, skb);
        skb->dev = NULL;
 
        bh_lock_sock_nested(sk);
@@ -2803,52 +2779,6 @@ void tcp4_proc_exit(void)
 }
 #endif /* CONFIG_PROC_FS */
 
-struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb)
-{
-       const struct iphdr *iph = skb_gro_network_header(skb);
-       __wsum wsum;
-       __sum16 sum;
-
-       switch (skb->ip_summed) {
-       case CHECKSUM_COMPLETE:
-               if (!tcp_v4_check(skb_gro_len(skb), iph->saddr, iph->daddr,
-                                 skb->csum)) {
-                       skb->ip_summed = CHECKSUM_UNNECESSARY;
-                       break;
-               }
-flush:
-               NAPI_GRO_CB(skb)->flush = 1;
-               return NULL;
-
-       case CHECKSUM_NONE:
-               wsum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
-                                         skb_gro_len(skb), IPPROTO_TCP, 0);
-               sum = csum_fold(skb_checksum(skb,
-                                            skb_gro_offset(skb),
-                                            skb_gro_len(skb),
-                                            wsum));
-               if (sum)
-                       goto flush;
-
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
-               break;
-       }
-
-       return tcp_gro_receive(head, skb);
-}
-
-int tcp4_gro_complete(struct sk_buff *skb)
-{
-       const struct iphdr *iph = ip_hdr(skb);
-       struct tcphdr *th = tcp_hdr(skb);
-
-       th->check = ~tcp_v4_check(skb->len - skb_transport_offset(skb),
-                                 iph->saddr, iph->daddr, 0);
-       skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
-
-       return tcp_gro_complete(skb);
-}
-
 struct proto tcp_prot = {
        .name                   = "TCP",
        .owner                  = THIS_MODULE,
index 0f01788..ab1c086 100644 (file)
@@ -317,7 +317,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
                        key = tp->af_specific->md5_lookup(sk, sk);
                        if (key != NULL) {
                                tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC);
-                               if (tcptw->tw_md5_key && tcp_alloc_md5sig_pool(sk) == NULL)
+                               if (tcptw->tw_md5_key && !tcp_alloc_md5sig_pool())
                                        BUG();
                        }
                } while (0);
@@ -358,10 +358,8 @@ void tcp_twsk_destructor(struct sock *sk)
 #ifdef CONFIG_TCP_MD5SIG
        struct tcp_timewait_sock *twsk = tcp_twsk(sk);
 
-       if (twsk->tw_md5_key) {
-               tcp_free_md5sig_pool();
+       if (twsk->tw_md5_key)
                kfree_rcu(twsk->tw_md5_key, rcu);
-       }
 #endif
 }
 EXPORT_SYMBOL_GPL(tcp_twsk_destructor);
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c
new file mode 100644 (file)
index 0000000..3a7525e
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ *     IPV4 GSO/GRO offload support
+ *     Linux INET implementation
+ *
+ *     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.
+ *
+ *     TCPv4 GSO/GRO support
+ */
+
+#include <linux/skbuff.h>
+#include <net/tcp.h>
+#include <net/protocol.h>
+
+struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
+                               netdev_features_t features)
+{
+       struct sk_buff *segs = ERR_PTR(-EINVAL);
+       struct tcphdr *th;
+       unsigned int thlen;
+       unsigned int seq;
+       __be32 delta;
+       unsigned int oldlen;
+       unsigned int mss;
+       struct sk_buff *gso_skb = skb;
+       __sum16 newcheck;
+       bool ooo_okay, copy_destructor;
+
+       if (!pskb_may_pull(skb, sizeof(*th)))
+               goto out;
+
+       th = tcp_hdr(skb);
+       thlen = th->doff * 4;
+       if (thlen < sizeof(*th))
+               goto out;
+
+       if (!pskb_may_pull(skb, thlen))
+               goto out;
+
+       oldlen = (u16)~skb->len;
+       __skb_pull(skb, thlen);
+
+       mss = tcp_skb_mss(skb);
+       if (unlikely(skb->len <= mss))
+               goto out;
+
+       if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
+               /* Packet is from an untrusted source, reset gso_segs. */
+               int type = skb_shinfo(skb)->gso_type;
+
+               if (unlikely(type &
+                            ~(SKB_GSO_TCPV4 |
+                              SKB_GSO_DODGY |
+                              SKB_GSO_TCP_ECN |
+                              SKB_GSO_TCPV6 |
+                              SKB_GSO_GRE |
+                              SKB_GSO_MPLS |
+                              SKB_GSO_UDP_TUNNEL |
+                              0) ||
+                            !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
+                       goto out;
+
+               skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
+
+               segs = NULL;
+               goto out;
+       }
+
+       copy_destructor = gso_skb->destructor == tcp_wfree;
+       ooo_okay = gso_skb->ooo_okay;
+       /* All segments but the first should have ooo_okay cleared */
+       skb->ooo_okay = 0;
+
+       segs = skb_segment(skb, features);
+       if (IS_ERR(segs))
+               goto out;
+
+       /* Only first segment might have ooo_okay set */
+       segs->ooo_okay = ooo_okay;
+
+       delta = htonl(oldlen + (thlen + mss));
+
+       skb = segs;
+       th = tcp_hdr(skb);
+       seq = ntohl(th->seq);
+
+       newcheck = ~csum_fold((__force __wsum)((__force u32)th->check +
+                                              (__force u32)delta));
+
+       do {
+               th->fin = th->psh = 0;
+               th->check = newcheck;
+
+               if (skb->ip_summed != CHECKSUM_PARTIAL)
+                       th->check =
+                            csum_fold(csum_partial(skb_transport_header(skb),
+                                                   thlen, skb->csum));
+
+               seq += mss;
+               if (copy_destructor) {
+                       skb->destructor = gso_skb->destructor;
+                       skb->sk = gso_skb->sk;
+                       /* {tcp|sock}_wfree() use exact truesize accounting :
+                        * sum(skb->truesize) MUST be exactly be gso_skb->truesize
+                        * So we account mss bytes of 'true size' for each segment.
+                        * The last segment will contain the remaining.
+                        */
+                       skb->truesize = mss;
+                       gso_skb->truesize -= mss;
+               }
+               skb = skb->next;
+               th = tcp_hdr(skb);
+
+               th->seq = htonl(seq);
+               th->cwr = 0;
+       } while (skb->next);
+
+       /* Following permits TCP Small Queues to work well with GSO :
+        * The callback to TCP stack will be called at the time last frag
+        * is freed at TX completion, and not right now when gso_skb
+        * is freed by GSO engine
+        */
+       if (copy_destructor) {
+               swap(gso_skb->sk, skb->sk);
+               swap(gso_skb->destructor, skb->destructor);
+               swap(gso_skb->truesize, skb->truesize);
+       }
+
+       delta = htonl(oldlen + (skb_tail_pointer(skb) -
+                               skb_transport_header(skb)) +
+                     skb->data_len);
+       th->check = ~csum_fold((__force __wsum)((__force u32)th->check +
+                               (__force u32)delta));
+       if (skb->ip_summed != CHECKSUM_PARTIAL)
+               th->check = csum_fold(csum_partial(skb_transport_header(skb),
+                                                  thlen, skb->csum));
+out:
+       return segs;
+}
+EXPORT_SYMBOL(tcp_tso_segment);
+
+struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb)
+{
+       struct sk_buff **pp = NULL;
+       struct sk_buff *p;
+       struct tcphdr *th;
+       struct tcphdr *th2;
+       unsigned int len;
+       unsigned int thlen;
+       __be32 flags;
+       unsigned int mss = 1;
+       unsigned int hlen;
+       unsigned int off;
+       int flush = 1;
+       int i;
+
+       off = skb_gro_offset(skb);
+       hlen = off + sizeof(*th);
+       th = skb_gro_header_fast(skb, off);
+       if (skb_gro_header_hard(skb, hlen)) {
+               th = skb_gro_header_slow(skb, hlen, off);
+               if (unlikely(!th))
+                       goto out;
+       }
+
+       thlen = th->doff * 4;
+       if (thlen < sizeof(*th))
+               goto out;
+
+       hlen = off + thlen;
+       if (skb_gro_header_hard(skb, hlen)) {
+               th = skb_gro_header_slow(skb, hlen, off);
+               if (unlikely(!th))
+                       goto out;
+       }
+
+       skb_gro_pull(skb, thlen);
+
+       len = skb_gro_len(skb);
+       flags = tcp_flag_word(th);
+
+       for (; (p = *head); head = &p->next) {
+               if (!NAPI_GRO_CB(p)->same_flow)
+                       continue;
+
+               th2 = tcp_hdr(p);
+
+               if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
+                       NAPI_GRO_CB(p)->same_flow = 0;
+                       continue;
+               }
+
+               goto found;
+       }
+
+       goto out_check_final;
+
+found:
+       flush = NAPI_GRO_CB(p)->flush;
+       flush |= (__force int)(flags & TCP_FLAG_CWR);
+       flush |= (__force int)((flags ^ tcp_flag_word(th2)) &
+                 ~(TCP_FLAG_CWR | TCP_FLAG_FIN | TCP_FLAG_PSH));
+       flush |= (__force int)(th->ack_seq ^ th2->ack_seq);
+       for (i = sizeof(*th); i < thlen; i += 4)
+               flush |= *(u32 *)((u8 *)th + i) ^
+                        *(u32 *)((u8 *)th2 + i);
+
+       mss = tcp_skb_mss(p);
+
+       flush |= (len - 1) >= mss;
+       flush |= (ntohl(th2->seq) + skb_gro_len(p)) ^ ntohl(th->seq);
+
+       if (flush || skb_gro_receive(head, skb)) {
+               mss = 1;
+               goto out_check_final;
+       }
+
+       p = *head;
+       th2 = tcp_hdr(p);
+       tcp_flag_word(th2) |= flags & (TCP_FLAG_FIN | TCP_FLAG_PSH);
+
+out_check_final:
+       flush = len < mss;
+       flush |= (__force int)(flags & (TCP_FLAG_URG | TCP_FLAG_PSH |
+                                       TCP_FLAG_RST | TCP_FLAG_SYN |
+                                       TCP_FLAG_FIN));
+
+       if (p && (!NAPI_GRO_CB(skb)->same_flow || flush))
+               pp = head;
+
+out:
+       NAPI_GRO_CB(skb)->flush |= flush;
+
+       return pp;
+}
+EXPORT_SYMBOL(tcp_gro_receive);
+
+int tcp_gro_complete(struct sk_buff *skb)
+{
+       struct tcphdr *th = tcp_hdr(skb);
+
+       skb->csum_start = skb_transport_header(skb) - skb->head;
+       skb->csum_offset = offsetof(struct tcphdr, check);
+       skb->ip_summed = CHECKSUM_PARTIAL;
+
+       skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
+
+       if (th->cwr)
+               skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;
+
+       return 0;
+}
+EXPORT_SYMBOL(tcp_gro_complete);
+
+static int tcp_v4_gso_send_check(struct sk_buff *skb)
+{
+       const struct iphdr *iph;
+       struct tcphdr *th;
+
+       if (!pskb_may_pull(skb, sizeof(*th)))
+               return -EINVAL;
+
+       iph = ip_hdr(skb);
+       th = tcp_hdr(skb);
+
+       th->check = 0;
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       __tcp_v4_send_check(skb, iph->saddr, iph->daddr);
+       return 0;
+}
+
+static struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb)
+{
+       const struct iphdr *iph = skb_gro_network_header(skb);
+       __wsum wsum;
+       __sum16 sum;
+
+       switch (skb->ip_summed) {
+       case CHECKSUM_COMPLETE:
+               if (!tcp_v4_check(skb_gro_len(skb), iph->saddr, iph->daddr,
+                                 skb->csum)) {
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+                       break;
+               }
+flush:
+               NAPI_GRO_CB(skb)->flush = 1;
+               return NULL;
+
+       case CHECKSUM_NONE:
+               wsum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
+                                         skb_gro_len(skb), IPPROTO_TCP, 0);
+               sum = csum_fold(skb_checksum(skb,
+                                            skb_gro_offset(skb),
+                                            skb_gro_len(skb),
+                                            wsum));
+               if (sum)
+                       goto flush;
+
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               break;
+       }
+
+       return tcp_gro_receive(head, skb);
+}
+
+static int tcp4_gro_complete(struct sk_buff *skb)
+{
+       const struct iphdr *iph = ip_hdr(skb);
+       struct tcphdr *th = tcp_hdr(skb);
+
+       th->check = ~tcp_v4_check(skb->len - skb_transport_offset(skb),
+                                 iph->saddr, iph->daddr, 0);
+       skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+
+       return tcp_gro_complete(skb);
+}
+
+static const struct net_offload tcpv4_offload = {
+       .callbacks = {
+               .gso_send_check =       tcp_v4_gso_send_check,
+               .gso_segment    =       tcp_tso_segment,
+               .gro_receive    =       tcp4_gro_receive,
+               .gro_complete   =       tcp4_gro_complete,
+       },
+};
+
+int __init tcpv4_offload_init(void)
+{
+       return inet_add_offload(&tcpv4_offload, IPPROTO_TCP);
+}
index ec335fa..3d60949 100644 (file)
@@ -160,6 +160,7 @@ static void tcp_event_data_sent(struct tcp_sock *tp,
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
        const u32 now = tcp_time_stamp;
+       const struct dst_entry *dst = __sk_dst_get(sk);
 
        if (sysctl_tcp_slow_start_after_idle &&
            (!tp->packets_out && (s32)(now - tp->lsndtime) > icsk->icsk_rto))
@@ -170,8 +171,9 @@ static void tcp_event_data_sent(struct tcp_sock *tp,
        /* If it is a reply for ato after last received
         * packet, enter pingpong mode.
         */
-       if ((u32)(now - icsk->icsk_ack.lrcvtime) < icsk->icsk_ack.ato)
-               icsk->icsk_ack.pingpong = 1;
+       if ((u32)(now - icsk->icsk_ack.lrcvtime) < icsk->icsk_ack.ato &&
+           (!dst || !dst_metric(dst, RTAX_QUICKACK)))
+                       icsk->icsk_ack.pingpong = 1;
 }
 
 /* Account for an ACK we sent. */
@@ -181,6 +183,21 @@ static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts)
        inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
 }
 
+
+u32 tcp_default_init_rwnd(u32 mss)
+{
+       /* Initial receive window should be twice of TCP_INIT_CWND to
+        * enable proper sending of new unsent data during fast recovery
+        * (RFC 3517, Section 4, NextSeg() rule (2)). Further place a
+        * limit when mss is larger than 1460.
+        */
+       u32 init_rwnd = TCP_INIT_CWND * 2;
+
+       if (mss > 1460)
+               init_rwnd = max((1460 * init_rwnd) / mss, 2U);
+       return init_rwnd;
+}
+
 /* Determine a window scaling and initial window to offer.
  * Based on the assumption that the given amount of space
  * will be offered. Store the results in the tp structure.
@@ -230,22 +247,10 @@ void tcp_select_initial_window(int __space, __u32 mss,
                }
        }
 
-       /* Set initial window to a value enough for senders starting with
-        * initial congestion window of TCP_DEFAULT_INIT_RCVWND. Place
-        * a limit on the initial window when mss is larger than 1460.
-        */
        if (mss > (1 << *rcv_wscale)) {
-               int init_cwnd = TCP_DEFAULT_INIT_RCVWND;
-               if (mss > 1460)
-                       init_cwnd =
-                       max_t(u32, (1460 * TCP_DEFAULT_INIT_RCVWND) / mss, 2);
-               /* when initializing use the value from init_rcv_wnd
-                * rather than the default from above
-                */
-               if (init_rcv_wnd)
-                       *rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss);
-               else
-                       *rcv_wnd = min(*rcv_wnd, init_cwnd * mss);
+               if (!init_rcv_wnd) /* Use default unless specified otherwise */
+                       init_rcv_wnd = tcp_default_init_rwnd(mss);
+               *rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss);
        }
 
        /* Set the clamp no higher than max representable value */
index 0bf5d39..6b270e5 100644 (file)
 #include <trace/events/udp.h>
 #include <linux/static_key.h>
 #include <trace/events/skb.h>
+#include <net/ll_poll.h>
 #include "udp_impl.h"
 
 struct udp_table udp_table __read_mostly;
@@ -429,7 +430,7 @@ begin:
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
                                hash = inet_ehashfn(net, daddr, hnum,
-                                                   saddr, htons(sport));
+                                                   saddr, sport);
                                matches = 1;
                        }
                } else if (score == badness && reuseport) {
@@ -510,7 +511,7 @@ begin:
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
                                hash = inet_ehashfn(net, daddr, hnum,
-                                                   saddr, htons(sport));
+                                                   saddr, sport);
                                matches = 1;
                        }
                } else if (score == badness && reuseport) {
@@ -799,7 +800,7 @@ send:
 /*
  * Push out all pending data as one UDP datagram. Socket is locked.
  */
-static int udp_push_pending_frames(struct sock *sk)
+int udp_push_pending_frames(struct sock *sk)
 {
        struct udp_sock  *up = udp_sk(sk);
        struct inet_sock *inet = inet_sk(sk);
@@ -818,6 +819,7 @@ out:
        up->pending = 0;
        return err;
 }
+EXPORT_SYMBOL(udp_push_pending_frames);
 
 int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                size_t len)
@@ -1709,7 +1711,10 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
        sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
 
        if (sk != NULL) {
-               int ret = udp_queue_rcv_skb(sk, skb);
+               int ret;
+
+               sk_mark_ll(sk, skb);
+               ret = udp_queue_rcv_skb(sk, skb);
                sock_put(sk);
 
                /* a return value > 0 means to resubmit the input, but
@@ -1967,6 +1972,8 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait)
        unsigned int mask = datagram_poll(file, sock, wait);
        struct sock *sk = sock->sk;
 
+       sock_rps_record_flow(sk);
+
        /* Check for false positives due to checksum errors */
        if ((mask & POLLRDNORM) && !(file->f_flags & O_NONBLOCK) &&
            !(sk->sk_shutdown & RCV_SHUTDOWN) && !first_packet_length(sk))
@@ -2284,29 +2291,8 @@ void __init udp_init(void)
        sysctl_udp_wmem_min = SK_MEM_QUANTUM;
 }
 
-int udp4_ufo_send_check(struct sk_buff *skb)
-{
-       if (!pskb_may_pull(skb, sizeof(struct udphdr)))
-               return -EINVAL;
-
-       if (likely(!skb->encapsulation)) {
-               const struct iphdr *iph;
-               struct udphdr *uh;
-
-               iph = ip_hdr(skb);
-               uh = udp_hdr(skb);
-
-               uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
-                               IPPROTO_UDP, 0);
-               skb->csum_start = skb_transport_header(skb) - skb->head;
-               skb->csum_offset = offsetof(struct udphdr, check);
-               skb->ip_summed = CHECKSUM_PARTIAL;
-       }
-       return 0;
-}
-
-static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
-               netdev_features_t features)
+struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
+                                      netdev_features_t features)
 {
        struct sk_buff *segs = ERR_PTR(-EINVAL);
        int mac_len = skb->mac_len;
@@ -2365,53 +2351,3 @@ static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
 out:
        return segs;
 }
-
-struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
-       netdev_features_t features)
-{
-       struct sk_buff *segs = ERR_PTR(-EINVAL);
-       unsigned int mss;
-       mss = skb_shinfo(skb)->gso_size;
-       if (unlikely(skb->len <= mss))
-               goto out;
-
-       if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
-               /* Packet is from an untrusted source, reset gso_segs. */
-               int type = skb_shinfo(skb)->gso_type;
-
-               if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
-                                     SKB_GSO_UDP_TUNNEL |
-                                     SKB_GSO_GRE) ||
-                            !(type & (SKB_GSO_UDP))))
-                       goto out;
-
-               skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
-
-               segs = NULL;
-               goto out;
-       }
-
-       /* Fragment the skb. IP headers of the fragments are updated in
-        * inet_gso_segment()
-        */
-       if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
-               segs = skb_udp_tunnel_segment(skb, features);
-       else {
-               int offset;
-               __wsum csum;
-
-               /* Do software UFO. Complete and fill in the UDP checksum as
-                * HW cannot do checksum of UDP packets sent as multiple
-                * IP fragments.
-                */
-               offset = skb_checksum_start_offset(skb);
-               csum = skb_checksum(skb, offset, skb->len - offset, 0);
-               offset += skb->csum_offset;
-               *(__sum16 *)(skb->data + offset) = csum_fold(csum);
-               skb->ip_summed = CHECKSUM_NONE;
-
-               segs = skb_segment(skb, features);
-       }
-out:
-       return segs;
-}
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
new file mode 100644 (file)
index 0000000..f35ecca
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ *     IPV4 GSO/GRO offload support
+ *     Linux INET implementation
+ *
+ *     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.
+ *
+ *     UDPv4 GSO support
+ */
+
+#include <linux/skbuff.h>
+#include <net/udp.h>
+#include <net/protocol.h>
+
+static int udp4_ufo_send_check(struct sk_buff *skb)
+{
+       if (!pskb_may_pull(skb, sizeof(struct udphdr)))
+               return -EINVAL;
+
+       if (likely(!skb->encapsulation)) {
+               const struct iphdr *iph;
+               struct udphdr *uh;
+
+               iph = ip_hdr(skb);
+               uh = udp_hdr(skb);
+
+               uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
+                               IPPROTO_UDP, 0);
+               skb->csum_start = skb_transport_header(skb) - skb->head;
+               skb->csum_offset = offsetof(struct udphdr, check);
+               skb->ip_summed = CHECKSUM_PARTIAL;
+       }
+
+       return 0;
+}
+
+static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
+                                        netdev_features_t features)
+{
+       struct sk_buff *segs = ERR_PTR(-EINVAL);
+       unsigned int mss;
+
+       mss = skb_shinfo(skb)->gso_size;
+       if (unlikely(skb->len <= mss))
+               goto out;
+
+       if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
+               /* Packet is from an untrusted source, reset gso_segs. */
+               int type = skb_shinfo(skb)->gso_type;
+
+               if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
+                                     SKB_GSO_UDP_TUNNEL |
+                                     SKB_GSO_GRE | SKB_GSO_MPLS) ||
+                            !(type & (SKB_GSO_UDP))))
+                       goto out;
+
+               skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
+
+               segs = NULL;
+               goto out;
+       }
+
+       /* Fragment the skb. IP headers of the fragments are updated in
+        * inet_gso_segment()
+        */
+       if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
+               segs = skb_udp_tunnel_segment(skb, features);
+       else {
+               int offset;
+               __wsum csum;
+
+               /* Do software UFO. Complete and fill in the UDP checksum as
+                * HW cannot do checksum of UDP packets sent as multiple
+                * IP fragments.
+                */
+               offset = skb_checksum_start_offset(skb);
+               csum = skb_checksum(skb, offset, skb->len - offset, 0);
+               offset += skb->csum_offset;
+               *(__sum16 *)(skb->data + offset) = csum_fold(csum);
+               skb->ip_summed = CHECKSUM_NONE;
+
+               segs = skb_segment(skb, features);
+       }
+out:
+       return segs;
+}
+
+static const struct net_offload udpv4_offload = {
+       .callbacks = {
+               .gso_send_check = udp4_ufo_send_check,
+               .gso_segment = udp4_ufo_fragment,
+       },
+};
+
+int __init udpv4_offload_init(void)
+{
+       return inet_add_offload(&udpv4_offload, IPPROTO_UDP);
+}
index 05a5df2..06347db 100644 (file)
@@ -63,7 +63,7 @@ static int xfrm_tunnel_err(struct sk_buff *skb, u32 info)
 static struct xfrm_tunnel xfrm_tunnel_handler __read_mostly = {
        .handler        =       xfrm_tunnel_rcv,
        .err_handler    =       xfrm_tunnel_err,
-       .priority       =       2,
+       .priority       =       3,
 };
 
 #if IS_ENABLED(CONFIG_IPV6)
index 9af088d..470a9c0 100644 (file)
@@ -7,7 +7,7 @@ obj-$(CONFIG_IPV6) += ipv6.o
 ipv6-objs :=   af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
                addrlabel.o \
                route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
-               raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
+               raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \
                exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o
 
 ipv6-offload :=        ip6_offload.o tcpv6_offload.o udp_offload.o exthdrs_offload.o
index 4ab4c38..cfdcf7b 100644 (file)
@@ -253,37 +253,32 @@ static inline bool addrconf_qdisc_ok(const struct net_device *dev)
        return !qdisc_tx_is_noop(dev);
 }
 
-static void addrconf_del_timer(struct inet6_ifaddr *ifp)
+static void addrconf_del_rs_timer(struct inet6_dev *idev)
 {
-       if (del_timer(&ifp->timer))
+       if (del_timer(&idev->rs_timer))
+               __in6_dev_put(idev);
+}
+
+static void addrconf_del_dad_timer(struct inet6_ifaddr *ifp)
+{
+       if (del_timer(&ifp->dad_timer))
                __in6_ifa_put(ifp);
 }
 
-enum addrconf_timer_t {
-       AC_NONE,
-       AC_DAD,
-       AC_RS,
-};
+static void addrconf_mod_rs_timer(struct inet6_dev *idev,
+                                 unsigned long when)
+{
+       if (!timer_pending(&idev->rs_timer))
+               in6_dev_hold(idev);
+       mod_timer(&idev->rs_timer, jiffies + when);
+}
 
-static void addrconf_mod_timer(struct inet6_ifaddr *ifp,
-                              enum addrconf_timer_t what,
-                              unsigned long when)
+static void addrconf_mod_dad_timer(struct inet6_ifaddr *ifp,
+                                  unsigned long when)
 {
-       if (!del_timer(&ifp->timer))
+       if (!timer_pending(&ifp->dad_timer))
                in6_ifa_hold(ifp);
-
-       switch (what) {
-       case AC_DAD:
-               ifp->timer.function = addrconf_dad_timer;
-               break;
-       case AC_RS:
-               ifp->timer.function = addrconf_rs_timer;
-               break;
-       default:
-               break;
-       }
-       ifp->timer.expires = jiffies + when;
-       add_timer(&ifp->timer);
+       mod_timer(&ifp->dad_timer, jiffies + when);
 }
 
 static int snmp6_alloc_dev(struct inet6_dev *idev)
@@ -326,6 +321,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
 
        WARN_ON(!list_empty(&idev->addr_list));
        WARN_ON(idev->mc_list != NULL);
+       WARN_ON(timer_pending(&idev->rs_timer));
 
 #ifdef NET_REFCNT_DEBUG
        pr_debug("%s: %s\n", __func__, dev ? dev->name : "NIL");
@@ -357,7 +353,8 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
        rwlock_init(&ndev->lock);
        ndev->dev = dev;
        INIT_LIST_HEAD(&ndev->addr_list);
-
+       setup_timer(&ndev->rs_timer, addrconf_rs_timer,
+                   (unsigned long)ndev);
        memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
        ndev->cnf.mtu6 = dev->mtu;
        ndev->cnf.sysctl = NULL;
@@ -776,7 +773,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
 
        in6_dev_put(ifp->idev);
 
-       if (del_timer(&ifp->timer))
+       if (del_timer(&ifp->dad_timer))
                pr_notice("Timer is still running, when freeing ifa=%p\n", ifp);
 
        if (ifp->state != INET6_IFADDR_STATE_DEAD) {
@@ -869,9 +866,9 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 
        spin_lock_init(&ifa->lock);
        spin_lock_init(&ifa->state_lock);
-       init_timer(&ifa->timer);
+       setup_timer(&ifa->dad_timer, addrconf_dad_timer,
+                   (unsigned long)ifa);
        INIT_HLIST_NODE(&ifa->addr_lst);
-       ifa->timer.data = (unsigned long) ifa;
        ifa->scope = scope;
        ifa->prefix_len = pfxlen;
        ifa->flags = flags | IFA_F_TENTATIVE;
@@ -994,7 +991,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
        }
        write_unlock_bh(&idev->lock);
 
-       addrconf_del_timer(ifp);
+       addrconf_del_dad_timer(ifp);
 
        ipv6_ifa_notify(RTM_DELADDR, ifp);
 
@@ -1126,8 +1123,7 @@ retry:
 
        ift = !max_addresses ||
              ipv6_count_addresses(idev) < max_addresses ?
-               ipv6_add_addr(idev, &addr, tmp_plen,
-                             ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
+               ipv6_add_addr(idev, &addr, tmp_plen, ipv6_addr_scope(&addr),
                              addr_flags) : NULL;
        if (IS_ERR_OR_NULL(ift)) {
                in6_ifa_put(ifp);
@@ -1448,6 +1444,23 @@ try_nextdev:
 }
 EXPORT_SYMBOL(ipv6_dev_get_saddr);
 
+int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
+                     unsigned char banned_flags)
+{
+       struct inet6_ifaddr *ifp;
+       int err = -EADDRNOTAVAIL;
+
+       list_for_each_entry(ifp, &idev->addr_list, if_list) {
+               if (ifp->scope == IFA_LINK &&
+                   !(ifp->flags & banned_flags)) {
+                       *addr = ifp->addr;
+                       err = 0;
+                       break;
+               }
+       }
+       return err;
+}
+
 int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
                    unsigned char banned_flags)
 {
@@ -1457,17 +1470,8 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
        rcu_read_lock();
        idev = __in6_dev_get(dev);
        if (idev) {
-               struct inet6_ifaddr *ifp;
-
                read_lock_bh(&idev->lock);
-               list_for_each_entry(ifp, &idev->addr_list, if_list) {
-                       if (ifp->scope == IFA_LINK &&
-                           !(ifp->flags & banned_flags)) {
-                               *addr = ifp->addr;
-                               err = 0;
-                               break;
-                       }
-               }
+               err = __ipv6_get_lladdr(idev, addr, banned_flags);
                read_unlock_bh(&idev->lock);
        }
        rcu_read_unlock();
@@ -1581,7 +1585,7 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
 {
        if (ifp->flags&IFA_F_PERMANENT) {
                spin_lock_bh(&ifp->lock);
-               addrconf_del_timer(ifp);
+               addrconf_del_dad_timer(ifp);
                ifp->flags |= IFA_F_TENTATIVE;
                if (dad_failed)
                        ifp->flags |= IFA_F_DADFAILED;
@@ -2402,6 +2406,7 @@ err_exit:
  *     Manual configuration of address on an interface
  */
 static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx,
+                         const struct in6_addr *peer_pfx,
                          unsigned int plen, __u8 ifa_flags, __u32 prefered_lft,
                          __u32 valid_lft)
 {
@@ -2457,6 +2462,8 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p
                ifp->valid_lft = valid_lft;
                ifp->prefered_lft = prefered_lft;
                ifp->tstamp = jiffies;
+               if (peer_pfx)
+                       ifp->peer_addr = *peer_pfx;
                spin_unlock_bh(&ifp->lock);
 
                addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
@@ -2500,12 +2507,6 @@ static int inet6_addr_del(struct net *net, int ifindex, const struct in6_addr *p
                        read_unlock_bh(&idev->lock);
 
                        ipv6_del_addr(ifp);
-
-                       /* If the last address is deleted administratively,
-                          disable IPv6 on this interface.
-                        */
-                       if (list_empty(&idev->addr_list))
-                               addrconf_ifdown(idev->dev, 1);
                        return 0;
                }
        }
@@ -2526,7 +2527,7 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg)
                return -EFAULT;
 
        rtnl_lock();
-       err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr,
+       err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, NULL,
                             ireq.ifr6_prefixlen, IFA_F_PERMANENT,
                             INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
        rtnl_unlock();
@@ -2761,8 +2762,6 @@ static void addrconf_gre_config(struct net_device *dev)
        struct inet6_dev *idev;
        struct in6_addr addr;
 
-       pr_info("%s(%s)\n", __func__, dev->name);
-
        ASSERT_RTNL();
 
        if ((idev = ipv6_find_idev(dev)) == NULL) {
@@ -2829,9 +2828,9 @@ static void addrconf_ip6_tnl_config(struct net_device *dev)
 }
 
 static int addrconf_notify(struct notifier_block *this, unsigned long event,
-                          void *data)
+                          void *ptr)
 {
-       struct net_device *dev = (struct net_device *) data;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct inet6_dev *idev = __in6_dev_get(dev);
        int run_pending = 0;
        int err;
@@ -3039,7 +3038,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
                hlist_for_each_entry_rcu(ifa, h, addr_lst) {
                        if (ifa->idev == idev) {
                                hlist_del_init_rcu(&ifa->addr_lst);
-                               addrconf_del_timer(ifa);
+                               addrconf_del_dad_timer(ifa);
                                goto restart;
                        }
                }
@@ -3048,6 +3047,8 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
        write_lock_bh(&idev->lock);
 
+       addrconf_del_rs_timer(idev);
+
        /* Step 2: clear flags for stateless addrconf */
        if (!how)
                idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY);
@@ -3077,7 +3078,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
        while (!list_empty(&idev->addr_list)) {
                ifa = list_first_entry(&idev->addr_list,
                                       struct inet6_ifaddr, if_list);
-               addrconf_del_timer(ifa);
+               addrconf_del_dad_timer(ifa);
 
                list_del(&ifa->if_list);
 
@@ -3119,10 +3120,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
 static void addrconf_rs_timer(unsigned long data)
 {
-       struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
-       struct inet6_dev *idev = ifp->idev;
+       struct inet6_dev *idev = (struct inet6_dev *)data;
+       struct in6_addr lladdr;
 
-       read_lock(&idev->lock);
+       write_lock(&idev->lock);
        if (idev->dead || !(idev->if_flags & IF_READY))
                goto out;
 
@@ -3133,18 +3134,19 @@ static void addrconf_rs_timer(unsigned long data)
        if (idev->if_flags & IF_RA_RCVD)
                goto out;
 
-       spin_lock(&ifp->lock);
-       if (ifp->probes++ < idev->cnf.rtr_solicits) {
-               /* The wait after the last probe can be shorter */
-               addrconf_mod_timer(ifp, AC_RS,
-                                  (ifp->probes == idev->cnf.rtr_solicits) ?
-                                  idev->cnf.rtr_solicit_delay :
-                                  idev->cnf.rtr_solicit_interval);
-               spin_unlock(&ifp->lock);
+       if (idev->rs_probes++ < idev->cnf.rtr_solicits) {
+               if (!__ipv6_get_lladdr(idev, &lladdr, IFA_F_TENTATIVE))
+                       ndisc_send_rs(idev->dev, &lladdr,
+                                     &in6addr_linklocal_allrouters);
+               else
+                       goto out;
 
-               ndisc_send_rs(idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
+               /* The wait after the last probe can be shorter */
+               addrconf_mod_rs_timer(idev, (idev->rs_probes ==
+                                            idev->cnf.rtr_solicits) ?
+                                     idev->cnf.rtr_solicit_delay :
+                                     idev->cnf.rtr_solicit_interval);
        } else {
-               spin_unlock(&ifp->lock);
                /*
                 * Note: we do not support deprecated "all on-link"
                 * assumption any longer.
@@ -3153,8 +3155,8 @@ static void addrconf_rs_timer(unsigned long data)
        }
 
 out:
-       read_unlock(&idev->lock);
-       in6_ifa_put(ifp);
+       write_unlock(&idev->lock);
+       in6_dev_put(idev);
 }
 
 /*
@@ -3170,8 +3172,8 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
        else
                rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
 
-       ifp->probes = idev->cnf.dad_transmits;
-       addrconf_mod_timer(ifp, AC_DAD, rand_num);
+       ifp->dad_probes = idev->cnf.dad_transmits;
+       addrconf_mod_dad_timer(ifp, rand_num);
 }
 
 static void addrconf_dad_start(struct inet6_ifaddr *ifp)
@@ -3232,40 +3234,40 @@ static void addrconf_dad_timer(unsigned long data)
        struct inet6_dev *idev = ifp->idev;
        struct in6_addr mcaddr;
 
-       if (!ifp->probes && addrconf_dad_end(ifp))
+       if (!ifp->dad_probes && addrconf_dad_end(ifp))
                goto out;
 
-       read_lock(&idev->lock);
+       write_lock(&idev->lock);
        if (idev->dead || !(idev->if_flags & IF_READY)) {
-               read_unlock(&idev->lock);
+               write_unlock(&idev->lock);
                goto out;
        }
 
        spin_lock(&ifp->lock);
        if (ifp->state == INET6_IFADDR_STATE_DEAD) {
                spin_unlock(&ifp->lock);
-               read_unlock(&idev->lock);
+               write_unlock(&idev->lock);
                goto out;
        }
 
-       if (ifp->probes == 0) {
+       if (ifp->dad_probes == 0) {
                /*
                 * DAD was successful
                 */
 
                ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
                spin_unlock(&ifp->lock);
-               read_unlock(&idev->lock);
+               write_unlock(&idev->lock);
 
                addrconf_dad_completed(ifp);
 
                goto out;
        }
 
-       ifp->probes--;
-       addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time);
+       ifp->dad_probes--;
+       addrconf_mod_dad_timer(ifp, ifp->idev->nd_parms->retrans_time);
        spin_unlock(&ifp->lock);
-       read_unlock(&idev->lock);
+       write_unlock(&idev->lock);
 
        /* send a neighbour solicitation for our addr */
        addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
@@ -3277,6 +3279,10 @@ out:
 static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
 {
        struct net_device *dev = ifp->idev->dev;
+       struct in6_addr lladdr;
+       bool send_rs, send_mld;
+
+       addrconf_del_dad_timer(ifp);
 
        /*
         *      Configure the address for reception. Now it is valid.
@@ -3288,22 +3294,41 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
           router advertisements, start sending router solicitations.
         */
 
-       if (ipv6_accept_ra(ifp->idev) &&
-           ifp->idev->cnf.rtr_solicits > 0 &&
-           (dev->flags&IFF_LOOPBACK) == 0 &&
-           (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
+       read_lock_bh(&ifp->idev->lock);
+       spin_lock(&ifp->lock);
+       send_mld = ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL &&
+                  ifp->idev->valid_ll_addr_cnt == 1;
+       send_rs = send_mld &&
+                 ipv6_accept_ra(ifp->idev) &&
+                 ifp->idev->cnf.rtr_solicits > 0 &&
+                 (dev->flags&IFF_LOOPBACK) == 0;
+       spin_unlock(&ifp->lock);
+       read_unlock_bh(&ifp->idev->lock);
+
+       /* While dad is in progress mld report's source address is in6_addrany.
+        * Resend with proper ll now.
+        */
+       if (send_mld)
+               ipv6_mc_dad_complete(ifp->idev);
+
+       if (send_rs) {
                /*
                 *      If a host as already performed a random delay
                 *      [...] as part of DAD [...] there is no need
                 *      to delay again before sending the first RS
                 */
-               ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
+               if (ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
+                       return;
+               ndisc_send_rs(dev, &lladdr, &in6addr_linklocal_allrouters);
 
-               spin_lock_bh(&ifp->lock);
-               ifp->probes = 1;
+               write_lock_bh(&ifp->idev->lock);
+               spin_lock(&ifp->lock);
+               ifp->idev->rs_probes = 1;
                ifp->idev->if_flags |= IF_RS_SENT;
-               addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval);
-               spin_unlock_bh(&ifp->lock);
+               addrconf_mod_rs_timer(ifp->idev,
+                                     ifp->idev->cnf.rtr_solicit_interval);
+               spin_unlock(&ifp->lock);
+               write_unlock_bh(&ifp->idev->lock);
        }
 }
 
@@ -3615,18 +3640,20 @@ restart:
        rcu_read_unlock_bh();
 }
 
-static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local)
+static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local,
+                                    struct in6_addr **peer_pfx)
 {
        struct in6_addr *pfx = NULL;
 
+       *peer_pfx = NULL;
+
        if (addr)
                pfx = nla_data(addr);
 
        if (local) {
                if (pfx && nla_memcmp(local, pfx, sizeof(*pfx)))
-                       pfx = NULL;
-               else
-                       pfx = nla_data(local);
+                       *peer_pfx = pfx;
+               pfx = nla_data(local);
        }
 
        return pfx;
@@ -3644,7 +3671,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct net *net = sock_net(skb->sk);
        struct ifaddrmsg *ifm;
        struct nlattr *tb[IFA_MAX+1];
-       struct in6_addr *pfx;
+       struct in6_addr *pfx, *peer_pfx;
        int err;
 
        err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
@@ -3652,7 +3679,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
                return err;
 
        ifm = nlmsg_data(nlh);
-       pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]);
+       pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);
        if (pfx == NULL)
                return -EINVAL;
 
@@ -3710,7 +3737,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct net *net = sock_net(skb->sk);
        struct ifaddrmsg *ifm;
        struct nlattr *tb[IFA_MAX+1];
-       struct in6_addr *pfx;
+       struct in6_addr *pfx, *peer_pfx;
        struct inet6_ifaddr *ifa;
        struct net_device *dev;
        u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME;
@@ -3722,7 +3749,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
                return err;
 
        ifm = nlmsg_data(nlh);
-       pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]);
+       pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);
        if (pfx == NULL)
                return -EINVAL;
 
@@ -3750,7 +3777,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
                 * It would be best to check for !NLM_F_CREATE here but
                 * userspace alreay relies on not having to provide this.
                 */
-               return inet6_addr_add(net, ifm->ifa_index, pfx,
+               return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx,
                                      ifm->ifa_prefixlen, ifa_flags,
                                      preferred_lft, valid_lft);
        }
@@ -3807,6 +3834,7 @@ static inline int rt_scope(int ifa_scope)
 static inline int inet6_ifaddr_msgsize(void)
 {
        return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
+              + nla_total_size(16) /* IFA_LOCAL */
               + nla_total_size(16) /* IFA_ADDRESS */
               + nla_total_size(sizeof(struct ifa_cacheinfo));
 }
@@ -3845,13 +3873,22 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
                valid = INFINITY_LIFE_TIME;
        }
 
-       if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0 ||
-           put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) {
-               nlmsg_cancel(skb, nlh);
-               return -EMSGSIZE;
-       }
+       if (!ipv6_addr_any(&ifa->peer_addr)) {
+               if (nla_put(skb, IFA_LOCAL, 16, &ifa->addr) < 0 ||
+                   nla_put(skb, IFA_ADDRESS, 16, &ifa->peer_addr) < 0)
+                       goto error;
+       } else
+               if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0)
+                       goto error;
+
+       if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0)
+               goto error;
 
        return nlmsg_end(skb, nlh);
+
+error:
+       nlmsg_cancel(skb, nlh);
+       return -EMSGSIZE;
 }
 
 static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca,
@@ -4051,7 +4088,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh)
        struct net *net = sock_net(in_skb->sk);
        struct ifaddrmsg *ifm;
        struct nlattr *tb[IFA_MAX+1];
-       struct in6_addr *addr = NULL;
+       struct in6_addr *addr = NULL, *peer;
        struct net_device *dev = NULL;
        struct inet6_ifaddr *ifa;
        struct sk_buff *skb;
@@ -4061,7 +4098,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh)
        if (err < 0)
                goto errout;
 
-       addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]);
+       addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer);
        if (addr == NULL) {
                err = -EINVAL;
                goto errout;
@@ -4339,8 +4376,11 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
 
        write_lock_bh(&idev->lock);
 
-       if (update_rs)
+       if (update_rs) {
                idev->if_flags |= IF_RS_SENT;
+               idev->rs_probes = 1;
+               addrconf_mod_rs_timer(idev, idev->cnf.rtr_solicit_interval);
+       }
 
        /* Well, that's kinda nasty ... */
        list_for_each_entry(ifp, &idev->addr_list, if_list) {
@@ -4353,6 +4393,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
        }
 
        write_unlock_bh(&idev->lock);
+       addrconf_verify(0);
        return 0;
 }
 
@@ -4550,6 +4591,19 @@ errout:
                rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err);
 }
 
+static void update_valid_ll_addr_cnt(struct inet6_ifaddr *ifp, int count)
+{
+       write_lock_bh(&ifp->idev->lock);
+       spin_lock(&ifp->lock);
+       if (((ifp->flags & (IFA_F_PERMANENT|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|
+                           IFA_F_DADFAILED)) == IFA_F_PERMANENT) &&
+           (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL))
+               ifp->idev->valid_ll_addr_cnt += count;
+       WARN_ON(ifp->idev->valid_ll_addr_cnt < 0);
+       spin_unlock(&ifp->lock);
+       write_unlock_bh(&ifp->idev->lock);
+}
+
 static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 {
        struct net *net = dev_net(ifp->idev->dev);
@@ -4558,6 +4612,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 
        switch (event) {
        case RTM_NEWADDR:
+               update_valid_ll_addr_cnt(ifp, 1);
+
                /*
                 * If the address was optimistic
                 * we inserted the route at the start of
@@ -4568,11 +4624,28 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
                        ip6_ins_rt(ifp->rt);
                if (ifp->idev->cnf.forwarding)
                        addrconf_join_anycast(ifp);
+               if (!ipv6_addr_any(&ifp->peer_addr))
+                       addrconf_prefix_route(&ifp->peer_addr, 128,
+                                             ifp->idev->dev, 0, 0);
                break;
        case RTM_DELADDR:
+               update_valid_ll_addr_cnt(ifp, -1);
+
                if (ifp->idev->cnf.forwarding)
                        addrconf_leave_anycast(ifp);
                addrconf_leave_solict(ifp->idev, &ifp->addr);
+               if (!ipv6_addr_any(&ifp->peer_addr)) {
+                       struct rt6_info *rt;
+                       struct net_device *dev = ifp->idev->dev;
+
+                       rt = rt6_lookup(dev_net(dev), &ifp->peer_addr, NULL,
+                                       dev->ifindex, 1);
+                       if (rt) {
+                               dst_hold(&rt->dst);
+                               if (ip6_del_rt(rt))
+                                       dst_free(&rt->dst);
+                       }
+               }
                dst_hold(&ifp->rt->dst);
 
                if (ip6_del_rt(ifp->rt))
@@ -4593,13 +4666,13 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 #ifdef CONFIG_SYSCTL
 
 static
-int addrconf_sysctl_forward(ctl_table *ctl, int write,
+int addrconf_sysctl_forward(struct ctl_table *ctl, int write,
                           void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int val = *valp;
        loff_t pos = *ppos;
-       ctl_table lctl;
+       struct ctl_table lctl;
        int ret;
 
        /*
@@ -4620,13 +4693,16 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write,
 
 static void dev_disable_change(struct inet6_dev *idev)
 {
+       struct netdev_notifier_info info;
+
        if (!idev || !idev->dev)
                return;
 
+       netdev_notifier_info_init(&info, idev->dev);
        if (idev->cnf.disable_ipv6)
-               addrconf_notify(NULL, NETDEV_DOWN, idev->dev);
+               addrconf_notify(NULL, NETDEV_DOWN, &info);
        else
-               addrconf_notify(NULL, NETDEV_UP, idev->dev);
+               addrconf_notify(NULL, NETDEV_UP, &info);
 }
 
 static void addrconf_disable_change(struct net *net, __s32 newf)
@@ -4675,13 +4751,13 @@ static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int newf)
 }
 
 static
-int addrconf_sysctl_disable(ctl_table *ctl, int write,
+int addrconf_sysctl_disable(struct ctl_table *ctl, int write,
                            void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int val = *valp;
        loff_t pos = *ppos;
-       ctl_table lctl;
+       struct ctl_table lctl;
        int ret;
 
        /*
@@ -4703,7 +4779,7 @@ int addrconf_sysctl_disable(ctl_table *ctl, int write,
 static struct addrconf_sysctl_table
 {
        struct ctl_table_header *sysctl_header;
-       ctl_table addrconf_vars[DEVCONF_MAX+1];
+       struct ctl_table addrconf_vars[DEVCONF_MAX+1];
 } addrconf_sysctl __read_mostly = {
        .sysctl_header = NULL,
        .addrconf_vars = {
index 7210456..d2f8742 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <linux/export.h>
 #include <net/ipv6.h>
+#include <net/addrconf.h>
 
 #define IPV6_ADDR_SCOPE_TYPE(scope)    ((scope) << 16)
 
index ab5c7ad..a5ac969 100644 (file)
@@ -49,6 +49,7 @@
 #include <net/udp.h>
 #include <net/udplite.h>
 #include <net/tcp.h>
+#include <net/ping.h>
 #include <net/protocol.h>
 #include <net/inet_common.h>
 #include <net/route.h>
@@ -840,6 +841,9 @@ static int __init inet6_init(void)
        if (err)
                goto out_unregister_udplite_proto;
 
+       err = proto_register(&pingv6_prot, 1);
+       if (err)
+               goto out_unregister_ping_proto;
 
        /* We MUST register RAW sockets before we create the ICMP6,
         * IGMP6, or NDISC control sockets.
@@ -930,6 +934,10 @@ static int __init inet6_init(void)
        if (err)
                goto ipv6_packet_fail;
 
+       err = pingv6_init();
+       if (err)
+               goto pingv6_fail;
+
 #ifdef CONFIG_SYSCTL
        err = ipv6_sysctl_register();
        if (err)
@@ -942,6 +950,8 @@ out:
 sysctl_fail:
        ipv6_packet_cleanup();
 #endif
+pingv6_fail:
+       pingv6_exit();
 ipv6_packet_fail:
        tcpv6_exit();
 tcpv6_fail:
@@ -985,6 +995,8 @@ register_pernet_fail:
        rtnl_unregister_all(PF_INET6);
 out_sock_register_fail:
        rawv6_exit();
+out_unregister_ping_proto:
+       proto_unregister(&pingv6_prot);
 out_unregister_raw_proto:
        proto_unregister(&rawv6_prot);
 out_unregister_udplite_proto:
index 4b56cbb..197e6f4 100644 (file)
@@ -879,3 +879,30 @@ exit_f:
        return err;
 }
 EXPORT_SYMBOL_GPL(ip6_datagram_send_ctl);
+
+void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
+                            __u16 srcp, __u16 destp, int bucket)
+{
+       struct ipv6_pinfo *np = inet6_sk(sp);
+       const struct in6_addr *dest, *src;
+
+       dest  = &np->daddr;
+       src   = &np->rcv_saddr;
+       seq_printf(seq,
+                  "%5d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
+                  "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %pK %d\n",
+                  bucket,
+                  src->s6_addr32[0], src->s6_addr32[1],
+                  src->s6_addr32[2], src->s6_addr32[3], srcp,
+                  dest->s6_addr32[0], dest->s6_addr32[1],
+                  dest->s6_addr32[2], dest->s6_addr32[3], destp,
+                  sp->sk_state,
+                  sk_wmem_alloc_get(sp),
+                  sk_rmem_alloc_get(sp),
+                  0, 0L, 0,
+                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
+                  0,
+                  sock_i_ino(sp),
+                  atomic_read(&sp->sk_refcnt), sp,
+                  atomic_read(&sp->sk_drops));
+}
index c5e83fa..140748d 100644 (file)
@@ -115,7 +115,7 @@ EXPORT_SYMBOL(ipv6_skip_exthdr);
 int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
 {
        const unsigned char *nh = skb_network_header(skb);
-       int packet_len = skb->tail - skb->network_header;
+       int packet_len = skb_tail_pointer(skb) - skb_network_header(skb);
        struct ipv6_opt_hdr *hdr;
        int len;
 
index b4ff0a4..7cfc8d2 100644 (file)
@@ -57,6 +57,7 @@
 
 #include <net/ipv6.h>
 #include <net/ip6_checksum.h>
+#include <net/ping.h>
 #include <net/protocol.h>
 #include <net/raw.h>
 #include <net/rawv6.h>
@@ -84,12 +85,18 @@ static inline struct sock *icmpv6_sk(struct net *net)
 static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                       u8 type, u8 code, int offset, __be32 info)
 {
+       /* icmpv6_notify checks 8 bytes can be pulled, icmp6hdr is 8 bytes */
+       struct icmp6hdr *icmp6 = (struct icmp6hdr *) (skb->data + offset);
        struct net *net = dev_net(skb->dev);
 
        if (type == ICMPV6_PKT_TOOBIG)
                ip6_update_pmtu(skb, net, info, 0, 0);
        else if (type == NDISC_REDIRECT)
                ip6_redirect(skb, net, 0, 0);
+
+       if (!(type & ICMPV6_INFOMSG_MASK))
+               if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST)
+                       ping_err(skb, offset, info);
 }
 
 static int icmpv6_rcv(struct sk_buff *skb);
@@ -224,7 +231,8 @@ static bool opt_unrec(struct sk_buff *skb, __u32 offset)
        return (*op & 0xC0) == 0x80;
 }
 
-static int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, struct icmp6hdr *thdr, int len)
+int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
+                              struct icmp6hdr *thdr, int len)
 {
        struct sk_buff *skb;
        struct icmp6hdr *icmp6h;
@@ -307,8 +315,8 @@ static void mip6_addr_swap(struct sk_buff *skb)
 static inline void mip6_addr_swap(struct sk_buff *skb) {}
 #endif
 
-static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb,
-                                            struct sock *sk, struct flowi6 *fl6)
+struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb,
+                                     struct sock *sk, struct flowi6 *fl6)
 {
        struct dst_entry *dst, *dst2;
        struct flowi6 fl2;
@@ -391,7 +399,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        int err = 0;
 
        if ((u8 *)hdr < skb->head ||
-           (skb->network_header + sizeof(*hdr)) > skb->tail)
+           (skb_network_header(skb) + sizeof(*hdr)) > skb_tail_pointer(skb))
                return;
 
        /*
@@ -697,7 +705,8 @@ static int icmpv6_rcv(struct sk_buff *skb)
                skb->csum = ~csum_unfold(csum_ipv6_magic(saddr, daddr, skb->len,
                                             IPPROTO_ICMPV6, 0));
                if (__skb_checksum_complete(skb)) {
-                       LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [%pI6 > %pI6]\n",
+                       LIMIT_NETDEBUG(KERN_DEBUG
+                                      "ICMPv6 checksum failed [%pI6c > %pI6c]\n",
                                       saddr, daddr);
                        goto csum_error;
                }
@@ -718,7 +727,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
                break;
 
        case ICMPV6_ECHO_REPLY:
-               /* we couldn't care less */
+               ping_rcv(skb);
                break;
 
        case ICMPV6_PKT_TOOBIG:
@@ -967,7 +976,7 @@ int icmpv6_err_convert(u8 type, u8 code, int *err)
 EXPORT_SYMBOL(icmpv6_err_convert);
 
 #ifdef CONFIG_SYSCTL
-ctl_table ipv6_icmp_table_template[] = {
+struct ctl_table ipv6_icmp_table_template[] = {
        {
                .procname       = "ratelimit",
                .data           = &init_net.ipv6.sysctl.icmpv6_time,
index 71b766e..a263b99 100644 (file)
@@ -98,6 +98,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
                       SKB_GSO_UDP_TUNNEL |
+                      SKB_GSO_MPLS |
                       SKB_GSO_TCPV6 |
                       0)))
                goto out;
index d5d20cd..6e3ddf8 100644 (file)
@@ -1098,11 +1098,12 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src,
        return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL;
 }
 
-static void ip6_append_data_mtu(int *mtu,
+static void ip6_append_data_mtu(unsigned int *mtu,
                                int *maxfraglen,
                                unsigned int fragheaderlen,
                                struct sk_buff *skb,
-                               struct rt6_info *rt)
+                               struct rt6_info *rt,
+                               bool pmtuprobe)
 {
        if (!(rt->dst.flags & DST_XFRM_TUNNEL)) {
                if (skb == NULL) {
@@ -1114,7 +1115,9 @@ static void ip6_append_data_mtu(int *mtu,
                         * this fragment is not first, the headers
                         * space is regarded as data space.
                         */
-                       *mtu = dst_mtu(rt->dst.path);
+                       *mtu = min(*mtu, pmtuprobe ?
+                                  rt->dst.dev->mtu :
+                                  dst_mtu(rt->dst.path));
                }
                *maxfraglen = ((*mtu - fragheaderlen) & ~7)
                              + fragheaderlen - sizeof(struct frag_hdr);
@@ -1131,11 +1134,10 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct inet_cork *cork;
        struct sk_buff *skb, *skb_prev = NULL;
-       unsigned int maxfraglen, fragheaderlen;
+       unsigned int maxfraglen, fragheaderlen, mtu;
        int exthdrlen;
        int dst_exthdrlen;
        int hh_len;
-       int mtu;
        int copy;
        int err;
        int offset = 0;
@@ -1292,7 +1294,9 @@ alloc_new_skb:
                        /* update mtu and maxfraglen if necessary */
                        if (skb == NULL || skb_prev == NULL)
                                ip6_append_data_mtu(&mtu, &maxfraglen,
-                                                   fragheaderlen, skb, rt);
+                                                   fragheaderlen, skb, rt,
+                                                   np->pmtudisc ==
+                                                   IPV6_PMTUDISC_PROBE);
 
                        skb_prev = skb;
 
index 241fb8a..583e8d4 100644 (file)
@@ -1319,7 +1319,7 @@ static int ip6mr_mfc_delete(struct mr6_table *mrt, struct mf6cctl *mfc,
 static int ip6mr_device_event(struct notifier_block *this,
                              unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net *net = dev_net(dev);
        struct mr6_table *mrt;
        struct mif_device *v;
index bfa6cc3..99cd65c 100644 (file)
@@ -999,6 +999,14 @@ static void mld_ifc_start_timer(struct inet6_dev *idev, int delay)
                in6_dev_hold(idev);
 }
 
+static void mld_dad_start_timer(struct inet6_dev *idev, int delay)
+{
+       int tv = net_random() % delay;
+
+       if (!mod_timer(&idev->mc_dad_timer, jiffies+tv+2))
+               in6_dev_hold(idev);
+}
+
 /*
  *     IGMP handling (alias multicast ICMPv6 messages)
  */
@@ -1343,8 +1351,9 @@ static void ip6_mc_hdr(struct sock *sk, struct sk_buff *skb,
        hdr->daddr = *daddr;
 }
 
-static struct sk_buff *mld_newpack(struct net_device *dev, int size)
+static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size)
 {
+       struct net_device *dev = idev->dev;
        struct net *net = dev_net(dev);
        struct sock *sk = net->ipv6.igmp_sk;
        struct sk_buff *skb;
@@ -1369,7 +1378,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
        skb_reserve(skb, hlen);
 
-       if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
+       if (__ipv6_get_lladdr(idev, &addr_buf, IFA_F_TENTATIVE)) {
                /* <draft-ietf-magma-mld-source-05.txt>:
                 * use unspecified address as the source address
                 * when a valid link-local address is not available.
@@ -1409,8 +1418,9 @@ static void mld_sendpack(struct sk_buff *skb)
        idev = __in6_dev_get(skb->dev);
        IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
 
-       payload_len = (skb->tail - skb->network_header) - sizeof(*pip6);
-       mldlen = skb->tail - skb->transport_header;
+       payload_len = (skb_tail_pointer(skb) - skb_network_header(skb)) -
+               sizeof(*pip6);
+       mldlen = skb_tail_pointer(skb) - skb_transport_header(skb);
        pip6->payload_len = htons(payload_len);
 
        pmr->mld2r_cksum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,
@@ -1465,7 +1475,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
        struct mld2_grec *pgr;
 
        if (!skb)
-               skb = mld_newpack(dev, dev->mtu);
+               skb = mld_newpack(pmc->idev, dev->mtu);
        if (!skb)
                return NULL;
        pgr = (struct mld2_grec *)skb_put(skb, sizeof(struct mld2_grec));
@@ -1485,7 +1495,8 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
 static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
        int type, int gdeleted, int sdeleted)
 {
-       struct net_device *dev = pmc->idev->dev;
+       struct inet6_dev *idev = pmc->idev;
+       struct net_device *dev = idev->dev;
        struct mld2_report *pmr;
        struct mld2_grec *pgr = NULL;
        struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list;
@@ -1514,7 +1525,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
                    AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {
                        if (skb)
                                mld_sendpack(skb);
-                       skb = mld_newpack(dev, dev->mtu);
+                       skb = mld_newpack(idev, dev->mtu);
                }
        }
        first = 1;
@@ -1541,7 +1552,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
                                pgr->grec_nsrcs = htons(scount);
                        if (skb)
                                mld_sendpack(skb);
-                       skb = mld_newpack(dev, dev->mtu);
+                       skb = mld_newpack(idev, dev->mtu);
                        first = 1;
                        scount = 0;
                }
@@ -1596,8 +1607,8 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc)
        struct sk_buff *skb = NULL;
        int type;
 
+       read_lock_bh(&idev->lock);
        if (!pmc) {
-               read_lock_bh(&idev->lock);
                for (pmc=idev->mc_list; pmc; pmc=pmc->next) {
                        if (pmc->mca_flags & MAF_NOREPORT)
                                continue;
@@ -1609,7 +1620,6 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc)
                        skb = add_grec(skb, pmc, type, 0, 0);
                        spin_unlock_bh(&pmc->mca_lock);
                }
-               read_unlock_bh(&idev->lock);
        } else {
                spin_lock_bh(&pmc->mca_lock);
                if (pmc->mca_sfcount[MCAST_EXCLUDE])
@@ -1619,6 +1629,7 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc)
                skb = add_grec(skb, pmc, type, 0, 0);
                spin_unlock_bh(&pmc->mca_lock);
        }
+       read_unlock_bh(&idev->lock);
        if (skb)
                mld_sendpack(skb);
 }
@@ -1814,6 +1825,46 @@ err_out:
        goto out;
 }
 
+static void mld_resend_report(struct inet6_dev *idev)
+{
+       if (MLD_V1_SEEN(idev)) {
+               struct ifmcaddr6 *mcaddr;
+               read_lock_bh(&idev->lock);
+               for (mcaddr = idev->mc_list; mcaddr; mcaddr = mcaddr->next) {
+                       if (!(mcaddr->mca_flags & MAF_NOREPORT))
+                               igmp6_send(&mcaddr->mca_addr, idev->dev,
+                                          ICMPV6_MGM_REPORT);
+               }
+               read_unlock_bh(&idev->lock);
+       } else {
+               mld_send_report(idev, NULL);
+       }
+}
+
+void ipv6_mc_dad_complete(struct inet6_dev *idev)
+{
+       idev->mc_dad_count = idev->mc_qrv;
+       if (idev->mc_dad_count) {
+               mld_resend_report(idev);
+               idev->mc_dad_count--;
+               if (idev->mc_dad_count)
+                       mld_dad_start_timer(idev, idev->mc_maxdelay);
+       }
+}
+
+static void mld_dad_timer_expire(unsigned long data)
+{
+       struct inet6_dev *idev = (struct inet6_dev *)data;
+
+       mld_resend_report(idev);
+       if (idev->mc_dad_count) {
+               idev->mc_dad_count--;
+               if (idev->mc_dad_count)
+                       mld_dad_start_timer(idev, idev->mc_maxdelay);
+       }
+       __in6_dev_put(idev);
+}
+
 static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
        const struct in6_addr *psfsrc)
 {
@@ -2231,6 +2282,8 @@ void ipv6_mc_down(struct inet6_dev *idev)
        idev->mc_gq_running = 0;
        if (del_timer(&idev->mc_gq_timer))
                __in6_dev_put(idev);
+       if (del_timer(&idev->mc_dad_timer))
+               __in6_dev_put(idev);
 
        for (i = idev->mc_list; i; i=i->next)
                igmp6_group_dropped(i);
@@ -2267,6 +2320,8 @@ void ipv6_mc_init_dev(struct inet6_dev *idev)
        idev->mc_ifc_count = 0;
        setup_timer(&idev->mc_ifc_timer, mld_ifc_timer_expire,
                        (unsigned long)idev);
+       setup_timer(&idev->mc_dad_timer, mld_dad_timer_expire,
+                   (unsigned long)idev);
        idev->mc_qrv = MLD_QRV_DEFAULT;
        idev->mc_maxdelay = IGMP6_UNSOLICITED_IVAL;
        idev->mc_v1_seen = 0;
index 0f9bdc5..9ac01dc 100644 (file)
@@ -268,7 +268,8 @@ static int mip6_destopt_offset(struct xfrm_state *x, struct sk_buff *skb,
        struct ipv6_opt_hdr *exthdr =
                                   (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
        const unsigned char *nh = skb_network_header(skb);
-       unsigned int packet_len = skb->tail - skb->network_header;
+       unsigned int packet_len = skb_tail_pointer(skb) -
+               skb_network_header(skb);
        int found_rhdr = 0;
 
        *nexthdr = &ipv6_hdr(skb)->nexthdr;
@@ -404,7 +405,8 @@ static int mip6_rthdr_offset(struct xfrm_state *x, struct sk_buff *skb,
        struct ipv6_opt_hdr *exthdr =
                                   (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
        const unsigned char *nh = skb_network_header(skb);
-       unsigned int packet_len = skb->tail - skb->network_header;
+       unsigned int packet_len = skb_tail_pointer(skb) -
+               skb_network_header(skb);
        int found_rhdr = 0;
 
        *nexthdr = &ipv6_hdr(skb)->nexthdr;
index ca4ffcc..b3b5730 100644 (file)
@@ -693,7 +693,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
        const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
        const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
        u8 *lladdr = NULL;
-       u32 ndoptlen = skb->tail - (skb->transport_header +
+       u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
                                    offsetof(struct nd_msg, opt));
        struct ndisc_options ndopts;
        struct net_device *dev = skb->dev;
@@ -853,7 +853,7 @@ static void ndisc_recv_na(struct sk_buff *skb)
        const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
        const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
        u8 *lladdr = NULL;
-       u32 ndoptlen = skb->tail - (skb->transport_header +
+       u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
                                    offsetof(struct nd_msg, opt));
        struct ndisc_options ndopts;
        struct net_device *dev = skb->dev;
@@ -1069,7 +1069,8 @@ static void ndisc_router_discovery(struct sk_buff *skb)
 
        __u8 * opt = (__u8 *)(ra_msg + 1);
 
-       optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
+       optlen = (skb_tail_pointer(skb) - skb_transport_header(skb)) -
+               sizeof(struct ra_msg);
 
        if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
                ND_PRINTK(2, warn, "RA: source address is not link-local\n");
@@ -1346,7 +1347,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
        u8 *hdr;
        struct ndisc_options ndopts;
        struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
-       u32 ndoptlen = skb->tail - (skb->transport_header +
+       u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
                                    offsetof(struct rd_msg, opt));
 
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
@@ -1568,7 +1569,7 @@ int ndisc_rcv(struct sk_buff *skb)
 
 static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net *net = dev_net(dev);
        struct inet6_dev *idev;
 
index 60e9053..47bff61 100644 (file)
@@ -71,7 +71,7 @@ static int device_cmp(struct nf_conn *ct, void *ifindex)
 static int masq_device_event(struct notifier_block *this,
                             unsigned long event, void *ptr)
 {
-       const struct net_device *dev = ptr;
+       const struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net *net = dev_net(dev);
 
        if (event == NETDEV_DOWN)
@@ -89,8 +89,10 @@ static int masq_inet_event(struct notifier_block *this,
                           unsigned long event, void *ptr)
 {
        struct inet6_ifaddr *ifa = ptr;
+       struct netdev_notifier_info info;
 
-       return masq_device_event(this, event, ifa->idev->dev);
+       netdev_notifier_info_init(&info, ifa->idev->dev);
+       return masq_device_event(this, event, &info);
 }
 
 static struct notifier_block masq_inet_notifier = {
index c2e73e6..ab92a36 100644 (file)
@@ -40,7 +40,8 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
        u16 offset = sizeof(struct ipv6hdr);
        struct ipv6_opt_hdr *exthdr =
                                (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
-       unsigned int packet_len = skb->tail - skb->network_header;
+       unsigned int packet_len = skb_tail_pointer(skb) -
+               skb_network_header(skb);
        int found_rhdr = 0;
        *nexthdr = &ipv6_hdr(skb)->nexthdr;
 
diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
new file mode 100644 (file)
index 0000000..18f19df
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * INET                An implementation of the TCP/IP protocol suite for the LINUX
+ *             operating system.  INET is implemented using the  BSD Socket
+ *             interface as the means of communication with the user level.
+ *
+ *             "Ping" sockets
+ *
+ *             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.
+ *
+ * Based on ipv4/ping.c code.
+ *
+ * Authors:    Lorenzo Colitti (IPv6 support)
+ *             Vasiliy Kulikov / Openwall (IPv4 implementation, for Linux 2.6),
+ *             Pavel Kankovsky (IPv4 implementation, for Linux 2.4.32)
+ *
+ */
+
+#include <net/addrconf.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/protocol.h>
+#include <net/udp.h>
+#include <net/transp_v6.h>
+#include <net/ping.h>
+
+struct proto pingv6_prot = {
+       .name =         "PINGv6",
+       .owner =        THIS_MODULE,
+       .init =         ping_init_sock,
+       .close =        ping_close,
+       .connect =      ip6_datagram_connect,
+       .disconnect =   udp_disconnect,
+       .setsockopt =   ipv6_setsockopt,
+       .getsockopt =   ipv6_getsockopt,
+       .sendmsg =      ping_v6_sendmsg,
+       .recvmsg =      ping_recvmsg,
+       .bind =         ping_bind,
+       .backlog_rcv =  ping_queue_rcv_skb,
+       .hash =         ping_hash,
+       .unhash =       ping_unhash,
+       .get_port =     ping_get_port,
+       .obj_size =     sizeof(struct raw6_sock),
+};
+EXPORT_SYMBOL_GPL(pingv6_prot);
+
+static struct inet_protosw pingv6_protosw = {
+       .type =      SOCK_DGRAM,
+       .protocol =  IPPROTO_ICMPV6,
+       .prot =      &pingv6_prot,
+       .ops =       &inet6_dgram_ops,
+       .no_check =  UDP_CSUM_DEFAULT,
+       .flags =     INET_PROTOSW_REUSE,
+};
+
+
+/* Compatibility glue so we can support IPv6 when it's compiled as a module */
+static int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
+{
+       return -EAFNOSUPPORT;
+}
+static int dummy_ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg,
+                                      struct sk_buff *skb)
+{
+       return -EAFNOSUPPORT;
+}
+static int dummy_icmpv6_err_convert(u8 type, u8 code, int *err)
+{
+       return -EAFNOSUPPORT;
+}
+static void dummy_ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
+                                 __be16 port, u32 info, u8 *payload) {}
+static int dummy_ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
+                              const struct net_device *dev, int strict)
+{
+       return 0;
+}
+
+int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+                   size_t len)
+{
+       struct inet_sock *inet = inet_sk(sk);
+       struct ipv6_pinfo *np = inet6_sk(sk);
+       struct icmp6hdr user_icmph;
+       int addr_type;
+       struct in6_addr *daddr;
+       int iif = 0;
+       struct flowi6 fl6;
+       int err;
+       int hlimit;
+       struct dst_entry *dst;
+       struct rt6_info *rt;
+       struct pingfakehdr pfh;
+
+       pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num);
+
+       err = ping_common_sendmsg(AF_INET6, msg, len, &user_icmph,
+                                 sizeof(user_icmph));
+       if (err)
+               return err;
+
+       if (msg->msg_name) {
+               struct sockaddr_in6 *u = (struct sockaddr_in6 *) msg->msg_name;
+               if (msg->msg_namelen < sizeof(struct sockaddr_in6) ||
+                   u->sin6_family != AF_INET6) {
+                       return -EINVAL;
+               }
+               if (sk->sk_bound_dev_if &&
+                   sk->sk_bound_dev_if != u->sin6_scope_id) {
+                       return -EINVAL;
+               }
+               daddr = &(u->sin6_addr);
+               iif = u->sin6_scope_id;
+       } else {
+               if (sk->sk_state != TCP_ESTABLISHED)
+                       return -EDESTADDRREQ;
+               daddr = &np->daddr;
+       }
+
+       if (!iif)
+               iif = sk->sk_bound_dev_if;
+
+       addr_type = ipv6_addr_type(daddr);
+       if (__ipv6_addr_needs_scope_id(addr_type) && !iif)
+               return -EINVAL;
+       if (addr_type & IPV6_ADDR_MAPPED)
+               return -EINVAL;
+
+       /* TODO: use ip6_datagram_send_ctl to get options from cmsg */
+
+       memset(&fl6, 0, sizeof(fl6));
+
+       fl6.flowi6_proto = IPPROTO_ICMPV6;
+       fl6.saddr = np->saddr;
+       fl6.daddr = *daddr;
+       fl6.fl6_icmp_type = user_icmph.icmp6_type;
+       fl6.fl6_icmp_code = user_icmph.icmp6_code;
+       security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
+
+       if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
+               fl6.flowi6_oif = np->mcast_oif;
+       else if (!fl6.flowi6_oif)
+               fl6.flowi6_oif = np->ucast_oif;
+
+       dst = ip6_sk_dst_lookup_flow(sk, &fl6,  daddr, 1);
+       if (IS_ERR(dst))
+               return PTR_ERR(dst);
+       rt = (struct rt6_info *) dst;
+
+       np = inet6_sk(sk);
+       if (!np)
+               return -EBADF;
+
+       if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
+               fl6.flowi6_oif = np->mcast_oif;
+       else if (!fl6.flowi6_oif)
+               fl6.flowi6_oif = np->ucast_oif;
+
+       pfh.icmph.type = user_icmph.icmp6_type;
+       pfh.icmph.code = user_icmph.icmp6_code;
+       pfh.icmph.checksum = 0;
+       pfh.icmph.un.echo.id = inet->inet_sport;
+       pfh.icmph.un.echo.sequence = user_icmph.icmp6_sequence;
+       pfh.iov = msg->msg_iov;
+       pfh.wcheck = 0;
+       pfh.family = AF_INET6;
+
+       if (ipv6_addr_is_multicast(&fl6.daddr))
+               hlimit = np->mcast_hops;
+       else
+               hlimit = np->hop_limit;
+       if (hlimit < 0)
+               hlimit = ip6_dst_hoplimit(dst);
+
+       lock_sock(sk);
+       err = ip6_append_data(sk, ping_getfrag, &pfh, len,
+                             0, hlimit,
+                             np->tclass, NULL, &fl6, rt,
+                             MSG_DONTWAIT, np->dontfrag);
+
+       if (err) {
+               ICMP6_INC_STATS_BH(sock_net(sk), rt->rt6i_idev,
+                                  ICMP6_MIB_OUTERRORS);
+               ip6_flush_pending_frames(sk);
+       } else {
+               err = icmpv6_push_pending_frames(sk, &fl6,
+                                                (struct icmp6hdr *) &pfh.icmph,
+                                                len);
+       }
+       release_sock(sk);
+
+       if (err)
+               return err;
+
+       return len;
+}
+
+#ifdef CONFIG_PROC_FS
+static void *ping_v6_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       return ping_seq_start(seq, pos, AF_INET6);
+}
+
+static int ping_v6_seq_show(struct seq_file *seq, void *v)
+{
+       if (v == SEQ_START_TOKEN) {
+               seq_puts(seq, IPV6_SEQ_DGRAM_HEADER);
+       } else {
+               int bucket = ((struct ping_iter_state *) seq->private)->bucket;
+               struct inet_sock *inet = inet_sk(v);
+               __u16 srcp = ntohs(inet->inet_sport);
+               __u16 destp = ntohs(inet->inet_dport);
+               ip6_dgram_sock_seq_show(seq, v, srcp, destp, bucket);
+       }
+       return 0;
+}
+
+static struct ping_seq_afinfo ping_v6_seq_afinfo = {
+       .name           = "icmp6",
+       .family         = AF_INET6,
+       .seq_fops       = &ping_seq_fops,
+       .seq_ops        = {
+               .start          = ping_v6_seq_start,
+               .show           = ping_v6_seq_show,
+               .next           = ping_seq_next,
+               .stop           = ping_seq_stop,
+       },
+};
+
+static int __net_init ping_v6_proc_init_net(struct net *net)
+{
+       return ping_proc_register(net, &ping_v6_seq_afinfo);
+}
+
+static void __net_init ping_v6_proc_exit_net(struct net *net)
+{
+       return ping_proc_unregister(net, &ping_v6_seq_afinfo);
+}
+
+static struct pernet_operations ping_v6_net_ops = {
+       .init = ping_v6_proc_init_net,
+       .exit = ping_v6_proc_exit_net,
+};
+#endif
+
+int __init pingv6_init(void)
+{
+#ifdef CONFIG_PROC_FS
+       int ret = register_pernet_subsys(&ping_v6_net_ops);
+       if (ret)
+               return ret;
+#endif
+       pingv6_ops.ipv6_recv_error = ipv6_recv_error;
+       pingv6_ops.ip6_datagram_recv_ctl = ip6_datagram_recv_ctl;
+       pingv6_ops.icmpv6_err_convert = icmpv6_err_convert;
+       pingv6_ops.ipv6_icmp_error = ipv6_icmp_error;
+       pingv6_ops.ipv6_chk_addr = ipv6_chk_addr;
+       return inet6_register_protosw(&pingv6_protosw);
+}
+
+/* This never gets called because it's not possible to unload the ipv6 module,
+ * but just in case.
+ */
+void pingv6_exit(void)
+{
+       pingv6_ops.ipv6_recv_error = dummy_ipv6_recv_error;
+       pingv6_ops.ip6_datagram_recv_ctl = dummy_ip6_datagram_recv_ctl;
+       pingv6_ops.icmpv6_err_convert = dummy_icmpv6_err_convert;
+       pingv6_ops.ipv6_icmp_error = dummy_ipv6_icmp_error;
+       pingv6_ops.ipv6_chk_addr = dummy_ipv6_chk_addr;
+#ifdef CONFIG_PROC_FS
+       unregister_pernet_subsys(&ping_v6_net_ops);
+#endif
+       inet6_unregister_protosw(&pingv6_protosw);
+}
index eedff8c..c45f7a5 100644 (file)
@@ -1132,7 +1132,8 @@ static int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg)
                spin_lock_bh(&sk->sk_receive_queue.lock);
                skb = skb_peek(&sk->sk_receive_queue);
                if (skb != NULL)
-                       amount = skb->tail - skb->transport_header;
+                       amount = skb_tail_pointer(skb) -
+                               skb_transport_header(skb);
                spin_unlock_bh(&sk->sk_receive_queue.lock);
                return put_user(amount, (int __user *)arg);
        }
@@ -1226,45 +1227,16 @@ struct proto rawv6_prot = {
 };
 
 #ifdef CONFIG_PROC_FS
-static void raw6_sock_seq_show(struct seq_file *seq, struct sock *sp, int i)
-{
-       struct ipv6_pinfo *np = inet6_sk(sp);
-       const struct in6_addr *dest, *src;
-       __u16 destp, srcp;
-
-       dest  = &np->daddr;
-       src   = &np->rcv_saddr;
-       destp = 0;
-       srcp  = inet_sk(sp)->inet_num;
-       seq_printf(seq,
-                  "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
-                  "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %pK %d\n",
-                  i,
-                  src->s6_addr32[0], src->s6_addr32[1],
-                  src->s6_addr32[2], src->s6_addr32[3], srcp,
-                  dest->s6_addr32[0], dest->s6_addr32[1],
-                  dest->s6_addr32[2], dest->s6_addr32[3], destp,
-                  sp->sk_state,
-                  sk_wmem_alloc_get(sp),
-                  sk_rmem_alloc_get(sp),
-                  0, 0L, 0,
-                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
-                  0,
-                  sock_i_ino(sp),
-                  atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));
-}
-
 static int raw6_seq_show(struct seq_file *seq, void *v)
 {
-       if (v == SEQ_START_TOKEN)
-               seq_printf(seq,
-                          "  sl  "
-                          "local_address                         "
-                          "remote_address                        "
-                          "st tx_queue rx_queue tr tm->when retrnsmt"
-                          "   uid  timeout inode ref pointer drops\n");
-       else
-               raw6_sock_seq_show(seq, v, raw_seq_private(seq)->bucket);
+       if (v == SEQ_START_TOKEN) {
+               seq_puts(seq, IPV6_SEQ_DGRAM_HEADER);
+       } else {
+               struct sock *sp = v;
+               __u16 srcp  = inet_sk(sp)->inet_num;
+               ip6_dgram_sock_seq_show(seq, v, srcp, 0,
+                                       raw_seq_private(seq)->bucket);
+       }
        return 0;
 }
 
index ad0aa6b..bd5fd70 100644 (file)
@@ -83,6 +83,7 @@ static void           ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
                                           struct sk_buff *skb, u32 mtu);
 static void            rt6_do_redirect(struct dst_entry *dst, struct sock *sk,
                                        struct sk_buff *skb);
+static int rt6_score_route(struct rt6_info *rt, int oif, int strict);
 
 #ifdef CONFIG_IPV6_ROUTE_INFO
 static struct rt6_info *rt6_add_route_info(struct net *net,
@@ -394,7 +395,8 @@ static int rt6_info_hash_nhsfn(unsigned int candidate_count,
 }
 
 static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
-                                            struct flowi6 *fl6)
+                                            struct flowi6 *fl6, int oif,
+                                            int strict)
 {
        struct rt6_info *sibling, *next_sibling;
        int route_choosen;
@@ -408,6 +410,8 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
                                &match->rt6i_siblings, rt6i_siblings) {
                        route_choosen--;
                        if (route_choosen == 0) {
+                               if (rt6_score_route(sibling, oif, strict) < 0)
+                                       break;
                                match = sibling;
                                break;
                        }
@@ -547,6 +551,8 @@ static inline bool rt6_check_neigh(struct rt6_info *rt)
                        ret = true;
 #endif
                read_unlock(&neigh->lock);
+       } else if (IS_ENABLED(CONFIG_IPV6_ROUTER_PREF)) {
+               ret = true;
        }
        rcu_read_unlock_bh();
 
@@ -743,7 +749,7 @@ restart:
        rt = fn->leaf;
        rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
        if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
-               rt = rt6_multipath_select(rt, fl6);
+               rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
        BACKTRACK(net, &fl6->saddr);
 out:
        dst_use(&rt->dst, jiffies);
@@ -875,8 +881,8 @@ restart_2:
 
 restart:
        rt = rt6_select(fn, oif, strict | reachable);
-       if (rt->rt6i_nsiblings && oif == 0)
-               rt = rt6_multipath_select(rt, fl6);
+       if (rt->rt6i_nsiblings)
+               rt = rt6_multipath_select(rt, fl6, oif, strict | reachable);
        BACKTRACK(net, &fl6->saddr);
        if (rt == net->ipv6.ip6_null_entry ||
            rt->rt6i_flags & RTF_CACHE)
@@ -1649,7 +1655,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
        int optlen, on_link;
        u8 *lladdr;
 
-       optlen = skb->tail - skb->transport_header;
+       optlen = skb_tail_pointer(skb) - skb_transport_header(skb);
        optlen -= sizeof(*msg);
 
        if (optlen < 0) {
@@ -2681,9 +2687,9 @@ errout:
 }
 
 static int ip6_route_dev_notify(struct notifier_block *this,
-                               unsigned long event, void *data)
+                               unsigned long event, void *ptr)
 {
-       struct net_device *dev = (struct net_device *)data;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net *net = dev_net(dev);
 
        if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) {
@@ -2790,7 +2796,7 @@ static const struct file_operations rt6_stats_seq_fops = {
 #ifdef CONFIG_SYSCTL
 
 static
-int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
+int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
                              void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net;
@@ -2805,7 +2811,7 @@ int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
        return 0;
 }
 
-ctl_table ipv6_route_table_template[] = {
+struct ctl_table ipv6_route_table_template[] = {
        {
                .procname       =       "flush",
                .data           =       &init_net.ipv6.sysctl.flush_delay,
index 3353634..a3437a4 100644 (file)
@@ -466,14 +466,14 @@ isatap_chksrc(struct sk_buff *skb, const struct iphdr *iph, struct ip_tunnel *t)
 
 static void ipip6_tunnel_uninit(struct net_device *dev)
 {
-       struct net *net = dev_net(dev);
-       struct sit_net *sitn = net_generic(net, sit_net_id);
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       struct sit_net *sitn = net_generic(tunnel->net, sit_net_id);
 
        if (dev == sitn->fb_tunnel_dev) {
                RCU_INIT_POINTER(sitn->tunnels_wc[0], NULL);
        } else {
-               ipip6_tunnel_unlink(sitn, netdev_priv(dev));
-               ipip6_tunnel_del_prl(netdev_priv(dev), NULL);
+               ipip6_tunnel_unlink(sitn, tunnel);
+               ipip6_tunnel_del_prl(tunnel, NULL);
        }
        dev_put(dev);
 }
@@ -577,6 +577,10 @@ static int ipip6_rcv(struct sk_buff *skb)
        if (tunnel != NULL) {
                struct pcpu_tstats *tstats;
 
+               if (tunnel->parms.iph.protocol != IPPROTO_IPV6 &&
+                   tunnel->parms.iph.protocol != 0)
+                       goto out;
+
                secpath_reset(skb);
                skb->mac_header = skb->network_header;
                skb_reset_network_header(skb);
@@ -589,7 +593,7 @@ static int ipip6_rcv(struct sk_buff *skb)
                                tunnel->dev->stats.rx_errors++;
                                goto out;
                        }
-               } else {
+               } else if (!(tunnel->dev->flags&IFF_POINTOPOINT)) {
                        if (is_spoofed_6rd(tunnel, iph->saddr,
                                           &ipv6_hdr(skb)->saddr) ||
                            is_spoofed_6rd(tunnel, iph->daddr,
@@ -617,6 +621,8 @@ static int ipip6_rcv(struct sk_buff *skb)
                tstats->rx_packets++;
                tstats->rx_bytes += skb->len;
 
+               if (tunnel->net != dev_net(tunnel->dev))
+                       skb_scrub_packet(skb);
                netif_rx(skb);
 
                return 0;
@@ -629,6 +635,40 @@ out:
        return 0;
 }
 
+static const struct tnl_ptk_info tpi = {
+       /* no tunnel info required for ipip. */
+       .proto = htons(ETH_P_IP),
+};
+
+static int ipip_rcv(struct sk_buff *skb)
+{
+       const struct iphdr *iph;
+       struct ip_tunnel *tunnel;
+
+       if (iptunnel_pull_header(skb, 0, tpi.proto))
+               goto drop;
+
+       iph = ip_hdr(skb);
+
+       tunnel = ipip6_tunnel_lookup(dev_net(skb->dev), skb->dev,
+                                    iph->saddr, iph->daddr);
+       if (tunnel != NULL) {
+               if (tunnel->parms.iph.protocol != IPPROTO_IPIP &&
+                   tunnel->parms.iph.protocol != 0)
+                       goto drop;
+
+               if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
+                       goto drop;
+               return ip_tunnel_rcv(tunnel, skb, &tpi, log_ecn_error);
+       }
+
+       return 1;
+
+drop:
+       kfree_skb(skb);
+       return 0;
+}
+
 /*
  * If the IPv6 address comes from 6rd / 6to4 (RFC 3056) addr space this function
  * stores the embedded IPv4 address in v4dst and returns true.
@@ -690,13 +730,14 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
        __be16 df = tiph->frag_off;
        struct rtable *rt;                      /* Route to the other host */
        struct net_device *tdev;                /* Device to other host */
-       struct iphdr  *iph;                     /* Our new IP header */
        unsigned int max_headroom;              /* The extra header space needed */
        __be32 dst = tiph->daddr;
        struct flowi4 fl4;
        int    mtu;
        const struct in6_addr *addr6;
        int addr_type;
+       u8 ttl;
+       int err;
 
        if (skb->protocol != htons(ETH_P_IPV6))
                goto tx_error;
@@ -764,7 +805,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                        goto tx_error;
        }
 
-       rt = ip_route_output_ports(dev_net(dev), &fl4, NULL,
+       rt = ip_route_output_ports(tunnel->net, &fl4, NULL,
                                   dst, tiph->saddr,
                                   0, 0,
                                   IPPROTO_IPV6, RT_TOS(tos),
@@ -819,6 +860,9 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                        tunnel->err_count = 0;
        }
 
+       if (tunnel->net != dev_net(dev))
+               skb_scrub_packet(skb);
+
        /*
         * Okay, now see if we can stuff it in the buffer as-is.
         */
@@ -839,34 +883,14 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                skb = new_skb;
                iph6 = ipv6_hdr(skb);
        }
-
-       skb->transport_header = skb->network_header;
-       skb_push(skb, sizeof(struct iphdr));
-       skb_reset_network_header(skb);
-       memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
-       IPCB(skb)->flags = 0;
-       skb_dst_drop(skb);
-       skb_dst_set(skb, &rt->dst);
-
-       /*
-        *      Push down and install the IPIP header.
-        */
-
-       iph                     =       ip_hdr(skb);
-       iph->version            =       4;
-       iph->ihl                =       sizeof(struct iphdr)>>2;
-       iph->frag_off           =       df;
-       iph->protocol           =       IPPROTO_IPV6;
-       iph->tos                =       INET_ECN_encapsulate(tos, ipv6_get_dsfield(iph6));
-       iph->daddr              =       fl4.daddr;
-       iph->saddr              =       fl4.saddr;
-
-       if ((iph->ttl = tiph->ttl) == 0)
-               iph->ttl        =       iph6->hop_limit;
-
-       skb->ip_summed = CHECKSUM_NONE;
-       ip_select_ident(iph, skb_dst(skb), NULL);
-       iptunnel_xmit(skb, dev);
+       ttl = tiph->ttl;
+       if (ttl == 0)
+               ttl = iph6->hop_limit;
+       tos = INET_ECN_encapsulate(tos, ipv6_get_dsfield(iph6));
+
+       err = iptunnel_xmit(dev_net(dev), rt, skb, fl4.saddr, fl4.daddr,
+                           IPPROTO_IPV6, tos, ttl, df);
+       iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
        return NETDEV_TX_OK;
 
 tx_error_icmp:
@@ -877,6 +901,43 @@ tx_error:
        return NETDEV_TX_OK;
 }
 
+static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       const struct iphdr  *tiph = &tunnel->parms.iph;
+
+       if (likely(!skb->encapsulation)) {
+               skb_reset_inner_headers(skb);
+               skb->encapsulation = 1;
+       }
+
+       ip_tunnel_xmit(skb, dev, tiph, IPPROTO_IPIP);
+       return NETDEV_TX_OK;
+}
+
+static netdev_tx_t sit_tunnel_xmit(struct sk_buff *skb,
+                                  struct net_device *dev)
+{
+       switch (skb->protocol) {
+       case htons(ETH_P_IP):
+               ipip_tunnel_xmit(skb, dev);
+               break;
+       case htons(ETH_P_IPV6):
+               ipip6_tunnel_xmit(skb, dev);
+               break;
+       default:
+               goto tx_err;
+       }
+
+       return NETDEV_TX_OK;
+
+tx_err:
+       dev->stats.tx_errors++;
+       dev_kfree_skb(skb);
+       return NETDEV_TX_OK;
+
+}
+
 static void ipip6_tunnel_bind_dev(struct net_device *dev)
 {
        struct net_device *tdev = NULL;
@@ -888,7 +949,8 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)
        iph = &tunnel->parms.iph;
 
        if (iph->daddr) {
-               struct rtable *rt = ip_route_output_ports(dev_net(dev), &fl4, NULL,
+               struct rtable *rt = ip_route_output_ports(tunnel->net, &fl4,
+                                                         NULL,
                                                          iph->daddr, iph->saddr,
                                                          0, 0,
                                                          IPPROTO_IPV6,
@@ -903,7 +965,7 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)
        }
 
        if (!tdev && tunnel->parms.link)
-               tdev = __dev_get_by_index(dev_net(dev), tunnel->parms.link);
+               tdev = __dev_get_by_index(tunnel->net, tunnel->parms.link);
 
        if (tdev) {
                dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr);
@@ -916,7 +978,7 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)
 
 static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p)
 {
-       struct net *net = dev_net(t->dev);
+       struct net *net = t->net;
        struct sit_net *sitn = net_generic(net, sit_net_id);
 
        ipip6_tunnel_unlink(sitn, t);
@@ -1027,7 +1089,11 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                        goto done;
 
                err = -EINVAL;
-               if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPV6 ||
+               if (p.iph.protocol != IPPROTO_IPV6 &&
+                   p.iph.protocol != IPPROTO_IPIP &&
+                   p.iph.protocol != 0)
+                       goto done;
+               if (p.iph.version != 4 ||
                    p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
                        goto done;
                if (p.iph.ttl)
@@ -1164,7 +1230,7 @@ static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 
 static const struct net_device_ops ipip6_netdev_ops = {
        .ndo_uninit     = ipip6_tunnel_uninit,
-       .ndo_start_xmit = ipip6_tunnel_xmit,
+       .ndo_start_xmit = sit_tunnel_xmit,
        .ndo_do_ioctl   = ipip6_tunnel_ioctl,
        .ndo_change_mtu = ipip6_tunnel_change_mtu,
        .ndo_get_stats64 = ip_tunnel_get_stats64,
@@ -1188,7 +1254,6 @@ static void ipip6_tunnel_setup(struct net_device *dev)
        dev->priv_flags        &= ~IFF_XMIT_DST_RELEASE;
        dev->iflink             = 0;
        dev->addr_len           = 4;
-       dev->features           |= NETIF_F_NETNS_LOCAL;
        dev->features           |= NETIF_F_LLTX;
 }
 
@@ -1197,6 +1262,7 @@ static int ipip6_tunnel_init(struct net_device *dev)
        struct ip_tunnel *tunnel = netdev_priv(dev);
 
        tunnel->dev = dev;
+       tunnel->net = dev_net(dev);
 
        memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
        memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
@@ -1217,6 +1283,7 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
        struct sit_net *sitn = net_generic(net, sit_net_id);
 
        tunnel->dev = dev;
+       tunnel->net = dev_net(dev);
        strcpy(tunnel->parms.name, dev->name);
 
        iph->version            = 4;
@@ -1232,6 +1299,22 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
        return 0;
 }
 
+static int ipip6_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+       u8 proto;
+
+       if (!data || !data[IFLA_IPTUN_PROTO])
+               return 0;
+
+       proto = nla_get_u8(data[IFLA_IPTUN_PROTO]);
+       if (proto != IPPROTO_IPV6 &&
+           proto != IPPROTO_IPIP &&
+           proto != 0)
+               return -EINVAL;
+
+       return 0;
+}
+
 static void ipip6_netlink_parms(struct nlattr *data[],
                                struct ip_tunnel_parm *parms)
 {
@@ -1268,6 +1351,10 @@ static void ipip6_netlink_parms(struct nlattr *data[],
 
        if (data[IFLA_IPTUN_FLAGS])
                parms->i_flags = nla_get_be16(data[IFLA_IPTUN_FLAGS]);
+
+       if (data[IFLA_IPTUN_PROTO])
+               parms->iph.protocol = nla_get_u8(data[IFLA_IPTUN_PROTO]);
+
 }
 
 #ifdef CONFIG_IPV6_SIT_6RD
@@ -1339,9 +1426,9 @@ static int ipip6_newlink(struct net *src_net, struct net_device *dev,
 static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[],
                          struct nlattr *data[])
 {
-       struct ip_tunnel *t;
+       struct ip_tunnel *t = netdev_priv(dev);
        struct ip_tunnel_parm p;
-       struct net *net = dev_net(dev);
+       struct net *net = t->net;
        struct sit_net *sitn = net_generic(net, sit_net_id);
 #ifdef CONFIG_IPV6_SIT_6RD
        struct ip_tunnel_6rd ip6rd;
@@ -1391,6 +1478,8 @@ static size_t ipip6_get_size(const struct net_device *dev)
                nla_total_size(1) +
                /* IFLA_IPTUN_FLAGS */
                nla_total_size(2) +
+               /* IFLA_IPTUN_PROTO */
+               nla_total_size(1) +
 #ifdef CONFIG_IPV6_SIT_6RD
                /* IFLA_IPTUN_6RD_PREFIX */
                nla_total_size(sizeof(struct in6_addr)) +
@@ -1416,6 +1505,7 @@ static int ipip6_fill_info(struct sk_buff *skb, const struct net_device *dev)
            nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) ||
            nla_put_u8(skb, IFLA_IPTUN_PMTUDISC,
                       !!(parm->iph.frag_off & htons(IP_DF))) ||
+           nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->iph.protocol) ||
            nla_put_be16(skb, IFLA_IPTUN_FLAGS, parm->i_flags))
                goto nla_put_failure;
 
@@ -1445,6 +1535,7 @@ static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = {
        [IFLA_IPTUN_TOS]                = { .type = NLA_U8 },
        [IFLA_IPTUN_PMTUDISC]           = { .type = NLA_U8 },
        [IFLA_IPTUN_FLAGS]              = { .type = NLA_U16 },
+       [IFLA_IPTUN_PROTO]              = { .type = NLA_U8 },
 #ifdef CONFIG_IPV6_SIT_6RD
        [IFLA_IPTUN_6RD_PREFIX]         = { .len = sizeof(struct in6_addr) },
        [IFLA_IPTUN_6RD_RELAY_PREFIX]   = { .type = NLA_U32 },
@@ -1459,6 +1550,7 @@ static struct rtnl_link_ops sit_link_ops __read_mostly = {
        .policy         = ipip6_policy,
        .priv_size      = sizeof(struct ip_tunnel),
        .setup          = ipip6_tunnel_setup,
+       .validate       = ipip6_validate,
        .newlink        = ipip6_newlink,
        .changelink     = ipip6_changelink,
        .get_size       = ipip6_get_size,
@@ -1471,10 +1563,22 @@ static struct xfrm_tunnel sit_handler __read_mostly = {
        .priority       =       1,
 };
 
+static struct xfrm_tunnel ipip_handler __read_mostly = {
+       .handler        =       ipip_rcv,
+       .err_handler    =       ipip6_err,
+       .priority       =       2,
+};
+
 static void __net_exit sit_destroy_tunnels(struct sit_net *sitn, struct list_head *head)
 {
+       struct net *net = dev_net(sitn->fb_tunnel_dev);
+       struct net_device *dev, *aux;
        int prio;
 
+       for_each_netdev_safe(net, dev, aux)
+               if (dev->rtnl_link_ops == &sit_link_ops)
+                       unregister_netdevice_queue(dev, head);
+
        for (prio = 1; prio < 4; prio++) {
                int h;
                for (h = 0; h < HASH_SIZE; h++) {
@@ -1482,7 +1586,12 @@ static void __net_exit sit_destroy_tunnels(struct sit_net *sitn, struct list_hea
 
                        t = rtnl_dereference(sitn->tunnels[prio][h]);
                        while (t != NULL) {
-                               unregister_netdevice_queue(t->dev, head);
+                               /* If dev is in the same netns, it has already
+                                * been added to the list by the previous loop.
+                                */
+                               if (dev_net(t->dev) != net)
+                                       unregister_netdevice_queue(t->dev,
+                                                                  head);
                                t = rtnl_dereference(t->next);
                        }
                }
@@ -1507,6 +1616,10 @@ static int __net_init sit_init_net(struct net *net)
                goto err_alloc_dev;
        }
        dev_net_set(sitn->fb_tunnel_dev, net);
+       /* FB netdevice is special: we have one, and only one per netns.
+        * Allowing to move it to another netns is clearly unsafe.
+        */
+       sitn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
 
        err = ipip6_fb_tunnel_init(sitn->fb_tunnel_dev);
        if (err)
@@ -1553,6 +1666,7 @@ static void __exit sit_cleanup(void)
 {
        rtnl_link_unregister(&sit_link_ops);
        xfrm4_tunnel_deregister(&sit_handler, AF_INET6);
+       xfrm4_tunnel_deregister(&ipip_handler, AF_INET);
 
        unregister_pernet_device(&sit_net_ops);
        rcu_barrier(); /* Wait for completion of call_rcu()'s */
@@ -1569,9 +1683,14 @@ static int __init sit_init(void)
                return err;
        err = xfrm4_tunnel_register(&sit_handler, AF_INET6);
        if (err < 0) {
-               pr_info("%s: can't add protocol\n", __func__);
+               pr_info("%s: can't register ip6ip4\n", __func__);
                goto xfrm_tunnel_failed;
        }
+       err = xfrm4_tunnel_register(&ipip_handler, AF_INET);
+       if (err < 0) {
+               pr_info("%s: can't register ip4ip4\n", __func__);
+               goto xfrm_tunnel4_failed;
+       }
        err = rtnl_link_register(&sit_link_ops);
        if (err < 0)
                goto rtnl_link_failed;
@@ -1580,6 +1699,8 @@ out:
        return err;
 
 rtnl_link_failed:
+       xfrm4_tunnel_deregister(&ipip_handler, AF_INET);
+xfrm_tunnel4_failed:
        xfrm4_tunnel_deregister(&sit_handler, AF_INET6);
 xfrm_tunnel_failed:
        unregister_pernet_device(&sit_net_ops);
index e85c48b..107b2f1 100644 (file)
@@ -16,7 +16,7 @@
 #include <net/addrconf.h>
 #include <net/inet_frag.h>
 
-static ctl_table ipv6_table_template[] = {
+static struct ctl_table ipv6_table_template[] = {
        {
                .procname       = "bindv6only",
                .data           = &init_net.ipv6.sysctl.bindv6only,
@@ -27,7 +27,7 @@ static ctl_table ipv6_table_template[] = {
        { }
 };
 
-static ctl_table ipv6_rotable[] = {
+static struct ctl_table ipv6_rotable[] = {
        {
                .procname       = "mld_max_msf",
                .data           = &sysctl_mld_max_msf,
index 0a17ed9..5cffa5c 100644 (file)
@@ -63,6 +63,7 @@
 #include <net/inet_common.h>
 #include <net/secure_seq.h>
 #include <net/tcp_memcontrol.h>
+#include <net/ll_poll.h>
 
 #include <asm/uaccess.h>
 
@@ -1498,6 +1499,7 @@ process:
        if (sk_filter(sk, skb))
                goto discard_and_relse;
 
+       sk_mark_ll(sk, skb);
        skb->dev = NULL;
 
        bh_lock_sock_nested(sk);
index 42923b1..b6f3143 100644 (file)
@@ -46,6 +46,7 @@
 #include <net/ip6_checksum.h>
 #include <net/xfrm.h>
 #include <net/inet6_hashtables.h>
+#include <net/ll_poll.h>
 
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
@@ -841,7 +842,10 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
         */
        sk = __udp6_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
        if (sk != NULL) {
-               int ret = udpv6_queue_rcv_skb(sk, skb);
+               int ret;
+
+               sk_mark_ll(sk, skb);
+               ret = udpv6_queue_rcv_skb(sk, skb);
                sock_put(sk);
 
                /* a return value > 0 means to resubmit the input, but
@@ -955,11 +959,16 @@ static int udp_v6_push_pending_frames(struct sock *sk)
        struct udphdr *uh;
        struct udp_sock  *up = udp_sk(sk);
        struct inet_sock *inet = inet_sk(sk);
-       struct flowi6 *fl6 = &inet->cork.fl.u.ip6;
+       struct flowi6 *fl6;
        int err = 0;
        int is_udplite = IS_UDPLITE(sk);
        __wsum csum = 0;
 
+       if (up->pending == AF_INET)
+               return udp_push_pending_frames(sk);
+
+       fl6 = &inet->cork.fl.u.ip6;
+
        /* Grab the skbuff where UDP header space exists. */
        if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
                goto out;
@@ -1359,48 +1368,17 @@ static const struct inet6_protocol udpv6_protocol = {
 
 /* ------------------------------------------------------------------------ */
 #ifdef CONFIG_PROC_FS
-
-static void udp6_sock_seq_show(struct seq_file *seq, struct sock *sp, int bucket)
-{
-       struct inet_sock *inet = inet_sk(sp);
-       struct ipv6_pinfo *np = inet6_sk(sp);
-       const struct in6_addr *dest, *src;
-       __u16 destp, srcp;
-
-       dest  = &np->daddr;
-       src   = &np->rcv_saddr;
-       destp = ntohs(inet->inet_dport);
-       srcp  = ntohs(inet->inet_sport);
-       seq_printf(seq,
-                  "%5d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
-                  "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %pK %d\n",
-                  bucket,
-                  src->s6_addr32[0], src->s6_addr32[1],
-                  src->s6_addr32[2], src->s6_addr32[3], srcp,
-                  dest->s6_addr32[0], dest->s6_addr32[1],
-                  dest->s6_addr32[2], dest->s6_addr32[3], destp,
-                  sp->sk_state,
-                  sk_wmem_alloc_get(sp),
-                  sk_rmem_alloc_get(sp),
-                  0, 0L, 0,
-                  from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
-                  0,
-                  sock_i_ino(sp),
-                  atomic_read(&sp->sk_refcnt), sp,
-                  atomic_read(&sp->sk_drops));
-}
-
 int udp6_seq_show(struct seq_file *seq, void *v)
 {
-       if (v == SEQ_START_TOKEN)
-               seq_printf(seq,
-                          "  sl  "
-                          "local_address                         "
-                          "remote_address                        "
-                          "st tx_queue rx_queue tr tm->when retrnsmt"
-                          "   uid  timeout inode ref pointer drops\n");
-       else
-               udp6_sock_seq_show(seq, v, ((struct udp_iter_state *)seq->private)->bucket);
+       if (v == SEQ_START_TOKEN) {
+               seq_puts(seq, IPV6_SEQ_DGRAM_HEADER);
+       } else {
+               int bucket = ((struct udp_iter_state *)seq->private)->bucket;
+               struct inet_sock *inet = inet_sk(v);
+               __u16 srcp = ntohs(inet->inet_sport);
+               __u16 destp = ntohs(inet->inet_dport);
+               ip6_dgram_sock_seq_show(seq, v, srcp, destp, bucket);
+       }
        return 0;
 }
 
index d3cfaf9..5d1b8d7 100644 (file)
@@ -64,7 +64,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
                if (unlikely(type & ~(SKB_GSO_UDP |
                                      SKB_GSO_DODGY |
                                      SKB_GSO_UDP_TUNNEL |
-                                     SKB_GSO_GRE) ||
+                                     SKB_GSO_GRE |
+                                     SKB_GSO_MPLS) ||
                             !(type & (SKB_GSO_UDP))))
                        goto out;
 
index f547a47..7a1e0fc 100644 (file)
@@ -330,7 +330,7 @@ static __inline__ void __ipxitf_put(struct ipx_interface *intrfc)
 static int ipxitf_device_event(struct notifier_block *notifier,
                                unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct ipx_interface *i, *tmp;
 
        if (!net_eq(dev_net(dev), &init_net))
index de73f64..d6a5965 100644 (file)
@@ -73,7 +73,7 @@ static int min_lap_keepalive_time = 100;      /* 100us */
 /* For other sysctl, I've no idea of the range. Maybe Dag could help
  * us on that - Jean II */
 
-static int do_devname(ctl_table *table, int write,
+static int do_devname(struct ctl_table *table, int write,
                      void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
@@ -90,7 +90,7 @@ static int do_devname(ctl_table *table, int write,
 }
 
 
-static int do_discovery(ctl_table *table, int write,
+static int do_discovery(struct ctl_table *table, int write,
                     void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
@@ -111,7 +111,7 @@ static int do_discovery(ctl_table *table, int write,
 }
 
 /* One file */
-static ctl_table irda_table[] = {
+static struct ctl_table irda_table[] = {
        {
                .procname       = "discovery",
                .data           = &sysctl_discovery,
index ae69165..168aff5 100644 (file)
@@ -2293,7 +2293,7 @@ out_unlock:
 static int afiucv_netdev_event(struct notifier_block *this,
                               unsigned long event, void *ptr)
 {
-       struct net_device *event_dev = (struct net_device *)ptr;
+       struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
        struct sock *sk;
        struct iucv_sock *iucv;
 
index 6984c3a..feae495 100644 (file)
@@ -414,10 +414,7 @@ static void l2tp_recv_dequeue_skb(struct l2tp_session *session, struct sk_buff *
        if (L2TP_SKB_CB(skb)->has_seq) {
                /* Bump our Nr */
                session->nr++;
-               if (tunnel->version == L2TP_HDR_VER_2)
-                       session->nr &= 0xffff;
-               else
-                       session->nr &= 0xffffff;
+               session->nr &= session->nr_max;
 
                l2tp_dbg(session, L2TP_MSG_SEQ, "%s: updated nr to %hu\n",
                         session->name, session->nr);
@@ -542,6 +539,84 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk,
        return __skb_checksum_complete(skb);
 }
 
+static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr)
+{
+       u32 nws;
+
+       if (nr >= session->nr)
+               nws = nr - session->nr;
+       else
+               nws = (session->nr_max + 1) - (session->nr - nr);
+
+       return nws < session->nr_window_size;
+}
+
+/* If packet has sequence numbers, queue it if acceptable. Returns 0 if
+ * acceptable, else non-zero.
+ */
+static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb)
+{
+       if (!l2tp_seq_check_rx_window(session, L2TP_SKB_CB(skb)->ns)) {
+               /* Packet sequence number is outside allowed window.
+                * Discard it.
+                */
+               l2tp_dbg(session, L2TP_MSG_SEQ,
+                        "%s: pkt %u len %d discarded, outside window, nr=%u\n",
+                        session->name, L2TP_SKB_CB(skb)->ns,
+                        L2TP_SKB_CB(skb)->length, session->nr);
+               goto discard;
+       }
+
+       if (session->reorder_timeout != 0) {
+               /* Packet reordering enabled. Add skb to session's
+                * reorder queue, in order of ns.
+                */
+               l2tp_recv_queue_skb(session, skb);
+               goto out;
+       }
+
+       /* Packet reordering disabled. Discard out-of-sequence packets, while
+        * tracking the number if in-sequence packets after the first OOS packet
+        * is seen. After nr_oos_count_max in-sequence packets, reset the
+        * sequence number to re-enable packet reception.
+        */
+       if (L2TP_SKB_CB(skb)->ns == session->nr) {
+               skb_queue_tail(&session->reorder_q, skb);
+       } else {
+               u32 nr_oos = L2TP_SKB_CB(skb)->ns;
+               u32 nr_next = (session->nr_oos + 1) & session->nr_max;
+
+               if (nr_oos == nr_next)
+                       session->nr_oos_count++;
+               else
+                       session->nr_oos_count = 0;
+
+               session->nr_oos = nr_oos;
+               if (session->nr_oos_count > session->nr_oos_count_max) {
+                       session->reorder_skip = 1;
+                       l2tp_dbg(session, L2TP_MSG_SEQ,
+                                "%s: %d oos packets received. Resetting sequence numbers\n",
+                                session->name, session->nr_oos_count);
+               }
+               if (!session->reorder_skip) {
+                       atomic_long_inc(&session->stats.rx_seq_discards);
+                       l2tp_dbg(session, L2TP_MSG_SEQ,
+                                "%s: oos pkt %u len %d discarded, waiting for %u, reorder_q_len=%d\n",
+                                session->name, L2TP_SKB_CB(skb)->ns,
+                                L2TP_SKB_CB(skb)->length, session->nr,
+                                skb_queue_len(&session->reorder_q));
+                       goto discard;
+               }
+               skb_queue_tail(&session->reorder_q, skb);
+       }
+
+out:
+       return 0;
+
+discard:
+       return 1;
+}
+
 /* Do receive processing of L2TP data frames. We handle both L2TPv2
  * and L2TPv3 data frames here.
  *
@@ -757,26 +832,8 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
         * enabled. Saved L2TP protocol info is stored in skb->sb[].
         */
        if (L2TP_SKB_CB(skb)->has_seq) {
-               if (session->reorder_timeout != 0) {
-                       /* Packet reordering enabled. Add skb to session's
-                        * reorder queue, in order of ns.
-                        */
-                       l2tp_recv_queue_skb(session, skb);
-               } else {
-                       /* Packet reordering disabled. Discard out-of-sequence
-                        * packets
-                        */
-                       if (L2TP_SKB_CB(skb)->ns != session->nr) {
-                               atomic_long_inc(&session->stats.rx_seq_discards);
-                               l2tp_dbg(session, L2TP_MSG_SEQ,
-                                        "%s: oos pkt %u len %d discarded, waiting for %u, reorder_q_len=%d\n",
-                                        session->name, L2TP_SKB_CB(skb)->ns,
-                                        L2TP_SKB_CB(skb)->length, session->nr,
-                                        skb_queue_len(&session->reorder_q));
-                               goto discard;
-                       }
-                       skb_queue_tail(&session->reorder_q, skb);
-               }
+               if (l2tp_recv_data_seq(session, skb))
+                       goto discard;
        } else {
                /* No sequence numbers. Add the skb to the tail of the
                 * reorder queue. This ensures that it will be
@@ -1812,6 +1869,15 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn
                session->session_id = session_id;
                session->peer_session_id = peer_session_id;
                session->nr = 0;
+               if (tunnel->version == L2TP_HDR_VER_2)
+                       session->nr_max = 0xffff;
+               else
+                       session->nr_max = 0xffffff;
+               session->nr_window_size = session->nr_max / 2;
+               session->nr_oos_count_max = 4;
+
+               /* Use NR of first received packet */
+               session->reorder_skip = 1;
 
                sprintf(&session->name[0], "sess %u/%u",
                        tunnel->tunnel_id, session->session_id);
index 485a490..66a559b 100644 (file)
@@ -102,6 +102,11 @@ struct l2tp_session {
        u32                     nr;             /* session NR state (receive) */
        u32                     ns;             /* session NR state (send) */
        struct sk_buff_head     reorder_q;      /* receive reorder queue */
+       u32                     nr_max;         /* max NR. Depends on tunnel */
+       u32                     nr_window_size; /* NR window size */
+       u32                     nr_oos;         /* NR of last OOS packet */
+       int                     nr_oos_count;   /* For OOS recovery */
+       int                     nr_oos_count_max;
        struct hlist_node       hlist;          /* Hash list node */
        atomic_t                ref_count;
 
index 8dec687..5ebee2d 100644 (file)
@@ -1793,7 +1793,8 @@ static const struct proto_ops pppol2tp_ops = {
 
 static const struct pppox_proto pppol2tp_proto = {
        .create         = pppol2tp_create,
-       .ioctl          = pppol2tp_ioctl
+       .ioctl          = pppol2tp_ioctl,
+       .owner          = THIS_MODULE,
 };
 
 #ifdef CONFIG_L2TP_V3
index 0785e95..be7614b 100644 (file)
@@ -85,7 +85,7 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
                        *cpos++ = *pos++ ^ e[i];
        }
 
-       for (i = 0; i < CCMP_MIC_LEN; i++)
+       for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++)
                mic[i] = b[i] ^ s_0[i];
 }
 
@@ -123,7 +123,7 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
                crypto_cipher_encrypt_one(tfm, a, a);
        }
 
-       for (i = 0; i < CCMP_MIC_LEN; i++) {
+       for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) {
                if ((mic[i] ^ s_0[i]) != a[i])
                        return -1;
        }
@@ -138,7 +138,7 @@ struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
 
        tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
        if (!IS_ERR(tfm))
-               crypto_cipher_setkey(tfm, key, ALG_CCMP_KEY_LEN);
+               crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
 
        return tfm;
 }
index 4fdb306..8184d12 100644 (file)
@@ -73,16 +73,19 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
                struct ieee80211_local *local = sdata->local;
 
                if (ieee80211_sdata_running(sdata)) {
+                       u32 mask = MONITOR_FLAG_COOK_FRAMES |
+                                  MONITOR_FLAG_ACTIVE;
+
                        /*
-                        * Prohibit MONITOR_FLAG_COOK_FRAMES to be
-                        * changed while the interface is up.
+                        * Prohibit MONITOR_FLAG_COOK_FRAMES and
+                        * MONITOR_FLAG_ACTIVE to be changed while the
+                        * interface is up.
                         * Else we would need to add a lot of cruft
                         * to update everything:
                         *      cooked_mntrs, monitor and all fif_* counters
                         *      reconfigure hardware
                         */
-                       if ((*flags & MONITOR_FLAG_COOK_FRAMES) !=
-                           (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES))
+                       if ((*flags & mask) != (sdata->u.mntr_flags & mask))
                                return -EBUSY;
 
                        ieee80211_adjust_monitor_flags(sdata, -1);
@@ -444,7 +447,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
        struct ieee80211_local *local = sdata->local;
        struct timespec uptime;
        u64 packets = 0;
-       int ac;
+       int i, ac;
 
        sinfo->generation = sdata->local->sta_generation;
 
@@ -488,6 +491,17 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                        sinfo->signal = (s8)sta->last_signal;
                sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
        }
+       if (sta->chains) {
+               sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
+                                STATION_INFO_CHAIN_SIGNAL_AVG;
+
+               sinfo->chains = sta->chains;
+               for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
+                       sinfo->chain_signal[i] = sta->chain_signal_last[i];
+                       sinfo->chain_signal_avg[i] =
+                               (s8) -ewma_read(&sta->chain_signal_avg[i]);
+               }
+       }
 
        sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
        sta_set_rate_info_rx(sta, &sinfo->rxrate);
@@ -728,7 +742,7 @@ static void ieee80211_get_et_strings(struct wiphy *wiphy,
 
        if (sset == ETH_SS_STATS) {
                sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
-               memcpy(data, *ieee80211_gstrings_sta_stats, sz_sta_stats);
+               memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
        }
        drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
 }
@@ -1741,6 +1755,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
        ifmsh->mesh_pp_id = setup->path_sel_proto;
        ifmsh->mesh_pm_id = setup->path_metric;
        ifmsh->user_mpm = setup->user_mpm;
+       ifmsh->mesh_auth_id = setup->auth_id;
        ifmsh->security = IEEE80211_MESH_SEC_NONE;
        if (setup->is_authenticated)
                ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
@@ -1750,6 +1765,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
        /* mcast rate setting in Mesh Node */
        memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate,
                                                sizeof(setup->mcast_rate));
+       sdata->vif.bss_conf.basic_rates = setup->basic_rates;
 
        sdata->vif.bss_conf.beacon_int = setup->beacon_interval;
        sdata->vif.bss_conf.dtim_period = setup->dtim_period;
@@ -1862,6 +1878,8 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
        if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask))
                conf->dot11MeshAwakeWindowDuration =
                        nconf->dot11MeshAwakeWindowDuration;
+       if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask))
+               conf->plink_timeout = nconf->plink_timeout;
        ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
        return 0;
 }
@@ -2312,7 +2330,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
        enum ieee80211_smps_mode old_req;
        int err;
 
-       lockdep_assert_held(&sdata->u.mgd.mtx);
+       lockdep_assert_held(&sdata->wdev.mtx);
 
        old_req = sdata->u.mgd.req_smps;
        sdata->u.mgd.req_smps = smps_mode;
@@ -2369,9 +2387,9 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
        local->dynamic_ps_forced_timeout = timeout;
 
        /* no change, but if automatic follow powersave */
-       mutex_lock(&sdata->u.mgd.mtx);
+       sdata_lock(sdata);
        __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
-       mutex_unlock(&sdata->u.mgd.mtx);
+       sdata_unlock(sdata);
 
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
@@ -2809,7 +2827,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                    !rcu_access_pointer(sdata->bss->beacon))
                        need_offchan = true;
                if (!ieee80211_is_action(mgmt->frame_control) ||
-                   mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)
+                   mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
+                   mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED)
                        break;
                rcu_read_lock();
                sta = sta_info_get(sdata, mgmt->da);
@@ -2829,6 +2848,12 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                return -EOPNOTSUPP;
        }
 
+       /* configurations requiring offchan cannot work if no channel has been
+        * specified
+        */
+       if (need_offchan && !chan)
+               return -EINVAL;
+
        mutex_lock(&local->mtx);
 
        /* Check if the operating channel is the requested channel */
@@ -2838,10 +2863,15 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                rcu_read_lock();
                chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 
-               if (chanctx_conf)
-                       need_offchan = chan != chanctx_conf->def.chan;
-               else
+               if (chanctx_conf) {
+                       need_offchan = chan && (chan != chanctx_conf->def.chan);
+               } else if (!chan) {
+                       ret = -EINVAL;
+                       rcu_read_unlock();
+                       goto out_unlock;
+               } else {
                        need_offchan = true;
+               }
                rcu_read_unlock();
        }
 
@@ -2901,19 +2931,8 @@ static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
                                          u16 frame_type, bool reg)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
-       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
 
        switch (frame_type) {
-       case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH:
-               if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
-                       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
-
-                       if (reg)
-                               ifibss->auth_frame_registrations++;
-                       else
-                               ifibss->auth_frame_registrations--;
-               }
-               break;
        case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ:
                if (reg)
                        local->probe_req_reg++;
index 14abcf4..cafe614 100644 (file)
@@ -228,9 +228,9 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
        if (sdata->vif.type != NL80211_IFTYPE_STATION)
                return -EOPNOTSUPP;
 
-       mutex_lock(&sdata->u.mgd.mtx);
+       sdata_lock(sdata);
        err = __ieee80211_request_smps(sdata, smps_mode);
-       mutex_unlock(&sdata->u.mgd.mtx);
+       sdata_unlock(sdata);
 
        return err;
 }
@@ -313,16 +313,16 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
        case NL80211_IFTYPE_STATION:
                fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
                /* BSSID SA DA */
-               mutex_lock(&sdata->u.mgd.mtx);
+               sdata_lock(sdata);
                if (!sdata->u.mgd.associated) {
-                       mutex_unlock(&sdata->u.mgd.mtx);
+                       sdata_unlock(sdata);
                        dev_kfree_skb(skb);
                        return -ENOTCONN;
                }
                memcpy(hdr->addr1, sdata->u.mgd.associated->bssid, ETH_ALEN);
                memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
                memcpy(hdr->addr3, addr, ETH_ALEN);
-               mutex_unlock(&sdata->u.mgd.mtx);
+               sdata_unlock(sdata);
                break;
        default:
                dev_kfree_skb(skb);
@@ -471,6 +471,8 @@ __IEEE80211_IF_FILE_W(tsf);
 IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
 
 #ifdef CONFIG_MAC80211_MESH
+IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC);
+
 /* Mesh stats attributes */
 IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC);
 IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC);
@@ -480,7 +482,6 @@ IEEE80211_IF_FILE(dropped_frames_congestion,
                  u.mesh.mshstats.dropped_frames_congestion, DEC);
 IEEE80211_IF_FILE(dropped_frames_no_route,
                  u.mesh.mshstats.dropped_frames_no_route, DEC);
-IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC);
 
 /* Mesh parameters */
 IEEE80211_IF_FILE(dot11MeshMaxRetries,
@@ -583,6 +584,7 @@ static void add_wds_files(struct ieee80211_sub_if_data *sdata)
 static void add_mesh_files(struct ieee80211_sub_if_data *sdata)
 {
        DEBUGFS_ADD_MODE(tsf, 0600);
+       DEBUGFS_ADD_MODE(estab_plinks, 0400);
 }
 
 static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
@@ -598,7 +600,6 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
        MESHSTATS_ADD(dropped_frames_ttl);
        MESHSTATS_ADD(dropped_frames_no_route);
        MESHSTATS_ADD(dropped_frames_congestion);
-       MESHSTATS_ADD(estab_plinks);
 #undef MESHSTATS_ADD
 }
 
index 169664c..b931c96 100644 (file)
@@ -146,7 +146,8 @@ static inline int drv_add_interface(struct ieee80211_local *local,
 
        if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
                    (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
-                    !(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))))
+                    !(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF) &&
+                    !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))))
                return -EINVAL;
 
        trace_drv_add_interface(local, sdata);
index af8cee0..f83534f 100644 (file)
@@ -281,13 +281,14 @@ void ieee80211_ba_session_work(struct work_struct *work)
                                sta, tid, WLAN_BACK_RECIPIENT,
                                WLAN_REASON_UNSPECIFIED, true);
 
+               spin_lock_bh(&sta->lock);
+
                tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
                if (tid_tx) {
                        /*
                         * Assign it over to the normal tid_tx array
                         * where it "goes live".
                         */
-                       spin_lock_bh(&sta->lock);
 
                        sta->ampdu_mlme.tid_start_tx[tid] = NULL;
                        /* could there be a race? */
@@ -300,6 +301,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
                        ieee80211_tx_ba_session_handle_start(sta, tid);
                        continue;
                }
+               spin_unlock_bh(&sta->lock);
 
                tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
                if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP,
@@ -429,9 +431,9 @@ void ieee80211_request_smps_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data,
                             u.mgd.request_smps_work);
 
-       mutex_lock(&sdata->u.mgd.mtx);
+       sdata_lock(sdata);
        __ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode);
-       mutex_unlock(&sdata->u.mgd.mtx);
+       sdata_unlock(sdata);
 }
 
 void ieee80211_request_smps(struct ieee80211_vif *vif,
index 170f9a7..ea7b9c2 100644 (file)
@@ -54,7 +54,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        struct beacon_data *presp;
        int frame_len;
 
-       lockdep_assert_held(&ifibss->mtx);
+       sdata_assert_lock(sdata);
 
        /* Reset own TSF to allow time synchronization work. */
        drv_reset_tsf(local, sdata);
@@ -74,14 +74,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        }
 
        presp = rcu_dereference_protected(ifibss->presp,
-                                         lockdep_is_held(&ifibss->mtx));
+                                         lockdep_is_held(&sdata->wdev.mtx));
        rcu_assign_pointer(ifibss->presp, NULL);
        if (presp)
                kfree_rcu(presp, rcu_head);
 
        sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
 
-       cfg80211_chandef_create(&chandef, chan, ifibss->channel_type);
+       chandef = ifibss->chandef;
        if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
                chandef.width = NL80211_CHAN_WIDTH_20;
                chandef.center_freq1 = chan->center_freq;
@@ -176,6 +176,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        /* add HT capability and information IEs */
        if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+           chandef.width != NL80211_CHAN_WIDTH_5 &&
+           chandef.width != NL80211_CHAN_WIDTH_10 &&
            sband->ht_cap.ht_supported) {
                pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
                                                sband->ht_cap.cap);
@@ -263,7 +265,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        const struct cfg80211_bss_ies *ies;
        u64 tsf;
 
-       lockdep_assert_held(&sdata->u.ibss.mtx);
+       sdata_assert_lock(sdata);
 
        if (beacon_int < 10)
                beacon_int = 10;
@@ -298,8 +300,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                                  tsf, false);
 }
 
-static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
-                                                 bool auth)
+static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
        __acquires(RCU)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -321,26 +322,19 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
        /* If it fails, maybe we raced another insertion? */
        if (sta_info_insert_rcu(sta))
                return sta_info_get(sdata, addr);
-       if (auth && !sdata->u.ibss.auth_frame_registrations) {
-               ibss_dbg(sdata,
-                        "TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n",
-                        sdata->vif.addr, addr, sdata->u.ibss.bssid);
-               ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, 0, NULL, 0,
-                                   addr, sdata->u.ibss.bssid, NULL, 0, 0, 0);
-       }
        return sta;
 }
 
 static struct sta_info *
-ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
-                      const u8 *bssid, const u8 *addr,
-                      u32 supp_rates, bool auth)
+ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
+                      const u8 *addr, u32 supp_rates)
        __acquires(RCU)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_supported_band *sband;
        int band;
 
        /*
@@ -380,10 +374,11 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
        sta->last_rx = jiffies;
 
        /* make sure mandatory rates are always added */
+       sband = local->hw.wiphy->bands[band];
        sta->sta.supp_rates[band] = supp_rates |
-                       ieee80211_mandatory_rates(local, band);
+                       ieee80211_mandatory_rates(sband);
 
-       return ieee80211_ibss_finish_sta(sta, auth);
+       return ieee80211_ibss_finish_sta(sta);
 }
 
 static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
@@ -405,10 +400,8 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
                                        size_t len)
 {
        u16 auth_alg, auth_transaction;
-       struct sta_info *sta;
-       u8 deauth_frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
-       lockdep_assert_held(&sdata->u.ibss.mtx);
+       sdata_assert_lock(sdata);
 
        if (len < 24 + 6)
                return;
@@ -423,22 +416,6 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
        if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
                return;
 
-       sta_info_destroy_addr(sdata, mgmt->sa);
-       sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
-       rcu_read_unlock();
-
-       /*
-        * if we have any problem in allocating the new station, we reply with a
-        * DEAUTH frame to tell the other end that we had a problem
-        */
-       if (!sta) {
-               ieee80211_send_deauth_disassoc(sdata, sdata->u.ibss.bssid,
-                                              IEEE80211_STYPE_DEAUTH,
-                                              WLAN_REASON_UNSPECIFIED, true,
-                                              deauth_frame_buf);
-               return;
-       }
-
        /*
         * IEEE 802.11 standard does not require authentication in IBSS
         * networks and most implementations do not seem to use it.
@@ -492,7 +469,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                                prev_rates = sta->sta.supp_rates[band];
                                /* make sure mandatory rates are always added */
                                sta->sta.supp_rates[band] = supp_rates |
-                                       ieee80211_mandatory_rates(local, band);
+                                       ieee80211_mandatory_rates(sband);
 
                                if (sta->sta.supp_rates[band] != prev_rates) {
                                        ibss_dbg(sdata,
@@ -504,7 +481,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                        } else {
                                rcu_read_unlock();
                                sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
-                                               mgmt->sa, supp_rates, true);
+                                               mgmt->sa, supp_rates);
                        }
                }
 
@@ -512,7 +489,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                        set_sta_flag(sta, WLAN_STA_WME);
 
                if (sta && elems->ht_operation && elems->ht_cap_elem &&
-                   sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) {
+                   sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+                   sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 &&
+                   sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) {
                        /* we both use HT */
                        struct ieee80211_ht_cap htcap_ie;
                        struct cfg80211_chan_def chandef;
@@ -527,8 +506,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                         * fall back to HT20 if we don't use or use
                         * the other extension channel
                         */
-                       if (cfg80211_get_chandef_type(&chandef) !=
-                                               sdata->u.ibss.channel_type)
+                       if (chandef.center_freq1 !=
+                           sdata->u.ibss.chandef.center_freq1)
                                htcap_ie.cap_info &=
                                        cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
 
@@ -567,7 +546,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
 
        /* different channel */
        if (sdata->u.ibss.fixed_channel &&
-           sdata->u.ibss.channel != cbss->channel)
+           sdata->u.ibss.chandef.chan != cbss->channel)
                goto put_bss;
 
        /* different SSID */
@@ -608,7 +587,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                ieee80211_sta_join_ibss(sdata, bss);
                supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL);
                ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
-                                      supp_rates, true);
+                                      supp_rates);
                rcu_read_unlock();
        }
 
@@ -624,6 +603,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_supported_band *sband;
        int band;
 
        /*
@@ -658,8 +638,9 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
        sta->last_rx = jiffies;
 
        /* make sure mandatory rates are always added */
+       sband = local->hw.wiphy->bands[band];
        sta->sta.supp_rates[band] = supp_rates |
-                       ieee80211_mandatory_rates(local, band);
+                       ieee80211_mandatory_rates(sband);
 
        spin_lock(&ifibss->incomplete_lock);
        list_add(&sta->list, &ifibss->incomplete_stations);
@@ -673,7 +654,7 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
        int active = 0;
        struct sta_info *sta;
 
-       lockdep_assert_held(&sdata->u.ibss.mtx);
+       sdata_assert_lock(sdata);
 
        rcu_read_lock();
 
@@ -699,7 +680,7 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 
-       lockdep_assert_held(&ifibss->mtx);
+       sdata_assert_lock(sdata);
 
        mod_timer(&ifibss->timer,
                  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
@@ -730,7 +711,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
        u16 capability;
        int i;
 
-       lockdep_assert_held(&ifibss->mtx);
+       sdata_assert_lock(sdata);
 
        if (ifibss->fixed_bssid) {
                memcpy(bssid, ifibss->bssid, ETH_ALEN);
@@ -755,7 +736,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
                sdata->drop_unencrypted = 0;
 
        __ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
-                                 ifibss->channel, ifibss->basic_rates,
+                                 ifibss->chandef.chan, ifibss->basic_rates,
                                  capability, 0, true);
 }
 
@@ -773,7 +754,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
        int active_ibss;
        u16 capability;
 
-       lockdep_assert_held(&ifibss->mtx);
+       sdata_assert_lock(sdata);
 
        active_ibss = ieee80211_sta_active_ibss(sdata);
        ibss_dbg(sdata, "sta_find_ibss (active_ibss=%d)\n", active_ibss);
@@ -787,7 +768,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
        if (ifibss->fixed_bssid)
                bssid = ifibss->bssid;
        if (ifibss->fixed_channel)
-               chan = ifibss->channel;
+               chan = ifibss->chandef.chan;
        if (!is_zero_ether_addr(ifibss->bssid))
                bssid = ifibss->bssid;
        cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid,
@@ -843,10 +824,10 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
        struct beacon_data *presp;
        u8 *pos, *end;
 
-       lockdep_assert_held(&ifibss->mtx);
+       sdata_assert_lock(sdata);
 
        presp = rcu_dereference_protected(ifibss->presp,
-                                         lockdep_is_held(&ifibss->mtx));
+                                         lockdep_is_held(&sdata->wdev.mtx));
 
        if (ifibss->state != IEEE80211_IBSS_MLME_JOINED ||
            len < 24 + 2 || !presp)
@@ -930,7 +911,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        mgmt = (struct ieee80211_mgmt *) skb->data;
        fc = le16_to_cpu(mgmt->frame_control);
 
-       mutex_lock(&sdata->u.ibss.mtx);
+       sdata_lock(sdata);
 
        if (!sdata->u.ibss.ssid_len)
                goto mgmt_out; /* not ready to merge yet */
@@ -953,7 +934,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        }
 
  mgmt_out:
-       mutex_unlock(&sdata->u.ibss.mtx);
+       sdata_unlock(sdata);
 }
 
 void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
@@ -961,7 +942,7 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct sta_info *sta;
 
-       mutex_lock(&ifibss->mtx);
+       sdata_lock(sdata);
 
        /*
         * Work could be scheduled after scan or similar
@@ -978,7 +959,7 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
                list_del(&sta->list);
                spin_unlock_bh(&ifibss->incomplete_lock);
 
-               ieee80211_ibss_finish_sta(sta, true);
+               ieee80211_ibss_finish_sta(sta);
                rcu_read_unlock();
                spin_lock_bh(&ifibss->incomplete_lock);
        }
@@ -997,7 +978,7 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
        }
 
  out:
-       mutex_unlock(&ifibss->mtx);
+       sdata_unlock(sdata);
 }
 
 static void ieee80211_ibss_timer(unsigned long data)
@@ -1014,7 +995,6 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
 
        setup_timer(&ifibss->timer, ieee80211_ibss_timer,
                    (unsigned long) sdata);
-       mutex_init(&ifibss->mtx);
        INIT_LIST_HEAD(&ifibss->incomplete_stations);
        spin_lock_init(&ifibss->incomplete_lock);
 }
@@ -1041,8 +1021,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
 {
        u32 changed = 0;
 
-       mutex_lock(&sdata->u.ibss.mtx);
-
        if (params->bssid) {
                memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
                sdata->u.ibss.fixed_bssid = true;
@@ -1057,9 +1035,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
 
        sdata->vif.bss_conf.beacon_int = params->beacon_interval;
 
-       sdata->u.ibss.channel = params->chandef.chan;
-       sdata->u.ibss.channel_type =
-               cfg80211_get_chandef_type(&params->chandef);
+       sdata->u.ibss.chandef = params->chandef;
        sdata->u.ibss.fixed_channel = params->channel_fixed;
 
        if (params->ie) {
@@ -1075,8 +1051,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);
        sdata->u.ibss.ssid_len = params->ssid_len;
 
-       mutex_unlock(&sdata->u.ibss.mtx);
-
        /*
         * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
         * reserved, but an HT STA shall protect HT transmissions as though
@@ -1112,8 +1086,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        struct sta_info *sta;
        struct beacon_data *presp;
 
-       mutex_lock(&sdata->u.ibss.mtx);
-
        active_ibss = ieee80211_sta_active_ibss(sdata);
 
        if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
@@ -1122,7 +1094,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
                if (ifibss->privacy)
                        capability |= WLAN_CAPABILITY_PRIVACY;
 
-               cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->channel,
+               cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
                                        ifibss->bssid, ifibss->ssid,
                                        ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
                                        WLAN_CAPABILITY_PRIVACY,
@@ -1157,7 +1129,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        /* remove beacon */
        kfree(sdata->u.ibss.ie);
        presp = rcu_dereference_protected(ifibss->presp,
-                                         lockdep_is_held(&sdata->u.ibss.mtx));
+                                         lockdep_is_held(&sdata->wdev.mtx));
        RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
        sdata->vif.bss_conf.ibss_joined = false;
        sdata->vif.bss_conf.ibss_creator = false;
@@ -1173,7 +1145,5 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
 
        del_timer_sync(&sdata->u.ibss.timer);
 
-       mutex_unlock(&sdata->u.ibss.mtx);
-
        return 0;
 }
index 9ca8e32..8412a30 100644 (file)
@@ -94,6 +94,7 @@ struct ieee80211_bss {
 #define IEEE80211_MAX_SUPP_RATES 32
        u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
        size_t supp_rates_len;
+       struct ieee80211_rate *beacon_rate;
 
        /*
         * During association, we save an ERP value from a probe response so
@@ -366,7 +367,7 @@ struct ieee80211_mgd_assoc_data {
        u8 ssid_len;
        u8 supp_rates_len;
        bool wmm, uapsd;
-       bool have_beacon, need_beacon;
+       bool need_beacon;
        bool synced;
        bool timeout_started;
 
@@ -394,7 +395,6 @@ struct ieee80211_if_managed {
        bool nullfunc_failed;
        bool connection_loss;
 
-       struct mutex mtx;
        struct cfg80211_bss *associated;
        struct ieee80211_mgd_auth_data *auth_data;
        struct ieee80211_mgd_assoc_data *assoc_data;
@@ -405,6 +405,7 @@ struct ieee80211_if_managed {
 
        bool powersave; /* powersave requested for this iface */
        bool broken_ap; /* AP is broken -- turn off powersave */
+       bool have_beacon;
        u8 dtim_period;
        enum ieee80211_smps_mode req_smps, /* requested smps mode */
                                 driver_smps_mode; /* smps mode request */
@@ -488,8 +489,6 @@ struct ieee80211_if_managed {
 struct ieee80211_if_ibss {
        struct timer_list timer;
 
-       struct mutex mtx;
-
        unsigned long last_scan_completed;
 
        u32 basic_rates;
@@ -499,14 +498,12 @@ struct ieee80211_if_ibss {
        bool privacy;
 
        bool control_port;
-       unsigned int auth_frame_registrations;
 
        u8 bssid[ETH_ALEN] __aligned(2);
        u8 ssid[IEEE80211_MAX_SSID_LEN];
        u8 ssid_len, ie_len;
        u8 *ie;
-       struct ieee80211_channel *channel;
-       enum nl80211_channel_type channel_type;
+       struct cfg80211_chan_def chandef;
 
        unsigned long ibss_join_req;
        /* probe response/beacon for IBSS */
@@ -545,6 +542,7 @@ struct ieee80211_if_mesh {
        struct timer_list mesh_path_root_timer;
 
        unsigned long wrkq_flags;
+       unsigned long mbss_changed;
 
        u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
        size_t mesh_id_len;
@@ -580,8 +578,6 @@ struct ieee80211_if_mesh {
        bool accepting_plinks;
        int num_gates;
        struct beacon_data __rcu *beacon;
-       /* just protects beacon updates for now */
-       struct mutex mtx;
        const u8 *ie;
        u8 ie_len;
        enum {
@@ -778,6 +774,26 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p)
        return container_of(p, struct ieee80211_sub_if_data, vif);
 }
 
+static inline void sdata_lock(struct ieee80211_sub_if_data *sdata)
+       __acquires(&sdata->wdev.mtx)
+{
+       mutex_lock(&sdata->wdev.mtx);
+       __acquire(&sdata->wdev.mtx);
+}
+
+static inline void sdata_unlock(struct ieee80211_sub_if_data *sdata)
+       __releases(&sdata->wdev.mtx)
+{
+       mutex_unlock(&sdata->wdev.mtx);
+       __release(&sdata->wdev.mtx);
+}
+
+static inline void
+sdata_assert_lock(struct ieee80211_sub_if_data *sdata)
+{
+       lockdep_assert_held(&sdata->wdev.mtx);
+}
+
 static inline enum ieee80211_band
 ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
 {
@@ -1507,9 +1523,6 @@ static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
        ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
 }
 
-u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
-                             enum ieee80211_band band);
-
 void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
 void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
 void ieee80211_dynamic_ps_timer(unsigned long data);
index 98d20c0..cc11759 100644 (file)
@@ -159,7 +159,8 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
        return 0;
 }
 
-static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr)
+static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr,
+                               bool check_dup)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_sub_if_data *iter;
@@ -180,13 +181,16 @@ static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr)
                ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
                ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
 
+       if (!check_dup)
+               return ret;
 
        mutex_lock(&local->iflist_mtx);
        list_for_each_entry(iter, &local->interfaces, list) {
                if (iter == sdata)
                        continue;
 
-               if (iter->vif.type == NL80211_IFTYPE_MONITOR)
+               if (iter->vif.type == NL80211_IFTYPE_MONITOR &&
+                   !(iter->u.mntr_flags & MONITOR_FLAG_ACTIVE))
                        continue;
 
                m = iter->vif.addr;
@@ -208,12 +212,17 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct sockaddr *sa = addr;
+       bool check_dup = true;
        int ret;
 
        if (ieee80211_sdata_running(sdata))
                return -EBUSY;
 
-       ret = ieee80211_verify_mac(sdata, sa->sa_data);
+       if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
+           !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
+               check_dup = false;
+
+       ret = ieee80211_verify_mac(sdata, sa->sa_data, check_dup);
        if (ret)
                return ret;
 
@@ -545,7 +554,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                        break;
                }
 
-               if (local->monitors == 0 && local->open_count == 0) {
+               if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
+                       res = drv_add_interface(local, sdata);
+                       if (res)
+                               goto err_stop;
+               } else if (local->monitors == 0 && local->open_count == 0) {
                        res = ieee80211_add_virtual_monitor(local);
                        if (res)
                                goto err_stop;
@@ -923,7 +936,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                mutex_lock(&local->mtx);
                ieee80211_recalc_idle(local);
                mutex_unlock(&local->mtx);
-               break;
+
+               if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
+                       break;
+
+               /* fall through */
        default:
                if (going_down)
                        drv_remove_interface(local, sdata);
@@ -1072,7 +1089,7 @@ static const struct net_device_ops ieee80211_monitorif_ops = {
        .ndo_start_xmit         = ieee80211_monitor_start_xmit,
        .ndo_set_rx_mode        = ieee80211_set_multicast_list,
        .ndo_change_mtu         = ieee80211_change_mtu,
-       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_set_mac_address    = ieee80211_change_mac,
        .ndo_select_queue       = ieee80211_monitor_select_queue,
 };
 
@@ -1747,10 +1764,9 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)
 }
 
 static int netdev_notify(struct notifier_block *nb,
-                        unsigned long state,
-                        void *ndev)
+                        unsigned long state, void *ptr)
 {
-       struct net_device *dev = ndev;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct ieee80211_sub_if_data *sdata;
 
        if (state != NETDEV_CHANGENAME)
index 67059b8..e39cc91 100644 (file)
@@ -335,12 +335,12 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
        switch (cipher) {
        case WLAN_CIPHER_SUITE_WEP40:
        case WLAN_CIPHER_SUITE_WEP104:
-               key->conf.iv_len = WEP_IV_LEN;
-               key->conf.icv_len = WEP_ICV_LEN;
+               key->conf.iv_len = IEEE80211_WEP_IV_LEN;
+               key->conf.icv_len = IEEE80211_WEP_ICV_LEN;
                break;
        case WLAN_CIPHER_SUITE_TKIP:
-               key->conf.iv_len = TKIP_IV_LEN;
-               key->conf.icv_len = TKIP_ICV_LEN;
+               key->conf.iv_len = IEEE80211_TKIP_IV_LEN;
+               key->conf.icv_len = IEEE80211_TKIP_ICV_LEN;
                if (seq) {
                        for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
                                key->u.tkip.rx[i].iv32 =
@@ -352,13 +352,13 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
                spin_lock_init(&key->u.tkip.txlock);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
-               key->conf.iv_len = CCMP_HDR_LEN;
-               key->conf.icv_len = CCMP_MIC_LEN;
+               key->conf.iv_len = IEEE80211_CCMP_HDR_LEN;
+               key->conf.icv_len = IEEE80211_CCMP_MIC_LEN;
                if (seq) {
                        for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++)
-                               for (j = 0; j < CCMP_PN_LEN; j++)
+                               for (j = 0; j < IEEE80211_CCMP_PN_LEN; j++)
                                        key->u.ccmp.rx_pn[i][j] =
-                                               seq[CCMP_PN_LEN - j - 1];
+                                               seq[IEEE80211_CCMP_PN_LEN - j - 1];
                }
                /*
                 * Initialize AES key state here as an optimization so that
@@ -375,9 +375,9 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
                key->conf.iv_len = 0;
                key->conf.icv_len = sizeof(struct ieee80211_mmie);
                if (seq)
-                       for (j = 0; j < CMAC_PN_LEN; j++)
+                       for (j = 0; j < IEEE80211_CMAC_PN_LEN; j++)
                                key->u.aes_cmac.rx_pn[j] =
-                                       seq[CMAC_PN_LEN - j - 1];
+                                       seq[IEEE80211_CMAC_PN_LEN - j - 1];
                /*
                 * Initialize AES key state here as an optimization so that
                 * it does not need to be initialized for every packet.
@@ -740,13 +740,13 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
                        pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS];
                else
                        pn = key->u.ccmp.rx_pn[tid];
-               memcpy(seq->ccmp.pn, pn, CCMP_PN_LEN);
+               memcpy(seq->ccmp.pn, pn, IEEE80211_CCMP_PN_LEN);
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
                if (WARN_ON(tid != 0))
                        return;
                pn = key->u.aes_cmac.rx_pn;
-               memcpy(seq->aes_cmac.pn, pn, CMAC_PN_LEN);
+               memcpy(seq->aes_cmac.pn, pn, IEEE80211_CMAC_PN_LEN);
                break;
        }
 }
index e8de3e6..036d57e 100644 (file)
 #define NUM_DEFAULT_KEYS 4
 #define NUM_DEFAULT_MGMT_KEYS 2
 
-#define WEP_IV_LEN             4
-#define WEP_ICV_LEN            4
-#define ALG_CCMP_KEY_LEN       16
-#define CCMP_HDR_LEN           8
-#define CCMP_MIC_LEN           8
-#define CCMP_TK_LEN            16
-#define CCMP_PN_LEN            6
-#define TKIP_IV_LEN            8
-#define TKIP_ICV_LEN           4
-#define CMAC_PN_LEN            6
-
 struct ieee80211_local;
 struct ieee80211_sub_if_data;
 struct sta_info;
@@ -93,13 +82,13 @@ struct ieee80211_key {
                         * frames and the last counter is used with Robust
                         * Management frames.
                         */
-                       u8 rx_pn[IEEE80211_NUM_TIDS + 1][CCMP_PN_LEN];
+                       u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN];
                        struct crypto_cipher *tfm;
                        u32 replays; /* dot11RSNAStatsCCMPReplays */
                } ccmp;
                struct {
                        atomic64_t tx_pn;
-                       u8 rx_pn[CMAC_PN_LEN];
+                       u8 rx_pn[IEEE80211_CMAC_PN_LEN];
                        struct crypto_cipher *tfm;
                        u32 replays; /* dot11RSNAStatsCMACReplays */
                        u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
index 8eae74a..091088a 100644 (file)
@@ -331,7 +331,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
                return NOTIFY_DONE;
 
        ifmgd = &sdata->u.mgd;
-       mutex_lock(&ifmgd->mtx);
+       sdata_lock(sdata);
 
        /* Copy the addresses to the bss_conf list */
        ifa = idev->ifa_list;
@@ -349,7 +349,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
                ieee80211_bss_info_change_notify(sdata,
                                                 BSS_CHANGED_ARP_FILTER);
 
-       mutex_unlock(&ifmgd->mtx);
+       sdata_unlock(sdata);
 
        return NOTIFY_DONE;
 }
@@ -686,8 +686,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                return -EINVAL;
 
 #ifdef CONFIG_PM
-       if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns) &&
-           (!local->ops->suspend || !local->ops->resume))
+       if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume))
                return -EINVAL;
 #endif
 
index 6952760..447f41b 100644 (file)
@@ -271,8 +271,7 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
        *pos++ = ifmsh->mesh_auth_id;
        /* Mesh Formation Info - number of neighbors */
        neighbors = atomic_read(&ifmsh->estab_plinks);
-       /* Number of neighbor mesh STAs or 15 whichever is smaller */
-       neighbors = (neighbors > 15) ? 15 : neighbors;
+       neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);
        *pos++ = neighbors << 1;
        /* Mesh capability */
        *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
@@ -417,7 +416,9 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata,
 
        sband = local->hw.wiphy->bands[band];
        if (!sband->ht_cap.ht_supported ||
-           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
                return 0;
 
        if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
@@ -573,7 +574,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        u32 changed;
 
-       ieee80211_sta_expire(sdata, IEEE80211_MESH_PEER_INACTIVITY_LIMIT);
+       ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);
        mesh_path_expire(sdata);
 
        changed = mesh_accept_plinks_update(sdata);
@@ -697,38 +698,38 @@ out_free:
 }
 
 static int
-ieee80211_mesh_rebuild_beacon(struct ieee80211_if_mesh *ifmsh)
+ieee80211_mesh_rebuild_beacon(struct ieee80211_sub_if_data *sdata)
 {
        struct beacon_data *old_bcn;
        int ret;
 
-       mutex_lock(&ifmsh->mtx);
-
-       old_bcn = rcu_dereference_protected(ifmsh->beacon,
-                                           lockdep_is_held(&ifmsh->mtx));
-       ret = ieee80211_mesh_build_beacon(ifmsh);
+       old_bcn = rcu_dereference_protected(sdata->u.mesh.beacon,
+                                           lockdep_is_held(&sdata->wdev.mtx));
+       ret = ieee80211_mesh_build_beacon(&sdata->u.mesh);
        if (ret)
                /* just reuse old beacon */
-               goto out;
+               return ret;
 
        if (old_bcn)
                kfree_rcu(old_bcn, rcu_head);
-out:
-       mutex_unlock(&ifmsh->mtx);
-       return ret;
+       return 0;
 }
 
 void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
                                       u32 changed)
 {
-       if (sdata->vif.bss_conf.enable_beacon &&
-           (changed & (BSS_CHANGED_BEACON |
-                       BSS_CHANGED_HT |
-                       BSS_CHANGED_BASIC_RATES |
-                       BSS_CHANGED_BEACON_INT)))
-               if (ieee80211_mesh_rebuild_beacon(&sdata->u.mesh))
-                       return;
-       ieee80211_bss_info_change_notify(sdata, changed);
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       unsigned long bits = changed;
+       u32 bit;
+
+       if (!bits)
+               return;
+
+       /* if we race with running work, worst case this work becomes a noop */
+       for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE)
+               set_bit(bit, &ifmsh->mbss_changed);
+       set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags);
+       ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 }
 
 int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
@@ -740,7 +741,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
                      BSS_CHANGED_HT |
                      BSS_CHANGED_BASIC_RATES |
                      BSS_CHANGED_BEACON_INT;
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
 
        local->fif_other_bss++;
        /* mesh ifaces must set allmulti to forward mcast traffic */
@@ -748,7 +748,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
        ieee80211_configure_filter(local);
 
        ifmsh->mesh_cc_id = 0;  /* Disabled */
-       ifmsh->mesh_auth_id = 0;        /* Disabled */
        /* register sync ops from extensible synchronization framework */
        ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id);
        ifmsh->adjusting_tbtt = false;
@@ -759,8 +758,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
        sdata->vif.bss_conf.ht_operation_mode =
                                ifmsh->mshcfg.ht_opmode;
        sdata->vif.bss_conf.enable_beacon = true;
-       sdata->vif.bss_conf.basic_rates =
-               ieee80211_mandatory_rates(local, band);
 
        changed |= ieee80211_mps_local_status_update(sdata);
 
@@ -788,12 +785,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        sdata->vif.bss_conf.enable_beacon = false;
        clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
-       mutex_lock(&ifmsh->mtx);
        bcn = rcu_dereference_protected(ifmsh->beacon,
-                                       lockdep_is_held(&ifmsh->mtx));
+                                       lockdep_is_held(&sdata->wdev.mtx));
        rcu_assign_pointer(ifmsh->beacon, NULL);
        kfree_rcu(bcn, rcu_head);
-       mutex_unlock(&ifmsh->mtx);
 
        /* flush STAs and mpaths on this iface */
        sta_info_flush(sdata);
@@ -806,14 +801,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        del_timer_sync(&sdata->u.mesh.housekeeping_timer);
        del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
        del_timer_sync(&sdata->u.mesh.mesh_path_timer);
-       /*
-        * If the timer fired while we waited for it, it will have
-        * requeued the work. Now the work will be running again
-        * but will not rearm the timer again because it checks
-        * whether the interface is running, which, at this point,
-        * it no longer is.
-        */
-       cancel_work_sync(&sdata->work);
+
+       /* clear any mesh work (for next join) we may have accrued */
+       ifmsh->wrkq_flags = 0;
+       ifmsh->mbss_changed = 0;
 
        local->fif_other_bss--;
        atomic_dec(&local->iff_allmultis);
@@ -954,6 +945,12 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_mgmt *mgmt;
        u16 stype;
 
+       sdata_lock(sdata);
+
+       /* mesh already went down */
+       if (!sdata->wdev.mesh_id_len)
+               goto out;
+
        rx_status = IEEE80211_SKB_RXCB(skb);
        mgmt = (struct ieee80211_mgmt *) skb->data;
        stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
@@ -971,12 +968,42 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status);
                break;
        }
+out:
+       sdata_unlock(sdata);
+}
+
+static void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       u32 bit, changed = 0;
+
+       for_each_set_bit(bit, &ifmsh->mbss_changed,
+                        sizeof(changed) * BITS_PER_BYTE) {
+               clear_bit(bit, &ifmsh->mbss_changed);
+               changed |= BIT(bit);
+       }
+
+       if (sdata->vif.bss_conf.enable_beacon &&
+           (changed & (BSS_CHANGED_BEACON |
+                       BSS_CHANGED_HT |
+                       BSS_CHANGED_BASIC_RATES |
+                       BSS_CHANGED_BEACON_INT)))
+               if (ieee80211_mesh_rebuild_beacon(sdata))
+                       return;
+
+       ieee80211_bss_info_change_notify(sdata, changed);
 }
 
 void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 
+       sdata_lock(sdata);
+
+       /* mesh already went down */
+       if (!sdata->wdev.mesh_id_len)
+               goto out;
+
        if (ifmsh->preq_queue_len &&
            time_after(jiffies,
                       ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval)))
@@ -996,6 +1023,11 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
 
        if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
                mesh_sync_adjust_tbtt(sdata);
+
+       if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags))
+               mesh_bss_info_changed(sdata);
+out:
+       sdata_unlock(sdata);
 }
 
 void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
@@ -1041,7 +1073,6 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
        spin_lock_init(&ifmsh->mesh_preq_queue_lock);
        spin_lock_init(&ifmsh->sync_offset_lock);
        RCU_INIT_POINTER(ifmsh->beacon, NULL);
-       mutex_init(&ifmsh->mtx);
 
        sdata->vif.bss_conf.bssid = zero_addr;
 }
index da15877..2bc7fd2 100644 (file)
@@ -58,6 +58,7 @@ enum mesh_path_flags {
  * @MESH_WORK_ROOT: the mesh root station needs to send a frame
  * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other
  * mesh nodes
+ * @MESH_WORK_MBSS_CHANGED: rebuild beacon and notify driver of BSS changes
  */
 enum mesh_deferred_task_flags {
        MESH_WORK_HOUSEKEEPING,
@@ -65,6 +66,7 @@ enum mesh_deferred_task_flags {
        MESH_WORK_GROW_MPP_TABLE,
        MESH_WORK_ROOT,
        MESH_WORK_DRIFT_ADJUST,
+       MESH_WORK_MBSS_CHANGED,
 };
 
 /**
@@ -188,7 +190,6 @@ struct mesh_rmc {
        u32 idx_mask;
 };
 
-#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ)
 #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ)
 
 #define MESH_PATH_EXPIRE (600 * HZ)
@@ -324,14 +325,14 @@ static inline
 u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
 {
        atomic_inc(&sdata->u.mesh.estab_plinks);
-       return mesh_accept_plinks_update(sdata);
+       return mesh_accept_plinks_update(sdata) | BSS_CHANGED_BEACON;
 }
 
 static inline
 u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
 {
        atomic_dec(&sdata->u.mesh.estab_plinks);
-       return mesh_accept_plinks_update(sdata);
+       return mesh_accept_plinks_update(sdata) | BSS_CHANGED_BEACON;
 }
 
 static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata)
index 09bebed..02c05fa 100644 (file)
@@ -154,8 +154,14 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
        u16 ht_opmode;
        bool non_ht_sta = false, ht20_sta = false;
 
-       if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
+       switch (sdata->vif.bss_conf.chandef.width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
                return 0;
+       default:
+               break;
+       }
 
        rcu_read_lock();
        list_for_each_entry_rcu(sta, &local->sta_list, list) {
index 741448b..ae31968 100644 (file)
@@ -90,41 +90,6 @@ MODULE_PARM_DESC(probe_wait_ms,
  */
 #define IEEE80211_SIGNAL_AVE_MIN_COUNT 4
 
-/*
- * All cfg80211 functions have to be called outside a locked
- * section so that they can acquire a lock themselves... This
- * is much simpler than queuing up things in cfg80211, but we
- * do need some indirection for that here.
- */
-enum rx_mgmt_action {
-       /* no action required */
-       RX_MGMT_NONE,
-
-       /* caller must call cfg80211_send_deauth() */
-       RX_MGMT_CFG80211_DEAUTH,
-
-       /* caller must call cfg80211_send_disassoc() */
-       RX_MGMT_CFG80211_DISASSOC,
-
-       /* caller must call cfg80211_send_rx_auth() */
-       RX_MGMT_CFG80211_RX_AUTH,
-
-       /* caller must call cfg80211_send_rx_assoc() */
-       RX_MGMT_CFG80211_RX_ASSOC,
-
-       /* caller must call cfg80211_send_assoc_timeout() */
-       RX_MGMT_CFG80211_ASSOC_TIMEOUT,
-
-       /* used when a processed beacon causes a deauth */
-       RX_MGMT_CFG80211_TX_DEAUTH,
-};
-
-/* utils */
-static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
-{
-       lockdep_assert_held(&ifmgd->mtx);
-}
-
 /*
  * We can have multiple work items (and connection probing)
  * scheduling this timer, but we need to take care to only
@@ -135,13 +100,14 @@ static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
  * has happened -- the work that runs from this timer will
  * do that.
  */
-static void run_again(struct ieee80211_if_managed *ifmgd, unsigned long timeout)
+static void run_again(struct ieee80211_sub_if_data *sdata,
+                     unsigned long timeout)
 {
-       ASSERT_MGD_MTX(ifmgd);
+       sdata_assert_lock(sdata);
 
-       if (!timer_pending(&ifmgd->timer) ||
-           time_before(timeout, ifmgd->timer.expires))
-               mod_timer(&ifmgd->timer, timeout);
+       if (!timer_pending(&sdata->u.mgd.timer) ||
+           time_before(timeout, sdata->u.mgd.timer.expires))
+               mod_timer(&sdata->u.mgd.timer, timeout);
 }
 
 void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
@@ -224,6 +190,12 @@ static u32 chandef_downgrade(struct cfg80211_chan_def *c)
                c->width = NL80211_CHAN_WIDTH_20_NOHT;
                ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
                break;
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
+               WARN_ON_ONCE(1);
+               /* keep c->width */
+               ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+               break;
        }
 
        WARN_ON_ONCE(!cfg80211_chandef_valid(c));
@@ -652,7 +624,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_channel *chan;
        u32 rates = 0;
 
-       lockdep_assert_held(&ifmgd->mtx);
+       sdata_assert_lock(sdata);
 
        rcu_read_lock();
        chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
@@ -914,6 +886,10 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
 
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
                                        IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
+
+       if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+               IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+
        if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
                            IEEE80211_STA_CONNECTION_POLL))
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
@@ -962,7 +938,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        if (!ieee80211_sdata_running(sdata))
                return;
 
-       mutex_lock(&ifmgd->mtx);
+       sdata_lock(sdata);
        if (!ifmgd->associated)
                goto out;
 
@@ -985,7 +961,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
  out:
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
-       mutex_unlock(&ifmgd->mtx);
+       sdata_unlock(sdata);
 }
 
 void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
@@ -1036,7 +1012,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        const struct ieee80211_ht_operation *ht_oper;
        int secondary_channel_offset = -1;
 
-       ASSERT_MGD_MTX(ifmgd);
+       sdata_assert_lock(sdata);
 
        if (!cbss)
                return;
@@ -1390,6 +1366,9 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
                          IEEE80211_STA_CONNECTION_POLL))
                return false;
 
+       if (!mgd->have_beacon)
+               return false;
+
        rcu_read_lock();
        sta = sta_info_get(sdata, mgd->bssid);
        if (sta)
@@ -1798,7 +1777,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_led_assoc(local, 1);
 
-       if (sdata->u.mgd.assoc_data->have_beacon) {
+       if (sdata->u.mgd.have_beacon) {
                /*
                 * If the AP is buggy we may get here with no DTIM period
                 * known, so assume it's 1 which is the only safe assumption
@@ -1806,8 +1785,10 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
                 * probably just won't work at all.
                 */
                bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
-               bss_info_changed |= BSS_CHANGED_DTIM_PERIOD;
+               bss_conf->beacon_rate = bss->beacon_rate;
+               bss_info_changed |= BSS_CHANGED_BEACON_INFO;
        } else {
+               bss_conf->beacon_rate = NULL;
                bss_conf->dtim_period = 0;
        }
 
@@ -1842,7 +1823,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        u32 changed = 0;
 
-       ASSERT_MGD_MTX(ifmgd);
+       sdata_assert_lock(sdata);
 
        if (WARN_ON_ONCE(tx && !frame_buf))
                return;
@@ -1930,6 +1911,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        del_timer_sync(&sdata->u.mgd.chswitch_timer);
 
        sdata->vif.bss_conf.dtim_period = 0;
+       sdata->vif.bss_conf.beacon_rate = NULL;
+
+       ifmgd->have_beacon = false;
 
        ifmgd->flags = 0;
        ieee80211_vif_release_channel(sdata);
@@ -2051,7 +2035,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
        }
 
        ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
-       run_again(ifmgd, ifmgd->probe_timeout);
+       run_again(sdata, ifmgd->probe_timeout);
        if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
                ieee80211_flush_queues(sdata->local, sdata);
 }
@@ -2065,7 +2049,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
        if (!ieee80211_sdata_running(sdata))
                return;
 
-       mutex_lock(&ifmgd->mtx);
+       sdata_lock(sdata);
 
        if (!ifmgd->associated)
                goto out;
@@ -2119,7 +2103,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
        ifmgd->probe_send_count = 0;
        ieee80211_mgd_probe_ap_send(sdata);
  out:
-       mutex_unlock(&ifmgd->mtx);
+       sdata_unlock(sdata);
 }
 
 struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
@@ -2135,7 +2119,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
        if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
                return NULL;
 
-       ASSERT_MGD_MTX(ifmgd);
+       sdata_assert_lock(sdata);
 
        if (ifmgd->associated)
                cbss = ifmgd->associated;
@@ -2168,9 +2152,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
-       mutex_lock(&ifmgd->mtx);
+       sdata_lock(sdata);
        if (!ifmgd->associated) {
-               mutex_unlock(&ifmgd->mtx);
+               sdata_unlock(sdata);
                return;
        }
 
@@ -2181,13 +2165,10 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
-       mutex_unlock(&ifmgd->mtx);
 
-       /*
-        * must be outside lock due to cfg80211,
-        * but that's not a problem.
-        */
-       cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
+       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                             IEEE80211_DEAUTH_FRAME_LEN);
+       sdata_unlock(sdata);
 }
 
 static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
@@ -2254,7 +2235,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
 
-       lockdep_assert_held(&sdata->u.mgd.mtx);
+       sdata_assert_lock(sdata);
 
        if (!assoc) {
                sta_info_destroy_addr(sdata, auth_data->bss->bssid);
@@ -2295,27 +2276,26 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
                            auth_data->key_idx, tx_flags);
 }
 
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
-                      struct ieee80211_mgmt *mgmt, size_t len)
+static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
+                                  struct ieee80211_mgmt *mgmt, size_t len)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 bssid[ETH_ALEN];
        u16 auth_alg, auth_transaction, status_code;
        struct sta_info *sta;
 
-       lockdep_assert_held(&ifmgd->mtx);
+       sdata_assert_lock(sdata);
 
        if (len < 24 + 6)
-               return RX_MGMT_NONE;
+               return;
 
        if (!ifmgd->auth_data || ifmgd->auth_data->done)
-               return RX_MGMT_NONE;
+               return;
 
        memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
 
        if (!ether_addr_equal(bssid, mgmt->bssid))
-               return RX_MGMT_NONE;
+               return;
 
        auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
        auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
@@ -2327,14 +2307,15 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                           mgmt->sa, auth_alg, ifmgd->auth_data->algorithm,
                           auth_transaction,
                           ifmgd->auth_data->expected_transaction);
-               return RX_MGMT_NONE;
+               return;
        }
 
        if (status_code != WLAN_STATUS_SUCCESS) {
                sdata_info(sdata, "%pM denied authentication (status %d)\n",
                           mgmt->sa, status_code);
                ieee80211_destroy_auth_data(sdata, false);
-               return RX_MGMT_CFG80211_RX_AUTH;
+               cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+               return;
        }
 
        switch (ifmgd->auth_data->algorithm) {
@@ -2347,20 +2328,20 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                if (ifmgd->auth_data->expected_transaction != 4) {
                        ieee80211_auth_challenge(sdata, mgmt, len);
                        /* need another frame */
-                       return RX_MGMT_NONE;
+                       return;
                }
                break;
        default:
                WARN_ONCE(1, "invalid auth alg %d",
                          ifmgd->auth_data->algorithm);
-               return RX_MGMT_NONE;
+               return;
        }
 
        sdata_info(sdata, "authenticated\n");
        ifmgd->auth_data->done = true;
        ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
        ifmgd->auth_data->timeout_started = true;
-       run_again(ifmgd, ifmgd->auth_data->timeout);
+       run_again(sdata, ifmgd->auth_data->timeout);
 
        if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE &&
            ifmgd->auth_data->expected_transaction != 2) {
@@ -2368,7 +2349,8 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                 * Report auth frame to user space for processing since another
                 * round of Authentication frames is still needed.
                 */
-               return RX_MGMT_CFG80211_RX_AUTH;
+               cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+               return;
        }
 
        /* move station state to auth */
@@ -2384,30 +2366,29 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
        }
        mutex_unlock(&sdata->local->sta_mtx);
 
-       return RX_MGMT_CFG80211_RX_AUTH;
+       cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+       return;
  out_err:
        mutex_unlock(&sdata->local->sta_mtx);
        /* ignore frame -- wait for timeout */
-       return RX_MGMT_NONE;
 }
 
 
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
-                        struct ieee80211_mgmt *mgmt, size_t len)
+static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
+                                    struct ieee80211_mgmt *mgmt, size_t len)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        const u8 *bssid = NULL;
        u16 reason_code;
 
-       lockdep_assert_held(&ifmgd->mtx);
+       sdata_assert_lock(sdata);
 
        if (len < 24 + 2)
-               return RX_MGMT_NONE;
+               return;
 
        if (!ifmgd->associated ||
            !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
-               return RX_MGMT_NONE;
+               return;
 
        bssid = ifmgd->associated->bssid;
 
@@ -2418,25 +2399,24 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-       return RX_MGMT_CFG80211_DEAUTH;
+       cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
 }
 
 
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
-                          struct ieee80211_mgmt *mgmt, size_t len)
+static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
+                                      struct ieee80211_mgmt *mgmt, size_t len)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u16 reason_code;
 
-       lockdep_assert_held(&ifmgd->mtx);
+       sdata_assert_lock(sdata);
 
        if (len < 24 + 2)
-               return RX_MGMT_NONE;
+               return;
 
        if (!ifmgd->associated ||
            !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
-               return RX_MGMT_NONE;
+               return;
 
        reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
 
@@ -2445,7 +2425,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-       return RX_MGMT_CFG80211_DISASSOC;
+       cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
 }
 
 static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
@@ -2495,7 +2475,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
 
-       lockdep_assert_held(&sdata->u.mgd.mtx);
+       sdata_assert_lock(sdata);
 
        if (!assoc) {
                sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
@@ -2749,10 +2729,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
        return ret;
 }
 
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
-                            struct ieee80211_mgmt *mgmt, size_t len,
-                            struct cfg80211_bss **bss)
+static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
+                                        struct ieee80211_mgmt *mgmt,
+                                        size_t len)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
@@ -2760,13 +2739,14 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        struct ieee802_11_elems elems;
        u8 *pos;
        bool reassoc;
+       struct cfg80211_bss *bss;
 
-       lockdep_assert_held(&ifmgd->mtx);
+       sdata_assert_lock(sdata);
 
        if (!assoc_data)
-               return RX_MGMT_NONE;
+               return;
        if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid))
-               return RX_MGMT_NONE;
+               return;
 
        /*
         * AssocResp and ReassocResp have identical structure, so process both
@@ -2774,7 +2754,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
         */
 
        if (len < 24 + 6)
-               return RX_MGMT_NONE;
+               return;
 
        reassoc = ieee80211_is_reassoc_req(mgmt->frame_control);
        capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
@@ -2801,22 +2781,22 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                assoc_data->timeout = jiffies + msecs_to_jiffies(ms);
                assoc_data->timeout_started = true;
                if (ms > IEEE80211_ASSOC_TIMEOUT)
-                       run_again(ifmgd, assoc_data->timeout);
-               return RX_MGMT_NONE;
+                       run_again(sdata, assoc_data->timeout);
+               return;
        }
 
-       *bss = assoc_data->bss;
+       bss = assoc_data->bss;
 
        if (status_code != WLAN_STATUS_SUCCESS) {
                sdata_info(sdata, "%pM denied association (code=%d)\n",
                           mgmt->sa, status_code);
                ieee80211_destroy_assoc_data(sdata, false);
        } else {
-               if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) {
+               if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
                        /* oops -- internal error -- send timeout for now */
                        ieee80211_destroy_assoc_data(sdata, false);
-                       cfg80211_put_bss(sdata->local->hw.wiphy, *bss);
-                       return RX_MGMT_CFG80211_ASSOC_TIMEOUT;
+                       cfg80211_assoc_timeout(sdata->dev, bss);
+                       return;
                }
                sdata_info(sdata, "associated\n");
 
@@ -2828,7 +2808,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                ieee80211_destroy_assoc_data(sdata, true);
        }
 
-       return RX_MGMT_CFG80211_RX_ASSOC;
+       cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len);
 }
 
 static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
@@ -2840,23 +2820,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        int freq;
        struct ieee80211_bss *bss;
        struct ieee80211_channel *channel;
-       bool need_ps = false;
-
-       lockdep_assert_held(&sdata->u.mgd.mtx);
 
-       if ((sdata->u.mgd.associated &&
-            ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||
-           (sdata->u.mgd.assoc_data &&
-            ether_addr_equal(mgmt->bssid,
-                             sdata->u.mgd.assoc_data->bss->bssid))) {
-               /* not previously set so we may need to recalc */
-               need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period;
-
-               if (elems->tim && !elems->parse_error) {
-                       const struct ieee80211_tim_ie *tim_ie = elems->tim;
-                       sdata->u.mgd.dtim_period = tim_ie->dtim_period;
-               }
-       }
+       sdata_assert_lock(sdata);
 
        if (elems->ds_params)
                freq = ieee80211_channel_to_frequency(elems->ds_params[0],
@@ -2871,19 +2836,15 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
 
        bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
                                        channel);
-       if (bss)
+       if (bss) {
                ieee80211_rx_bss_put(local, bss);
+               sdata->vif.bss_conf.beacon_rate = bss->beacon_rate;
+       }
 
        if (!sdata->u.mgd.associated ||
            !ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid))
                return;
 
-       if (need_ps) {
-               mutex_lock(&local->iflist_mtx);
-               ieee80211_recalc_ps(local, -1);
-               mutex_unlock(&local->iflist_mtx);
-       }
-
        ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
                                         elems, true);
 
@@ -2901,7 +2862,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
 
        ifmgd = &sdata->u.mgd;
 
-       ASSERT_MGD_MTX(ifmgd);
+       sdata_assert_lock(sdata);
 
        if (!ether_addr_equal(mgmt->da, sdata->vif.addr))
                return; /* ignore ProbeResp to foreign address */
@@ -2926,7 +2887,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
                ifmgd->auth_data->tries = 0;
                ifmgd->auth_data->timeout = jiffies;
                ifmgd->auth_data->timeout_started = true;
-               run_again(ifmgd, ifmgd->auth_data->timeout);
+               run_again(sdata, ifmgd->auth_data->timeout);
        }
 }
 
@@ -2951,10 +2912,9 @@ static const u64 care_about_ies =
        (1ULL << WLAN_EID_HT_CAPABILITY) |
        (1ULL << WLAN_EID_HT_OPERATION);
 
-static enum rx_mgmt_action
-ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
-                        struct ieee80211_mgmt *mgmt, size_t len,
-                        u8 *deauth_buf, struct ieee80211_rx_status *rx_status)
+static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
+                                    struct ieee80211_mgmt *mgmt, size_t len,
+                                    struct ieee80211_rx_status *rx_status)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
@@ -2969,24 +2929,25 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        u8 erp_value = 0;
        u32 ncrc;
        u8 *bssid;
+       u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
-       lockdep_assert_held(&ifmgd->mtx);
+       sdata_assert_lock(sdata);
 
        /* Process beacon from the current BSS */
        baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
        if (baselen > len)
-               return RX_MGMT_NONE;
+               return;
 
        rcu_read_lock();
        chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
        if (!chanctx_conf) {
                rcu_read_unlock();
-               return RX_MGMT_NONE;
+               return;
        }
 
        if (rx_status->freq != chanctx_conf->def.chan->center_freq) {
                rcu_read_unlock();
-               return RX_MGMT_NONE;
+               return;
        }
        chan = chanctx_conf->def.chan;
        rcu_read_unlock();
@@ -2997,7 +2958,11 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                       len - baselen, false, &elems);
 
                ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
-               ifmgd->assoc_data->have_beacon = true;
+               if (elems.tim && !elems.parse_error) {
+                       const struct ieee80211_tim_ie *tim_ie = elems.tim;
+                       ifmgd->dtim_period = tim_ie->dtim_period;
+               }
+               ifmgd->have_beacon = true;
                ifmgd->assoc_data->need_beacon = false;
                if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
                        sdata->vif.bss_conf.sync_tsf =
@@ -3013,13 +2978,13 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                /* continue assoc process */
                ifmgd->assoc_data->timeout = jiffies;
                ifmgd->assoc_data->timeout_started = true;
-               run_again(ifmgd, ifmgd->assoc_data->timeout);
-               return RX_MGMT_NONE;
+               run_again(sdata, ifmgd->assoc_data->timeout);
+               return;
        }
 
        if (!ifmgd->associated ||
            !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
-               return RX_MGMT_NONE;
+               return;
        bssid = ifmgd->associated->bssid;
 
        /* Track average RSSI from the Beacon frames of the current AP */
@@ -3165,7 +3130,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        }
 
        if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
-               return RX_MGMT_NONE;
+               return;
        ifmgd->beacon_crc = ncrc;
        ifmgd->beacon_crc_valid = true;
 
@@ -3179,7 +3144,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
         * If we haven't had a beacon before, tell the driver about the
         * DTIM period (and beacon timing if desired) now.
         */
-       if (!bss_conf->dtim_period) {
+       if (!ifmgd->have_beacon) {
                /* a few bogus AP send dtim_period = 0 or no TIM IE */
                if (elems.tim)
                        bss_conf->dtim_period = elems.tim->dtim_period ?: 1;
@@ -3198,7 +3163,14 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                sdata->vif.bss_conf.sync_dtim_count = 0;
                }
 
-               changed |= BSS_CHANGED_DTIM_PERIOD;
+               changed |= BSS_CHANGED_BEACON_INFO;
+               ifmgd->have_beacon = true;
+
+               mutex_lock(&local->iflist_mtx);
+               ieee80211_recalc_ps(local, -1);
+               mutex_unlock(&local->iflist_mtx);
+
+               ieee80211_recalc_ps_vif(sdata);
        }
 
        if (elems.erp_info) {
@@ -3220,7 +3192,9 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                                       WLAN_REASON_DEAUTH_LEAVING,
                                       true, deauth_buf);
-               return RX_MGMT_CFG80211_TX_DEAUTH;
+               cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf,
+                                     sizeof(deauth_buf));
+               return;
        }
 
        if (sta && elems.opmode_notif)
@@ -3237,19 +3211,13 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                                       elems.pwr_constr_elem);
 
        ieee80211_bss_info_change_notify(sdata, changed);
-
-       return RX_MGMT_NONE;
 }
 
 void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                  struct sk_buff *skb)
 {
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_rx_status *rx_status;
        struct ieee80211_mgmt *mgmt;
-       struct cfg80211_bss *bss = NULL;
-       enum rx_mgmt_action rma = RX_MGMT_NONE;
-       u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
        u16 fc;
        struct ieee802_11_elems elems;
        int ies_len;
@@ -3258,28 +3226,27 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        mgmt = (struct ieee80211_mgmt *) skb->data;
        fc = le16_to_cpu(mgmt->frame_control);
 
-       mutex_lock(&ifmgd->mtx);
+       sdata_lock(sdata);
 
        switch (fc & IEEE80211_FCTL_STYPE) {
        case IEEE80211_STYPE_BEACON:
-               rma = ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
-                                              deauth_buf, rx_status);
+               ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status);
                break;
        case IEEE80211_STYPE_PROBE_RESP:
                ieee80211_rx_mgmt_probe_resp(sdata, skb);
                break;
        case IEEE80211_STYPE_AUTH:
-               rma = ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
+               ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
                break;
        case IEEE80211_STYPE_DEAUTH:
-               rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
+               ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
                break;
        case IEEE80211_STYPE_DISASSOC:
-               rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
+               ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
                break;
        case IEEE80211_STYPE_ASSOC_RESP:
        case IEEE80211_STYPE_REASSOC_RESP:
-               rma = ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, &bss);
+               ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len);
                break;
        case IEEE80211_STYPE_ACTION:
                if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) {
@@ -3325,34 +3292,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                }
                break;
        }
-       mutex_unlock(&ifmgd->mtx);
-
-       switch (rma) {
-       case RX_MGMT_NONE:
-               /* no action */
-               break;
-       case RX_MGMT_CFG80211_DEAUTH:
-               cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
-               break;
-       case RX_MGMT_CFG80211_DISASSOC:
-               cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
-               break;
-       case RX_MGMT_CFG80211_RX_AUTH:
-               cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, skb->len);
-               break;
-       case RX_MGMT_CFG80211_RX_ASSOC:
-               cfg80211_send_rx_assoc(sdata->dev, bss, (u8 *)mgmt, skb->len);
-               break;
-       case RX_MGMT_CFG80211_ASSOC_TIMEOUT:
-               cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
-               break;
-       case RX_MGMT_CFG80211_TX_DEAUTH:
-               cfg80211_send_deauth(sdata->dev, deauth_buf,
-                                    sizeof(deauth_buf));
-               break;
-       default:
-               WARN(1, "unexpected: %d", rma);
-       }
+       sdata_unlock(sdata);
 }
 
 static void ieee80211_sta_timer(unsigned long data)
@@ -3366,20 +3306,13 @@ static void ieee80211_sta_timer(unsigned long data)
 static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
                                          u8 *bssid, u8 reason, bool tx)
 {
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
                               tx, frame_buf);
-       mutex_unlock(&ifmgd->mtx);
 
-       /*
-        * must be outside lock due to cfg80211,
-        * but that's not a problem.
-        */
-       cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
-
-       mutex_lock(&ifmgd->mtx);
+       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                             IEEE80211_DEAUTH_FRAME_LEN);
 }
 
 static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
@@ -3389,7 +3322,7 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data;
        u32 tx_flags = 0;
 
-       lockdep_assert_held(&ifmgd->mtx);
+       sdata_assert_lock(sdata);
 
        if (WARN_ON_ONCE(!auth_data))
                return -EINVAL;
@@ -3462,7 +3395,7 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
        if (tx_flags == 0) {
                auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
                ifmgd->auth_data->timeout_started = true;
-               run_again(ifmgd, auth_data->timeout);
+               run_again(sdata, auth_data->timeout);
        } else {
                auth_data->timeout_started = false;
        }
@@ -3475,7 +3408,7 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
        struct ieee80211_local *local = sdata->local;
 
-       lockdep_assert_held(&sdata->u.mgd.mtx);
+       sdata_assert_lock(sdata);
 
        assoc_data->tries++;
        if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) {
@@ -3499,7 +3432,7 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
        if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
                assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
                assoc_data->timeout_started = true;
-               run_again(&sdata->u.mgd, assoc_data->timeout);
+               run_again(sdata, assoc_data->timeout);
        } else {
                assoc_data->timeout_started = false;
        }
@@ -3524,7 +3457,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-       mutex_lock(&ifmgd->mtx);
+       sdata_lock(sdata);
 
        if (ifmgd->status_received) {
                __le16 fc = ifmgd->status_fc;
@@ -3536,7 +3469,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                        if (status_acked) {
                                ifmgd->auth_data->timeout =
                                        jiffies + IEEE80211_AUTH_TIMEOUT_SHORT;
-                               run_again(ifmgd, ifmgd->auth_data->timeout);
+                               run_again(sdata, ifmgd->auth_data->timeout);
                        } else {
                                ifmgd->auth_data->timeout = jiffies - 1;
                        }
@@ -3547,7 +3480,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                        if (status_acked) {
                                ifmgd->assoc_data->timeout =
                                        jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
-                               run_again(ifmgd, ifmgd->assoc_data->timeout);
+                               run_again(sdata, ifmgd->assoc_data->timeout);
                        } else {
                                ifmgd->assoc_data->timeout = jiffies - 1;
                        }
@@ -3570,30 +3503,22 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
 
                        ieee80211_destroy_auth_data(sdata, false);
 
-                       mutex_unlock(&ifmgd->mtx);
-                       cfg80211_send_auth_timeout(sdata->dev, bssid);
-                       mutex_lock(&ifmgd->mtx);
+                       cfg80211_auth_timeout(sdata->dev, bssid);
                }
        } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
-               run_again(ifmgd, ifmgd->auth_data->timeout);
+               run_again(sdata, ifmgd->auth_data->timeout);
 
        if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started &&
            time_after(jiffies, ifmgd->assoc_data->timeout)) {
-               if ((ifmgd->assoc_data->need_beacon &&
-                    !ifmgd->assoc_data->have_beacon) ||
+               if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) ||
                    ieee80211_do_assoc(sdata)) {
-                       u8 bssid[ETH_ALEN];
-
-                       memcpy(bssid, ifmgd->assoc_data->bss->bssid, ETH_ALEN);
+                       struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
 
                        ieee80211_destroy_assoc_data(sdata, false);
-
-                       mutex_unlock(&ifmgd->mtx);
-                       cfg80211_send_assoc_timeout(sdata->dev, bssid);
-                       mutex_lock(&ifmgd->mtx);
+                       cfg80211_assoc_timeout(sdata->dev, bss);
                }
        } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
-               run_again(ifmgd, ifmgd->assoc_data->timeout);
+               run_again(sdata, ifmgd->assoc_data->timeout);
 
        if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
                            IEEE80211_STA_CONNECTION_POLL) &&
@@ -3627,7 +3552,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                                        false);
                        }
                } else if (time_is_after_jiffies(ifmgd->probe_timeout))
-                       run_again(ifmgd, ifmgd->probe_timeout);
+                       run_again(sdata, ifmgd->probe_timeout);
                else if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
                        mlme_dbg(sdata,
                                 "Failed to send nullfunc to AP %pM after %dms, disconnecting\n",
@@ -3656,7 +3581,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                }
        }
 
-       mutex_unlock(&ifmgd->mtx);
+       sdata_unlock(sdata);
 }
 
 static void ieee80211_sta_bcn_mon_timer(unsigned long data)
@@ -3717,9 +3642,9 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-       mutex_lock(&ifmgd->mtx);
+       sdata_lock(sdata);
        if (!ifmgd->associated) {
-               mutex_unlock(&ifmgd->mtx);
+               sdata_unlock(sdata);
                return;
        }
 
@@ -3730,10 +3655,10 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
                                              ifmgd->associated->bssid,
                                              WLAN_REASON_UNSPECIFIED,
                                              true);
-               mutex_unlock(&ifmgd->mtx);
+               sdata_unlock(sdata);
                return;
        }
-       mutex_unlock(&ifmgd->mtx);
+       sdata_unlock(sdata);
 }
 #endif
 
@@ -3765,8 +3690,6 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
        ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len;
        ifmgd->p2p_noa_index = -1;
 
-       mutex_init(&ifmgd->mtx);
-
        if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
                ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
        else
@@ -3923,6 +3846,12 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
         */
        ret = ieee80211_vif_use_channel(sdata, &chandef,
                                        IEEE80211_CHANCTX_SHARED);
+
+       /* don't downgrade for 5 and 10 MHz channels, though. */
+       if (chandef.width == NL80211_CHAN_WIDTH_5 ||
+           chandef.width == NL80211_CHAN_WIDTH_10)
+               return ret;
+
        while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
                ifmgd->flags |= chandef_downgrade(&chandef);
                ret = ieee80211_vif_use_channel(sdata, &chandef,
@@ -4122,8 +4051,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
 
        /* try to authenticate/probe */
 
-       mutex_lock(&ifmgd->mtx);
-
        if ((ifmgd->auth_data && !ifmgd->auth_data->done) ||
            ifmgd->assoc_data) {
                err = -EBUSY;
@@ -4143,8 +4070,8 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
 
-               __cfg80211_send_deauth(sdata->dev, frame_buf,
-                                      sizeof(frame_buf));
+               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                                     sizeof(frame_buf));
        }
 
        sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
@@ -4161,8 +4088,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
 
        /* hold our own reference */
        cfg80211_ref_bss(local->hw.wiphy, auth_data->bss);
-       err = 0;
-       goto out_unlock;
+       return 0;
 
  err_clear:
        memset(ifmgd->bssid, 0, ETH_ALEN);
@@ -4170,9 +4096,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        ifmgd->auth_data = NULL;
  err_free:
        kfree(auth_data);
- out_unlock:
-       mutex_unlock(&ifmgd->mtx);
-
        return err;
 }
 
@@ -4203,8 +4126,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        assoc_data->ssid_len = ssidie[1];
        rcu_read_unlock();
 
-       mutex_lock(&ifmgd->mtx);
-
        if (ifmgd->associated) {
                u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
@@ -4212,8 +4133,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
 
-               __cfg80211_send_deauth(sdata->dev, frame_buf,
-                                      sizeof(frame_buf));
+               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                                     sizeof(frame_buf));
        }
 
        if (ifmgd->auth_data && !ifmgd->auth_data->done) {
@@ -4360,6 +4281,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 
        ifmgd->assoc_data = assoc_data;
        ifmgd->dtim_period = 0;
+       ifmgd->have_beacon = false;
 
        err = ieee80211_prep_connection(sdata, req->bss, true);
        if (err)
@@ -4391,7 +4313,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                        ifmgd->dtim_period = tim->dtim_period;
                        dtim_count = tim->dtim_count;
                }
-               assoc_data->have_beacon = true;
+               ifmgd->have_beacon = true;
                assoc_data->timeout = jiffies;
                assoc_data->timeout_started = true;
 
@@ -4407,7 +4329,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        }
        rcu_read_unlock();
 
-       run_again(ifmgd, assoc_data->timeout);
+       run_again(sdata, assoc_data->timeout);
 
        if (bss->corrupt_data) {
                char *corrupt_type = "data";
@@ -4423,17 +4345,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                           corrupt_type);
        }
 
-       err = 0;
-       goto out;
+       return 0;
  err_clear:
        memset(ifmgd->bssid, 0, ETH_ALEN);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
        ifmgd->assoc_data = NULL;
  err_free:
        kfree(assoc_data);
- out:
-       mutex_unlock(&ifmgd->mtx);
-
        return err;
 }
 
@@ -4445,8 +4363,6 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
        bool tx = !req->local_state_change;
        bool report_frame = false;
 
-       mutex_lock(&ifmgd->mtx);
-
        sdata_info(sdata,
                   "deauthenticating from %pM by local choice (reason=%d)\n",
                   req->bssid, req->reason_code);
@@ -4458,7 +4374,6 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                                               req->reason_code, tx,
                                               frame_buf);
                ieee80211_destroy_auth_data(sdata, false);
-               mutex_unlock(&ifmgd->mtx);
 
                report_frame = true;
                goto out;
@@ -4470,12 +4385,11 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                                       req->reason_code, tx, frame_buf);
                report_frame = true;
        }
-       mutex_unlock(&ifmgd->mtx);
 
  out:
        if (report_frame)
-               __cfg80211_send_deauth(sdata->dev, frame_buf,
-                                      IEEE80211_DEAUTH_FRAME_LEN);
+               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                                     IEEE80211_DEAUTH_FRAME_LEN);
 
        return 0;
 }
@@ -4487,18 +4401,14 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
        u8 bssid[ETH_ALEN];
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
-       mutex_lock(&ifmgd->mtx);
-
        /*
         * cfg80211 should catch this ... but it's racy since
         * we can receive a disassoc frame, process it, hand it
         * to cfg80211 while that's in a locked section already
         * trying to tell us that the user wants to disconnect.
         */
-       if (ifmgd->associated != req->bss) {
-               mutex_unlock(&ifmgd->mtx);
+       if (ifmgd->associated != req->bss)
                return -ENOLINK;
-       }
 
        sdata_info(sdata,
                   "disassociating from %pM by local choice (reason=%d)\n",
@@ -4508,10 +4418,9 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC,
                               req->reason_code, !req->local_state_change,
                               frame_buf);
-       mutex_unlock(&ifmgd->mtx);
 
-       __cfg80211_send_disassoc(sdata->dev, frame_buf,
-                                IEEE80211_DEAUTH_FRAME_LEN);
+       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                             IEEE80211_DEAUTH_FRAME_LEN);
 
        return 0;
 }
@@ -4531,13 +4440,16 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
        cancel_work_sync(&ifmgd->csa_connection_drop_work);
        cancel_work_sync(&ifmgd->chswitch_work);
 
-       mutex_lock(&ifmgd->mtx);
-       if (ifmgd->assoc_data)
+       sdata_lock(sdata);
+       if (ifmgd->assoc_data) {
+               struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
                ieee80211_destroy_assoc_data(sdata, false);
+               cfg80211_assoc_timeout(sdata->dev, bss);
+       }
        if (ifmgd->auth_data)
                ieee80211_destroy_auth_data(sdata, false);
        del_timer_sync(&ifmgd->timer);
-       mutex_unlock(&ifmgd->mtx);
+       sdata_unlock(sdata);
 }
 
 void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
index a02bef3..30d58d2 100644 (file)
@@ -397,8 +397,14 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
                        return;
 
                /* if HT BSS, and we handle a data frame, also try HT rates */
-               if (chan_width == NL80211_CHAN_WIDTH_20_NOHT)
+               switch (chan_width) {
+               case NL80211_CHAN_WIDTH_20_NOHT:
+               case NL80211_CHAN_WIDTH_5:
+               case NL80211_CHAN_WIDTH_10:
                        return;
+               default:
+                       break;
+               }
 
                alt_rate.idx = 0;
                /* keep protection flags */
index 8e29526..23dbcfc 100644 (file)
@@ -258,6 +258,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
        pos += 2;
 
        if (status->flag & RX_FLAG_HT) {
+               unsigned int stbc;
+
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
                *pos++ = local->hw.radiotap_mcs_details;
                *pos = 0;
@@ -267,6 +269,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                        *pos |= IEEE80211_RADIOTAP_MCS_BW_40;
                if (status->flag & RX_FLAG_HT_GF)
                        *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
+               stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT;
+               *pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
                pos++;
                *pos++ = status->rate_idx;
        }
@@ -1372,6 +1376,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
        struct sk_buff *skb = rx->skb;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       int i;
 
        if (!sta)
                return RX_CONTINUE;
@@ -1422,6 +1427,19 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
                ewma_add(&sta->avg_signal, -status->signal);
        }
 
+       if (status->chains) {
+               sta->chains = status->chains;
+               for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
+                       int signal = status->chain_signal[i];
+
+                       if (!(status->chains & BIT(i)))
+                               continue;
+
+                       sta->chain_signal_last[i] = signal;
+                       ewma_add(&sta->chain_signal_avg[i], -signal);
+               }
+       }
+
        /*
         * Change STA power saving mode only at the end of a frame
         * exchange sequence.
@@ -1608,7 +1626,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
                        entry->ccmp = 1;
                        memcpy(entry->last_pn,
                               rx->key->u.ccmp.rx_pn[queue],
-                              CCMP_PN_LEN);
+                              IEEE80211_CCMP_PN_LEN);
                }
                return RX_QUEUED;
        }
@@ -1627,21 +1645,21 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
         * (IEEE 802.11i, 8.3.3.4.5) */
        if (entry->ccmp) {
                int i;
-               u8 pn[CCMP_PN_LEN], *rpn;
+               u8 pn[IEEE80211_CCMP_PN_LEN], *rpn;
                int queue;
                if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP)
                        return RX_DROP_UNUSABLE;
-               memcpy(pn, entry->last_pn, CCMP_PN_LEN);
-               for (i = CCMP_PN_LEN - 1; i >= 0; i--) {
+               memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN);
+               for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) {
                        pn[i]++;
                        if (pn[i])
                                break;
                }
                queue = rx->security_idx;
                rpn = rx->key->u.ccmp.rx_pn[queue];
-               if (memcmp(pn, rpn, CCMP_PN_LEN))
+               if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN))
                        return RX_DROP_UNUSABLE;
-               memcpy(entry->last_pn, pn, CCMP_PN_LEN);
+               memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN);
        }
 
        skb_pull(rx->skb, ieee80211_hdrlen(fc));
@@ -1729,27 +1747,21 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
                if (unlikely(!ieee80211_has_protected(fc) &&
                             ieee80211_is_unicast_robust_mgmt_frame(rx->skb) &&
                             rx->key)) {
-                       if (ieee80211_is_deauth(fc))
-                               cfg80211_send_unprot_deauth(rx->sdata->dev,
-                                                           rx->skb->data,
-                                                           rx->skb->len);
-                       else if (ieee80211_is_disassoc(fc))
-                               cfg80211_send_unprot_disassoc(rx->sdata->dev,
-                                                             rx->skb->data,
-                                                             rx->skb->len);
+                       if (ieee80211_is_deauth(fc) ||
+                           ieee80211_is_disassoc(fc))
+                               cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+                                                            rx->skb->data,
+                                                            rx->skb->len);
                        return -EACCES;
                }
                /* BIP does not use Protected field, so need to check MMIE */
                if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) &&
                             ieee80211_get_mmie_keyidx(rx->skb) < 0)) {
-                       if (ieee80211_is_deauth(fc))
-                               cfg80211_send_unprot_deauth(rx->sdata->dev,
-                                                           rx->skb->data,
-                                                           rx->skb->len);
-                       else if (ieee80211_is_disassoc(fc))
-                               cfg80211_send_unprot_disassoc(rx->sdata->dev,
-                                                             rx->skb->data,
-                                                             rx->skb->len);
+                       if (ieee80211_is_deauth(fc) ||
+                           ieee80211_is_disassoc(fc))
+                               cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+                                                            rx->skb->data,
+                                                            rx->skb->len);
                        return -EACCES;
                }
                /*
index 99b1039..1b122a7 100644 (file)
@@ -140,6 +140,15 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
                        bss->valid_data |= IEEE80211_BSS_VALID_WMM;
        }
 
+       if (beacon) {
+               struct ieee80211_supported_band *sband =
+                       local->hw.wiphy->bands[rx_status->band];
+               if (!(rx_status->flag & RX_FLAG_HT) &&
+                   !(rx_status->flag & RX_FLAG_VHT))
+                       bss->beacon_rate =
+                               &sband->bitrates[rx_status->rate_idx];
+       }
+
        return bss;
 }
 
index 11216bc..aeb967a 100644 (file)
@@ -149,6 +149,7 @@ static void cleanup_single_sta(struct sta_info *sta)
         * directly by station destruction.
         */
        for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
+               kfree(sta->ampdu_mlme.tid_start_tx[i]);
                tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
                if (!tid_tx)
                        continue;
@@ -346,6 +347,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        if (ieee80211_vif_is_mesh(&sdata->vif) &&
            !sdata->u.mesh.user_mpm)
                init_timer(&sta->plink_timer);
+       sta->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
 #endif
 
        memcpy(sta->sta.addr, addr, ETH_ALEN);
@@ -358,6 +360,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        do_posix_clock_monotonic_gettime(&uptime);
        sta->last_connected = uptime.tv_sec;
        ewma_init(&sta->avg_signal, 1024, 8);
+       for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
+               ewma_init(&sta->chain_signal_avg[i], 1024, 8);
 
        if (sta_prepare_rate_control(local, sta, gfp)) {
                kfree(sta);
@@ -1130,6 +1134,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
         * ends the poll/service period.
         */
        info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
+                      IEEE80211_TX_CTL_PS_RESPONSE |
                       IEEE80211_TX_STATUS_EOSP |
                       IEEE80211_TX_CTL_REQ_TX_STATUS;
 
@@ -1267,7 +1272,8 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                         * STA may still remain is PS mode after this frame
                         * exchange.
                         */
-                       info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
+                       info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
+                                      IEEE80211_TX_CTL_PS_RESPONSE;
 
                        /*
                         * Use MoreData flag to indicate whether there are
index adc3004..4208dbd 100644 (file)
@@ -203,6 +203,7 @@ struct tid_ampdu_rx {
  *     driver requested to close until the work for it runs
  * @mtx: mutex to protect all TX data (except non-NULL assignments
  *     to tid_tx[idx], which are protected by the sta spinlock)
+ *     tid_start_tx is also protected by sta->lock.
  */
 struct sta_ampdu_mlme {
        struct mutex mtx;
@@ -297,6 +298,9 @@ struct sta_ampdu_mlme {
  * @rcu_head: RCU head used for freeing this station struct
  * @cur_max_bandwidth: maximum bandwidth to use for TX to the station,
  *     taken from HT/VHT capabilities or VHT operating mode notification
+ * @chains: chains ever used for RX from this station
+ * @chain_signal_last: last signal (per chain)
+ * @chain_signal_avg: signal average (per chain)
  */
 struct sta_info {
        /* General information, mostly static */
@@ -344,6 +348,11 @@ struct sta_info {
        int last_signal;
        struct ewma avg_signal;
        int last_ack_signal;
+
+       u8 chains;
+       s8 chain_signal_last[IEEE80211_MAX_CHAINS];
+       struct ewma chain_signal_avg[IEEE80211_MAX_CHAINS];
+
        /* Plus 1 for non-QoS frames */
        __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
 
index 9972e07..4105d0c 100644 (file)
@@ -398,13 +398,14 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
        if (ieee80211_has_order(hdr->frame_control))
                return TX_CONTINUE;
 
+       if (tx->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
+               info->hw_queue = tx->sdata->vif.cab_queue;
+
        /* no stations in PS mode */
        if (!atomic_read(&ps->num_sta_ps))
                return TX_CONTINUE;
 
        info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
-       if (tx->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
-               info->hw_queue = tx->sdata->vif.cab_queue;
 
        /* device releases frame after DTIM beacon */
        if (!(tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING))
@@ -1789,12 +1790,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                break;
 #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
-               if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
-                       /* Do not send frames with mesh_ttl == 0 */
-                       sdata->u.mesh.mshstats.dropped_frames_ttl++;
-                       goto fail_rcu;
-               }
-
                if (!is_multicast_ether_addr(skb->data)) {
                        struct sta_info *next_hop;
                        bool mpp_lookup = true;
index 72e6292..2265445 100644 (file)
@@ -560,6 +560,9 @@ void ieee80211_iterate_active_interfaces(
        list_for_each_entry(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_MONITOR:
+                       if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
+                               continue;
+                       break;
                case NL80211_IFTYPE_AP_VLAN:
                        continue;
                default:
@@ -598,6 +601,9 @@ void ieee80211_iterate_active_interfaces_atomic(
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_MONITOR:
+                       if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
+                               continue;
+                       break;
                case NL80211_IFTYPE_AP_VLAN:
                        continue;
                default:
@@ -1072,32 +1078,6 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
        ieee80211_set_wmm_default(sdata, true);
 }
 
-u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
-                             enum ieee80211_band band)
-{
-       struct ieee80211_supported_band *sband;
-       struct ieee80211_rate *bitrates;
-       u32 mandatory_rates;
-       enum ieee80211_rate_flags mandatory_flag;
-       int i;
-
-       sband = local->hw.wiphy->bands[band];
-       if (WARN_ON(!sband))
-               return 1;
-
-       if (band == IEEE80211_BAND_2GHZ)
-               mandatory_flag = IEEE80211_RATE_MANDATORY_B;
-       else
-               mandatory_flag = IEEE80211_RATE_MANDATORY_A;
-
-       bitrates = sband->bitrates;
-       mandatory_rates = 0;
-       for (i = 0; i < sband->n_bitrates; i++)
-               if (bitrates[i].flags & mandatory_flag)
-                       mandatory_rates |= BIT(i);
-       return mandatory_rates;
-}
-
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg, u16 status,
                         const u8 *extra, size_t extra_len, const u8 *da,
@@ -1604,12 +1584,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                                   BSS_CHANGED_ARP_FILTER |
                                   BSS_CHANGED_PS;
 
-                       if (sdata->u.mgd.dtim_period)
-                               changed |= BSS_CHANGED_DTIM_PERIOD;
+                       /* Re-send beacon info report to the driver */
+                       if (sdata->u.mgd.have_beacon)
+                               changed |= BSS_CHANGED_BEACON_INFO;
 
-                       mutex_lock(&sdata->u.mgd.mtx);
+                       sdata_lock(sdata);
                        ieee80211_bss_info_change_notify(sdata, changed);
-                       mutex_unlock(&sdata->u.mgd.mtx);
+                       sdata_unlock(sdata);
                        break;
                case NL80211_IFTYPE_ADHOC:
                        changed |= BSS_CHANGED_IBSS;
index 171344d..97c2894 100644 (file)
@@ -396,7 +396,7 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
        new_bw = ieee80211_sta_cur_vht_bw(sta);
        if (new_bw != sta->sta.bandwidth) {
                sta->sta.bandwidth = new_bw;
-               changed |= IEEE80211_RC_NSS_CHANGED;
+               changed |= IEEE80211_RC_BW_CHANGED;
        }
 
  change:
index c04d401..6ee2b58 100644 (file)
@@ -28,7 +28,7 @@
 int ieee80211_wep_init(struct ieee80211_local *local)
 {
        /* start WEP IV from a random value */
-       get_random_bytes(&local->wep_iv, WEP_IV_LEN);
+       get_random_bytes(&local->wep_iv, IEEE80211_WEP_IV_LEN);
 
        local->wep_tx_tfm = crypto_alloc_cipher("arc4", 0, CRYPTO_ALG_ASYNC);
        if (IS_ERR(local->wep_tx_tfm)) {
@@ -98,20 +98,21 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local,
 
        hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
 
-       if (WARN_ON(skb_tailroom(skb) < WEP_ICV_LEN ||
-                   skb_headroom(skb) < WEP_IV_LEN))
+       if (WARN_ON(skb_tailroom(skb) < IEEE80211_WEP_ICV_LEN ||
+                   skb_headroom(skb) < IEEE80211_WEP_IV_LEN))
                return NULL;
 
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
-       newhdr = skb_push(skb, WEP_IV_LEN);
-       memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen);
+       newhdr = skb_push(skb, IEEE80211_WEP_IV_LEN);
+       memmove(newhdr, newhdr + IEEE80211_WEP_IV_LEN, hdrlen);
 
        /* the HW only needs room for the IV, but not the actual IV */
        if (info->control.hw_key &&
            (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
                return newhdr + hdrlen;
 
-       skb_set_network_header(skb, skb_network_offset(skb) + WEP_IV_LEN);
+       skb_set_network_header(skb, skb_network_offset(skb) +
+                                   IEEE80211_WEP_IV_LEN);
        ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen);
        return newhdr + hdrlen;
 }
@@ -125,8 +126,8 @@ static void ieee80211_wep_remove_iv(struct ieee80211_local *local,
        unsigned int hdrlen;
 
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
-       memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen);
-       skb_pull(skb, WEP_IV_LEN);
+       memmove(skb->data + IEEE80211_WEP_IV_LEN, skb->data, hdrlen);
+       skb_pull(skb, IEEE80211_WEP_IV_LEN);
 }
 
 
@@ -146,7 +147,7 @@ int ieee80211_wep_encrypt_data(struct crypto_cipher *tfm, u8 *rc4key,
        put_unaligned(icv, (__le32 *)(data + data_len));
 
        crypto_cipher_setkey(tfm, rc4key, klen);
-       for (i = 0; i < data_len + WEP_ICV_LEN; i++)
+       for (i = 0; i < data_len + IEEE80211_WEP_ICV_LEN; i++)
                crypto_cipher_encrypt_one(tfm, data + i, data + i);
 
        return 0;
@@ -172,7 +173,7 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local,
        if (!iv)
                return -1;
 
-       len = skb->len - (iv + WEP_IV_LEN - skb->data);
+       len = skb->len - (iv + IEEE80211_WEP_IV_LEN - skb->data);
 
        /* Prepend 24-bit IV to RC4 key */
        memcpy(rc4key, iv, 3);
@@ -181,10 +182,10 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local,
        memcpy(rc4key + 3, key, keylen);
 
        /* Add room for ICV */
-       skb_put(skb, WEP_ICV_LEN);
+       skb_put(skb, IEEE80211_WEP_ICV_LEN);
 
        return ieee80211_wep_encrypt_data(local->wep_tx_tfm, rc4key, keylen + 3,
-                                         iv + WEP_IV_LEN, len);
+                                         iv + IEEE80211_WEP_IV_LEN, len);
 }
 
 
@@ -201,11 +202,11 @@ int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key,
                return -1;
 
        crypto_cipher_setkey(tfm, rc4key, klen);
-       for (i = 0; i < data_len + WEP_ICV_LEN; i++)
+       for (i = 0; i < data_len + IEEE80211_WEP_ICV_LEN; i++)
                crypto_cipher_decrypt_one(tfm, data + i, data + i);
 
        crc = cpu_to_le32(~crc32_le(~0, data, data_len));
-       if (memcmp(&crc, data + data_len, WEP_ICV_LEN) != 0)
+       if (memcmp(&crc, data + data_len, IEEE80211_WEP_ICV_LEN) != 0)
                /* ICV mismatch */
                return -1;
 
@@ -237,10 +238,10 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local,
                return -1;
 
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
-       if (skb->len < hdrlen + WEP_IV_LEN + WEP_ICV_LEN)
+       if (skb->len < hdrlen + IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN)
                return -1;
 
-       len = skb->len - hdrlen - WEP_IV_LEN - WEP_ICV_LEN;
+       len = skb->len - hdrlen - IEEE80211_WEP_IV_LEN - IEEE80211_WEP_ICV_LEN;
 
        keyidx = skb->data[hdrlen + 3] >> 6;
 
@@ -256,16 +257,16 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local,
        memcpy(rc4key + 3, key->conf.key, key->conf.keylen);
 
        if (ieee80211_wep_decrypt_data(local->wep_rx_tfm, rc4key, klen,
-                                      skb->data + hdrlen + WEP_IV_LEN,
-                                      len))
+                                      skb->data + hdrlen +
+                                      IEEE80211_WEP_IV_LEN, len))
                ret = -1;
 
        /* Trim ICV */
-       skb_trim(skb, skb->len - WEP_ICV_LEN);
+       skb_trim(skb, skb->len - IEEE80211_WEP_ICV_LEN);
 
        /* Remove IV */
-       memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen);
-       skb_pull(skb, WEP_IV_LEN);
+       memmove(skb->data + IEEE80211_WEP_IV_LEN, skb->data, hdrlen);
+       skb_pull(skb, IEEE80211_WEP_IV_LEN);
 
        return ret;
 }
@@ -305,13 +306,14 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
                if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key))
                        return RX_DROP_UNUSABLE;
        } else if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
-               if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) + WEP_IV_LEN))
+               if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) +
+                                           IEEE80211_WEP_IV_LEN))
                        return RX_DROP_UNUSABLE;
                if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key))
                        rx->sta->wep_weak_iv_count++;
                ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
                /* remove ICV */
-               if (pskb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN))
+               if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
                        return RX_DROP_UNUSABLE;
        }
 
index c7c6d64..c9edfcb 100644 (file)
@@ -62,10 +62,10 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
 
        tail = MICHAEL_MIC_LEN;
        if (!info->control.hw_key)
-               tail += TKIP_ICV_LEN;
+               tail += IEEE80211_TKIP_ICV_LEN;
 
        if (WARN_ON(skb_tailroom(skb) < tail ||
-                   skb_headroom(skb) < TKIP_IV_LEN))
+                   skb_headroom(skb) < IEEE80211_TKIP_IV_LEN))
                return TX_DROP;
 
        key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY];
@@ -198,15 +198,16 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
        if (info->control.hw_key)
                tail = 0;
        else
-               tail = TKIP_ICV_LEN;
+               tail = IEEE80211_TKIP_ICV_LEN;
 
        if (WARN_ON(skb_tailroom(skb) < tail ||
-                   skb_headroom(skb) < TKIP_IV_LEN))
+                   skb_headroom(skb) < IEEE80211_TKIP_IV_LEN))
                return -1;
 
-       pos = skb_push(skb, TKIP_IV_LEN);
-       memmove(pos, pos + TKIP_IV_LEN, hdrlen);
-       skb_set_network_header(skb, skb_network_offset(skb) + TKIP_IV_LEN);
+       pos = skb_push(skb, IEEE80211_TKIP_IV_LEN);
+       memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen);
+       skb_set_network_header(skb, skb_network_offset(skb) +
+                                   IEEE80211_TKIP_IV_LEN);
        pos += hdrlen;
 
        /* the HW only needs room for the IV, but not the actual IV */
@@ -227,7 +228,7 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
                return 0;
 
        /* Add room for ICV */
-       skb_put(skb, TKIP_ICV_LEN);
+       skb_put(skb, IEEE80211_TKIP_ICV_LEN);
 
        return ieee80211_tkip_encrypt_data(tx->local->wep_tx_tfm,
                                           key, skb, pos, len);
@@ -290,11 +291,11 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
                return RX_DROP_UNUSABLE;
 
        /* Trim ICV */
-       skb_trim(skb, skb->len - TKIP_ICV_LEN);
+       skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
 
        /* Remove IV */
-       memmove(skb->data + TKIP_IV_LEN, skb->data, hdrlen);
-       skb_pull(skb, TKIP_IV_LEN);
+       memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen);
+       skb_pull(skb, IEEE80211_TKIP_IV_LEN);
 
        return RX_CONTINUE;
 }
@@ -337,9 +338,9 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
        else
                qos_tid = 0;
 
-       data_len = skb->len - hdrlen - CCMP_HDR_LEN;
+       data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN;
        if (encrypted)
-               data_len -= CCMP_MIC_LEN;
+               data_len -= IEEE80211_CCMP_MIC_LEN;
 
        /* First block, b_0 */
        b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
@@ -348,7 +349,7 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
         */
        b_0[1] = qos_tid | (mgmt << 4);
        memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
-       memcpy(&b_0[8], pn, CCMP_PN_LEN);
+       memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
        /* l(m) */
        put_unaligned_be16(data_len, &b_0[14]);
 
@@ -424,15 +425,16 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
        if (info->control.hw_key)
                tail = 0;
        else
-               tail = CCMP_MIC_LEN;
+               tail = IEEE80211_CCMP_MIC_LEN;
 
        if (WARN_ON(skb_tailroom(skb) < tail ||
-                   skb_headroom(skb) < CCMP_HDR_LEN))
+                   skb_headroom(skb) < IEEE80211_CCMP_HDR_LEN))
                return -1;
 
-       pos = skb_push(skb, CCMP_HDR_LEN);
-       memmove(pos, pos + CCMP_HDR_LEN, hdrlen);
-       skb_set_network_header(skb, skb_network_offset(skb) + CCMP_HDR_LEN);
+       pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN);
+       memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen);
+       skb_set_network_header(skb, skb_network_offset(skb) +
+                                   IEEE80211_CCMP_HDR_LEN);
 
        /* the HW only needs room for the IV, but not the actual IV */
        if (info->control.hw_key &&
@@ -457,10 +459,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
        if (info->control.hw_key)
                return 0;
 
-       pos += CCMP_HDR_LEN;
+       pos += IEEE80211_CCMP_HDR_LEN;
        ccmp_special_blocks(skb, pn, scratch, 0);
        ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len,
-                                 pos, skb_put(skb, CCMP_MIC_LEN));
+                                 pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN));
 
        return 0;
 }
@@ -490,7 +492,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
        struct ieee80211_key *key = rx->key;
        struct sk_buff *skb = rx->skb;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-       u8 pn[CCMP_PN_LEN];
+       u8 pn[IEEE80211_CCMP_PN_LEN];
        int data_len;
        int queue;
 
@@ -500,12 +502,13 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
            !ieee80211_is_robust_mgmt_frame(hdr))
                return RX_CONTINUE;
 
-       data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN;
+       data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN -
+                  IEEE80211_CCMP_MIC_LEN;
        if (!rx->sta || data_len < 0)
                return RX_DROP_UNUSABLE;
 
        if (status->flag & RX_FLAG_DECRYPTED) {
-               if (!pskb_may_pull(rx->skb, hdrlen + CCMP_HDR_LEN))
+               if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_CCMP_HDR_LEN))
                        return RX_DROP_UNUSABLE;
        } else {
                if (skb_linearize(rx->skb))
@@ -516,7 +519,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
 
        queue = rx->security_idx;
 
-       if (memcmp(pn, key->u.ccmp.rx_pn[queue], CCMP_PN_LEN) <= 0) {
+       if (memcmp(pn, key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN) <= 0) {
                key->u.ccmp.replays++;
                return RX_DROP_UNUSABLE;
        }
@@ -528,19 +531,20 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
 
                if (ieee80211_aes_ccm_decrypt(
                            key->u.ccmp.tfm, scratch,
-                           skb->data + hdrlen + CCMP_HDR_LEN, data_len,
-                           skb->data + skb->len - CCMP_MIC_LEN,
-                           skb->data + hdrlen + CCMP_HDR_LEN))
+                           skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
+                           data_len,
+                           skb->data + skb->len - IEEE80211_CCMP_MIC_LEN,
+                           skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN))
                        return RX_DROP_UNUSABLE;
        }
 
-       memcpy(key->u.ccmp.rx_pn[queue], pn, CCMP_PN_LEN);
+       memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
 
        /* Remove CCMP header and MIC */
-       if (pskb_trim(skb, skb->len - CCMP_MIC_LEN))
+       if (pskb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN))
                return RX_DROP_UNUSABLE;
-       memmove(skb->data + CCMP_HDR_LEN, skb->data, hdrlen);
-       skb_pull(skb, CCMP_HDR_LEN);
+       memmove(skb->data + IEEE80211_CCMP_HDR_LEN, skb->data, hdrlen);
+       skb_pull(skb, IEEE80211_CCMP_HDR_LEN);
 
        return RX_CONTINUE;
 }
diff --git a/net/mpls/Kconfig b/net/mpls/Kconfig
new file mode 100644 (file)
index 0000000..37421db
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# MPLS configuration
+#
+config NET_MPLS_GSO
+       tristate "MPLS: GSO support"
+       help
+        This is helper module to allow segmentation of non-MPLS GSO packets
+        that have had MPLS stack entries pushed onto them and thus
+        become MPLS GSO packets.
diff --git a/net/mpls/Makefile b/net/mpls/Makefile
new file mode 100644 (file)
index 0000000..0a3c171
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# Makefile for MPLS.
+#
+obj-y += mpls_gso.o
diff --git a/net/mpls/mpls_gso.c b/net/mpls/mpls_gso.c
new file mode 100644 (file)
index 0000000..1bec121
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ *     MPLS GSO Support
+ *
+ *     Authors: Simon Horman (horms@verge.net.au)
+ *
+ *     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.
+ *
+ *     Based on: GSO portions of net/ipv4/gre.c
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/netdev_features.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+static struct sk_buff *mpls_gso_segment(struct sk_buff *skb,
+                                      netdev_features_t features)
+{
+       struct sk_buff *segs = ERR_PTR(-EINVAL);
+       netdev_features_t mpls_features;
+       __be16 mpls_protocol;
+
+       if (unlikely(skb_shinfo(skb)->gso_type &
+                               ~(SKB_GSO_TCPV4 |
+                                 SKB_GSO_TCPV6 |
+                                 SKB_GSO_UDP |
+                                 SKB_GSO_DODGY |
+                                 SKB_GSO_TCP_ECN |
+                                 SKB_GSO_GRE |
+                                 SKB_GSO_MPLS)))
+               goto out;
+
+       /* Setup inner SKB. */
+       mpls_protocol = skb->protocol;
+       skb->protocol = skb->inner_protocol;
+
+       /* Push back the mac header that skb_mac_gso_segment() has pulled.
+        * It will be re-pulled by the call to skb_mac_gso_segment() below
+        */
+       __skb_push(skb, skb->mac_len);
+
+       /* Segment inner packet. */
+       mpls_features = skb->dev->mpls_features & netif_skb_features(skb);
+       segs = skb_mac_gso_segment(skb, mpls_features);
+
+
+       /* Restore outer protocol. */
+       skb->protocol = mpls_protocol;
+
+       /* Re-pull the mac header that the call to skb_mac_gso_segment()
+        * above pulled.  It will be re-pushed after returning
+        * skb_mac_gso_segment(), an indirect caller of this function.
+        */
+       __skb_push(skb, skb->data - skb_mac_header(skb));
+
+out:
+       return segs;
+}
+
+static int mpls_gso_send_check(struct sk_buff *skb)
+{
+       return 0;
+}
+
+static struct packet_offload mpls_mc_offload = {
+       .type = cpu_to_be16(ETH_P_MPLS_MC),
+       .callbacks = {
+               .gso_send_check =       mpls_gso_send_check,
+               .gso_segment    =       mpls_gso_segment,
+       },
+};
+
+static struct packet_offload mpls_uc_offload = {
+       .type = cpu_to_be16(ETH_P_MPLS_UC),
+       .callbacks = {
+               .gso_send_check =       mpls_gso_send_check,
+               .gso_segment    =       mpls_gso_segment,
+       },
+};
+
+static int __init mpls_gso_init(void)
+{
+       pr_info("MPLS GSO support\n");
+
+       dev_add_offload(&mpls_uc_offload);
+       dev_add_offload(&mpls_mc_offload);
+
+       return 0;
+}
+
+static void __exit mpls_gso_exit(void)
+{
+       dev_remove_offload(&mpls_uc_offload);
+       dev_remove_offload(&mpls_mc_offload);
+}
+
+module_init(mpls_gso_init);
+module_exit(mpls_gso_exit);
+
+MODULE_DESCRIPTION("MPLS GSO support");
+MODULE_AUTHOR("Simon Horman (horms@verge.net.au)");
+MODULE_LICENSE("GPL");
index 857ca9f..2217363 100644 (file)
@@ -304,17 +304,26 @@ static struct pernet_operations netfilter_net_ops = {
        .exit = netfilter_net_exit,
 };
 
-void __init netfilter_init(void)
+int __init netfilter_init(void)
 {
-       int i, h;
+       int i, h, ret;
+
        for (i = 0; i < ARRAY_SIZE(nf_hooks); i++) {
                for (h = 0; h < NF_MAX_HOOKS; h++)
                        INIT_LIST_HEAD(&nf_hooks[i][h]);
        }
 
-       if (register_pernet_subsys(&netfilter_net_ops) < 0)
-               panic("cannot create netfilter proc entry");
+       ret = register_pernet_subsys(&netfilter_net_ops);
+       if (ret < 0)
+               goto err;
+
+       ret = netfilter_log_init();
+       if (ret < 0)
+               goto err_pernet;
 
-       if (netfilter_log_init() < 0)
-               panic("cannot initialize nf_log");
+       return 0;
+err_pernet:
+       unregister_pernet_subsys(&netfilter_net_ops);
+err:
+       return ret;
 }
index a083bda..4c8e5c0 100644 (file)
@@ -975,8 +975,7 @@ static void *ip_vs_conn_array(struct seq_file *seq, loff_t pos)
                                return cp;
                        }
                }
-               rcu_read_unlock();
-               rcu_read_lock();
+               cond_resched_rcu();
        }
 
        return NULL;
@@ -1015,8 +1014,7 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos)
                        iter->l = &ip_vs_conn_tab[idx];
                        return cp;
                }
-               rcu_read_unlock();
-               rcu_read_lock();
+               cond_resched_rcu();
        }
        iter->l = NULL;
        return NULL;
@@ -1206,17 +1204,13 @@ void ip_vs_random_dropentry(struct net *net)
        int idx;
        struct ip_vs_conn *cp, *cp_c;
 
+       rcu_read_lock();
        /*
         * Randomly scan 1/32 of the whole table every second
         */
        for (idx = 0; idx < (ip_vs_conn_tab_size>>5); idx++) {
                unsigned int hash = net_random() & ip_vs_conn_tab_mask;
 
-               /*
-                *  Lock is actually needed in this loop.
-                */
-               rcu_read_lock();
-
                hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) {
                        if (cp->flags & IP_VS_CONN_F_TEMPLATE)
                                /* connection template */
@@ -1237,6 +1231,18 @@ void ip_vs_random_dropentry(struct net *net)
                                default:
                                        continue;
                                }
+                       } else if (cp->protocol == IPPROTO_SCTP) {
+                               switch (cp->state) {
+                               case IP_VS_SCTP_S_INIT1:
+                               case IP_VS_SCTP_S_INIT:
+                                       break;
+                               case IP_VS_SCTP_S_ESTABLISHED:
+                                       if (todrop_entry(cp))
+                                               break;
+                                       continue;
+                               default:
+                                       continue;
+                               }
                        } else {
                                if (!todrop_entry(cp))
                                        continue;
@@ -1252,8 +1258,9 @@ void ip_vs_random_dropentry(struct net *net)
                                __ip_vs_conn_put(cp);
                        }
                }
-               rcu_read_unlock();
+               cond_resched_rcu();
        }
+       rcu_read_unlock();
 }
 
 
@@ -1267,11 +1274,8 @@ static void ip_vs_conn_flush(struct net *net)
        struct netns_ipvs *ipvs = net_ipvs(net);
 
 flush_again:
+       rcu_read_lock();
        for (idx = 0; idx < ip_vs_conn_tab_size; idx++) {
-               /*
-                *  Lock is actually needed in this loop.
-                */
-               rcu_read_lock();
 
                hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[idx], c_list) {
                        if (!ip_vs_conn_net_eq(cp, net))
@@ -1286,8 +1290,9 @@ flush_again:
                                __ip_vs_conn_put(cp);
                        }
                }
-               rcu_read_unlock();
+               cond_resched_rcu();
        }
+       rcu_read_unlock();
 
        /* the counter may be not NULL, because maybe some conn entries
           are run by slow timer handler or unhashed but still referred */
index 23b8eb5..4f69e83 100644 (file)
@@ -305,7 +305,7 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
                 * return *ignored=0 i.e. ICMP and NF_DROP
                 */
                sched = rcu_dereference(svc->scheduler);
-               dest = sched->schedule(svc, skb);
+               dest = sched->schedule(svc, skb, iph);
                if (!dest) {
                        IP_VS_DBG(1, "p-schedule: no dest found.\n");
                        kfree(param.pe_data);
@@ -452,7 +452,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
        }
 
        sched = rcu_dereference(svc->scheduler);
-       dest = sched->schedule(svc, skb);
+       dest = sched->schedule(svc, skb, iph);
        if (dest == NULL) {
                IP_VS_DBG(1, "Schedule: no dest found.\n");
                return NULL;
index 9e6c2a0..c8148e4 100644 (file)
@@ -1487,9 +1487,9 @@ ip_vs_forget_dev(struct ip_vs_dest *dest, struct net_device *dev)
  * Currently only NETDEV_DOWN is handled to release refs to cached dsts
  */
 static int ip_vs_dst_event(struct notifier_block *this, unsigned long event,
-                           void *ptr)
+                          void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net *net = dev_net(dev);
        struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_service *svc;
@@ -1575,7 +1575,7 @@ static int zero;
 static int three = 3;
 
 static int
-proc_do_defense_mode(ctl_table *table, int write,
+proc_do_defense_mode(struct ctl_table *table, int write,
                     void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net = current->nsproxy->net_ns;
@@ -1596,7 +1596,7 @@ proc_do_defense_mode(ctl_table *table, int write,
 }
 
 static int
-proc_do_sync_threshold(ctl_table *table, int write,
+proc_do_sync_threshold(struct ctl_table *table, int write,
                       void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = table->data;
@@ -1616,7 +1616,7 @@ proc_do_sync_threshold(ctl_table *table, int write,
 }
 
 static int
-proc_do_sync_mode(ctl_table *table, int write,
+proc_do_sync_mode(struct ctl_table *table, int write,
                     void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = table->data;
@@ -1634,7 +1634,7 @@ proc_do_sync_mode(ctl_table *table, int write,
 }
 
 static int
-proc_do_sync_ports(ctl_table *table, int write,
+proc_do_sync_ports(struct ctl_table *table, int write,
                   void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = table->data;
@@ -1715,11 +1715,17 @@ static struct ctl_table vs_vars[] = {
                .proc_handler   = &proc_do_sync_ports,
        },
        {
-               .procname       = "sync_qlen_max",
+               .procname       = "sync_persist_mode",
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
+       {
+               .procname       = "sync_qlen_max",
+               .maxlen         = sizeof(unsigned long),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
        {
                .procname       = "sync_sock_size",
                .maxlen         = sizeof(int),
@@ -1738,6 +1744,18 @@ static struct ctl_table vs_vars[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
+       {
+               .procname       = "sloppy_tcp",
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "sloppy_sctp",
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
        {
                .procname       = "expire_quiescent_template",
                .maxlen         = sizeof(int),
@@ -3717,12 +3735,15 @@ static int __net_init ip_vs_control_net_init_sysctl(struct net *net)
        tbl[idx++].data = &ipvs->sysctl_sync_ver;
        ipvs->sysctl_sync_ports = 1;
        tbl[idx++].data = &ipvs->sysctl_sync_ports;
+       tbl[idx++].data = &ipvs->sysctl_sync_persist_mode;
        ipvs->sysctl_sync_qlen_max = nr_free_buffer_pages() / 32;
        tbl[idx++].data = &ipvs->sysctl_sync_qlen_max;
        ipvs->sysctl_sync_sock_size = 0;
        tbl[idx++].data = &ipvs->sysctl_sync_sock_size;
        tbl[idx++].data = &ipvs->sysctl_cache_bypass;
        tbl[idx++].data = &ipvs->sysctl_expire_nodest_conn;
+       tbl[idx++].data = &ipvs->sysctl_sloppy_tcp;
+       tbl[idx++].data = &ipvs->sysctl_sloppy_sctp;
        tbl[idx++].data = &ipvs->sysctl_expire_quiescent_template;
        ipvs->sysctl_sync_threshold[0] = DEFAULT_SYNC_THRESHOLD;
        ipvs->sysctl_sync_threshold[1] = DEFAULT_SYNC_PERIOD;
index ccab120..c3b8454 100644 (file)
@@ -214,18 +214,16 @@ static inline int is_overloaded(struct ip_vs_dest *dest)
  *      Destination hashing scheduling
  */
 static struct ip_vs_dest *
-ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
+ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
+                 struct ip_vs_iphdr *iph)
 {
        struct ip_vs_dest *dest;
        struct ip_vs_dh_state *s;
-       struct ip_vs_iphdr iph;
-
-       ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
 
        IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
 
        s = (struct ip_vs_dh_state *) svc->sched_data;
-       dest = ip_vs_dh_get(svc->af, s, &iph.daddr);
+       dest = ip_vs_dh_get(svc->af, s, &iph->daddr);
        if (!dest
            || !(dest->flags & IP_VS_DEST_F_AVAILABLE)
            || atomic_read(&dest->weight) <= 0
@@ -235,7 +233,7 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
        }
 
        IP_VS_DBG_BUF(6, "DH: destination IP address %s --> server %s:%d\n",
-                     IP_VS_DBG_ADDR(svc->af, &iph.daddr),
+                     IP_VS_DBG_ADDR(svc->af, &iph->daddr),
                      IP_VS_DBG_ADDR(svc->af, &dest->addr),
                      ntohs(dest->port));
 
index 5ea26bd..1383b0e 100644 (file)
@@ -118,7 +118,7 @@ struct ip_vs_lblc_table {
  *      IPVS LBLC sysctl table
  */
 #ifdef CONFIG_SYSCTL
-static ctl_table vs_vars_table[] = {
+static struct ctl_table vs_vars_table[] = {
        {
                .procname       = "lblc_expiration",
                .data           = NULL,
@@ -487,19 +487,17 @@ is_overloaded(struct ip_vs_dest *dest, struct ip_vs_service *svc)
  *    Locality-Based (weighted) Least-Connection scheduling
  */
 static struct ip_vs_dest *
-ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
+ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
+                   struct ip_vs_iphdr *iph)
 {
        struct ip_vs_lblc_table *tbl = svc->sched_data;
-       struct ip_vs_iphdr iph;
        struct ip_vs_dest *dest = NULL;
        struct ip_vs_lblc_entry *en;
 
-       ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
-
        IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
 
        /* First look in our cache */
-       en = ip_vs_lblc_get(svc->af, tbl, &iph.daddr);
+       en = ip_vs_lblc_get(svc->af, tbl, &iph->daddr);
        if (en) {
                /* We only hold a read lock, but this is atomic */
                en->lastuse = jiffies;
@@ -529,12 +527,12 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
        /* If we fail to create a cache entry, we'll just use the valid dest */
        spin_lock_bh(&svc->sched_lock);
        if (!tbl->dead)
-               ip_vs_lblc_new(tbl, &iph.daddr, dest);
+               ip_vs_lblc_new(tbl, &iph->daddr, dest);
        spin_unlock_bh(&svc->sched_lock);
 
 out:
        IP_VS_DBG_BUF(6, "LBLC: destination IP address %s --> server %s:%d\n",
-                     IP_VS_DBG_ADDR(svc->af, &iph.daddr),
+                     IP_VS_DBG_ADDR(svc->af, &iph->daddr),
                      IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port));
 
        return dest;
index 50123c2..3cd85b2 100644 (file)
@@ -299,7 +299,7 @@ struct ip_vs_lblcr_table {
  *      IPVS LBLCR sysctl table
  */
 
-static ctl_table vs_vars_table[] = {
+static struct ctl_table vs_vars_table[] = {
        {
                .procname       = "lblcr_expiration",
                .data           = NULL,
@@ -655,19 +655,17 @@ is_overloaded(struct ip_vs_dest *dest, struct ip_vs_service *svc)
  *    Locality-Based (weighted) Least-Connection scheduling
  */
 static struct ip_vs_dest *
-ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
+ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
+                    struct ip_vs_iphdr *iph)
 {
        struct ip_vs_lblcr_table *tbl = svc->sched_data;
-       struct ip_vs_iphdr iph;
        struct ip_vs_dest *dest;
        struct ip_vs_lblcr_entry *en;
 
-       ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
-
        IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
 
        /* First look in our cache */
-       en = ip_vs_lblcr_get(svc->af, tbl, &iph.daddr);
+       en = ip_vs_lblcr_get(svc->af, tbl, &iph->daddr);
        if (en) {
                en->lastuse = jiffies;
 
@@ -718,12 +716,12 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
        /* If we fail to create a cache entry, we'll just use the valid dest */
        spin_lock_bh(&svc->sched_lock);
        if (!tbl->dead)
-               ip_vs_lblcr_new(tbl, &iph.daddr, dest);
+               ip_vs_lblcr_new(tbl, &iph->daddr, dest);
        spin_unlock_bh(&svc->sched_lock);
 
 out:
        IP_VS_DBG_BUF(6, "LBLCR: destination IP address %s --> server %s:%d\n",
-                     IP_VS_DBG_ADDR(svc->af, &iph.daddr),
+                     IP_VS_DBG_ADDR(svc->af, &iph->daddr),
                      IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port));
 
        return dest;
index 5128e33..2bdcb1c 100644 (file)
@@ -26,7 +26,8 @@
  *     Least Connection scheduling
  */
 static struct ip_vs_dest *
-ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
+ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
+                 struct ip_vs_iphdr *iph)
 {
        struct ip_vs_dest *dest, *least = NULL;
        unsigned int loh = 0, doh;
index 646cfd4..d8d9860 100644 (file)
@@ -55,7 +55,8 @@ ip_vs_nq_dest_overhead(struct ip_vs_dest *dest)
  *     Weighted Least Connection scheduling
  */
 static struct ip_vs_dest *
-ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
+ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
+                 struct ip_vs_iphdr *iph)
 {
        struct ip_vs_dest *dest, *least = NULL;
        unsigned int loh = 0, doh;
index 8646488..3c0da87 100644 (file)
@@ -15,6 +15,7 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
 {
        struct net *net;
        struct ip_vs_service *svc;
+       struct netns_ipvs *ipvs;
        sctp_chunkhdr_t _schunkh, *sch;
        sctp_sctphdr_t *sh, _sctph;
 
@@ -27,13 +28,14 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
        if (sch == NULL)
                return 0;
        net = skb_net(skb);
+       ipvs = net_ipvs(net);
        rcu_read_lock();
-       if ((sch->type == SCTP_CID_INIT) &&
+       if ((sch->type == SCTP_CID_INIT || sysctl_sloppy_sctp(ipvs)) &&
            (svc = ip_vs_service_find(net, af, skb->mark, iph->protocol,
                                      &iph->daddr, sh->dest))) {
                int ignored;
 
-               if (ip_vs_todrop(net_ipvs(net))) {
+               if (ip_vs_todrop(ipvs)) {
                        /*
                         * It seems that we are very loaded.
                         * We have to drop this packet :(
@@ -183,710 +185,159 @@ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
        return 1;
 }
 
-struct ipvs_sctp_nextstate {
-       int next_state;
-};
 enum ipvs_sctp_event_t {
-       IP_VS_SCTP_EVE_DATA_CLI,
-       IP_VS_SCTP_EVE_DATA_SER,
-       IP_VS_SCTP_EVE_INIT_CLI,
-       IP_VS_SCTP_EVE_INIT_SER,
-       IP_VS_SCTP_EVE_INIT_ACK_CLI,
-       IP_VS_SCTP_EVE_INIT_ACK_SER,
-       IP_VS_SCTP_EVE_COOKIE_ECHO_CLI,
-       IP_VS_SCTP_EVE_COOKIE_ECHO_SER,
-       IP_VS_SCTP_EVE_COOKIE_ACK_CLI,
-       IP_VS_SCTP_EVE_COOKIE_ACK_SER,
-       IP_VS_SCTP_EVE_ABORT_CLI,
-       IP_VS_SCTP_EVE__ABORT_SER,
-       IP_VS_SCTP_EVE_SHUT_CLI,
-       IP_VS_SCTP_EVE_SHUT_SER,
-       IP_VS_SCTP_EVE_SHUT_ACK_CLI,
-       IP_VS_SCTP_EVE_SHUT_ACK_SER,
-       IP_VS_SCTP_EVE_SHUT_COM_CLI,
-       IP_VS_SCTP_EVE_SHUT_COM_SER,
-       IP_VS_SCTP_EVE_LAST
+       IP_VS_SCTP_DATA = 0,            /* DATA, SACK, HEARTBEATs */
+       IP_VS_SCTP_INIT,
+       IP_VS_SCTP_INIT_ACK,
+       IP_VS_SCTP_COOKIE_ECHO,
+       IP_VS_SCTP_COOKIE_ACK,
+       IP_VS_SCTP_SHUTDOWN,
+       IP_VS_SCTP_SHUTDOWN_ACK,
+       IP_VS_SCTP_SHUTDOWN_COMPLETE,
+       IP_VS_SCTP_ERROR,
+       IP_VS_SCTP_ABORT,
+       IP_VS_SCTP_EVENT_LAST
 };
 
-static enum ipvs_sctp_event_t sctp_events[256] = {
-       IP_VS_SCTP_EVE_DATA_CLI,
-       IP_VS_SCTP_EVE_INIT_CLI,
-       IP_VS_SCTP_EVE_INIT_ACK_CLI,
-       IP_VS_SCTP_EVE_DATA_CLI,
-       IP_VS_SCTP_EVE_DATA_CLI,
-       IP_VS_SCTP_EVE_DATA_CLI,
-       IP_VS_SCTP_EVE_ABORT_CLI,
-       IP_VS_SCTP_EVE_SHUT_CLI,
-       IP_VS_SCTP_EVE_SHUT_ACK_CLI,
-       IP_VS_SCTP_EVE_DATA_CLI,
-       IP_VS_SCTP_EVE_COOKIE_ECHO_CLI,
-       IP_VS_SCTP_EVE_COOKIE_ACK_CLI,
-       IP_VS_SCTP_EVE_DATA_CLI,
-       IP_VS_SCTP_EVE_DATA_CLI,
-       IP_VS_SCTP_EVE_SHUT_COM_CLI,
+/* RFC 2960, 3.2 Chunk Field Descriptions */
+static __u8 sctp_events[] = {
+       [SCTP_CID_DATA]                 = IP_VS_SCTP_DATA,
+       [SCTP_CID_INIT]                 = IP_VS_SCTP_INIT,
+       [SCTP_CID_INIT_ACK]             = IP_VS_SCTP_INIT_ACK,
+       [SCTP_CID_SACK]                 = IP_VS_SCTP_DATA,
+       [SCTP_CID_HEARTBEAT]            = IP_VS_SCTP_DATA,
+       [SCTP_CID_HEARTBEAT_ACK]        = IP_VS_SCTP_DATA,
+       [SCTP_CID_ABORT]                = IP_VS_SCTP_ABORT,
+       [SCTP_CID_SHUTDOWN]             = IP_VS_SCTP_SHUTDOWN,
+       [SCTP_CID_SHUTDOWN_ACK]         = IP_VS_SCTP_SHUTDOWN_ACK,
+       [SCTP_CID_ERROR]                = IP_VS_SCTP_ERROR,
+       [SCTP_CID_COOKIE_ECHO]          = IP_VS_SCTP_COOKIE_ECHO,
+       [SCTP_CID_COOKIE_ACK]           = IP_VS_SCTP_COOKIE_ACK,
+       [SCTP_CID_ECN_ECNE]             = IP_VS_SCTP_DATA,
+       [SCTP_CID_ECN_CWR]              = IP_VS_SCTP_DATA,
+       [SCTP_CID_SHUTDOWN_COMPLETE]    = IP_VS_SCTP_SHUTDOWN_COMPLETE,
 };
 
-static struct ipvs_sctp_nextstate
- sctp_states_table[IP_VS_SCTP_S_LAST][IP_VS_SCTP_EVE_LAST] = {
-       /*
-        * STATE : IP_VS_SCTP_S_NONE
-        */
-       /*next state *//*event */
-       {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ },
-        },
-       /*
-        * STATE : IP_VS_SCTP_S_INIT_CLI
-        * Cient sent INIT and is waiting for reply from server(In ECHO_WAIT)
-        */
-       {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        {IP_VS_SCTP_S_INIT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ECHO_CLI */ },
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_ECHO_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        },
-       /*
-        * State : IP_VS_SCTP_S_INIT_SER
-        * Server sent INIT and waiting for INIT ACK from the client
-        */
-       {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        {IP_VS_SCTP_S_INIT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        },
-       /*
-        * State : IP_VS_SCTP_S_INIT_ACK_CLI
-        * Client sent INIT ACK and waiting for ECHO from the server
-        */
-       {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
-        /*
-         * We have got an INIT from client. From the spec.“Upon receipt of
-         * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
-         * an INIT ACK using the same parameters it sent in its  original
-         * INIT chunk (including its Initiate Tag, unchanged”).
-         */
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        /*
-         * INIT_ACK has been resent by the client, let us stay is in
-         * the same state
-         */
-        {IP_VS_SCTP_S_INIT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        /*
-         * INIT_ACK sent by the server, close the connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        /*
-         * ECHO by client, it should not happen, close the connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        /*
-         * ECHO by server, this is what we are expecting, move to ECHO_SER
-         */
-        {IP_VS_SCTP_S_ECHO_SER /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        /*
-         * COOKIE ACK from client, it should not happen, close the connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        /*
-         * Unexpected COOKIE ACK from server, staty in the same state
-         */
-        {IP_VS_SCTP_S_INIT_ACK_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        },
-       /*
-        * State : IP_VS_SCTP_S_INIT_ACK_SER
-        * Server sent INIT ACK and waiting for ECHO from the client
-        */
-       {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
-        /*
-         * We have got an INIT from client. From the spec.“Upon receipt of
-         * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
-         * an INIT ACK using the same parameters it sent in its  original
-         * INIT chunk (including its Initiate Tag, unchanged”).
-         */
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        /*
-         * Unexpected INIT_ACK by the client, let us close the connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        /*
-         * INIT_ACK resent by the server, let us move to same state
-         */
-        {IP_VS_SCTP_S_INIT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        /*
-         * Client send the ECHO, this is what we are expecting,
-         * move to ECHO_CLI
-         */
-        {IP_VS_SCTP_S_ECHO_CLI /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        /*
-         * ECHO received from the server, Not sure what to do,
-         * let us close it
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        /*
-         * COOKIE ACK from client, let us stay in the same state
-         */
-        {IP_VS_SCTP_S_INIT_ACK_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        /*
-         * COOKIE ACK from server, hmm... this should not happen, lets close
-         * the connection.
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        },
-       /*
-        * State : IP_VS_SCTP_S_ECHO_CLI
-        * Cient  sent ECHO and waiting COOKEI ACK from the Server
-        */
-       {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
-        /*
-         * We have got an INIT from client. From the spec.“Upon receipt of
-         * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
-         * an INIT ACK using the same parameters it sent in its  original
-         * INIT chunk (including its Initiate Tag, unchanged”).
-         */
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        /*
-         * INIT_ACK has been by the client, let us close the connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        /*
-         * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
-         * “If an INIT ACK is received by an endpoint in any state other
-         * than the COOKIE-WAIT state, the endpoint should discard the
-         * INIT ACK chunk”. Stay in the same state
-         */
-        {IP_VS_SCTP_S_ECHO_CLI /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        /*
-         * Client resent the ECHO, let us stay in the same state
-         */
-        {IP_VS_SCTP_S_ECHO_CLI /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        /*
-         * ECHO received from the server, Not sure what to do,
-         * let us close it
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        /*
-         * COOKIE ACK from client, this shoud not happen, let's close the
-         * connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        /*
-         * COOKIE ACK from server, this is what we are awaiting,lets move to
-         * ESTABLISHED.
-         */
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        },
-       /*
-        * State : IP_VS_SCTP_S_ECHO_SER
-        * Server sent ECHO and waiting COOKEI ACK from the client
-        */
-       {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
-        /*
-         * We have got an INIT from client. From the spec.“Upon receipt of
-         * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
-         * an INIT ACK using the same parameters it sent in its  original
-         * INIT chunk (including its Initiate Tag, unchanged”).
-         */
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        /*
-         * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
-         * “If an INIT ACK is received by an endpoint in any state other
-         * than the COOKIE-WAIT state, the endpoint should discard the
-         * INIT ACK chunk”. Stay in the same state
-         */
-        {IP_VS_SCTP_S_ECHO_SER /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        /*
-         * INIT_ACK has been by the server, let us close the connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        /*
-         * Client sent the ECHO, not sure what to do, let's close the
-         * connection.
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        /*
-         * ECHO resent by the server, stay in the same state
-         */
-        {IP_VS_SCTP_S_ECHO_SER /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        /*
-         * COOKIE ACK from client, this is what we are expecting, let's move
-         * to ESTABLISHED.
-         */
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        /*
-         * COOKIE ACK from server, this should not happen, lets close the
-         * connection.
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        },
-       /*
-        * State : IP_VS_SCTP_S_ESTABLISHED
-        * Association established
-        */
-       {{IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_DATA_SER */ },
-        /*
-         * We have got an INIT from client. From the spec.“Upon receipt of
-         * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
-         * an INIT ACK using the same parameters it sent in its  original
-         * INIT chunk (including its Initiate Tag, unchanged”).
-         */
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        /*
-         * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
-         * “If an INIT ACK is received by an endpoint in any state other
-         * than the COOKIE-WAIT state, the endpoint should discard the
-         * INIT ACK chunk”. Stay in the same state
-         */
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        /*
-         * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
-         * peer and peer shall move to the ESTABISHED. if it doesn't handle
-         * it will send ERROR chunk. So, stay in the same state
-         */
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        /*
-         * COOKIE ACK from client, not sure what to do stay in the same state
-         */
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        /*
-         * SHUTDOWN from the client, move to SHUDDOWN_CLI
-         */
-        {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        /*
-         * SHUTDOWN from the server, move to SHUTDOWN_SER
-         */
-        {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        /*
-         * client sent SHUDTDOWN_ACK, this should not happen, let's close
-         * the connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        },
-       /*
-        * State : IP_VS_SCTP_S_SHUT_CLI
-        * SHUTDOWN sent from the client, waitinf for SHUT ACK from the server
-        */
-       /*
-        * We received the data chuck, keep the state unchanged. I assume
-        * that still data chuncks  can be received by both the peers in
-        * SHUDOWN state
-        */
-
-       {{IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_DATA_SER */ },
-        /*
-         * We have got an INIT from client. From the spec.“Upon receipt of
-         * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
-         * an INIT ACK using the same parameters it sent in its  original
-         * INIT chunk (including its Initiate Tag, unchanged”).
-         */
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        /*
-         * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
-         * “If an INIT ACK is received by an endpoint in any state other
-         * than the COOKIE-WAIT state, the endpoint should discard the
-         * INIT ACK chunk”. Stay in the same state
-         */
-        {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        /*
-         * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
-         * peer and peer shall move to the ESTABISHED. if it doesn't handle
-         * it will send ERROR chunk. So, stay in the same state
-         */
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        /*
-         * COOKIE ACK from client, not sure what to do stay in the same state
-         */
-        {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        /*
-         * SHUTDOWN resent from the client, move to SHUDDOWN_CLI
-         */
-        {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        /*
-         * SHUTDOWN from the server, move to SHUTDOWN_SER
-         */
-        {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        /*
-         * client sent SHUDTDOWN_ACK, this should not happen, let's close
-         * the connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        /*
-         * Server sent SHUTDOWN ACK, this is what we are expecting, let's move
-         * to SHUDOWN_ACK_SER
-         */
-        {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        /*
-         * SHUTDOWN COM from client, this should not happen, let's close the
-         * connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        },
-       /*
-        * State : IP_VS_SCTP_S_SHUT_SER
-        * SHUTDOWN sent from the server, waitinf for SHUTDOWN ACK from client
-        */
-       /*
-        * We received the data chuck, keep the state unchanged. I assume
-        * that still data chuncks  can be received by both the peers in
-        * SHUDOWN state
-        */
-
-       {{IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_DATA_SER */ },
-        /*
-         * We have got an INIT from client. From the spec.“Upon receipt of
-         * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
-         * an INIT ACK using the same parameters it sent in its  original
-         * INIT chunk (including its Initiate Tag, unchanged”).
-         */
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        /*
-         * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
-         * “If an INIT ACK is received by an endpoint in any state other
-         * than the COOKIE-WAIT state, the endpoint should discard the
-         * INIT ACK chunk”. Stay in the same state
-         */
-        {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        /*
-         * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
-         * peer and peer shall move to the ESTABISHED. if it doesn't handle
-         * it will send ERROR chunk. So, stay in the same state
-         */
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        /*
-         * COOKIE ACK from client, not sure what to do stay in the same state
-         */
-        {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        /*
-         * SHUTDOWN resent from the client, move to SHUDDOWN_CLI
-         */
-        {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        /*
-         * SHUTDOWN resent from the server, move to SHUTDOWN_SER
-         */
-        {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        /*
-         * client sent SHUDTDOWN_ACK, this is what we are expecting, let's
-         * move to SHUT_ACK_CLI
-         */
-        {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        /*
-         * Server sent SHUTDOWN ACK, this should not happen, let's close the
-         * connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        /*
-         * SHUTDOWN COM from client, this should not happen, let's close the
-         * connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        },
-
-       /*
-        * State : IP_VS_SCTP_S_SHUT_ACK_CLI
-        * SHUTDOWN ACK from the client, awaiting for SHUTDOWN COM from server
-        */
-       /*
-        * We received the data chuck, keep the state unchanged. I assume
-        * that still data chuncks  can be received by both the peers in
-        * SHUDOWN state
-        */
-
-       {{IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_DATA_SER */ },
-        /*
-         * We have got an INIT from client. From the spec.“Upon receipt of
-         * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
-         * an INIT ACK using the same parameters it sent in its  original
-         * INIT chunk (including its Initiate Tag, unchanged”).
-         */
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        /*
-         * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
-         * “If an INIT ACK is received by an endpoint in any state other
-         * than the COOKIE-WAIT state, the endpoint should discard the
-         * INIT ACK chunk”. Stay in the same state
-         */
-        {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        /*
-         * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
-         * peer and peer shall move to the ESTABISHED. if it doesn't handle
-         * it will send ERROR chunk. So, stay in the same state
-         */
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        /*
-         * COOKIE ACK from client, not sure what to do stay in the same state
-         */
-        {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        /*
-         * SHUTDOWN sent from the client, move to SHUDDOWN_CLI
-         */
-        {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        /*
-         * SHUTDOWN sent from the server, move to SHUTDOWN_SER
-         */
-        {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        /*
-         * client resent SHUDTDOWN_ACK, let's stay in the same state
-         */
-        {IP_VS_SCTP_S_SHUT_ACK_CLI /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        /*
-         * Server sent SHUTDOWN ACK, this should not happen, let's close the
-         * connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        /*
-         * SHUTDOWN COM from client, this should not happen, let's close the
-         * connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        /*
-         * SHUTDOWN COMPLETE from server this is what we are expecting.
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        },
-
-       /*
-        * State : IP_VS_SCTP_S_SHUT_ACK_SER
-        * SHUTDOWN ACK from the server, awaiting for SHUTDOWN COM from client
-        */
-       /*
-        * We received the data chuck, keep the state unchanged. I assume
-        * that still data chuncks  can be received by both the peers in
-        * SHUDOWN state
-        */
+/* SCTP States:
+ * See RFC 2960, 4. SCTP Association State Diagram
+ *
+ * New states (not in diagram):
+ * - INIT1 state: use shorter timeout for dropped INIT packets
+ * - REJECTED state: use shorter timeout if INIT is rejected with ABORT
+ * - INIT, COOKIE_SENT, COOKIE_REPLIED, COOKIE states: for better debugging
+ *
+ * The states are as seen in real server. In the diagram, INIT1, INIT,
+ * COOKIE_SENT and COOKIE_REPLIED processing happens in CLOSED state.
+ *
+ * States as per packets from client (C) and server (S):
+ *
+ * Setup of client connection:
+ * IP_VS_SCTP_S_INIT1: First C:INIT sent, wait for S:INIT-ACK
+ * IP_VS_SCTP_S_INIT: Next C:INIT sent, wait for S:INIT-ACK
+ * IP_VS_SCTP_S_COOKIE_SENT: S:INIT-ACK sent, wait for C:COOKIE-ECHO
+ * IP_VS_SCTP_S_COOKIE_REPLIED: C:COOKIE-ECHO sent, wait for S:COOKIE-ACK
+ *
+ * Setup of server connection:
+ * IP_VS_SCTP_S_COOKIE_WAIT: S:INIT sent, wait for C:INIT-ACK
+ * IP_VS_SCTP_S_COOKIE: C:INIT-ACK sent, wait for S:COOKIE-ECHO
+ * IP_VS_SCTP_S_COOKIE_ECHOED: S:COOKIE-ECHO sent, wait for C:COOKIE-ACK
+ */
 
-       {{IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_DATA_SER */ },
-        /*
-         * We have got an INIT from client. From the spec.“Upon receipt of
-         * an INIT in the COOKIE-WAIT state, an endpoint MUST respond with
-         * an INIT ACK using the same parameters it sent in its  original
-         * INIT chunk (including its Initiate Tag, unchanged”).
-         */
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        /*
-         * INIT_ACK sent by the server, Unexpected INIT ACK, spec says,
-         * “If an INIT ACK is received by an endpoint in any state other
-         * than the COOKIE-WAIT state, the endpoint should discard the
-         * INIT ACK chunk”. Stay in the same state
-         */
-        {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        /*
-         * Client sent ECHO, Spec(sec 5.2.4) says it may be handled by the
-         * peer and peer shall move to the ESTABISHED. if it doesn't handle
-         * it will send ERROR chunk. So, stay in the same state
-         */
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        {IP_VS_SCTP_S_ESTABLISHED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        /*
-         * COOKIE ACK from client, not sure what to do stay in the same state
-         */
-        {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        /*
-         * SHUTDOWN sent from the client, move to SHUDDOWN_CLI
-         */
-        {IP_VS_SCTP_S_SHUT_CLI /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        /*
-         * SHUTDOWN sent from the server, move to SHUTDOWN_SER
-         */
-        {IP_VS_SCTP_S_SHUT_SER /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        /*
-         * client sent SHUDTDOWN_ACK, this should not happen let's close
-         * the connection.
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        /*
-         * Server resent SHUTDOWN ACK, stay in the same state
-         */
-        {IP_VS_SCTP_S_SHUT_ACK_SER /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        /*
-         * SHUTDOWN COM from client, this what we are expecting, let's close
-         * the connection
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        /*
-         * SHUTDOWN COMPLETE from server this should not happen.
-         */
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        },
-       /*
-        * State : IP_VS_SCTP_S_CLOSED
-        */
-       {{IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_DATA_SER */ },
-        {IP_VS_SCTP_S_INIT_CLI /* IP_VS_SCTP_EVE_INIT_CLI */ },
-        {IP_VS_SCTP_S_INIT_SER /* IP_VS_SCTP_EVE_INIT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_INIT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ECHO_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_COOKIE_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_ABORT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_ACK_SER */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_CLI */ },
-        {IP_VS_SCTP_S_CLOSED /* IP_VS_SCTP_EVE_SHUT_COM_SER */ }
-        }
+#define sNO IP_VS_SCTP_S_NONE
+#define sI1 IP_VS_SCTP_S_INIT1
+#define sIN IP_VS_SCTP_S_INIT
+#define sCS IP_VS_SCTP_S_COOKIE_SENT
+#define sCR IP_VS_SCTP_S_COOKIE_REPLIED
+#define sCW IP_VS_SCTP_S_COOKIE_WAIT
+#define sCO IP_VS_SCTP_S_COOKIE
+#define sCE IP_VS_SCTP_S_COOKIE_ECHOED
+#define sES IP_VS_SCTP_S_ESTABLISHED
+#define sSS IP_VS_SCTP_S_SHUTDOWN_SENT
+#define sSR IP_VS_SCTP_S_SHUTDOWN_RECEIVED
+#define sSA IP_VS_SCTP_S_SHUTDOWN_ACK_SENT
+#define sRJ IP_VS_SCTP_S_REJECTED
+#define sCL IP_VS_SCTP_S_CLOSED
+
+static const __u8 sctp_states
+       [IP_VS_DIR_LAST][IP_VS_SCTP_EVENT_LAST][IP_VS_SCTP_S_LAST] = {
+       { /* INPUT */
+/*        sNO, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL*/
+/* d   */{sES, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* i   */{sI1, sIN, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sIN, sIN},
+/* i_a */{sCW, sCW, sCW, sCS, sCR, sCO, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* c_e */{sCR, sIN, sIN, sCR, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* c_a */{sES, sI1, sIN, sCS, sCR, sCW, sCO, sES, sES, sSS, sSR, sSA, sRJ, sCL},
+/* s   */{sSR, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sSR, sSS, sSR, sSA, sRJ, sCL},
+/* s_a */{sCL, sIN, sIN, sCS, sCR, sCW, sCO, sCE, sES, sCL, sSR, sCL, sRJ, sCL},
+/* s_c */{sCL, sCL, sCL, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sCL, sRJ, sCL},
+/* err */{sCL, sI1, sIN, sCS, sCR, sCW, sCO, sCL, sES, sSS, sSR, sSA, sRJ, sCL},
+/* ab  */{sCL, sCL, sCL, sCL, sCL, sRJ, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
+       },
+       { /* OUTPUT */
+/*        sNO, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL*/
+/* d   */{sES, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* i   */{sCW, sCW, sCW, sCW, sCW, sCW, sCW, sCW, sES, sCW, sCW, sCW, sCW, sCW},
+/* i_a */{sCS, sCS, sCS, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* c_e */{sCE, sCE, sCE, sCE, sCE, sCE, sCE, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* c_a */{sES, sES, sES, sES, sES, sES, sES, sES, sES, sSS, sSR, sSA, sRJ, sCL},
+/* s   */{sSS, sSS, sSS, sSS, sSS, sSS, sSS, sSS, sSS, sSS, sSR, sSA, sRJ, sCL},
+/* s_a */{sSA, sSA, sSA, sSA, sSA, sCW, sCO, sCE, sES, sSA, sSA, sSA, sRJ, sCL},
+/* s_c */{sCL, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* err */{sCL, sCL, sCL, sCL, sCL, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* ab  */{sCL, sRJ, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
+       },
+       { /* INPUT-ONLY */
+/*        sNO, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL*/
+/* d   */{sES, sI1, sIN, sCS, sCR, sES, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* i   */{sI1, sIN, sIN, sIN, sIN, sIN, sCO, sCE, sES, sSS, sSR, sSA, sIN, sIN},
+/* i_a */{sCE, sCE, sCE, sCE, sCE, sCE, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* c_e */{sES, sES, sES, sES, sES, sES, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* c_a */{sES, sI1, sIN, sES, sES, sCW, sES, sES, sES, sSS, sSR, sSA, sRJ, sCL},
+/* s   */{sSR, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sSR, sSS, sSR, sSA, sRJ, sCL},
+/* s_a */{sCL, sIN, sIN, sCS, sCR, sCW, sCO, sCE, sCL, sCL, sSR, sCL, sRJ, sCL},
+/* s_c */{sCL, sCL, sCL, sCL, sCL, sCW, sCO, sCE, sES, sSS, sCL, sCL, sRJ, sCL},
+/* err */{sCL, sI1, sIN, sCS, sCR, sCW, sCO, sCE, sES, sSS, sSR, sSA, sRJ, sCL},
+/* ab  */{sCL, sCL, sCL, sCL, sCL, sRJ, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
+       },
 };
 
-/*
- *      Timeout table[state]
- */
+#define IP_VS_SCTP_MAX_RTO     ((60 + 1) * HZ)
+
+/* Timeout table[state] */
 static const int sctp_timeouts[IP_VS_SCTP_S_LAST + 1] = {
-       [IP_VS_SCTP_S_NONE]         =     2 * HZ,
-       [IP_VS_SCTP_S_INIT_CLI]     =     1 * 60 * HZ,
-       [IP_VS_SCTP_S_INIT_SER]     =     1 * 60 * HZ,
-       [IP_VS_SCTP_S_INIT_ACK_CLI] =     1 * 60 * HZ,
-       [IP_VS_SCTP_S_INIT_ACK_SER] =     1 * 60 * HZ,
-       [IP_VS_SCTP_S_ECHO_CLI]     =     1 * 60 * HZ,
-       [IP_VS_SCTP_S_ECHO_SER]     =     1 * 60 * HZ,
-       [IP_VS_SCTP_S_ESTABLISHED]  =    15 * 60 * HZ,
-       [IP_VS_SCTP_S_SHUT_CLI]     =     1 * 60 * HZ,
-       [IP_VS_SCTP_S_SHUT_SER]     =     1 * 60 * HZ,
-       [IP_VS_SCTP_S_SHUT_ACK_CLI] =     1 * 60 * HZ,
-       [IP_VS_SCTP_S_SHUT_ACK_SER] =     1 * 60 * HZ,
-       [IP_VS_SCTP_S_CLOSED]       =    10 * HZ,
-       [IP_VS_SCTP_S_LAST]         =     2 * HZ,
+       [IP_VS_SCTP_S_NONE]                     = 2 * HZ,
+       [IP_VS_SCTP_S_INIT1]                    = (0 + 3 + 1) * HZ,
+       [IP_VS_SCTP_S_INIT]                     = IP_VS_SCTP_MAX_RTO,
+       [IP_VS_SCTP_S_COOKIE_SENT]              = IP_VS_SCTP_MAX_RTO,
+       [IP_VS_SCTP_S_COOKIE_REPLIED]           = IP_VS_SCTP_MAX_RTO,
+       [IP_VS_SCTP_S_COOKIE_WAIT]              = IP_VS_SCTP_MAX_RTO,
+       [IP_VS_SCTP_S_COOKIE]                   = IP_VS_SCTP_MAX_RTO,
+       [IP_VS_SCTP_S_COOKIE_ECHOED]            = IP_VS_SCTP_MAX_RTO,
+       [IP_VS_SCTP_S_ESTABLISHED]              = 15 * 60 * HZ,
+       [IP_VS_SCTP_S_SHUTDOWN_SENT]            = IP_VS_SCTP_MAX_RTO,
+       [IP_VS_SCTP_S_SHUTDOWN_RECEIVED]        = IP_VS_SCTP_MAX_RTO,
+       [IP_VS_SCTP_S_SHUTDOWN_ACK_SENT]        = IP_VS_SCTP_MAX_RTO,
+       [IP_VS_SCTP_S_REJECTED]                 = (0 + 3 + 1) * HZ,
+       [IP_VS_SCTP_S_CLOSED]                   = IP_VS_SCTP_MAX_RTO,
+       [IP_VS_SCTP_S_LAST]                     = 2 * HZ,
 };
 
 static const char *sctp_state_name_table[IP_VS_SCTP_S_LAST + 1] = {
-       [IP_VS_SCTP_S_NONE]         =    "NONE",
-       [IP_VS_SCTP_S_INIT_CLI]     =    "INIT_CLI",
-       [IP_VS_SCTP_S_INIT_SER]     =    "INIT_SER",
-       [IP_VS_SCTP_S_INIT_ACK_CLI] =    "INIT_ACK_CLI",
-       [IP_VS_SCTP_S_INIT_ACK_SER] =    "INIT_ACK_SER",
-       [IP_VS_SCTP_S_ECHO_CLI]     =    "COOKIE_ECHO_CLI",
-       [IP_VS_SCTP_S_ECHO_SER]     =    "COOKIE_ECHO_SER",
-       [IP_VS_SCTP_S_ESTABLISHED]  =    "ESTABISHED",
-       [IP_VS_SCTP_S_SHUT_CLI]     =    "SHUTDOWN_CLI",
-       [IP_VS_SCTP_S_SHUT_SER]     =    "SHUTDOWN_SER",
-       [IP_VS_SCTP_S_SHUT_ACK_CLI] =    "SHUTDOWN_ACK_CLI",
-       [IP_VS_SCTP_S_SHUT_ACK_SER] =    "SHUTDOWN_ACK_SER",
-       [IP_VS_SCTP_S_CLOSED]       =    "CLOSED",
-       [IP_VS_SCTP_S_LAST]         =    "BUG!"
+       [IP_VS_SCTP_S_NONE]                     = "NONE",
+       [IP_VS_SCTP_S_INIT1]                    = "INIT1",
+       [IP_VS_SCTP_S_INIT]                     = "INIT",
+       [IP_VS_SCTP_S_COOKIE_SENT]              = "C-SENT",
+       [IP_VS_SCTP_S_COOKIE_REPLIED]           = "C-REPLIED",
+       [IP_VS_SCTP_S_COOKIE_WAIT]              = "C-WAIT",
+       [IP_VS_SCTP_S_COOKIE]                   = "COOKIE",
+       [IP_VS_SCTP_S_COOKIE_ECHOED]            = "C-ECHOED",
+       [IP_VS_SCTP_S_ESTABLISHED]              = "ESTABLISHED",
+       [IP_VS_SCTP_S_SHUTDOWN_SENT]            = "S-SENT",
+       [IP_VS_SCTP_S_SHUTDOWN_RECEIVED]        = "S-RECEIVED",
+       [IP_VS_SCTP_S_SHUTDOWN_ACK_SENT]        = "S-ACK-SENT",
+       [IP_VS_SCTP_S_REJECTED]                 = "REJECTED",
+       [IP_VS_SCTP_S_CLOSED]                   = "CLOSED",
+       [IP_VS_SCTP_S_LAST]                     = "BUG!",
 };
 
 
@@ -943,17 +394,20 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
                }
        }
 
-       event = sctp_events[chunk_type];
+       event = (chunk_type < sizeof(sctp_events)) ?
+               sctp_events[chunk_type] : IP_VS_SCTP_DATA;
 
-       /*
-        *  If the direction is IP_VS_DIR_OUTPUT, this event is from server
-        */
-       if (direction == IP_VS_DIR_OUTPUT)
-               event++;
-       /*
-        * get next state
+       /* Update direction to INPUT_ONLY if necessary
+        * or delete NO_OUTPUT flag if output packet detected
         */
-       next_state = sctp_states_table[cp->state][event].next_state;
+       if (cp->flags & IP_VS_CONN_F_NOOUTPUT) {
+               if (direction == IP_VS_DIR_OUTPUT)
+                       cp->flags &= ~IP_VS_CONN_F_NOOUTPUT;
+               else
+                       direction = IP_VS_DIR_INPUT_ONLY;
+       }
+
+       next_state = sctp_states[direction][event][cp->state];
 
        if (next_state != cp->state) {
                struct ip_vs_dest *dest = cp->dest;
index 50a1594..e3a6972 100644 (file)
@@ -39,6 +39,7 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
        struct net *net;
        struct ip_vs_service *svc;
        struct tcphdr _tcph, *th;
+       struct netns_ipvs *ipvs;
 
        th = skb_header_pointer(skb, iph->len, sizeof(_tcph), &_tcph);
        if (th == NULL) {
@@ -46,14 +47,15 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
                return 0;
        }
        net = skb_net(skb);
+       ipvs = net_ipvs(net);
        /* No !th->ack check to allow scheduling on SYN+ACK for Active FTP */
        rcu_read_lock();
-       if (th->syn &&
+       if ((th->syn || sysctl_sloppy_tcp(ipvs)) && !th->rst &&
            (svc = ip_vs_service_find(net, af, skb->mark, iph->protocol,
                                      &iph->daddr, th->dest))) {
                int ignored;
 
-               if (ip_vs_todrop(net_ipvs(net))) {
+               if (ip_vs_todrop(ipvs)) {
                        /*
                         * It seems that we are very loaded.
                         * We have to drop this packet :(
@@ -401,7 +403,7 @@ static struct tcp_states_t tcp_states [] = {
 /*        sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI, sSA        */
 /*syn*/ {{sSR, sES, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR }},
 /*fin*/ {{sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI, sTW }},
-/*ack*/ {{sCL, sES, sSS, sES, sFW, sTW, sCL, sCW, sCL, sLI, sES }},
+/*ack*/ {{sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sCL, sLI, sES }},
 /*rst*/ {{sCL, sCL, sCL, sSR, sCL, sCL, sCL, sCL, sLA, sLI, sSR }},
 
 /*     OUTPUT */
@@ -415,7 +417,7 @@ static struct tcp_states_t tcp_states [] = {
 /*        sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI, sSA        */
 /*syn*/ {{sSR, sES, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR }},
 /*fin*/ {{sCL, sFW, sSS, sTW, sFW, sTW, sCL, sCW, sLA, sLI, sTW }},
-/*ack*/ {{sCL, sES, sSS, sES, sFW, sTW, sCL, sCW, sCL, sLI, sES }},
+/*ack*/ {{sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sCL, sLI, sES }},
 /*rst*/ {{sCL, sCL, sCL, sSR, sCL, sCL, sCL, sCL, sLA, sLI, sCL }},
 };
 
@@ -424,7 +426,7 @@ static struct tcp_states_t tcp_states_dos [] = {
 /*        sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI, sSA        */
 /*syn*/ {{sSR, sES, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSA }},
 /*fin*/ {{sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI, sSA }},
-/*ack*/ {{sCL, sES, sSS, sSR, sFW, sTW, sCL, sCW, sCL, sLI, sSA }},
+/*ack*/ {{sES, sES, sSS, sSR, sFW, sTW, sCL, sCW, sCL, sLI, sSA }},
 /*rst*/ {{sCL, sCL, sCL, sSR, sCL, sCL, sCL, sCL, sLA, sLI, sCL }},
 
 /*     OUTPUT */
@@ -438,7 +440,7 @@ static struct tcp_states_t tcp_states_dos [] = {
 /*        sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI, sSA        */
 /*syn*/ {{sSA, sES, sES, sSR, sSA, sSA, sSA, sSA, sSA, sSA, sSA }},
 /*fin*/ {{sCL, sFW, sSS, sTW, sFW, sTW, sCL, sCW, sLA, sLI, sTW }},
-/*ack*/ {{sCL, sES, sSS, sES, sFW, sTW, sCL, sCW, sCL, sLI, sES }},
+/*ack*/ {{sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sCL, sLI, sES }},
 /*rst*/ {{sCL, sCL, sCL, sSR, sCL, sCL, sCL, sCL, sLA, sLI, sCL }},
 };
 
index c35986c..176b87c 100644 (file)
@@ -55,7 +55,8 @@ static int ip_vs_rr_del_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest)
  * Round-Robin Scheduling
  */
 static struct ip_vs_dest *
-ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
+ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
+                 struct ip_vs_iphdr *iph)
 {
        struct list_head *p;
        struct ip_vs_dest *dest, *last;
index f320592..a5284cc 100644 (file)
@@ -59,7 +59,8 @@ ip_vs_sed_dest_overhead(struct ip_vs_dest *dest)
  *     Weighted Least Connection scheduling
  */
 static struct ip_vs_dest *
-ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
+ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
+                  struct ip_vs_iphdr *iph)
 {
        struct ip_vs_dest *dest, *least;
        unsigned int loh, doh;
index a65edfe..f16c027 100644 (file)
 
 #include <net/ip_vs.h>
 
+#include <net/tcp.h>
+#include <linux/udp.h>
+#include <linux/sctp.h>
+
 
 /*
  *      IPVS SH bucket
@@ -71,10 +75,19 @@ struct ip_vs_sh_state {
        struct ip_vs_sh_bucket          buckets[IP_VS_SH_TAB_SIZE];
 };
 
+/* Helper function to determine if server is unavailable */
+static inline bool is_unavailable(struct ip_vs_dest *dest)
+{
+       return atomic_read(&dest->weight) <= 0 ||
+              dest->flags & IP_VS_DEST_F_OVERLOAD;
+}
+
 /*
  *     Returns hash value for IPVS SH entry
  */
-static inline unsigned int ip_vs_sh_hashkey(int af, const union nf_inet_addr *addr)
+static inline unsigned int
+ip_vs_sh_hashkey(int af, const union nf_inet_addr *addr,
+                __be16 port, unsigned int offset)
 {
        __be32 addr_fold = addr->ip;
 
@@ -83,7 +96,8 @@ static inline unsigned int ip_vs_sh_hashkey(int af, const union nf_inet_addr *ad
                addr_fold = addr->ip6[0]^addr->ip6[1]^
                            addr->ip6[2]^addr->ip6[3];
 #endif
-       return (ntohl(addr_fold)*2654435761UL) & IP_VS_SH_TAB_MASK;
+       return (offset + (ntohs(port) + ntohl(addr_fold))*2654435761UL) &
+               IP_VS_SH_TAB_MASK;
 }
 
 
@@ -91,12 +105,42 @@ static inline unsigned int ip_vs_sh_hashkey(int af, const union nf_inet_addr *ad
  *      Get ip_vs_dest associated with supplied parameters.
  */
 static inline struct ip_vs_dest *
-ip_vs_sh_get(int af, struct ip_vs_sh_state *s, const union nf_inet_addr *addr)
+ip_vs_sh_get(struct ip_vs_service *svc, struct ip_vs_sh_state *s,
+            const union nf_inet_addr *addr, __be16 port)
 {
-       return rcu_dereference(s->buckets[ip_vs_sh_hashkey(af, addr)].dest);
+       unsigned int hash = ip_vs_sh_hashkey(svc->af, addr, port, 0);
+       struct ip_vs_dest *dest = rcu_dereference(s->buckets[hash].dest);
+
+       return (!dest || is_unavailable(dest)) ? NULL : dest;
 }
 
 
+/* As ip_vs_sh_get, but with fallback if selected server is unavailable */
+static inline struct ip_vs_dest *
+ip_vs_sh_get_fallback(struct ip_vs_service *svc, struct ip_vs_sh_state *s,
+                     const union nf_inet_addr *addr, __be16 port)
+{
+       unsigned int offset;
+       unsigned int hash;
+       struct ip_vs_dest *dest;
+
+       for (offset = 0; offset < IP_VS_SH_TAB_SIZE; offset++) {
+               hash = ip_vs_sh_hashkey(svc->af, addr, port, offset);
+               dest = rcu_dereference(s->buckets[hash].dest);
+               if (!dest)
+                       break;
+               if (is_unavailable(dest))
+                       IP_VS_DBG_BUF(6, "SH: selected unavailable server "
+                                     "%s:%d (offset %d)",
+                                     IP_VS_DBG_ADDR(svc->af, &dest->addr),
+                                     ntohs(dest->port), offset);
+               else
+                       return dest;
+       }
+
+       return NULL;
+}
+
 /*
  *      Assign all the hash buckets of the specified table with the service.
  */
@@ -213,13 +257,33 @@ static int ip_vs_sh_dest_changed(struct ip_vs_service *svc,
 }
 
 
-/*
- *      If the dest flags is set with IP_VS_DEST_F_OVERLOAD,
- *      consider that the server is overloaded here.
- */
-static inline int is_overloaded(struct ip_vs_dest *dest)
+/* Helper function to get port number */
+static inline __be16
+ip_vs_sh_get_port(const struct sk_buff *skb, struct ip_vs_iphdr *iph)
 {
-       return dest->flags & IP_VS_DEST_F_OVERLOAD;
+       __be16 port;
+       struct tcphdr _tcph, *th;
+       struct udphdr _udph, *uh;
+       sctp_sctphdr_t _sctph, *sh;
+
+       switch (iph->protocol) {
+       case IPPROTO_TCP:
+               th = skb_header_pointer(skb, iph->len, sizeof(_tcph), &_tcph);
+               port = th->source;
+               break;
+       case IPPROTO_UDP:
+               uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
+               port = uh->source;
+               break;
+       case IPPROTO_SCTP:
+               sh = skb_header_pointer(skb, iph->len, sizeof(_sctph), &_sctph);
+               port = sh->source;
+               break;
+       default:
+               port = 0;
+       }
+
+       return port;
 }
 
 
@@ -227,28 +291,32 @@ static inline int is_overloaded(struct ip_vs_dest *dest)
  *      Source Hashing scheduling
  */
 static struct ip_vs_dest *
-ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
+ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
+                 struct ip_vs_iphdr *iph)
 {
        struct ip_vs_dest *dest;
        struct ip_vs_sh_state *s;
-       struct ip_vs_iphdr iph;
-
-       ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
+       __be16 port = 0;
 
        IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n");
 
+       if (svc->flags & IP_VS_SVC_F_SCHED_SH_PORT)
+               port = ip_vs_sh_get_port(skb, iph);
+
        s = (struct ip_vs_sh_state *) svc->sched_data;
-       dest = ip_vs_sh_get(svc->af, s, &iph.saddr);
-       if (!dest
-           || !(dest->flags & IP_VS_DEST_F_AVAILABLE)
-           || atomic_read(&dest->weight) <= 0
-           || is_overloaded(dest)) {
+
+       if (svc->flags & IP_VS_SVC_F_SCHED_SH_FALLBACK)
+               dest = ip_vs_sh_get_fallback(svc, s, &iph->saddr, port);
+       else
+               dest = ip_vs_sh_get(svc, s, &iph->saddr, port);
+
+       if (!dest) {
                ip_vs_scheduler_err(svc, "no destination available");
                return NULL;
        }
 
        IP_VS_DBG_BUF(6, "SH: source IP address %s --> server %s:%d\n",
-                     IP_VS_DBG_ADDR(svc->af, &iph.saddr),
+                     IP_VS_DBG_ADDR(svc->af, &iph->saddr),
                      IP_VS_DBG_ADDR(svc->af, &dest->addr),
                      ntohs(dest->port));
 
index f6046d9..f448471 100644 (file)
@@ -425,6 +425,16 @@ ip_vs_sync_buff_create_v0(struct netns_ipvs *ipvs)
        return sb;
 }
 
+/* Check if connection is controlled by persistence */
+static inline bool in_persistence(struct ip_vs_conn *cp)
+{
+       for (cp = cp->control; cp; cp = cp->control) {
+               if (cp->flags & IP_VS_CONN_F_TEMPLATE)
+                       return true;
+       }
+       return false;
+}
+
 /* Check if conn should be synced.
  * pkts: conn packets, use sysctl_sync_threshold to avoid packet check
  * - (1) sync_refresh_period: reduce sync rate. Additionally, retry
@@ -447,6 +457,8 @@ static int ip_vs_sync_conn_needed(struct netns_ipvs *ipvs,
        /* Check if we sync in current state */
        if (unlikely(cp->flags & IP_VS_CONN_F_TEMPLATE))
                force = 0;
+       else if (unlikely(sysctl_sync_persist_mode(ipvs) && in_persistence(cp)))
+               return 0;
        else if (likely(cp->protocol == IPPROTO_TCP)) {
                if (!((1 << cp->state) &
                      ((1 << IP_VS_TCP_S_ESTABLISHED) |
@@ -461,9 +473,10 @@ static int ip_vs_sync_conn_needed(struct netns_ipvs *ipvs,
        } else if (unlikely(cp->protocol == IPPROTO_SCTP)) {
                if (!((1 << cp->state) &
                      ((1 << IP_VS_SCTP_S_ESTABLISHED) |
-                      (1 << IP_VS_SCTP_S_CLOSED) |
-                      (1 << IP_VS_SCTP_S_SHUT_ACK_CLI) |
-                      (1 << IP_VS_SCTP_S_SHUT_ACK_SER))))
+                      (1 << IP_VS_SCTP_S_SHUTDOWN_SENT) |
+                      (1 << IP_VS_SCTP_S_SHUTDOWN_RECEIVED) |
+                      (1 << IP_VS_SCTP_S_SHUTDOWN_ACK_SENT) |
+                      (1 << IP_VS_SCTP_S_CLOSED))))
                        return 0;
                force = cp->state != cp->old_state;
                if (force && cp->state != IP_VS_SCTP_S_ESTABLISHED)
index c60a81c..6dc1fa1 100644 (file)
@@ -31,7 +31,8 @@
  *     Weighted Least Connection scheduling
  */
 static struct ip_vs_dest *
-ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
+ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
+                  struct ip_vs_iphdr *iph)
 {
        struct ip_vs_dest *dest, *least;
        unsigned int loh, doh;
index 0e68555..0546cd5 100644 (file)
@@ -162,7 +162,8 @@ static int ip_vs_wrr_dest_changed(struct ip_vs_service *svc,
  *    Weighted Round-Robin Scheduling
  */
 static struct ip_vs_dest *
-ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
+ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
+                  struct ip_vs_iphdr *iph)
 {
        struct ip_vs_dest *dest, *last, *stop = NULL;
        struct ip_vs_wrr_mark *mark = svc->sched_data;
index 6b21707..b8a0924 100644 (file)
@@ -55,10 +55,14 @@ unsigned int (*nf_nat_ftp_hook)(struct sk_buff *skb,
                                struct nf_conntrack_expect *exp);
 EXPORT_SYMBOL_GPL(nf_nat_ftp_hook);
 
-static int try_rfc959(const char *, size_t, struct nf_conntrack_man *, char);
-static int try_eprt(const char *, size_t, struct nf_conntrack_man *, char);
+static int try_rfc959(const char *, size_t, struct nf_conntrack_man *,
+                     char, unsigned int *);
+static int try_rfc1123(const char *, size_t, struct nf_conntrack_man *,
+                      char, unsigned int *);
+static int try_eprt(const char *, size_t, struct nf_conntrack_man *,
+                   char, unsigned int *);
 static int try_epsv_response(const char *, size_t, struct nf_conntrack_man *,
-                            char);
+                            char, unsigned int *);
 
 static struct ftp_search {
        const char *pattern;
@@ -66,7 +70,7 @@ static struct ftp_search {
        char skip;
        char term;
        enum nf_ct_ftp_type ftptype;
-       int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char);
+       int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char, unsigned int *);
 } search[IP_CT_DIR_MAX][2] = {
        [IP_CT_DIR_ORIGINAL] = {
                {
@@ -90,10 +94,8 @@ static struct ftp_search {
                {
                        .pattern        = "227 ",
                        .plen           = sizeof("227 ") - 1,
-                       .skip           = '(',
-                       .term           = ')',
                        .ftptype        = NF_CT_FTP_PASV,
-                       .getnum         = try_rfc959,
+                       .getnum         = try_rfc1123,
                },
                {
                        .pattern        = "229 ",
@@ -132,8 +134,9 @@ static int try_number(const char *data, size_t dlen, u_int32_t array[],
                        i++;
                else {
                        /* Unexpected character; true if it's the
-                          terminator and we're finished. */
-                       if (*data == term && i == array_size - 1)
+                          terminator (or we don't care about one)
+                          and we're finished. */
+                       if ((*data == term || !term) && i == array_size - 1)
                                return len;
 
                        pr_debug("Char %u (got %u nums) `%u' unexpected\n",
@@ -148,7 +151,8 @@ static int try_number(const char *data, size_t dlen, u_int32_t array[],
 
 /* Returns 0, or length of numbers: 192,168,1,1,5,6 */
 static int try_rfc959(const char *data, size_t dlen,
-                     struct nf_conntrack_man *cmd, char term)
+                     struct nf_conntrack_man *cmd, char term,
+                     unsigned int *offset)
 {
        int length;
        u_int32_t array[6];
@@ -163,6 +167,33 @@ static int try_rfc959(const char *data, size_t dlen,
        return length;
 }
 
+/*
+ * From RFC 1123:
+ * The format of the 227 reply to a PASV command is not
+ * well standardized.  In particular, an FTP client cannot
+ * assume that the parentheses shown on page 40 of RFC-959
+ * will be present (and in fact, Figure 3 on page 43 omits
+ * them).  Therefore, a User-FTP program that interprets
+ * the PASV reply must scan the reply for the first digit
+ * of the host and port numbers.
+ */
+static int try_rfc1123(const char *data, size_t dlen,
+                      struct nf_conntrack_man *cmd, char term,
+                      unsigned int *offset)
+{
+       int i;
+       for (i = 0; i < dlen; i++)
+               if (isdigit(data[i]))
+                       break;
+
+       if (i == dlen)
+               return 0;
+
+       *offset += i;
+
+       return try_rfc959(data + i, dlen - i, cmd, 0, offset);
+}
+
 /* Grab port: number up to delimiter */
 static int get_port(const char *data, int start, size_t dlen, char delim,
                    __be16 *port)
@@ -191,7 +222,7 @@ static int get_port(const char *data, int start, size_t dlen, char delim,
 
 /* Returns 0, or length of numbers: |1|132.235.1.2|6275| or |2|3ffe::1|6275| */
 static int try_eprt(const char *data, size_t dlen, struct nf_conntrack_man *cmd,
-                   char term)
+                   char term, unsigned int *offset)
 {
        char delim;
        int length;
@@ -239,7 +270,8 @@ static int try_eprt(const char *data, size_t dlen, struct nf_conntrack_man *cmd,
 
 /* Returns 0, or length of numbers: |||6446| */
 static int try_epsv_response(const char *data, size_t dlen,
-                            struct nf_conntrack_man *cmd, char term)
+                            struct nf_conntrack_man *cmd, char term,
+                            unsigned int *offset)
 {
        char delim;
 
@@ -261,9 +293,10 @@ static int find_pattern(const char *data, size_t dlen,
                        unsigned int *numlen,
                        struct nf_conntrack_man *cmd,
                        int (*getnum)(const char *, size_t,
-                                     struct nf_conntrack_man *, char))
+                                     struct nf_conntrack_man *, char,
+                                     unsigned int *))
 {
-       size_t i;
+       size_t i = plen;
 
        pr_debug("find_pattern `%s': dlen = %Zu\n", pattern, dlen);
        if (dlen == 0)
@@ -293,16 +326,18 @@ static int find_pattern(const char *data, size_t dlen,
        pr_debug("Pattern matches!\n");
        /* Now we've found the constant string, try to skip
           to the 'skip' character */
-       for (i = plen; data[i] != skip; i++)
-               if (i == dlen - 1) return -1;
+       if (skip) {
+               for (i = plen; data[i] != skip; i++)
+                       if (i == dlen - 1) return -1;
 
-       /* Skip over the last character */
-       i++;
+               /* Skip over the last character */
+               i++;
+       }
 
        pr_debug("Skipped up to `%c'!\n", skip);
 
        *numoff = i;
-       *numlen = getnum(data + i, dlen - i, cmd, term);
+       *numlen = getnum(data + i, dlen - i, cmd, term, numoff);
        if (!*numlen)
                return -1;
 
index ecf065f..edc410e 100644 (file)
@@ -828,7 +828,9 @@ ctnetlink_parse_tuple_ip(struct nlattr *attr, struct nf_conntrack_tuple *tuple)
        struct nf_conntrack_l3proto *l3proto;
        int ret = 0;
 
-       nla_parse_nested(tb, CTA_IP_MAX, attr, NULL);
+       ret = nla_parse_nested(tb, CTA_IP_MAX, attr, NULL);
+       if (ret < 0)
+               return ret;
 
        rcu_read_lock();
        l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
@@ -895,7 +897,9 @@ ctnetlink_parse_tuple(const struct nlattr * const cda[],
 
        memset(tuple, 0, sizeof(*tuple));
 
-       nla_parse_nested(tb, CTA_TUPLE_MAX, cda[type], tuple_nla_policy);
+       err = nla_parse_nested(tb, CTA_TUPLE_MAX, cda[type], tuple_nla_policy);
+       if (err < 0)
+               return err;
 
        if (!tb[CTA_TUPLE_IP])
                return -EINVAL;
@@ -946,9 +950,12 @@ static inline int
 ctnetlink_parse_help(const struct nlattr *attr, char **helper_name,
                     struct nlattr **helpinfo)
 {
+       int err;
        struct nlattr *tb[CTA_HELP_MAX+1];
 
-       nla_parse_nested(tb, CTA_HELP_MAX, attr, help_nla_policy);
+       err = nla_parse_nested(tb, CTA_HELP_MAX, attr, help_nla_policy);
+       if (err < 0)
+               return err;
 
        if (!tb[CTA_HELP_NAME])
                return -EINVAL;
@@ -1431,7 +1438,9 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, const struct nlattr * const cda[]
        struct nf_conntrack_l4proto *l4proto;
        int err = 0;
 
-       nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, protoinfo_policy);
+       err = nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, protoinfo_policy);
+       if (err < 0)
+               return err;
 
        rcu_read_lock();
        l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
@@ -1452,9 +1461,12 @@ static const struct nla_policy nat_seq_policy[CTA_NAT_SEQ_MAX+1] = {
 static inline int
 change_nat_seq_adj(struct nf_nat_seq *natseq, const struct nlattr * const attr)
 {
+       int err;
        struct nlattr *cda[CTA_NAT_SEQ_MAX+1];
 
-       nla_parse_nested(cda, CTA_NAT_SEQ_MAX, attr, nat_seq_policy);
+       err = nla_parse_nested(cda, CTA_NAT_SEQ_MAX, attr, nat_seq_policy);
+       if (err < 0)
+               return err;
 
        if (!cda[CTA_NAT_SEQ_CORRECTION_POS])
                return -EINVAL;
@@ -2116,7 +2128,9 @@ ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct)
        struct nlattr *cda[CTA_MAX+1];
        int ret;
 
-       nla_parse_nested(cda, CTA_MAX, attr, ct_nla_policy);
+       ret = nla_parse_nested(cda, CTA_MAX, attr, ct_nla_policy);
+       if (ret < 0)
+               return ret;
 
        spin_lock_bh(&nf_conntrack_lock);
        ret = ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct);
@@ -2711,7 +2725,9 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr,
        struct nf_conntrack_tuple nat_tuple = {};
        int err;
 
-       nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_nla_policy);
+       err = nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_nla_policy);
+       if (err < 0)
+               return err;
 
        if (!tb[CTA_EXPECT_NAT_DIR] || !tb[CTA_EXPECT_NAT_TUPLE])
                return -EINVAL;
index 4d4d8f1..7dcc376 100644 (file)
@@ -1043,6 +1043,12 @@ static int tcp_packet(struct nf_conn *ct,
                        nf_ct_kill_acct(ct, ctinfo, skb);
                        return NF_ACCEPT;
                }
+               /* ESTABLISHED without SEEN_REPLY, i.e. mid-connection
+                * pickup with loose=1. Avoid large ESTABLISHED timeout.
+                */
+               if (new_state == TCP_CONNTRACK_ESTABLISHED &&
+                   timeout > timeouts[TCP_CONNTRACK_UNACK])
+                       timeout = timeouts[TCP_CONNTRACK_UNACK];
        } else if (!test_bit(IPS_ASSURED_BIT, &ct->status)
                   && (old_state == TCP_CONNTRACK_SYN_RECV
                       || old_state == TCP_CONNTRACK_ESTABLISHED)
index bd700b4..f641751 100644 (file)
@@ -408,7 +408,7 @@ static int log_invalid_proto_max = 255;
 
 static struct ctl_table_header *nf_ct_netfilter_header;
 
-static ctl_table nf_ct_sysctl_table[] = {
+static struct ctl_table nf_ct_sysctl_table[] = {
        {
                .procname       = "nf_conntrack_max",
                .data           = &nf_conntrack_max,
@@ -458,7 +458,7 @@ static ctl_table nf_ct_sysctl_table[] = {
 
 #define NET_NF_CONNTRACK_MAX 2089
 
-static ctl_table nf_ct_netfilter_table[] = {
+static struct ctl_table nf_ct_netfilter_table[] = {
        {
                .procname       = "nf_conntrack_max",
                .data           = &nf_conntrack_max,
index 3b18dd1..85296d4 100644 (file)
@@ -245,7 +245,7 @@ static const struct file_operations nflog_file_ops = {
 static char nf_log_sysctl_fnames[NFPROTO_NUMPROTO-NFPROTO_UNSPEC][3];
 static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO+1];
 
-static int nf_log_proc_dostring(ctl_table *table, int write,
+static int nf_log_proc_dostring(struct ctl_table *table, int write,
                         void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        const struct nf_logger *logger;
@@ -369,9 +369,7 @@ static int __net_init nf_log_net_init(struct net *net)
 
 out_sysctl:
 #ifdef CONFIG_PROC_FS
-       /* For init_net: errors will trigger panic, don't unroll on error. */
-       if (!net_eq(net, &init_net))
-               remove_proc_entry("nf_log", net->nf.proc_netfilter);
+       remove_proc_entry("nf_log", net->nf.proc_netfilter);
 #endif
        return ret;
 }
index 5fea563..85e20a9 100644 (file)
@@ -104,7 +104,7 @@ static void mangle_contents(struct sk_buff *skb,
        /* move post-replacement */
        memmove(data + match_offset + rep_len,
                data + match_offset + match_len,
-               skb->tail - (skb->network_header + dataoff +
+               skb_tail_pointer(skb) - (skb_network_header(skb) + dataoff +
                             match_offset + match_len));
 
        /* insert data from buffer */
index a191b6d..9e287cb 100644 (file)
@@ -67,9 +67,12 @@ static int
 nfnl_cthelper_parse_tuple(struct nf_conntrack_tuple *tuple,
                          const struct nlattr *attr)
 {
+       int err;
        struct nlattr *tb[NFCTH_TUPLE_MAX+1];
 
-       nla_parse_nested(tb, NFCTH_TUPLE_MAX, attr, nfnl_cthelper_tuple_pol);
+       err = nla_parse_nested(tb, NFCTH_TUPLE_MAX, attr, nfnl_cthelper_tuple_pol);
+       if (err < 0)
+               return err;
 
        if (!tb[NFCTH_TUPLE_L3PROTONUM] || !tb[NFCTH_TUPLE_L4PROTONUM])
                return -EINVAL;
@@ -121,9 +124,12 @@ static int
 nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy,
                            const struct nlattr *attr)
 {
+       int err;
        struct nlattr *tb[NFCTH_POLICY_MAX+1];
 
-       nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, nfnl_cthelper_expect_pol);
+       err = nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, nfnl_cthelper_expect_pol);
+       if (err < 0)
+               return err;
 
        if (!tb[NFCTH_POLICY_NAME] ||
            !tb[NFCTH_POLICY_EXPECT_MAX] ||
@@ -153,8 +159,10 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper,
        struct nf_conntrack_expect_policy *expect_policy;
        struct nlattr *tb[NFCTH_POLICY_SET_MAX+1];
 
-       nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr,
-                                       nfnl_cthelper_expect_policy_set);
+       ret = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr,
+                              nfnl_cthelper_expect_policy_set);
+       if (ret < 0)
+               return ret;
 
        if (!tb[NFCTH_POLICY_SET_NUM])
                return -EINVAL;
index 65074df..5058049 100644 (file)
@@ -59,8 +59,10 @@ ctnl_timeout_parse_policy(struct ctnl_timeout *timeout,
        if (likely(l4proto->ctnl_timeout.nlattr_to_obj)) {
                struct nlattr *tb[l4proto->ctnl_timeout.nlattr_max+1];
 
-               nla_parse_nested(tb, l4proto->ctnl_timeout.nlattr_max,
-                                attr, l4proto->ctnl_timeout.nla_policy);
+               ret = nla_parse_nested(tb, l4proto->ctnl_timeout.nlattr_max,
+                                      attr, l4proto->ctnl_timeout.nla_policy);
+               if (ret < 0)
+                       return ret;
 
                ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net,
                                                          &timeout->data);
index 5352b2d..971ea14 100644 (file)
 
 #define NFQNL_QMAX_DEFAULT 1024
 
+/* We're using struct nlattr which has 16bit nla_len. Note that nla_len
+ * includes the header length. Thus, the maximum packet length that we
+ * support is 65531 bytes. We send truncated packets if the specified length
+ * is larger than that.  Userspace can check for presence of NFQA_CAP_LEN
+ * attribute to detect truncation.
+ */
+#define NFQNL_MAX_COPY_RANGE (0xffff - NLA_HDRLEN)
+
 struct nfqnl_instance {
        struct hlist_node hlist;                /* global list of queues */
        struct rcu_head rcu;
@@ -122,7 +130,7 @@ instance_create(struct nfnl_queue_net *q, u_int16_t queue_num,
        inst->queue_num = queue_num;
        inst->peer_portid = portid;
        inst->queue_maxlen = NFQNL_QMAX_DEFAULT;
-       inst->copy_range = 0xffff;
+       inst->copy_range = NFQNL_MAX_COPY_RANGE;
        inst->copy_mode = NFQNL_COPY_NONE;
        spin_lock_init(&inst->lock);
        INIT_LIST_HEAD(&inst->queue_list);
@@ -272,12 +280,17 @@ nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
        skb_shinfo(to)->nr_frags = j;
 }
 
-static int nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet)
+static int
+nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet,
+                     bool csum_verify)
 {
        __u32 flags = 0;
 
        if (packet->ip_summed == CHECKSUM_PARTIAL)
                flags = NFQA_SKB_CSUMNOTREADY;
+       else if (csum_verify)
+               flags = NFQA_SKB_CSUM_NOTVERIFIED;
+
        if (skb_is_gso(packet))
                flags |= NFQA_SKB_GSO;
 
@@ -302,6 +315,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
        struct net_device *outdev;
        struct nf_conn *ct = NULL;
        enum ip_conntrack_info uninitialized_var(ctinfo);
+       bool csum_verify;
 
        size =    nlmsg_total_size(sizeof(struct nfgenmsg))
                + nla_total_size(sizeof(struct nfqnl_msg_packet_hdr))
@@ -319,6 +333,12 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
        if (entskb->tstamp.tv64)
                size += nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp));
 
+       if (entry->hook <= NF_INET_FORWARD ||
+          (entry->hook == NF_INET_POST_ROUTING && entskb->sk == NULL))
+               csum_verify = !skb_csum_unnecessary(entskb);
+       else
+               csum_verify = false;
+
        outdev = entry->outdev;
 
        switch ((enum nfqnl_config_mode)ACCESS_ONCE(queue->copy_mode)) {
@@ -333,10 +353,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
                        return NULL;
 
                data_len = ACCESS_ONCE(queue->copy_range);
-               if (data_len == 0 || data_len > entskb->len)
+               if (data_len > entskb->len)
                        data_len = entskb->len;
 
-
                if (!entskb->head_frag ||
                    skb_headlen(entskb) < L1_CACHE_BYTES ||
                    skb_shinfo(entskb)->nr_frags >= MAX_SKB_FRAGS)
@@ -465,10 +484,11 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
        if (ct && nfqnl_ct_put(skb, ct, ctinfo) < 0)
                goto nla_put_failure;
 
-       if (cap_len > 0 && nla_put_be32(skb, NFQA_CAP_LEN, htonl(cap_len)))
+       if (cap_len > data_len &&
+           nla_put_be32(skb, NFQA_CAP_LEN, htonl(cap_len)))
                goto nla_put_failure;
 
-       if (nfqnl_put_packet_info(skb, entskb))
+       if (nfqnl_put_packet_info(skb, entskb, csum_verify))
                goto nla_put_failure;
 
        if (data_len) {
@@ -509,10 +529,6 @@ __nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue,
        }
        spin_lock_bh(&queue->lock);
 
-       if (!queue->peer_portid) {
-               err = -EINVAL;
-               goto err_out_free_nskb;
-       }
        if (queue->queue_total >= queue->queue_maxlen) {
                if (queue->flags & NFQA_CFG_F_FAIL_OPEN) {
                        failopen = 1;
@@ -731,13 +747,8 @@ nfqnl_set_mode(struct nfqnl_instance *queue,
 
        case NFQNL_COPY_PACKET:
                queue->copy_mode = mode;
-               /* We're using struct nlattr which has 16bit nla_len. Note that
-                * nla_len includes the header length. Thus, the maximum packet
-                * length that we support is 65531 bytes. We send truncated
-                * packets if the specified length is larger than that.
-                */
-               if (range > 0xffff - NLA_HDRLEN)
-                       queue->copy_range = 0xffff - NLA_HDRLEN;
+               if (range == 0 || range > NFQNL_MAX_COPY_RANGE)
+                       queue->copy_range = NFQNL_MAX_COPY_RANGE;
                else
                        queue->copy_range = range;
                break;
@@ -800,7 +811,7 @@ static int
 nfqnl_rcv_dev_event(struct notifier_block *this,
                    unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        /* Drop any packets associated with the downed device */
        if (event == NETDEV_DOWN)
index a60261c..da35ac0 100644 (file)
@@ -26,6 +26,9 @@ static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct)
        if (skb->nfct != NULL)
                return XT_CONTINUE;
 
+       /* special case the untracked ct : we want the percpu object */
+       if (!ct)
+               ct = nf_ct_untracked_get();
        atomic_inc(&ct->ct_general.use);
        skb->nfct = &ct->ct_general;
        skb->nfctinfo = IP_CT_NEW;
@@ -186,8 +189,7 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par,
        int ret = -EOPNOTSUPP;
 
        if (info->flags & XT_CT_NOTRACK) {
-               ct = nf_ct_untracked_get();
-               atomic_inc(&ct->ct_general.use);
+               ct = NULL;
                goto out;
        }
 
@@ -311,7 +313,7 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par,
        struct nf_conn *ct = info->ct;
        struct nf_conn_help *help;
 
-       if (!nf_ct_is_untracked(ct)) {
+       if (ct && !nf_ct_is_untracked(ct)) {
                help = nfct_help(ct);
                if (help)
                        module_put(help->helper->me);
@@ -319,8 +321,8 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par,
                nf_ct_l3proto_module_put(par->family);
 
                xt_ct_destroy_timeout(ct);
+               nf_ct_put(info->ct);
        }
-       nf_ct_put(info->ct);
 }
 
 static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
index bd93e51..292934d 100644 (file)
@@ -200,7 +200,7 @@ tee_tg6(struct sk_buff *skb, const struct xt_action_param *par)
 static int tee_netdev_event(struct notifier_block *this, unsigned long event,
                            void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct xt_tee_priv *priv;
 
        priv = container_of(this, struct xt_tee_priv, notifier);
index ed0db15..7720b03 100644 (file)
@@ -18,7 +18,7 @@ static bool
 xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par)
 {
        const struct xt_rateest_match_info *info = par->matchinfo;
-       struct gnet_stats_rate_est *r;
+       struct gnet_stats_rate_est64 *r;
        u_int32_t bps1, bps2, pps1, pps2;
        bool ret = true;
 
index 63b2bdb..f8b7191 100644 (file)
@@ -107,7 +107,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
 {
        const struct iphdr *iph = ip_hdr(skb);
        struct udphdr _hdr, *hp = NULL;
-       struct sock *sk;
+       struct sock *sk = skb->sk;
        __be32 uninitialized_var(daddr), uninitialized_var(saddr);
        __be16 uninitialized_var(dport), uninitialized_var(sport);
        u8 uninitialized_var(protocol);
@@ -155,14 +155,19 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
        }
 #endif
 
-       sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol,
-                                  saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY);
-       if (sk != NULL) {
+       if (!sk)
+               sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol,
+                                          saddr, daddr, sport, dport,
+                                          par->in, NFT_LOOKUP_ANY);
+       if (sk) {
                bool wildcard;
                bool transparent = true;
 
-               /* Ignore sockets listening on INADDR_ANY */
-               wildcard = (sk->sk_state != TCP_TIME_WAIT &&
+               /* Ignore sockets listening on INADDR_ANY,
+                * unless XT_SOCKET_NOWILDCARD is set
+                */
+               wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) &&
+                           sk->sk_state != TCP_TIME_WAIT &&
                            inet_sk(sk)->inet_rcv_saddr == 0);
 
                /* Ignore non-transparent sockets,
@@ -173,7 +178,8 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
                                       (sk->sk_state == TCP_TIME_WAIT &&
                                        inet_twsk(sk)->tw_transparent));
 
-               xt_socket_put_sk(sk);
+               if (sk != skb->sk)
+                       xt_socket_put_sk(sk);
 
                if (wildcard || !transparent)
                        sk = NULL;
@@ -194,7 +200,7 @@ socket_mt4_v0(const struct sk_buff *skb, struct xt_action_param *par)
 }
 
 static bool
-socket_mt4_v1(const struct sk_buff *skb, struct xt_action_param *par)
+socket_mt4_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
 {
        return socket_match(skb, par, par->matchinfo);
 }
@@ -256,11 +262,11 @@ extract_icmp6_fields(const struct sk_buff *skb,
 }
 
 static bool
-socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)
+socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
 {
        struct ipv6hdr *iph = ipv6_hdr(skb);
        struct udphdr _hdr, *hp = NULL;
-       struct sock *sk;
+       struct sock *sk = skb->sk;
        struct in6_addr *daddr = NULL, *saddr = NULL;
        __be16 uninitialized_var(dport), uninitialized_var(sport);
        int thoff = 0, uninitialized_var(tproto);
@@ -291,14 +297,19 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)
                return false;
        }
 
-       sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
-                                  saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY);
-       if (sk != NULL) {
+       if (!sk)
+               sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
+                                          saddr, daddr, sport, dport,
+                                          par->in, NFT_LOOKUP_ANY);
+       if (sk) {
                bool wildcard;
                bool transparent = true;
 
-               /* Ignore sockets listening on INADDR_ANY */
-               wildcard = (sk->sk_state != TCP_TIME_WAIT &&
+               /* Ignore sockets listening on INADDR_ANY
+                * unless XT_SOCKET_NOWILDCARD is set
+                */
+               wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) &&
+                           sk->sk_state != TCP_TIME_WAIT &&
                            ipv6_addr_any(&inet6_sk(sk)->rcv_saddr));
 
                /* Ignore non-transparent sockets,
@@ -309,7 +320,8 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)
                                       (sk->sk_state == TCP_TIME_WAIT &&
                                        inet_twsk(sk)->tw_transparent));
 
-               xt_socket_put_sk(sk);
+               if (sk != skb->sk)
+                       xt_socket_put_sk(sk);
 
                if (wildcard || !transparent)
                        sk = NULL;
@@ -325,6 +337,28 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)
 }
 #endif
 
+static int socket_mt_v1_check(const struct xt_mtchk_param *par)
+{
+       const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
+
+       if (info->flags & ~XT_SOCKET_FLAGS_V1) {
+               pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V1);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int socket_mt_v2_check(const struct xt_mtchk_param *par)
+{
+       const struct xt_socket_mtinfo2 *info = (struct xt_socket_mtinfo2 *) par->matchinfo;
+
+       if (info->flags & ~XT_SOCKET_FLAGS_V2) {
+               pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V2);
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static struct xt_match socket_mt_reg[] __read_mostly = {
        {
                .name           = "socket",
@@ -339,7 +373,8 @@ static struct xt_match socket_mt_reg[] __read_mostly = {
                .name           = "socket",
                .revision       = 1,
                .family         = NFPROTO_IPV4,
-               .match          = socket_mt4_v1,
+               .match          = socket_mt4_v1_v2,
+               .checkentry     = socket_mt_v1_check,
                .matchsize      = sizeof(struct xt_socket_mtinfo1),
                .hooks          = (1 << NF_INET_PRE_ROUTING) |
                                  (1 << NF_INET_LOCAL_IN),
@@ -350,7 +385,32 @@ static struct xt_match socket_mt_reg[] __read_mostly = {
                .name           = "socket",
                .revision       = 1,
                .family         = NFPROTO_IPV6,
-               .match          = socket_mt6_v1,
+               .match          = socket_mt6_v1_v2,
+               .checkentry     = socket_mt_v1_check,
+               .matchsize      = sizeof(struct xt_socket_mtinfo1),
+               .hooks          = (1 << NF_INET_PRE_ROUTING) |
+                                 (1 << NF_INET_LOCAL_IN),
+               .me             = THIS_MODULE,
+       },
+#endif
+       {
+               .name           = "socket",
+               .revision       = 2,
+               .family         = NFPROTO_IPV4,
+               .match          = socket_mt4_v1_v2,
+               .checkentry     = socket_mt_v2_check,
+               .matchsize      = sizeof(struct xt_socket_mtinfo1),
+               .hooks          = (1 << NF_INET_PRE_ROUTING) |
+                                 (1 << NF_INET_LOCAL_IN),
+               .me             = THIS_MODULE,
+       },
+#ifdef XT_SOCKET_HAVE_IPV6
+       {
+               .name           = "socket",
+               .revision       = 2,
+               .family         = NFPROTO_IPV6,
+               .match          = socket_mt6_v1_v2,
+               .checkentry     = socket_mt_v2_check,
                .matchsize      = sizeof(struct xt_socket_mtinfo1),
                .hooks          = (1 << NF_INET_PRE_ROUTING) |
                                  (1 << NF_INET_LOCAL_IN),
index 8a6c6ea..af35319 100644 (file)
@@ -708,7 +708,7 @@ unlhsh_remove_return:
  * netlbl_unlhsh_netdev_handler - Network device notification handler
  * @this: notifier block
  * @event: the event
- * @ptr: the network device (cast to void)
+ * @ptr: the netdevice notifier info (cast to void)
  *
  * Description:
  * Handle network device events, although at present all we care about is a
@@ -717,10 +717,9 @@ unlhsh_remove_return:
  *
  */
 static int netlbl_unlhsh_netdev_handler(struct notifier_block *this,
-                                       unsigned long event,
-                                       void *ptr)
+                                       unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct netlbl_unlhsh_iface *iface = NULL;
 
        if (!net_eq(dev_net(dev), &init_net))
index 57ee84d..0c61b59 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/audit.h>
 #include <linux/mutex.h>
 #include <linux/vmalloc.h>
+#include <linux/if_arp.h>
 #include <asm/cacheflush.h>
 
 #include <net/net_namespace.h>
@@ -101,6 +102,9 @@ static atomic_t nl_table_users = ATOMIC_INIT(0);
 
 static ATOMIC_NOTIFIER_HEAD(netlink_chain);
 
+static DEFINE_SPINLOCK(netlink_tap_lock);
+static struct list_head netlink_tap_all __read_mostly;
+
 static inline u32 netlink_group_mask(u32 group)
 {
        return group ? 1 << (group - 1) : 0;
@@ -111,6 +115,100 @@ static inline struct hlist_head *nl_portid_hashfn(struct nl_portid_hash *hash, u
        return &hash->table[jhash_1word(portid, hash->rnd) & hash->mask];
 }
 
+int netlink_add_tap(struct netlink_tap *nt)
+{
+       if (unlikely(nt->dev->type != ARPHRD_NETLINK))
+               return -EINVAL;
+
+       spin_lock(&netlink_tap_lock);
+       list_add_rcu(&nt->list, &netlink_tap_all);
+       spin_unlock(&netlink_tap_lock);
+
+       if (nt->module)
+               __module_get(nt->module);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(netlink_add_tap);
+
+int __netlink_remove_tap(struct netlink_tap *nt)
+{
+       bool found = false;
+       struct netlink_tap *tmp;
+
+       spin_lock(&netlink_tap_lock);
+
+       list_for_each_entry(tmp, &netlink_tap_all, list) {
+               if (nt == tmp) {
+                       list_del_rcu(&nt->list);
+                       found = true;
+                       goto out;
+               }
+       }
+
+       pr_warn("__netlink_remove_tap: %p not found\n", nt);
+out:
+       spin_unlock(&netlink_tap_lock);
+
+       if (found && nt->module)
+               module_put(nt->module);
+
+       return found ? 0 : -ENODEV;
+}
+EXPORT_SYMBOL_GPL(__netlink_remove_tap);
+
+int netlink_remove_tap(struct netlink_tap *nt)
+{
+       int ret;
+
+       ret = __netlink_remove_tap(nt);
+       synchronize_net();
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(netlink_remove_tap);
+
+static int __netlink_deliver_tap_skb(struct sk_buff *skb,
+                                    struct net_device *dev)
+{
+       struct sk_buff *nskb;
+       int ret = -ENOMEM;
+
+       dev_hold(dev);
+       nskb = skb_clone(skb, GFP_ATOMIC);
+       if (nskb) {
+               nskb->dev = dev;
+               ret = dev_queue_xmit(nskb);
+               if (unlikely(ret > 0))
+                       ret = net_xmit_errno(ret);
+       }
+
+       dev_put(dev);
+       return ret;
+}
+
+static void __netlink_deliver_tap(struct sk_buff *skb)
+{
+       int ret;
+       struct netlink_tap *tmp;
+
+       list_for_each_entry_rcu(tmp, &netlink_tap_all, list) {
+               ret = __netlink_deliver_tap_skb(skb, tmp->dev);
+               if (unlikely(ret))
+                       break;
+       }
+}
+
+static void netlink_deliver_tap(struct sk_buff *skb)
+{
+       rcu_read_lock();
+
+       if (unlikely(!list_empty(&netlink_tap_all)))
+               __netlink_deliver_tap(skb);
+
+       rcu_read_unlock();
+}
+
 static void netlink_overrun(struct sock *sk)
 {
        struct netlink_sock *nlk = nlk_sk(sk);
@@ -750,6 +848,13 @@ static void netlink_skb_destructor(struct sk_buff *skb)
                skb->head = NULL;
        }
 #endif
+       if (is_vmalloc_addr(skb->head)) {
+               if (!skb->cloned ||
+                   !atomic_dec_return(&(skb_shinfo(skb)->dataref)))
+                       vfree(skb->head);
+
+               skb->head = NULL;
+       }
        if (skb->sk != NULL)
                sock_rfree(skb);
 }
@@ -854,16 +959,23 @@ netlink_unlock_table(void)
                wake_up(&nl_table_wait);
 }
 
+static bool netlink_compare(struct net *net, struct sock *sk)
+{
+       return net_eq(sock_net(sk), net);
+}
+
 static struct sock *netlink_lookup(struct net *net, int protocol, u32 portid)
 {
-       struct nl_portid_hash *hash = &nl_table[protocol].hash;
+       struct netlink_table *table = &nl_table[protocol];
+       struct nl_portid_hash *hash = &table->hash;
        struct hlist_head *head;
        struct sock *sk;
 
        read_lock(&nl_table_lock);
        head = nl_portid_hashfn(hash, portid);
        sk_for_each(sk, head) {
-               if (net_eq(sock_net(sk), net) && (nlk_sk(sk)->portid == portid)) {
+               if (table->compare(net, sk) &&
+                   (nlk_sk(sk)->portid == portid)) {
                        sock_hold(sk);
                        goto found;
                }
@@ -976,7 +1088,8 @@ netlink_update_listeners(struct sock *sk)
 
 static int netlink_insert(struct sock *sk, struct net *net, u32 portid)
 {
-       struct nl_portid_hash *hash = &nl_table[sk->sk_protocol].hash;
+       struct netlink_table *table = &nl_table[sk->sk_protocol];
+       struct nl_portid_hash *hash = &table->hash;
        struct hlist_head *head;
        int err = -EADDRINUSE;
        struct sock *osk;
@@ -986,7 +1099,8 @@ static int netlink_insert(struct sock *sk, struct net *net, u32 portid)
        head = nl_portid_hashfn(hash, portid);
        len = 0;
        sk_for_each(osk, head) {
-               if (net_eq(sock_net(osk), net) && (nlk_sk(osk)->portid == portid))
+               if (table->compare(net, osk) &&
+                   (nlk_sk(osk)->portid == portid))
                        break;
                len++;
        }
@@ -1183,7 +1297,8 @@ static int netlink_autobind(struct socket *sock)
 {
        struct sock *sk = sock->sk;
        struct net *net = sock_net(sk);
-       struct nl_portid_hash *hash = &nl_table[sk->sk_protocol].hash;
+       struct netlink_table *table = &nl_table[sk->sk_protocol];
+       struct nl_portid_hash *hash = &table->hash;
        struct hlist_head *head;
        struct sock *osk;
        s32 portid = task_tgid_vnr(current);
@@ -1195,7 +1310,7 @@ retry:
        netlink_table_grab();
        head = nl_portid_hashfn(hash, portid);
        sk_for_each(osk, head) {
-               if (!net_eq(sock_net(osk), net))
+               if (!table->compare(net, osk))
                        continue;
                if (nlk_sk(osk)->portid == portid) {
                        /* Bind collision, search negative portid values. */
@@ -1420,6 +1535,33 @@ struct sock *netlink_getsockbyfilp(struct file *filp)
        return sock;
 }
 
+static struct sk_buff *netlink_alloc_large_skb(unsigned int size,
+                                              int broadcast)
+{
+       struct sk_buff *skb;
+       void *data;
+
+       if (size <= NLMSG_GOODSIZE || broadcast)
+               return alloc_skb(size, GFP_KERNEL);
+
+       size = SKB_DATA_ALIGN(size) +
+              SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+       data = vmalloc(size);
+       if (data == NULL)
+               return NULL;
+
+       skb = build_skb(data, size);
+       if (skb == NULL)
+               vfree(data);
+       else {
+               skb->head_frag = 0;
+               skb->destructor = netlink_skb_destructor;
+       }
+
+       return skb;
+}
+
 /*
  * Attach a skb to a netlink socket.
  * The caller must hold a reference to the destination socket. On error, the
@@ -1475,6 +1617,8 @@ static int __netlink_sendskb(struct sock *sk, struct sk_buff *skb)
 {
        int len = skb->len;
 
+       netlink_deliver_tap(skb);
+
 #ifdef CONFIG_NETLINK_MMAP
        if (netlink_skb_is_mmaped(skb))
                netlink_queue_mmaped_skb(sk, skb);
@@ -1510,7 +1654,7 @@ static struct sk_buff *netlink_trim(struct sk_buff *skb, gfp_t allocation)
                return skb;
 
        delta = skb->end - skb->tail;
-       if (delta * 2 < skb->truesize)
+       if (is_vmalloc_addr(skb->head) || delta * 2 < skb->truesize)
                return skb;
 
        if (skb_shared(skb)) {
@@ -1535,6 +1679,11 @@ static int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb,
 
        ret = -ECONNREFUSED;
        if (nlk->netlink_rcv != NULL) {
+               /* We could do a netlink_deliver_tap(skb) here as well
+                * but since this is intended for the kernel only, we
+                * should rather let it stay under the hood.
+                */
+
                ret = skb->len;
                netlink_skb_set_owner_r(skb, sk);
                NETLINK_CB(skb).sk = ssk;
@@ -2096,7 +2245,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
        if (len > sk->sk_sndbuf - 32)
                goto out;
        err = -ENOBUFS;
-       skb = alloc_skb(len, GFP_KERNEL);
+       skb = netlink_alloc_large_skb(len, dst_group);
        if (skb == NULL)
                goto out;
 
@@ -2285,6 +2434,8 @@ __netlink_kernel_create(struct net *net, int unit, struct module *module,
                if (cfg) {
                        nl_table[unit].bind = cfg->bind;
                        nl_table[unit].flags = cfg->flags;
+                       if (cfg->compare)
+                               nl_table[unit].compare = cfg->compare;
                }
                nl_table[unit].registered = 1;
        } else {
@@ -2707,6 +2858,7 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
        struct sock *s;
        struct nl_seq_iter *iter;
+       struct net *net;
        int i, j;
 
        ++*pos;
@@ -2714,11 +2866,12 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos)
        if (v == SEQ_START_TOKEN)
                return netlink_seq_socket_idx(seq, 0);
 
+       net = seq_file_net(seq);
        iter = seq->private;
        s = v;
        do {
                s = sk_next(s);
-       } while (s && sock_net(s) != seq_file_net(seq));
+       } while (s && !nl_table[s->sk_protocol].compare(net, s));
        if (s)
                return s;
 
@@ -2730,7 +2883,8 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 
                for (; j <= hash->mask; j++) {
                        s = sk_head(&hash->table[j]);
-                       while (s && sock_net(s) != seq_file_net(seq))
+
+                       while (s && !nl_table[s->sk_protocol].compare(net, s))
                                s = sk_next(s);
                        if (s) {
                                iter->link = i;
@@ -2923,8 +3077,12 @@ static int __init netlink_proto_init(void)
                hash->shift = 0;
                hash->mask = 0;
                hash->rehash_time = jiffies;
+
+               nl_table[i].compare = netlink_compare;
        }
 
+       INIT_LIST_HEAD(&netlink_tap_all);
+
        netlink_add_usersock_entry();
 
        sock_register(&netlink_family_ops);
index ed85222..eaa88d1 100644 (file)
@@ -73,6 +73,7 @@ struct netlink_table {
        struct mutex            *cb_mutex;
        struct module           *module;
        void                    (*bind)(int group);
+       bool                    (*compare)(struct net *net, struct sock *sock);
        int                     registered;
 };
 
index ec0c80f..698814b 100644 (file)
@@ -117,7 +117,7 @@ static void nr_kill_by_device(struct net_device *dev)
  */
 static int nr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
-       struct net_device *dev = (struct net_device *)ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
index 42f630b..ba1c368 100644 (file)
@@ -34,7 +34,7 @@ static int min_reset[]   = {0}, max_reset[]   = {1};
 
 static struct ctl_table_header *nr_table_header;
 
-static ctl_table nr_table[] = {
+static struct ctl_table nr_table[] = {
        {
                .procname       = "default_path_quality",
                .data           = &sysctl_netrom_default_path_quality,
index 40d2527..dc96a83 100644 (file)
@@ -44,6 +44,47 @@ DEFINE_MUTEX(nfc_devlist_mutex);
 /* NFC device ID bitmap */
 static DEFINE_IDA(nfc_index_ida);
 
+int nfc_fw_upload(struct nfc_dev *dev, const char *firmware_name)
+{
+       int rc = 0;
+
+       pr_debug("%s do firmware %s\n", dev_name(&dev->dev), firmware_name);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (dev->dev_up) {
+               rc = -EBUSY;
+               goto error;
+       }
+
+       if (!dev->ops->fw_upload) {
+               rc = -EOPNOTSUPP;
+               goto error;
+       }
+
+       dev->fw_upload_in_progress = true;
+       rc = dev->ops->fw_upload(dev, firmware_name);
+       if (rc)
+               dev->fw_upload_in_progress = false;
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+int nfc_fw_upload_done(struct nfc_dev *dev, const char *firmware_name)
+{
+       dev->fw_upload_in_progress = false;
+
+       return nfc_genl_fw_upload_done(dev, firmware_name);
+}
+EXPORT_SYMBOL(nfc_fw_upload_done);
+
 /**
  * nfc_dev_up - turn on the NFC device
  *
@@ -69,6 +110,11 @@ int nfc_dev_up(struct nfc_dev *dev)
                goto error;
        }
 
+       if (dev->fw_upload_in_progress) {
+               rc = -EBUSY;
+               goto error;
+       }
+
        if (dev->dev_up) {
                rc = -EALREADY;
                goto error;
@@ -80,6 +126,13 @@ int nfc_dev_up(struct nfc_dev *dev)
        if (!rc)
                dev->dev_up = true;
 
+       /* We have to enable the device before discovering SEs */
+       if (dev->ops->discover_se) {
+               rc = dev->ops->discover_se(dev);
+               if (!rc)
+                       pr_warn("SE discovery failed\n");
+       }
+
 error:
        device_unlock(&dev->dev);
        return rc;
@@ -475,6 +528,108 @@ error:
        return rc;
 }
 
+static struct nfc_se *find_se(struct nfc_dev *dev, u32 se_idx)
+{
+       struct nfc_se *se, *n;
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list)
+               if (se->idx == se_idx)
+                       return se;
+
+       return NULL;
+}
+
+int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
+{
+
+       struct nfc_se *se;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (!dev->dev_up) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (dev->polling) {
+               rc = -EBUSY;
+               goto error;
+       }
+
+       if (!dev->ops->enable_se || !dev->ops->disable_se) {
+               rc = -EOPNOTSUPP;
+               goto error;
+       }
+
+       se = find_se(dev, se_idx);
+       if (!se) {
+               rc = -EINVAL;
+               goto error;
+       }
+
+       if (se->type == NFC_SE_ENABLED) {
+               rc = -EALREADY;
+               goto error;
+       }
+
+       rc = dev->ops->enable_se(dev, se_idx);
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
+{
+
+       struct nfc_se *se;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (!dev->dev_up) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (!dev->ops->enable_se || !dev->ops->disable_se) {
+               rc = -EOPNOTSUPP;
+               goto error;
+       }
+
+       se = find_se(dev, se_idx);
+       if (!se) {
+               rc = -EINVAL;
+               goto error;
+       }
+
+       if (se->type == NFC_SE_DISABLED) {
+               rc = -EALREADY;
+               goto error;
+       }
+
+       rc = dev->ops->disable_se(dev, se_idx);
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
 int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len)
 {
        pr_debug("dev_name=%s gb_len=%d\n", dev_name(&dev->dev), gb_len);
@@ -707,14 +862,79 @@ inline void nfc_driver_failure(struct nfc_dev *dev, int err)
 }
 EXPORT_SYMBOL(nfc_driver_failure);
 
+int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type)
+{
+       struct nfc_se *se;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       se = find_se(dev, se_idx);
+       if (se)
+               return -EALREADY;
+
+       se = kzalloc(sizeof(struct nfc_se), GFP_KERNEL);
+       if (!se)
+               return -ENOMEM;
+
+       se->idx = se_idx;
+       se->type = type;
+       se->state = NFC_SE_DISABLED;
+       INIT_LIST_HEAD(&se->list);
+
+       list_add(&se->list, &dev->secure_elements);
+
+       rc = nfc_genl_se_added(dev, se_idx, type);
+       if (rc < 0) {
+               list_del(&se->list);
+               kfree(se);
+
+               return rc;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(nfc_add_se);
+
+int nfc_remove_se(struct nfc_dev *dev, u32 se_idx)
+{
+       struct nfc_se *se, *n;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list)
+               if (se->idx == se_idx) {
+                       rc = nfc_genl_se_removed(dev, se_idx);
+                       if (rc < 0)
+                               return rc;
+
+                       list_del(&se->list);
+                       kfree(se);
+
+                       return 0;
+               }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(nfc_remove_se);
+
 static void nfc_release(struct device *d)
 {
        struct nfc_dev *dev = to_nfc_dev(d);
+       struct nfc_se *se, *n;
 
        pr_debug("dev_name=%s\n", dev_name(&dev->dev));
 
        nfc_genl_data_exit(&dev->genl_data);
        kfree(dev->targets);
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list) {
+                       nfc_genl_se_removed(dev, se->idx);
+                       list_del(&se->list);
+                       kfree(se);
+       }
+
        kfree(dev);
 }
 
@@ -786,7 +1006,6 @@ struct nfc_dev *nfc_get_device(unsigned int idx)
  */
 struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                                    u32 supported_protocols,
-                                   u32 supported_se,
                                    int tx_headroom, int tx_tailroom)
 {
        struct nfc_dev *dev;
@@ -804,10 +1023,9 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
 
        dev->ops = ops;
        dev->supported_protocols = supported_protocols;
-       dev->supported_se = supported_se;
-       dev->active_se = NFC_SE_NONE;
        dev->tx_headroom = tx_headroom;
        dev->tx_tailroom = tx_tailroom;
+       INIT_LIST_HEAD(&dev->secure_elements);
 
        nfc_genl_data_init(&dev->genl_data);
 
index 91020b2..7b1c186 100644 (file)
@@ -570,21 +570,21 @@ static int hci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->dep_link_up)
-               return hdev->ops->dep_link_up(hdev, target, comm_mode,
-                                               gb, gb_len);
+       if (!hdev->ops->dep_link_up)
+               return 0;
 
-       return 0;
+       return hdev->ops->dep_link_up(hdev, target, comm_mode,
+                                     gb, gb_len);
 }
 
 static int hci_dep_link_down(struct nfc_dev *nfc_dev)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->dep_link_down)
-               return hdev->ops->dep_link_down(hdev);
+       if (!hdev->ops->dep_link_down)
+               return 0;
 
-       return 0;
+       return hdev->ops->dep_link_down(hdev);
 }
 
 static int hci_activate_target(struct nfc_dev *nfc_dev,
@@ -673,12 +673,12 @@ static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->tm_send)
-               return hdev->ops->tm_send(hdev, skb);
-
-       kfree_skb(skb);
+       if (!hdev->ops->tm_send) {
+               kfree_skb(skb);
+               return -ENOTSUPP;
+       }
 
-       return -ENOTSUPP;
+       return hdev->ops->tm_send(hdev, skb);
 }
 
 static int hci_check_presence(struct nfc_dev *nfc_dev,
@@ -686,8 +686,38 @@ static int hci_check_presence(struct nfc_dev *nfc_dev,
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->check_presence)
-               return hdev->ops->check_presence(hdev, target);
+       if (!hdev->ops->check_presence)
+               return 0;
+
+       return hdev->ops->check_presence(hdev, target);
+}
+
+static int hci_discover_se(struct nfc_dev *nfc_dev)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->discover_se)
+               return hdev->ops->discover_se(hdev);
+
+       return 0;
+}
+
+static int hci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->enable_se)
+               return hdev->ops->enable_se(hdev, se_idx);
+
+       return 0;
+}
+
+static int hci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->disable_se)
+               return hdev->ops->enable_se(hdev, se_idx);
 
        return 0;
 }
@@ -779,6 +809,16 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
        }
 }
 
+static int hci_fw_upload(struct nfc_dev *nfc_dev, const char *firmware_name)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (!hdev->ops->fw_upload)
+               return -ENOTSUPP;
+
+       return hdev->ops->fw_upload(hdev, firmware_name);
+}
+
 static struct nfc_ops hci_nfc_ops = {
        .dev_up = hci_dev_up,
        .dev_down = hci_dev_down,
@@ -791,13 +831,16 @@ static struct nfc_ops hci_nfc_ops = {
        .im_transceive = hci_transceive,
        .tm_send = hci_tm_send,
        .check_presence = hci_check_presence,
+       .fw_upload = hci_fw_upload,
+       .discover_se = hci_discover_se,
+       .enable_se = hci_enable_se,
+       .disable_se = hci_disable_se,
 };
 
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
                                            unsigned long quirks,
                                            u32 protocols,
-                                           u32 supported_se,
                                            const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
@@ -823,7 +866,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                return NULL;
        }
 
-       hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, supported_se,
+       hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
                                         tx_headroom + HCI_CMDS_HEADROOM,
                                         tx_tailroom);
        if (!hdev->ndev) {
index ff8c434..f4d48b5 100644 (file)
@@ -19,6 +19,8 @@
 
 enum llcp_state {
        LLCP_CONNECTED = 1, /* wait_for_packet() wants that */
+       LLCP_CONNECTING,
+       LLCP_DISCONNECTING,
        LLCP_CLOSED,
        LLCP_BOUND,
        LLCP_LISTEN,
@@ -246,7 +248,6 @@ struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri,
 void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp);
 void nfc_llcp_free_sdp_tlv_list(struct hlist_head *sdp_head);
 void nfc_llcp_recv(void *data, struct sk_buff *skb, int err);
-int nfc_llcp_disconnect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_symm(struct nfc_dev *dev);
 int nfc_llcp_send_connect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_cc(struct nfc_llcp_sock *sock);
index c1b23ee..1017894 100644 (file)
@@ -339,7 +339,7 @@ static struct sk_buff *llcp_allocate_pdu(struct nfc_llcp_sock *sock,
        return skb;
 }
 
-int nfc_llcp_disconnect(struct nfc_llcp_sock *sock)
+int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock)
 {
        struct sk_buff *skb;
        struct nfc_dev *dev;
@@ -630,26 +630,6 @@ int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason)
        return 0;
 }
 
-int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock)
-{
-       struct sk_buff *skb;
-       struct nfc_llcp_local *local;
-
-       pr_debug("Send DISC\n");
-
-       local = sock->local;
-       if (local == NULL)
-               return -ENODEV;
-
-       skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0);
-       if (skb == NULL)
-               return -ENOMEM;
-
-       skb_queue_head(&local->tx_queue, skb);
-
-       return 0;
-}
-
 int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
                          struct msghdr *msg, size_t len)
 {
index 158bdbf..81cd341 100644 (file)
@@ -537,6 +537,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        u8 *lto_tlv, lto_length;
        u8 *wks_tlv, wks_length;
        u8 *miux_tlv, miux_length;
+       __be16 wks = cpu_to_be16(local->local_wks);
        u8 gb_len = 0;
        int ret = 0;
 
@@ -549,8 +550,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        gb_len += lto_length;
 
        pr_debug("Local wks 0x%lx\n", local->local_wks);
-       wks_tlv = nfc_llcp_build_tlv(LLCP_TLV_WKS, (u8 *)&local->local_wks, 2,
-                                    &wks_length);
+       wks_tlv = nfc_llcp_build_tlv(LLCP_TLV_WKS, (u8 *)&wks, 2, &wks_length);
        gb_len += wks_length;
 
        miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
@@ -719,6 +719,10 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                llcp_sock = nfc_llcp_sock(sk);
 
                if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
+                       kfree_skb(skb);
+                       nfc_llcp_send_symm(local->dev);
+               } else if (llcp_sock && !llcp_sock->remote_ready) {
+                       skb_queue_head(&local->tx_queue, skb);
                        nfc_llcp_send_symm(local->dev);
                } else {
                        struct sk_buff *copy_skb = NULL;
@@ -730,6 +734,13 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                                       DUMP_PREFIX_OFFSET, 16, 1,
                                       skb->data, skb->len, true);
 
+                       if (ptype == LLCP_PDU_DISC && sk != NULL &&
+                           sk->sk_state == LLCP_DISCONNECTING) {
+                               nfc_llcp_sock_unlink(&local->sockets, sk);
+                               sock_orphan(sk);
+                               sock_put(sk);
+                       }
+
                        if (ptype == LLCP_PDU_I)
                                copy_skb = skb_copy(skb, GFP_ATOMIC);
 
@@ -1579,6 +1590,7 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
        local->lto = 150; /* 1500 ms */
        local->rw = LLCP_MAX_RW;
        local->miux = cpu_to_be16(LLCP_MAX_MIUX);
+       local->local_wks = 0x1; /* LLC Link Management */
 
        nfc_llcp_build_gb(local);
 
index 380253e..d308402 100644 (file)
@@ -571,7 +571,7 @@ static unsigned int llcp_sock_poll(struct file *file, struct socket *sock,
        if (sk->sk_shutdown == SHUTDOWN_MASK)
                mask |= POLLHUP;
 
-       if (sock_writeable(sk))
+       if (sock_writeable(sk) && sk->sk_state == LLCP_CONNECTED)
                mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
        else
                set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
@@ -603,7 +603,7 @@ static int llcp_sock_release(struct socket *sock)
 
        /* Send a DISC */
        if (sk->sk_state == LLCP_CONNECTED)
-               nfc_llcp_disconnect(llcp_sock);
+               nfc_llcp_send_disconnect(llcp_sock);
 
        if (sk->sk_state == LLCP_LISTEN) {
                struct nfc_llcp_sock *lsk, *n;
@@ -614,7 +614,7 @@ static int llcp_sock_release(struct socket *sock)
                        accept_sk = &lsk->sk;
                        lock_sock(accept_sk);
 
-                       nfc_llcp_disconnect(lsk);
+                       nfc_llcp_send_disconnect(lsk);
                        nfc_llcp_accept_unlink(accept_sk);
 
                        release_sock(accept_sk);
@@ -626,6 +626,13 @@ static int llcp_sock_release(struct socket *sock)
 
        release_sock(sk);
 
+       /* Keep this sock alive and therefore do not remove it from the sockets
+        * list until the DISC PDU has been actually sent. Otherwise we would
+        * reply with DM PDUs before sending the DISC one.
+        */
+       if (sk->sk_state == LLCP_DISCONNECTING)
+               return err;
+
        if (sock->type == SOCK_RAW)
                nfc_llcp_sock_unlink(&local->raw_sockets, sk);
        else
@@ -722,14 +729,16 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
        if (ret)
                goto sock_unlink;
 
+       sk->sk_state = LLCP_CONNECTING;
+
        ret = sock_wait_state(sk, LLCP_CONNECTED,
                              sock_sndtimeo(sk, flags & O_NONBLOCK));
-       if (ret)
+       if (ret && ret != -EINPROGRESS)
                goto sock_unlink;
 
        release_sock(sk);
 
-       return 0;
+       return ret;
 
 sock_unlink:
        nfc_llcp_put_ssap(local, llcp_sock->ssap);
index 6d69b5f..2a24160 100644 (file)
@@ -8,3 +8,13 @@ config NFC_NCI
 
          Say Y here to compile NCI support into the kernel or say M to
          compile it as module (nci).
+
+config NFC_NCI_SPI
+       depends on NFC_NCI && SPI
+       bool "NCI over SPI protocol support"
+       default n
+       help
+         NCI (NFC Controller Interface) is a communication protocol between
+         an NFC Controller (NFCC) and a Device Host (DH).
+
+         Say yes if you use an NCI driver that requires SPI link layer.
index cdb3a2e..7aeedc4 100644 (file)
@@ -4,4 +4,6 @@
 
 obj-$(CONFIG_NFC_NCI) += nci.o
 
-nci-objs := core.o data.o lib.o ntf.o rsp.o
\ No newline at end of file
+nci-objs := core.o data.o lib.o ntf.o rsp.o
+
+nci-$(CONFIG_NFC_NCI_SPI) += spi.o
index 48ada0e..b943d46 100644 (file)
@@ -636,6 +636,21 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
        return rc;
 }
 
+static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+       return 0;
+}
+
+static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+       return 0;
+}
+
+static int nci_discover_se(struct nfc_dev *nfc_dev)
+{
+       return 0;
+}
+
 static struct nfc_ops nci_nfc_ops = {
        .dev_up = nci_dev_up,
        .dev_down = nci_dev_down,
@@ -646,6 +661,9 @@ static struct nfc_ops nci_nfc_ops = {
        .activate_target = nci_activate_target,
        .deactivate_target = nci_deactivate_target,
        .im_transceive = nci_transceive,
+       .enable_se = nci_enable_se,
+       .disable_se = nci_disable_se,
+       .discover_se = nci_discover_se,
 };
 
 /* ---- Interface to NCI drivers ---- */
@@ -658,7 +676,6 @@ static struct nfc_ops nci_nfc_ops = {
  */
 struct nci_dev *nci_allocate_device(struct nci_ops *ops,
                                    __u32 supported_protocols,
-                                   __u32 supported_se,
                                    int tx_headroom, int tx_tailroom)
 {
        struct nci_dev *ndev;
@@ -681,7 +698,6 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,
 
        ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
                                            supported_protocols,
-                                           supported_se,
                                            tx_headroom + NCI_DATA_HDR_SIZE,
                                            tx_tailroom);
        if (!ndev->nfc_dev)
@@ -797,12 +813,11 @@ EXPORT_SYMBOL(nci_unregister_device);
 /**
  * nci_recv_frame - receive frame from NCI drivers
  *
+ * @ndev: The nci device
  * @skb: The sk_buff to receive
  */
-int nci_recv_frame(struct sk_buff *skb)
+int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
 {
-       struct nci_dev *ndev = (struct nci_dev *) skb->dev;
-
        pr_debug("len %d\n", skb->len);
 
        if (!ndev || (!test_bit(NCI_UP, &ndev->flags) &&
@@ -819,10 +834,8 @@ int nci_recv_frame(struct sk_buff *skb)
 }
 EXPORT_SYMBOL(nci_recv_frame);
 
-static int nci_send_frame(struct sk_buff *skb)
+static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
 {
-       struct nci_dev *ndev = (struct nci_dev *) skb->dev;
-
        pr_debug("len %d\n", skb->len);
 
        if (!ndev) {
@@ -833,7 +846,7 @@ static int nci_send_frame(struct sk_buff *skb)
        /* Get rid of skb owner, prior to sending to the driver. */
        skb_orphan(skb);
 
-       return ndev->ops->send(skb);
+       return ndev->ops->send(ndev, skb);
 }
 
 /* Send NCI command */
@@ -861,8 +874,6 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload)
        if (plen)
                memcpy(skb_put(skb, plen), payload, plen);
 
-       skb->dev = (void *) ndev;
-
        skb_queue_tail(&ndev->cmd_q, skb);
        queue_work(ndev->cmd_wq, &ndev->cmd_work);
 
@@ -894,7 +905,7 @@ static void nci_tx_work(struct work_struct *work)
                         nci_conn_id(skb->data),
                         nci_plen(skb->data));
 
-               nci_send_frame(skb);
+               nci_send_frame(ndev, skb);
 
                mod_timer(&ndev->data_timer,
                          jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT));
@@ -963,7 +974,7 @@ static void nci_cmd_work(struct work_struct *work)
                         nci_opcode_oid(nci_opcode(skb->data)),
                         nci_plen(skb->data));
 
-               nci_send_frame(skb);
+               nci_send_frame(ndev, skb);
 
                mod_timer(&ndev->cmd_timer,
                          jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT));
index 76c48c5..2a9399d 100644 (file)
@@ -80,8 +80,6 @@ static inline void nci_push_data_hdr(struct nci_dev *ndev,
 
        nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
        nci_pbf_set((__u8 *)hdr, pbf);
-
-       skb->dev = (void *) ndev;
 }
 
 static int nci_queue_tx_data_frags(struct nci_dev *ndev,
diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c
new file mode 100644 (file)
index 0000000..c7cf37b
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#define pr_fmt(fmt) "nci_spi: %s: " fmt, __func__
+
+#include <linux/export.h>
+#include <linux/spi/spi.h>
+#include <linux/crc-ccitt.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci_core.h>
+
+#define NCI_SPI_HDR_LEN                        4
+#define NCI_SPI_CRC_LEN                        2
+#define NCI_SPI_ACK_SHIFT              6
+#define NCI_SPI_MSB_PAYLOAD_MASK       0x3F
+
+#define NCI_SPI_SEND_TIMEOUT   (NCI_CMD_TIMEOUT > NCI_DATA_TIMEOUT ? \
+                                       NCI_CMD_TIMEOUT : NCI_DATA_TIMEOUT)
+
+#define NCI_SPI_DIRECT_WRITE   0x01
+#define NCI_SPI_DIRECT_READ    0x02
+
+#define ACKNOWLEDGE_NONE       0
+#define ACKNOWLEDGE_ACK                1
+#define ACKNOWLEDGE_NACK       2
+
+#define CRC_INIT               0xFFFF
+
+static int nci_spi_open(struct nci_dev *nci_dev)
+{
+       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+
+       return ndev->ops->open(ndev);
+}
+
+static int nci_spi_close(struct nci_dev *nci_dev)
+{
+       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+
+       return ndev->ops->close(ndev);
+}
+
+static int __nci_spi_send(struct nci_spi_dev *ndev, struct sk_buff *skb)
+{
+       struct spi_message m;
+       struct spi_transfer t;
+
+       t.tx_buf = skb->data;
+       t.len = skb->len;
+       t.cs_change = 0;
+       t.delay_usecs = ndev->xfer_udelay;
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t, &m);
+
+       return spi_sync(ndev->spi, &m);
+}
+
+static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb)
+{
+       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+       unsigned int payload_len = skb->len;
+       unsigned char *hdr;
+       int ret;
+       long completion_rc;
+
+       ndev->ops->deassert_int(ndev);
+
+       /* add the NCI SPI header to the start of the buffer */
+       hdr = skb_push(skb, NCI_SPI_HDR_LEN);
+       hdr[0] = NCI_SPI_DIRECT_WRITE;
+       hdr[1] = ndev->acknowledge_mode;
+       hdr[2] = payload_len >> 8;
+       hdr[3] = payload_len & 0xFF;
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+               u16 crc;
+
+               crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
+               *skb_put(skb, 1) = crc >> 8;
+               *skb_put(skb, 1) = crc & 0xFF;
+       }
+
+       ret = __nci_spi_send(ndev, skb);
+
+       kfree_skb(skb);
+       ndev->ops->assert_int(ndev);
+
+       if (ret != 0 || ndev->acknowledge_mode == NCI_SPI_CRC_DISABLED)
+               goto done;
+
+       init_completion(&ndev->req_completion);
+       completion_rc =
+               wait_for_completion_interruptible_timeout(&ndev->req_completion,
+                                                         NCI_SPI_SEND_TIMEOUT);
+
+       if (completion_rc <= 0 || ndev->req_result == ACKNOWLEDGE_NACK)
+               ret = -EIO;
+
+done:
+       return ret;
+}
+
+static struct nci_ops nci_spi_ops = {
+       .open = nci_spi_open,
+       .close = nci_spi_close,
+       .send = nci_spi_send,
+};
+
+/* ---- Interface to NCI SPI drivers ---- */
+
+/**
+ * nci_spi_allocate_device - allocate a new nci spi device
+ *
+ * @spi: SPI device
+ * @ops: device operations
+ * @supported_protocols: NFC protocols supported by the device
+ * @supported_se: NFC Secure Elements supported by the device
+ * @acknowledge_mode: Acknowledge mode used by the device
+ * @delay: delay between transactions in us
+ */
+struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
+                                               struct nci_spi_ops *ops,
+                                               u32 supported_protocols,
+                                               u32 supported_se,
+                                               u8 acknowledge_mode,
+                                               unsigned int delay)
+{
+       struct nci_spi_dev *ndev;
+       int tailroom = 0;
+
+       if (!ops->open || !ops->close || !ops->assert_int || !ops->deassert_int)
+               return NULL;
+
+       if (!supported_protocols)
+               return NULL;
+
+       ndev = devm_kzalloc(&spi->dev, sizeof(struct nci_dev), GFP_KERNEL);
+       if (!ndev)
+               return NULL;
+
+       ndev->ops = ops;
+       ndev->acknowledge_mode = acknowledge_mode;
+       ndev->xfer_udelay = delay;
+
+       if (acknowledge_mode == NCI_SPI_CRC_ENABLED)
+               tailroom += NCI_SPI_CRC_LEN;
+
+       ndev->nci_dev = nci_allocate_device(&nci_spi_ops, supported_protocols,
+                                           NCI_SPI_HDR_LEN, tailroom);
+       if (!ndev->nci_dev)
+               return NULL;
+
+       nci_set_drvdata(ndev->nci_dev, ndev);
+
+       return ndev;
+}
+EXPORT_SYMBOL_GPL(nci_spi_allocate_device);
+
+/**
+ * nci_spi_free_device - deallocate nci spi device
+ *
+ * @ndev: The nci spi device to deallocate
+ */
+void nci_spi_free_device(struct nci_spi_dev *ndev)
+{
+       nci_free_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_free_device);
+
+/**
+ * nci_spi_register_device - register a nci spi device in the nfc subsystem
+ *
+ * @pdev: The nci spi device to register
+ */
+int nci_spi_register_device(struct nci_spi_dev *ndev)
+{
+       return nci_register_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_register_device);
+
+/**
+ * nci_spi_unregister_device - unregister a nci spi device in the nfc subsystem
+ *
+ * @dev: The nci spi device to unregister
+ */
+void nci_spi_unregister_device(struct nci_spi_dev *ndev)
+{
+       nci_unregister_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_unregister_device);
+
+static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge)
+{
+       struct sk_buff *skb;
+       unsigned char *hdr;
+       u16 crc;
+       int ret;
+
+       skb = nci_skb_alloc(ndev->nci_dev, 0, GFP_KERNEL);
+
+       /* add the NCI SPI header to the start of the buffer */
+       hdr = skb_push(skb, NCI_SPI_HDR_LEN);
+       hdr[0] = NCI_SPI_DIRECT_WRITE;
+       hdr[1] = NCI_SPI_CRC_ENABLED;
+       hdr[2] = acknowledge << NCI_SPI_ACK_SHIFT;
+       hdr[3] = 0;
+
+       crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
+       *skb_put(skb, 1) = crc >> 8;
+       *skb_put(skb, 1) = crc & 0xFF;
+
+       ret = __nci_spi_send(ndev, skb);
+
+       kfree_skb(skb);
+
+       return ret;
+}
+
+static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev)
+{
+       struct sk_buff *skb;
+       struct spi_message m;
+       unsigned char req[2], resp_hdr[2];
+       struct spi_transfer tx, rx;
+       unsigned short rx_len = 0;
+       int ret;
+
+       spi_message_init(&m);
+       req[0] = NCI_SPI_DIRECT_READ;
+       req[1] = ndev->acknowledge_mode;
+       tx.tx_buf = req;
+       tx.len = 2;
+       tx.cs_change = 0;
+       spi_message_add_tail(&tx, &m);
+       rx.rx_buf = resp_hdr;
+       rx.len = 2;
+       rx.cs_change = 1;
+       spi_message_add_tail(&rx, &m);
+       ret = spi_sync(ndev->spi, &m);
+
+       if (ret)
+               return NULL;
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+               rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
+                               resp_hdr[1] + NCI_SPI_CRC_LEN;
+       else
+               rx_len = (resp_hdr[0] << 8) | resp_hdr[1];
+
+       skb = nci_skb_alloc(ndev->nci_dev, rx_len, GFP_KERNEL);
+       if (!skb)
+               return NULL;
+
+       spi_message_init(&m);
+       rx.rx_buf = skb_put(skb, rx_len);
+       rx.len = rx_len;
+       rx.cs_change = 0;
+       rx.delay_usecs = ndev->xfer_udelay;
+       spi_message_add_tail(&rx, &m);
+       ret = spi_sync(ndev->spi, &m);
+
+       if (ret)
+               goto receive_error;
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+               *skb_push(skb, 1) = resp_hdr[1];
+               *skb_push(skb, 1) = resp_hdr[0];
+       }
+
+       return skb;
+
+receive_error:
+       kfree_skb(skb);
+
+       return NULL;
+}
+
+static int nci_spi_check_crc(struct sk_buff *skb)
+{
+       u16 crc_data = (skb->data[skb->len - 2] << 8) |
+                       skb->data[skb->len - 1];
+       int ret;
+
+       ret = (crc_ccitt(CRC_INIT, skb->data, skb->len - NCI_SPI_CRC_LEN)
+                       == crc_data);
+
+       skb_trim(skb, skb->len - NCI_SPI_CRC_LEN);
+
+       return ret;
+}
+
+static u8 nci_spi_get_ack(struct sk_buff *skb)
+{
+       u8 ret;
+
+       ret = skb->data[0] >> NCI_SPI_ACK_SHIFT;
+
+       /* Remove NFCC part of the header: ACK, NACK and MSB payload len */
+       skb_pull(skb, 2);
+
+       return ret;
+}
+
+/**
+ * nci_spi_recv_frame - receive frame from NCI SPI drivers
+ *
+ * @ndev: The nci spi device
+ * Context: can sleep
+ *
+ * This call may only be used from a context that may sleep.  The sleep
+ * is non-interruptible, and has no timeout.
+ *
+ * It returns zero on success, else a negative error code.
+ */
+int nci_spi_recv_frame(struct nci_spi_dev *ndev)
+{
+       struct sk_buff *skb;
+       int ret = 0;
+
+       ndev->ops->deassert_int(ndev);
+
+       /* Retrieve frame from SPI */
+       skb = __nci_spi_recv_frame(ndev);
+       if (!skb) {
+               ret = -EIO;
+               goto done;
+       }
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+               if (!nci_spi_check_crc(skb)) {
+                       send_acknowledge(ndev, ACKNOWLEDGE_NACK);
+                       goto done;
+               }
+
+               /* In case of acknowledged mode: if ACK or NACK received,
+                * unblock completion of latest frame sent.
+                */
+               ndev->req_result = nci_spi_get_ack(skb);
+               if (ndev->req_result)
+                       complete(&ndev->req_completion);
+       }
+
+       /* If there is no payload (ACK/NACK only frame),
+        * free the socket buffer
+        */
+       if (skb->len == 0) {
+               kfree_skb(skb);
+               goto done;
+       }
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+               send_acknowledge(ndev, ACKNOWLEDGE_ACK);
+
+       /* Forward skb to NCI core layer */
+       ret = nci_recv_frame(ndev->nci_dev, skb);
+
+done:
+       ndev->ops->assert_int(ndev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nci_spi_recv_frame);
index f0c4d61..b05ad90 100644 (file)
@@ -56,6 +56,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
        [NFC_ATTR_LLC_PARAM_RW] = { .type = NLA_U8 },
        [NFC_ATTR_LLC_PARAM_MIUX] = { .type = NLA_U16 },
        [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED },
+       [NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING,
+                                    .len = NFC_FIRMWARE_NAME_MAXSIZE },
 };
 
 static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
@@ -424,6 +426,69 @@ free_msg:
        return rc;
 }
 
+int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_EVENT_SE_ADDED);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
+           nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) ||
+           nla_put_u8(msg, NFC_ATTR_SE_TYPE, type))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
+int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_EVENT_SE_REMOVED);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
+           nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
 static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
                                u32 portid, u32 seq,
                                struct netlink_callback *cb,
@@ -442,7 +507,6 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
        if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
            nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
            nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
-           nla_put_u32(msg, NFC_ATTR_SE, dev->supported_se) ||
            nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) ||
            nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
                goto nla_put_failure;
@@ -1025,6 +1089,108 @@ exit:
        return rc;
 }
 
+static int nfc_genl_fw_upload(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx;
+       char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       nla_strlcpy(firmware_name, info->attrs[NFC_ATTR_FIRMWARE_NAME],
+                   sizeof(firmware_name));
+
+       rc = nfc_fw_upload(dev, firmware_name);
+
+       nfc_put_device(dev);
+       return rc;
+}
+
+int nfc_genl_fw_upload_done(struct nfc_dev *dev, const char *firmware_name)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_CMD_FW_UPLOAD);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) ||
+           nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
+static int nfc_genl_enable_se(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx, se_idx;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           !info->attrs[NFC_ATTR_SE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+       se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       rc = nfc_enable_se(dev, se_idx);
+
+       nfc_put_device(dev);
+       return rc;
+}
+
+static int nfc_genl_disable_se(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx, se_idx;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           !info->attrs[NFC_ATTR_SE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+       se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       rc = nfc_disable_se(dev, se_idx);
+
+       nfc_put_device(dev);
+       return rc;
+}
+
 static struct genl_ops nfc_genl_ops[] = {
        {
                .cmd = NFC_CMD_GET_DEVICE,
@@ -1084,6 +1250,21 @@ static struct genl_ops nfc_genl_ops[] = {
                .doit = nfc_genl_llc_sdreq,
                .policy = nfc_genl_policy,
        },
+       {
+               .cmd = NFC_CMD_FW_UPLOAD,
+               .doit = nfc_genl_fw_upload,
+               .policy = nfc_genl_policy,
+       },
+       {
+               .cmd = NFC_CMD_ENABLE_SE,
+               .doit = nfc_genl_enable_se,
+               .policy = nfc_genl_policy,
+       },
+       {
+               .cmd = NFC_CMD_DISABLE_SE,
+               .doit = nfc_genl_disable_se,
+               .policy = nfc_genl_policy,
+       },
 };
 
 
index afa1f84..ee85a1f 100644 (file)
@@ -94,6 +94,9 @@ int nfc_genl_tm_deactivated(struct nfc_dev *dev);
 
 int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list);
 
+int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type);
+int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx);
+
 struct nfc_dev *nfc_get_device(unsigned int idx);
 
 static inline void nfc_put_device(struct nfc_dev *dev)
@@ -120,6 +123,11 @@ static inline void nfc_device_iter_exit(struct class_dev_iter *iter)
        class_dev_iter_exit(iter);
 }
 
+int nfc_fw_upload(struct nfc_dev *dev, const char *firmware_name);
+int nfc_genl_fw_upload_done(struct nfc_dev *dev, const char *firmware_name);
+
+int nfc_fw_upload_done(struct nfc_dev *dev, const char *firmware_name);
+
 int nfc_dev_up(struct nfc_dev *dev);
 
 int nfc_dev_down(struct nfc_dev *dev);
@@ -139,4 +147,7 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx);
 int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
                      data_exchange_cb_t cb, void *cb_context);
 
+int nfc_enable_se(struct nfc_dev *dev, u32 se_idx);
+int nfc_disable_se(struct nfc_dev *dev, u32 se_idx);
+
 #endif /* __LOCAL_NFC_H */
index d9ea33c..27ee56b 100644 (file)
@@ -26,3 +26,17 @@ config OPENVSWITCH
          called openvswitch.
 
          If unsure, say N.
+
+config OPENVSWITCH_GRE
+       bool "Open vSwitch GRE tunneling support"
+       depends on INET
+       depends on OPENVSWITCH
+       depends on NET_IPGRE_DEMUX && !(OPENVSWITCH=y && NET_IPGRE_DEMUX=m)
+       default y
+       ---help---
+         If you say Y here, then the Open vSwitch will be able create GRE
+         vport.
+
+         Say N to exclude this support and reduce the binary size.
+
+         If unsure, say Y.
index 15e7384..01bddb2 100644 (file)
@@ -10,5 +10,6 @@ openvswitch-y := \
        dp_notify.o \
        flow.o \
        vport.o \
+       vport-gre.o \
        vport-internal_dev.o \
-       vport-netdev.o \
+       vport-netdev.o
index 894b6cb..22c5f39 100644 (file)
@@ -130,9 +130,13 @@ static int set_eth_addr(struct sk_buff *skb,
        if (unlikely(err))
                return err;
 
+       skb_postpull_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2);
+
        memcpy(eth_hdr(skb)->h_source, eth_key->eth_src, ETH_ALEN);
        memcpy(eth_hdr(skb)->h_dest, eth_key->eth_dst, ETH_ALEN);
 
+       ovs_skb_postpush_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2);
+
        return 0;
 }
 
@@ -432,6 +436,10 @@ static int execute_set_action(struct sk_buff *skb,
                skb->mark = nla_get_u32(nested_attr);
                break;
 
+       case OVS_KEY_ATTR_IPV4_TUNNEL:
+               OVS_CB(skb)->tun_key = nla_data(nested_attr);
+               break;
+
        case OVS_KEY_ATTR_ETHERNET:
                err = set_eth_addr(skb, nla_data(nested_attr));
                break;
index d12d6b8..f7e3a0d 100644 (file)
@@ -362,6 +362,14 @@ static int queue_gso_packets(struct net *net, int dp_ifindex,
 static size_t key_attr_size(void)
 {
        return    nla_total_size(4)   /* OVS_KEY_ATTR_PRIORITY */
+               + nla_total_size(0)   /* OVS_KEY_ATTR_TUNNEL */
+                 + nla_total_size(8)   /* OVS_TUNNEL_KEY_ATTR_ID */
+                 + nla_total_size(4)   /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */
+                 + nla_total_size(4)   /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */
+                 + nla_total_size(1)   /* OVS_TUNNEL_KEY_ATTR_TOS */
+                 + nla_total_size(1)   /* OVS_TUNNEL_KEY_ATTR_TTL */
+                 + nla_total_size(0)   /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
+                 + nla_total_size(0)   /* OVS_TUNNEL_KEY_ATTR_CSUM */
                + nla_total_size(4)   /* OVS_KEY_ATTR_IN_PORT */
                + nla_total_size(4)   /* OVS_KEY_ATTR_SKB_MARK */
                + nla_total_size(12)  /* OVS_KEY_ATTR_ETHERNET */
@@ -464,16 +472,89 @@ static int flush_flows(struct datapath *dp)
        return 0;
 }
 
-static int validate_actions(const struct nlattr *attr,
-                               const struct sw_flow_key *key, int depth);
+static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa, int attr_len)
+{
+
+       struct sw_flow_actions *acts;
+       int new_acts_size;
+       int req_size = NLA_ALIGN(attr_len);
+       int next_offset = offsetof(struct sw_flow_actions, actions) +
+                                       (*sfa)->actions_len;
+
+       if (req_size <= (ksize(*sfa) - next_offset))
+               goto out;
+
+       new_acts_size = ksize(*sfa) * 2;
+
+       if (new_acts_size > MAX_ACTIONS_BUFSIZE) {
+               if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size)
+                       return ERR_PTR(-EMSGSIZE);
+               new_acts_size = MAX_ACTIONS_BUFSIZE;
+       }
+
+       acts = ovs_flow_actions_alloc(new_acts_size);
+       if (IS_ERR(acts))
+               return (void *)acts;
+
+       memcpy(acts->actions, (*sfa)->actions, (*sfa)->actions_len);
+       acts->actions_len = (*sfa)->actions_len;
+       kfree(*sfa);
+       *sfa = acts;
+
+out:
+       (*sfa)->actions_len += req_size;
+       return  (struct nlattr *) ((unsigned char *)(*sfa) + next_offset);
+}
+
+static int add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len)
+{
+       struct nlattr *a;
+
+       a = reserve_sfa_size(sfa, nla_attr_size(len));
+       if (IS_ERR(a))
+               return PTR_ERR(a);
+
+       a->nla_type = attrtype;
+       a->nla_len = nla_attr_size(len);
+
+       if (data)
+               memcpy(nla_data(a), data, len);
+       memset((unsigned char *) a + a->nla_len, 0, nla_padlen(len));
+
+       return 0;
+}
+
+static inline int add_nested_action_start(struct sw_flow_actions **sfa, int attrtype)
+{
+       int used = (*sfa)->actions_len;
+       int err;
+
+       err = add_action(sfa, attrtype, NULL, 0);
+       if (err)
+               return err;
+
+       return used;
+}
 
-static int validate_sample(const struct nlattr *attr,
-                               const struct sw_flow_key *key, int depth)
+static inline void add_nested_action_end(struct sw_flow_actions *sfa, int st_offset)
+{
+       struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions + st_offset);
+
+       a->nla_len = sfa->actions_len - st_offset;
+}
+
+static int validate_and_copy_actions(const struct nlattr *attr,
+                                    const struct sw_flow_key *key, int depth,
+                                    struct sw_flow_actions **sfa);
+
+static int validate_and_copy_sample(const struct nlattr *attr,
+                                   const struct sw_flow_key *key, int depth,
+                                   struct sw_flow_actions **sfa)
 {
        const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1];
        const struct nlattr *probability, *actions;
        const struct nlattr *a;
-       int rem;
+       int rem, start, err, st_acts;
 
        memset(attrs, 0, sizeof(attrs));
        nla_for_each_nested(a, attr, rem) {
@@ -492,7 +573,26 @@ static int validate_sample(const struct nlattr *attr,
        actions = attrs[OVS_SAMPLE_ATTR_ACTIONS];
        if (!actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN))
                return -EINVAL;
-       return validate_actions(actions, key, depth + 1);
+
+       /* validation done, copy sample action. */
+       start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE);
+       if (start < 0)
+               return start;
+       err = add_action(sfa, OVS_SAMPLE_ATTR_PROBABILITY, nla_data(probability), sizeof(u32));
+       if (err)
+               return err;
+       st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS);
+       if (st_acts < 0)
+               return st_acts;
+
+       err = validate_and_copy_actions(actions, key, depth + 1, sfa);
+       if (err)
+               return err;
+
+       add_nested_action_end(*sfa, st_acts);
+       add_nested_action_end(*sfa, start);
+
+       return 0;
 }
 
 static int validate_tp_port(const struct sw_flow_key *flow_key)
@@ -508,8 +608,30 @@ static int validate_tp_port(const struct sw_flow_key *flow_key)
        return -EINVAL;
 }
 
+static int validate_and_copy_set_tun(const struct nlattr *attr,
+                                    struct sw_flow_actions **sfa)
+{
+       struct ovs_key_ipv4_tunnel tun_key;
+       int err, start;
+
+       err = ovs_ipv4_tun_from_nlattr(nla_data(attr), &tun_key);
+       if (err)
+               return err;
+
+       start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET);
+       if (start < 0)
+               return start;
+
+       err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key, sizeof(tun_key));
+       add_nested_action_end(*sfa, start);
+
+       return err;
+}
+
 static int validate_set(const struct nlattr *a,
-                       const struct sw_flow_key *flow_key)
+                       const struct sw_flow_key *flow_key,
+                       struct sw_flow_actions **sfa,
+                       bool *set_tun)
 {
        const struct nlattr *ovs_key = nla_data(a);
        int key_type = nla_type(ovs_key);
@@ -519,18 +641,27 @@ static int validate_set(const struct nlattr *a,
                return -EINVAL;
 
        if (key_type > OVS_KEY_ATTR_MAX ||
-           nla_len(ovs_key) != ovs_key_lens[key_type])
+          (ovs_key_lens[key_type] != nla_len(ovs_key) &&
+           ovs_key_lens[key_type] != -1))
                return -EINVAL;
 
        switch (key_type) {
        const struct ovs_key_ipv4 *ipv4_key;
        const struct ovs_key_ipv6 *ipv6_key;
+       int err;
 
        case OVS_KEY_ATTR_PRIORITY:
        case OVS_KEY_ATTR_SKB_MARK:
        case OVS_KEY_ATTR_ETHERNET:
                break;
 
+       case OVS_KEY_ATTR_TUNNEL:
+               *set_tun = true;
+               err = validate_and_copy_set_tun(a, sfa);
+               if (err)
+                       return err;
+               break;
+
        case OVS_KEY_ATTR_IPV4:
                if (flow_key->eth.type != htons(ETH_P_IP))
                        return -EINVAL;
@@ -606,8 +737,24 @@ static int validate_userspace(const struct nlattr *attr)
        return 0;
 }
 
-static int validate_actions(const struct nlattr *attr,
-                               const struct sw_flow_key *key,  int depth)
+static int copy_action(const struct nlattr *from,
+                      struct sw_flow_actions **sfa)
+{
+       int totlen = NLA_ALIGN(from->nla_len);
+       struct nlattr *to;
+
+       to = reserve_sfa_size(sfa, from->nla_len);
+       if (IS_ERR(to))
+               return PTR_ERR(to);
+
+       memcpy(to, from, totlen);
+       return 0;
+}
+
+static int validate_and_copy_actions(const struct nlattr *attr,
+                                    const struct sw_flow_key *key,
+                                    int depth,
+                                    struct sw_flow_actions **sfa)
 {
        const struct nlattr *a;
        int rem, err;
@@ -627,12 +774,14 @@ static int validate_actions(const struct nlattr *attr,
                };
                const struct ovs_action_push_vlan *vlan;
                int type = nla_type(a);
+               bool skip_copy;
 
                if (type > OVS_ACTION_ATTR_MAX ||
                    (action_lens[type] != nla_len(a) &&
                     action_lens[type] != (u32)-1))
                        return -EINVAL;
 
+               skip_copy = false;
                switch (type) {
                case OVS_ACTION_ATTR_UNSPEC:
                        return -EINVAL;
@@ -661,20 +810,26 @@ static int validate_actions(const struct nlattr *attr,
                        break;
 
                case OVS_ACTION_ATTR_SET:
-                       err = validate_set(a, key);
+                       err = validate_set(a, key, sfa, &skip_copy);
                        if (err)
                                return err;
                        break;
 
                case OVS_ACTION_ATTR_SAMPLE:
-                       err = validate_sample(a, key, depth);
+                       err = validate_and_copy_sample(a, key, depth, sfa);
                        if (err)
                                return err;
+                       skip_copy = true;
                        break;
 
                default:
                        return -EINVAL;
                }
+               if (!skip_copy) {
+                       err = copy_action(a, sfa);
+                       if (err)
+                               return err;
+               }
        }
 
        if (rem > 0)
@@ -739,24 +894,18 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto err_flow_free;
 
-       err = ovs_flow_metadata_from_nlattrs(&flow->key.phy.priority,
-                                            &flow->key.phy.skb_mark,
-                                            &flow->key.phy.in_port,
-                                            a[OVS_PACKET_ATTR_KEY]);
-       if (err)
-               goto err_flow_free;
-
-       err = validate_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 0);
+       err = ovs_flow_metadata_from_nlattrs(flow, key_len, a[OVS_PACKET_ATTR_KEY]);
        if (err)
                goto err_flow_free;
-
-       flow->hash = ovs_flow_hash(&flow->key, key_len);
-
-       acts = ovs_flow_actions_alloc(a[OVS_PACKET_ATTR_ACTIONS]);
+       acts = ovs_flow_actions_alloc(nla_len(a[OVS_PACKET_ATTR_ACTIONS]));
        err = PTR_ERR(acts);
        if (IS_ERR(acts))
                goto err_flow_free;
+
+       err = validate_and_copy_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 0, &acts);
        rcu_assign_pointer(flow->sf_acts, acts);
+       if (err)
+               goto err_flow_free;
 
        OVS_CB(packet)->flow = flow;
        packet->priority = flow->key.phy.priority;
@@ -846,6 +995,99 @@ static struct genl_multicast_group ovs_dp_flow_multicast_group = {
        .name = OVS_FLOW_MCGROUP
 };
 
+static int actions_to_attr(const struct nlattr *attr, int len, struct sk_buff *skb);
+static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb)
+{
+       const struct nlattr *a;
+       struct nlattr *start;
+       int err = 0, rem;
+
+       start = nla_nest_start(skb, OVS_ACTION_ATTR_SAMPLE);
+       if (!start)
+               return -EMSGSIZE;
+
+       nla_for_each_nested(a, attr, rem) {
+               int type = nla_type(a);
+               struct nlattr *st_sample;
+
+               switch (type) {
+               case OVS_SAMPLE_ATTR_PROBABILITY:
+                       if (nla_put(skb, OVS_SAMPLE_ATTR_PROBABILITY, sizeof(u32), nla_data(a)))
+                               return -EMSGSIZE;
+                       break;
+               case OVS_SAMPLE_ATTR_ACTIONS:
+                       st_sample = nla_nest_start(skb, OVS_SAMPLE_ATTR_ACTIONS);
+                       if (!st_sample)
+                               return -EMSGSIZE;
+                       err = actions_to_attr(nla_data(a), nla_len(a), skb);
+                       if (err)
+                               return err;
+                       nla_nest_end(skb, st_sample);
+                       break;
+               }
+       }
+
+       nla_nest_end(skb, start);
+       return err;
+}
+
+static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
+{
+       const struct nlattr *ovs_key = nla_data(a);
+       int key_type = nla_type(ovs_key);
+       struct nlattr *start;
+       int err;
+
+       switch (key_type) {
+       case OVS_KEY_ATTR_IPV4_TUNNEL:
+               start = nla_nest_start(skb, OVS_ACTION_ATTR_SET);
+               if (!start)
+                       return -EMSGSIZE;
+
+               err = ovs_ipv4_tun_to_nlattr(skb, nla_data(ovs_key));
+               if (err)
+                       return err;
+               nla_nest_end(skb, start);
+               break;
+       default:
+               if (nla_put(skb, OVS_ACTION_ATTR_SET, nla_len(a), ovs_key))
+                       return -EMSGSIZE;
+               break;
+       }
+
+       return 0;
+}
+
+static int actions_to_attr(const struct nlattr *attr, int len, struct sk_buff *skb)
+{
+       const struct nlattr *a;
+       int rem, err;
+
+       nla_for_each_attr(a, attr, len, rem) {
+               int type = nla_type(a);
+
+               switch (type) {
+               case OVS_ACTION_ATTR_SET:
+                       err = set_action_to_attr(a, skb);
+                       if (err)
+                               return err;
+                       break;
+
+               case OVS_ACTION_ATTR_SAMPLE:
+                       err = sample_action_to_attr(a, skb);
+                       if (err)
+                               return err;
+                       break;
+               default:
+                       if (nla_put(skb, type, nla_len(a), nla_data(a)))
+                               return -EMSGSIZE;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
 static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
 {
        return NLMSG_ALIGN(sizeof(struct ovs_header))
@@ -863,6 +1105,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
 {
        const int skb_orig_len = skb->len;
        const struct sw_flow_actions *sf_acts;
+       struct nlattr *start;
        struct ovs_flow_stats stats;
        struct ovs_header *ovs_header;
        struct nlattr *nla;
@@ -916,10 +1159,19 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
         * This can only fail for dump operations because the skb is always
         * properly sized for single flows.
         */
-       err = nla_put(skb, OVS_FLOW_ATTR_ACTIONS, sf_acts->actions_len,
-                     sf_acts->actions);
-       if (err < 0 && skb_orig_len)
-               goto error;
+       start = nla_nest_start(skb, OVS_FLOW_ATTR_ACTIONS);
+       if (start) {
+               err = actions_to_attr(sf_acts->actions, sf_acts->actions_len, skb);
+               if (!err)
+                       nla_nest_end(skb, start);
+               else {
+                       if (skb_orig_len)
+                               goto error;
+
+                       nla_nest_cancel(skb, start);
+               }
+       } else if (skb_orig_len)
+               goto nla_put_failure;
 
        return genlmsg_end(skb, ovs_header);
 
@@ -964,6 +1216,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
        struct sk_buff *reply;
        struct datapath *dp;
        struct flow_table *table;
+       struct sw_flow_actions *acts = NULL;
        int error;
        int key_len;
 
@@ -977,9 +1230,14 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 
        /* Validate actions. */
        if (a[OVS_FLOW_ATTR_ACTIONS]) {
-               error = validate_actions(a[OVS_FLOW_ATTR_ACTIONS], &key,  0);
-               if (error)
+               acts = ovs_flow_actions_alloc(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
+               error = PTR_ERR(acts);
+               if (IS_ERR(acts))
                        goto error;
+
+               error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &key,  0, &acts);
+               if (error)
+                       goto err_kfree;
        } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
                error = -EINVAL;
                goto error;
@@ -994,8 +1252,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
        table = ovsl_dereference(dp->table);
        flow = ovs_flow_tbl_lookup(table, &key, key_len);
        if (!flow) {
-               struct sw_flow_actions *acts;
-
                /* Bail out if we're not allowed to create a new flow. */
                error = -ENOENT;
                if (info->genlhdr->cmd == OVS_FLOW_CMD_SET)
@@ -1019,19 +1275,12 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                        error = PTR_ERR(flow);
                        goto err_unlock_ovs;
                }
-               flow->key = key;
                clear_stats(flow);
 
-               /* Obtain actions. */
-               acts = ovs_flow_actions_alloc(a[OVS_FLOW_ATTR_ACTIONS]);
-               error = PTR_ERR(acts);
-               if (IS_ERR(acts))
-                       goto error_free_flow;
                rcu_assign_pointer(flow->sf_acts, acts);
 
                /* Put flow in bucket. */
-               flow->hash = ovs_flow_hash(&key, key_len);
-               ovs_flow_tbl_insert(table, flow);
+               ovs_flow_tbl_insert(table, flow, &key, key_len);
 
                reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                                info->snd_seq,
@@ -1039,7 +1288,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
        } else {
                /* We found a matching flow. */
                struct sw_flow_actions *old_acts;
-               struct nlattr *acts_attrs;
 
                /* Bail out if we're not allowed to modify an existing flow.
                 * We accept NLM_F_CREATE in place of the intended NLM_F_EXCL
@@ -1054,21 +1302,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 
                /* Update actions. */
                old_acts = ovsl_dereference(flow->sf_acts);
-               acts_attrs = a[OVS_FLOW_ATTR_ACTIONS];
-               if (acts_attrs &&
-                  (old_acts->actions_len != nla_len(acts_attrs) ||
-                  memcmp(old_acts->actions, nla_data(acts_attrs),
-                         old_acts->actions_len))) {
-                       struct sw_flow_actions *new_acts;
-
-                       new_acts = ovs_flow_actions_alloc(acts_attrs);
-                       error = PTR_ERR(new_acts);
-                       if (IS_ERR(new_acts))
-                               goto err_unlock_ovs;
-
-                       rcu_assign_pointer(flow->sf_acts, new_acts);
-                       ovs_flow_deferred_free_acts(old_acts);
-               }
+               rcu_assign_pointer(flow->sf_acts, acts);
+               ovs_flow_deferred_free_acts(old_acts);
 
                reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                               info->snd_seq, OVS_FLOW_CMD_NEW);
@@ -1089,10 +1324,10 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                                ovs_dp_flow_multicast_group.id, PTR_ERR(reply));
        return 0;
 
-error_free_flow:
-       ovs_flow_free(flow);
 err_unlock_ovs:
        ovs_unlock();
+err_kfree:
+       kfree(acts);
 error:
        return error;
 }
@@ -1812,10 +2047,11 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(vport))
                goto exit_unlock;
 
-       err = 0;
        if (a[OVS_VPORT_ATTR_TYPE] &&
-           nla_get_u32(a[OVS_VPORT_ATTR_TYPE]) != vport->ops->type)
+           nla_get_u32(a[OVS_VPORT_ATTR_TYPE]) != vport->ops->type) {
                err = -EINVAL;
+               goto exit_unlock;
+       }
 
        reply = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!reply) {
@@ -1823,10 +2059,11 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
                goto exit_unlock;
        }
 
-       if (!err && a[OVS_VPORT_ATTR_OPTIONS])
+       if (a[OVS_VPORT_ATTR_OPTIONS]) {
                err = ovs_vport_set_options(vport, a[OVS_VPORT_ATTR_OPTIONS]);
-       if (err)
-               goto exit_free;
+               if (err)
+                       goto exit_free;
+       }
 
        if (a[OVS_VPORT_ATTR_UPCALL_PID])
                vport->upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]);
@@ -1867,8 +2104,8 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
                goto exit_unlock;
        }
 
-       reply = ovs_vport_cmd_build_info(vport, info->snd_portid, info->snd_seq,
-                                        OVS_VPORT_CMD_DEL);
+       reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
+                                        info->snd_seq, OVS_VPORT_CMD_DEL);
        err = PTR_ERR(reply);
        if (IS_ERR(reply))
                goto exit_unlock;
@@ -1897,8 +2134,8 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(vport))
                goto exit_unlock;
 
-       reply = ovs_vport_cmd_build_info(vport, info->snd_portid, info->snd_seq,
-                                        OVS_VPORT_CMD_NEW);
+       reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
+                                        info->snd_seq, OVS_VPORT_CMD_NEW);
        err = PTR_ERR(reply);
        if (IS_ERR(reply))
                goto exit_unlock;
index 16b8406..a914864 100644 (file)
@@ -88,9 +88,12 @@ struct datapath {
 /**
  * struct ovs_skb_cb - OVS data in skb CB
  * @flow: The flow associated with this packet.  May be %NULL if no flow.
+ * @tun_key: Key for the tunnel that encapsulated this packet. NULL if the
+ * packet is not being tunneled.
  */
 struct ovs_skb_cb {
        struct sw_flow          *flow;
+       struct ovs_key_ipv4_tunnel  *tun_key;
 };
 #define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
 
@@ -119,6 +122,7 @@ struct dp_upcall_info {
 struct ovs_net {
        struct list_head dps;
        struct work_struct dp_notify_work;
+       struct vport_net vport_net;
 };
 
 extern int ovs_net_id;
index ef4feec..c323567 100644 (file)
@@ -78,7 +78,7 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event,
                           void *ptr)
 {
        struct ovs_net *ovs_net;
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct vport *vport = NULL;
 
        if (!ovs_is_internal_dev(dev))
index b15321a..5c519b1 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/icmpv6.h>
 #include <linux/rculist.h>
 #include <net/ip.h>
+#include <net/ip_tunnels.h>
 #include <net/ipv6.h>
 #include <net/ndisc.h>
 
@@ -198,20 +199,18 @@ void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb)
        spin_unlock(&flow->lock);
 }
 
-struct sw_flow_actions *ovs_flow_actions_alloc(const struct nlattr *actions)
+struct sw_flow_actions *ovs_flow_actions_alloc(int size)
 {
-       int actions_len = nla_len(actions);
        struct sw_flow_actions *sfa;
 
-       if (actions_len > MAX_ACTIONS_BUFSIZE)
+       if (size > MAX_ACTIONS_BUFSIZE)
                return ERR_PTR(-EINVAL);
 
-       sfa = kmalloc(sizeof(*sfa) + actions_len, GFP_KERNEL);
+       sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL);
        if (!sfa)
                return ERR_PTR(-ENOMEM);
 
-       sfa->actions_len = actions_len;
-       nla_memcpy(sfa->actions, actions, actions_len);
+       sfa->actions_len = 0;
        return sfa;
 }
 
@@ -354,6 +353,14 @@ struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *la
        return NULL;
 }
 
+static void __flow_tbl_insert(struct flow_table *table, struct sw_flow *flow)
+{
+       struct hlist_head *head;
+       head = find_bucket(table, flow->hash);
+       hlist_add_head_rcu(&flow->hash_node[table->node_ver], head);
+       table->count++;
+}
+
 static void flow_table_copy_flows(struct flow_table *old, struct flow_table *new)
 {
        int old_ver;
@@ -370,7 +377,7 @@ static void flow_table_copy_flows(struct flow_table *old, struct flow_table *new
                head = flex_array_get(old->buckets, i);
 
                hlist_for_each_entry(flow, head, hash_node[old_ver])
-                       ovs_flow_tbl_insert(new, flow);
+                       __flow_tbl_insert(new, flow);
        }
        old->keep_flows = true;
 }
@@ -590,10 +597,10 @@ out:
  *    - skb->network_header: just past the Ethernet header, or just past the
  *      VLAN header, to the first byte of the Ethernet payload.
  *
- *    - skb->transport_header: If key->dl_type is ETH_P_IP or ETH_P_IPV6
+ *    - skb->transport_header: If key->eth.type is ETH_P_IP or ETH_P_IPV6
  *      on output, then just past the IP header, if one is present and
  *      of a correct length, otherwise the same as skb->network_header.
- *      For other key->dl_type values it is left untouched.
+ *      For other key->eth.type values it is left untouched.
  */
 int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
                 int *key_lenp)
@@ -605,6 +612,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
        memset(key, 0, sizeof(*key));
 
        key->phy.priority = skb->priority;
+       if (OVS_CB(skb)->tun_key)
+               memcpy(&key->tun_key, OVS_CB(skb)->tun_key, sizeof(key->tun_key));
        key->phy.in_port = in_port;
        key->phy.skb_mark = skb->mark;
 
@@ -618,6 +627,9 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
        memcpy(key->eth.dst, eth->h_dest, ETH_ALEN);
 
        __skb_pull(skb, 2 * ETH_ALEN);
+       /* We are going to push all headers that we pull, so no need to
+        * update skb->csum here.
+        */
 
        if (vlan_tx_tag_present(skb))
                key->eth.tci = htons(skb->vlan_tci);
@@ -759,9 +771,18 @@ out:
        return error;
 }
 
-u32 ovs_flow_hash(const struct sw_flow_key *key, int key_len)
+static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start, int key_len)
+{
+       return jhash2((u32 *)((u8 *)key + key_start),
+                     DIV_ROUND_UP(key_len - key_start, sizeof(u32)), 0);
+}
+
+static int flow_key_start(struct sw_flow_key *key)
 {
-       return jhash2((u32 *)key, DIV_ROUND_UP(key_len, sizeof(u32)), 0);
+       if (key->tun_key.ipv4_dst)
+               return 0;
+       else
+               return offsetof(struct sw_flow_key, phy);
 }
 
 struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *table,
@@ -769,28 +790,31 @@ struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *table,
 {
        struct sw_flow *flow;
        struct hlist_head *head;
+       u8 *_key;
+       int key_start;
        u32 hash;
 
-       hash = ovs_flow_hash(key, key_len);
+       key_start = flow_key_start(key);
+       hash = ovs_flow_hash(key, key_start, key_len);
 
+       _key = (u8 *) key + key_start;
        head = find_bucket(table, hash);
        hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) {
 
                if (flow->hash == hash &&
-                   !memcmp(&flow->key, key, key_len)) {
+                   !memcmp((u8 *)&flow->key + key_start, _key, key_len - key_start)) {
                        return flow;
                }
        }
        return NULL;
 }
 
-void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow)
+void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
+                        struct sw_flow_key *key, int key_len)
 {
-       struct hlist_head *head;
-
-       head = find_bucket(table, flow->hash);
-       hlist_add_head_rcu(&flow->hash_node[table->node_ver], head);
-       table->count++;
+       flow->hash = ovs_flow_hash(key, flow_key_start(key), key_len);
+       memcpy(&flow->key, key, sizeof(flow->key));
+       __flow_tbl_insert(table, flow);
 }
 
 void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
@@ -817,6 +841,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
        [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6),
        [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp),
        [OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd),
+       [OVS_KEY_ATTR_TUNNEL] = -1,
 };
 
 static int ipv4_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len,
@@ -954,6 +979,105 @@ static int parse_flow_nlattrs(const struct nlattr *attr,
        return 0;
 }
 
+int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr,
+                            struct ovs_key_ipv4_tunnel *tun_key)
+{
+       struct nlattr *a;
+       int rem;
+       bool ttl = false;
+
+       memset(tun_key, 0, sizeof(*tun_key));
+
+       nla_for_each_nested(a, attr, rem) {
+               int type = nla_type(a);
+               static const u32 ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = {
+                       [OVS_TUNNEL_KEY_ATTR_ID] = sizeof(u64),
+                       [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = sizeof(u32),
+                       [OVS_TUNNEL_KEY_ATTR_IPV4_DST] = sizeof(u32),
+                       [OVS_TUNNEL_KEY_ATTR_TOS] = 1,
+                       [OVS_TUNNEL_KEY_ATTR_TTL] = 1,
+                       [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = 0,
+                       [OVS_TUNNEL_KEY_ATTR_CSUM] = 0,
+               };
+
+               if (type > OVS_TUNNEL_KEY_ATTR_MAX ||
+                       ovs_tunnel_key_lens[type] != nla_len(a))
+                       return -EINVAL;
+
+               switch (type) {
+               case OVS_TUNNEL_KEY_ATTR_ID:
+                       tun_key->tun_id = nla_get_be64(a);
+                       tun_key->tun_flags |= TUNNEL_KEY;
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
+                       tun_key->ipv4_src = nla_get_be32(a);
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
+                       tun_key->ipv4_dst = nla_get_be32(a);
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_TOS:
+                       tun_key->ipv4_tos = nla_get_u8(a);
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_TTL:
+                       tun_key->ipv4_ttl = nla_get_u8(a);
+                       ttl = true;
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
+                       tun_key->tun_flags |= TUNNEL_DONT_FRAGMENT;
+                       break;
+               case OVS_TUNNEL_KEY_ATTR_CSUM:
+                       tun_key->tun_flags |= TUNNEL_CSUM;
+                       break;
+               default:
+                       return -EINVAL;
+
+               }
+       }
+       if (rem > 0)
+               return -EINVAL;
+
+       if (!tun_key->ipv4_dst)
+               return -EINVAL;
+
+       if (!ttl)
+               return -EINVAL;
+
+       return 0;
+}
+
+int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb,
+                          const struct ovs_key_ipv4_tunnel *tun_key)
+{
+       struct nlattr *nla;
+
+       nla = nla_nest_start(skb, OVS_KEY_ATTR_TUNNEL);
+       if (!nla)
+               return -EMSGSIZE;
+
+       if (tun_key->tun_flags & TUNNEL_KEY &&
+           nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id))
+               return -EMSGSIZE;
+       if (tun_key->ipv4_src &&
+           nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tun_key->ipv4_src))
+               return -EMSGSIZE;
+       if (nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ipv4_dst))
+               return -EMSGSIZE;
+       if (tun_key->ipv4_tos &&
+           nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ipv4_tos))
+               return -EMSGSIZE;
+       if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, tun_key->ipv4_ttl))
+               return -EMSGSIZE;
+       if ((tun_key->tun_flags & TUNNEL_DONT_FRAGMENT) &&
+               nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT))
+               return -EMSGSIZE;
+       if ((tun_key->tun_flags & TUNNEL_CSUM) &&
+               nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM))
+               return -EMSGSIZE;
+
+       nla_nest_end(skb, nla);
+       return 0;
+}
+
 /**
  * ovs_flow_from_nlattrs - parses Netlink attributes into a flow key.
  * @swkey: receives the extracted flow key.
@@ -996,6 +1120,14 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
                attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK);
        }
 
+       if (attrs & (1 << OVS_KEY_ATTR_TUNNEL)) {
+               err = ovs_ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], &swkey->tun_key);
+               if (err)
+                       return err;
+
+               attrs &= ~(1 << OVS_KEY_ATTR_TUNNEL);
+       }
+
        /* Data attributes. */
        if (!(attrs & (1 << OVS_KEY_ATTR_ETHERNET)))
                return -EINVAL;
@@ -1122,10 +1254,9 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 
 /**
  * ovs_flow_metadata_from_nlattrs - parses Netlink attributes into a flow key.
- * @priority: receives the skb priority
- * @mark: receives the skb mark
- * @in_port: receives the extracted input port.
- * @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
+ * @flow: Receives extracted in_port, priority, tun_key and skb_mark.
+ * @key_len: Length of key in @flow.  Used for calculating flow hash.
+ * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
  * sequence.
  *
  * This parses a series of Netlink attributes that form a flow key, which must
@@ -1133,42 +1264,56 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
  * get the metadata, that is, the parts of the flow key that cannot be
  * extracted from the packet itself.
  */
-int ovs_flow_metadata_from_nlattrs(u32 *priority, u32 *mark, u16 *in_port,
-                              const struct nlattr *attr)
+int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len,
+                                  const struct nlattr *attr)
 {
+       struct ovs_key_ipv4_tunnel *tun_key = &flow->key.tun_key;
        const struct nlattr *nla;
        int rem;
 
-       *in_port = DP_MAX_PORTS;
-       *priority = 0;
-       *mark = 0;
+       flow->key.phy.in_port = DP_MAX_PORTS;
+       flow->key.phy.priority = 0;
+       flow->key.phy.skb_mark = 0;
+       memset(tun_key, 0, sizeof(flow->key.tun_key));
 
        nla_for_each_nested(nla, attr, rem) {
                int type = nla_type(nla);
 
                if (type <= OVS_KEY_ATTR_MAX && ovs_key_lens[type] > 0) {
+                       int err;
+
                        if (nla_len(nla) != ovs_key_lens[type])
                                return -EINVAL;
 
                        switch (type) {
                        case OVS_KEY_ATTR_PRIORITY:
-                               *priority = nla_get_u32(nla);
+                               flow->key.phy.priority = nla_get_u32(nla);
+                               break;
+
+                       case OVS_KEY_ATTR_TUNNEL:
+                               err = ovs_ipv4_tun_from_nlattr(nla, tun_key);
+                               if (err)
+                                       return err;
                                break;
 
                        case OVS_KEY_ATTR_IN_PORT:
                                if (nla_get_u32(nla) >= DP_MAX_PORTS)
                                        return -EINVAL;
-                               *in_port = nla_get_u32(nla);
+                               flow->key.phy.in_port = nla_get_u32(nla);
                                break;
 
                        case OVS_KEY_ATTR_SKB_MARK:
-                               *mark = nla_get_u32(nla);
+                               flow->key.phy.skb_mark = nla_get_u32(nla);
                                break;
                        }
                }
        }
        if (rem)
                return -EINVAL;
+
+       flow->hash = ovs_flow_hash(&flow->key,
+                                  flow_key_start(&flow->key), key_len);
+
        return 0;
 }
 
@@ -1181,6 +1326,10 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
            nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, swkey->phy.priority))
                goto nla_put_failure;
 
+       if (swkey->tun_key.ipv4_dst &&
+           ovs_ipv4_tun_to_nlattr(skb, &swkey->tun_key))
+               goto nla_put_failure;
+
        if (swkey->phy.in_port != DP_MAX_PORTS &&
            nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, swkey->phy.in_port))
                goto nla_put_failure;
index 0875fde..66ef722 100644 (file)
@@ -40,7 +40,38 @@ struct sw_flow_actions {
        struct nlattr actions[];
 };
 
+/* Used to memset ovs_key_ipv4_tunnel padding. */
+#define OVS_TUNNEL_KEY_SIZE                                    \
+       (offsetof(struct ovs_key_ipv4_tunnel, ipv4_ttl) +       \
+       FIELD_SIZEOF(struct ovs_key_ipv4_tunnel, ipv4_ttl))
+
+struct ovs_key_ipv4_tunnel {
+       __be64 tun_id;
+       __be32 ipv4_src;
+       __be32 ipv4_dst;
+       __be16 tun_flags;
+       u8   ipv4_tos;
+       u8   ipv4_ttl;
+};
+
+static inline void ovs_flow_tun_key_init(struct ovs_key_ipv4_tunnel *tun_key,
+                                        const struct iphdr *iph, __be64 tun_id,
+                                        __be16 tun_flags)
+{
+       tun_key->tun_id = tun_id;
+       tun_key->ipv4_src = iph->saddr;
+       tun_key->ipv4_dst = iph->daddr;
+       tun_key->ipv4_tos = iph->tos;
+       tun_key->ipv4_ttl = iph->ttl;
+       tun_key->tun_flags = tun_flags;
+
+       /* clear struct padding. */
+       memset((unsigned char *) tun_key + OVS_TUNNEL_KEY_SIZE, 0,
+              sizeof(*tun_key) - OVS_TUNNEL_KEY_SIZE);
+}
+
 struct sw_flow_key {
+       struct ovs_key_ipv4_tunnel tun_key;  /* Encapsulating tunnel key. */
        struct {
                u32     priority;       /* Packet QoS priority. */
                u32     skb_mark;       /* SKB mark. */
@@ -130,7 +161,7 @@ struct sw_flow *ovs_flow_alloc(void);
 void ovs_flow_deferred_free(struct sw_flow *);
 void ovs_flow_free(struct sw_flow *flow);
 
-struct sw_flow_actions *ovs_flow_actions_alloc(const struct nlattr *);
+struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len);
 void ovs_flow_deferred_free_acts(struct sw_flow_actions *);
 
 int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *,
@@ -141,10 +172,10 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies);
 int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
 int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
                      const struct nlattr *);
-int ovs_flow_metadata_from_nlattrs(u32 *priority, u32 *mark, u16 *in_port,
-                              const struct nlattr *);
+int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len,
+                                 const struct nlattr *attr);
 
-#define MAX_ACTIONS_BUFSIZE    (16 * 1024)
+#define MAX_ACTIONS_BUFSIZE    (32 * 1024)
 #define TBL_MIN_BUCKETS                1024
 
 struct flow_table {
@@ -173,11 +204,15 @@ void ovs_flow_tbl_deferred_destroy(struct flow_table *table);
 struct flow_table *ovs_flow_tbl_alloc(int new_size);
 struct flow_table *ovs_flow_tbl_expand(struct flow_table *table);
 struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table);
-void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow);
+void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
+                        struct sw_flow_key *key, int key_len);
 void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow);
-u32 ovs_flow_hash(const struct sw_flow_key *key, int key_len);
 
 struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *idx);
 extern const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1];
+int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr,
+                        struct ovs_key_ipv4_tunnel *tun_key);
+int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb,
+                       const struct ovs_key_ipv4_tunnel *tun_key);
 
 #endif /* flow.h */
diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c
new file mode 100644 (file)
index 0000000..493e977
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2007-2013 Nicira, Inc.
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifdef CONFIG_OPENVSWITCH_GRE
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/if.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include <linux/if_vlan.h>
+#include <linux/in.h>
+#include <linux/if_vlan.h>
+#include <linux/in.h>
+#include <linux/in_route.h>
+#include <linux/inetdevice.h>
+#include <linux/jhash.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/rculist.h>
+#include <net/route.h>
+#include <net/xfrm.h>
+
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/ip_tunnels.h>
+#include <net/gre.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/protocol.h>
+
+#include "datapath.h"
+#include "vport.h"
+
+/* Returns the least-significant 32 bits of a __be64. */
+static __be32 be64_get_low32(__be64 x)
+{
+#ifdef __BIG_ENDIAN
+       return (__force __be32)x;
+#else
+       return (__force __be32)((__force u64)x >> 32);
+#endif
+}
+
+static __be16 filter_tnl_flags(__be16 flags)
+{
+       return flags & (TUNNEL_CSUM | TUNNEL_KEY);
+}
+
+static struct sk_buff *__build_header(struct sk_buff *skb,
+                                     int tunnel_hlen)
+{
+       const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
+       struct tnl_ptk_info tpi;
+
+       skb = gre_handle_offloads(skb, !!(tun_key->tun_flags & TUNNEL_CSUM));
+       if (IS_ERR(skb))
+               return NULL;
+
+       tpi.flags = filter_tnl_flags(tun_key->tun_flags);
+       tpi.proto = htons(ETH_P_TEB);
+       tpi.key = be64_get_low32(tun_key->tun_id);
+       tpi.seq = 0;
+       gre_build_header(skb, &tpi, tunnel_hlen);
+
+       return skb;
+}
+
+static __be64 key_to_tunnel_id(__be32 key, __be32 seq)
+{
+#ifdef __BIG_ENDIAN
+       return (__force __be64)((__force u64)seq << 32 | (__force u32)key);
+#else
+       return (__force __be64)((__force u64)key << 32 | (__force u32)seq);
+#endif
+}
+
+/* Called with rcu_read_lock and BH disabled. */
+static int gre_rcv(struct sk_buff *skb,
+                  const struct tnl_ptk_info *tpi)
+{
+       struct ovs_key_ipv4_tunnel tun_key;
+       struct ovs_net *ovs_net;
+       struct vport *vport;
+       __be64 key;
+
+       ovs_net = net_generic(dev_net(skb->dev), ovs_net_id);
+       vport = rcu_dereference(ovs_net->vport_net.gre_vport);
+       if (unlikely(!vport))
+               return PACKET_REJECT;
+
+       key = key_to_tunnel_id(tpi->key, tpi->seq);
+       ovs_flow_tun_key_init(&tun_key, ip_hdr(skb), key,
+                             filter_tnl_flags(tpi->flags));
+
+       ovs_vport_receive(vport, skb, &tun_key);
+       return PACKET_RCVD;
+}
+
+static int gre_tnl_send(struct vport *vport, struct sk_buff *skb)
+{
+       struct net *net = ovs_dp_get_net(vport->dp);
+       struct flowi4 fl;
+       struct rtable *rt;
+       int min_headroom;
+       int tunnel_hlen;
+       __be16 df;
+       int err;
+
+       if (unlikely(!OVS_CB(skb)->tun_key)) {
+               err = -EINVAL;
+               goto error;
+       }
+
+       /* Route lookup */
+       memset(&fl, 0, sizeof(fl));
+       fl.daddr = OVS_CB(skb)->tun_key->ipv4_dst;
+       fl.saddr = OVS_CB(skb)->tun_key->ipv4_src;
+       fl.flowi4_tos = RT_TOS(OVS_CB(skb)->tun_key->ipv4_tos);
+       fl.flowi4_mark = skb->mark;
+       fl.flowi4_proto = IPPROTO_GRE;
+
+       rt = ip_route_output_key(net, &fl);
+       if (IS_ERR(rt))
+               return PTR_ERR(rt);
+
+       tunnel_hlen = ip_gre_calc_hlen(OVS_CB(skb)->tun_key->tun_flags);
+
+       min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
+                       + tunnel_hlen + sizeof(struct iphdr)
+                       + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0);
+       if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) {
+               int head_delta = SKB_DATA_ALIGN(min_headroom -
+                                               skb_headroom(skb) +
+                                               16);
+               err = pskb_expand_head(skb, max_t(int, head_delta, 0),
+                                       0, GFP_ATOMIC);
+               if (unlikely(err))
+                       goto err_free_rt;
+       }
+
+       if (vlan_tx_tag_present(skb)) {
+               if (unlikely(!__vlan_put_tag(skb,
+                                            skb->vlan_proto,
+                                            vlan_tx_tag_get(skb)))) {
+                       err = -ENOMEM;
+                       goto err_free_rt;
+               }
+               skb->vlan_tci = 0;
+       }
+
+       /* Push Tunnel header. */
+       skb = __build_header(skb, tunnel_hlen);
+       if (unlikely(!skb)) {
+               err = 0;
+               goto err_free_rt;
+       }
+
+       df = OVS_CB(skb)->tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ?
+               htons(IP_DF) : 0;
+
+       skb->local_df = 1;
+
+       return iptunnel_xmit(net, rt, skb, fl.saddr,
+                            OVS_CB(skb)->tun_key->ipv4_dst, IPPROTO_GRE,
+                            OVS_CB(skb)->tun_key->ipv4_tos,
+                            OVS_CB(skb)->tun_key->ipv4_ttl, df);
+err_free_rt:
+       ip_rt_put(rt);
+error:
+       return err;
+}
+
+static struct gre_cisco_protocol gre_protocol = {
+       .handler        = gre_rcv,
+       .priority       = 1,
+};
+
+static int gre_ports;
+static int gre_init(void)
+{
+       int err;
+
+       gre_ports++;
+       if (gre_ports > 1)
+               return 0;
+
+       err = gre_cisco_register(&gre_protocol);
+       if (err)
+               pr_warn("cannot register gre protocol handler\n");
+
+       return err;
+}
+
+static void gre_exit(void)
+{
+       gre_ports--;
+       if (gre_ports > 0)
+               return;
+
+       gre_cisco_unregister(&gre_protocol);
+}
+
+static const char *gre_get_name(const struct vport *vport)
+{
+       return vport_priv(vport);
+}
+
+static struct vport *gre_create(const struct vport_parms *parms)
+{
+       struct net *net = ovs_dp_get_net(parms->dp);
+       struct ovs_net *ovs_net;
+       struct vport *vport;
+       int err;
+
+       err = gre_init();
+       if (err)
+               return ERR_PTR(err);
+
+       ovs_net = net_generic(net, ovs_net_id);
+       if (ovsl_dereference(ovs_net->vport_net.gre_vport)) {
+               vport = ERR_PTR(-EEXIST);
+               goto error;
+       }
+
+       vport = ovs_vport_alloc(IFNAMSIZ, &ovs_gre_vport_ops, parms);
+       if (IS_ERR(vport))
+               goto error;
+
+       strncpy(vport_priv(vport), parms->name, IFNAMSIZ);
+       rcu_assign_pointer(ovs_net->vport_net.gre_vport, vport);
+       return vport;
+
+error:
+       gre_exit();
+       return vport;
+}
+
+static void gre_tnl_destroy(struct vport *vport)
+{
+       struct net *net = ovs_dp_get_net(vport->dp);
+       struct ovs_net *ovs_net;
+
+       ovs_net = net_generic(net, ovs_net_id);
+
+       rcu_assign_pointer(ovs_net->vport_net.gre_vport, NULL);
+       ovs_vport_deferred_free(vport);
+       gre_exit();
+}
+
+const struct vport_ops ovs_gre_vport_ops = {
+       .type           = OVS_VPORT_TYPE_GRE,
+       .create         = gre_create,
+       .destroy        = gre_tnl_destroy,
+       .get_name       = gre_get_name,
+       .send           = gre_tnl_send,
+};
+
+#endif /* OPENVSWITCH_GRE */
index 84e0a03..98d3edb 100644 (file)
@@ -67,7 +67,7 @@ static struct rtnl_link_stats64 *internal_dev_get_stats(struct net_device *netde
 static int internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
        rcu_read_lock();
-       ovs_vport_receive(internal_dev_priv(netdev)->vport, skb);
+       ovs_vport_receive(internal_dev_priv(netdev)->vport, skb, NULL);
        rcu_read_unlock();
        return 0;
 }
@@ -221,6 +221,7 @@ static int internal_dev_recv(struct vport *vport, struct sk_buff *skb)
        skb->dev = netdev;
        skb->pkt_type = PACKET_HOST;
        skb->protocol = eth_type_trans(skb, netdev);
+       skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
 
        netif_rx(skb);
 
index 4f01c6d..5982f3f 100644 (file)
@@ -49,7 +49,9 @@ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb)
                return;
 
        skb_push(skb, ETH_HLEN);
-       ovs_vport_receive(vport, skb);
+       ovs_skb_postpush_rcsum(skb, skb->data, ETH_HLEN);
+
+       ovs_vport_receive(vport, skb, NULL);
        return;
 
 error:
@@ -170,7 +172,7 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
                net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",
                                     netdev_vport->dev->name,
                                     packet_length(skb), mtu);
-               goto error;
+               goto drop;
        }
 
        skb->dev = netdev_vport->dev;
@@ -179,9 +181,8 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
 
        return len;
 
-error:
+drop:
        kfree_skb(skb);
-       ovs_vport_record_error(vport, VPORT_E_TX_DROPPED);
        return 0;
 }
 
index a3cb3a3..dd298b5 100644 (file)
@@ -39,6 +39,5 @@ netdev_vport_priv(const struct vport *vport)
 }
 
 const char *ovs_netdev_get_name(const struct vport *);
-const char *ovs_netdev_get_config(const struct vport *);
 
 #endif /* vport_netdev.h */
index 7206231..d4c7fa0 100644 (file)
 static const struct vport_ops *vport_ops_list[] = {
        &ovs_netdev_vport_ops,
        &ovs_internal_vport_ops,
+
+#ifdef CONFIG_OPENVSWITCH_GRE
+       &ovs_gre_vport_ops,
+#endif
 };
 
 /* Protected by RCU read lock for reading, ovs_mutex for writing. */
@@ -325,7 +329,8 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
  * Must be called with rcu_read_lock.  The packet cannot be shared and
  * skb->data should point to the Ethernet header.
  */
-void ovs_vport_receive(struct vport *vport, struct sk_buff *skb)
+void ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
+                      struct ovs_key_ipv4_tunnel *tun_key)
 {
        struct pcpu_tstats *stats;
 
@@ -335,6 +340,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb)
        stats->rx_bytes += skb->len;
        u64_stats_update_end(&stats->syncp);
 
+       OVS_CB(skb)->tun_key = tun_key;
        ovs_dp_process_received_packet(vport, skb);
 }
 
@@ -351,7 +357,7 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
 {
        int sent = vport->ops->send(vport, skb);
 
-       if (likely(sent)) {
+       if (likely(sent > 0)) {
                struct pcpu_tstats *stats;
 
                stats = this_cpu_ptr(vport->percpu_stats);
@@ -360,7 +366,12 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
                stats->tx_packets++;
                stats->tx_bytes += sent;
                u64_stats_update_end(&stats->syncp);
-       }
+       } else if (sent < 0) {
+               ovs_vport_record_error(vport, VPORT_E_TX_ERROR);
+               kfree_skb(skb);
+       } else
+               ovs_vport_record_error(vport, VPORT_E_TX_DROPPED);
+
        return sent;
 }
 
@@ -371,7 +382,7 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
  * @err_type: one of enum vport_err_type types to indicate the error type
  *
  * If using the vport generic stats layer indicate that an error of the given
- * type has occured.
+ * type has occurred.
  */
 void ovs_vport_record_error(struct vport *vport, enum vport_err_type err_type)
 {
@@ -397,3 +408,18 @@ void ovs_vport_record_error(struct vport *vport, enum vport_err_type err_type)
 
        spin_unlock(&vport->stats_lock);
 }
+
+static void free_vport_rcu(struct rcu_head *rcu)
+{
+       struct vport *vport = container_of(rcu, struct vport, rcu);
+
+       ovs_vport_free(vport);
+}
+
+void ovs_vport_deferred_free(struct vport *vport)
+{
+       if (!vport)
+               return;
+
+       call_rcu(&vport->rcu, free_vport_rcu);
+}
index 68a377b..376045c 100644 (file)
@@ -34,6 +34,11 @@ struct vport_parms;
 
 /* The following definitions are for users of the vport subsytem: */
 
+/* The following definitions are for users of the vport subsytem: */
+struct vport_net {
+       struct vport __rcu *gre_vport;
+};
+
 int ovs_vport_init(void);
 void ovs_vport_exit(void);
 
@@ -123,9 +128,8 @@ struct vport_parms {
  * existing vport to a &struct sk_buff.  May be %NULL for a vport that does not
  * have any configuration.
  * @get_name: Get the device's name.
- * @get_config: Get the device's configuration.
- * May be null if the device does not have an ifindex.
- * @send: Send a packet on the device.  Returns the length of the packet sent.
+ * @send: Send a packet on the device.  Returns the length of the packet sent,
+ * zero for dropped packets or negative for error.
  */
 struct vport_ops {
        enum ovs_vport_type type;
@@ -139,7 +143,6 @@ struct vport_ops {
 
        /* Called with rcu_read_lock or ovs_mutex. */
        const char *(*get_name)(const struct vport *);
-       void (*get_config)(const struct vport *, void *);
 
        int (*send)(struct vport *, struct sk_buff *);
 };
@@ -154,6 +157,7 @@ enum vport_err_type {
 struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *,
                              const struct vport_parms *);
 void ovs_vport_free(struct vport *);
+void ovs_vport_deferred_free(struct vport *vport);
 
 #define VPORT_ALIGN 8
 
@@ -186,12 +190,21 @@ static inline struct vport *vport_from_priv(const void *priv)
        return (struct vport *)(priv - ALIGN(sizeof(struct vport), VPORT_ALIGN));
 }
 
-void ovs_vport_receive(struct vport *, struct sk_buff *);
+void ovs_vport_receive(struct vport *, struct sk_buff *,
+                      struct ovs_key_ipv4_tunnel *);
 void ovs_vport_record_error(struct vport *, enum vport_err_type err_type);
 
 /* List of statically compiled vport implementations.  Don't forget to also
  * add yours to the list at the top of vport.c. */
 extern const struct vport_ops ovs_netdev_vport_ops;
 extern const struct vport_ops ovs_internal_vport_ops;
+extern const struct vport_ops ovs_gre_vport_ops;
+
+static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb,
+                                     const void *start, unsigned int len)
+{
+       if (skb->ip_summed == CHECKSUM_COMPLETE)
+               skb->csum = csum_add(skb->csum, csum_partial(start, len, 0));
+}
 
 #endif /* vport.h */
index 20a1bd0..4b66c75 100644 (file)
@@ -3330,10 +3330,11 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
 }
 
 
-static int packet_notifier(struct notifier_block *this, unsigned long msg, void *data)
+static int packet_notifier(struct notifier_block *this,
+                          unsigned long msg, void *ptr)
 {
        struct sock *sk;
-       struct net_device *dev = data;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct net *net = dev_net(dev);
 
        rcu_read_lock();
index 45a7df6..56a6146 100644 (file)
@@ -292,9 +292,9 @@ static void phonet_route_autodel(struct net_device *dev)
 
 /* notify Phonet of device events */
 static int phonet_device_notify(struct notifier_block *me, unsigned long what,
-                               void *arg)
+                               void *ptr)
 {
-       struct net_device *dev = arg;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        switch (what) {
        case NETDEV_REGISTER:
index d6bbbbd..c02a8c4 100644 (file)
@@ -61,13 +61,13 @@ void phonet_get_local_port_range(int *min, int *max)
        } while (read_seqretry(&local_port_range_lock, seq));
 }
 
-static int proc_local_port_range(ctl_table *table, int write,
+static int proc_local_port_range(struct ctl_table *table, int write,
                                void __user *buffer,
                                size_t *lenp, loff_t *ppos)
 {
        int ret;
        int range[2] = {local_port_range[0], local_port_range[1]};
-       ctl_table tmp = {
+       struct ctl_table tmp = {
                .data = &range,
                .maxlen = sizeof(range),
                .mode = table->mode,
index 7e643ba..e4e41b3 100644 (file)
@@ -61,7 +61,7 @@ static unsigned long rds_ib_sysctl_max_unsig_wr_max = 64;
  */
 unsigned int rds_ib_sysctl_flow_control = 0;
 
-static ctl_table rds_ib_sysctl_table[] = {
+static struct ctl_table rds_ib_sysctl_table[] = {
        {
                .procname       = "max_send_wr",
                .data           = &rds_ib_sysctl_max_send_wr,
index 5d5ebd5..89c9151 100644 (file)
@@ -55,7 +55,7 @@ static unsigned long rds_iw_sysctl_max_unsig_bytes_max = ~0UL;
 
 unsigned int rds_iw_sysctl_flow_control = 1;
 
-static ctl_table rds_iw_sysctl_table[] = {
+static struct ctl_table rds_iw_sysctl_table[] = {
        {
                .procname       = "max_send_wr",
                .data           = &rds_iw_sysctl_max_send_wr,
index 907214b..b5cb2aa 100644 (file)
@@ -49,7 +49,7 @@ unsigned int  rds_sysctl_max_unacked_bytes = (16 << 20);
 
 unsigned int rds_sysctl_ping_enable = 1;
 
-static ctl_table rds_sysctl_rds_table[] = {
+static struct ctl_table rds_sysctl_rds_table[] = {
        {
                .procname       = "reconnect_min_delay_ms",
                .data           = &rds_sysctl_reconnect_min_jiffies,
index 9c83474..e98fcfb 100644 (file)
@@ -202,10 +202,10 @@ static void rose_kill_by_device(struct net_device *dev)
 /*
  *     Handle device status changes.
  */
-static int rose_device_event(struct notifier_block *this, unsigned long event,
-       void *ptr)
+static int rose_device_event(struct notifier_block *this,
+                            unsigned long event, void *ptr)
 {
-       struct net_device *dev = (struct net_device *)ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
index 94ca9c2..89a9278 100644 (file)
@@ -24,7 +24,7 @@ static int min_window[] = {1}, max_window[] = {7};
 
 static struct ctl_table_header *rose_table_header;
 
-static ctl_table rose_table[] = {
+static struct ctl_table rose_table[] = {
        {
                .procname       = "restart_request_timeout",
                .data           = &sysctl_rose_restart_request_timeout,
index 5d676ed..977c10e 100644 (file)
@@ -243,7 +243,7 @@ nla_put_failure:
 static int mirred_device_event(struct notifier_block *unused,
                               unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct tcf_mirred *m;
 
        if (event == NETDEV_UNREGISTER)
index 1bc210f..71a5688 100644 (file)
@@ -130,7 +130,7 @@ struct cbq_class {
        psched_time_t           penalized;
        struct gnet_stats_basic_packed bstats;
        struct gnet_stats_queue qstats;
-       struct gnet_stats_rate_est rate_est;
+       struct gnet_stats_rate_est64 rate_est;
        struct tc_cbq_xstats    xstats;
 
        struct tcf_proto        *filter_list;
index 759b308..8302717 100644 (file)
@@ -25,7 +25,7 @@ struct drr_class {
 
        struct gnet_stats_basic_packed          bstats;
        struct gnet_stats_queue         qstats;
-       struct gnet_stats_rate_est      rate_est;
+       struct gnet_stats_rate_est64    rate_est;
        struct list_head                alist;
        struct Qdisc                    *qdisc;
 
index 2022408..4626cef 100644 (file)
@@ -901,37 +901,33 @@ void dev_shutdown(struct net_device *dev)
 void psched_ratecfg_precompute(struct psched_ratecfg *r,
                               const struct tc_ratespec *conf)
 {
-       u64 factor;
-       u64 mult;
-       int shift;
-
        memset(r, 0, sizeof(*r));
        r->overhead = conf->overhead;
-       r->rate_bps = (u64)conf->rate << 3;
+       r->rate_bytes_ps = conf->rate;
        r->mult = 1;
        /*
-        * Calibrate mult, shift so that token counting is accurate
-        * for smallest packet size (64 bytes).  Token (time in ns) is
-        * computed as (bytes * 8) * NSEC_PER_SEC / rate_bps.  It will
-        * work as long as the smallest packet transfer time can be
-        * accurately represented in nanosec.
+        * The deal here is to replace a divide by a reciprocal one
+        * in fast path (a reciprocal divide is a multiply and a shift)
+        *
+        * Normal formula would be :
+        *  time_in_ns = (NSEC_PER_SEC * len) / rate_bps
+        *
+        * We compute mult/shift to use instead :
+        *  time_in_ns = (len * mult) >> shift;
+        *
+        * We try to get the highest possible mult value for accuracy,
+        * but have to make sure no overflows will ever happen.
         */
-       if (r->rate_bps > 0) {
-               /*
-                * Higher shift gives better accuracy.  Find the largest
-                * shift such that mult fits in 32 bits.
-                */
-               for (shift = 0; shift < 16; shift++) {
-                       r->shift = shift;
-                       factor = 8LLU * NSEC_PER_SEC * (1 << r->shift);
-                       mult = div64_u64(factor, r->rate_bps);
-                       if (mult > UINT_MAX)
+       if (r->rate_bytes_ps > 0) {
+               u64 factor = NSEC_PER_SEC;
+
+               for (;;) {
+                       r->mult = div64_u64(factor, r->rate_bytes_ps);
+                       if (r->mult & (1U << 31) || factor & (1ULL << 63))
                                break;
+                       factor <<= 1;
+                       r->shift++;
                }
-
-               r->shift = shift - 1;
-               factor = 8LLU * NSEC_PER_SEC * (1 << r->shift);
-               r->mult = div64_u64(factor, r->rate_bps);
        }
 }
 EXPORT_SYMBOL(psched_ratecfg_precompute);
index 9facea0..c407561 100644 (file)
@@ -114,7 +114,7 @@ struct hfsc_class {
 
        struct gnet_stats_basic_packed bstats;
        struct gnet_stats_queue qstats;
-       struct gnet_stats_rate_est rate_est;
+       struct gnet_stats_rate_est64 rate_est;
        unsigned int    level;          /* class level in hierarchy */
        struct tcf_proto *filter_list;  /* filter list */
        unsigned int    filter_cnt;     /* filter count */
index adaedd7..c2124ea 100644 (file)
@@ -65,6 +65,10 @@ static int htb_hysteresis __read_mostly = 0; /* whether to use mode hysteresis f
 module_param    (htb_hysteresis, int, 0640);
 MODULE_PARM_DESC(htb_hysteresis, "Hysteresis mode, less CPU load, less accurate");
 
+static int htb_rate_est = 0; /* htb classes have a default rate estimator */
+module_param(htb_rate_est, int, 0640);
+MODULE_PARM_DESC(htb_rate_est, "setup a default rate estimator (4sec 16sec) for htb classes");
+
 /* used internaly to keep status of single class */
 enum htb_cmode {
        HTB_CANT_SEND,          /* class can't send and can't borrow */
@@ -72,95 +76,105 @@ enum htb_cmode {
        HTB_CAN_SEND            /* class can send */
 };
 
-/* interior & leaf nodes; props specific to leaves are marked L: */
+struct htb_prio {
+       union {
+               struct rb_root  row;
+               struct rb_root  feed;
+       };
+       struct rb_node  *ptr;
+       /* When class changes from state 1->2 and disconnects from
+        * parent's feed then we lost ptr value and start from the
+        * first child again. Here we store classid of the
+        * last valid ptr (used when ptr is NULL).
+        */
+       u32             last_ptr_id;
+};
+
+/* interior & leaf nodes; props specific to leaves are marked L:
+ * To reduce false sharing, place mostly read fields at beginning,
+ * and mostly written ones at the end.
+ */
 struct htb_class {
        struct Qdisc_class_common common;
-       /* general class parameters */
-       struct gnet_stats_basic_packed bstats;
-       struct gnet_stats_queue qstats;
-       struct gnet_stats_rate_est rate_est;
-       struct tc_htb_xstats xstats;    /* our special stats */
-       int refcnt;             /* usage count of this class */
+       struct psched_ratecfg   rate;
+       struct psched_ratecfg   ceil;
+       s64                     buffer, cbuffer;/* token bucket depth/rate */
+       s64                     mbuffer;        /* max wait time */
+       int                     prio;           /* these two are used only by leaves... */
+       int                     quantum;        /* but stored for parent-to-leaf return */
 
-       /* topology */
-       int level;              /* our level (see above) */
-       unsigned int children;
-       struct htb_class *parent;       /* parent class */
+       struct tcf_proto        *filter_list;   /* class attached filters */
+       int                     filter_cnt;
+       int                     refcnt;         /* usage count of this class */
 
-       int prio;               /* these two are used only by leaves... */
-       int quantum;            /* but stored for parent-to-leaf return */
+       int                     level;          /* our level (see above) */
+       unsigned int            children;
+       struct htb_class        *parent;        /* parent class */
+
+       struct gnet_stats_rate_est64 rate_est;
+
+       /*
+        * Written often fields
+        */
+       struct gnet_stats_basic_packed bstats;
+       struct gnet_stats_queue qstats;
+       struct tc_htb_xstats    xstats; /* our special stats */
+
+       /* token bucket parameters */
+       s64                     tokens, ctokens;/* current number of tokens */
+       s64                     t_c;            /* checkpoint time */
 
        union {
                struct htb_class_leaf {
-                       struct Qdisc *q;
-                       int deficit[TC_HTB_MAXDEPTH];
                        struct list_head drop_list;
+                       int             deficit[TC_HTB_MAXDEPTH];
+                       struct Qdisc    *q;
                } leaf;
                struct htb_class_inner {
-                       struct rb_root feed[TC_HTB_NUMPRIO];    /* feed trees */
-                       struct rb_node *ptr[TC_HTB_NUMPRIO];    /* current class ptr */
-                       /* When class changes from state 1->2 and disconnects from
-                        * parent's feed then we lost ptr value and start from the
-                        * first child again. Here we store classid of the
-                        * last valid ptr (used when ptr is NULL).
-                        */
-                       u32 last_ptr_id[TC_HTB_NUMPRIO];
+                       struct htb_prio clprio[TC_HTB_NUMPRIO];
                } inner;
        } un;
-       struct rb_node node[TC_HTB_NUMPRIO];    /* node for self or feed tree */
-       struct rb_node pq_node; /* node for event queue */
-       s64     pq_key;
+       s64                     pq_key;
 
-       int prio_activity;      /* for which prios are we active */
-       enum htb_cmode cmode;   /* current mode of the class */
-
-       /* class attached filters */
-       struct tcf_proto *filter_list;
-       int filter_cnt;
+       int                     prio_activity;  /* for which prios are we active */
+       enum htb_cmode          cmode;          /* current mode of the class */
+       struct rb_node          pq_node;        /* node for event queue */
+       struct rb_node          node[TC_HTB_NUMPRIO];   /* node for self or feed tree */
+};
 
-       /* token bucket parameters */
-       struct psched_ratecfg rate;
-       struct psched_ratecfg ceil;
-       s64     buffer, cbuffer;        /* token bucket depth/rate */
-       s64     mbuffer;                /* max wait time */
-       s64     tokens, ctokens;        /* current number of tokens */
-       s64     t_c;                    /* checkpoint time */
+struct htb_level {
+       struct rb_root  wait_pq;
+       struct htb_prio hprio[TC_HTB_NUMPRIO];
 };
 
 struct htb_sched {
        struct Qdisc_class_hash clhash;
-       struct list_head drops[TC_HTB_NUMPRIO];/* active leaves (for drops) */
-
-       /* self list - roots of self generating tree */
-       struct rb_root row[TC_HTB_MAXDEPTH][TC_HTB_NUMPRIO];
-       int row_mask[TC_HTB_MAXDEPTH];
-       struct rb_node *ptr[TC_HTB_MAXDEPTH][TC_HTB_NUMPRIO];
-       u32 last_ptr_id[TC_HTB_MAXDEPTH][TC_HTB_NUMPRIO];
+       int                     defcls;         /* class where unclassified flows go to */
+       int                     rate2quantum;   /* quant = rate / rate2quantum */
 
-       /* self wait list - roots of wait PQs per row */
-       struct rb_root wait_pq[TC_HTB_MAXDEPTH];
+       /* filters for qdisc itself */
+       struct tcf_proto        *filter_list;
 
-       /* time of nearest event per level (row) */
-       s64     near_ev_cache[TC_HTB_MAXDEPTH];
+#define HTB_WARN_TOOMANYEVENTS 0x1
+       unsigned int            warned; /* only one warning */
+       int                     direct_qlen;
+       struct work_struct      work;
 
-       int defcls;             /* class where unclassified flows go to */
+       /* non shaped skbs; let them go directly thru */
+       struct sk_buff_head     direct_queue;
+       long                    direct_pkts;
 
-       /* filters for qdisc itself */
-       struct tcf_proto *filter_list;
+       struct qdisc_watchdog   watchdog;
 
-       int     rate2quantum;   /* quant = rate / rate2quantum */
-       s64     now;    /* cached dequeue time */
-       struct qdisc_watchdog watchdog;
+       s64                     now;    /* cached dequeue time */
+       struct list_head        drops[TC_HTB_NUMPRIO];/* active leaves (for drops) */
 
-       /* non shaped skbs; let them go directly thru */
-       struct sk_buff_head direct_queue;
-       int direct_qlen;        /* max qlen of above */
+       /* time of nearest event per level (row) */
+       s64                     near_ev_cache[TC_HTB_MAXDEPTH];
 
-       long direct_pkts;
+       int                     row_mask[TC_HTB_MAXDEPTH];
 
-#define HTB_WARN_TOOMANYEVENTS 0x1
-       unsigned int warned;    /* only one warning */
-       struct work_struct work;
+       struct htb_level        hlevel[TC_HTB_MAXDEPTH];
 };
 
 /* find class in global hash table using given handle */
@@ -276,7 +290,7 @@ static void htb_add_to_id_tree(struct rb_root *root,
 static void htb_add_to_wait_tree(struct htb_sched *q,
                                 struct htb_class *cl, s64 delay)
 {
-       struct rb_node **p = &q->wait_pq[cl->level].rb_node, *parent = NULL;
+       struct rb_node **p = &q->hlevel[cl->level].wait_pq.rb_node, *parent = NULL;
 
        cl->pq_key = q->now + delay;
        if (cl->pq_key == q->now)
@@ -296,7 +310,7 @@ static void htb_add_to_wait_tree(struct htb_sched *q,
                        p = &parent->rb_left;
        }
        rb_link_node(&cl->pq_node, parent, p);
-       rb_insert_color(&cl->pq_node, &q->wait_pq[cl->level]);
+       rb_insert_color(&cl->pq_node, &q->hlevel[cl->level].wait_pq);
 }
 
 /**
@@ -323,7 +337,7 @@ static inline void htb_add_class_to_row(struct htb_sched *q,
        while (mask) {
                int prio = ffz(~mask);
                mask &= ~(1 << prio);
-               htb_add_to_id_tree(q->row[cl->level] + prio, cl, prio);
+               htb_add_to_id_tree(&q->hlevel[cl->level].hprio[prio].row, cl, prio);
        }
 }
 
@@ -349,16 +363,18 @@ static inline void htb_remove_class_from_row(struct htb_sched *q,
                                                 struct htb_class *cl, int mask)
 {
        int m = 0;
+       struct htb_level *hlevel = &q->hlevel[cl->level];
 
        while (mask) {
                int prio = ffz(~mask);
+               struct htb_prio *hprio = &hlevel->hprio[prio];
 
                mask &= ~(1 << prio);
-               if (q->ptr[cl->level][prio] == cl->node + prio)
-                       htb_next_rb_node(q->ptr[cl->level] + prio);
+               if (hprio->ptr == cl->node + prio)
+                       htb_next_rb_node(&hprio->ptr);
 
-               htb_safe_rb_erase(cl->node + prio, q->row[cl->level] + prio);
-               if (!q->row[cl->level][prio].rb_node)
+               htb_safe_rb_erase(cl->node + prio, &hprio->row);
+               if (!hprio->row.rb_node)
                        m |= 1 << prio;
        }
        q->row_mask[cl->level] &= ~m;
@@ -382,13 +398,13 @@ static void htb_activate_prios(struct htb_sched *q, struct htb_class *cl)
                        int prio = ffz(~m);
                        m &= ~(1 << prio);
 
-                       if (p->un.inner.feed[prio].rb_node)
+                       if (p->un.inner.clprio[prio].feed.rb_node)
                                /* parent already has its feed in use so that
                                 * reset bit in mask as parent is already ok
                                 */
                                mask &= ~(1 << prio);
 
-                       htb_add_to_id_tree(p->un.inner.feed + prio, cl, prio);
+                       htb_add_to_id_tree(&p->un.inner.clprio[prio].feed, cl, prio);
                }
                p->prio_activity |= mask;
                cl = p;
@@ -418,18 +434,19 @@ static void htb_deactivate_prios(struct htb_sched *q, struct htb_class *cl)
                        int prio = ffz(~m);
                        m &= ~(1 << prio);
 
-                       if (p->un.inner.ptr[prio] == cl->node + prio) {
+                       if (p->un.inner.clprio[prio].ptr == cl->node + prio) {
                                /* we are removing child which is pointed to from
                                 * parent feed - forget the pointer but remember
                                 * classid
                                 */
-                               p->un.inner.last_ptr_id[prio] = cl->common.classid;
-                               p->un.inner.ptr[prio] = NULL;
+                               p->un.inner.clprio[prio].last_ptr_id = cl->common.classid;
+                               p->un.inner.clprio[prio].ptr = NULL;
                        }
 
-                       htb_safe_rb_erase(cl->node + prio, p->un.inner.feed + prio);
+                       htb_safe_rb_erase(cl->node + prio,
+                                         &p->un.inner.clprio[prio].feed);
 
-                       if (!p->un.inner.feed[prio].rb_node)
+                       if (!p->un.inner.clprio[prio].feed.rb_node)
                                mask |= 1 << prio;
                }
 
@@ -644,7 +661,7 @@ static void htb_charge_class(struct htb_sched *q, struct htb_class *cl,
                htb_change_class_mode(q, cl, &diff);
                if (old_mode != cl->cmode) {
                        if (old_mode != HTB_CAN_SEND)
-                               htb_safe_rb_erase(&cl->pq_node, q->wait_pq + cl->level);
+                               htb_safe_rb_erase(&cl->pq_node, &q->hlevel[cl->level].wait_pq);
                        if (cl->cmode != HTB_CAN_SEND)
                                htb_add_to_wait_tree(q, cl, diff);
                }
@@ -664,7 +681,7 @@ static void htb_charge_class(struct htb_sched *q, struct htb_class *cl,
  * next pending event (0 for no event in pq, q->now for too many events).
  * Note: Applied are events whose have cl->pq_key <= q->now.
  */
-static s64 htb_do_events(struct htb_sched *q, int level,
+static s64 htb_do_events(struct htb_sched *q, const int level,
                         unsigned long start)
 {
        /* don't run for longer than 2 jiffies; 2 is used instead of
@@ -672,10 +689,12 @@ static s64 htb_do_events(struct htb_sched *q, int level,
         * too soon
         */
        unsigned long stop_at = start + 2;
+       struct rb_root *wait_pq = &q->hlevel[level].wait_pq;
+
        while (time_before(jiffies, stop_at)) {
                struct htb_class *cl;
                s64 diff;
-               struct rb_node *p = rb_first(&q->wait_pq[level]);
+               struct rb_node *p = rb_first(wait_pq);
 
                if (!p)
                        return 0;
@@ -684,7 +703,7 @@ static s64 htb_do_events(struct htb_sched *q, int level,
                if (cl->pq_key > q->now)
                        return cl->pq_key;
 
-               htb_safe_rb_erase(p, q->wait_pq + level);
+               htb_safe_rb_erase(p, wait_pq);
                diff = min_t(s64, q->now - cl->t_c, cl->mbuffer);
                htb_change_class_mode(q, cl, &diff);
                if (cl->cmode != HTB_CAN_SEND)
@@ -728,8 +747,7 @@ static struct rb_node *htb_id_find_next_upper(int prio, struct rb_node *n,
  *
  * Find leaf where current feed pointers points to.
  */
-static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
-                                        struct rb_node **pptr, u32 * pid)
+static struct htb_class *htb_lookup_leaf(struct htb_prio *hprio, const int prio)
 {
        int i;
        struct {
@@ -738,10 +756,10 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
                u32 *pid;
        } stk[TC_HTB_MAXDEPTH], *sp = stk;
 
-       BUG_ON(!tree->rb_node);
-       sp->root = tree->rb_node;
-       sp->pptr = pptr;
-       sp->pid = pid;
+       BUG_ON(!hprio->row.rb_node);
+       sp->root = hprio->row.rb_node;
+       sp->pptr = &hprio->ptr;
+       sp->pid = &hprio->last_ptr_id;
 
        for (i = 0; i < 65535; i++) {
                if (!*sp->pptr && *sp->pid) {
@@ -768,12 +786,15 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
                        }
                } else {
                        struct htb_class *cl;
+                       struct htb_prio *clp;
+
                        cl = rb_entry(*sp->pptr, struct htb_class, node[prio]);
                        if (!cl->level)
                                return cl;
-                       (++sp)->root = cl->un.inner.feed[prio].rb_node;
-                       sp->pptr = cl->un.inner.ptr + prio;
-                       sp->pid = cl->un.inner.last_ptr_id + prio;
+                       clp = &cl->un.inner.clprio[prio];
+                       (++sp)->root = clp->feed.rb_node;
+                       sp->pptr = &clp->ptr;
+                       sp->pid = &clp->last_ptr_id;
                }
        }
        WARN_ON(1);
@@ -783,15 +804,16 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
 /* dequeues packet at given priority and level; call only if
  * you are sure that there is active class at prio/level
  */
-static struct sk_buff *htb_dequeue_tree(struct htb_sched *q, int prio,
-                                       int level)
+static struct sk_buff *htb_dequeue_tree(struct htb_sched *q, const int prio,
+                                       const int level)
 {
        struct sk_buff *skb = NULL;
        struct htb_class *cl, *start;
+       struct htb_level *hlevel = &q->hlevel[level];
+       struct htb_prio *hprio = &hlevel->hprio[prio];
+
        /* look initial class up in the row */
-       start = cl = htb_lookup_leaf(q->row[level] + prio, prio,
-                                    q->ptr[level] + prio,
-                                    q->last_ptr_id[level] + prio);
+       start = cl = htb_lookup_leaf(hprio, prio);
 
        do {
 next:
@@ -811,9 +833,7 @@ next:
                        if ((q->row_mask[level] & (1 << prio)) == 0)
                                return NULL;
 
-                       next = htb_lookup_leaf(q->row[level] + prio,
-                                              prio, q->ptr[level] + prio,
-                                              q->last_ptr_id[level] + prio);
+                       next = htb_lookup_leaf(hprio, prio);
 
                        if (cl == start)        /* fix start if we just deleted it */
                                start = next;
@@ -826,11 +846,9 @@ next:
                        break;
 
                qdisc_warn_nonwc("htb", cl->un.leaf.q);
-               htb_next_rb_node((level ? cl->parent->un.inner.ptr : q->
-                                 ptr[0]) + prio);
-               cl = htb_lookup_leaf(q->row[level] + prio, prio,
-                                    q->ptr[level] + prio,
-                                    q->last_ptr_id[level] + prio);
+               htb_next_rb_node(level ? &cl->parent->un.inner.clprio[prio].ptr:
+                                        &q->hlevel[0].hprio[prio].ptr);
+               cl = htb_lookup_leaf(hprio, prio);
 
        } while (cl != start);
 
@@ -839,8 +857,8 @@ next:
                cl->un.leaf.deficit[level] -= qdisc_pkt_len(skb);
                if (cl->un.leaf.deficit[level] < 0) {
                        cl->un.leaf.deficit[level] += cl->quantum;
-                       htb_next_rb_node((level ? cl->parent->un.inner.ptr : q->
-                                         ptr[0]) + prio);
+                       htb_next_rb_node(level ? &cl->parent->un.inner.clprio[prio].ptr :
+                                                &q->hlevel[0].hprio[prio].ptr);
                }
                /* this used to be after charge_class but this constelation
                 * gives us slightly better performance
@@ -880,15 +898,14 @@ ok:
        for (level = 0; level < TC_HTB_MAXDEPTH; level++) {
                /* common case optimization - skip event handler quickly */
                int m;
-               s64 event;
+               s64 event = q->near_ev_cache[level];
 
-               if (q->now >= q->near_ev_cache[level]) {
+               if (q->now >= event) {
                        event = htb_do_events(q, level, start_at);
                        if (!event)
                                event = q->now + NSEC_PER_SEC;
                        q->near_ev_cache[level] = event;
-               } else
-                       event = q->near_ev_cache[level];
+               }
 
                if (next_event > event)
                        next_event = event;
@@ -968,10 +985,8 @@ static void htb_reset(struct Qdisc *sch)
        qdisc_watchdog_cancel(&q->watchdog);
        __skb_queue_purge(&q->direct_queue);
        sch->q.qlen = 0;
-       memset(q->row, 0, sizeof(q->row));
+       memset(q->hlevel, 0, sizeof(q->hlevel));
        memset(q->row_mask, 0, sizeof(q->row_mask));
-       memset(q->wait_pq, 0, sizeof(q->wait_pq));
-       memset(q->ptr, 0, sizeof(q->ptr));
        for (i = 0; i < TC_HTB_NUMPRIO; i++)
                INIT_LIST_HEAD(q->drops + i);
 }
@@ -1192,7 +1207,8 @@ static void htb_parent_to_leaf(struct htb_sched *q, struct htb_class *cl,
        WARN_ON(cl->level || !cl->un.leaf.q || cl->prio_activity);
 
        if (parent->cmode != HTB_CAN_SEND)
-               htb_safe_rb_erase(&parent->pq_node, q->wait_pq + parent->level);
+               htb_safe_rb_erase(&parent->pq_node,
+                                 &q->hlevel[parent->level].wait_pq);
 
        parent->level = 0;
        memset(&parent->un.inner, 0, sizeof(parent->un.inner));
@@ -1281,7 +1297,8 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg)
                htb_deactivate(q, cl);
 
        if (cl->cmode != HTB_CAN_SEND)
-               htb_safe_rb_erase(&cl->pq_node, q->wait_pq + cl->level);
+               htb_safe_rb_erase(&cl->pq_node,
+                                 &q->hlevel[cl->level].wait_pq);
 
        if (last_child)
                htb_parent_to_leaf(q, cl, new_q);
@@ -1366,12 +1383,14 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
                if (!cl)
                        goto failure;
 
-               err = gen_new_estimator(&cl->bstats, &cl->rate_est,
-                                       qdisc_root_sleeping_lock(sch),
-                                       tca[TCA_RATE] ? : &est.nla);
-               if (err) {
-                       kfree(cl);
-                       goto failure;
+               if (htb_rate_est || tca[TCA_RATE]) {
+                       err = gen_new_estimator(&cl->bstats, &cl->rate_est,
+                                               qdisc_root_sleeping_lock(sch),
+                                               tca[TCA_RATE] ? : &est.nla);
+                       if (err) {
+                               kfree(cl);
+                               goto failure;
+                       }
                }
 
                cl->refcnt = 1;
@@ -1401,7 +1420,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
 
                        /* remove from evt list because of level change */
                        if (parent->cmode != HTB_CAN_SEND) {
-                               htb_safe_rb_erase(&parent->pq_node, q->wait_pq);
+                               htb_safe_rb_erase(&parent->pq_node, &q->hlevel[0].wait_pq);
                                parent->cmode = HTB_CAN_SEND;
                        }
                        parent->level = (parent->parent ? parent->parent->level
index 3d2acc7..82f6016 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/vmalloc.h>
 #include <linux/rtnetlink.h>
 #include <linux/reciprocal_div.h>
+#include <linux/rbtree.h>
 
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
@@ -68,7 +69,8 @@
 */
 
 struct netem_sched_data {
-       /* internal t(ime)fifo qdisc uses sch->q and sch->limit */
+       /* internal t(ime)fifo qdisc uses t_root and sch->limit */
+       struct rb_root t_root;
 
        /* optional qdisc for classful handling (NULL at netem init) */
        struct Qdisc    *qdisc;
@@ -128,10 +130,35 @@ struct netem_sched_data {
  */
 struct netem_skb_cb {
        psched_time_t   time_to_send;
+       ktime_t         tstamp_save;
 };
 
+/* Because space in skb->cb[] is tight, netem overloads skb->next/prev/tstamp
+ * to hold a rb_node structure.
+ *
+ * If struct sk_buff layout is changed, the following checks will complain.
+ */
+static struct rb_node *netem_rb_node(struct sk_buff *skb)
+{
+       BUILD_BUG_ON(offsetof(struct sk_buff, next) != 0);
+       BUILD_BUG_ON(offsetof(struct sk_buff, prev) !=
+                    offsetof(struct sk_buff, next) + sizeof(skb->next));
+       BUILD_BUG_ON(offsetof(struct sk_buff, tstamp) !=
+                    offsetof(struct sk_buff, prev) + sizeof(skb->prev));
+       BUILD_BUG_ON(sizeof(struct rb_node) > sizeof(skb->next) +
+                                             sizeof(skb->prev) +
+                                             sizeof(skb->tstamp));
+       return (struct rb_node *)&skb->next;
+}
+
+static struct sk_buff *netem_rb_to_skb(struct rb_node *rb)
+{
+       return (struct sk_buff *)rb;
+}
+
 static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb)
 {
+       /* we assume we can use skb next/prev/tstamp as storage for rb_node */
        qdisc_cb_private_validate(skb, sizeof(struct netem_skb_cb));
        return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data;
 }
@@ -333,20 +360,23 @@ static psched_time_t packet_len_2_sched_time(unsigned int len, struct netem_sche
 
 static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
 {
-       struct sk_buff_head *list = &sch->q;
+       struct netem_sched_data *q = qdisc_priv(sch);
        psched_time_t tnext = netem_skb_cb(nskb)->time_to_send;
-       struct sk_buff *skb = skb_peek_tail(list);
+       struct rb_node **p = &q->t_root.rb_node, *parent = NULL;
 
-       /* Optimize for add at tail */
-       if (likely(!skb || tnext >= netem_skb_cb(skb)->time_to_send))
-               return __skb_queue_tail(list, nskb);
+       while (*p) {
+               struct sk_buff *skb;
 
-       skb_queue_reverse_walk(list, skb) {
+               parent = *p;
+               skb = netem_rb_to_skb(parent);
                if (tnext >= netem_skb_cb(skb)->time_to_send)
-                       break;
+                       p = &parent->rb_right;
+               else
+                       p = &parent->rb_left;
        }
-
-       __skb_queue_after(list, skb, nskb);
+       rb_link_node(netem_rb_node(nskb), parent, p);
+       rb_insert_color(netem_rb_node(nskb), &q->t_root);
+       sch->q.qlen++;
 }
 
 /*
@@ -436,23 +466,28 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
                now = psched_get_time();
 
                if (q->rate) {
-                       struct sk_buff_head *list = &sch->q;
+                       struct sk_buff *last;
 
-                       if (!skb_queue_empty(list)) {
+                       if (!skb_queue_empty(&sch->q))
+                               last = skb_peek_tail(&sch->q);
+                       else
+                               last = netem_rb_to_skb(rb_last(&q->t_root));
+                       if (last) {
                                /*
                                 * Last packet in queue is reference point (now),
                                 * calculate this time bonus and subtract
                                 * from delay.
                                 */
-                               delay -= netem_skb_cb(skb_peek_tail(list))->time_to_send - now;
+                               delay -= netem_skb_cb(last)->time_to_send - now;
                                delay = max_t(psched_tdiff_t, 0, delay);
-                               now = netem_skb_cb(skb_peek_tail(list))->time_to_send;
+                               now = netem_skb_cb(last)->time_to_send;
                        }
 
                        delay += packet_len_2_sched_time(skb->len, q);
                }
 
                cb->time_to_send = now + delay;
+               cb->tstamp_save = skb->tstamp;
                ++q->counter;
                tfifo_enqueue(skb, sch);
        } else {
@@ -476,6 +511,21 @@ static unsigned int netem_drop(struct Qdisc *sch)
        unsigned int len;
 
        len = qdisc_queue_drop(sch);
+
+       if (!len) {
+               struct rb_node *p = rb_first(&q->t_root);
+
+               if (p) {
+                       struct sk_buff *skb = netem_rb_to_skb(p);
+
+                       rb_erase(p, &q->t_root);
+                       sch->q.qlen--;
+                       skb->next = NULL;
+                       skb->prev = NULL;
+                       len = qdisc_pkt_len(skb);
+                       kfree_skb(skb);
+               }
+       }
        if (!len && q->qdisc && q->qdisc->ops->drop)
            len = q->qdisc->ops->drop(q->qdisc);
        if (len)
@@ -488,19 +538,35 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
        struct sk_buff *skb;
+       struct rb_node *p;
 
        if (qdisc_is_throttled(sch))
                return NULL;
 
 tfifo_dequeue:
-       skb = qdisc_peek_head(sch);
+       skb = __skb_dequeue(&sch->q);
        if (skb) {
-               const struct netem_skb_cb *cb = netem_skb_cb(skb);
+deliver:
+               sch->qstats.backlog -= qdisc_pkt_len(skb);
+               qdisc_unthrottled(sch);
+               qdisc_bstats_update(sch, skb);
+               return skb;
+       }
+       p = rb_first(&q->t_root);
+       if (p) {
+               psched_time_t time_to_send;
+
+               skb = netem_rb_to_skb(p);
 
                /* if more time remaining? */
-               if (cb->time_to_send <= psched_get_time()) {
-                       __skb_unlink(skb, &sch->q);
-                       sch->qstats.backlog -= qdisc_pkt_len(skb);
+               time_to_send = netem_skb_cb(skb)->time_to_send;
+               if (time_to_send <= psched_get_time()) {
+                       rb_erase(p, &q->t_root);
+
+                       sch->q.qlen--;
+                       skb->next = NULL;
+                       skb->prev = NULL;
+                       skb->tstamp = netem_skb_cb(skb)->tstamp_save;
 
 #ifdef CONFIG_NET_CLS_ACT
                        /*
@@ -522,10 +588,7 @@ tfifo_dequeue:
                                }
                                goto tfifo_dequeue;
                        }
-deliver:
-                       qdisc_unthrottled(sch);
-                       qdisc_bstats_update(sch, skb);
-                       return skb;
+                       goto deliver;
                }
 
                if (q->qdisc) {
@@ -533,7 +596,7 @@ deliver:
                        if (skb)
                                goto deliver;
                }
-               qdisc_watchdog_schedule(&q->watchdog, cb->time_to_send);
+               qdisc_watchdog_schedule(&q->watchdog, time_to_send);
        }
 
        if (q->qdisc) {
index d51852b..7c195d9 100644 (file)
@@ -138,7 +138,7 @@ struct qfq_class {
 
        struct gnet_stats_basic_packed bstats;
        struct gnet_stats_queue qstats;
-       struct gnet_stats_rate_est rate_est;
+       struct gnet_stats_rate_est64 rate_est;
        struct Qdisc *qdisc;
        struct list_head alist;         /* Link for active-classes list. */
        struct qfq_aggregate *agg;      /* Parent aggregate. */
index e478d31..1aaf1b6 100644 (file)
@@ -116,14 +116,57 @@ struct tbf_sched_data {
        struct qdisc_watchdog watchdog; /* Watchdog timer */
 };
 
+
+/* GSO packet is too big, segment it so that tbf can transmit
+ * each segment in time
+ */
+static int tbf_segment(struct sk_buff *skb, struct Qdisc *sch)
+{
+       struct tbf_sched_data *q = qdisc_priv(sch);
+       struct sk_buff *segs, *nskb;
+       netdev_features_t features = netif_skb_features(skb);
+       int ret, nb;
+
+       segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
+
+       if (IS_ERR_OR_NULL(segs))
+               return qdisc_reshape_fail(skb, sch);
+
+       nb = 0;
+       while (segs) {
+               nskb = segs->next;
+               segs->next = NULL;
+               if (likely(segs->len <= q->max_size)) {
+                       qdisc_skb_cb(segs)->pkt_len = segs->len;
+                       ret = qdisc_enqueue(segs, q->qdisc);
+               } else {
+                       ret = qdisc_reshape_fail(skb, sch);
+               }
+               if (ret != NET_XMIT_SUCCESS) {
+                       if (net_xmit_drop_count(ret))
+                               sch->qstats.drops++;
+               } else {
+                       nb++;
+               }
+               segs = nskb;
+       }
+       sch->q.qlen += nb;
+       if (nb > 1)
+               qdisc_tree_decrease_qlen(sch, 1 - nb);
+       consume_skb(skb);
+       return nb > 0 ? NET_XMIT_SUCCESS : NET_XMIT_DROP;
+}
+
 static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 {
        struct tbf_sched_data *q = qdisc_priv(sch);
        int ret;
 
-       if (qdisc_pkt_len(skb) > q->max_size)
+       if (qdisc_pkt_len(skb) > q->max_size) {
+               if (skb_is_gso(skb))
+                       return tbf_segment(skb, sch);
                return qdisc_reshape_fail(skb, sch);
-
+       }
        ret = qdisc_enqueue(skb, q->qdisc);
        if (ret != NET_XMIT_SUCCESS) {
                if (net_xmit_drop_count(ret))
index cf48528..71c1a59 100644 (file)
@@ -30,7 +30,8 @@ menuconfig IP_SCTP
          homing at either or both ends of an association."
 
          To compile this protocol support as a module, choose M here: the
-         module will be called sctp.
+         module will be called sctp. Debug messages are handeled by the
+         kernel's dynamic debugging framework.
 
          If in doubt, say N.
 
@@ -48,14 +49,6 @@ config NET_SCTPPROBE
         To compile this code as a module, choose M here: the
         module will be called sctp_probe.
 
-config SCTP_DBG_MSG
-       bool "SCTP: Debug messages"
-       help
-         If you say Y, this will enable verbose debugging messages. 
-
-         If unsure, say N.  However, if you are running into problems, use 
-         this option to gather detailed trace information
-
 config SCTP_DBG_OBJCNT
        bool "SCTP: Debug object counts"
        depends on PROC_FS
index 91cfd8f..bce5b79 100644 (file)
@@ -86,10 +86,9 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
 
        /* Discarding const is appropriate here.  */
        asoc->ep = (struct sctp_endpoint *)ep;
-       sctp_endpoint_hold(asoc->ep);
-
-       /* Hold the sock.  */
        asoc->base.sk = (struct sock *)sk;
+
+       sctp_endpoint_hold(asoc->ep);
        sock_hold(asoc->base.sk);
 
        /* Initialize the common base substructure.  */
@@ -103,13 +102,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
        sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port);
 
        asoc->state = SCTP_STATE_CLOSED;
-
-       /* Set these values from the socket values, a conversion between
-        * millsecons to seconds/microseconds must also be done.
-        */
-       asoc->cookie_life.tv_sec = sp->assocparams.sasoc_cookie_life / 1000;
-       asoc->cookie_life.tv_usec = (sp->assocparams.sasoc_cookie_life % 1000)
-                                       * 1000;
+       asoc->cookie_life = ms_to_ktime(sp->assocparams.sasoc_cookie_life);
        asoc->frag_point = 0;
        asoc->user_frag = sp->user_frag;
 
@@ -343,8 +336,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
        return asoc;
 
 fail_init:
-       sctp_endpoint_put(asoc->ep);
        sock_put(asoc->base.sk);
+       sctp_endpoint_put(asoc->ep);
        return NULL;
 }
 
@@ -356,7 +349,7 @@ struct sctp_association *sctp_association_new(const struct sctp_endpoint *ep,
 {
        struct sctp_association *asoc;
 
-       asoc = t_new(struct sctp_association, gfp);
+       asoc = kzalloc(sizeof(*asoc), gfp);
        if (!asoc)
                goto fail;
 
@@ -364,7 +357,8 @@ struct sctp_association *sctp_association_new(const struct sctp_endpoint *ep,
                goto fail_init;
 
        SCTP_DBG_OBJCNT_INC(assoc);
-       SCTP_DEBUG_PRINTK("Created asoc %p\n", asoc);
+
+       pr_debug("Created asoc %p\n", asoc);
 
        return asoc;
 
@@ -462,7 +456,10 @@ void sctp_association_free(struct sctp_association *asoc)
 /* Cleanup and free up an association. */
 static void sctp_association_destroy(struct sctp_association *asoc)
 {
-       SCTP_ASSERT(asoc->base.dead, "Assoc is not dead", return);
+       if (unlikely(!asoc->base.dead)) {
+               WARN(1, "Attempt to destroy undead association %p!\n", asoc);
+               return;
+       }
 
        sctp_endpoint_put(asoc->ep);
        sock_put(asoc->base.sk);
@@ -543,11 +540,8 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
        struct list_head        *pos;
        struct sctp_transport   *transport;
 
-       SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_rm_peer:association %p addr: ",
-                                " port: %d\n",
-                                asoc,
-                                (&peer->ipaddr),
-                                ntohs(peer->ipaddr.v4.sin_port));
+       pr_debug("%s: association:%p addr:%pISpc\n",
+                __func__, asoc, &peer->ipaddr.sa);
 
        /* If we are to remove the current retran_path, update it
         * to the next peer before removing this peer from the list.
@@ -643,12 +637,8 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
        /* AF_INET and AF_INET6 share common port field. */
        port = ntohs(addr->v4.sin_port);
 
-       SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_add_peer:association %p addr: ",
-                                " port: %d state:%d\n",
-                                asoc,
-                                addr,
-                                port,
-                                peer_state);
+       pr_debug("%s: association:%p addr:%pISpc state:%d\n", __func__,
+                asoc, &addr->sa, peer_state);
 
        /* Set the port if it has not been set yet.  */
        if (0 == asoc->peer.port)
@@ -715,8 +705,9 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
        else
                asoc->pathmtu = peer->pathmtu;
 
-       SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to "
-                         "%d\n", asoc, asoc->pathmtu);
+       pr_debug("%s: association:%p PMTU set to %d\n", __func__, asoc,
+                asoc->pathmtu);
+
        peer->pmtu_pending = 0;
 
        asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);
@@ -1356,12 +1347,8 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
        else
                t = asoc->peer.retran_path;
 
-       SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association"
-                                " %p addr: ",
-                                " port: %d\n",
-                                asoc,
-                                (&t->ipaddr),
-                                ntohs(t->ipaddr.v4.sin_port));
+       pr_debug("%s: association:%p addr:%pISpc\n", __func__, asoc,
+                &t->ipaddr.sa);
 }
 
 /* Choose the transport for sending retransmit packet.  */
@@ -1408,8 +1395,8 @@ void sctp_assoc_sync_pmtu(struct sock *sk, struct sctp_association *asoc)
                asoc->frag_point = sctp_frag_point(asoc, pmtu);
        }
 
-       SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n",
-                         __func__, asoc, asoc->pathmtu, asoc->frag_point);
+       pr_debug("%s: asoc:%p, pmtu:%d, frag_point:%d\n", __func__, asoc,
+                asoc->pathmtu, asoc->frag_point);
 }
 
 /* Should we send a SACK to update our peer? */
@@ -1461,9 +1448,9 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned int len)
                asoc->rwnd_press -= change;
        }
 
-       SCTP_DEBUG_PRINTK("%s: asoc %p rwnd increased by %d to (%u, %u) "
-                         "- %u\n", __func__, asoc, len, asoc->rwnd,
-                         asoc->rwnd_over, asoc->a_rwnd);
+       pr_debug("%s: asoc:%p rwnd increased by %d to (%u, %u) - %u\n",
+                __func__, asoc, len, asoc->rwnd, asoc->rwnd_over,
+                asoc->a_rwnd);
 
        /* Send a window update SACK if the rwnd has increased by at least the
         * minimum of the association's PMTU and half of the receive buffer.
@@ -1472,9 +1459,11 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned int len)
         */
        if (sctp_peer_needs_update(asoc)) {
                asoc->a_rwnd = asoc->rwnd;
-               SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p "
-                                 "rwnd: %u a_rwnd: %u\n", __func__,
-                                 asoc, asoc->rwnd, asoc->a_rwnd);
+
+               pr_debug("%s: sending window update SACK- asoc:%p rwnd:%u "
+                        "a_rwnd:%u\n", __func__, asoc, asoc->rwnd,
+                        asoc->a_rwnd);
+
                sack = sctp_make_sack(asoc);
                if (!sack)
                        return;
@@ -1496,8 +1485,10 @@ void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned int len)
        int rx_count;
        int over = 0;
 
-       SCTP_ASSERT(asoc->rwnd, "rwnd zero", return);
-       SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return);
+       if (unlikely(!asoc->rwnd || asoc->rwnd_over))
+               pr_debug("%s: association:%p has asoc->rwnd:%u, "
+                        "asoc->rwnd_over:%u!\n", __func__, asoc,
+                        asoc->rwnd, asoc->rwnd_over);
 
        if (asoc->ep->rcvbuf_policy)
                rx_count = atomic_read(&asoc->rmem_alloc);
@@ -1522,9 +1513,10 @@ void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned int len)
                asoc->rwnd_over = len - asoc->rwnd;
                asoc->rwnd = 0;
        }
-       SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u, %u)\n",
-                         __func__, asoc, len, asoc->rwnd,
-                         asoc->rwnd_over, asoc->rwnd_press);
+
+       pr_debug("%s: asoc:%p rwnd decreased by %d to (%u, %u, %u)\n",
+                __func__, asoc, len, asoc->rwnd, asoc->rwnd_over,
+                asoc->rwnd_press);
 }
 
 /* Build the bind address list for the association based on info from the
index 41145fe..64977ea 100644 (file)
@@ -162,7 +162,7 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new,
        struct sctp_sockaddr_entry *addr;
 
        /* Add the address to the bind address list.  */
-       addr = t_new(struct sctp_sockaddr_entry, gfp);
+       addr = kzalloc(sizeof(*addr), gfp);
        if (!addr)
                return -ENOMEM;
 
index 69ce21e..5780565 100644 (file)
@@ -66,7 +66,7 @@ static void sctp_datamsg_init(struct sctp_datamsg *msg)
 }
 
 /* Allocate and initialize datamsg. */
-SCTP_STATIC struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp)
+static struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp)
 {
        struct sctp_datamsg *msg;
        msg = kmalloc(sizeof(struct sctp_datamsg), gfp);
@@ -193,8 +193,9 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
                msg->expires_at = jiffies +
                                    msecs_to_jiffies(sinfo->sinfo_timetolive);
                msg->can_abandon = 1;
-               SCTP_DEBUG_PRINTK("%s: msg:%p expires_at: %ld jiffies:%ld\n",
-                                 __func__, msg, msg->expires_at, jiffies);
+
+               pr_debug("%s: msg:%p expires_at:%ld jiffies:%ld\n", __func__,
+                        msg, msg->expires_at, jiffies);
        }
 
        /* This is the biggest possible DATA chunk that can fit into
index ec997cf..f499878 100644 (file)
 
 #include <net/sctp/sctp.h>
 
-#if SCTP_DEBUG
-int sctp_debug_flag = 1;       /* Initially enable DEBUG */
-#endif /* SCTP_DEBUG */
-
 /* These are printable forms of Chunk ID's from section 3.1.  */
 static const char *const sctp_cid_tbl[SCTP_NUM_BASE_CHUNK_TYPES] = {
        "DATA",
index 5fbd7bc..9e3d257 100644 (file)
@@ -192,9 +192,10 @@ struct sctp_endpoint *sctp_endpoint_new(struct sock *sk, gfp_t gfp)
        struct sctp_endpoint *ep;
 
        /* Build a local endpoint. */
-       ep = t_new(struct sctp_endpoint, gfp);
+       ep = kzalloc(sizeof(*ep), gfp);
        if (!ep)
                goto fail;
+
        if (!sctp_endpoint_init(ep, sk, gfp))
                goto fail_init;
 
@@ -246,10 +247,12 @@ void sctp_endpoint_free(struct sctp_endpoint *ep)
 /* Final destructor for endpoint.  */
 static void sctp_endpoint_destroy(struct sctp_endpoint *ep)
 {
-       SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return);
+       struct sock *sk;
 
-       /* Free up the HMAC transform. */
-       crypto_free_hash(sctp_sk(ep->base.sk)->hmac);
+       if (unlikely(!ep->base.dead)) {
+               WARN(1, "Attempt to destroy undead endpoint %p!\n", ep);
+               return;
+       }
 
        /* Free the digest buffer */
        kfree(ep->digest);
@@ -270,13 +273,15 @@ static void sctp_endpoint_destroy(struct sctp_endpoint *ep)
 
        memset(ep->secret_key, 0, sizeof(ep->secret_key));
 
-       /* Remove and free the port */
-       if (sctp_sk(ep->base.sk)->bind_hash)
-               sctp_put_port(ep->base.sk);
-
        /* Give up our hold on the sock. */
-       if (ep->base.sk)
-               sock_put(ep->base.sk);
+       sk = ep->base.sk;
+       if (sk != NULL) {
+               /* Remove and free the port */
+               if (sctp_sk(sk)->bind_hash)
+                       sctp_put_port(sk);
+
+               sock_put(sk);
+       }
 
        kfree(ep);
        SCTP_DBG_OBJCNT_DEC(ep);
index 4b2c831..3fa4d85 100644 (file)
@@ -454,8 +454,6 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
                           struct sctp_association *asoc,
                           struct sctp_transport *t)
 {
-       SCTP_DEBUG_PRINTK("%s\n",  __func__);
-
        if (sock_owned_by_user(sk)) {
                if (timer_pending(&t->proto_unreach_timer))
                        return;
@@ -464,10 +462,12 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
                                                jiffies + (HZ/20)))
                                sctp_association_hold(asoc);
                }
-                       
        } else {
                struct net *net = sock_net(sk);
 
+               pr_debug("%s: unrecognized next header type "
+                        "encountered!\n", __func__);
+
                if (del_timer(&t->proto_unreach_timer))
                        sctp_association_put(asoc);
 
@@ -589,7 +589,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
        struct sctp_association *asoc = NULL;
        struct sctp_transport *transport;
        struct inet_sock *inet;
-       sk_buff_data_t saveip, savesctp;
+       __u16 saveip, savesctp;
        int err;
        struct net *net = dev_net(skb->dev);
 
@@ -903,11 +903,11 @@ hit:
 }
 
 /* Look up an association. BH-safe. */
-SCTP_STATIC
+static
 struct sctp_association *sctp_lookup_association(struct net *net,
                                                 const union sctp_addr *laddr,
                                                 const union sctp_addr *paddr,
-                                           struct sctp_transport **transportp)
+                                                struct sctp_transport **transportp)
 {
        struct sctp_association *asoc;
 
index 3221d07..cb25f04 100644 (file)
@@ -219,10 +219,10 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
                chunk->end_of_packet = 1;
        }
 
-       SCTP_DEBUG_PRINTK("+++sctp_inq_pop+++ chunk %p[%s],"
-                         " length %d, skb->len %d\n",chunk,
-                         sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)),
-                         ntohs(chunk->chunk_hdr->length), chunk->skb->len);
+       pr_debug("+++sctp_inq_pop+++ chunk:%p[%s], length:%d, skb->len:%d\n",
+                chunk, sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)),
+                ntohs(chunk->chunk_hdr->length), chunk->skb->len);
+
        return chunk;
 }
 
@@ -238,4 +238,3 @@ void sctp_inq_set_th_handler(struct sctp_inq *q, work_func_t callback)
 {
        INIT_WORK(&q->immediate, callback);
 }
-
index 391a245..09ffcc9 100644 (file)
@@ -145,15 +145,15 @@ static struct notifier_block sctp_inet6addr_notifier = {
 };
 
 /* ICMP error handler. */
-SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
-                            u8 type, u8 code, int offset, __be32 info)
+static void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+                       u8 type, u8 code, int offset, __be32 info)
 {
        struct inet6_dev *idev;
        struct sock *sk;
        struct sctp_association *asoc;
        struct sctp_transport *transport;
        struct ipv6_pinfo *np;
-       sk_buff_data_t saveip, savesctp;
+       __u16 saveip, savesctp;
        int err;
        struct net *net = dev_net(skb->dev);
 
@@ -239,9 +239,8 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
                fl6.daddr = *rt0->addr;
        }
 
-       SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n",
-                         __func__, skb, skb->len,
-                         &fl6.saddr, &fl6.daddr);
+       pr_debug("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n", __func__, skb,
+                skb->len, &fl6.saddr, &fl6.daddr);
 
        SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS);
 
@@ -276,7 +275,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
        if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
                fl6->flowi6_oif = daddr->v6.sin6_scope_id;
 
-       SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr);
+       pr_debug("%s: dst=%pI6 ", __func__, &fl6->daddr);
 
        if (asoc)
                fl6->fl6_sport = htons(asoc->base.bind_addr.port);
@@ -284,7 +283,8 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
        if (saddr) {
                fl6->saddr = saddr->v6.sin6_addr;
                fl6->fl6_sport = saddr->v6.sin6_port;
-               SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr);
+
+               pr_debug("src=%pI6 - ", &fl6->saddr);
        }
 
        dst = ip6_dst_lookup_flow(sk, fl6, NULL, false);
@@ -348,13 +348,16 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
 out:
        if (!IS_ERR_OR_NULL(dst)) {
                struct rt6_info *rt;
+
                rt = (struct rt6_info *)dst;
                t->dst = dst;
-               SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n",
-                       &rt->rt6i_dst.addr, &fl6->saddr);
+
+               pr_debug("rt6_dst:%pI6 rt6_src:%pI6\n", &rt->rt6i_dst.addr,
+                        &fl6->saddr);
        } else {
                t->dst = NULL;
-               SCTP_DEBUG_PRINTK("NO ROUTE\n");
+
+               pr_debug("no route\n");
        }
 }
 
@@ -377,7 +380,7 @@ static void sctp_v6_get_saddr(struct sctp_sock *sk,
        struct flowi6 *fl6 = &fl->u.ip6;
        union sctp_addr *saddr = &t->saddr;
 
-       SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p\n", __func__, t->asoc, t->dst);
+       pr_debug("%s: asoc:%p dst:%p\n", __func__, t->asoc, t->dst);
 
        if (t->dst) {
                saddr->v6.sin6_family = AF_INET6;
@@ -402,7 +405,7 @@ static void sctp_v6_copy_addrlist(struct list_head *addrlist,
        read_lock_bh(&in6_dev->lock);
        list_for_each_entry(ifp, &in6_dev->addr_list, if_list) {
                /* Add the address to the local list.  */
-               addr = t_new(struct sctp_sockaddr_entry, GFP_ATOMIC);
+               addr = kzalloc(sizeof(*addr), GFP_ATOMIC);
                if (addr) {
                        addr->a.v6.sin6_family = AF_INET6;
                        addr->a.v6.sin6_port = 0;
index bbef4a7..a46d1eb 100644 (file)
@@ -93,8 +93,7 @@ struct sctp_packet *sctp_packet_config(struct sctp_packet *packet,
 {
        struct sctp_chunk *chunk = NULL;
 
-       SCTP_DEBUG_PRINTK("%s: packet:%p vtag:0x%x\n", __func__,
-                         packet, vtag);
+       pr_debug("%s: packet:%p vtag:0x%x\n", __func__, packet, vtag);
 
        packet->vtag = vtag;
 
@@ -119,8 +118,7 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet,
        struct sctp_association *asoc = transport->asoc;
        size_t overhead;
 
-       SCTP_DEBUG_PRINTK("%s: packet:%p transport:%p\n", __func__,
-                         packet, transport);
+       pr_debug("%s: packet:%p transport:%p\n", __func__, packet, transport);
 
        packet->transport = transport;
        packet->source_port = sport;
@@ -145,7 +143,7 @@ void sctp_packet_free(struct sctp_packet *packet)
 {
        struct sctp_chunk *chunk, *tmp;
 
-       SCTP_DEBUG_PRINTK("%s: packet:%p\n", __func__, packet);
+       pr_debug("%s: packet:%p\n", __func__, packet);
 
        list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) {
                list_del_init(&chunk->list);
@@ -167,8 +165,7 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
        sctp_xmit_t retval;
        int error = 0;
 
-       SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __func__,
-                         packet, chunk);
+       pr_debug("%s: packet:%p chunk:%p\n", __func__, packet, chunk);
 
        switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) {
        case SCTP_XMIT_PMTU_FULL:
@@ -334,8 +331,7 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
 {
        sctp_xmit_t retval = SCTP_XMIT_OK;
 
-       SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __func__, packet,
-                         chunk);
+       pr_debug("%s: packet:%p chunk:%p\n", __func__, packet, chunk);
 
        /* Data chunks are special.  Before seeing what else we can
         * bundle into this packet, check to see if we are allowed to
@@ -402,7 +398,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
        unsigned char *auth = NULL;     /* pointer to auth in skb data */
        __u32 cksum_buf_len = sizeof(struct sctphdr);
 
-       SCTP_DEBUG_PRINTK("%s: packet:%p\n", __func__, packet);
+       pr_debug("%s: packet:%p\n", __func__, packet);
 
        /* Do NOT generate a chunkless packet. */
        if (list_empty(&packet->chunk_list))
@@ -472,7 +468,9 @@ int sctp_packet_transmit(struct sctp_packet *packet)
         *
         * [This whole comment explains WORD_ROUND() below.]
         */
-       SCTP_DEBUG_PRINTK("***sctp_transmit_packet***\n");
+
+       pr_debug("***sctp_transmit_packet***\n");
+
        list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) {
                list_del_init(&chunk->list);
                if (sctp_chunk_is_data(chunk)) {
@@ -505,16 +503,13 @@ int sctp_packet_transmit(struct sctp_packet *packet)
                memcpy(skb_put(nskb, chunk->skb->len),
                               chunk->skb->data, chunk->skb->len);
 
-               SCTP_DEBUG_PRINTK("%s %p[%s] %s 0x%x, %s %d, %s %d, %s %d\n",
-                                 "*** Chunk", chunk,
-                                 sctp_cname(SCTP_ST_CHUNK(
-                                         chunk->chunk_hdr->type)),
-                                 chunk->has_tsn ? "TSN" : "No TSN",
-                                 chunk->has_tsn ?
-                                 ntohl(chunk->subh.data_hdr->tsn) : 0,
-                                 "length", ntohs(chunk->chunk_hdr->length),
-                                 "chunk->skb->len", chunk->skb->len,
-                                 "rtt_in_progress", chunk->rtt_in_progress);
+               pr_debug("*** Chunk:%p[%s] %s 0x%x, length:%d, chunk->skb->len:%d, "
+                        "rtt_in_progress:%d\n", chunk,
+                        sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)),
+                        chunk->has_tsn ? "TSN" : "No TSN",
+                        chunk->has_tsn ? ntohl(chunk->subh.data_hdr->tsn) : 0,
+                        ntohs(chunk->chunk_hdr->length), chunk->skb->len,
+                        chunk->rtt_in_progress);
 
                /*
                 * If this is a control chunk, this is our last
@@ -606,8 +601,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
                }
        }
 
-       SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb len %d\n",
-                         nskb->len);
+       pr_debug("***sctp_transmit_packet*** skb->len:%d\n", nskb->len);
 
        nskb->local_df = packet->ipfragok;
        (*tp->af_specific->sctp_xmit)(nskb, tp);
index be35e2d..ef9e2bb 100644 (file)
@@ -299,10 +299,10 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk)
        struct net *net = sock_net(q->asoc->base.sk);
        int error = 0;
 
-       SCTP_DEBUG_PRINTK("sctp_outq_tail(%p, %p[%s])\n",
-                         q, chunk, chunk && chunk->chunk_hdr ?
-                         sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
-                         : "Illegal Chunk");
+       pr_debug("%s: outq:%p, chunk:%p[%s]\n", __func__, q, chunk,
+                chunk && chunk->chunk_hdr ?
+                sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) :
+                "illegal chunk");
 
        /* If it is data, queue it up, otherwise, send it
         * immediately.
@@ -328,10 +328,10 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk)
                        break;
 
                default:
-                       SCTP_DEBUG_PRINTK("outqueueing (%p, %p[%s])\n",
-                         q, chunk, chunk && chunk->chunk_hdr ?
-                         sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
-                         : "Illegal Chunk");
+                       pr_debug("%s: outqueueing: outq:%p, chunk:%p[%s])\n",
+                                __func__, q, chunk, chunk && chunk->chunk_hdr ?
+                                sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) :
+                                "illegal chunk");
 
                        sctp_outq_tail_data(q, chunk);
                        if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
@@ -460,14 +460,10 @@ void sctp_retransmit_mark(struct sctp_outq *q,
                }
        }
 
-       SCTP_DEBUG_PRINTK("%s: transport: %p, reason: %d, "
-                         "cwnd: %d, ssthresh: %d, flight_size: %d, "
-                         "pba: %d\n", __func__,
-                         transport, reason,
-                         transport->cwnd, transport->ssthresh,
-                         transport->flight_size,
-                         transport->partial_bytes_acked);
-
+       pr_debug("%s: transport:%p, reason:%d, cwnd:%d, ssthresh:%d, "
+                "flight_size:%d, pba:%d\n", __func__, transport, reason,
+                transport->cwnd, transport->ssthresh, transport->flight_size,
+                transport->partial_bytes_acked);
 }
 
 /* Mark all the eligible packets on a transport for retransmission and force
@@ -1014,19 +1010,13 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
                                sctp_transport_burst_limited(transport);
                        }
 
-                       SCTP_DEBUG_PRINTK("sctp_outq_flush(%p, %p[%s]), ",
-                                         q, chunk,
-                                         chunk && chunk->chunk_hdr ?
-                                         sctp_cname(SCTP_ST_CHUNK(
-                                                 chunk->chunk_hdr->type))
-                                         : "Illegal Chunk");
-
-                       SCTP_DEBUG_PRINTK("TX TSN 0x%x skb->head "
-                                       "%p skb->users %d.\n",
-                                       ntohl(chunk->subh.data_hdr->tsn),
-                                       chunk->skb ?chunk->skb->head : NULL,
-                                       chunk->skb ?
-                                       atomic_read(&chunk->skb->users) : -1);
+                       pr_debug("%s: outq:%p, chunk:%p[%s], tx-tsn:0x%x skb->head:%p "
+                                "skb->users:%d\n",
+                                __func__, q, chunk, chunk && chunk->chunk_hdr ?
+                                sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) :
+                                "illegal chunk", ntohl(chunk->subh.data_hdr->tsn),
+                                chunk->skb ? chunk->skb->head : NULL, chunk->skb ?
+                                atomic_read(&chunk->skb->users) : -1);
 
                        /* Add the chunk to the packet.  */
                        status = sctp_packet_transmit_chunk(packet, chunk, 0);
@@ -1038,10 +1028,10 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
                                /* We could not append this chunk, so put
                                 * the chunk back on the output queue.
                                 */
-                               SCTP_DEBUG_PRINTK("sctp_outq_flush: could "
-                                       "not transmit TSN: 0x%x, status: %d\n",
-                                       ntohl(chunk->subh.data_hdr->tsn),
-                                       status);
+                               pr_debug("%s: could not transmit tsn:0x%x, status:%d\n",
+                                        __func__, ntohl(chunk->subh.data_hdr->tsn),
+                                        status);
+
                                sctp_outq_head_data(q, chunk);
                                goto sctp_flush_out;
                                break;
@@ -1284,11 +1274,10 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk)
 
        sctp_generate_fwdtsn(q, sack_ctsn);
 
-       SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n",
-                         __func__, sack_ctsn);
-       SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association, "
-                         "%p is 0x%x. Adv peer ack point: 0x%x\n",
-                         __func__, asoc, ctsn, asoc->adv_peer_ack_point);
+       pr_debug("%s: sack cumulative tsn ack:0x%x\n", __func__, sack_ctsn);
+       pr_debug("%s: cumulative tsn ack of assoc:%p is 0x%x, "
+                "advertised peer ack point:0x%x\n", __func__, asoc, ctsn,
+                asoc->adv_peer_ack_point);
 
        /* See if all chunks are acked.
         * Make sure the empty queue handler will get run later.
@@ -1304,7 +1293,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk)
                        goto finish;
        }
 
-       SCTP_DEBUG_PRINTK("sack queue is empty.\n");
+       pr_debug("%s: sack queue is empty\n", __func__);
 finish:
        return q->empty;
 }
@@ -1345,21 +1334,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
        __u8 restart_timer = 0;
        int bytes_acked = 0;
        int migrate_bytes = 0;
-
-       /* These state variables are for coherent debug output. --xguo */
-
-#if SCTP_DEBUG
-       __u32 dbg_ack_tsn = 0;  /* An ACKed TSN range starts here... */
-       __u32 dbg_last_ack_tsn = 0;  /* ...and finishes here.        */
-       __u32 dbg_kept_tsn = 0; /* An un-ACKed range starts here...  */
-       __u32 dbg_last_kept_tsn = 0; /* ...and finishes here.        */
-
-       /* 0 : The last TSN was ACKed.
-        * 1 : The last TSN was NOT ACKed (i.e. KEPT).
-        * -1: We need to initialize.
-        */
-       int dbg_prt_state = -1;
-#endif /* SCTP_DEBUG */
+       bool forward_progress = false;
 
        sack_ctsn = ntohl(sack->cum_tsn_ack);
 
@@ -1426,6 +1401,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
                                bytes_acked += sctp_data_size(tchunk);
                                if (!tchunk->transport)
                                        migrate_bytes += sctp_data_size(tchunk);
+                               forward_progress = true;
                        }
 
                        if (TSN_lte(tsn, sack_ctsn)) {
@@ -1439,6 +1415,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
                                 * current RTO.
                                 */
                                restart_timer = 1;
+                               forward_progress = true;
 
                                if (!tchunk->tsn_gap_acked) {
                                        /*
@@ -1482,57 +1459,11 @@ static void sctp_check_transmitted(struct sctp_outq *q,
                                 */
                                list_add_tail(lchunk, &tlist);
                        }
-
-#if SCTP_DEBUG
-                       switch (dbg_prt_state) {
-                       case 0: /* last TSN was ACKed */
-                               if (dbg_last_ack_tsn + 1 == tsn) {
-                                       /* This TSN belongs to the
-                                        * current ACK range.
-                                        */
-                                       break;
-                               }
-
-                               if (dbg_last_ack_tsn != dbg_ack_tsn) {
-                                       /* Display the end of the
-                                        * current range.
-                                        */
-                                       SCTP_DEBUG_PRINTK_CONT("-%08x",
-                                                              dbg_last_ack_tsn);
-                               }
-
-                               /* Start a new range.  */
-                               SCTP_DEBUG_PRINTK_CONT(",%08x", tsn);
-                               dbg_ack_tsn = tsn;
-                               break;
-
-                       case 1: /* The last TSN was NOT ACKed. */
-                               if (dbg_last_kept_tsn != dbg_kept_tsn) {
-                                       /* Display the end of current range. */
-                                       SCTP_DEBUG_PRINTK_CONT("-%08x",
-                                                              dbg_last_kept_tsn);
-                               }
-
-                               SCTP_DEBUG_PRINTK_CONT("\n");
-
-                               /* FALL THROUGH... */
-                       default:
-                               /* This is the first-ever TSN we examined.  */
-                               /* Start a new range of ACK-ed TSNs.  */
-                               SCTP_DEBUG_PRINTK("ACKed: %08x", tsn);
-                               dbg_prt_state = 0;
-                               dbg_ack_tsn = tsn;
-                       }
-
-                       dbg_last_ack_tsn = tsn;
-#endif /* SCTP_DEBUG */
-
                } else {
                        if (tchunk->tsn_gap_acked) {
-                               SCTP_DEBUG_PRINTK("%s: Receiver reneged on "
-                                                 "data TSN: 0x%x\n",
-                                                 __func__,
-                                                 tsn);
+                               pr_debug("%s: receiver reneged on data TSN:0x%x\n",
+                                        __func__, tsn);
+
                                tchunk->tsn_gap_acked = 0;
 
                                if (tchunk->transport)
@@ -1551,59 +1482,9 @@ static void sctp_check_transmitted(struct sctp_outq *q,
                        }
 
                        list_add_tail(lchunk, &tlist);
-
-#if SCTP_DEBUG
-                       /* See the above comments on ACK-ed TSNs. */
-                       switch (dbg_prt_state) {
-                       case 1:
-                               if (dbg_last_kept_tsn + 1 == tsn)
-                                       break;
-
-                               if (dbg_last_kept_tsn != dbg_kept_tsn)
-                                       SCTP_DEBUG_PRINTK_CONT("-%08x",
-                                                              dbg_last_kept_tsn);
-
-                               SCTP_DEBUG_PRINTK_CONT(",%08x", tsn);
-                               dbg_kept_tsn = tsn;
-                               break;
-
-                       case 0:
-                               if (dbg_last_ack_tsn != dbg_ack_tsn)
-                                       SCTP_DEBUG_PRINTK_CONT("-%08x",
-                                                              dbg_last_ack_tsn);
-                               SCTP_DEBUG_PRINTK_CONT("\n");
-
-                               /* FALL THROUGH... */
-                       default:
-                               SCTP_DEBUG_PRINTK("KEPT: %08x",tsn);
-                               dbg_prt_state = 1;
-                               dbg_kept_tsn = tsn;
-                       }
-
-                       dbg_last_kept_tsn = tsn;
-#endif /* SCTP_DEBUG */
                }
        }
 
-#if SCTP_DEBUG
-       /* Finish off the last range, displaying its ending TSN.  */
-       switch (dbg_prt_state) {
-       case 0:
-               if (dbg_last_ack_tsn != dbg_ack_tsn) {
-                       SCTP_DEBUG_PRINTK_CONT("-%08x\n", dbg_last_ack_tsn);
-               } else {
-                       SCTP_DEBUG_PRINTK_CONT("\n");
-               }
-       break;
-
-       case 1:
-               if (dbg_last_kept_tsn != dbg_kept_tsn) {
-                       SCTP_DEBUG_PRINTK_CONT("-%08x\n", dbg_last_kept_tsn);
-               } else {
-                       SCTP_DEBUG_PRINTK_CONT("\n");
-               }
-       }
-#endif /* SCTP_DEBUG */
        if (transport) {
                if (bytes_acked) {
                        struct sctp_association *asoc = transport->asoc;
@@ -1625,6 +1506,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
                         */
                        transport->error_count = 0;
                        transport->asoc->overall_error_count = 0;
+                       forward_progress = true;
 
                        /*
                         * While in SHUTDOWN PENDING, we may have started
@@ -1676,9 +1558,9 @@ static void sctp_check_transmitted(struct sctp_outq *q,
                            !list_empty(&tlist) &&
                            (sack_ctsn+2 == q->asoc->next_tsn) &&
                            q->asoc->state < SCTP_STATE_SHUTDOWN_PENDING) {
-                               SCTP_DEBUG_PRINTK("%s: SACK received for zero "
-                                                 "window probe: %u\n",
-                                                 __func__, sack_ctsn);
+                               pr_debug("%s: sack received for zero window "
+                                        "probe:%u\n", __func__, sack_ctsn);
+
                                q->asoc->overall_error_count = 0;
                                transport->error_count = 0;
                        }
@@ -1698,6 +1580,11 @@ static void sctp_check_transmitted(struct sctp_outq *q,
                                       jiffies + transport->rto))
                                sctp_transport_hold(transport);
                }
+
+               if (forward_progress) {
+                       if (transport->dst)
+                               dst_confirm(transport->dst);
+               }
        }
 
        list_splice(&tlist, transmitted_queue);
@@ -1739,10 +1626,8 @@ static void sctp_mark_missing(struct sctp_outq *q,
                                                count_of_newacks, tsn)) {
                                chunk->tsn_missing_report++;
 
-                               SCTP_DEBUG_PRINTK(
-                                       "%s: TSN 0x%x missing counter: %d\n",
-                                       __func__, tsn,
-                                       chunk->tsn_missing_report);
+                               pr_debug("%s: tsn:0x%x missing counter:%d\n",
+                                        __func__, tsn, chunk->tsn_missing_report);
                        }
                }
                /*
@@ -1762,11 +1647,10 @@ static void sctp_mark_missing(struct sctp_outq *q,
                if (do_fast_retransmit)
                        sctp_retransmit(q, transport, SCTP_RTXR_FAST_RTX);
 
-               SCTP_DEBUG_PRINTK("%s: transport: %p, cwnd: %d, "
-                                 "ssthresh: %d, flight_size: %d, pba: %d\n",
-                                 __func__, transport, transport->cwnd,
-                                 transport->ssthresh, transport->flight_size,
-                                 transport->partial_bytes_acked);
+               pr_debug("%s: transport:%p, cwnd:%d, ssthresh:%d, "
+                        "flight_size:%d, pba:%d\n",  __func__, transport,
+                        transport->cwnd, transport->ssthresh,
+                        transport->flight_size, transport->partial_bytes_acked);
        }
 }
 
index 4e45ee3..62526c4 100644 (file)
@@ -134,9 +134,15 @@ static void sctp_seq_dump_local_addrs(struct seq_file *seq, struct sctp_ep_commo
        struct sctp_af *af;
 
        if (epb->type == SCTP_EP_TYPE_ASSOCIATION) {
-           asoc = sctp_assoc(epb);
-           peer = asoc->peer.primary_path;
-           primary = &peer->saddr;
+               asoc = sctp_assoc(epb);
+
+               peer = asoc->peer.primary_path;
+               if (unlikely(peer == NULL)) {
+                       WARN(1, "Association %p with NULL primary path!\n", asoc);
+                       return;
+               }
+
+               primary = &peer->saddr;
        }
 
        rcu_read_lock();
index eaee00c..4a17494 100644 (file)
@@ -153,7 +153,7 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist,
 
        for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
                /* Add the address to the local list.  */
-               addr = t_new(struct sctp_sockaddr_entry, GFP_ATOMIC);
+               addr = kzalloc(sizeof(*addr), GFP_ATOMIC);
                if (addr) {
                        addr->a.v4.sin_family = AF_INET;
                        addr->a.v4.sin_port = 0;
@@ -178,7 +178,7 @@ static void sctp_get_local_addr_list(struct net *net)
 
        rcu_read_lock();
        for_each_netdev_rcu(net, dev) {
-               __list_for_each(pos, &sctp_address_families) {
+               list_for_each(pos, &sctp_address_families) {
                        af = list_entry(pos, struct sctp_af, list);
                        af->copy_addrlist(&net->sctp.local_addr_list, dev);
                }
@@ -451,8 +451,8 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
                fl4->fl4_sport = saddr->v4.sin_port;
        }
 
-       SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ",
-                         __func__, &fl4->daddr, &fl4->saddr);
+       pr_debug("%s: dst:%pI4, src:%pI4 - ", __func__, &fl4->daddr,
+                &fl4->saddr);
 
        rt = ip_route_output_key(sock_net(sk), fl4);
        if (!IS_ERR(rt))
@@ -513,10 +513,10 @@ out_unlock:
 out:
        t->dst = dst;
        if (dst)
-               SCTP_DEBUG_PRINTK("rt_dst:%pI4, rt_src:%pI4\n",
-                                 &fl4->daddr, &fl4->saddr);
+               pr_debug("rt_dst:%pI4, rt_src:%pI4\n",
+                        &fl4->daddr, &fl4->saddr);
        else
-               SCTP_DEBUG_PRINTK("NO ROUTE\n");
+               pr_debug("no route\n");
 }
 
 /* For v4, the source address is cached in the route entry(dst). So no need
@@ -604,9 +604,9 @@ static void sctp_addr_wq_timeout_handler(unsigned long arg)
        spin_lock_bh(&net->sctp.addr_wq_lock);
 
        list_for_each_entry_safe(addrw, temp, &net->sctp.addr_waitq, list) {
-               SCTP_DEBUG_PRINTK_IPADDR("sctp_addrwq_timo_handler: the first ent in wq %p is ",
-                   " for cmd %d at entry %p\n", &net->sctp.addr_waitq, &addrw->a, addrw->state,
-                   addrw);
+               pr_debug("%s: the first ent in wq:%p is addr:%pISc for cmd:%d at "
+                        "entry:%p\n", __func__, &net->sctp.addr_waitq, &addrw->a.sa,
+                        addrw->state, addrw);
 
 #if IS_ENABLED(CONFIG_IPV6)
                /* Now we send an ASCONF for each association */
@@ -623,8 +623,10 @@ static void sctp_addr_wq_timeout_handler(unsigned long arg)
                            addrw->state == SCTP_ADDR_NEW) {
                                unsigned long timeo_val;
 
-                               SCTP_DEBUG_PRINTK("sctp_timo_handler: this is on DAD, trying %d sec later\n",
-                                   SCTP_ADDRESS_TICK_DELAY);
+                               pr_debug("%s: this is on DAD, trying %d sec "
+                                        "later\n", __func__,
+                                        SCTP_ADDRESS_TICK_DELAY);
+
                                timeo_val = jiffies;
                                timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY);
                                mod_timer(&net->sctp.addr_wq_timer, timeo_val);
@@ -641,7 +643,7 @@ static void sctp_addr_wq_timeout_handler(unsigned long arg)
                                continue;
                        sctp_bh_lock_sock(sk);
                        if (sctp_asconf_mgmt(sp, addrw) < 0)
-                               SCTP_DEBUG_PRINTK("sctp_addrwq_timo_handler: sctp_asconf_mgmt failed\n");
+                               pr_debug("%s: sctp_asconf_mgmt failed\n", __func__);
                        sctp_bh_unlock_sock(sk);
                }
 #if IS_ENABLED(CONFIG_IPV6)
@@ -707,9 +709,10 @@ void sctp_addr_wq_mgmt(struct net *net, struct sctp_sockaddr_entry *addr, int cm
        addrw = sctp_addr_wq_lookup(net, addr);
        if (addrw) {
                if (addrw->state != cmd) {
-                       SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt offsets existing entry for %d ",
-                           " in wq %p\n", addrw->state, &addrw->a,
-                           &net->sctp.addr_waitq);
+                       pr_debug("%s: offsets existing entry for %d, addr:%pISc "
+                                "in wq:%p\n", __func__, addrw->state, &addrw->a.sa,
+                                &net->sctp.addr_waitq);
+
                        list_del(&addrw->list);
                        kfree(addrw);
                }
@@ -725,8 +728,9 @@ void sctp_addr_wq_mgmt(struct net *net, struct sctp_sockaddr_entry *addr, int cm
        }
        addrw->state = cmd;
        list_add_tail(&addrw->list, &net->sctp.addr_waitq);
-       SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt add new entry for cmd:%d ",
-           " in wq %p\n", addrw->state, &addrw->a, &net->sctp.addr_waitq);
+
+       pr_debug("%s: add new entry for cmd:%d, addr:%pISc in wq:%p\n",
+                __func__, addrw->state, &addrw->a.sa, &net->sctp.addr_waitq);
 
        if (!timer_pending(&net->sctp.addr_wq_timer)) {
                timeo_val = jiffies;
@@ -952,15 +956,14 @@ static inline int sctp_v4_xmit(struct sk_buff *skb,
 {
        struct inet_sock *inet = inet_sk(skb->sk);
 
-       SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI4, dst:%pI4\n",
-                         __func__, skb, skb->len,
-                         &transport->fl.u.ip4.saddr,
-                         &transport->fl.u.ip4.daddr);
+       pr_debug("%s: skb:%p, len:%d, src:%pI4, dst:%pI4\n", __func__, skb,
+                skb->len, &transport->fl.u.ip4.saddr, &transport->fl.u.ip4.daddr);
 
        inet->pmtudisc = transport->param_flags & SPP_PMTUD_ENABLE ?
                         IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
 
        SCTP_INC_STATS(sock_net(&inet->sk), SCTP_MIB_OUTSCTPPACKS);
+
        return ip_queue_xmit(skb, &transport->fl);
 }
 
@@ -1312,7 +1315,7 @@ static struct pernet_operations sctp_net_ops = {
 };
 
 /* Initialize the universe into something sensible.  */
-SCTP_STATIC __init int sctp_init(void)
+static __init int sctp_init(void)
 {
        int i;
        int status = -EINVAL;
@@ -1321,9 +1324,8 @@ SCTP_STATIC __init int sctp_init(void)
        int max_share;
        int order;
 
-       /* SCTP_DEBUG sanity check. */
-       if (!sctp_sanity_check())
-               goto out;
+       BUILD_BUG_ON(sizeof(struct sctp_ulpevent) >
+                    sizeof(((struct sk_buff *) 0)->cb));
 
        /* Allocate bind_bucket and chunk caches. */
        status = -ENOBUFS;
@@ -1499,7 +1501,7 @@ err_chunk_cachep:
 }
 
 /* Exit handler for the SCTP protocol.  */
-SCTP_STATIC __exit void sctp_exit(void)
+static __exit void sctp_exit(void)
 {
        /* BUG.  This should probably do something useful like clean
         * up all the remaining associations and all that memory.
index cf579e7..362ae6e 100644 (file)
@@ -68,9 +68,8 @@
 #include <net/sctp/sctp.h>
 #include <net/sctp/sm.h>
 
-SCTP_STATIC
-struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc,
-                                  __u8 type, __u8 flags, int paylen);
+static struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc,
+                                         __u8 type, __u8 flags, int paylen);
 static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
                                        const struct sctp_association *asoc,
                                        const struct sctp_chunk *init_chunk,
@@ -742,7 +741,8 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc)
 
        memset(gabs, 0, sizeof(gabs));
        ctsn = sctp_tsnmap_get_ctsn(map);
-       SCTP_DEBUG_PRINTK("sackCTSNAck sent:  0x%x.\n", ctsn);
+
+       pr_debug("%s: sackCTSNAck sent:0x%x\n", __func__, ctsn);
 
        /* How much room is needed in the chunk? */
        num_gabs = sctp_tsnmap_num_gabs(map, gabs);
@@ -1288,10 +1288,8 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb,
 
        if (!retval)
                goto nodata;
-
-       if (!sk) {
-               SCTP_DEBUG_PRINTK("chunkifying skb %p w/o an sk\n", skb);
-       }
+       if (!sk)
+               pr_debug("%s: chunkifying skb:%p w/o an sk\n", __func__, skb);
 
        INIT_LIST_HEAD(&retval->list);
        retval->skb             = skb;
@@ -1353,9 +1351,8 @@ const union sctp_addr *sctp_source(const struct sctp_chunk *chunk)
 /* Create a new chunk, setting the type and flags headers from the
  * arguments, reserving enough space for a 'paylen' byte payload.
  */
-SCTP_STATIC
-struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc,
-                                  __u8 type, __u8 flags, int paylen)
+static struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc,
+                                         __u8 type, __u8 flags, int paylen)
 {
        struct sctp_chunk *retval;
        sctp_chunkhdr_t *chunk_hdr;
@@ -1632,8 +1629,8 @@ static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
        cookie->c.adaptation_ind = asoc->peer.adaptation_ind;
 
        /* Set an expiration time for the cookie.  */
-       do_gettimeofday(&cookie->c.expiration);
-       TIMEVAL_ADD(asoc->cookie_life, cookie->c.expiration);
+       cookie->c.expiration = ktime_add(asoc->cookie_life,
+                                        ktime_get());
 
        /* Copy the peer's init packet.  */
        memcpy(&cookie->c.peer_init[0], init_chunk->chunk_hdr,
@@ -1682,7 +1679,7 @@ struct sctp_association *sctp_unpack_cookie(
        unsigned int len;
        sctp_scope_t scope;
        struct sk_buff *skb = chunk->skb;
-       struct timeval tv;
+       ktime_t kt;
        struct hash_desc desc;
 
        /* Header size is static data prior to the actual cookie, including
@@ -1759,11 +1756,11 @@ no_hmac:
         * down the new association establishment instead of every packet.
         */
        if (sock_flag(ep->base.sk, SOCK_TIMESTAMP))
-               skb_get_timestamp(skb, &tv);
+               kt = skb_get_ktime(skb);
        else
-               do_gettimeofday(&tv);
+               kt = ktime_get();
 
-       if (!asoc && tv_lt(bear_cookie->expiration, tv)) {
+       if (!asoc && ktime_compare(bear_cookie->expiration, kt) < 0) {
                /*
                 * Section 3.3.10.3 Stale Cookie Error (3)
                 *
@@ -1775,9 +1772,7 @@ no_hmac:
                len = ntohs(chunk->chunk_hdr->length);
                *errp = sctp_make_op_error_space(asoc, chunk, len);
                if (*errp) {
-                       suseconds_t usecs = (tv.tv_sec -
-                               bear_cookie->expiration.tv_sec) * 1000000L +
-                               tv.tv_usec - bear_cookie->expiration.tv_usec;
+                       suseconds_t usecs = ktime_to_us(ktime_sub(kt, bear_cookie->expiration));
                        __be32 n = htonl(usecs);
 
                        sctp_init_cause(*errp, SCTP_ERROR_STALE_COOKIE,
@@ -2195,8 +2190,9 @@ static sctp_ierror_t sctp_verify_param(struct net *net,
                break;
 fallthrough:
        default:
-               SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
-                               ntohs(param.p->type), cid);
+               pr_debug("%s: unrecognized param:%d for chunk:%d\n",
+                        __func__, ntohs(param.p->type), cid);
+
                retval = sctp_process_unk_param(asoc, param, chunk, err_chunk);
                break;
        }
@@ -2516,12 +2512,11 @@ do_addr_param:
                /* Suggested Cookie Life span increment's unit is msec,
                 * (1/1000sec).
                 */
-               asoc->cookie_life.tv_sec += stale / 1000;
-               asoc->cookie_life.tv_usec += (stale % 1000) * 1000;
+               asoc->cookie_life = ktime_add_ms(asoc->cookie_life, stale);
                break;
 
        case SCTP_PARAM_HOST_NAME_ADDRESS:
-               SCTP_DEBUG_PRINTK("unimplemented SCTP_HOST_NAME_ADDRESS\n");
+               pr_debug("%s: unimplemented SCTP_HOST_NAME_ADDRESS\n", __func__);
                break;
 
        case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES:
@@ -2667,8 +2662,8 @@ fall_through:
                 * called prior to this routine.  Simply log the error
                 * here.
                 */
-               SCTP_DEBUG_PRINTK("Ignoring param: %d for association %p.\n",
-                                 ntohs(param.p->type), asoc);
+               pr_debug("%s: ignoring param:%d for association:%p.\n",
+                        __func__, ntohs(param.p->type), asoc);
                break;
        }
 
@@ -2810,7 +2805,10 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
                        totallen += paramlen;
                        totallen += addr_param_len;
                        del_pickup = 1;
-                       SCTP_DEBUG_PRINTK("mkasconf_update_ip: picked same-scope del_pending addr, totallen for all addresses is %d\n", totallen);
+
+                       pr_debug("%s: picked same-scope del_pending addr, "
+                                "totallen for all addresses is %d\n",
+                                __func__, totallen);
                }
        }
 
index 8aab894..9da6885 100644 (file)
@@ -257,7 +257,7 @@ void sctp_generate_t3_rtx_event(unsigned long peer)
 
        sctp_bh_lock_sock(asoc->base.sk);
        if (sock_owned_by_user(asoc->base.sk)) {
-               SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __func__);
+               pr_debug("%s: sock is busy\n", __func__);
 
                /* Try again later.  */
                if (!mod_timer(&transport->T3_rtx_timer, jiffies + (HZ/20)))
@@ -297,9 +297,8 @@ static void sctp_generate_timeout_event(struct sctp_association *asoc,
 
        sctp_bh_lock_sock(asoc->base.sk);
        if (sock_owned_by_user(asoc->base.sk)) {
-               SCTP_DEBUG_PRINTK("%s:Sock is busy: timer %d\n",
-                                 __func__,
-                                 timeout_type);
+               pr_debug("%s: sock is busy: timer %d\n", __func__,
+                        timeout_type);
 
                /* Try again later.  */
                if (!mod_timer(&asoc->timers[timeout_type], jiffies + (HZ/20)))
@@ -377,7 +376,7 @@ void sctp_generate_heartbeat_event(unsigned long data)
 
        sctp_bh_lock_sock(asoc->base.sk);
        if (sock_owned_by_user(asoc->base.sk)) {
-               SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __func__);
+               pr_debug("%s: sock is busy\n", __func__);
 
                /* Try again later.  */
                if (!mod_timer(&transport->hb_timer, jiffies + (HZ/20)))
@@ -415,7 +414,7 @@ void sctp_generate_proto_unreach_event(unsigned long data)
        
        sctp_bh_lock_sock(asoc->base.sk);
        if (sock_owned_by_user(asoc->base.sk)) {
-               SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __func__);
+               pr_debug("%s: sock is busy\n", __func__);
 
                /* Try again later.  */
                if (!mod_timer(&transport->proto_unreach_timer,
@@ -521,11 +520,9 @@ static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands,
 
        if (transport->state != SCTP_INACTIVE &&
            (transport->error_count > transport->pathmaxrxt)) {
-               SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p",
-                                        " transport IP: port:%d failed.\n",
-                                        asoc,
-                                        (&transport->ipaddr),
-                                        ntohs(transport->ipaddr.v4.sin_port));
+               pr_debug("%s: association:%p transport addr:%pISpc failed\n",
+                        __func__, asoc, &transport->ipaddr.sa);
+
                sctp_assoc_control_transport(asoc, transport,
                                             SCTP_TRANSPORT_DOWN,
                                             SCTP_FAILED_THRESHOLD);
@@ -733,6 +730,12 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
                sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP,
                                             SCTP_HEARTBEAT_SUCCESS);
 
+       /* HB-ACK was received for a the proper HB.  Consider this
+        * forward progress.
+        */
+       if (t->dst)
+               dst_confirm(t->dst);
+
        /* The receiver of the HEARTBEAT ACK should also perform an
         * RTT measurement for that destination transport address
         * using the time value carried in the HEARTBEAT ACK chunk.
@@ -804,8 +807,7 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds,
 
        asoc->state = state;
 
-       SCTP_DEBUG_PRINTK("sctp_cmd_new_state: asoc %p[%s]\n",
-                         asoc, sctp_state_tbl[state]);
+       pr_debug("%s: asoc:%p[%s]\n", __func__, asoc, sctp_state_tbl[state]);
 
        if (sctp_style(sk, TCP)) {
                /* Change the sk->sk_state of a TCP-style socket that has
@@ -864,6 +866,7 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds,
            (!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK))
                return;
 
+       BUG_ON(asoc->peer.primary_path == NULL);
        sctp_unhash_established(asoc);
        sctp_association_free(asoc);
 }
@@ -1016,15 +1019,11 @@ static void sctp_cmd_t1_timer_update(struct sctp_association *asoc,
                        asoc->timeouts[timer] = asoc->max_init_timeo;
                }
                asoc->init_cycle++;
-               SCTP_DEBUG_PRINTK(
-                       "T1 %s Timeout adjustment"
-                       " init_err_counter: %d"
-                       " cycle: %d"
-                       " timeout: %ld\n",
-                       name,
-                       asoc->init_err_counter,
-                       asoc->init_cycle,
-                       asoc->timeouts[timer]);
+
+               pr_debug("%s: T1[%s] timeout adjustment init_err_counter:%d"
+                        " cycle:%d timeout:%ld\n", __func__, name,
+                        asoc->init_err_counter, asoc->init_cycle,
+                        asoc->timeouts[timer]);
        }
 
 }
@@ -1079,23 +1078,19 @@ static void sctp_cmd_send_asconf(struct sctp_association *asoc)
  * main flow of sctp_do_sm() to keep attention focused on the real
  * functionality there.
  */
-#define DEBUG_PRE \
-       SCTP_DEBUG_PRINTK("sctp_do_sm prefn: " \
-                         "ep %p, %s, %s, asoc %p[%s], %s\n", \
-                         ep, sctp_evttype_tbl[event_type], \
-                         (*debug_fn)(subtype), asoc, \
-                         sctp_state_tbl[state], state_fn->name)
-
-#define DEBUG_POST \
-       SCTP_DEBUG_PRINTK("sctp_do_sm postfn: " \
-                         "asoc %p, status: %s\n", \
-                         asoc, sctp_status_tbl[status])
-
-#define DEBUG_POST_SFX \
-       SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \
-                         error, asoc, \
-                         sctp_state_tbl[(asoc && sctp_id2assoc(ep->base.sk, \
-                         sctp_assoc2id(asoc)))?asoc->state:SCTP_STATE_CLOSED])
+#define debug_pre_sfn() \
+       pr_debug("%s[pre-fn]: ep:%p, %s, %s, asoc:%p[%s], %s\n", __func__, \
+                ep, sctp_evttype_tbl[event_type], (*debug_fn)(subtype),   \
+                asoc, sctp_state_tbl[state], state_fn->name)
+
+#define debug_post_sfn() \
+       pr_debug("%s[post-fn]: asoc:%p, status:%s\n", __func__, asoc, \
+                sctp_status_tbl[status])
+
+#define debug_post_sfx() \
+       pr_debug("%s[post-sfx]: error:%d, asoc:%p[%s]\n", __func__, error, \
+                asoc, sctp_state_tbl[(asoc && sctp_id2assoc(ep->base.sk, \
+                sctp_assoc2id(asoc))) ? asoc->state : SCTP_STATE_CLOSED])
 
 /*
  * This is the master state machine processing function.
@@ -1115,7 +1110,6 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
        sctp_disposition_t status;
        int error = 0;
        typedef const char *(printfn_t)(sctp_subtype_t);
-
        static printfn_t *table[] = {
                NULL, sctp_cname, sctp_tname, sctp_oname, sctp_pname,
        };
@@ -1128,21 +1122,18 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
 
        sctp_init_cmd_seq(&commands);
 
-       DEBUG_PRE;
+       debug_pre_sfn();
        status = (*state_fn->fn)(net, ep, asoc, subtype, event_arg, &commands);
-       DEBUG_POST;
+       debug_post_sfn();
 
        error = sctp_side_effects(event_type, subtype, state,
                                  ep, asoc, event_arg, status,
                                  &commands, gfp);
-       DEBUG_POST_SFX;
+       debug_post_sfx();
 
        return error;
 }
 
-#undef DEBUG_PRE
-#undef DEBUG_POST
-
 /*****************************************************************
  * This the master state function side effect processing function.
  *****************************************************************/
@@ -1171,9 +1162,9 @@ static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
 
        switch (status) {
        case SCTP_DISPOSITION_DISCARD:
-               SCTP_DEBUG_PRINTK("Ignored sctp protocol event - state %d, "
-                                 "event_type %d, event_id %d\n",
-                                 state, event_type, subtype.chunk);
+               pr_debug("%s: ignored sctp protocol event - state:%d, "
+                        "event_type:%d, event_id:%d\n", __func__, state,
+                        event_type, subtype.chunk);
                break;
 
        case SCTP_DISPOSITION_NOMEM:
@@ -1274,8 +1265,10 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
                                sctp_outq_uncork(&asoc->outqueue);
                                local_cork = 0;
                        }
-                       asoc = cmd->obj.asoc;
+
                        /* Register with the endpoint.  */
+                       asoc = cmd->obj.asoc;
+                       BUG_ON(asoc->peer.primary_path == NULL);
                        sctp_endpoint_add_asoc(ep, asoc);
                        sctp_hash_established(asoc);
                        break;
@@ -1422,18 +1415,18 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
 
                case SCTP_CMD_CHUNK_ULP:
                        /* Send a chunk to the sockets layer.  */
-                       SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n",
-                                         "chunk_up:", cmd->obj.chunk,
-                                         "ulpq:", &asoc->ulpq);
+                       pr_debug("%s: sm_sideff: chunk_up:%p, ulpq:%p\n",
+                                __func__, cmd->obj.chunk, &asoc->ulpq);
+
                        sctp_ulpq_tail_data(&asoc->ulpq, cmd->obj.chunk,
                                            GFP_ATOMIC);
                        break;
 
                case SCTP_CMD_EVENT_ULP:
                        /* Send a notification to the sockets layer.  */
-                       SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n",
-                                         "event_up:",cmd->obj.ulpevent,
-                                         "ulpq:",&asoc->ulpq);
+                       pr_debug("%s: sm_sideff: event_up:%p, ulpq:%p\n",
+                                __func__, cmd->obj.ulpevent, &asoc->ulpq);
+
                        sctp_ulpq_tail_event(&asoc->ulpq, cmd->obj.ulpevent);
                        break;
 
@@ -1598,7 +1591,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
                        break;
 
                case SCTP_CMD_REPORT_BAD_TAG:
-                       SCTP_DEBUG_PRINTK("vtag mismatch!\n");
+                       pr_debug("%s: vtag mismatch!\n", __func__);
                        break;
 
                case SCTP_CMD_STRIKE:
index de1a013..f6b7109 100644 (file)
@@ -1179,9 +1179,9 @@ sctp_disposition_t sctp_sf_backbeat_8_3(struct net *net,
        /* Check if the timestamp looks valid.  */
        if (time_after(hbinfo->sent_at, jiffies) ||
            time_after(jiffies, hbinfo->sent_at + max_interval)) {
-               SCTP_DEBUG_PRINTK("%s: HEARTBEAT ACK with invalid timestamp "
-                                 "received for transport: %p\n",
-                                  __func__, link);
+               pr_debug("%s: HEARTBEAT ACK with invalid timestamp received "
+                        "for transport:%p\n", __func__, link);
+
                return SCTP_DISPOSITION_DISCARD;
        }
 
@@ -2562,7 +2562,8 @@ static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net,
                                           const struct sctp_association *asoc,
                                           struct sctp_transport *transport)
 {
-       SCTP_DEBUG_PRINTK("ABORT received (INIT).\n");
+       pr_debug("%s: ABORT received (INIT)\n", __func__);
+
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
                        SCTP_STATE(SCTP_STATE_CLOSED));
        SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
@@ -2572,6 +2573,7 @@ static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net,
        /* CMD_INIT_FAILED will DELETE_TCB. */
        sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
                        SCTP_PERR(error));
+
        return SCTP_DISPOSITION_ABORT;
 }
 
@@ -2637,8 +2639,9 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(struct net *net,
        ctsn = ntohl(sdh->cum_tsn_ack);
 
        if (TSN_lt(ctsn, asoc->ctsn_ack_point)) {
-               SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn);
-               SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point);
+               pr_debug("%s: ctsn:%x, ctsn_ack_point:%x\n", __func__, ctsn,
+                        asoc->ctsn_ack_point);
+
                return SCTP_DISPOSITION_DISCARD;
        }
 
@@ -2721,8 +2724,9 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net,
        ctsn = ntohl(sdh->cum_tsn_ack);
 
        if (TSN_lt(ctsn, asoc->ctsn_ack_point)) {
-               SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn);
-               SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point);
+               pr_debug("%s: ctsn:%x, ctsn_ack_point:%x\n", __func__, ctsn,
+                        asoc->ctsn_ack_point);
+
                return SCTP_DISPOSITION_DISCARD;
        }
 
@@ -3174,8 +3178,9 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(struct net *net,
         *     Point indicates an out-of-order SACK.
         */
        if (TSN_lt(ctsn, asoc->ctsn_ack_point)) {
-               SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn);
-               SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point);
+               pr_debug("%s: ctsn:%x, ctsn_ack_point:%x\n", __func__, ctsn,
+                        asoc->ctsn_ack_point);
+
                return SCTP_DISPOSITION_DISCARD;
        }
 
@@ -3859,7 +3864,7 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn(struct net *net,
        skb_pull(chunk->skb, len);
 
        tsn = ntohl(fwdtsn_hdr->new_cum_tsn);
-       SCTP_DEBUG_PRINTK("%s: TSN 0x%x.\n", __func__, tsn);
+       pr_debug("%s: TSN 0x%x\n", __func__, tsn);
 
        /* The TSN is too high--silently discard the chunk and count on it
         * getting retransmitted later.
@@ -3927,7 +3932,7 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn_fast(
        skb_pull(chunk->skb, len);
 
        tsn = ntohl(fwdtsn_hdr->new_cum_tsn);
-       SCTP_DEBUG_PRINTK("%s: TSN 0x%x.\n", __func__, tsn);
+       pr_debug("%s: TSN 0x%x\n", __func__, tsn);
 
        /* The TSN is too high--silently discard the chunk and count on it
         * getting retransmitted later.
@@ -4166,7 +4171,7 @@ sctp_disposition_t sctp_sf_unk_chunk(struct net *net,
        struct sctp_chunk *err_chunk;
        sctp_chunkhdr_t *hdr;
 
-       SCTP_DEBUG_PRINTK("Processing the unknown chunk id %d.\n", type.chunk);
+       pr_debug("%s: processing unknown chunk id:%d\n", __func__, type.chunk);
 
        if (!sctp_vtag_verify(unk_chunk, asoc))
                return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
@@ -4256,7 +4261,8 @@ sctp_disposition_t sctp_sf_discard_chunk(struct net *net,
                return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
-       SCTP_DEBUG_PRINTK("Chunk %d is discarded\n", type.chunk);
+       pr_debug("%s: chunk:%d is discarded\n", __func__, type.chunk);
+
        return SCTP_DISPOSITION_DISCARD;
 }
 
@@ -4632,16 +4638,16 @@ sctp_disposition_t sctp_sf_do_prm_asoc(struct net *net,
        if (!repl)
                goto nomem;
 
+       /* Choose transport for INIT. */
+       sctp_add_cmd_sf(commands, SCTP_CMD_INIT_CHOOSE_TRANSPORT,
+                       SCTP_CHUNK(repl));
+
        /* Cast away the const modifier, as we want to just
         * rerun it through as a sideffect.
         */
        my_asoc = (struct sctp_association *)asoc;
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(my_asoc));
 
-       /* Choose transport for INIT. */
-       sctp_add_cmd_sf(commands, SCTP_CMD_INIT_CHOOSE_TRANSPORT,
-                       SCTP_CHUNK(repl));
-
        /* After sending the INIT, "A" starts the T1-init timer and
         * enters the COOKIE-WAIT state.
         */
@@ -5184,7 +5190,9 @@ sctp_disposition_t sctp_sf_ignore_primitive(
        void *arg,
        sctp_cmd_seq_t *commands)
 {
-       SCTP_DEBUG_PRINTK("Primitive type %d is ignored.\n", type.primitive);
+       pr_debug("%s: primitive type:%d is ignored\n", __func__,
+                type.primitive);
+
        return SCTP_DISPOSITION_DISCARD;
 }
 
@@ -5379,7 +5387,9 @@ sctp_disposition_t sctp_sf_ignore_other(struct net *net,
                                        void *arg,
                                        sctp_cmd_seq_t *commands)
 {
-       SCTP_DEBUG_PRINTK("The event other type %d is ignored\n", type.other);
+       pr_debug("%s: the event other type:%d is ignored\n",
+                __func__, type.other);
+
        return SCTP_DISPOSITION_DISCARD;
 }
 
@@ -5527,7 +5537,8 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(struct net *net,
        struct sctp_bind_addr *bp;
        int attempts = asoc->init_err_counter + 1;
 
-       SCTP_DEBUG_PRINTK("Timer T1 expired (INIT).\n");
+       pr_debug("%s: timer T1 expired (INIT)\n", __func__);
+
        SCTP_INC_STATS(net, SCTP_MIB_T1_INIT_EXPIREDS);
 
        if (attempts <= asoc->max_init_attempts) {
@@ -5546,9 +5557,10 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(struct net *net,
 
                sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
        } else {
-               SCTP_DEBUG_PRINTK("Giving up on INIT, attempts: %d"
-                                 " max_init_attempts: %d\n",
-                                 attempts, asoc->max_init_attempts);
+               pr_debug("%s: giving up on INIT, attempts:%d "
+                        "max_init_attempts:%d\n", __func__, attempts,
+                        asoc->max_init_attempts);
+
                sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
                                SCTP_ERROR(ETIMEDOUT));
                sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
@@ -5588,7 +5600,8 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(struct net *net,
        struct sctp_chunk *repl = NULL;
        int attempts = asoc->init_err_counter + 1;
 
-       SCTP_DEBUG_PRINTK("Timer T1 expired (COOKIE-ECHO).\n");
+       pr_debug("%s: timer T1 expired (COOKIE-ECHO)\n", __func__);
+
        SCTP_INC_STATS(net, SCTP_MIB_T1_COOKIE_EXPIREDS);
 
        if (attempts <= asoc->max_init_attempts) {
@@ -5636,7 +5649,8 @@ sctp_disposition_t sctp_sf_t2_timer_expire(struct net *net,
 {
        struct sctp_chunk *reply = NULL;
 
-       SCTP_DEBUG_PRINTK("Timer T2 expired.\n");
+       pr_debug("%s: timer T2 expired\n", __func__);
+
        SCTP_INC_STATS(net, SCTP_MIB_T2_SHUTDOWN_EXPIREDS);
 
        ((struct sctp_association *)asoc)->shutdown_retries++;
@@ -5777,7 +5791,8 @@ sctp_disposition_t sctp_sf_t5_timer_expire(struct net *net,
 {
        struct sctp_chunk *reply = NULL;
 
-       SCTP_DEBUG_PRINTK("Timer T5 expired.\n");
+       pr_debug("%s: timer T5 expired\n", __func__);
+
        SCTP_INC_STATS(net, SCTP_MIB_T5_SHUTDOWN_GUARD_EXPIREDS);
 
        reply = sctp_make_abort(asoc, NULL, 0);
@@ -5892,7 +5907,8 @@ sctp_disposition_t sctp_sf_timer_ignore(struct net *net,
                                        void *arg,
                                        sctp_cmd_seq_t *commands)
 {
-       SCTP_DEBUG_PRINTK("Timer %d ignored.\n", type.chunk);
+       pr_debug("%s: timer %d ignored\n", __func__, type.chunk);
+
        return SCTP_DISPOSITION_CONSUME;
 }
 
@@ -6102,7 +6118,7 @@ static int sctp_eat_data(const struct sctp_association *asoc,
        skb_pull(chunk->skb, sizeof(sctp_datahdr_t));
 
        tsn = ntohl(data_hdr->tsn);
-       SCTP_DEBUG_PRINTK("eat_data: TSN 0x%x.\n", tsn);
+       pr_debug("%s: TSN 0x%x\n", __func__, tsn);
 
        /* ASSERT:  Now skb->data is really the user data.  */
 
@@ -6179,12 +6195,12 @@ static int sctp_eat_data(const struct sctp_association *asoc,
                 */
                if (sctp_tsnmap_has_gap(map) &&
                    (sctp_tsnmap_get_ctsn(map) + 1) == tsn) {
-                       SCTP_DEBUG_PRINTK("Reneging for tsn:%u\n", tsn);
+                       pr_debug("%s: reneging for tsn:%u\n", __func__, tsn);
                        deliver = SCTP_CMD_RENEGE;
                } else {
-                       SCTP_DEBUG_PRINTK("Discard tsn: %u len: %Zd, "
-                                         "rwnd: %d\n", tsn, datalen,
-                                         asoc->rwnd);
+                       pr_debug("%s: discard tsn:%u len:%zu, rwnd:%d\n",
+                                __func__, tsn, datalen, asoc->rwnd);
+
                        return SCTP_IERROR_IGNORE_TSN;
                }
        }
@@ -6199,7 +6215,8 @@ static int sctp_eat_data(const struct sctp_association *asoc,
        if (*sk->sk_prot_creator->memory_pressure) {
                if (sctp_tsnmap_has_gap(map) &&
                   (sctp_tsnmap_get_ctsn(map) + 1) == tsn) {
-                       SCTP_DEBUG_PRINTK("Under Pressure! Reneging for tsn:%u\n", tsn);
+                       pr_debug("%s: under pressure, reneging for tsn:%u\n",
+                                __func__, tsn);
                        deliver = SCTP_CMD_RENEGE;
                 }
        }
index 6abb1ca..c6670d2 100644 (file)
 #include <net/sctp/sctp.h>
 #include <net/sctp/sm.h>
 
-/* WARNING:  Please do not remove the SCTP_STATIC attribute to
- * any of the functions below as they are used to export functions
- * used by a project regression testsuite.
- */
-
 /* Forward declarations for internal helper functions. */
 static int sctp_writeable(struct sock *sk);
 static void sctp_wfree(struct sk_buff *skb);
@@ -98,6 +93,7 @@ static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p);
 static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
 static int sctp_wait_for_accept(struct sock *sk, long timeo);
 static void sctp_wait_for_close(struct sock *sk, long timeo);
+static void sctp_destruct_sock(struct sock *sk);
 static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
                                        union sctp_addr *addr, int len);
 static int sctp_bindx_add(struct sock *, struct sockaddr *, int);
@@ -279,14 +275,14 @@ static struct sctp_transport *sctp_addr_id2transport(struct sock *sk,
  *             sockaddr_in6 [RFC 2553]),
  *   addr_len - the size of the address structure.
  */
-SCTP_STATIC int sctp_bind(struct sock *sk, struct sockaddr *addr, int addr_len)
+static int sctp_bind(struct sock *sk, struct sockaddr *addr, int addr_len)
 {
        int retval = 0;
 
        sctp_lock_sock(sk);
 
-       SCTP_DEBUG_PRINTK("sctp_bind(sk: %p, addr: %p, addr_len: %d)\n",
-                         sk, addr, addr_len);
+       pr_debug("%s: sk:%p, addr:%p, addr_len:%d\n", __func__, sk,
+                addr, addr_len);
 
        /* Disallow binding twice. */
        if (!sctp_sk(sk)->ep->base.bind_addr.port)
@@ -333,7 +329,7 @@ static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
 }
 
 /* Bind a local address either to an endpoint or to an association.  */
-SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
+static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
 {
        struct net *net = sock_net(sk);
        struct sctp_sock *sp = sctp_sk(sk);
@@ -346,19 +342,15 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
        /* Common sockaddr verification. */
        af = sctp_sockaddr_af(sp, addr, len);
        if (!af) {
-               SCTP_DEBUG_PRINTK("sctp_do_bind(sk: %p, newaddr: %p, len: %d) EINVAL\n",
-                                 sk, addr, len);
+               pr_debug("%s: sk:%p, newaddr:%p, len:%d EINVAL\n",
+                        __func__, sk, addr, len);
                return -EINVAL;
        }
 
        snum = ntohs(addr->v4.sin_port);
 
-       SCTP_DEBUG_PRINTK_IPADDR("sctp_do_bind(sk: %p, new addr: ",
-                                ", port: %d, new port: %d, len: %d)\n",
-                                sk,
-                                addr,
-                                bp->port, snum,
-                                len);
+       pr_debug("%s: sk:%p, new addr:%pISc, port:%d, new port:%d, len:%d\n",
+                __func__, sk, &addr->sa, bp->port, snum, len);
 
        /* PF specific bind() address verification. */
        if (!sp->pf->bind_verify(sp, addr))
@@ -372,9 +364,8 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
                if (!snum)
                        snum = bp->port;
                else if (snum != bp->port) {
-                       SCTP_DEBUG_PRINTK("sctp_do_bind:"
-                                 " New port %d does not match existing port "
-                                 "%d.\n", snum, bp->port);
+                       pr_debug("%s: new port %d doesn't match existing port "
+                                "%d\n", __func__, snum, bp->port);
                        return -EINVAL;
                }
        }
@@ -472,8 +463,8 @@ static int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
        struct sockaddr *sa_addr;
        struct sctp_af *af;
 
-       SCTP_DEBUG_PRINTK("sctp_bindx_add (sk: %p, addrs: %p, addrcnt: %d)\n",
-                         sk, addrs, addrcnt);
+       pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n", __func__, sk,
+                addrs, addrcnt);
 
        addr_buf = addrs;
        for (cnt = 0; cnt < addrcnt; cnt++) {
@@ -539,11 +530,10 @@ static int sctp_send_asconf_add_ip(struct sock            *sk,
        sp = sctp_sk(sk);
        ep = sp->ep;
 
-       SCTP_DEBUG_PRINTK("%s: (sk: %p, addrs: %p, addrcnt: %d)\n",
-                         __func__, sk, addrs, addrcnt);
+       pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
+                __func__, sk, addrs, addrcnt);
 
        list_for_each_entry(asoc, &ep->asocs, asocs) {
-
                if (!asoc->peer.asconf_capable)
                        continue;
 
@@ -650,8 +640,8 @@ static int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
        union sctp_addr *sa_addr;
        struct sctp_af *af;
 
-       SCTP_DEBUG_PRINTK("sctp_bindx_rem (sk: %p, addrs: %p, addrcnt: %d)\n",
-                         sk, addrs, addrcnt);
+       pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
+                __func__, sk, addrs, addrcnt);
 
        addr_buf = addrs;
        for (cnt = 0; cnt < addrcnt; cnt++) {
@@ -744,8 +734,8 @@ static int sctp_send_asconf_del_ip(struct sock              *sk,
        sp = sctp_sk(sk);
        ep = sp->ep;
 
-       SCTP_DEBUG_PRINTK("%s: (sk: %p, addrs: %p, addrcnt: %d)\n",
-                         __func__, sk, addrs, addrcnt);
+       pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
+                __func__, sk, addrs, addrcnt);
 
        list_for_each_entry(asoc, &ep->asocs, asocs) {
 
@@ -812,9 +802,11 @@ static int sctp_send_asconf_del_ip(struct sock             *sk,
                                sin6 = (struct sockaddr_in6 *)addrs;
                                asoc->asconf_addr_del_pending->v6.sin6_addr = sin6->sin6_addr;
                        }
-                       SCTP_DEBUG_PRINTK_IPADDR("send_asconf_del_ip: keep the last address asoc: %p ",
-                           " at %p\n", asoc, asoc->asconf_addr_del_pending,
-                           asoc->asconf_addr_del_pending);
+
+                       pr_debug("%s: keep the last address asoc:%p %pISc at %p\n",
+                                __func__, asoc, &asoc->asconf_addr_del_pending->sa,
+                                asoc->asconf_addr_del_pending);
+
                        asoc->src_out_of_asoc_ok = 1;
                        stored = 1;
                        goto skip_mkasconf;
@@ -964,9 +956,9 @@ int sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw)
  *
  * Returns 0 if ok, <0 errno code on error.
  */
-SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
-                                     struct sockaddr __user *addrs,
-                                     int addrs_size, int op)
+static int sctp_setsockopt_bindx(struct sock* sk,
+                                struct sockaddr __user *addrs,
+                                int addrs_size, int op)
 {
        struct sockaddr *kaddrs;
        int err;
@@ -976,8 +968,8 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
        void *addr_buf;
        struct sctp_af *af;
 
-       SCTP_DEBUG_PRINTK("sctp_setsockopt_bindx: sk %p addrs %p"
-                         " addrs_size %d opt %d\n", sk, addrs, addrs_size, op);
+       pr_debug("%s: sk:%p addrs:%p addrs_size:%d opt:%d\n",
+                __func__, sk, addrs, addrs_size, op);
 
        if (unlikely(addrs_size <= 0))
                return -EINVAL;
@@ -1235,10 +1227,9 @@ static int __sctp_connect(struct sock* sk,
        asoc = NULL;
 
 out_free:
+       pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n",
+                __func__, asoc, kaddrs, err);
 
-       SCTP_DEBUG_PRINTK("About to exit __sctp_connect() free asoc: %p"
-                         " kaddrs: %p err: %d\n",
-                         asoc, kaddrs, err);
        if (asoc) {
                /* sctp_primitive_ASSOCIATE may have added this association
                 * To the hash table, try to unhash it, just in case, its a noop
@@ -1312,7 +1303,7 @@ out_free:
  *
  * Returns >=0 if ok, <0 errno code on error.
  */
-SCTP_STATIC int __sctp_setsockopt_connectx(struct sock* sk,
+static int __sctp_setsockopt_connectx(struct sock* sk,
                                      struct sockaddr __user *addrs,
                                      int addrs_size,
                                      sctp_assoc_t *assoc_id)
@@ -1320,8 +1311,8 @@ SCTP_STATIC int __sctp_setsockopt_connectx(struct sock* sk,
        int err = 0;
        struct sockaddr *kaddrs;
 
-       SCTP_DEBUG_PRINTK("%s - sk %p addrs %p addrs_size %d\n",
-                         __func__, sk, addrs, addrs_size);
+       pr_debug("%s: sk:%p addrs:%p addrs_size:%d\n",
+                __func__, sk, addrs, addrs_size);
 
        if (unlikely(addrs_size <= 0))
                return -EINVAL;
@@ -1350,9 +1341,9 @@ SCTP_STATIC int __sctp_setsockopt_connectx(struct sock* sk,
  * This is an older interface.  It's kept for backward compatibility
  * to the option that doesn't provide association id.
  */
-SCTP_STATIC int sctp_setsockopt_connectx_old(struct sock* sk,
-                                     struct sockaddr __user *addrs,
-                                     int addrs_size)
+static int sctp_setsockopt_connectx_old(struct sock* sk,
+                                       struct sockaddr __user *addrs,
+                                       int addrs_size)
 {
        return __sctp_setsockopt_connectx(sk, addrs, addrs_size, NULL);
 }
@@ -1363,9 +1354,9 @@ SCTP_STATIC int sctp_setsockopt_connectx_old(struct sock* sk,
  * indication to the call.  Error is always negative and association id is
  * always positive.
  */
-SCTP_STATIC int sctp_setsockopt_connectx(struct sock* sk,
-                                     struct sockaddr __user *addrs,
-                                     int addrs_size)
+static int sctp_setsockopt_connectx(struct sock* sk,
+                                   struct sockaddr __user *addrs,
+                                   int addrs_size)
 {
        sctp_assoc_t assoc_id = 0;
        int err = 0;
@@ -1386,9 +1377,9 @@ SCTP_STATIC int sctp_setsockopt_connectx(struct sock* sk,
  * addrs_num structure member.  That way we can re-use the existing
  * code.
  */
-SCTP_STATIC int sctp_getsockopt_connectx3(struct sock* sk, int len,
-                                       char __user *optval,
-                                       int __user *optlen)
+static int sctp_getsockopt_connectx3(struct sock* sk, int len,
+                                    char __user *optval,
+                                    int __user *optlen)
 {
        struct sctp_getaddrs_old param;
        sctp_assoc_t assoc_id = 0;
@@ -1464,7 +1455,7 @@ SCTP_STATIC int sctp_getsockopt_connectx3(struct sock* sk, int len,
  * shutdown phase does not finish during this period, close() will
  * return but the graceful shutdown phase continues in the system.
  */
-SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
+static void sctp_close(struct sock *sk, long timeout)
 {
        struct net *net = sock_net(sk);
        struct sctp_endpoint *ep;
@@ -1472,7 +1463,7 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
        struct list_head *pos, *temp;
        unsigned int data_was_unread;
 
-       SCTP_DEBUG_PRINTK("sctp_close(sk: 0x%p, timeout:%ld)\n", sk, timeout);
+       pr_debug("%s: sk:%p, timeout:%ld\n", __func__, sk, timeout);
 
        sctp_lock_sock(sk);
        sk->sk_shutdown = SHUTDOWN_MASK;
@@ -1573,10 +1564,10 @@ static int sctp_error(struct sock *sk, int flags, int err)
  */
 /* BUG:  We do not implement the equivalent of sk_stream_wait_memory(). */
 
-SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
+static int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
 
-SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
-                            struct msghdr *msg, size_t msg_len)
+static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
+                       struct msghdr *msg, size_t msg_len)
 {
        struct net *net = sock_net(sk);
        struct sctp_sock *sp;
@@ -1598,14 +1589,12 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
        struct sctp_datamsg *datamsg;
        int msg_flags = msg->msg_flags;
 
-       SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %zu)\n",
-                         sk, msg, msg_len);
-
        err = 0;
        sp = sctp_sk(sk);
        ep = sp->ep;
 
-       SCTP_DEBUG_PRINTK("Using endpoint: %p.\n", ep);
+       pr_debug("%s: sk:%p, msg:%p, msg_len:%zu ep:%p\n", __func__, sk,
+                msg, msg_len, ep);
 
        /* We cannot send a message over a TCP-style listening socket. */
        if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING)) {
@@ -1615,9 +1604,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
 
        /* Parse out the SCTP CMSGs.  */
        err = sctp_msghdr_parse(msg, &cmsgs);
-
        if (err) {
-               SCTP_DEBUG_PRINTK("msghdr parse err = %x\n", err);
+               pr_debug("%s: msghdr parse err:%x\n", __func__, err);
                goto out_nounlock;
        }
 
@@ -1649,8 +1637,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                associd = sinfo->sinfo_assoc_id;
        }
 
-       SCTP_DEBUG_PRINTK("msg_len: %zu, sinfo_flags: 0x%x\n",
-                         msg_len, sinfo_flags);
+       pr_debug("%s: msg_len:%zu, sinfo_flags:0x%x\n", __func__,
+                msg_len, sinfo_flags);
 
        /* SCTP_EOF or SCTP_ABORT cannot be set on a TCP-style socket. */
        if (sctp_style(sk, TCP) && (sinfo_flags & (SCTP_EOF | SCTP_ABORT))) {
@@ -1679,7 +1667,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
 
        transport = NULL;
 
-       SCTP_DEBUG_PRINTK("About to look up association.\n");
+       pr_debug("%s: about to look up association\n", __func__);
 
        sctp_lock_sock(sk);
 
@@ -1709,7 +1697,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
        }
 
        if (asoc) {
-               SCTP_DEBUG_PRINTK("Just looked up association: %p.\n", asoc);
+               pr_debug("%s: just looked up association:%p\n", __func__, asoc);
 
                /* We cannot send a message on a TCP-style SCTP_SS_ESTABLISHED
                 * socket that has an association in CLOSED state. This can
@@ -1722,8 +1710,9 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                }
 
                if (sinfo_flags & SCTP_EOF) {
-                       SCTP_DEBUG_PRINTK("Shutting down association: %p\n",
-                                         asoc);
+                       pr_debug("%s: shutting down association:%p\n",
+                                __func__, asoc);
+
                        sctp_primitive_SHUTDOWN(net, asoc, NULL);
                        err = 0;
                        goto out_unlock;
@@ -1736,7 +1725,9 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                                goto out_unlock;
                        }
 
-                       SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc);
+                       pr_debug("%s: aborting association:%p\n",
+                                __func__, asoc);
+
                        sctp_primitive_ABORT(net, asoc, chunk);
                        err = 0;
                        goto out_unlock;
@@ -1745,7 +1736,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
 
        /* Do we need to create the association?  */
        if (!asoc) {
-               SCTP_DEBUG_PRINTK("There is no association yet.\n");
+               pr_debug("%s: there is no association yet\n", __func__);
 
                if (sinfo_flags & (SCTP_EOF | SCTP_ABORT)) {
                        err = -EINVAL;
@@ -1844,7 +1835,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
        }
 
        /* ASSERT: we have a valid association at this point.  */
-       SCTP_DEBUG_PRINTK("We have a valid association.\n");
+       pr_debug("%s: we have a valid association\n", __func__);
 
        if (!sinfo) {
                /* If the user didn't specify SNDRCVINFO, make up one with
@@ -1913,7 +1904,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                err = sctp_primitive_ASSOCIATE(net, asoc, NULL);
                if (err < 0)
                        goto out_free;
-               SCTP_DEBUG_PRINTK("We associated primitively.\n");
+
+               pr_debug("%s: we associated primitively\n", __func__);
        }
 
        /* Break the message into multiple chunks of maximum size. */
@@ -1940,17 +1932,15 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
         */
        err = sctp_primitive_SEND(net, asoc, datamsg);
        /* Did the lower layer accept the chunk? */
-       if (err)
+       if (err) {
                sctp_datamsg_free(datamsg);
-       else
-               sctp_datamsg_put(datamsg);
+               goto out_free;
+       }
 
-       SCTP_DEBUG_PRINTK("We sent primitively.\n");
+       pr_debug("%s: we sent primitively\n", __func__);
 
-       if (err)
-               goto out_free;
-       else
-               err = msg_len;
+       sctp_datamsg_put(datamsg);
+       err = msg_len;
 
        /* If we are already past ASSOCIATE, the lower
         * layers are responsible for association cleanup.
@@ -2034,9 +2024,9 @@ static int sctp_skb_pull(struct sk_buff *skb, int len)
  */
 static struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *);
 
-SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
-                            struct msghdr *msg, size_t len, int noblock,
-                            int flags, int *addr_len)
+static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
+                       struct msghdr *msg, size_t len, int noblock,
+                       int flags, int *addr_len)
 {
        struct sctp_ulpevent *event = NULL;
        struct sctp_sock *sp = sctp_sk(sk);
@@ -2045,10 +2035,9 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
        int err = 0;
        int skb_len;
 
-       SCTP_DEBUG_PRINTK("sctp_recvmsg(%s: %p, %s: %p, %s: %zd, %s: %d, %s: "
-                         "0x%x, %s: %p)\n", "sk", sk, "msghdr", msg,
-                         "len", len, "knoblauch", noblock,
-                         "flags", flags, "addr_len", addr_len);
+       pr_debug("%s: sk:%p, msghdr:%p, len:%zd, noblock:%d, flags:0x%x, "
+                "addr_len:%p)\n", __func__, sk, msg, len, noblock, flags,
+                addr_len);
 
        sctp_lock_sock(sk);
 
@@ -2915,13 +2904,8 @@ static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, unsig
                        asoc->max_retrans = assocparams.sasoc_asocmaxrxt;
                }
 
-               if (assocparams.sasoc_cookie_life != 0) {
-                       asoc->cookie_life.tv_sec =
-                                       assocparams.sasoc_cookie_life / 1000;
-                       asoc->cookie_life.tv_usec =
-                                       (assocparams.sasoc_cookie_life % 1000)
-                                       * 1000;
-               }
+               if (assocparams.sasoc_cookie_life != 0)
+                       asoc->cookie_life = ms_to_ktime(assocparams.sasoc_cookie_life);
        } else {
                /* Set the values to the endpoint */
                struct sctp_sock *sp = sctp_sk(sk);
@@ -3095,7 +3079,7 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva
 
        err = sctp_send_asconf(asoc, chunk);
 
-       SCTP_DEBUG_PRINTK("We set peer primary addr primitively.\n");
+       pr_debug("%s: we set peer primary addr primitively\n", __func__);
 
        return err;
 }
@@ -3565,13 +3549,12 @@ static int sctp_setsockopt_paddr_thresholds(struct sock *sk,
  *   optval  - the buffer to store the value of the option.
  *   optlen  - the size of the buffer.
  */
-SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
-                               char __user *optval, unsigned int optlen)
+static int sctp_setsockopt(struct sock *sk, int level, int optname,
+                          char __user *optval, unsigned int optlen)
 {
        int retval = 0;
 
-       SCTP_DEBUG_PRINTK("sctp_setsockopt(sk: %p... optname: %d)\n",
-                         sk, optname);
+       pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname);
 
        /* I can hardly begin to describe how wrong this is.  This is
         * so broken as to be worse than useless.  The API draft
@@ -3725,16 +3708,16 @@ out_nounlock:
  *
  * len: the size of the address.
  */
-SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *addr,
-                            int addr_len)
+static int sctp_connect(struct sock *sk, struct sockaddr *addr,
+                       int addr_len)
 {
        int err = 0;
        struct sctp_af *af;
 
        sctp_lock_sock(sk);
 
-       SCTP_DEBUG_PRINTK("%s - sk: %p, sockaddr: %p, addr_len: %d\n",
-                         __func__, sk, addr, addr_len);
+       pr_debug("%s: sk:%p, sockaddr:%p, addr_len:%d\n", __func__, sk,
+                addr, addr_len);
 
        /* Validate addr_len before calling common connect/connectx routine. */
        af = sctp_get_af_specific(addr->sa_family);
@@ -3752,7 +3735,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *addr,
 }
 
 /* FIXME: Write comments. */
-SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags)
+static int sctp_disconnect(struct sock *sk, int flags)
 {
        return -EOPNOTSUPP; /* STUB */
 }
@@ -3764,7 +3747,7 @@ SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags)
  * descriptor will be returned from accept() to represent the newly
  * formed association.
  */
-SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err)
+static struct sock *sctp_accept(struct sock *sk, int flags, int *err)
 {
        struct sctp_sock *sp;
        struct sctp_endpoint *ep;
@@ -3817,7 +3800,7 @@ out:
 }
 
 /* The SCTP ioctl handler. */
-SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
        int rc = -ENOTCONN;
 
@@ -3859,13 +3842,12 @@ out:
  * initialized the SCTP-specific portion of the sock.
  * The sock structure should already be zero-filled memory.
  */
-SCTP_STATIC int sctp_init_sock(struct sock *sk)
+static int sctp_init_sock(struct sock *sk)
 {
        struct net *net = sock_net(sk);
-       struct sctp_endpoint *ep;
        struct sctp_sock *sp;
 
-       SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk);
+       pr_debug("%s: sk:%p\n", __func__, sk);
 
        sp = sctp_sk(sk);
 
@@ -3971,13 +3953,14 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
         * change the data structure relationships, this may still
         * be useful for storing pre-connect address information.
         */
-       ep = sctp_endpoint_new(sk, GFP_KERNEL);
-       if (!ep)
+       sp->ep = sctp_endpoint_new(sk, GFP_KERNEL);
+       if (!sp->ep)
                return -ENOMEM;
 
-       sp->ep = ep;
        sp->hmac = NULL;
 
+       sk->sk_destruct = sctp_destruct_sock;
+
        SCTP_DBG_OBJCNT_INC(sock);
 
        local_bh_disable();
@@ -3995,11 +3978,11 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
 }
 
 /* Cleanup any SCTP per socket resources.  */
-SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
+static void sctp_destroy_sock(struct sock *sk)
 {
        struct sctp_sock *sp;
 
-       SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk);
+       pr_debug("%s: sk:%p\n", __func__, sk);
 
        /* Release our hold on the endpoint. */
        sp = sctp_sk(sk);
@@ -4020,6 +4003,17 @@ SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
        local_bh_enable();
 }
 
+/* Triggered when there are no references on the socket anymore */
+static void sctp_destruct_sock(struct sock *sk)
+{
+       struct sctp_sock *sp = sctp_sk(sk);
+
+       /* Free up the HMAC transform. */
+       crypto_free_hash(sp->hmac);
+
+       inet_sock_destruct(sk);
+}
+
 /* API 4.1.7 shutdown() - TCP Style Syntax
  *     int shutdown(int socket, int how);
  *
@@ -4036,7 +4030,7 @@ SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
  *                     Disables further send  and  receive  operations
  *                     and initiates the SCTP shutdown sequence.
  */
-SCTP_STATIC void sctp_shutdown(struct sock *sk, int how)
+static void sctp_shutdown(struct sock *sk, int how)
 {
        struct net *net = sock_net(sk);
        struct sctp_endpoint *ep;
@@ -4121,9 +4115,9 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len,
                goto out;
        }
 
-       SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %d\n",
-                         len, status.sstat_state, status.sstat_rwnd,
-                         status.sstat_assoc_id);
+       pr_debug("%s: len:%d, state:%d, rwnd:%d, assoc_id:%d\n",
+                __func__, len, status.sstat_state, status.sstat_rwnd,
+                status.sstat_assoc_id);
 
        if (copy_to_user(optval, &status, len)) {
                retval = -EFAULT;
@@ -4318,7 +4312,7 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
                goto out;
 
        /* Map the socket to an unused fd that can be returned to the user.  */
-       retval = get_unused_fd();
+       retval = get_unused_fd_flags(0);
        if (retval < 0) {
                sock_release(newsock);
                goto out;
@@ -4331,8 +4325,8 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
                return PTR_ERR(newfile);
        }
 
-       SCTP_DEBUG_PRINTK("%s: sk: %p newsk: %p sd: %d\n",
-                         __func__, sk, newsock->sk, retval);
+       pr_debug("%s: sk:%p, newsk:%p, sd:%d\n", __func__, sk, newsock->sk,
+                retval);
 
        /* Return the fd mapped to the new socket.  */
        if (put_user(len, optlen)) {
@@ -4465,7 +4459,7 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
                trans = sctp_addr_id2transport(sk, &params.spp_address,
                                               params.spp_assoc_id);
                if (!trans) {
-                       SCTP_DEBUG_PRINTK("Failed no transport\n");
+                       pr_debug("%s: failed no transport\n", __func__);
                        return -EINVAL;
                }
        }
@@ -4476,7 +4470,7 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
         */
        asoc = sctp_id2assoc(sk, params.spp_assoc_id);
        if (!asoc && params.spp_assoc_id && sctp_style(sk, UDP)) {
-               SCTP_DEBUG_PRINTK("Failed no association\n");
+               pr_debug("%s: failed no association\n", __func__);
                return -EINVAL;
        }
 
@@ -5081,10 +5075,7 @@ static int sctp_getsockopt_associnfo(struct sock *sk, int len,
                assocparams.sasoc_asocmaxrxt = asoc->max_retrans;
                assocparams.sasoc_peer_rwnd = asoc->peer.rwnd;
                assocparams.sasoc_local_rwnd = asoc->a_rwnd;
-               assocparams.sasoc_cookie_life = (asoc->cookie_life.tv_sec
-                                               * 1000) +
-                                               (asoc->cookie_life.tv_usec
-                                               / 1000);
+               assocparams.sasoc_cookie_life = ktime_to_ms(asoc->cookie_life);
 
                list_for_each(pos, &asoc->peer.transport_addr_list) {
                        cnt ++;
@@ -5699,8 +5690,7 @@ static int sctp_getsockopt_assoc_stats(struct sock *sk, int len,
        if (put_user(len, optlen))
                return -EFAULT;
 
-       SCTP_DEBUG_PRINTK("sctp_getsockopt_assoc_stat(%d): %d\n",
-                         len, sas.sas_assoc_id);
+       pr_debug("%s: len:%d, assoc_id:%d\n", __func__, len, sas.sas_assoc_id);
 
        if (copy_to_user(optval, &sas, len))
                return -EFAULT;
@@ -5708,14 +5698,13 @@ static int sctp_getsockopt_assoc_stats(struct sock *sk, int len,
        return 0;
 }
 
-SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
-                               char __user *optval, int __user *optlen)
+static int sctp_getsockopt(struct sock *sk, int level, int optname,
+                          char __user *optval, int __user *optlen)
 {
        int retval = 0;
        int len;
 
-       SCTP_DEBUG_PRINTK("sctp_getsockopt(sk: %p... optname: %d)\n",
-                         sk, optname);
+       pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname);
 
        /* I can hardly begin to describe how wrong this is.  This is
         * so broken as to be worse than useless.  The API draft
@@ -5895,7 +5884,8 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
 
        snum = ntohs(addr->v4.sin_port);
 
-       SCTP_DEBUG_PRINTK("sctp_get_port() begins, snum=%d\n", snum);
+       pr_debug("%s: begins, snum:%d\n", __func__, snum);
+
        sctp_local_bh_disable();
 
        if (snum == 0) {
@@ -5961,7 +5951,8 @@ pp_found:
                int reuse = sk->sk_reuse;
                struct sock *sk2;
 
-               SCTP_DEBUG_PRINTK("sctp_get_port() found a possible match\n");
+               pr_debug("%s: found a possible match\n", __func__);
+
                if (pp->fastreuse && sk->sk_reuse &&
                        sk->sk_state != SCTP_SS_LISTENING)
                        goto success;
@@ -5991,7 +5982,8 @@ pp_found:
                                goto fail_unlock;
                        }
                }
-               SCTP_DEBUG_PRINTK("sctp_get_port(): Found a match\n");
+
+               pr_debug("%s: found a match\n", __func__);
        }
 pp_not_found:
        /* If there was a hash table miss, create a new port.  */
@@ -6037,7 +6029,6 @@ fail:
  */
 static int sctp_get_port(struct sock *sk, unsigned short snum)
 {
-       long ret;
        union sctp_addr addr;
        struct sctp_af *af = sctp_sk(sk)->pf->af;
 
@@ -6046,15 +6037,13 @@ static int sctp_get_port(struct sock *sk, unsigned short snum)
        addr.v4.sin_port = htons(snum);
 
        /* Note: sk->sk_num gets filled in if ephemeral port request. */
-       ret = sctp_get_port_local(sk, &addr);
-
-       return ret ? 1 : 0;
+       return !!sctp_get_port_local(sk, &addr);
 }
 
 /*
  *  Move a socket to LISTENING state.
  */
-SCTP_STATIC int sctp_listen_start(struct sock *sk, int backlog)
+static int sctp_listen_start(struct sock *sk, int backlog)
 {
        struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_endpoint *ep = sp->ep;
@@ -6341,8 +6330,7 @@ static int sctp_autobind(struct sock *sk)
  * msg_control
  * points here
  */
-SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
-                                 sctp_cmsgs_t *cmsgs)
+static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
 {
        struct cmsghdr *cmsg;
        struct msghdr *my_msg = (struct msghdr *)msg;
@@ -6484,8 +6472,8 @@ static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags,
 
        timeo = sock_rcvtimeo(sk, noblock);
 
-       SCTP_DEBUG_PRINTK("Timeout: timeo: %ld, MAX: %ld.\n",
-                         timeo, MAX_SCHEDULE_TIMEOUT);
+       pr_debug("%s: timeo:%ld, max:%ld\n", __func__, timeo,
+                MAX_SCHEDULE_TIMEOUT);
 
        do {
                /* Again only user level code calls this function,
@@ -6616,8 +6604,8 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
        long current_timeo = *timeo_p;
        DEFINE_WAIT(wait);
 
-       SCTP_DEBUG_PRINTK("wait_for_sndbuf: asoc=%p, timeo=%ld, msg_len=%zu\n",
-                         asoc, (long)(*timeo_p), msg_len);
+       pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc,
+                *timeo_p, msg_len);
 
        /* Increment the association's refcnt.  */
        sctp_association_hold(asoc);
@@ -6723,8 +6711,7 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
        long current_timeo = *timeo_p;
        DEFINE_WAIT(wait);
 
-       SCTP_DEBUG_PRINTK("%s: asoc=%p, timeo=%ld\n", __func__, asoc,
-                         (long)(*timeo_p));
+       pr_debug("%s: asoc:%p, timeo:%ld\n", __func__, asoc, *timeo_p);
 
        /* Increment the association's refcnt.  */
        sctp_association_hold(asoc);
@@ -6864,7 +6851,7 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
        newsk->sk_reuse = sk->sk_reuse;
 
        newsk->sk_shutdown = sk->sk_shutdown;
-       newsk->sk_destruct = inet_sock_destruct;
+       newsk->sk_destruct = sctp_destruct_sock;
        newsk->sk_family = sk->sk_family;
        newsk->sk_protocol = IPPROTO_SCTP;
        newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
index bf3c6e8..9a5c4c9 100644 (file)
@@ -62,12 +62,12 @@ extern long sysctl_sctp_mem[3];
 extern int sysctl_sctp_rmem[3];
 extern int sysctl_sctp_wmem[3];
 
-static int proc_sctp_do_hmac_alg(ctl_table *ctl,
+static int proc_sctp_do_hmac_alg(struct ctl_table *ctl,
                                int write,
                                void __user *buffer, size_t *lenp,
 
                                loff_t *ppos);
-static ctl_table sctp_table[] = {
+static struct ctl_table sctp_table[] = {
        {
                .procname       = "sctp_mem",
                .data           = &sysctl_sctp_mem,
@@ -93,7 +93,7 @@ static ctl_table sctp_table[] = {
        { /* sentinel */ }
 };
 
-static ctl_table sctp_net_table[] = {
+static struct ctl_table sctp_net_table[] = {
        {
                .procname       = "rto_initial",
                .data           = &init_net.sctp.rto_initial,
@@ -300,14 +300,14 @@ static ctl_table sctp_net_table[] = {
        { /* sentinel */ }
 };
 
-static int proc_sctp_do_hmac_alg(ctl_table *ctl,
+static int proc_sctp_do_hmac_alg(struct ctl_table *ctl,
                                int write,
                                void __user *buffer, size_t *lenp,
                                loff_t *ppos)
 {
        struct net *net = current->nsproxy->net_ns;
        char tmp[8];
-       ctl_table tbl;
+       struct ctl_table tbl;
        int ret;
        int changed = 0;
        char *none = "none";
index 098f1d5..bdbbc3f 100644 (file)
@@ -116,7 +116,7 @@ struct sctp_transport *sctp_transport_new(struct net *net,
 {
        struct sctp_transport *transport;
 
-       transport = t_new(struct sctp_transport, gfp);
+       transport = kzalloc(sizeof(*transport), gfp);
        if (!transport)
                goto fail;
 
@@ -176,7 +176,10 @@ static void sctp_transport_destroy_rcu(struct rcu_head *head)
  */
 static void sctp_transport_destroy(struct sctp_transport *transport)
 {
-       SCTP_ASSERT(transport->dead, "Transport is not dead", return);
+       if (unlikely(!transport->dead)) {
+               WARN(1, "Attempt to destroy undead transport %p!\n", transport);
+               return;
+       }
 
        call_rcu(&transport->rcu, sctp_transport_destroy_rcu);
 
@@ -317,11 +320,9 @@ void sctp_transport_put(struct sctp_transport *transport)
 /* Update transport's RTO based on the newly calculated RTT. */
 void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
 {
-       /* Check for valid transport.  */
-       SCTP_ASSERT(tp, "NULL transport", return);
-
-       /* We should not be doing any RTO updates unless rto_pending is set.  */
-       SCTP_ASSERT(tp->rto_pending, "rto_pending not set", return);
+       if (unlikely(!tp->rto_pending))
+               /* We should not be doing any RTO updates unless rto_pending is set.  */
+               pr_debug("%s: rto_pending not set on transport %p!\n", __func__, tp);
 
        if (tp->rttvar || tp->srtt) {
                struct net *net = sock_net(tp->asoc->base.sk);
@@ -377,9 +378,8 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
         */
        tp->rto_pending = 0;
 
-       SCTP_DEBUG_PRINTK("%s: transport: %p, rtt: %d, srtt: %d "
-                         "rttvar: %d, rto: %ld\n", __func__,
-                         tp, rtt, tp->srtt, tp->rttvar, tp->rto);
+       pr_debug("%s: transport:%p, rtt:%d, srtt:%d rttvar:%d, rto:%ld\n",
+                __func__, tp, rtt, tp->srtt, tp->rttvar, tp->rto);
 }
 
 /* This routine updates the transport's cwnd and partial_bytes_acked
@@ -433,12 +433,11 @@ void sctp_transport_raise_cwnd(struct sctp_transport *transport,
                        cwnd += pmtu;
                else
                        cwnd += bytes_acked;
-               SCTP_DEBUG_PRINTK("%s: SLOW START: transport: %p, "
-                                 "bytes_acked: %d, cwnd: %d, ssthresh: %d, "
-                                 "flight_size: %d, pba: %d\n",
-                                 __func__,
-                                 transport, bytes_acked, cwnd,
-                                 ssthresh, flight_size, pba);
+
+               pr_debug("%s: slow start: transport:%p, bytes_acked:%d, "
+                        "cwnd:%d, ssthresh:%d, flight_size:%d, pba:%d\n",
+                        __func__, transport, bytes_acked, cwnd, ssthresh,
+                        flight_size, pba);
        } else {
                /* RFC 2960 7.2.2 Whenever cwnd is greater than ssthresh,
                 * upon each SACK arrival that advances the Cumulative TSN Ack
@@ -459,12 +458,12 @@ void sctp_transport_raise_cwnd(struct sctp_transport *transport,
                        cwnd += pmtu;
                        pba = ((cwnd < pba) ? (pba - cwnd) : 0);
                }
-               SCTP_DEBUG_PRINTK("%s: CONGESTION AVOIDANCE: "
-                                 "transport: %p, bytes_acked: %d, cwnd: %d, "
-                                 "ssthresh: %d, flight_size: %d, pba: %d\n",
-                                 __func__,
-                                 transport, bytes_acked, cwnd,
-                                 ssthresh, flight_size, pba);
+
+               pr_debug("%s: congestion avoidance: transport:%p, "
+                        "bytes_acked:%d, cwnd:%d, ssthresh:%d, "
+                        "flight_size:%d, pba:%d\n", __func__,
+                        transport, bytes_acked, cwnd, ssthresh,
+                        flight_size, pba);
        }
 
        transport->cwnd = cwnd;
@@ -558,10 +557,10 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport,
        }
 
        transport->partial_bytes_acked = 0;
-       SCTP_DEBUG_PRINTK("%s: transport: %p reason: %d cwnd: "
-                         "%d ssthresh: %d\n", __func__,
-                         transport, reason,
-                         transport->cwnd, transport->ssthresh);
+
+       pr_debug("%s: transport:%p, reason:%d, cwnd:%d, ssthresh:%d\n",
+                __func__, transport, reason, transport->cwnd,
+                transport->ssthresh);
 }
 
 /* Apply Max.Burst limit to the congestion window:
index 396c451..b460195 100644 (file)
@@ -161,8 +161,8 @@ int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn,
 
 
 /* Initialize a Gap Ack Block iterator from memory being provided.  */
-SCTP_STATIC void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map,
-                                      struct sctp_tsnmap_iter *iter)
+static void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map,
+                                 struct sctp_tsnmap_iter *iter)
 {
        /* Only start looking one past the Cumulative TSN Ack Point.  */
        iter->start = map->cumulative_tsn_ack_point + 1;
@@ -171,9 +171,9 @@ SCTP_STATIC void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map,
 /* Get the next Gap Ack Blocks. Returns 0 if there was not another block
  * to get.
  */
-SCTP_STATIC int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map,
-                                        struct sctp_tsnmap_iter *iter,
-                                        __u16 *start, __u16 *end)
+static int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map,
+                                   struct sctp_tsnmap_iter *iter,
+                                   __u16 *start, __u16 *end)
 {
        int ended = 0;
        __u16 start_ = 0, end_ = 0, offset;
index 10c018a..44a45db 100644 (file)
@@ -57,9 +57,9 @@ static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event);
 
 
 /* Initialize an ULP event from an given skb.  */
-SCTP_STATIC void sctp_ulpevent_init(struct sctp_ulpevent *event,
-                                   int msg_flags,
-                                   unsigned int len)
+static void sctp_ulpevent_init(struct sctp_ulpevent *event,
+                              int msg_flags,
+                              unsigned int len)
 {
        memset(event, 0, sizeof(struct sctp_ulpevent));
        event->msg_flags = msg_flags;
@@ -67,8 +67,8 @@ SCTP_STATIC void sctp_ulpevent_init(struct sctp_ulpevent *event,
 }
 
 /* Create a new sctp_ulpevent.  */
-SCTP_STATIC struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags,
-                                                   gfp_t gfp)
+static struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags,
+                                              gfp_t gfp)
 {
        struct sctp_ulpevent *event;
        struct sk_buff *skb;
index 4ca1526..45afa64 100644 (file)
 #include <linux/route.h>
 #include <linux/sockios.h>
 #include <linux/atalk.h>
+#include <net/ll_poll.h>
+
+#ifdef CONFIG_NET_LL_RX_POLL
+unsigned int sysctl_net_ll_read __read_mostly;
+unsigned int sysctl_net_ll_poll __read_mostly;
+#endif
 
 static int sock_no_open(struct inode *irrelevant, struct file *dontcare);
 static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov,
@@ -1142,13 +1148,24 @@ EXPORT_SYMBOL(sock_create_lite);
 /* No kernel lock held - perfect */
 static unsigned int sock_poll(struct file *file, poll_table *wait)
 {
+       unsigned int busy_flag = 0;
        struct socket *sock;
 
        /*
         *      We can't return errors to poll, so it's either yes or no.
         */
        sock = file->private_data;
-       return sock->ops->poll(file, sock, wait);
+
+       if (sk_can_busy_loop(sock->sk)) {
+               /* this socket can poll_ll so tell the system call */
+               busy_flag = POLL_BUSY_LOOP;
+
+               /* once, only if requested by syscall */
+               if (wait && (wait->_key & POLL_BUSY_LOOP))
+                       sk_busy_loop(sock->sk, 1);
+       }
+
+       return busy_flag | sock->ops->poll(file, sock, wait);
 }
 
 static int sock_mmap(struct file *file, struct vm_area_struct *vma)
@@ -2635,7 +2652,9 @@ static int __init sock_init(void)
         */
 
 #ifdef CONFIG_NETFILTER
-       netfilter_init();
+       err = netfilter_init();
+       if (err)
+               goto out;
 #endif
 
 #ifdef CONFIG_NETWORK_PHY_TIMESTAMPING
index 5a750b9..f0339ae 100644 (file)
@@ -157,20 +157,15 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,
 }
 
 static int
-rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name)
+rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name,
+                 struct super_block *pipefs_sb)
 {
-       struct net *net = rpc_net_ns(clnt);
-       struct super_block *pipefs_sb;
        struct dentry *dentry;
 
        clnt->cl_dentry = NULL;
        if (dir_name == NULL)
                return 0;
-       pipefs_sb = rpc_get_sb_net(net);
-       if (!pipefs_sb)
-               return 0;
        dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name);
-       rpc_put_sb_net(net);
        if (IS_ERR(dentry))
                return PTR_ERR(dentry);
        clnt->cl_dentry = dentry;
@@ -182,6 +177,8 @@ static inline int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event
        if (((event == RPC_PIPEFS_MOUNT) && clnt->cl_dentry) ||
            ((event == RPC_PIPEFS_UMOUNT) && !clnt->cl_dentry))
                return 1;
+       if ((event == RPC_PIPEFS_MOUNT) && atomic_read(&clnt->cl_count) == 0)
+               return 1;
        return 0;
 }
 
@@ -241,8 +238,6 @@ static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event)
                        continue;
                if (rpc_clnt_skip_event(clnt, event))
                        continue;
-               if (atomic_inc_not_zero(&clnt->cl_count) == 0)
-                       continue;
                spin_unlock(&sn->rpc_client_lock);
                return clnt;
        }
@@ -259,7 +254,6 @@ static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event,
 
        while ((clnt = rpc_get_client_for_event(sb->s_fs_info, event))) {
                error = __rpc_pipefs_event(clnt, event, sb);
-               rpc_release_client(clnt);
                if (error)
                        break;
        }
@@ -289,12 +283,46 @@ static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename)
        memcpy(clnt->cl_nodename, nodename, clnt->cl_nodelen);
 }
 
+static int rpc_client_register(const struct rpc_create_args *args,
+                              struct rpc_clnt *clnt)
+{
+       const struct rpc_program *program = args->program;
+       struct rpc_auth *auth;
+       struct net *net = rpc_net_ns(clnt);
+       struct super_block *pipefs_sb;
+       int err = 0;
+
+       pipefs_sb = rpc_get_sb_net(net);
+       if (pipefs_sb) {
+               err = rpc_setup_pipedir(clnt, program->pipe_dir_name, pipefs_sb);
+               if (err)
+                       goto out;
+       }
+
+       auth = rpcauth_create(args->authflavor, clnt);
+       if (IS_ERR(auth)) {
+               dprintk("RPC:       Couldn't create auth handle (flavor %u)\n",
+                               args->authflavor);
+               err = PTR_ERR(auth);
+               goto err_auth;
+       }
+
+       rpc_register_client(clnt);
+out:
+       if (pipefs_sb)
+               rpc_put_sb_net(net);
+       return err;
+
+err_auth:
+       __rpc_clnt_remove_pipedir(clnt);
+       goto out;
+}
+
 static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt *xprt)
 {
        const struct rpc_program *program = args->program;
        const struct rpc_version *version;
        struct rpc_clnt         *clnt = NULL;
-       struct rpc_auth         *auth;
        int err;
 
        /* sanity check the name before trying to print it */
@@ -354,25 +382,14 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
 
        atomic_set(&clnt->cl_count, 1);
 
-       err = rpc_setup_pipedir(clnt, program->pipe_dir_name);
-       if (err < 0)
-               goto out_no_path;
-
-       auth = rpcauth_create(args->authflavor, clnt);
-       if (IS_ERR(auth)) {
-               dprintk("RPC:       Couldn't create auth handle (flavor %u)\n",
-                               args->authflavor);
-               err = PTR_ERR(auth);
-               goto out_no_auth;
-       }
-
        /* save the nodename */
        rpc_clnt_set_nodename(clnt, utsname()->nodename);
-       rpc_register_client(clnt);
+
+       err = rpc_client_register(args, clnt);
+       if (err)
+               goto out_no_path;
        return clnt;
 
-out_no_auth:
-       rpc_clnt_remove_pipedir(clnt);
 out_no_path:
        kfree(clnt->cl_principal);
 out_no_principal:
@@ -637,8 +654,8 @@ rpc_free_client(struct rpc_clnt *clnt)
                        rcu_dereference(clnt->cl_xprt)->servername);
        if (clnt->cl_parent != clnt)
                rpc_release_client(clnt->cl_parent);
-       rpc_unregister_client(clnt);
        rpc_clnt_remove_pipedir(clnt);
+       rpc_unregister_client(clnt);
        rpc_free_iostats(clnt->cl_metrics);
        kfree(clnt->cl_principal);
        clnt->cl_metrics = NULL;
index e7ce4b3..4679df5 100644 (file)
@@ -667,7 +667,8 @@ static struct dentry *__rpc_lookup_create_exclusive(struct dentry *parent,
                        return ERR_PTR(-ENOMEM);
        }
        if (dentry->d_inode == NULL) {
-               d_set_d_op(dentry, &rpc_dentry_operations);
+               if (!dentry->d_op)
+                       d_set_d_op(dentry, &rpc_dentry_operations);
                return dentry;
        }
        dput(dentry);
@@ -1126,6 +1127,7 @@ rpc_fill_super(struct super_block *sb, void *data, int silent)
                return -ENOMEM;
        dprintk("RPC:       sending pipefs MOUNT notification for net %p%s\n",
                net, NET_NAME(net));
+       mutex_lock(&sn->pipefs_sb_lock);
        sn->pipefs_sb = sb;
        err = blocking_notifier_call_chain(&rpc_pipefs_notifier_list,
                                           RPC_PIPEFS_MOUNT,
@@ -1133,6 +1135,7 @@ rpc_fill_super(struct super_block *sb, void *data, int silent)
        if (err)
                goto err_depopulate;
        sb->s_fs_info = get_net(net);
+       mutex_unlock(&sn->pipefs_sb_lock);
        return 0;
 
 err_depopulate:
@@ -1141,6 +1144,7 @@ err_depopulate:
                                           sb);
        sn->pipefs_sb = NULL;
        __rpc_depopulate(root, files, RPCAUTH_lockd, RPCAUTH_RootEOF);
+       mutex_unlock(&sn->pipefs_sb_lock);
        return err;
 }
 
@@ -1162,12 +1166,12 @@ static void rpc_kill_sb(struct super_block *sb)
                goto out;
        }
        sn->pipefs_sb = NULL;
-       mutex_unlock(&sn->pipefs_sb_lock);
        dprintk("RPC:       sending pipefs UMOUNT notification for net %p%s\n",
                net, NET_NAME(net));
        blocking_notifier_call_chain(&rpc_pipefs_notifier_list,
                                           RPC_PIPEFS_UMOUNT,
                                           sb);
+       mutex_unlock(&sn->pipefs_sb_lock);
        put_net(net);
 out:
        kill_litter_super(sb);
index 77d251e..93a7a4e 100644 (file)
@@ -445,20 +445,6 @@ static void rpc_wake_up_task_queue_locked(struct rpc_wait_queue *queue, struct r
        }
 }
 
-/*
- * Tests whether rpc queue is empty
- */
-int rpc_queue_empty(struct rpc_wait_queue *queue)
-{
-       int res;
-
-       spin_lock_bh(&queue->lock);
-       res = queue->qlen;
-       spin_unlock_bh(&queue->lock);
-       return res == 0;
-}
-EXPORT_SYMBOL_GPL(rpc_queue_empty);
-
 /*
  * Wake up a task on a specific queue
  */
@@ -804,7 +790,6 @@ static void __rpc_execute(struct rpc_task *task)
                        task->tk_flags |= RPC_TASK_KILLED;
                        rpc_exit(task, -ERESTARTSYS);
                }
-               rpc_set_running(task);
                dprintk("RPC: %5u sync task resuming\n", task->tk_pid);
        }
 
@@ -825,9 +810,11 @@ static void __rpc_execute(struct rpc_task *task)
  */
 void rpc_execute(struct rpc_task *task)
 {
+       bool is_async = RPC_IS_ASYNC(task);
+
        rpc_set_active(task);
        rpc_make_runnable(task);
-       if (!RPC_IS_ASYNC(task))
+       if (!is_async)
                __rpc_execute(task);
 }
 
index af7d339..c99c58e 100644 (file)
@@ -40,7 +40,7 @@ EXPORT_SYMBOL_GPL(nlm_debug);
 #ifdef RPC_DEBUG
 
 static struct ctl_table_header *sunrpc_table_header;
-static ctl_table               sunrpc_table[];
+static struct ctl_table sunrpc_table[];
 
 void
 rpc_register_sysctl(void)
@@ -58,7 +58,7 @@ rpc_unregister_sysctl(void)
        }
 }
 
-static int proc_do_xprt(ctl_table *table, int write,
+static int proc_do_xprt(struct ctl_table *table, int write,
                        void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        char tmpbuf[256];
@@ -73,7 +73,7 @@ static int proc_do_xprt(ctl_table *table, int write,
 }
 
 static int
-proc_dodebug(ctl_table *table, int write,
+proc_dodebug(struct ctl_table *table, int write,
                                void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        char            tmpbuf[20], c, *s;
@@ -135,7 +135,7 @@ done:
 }
 
 
-static ctl_table debug_table[] = {
+static struct ctl_table debug_table[] = {
        {
                .procname       = "rpc_debug",
                .data           = &rpc_debug,
@@ -173,7 +173,7 @@ static ctl_table debug_table[] = {
        { }
 };
 
-static ctl_table sunrpc_table[] = {
+static struct ctl_table sunrpc_table[] = {
        {
                .procname       = "sunrpc",
                .mode           = 0555,
index 8343737..c1b6270 100644 (file)
@@ -84,7 +84,7 @@ struct workqueue_struct *svc_rdma_wq;
  * resets the associated statistic to zero. Any read returns it's
  * current value.
  */
-static int read_reset_stat(ctl_table *table, int write,
+static int read_reset_stat(struct ctl_table *table, int write,
                           void __user *buffer, size_t *lenp,
                           loff_t *ppos)
 {
@@ -119,7 +119,7 @@ static int read_reset_stat(ctl_table *table, int write,
 }
 
 static struct ctl_table_header *svcrdma_table_header;
-static ctl_table svcrdma_parm_table[] = {
+static struct ctl_table svcrdma_parm_table[] = {
        {
                .procname       = "max_requests",
                .data           = &svcrdma_max_requests,
@@ -214,7 +214,7 @@ static ctl_table svcrdma_parm_table[] = {
        { },
 };
 
-static ctl_table svcrdma_table[] = {
+static struct ctl_table svcrdma_table[] = {
        {
                .procname       = "svc_rdma",
                .mode           = 0555,
@@ -223,7 +223,7 @@ static ctl_table svcrdma_table[] = {
        { },
 };
 
-static ctl_table svcrdma_root_table[] = {
+static struct ctl_table svcrdma_root_table[] = {
        {
                .procname       = "sunrpc",
                .mode           = 0555,
index 794312f..285dc08 100644 (file)
@@ -86,7 +86,7 @@ static unsigned int max_memreg = RPCRDMA_LAST - 1;
 
 static struct ctl_table_header *sunrpc_table_header;
 
-static ctl_table xr_tunables_table[] = {
+static struct ctl_table xr_tunables_table[] = {
        {
                .procname       = "rdma_slot_table_entries",
                .data           = &xprt_rdma_slot_table_entries,
@@ -138,7 +138,7 @@ static ctl_table xr_tunables_table[] = {
        { },
 };
 
-static ctl_table sunrpc_table[] = {
+static struct ctl_table sunrpc_table[] = {
        {
                .procname       = "sunrpc",
                .mode           = 0555,
index ffd5034..412de7c 100644 (file)
@@ -87,7 +87,7 @@ static struct ctl_table_header *sunrpc_table_header;
  * FIXME: changing the UDP slot table size should also resize the UDP
  *        socket buffers for existing UDP transports
  */
-static ctl_table xs_tunables_table[] = {
+static struct ctl_table xs_tunables_table[] = {
        {
                .procname       = "udp_slot_table_entries",
                .data           = &xprt_udp_slot_table_entries,
@@ -143,7 +143,7 @@ static ctl_table xs_tunables_table[] = {
        { },
 };
 
-static ctl_table sunrpc_table[] = {
+static struct ctl_table sunrpc_table[] = {
        {
                .procname       = "sunrpc",
                .mode           = 0555,
index 4df8e02..b282f71 100644 (file)
@@ -8,6 +8,7 @@ tipc-y  += addr.o bcast.o bearer.o config.o \
           core.o handler.o link.o discover.o msg.o  \
           name_distr.o  subscr.o name_table.o net.o  \
           netlink.o node.o node_subscr.o port.o ref.o  \
-          socket.o log.o eth_media.o
+          socket.o log.o eth_media.o server.o
 
 tipc-$(CONFIG_TIPC_MEDIA_IB)   += ib_media.o
+tipc-$(CONFIG_SYSCTL)          += sysctl.o
index e5f3da5..716de1a 100644 (file)
@@ -578,8 +578,7 @@ u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr)
  * Returns 0 (packet sent successfully) under all circumstances,
  * since the broadcast link's pseudo-bearer never blocks
  */
-static int tipc_bcbearer_send(struct sk_buff *buf,
-                             struct tipc_bearer *unused1,
+static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1,
                              struct tipc_media_addr *unused2)
 {
        int bp_index;
index a933065..6ee587b 100644 (file)
@@ -75,7 +75,8 @@ void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node);
 /**
  * tipc_nmap_equal - test for equality of node maps
  */
-static inline int tipc_nmap_equal(struct tipc_node_map *nm_a, struct tipc_node_map *nm_b)
+static inline int tipc_nmap_equal(struct tipc_node_map *nm_a,
+                                 struct tipc_node_map *nm_b)
 {
        return !memcmp(nm_a, nm_b, sizeof(*nm_a));
 }
index f67866c..c301a9a 100644 (file)
@@ -2,7 +2,7 @@
  * net/tipc/config.c: TIPC configuration management code
  *
  * Copyright (c) 2002-2006, Ericsson AB
- * Copyright (c) 2004-2007, 2010-2012, Wind River Systems
+ * Copyright (c) 2004-2007, 2010-2013, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "port.h"
 #include "name_table.h"
 #include "config.h"
+#include "server.h"
 
 #define REPLY_TRUNCATED "<truncated>\n"
 
-static u32 config_port_ref;
-
-static DEFINE_SPINLOCK(config_lock);
+static DEFINE_MUTEX(config_mutex);
+static struct tipc_server cfgsrv;
 
 static const void *req_tlv_area;       /* request message TLV area */
 static int req_tlv_space;              /* request message TLV area size */
@@ -181,18 +181,7 @@ static struct sk_buff *cfg_set_own_addr(void)
        if (tipc_own_addr)
                return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
                                                   " (cannot change node address once assigned)");
-
-       /*
-        * Must temporarily release configuration spinlock while switching into
-        * networking mode as it calls tipc_eth_media_start(), which may sleep.
-        * Releasing the lock is harmless as other locally-issued configuration
-        * commands won't occur until this one completes, and remotely-issued
-        * configuration commands can't be received until a local configuration
-        * command to enable the first bearer is received and processed.
-        */
-       spin_unlock_bh(&config_lock);
        tipc_core_start_net(addr);
-       spin_lock_bh(&config_lock);
        return tipc_cfg_reply_none();
 }
 
@@ -248,7 +237,7 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area
 {
        struct sk_buff *rep_tlv_buf;
 
-       spin_lock_bh(&config_lock);
+       mutex_lock(&config_mutex);
 
        /* Save request and reply details in a well-known location */
        req_tlv_area = request_area;
@@ -377,37 +366,31 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area
 
        /* Return reply buffer */
 exit:
-       spin_unlock_bh(&config_lock);
+       mutex_unlock(&config_mutex);
        return rep_tlv_buf;
 }
 
-static void cfg_named_msg_event(void *userdata,
-                               u32 port_ref,
-                               struct sk_buff **buf,
-                               const unchar *msg,
-                               u32 size,
-                               u32 importance,
-                               struct tipc_portid const *orig,
-                               struct tipc_name_seq const *dest)
+static void cfg_conn_msg_event(int conid, struct sockaddr_tipc *addr,
+                              void *usr_data, void *buf, size_t len)
 {
        struct tipc_cfg_msg_hdr *req_hdr;
        struct tipc_cfg_msg_hdr *rep_hdr;
        struct sk_buff *rep_buf;
+       int ret;
 
        /* Validate configuration message header (ignore invalid message) */
-       req_hdr = (struct tipc_cfg_msg_hdr *)msg;
-       if ((size < sizeof(*req_hdr)) ||
-           (size != TCM_ALIGN(ntohl(req_hdr->tcm_len))) ||
+       req_hdr = (struct tipc_cfg_msg_hdr *)buf;
+       if ((len < sizeof(*req_hdr)) ||
+           (len != TCM_ALIGN(ntohl(req_hdr->tcm_len))) ||
            (ntohs(req_hdr->tcm_flags) != TCM_F_REQUEST)) {
                pr_warn("Invalid configuration message discarded\n");
                return;
        }
 
        /* Generate reply for request (if can't, return request) */
-       rep_buf = tipc_cfg_do_cmd(orig->node,
-                                 ntohs(req_hdr->tcm_type),
-                                 msg + sizeof(*req_hdr),
-                                 size - sizeof(*req_hdr),
+       rep_buf = tipc_cfg_do_cmd(addr->addr.id.node, ntohs(req_hdr->tcm_type),
+                                 buf + sizeof(*req_hdr),
+                                 len - sizeof(*req_hdr),
                                  BUF_HEADROOM + MAX_H_SIZE + sizeof(*rep_hdr));
        if (rep_buf) {
                skb_push(rep_buf, sizeof(*rep_hdr));
@@ -415,57 +398,51 @@ static void cfg_named_msg_event(void *userdata,
                memcpy(rep_hdr, req_hdr, sizeof(*rep_hdr));
                rep_hdr->tcm_len = htonl(rep_buf->len);
                rep_hdr->tcm_flags &= htons(~TCM_F_REQUEST);
-       } else {
-               rep_buf = *buf;
-               *buf = NULL;
-       }
 
-       /* NEED TO ADD CODE TO HANDLE FAILED SEND (SUCH AS CONGESTION) */
-       tipc_send_buf2port(port_ref, orig, rep_buf, rep_buf->len);
+               ret = tipc_conn_sendmsg(&cfgsrv, conid, addr, rep_buf->data,
+                                       rep_buf->len);
+               if (ret < 0)
+                       pr_err("Sending cfg reply message failed, no memory\n");
+
+               kfree_skb(rep_buf);
+       }
 }
 
+static struct sockaddr_tipc cfgsrv_addr __read_mostly = {
+       .family                 = AF_TIPC,
+       .addrtype               = TIPC_ADDR_NAMESEQ,
+       .addr.nameseq.type      = TIPC_CFG_SRV,
+       .addr.nameseq.lower     = 0,
+       .addr.nameseq.upper     = 0,
+       .scope                  = TIPC_ZONE_SCOPE
+};
+
+static struct tipc_server cfgsrv __read_mostly = {
+       .saddr                  = &cfgsrv_addr,
+       .imp                    = TIPC_CRITICAL_IMPORTANCE,
+       .type                   = SOCK_RDM,
+       .max_rcvbuf_size        = 64 * 1024,
+       .name                   = "cfg_server",
+       .tipc_conn_recvmsg      = cfg_conn_msg_event,
+       .tipc_conn_new          = NULL,
+       .tipc_conn_shutdown     = NULL
+};
+
 int tipc_cfg_init(void)
 {
-       struct tipc_name_seq seq;
-       int res;
-
-       res = tipc_createport(NULL, TIPC_CRITICAL_IMPORTANCE,
-                             NULL, NULL, NULL,
-                             NULL, cfg_named_msg_event, NULL,
-                             NULL, &config_port_ref);
-       if (res)
-               goto failed;
-
-       seq.type = TIPC_CFG_SRV;
-       seq.lower = seq.upper = tipc_own_addr;
-       res = tipc_publish(config_port_ref, TIPC_ZONE_SCOPE, &seq);
-       if (res)
-               goto failed;
-
-       return 0;
-
-failed:
-       pr_err("Unable to create configuration service\n");
-       return res;
+       return tipc_server_start(&cfgsrv);
 }
 
 void tipc_cfg_reinit(void)
 {
-       struct tipc_name_seq seq;
-       int res;
-
-       seq.type = TIPC_CFG_SRV;
-       seq.lower = seq.upper = 0;
-       tipc_withdraw(config_port_ref, TIPC_ZONE_SCOPE, &seq);
+       tipc_server_stop(&cfgsrv);
 
-       seq.lower = seq.upper = tipc_own_addr;
-       res = tipc_publish(config_port_ref, TIPC_ZONE_SCOPE, &seq);
-       if (res)
-               pr_err("Unable to reinitialize configuration service\n");
+       cfgsrv_addr.addr.nameseq.lower = tipc_own_addr;
+       cfgsrv_addr.addr.nameseq.upper = tipc_own_addr;
+       tipc_server_start(&cfgsrv);
 }
 
 void tipc_cfg_stop(void)
 {
-       tipc_deleteport(config_port_ref);
-       config_port_ref = 0;
+       tipc_server_stop(&cfgsrv);
 }
index 7ec2c1e..fd4eeea 100644 (file)
@@ -2,7 +2,7 @@
  * net/tipc/core.c: TIPC module code
  *
  * Copyright (c) 2003-2006, Ericsson AB
- * Copyright (c) 2005-2006, 2010-2011, Wind River Systems
+ * Copyright (c) 2005-2006, 2010-2013, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,7 @@
 #include "name_table.h"
 #include "subscr.h"
 #include "config.h"
+#include "port.h"
 
 #include <linux/module.h>
 
@@ -50,7 +51,7 @@ u32 tipc_own_addr __read_mostly;
 int tipc_max_ports __read_mostly;
 int tipc_net_id __read_mostly;
 int tipc_remote_management __read_mostly;
-
+int sysctl_tipc_rmem[3] __read_mostly; /* min/default/max */
 
 /**
  * tipc_buf_acquire - creates a TIPC message buffer
@@ -118,6 +119,7 @@ static void tipc_core_stop(void)
        tipc_nametbl_stop();
        tipc_ref_table_stop();
        tipc_socket_stop();
+       tipc_unregister_sysctl();
 }
 
 /**
@@ -134,21 +136,22 @@ static int tipc_core_start(void)
                res = tipc_ref_table_init(tipc_max_ports, tipc_random);
        if (!res)
                res = tipc_nametbl_init();
-       if (!res)
-               res = tipc_subscr_start();
-       if (!res)
-               res = tipc_cfg_init();
        if (!res)
                res = tipc_netlink_start();
        if (!res)
                res = tipc_socket_init();
+       if (!res)
+               res = tipc_register_sysctl();
+       if (!res)
+               res = tipc_subscr_start();
+       if (!res)
+               res = tipc_cfg_init();
        if (res)
                tipc_core_stop();
 
        return res;
 }
 
-
 static int __init tipc_init(void)
 {
        int res;
@@ -160,6 +163,11 @@ static int __init tipc_init(void)
        tipc_max_ports = CONFIG_TIPC_PORTS;
        tipc_net_id = 4711;
 
+       sysctl_tipc_rmem[0] = CONN_OVERLOAD_LIMIT >> 4 << TIPC_LOW_IMPORTANCE;
+       sysctl_tipc_rmem[1] = CONN_OVERLOAD_LIMIT >> 4 <<
+                             TIPC_CRITICAL_IMPORTANCE;
+       sysctl_tipc_rmem[2] = CONN_OVERLOAD_LIMIT;
+
        res = tipc_core_start();
        if (res)
                pr_err("Unable to start in single node mode\n");
index 0207db0..be72f8c 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * net/tipc/core.h: Include file for TIPC global declarations
  *
- * Copyright (c) 2005-2006, Ericsson AB
- * Copyright (c) 2005-2007, 2010-2011, Wind River Systems
+ * Copyright (c) 2005-2006, 2013 Ericsson AB
+ * Copyright (c) 2005-2007, 2010-2013, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -80,6 +80,7 @@ extern u32 tipc_own_addr __read_mostly;
 extern int tipc_max_ports __read_mostly;
 extern int tipc_net_id __read_mostly;
 extern int tipc_remote_management __read_mostly;
+extern int sysctl_tipc_rmem[3] __read_mostly;
 
 /*
  * Other global variables
@@ -96,6 +97,18 @@ extern int  tipc_netlink_start(void);
 extern void tipc_netlink_stop(void);
 extern int  tipc_socket_init(void);
 extern void tipc_socket_stop(void);
+extern int tipc_sock_create_local(int type, struct socket **res);
+extern void tipc_sock_release_local(struct socket *sock);
+extern int tipc_sock_accept_local(struct socket *sock,
+                                 struct socket **newsock, int flags);
+
+#ifdef CONFIG_SYSCTL
+extern int tipc_register_sysctl(void);
+extern void tipc_unregister_sysctl(void);
+#else
+#define tipc_register_sysctl() 0
+#define tipc_unregister_sysctl()
+#endif
 
 /*
  * TIPC timer and signal code
index eedff58..ecc758c 100644 (file)
@@ -70,8 +70,7 @@ struct tipc_link_req {
  * @dest_domain: network domain of node(s) which should respond to message
  * @b_ptr: ptr to bearer issuing message
  */
-static struct sk_buff *tipc_disc_init_msg(u32 type,
-                                         u32 dest_domain,
+static struct sk_buff *tipc_disc_init_msg(u32 type, u32 dest_domain,
                                          struct tipc_bearer *b_ptr)
 {
        struct sk_buff *buf = tipc_buf_acquire(INT_H_SIZE);
@@ -346,8 +345,8 @@ exit:
  *
  * Returns 0 if successful, otherwise -errno.
  */
-int tipc_disc_create(struct tipc_bearer *b_ptr,
-                    struct tipc_media_addr *dest, u32 dest_domain)
+int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest,
+                    u32 dest_domain)
 {
        struct tipc_link_req *req;
 
index 120a676..40ea40c 100644 (file)
@@ -62,7 +62,7 @@ static struct eth_bearer eth_bearers[MAX_ETH_BEARERS];
 static int eth_started;
 
 static int recv_notification(struct notifier_block *nb, unsigned long evt,
-                             void *dv);
+                            void *dv);
 /*
  * Network device notifier info
  */
@@ -162,8 +162,7 @@ static void setup_bearer(struct work_struct *work)
  */
 static int enable_bearer(struct tipc_bearer *tb_ptr)
 {
-       struct net_device *dev = NULL;
-       struct net_device *pdev = NULL;
+       struct net_device *dev;
        struct eth_bearer *eb_ptr = &eth_bearers[0];
        struct eth_bearer *stop = &eth_bearers[MAX_ETH_BEARERS];
        char *driver_name = strchr((const char *)tb_ptr->name, ':') + 1;
@@ -178,15 +177,7 @@ static int enable_bearer(struct tipc_bearer *tb_ptr)
        }
 
        /* Find device with specified name */
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, pdev) {
-               if (!strncmp(pdev->name, driver_name, IFNAMSIZ)) {
-                       dev = pdev;
-                       dev_hold(dev);
-                       break;
-               }
-       }
-       read_unlock(&dev_base_lock);
+       dev = dev_get_by_name(&init_net, driver_name);
        if (!dev)
                return -ENODEV;
 
@@ -251,9 +242,9 @@ static void disable_bearer(struct tipc_bearer *tb_ptr)
  * specified device.
  */
 static int recv_notification(struct notifier_block *nb, unsigned long evt,
-                            void *dv)
+                            void *ptr)
 {
-       struct net_device *dev = (struct net_device *)dv;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct eth_bearer *eb_ptr = &eth_bearers[0];
        struct eth_bearer *stop = &eth_bearers[MAX_ETH_BEARERS];
 
index 2a2864c..ad2e1ec 100644 (file)
@@ -155,8 +155,7 @@ static void setup_bearer(struct work_struct *work)
  */
 static int enable_bearer(struct tipc_bearer *tb_ptr)
 {
-       struct net_device *dev = NULL;
-       struct net_device *pdev = NULL;
+       struct net_device *dev;
        struct ib_bearer *ib_ptr = &ib_bearers[0];
        struct ib_bearer *stop = &ib_bearers[MAX_IB_BEARERS];
        char *driver_name = strchr((const char *)tb_ptr->name, ':') + 1;
@@ -171,15 +170,7 @@ static int enable_bearer(struct tipc_bearer *tb_ptr)
        }
 
        /* Find device with specified name */
-       read_lock(&dev_base_lock);
-       for_each_netdev(&init_net, pdev) {
-               if (!strncmp(pdev->name, driver_name, IFNAMSIZ)) {
-                       dev = pdev;
-                       dev_hold(dev);
-                       break;
-               }
-       }
-       read_unlock(&dev_base_lock);
+       dev = dev_get_by_name(&init_net, driver_name);
        if (!dev)
                return -ENODEV;
 
@@ -244,9 +235,9 @@ static void disable_bearer(struct tipc_bearer *tb_ptr)
  * specified device.
  */
 static int recv_notification(struct notifier_block *nb, unsigned long evt,
-                            void *dv)
+                            void *ptr)
 {
-       struct net_device *dev = (struct net_device *)dv;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct ib_bearer *ib_ptr = &ib_bearers[0];
        struct ib_bearer *stop = &ib_bearers[MAX_IB_BEARERS];
 
index a80feee..0cc3d90 100644 (file)
@@ -2,7 +2,7 @@
  * net/tipc/link.c: TIPC link code
  *
  * Copyright (c) 1996-2007, 2012, Ericsson AB
- * Copyright (c) 2004-2007, 2010-2011, Wind River Systems
+ * Copyright (c) 2004-2007, 2010-2013, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -41,6 +41,8 @@
 #include "discover.h"
 #include "config.h"
 
+#include <linux/pkt_sched.h>
+
 /*
  * Error message prefixes
  */
@@ -771,8 +773,7 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
  * link_bundle_buf(): Append contents of a buffer to
  * the tail of an existing one.
  */
-static int link_bundle_buf(struct tipc_link *l_ptr,
-                          struct sk_buff *bundler,
+static int link_bundle_buf(struct tipc_link *l_ptr, struct sk_buff *bundler,
                           struct sk_buff *buf)
 {
        struct tipc_msg *bundler_msg = buf_msg(bundler);
@@ -1056,40 +1057,6 @@ static int link_send_buf_fast(struct tipc_link *l_ptr, struct sk_buff *buf,
        return tipc_link_send_buf(l_ptr, buf);  /* All other cases */
 }
 
-/*
- * tipc_send_buf_fast: Entry for data messages where the
- * destination node is known and the header is complete,
- * inclusive total message length.
- * Returns user data length.
- */
-int tipc_send_buf_fast(struct sk_buff *buf, u32 destnode)
-{
-       struct tipc_link *l_ptr;
-       struct tipc_node *n_ptr;
-       int res;
-       u32 selector = msg_origport(buf_msg(buf)) & 1;
-       u32 dummy;
-
-       read_lock_bh(&tipc_net_lock);
-       n_ptr = tipc_node_find(destnode);
-       if (likely(n_ptr)) {
-               tipc_node_lock(n_ptr);
-               l_ptr = n_ptr->active_links[selector];
-               if (likely(l_ptr)) {
-                       res = link_send_buf_fast(l_ptr, buf, &dummy);
-                       tipc_node_unlock(n_ptr);
-                       read_unlock_bh(&tipc_net_lock);
-                       return res;
-               }
-               tipc_node_unlock(n_ptr);
-       }
-       read_unlock_bh(&tipc_net_lock);
-       res = msg_data_sz(buf_msg(buf));
-       tipc_reject_msg(buf, TIPC_ERR_NO_NODE);
-       return res;
-}
-
-
 /*
  * tipc_link_send_sections_fast: Entry for messages where the
  * destination processor is known and the header is complete,
@@ -1098,8 +1065,7 @@ int tipc_send_buf_fast(struct sk_buff *buf, u32 destnode)
  */
 int tipc_link_send_sections_fast(struct tipc_port *sender,
                                 struct iovec const *msg_sect,
-                                const u32 num_sect,
-                                unsigned int total_len,
+                                const u32 num_sect, unsigned int total_len,
                                 u32 destaddr)
 {
        struct tipc_msg *hdr = &sender->phdr;
@@ -1115,7 +1081,10 @@ again:
         * (Must not hold any locks while building message.)
         */
        res = tipc_msg_build(hdr, msg_sect, num_sect, total_len,
-                            sender->max_pkt, !sender->user_port, &buf);
+                            sender->max_pkt, &buf);
+       /* Exit if build request was invalid */
+       if (unlikely(res < 0))
+               return res;
 
        read_lock_bh(&tipc_net_lock);
        node = tipc_node_find(destaddr);
@@ -1132,10 +1101,6 @@ exit:
                                return res;
                        }
 
-                       /* Exit if build request was invalid */
-                       if (unlikely(res < 0))
-                               goto exit;
-
                        /* Exit if link (or bearer) is congested */
                        if (link_congested(l_ptr) ||
                            tipc_bearer_blocked(l_ptr->b_ptr)) {
@@ -1189,8 +1154,7 @@ exit:
  */
 static int link_send_sections_long(struct tipc_port *sender,
                                   struct iovec const *msg_sect,
-                                  u32 num_sect,
-                                  unsigned int total_len,
+                                  u32 num_sect, unsigned int total_len,
                                   u32 destaddr)
 {
        struct tipc_link *l_ptr;
@@ -1204,6 +1168,7 @@ static int link_send_sections_long(struct tipc_port *sender,
        const unchar *sect_crs;
        int curr_sect;
        u32 fragm_no;
+       int res = 0;
 
 again:
        fragm_no = 1;
@@ -1250,18 +1215,15 @@ again:
                else
                        sz = fragm_rest;
 
-               if (likely(!sender->user_port)) {
-                       if (copy_from_user(buf->data + fragm_crs, sect_crs, sz)) {
+               if (copy_from_user(buf->data + fragm_crs, sect_crs, sz)) {
+                       res = -EFAULT;
 error:
-                               for (; buf_chain; buf_chain = buf) {
-                                       buf = buf_chain->next;
-                                       kfree_skb(buf_chain);
-                               }
-                               return -EFAULT;
+                       for (; buf_chain; buf_chain = buf) {
+                               buf = buf_chain->next;
+                               kfree_skb(buf_chain);
                        }
-               } else
-                       skb_copy_to_linear_data_offset(buf, fragm_crs,
-                                                      sect_crs, sz);
+                       return res;
+               }
                sect_crs += sz;
                sect_rest -= sz;
                fragm_crs += sz;
@@ -1281,8 +1243,10 @@ error:
                        msg_set_fragm_no(&fragm_hdr, ++fragm_no);
                        prev = buf;
                        buf = tipc_buf_acquire(fragm_sz + INT_H_SIZE);
-                       if (!buf)
+                       if (!buf) {
+                               res = -ENOMEM;
                                goto error;
+                       }
 
                        buf->next = NULL;
                        prev->next = buf;
@@ -1446,7 +1410,7 @@ static void link_reset_all(unsigned long addr)
 }
 
 static void link_retransmit_failure(struct tipc_link *l_ptr,
-                                       struct sk_buff *buf)
+                                   struct sk_buff *buf)
 {
        struct tipc_msg *msg = buf_msg(buf);
 
@@ -1901,8 +1865,8 @@ static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr,
  * Send protocol message to the other endpoint.
  */
 void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ,
-                               int probe_msg, u32 gap, u32 tolerance,
-                               u32 priority, u32 ack_mtu)
+                             int probe_msg, u32 gap, u32 tolerance,
+                             u32 priority, u32 ack_mtu)
 {
        struct sk_buff *buf = NULL;
        struct tipc_msg *msg = l_ptr->pmsg;
@@ -1988,6 +1952,7 @@ void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ,
                return;
 
        skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg));
+       buf->priority = TC_PRIO_CONTROL;
 
        /* Defer message if bearer is already blocked */
        if (tipc_bearer_blocked(l_ptr->b_ptr)) {
@@ -2145,8 +2110,7 @@ exit:
  * another bearer. Owner node is locked.
  */
 static void tipc_link_tunnel(struct tipc_link *l_ptr,
-                            struct tipc_msg *tunnel_hdr,
-                            struct tipc_msg  *msg,
+                            struct tipc_msg *tunnel_hdr, struct tipc_msg *msg,
                             u32 selector)
 {
        struct tipc_link *tunnel;
index f2db8a8..ced60e2 100644 (file)
@@ -51,8 +51,8 @@ u32 tipc_msg_tot_importance(struct tipc_msg *m)
 }
 
 
-void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type,
-                           u32 hsize, u32 destnode)
+void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize,
+                  u32 destnode)
 {
        memset(m, 0, hsize);
        msg_set_version(m);
@@ -73,8 +73,8 @@ void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type,
  * Returns message data size or errno
  */
 int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect,
-                  u32 num_sect, unsigned int total_len,
-                           int max_size, int usrmem, struct sk_buff **buf)
+                  u32 num_sect, unsigned int total_len, int max_size,
+                  struct sk_buff **buf)
 {
        int dsz, sz, hsz, pos, res, cnt;
 
@@ -92,14 +92,9 @@ int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect,
                return -ENOMEM;
        skb_copy_to_linear_data(*buf, hdr, hsz);
        for (res = 1, cnt = 0; res && (cnt < num_sect); cnt++) {
-               if (likely(usrmem))
-                       res = !copy_from_user((*buf)->data + pos,
-                                             msg_sect[cnt].iov_base,
-                                             msg_sect[cnt].iov_len);
-               else
-                       skb_copy_to_linear_data_offset(*buf, pos,
-                                                      msg_sect[cnt].iov_base,
-                                                      msg_sect[cnt].iov_len);
+               skb_copy_to_linear_data_offset(*buf, pos,
+                                              msg_sect[cnt].iov_base,
+                                              msg_sect[cnt].iov_len);
                pos += msg_sect[cnt].iov_len;
        }
        if (likely(res))
index ba2a72b..5e4ccf5 100644 (file)
@@ -719,9 +719,9 @@ static inline void msg_set_link_tolerance(struct tipc_msg *m, u32 n)
 }
 
 u32 tipc_msg_tot_importance(struct tipc_msg *m);
-void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type,
-                           u32 hsize, u32 destnode);
+void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize,
+                  u32 destnode);
 int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect,
-                  u32 num_sect, unsigned int total_len,
-                           int max_size, int usrmem, struct sk_buff **buf);
+                  u32 num_sect, unsigned int total_len, int max_size,
+                  struct sk_buff **buf);
 #endif
index 24b1679..09dcd54 100644 (file)
@@ -440,7 +440,7 @@ found:
  * sequence overlapping with the requested sequence
  */
 static void tipc_nameseq_subscribe(struct name_seq *nseq,
-                                       struct tipc_subscription *s)
+                                  struct tipc_subscription *s)
 {
        struct sub_seq *sseq = nseq->sseqs;
 
@@ -662,7 +662,7 @@ exit:
  * tipc_nametbl_publish - add name publication to network name tables
  */
 struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
-                                   u32 scope, u32 port_ref, u32 key)
+                                        u32 scope, u32 port_ref, u32 key)
 {
        struct publication *publ;
 
@@ -753,7 +753,7 @@ void tipc_nametbl_unsubscribe(struct tipc_subscription *s)
  * subseq_list - print specified sub-sequence contents into the given buffer
  */
 static int subseq_list(struct sub_seq *sseq, char *buf, int len, u32 depth,
-                       u32 index)
+                      u32 index)
 {
        char portIdStr[27];
        const char *scope_str[] = {"", " zone", " cluster", " node"};
@@ -792,7 +792,7 @@ static int subseq_list(struct sub_seq *sseq, char *buf, int len, u32 depth,
  * nameseq_list - print specified name sequence contents into the given buffer
  */
 static int nameseq_list(struct name_seq *seq, char *buf, int len, u32 depth,
-                        u32 type, u32 lowbound, u32 upbound, u32 index)
+                       u32 type, u32 lowbound, u32 upbound, u32 index)
 {
        struct sub_seq *sseq;
        char typearea[11];
@@ -849,7 +849,7 @@ static int nametbl_header(char *buf, int len, u32 depth)
  * nametbl_list - print specified name table contents into the given buffer
  */
 static int nametbl_list(char *buf, int len, u32 depth_info,
-                        u32 type, u32 lowbound, u32 upbound)
+                       u32 type, u32 lowbound, u32 upbound)
 {
        struct hlist_head *seq_head;
        struct name_seq *seq;
index 71cb4dc..f02f48b 100644 (file)
@@ -87,14 +87,15 @@ extern rwlock_t tipc_nametbl_lock;
 struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space);
 u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *node);
 int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit,
-                        struct tipc_port_list *dports);
+                             struct tipc_port_list *dports);
 struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
-                                   u32 scope, u32 port_ref, u32 key);
+                                        u32 scope, u32 port_ref, u32 key);
 int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key);
 struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper,
-                                       u32 scope, u32 node, u32 ref, u32 key);
-struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower,
-                                       u32 node, u32 ref, u32 key);
+                                            u32 scope, u32 node, u32 ref,
+                                            u32 key);
+struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower, u32 node,
+                                            u32 ref, u32 key);
 void tipc_nametbl_subscribe(struct tipc_subscription *s);
 void tipc_nametbl_unsubscribe(struct tipc_subscription *s);
 int tipc_nametbl_init(void);
index 5e34b01..8a7384c 100644 (file)
@@ -42,7 +42,7 @@
  * tipc_nodesub_subscribe - create "node down" subscription for specified node
  */
 void tipc_nodesub_subscribe(struct tipc_node_subscr *node_sub, u32 addr,
-                      void *usr_handle, net_ev_handler handle_down)
+                           void *usr_handle, net_ev_handler handle_down)
 {
        if (in_own_node(addr)) {
                node_sub->node = NULL;
index 18098ca..b3ed2fc 100644 (file)
@@ -2,7 +2,7 @@
  * net/tipc/port.c: TIPC port code
  *
  * Copyright (c) 1992-2007, Ericsson AB
- * Copyright (c) 2004-2008, 2010-2011, Wind River Systems
+ * Copyright (c) 2004-2008, 2010-2013, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 
 #define MAX_REJECT_SIZE 1024
 
-static struct sk_buff *msg_queue_head;
-static struct sk_buff *msg_queue_tail;
-
 DEFINE_SPINLOCK(tipc_port_list_lock);
-static DEFINE_SPINLOCK(queue_lock);
 
 static LIST_HEAD(ports);
 static void port_handle_node_down(unsigned long ref);
@@ -119,7 +115,7 @@ int tipc_multicast(u32 ref, struct tipc_name_seq const *seq,
        msg_set_nameupper(hdr, seq->upper);
        msg_set_hdr_sz(hdr, MCAST_H_SIZE);
        res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, MAX_MSG_SIZE,
-                       !oport->user_port, &buf);
+                            &buf);
        if (unlikely(!buf))
                return res;
 
@@ -206,14 +202,15 @@ exit:
 }
 
 /**
- * tipc_createport_raw - create a generic TIPC port
+ * tipc_createport - create a generic TIPC port
  *
  * Returns pointer to (locked) TIPC port, or NULL if unable to create it
  */
-struct tipc_port *tipc_createport_raw(void *usr_handle,
-                       u32 (*dispatcher)(struct tipc_port *, struct sk_buff *),
-                       void (*wakeup)(struct tipc_port *),
-                       const u32 importance)
+struct tipc_port *tipc_createport(struct sock *sk,
+                                 u32 (*dispatcher)(struct tipc_port *,
+                                 struct sk_buff *),
+                                 void (*wakeup)(struct tipc_port *),
+                                 const u32 importance)
 {
        struct tipc_port *p_ptr;
        struct tipc_msg *msg;
@@ -231,14 +228,13 @@ struct tipc_port *tipc_createport_raw(void *usr_handle,
                return NULL;
        }
 
-       p_ptr->usr_handle = usr_handle;
+       p_ptr->sk = sk;
        p_ptr->max_pkt = MAX_PKT_DEFAULT;
        p_ptr->ref = ref;
        INIT_LIST_HEAD(&p_ptr->wait_list);
        INIT_LIST_HEAD(&p_ptr->subscription.nodesub_list);
        p_ptr->dispatcher = dispatcher;
        p_ptr->wakeup = wakeup;
-       p_ptr->user_port = NULL;
        k_init_timer(&p_ptr->timer, (Handler)port_timeout, ref);
        INIT_LIST_HEAD(&p_ptr->publications);
        INIT_LIST_HEAD(&p_ptr->port_list);
@@ -275,7 +271,6 @@ int tipc_deleteport(u32 ref)
                buf = port_build_peer_abort_msg(p_ptr, TIPC_ERR_NO_PORT);
                tipc_nodesub_unsubscribe(&p_ptr->subscription);
        }
-       kfree(p_ptr->user_port);
 
        spin_lock_bh(&tipc_port_list_lock);
        list_del(&p_ptr->port_list);
@@ -448,7 +443,7 @@ int tipc_port_reject_sections(struct tipc_port *p_ptr, struct tipc_msg *hdr,
        int res;
 
        res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, MAX_MSG_SIZE,
-                       !p_ptr->user_port, &buf);
+                            &buf);
        if (!buf)
                return res;
 
@@ -668,215 +663,6 @@ void tipc_port_reinit(void)
        spin_unlock_bh(&tipc_port_list_lock);
 }
 
-
-/*
- *  port_dispatcher_sigh(): Signal handler for messages destinated
- *                          to the tipc_port interface.
- */
-static void port_dispatcher_sigh(void *dummy)
-{
-       struct sk_buff *buf;
-
-       spin_lock_bh(&queue_lock);
-       buf = msg_queue_head;
-       msg_queue_head = NULL;
-       spin_unlock_bh(&queue_lock);
-
-       while (buf) {
-               struct tipc_port *p_ptr;
-               struct user_port *up_ptr;
-               struct tipc_portid orig;
-               struct tipc_name_seq dseq;
-               void *usr_handle;
-               int connected;
-               int peer_invalid;
-               int published;
-               u32 message_type;
-
-               struct sk_buff *next = buf->next;
-               struct tipc_msg *msg = buf_msg(buf);
-               u32 dref = msg_destport(msg);
-
-               message_type = msg_type(msg);
-               if (message_type > TIPC_DIRECT_MSG)
-                       goto reject;    /* Unsupported message type */
-
-               p_ptr = tipc_port_lock(dref);
-               if (!p_ptr)
-                       goto reject;    /* Port deleted while msg in queue */
-
-               orig.ref = msg_origport(msg);
-               orig.node = msg_orignode(msg);
-               up_ptr = p_ptr->user_port;
-               usr_handle = up_ptr->usr_handle;
-               connected = p_ptr->connected;
-               peer_invalid = connected && !tipc_port_peer_msg(p_ptr, msg);
-               published = p_ptr->published;
-
-               if (unlikely(msg_errcode(msg)))
-                       goto err;
-
-               switch (message_type) {
-
-               case TIPC_CONN_MSG:{
-                               tipc_conn_msg_event cb = up_ptr->conn_msg_cb;
-                               u32 dsz;
-
-                               tipc_port_unlock(p_ptr);
-                               if (unlikely(!cb))
-                                       goto reject;
-                               if (unlikely(!connected)) {
-                                       if (tipc_connect(dref, &orig))
-                                               goto reject;
-                               } else if (peer_invalid)
-                                       goto reject;
-                               dsz = msg_data_sz(msg);
-                               if (unlikely(dsz &&
-                                            (++p_ptr->conn_unacked >=
-                                             TIPC_FLOW_CONTROL_WIN)))
-                                       tipc_acknowledge(dref,
-                                                        p_ptr->conn_unacked);
-                               skb_pull(buf, msg_hdr_sz(msg));
-                               cb(usr_handle, dref, &buf, msg_data(msg), dsz);
-                               break;
-                       }
-               case TIPC_DIRECT_MSG:{
-                               tipc_msg_event cb = up_ptr->msg_cb;
-
-                               tipc_port_unlock(p_ptr);
-                               if (unlikely(!cb || connected))
-                                       goto reject;
-                               skb_pull(buf, msg_hdr_sz(msg));
-                               cb(usr_handle, dref, &buf, msg_data(msg),
-                                  msg_data_sz(msg), msg_importance(msg),
-                                  &orig);
-                               break;
-                       }
-               case TIPC_MCAST_MSG:
-               case TIPC_NAMED_MSG:{
-                               tipc_named_msg_event cb = up_ptr->named_msg_cb;
-
-                               tipc_port_unlock(p_ptr);
-                               if (unlikely(!cb || connected || !published))
-                                       goto reject;
-                               dseq.type =  msg_nametype(msg);
-                               dseq.lower = msg_nameinst(msg);
-                               dseq.upper = (message_type == TIPC_NAMED_MSG)
-                                       ? dseq.lower : msg_nameupper(msg);
-                               skb_pull(buf, msg_hdr_sz(msg));
-                               cb(usr_handle, dref, &buf, msg_data(msg),
-                                  msg_data_sz(msg), msg_importance(msg),
-                                  &orig, &dseq);
-                               break;
-                       }
-               }
-               if (buf)
-                       kfree_skb(buf);
-               buf = next;
-               continue;
-err:
-               switch (message_type) {
-
-               case TIPC_CONN_MSG:{
-                               tipc_conn_shutdown_event cb =
-                                       up_ptr->conn_err_cb;
-
-                               tipc_port_unlock(p_ptr);
-                               if (!cb || !connected || peer_invalid)
-                                       break;
-                               tipc_disconnect(dref);
-                               skb_pull(buf, msg_hdr_sz(msg));
-                               cb(usr_handle, dref, &buf, msg_data(msg),
-                                  msg_data_sz(msg), msg_errcode(msg));
-                               break;
-                       }
-               case TIPC_DIRECT_MSG:{
-                               tipc_msg_err_event cb = up_ptr->err_cb;
-
-                               tipc_port_unlock(p_ptr);
-                               if (!cb || connected)
-                                       break;
-                               skb_pull(buf, msg_hdr_sz(msg));
-                               cb(usr_handle, dref, &buf, msg_data(msg),
-                                  msg_data_sz(msg), msg_errcode(msg), &orig);
-                               break;
-                       }
-               case TIPC_MCAST_MSG:
-               case TIPC_NAMED_MSG:{
-                               tipc_named_msg_err_event cb =
-                                       up_ptr->named_err_cb;
-
-                               tipc_port_unlock(p_ptr);
-                               if (!cb || connected)
-                                       break;
-                               dseq.type =  msg_nametype(msg);
-                               dseq.lower = msg_nameinst(msg);
-                               dseq.upper = (message_type == TIPC_NAMED_MSG)
-                                       ? dseq.lower : msg_nameupper(msg);
-                               skb_pull(buf, msg_hdr_sz(msg));
-                               cb(usr_handle, dref, &buf, msg_data(msg),
-                                  msg_data_sz(msg), msg_errcode(msg), &dseq);
-                               break;
-                       }
-               }
-               if (buf)
-                       kfree_skb(buf);
-               buf = next;
-               continue;
-reject:
-               tipc_reject_msg(buf, TIPC_ERR_NO_PORT);
-               buf = next;
-       }
-}
-
-/*
- *  port_dispatcher(): Dispatcher for messages destinated
- *  to the tipc_port interface. Called with port locked.
- */
-static u32 port_dispatcher(struct tipc_port *dummy, struct sk_buff *buf)
-{
-       buf->next = NULL;
-       spin_lock_bh(&queue_lock);
-       if (msg_queue_head) {
-               msg_queue_tail->next = buf;
-               msg_queue_tail = buf;
-       } else {
-               msg_queue_tail = msg_queue_head = buf;
-               tipc_k_signal((Handler)port_dispatcher_sigh, 0);
-       }
-       spin_unlock_bh(&queue_lock);
-       return 0;
-}
-
-/*
- * Wake up port after congestion: Called with port locked
- */
-static void port_wakeup_sh(unsigned long ref)
-{
-       struct tipc_port *p_ptr;
-       struct user_port *up_ptr;
-       tipc_continue_event cb = NULL;
-       void *uh = NULL;
-
-       p_ptr = tipc_port_lock(ref);
-       if (p_ptr) {
-               up_ptr = p_ptr->user_port;
-               if (up_ptr) {
-                       cb = up_ptr->continue_event_cb;
-                       uh = up_ptr->usr_handle;
-               }
-               tipc_port_unlock(p_ptr);
-       }
-       if (cb)
-               cb(uh, ref);
-}
-
-
-static void port_wakeup(struct tipc_port *p_ptr)
-{
-       tipc_k_signal((Handler)port_wakeup_sh, p_ptr->ref);
-}
-
 void tipc_acknowledge(u32 ref, u32 ack)
 {
        struct tipc_port *p_ptr;
@@ -893,50 +679,6 @@ void tipc_acknowledge(u32 ref, u32 ack)
        tipc_net_route_msg(buf);
 }
 
-/*
- * tipc_createport(): user level call.
- */
-int tipc_createport(void *usr_handle,
-                   unsigned int importance,
-                   tipc_msg_err_event error_cb,
-                   tipc_named_msg_err_event named_error_cb,
-                   tipc_conn_shutdown_event conn_error_cb,
-                   tipc_msg_event msg_cb,
-                   tipc_named_msg_event named_msg_cb,
-                   tipc_conn_msg_event conn_msg_cb,
-                   tipc_continue_event continue_event_cb, /* May be zero */
-                   u32 *portref)
-{
-       struct user_port *up_ptr;
-       struct tipc_port *p_ptr;
-
-       up_ptr = kmalloc(sizeof(*up_ptr), GFP_ATOMIC);
-       if (!up_ptr) {
-               pr_warn("Port creation failed, no memory\n");
-               return -ENOMEM;
-       }
-       p_ptr = tipc_createport_raw(NULL, port_dispatcher, port_wakeup,
-                                   importance);
-       if (!p_ptr) {
-               kfree(up_ptr);
-               return -ENOMEM;
-       }
-
-       p_ptr->user_port = up_ptr;
-       up_ptr->usr_handle = usr_handle;
-       up_ptr->ref = p_ptr->ref;
-       up_ptr->err_cb = error_cb;
-       up_ptr->named_err_cb = named_error_cb;
-       up_ptr->conn_err_cb = conn_error_cb;
-       up_ptr->msg_cb = msg_cb;
-       up_ptr->named_msg_cb = named_msg_cb;
-       up_ptr->conn_msg_cb = conn_msg_cb;
-       up_ptr->continue_event_cb = continue_event_cb;
-       *portref = p_ptr->ref;
-       tipc_port_unlock(p_ptr);
-       return 0;
-}
-
 int tipc_portimportance(u32 ref, unsigned int *importance)
 {
        struct tipc_port *p_ptr;
@@ -1184,7 +926,7 @@ static int tipc_port_recv_sections(struct tipc_port *sender, unsigned int num_se
        int res;
 
        res = tipc_msg_build(&sender->phdr, msg_sect, num_sect, total_len,
-                       MAX_MSG_SIZE, !sender->user_port, &buf);
+                            MAX_MSG_SIZE, &buf);
        if (likely(buf))
                tipc_port_recv_msg(buf);
        return res;
@@ -1322,43 +1064,3 @@ int tipc_send2port(u32 ref, struct tipc_portid const *dest,
        }
        return -ELINKCONG;
 }
-
-/**
- * tipc_send_buf2port - send message buffer to port identity
- */
-int tipc_send_buf2port(u32 ref, struct tipc_portid const *dest,
-              struct sk_buff *buf, unsigned int dsz)
-{
-       struct tipc_port *p_ptr;
-       struct tipc_msg *msg;
-       int res;
-
-       p_ptr = (struct tipc_port *)tipc_ref_deref(ref);
-       if (!p_ptr || p_ptr->connected)
-               return -EINVAL;
-
-       msg = &p_ptr->phdr;
-       msg_set_type(msg, TIPC_DIRECT_MSG);
-       msg_set_destnode(msg, dest->node);
-       msg_set_destport(msg, dest->ref);
-       msg_set_hdr_sz(msg, BASIC_H_SIZE);
-       msg_set_size(msg, BASIC_H_SIZE + dsz);
-       if (skb_cow(buf, BASIC_H_SIZE))
-               return -ENOMEM;
-
-       skb_push(buf, BASIC_H_SIZE);
-       skb_copy_to_linear_data(buf, msg, BASIC_H_SIZE);
-
-       if (in_own_node(dest->node))
-               res = tipc_port_recv_msg(buf);
-       else
-               res = tipc_send_buf_fast(buf, dest->node);
-       if (likely(res != -ELINKCONG)) {
-               if (res > 0)
-                       p_ptr->sent++;
-               return res;
-       }
-       if (port_unreliable(p_ptr))
-               return dsz;
-       return -ELINKCONG;
-}
index fb66e2e..5a7026b 100644 (file)
@@ -2,7 +2,7 @@
  * net/tipc/port.h: Include file for TIPC port code
  *
  * Copyright (c) 1994-2007, Ericsson AB
- * Copyright (c) 2004-2007, 2010-2011, Wind River Systems
+ * Copyright (c) 2004-2007, 2010-2013, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "node_subscr.h"
 
 #define TIPC_FLOW_CONTROL_WIN 512
-
-typedef void (*tipc_msg_err_event) (void *usr_handle, u32 portref,
-               struct sk_buff **buf, unsigned char const *data,
-               unsigned int size, int reason,
-               struct tipc_portid const *attmpt_destid);
-
-typedef void (*tipc_named_msg_err_event) (void *usr_handle, u32 portref,
-               struct sk_buff **buf, unsigned char const *data,
-               unsigned int size, int reason,
-               struct tipc_name_seq const *attmpt_dest);
-
-typedef void (*tipc_conn_shutdown_event) (void *usr_handle, u32 portref,
-               struct sk_buff **buf, unsigned char const *data,
-               unsigned int size, int reason);
-
-typedef void (*tipc_msg_event) (void *usr_handle, u32 portref,
-               struct sk_buff **buf, unsigned char const *data,
-               unsigned int size, unsigned int importance,
-               struct tipc_portid const *origin);
-
-typedef void (*tipc_named_msg_event) (void *usr_handle, u32 portref,
-               struct sk_buff **buf, unsigned char const *data,
-               unsigned int size, unsigned int importance,
-               struct tipc_portid const *orig,
-               struct tipc_name_seq const *dest);
-
-typedef void (*tipc_conn_msg_event) (void *usr_handle, u32 portref,
-               struct sk_buff **buf, unsigned char const *data,
-               unsigned int size);
-
-typedef void (*tipc_continue_event) (void *usr_handle, u32 portref);
-
-/**
- * struct user_port - TIPC user port (used with native API)
- * @usr_handle: user-specified field
- * @ref: object reference to associated TIPC port
- *
- * <various callback routines>
- */
-struct user_port {
-       void *usr_handle;
-       u32 ref;
-       tipc_msg_err_event err_cb;
-       tipc_named_msg_err_event named_err_cb;
-       tipc_conn_shutdown_event conn_err_cb;
-       tipc_msg_event msg_cb;
-       tipc_named_msg_event named_msg_cb;
-       tipc_conn_msg_event conn_msg_cb;
-       tipc_continue_event continue_event_cb;
-};
+#define CONN_OVERLOAD_LIMIT    ((TIPC_FLOW_CONTROL_WIN * 2 + 1) * \
+                               SKB_TRUESIZE(TIPC_MAX_USER_MSG_SIZE))
 
 /**
  * struct tipc_port - TIPC port structure
- * @usr_handle: pointer to additional user-defined information about port
+ * @sk: pointer to socket handle
  * @lock: pointer to spinlock for controlling access to port
  * @connected: non-zero if port is currently connected to a peer port
  * @conn_type: TIPC type used when connection was established
@@ -110,7 +62,6 @@ struct user_port {
  * @port_list: adjacent ports in TIPC's global list of ports
  * @dispatcher: ptr to routine which handles received messages
  * @wakeup: ptr to routine to call when port is no longer congested
- * @user_port: ptr to user port associated with port (if any)
  * @wait_list: adjacent ports in list of ports waiting on link congestion
  * @waiting_pkts:
  * @sent: # of non-empty messages sent by port
@@ -123,7 +74,7 @@ struct user_port {
  * @subscription: "node down" subscription used to terminate failed connections
  */
 struct tipc_port {
-       void *usr_handle;
+       struct sock *sk;
        spinlock_t *lock;
        int connected;
        u32 conn_type;
@@ -137,7 +88,6 @@ struct tipc_port {
        struct list_head port_list;
        u32 (*dispatcher)(struct tipc_port *, struct sk_buff *);
        void (*wakeup)(struct tipc_port *);
-       struct user_port *user_port;
        struct list_head wait_list;
        u32 waiting_pkts;
        u32 sent;
@@ -156,24 +106,16 @@ struct tipc_port_list;
 /*
  * TIPC port manipulation routines
  */
-struct tipc_port *tipc_createport_raw(void *usr_handle,
-               u32 (*dispatcher)(struct tipc_port *, struct sk_buff *),
-               void (*wakeup)(struct tipc_port *), const u32 importance);
+struct tipc_port *tipc_createport(struct sock *sk,
+                                 u32 (*dispatcher)(struct tipc_port *,
+                                 struct sk_buff *),
+                                 void (*wakeup)(struct tipc_port *),
+                                 const u32 importance);
 
 int tipc_reject_msg(struct sk_buff *buf, u32 err);
 
-int tipc_send_buf_fast(struct sk_buff *buf, u32 destnode);
-
 void tipc_acknowledge(u32 port_ref, u32 ack);
 
-int tipc_createport(void *usr_handle,
-               unsigned int importance, tipc_msg_err_event error_cb,
-               tipc_named_msg_err_event named_error_cb,
-               tipc_conn_shutdown_event conn_error_cb, tipc_msg_event msg_cb,
-               tipc_named_msg_event named_msg_cb,
-               tipc_conn_msg_event conn_msg_cb,
-               tipc_continue_event continue_event_cb, u32 *portref);
-
 int tipc_deleteport(u32 portref);
 
 int tipc_portimportance(u32 portref, unsigned int *importance);
@@ -186,9 +128,9 @@ int tipc_portunreturnable(u32 portref, unsigned int *isunreturnable);
 int tipc_set_portunreturnable(u32 portref, unsigned int isunreturnable);
 
 int tipc_publish(u32 portref, unsigned int scope,
-               struct tipc_name_seq const *name_seq);
+                struct tipc_name_seq const *name_seq);
 int tipc_withdraw(u32 portref, unsigned int scope,
-               struct tipc_name_seq const *name_seq);
+                 struct tipc_name_seq const *name_seq);
 
 int tipc_connect(u32 portref, struct tipc_portid const *port);
 
@@ -220,9 +162,6 @@ int tipc_send2port(u32 portref, struct tipc_portid const *dest,
                   unsigned int num_sect, struct iovec const *msg_sect,
                   unsigned int total_len);
 
-int tipc_send_buf2port(u32 portref, struct tipc_portid const *dest,
-               struct sk_buff *buf, unsigned int dsz);
-
 int tipc_multicast(u32 portref, struct tipc_name_seq const *seq,
                   unsigned int section_count, struct iovec const *msg,
                   unsigned int total_len);
diff --git a/net/tipc/server.c b/net/tipc/server.c
new file mode 100644 (file)
index 0000000..19da5ab
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * net/tipc/server.c: TIPC server infrastructure
+ *
+ * Copyright (c) 2012-2013, Wind River Systems
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders 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") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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 "server.h"
+#include "core.h"
+#include <net/sock.h>
+
+/* Number of messages to send before rescheduling */
+#define MAX_SEND_MSG_COUNT     25
+#define MAX_RECV_MSG_COUNT     25
+#define CF_CONNECTED           1
+
+#define sock2con(x) ((struct tipc_conn *)(x)->sk_user_data)
+
+/**
+ * struct tipc_conn - TIPC connection structure
+ * @kref: reference counter to connection object
+ * @conid: connection identifier
+ * @sock: socket handler associated with connection
+ * @flags: indicates connection state
+ * @server: pointer to connected server
+ * @rwork: receive work item
+ * @usr_data: user-specified field
+ * @rx_action: what to do when connection socket is active
+ * @outqueue: pointer to first outbound message in queue
+ * @outqueue_lock: controll access to the outqueue
+ * @outqueue: list of connection objects for its server
+ * @swork: send work item
+ */
+struct tipc_conn {
+       struct kref kref;
+       int conid;
+       struct socket *sock;
+       unsigned long flags;
+       struct tipc_server *server;
+       struct work_struct rwork;
+       int (*rx_action) (struct tipc_conn *con);
+       void *usr_data;
+       struct list_head outqueue;
+       spinlock_t outqueue_lock;
+       struct work_struct swork;
+};
+
+/* An entry waiting to be sent */
+struct outqueue_entry {
+       struct list_head list;
+       struct kvec iov;
+       struct sockaddr_tipc dest;
+};
+
+static void tipc_recv_work(struct work_struct *work);
+static void tipc_send_work(struct work_struct *work);
+static void tipc_clean_outqueues(struct tipc_conn *con);
+
+static void tipc_conn_kref_release(struct kref *kref)
+{
+       struct tipc_conn *con = container_of(kref, struct tipc_conn, kref);
+       struct tipc_server *s = con->server;
+
+       if (con->sock) {
+               tipc_sock_release_local(con->sock);
+               con->sock = NULL;
+       }
+
+       tipc_clean_outqueues(con);
+
+       if (con->conid)
+               s->tipc_conn_shutdown(con->conid, con->usr_data);
+
+       kfree(con);
+}
+
+static void conn_put(struct tipc_conn *con)
+{
+       kref_put(&con->kref, tipc_conn_kref_release);
+}
+
+static void conn_get(struct tipc_conn *con)
+{
+       kref_get(&con->kref);
+}
+
+static struct tipc_conn *tipc_conn_lookup(struct tipc_server *s, int conid)
+{
+       struct tipc_conn *con;
+
+       spin_lock_bh(&s->idr_lock);
+       con = idr_find(&s->conn_idr, conid);
+       if (con)
+               conn_get(con);
+       spin_unlock_bh(&s->idr_lock);
+       return con;
+}
+
+static void sock_data_ready(struct sock *sk, int unused)
+{
+       struct tipc_conn *con;
+
+       read_lock(&sk->sk_callback_lock);
+       con = sock2con(sk);
+       if (con && test_bit(CF_CONNECTED, &con->flags)) {
+               conn_get(con);
+               if (!queue_work(con->server->rcv_wq, &con->rwork))
+                       conn_put(con);
+       }
+       read_unlock(&sk->sk_callback_lock);
+}
+
+static void sock_write_space(struct sock *sk)
+{
+       struct tipc_conn *con;
+
+       read_lock(&sk->sk_callback_lock);
+       con = sock2con(sk);
+       if (con && test_bit(CF_CONNECTED, &con->flags)) {
+               conn_get(con);
+               if (!queue_work(con->server->send_wq, &con->swork))
+                       conn_put(con);
+       }
+       read_unlock(&sk->sk_callback_lock);
+}
+
+static void tipc_register_callbacks(struct socket *sock, struct tipc_conn *con)
+{
+       struct sock *sk = sock->sk;
+
+       write_lock_bh(&sk->sk_callback_lock);
+
+       sk->sk_data_ready = sock_data_ready;
+       sk->sk_write_space = sock_write_space;
+       sk->sk_user_data = con;
+
+       con->sock = sock;
+
+       write_unlock_bh(&sk->sk_callback_lock);
+}
+
+static void tipc_unregister_callbacks(struct tipc_conn *con)
+{
+       struct sock *sk = con->sock->sk;
+
+       write_lock_bh(&sk->sk_callback_lock);
+       sk->sk_user_data = NULL;
+       write_unlock_bh(&sk->sk_callback_lock);
+}
+
+static void tipc_close_conn(struct tipc_conn *con)
+{
+       struct tipc_server *s = con->server;
+
+       if (test_and_clear_bit(CF_CONNECTED, &con->flags)) {
+               spin_lock_bh(&s->idr_lock);
+               idr_remove(&s->conn_idr, con->conid);
+               s->idr_in_use--;
+               spin_unlock_bh(&s->idr_lock);
+
+               tipc_unregister_callbacks(con);
+
+               /* We shouldn't flush pending works as we may be in the
+                * thread. In fact the races with pending rx/tx work structs
+                * are harmless for us here as we have already deleted this
+                * connection from server connection list and set
+                * sk->sk_user_data to 0 before releasing connection object.
+                */
+               kernel_sock_shutdown(con->sock, SHUT_RDWR);
+
+               conn_put(con);
+       }
+}
+
+static struct tipc_conn *tipc_alloc_conn(struct tipc_server *s)
+{
+       struct tipc_conn *con;
+       int ret;
+
+       con = kzalloc(sizeof(struct tipc_conn), GFP_ATOMIC);
+       if (!con)
+               return ERR_PTR(-ENOMEM);
+
+       kref_init(&con->kref);
+       INIT_LIST_HEAD(&con->outqueue);
+       spin_lock_init(&con->outqueue_lock);
+       INIT_WORK(&con->swork, tipc_send_work);
+       INIT_WORK(&con->rwork, tipc_recv_work);
+
+       spin_lock_bh(&s->idr_lock);
+       ret = idr_alloc(&s->conn_idr, con, 0, 0, GFP_ATOMIC);
+       if (ret < 0) {
+               kfree(con);
+               spin_unlock_bh(&s->idr_lock);
+               return ERR_PTR(-ENOMEM);
+       }
+       con->conid = ret;
+       s->idr_in_use++;
+       spin_unlock_bh(&s->idr_lock);
+
+       set_bit(CF_CONNECTED, &con->flags);
+       con->server = s;
+
+       return con;
+}
+
+static int tipc_receive_from_sock(struct tipc_conn *con)
+{
+       struct msghdr msg = {};
+       struct tipc_server *s = con->server;
+       struct sockaddr_tipc addr;
+       struct kvec iov;
+       void *buf;
+       int ret;
+
+       buf = kmem_cache_alloc(s->rcvbuf_cache, GFP_ATOMIC);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto out_close;
+       }
+
+       iov.iov_base = buf;
+       iov.iov_len = s->max_rcvbuf_size;
+       msg.msg_name = &addr;
+       ret = kernel_recvmsg(con->sock, &msg, &iov, 1, iov.iov_len,
+                            MSG_DONTWAIT);
+       if (ret <= 0) {
+               kmem_cache_free(s->rcvbuf_cache, buf);
+               goto out_close;
+       }
+
+       s->tipc_conn_recvmsg(con->conid, &addr, con->usr_data, buf, ret);
+
+       kmem_cache_free(s->rcvbuf_cache, buf);
+
+       return 0;
+
+out_close:
+       if (ret != -EWOULDBLOCK)
+               tipc_close_conn(con);
+       else if (ret == 0)
+               /* Don't return success if we really got EOF */
+               ret = -EAGAIN;
+
+       return ret;
+}
+
+static int tipc_accept_from_sock(struct tipc_conn *con)
+{
+       struct tipc_server *s = con->server;
+       struct socket *sock = con->sock;
+       struct socket *newsock;
+       struct tipc_conn *newcon;
+       int ret;
+
+       ret = tipc_sock_accept_local(sock, &newsock, O_NONBLOCK);
+       if (ret < 0)
+               return ret;
+
+       newcon = tipc_alloc_conn(con->server);
+       if (IS_ERR(newcon)) {
+               ret = PTR_ERR(newcon);
+               sock_release(newsock);
+               return ret;
+       }
+
+       newcon->rx_action = tipc_receive_from_sock;
+       tipc_register_callbacks(newsock, newcon);
+
+       /* Notify that new connection is incoming */
+       newcon->usr_data = s->tipc_conn_new(newcon->conid);
+
+       /* Wake up receive process in case of 'SYN+' message */
+       newsock->sk->sk_data_ready(newsock->sk, 0);
+       return ret;
+}
+
+static struct socket *tipc_create_listen_sock(struct tipc_conn *con)
+{
+       struct tipc_server *s = con->server;
+       struct socket *sock = NULL;
+       int ret;
+
+       ret = tipc_sock_create_local(s->type, &sock);
+       if (ret < 0)
+               return NULL;
+       ret = kernel_setsockopt(sock, SOL_TIPC, TIPC_IMPORTANCE,
+                               (char *)&s->imp, sizeof(s->imp));
+       if (ret < 0)
+               goto create_err;
+       ret = kernel_bind(sock, (struct sockaddr *)s->saddr, sizeof(*s->saddr));
+       if (ret < 0)
+               goto create_err;
+
+       switch (s->type) {
+       case SOCK_STREAM:
+       case SOCK_SEQPACKET:
+               con->rx_action = tipc_accept_from_sock;
+
+               ret = kernel_listen(sock, 0);
+               if (ret < 0)
+                       goto create_err;
+               break;
+       case SOCK_DGRAM:
+       case SOCK_RDM:
+               con->rx_action = tipc_receive_from_sock;
+               break;
+       default:
+               pr_err("Unknown socket type %d\n", s->type);
+               goto create_err;
+       }
+       return sock;
+
+create_err:
+       sock_release(sock);
+       con->sock = NULL;
+       return NULL;
+}
+
+static int tipc_open_listening_sock(struct tipc_server *s)
+{
+       struct socket *sock;
+       struct tipc_conn *con;
+
+       con = tipc_alloc_conn(s);
+       if (IS_ERR(con))
+               return PTR_ERR(con);
+
+       sock = tipc_create_listen_sock(con);
+       if (!sock)
+               return -EINVAL;
+
+       tipc_register_callbacks(sock, con);
+       return 0;
+}
+
+static struct outqueue_entry *tipc_alloc_entry(void *data, int len)
+{
+       struct outqueue_entry *entry;
+       void *buf;
+
+       entry = kmalloc(sizeof(struct outqueue_entry), GFP_ATOMIC);
+       if (!entry)
+               return NULL;
+
+       buf = kmalloc(len, GFP_ATOMIC);
+       if (!buf) {
+               kfree(entry);
+               return NULL;
+       }
+
+       memcpy(buf, data, len);
+       entry->iov.iov_base = buf;
+       entry->iov.iov_len = len;
+
+       return entry;
+}
+
+static void tipc_free_entry(struct outqueue_entry *e)
+{
+       kfree(e->iov.iov_base);
+       kfree(e);
+}
+
+static void tipc_clean_outqueues(struct tipc_conn *con)
+{
+       struct outqueue_entry *e, *safe;
+
+       spin_lock_bh(&con->outqueue_lock);
+       list_for_each_entry_safe(e, safe, &con->outqueue, list) {
+               list_del(&e->list);
+               tipc_free_entry(e);
+       }
+       spin_unlock_bh(&con->outqueue_lock);
+}
+
+int tipc_conn_sendmsg(struct tipc_server *s, int conid,
+                     struct sockaddr_tipc *addr, void *data, size_t len)
+{
+       struct outqueue_entry *e;
+       struct tipc_conn *con;
+
+       con = tipc_conn_lookup(s, conid);
+       if (!con)
+               return -EINVAL;
+
+       e = tipc_alloc_entry(data, len);
+       if (!e) {
+               conn_put(con);
+               return -ENOMEM;
+       }
+
+       if (addr)
+               memcpy(&e->dest, addr, sizeof(struct sockaddr_tipc));
+
+       spin_lock_bh(&con->outqueue_lock);
+       list_add_tail(&e->list, &con->outqueue);
+       spin_unlock_bh(&con->outqueue_lock);
+
+       if (test_bit(CF_CONNECTED, &con->flags))
+               if (!queue_work(s->send_wq, &con->swork))
+                       conn_put(con);
+
+       return 0;
+}
+
+void tipc_conn_terminate(struct tipc_server *s, int conid)
+{
+       struct tipc_conn *con;
+
+       con = tipc_conn_lookup(s, conid);
+       if (con) {
+               tipc_close_conn(con);
+               conn_put(con);
+       }
+}
+
+static void tipc_send_to_sock(struct tipc_conn *con)
+{
+       int count = 0;
+       struct tipc_server *s = con->server;
+       struct outqueue_entry *e;
+       struct msghdr msg;
+       int ret;
+
+       spin_lock_bh(&con->outqueue_lock);
+       while (1) {
+               e = list_entry(con->outqueue.next, struct outqueue_entry,
+                              list);
+               if ((struct list_head *) e == &con->outqueue)
+                       break;
+               spin_unlock_bh(&con->outqueue_lock);
+
+               memset(&msg, 0, sizeof(msg));
+               msg.msg_flags = MSG_DONTWAIT;
+
+               if (s->type == SOCK_DGRAM || s->type == SOCK_RDM) {
+                       msg.msg_name = &e->dest;
+                       msg.msg_namelen = sizeof(struct sockaddr_tipc);
+               }
+               ret = kernel_sendmsg(con->sock, &msg, &e->iov, 1,
+                                    e->iov.iov_len);
+               if (ret == -EWOULDBLOCK || ret == 0) {
+                       cond_resched();
+                       goto out;
+               } else if (ret < 0) {
+                       goto send_err;
+               }
+
+               /* Don't starve users filling buffers */
+               if (++count >= MAX_SEND_MSG_COUNT) {
+                       cond_resched();
+                       count = 0;
+               }
+
+               spin_lock_bh(&con->outqueue_lock);
+               list_del(&e->list);
+               tipc_free_entry(e);
+       }
+       spin_unlock_bh(&con->outqueue_lock);
+out:
+       return;
+
+send_err:
+       tipc_close_conn(con);
+}
+
+static void tipc_recv_work(struct work_struct *work)
+{
+       struct tipc_conn *con = container_of(work, struct tipc_conn, rwork);
+       int count = 0;
+
+       while (test_bit(CF_CONNECTED, &con->flags)) {
+               if (con->rx_action(con))
+                       break;
+
+               /* Don't flood Rx machine */
+               if (++count >= MAX_RECV_MSG_COUNT) {
+                       cond_resched();
+                       count = 0;
+               }
+       }
+       conn_put(con);
+}
+
+static void tipc_send_work(struct work_struct *work)
+{
+       struct tipc_conn *con = container_of(work, struct tipc_conn, swork);
+
+       if (test_bit(CF_CONNECTED, &con->flags))
+               tipc_send_to_sock(con);
+
+       conn_put(con);
+}
+
+static void tipc_work_stop(struct tipc_server *s)
+{
+       destroy_workqueue(s->rcv_wq);
+       destroy_workqueue(s->send_wq);
+}
+
+static int tipc_work_start(struct tipc_server *s)
+{
+       s->rcv_wq = alloc_workqueue("tipc_rcv", WQ_UNBOUND, 1);
+       if (!s->rcv_wq) {
+               pr_err("can't start tipc receive workqueue\n");
+               return -ENOMEM;
+       }
+
+       s->send_wq = alloc_workqueue("tipc_send", WQ_UNBOUND, 1);
+       if (!s->send_wq) {
+               pr_err("can't start tipc send workqueue\n");
+               destroy_workqueue(s->rcv_wq);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+int tipc_server_start(struct tipc_server *s)
+{
+       int ret;
+
+       spin_lock_init(&s->idr_lock);
+       idr_init(&s->conn_idr);
+       s->idr_in_use = 0;
+
+       s->rcvbuf_cache = kmem_cache_create(s->name, s->max_rcvbuf_size,
+                                           0, SLAB_HWCACHE_ALIGN, NULL);
+       if (!s->rcvbuf_cache)
+               return -ENOMEM;
+
+       ret = tipc_work_start(s);
+       if (ret < 0) {
+               kmem_cache_destroy(s->rcvbuf_cache);
+               return ret;
+       }
+       s->enabled = 1;
+
+       return tipc_open_listening_sock(s);
+}
+
+void tipc_server_stop(struct tipc_server *s)
+{
+       struct tipc_conn *con;
+       int total = 0;
+       int id;
+
+       if (!s->enabled)
+               return;
+
+       s->enabled = 0;
+       spin_lock_bh(&s->idr_lock);
+       for (id = 0; total < s->idr_in_use; id++) {
+               con = idr_find(&s->conn_idr, id);
+               if (con) {
+                       total++;
+                       spin_unlock_bh(&s->idr_lock);
+                       tipc_close_conn(con);
+                       spin_lock_bh(&s->idr_lock);
+               }
+       }
+       spin_unlock_bh(&s->idr_lock);
+
+       tipc_work_stop(s);
+       kmem_cache_destroy(s->rcvbuf_cache);
+       idr_destroy(&s->conn_idr);
+}
diff --git a/net/tipc/server.h b/net/tipc/server.h
new file mode 100644 (file)
index 0000000..98b23f2
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * net/tipc/server.h: Include file for TIPC server code
+ *
+ * Copyright (c) 2012-2013, Wind River Systems
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders 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") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _TIPC_SERVER_H
+#define _TIPC_SERVER_H
+
+#include "core.h"
+
+#define TIPC_SERVER_NAME_LEN   32
+
+/**
+ * struct tipc_server - TIPC server structure
+ * @conn_idr: identifier set of connection
+ * @idr_lock: protect the connection identifier set
+ * @idr_in_use: amount of allocated identifier entry
+ * @rcvbuf_cache: memory cache of server receive buffer
+ * @rcv_wq: receive workqueue
+ * @send_wq: send workqueue
+ * @max_rcvbuf_size: maximum permitted receive message length
+ * @tipc_conn_new: callback will be called when new connection is incoming
+ * @tipc_conn_shutdown: callback will be called when connection is shut down
+ * @tipc_conn_recvmsg: callback will be called when message arrives
+ * @saddr: TIPC server address
+ * @name: server name
+ * @imp: message importance
+ * @type: socket type
+ * @enabled: identify whether server is launched or not
+ */
+struct tipc_server {
+       struct idr conn_idr;
+       spinlock_t idr_lock;
+       int idr_in_use;
+       struct kmem_cache *rcvbuf_cache;
+       struct workqueue_struct *rcv_wq;
+       struct workqueue_struct *send_wq;
+       int max_rcvbuf_size;
+       void *(*tipc_conn_new) (int conid);
+       void (*tipc_conn_shutdown) (int conid, void *usr_data);
+       void (*tipc_conn_recvmsg) (int conid, struct sockaddr_tipc *addr,
+                                  void *usr_data, void *buf, size_t len);
+       struct sockaddr_tipc *saddr;
+       const char name[TIPC_SERVER_NAME_LEN];
+       int imp;
+       int type;
+       int enabled;
+};
+
+int tipc_conn_sendmsg(struct tipc_server *s, int conid,
+                     struct sockaddr_tipc *addr, void *data, size_t len);
+
+/**
+ * tipc_conn_terminate - terminate connection with server
+ *
+ * Note: Must call it in process context since it might sleep
+ */
+void tipc_conn_terminate(struct tipc_server *s, int conid);
+
+int tipc_server_start(struct tipc_server *s);
+
+void tipc_server_stop(struct tipc_server *s);
+
+#endif
index 515ce38..ce8249c 100644 (file)
@@ -2,7 +2,7 @@
  * net/tipc/socket.c: TIPC socket API
  *
  * Copyright (c) 2001-2007, 2012 Ericsson AB
- * Copyright (c) 2004-2008, 2010-2012, Wind River Systems
+ * Copyright (c) 2004-2008, 2010-2013, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -43,8 +43,6 @@
 #define SS_LISTENING   -1      /* socket is listening */
 #define SS_READY       -2      /* socket is connectionless */
 
-#define CONN_OVERLOAD_LIMIT    ((TIPC_FLOW_CONTROL_WIN * 2 + 1) * \
-                               SKB_TRUESIZE(TIPC_MAX_USER_MSG_SIZE))
 #define CONN_TIMEOUT_DEFAULT   8000    /* default connect timeout = 8s */
 
 struct tipc_sock {
@@ -65,12 +63,15 @@ static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf);
 static void wakeupdispatch(struct tipc_port *tport);
 static void tipc_data_ready(struct sock *sk, int len);
 static void tipc_write_space(struct sock *sk);
+static int release(struct socket *sock);
+static int accept(struct socket *sock, struct socket *new_sock, int flags);
 
 static const struct proto_ops packet_ops;
 static const struct proto_ops stream_ops;
 static const struct proto_ops msg_ops;
 
 static struct proto tipc_proto;
+static struct proto tipc_proto_kern;
 
 static int sockets_enabled;
 
@@ -143,7 +144,7 @@ static void reject_rx_queue(struct sock *sk)
 }
 
 /**
- * tipc_create - create a TIPC socket
+ * tipc_sk_create - create a TIPC socket
  * @net: network namespace (must be default network)
  * @sock: pre-allocated socket structure
  * @protocol: protocol indicator (must be 0)
@@ -154,8 +155,8 @@ static void reject_rx_queue(struct sock *sk)
  *
  * Returns 0 on success, errno otherwise
  */
-static int tipc_create(struct net *net, struct socket *sock, int protocol,
-                      int kern)
+static int tipc_sk_create(struct net *net, struct socket *sock, int protocol,
+                         int kern)
 {
        const struct proto_ops *ops;
        socket_state state;
@@ -185,13 +186,17 @@ static int tipc_create(struct net *net, struct socket *sock, int protocol,
        }
 
        /* Allocate socket's protocol area */
-       sk = sk_alloc(net, AF_TIPC, GFP_KERNEL, &tipc_proto);
+       if (!kern)
+               sk = sk_alloc(net, AF_TIPC, GFP_KERNEL, &tipc_proto);
+       else
+               sk = sk_alloc(net, AF_TIPC, GFP_KERNEL, &tipc_proto_kern);
+
        if (sk == NULL)
                return -ENOMEM;
 
        /* Allocate TIPC port for socket to use */
-       tp_ptr = tipc_createport_raw(sk, &dispatch, &wakeupdispatch,
-                                    TIPC_LOW_IMPORTANCE);
+       tp_ptr = tipc_createport(sk, &dispatch, &wakeupdispatch,
+                                TIPC_LOW_IMPORTANCE);
        if (unlikely(!tp_ptr)) {
                sk_free(sk);
                return -ENOMEM;
@@ -203,6 +208,7 @@ static int tipc_create(struct net *net, struct socket *sock, int protocol,
 
        sock_init_data(sock, sk);
        sk->sk_backlog_rcv = backlog_rcv;
+       sk->sk_rcvbuf = sysctl_tipc_rmem[1];
        sk->sk_data_ready = tipc_data_ready;
        sk->sk_write_space = tipc_write_space;
        tipc_sk(sk)->p = tp_ptr;
@@ -219,6 +225,78 @@ static int tipc_create(struct net *net, struct socket *sock, int protocol,
        return 0;
 }
 
+/**
+ * tipc_sock_create_local - create TIPC socket from inside TIPC module
+ * @type: socket type - SOCK_RDM or SOCK_SEQPACKET
+ *
+ * We cannot use sock_creat_kern here because it bumps module user count.
+ * Since socket owner and creator is the same module we must make sure
+ * that module count remains zero for module local sockets, otherwise
+ * we cannot do rmmod.
+ *
+ * Returns 0 on success, errno otherwise
+ */
+int tipc_sock_create_local(int type, struct socket **res)
+{
+       int rc;
+       struct sock *sk;
+
+       rc = sock_create_lite(AF_TIPC, type, 0, res);
+       if (rc < 0) {
+               pr_err("Failed to create kernel socket\n");
+               return rc;
+       }
+       tipc_sk_create(&init_net, *res, 0, 1);
+
+       sk = (*res)->sk;
+
+       return 0;
+}
+
+/**
+ * tipc_sock_release_local - release socket created by tipc_sock_create_local
+ * @sock: the socket to be released.
+ *
+ * Module reference count is not incremented when such sockets are created,
+ * so we must keep it from being decremented when they are released.
+ */
+void tipc_sock_release_local(struct socket *sock)
+{
+       release(sock);
+       sock->ops = NULL;
+       sock_release(sock);
+}
+
+/**
+ * tipc_sock_accept_local - accept a connection on a socket created
+ * with tipc_sock_create_local. Use this function to avoid that
+ * module reference count is inadvertently incremented.
+ *
+ * @sock:    the accepting socket
+ * @newsock: reference to the new socket to be created
+ * @flags:   socket flags
+ */
+
+int tipc_sock_accept_local(struct socket *sock, struct socket **newsock,
+                          int flags)
+{
+       struct sock *sk = sock->sk;
+       int ret;
+
+       ret = sock_create_lite(sk->sk_family, sk->sk_type,
+                              sk->sk_protocol, newsock);
+       if (ret < 0)
+               return ret;
+
+       ret = accept(sock, *newsock, flags);
+       if (ret < 0) {
+               sock_release(*newsock);
+               return ret;
+       }
+       (*newsock)->ops = sock->ops;
+       return ret;
+}
+
 /**
  * release - destroy a TIPC socket
  * @sock: socket to destroy
@@ -324,7 +402,9 @@ static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len)
        else if (addr->addrtype != TIPC_ADDR_NAMESEQ)
                return -EAFNOSUPPORT;
 
-       if (addr->addr.nameseq.type < TIPC_RESERVED_TYPES)
+       if ((addr->addr.nameseq.type < TIPC_RESERVED_TYPES) &&
+           (addr->addr.nameseq.type != TIPC_TOP_SRV) &&
+           (addr->addr.nameseq.type != TIPC_CFG_SRV))
                return -EACCES;
 
        return (addr->scope > 0) ?
@@ -519,8 +599,7 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,
                        res = -EISCONN;
                        goto exit;
                }
-               if ((tport->published) ||
-                   ((sock->type == SOCK_STREAM) && (total_len != 0))) {
+               if (tport->published) {
                        res = -EOPNOTSUPP;
                        goto exit;
                }
@@ -810,7 +889,7 @@ static void set_orig_addr(struct msghdr *m, struct tipc_msg *msg)
  * Returns 0 if successful, otherwise errno
  */
 static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
-                               struct tipc_port *tport)
+                        struct tipc_port *tport)
 {
        u32 anc_data[3];
        u32 err;
@@ -1011,8 +1090,7 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock,
 
        lock_sock(sk);
 
-       if (unlikely((sock->state == SS_UNCONNECTED) ||
-                    (sock->state == SS_CONNECTING))) {
+       if (unlikely((sock->state == SS_UNCONNECTED))) {
                res = -ENOTCONN;
                goto exit;
        }
@@ -1233,10 +1311,10 @@ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf)
  * For all connectionless messages, by default new queue limits are
  * as belows:
  *
- * TIPC_LOW_IMPORTANCE       (5MB)
- * TIPC_MEDIUM_IMPORTANCE    (10MB)
- * TIPC_HIGH_IMPORTANCE      (20MB)
- * TIPC_CRITICAL_IMPORTANCE  (40MB)
+ * TIPC_LOW_IMPORTANCE       (MB)
+ * TIPC_MEDIUM_IMPORTANCE    (MB)
+ * TIPC_HIGH_IMPORTANCE      (16 MB)
+ * TIPC_CRITICAL_IMPORTANCE  (32 MB)
  *
  * Returns overload limit according to corresponding message importance
  */
@@ -1246,9 +1324,10 @@ static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *buf)
        unsigned int limit;
 
        if (msg_connected(msg))
-               limit = CONN_OVERLOAD_LIMIT;
+               limit = sysctl_tipc_rmem[2];
        else
-               limit = sk->sk_rcvbuf << (msg_importance(msg) + 5);
+               limit = sk->sk_rcvbuf >> TIPC_CRITICAL_IMPORTANCE <<
+                       msg_importance(msg);
        return limit;
 }
 
@@ -1327,7 +1406,7 @@ static int backlog_rcv(struct sock *sk, struct sk_buff *buf)
  */
 static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf)
 {
-       struct sock *sk = (struct sock *)tport->usr_handle;
+       struct sock *sk = tport->sk;
        u32 res;
 
        /*
@@ -1358,7 +1437,7 @@ static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf)
  */
 static void wakeupdispatch(struct tipc_port *tport)
 {
-       struct sock *sk = (struct sock *)tport->usr_handle;
+       struct sock *sk = tport->sk;
 
        sk->sk_write_space(sk);
 }
@@ -1531,7 +1610,7 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags)
 
        buf = skb_peek(&sk->sk_receive_queue);
 
-       res = tipc_create(sock_net(sock->sk), new_sock, 0, 0);
+       res = tipc_sk_create(sock_net(sock->sk), new_sock, 0, 1);
        if (res)
                goto exit;
 
@@ -1657,8 +1736,8 @@ restart:
  *
  * Returns 0 on success, errno otherwise
  */
-static int setsockopt(struct socket *sock,
-                     int lvl, int opt, char __user *ov, unsigned int ol)
+static int setsockopt(struct socket *sock, int lvl, int opt, char __user *ov,
+                     unsigned int ol)
 {
        struct sock *sk = sock->sk;
        struct tipc_port *tport = tipc_sk_port(sk);
@@ -1716,8 +1795,8 @@ static int setsockopt(struct socket *sock,
  *
  * Returns 0 on success, errno otherwise
  */
-static int getsockopt(struct socket *sock,
-                     int lvl, int opt, char __user *ov, int __user *ol)
+static int getsockopt(struct socket *sock, int lvl, int opt, char __user *ov,
+                     int __user *ol)
 {
        struct sock *sk = sock->sk;
        struct tipc_port *tport = tipc_sk_port(sk);
@@ -1841,13 +1920,20 @@ static const struct proto_ops stream_ops = {
 static const struct net_proto_family tipc_family_ops = {
        .owner          = THIS_MODULE,
        .family         = AF_TIPC,
-       .create         = tipc_create
+       .create         = tipc_sk_create
 };
 
 static struct proto tipc_proto = {
        .name           = "TIPC",
        .owner          = THIS_MODULE,
-       .obj_size       = sizeof(struct tipc_sock)
+       .obj_size       = sizeof(struct tipc_sock),
+       .sysctl_rmem    = sysctl_tipc_rmem
+};
+
+static struct proto tipc_proto_kern = {
+       .name           = "TIPC",
+       .obj_size       = sizeof(struct tipc_sock),
+       .sysctl_rmem    = sysctl_tipc_rmem
 };
 
 /**
index 6b42d47..d38bb45 100644 (file)
@@ -2,7 +2,7 @@
  * net/tipc/subscr.c: TIPC network topology service
  *
  * Copyright (c) 2000-2006, Ericsson AB
- * Copyright (c) 2005-2007, 2010-2011, Wind River Systems
+ * Copyright (c) 2005-2007, 2010-2013, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 
 /**
  * struct tipc_subscriber - TIPC network topology subscriber
- * @port_ref: object reference to server port connecting to subscriber
- * @lock: pointer to spinlock controlling access to subscriber's server port
- * @subscriber_list: adjacent subscribers in top. server's list of subscribers
+ * @conid: connection identifier to server connecting to subscriber
+ * @lock: controll access to subscriber
  * @subscription_list: list of subscription objects for this subscriber
  */
 struct tipc_subscriber {
-       u32 port_ref;
-       spinlock_t *lock;
-       struct list_head subscriber_list;
+       int conid;
+       spinlock_t lock;
        struct list_head subscription_list;
 };
 
-/**
- * struct top_srv - TIPC network topology subscription service
- * @setup_port: reference to TIPC port that handles subscription requests
- * @subscription_count: number of active subscriptions (not subscribers!)
- * @subscriber_list: list of ports subscribing to service
- * @lock: spinlock govering access to subscriber list
- */
-struct top_srv {
-       u32 setup_port;
-       atomic_t subscription_count;
-       struct list_head subscriber_list;
-       spinlock_t lock;
+static void subscr_conn_msg_event(int conid, struct sockaddr_tipc *addr,
+                                 void *usr_data, void *buf, size_t len);
+static void *subscr_named_msg_event(int conid);
+static void subscr_conn_shutdown_event(int conid, void *usr_data);
+
+static atomic_t subscription_count = ATOMIC_INIT(0);
+
+static struct sockaddr_tipc topsrv_addr __read_mostly = {
+       .family                 = AF_TIPC,
+       .addrtype               = TIPC_ADDR_NAMESEQ,
+       .addr.nameseq.type      = TIPC_TOP_SRV,
+       .addr.nameseq.lower     = TIPC_TOP_SRV,
+       .addr.nameseq.upper     = TIPC_TOP_SRV,
+       .scope                  = TIPC_NODE_SCOPE
 };
 
-static struct top_srv topsrv;
+static struct tipc_server topsrv __read_mostly = {
+       .saddr                  = &topsrv_addr,
+       .imp                    = TIPC_CRITICAL_IMPORTANCE,
+       .type                   = SOCK_SEQPACKET,
+       .max_rcvbuf_size        = sizeof(struct tipc_subscr),
+       .name                   = "topology_server",
+       .tipc_conn_recvmsg      = subscr_conn_msg_event,
+       .tipc_conn_new          = subscr_named_msg_event,
+       .tipc_conn_shutdown     = subscr_conn_shutdown_event,
+};
 
 /**
  * htohl - convert value to endianness used by destination
@@ -81,20 +90,13 @@ static u32 htohl(u32 in, int swap)
        return swap ? swab32(in) : in;
 }
 
-/**
- * subscr_send_event - send a message containing a tipc_event to the subscriber
- *
- * Note: Must not hold subscriber's server port lock, since tipc_send() will
- *       try to take the lock if the message is rejected and returned!
- */
-static void subscr_send_event(struct tipc_subscription *sub,
-                             u32 found_lower,
-                             u32 found_upper,
-                             u32 event,
-                             u32 port_ref,
+static void subscr_send_event(struct tipc_subscription *sub, u32 found_lower,
+                             u32 found_upper, u32 event, u32 port_ref,
                              u32 node)
 {
-       struct iovec msg_sect;
+       struct tipc_subscriber *subscriber = sub->subscriber;
+       struct kvec msg_sect;
+       int ret;
 
        msg_sect.iov_base = (void *)&sub->evt;
        msg_sect.iov_len = sizeof(struct tipc_event);
@@ -104,7 +106,10 @@ static void subscr_send_event(struct tipc_subscription *sub,
        sub->evt.found_upper = htohl(found_upper, sub->swap);
        sub->evt.port.ref = htohl(port_ref, sub->swap);
        sub->evt.port.node = htohl(node, sub->swap);
-       tipc_send(sub->server_ref, 1, &msg_sect, msg_sect.iov_len);
+       ret = tipc_conn_sendmsg(&topsrv, subscriber->conid, NULL,
+                               msg_sect.iov_base, msg_sect.iov_len);
+       if (ret < 0)
+               pr_err("Sending subscription event failed, no memory\n");
 }
 
 /**
@@ -112,10 +117,8 @@ static void subscr_send_event(struct tipc_subscription *sub,
  *
  * Returns 1 if there is overlap, otherwise 0.
  */
-int tipc_subscr_overlap(struct tipc_subscription *sub,
-                       u32 found_lower,
+int tipc_subscr_overlap(struct tipc_subscription *sub, u32 found_lower,
                        u32 found_upper)
-
 {
        if (found_lower < sub->seq.lower)
                found_lower = sub->seq.lower;
@@ -131,13 +134,9 @@ int tipc_subscr_overlap(struct tipc_subscription *sub,
  *
  * Protected by nameseq.lock in name_table.c
  */
-void tipc_subscr_report_overlap(struct tipc_subscription *sub,
-                               u32 found_lower,
-                               u32 found_upper,
-                               u32 event,
-                               u32 port_ref,
-                               u32 node,
-                               int must)
+void tipc_subscr_report_overlap(struct tipc_subscription *sub, u32 found_lower,
+                               u32 found_upper, u32 event, u32 port_ref,
+                               u32 node, int must)
 {
        if (!tipc_subscr_overlap(sub, found_lower, found_upper))
                return;
@@ -147,21 +146,24 @@ void tipc_subscr_report_overlap(struct tipc_subscription *sub,
        subscr_send_event(sub, found_lower, found_upper, event, port_ref, node);
 }
 
-/**
- * subscr_timeout - subscription timeout has occurred
- */
 static void subscr_timeout(struct tipc_subscription *sub)
 {
-       struct tipc_port *server_port;
+       struct tipc_subscriber *subscriber = sub->subscriber;
+
+       /* The spin lock per subscriber is used to protect its members */
+       spin_lock_bh(&subscriber->lock);
 
-       /* Validate server port reference (in case subscriber is terminating) */
-       server_port = tipc_port_lock(sub->server_ref);
-       if (server_port == NULL)
+       /* Validate if the connection related to the subscriber is
+        * closed (in case subscriber is terminating)
+        */
+       if (subscriber->conid == 0) {
+               spin_unlock_bh(&subscriber->lock);
                return;
+       }
 
        /* Validate timeout (in case subscription is being cancelled) */
        if (sub->timeout == TIPC_WAIT_FOREVER) {
-               tipc_port_unlock(server_port);
+               spin_unlock_bh(&subscriber->lock);
                return;
        }
 
@@ -171,8 +173,7 @@ static void subscr_timeout(struct tipc_subscription *sub)
        /* Unlink subscription from subscriber */
        list_del(&sub->subscription_list);
 
-       /* Release subscriber's server port */
-       tipc_port_unlock(server_port);
+       spin_unlock_bh(&subscriber->lock);
 
        /* Notify subscriber of timeout */
        subscr_send_event(sub, sub->evt.s.seq.lower, sub->evt.s.seq.upper,
@@ -181,64 +182,54 @@ static void subscr_timeout(struct tipc_subscription *sub)
        /* Now destroy subscription */
        k_term_timer(&sub->timer);
        kfree(sub);
-       atomic_dec(&topsrv.subscription_count);
+       atomic_dec(&subscription_count);
 }
 
 /**
  * subscr_del - delete a subscription within a subscription list
  *
- * Called with subscriber port locked.
+ * Called with subscriber lock held.
  */
 static void subscr_del(struct tipc_subscription *sub)
 {
        tipc_nametbl_unsubscribe(sub);
        list_del(&sub->subscription_list);
        kfree(sub);
-       atomic_dec(&topsrv.subscription_count);
+       atomic_dec(&subscription_count);
 }
 
 /**
  * subscr_terminate - terminate communication with a subscriber
  *
- * Called with subscriber port locked.  Routine must temporarily release lock
- * to enable subscription timeout routine(s) to finish without deadlocking;
- * the lock is then reclaimed to allow caller to release it upon return.
- * (This should work even in the unlikely event some other thread creates
- * a new object reference in the interim that uses this lock; this routine will
- * simply wait for it to be released, then claim it.)
+ * Note: Must call it in process context since it might sleep.
  */
 static void subscr_terminate(struct tipc_subscriber *subscriber)
 {
-       u32 port_ref;
+       tipc_conn_terminate(&topsrv, subscriber->conid);
+}
+
+static void subscr_release(struct tipc_subscriber *subscriber)
+{
        struct tipc_subscription *sub;
        struct tipc_subscription *sub_temp;
 
-       /* Invalidate subscriber reference */
-       port_ref = subscriber->port_ref;
-       subscriber->port_ref = 0;
-       spin_unlock_bh(subscriber->lock);
+       spin_lock_bh(&subscriber->lock);
 
-       /* Sever connection to subscriber */
-       tipc_shutdown(port_ref);
-       tipc_deleteport(port_ref);
+       /* Invalidate subscriber reference */
+       subscriber->conid = 0;
 
        /* Destroy any existing subscriptions for subscriber */
        list_for_each_entry_safe(sub, sub_temp, &subscriber->subscription_list,
                                 subscription_list) {
                if (sub->timeout != TIPC_WAIT_FOREVER) {
+                       spin_unlock_bh(&subscriber->lock);
                        k_cancel_timer(&sub->timer);
                        k_term_timer(&sub->timer);
+                       spin_lock_bh(&subscriber->lock);
                }
                subscr_del(sub);
        }
-
-       /* Remove subscriber from topology server's subscriber list */
-       spin_lock_bh(&topsrv.lock);
-       list_del(&subscriber->subscriber_list);
-       spin_unlock_bh(&topsrv.lock);
-
-       /* Reclaim subscriber lock */
-       spin_lock_bh(subscriber->lock);
+       spin_unlock_bh(&subscriber->lock);
 
        /* Now destroy subscriber */
        kfree(subscriber);
@@ -247,7 +238,7 @@ static void subscr_terminate(struct tipc_subscriber *subscriber)
 /**
  * subscr_cancel - handle subscription cancellation request
  *
- * Called with subscriber port locked.  Routine must temporarily release lock
+ * Called with subscriber lock held. Routine must temporarily release lock
  * to enable the subscription timeout routine to finish without deadlocking;
  * the lock is then reclaimed to allow caller to release it upon return.
  *
@@ -274,10 +265,10 @@ static void subscr_cancel(struct tipc_subscr *s,
        /* Cancel subscription timer (if used), then delete subscription */
        if (sub->timeout != TIPC_WAIT_FOREVER) {
                sub->timeout = TIPC_WAIT_FOREVER;
-               spin_unlock_bh(subscriber->lock);
+               spin_unlock_bh(&subscriber->lock);
                k_cancel_timer(&sub->timer);
                k_term_timer(&sub->timer);
-               spin_lock_bh(subscriber->lock);
+               spin_lock_bh(&subscriber->lock);
        }
        subscr_del(sub);
 }
@@ -285,7 +276,7 @@ static void subscr_cancel(struct tipc_subscr *s,
 /**
  * subscr_subscribe - create subscription for subscriber
  *
- * Called with subscriber port locked.
+ * Called with subscriber lock held.
  */
 static struct tipc_subscription *subscr_subscribe(struct tipc_subscr *s,
                                             struct tipc_subscriber *subscriber)
@@ -304,7 +295,7 @@ static struct tipc_subscription *subscr_subscribe(struct tipc_subscr *s,
        }
 
        /* Refuse subscription if global limit exceeded */
-       if (atomic_read(&topsrv.subscription_count) >= TIPC_MAX_SUBSCRIPTIONS) {
+       if (atomic_read(&subscription_count) >= TIPC_MAX_SUBSCRIPTIONS) {
                pr_warn("Subscription rejected, limit reached (%u)\n",
                        TIPC_MAX_SUBSCRIPTIONS);
                subscr_terminate(subscriber);
@@ -335,10 +326,10 @@ static struct tipc_subscription *subscr_subscribe(struct tipc_subscr *s,
        }
        INIT_LIST_HEAD(&sub->nameseq_list);
        list_add(&sub->subscription_list, &subscriber->subscription_list);
-       sub->server_ref = subscriber->port_ref;
+       sub->subscriber = subscriber;
        sub->swap = swap;
        memcpy(&sub->evt.s, s, sizeof(struct tipc_subscr));
-       atomic_inc(&topsrv.subscription_count);
+       atomic_inc(&subscription_count);
        if (sub->timeout != TIPC_WAIT_FOREVER) {
                k_init_timer(&sub->timer,
                             (Handler)subscr_timeout, (unsigned long)sub);
@@ -348,196 +339,51 @@ static struct tipc_subscription *subscr_subscribe(struct tipc_subscr *s,
        return sub;
 }
 
-/**
- * subscr_conn_shutdown_event - handle termination request from subscriber
- *
- * Called with subscriber's server port unlocked.
- */
-static void subscr_conn_shutdown_event(void *usr_handle,
-                                      u32 port_ref,
-                                      struct sk_buff **buf,
-                                      unsigned char const *data,
-                                      unsigned int size,
-                                      int reason)
+/* Handle one termination request for the subscriber */
+static void subscr_conn_shutdown_event(int conid, void *usr_data)
 {
-       struct tipc_subscriber *subscriber = usr_handle;
-       spinlock_t *subscriber_lock;
-
-       if (tipc_port_lock(port_ref) == NULL)
-               return;
-
-       subscriber_lock = subscriber->lock;
-       subscr_terminate(subscriber);
-       spin_unlock_bh(subscriber_lock);
+       subscr_release((struct tipc_subscriber *)usr_data);
 }
 
-/**
- * subscr_conn_msg_event - handle new subscription request from subscriber
- *
- * Called with subscriber's server port unlocked.
- */
-static void subscr_conn_msg_event(void *usr_handle,
-                                 u32 port_ref,
-                                 struct sk_buff **buf,
-                                 const unchar *data,
-                                 u32 size)
+/* Handle one request to create a new subscription for the subscriber */
+static void subscr_conn_msg_event(int conid, struct sockaddr_tipc *addr,
+                                 void *usr_data, void *buf, size_t len)
 {
-       struct tipc_subscriber *subscriber = usr_handle;
-       spinlock_t *subscriber_lock;
+       struct tipc_subscriber *subscriber = usr_data;
        struct tipc_subscription *sub;
 
-       /*
-        * Lock subscriber's server port (& make a local copy of lock pointer,
-        * in case subscriber is deleted while processing subscription request)
-        */
-       if (tipc_port_lock(port_ref) == NULL)
-               return;
-
-       subscriber_lock = subscriber->lock;
-
-       if (size != sizeof(struct tipc_subscr)) {
-               subscr_terminate(subscriber);
-               spin_unlock_bh(subscriber_lock);
-       } else {
-               sub = subscr_subscribe((struct tipc_subscr *)data, subscriber);
-               spin_unlock_bh(subscriber_lock);
-               if (sub != NULL) {
-
-                       /*
-                        * We must release the server port lock before adding a
-                        * subscription to the name table since TIPC needs to be
-                        * able to (re)acquire the port lock if an event message
-                        * issued by the subscription process is rejected and
-                        * returned.  The subscription cannot be deleted while
-                        * it is being added to the name table because:
-                        * a) the single-threading of the native API port code
-                        *    ensures the subscription cannot be cancelled and
-                        *    the subscriber connection cannot be broken, and
-                        * b) the name table lock ensures the subscription
-                        *    timeout code cannot delete the subscription,
-                        * so the subscription object is still protected.
-                        */
-                       tipc_nametbl_subscribe(sub);
-               }
-       }
+       spin_lock_bh(&subscriber->lock);
+       sub = subscr_subscribe((struct tipc_subscr *)buf, subscriber);
+       if (sub)
+               tipc_nametbl_subscribe(sub);
+       spin_unlock_bh(&subscriber->lock);
 }
 
-/**
- * subscr_named_msg_event - handle request to establish a new subscriber
- */
-static void subscr_named_msg_event(void *usr_handle,
-                                  u32 port_ref,
-                                  struct sk_buff **buf,
-                                  const unchar *data,
-                                  u32 size,
-                                  u32 importance,
-                                  struct tipc_portid const *orig,
-                                  struct tipc_name_seq const *dest)
+
+/* Handle one request to establish a new subscriber */
+static void *subscr_named_msg_event(int conid)
 {
        struct tipc_subscriber *subscriber;
-       u32 server_port_ref;
 
        /* Create subscriber object */
        subscriber = kzalloc(sizeof(struct tipc_subscriber), GFP_ATOMIC);
        if (subscriber == NULL) {
                pr_warn("Subscriber rejected, no memory\n");
-               return;
+               return NULL;
        }
        INIT_LIST_HEAD(&subscriber->subscription_list);
-       INIT_LIST_HEAD(&subscriber->subscriber_list);
-
-       /* Create server port & establish connection to subscriber */
-       tipc_createport(subscriber,
-                       importance,
-                       NULL,
-                       NULL,
-                       subscr_conn_shutdown_event,
-                       NULL,
-                       NULL,
-                       subscr_conn_msg_event,
-                       NULL,
-                       &subscriber->port_ref);
-       if (subscriber->port_ref == 0) {
-               pr_warn("Subscriber rejected, unable to create port\n");
-               kfree(subscriber);
-               return;
-       }
-       tipc_connect(subscriber->port_ref, orig);
-
-       /* Lock server port (& save lock address for future use) */
-       subscriber->lock = tipc_port_lock(subscriber->port_ref)->lock;
-
-       /* Add subscriber to topology server's subscriber list */
-       spin_lock_bh(&topsrv.lock);
-       list_add(&subscriber->subscriber_list, &topsrv.subscriber_list);
-       spin_unlock_bh(&topsrv.lock);
-
-       /* Unlock server port */
-       server_port_ref = subscriber->port_ref;
-       spin_unlock_bh(subscriber->lock);
-
-       /* Send an ACK- to complete connection handshaking */
-       tipc_send(server_port_ref, 0, NULL, 0);
+       subscriber->conid = conid;
+       spin_lock_init(&subscriber->lock);
 
-       /* Handle optional subscription request */
-       if (size != 0) {
-               subscr_conn_msg_event(subscriber, server_port_ref,
-                                     buf, data, size);
-       }
+       return (void *)subscriber;
 }
 
 int tipc_subscr_start(void)
 {
-       struct tipc_name_seq seq = {TIPC_TOP_SRV, TIPC_TOP_SRV, TIPC_TOP_SRV};
-       int res;
-
-       spin_lock_init(&topsrv.lock);
-       INIT_LIST_HEAD(&topsrv.subscriber_list);
-
-       res = tipc_createport(NULL,
-                             TIPC_CRITICAL_IMPORTANCE,
-                             NULL,
-                             NULL,
-                             NULL,
-                             NULL,
-                             subscr_named_msg_event,
-                             NULL,
-                             NULL,
-                             &topsrv.setup_port);
-       if (res)
-               goto failed;
-
-       res = tipc_publish(topsrv.setup_port, TIPC_NODE_SCOPE, &seq);
-       if (res) {
-               tipc_deleteport(topsrv.setup_port);
-               topsrv.setup_port = 0;
-               goto failed;
-       }
-
-       return 0;
-
-failed:
-       pr_err("Failed to create subscription service\n");
-       return res;
+       return tipc_server_start(&topsrv);
 }
 
 void tipc_subscr_stop(void)
 {
-       struct tipc_subscriber *subscriber;
-       struct tipc_subscriber *subscriber_temp;
-       spinlock_t *subscriber_lock;
-
-       if (topsrv.setup_port) {
-               tipc_deleteport(topsrv.setup_port);
-               topsrv.setup_port = 0;
-
-               list_for_each_entry_safe(subscriber, subscriber_temp,
-                                        &topsrv.subscriber_list,
-                                        subscriber_list) {
-                       subscriber_lock = subscriber->lock;
-                       spin_lock_bh(subscriber_lock);
-                       subscr_terminate(subscriber);
-                       spin_unlock_bh(subscriber_lock);
-               }
-       }
+       tipc_server_stop(&topsrv);
 }
index 218d2e0..393e417 100644 (file)
@@ -2,7 +2,7 @@
  * net/tipc/subscr.h: Include file for TIPC network topology service
  *
  * Copyright (c) 2003-2006, Ericsson AB
- * Copyright (c) 2005-2007, Wind River Systems
+ * Copyright (c) 2005-2007, 2012-2013, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #ifndef _TIPC_SUBSCR_H
 #define _TIPC_SUBSCR_H
 
+#include "server.h"
+
 struct tipc_subscription;
+struct tipc_subscriber;
 
 /**
  * struct tipc_subscription - TIPC network topology subscription object
+ * @subscriber: pointer to its subscriber
  * @seq: name sequence associated with subscription
  * @timeout: duration of subscription (in ms)
  * @filter: event filtering to be done for subscription
@@ -52,28 +56,23 @@ struct tipc_subscription;
  * @evt: template for events generated by subscription
  */
 struct tipc_subscription {
+       struct tipc_subscriber *subscriber;
        struct tipc_name_seq seq;
        u32 timeout;
        u32 filter;
        struct timer_list timer;
        struct list_head nameseq_list;
        struct list_head subscription_list;
-       u32 server_ref;
        int swap;
        struct tipc_event evt;
 };
 
-int tipc_subscr_overlap(struct tipc_subscription *sub,
-                       u32 found_lower,
+int tipc_subscr_overlap(struct tipc_subscription *sub, u32 found_lower,
                        u32 found_upper);
 
-void tipc_subscr_report_overlap(struct tipc_subscription *sub,
-                               u32 found_lower,
-                               u32 found_upper,
-                               u32 event,
-                               u32 port_ref,
-                               u32 node,
-                               int must_report);
+void tipc_subscr_report_overlap(struct tipc_subscription *sub, u32 found_lower,
+                               u32 found_upper, u32 event, u32 port_ref,
+                               u32 node, int must);
 
 int tipc_subscr_start(void);
 
diff --git a/net/tipc/sysctl.c b/net/tipc/sysctl.c
new file mode 100644 (file)
index 0000000..f3fef93
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * net/tipc/sysctl.c: sysctl interface to TIPC subsystem
+ *
+ * Copyright (c) 2013, Wind River Systems
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders 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") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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 "core.h"
+
+#include <linux/sysctl.h>
+
+static struct ctl_table_header *tipc_ctl_hdr;
+
+static struct ctl_table tipc_table[] = {
+       {
+               .procname       = "tipc_rmem",
+               .data           = &sysctl_tipc_rmem,
+               .maxlen         = sizeof(sysctl_tipc_rmem),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {}
+};
+
+int tipc_register_sysctl(void)
+{
+       tipc_ctl_hdr = register_net_sysctl(&init_net, "net/tipc", tipc_table);
+       if (tipc_ctl_hdr == NULL)
+               return -ENOMEM;
+       return 0;
+}
+
+void tipc_unregister_sysctl(void)
+{
+       unregister_net_sysctl_table(tipc_ctl_hdr);
+}
index 8800604..b3d5150 100644 (file)
@@ -15,7 +15,7 @@
 
 #include <net/af_unix.h>
 
-static ctl_table unix_table[] = {
+static struct ctl_table unix_table[] = {
        {
                .procname       = "max_dgram_qlen",
                .data           = &init_net.unx.sysctl_max_dgram_qlen,
index 3f77f42..593071d 100644 (file)
@@ -144,18 +144,18 @@ EXPORT_SYMBOL_GPL(vm_sockets_get_local_cid);
  * VSOCK_HASH_SIZE + 1 so that vsock_bind_table[0] through
  * vsock_bind_table[VSOCK_HASH_SIZE - 1] are for bound sockets and
  * vsock_bind_table[VSOCK_HASH_SIZE] is for unbound sockets.  The hash function
- * mods with VSOCK_HASH_SIZE - 1 to ensure this.
+ * mods with VSOCK_HASH_SIZE to ensure this.
  */
 #define VSOCK_HASH_SIZE         251
 #define MAX_PORT_RETRIES        24
 
-#define VSOCK_HASH(addr)        ((addr)->svm_port % (VSOCK_HASH_SIZE - 1))
+#define VSOCK_HASH(addr)        ((addr)->svm_port % VSOCK_HASH_SIZE)
 #define vsock_bound_sockets(addr) (&vsock_bind_table[VSOCK_HASH(addr)])
 #define vsock_unbound_sockets     (&vsock_bind_table[VSOCK_HASH_SIZE])
 
 /* XXX This can probably be implemented in a better way. */
 #define VSOCK_CONN_HASH(src, dst)                              \
-       (((src)->svm_cid ^ (dst)->svm_port) % (VSOCK_HASH_SIZE - 1))
+       (((src)->svm_cid ^ (dst)->svm_port) % VSOCK_HASH_SIZE)
 #define vsock_connected_sockets(src, dst)              \
        (&vsock_connected_table[VSOCK_CONN_HASH(src, dst)])
 #define vsock_connected_sockets_vsk(vsk)                               \
@@ -165,6 +165,18 @@ static struct list_head vsock_bind_table[VSOCK_HASH_SIZE + 1];
 static struct list_head vsock_connected_table[VSOCK_HASH_SIZE];
 static DEFINE_SPINLOCK(vsock_table_lock);
 
+/* Autobind this socket to the local address if necessary. */
+static int vsock_auto_bind(struct vsock_sock *vsk)
+{
+       struct sock *sk = sk_vsock(vsk);
+       struct sockaddr_vm local_addr;
+
+       if (vsock_addr_bound(&vsk->local_addr))
+               return 0;
+       vsock_addr_init(&local_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY);
+       return __vsock_bind(sk, &local_addr);
+}
+
 static void vsock_init_tables(void)
 {
        int i;
@@ -956,15 +968,10 @@ static int vsock_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
 
        lock_sock(sk);
 
-       if (!vsock_addr_bound(&vsk->local_addr)) {
-               struct sockaddr_vm local_addr;
-
-               vsock_addr_init(&local_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY);
-               err = __vsock_bind(sk, &local_addr);
-               if (err != 0)
-                       goto out;
+       err = vsock_auto_bind(vsk);
+       if (err)
+               goto out;
 
-       }
 
        /* If the provided message contains an address, use that.  Otherwise
         * fall back on the socket's remote handle (if it has been connected).
@@ -1038,15 +1045,9 @@ static int vsock_dgram_connect(struct socket *sock,
 
        lock_sock(sk);
 
-       if (!vsock_addr_bound(&vsk->local_addr)) {
-               struct sockaddr_vm local_addr;
-
-               vsock_addr_init(&local_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY);
-               err = __vsock_bind(sk, &local_addr);
-               if (err != 0)
-                       goto out;
-
-       }
+       err = vsock_auto_bind(vsk);
+       if (err)
+               goto out;
 
        if (!transport->dgram_allow(remote_addr->svm_cid,
                                    remote_addr->svm_port)) {
@@ -1163,17 +1164,9 @@ static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr,
                memcpy(&vsk->remote_addr, remote_addr,
                       sizeof(vsk->remote_addr));
 
-               /* Autobind this socket to the local address if necessary. */
-               if (!vsock_addr_bound(&vsk->local_addr)) {
-                       struct sockaddr_vm local_addr;
-
-                       vsock_addr_init(&local_addr, VMADDR_CID_ANY,
-                                       VMADDR_PORT_ANY);
-                       err = __vsock_bind(sk, &local_addr);
-                       if (err != 0)
-                               goto out;
-
-               }
+               err = vsock_auto_bind(vsk);
+               if (err)
+                       goto out;
 
                sk->sk_state = SS_CONNECTING;
 
index daff752..ffc11df 100644 (file)
@@ -625,13 +625,14 @@ static int vmci_transport_recv_dgram_cb(void *data, struct vmci_datagram *dg)
 
        /* Attach the packet to the socket's receive queue as an sk_buff. */
        skb = alloc_skb(size, GFP_ATOMIC);
-       if (skb) {
-               /* sk_receive_skb() will do a sock_put(), so hold here. */
-               sock_hold(sk);
-               skb_put(skb, size);
-               memcpy(skb->data, dg, size);
-               sk_receive_skb(sk, skb, 0);
-       }
+       if (!skb)
+               return VMCI_ERROR_NO_MEM;
+
+       /* sk_receive_skb() will do a sock_put(), so hold here. */
+       sock_hold(sk);
+       skb_put(skb, size);
+       memcpy(skb->data, dg, size);
+       sk_receive_skb(sk, skb, 0);
 
        return VMCI_SUCCESS;
 }
@@ -939,10 +940,9 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
                 * reset to prevent that.
                 */
                vmci_transport_send_reset(sk, pkt);
-               goto out;
+               break;
        }
 
-out:
        release_sock(sk);
        kfree(recv_pkt_info);
        /* Release reference obtained in the stream callback when we fetched
index fd556ac..50f6195 100644 (file)
@@ -54,6 +54,8 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
        control_freq = chandef->chan->center_freq;
 
        switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
        case NL80211_CHAN_WIDTH_20:
        case NL80211_CHAN_WIDTH_20_NOHT:
                if (chandef->center_freq1 != control_freq)
@@ -152,6 +154,12 @@ static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
        int width;
 
        switch (c->width) {
+       case NL80211_CHAN_WIDTH_5:
+               width = 5;
+               break;
+       case NL80211_CHAN_WIDTH_10:
+               width = 10;
+               break;
        case NL80211_CHAN_WIDTH_20:
        case NL80211_CHAN_WIDTH_20_NOHT:
                width = 20;
@@ -194,6 +202,16 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
        if (c1->width == c2->width)
                return NULL;
 
+       /*
+        * can't be compatible if one of them is 5 or 10 MHz,
+        * but they don't have the same width.
+        */
+       if (c1->width == NL80211_CHAN_WIDTH_5 ||
+           c1->width == NL80211_CHAN_WIDTH_10 ||
+           c2->width == NL80211_CHAN_WIDTH_5 ||
+           c2->width == NL80211_CHAN_WIDTH_10)
+               return NULL;
+
        if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
            c1->width == NL80211_CHAN_WIDTH_20)
                return c2;
@@ -264,11 +282,17 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
                                            u32 bandwidth)
 {
        struct ieee80211_channel *c;
-       u32 freq;
+       u32 freq, start_freq, end_freq;
+
+       if (bandwidth <= 20) {
+               start_freq = center_freq;
+               end_freq = center_freq;
+       } else {
+               start_freq = center_freq - bandwidth/2 + 10;
+               end_freq = center_freq + bandwidth/2 - 10;
+       }
 
-       for (freq = center_freq - bandwidth/2 + 10;
-            freq <= center_freq + bandwidth/2 - 10;
-            freq += 20) {
+       for (freq = start_freq; freq <= end_freq; freq += 20) {
                c = ieee80211_get_channel(wiphy, freq);
                if (!c)
                        return -EINVAL;
@@ -310,11 +334,17 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
                                        u32 prohibited_flags)
 {
        struct ieee80211_channel *c;
-       u32 freq;
+       u32 freq, start_freq, end_freq;
+
+       if (bandwidth <= 20) {
+               start_freq = center_freq;
+               end_freq = center_freq;
+       } else {
+               start_freq = center_freq - bandwidth/2 + 10;
+               end_freq = center_freq + bandwidth/2 - 10;
+       }
 
-       for (freq = center_freq - bandwidth/2 + 10;
-            freq <= center_freq + bandwidth/2 - 10;
-            freq += 20) {
+       for (freq = start_freq; freq <= end_freq; freq += 20) {
                c = ieee80211_get_channel(wiphy, freq);
                if (!c)
                        return false;
@@ -349,6 +379,12 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
        control_freq = chandef->chan->center_freq;
 
        switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               width = 5;
+               break;
+       case NL80211_CHAN_WIDTH_10:
+               width = 10;
+               break;
        case NL80211_CHAN_WIDTH_20:
                if (!ht_cap->ht_supported)
                        return false;
@@ -405,6 +441,11 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
        if (width > 20)
                prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
 
+       /* 5 and 10 MHz are only defined for the OFDM PHY */
+       if (width < 20)
+               prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
+
+
        if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
                                         width, prohibited_flags))
                return false;
index 73405e0..4f9f216 100644 (file)
 MODULE_AUTHOR("Johannes Berg");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("wireless configuration support");
+MODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME);
 
-/* RCU-protected (and cfg80211_mutex for writers) */
+/* RCU-protected (and RTNL for writers) */
 LIST_HEAD(cfg80211_rdev_list);
 int cfg80211_rdev_list_generation;
 
-DEFINE_MUTEX(cfg80211_mutex);
-
 /* for debugfs */
 static struct dentry *ieee80211_debugfs_dir;
 
@@ -52,12 +51,11 @@ module_param(cfg80211_disable_40mhz_24ghz, bool, 0644);
 MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz,
                 "Disable 40MHz support in the 2.4GHz band");
 
-/* requires cfg80211_mutex to be held! */
 struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
 {
        struct cfg80211_registered_device *result = NULL, *rdev;
 
-       assert_cfg80211_lock();
+       ASSERT_RTNL();
 
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                if (rdev->wiphy_idx == wiphy_idx) {
@@ -76,12 +74,11 @@ int get_wiphy_idx(struct wiphy *wiphy)
        return rdev->wiphy_idx;
 }
 
-/* requires cfg80211_rdev_mutex to be held! */
 struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
 {
        struct cfg80211_registered_device *rdev;
 
-       assert_cfg80211_lock();
+       ASSERT_RTNL();
 
        rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx);
        if (!rdev)
@@ -89,35 +86,13 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
        return &rdev->wiphy;
 }
 
-struct cfg80211_registered_device *
-cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
-{
-       struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV);
-       struct net_device *dev;
-
-       mutex_lock(&cfg80211_mutex);
-       dev = dev_get_by_index(net, ifindex);
-       if (!dev)
-               goto out;
-       if (dev->ieee80211_ptr) {
-               rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
-               mutex_lock(&rdev->mtx);
-       } else
-               rdev = ERR_PTR(-ENODEV);
-       dev_put(dev);
- out:
-       mutex_unlock(&cfg80211_mutex);
-       return rdev;
-}
-
-/* requires cfg80211_mutex to be held */
 int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
                        char *newname)
 {
        struct cfg80211_registered_device *rdev2;
        int wiphy_idx, taken = -1, result, digits;
 
-       assert_cfg80211_lock();
+       ASSERT_RTNL();
 
        /* prohibit calling the thing phy%d when %d is not its number */
        sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
@@ -215,8 +190,7 @@ static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
 void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
                              struct wireless_dev *wdev)
 {
-       lockdep_assert_held(&rdev->devlist_mtx);
-       lockdep_assert_held(&rdev->sched_scan_mtx);
+       ASSERT_RTNL();
 
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE))
                return;
@@ -230,18 +204,15 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
        rdev->opencount--;
 
        if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
-               bool busy = work_busy(&rdev->scan_done_wk);
-
                /*
-                * If the work isn't pending or running (in which case it would
-                * be waiting for the lock we hold) the driver didn't properly
-                * cancel the scan when the interface was removed. In this case
-                * warn and leak the scan request object to not crash later.
+                * If the scan request wasn't notified as done, set it
+                * to aborted and leak it after a warning. The driver
+                * should have notified us that it ended at the latest
+                * during rdev_stop_p2p_device().
                 */
-               WARN_ON(!busy);
-
-               rdev->scan_req->aborted = true;
-               ___cfg80211_scan_done(rdev, !busy);
+               if (WARN_ON(!rdev->scan_req->notified))
+                       rdev->scan_req->aborted = true;
+               ___cfg80211_scan_done(rdev, !rdev->scan_req->notified);
        }
 }
 
@@ -255,8 +226,6 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)
 
        rtnl_lock();
 
-       /* read-only iteration need not hold the devlist_mtx */
-
        list_for_each_entry(wdev, &rdev->wdev_list, list) {
                if (wdev->netdev) {
                        dev_close(wdev->netdev);
@@ -265,12 +234,7 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)
                /* otherwise, check iftype */
                switch (wdev->iftype) {
                case NL80211_IFTYPE_P2P_DEVICE:
-                       /* but this requires it */
-                       mutex_lock(&rdev->devlist_mtx);
-                       mutex_lock(&rdev->sched_scan_mtx);
                        cfg80211_stop_p2p_device(rdev, wdev);
-                       mutex_unlock(&rdev->sched_scan_mtx);
-                       mutex_unlock(&rdev->devlist_mtx);
                        break;
                default:
                        break;
@@ -298,10 +262,7 @@ static void cfg80211_event_work(struct work_struct *work)
                            event_work);
 
        rtnl_lock();
-       cfg80211_lock_rdev(rdev);
-
        cfg80211_process_rdev_events(rdev);
-       cfg80211_unlock_rdev(rdev);
        rtnl_unlock();
 }
 
@@ -309,7 +270,7 @@ static void cfg80211_event_work(struct work_struct *work)
 
 struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
 {
-       static int wiphy_counter;
+       static atomic_t wiphy_counter = ATOMIC_INIT(0);
 
        struct cfg80211_registered_device *rdev;
        int alloc_size;
@@ -331,26 +292,21 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
 
        rdev->ops = ops;
 
-       mutex_lock(&cfg80211_mutex);
-
-       rdev->wiphy_idx = wiphy_counter++;
+       rdev->wiphy_idx = atomic_inc_return(&wiphy_counter);
 
        if (unlikely(rdev->wiphy_idx < 0)) {
-               wiphy_counter--;
-               mutex_unlock(&cfg80211_mutex);
                /* ugh, wrapped! */
+               atomic_dec(&wiphy_counter);
                kfree(rdev);
                return NULL;
        }
 
-       mutex_unlock(&cfg80211_mutex);
+       /* atomic_inc_return makes it start at 1, make it start at 0 */
+       rdev->wiphy_idx--;
 
        /* give it a proper name */
        dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
 
-       mutex_init(&rdev->mtx);
-       mutex_init(&rdev->devlist_mtx);
-       mutex_init(&rdev->sched_scan_mtx);
        INIT_LIST_HEAD(&rdev->wdev_list);
        INIT_LIST_HEAD(&rdev->beacon_registrations);
        spin_lock_init(&rdev->beacon_registrations_lock);
@@ -496,8 +452,13 @@ int wiphy_register(struct wiphy *wiphy)
        u16 ifmodes = wiphy->interface_modes;
 
 #ifdef CONFIG_PM
-       if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
-                   !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)))
+       if (WARN_ON(wiphy->wowlan &&
+                   (wiphy->wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+                   !(wiphy->wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)))
+               return -EINVAL;
+       if (WARN_ON(wiphy->wowlan &&
+                   !wiphy->wowlan->flags && !wiphy->wowlan->n_patterns &&
+                   !wiphy->wowlan->tcp))
                return -EINVAL;
 #endif
 
@@ -587,25 +548,28 @@ int wiphy_register(struct wiphy *wiphy)
        }
 
 #ifdef CONFIG_PM
-       if (rdev->wiphy.wowlan.n_patterns) {
-               if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len ||
-                           rdev->wiphy.wowlan.pattern_min_len >
-                           rdev->wiphy.wowlan.pattern_max_len))
-                       return -EINVAL;
-       }
+       if (WARN_ON(rdev->wiphy.wowlan && rdev->wiphy.wowlan->n_patterns &&
+                   (!rdev->wiphy.wowlan->pattern_min_len ||
+                    rdev->wiphy.wowlan->pattern_min_len >
+                               rdev->wiphy.wowlan->pattern_max_len)))
+               return -EINVAL;
 #endif
 
        /* check and set up bitrates */
        ieee80211_set_bitrate_flags(wiphy);
 
-       mutex_lock(&cfg80211_mutex);
 
        res = device_add(&rdev->wiphy.dev);
+       if (res)
+               return res;
+
+       res = rfkill_register(rdev->rfkill);
        if (res) {
-               mutex_unlock(&cfg80211_mutex);
+               device_del(&rdev->wiphy.dev);
                return res;
        }
 
+       rtnl_lock();
        /* set up regulatory info */
        wiphy_regulatory_register(wiphy);
 
@@ -631,25 +595,7 @@ int wiphy_register(struct wiphy *wiphy)
        }
 
        cfg80211_debugfs_rdev_add(rdev);
-       mutex_unlock(&cfg80211_mutex);
-
-       /*
-        * due to a locking dependency this has to be outside of the
-        * cfg80211_mutex lock
-        */
-       res = rfkill_register(rdev->rfkill);
-       if (res) {
-               device_del(&rdev->wiphy.dev);
-
-               mutex_lock(&cfg80211_mutex);
-               debugfs_remove_recursive(rdev->wiphy.debugfsdir);
-               list_del_rcu(&rdev->list);
-               wiphy_regulatory_deregister(wiphy);
-               mutex_unlock(&cfg80211_mutex);
-               return res;
-       }
 
-       rtnl_lock();
        rdev->wiphy.registered = true;
        rtnl_unlock();
        return 0;
@@ -679,25 +625,19 @@ void wiphy_unregister(struct wiphy *wiphy)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
 
-       rtnl_lock();
-       rdev->wiphy.registered = false;
-       rtnl_unlock();
-
-       rfkill_unregister(rdev->rfkill);
-
-       /* protect the device list */
-       mutex_lock(&cfg80211_mutex);
-
        wait_event(rdev->dev_wait, ({
                int __count;
-               mutex_lock(&rdev->devlist_mtx);
+               rtnl_lock();
                __count = rdev->opencount;
-               mutex_unlock(&rdev->devlist_mtx);
+               rtnl_unlock();
                __count == 0; }));
 
-       mutex_lock(&rdev->devlist_mtx);
+       rfkill_unregister(rdev->rfkill);
+
+       rtnl_lock();
+       rdev->wiphy.registered = false;
+
        BUG_ON(!list_empty(&rdev->wdev_list));
-       mutex_unlock(&rdev->devlist_mtx);
 
        /*
         * First remove the hardware from everywhere, this makes
@@ -707,20 +647,6 @@ void wiphy_unregister(struct wiphy *wiphy)
        list_del_rcu(&rdev->list);
        synchronize_rcu();
 
-       /*
-        * Try to grab rdev->mtx. If a command is still in progress,
-        * hopefully the driver will refuse it since it's tearing
-        * down the device already. We wait for this command to complete
-        * before unlinking the item from the list.
-        * Note: as codified by the BUG_ON above we cannot get here if
-        * a virtual interface is still present. Hence, we can only get
-        * to lock contention here if userspace issues a command that
-        * identified the hardware by wiphy index.
-        */
-       cfg80211_lock_rdev(rdev);
-       /* nothing */
-       cfg80211_unlock_rdev(rdev);
-
        /*
         * If this device got a regulatory hint tell core its
         * free to listen now to a new shiny device regulatory hint
@@ -730,15 +656,17 @@ void wiphy_unregister(struct wiphy *wiphy)
        cfg80211_rdev_list_generation++;
        device_del(&rdev->wiphy.dev);
 
-       mutex_unlock(&cfg80211_mutex);
+       rtnl_unlock();
 
        flush_work(&rdev->scan_done_wk);
        cancel_work_sync(&rdev->conn_work);
        flush_work(&rdev->event_work);
        cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
 
-       if (rdev->wowlan && rdev->ops->set_wakeup)
+#ifdef CONFIG_PM
+       if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
                rdev_set_wakeup(rdev, false);
+#endif
        cfg80211_rdev_free_wowlan(rdev);
 }
 EXPORT_SYMBOL(wiphy_unregister);
@@ -748,9 +676,6 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
        struct cfg80211_internal_bss *scan, *tmp;
        struct cfg80211_beacon_registration *reg, *treg;
        rfkill_destroy(rdev->rfkill);
-       mutex_destroy(&rdev->mtx);
-       mutex_destroy(&rdev->devlist_mtx);
-       mutex_destroy(&rdev->sched_scan_mtx);
        list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) {
                list_del(&reg->list);
                kfree(reg);
@@ -775,36 +700,6 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)
 }
 EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);
 
-static void wdev_cleanup_work(struct work_struct *work)
-{
-       struct wireless_dev *wdev;
-       struct cfg80211_registered_device *rdev;
-
-       wdev = container_of(work, struct wireless_dev, cleanup_work);
-       rdev = wiphy_to_dev(wdev->wiphy);
-
-       mutex_lock(&rdev->sched_scan_mtx);
-
-       if (WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev)) {
-               rdev->scan_req->aborted = true;
-               ___cfg80211_scan_done(rdev, true);
-       }
-
-       if (WARN_ON(rdev->sched_scan_req &&
-                   rdev->sched_scan_req->dev == wdev->netdev)) {
-               __cfg80211_stop_sched_scan(rdev, false);
-       }
-
-       mutex_unlock(&rdev->sched_scan_mtx);
-
-       mutex_lock(&rdev->devlist_mtx);
-       rdev->opencount--;
-       mutex_unlock(&rdev->devlist_mtx);
-       wake_up(&rdev->dev_wait);
-
-       dev_put(wdev->netdev);
-}
-
 void cfg80211_unregister_wdev(struct wireless_dev *wdev)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
@@ -814,8 +709,6 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
        if (WARN_ON(wdev->netdev))
                return;
 
-       mutex_lock(&rdev->devlist_mtx);
-       mutex_lock(&rdev->sched_scan_mtx);
        list_del_rcu(&wdev->list);
        rdev->devlist_generation++;
 
@@ -827,8 +720,6 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
                WARN_ON_ONCE(1);
                break;
        }
-       mutex_unlock(&rdev->sched_scan_mtx);
-       mutex_unlock(&rdev->devlist_mtx);
 }
 EXPORT_SYMBOL(cfg80211_unregister_wdev);
 
@@ -847,7 +738,7 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
 }
 
 void cfg80211_leave(struct cfg80211_registered_device *rdev,
-                  struct wireless_dev *wdev)
+                   struct wireless_dev *wdev)
 {
        struct net_device *dev = wdev->netdev;
 
@@ -857,9 +748,7 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
-               mutex_lock(&rdev->sched_scan_mtx);
                __cfg80211_stop_sched_scan(rdev, false);
-               mutex_unlock(&rdev->sched_scan_mtx);
 
                wdev_lock(wdev);
 #ifdef CONFIG_CFG80211_WEXT
@@ -868,8 +757,8 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
                wdev->wext.ie_len = 0;
                wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
 #endif
-               __cfg80211_disconnect(rdev, dev,
-                                     WLAN_REASON_DEAUTH_LEAVING, true);
+               cfg80211_disconnect(rdev, dev,
+                                   WLAN_REASON_DEAUTH_LEAVING, true);
                wdev_unlock(wdev);
                break;
        case NL80211_IFTYPE_MESH_POINT:
@@ -886,10 +775,9 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
 }
 
 static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
-                                        unsigned long state,
-                                        void *ndev)
+                                        unsigned long state, void *ptr)
 {
-       struct net_device *dev = ndev;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_registered_device *rdev;
        int ret;
@@ -912,13 +800,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                 * are added with nl80211.
                 */
                mutex_init(&wdev->mtx);
-               INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work);
                INIT_LIST_HEAD(&wdev->event_list);
                spin_lock_init(&wdev->event_lock);
                INIT_LIST_HEAD(&wdev->mgmt_registrations);
                spin_lock_init(&wdev->mgmt_registrations_lock);
 
-               mutex_lock(&rdev->devlist_mtx);
                wdev->identifier = ++rdev->wdev_id;
                list_add_rcu(&wdev->list, &rdev->wdev_list);
                rdev->devlist_generation++;
@@ -930,8 +816,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                        pr_err("failed to add phy80211 symlink to netdev!\n");
                }
                wdev->netdev = dev;
-               wdev->sme_state = CFG80211_SME_IDLE;
-               mutex_unlock(&rdev->devlist_mtx);
 #ifdef CONFIG_CFG80211_WEXT
                wdev->wext.default_key = -1;
                wdev->wext.default_mgmt_key = -1;
@@ -957,26 +841,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                break;
        case NETDEV_DOWN:
                cfg80211_update_iface_num(rdev, wdev->iftype, -1);
-               dev_hold(dev);
-               queue_work(cfg80211_wq, &wdev->cleanup_work);
+               if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
+                       if (WARN_ON(!rdev->scan_req->notified))
+                               rdev->scan_req->aborted = true;
+                       ___cfg80211_scan_done(rdev, true);
+               }
+
+               if (WARN_ON(rdev->sched_scan_req &&
+                           rdev->sched_scan_req->dev == wdev->netdev)) {
+                       __cfg80211_stop_sched_scan(rdev, false);
+               }
+
+               rdev->opencount--;
+               wake_up(&rdev->dev_wait);
                break;
        case NETDEV_UP:
-               /*
-                * If we have a really quick DOWN/UP succession we may
-                * have this work still pending ... cancel it and see
-                * if it was pending, in which case we need to account
-                * for some of the work it would have done.
-                */
-               if (cancel_work_sync(&wdev->cleanup_work)) {
-                       mutex_lock(&rdev->devlist_mtx);
-                       rdev->opencount--;
-                       mutex_unlock(&rdev->devlist_mtx);
-                       dev_put(dev);
-               }
                cfg80211_update_iface_num(rdev, wdev->iftype, 1);
-               cfg80211_lock_rdev(rdev);
-               mutex_lock(&rdev->devlist_mtx);
-               mutex_lock(&rdev->sched_scan_mtx);
                wdev_lock(wdev);
                switch (wdev->iftype) {
 #ifdef CONFIG_CFG80211_WEXT
@@ -1008,10 +888,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                        break;
                }
                wdev_unlock(wdev);
-               mutex_unlock(&rdev->sched_scan_mtx);
                rdev->opencount++;
-               mutex_unlock(&rdev->devlist_mtx);
-               cfg80211_unlock_rdev(rdev);
 
                /*
                 * Configure power management to the driver here so that its
@@ -1027,12 +904,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                        }
                break;
        case NETDEV_UNREGISTER:
-               /*
-                * NB: cannot take rdev->mtx here because this may be
-                * called within code protected by it when interfaces
-                * are removed with nl80211.
-                */
-               mutex_lock(&rdev->devlist_mtx);
                /*
                 * It is possible to get NETDEV_UNREGISTER
                 * multiple times. To detect that, check
@@ -1049,7 +920,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                        kfree(wdev->wext.keys);
 #endif
                }
-               mutex_unlock(&rdev->devlist_mtx);
                /*
                 * synchronise (so that we won't find this netdev
                 * from other code any more) and then clear the list
@@ -1063,15 +933,19 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                 * freed.
                 */
                cfg80211_process_wdev_events(wdev);
+
+               if (WARN_ON(wdev->current_bss)) {
+                       cfg80211_unhold_bss(wdev->current_bss);
+                       cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
+                       wdev->current_bss = NULL;
+               }
                break;
        case NETDEV_PRE_UP:
                if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
                        return notifier_from_errno(-EOPNOTSUPP);
                if (rfkill_blocked(rdev->rfkill))
                        return notifier_from_errno(-ERFKILL);
-               mutex_lock(&rdev->devlist_mtx);
                ret = cfg80211_can_add_interface(rdev, wdev->iftype);
-               mutex_unlock(&rdev->devlist_mtx);
                if (ret)
                        return notifier_from_errno(ret);
                break;
@@ -1089,12 +963,10 @@ static void __net_exit cfg80211_pernet_exit(struct net *net)
        struct cfg80211_registered_device *rdev;
 
        rtnl_lock();
-       mutex_lock(&cfg80211_mutex);
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                if (net_eq(wiphy_net(&rdev->wiphy), net))
                        WARN_ON(cfg80211_switch_netns(rdev, &init_net));
        }
-       mutex_unlock(&cfg80211_mutex);
        rtnl_unlock();
 }
 
index fd35dae..a6b45bf 100644 (file)
@@ -5,7 +5,6 @@
  */
 #ifndef __NET_WIRELESS_CORE_H
 #define __NET_WIRELESS_CORE_H
-#include <linux/mutex.h>
 #include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/rbtree.h>
 struct cfg80211_registered_device {
        const struct cfg80211_ops *ops;
        struct list_head list;
-       /* we hold this mutex during any call so that
-        * we cannot do multiple calls at once, and also
-        * to avoid the deregister call to proceed while
-        * any call is in progress */
-       struct mutex mtx;
 
        /* rfkill support */
        struct rfkill_ops rfkill_ops;
@@ -49,9 +43,7 @@ struct cfg80211_registered_device {
        /* wiphy index, internal only */
        int wiphy_idx;
 
-       /* associated wireless interfaces */
-       struct mutex devlist_mtx;
-       /* protected by devlist_mtx or RCU */
+       /* associated wireless interfaces, protected by rtnl or RCU */
        struct list_head wdev_list;
        int devlist_generation, wdev_id;
        int opencount; /* also protected by devlist_mtx */
@@ -75,8 +67,6 @@ struct cfg80211_registered_device {
        struct work_struct scan_done_wk;
        struct work_struct sched_scan_results_wk;
 
-       struct mutex sched_scan_mtx;
-
 #ifdef CONFIG_NL80211_TESTMODE
        struct genl_info *testmode_info;
 #endif
@@ -84,8 +74,6 @@ struct cfg80211_registered_device {
        struct work_struct conn_work;
        struct work_struct event_work;
 
-       struct cfg80211_wowlan *wowlan;
-
        struct delayed_work dfs_update_channels_wk;
 
        /* netlink port which started critical protocol (0 means not started) */
@@ -106,29 +94,26 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
 static inline void
 cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
 {
+#ifdef CONFIG_PM
        int i;
 
-       if (!rdev->wowlan)
+       if (!rdev->wiphy.wowlan_config)
                return;
-       for (i = 0; i < rdev->wowlan->n_patterns; i++)
-               kfree(rdev->wowlan->patterns[i].mask);
-       kfree(rdev->wowlan->patterns);
-       if (rdev->wowlan->tcp && rdev->wowlan->tcp->sock)
-               sock_release(rdev->wowlan->tcp->sock);
-       kfree(rdev->wowlan->tcp);
-       kfree(rdev->wowlan);
+       for (i = 0; i < rdev->wiphy.wowlan_config->n_patterns; i++)
+               kfree(rdev->wiphy.wowlan_config->patterns[i].mask);
+       kfree(rdev->wiphy.wowlan_config->patterns);
+       if (rdev->wiphy.wowlan_config->tcp &&
+           rdev->wiphy.wowlan_config->tcp->sock)
+               sock_release(rdev->wiphy.wowlan_config->tcp->sock);
+       kfree(rdev->wiphy.wowlan_config->tcp);
+       kfree(rdev->wiphy.wowlan_config);
+#endif
 }
 
 extern struct workqueue_struct *cfg80211_wq;
-extern struct mutex cfg80211_mutex;
 extern struct list_head cfg80211_rdev_list;
 extern int cfg80211_rdev_list_generation;
 
-static inline void assert_cfg80211_lock(void)
-{
-       lockdep_assert_held(&cfg80211_mutex);
-}
-
 struct cfg80211_internal_bss {
        struct list_head list;
        struct list_head hidden_list;
@@ -161,27 +146,11 @@ static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss)
 struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx);
 int get_wiphy_idx(struct wiphy *wiphy);
 
-/* requires cfg80211_rdev_mutex to be held! */
 struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx);
 
-/* identical to cfg80211_get_dev_from_info but only operate on ifindex */
-extern struct cfg80211_registered_device *
-cfg80211_get_dev_from_ifindex(struct net *net, int ifindex);
-
 int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
                          struct net *net);
 
-static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev)
-{
-       mutex_lock(&rdev->mtx);
-}
-
-static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *rdev)
-{
-       BUG_ON(IS_ERR(rdev) || !rdev);
-       mutex_unlock(&rdev->mtx);
-}
-
 static inline void wdev_lock(struct wireless_dev *wdev)
        __acquires(wdev)
 {
@@ -196,7 +165,7 @@ static inline void wdev_unlock(struct wireless_dev *wdev)
        mutex_unlock(&wdev->mtx);
 }
 
-#define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx)
+#define ASSERT_RDEV_LOCK(rdev) ASSERT_RTNL()
 #define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx)
 
 static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev)
@@ -314,38 +283,21 @@ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
                     struct net_device *dev);
 
 /* MLME */
-int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
-                        struct net_device *dev,
-                        struct ieee80211_channel *chan,
-                        enum nl80211_auth_type auth_type,
-                        const u8 *bssid,
-                        const u8 *ssid, int ssid_len,
-                        const u8 *ie, int ie_len,
-                        const u8 *key, int key_len, int key_idx,
-                        const u8 *sae_data, int sae_data_len);
 int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
-                      struct net_device *dev, struct ieee80211_channel *chan,
-                      enum nl80211_auth_type auth_type, const u8 *bssid,
+                      struct net_device *dev,
+                      struct ieee80211_channel *chan,
+                      enum nl80211_auth_type auth_type,
+                      const u8 *bssid,
                       const u8 *ssid, int ssid_len,
                       const u8 *ie, int ie_len,
                       const u8 *key, int key_len, int key_idx,
                       const u8 *sae_data, int sae_data_len);
-int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
-                         struct net_device *dev,
-                         struct ieee80211_channel *chan,
-                         const u8 *bssid,
-                         const u8 *ssid, int ssid_len,
-                         struct cfg80211_assoc_request *req);
 int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
                        struct net_device *dev,
                        struct ieee80211_channel *chan,
                        const u8 *bssid,
                        const u8 *ssid, int ssid_len,
                        struct cfg80211_assoc_request *req);
-int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
-                          struct net_device *dev, const u8 *bssid,
-                          const u8 *ie, int ie_len, u16 reason,
-                          bool local_state_change);
 int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
                         struct net_device *dev, const u8 *bssid,
                         const u8 *ie, int ie_len, u16 reason,
@@ -356,11 +308,6 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
                           bool local_state_change);
 void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
                        struct net_device *dev);
-void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
-                              const u8 *req_ie, size_t req_ie_len,
-                              const u8 *resp_ie, size_t resp_ie_len,
-                              u16 status, bool wextev,
-                              struct cfg80211_bss *bss);
 int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
                                u16 frame_type, const u8 *match_data,
                                int match_len);
@@ -376,19 +323,19 @@ void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
 void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
                                const struct ieee80211_vht_cap *vht_capa_mask);
 
-/* SME */
-int __cfg80211_connect(struct cfg80211_registered_device *rdev,
-                      struct net_device *dev,
-                      struct cfg80211_connect_params *connect,
-                      struct cfg80211_cached_keys *connkeys,
-                      const u8 *prev_bssid);
+/* SME events */
 int cfg80211_connect(struct cfg80211_registered_device *rdev,
                     struct net_device *dev,
                     struct cfg80211_connect_params *connect,
-                    struct cfg80211_cached_keys *connkeys);
-int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
-                         struct net_device *dev, u16 reason,
-                         bool wextev);
+                    struct cfg80211_cached_keys *connkeys,
+                    const u8 *prev_bssid);
+void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
+                              const u8 *req_ie, size_t req_ie_len,
+                              const u8 *resp_ie, size_t resp_ie_len,
+                              u16 status, bool wextev,
+                              struct cfg80211_bss *bss);
+void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
+                            size_t ie_len, u16 reason, bool from_ap);
 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
                        struct net_device *dev, u16 reason,
                        bool wextev);
@@ -399,21 +346,21 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
 int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
                              struct wireless_dev *wdev);
 
+/* SME implementation */
 void cfg80211_conn_work(struct work_struct *work);
-void cfg80211_sme_failed_assoc(struct wireless_dev *wdev);
-bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev);
+void cfg80211_sme_scan_done(struct net_device *dev);
+bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status);
+void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len);
+void cfg80211_sme_disassoc(struct wireless_dev *wdev);
+void cfg80211_sme_deauth(struct wireless_dev *wdev);
+void cfg80211_sme_auth_timeout(struct wireless_dev *wdev);
+void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev);
 
 /* internal helpers */
 bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
 int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
                                   struct key_params *params, int key_idx,
                                   bool pairwise, const u8 *mac_addr);
-void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
-                            size_t ie_len, u16 reason, bool from_ap);
-void cfg80211_sme_scan_done(struct net_device *dev);
-void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
-void cfg80211_sme_disassoc(struct net_device *dev,
-                          struct cfg80211_internal_bss *bss);
 void __cfg80211_scan_done(struct work_struct *wk);
 void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak);
 void __cfg80211_sched_scan_results(struct work_struct *wk);
index 920cabe..90d0500 100644 (file)
@@ -74,7 +74,7 @@ static ssize_t ht40allow_map_read(struct file *file,
        if (!buf)
                return -ENOMEM;
 
-       mutex_lock(&cfg80211_mutex);
+       rtnl_lock();
 
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                sband = wiphy->bands[band];
@@ -85,7 +85,7 @@ static ssize_t ht40allow_map_read(struct file *file,
                                                buf, buf_size, offset);
        }
 
-       mutex_unlock(&cfg80211_mutex);
+       rtnl_unlock();
 
        r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
 
index d80e471..39bff7d 100644 (file)
@@ -43,7 +43,6 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
        cfg80211_hold_bss(bss_from_pub(bss));
        wdev->current_bss = bss_from_pub(bss);
 
-       wdev->sme_state = CFG80211_SME_CONNECTED;
        cfg80211_upload_connect_keys(wdev);
 
        nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid,
@@ -64,8 +63,6 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
 
        trace_cfg80211_ibss_joined(dev, bssid);
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
-
        ev = kzalloc(sizeof(*ev), gfp);
        if (!ev)
                return;
@@ -120,7 +117,6 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.chandef = params->chandef;
 #endif
-       wdev->sme_state = CFG80211_SME_CONNECTING;
 
        err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
                                    params->channel_fixed
@@ -134,7 +130,6 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
        err = rdev_join_ibss(rdev, dev, params);
        if (err) {
                wdev->connect_keys = NULL;
-               wdev->sme_state = CFG80211_SME_IDLE;
                return err;
        }
 
@@ -152,11 +147,11 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
 
-       mutex_lock(&rdev->devlist_mtx);
+       ASSERT_RTNL();
+
        wdev_lock(wdev);
        err = __cfg80211_join_ibss(rdev, dev, params, connkeys);
        wdev_unlock(wdev);
-       mutex_unlock(&rdev->devlist_mtx);
 
        return err;
 }
@@ -186,7 +181,6 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
        }
 
        wdev->current_bss = NULL;
-       wdev->sme_state = CFG80211_SME_IDLE;
        wdev->ssid_len = 0;
 #ifdef CONFIG_CFG80211_WEXT
        if (!nowext)
@@ -359,11 +353,9 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
                wdev->wext.ibss.channel_fixed = false;
        }
 
-       mutex_lock(&rdev->devlist_mtx);
        wdev_lock(wdev);
        err = cfg80211_ibss_wext_join(rdev, wdev);
        wdev_unlock(wdev);
-       mutex_unlock(&rdev->devlist_mtx);
 
        return err;
 }
@@ -429,11 +421,9 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
        memcpy(wdev->wext.ibss.ssid, ssid, len);
        wdev->wext.ibss.ssid_len = len;
 
-       mutex_lock(&rdev->devlist_mtx);
        wdev_lock(wdev);
        err = cfg80211_ibss_wext_join(rdev, wdev);
        wdev_unlock(wdev);
-       mutex_unlock(&rdev->devlist_mtx);
 
        return err;
 }
@@ -512,11 +502,9 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
        } else
                wdev->wext.ibss.bssid = NULL;
 
-       mutex_lock(&rdev->devlist_mtx);
        wdev_lock(wdev);
        err = cfg80211_ibss_wext_join(rdev, wdev);
        wdev_unlock(wdev);
-       mutex_unlock(&rdev->devlist_mtx);
 
        return err;
 }
index 0bb93f3..30c4920 100644 (file)
@@ -18,6 +18,7 @@
 #define MESH_PATH_TO_ROOT_TIMEOUT      6000
 #define MESH_ROOT_INTERVAL     5000
 #define MESH_ROOT_CONFIRMATION_INTERVAL 2000
+#define MESH_DEFAULT_PLINK_TIMEOUT     1800 /* timeout in seconds */
 
 /*
  * Minimum interval between two consecutive PREQs originated by the same
@@ -75,6 +76,7 @@ const struct mesh_config default_mesh_config = {
        .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL,
        .power_mode = NL80211_MESH_POWER_ACTIVE,
        .dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW,
+       .plink_timeout = MESH_DEFAULT_PLINK_TIMEOUT,
 };
 
 const struct mesh_setup default_mesh_setup = {
@@ -82,6 +84,7 @@ const struct mesh_setup default_mesh_setup = {
        .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
        .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
        .path_metric = IEEE80211_PATH_METRIC_AIRTIME,
+       .auth_id = 0, /* open */
        .ie = NULL,
        .ie_len = 0,
        .is_secure = false,
@@ -159,6 +162,16 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
                setup->chandef.center_freq1 = setup->chandef.chan->center_freq;
        }
 
+       /*
+        * check if basic rates are available otherwise use mandatory rates as
+        * basic rates
+        */
+       if (!setup->basic_rates) {
+               struct ieee80211_supported_band *sband =
+                               rdev->wiphy.bands[setup->chandef.chan->band];
+               setup->basic_rates = ieee80211_mandatory_rates(sband);
+       }
+
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
                return -EINVAL;
 
@@ -185,11 +198,9 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
 
-       mutex_lock(&rdev->devlist_mtx);
        wdev_lock(wdev);
        err = __cfg80211_join_mesh(rdev, dev, setup, conf);
        wdev_unlock(wdev);
-       mutex_unlock(&rdev->devlist_mtx);
 
        return err;
 }
index 0c7b7dd..bfac5e1 100644 (file)
 #include "rdev-ops.h"
 
 
-void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_send_rx_auth(dev);
-       wdev_lock(wdev);
-
-       nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
-       cfg80211_sme_rx_auth(dev, buf, len);
-
-       wdev_unlock(wdev);
-}
-EXPORT_SYMBOL(cfg80211_send_rx_auth);
-
-void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
+void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
                            const u8 *buf, size_t len)
 {
-       u16 status_code;
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
        u8 *ie = mgmt->u.assoc_resp.variable;
        int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+       u16 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
 
        trace_cfg80211_send_rx_assoc(dev, bss);
-       wdev_lock(wdev);
-
-       status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
 
        /*
         * This is a bit of a hack, we don't notify userspace of
@@ -56,174 +37,135 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
         * and got a reject -- we only try again with an assoc
         * frame instead of reassoc.
         */
-       if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
-           cfg80211_sme_failed_reassoc(wdev)) {
+       if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) {
+               cfg80211_unhold_bss(bss_from_pub(bss));
                cfg80211_put_bss(wiphy, bss);
-               goto out;
+               return;
        }
 
        nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
-
-       if (status_code != WLAN_STATUS_SUCCESS && wdev->conn) {
-               cfg80211_sme_failed_assoc(wdev);
-               /*
-                * do not call connect_result() now because the
-                * sme will schedule work that does it later.
-                */
-               cfg80211_put_bss(wiphy, bss);
-               goto out;
-       }
-
-       if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
-               /*
-                * This is for the userspace SME, the CONNECTING
-                * state will be changed to CONNECTED by
-                * __cfg80211_connect_result() below.
-                */
-               wdev->sme_state = CFG80211_SME_CONNECTING;
-       }
-
-       /* this consumes the bss reference */
+       /* update current_bss etc., consumes the bss reference */
        __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
                                  status_code,
                                  status_code == WLAN_STATUS_SUCCESS, bss);
- out:
-       wdev_unlock(wdev);
 }
-EXPORT_SYMBOL(cfg80211_send_rx_assoc);
+EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
 
-void __cfg80211_send_deauth(struct net_device *dev,
-                                  const u8 *buf, size_t len)
+static void cfg80211_process_auth(struct wireless_dev *wdev,
+                                 const u8 *buf, size_t len)
 {
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
-       const u8 *bssid = mgmt->bssid;
-       bool was_current = false;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 
-       trace___cfg80211_send_deauth(dev);
-       ASSERT_WDEV_LOCK(wdev);
-
-       if (wdev->current_bss &&
-           ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
-               cfg80211_unhold_bss(wdev->current_bss);
-               cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
-               wdev->current_bss = NULL;
-               was_current = true;
-       }
+       nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
+       cfg80211_sme_rx_auth(wdev, buf, len);
+}
 
-       nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
+static void cfg80211_process_deauth(struct wireless_dev *wdev,
+                                   const u8 *buf, size_t len)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+       const u8 *bssid = mgmt->bssid;
+       u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+       bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
 
-       if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) {
-               u16 reason_code;
-               bool from_ap;
+       nl80211_send_deauth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
 
-               reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+       if (!wdev->current_bss ||
+           !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
+               return;
 
-               from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
-               __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
-       } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
-               __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         false, NULL);
-       }
+       __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
+       cfg80211_sme_deauth(wdev);
 }
-EXPORT_SYMBOL(__cfg80211_send_deauth);
 
-void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
+static void cfg80211_process_disassoc(struct wireless_dev *wdev,
+                                     const u8 *buf, size_t len)
 {
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+       const u8 *bssid = mgmt->bssid;
+       u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
+       bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
 
-       wdev_lock(wdev);
-       __cfg80211_send_deauth(dev, buf, len);
-       wdev_unlock(wdev);
+       nl80211_send_disassoc(rdev, wdev->netdev, buf, len, GFP_KERNEL);
+
+       if (WARN_ON(!wdev->current_bss ||
+                   !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
+               return;
+
+       __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
+       cfg80211_sme_disassoc(wdev);
 }
-EXPORT_SYMBOL(cfg80211_send_deauth);
 
-void __cfg80211_send_disassoc(struct net_device *dev,
-                                    const u8 *buf, size_t len)
+void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
-       const u8 *bssid = mgmt->bssid;
-       u16 reason_code;
-       bool from_ap;
+       struct ieee80211_mgmt *mgmt = (void *)buf;
 
-       trace___cfg80211_send_disassoc(dev);
        ASSERT_WDEV_LOCK(wdev);
 
-       nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
+       trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
+       if (WARN_ON(len < 2))
                return;
 
-       if (wdev->current_bss &&
-           ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
-               cfg80211_sme_disassoc(dev, wdev->current_bss);
-               cfg80211_unhold_bss(wdev->current_bss);
-               cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
-               wdev->current_bss = NULL;
-       } else
-               WARN_ON(1);
-
-
-       reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
-
-       from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
-       __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
+       if (ieee80211_is_auth(mgmt->frame_control))
+               cfg80211_process_auth(wdev, buf, len);
+       else if (ieee80211_is_deauth(mgmt->frame_control))
+               cfg80211_process_deauth(wdev, buf, len);
+       else if (ieee80211_is_disassoc(mgmt->frame_control))
+               cfg80211_process_disassoc(wdev, buf, len);
 }
-EXPORT_SYMBOL(__cfg80211_send_disassoc);
+EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt);
 
-void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
+void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_send_auth_timeout(dev, addr);
 
-       wdev_lock(wdev);
-       __cfg80211_send_disassoc(dev, buf, len);
-       wdev_unlock(wdev);
+       nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
+       cfg80211_sme_auth_timeout(wdev);
 }
-EXPORT_SYMBOL(cfg80211_send_disassoc);
+EXPORT_SYMBOL(cfg80211_auth_timeout);
 
-void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
+void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
 
-       trace_cfg80211_send_auth_timeout(dev, addr);
-       wdev_lock(wdev);
+       trace_cfg80211_send_assoc_timeout(dev, bss->bssid);
 
-       nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
-       if (wdev->sme_state == CFG80211_SME_CONNECTING)
-               __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         false, NULL);
+       nl80211_send_assoc_timeout(rdev, dev, bss->bssid, GFP_KERNEL);
+       cfg80211_sme_assoc_timeout(wdev);
 
-       wdev_unlock(wdev);
+       cfg80211_unhold_bss(bss_from_pub(bss));
+       cfg80211_put_bss(wiphy, bss);
 }
-EXPORT_SYMBOL(cfg80211_send_auth_timeout);
+EXPORT_SYMBOL(cfg80211_assoc_timeout);
 
-void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
+void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct ieee80211_mgmt *mgmt = (void *)buf;
 
-       trace_cfg80211_send_assoc_timeout(dev, addr);
-       wdev_lock(wdev);
+       ASSERT_WDEV_LOCK(wdev);
 
-       nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
-       if (wdev->sme_state == CFG80211_SME_CONNECTING)
-               __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         false, NULL);
+       trace_cfg80211_tx_mlme_mgmt(dev, buf, len);
 
-       wdev_unlock(wdev);
+       if (WARN_ON(len < 2))
+               return;
+
+       if (ieee80211_is_deauth(mgmt->frame_control))
+               cfg80211_process_deauth(wdev, buf, len);
+       else
+               cfg80211_process_disassoc(wdev, buf, len);
 }
-EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
+EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt);
 
 void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
                                  enum nl80211_key_type key_type, int key_id,
@@ -253,18 +195,27 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
 EXPORT_SYMBOL(cfg80211_michael_mic_failure);
 
 /* some MLME handling for userspace SME */
-int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
-                        struct net_device *dev,
-                        struct ieee80211_channel *chan,
-                        enum nl80211_auth_type auth_type,
-                        const u8 *bssid,
-                        const u8 *ssid, int ssid_len,
-                        const u8 *ie, int ie_len,
-                        const u8 *key, int key_len, int key_idx,
-                        const u8 *sae_data, int sae_data_len)
+int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
+                      struct net_device *dev,
+                      struct ieee80211_channel *chan,
+                      enum nl80211_auth_type auth_type,
+                      const u8 *bssid,
+                      const u8 *ssid, int ssid_len,
+                      const u8 *ie, int ie_len,
+                      const u8 *key, int key_len, int key_idx,
+                      const u8 *sae_data, int sae_data_len)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_auth_request req;
+       struct cfg80211_auth_request req = {
+               .ie = ie,
+               .ie_len = ie_len,
+               .sae_data = sae_data,
+               .sae_data_len = sae_data_len,
+               .auth_type = auth_type,
+               .key = key,
+               .key_len = key_len,
+               .key_idx = key_idx,
+       };
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -277,18 +228,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
            ether_addr_equal(bssid, wdev->current_bss->pub.bssid))
                return -EALREADY;
 
-       memset(&req, 0, sizeof(req));
-
-       req.ie = ie;
-       req.ie_len = ie_len;
-       req.sae_data = sae_data;
-       req.sae_data_len = sae_data_len;
-       req.auth_type = auth_type;
        req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
                                   WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
-       req.key = key;
-       req.key_len = key_len;
-       req.key_idx = key_idx;
        if (!req.bss)
                return -ENOENT;
 
@@ -304,28 +245,6 @@ out:
        return err;
 }
 
-int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
-                      struct net_device *dev, struct ieee80211_channel *chan,
-                      enum nl80211_auth_type auth_type, const u8 *bssid,
-                      const u8 *ssid, int ssid_len,
-                      const u8 *ie, int ie_len,
-                      const u8 *key, int key_len, int key_idx,
-                      const u8 *sae_data, int sae_data_len)
-{
-       int err;
-
-       mutex_lock(&rdev->devlist_mtx);
-       wdev_lock(dev->ieee80211_ptr);
-       err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
-                                  ssid, ssid_len, ie, ie_len,
-                                  key, key_len, key_idx,
-                                  sae_data, sae_data_len);
-       wdev_unlock(dev->ieee80211_ptr);
-       mutex_unlock(&rdev->devlist_mtx);
-
-       return err;
-}
-
 /*  Do a logical ht_capa &= ht_capa_mask.  */
 void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
                               const struct ieee80211_ht_cap *ht_capa_mask)
@@ -360,30 +279,21 @@ void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
                p1[i] &= p2[i];
 }
 
-int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
-                         struct net_device *dev,
-                         struct ieee80211_channel *chan,
-                         const u8 *bssid,
-                         const u8 *ssid, int ssid_len,
-                         struct cfg80211_assoc_request *req)
+int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+                       struct net_device *dev,
+                       struct ieee80211_channel *chan,
+                       const u8 *bssid,
+                       const u8 *ssid, int ssid_len,
+                       struct cfg80211_assoc_request *req)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
-       bool was_connected = false;
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->current_bss && req->prev_bssid &&
-           ether_addr_equal(wdev->current_bss->pub.bssid, req->prev_bssid)) {
-               /*
-                * Trying to reassociate: Allow this to proceed and let the old
-                * association to be dropped when the new one is completed.
-                */
-               if (wdev->sme_state == CFG80211_SME_CONNECTED) {
-                       was_connected = true;
-                       wdev->sme_state = CFG80211_SME_CONNECTING;
-               }
-       } else if (wdev->current_bss)
+       if (wdev->current_bss &&
+           (!req->prev_bssid || !ether_addr_equal(wdev->current_bss->pub.bssid,
+                                                  req->prev_bssid)))
                return -EALREADY;
 
        cfg80211_oper_and_ht_capa(&req->ht_capa_mask,
@@ -393,52 +303,28 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
 
        req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
                                    WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
-       if (!req->bss) {
-               if (was_connected)
-                       wdev->sme_state = CFG80211_SME_CONNECTED;
+       if (!req->bss)
                return -ENOENT;
-       }
 
        err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED);
        if (err)
                goto out;
 
        err = rdev_assoc(rdev, dev, req);
+       if (!err)
+               cfg80211_hold_bss(bss_from_pub(req->bss));
 
 out:
-       if (err) {
-               if (was_connected)
-                       wdev->sme_state = CFG80211_SME_CONNECTED;
+       if (err)
                cfg80211_put_bss(&rdev->wiphy, req->bss);
-       }
 
        return err;
 }
 
-int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
-                       struct net_device *dev,
-                       struct ieee80211_channel *chan,
-                       const u8 *bssid,
-                       const u8 *ssid, int ssid_len,
-                       struct cfg80211_assoc_request *req)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       int err;
-
-       mutex_lock(&rdev->devlist_mtx);
-       wdev_lock(wdev);
-       err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid,
-                                   ssid, ssid_len, req);
-       wdev_unlock(wdev);
-       mutex_unlock(&rdev->devlist_mtx);
-
-       return err;
-}
-
-int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
-                          struct net_device *dev, const u8 *bssid,
-                          const u8 *ie, int ie_len, u16 reason,
-                          bool local_state_change)
+int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
+                        struct net_device *dev, const u8 *bssid,
+                        const u8 *ie, int ie_len, u16 reason,
+                        bool local_state_change)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_deauth_request req = {
@@ -451,79 +337,51 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (local_state_change && (!wdev->current_bss ||
-           !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
+       if (local_state_change &&
+           (!wdev->current_bss ||
+            !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
                return 0;
 
        return rdev_deauth(rdev, dev, &req);
 }
 
-int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
-                        struct net_device *dev, const u8 *bssid,
-                        const u8 *ie, int ie_len, u16 reason,
-                        bool local_state_change)
+int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
+                          struct net_device *dev, const u8 *bssid,
+                          const u8 *ie, int ie_len, u16 reason,
+                          bool local_state_change)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_disassoc_request req = {
+               .reason_code = reason,
+               .local_state_change = local_state_change,
+               .ie = ie,
+               .ie_len = ie_len,
+       };
        int err;
 
-       wdev_lock(wdev);
-       err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason,
-                                    local_state_change);
-       wdev_unlock(wdev);
-
-       return err;
-}
-
-static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
-                                   struct net_device *dev, const u8 *bssid,
-                                   const u8 *ie, int ie_len, u16 reason,
-                                   bool local_state_change)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_disassoc_request req;
-
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
-               return -ENOTCONN;
-
-       if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state))
+       if (!wdev->current_bss)
                return -ENOTCONN;
 
-       memset(&req, 0, sizeof(req));
-       req.reason_code = reason;
-       req.local_state_change = local_state_change;
-       req.ie = ie;
-       req.ie_len = ie_len;
        if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
                req.bss = &wdev->current_bss->pub;
        else
                return -ENOTCONN;
 
-       return rdev_disassoc(rdev, dev, &req);
-}
-
-int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
-                          struct net_device *dev, const u8 *bssid,
-                          const u8 *ie, int ie_len, u16 reason,
-                          bool local_state_change)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       int err;
-
-       wdev_lock(wdev);
-       err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason,
-                                      local_state_change);
-       wdev_unlock(wdev);
+       err = rdev_disassoc(rdev, dev, &req);
+       if (err)
+               return err;
 
-       return err;
+       /* driver should have reported the disassoc */
+       WARN_ON(wdev->current_bss);
+       return 0;
 }
 
 void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
                        struct net_device *dev)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_deauth_request req;
        u8 bssid[ETH_ALEN];
 
        ASSERT_WDEV_LOCK(wdev);
@@ -531,23 +389,12 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
        if (!rdev->ops->deauth)
                return;
 
-       memset(&req, 0, sizeof(req));
-       req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
-       req.ie = NULL;
-       req.ie_len = 0;
-
        if (!wdev->current_bss)
                return;
 
        memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
-       req.bssid = bssid;
-       rdev_deauth(rdev, dev, &req);
-
-       if (wdev->current_bss) {
-               cfg80211_unhold_bss(wdev->current_bss);
-               cfg80211_put_bss(&rdev->wiphy, &wdev->current_bss->pub);
-               wdev->current_bss = NULL;
-       }
+       cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
+                            WLAN_REASON_DEAUTH_LEAVING, false);
 }
 
 struct cfg80211_mgmt_registration {
@@ -848,7 +695,7 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
                            dfs_update_channels_wk);
        wiphy = &rdev->wiphy;
 
-       mutex_lock(&cfg80211_mutex);
+       rtnl_lock();
        for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) {
                sband = wiphy->bands[bandid];
                if (!sband)
@@ -881,7 +728,7 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
                        check_again = true;
                }
        }
-       mutex_unlock(&cfg80211_mutex);
+       rtnl_unlock();
 
        /* reschedule if there are other channels waiting to be cleared again */
        if (check_again)
index b14b7e3..1cc47ac 100644 (file)
@@ -37,10 +37,10 @@ static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
 
 /* the netlink family */
 static struct genl_family nl80211_fam = {
-       .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
-       .name = "nl80211",      /* have users key off the name instead */
-       .hdrsize = 0,           /* no private header */
-       .version = 1,           /* no particular meaning now */
+       .id = GENL_ID_GENERATE,         /* don't bother with a hardcoded ID */
+       .name = NL80211_GENL_NAME,      /* have users key off the name instead */
+       .hdrsize = 0,                   /* no private header */
+       .version = 1,                   /* no particular meaning now */
        .maxattr = NL80211_ATTR_MAX,
        .netnsok = true,
        .pre_doit = nl80211_pre_doit,
@@ -59,7 +59,7 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
        int wiphy_idx = -1;
        int ifidx = -1;
 
-       assert_cfg80211_lock();
+       ASSERT_RTNL();
 
        if (!have_ifidx && !have_wdev_id)
                return ERR_PTR(-EINVAL);
@@ -80,7 +80,6 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
                if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
                        continue;
 
-               mutex_lock(&rdev->devlist_mtx);
                list_for_each_entry(wdev, &rdev->wdev_list, list) {
                        if (have_ifidx && wdev->netdev &&
                            wdev->netdev->ifindex == ifidx) {
@@ -92,7 +91,6 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
                                break;
                        }
                }
-               mutex_unlock(&rdev->devlist_mtx);
 
                if (result)
                        break;
@@ -109,7 +107,7 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
        struct cfg80211_registered_device *rdev = NULL, *tmp;
        struct net_device *netdev;
 
-       assert_cfg80211_lock();
+       ASSERT_RTNL();
 
        if (!attrs[NL80211_ATTR_WIPHY] &&
            !attrs[NL80211_ATTR_IFINDEX] &&
@@ -128,14 +126,12 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
                tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
                if (tmp) {
                        /* make sure wdev exists */
-                       mutex_lock(&tmp->devlist_mtx);
                        list_for_each_entry(wdev, &tmp->wdev_list, list) {
                                if (wdev->identifier != (u32)wdev_id)
                                        continue;
                                found = true;
                                break;
                        }
-                       mutex_unlock(&tmp->devlist_mtx);
 
                        if (!found)
                                tmp = NULL;
@@ -182,19 +178,6 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
 /*
  * This function returns a pointer to the driver
  * that the genl_info item that is passed refers to.
- * If successful, it returns non-NULL and also locks
- * the driver's mutex!
- *
- * This means that you need to call cfg80211_unlock_rdev()
- * before being allowed to acquire &cfg80211_mutex!
- *
- * This is necessary because we need to lock the global
- * mutex to get an item off the list safely, and then
- * we lock the rdev mutex so it doesn't go away under us.
- *
- * We don't want to keep cfg80211_mutex locked
- * for all the time in order to allow requests on
- * other interfaces to go through at the same time.
  *
  * The result of this can be a PTR_ERR and hence must
  * be checked with IS_ERR() for errors.
@@ -202,20 +185,7 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
 static struct cfg80211_registered_device *
 cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
 {
-       struct cfg80211_registered_device *rdev;
-
-       mutex_lock(&cfg80211_mutex);
-       rdev = __cfg80211_rdev_from_attrs(netns, info->attrs);
-
-       /* if it is not an error we grab the lock on
-        * it to assure it won't be going away while
-        * we operate on it */
-       if (!IS_ERR(rdev))
-               mutex_lock(&rdev->mtx);
-
-       mutex_unlock(&cfg80211_mutex);
-
-       return rdev;
+       return __cfg80211_rdev_from_attrs(netns, info->attrs);
 }
 
 /* policy for the attributes */
@@ -378,6 +348,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_MDID] = { .type = NLA_U16 },
        [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
                                  .len = IEEE80211_MAX_DATA_LEN },
+       [NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
 };
 
 /* policy for the key attributes */
@@ -455,7 +426,6 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
        int err;
 
        rtnl_lock();
-       mutex_lock(&cfg80211_mutex);
 
        if (!cb->args[0]) {
                err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
@@ -484,14 +454,12 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
                *rdev = wiphy_to_dev(wiphy);
                *wdev = NULL;
 
-               mutex_lock(&(*rdev)->devlist_mtx);
                list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
                        if (tmp->identifier == cb->args[1]) {
                                *wdev = tmp;
                                break;
                        }
                }
-               mutex_unlock(&(*rdev)->devlist_mtx);
 
                if (!*wdev) {
                        err = -ENODEV;
@@ -499,19 +467,14 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
                }
        }
 
-       cfg80211_lock_rdev(*rdev);
-
-       mutex_unlock(&cfg80211_mutex);
        return 0;
  out_unlock:
-       mutex_unlock(&cfg80211_mutex);
        rtnl_unlock();
        return err;
 }
 
 static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev)
 {
-       cfg80211_unlock_rdev(rdev);
        rtnl_unlock();
 }
 
@@ -837,12 +800,9 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
        case NL80211_IFTYPE_MESH_POINT:
                break;
        case NL80211_IFTYPE_ADHOC:
-               if (!wdev->current_bss)
-                       return -ENOLINK;
-               break;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
-               if (wdev->sme_state != CFG80211_SME_CONNECTED)
+               if (!wdev->current_bss)
                        return -ENOLINK;
                break;
        default:
@@ -945,7 +905,7 @@ nla_put_failure:
 static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
                                        struct sk_buff *msg)
 {
-       const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp;
+       const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan->tcp;
        struct nlattr *nl_tcp;
 
        if (!tcp)
@@ -988,37 +948,37 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
 {
        struct nlattr *nl_wowlan;
 
-       if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
+       if (!dev->wiphy.wowlan)
                return 0;
 
        nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
        if (!nl_wowlan)
                return -ENOBUFS;
 
-       if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
+       if (((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
                return -ENOBUFS;
 
-       if (dev->wiphy.wowlan.n_patterns) {
+       if (dev->wiphy.wowlan->n_patterns) {
                struct nl80211_wowlan_pattern_support pat = {
-                       .max_patterns = dev->wiphy.wowlan.n_patterns,
-                       .min_pattern_len = dev->wiphy.wowlan.pattern_min_len,
-                       .max_pattern_len = dev->wiphy.wowlan.pattern_max_len,
-                       .max_pkt_offset = dev->wiphy.wowlan.max_pkt_offset,
+                       .max_patterns = dev->wiphy.wowlan->n_patterns,
+                       .min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
+                       .max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
+                       .max_pkt_offset = dev->wiphy.wowlan->max_pkt_offset,
                };
 
                if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
@@ -1151,10 +1111,16 @@ nl80211_send_mgmt_stypes(struct sk_buff *msg,
        return 0;
 }
 
+struct nl80211_dump_wiphy_state {
+       s64 filter_wiphy;
+       long start;
+       long split_start, band_start, chan_start;
+       bool split;
+};
+
 static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                              struct sk_buff *msg, u32 portid, u32 seq,
-                             int flags, bool split, long *split_start,
-                             long *band_start, long *chan_start)
+                             int flags, struct nl80211_dump_wiphy_state *state)
 {
        void *hdr;
        struct nlattr *nl_bands, *nl_band;
@@ -1165,19 +1131,14 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
        int i;
        const struct ieee80211_txrx_stypes *mgmt_stypes =
                                dev->wiphy.mgmt_stypes;
-       long start = 0, start_chan = 0, start_band = 0;
        u32 features;
 
        hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
        if (!hdr)
                return -ENOBUFS;
 
-       /* allow always using the variables */
-       if (!split) {
-               split_start = &start;
-               band_start = &start_band;
-               chan_start = &start_chan;
-       }
+       if (WARN_ON(!state))
+               return -EINVAL;
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
            nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
@@ -1186,7 +1147,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        cfg80211_rdev_list_generation))
                goto nla_put_failure;
 
-       switch (*split_start) {
+       switch (state->split_start) {
        case 0:
                if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
                               dev->wiphy.retry_short) ||
@@ -1228,9 +1189,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
                    nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
                        goto nla_put_failure;
+               if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
+                   nla_put_flag(msg, WIPHY_FLAG_SUPPORTS_5_10_MHZ))
+                       goto nla_put_failure;
 
-               (*split_start)++;
-               if (split)
+               state->split_start++;
+               if (state->split)
                        break;
        case 1:
                if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
@@ -1274,22 +1238,23 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        }
                }
 
-               (*split_start)++;
-               if (split)
+               state->split_start++;
+               if (state->split)
                        break;
        case 2:
                if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
                                        dev->wiphy.interface_modes))
                                goto nla_put_failure;
-               (*split_start)++;
-               if (split)
+               state->split_start++;
+               if (state->split)
                        break;
        case 3:
                nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
                if (!nl_bands)
                        goto nla_put_failure;
 
-               for (band = *band_start; band < IEEE80211_NUM_BANDS; band++) {
+               for (band = state->band_start;
+                    band < IEEE80211_NUM_BANDS; band++) {
                        struct ieee80211_supported_band *sband;
 
                        sband = dev->wiphy.bands[band];
@@ -1301,12 +1266,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        if (!nl_band)
                                goto nla_put_failure;
 
-                       switch (*chan_start) {
+                       switch (state->chan_start) {
                        case 0:
                                if (nl80211_send_band_rateinfo(msg, sband))
                                        goto nla_put_failure;
-                               (*chan_start)++;
-                               if (split)
+                               state->chan_start++;
+                               if (state->split)
                                        break;
                        default:
                                /* add frequencies */
@@ -1315,7 +1280,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                                if (!nl_freqs)
                                        goto nla_put_failure;
 
-                               for (i = *chan_start - 1;
+                               for (i = state->chan_start - 1;
                                     i < sband->n_channels;
                                     i++) {
                                        nl_freq = nla_nest_start(msg, i);
@@ -1324,26 +1289,27 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
 
                                        chan = &sband->channels[i];
 
-                                       if (nl80211_msg_put_channel(msg, chan,
-                                                                   split))
+                                       if (nl80211_msg_put_channel(
+                                                       msg, chan,
+                                                       state->split))
                                                goto nla_put_failure;
 
                                        nla_nest_end(msg, nl_freq);
-                                       if (split)
+                                       if (state->split)
                                                break;
                                }
                                if (i < sband->n_channels)
-                                       *chan_start = i + 2;
+                                       state->chan_start = i + 2;
                                else
-                                       *chan_start = 0;
+                                       state->chan_start = 0;
                                nla_nest_end(msg, nl_freqs);
                        }
 
                        nla_nest_end(msg, nl_band);
 
-                       if (split) {
+                       if (state->split) {
                                /* start again here */
-                               if (*chan_start)
+                               if (state->chan_start)
                                        band--;
                                break;
                        }
@@ -1351,14 +1317,14 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                nla_nest_end(msg, nl_bands);
 
                if (band < IEEE80211_NUM_BANDS)
-                       *band_start = band + 1;
+                       state->band_start = band + 1;
                else
-                       *band_start = 0;
+                       state->band_start = 0;
 
                /* if bands & channels are done, continue outside */
-               if (*band_start == 0 && *chan_start == 0)
-                       (*split_start)++;
-               if (split)
+               if (state->band_start == 0 && state->chan_start == 0)
+                       state->split_start++;
+               if (state->split)
                        break;
        case 4:
                nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
@@ -1424,7 +1390,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                }
                CMD(start_p2p_device, START_P2P_DEVICE);
                CMD(set_mcast_rate, SET_MCAST_RATE);
-               if (split) {
+               if (state->split) {
                        CMD(crit_proto_start, CRIT_PROTOCOL_START);
                        CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
                }
@@ -1448,8 +1414,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                }
 
                nla_nest_end(msg, nl_cmds);
-               (*split_start)++;
-               if (split)
+               state->split_start++;
+               if (state->split)
                        break;
        case 5:
                if (dev->ops->remain_on_channel &&
@@ -1465,29 +1431,30 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
 
                if (nl80211_send_mgmt_stypes(msg, mgmt_stypes))
                        goto nla_put_failure;
-               (*split_start)++;
-               if (split)
+               state->split_start++;
+               if (state->split)
                        break;
        case 6:
 #ifdef CONFIG_PM
-               if (nl80211_send_wowlan(msg, dev, split))
+               if (nl80211_send_wowlan(msg, dev, state->split))
                        goto nla_put_failure;
-               (*split_start)++;
-               if (split)
+               state->split_start++;
+               if (state->split)
                        break;
 #else
-               (*split_start)++;
+               state->split_start++;
 #endif
        case 7:
                if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
                                        dev->wiphy.software_iftypes))
                        goto nla_put_failure;
 
-               if (nl80211_put_iface_combinations(&dev->wiphy, msg, split))
+               if (nl80211_put_iface_combinations(&dev->wiphy, msg,
+                                                  state->split))
                        goto nla_put_failure;
 
-               (*split_start)++;
-               if (split)
+               state->split_start++;
+               if (state->split)
                        break;
        case 8:
                if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
@@ -1501,7 +1468,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                 * dump is split, otherwise it makes it too big. Therefore
                 * only advertise it in that case.
                 */
-               if (split)
+               if (state->split)
                        features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
                if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
                        goto nla_put_failure;
@@ -1528,7 +1495,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                 * case we'll continue with more data in the next round,
                 * but break unconditionally so unsplit data stops here.
                 */
-               (*split_start)++;
+               state->split_start++;
                break;
        case 9:
                if (dev->wiphy.extended_capabilities &&
@@ -1547,7 +1514,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        goto nla_put_failure;
 
                /* done */
-               *split_start = 0;
+               state->split_start = 0;
                break;
        }
        return genlmsg_end(msg, hdr);
@@ -1557,66 +1524,78 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
        return -EMSGSIZE;
 }
 
+static int nl80211_dump_wiphy_parse(struct sk_buff *skb,
+                                   struct netlink_callback *cb,
+                                   struct nl80211_dump_wiphy_state *state)
+{
+       struct nlattr **tb = nl80211_fam.attrbuf;
+       int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+                             tb, nl80211_fam.maxattr, nl80211_policy);
+       /* ignore parse errors for backward compatibility */
+       if (ret)
+               return 0;
+
+       state->split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
+       if (tb[NL80211_ATTR_WIPHY])
+               state->filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+       if (tb[NL80211_ATTR_WDEV])
+               state->filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32;
+       if (tb[NL80211_ATTR_IFINDEX]) {
+               struct net_device *netdev;
+               struct cfg80211_registered_device *rdev;
+               int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+               netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
+               if (!netdev)
+                       return -ENODEV;
+               if (netdev->ieee80211_ptr) {
+                       rdev = wiphy_to_dev(
+                               netdev->ieee80211_ptr->wiphy);
+                       state->filter_wiphy = rdev->wiphy_idx;
+               }
+               dev_put(netdev);
+       }
+
+       return 0;
+}
+
 static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
 {
        int idx = 0, ret;
-       int start = cb->args[0];
+       struct nl80211_dump_wiphy_state *state = (void *)cb->args[0];
        struct cfg80211_registered_device *dev;
-       s64 filter_wiphy = -1;
-       bool split = false;
-       struct nlattr **tb;
-       int res;
 
-       /* will be zeroed in nlmsg_parse() */
-       tb = kmalloc(sizeof(*tb) * (NL80211_ATTR_MAX + 1), GFP_KERNEL);
-       if (!tb)
-               return -ENOMEM;
-
-       mutex_lock(&cfg80211_mutex);
-       res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
-                         tb, NL80211_ATTR_MAX, nl80211_policy);
-       if (res == 0) {
-               split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
-               if (tb[NL80211_ATTR_WIPHY])
-                       filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
-               if (tb[NL80211_ATTR_WDEV])
-                       filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32;
-               if (tb[NL80211_ATTR_IFINDEX]) {
-                       struct net_device *netdev;
-                       int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
-
-                       netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
-                       if (!netdev) {
-                               mutex_unlock(&cfg80211_mutex);
-                               kfree(tb);
-                               return -ENODEV;
-                       }
-                       if (netdev->ieee80211_ptr) {
-                               dev = wiphy_to_dev(
-                                       netdev->ieee80211_ptr->wiphy);
-                               filter_wiphy = dev->wiphy_idx;
-                       }
-                       dev_put(netdev);
+       rtnl_lock();
+       if (!state) {
+               state = kzalloc(sizeof(*state), GFP_KERNEL);
+               if (!state) {
+                       rtnl_unlock();
+                       return -ENOMEM;
+               }
+               state->filter_wiphy = -1;
+               ret = nl80211_dump_wiphy_parse(skb, cb, state);
+               if (ret) {
+                       kfree(state);
+                       rtnl_unlock();
+                       return ret;
                }
+               cb->args[0] = (long)state;
        }
-       kfree(tb);
 
        list_for_each_entry(dev, &cfg80211_rdev_list, list) {
                if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
                        continue;
-               if (++idx <= start)
+               if (++idx <= state->start)
                        continue;
-               if (filter_wiphy != -1 && dev->wiphy_idx != filter_wiphy)
+               if (state->filter_wiphy != -1 &&
+                   state->filter_wiphy != dev->wiphy_idx)
                        continue;
                /* attempt to fit multiple wiphy data chunks into the skb */
                do {
                        ret = nl80211_send_wiphy(dev, skb,
                                                 NETLINK_CB(cb->skb).portid,
                                                 cb->nlh->nlmsg_seq,
-                                                NLM_F_MULTI,
-                                                split, &cb->args[1],
-                                                &cb->args[2],
-                                                &cb->args[3]);
+                                                NLM_F_MULTI, state);
                        if (ret < 0) {
                                /*
                                 * If sending the wiphy data didn't fit (ENOBUFS
@@ -1635,33 +1614,40 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
                                    !skb->len &&
                                    cb->min_dump_alloc < 4096) {
                                        cb->min_dump_alloc = 4096;
-                                       mutex_unlock(&cfg80211_mutex);
+                                       rtnl_unlock();
                                        return 1;
                                }
                                idx--;
                                break;
                        }
-               } while (cb->args[1] > 0);
+               } while (state->split_start > 0);
                break;
        }
-       mutex_unlock(&cfg80211_mutex);
+       rtnl_unlock();
 
-       cb->args[0] = idx;
+       state->start = idx;
 
        return skb->len;
 }
 
+static int nl80211_dump_wiphy_done(struct netlink_callback *cb)
+{
+       kfree((void *)cb->args[0]);
+       return 0;
+}
+
 static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
 {
        struct sk_buff *msg;
        struct cfg80211_registered_device *dev = info->user_ptr[0];
+       struct nl80211_dump_wiphy_state state = {};
 
        msg = nlmsg_new(4096, GFP_KERNEL);
        if (!msg)
                return -ENOMEM;
 
        if (nl80211_send_wiphy(dev, msg, info->snd_portid, info->snd_seq, 0,
-                              false, NULL, NULL, NULL) < 0) {
+                              &state) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
@@ -1778,6 +1764,11 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
                                     IEEE80211_CHAN_DISABLED))
                return -EINVAL;
 
+       if ((chandef->width == NL80211_CHAN_WIDTH_5 ||
+            chandef->width == NL80211_CHAN_WIDTH_10) &&
+           !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ))
+               return -EINVAL;
+
        return 0;
 }
 
@@ -1799,7 +1790,6 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
        if (result)
                return result;
 
-       mutex_lock(&rdev->devlist_mtx);
        switch (iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
@@ -1823,7 +1813,6 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
        default:
                result = -EINVAL;
        }
-       mutex_unlock(&rdev->devlist_mtx);
 
        return result;
 }
@@ -1872,6 +1861,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        u32 frag_threshold = 0, rts_threshold = 0;
        u8 coverage_class = 0;
 
+       ASSERT_RTNL();
+
        /*
         * Try to find the wiphy and netdev. Normally this
         * function shouldn't need the netdev, but this is
@@ -1881,31 +1872,25 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
         * also passed a netdev to set_wiphy, so that it is
         * possible to let that go to the right netdev!
         */
-       mutex_lock(&cfg80211_mutex);
 
        if (info->attrs[NL80211_ATTR_IFINDEX]) {
                int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
 
                netdev = dev_get_by_index(genl_info_net(info), ifindex);
-               if (netdev && netdev->ieee80211_ptr) {
+               if (netdev && netdev->ieee80211_ptr)
                        rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
-                       mutex_lock(&rdev->mtx);
-               } else
+               else
                        netdev = NULL;
        }
 
        if (!netdev) {
                rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
                                                  info->attrs);
-               if (IS_ERR(rdev)) {
-                       mutex_unlock(&cfg80211_mutex);
+               if (IS_ERR(rdev))
                        return PTR_ERR(rdev);
-               }
                wdev = NULL;
                netdev = NULL;
                result = 0;
-
-               mutex_lock(&rdev->mtx);
        } else
                wdev = netdev->ieee80211_ptr;
 
@@ -1918,8 +1903,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                result = cfg80211_dev_rename(
                        rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
 
-       mutex_unlock(&cfg80211_mutex);
-
        if (result)
                goto bad_res;
 
@@ -2126,7 +2109,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        }
 
  bad_res:
-       mutex_unlock(&rdev->mtx);
        if (netdev)
                dev_put(netdev);
        return result;
@@ -2224,7 +2206,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
        struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
 
-       mutex_lock(&cfg80211_mutex);
+       rtnl_lock();
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
                        continue;
@@ -2234,7 +2216,6 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
                }
                if_idx = 0;
 
-               mutex_lock(&rdev->devlist_mtx);
                list_for_each_entry(wdev, &rdev->wdev_list, list) {
                        if (if_idx < if_start) {
                                if_idx++;
@@ -2243,17 +2224,15 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
                        if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
                                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
                                               rdev, wdev) < 0) {
-                               mutex_unlock(&rdev->devlist_mtx);
                                goto out;
                        }
                        if_idx++;
                }
-               mutex_unlock(&rdev->devlist_mtx);
 
                wp_idx++;
        }
  out:
-       mutex_unlock(&cfg80211_mutex);
+       rtnl_unlock();
 
        cb->args[0] = wp_idx;
        cb->args[1] = if_idx;
@@ -2286,6 +2265,7 @@ static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
        [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
        [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
        [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
+       [NL80211_MNTR_FLAG_ACTIVE] = { .type = NLA_FLAG },
 };
 
 static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
@@ -2397,6 +2377,10 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
                change = true;
        }
 
+       if (flags && (*flags & NL80211_MNTR_FLAG_ACTIVE) &&
+           !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
+               return -EOPNOTSUPP;
+
        if (change)
                err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
        else
@@ -2454,6 +2438,11 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
                                  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
                                  &flags);
+
+       if (!err && (flags & NL80211_MNTR_FLAG_ACTIVE) &&
+           !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
+               return -EOPNOTSUPP;
+
        wdev = rdev_add_virtual_intf(rdev,
                                nla_data(info->attrs[NL80211_ATTR_IFNAME]),
                                type, err ? NULL : &flags, &params);
@@ -2486,11 +2475,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                INIT_LIST_HEAD(&wdev->mgmt_registrations);
                spin_lock_init(&wdev->mgmt_registrations_lock);
 
-               mutex_lock(&rdev->devlist_mtx);
                wdev->identifier = ++rdev->wdev_id;
                list_add_rcu(&wdev->list, &rdev->wdev_list);
                rdev->devlist_generation++;
-               mutex_unlock(&rdev->devlist_mtx);
                break;
        default:
                break;
@@ -2933,61 +2920,58 @@ static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
-static int nl80211_parse_beacon(struct genl_info *info,
+static int nl80211_parse_beacon(struct nlattr *attrs[],
                                struct cfg80211_beacon_data *bcn)
 {
        bool haveinfo = false;
 
-       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
-           !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
-           !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
-           !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
+       if (!is_valid_ie_attr(attrs[NL80211_ATTR_BEACON_TAIL]) ||
+           !is_valid_ie_attr(attrs[NL80211_ATTR_IE]) ||
+           !is_valid_ie_attr(attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
+           !is_valid_ie_attr(attrs[NL80211_ATTR_IE_ASSOC_RESP]))
                return -EINVAL;
 
        memset(bcn, 0, sizeof(*bcn));
 
-       if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
-               bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
-               bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
+       if (attrs[NL80211_ATTR_BEACON_HEAD]) {
+               bcn->head = nla_data(attrs[NL80211_ATTR_BEACON_HEAD]);
+               bcn->head_len = nla_len(attrs[NL80211_ATTR_BEACON_HEAD]);
                if (!bcn->head_len)
                        return -EINVAL;
                haveinfo = true;
        }
 
-       if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
-               bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
-               bcn->tail_len =
-                   nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
+       if (attrs[NL80211_ATTR_BEACON_TAIL]) {
+               bcn->tail = nla_data(attrs[NL80211_ATTR_BEACON_TAIL]);
+               bcn->tail_len = nla_len(attrs[NL80211_ATTR_BEACON_TAIL]);
                haveinfo = true;
        }
 
        if (!haveinfo)
                return -EINVAL;
 
-       if (info->attrs[NL80211_ATTR_IE]) {
-               bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
-               bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       if (attrs[NL80211_ATTR_IE]) {
+               bcn->beacon_ies = nla_data(attrs[NL80211_ATTR_IE]);
+               bcn->beacon_ies_len = nla_len(attrs[NL80211_ATTR_IE]);
        }
 
-       if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
+       if (attrs[NL80211_ATTR_IE_PROBE_RESP]) {
                bcn->proberesp_ies =
-                       nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
+                       nla_data(attrs[NL80211_ATTR_IE_PROBE_RESP]);
                bcn->proberesp_ies_len =
-                       nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
+                       nla_len(attrs[NL80211_ATTR_IE_PROBE_RESP]);
        }
 
-       if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
+       if (attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
                bcn->assocresp_ies =
-                       nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
+                       nla_data(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
                bcn->assocresp_ies_len =
-                       nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
+                       nla_len(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
        }
 
-       if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
-               bcn->probe_resp =
-                       nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
-               bcn->probe_resp_len =
-                       nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
+       if (attrs[NL80211_ATTR_PROBE_RESP]) {
+               bcn->probe_resp = nla_data(attrs[NL80211_ATTR_PROBE_RESP]);
+               bcn->probe_resp_len = nla_len(attrs[NL80211_ATTR_PROBE_RESP]);
        }
 
        return 0;
@@ -2999,8 +2983,6 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
        struct wireless_dev *wdev;
        bool ret = false;
 
-       mutex_lock(&rdev->devlist_mtx);
-
        list_for_each_entry(wdev, &rdev->wdev_list, list) {
                if (wdev->iftype != NL80211_IFTYPE_AP &&
                    wdev->iftype != NL80211_IFTYPE_P2P_GO)
@@ -3014,8 +2996,6 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
                break;
        }
 
-       mutex_unlock(&rdev->devlist_mtx);
-
        return ret;
 }
 
@@ -3070,7 +3050,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
            !info->attrs[NL80211_ATTR_BEACON_HEAD])
                return -EINVAL;
 
-       err = nl80211_parse_beacon(info, &params.beacon);
+       err = nl80211_parse_beacon(info->attrs, &params.beacon);
        if (err)
                return err;
 
@@ -3177,13 +3157,10 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                params.radar_required = true;
        }
 
-       mutex_lock(&rdev->devlist_mtx);
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
                                           params.chandef.chan,
                                           CHAN_MODE_SHARED,
                                           radar_detect_width);
-       mutex_unlock(&rdev->devlist_mtx);
-
        if (err)
                return err;
 
@@ -3225,7 +3202,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
        if (!wdev->beacon_interval)
                return -EINVAL;
 
-       err = nl80211_parse_beacon(info, &params);
+       err = nl80211_parse_beacon(info->attrs, &params);
        if (err)
                return err;
 
@@ -3383,6 +3360,32 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
        return true;
 }
 
+static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
+                              int id)
+{
+       void *attr;
+       int i = 0;
+
+       if (!mask)
+               return true;
+
+       attr = nla_nest_start(msg, id);
+       if (!attr)
+               return false;
+
+       for (i = 0; i < IEEE80211_MAX_CHAINS; i++) {
+               if (!(mask & BIT(i)))
+                       continue;
+
+               if (nla_put_u8(msg, i, signal[i]))
+                       return false;
+       }
+
+       nla_nest_end(msg, attr);
+
+       return true;
+}
+
 static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
                                int flags,
                                struct cfg80211_registered_device *rdev,
@@ -3454,6 +3457,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
        default:
                break;
        }
+       if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) {
+               if (!nl80211_put_signal(msg, sinfo->chains,
+                                       sinfo->chain_signal,
+                                       NL80211_STA_INFO_CHAIN_SIGNAL))
+                       goto nla_put_failure;
+       }
+       if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) {
+               if (!nl80211_put_signal(msg, sinfo->chains,
+                                       sinfo->chain_signal_avg,
+                                       NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
+                       goto nla_put_failure;
+       }
        if (sinfo->filled & STATION_INFO_TX_BITRATE) {
                if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
                                          NL80211_STA_INFO_TX_BITRATE))
@@ -3841,6 +3856,8 @@ static int nl80211_set_station_tdls(struct genl_info *info,
                                    struct station_parameters *params)
 {
        /* Dummy STA entry gets updated once the peer capabilities are known */
+       if (info->attrs[NL80211_ATTR_PEER_AID])
+               params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
        if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
                params->ht_capa =
                        nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
@@ -3981,7 +3998,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
        if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
                return -EINVAL;
 
-       if (!info->attrs[NL80211_ATTR_STA_AID])
+       if (!info->attrs[NL80211_ATTR_STA_AID] &&
+           !info->attrs[NL80211_ATTR_PEER_AID])
                return -EINVAL;
 
        mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
@@ -3992,7 +4010,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
        params.listen_interval =
                nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
 
-       params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
+       if (info->attrs[NL80211_ATTR_PEER_AID])
+               params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
+       else
+               params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
        if (!params.aid || params.aid > IEEE80211_MAX_AID)
                return -EINVAL;
 
@@ -4044,7 +4065,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
 
                /* TDLS peers cannot be added */
-               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+               if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+                   info->attrs[NL80211_ATTR_PEER_AID])
                        return -EINVAL;
                /* but don't bother the driver with it */
                params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
@@ -4070,7 +4092,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
                        return -EINVAL;
                /* TDLS peers cannot be added */
-               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+               if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+                   info->attrs[NL80211_ATTR_PEER_AID])
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_STATION:
@@ -4592,7 +4615,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
            nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE,
                        cur_params.power_mode) ||
            nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
-                       cur_params.dot11MeshAwakeWindowDuration))
+                       cur_params.dot11MeshAwakeWindowDuration) ||
+           nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+                       cur_params.plink_timeout))
                goto nla_put_failure;
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
@@ -4633,6 +4658,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
        [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
        [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 },
        [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 },
 };
 
 static const struct nla_policy
@@ -4641,6 +4667,7 @@ static const struct nla_policy
        [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
+       [NL80211_MESH_SETUP_AUTH_PROTOCOL] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
        [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
                                    .len = IEEE80211_MAX_DATA_LEN },
@@ -4769,6 +4796,9 @@ do {                                                                          \
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
                                  0, 65535, mask,
                                  NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 1, 0xffffffff,
+                                 mask, NL80211_MESHCONF_PLINK_TIMEOUT,
+                                 nla_get_u32);
        if (mask_out)
                *mask_out = mask;
 
@@ -4826,6 +4856,13 @@ static int nl80211_parse_mesh_setup(struct genl_info *info,
        if (setup->is_secure)
                setup->user_mpm = true;
 
+       if (tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]) {
+               if (!setup->user_mpm)
+                       return -EINVAL;
+               setup->auth_id =
+                       nla_get_u8(tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]);
+       }
+
        return 0;
 }
 
@@ -4868,18 +4905,13 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
        void *hdr = NULL;
        struct nlattr *nl_reg_rules;
        unsigned int i;
-       int err = -EINVAL;
-
-       mutex_lock(&cfg80211_mutex);
 
        if (!cfg80211_regdomain)
-               goto out;
+               return -EINVAL;
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg) {
-               err = -ENOBUFS;
-               goto out;
-       }
+       if (!msg)
+               return -ENOBUFS;
 
        hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
                             NL80211_CMD_GET_REG);
@@ -4938,8 +4970,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
        nla_nest_end(msg, nl_reg_rules);
 
        genlmsg_end(msg, hdr);
-       err = genlmsg_reply(msg, info);
-       goto out;
+       return genlmsg_reply(msg, info);
 
 nla_put_failure_rcu:
        rcu_read_unlock();
@@ -4947,10 +4978,7 @@ nla_put_failure:
        genlmsg_cancel(msg, hdr);
 put_failure:
        nlmsg_free(msg);
-       err = -EMSGSIZE;
-out:
-       mutex_unlock(&cfg80211_mutex);
-       return err;
+       return -EMSGSIZE;
 }
 
 static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
@@ -5016,12 +5044,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       mutex_lock(&cfg80211_mutex);
-
        r = set_regdom(rd);
        /* set_regdom took ownership */
        rd = NULL;
-       mutex_unlock(&cfg80211_mutex);
 
  bad_reg:
        kfree(rd);
@@ -5071,7 +5096,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        if (!rdev->ops->scan)
                return -EOPNOTSUPP;
 
-       mutex_lock(&rdev->sched_scan_mtx);
        if (rdev->scan_req) {
                err = -EBUSY;
                goto unlock;
@@ -5257,7 +5281,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        }
 
  unlock:
-       mutex_unlock(&rdev->sched_scan_mtx);
        return err;
 }
 
@@ -5329,8 +5352,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        if (ie_len > wiphy->max_sched_scan_ie_len)
                return -EINVAL;
 
-       mutex_lock(&rdev->sched_scan_mtx);
-
        if (rdev->sched_scan_req) {
                err = -EINPROGRESS;
                goto out;
@@ -5498,7 +5519,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
 out_free:
        kfree(request);
 out:
-       mutex_unlock(&rdev->sched_scan_mtx);
        return err;
 }
 
@@ -5506,17 +5526,12 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb,
                                   struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
-       int err;
 
        if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
            !rdev->ops->sched_scan_stop)
                return -EOPNOTSUPP;
 
-       mutex_lock(&rdev->sched_scan_mtx);
-       err = __cfg80211_stop_sched_scan(rdev, false);
-       mutex_unlock(&rdev->sched_scan_mtx);
-
-       return err;
+       return __cfg80211_stop_sched_scan(rdev, false);
 }
 
 static int nl80211_start_radar_detection(struct sk_buff *skb,
@@ -5548,12 +5563,11 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        if (!rdev->ops->start_radar_detection)
                return -EOPNOTSUPP;
 
-       mutex_lock(&rdev->devlist_mtx);
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
                                           chandef.chan, CHAN_MODE_SHARED,
                                           BIT(chandef.width));
        if (err)
-               goto err_locked;
+               return err;
 
        err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
        if (!err) {
@@ -5561,9 +5575,6 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
                wdev->cac_started = true;
                wdev->cac_start_time = jiffies;
        }
-err_locked:
-       mutex_unlock(&rdev->devlist_mtx);
-
        return err;
 }
 
@@ -5946,10 +5957,13 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
        if (local_state_change)
                return 0;
 
-       return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
-                                 ssid, ssid_len, ie, ie_len,
-                                 key.p.key, key.p.key_len, key.idx,
-                                 sae_data, sae_data_len);
+       wdev_lock(dev->ieee80211_ptr);
+       err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
+                                ssid, ssid_len, ie, ie_len,
+                                key.p.key, key.p.key_len, key.idx,
+                                sae_data, sae_data_len);
+       wdev_unlock(dev->ieee80211_ptr);
+       return err;
 }
 
 static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
@@ -6116,9 +6130,12 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        }
 
        err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
-       if (!err)
+       if (!err) {
+               wdev_lock(dev->ieee80211_ptr);
                err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
                                          ssid, ssid_len, &req);
+               wdev_unlock(dev->ieee80211_ptr);
+       }
 
        return err;
 }
@@ -6128,7 +6145,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        const u8 *ie = NULL, *bssid;
-       int ie_len = 0;
+       int ie_len = 0, err;
        u16 reason_code;
        bool local_state_change;
 
@@ -6163,8 +6180,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
 
        local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
 
-       return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
-                                   local_state_change);
+       wdev_lock(dev->ieee80211_ptr);
+       err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
+                                  local_state_change);
+       wdev_unlock(dev->ieee80211_ptr);
+       return err;
 }
 
 static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
@@ -6172,7 +6192,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        const u8 *ie = NULL, *bssid;
-       int ie_len = 0;
+       int ie_len = 0, err;
        u16 reason_code;
        bool local_state_change;
 
@@ -6207,8 +6227,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
 
        local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
 
-       return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
-                                     local_state_change);
+       wdev_lock(dev->ieee80211_ptr);
+       err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
+                                    local_state_change);
+       wdev_unlock(dev->ieee80211_ptr);
+       return err;
 }
 
 static bool
@@ -6295,11 +6318,16 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
                return -EINVAL;
 
-       if (ibss.chandef.width > NL80211_CHAN_WIDTH_40)
-               return -EINVAL;
-       if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
-           !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
+       switch (ibss.chandef.width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               break;
+       case NL80211_CHAN_WIDTH_20:
+       case NL80211_CHAN_WIDTH_40:
+               if (rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)
+                       break;
+       default:
                return -EINVAL;
+       }
 
        ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
        ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
@@ -6426,6 +6454,8 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
        void *data = NULL;
        int data_len = 0;
 
+       rtnl_lock();
+
        if (cb->args[0]) {
                /*
                 * 0 is a valid index, but not valid for args[0],
@@ -6437,18 +6467,16 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
                                  nl80211_fam.attrbuf, nl80211_fam.maxattr,
                                  nl80211_policy);
                if (err)
-                       return err;
+                       goto out_err;
 
-               mutex_lock(&cfg80211_mutex);
                rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk),
                                                  nl80211_fam.attrbuf);
                if (IS_ERR(rdev)) {
-                       mutex_unlock(&cfg80211_mutex);
-                       return PTR_ERR(rdev);
+                       err = PTR_ERR(rdev);
+                       goto out_err;
                }
                phy_idx = rdev->wiphy_idx;
                rdev = NULL;
-               mutex_unlock(&cfg80211_mutex);
 
                if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
                        cb->args[1] =
@@ -6460,14 +6488,11 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
                data_len = nla_len((void *)cb->args[1]);
        }
 
-       mutex_lock(&cfg80211_mutex);
        rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
        if (!rdev) {
-               mutex_unlock(&cfg80211_mutex);
-               return -ENOENT;
+               err = -ENOENT;
+               goto out_err;
        }
-       cfg80211_lock_rdev(rdev);
-       mutex_unlock(&cfg80211_mutex);
 
        if (!rdev->ops->testmode_dump) {
                err = -EOPNOTSUPP;
@@ -6508,7 +6533,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
        /* see above */
        cb->args[0] = phy_idx + 1;
  out_err:
-       cfg80211_unlock_rdev(rdev);
+       rtnl_unlock();
        return err;
 }
 
@@ -6716,7 +6741,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                       sizeof(connect.vht_capa));
        }
 
-       err = cfg80211_connect(rdev, dev, &connect, connkeys);
+       wdev_lock(dev->ieee80211_ptr);
+       err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
+       wdev_unlock(dev->ieee80211_ptr);
        if (err)
                kfree(connkeys);
        return err;
@@ -6727,6 +6754,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        u16 reason;
+       int ret;
 
        if (!info->attrs[NL80211_ATTR_REASON_CODE])
                reason = WLAN_REASON_DEAUTH_LEAVING;
@@ -6740,7 +6768,10 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
                return -EOPNOTSUPP;
 
-       return cfg80211_disconnect(rdev, dev, reason, true);
+       wdev_lock(dev->ieee80211_ptr);
+       ret = cfg80211_disconnect(rdev, dev, reason, true);
+       wdev_unlock(dev->ieee80211_ptr);
+       return ret;
 }
 
 static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
@@ -7159,6 +7190,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                return -EOPNOTSUPP;
 
        switch (wdev->iftype) {
+       case NL80211_IFTYPE_P2P_DEVICE:
+               if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+                       return -EINVAL;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_P2P_CLIENT:
@@ -7166,7 +7200,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
-       case NL80211_IFTYPE_P2P_DEVICE:
                break;
        default:
                return -EOPNOTSUPP;
@@ -7194,9 +7227,18 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 
        no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
 
-       err = nl80211_parse_chandef(rdev, info, &chandef);
-       if (err)
-               return err;
+       /* get the channel if any has been specified, otherwise pass NULL to
+        * the driver. The latter will use the current one
+        */
+       chandef.chan = NULL;
+       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+               err = nl80211_parse_chandef(rdev, info, &chandef);
+               if (err)
+                       return err;
+       }
+
+       if (!chandef.chan && offchan)
+               return -EINVAL;
 
        if (!dont_wait_for_ack) {
                msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -7501,6 +7543,23 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
                setup.chandef.chan = NULL;
        }
 
+       if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+               u8 *rates = nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+               int n_rates =
+                       nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+               struct ieee80211_supported_band *sband;
+
+               if (!setup.chandef.chan)
+                       return -EINVAL;
+
+               sband = rdev->wiphy.bands[setup.chandef.chan->band];
+
+               err = ieee80211_get_ratemask(sband, rates, n_rates,
+                                            &setup.basic_rates);
+               if (err)
+                       return err;
+       }
+
        return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
 }
 
@@ -7516,28 +7575,29 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
 static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
                                        struct cfg80211_registered_device *rdev)
 {
+       struct cfg80211_wowlan *wowlan = rdev->wiphy.wowlan_config;
        struct nlattr *nl_pats, *nl_pat;
        int i, pat_len;
 
-       if (!rdev->wowlan->n_patterns)
+       if (!wowlan->n_patterns)
                return 0;
 
        nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN);
        if (!nl_pats)
                return -ENOBUFS;
 
-       for (i = 0; i < rdev->wowlan->n_patterns; i++) {
+       for (i = 0; i < wowlan->n_patterns; i++) {
                nl_pat = nla_nest_start(msg, i + 1);
                if (!nl_pat)
                        return -ENOBUFS;
-               pat_len = rdev->wowlan->patterns[i].pattern_len;
+               pat_len = wowlan->patterns[i].pattern_len;
                if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
                            DIV_ROUND_UP(pat_len, 8),
-                           rdev->wowlan->patterns[i].mask) ||
+                           wowlan->patterns[i].mask) ||
                    nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
-                           pat_len, rdev->wowlan->patterns[i].pattern) ||
+                           pat_len, wowlan->patterns[i].pattern) ||
                    nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
-                               rdev->wowlan->patterns[i].pkt_offset))
+                               wowlan->patterns[i].pkt_offset))
                        return -ENOBUFS;
                nla_nest_end(msg, nl_pat);
        }
@@ -7596,16 +7656,15 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
        void *hdr;
        u32 size = NLMSG_DEFAULT_SIZE;
 
-       if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
-           !rdev->wiphy.wowlan.tcp)
+       if (!rdev->wiphy.wowlan)
                return -EOPNOTSUPP;
 
-       if (rdev->wowlan && rdev->wowlan->tcp) {
+       if (rdev->wiphy.wowlan_config && rdev->wiphy.wowlan_config->tcp) {
                /* adjust size to have room for all the data */
-               size += rdev->wowlan->tcp->tokens_size +
-                       rdev->wowlan->tcp->payload_len +
-                       rdev->wowlan->tcp->wake_len +
-                       rdev->wowlan->tcp->wake_len / 8;
+               size += rdev->wiphy.wowlan_config->tcp->tokens_size +
+                       rdev->wiphy.wowlan_config->tcp->payload_len +
+                       rdev->wiphy.wowlan_config->tcp->wake_len +
+                       rdev->wiphy.wowlan_config->tcp->wake_len / 8;
        }
 
        msg = nlmsg_new(size, GFP_KERNEL);
@@ -7617,33 +7676,34 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
        if (!hdr)
                goto nla_put_failure;
 
-       if (rdev->wowlan) {
+       if (rdev->wiphy.wowlan_config) {
                struct nlattr *nl_wowlan;
 
                nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
                if (!nl_wowlan)
                        goto nla_put_failure;
 
-               if ((rdev->wowlan->any &&
+               if ((rdev->wiphy.wowlan_config->any &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
-                   (rdev->wowlan->disconnect &&
+                   (rdev->wiphy.wowlan_config->disconnect &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
-                   (rdev->wowlan->magic_pkt &&
+                   (rdev->wiphy.wowlan_config->magic_pkt &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
-                   (rdev->wowlan->gtk_rekey_failure &&
+                   (rdev->wiphy.wowlan_config->gtk_rekey_failure &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
-                   (rdev->wowlan->eap_identity_req &&
+                   (rdev->wiphy.wowlan_config->eap_identity_req &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
-                   (rdev->wowlan->four_way_handshake &&
+                   (rdev->wiphy.wowlan_config->four_way_handshake &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
-                   (rdev->wowlan->rfkill_release &&
+                   (rdev->wiphy.wowlan_config->rfkill_release &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
                        goto nla_put_failure;
 
                if (nl80211_send_wowlan_patterns(msg, rdev))
                        goto nla_put_failure;
 
-               if (nl80211_send_wowlan_tcp(msg, rdev->wowlan->tcp))
+               if (nl80211_send_wowlan_tcp(msg,
+                                           rdev->wiphy.wowlan_config->tcp))
                        goto nla_put_failure;
 
                nla_nest_end(msg, nl_wowlan);
@@ -7669,7 +7729,7 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
        u32 data_size, wake_size, tokens_size = 0, wake_mask_size;
        int err, port;
 
-       if (!rdev->wiphy.wowlan.tcp)
+       if (!rdev->wiphy.wowlan->tcp)
                return -EINVAL;
 
        err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP,
@@ -7689,16 +7749,16 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
                return -EINVAL;
 
        data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]);
-       if (data_size > rdev->wiphy.wowlan.tcp->data_payload_max)
+       if (data_size > rdev->wiphy.wowlan->tcp->data_payload_max)
                return -EINVAL;
 
        if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
-                       rdev->wiphy.wowlan.tcp->data_interval_max ||
+                       rdev->wiphy.wowlan->tcp->data_interval_max ||
            nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
                return -EINVAL;
 
        wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
-       if (wake_size > rdev->wiphy.wowlan.tcp->wake_payload_max)
+       if (wake_size > rdev->wiphy.wowlan->tcp->wake_payload_max)
                return -EINVAL;
 
        wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]);
@@ -7713,13 +7773,13 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
 
                if (!tok->len || tokens_size % tok->len)
                        return -EINVAL;
-               if (!rdev->wiphy.wowlan.tcp->tok)
+               if (!rdev->wiphy.wowlan->tcp->tok)
                        return -EINVAL;
-               if (tok->len > rdev->wiphy.wowlan.tcp->tok->max_len)
+               if (tok->len > rdev->wiphy.wowlan->tcp->tok->max_len)
                        return -EINVAL;
-               if (tok->len < rdev->wiphy.wowlan.tcp->tok->min_len)
+               if (tok->len < rdev->wiphy.wowlan->tcp->tok->min_len)
                        return -EINVAL;
-               if (tokens_size > rdev->wiphy.wowlan.tcp->tok->bufsize)
+               if (tokens_size > rdev->wiphy.wowlan->tcp->tok->bufsize)
                        return -EINVAL;
                if (tok->offset + tok->len > data_size)
                        return -EINVAL;
@@ -7727,7 +7787,7 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
 
        if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) {
                seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]);
-               if (!rdev->wiphy.wowlan.tcp->seq)
+               if (!rdev->wiphy.wowlan->tcp->seq)
                        return -EINVAL;
                if (seq->len == 0 || seq->len > 4)
                        return -EINVAL;
@@ -7808,17 +7868,16 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
        struct cfg80211_wowlan new_triggers = {};
        struct cfg80211_wowlan *ntrig;
-       struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
+       const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
        int err, i;
-       bool prev_enabled = rdev->wowlan;
+       bool prev_enabled = rdev->wiphy.wowlan_config;
 
-       if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
-           !rdev->wiphy.wowlan.tcp)
+       if (!wowlan)
                return -EOPNOTSUPP;
 
        if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
                cfg80211_rdev_free_wowlan(rdev);
-               rdev->wowlan = NULL;
+               rdev->wiphy.wowlan_config = NULL;
                goto set_wakeup;
        }
 
@@ -7954,11 +8013,12 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                goto error;
        }
        cfg80211_rdev_free_wowlan(rdev);
-       rdev->wowlan = ntrig;
+       rdev->wiphy.wowlan_config = ntrig;
 
  set_wakeup:
-       if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
-               rdev_set_wakeup(rdev, rdev->wowlan);
+       if (rdev->ops->set_wakeup &&
+           prev_enabled != !!rdev->wiphy.wowlan_config)
+               rdev_set_wakeup(rdev, rdev->wiphy.wowlan_config);
 
        return 0;
  error:
@@ -8143,9 +8203,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
        if (wdev->p2p_started)
                return 0;
 
-       mutex_lock(&rdev->devlist_mtx);
        err = cfg80211_can_add_interface(rdev, wdev->iftype);
-       mutex_unlock(&rdev->devlist_mtx);
        if (err)
                return err;
 
@@ -8154,9 +8212,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
                return err;
 
        wdev->p2p_started = true;
-       mutex_lock(&rdev->devlist_mtx);
        rdev->opencount++;
-       mutex_unlock(&rdev->devlist_mtx);
 
        return 0;
 }
@@ -8172,11 +8228,7 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
        if (!rdev->ops->stop_p2p_device)
                return -EOPNOTSUPP;
 
-       mutex_lock(&rdev->devlist_mtx);
-       mutex_lock(&rdev->sched_scan_mtx);
        cfg80211_stop_p2p_device(rdev, wdev);
-       mutex_unlock(&rdev->sched_scan_mtx);
-       mutex_unlock(&rdev->devlist_mtx);
 
        return 0;
 }
@@ -8319,11 +8371,11 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
                info->user_ptr[0] = rdev;
        } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
                   ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
-               mutex_lock(&cfg80211_mutex);
+               ASSERT_RTNL();
+
                wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
                                                  info->attrs);
                if (IS_ERR(wdev)) {
-                       mutex_unlock(&cfg80211_mutex);
                        if (rtnl)
                                rtnl_unlock();
                        return PTR_ERR(wdev);
@@ -8334,7 +8386,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
 
                if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
                        if (!dev) {
-                               mutex_unlock(&cfg80211_mutex);
                                if (rtnl)
                                        rtnl_unlock();
                                return -EINVAL;
@@ -8348,7 +8399,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
                if (dev) {
                        if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
                            !netif_running(dev)) {
-                               mutex_unlock(&cfg80211_mutex);
                                if (rtnl)
                                        rtnl_unlock();
                                return -ENETDOWN;
@@ -8357,17 +8407,12 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
                        dev_hold(dev);
                } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) {
                        if (!wdev->p2p_started) {
-                               mutex_unlock(&cfg80211_mutex);
                                if (rtnl)
                                        rtnl_unlock();
                                return -ENETDOWN;
                        }
                }
 
-               cfg80211_lock_rdev(rdev);
-
-               mutex_unlock(&cfg80211_mutex);
-
                info->user_ptr[0] = rdev;
        }
 
@@ -8377,8 +8422,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
 static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
                              struct genl_info *info)
 {
-       if (info->user_ptr[0])
-               cfg80211_unlock_rdev(info->user_ptr[0]);
        if (info->user_ptr[1]) {
                if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
                        struct wireless_dev *wdev = info->user_ptr[1];
@@ -8398,9 +8441,11 @@ static struct genl_ops nl80211_ops[] = {
                .cmd = NL80211_CMD_GET_WIPHY,
                .doit = nl80211_get_wiphy,
                .dumpit = nl80211_dump_wiphy,
+               .done = nl80211_dump_wiphy_done,
                .policy = nl80211_policy,
                /* can be retrieved by unprivileged users */
-               .internal_flags = NL80211_FLAG_NEED_WIPHY,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
        },
        {
                .cmd = NL80211_CMD_SET_WIPHY,
@@ -8415,7 +8460,8 @@ static struct genl_ops nl80211_ops[] = {
                .dumpit = nl80211_dump_interface,
                .policy = nl80211_policy,
                /* can be retrieved by unprivileged users */
-               .internal_flags = NL80211_FLAG_NEED_WDEV,
+               .internal_flags = NL80211_FLAG_NEED_WDEV |
+                                 NL80211_FLAG_NEED_RTNL,
        },
        {
                .cmd = NL80211_CMD_SET_INTERFACE,
@@ -8574,6 +8620,7 @@ static struct genl_ops nl80211_ops[] = {
                .cmd = NL80211_CMD_GET_REG,
                .doit = nl80211_get_reg,
                .policy = nl80211_policy,
+               .internal_flags = NL80211_FLAG_NEED_RTNL,
                /* can be retrieved by unprivileged users */
        },
        {
@@ -8581,6 +8628,7 @@ static struct genl_ops nl80211_ops[] = {
                .doit = nl80211_set_reg,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_RTNL,
        },
        {
                .cmd = NL80211_CMD_REQ_SET_REG,
@@ -9014,13 +9062,13 @@ static struct genl_multicast_group nl80211_regulatory_mcgrp = {
 void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
 {
        struct sk_buff *msg;
+       struct nl80211_dump_wiphy_state state = {};
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
-       if (nl80211_send_wiphy(rdev, msg, 0, 0, 0,
-                              false, NULL, NULL, NULL) < 0) {
+       if (nl80211_send_wiphy(rdev, msg, 0, 0, 0, &state) < 0) {
                nlmsg_free(msg);
                return;
        }
@@ -9036,8 +9084,6 @@ static int nl80211_add_scan_req(struct sk_buff *msg,
        struct nlattr *nest;
        int i;
 
-       lockdep_assert_held(&rdev->sched_scan_mtx);
-
        if (WARN_ON(!req))
                return 0;
 
@@ -9344,31 +9390,27 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
                                NL80211_CMD_DISASSOCIATE, gfp);
 }
 
-void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
-                                size_t len)
+void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
+                                 size_t len)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       const struct ieee80211_mgmt *mgmt = (void *)buf;
+       u32 cmd;
 
-       trace_cfg80211_send_unprot_deauth(dev);
-       nl80211_send_mlme_event(rdev, dev, buf, len,
-                               NL80211_CMD_UNPROT_DEAUTHENTICATE, GFP_ATOMIC);
-}
-EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
+       if (WARN_ON(len < 2))
+               return;
 
-void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
-                                  size_t len)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       if (ieee80211_is_deauth(mgmt->frame_control))
+               cmd = NL80211_CMD_UNPROT_DEAUTHENTICATE;
+       else
+               cmd = NL80211_CMD_UNPROT_DISASSOCIATE;
 
-       trace_cfg80211_send_unprot_disassoc(dev);
-       nl80211_send_mlme_event(rdev, dev, buf, len,
-                               NL80211_CMD_UNPROT_DISASSOCIATE, GFP_ATOMIC);
+       trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len);
+       nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC);
 }
-EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
+EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt);
 
 static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
                                      struct net_device *netdev, int cmd,
@@ -9879,7 +9921,6 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
        u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid);
 
        if (!nlportid)
@@ -9900,12 +9941,7 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return true;
-       }
-
+       genlmsg_end(msg, hdr);
        genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
        return true;
 
@@ -10348,10 +10384,7 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
        if (nl80211_send_chandef(msg, chandef))
                goto nla_put_failure;
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10417,7 +10450,6 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_probe_status(dev, addr, cookie, acked);
 
@@ -10439,11 +10471,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
            (acked && nla_put_flag(msg, NL80211_ATTR_ACK)))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10509,7 +10537,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err, size = 200;
+       int size = 200;
 
        trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup);
 
@@ -10595,9 +10623,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
                nla_nest_end(msg, reasons);
        }
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0)
-               goto free_msg;
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10617,7 +10643,6 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
                                         reason_code);
@@ -10640,11 +10665,7 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
             nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10702,7 +10723,6 @@ void cfg80211_ft_event(struct net_device *netdev,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_ft_event(wiphy, netdev, ft_event);
 
@@ -10728,11 +10748,7 @@ void cfg80211_ft_event(struct net_device *netdev,
                nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
                        ft_event->ric_ies);
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, GFP_KERNEL);
index cc35fba..5a24c98 100644 (file)
@@ -81,7 +81,10 @@ static struct regulatory_request core_request_world = {
        .country_ie_env = ENVIRON_ANY,
 };
 
-/* Receipt of information from last regulatory request */
+/*
+ * Receipt of information from last regulatory request,
+ * protected by RTNL (and can be accessed with RCU protection)
+ */
 static struct regulatory_request __rcu *last_request =
        (void __rcu *)&core_request_world;
 
@@ -96,39 +99,25 @@ static struct device_type reg_device_type = {
  * Central wireless core regulatory domains, we only need two,
  * the current one and a world regulatory domain in case we have no
  * information to give us an alpha2.
+ * (protected by RTNL, can be read under RCU)
  */
 const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
 
-/*
- * Protects static reg.c components:
- *     - cfg80211_regdomain (if not used with RCU)
- *     - cfg80211_world_regdom
- *     - last_request (if not used with RCU)
- *     - reg_num_devs_support_basehint
- */
-static DEFINE_MUTEX(reg_mutex);
-
 /*
  * Number of devices that registered to the core
  * that support cellular base station regulatory hints
+ * (protected by RTNL)
  */
 static int reg_num_devs_support_basehint;
 
-static inline void assert_reg_lock(void)
-{
-       lockdep_assert_held(&reg_mutex);
-}
-
 static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
 {
-       return rcu_dereference_protected(cfg80211_regdomain,
-                                        lockdep_is_held(&reg_mutex));
+       return rtnl_dereference(cfg80211_regdomain);
 }
 
 static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
 {
-       return rcu_dereference_protected(wiphy->regd,
-                                        lockdep_is_held(&reg_mutex));
+       return rtnl_dereference(wiphy->regd);
 }
 
 static void rcu_free_regdom(const struct ieee80211_regdomain *r)
@@ -140,8 +129,7 @@ static void rcu_free_regdom(const struct ieee80211_regdomain *r)
 
 static struct regulatory_request *get_last_request(void)
 {
-       return rcu_dereference_check(last_request,
-                                    lockdep_is_held(&reg_mutex));
+       return rcu_dereference_rtnl(last_request);
 }
 
 /* Used to queue up regulatory hints */
@@ -200,6 +188,7 @@ static const struct ieee80211_regdomain world_regdom = {
        }
 };
 
+/* protected by RTNL */
 static const struct ieee80211_regdomain *cfg80211_world_regdom =
        &world_regdom;
 
@@ -215,7 +204,7 @@ static void reset_regdomains(bool full_reset,
        const struct ieee80211_regdomain *r;
        struct regulatory_request *lr;
 
-       assert_reg_lock();
+       ASSERT_RTNL();
 
        r = get_cfg80211_regdom();
 
@@ -377,7 +366,7 @@ static void reg_regdb_search(struct work_struct *work)
        const struct ieee80211_regdomain *curdom, *regdom = NULL;
        int i;
 
-       mutex_lock(&cfg80211_mutex);
+       rtnl_lock();
 
        mutex_lock(&reg_regdb_search_mutex);
        while (!list_empty(&reg_regdb_search_list)) {
@@ -402,7 +391,7 @@ static void reg_regdb_search(struct work_struct *work)
        if (!IS_ERR_OR_NULL(regdom))
                set_regdom(regdom);
 
-       mutex_unlock(&cfg80211_mutex);
+       rtnl_unlock();
 }
 
 static DECLARE_WORK(reg_regdb_work, reg_regdb_search);
@@ -936,13 +925,7 @@ static bool reg_request_cell_base(struct regulatory_request *request)
 
 bool reg_last_request_cell_base(void)
 {
-       bool val;
-
-       mutex_lock(&reg_mutex);
-       val = reg_request_cell_base(get_last_request());
-       mutex_unlock(&reg_mutex);
-
-       return val;
+       return reg_request_cell_base(get_last_request());
 }
 
 #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
@@ -1225,7 +1208,7 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
        struct cfg80211_registered_device *rdev;
        struct wiphy *wiphy;
 
-       assert_cfg80211_lock();
+       ASSERT_RTNL();
 
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                wiphy = &rdev->wiphy;
@@ -1362,7 +1345,7 @@ get_reg_request_treatment(struct wiphy *wiphy,
                                return REG_REQ_OK;
                        return REG_REQ_ALREADY_SET;
                }
-               return 0;
+               return REG_REQ_OK;
        case NL80211_REGDOM_SET_BY_DRIVER:
                if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
                        if (regdom_changes(pending_request->alpha2))
@@ -1444,8 +1427,6 @@ static void reg_set_request_processed(void)
  * what it believes should be the current regulatory domain.
  *
  * Returns one of the different reg request treatment values.
- *
- * Caller must hold &reg_mutex
  */
 static enum reg_request_treatment
 __regulatory_hint(struct wiphy *wiphy,
@@ -1570,21 +1551,19 @@ static void reg_process_pending_hints(void)
 {
        struct regulatory_request *reg_request, *lr;
 
-       mutex_lock(&cfg80211_mutex);
-       mutex_lock(&reg_mutex);
        lr = get_last_request();
 
        /* When last_request->processed becomes true this will be rescheduled */
        if (lr && !lr->processed) {
                REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n");
-               goto out;
+               return;
        }
 
        spin_lock(&reg_requests_lock);
 
        if (list_empty(&reg_requests_list)) {
                spin_unlock(&reg_requests_lock);
-               goto out;
+               return;
        }
 
        reg_request = list_first_entry(&reg_requests_list,
@@ -1595,10 +1574,6 @@ static void reg_process_pending_hints(void)
        spin_unlock(&reg_requests_lock);
 
        reg_process_hint(reg_request, reg_request->initiator);
-
-out:
-       mutex_unlock(&reg_mutex);
-       mutex_unlock(&cfg80211_mutex);
 }
 
 /* Processes beacon hints -- this has nothing to do with country IEs */
@@ -1607,9 +1582,6 @@ static void reg_process_pending_beacon_hints(void)
        struct cfg80211_registered_device *rdev;
        struct reg_beacon *pending_beacon, *tmp;
 
-       mutex_lock(&cfg80211_mutex);
-       mutex_lock(&reg_mutex);
-
        /* This goes through the _pending_ beacon list */
        spin_lock_bh(&reg_pending_beacons_lock);
 
@@ -1626,14 +1598,14 @@ static void reg_process_pending_beacon_hints(void)
        }
 
        spin_unlock_bh(&reg_pending_beacons_lock);
-       mutex_unlock(&reg_mutex);
-       mutex_unlock(&cfg80211_mutex);
 }
 
 static void reg_todo(struct work_struct *work)
 {
+       rtnl_lock();
        reg_process_pending_hints();
        reg_process_pending_beacon_hints();
+       rtnl_unlock();
 }
 
 static void queue_regulatory_request(struct regulatory_request *request)
@@ -1717,29 +1689,23 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 }
 EXPORT_SYMBOL(regulatory_hint);
 
-/*
- * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and
- * therefore cannot iterate over the rdev list here.
- */
 void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
                         const u8 *country_ie, u8 country_ie_len)
 {
        char alpha2[2];
        enum environment_cap env = ENVIRON_ANY;
-       struct regulatory_request *request, *lr;
-
-       mutex_lock(&reg_mutex);
-       lr = get_last_request();
-
-       if (unlikely(!lr))
-               goto out;
+       struct regulatory_request *request = NULL, *lr;
 
        /* IE len must be evenly divisible by 2 */
        if (country_ie_len & 0x01)
-               goto out;
+               return;
 
        if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
-               goto out;
+               return;
+
+       request = kzalloc(sizeof(*request), GFP_KERNEL);
+       if (!request)
+               return;
 
        alpha2[0] = country_ie[0];
        alpha2[1] = country_ie[1];
@@ -1749,19 +1715,21 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
        else if (country_ie[2] == 'O')
                env = ENVIRON_OUTDOOR;
 
+       rcu_read_lock();
+       lr = get_last_request();
+
+       if (unlikely(!lr))
+               goto out;
+
        /*
         * We will run this only upon a successful connection on cfg80211.
         * We leave conflict resolution to the workqueue, where can hold
-        * cfg80211_mutex.
+        * the RTNL.
         */
        if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
            lr->wiphy_idx != WIPHY_IDX_INVALID)
                goto out;
 
-       request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
-       if (!request)
-               goto out;
-
        request->wiphy_idx = get_wiphy_idx(wiphy);
        request->alpha2[0] = alpha2[0];
        request->alpha2[1] = alpha2[1];
@@ -1769,8 +1737,10 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
        request->country_ie_env = env;
 
        queue_regulatory_request(request);
+       request = NULL;
 out:
-       mutex_unlock(&reg_mutex);
+       kfree(request);
+       rcu_read_unlock();
 }
 
 static void restore_alpha2(char *alpha2, bool reset_user)
@@ -1858,8 +1828,7 @@ static void restore_regulatory_settings(bool reset_user)
        LIST_HEAD(tmp_reg_req_list);
        struct cfg80211_registered_device *rdev;
 
-       mutex_lock(&cfg80211_mutex);
-       mutex_lock(&reg_mutex);
+       ASSERT_RTNL();
 
        reset_regdomains(true, &world_regdom);
        restore_alpha2(alpha2, reset_user);
@@ -1914,9 +1883,6 @@ static void restore_regulatory_settings(bool reset_user)
        list_splice_tail_init(&tmp_reg_req_list, &reg_requests_list);
        spin_unlock(&reg_requests_lock);
 
-       mutex_unlock(&reg_mutex);
-       mutex_unlock(&cfg80211_mutex);
-
        REG_DBG_PRINT("Kicking the queue\n");
 
        schedule_work(&reg_work);
@@ -2231,7 +2197,6 @@ int set_regdom(const struct ieee80211_regdomain *rd)
        struct regulatory_request *lr;
        int r;
 
-       mutex_lock(&reg_mutex);
        lr = get_last_request();
 
        /* Note that this doesn't update the wiphys, this is done below */
@@ -2241,14 +2206,12 @@ int set_regdom(const struct ieee80211_regdomain *rd)
                        reg_set_request_processed();
 
                kfree(rd);
-               goto out;
+               return r;
        }
 
        /* This would make this whole thing pointless */
-       if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) {
-               r = -EINVAL;
-               goto out;
-       }
+       if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom()))
+               return -EINVAL;
 
        /* update all wiphys now with the new established regulatory domain */
        update_all_wiphy_regulatory(lr->initiator);
@@ -2259,10 +2222,7 @@ int set_regdom(const struct ieee80211_regdomain *rd)
 
        reg_set_request_processed();
 
- out:
-       mutex_unlock(&reg_mutex);
-
-       return r;
+       return 0;
 }
 
 int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
@@ -2287,23 +2247,17 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
 
 void wiphy_regulatory_register(struct wiphy *wiphy)
 {
-       mutex_lock(&reg_mutex);
-
        if (!reg_dev_ignore_cell_hint(wiphy))
                reg_num_devs_support_basehint++;
 
        wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE);
-
-       mutex_unlock(&reg_mutex);
 }
 
-/* Caller must hold cfg80211_mutex */
 void wiphy_regulatory_deregister(struct wiphy *wiphy)
 {
        struct wiphy *request_wiphy = NULL;
        struct regulatory_request *lr;
 
-       mutex_lock(&reg_mutex);
        lr = get_last_request();
 
        if (!reg_dev_ignore_cell_hint(wiphy))
@@ -2316,12 +2270,10 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy)
                request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
 
        if (!request_wiphy || request_wiphy != wiphy)
-               goto out;
+               return;
 
        lr->wiphy_idx = WIPHY_IDX_INVALID;
        lr->country_ie_env = ENVIRON_ANY;
-out:
-       mutex_unlock(&reg_mutex);
 }
 
 static void reg_timeout_work(struct work_struct *work)
@@ -2385,9 +2337,9 @@ void regulatory_exit(void)
        cancel_delayed_work_sync(&reg_timeout);
 
        /* Lock to suppress warnings */
-       mutex_lock(&reg_mutex);
+       rtnl_lock();
        reset_regdomains(true, NULL);
-       mutex_unlock(&reg_mutex);
+       rtnl_unlock();
 
        dev_set_uevent_suppress(&reg_pdev->dev, true);
 
index fd99ea4..ae8c186 100644 (file)
@@ -169,7 +169,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
        union iwreq_data wrqu;
 #endif
 
-       lockdep_assert_held(&rdev->sched_scan_mtx);
+       ASSERT_RTNL();
 
        request = rdev->scan_req;
 
@@ -230,9 +230,9 @@ void __cfg80211_scan_done(struct work_struct *wk)
        rdev = container_of(wk, struct cfg80211_registered_device,
                            scan_done_wk);
 
-       mutex_lock(&rdev->sched_scan_mtx);
+       rtnl_lock();
        ___cfg80211_scan_done(rdev, false);
-       mutex_unlock(&rdev->sched_scan_mtx);
+       rtnl_unlock();
 }
 
 void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
@@ -241,6 +241,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
        WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
 
        request->aborted = aborted;
+       request->notified = true;
        queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk);
 }
 EXPORT_SYMBOL(cfg80211_scan_done);
@@ -255,7 +256,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
 
        request = rdev->sched_scan_req;
 
-       mutex_lock(&rdev->sched_scan_mtx);
+       rtnl_lock();
 
        /* we don't have sched_scan_req anymore if the scan is stopping */
        if (request) {
@@ -270,7 +271,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
                nl80211_send_sched_scan_results(rdev, request->dev);
        }
 
-       mutex_unlock(&rdev->sched_scan_mtx);
+       rtnl_unlock();
 }
 
 void cfg80211_sched_scan_results(struct wiphy *wiphy)
@@ -289,9 +290,9 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy)
 
        trace_cfg80211_sched_scan_stopped(wiphy);
 
-       mutex_lock(&rdev->sched_scan_mtx);
+       rtnl_lock();
        __cfg80211_stop_sched_scan(rdev, true);
-       mutex_unlock(&rdev->sched_scan_mtx);
+       rtnl_unlock();
 }
 EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
 
@@ -300,7 +301,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
 {
        struct net_device *dev;
 
-       lockdep_assert_held(&rdev->sched_scan_mtx);
+       ASSERT_RTNL();
 
        if (!rdev->sched_scan_req)
                return -ENOENT;
@@ -522,6 +523,7 @@ static int cmp_bss(struct cfg80211_bss *a,
        }
 }
 
+/* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
                                      struct ieee80211_channel *channel,
                                      const u8 *bssid,
@@ -677,6 +679,7 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
        return true;
 }
 
+/* Returned bss is reference counted and must be cleaned up appropriately. */
 static struct cfg80211_internal_bss *
 cfg80211_bss_update(struct cfg80211_registered_device *dev,
                    struct cfg80211_internal_bss *tmp)
@@ -865,6 +868,7 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
        return channel;
 }
 
+/* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss*
 cfg80211_inform_bss(struct wiphy *wiphy,
                    struct ieee80211_channel *channel,
@@ -922,6 +926,7 @@ cfg80211_inform_bss(struct wiphy *wiphy,
 }
 EXPORT_SYMBOL(cfg80211_inform_bss);
 
+/* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss *
 cfg80211_inform_bss_frame(struct wiphy *wiphy,
                          struct ieee80211_channel *channel,
@@ -1040,6 +1045,25 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 EXPORT_SYMBOL(cfg80211_unlink_bss);
 
 #ifdef CONFIG_CFG80211_WEXT
+static struct cfg80211_registered_device *
+cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
+{
+       struct cfg80211_registered_device *rdev;
+       struct net_device *dev;
+
+       ASSERT_RTNL();
+
+       dev = dev_get_by_index(net, ifindex);
+       if (!dev)
+               return ERR_PTR(-ENODEV);
+       if (dev->ieee80211_ptr)
+               rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
+       else
+               rdev = ERR_PTR(-ENODEV);
+       dev_put(dev);
+       return rdev;
+}
+
 int cfg80211_wext_siwscan(struct net_device *dev,
                          struct iw_request_info *info,
                          union iwreq_data *wrqu, char *extra)
@@ -1062,7 +1086,6 @@ int cfg80211_wext_siwscan(struct net_device *dev,
        if (IS_ERR(rdev))
                return PTR_ERR(rdev);
 
-       mutex_lock(&rdev->sched_scan_mtx);
        if (rdev->scan_req) {
                err = -EBUSY;
                goto out;
@@ -1169,9 +1192,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
                dev_hold(dev);
        }
  out:
-       mutex_unlock(&rdev->sched_scan_mtx);
        kfree(creq);
-       cfg80211_unlock_rdev(rdev);
        return err;
 }
 EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
@@ -1470,10 +1491,8 @@ int cfg80211_wext_giwscan(struct net_device *dev,
        if (IS_ERR(rdev))
                return PTR_ERR(rdev);
 
-       if (rdev->scan_req) {
-               res = -EAGAIN;
-               goto out;
-       }
+       if (rdev->scan_req)
+               return -EAGAIN;
 
        res = ieee80211_scan_results(rdev, info, extra, data->length);
        data->length = 0;
@@ -1482,8 +1501,6 @@ int cfg80211_wext_giwscan(struct net_device *dev,
                res = 0;
        }
 
- out:
-       cfg80211_unlock_rdev(rdev);
        return res;
 }
 EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan);
index 3ed35c3..1d3cfb1 100644 (file)
@@ -1,5 +1,7 @@
 /*
- * SME code for cfg80211's connect emulation.
+ * SME code for cfg80211
+ * both driver SME event handling and the SME implementation
+ * (for nl80211's connect() and wext)
  *
  * Copyright 2009      Johannes Berg <johannes@sipsolutions.net>
  * Copyright (C) 2009   Intel Corporation. All rights reserved.
 #include "reg.h"
 #include "rdev-ops.h"
 
+/*
+ * Software SME in cfg80211, using auth/assoc/deauth calls to the
+ * driver. This is is for implementing nl80211's connect/disconnect
+ * and wireless extensions (if configured.)
+ */
+
 struct cfg80211_conn {
        struct cfg80211_connect_params params;
        /* these are sub-states of the _CONNECTING sme_state */
        enum {
-               CFG80211_CONN_IDLE,
                CFG80211_CONN_SCANNING,
                CFG80211_CONN_SCAN_AGAIN,
                CFG80211_CONN_AUTHENTICATE_NEXT,
                CFG80211_CONN_AUTHENTICATING,
                CFG80211_CONN_ASSOCIATE_NEXT,
                CFG80211_CONN_ASSOCIATING,
-               CFG80211_CONN_DEAUTH_ASSOC_FAIL,
+               CFG80211_CONN_DEAUTH,
+               CFG80211_CONN_CONNECTED,
        } state;
        u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
        u8 *ie;
@@ -37,45 +45,16 @@ struct cfg80211_conn {
        bool auto_auth, prev_bssid_valid;
 };
 
-static bool cfg80211_is_all_idle(void)
-{
-       struct cfg80211_registered_device *rdev;
-       struct wireless_dev *wdev;
-       bool is_all_idle = true;
-
-       mutex_lock(&cfg80211_mutex);
-
-       /*
-        * All devices must be idle as otherwise if you are actively
-        * scanning some new beacon hints could be learned and would
-        * count as new regulatory hints.
-        */
-       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
-               cfg80211_lock_rdev(rdev);
-               list_for_each_entry(wdev, &rdev->wdev_list, list) {
-                       wdev_lock(wdev);
-                       if (wdev->sme_state != CFG80211_SME_IDLE)
-                               is_all_idle = false;
-                       wdev_unlock(wdev);
-               }
-               cfg80211_unlock_rdev(rdev);
-       }
-
-       mutex_unlock(&cfg80211_mutex);
-
-       return is_all_idle;
-}
-
-static void disconnect_work(struct work_struct *work)
+static void cfg80211_sme_free(struct wireless_dev *wdev)
 {
-       if (!cfg80211_is_all_idle())
+       if (!wdev->conn)
                return;
 
-       regulatory_hint_disconnect();
+       kfree(wdev->conn->ie);
+       kfree(wdev->conn);
+       wdev->conn = NULL;
 }
 
-static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
-
 static int cfg80211_conn_scan(struct wireless_dev *wdev)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
@@ -85,7 +64,6 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
        ASSERT_RTNL();
        ASSERT_RDEV_LOCK(rdev);
        ASSERT_WDEV_LOCK(wdev);
-       lockdep_assert_held(&rdev->sched_scan_mtx);
 
        if (rdev->scan_req)
                return -EBUSY;
@@ -171,18 +149,21 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
        params = &wdev->conn->params;
 
        switch (wdev->conn->state) {
+       case CFG80211_CONN_SCANNING:
+               /* didn't find it during scan ... */
+               return -ENOENT;
        case CFG80211_CONN_SCAN_AGAIN:
                return cfg80211_conn_scan(wdev);
        case CFG80211_CONN_AUTHENTICATE_NEXT:
                BUG_ON(!rdev->ops->auth);
                wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
-               return __cfg80211_mlme_auth(rdev, wdev->netdev,
-                                           params->channel, params->auth_type,
-                                           params->bssid,
-                                           params->ssid, params->ssid_len,
-                                           NULL, 0,
-                                           params->key, params->key_len,
-                                           params->key_idx, NULL, 0);
+               return cfg80211_mlme_auth(rdev, wdev->netdev,
+                                         params->channel, params->auth_type,
+                                         params->bssid,
+                                         params->ssid, params->ssid_len,
+                                         NULL, 0,
+                                         params->key, params->key_len,
+                                         params->key_idx, NULL, 0);
        case CFG80211_CONN_ASSOCIATE_NEXT:
                BUG_ON(!rdev->ops->assoc);
                wdev->conn->state = CFG80211_CONN_ASSOCIATING;
@@ -198,21 +179,20 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                req.vht_capa = params->vht_capa;
                req.vht_capa_mask = params->vht_capa_mask;
 
-               err = __cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel,
-                                           params->bssid, params->ssid,
-                                           params->ssid_len, &req);
+               err = cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel,
+                                         params->bssid, params->ssid,
+                                         params->ssid_len, &req);
                if (err)
-                       __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
-                                              NULL, 0,
-                                              WLAN_REASON_DEAUTH_LEAVING,
-                                              false);
+                       cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
+                                            NULL, 0,
+                                            WLAN_REASON_DEAUTH_LEAVING,
+                                            false);
                return err;
-       case CFG80211_CONN_DEAUTH_ASSOC_FAIL:
-               __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
-                                      NULL, 0,
-                                      WLAN_REASON_DEAUTH_LEAVING, false);
-               /* return an error so that we call __cfg80211_connect_result() */
-               return -EINVAL;
+       case CFG80211_CONN_DEAUTH:
+               cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
+                                    NULL, 0,
+                                    WLAN_REASON_DEAUTH_LEAVING, false);
+               return 0;
        default:
                return 0;
        }
@@ -226,9 +206,6 @@ void cfg80211_conn_work(struct work_struct *work)
        u8 bssid_buf[ETH_ALEN], *bssid = NULL;
 
        rtnl_lock();
-       cfg80211_lock_rdev(rdev);
-       mutex_lock(&rdev->devlist_mtx);
-       mutex_lock(&rdev->sched_scan_mtx);
 
        list_for_each_entry(wdev, &rdev->wdev_list, list) {
                if (!wdev->netdev)
@@ -239,7 +216,8 @@ void cfg80211_conn_work(struct work_struct *work)
                        wdev_unlock(wdev);
                        continue;
                }
-               if (wdev->sme_state != CFG80211_SME_CONNECTING || !wdev->conn) {
+               if (!wdev->conn ||
+                   wdev->conn->state == CFG80211_CONN_CONNECTED) {
                        wdev_unlock(wdev);
                        continue;
                }
@@ -247,21 +225,21 @@ void cfg80211_conn_work(struct work_struct *work)
                        memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
                        bssid = bssid_buf;
                }
-               if (cfg80211_conn_do_work(wdev))
+               if (cfg80211_conn_do_work(wdev)) {
                        __cfg80211_connect_result(
                                        wdev->netdev, bssid,
                                        NULL, 0, NULL, 0,
                                        WLAN_STATUS_UNSPECIFIED_FAILURE,
                                        false, NULL);
+                       cfg80211_sme_free(wdev);
+               }
                wdev_unlock(wdev);
        }
 
-       mutex_unlock(&rdev->sched_scan_mtx);
-       mutex_unlock(&rdev->devlist_mtx);
-       cfg80211_unlock_rdev(rdev);
        rtnl_unlock();
 }
 
+/* Returned bss is reference counted and must be cleaned up appropriately. */
 static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
@@ -299,9 +277,6 @@ static void __cfg80211_sme_scan_done(struct net_device *dev)
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTING)
-               return;
-
        if (!wdev->conn)
                return;
 
@@ -310,20 +285,10 @@ static void __cfg80211_sme_scan_done(struct net_device *dev)
                return;
 
        bss = cfg80211_get_conn_bss(wdev);
-       if (bss) {
+       if (bss)
                cfg80211_put_bss(&rdev->wiphy, bss);
-       } else {
-               /* not found */
-               if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
-                       schedule_work(&rdev->conn_work);
-               else
-                       __cfg80211_connect_result(
-                                       wdev->netdev,
-                                       wdev->conn->params.bssid,
-                                       NULL, 0, NULL, 0,
-                                       WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                       false, NULL);
-       }
+       else
+               schedule_work(&rdev->conn_work);
 }
 
 void cfg80211_sme_scan_done(struct net_device *dev)
@@ -335,10 +300,8 @@ void cfg80211_sme_scan_done(struct net_device *dev)
        wdev_unlock(wdev);
 }
 
-void cfg80211_sme_rx_auth(struct net_device *dev,
-                         const u8 *buf, size_t len)
+void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
 {
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
@@ -346,11 +309,7 @@ void cfg80211_sme_rx_auth(struct net_device *dev,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       /* should only RX auth frames when connecting */
-       if (wdev->sme_state != CFG80211_SME_CONNECTING)
-               return;
-
-       if (WARN_ON(!wdev->conn))
+       if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED)
                return;
 
        if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
@@ -379,46 +338,227 @@ void cfg80211_sme_rx_auth(struct net_device *dev,
                wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
                schedule_work(&rdev->conn_work);
        } else if (status_code != WLAN_STATUS_SUCCESS) {
-               __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
+               __cfg80211_connect_result(wdev->netdev, mgmt->bssid,
+                                         NULL, 0, NULL, 0,
                                          status_code, false, NULL);
-       } else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
-                wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
+       } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
                wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
                schedule_work(&rdev->conn_work);
        }
 }
 
-bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev)
+bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status)
 {
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 
-       if (WARN_ON(!wdev->conn))
+       if (!wdev->conn)
                return false;
 
-       if (!wdev->conn->prev_bssid_valid)
+       if (status == WLAN_STATUS_SUCCESS) {
+               wdev->conn->state = CFG80211_CONN_CONNECTED;
                return false;
+       }
 
-       /*
-        * Some stupid APs don't accept reassoc, so we
-        * need to fall back to trying regular assoc.
-        */
-       wdev->conn->prev_bssid_valid = false;
-       wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
+       if (wdev->conn->prev_bssid_valid) {
+               /*
+                * Some stupid APs don't accept reassoc, so we
+                * need to fall back to trying regular assoc;
+                * return true so no event is sent to userspace.
+                */
+               wdev->conn->prev_bssid_valid = false;
+               wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
+               schedule_work(&rdev->conn_work);
+               return true;
+       }
+
+       wdev->conn->state = CFG80211_CONN_DEAUTH;
        schedule_work(&rdev->conn_work);
+       return false;
+}
 
-       return true;
+void cfg80211_sme_deauth(struct wireless_dev *wdev)
+{
+       cfg80211_sme_free(wdev);
 }
 
-void cfg80211_sme_failed_assoc(struct wireless_dev *wdev)
+void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
 {
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       cfg80211_sme_free(wdev);
+}
 
-       wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL;
+void cfg80211_sme_disassoc(struct wireless_dev *wdev)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+
+       if (!wdev->conn)
+               return;
+
+       wdev->conn->state = CFG80211_CONN_DEAUTH;
        schedule_work(&rdev->conn_work);
 }
 
+void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
+{
+       cfg80211_sme_disassoc(wdev);
+}
+
+static int cfg80211_sme_connect(struct wireless_dev *wdev,
+                               struct cfg80211_connect_params *connect,
+                               const u8 *prev_bssid)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_bss *bss;
+       int err;
+
+       if (!rdev->ops->auth || !rdev->ops->assoc)
+               return -EOPNOTSUPP;
+
+       if (wdev->current_bss)
+               return -EALREADY;
+
+       if (WARN_ON(wdev->conn))
+               return -EINPROGRESS;
+
+       wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
+       if (!wdev->conn)
+               return -ENOMEM;
+
+       /*
+        * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
+        */
+       memcpy(&wdev->conn->params, connect, sizeof(*connect));
+       if (connect->bssid) {
+               wdev->conn->params.bssid = wdev->conn->bssid;
+               memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
+       }
+
+       if (connect->ie) {
+               wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
+                                       GFP_KERNEL);
+               wdev->conn->params.ie = wdev->conn->ie;
+               if (!wdev->conn->ie) {
+                       kfree(wdev->conn);
+                       wdev->conn = NULL;
+                       return -ENOMEM;
+               }
+       }
+
+       if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
+               wdev->conn->auto_auth = true;
+               /* start with open system ... should mostly work */
+               wdev->conn->params.auth_type =
+                       NL80211_AUTHTYPE_OPEN_SYSTEM;
+       } else {
+               wdev->conn->auto_auth = false;
+       }
+
+       wdev->conn->params.ssid = wdev->ssid;
+       wdev->conn->params.ssid_len = connect->ssid_len;
+
+       /* see if we have the bss already */
+       bss = cfg80211_get_conn_bss(wdev);
+
+       if (prev_bssid) {
+               memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
+               wdev->conn->prev_bssid_valid = true;
+       }
+
+       /* we're good if we have a matching bss struct */
+       if (bss) {
+               wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
+               err = cfg80211_conn_do_work(wdev);
+               cfg80211_put_bss(wdev->wiphy, bss);
+       } else {
+               /* otherwise we'll need to scan for the AP first */
+               err = cfg80211_conn_scan(wdev);
+
+               /*
+                * If we can't scan right now, then we need to scan again
+                * after the current scan finished, since the parameters
+                * changed (unless we find a good AP anyway).
+                */
+               if (err == -EBUSY) {
+                       err = 0;
+                       wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
+               }
+       }
+
+       if (err)
+               cfg80211_sme_free(wdev);
+
+       return err;
+}
+
+static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       int err;
+
+       if (!wdev->conn)
+               return 0;
+
+       if (!rdev->ops->deauth)
+               return -EOPNOTSUPP;
+
+       if (wdev->conn->state == CFG80211_CONN_SCANNING ||
+           wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) {
+               err = 0;
+               goto out;
+       }
+
+       /* wdev->conn->params.bssid must be set if > SCANNING */
+       err = cfg80211_mlme_deauth(rdev, wdev->netdev,
+                                  wdev->conn->params.bssid,
+                                  NULL, 0, reason, false);
+ out:
+       cfg80211_sme_free(wdev);
+       return err;
+}
+
+/*
+ * code shared for in-device and software SME
+ */
+
+static bool cfg80211_is_all_idle(void)
+{
+       struct cfg80211_registered_device *rdev;
+       struct wireless_dev *wdev;
+       bool is_all_idle = true;
+
+       /*
+        * All devices must be idle as otherwise if you are actively
+        * scanning some new beacon hints could be learned and would
+        * count as new regulatory hints.
+        */
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               list_for_each_entry(wdev, &rdev->wdev_list, list) {
+                       wdev_lock(wdev);
+                       if (wdev->conn || wdev->current_bss)
+                               is_all_idle = false;
+                       wdev_unlock(wdev);
+               }
+       }
+
+       return is_all_idle;
+}
+
+static void disconnect_work(struct work_struct *work)
+{
+       rtnl_lock();
+       if (cfg80211_is_all_idle())
+               regulatory_hint_disconnect();
+       rtnl_unlock();
+}
+
+static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
+
+
+/*
+ * API calls for drivers implementing connect/disconnect and
+ * SME event handling
+ */
+
+/* This method must consume bss one way or another */
 void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                               const u8 *req_ie, size_t req_ie_len,
                               const u8 *resp_ie, size_t resp_ie_len,
@@ -434,11 +574,10 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        ASSERT_WDEV_LOCK(wdev);
 
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
-                   wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
-               return;
-
-       if (wdev->sme_state != CFG80211_SME_CONNECTING)
+                   wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) {
+               cfg80211_put_bss(wdev->wiphy, bss);
                return;
+       }
 
        nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
                                    bssid, req_ie, req_ie_len,
@@ -476,38 +615,30 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                wdev->current_bss = NULL;
        }
 
-       if (wdev->conn)
-               wdev->conn->state = CFG80211_CONN_IDLE;
-
        if (status != WLAN_STATUS_SUCCESS) {
-               wdev->sme_state = CFG80211_SME_IDLE;
-               if (wdev->conn)
-                       kfree(wdev->conn->ie);
-               kfree(wdev->conn);
-               wdev->conn = NULL;
                kfree(wdev->connect_keys);
                wdev->connect_keys = NULL;
                wdev->ssid_len = 0;
-               cfg80211_put_bss(wdev->wiphy, bss);
+               if (bss) {
+                       cfg80211_unhold_bss(bss_from_pub(bss));
+                       cfg80211_put_bss(wdev->wiphy, bss);
+               }
                return;
        }
 
-       if (!bss)
-               bss = cfg80211_get_bss(wdev->wiphy,
-                                      wdev->conn ? wdev->conn->params.channel :
-                                      NULL,
-                                      bssid,
+       if (!bss) {
+               WARN_ON_ONCE(!wiphy_to_dev(wdev->wiphy)->ops->connect);
+               bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
                                       wdev->ssid, wdev->ssid_len,
                                       WLAN_CAPABILITY_ESS,
                                       WLAN_CAPABILITY_ESS);
+               if (WARN_ON(!bss))
+                       return;
+               cfg80211_hold_bss(bss_from_pub(bss));
+       }
 
-       if (WARN_ON(!bss))
-               return;
-
-       cfg80211_hold_bss(bss_from_pub(bss));
        wdev->current_bss = bss_from_pub(bss);
 
-       wdev->sme_state = CFG80211_SME_CONNECTED;
        cfg80211_upload_connect_keys(wdev);
 
        rcu_read_lock();
@@ -543,8 +674,6 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        struct cfg80211_event *ev;
        unsigned long flags;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
-
        ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
        if (!ev)
                return;
@@ -571,6 +700,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
 }
 EXPORT_SYMBOL(cfg80211_connect_result);
 
+/* Consumes bss object one way or another */
 void __cfg80211_roamed(struct wireless_dev *wdev,
                       struct cfg80211_bss *bss,
                       const u8 *req_ie, size_t req_ie_len,
@@ -585,14 +715,9 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
                    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
                goto out;
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
+       if (WARN_ON(!wdev->current_bss))
                goto out;
 
-       /* internal error -- how did we get to CONNECTED w/o BSS? */
-       if (WARN_ON(!wdev->current_bss)) {
-               goto out;
-       }
-
        cfg80211_unhold_bss(wdev->current_bss);
        cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
        wdev->current_bss = NULL;
@@ -641,8 +766,6 @@ void cfg80211_roamed(struct net_device *dev,
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_bss *bss;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
-
        bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
                               wdev->ssid_len, WLAN_CAPABILITY_ESS,
                               WLAN_CAPABILITY_ESS);
@@ -654,6 +777,7 @@ void cfg80211_roamed(struct net_device *dev,
 }
 EXPORT_SYMBOL(cfg80211_roamed);
 
+/* Consumes bss object one way or another */
 void cfg80211_roamed_bss(struct net_device *dev,
                         struct cfg80211_bss *bss, const u8 *req_ie,
                         size_t req_ie_len, const u8 *resp_ie,
@@ -664,8 +788,6 @@ void cfg80211_roamed_bss(struct net_device *dev,
        struct cfg80211_event *ev;
        unsigned long flags;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
-
        if (WARN_ON(!bss))
                return;
 
@@ -707,25 +829,14 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
                    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
                return;
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
-               return;
-
        if (wdev->current_bss) {
                cfg80211_unhold_bss(wdev->current_bss);
                cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
        }
 
        wdev->current_bss = NULL;
-       wdev->sme_state = CFG80211_SME_IDLE;
        wdev->ssid_len = 0;
 
-       if (wdev->conn) {
-               kfree(wdev->conn->ie);
-               wdev->conn->ie = NULL;
-               kfree(wdev->conn);
-               wdev->conn = NULL;
-       }
-
        nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
 
        /*
@@ -754,8 +865,6 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason,
        struct cfg80211_event *ev;
        unsigned long flags;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
-
        ev = kzalloc(sizeof(*ev) + ie_len, gfp);
        if (!ev)
                return;
@@ -773,21 +882,20 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason,
 }
 EXPORT_SYMBOL(cfg80211_disconnected);
 
-int __cfg80211_connect(struct cfg80211_registered_device *rdev,
-                      struct net_device *dev,
-                      struct cfg80211_connect_params *connect,
-                      struct cfg80211_cached_keys *connkeys,
-                      const u8 *prev_bssid)
+/*
+ * API calls for nl80211/wext compatibility code
+ */
+int cfg80211_connect(struct cfg80211_registered_device *rdev,
+                    struct net_device *dev,
+                    struct cfg80211_connect_params *connect,
+                    struct cfg80211_cached_keys *connkeys,
+                    const u8 *prev_bssid)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_bss *bss = NULL;
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_IDLE)
-               return -EALREADY;
-
        if (WARN_ON(wdev->connect_keys)) {
                kfree(wdev->connect_keys);
                wdev->connect_keys = NULL;
@@ -823,219 +931,43 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
                }
        }
 
-       if (!rdev->ops->connect) {
-               if (!rdev->ops->auth || !rdev->ops->assoc)
-                       return -EOPNOTSUPP;
-
-               if (WARN_ON(wdev->conn))
-                       return -EINPROGRESS;
-
-               wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
-               if (!wdev->conn)
-                       return -ENOMEM;
-
-               /*
-                * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
-                */
-               memcpy(&wdev->conn->params, connect, sizeof(*connect));
-               if (connect->bssid) {
-                       wdev->conn->params.bssid = wdev->conn->bssid;
-                       memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
-               }
-
-               if (connect->ie) {
-                       wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
-                                               GFP_KERNEL);
-                       wdev->conn->params.ie = wdev->conn->ie;
-                       if (!wdev->conn->ie) {
-                               kfree(wdev->conn);
-                               wdev->conn = NULL;
-                               return -ENOMEM;
-                       }
-               }
-
-               if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
-                       wdev->conn->auto_auth = true;
-                       /* start with open system ... should mostly work */
-                       wdev->conn->params.auth_type =
-                               NL80211_AUTHTYPE_OPEN_SYSTEM;
-               } else {
-                       wdev->conn->auto_auth = false;
-               }
-
-               memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
-               wdev->ssid_len = connect->ssid_len;
-               wdev->conn->params.ssid = wdev->ssid;
-               wdev->conn->params.ssid_len = connect->ssid_len;
-
-               /* see if we have the bss already */
-               bss = cfg80211_get_conn_bss(wdev);
-
-               wdev->sme_state = CFG80211_SME_CONNECTING;
-               wdev->connect_keys = connkeys;
-
-               if (prev_bssid) {
-                       memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
-                       wdev->conn->prev_bssid_valid = true;
-               }
-
-               /* we're good if we have a matching bss struct */
-               if (bss) {
-                       wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
-                       err = cfg80211_conn_do_work(wdev);
-                       cfg80211_put_bss(wdev->wiphy, bss);
-               } else {
-                       /* otherwise we'll need to scan for the AP first */
-                       err = cfg80211_conn_scan(wdev);
-                       /*
-                        * If we can't scan right now, then we need to scan again
-                        * after the current scan finished, since the parameters
-                        * changed (unless we find a good AP anyway).
-                        */
-                       if (err == -EBUSY) {
-                               err = 0;
-                               wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
-                       }
-               }
-               if (err) {
-                       kfree(wdev->conn->ie);
-                       kfree(wdev->conn);
-                       wdev->conn = NULL;
-                       wdev->sme_state = CFG80211_SME_IDLE;
-                       wdev->connect_keys = NULL;
-                       wdev->ssid_len = 0;
-               }
+       wdev->connect_keys = connkeys;
+       memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
+       wdev->ssid_len = connect->ssid_len;
 
-               return err;
-       } else {
-               wdev->sme_state = CFG80211_SME_CONNECTING;
-               wdev->connect_keys = connkeys;
+       if (!rdev->ops->connect)
+               err = cfg80211_sme_connect(wdev, connect, prev_bssid);
+       else
                err = rdev_connect(rdev, dev, connect);
-               if (err) {
-                       wdev->connect_keys = NULL;
-                       wdev->sme_state = CFG80211_SME_IDLE;
-                       return err;
-               }
-
-               memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
-               wdev->ssid_len = connect->ssid_len;
 
-               return 0;
+       if (err) {
+               wdev->connect_keys = NULL;
+               wdev->ssid_len = 0;
+               return err;
        }
-}
-
-int cfg80211_connect(struct cfg80211_registered_device *rdev,
-                    struct net_device *dev,
-                    struct cfg80211_connect_params *connect,
-                    struct cfg80211_cached_keys *connkeys)
-{
-       int err;
-
-       mutex_lock(&rdev->devlist_mtx);
-       /* might request scan - scan_mtx -> wdev_mtx dependency */
-       mutex_lock(&rdev->sched_scan_mtx);
-       wdev_lock(dev->ieee80211_ptr);
-       err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL);
-       wdev_unlock(dev->ieee80211_ptr);
-       mutex_unlock(&rdev->sched_scan_mtx);
-       mutex_unlock(&rdev->devlist_mtx);
 
-       return err;
+       return 0;
 }
 
-int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
-                         struct net_device *dev, u16 reason, bool wextev)
+int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
+                       struct net_device *dev, u16 reason, bool wextev)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state == CFG80211_SME_IDLE)
-               return -EINVAL;
-
        kfree(wdev->connect_keys);
        wdev->connect_keys = NULL;
 
-       if (!rdev->ops->disconnect) {
-               if (!rdev->ops->deauth)
-                       return -EOPNOTSUPP;
-
-               /* was it connected by userspace SME? */
-               if (!wdev->conn) {
-                       cfg80211_mlme_down(rdev, dev);
-                       goto disconnect;
-               }
-
-               if (wdev->sme_state == CFG80211_SME_CONNECTING &&
-                   (wdev->conn->state == CFG80211_CONN_SCANNING ||
-                    wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) {
-                       wdev->sme_state = CFG80211_SME_IDLE;
-                       kfree(wdev->conn->ie);
-                       kfree(wdev->conn);
-                       wdev->conn = NULL;
-                       wdev->ssid_len = 0;
-                       return 0;
-               }
-
-               /* wdev->conn->params.bssid must be set if > SCANNING */
-               err = __cfg80211_mlme_deauth(rdev, dev,
-                                            wdev->conn->params.bssid,
-                                            NULL, 0, reason, false);
-               if (err)
-                       return err;
+       if (wdev->conn) {
+               err = cfg80211_sme_disconnect(wdev, reason);
+       } else if (!rdev->ops->disconnect) {
+               cfg80211_mlme_down(rdev, dev);
+               err = 0;
        } else {
                err = rdev_disconnect(rdev, dev, reason);
-               if (err)
-                       return err;
        }
 
- disconnect:
-       if (wdev->sme_state == CFG80211_SME_CONNECTED)
-               __cfg80211_disconnected(dev, NULL, 0, 0, false);
-       else if (wdev->sme_state == CFG80211_SME_CONNECTING)
-               __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         wextev, NULL);
-
-       return 0;
-}
-
-int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
-                       struct net_device *dev,
-                       u16 reason, bool wextev)
-{
-       int err;
-
-       wdev_lock(dev->ieee80211_ptr);
-       err = __cfg80211_disconnect(rdev, dev, reason, wextev);
-       wdev_unlock(dev->ieee80211_ptr);
-
        return err;
 }
-
-void cfg80211_sme_disassoc(struct net_device *dev,
-                          struct cfg80211_internal_bss *bss)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
-       u8 bssid[ETH_ALEN];
-
-       ASSERT_WDEV_LOCK(wdev);
-
-       if (!wdev->conn)
-               return;
-
-       if (wdev->conn->state == CFG80211_CONN_IDLE)
-               return;
-
-       /*
-        * Ok, so the association was made by this SME -- we don't
-        * want it any more so deauthenticate too.
-        */
-
-       memcpy(bssid, bss->pub.bssid, ETH_ALEN);
-
-       __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
-                              WLAN_REASON_DEAUTH_LEAVING, false);
-}
index 8f28b9f..a23253e 100644 (file)
@@ -83,6 +83,7 @@ static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
        return 0;
 }
 
+#ifdef CONFIG_PM
 static void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
 {
        struct wireless_dev *wdev;
@@ -100,10 +101,10 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
 
        rtnl_lock();
        if (rdev->wiphy.registered) {
-               if (!rdev->wowlan)
+               if (!rdev->wiphy.wowlan_config)
                        cfg80211_leave_all(rdev);
                if (rdev->ops->suspend)
-                       ret = rdev_suspend(rdev, rdev->wowlan);
+                       ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config);
                if (ret == 1) {
                        /* Driver refuse to configure wowlan */
                        cfg80211_leave_all(rdev);
@@ -132,6 +133,7 @@ static int wiphy_resume(struct device *dev)
 
        return ret;
 }
+#endif
 
 static const void *wiphy_namespace(struct device *d)
 {
@@ -146,8 +148,10 @@ struct class ieee80211_class = {
        .dev_release = wiphy_dev_release,
        .dev_attrs = ieee80211_dev_attrs,
        .dev_uevent = wiphy_uevent,
+#ifdef CONFIG_PM
        .suspend = wiphy_suspend,
        .resume = wiphy_resume,
+#endif
        .ns_type = &net_ns_type_operations,
        .namespace = wiphy_namespace,
 };
index 5755bc1..e1534ba 100644 (file)
@@ -1911,24 +1911,46 @@ TRACE_EVENT(cfg80211_send_rx_assoc,
                  NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG)
 );
 
-DEFINE_EVENT(netdev_evt_only, __cfg80211_send_deauth,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+DECLARE_EVENT_CLASS(netdev_frame_event,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len),
+       TP_STRUCT__entry(
+               NETDEV_ENTRY
+               __dynamic_array(u8, frame, len)
+       ),
+       TP_fast_assign(
+               NETDEV_ASSIGN;
+               memcpy(__get_dynamic_array(frame), buf, len);
+       ),
+       TP_printk(NETDEV_PR_FMT ", ftype:0x%.2x",
+                 NETDEV_PR_ARG,
+                 le16_to_cpup((__le16 *)__get_dynamic_array(frame)))
 );
 
-DEFINE_EVENT(netdev_evt_only, __cfg80211_send_disassoc,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+DEFINE_EVENT(netdev_frame_event, cfg80211_rx_unprot_mlme_mgmt,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len)
 );
 
-DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_deauth,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+DEFINE_EVENT(netdev_frame_event, cfg80211_rx_mlme_mgmt,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len)
 );
 
-DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_disassoc,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+TRACE_EVENT(cfg80211_tx_mlme_mgmt,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len),
+       TP_STRUCT__entry(
+               NETDEV_ENTRY
+               __dynamic_array(u8, frame, len)
+       ),
+       TP_fast_assign(
+               NETDEV_ASSIGN;
+               memcpy(__get_dynamic_array(frame), buf, len);
+       ),
+       TP_printk(NETDEV_PR_FMT ", ftype:0x%.2x",
+                 NETDEV_PR_ARG,
+                 le16_to_cpup((__le16 *)__get_dynamic_array(frame)))
 );
 
 DECLARE_EVENT_CLASS(netdev_mac_evt,
index f5ad4d9..74458b7 100644 (file)
@@ -33,6 +33,29 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
 }
 EXPORT_SYMBOL(ieee80211_get_response_rate);
 
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband)
+{
+       struct ieee80211_rate *bitrates;
+       u32 mandatory_rates = 0;
+       enum ieee80211_rate_flags mandatory_flag;
+       int i;
+
+       if (WARN_ON(!sband))
+               return 1;
+
+       if (sband->band == IEEE80211_BAND_2GHZ)
+               mandatory_flag = IEEE80211_RATE_MANDATORY_B;
+       else
+               mandatory_flag = IEEE80211_RATE_MANDATORY_A;
+
+       bitrates = sband->bitrates;
+       for (i = 0; i < sband->n_bitrates; i++)
+               if (bitrates[i].flags & mandatory_flag)
+                       mandatory_rates |= BIT(i);
+       return mandatory_rates;
+}
+EXPORT_SYMBOL(ieee80211_mandatory_rates);
+
 int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band)
 {
        /* see 802.11 17.3.8.3.2 and Annex J
@@ -785,12 +808,8 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev)
        ASSERT_RTNL();
        ASSERT_RDEV_LOCK(rdev);
 
-       mutex_lock(&rdev->devlist_mtx);
-
        list_for_each_entry(wdev, &rdev->wdev_list, list)
                cfg80211_process_wdev_events(wdev);
-
-       mutex_unlock(&rdev->devlist_mtx);
 }
 
 int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
@@ -822,10 +841,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
                return -EBUSY;
 
        if (ntype != otype && netif_running(dev)) {
-               mutex_lock(&rdev->devlist_mtx);
                err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr,
                                                    ntype);
-               mutex_unlock(&rdev->devlist_mtx);
                if (err)
                        return err;
 
@@ -841,8 +858,10 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
                        break;
                case NL80211_IFTYPE_STATION:
                case NL80211_IFTYPE_P2P_CLIENT:
+                       wdev_lock(dev->ieee80211_ptr);
                        cfg80211_disconnect(rdev, dev,
                                            WLAN_REASON_DEAUTH_LEAVING, true);
+                       wdev_unlock(dev->ieee80211_ptr);
                        break;
                case NL80211_IFTYPE_MESH_POINT:
                        /* mesh should be handled? */
@@ -1169,6 +1188,9 @@ bool ieee80211_operating_class_to_band(u8 operating_class,
        case 84:
                *band = IEEE80211_BAND_2GHZ;
                return true;
+       case 180:
+               *band = IEEE80211_BAND_60GHZ;
+               return true;
        }
 
        return false;
@@ -1184,8 +1206,6 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
        if (!beacon_int)
                return -EINVAL;
 
-       mutex_lock(&rdev->devlist_mtx);
-
        list_for_each_entry(wdev, &rdev->wdev_list, list) {
                if (!wdev->beacon_interval)
                        continue;
@@ -1195,8 +1215,6 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
                }
        }
 
-       mutex_unlock(&rdev->devlist_mtx);
-
        return res;
 }
 
@@ -1220,7 +1238,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        int i, j;
 
        ASSERT_RTNL();
-       lockdep_assert_held(&rdev->devlist_mtx);
 
        if (WARN_ON(hweight32(radar_detect) > 1))
                return -EINVAL;
index d997d0f..e7c6e86 100644 (file)
@@ -72,7 +72,6 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
        struct cfg80211_registered_device *rdev;
        struct vif_params vifparams;
        enum nl80211_iftype type;
-       int ret;
 
        rdev = wiphy_to_dev(wdev->wiphy);
 
@@ -98,11 +97,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
 
        memset(&vifparams, 0, sizeof(vifparams));
 
-       cfg80211_lock_rdev(rdev);
-       ret = cfg80211_change_iface(rdev, dev, type, NULL, &vifparams);
-       cfg80211_unlock_rdev(rdev);
-
-       return ret;
+       return cfg80211_change_iface(rdev, dev, type, NULL, &vifparams);
 }
 EXPORT_SYMBOL_GPL(cfg80211_wext_siwmode);
 
@@ -579,13 +574,10 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
 {
        int err;
 
-       /* devlist mutex needed for possible IBSS re-join */
-       mutex_lock(&rdev->devlist_mtx);
        wdev_lock(dev->ieee80211_ptr);
        err = __cfg80211_set_encryption(rdev, dev, pairwise, addr,
                                        remove, tx_key, idx, params);
        wdev_unlock(dev->ieee80211_ptr);
-       mutex_unlock(&rdev->devlist_mtx);
 
        return err;
 }
@@ -787,7 +779,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
        struct cfg80211_chan_def chandef = {
                .width = NL80211_CHAN_WIDTH_20_NOHT,
        };
-       int freq, err;
+       int freq;
 
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
@@ -804,10 +796,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
                chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
                if (!chandef.chan)
                        return -EINVAL;
-               mutex_lock(&rdev->devlist_mtx);
-               err = cfg80211_set_monitor_channel(rdev, &chandef);
-               mutex_unlock(&rdev->devlist_mtx);
-               return err;
+               return cfg80211_set_monitor_channel(rdev, &chandef);
        case NL80211_IFTYPE_MESH_POINT:
                freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
                if (freq < 0)
@@ -818,10 +807,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
                chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
                if (!chandef.chan)
                        return -EINVAL;
-               mutex_lock(&rdev->devlist_mtx);
-               err = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
-               mutex_unlock(&rdev->devlist_mtx);
-               return err;
+               return cfg80211_set_mesh_channel(rdev, wdev, &chandef);
        default:
                return -EOPNOTSUPP;
        }
index e79cb5c..14c9a25 100644 (file)
@@ -54,8 +54,8 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
        if (wdev->wext.prev_bssid_valid)
                prev_bssid = wdev->wext.prev_bssid;
 
-       err = __cfg80211_connect(rdev, wdev->netdev,
-                                &wdev->wext.connect, ck, prev_bssid);
+       err = cfg80211_connect(rdev, wdev->netdev,
+                              &wdev->wext.connect, ck, prev_bssid);
        if (err)
                kfree(ck);
 
@@ -87,12 +87,9 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
                        return -EINVAL;
        }
 
-       cfg80211_lock_rdev(rdev);
-       mutex_lock(&rdev->devlist_mtx);
-       mutex_lock(&rdev->sched_scan_mtx);
        wdev_lock(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
+       if (wdev->conn) {
                bool event = true;
 
                if (wdev->wext.connect.channel == chan) {
@@ -103,8 +100,8 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
                /* if SSID set, we'll try right again, avoid event */
                if (wdev->wext.connect.ssid_len)
                        event = false;
-               err = __cfg80211_disconnect(rdev, dev,
-                                           WLAN_REASON_DEAUTH_LEAVING, event);
+               err = cfg80211_disconnect(rdev, dev,
+                                         WLAN_REASON_DEAUTH_LEAVING, event);
                if (err)
                        goto out;
        }
@@ -136,9 +133,6 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
        err = cfg80211_mgd_wext_connect(rdev, wdev);
  out:
        wdev_unlock(wdev);
-       mutex_unlock(&rdev->sched_scan_mtx);
-       mutex_unlock(&rdev->devlist_mtx);
-       cfg80211_unlock_rdev(rdev);
        return err;
 }
 
@@ -190,14 +184,11 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
        if (len > 0 && ssid[len - 1] == '\0')
                len--;
 
-       cfg80211_lock_rdev(rdev);
-       mutex_lock(&rdev->devlist_mtx);
-       mutex_lock(&rdev->sched_scan_mtx);
        wdev_lock(wdev);
 
        err = 0;
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
+       if (wdev->conn) {
                bool event = true;
 
                if (wdev->wext.connect.ssid && len &&
@@ -208,8 +199,8 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
                /* if SSID set now, we'll try to connect, avoid event */
                if (len)
                        event = false;
-               err = __cfg80211_disconnect(rdev, dev,
-                                           WLAN_REASON_DEAUTH_LEAVING, event);
+               err = cfg80211_disconnect(rdev, dev,
+                                         WLAN_REASON_DEAUTH_LEAVING, event);
                if (err)
                        goto out;
        }
@@ -226,9 +217,6 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
        err = cfg80211_mgd_wext_connect(rdev, wdev);
  out:
        wdev_unlock(wdev);
-       mutex_unlock(&rdev->sched_scan_mtx);
-       mutex_unlock(&rdev->devlist_mtx);
-       cfg80211_unlock_rdev(rdev);
        return err;
 }
 
@@ -287,12 +275,9 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
        if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
                bssid = NULL;
 
-       cfg80211_lock_rdev(rdev);
-       mutex_lock(&rdev->devlist_mtx);
-       mutex_lock(&rdev->sched_scan_mtx);
        wdev_lock(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
+       if (wdev->conn) {
                err = 0;
                /* both automatic */
                if (!bssid && !wdev->wext.connect.bssid)
@@ -303,8 +288,8 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
                    ether_addr_equal(bssid, wdev->wext.connect.bssid))
                        goto out;
 
-               err = __cfg80211_disconnect(rdev, dev,
-                                           WLAN_REASON_DEAUTH_LEAVING, false);
+               err = cfg80211_disconnect(rdev, dev,
+                                         WLAN_REASON_DEAUTH_LEAVING, false);
                if (err)
                        goto out;
        }
@@ -318,9 +303,6 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
        err = cfg80211_mgd_wext_connect(rdev, wdev);
  out:
        wdev_unlock(wdev);
-       mutex_unlock(&rdev->sched_scan_mtx);
-       mutex_unlock(&rdev->devlist_mtx);
-       cfg80211_unlock_rdev(rdev);
        return err;
 }
 
@@ -382,9 +364,9 @@ int cfg80211_wext_siwgenie(struct net_device *dev,
        wdev->wext.ie = ie;
        wdev->wext.ie_len = ie_len;
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
-               err = __cfg80211_disconnect(rdev, dev,
-                                           WLAN_REASON_DEAUTH_LEAVING, false);
+       if (wdev->conn) {
+               err = cfg80211_disconnect(rdev, dev,
+                                         WLAN_REASON_DEAUTH_LEAVING, false);
                if (err)
                        goto out;
        }
@@ -420,8 +402,7 @@ int cfg80211_wext_siwmlme(struct net_device *dev,
        switch (mlme->cmd) {
        case IW_MLME_DEAUTH:
        case IW_MLME_DISASSOC:
-               err = __cfg80211_disconnect(rdev, dev, mlme->reason_code,
-                                           true);
+               err = cfg80211_disconnect(rdev, dev, mlme->reason_code, true);
                break;
        default:
                err = -EOPNOTSUPP;
index 37ca969..45a3ab5 100644 (file)
@@ -224,7 +224,7 @@ static void x25_kill_by_device(struct net_device *dev)
 static int x25_device_event(struct notifier_block *this, unsigned long event,
                            void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct x25_neigh *nb;
 
        if (!net_eq(dev_net(dev), &init_net))
@@ -1583,11 +1583,11 @@ out_cud_release:
        case SIOCX25CALLACCPTAPPRV: {
                rc = -EINVAL;
                lock_sock(sk);
-               if (sk->sk_state != TCP_CLOSE)
-                       break;
-               clear_bit(X25_ACCPT_APPRV_FLAG, &x25->flags);
+               if (sk->sk_state == TCP_CLOSE) {
+                       clear_bit(X25_ACCPT_APPRV_FLAG, &x25->flags);
+                       rc = 0;
+               }
                release_sock(sk);
-               rc = 0;
                break;
        }
 
@@ -1595,14 +1595,15 @@ out_cud_release:
                rc = -EINVAL;
                lock_sock(sk);
                if (sk->sk_state != TCP_ESTABLISHED)
-                       break;
+                       goto out_sendcallaccpt_release;
                /* must call accptapprv above */
                if (test_bit(X25_ACCPT_APPRV_FLAG, &x25->flags))
-                       break;
+                       goto out_sendcallaccpt_release;
                x25_write_internal(sk, X25_CALL_ACCEPTED);
                x25->state = X25_STATE_3;
-               release_sock(sk);
                rc = 0;
+out_sendcallaccpt_release:
+               release_sock(sk);
                break;
        }
 
index ab2bb42..8884399 100644 (file)
@@ -163,6 +163,11 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
                skb->sp->xvec[skb->sp->len++] = x;
 
                spin_lock(&x->lock);
+               if (unlikely(x->km.state == XFRM_STATE_ACQ)) {
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR);
+                       goto drop_unlock;
+               }
+
                if (unlikely(x->km.state != XFRM_STATE_VALID)) {
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEINVALID);
                        goto drop_unlock;
index 0cf003d..eb4a842 100644 (file)
@@ -89,7 +89,7 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
 
                err = x->type->output(x, skb);
                if (err == -EINPROGRESS)
-                       goto out_exit;
+                       goto out;
 
 resume:
                if (err) {
@@ -107,15 +107,14 @@ resume:
                x = dst->xfrm;
        } while (x && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL));
 
-       err = 0;
+       return 0;
 
-out_exit:
-       return err;
 error:
        spin_unlock_bh(&x->lock);
 error_nolock:
        kfree_skb(skb);
-       goto out_exit;
+out:
+       return err;
 }
 
 int xfrm_output_resume(struct sk_buff *skb, int err)
index ea970b8..e52cab3 100644 (file)
@@ -2785,7 +2785,7 @@ static void __net_init xfrm_dst_ops_init(struct net *net)
 
 static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        switch (event) {
        case NETDEV_DOWN:
index c721b0d..80cd1e5 100644 (file)
@@ -44,6 +44,7 @@ static const struct snmp_mib xfrm_mib_list[] = {
        SNMP_MIB_ITEM("XfrmOutPolError", LINUX_MIB_XFRMOUTPOLERROR),
        SNMP_MIB_ITEM("XfrmFwdHdrError", LINUX_MIB_XFRMFWDHDRERROR),
        SNMP_MIB_ITEM("XfrmOutStateInvalid", LINUX_MIB_XFRMOUTSTATEINVALID),
+       SNMP_MIB_ITEM("XfrmAcquireError", LINUX_MIB_XFRMACQUIREERROR),
        SNMP_MIB_SENTINEL
 };
 
index f97869f..6031e23 100644 (file)
@@ -311,6 +311,11 @@ cmd_lzo = (cat $(filter-out FORCE,$^) | \
        lzop -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ || \
        (rm -f $@ ; false)
 
+quiet_cmd_lz4 = LZ4     $@
+cmd_lz4 = (cat $(filter-out FORCE,$^) | \
+       lz4c -l -c1 stdin stdout && $(call size_append, $(filter-out FORCE,$^))) > $@ || \
+       (rm -f $@ ; false)
+
 # U-Boot mkimage
 # ---------------------------------------------------------------------------
 
index 6afcd12..2ee9eb7 100755 (executable)
@@ -6,6 +6,7 @@
 # Licensed under the terms of the GNU GPL License version 2
 
 use strict;
+use POSIX;
 
 my $P = $0;
 $P =~ s@.*/@@g;
@@ -399,37 +400,52 @@ sub seed_camelcase_includes {
        return if ($camelcase_seeded);
 
        my $files;
-       my $camelcase_git_file = "";
+       my $camelcase_cache = "";
+       my @include_files = ();
+
+       $camelcase_seeded = 1;
 
        if (-d ".git") {
                my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`;
                chomp $git_last_include_commit;
-               $camelcase_git_file = ".checkpatch-camelcase.$git_last_include_commit";
-               if (-f $camelcase_git_file) {
-                       open(my $camelcase_file, '<', "$camelcase_git_file")
-                           or warn "$P: Can't read '$camelcase_git_file' $!\n";
-                       while (<$camelcase_file>) {
-                               chomp;
-                               $camelcase{$_} = 1;
-                       }
-                       close($camelcase_file);
-
-                       return;
-               }
-               $files = `git ls-files include`;
+               $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit";
        } else {
+               my $last_mod_date = 0;
                $files = `find $root/include -name "*.h"`;
+               @include_files = split('\n', $files);
+               foreach my $file (@include_files) {
+                       my $date = POSIX::strftime("%Y%m%d%H%M",
+                                                  localtime((stat $file)[9]));
+                       $last_mod_date = $date if ($last_mod_date < $date);
+               }
+               $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date";
+       }
+
+       if ($camelcase_cache ne "" && -f $camelcase_cache) {
+               open(my $camelcase_file, '<', "$camelcase_cache")
+                   or warn "$P: Can't read '$camelcase_cache' $!\n";
+               while (<$camelcase_file>) {
+                       chomp;
+                       $camelcase{$_} = 1;
+               }
+               close($camelcase_file);
+
+               return;
+       }
+
+       if (-d ".git") {
+               $files = `git ls-files "include/*.h"`;
+               @include_files = split('\n', $files);
        }
-       my @include_files = split('\n', $files);
+
        foreach my $file (@include_files) {
                seed_camelcase_file($file);
        }
-       $camelcase_seeded = 1;
 
-       if ($camelcase_git_file ne "") {
+       if ($camelcase_cache ne "") {
                unlink glob ".checkpatch-camelcase.*";
-               open(my $camelcase_file, '>', "$camelcase_git_file")
-                   or warn "$P: Can't write '$camelcase_git_file' $!\n";
+               open(my $camelcase_file, '>', "$camelcase_cache")
+                   or warn "$P: Can't write '$camelcase_cache' $!\n";
                foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) {
                        print $camelcase_file ("$_\n");
                }
index 1728d4e..d32e16e 100644 (file)
@@ -91,7 +91,10 @@ static int cap_sb_pivotroot(struct path *old_path, struct path *new_path)
 }
 
 static int cap_sb_set_mnt_opts(struct super_block *sb,
-                              struct security_mnt_opts *opts)
+                              struct security_mnt_opts *opts,
+                              unsigned long kern_flags,
+                              unsigned long *set_kern_flags)
+
 {
        if (unlikely(opts->num_mnt_opts))
                return -EOPNOTSUPP;
@@ -109,6 +112,13 @@ static int cap_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
        return 0;
 }
 
+static int cap_dentry_init_security(struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen)
+{
+       return 0;
+}
+
 static int cap_inode_alloc_security(struct inode *inode)
 {
        return 0;
@@ -816,6 +826,11 @@ static int cap_setprocattr(struct task_struct *p, char *name, void *value,
        return -EINVAL;
 }
 
+static int cap_ismaclabel(const char *name)
+{
+       return 0;
+}
+
 static int cap_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 {
        return -EOPNOTSUPP;
@@ -931,6 +946,7 @@ void __init security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, sb_set_mnt_opts);
        set_to_cap_if_null(ops, sb_clone_mnt_opts);
        set_to_cap_if_null(ops, sb_parse_opts_str);
+       set_to_cap_if_null(ops, dentry_init_security);
        set_to_cap_if_null(ops, inode_alloc_security);
        set_to_cap_if_null(ops, inode_free_security);
        set_to_cap_if_null(ops, inode_init_security);
@@ -1034,6 +1050,7 @@ void __init security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, d_instantiate);
        set_to_cap_if_null(ops, getprocattr);
        set_to_cap_if_null(ops, setprocattr);
+       set_to_cap_if_null(ops, ismaclabel);
        set_to_cap_if_null(ops, secid_to_secctx);
        set_to_cap_if_null(ops, secctx_to_secid);
        set_to_cap_if_null(ops, release_secctx);
index a3dce87..94b35ae 100644 (file)
@@ -12,6 +12,7 @@
  */
 
 #include <linux/capability.h>
+#include <linux/dcache.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -293,9 +294,12 @@ int security_sb_pivotroot(struct path *old_path, struct path *new_path)
 }
 
 int security_sb_set_mnt_opts(struct super_block *sb,
-                               struct security_mnt_opts *opts)
+                               struct security_mnt_opts *opts,
+                               unsigned long kern_flags,
+                               unsigned long *set_kern_flags)
 {
-       return security_ops->sb_set_mnt_opts(sb, opts);
+       return security_ops->sb_set_mnt_opts(sb, opts, kern_flags,
+                                               set_kern_flags);
 }
 EXPORT_SYMBOL(security_sb_set_mnt_opts);
 
@@ -324,6 +328,15 @@ void security_inode_free(struct inode *inode)
        security_ops->inode_free_security(inode);
 }
 
+int security_dentry_init_security(struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen)
+{
+       return security_ops->dentry_init_security(dentry, mode, name,
+                                                       ctx, ctxlen);
+}
+EXPORT_SYMBOL(security_dentry_init_security);
+
 int security_inode_init_security(struct inode *inode, struct inode *dir,
                                 const struct qstr *qstr,
                                 const initxattrs initxattrs, void *fs_data)
@@ -647,6 +660,7 @@ int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer
                return 0;
        return security_ops->inode_listsecurity(inode, buffer, buffer_size);
 }
+EXPORT_SYMBOL(security_inode_listsecurity);
 
 void security_inode_getsecid(const struct inode *inode, u32 *secid)
 {
@@ -1047,6 +1061,12 @@ int security_netlink_send(struct sock *sk, struct sk_buff *skb)
        return security_ops->netlink_send(sk, skb);
 }
 
+int security_ismaclabel(const char *name)
+{
+       return security_ops->ismaclabel(name);
+}
+EXPORT_SYMBOL(security_ismaclabel);
+
 int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 {
        return security_ops->secid_to_secctx(secid, secdata, seclen);
index db1fca9..c956390 100644 (file)
@@ -81,6 +81,7 @@
 #include <linux/syslog.h>
 #include <linux/user_namespace.h>
 #include <linux/export.h>
+#include <linux/security.h>
 #include <linux/msg.h>
 #include <linux/shm.h>
 
@@ -284,13 +285,14 @@ static void superblock_free_security(struct super_block *sb)
 
 /* The file system's label must be initialized prior to use. */
 
-static const char *labeling_behaviors[6] = {
+static const char *labeling_behaviors[7] = {
        "uses xattr",
        "uses transition SIDs",
        "uses task SIDs",
        "uses genfs_contexts",
        "not configured for labeling",
        "uses mountpoint labeling",
+       "uses native labeling",
 };
 
 static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
@@ -552,7 +554,9 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
  * labeling information.
  */
 static int selinux_set_mnt_opts(struct super_block *sb,
-                               struct security_mnt_opts *opts)
+                               struct security_mnt_opts *opts,
+                               unsigned long kern_flags,
+                               unsigned long *set_kern_flags)
 {
        const struct cred *cred = current_cred();
        int rc = 0, i;
@@ -580,6 +584,12 @@ static int selinux_set_mnt_opts(struct super_block *sb,
                        "before the security server is initialized\n");
                goto out;
        }
+       if (kern_flags && !set_kern_flags) {
+               /* Specifying internal flags without providing a place to
+                * place the results is not allowed */
+               rc = -EINVAL;
+               goto out;
+       }
 
        /*
         * Binary mount data FS will come through this function twice.  Once
@@ -670,14 +680,21 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        if (strcmp(sb->s_type->name, "proc") == 0)
                sbsec->flags |= SE_SBPROC;
 
-       /* 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);
-       if (rc) {
-               printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
-                      __func__, sb->s_type->name, rc);
-               goto out;
+       if (!sbsec->behavior) {
+               /*
+                * 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);
+               if (rc) {
+                       printk(KERN_WARNING
+                               "%s: security_fs_use(%s) returned %d\n",
+                                       __func__, sb->s_type->name, rc);
+                       goto out;
+               }
        }
-
        /* sets the context of the superblock for the fs being mounted. */
        if (fscontext_sid) {
                rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);
@@ -692,6 +709,11 @@ static int selinux_set_mnt_opts(struct super_block *sb,
         * sets the label used on all file below the mountpoint, and will set
         * the superblock context if not already set.
         */
+       if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !context_sid) {
+               sbsec->behavior = SECURITY_FS_USE_NATIVE;
+               *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
+       }
+
        if (context_sid) {
                if (!fscontext_sid) {
                        rc = may_context_mount_sb_relabel(context_sid, sbsec,
@@ -723,7 +745,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        }
 
        if (defcontext_sid) {
-               if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
+               if (sbsec->behavior != SECURITY_FS_USE_XATTR &&
+                       sbsec->behavior != SECURITY_FS_USE_NATIVE) {
                        rc = -EINVAL;
                        printk(KERN_WARNING "SELinux: defcontext option is "
                               "invalid for this filesystem type\n");
@@ -980,7 +1003,7 @@ static int superblock_doinit(struct super_block *sb, void *data)
                goto out_err;
 
 out:
-       rc = selinux_set_mnt_opts(sb, &opts);
+       rc = selinux_set_mnt_opts(sb, &opts, 0, NULL);
 
 out_err:
        security_free_mnt_opts(&opts);
@@ -1222,6 +1245,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
        }
 
        switch (sbsec->behavior) {
+       case SECURITY_FS_USE_NATIVE:
+               break;
        case SECURITY_FS_USE_XATTR:
                if (!inode->i_op->getxattr) {
                        isec->sid = sbsec->def_sid;
@@ -2527,6 +2552,40 @@ static void selinux_inode_free_security(struct inode *inode)
        inode_free_security(inode);
 }
 
+static int selinux_dentry_init_security(struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen)
+{
+       const struct cred *cred = current_cred();
+       struct task_security_struct *tsec;
+       struct inode_security_struct *dsec;
+       struct superblock_security_struct *sbsec;
+       struct inode *dir = dentry->d_parent->d_inode;
+       u32 newsid;
+       int rc;
+
+       tsec = cred->security;
+       dsec = dir->i_security;
+       sbsec = dir->i_sb->s_security;
+
+       if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+               newsid = tsec->create_sid;
+       } else {
+               rc = security_transition_sid(tsec->sid, dsec->sid,
+                                            inode_mode_to_security_class(mode),
+                                            name,
+                                            &newsid);
+               if (rc) {
+                       printk(KERN_WARNING
+                               "%s: security_transition_sid failed, rc=%d\n",
+                              __func__, -rc);
+                       return rc;
+               }
+       }
+
+       return security_sid_to_context(newsid, (char **)ctx, ctxlen);
+}
+
 static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
                                       const struct qstr *qstr, char **name,
                                       void **value, size_t *len)
@@ -2861,7 +2920,10 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
                return;
        }
 
+       isec->sclass = inode_mode_to_security_class(inode->i_mode);
        isec->sid = newsid;
+       isec->initialized = 1;
+
        return;
 }
 
@@ -2949,6 +3011,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
        if (rc)
                return rc;
 
+       isec->sclass = inode_mode_to_security_class(inode->i_mode);
        isec->sid = newsid;
        isec->initialized = 1;
        return 0;
@@ -5432,6 +5495,11 @@ abort_change:
        return error;
 }
 
+static int selinux_ismaclabel(const char *name)
+{
+       return (strcmp(name, XATTR_SELINUX_SUFFIX) == 0);
+}
+
 static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 {
        return security_sid_to_context(secid, secdata, seclen);
@@ -5574,6 +5642,7 @@ static struct security_operations selinux_ops = {
        .sb_clone_mnt_opts =            selinux_sb_clone_mnt_opts,
        .sb_parse_opts_str =            selinux_parse_opts_str,
 
+       .dentry_init_security =         selinux_dentry_init_security,
 
        .inode_alloc_security =         selinux_inode_alloc_security,
        .inode_free_security =          selinux_inode_free_security,
@@ -5669,6 +5738,7 @@ static struct security_operations selinux_ops = {
        .getprocattr =                  selinux_getprocattr,
        .setprocattr =                  selinux_setprocattr,
 
+       .ismaclabel =                   selinux_ismaclabel,
        .secid_to_secctx =              selinux_secid_to_secctx,
        .secctx_to_secid =              selinux_secctx_to_secid,
        .release_secctx =               selinux_release_secctx,
index 6d38851..8fd8e18 100644 (file)
@@ -169,6 +169,8 @@ int security_get_allow_unknown(void);
 #define SECURITY_FS_USE_GENFS          4 /* use the genfs support */
 #define SECURITY_FS_USE_NONE           5 /* no labeling support */
 #define SECURITY_FS_USE_MNTPOINT       6 /* use mountpoint labeling */
+#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);
index 47a49d1..694e9e4 100644 (file)
@@ -264,7 +264,7 @@ static int sel_netif_avc_callback(u32 event)
 static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
                                             unsigned long event, void *ptr)
 {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (dev_net(dev) != &init_net)
                return NOTIFY_DONE;
index 9cd9b7c..c8adde3 100644 (file)
@@ -2168,7 +2168,10 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
 
                                rc = -EINVAL;
                                c->v.behavior = le32_to_cpu(buf[0]);
-                               if (c->v.behavior > SECURITY_FS_USE_NONE)
+                               /* Determined at runtime, not in policy DB. */
+                               if (c->v.behavior == SECURITY_FS_USE_MNTPOINT)
+                                       goto out;
+                               if (c->v.behavior > SECURITY_FS_USE_MAX)
                                        goto out;
 
                                rc = -ENOMEM;
index 6a08330..3f7682a 100644 (file)
@@ -3639,6 +3639,16 @@ static void smack_audit_rule_free(void *vrule)
 
 #endif /* CONFIG_AUDIT */
 
+/**
+ * smack_ismaclabel - check if xattr @name references a smack MAC label
+ * @name: Full xattr name to check.
+ */
+static int smack_ismaclabel(const char *name)
+{
+       return (strcmp(name, XATTR_SMACK_SUFFIX) == 0);
+}
+
+
 /**
  * smack_secid_to_secctx - return the smack label for a secid
  * @secid: incoming integer
@@ -3836,6 +3846,7 @@ struct security_operations smack_ops = {
        .audit_rule_free =              smack_audit_rule_free,
 #endif /* CONFIG_AUDIT */
 
+       .ismaclabel =                   smack_ismaclabel,
        .secid_to_secctx =              smack_secid_to_secctx,
        .secctx_to_secid =              smack_secctx_to_secid,
        .release_secctx =               smack_release_secctx,
index 0c5371a..59c5e9c 100644 (file)
@@ -151,6 +151,16 @@ config SND_HDA_CODEC_HDMI
          snd-hda-codec-hdmi.
          This module is automatically loaded at probing.
 
+config SND_HDA_I915
+       bool "Build Display HD-audio controller/codec power well support for i915 cards"
+       depends on DRM_I915
+       help
+         Say Y here to include full HDMI and DisplayPort HD-audio controller/codec
+         power-well support for Intel Haswell graphics cards based on the i915 driver.
+
+         Note that this option must be enabled for Intel Haswell C+ stepping machines, otherwise
+         the GPU audio controller/codecs will not be initialized or damaged when exit from S3 mode.
+
 config SND_HDA_CODEC_CIRRUS
        bool "Build Cirrus Logic codec support"
        default y
index 24a2514..c091438 100644 (file)
@@ -1,4 +1,6 @@
 snd-hda-intel-objs := hda_intel.o
+# for haswell power well
+snd-hda-intel-$(CONFIG_SND_HDA_I915) +=        hda_i915.o
 
 snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o
 snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c
new file mode 100644 (file)
index 0000000..76c13d5
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *  hda_i915.c - routines for Haswell HDA controller power well support
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the Free
+ *  Software Foundation; either version 2 of the License, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software Foundation,
+ *  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <drm/i915_powerwell.h>
+#include "hda_i915.h"
+
+static void (*get_power)(void);
+static void (*put_power)(void);
+
+void hda_display_power(bool enable)
+{
+       if (!get_power || !put_power)
+               return;
+
+       snd_printdd("HDA display power %s \n",
+                       enable ? "Enable" : "Disable");
+       if (enable)
+               get_power();
+       else
+               put_power();
+}
+
+int hda_i915_init(void)
+{
+       int err = 0;
+
+       get_power = symbol_request(i915_request_power_well);
+       if (!get_power) {
+               snd_printk(KERN_WARNING "hda-i915: get_power symbol get fail\n");
+               return -ENODEV;
+       }
+
+       put_power = symbol_request(i915_release_power_well);
+       if (!put_power) {
+               symbol_put(i915_request_power_well);
+               get_power = NULL;
+               return -ENODEV;
+       }
+
+       snd_printd("HDA driver get symbol successfully from i915 module\n");
+
+       return err;
+}
+
+int hda_i915_exit(void)
+{
+       if (get_power) {
+               symbol_put(i915_request_power_well);
+               get_power = NULL;
+       }
+       if (put_power) {
+               symbol_put(i915_release_power_well);
+               put_power = NULL;
+       }
+
+       return 0;
+}
diff --git a/sound/pci/hda/hda_i915.h b/sound/pci/hda/hda_i915.h
new file mode 100644 (file)
index 0000000..5a63da2
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  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 __SOUND_HDA_I915_H
+#define __SOUND_HDA_I915_H
+
+#ifdef CONFIG_SND_HDA_I915
+void hda_display_power(bool enable);
+int hda_i915_init(void);
+int hda_i915_exit(void);
+#else
+static inline void hda_display_power(bool enable) {}
+static inline int hda_i915_init(void)
+{
+       return -ENODEV;
+}
+static inline int hda_i915_exit(void)
+{
+       return 0;
+}
+#endif
+
+#endif
index f39de90..8860dd5 100644 (file)
@@ -62,6 +62,7 @@
 #include <linux/vga_switcheroo.h>
 #include <linux/firmware.h>
 #include "hda_codec.h"
+#include "hda_i915.h"
 
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
@@ -541,6 +542,10 @@ struct azx {
        /* for pending irqs */
        struct work_struct irq_pending_work;
 
+#ifdef CONFIG_SND_HDA_I915
+       struct work_struct probe_work;
+#endif
+
        /* reboot notifier (for mysterious hangup problem at power-down) */
        struct notifier_block reboot_notifier;
 
@@ -594,6 +599,7 @@ enum {
 #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23)   /* BDLE in 4k boundary */
 #define AZX_DCAPS_COUNT_LPIB_DELAY  (1 << 25)  /* Take LPIB as delay */
 #define AZX_DCAPS_PM_RUNTIME   (1 << 26)       /* runtime PM support */
+#define AZX_DCAPS_I915_POWERWELL (1 << 27)     /* HSW i915 power well support */
 
 /* quirks for Intel PCH */
 #define AZX_DCAPS_INTEL_PCH_NOPM \
@@ -2919,6 +2925,8 @@ static int azx_suspend(struct device *dev)
        pci_disable_device(pci);
        pci_save_state(pci);
        pci_set_power_state(pci, PCI_D3hot);
+       if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+               hda_display_power(false);
        return 0;
 }
 
@@ -2931,6 +2939,8 @@ static int azx_resume(struct device *dev)
        if (chip->disabled)
                return 0;
 
+       if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+               hda_display_power(true);
        pci_set_power_state(pci, PCI_D0);
        pci_restore_state(pci);
        if (pci_enable_device(pci) < 0) {
@@ -2964,6 +2974,8 @@ static int azx_runtime_suspend(struct device *dev)
        azx_stop_chip(chip);
        azx_enter_link_reset(chip);
        azx_clear_irq_pending(chip);
+       if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+               hda_display_power(false);
        return 0;
 }
 
@@ -2972,6 +2984,8 @@ static int azx_runtime_resume(struct device *dev)
        struct snd_card *card = dev_get_drvdata(dev);
        struct azx *chip = card->private_data;
 
+       if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
+               hda_display_power(true);
        azx_init_pci(chip);
        azx_init_chip(chip, 1);
        return 0;
@@ -3026,7 +3040,6 @@ static void azx_notifier_unregister(struct azx *chip)
                unregister_reboot_notifier(&chip->reboot_notifier);
 }
 
-static int azx_first_init(struct azx *chip);
 static int azx_probe_continue(struct azx *chip);
 
 #ifdef SUPPORT_VGA_SWITCHEROO
@@ -3053,8 +3066,7 @@ static void azx_vs_set_state(struct pci_dev *pci,
                        snd_printk(KERN_INFO SFX
                                   "%s: Start delayed initialization\n",
                                   pci_name(chip->pci));
-                       if (azx_first_init(chip) < 0 ||
-                           azx_probe_continue(chip) < 0) {
+                       if (azx_probe_continue(chip) < 0) {
                                snd_printk(KERN_ERR SFX
                                           "%s: initialization error\n",
                                           pci_name(chip->pci));
@@ -3140,8 +3152,13 @@ static int register_vga_switcheroo(struct azx *chip)
  */
 static int azx_free(struct azx *chip)
 {
+       struct pci_dev *pci = chip->pci;
        int i;
 
+       if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
+                       && chip->running)
+               pm_runtime_get_noresume(&pci->dev);
+
        azx_del_card_list(chip);
 
        azx_notifier_unregister(chip);
@@ -3193,6 +3210,10 @@ static int azx_free(struct azx *chip)
        if (chip->fw)
                release_firmware(chip->fw);
 #endif
+       if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+               hda_display_power(false);
+               hda_i915_exit();
+       }
        kfree(chip);
 
        return 0;
@@ -3418,6 +3439,13 @@ static void azx_check_snoop_available(struct azx *chip)
        }
 }
 
+#ifdef CONFIG_SND_HDA_I915
+static void azx_probe_work(struct work_struct *work)
+{
+       azx_probe_continue(container_of(work, struct azx, probe_work));
+}
+#endif
+
 /*
  * constructor
  */
@@ -3493,7 +3521,13 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
                return err;
        }
 
+#ifdef CONFIG_SND_HDA_I915
+       /* continue probing in work context as may trigger request module */
+       INIT_WORK(&chip->probe_work, azx_probe_work);
+#endif
+
        *rchip = chip;
+
        return 0;
 }
 
@@ -3750,11 +3784,6 @@ static int azx_probe(struct pci_dev *pci,
        }
 
        probe_now = !chip->disabled;
-       if (probe_now) {
-               err = azx_first_init(chip);
-               if (err < 0)
-                       goto out_free;
-       }
 
 #ifdef CONFIG_SND_HDA_PATCH_LOADER
        if (patch[dev] && *patch[dev]) {
@@ -3769,15 +3798,22 @@ static int azx_probe(struct pci_dev *pci,
        }
 #endif /* CONFIG_SND_HDA_PATCH_LOADER */
 
+       /* continue probing in work context, avoid request_module deadlock */
+       if (probe_now && (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)) {
+#ifdef CONFIG_SND_HDA_I915
+               probe_now = false;
+               schedule_work(&chip->probe_work);
+#else
+               snd_printk(KERN_ERR SFX "Haswell must build in CONFIG_SND_HDA_I915\n");
+#endif
+       }
+
        if (probe_now) {
                err = azx_probe_continue(chip);
                if (err < 0)
                        goto out_free;
        }
 
-       if (pci_dev_run_wake(pci))
-               pm_runtime_put_noidle(&pci->dev);
-
        dev++;
        complete_all(&chip->probe_wait);
        return 0;
@@ -3789,9 +3825,24 @@ out_free:
 
 static int azx_probe_continue(struct azx *chip)
 {
+       struct pci_dev *pci = chip->pci;
        int dev = chip->dev_index;
        int err;
 
+       /* Request power well for Haswell HDA controller and codec */
+       if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+               err = hda_i915_init();
+               if (err < 0) {
+                       snd_printk(KERN_ERR SFX "Error request power-well from i915\n");
+                       goto out_free;
+               }
+               hda_display_power(true);
+       }
+
+       err = azx_first_init(chip);
+       if (err < 0)
+               goto out_free;
+
 #ifdef CONFIG_SND_HDA_INPUT_BEEP
        chip->beep_mode = beep_mode[dev];
 #endif
@@ -3836,6 +3887,8 @@ static int azx_probe_continue(struct azx *chip)
        power_down_all_codecs(chip);
        azx_notifier_register(chip);
        azx_add_card_list(chip);
+       if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
+               pm_runtime_put_noidle(&pci->dev);
 
        return 0;
 
@@ -3848,9 +3901,6 @@ static void azx_remove(struct pci_dev *pci)
 {
        struct snd_card *card = pci_get_drvdata(pci);
 
-       if (pci_dev_run_wake(pci))
-               pm_runtime_get_noresume(&pci->dev);
-
        if (card)
                snd_card_free(card);
 }
@@ -3882,11 +3932,14 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
          .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
        /* Haswell */
        { PCI_DEVICE(0x8086, 0x0a0c),
-         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
+         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
+         AZX_DCAPS_I915_POWERWELL },
        { PCI_DEVICE(0x8086, 0x0c0c),
-         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
+         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
+         AZX_DCAPS_I915_POWERWELL },
        { PCI_DEVICE(0x8086, 0x0d0c),
-         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
+         .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH |
+         AZX_DCAPS_I915_POWERWELL },
        /* 5 Series/3400 */
        { PCI_DEVICE(0x8086, 0x3b56),
          .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
index c41b586..24adf70 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright 2013 Red Hat, Inc.
  * Author: Daniel Borkmann <dborkman@redhat.com>
+ *         Chetan Loke <loke.chetan@gmail.com> (TPACKET_V3 usage example)
  *
  * A basic test of packet socket's TPACKET_V1/TPACKET_V2/TPACKET_V3 behavior.
  *
 # define __align_tpacket(x)    __attribute__((aligned(TPACKET_ALIGN(x))))
 #endif
 
-#define BLOCK_STATUS(x)                ((x)->h1.block_status)
-#define BLOCK_NUM_PKTS(x)      ((x)->h1.num_pkts)
-#define BLOCK_O2FP(x)          ((x)->h1.offset_to_first_pkt)
-#define BLOCK_LEN(x)           ((x)->h1.blk_len)
-#define BLOCK_SNUM(x)          ((x)->h1.seq_num)
-#define BLOCK_O2PRIV(x)                ((x)->offset_to_priv)
-#define BLOCK_PRIV(x)          ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x)))
-#define BLOCK_HDR_LEN          (ALIGN_8(sizeof(struct block_desc)))
-#define ALIGN_8(x)             (((x) + 8 - 1) & ~(8 - 1))
-#define BLOCK_PLUS_PRIV(sz_pri)        (BLOCK_HDR_LEN + ALIGN_8((sz_pri)))
-
 #define NUM_PACKETS            100
+#define ALIGN_8(x)             (((x) + 8 - 1) & ~(8 - 1))
 
 struct ring {
        struct iovec *rd;
@@ -476,41 +467,30 @@ static uint64_t __v3_prev_block_seq_num = 0;
 
 void __v3_test_block_seq_num(struct block_desc *pbd)
 {
-       if (__v3_prev_block_seq_num + 1 != BLOCK_SNUM(pbd)) {
+       if (__v3_prev_block_seq_num + 1 != pbd->h1.seq_num) {
                fprintf(stderr, "\nprev_block_seq_num:%"PRIu64", expected "
                        "seq:%"PRIu64" != actual seq:%"PRIu64"\n",
                        __v3_prev_block_seq_num, __v3_prev_block_seq_num + 1,
-                       (uint64_t) BLOCK_SNUM(pbd));
+                       (uint64_t) pbd->h1.seq_num);
                exit(1);
        }
 
-       __v3_prev_block_seq_num = BLOCK_SNUM(pbd);
+       __v3_prev_block_seq_num = pbd->h1.seq_num;
 }
 
 static void __v3_test_block_len(struct block_desc *pbd, uint32_t bytes, int block_num)
 {
-       if (BLOCK_NUM_PKTS(pbd)) {
-               if (bytes != BLOCK_LEN(pbd)) {
-                       fprintf(stderr, "\nblock:%u with %upackets, expected "
-                               "len:%u != actual len:%u\n", block_num,
-                               BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd));
-                       exit(1);
-               }
-       } else {
-               if (BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(13)) {
-                       fprintf(stderr, "\nblock:%u, expected len:%lu != "
-                               "actual len:%u\n", block_num, BLOCK_HDR_LEN,
-                               BLOCK_LEN(pbd));
-                       exit(1);
-               }
+       if (pbd->h1.num_pkts && bytes != pbd->h1.blk_len) {
+               fprintf(stderr, "\nblock:%u with %upackets, expected "
+                       "len:%u != actual len:%u\n", block_num,
+                       pbd->h1.num_pkts, bytes, pbd->h1.blk_len);
+               exit(1);
        }
 }
 
 static void __v3_test_block_header(struct block_desc *pbd, const int block_num)
 {
-       uint32_t block_status = BLOCK_STATUS(pbd);
-
-       if ((block_status & TP_STATUS_USER) == 0) {
+       if ((pbd->h1.block_status & TP_STATUS_USER) == 0) {
                fprintf(stderr, "\nblock %u: not in TP_STATUS_USER\n", block_num);
                exit(1);
        }
@@ -520,14 +500,15 @@ static void __v3_test_block_header(struct block_desc *pbd, const int block_num)
 
 static void __v3_walk_block(struct block_desc *pbd, const int block_num)
 {
-       int num_pkts = BLOCK_NUM_PKTS(pbd), i;
-       unsigned long bytes = 0;
-       unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(13);
+       int num_pkts = pbd->h1.num_pkts, i;
+       unsigned long bytes = 0, bytes_with_padding = ALIGN_8(sizeof(*pbd));
        struct tpacket3_hdr *ppd;
 
        __v3_test_block_header(pbd, block_num);
 
-       ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd));
+       ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd +
+                                      pbd->h1.offset_to_first_pkt);
+
        for (i = 0; i < num_pkts; ++i) {
                bytes += ppd->tp_snaplen;
 
@@ -551,7 +532,7 @@ static void __v3_walk_block(struct block_desc *pbd, const int block_num)
 
 void __v3_flush_block(struct block_desc *pbd)
 {
-       BLOCK_STATUS(pbd) = TP_STATUS_KERNEL;
+       pbd->h1.block_status = TP_STATUS_KERNEL;
        __sync_synchronize();
 }
 
@@ -577,7 +558,7 @@ static void walk_v3_rx(int sock, struct ring *ring)
        while (total_packets < NUM_PACKETS * 2) {
                pbd = (struct block_desc *) ring->rd[block_num].iov_base;
 
-               while ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0)
+               while ((pbd->h1.block_status & TP_STATUS_USER) == 0)
                        poll(&pfd, 1, 1);
 
                __v3_walk_block(pbd, block_num);
@@ -624,8 +605,8 @@ static void __v1_v2_fill(struct ring *ring, unsigned int blocks)
 static void __v3_fill(struct ring *ring, unsigned int blocks)
 {
        ring->req3.tp_retire_blk_tov = 64;
-       ring->req3.tp_sizeof_priv = 13;
-       ring->req3.tp_feature_req_word |= TP_FT_REQ_FILL_RXHASH;
+       ring->req3.tp_sizeof_priv = 0;
+       ring->req3.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH;
 
        ring->req3.tp_block_size = getpagesize() << 2;
        ring->req3.tp_frame_size = TPACKET_ALIGNMENT << 7;
index 085872b..642f503 100644 (file)
@@ -90,6 +90,15 @@ config RD_LZO
          Support loading of a LZO encoded initial ramdisk or cpio buffer
          If unsure, say N.
 
+config RD_LZ4
+       bool "Support initial ramdisks compressed using LZ4" if EXPERT
+       default !EXPERT
+       depends on BLK_DEV_INITRD
+       select DECOMPRESS_LZ4
+       help
+         Support loading of a LZ4 encoded initial ramdisk or cpio buffer
+         If unsure, say N.
+
 choice
        prompt "Built-in initramfs compression mode" if INITRAMFS_SOURCE!=""
        help